mirror of
https://github.com/BlueMap-Minecraft/BlueMap.git
synced 2024-11-21 18:16: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