mirror of
https://github.com/BlueMap-Minecraft/BlueMap.git
synced 2024-12-27 19:37:42 +01:00
Complete rewrite of resource loading, render-optimizations and fixes for resource-packs
This commit is contained in:
parent
d607e49ede
commit
ff00f3f1b6
@ -45,6 +45,7 @@
|
|||||||
import org.apache.commons.cli.Option;
|
import org.apache.commons.cli.Option;
|
||||||
import org.apache.commons.cli.Options;
|
import org.apache.commons.cli.Options;
|
||||||
import org.apache.commons.cli.ParseException;
|
import org.apache.commons.cli.ParseException;
|
||||||
|
import org.apache.commons.io.FileUtils;
|
||||||
|
|
||||||
import com.flowpowered.math.vector.Vector2i;
|
import com.flowpowered.math.vector.Vector2i;
|
||||||
import com.flowpowered.math.vector.Vector3i;
|
import com.flowpowered.math.vector.Vector3i;
|
||||||
@ -59,7 +60,7 @@
|
|||||||
import de.bluecolored.bluemap.core.render.TileRenderer;
|
import de.bluecolored.bluemap.core.render.TileRenderer;
|
||||||
import de.bluecolored.bluemap.core.render.hires.HiresModelManager;
|
import de.bluecolored.bluemap.core.render.hires.HiresModelManager;
|
||||||
import de.bluecolored.bluemap.core.render.lowres.LowresModelManager;
|
import de.bluecolored.bluemap.core.render.lowres.LowresModelManager;
|
||||||
import de.bluecolored.bluemap.core.resourcepack.NoSuchResourceException;
|
import de.bluecolored.bluemap.core.resourcepack.ParseResourceException;
|
||||||
import de.bluecolored.bluemap.core.resourcepack.ResourcePack;
|
import de.bluecolored.bluemap.core.resourcepack.ResourcePack;
|
||||||
import de.bluecolored.bluemap.core.web.BlueMapWebServer;
|
import de.bluecolored.bluemap.core.web.BlueMapWebServer;
|
||||||
import de.bluecolored.bluemap.core.web.WebSettings;
|
import de.bluecolored.bluemap.core.web.WebSettings;
|
||||||
@ -69,17 +70,19 @@ public class BlueMapCLI {
|
|||||||
|
|
||||||
private ConfigurationFile configFile;
|
private ConfigurationFile configFile;
|
||||||
private Configuration config;
|
private Configuration config;
|
||||||
|
private File configFolder;
|
||||||
private ResourcePack resourcePack;
|
private ResourcePack resourcePack;
|
||||||
private boolean forceRender;
|
private boolean forceRender;
|
||||||
|
|
||||||
public BlueMapCLI(ConfigurationFile configFile, boolean forceRender) {
|
public BlueMapCLI(ConfigurationFile configFile, File configFolder, boolean forceRender) {
|
||||||
this.configFile = configFile;
|
this.configFile = configFile;
|
||||||
this.config = configFile.getConfig();
|
this.config = configFile.getConfig();
|
||||||
|
this.configFolder = configFolder;
|
||||||
this.forceRender = forceRender;
|
this.forceRender = forceRender;
|
||||||
this.resourcePack = null;
|
this.resourcePack = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void renderMaps() throws IOException, NoSuchResourceException {
|
public void renderMaps() throws IOException {
|
||||||
Preconditions.checkNotNull(resourcePack);
|
Preconditions.checkNotNull(resourcePack);
|
||||||
|
|
||||||
config.getWebDataPath().toFile().mkdirs();
|
config.getWebDataPath().toFile().mkdirs();
|
||||||
@ -126,6 +129,10 @@ public void renderMaps() throws IOException, NoSuchResourceException {
|
|||||||
}
|
}
|
||||||
webSettings.save();
|
webSettings.save();
|
||||||
|
|
||||||
|
Logger.global.logInfo("Writing textures.json ...");
|
||||||
|
File textureExportFile = config.getWebDataPath().resolve("textures.json").toFile();
|
||||||
|
resourcePack.saveTextureFile(textureExportFile);
|
||||||
|
|
||||||
for (MapType map : maps.values()) {
|
for (MapType map : maps.values()) {
|
||||||
Logger.global.logInfo("Rendering map '" + map.getId() + "' ...");
|
Logger.global.logInfo("Rendering map '" + map.getId() + "' ...");
|
||||||
Logger.global.logInfo("Collecting tiles to render...");
|
Logger.global.logInfo("Collecting tiles to render...");
|
||||||
@ -187,14 +194,25 @@ public void startWebserver() throws IOException {
|
|||||||
webserver.start();
|
webserver.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean loadResources() throws IOException, NoSuchResourceException {
|
private boolean loadResources() throws IOException, ParseResourceException {
|
||||||
|
Logger.global.logInfo("Loading resources...");
|
||||||
|
|
||||||
File defaultResourceFile = config.getDataPath().resolve("minecraft-client-" + ResourcePack.MINECRAFT_CLIENT_VERSION + ".jar").toFile();
|
File defaultResourceFile = config.getDataPath().resolve("minecraft-client-" + ResourcePack.MINECRAFT_CLIENT_VERSION + ".jar").toFile();
|
||||||
|
File resourceExtensionsFile = config.getDataPath().resolve("resourceExtensions.zip").toFile();
|
||||||
File textureExportFile = config.getWebDataPath().resolve("textures.json").toFile();
|
File textureExportFile = config.getWebDataPath().resolve("textures.json").toFile();
|
||||||
|
|
||||||
if (!defaultResourceFile.exists()) {
|
if (!defaultResourceFile.exists()) {
|
||||||
if (!handleMissingResources(defaultResourceFile)) return false;
|
if (!handleMissingResources(defaultResourceFile)) return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resourceExtensionsFile.delete();
|
||||||
|
FileUtils.copyURLToFile(BlueMapCLI.class.getResource("/resourceExtensions.zip"), resourceExtensionsFile, 10000, 10000);
|
||||||
|
|
||||||
|
File blockColorsConfigFile = new File(configFolder, "blockColors.json");
|
||||||
|
if (!blockColorsConfigFile.exists()) {
|
||||||
|
FileUtils.copyURLToFile(BlueMapCLI.class.getResource("/blockColors.json"), blockColorsConfigFile, 10000, 10000);
|
||||||
|
}
|
||||||
|
|
||||||
//find more resource packs
|
//find more resource packs
|
||||||
File resourcePackFolder = configFile.getFile().toPath().resolveSibling("resourcepacks").toFile();
|
File resourcePackFolder = configFile.getFile().toPath().resolveSibling("resourcepacks").toFile();
|
||||||
resourcePackFolder.mkdirs();
|
resourcePackFolder.mkdirs();
|
||||||
@ -204,8 +222,13 @@ private boolean loadResources() throws IOException, NoSuchResourceException {
|
|||||||
List<File> resources = new ArrayList<>(resourcePacks.length + 1);
|
List<File> resources = new ArrayList<>(resourcePacks.length + 1);
|
||||||
resources.add(defaultResourceFile);
|
resources.add(defaultResourceFile);
|
||||||
for (File file : resourcePacks) resources.add(file);
|
for (File file : resourcePacks) resources.add(file);
|
||||||
|
resources.add(resourceExtensionsFile);
|
||||||
|
|
||||||
resourcePack = new ResourcePack(resources, textureExportFile);
|
resourcePack = new ResourcePack();
|
||||||
|
if (textureExportFile.exists()) resourcePack.loadTextureFile(textureExportFile);
|
||||||
|
resourcePack.load(resources);
|
||||||
|
resourcePack.loadBlockColorConfig(blockColorsConfigFile);
|
||||||
|
resourcePack.saveTextureFile(textureExportFile);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -228,7 +251,7 @@ private boolean handleMissingResources(File resourceFile) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) throws IOException, NoSuchResourceException {
|
public static void main(String[] args) throws IOException, ParseResourceException {
|
||||||
CommandLineParser parser = new DefaultParser();
|
CommandLineParser parser = new DefaultParser();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -241,12 +264,14 @@ public static void main(String[] args) throws IOException, NoSuchResourceExcepti
|
|||||||
}
|
}
|
||||||
|
|
||||||
//load config
|
//load config
|
||||||
File configFile = new File("bluemap.conf").getAbsoluteFile();
|
File configFolder = new File(".");
|
||||||
if (cmd.hasOption("c")) {
|
if (cmd.hasOption("c")) {
|
||||||
configFile = new File(cmd.getOptionValue("c"));
|
configFolder = new File(cmd.getOptionValue("c"));
|
||||||
configFile.getParentFile().mkdirs();
|
configFolder.mkdirs();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
File configFile = new File(configFolder, "bluemap.conf");
|
||||||
|
|
||||||
boolean configCreated = !configFile.exists();
|
boolean configCreated = !configFile.exists();
|
||||||
|
|
||||||
ConfigurationFile config = ConfigurationFile.loadOrCreate(configFile, BlueMapCLI.class.getResource("/bluemap-cli.conf"));
|
ConfigurationFile config = ConfigurationFile.loadOrCreate(configFile, BlueMapCLI.class.getResource("/bluemap-cli.conf"));
|
||||||
@ -256,7 +281,7 @@ public static void main(String[] args) throws IOException, NoSuchResourceExcepti
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
BlueMapCLI bluemap = new BlueMapCLI(config, cmd.hasOption("f"));
|
BlueMapCLI bluemap = new BlueMapCLI(config, configFolder, cmd.hasOption("f"));
|
||||||
|
|
||||||
if (config.getConfig().isWebserverEnabled()) {
|
if (config.getConfig().isWebserverEnabled()) {
|
||||||
//start webserver
|
//start webserver
|
||||||
@ -294,8 +319,8 @@ private static Options createOptions() {
|
|||||||
Option.builder("c")
|
Option.builder("c")
|
||||||
.longOpt("config")
|
.longOpt("config")
|
||||||
.hasArg()
|
.hasArg()
|
||||||
.argName("config-file")
|
.argName("config-folder")
|
||||||
.desc("Sets path of the configuration file to use")
|
.desc("Sets path of the folder containing the configuration-files to use (configurations will be generated here if they don't exist)")
|
||||||
.build()
|
.build()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -16,5 +16,13 @@ task zipWebroot(type: Zip) {
|
|||||||
outputs.upToDateWhen { false }
|
outputs.upToDateWhen { false }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
task zipResourceExtensions(type: Zip) {
|
||||||
|
from fileTree('src/main/resourceExtensions')
|
||||||
|
archiveName 'resourceExtensions.zip'
|
||||||
|
destinationDir(file('/src/main/resources/'))
|
||||||
|
outputs.upToDateWhen { false }
|
||||||
|
}
|
||||||
|
|
||||||
//always update the zip before build
|
//always update the zip before build
|
||||||
compileJava.dependsOn(zipWebroot)
|
compileJava.dependsOn(zipWebroot)
|
||||||
|
compileJava.dependsOn(zipResourceExtensions)
|
||||||
|
@ -24,46 +24,63 @@
|
|||||||
*/
|
*/
|
||||||
package de.bluecolored.bluemap.core.logger;
|
package de.bluecolored.bluemap.core.logger;
|
||||||
|
|
||||||
import java.util.Set;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import com.google.common.collect.Sets;
|
import com.google.common.cache.Cache;
|
||||||
|
import com.google.common.cache.CacheBuilder;
|
||||||
|
|
||||||
public abstract class AbstractLogger extends Logger {
|
public abstract class AbstractLogger extends Logger {
|
||||||
|
|
||||||
private Set<String> noFloodLog;
|
private static final Object DUMMY = new Object();
|
||||||
|
|
||||||
|
private Cache<String, Object> noFloodCache;
|
||||||
|
|
||||||
public AbstractLogger() {
|
public AbstractLogger() {
|
||||||
noFloodLog = Sets.newConcurrentHashSet();
|
noFloodCache = CacheBuilder.newBuilder()
|
||||||
|
.concurrencyLevel(4)
|
||||||
|
.expireAfterWrite(1, TimeUnit.HOURS)
|
||||||
|
.maximumSize(10000)
|
||||||
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void noFloodError(String key, String message, Throwable throwable){
|
public void noFloodError(String key, String message, Throwable throwable){
|
||||||
if (noFloodLog.add(key)) logError(message, throwable);
|
if (check(key)) logError(message, throwable);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void noFloodWarning(String key, String message){
|
public void noFloodWarning(String key, String message){
|
||||||
if (noFloodLog.add(key)) logWarning(message);
|
if (check(key)) logWarning(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void noFloodInfo(String key, String message){
|
public void noFloodInfo(String key, String message){
|
||||||
if (noFloodLog.add(key)) logInfo(message);
|
if (check(key)) logInfo(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void noFloodDebug(String key, String message){
|
public void noFloodDebug(String key, String message){
|
||||||
if (noFloodLog.add(key)) logDebug(message);
|
if (check(key)) logDebug(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void clearNoFloodLog() {
|
public void clearNoFloodLog() {
|
||||||
noFloodLog.clear();
|
noFloodCache.invalidateAll();
|
||||||
|
noFloodCache.cleanUp();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void removeNoFloodKey(String key) {
|
public void removeNoFloodKey(String key) {
|
||||||
noFloodLog.remove(key);
|
noFloodCache.invalidate(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean check(String key) {
|
||||||
|
if (noFloodCache.getIfPresent(key) == null) {
|
||||||
|
noFloodCache.put(key, DUMMY);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -30,9 +30,26 @@ public class PrintStreamLogger extends AbstractLogger {
|
|||||||
|
|
||||||
private PrintStream out, err;
|
private PrintStream out, err;
|
||||||
|
|
||||||
|
boolean isDebug;
|
||||||
|
|
||||||
public PrintStreamLogger(PrintStream out, PrintStream err) {
|
public PrintStreamLogger(PrintStream out, PrintStream err) {
|
||||||
this.out = out;
|
this.out = out;
|
||||||
this.err = err;
|
this.err = err;
|
||||||
|
this.isDebug = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PrintStreamLogger(PrintStream out, PrintStream err, boolean debug) {
|
||||||
|
this.out = out;
|
||||||
|
this.err = err;
|
||||||
|
this.isDebug = debug;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isDebug() {
|
||||||
|
return isDebug;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDebug(boolean debug) {
|
||||||
|
this.isDebug = debug;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -53,7 +70,17 @@ public void logInfo(String message) {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void logDebug(String message) {
|
public void logDebug(String message) {
|
||||||
out.println("[DEBUG] " + message);
|
if (isDebug) out.println("[DEBUG] " + message);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void noFloodDebug(String key, String message) {
|
||||||
|
if (isDebug) super.noFloodDebug(key, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void noFloodDebug(String message) {
|
||||||
|
if (isDebug) super.noFloodDebug(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -29,8 +29,9 @@
|
|||||||
import com.flowpowered.math.vector.Vector2i;
|
import com.flowpowered.math.vector.Vector2i;
|
||||||
import com.flowpowered.math.vector.Vector3i;
|
import com.flowpowered.math.vector.Vector3i;
|
||||||
|
|
||||||
import de.bluecolored.bluemap.core.mca.mapping.LightData;
|
import de.bluecolored.bluemap.core.world.Biome;
|
||||||
import de.bluecolored.bluemap.core.world.BlockState;
|
import de.bluecolored.bluemap.core.world.BlockState;
|
||||||
|
import de.bluecolored.bluemap.core.world.LightData;
|
||||||
import net.querz.nbt.CompoundTag;
|
import net.querz.nbt.CompoundTag;
|
||||||
|
|
||||||
public abstract class Chunk {
|
public abstract class Chunk {
|
||||||
@ -63,7 +64,7 @@ public MCAWorld getWorld() {
|
|||||||
|
|
||||||
public abstract LightData getLightData(Vector3i pos);
|
public abstract LightData getLightData(Vector3i pos);
|
||||||
|
|
||||||
public abstract String getBiomeId(Vector3i pos);
|
public abstract Biome getBiome(Vector3i pos);
|
||||||
|
|
||||||
public static Chunk create(MCAWorld world, CompoundTag chunkTag) throws IOException {
|
public static Chunk create(MCAWorld world, CompoundTag chunkTag) throws IOException {
|
||||||
int version = chunkTag.getInt("DataVersion");
|
int version = chunkTag.getInt("DataVersion");
|
||||||
|
@ -28,8 +28,9 @@
|
|||||||
|
|
||||||
import de.bluecolored.bluemap.core.mca.mapping.BiomeIdMapper;
|
import de.bluecolored.bluemap.core.mca.mapping.BiomeIdMapper;
|
||||||
import de.bluecolored.bluemap.core.mca.mapping.BlockIdMapper;
|
import de.bluecolored.bluemap.core.mca.mapping.BlockIdMapper;
|
||||||
import de.bluecolored.bluemap.core.mca.mapping.LightData;
|
import de.bluecolored.bluemap.core.world.Biome;
|
||||||
import de.bluecolored.bluemap.core.world.BlockState;
|
import de.bluecolored.bluemap.core.world.BlockState;
|
||||||
|
import de.bluecolored.bluemap.core.world.LightData;
|
||||||
import net.querz.nbt.CompoundTag;
|
import net.querz.nbt.CompoundTag;
|
||||||
import net.querz.nbt.ListTag;
|
import net.querz.nbt.ListTag;
|
||||||
import net.querz.nbt.mca.MCAUtil;
|
import net.querz.nbt.mca.MCAUtil;
|
||||||
@ -90,7 +91,7 @@ public LightData getLightData(Vector3i pos) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getBiomeId(Vector3i pos) {
|
public Biome getBiome(Vector3i pos) {
|
||||||
int x = pos.getX() & 0xF; // Math.floorMod(pos.getX(), 16)
|
int x = pos.getX() & 0xF; // Math.floorMod(pos.getX(), 16)
|
||||||
int z = pos.getZ() & 0xF;
|
int z = pos.getZ() & 0xF;
|
||||||
int biomeByteIndex = z * 16 + x;
|
int biomeByteIndex = z * 16 + x;
|
||||||
|
@ -32,8 +32,9 @@
|
|||||||
|
|
||||||
import de.bluecolored.bluemap.core.logger.Logger;
|
import de.bluecolored.bluemap.core.logger.Logger;
|
||||||
import de.bluecolored.bluemap.core.mca.mapping.BiomeIdMapper;
|
import de.bluecolored.bluemap.core.mca.mapping.BiomeIdMapper;
|
||||||
import de.bluecolored.bluemap.core.mca.mapping.LightData;
|
import de.bluecolored.bluemap.core.world.Biome;
|
||||||
import de.bluecolored.bluemap.core.world.BlockState;
|
import de.bluecolored.bluemap.core.world.BlockState;
|
||||||
|
import de.bluecolored.bluemap.core.world.LightData;
|
||||||
import net.querz.nbt.ByteArrayTag;
|
import net.querz.nbt.ByteArrayTag;
|
||||||
import net.querz.nbt.CompoundTag;
|
import net.querz.nbt.CompoundTag;
|
||||||
import net.querz.nbt.IntArrayTag;
|
import net.querz.nbt.IntArrayTag;
|
||||||
@ -111,7 +112,7 @@ public LightData getLightData(Vector3i pos) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getBiomeId(Vector3i pos) {
|
public Biome getBiome(Vector3i pos) {
|
||||||
int x = pos.getX() & 0xF; // Math.floorMod(pos.getX(), 16)
|
int x = pos.getX() & 0xF; // Math.floorMod(pos.getX(), 16)
|
||||||
int z = pos.getZ() & 0xF;
|
int z = pos.getZ() & 0xF;
|
||||||
int biomeByteIndex = z * 16 + x;
|
int biomeByteIndex = z * 16 + x;
|
||||||
@ -148,7 +149,7 @@ public Section(CompoundTag sectionData) {
|
|||||||
if (stateTag.containsKey("Properties")) {
|
if (stateTag.containsKey("Properties")) {
|
||||||
CompoundTag propertiesTag = stateTag.getCompoundTag("Properties");
|
CompoundTag propertiesTag = stateTag.getCompoundTag("Properties");
|
||||||
for (Entry<String, Tag<?>> property : propertiesTag) {
|
for (Entry<String, Tag<?>> property : propertiesTag) {
|
||||||
properties.put(property.getKey(), ((StringTag) property.getValue()).getValue());
|
properties.put(property.getKey().toLowerCase(), ((StringTag) property.getValue()).getValue().toLowerCase());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,20 +27,21 @@
|
|||||||
import com.flowpowered.math.vector.Vector3i;
|
import com.flowpowered.math.vector.Vector3i;
|
||||||
|
|
||||||
import de.bluecolored.bluemap.core.mca.mapping.BlockProperties;
|
import de.bluecolored.bluemap.core.mca.mapping.BlockProperties;
|
||||||
import de.bluecolored.bluemap.core.mca.mapping.LightData;
|
import de.bluecolored.bluemap.core.world.Biome;
|
||||||
import de.bluecolored.bluemap.core.world.Block;
|
import de.bluecolored.bluemap.core.world.Block;
|
||||||
import de.bluecolored.bluemap.core.world.BlockState;
|
import de.bluecolored.bluemap.core.world.BlockState;
|
||||||
|
import de.bluecolored.bluemap.core.world.LightData;
|
||||||
|
|
||||||
public class MCABlock extends Block {
|
public class MCABlock extends Block {
|
||||||
|
|
||||||
private MCAWorld world;
|
private MCAWorld world;
|
||||||
private BlockState blockState;
|
private BlockState blockState;
|
||||||
private LightData lightData;
|
private LightData lightData;
|
||||||
private String biome;
|
private Biome biome;
|
||||||
private BlockProperties properties;
|
private BlockProperties properties;
|
||||||
private Vector3i pos;
|
private Vector3i pos;
|
||||||
|
|
||||||
public MCABlock(MCAWorld world, BlockState blockState, LightData lightData, String biome, BlockProperties properties, Vector3i pos) {
|
public MCABlock(MCAWorld world, BlockState blockState, LightData lightData, Biome biome, BlockProperties properties, Vector3i pos) {
|
||||||
this.world = world;
|
this.world = world;
|
||||||
this.blockState = blockState;
|
this.blockState = blockState;
|
||||||
this.lightData = lightData;
|
this.lightData = lightData;
|
||||||
@ -85,7 +86,7 @@ public boolean isOccludingNeighborFaces() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getBiome() {
|
public Biome getBiome() {
|
||||||
return biome;
|
return biome;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,11 +64,12 @@
|
|||||||
import de.bluecolored.bluemap.core.mca.mapping.BlockIdMapper;
|
import de.bluecolored.bluemap.core.mca.mapping.BlockIdMapper;
|
||||||
import de.bluecolored.bluemap.core.mca.mapping.BlockProperties;
|
import de.bluecolored.bluemap.core.mca.mapping.BlockProperties;
|
||||||
import de.bluecolored.bluemap.core.mca.mapping.BlockPropertyMapper;
|
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.util.AABB;
|
||||||
|
import de.bluecolored.bluemap.core.world.Biome;
|
||||||
import de.bluecolored.bluemap.core.world.Block;
|
import de.bluecolored.bluemap.core.world.Block;
|
||||||
import de.bluecolored.bluemap.core.world.BlockState;
|
import de.bluecolored.bluemap.core.world.BlockState;
|
||||||
import de.bluecolored.bluemap.core.world.ChunkNotGeneratedException;
|
import de.bluecolored.bluemap.core.world.ChunkNotGeneratedException;
|
||||||
|
import de.bluecolored.bluemap.core.world.LightData;
|
||||||
import de.bluecolored.bluemap.core.world.World;
|
import de.bluecolored.bluemap.core.world.World;
|
||||||
import de.bluecolored.bluemap.core.world.WorldChunk;
|
import de.bluecolored.bluemap.core.world.WorldChunk;
|
||||||
import net.querz.nbt.CompoundTag;
|
import net.querz.nbt.CompoundTag;
|
||||||
@ -162,7 +163,7 @@ public Block getBlock(Vector3i pos) throws ChunkNotGeneratedException {
|
|||||||
Chunk chunk = getChunk(chunkPos);
|
Chunk chunk = getChunk(chunkPos);
|
||||||
BlockState blockState = getExtendedBlockState(chunk, pos);
|
BlockState blockState = getExtendedBlockState(chunk, pos);
|
||||||
LightData lightData = chunk.getLightData(pos);
|
LightData lightData = chunk.getLightData(pos);
|
||||||
String biome = chunk.getBiomeId(pos);
|
Biome biome = chunk.getBiome(pos);
|
||||||
BlockProperties properties = blockPropertyMapper.map(blockState);
|
BlockProperties properties = blockPropertyMapper.map(blockState);
|
||||||
return new MCABlock(this, blockState, lightData, biome, properties, pos);
|
return new MCABlock(this, blockState, lightData, biome, properties, pos);
|
||||||
|
|
||||||
|
@ -25,21 +25,19 @@
|
|||||||
package de.bluecolored.bluemap.core.mca.mapping;
|
package de.bluecolored.bluemap.core.mca.mapping;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
|
import de.bluecolored.bluemap.core.world.Biome;
|
||||||
import ninja.leaping.configurate.ConfigurationNode;
|
import ninja.leaping.configurate.ConfigurationNode;
|
||||||
import ninja.leaping.configurate.gson.GsonConfigurationLoader;
|
import ninja.leaping.configurate.gson.GsonConfigurationLoader;
|
||||||
|
|
||||||
public class BiomeIdMapper {
|
public class BiomeIdMapper {
|
||||||
private static final String DEFAULT_BIOME = "ocean";
|
|
||||||
|
|
||||||
private String[] biomes;
|
private HashMap<Integer, Biome> biomes;
|
||||||
|
|
||||||
public BiomeIdMapper() throws IOException {
|
public BiomeIdMapper() throws IOException {
|
||||||
biomes = new String[256];
|
biomes = new HashMap<>();
|
||||||
for (int i = 0; i < biomes.length; i++) {
|
|
||||||
biomes[i] = DEFAULT_BIOME;
|
|
||||||
}
|
|
||||||
|
|
||||||
GsonConfigurationLoader loader = GsonConfigurationLoader.builder()
|
GsonConfigurationLoader loader = GsonConfigurationLoader.builder()
|
||||||
.setURL(getClass().getResource("/biomes.json"))
|
.setURL(getClass().getResource("/biomes.json"))
|
||||||
@ -48,18 +46,17 @@ public BiomeIdMapper() throws IOException {
|
|||||||
ConfigurationNode node = loader.load();
|
ConfigurationNode node = loader.load();
|
||||||
|
|
||||||
for (Entry<Object, ? extends ConfigurationNode> e : node.getChildrenMap().entrySet()){
|
for (Entry<Object, ? extends ConfigurationNode> e : node.getChildrenMap().entrySet()){
|
||||||
String biome = e.getKey().toString();
|
String id = e.getKey().toString();
|
||||||
int id = e.getValue().getNode("id").getInt(-1);
|
Biome biome = Biome.create(id, e.getValue());
|
||||||
if (id >= 0 && id < biomes.length) {
|
biomes.put(biome.getOrdinal(), biome);
|
||||||
biomes[id] = biome;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String get(int id) {
|
public Biome get(int id) {
|
||||||
if (id < 0 || id >= biomes.length) return DEFAULT_BIOME;
|
Biome biome = biomes.get(id);
|
||||||
return biomes[id];
|
if (biome == null) return Biome.DEFAULT;
|
||||||
|
return biome;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static BiomeIdMapper create() throws IOException {
|
public static BiomeIdMapper create() throws IOException {
|
||||||
|
@ -33,6 +33,7 @@
|
|||||||
import com.flowpowered.math.vector.Vector3i;
|
import com.flowpowered.math.vector.Vector3i;
|
||||||
|
|
||||||
import de.bluecolored.bluemap.core.util.AABB;
|
import de.bluecolored.bluemap.core.util.AABB;
|
||||||
|
import de.bluecolored.bluemap.core.world.Biome;
|
||||||
import de.bluecolored.bluemap.core.world.Block;
|
import de.bluecolored.bluemap.core.world.Block;
|
||||||
import de.bluecolored.bluemap.core.world.BlockState;
|
import de.bluecolored.bluemap.core.world.BlockState;
|
||||||
import de.bluecolored.bluemap.core.world.World;
|
import de.bluecolored.bluemap.core.world.World;
|
||||||
@ -95,8 +96,8 @@ public boolean isCullingNeighborFaces() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getBiome() {
|
public Biome getBiome() {
|
||||||
return "ocean";
|
return Biome.DEFAULT;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -34,9 +34,7 @@
|
|||||||
import de.bluecolored.bluemap.core.render.context.SlicedWorldChunkBlockContext;
|
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.BlockStateModel;
|
||||||
import de.bluecolored.bluemap.core.render.hires.blockmodel.BlockStateModelFactory;
|
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.NoSuchResourceException;
|
||||||
import de.bluecolored.bluemap.core.resourcepack.NoSuchTextureException;
|
|
||||||
import de.bluecolored.bluemap.core.resourcepack.ResourcePack;
|
import de.bluecolored.bluemap.core.resourcepack.ResourcePack;
|
||||||
import de.bluecolored.bluemap.core.util.AABB;
|
import de.bluecolored.bluemap.core.util.AABB;
|
||||||
import de.bluecolored.bluemap.core.util.MathUtils;
|
import de.bluecolored.bluemap.core.util.MathUtils;
|
||||||
@ -84,9 +82,9 @@ public HiresModel render(WorldTile tile, AABB region, RenderSettings renderSetti
|
|||||||
BlockStateModel blockModel;
|
BlockStateModel blockModel;
|
||||||
try {
|
try {
|
||||||
blockModel = modelFactory.createFrom(block.getBlock(), new SlicedWorldChunkBlockContext(chunk, new Vector3i(x, y, z), renderSettings.getSliceY()), renderSettings);
|
blockModel = modelFactory.createFrom(block.getBlock(), new SlicedWorldChunkBlockContext(chunk, new Vector3i(x, y, z), renderSettings.getSliceY()), renderSettings);
|
||||||
} catch (NoSuchResourceException | InvalidResourceDeclarationException | NoSuchTextureException e) {
|
} catch (NoSuchResourceException e) {
|
||||||
blockModel = new BlockStateModel();
|
blockModel = new BlockStateModel();
|
||||||
Logger.global.noFloodDebug(block.getBlock().getId() + "-hiresModelRenderer-blockmodelerr", "Failed to create BlockModel for BlockState: " + block.getBlock() + " (" + e.toString() + ")");
|
Logger.global.noFloodDebug(block.getBlock().getFullId() + "-hiresModelRenderer-blockmodelerr", "Failed to create BlockModel for BlockState: " + block.getBlock() + " (" + e.toString() + ")");
|
||||||
}
|
}
|
||||||
|
|
||||||
blockModel.translate(new Vector3f(x, y, z).sub(min.toFloat()));
|
blockModel.translate(new Vector3f(x, y, z).sub(min.toFloat()));
|
||||||
|
@ -27,11 +27,11 @@
|
|||||||
import de.bluecolored.bluemap.core.render.RenderSettings;
|
import de.bluecolored.bluemap.core.render.RenderSettings;
|
||||||
import de.bluecolored.bluemap.core.render.context.EmptyBlockContext;
|
import de.bluecolored.bluemap.core.render.context.EmptyBlockContext;
|
||||||
import de.bluecolored.bluemap.core.render.context.ExtendedBlockContext;
|
import de.bluecolored.bluemap.core.render.context.ExtendedBlockContext;
|
||||||
|
import de.bluecolored.bluemap.core.resourcepack.BlockColorCalculator;
|
||||||
import de.bluecolored.bluemap.core.resourcepack.BlockStateResource;
|
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.NoSuchResourceException;
|
||||||
import de.bluecolored.bluemap.core.resourcepack.NoSuchTextureException;
|
|
||||||
import de.bluecolored.bluemap.core.resourcepack.ResourcePack;
|
import de.bluecolored.bluemap.core.resourcepack.ResourcePack;
|
||||||
|
import de.bluecolored.bluemap.core.resourcepack.TransformedBlockModelResource;
|
||||||
import de.bluecolored.bluemap.core.world.BlockState;
|
import de.bluecolored.bluemap.core.world.BlockState;
|
||||||
|
|
||||||
public class BlockStateModelFactory {
|
public class BlockStateModelFactory {
|
||||||
@ -42,7 +42,7 @@ public BlockStateModelFactory(ResourcePack resources) {
|
|||||||
this.resourcePack = resources;
|
this.resourcePack = resources;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BlockStateModel createFrom(BlockState blockState) throws NoSuchResourceException, InvalidResourceDeclarationException, NoSuchTextureException {
|
public BlockStateModel createFrom(BlockState blockState) throws NoSuchResourceException {
|
||||||
return createFrom(blockState, EmptyBlockContext.instance(), new RenderSettings() {
|
return createFrom(blockState, EmptyBlockContext.instance(), new RenderSettings() {
|
||||||
@Override
|
@Override
|
||||||
public float getLightShadeMultiplier() {
|
public float getLightShadeMultiplier() {
|
||||||
@ -61,38 +61,50 @@ public float getAmbientOcclusionStrenght() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public BlockStateModel createFrom(BlockState blockState, ExtendedBlockContext context, RenderSettings renderSettings) throws NoSuchResourceException, InvalidResourceDeclarationException, NoSuchTextureException {
|
public BlockStateModel createFrom(BlockState blockState, ExtendedBlockContext context, RenderSettings renderSettings) throws NoSuchResourceException {
|
||||||
|
|
||||||
//air won't be rendered
|
//shortcut for air
|
||||||
if (
|
if (
|
||||||
blockState.getId().equals("air") ||
|
blockState.getFullId().equals("minecraft:air") ||
|
||||||
blockState.getId().equals("cave_air") ||
|
blockState.getFullId().equals("minecraft:cave_air") ||
|
||||||
blockState.getId().equals("void_air")
|
blockState.getFullId().equals("minecraft:void_air")
|
||||||
) {
|
) {
|
||||||
return new BlockStateModel();
|
return new BlockStateModel();
|
||||||
}
|
}
|
||||||
|
|
||||||
// if it is a liquid, use the LiquidModelBuilder
|
BlockStateModel model = createModel(blockState, context, renderSettings);
|
||||||
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 block is waterlogged
|
||||||
if (LiquidModelBuilder.isWaterlogged(blockState)) {
|
if (LiquidModelBuilder.isWaterlogged(blockState)) {
|
||||||
BlockStateModel watermodel = new LiquidModelBuilder(WATERLOGGED_BLOCKSTATE, context, resourcePack, renderSettings).build();
|
model.merge(createModel(WATERLOGGED_BLOCKSTATE, context, renderSettings));
|
||||||
model.merge(watermodel);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return model;
|
return model;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private BlockStateModel createModel(BlockState blockState, ExtendedBlockContext context, RenderSettings renderSettings) throws NoSuchResourceException {
|
||||||
|
|
||||||
|
BlockStateResource resource = resourcePack.getBlockStateResource(blockState);
|
||||||
|
BlockStateModel model = new BlockStateModel();
|
||||||
|
BlockColorCalculator colorCalculator = resourcePack.getBlockColorCalculator();
|
||||||
|
ResourceModelBuilder modelBuilder = new ResourceModelBuilder(renderSettings, context, colorCalculator);
|
||||||
|
LiquidModelBuilder liquidBuilder = new LiquidModelBuilder(renderSettings, context, blockState, colorCalculator);
|
||||||
|
|
||||||
|
for (TransformedBlockModelResource bmr : resource.getModels(blockState, context.getPosition())){
|
||||||
|
switch (bmr.getModel().getType()){
|
||||||
|
case LIQUID:
|
||||||
|
model.merge(liquidBuilder.build(blockState, bmr.getModel()));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
model.merge(modelBuilder.build(bmr));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return model;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
private BlockState WATERLOGGED_BLOCKSTATE = new BlockState("minecraft:water");
|
private BlockState WATERLOGGED_BLOCKSTATE = new BlockState("minecraft:water");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -36,8 +36,9 @@
|
|||||||
import de.bluecolored.bluemap.core.model.Model;
|
import de.bluecolored.bluemap.core.model.Model;
|
||||||
import de.bluecolored.bluemap.core.render.RenderSettings;
|
import de.bluecolored.bluemap.core.render.RenderSettings;
|
||||||
import de.bluecolored.bluemap.core.render.context.ExtendedBlockContext;
|
import de.bluecolored.bluemap.core.render.context.ExtendedBlockContext;
|
||||||
import de.bluecolored.bluemap.core.resourcepack.NoSuchTextureException;
|
import de.bluecolored.bluemap.core.resourcepack.BlockColorCalculator;
|
||||||
import de.bluecolored.bluemap.core.resourcepack.ResourcePack;
|
import de.bluecolored.bluemap.core.resourcepack.BlockModelResource;
|
||||||
|
import de.bluecolored.bluemap.core.resourcepack.Texture;
|
||||||
import de.bluecolored.bluemap.core.util.Direction;
|
import de.bluecolored.bluemap.core.util.Direction;
|
||||||
import de.bluecolored.bluemap.core.world.Block;
|
import de.bluecolored.bluemap.core.world.Block;
|
||||||
import de.bluecolored.bluemap.core.world.BlockState;
|
import de.bluecolored.bluemap.core.world.BlockState;
|
||||||
@ -50,43 +51,73 @@ public class LiquidModelBuilder {
|
|||||||
private static final HashSet<String> DEFAULT_WATERLOGGED_BLOCK_IDS = Sets.newHashSet(
|
private static final HashSet<String> DEFAULT_WATERLOGGED_BLOCK_IDS = Sets.newHashSet(
|
||||||
"minecraft:seagrass",
|
"minecraft:seagrass",
|
||||||
"minecraft:tall_seagrass",
|
"minecraft:tall_seagrass",
|
||||||
"minecraft:kelp"
|
"minecraft:kelp",
|
||||||
|
"minecraft:bubble_column"
|
||||||
);
|
);
|
||||||
|
|
||||||
private BlockState blockState;
|
private BlockState liquidBlockState;
|
||||||
private ExtendedBlockContext context;
|
private ExtendedBlockContext context;
|
||||||
private ResourcePack resourcePack;
|
|
||||||
private RenderSettings renderSettings;
|
private RenderSettings renderSettings;
|
||||||
|
private BlockColorCalculator colorCalculator;
|
||||||
|
|
||||||
private float[] heights;
|
public LiquidModelBuilder(RenderSettings renderSettings, ExtendedBlockContext context, BlockState liquidBlockState, BlockColorCalculator colorCalculator) {
|
||||||
|
|
||||||
public LiquidModelBuilder(BlockState blockState, ExtendedBlockContext context, ResourcePack resourcePack, RenderSettings renderSettings) {
|
|
||||||
this.blockState = blockState;
|
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.resourcePack = resourcePack;
|
|
||||||
this.renderSettings = renderSettings;
|
this.renderSettings = renderSettings;
|
||||||
|
this.liquidBlockState = liquidBlockState;
|
||||||
this.heights = new float[]{14f, 14f, 14f, 14f};
|
this.colorCalculator = colorCalculator;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BlockStateModel build() throws NoSuchTextureException {
|
public BlockStateModel build(BlockState blockState, BlockModelResource bmr) {
|
||||||
if (this.renderSettings.isExcludeFacesWithoutSunlight() && context.getRelativeBlock(0, 0, 0).getSunLightLevel() == 0) return new BlockStateModel();
|
if (this.renderSettings.isExcludeFacesWithoutSunlight() && context.getRelativeBlock(0, 0, 0).getSunLightLevel() == 0) return new BlockStateModel();
|
||||||
|
|
||||||
int level = getLiquidLevel(blockState);
|
int level = getLiquidLevel(blockState);
|
||||||
|
float[] heights = new float[]{16f, 16f, 16f, 16f};
|
||||||
|
|
||||||
if (level >= 8 ||level == 0 && isLiquid(context.getRelativeBlock(0, 1, 0))){
|
if (level < 8 && !(level == 0 && isLiquid(context.getRelativeBlock(0, 1, 0)))){
|
||||||
this.heights = new float[]{16f, 16f, 16f, 16f};
|
heights = new float[]{
|
||||||
return buildModel();
|
getLiquidCornerHeight(-1, 0, -1),
|
||||||
|
getLiquidCornerHeight(-1, 0, 0),
|
||||||
|
getLiquidCornerHeight(0, 0, -1),
|
||||||
|
getLiquidCornerHeight(0, 0, 0)
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
this.heights = new float[]{
|
BlockStateModel model = new BlockStateModel();
|
||||||
getLiquidCornerHeight(-1, 0, -1),
|
Texture texture = bmr.getTexture("still");
|
||||||
getLiquidCornerHeight(-1, 0, 0),
|
|
||||||
getLiquidCornerHeight(0, 0, -1),
|
Vector3f[] c = new Vector3f[]{
|
||||||
getLiquidCornerHeight(0, 0, 0)
|
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 ),
|
||||||
};
|
};
|
||||||
|
|
||||||
return buildModel();
|
int textureId = texture.getId();
|
||||||
|
Vector3f tintcolor = Vector3f.ONE;
|
||||||
|
if (liquidBlockState.getFullId().equals("minecraft:water")) {
|
||||||
|
tintcolor = colorCalculator.getWaterAverageColor(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 = texture.getColor();
|
||||||
|
mapcolor = mapcolor.mul(tintcolor.toVector4(0.5));
|
||||||
|
model.setMapColor(mapcolor);
|
||||||
|
|
||||||
|
return model;
|
||||||
}
|
}
|
||||||
|
|
||||||
private float getLiquidCornerHeight(int x, int y, int z){
|
private float getLiquidCornerHeight(int x, int y, int z){
|
||||||
@ -133,9 +164,9 @@ private boolean isLiquid(Block block){
|
|||||||
return isLiquid(block.getBlock());
|
return isLiquid(block.getBlock());
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isLiquid(BlockState blockstate){
|
private boolean isLiquid(BlockState blockState){
|
||||||
if (blockstate.getId().equals(blockState.getId())) return true;
|
if (blockState.getFullId().equals(liquidBlockState.getFullId())) return true;
|
||||||
return LiquidModelBuilder.isWaterlogged(blockstate);
|
return LiquidModelBuilder.isWaterlogged(blockState);
|
||||||
}
|
}
|
||||||
|
|
||||||
private float getLiquidBaseHeight(BlockState block){
|
private float getLiquidBaseHeight(BlockState block){
|
||||||
@ -151,45 +182,7 @@ private int getLiquidLevel(BlockState block){
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private BlockStateModel buildModel() throws NoSuchTextureException {
|
private void createElementFace(Model model, Direction faceDir, Vector3f c0, Vector3f c1, Vector3f c2, Vector3f c3, Vector3f color, int textureId) {
|
||||||
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
|
//face culling
|
||||||
Block bl = context.getRelativeBlock(faceDir);
|
Block bl = context.getRelativeBlock(faceDir);
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
import com.flowpowered.math.imaginary.Quaternionf;
|
import com.flowpowered.math.imaginary.Quaternionf;
|
||||||
import com.flowpowered.math.matrix.Matrix3f;
|
import com.flowpowered.math.matrix.Matrix3f;
|
||||||
import com.flowpowered.math.vector.Vector2f;
|
import com.flowpowered.math.vector.Vector2f;
|
||||||
|
import com.flowpowered.math.vector.Vector2i;
|
||||||
import com.flowpowered.math.vector.Vector3f;
|
import com.flowpowered.math.vector.Vector3f;
|
||||||
import com.flowpowered.math.vector.Vector3i;
|
import com.flowpowered.math.vector.Vector3i;
|
||||||
import com.flowpowered.math.vector.Vector4f;
|
import com.flowpowered.math.vector.Vector4f;
|
||||||
@ -37,16 +38,12 @@
|
|||||||
import de.bluecolored.bluemap.core.render.RenderSettings;
|
import de.bluecolored.bluemap.core.render.RenderSettings;
|
||||||
import de.bluecolored.bluemap.core.render.context.BlockContext;
|
import de.bluecolored.bluemap.core.render.context.BlockContext;
|
||||||
import de.bluecolored.bluemap.core.render.context.ExtendedBlockContext;
|
import de.bluecolored.bluemap.core.render.context.ExtendedBlockContext;
|
||||||
import de.bluecolored.bluemap.core.resourcepack.BlockModelElementFaceResource;
|
import de.bluecolored.bluemap.core.resourcepack.BlockColorCalculator;
|
||||||
import de.bluecolored.bluemap.core.resourcepack.BlockModelElementResource;
|
|
||||||
import de.bluecolored.bluemap.core.resourcepack.BlockModelResource;
|
import de.bluecolored.bluemap.core.resourcepack.BlockModelResource;
|
||||||
import de.bluecolored.bluemap.core.resourcepack.BlockStateResource;
|
import de.bluecolored.bluemap.core.resourcepack.BlockModelResource.Element.Rotation;
|
||||||
import de.bluecolored.bluemap.core.resourcepack.NoSuchTextureException;
|
import de.bluecolored.bluemap.core.resourcepack.Texture;
|
||||||
import de.bluecolored.bluemap.core.resourcepack.ResourcePack;
|
import de.bluecolored.bluemap.core.resourcepack.TransformedBlockModelResource;
|
||||||
import de.bluecolored.bluemap.core.resourcepack.TextureProvider.Texture;
|
|
||||||
import de.bluecolored.bluemap.core.util.Direction;
|
import de.bluecolored.bluemap.core.util.Direction;
|
||||||
import de.bluecolored.bluemap.core.util.MathUtils;
|
|
||||||
import de.bluecolored.bluemap.core.util.WeighedArrayList;
|
|
||||||
import de.bluecolored.bluemap.core.world.Block;
|
import de.bluecolored.bluemap.core.world.Block;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -58,49 +55,37 @@ public class ResourceModelBuilder {
|
|||||||
private static final Vector3f NEG_HALF_3F = HALF_3F.negate();
|
private static final Vector3f NEG_HALF_3F = HALF_3F.negate();
|
||||||
private static final Vector2f HALF_2F = Vector2f.ONE.mul(0.5);
|
private static final Vector2f HALF_2F = Vector2f.ONE.mul(0.5);
|
||||||
|
|
||||||
private BlockStateResource resource;
|
|
||||||
private ExtendedBlockContext context;
|
private ExtendedBlockContext context;
|
||||||
private ResourcePack resourcePack;
|
|
||||||
private RenderSettings renderSettings;
|
private RenderSettings renderSettings;
|
||||||
|
private BlockColorCalculator colorCalculator;
|
||||||
|
|
||||||
public ResourceModelBuilder(BlockStateResource resource, ExtendedBlockContext context, ResourcePack resourcePack, RenderSettings renderSettings) {
|
public ResourceModelBuilder(RenderSettings renderSettings, ExtendedBlockContext context, BlockColorCalculator colorCalculator) {
|
||||||
this.resource = resource;
|
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.resourcePack = resourcePack;
|
|
||||||
this.renderSettings = renderSettings;
|
this.renderSettings = renderSettings;
|
||||||
|
this.colorCalculator = colorCalculator;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BlockStateModel build() throws NoSuchTextureException {
|
public BlockStateModel build(TransformedBlockModelResource bmr) {
|
||||||
BlockStateModel model = new BlockStateModel();
|
BlockStateModel model = new BlockStateModel();
|
||||||
|
|
||||||
for (WeighedArrayList<BlockModelResource> bmrList : resource.getModelResources()){
|
for (BlockModelResource.Element element : bmr.getModel().getElements()){
|
||||||
BlockModelResource bmr = bmrList.get((int) Math.floor(MathUtils.hashToFloat(context.getPosition(), 23489756) * bmrList.size()));
|
model.merge(fromModelElementResource(element, bmr));
|
||||||
|
}
|
||||||
|
|
||||||
model.merge(fromModelResource(bmr));
|
if (!bmr.getRotation().equals(Vector2i.ZERO)) {
|
||||||
|
model.translate(NEG_HALF_3F);
|
||||||
|
model.rotate(Quaternionf.fromAxesAnglesDeg(
|
||||||
|
-bmr.getRotation().getX(),
|
||||||
|
-bmr.getRotation().getY(),
|
||||||
|
0
|
||||||
|
));
|
||||||
|
model.translate(HALF_3F);
|
||||||
}
|
}
|
||||||
|
|
||||||
return model;
|
return model;
|
||||||
}
|
}
|
||||||
|
|
||||||
private BlockStateModel fromModelResource(BlockModelResource bmr) throws NoSuchTextureException {
|
private BlockStateModel fromModelElementResource(BlockModelResource.Element bmer, TransformedBlockModelResource bmr) {
|
||||||
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();
|
BlockStateModel model = new BlockStateModel();
|
||||||
|
|
||||||
//create faces
|
//create faces
|
||||||
@ -118,28 +103,31 @@ private BlockStateModel fromModelElementResource(BlockModelElementResource bmer)
|
|||||||
new Vector3f( max .getX(), max .getY(), max .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, bmr, bmer, 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, bmr, bmer, 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, bmr, bmer, 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, bmr, bmer, 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, bmr, bmer, Direction.WEST, c[0], c[1], c[5], c[4]);
|
||||||
createElementFace(model, bmer.getEastFace(), Direction.EAST, c[3], c[2], c[6], c[7]);
|
createElementFace(model, bmr, bmer, Direction.EAST, c[3], c[2], c[6], c[7]);
|
||||||
|
|
||||||
//rotate
|
//rotate
|
||||||
if (bmer.isRotation()){
|
Rotation rotation = bmer.getRotation();
|
||||||
Vector3f translation = bmer.getRotationOrigin();
|
if (rotation.getAngle() != 0f){
|
||||||
|
Vector3f translation = rotation.getOrigin();
|
||||||
model.translate(translation.negate());
|
model.translate(translation.negate());
|
||||||
|
|
||||||
|
Vector3f rotAxis = rotation.getAxis().toVector().toFloat();
|
||||||
|
|
||||||
model.rotate(Quaternionf.fromAngleDegAxis(
|
model.rotate(Quaternionf.fromAngleDegAxis(
|
||||||
bmer.getRotationAngle(),
|
rotation.getAngle(),
|
||||||
bmer.getRotationAxis().toVector().toFloat()
|
rotAxis
|
||||||
));
|
));
|
||||||
|
|
||||||
if (bmer.isRotationRescale()){
|
if (rotation.isRescale()){
|
||||||
Vector3f scale =
|
Vector3f scale =
|
||||||
Vector3f.ONE
|
Vector3f.ONE
|
||||||
.sub(bmer.getRotationAxis().toVector().toFloat())
|
.sub(rotAxis)
|
||||||
.mul(Math.abs(TrigMath.sin(bmer.getRotationAngle() * TrigMath.DEG_TO_RAD)))
|
.mul(Math.abs(TrigMath.sin(rotation.getAngle() * TrigMath.DEG_TO_RAD)))
|
||||||
.mul(1 - (TrigMath.SQRT_OF_TWO - 1))
|
.mul(1 - (TrigMath.SQRT_OF_TWO - 1))
|
||||||
.add(Vector3f.ONE);
|
.add(Vector3f.ONE);
|
||||||
model.transform(Matrix3f.createScaling(scale));
|
model.transform(Matrix3f.createScaling(scale));
|
||||||
@ -155,19 +143,20 @@ private BlockStateModel fromModelElementResource(BlockModelElementResource bmer)
|
|||||||
return model;
|
return model;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createElementFace(BlockStateModel model, BlockModelElementFaceResource face, Direction faceDir, Vector3f c0, Vector3f c1, Vector3f c2, Vector3f c3) throws NoSuchTextureException {
|
private void createElementFace(BlockStateModel model, TransformedBlockModelResource modelResource, BlockModelResource.Element element, Direction faceDir, Vector3f c0, Vector3f c1, Vector3f c2, Vector3f c3) {
|
||||||
|
BlockModelResource.Element.Face face = element.getFaces().get(faceDir);
|
||||||
|
|
||||||
if (face == null) return;
|
if (face == null) return;
|
||||||
BlockModelResource m = face.getElement().getModel();
|
|
||||||
|
|
||||||
//face culling
|
//face culling
|
||||||
if (face.isCullface()){
|
if (face.getCullface() != null){
|
||||||
Block b = getRotationRelativeBlock(m, face.getCullface());
|
Block b = getRotationRelativeBlock(modelResource, face.getCullface());
|
||||||
if (b.isCullingNeighborFaces()) return;
|
if (b.isCullingNeighborFaces()) return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//light calculation
|
//light calculation
|
||||||
Block b = getRotationRelativeBlock(m, faceDir);
|
Block b = getRotationRelativeBlock(modelResource, faceDir);
|
||||||
BlockContext bContext = context.getRelativeView(getRotationRelativeDirectionVector(m, faceDir.toVector().toFloat()).toInt());
|
BlockContext bContext = context.getRelativeView(getRotationRelativeDirectionVector(modelResource, faceDir.toVector().toFloat()).toInt());
|
||||||
float skyLight = b.getPassedSunLight(bContext);
|
float skyLight = b.getPassedSunLight(bContext);
|
||||||
|
|
||||||
//filter out faces that are not skylighted
|
//filter out faces that are not skylighted
|
||||||
@ -186,12 +175,13 @@ private void createElementFace(BlockStateModel model, BlockModelElementFaceResou
|
|||||||
|
|
||||||
//UV-Lock counter-rotation
|
//UV-Lock counter-rotation
|
||||||
int uvLockAngle = 0;
|
int uvLockAngle = 0;
|
||||||
if (m.isUvLock()){
|
Vector2i rotation = modelResource.getRotation();
|
||||||
Quaternionf rot = Quaternionf.fromAxesAnglesDeg(m.getXRot(), m.getYRot(), 0);
|
if (modelResource.isUVLock()){
|
||||||
|
Quaternionf rot = Quaternionf.fromAxesAnglesDeg(rotation.getX(), rotation.getY(), 0);
|
||||||
uvLockAngle = (int) rot.getAxesAnglesDeg().dot(faceDir.toVector().toFloat());
|
uvLockAngle = (int) rot.getAxesAnglesDeg().dot(faceDir.toVector().toFloat());
|
||||||
|
|
||||||
//TODO: my math has stopped working, there has to be a more consistent solution
|
//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;
|
if (rotation.getX() >= 180 && rotation.getY() != 90 && rotation.getY() != 270) uvLockAngle += 180;
|
||||||
}
|
}
|
||||||
|
|
||||||
//create both triangles
|
//create both triangles
|
||||||
@ -205,27 +195,25 @@ private void createElementFace(BlockStateModel model, BlockModelElementFaceResou
|
|||||||
uvs = rotateUVOuter(uvs, uvLockAngle);
|
uvs = rotateUVOuter(uvs, uvLockAngle);
|
||||||
uvs = rotateUVInner(uvs, face.getRotation());
|
uvs = rotateUVInner(uvs, face.getRotation());
|
||||||
|
|
||||||
String textureName = face.getResolvedTexture();
|
Texture texture = face.getTexture();
|
||||||
if (textureName == null) throw new NoSuchTextureException("There is no Texture-Definition for a face: " + faceDir + " of block: " + resource.getBlock());
|
int textureId = texture.getId();
|
||||||
|
|
||||||
int textureId = resourcePack.getTextureProvider().getTextureIndex(textureName);
|
|
||||||
|
|
||||||
Face f1 = new Face(c0, c1, c2, uvs[0], uvs[1], uvs[2], textureId);
|
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);
|
Face f2 = new Face(c0, c2, c3, uvs[0], uvs[2], uvs[3], textureId);
|
||||||
|
|
||||||
//calculate ao
|
//calculate ao
|
||||||
double ao0 = 1d, ao1 = 1d, ao2 = 1d, ao3 = 1d;
|
double ao0 = 1d, ao1 = 1d, ao2 = 1d, ao3 = 1d;
|
||||||
if (renderSettings.getAmbientOcclusionStrenght() > 0f && m.isAmbientOcclusion()){
|
if (renderSettings.getAmbientOcclusionStrenght() > 0f && modelResource.getModel().isAmbientOcclusion()){
|
||||||
ao0 = testAo(m, c0, faceDir);
|
ao0 = testAo(modelResource, c0, faceDir);
|
||||||
ao1 = testAo(m, c1, faceDir);
|
ao1 = testAo(modelResource, c1, faceDir);
|
||||||
ao2 = testAo(m, c2, faceDir);
|
ao2 = testAo(modelResource, c2, faceDir);
|
||||||
ao3 = testAo(m, c3, faceDir);
|
ao3 = testAo(modelResource, c3, faceDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
//tint the face
|
//tint the face
|
||||||
Vector3f color = Vector3f.ONE;
|
Vector3f color = Vector3f.ONE;
|
||||||
if (face.isTinted()){
|
if (face.isTinted()){
|
||||||
color = resourcePack.getBlockColorProvider().getBlockColor(context);
|
color = colorCalculator.getBlockColor(context); //TODO: cache this so we don't recalculate the tint color again for each face?
|
||||||
}
|
}
|
||||||
|
|
||||||
color = color.mul(light);
|
color = color.mul(light);
|
||||||
@ -251,50 +239,46 @@ private void createElementFace(BlockStateModel model, BlockModelElementFaceResou
|
|||||||
model.addFace(f2);
|
model.addFace(f2);
|
||||||
|
|
||||||
//if is top face set model-color
|
//if is top face set model-color
|
||||||
Vector3f dir = getRotationRelativeDirectionVector(m, faceDir.toVector().toFloat());
|
Vector3f dir = getRotationRelativeDirectionVector(modelResource, faceDir.toVector().toFloat());
|
||||||
|
|
||||||
BlockModelElementResource bmer = face.getElement();
|
if (element.getRotation().getAngle() > 0){
|
||||||
if (bmer.isRotation()){
|
|
||||||
Quaternionf rot = Quaternionf.fromAngleDegAxis(
|
Quaternionf rot = Quaternionf.fromAngleDegAxis(
|
||||||
bmer.getRotationAngle(),
|
element.getRotation().getAngle(),
|
||||||
bmer.getRotationAxis().toVector().toFloat()
|
element.getRotation().getAxis().toVector().toFloat()
|
||||||
);
|
);
|
||||||
dir = rot.rotate(dir);
|
dir = rot.rotate(dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
float a = dir.getY();
|
float a = dir.getY();
|
||||||
if (a > 0){
|
if (a > 0){
|
||||||
Texture t = resourcePack.getTextureProvider().getTexture(textureId);
|
Vector4f c = texture.getColor();
|
||||||
if (t != null){
|
c = c.mul(color.toVector4(1f));
|
||||||
Vector4f c = t.getColor();
|
c = new Vector4f(c.getX(), c.getY(), c.getZ(), c.getW() * a);
|
||||||
c = c.mul(color.toVector4(1f));
|
model.mergeMapColor(c);
|
||||||
c = new Vector4f(c.getX(), c.getY(), c.getZ(), c.getW() * a);
|
|
||||||
model.mergeMapColor(c);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Block getRotationRelativeBlock(BlockModelResource model, Direction direction){
|
private Block getRotationRelativeBlock(TransformedBlockModelResource model, Direction direction){
|
||||||
return getRotationRelativeBlock(model, direction.toVector());
|
return getRotationRelativeBlock(model, direction.toVector());
|
||||||
}
|
}
|
||||||
|
|
||||||
private Block getRotationRelativeBlock(BlockModelResource model, Vector3i direction){
|
private Block getRotationRelativeBlock(TransformedBlockModelResource model, Vector3i direction){
|
||||||
Vector3i dir = getRotationRelativeDirectionVector(model, direction.toFloat()).round().toInt();
|
Vector3i dir = getRotationRelativeDirectionVector(model, direction.toFloat()).round().toInt();
|
||||||
return context.getRelativeBlock(dir);
|
return context.getRelativeBlock(dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Vector3f getRotationRelativeDirectionVector(BlockModelResource model, Vector3f direction){
|
private Vector3f getRotationRelativeDirectionVector(TransformedBlockModelResource model, Vector3f direction){
|
||||||
Quaternionf rot = Quaternionf.fromAxesAnglesDeg(
|
Quaternionf rot = Quaternionf.fromAxesAnglesDeg(
|
||||||
-model.getXRot(),
|
-model.getRotation().getX(),
|
||||||
-model.getYRot(),
|
-model.getRotation().getY(),
|
||||||
0
|
0
|
||||||
);
|
);
|
||||||
Vector3f dir = rot.rotate(direction);
|
Vector3f dir = rot.rotate(direction);
|
||||||
return dir;
|
return dir;
|
||||||
}
|
}
|
||||||
|
|
||||||
private double testAo(BlockModelResource model, Vector3f vertex, Direction dir){
|
private double testAo(TransformedBlockModelResource model, Vector3f vertex, Direction dir){
|
||||||
int occluding = 0;
|
int occluding = 0;
|
||||||
|
|
||||||
int x = 0;
|
int x = 0;
|
||||||
|
@ -0,0 +1,197 @@
|
|||||||
|
/*
|
||||||
|
* 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.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
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.BlockContext;
|
||||||
|
import de.bluecolored.bluemap.core.util.ConfigUtils;
|
||||||
|
import de.bluecolored.bluemap.core.util.MathUtils;
|
||||||
|
import de.bluecolored.bluemap.core.world.Biome;
|
||||||
|
import de.bluecolored.bluemap.core.world.Block;
|
||||||
|
import ninja.leaping.configurate.ConfigurationNode;
|
||||||
|
import ninja.leaping.configurate.gson.GsonConfigurationLoader;
|
||||||
|
|
||||||
|
public class BlockColorCalculator {
|
||||||
|
|
||||||
|
private BufferedImage foliageMap;
|
||||||
|
private BufferedImage grassMap;
|
||||||
|
|
||||||
|
private Map<String, Function<BlockContext, Vector3f>> blockColorMap;
|
||||||
|
|
||||||
|
public BlockColorCalculator(BufferedImage foliageMap, BufferedImage grassMap) {
|
||||||
|
this.foliageMap = foliageMap;
|
||||||
|
this.grassMap = grassMap;
|
||||||
|
|
||||||
|
this.blockColorMap = new HashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void loadColorConfig(File configFile) throws IOException {
|
||||||
|
blockColorMap.clear();
|
||||||
|
|
||||||
|
ConfigurationNode colorConfig = GsonConfigurationLoader.builder()
|
||||||
|
.setFile(configFile)
|
||||||
|
.build()
|
||||||
|
.load();
|
||||||
|
|
||||||
|
for (Entry<Object, ? extends ConfigurationNode> entry : colorConfig.getChildrenMap().entrySet()){
|
||||||
|
String key = entry.getKey().toString();
|
||||||
|
String value = entry.getValue().getString();
|
||||||
|
|
||||||
|
Function<BlockContext, Vector3f> colorFunction;
|
||||||
|
switch (value) {
|
||||||
|
case "@foliage":
|
||||||
|
colorFunction = this::getFoliageAverageColor;
|
||||||
|
break;
|
||||||
|
case "@grass":
|
||||||
|
colorFunction = this::getGrassAverageColor;
|
||||||
|
break;
|
||||||
|
case "@water":
|
||||||
|
colorFunction = this::getWaterAverageColor;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
final Vector3f color = MathUtils.color3FromInt(ConfigUtils.readInt(entry.getValue()));
|
||||||
|
colorFunction = context -> color;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
blockColorMap.put(key, colorFunction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector3f getBlockColor(BlockContext context){
|
||||||
|
Block block = context.getRelativeBlock(0, 0, 0);
|
||||||
|
String blockId = block.getBlock().getFullId();
|
||||||
|
|
||||||
|
Function<BlockContext, Vector3f> colorFunction = blockColorMap.get(blockId);
|
||||||
|
if (colorFunction == null) colorFunction = blockColorMap.get("default");
|
||||||
|
if (colorFunction == null) colorFunction = this::getFoliageAverageColor;
|
||||||
|
|
||||||
|
return colorFunction.apply(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector3f getWaterAverageColor(BlockContext context){
|
||||||
|
Vector3f color = Vector3f.ZERO;
|
||||||
|
|
||||||
|
for (int x = -1; x <= 1; x++){
|
||||||
|
for (int z = -1; z <= 1; z++){
|
||||||
|
color = color.add(context.getRelativeBlock(x, 0, z).getBiome().getWaterColor());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return color.div(9f);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector3f getFoliageAverageColor(BlockContext context){
|
||||||
|
Vector3f color = Vector3f.ZERO;
|
||||||
|
|
||||||
|
for (int x = -1; x <= 1; x++){
|
||||||
|
for (int z = -1; z <= 1; z++){
|
||||||
|
color = color.add(getFoliageColor(context.getRelativeBlock(x, 0, z)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return color.div(9f);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector3f getFoliageColor(Block block){
|
||||||
|
int blocksAboveSeaLevel = Math.max(block.getPosition().getY() - block.getWorld().getSeaLevel(), 0);
|
||||||
|
return getFoliageColor(block.getBiome(), blocksAboveSeaLevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector3f getFoliageColor(Biome biome, int blocksAboveSeaLevel){
|
||||||
|
Vector3f mapColor = getColorFromMap(biome, blocksAboveSeaLevel, foliageMap);
|
||||||
|
Vector3f overlayColor = biome.getOverlayFoliageColor().toVector3();
|
||||||
|
float overlayAlpha = biome.getOverlayFoliageColor().getW();
|
||||||
|
return mapColor.mul(1f - overlayAlpha).add(overlayColor.mul(overlayAlpha));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector3f getGrassAverageColor(BlockContext context){
|
||||||
|
Vector3f color = Vector3f.ZERO;
|
||||||
|
|
||||||
|
for (int x = -1; x <= 1; x++){
|
||||||
|
for (int z = -1; z <= 1; z++){
|
||||||
|
color = color.add(getGrassColor(context.getRelativeBlock(x, 0, z)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return color.div(9f);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector3f getGrassColor(Block block){
|
||||||
|
int blocksAboveSeaLevel = Math.max(block.getPosition().getY() - block.getWorld().getSeaLevel(), 0);
|
||||||
|
return getGrassColor(block.getBiome(), blocksAboveSeaLevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector3f getGrassColor(Biome biome, int blocksAboveSeaLevel){
|
||||||
|
Vector3f mapColor = getColorFromMap(biome, blocksAboveSeaLevel, grassMap);
|
||||||
|
Vector3f overlayColor = biome.getOverlayGrassColor().toVector3();
|
||||||
|
float overlayAlpha = biome.getOverlayGrassColor().getW();
|
||||||
|
return mapColor.mul(1f - overlayAlpha).add(overlayColor.mul(overlayAlpha));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Vector3f getColorFromMap(Biome biome, int blocksAboveSeaLevel, BufferedImage map){
|
||||||
|
Vector2i pixel = getColorMapPosition(biome, 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(Biome biome, int blocksAboveSeaLevel){
|
||||||
|
float adjTemp = (float) GenericMath.clamp(biome.getTemp() - (0.00166667 * blocksAboveSeaLevel), 0d, 1d);
|
||||||
|
float adjHumidity = (float) GenericMath.clamp(biome.getHumidity(), 0d, 1d) * adjTemp;
|
||||||
|
return new Vector2f(1 - adjTemp, 1 - adjHumidity);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BufferedImage getFoliageMap() {
|
||||||
|
return foliageMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFoliageMap(BufferedImage foliageMap) {
|
||||||
|
this.foliageMap = foliageMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BufferedImage getGrassMap() {
|
||||||
|
return grassMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setGrassMap(BufferedImage grassMap) {
|
||||||
|
this.grassMap = grassMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,239 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 * 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;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,142 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.ConfigUtils;
|
|
||||||
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 = ConfigUtils.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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,144 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.ConfigUtils;
|
|
||||||
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 = ConfigUtils.readVector3f(declaration.getNode("from"));
|
|
||||||
this.to = ConfigUtils.readVector3f(declaration.getNode("to"));
|
|
||||||
|
|
||||||
this.rotAngle = 0f;
|
|
||||||
ConfigurationNode rotation = declaration.getNode("rotation");
|
|
||||||
if (!rotation.isVirtual()){
|
|
||||||
this.rotOrigin = ConfigUtils.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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -25,118 +25,358 @@
|
|||||||
package de.bluecolored.bluemap.core.resourcepack;
|
package de.bluecolored.bluemap.core.resourcepack;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.nio.file.Paths;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.EnumMap;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
import java.util.Vector;
|
import java.util.NoSuchElementException;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
|
|
||||||
|
import com.flowpowered.math.vector.Vector3f;
|
||||||
|
import com.flowpowered.math.vector.Vector4f;
|
||||||
|
|
||||||
|
import de.bluecolored.bluemap.core.logger.Logger;
|
||||||
|
import de.bluecolored.bluemap.core.resourcepack.BlockModelResource.Element.Face;
|
||||||
|
import de.bluecolored.bluemap.core.resourcepack.fileaccess.FileAccess;
|
||||||
|
import de.bluecolored.bluemap.core.util.Axis;
|
||||||
|
import de.bluecolored.bluemap.core.util.Direction;
|
||||||
import ninja.leaping.configurate.ConfigurationNode;
|
import ninja.leaping.configurate.ConfigurationNode;
|
||||||
import ninja.leaping.configurate.gson.GsonConfigurationLoader;
|
import ninja.leaping.configurate.gson.GsonConfigurationLoader;
|
||||||
|
|
||||||
public class BlockModelResource {
|
public class BlockModelResource {
|
||||||
|
|
||||||
private BlockStateResource blockState;
|
private ModelType modelType = ModelType.NORMAL;
|
||||||
|
|
||||||
private int xRot, yRot;
|
private boolean ambientOcclusion = true;
|
||||||
private boolean uvLock;
|
private Collection<Element> elements = new ArrayList<>();
|
||||||
private boolean ambientOcclusion;
|
private Map<String, Texture> textures = new HashMap<>();
|
||||||
private Collection<BlockModelElementResource> elements;
|
|
||||||
private Map<String, String> textures;
|
|
||||||
|
|
||||||
protected BlockModelResource(BlockStateResource blockState, ConfigurationNode declaration, ResourcePack resources) throws InvalidResourceDeclarationException {
|
private BlockModelResource() {}
|
||||||
this.blockState = blockState;
|
|
||||||
|
|
||||||
this.xRot = declaration.getNode("x").getInt(0);
|
public ModelType getType() {
|
||||||
this.yRot = declaration.getNode("y").getInt(0);
|
return modelType;
|
||||||
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() {
|
public boolean isAmbientOcclusion() {
|
||||||
return ambientOcclusion;
|
return ambientOcclusion;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Collection<BlockModelElementResource> getElements() {
|
public Collection<Element> getElements() {
|
||||||
return Collections.unmodifiableCollection(elements);
|
return elements;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String resolveTexture(String key){
|
public Texture getTexture(String key) {
|
||||||
if (key == null) return null;
|
return textures.get(key);
|
||||||
if (!key.startsWith("#")) return key;
|
|
||||||
String texture = textures.get(key.substring(1));
|
|
||||||
if (texture == null) return key;
|
|
||||||
return resolveTexture(texture);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Collection<String> getAllTextureIds(){
|
public class Element {
|
||||||
List<String> list = new ArrayList<>();
|
|
||||||
for (String tex : textures.values()){
|
private Vector3f from = Vector3f.ZERO, to = new Vector3f(16f, 16f, 16f);
|
||||||
if (!tex.startsWith("#")) list.add(tex);
|
private Rotation rotation = new Rotation();
|
||||||
|
private boolean shade = true;
|
||||||
|
private EnumMap<Direction, Face> faces = new EnumMap<>(Direction.class);
|
||||||
|
|
||||||
|
private Element() {}
|
||||||
|
|
||||||
|
public Vector4f getDefaultUV(Direction face) {
|
||||||
|
switch (face){
|
||||||
|
|
||||||
|
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
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return list;
|
|
||||||
|
public BlockModelResource getModel() {
|
||||||
|
return BlockModelResource.this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector3f getFrom() {
|
||||||
|
return from;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector3f getTo() {
|
||||||
|
return to;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rotation getRotation() {
|
||||||
|
return rotation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isShade() {
|
||||||
|
return shade;
|
||||||
|
}
|
||||||
|
|
||||||
|
public EnumMap<Direction, Face> getFaces() {
|
||||||
|
return faces;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Face {
|
||||||
|
|
||||||
|
private Vector4f uv;
|
||||||
|
private Texture texture;
|
||||||
|
private Direction cullface;
|
||||||
|
private int rotation = 0;
|
||||||
|
private boolean tinted = false;
|
||||||
|
|
||||||
|
private Face(Direction dir) {
|
||||||
|
uv = getDefaultUV(dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Element getElement() {
|
||||||
|
return Element.this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector4f getUv() {
|
||||||
|
return uv;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Texture getTexture() {
|
||||||
|
return texture;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Direction getCullface() {
|
||||||
|
return cullface;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getRotation() {
|
||||||
|
return rotation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isTinted() {
|
||||||
|
return tinted;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Rotation {
|
||||||
|
|
||||||
|
private Vector3f origin = new Vector3f(8, 8, 8);
|
||||||
|
private Axis axis = Axis.Y;
|
||||||
|
private float angle = 0;
|
||||||
|
private boolean rescale = false;
|
||||||
|
|
||||||
|
private Rotation() {}
|
||||||
|
|
||||||
|
public Vector3f getOrigin() {
|
||||||
|
return origin;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Axis getAxis() {
|
||||||
|
return axis;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getAngle() {
|
||||||
|
return angle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isRescale() {
|
||||||
|
return rescale;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Builder builder(FileAccess sourcesAccess, ResourcePack resourcePack) {
|
||||||
|
return new Builder(sourcesAccess, resourcePack);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Builder {
|
||||||
|
|
||||||
|
private FileAccess sourcesAccess;
|
||||||
|
private ResourcePack resourcePack;
|
||||||
|
|
||||||
|
private HashMap<String, String> textures;
|
||||||
|
|
||||||
|
private Builder(FileAccess sourcesAccess, ResourcePack resourcePack) {
|
||||||
|
this.sourcesAccess = sourcesAccess;
|
||||||
|
this.resourcePack = resourcePack;
|
||||||
|
|
||||||
|
this.textures = new HashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized BlockModelResource build(String modelPath) throws IOException, ParseResourceException {
|
||||||
|
textures.clear();
|
||||||
|
return buildNoReset(modelPath, true, modelPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
private BlockModelResource buildNoReset(String modelPath, boolean renderElements, String topModelPath) throws IOException, ParseResourceException {
|
||||||
|
BlockModelResource blockModel = new BlockModelResource();
|
||||||
|
ConfigurationNode config = GsonConfigurationLoader.builder()
|
||||||
|
.setSource(() -> new BufferedReader(new InputStreamReader(sourcesAccess.readFile(modelPath), StandardCharsets.UTF_8)))
|
||||||
|
.build()
|
||||||
|
.load();
|
||||||
|
|
||||||
|
for (Entry<Object, ? extends ConfigurationNode> entry : config.getNode("textures").getChildrenMap().entrySet()) {
|
||||||
|
textures.putIfAbsent(entry.getKey().toString(), entry.getValue().getString(null));
|
||||||
|
}
|
||||||
|
|
||||||
|
String parentPath = config.getNode("parent").getString();
|
||||||
|
if (parentPath != null) {
|
||||||
|
if (parentPath.startsWith("builtin")) {
|
||||||
|
switch (parentPath) {
|
||||||
|
case "builtin/liquid":
|
||||||
|
blockModel.modelType = ModelType.LIQUID;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
parentPath = ResourcePack.namespacedToAbsoluteResourcePath(parentPath, "models") + ".json";
|
||||||
|
blockModel = this.buildNoReset(parentPath, config.getNode("elements").isVirtual(), topModelPath);
|
||||||
|
} catch (IOException ex) {
|
||||||
|
Logger.global.logWarning("Failed to load parent model " + parentPath + " of model " + topModelPath + ": " + ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (renderElements) {
|
||||||
|
for (ConfigurationNode elementNode : config.getNode("elements").getChildrenList()) {
|
||||||
|
try {
|
||||||
|
blockModel.elements.add(buildElement(blockModel, elementNode, topModelPath));
|
||||||
|
} catch (ParseResourceException ex) {
|
||||||
|
Logger.global.logWarning("Failed to parse element of model " + modelPath + " (" + topModelPath + "): " + ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (String key : textures.keySet()) {
|
||||||
|
try {
|
||||||
|
blockModel.textures.put(key, getTexture("#" + key));
|
||||||
|
} catch (NoSuchElementException | FileNotFoundException ex) {
|
||||||
|
Logger.global.logDebug("Failed to map Texture key '" + key + "': " + ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return blockModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Element buildElement(BlockModelResource model, ConfigurationNode node, String topModelPath) throws ParseResourceException {
|
||||||
|
Element element = model.new Element();
|
||||||
|
|
||||||
|
element.from = readVector3f(node.getNode("from"));
|
||||||
|
element.to = readVector3f(node.getNode("to"));
|
||||||
|
|
||||||
|
element.shade = node.getNode("shade").getBoolean(false);
|
||||||
|
|
||||||
|
if (!node.getNode("rotation").isVirtual()) {
|
||||||
|
element.rotation.angle = node.getNode("rotation", "angle").getFloat(0);
|
||||||
|
element.rotation.axis = Axis.fromString(node.getNode("rotation", "axis").getString("x"));
|
||||||
|
if (!node.getNode("rotation", "origin").isVirtual()) element.rotation.origin = readVector3f(node.getNode("rotation", "origin"));
|
||||||
|
element.rotation.rescale = node.getNode("rotation", "rescale").getBoolean(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Direction direction : Direction.values()) {
|
||||||
|
ConfigurationNode faceNode = node.getNode("faces", direction.name().toLowerCase());
|
||||||
|
if (!faceNode.isVirtual()) {
|
||||||
|
try {
|
||||||
|
Face face = buildFace(element, direction, faceNode);
|
||||||
|
element.faces.put(direction, face);
|
||||||
|
} catch (ParseResourceException | IOException ex) {
|
||||||
|
Logger.global.logDebug("Failed to parse an " + direction + " face for the model " + topModelPath + "! " + ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Face buildFace(Element element, Direction direction, ConfigurationNode node) throws ParseResourceException, IOException {
|
||||||
|
try {
|
||||||
|
Face face = element.new Face(direction);
|
||||||
|
|
||||||
|
if (!node.getNode("uv").isVirtual()) face.uv = readVector4f(node.getNode("uv"));
|
||||||
|
face.texture = getTexture(node.getNode("texture").getString());
|
||||||
|
face.tinted = node.getNode("tintindex").getInt(-1) >= 0;
|
||||||
|
face.rotation = node.getNode("rotation").getInt(0);
|
||||||
|
|
||||||
|
if (!node.getNode("cullface").isVirtual()) {
|
||||||
|
String dirString = node.getNode("cullface").getString();
|
||||||
|
if (dirString.equals("bottom")) dirString = "down";
|
||||||
|
if (dirString.equals("top")) dirString = "up";
|
||||||
|
face.cullface = Direction.fromString(dirString);
|
||||||
|
}
|
||||||
|
|
||||||
|
return face;
|
||||||
|
} catch (FileNotFoundException ex) {
|
||||||
|
throw new ParseResourceException("There is no texture with the path: " + node.getNode("texture").getString(), ex);
|
||||||
|
} catch (NoSuchElementException ex) {
|
||||||
|
throw new ParseResourceException("Texture key '" + node.getNode("texture").getString() + "' has no texture assigned!", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Vector3f readVector3f(ConfigurationNode node) throws ParseResourceException {
|
||||||
|
List<? extends ConfigurationNode> nodeList = node.getChildrenList();
|
||||||
|
if (nodeList.size() < 3) throw new ParseResourceException("Failed to load Vector3: Not enough values in list-node!");
|
||||||
|
|
||||||
|
return new Vector3f(
|
||||||
|
nodeList.get(0).getFloat(0),
|
||||||
|
nodeList.get(1).getFloat(0),
|
||||||
|
nodeList.get(2).getFloat(0)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Vector4f readVector4f(ConfigurationNode node) throws ParseResourceException {
|
||||||
|
List<? extends ConfigurationNode> nodeList = node.getChildrenList();
|
||||||
|
if (nodeList.size() < 4) throw new ParseResourceException("Failed to load Vector4: Not enough values in list-node!");
|
||||||
|
|
||||||
|
return new Vector4f(
|
||||||
|
nodeList.get(0).getFloat(0),
|
||||||
|
nodeList.get(1).getFloat(0),
|
||||||
|
nodeList.get(2).getFloat(0),
|
||||||
|
nodeList.get(3).getFloat(0)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Texture getTexture(String key) throws NoSuchElementException, FileNotFoundException, IOException {
|
||||||
|
if (key.charAt(0) == '#') {
|
||||||
|
String value = textures.get(key.substring(1));
|
||||||
|
if (value == null) throw new NoSuchElementException("There is no texture defined for the key " + key);
|
||||||
|
return getTexture(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
String path = ResourcePack.namespacedToAbsoluteResourcePath(key, "textures") + ".png";
|
||||||
|
|
||||||
|
Texture texture;
|
||||||
|
try {
|
||||||
|
texture = resourcePack.textures.get(path);
|
||||||
|
} catch (NoSuchElementException ex) {
|
||||||
|
texture = resourcePack.textures.loadTexture(sourcesAccess, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
return texture;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -28,127 +28,231 @@
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.nio.file.Path;
|
import java.util.ArrayList;
|
||||||
import java.nio.file.Paths;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
import java.util.Vector;
|
|
||||||
|
|
||||||
import com.google.common.base.Preconditions;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import de.bluecolored.bluemap.core.util.WeighedArrayList;
|
import com.flowpowered.math.vector.Vector2i;
|
||||||
|
import com.flowpowered.math.vector.Vector3i;
|
||||||
|
|
||||||
|
import de.bluecolored.bluemap.core.logger.Logger;
|
||||||
|
import de.bluecolored.bluemap.core.resourcepack.fileaccess.FileAccess;
|
||||||
|
import de.bluecolored.bluemap.core.util.MathUtils;
|
||||||
import de.bluecolored.bluemap.core.world.BlockState;
|
import de.bluecolored.bluemap.core.world.BlockState;
|
||||||
import ninja.leaping.configurate.ConfigurationNode;
|
import ninja.leaping.configurate.ConfigurationNode;
|
||||||
import ninja.leaping.configurate.gson.GsonConfigurationLoader;
|
import ninja.leaping.configurate.gson.GsonConfigurationLoader;
|
||||||
|
|
||||||
public class BlockStateResource {
|
public class BlockStateResource {
|
||||||
private BlockState block;
|
|
||||||
private Collection<WeighedArrayList<BlockModelResource>> modelResources;
|
|
||||||
|
|
||||||
protected BlockStateResource(BlockState block, ResourcePack resources) throws NoSuchResourceException, InvalidResourceDeclarationException {
|
private List<Variant> variants = new ArrayList<>();
|
||||||
this.block = Preconditions.checkNotNull(block);
|
private Collection<Variant> multipart = new ArrayList<>();
|
||||||
this.modelResources = new Vector<>();
|
|
||||||
|
|
||||||
try {
|
private BlockStateResource() {}
|
||||||
ConfigurationNode data = GsonConfigurationLoader.builder()
|
|
||||||
.setSource(() -> new BufferedReader(new InputStreamReader(resources.getResource(getResourcePath()), StandardCharsets.UTF_8)))
|
public Collection<TransformedBlockModelResource> getModels(BlockState blockState){
|
||||||
|
return getModels(blockState, Vector3i.ZERO);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Collection<TransformedBlockModelResource> getModels(BlockState blockState, Vector3i pos){
|
||||||
|
Collection<TransformedBlockModelResource> models = new ArrayList<>();
|
||||||
|
for (Variant variant : variants) {
|
||||||
|
if (variant.condition.matches(blockState)) {
|
||||||
|
models.add(variant.getModel(pos));
|
||||||
|
return models;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Variant variant : multipart) {
|
||||||
|
if (variant.condition.matches(blockState)) {
|
||||||
|
models.add(variant.getModel(pos));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return models;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class Variant {
|
||||||
|
|
||||||
|
private PropertyCondition condition = PropertyCondition.all();
|
||||||
|
private Collection<Weighted<TransformedBlockModelResource>> models = new ArrayList<>();
|
||||||
|
|
||||||
|
private double totalWeight;
|
||||||
|
|
||||||
|
private Variant() {}
|
||||||
|
|
||||||
|
public TransformedBlockModelResource getModel(Vector3i pos) {
|
||||||
|
double selection = MathUtils.hashToFloat(pos, 827364) * totalWeight; //random based on position
|
||||||
|
for (Weighted<TransformedBlockModelResource> w : models) {
|
||||||
|
selection -= w.weight;
|
||||||
|
if (selection < 0) return w.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new RuntimeException("This line should never be reached!");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateTotalWeight() {
|
||||||
|
totalWeight = 0d;
|
||||||
|
for (Weighted<?> w : models) {
|
||||||
|
totalWeight += w.weight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class Weighted<T> {
|
||||||
|
|
||||||
|
private T value;
|
||||||
|
private double weight;
|
||||||
|
|
||||||
|
public Weighted(T value, double weight) {
|
||||||
|
this.value = value;
|
||||||
|
this.weight = weight;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Builder builder(FileAccess sourcesAccess, ResourcePack resourcePack) {
|
||||||
|
return new Builder(sourcesAccess, resourcePack);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Builder {
|
||||||
|
private final FileAccess sourcesAccess;
|
||||||
|
private final ResourcePack resourcePack;
|
||||||
|
|
||||||
|
private Builder(FileAccess sourcesAccess, ResourcePack resourcePack) {
|
||||||
|
this.sourcesAccess = sourcesAccess;
|
||||||
|
this.resourcePack = resourcePack;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BlockStateResource build(String blockstateFile) throws IOException {
|
||||||
|
BlockStateResource blockState = new BlockStateResource();
|
||||||
|
ConfigurationNode config = GsonConfigurationLoader.builder()
|
||||||
|
.setSource(() -> new BufferedReader(new InputStreamReader(sourcesAccess.readFile(blockstateFile), StandardCharsets.UTF_8)))
|
||||||
.build()
|
.build()
|
||||||
.load();
|
.load();
|
||||||
|
|
||||||
load(data, resources);
|
//create variants
|
||||||
} catch (IOException e) {
|
for (Entry<Object, ? extends ConfigurationNode> entry : config.getNode("variants").getChildrenMap().entrySet()) {
|
||||||
throw new NoSuchResourceException("There is no definition for resource-id: " + block.getId(), e);
|
String conditionString = entry.getKey().toString();
|
||||||
} catch (NullPointerException e){
|
ConfigurationNode transformedModelNode = entry.getValue();
|
||||||
throw new InvalidResourceDeclarationException(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.modelResources = Collections.unmodifiableCollection(this.modelResources);
|
Variant variant = blockState.new Variant();
|
||||||
}
|
variant.condition = parseConditionString(conditionString);
|
||||||
|
variant.models = loadModels(transformedModelNode, blockstateFile);
|
||||||
|
|
||||||
private void load(ConfigurationNode data, ResourcePack resources) throws InvalidResourceDeclarationException {
|
variant.updateTotalWeight();
|
||||||
|
|
||||||
//load variants
|
blockState.variants.add(variant);
|
||||||
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 {
|
//create multipart
|
||||||
models.add(new BlockModelResource(this, n, resources));
|
for (ConfigurationNode partNode : config.getNode("multipart").getChildrenList()) {
|
||||||
}
|
Variant variant = blockState.new Variant();
|
||||||
|
ConfigurationNode whenNode = partNode.getNode("when");
|
||||||
|
if (!whenNode.isVirtual()) {
|
||||||
|
variant.condition = parseCondition(whenNode);
|
||||||
|
}
|
||||||
|
variant.models = loadModels(partNode.getNode("apply"), blockstateFile);
|
||||||
|
|
||||||
modelResources.add(models);
|
variant.updateTotalWeight();
|
||||||
}
|
|
||||||
|
|
||||||
private boolean checkMultipartCondition(ConfigurationNode when){
|
blockState.multipart.add(variant);
|
||||||
ConfigurationNode or = when.getNode("OR");
|
|
||||||
if (!or.isVirtual()){
|
|
||||||
for (ConfigurationNode condition : or.getChildrenList()){
|
|
||||||
if (checkMultipartCondition(condition)) return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return blockState;
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, String> blockProperties = getBlock().getProperties();
|
private Collection<Weighted<TransformedBlockModelResource>> loadModels(ConfigurationNode node, String blockstateFile) {
|
||||||
for (Entry<Object, ? extends ConfigurationNode> e : when.getChildrenMap().entrySet()){
|
Collection<Weighted<TransformedBlockModelResource>> models = new ArrayList<>();
|
||||||
String key = e.getKey().toString();
|
|
||||||
String[] values = e.getValue().getString().split("\\|");
|
|
||||||
|
|
||||||
boolean found = false;
|
if (node.hasListChildren()) {
|
||||||
for (String value : values){
|
for (ConfigurationNode modelNode : node.getChildrenList()) {
|
||||||
if (value.equals(blockProperties.get(key))){
|
try {
|
||||||
found = true;
|
models.add(loadModel(modelNode));
|
||||||
break;
|
} catch (ParseResourceException ex) {
|
||||||
|
Logger.global.logWarning("Failed to load a model trying to parse " + blockstateFile + ": " + ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (node.hasMapChildren()) {
|
||||||
|
try {
|
||||||
|
models.add(loadModel(node));
|
||||||
|
} catch (ParseResourceException ex) {
|
||||||
|
Logger.global.logWarning("Failed to load a model trying to parse " + blockstateFile + ": " + ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!found) return false;
|
return models;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
private Weighted<TransformedBlockModelResource> loadModel(ConfigurationNode node) throws ParseResourceException {
|
||||||
}
|
String modelPath = node.getNode("model").getString();
|
||||||
|
if (modelPath == null) throw new ParseResourceException("No model defined!");
|
||||||
|
|
||||||
public BlockState getBlock() {
|
modelPath = ResourcePack.namespacedToAbsoluteResourcePath(modelPath, "models") + ".json";
|
||||||
return block;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Collection<WeighedArrayList<BlockModelResource>> getModelResources(){
|
BlockModelResource model = resourcePack.blockModelResources.get(modelPath);
|
||||||
return modelResources;
|
if (model == null) {
|
||||||
}
|
try {
|
||||||
|
model = BlockModelResource.builder(sourcesAccess, resourcePack).build(modelPath);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new ParseResourceException("Failed to load model " + modelPath, e);
|
||||||
|
}
|
||||||
|
|
||||||
private Path getResourcePath(){
|
resourcePack.blockModelResources.put(modelPath, model);
|
||||||
return Paths.get("assets", block.getNamespace(), "blockstates", block.getId() + ".json");
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
Vector2i rotation = new Vector2i(
|
||||||
|
node.getNode("x").getInt(0),
|
||||||
|
node.getNode("y").getInt(0)
|
||||||
|
);
|
||||||
|
boolean uvLock = node.getNode("uvlock").getBoolean(false);
|
||||||
|
|
||||||
|
TransformedBlockModelResource transformedModel = new TransformedBlockModelResource(rotation, uvLock, model);
|
||||||
|
return new Weighted<TransformedBlockModelResource>(transformedModel, node.getNode("weight").getDouble(1d));
|
||||||
|
}
|
||||||
|
|
||||||
|
private PropertyCondition parseCondition(ConfigurationNode conditionNode) {
|
||||||
|
List<PropertyCondition> andConditions = new ArrayList<>();
|
||||||
|
for (Entry<Object, ? extends ConfigurationNode> entry : conditionNode.getChildrenMap().entrySet()) {
|
||||||
|
String key = entry.getKey().toString();
|
||||||
|
if (key.equals("OR")) {
|
||||||
|
List<PropertyCondition> orConditions = new ArrayList<>();
|
||||||
|
for (ConfigurationNode orConditionNode : entry.getValue().getChildrenList()) {
|
||||||
|
orConditions.add(parseCondition(orConditionNode));
|
||||||
|
}
|
||||||
|
andConditions.add(PropertyCondition.or(orConditions.toArray(new PropertyCondition[orConditions.size()])));
|
||||||
|
} else {
|
||||||
|
String[] values = StringUtils.split(entry.getValue().getString(""), '|');
|
||||||
|
andConditions.add(PropertyCondition.property(key, values));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return PropertyCondition.and(andConditions.toArray(new PropertyCondition[andConditions.size()]));
|
||||||
|
}
|
||||||
|
|
||||||
|
private PropertyCondition parseConditionString(String conditionString) {
|
||||||
|
List<PropertyCondition> conditions = new ArrayList<>();
|
||||||
|
if (!conditionString.isEmpty() && !conditionString.equals("default")) {
|
||||||
|
String[] conditionSplit = StringUtils.split(conditionString, ',');
|
||||||
|
for (String element : conditionSplit) {
|
||||||
|
String[] keyval = StringUtils.split(element, "=", 2); //TODO what if it is wrong formatted?
|
||||||
|
conditions.add(PropertyCondition.property(keyval[0], keyval[1]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PropertyCondition condition;
|
||||||
|
if (conditions.isEmpty()) {
|
||||||
|
condition = PropertyCondition.all();
|
||||||
|
} else if (conditions.size() == 1) {
|
||||||
|
condition = conditions.get(0);
|
||||||
|
} else {
|
||||||
|
condition = PropertyCondition.and(conditions.toArray(new PropertyCondition[conditions.size()]));
|
||||||
|
}
|
||||||
|
|
||||||
|
return condition;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,20 +24,9 @@
|
|||||||
*/
|
*/
|
||||||
package de.bluecolored.bluemap.core.resourcepack;
|
package de.bluecolored.bluemap.core.resourcepack;
|
||||||
|
|
||||||
public class InvalidResourceDeclarationException extends Exception {
|
public enum ModelType {
|
||||||
private static final long serialVersionUID = 0L;
|
|
||||||
|
|
||||||
public InvalidResourceDeclarationException() {}
|
NORMAL,
|
||||||
|
LIQUID;
|
||||||
|
|
||||||
public InvalidResourceDeclarationException(Throwable e) {
|
|
||||||
super(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
public InvalidResourceDeclarationException(String message){
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
public InvalidResourceDeclarationException(String message, Throwable e) {
|
|
||||||
super(message, e);
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -25,19 +25,18 @@
|
|||||||
package de.bluecolored.bluemap.core.resourcepack;
|
package de.bluecolored.bluemap.core.resourcepack;
|
||||||
|
|
||||||
public class NoSuchResourceException extends Exception {
|
public class NoSuchResourceException extends Exception {
|
||||||
private static final long serialVersionUID = 0L;
|
private static final long serialVersionUID = -8545428385061010089L;
|
||||||
|
|
||||||
public NoSuchResourceException() {}
|
public NoSuchResourceException() {
|
||||||
|
super();
|
||||||
public NoSuchResourceException(Throwable e) {
|
|
||||||
super(e);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public NoSuchResourceException(String message){
|
public NoSuchResourceException(String message) {
|
||||||
super(message);
|
super(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
public NoSuchResourceException(String message, Throwable e) {
|
public NoSuchResourceException(String message, Throwable cause) {
|
||||||
super(message, e);
|
super(message, cause);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -24,20 +24,19 @@
|
|||||||
*/
|
*/
|
||||||
package de.bluecolored.bluemap.core.resourcepack;
|
package de.bluecolored.bluemap.core.resourcepack;
|
||||||
|
|
||||||
public class NoSuchTextureException extends Exception {
|
public class ParseResourceException extends Exception {
|
||||||
private static final long serialVersionUID = 0L;
|
private static final long serialVersionUID = -2857915193389089307L;
|
||||||
|
|
||||||
public NoSuchTextureException() {}
|
public ParseResourceException() {
|
||||||
|
super();
|
||||||
public NoSuchTextureException(Throwable e) {
|
|
||||||
super(e);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public NoSuchTextureException(String message){
|
public ParseResourceException(String message) {
|
||||||
super(message);
|
super(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
public NoSuchTextureException(String message, Throwable e) {
|
public ParseResourceException(String message, Throwable cause) {
|
||||||
super(message, e);
|
super(message, cause);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -0,0 +1,137 @@
|
|||||||
|
/*
|
||||||
|
* 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.google.common.base.Preconditions;
|
||||||
|
|
||||||
|
import de.bluecolored.bluemap.core.world.BlockState;
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface PropertyCondition {
|
||||||
|
|
||||||
|
static final PropertyCondition MATCH_ALL = state -> true;
|
||||||
|
static final PropertyCondition MATCH_NONE = state -> false;
|
||||||
|
|
||||||
|
boolean matches(BlockState state);
|
||||||
|
|
||||||
|
public class Property implements PropertyCondition {
|
||||||
|
|
||||||
|
private String key;
|
||||||
|
private String value;
|
||||||
|
|
||||||
|
private Property (String key, String value) {
|
||||||
|
this.key = key.toLowerCase();
|
||||||
|
this.value = value.toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean matches(BlockState state) {
|
||||||
|
String value = state.getProperties().get(this.key);
|
||||||
|
if (value == null) return false;
|
||||||
|
return value.equals(this.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class And implements PropertyCondition {
|
||||||
|
|
||||||
|
private PropertyCondition[] conditions;
|
||||||
|
|
||||||
|
private And (PropertyCondition... conditions) {
|
||||||
|
Preconditions.checkArgument(conditions.length > 0, "Must be at least one condition!");
|
||||||
|
|
||||||
|
this.conditions = conditions;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean matches(BlockState state) {
|
||||||
|
for (PropertyCondition condition : conditions) {
|
||||||
|
if (!condition.matches(state)) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class Or implements PropertyCondition {
|
||||||
|
|
||||||
|
private PropertyCondition[] conditions;
|
||||||
|
|
||||||
|
private Or (PropertyCondition... conditions) {
|
||||||
|
Preconditions.checkArgument(conditions.length > 0, "Must be at least one condition!");
|
||||||
|
|
||||||
|
this.conditions = conditions;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean matches(BlockState state) {
|
||||||
|
for (PropertyCondition condition : conditions) {
|
||||||
|
if (condition.matches(state)) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static PropertyCondition all() {
|
||||||
|
return MATCH_ALL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PropertyCondition none() {
|
||||||
|
return MATCH_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PropertyCondition and(PropertyCondition... conditions) {
|
||||||
|
Preconditions.checkArgument(conditions.length > 0, "Must be at least one condition!");
|
||||||
|
|
||||||
|
return new And(conditions);
|
||||||
|
}
|
||||||
|
|
||||||
|
static PropertyCondition or(PropertyCondition... conditions) {
|
||||||
|
Preconditions.checkArgument(conditions.length > 0, "Must be at least one condition!");
|
||||||
|
|
||||||
|
return new Or(conditions);
|
||||||
|
}
|
||||||
|
|
||||||
|
static PropertyCondition property(String key, String value) {
|
||||||
|
return new Property(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static PropertyCondition property(String key, String... possibleValues) {
|
||||||
|
Preconditions.checkArgument(possibleValues.length > 0, "Must be at least one value!");
|
||||||
|
|
||||||
|
if (possibleValues.length == 1) {
|
||||||
|
return property(key, possibleValues[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
PropertyCondition[] conditions = new PropertyCondition[possibleValues.length];
|
||||||
|
for (int i = 0; i < possibleValues.length; i++) {
|
||||||
|
conditions[i] = property(key, possibleValues[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return or(conditions);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -24,174 +24,175 @@
|
|||||||
*/
|
*/
|
||||||
package de.bluecolored.bluemap.core.resourcepack;
|
package de.bluecolored.bluemap.core.resourcepack;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.awt.image.BufferedImage;
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.nio.file.Files;
|
import java.util.Collection;
|
||||||
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.HashMap;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.zip.ZipEntry;
|
|
||||||
import java.util.zip.ZipFile;
|
|
||||||
|
|
||||||
import com.google.common.cache.Cache;
|
import javax.imageio.ImageIO;
|
||||||
import com.google.common.cache.CacheBuilder;
|
|
||||||
|
|
||||||
import de.bluecolored.bluemap.core.logger.Logger;
|
import de.bluecolored.bluemap.core.logger.Logger;
|
||||||
import de.bluecolored.bluemap.core.util.FileUtils;
|
import de.bluecolored.bluemap.core.resourcepack.BlockStateResource.Builder;
|
||||||
|
import de.bluecolored.bluemap.core.resourcepack.fileaccess.CombinedFileAccess;
|
||||||
|
import de.bluecolored.bluemap.core.resourcepack.fileaccess.FileAccess;
|
||||||
import de.bluecolored.bluemap.core.world.BlockState;
|
import de.bluecolored.bluemap.core.world.BlockState;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents all resources (BlockStates / BlockModels and Textures) that are loaded and used to generate map-models.
|
||||||
|
*/
|
||||||
public class ResourcePack {
|
public class ResourcePack {
|
||||||
|
|
||||||
public static final String MINECRAFT_CLIENT_VERSION = "1.14.4";
|
public static final String MINECRAFT_CLIENT_VERSION = "1.14.4";
|
||||||
public static final String MINECRAFT_CLIENT_URL = "https://launcher.mojang.com/v1/objects/8c325a0c5bd674dd747d6ebaa4c791fd363ad8a9/client.jar";
|
public static final String MINECRAFT_CLIENT_URL = "https://launcher.mojang.com/v1/objects/8c325a0c5bd674dd747d6ebaa4c791fd363ad8a9/client.jar";
|
||||||
|
|
||||||
private Map<Path, Resource> resources;
|
protected Map<String, BlockStateResource> blockStateResources;
|
||||||
|
protected Map<String, BlockModelResource> blockModelResources;
|
||||||
|
protected TextureGallery textures;
|
||||||
|
|
||||||
private TextureProvider textureProvider;
|
private BlockColorCalculator blockColorCalculator;
|
||||||
private BlockColorProvider blockColorProvider;
|
|
||||||
private Cache<BlockState, BlockStateResource> blockStateResourceCache;
|
|
||||||
|
|
||||||
public ResourcePack(List<File> dataSources, File textureExportFile) throws IOException, NoSuchResourceException {
|
private BufferedImage foliageMap;
|
||||||
this.resources = new HashMap<>();
|
private BufferedImage grassMap;
|
||||||
|
|
||||||
//load resources in order
|
public ResourcePack() {
|
||||||
for (File resource : dataSources) overrideResourcesWith(resource);
|
blockStateResources = new HashMap<>();
|
||||||
|
blockModelResources = new HashMap<>();
|
||||||
blockStateResourceCache = CacheBuilder.newBuilder()
|
textures = new TextureGallery();
|
||||||
.maximumSize(10000)
|
foliageMap = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
|
||||||
.build();
|
foliageMap.setRGB(0, 0, 0xFF00FF00);
|
||||||
|
grassMap = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
|
||||||
textureProvider = new TextureProvider();
|
grassMap.setRGB(0, 0, 0xFF00FF00);
|
||||||
|
blockColorCalculator = new BlockColorCalculator(foliageMap, grassMap);
|
||||||
if (textureExportFile.exists()){
|
|
||||||
textureProvider.load(textureExportFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
textureProvider.generate(this); //if loaded add missing textures
|
|
||||||
textureProvider.save(textureExportFile);
|
|
||||||
|
|
||||||
blockColorProvider = new BlockColorProvider(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void overrideResourcesWith(File resource){
|
public void loadBlockColorConfig(File file) throws IOException {
|
||||||
if (resource.isFile() && resource.getName().endsWith(".zip") || resource.getName().endsWith(".jar")){
|
blockColorCalculator.loadColorConfig(file);
|
||||||
overrideResourcesWithZipFile(resource);
|
|
||||||
} else {
|
|
||||||
overrideResourcesWith(resource, Paths.get(""));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void overrideResourcesWith(File resource, Path resourcePath){
|
/**
|
||||||
if (resource.isDirectory()){
|
* See {@link TextureGallery#loadTextureFile(File)}
|
||||||
for (File childFile : resource.listFiles()){
|
* @see TextureGallery#loadTextureFile(File)
|
||||||
overrideResourcesWith(childFile, resourcePath.resolve(childFile.getName()));
|
*/
|
||||||
}
|
public void loadTextureFile(File file) throws IOException, ParseResourceException {
|
||||||
return;
|
textures.loadTextureFile(file);
|
||||||
}
|
|
||||||
|
|
||||||
if (resource.isFile() && isActualResourcePath(resourcePath)){
|
|
||||||
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 (
|
* See {@link TextureGallery#saveTextureFile(File)}
|
||||||
ZipFile zipFile = new ZipFile(resourceFile);
|
* @see TextureGallery#saveTextureFile(File)
|
||||||
){
|
*/
|
||||||
Enumeration<? extends ZipEntry> files = zipFile.entries();
|
public void saveTextureFile(File file) throws IOException {
|
||||||
byte[] buffer = new byte[1024];
|
textures.saveTextureFile(file);
|
||||||
while (files.hasMoreElements()){
|
}
|
||||||
ZipEntry file = files.nextElement();
|
|
||||||
if (file.isDirectory()) continue;
|
|
||||||
|
|
||||||
Path resourcePath = Paths.get("", file.getName().split("/"));
|
/**
|
||||||
if (!isActualResourcePath(resourcePath)) continue;
|
* Loads and generates all {@link BlockStateResource}s from the listed sources.
|
||||||
|
* Resources from sources that are "later" (more to the end) in the list are overriding resources from sources "earlier" (more to the start/head) in the list.<br>
|
||||||
|
* <br>
|
||||||
|
* Any exceptions occurred while loading the resources are logged and ignored.
|
||||||
|
*
|
||||||
|
* @param sources The list of {@link File} sources. Each can be a folder or any zip-compressed file. (E.g. .zip or .jar)
|
||||||
|
*/
|
||||||
|
public void load(Collection<File> sources) throws IOException {
|
||||||
|
load(sources.toArray(new File[sources.size()]));
|
||||||
|
}
|
||||||
|
|
||||||
InputStream fileInputStream = zipFile.getInputStream(file);
|
/**
|
||||||
ByteArrayOutputStream bos = new ByteArrayOutputStream(Math.max(8, (int) file.getSize()));
|
* Loads and generates all {@link BlockStateResource}s and {@link Texture}s from the listed sources.
|
||||||
int bytesRead;
|
* Resources from sources that are "later" (more to the end) in the list are overriding resources from sources "earlier" (more to the start/head) in the list.<br>
|
||||||
while ((bytesRead = fileInputStream.read(buffer)) != -1){
|
* <br>
|
||||||
bos.write(buffer, 0, bytesRead);
|
* Any exceptions occurred while loading the resources are logged and ignored.
|
||||||
|
*
|
||||||
|
* @param sources The list of {@link File} sources. Each can be a folder or any zip-compressed file. (E.g. .zip or .jar)
|
||||||
|
*/
|
||||||
|
public void load(File... sources) {
|
||||||
|
try (CombinedFileAccess sourcesAccess = new CombinedFileAccess()){
|
||||||
|
for (File file : sources) {
|
||||||
|
try {
|
||||||
|
sourcesAccess.addFileAccess(FileAccess.of(file));
|
||||||
|
} catch (IOException e) {
|
||||||
|
Logger.global.logError("Failed to read ResourcePack: " + file, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
resources.put(resourcePath, new Resource(bos.toByteArray()));
|
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
|
||||||
Logger.global.logError("Failed to load resource: " + resourceFile, e);
|
textures.reloadAllTextures(sourcesAccess);
|
||||||
|
|
||||||
|
Builder builder = BlockStateResource.builder(sourcesAccess, this);
|
||||||
|
|
||||||
|
Collection<String> namespaces = sourcesAccess.listFolders("assets");
|
||||||
|
for (String namespaceRoot : namespaces) {
|
||||||
|
String namespace = namespaceRoot.substring("assets/".length());
|
||||||
|
Collection<String> blockstateFiles = sourcesAccess.listFiles(namespaceRoot + "/blockstates", true);
|
||||||
|
for (String blockstateFile : blockstateFiles) {
|
||||||
|
String filename = FileAccess.getFileName(blockstateFile);
|
||||||
|
if (!filename.endsWith(".json")) continue;
|
||||||
|
|
||||||
|
try {
|
||||||
|
blockStateResources.put(namespace + ":" + filename.substring(0, filename.length() - 5), builder.build(blockstateFile));
|
||||||
|
} catch (IOException ex) {
|
||||||
|
Logger.global.logError("Failed to load blockstate: " + namespace + ":" + filename.substring(0, filename.length() - 5), ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
foliageMap = ImageIO.read(sourcesAccess.readFile("assets/minecraft/textures/colormap/foliage.png"));
|
||||||
|
grassMap = ImageIO.read(sourcesAccess.readFile("assets/minecraft/textures/colormap/grass.png"));
|
||||||
|
|
||||||
|
blockColorCalculator.setFoliageMap(foliageMap);
|
||||||
|
blockColorCalculator.setGrassMap(grassMap);
|
||||||
|
} catch (IOException ex) {
|
||||||
|
Logger.global.logError("Failed to load foliage- or grass-map!", ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (IOException ex) {
|
||||||
|
Logger.global.logError("Failed to close FileAccess!", ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isActualResourcePath(Path path) {
|
/**
|
||||||
String[] blockstatesPattern = {"assets", ".*", "blockstates", "*"};
|
* Returns a {@link BlockStateResource} for the given {@link BlockState} if found.
|
||||||
String[] modelsPattern = {"assets", ".*", "models", "blocks?", "*"};
|
* @param state The {@link BlockState}
|
||||||
String[] texturesPattern = {"assets", ".*", "textures", "block|colormap", "*"};
|
* @return The {@link BlockStateResource}
|
||||||
|
* @throws NoSuchResourceException If no resource is loaded for this {@link BlockState}
|
||||||
return
|
*/
|
||||||
FileUtils.matchPath(path, blockstatesPattern) ||
|
public BlockStateResource getBlockStateResource(BlockState state) throws NoSuchResourceException {
|
||||||
FileUtils.matchPath(path, modelsPattern) ||
|
BlockStateResource resource = blockStateResources.get(state.getFullId());
|
||||||
FileUtils.matchPath(path, texturesPattern);
|
if (resource == null) throw new NoSuchResourceException("No resource for blockstate: " + state.getFullId());
|
||||||
|
return resource;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BlockStateResource getBlockStateResource(BlockState block) throws NoSuchResourceException, InvalidResourceDeclarationException {
|
public BlockColorCalculator getBlockColorCalculator() {
|
||||||
BlockStateResource bsr = blockStateResourceCache.getIfPresent(block);
|
return blockColorCalculator;
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Synchronously downloads the default minecraft resources from the mojang-servers.
|
||||||
|
* @param file The file to save the downloaded resources to
|
||||||
|
* @throws IOException If an IOException occurs during the download
|
||||||
|
*/
|
||||||
public static void downloadDefaultResource(File file) throws IOException {
|
public static void downloadDefaultResource(File file) throws IOException {
|
||||||
if (file.exists()) file.delete();
|
if (file.exists()) file.delete();
|
||||||
file.getParentFile().mkdirs();
|
file.getParentFile().mkdirs();
|
||||||
org.apache.commons.io.FileUtils.copyURLToFile(new URL(MINECRAFT_CLIENT_URL), file, 10000, 10000);
|
org.apache.commons.io.FileUtils.copyURLToFile(new URL(MINECRAFT_CLIENT_URL), file, 10000, 10000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected static String namespacedToAbsoluteResourcePath(String namespacedPath, String resourceTypeFolder) {
|
||||||
|
String path = namespacedPath;
|
||||||
|
|
||||||
|
int namespaceIndex = path.indexOf(':');
|
||||||
|
String namespace = "minecraft";
|
||||||
|
if (namespaceIndex != -1) {
|
||||||
|
namespace = path.substring(0, namespaceIndex);
|
||||||
|
path = path.substring(namespaceIndex + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
path = "assets/" + namespace + "/" + resourceTypeFolder + "/" + FileAccess.normalize(path);
|
||||||
|
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,87 @@
|
|||||||
|
/*
|
||||||
|
* 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.Vector4f;
|
||||||
|
|
||||||
|
public class Texture {
|
||||||
|
|
||||||
|
private final int id;
|
||||||
|
private final String path;
|
||||||
|
private Vector4f color;
|
||||||
|
private boolean isHalfTransparent;
|
||||||
|
private String texture;
|
||||||
|
|
||||||
|
protected Texture(int id, String path, Vector4f color, boolean halfTransparent, String texture) {
|
||||||
|
this.id = id;
|
||||||
|
this.path = path;
|
||||||
|
this.color = color;
|
||||||
|
this.isHalfTransparent = halfTransparent;
|
||||||
|
this.texture = texture;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPath() {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the calculated median color of the {@link Texture}.
|
||||||
|
* @return The median color of this {@link Texture}
|
||||||
|
*/
|
||||||
|
public Vector4f getColor() {
|
||||||
|
return color;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the {@link Texture} has half-transparent pixels or not.
|
||||||
|
* @return <code>true</code> if the {@link Texture} has half-transparent pixels, <code>false</code> if not
|
||||||
|
*/
|
||||||
|
public boolean isHalfTransparent() {
|
||||||
|
return isHalfTransparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTexture() {
|
||||||
|
return texture;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (obj instanceof Texture) {
|
||||||
|
return ((Texture) obj).getId() == id;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,315 @@
|
|||||||
|
/*
|
||||||
|
* 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.FileNotFoundException;
|
||||||
|
import java.io.FileReader;
|
||||||
|
import java.io.FileWriter;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Base64;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
|
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
|
|
||||||
|
import com.flowpowered.math.vector.Vector4f;
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.GsonBuilder;
|
||||||
|
import com.google.gson.JsonArray;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import com.google.gson.JsonStreamParser;
|
||||||
|
|
||||||
|
import de.bluecolored.bluemap.core.logger.Logger;
|
||||||
|
import de.bluecolored.bluemap.core.resourcepack.fileaccess.FileAccess;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link TextureGallery} is managing {@link Texture}s and their id's and path's.<br>
|
||||||
|
* I can also load and generate the texture.json file, or load new {@link Texture}s from a {@link FileAccess}.
|
||||||
|
*/
|
||||||
|
public class TextureGallery {
|
||||||
|
|
||||||
|
private static final String EMPTY_BASE64 = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAQAAAC1+jfqAAAAEUlEQVR42mNkIAAYRxWMJAUAE5gAEdz4t9QAAAAASUVORK5CYII=";
|
||||||
|
|
||||||
|
private Map<String, Texture> textureMap;
|
||||||
|
private List<Texture> textureList;
|
||||||
|
|
||||||
|
public TextureGallery() {
|
||||||
|
textureMap = new HashMap<>();
|
||||||
|
textureList = new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a {@link Texture} by its id, there can always be only one texture per id in a gallery.
|
||||||
|
* @param id The texture id
|
||||||
|
* @return The {@link Texture}
|
||||||
|
*/
|
||||||
|
public Texture get(int id) {
|
||||||
|
return textureList.get(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a {@link Texture} by its path, there can always be only one texture per path in a gallery.
|
||||||
|
* @param path The texture-path
|
||||||
|
* @return The {@link Texture}
|
||||||
|
*/
|
||||||
|
public Texture get(String path) {
|
||||||
|
Texture texture = textureMap.get(path);
|
||||||
|
if (texture == null) throw new NoSuchElementException("There is no texture with the path " + path + " in this gallery!");
|
||||||
|
return texture;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The count of {@link Texture}s managed by this gallery
|
||||||
|
* @return The count of textures
|
||||||
|
*/
|
||||||
|
public int size() {
|
||||||
|
return textureList.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a texture.json file with all the {@link Texture}s in this gallery
|
||||||
|
* @param file The file to save the json in
|
||||||
|
* @throws IOException If an IOException occurs while writing
|
||||||
|
*/
|
||||||
|
public void saveTextureFile(File file) throws IOException {
|
||||||
|
|
||||||
|
JsonArray textures = new JsonArray();
|
||||||
|
for (int i = 0; i < textureList.size(); i++) {
|
||||||
|
Texture texture = textureList.get(i);
|
||||||
|
|
||||||
|
JsonObject textureNode = new JsonObject();
|
||||||
|
textureNode.addProperty("id", texture.getPath());
|
||||||
|
textureNode.addProperty("texture", texture.getTexture());
|
||||||
|
textureNode.addProperty("transparent", texture.isHalfTransparent());
|
||||||
|
|
||||||
|
Vector4f color = texture.getColor();
|
||||||
|
JsonArray colorNode = new JsonArray();
|
||||||
|
colorNode.add(color.getX());
|
||||||
|
colorNode.add(color.getY());
|
||||||
|
colorNode.add(color.getZ());
|
||||||
|
colorNode.add(color.getW());
|
||||||
|
|
||||||
|
textureNode.add("color", colorNode);
|
||||||
|
|
||||||
|
textures.add(textureNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonObject root = new JsonObject();
|
||||||
|
root.add("textures", textures);
|
||||||
|
|
||||||
|
Gson gson = new GsonBuilder()
|
||||||
|
.setPrettyPrinting()
|
||||||
|
.create();
|
||||||
|
String json = gson.toJson(root);
|
||||||
|
|
||||||
|
file.delete();
|
||||||
|
file.getParentFile().mkdirs();
|
||||||
|
file.createNewFile();
|
||||||
|
|
||||||
|
try (FileWriter fileWriter = new FileWriter(file)) {
|
||||||
|
fileWriter.append(json);
|
||||||
|
fileWriter.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads all the {@link Texture}s from the provided texture.json file, removes any existing {@link Texture}s from this gallery.
|
||||||
|
* @param file The texture.json file.
|
||||||
|
* @throws IOException If an IOException occurs while reading the file.
|
||||||
|
* @throws ParseResourceException If the whole file can not be read. Errors with single textures are logged and ignored.
|
||||||
|
*/
|
||||||
|
public synchronized void loadTextureFile(File file) throws IOException, ParseResourceException {
|
||||||
|
textureList.clear();
|
||||||
|
textureMap.clear();
|
||||||
|
|
||||||
|
try (FileReader fileReader = new FileReader(file)){
|
||||||
|
JsonStreamParser jsonFile = new JsonStreamParser(fileReader);
|
||||||
|
JsonArray textures = jsonFile.next().getAsJsonObject().getAsJsonArray("textures");
|
||||||
|
int size = textures.size();
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
while (i >= textureList.size()) { //prepopulate with placeholder so we don't get an IndexOutOfBounds below
|
||||||
|
textureList.add(new Texture(textureList.size(), "empty", Vector4f.ZERO, false, EMPTY_BASE64));
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
JsonObject texture = textures.get(i).getAsJsonObject();
|
||||||
|
String path = texture.get("id").getAsString();
|
||||||
|
boolean transparent = texture.get("transparent").getAsBoolean();
|
||||||
|
Vector4f color = readVector4f(texture.get("color").getAsJsonArray());
|
||||||
|
textureList.set(i, new Texture(i, path, color, transparent, EMPTY_BASE64));
|
||||||
|
} catch (Exception ex) {
|
||||||
|
Logger.global.logWarning("Failed to load texture with id " + i + " from texture file " + file + "!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (IOException ex) {
|
||||||
|
throw ex;
|
||||||
|
} catch (Exception ex) {
|
||||||
|
throw new ParseResourceException("Invalid texture file format!", ex);
|
||||||
|
} finally {
|
||||||
|
regenerateMap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads a {@link Texture} from the {@link FileAccess} and the given path and returns it.<br>
|
||||||
|
* If there is already a {@link Texture} with this path in this Gallery it replaces the {@link Texture} with the new one
|
||||||
|
* and the new one will have the same id as the old one.<br>
|
||||||
|
* Otherwise the {@link Texture} will be added to the end of this gallery with the next available id.
|
||||||
|
* @param fileAccess The {@link FileAccess} to load the image from.
|
||||||
|
* @param path The path of the image on the {@link FileAccess}
|
||||||
|
* @return The loaded {@link Texture}
|
||||||
|
* @throws FileNotFoundException If there is no image in that FileAccess on that path
|
||||||
|
* @throws IOException If an IOException occurred while loading the file
|
||||||
|
*/
|
||||||
|
public synchronized Texture loadTexture(FileAccess fileAccess, String path) throws FileNotFoundException, IOException {
|
||||||
|
try (InputStream input = fileAccess.readFile(path)) {
|
||||||
|
BufferedImage image = ImageIO.read(input);
|
||||||
|
if (image == null) throw new IOException("Failed to read image: " + path);
|
||||||
|
|
||||||
|
//crop off animation frames
|
||||||
|
if (image.getHeight() > image.getWidth()){
|
||||||
|
BufferedImage cropped = new BufferedImage(image.getWidth(), image.getWidth(), BufferedImage.TYPE_INT_ARGB);
|
||||||
|
Graphics2D g = cropped.createGraphics();
|
||||||
|
g.drawImage(image, 0, 0, null);
|
||||||
|
image = cropped;
|
||||||
|
}
|
||||||
|
|
||||||
|
//check halfTransparency
|
||||||
|
boolean halfTransparent = checkHalfTransparent(image);
|
||||||
|
|
||||||
|
//calculate color
|
||||||
|
Vector4f color = calculateColor(image);
|
||||||
|
|
||||||
|
//write to Base64
|
||||||
|
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||||
|
ImageIO.write(image, "png", os);
|
||||||
|
String base64 = "data:image/png;base64," + Base64.getEncoder().encodeToString(os.toByteArray());
|
||||||
|
|
||||||
|
//replace if texture with this path already exists
|
||||||
|
Texture texture = textureMap.get(path);
|
||||||
|
if (texture != null) {
|
||||||
|
texture = new Texture(texture.getId(), path, color, halfTransparent, base64);
|
||||||
|
textureMap.put(path, texture);
|
||||||
|
textureList.set(texture.getId(), texture);
|
||||||
|
} else {
|
||||||
|
texture = new Texture(textureList.size(), path, color, halfTransparent, base64);
|
||||||
|
textureMap.put(path, texture);
|
||||||
|
textureList.add(texture);
|
||||||
|
}
|
||||||
|
|
||||||
|
return texture;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tries to reload all {@link Texture}s from the given {@link FileAccess}<br>
|
||||||
|
* <br>
|
||||||
|
* Exceptions are being logged and ignored.
|
||||||
|
* @param fileAccess The {@link FileAccess} to load the {@link Texture}s from
|
||||||
|
*/
|
||||||
|
public synchronized void reloadAllTextures(FileAccess fileAccess) {
|
||||||
|
for (Texture texture : textureList.toArray(new Texture[textureList.size()])) {
|
||||||
|
try {
|
||||||
|
loadTexture(fileAccess, texture.getPath());
|
||||||
|
} catch (IOException e) {
|
||||||
|
Logger.global.logWarning("Failed to reload texture: " + texture.getPath());
|
||||||
|
Logger.global.noFloodWarning("This happens if the resource-packs have changed, but you have not deleted your generated maps. This might result in broken map-models!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized void regenerateMap() {
|
||||||
|
textureMap.clear();
|
||||||
|
for (int i = 0; i < textureList.size(); i++) {
|
||||||
|
Texture texture = textureList.get(i);
|
||||||
|
textureMap.put(texture.getPath(), texture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Vector4f readVector4f(JsonArray jsonArray) throws ParseResourceException {
|
||||||
|
if (jsonArray.size() < 4) throw new ParseResourceException("Failed to load Vector4: Not enough values in list-node!");
|
||||||
|
|
||||||
|
float r = jsonArray.get(0).getAsFloat();
|
||||||
|
float g = jsonArray.get(1).getAsFloat();
|
||||||
|
float b = jsonArray.get(2).getAsFloat();
|
||||||
|
float a = jsonArray.get(3).getAsFloat();
|
||||||
|
|
||||||
|
return new Vector4f(r, g, b, a);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Vector4f calculateColor(BufferedImage image){
|
||||||
|
double alpha = 0d, red = 0d, green = 0d, blue = 0d;
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
for (int x = 0; x < image.getWidth(); x++){
|
||||||
|
for (int y = 0; y < image.getHeight(); y++){
|
||||||
|
int pixel = image.getRGB(x, y);
|
||||||
|
double pixelAlpha = (double)((pixel >> 24) & 0xff) / (double) 0xff;
|
||||||
|
double pixelRed = (double)((pixel >> 16) & 0xff) / (double) 0xff;
|
||||||
|
double pixelGreen = (double)((pixel >> 8) & 0xff) / (double) 0xff;
|
||||||
|
double pixelBlue = (double)((pixel >> 0) & 0xff) / (double) 0xff;
|
||||||
|
|
||||||
|
count++;
|
||||||
|
alpha += pixelAlpha;
|
||||||
|
red += pixelRed * pixelAlpha;
|
||||||
|
green += pixelGreen * pixelAlpha;
|
||||||
|
blue += pixelBlue * pixelAlpha;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count == 0 || alpha == 0) return Vector4f.ZERO;
|
||||||
|
|
||||||
|
red /= alpha;
|
||||||
|
green /= alpha;
|
||||||
|
blue /= alpha;
|
||||||
|
alpha /= count;
|
||||||
|
|
||||||
|
return new Vector4f(red, green, blue, alpha);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,236 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.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.ConfigUtils;
|
|
||||||
import de.bluecolored.bluemap.core.util.FileUtils;
|
|
||||||
import de.bluecolored.bluemap.core.util.MathUtils;
|
|
||||||
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 {
|
|
||||||
String[] texturesPathPattern = {"assets", ".*", "textures", "block", "*"};
|
|
||||||
|
|
||||||
for (Entry<Path, Resource> entry : resources.getAllResources().entrySet()){
|
|
||||||
Path key = entry.getKey();
|
|
||||||
if (FileUtils.matchPath(key, texturesPathPattern) && key.toString().endsWith(".png")){
|
|
||||||
String path = key.subpath(3, key.getNameCount()).normalize().toString();
|
|
||||||
String id = path
|
|
||||||
.substring(0, path.length() - ".png".length())
|
|
||||||
.replace(File.separatorChar, '/');
|
|
||||||
|
|
||||||
BufferedImage image = ImageIO.read(entry.getValue().getStream());
|
|
||||||
if (image == null) throw new IOException("Failed to read Image: " + key);
|
|
||||||
|
|
||||||
Texture texture = new Texture(id, image);
|
|
||||||
|
|
||||||
//update if existing else add new
|
|
||||||
if (indexMap.containsKey(id)) {
|
|
||||||
int index = indexMap.get(id);
|
|
||||||
textures.set(index, texture);
|
|
||||||
} else {
|
|
||||||
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),
|
|
||||||
ConfigUtils.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());
|
|
||||||
ConfigUtils.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 = MathUtils.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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -22,55 +22,33 @@
|
|||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
* THE SOFTWARE.
|
* THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
package de.bluecolored.bluemap.core.util;
|
package de.bluecolored.bluemap.core.resourcepack;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import com.flowpowered.math.vector.Vector2i;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class WeighedArrayList<E> extends ArrayList<E> implements List<E> {
|
public class TransformedBlockModelResource {
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
public WeighedArrayList() {}
|
private Vector2i rotation = Vector2i.ZERO;
|
||||||
|
private boolean uvLock = false;
|
||||||
|
|
||||||
public WeighedArrayList(int capacity) {
|
private BlockModelResource model;
|
||||||
super(capacity);
|
|
||||||
|
public TransformedBlockModelResource(Vector2i rotation, boolean uvLock, BlockModelResource model) {
|
||||||
|
this.model = model;
|
||||||
|
this.rotation = rotation;
|
||||||
|
this.uvLock = uvLock;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public Vector2i getRotation() {
|
||||||
* Adds the element weight times to this list.
|
return rotation;
|
||||||
* @return Always true
|
|
||||||
*/
|
|
||||||
public void add(E e, int weight) {
|
|
||||||
for (int i = 0; i < weight; i++){
|
|
||||||
add(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public boolean isUVLock() {
|
||||||
* Removes the first weight number of items that equal o from this list.<br>
|
return uvLock;
|
||||||
* @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;
|
public BlockModelResource getModel() {
|
||||||
|
return model;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -0,0 +1,97 @@
|
|||||||
|
/*
|
||||||
|
* 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.fileaccess;
|
||||||
|
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public class CombinedFileAccess implements FileAccess {
|
||||||
|
|
||||||
|
public List<FileAccess> sources;
|
||||||
|
|
||||||
|
public CombinedFileAccess() {
|
||||||
|
sources = new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addFileAccess(FileAccess source) {
|
||||||
|
sources.add(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InputStream readFile(String path) throws FileNotFoundException, IOException {
|
||||||
|
for (int i = sources.size() - 1; i >= 0; i--) { //reverse order because later sources override earlier ones
|
||||||
|
try {
|
||||||
|
return sources.get(i).readFile(path);
|
||||||
|
} catch (FileNotFoundException ex) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new FileNotFoundException("File " + path + " does not exist in any of the sources!");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<String> listFiles(String path, boolean recursive) {
|
||||||
|
Set<String> files = new HashSet<>();
|
||||||
|
|
||||||
|
for (int i = 0; i < sources.size(); i++) {
|
||||||
|
files.addAll(sources.get(i).listFiles(path, recursive));
|
||||||
|
}
|
||||||
|
|
||||||
|
return files;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<String> listFolders(String path) {
|
||||||
|
Set<String> folders = new HashSet<>();
|
||||||
|
|
||||||
|
for (int i = 0; i < sources.size(); i++) {
|
||||||
|
folders.addAll(sources.get(i).listFolders(path));
|
||||||
|
}
|
||||||
|
|
||||||
|
return folders;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws IOException {
|
||||||
|
IOException exception = null;
|
||||||
|
|
||||||
|
for (FileAccess source : sources) {
|
||||||
|
try {
|
||||||
|
source.close();
|
||||||
|
} catch (IOException ex) {
|
||||||
|
if (exception == null) exception = ex;
|
||||||
|
else exception.addSuppressed(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (exception != null) throw exception;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,65 @@
|
|||||||
|
/*
|
||||||
|
* 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.fileaccess;
|
||||||
|
|
||||||
|
import java.io.Closeable;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
public interface FileAccess extends Closeable, AutoCloseable {
|
||||||
|
|
||||||
|
InputStream readFile(String path) throws FileNotFoundException, IOException;
|
||||||
|
|
||||||
|
Collection<String> listFiles(String path, boolean recursive);
|
||||||
|
|
||||||
|
Collection<String> listFolders(String path);
|
||||||
|
|
||||||
|
static FileAccess of(File file) throws IOException {
|
||||||
|
if (file.isDirectory()) return new FolderFileAccess(file);
|
||||||
|
if (file.isFile()) return new ZipFileAccess(file);
|
||||||
|
throw new IOException("Unsupported file!");
|
||||||
|
}
|
||||||
|
|
||||||
|
static String getFileName(String path) {
|
||||||
|
String filename = path;
|
||||||
|
|
||||||
|
int nameSplit = path.lastIndexOf('/');
|
||||||
|
if (nameSplit > -1) {
|
||||||
|
filename = path.substring(nameSplit + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
static String normalize(String path) {
|
||||||
|
if (path.charAt(path.length() - 1) == '/') path = path.substring(0, path.length() - 1);
|
||||||
|
if (path.charAt(0) == '/') path = path.substring(1);
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,138 @@
|
|||||||
|
/*
|
||||||
|
* 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.fileaccess;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class FolderFileAccess implements FileAccess {
|
||||||
|
|
||||||
|
private File folder;
|
||||||
|
private Collection<WeakReference<InputStream>> openedStreams;
|
||||||
|
|
||||||
|
public FolderFileAccess(File folder) {
|
||||||
|
this.folder = folder;
|
||||||
|
openedStreams = new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized InputStream readFile(String path) throws FileNotFoundException {
|
||||||
|
InputStream stream = new FileInputStream(resolve(path).toFile());
|
||||||
|
tidy();
|
||||||
|
openedStreams.add(new WeakReference<>(stream));
|
||||||
|
return stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<String> listFiles(String path, boolean recursive) {
|
||||||
|
File subFolder = resolve(path).toFile();
|
||||||
|
List<String> paths = new ArrayList<>();
|
||||||
|
listFiles(subFolder, paths, recursive);
|
||||||
|
return paths;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void listFiles(File folder, Collection<String> paths, boolean recursive) {
|
||||||
|
if (!folder.isDirectory()) return;
|
||||||
|
for (File file : folder.listFiles()) {
|
||||||
|
if (recursive && file.isDirectory()) listFiles(file, paths, true);
|
||||||
|
if (file.isFile()) paths.add(toPath(file));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<String> listFolders(String path) {
|
||||||
|
File subFolder = resolve(path).toFile();
|
||||||
|
List<String> paths = new ArrayList<>();
|
||||||
|
|
||||||
|
if (subFolder.isDirectory()) {
|
||||||
|
for (File file : subFolder.listFiles()) {
|
||||||
|
if (file.isDirectory()) paths.add(toPath(file));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return paths;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void close() throws IOException {
|
||||||
|
IOException exception = null;
|
||||||
|
|
||||||
|
for (WeakReference<InputStream> streamRef : openedStreams) {
|
||||||
|
try {
|
||||||
|
InputStream stream = streamRef.get();
|
||||||
|
if (stream != null) stream.close();
|
||||||
|
streamRef.clear();
|
||||||
|
} catch (IOException ex) {
|
||||||
|
if (exception == null) exception = ex;
|
||||||
|
else exception.addSuppressed(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (exception != null) throw exception;
|
||||||
|
|
||||||
|
openedStreams.clear(); //only clear if no exception is thrown
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized void tidy() {
|
||||||
|
Iterator<WeakReference<InputStream>> iterator = openedStreams.iterator();
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
WeakReference<InputStream> ref = iterator.next();
|
||||||
|
if (ref.get() == null) iterator.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Path resolve(String path) {
|
||||||
|
if (File.separatorChar != '/') path = path.replace('/', File.separatorChar);
|
||||||
|
if (path.charAt(0) == '/') path = path.substring(1);
|
||||||
|
Path resolve = folder.toPath();
|
||||||
|
for (String s : path.split("/")) {
|
||||||
|
resolve = resolve.resolve(s);
|
||||||
|
}
|
||||||
|
return resolve;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String toPath(File file) {
|
||||||
|
return toPath(file.toPath());
|
||||||
|
}
|
||||||
|
|
||||||
|
private String toPath(Path path) {
|
||||||
|
return folder
|
||||||
|
.toPath()
|
||||||
|
.relativize(path)
|
||||||
|
.normalize()
|
||||||
|
.toString()
|
||||||
|
.replace(File.separatorChar, '/');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,124 @@
|
|||||||
|
/*
|
||||||
|
* 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.fileaccess;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Enumeration;
|
||||||
|
import java.util.zip.ZipEntry;
|
||||||
|
import java.util.zip.ZipException;
|
||||||
|
import java.util.zip.ZipFile;
|
||||||
|
|
||||||
|
public class ZipFileAccess implements FileAccess {
|
||||||
|
|
||||||
|
private ZipFile file;
|
||||||
|
|
||||||
|
public ZipFileAccess(File file) throws ZipException, IOException {
|
||||||
|
this.file = new ZipFile(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InputStream readFile(String path) throws FileNotFoundException, IOException {
|
||||||
|
ZipEntry entry = file.getEntry(path);
|
||||||
|
if (entry == null) throw new FileNotFoundException("File " + path + " does not exist in this zip-file!");
|
||||||
|
|
||||||
|
return file.getInputStream(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<String> listFiles(String path, boolean recursive) {
|
||||||
|
path = normalizeFolderPath(path);
|
||||||
|
|
||||||
|
Collection<String> files = new ArrayList<String>();
|
||||||
|
for (Enumeration<? extends ZipEntry> entries = file.entries(); entries.hasMoreElements();) {
|
||||||
|
ZipEntry entry = entries.nextElement();
|
||||||
|
|
||||||
|
if (entry.isDirectory()) continue;
|
||||||
|
|
||||||
|
String file = entry.getName();
|
||||||
|
int nameSplit = file.lastIndexOf('/');
|
||||||
|
String filePath = "";
|
||||||
|
if (nameSplit != -1) {
|
||||||
|
filePath = file.substring(0, nameSplit);
|
||||||
|
}
|
||||||
|
filePath = normalizeFolderPath(filePath);
|
||||||
|
|
||||||
|
if (recursive) {
|
||||||
|
if (!filePath.startsWith(path) && !path.equals(filePath)) continue;
|
||||||
|
} else {
|
||||||
|
if (!path.equals(filePath)) continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
files.add(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
return files;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<String> listFolders(String path) {
|
||||||
|
path = normalizeFolderPath(path);
|
||||||
|
|
||||||
|
Collection<String> folders = new ArrayList<String>();
|
||||||
|
for (Enumeration<? extends ZipEntry> entries = file.entries(); entries.hasMoreElements();) {
|
||||||
|
ZipEntry entry = entries.nextElement();
|
||||||
|
|
||||||
|
if (!entry.isDirectory()) continue;
|
||||||
|
|
||||||
|
String file = entry.getName();
|
||||||
|
file = file.substring(0, file.length() - 1); //strip last /
|
||||||
|
|
||||||
|
int nameSplit = file.lastIndexOf('/');
|
||||||
|
String filePath = "/";
|
||||||
|
if (nameSplit != -1) {
|
||||||
|
filePath = file.substring(0, nameSplit);
|
||||||
|
}
|
||||||
|
filePath = normalizeFolderPath(filePath);
|
||||||
|
|
||||||
|
if (!path.equals(filePath)) continue;
|
||||||
|
|
||||||
|
folders.add(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
return folders;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String normalizeFolderPath(String path) {
|
||||||
|
if (path.isEmpty()) return path;
|
||||||
|
if (path.charAt(path.length() - 1) != '/') path = path + "/";
|
||||||
|
if (path.charAt(0) == '/') path = path.substring(1);
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws IOException {
|
||||||
|
file.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -132,4 +132,28 @@ public static void writeVector4f(ConfigurationNode vectorNode, Vector4f v){
|
|||||||
vectorNode.getAppendedNode().setValue(v.getW());
|
vectorNode.getAppendedNode().setValue(v.getW());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an integer The value can be a normal integer, an integer in String-Format, or a string in hexadecimal format prefixed with #.
|
||||||
|
* @param node The Configuration Node with the value
|
||||||
|
* @return The parsed Integer
|
||||||
|
* @throws NumberFormatException If the value is not formatted correctly or if there is no value present.
|
||||||
|
*/
|
||||||
|
public static int readInt(ConfigurationNode node) throws NumberFormatException {
|
||||||
|
Object value = node.getValue();
|
||||||
|
|
||||||
|
if (value == null) throw new NumberFormatException("No value!");
|
||||||
|
|
||||||
|
if (value instanceof Number) {
|
||||||
|
return ((Number) value).intValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
String val = value.toString();
|
||||||
|
|
||||||
|
if (val.charAt(0) == '#') {
|
||||||
|
return Integer.parseInt(val.substring(1), 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Integer.parseInt(val);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -33,6 +33,13 @@ public class MathUtils {
|
|||||||
|
|
||||||
private MathUtils() {}
|
private MathUtils() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the surface-normal of a plane spanned between three vectors.
|
||||||
|
* @param p1 The first vector
|
||||||
|
* @param p2 The second vector
|
||||||
|
* @param p3 The third vector
|
||||||
|
* @return The calculated normal
|
||||||
|
*/
|
||||||
public static Vector3d getSurfaceNormal(Vector3d p1, Vector3d p2, Vector3d p3){
|
public static Vector3d getSurfaceNormal(Vector3d p1, Vector3d p2, Vector3d p3){
|
||||||
Vector3d u = p2.sub(p1);
|
Vector3d u = p2.sub(p1);
|
||||||
Vector3d v = p3.sub(p1);
|
Vector3d v = p3.sub(p1);
|
||||||
@ -44,6 +51,13 @@ public static Vector3d getSurfaceNormal(Vector3d p1, Vector3d p2, Vector3d p3){
|
|||||||
return new Vector3d(nX, nY, nZ);
|
return new Vector3d(nX, nY, nZ);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the surface-normal of a plane spanned between three vectors.
|
||||||
|
* @param p1 The first vector
|
||||||
|
* @param p2 The second vector
|
||||||
|
* @param p3 The third vector
|
||||||
|
* @return The calculated normal
|
||||||
|
*/
|
||||||
public static Vector3f getSurfaceNormal(Vector3f p1, Vector3f p2, Vector3f p3) {
|
public static Vector3f getSurfaceNormal(Vector3f p1, Vector3f p2, Vector3f p3) {
|
||||||
Vector3f u = p2.sub(p1);
|
Vector3f u = p2.sub(p1);
|
||||||
Vector3f v = p3.sub(p1);
|
Vector3f v = p3.sub(p1);
|
||||||
@ -58,18 +72,42 @@ public static Vector3f getSurfaceNormal(Vector3f p1, Vector3f p2, Vector3f p3) {
|
|||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hashes the provided position to a random float between 0 and 1.<br>
|
||||||
|
* <br>
|
||||||
|
* <i>(Implementation adapted from https://github.com/SpongePowered/SpongeAPI/blob/ecd761a70219e467dea47a09fc310e8238e9911f/src/main/java/org/spongepowered/api/extra/skylands/SkylandsUtil.java)</i>
|
||||||
|
*
|
||||||
|
* @param pos The position to hash
|
||||||
|
* @param seed A seed for the hashing
|
||||||
|
* @return The hashed value between 0 and 1
|
||||||
|
*/
|
||||||
public static float hashToFloat(Vector3i pos, long seed) {
|
public static float hashToFloat(Vector3i pos, long seed) {
|
||||||
return hashToFloat(pos.getX(), pos.getY(), pos.getZ(), 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
|
* Hashes the provided position to a random float between 0 and 1.<br>
|
||||||
|
* <br>
|
||||||
|
* <i>(Implementation adapted from https://github.com/SpongePowered/SpongeAPI/blob/ecd761a70219e467dea47a09fc310e8238e9911f/src/main/java/org/spongepowered/api/extra/skylands/SkylandsUtil.java)</i>
|
||||||
|
*
|
||||||
|
* @param x The x component of the position
|
||||||
|
* @param y The y component of the position
|
||||||
|
* @param z The z component of the position
|
||||||
|
* @param seed A seed for the hashing
|
||||||
|
* @return The hashed value between 0 and 1
|
||||||
*/
|
*/
|
||||||
public static float hashToFloat(int x, int y, int z, long seed) {
|
public static float hashToFloat(int x, int y, int z, long seed) {
|
||||||
final long hash = x * 73428767 ^ y * 9122569 ^ z * 4382893 ^ seed * 457;
|
final long hash = x * 73428767 ^ y * 9122569 ^ z * 4382893 ^ seed * 457;
|
||||||
return (hash * (hash + 456149) & 0x00ffffff) / (float) 0x01000000;
|
return (hash * (hash + 456149) & 0x00ffffff) / (float) 0x01000000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Blends two colors, taking into account the alpha component
|
||||||
|
* @param top The top color
|
||||||
|
* @param bottom The bottom color
|
||||||
|
* @return The merged color
|
||||||
|
*/
|
||||||
public static Vector4f blendColors(Vector4f top, Vector4f bottom){
|
public static Vector4f blendColors(Vector4f top, Vector4f bottom){
|
||||||
if (top.getW() > 0 && bottom.getW() > 0){
|
if (top.getW() > 0 && bottom.getW() > 0){
|
||||||
float a = 1 - (1 - top.getW()) * (1 - bottom.getW());
|
float a = 1 - (1 - top.getW()) * (1 - bottom.getW());
|
||||||
@ -84,6 +122,12 @@ public static Vector4f blendColors(Vector4f top, Vector4f bottom){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overlays two colors, taking into account the alpha component
|
||||||
|
* @param top The top color
|
||||||
|
* @param bottom The bottom color
|
||||||
|
* @return The merged color
|
||||||
|
*/
|
||||||
public static Vector4f overlayColors(Vector4f top, Vector4f bottom){
|
public static Vector4f overlayColors(Vector4f top, Vector4f bottom){
|
||||||
if (top.getW() > 0 && bottom.getW() > 0){
|
if (top.getW() > 0 && bottom.getW() > 0){
|
||||||
float p = (1 - top.getW()) * bottom.getW();
|
float p = (1 - top.getW()) * bottom.getW();
|
||||||
@ -99,4 +143,31 @@ public static Vector4f overlayColors(Vector4f top, Vector4f bottom){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link Vector3f} representing the color from the integer
|
||||||
|
* @param color The color-int
|
||||||
|
* @return The color-Vector
|
||||||
|
*/
|
||||||
|
public static Vector3f color3FromInt(int color){
|
||||||
|
return new Vector3f(
|
||||||
|
(color >> 16) & 0xFF,
|
||||||
|
(color >> 8) & 0xFF,
|
||||||
|
color & 0xFF
|
||||||
|
).div(0xFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link Vector4f} representing the color from the integer
|
||||||
|
* @param color The color-int
|
||||||
|
* @return The color-Vector
|
||||||
|
*/
|
||||||
|
public static Vector4f color4FromInt(int color){
|
||||||
|
return new Vector4f(
|
||||||
|
(color >> 16) & 0xFF,
|
||||||
|
(color >> 8) & 0xFF,
|
||||||
|
color & 0xFF,
|
||||||
|
(color >> 24) & 0xFF
|
||||||
|
).div(0xFF);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -50,7 +50,7 @@ public WebSettings(File settingsFile) throws IOException {
|
|||||||
.setFile(settingsFile)
|
.setFile(settingsFile)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
load();
|
rootNode = configLoader.createEmptyNode();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void load() throws IOException {
|
public void load() throws IOException {
|
||||||
|
@ -0,0 +1,108 @@
|
|||||||
|
/*
|
||||||
|
* 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.Vector3f;
|
||||||
|
import com.flowpowered.math.vector.Vector4f;
|
||||||
|
|
||||||
|
import de.bluecolored.bluemap.core.util.ConfigUtils;
|
||||||
|
import de.bluecolored.bluemap.core.util.MathUtils;
|
||||||
|
import ninja.leaping.configurate.ConfigurationNode;
|
||||||
|
|
||||||
|
public class Biome {
|
||||||
|
|
||||||
|
public static final Biome DEFAULT = new Biome();
|
||||||
|
|
||||||
|
private String id = "ocean";
|
||||||
|
private int ordinal = 0;
|
||||||
|
private float humidity = 0.5f;
|
||||||
|
private float temp = 0.5f;
|
||||||
|
private Vector3f waterColor = MathUtils.color3FromInt(4159204);
|
||||||
|
|
||||||
|
private Vector4f overlayFoliageColor = Vector4f.ZERO;
|
||||||
|
private Vector4f overlayGrassColor = Vector4f.ZERO;
|
||||||
|
|
||||||
|
private Biome() {}
|
||||||
|
|
||||||
|
public Biome(String id, int ordinal, float humidity, float temp, Vector3f waterColor) {
|
||||||
|
this.id = id;
|
||||||
|
this.ordinal = ordinal;
|
||||||
|
this.humidity = humidity;
|
||||||
|
this.temp = temp;
|
||||||
|
this.waterColor = waterColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Biome(String id, int ordinal, float humidity, float temp, Vector3f waterColor, Vector4f overlayFoliageColor, Vector4f overlayGrassColor) {
|
||||||
|
this (id, ordinal, humidity, temp, waterColor);
|
||||||
|
|
||||||
|
this.overlayFoliageColor = overlayFoliageColor;
|
||||||
|
this.overlayGrassColor = overlayGrassColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getOrdinal() {
|
||||||
|
return ordinal;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getHumidity() {
|
||||||
|
return humidity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getTemp() {
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector3f getWaterColor() {
|
||||||
|
return waterColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector4f getOverlayFoliageColor() {
|
||||||
|
return overlayFoliageColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector4f getOverlayGrassColor() {
|
||||||
|
return overlayGrassColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Biome create(String id, ConfigurationNode node) {
|
||||||
|
Biome biome = new Biome();
|
||||||
|
|
||||||
|
biome.id = id;
|
||||||
|
biome.ordinal = node.getNode("id").getInt(biome.ordinal);
|
||||||
|
biome.humidity = node.getNode("humidity").getFloat(biome.humidity);
|
||||||
|
biome.temp = node.getNode("temp").getFloat(biome.temp);
|
||||||
|
try { biome.waterColor = MathUtils.color3FromInt(ConfigUtils.readInt(node.getNode("watercolor"))); } catch (NumberFormatException ignored) {}
|
||||||
|
try { biome.overlayFoliageColor = MathUtils.color4FromInt(ConfigUtils.readInt(node.getNode("foliagecolor"))); } catch (NumberFormatException ignored) {}
|
||||||
|
try { biome.overlayGrassColor = MathUtils.color4FromInt(ConfigUtils.readInt(node.getNode("grasscolor"))); } catch (NumberFormatException ignored) {}
|
||||||
|
|
||||||
|
return biome;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -56,7 +56,7 @@ public boolean isOccludingNeighborFaces(){
|
|||||||
return isCullingNeighborFaces();
|
return isCullingNeighborFaces();
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract String getBiome();
|
public abstract Biome getBiome();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is internally used for light rendering
|
* This is internally used for light rendering
|
||||||
|
@ -51,6 +51,7 @@ public class BlockState {
|
|||||||
|
|
||||||
private final String namespace;
|
private final String namespace;
|
||||||
private final String id;
|
private final String id;
|
||||||
|
private final String fullId;
|
||||||
private final Map<String, String> properties;
|
private final Map<String, String> properties;
|
||||||
|
|
||||||
public BlockState(String id) {
|
public BlockState(String id) {
|
||||||
@ -73,6 +74,7 @@ public BlockState(String id, Map<String, String> properties) {
|
|||||||
|
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.namespace = namespace;
|
this.namespace = namespace;
|
||||||
|
this.fullId = namespace + ":" + id;
|
||||||
}
|
}
|
||||||
|
|
||||||
private BlockState(BlockState blockState, String withKey, String withValue) {
|
private BlockState(BlockState blockState, String withKey, String withValue) {
|
||||||
@ -84,6 +86,7 @@ private BlockState(BlockState blockState, String withKey, String withValue) {
|
|||||||
|
|
||||||
this.id = blockState.getId();
|
this.id = blockState.getId();
|
||||||
this.namespace = blockState.getNamespace();
|
this.namespace = blockState.getNamespace();
|
||||||
|
this.fullId = namespace + ":" + id;
|
||||||
this.properties = Collections.unmodifiableMap(props);
|
this.properties = Collections.unmodifiableMap(props);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -107,7 +110,7 @@ public String getId() {
|
|||||||
* Returns the namespaced id of this blockstate
|
* Returns the namespaced id of this blockstate
|
||||||
*/
|
*/
|
||||||
public String getFullId() {
|
public String getFullId() {
|
||||||
return getNamespace() + ":" + getId();
|
return fullId;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -38,7 +38,7 @@ public class CachedBlock extends Block {
|
|||||||
private World world;
|
private World world;
|
||||||
private Vector3i position;
|
private Vector3i position;
|
||||||
private double sunLight, blockLight;
|
private double sunLight, blockLight;
|
||||||
private String biome;
|
private Biome biome;
|
||||||
|
|
||||||
private boolean isCullingCached;
|
private boolean isCullingCached;
|
||||||
private boolean isCulling;
|
private boolean isCulling;
|
||||||
@ -113,7 +113,7 @@ public boolean isOccludingNeighborFaces() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getBiome() {
|
public Biome getBiome() {
|
||||||
if (biome == null){
|
if (biome == null){
|
||||||
biome = block.getBiome();
|
biome = block.getBiome();
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
* THE SOFTWARE.
|
* THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
package de.bluecolored.bluemap.core.mca.mapping;
|
package de.bluecolored.bluemap.core.world;
|
||||||
|
|
||||||
public class LightData {
|
public class LightData {
|
||||||
|
|
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"variants": {
|
||||||
|
"": { "model": "block/lava" }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"variants": {
|
||||||
|
"": { "model": "block/water" }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"parent": "builtin/liquid",
|
||||||
|
"textures": {
|
||||||
|
"particle": "block/lava_still",
|
||||||
|
"still": "block/lava_still",
|
||||||
|
"flow": "block/lava_flow"
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"parent": "builtin/liquid",
|
||||||
|
"textures": {
|
||||||
|
"particle": "block/water_still",
|
||||||
|
"still": "block/water_still",
|
||||||
|
"flow": "block/water_flow"
|
||||||
|
}
|
||||||
|
}
|
@ -1,11 +1,12 @@
|
|||||||
{
|
{
|
||||||
"default": "#foliage",
|
"default": "@foliage",
|
||||||
"grass_block" : "#grass",
|
"minecraft:water" : "@water",
|
||||||
"grass" : "#grass",
|
"minecraft:grass_block" : "@grass",
|
||||||
"tall_grass" : "#grass",
|
"minecraft:grass" : "@grass",
|
||||||
"fern" : "#grass",
|
"minecraft:tall_grass" : "@grass",
|
||||||
"large_fern" : "#grass",
|
"minecraft:fern" : "@grass",
|
||||||
"redstone_wire" : "ff0000",
|
"minecraft:large_fern" : "@grass",
|
||||||
"birch_leaves" : "86a863",
|
"minecraft:redstone_wire" : "#ff0000",
|
||||||
"spruce_leaves" : "51946b"
|
"minecraft:birch_leaves" : "#86a863",
|
||||||
|
"minecraft:spruce_leaves" : "#51946b"
|
||||||
}
|
}
|
BIN
BlueMapCore/src/main/resources/resourceExtensions.zip
Normal file
BIN
BlueMapCore/src/main/resources/resourceExtensions.zip
Normal file
Binary file not shown.
@ -53,7 +53,17 @@ public void logInfo(String message) {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void logDebug(String message) {
|
public void logDebug(String message) {
|
||||||
out.debug(message);
|
if (out.isDebugEnabled()) out.debug(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void noFloodDebug(String message) {
|
||||||
|
if (out.isDebugEnabled()) super.noFloodDebug(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void noFloodDebug(String key, String message) {
|
||||||
|
if (out.isDebugEnabled()) super.noFloodDebug(key, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -45,6 +45,7 @@
|
|||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import org.apache.commons.io.FileUtils;
|
||||||
import org.bstats.sponge.MetricsLite2;
|
import org.bstats.sponge.MetricsLite2;
|
||||||
import org.spongepowered.api.Sponge;
|
import org.spongepowered.api.Sponge;
|
||||||
import org.spongepowered.api.config.ConfigDir;
|
import org.spongepowered.api.config.ConfigDir;
|
||||||
@ -68,7 +69,7 @@
|
|||||||
import de.bluecolored.bluemap.core.render.TileRenderer;
|
import de.bluecolored.bluemap.core.render.TileRenderer;
|
||||||
import de.bluecolored.bluemap.core.render.hires.HiresModelManager;
|
import de.bluecolored.bluemap.core.render.hires.HiresModelManager;
|
||||||
import de.bluecolored.bluemap.core.render.lowres.LowresModelManager;
|
import de.bluecolored.bluemap.core.render.lowres.LowresModelManager;
|
||||||
import de.bluecolored.bluemap.core.resourcepack.NoSuchResourceException;
|
import de.bluecolored.bluemap.core.resourcepack.ParseResourceException;
|
||||||
import de.bluecolored.bluemap.core.resourcepack.ResourcePack;
|
import de.bluecolored.bluemap.core.resourcepack.ResourcePack;
|
||||||
import de.bluecolored.bluemap.core.web.BlueMapWebServer;
|
import de.bluecolored.bluemap.core.web.BlueMapWebServer;
|
||||||
import de.bluecolored.bluemap.core.web.WebFilesManager;
|
import de.bluecolored.bluemap.core.web.WebFilesManager;
|
||||||
@ -125,7 +126,7 @@ public SpongePlugin(org.slf4j.Logger logger) {
|
|||||||
instance = this;
|
instance = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void load() throws ExecutionException, IOException, NoSuchResourceException, InterruptedException {
|
public synchronized void load() throws ExecutionException, IOException, InterruptedException, ParseResourceException {
|
||||||
if (loaded) return;
|
if (loaded) return;
|
||||||
unload(); //ensure nothing is left running (from a failed load or something)
|
unload(); //ensure nothing is left running (from a failed load or something)
|
||||||
|
|
||||||
@ -133,8 +134,14 @@ public synchronized void load() throws ExecutionException, IOException, NoSuchRe
|
|||||||
File configFile = getConfigPath().resolve("bluemap.conf").toFile();
|
File configFile = getConfigPath().resolve("bluemap.conf").toFile();
|
||||||
config = ConfigurationFile.loadOrCreate(configFile, SpongePlugin.class.getResource("/bluemap-sponge.conf")).getConfig();
|
config = ConfigurationFile.loadOrCreate(configFile, SpongePlugin.class.getResource("/bluemap-sponge.conf")).getConfig();
|
||||||
|
|
||||||
|
File blockColorsConfigFile = getConfigPath().resolve("blockColors.json").toFile();
|
||||||
|
if (!blockColorsConfigFile.exists()) {
|
||||||
|
FileUtils.copyURLToFile(SpongePlugin.class.getResource("/blockColors.json"), blockColorsConfigFile, 10000, 10000);
|
||||||
|
}
|
||||||
|
|
||||||
//load resources
|
//load resources
|
||||||
File defaultResourceFile = config.getDataPath().resolve("minecraft-client-" + ResourcePack.MINECRAFT_CLIENT_VERSION + ".jar").toFile();
|
File defaultResourceFile = config.getDataPath().resolve("minecraft-client-" + ResourcePack.MINECRAFT_CLIENT_VERSION + ".jar").toFile();
|
||||||
|
File resourceExtensionsFile = config.getDataPath().resolve("resourceExtensions.zip").toFile();
|
||||||
File textureExportFile = config.getWebDataPath().resolve("textures.json").toFile();
|
File textureExportFile = config.getWebDataPath().resolve("textures.json").toFile();
|
||||||
|
|
||||||
if (!defaultResourceFile.exists()) {
|
if (!defaultResourceFile.exists()) {
|
||||||
@ -145,6 +152,9 @@ public synchronized void load() throws ExecutionException, IOException, NoSuchRe
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resourceExtensionsFile.delete();
|
||||||
|
FileUtils.copyURLToFile(SpongePlugin.class.getResource("/resourceExtensions.zip"), resourceExtensionsFile, 10000, 10000);
|
||||||
|
|
||||||
//find more resource packs
|
//find more resource packs
|
||||||
File resourcePackFolder = getConfigPath().resolve("resourcepacks").toFile();
|
File resourcePackFolder = getConfigPath().resolve("resourcepacks").toFile();
|
||||||
resourcePackFolder.mkdirs();
|
resourcePackFolder.mkdirs();
|
||||||
@ -154,8 +164,13 @@ public synchronized void load() throws ExecutionException, IOException, NoSuchRe
|
|||||||
List<File> resources = new ArrayList<>(resourcePacks.length + 1);
|
List<File> resources = new ArrayList<>(resourcePacks.length + 1);
|
||||||
resources.add(defaultResourceFile);
|
resources.add(defaultResourceFile);
|
||||||
for (File file : resourcePacks) resources.add(file);
|
for (File file : resourcePacks) resources.add(file);
|
||||||
|
resources.add(resourceExtensionsFile);
|
||||||
|
|
||||||
resourcePack = new ResourcePack(resources, textureExportFile);
|
resourcePack = new ResourcePack();
|
||||||
|
if (textureExportFile.exists()) resourcePack.loadTextureFile(textureExportFile);
|
||||||
|
resourcePack.load(resources);
|
||||||
|
resourcePack.loadBlockColorConfig(blockColorsConfigFile);
|
||||||
|
resourcePack.saveTextureFile(textureExportFile);
|
||||||
|
|
||||||
//load maps
|
//load maps
|
||||||
for (MapConfig mapConfig : config.getMapConfigs()) {
|
for (MapConfig mapConfig : config.getMapConfigs()) {
|
||||||
@ -319,7 +334,7 @@ public synchronized void unload() {
|
|||||||
loaded = false;
|
loaded = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void reload() throws IOException, NoSuchResourceException, ExecutionException, InterruptedException {
|
public synchronized void reload() throws IOException, ExecutionException, InterruptedException, ParseResourceException {
|
||||||
unload();
|
unload();
|
||||||
load();
|
load();
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user