mirror of
https://github.com/BlueMap-Minecraft/BlueMap.git
synced 2024-11-21 10:06:09 +01:00
Initial commit: Merging Projects BlueMapCore, BlueMapCLI and BlueMapSponge into one
This commit is contained in:
commit
49ae2cf08f
24
.gitignore
vendored
Normal file
24
.gitignore
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
.gradle/
|
||||
.gradle/*
|
||||
*/.gradle/*
|
||||
|
||||
build/
|
||||
build/*
|
||||
*/build/*
|
||||
|
||||
.settings/
|
||||
.settings/*
|
||||
*/.settings/*
|
||||
|
||||
bin/
|
||||
bin/*
|
||||
*/bin/*
|
||||
|
||||
.classpath
|
||||
*/.classpath
|
||||
|
||||
.project
|
||||
*/.project
|
||||
|
||||
# exclude generated resource
|
||||
src/main/resources/webroot.zip
|
4
BlueMapCLI/build.gradle
Normal file
4
BlueMapCLI/build.gradle
Normal file
@ -0,0 +1,4 @@
|
||||
dependencies {
|
||||
compile group: 'commons-cli', name: 'commons-cli', version: '1.4'
|
||||
compile project(':BlueMapCore')
|
||||
}
|
@ -0,0 +1,555 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.cli;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ForkJoinPool;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.apache.commons.cli.CommandLine;
|
||||
import org.apache.commons.cli.CommandLineParser;
|
||||
import org.apache.commons.cli.DefaultParser;
|
||||
import org.apache.commons.cli.HelpFormatter;
|
||||
import org.apache.commons.cli.Option;
|
||||
import org.apache.commons.cli.Options;
|
||||
import org.apache.commons.cli.ParseException;
|
||||
|
||||
import com.flowpowered.math.vector.Vector2i;
|
||||
import com.flowpowered.math.vector.Vector3i;
|
||||
|
||||
import de.bluecolored.bluemap.core.logger.Logger;
|
||||
import de.bluecolored.bluemap.core.mca.MCAWorld;
|
||||
import de.bluecolored.bluemap.core.render.StaticRenderSettings;
|
||||
import de.bluecolored.bluemap.core.render.TileRenderer;
|
||||
import de.bluecolored.bluemap.core.render.hires.HiresModelManager;
|
||||
import de.bluecolored.bluemap.core.render.lowres.LowresModelManager;
|
||||
import de.bluecolored.bluemap.core.resourcepack.NoSuchResourceException;
|
||||
import de.bluecolored.bluemap.core.resourcepack.ResourcePack;
|
||||
import de.bluecolored.bluemap.core.web.BlueMapWebRequestHandler;
|
||||
import de.bluecolored.bluemap.core.web.WebFilesManager;
|
||||
import de.bluecolored.bluemap.core.web.WebSettings;
|
||||
import de.bluecolored.bluemap.core.webserver.WebServer;
|
||||
import de.bluecolored.bluemap.core.world.World;
|
||||
|
||||
public class BlueMapCLI {
|
||||
|
||||
private File webroot = new File("web");
|
||||
private File dataPath = new File(webroot, "data");
|
||||
|
||||
private File extraResourceFile = null;
|
||||
private int threadCount;
|
||||
|
||||
private String mapId = null;
|
||||
private String mapName = null;
|
||||
|
||||
private int highresTileSize = 32;
|
||||
private int lowresTileSize = 50;
|
||||
private int samplesPerHighresTile = 4;
|
||||
|
||||
private float highresViewDistance = 6f;
|
||||
private float lowresViewDistance = 5f;
|
||||
|
||||
private boolean excludeFacesWithoutSunlight = true;
|
||||
private float ambientOcclusion = 0.25f;
|
||||
private float lighting = 0.8f;
|
||||
private int sliceY = Integer.MAX_VALUE;
|
||||
private int maxY = Integer.MAX_VALUE;
|
||||
private int minY = 0;
|
||||
|
||||
private int port = 8100;
|
||||
private int maxConnections = 100;
|
||||
private InetAddress bindAdress = null;
|
||||
|
||||
public BlueMapCLI() {
|
||||
threadCount = Runtime.getRuntime().availableProcessors();
|
||||
}
|
||||
|
||||
public void renderMap(File mapPath, boolean updateOnly) throws IOException, NoSuchResourceException {
|
||||
dataPath.mkdirs();
|
||||
|
||||
if (!mapPath.exists() || !mapPath.isDirectory()) {
|
||||
throw new IOException("Save folder '" + mapPath + "' does not exist or is not a directory!");
|
||||
}
|
||||
|
||||
Logger.global.logInfo("Reading world...");
|
||||
World world = MCAWorld.load(mapPath.toPath(), UUID.randomUUID());
|
||||
|
||||
if (mapName == null) {
|
||||
mapName = world.getName();
|
||||
}
|
||||
|
||||
if (mapId == null) {
|
||||
mapId = mapPath.getName().toLowerCase();
|
||||
}
|
||||
|
||||
Logger.global.logInfo("Starting Render:"
|
||||
+ "\n map: " + mapPath.getAbsolutePath()
|
||||
+ "\n map-id: " + mapId
|
||||
+ "\n map-name: " + mapName
|
||||
+ "\n thread-count: " + threadCount
|
||||
+ "\n data-path: " + dataPath.getAbsolutePath()
|
||||
+ "\n render-all: " + !excludeFacesWithoutSunlight
|
||||
+ "\n ambient-occlusion: " + ambientOcclusion
|
||||
+ "\n lighting: " + lighting
|
||||
+ "\n sliceY: " + (sliceY < Integer.MAX_VALUE ? sliceY : "-")
|
||||
+ "\n maxY: " + (maxY < Integer.MAX_VALUE ? maxY : "-")
|
||||
+ "\n minY: " + (minY > 0 ? minY : "-")
|
||||
+ "\n hr-tilesize: " + highresTileSize
|
||||
+ "\n lr-tilesize: " + lowresTileSize
|
||||
+ "\n lr-resolution: " + samplesPerHighresTile
|
||||
+ "\n hr-viewdistance: " + highresViewDistance
|
||||
+ "\n lr-viewdistance: " + lowresViewDistance
|
||||
);
|
||||
|
||||
Logger.global.logInfo("Loading Resources...");
|
||||
ResourcePack resourcePack = loadResources();
|
||||
|
||||
Logger.global.logInfo("Initializing renderer...");
|
||||
HiresModelManager hiresModelManager = new HiresModelManager(
|
||||
dataPath.toPath().resolve("hires").resolve(mapId),
|
||||
resourcePack,
|
||||
new Vector2i(highresTileSize, highresTileSize),
|
||||
ForkJoinPool.commonPool()
|
||||
);
|
||||
|
||||
LowresModelManager lowresModelManager = new LowresModelManager(
|
||||
dataPath.toPath().resolve("lowres").resolve(mapId),
|
||||
new Vector2i(lowresTileSize, lowresTileSize),
|
||||
new Vector2i(samplesPerHighresTile, samplesPerHighresTile)
|
||||
);
|
||||
|
||||
TileRenderer tileRenderer = new TileRenderer(hiresModelManager, lowresModelManager, new StaticRenderSettings(
|
||||
ambientOcclusion,
|
||||
excludeFacesWithoutSunlight,
|
||||
lighting,
|
||||
maxY,
|
||||
minY,
|
||||
sliceY
|
||||
));
|
||||
|
||||
File webSettingsFile = new File(dataPath, "settings.json");
|
||||
Logger.global.logInfo("Writing '" + webSettingsFile.getAbsolutePath() + "'...");
|
||||
WebSettings webSettings = new WebSettings(webSettingsFile);
|
||||
webSettings.setName(mapName, mapId);
|
||||
webSettings.setFrom(tileRenderer, mapId);
|
||||
webSettings.setHiresViewDistance(highresViewDistance, mapId);
|
||||
webSettings.setLowresViewDistance(lowresViewDistance, mapId);
|
||||
webSettings.save();
|
||||
|
||||
|
||||
Logger.global.logInfo("Collecting tiles to render...");
|
||||
|
||||
Collection<Vector2i> chunks;
|
||||
if (updateOnly) {
|
||||
long lastRender = webSettings.getLong(mapId, "last-render");
|
||||
chunks = world.getChunkList(lastRender);
|
||||
} else {
|
||||
chunks = world.getChunkList();
|
||||
}
|
||||
|
||||
Set<Vector2i> tiles = new HashSet<>();
|
||||
for (Vector2i chunk : chunks) {
|
||||
Vector3i minBlockPos = new Vector3i(chunk.getX() * 16, 0, chunk.getY() * 16);
|
||||
tiles.add(hiresModelManager.posToTile(minBlockPos));
|
||||
tiles.add(hiresModelManager.posToTile(minBlockPos.add(0, 0, 15)));
|
||||
tiles.add(hiresModelManager.posToTile(minBlockPos.add(15, 0, 0)));
|
||||
tiles.add(hiresModelManager.posToTile(minBlockPos.add(15, 0, 15)));
|
||||
}
|
||||
Logger.global.logInfo("Found " + tiles.size() + " tiles to render! (" + chunks.size() + " chunks)");
|
||||
|
||||
if (tiles.isEmpty()) {
|
||||
Logger.global.logInfo("Render finished!");
|
||||
return;
|
||||
}
|
||||
|
||||
Logger.global.logInfo("Starting Render...");
|
||||
long starttime = System.currentTimeMillis();
|
||||
RenderManager renderManager = new RenderManager(world, tileRenderer, tiles, threadCount);
|
||||
renderManager.start(() -> {
|
||||
Logger.global.logInfo("Waiting for threads to quit...");
|
||||
if (!ForkJoinPool.commonPool().awaitQuiescence(30, TimeUnit.SECONDS)) {
|
||||
Logger.global.logWarning("Some save-threads are taking very long to exit (>30s), they will be ignored.");
|
||||
}
|
||||
|
||||
try {
|
||||
webSettings.set(starttime, mapId, "last-render");
|
||||
webSettings.save();
|
||||
} catch (IOException e) {
|
||||
Logger.global.logError("Failed to update web-settings!", e);
|
||||
}
|
||||
|
||||
Logger.global.logInfo("Render finished!");
|
||||
});
|
||||
}
|
||||
|
||||
public void updateWebFiles() throws IOException {
|
||||
webroot.mkdirs();
|
||||
|
||||
Logger.global.logInfo("Creating webfiles in " + webroot.getAbsolutePath());
|
||||
WebFilesManager webFilesManager = new WebFilesManager(webroot.toPath());
|
||||
webFilesManager.updateFiles();
|
||||
}
|
||||
|
||||
public void startWebserver() throws UnknownHostException {
|
||||
if (bindAdress == null) bindAdress = InetAddress.getLocalHost();
|
||||
|
||||
Logger.global.logInfo("Starting webserver:"
|
||||
+ "\n address: " + this.bindAdress.toString() + ""
|
||||
+ "\n port: " + this.port
|
||||
+ "\n max connections: " + this.maxConnections
|
||||
+ "\n webroot: " + this.webroot.getAbsolutePath()
|
||||
);
|
||||
|
||||
WebServer webserver = new WebServer(
|
||||
this.port,
|
||||
this.maxConnections,
|
||||
this.bindAdress,
|
||||
new BlueMapWebRequestHandler(this.webroot.toPath())
|
||||
);
|
||||
|
||||
webserver.start();
|
||||
}
|
||||
|
||||
private ResourcePack loadResources() throws IOException, NoSuchResourceException {
|
||||
File defaultResourceFile;
|
||||
try {
|
||||
defaultResourceFile = File.createTempFile("res", ".zip");
|
||||
defaultResourceFile.delete();
|
||||
} catch (IOException e) {
|
||||
throw new IOException("Failed to create temporary resource file!", e);
|
||||
}
|
||||
try {
|
||||
ResourcePack.createDefaultResource(defaultResourceFile);
|
||||
} catch (IOException e) {
|
||||
throw new IOException("Failed to create default resources!", e);
|
||||
}
|
||||
|
||||
List<File> resourcePacks = new ArrayList<>();
|
||||
resourcePacks.add(defaultResourceFile);
|
||||
if (this.extraResourceFile != null) resourcePacks.add(extraResourceFile);
|
||||
|
||||
ResourcePack resourcePack = new ResourcePack(resourcePacks, new File(dataPath, "textures.json"));
|
||||
|
||||
defaultResourceFile.delete();
|
||||
|
||||
return resourcePack;
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws IOException, NoSuchResourceException {
|
||||
CommandLineParser parser = new DefaultParser();
|
||||
|
||||
try {
|
||||
CommandLine cmd = parser.parse(BlueMapCLI.createOptions(), args, false);
|
||||
|
||||
if (cmd.hasOption("h")) {
|
||||
BlueMapCLI.printHelp();
|
||||
return;
|
||||
}
|
||||
|
||||
boolean executed = false;
|
||||
|
||||
BlueMapCLI bluemapcli = new BlueMapCLI();
|
||||
|
||||
if (cmd.hasOption("o")) bluemapcli.dataPath = new File(cmd.getOptionValue("o"));
|
||||
if (cmd.hasOption("r")) bluemapcli.extraResourceFile = new File(cmd.getOptionValue("r"));
|
||||
if (cmd.hasOption("t")) bluemapcli.threadCount = Integer.parseInt(cmd.getOptionValue("t"));
|
||||
|
||||
if (cmd.hasOption("d")) bluemapcli.webroot = new File(cmd.getOptionValue("d"));
|
||||
if (cmd.hasOption("i")) bluemapcli.bindAdress = InetAddress.getByName(cmd.getOptionValue("i"));
|
||||
bluemapcli.port = Integer.parseInt(cmd.getOptionValue("p", Integer.toString(bluemapcli.port)));
|
||||
bluemapcli.maxConnections = Integer.parseInt(cmd.getOptionValue("connections", Integer.toString(bluemapcli.maxConnections)));
|
||||
|
||||
bluemapcli.mapName = cmd.getOptionValue("n", bluemapcli.mapName);
|
||||
bluemapcli.mapId = cmd.getOptionValue("id", bluemapcli.mapId);
|
||||
|
||||
bluemapcli.ambientOcclusion = Float.parseFloat(cmd.getOptionValue("ao", Float.toString(bluemapcli.ambientOcclusion)));
|
||||
bluemapcli.lighting = Float.parseFloat(cmd.getOptionValue("lighting", Float.toString(bluemapcli.lighting)));
|
||||
bluemapcli.sliceY = Integer.parseInt(cmd.getOptionValue("y-slice", Integer.toString(bluemapcli.sliceY)));
|
||||
bluemapcli.maxY = Integer.parseInt(cmd.getOptionValue("y-max", Integer.toString(bluemapcli.maxY)));
|
||||
bluemapcli.minY = Integer.parseInt(cmd.getOptionValue("y-min", Integer.toString(bluemapcli.minY)));
|
||||
|
||||
bluemapcli.highresTileSize = Integer.parseInt(cmd.getOptionValue("hr-tilesize", Integer.toString(bluemapcli.highresTileSize)));
|
||||
bluemapcli.highresViewDistance = Float.parseFloat(cmd.getOptionValue("hr-viewdist", Float.toString(bluemapcli.highresViewDistance)));
|
||||
bluemapcli.lowresTileSize = Integer.parseInt(cmd.getOptionValue("lr-tilesize", Integer.toString(bluemapcli.lowresTileSize)));
|
||||
bluemapcli.samplesPerHighresTile = Integer.parseInt(cmd.getOptionValue("lr-resolution", Integer.toString(bluemapcli.samplesPerHighresTile)));
|
||||
bluemapcli.lowresViewDistance = Float.parseFloat(cmd.getOptionValue("lr-viewdist", Float.toString(bluemapcli.lowresViewDistance)));
|
||||
|
||||
if (cmd.hasOption("c")) {
|
||||
bluemapcli.updateWebFiles();
|
||||
executed = true;
|
||||
}
|
||||
|
||||
if (cmd.hasOption("s")) {
|
||||
bluemapcli.startWebserver();
|
||||
executed = true;
|
||||
}
|
||||
|
||||
if (cmd.hasOption("w")) {
|
||||
bluemapcli.renderMap(new File(cmd.getOptionValue("w")), !cmd.hasOption("f"));
|
||||
executed = true;
|
||||
}
|
||||
|
||||
if (executed) return;
|
||||
|
||||
} catch (ParseException e) {
|
||||
Logger.global.logError("Failed to parse provided arguments!", e);
|
||||
} catch (NumberFormatException e) {
|
||||
Logger.global.logError("One argument expected a number but got the wrong format!", e);
|
||||
}
|
||||
|
||||
BlueMapCLI.printHelp();
|
||||
}
|
||||
|
||||
private static Options createOptions() {
|
||||
Options options = new Options();
|
||||
|
||||
options.addOption("h", "help", false, "Displays this message");
|
||||
|
||||
options.addOption(
|
||||
Option.builder("o")
|
||||
.longOpt("out")
|
||||
.hasArg()
|
||||
.argName("directory-path")
|
||||
.desc("Defines the render-output directory. Default is '<webroot>/data' (See option -d)")
|
||||
.build()
|
||||
);
|
||||
options.addOption(
|
||||
Option.builder("d")
|
||||
.longOpt("dir")
|
||||
.hasArg()
|
||||
.argName("directory-path")
|
||||
.desc("Defines the webroot directory. Default is './web'")
|
||||
.build()
|
||||
);
|
||||
|
||||
options.addOption("s", "webserver", false, "Starts the integrated webserver");
|
||||
options.addOption(
|
||||
Option.builder("c")
|
||||
.longOpt("create-web")
|
||||
.desc("The webfiles will be (re)created, existing web-files in the webroot will be replaced!")
|
||||
.build()
|
||||
);
|
||||
options.addOption(
|
||||
Option.builder("i")
|
||||
.longOpt("ip")
|
||||
.hasArg()
|
||||
.argName("ip-adress")
|
||||
.desc("Specifies the IP adress the webserver will use")
|
||||
.build()
|
||||
);
|
||||
options.addOption(
|
||||
Option.builder("p")
|
||||
.longOpt("port")
|
||||
.hasArg()
|
||||
.argName("port")
|
||||
.desc("Specifies the port the webserver will use. Default is 8100")
|
||||
.build()
|
||||
);
|
||||
options.addOption(
|
||||
Option.builder()
|
||||
.longOpt("connections")
|
||||
.hasArg()
|
||||
.argName("count")
|
||||
.desc("Sets the maximum count of simultaneous client-connections that the webserver will allow. Default is 100")
|
||||
.build()
|
||||
);
|
||||
|
||||
options.addOption(
|
||||
Option.builder("w")
|
||||
.longOpt("world")
|
||||
.hasArg()
|
||||
.argName("directory-path")
|
||||
.desc("Defines the world-save folder that will be rendered")
|
||||
.build()
|
||||
);
|
||||
options.addOption(
|
||||
Option.builder("f")
|
||||
.longOpt("force-render")
|
||||
.desc("Rerenders all tiles even if there are no changes since the last render")
|
||||
.build()
|
||||
);
|
||||
options.addOption(
|
||||
Option.builder("r")
|
||||
.longOpt("resource")
|
||||
.hasArg()
|
||||
.argName("file")
|
||||
.desc("Defines the resourcepack that will be used to render the map")
|
||||
.build()
|
||||
);
|
||||
options.addOption(
|
||||
Option.builder("t")
|
||||
.longOpt("threads")
|
||||
.hasArg()
|
||||
.argName("thread-count")
|
||||
.desc("Defines the number of threads that will be used to render the map. Default is the number of system cores")
|
||||
.build()
|
||||
);
|
||||
options.addOption(
|
||||
Option.builder("I")
|
||||
.longOpt("id")
|
||||
.hasArg()
|
||||
.argName("id")
|
||||
.desc("The id of the world. Default is the name of the world-folder")
|
||||
.build()
|
||||
);
|
||||
options.addOption(
|
||||
Option.builder("n")
|
||||
.longOpt("name")
|
||||
.hasArg()
|
||||
.argName("name")
|
||||
.desc("The name of the world. Default is the world-name defined in the level.dat")
|
||||
.build()
|
||||
);
|
||||
options.addOption(
|
||||
Option.builder()
|
||||
.longOpt("render-all")
|
||||
.desc("Also renders blocks that are normally omitted due to a sunlight value of 0. Enabling this can cause a big performance impact in the web-viewer, but it might fix some cases where blocks are missing.")
|
||||
.build()
|
||||
);
|
||||
options.addOption(
|
||||
Option.builder("ao")
|
||||
.longOpt("ambient-occlusion")
|
||||
.hasArg()
|
||||
.argName("value")
|
||||
.desc("The strength of ambient-occlusion baked into the model (a value between 0 and 1). Default is 0.25")
|
||||
.build()
|
||||
);
|
||||
options.addOption(
|
||||
Option.builder("l")
|
||||
.longOpt("lighting")
|
||||
.hasArg()
|
||||
.argName("value")
|
||||
.desc("The max strength of shadows baked into the model (a value between 0 and 1 where 0 is fully bright (no lighting) and 1 is max lighting-contrast). Default is 0.8")
|
||||
.build()
|
||||
);
|
||||
options.addOption(
|
||||
Option.builder("ys")
|
||||
.longOpt("y-slice")
|
||||
.hasArg()
|
||||
.argName("value")
|
||||
.desc("Using this, BlueMap pretends that every Block above the defined value is AIR. Default is disabled")
|
||||
.build()
|
||||
);
|
||||
options.addOption(
|
||||
Option.builder("yM")
|
||||
.longOpt("y-max")
|
||||
.hasArg()
|
||||
.argName("value")
|
||||
.desc("Blocks above this height will not be rendered. Default is no limit")
|
||||
.build()
|
||||
);
|
||||
options.addOption(
|
||||
Option.builder("ym")
|
||||
.longOpt("y-min")
|
||||
.hasArg()
|
||||
.argName("value")
|
||||
.desc("Blocks below this height will not be rendered. Default is no limit")
|
||||
.build()
|
||||
);
|
||||
|
||||
options.addOption(
|
||||
Option.builder()
|
||||
.longOpt("hr-tilesize")
|
||||
.hasArg()
|
||||
.argName("value")
|
||||
.desc("Defines the size of one map-tile in blocks. If you change this value, the lowres values might need adjustment as well! Default is 32")
|
||||
.build()
|
||||
);
|
||||
options.addOption(
|
||||
Option.builder()
|
||||
.longOpt("hr-viewdist")
|
||||
.hasArg()
|
||||
.argName("value")
|
||||
.desc("The View-Distance for hires tiles on the web-map (the value is the radius in tiles). Default is 6")
|
||||
.build()
|
||||
);
|
||||
options.addOption(
|
||||
Option.builder()
|
||||
.longOpt("lr-tilesize")
|
||||
.hasArg()
|
||||
.argName("value")
|
||||
.desc("Defines the size of one lowres-map-tile in grid-points. Default is 50")
|
||||
.build()
|
||||
);
|
||||
options.addOption(
|
||||
Option.builder()
|
||||
.longOpt("lr-resolution")
|
||||
.hasArg()
|
||||
.argName("value")
|
||||
.desc("Defines resolution of the lowres model. E.g. If the hires.tileSize is 32, a value of 4 means that every 8*8 blocks will be summarized by one point on the lowres map. Calculation: 32 / 4 = 8! You have to use values that result in an integer if you use the above calculation! Default is 4")
|
||||
.build()
|
||||
);
|
||||
options.addOption(
|
||||
Option.builder()
|
||||
.longOpt("lr-viewdist")
|
||||
.hasArg()
|
||||
.argName("value")
|
||||
.desc("The View-Distance for lowres tiles on the web-map (the value is the radius in tiles). Default is 5")
|
||||
.build()
|
||||
);
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
private static void printHelp() {
|
||||
HelpFormatter formatter = new HelpFormatter();
|
||||
|
||||
String filename = "bluemapcli.jar";
|
||||
try {
|
||||
File file = new File(BlueMapCLI.class.getProtectionDomain()
|
||||
.getCodeSource()
|
||||
.getLocation()
|
||||
.getPath());
|
||||
|
||||
if (file.isFile()) {
|
||||
try {
|
||||
filename = "./" + new File(".").toPath().relativize(file.toPath()).toString();
|
||||
} catch (IllegalArgumentException ex) {
|
||||
filename = file.getAbsolutePath();
|
||||
}
|
||||
}
|
||||
} catch (Exception ex) {}
|
||||
|
||||
String command = "java -jar " + filename;
|
||||
|
||||
formatter.printHelp(command + " [options]", "\nOptions:", createOptions(), "\n"
|
||||
+ "Examples:\n\n"
|
||||
+ command + " -w ./world/\n"
|
||||
+ " -> Renders the whole world to ./web/data/\n\n"
|
||||
+ command + " -csi localhost\n"
|
||||
+ " -> Creates all neccesary web-files in ./web/ and starts the webserver. (Open http://localhost:8100/ in your browser)"
|
||||
);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,176 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.cli;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Deque;
|
||||
|
||||
import org.apache.commons.lang3.time.DurationFormatUtils;
|
||||
|
||||
import com.flowpowered.math.GenericMath;
|
||||
import com.flowpowered.math.vector.Vector2d;
|
||||
import com.flowpowered.math.vector.Vector2i;
|
||||
|
||||
import de.bluecolored.bluemap.core.logger.Logger;
|
||||
import de.bluecolored.bluemap.core.render.TileRenderer;
|
||||
import de.bluecolored.bluemap.core.render.WorldTile;
|
||||
import de.bluecolored.bluemap.core.world.ChunkNotGeneratedException;
|
||||
import de.bluecolored.bluemap.core.world.World;
|
||||
|
||||
public class RenderManager extends Thread {
|
||||
|
||||
private World world;
|
||||
private TileRenderer tileRenderer;
|
||||
private Deque<Vector2i> tilesToRender;
|
||||
|
||||
private int tileCount;
|
||||
private long startTime = -1;
|
||||
private int renderedTiles = 0;
|
||||
|
||||
private Thread[] threads;
|
||||
|
||||
private Runnable onFinished;
|
||||
|
||||
public RenderManager(World world, TileRenderer tileRenderer, Collection<Vector2i> tilesToRender, int threadCount) {
|
||||
this.world = world;
|
||||
this.tileRenderer = tileRenderer;
|
||||
|
||||
//Sort the chunks to opimize the chunk-cache usage of MCAWorld and generate the world in a nicer order, so you can see the first results early in the web-map during render
|
||||
Vector2d sortGridSize = new Vector2d(20, 20).div(tileRenderer.getHiresModelManager().getTileSize().toDouble().div(16)).ceil().max(1, 1); //Find a good grid size to match the MCAWorlds chunk-cache size of 500
|
||||
ArrayList<Vector2i> sortedTiles = new ArrayList<>(tilesToRender);
|
||||
sortedTiles.sort((v1, v2) -> {
|
||||
Vector2i v1SortGridPos = v1.toDouble().div(sortGridSize).floor().toInt();
|
||||
Vector2i v2SortGridPos = v2.toDouble().div(sortGridSize).floor().toInt();
|
||||
|
||||
if (v1SortGridPos != v2SortGridPos){
|
||||
int v1Dist = v1SortGridPos.distanceSquared(Vector2i.ZERO);
|
||||
int v2Dist = v2SortGridPos.distanceSquared(Vector2i.ZERO);
|
||||
|
||||
if (v1Dist < v2Dist) return -1;
|
||||
if (v1Dist > v2Dist) return 1;
|
||||
}
|
||||
|
||||
if (v1.getY() < v1.getY()) return -1;
|
||||
if (v1.getY() > v1.getY()) return 1;
|
||||
if (v1.getX() < v1.getX()) return -1;
|
||||
if (v1.getX() > v1.getX()) return 1;
|
||||
|
||||
return 0;
|
||||
});
|
||||
|
||||
this.tilesToRender = new ArrayDeque<>(sortedTiles);
|
||||
|
||||
this.tileCount = this.tilesToRender.size();
|
||||
this.threads = new Thread[threadCount];
|
||||
|
||||
|
||||
}
|
||||
|
||||
public synchronized void start(Runnable onFinished) {
|
||||
this.onFinished = onFinished;
|
||||
|
||||
start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
this.startTime = System.currentTimeMillis();
|
||||
|
||||
for (int i = 0; i < threads.length; i++) {
|
||||
if (threads[i] != null) threads[i].interrupt();
|
||||
|
||||
threads[i] = new Thread(this::renderThread);
|
||||
threads[i].start();
|
||||
}
|
||||
|
||||
long lastLogUpdate = startTime;
|
||||
long lastSave = startTime;
|
||||
|
||||
while (!Thread.interrupted()) {
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException e) {
|
||||
break;
|
||||
}
|
||||
|
||||
boolean stillRendering = false;
|
||||
for (Thread t : threads) {
|
||||
if (t.isAlive()) {
|
||||
stillRendering = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!stillRendering) break;
|
||||
|
||||
long now = System.currentTimeMillis();
|
||||
if (lastLogUpdate < now - 10000) { // print update all 10 seconds
|
||||
lastLogUpdate = now;
|
||||
|
||||
long time = now - startTime;
|
||||
String durationString = DurationFormatUtils.formatDurationWords(time, true, true);
|
||||
double pct = (double)renderedTiles / (double)tileCount;
|
||||
|
||||
long ert = (long)(((double) time / pct) * (1d - pct));
|
||||
String ertDurationString = DurationFormatUtils.formatDurationWords(ert, true, true);
|
||||
|
||||
Logger.global.logInfo("Rendered " + renderedTiles + " of " + tileCount + " tiles in " + durationString);
|
||||
Logger.global.logInfo(GenericMath.round(pct * 100, 3) + "% | Estimated remaining time: " + ertDurationString);
|
||||
}
|
||||
|
||||
if (lastSave < now - 5 * 60000) { // save every 5 minutes
|
||||
lastSave = now;
|
||||
tileRenderer.save();
|
||||
}
|
||||
}
|
||||
|
||||
tileRenderer.save();
|
||||
|
||||
onFinished.run();
|
||||
}
|
||||
|
||||
private void renderThread() {
|
||||
Vector2i tilePos;
|
||||
|
||||
while (!Thread.interrupted()) {
|
||||
synchronized (tilesToRender) {
|
||||
if (tilesToRender.isEmpty()) break;
|
||||
tilePos = tilesToRender.poll();
|
||||
}
|
||||
|
||||
WorldTile tile = new WorldTile(world, tilePos);
|
||||
try {
|
||||
tileRenderer.render(tile);
|
||||
} catch (IOException e) {
|
||||
Logger.global.logError("Failed to render tile " + tilePos, e);
|
||||
} catch (ChunkNotGeneratedException e) {}
|
||||
|
||||
renderedTiles++;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
21
BlueMapCore/build.gradle
Normal file
21
BlueMapCore/build.gradle
Normal file
@ -0,0 +1,21 @@
|
||||
dependencies {
|
||||
compile 'com.google.guava:guava:21.0'
|
||||
compile 'com.google.code.gson:gson:2.8.0'
|
||||
compile 'org.apache.commons:commons-lang3:3.5'
|
||||
compile group: 'commons-io', name: 'commons-io', version: '2.6'
|
||||
compile 'com.flowpowered:flow-math:1.0.3'
|
||||
compile 'ninja.leaping.configurate:configurate-hocon:3.3'
|
||||
compile 'ninja.leaping.configurate:configurate-gson:3.3'
|
||||
compile 'com.github.Querz:NBT:4.0'
|
||||
compile group: 'commons-cli', name: 'commons-cli', version: '1.4'
|
||||
}
|
||||
|
||||
task zipWebroot(type: Zip) {
|
||||
from fileTree('src/main/webroot')
|
||||
archiveName 'webroot.zip'
|
||||
destinationDir(file('/src/main/resources/'))
|
||||
outputs.upToDateWhen { false }
|
||||
}
|
||||
|
||||
//always update the zip before build
|
||||
compileJava.dependsOn(zipWebroot)
|
@ -0,0 +1,69 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.logger;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
public abstract class AbstractLogger extends Logger {
|
||||
|
||||
private Set<String> noFloodLog;
|
||||
|
||||
public AbstractLogger() {
|
||||
noFloodLog = Sets.newConcurrentHashSet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void noFloodError(String key, String message, Throwable throwable){
|
||||
if (noFloodLog.add(key)) logError(message, throwable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void noFloodWarning(String key, String message){
|
||||
if (noFloodLog.add(key)) logWarning(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void noFloodInfo(String key, String message){
|
||||
if (noFloodLog.add(key)) logInfo(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void noFloodDebug(String key, String message){
|
||||
if (noFloodLog.add(key)) logDebug(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearNoFloodLog() {
|
||||
noFloodLog.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeNoFloodKey(String key) {
|
||||
noFloodLog.remove(key);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,99 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.logger;
|
||||
|
||||
public abstract class Logger {
|
||||
|
||||
public static Logger global = stdOut();
|
||||
|
||||
public abstract void logError(String message, Throwable throwable);
|
||||
|
||||
public abstract void logWarning(String message);
|
||||
|
||||
public abstract void logInfo(String message);
|
||||
|
||||
public abstract void logDebug(String message);
|
||||
|
||||
/**
|
||||
* Only log the error if no message has been logged before with the same key.
|
||||
*/
|
||||
public abstract void noFloodError(String key, String message, Throwable throwable);
|
||||
|
||||
/**
|
||||
* Only log the warning if no message has been logged before with the same key.
|
||||
*/
|
||||
public abstract void noFloodWarning(String key, String message);
|
||||
|
||||
/**
|
||||
* Only log the info if no message has been logged before with the same key.
|
||||
*/
|
||||
public abstract void noFloodInfo(String key, String message);
|
||||
|
||||
/**
|
||||
* Only log the debug-message if no message has been logged before with the same key.
|
||||
*/
|
||||
public abstract void noFloodDebug(String key, String message);
|
||||
|
||||
/**
|
||||
* Only log the error if no message has been logged before with the same content.
|
||||
*/
|
||||
public void noFloodError(String message, Throwable throwable){
|
||||
noFloodError(message, message, throwable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Only log the warning if no message has been logged before with the same content.
|
||||
*/
|
||||
public void noFloodWarning(String message){
|
||||
noFloodWarning(message, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Only log the info if no message has been logged before with the same content.
|
||||
*/
|
||||
public void noFloodInfo(String message){
|
||||
noFloodInfo(message, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Only log the debug-message if no message has been logged before with the same content.
|
||||
*/
|
||||
public void noFloodDebug(String message){
|
||||
noFloodDebug(message, message);
|
||||
}
|
||||
|
||||
public abstract void clearNoFloodLog();
|
||||
|
||||
public abstract void removeNoFloodKey(String key);
|
||||
|
||||
public void removeNoFloodMessage(String message){
|
||||
removeNoFloodKey(message);
|
||||
}
|
||||
|
||||
public static Logger stdOut(){
|
||||
return new PrintStreamLogger(System.out, System.err);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.logger;
|
||||
|
||||
import java.io.PrintStream;
|
||||
|
||||
public class PrintStreamLogger extends AbstractLogger {
|
||||
|
||||
private PrintStream out, err;
|
||||
|
||||
public PrintStreamLogger(PrintStream out, PrintStream err) {
|
||||
this.out = out;
|
||||
this.err = err;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logError(String message, Throwable throwable) {
|
||||
err.println("[ERROR] " + message);
|
||||
throwable.printStackTrace(err);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logWarning(String message) {
|
||||
out.println("[WARNING] " + message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logInfo(String message) {
|
||||
out.println("[INFO] " + message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logDebug(String message) {
|
||||
out.println("[DEBUG] " + message);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.logger;
|
||||
|
||||
public class VoidLogger extends Logger {
|
||||
|
||||
@Override
|
||||
public void logError(String message, Throwable throwable) {}
|
||||
|
||||
@Override
|
||||
public void logWarning(String message) {}
|
||||
|
||||
@Override
|
||||
public void logInfo(String message) {}
|
||||
|
||||
@Override
|
||||
public void logDebug(String message) {}
|
||||
|
||||
@Override
|
||||
public void noFloodError(String key, String message, Throwable throwable) {}
|
||||
|
||||
@Override
|
||||
public void noFloodWarning(String key, String message) {}
|
||||
|
||||
@Override
|
||||
public void noFloodInfo(String key, String message) {}
|
||||
|
||||
@Override
|
||||
public void noFloodDebug(String key, String message) {}
|
||||
|
||||
@Override
|
||||
public void clearNoFloodLog() {}
|
||||
|
||||
@Override
|
||||
public void removeNoFloodKey(String key) {}
|
||||
|
||||
}
|
@ -0,0 +1,75 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.mca;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.flowpowered.math.vector.Vector2i;
|
||||
import com.flowpowered.math.vector.Vector3i;
|
||||
|
||||
import de.bluecolored.bluemap.core.mca.mapping.LightData;
|
||||
import de.bluecolored.bluemap.core.world.BlockState;
|
||||
import net.querz.nbt.CompoundTag;
|
||||
|
||||
public abstract class Chunk {
|
||||
|
||||
private final MCAWorld world;
|
||||
private final Vector2i chunkPos;
|
||||
|
||||
protected Chunk(MCAWorld world, CompoundTag chunkTag) {
|
||||
this.world = world;
|
||||
|
||||
CompoundTag levelData = chunkTag.getCompoundTag("Level");
|
||||
|
||||
chunkPos = new Vector2i(
|
||||
levelData.getInt("xPos"),
|
||||
levelData.getInt("zPos")
|
||||
);
|
||||
}
|
||||
|
||||
public abstract boolean isGenerated();
|
||||
|
||||
public Vector2i getChunkPos() {
|
||||
return chunkPos;
|
||||
}
|
||||
|
||||
public MCAWorld getWorld() {
|
||||
return world;
|
||||
}
|
||||
|
||||
public abstract BlockState getBlockState(Vector3i pos);
|
||||
|
||||
public abstract LightData getLightData(Vector3i pos);
|
||||
|
||||
public abstract String getBiomeId(Vector3i pos);
|
||||
|
||||
public static Chunk create(MCAWorld world, CompoundTag chunkTag) throws IOException {
|
||||
int version = chunkTag.getInt("DataVersion");
|
||||
|
||||
if (version <= 1343) return new ChunkAnvil112(world, chunkTag);
|
||||
return new ChunkAnvil113(world, chunkTag);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,172 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.mca;
|
||||
|
||||
import com.flowpowered.math.vector.Vector3i;
|
||||
|
||||
import de.bluecolored.bluemap.core.mca.mapping.BiomeIdMapper;
|
||||
import de.bluecolored.bluemap.core.mca.mapping.BlockIdMapper;
|
||||
import de.bluecolored.bluemap.core.mca.mapping.LightData;
|
||||
import de.bluecolored.bluemap.core.world.BlockState;
|
||||
import net.querz.nbt.CompoundTag;
|
||||
import net.querz.nbt.ListTag;
|
||||
import net.querz.nbt.mca.MCAUtil;
|
||||
|
||||
class ChunkAnvil112 extends Chunk {
|
||||
private BlockIdMapper blockIdMapper;
|
||||
private BiomeIdMapper biomeIdMapper;
|
||||
|
||||
private boolean isGenerated;
|
||||
private Section[] sections;
|
||||
private byte[] biomes;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public ChunkAnvil112(MCAWorld world, CompoundTag chunkTag) {
|
||||
super(world, chunkTag);
|
||||
|
||||
blockIdMapper = getWorld().getBlockIdMapper();
|
||||
biomeIdMapper = getWorld().getBiomeIdMapper();
|
||||
|
||||
CompoundTag levelData = chunkTag.getCompoundTag("Level");
|
||||
|
||||
isGenerated =
|
||||
levelData.getBoolean("LightPopulated") &&
|
||||
levelData.getBoolean("TerrainPopulated");
|
||||
|
||||
sections = new Section[32]; //32 supports a max world-height of 512 which is the max that the hightmaps of Minecraft V1.13+ can store with 9 bits, i believe?
|
||||
for (CompoundTag sectionTag : ((ListTag<CompoundTag>) levelData.getListTag("Sections"))) {
|
||||
Section section = new Section(sectionTag);
|
||||
sections[section.getSectionY()] = section;
|
||||
}
|
||||
|
||||
biomes = levelData.getByteArray("Biomes");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isGenerated() {
|
||||
return isGenerated;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockState getBlockState(Vector3i pos) {
|
||||
int sectionY = MCAUtil.blockToChunk(pos.getY());
|
||||
|
||||
Section section = this.sections[sectionY];
|
||||
if (section == null) return BlockState.AIR;
|
||||
|
||||
return section.getBlockState(pos);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LightData getLightData(Vector3i pos) {
|
||||
int sectionY = MCAUtil.blockToChunk(pos.getY());
|
||||
|
||||
Section section = this.sections[sectionY];
|
||||
if (section == null) return LightData.FULL;
|
||||
|
||||
return section.getLightData(pos);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBiomeId(Vector3i pos) {
|
||||
int x = pos.getX() & 0xF; // Math.floorMod(pos.getX(), 16)
|
||||
int z = pos.getZ() & 0xF;
|
||||
int biomeByteIndex = z * 16 + x;
|
||||
|
||||
return biomeIdMapper.get(biomes[biomeByteIndex]);
|
||||
}
|
||||
|
||||
private class Section {
|
||||
private int sectionY;
|
||||
private byte[] blocks;
|
||||
private byte[] add;
|
||||
private byte[] blockLight;
|
||||
private byte[] skyLight;
|
||||
private byte[] data;
|
||||
|
||||
public Section(CompoundTag sectionData) {
|
||||
this.sectionY = sectionData.getByte("Y");
|
||||
this.blocks = sectionData.getByteArray("Blocks");
|
||||
this.add = sectionData.getByteArray("Add");
|
||||
this.blockLight = sectionData.getByteArray("BlockLight");
|
||||
this.skyLight = sectionData.getByteArray("SkyLight");
|
||||
this.data = sectionData.getByteArray("Data");
|
||||
}
|
||||
|
||||
public int getSectionY() {
|
||||
return sectionY;
|
||||
}
|
||||
|
||||
public BlockState getBlockState(Vector3i pos) {
|
||||
int x = pos.getX() & 0xF; // Math.floorMod(pos.getX(), 16)
|
||||
int y = pos.getY() & 0xF;
|
||||
int z = pos.getZ() & 0xF;
|
||||
int blockByteIndex = y * 256 + z * 16 + x;
|
||||
int blockHalfByteIndex = blockByteIndex >> 1; // blockByteIndex / 2
|
||||
boolean largeHalf = (blockByteIndex & 0x1) != 0; // (blockByteIndex % 2) == 0
|
||||
|
||||
int blockId = this.blocks[blockByteIndex] & 0xFF;
|
||||
|
||||
if (this.add.length > 0) {
|
||||
blockId = blockId & (getByteHalf(this.add[blockHalfByteIndex], largeHalf) << 8);
|
||||
}
|
||||
|
||||
int blockData = getByteHalf(this.data[blockHalfByteIndex], largeHalf);
|
||||
|
||||
BlockState blockState = blockIdMapper.get(blockId, blockData);
|
||||
|
||||
return blockState;
|
||||
}
|
||||
|
||||
public LightData getLightData(Vector3i pos) {
|
||||
int x = pos.getX() & 0xF; // Math.floorMod(pos.getX(), 16)
|
||||
int y = pos.getY() & 0xF;
|
||||
int z = pos.getZ() & 0xF;
|
||||
int blockByteIndex = y * 256 + z * 16 + x;
|
||||
int blockHalfByteIndex = blockByteIndex >> 1; // blockByteIndex / 2
|
||||
boolean largeHalf = (blockByteIndex & 0x1) != 0; // (blockByteIndex % 2) == 0
|
||||
|
||||
int blockLight = getByteHalf(this.blockLight[blockHalfByteIndex], largeHalf);
|
||||
int skyLight = getByteHalf(this.skyLight[blockHalfByteIndex], largeHalf);
|
||||
|
||||
return new LightData(skyLight, blockLight);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the 4 bits of the left (largeHalf = <code>true</code>) or the right (largeHalf = <code>false</code>) side of the byte stored in <code>value</code>.<br>
|
||||
* The value is treated as an unsigned byte.
|
||||
*/
|
||||
private int getByteHalf(int value, boolean largeHalf) {
|
||||
value = value & 0xFF;
|
||||
if (largeHalf) {
|
||||
value = value >> 4;
|
||||
}
|
||||
value = value & 0xF;
|
||||
return value;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,224 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.mca;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import com.flowpowered.math.vector.Vector3i;
|
||||
|
||||
import de.bluecolored.bluemap.core.logger.Logger;
|
||||
import de.bluecolored.bluemap.core.mca.mapping.BiomeIdMapper;
|
||||
import de.bluecolored.bluemap.core.mca.mapping.LightData;
|
||||
import de.bluecolored.bluemap.core.world.BlockState;
|
||||
import net.querz.nbt.ByteArrayTag;
|
||||
import net.querz.nbt.CompoundTag;
|
||||
import net.querz.nbt.IntArrayTag;
|
||||
import net.querz.nbt.ListTag;
|
||||
import net.querz.nbt.StringTag;
|
||||
import net.querz.nbt.Tag;
|
||||
import net.querz.nbt.mca.MCAUtil;
|
||||
|
||||
class ChunkAnvil113 extends Chunk {
|
||||
private BiomeIdMapper biomeIdMapper;
|
||||
|
||||
private boolean isGenerated;
|
||||
private Section[] sections;
|
||||
private int[] biomes;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public ChunkAnvil113(MCAWorld world, CompoundTag chunkTag) {
|
||||
super(world, chunkTag);
|
||||
|
||||
biomeIdMapper = getWorld().getBiomeIdMapper();
|
||||
|
||||
CompoundTag levelData = chunkTag.getCompoundTag("Level");
|
||||
|
||||
String status = levelData.getString("Status");
|
||||
isGenerated = status.equals("full") || status.equals("spawn"); // full is normal fully generated and spawn seems to be converted from old format but not yet loaded if you optimized your world
|
||||
|
||||
sections = new Section[32]; //32 supports a max world-height of 512 which is the max that the hightmaps of Minecraft V1.13+ can store with 9 bits, i believe?
|
||||
if (levelData.containsKey("Sections")) {
|
||||
for (CompoundTag sectionTag : ((ListTag<CompoundTag>) levelData.getListTag("Sections"))) {
|
||||
Section section = new Section(sectionTag);
|
||||
if (section.getSectionY() >= 0) sections[section.getSectionY()] = section;
|
||||
}
|
||||
}
|
||||
|
||||
Tag<?> tag = levelData.get("Biomes"); //tag can be byte-array or int-array
|
||||
if (tag instanceof ByteArrayTag) {
|
||||
byte[] bs = ((ByteArrayTag) tag).getValue();
|
||||
biomes = new int[bs.length];
|
||||
|
||||
for (int i = 0; i < bs.length; i++) {
|
||||
biomes[i] = bs[i];
|
||||
}
|
||||
}
|
||||
else if (tag instanceof IntArrayTag) {
|
||||
biomes = ((IntArrayTag) tag).getValue();
|
||||
}
|
||||
else {
|
||||
biomes = new int[2048];
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isGenerated() {
|
||||
return isGenerated;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockState getBlockState(Vector3i pos) {
|
||||
int sectionY = MCAUtil.blockToChunk(pos.getY());
|
||||
|
||||
Section section = this.sections[sectionY];
|
||||
if (section == null) return BlockState.AIR;
|
||||
|
||||
return section.getBlockState(pos);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LightData getLightData(Vector3i pos) {
|
||||
int sectionY = MCAUtil.blockToChunk(pos.getY());
|
||||
|
||||
Section section = this.sections[sectionY];
|
||||
if (section == null) return LightData.FULL;
|
||||
|
||||
return section.getLightData(pos);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBiomeId(Vector3i pos) {
|
||||
int x = pos.getX() & 0xF; // Math.floorMod(pos.getX(), 16)
|
||||
int z = pos.getZ() & 0xF;
|
||||
int biomeByteIndex = z * 16 + x;
|
||||
|
||||
return biomeIdMapper.get(biomes[biomeByteIndex]);
|
||||
}
|
||||
|
||||
private class Section {
|
||||
private int sectionY;
|
||||
private byte[] blockLight;
|
||||
private byte[] skyLight;
|
||||
private long[] blocks;
|
||||
private BlockState[] palette;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public Section(CompoundTag sectionData) {
|
||||
this.sectionY = sectionData.getByte("Y");
|
||||
this.blockLight = sectionData.getByteArray("BlockLight");
|
||||
if (blockLight.length == 0) blockLight = new byte[2048];
|
||||
this.skyLight = sectionData.getByteArray("SkyLight");
|
||||
if (skyLight.length == 0) skyLight = new byte[2048];
|
||||
this.blocks = sectionData.getLongArray("BlockStates");
|
||||
|
||||
//read block palette
|
||||
ListTag<CompoundTag> paletteTag = (ListTag<CompoundTag>) sectionData.getListTag("Palette");
|
||||
if (paletteTag != null) {
|
||||
this.palette = new BlockState[paletteTag.size()];
|
||||
for (int i = 0; i < this.palette.length; i++) {
|
||||
CompoundTag stateTag = paletteTag.get(i);
|
||||
|
||||
String id = stateTag.getString("Name");
|
||||
Map<String, String> properties = new HashMap<>();
|
||||
|
||||
if (stateTag.containsKey("Properties")) {
|
||||
CompoundTag propertiesTag = stateTag.getCompoundTag("Properties");
|
||||
for (Entry<String, Tag<?>> property : propertiesTag) {
|
||||
properties.put(property.getKey(), ((StringTag) property.getValue()).getValue());
|
||||
}
|
||||
}
|
||||
|
||||
palette[i] = new BlockState(id, properties);
|
||||
}
|
||||
} else {
|
||||
this.palette = new BlockState[0];
|
||||
}
|
||||
}
|
||||
|
||||
public int getSectionY() {
|
||||
return sectionY;
|
||||
}
|
||||
|
||||
public BlockState getBlockState(Vector3i pos) {
|
||||
if (blocks.length == 0) return BlockState.AIR;
|
||||
|
||||
int x = pos.getX() & 0xF; // Math.floorMod(pos.getX(), 16)
|
||||
int y = pos.getY() & 0xF;
|
||||
int z = pos.getZ() & 0xF;
|
||||
int blockIndex = y * 256 + z * 16 + x;
|
||||
int bitsPerBlock = blocks.length * 64 / 4096; //64 bits per long and 4096 blocks per section
|
||||
int index = blockIndex * bitsPerBlock;
|
||||
int firstLong = index >> 6; // index / 64
|
||||
int bitoffset = index & 0x3F; // Math.floorMod(index, 64)
|
||||
|
||||
long value = blocks[firstLong] >>> bitoffset;
|
||||
|
||||
if (bitoffset > 0 && firstLong + 1 < blocks.length) {
|
||||
long value2 = blocks[firstLong + 1];
|
||||
value2 = value2 << -bitoffset;
|
||||
value = value | value2;
|
||||
}
|
||||
|
||||
value = value & (0xFFFFFFFFFFFFFFFFL >>> -bitsPerBlock);
|
||||
|
||||
if (value >= palette.length) {
|
||||
Logger.global.noFloodWarning("palettewarning", "Got palette value " + value + " but palette has size of " + palette.length + " (Future occasions of this error will not be logged)");
|
||||
return BlockState.AIR;
|
||||
}
|
||||
|
||||
return palette[(int) value];
|
||||
}
|
||||
|
||||
public LightData getLightData(Vector3i pos) {
|
||||
int x = pos.getX() & 0xF; // Math.floorMod(pos.getX(), 16)
|
||||
int y = pos.getY() & 0xF;
|
||||
int z = pos.getZ() & 0xF;
|
||||
int blockByteIndex = y * 256 + z * 16 + x;
|
||||
int blockHalfByteIndex = blockByteIndex >> 1; // blockByteIndex / 2
|
||||
boolean largeHalf = (blockByteIndex & 0x1) != 0; // (blockByteIndex % 2) == 0
|
||||
|
||||
int blockLight = getByteHalf(this.blockLight[blockHalfByteIndex], largeHalf);
|
||||
int skyLight = getByteHalf(this.skyLight[blockHalfByteIndex], largeHalf);
|
||||
|
||||
return new LightData(skyLight, blockLight);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the 4 bits of the left (largeHalf = <code>true</code>) or the right (largeHalf = <code>false</code>) side of the byte stored in <code>value</code>.<br>
|
||||
* The value is treated as an unsigned byte.
|
||||
*/
|
||||
private int getByteHalf(int value, boolean largeHalf) {
|
||||
value = value & 0xFF;
|
||||
if (largeHalf) {
|
||||
value = value >> 4;
|
||||
}
|
||||
value = value & 0xF;
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,92 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.mca;
|
||||
|
||||
import com.flowpowered.math.vector.Vector3i;
|
||||
|
||||
import de.bluecolored.bluemap.core.mca.mapping.BlockProperties;
|
||||
import de.bluecolored.bluemap.core.mca.mapping.LightData;
|
||||
import de.bluecolored.bluemap.core.world.Block;
|
||||
import de.bluecolored.bluemap.core.world.BlockState;
|
||||
|
||||
public class MCABlock extends Block {
|
||||
|
||||
private MCAWorld world;
|
||||
private BlockState blockState;
|
||||
private LightData lightData;
|
||||
private String biome;
|
||||
private BlockProperties properties;
|
||||
private Vector3i pos;
|
||||
|
||||
public MCABlock(MCAWorld world, BlockState blockState, LightData lightData, String biome, BlockProperties properties, Vector3i pos) {
|
||||
this.world = world;
|
||||
this.blockState = blockState;
|
||||
this.lightData = lightData;
|
||||
this.biome = biome;
|
||||
this.properties = properties;
|
||||
this.pos = pos;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockState getBlock() {
|
||||
return blockState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MCAWorld getWorld() {
|
||||
return world;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector3i getPosition() {
|
||||
return pos;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getSunLightLevel() {
|
||||
return lightData.getSkyLight();
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getBlockLightLevel() {
|
||||
return lightData.getBlockLight();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCullingNeighborFaces() {
|
||||
return properties.isCulling();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOccludingNeighborFaces() {
|
||||
return properties.isOccluding();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBiome() {
|
||||
return biome;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,438 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.mca;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
import com.flowpowered.math.vector.Vector2i;
|
||||
import com.flowpowered.math.vector.Vector3i;
|
||||
import com.google.common.cache.Cache;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.google.common.collect.MultimapBuilder;
|
||||
|
||||
import de.bluecolored.bluemap.core.logger.Logger;
|
||||
import de.bluecolored.bluemap.core.mca.extensions.BlockStateExtension;
|
||||
import de.bluecolored.bluemap.core.mca.extensions.DoorExtension;
|
||||
import de.bluecolored.bluemap.core.mca.extensions.DoublePlantExtension;
|
||||
import de.bluecolored.bluemap.core.mca.extensions.FireExtension;
|
||||
import de.bluecolored.bluemap.core.mca.extensions.GlassPaneConnectExtension;
|
||||
import de.bluecolored.bluemap.core.mca.extensions.NetherFenceConnectExtension;
|
||||
import de.bluecolored.bluemap.core.mca.extensions.RedstoneExtension;
|
||||
import de.bluecolored.bluemap.core.mca.extensions.SnowyExtension;
|
||||
import de.bluecolored.bluemap.core.mca.extensions.StairShapeExtension;
|
||||
import de.bluecolored.bluemap.core.mca.extensions.TripwireConnectExtension;
|
||||
import de.bluecolored.bluemap.core.mca.extensions.WallConnectExtension;
|
||||
import de.bluecolored.bluemap.core.mca.extensions.WoodenFenceConnectExtension;
|
||||
import de.bluecolored.bluemap.core.mca.mapping.BiomeIdMapper;
|
||||
import de.bluecolored.bluemap.core.mca.mapping.BlockIdMapper;
|
||||
import de.bluecolored.bluemap.core.mca.mapping.BlockProperties;
|
||||
import de.bluecolored.bluemap.core.mca.mapping.BlockPropertyMapper;
|
||||
import de.bluecolored.bluemap.core.mca.mapping.LightData;
|
||||
import de.bluecolored.bluemap.core.util.AABB;
|
||||
import de.bluecolored.bluemap.core.world.Block;
|
||||
import de.bluecolored.bluemap.core.world.BlockState;
|
||||
import de.bluecolored.bluemap.core.world.ChunkNotGeneratedException;
|
||||
import de.bluecolored.bluemap.core.world.World;
|
||||
import de.bluecolored.bluemap.core.world.WorldChunk;
|
||||
import net.querz.nbt.CompoundTag;
|
||||
import net.querz.nbt.NBTUtil;
|
||||
import net.querz.nbt.Tag;
|
||||
import net.querz.nbt.mca.CompressionType;
|
||||
import net.querz.nbt.mca.MCAUtil;
|
||||
|
||||
public class MCAWorld implements World {
|
||||
|
||||
private static final Cache<WorldChunkHash, Chunk> CHUNK_CACHE = CacheBuilder.newBuilder().maximumSize(500).build();
|
||||
private static final Multimap<String, BlockStateExtension> BLOCK_STATE_EXTENSIONS = MultimapBuilder.hashKeys().arrayListValues().build();
|
||||
|
||||
public static final BlockIdMapper DEFAULT_BLOCK_ID_MAPPER;
|
||||
public static final BlockPropertyMapper DEFAULT_BLOCK_PROPERTY_MAPPER;
|
||||
public static final BiomeIdMapper DEFAULT_BIOME_ID_MAPPER;
|
||||
|
||||
static {
|
||||
try {
|
||||
DEFAULT_BLOCK_ID_MAPPER = BlockIdMapper.create();
|
||||
DEFAULT_BLOCK_PROPERTY_MAPPER = BlockPropertyMapper.create();
|
||||
DEFAULT_BIOME_ID_MAPPER = BiomeIdMapper.create();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Failed to load essential resources!", e);
|
||||
}
|
||||
|
||||
registerBlockStateExtension(new SnowyExtension());
|
||||
registerBlockStateExtension(new StairShapeExtension());
|
||||
registerBlockStateExtension(new FireExtension());
|
||||
registerBlockStateExtension(new RedstoneExtension());
|
||||
registerBlockStateExtension(new DoorExtension());
|
||||
registerBlockStateExtension(new NetherFenceConnectExtension());
|
||||
registerBlockStateExtension(new TripwireConnectExtension());
|
||||
registerBlockStateExtension(new WallConnectExtension());
|
||||
registerBlockStateExtension(new WoodenFenceConnectExtension());
|
||||
registerBlockStateExtension(new GlassPaneConnectExtension());
|
||||
registerBlockStateExtension(new DoublePlantExtension());
|
||||
}
|
||||
|
||||
private final UUID uuid;
|
||||
private final Path worldFolder;
|
||||
private String name;
|
||||
private AABB boundaries;
|
||||
private int seaLevel;
|
||||
private Vector3i spawnPoint;
|
||||
|
||||
private BlockIdMapper blockIdMapper;
|
||||
private BlockPropertyMapper blockPropertyMapper;
|
||||
private BiomeIdMapper biomeIdMapper;
|
||||
|
||||
private MCAWorld(
|
||||
Path worldFolder,
|
||||
UUID uuid,
|
||||
String name,
|
||||
int worldHeight,
|
||||
int seaLevel,
|
||||
Vector3i spawnPoint,
|
||||
BlockIdMapper blockIdMapper,
|
||||
BlockPropertyMapper blockPropertyMapper,
|
||||
BiomeIdMapper biomeIdMapper
|
||||
) {
|
||||
this.uuid = uuid;
|
||||
this.worldFolder = worldFolder;
|
||||
this.name = name;
|
||||
this.boundaries = new AABB(new Vector3i(-10000000, 0, -10000000), new Vector3i(10000000, worldHeight, 10000000));
|
||||
this.seaLevel = seaLevel;
|
||||
this.spawnPoint = spawnPoint;
|
||||
|
||||
this.blockIdMapper = blockIdMapper;
|
||||
this.blockPropertyMapper = blockPropertyMapper;
|
||||
this.biomeIdMapper = biomeIdMapper;
|
||||
}
|
||||
|
||||
public BlockState getBlockState(Vector3i pos) {
|
||||
try {
|
||||
|
||||
Vector2i chunkPos = blockToChunk(pos);
|
||||
Chunk chunk = getChunk(chunkPos);
|
||||
return chunk.getBlockState(pos);
|
||||
|
||||
} catch (Exception ex) {
|
||||
return BlockState.AIR;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Block getBlock(Vector3i pos) throws ChunkNotGeneratedException {
|
||||
try {
|
||||
|
||||
Vector2i chunkPos = blockToChunk(pos);
|
||||
Chunk chunk = getChunk(chunkPos);
|
||||
BlockState blockState = getExtendedBlockState(chunk, pos);
|
||||
LightData lightData = chunk.getLightData(pos);
|
||||
String biome = chunk.getBiomeId(pos);
|
||||
BlockProperties properties = blockPropertyMapper.map(blockState);
|
||||
return new MCABlock(this, blockState, lightData, biome, properties, pos);
|
||||
|
||||
} catch (IOException ex) {
|
||||
throw new ChunkNotGeneratedException(ex); // to resolve the error, act like the chunk has not been generated yet
|
||||
}
|
||||
}
|
||||
|
||||
private BlockState getExtendedBlockState(Chunk chunk, Vector3i pos) throws ChunkNotGeneratedException {
|
||||
BlockState blockState = chunk.getBlockState(pos);
|
||||
|
||||
for (BlockStateExtension ext : BLOCK_STATE_EXTENSIONS.get(blockState.getId())) {
|
||||
blockState = ext.extend(this, pos, blockState);
|
||||
}
|
||||
|
||||
return blockState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AABB getBoundaries() {
|
||||
return boundaries;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WorldChunk getWorldChunk(AABB boundaries) {
|
||||
return new MCAWorldChunk(this, boundaries);
|
||||
}
|
||||
|
||||
public Chunk getChunk(Vector2i chunkPos) throws IOException, ChunkNotGeneratedException {
|
||||
try {
|
||||
Chunk chunk = CHUNK_CACHE.get(new WorldChunkHash(this, chunkPos), () -> this.loadChunk(chunkPos));
|
||||
if (!chunk.isGenerated()) throw new ChunkNotGeneratedException();
|
||||
return chunk;
|
||||
} catch (ExecutionException e) {
|
||||
Throwable cause = e.getCause();
|
||||
|
||||
if (cause instanceof IOException) {
|
||||
throw (IOException) cause;
|
||||
}
|
||||
|
||||
else if (cause instanceof ChunkNotGeneratedException) {
|
||||
throw (ChunkNotGeneratedException) cause;
|
||||
}
|
||||
|
||||
else throw new IOException(cause);
|
||||
}
|
||||
}
|
||||
|
||||
private Chunk loadChunk(Vector2i chunkPos) throws IOException, ChunkNotGeneratedException {
|
||||
Vector2i regionPos = chunkToRegion(chunkPos);
|
||||
Path regionPath = getMCAFilePath(regionPos);
|
||||
|
||||
try (RandomAccessFile raf = new RandomAccessFile(regionPath.toFile(), "r")) {
|
||||
|
||||
int xzChunk = Math.floorMod(chunkPos.getY(), 32) * 32 + Math.floorMod(chunkPos.getX(), 32);
|
||||
|
||||
raf.seek(xzChunk * 4);
|
||||
int offset = raf.read() << 16;
|
||||
offset |= (raf.read() & 0xFF) << 8;
|
||||
offset |= raf.read() & 0xFF;
|
||||
offset *= 4096;
|
||||
|
||||
int size = raf.readByte() * 4096;
|
||||
if (size == 0) throw new ChunkNotGeneratedException();
|
||||
|
||||
raf.seek(offset + 4); // +4 skip chunk size
|
||||
|
||||
byte compressionTypeByte = raf.readByte();
|
||||
CompressionType compressionType = CompressionType.getFromID(compressionTypeByte);
|
||||
if (compressionType == null) {
|
||||
throw new IOException("invalid compression type " + compressionTypeByte);
|
||||
}
|
||||
|
||||
DataInputStream dis = new DataInputStream(new BufferedInputStream(compressionType.decompress(new FileInputStream(raf.getFD()))));
|
||||
Tag<?> tag = Tag.deserialize(dis, Tag.DEFAULT_MAX_DEPTH);
|
||||
if (tag instanceof CompoundTag) {
|
||||
return Chunk.create(this, (CompoundTag) tag);
|
||||
} else {
|
||||
throw new IOException("invalid data tag: " + (tag == null ? "null" : tag.getClass().getName()));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isChunkGenerated(Vector2i chunkPos) {
|
||||
try {
|
||||
getChunk(chunkPos);
|
||||
} catch (ChunkNotGeneratedException | IOException e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Vector2i> getChunkList(long modifiedSinceMillis){
|
||||
List<Vector2i> chunks = new ArrayList<>(10000);
|
||||
|
||||
for (File file : getRegionFolder().toFile().listFiles()) {
|
||||
if (!file.getName().endsWith(".mca")) continue;
|
||||
|
||||
try (RandomAccessFile raf = new RandomAccessFile(file, "r")) {
|
||||
|
||||
String[] filenameParts = file.getName().split("\\.");
|
||||
int rX = Integer.parseInt(filenameParts[1]);
|
||||
int rZ = Integer.parseInt(filenameParts[2]);
|
||||
|
||||
for (int x = 0; x < 32; x++) {
|
||||
for (int z = 0; z < 32; z++) {
|
||||
int xzChunk = z * 32 + x;
|
||||
|
||||
raf.seek(xzChunk * 4 + 3);
|
||||
int size = raf.readByte() * 4096;
|
||||
|
||||
if (size == 0) continue;
|
||||
|
||||
raf.seek(xzChunk * 4 + 4096);
|
||||
int timestamp = raf.read() << 24;
|
||||
timestamp |= (raf.read() & 0xFF) << 16;
|
||||
timestamp |= (raf.read() & 0xFF) << 8;
|
||||
timestamp |= raf.read() & 0xFF;
|
||||
|
||||
if (timestamp >= (modifiedSinceMillis / 1000)) chunks.add(new Vector2i(rX * 32 + x, rZ * 32 + z));
|
||||
}
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
Logger.global.logWarning("Failed to read .mca file: " + file.getAbsolutePath() + " (" + ex.toString() + ")");
|
||||
}
|
||||
}
|
||||
|
||||
return chunks;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getUUID() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSeaLevel() {
|
||||
return seaLevel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector3i getSpawnPoint() {
|
||||
return spawnPoint;
|
||||
}
|
||||
|
||||
public BlockIdMapper getBlockIdMapper() {
|
||||
return blockIdMapper;
|
||||
}
|
||||
|
||||
public BlockPropertyMapper getBlockPropertyMapper() {
|
||||
return blockPropertyMapper;
|
||||
}
|
||||
|
||||
public BiomeIdMapper getBiomeIdMapper() {
|
||||
return biomeIdMapper;
|
||||
}
|
||||
|
||||
public void setBlockIdMapper(BlockIdMapper blockIdMapper) {
|
||||
this.blockIdMapper = blockIdMapper;
|
||||
}
|
||||
|
||||
public void setBlockPropertyMapper(BlockPropertyMapper blockPropertyMapper) {
|
||||
this.blockPropertyMapper = blockPropertyMapper;
|
||||
}
|
||||
|
||||
public void setBiomeIdMapper(BiomeIdMapper biomeIdMapper) {
|
||||
this.biomeIdMapper = biomeIdMapper;
|
||||
}
|
||||
|
||||
public Path getWorldFolder() {
|
||||
return worldFolder;
|
||||
}
|
||||
|
||||
private Path getRegionFolder() {
|
||||
return worldFolder.resolve("region");
|
||||
}
|
||||
|
||||
private Path getMCAFilePath(Vector2i region) {
|
||||
return getRegionFolder().resolve(MCAUtil.createNameFromRegionLocation(region.getX(), region.getY()));
|
||||
}
|
||||
|
||||
public static MCAWorld load(Path worldFolder, UUID uuid) throws IOException {
|
||||
try {
|
||||
CompoundTag level = (CompoundTag) NBTUtil.readTag(worldFolder.resolve("level.dat").toFile());
|
||||
CompoundTag levelData = level.getCompoundTag("Data");
|
||||
|
||||
String name = levelData.getString("LevelName");
|
||||
int worldHeight = 255;
|
||||
int seaLevel = 63;
|
||||
Vector3i spawnPoint = new Vector3i(
|
||||
levelData.getInt("SpawnX"),
|
||||
levelData.getInt("SpawnY"),
|
||||
levelData.getInt("SpawnZ")
|
||||
);
|
||||
|
||||
return new MCAWorld(
|
||||
worldFolder,
|
||||
uuid,
|
||||
name,
|
||||
worldHeight,
|
||||
seaLevel,
|
||||
spawnPoint,
|
||||
DEFAULT_BLOCK_ID_MAPPER,
|
||||
DEFAULT_BLOCK_PROPERTY_MAPPER,
|
||||
DEFAULT_BIOME_ID_MAPPER
|
||||
);
|
||||
} catch (ClassCastException | NullPointerException ex) {
|
||||
throw new IOException("Invaid level.dat format!", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public static Vector2i blockToChunk(Vector3i pos) {
|
||||
return new Vector2i(
|
||||
MCAUtil.blockToChunk(pos.getX()),
|
||||
MCAUtil.blockToChunk(pos.getZ())
|
||||
);
|
||||
}
|
||||
|
||||
public static Vector2i blockToRegion(Vector3i pos) {
|
||||
return new Vector2i(
|
||||
MCAUtil.blockToRegion(pos.getX()),
|
||||
MCAUtil.blockToRegion(pos.getZ())
|
||||
);
|
||||
}
|
||||
|
||||
public static Vector2i chunkToRegion(Vector2i pos) {
|
||||
return new Vector2i(
|
||||
MCAUtil.chunkToRegion(pos.getX()),
|
||||
MCAUtil.chunkToRegion(pos.getY())
|
||||
);
|
||||
}
|
||||
|
||||
public static void registerBlockStateExtension(BlockStateExtension extension) {
|
||||
for (String id : extension.getAffectedBlockIds()) {
|
||||
BLOCK_STATE_EXTENSIONS.put(id, extension);
|
||||
}
|
||||
}
|
||||
|
||||
private static class WorldChunkHash {
|
||||
|
||||
private final UUID world;
|
||||
private final Vector2i chunk;
|
||||
|
||||
public WorldChunkHash(MCAWorld world, Vector2i chunk) {
|
||||
this.world = world.getUUID();
|
||||
this.chunk = chunk;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(world, chunk);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
|
||||
if (obj instanceof WorldChunkHash) {
|
||||
WorldChunkHash other = (WorldChunkHash) obj;
|
||||
return other.chunk.equals(chunk) && world.equals(other.world);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,85 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.mca;
|
||||
|
||||
import com.flowpowered.math.vector.Vector2i;
|
||||
import com.flowpowered.math.vector.Vector3i;
|
||||
|
||||
import de.bluecolored.bluemap.core.util.AABB;
|
||||
import de.bluecolored.bluemap.core.world.Block;
|
||||
import de.bluecolored.bluemap.core.world.ChunkNotGeneratedException;
|
||||
import de.bluecolored.bluemap.core.world.World;
|
||||
import de.bluecolored.bluemap.core.world.WorldChunk;
|
||||
|
||||
public class MCAWorldChunk implements WorldChunk {
|
||||
|
||||
private MCAWorld world;
|
||||
private AABB boundaries, extendedBounds;
|
||||
|
||||
public MCAWorldChunk(MCAWorld world, AABB boundaries) {
|
||||
this.world = world;
|
||||
this.boundaries = boundaries;
|
||||
this.extendedBounds = boundaries.expand(2, 2, 2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public World getWorld() {
|
||||
return world;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Block getBlock(Vector3i pos) throws ChunkNotGeneratedException {
|
||||
return world.getBlock(pos);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AABB getBoundaries() {
|
||||
return boundaries;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WorldChunk getWorldChunk(AABB boundaries) {
|
||||
return new MCAWorldChunk(world, boundaries);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isGenerated() {
|
||||
|
||||
//check one more block in every direction to make sure that extended block states can be generated!
|
||||
Vector2i minChunk = MCAWorld.blockToChunk(extendedBounds.getMin().toInt());
|
||||
Vector2i maxChunk = MCAWorld.blockToChunk(extendedBounds.getMax().toInt());
|
||||
|
||||
for (int x = minChunk.getX(); x <= maxChunk.getX(); x++) {
|
||||
for (int z = minChunk.getY(); z <= maxChunk.getY(); z++) {
|
||||
if (!world.isChunkGenerated(new Vector2i(x, z))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.mca.extensions;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import com.flowpowered.math.vector.Vector3i;
|
||||
|
||||
import de.bluecolored.bluemap.core.mca.MCAWorld;
|
||||
import de.bluecolored.bluemap.core.world.BlockState;
|
||||
|
||||
public interface BlockStateExtension {
|
||||
|
||||
BlockState extend(MCAWorld world, Vector3i pos, BlockState state);
|
||||
|
||||
Collection<String> getAffectedBlockIds();
|
||||
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.mca.extensions;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import com.flowpowered.math.vector.Vector3i;
|
||||
|
||||
import de.bluecolored.bluemap.core.mca.MCAWorld;
|
||||
import de.bluecolored.bluemap.core.util.Direction;
|
||||
import de.bluecolored.bluemap.core.world.BlockState;
|
||||
|
||||
public abstract class ConnectExtension implements BlockStateExtension {
|
||||
|
||||
@Override
|
||||
public BlockState extend(MCAWorld world, Vector3i pos, BlockState state) {
|
||||
return state
|
||||
.with("north", String.valueOf(connectsTo(world, pos.add(Direction.NORTH.toVector()))))
|
||||
.with("east", String.valueOf(connectsTo(world, pos.add(Direction.EAST.toVector()))))
|
||||
.with("south", String.valueOf(connectsTo(world, pos.add(Direction.SOUTH.toVector()))))
|
||||
.with("west", String.valueOf(connectsTo(world, pos.add(Direction.WEST.toVector()))));
|
||||
}
|
||||
|
||||
public boolean connectsTo(MCAWorld world, Vector3i pos) {
|
||||
return connectsTo(world, pos, world.getBlockState(pos));
|
||||
}
|
||||
|
||||
public boolean connectsTo(MCAWorld world, Vector3i pos, BlockState block) {
|
||||
return getAffectedBlockIds().contains(block.getFullId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public abstract Set<String> getAffectedBlockIds();
|
||||
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.mca.extensions;
|
||||
|
||||
import com.flowpowered.math.vector.Vector3i;
|
||||
|
||||
import de.bluecolored.bluemap.core.mca.MCAWorld;
|
||||
import de.bluecolored.bluemap.core.world.BlockState;
|
||||
|
||||
public abstract class ConnectSameOrFullBlockExtension extends ConnectExtension {
|
||||
|
||||
@Override
|
||||
public boolean connectsTo(MCAWorld world, Vector3i pos, BlockState block) {
|
||||
if (super.connectsTo(world, pos, block)) return true;
|
||||
|
||||
return world.getBlockPropertyMapper().map(block).isCulling();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,74 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.mca.extensions;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import com.flowpowered.math.vector.Vector3i;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import de.bluecolored.bluemap.core.mca.MCAWorld;
|
||||
import de.bluecolored.bluemap.core.util.Direction;
|
||||
import de.bluecolored.bluemap.core.world.BlockState;
|
||||
|
||||
public class DoorExtension implements BlockStateExtension {
|
||||
|
||||
private static final Collection<String> AFFECTED_BLOCK_IDS = Lists.newArrayList(
|
||||
"minecraft:wooden_door",
|
||||
"minecraft:iron_door",
|
||||
"minecraft:spruce_door",
|
||||
"minecraft:birch_door",
|
||||
"minecraft:jungle_door",
|
||||
"minecraft:acacia_door",
|
||||
"minecraft:dark_oak_door"
|
||||
);
|
||||
|
||||
@Override
|
||||
public BlockState extend(MCAWorld world, Vector3i pos, BlockState state) {
|
||||
BlockState otherDoor;
|
||||
|
||||
if (state.getProperties().get("half").equals("lower")) {
|
||||
otherDoor = world.getBlockState(pos.add(Direction.UP.toVector()));
|
||||
} else {
|
||||
otherDoor = world.getBlockState(pos.add(Direction.DOWN.toVector()));
|
||||
}
|
||||
|
||||
//copy all properties from the other door
|
||||
for (Entry<String, String> prop : otherDoor.getProperties().entrySet()) {
|
||||
if (!state.getProperties().containsKey(prop.getKey())) {
|
||||
state = state.with(prop.getKey(), prop.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> getAffectedBlockIds() {
|
||||
return AFFECTED_BLOCK_IDS;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.mca.extensions;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import com.flowpowered.math.vector.Vector3i;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import de.bluecolored.bluemap.core.mca.MCAWorld;
|
||||
import de.bluecolored.bluemap.core.util.Direction;
|
||||
import de.bluecolored.bluemap.core.world.BlockState;
|
||||
|
||||
public class DoublePlantExtension implements BlockStateExtension {
|
||||
|
||||
private static final Collection<String> AFFECTED_BLOCK_IDS = Lists.newArrayList(
|
||||
"minecraft:double_plant"
|
||||
);
|
||||
|
||||
@Override
|
||||
public BlockState extend(MCAWorld world, Vector3i pos, BlockState state) {
|
||||
if (state.getProperties().get("half").equals("upper")) {
|
||||
BlockState otherPlant = world.getBlockState(pos.add(Direction.DOWN.toVector()));
|
||||
|
||||
//copy all properties from the other half
|
||||
for (Entry<String, String> prop : otherPlant.getProperties().entrySet()) {
|
||||
if (!state.getProperties().containsKey(prop.getKey())) {
|
||||
state = state.with(prop.getKey(), prop.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> getAffectedBlockIds() {
|
||||
return AFFECTED_BLOCK_IDS;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.mca.extensions;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import com.flowpowered.math.vector.Vector3i;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import de.bluecolored.bluemap.core.mca.MCAWorld;
|
||||
import de.bluecolored.bluemap.core.util.Direction;
|
||||
import de.bluecolored.bluemap.core.world.BlockState;
|
||||
|
||||
public class FireExtension implements BlockStateExtension {
|
||||
|
||||
private static final Collection<String> AFFECTED_BLOCK_IDS = Lists.newArrayList(
|
||||
"minecraft:fire"
|
||||
);
|
||||
|
||||
@Override
|
||||
public BlockState extend(MCAWorld world, Vector3i pos, BlockState state) {
|
||||
BlockState below = world.getBlockState(pos.add(0, -1, 0));
|
||||
|
||||
boolean isOnGround = world.getBlockPropertyMapper().map(below).isCulling();
|
||||
for (Direction dir : Direction.values()) {
|
||||
if (dir != Direction.DOWN) {
|
||||
if (!isOnGround) {
|
||||
BlockState neighbor = world.getBlockState(pos.add(dir.toVector()));
|
||||
|
||||
state = state.with(dir.name().toLowerCase(), String.valueOf(!world.getBlockPropertyMapper().map(neighbor).isCulling()));
|
||||
} else {
|
||||
state = state.with(dir.name().toLowerCase(), "false");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> getAffectedBlockIds() {
|
||||
return AFFECTED_BLOCK_IDS;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.mca.extensions;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
public class GlassPaneConnectExtension extends ConnectSameOrFullBlockExtension {
|
||||
|
||||
private static final HashSet<String> AFFECTED_BLOCK_IDS = Sets.newHashSet(
|
||||
"minecraft:glass_pane",
|
||||
"minecraft:stained_glass_pane",
|
||||
"minecraft:iron_bars"
|
||||
);
|
||||
|
||||
@Override
|
||||
public Set<String> getAffectedBlockIds() {
|
||||
return AFFECTED_BLOCK_IDS;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.mca.extensions;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
public class NetherFenceConnectExtension extends ConnectSameOrFullBlockExtension {
|
||||
|
||||
private static final HashSet<String> AFFECTED_BLOCK_IDS = Sets.newHashSet(
|
||||
"minecraft:nether_brick_fence"
|
||||
);
|
||||
|
||||
@Override
|
||||
public Set<String> getAffectedBlockIds() {
|
||||
return AFFECTED_BLOCK_IDS;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,84 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.mca.extensions;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
|
||||
import com.flowpowered.math.vector.Vector3i;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
import de.bluecolored.bluemap.core.mca.MCAWorld;
|
||||
import de.bluecolored.bluemap.core.util.Direction;
|
||||
import de.bluecolored.bluemap.core.world.BlockState;
|
||||
|
||||
public class RedstoneExtension implements BlockStateExtension {
|
||||
|
||||
private static final Collection<String> AFFECTED_BLOCK_IDS = Lists.newArrayList(
|
||||
"minecraft:redstone_wire"
|
||||
);
|
||||
|
||||
|
||||
private static final Set<String> CONNECTIBLE = Sets.newHashSet(
|
||||
"minecraft:redstone_wire",
|
||||
"minecraft:unlit_redstone_torch",
|
||||
"minecraft:redstone_torch",
|
||||
"minecraft:stone_button",
|
||||
"minecraft:wooden_button",
|
||||
"minecraft:stone_button",
|
||||
"minecraft:lever",
|
||||
"minecraft:stone_pressure_plate",
|
||||
"minecraft:wooden_pressure_plate",
|
||||
"minecraft:light_weighted_pressure_plate",
|
||||
"minecraft:heavy_weighted_pressure_plate"
|
||||
);
|
||||
|
||||
@Override
|
||||
public BlockState extend(MCAWorld world, Vector3i pos, BlockState state) {
|
||||
state = state
|
||||
.with("north", connection(world, pos, state, Direction.NORTH))
|
||||
.with("east", connection(world, pos, state, Direction.EAST))
|
||||
.with("south", connection(world, pos, state, Direction.SOUTH))
|
||||
.with("west", connection(world, pos, state, Direction.WEST));
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
private String connection(MCAWorld world, Vector3i pos, BlockState state, Direction direction) {
|
||||
BlockState next = world.getBlockState(pos.add(direction.toVector()));
|
||||
if (CONNECTIBLE.contains(next.getId())) return "side";
|
||||
|
||||
//TODO: up
|
||||
|
||||
return "none";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> getAffectedBlockIds() {
|
||||
return AFFECTED_BLOCK_IDS;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.mca.extensions;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import com.flowpowered.math.vector.Vector3i;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import de.bluecolored.bluemap.core.mca.MCAWorld;
|
||||
import de.bluecolored.bluemap.core.world.BlockState;
|
||||
|
||||
public class SnowyExtension implements BlockStateExtension {
|
||||
|
||||
private static final Collection<String> AFFECTED_BLOCK_IDS = Lists.newArrayList(
|
||||
"minecraft:grass",
|
||||
"minecraft:dirt"
|
||||
);
|
||||
|
||||
@Override
|
||||
public BlockState extend(MCAWorld world, Vector3i pos, BlockState state) {
|
||||
BlockState above = world.getBlockState(pos.add(0, 1, 0));
|
||||
|
||||
if (above.getId().equals("minecraft:snow_layer") || above.getId().equals("minecraft:snow")) {
|
||||
return state.with("snowy", "true");
|
||||
} else {
|
||||
return state.with("snowy", "false");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> getAffectedBlockIds() {
|
||||
return AFFECTED_BLOCK_IDS;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,129 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.mca.extensions;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
|
||||
import com.flowpowered.math.vector.Vector3i;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
import de.bluecolored.bluemap.core.mca.MCAWorld;
|
||||
import de.bluecolored.bluemap.core.util.Direction;
|
||||
import de.bluecolored.bluemap.core.world.BlockState;
|
||||
|
||||
public class StairShapeExtension implements BlockStateExtension {
|
||||
|
||||
private static final HashSet<String> AFFECTED_BLOCK_IDS = Sets.newHashSet(
|
||||
"minecraft:oak_stairs",
|
||||
"minecraft:stone_stairs",
|
||||
"minecraft:brick_stairs",
|
||||
"minecraft:stone_brick_stairs",
|
||||
"minecraft:nether_brick_stairs",
|
||||
"minecraft:sandstone_stairs",
|
||||
"minecraft:spruce_stairs",
|
||||
"minecraft:birch_stairs",
|
||||
"minecraft:jungle_stairs",
|
||||
"minecraft:quartz_stairs",
|
||||
"minecraft:acacia_stairs",
|
||||
"minecraft:dark_oak_stairs",
|
||||
"minecraft:red_sandstone_stairs",
|
||||
"minecraft:purpur_stairs"
|
||||
);
|
||||
|
||||
@Override
|
||||
public BlockState extend(MCAWorld world, Vector3i pos, BlockState state) {
|
||||
try {
|
||||
Direction facing = Direction.fromString(state.getProperties().get("facing"));
|
||||
BlockState back = world.getBlockState(pos.add(facing.toVector()));
|
||||
|
||||
if (isStairs(back) && state.getProperties().get("half").equals(back.getProperties().get("half"))) {
|
||||
Direction backFacing = Direction.fromString(back.getProperties().get("facing"));
|
||||
|
||||
if (facing.getAxis() != backFacing.getAxis()){
|
||||
BlockState next = world.getBlockState(pos.add(backFacing.opposite().toVector()));
|
||||
|
||||
if (!isStairs(next) || !isEqualStairs(state, next)) {
|
||||
|
||||
if (backFacing == rotateYCCW(facing)){
|
||||
return state.with("shape", "outer_left");
|
||||
}
|
||||
|
||||
return state.with("shape", "outer_right");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BlockState front = world.getBlockState(pos.add(facing.opposite().toVector()));
|
||||
|
||||
if (isStairs(front) && state.getProperties().get("half").equals(front.getProperties().get("half"))) {
|
||||
Direction frontFacing = Direction.fromString(front.getProperties().get("facing"));
|
||||
|
||||
if (facing.getAxis() != frontFacing.getAxis()){
|
||||
BlockState next = world.getBlockState(pos.add(frontFacing.toVector()));
|
||||
|
||||
if (!isStairs(next) || !isEqualStairs(state, next)) {
|
||||
if (frontFacing == rotateYCCW(facing)){
|
||||
return state.with("shape", "inner_left");
|
||||
}
|
||||
|
||||
return state.with("shape", "inner_right");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return state.with("shape", "straight");
|
||||
|
||||
} catch (IllegalArgumentException | NullPointerException ex) {
|
||||
return state.with("shape", "straight");
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isStairs(BlockState state) {
|
||||
return AFFECTED_BLOCK_IDS.contains(state.getId());
|
||||
}
|
||||
|
||||
private boolean isEqualStairs(BlockState stair1, BlockState stair2) {
|
||||
return
|
||||
stair1.getProperties().get("facing").equals(stair2.getProperties().get("facing")) &&
|
||||
stair1.getProperties().get("half").equals(stair2.getProperties().get("half"));
|
||||
}
|
||||
|
||||
private Direction rotateYCCW(Direction dir) {
|
||||
switch (dir) {
|
||||
case NORTH: return Direction.WEST;
|
||||
case WEST: return Direction.SOUTH;
|
||||
case SOUTH: return Direction.EAST;
|
||||
case EAST: return Direction.NORTH;
|
||||
default: return dir;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> getAffectedBlockIds() {
|
||||
return AFFECTED_BLOCK_IDS;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.mca.extensions;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
public class TripwireConnectExtension extends ConnectExtension {
|
||||
|
||||
private static final HashSet<String> AFFECTED_BLOCK_IDS = Sets.newHashSet(
|
||||
"minecraft:tripwire"
|
||||
);
|
||||
|
||||
@Override
|
||||
public Set<String> getAffectedBlockIds() {
|
||||
return AFFECTED_BLOCK_IDS;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.mca.extensions;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import com.flowpowered.math.vector.Vector3i;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
import de.bluecolored.bluemap.core.mca.MCAWorld;
|
||||
import de.bluecolored.bluemap.core.util.Direction;
|
||||
import de.bluecolored.bluemap.core.world.BlockState;
|
||||
|
||||
public class WallConnectExtension extends ConnectSameOrFullBlockExtension {
|
||||
|
||||
private static final HashSet<String> AFFECTED_BLOCK_IDS = Sets.newHashSet(
|
||||
"minecraft:cobblestone_wall"
|
||||
);
|
||||
|
||||
@Override
|
||||
public BlockState extend(MCAWorld world, Vector3i pos, BlockState state) {
|
||||
state = super.extend(world, pos, state);
|
||||
|
||||
if (
|
||||
state.getProperties().get("north").equals(state.getProperties().get("south")) &&
|
||||
state.getProperties().get("east").equals(state.getProperties().get("west")) &&
|
||||
!state.getProperties().get("north").equals(state.getProperties().get("east")) &&
|
||||
!connectsTo(world, pos.add(Direction.UP.toVector()))
|
||||
) {
|
||||
return state.with("up", "false");
|
||||
} else {
|
||||
return state.with("up", "true");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getAffectedBlockIds() {
|
||||
return AFFECTED_BLOCK_IDS;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.mca.extensions;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
public class WoodenFenceConnectExtension extends ConnectSameOrFullBlockExtension {
|
||||
|
||||
private static final HashSet<String> AFFECTED_BLOCK_IDS = Sets.newHashSet(
|
||||
"minecraft:fence",
|
||||
"minecraft:spruce_fence",
|
||||
"minecraft:birch_fence",
|
||||
"minecraft:jungle_fence",
|
||||
"minecraft:dark_oak_fence",
|
||||
"minecraft:acacia_fence"
|
||||
);
|
||||
|
||||
@Override
|
||||
public Set<String> getAffectedBlockIds() {
|
||||
return AFFECTED_BLOCK_IDS;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.mca.mapping;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import ninja.leaping.configurate.ConfigurationNode;
|
||||
import ninja.leaping.configurate.gson.GsonConfigurationLoader;
|
||||
|
||||
public class BiomeIdMapper {
|
||||
private static final String DEFAULT_BIOME = "ocean";
|
||||
|
||||
private String[] biomes;
|
||||
|
||||
public BiomeIdMapper() throws IOException {
|
||||
biomes = new String[256];
|
||||
for (int i = 0; i < biomes.length; i++) {
|
||||
biomes[i] = DEFAULT_BIOME;
|
||||
}
|
||||
|
||||
GsonConfigurationLoader loader = GsonConfigurationLoader.builder()
|
||||
.setURL(getClass().getResource("/biomes.json"))
|
||||
.build();
|
||||
|
||||
ConfigurationNode node = loader.load();
|
||||
|
||||
for (Entry<Object, ? extends ConfigurationNode> e : node.getChildrenMap().entrySet()){
|
||||
String biome = e.getKey().toString();
|
||||
int id = e.getValue().getNode("id").getInt(-1);
|
||||
if (id >= 0 && id < biomes.length) {
|
||||
biomes[id] = biome;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public String get(int id) {
|
||||
if (id < 0 || id >= biomes.length) return DEFAULT_BIOME;
|
||||
return biomes[id];
|
||||
}
|
||||
|
||||
public static BiomeIdMapper create() throws IOException {
|
||||
return new BiomeIdMapper();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,119 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.mca.mapping;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import de.bluecolored.bluemap.core.logger.Logger;
|
||||
import de.bluecolored.bluemap.core.world.BlockState;
|
||||
import ninja.leaping.configurate.ConfigurationNode;
|
||||
import ninja.leaping.configurate.gson.GsonConfigurationLoader;
|
||||
|
||||
public class BlockIdMapper {
|
||||
|
||||
private Map<BlockIDMeta, BlockState> mappings;
|
||||
|
||||
public BlockIdMapper() throws IOException {
|
||||
mappings = new HashMap<>();
|
||||
|
||||
GsonConfigurationLoader loader = GsonConfigurationLoader.builder()
|
||||
.setURL(getClass().getResource("/blockIdMappings.json"))
|
||||
.build();
|
||||
|
||||
ConfigurationNode node = loader.load();
|
||||
|
||||
for (Entry<Object, ? extends ConfigurationNode> e : node.getChildrenMap().entrySet()){
|
||||
String key = e.getKey().toString();
|
||||
String value = e.getValue().getString();
|
||||
|
||||
int splitIndex = key.indexOf(':');
|
||||
int blockId = Integer.parseInt(key.substring(0, splitIndex));
|
||||
int blockMeta = Integer.parseInt(key.substring(splitIndex + 1));
|
||||
|
||||
BlockIDMeta idmeta = new BlockIDMeta(blockId, blockMeta);
|
||||
BlockState state = BlockState.fromString(value);
|
||||
|
||||
mappings.put(idmeta, state);
|
||||
}
|
||||
}
|
||||
|
||||
public BlockState get(int id, int meta) {
|
||||
if (id == 0) return BlockState.AIR;
|
||||
|
||||
BlockState state = mappings.get(new BlockIDMeta(id, meta));
|
||||
|
||||
if (state == null) {
|
||||
state = mappings.get(new BlockIDMeta(id, 0)); //fallback
|
||||
|
||||
if (state == null) {
|
||||
Logger.global.noFloodDebug(id + ":" + meta + "-blockidmapper-mappingerr", "Block ID can not be mapped: " + id + ":" + meta);
|
||||
return BlockState.AIR;
|
||||
}
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
class BlockIDMeta {
|
||||
private final int id;
|
||||
private final int meta;
|
||||
|
||||
public BlockIDMeta(int id, int meta) {
|
||||
this.id = id;
|
||||
this.meta = meta;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public int getMeta() {
|
||||
return meta;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return id * 0xFFFF + meta;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj instanceof BlockIDMeta) {
|
||||
BlockIDMeta other = (BlockIDMeta) obj;
|
||||
return other.id == id && other.meta == meta;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static BlockIdMapper create() throws IOException {
|
||||
return new BlockIdMapper();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.mca.mapping;
|
||||
|
||||
public class BlockProperties {
|
||||
|
||||
private final boolean culling, occluding, flammable;
|
||||
|
||||
public BlockProperties(boolean culling, boolean occluding, boolean flammable) {
|
||||
this.culling = culling;
|
||||
this.occluding = occluding;
|
||||
this.flammable = flammable;
|
||||
}
|
||||
|
||||
public boolean isCulling() {
|
||||
return culling;
|
||||
}
|
||||
|
||||
public boolean isOccluding() {
|
||||
return occluding;
|
||||
}
|
||||
|
||||
public boolean isFlammable() {
|
||||
return flammable;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,103 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.mca.mapping;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import com.google.common.collect.HashMultimap;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.google.common.collect.Multimaps;
|
||||
|
||||
import de.bluecolored.bluemap.core.world.BlockState;
|
||||
import ninja.leaping.configurate.ConfigurationNode;
|
||||
import ninja.leaping.configurate.gson.GsonConfigurationLoader;
|
||||
|
||||
public class BlockPropertyMapper {
|
||||
|
||||
private static final BlockProperties DEFAULT_PROPERTIES = new BlockProperties(false, false, false);
|
||||
|
||||
private Multimap<String, BlockStateMapping<BlockProperties>> mappings;
|
||||
private LoadingCache<BlockState, BlockProperties> mappingCache;
|
||||
|
||||
private BlockPropertyMapper() throws IOException {
|
||||
mappings = HashMultimap.create();
|
||||
|
||||
GsonConfigurationLoader loader = GsonConfigurationLoader.builder()
|
||||
.setURL(getClass().getResource("/blockProperties.json"))
|
||||
.build();
|
||||
|
||||
ConfigurationNode node = loader.load();
|
||||
|
||||
for (Entry<Object, ? extends ConfigurationNode> e : node.getChildrenMap().entrySet()){
|
||||
String key = e.getKey().toString();
|
||||
BlockState bsKey = BlockState.fromString(key);
|
||||
BlockProperties bsValue = new BlockProperties(
|
||||
e.getValue().getNode("culling").getBoolean(false),
|
||||
e.getValue().getNode("occluding").getBoolean(false),
|
||||
e.getValue().getNode("flammable").getBoolean(false)
|
||||
);
|
||||
BlockStateMapping<BlockProperties> mapping = new BlockStateMapping<>(bsKey, bsValue);
|
||||
mappings.put(bsKey.getId(), mapping);
|
||||
}
|
||||
|
||||
mappings = Multimaps.unmodifiableMultimap(mappings);
|
||||
|
||||
mappingCache = CacheBuilder.newBuilder()
|
||||
.concurrencyLevel(8)
|
||||
.maximumSize(10000)
|
||||
.build(new CacheLoader<BlockState, BlockProperties>(){
|
||||
@Override public BlockProperties load(BlockState key) { return mapNoCache(key); }
|
||||
});
|
||||
}
|
||||
|
||||
public BlockProperties map(BlockState from){
|
||||
try {
|
||||
return mappingCache.get(from);
|
||||
} catch (ExecutionException e) {
|
||||
//should never happen, since the CacheLoader does not throw any exceptions
|
||||
throw new RuntimeException("Unexpected error while trying to map a BlockState's properties", e.getCause());
|
||||
}
|
||||
}
|
||||
|
||||
private BlockProperties mapNoCache(BlockState bs){
|
||||
for (BlockStateMapping<BlockProperties> bm : mappings.get(bs.getId())){
|
||||
if (bm.fitsTo(bs)){
|
||||
return bm.getMapping();
|
||||
}
|
||||
}
|
||||
|
||||
return DEFAULT_PROPERTIES;
|
||||
}
|
||||
|
||||
public static BlockPropertyMapper create() throws IOException {
|
||||
return new BlockPropertyMapper();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.mca.mapping;
|
||||
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import de.bluecolored.bluemap.core.world.BlockState;
|
||||
|
||||
class BlockStateMapping<T> {
|
||||
private BlockState blockState;
|
||||
private T mapping;
|
||||
|
||||
public BlockStateMapping(BlockState blockState, T mapping) {
|
||||
this.blockState = blockState;
|
||||
this.mapping = mapping;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the all the properties on this BlockMapping-key are the same in the provided BlockState.<br>
|
||||
* Properties that are not defined in this Mapping are ignored on the provided BlockState.<br>
|
||||
*/
|
||||
public boolean fitsTo(BlockState blockState){
|
||||
if (!this.blockState.getId().equals(blockState.getId())) return false;
|
||||
for (Entry<String, String> e : this.blockState.getProperties().entrySet()){
|
||||
if (!e.getValue().equals(blockState.getProperties().get(e.getKey()))){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public BlockState getBlockState(){
|
||||
return blockState;
|
||||
}
|
||||
|
||||
public T getMapping(){
|
||||
return mapping;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.mca.mapping;
|
||||
|
||||
public class LightData {
|
||||
|
||||
public static final LightData ZERO = new LightData(0, 0);
|
||||
public static final LightData FULL = new LightData(15, 15);
|
||||
|
||||
private final int skyLight, blockLight;
|
||||
|
||||
public LightData(int skyLight, int blockLight) {
|
||||
this.skyLight = skyLight;
|
||||
this.blockLight = blockLight;
|
||||
}
|
||||
|
||||
public int getSkyLight() {
|
||||
return skyLight;
|
||||
}
|
||||
|
||||
public int getBlockLight() {
|
||||
return blockLight;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "LightData[B:" + getBlockLight() + "|S:" + getSkyLight() + "]";
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,218 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.model;
|
||||
|
||||
import com.flowpowered.math.imaginary.Quaternionf;
|
||||
import com.flowpowered.math.matrix.Matrix3f;
|
||||
import com.flowpowered.math.vector.Vector2f;
|
||||
import com.flowpowered.math.vector.Vector3f;
|
||||
|
||||
import de.bluecolored.bluemap.core.util.MathUtil;
|
||||
|
||||
public class Face {
|
||||
|
||||
private Vector3f p1, p2, p3;
|
||||
private Vector3f n1, n2, n3;
|
||||
private Vector3f c1, c2, c3;
|
||||
private Vector2f uv1, uv2, uv3;
|
||||
private int materialIndex;
|
||||
private boolean normalizedNormals;
|
||||
|
||||
public Face(Vector3f p1, Vector3f p2, Vector3f p3, Vector2f uv1, Vector2f uv2, Vector2f uv3, int materialIndex) {
|
||||
this.p1 = p1;
|
||||
this.p2 = p2;
|
||||
this.p3 = p3;
|
||||
|
||||
this.uv1 = uv1;
|
||||
this.uv2 = uv2;
|
||||
this.uv3 = uv3;
|
||||
|
||||
this.materialIndex = materialIndex;
|
||||
|
||||
Vector3f faceNormal = getFaceNormal();
|
||||
this.n1 = faceNormal;
|
||||
this.n2 = faceNormal;
|
||||
this.n3 = faceNormal;
|
||||
this.normalizedNormals = true;
|
||||
|
||||
Vector3f color = Vector3f.ONE;
|
||||
this.c1 = color;
|
||||
this.c2 = color;
|
||||
this.c3 = color;
|
||||
}
|
||||
|
||||
public void rotate(Quaternionf rotation){
|
||||
p1 = rotation.rotate(p1);
|
||||
p2 = rotation.rotate(p2);
|
||||
p3 = rotation.rotate(p3);
|
||||
|
||||
n1 = rotation.rotate(n1);
|
||||
n2 = rotation.rotate(n2);
|
||||
n3 = rotation.rotate(n3);
|
||||
}
|
||||
|
||||
public void transform(Matrix3f transformation){
|
||||
p1 = transformation.transform(p1);
|
||||
p2 = transformation.transform(p2);
|
||||
p3 = transformation.transform(p3);
|
||||
|
||||
n1 = transformation.transform(n1);
|
||||
n2 = transformation.transform(n2);
|
||||
n3 = transformation.transform(n3);
|
||||
|
||||
normalizedNormals = false;
|
||||
}
|
||||
|
||||
public void translate(Vector3f translation){
|
||||
p1 = translation.add(p1);
|
||||
p2 = translation.add(p2);
|
||||
p3 = translation.add(p3);
|
||||
}
|
||||
|
||||
public Vector3f getP1() {
|
||||
return p1;
|
||||
}
|
||||
|
||||
public void setP1(Vector3f p1) {
|
||||
this.p1 = p1;
|
||||
}
|
||||
|
||||
public Vector3f getP2() {
|
||||
return p2;
|
||||
}
|
||||
|
||||
public void setP2(Vector3f p2) {
|
||||
this.p2 = p2;
|
||||
}
|
||||
|
||||
public Vector3f getP3() {
|
||||
return p3;
|
||||
}
|
||||
|
||||
public void setP3(Vector3f p3) {
|
||||
this.p3 = p3;
|
||||
}
|
||||
|
||||
public Vector3f getN1() {
|
||||
normlizeNormals();
|
||||
return n1;
|
||||
}
|
||||
|
||||
public void setN1(Vector3f n1) {
|
||||
this.n1 = n1;
|
||||
normalizedNormals = false;
|
||||
}
|
||||
|
||||
public Vector3f getN2() {
|
||||
normlizeNormals();
|
||||
return n2;
|
||||
}
|
||||
|
||||
public void setN2(Vector3f n2) {
|
||||
this.n2 = n2;
|
||||
normalizedNormals = false;
|
||||
}
|
||||
|
||||
public Vector3f getN3() {
|
||||
normlizeNormals();
|
||||
return n3;
|
||||
}
|
||||
|
||||
public void setN3(Vector3f n3) {
|
||||
this.n3 = n3;
|
||||
normalizedNormals = false;
|
||||
}
|
||||
|
||||
public Vector3f getC1() {
|
||||
return c1;
|
||||
}
|
||||
|
||||
public void setC1(Vector3f c1) {
|
||||
this.c1 = c1;
|
||||
}
|
||||
|
||||
public Vector3f getC2() {
|
||||
return c2;
|
||||
}
|
||||
|
||||
public void setC2(Vector3f c2) {
|
||||
this.c2 = c2;
|
||||
}
|
||||
|
||||
public Vector3f getC3() {
|
||||
return c3;
|
||||
}
|
||||
|
||||
public void setC3(Vector3f c3) {
|
||||
this.c3 = c3;
|
||||
}
|
||||
|
||||
public Vector2f getUv1() {
|
||||
return uv1;
|
||||
}
|
||||
|
||||
public void setUv1(Vector2f uv1) {
|
||||
this.uv1 = uv1;
|
||||
}
|
||||
|
||||
public Vector2f getUv2() {
|
||||
return uv2;
|
||||
}
|
||||
|
||||
public void setUv2(Vector2f uv2) {
|
||||
this.uv2 = uv2;
|
||||
}
|
||||
|
||||
public Vector2f getUv3() {
|
||||
return uv3;
|
||||
}
|
||||
|
||||
public void setUv3(Vector2f uv3) {
|
||||
this.uv3 = uv3;
|
||||
}
|
||||
|
||||
public int getMaterialIndex() {
|
||||
return materialIndex;
|
||||
}
|
||||
|
||||
public void setMaterialIndex(int materialIndex) {
|
||||
this.materialIndex = materialIndex;
|
||||
}
|
||||
|
||||
public Vector3f getFaceNormal(){
|
||||
return MathUtil.getSurfaceNormal(p1, p2, p3);
|
||||
}
|
||||
|
||||
private void normlizeNormals(){
|
||||
if (normalizedNormals) return;
|
||||
|
||||
n1 = n1.normalize();
|
||||
n2 = n2.normalize();
|
||||
n3 = n3.normalize();
|
||||
|
||||
normalizedNormals = true;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,149 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.model;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import com.flowpowered.math.imaginary.Quaternionf;
|
||||
import com.flowpowered.math.matrix.Matrix3f;
|
||||
import com.flowpowered.math.vector.Vector2f;
|
||||
import com.flowpowered.math.vector.Vector3f;
|
||||
|
||||
import de.bluecolored.bluemap.core.threejs.BufferGeometry;
|
||||
|
||||
public class Model {
|
||||
|
||||
private List<Face> faces;
|
||||
|
||||
public Model() {
|
||||
this.faces = new ArrayList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges the given Model into this model<br>
|
||||
* Faces are not cloned: So changes to the faces of the previous model will mirror in this model, but adding and removing faces will not.
|
||||
*/
|
||||
public void merge(Model model){
|
||||
faces.addAll(model.getFaces());
|
||||
}
|
||||
|
||||
public void addFace(Face face){
|
||||
faces.add(face);
|
||||
}
|
||||
|
||||
public void removeFace(Face face){
|
||||
faces.remove(face);
|
||||
}
|
||||
|
||||
public Collection<Face> getFaces(){
|
||||
return faces;
|
||||
}
|
||||
|
||||
public void rotate(Quaternionf rotation){
|
||||
for (Face f : faces){
|
||||
f.rotate(rotation);
|
||||
}
|
||||
}
|
||||
|
||||
public void transform(Matrix3f transformation){
|
||||
for (Face f : faces){
|
||||
f.transform(transformation);
|
||||
}
|
||||
}
|
||||
|
||||
public void translate(Vector3f translation){
|
||||
for (Face f : faces){
|
||||
f.translate(translation);
|
||||
}
|
||||
}
|
||||
|
||||
public BufferGeometry toBufferGeometry() {
|
||||
|
||||
//sort faces by material index
|
||||
faces.sort((f1, f2) -> (int) Math.signum(f1.getMaterialIndex() - f2.getMaterialIndex()));
|
||||
|
||||
//reorganize all faces into arrays and create material-groups
|
||||
int count = faces.size();
|
||||
|
||||
List<BufferGeometry.MaterialGroup> groups = new ArrayList<>();
|
||||
int groupStart = 0;
|
||||
int currentMaterialIndex = -1;
|
||||
if (count > 0) currentMaterialIndex = faces.get(0).getMaterialIndex();
|
||||
|
||||
float[] position = new float[count * 3 * 3];
|
||||
float[] normal = new float[count * 3 * 3];
|
||||
float[] color = new float[count * 3 * 3];
|
||||
float[] uv = new float[count * 2 * 3];
|
||||
|
||||
for (int itemIndex = 0; itemIndex < count; itemIndex++){
|
||||
Face f = faces.get(itemIndex);
|
||||
|
||||
if (currentMaterialIndex != f.getMaterialIndex()){
|
||||
groups.add(new BufferGeometry.MaterialGroup(currentMaterialIndex, groupStart * 3, (itemIndex - groupStart) * 3));
|
||||
groupStart = itemIndex;
|
||||
currentMaterialIndex = f.getMaterialIndex();
|
||||
}
|
||||
|
||||
addVector3fToArray( position, f.getP1(), (itemIndex * 3 + 0) * 3 );
|
||||
addVector3fToArray( normal, f.getN1(), (itemIndex * 3 + 0) * 3 );
|
||||
addVector3fToArray( color, f.getC1(), (itemIndex * 3 + 0) * 3 );
|
||||
addVector2fToArray( uv, f.getUv1(), (itemIndex * 3 + 0) * 2 );
|
||||
|
||||
addVector3fToArray( position, f.getP2(), (itemIndex * 3 + 1) * 3 );
|
||||
addVector3fToArray( normal, f.getN2(), (itemIndex * 3 + 1) * 3 );
|
||||
addVector3fToArray( color, f.getC2(), (itemIndex * 3 + 1) * 3 );
|
||||
addVector2fToArray( uv, f.getUv2(), (itemIndex * 3 + 1) * 2 );
|
||||
|
||||
addVector3fToArray( position, f.getP3(), (itemIndex * 3 + 2) * 3 );
|
||||
addVector3fToArray( normal, f.getN3(), (itemIndex * 3 + 2) * 3 );
|
||||
addVector3fToArray( color, f.getC3(), (itemIndex * 3 + 2) * 3 );
|
||||
addVector2fToArray( uv, f.getUv3(), (itemIndex * 3 + 2) * 2 );
|
||||
}
|
||||
|
||||
groups.add(new BufferGeometry.MaterialGroup(currentMaterialIndex, groupStart * 3, (count - groupStart) * 3));
|
||||
|
||||
return new BufferGeometry(
|
||||
position,
|
||||
normal,
|
||||
color,
|
||||
uv,
|
||||
groups.toArray(new BufferGeometry.MaterialGroup[groups.size()])
|
||||
);
|
||||
}
|
||||
|
||||
private void addVector3fToArray(float[] array, Vector3f v, int startIndex){
|
||||
array[startIndex] = v.getX();
|
||||
array[startIndex + 1] = v.getY();
|
||||
array[startIndex + 2] = v.getZ();
|
||||
}
|
||||
|
||||
private void addVector2fToArray(float[] array, Vector2f v, int startIndex){
|
||||
array[startIndex] = v.getX();
|
||||
array[startIndex + 1] = v.getY();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,88 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.render;
|
||||
|
||||
public interface RenderSettings {
|
||||
|
||||
/**
|
||||
* The strenght of ao-shading calculated for each vertex.<br>
|
||||
* A value of 0 turns off ao.<br>
|
||||
* The value represents the amount that each occluding face subtracts of the light-multiplier. (There are at most 3 occluding faces)
|
||||
*/
|
||||
default float getAmbientOcclusionStrenght() {
|
||||
return 0.25f;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether faces that have a sky-light-value of 0 will be rendered or not.
|
||||
*/
|
||||
default boolean isExcludeFacesWithoutSunlight() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* A multiplier to how much faces are shaded due to their light value<br>
|
||||
* This can be used to make sure blocks with a light value of 0 are not pitch black
|
||||
*/
|
||||
default float getLightShadeMultiplier() {
|
||||
return 0.8f;
|
||||
}
|
||||
|
||||
/**
|
||||
* The maximum height of rendered blocks
|
||||
*/
|
||||
default int getMaxY() {
|
||||
return Integer.MAX_VALUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* The minimum height of rendered blocks
|
||||
*/
|
||||
default int getMinY() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* The same as the maximum height, but blocks that are above this value are treated as AIR.<br>
|
||||
* This leads to the top-faces being rendered instead of them being culled.
|
||||
*/
|
||||
default int getSliceY() {
|
||||
return Integer.MAX_VALUE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
default RenderSettings copy() {
|
||||
return new StaticRenderSettings(
|
||||
getAmbientOcclusionStrenght(),
|
||||
isExcludeFacesWithoutSunlight(),
|
||||
getLightShadeMultiplier(),
|
||||
getMaxY(),
|
||||
getMinY(),
|
||||
getSliceY()
|
||||
);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,80 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.render;
|
||||
|
||||
public class StaticRenderSettings implements RenderSettings {
|
||||
|
||||
private float ambientOcclusion;
|
||||
private boolean excludeFacesWithoutSunlight;
|
||||
private float lightShade;
|
||||
private int maxY, minY, sliceY;
|
||||
|
||||
public StaticRenderSettings(
|
||||
float ambientOcclusion,
|
||||
boolean excludeFacesWithoutSunlight,
|
||||
float ligheShade,
|
||||
int maxY,
|
||||
int minY,
|
||||
int sliceY
|
||||
) {
|
||||
this.ambientOcclusion = ambientOcclusion;
|
||||
this.excludeFacesWithoutSunlight = excludeFacesWithoutSunlight;
|
||||
this.lightShade = ligheShade;
|
||||
this.maxY = maxY;
|
||||
this.minY = minY;
|
||||
this.sliceY = sliceY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getAmbientOcclusionStrenght() {
|
||||
return ambientOcclusion;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isExcludeFacesWithoutSunlight() {
|
||||
return excludeFacesWithoutSunlight;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getLightShadeMultiplier() {
|
||||
return lightShade;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxY() {
|
||||
return maxY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMinY() {
|
||||
return minY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSliceY() {
|
||||
return sliceY;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,74 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.render;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import de.bluecolored.bluemap.core.render.hires.HiresModel;
|
||||
import de.bluecolored.bluemap.core.render.hires.HiresModelManager;
|
||||
import de.bluecolored.bluemap.core.render.lowres.LowresModelManager;
|
||||
import de.bluecolored.bluemap.core.world.ChunkNotGeneratedException;
|
||||
|
||||
public class TileRenderer {
|
||||
private HiresModelManager hiresModelManager;
|
||||
private LowresModelManager lowresModelManager;
|
||||
private RenderSettings renderSettings;
|
||||
|
||||
public TileRenderer(HiresModelManager hiresModelManager, LowresModelManager lowresModelManager, RenderSettings renderSettings) {
|
||||
this.hiresModelManager = hiresModelManager;
|
||||
this.lowresModelManager = lowresModelManager;
|
||||
this.renderSettings = renderSettings.copy();
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the provided WorldTile
|
||||
* @throws ChunkNotGeneratedException if that WorldTile's WorldChunk is not fully generated
|
||||
* @throws IOException if a lowres-model that needs to be updated could not be loaded
|
||||
*/
|
||||
public void render(WorldTile tile) throws IOException, ChunkNotGeneratedException {
|
||||
HiresModel hiresModel = hiresModelManager.render(tile, renderSettings);
|
||||
lowresModelManager.render(hiresModel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves changes to disk
|
||||
*/
|
||||
public void save(){
|
||||
lowresModelManager.save();
|
||||
}
|
||||
|
||||
public HiresModelManager getHiresModelManager() {
|
||||
return hiresModelManager;
|
||||
}
|
||||
|
||||
public LowresModelManager getLowresModelManager() {
|
||||
return lowresModelManager;
|
||||
}
|
||||
|
||||
public RenderSettings getRenderSettings() {
|
||||
return renderSettings;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.render;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import com.flowpowered.math.vector.Vector2i;
|
||||
|
||||
import de.bluecolored.bluemap.core.world.World;
|
||||
|
||||
public class WorldTile {
|
||||
|
||||
private World world;
|
||||
private Vector2i tile;
|
||||
|
||||
private int hash;
|
||||
|
||||
public WorldTile(World world, Vector2i tile) {
|
||||
this.world = world;
|
||||
this.tile = tile;
|
||||
|
||||
this.hash = Objects.hash(world.getUUID(), tile);
|
||||
}
|
||||
|
||||
public World getWorld() {
|
||||
return world;
|
||||
}
|
||||
|
||||
public Vector2i getTile() {
|
||||
return tile;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (!(obj instanceof WorldTile)) return false;
|
||||
WorldTile that = (WorldTile) obj;
|
||||
|
||||
if (!this.world.getUUID().equals(that.world.getUUID())) return false;
|
||||
return this.tile.equals(that.tile);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return hash;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.render.context;
|
||||
|
||||
import com.flowpowered.math.vector.Vector3i;
|
||||
|
||||
import de.bluecolored.bluemap.core.util.Direction;
|
||||
import de.bluecolored.bluemap.core.world.Block;
|
||||
|
||||
public interface BlockContext {
|
||||
|
||||
Vector3i getPosition();
|
||||
|
||||
/**
|
||||
* This returns neighbour blocks.<br>
|
||||
* The distance can not be larger than one block in each direction!<br>
|
||||
*/
|
||||
Block getRelativeBlock(Vector3i direction);
|
||||
|
||||
/**
|
||||
* This returns neighbour blocks.<br>
|
||||
* The distance can not be larger than one block in each direction!<br>
|
||||
*/
|
||||
default Block getRelativeBlock(int x, int y, int z){
|
||||
return getRelativeBlock(new Vector3i(x, y, z));
|
||||
}
|
||||
|
||||
/**
|
||||
* This returns neighbour blocks.
|
||||
*/
|
||||
default Block getRelativeBlock(Direction direction){
|
||||
return getRelativeBlock(direction.toVector());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,168 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.render.context;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.UUID;
|
||||
|
||||
import com.flowpowered.math.vector.Vector2i;
|
||||
import com.flowpowered.math.vector.Vector3d;
|
||||
import com.flowpowered.math.vector.Vector3i;
|
||||
|
||||
import de.bluecolored.bluemap.core.util.AABB;
|
||||
import de.bluecolored.bluemap.core.world.Block;
|
||||
import de.bluecolored.bluemap.core.world.BlockState;
|
||||
import de.bluecolored.bluemap.core.world.World;
|
||||
import de.bluecolored.bluemap.core.world.WorldChunk;
|
||||
|
||||
public class EmptyBlockContext implements ExtendedBlockContext {
|
||||
|
||||
private static final EmptyBlockContext instance = new EmptyBlockContext();
|
||||
|
||||
public static final Block AIR_BLOCK = new AirBlock();
|
||||
|
||||
private EmptyBlockContext() {}
|
||||
|
||||
@Override
|
||||
public Block getRelativeBlock(Vector3i direction) {
|
||||
return AIR_BLOCK;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector3i getPosition() {
|
||||
return Vector3i.ZERO;
|
||||
}
|
||||
|
||||
public static ExtendedBlockContext instance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
private static class AirBlock extends Block {
|
||||
|
||||
private BlockState state = BlockState.AIR;
|
||||
|
||||
@Override
|
||||
public BlockState getBlock() {
|
||||
return state;
|
||||
}
|
||||
|
||||
@Override
|
||||
public World getWorld() {
|
||||
return new EmptyWorld();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector3i getPosition() {
|
||||
return Vector3i.ZERO;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getSunLightLevel() {
|
||||
return 0d;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getBlockLightLevel() {
|
||||
return 0d;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCullingNeighborFaces() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBiome() {
|
||||
return "ocean";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class EmptyWorld implements World {
|
||||
|
||||
private AABB bounds;
|
||||
|
||||
public EmptyWorld() {
|
||||
this.bounds = new AABB(Vector3d.from(Double.POSITIVE_INFINITY), Vector3d.from(Double.NEGATIVE_INFINITY));
|
||||
}
|
||||
|
||||
public EmptyWorld(AABB bounds){
|
||||
this.bounds = bounds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public World getWorld() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Block getBlock(Vector3i pos) {
|
||||
return new AirBlock();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AABB getBoundaries() {
|
||||
return bounds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WorldChunk getWorldChunk(AABB boundaries) {
|
||||
return new EmptyWorld(boundaries);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isGenerated() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "-empty-";
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getUUID() {
|
||||
return new UUID(0, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSeaLevel() {
|
||||
return 63;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector3i getSpawnPoint() {
|
||||
return new Vector3i(0, 63, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Vector2i> getChunkList(long modifiedSince) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.render.context;
|
||||
|
||||
import com.flowpowered.math.vector.Vector3i;
|
||||
|
||||
import de.bluecolored.bluemap.core.world.Block;
|
||||
|
||||
/**
|
||||
* A BlockContext that has a range of TWO blocks instead of one
|
||||
*/
|
||||
public interface ExtendedBlockContext extends BlockContext {
|
||||
|
||||
/**
|
||||
* This returns neighbour blocks.<br>
|
||||
* The distance can not be larger than two blocks in each direction!<br>
|
||||
*/
|
||||
Block getRelativeBlock(Vector3i direction);
|
||||
|
||||
/**
|
||||
* This returns neighbour blocks.<br>
|
||||
* The distance can not be larger than two blocks in each direction!<br>
|
||||
*/
|
||||
default Block getRelativeBlock(int x, int y, int z){
|
||||
return getRelativeBlock(new Vector3i(x, y, z));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a relative view of this ExtendedBlockContext!
|
||||
* The distance can not be larger than two blocks in each direction!<br>
|
||||
*/
|
||||
default BlockContext getRelativeView(final Vector3i relative) {
|
||||
final ExtendedBlockContext parent = this;
|
||||
|
||||
return new BlockContext() {
|
||||
|
||||
@Override
|
||||
public Block getRelativeBlock(Vector3i direction) {
|
||||
return parent.getRelativeBlock(direction.add(relative));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector3i getPosition() {
|
||||
return parent.getPosition().add(relative);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.render.context;
|
||||
|
||||
import com.flowpowered.math.vector.Vector3i;
|
||||
|
||||
import de.bluecolored.bluemap.core.world.Block;
|
||||
import de.bluecolored.bluemap.core.world.WorldChunk;
|
||||
|
||||
public class SlicedWorldChunkBlockContext extends WorldChunkBlockContext {
|
||||
|
||||
private int sliceY;
|
||||
|
||||
/**
|
||||
* Same as a {@link WorldChunkBlockContext} but if the requested Block is above the slice-value it will return air.
|
||||
*/
|
||||
public SlicedWorldChunkBlockContext(WorldChunk worldChunk, Vector3i blockPos, int sliceY) {
|
||||
super(worldChunk, blockPos);
|
||||
|
||||
this.sliceY = sliceY;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Block getBlock(Vector3i position) {
|
||||
if (position.getY() > sliceY) return EmptyBlockContext.AIR_BLOCK;
|
||||
|
||||
return super.getBlock(position);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,79 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.render.context;
|
||||
|
||||
import com.flowpowered.math.vector.Vector3i;
|
||||
|
||||
import de.bluecolored.bluemap.core.world.Block;
|
||||
import de.bluecolored.bluemap.core.world.ChunkNotGeneratedException;
|
||||
import de.bluecolored.bluemap.core.world.WorldChunk;
|
||||
|
||||
public class WorldChunkBlockContext implements ExtendedBlockContext {
|
||||
|
||||
private Vector3i blockPos;
|
||||
private WorldChunk chunk;
|
||||
|
||||
/**
|
||||
* A BlockContext backed by a WorldChunk.
|
||||
*
|
||||
* This Context assumes that the world-chunk is generated around that block-position.
|
||||
* If the given world chunk is not generated, using this context will result in a RuntimeException!
|
||||
*/
|
||||
public WorldChunkBlockContext(WorldChunk worldChunk, Vector3i blockPos) {
|
||||
this.blockPos = blockPos;
|
||||
this.chunk = worldChunk;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector3i getPosition() {
|
||||
return blockPos;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Block getRelativeBlock(int x, int y, int z) {
|
||||
Vector3i pos = blockPos.add(x, y, z);
|
||||
return getBlock(pos);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Block getRelativeBlock(Vector3i direction) {
|
||||
Vector3i pos = blockPos.add(direction);
|
||||
return getBlock(pos);
|
||||
}
|
||||
|
||||
protected Block getBlock(Vector3i position) {
|
||||
if (!chunk.containsBlock(position)) {
|
||||
return EmptyBlockContext.AIR_BLOCK;
|
||||
}
|
||||
|
||||
try {
|
||||
return chunk.getBlock(position);
|
||||
} catch (ChunkNotGeneratedException e) {
|
||||
//we assume the chunk being generated
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,94 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.render.hires;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import com.flowpowered.math.vector.Vector2i;
|
||||
import com.flowpowered.math.vector.Vector3i;
|
||||
import com.flowpowered.math.vector.Vector4f;
|
||||
|
||||
import de.bluecolored.bluemap.core.model.Model;
|
||||
|
||||
/**
|
||||
* A model, containing additional information about the tile it represents
|
||||
*/
|
||||
public class HiresModel extends Model {
|
||||
|
||||
private UUID world;
|
||||
private Vector2i tile;
|
||||
private Vector3i blockMin, blockMax, blockSize;
|
||||
|
||||
private int[][] heights;
|
||||
private Vector4f[][] colors;
|
||||
|
||||
public HiresModel(UUID world, Vector2i tile, Vector3i blockMin, Vector3i blockMax) {
|
||||
this.world = world;
|
||||
this.tile = tile;
|
||||
this.blockMin = blockMin;
|
||||
this.blockMax = blockMax;
|
||||
this.blockSize = blockMax.sub(blockMin).add(Vector3i.ONE);
|
||||
|
||||
heights = new int[blockSize.getX()][blockSize.getZ()];
|
||||
colors = new Vector4f[blockSize.getX()][blockSize.getZ()];
|
||||
}
|
||||
|
||||
public void setColor(int x, int z, Vector4f color){
|
||||
colors[x - blockMin.getX()][z - blockMin.getZ()] = color;
|
||||
}
|
||||
|
||||
public Vector4f getColor(int x, int z){
|
||||
return colors[x - blockMin.getX()][z - blockMin.getZ()];
|
||||
}
|
||||
|
||||
public void setHeight(int x, int z, int height){
|
||||
heights[x - blockMin.getX()][z - blockMin.getZ()] = height;
|
||||
}
|
||||
|
||||
public int getHeight(int x, int z){
|
||||
return heights[x - blockMin.getX()][z - blockMin.getZ()];
|
||||
}
|
||||
|
||||
public UUID getWorld(){
|
||||
return world;
|
||||
}
|
||||
|
||||
public Vector3i getBlockMin(){
|
||||
return blockMin;
|
||||
}
|
||||
|
||||
public Vector3i getBlockMax(){
|
||||
return blockMax;
|
||||
}
|
||||
|
||||
public Vector3i getBlockSize(){
|
||||
return blockSize;
|
||||
}
|
||||
|
||||
public Vector2i getTile(){
|
||||
return tile;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,168 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.render.hires;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.PrintWriter;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Path;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
|
||||
import com.flowpowered.math.vector.Vector2i;
|
||||
import com.flowpowered.math.vector.Vector3d;
|
||||
import com.flowpowered.math.vector.Vector3i;
|
||||
|
||||
import de.bluecolored.bluemap.core.logger.Logger;
|
||||
import de.bluecolored.bluemap.core.render.RenderSettings;
|
||||
import de.bluecolored.bluemap.core.render.WorldTile;
|
||||
import de.bluecolored.bluemap.core.resourcepack.ResourcePack;
|
||||
import de.bluecolored.bluemap.core.util.AABB;
|
||||
import de.bluecolored.bluemap.core.util.FileUtil;
|
||||
import de.bluecolored.bluemap.core.world.ChunkNotGeneratedException;
|
||||
|
||||
public class HiresModelManager {
|
||||
|
||||
private Path fileRoot;
|
||||
private HiresModelRenderer renderer;
|
||||
|
||||
private Vector2i tileSize;
|
||||
private Vector2i gridOrigin;
|
||||
|
||||
private ExecutorService savingExecutor;
|
||||
|
||||
public HiresModelManager(Path fileRoot, ResourcePack resourcePack, Vector2i tileSize, ExecutorService savingExecutor) {
|
||||
this(fileRoot, new HiresModelRenderer(resourcePack), tileSize, new Vector2i(2, 2), savingExecutor);
|
||||
}
|
||||
|
||||
public HiresModelManager(Path fileRoot, HiresModelRenderer renderer, Vector2i tileSize, Vector2i gridOrigin, ExecutorService savingExecutor) {
|
||||
this.fileRoot = fileRoot;
|
||||
this.renderer = renderer;
|
||||
|
||||
this.tileSize = tileSize;
|
||||
this.gridOrigin = gridOrigin;
|
||||
|
||||
this.savingExecutor = savingExecutor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the given world tile with the provided render-settings
|
||||
* @throws ChunkNotGeneratedException if a minecraft-chunk needed for thies tile is not yet generated
|
||||
*/
|
||||
public HiresModel render(WorldTile tile, RenderSettings renderSettings) throws ChunkNotGeneratedException {
|
||||
HiresModel model = renderer.render(tile, getTileRegion(tile), renderSettings);
|
||||
save(model);
|
||||
return model;
|
||||
}
|
||||
|
||||
private void save(final HiresModel model) {
|
||||
final String modelJson = model.toBufferGeometry().toJson();
|
||||
savingExecutor.submit(() -> save(model, modelJson));
|
||||
}
|
||||
|
||||
private void save(HiresModel model, String modelJson){
|
||||
File file = getFile(model.getTile());
|
||||
|
||||
try {
|
||||
if (!file.exists()){
|
||||
file.getParentFile().mkdirs();
|
||||
file.createNewFile();
|
||||
}
|
||||
|
||||
FileOutputStream fos = new FileOutputStream(file);
|
||||
GZIPOutputStream zos = new GZIPOutputStream(fos);
|
||||
OutputStreamWriter osw = new OutputStreamWriter(zos, StandardCharsets.UTF_8);
|
||||
try (
|
||||
PrintWriter pw = new PrintWriter(osw);
|
||||
){
|
||||
pw.print(modelJson);
|
||||
}
|
||||
|
||||
//logger.logDebug("Saved hires model: " + model.getTile());
|
||||
} catch (IOException e){
|
||||
Logger.global.logError("Failed to save hires model: " + file, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the region of blocks that a tile includes
|
||||
*/
|
||||
public AABB getTileRegion(WorldTile tile) {
|
||||
Vector3i min = new Vector3i(
|
||||
tile.getTile().getX() * tileSize.getX() + gridOrigin.getX(),
|
||||
tile.getWorld().getBoundaries().getMin().getFloorY(),
|
||||
tile.getTile().getY() * tileSize.getY() + gridOrigin.getY()
|
||||
);
|
||||
Vector3i max = min.add(
|
||||
tileSize.getX() - 1,
|
||||
tile.getWorld().getBoundaries().getMax().getFloorY(),
|
||||
tileSize.getY() - 1
|
||||
);
|
||||
return new AABB(min, max);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the tile-size
|
||||
*/
|
||||
public Vector2i getTileSize() {
|
||||
return tileSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the grid-origin
|
||||
*/
|
||||
public Vector2i getGridOrigin() {
|
||||
return gridOrigin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a block-position to a map-tile-coordinate
|
||||
*/
|
||||
public Vector2i posToTile(Vector3i pos){
|
||||
return posToTile(pos.toDouble());
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a block-position to a map-tile-coordinate
|
||||
*/
|
||||
public Vector2i posToTile(Vector3d pos){
|
||||
pos = pos.sub(new Vector3d(gridOrigin.getX(), 0.0, gridOrigin.getY()));
|
||||
return Vector2i.from(
|
||||
(int) Math.floor(pos.getX() / (double) getTileSize().getX()),
|
||||
(int) Math.floor(pos.getZ() / (double) getTileSize().getY())
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the file for a tile
|
||||
*/
|
||||
public File getFile(Vector2i tilePos){
|
||||
return FileUtil.coordsToFile(fileRoot, tilePos, "json.gz");
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,115 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.render.hires;
|
||||
|
||||
import com.flowpowered.math.vector.Vector3f;
|
||||
import com.flowpowered.math.vector.Vector3i;
|
||||
import com.flowpowered.math.vector.Vector4f;
|
||||
|
||||
import de.bluecolored.bluemap.core.logger.Logger;
|
||||
import de.bluecolored.bluemap.core.render.RenderSettings;
|
||||
import de.bluecolored.bluemap.core.render.WorldTile;
|
||||
import de.bluecolored.bluemap.core.render.context.SlicedWorldChunkBlockContext;
|
||||
import de.bluecolored.bluemap.core.render.hires.blockmodel.BlockStateModel;
|
||||
import de.bluecolored.bluemap.core.render.hires.blockmodel.BlockStateModelFactory;
|
||||
import de.bluecolored.bluemap.core.resourcepack.InvalidResourceDeclarationException;
|
||||
import de.bluecolored.bluemap.core.resourcepack.NoSuchResourceException;
|
||||
import de.bluecolored.bluemap.core.resourcepack.NoSuchTextureException;
|
||||
import de.bluecolored.bluemap.core.resourcepack.ResourcePack;
|
||||
import de.bluecolored.bluemap.core.util.AABB;
|
||||
import de.bluecolored.bluemap.core.util.MathUtil;
|
||||
import de.bluecolored.bluemap.core.world.Block;
|
||||
import de.bluecolored.bluemap.core.world.ChunkNotGeneratedException;
|
||||
import de.bluecolored.bluemap.core.world.WorldChunk;
|
||||
|
||||
public class HiresModelRenderer {
|
||||
|
||||
private BlockStateModelFactory modelFactory;
|
||||
|
||||
public HiresModelRenderer(ResourcePack resourcePack) {
|
||||
this(new BlockStateModelFactory(resourcePack));
|
||||
}
|
||||
|
||||
public HiresModelRenderer(BlockStateModelFactory modelFactory) {
|
||||
this.modelFactory = modelFactory;
|
||||
}
|
||||
|
||||
public HiresModel render(WorldTile tile, AABB region, RenderSettings renderSettings) throws ChunkNotGeneratedException {
|
||||
Vector3i min = region.getMin().toInt();
|
||||
Vector3i max = region.getMax().toInt();
|
||||
|
||||
min = new Vector3i(min.getX(), Math.max(min.getY(), renderSettings.getMinY()), min.getZ());
|
||||
max = new Vector3i(max.getX(), Math.min(max.getY(), Math.min(renderSettings.getMaxY(), renderSettings.getSliceY())), max.getZ());
|
||||
|
||||
WorldChunk chunk = tile.getWorld().getWorldChunk(region.expand(4, 0, 4));
|
||||
|
||||
if (!chunk.isGenerated()) throw new ChunkNotGeneratedException();
|
||||
|
||||
HiresModel model = new HiresModel(tile.getWorld().getUUID(), tile.getTile(), min, max);
|
||||
|
||||
for (int x = min.getX(); x <= max.getX(); x++){
|
||||
for (int z = min.getZ(); z <= max.getZ(); z++){
|
||||
|
||||
int maxHeight = 0;
|
||||
Vector4f color = Vector4f.ZERO;
|
||||
|
||||
for (int y = min.getY(); y <= max.getY(); y++){
|
||||
Block block = chunk.getBlock(x, y, z);
|
||||
if (block.getBlock().getId().equals("air")) continue;
|
||||
|
||||
maxHeight = y;
|
||||
|
||||
BlockStateModel blockModel;
|
||||
try {
|
||||
blockModel = modelFactory.createFrom(block.getBlock(), new SlicedWorldChunkBlockContext(chunk, new Vector3i(x, y, z), renderSettings.getSliceY()), renderSettings);
|
||||
} catch (NoSuchResourceException | InvalidResourceDeclarationException | NoSuchTextureException e) {
|
||||
blockModel = new BlockStateModel();
|
||||
Logger.global.noFloodDebug(block.getBlock().getId() + "-hiresModelRenderer-blockmodelerr", "Failed to create BlockModel for BlockState: " + block.getBlock() + " (" + e.toString() + ")");
|
||||
}
|
||||
|
||||
blockModel.translate(new Vector3f(x, y, z).sub(min.toFloat()));
|
||||
|
||||
color = MathUtil.overlayColors(blockModel.getMapColor(), color);
|
||||
|
||||
//TODO: quick hack to random offset grass
|
||||
if (block.getBlock().getId().equals("grass")){
|
||||
float dx = (MathUtil.hashToFloat(x, y, z, 123984) - 0.5f) * 0.75f;
|
||||
float dz = (MathUtil.hashToFloat(x, y, z, 345542) - 0.5f) * 0.75f;
|
||||
blockModel.translate(new Vector3f(dx, 0, dz));
|
||||
}
|
||||
|
||||
model.merge(blockModel);
|
||||
}
|
||||
|
||||
model.setHeight(x, z, maxHeight);
|
||||
model.setColor(x, z, color);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.render.hires.blockmodel;
|
||||
|
||||
import com.flowpowered.math.vector.Vector4f;
|
||||
|
||||
import de.bluecolored.bluemap.core.model.Model;
|
||||
import de.bluecolored.bluemap.core.util.MathUtil;
|
||||
|
||||
/**
|
||||
* A model with some extra information about the BlockState it represents
|
||||
*/
|
||||
public class BlockStateModel extends Model {
|
||||
|
||||
private Vector4f mapColor;
|
||||
|
||||
public BlockStateModel(){
|
||||
this(Vector4f.ZERO);
|
||||
}
|
||||
|
||||
public BlockStateModel(Vector4f mapColor) {
|
||||
this.mapColor = mapColor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void merge(Model model) {
|
||||
super.merge(model);
|
||||
|
||||
if (model instanceof BlockStateModel){
|
||||
mergeMapColor(((BlockStateModel) model).getMapColor());
|
||||
}
|
||||
}
|
||||
|
||||
public Vector4f getMapColor() {
|
||||
return mapColor;
|
||||
}
|
||||
|
||||
public void setMapColor(Vector4f mapColor) {
|
||||
this.mapColor = mapColor;
|
||||
}
|
||||
|
||||
public void mergeMapColor(Vector4f mapColor) {
|
||||
this.mapColor = MathUtil.blendColors(this.mapColor, mapColor);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,98 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.render.hires.blockmodel;
|
||||
|
||||
import de.bluecolored.bluemap.core.render.RenderSettings;
|
||||
import de.bluecolored.bluemap.core.render.context.EmptyBlockContext;
|
||||
import de.bluecolored.bluemap.core.render.context.ExtendedBlockContext;
|
||||
import de.bluecolored.bluemap.core.resourcepack.BlockStateResource;
|
||||
import de.bluecolored.bluemap.core.resourcepack.InvalidResourceDeclarationException;
|
||||
import de.bluecolored.bluemap.core.resourcepack.NoSuchResourceException;
|
||||
import de.bluecolored.bluemap.core.resourcepack.NoSuchTextureException;
|
||||
import de.bluecolored.bluemap.core.resourcepack.ResourcePack;
|
||||
import de.bluecolored.bluemap.core.world.BlockState;
|
||||
|
||||
public class BlockStateModelFactory {
|
||||
|
||||
private ResourcePack resourcePack;
|
||||
|
||||
public BlockStateModelFactory(ResourcePack resources) {
|
||||
this.resourcePack = resources;
|
||||
}
|
||||
|
||||
public BlockStateModel createFrom(BlockState blockState) throws NoSuchResourceException, InvalidResourceDeclarationException, NoSuchTextureException {
|
||||
return createFrom(blockState, EmptyBlockContext.instance(), new RenderSettings() {
|
||||
@Override
|
||||
public float getLightShadeMultiplier() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isExcludeFacesWithoutSunlight() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getAmbientOcclusionStrenght() {
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public BlockStateModel createFrom(BlockState blockState, ExtendedBlockContext context, RenderSettings renderSettings) throws NoSuchResourceException, InvalidResourceDeclarationException, NoSuchTextureException {
|
||||
|
||||
//air won't be rendered
|
||||
if (
|
||||
blockState.getId().equals("air") ||
|
||||
blockState.getId().equals("cave_air") ||
|
||||
blockState.getId().equals("void_air")
|
||||
) {
|
||||
return new BlockStateModel();
|
||||
}
|
||||
|
||||
// if it is a liquid, use the LiquidModelBuilder
|
||||
if (
|
||||
blockState.getId().equals("water") ||
|
||||
blockState.getId().equals("lava")
|
||||
){
|
||||
return new LiquidModelBuilder(blockState, context, resourcePack, renderSettings).build();
|
||||
}
|
||||
|
||||
// if no other model builder matched try to find a model definition from the resource-packs and use the default ResourceModelBuilder
|
||||
BlockStateResource resource = resourcePack.getBlockStateResource(blockState);
|
||||
BlockStateModel model = new ResourceModelBuilder(resource, context, resourcePack, renderSettings).build();
|
||||
|
||||
// if block is waterlogged
|
||||
if (LiquidModelBuilder.isWaterlogged(blockState)) {
|
||||
BlockStateModel watermodel = new LiquidModelBuilder(WATERLOGGED_BLOCKSTATE, context, resourcePack, renderSettings).build();
|
||||
model.merge(watermodel);
|
||||
}
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
private BlockState WATERLOGGED_BLOCKSTATE = new BlockState("minecraft:water");
|
||||
|
||||
}
|
@ -0,0 +1,245 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.render.hires.blockmodel;
|
||||
|
||||
import java.util.HashSet;
|
||||
|
||||
import com.flowpowered.math.matrix.Matrix3f;
|
||||
import com.flowpowered.math.vector.Vector2f;
|
||||
import com.flowpowered.math.vector.Vector3f;
|
||||
import com.flowpowered.math.vector.Vector4f;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
import de.bluecolored.bluemap.core.model.Face;
|
||||
import de.bluecolored.bluemap.core.model.Model;
|
||||
import de.bluecolored.bluemap.core.render.RenderSettings;
|
||||
import de.bluecolored.bluemap.core.render.context.ExtendedBlockContext;
|
||||
import de.bluecolored.bluemap.core.resourcepack.NoSuchTextureException;
|
||||
import de.bluecolored.bluemap.core.resourcepack.ResourcePack;
|
||||
import de.bluecolored.bluemap.core.util.Direction;
|
||||
import de.bluecolored.bluemap.core.world.Block;
|
||||
import de.bluecolored.bluemap.core.world.BlockState;
|
||||
|
||||
/**
|
||||
* A model builder for all liquid blocks
|
||||
*/
|
||||
public class LiquidModelBuilder {
|
||||
|
||||
private static final HashSet<String> DEFAULT_WATERLOGGED_BLOCK_IDS = Sets.newHashSet(
|
||||
"minecraft:seagrass",
|
||||
"minecraft:tall_seagrass",
|
||||
"minecraft:kelp"
|
||||
);
|
||||
|
||||
private BlockState blockState;
|
||||
private ExtendedBlockContext context;
|
||||
private ResourcePack resourcePack;
|
||||
private RenderSettings renderSettings;
|
||||
|
||||
private float[] heights;
|
||||
|
||||
public LiquidModelBuilder(BlockState blockState, ExtendedBlockContext context, ResourcePack resourcePack, RenderSettings renderSettings) {
|
||||
this.blockState = blockState;
|
||||
this.context = context;
|
||||
this.resourcePack = resourcePack;
|
||||
this.renderSettings = renderSettings;
|
||||
|
||||
this.heights = new float[]{14f, 14f, 14f, 14f};
|
||||
}
|
||||
|
||||
public BlockStateModel build() throws NoSuchTextureException {
|
||||
if (this.renderSettings.isExcludeFacesWithoutSunlight() && context.getRelativeBlock(0, 0, 0).getSunLightLevel() == 0) return new BlockStateModel();
|
||||
|
||||
int level = getLiquidLevel(blockState);
|
||||
|
||||
if (level >= 8 ||level == 0 && isLiquid(context.getRelativeBlock(0, 1, 0))){
|
||||
this.heights = new float[]{16f, 16f, 16f, 16f};
|
||||
return buildModel();
|
||||
}
|
||||
|
||||
this.heights = new float[]{
|
||||
getLiquidCornerHeight(-1, 0, -1),
|
||||
getLiquidCornerHeight(-1, 0, 0),
|
||||
getLiquidCornerHeight(0, 0, -1),
|
||||
getLiquidCornerHeight(0, 0, 0)
|
||||
};
|
||||
|
||||
return buildModel();
|
||||
}
|
||||
|
||||
private float getLiquidCornerHeight(int x, int y, int z){
|
||||
for (int ix = x; ix <= x+1; ix++){
|
||||
for (int iz = z; iz<= z+1; iz++){
|
||||
if (isLiquid(context.getRelativeBlock(ix, y+1, iz))){
|
||||
return 16f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float sumHeight = 0f;
|
||||
int count = 0;
|
||||
|
||||
for (int ix = x; ix <= x+1; ix++){
|
||||
for (int iz = z; iz<= z+1; iz++){
|
||||
Block b = context.getRelativeBlock(ix, y, iz);
|
||||
if (isLiquid(b)){
|
||||
if (getLiquidLevel(b.getBlock()) == 0) return 14f;
|
||||
|
||||
sumHeight += getLiquidBaseHeight(b.getBlock());
|
||||
count++;
|
||||
}
|
||||
|
||||
else if (!isLiquidBlockingBlock(b)){
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//should both never happen
|
||||
if (sumHeight == 0) return 3f;
|
||||
if (count == 0) return 3f;
|
||||
|
||||
return sumHeight / count;
|
||||
}
|
||||
|
||||
private boolean isLiquidBlockingBlock(Block b){
|
||||
if (b.getBlock().getId().equals("air")) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean isLiquid(Block block){
|
||||
return isLiquid(block.getBlock());
|
||||
}
|
||||
|
||||
private boolean isLiquid(BlockState blockstate){
|
||||
if (blockstate.getId().equals(blockState.getId())) return true;
|
||||
return LiquidModelBuilder.isWaterlogged(blockstate);
|
||||
}
|
||||
|
||||
private float getLiquidBaseHeight(BlockState block){
|
||||
int level = getLiquidLevel(block);
|
||||
float baseHeight = 14f - level * 1.9f;
|
||||
return baseHeight;
|
||||
}
|
||||
|
||||
private int getLiquidLevel(BlockState block){
|
||||
if (block.getProperties().containsKey("level")) {
|
||||
return Integer.parseInt(block.getProperties().get("level"));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private BlockStateModel buildModel() throws NoSuchTextureException {
|
||||
BlockStateModel model = new BlockStateModel();
|
||||
|
||||
Vector3f[] c = new Vector3f[]{
|
||||
new Vector3f( 0, 0, 0 ),
|
||||
new Vector3f( 0, 0, 16 ),
|
||||
new Vector3f( 16, 0, 0 ),
|
||||
new Vector3f( 16, 0, 16 ),
|
||||
new Vector3f( 0, heights[0], 0 ),
|
||||
new Vector3f( 0, heights[1], 16 ),
|
||||
new Vector3f( 16, heights[2], 0 ),
|
||||
new Vector3f( 16, heights[3], 16 ),
|
||||
};
|
||||
|
||||
int textureId = resourcePack.getTextureProvider().getTextureIndex("block/" + blockState.getId() + "_still");
|
||||
Vector3f tintcolor = Vector3f.ONE;
|
||||
if (blockState.getId().equals("water")) {
|
||||
tintcolor = resourcePack.getBlockColorProvider().getBiomeWaterAverageColor(context);
|
||||
}
|
||||
|
||||
createElementFace(model, Direction.DOWN, c[0], c[2], c[3], c[1], tintcolor, textureId);
|
||||
createElementFace(model, Direction.UP, c[5], c[7], c[6], c[4], tintcolor, textureId);
|
||||
createElementFace(model, Direction.NORTH, c[2], c[0], c[4], c[6], tintcolor, textureId);
|
||||
createElementFace(model, Direction.SOUTH, c[1], c[3], c[7], c[5], tintcolor, textureId);
|
||||
createElementFace(model, Direction.WEST, c[0], c[1], c[5], c[4], tintcolor, textureId);
|
||||
createElementFace(model, Direction.EAST, c[3], c[2], c[6], c[7], tintcolor, textureId);
|
||||
|
||||
//scale down
|
||||
model.transform(Matrix3f.createScaling(1f / 16f));
|
||||
|
||||
//calculate mapcolor
|
||||
Vector4f mapcolor = resourcePack.getTextureProvider().getTexture("block/" + blockState.getId() + "_still").getColor();
|
||||
mapcolor = mapcolor.mul(tintcolor.toVector4(1));
|
||||
model.setMapColor(mapcolor);
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
private void createElementFace(Model model, Direction faceDir, Vector3f c0, Vector3f c1, Vector3f c2, Vector3f c3, Vector3f color, int textureId) throws NoSuchTextureException {
|
||||
|
||||
//face culling
|
||||
Block bl = context.getRelativeBlock(faceDir);
|
||||
if (isLiquid(bl) || (faceDir != Direction.UP && bl.isCullingNeighborFaces())) return;
|
||||
|
||||
//UV
|
||||
Vector4f uv = new Vector4f(0, 0, 16, 16).div(16);
|
||||
|
||||
//create both triangles
|
||||
Vector2f[] uvs = new Vector2f[4];
|
||||
uvs[0] = new Vector2f(uv.getX(), uv.getW());
|
||||
uvs[1] = new Vector2f(uv.getZ(), uv.getW());
|
||||
uvs[2] = new Vector2f(uv.getZ(), uv.getY());
|
||||
uvs[3] = new Vector2f(uv.getX(), uv.getY());
|
||||
|
||||
Face f1 = new Face(c0, c1, c2, uvs[0], uvs[1], uvs[2], textureId);
|
||||
Face f2 = new Face(c0, c2, c3, uvs[0], uvs[2], uvs[3], textureId);
|
||||
|
||||
//move face in a tiny bit to avoid z-fighting with waterlogged blocks
|
||||
f1.translate(faceDir.opposite().toVector().toFloat().mul(0.01));
|
||||
f2.translate(faceDir.opposite().toVector().toFloat().mul(0.01));
|
||||
|
||||
float light = 1f;
|
||||
if (renderSettings.getLightShadeMultiplier() > 0) {
|
||||
light = 0f;
|
||||
for (Direction d : Direction.values()){
|
||||
Block b = context.getRelativeBlock(d.toVector());
|
||||
float l = (float) (Math.max(b.getBlockLightLevel(), b.getSunLightLevel()) / 15f) * renderSettings.getLightShadeMultiplier() + (1 - renderSettings.getLightShadeMultiplier());
|
||||
if (l > light) light = l;
|
||||
}
|
||||
}
|
||||
|
||||
color = color.mul(light);
|
||||
|
||||
f1.setC1(color);
|
||||
f1.setC2(color);
|
||||
f1.setC3(color);
|
||||
|
||||
f2.setC1(color);
|
||||
f2.setC2(color);
|
||||
f2.setC3(color);
|
||||
|
||||
//add the face
|
||||
model.addFace(f1);
|
||||
model.addFace(f2);
|
||||
}
|
||||
|
||||
public static boolean isWaterlogged(BlockState blockState) {
|
||||
if (DEFAULT_WATERLOGGED_BLOCK_IDS.contains(blockState.getFullId())) return true;
|
||||
return blockState.getProperties().getOrDefault("waterlogged", "false").equals("true");
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,386 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.render.hires.blockmodel;
|
||||
|
||||
import com.flowpowered.math.TrigMath;
|
||||
import com.flowpowered.math.imaginary.Complexf;
|
||||
import com.flowpowered.math.imaginary.Quaternionf;
|
||||
import com.flowpowered.math.matrix.Matrix3f;
|
||||
import com.flowpowered.math.vector.Vector2f;
|
||||
import com.flowpowered.math.vector.Vector3f;
|
||||
import com.flowpowered.math.vector.Vector3i;
|
||||
import com.flowpowered.math.vector.Vector4f;
|
||||
|
||||
import de.bluecolored.bluemap.core.model.Face;
|
||||
import de.bluecolored.bluemap.core.render.RenderSettings;
|
||||
import de.bluecolored.bluemap.core.render.context.BlockContext;
|
||||
import de.bluecolored.bluemap.core.render.context.ExtendedBlockContext;
|
||||
import de.bluecolored.bluemap.core.resourcepack.BlockModelElementFaceResource;
|
||||
import de.bluecolored.bluemap.core.resourcepack.BlockModelElementResource;
|
||||
import de.bluecolored.bluemap.core.resourcepack.BlockModelResource;
|
||||
import de.bluecolored.bluemap.core.resourcepack.BlockStateResource;
|
||||
import de.bluecolored.bluemap.core.resourcepack.NoSuchTextureException;
|
||||
import de.bluecolored.bluemap.core.resourcepack.ResourcePack;
|
||||
import de.bluecolored.bluemap.core.resourcepack.TextureProvider.Texture;
|
||||
import de.bluecolored.bluemap.core.util.Direction;
|
||||
import de.bluecolored.bluemap.core.util.MathUtil;
|
||||
import de.bluecolored.bluemap.core.util.WeighedArrayList;
|
||||
import de.bluecolored.bluemap.core.world.Block;
|
||||
|
||||
/**
|
||||
* This model builder creates a BlockStateModel using the information from parsed resource-pack json files.
|
||||
*/
|
||||
public class ResourceModelBuilder {
|
||||
|
||||
private static final Vector3f HALF_3F = Vector3f.ONE.mul(0.5);
|
||||
private static final Vector3f NEG_HALF_3F = HALF_3F.negate();
|
||||
private static final Vector2f HALF_2F = Vector2f.ONE.mul(0.5);
|
||||
|
||||
private BlockStateResource resource;
|
||||
private ExtendedBlockContext context;
|
||||
private ResourcePack resourcePack;
|
||||
private RenderSettings renderSettings;
|
||||
|
||||
public ResourceModelBuilder(BlockStateResource resource, ExtendedBlockContext context, ResourcePack resourcePack, RenderSettings renderSettings) {
|
||||
this.resource = resource;
|
||||
this.context = context;
|
||||
this.resourcePack = resourcePack;
|
||||
this.renderSettings = renderSettings;
|
||||
}
|
||||
|
||||
public BlockStateModel build() throws NoSuchTextureException {
|
||||
BlockStateModel model = new BlockStateModel();
|
||||
|
||||
for (WeighedArrayList<BlockModelResource> bmrList : resource.getModelResources()){
|
||||
BlockModelResource bmr = bmrList.get((int) Math.floor(MathUtil.hashToFloat(context.getPosition(), 23489756) * (float) bmrList.size()));
|
||||
|
||||
model.merge(fromModelResource(bmr));
|
||||
}
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
private BlockStateModel fromModelResource(BlockModelResource bmr) throws NoSuchTextureException {
|
||||
BlockStateModel model = new BlockStateModel();
|
||||
|
||||
for (BlockModelElementResource bmer : bmr.getElements()){
|
||||
model.merge(fromModelElementResource(bmer));
|
||||
}
|
||||
|
||||
model.translate(NEG_HALF_3F);
|
||||
model.rotate(Quaternionf.fromAxesAnglesDeg(
|
||||
-bmr.getXRot(),
|
||||
-bmr.getYRot(),
|
||||
0
|
||||
));
|
||||
model.translate(HALF_3F);
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
private BlockStateModel fromModelElementResource(BlockModelElementResource bmer) throws NoSuchTextureException {
|
||||
BlockStateModel model = new BlockStateModel();
|
||||
|
||||
//create faces
|
||||
Vector3f min = bmer.getFrom().min(bmer.getTo());
|
||||
Vector3f max = bmer.getFrom().max(bmer.getTo());
|
||||
|
||||
Vector3f[] c = new Vector3f[]{
|
||||
new Vector3f( min .getX(), min .getY(), min .getZ()),
|
||||
new Vector3f( min .getX(), min .getY(), max .getZ()),
|
||||
new Vector3f( max .getX(), min .getY(), min .getZ()),
|
||||
new Vector3f( max .getX(), min .getY(), max .getZ()),
|
||||
new Vector3f( min .getX(), max .getY(), min .getZ()),
|
||||
new Vector3f( min .getX(), max .getY(), max .getZ()),
|
||||
new Vector3f( max .getX(), max .getY(), min .getZ()),
|
||||
new Vector3f( max .getX(), max .getY(), max .getZ()),
|
||||
};
|
||||
|
||||
createElementFace(model, bmer.getDownFace(), Direction.DOWN, c[0], c[2], c[3], c[1]);
|
||||
createElementFace(model, bmer.getUpFace(), Direction.UP, c[5], c[7], c[6], c[4]);
|
||||
createElementFace(model, bmer.getNorthFace(), Direction.NORTH, c[2], c[0], c[4], c[6]);
|
||||
createElementFace(model, bmer.getSouthFace(), Direction.SOUTH, c[1], c[3], c[7], c[5]);
|
||||
createElementFace(model, bmer.getWestFace(), Direction.WEST, c[0], c[1], c[5], c[4]);
|
||||
createElementFace(model, bmer.getEastFace(), Direction.EAST, c[3], c[2], c[6], c[7]);
|
||||
|
||||
//rotate
|
||||
if (bmer.isRotation()){
|
||||
Vector3f translation = bmer.getRotationOrigin();
|
||||
model.translate(translation.negate());
|
||||
|
||||
model.rotate(Quaternionf.fromAngleDegAxis(
|
||||
bmer.getRotationAngle(),
|
||||
bmer.getRotationAxis().toVector().toFloat()
|
||||
));
|
||||
|
||||
if (bmer.isRotationRescale()){
|
||||
Vector3f scale =
|
||||
Vector3f.ONE
|
||||
.sub(bmer.getRotationAxis().toVector().toFloat())
|
||||
.mul(Math.abs(TrigMath.sin(bmer.getRotationAngle() * TrigMath.DEG_TO_RAD)))
|
||||
.mul(1 - (TrigMath.SQRT_OF_TWO - 1))
|
||||
.add(Vector3f.ONE);
|
||||
model.transform(Matrix3f.createScaling(scale));
|
||||
}
|
||||
|
||||
model.translate(translation);
|
||||
|
||||
}
|
||||
|
||||
//scale down
|
||||
model.transform(Matrix3f.createScaling(1f / 16f));
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
private void createElementFace(BlockStateModel model, BlockModelElementFaceResource face, Direction faceDir, Vector3f c0, Vector3f c1, Vector3f c2, Vector3f c3) throws NoSuchTextureException {
|
||||
if (face == null) return;
|
||||
BlockModelResource m = face.getElement().getModel();
|
||||
|
||||
//face culling
|
||||
if (face.isCullface()){
|
||||
Block b = getRotationRelativeBlock(m, face.getCullface());
|
||||
if (b.isCullingNeighborFaces()) return;
|
||||
}
|
||||
|
||||
//light calculation
|
||||
Block b = getRotationRelativeBlock(m, faceDir);
|
||||
BlockContext bContext = context.getRelativeView(getRotationRelativeDirectionVector(m, faceDir.toVector().toFloat()).toInt());
|
||||
float skyLight = b.getPassedSunLight(bContext);
|
||||
|
||||
//filter out faces that are not skylighted
|
||||
if (skyLight == 0f && renderSettings.isExcludeFacesWithoutSunlight()) return;
|
||||
|
||||
float light = 1;
|
||||
if (renderSettings.getLightShadeMultiplier() > 0) {
|
||||
float blockLight = b.getPassedBlockLight(bContext);
|
||||
light = (Math.max(skyLight, blockLight) / 15f) * renderSettings.getLightShadeMultiplier() + (1 - renderSettings.getLightShadeMultiplier());
|
||||
if (light > 1) light = 1;
|
||||
if (light < 0) light = 0;
|
||||
}
|
||||
|
||||
//UV
|
||||
Vector4f uv = face.getUv().toFloat().div(16);
|
||||
|
||||
//UV-Lock counter-rotation
|
||||
int uvLockAngle = 0;
|
||||
if (m.isUvLock()){
|
||||
Quaternionf rot = Quaternionf.fromAxesAnglesDeg(m.getXRot(), m.getYRot(), 0);
|
||||
uvLockAngle = (int) rot.getAxesAnglesDeg().dot(faceDir.toVector().toFloat());
|
||||
|
||||
//TODO: my math has stopped working, there has to be a more consistent solution
|
||||
if (m.getXRot() >= 180 && m.getYRot() != 90 && m.getYRot() != 270) uvLockAngle += 180;
|
||||
}
|
||||
|
||||
//create both triangles
|
||||
Vector2f[] uvs = new Vector2f[4];
|
||||
uvs[0] = new Vector2f(uv.getX(), uv.getW());
|
||||
uvs[1] = new Vector2f(uv.getZ(), uv.getW());
|
||||
uvs[2] = new Vector2f(uv.getZ(), uv.getY());
|
||||
uvs[3] = new Vector2f(uv.getX(), uv.getY());
|
||||
|
||||
//face texture rotation
|
||||
uvs = rotateUVOuter(uvs, uvLockAngle);
|
||||
uvs = rotateUVInner(uvs, face.getRotation());
|
||||
|
||||
String textureName = face.getResolvedTexture();
|
||||
if (textureName == null) throw new NoSuchTextureException("There is no Texture-Definition for a face: " + faceDir + " of block: " + resource.getBlock());
|
||||
|
||||
int textureId = resourcePack.getTextureProvider().getTextureIndex(textureName);
|
||||
|
||||
Face f1 = new Face(c0, c1, c2, uvs[0], uvs[1], uvs[2], textureId);
|
||||
Face f2 = new Face(c0, c2, c3, uvs[0], uvs[2], uvs[3], textureId);
|
||||
|
||||
//calculate ao
|
||||
double ao0 = 1d, ao1 = 1d, ao2 = 1d, ao3 = 1d;
|
||||
if (renderSettings.getAmbientOcclusionStrenght() > 0f && m.isAmbientOcclusion()){
|
||||
ao0 = testAo(m, c0, faceDir);
|
||||
ao1 = testAo(m, c1, faceDir);
|
||||
ao2 = testAo(m, c2, faceDir);
|
||||
ao3 = testAo(m, c3, faceDir);
|
||||
}
|
||||
|
||||
//tint the face
|
||||
Vector3f color = Vector3f.ONE;
|
||||
if (face.isTinted()){
|
||||
color = resourcePack.getBlockColorProvider().getBlockColor(context);
|
||||
}
|
||||
|
||||
color = color.mul(light);
|
||||
|
||||
Vector3f aoColor;
|
||||
|
||||
aoColor = color.mul(ao0);
|
||||
f1.setC1(aoColor);
|
||||
f2.setC1(aoColor);
|
||||
|
||||
aoColor = color.mul(ao1);
|
||||
f1.setC2(aoColor);
|
||||
|
||||
aoColor = color.mul(ao2);
|
||||
f1.setC3(aoColor);
|
||||
f2.setC2(aoColor);
|
||||
|
||||
aoColor = color.mul(ao3);
|
||||
f2.setC3(aoColor);
|
||||
|
||||
//add the face
|
||||
model.addFace(f1);
|
||||
model.addFace(f2);
|
||||
|
||||
//if is top face set model-color
|
||||
Vector3f dir = getRotationRelativeDirectionVector(m, faceDir.toVector().toFloat());
|
||||
|
||||
BlockModelElementResource bmer = face.getElement();
|
||||
if (bmer.isRotation()){
|
||||
Quaternionf rot = Quaternionf.fromAngleDegAxis(
|
||||
bmer.getRotationAngle(),
|
||||
bmer.getRotationAxis().toVector().toFloat()
|
||||
);
|
||||
dir = rot.rotate(dir);
|
||||
}
|
||||
|
||||
float a = dir.getY();
|
||||
if (a > 0){
|
||||
Texture t = resourcePack.getTextureProvider().getTexture(textureId);
|
||||
if (t != null){
|
||||
Vector4f c = t.getColor();
|
||||
c = c.mul(color.toVector4(1f));
|
||||
c = new Vector4f(c.getX(), c.getY(), c.getZ(), c.getW() * a);
|
||||
model.mergeMapColor(c);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private Block getRotationRelativeBlock(BlockModelResource model, Direction direction){
|
||||
return getRotationRelativeBlock(model, direction.toVector());
|
||||
}
|
||||
|
||||
private Block getRotationRelativeBlock(BlockModelResource model, Vector3i direction){
|
||||
Vector3i dir = getRotationRelativeDirectionVector(model, direction.toFloat()).round().toInt();
|
||||
return context.getRelativeBlock(dir);
|
||||
}
|
||||
|
||||
private Vector3f getRotationRelativeDirectionVector(BlockModelResource model, Vector3f direction){
|
||||
Quaternionf rot = Quaternionf.fromAxesAnglesDeg(
|
||||
-model.getXRot(),
|
||||
-model.getYRot(),
|
||||
0
|
||||
);
|
||||
Vector3f dir = rot.rotate(direction);
|
||||
return dir;
|
||||
}
|
||||
|
||||
private double testAo(BlockModelResource model, Vector3f vertex, Direction dir){
|
||||
int occluding = 0;
|
||||
|
||||
int x = 0;
|
||||
if (vertex.getX() == 16){
|
||||
x = 1;
|
||||
} else if (vertex.getX() == 0){
|
||||
x = -1;
|
||||
}
|
||||
|
||||
int y = 0;
|
||||
if (vertex.getY() == 16){
|
||||
y = 1;
|
||||
} else if (vertex.getY() == 0){
|
||||
y = -1;
|
||||
}
|
||||
|
||||
int z = 0;
|
||||
if (vertex.getZ() == 16){
|
||||
z = 1;
|
||||
} else if (vertex.getZ() == 0){
|
||||
z = -1;
|
||||
}
|
||||
|
||||
Vector3i rel = new Vector3i(x, y, 0);
|
||||
if (rel.dot(dir.toVector()) > 0){
|
||||
if (getRotationRelativeBlock(model, rel).isOccludingNeighborFaces()) occluding++;
|
||||
}
|
||||
|
||||
rel = new Vector3i(x, 0, z);
|
||||
if (rel.dot(dir.toVector()) > 0){
|
||||
if (getRotationRelativeBlock(model, rel).isOccludingNeighborFaces()) occluding++;
|
||||
}
|
||||
|
||||
rel = new Vector3i(0, y, z);
|
||||
if (rel.dot(dir.toVector()) > 0){
|
||||
if (getRotationRelativeBlock(model, rel).isOccludingNeighborFaces()) occluding++;
|
||||
}
|
||||
|
||||
rel = new Vector3i(x, y, z);
|
||||
if (rel.dot(dir.toVector()) > 0){
|
||||
if (getRotationRelativeBlock(model, rel).isOccludingNeighborFaces()) occluding++;
|
||||
}
|
||||
|
||||
if (occluding > 3)
|
||||
occluding = 3;
|
||||
|
||||
return Math.max(0.0, Math.min(1.0 - ((double) occluding * renderSettings.getAmbientOcclusionStrenght()), 1.0));
|
||||
}
|
||||
|
||||
private Vector2f[] rotateUVInner(Vector2f[] uv, int angle){
|
||||
if (uv.length == 0) return uv;
|
||||
|
||||
int steps = getRotationSteps(angle);
|
||||
|
||||
for (int i = 0; i < steps; i++){
|
||||
Vector2f first = uv[uv.length - 1];
|
||||
System.arraycopy(uv, 0, uv, 1, uv.length - 1);
|
||||
uv[0] = first;
|
||||
}
|
||||
|
||||
return uv;
|
||||
}
|
||||
|
||||
private Vector2f[] rotateUVOuter(Vector2f[] uv, float angle){
|
||||
angle %= 360;
|
||||
if (angle < 0) angle += 360;
|
||||
|
||||
if (angle == 0) return uv;
|
||||
|
||||
Complexf c = Complexf.fromAngleDeg(angle);
|
||||
|
||||
for (int i = 0; i < uv.length; i++){
|
||||
uv[i] = uv[i].sub(HALF_2F);
|
||||
uv[i] = c.rotate(uv[i]);
|
||||
uv[i] = uv[i].add(HALF_2F);
|
||||
}
|
||||
|
||||
return uv;
|
||||
}
|
||||
|
||||
private int getRotationSteps(int angle){
|
||||
angle = -Math.floorDiv(angle, 90);
|
||||
angle %= 4;
|
||||
if (angle < 0) angle += 4;
|
||||
|
||||
return angle;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,229 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.render.lowres;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.PrintWriter;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
|
||||
import com.flowpowered.math.vector.Vector2i;
|
||||
import com.flowpowered.math.vector.Vector3f;
|
||||
|
||||
import de.bluecolored.bluemap.core.threejs.BufferGeometry;
|
||||
import de.bluecolored.bluemap.core.util.FileUtil;
|
||||
import de.bluecolored.bluemap.core.util.MathUtil;
|
||||
import de.bluecolored.bluemap.core.util.ModelUtils;
|
||||
|
||||
public class LowresModel {
|
||||
|
||||
private UUID world;
|
||||
private Vector2i tilePos;
|
||||
private BufferGeometry model;
|
||||
|
||||
private Map<Vector2i, LowresPoint> changes;
|
||||
|
||||
private boolean hasUnsavedChanges;
|
||||
|
||||
private final Object
|
||||
fileLock = new Object(),
|
||||
modelLock = new Object();
|
||||
|
||||
public LowresModel(UUID world, Vector2i tilePos, Vector2i gridSize) {
|
||||
this(
|
||||
world,
|
||||
tilePos,
|
||||
ModelUtils.makeGrid(gridSize).toBufferGeometry()
|
||||
);
|
||||
}
|
||||
|
||||
public LowresModel(UUID world, Vector2i tilePos, BufferGeometry model) {
|
||||
this.world = world;
|
||||
this.tilePos = tilePos;
|
||||
this.model = model;
|
||||
|
||||
this.changes = new ConcurrentHashMap<>();
|
||||
|
||||
this.hasUnsavedChanges = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for all vertices at that point on the grid-model and change the height and color.<br>
|
||||
* <br>
|
||||
* <i>
|
||||
* Implementation note:<br>
|
||||
* The vertex x, z -coords are rounded, so we can compare them using == without worrying about floating point rounding differences.<br>
|
||||
* </i>
|
||||
*/
|
||||
public void update(Vector2i point, float height, Vector3f color){
|
||||
changes.put(point, new LowresPoint(height, color));
|
||||
this.hasUnsavedChanges = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves this model to its file
|
||||
* @param force if this is false, the model is only saved if it has any changes
|
||||
*/
|
||||
public void save(File file, boolean force) throws IOException {
|
||||
if (!force && !hasUnsavedChanges) return;
|
||||
this.hasUnsavedChanges = false;
|
||||
|
||||
flush();
|
||||
|
||||
String json;
|
||||
synchronized (modelLock) {
|
||||
json = model.toJson();
|
||||
}
|
||||
|
||||
synchronized (fileLock) {
|
||||
if (!file.exists()){
|
||||
file.getParentFile().mkdirs();
|
||||
file.createNewFile();
|
||||
}
|
||||
|
||||
try {
|
||||
FileUtil.waitForFile(file, 10, TimeUnit.SECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
throw new IOException("Failed to get write-access to file: " + file, e);
|
||||
}
|
||||
|
||||
FileOutputStream fos = new FileOutputStream(file);
|
||||
GZIPOutputStream zos = new GZIPOutputStream(fos);
|
||||
OutputStreamWriter osw = new OutputStreamWriter(zos, StandardCharsets.UTF_8);
|
||||
try (
|
||||
PrintWriter pw = new PrintWriter(osw);
|
||||
){
|
||||
pw.print(json);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public void flush(){
|
||||
if (changes.isEmpty()) return;
|
||||
|
||||
synchronized (modelLock) {
|
||||
if (changes.isEmpty()) return;
|
||||
|
||||
Map<Vector2i, LowresPoint> points = changes;
|
||||
changes = new HashMap<>();
|
||||
|
||||
int vertexCount = model.position.length / 3;
|
||||
|
||||
for (int i = 0; i < vertexCount; i++){
|
||||
int j = i * 3;
|
||||
int px = Math.round(model.position[j + 0]);
|
||||
int pz = Math.round(model.position[j + 2]);
|
||||
|
||||
Vector2i p = new Vector2i(px, pz);
|
||||
|
||||
LowresPoint lrp = points.get(p);
|
||||
if (lrp == null) continue;
|
||||
|
||||
model.position[j + 1] = lrp.height;
|
||||
|
||||
model.color[j + 0] = lrp.color.getX();
|
||||
model.color[j + 1] = lrp.color.getY();
|
||||
model.color[j + 2] = lrp.color.getZ();
|
||||
|
||||
//recalculate normals
|
||||
int f = Math.floorDiv(i, 3) * 3 * 3;
|
||||
Vector3f p1 = new Vector3f(model.position[f + 0], model.position[f + 1], model.position[f + 2]);
|
||||
Vector3f p2 = new Vector3f(model.position[f + 3], model.position[f + 4], model.position[f + 5]);
|
||||
Vector3f p3 = new Vector3f(model.position[f + 6], model.position[f + 7], model.position[f + 8]);
|
||||
|
||||
Vector3f n = MathUtil.getSurfaceNormal(p1, p2, p3);
|
||||
|
||||
model.normal[f + 0] = n.getX(); model.normal[f + 1] = n.getY(); model.normal[f + 2] = n.getZ();
|
||||
model.normal[f + 3] = n.getX(); model.normal[f + 4] = n.getY(); model.normal[f + 5] = n.getZ();
|
||||
model.normal[f + 6] = n.getX(); model.normal[f + 7] = n.getY(); model.normal[f + 8] = n.getZ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public BufferGeometry getBufferGeometry(){
|
||||
flush();
|
||||
return model;
|
||||
}
|
||||
|
||||
public UUID getWorld(){
|
||||
return world;
|
||||
}
|
||||
|
||||
public Vector2i getTile(){
|
||||
return tilePos;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(world, tilePos);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj instanceof LowresModel){
|
||||
LowresModel other = (LowresModel) obj;
|
||||
if (!other.world.equals(world)) return false;
|
||||
if (other.tilePos.equals(tilePos)) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* a point on this lowres-model-grid
|
||||
*/
|
||||
public class LowresPoint {
|
||||
private float height;
|
||||
private Vector3f color;
|
||||
|
||||
public LowresPoint(float height, Vector3f color) {
|
||||
this.height = height;
|
||||
this.color = color;
|
||||
}
|
||||
|
||||
public LowresPoint add(LowresPoint other){
|
||||
float newHeight = height + other.height;
|
||||
Vector3f newColor = color.add(other.color);
|
||||
return new LowresPoint(newHeight, newColor);
|
||||
}
|
||||
|
||||
public LowresPoint div(float divisor){
|
||||
float newHeight = height / divisor;
|
||||
Vector3f newColor = color.div(divisor);
|
||||
return new LowresPoint(newHeight, newColor);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,309 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.render.lowres;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
||||
import com.flowpowered.math.vector.Vector2i;
|
||||
import com.flowpowered.math.vector.Vector3d;
|
||||
import com.flowpowered.math.vector.Vector3f;
|
||||
import com.flowpowered.math.vector.Vector3i;
|
||||
import com.flowpowered.math.vector.Vector4f;
|
||||
|
||||
import de.bluecolored.bluemap.core.logger.Logger;
|
||||
import de.bluecolored.bluemap.core.render.hires.HiresModel;
|
||||
import de.bluecolored.bluemap.core.threejs.BufferGeometry;
|
||||
import de.bluecolored.bluemap.core.util.FileUtil;
|
||||
|
||||
public class LowresModelManager {
|
||||
|
||||
private Path fileRoot;
|
||||
|
||||
private Vector2i gridSize;
|
||||
private Vector2i pointsPerHiresTile;
|
||||
|
||||
private Map<File, CachedModel> models;
|
||||
|
||||
public LowresModelManager(Path fileRoot, Vector2i gridSize, Vector2i pointsPerHiresTile) {
|
||||
this.fileRoot = fileRoot;
|
||||
|
||||
this.gridSize = gridSize;
|
||||
this.pointsPerHiresTile = pointsPerHiresTile;
|
||||
|
||||
models = new ConcurrentHashMap<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders all points from the given highres-model onto the lowres-grid
|
||||
*/
|
||||
public void render(HiresModel hiresModel) throws IOException {
|
||||
Vector3i min = hiresModel.getBlockMin();
|
||||
Vector3i max = hiresModel.getBlockMax();
|
||||
Vector3i size = max.sub(min).add(Vector3i.ONE);
|
||||
|
||||
Vector2i blocksPerPoint =
|
||||
size
|
||||
.toVector2(true)
|
||||
.div(pointsPerHiresTile);
|
||||
|
||||
Vector2i pointMin = min
|
||||
.toVector2(true)
|
||||
.toDouble()
|
||||
.div(blocksPerPoint.toDouble())
|
||||
.floor()
|
||||
.toInt();
|
||||
|
||||
for (int tx = 0; tx < pointsPerHiresTile.getX(); tx++){
|
||||
for (int tz = 0; tz < pointsPerHiresTile.getY(); tz++){
|
||||
|
||||
double height = 0;
|
||||
|
||||
Vector3d color = Vector3d.ZERO;
|
||||
double colorCount = 0;
|
||||
|
||||
for (int x = 0; x < blocksPerPoint.getX(); x++){
|
||||
for (int z = 0; z < blocksPerPoint.getY(); z++){
|
||||
|
||||
int rx = tx * blocksPerPoint.getX() + x + min.getX();
|
||||
int rz = tz * blocksPerPoint.getY() + z + min.getZ();
|
||||
height += hiresModel.getHeight(rx, rz);
|
||||
|
||||
Vector4f c = hiresModel.getColor(rx, rz);
|
||||
color = color.add(c.toVector3().toDouble().mul(c.getW()));
|
||||
colorCount += c.getW();
|
||||
}
|
||||
}
|
||||
|
||||
if (colorCount > 0) color = color.div(colorCount);
|
||||
|
||||
int count = blocksPerPoint.getX() * blocksPerPoint.getY();
|
||||
height /= count;
|
||||
|
||||
Vector2i point = pointMin.add(tx, tz);
|
||||
update(hiresModel.getWorld(), point, (float) height, color.toFloat());
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves all unsaved changes to the models to disk
|
||||
*/
|
||||
public synchronized void save(){
|
||||
for (CachedModel model : models.values()){
|
||||
saveModel(model);
|
||||
}
|
||||
|
||||
tidyUpModelCache();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a point on the lowresmodel-grid
|
||||
*/
|
||||
public void update(UUID world, Vector2i point, float height, Vector3f color) throws IOException {
|
||||
Vector2i tile = pointToTile(point);
|
||||
Vector2i relPoint = getPointRelativeToTile(tile, point);
|
||||
LowresModel model = getModel(world, tile);
|
||||
model.update(relPoint, height, color);
|
||||
|
||||
if (relPoint.getX() == 0){
|
||||
Vector2i tile2 = tile.add(-1, 0);
|
||||
Vector2i relPoint2 = getPointRelativeToTile(tile2, point);
|
||||
LowresModel model2 = getModel(world, tile2);
|
||||
model2.update(relPoint2, height, color);
|
||||
}
|
||||
|
||||
if (relPoint.getY() == 0){
|
||||
Vector2i tile2 = tile.add(0, -1);
|
||||
Vector2i relPoint2 = getPointRelativeToTile(tile2, point);
|
||||
LowresModel model2 = getModel(world, tile2);
|
||||
model2.update(relPoint2, height, color);
|
||||
}
|
||||
|
||||
if (relPoint.getX() == 0 && relPoint.getY() == 0){
|
||||
Vector2i tile2 = tile.add(-1, -1);
|
||||
Vector2i relPoint2 = getPointRelativeToTile(tile2, point);
|
||||
LowresModel model2 = getModel(world, tile2);
|
||||
model2.update(relPoint2, height, color);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the file for a tile
|
||||
*/
|
||||
public File getFile(Vector2i tile){
|
||||
return FileUtil.coordsToFile(fileRoot, tile, "json.gz");
|
||||
}
|
||||
|
||||
private LowresModel getModel(UUID world, Vector2i tile) throws IOException {
|
||||
|
||||
File modelFile = getFile(tile);
|
||||
CachedModel model = models.get(modelFile);
|
||||
|
||||
if (model == null){
|
||||
synchronized (this) {
|
||||
model = models.get(modelFile);
|
||||
if (model == null){
|
||||
if (modelFile.exists()){
|
||||
|
||||
FileInputStream fis = new FileInputStream(modelFile);
|
||||
try(
|
||||
GZIPInputStream zis = new GZIPInputStream(fis);
|
||||
){
|
||||
String json = IOUtils.toString(zis, StandardCharsets.UTF_8);
|
||||
|
||||
try {
|
||||
model = new CachedModel(world, tile, BufferGeometry.fromJson(json));
|
||||
} catch (IllegalArgumentException | IOException ex){
|
||||
Logger.global.logError("Failed to load lowres model: " + modelFile, ex);
|
||||
//gridFile.renameTo(gridFile.toPath().getParent().resolve(gridFile.getName() + ".broken").toFile());
|
||||
modelFile.delete();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (model == null){
|
||||
model = new CachedModel(world, tile, gridSize);
|
||||
}
|
||||
|
||||
models.put(modelFile, model);
|
||||
|
||||
tidyUpModelCache();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
/**
|
||||
* This Method tidies up the model cache:<br>
|
||||
* it saves all modified models that have not been saved for 2 minutes and<br>
|
||||
* saves and removes the oldest models from the cache until the cache size is 10 or less.<br>
|
||||
* <br>
|
||||
* This method gets automatically called if the cache grows, but if you want to ensure model will be saved after 2 minutes, you could e.g call this method every second.<br>
|
||||
*/
|
||||
public synchronized void tidyUpModelCache() {
|
||||
List<Entry<File, CachedModel>> entries = new ArrayList<>(models.size());
|
||||
entries.addAll(models.entrySet());
|
||||
entries.sort((e1, e2) -> (int) Math.signum(e1.getValue().cacheTime - e2.getValue().cacheTime));
|
||||
|
||||
int size = entries.size();
|
||||
for (Entry<File, CachedModel> e : entries) {
|
||||
if (size > 10) {
|
||||
saveAndRemoveModel(e.getValue());
|
||||
continue;
|
||||
}
|
||||
|
||||
if (e.getValue().getCacheTime() > 120000) {
|
||||
saveModel(e.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void saveAndRemoveModel(CachedModel model) {
|
||||
File modelFile = getFile(model.getTile());
|
||||
models.remove(modelFile);
|
||||
try {
|
||||
model.save(modelFile, false);
|
||||
//logger.logDebug("Saved and unloaded lowres tile: " + model.getTile());
|
||||
} catch (IOException ex) {
|
||||
Logger.global.logError("Failed to save and unload lowres-model: " + modelFile, ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void saveModel(CachedModel model) {
|
||||
File modelFile = getFile(model.getTile());
|
||||
try {
|
||||
model.save(modelFile, false);
|
||||
//logger.logDebug("Saved lowres tile: " + model.getTile());
|
||||
} catch (IOException ex) {
|
||||
Logger.global.logError("Failed to save lowres-model: " + modelFile, ex);
|
||||
}
|
||||
|
||||
model.resetCacheTime();
|
||||
}
|
||||
|
||||
private Vector2i pointToTile(Vector2i point){
|
||||
return point
|
||||
.toDouble()
|
||||
.div(gridSize.toDouble())
|
||||
.floor()
|
||||
.toInt();
|
||||
}
|
||||
|
||||
private Vector2i getPointRelativeToTile(Vector2i tile, Vector2i point){
|
||||
return point.sub(tile.mul(gridSize));
|
||||
}
|
||||
|
||||
public Vector2i getTileSize() {
|
||||
return gridSize;
|
||||
}
|
||||
|
||||
public Vector2i getPointsPerHiresTile() {
|
||||
return pointsPerHiresTile;
|
||||
}
|
||||
|
||||
private class CachedModel extends LowresModel {
|
||||
|
||||
private long cacheTime;
|
||||
|
||||
public CachedModel(UUID world, Vector2i tilePos, BufferGeometry model) {
|
||||
super(world, tilePos, model);
|
||||
|
||||
cacheTime = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public CachedModel(UUID world, Vector2i tilePos, Vector2i gridSize) {
|
||||
super(world, tilePos, gridSize);
|
||||
|
||||
cacheTime = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public long getCacheTime() {
|
||||
return System.currentTimeMillis() - cacheTime;
|
||||
}
|
||||
|
||||
public void resetCacheTime() {
|
||||
cacheTime = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,239 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.resourcepack;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
|
||||
import com.flowpowered.math.GenericMath;
|
||||
import com.flowpowered.math.vector.Vector2f;
|
||||
import com.flowpowered.math.vector.Vector2i;
|
||||
import com.flowpowered.math.vector.Vector3f;
|
||||
|
||||
import de.bluecolored.bluemap.core.render.context.ExtendedBlockContext;
|
||||
import de.bluecolored.bluemap.core.world.Block;
|
||||
import ninja.leaping.configurate.ConfigurationNode;
|
||||
import ninja.leaping.configurate.gson.GsonConfigurationLoader;
|
||||
|
||||
public class BlockColorProvider {
|
||||
|
||||
private BufferedImage foliageMap;
|
||||
private BufferedImage grassMap;
|
||||
private Map<String, BiomeInfo> biomeInfos;
|
||||
private Map<String, String> blockColors;
|
||||
|
||||
public BlockColorProvider(ResourcePack resourcePack) throws IOException, NoSuchResourceException {
|
||||
|
||||
this.foliageMap = ImageIO.read(resourcePack.getResource(Paths.get("assets", "minecraft", "textures", "colormap", "foliage.png")));
|
||||
this.grassMap = ImageIO.read(resourcePack.getResource(Paths.get("assets", "minecraft", "textures", "colormap", "grass.png")));
|
||||
|
||||
|
||||
this.biomeInfos = new ConcurrentHashMap<>();
|
||||
GsonConfigurationLoader loader = GsonConfigurationLoader.builder()
|
||||
.setURL(getClass().getResource("/biomes.json"))
|
||||
.build();
|
||||
ConfigurationNode biomesConfig = loader.load();
|
||||
|
||||
for (Entry<Object, ? extends ConfigurationNode> n : biomesConfig.getChildrenMap().entrySet()){
|
||||
String key = n.getKey().toString();
|
||||
BiomeInfo value = new BiomeInfo();
|
||||
value.humidity = n.getValue().getNode("humidity").getFloat(0.4f);
|
||||
value.temp = n.getValue().getNode("temp").getFloat(0.8f);
|
||||
value.watercolor = n.getValue().getNode("watercolor").getInt(4159204);
|
||||
|
||||
biomeInfos.put(key, value);
|
||||
}
|
||||
|
||||
this.blockColors = new ConcurrentHashMap<>();
|
||||
loader = GsonConfigurationLoader.builder()
|
||||
.setURL(getClass().getResource("/blockColors.json"))
|
||||
.build();
|
||||
ConfigurationNode blockConfig = loader.load();
|
||||
|
||||
for (Entry<Object, ? extends ConfigurationNode> n : blockConfig.getChildrenMap().entrySet()){
|
||||
String blockId = n.getKey().toString();
|
||||
String color = n.getValue().getString();
|
||||
blockColors.put(blockId, color);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public Vector3f getBlockColor(ExtendedBlockContext context){
|
||||
Block block = context.getRelativeBlock(0, 0, 0);
|
||||
String blockId = block.getBlock().getId();
|
||||
|
||||
// water color
|
||||
if (blockId.equals("water")) {
|
||||
return getBiomeWaterAverageColor(context);
|
||||
}
|
||||
|
||||
String colorDef = blockColors.get(blockId);
|
||||
if (colorDef == null) colorDef = blockColors.get("default");
|
||||
if (colorDef == null) colorDef = "#foliage";
|
||||
|
||||
// grass map
|
||||
if (colorDef.equals("#grass")){
|
||||
return getBiomeGrassAverageColor(context);
|
||||
}
|
||||
|
||||
// foliage map
|
||||
if (colorDef.equals("#foliage")){
|
||||
return getBiomeFoliageAverageColor(context);
|
||||
}
|
||||
|
||||
int cValue = Integer.parseInt(colorDef, 16);
|
||||
return colorFromInt(cValue);
|
||||
}
|
||||
|
||||
public Vector3f getBiomeFoliageAverageColor(ExtendedBlockContext context){
|
||||
Vector3f color = Vector3f.ZERO;
|
||||
|
||||
for (int x = -1; x <= 1; x++){
|
||||
for (int z = -1; z <= 1; z++){
|
||||
color = color.add(getBiomeFoliageColor(context.getRelativeBlock(x, 0, z)));
|
||||
}
|
||||
}
|
||||
|
||||
return color.div(9f);
|
||||
}
|
||||
|
||||
private Vector3f getBiomeFoliageColor(Block block){
|
||||
Vector3f color = Vector3f.ONE;
|
||||
|
||||
if (block.getBiome().contains("mesa")){
|
||||
return colorFromInt(0x9e814d);
|
||||
}
|
||||
|
||||
if (block.getBiome().contains("swamp")) {
|
||||
return colorFromInt(0x6A7039);
|
||||
}
|
||||
|
||||
int blocksAboveSeaLevel = Math.max(block.getPosition().getY() - block.getWorld().getSeaLevel(), 0);
|
||||
color = getFoliageColor(block.getBiome(), blocksAboveSeaLevel);
|
||||
|
||||
//improvised to match the original better
|
||||
if (block.getBiome().contains("roofed_forest")){
|
||||
color = color.mul(2f).add(colorFromInt(0x28340a)).div(3f);
|
||||
}
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
public Vector3f getBiomeGrassAverageColor(ExtendedBlockContext context){
|
||||
Vector3f color = Vector3f.ZERO;
|
||||
|
||||
for (int x = -1; x <= 1; x++){
|
||||
for (int z = -1; z <= 1; z++){
|
||||
color = color.add(getBiomeGrassColor(context.getRelativeBlock(x, 0, z)));
|
||||
}
|
||||
}
|
||||
|
||||
return color.div(9f);
|
||||
}
|
||||
|
||||
private Vector3f getBiomeGrassColor(Block block){
|
||||
Vector3f color = Vector3f.ONE;
|
||||
|
||||
if (block.getBiome().contains("mesa")){
|
||||
return colorFromInt(0x90814d);
|
||||
}
|
||||
|
||||
if (block.getBiome().contains("swamp")) {
|
||||
return colorFromInt(0x6A7039);
|
||||
}
|
||||
|
||||
int blocksAboveSeaLevel = Math.max(block.getPosition().getY() - block.getWorld().getSeaLevel(), 0);
|
||||
color = getGrassColor(block.getBiome(), blocksAboveSeaLevel);
|
||||
|
||||
if (block.getBiome().contains("roofed_forest")){
|
||||
color = color.add(colorFromInt(0x28340a)).div(2f);
|
||||
}
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
public Vector3f getBiomeWaterAverageColor(ExtendedBlockContext context){
|
||||
Vector3f color = Vector3f.ZERO;
|
||||
|
||||
for (int x = -1; x <= 1; x++){
|
||||
for (int z = -1; z <= 1; z++){
|
||||
color = color.add(getBiomeWaterColor(context.getRelativeBlock(x, 0, z)));
|
||||
}
|
||||
}
|
||||
|
||||
return color.div(9f);
|
||||
}
|
||||
|
||||
private Vector3f getBiomeWaterColor(Block block){
|
||||
return colorFromInt(biomeInfos.get(block.getBiome()).watercolor);
|
||||
}
|
||||
|
||||
private Vector3f colorFromInt(int cValue){
|
||||
Color c = new Color(cValue, false);
|
||||
return new Vector3f(c.getRed(), c.getGreen(), c.getBlue()).div(0xff);
|
||||
}
|
||||
|
||||
private Vector3f getFoliageColor(String biomeId, int blocksAboveSeaLevel){
|
||||
return getColorFromMap(biomeId, blocksAboveSeaLevel, foliageMap);
|
||||
}
|
||||
|
||||
private Vector3f getGrassColor(String biomeId, int blocksAboveSeaLevel){
|
||||
return getColorFromMap(biomeId, blocksAboveSeaLevel, grassMap);
|
||||
}
|
||||
|
||||
private Vector3f getColorFromMap(String biomeId, int blocksAboveSeaLevel, BufferedImage map){
|
||||
Vector2i pixel = getColorMapPosition(biomeId, blocksAboveSeaLevel).mul(map.getWidth(), map.getHeight()).floor().toInt();
|
||||
int cValue = map.getRGB(GenericMath.clamp(pixel.getX(), 0, map.getWidth() - 1), GenericMath.clamp(pixel.getY(), 0, map.getHeight() - 1));
|
||||
Color color = new Color(cValue, false);
|
||||
return new Vector3f(color.getRed(), color.getGreen(), color.getBlue()).div(0xff);
|
||||
|
||||
}
|
||||
|
||||
private Vector2f getColorMapPosition(String biomeId, int blocksAboveSeaLevel){
|
||||
BiomeInfo bi = biomeInfos.get(biomeId);
|
||||
|
||||
if (bi == null){
|
||||
throw new NoSuchElementException("No biome found with id: " + biomeId);
|
||||
}
|
||||
|
||||
float adjTemp = (float) GenericMath.clamp(bi.temp - (0.00166667 * (double) blocksAboveSeaLevel), 0d, 1d);
|
||||
float adjHumidity = (float) GenericMath.clamp(bi.humidity, 0d, 1d) * adjTemp;
|
||||
return new Vector2f(1 - adjTemp, 1 - adjHumidity);
|
||||
}
|
||||
|
||||
class BiomeInfo {
|
||||
float humidity;
|
||||
float temp;
|
||||
int watercolor;
|
||||
}
|
||||
}
|
@ -0,0 +1,142 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.resourcepack;
|
||||
|
||||
import com.flowpowered.math.vector.Vector3f;
|
||||
import com.flowpowered.math.vector.Vector4f;
|
||||
|
||||
import de.bluecolored.bluemap.core.util.ConfigUtil;
|
||||
import de.bluecolored.bluemap.core.util.Direction;
|
||||
import ninja.leaping.configurate.ConfigurationNode;
|
||||
|
||||
public class BlockModelElementFaceResource {
|
||||
|
||||
private BlockModelElementResource element;
|
||||
|
||||
private Vector4f uv;
|
||||
private String texture;
|
||||
private String resolvedTexture;
|
||||
private Direction cullface;
|
||||
private int rotation;
|
||||
private int tintIndex;
|
||||
|
||||
protected BlockModelElementFaceResource(BlockModelElementResource element, ConfigurationNode declaration) throws InvalidResourceDeclarationException {
|
||||
this.element = element;
|
||||
|
||||
try {
|
||||
this.uv = getDefaultUV(declaration.getKey().toString(), element.getFrom(), element.getTo());
|
||||
|
||||
ConfigurationNode uv = declaration.getNode("uv");
|
||||
if (!uv.isVirtual()) this.uv = ConfigUtil.readVector4f(declaration.getNode("uv"));
|
||||
|
||||
this.texture = declaration.getNode("texture").getString();
|
||||
this.resolvedTexture = null;
|
||||
|
||||
this.cullface = null;
|
||||
ConfigurationNode cf = declaration.getNode("cullface");
|
||||
if (!cf.isVirtual()) this.cullface = Direction.fromString(cf.getString());
|
||||
|
||||
this.rotation = declaration.getNode("rotation").getInt(0);
|
||||
this.tintIndex = declaration.getNode("tintindex").getInt(-1);
|
||||
|
||||
} catch (NullPointerException | IllegalArgumentException e){
|
||||
throw new InvalidResourceDeclarationException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public Vector4f getDefaultUV(String faceId, Vector3f from, Vector3f to){
|
||||
switch (faceId){
|
||||
|
||||
case "down" :
|
||||
case "up" :
|
||||
return new Vector4f(
|
||||
from.getX(), from.getZ(),
|
||||
to.getX(), to.getZ()
|
||||
);
|
||||
|
||||
case "north" :
|
||||
case "south" :
|
||||
return new Vector4f(
|
||||
from.getX(), from.getY(),
|
||||
to.getX(), to.getY()
|
||||
);
|
||||
|
||||
case "west" :
|
||||
case "east" :
|
||||
return new Vector4f(
|
||||
from.getZ(), from.getY(),
|
||||
to.getZ(), to.getY()
|
||||
);
|
||||
|
||||
default :
|
||||
return new Vector4f(
|
||||
0, 0,
|
||||
16, 16
|
||||
);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public BlockModelElementResource getElement(){
|
||||
return element;
|
||||
}
|
||||
|
||||
public Vector4f getUv() {
|
||||
return uv;
|
||||
}
|
||||
|
||||
public String getTexture() {
|
||||
return texture;
|
||||
}
|
||||
|
||||
public String getResolvedTexture() {
|
||||
if (resolvedTexture == null){
|
||||
resolvedTexture = getElement().getModel().resolveTexture(getTexture());
|
||||
}
|
||||
|
||||
return resolvedTexture;
|
||||
}
|
||||
|
||||
public boolean isCullface() {
|
||||
return cullface != null;
|
||||
}
|
||||
|
||||
public Direction getCullface() {
|
||||
return cullface;
|
||||
}
|
||||
|
||||
public int getRotation() {
|
||||
return rotation;
|
||||
}
|
||||
|
||||
public boolean isTinted(){
|
||||
return tintIndex >= 0;
|
||||
}
|
||||
|
||||
public int getTintIndex() {
|
||||
return tintIndex;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,144 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.resourcepack;
|
||||
|
||||
import com.flowpowered.math.vector.Vector3f;
|
||||
|
||||
import de.bluecolored.bluemap.core.util.Axis;
|
||||
import de.bluecolored.bluemap.core.util.ConfigUtil;
|
||||
import ninja.leaping.configurate.ConfigurationNode;
|
||||
|
||||
public class BlockModelElementResource {
|
||||
|
||||
private BlockModelResource model;
|
||||
|
||||
private Vector3f from, to;
|
||||
|
||||
private Vector3f rotOrigin;
|
||||
private Axis rotAxis;
|
||||
private float rotAngle;
|
||||
private boolean rotRescale;
|
||||
|
||||
private boolean shade;
|
||||
|
||||
private BlockModelElementFaceResource down, up, north, south, west, east;
|
||||
|
||||
protected BlockModelElementResource(BlockModelResource model, ConfigurationNode declaration) throws InvalidResourceDeclarationException {
|
||||
this.model = model;
|
||||
|
||||
try {
|
||||
this.from = ConfigUtil.readVector3f(declaration.getNode("from"));
|
||||
this.to = ConfigUtil.readVector3f(declaration.getNode("to"));
|
||||
|
||||
this.rotAngle = 0f;
|
||||
ConfigurationNode rotation = declaration.getNode("rotation");
|
||||
if (!rotation.isVirtual()){
|
||||
this.rotOrigin = ConfigUtil.readVector3f(rotation.getNode("origin"));
|
||||
this.rotAxis = Axis.fromString(rotation.getNode("axis").getString());
|
||||
this.rotAngle = rotation.getNode("angle").getFloat();
|
||||
this.rotRescale = rotation.getNode("rescale").getBoolean(false);
|
||||
}
|
||||
|
||||
this.shade = declaration.getNode("shade").getBoolean(true);
|
||||
|
||||
ConfigurationNode faces = declaration.getNode("faces");
|
||||
this.down = loadFace(faces.getNode("down"));
|
||||
this.up = loadFace(faces.getNode("up"));
|
||||
this.north = loadFace(faces.getNode("north"));
|
||||
this.south = loadFace(faces.getNode("south"));
|
||||
this.west = loadFace(faces.getNode("west"));
|
||||
this.east = loadFace(faces.getNode("east"));
|
||||
|
||||
} catch (NullPointerException e){
|
||||
throw new InvalidResourceDeclarationException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private BlockModelElementFaceResource loadFace(ConfigurationNode faceNode) throws InvalidResourceDeclarationException {
|
||||
if (faceNode.isVirtual()) return null;
|
||||
return new BlockModelElementFaceResource(this, faceNode);
|
||||
}
|
||||
|
||||
public BlockModelResource getModel(){
|
||||
return model;
|
||||
}
|
||||
|
||||
public Vector3f getFrom() {
|
||||
return from;
|
||||
}
|
||||
|
||||
public Vector3f getTo() {
|
||||
return to;
|
||||
}
|
||||
|
||||
public boolean isRotation(){
|
||||
return rotAngle != 0f;
|
||||
}
|
||||
|
||||
public Vector3f getRotationOrigin() {
|
||||
return rotOrigin;
|
||||
}
|
||||
|
||||
public Axis getRotationAxis() {
|
||||
return rotAxis;
|
||||
}
|
||||
|
||||
public float getRotationAngle() {
|
||||
return rotAngle;
|
||||
}
|
||||
|
||||
public boolean isRotationRescale() {
|
||||
return rotRescale;
|
||||
}
|
||||
|
||||
public boolean isShade() {
|
||||
return shade;
|
||||
}
|
||||
|
||||
public BlockModelElementFaceResource getDownFace() {
|
||||
return down;
|
||||
}
|
||||
|
||||
public BlockModelElementFaceResource getUpFace() {
|
||||
return up;
|
||||
}
|
||||
|
||||
public BlockModelElementFaceResource getNorthFace() {
|
||||
return north;
|
||||
}
|
||||
|
||||
public BlockModelElementFaceResource getSouthFace() {
|
||||
return south;
|
||||
}
|
||||
|
||||
public BlockModelElementFaceResource getWestFace() {
|
||||
return west;
|
||||
}
|
||||
|
||||
public BlockModelElementFaceResource getEastFace() {
|
||||
return east;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,142 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.resourcepack;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Vector;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import ninja.leaping.configurate.ConfigurationNode;
|
||||
import ninja.leaping.configurate.gson.GsonConfigurationLoader;
|
||||
|
||||
public class BlockModelResource {
|
||||
|
||||
private BlockStateResource blockState;
|
||||
|
||||
private int xRot, yRot;
|
||||
private boolean uvLock;
|
||||
private boolean ambientOcclusion;
|
||||
private Collection<BlockModelElementResource> elements;
|
||||
private Map<String, String> textures;
|
||||
|
||||
protected BlockModelResource(BlockStateResource blockState, ConfigurationNode declaration, ResourcePack resources) throws InvalidResourceDeclarationException {
|
||||
this.blockState = blockState;
|
||||
|
||||
this.xRot = declaration.getNode("x").getInt(0);
|
||||
this.yRot = declaration.getNode("y").getInt(0);
|
||||
this.uvLock = declaration.getNode("uvlock").getBoolean(false);
|
||||
this.ambientOcclusion = true;
|
||||
this.elements = new Vector<>();
|
||||
this.textures = new ConcurrentHashMap<>();
|
||||
|
||||
try {
|
||||
loadModelResource(declaration.getNode("model").getString(), resources);
|
||||
} catch (IOException e) {
|
||||
throw new InvalidResourceDeclarationException("Model not found: " + declaration.getNode("model").getString(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private void loadModelResource(String modelId, ResourcePack resources) throws IOException, InvalidResourceDeclarationException {
|
||||
Path resourcePath = Paths.get("assets", "minecraft", "models", modelId + ".json");
|
||||
|
||||
ConfigurationNode data = GsonConfigurationLoader.builder()
|
||||
.setSource(() -> new BufferedReader(new InputStreamReader(resources.getResource(resourcePath), StandardCharsets.UTF_8)))
|
||||
.build()
|
||||
.load();
|
||||
|
||||
//load parent first
|
||||
ConfigurationNode parent = data.getNode("parent");
|
||||
if (!parent.isVirtual()){
|
||||
loadModelResource(parent.getString(), resources);
|
||||
}
|
||||
|
||||
for (Entry<Object, ? extends ConfigurationNode> texture : data.getNode("textures").getChildrenMap().entrySet()){
|
||||
String key = texture.getKey().toString();
|
||||
String value = texture.getValue().getString();
|
||||
textures.put(key, value);
|
||||
}
|
||||
|
||||
ambientOcclusion = data.getNode("ambientocclusion").getBoolean(ambientOcclusion);
|
||||
|
||||
if (!data.getNode("elements").isVirtual()){
|
||||
elements.clear();
|
||||
for (ConfigurationNode e : data.getNode("elements").getChildrenList()){
|
||||
elements.add(new BlockModelElementResource(this, e));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public BlockStateResource getBlockState(){
|
||||
return blockState;
|
||||
}
|
||||
|
||||
public int getXRot() {
|
||||
return xRot;
|
||||
}
|
||||
|
||||
public int getYRot() {
|
||||
return yRot;
|
||||
}
|
||||
|
||||
public boolean isUvLock() {
|
||||
return uvLock;
|
||||
}
|
||||
|
||||
public boolean isAmbientOcclusion() {
|
||||
return ambientOcclusion;
|
||||
}
|
||||
|
||||
public Collection<BlockModelElementResource> getElements() {
|
||||
return Collections.unmodifiableCollection(elements);
|
||||
}
|
||||
|
||||
public String resolveTexture(String key){
|
||||
if (key == null) return null;
|
||||
if (!key.startsWith("#")) return key;
|
||||
String texture = textures.get(key.substring(1));
|
||||
if (texture == null) return key;
|
||||
return resolveTexture(texture);
|
||||
}
|
||||
|
||||
public Collection<String> getAllTextureIds(){
|
||||
List<String> list = new ArrayList<>();
|
||||
for (String tex : textures.values()){
|
||||
if (!tex.startsWith("#")) list.add(tex);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,154 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.resourcepack;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Vector;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
|
||||
import de.bluecolored.bluemap.core.util.WeighedArrayList;
|
||||
import de.bluecolored.bluemap.core.world.BlockState;
|
||||
import ninja.leaping.configurate.ConfigurationNode;
|
||||
import ninja.leaping.configurate.gson.GsonConfigurationLoader;
|
||||
|
||||
public class BlockStateResource {
|
||||
private BlockState block;
|
||||
private Collection<WeighedArrayList<BlockModelResource>> modelResources;
|
||||
|
||||
protected BlockStateResource(BlockState block, ResourcePack resources) throws NoSuchResourceException, InvalidResourceDeclarationException {
|
||||
this.block = Preconditions.checkNotNull(block);
|
||||
this.modelResources = new Vector<>();
|
||||
|
||||
try {
|
||||
ConfigurationNode data = GsonConfigurationLoader.builder()
|
||||
.setSource(() -> new BufferedReader(new InputStreamReader(resources.getResource(getResourcePath()), StandardCharsets.UTF_8)))
|
||||
.build()
|
||||
.load();
|
||||
|
||||
load(data, resources);
|
||||
} catch (IOException e) {
|
||||
throw new NoSuchResourceException("There is no definition for resource-id: " + block.getId(), e);
|
||||
} catch (NullPointerException e){
|
||||
throw new InvalidResourceDeclarationException(e);
|
||||
}
|
||||
|
||||
this.modelResources = Collections.unmodifiableCollection(this.modelResources);
|
||||
}
|
||||
|
||||
private void load(ConfigurationNode data, ResourcePack resources) throws InvalidResourceDeclarationException {
|
||||
|
||||
//load variants
|
||||
ConfigurationNode variants = data.getNode("variants");
|
||||
for (Entry<Object, ? extends ConfigurationNode> e : variants.getChildrenMap().entrySet()){
|
||||
if (getBlock().checkVariantCondition(e.getKey().toString())){
|
||||
addModelResource(e.getValue(), resources);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//load multipart
|
||||
ConfigurationNode multipart = data.getNode("multipart");
|
||||
for (ConfigurationNode part : multipart.getChildrenList()){
|
||||
|
||||
ConfigurationNode when = part.getNode("when");
|
||||
if (when.isVirtual() || checkMultipartCondition(when)){
|
||||
addModelResource(part.getNode("apply"), resources);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void addModelResource(ConfigurationNode n, ResourcePack resources) throws InvalidResourceDeclarationException {
|
||||
WeighedArrayList<BlockModelResource> models = new WeighedArrayList<>();
|
||||
|
||||
if (n.hasListChildren()){
|
||||
|
||||
//if it is a weighted list of alternative models, select one by random and weight
|
||||
List<? extends ConfigurationNode> cList = n.getChildrenList();
|
||||
for (ConfigurationNode c : cList){
|
||||
int weight = c.getNode("weight").getInt(1);
|
||||
models.add(new BlockModelResource(this, c, resources), weight);
|
||||
}
|
||||
|
||||
} else {
|
||||
models.add(new BlockModelResource(this, n, resources));
|
||||
}
|
||||
|
||||
modelResources.add(models);
|
||||
}
|
||||
|
||||
private boolean checkMultipartCondition(ConfigurationNode when){
|
||||
ConfigurationNode or = when.getNode("OR");
|
||||
if (!or.isVirtual()){
|
||||
for (ConfigurationNode condition : or.getChildrenList()){
|
||||
if (checkMultipartCondition(condition)) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Map<String, String> blockProperties = getBlock().getProperties();
|
||||
for (Entry<Object, ? extends ConfigurationNode> e : when.getChildrenMap().entrySet()){
|
||||
String key = e.getKey().toString();
|
||||
String[] values = e.getValue().getString().split("\\|");
|
||||
|
||||
boolean found = false;
|
||||
for (String value : values){
|
||||
if (value.equals(blockProperties.get(key))){
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public BlockState getBlock() {
|
||||
return block;
|
||||
}
|
||||
|
||||
public Collection<WeighedArrayList<BlockModelResource>> getModelResources(){
|
||||
return modelResources;
|
||||
}
|
||||
|
||||
private Path getResourcePath(){
|
||||
return Paths.get("assets", block.getNamespace(), "blockstates", block.getId() + ".json");
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.resourcepack;
|
||||
|
||||
public class InvalidResourceDeclarationException extends Exception {
|
||||
private static final long serialVersionUID = 0L;
|
||||
|
||||
public InvalidResourceDeclarationException() {}
|
||||
|
||||
public InvalidResourceDeclarationException(Throwable e) {
|
||||
super(e);
|
||||
}
|
||||
|
||||
public InvalidResourceDeclarationException(String message){
|
||||
super(message);
|
||||
}
|
||||
|
||||
public InvalidResourceDeclarationException(String message, Throwable e) {
|
||||
super(message, e);
|
||||
}
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.resourcepack;
|
||||
|
||||
public class NoSuchResourceException extends Exception {
|
||||
private static final long serialVersionUID = 0L;
|
||||
|
||||
public NoSuchResourceException() {}
|
||||
|
||||
public NoSuchResourceException(Throwable e) {
|
||||
super(e);
|
||||
}
|
||||
|
||||
public NoSuchResourceException(String message){
|
||||
super(message);
|
||||
}
|
||||
|
||||
public NoSuchResourceException(String message, Throwable e) {
|
||||
super(message, e);
|
||||
}
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.resourcepack;
|
||||
|
||||
public class NoSuchTextureException extends Exception {
|
||||
private static final long serialVersionUID = 0L;
|
||||
|
||||
public NoSuchTextureException() {}
|
||||
|
||||
public NoSuchTextureException(Throwable e) {
|
||||
super(e);
|
||||
}
|
||||
|
||||
public NoSuchTextureException(String message){
|
||||
super(message);
|
||||
}
|
||||
|
||||
public NoSuchTextureException(String message, Throwable e) {
|
||||
super(message, e);
|
||||
}
|
||||
}
|
@ -0,0 +1,189 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.resourcepack;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
|
||||
import com.google.common.cache.Cache;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
|
||||
import de.bluecolored.bluemap.core.logger.Logger;
|
||||
import de.bluecolored.bluemap.core.world.BlockState;
|
||||
|
||||
public class ResourcePack {
|
||||
|
||||
private Map<Path, Resource> resources;
|
||||
|
||||
private TextureProvider textureProvider;
|
||||
private BlockColorProvider blockColorProvider;
|
||||
private Cache<BlockState, BlockStateResource> blockStateResourceCache;
|
||||
|
||||
public ResourcePack(List<File> dataSources, File textureExportFile) throws IOException, NoSuchResourceException {
|
||||
this.resources = new HashMap<>();
|
||||
|
||||
load(dataSources);
|
||||
|
||||
blockStateResourceCache = CacheBuilder.newBuilder()
|
||||
.maximumSize(10000)
|
||||
.build();
|
||||
|
||||
textureProvider = new TextureProvider();
|
||||
if (textureExportFile.exists()){
|
||||
textureProvider.load(textureExportFile);
|
||||
} else {
|
||||
textureProvider.generate(this);
|
||||
textureProvider.save(textureExportFile);
|
||||
}
|
||||
|
||||
blockColorProvider = new BlockColorProvider(this);
|
||||
}
|
||||
|
||||
private void load(List<File> dataSources) throws IOException {
|
||||
resources.clear();
|
||||
|
||||
//load resourcepacks in order
|
||||
for (File resourcePath : dataSources) overrideResourcesWith(resourcePath);
|
||||
}
|
||||
|
||||
private void overrideResourcesWith(File resourcePath){
|
||||
if (resourcePath.isFile() && resourcePath.getName().endsWith(".zip") || resourcePath.getName().endsWith(".jar")){
|
||||
overrideResourcesWithZipFile(resourcePath);
|
||||
return;
|
||||
}
|
||||
|
||||
overrideResourcesWith(resourcePath, Paths.get(""));
|
||||
}
|
||||
|
||||
private void overrideResourcesWith(File resource, Path resourcePath){
|
||||
if (resource.isDirectory()){
|
||||
for (File childFile : resource.listFiles()){
|
||||
overrideResourcesWith(childFile, resourcePath.resolve(childFile.getName()));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (resource.isFile()){
|
||||
try {
|
||||
byte[] bytes = Files.readAllBytes(resource.toPath());
|
||||
resources.put(resourcePath, new Resource(bytes));
|
||||
} catch (IOException e) {
|
||||
Logger.global.logError("Failed to load resource: " + resource, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void overrideResourcesWithZipFile(File resourceFile){
|
||||
try (
|
||||
ZipFile zipFile = new ZipFile(resourceFile);
|
||||
){
|
||||
Enumeration<? extends ZipEntry> files = zipFile.entries();
|
||||
byte[] buffer = new byte[1024];
|
||||
while (files.hasMoreElements()){
|
||||
ZipEntry file = files.nextElement();
|
||||
if (file.isDirectory()) continue;
|
||||
|
||||
Path resourcePath = Paths.get("", file.getName().split("/"));
|
||||
InputStream fileInputStream = zipFile.getInputStream(file);
|
||||
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream(Math.max(8, (int) file.getSize()));
|
||||
int bytesRead;
|
||||
while ((bytesRead = fileInputStream.read(buffer)) != -1){
|
||||
bos.write(buffer, 0, bytesRead);
|
||||
}
|
||||
|
||||
resources.put(resourcePath, new Resource(bos.toByteArray()));
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Logger.global.logError("Failed to load resource: " + resourceFile, e);
|
||||
}
|
||||
}
|
||||
|
||||
public BlockStateResource getBlockStateResource(BlockState block) throws NoSuchResourceException, InvalidResourceDeclarationException {
|
||||
BlockStateResource bsr = blockStateResourceCache.getIfPresent(block);
|
||||
|
||||
if (bsr == null){
|
||||
bsr = new BlockStateResource(block, this);
|
||||
blockStateResourceCache.put(block, bsr);
|
||||
}
|
||||
|
||||
return bsr;
|
||||
}
|
||||
|
||||
public TextureProvider getTextureProvider(){
|
||||
return textureProvider;
|
||||
}
|
||||
|
||||
public BlockColorProvider getBlockColorProvider(){
|
||||
return blockColorProvider;
|
||||
}
|
||||
|
||||
public Map<Path, Resource> getAllResources() {
|
||||
return Collections.unmodifiableMap(resources);
|
||||
}
|
||||
|
||||
public InputStream getResource(Path resourcePath) throws NoSuchResourceException {
|
||||
Resource resource = resources.get(resourcePath);
|
||||
if (resource == null) throw new NoSuchResourceException("There is no resource with that path: " + resourcePath);
|
||||
return resource.getStream();
|
||||
}
|
||||
|
||||
public class Resource {
|
||||
|
||||
private byte[] data;
|
||||
|
||||
public Resource(byte[] data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public InputStream getStream(){
|
||||
return new ByteArrayInputStream(data);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static void createDefaultResource(File file) throws IOException {
|
||||
if (!file.exists()) {
|
||||
file.getParentFile().mkdirs();
|
||||
FileUtils.copyURLToFile(ResourcePack.class.getResource("/DefaultResources.zip"), file, 10000, 10000);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,231 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.resourcepack;
|
||||
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Base64;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Vector;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
|
||||
import com.flowpowered.math.vector.Vector4f;
|
||||
|
||||
import de.bluecolored.bluemap.core.resourcepack.ResourcePack.Resource;
|
||||
import de.bluecolored.bluemap.core.util.ConfigUtil;
|
||||
import de.bluecolored.bluemap.core.util.MathUtil;
|
||||
import ninja.leaping.configurate.ConfigurationNode;
|
||||
import ninja.leaping.configurate.gson.GsonConfigurationLoader;
|
||||
|
||||
public class TextureProvider {
|
||||
|
||||
private Map<String, Integer> indexMap;
|
||||
private List<Texture> textures;
|
||||
|
||||
public TextureProvider() throws IOException {
|
||||
this.indexMap = new ConcurrentHashMap<>();
|
||||
this.textures = new Vector<>();
|
||||
}
|
||||
|
||||
public int getTextureIndex(String textureId) throws NoSuchTextureException {
|
||||
Integer tex = indexMap.get(textureId);
|
||||
|
||||
if (tex == null){
|
||||
throw new NoSuchTextureException("There is no texture with id: " + textureId);
|
||||
}
|
||||
|
||||
return tex.intValue();
|
||||
}
|
||||
|
||||
public Texture getTexture(String textureId) throws NoSuchTextureException {
|
||||
return getTexture(getTextureIndex(textureId));
|
||||
}
|
||||
|
||||
public Texture getTexture(int index){
|
||||
return textures.get(index);
|
||||
}
|
||||
|
||||
public void generate(ResourcePack resources) throws IOException {
|
||||
indexMap.clear();
|
||||
textures.clear();
|
||||
|
||||
Path textureRoot = Paths.get("assets", "minecraft", "textures");
|
||||
for (Entry<Path, Resource> entry : resources.getAllResources().entrySet()){
|
||||
if (entry.getKey().startsWith(textureRoot) && entry.getKey().toString().endsWith(".png")){
|
||||
BufferedImage image = ImageIO.read(entry.getValue().getStream());
|
||||
if (image == null) throw new IOException("Failed to read Image: " + entry.getKey());
|
||||
|
||||
String path = textureRoot.relativize(entry.getKey()).normalize().toString();
|
||||
String id = path
|
||||
.substring(0, path.length() - ".png".length())
|
||||
.replace(File.separatorChar, '/');
|
||||
|
||||
Texture texture = new Texture(id, image);
|
||||
textures.add(texture);
|
||||
indexMap.put(id, textures.size() - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void load(File file) throws IOException {
|
||||
|
||||
indexMap.clear();
|
||||
textures.clear();
|
||||
|
||||
GsonConfigurationLoader loader = GsonConfigurationLoader.builder().setFile(file).build();
|
||||
ConfigurationNode node = loader.load();
|
||||
|
||||
int i = 0;
|
||||
for(ConfigurationNode n : node.getNode("textures").getChildrenList()){
|
||||
Texture t = new Texture(
|
||||
n.getNode("id").getString(),
|
||||
n.getNode("texture").getString(),
|
||||
n.getNode("transparent").getBoolean(false),
|
||||
ConfigUtil.readVector4f(n.getNode("color"))
|
||||
);
|
||||
|
||||
textures.add(t);
|
||||
indexMap.put(t.getId(), i++);
|
||||
}
|
||||
}
|
||||
|
||||
public void save(File file) throws IOException {
|
||||
|
||||
if (!file.exists()) {
|
||||
file.getParentFile().mkdirs();
|
||||
file.createNewFile();
|
||||
}
|
||||
|
||||
GsonConfigurationLoader loader = GsonConfigurationLoader.builder().setFile(file).build();
|
||||
ConfigurationNode node = loader.createEmptyNode();
|
||||
|
||||
for (Texture t : textures){
|
||||
ConfigurationNode n = node.getNode("textures").getAppendedNode();
|
||||
n.getNode("id").setValue(t.getId());
|
||||
n.getNode("texture").setValue(t.getBase64());
|
||||
n.getNode("transparent").setValue(t.isHalfTransparent());
|
||||
ConfigUtil.writeVector4f(n.getNode("color"), t.getColor());
|
||||
}
|
||||
|
||||
loader.save(node);
|
||||
}
|
||||
|
||||
public class Texture {
|
||||
|
||||
private String id;
|
||||
private String base64;
|
||||
private boolean halfTransparent;
|
||||
private Vector4f color;
|
||||
|
||||
public Texture(String id, String base64, boolean halfTransparent, Vector4f color){
|
||||
this.id = id;
|
||||
this.halfTransparent = halfTransparent;
|
||||
this.base64 = base64;
|
||||
this.color = color;
|
||||
}
|
||||
|
||||
public Texture(String id, BufferedImage image) throws IOException {
|
||||
this.id = id;
|
||||
|
||||
//crop off animation frames
|
||||
if (image.getHeight() > image.getWidth()){
|
||||
BufferedImage cropped = new BufferedImage(image.getWidth(), image.getWidth(), image.getType());
|
||||
Graphics2D g = cropped.createGraphics();
|
||||
g.drawImage(image, 0, 0, null);
|
||||
image = cropped;
|
||||
}
|
||||
|
||||
//check halfTransparency
|
||||
this.halfTransparent = checkHalfTransparent(image);
|
||||
|
||||
//calculate color
|
||||
this.color = calculateColor(image);
|
||||
|
||||
//write to Base64
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
ImageIO.write(image, "png", os);
|
||||
this.base64 = "data:image/png;base64," + Base64.getEncoder().encodeToString(os.toByteArray());
|
||||
}
|
||||
|
||||
private Vector4f calculateColor(BufferedImage image){
|
||||
Vector4f color = Vector4f.ZERO;
|
||||
|
||||
for (int x = 0; x < image.getWidth(); x++){
|
||||
for (int y = 0; y < image.getHeight(); y++){
|
||||
int pixel = image.getRGB(x, y);
|
||||
double alpha = (double)((pixel >> 24) & 0xff) / (double) 0xff;
|
||||
double red = (double)((pixel >> 16) & 0xff) / (double) 0xff;
|
||||
double green = (double)((pixel >> 8) & 0xff) / (double) 0xff;
|
||||
double blue = (double)((pixel >> 0) & 0xff) / (double) 0xff;
|
||||
|
||||
color = MathUtil.blendColors(new Vector4f(red, green, blue, alpha), color);
|
||||
}
|
||||
}
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
private boolean checkHalfTransparent(BufferedImage image){
|
||||
for (int x = 0; x < image.getWidth(); x++){
|
||||
for (int y = 0; y < image.getHeight(); y++){
|
||||
int pixel = image.getRGB(x, y);
|
||||
int alpha = (pixel >> 24) & 0xff;
|
||||
if (alpha > 0x00 && alpha < 0xff){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getBase64() {
|
||||
return base64;
|
||||
}
|
||||
|
||||
public boolean isHalfTransparent() {
|
||||
return halfTransparent;
|
||||
}
|
||||
|
||||
public Vector4f getColor(){
|
||||
return color;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,312 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.threejs;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.io.StringWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import com.flowpowered.math.GenericMath;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
|
||||
public class BufferGeometry {
|
||||
|
||||
public final float[] position, normal, color, uv;
|
||||
public final MaterialGroup[] groups;
|
||||
|
||||
public BufferGeometry(float[] position, float[] normal, float[] color, float[] uv, MaterialGroup[] groups) {
|
||||
this.position = position;
|
||||
this.normal = normal;
|
||||
this.color = color;
|
||||
this.uv = uv;
|
||||
this.groups = groups;
|
||||
}
|
||||
|
||||
public int getFaceCount(){
|
||||
return Math.floorDiv(position.length, 3);
|
||||
}
|
||||
|
||||
public String toJson() {
|
||||
try {
|
||||
|
||||
StringWriter sw = new StringWriter();
|
||||
Gson gson = new GsonBuilder().create();
|
||||
JsonWriter json = gson.newJsonWriter(sw);
|
||||
|
||||
json.beginObject();
|
||||
|
||||
//set special values
|
||||
json.name("type").value("BufferGeometry");
|
||||
json.name("uuid").value(UUID.randomUUID().toString().toUpperCase());
|
||||
|
||||
json.name("data").beginObject();
|
||||
json.name("attributes").beginObject();
|
||||
|
||||
json.name("position");
|
||||
floatArray2Json(json, position, 3, false);
|
||||
|
||||
json.name("normal");
|
||||
floatArray2Json(json, normal, 3, true);
|
||||
|
||||
json.name("color");
|
||||
floatArray2Json(json, color, 3, false);
|
||||
|
||||
json.name("uv");
|
||||
floatArray2Json(json, uv, 2, false);
|
||||
|
||||
json.endObject(); //attributes
|
||||
|
||||
|
||||
json.name("groups").beginArray();
|
||||
|
||||
//write groups into json
|
||||
for (BufferGeometry.MaterialGroup g : groups){
|
||||
json.beginObject();
|
||||
|
||||
json.name("materialIndex").value(g.getMaterialIndex());
|
||||
json.name("start").value(g.getStart());
|
||||
json.name("count").value(g.getCount());
|
||||
|
||||
json.endObject();
|
||||
}
|
||||
|
||||
json.endArray(); //groups
|
||||
json.endObject(); //data
|
||||
json.endObject(); //main-object
|
||||
|
||||
//save and return
|
||||
json.flush();
|
||||
return sw.toString();
|
||||
|
||||
} catch (IOException e){
|
||||
//since we are using a StringWriter there should never be an IO exception thrown
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static BufferGeometry fromJson(String jsonString) throws IOException {
|
||||
|
||||
Gson gson = new GsonBuilder().create();
|
||||
JsonReader json = gson.newJsonReader(new StringReader(jsonString));
|
||||
|
||||
List<Float> positionList = new ArrayList<>(300);
|
||||
List<Float> normalList = new ArrayList<>(300);
|
||||
List<Float> colorList = new ArrayList<>(300);
|
||||
List<Float> uvList = new ArrayList<>(200);
|
||||
List<MaterialGroup> groups = new ArrayList<>(10);
|
||||
|
||||
json.beginObject(); //root
|
||||
while (json.hasNext()){
|
||||
String name1 = json.nextName();
|
||||
|
||||
if(name1.equals("data")){
|
||||
json.beginObject(); //data
|
||||
while (json.hasNext()){
|
||||
String name2 = json.nextName();
|
||||
|
||||
if(name2.equals("attributes")){
|
||||
json.beginObject(); //attributes
|
||||
while (json.hasNext()){
|
||||
String name3 = json.nextName();
|
||||
|
||||
if(name3.equals("position")){
|
||||
json2FloatList(json, positionList);
|
||||
}
|
||||
|
||||
else if(name3.equals("normal")){
|
||||
json2FloatList(json, normalList);
|
||||
}
|
||||
|
||||
else if(name3.equals("color")){
|
||||
json2FloatList(json, colorList);
|
||||
}
|
||||
|
||||
else if(name3.equals("uv")){
|
||||
json2FloatList(json, uvList);
|
||||
}
|
||||
|
||||
else json.skipValue();
|
||||
}
|
||||
json.endObject(); //attributes
|
||||
}
|
||||
|
||||
else if (name2.equals("groups")){
|
||||
json.beginArray(); //groups
|
||||
while (json.hasNext()){
|
||||
MaterialGroup group = new MaterialGroup(0, 0, 0);
|
||||
json.beginObject(); //group
|
||||
while (json.hasNext()){
|
||||
String name3 = json.nextName();
|
||||
|
||||
if(name3.equals("materialIndex")){
|
||||
group.setMaterialIndex(json.nextInt());
|
||||
}
|
||||
|
||||
else if(name3.equals("start")){
|
||||
group.setStart(json.nextInt());
|
||||
}
|
||||
|
||||
else if(name3.equals("count")){
|
||||
group.setCount(json.nextInt());
|
||||
}
|
||||
|
||||
else json.skipValue();
|
||||
}
|
||||
json.endObject(); //group
|
||||
groups.add(group);
|
||||
}
|
||||
json.endArray(); //groups
|
||||
}
|
||||
|
||||
else json.skipValue();
|
||||
}
|
||||
json.endObject();//data
|
||||
}
|
||||
|
||||
else json.skipValue();
|
||||
}
|
||||
json.endObject(); //root
|
||||
|
||||
//check if this is a valid BufferGeometry
|
||||
int faceCount = Math.floorDiv(positionList.size(), 3);
|
||||
if (positionList.size() != faceCount * 3) throw new IllegalArgumentException("Wrong count of positions! (Got " + positionList.size() + " but expected " + (faceCount * 3) + ")");
|
||||
if (normalList.size() != faceCount * 3) throw new IllegalArgumentException("Wrong count of normals! (Got " + normalList.size() + " but expected " + (faceCount * 3) + ")");
|
||||
if (colorList.size() != faceCount * 3) throw new IllegalArgumentException("Wrong count of colors! (Got " + colorList.size() + " but expected " + (faceCount * 3) + ")");
|
||||
if (uvList.size() != faceCount * 2) throw new IllegalArgumentException("Wrong count of uvs! (Got " + uvList.size() + " but expected " + (faceCount * 2) + ")");
|
||||
|
||||
groups.sort((g1, g2) -> (int) Math.signum(g1.getStart() - g2.getStart()));
|
||||
int nextGroup = 0;
|
||||
for (MaterialGroup g : groups){
|
||||
if(g.getStart() != nextGroup) throw new IllegalArgumentException("Group did not start at correct index! (Got " + g.getStart() + " but expected " + nextGroup + ")");
|
||||
if(g.getCount() < 0) throw new IllegalArgumentException("Group has a negative count! (" + g.getCount() + ")");
|
||||
nextGroup += g.getCount();
|
||||
}
|
||||
|
||||
//collect values in arrays
|
||||
float[] position = new float[positionList.size()];
|
||||
for (int i = 0; i < position.length; i++) {
|
||||
position[i] = positionList.get(i);
|
||||
}
|
||||
|
||||
float[] normal = new float[normalList.size()];
|
||||
for (int i = 0; i < normal.length; i++) {
|
||||
normal[i] = normalList.get(i);
|
||||
}
|
||||
|
||||
float[] color = new float[colorList.size()];
|
||||
for (int i = 0; i < color.length; i++) {
|
||||
color[i] = colorList.get(i);
|
||||
}
|
||||
|
||||
float[] uv = new float[uvList.size()];
|
||||
for (int i = 0; i < uv.length; i++) {
|
||||
uv[i] = uvList.get(i);
|
||||
}
|
||||
|
||||
return new BufferGeometry(position, normal, color, uv,
|
||||
groups.toArray(new MaterialGroup[groups.size()])
|
||||
);
|
||||
}
|
||||
|
||||
private static void json2FloatList(JsonReader json, List<Float> list) throws IOException {
|
||||
json.beginObject(); //root
|
||||
while (json.hasNext()){
|
||||
String name = json.nextName();
|
||||
|
||||
if(name.equals("array")){
|
||||
json.beginArray(); //array
|
||||
while (json.hasNext()){
|
||||
list.add(new Float(json.nextDouble()));
|
||||
}
|
||||
json.endArray(); //array
|
||||
}
|
||||
|
||||
else json.skipValue();
|
||||
}
|
||||
json.endObject(); //root
|
||||
}
|
||||
|
||||
private static void floatArray2Json(JsonWriter json, float[] array, int itemSize, boolean normalized) throws IOException {
|
||||
json.beginObject();
|
||||
|
||||
json.name("type").value("Float32Array");
|
||||
json.name("itemSize").value(itemSize);
|
||||
json.name("normalized").value(normalized);
|
||||
|
||||
json.name("array").beginArray();
|
||||
for (int i = 0; i < array.length; i++){
|
||||
//rounding and remove ".0" to save string space
|
||||
double d = GenericMath.round(array[i], 3);
|
||||
if (d == (int) d) json.value((int) d);
|
||||
else json.value(d);
|
||||
}
|
||||
json.endArray();
|
||||
|
||||
json.endObject();
|
||||
}
|
||||
|
||||
public static class MaterialGroup {
|
||||
private int materialIndex;
|
||||
private int start;
|
||||
private int count;
|
||||
|
||||
public MaterialGroup(int materialIndex, int start, int count) {
|
||||
this.materialIndex = materialIndex;
|
||||
this.start = start;
|
||||
this.count = count;
|
||||
}
|
||||
|
||||
public int getMaterialIndex() {
|
||||
return materialIndex;
|
||||
}
|
||||
|
||||
public int getStart() {
|
||||
return start;
|
||||
}
|
||||
|
||||
public int getCount() {
|
||||
return count;
|
||||
}
|
||||
|
||||
public void setMaterialIndex(int materialIndex) {
|
||||
this.materialIndex = materialIndex;
|
||||
}
|
||||
|
||||
public void setStart(int start) {
|
||||
this.start = start;
|
||||
}
|
||||
|
||||
public void setCount(int count) {
|
||||
this.count = count;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,438 @@
|
||||
/*
|
||||
* This file is part of SpongeAPI, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) SpongePowered <https://www.spongepowered.org>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.util;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import com.flowpowered.math.vector.Vector3d;
|
||||
import com.flowpowered.math.vector.Vector3i;
|
||||
|
||||
/**
|
||||
* An axis aligned bounding box. That is, an un-rotated cuboid.
|
||||
* It is represented by its minimum and maximum corners.
|
||||
*
|
||||
* <p>The box will never be degenerate: the corners are always not equal and
|
||||
* respect the minimum and maximum properties.</p>
|
||||
*
|
||||
* <p>This class is immutable, all objects returned are either new instances or
|
||||
* itself.</p>
|
||||
*/
|
||||
public class AABB {
|
||||
|
||||
private final Vector3d min;
|
||||
private final Vector3d max;
|
||||
private Vector3d size = null;
|
||||
private Vector3d center = null;
|
||||
|
||||
/**
|
||||
* Constructs a new bounding box from two opposite corners.
|
||||
* Fails the resulting box would be degenerate (a dimension is 0).
|
||||
*
|
||||
* @param firstCorner The first corner
|
||||
* @param secondCorner The second corner
|
||||
*/
|
||||
public AABB(Vector3i firstCorner, Vector3i secondCorner) {
|
||||
this(checkNotNull(firstCorner, "firstCorner").toDouble(), checkNotNull(secondCorner, "secondCorner").toDouble());
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new bounding box from two opposite corners.
|
||||
* Fails the resulting box would be degenerate (a dimension is 0).
|
||||
*
|
||||
* @param x1 The first corner x coordinate
|
||||
* @param y1 The first corner y coordinate
|
||||
* @param z1 The first corner z coordinate
|
||||
* @param x2 The second corner x coordinate
|
||||
* @param y2 The second corner y coordinate
|
||||
* @param z2 The second corner z coordinate
|
||||
*/
|
||||
public AABB(double x1, double y1, double z1, double x2, double y2, double z2) {
|
||||
this(new Vector3d(x1, y1, z1), new Vector3d(x2, y2, z2));
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new bounding box from two opposite corners.
|
||||
* Fails the resulting box would be degenerate (a dimension is 0).
|
||||
*
|
||||
* @param firstCorner The first corner
|
||||
* @param secondCorner The second corner
|
||||
*/
|
||||
public AABB(Vector3d firstCorner, Vector3d secondCorner) {
|
||||
checkNotNull(firstCorner, "firstCorner");
|
||||
checkNotNull(secondCorner, "secondCorner");
|
||||
this.min = firstCorner.min(secondCorner);
|
||||
this.max = firstCorner.max(secondCorner);
|
||||
checkArgument(this.min.getX() != this.max.getX(), "The box is degenerate on x");
|
||||
checkArgument(this.min.getY() != this.max.getY(), "The box is degenerate on y");
|
||||
checkArgument(this.min.getZ() != this.max.getZ(), "The box is degenerate on z");
|
||||
}
|
||||
|
||||
/**
|
||||
* The minimum corner of the box.
|
||||
*
|
||||
* @return The minimum corner
|
||||
*/
|
||||
public Vector3d getMin() {
|
||||
return this.min;
|
||||
}
|
||||
|
||||
/**
|
||||
* The maximum corner of the box.
|
||||
*
|
||||
* @return The maximum corner
|
||||
*/
|
||||
public Vector3d getMax() {
|
||||
return this.max;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the center of the box, halfway between each corner.
|
||||
*
|
||||
* @return The center
|
||||
*/
|
||||
public Vector3d getCenter() {
|
||||
if (this.center == null) {
|
||||
this.center = this.min.add(getSize().div(2));
|
||||
}
|
||||
return this.center;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the size of the box.
|
||||
*
|
||||
* @return The size
|
||||
*/
|
||||
public Vector3d getSize() {
|
||||
if (this.size == null) {
|
||||
this.size = this.max.sub(this.min);
|
||||
}
|
||||
return this.size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the bounding box contains a point.
|
||||
*
|
||||
* @param point The point to check
|
||||
* @return Whether or not the box contains the point
|
||||
*/
|
||||
public boolean contains(Vector3i point) {
|
||||
checkNotNull(point, "point");
|
||||
return contains(point.getX(), point.getY(), point.getZ());
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the bounding box contains a point.
|
||||
*
|
||||
* @param point The point to check
|
||||
* @return Whether or not the box contains the point
|
||||
*/
|
||||
public boolean contains(Vector3d point) {
|
||||
checkNotNull(point, "point");
|
||||
return contains(point.getX(), point.getY(), point.getZ());
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the bounding box contains a point.
|
||||
*
|
||||
* @param x The x coordinate of the point
|
||||
* @param y The y coordinate of the point
|
||||
* @param z The z coordinate of the point
|
||||
* @return Whether or not the box contains the point
|
||||
*/
|
||||
public boolean contains(double x, double y, double z) {
|
||||
return this.min.getX() <= x && this.max.getX() >= x
|
||||
&& this.min.getY() <= y && this.max.getY() >= y
|
||||
&& this.min.getZ() <= z && this.max.getZ() >= z;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the bounding box intersects another.
|
||||
*
|
||||
* @param other The other bounding box to check
|
||||
* @return Whether this bounding box intersects with the other
|
||||
*/
|
||||
public boolean intersects(AABB other) {
|
||||
checkNotNull(other, "other");
|
||||
return this.max.getX() >= other.getMin().getX() && other.getMax().getX() >= this.min.getX()
|
||||
&& this.max.getY() >= other.getMin().getY() && other.getMax().getY() >= this.min.getY()
|
||||
&& this.max.getZ() >= other.getMin().getZ() && other.getMax().getZ() >= this.min.getZ();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests for intersection between the box and a ray defined by a starting
|
||||
* point and a direction.
|
||||
*
|
||||
* @param start The starting point of the ray
|
||||
* @param direction The direction of the ray
|
||||
* @return An intersection point, if any
|
||||
*/
|
||||
public Optional<IntersectionPoint> intersects(Vector3d start, Vector3d direction) {
|
||||
checkNotNull(start, "start");
|
||||
checkNotNull(direction, "direction");
|
||||
// Adapted from: https://github.com/flow/react/blob/develop/src/main/java/com/flowpowered/react/collision/RayCaster.java#L156
|
||||
// The box is interpreted as 6 infinite perpendicular places, one for each face (being expanded infinitely)
|
||||
// "t" variables are multipliers: start + direction * t gives the intersection point
|
||||
// Find the intersections on the -x and +x planes, oriented by direction
|
||||
final double txMin;
|
||||
final double txMax;
|
||||
final Vector3d xNormal;
|
||||
if (Math.copySign(1, direction.getX()) > 0) {
|
||||
txMin = (this.min.getX() - start.getX()) / direction.getX();
|
||||
txMax = (this.max.getX() - start.getX()) / direction.getX();
|
||||
xNormal = Vector3d.UNIT_X;
|
||||
} else {
|
||||
txMin = (this.max.getX() - start.getX()) / direction.getX();
|
||||
txMax = (this.min.getX() - start.getX()) / direction.getX();
|
||||
xNormal = Vector3d.UNIT_X.negate();
|
||||
}
|
||||
// Find the intersections on the -y and +y planes, oriented by direction
|
||||
final double tyMin;
|
||||
final double tyMax;
|
||||
final Vector3d yNormal;
|
||||
if (Math.copySign(1, direction.getY()) > 0) {
|
||||
tyMin = (this.min.getY() - start.getY()) / direction.getY();
|
||||
tyMax = (this.max.getY() - start.getY()) / direction.getY();
|
||||
yNormal = Vector3d.UNIT_Y;
|
||||
} else {
|
||||
tyMin = (this.max.getY() - start.getY()) / direction.getY();
|
||||
tyMax = (this.min.getY() - start.getY()) / direction.getY();
|
||||
yNormal = Vector3d.UNIT_Y.negate();
|
||||
}
|
||||
// The ray should intersect the -x plane before the +y plane and intersect
|
||||
// the -y plane before the +x plane, else it is outside the box
|
||||
if (txMin > tyMax || txMax < tyMin) {
|
||||
return Optional.empty();
|
||||
}
|
||||
// Keep track of the intersection normal which also helps with floating point errors
|
||||
Vector3d normalMax;
|
||||
Vector3d normalMin;
|
||||
// The ray intersects only the furthest min plane on the box and only the closest
|
||||
// max plane on the box
|
||||
double tMin;
|
||||
if (tyMin == txMin) {
|
||||
tMin = tyMin;
|
||||
normalMin = xNormal.negate().sub(yNormal);
|
||||
} else if (tyMin > txMin) {
|
||||
tMin = tyMin;
|
||||
normalMin = yNormal.negate();
|
||||
} else {
|
||||
tMin = txMin;
|
||||
normalMin = xNormal.negate();
|
||||
}
|
||||
double tMax;
|
||||
if (tyMax == txMax) {
|
||||
tMax = tyMax;
|
||||
normalMax = xNormal.add(yNormal);
|
||||
} else if (tyMax < txMax) {
|
||||
tMax = tyMax;
|
||||
normalMax = yNormal;
|
||||
} else {
|
||||
tMax = txMax;
|
||||
normalMax = xNormal;
|
||||
}
|
||||
// Find the intersections on the -z and +z planes, oriented by direction
|
||||
final double tzMin;
|
||||
final double tzMax;
|
||||
final Vector3d zNormal;
|
||||
if (Math.copySign(1, direction.getZ()) > 0) {
|
||||
tzMin = (this.min.getZ() - start.getZ()) / direction.getZ();
|
||||
tzMax = (this.max.getZ() - start.getZ()) / direction.getZ();
|
||||
zNormal = Vector3d.UNIT_Z;
|
||||
} else {
|
||||
tzMin = (this.max.getZ() - start.getZ()) / direction.getZ();
|
||||
tzMax = (this.min.getZ() - start.getZ()) / direction.getZ();
|
||||
zNormal = Vector3d.UNIT_Z.negate();
|
||||
}
|
||||
// The ray intersects only the furthest min plane on the box and only the closest
|
||||
// max plane on the box
|
||||
if (tMin > tzMax || tMax < tzMin) {
|
||||
return Optional.empty();
|
||||
}
|
||||
// The ray should intersect the closest plane outside first and the furthest
|
||||
// plane outside last
|
||||
if (tzMin == tMin) {
|
||||
normalMin = normalMin.sub(zNormal);
|
||||
} else if (tzMin > tMin) {
|
||||
tMin = tzMin;
|
||||
normalMin = zNormal.negate();
|
||||
}
|
||||
if (tzMax == tMax) {
|
||||
normalMax = normalMax.add(zNormal);
|
||||
} else if (tzMax < tMax) {
|
||||
tMax = tzMax;
|
||||
normalMax = zNormal;
|
||||
}
|
||||
// Both intersection points are behind the start, there are no intersections
|
||||
if (tMax < 0) {
|
||||
return Optional.empty();
|
||||
}
|
||||
// Find the final intersection multiplier and normal
|
||||
final double t;
|
||||
Vector3d normal;
|
||||
if (tMin < 0) {
|
||||
// Only the furthest intersection is after the start, so use it
|
||||
t = tMax;
|
||||
normal = normalMax;
|
||||
} else {
|
||||
// Both are after the start, use the closest one
|
||||
t = tMin;
|
||||
normal = normalMin;
|
||||
}
|
||||
normal = normal.normalize();
|
||||
// To avoid rounding point errors leaving the intersection point just off the plane
|
||||
// we check the normal to use the actual plane value from the box coordinates
|
||||
final double x;
|
||||
final double y;
|
||||
final double z;
|
||||
if (normal.getX() > 0) {
|
||||
x = this.max.getX();
|
||||
} else if (normal.getX() < 0) {
|
||||
x = this.min.getX();
|
||||
} else {
|
||||
x = direction.getX() * t + start.getX();
|
||||
}
|
||||
if (normal.getY() > 0) {
|
||||
y = this.max.getY();
|
||||
} else if (normal.getY() < 0) {
|
||||
y = this.min.getY();
|
||||
} else {
|
||||
y = direction.getY() * t + start.getY();
|
||||
}
|
||||
if (normal.getZ() > 0) {
|
||||
z = this.max.getZ();
|
||||
} else if (normal.getZ() < 0) {
|
||||
z = this.min.getZ();
|
||||
} else {
|
||||
z = direction.getZ() * t + start.getZ();
|
||||
}
|
||||
return Optional.of(new IntersectionPoint(new Vector3d(x, y, z), normal));
|
||||
}
|
||||
|
||||
/**
|
||||
* Offsets this bounding box by a given amount and returns a new box.
|
||||
*
|
||||
* @param offset The offset to apply
|
||||
* @return The new offset box
|
||||
*/
|
||||
public AABB offset(Vector3i offset) {
|
||||
checkNotNull(offset, "offset");
|
||||
return offset(offset.getX(), offset.getY(), offset.getZ());
|
||||
}
|
||||
|
||||
/**
|
||||
* Offsets this bounding box by a given amount and returns a new box.
|
||||
*
|
||||
* @param offset The offset to apply
|
||||
* @return The new offset box
|
||||
*/
|
||||
public AABB offset(Vector3d offset) {
|
||||
checkNotNull(offset, "offset");
|
||||
return offset(offset.getX(), offset.getY(), offset.getZ());
|
||||
}
|
||||
|
||||
/**
|
||||
* Offsets this bounding box by a given amount and returns a new box.
|
||||
*
|
||||
* @param x The amount of offset for the x coordinate
|
||||
* @param y The amount of offset for the y coordinate
|
||||
* @param z The amount of offset for the z coordinate
|
||||
* @return The new offset box
|
||||
*/
|
||||
public AABB offset(double x, double y, double z) {
|
||||
return new AABB(this.min.add(x, y, z), this.max.add(x, y, z));
|
||||
}
|
||||
|
||||
/**
|
||||
* Expands this bounding box by a given amount in both directions and
|
||||
* returns a new box. The expansion is applied half and half to the
|
||||
* minimum and maximum corners.
|
||||
*
|
||||
* @param amount The amount of expansion to apply
|
||||
* @return The new expanded box
|
||||
*/
|
||||
public AABB expand(Vector3i amount) {
|
||||
checkNotNull(amount, "amount");
|
||||
return expand(amount.getX(), amount.getY(), amount.getZ());
|
||||
}
|
||||
|
||||
/**
|
||||
* Expands this bounding box by a given amount in both directions and
|
||||
* returns a new box. The expansion is applied half and half to the
|
||||
* minimum and maximum corners.
|
||||
*
|
||||
* @param amount The amount of expansion to apply
|
||||
* @return The new expanded box
|
||||
*/
|
||||
public AABB expand(Vector3d amount) {
|
||||
checkNotNull(amount, "amount");
|
||||
return expand(amount.getX(), amount.getY(), amount.getZ());
|
||||
}
|
||||
|
||||
/**
|
||||
* Expands this bounding box by a given amount in both directions and
|
||||
* returns a new box. The expansion is applied half and half to the
|
||||
* minimum and maximum corners.
|
||||
*
|
||||
* @param x The amount of expansion for the x coordinate
|
||||
* @param y The amount of expansion for the y coordinate
|
||||
* @param z The amount of expansion for the z coordinate
|
||||
* @return The new expanded box
|
||||
*/
|
||||
public AABB expand(double x, double y, double z) {
|
||||
x /= 2;
|
||||
y /= 2;
|
||||
z /= 2;
|
||||
return new AABB(this.min.sub(x, y, z), this.max.add(x, y, z));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (this == other) {
|
||||
return true;
|
||||
}
|
||||
if (!(other instanceof AABB)) {
|
||||
return false;
|
||||
}
|
||||
final AABB aabb = (AABB) other;
|
||||
return this.min.equals(aabb.min) && this.max.equals(aabb.max);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = this.min.hashCode();
|
||||
result = 31 * result + this.max.hashCode();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "AABB(" + this.min + " to " + this.max + ")";
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.util;
|
||||
|
||||
import com.flowpowered.math.vector.Vector3i;
|
||||
import com.google.common.base.Preconditions;
|
||||
|
||||
public enum Axis {
|
||||
|
||||
X (Vector3i.UNIT_X),
|
||||
Y (Vector3i.UNIT_Y),
|
||||
Z (Vector3i.UNIT_Z);
|
||||
|
||||
private final Vector3i axisVector;
|
||||
|
||||
Axis(Vector3i axisVector){
|
||||
this.axisVector = axisVector;
|
||||
}
|
||||
|
||||
public Vector3i toVector(){
|
||||
return axisVector;
|
||||
}
|
||||
|
||||
public static Axis fromString(String name){
|
||||
Preconditions.checkNotNull(name);
|
||||
|
||||
return valueOf(name.toUpperCase());
|
||||
}
|
||||
}
|
@ -0,0 +1,135 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.util;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.flowpowered.math.vector.Vector2i;
|
||||
import com.flowpowered.math.vector.Vector3f;
|
||||
import com.flowpowered.math.vector.Vector3i;
|
||||
import com.flowpowered.math.vector.Vector4f;
|
||||
import com.flowpowered.math.vector.Vector4i;
|
||||
|
||||
import ninja.leaping.configurate.ConfigurationNode;
|
||||
|
||||
public class ConfigUtil {
|
||||
|
||||
private ConfigUtil(){}
|
||||
|
||||
public static Vector2i readVector2i(ConfigurationNode vectorNode){
|
||||
if (vectorNode.hasListChildren()){
|
||||
List<? extends ConfigurationNode> list = vectorNode.getChildrenList();
|
||||
return new Vector2i(
|
||||
list.get(0).getInt(),
|
||||
list.get(1).getInt()
|
||||
);
|
||||
}
|
||||
|
||||
return new Vector2i(
|
||||
vectorNode.getNode("x").getInt(),
|
||||
vectorNode.getNode("y").getInt()
|
||||
);
|
||||
}
|
||||
|
||||
public static Vector3i readVector3i(ConfigurationNode vectorNode){
|
||||
if (vectorNode.hasListChildren()){
|
||||
List<? extends ConfigurationNode> list = vectorNode.getChildrenList();
|
||||
return new Vector3i(
|
||||
list.get(0).getInt(),
|
||||
list.get(1).getInt(),
|
||||
list.get(2).getInt()
|
||||
);
|
||||
}
|
||||
|
||||
return new Vector3i(
|
||||
vectorNode.getNode("x").getInt(),
|
||||
vectorNode.getNode("y").getInt(),
|
||||
vectorNode.getNode("z").getInt()
|
||||
);
|
||||
}
|
||||
|
||||
public static Vector3f readVector3f(ConfigurationNode vectorNode){
|
||||
if (vectorNode.hasListChildren()){
|
||||
List<? extends ConfigurationNode> list = vectorNode.getChildrenList();
|
||||
return new Vector3f(
|
||||
list.get(0).getFloat(),
|
||||
list.get(1).getFloat(),
|
||||
list.get(2).getFloat()
|
||||
);
|
||||
}
|
||||
|
||||
return new Vector3f(
|
||||
vectorNode.getNode("x").getFloat(),
|
||||
vectorNode.getNode("y").getFloat(),
|
||||
vectorNode.getNode("z").getFloat()
|
||||
);
|
||||
}
|
||||
|
||||
public static Vector4i readVector4i(ConfigurationNode vectorNode){
|
||||
if (vectorNode.hasListChildren()){
|
||||
List<? extends ConfigurationNode> list = vectorNode.getChildrenList();
|
||||
return new Vector4i(
|
||||
list.get(0).getInt(),
|
||||
list.get(1).getInt(),
|
||||
list.get(2).getInt(),
|
||||
list.get(3).getInt()
|
||||
);
|
||||
}
|
||||
|
||||
return new Vector4i(
|
||||
vectorNode.getNode("x").getInt(),
|
||||
vectorNode.getNode("y").getInt(),
|
||||
vectorNode.getNode("z").getInt(),
|
||||
vectorNode.getNode("w").getInt()
|
||||
);
|
||||
}
|
||||
|
||||
public static Vector4f readVector4f(ConfigurationNode vectorNode){
|
||||
if (vectorNode.hasListChildren()){
|
||||
List<? extends ConfigurationNode> list = vectorNode.getChildrenList();
|
||||
return new Vector4f(
|
||||
list.get(0).getFloat(),
|
||||
list.get(1).getFloat(),
|
||||
list.get(2).getFloat(),
|
||||
list.get(3).getFloat()
|
||||
);
|
||||
}
|
||||
|
||||
return new Vector4f(
|
||||
vectorNode.getNode("x").getFloat(),
|
||||
vectorNode.getNode("y").getFloat(),
|
||||
vectorNode.getNode("z").getFloat(),
|
||||
vectorNode.getNode("w").getFloat()
|
||||
);
|
||||
}
|
||||
|
||||
public static void writeVector4f(ConfigurationNode vectorNode, Vector4f v){
|
||||
vectorNode.getAppendedNode().setValue(v.getX());
|
||||
vectorNode.getAppendedNode().setValue(v.getY());
|
||||
vectorNode.getAppendedNode().setValue(v.getZ());
|
||||
vectorNode.getAppendedNode().setValue(v.getW());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,75 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.util;
|
||||
|
||||
import com.flowpowered.math.vector.Vector3i;
|
||||
import com.google.common.base.Preconditions;
|
||||
|
||||
public enum Direction {
|
||||
|
||||
UP ( 0, 1, 0, Axis.Y),
|
||||
DOWN ( 0,-1, 0, Axis.Y),
|
||||
NORTH ( 0, 0,-1, Axis.Z),
|
||||
SOUTH ( 0, 0, 1, Axis.Z),
|
||||
WEST (-1, 0, 0, Axis.X),
|
||||
EAST ( 1, 0, 0, Axis.X);
|
||||
|
||||
static {
|
||||
UP.opposite = DOWN;
|
||||
DOWN.opposite = UP;
|
||||
NORTH.opposite = SOUTH;
|
||||
SOUTH.opposite = NORTH;
|
||||
WEST.opposite = EAST;
|
||||
EAST.opposite = WEST;
|
||||
}
|
||||
|
||||
private Vector3i dir;
|
||||
private Axis axis;
|
||||
private Direction opposite;
|
||||
|
||||
private Direction(int x, int y, int z, Axis axis) {
|
||||
this.dir = new Vector3i(x, y, z);
|
||||
this.axis = axis;
|
||||
this.opposite = null;
|
||||
}
|
||||
|
||||
public Vector3i toVector(){
|
||||
return dir;
|
||||
}
|
||||
|
||||
public Direction opposite() {
|
||||
return opposite;
|
||||
}
|
||||
|
||||
public Axis getAxis() {
|
||||
return axis;
|
||||
}
|
||||
|
||||
public static Direction fromString(String name){
|
||||
Preconditions.checkNotNull(name);
|
||||
|
||||
return valueOf(name.toUpperCase());
|
||||
}
|
||||
}
|
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.util;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import com.flowpowered.math.vector.Vector2i;
|
||||
|
||||
public class FileUtil {
|
||||
|
||||
private FileUtil(){}
|
||||
|
||||
public static File coordsToFile(Path root, Vector2i coords, String fileType){
|
||||
String path = "x" + coords.getX() + "z" + coords.getY();
|
||||
char[] cs = path.toCharArray();
|
||||
List<String> folders = new ArrayList<>();
|
||||
String folder = "";
|
||||
for (char c : cs){
|
||||
folder += c;
|
||||
if (c >= '0' && c <= '9'){
|
||||
folders.add(folder);
|
||||
folder = "";
|
||||
}
|
||||
}
|
||||
String fileName = folders.remove(folders.size() - 1);
|
||||
|
||||
Path p = root;
|
||||
for (String s : folders){
|
||||
p = p.resolve(s);
|
||||
}
|
||||
|
||||
return p.resolve(fileName + "." + fileType).toFile();
|
||||
}
|
||||
|
||||
/**
|
||||
* Blocks until a file can be read and written.<br>
|
||||
* <i>(Do not use this method to sync file-access from different threads!)</i>
|
||||
*/
|
||||
public static void waitForFile(File file, long time, TimeUnit unit) throws InterruptedException {
|
||||
long start = System.currentTimeMillis();
|
||||
long timeout = start + TimeUnit.MILLISECONDS.convert(time, unit);
|
||||
long sleepTime = 1;
|
||||
while(!file.canWrite() || !file.canRead()){
|
||||
Thread.sleep(sleepTime);
|
||||
sleepTime = (long) Math.min(Math.ceil(sleepTime * 1.5), 1000);
|
||||
if (System.currentTimeMillis() > timeout) throw new InterruptedException();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.util;
|
||||
|
||||
import com.flowpowered.math.vector.Vector3d;
|
||||
|
||||
public class IntersectionPoint {
|
||||
|
||||
private final Vector3d intersection;
|
||||
private final Vector3d normal;
|
||||
|
||||
public IntersectionPoint(Vector3d intersection, Vector3d normal){
|
||||
this.intersection = intersection;
|
||||
this.normal = normal;
|
||||
}
|
||||
|
||||
public Vector3d getIntersection() {
|
||||
return intersection;
|
||||
}
|
||||
|
||||
public Vector3d getNormal() {
|
||||
return normal;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,102 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.util;
|
||||
|
||||
import com.flowpowered.math.vector.Vector3d;
|
||||
import com.flowpowered.math.vector.Vector3f;
|
||||
import com.flowpowered.math.vector.Vector3i;
|
||||
import com.flowpowered.math.vector.Vector4f;
|
||||
|
||||
public class MathUtil {
|
||||
|
||||
private MathUtil() {}
|
||||
|
||||
public static Vector3d getSurfaceNormal(Vector3d p1, Vector3d p2, Vector3d p3){
|
||||
Vector3d u = p2.sub(p1);
|
||||
Vector3d v = p3.sub(p1);
|
||||
|
||||
double nX = u.getY() * v.getZ() - u.getZ() * v.getY();
|
||||
double nY = u.getZ() * v.getX() - u.getX() * v.getZ();
|
||||
double nZ = u.getX() * v.getY() - u.getY() * v.getX();
|
||||
|
||||
return new Vector3d(nX, nY, nZ);
|
||||
}
|
||||
|
||||
public static Vector3f getSurfaceNormal(Vector3f p1, Vector3f p2, Vector3f p3) {
|
||||
Vector3f u = p2.sub(p1);
|
||||
Vector3f v = p3.sub(p1);
|
||||
|
||||
float nX = u.getY() * v.getZ() - u.getZ() * v.getY();
|
||||
float nY = u.getZ() * v.getX() - u.getX() * v.getZ();
|
||||
float nZ = u.getX() * v.getY() - u.getY() * v.getX();
|
||||
|
||||
Vector3f n = new Vector3f(nX, nY, nZ);
|
||||
n = n.normalize();
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
public static float hashToFloat(Vector3i pos, long seed) {
|
||||
return hashToFloat(pos.getX(), pos.getY(), pos.getZ(), seed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adapted from https://github.com/SpongePowered/SpongeAPI/blob/ecd761a70219e467dea47a09fc310e8238e9911f/src/main/java/org/spongepowered/api/extra/skylands/SkylandsUtil.java
|
||||
*/
|
||||
public static float hashToFloat(int x, int y, int z, long seed) {
|
||||
final long hash = x * 73428767 ^ y * 9122569 ^ z * 4382893 ^ seed * 457;
|
||||
return (hash * (hash + 456149) & 0x00ffffff) / (float) 0x01000000;
|
||||
}
|
||||
|
||||
public static Vector4f blendColors(Vector4f top, Vector4f bottom){
|
||||
if (top.getW() > 0 && bottom.getW() > 0){
|
||||
float a = 1 - (1 - top.getW()) * (1 - bottom.getW());
|
||||
float r = (top.getX() * top.getW() / a) + (bottom.getX() * bottom.getW() * (1 - top.getW()) / a);
|
||||
float g = (top.getY() * top.getW() / a) + (bottom.getY() * bottom.getW() * (1 - top.getW()) / a);
|
||||
float b = (top.getZ() * top.getW() / a) + (bottom.getZ() * bottom.getW() * (1 - top.getW()) / a);
|
||||
return new Vector4f(r, g, b, a);
|
||||
} else if (bottom.getW() > 0) {
|
||||
return bottom;
|
||||
} else {
|
||||
return top;
|
||||
}
|
||||
}
|
||||
|
||||
public static Vector4f overlayColors(Vector4f top, Vector4f bottom){
|
||||
if (top.getW() > 0 && bottom.getW() > 0){
|
||||
float p = (1 - top.getW()) * bottom.getW();
|
||||
float a = p + top.getW();
|
||||
float r = (p * bottom.getX() + top.getW() * top.getX()) / a;
|
||||
float g = (p * bottom.getY() + top.getW() * top.getY()) / a;
|
||||
float b = (p * bottom.getZ() + top.getW() * top.getZ()) / a;
|
||||
return new Vector4f(r, g, b, a);
|
||||
} else if (bottom.getW() > 0) {
|
||||
return bottom;
|
||||
} else {
|
||||
return top;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,90 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.util;
|
||||
|
||||
import com.flowpowered.math.vector.Vector2f;
|
||||
import com.flowpowered.math.vector.Vector2i;
|
||||
import com.flowpowered.math.vector.Vector3f;
|
||||
|
||||
import de.bluecolored.bluemap.core.model.Face;
|
||||
import de.bluecolored.bluemap.core.model.Model;
|
||||
|
||||
public class ModelUtils {
|
||||
|
||||
private ModelUtils() {}
|
||||
|
||||
/**
|
||||
* Creates a plane-grid with alternating face-rotations.
|
||||
*/
|
||||
public static Model makeGrid(Vector2i gridSize){
|
||||
Model m = new Model();
|
||||
|
||||
float y = 0;
|
||||
|
||||
for (int x = 0; x < gridSize.getX(); x++){
|
||||
for (int z = 0; z < gridSize.getY(); z++){
|
||||
|
||||
Vector3f[] p = new Vector3f[]{
|
||||
new Vector3f(x , y, z + 1),
|
||||
new Vector3f(x + 1, y, z + 1),
|
||||
new Vector3f(x + 1, y, z ),
|
||||
new Vector3f(x , y, z ),
|
||||
};
|
||||
|
||||
Vector2f[] uv = new Vector2f[]{
|
||||
new Vector2f(0, 1),
|
||||
new Vector2f(1, 1),
|
||||
new Vector2f(1, 0),
|
||||
new Vector2f(0, 0),
|
||||
};
|
||||
|
||||
Face f1, f2;
|
||||
if (x % 2 == z % 2){
|
||||
f1 = new Face(p[0], p[1], p[2], uv[0], uv[1], uv[2], -1);
|
||||
f2 = new Face(p[0], p[2], p[3], uv[0], uv[2], uv[3], -1);
|
||||
} else {
|
||||
f1 = new Face(p[0], p[1], p[3], uv[0], uv[1], uv[3], -1);
|
||||
f2 = new Face(p[1], p[2], p[3], uv[1], uv[2], uv[3], -1);
|
||||
}
|
||||
|
||||
Vector3f color = Vector3f.ZERO;
|
||||
|
||||
f1.setC1(color);
|
||||
f1.setC2(color);
|
||||
f1.setC3(color);
|
||||
|
||||
f2.setC1(color);
|
||||
f2.setC2(color);
|
||||
f2.setC3(color);
|
||||
|
||||
m.addFace(f1);
|
||||
m.addFace(f2);
|
||||
}
|
||||
}
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.util;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
public class UpdateDeamon extends Thread {
|
||||
|
||||
public WeakReference<Updateable> subject;
|
||||
public long frequency;
|
||||
|
||||
public UpdateDeamon(Runnable subject, long frequency) {
|
||||
this((Updateable) (()->subject.run()), frequency);
|
||||
}
|
||||
|
||||
public UpdateDeamon(Updateable subject, long frequency) {
|
||||
this.subject = new WeakReference<Updateable>(subject);
|
||||
this.frequency = frequency;
|
||||
this.setDaemon(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
while (true) {
|
||||
Thread.sleep(frequency);
|
||||
subject.get().update();
|
||||
}
|
||||
} catch (NullPointerException | InterruptedException ex) {}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.util;
|
||||
|
||||
public interface Updateable {
|
||||
|
||||
public void update();
|
||||
|
||||
}
|
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class WeighedArrayList<E> extends ArrayList<E> implements List<E> {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public WeighedArrayList() {}
|
||||
|
||||
public WeighedArrayList(int capacity) {
|
||||
super(capacity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the element weight times to this list.
|
||||
* @return Always true
|
||||
*/
|
||||
public void add(E e, int weight) {
|
||||
for (int i = 0; i < weight; i++){
|
||||
add(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the first weight number of items that equal o from this list.<br>
|
||||
* @return The number of elements removed.
|
||||
*/
|
||||
public int remove(Object o, int weight) {
|
||||
int removed = 0;
|
||||
if (o == null){
|
||||
for (int i = 0; i < size(); i++){
|
||||
if (get(i) == null){
|
||||
remove(i);
|
||||
removed++;
|
||||
if (removed >= weight) break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < size(); i++){
|
||||
if (o.equals(get(i))){
|
||||
remove(i);
|
||||
removed++;
|
||||
if (removed >= weight) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return removed;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,276 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.web;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.InvalidPathException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TimeZone;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang3.time.DateFormatUtils;
|
||||
|
||||
import de.bluecolored.bluemap.core.webserver.HttpRequest;
|
||||
import de.bluecolored.bluemap.core.webserver.HttpRequestHandler;
|
||||
import de.bluecolored.bluemap.core.webserver.HttpResponse;
|
||||
import de.bluecolored.bluemap.core.webserver.HttpStatusCode;
|
||||
|
||||
public class BlueMapWebRequestHandler implements HttpRequestHandler {
|
||||
|
||||
private static final long DEFLATE_MIN_SIZE = 10L * 1024L;
|
||||
private static final long DEFLATE_MAX_SIZE = 10L * 1024L * 1024L;
|
||||
private static final long INFLATE_MAX_SIZE = 10L * 1024L * 1024L;
|
||||
|
||||
private Path webRoot;
|
||||
|
||||
public BlueMapWebRequestHandler(Path webRoot) {
|
||||
this.webRoot = webRoot;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpResponse handle(HttpRequest request) {
|
||||
if (
|
||||
!request.getMethod().equalsIgnoreCase("GET") &&
|
||||
!request.getMethod().equalsIgnoreCase("POST")
|
||||
) return new HttpResponse(HttpStatusCode.NOT_IMPLEMENTED);
|
||||
|
||||
HttpResponse response = generateResponse(request);
|
||||
response.addHeader("Server", "BlueMap/WebServer");
|
||||
|
||||
HttpStatusCode status = response.getStatusCode();
|
||||
if (status.getCode() >= 400){
|
||||
response.setData(status.getCode() + " - " + status.getMessage() + "\nBlueMap/Webserver");
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
private HttpResponse generateResponse(HttpRequest request) {
|
||||
String adress = request.getPath();
|
||||
if (adress.isEmpty()) adress = "/";
|
||||
String[] adressParts = adress.split("\\?", 2);
|
||||
String path = adressParts[0];
|
||||
String getParamString = adressParts.length > 1 ? adressParts[1] : "";
|
||||
|
||||
Map<String, String> getParams = new HashMap<>();
|
||||
for (String getParam : getParamString.split("&")){
|
||||
if (getParam.isEmpty()) continue;
|
||||
String[] kv = getParam.split("=", 2);
|
||||
String key = kv[0];
|
||||
String value = kv.length > 1 ? kv[1] : "";
|
||||
getParams.put(key, value);
|
||||
}
|
||||
|
||||
if (path.startsWith("/")) path = path.substring(1);
|
||||
if (path.endsWith("/")) path = path.substring(0, path.length() - 1);
|
||||
|
||||
Path filePath = webRoot;
|
||||
try {
|
||||
filePath = webRoot.resolve(path);
|
||||
} catch (InvalidPathException e){
|
||||
return new HttpResponse(HttpStatusCode.NOT_FOUND);
|
||||
}
|
||||
|
||||
//can we use deflation?
|
||||
boolean isDeflationPossible = request.getLowercaseHeader("Accept-Encoding").contains("gzip");
|
||||
boolean isDeflated = false;
|
||||
|
||||
//check if file is in web-root
|
||||
if (!filePath.normalize().startsWith(webRoot.normalize())){
|
||||
return new HttpResponse(HttpStatusCode.FORBIDDEN);
|
||||
}
|
||||
|
||||
File file = filePath.toFile();
|
||||
|
||||
if (!file.exists() || file.isDirectory()){
|
||||
file = new File(filePath.toString() + ".gz");
|
||||
isDeflated = true;
|
||||
}
|
||||
|
||||
if (!file.exists() || file.isDirectory()){
|
||||
file = new File(filePath.toString() + "/index.html");
|
||||
isDeflated = false;
|
||||
}
|
||||
|
||||
if (!file.exists() || file.isDirectory()){
|
||||
file = new File(filePath.toString() + "/index.html.gz");
|
||||
isDeflated = true;
|
||||
}
|
||||
|
||||
if (!file.exists()){
|
||||
return new HttpResponse(HttpStatusCode.NOT_FOUND);
|
||||
}
|
||||
|
||||
if (isDeflationPossible && (!file.getName().endsWith(".gz"))){
|
||||
File deflatedFile = new File(file.getAbsolutePath() + ".gz");
|
||||
if (deflatedFile.exists()){
|
||||
file = deflatedFile;
|
||||
isDeflated = true;
|
||||
}
|
||||
}
|
||||
|
||||
//check if file is still in web-root
|
||||
if (!file.toPath().normalize().startsWith(webRoot.normalize())){
|
||||
return new HttpResponse(HttpStatusCode.FORBIDDEN);
|
||||
}
|
||||
|
||||
//check modified
|
||||
long lastModified = file.lastModified();
|
||||
Set<String> modStringSet = request.getHeader("If-Modified-Since");
|
||||
if (!modStringSet.isEmpty()){
|
||||
try {
|
||||
long since = stringToTimestamp(modStringSet.iterator().next());
|
||||
if (since + 1000 >= lastModified){
|
||||
return new HttpResponse(HttpStatusCode.NOT_MODIFIED);
|
||||
}
|
||||
} catch (IllegalArgumentException e){}
|
||||
}
|
||||
|
||||
|
||||
HttpResponse response = new HttpResponse(HttpStatusCode.OK);
|
||||
if (lastModified > 0) response.addHeader("Last-Modified", timestampToString(lastModified));
|
||||
|
||||
//add content type header
|
||||
String filetype = file.getName().toString();
|
||||
if (filetype.endsWith(".gz")) filetype = filetype.substring(3);
|
||||
int pointIndex = filetype.lastIndexOf('.');
|
||||
if (pointIndex >= 0) filetype = filetype.substring(pointIndex + 1);
|
||||
|
||||
String contentType = "text/plain";
|
||||
switch (filetype) {
|
||||
case "json" :
|
||||
contentType = "application/json";
|
||||
break;
|
||||
case "png" :
|
||||
contentType = "image/png";
|
||||
break;
|
||||
case "jpg" :
|
||||
case "jpeg" :
|
||||
case "jpe" :
|
||||
contentType = "image/jpeg";
|
||||
break;
|
||||
case "svg" :
|
||||
contentType = "image/svg+xml";
|
||||
break;
|
||||
case "css" :
|
||||
contentType = "text/css";
|
||||
break;
|
||||
case "js" :
|
||||
contentType = "text/javascript";
|
||||
break;
|
||||
case "html" :
|
||||
case "htm" :
|
||||
case "shtml" :
|
||||
contentType = "text/html";
|
||||
break;
|
||||
case "xml" :
|
||||
contentType = "text/xml";
|
||||
break;
|
||||
}
|
||||
response.addHeader("Content-Type", contentType);
|
||||
|
||||
|
||||
try {
|
||||
if (isDeflated){
|
||||
if (isDeflationPossible || file.length() > INFLATE_MAX_SIZE){
|
||||
response.addHeader("Content-Encoding", "gzip");
|
||||
response.setData(new FileInputStream(file));
|
||||
return response;
|
||||
} else {
|
||||
response.setData(new GZIPInputStream(new FileInputStream(file)));
|
||||
return response;
|
||||
}
|
||||
} else {
|
||||
if (isDeflationPossible && file.length() > DEFLATE_MIN_SIZE && file.length() < DEFLATE_MAX_SIZE){
|
||||
FileInputStream fis = new FileInputStream(file);
|
||||
ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
|
||||
GZIPOutputStream zos = new GZIPOutputStream(byteOut);
|
||||
IOUtils.copyLarge(fis, zos);
|
||||
zos.close();
|
||||
fis.close();
|
||||
byte[] compressedData = byteOut.toByteArray();
|
||||
response.setData(new ByteArrayInputStream(compressedData));
|
||||
response.addHeader("Content-Encoding", "gzip");
|
||||
return response;
|
||||
} else {
|
||||
response.setData(new FileInputStream(file));
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
} catch (FileNotFoundException e) {
|
||||
return new HttpResponse(HttpStatusCode.NOT_FOUND);
|
||||
} catch (IOException e) {
|
||||
return new HttpResponse(HttpStatusCode.INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
private static String timestampToString(long time){
|
||||
return DateFormatUtils.format(time, "EEE, dd MMM yyy HH:mm:ss 'GMT'", TimeZone.getTimeZone("GMT"), Locale.ENGLISH);
|
||||
}
|
||||
|
||||
private static long stringToTimestamp(String timeString) throws IllegalArgumentException {
|
||||
try {
|
||||
int day = Integer.parseInt(timeString.substring(5, 7));
|
||||
int month = 1;
|
||||
switch (timeString.substring(8, 11)){
|
||||
case "Jan" : month = 0; break;
|
||||
case "Feb" : month = 1; break;
|
||||
case "Mar" : month = 2; break;
|
||||
case "Apr" : month = 3; break;
|
||||
case "May" : month = 4; break;
|
||||
case "Jun" : month = 5; break;
|
||||
case "Jul" : month = 6; break;
|
||||
case "Aug" : month = 7; break;
|
||||
case "Sep" : month = 8; break;
|
||||
case "Oct" : month = 9; break;
|
||||
case "Nov" : month = 10; break;
|
||||
case "Dec" : month = 11; break;
|
||||
}
|
||||
int year = Integer.parseInt(timeString.substring(12, 16));
|
||||
int hour = Integer.parseInt(timeString.substring(17, 19));
|
||||
int min = Integer.parseInt(timeString.substring(20, 22));
|
||||
int sec = Integer.parseInt(timeString.substring(23, 25));
|
||||
GregorianCalendar cal = new GregorianCalendar(TimeZone.getTimeZone("GMT"));
|
||||
cal.set(year, month, day, hour, min, sec);
|
||||
return cal.getTimeInMillis();
|
||||
} catch (NumberFormatException | IndexOutOfBoundsException e){
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.web;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import de.bluecolored.bluemap.core.logger.Logger;
|
||||
import de.bluecolored.bluemap.core.webserver.WebServer;
|
||||
|
||||
public class BlueMapWebServer extends WebServer {
|
||||
|
||||
private WebFilesManager webFilesManager;
|
||||
|
||||
public BlueMapWebServer(WebServerConfig config) {
|
||||
super(
|
||||
config.getWebserverPort(),
|
||||
config.getWebserverMaxConnections(),
|
||||
config.getWebserverBindAdress(),
|
||||
new BlueMapWebRequestHandler(config.getWebRoot())
|
||||
);
|
||||
|
||||
this.webFilesManager = new WebFilesManager(config.getWebRoot());
|
||||
}
|
||||
|
||||
public void updateWebfiles() throws IOException {
|
||||
if (webFilesManager.needsUpdate()) {
|
||||
Logger.global.logInfo("Webfiles are missing or outdated, updating...");
|
||||
webFilesManager.updateFiles();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.web;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Enumeration;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
|
||||
public class WebFilesManager {
|
||||
|
||||
private Path webRoot;
|
||||
|
||||
public WebFilesManager(Path webRoot) {
|
||||
this.webRoot = webRoot;
|
||||
}
|
||||
|
||||
public boolean needsUpdate() {
|
||||
if (!webRoot.resolve("index.html").toFile().exists()) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void updateFiles() throws IOException {
|
||||
URL fileResource = getClass().getResource("/webroot.zip");
|
||||
File tempFile = File.createTempFile("bluemap_webroot_extraction", null);
|
||||
|
||||
try {
|
||||
FileUtils.copyURLToFile(fileResource, tempFile, 10000, 10000);
|
||||
try (ZipFile zipFile = new ZipFile(tempFile)){
|
||||
Enumeration<? extends ZipEntry> entries = zipFile.entries();
|
||||
while(entries.hasMoreElements()) {
|
||||
ZipEntry zipEntry = entries.nextElement();
|
||||
if (zipEntry.isDirectory()) webRoot.resolve(zipEntry.getName()).toFile().mkdirs();
|
||||
else {
|
||||
File target = webRoot.resolve(zipEntry.getName()).toFile();
|
||||
FileUtils.copyInputStreamToFile(zipFile.getInputStream(zipEntry), target);
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
tempFile.delete();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.web;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.nio.file.Path;
|
||||
|
||||
public interface WebServerConfig {
|
||||
|
||||
Path getWebRoot();
|
||||
|
||||
InetAddress getWebserverBindAdress();
|
||||
|
||||
int getWebserverPort();
|
||||
|
||||
int getWebserverMaxConnections();
|
||||
|
||||
}
|
@ -0,0 +1,132 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.web;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import com.flowpowered.math.vector.Vector2i;
|
||||
|
||||
import de.bluecolored.bluemap.core.render.TileRenderer;
|
||||
import ninja.leaping.configurate.ConfigurationNode;
|
||||
import ninja.leaping.configurate.gson.GsonConfigurationLoader;
|
||||
import ninja.leaping.configurate.loader.ConfigurationLoader;
|
||||
|
||||
public class WebSettings {
|
||||
|
||||
private ConfigurationLoader<? extends ConfigurationNode> configLoader;
|
||||
private ConfigurationNode rootNode;
|
||||
|
||||
public WebSettings(File settingsFile) throws IOException {
|
||||
|
||||
if (!settingsFile.exists()) {
|
||||
settingsFile.getParentFile().mkdirs();
|
||||
settingsFile.createNewFile();
|
||||
}
|
||||
|
||||
configLoader = GsonConfigurationLoader.builder()
|
||||
.setFile(settingsFile)
|
||||
.build();
|
||||
|
||||
load();
|
||||
}
|
||||
|
||||
public void load() throws IOException {
|
||||
rootNode = configLoader.load();
|
||||
}
|
||||
|
||||
public void save() throws IOException {
|
||||
configLoader.save(rootNode);
|
||||
}
|
||||
|
||||
public void set(Object value, Object... path) {
|
||||
rootNode.getNode(path).setValue(value);
|
||||
}
|
||||
|
||||
public Object get(Object... path) {
|
||||
return rootNode.getNode(path).getValue();
|
||||
}
|
||||
|
||||
public String getString(Object... path) {
|
||||
return rootNode.getNode(path).getString();
|
||||
}
|
||||
|
||||
public long getInt(Object... path) {
|
||||
return rootNode.getNode(path).getInt();
|
||||
}
|
||||
|
||||
public long getLong(Object... path) {
|
||||
return rootNode.getNode(path).getLong();
|
||||
}
|
||||
|
||||
public double getFloat(Object... path) {
|
||||
return rootNode.getNode(path).getFloat();
|
||||
}
|
||||
|
||||
public double getDouble(Object... path) {
|
||||
return rootNode.getNode(path).getDouble();
|
||||
}
|
||||
|
||||
public void setFrom(TileRenderer tileRenderer, String mapId) {
|
||||
Vector2i hiresTileSize = tileRenderer.getHiresModelManager().getTileSize();
|
||||
Vector2i gridOrigin = tileRenderer.getHiresModelManager().getGridOrigin();
|
||||
Vector2i lowresTileSize = tileRenderer.getLowresModelManager().getTileSize();
|
||||
Vector2i lowresPointsPerHiresTile = tileRenderer.getLowresModelManager().getPointsPerHiresTile();
|
||||
|
||||
set(hiresTileSize.getX(), mapId, "hires", "tileSize", "x");
|
||||
set(hiresTileSize.getY(), mapId, "hires", "tileSize", "z");
|
||||
set(1, mapId, "hires", "scale", "x");
|
||||
set(1, mapId, "hires", "scale", "z");
|
||||
set(gridOrigin.getX(), mapId, "hires", "translate", "x");
|
||||
set(gridOrigin.getY(), mapId, "hires", "translate", "z");
|
||||
|
||||
Vector2i pointSize = hiresTileSize.div(lowresPointsPerHiresTile);
|
||||
Vector2i tileSize = pointSize.mul(lowresTileSize);
|
||||
|
||||
set(tileSize.getX(), mapId, "lowres", "tileSize", "x");
|
||||
set(tileSize.getY(), mapId, "lowres", "tileSize", "z");
|
||||
set(pointSize.getX(), mapId, "lowres", "scale", "x");
|
||||
set(pointSize.getY(), mapId, "lowres", "scale", "z");
|
||||
set(pointSize.getX() / 2, mapId, "lowres", "translate", "x");
|
||||
set(pointSize.getY() / 2, mapId, "lowres", "translate", "z");
|
||||
}
|
||||
|
||||
public void setHiresViewDistance(float hiresViewDistance, String mapId) {
|
||||
set(hiresViewDistance, mapId, "hires", "viewDistance");
|
||||
}
|
||||
|
||||
public void setLowresViewDistance(float lowresViewDistance, String mapId) {
|
||||
set(lowresViewDistance, mapId, "lowres", "viewDistance");
|
||||
}
|
||||
|
||||
public void setName(String name, String mapId) {
|
||||
set(name, mapId, "name");
|
||||
}
|
||||
|
||||
public String getName(String mapId) {
|
||||
return getString(mapId, "name");
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,126 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.webserver;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketException;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import de.bluecolored.bluemap.core.logger.Logger;
|
||||
|
||||
public class HttpConnection implements Runnable {
|
||||
|
||||
private HttpRequestHandler handler;
|
||||
|
||||
private ServerSocket server;
|
||||
private Socket connection;
|
||||
private InputStream in;
|
||||
private OutputStream out;
|
||||
|
||||
public HttpConnection(ServerSocket server, Socket connection, HttpRequestHandler handler, int timeout, TimeUnit timeoutUnit) throws IOException {
|
||||
this.server = server;
|
||||
this.connection = connection;
|
||||
this.handler = handler;
|
||||
|
||||
if (isClosed()){
|
||||
throw new IOException("Socket already closed!");
|
||||
}
|
||||
|
||||
connection.setSoTimeout((int) timeoutUnit.toMillis(timeout));
|
||||
|
||||
in = this.connection.getInputStream();
|
||||
out = this.connection.getOutputStream();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
while (!isClosed() && !server.isClosed()){
|
||||
try {
|
||||
HttpRequest request = acceptRequest();
|
||||
HttpResponse response = handler.handle(request);
|
||||
sendResponse(response);
|
||||
} catch (InvalidRequestException e){
|
||||
try {
|
||||
sendResponse(new HttpResponse(HttpStatusCode.BAD_REQUEST));
|
||||
} catch (IOException e1) {}
|
||||
break;
|
||||
} catch (SocketTimeoutException e) {
|
||||
break;
|
||||
} catch (SocketException e){
|
||||
break;
|
||||
} catch (ConnectionClosedException e){
|
||||
break;
|
||||
} catch (IOException e) {
|
||||
Logger.global.logError("Unexpected error while processing a HttpRequest!", e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
close();
|
||||
} catch (IOException e){
|
||||
Logger.global.logError("Error while closing HttpConnection!", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void sendResponse(HttpResponse response) throws IOException {
|
||||
response.write(out);
|
||||
out.flush();
|
||||
}
|
||||
|
||||
private HttpRequest acceptRequest() throws ConnectionClosedException, InvalidRequestException, IOException {
|
||||
return HttpRequest.read(in);
|
||||
}
|
||||
|
||||
public boolean isClosed(){
|
||||
return !connection.isBound() || connection.isClosed() || !connection.isConnected() || connection.isOutputShutdown() || connection.isInputShutdown();
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
try {
|
||||
in.close();
|
||||
} finally {
|
||||
try {
|
||||
out.close();
|
||||
} finally {
|
||||
connection.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class ConnectionClosedException extends IOException {
|
||||
private static final long serialVersionUID = 1L;
|
||||
}
|
||||
|
||||
public static class InvalidRequestException extends IOException {
|
||||
private static final long serialVersionUID = 1L;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,206 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.webserver;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import de.bluecolored.bluemap.core.webserver.HttpConnection.ConnectionClosedException;
|
||||
import de.bluecolored.bluemap.core.webserver.HttpConnection.InvalidRequestException;
|
||||
|
||||
public class HttpRequest {
|
||||
|
||||
private static final Pattern REQUEST_PATTERN = Pattern.compile("^(\\w+) (\\S+) (.+)$");
|
||||
|
||||
private String method;
|
||||
private String path;
|
||||
private String version;
|
||||
private Map<String, Set<String>> header;
|
||||
private Map<String, Set<String>> headerLC;
|
||||
private byte[] data;
|
||||
|
||||
public HttpRequest(String method, String path, String version, Map<String, Set<String>> header) {
|
||||
this.method = method;
|
||||
this.path = path;
|
||||
this.version = version;
|
||||
this.header = header;
|
||||
this.headerLC = new HashMap<>();
|
||||
|
||||
for (Entry<String, Set<String>> e : header.entrySet()){
|
||||
Set<String> values = new HashSet<>();
|
||||
for (String v : e.getValue()){
|
||||
values.add(v.toLowerCase());
|
||||
}
|
||||
|
||||
headerLC.put(e.getKey().toLowerCase(), values);
|
||||
}
|
||||
|
||||
this.data = new byte[0];
|
||||
}
|
||||
|
||||
public String getMethod() {
|
||||
return method;
|
||||
}
|
||||
|
||||
public String getPath(){
|
||||
return path;
|
||||
}
|
||||
|
||||
public String getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public Map<String, Set<String>> getHeader() {
|
||||
return header;
|
||||
}
|
||||
|
||||
public Map<String, Set<String>> getLowercaseHeader() {
|
||||
return header;
|
||||
}
|
||||
|
||||
public Set<String> getHeader(String key){
|
||||
Set<String> headerValues = header.get(key);
|
||||
if (headerValues == null) return Collections.emptySet();
|
||||
return headerValues;
|
||||
}
|
||||
|
||||
public Set<String> getLowercaseHeader(String key){
|
||||
Set<String> headerValues = headerLC.get(key.toLowerCase());
|
||||
if (headerValues == null) return Collections.emptySet();
|
||||
return headerValues;
|
||||
}
|
||||
|
||||
public InputStream getData(){
|
||||
return new ByteArrayInputStream(data);
|
||||
}
|
||||
|
||||
public static HttpRequest read(InputStream in) throws IOException, InvalidRequestException {
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8));
|
||||
List<String> header = new ArrayList<>(20);
|
||||
while(header.size() < 1000){
|
||||
String headerLine = readLine(reader);
|
||||
if (headerLine.isEmpty()) break;
|
||||
header.add(headerLine);
|
||||
}
|
||||
|
||||
if (header.isEmpty()) throw new InvalidRequestException();
|
||||
|
||||
Matcher m = REQUEST_PATTERN.matcher(header.remove(0));
|
||||
if (!m.find()) throw new InvalidRequestException();
|
||||
|
||||
String method = m.group(1);
|
||||
if (method == null) throw new InvalidRequestException();
|
||||
|
||||
String adress = m.group(2);
|
||||
if (adress == null) throw new InvalidRequestException();
|
||||
|
||||
String version = m.group(3);
|
||||
if (version == null) throw new InvalidRequestException();
|
||||
|
||||
Map<String, Set<String>> headerMap = new HashMap<String, Set<String>>();
|
||||
for (String line : header){
|
||||
if (line.trim().isEmpty()) continue;
|
||||
|
||||
String[] kv = line.split(":", 2);
|
||||
if (kv.length < 2) continue;
|
||||
|
||||
Set<String> values = new HashSet<>();
|
||||
if (kv[0].trim().equalsIgnoreCase("If-Modified-Since")){
|
||||
values.add(kv[1].trim());
|
||||
} else {
|
||||
for(String v : kv[1].split(",")){
|
||||
values.add(v.trim());
|
||||
}
|
||||
}
|
||||
|
||||
headerMap.put(kv[0].trim(), values);
|
||||
}
|
||||
|
||||
HttpRequest request = new HttpRequest(method, adress, version, headerMap);
|
||||
|
||||
if (request.getLowercaseHeader("Transfer-Encoding").contains("chunked")){
|
||||
try {
|
||||
ByteArrayOutputStream dataStream = new ByteArrayOutputStream();
|
||||
while (dataStream.size() < 1000000){
|
||||
String hexSize = reader.readLine();
|
||||
int chunkSize = Integer.parseInt(hexSize, 16);
|
||||
if (chunkSize <= 0) break;
|
||||
byte[] data = new byte[chunkSize];
|
||||
in.read(data);
|
||||
dataStream.write(data);
|
||||
}
|
||||
|
||||
if (dataStream.size() >= 1000000) {
|
||||
throw new InvalidRequestException();
|
||||
}
|
||||
|
||||
request.data = dataStream.toByteArray();
|
||||
|
||||
return request;
|
||||
} catch (NumberFormatException ex){
|
||||
return request;
|
||||
}
|
||||
} else {
|
||||
Set<String> clSet = request.getLowercaseHeader("Content-Length");
|
||||
if (clSet.isEmpty()){
|
||||
return request;
|
||||
} else {
|
||||
try {
|
||||
int cl = Integer.parseInt(clSet.iterator().next());
|
||||
byte[] data = new byte[cl];
|
||||
in.read(data);
|
||||
request.data = data;
|
||||
return request;
|
||||
} catch (NumberFormatException ex){
|
||||
return request;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static String readLine(BufferedReader in) throws ConnectionClosedException, IOException {
|
||||
String line = in.readLine();
|
||||
if (line == null){
|
||||
throw new ConnectionClosedException();
|
||||
}
|
||||
return line;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.webserver;
|
||||
|
||||
public interface HttpRequestHandler {
|
||||
|
||||
HttpResponse handle(HttpRequest request);
|
||||
|
||||
}
|
@ -0,0 +1,149 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.webserver;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
public class HttpResponse {
|
||||
|
||||
private String version;
|
||||
private HttpStatusCode statusCode;
|
||||
private Map<String, Set<String>> header;
|
||||
private InputStream data;
|
||||
|
||||
public HttpResponse(HttpStatusCode statusCode) {
|
||||
this.version = "HTTP/1.1";
|
||||
this.statusCode = statusCode;
|
||||
|
||||
this.header = new HashMap<>();
|
||||
|
||||
addHeader("Connection", "keep-alive");
|
||||
}
|
||||
|
||||
public void addHeader(String key, String value){
|
||||
Set<String> valueSet = header.get(key);
|
||||
if (valueSet == null){
|
||||
valueSet = new HashSet<>();
|
||||
header.put(key, valueSet);
|
||||
}
|
||||
|
||||
valueSet.add(value);
|
||||
}
|
||||
|
||||
public void removeHeader(String key, String value){
|
||||
Set<String> valueSet = header.get(key);
|
||||
if (valueSet == null){
|
||||
valueSet = new HashSet<>();
|
||||
header.put(key, valueSet);
|
||||
}
|
||||
|
||||
valueSet.remove(value);
|
||||
}
|
||||
|
||||
public void setData(InputStream dataStream){
|
||||
this.data = dataStream;
|
||||
}
|
||||
|
||||
public void setData(String data){
|
||||
setData(new ByteArrayInputStream(data.getBytes(StandardCharsets.UTF_8)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes this Response to an Output-Stream.<br>
|
||||
* <br>
|
||||
* This method closes the data-Stream of this response so it can't be used again!
|
||||
*/
|
||||
public void write(OutputStream out) throws IOException {
|
||||
OutputStreamWriter writer = new OutputStreamWriter(out, StandardCharsets.UTF_8);
|
||||
|
||||
if (data != null){
|
||||
addHeader("Transfer-Encoding", "chunked");
|
||||
} else {
|
||||
addHeader("Content-Length", "0");
|
||||
}
|
||||
|
||||
writeLine(writer, version + " " + statusCode.getCode() + " " + statusCode.getMessage());
|
||||
for (Entry<String, Set<String>> e : header.entrySet()){
|
||||
if (e.getValue().isEmpty()) continue;
|
||||
writeLine(writer, e.getKey() + ": " + StringUtils.join(e.getValue(), ", "));
|
||||
}
|
||||
|
||||
writeLine(writer, "");
|
||||
writer.flush();
|
||||
|
||||
if(data != null){
|
||||
chunkedPipe(data, out);
|
||||
out.flush();
|
||||
data.close();
|
||||
}
|
||||
}
|
||||
|
||||
private void writeLine(OutputStreamWriter writer, String line) throws IOException {
|
||||
writer.write(line + "\r\n");
|
||||
}
|
||||
|
||||
private void chunkedPipe(InputStream input, OutputStream output) throws IOException {
|
||||
byte[] buffer = new byte[1024];
|
||||
int byteCount;
|
||||
while ((byteCount = input.read(buffer)) != -1) {
|
||||
output.write((Integer.toHexString(byteCount) + "\r\n").getBytes());
|
||||
output.write(buffer, 0, byteCount);
|
||||
output.write("\r\n".getBytes());
|
||||
}
|
||||
output.write("0\r\n\r\n".getBytes());
|
||||
}
|
||||
|
||||
public HttpStatusCode getStatusCode(){
|
||||
return statusCode;
|
||||
}
|
||||
|
||||
public String getVersion(){
|
||||
return version;
|
||||
}
|
||||
|
||||
public Map<String, Set<String>> getHeader() {
|
||||
return header;
|
||||
}
|
||||
|
||||
public Set<String> getHeader(String key){
|
||||
Set<String> headerValues = header.get(key);
|
||||
if (headerValues == null) return Collections.emptySet();
|
||||
return headerValues;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.webserver;
|
||||
|
||||
public enum HttpStatusCode {
|
||||
|
||||
CONTINUE (100, "Continue"),
|
||||
PROCESSING (102, "Processing"),
|
||||
|
||||
OK (200, "OK"),
|
||||
|
||||
MOVED_PERMANENTLY (301, "Moved Permanently"),
|
||||
FOUND (302, "Found"),
|
||||
SEE_OTHER (303, "See Other"),
|
||||
NOT_MODIFIED (304, "Not Modified"),
|
||||
|
||||
BAD_REQUEST (400, "Bad Request"),
|
||||
UNAUTHORIZED (401, "Unauthorized"),
|
||||
FORBIDDEN (403, "Forbidden"),
|
||||
NOT_FOUND (404, "Not Found"),
|
||||
|
||||
INTERNAL_SERVER_ERROR (500, "Internal Server Error"),
|
||||
NOT_IMPLEMENTED (501, "Not Implemented"),
|
||||
SERVICE_UNAVAILABLE (503, "Service Unavailable"),
|
||||
HTTP_VERSION_NOT_SUPPORTED (505, "HTTP Version not supported");
|
||||
|
||||
private int code;
|
||||
private String message;
|
||||
|
||||
private HttpStatusCode(int code, String message) {
|
||||
this.code = code;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public int getCode(){
|
||||
return code;
|
||||
}
|
||||
|
||||
public String getMessage(){
|
||||
return message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getCode() + " " + getMessage();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,113 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.webserver;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketException;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import de.bluecolored.bluemap.core.logger.Logger;
|
||||
|
||||
public class WebServer extends Thread {
|
||||
|
||||
private final int port;
|
||||
private final int maxConnections;
|
||||
private final InetAddress bindAdress;
|
||||
|
||||
private HttpRequestHandler handler;
|
||||
|
||||
private ThreadPoolExecutor connectionThreads;
|
||||
|
||||
private ServerSocket server;
|
||||
|
||||
public WebServer(int port, int maxConnections, InetAddress bindAdress, HttpRequestHandler handler) {
|
||||
this.port = port;
|
||||
this.maxConnections = maxConnections;
|
||||
this.bindAdress = bindAdress;
|
||||
|
||||
this.handler = handler;
|
||||
|
||||
connectionThreads = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(){
|
||||
close();
|
||||
|
||||
connectionThreads = new ThreadPoolExecutor(maxConnections, maxConnections, 10, TimeUnit.SECONDS, new LinkedBlockingQueue<>());
|
||||
connectionThreads.allowCoreThreadTimeOut(true);
|
||||
|
||||
try {
|
||||
server = new ServerSocket(port, maxConnections, bindAdress);
|
||||
server.setSoTimeout(0);
|
||||
} catch (IOException e){
|
||||
Logger.global.logError("Error while starting the WebServer!", e);
|
||||
return;
|
||||
}
|
||||
|
||||
Logger.global.logInfo("WebServer started.");
|
||||
|
||||
while (!server.isClosed() && server.isBound()){
|
||||
|
||||
try {
|
||||
Socket connection = server.accept();
|
||||
|
||||
try {
|
||||
connectionThreads.execute(new HttpConnection(server, connection, handler, 10, TimeUnit.SECONDS));
|
||||
} catch (RejectedExecutionException e){
|
||||
connection.close();
|
||||
Logger.global.logWarning("Dropped an incoming HttpConnection! (Too many connections?)");
|
||||
}
|
||||
|
||||
} catch (SocketException e){
|
||||
// this mainly occurs if the socket got closed, so we ignore this error
|
||||
} catch (IOException e){
|
||||
Logger.global.logError("Error while creating a new HttpConnection!", e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Logger.global.logInfo("WebServer closed.");
|
||||
}
|
||||
|
||||
public void close(){
|
||||
if (connectionThreads != null) connectionThreads.shutdown();
|
||||
|
||||
try {
|
||||
if (server != null && !server.isClosed()){
|
||||
server.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Logger.global.logError("Error while closing WebServer!", e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,91 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.world;
|
||||
|
||||
import com.flowpowered.math.vector.Vector3i;
|
||||
|
||||
import de.bluecolored.bluemap.core.render.context.BlockContext;
|
||||
import de.bluecolored.bluemap.core.util.Direction;
|
||||
|
||||
public abstract class Block {
|
||||
|
||||
private float sunLight;
|
||||
private float blockLight;
|
||||
|
||||
public Block() {
|
||||
sunLight = -1;
|
||||
blockLight = -1;
|
||||
}
|
||||
|
||||
public abstract BlockState getBlock();
|
||||
|
||||
public abstract World getWorld();
|
||||
|
||||
public abstract Vector3i getPosition();
|
||||
|
||||
public abstract double getSunLightLevel();
|
||||
|
||||
public abstract double getBlockLightLevel();
|
||||
|
||||
public abstract boolean isCullingNeighborFaces();
|
||||
|
||||
public boolean isOccludingNeighborFaces(){
|
||||
return isCullingNeighborFaces();
|
||||
}
|
||||
|
||||
public abstract String getBiome();
|
||||
|
||||
/**
|
||||
* This is internally used for light rendering
|
||||
* It is basically the sun light that is projected onto adjacent faces
|
||||
*/
|
||||
public float getPassedSunLight(BlockContext context) {
|
||||
if (sunLight < 0) calculateLight(context);
|
||||
return sunLight;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is internally used for light rendering
|
||||
* It is basically the block light that is projected onto adjacent faces
|
||||
*/
|
||||
public float getPassedBlockLight(BlockContext context) {
|
||||
if (blockLight < 0) calculateLight(context);
|
||||
return blockLight;
|
||||
}
|
||||
|
||||
private void calculateLight(BlockContext context) {
|
||||
sunLight = (float) getSunLightLevel();
|
||||
blockLight = (float) getBlockLightLevel();
|
||||
|
||||
if (blockLight > 0 || sunLight > 0) return;
|
||||
|
||||
for (Direction direction : Direction.values()) {
|
||||
Block neighbor = context.getRelativeBlock(direction);
|
||||
sunLight = (float) Math.max(neighbor.getSunLightLevel(), sunLight);
|
||||
blockLight = (float) Math.max(neighbor.getBlockLightLevel(), blockLight);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,199 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.world;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.StringJoiner;
|
||||
|
||||
/**
|
||||
* Represents a BlockState<br>
|
||||
* It is important that {@link #hashCode} and {@link #equals} are implemented correctly, for the caching to work properly.<br>
|
||||
* <br>
|
||||
* <i>The implementation of this class has to be thread-save!</i><br>
|
||||
*/
|
||||
public class BlockState {
|
||||
|
||||
private static Pattern BLOCKSTATE_SERIALIZATION_PATTERN = Pattern.compile("^(.+?)(?:\\[(.+)\\])?$");
|
||||
|
||||
public static final BlockState AIR = new BlockState("minecraft:air", Collections.emptyMap());
|
||||
|
||||
private boolean hashed;
|
||||
private int hash;
|
||||
|
||||
private final String namespace;
|
||||
private final String id;
|
||||
private final Map<String, String> properties;
|
||||
|
||||
public BlockState(String id) {
|
||||
this(id, Collections.emptyMap());
|
||||
}
|
||||
|
||||
public BlockState(String id, Map<String, String> properties) {
|
||||
this.hashed = false;
|
||||
this.hash = 0;
|
||||
|
||||
this.properties = Collections.unmodifiableMap(new HashMap<>(properties));
|
||||
|
||||
//resolve namespace
|
||||
String namespace = "minecraft";
|
||||
int namespaceSeperator = id.indexOf(':');
|
||||
if (namespaceSeperator > 0) {
|
||||
namespace = id.substring(0, namespaceSeperator);
|
||||
id = id.substring(namespaceSeperator + 1);
|
||||
}
|
||||
|
||||
this.id = id;
|
||||
this.namespace = namespace;
|
||||
}
|
||||
|
||||
private BlockState(BlockState blockState, String withKey, String withValue) {
|
||||
this.hashed = false;
|
||||
this.hash = 0;
|
||||
|
||||
Map<String, String> props = new HashMap<>(blockState.getProperties());
|
||||
props.put(withKey, withValue);
|
||||
|
||||
this.id = blockState.getId();
|
||||
this.namespace = blockState.getNamespace();
|
||||
this.properties = Collections.unmodifiableMap(props);
|
||||
}
|
||||
|
||||
/**
|
||||
* The namespace of this blockstate,<br>
|
||||
* this is always "minecraft" in vanilla.<br>
|
||||
*/
|
||||
public String getNamespace() {
|
||||
return namespace;
|
||||
}
|
||||
|
||||
/**
|
||||
* The id of this blockstate,<br>
|
||||
* also the name of the resource-file without the filetype that represents this block-state <i>(found in mineceraft in assets/minecraft/blockstates)</i>.<br>
|
||||
*/
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the namespaced id of this blockstate
|
||||
*/
|
||||
public String getFullId() {
|
||||
return getNamespace() + ":" + getId();
|
||||
}
|
||||
|
||||
/**
|
||||
* An immutable map of all properties of this block.<br>
|
||||
* <br>
|
||||
* For Example:<br>
|
||||
* <code>
|
||||
* facing = east<br>
|
||||
* half = bottom<br>
|
||||
* </code>
|
||||
*/
|
||||
public Map<String, String> getProperties() {
|
||||
return properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new BlockState with the given property changed
|
||||
*/
|
||||
public BlockState with(String property, String value) {
|
||||
return new BlockState(this, property, value);
|
||||
}
|
||||
|
||||
public final boolean checkVariantCondition(String condition){
|
||||
if (condition.isEmpty() || condition.equals("normal")) return true;
|
||||
|
||||
Map<String, String> blockProperties = getProperties();
|
||||
String[] conditions = condition.split(",");
|
||||
for (String c : conditions){
|
||||
String[] kv = c.split("=", 2);
|
||||
String key = kv[0];
|
||||
String value = kv[1];
|
||||
|
||||
if (!value.equals(blockProperties.get(key))){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (!(obj instanceof BlockState)) return false;
|
||||
BlockState b = (BlockState) obj;
|
||||
if (!Objects.equals(getId(), b.getId())) return false;
|
||||
if (!Objects.equals(getProperties(), b.getProperties())) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
if (!hashed){
|
||||
hash = Objects.hash( getId(), getProperties() );
|
||||
hashed = true;
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringJoiner sj = new StringJoiner(",");
|
||||
for (Entry<String, String> e : getProperties().entrySet()){
|
||||
sj.add(e.getKey() + "=" + e.getValue());
|
||||
}
|
||||
|
||||
return getId() + "[" + sj.toString() + "]";
|
||||
}
|
||||
|
||||
public static BlockState fromString(String serializedBlockState) {
|
||||
Matcher m = BLOCKSTATE_SERIALIZATION_PATTERN.matcher(serializedBlockState);
|
||||
m.find();
|
||||
|
||||
Map<String, String> pt = new HashMap<>();
|
||||
String g2 = m.group(2);
|
||||
if (g2 != null){
|
||||
String[] propertyStrings = g2.trim().split(",");
|
||||
for (String s : propertyStrings){
|
||||
String[] kv = s.split("=", 2);
|
||||
pt.put(kv[0], kv[1]);
|
||||
}
|
||||
}
|
||||
|
||||
String blockId = m.group(1).trim();
|
||||
|
||||
return new BlockState(blockId, pt);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,130 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.world;
|
||||
|
||||
import com.flowpowered.math.vector.Vector3i;
|
||||
|
||||
/**
|
||||
* This class wraps another Block to cache all getters.<br>
|
||||
* The implementation <b>can</b> use this to make sure all underlying getters are only called once and cached-data is used on the second call.
|
||||
*/
|
||||
public class CachedBlock extends Block {
|
||||
|
||||
private Block block;
|
||||
|
||||
private BlockState state;
|
||||
private World world;
|
||||
private Vector3i position;
|
||||
private double sunLight, blockLight;
|
||||
private String biome;
|
||||
|
||||
private boolean isCullingCached;
|
||||
private boolean isCulling;
|
||||
|
||||
private boolean isOccludingCached;
|
||||
private boolean isOccluding;
|
||||
|
||||
private CachedBlock(Block block) {
|
||||
this.block = block;
|
||||
|
||||
this.state = null;
|
||||
this.world = null;
|
||||
this.position = null;
|
||||
this.sunLight = -1;
|
||||
this.blockLight = -1;
|
||||
|
||||
this.isCullingCached = false;
|
||||
this.isCulling = false;
|
||||
|
||||
this.isOccludingCached = false;
|
||||
this.isOccluding = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockState getBlock() {
|
||||
if (state == null) state = block.getBlock();
|
||||
return state;
|
||||
}
|
||||
|
||||
@Override
|
||||
public World getWorld() {
|
||||
if (world == null) world = block.getWorld();
|
||||
return world;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector3i getPosition() {
|
||||
if (position == null) position = block.getPosition();
|
||||
return position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getSunLightLevel() {
|
||||
if (sunLight == -1) sunLight = block.getSunLightLevel();
|
||||
return sunLight;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getBlockLightLevel() {
|
||||
if (blockLight == -1) blockLight = block.getBlockLightLevel();
|
||||
return blockLight;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCullingNeighborFaces() {
|
||||
if (!isCullingCached){
|
||||
isCulling = block.isCullingNeighborFaces();
|
||||
isCullingCached = true;
|
||||
}
|
||||
|
||||
return isCulling;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOccludingNeighborFaces() {
|
||||
if (!isOccludingCached){
|
||||
isOccluding = block.isOccludingNeighborFaces();
|
||||
isOccludingCached = true;
|
||||
}
|
||||
|
||||
return isOccluding;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBiome() {
|
||||
if (biome == null){
|
||||
biome = block.getBiome();
|
||||
}
|
||||
|
||||
return biome;
|
||||
}
|
||||
|
||||
public static CachedBlock of(Block block){
|
||||
if (block instanceof CachedBlock) return (CachedBlock) block;
|
||||
|
||||
return new CachedBlock(block);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.world;
|
||||
|
||||
public class ChunkNotGeneratedException extends Exception {
|
||||
private static final long serialVersionUID = 0L;
|
||||
|
||||
public ChunkNotGeneratedException() {}
|
||||
|
||||
public ChunkNotGeneratedException(Throwable e) {
|
||||
super(e);
|
||||
}
|
||||
|
||||
public ChunkNotGeneratedException(String message){
|
||||
super(message);
|
||||
}
|
||||
|
||||
public ChunkNotGeneratedException(String message, Throwable e) {
|
||||
super(message, e);
|
||||
}
|
||||
}
|
@ -0,0 +1,78 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.world;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.UUID;
|
||||
|
||||
import com.flowpowered.math.vector.Vector2i;
|
||||
import com.flowpowered.math.vector.Vector3i;
|
||||
|
||||
/**
|
||||
* Represents a World on the Server<br>
|
||||
* <br>
|
||||
* <i>The implementation of this class has to be thread-save!</i><br>
|
||||
*/
|
||||
public interface World extends WorldChunk {
|
||||
|
||||
String getName();
|
||||
|
||||
UUID getUUID();
|
||||
|
||||
int getSeaLevel();
|
||||
|
||||
Vector3i getSpawnPoint();
|
||||
|
||||
/**
|
||||
* Returns itself
|
||||
*/
|
||||
@Override
|
||||
default World getWorld() {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Always returns false
|
||||
*/
|
||||
@Override
|
||||
default boolean isGenerated() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a collection of all generated chunks.<br>
|
||||
* <i>(Be aware that the collection is not cached and recollected each time from the world-files!)</i>
|
||||
*/
|
||||
public default Collection<Vector2i> getChunkList(){
|
||||
return getChunkList(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a collection of all chunks that have been modified at or after the specified timestamp.<br>
|
||||
* <i>(Be aware that the collection is not cached and recollected each time from the world-files!)</i>
|
||||
*/
|
||||
public Collection<Vector2i> getChunkList(long modifiedSince);
|
||||
|
||||
}
|
@ -0,0 +1,85 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.world;
|
||||
|
||||
import com.flowpowered.math.vector.Vector3i;
|
||||
|
||||
import de.bluecolored.bluemap.core.util.AABB;
|
||||
|
||||
/**
|
||||
* Represents a chunk of a world.<br>
|
||||
* <br>
|
||||
* <i>The implementation of this class has to be thread-save!</i><br>
|
||||
*/
|
||||
public interface WorldChunk {
|
||||
|
||||
/**
|
||||
* Returns the top-level World of this WorldChunk,
|
||||
* If this WorldChunk is already a World, the method returns the same instance (<code>return this;</code>).
|
||||
*/
|
||||
World getWorld();
|
||||
|
||||
/**
|
||||
* Returns the Block on the specified position.<br>
|
||||
* <br>
|
||||
* <i>(The implementation should not invoke the generation of new Terrain, it should rather throw a {@link ChunkNotGeneratedException} if a not generated block is requested)</i><br>
|
||||
*/
|
||||
Block getBlock(Vector3i pos) throws ChunkNotGeneratedException;
|
||||
|
||||
/**
|
||||
* Returns the Block on the specified position.<br>
|
||||
* <br>
|
||||
* <i>(The implementation should not invoke the generation of new Terrain, it should rather throw a {@link ChunkNotGeneratedException} if a not generated block is requested)</i><br>
|
||||
*/
|
||||
default Block getBlock(int x, int y, int z) throws ChunkNotGeneratedException {
|
||||
return getBlock(new Vector3i(x, y, z));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this WorldChunk contains the given position.
|
||||
*/
|
||||
default boolean containsBlock(Vector3i pos){
|
||||
return getBoundaries().contains(pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the boundaries of the WorldChunk.<br>
|
||||
*/
|
||||
AABB getBoundaries();
|
||||
|
||||
/**
|
||||
* Returns a smaller part of this WorldChunk<br>
|
||||
* <br>
|
||||
* This is used to give the implementation an easy way to optimize thread-save access to this world-chunk.<br>
|
||||
* The {@link #getBlock} method is and should be used in favour to {@link World#getBlock}.<br>
|
||||
*/
|
||||
WorldChunk getWorldChunk(AABB boundaries);
|
||||
|
||||
/**
|
||||
* Returns true if the complete WorldChunk is generated and populated by Minecraft.<br>
|
||||
*/
|
||||
boolean isGenerated();
|
||||
|
||||
}
|
BIN
BlueMapCore/src/main/resources/DefaultResources.zip
Normal file
BIN
BlueMapCore/src/main/resources/DefaultResources.zip
Normal file
Binary file not shown.
452
BlueMapCore/src/main/resources/biomes.json
Normal file
452
BlueMapCore/src/main/resources/biomes.json
Normal file
@ -0,0 +1,452 @@
|
||||
{
|
||||
"ocean": {
|
||||
"id": 0,
|
||||
"humidity": 0.5,
|
||||
"temp": 0.5,
|
||||
"watercolor": 4159204
|
||||
},
|
||||
"plains": {
|
||||
"id": 1,
|
||||
"humidity": 0.4,
|
||||
"temp": 0.8,
|
||||
"watercolor": 4159204
|
||||
},
|
||||
"desert": {
|
||||
"id": 2,
|
||||
"humidity": 0.0,
|
||||
"temp": 2.0,
|
||||
"watercolor": 4159204
|
||||
},
|
||||
"mountains": {
|
||||
"id": 3,
|
||||
"humidity": 0.3,
|
||||
"temp": 0.2,
|
||||
"watercolor": 4159204
|
||||
},
|
||||
"forest": {
|
||||
"id": 4,
|
||||
"humidity": 0.8,
|
||||
"temp": 0.7,
|
||||
"watercolor": 4159204
|
||||
},
|
||||
"taiga": {
|
||||
"id": 5,
|
||||
"humidity": 0.8,
|
||||
"temp": 0.25,
|
||||
"watercolor": 4159204
|
||||
},
|
||||
"swamp": {
|
||||
"id": 6,
|
||||
"humidity": 0.9,
|
||||
"temp": 0.8,
|
||||
"watercolor": 6388580
|
||||
},
|
||||
"river": {
|
||||
"id": 7,
|
||||
"humidity": 0.5,
|
||||
"temp": 0.5,
|
||||
"watercolor": 4159204
|
||||
},
|
||||
"nether": {
|
||||
"id": 8,
|
||||
"humidity": 0.0,
|
||||
"temp": 2.0,
|
||||
"watercolor": 4159204
|
||||
},
|
||||
"the_end": {
|
||||
"id": 9,
|
||||
"humidity": 0.5,
|
||||
"temp": 0.5,
|
||||
"watercolor": 4159204
|
||||
},
|
||||
"frozen_ocean": {
|
||||
"id": 10,
|
||||
"humidity": 0.5,
|
||||
"temp": 0.0,
|
||||
"watercolor": 3750089
|
||||
},
|
||||
"frozen_river": {
|
||||
"id": 11,
|
||||
"humidity": 0.5,
|
||||
"temp": 0.0,
|
||||
"watercolor": 3750089
|
||||
},
|
||||
"snowy_tundra": {
|
||||
"id": 12,
|
||||
"humidity": 0.5,
|
||||
"temp": 0.0,
|
||||
"watercolor": 4159204
|
||||
},
|
||||
"snowy_mountains": {
|
||||
"id": 13,
|
||||
"humidity": 0.5,
|
||||
"temp": 0.0,
|
||||
"watercolor": 4159204
|
||||
},
|
||||
"mushroom_fields": {
|
||||
"id": 14,
|
||||
"humidity": 1.0,
|
||||
"temp": 0.9,
|
||||
"watercolor": 4159204
|
||||
},
|
||||
"mushroom_field_shore": {
|
||||
"id": 15,
|
||||
"humidity": 1.0,
|
||||
"temp": 0.9,
|
||||
"watercolor": 4159204
|
||||
},
|
||||
"beach": {
|
||||
"id": 16,
|
||||
"humidity": 0.4,
|
||||
"temp": 0.8,
|
||||
"watercolor": 4159204
|
||||
},
|
||||
"desert_hills": {
|
||||
"id": 17,
|
||||
"humidity": 0.0,
|
||||
"temp": 2.0,
|
||||
"watercolor": 4159204
|
||||
},
|
||||
"wooded_hills": {
|
||||
"id": 18,
|
||||
"humidity": 0.8,
|
||||
"temp": 0.7,
|
||||
"watercolor": 4159204
|
||||
},
|
||||
"taiga_hills": {
|
||||
"id": 19,
|
||||
"humidity": 0.8,
|
||||
"temp": 0.25,
|
||||
"watercolor": 4159204
|
||||
},
|
||||
"mountain_edge": {
|
||||
"id": 20,
|
||||
"humidity": 0.3,
|
||||
"temp": 0.2,
|
||||
"watercolor": 4159204
|
||||
},
|
||||
"jungle": {
|
||||
"id": 21,
|
||||
"humidity": 0.9,
|
||||
"temp": 0.95,
|
||||
"watercolor": 4159204
|
||||
},
|
||||
"jungle_hills": {
|
||||
"id": 22,
|
||||
"humidity": 0.9,
|
||||
"temp": 0.95,
|
||||
"watercolor": 4159204
|
||||
},
|
||||
"jungle_edge": {
|
||||
"id": 23,
|
||||
"humidity": 0.8,
|
||||
"temp": 0.95,
|
||||
"watercolor": 4159204
|
||||
},
|
||||
"deep_ocean": {
|
||||
"id": 24,
|
||||
"humidity": 0.5,
|
||||
"temp": 0.5,
|
||||
"watercolor": 4159204
|
||||
},
|
||||
"stone_shore": {
|
||||
"id": 25,
|
||||
"humidity": 0.3,
|
||||
"temp": 0.2,
|
||||
"watercolor": 4159204
|
||||
},
|
||||
"snowy_beach": {
|
||||
"id": 26,
|
||||
"humidity": 0.3,
|
||||
"temp": 0.05,
|
||||
"watercolor": 4020182
|
||||
},
|
||||
"birch_forest": {
|
||||
"id": 27,
|
||||
"humidity": 0.6,
|
||||
"temp": 0.6,
|
||||
"watercolor": 4159204
|
||||
},
|
||||
"birch_forest_hills": {
|
||||
"id": 28,
|
||||
"humidity": 0.6,
|
||||
"temp": 0.6,
|
||||
"watercolor": 4159204
|
||||
},
|
||||
"dark_forest": {
|
||||
"id": 29,
|
||||
"humidity": 0.8,
|
||||
"temp": 0.7,
|
||||
"watercolor": 4159204
|
||||
},
|
||||
"snowy_taiga": {
|
||||
"id": 30,
|
||||
"humidity": 0.4,
|
||||
"temp": -0.5,
|
||||
"watercolor": 4020182
|
||||
},
|
||||
"snowy_taiga_hills": {
|
||||
"id": 31,
|
||||
"humidity": 0.4,
|
||||
"temp": -0.5,
|
||||
"watercolor": 4020182
|
||||
},
|
||||
"giant_tree_taiga": {
|
||||
"id": 32,
|
||||
"humidity": 0.8,
|
||||
"temp": 0.3,
|
||||
"watercolor": 4159204
|
||||
},
|
||||
"giant_tree_taiga_hills": {
|
||||
"id": 33,
|
||||
"humidity": 0.8,
|
||||
"temp": 0.3,
|
||||
"watercolor": 4159204
|
||||
},
|
||||
"wooded_mountains": {
|
||||
"id": 34,
|
||||
"humidity": 0.3,
|
||||
"temp": 0.2,
|
||||
"watercolor": 4159204
|
||||
},
|
||||
"savanna": {
|
||||
"id": 35,
|
||||
"humidity": 0.0,
|
||||
"temp": 1.2,
|
||||
"watercolor": 4159204
|
||||
},
|
||||
"savanna_plateau": {
|
||||
"id": 36,
|
||||
"humidity": 0.0,
|
||||
"temp": 1.0,
|
||||
"watercolor": 4159204
|
||||
},
|
||||
"badlands": {
|
||||
"id": 37,
|
||||
"humidity": 0.0,
|
||||
"temp": 2.0,
|
||||
"watercolor": 4159204
|
||||
},
|
||||
"wooded_badlands_plateau": {
|
||||
"id": 38,
|
||||
"humidity": 0.0,
|
||||
"temp": 2.0,
|
||||
"watercolor": 4159204
|
||||
},
|
||||
"badlands_plateau": {
|
||||
"id": 39,
|
||||
"humidity": 0.0,
|
||||
"temp": 2.0,
|
||||
"watercolor": 4159204
|
||||
},
|
||||
"small_end_islands": {
|
||||
"id": 40,
|
||||
"humidity": 0.5,
|
||||
"temp": 0.5,
|
||||
"watercolor": 4159204
|
||||
},
|
||||
"end_midlands": {
|
||||
"id": 41,
|
||||
"humidity": 0.5,
|
||||
"temp": 0.5,
|
||||
"watercolor": 4159204
|
||||
},
|
||||
"end_highlands": {
|
||||
"id": 42,
|
||||
"humidity": 0.5,
|
||||
"temp": 0.5,
|
||||
"watercolor": 4159204
|
||||
},
|
||||
"end_barrens": {
|
||||
"id": 43,
|
||||
"humidity": 0.5,
|
||||
"temp": 0.5,
|
||||
"watercolor": 4159204
|
||||
},
|
||||
"warm_ocean": {
|
||||
"id": 44,
|
||||
"humidity": 0.5,
|
||||
"temp": 0.5,
|
||||
"watercolor": 4445678
|
||||
},
|
||||
"lukewarm_ocean": {
|
||||
"id": 45,
|
||||
"humidity": 0.5,
|
||||
"temp": 0.5,
|
||||
"watercolor": 4566514
|
||||
},
|
||||
"cold_ocean": {
|
||||
"id": 46,
|
||||
"humidity": 0.5,
|
||||
"temp": 0.5,
|
||||
"watercolor": 4020182
|
||||
},
|
||||
"deep_warm_ocean": {
|
||||
"id": 47,
|
||||
"humidity": 0.5,
|
||||
"temp": 0.5,
|
||||
"watercolor": 4445678
|
||||
},
|
||||
"deep_lukewarm_ocean": {
|
||||
"id": 48,
|
||||
"humidity": 0.5,
|
||||
"temp": 0.5,
|
||||
"watercolor": 4566514
|
||||
},
|
||||
"deep_cold_ocean": {
|
||||
"id": 49,
|
||||
"humidity": 0.5,
|
||||
"temp": 0.5,
|
||||
"watercolor": 4020182
|
||||
},
|
||||
"deep_frozen_ocean": {
|
||||
"id": 50,
|
||||
"humidity": 0.5,
|
||||
"temp": 0.5,
|
||||
"watercolor": 3750089
|
||||
},
|
||||
"the_void": {
|
||||
"id": 127,
|
||||
"humidity": 0.5,
|
||||
"temp": 0.5,
|
||||
"watercolor": 4159204
|
||||
},
|
||||
"sunflower_plains": {
|
||||
"id": 129,
|
||||
"humidity": 0.4,
|
||||
"temp": 0.8,
|
||||
"watercolor": 4159204
|
||||
},
|
||||
"desert_lakes": {
|
||||
"id": 130,
|
||||
"humidity": 0.0,
|
||||
"temp": 2.0,
|
||||
"watercolor": 4159204
|
||||
},
|
||||
"gravelly_mountains": {
|
||||
"id": 131,
|
||||
"humidity": 0.3,
|
||||
"temp": 0.2,
|
||||
"watercolor": 4159204
|
||||
},
|
||||
"flower_forest": {
|
||||
"id": 132,
|
||||
"humidity": 0.8,
|
||||
"temp": 0.7,
|
||||
"watercolor": 4159204
|
||||
},
|
||||
"taiga_mountains": {
|
||||
"id": 133,
|
||||
"humidity": 0.8,
|
||||
"temp": 0.25,
|
||||
"watercolor": 4159204
|
||||
},
|
||||
"swamp_hills": {
|
||||
"id": 134,
|
||||
"humidity": 0.9,
|
||||
"temp": 0.8,
|
||||
"watercolor": 6388580
|
||||
},
|
||||
"ice_spikes": {
|
||||
"id": 140,
|
||||
"humidity": 0.5,
|
||||
"temp": 0.0,
|
||||
"watercolor": 4159204
|
||||
},
|
||||
"modified_jungle": {
|
||||
"id": 149,
|
||||
"humidity": 0.9,
|
||||
"temp": 0.95,
|
||||
"watercolor": 4159204
|
||||
},
|
||||
"modified_jungle_edge": {
|
||||
"id": 151,
|
||||
"humidity": 0.8,
|
||||
"temp": 0.95,
|
||||
"watercolor": 4159204
|
||||
},
|
||||
"tall_birch_forest": {
|
||||
"id": 155,
|
||||
"humidity": 0.6,
|
||||
"temp": 0.6,
|
||||
"watercolor": 4159204
|
||||
},
|
||||
"tall_birch_hills": {
|
||||
"id": 156,
|
||||
"humidity": 0.6,
|
||||
"temp": 0.6,
|
||||
"watercolor": 4159204
|
||||
},
|
||||
"dark_forest_hills": {
|
||||
"id": 157,
|
||||
"humidity": 0.8,
|
||||
"temp": 0.7,
|
||||
"watercolor": 4159204
|
||||
},
|
||||
"snowy_taiga_mountains": {
|
||||
"id": 158,
|
||||
"humidity": 0.4,
|
||||
"temp": -0.5,
|
||||
"watercolor": 4020182
|
||||
},
|
||||
"giant_spruce_taiga": {
|
||||
"id": 160,
|
||||
"humidity": 0.8,
|
||||
"temp": 0.25,
|
||||
"watercolor": 4159204
|
||||
},
|
||||
"giant_spruce_taiga_hills": {
|
||||
"id": 161,
|
||||
"humidity": 0.8,
|
||||
"temp": 0.25,
|
||||
"watercolor": 4159204
|
||||
},
|
||||
"modified_gravelly_mountains": {
|
||||
"id": 162,
|
||||
"humidity": 0.3,
|
||||
"temp": 0.2,
|
||||
"watercolor": 4159204
|
||||
},
|
||||
"shattered_savanna": {
|
||||
"id": 163,
|
||||
"humidity": 0.0,
|
||||
"temp": 1.1,
|
||||
"watercolor": 4159204
|
||||
},
|
||||
"shattered_savanna_plateau": {
|
||||
"id": 164,
|
||||
"humidity": 0.0,
|
||||
"temp": 1.0,
|
||||
"watercolor": 4159204
|
||||
},
|
||||
"eroded_badlands": {
|
||||
"id": 165,
|
||||
"humidity": 0.0,
|
||||
"temp": 2.0,
|
||||
"watercolor": 4159204
|
||||
},
|
||||
"modified_wooded_badlands_plateau": {
|
||||
"id": 166,
|
||||
"humidity": 0.0,
|
||||
"temp": 2.0,
|
||||
"watercolor": 4159204
|
||||
},
|
||||
"modified_badlands_plateau": {
|
||||
"id": 167,
|
||||
"humidity": 0.0,
|
||||
"temp": 2.0,
|
||||
"watercolor": 4159204
|
||||
},
|
||||
"bamboo_jungle": {
|
||||
"id": 168,
|
||||
"humidity": 0.9,
|
||||
"temp": 0.95,
|
||||
"watercolor": 4159204
|
||||
},
|
||||
"bamboo_jungle_hills": {
|
||||
"id": 169,
|
||||
"humidity": 0.9,
|
||||
"temp": 0.95,
|
||||
"watercolor": 4159204
|
||||
}
|
||||
}
|
11
BlueMapCore/src/main/resources/blockColors.json
Normal file
11
BlueMapCore/src/main/resources/blockColors.json
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"default": "#foliage",
|
||||
"grass_block" : "#grass",
|
||||
"grass" : "#grass",
|
||||
"tall_grass" : "#grass",
|
||||
"fern" : "#grass",
|
||||
"large_fern" : "#grass",
|
||||
"redstone_wire" : "ff0000",
|
||||
"birch_leaves" : "86a863",
|
||||
"spruce_leaves" : "51946b"
|
||||
}
|
1487
BlueMapCore/src/main/resources/blockIdMappings.json
Normal file
1487
BlueMapCore/src/main/resources/blockIdMappings.json
Normal file
File diff suppressed because it is too large
Load Diff
4402
BlueMapCore/src/main/resources/blockProperties.json
Normal file
4402
BlueMapCore/src/main/resources/blockProperties.json
Normal file
File diff suppressed because it is too large
Load Diff
BIN
BlueMapCore/src/main/resources/webroot.zip
Normal file
BIN
BlueMapCore/src/main/resources/webroot.zip
Normal file
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user