Rewrite of the render-engine and big performance improvements

- changed most immutable vectors with mutable ones
- dropped 1.12.2 support
- improved caching and block/chunk access
- much more smaller tweaks
This commit is contained in:
Blue (Lukas Rieger) 2021-07-25 13:08:40 +02:00
parent 8838bd6f32
commit e178f935aa
No known key found for this signature in database
GPG Key ID: 904C4995F9E1F800
178 changed files with 1317 additions and 26641 deletions

View File

@ -30,7 +30,6 @@
import de.bluecolored.bluemap.core.MinecraftVersion;
import de.bluecolored.bluemap.core.config.*;
import de.bluecolored.bluemap.core.debug.DebugDump;
import de.bluecolored.bluemap.core.debug.OneBlockWorld;
import de.bluecolored.bluemap.core.logger.Logger;
import de.bluecolored.bluemap.core.map.BmMap;
import de.bluecolored.bluemap.core.map.hires.RenderSettings;
@ -136,9 +135,6 @@ private synchronized void loadWorldsAndMaps() throws IOException, InterruptedExc
maps = new HashMap<>();
worlds = new HashMap<>();
ConfigManager configManager = getConfigManager();
configManager.loadResourceConfigs(configFolder, getResourcePack());
for (MapConfig mapConfig : getRenderConfig().getMapConfigs()) {
String id = mapConfig.getId();
String name = mapConfig.getName();
@ -160,7 +156,7 @@ private synchronized void loadWorldsAndMaps() throws IOException, InterruptedExc
World world = worlds.get(worldUUID);
if (world == null) {
try {
world = MCAWorld.load(worldFolder.toPath(), worldUUID, minecraftVersion, configManager.getBlockIdConfig(), configManager.getBlockPropertiesConfig(), configManager.getBiomeConfig(), worldNameProvider.apply(worldUUID), mapConfig.isIgnoreMissingLightData());
world = MCAWorld.load(worldFolder.toPath(), worldUUID, worldNameProvider.apply(worldUUID), mapConfig.isIgnoreMissingLightData());
worlds.put(worldUUID, world);
} catch (MissingResourcesException e) {
throw e; // rethrow this to stop loading and display resource-missing message
@ -230,7 +226,10 @@ public synchronized ResourcePack getResourcePack() throws IOException, Interrupt
if (resourceExtensionsFile.exists()) FileUtils.forceDelete(resourceExtensionsFile);
FileUtils.forceMkdirParent(resourceExtensionsFile);
FileUtils.copyURLToFile(Plugin.class.getResource("/de/bluecolored/bluemap/" + minecraftVersion.getResource().getResourcePrefix() + "/resourceExtensions.zip"), resourceExtensionsFile, 10000, 10000);
URL resourceExtensionsUrl = Objects.requireNonNull(
Plugin.class.getResource("/de/bluecolored/bluemap/" + minecraftVersion.getResource().getResourcePrefix() + "/resourceExtensions.zip")
);
FileUtils.copyURLToFile(resourceExtensionsUrl, resourceExtensionsFile, 10000, 10000);
//find more resource packs
File[] resourcePacks = resourcePackFolder.listFiles();
@ -243,7 +242,7 @@ public synchronized ResourcePack getResourcePack() throws IOException, Interrupt
resources.add(resourceExtensionsFile);
try {
resourcePack = new ResourcePack(minecraftVersion);
resourcePack = new ResourcePack();
if (textureExportFile.exists()) resourcePack.loadTextureFile(textureExportFile);
resourcePack.load(resources);
resourcePack.saveTextureFile(textureExportFile);

View File

@ -59,10 +59,8 @@
import de.bluecolored.bluemap.core.logger.Logger;
import de.bluecolored.bluemap.core.map.BmMap;
import de.bluecolored.bluemap.core.map.MapRenderState;
import de.bluecolored.bluemap.core.mca.ChunkAnvil112;
import de.bluecolored.bluemap.core.resourcepack.ParseResourceException;
import de.bluecolored.bluemap.core.world.Block;
import de.bluecolored.bluemap.core.world.Chunk;
import de.bluecolored.bluemap.core.world.World;
import java.io.IOException;
@ -528,24 +526,21 @@ public int debugBlockCommand(CommandContext<S> context) {
new Thread(() -> {
// collect and output debug info
Vector3i blockPos = position.floor().toInt();
Block block = world.getBlock(blockPos);
Block blockBelow = world.getBlock(blockPos.add(0, -1, 0));
Block<?> block = new Block<>(world, blockPos.getX(), blockPos.getY(), blockPos.getZ());
Block<?> blockBelow = new Block<>(null, 0, 0, 0).copy(block, 0, -1, 0);
String blockIdMeta = "";
String blockBelowIdMeta = "";
// populate lazy-loaded values
block.getBlockState();
block.getBiomeId();
block.getLightData();
Vector2i chunkPos = world.getChunkGrid().getCell(blockPos.toVector2(true));
Chunk chunk = world.getChunk(chunkPos.getX(), chunkPos.getY());
if (chunk instanceof ChunkAnvil112) {
blockIdMeta = " (" + ((ChunkAnvil112) chunk).getBlockIdMeta(blockPos) + ")";
blockBelowIdMeta = " (" + ((ChunkAnvil112) chunk).getBlockIdMeta(blockPos.add(0, -1, 0)) + ")";
}
blockBelow.getBlockState();
blockBelow.getBiomeId();
blockBelow.getLightData();
source.sendMessages(Arrays.asList(
Text.of(TextColor.GOLD, "Block at you: ", TextColor.WHITE, block, TextColor.GRAY, blockIdMeta),
Text.of(TextColor.GOLD, "Block below you: ", TextColor.WHITE, blockBelow, TextColor.GRAY, blockBelowIdMeta),
Text.of(TextColor.GOLD, "Chunk: ", TextColor.WHITE, chunk)
Text.of(TextColor.GOLD, "Block at you: ", TextColor.WHITE, block),
Text.of(TextColor.GOLD, "Block below you: ", TextColor.WHITE, blockBelow)
));
}).start();

View File

@ -1,85 +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.config;
import de.bluecolored.bluemap.core.logger.Logger;
import de.bluecolored.bluemap.core.mca.mapping.BiomeMapper;
import de.bluecolored.bluemap.core.world.Biome;
import org.spongepowered.configurate.ConfigurationNode;
import org.spongepowered.configurate.loader.ConfigurationLoader;
import java.io.IOException;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
public class BiomeConfig implements BiomeMapper {
private final ConfigurationLoader<? extends ConfigurationNode> autopoulationConfigLoader;
private final Map<Integer, Biome> biomes;
public BiomeConfig(ConfigurationNode node) {
this(node, null);
}
public BiomeConfig(ConfigurationNode node, ConfigurationLoader<? extends ConfigurationNode> autopoulationConfigLoader) {
this.autopoulationConfigLoader = autopoulationConfigLoader;
biomes = new ConcurrentHashMap<>(200, 0.5f, 8);
for (Entry<Object, ? extends ConfigurationNode> e : node.childrenMap().entrySet()){
String id = e.getKey().toString();
Biome biome = Biome.create(id, e.getValue());
biomes.put(biome.getNumeralId(), biome);
}
}
@Override
public Biome get(int id) {
Biome biome = biomes.get(id);
if (biome == null) {
if (autopoulationConfigLoader != null) {
biomes.put(id, Biome.DEFAULT);
synchronized (autopoulationConfigLoader) {
try {
ConfigurationNode node = autopoulationConfigLoader.load();
node.node("unknown:" + id).node("id").set(id);
autopoulationConfigLoader.save(node);
} catch (IOException ex) {
Logger.global.noFloodError("biomeconf-autopopulate-ioex", "Failed to auto-populate BiomeConfig!", ex);
}
}
}
return Biome.DEFAULT;
}
return biome;
}
}

View File

@ -1,252 +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.config;
import de.bluecolored.bluemap.core.MinecraftVersion;
import de.bluecolored.bluemap.core.logger.Logger;
import de.bluecolored.bluemap.core.mca.mapping.BlockIdMapper;
import de.bluecolored.bluemap.core.world.BlockState;
import org.spongepowered.configurate.ConfigurationNode;
import org.spongepowered.configurate.loader.ConfigurationLoader;
import java.io.IOException;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class BlockIdConfig implements BlockIdMapper {
private final ConfigurationLoader<? extends ConfigurationNode> autopoulationConfigLoader;
private final Map<BlockNumeralIDMeta, BlockState> numeralMappings;
private final Map<BlockIDMeta, BlockState> idMappings;
private final ReentrantReadWriteLock lock;
public BlockIdConfig(ConfigurationNode node) {
this(node, null);
}
public BlockIdConfig(ConfigurationNode node, ConfigurationLoader<? extends ConfigurationNode> autopoulationConfigLoader) {
this.autopoulationConfigLoader = autopoulationConfigLoader;
this.numeralMappings = new ConcurrentHashMap<>(200, 0.5f, 8);
this.idMappings = new ConcurrentHashMap<>(200, 0.5f, 8);
this.lock = new ReentrantReadWriteLock();
for (Entry<Object, ? extends ConfigurationNode> e : node.childrenMap().entrySet()){
String key = e.getKey().toString();
String value = e.getValue().getString();
try {
int splitIndex = key.lastIndexOf(':');
if (splitIndex <= 0 || splitIndex >= key.length() - 1) {
Logger.global.logWarning("Loading BlockIdConfig: Failed to parse blockid:meta from key '" + key + "'");
continue;
}
String blockId = key.substring(0, splitIndex);
int blockNumeralId;
try {
blockNumeralId = Integer.parseInt(blockId);
} catch (NumberFormatException ex) {
blockNumeralId = -1;
}
int blockMeta = Integer.parseInt(key.substring(splitIndex + 1));
BlockState state = BlockState.fromString(MinecraftVersion.EARLIEST_SUPPORTED, value);
if (blockNumeralId >= 0) {
BlockNumeralIDMeta idmeta = new BlockNumeralIDMeta(blockNumeralId, blockMeta);
if (blockNumeralId == 0) state = BlockState.AIR; //use the static field to increase render speed (== comparison)
numeralMappings.put(idmeta, state);
} else {
BlockIDMeta idmeta = new BlockIDMeta(blockId, blockMeta);
idMappings.put(idmeta, state);
}
} catch (NumberFormatException ex) {
Logger.global.logWarning("Loading BlockIdConfig: Failed to parse blockid:meta from key '" + key + "'");
} catch (IllegalArgumentException ex) {
Logger.global.logWarning("Loading BlockIdConfig: Failed to parse BlockState from value '" + value + "'");
}
}
}
@Override
public BlockState get(int numeralId, int meta) {
if (numeralId == 0) return BlockState.AIR;
BlockNumeralIDMeta numidmeta;
BlockState state;
try { lock.readLock().lock();
numidmeta = new BlockNumeralIDMeta(numeralId, meta);
state = numeralMappings.get(numidmeta);
} finally { lock.readLock().unlock(); }
if (state == null) {
try { lock.writeLock().lock();
state = numeralMappings.getOrDefault(new BlockNumeralIDMeta(numeralId, 0), BlockState.MISSING); //meta-fallback
numeralMappings.put(numidmeta, state);
if (autopoulationConfigLoader != null) {
try {
ConfigurationNode node = autopoulationConfigLoader.load();
node.node(numeralId + ":" + meta).set(state.toString());
autopoulationConfigLoader.save(node);
} catch (IOException ex) {
Logger.global.noFloodError("blockidconf-autopopulate-ioex", "Failed to auto-populate BlockIdConfig!", ex);
}
}
} finally { lock.writeLock().unlock(); }
}
return state;
}
@Override
public BlockState get(String id, int numeralId, int meta) {
if (numeralId == 0) return BlockState.AIR;
BlockNumeralIDMeta numidmeta;
BlockState state;
BlockIDMeta idmeta = null;
try { lock.readLock().lock();
numidmeta = new BlockNumeralIDMeta(numeralId, meta);
state = numeralMappings.get(numidmeta);
if (state == null) {
idmeta = new BlockIDMeta(id, meta);
state = idMappings.get(idmeta);
}
} finally { lock.readLock().unlock(); }
if (state == null) {
try { lock.writeLock().lock();
state = idMappings.get(new BlockIDMeta(id, 0));
if (state == null) {
state = numeralMappings.get(new BlockNumeralIDMeta(numeralId, 0));
if (state == null) state = new BlockState(MinecraftVersion.EARLIEST_SUPPORTED, id);
}
idMappings.put(idmeta, state);
if (autopoulationConfigLoader != null) {
try {
ConfigurationNode node = autopoulationConfigLoader.load();
node.node(id + ":" + meta).set(state.toString());
autopoulationConfigLoader.save(node);
} catch (IOException ex) {
Logger.global.noFloodError("blockidconf-autopopulate-ioex", "Failed to auto-populate BlockIdConfig!", ex);
}
}
} finally { lock.writeLock().unlock(); }
}
return state;
}
static class BlockNumeralIDMeta {
private final int id;
private final int meta;
public BlockNumeralIDMeta(int id, int meta) {
this.id = id;
this.meta = meta;
}
public int getId() {
return id;
}
public int getMeta() {
return meta;
}
@Override
public int hashCode() {
return id * 16 + meta;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof BlockNumeralIDMeta) {
BlockNumeralIDMeta other = (BlockNumeralIDMeta) obj;
return other.id == id && other.meta == meta;
}
return false;
}
}
static class BlockIDMeta {
private final String id;
private final int meta;
public BlockIDMeta(String id, int meta) {
this.id = id;
this.meta = meta;
}
public String getId() {
return id;
}
public int getMeta() {
return meta;
}
@Override
public int hashCode() {
return id.hashCode() * 16 + meta;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof BlockIDMeta) {
BlockIDMeta other = (BlockIDMeta) obj;
return other.id.equals(id) && other.meta == meta;
}
return false;
}
}
}

View File

@ -1,141 +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.config;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import de.bluecolored.bluemap.core.BlueMap;
import de.bluecolored.bluemap.core.MinecraftVersion;
import de.bluecolored.bluemap.core.logger.Logger;
import de.bluecolored.bluemap.core.mca.mapping.BlockPropertiesMapper;
import de.bluecolored.bluemap.core.resourcepack.NoSuchResourceException;
import de.bluecolored.bluemap.core.resourcepack.ResourcePack;
import de.bluecolored.bluemap.core.resourcepack.TransformedBlockModelResource;
import de.bluecolored.bluemap.core.world.BlockProperties;
import de.bluecolored.bluemap.core.world.BlockState;
import org.spongepowered.configurate.ConfigurationNode;
import org.spongepowered.configurate.loader.ConfigurationLoader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
public class BlockPropertiesConfig implements BlockPropertiesMapper {
private final ConfigurationLoader<? extends ConfigurationNode> autopoulationConfigLoader;
private final Map<String, List<BlockStateMapping<BlockProperties>>> mappings;
private final LoadingCache<BlockState, BlockProperties> mappingCache;
private final ResourcePack resourcePack;
public BlockPropertiesConfig(ConfigurationNode node, ResourcePack resourcePack) {
this(node, resourcePack, null);
}
public BlockPropertiesConfig(ConfigurationNode node, ResourcePack resourcePack, ConfigurationLoader<? extends ConfigurationNode> autopoulationConfigLoader) {
this.resourcePack = resourcePack;
this.autopoulationConfigLoader = autopoulationConfigLoader;
mappings = new ConcurrentHashMap<>();
for (Entry<Object, ? extends ConfigurationNode> e : node.childrenMap().entrySet()){
String key = e.getKey().toString();
try {
BlockState bsKey = BlockState.fromString(resourcePack.getMinecraftVersion(), key);
BlockProperties bsValue = new BlockProperties(
e.getValue().node("culling").getBoolean(true),
e.getValue().node("occluding").getBoolean(true),
e.getValue().node("flammable").getBoolean(false)
);
BlockStateMapping<BlockProperties> mapping = new BlockStateMapping<>(bsKey, bsValue);
mappings.computeIfAbsent(bsKey.getFullId(), k -> new ArrayList<>()).add(mapping);
} catch (IllegalArgumentException ex) {
Logger.global.logWarning("Loading BlockPropertiesConfig: Failed to parse BlockState from key '" + key + "'");
}
}
mappingCache = Caffeine.newBuilder()
.executor(BlueMap.THREAD_POOL)
.maximumSize(10000)
.build(this::mapNoCache);
}
@Override
public BlockProperties get(BlockState from){
return mappingCache.get(from);
}
private BlockProperties mapNoCache(BlockState bs){
for (BlockStateMapping<BlockProperties> bm : mappings.getOrDefault(bs.getFullId(), Collections.emptyList())){
if (bm.fitsTo(bs)){
return bm.getMapping();
}
}
BlockProperties generated = BlockProperties.SOLID;
MinecraftVersion version = MinecraftVersion.LATEST_SUPPORTED;
if (resourcePack != null) {
try {
boolean culling = false;
boolean occluding = false;
for(TransformedBlockModelResource model : resourcePack.getBlockStateResource(bs).getModels(bs, new ArrayList<>(10))) {
culling = culling || model.getModel().isCulling();
occluding = occluding || model.getModel().isOccluding();
if (culling && occluding) break;
}
generated = new BlockProperties(culling, occluding, generated.isFlammable());
} catch (NoSuchResourceException ignore) {} //ignoring this because it will be logged later again if we try to render that block
version = resourcePack.getMinecraftVersion();
}
mappings.computeIfAbsent(bs.getFullId(), k -> new ArrayList<>()).add(new BlockStateMapping<>(new BlockState(version, bs.getFullId()), generated));
if (autopoulationConfigLoader != null) {
synchronized (autopoulationConfigLoader) {
try {
ConfigurationNode node = autopoulationConfigLoader.load();
ConfigurationNode bpNode = node.node(bs.getFullId());
bpNode.node("culling").set(generated.isCulling());
bpNode.node("occluding").set(generated.isOccluding());
bpNode.node("flammable").set(generated.isFlammable());
autopoulationConfigLoader.save(node);
} catch (IOException ex) {
Logger.global.noFloodError("blockpropsconf-autopopulate-ioex", "Failed to auto-populate BlockPropertiesConfig!", ex);
}
}
}
return generated;
}
}

View File

@ -26,16 +26,16 @@
import de.bluecolored.bluemap.core.BlueMap;
import de.bluecolored.bluemap.core.logger.Logger;
import de.bluecolored.bluemap.core.resourcepack.ResourcePack;
import de.bluecolored.bluemap.core.resourcepack.ResourcePack.Resource;
import de.bluecolored.bluemap.core.util.FileUtils;
import org.spongepowered.configurate.ConfigurationNode;
import org.spongepowered.configurate.gson.GsonConfigurationLoader;
import org.spongepowered.configurate.hocon.HoconConfigurationLoader;
import org.spongepowered.configurate.loader.ConfigurationLoader;
import java.io.*;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
@ -54,10 +54,6 @@ public class ConfigManager {
CONFIG_PLACEHOLDERS.add(new Placeholder("datetime-iso", () -> LocalDateTime.now().withNano(0).toString()));
}
private BlockIdConfig blockIdConfig;
private BlockPropertiesConfig blockPropertiesConfig;
private BiomeConfig biomeConfig;
/**
* Loads or creates a config file for BlueMap.
*
@ -66,8 +62,8 @@ public class ConfigManager {
* @param defaultValues The default values used if a key is not present in the config (can be null)
* @param usePlaceholders Whether to replace placeholders from the defaultConfig if it is newly generated
* @param generateEmptyConfig Whether to generate an empty config file if no default config is provided
* @return
* @throws IOException
* @return The loaded configuration node
* @throws IOException if an IOException occurs while loading
*/
public ConfigurationNode loadOrCreate(File configFile, URL defaultConfig, URL defaultValues, boolean usePlaceholders, boolean generateEmptyConfig) throws IOException {
@ -116,107 +112,6 @@ public ConfigurationNode loadOrCreate(File configFile, URL defaultConfig, URL de
return configNode;
}
public void loadResourceConfigs(File configFolder, ResourcePack resourcePack) throws IOException {
//load blockColors.json from resources, config-folder and resourcepack
URL blockColorsConfigUrl = BlueMap.class.getResource("/de/bluecolored/bluemap/" + resourcePack.getMinecraftVersion().getResource().getResourcePrefix() + "/blockColors.json");
File blockColorsConfigFile = new File(configFolder, "blockColors.json");
ConfigurationNode blockColorsConfigNode = loadOrCreate(
blockColorsConfigFile,
null,
blockColorsConfigUrl,
false,
false
);
blockColorsConfigNode = joinFromResourcePack(resourcePack, "blockColors.json", blockColorsConfigNode);
resourcePack.getBlockColorCalculatorFactory().loadColorConfig(blockColorsConfigNode);
//load blockIds.json from resources, config-folder and resourcepack
URL blockIdsConfigUrl = BlueMap.class.getResource("/de/bluecolored/bluemap/" + resourcePack.getMinecraftVersion().getResource().getResourcePrefix() + "/blockIds.json");
File blockIdsConfigFile = new File(configFolder, "blockIds.json");
ConfigurationNode blockIdsConfigNode = loadOrCreate(
blockIdsConfigFile,
null,
blockIdsConfigUrl,
false,
false
);
blockIdsConfigNode = joinFromResourcePack(resourcePack, "blockIds.json", blockIdsConfigNode);
blockIdConfig = new BlockIdConfig(
blockIdsConfigNode
);
//load blockProperties.json from resources, config-folder and resourcepack
URL blockPropertiesConfigUrl = BlueMap.class.getResource("/de/bluecolored/bluemap/" + resourcePack.getMinecraftVersion().getResource().getResourcePrefix() + "/blockProperties.json");
File blockPropertiesConfigFile = new File(configFolder, "blockProperties.json");
ConfigurationNode blockPropertiesConfigNode = loadOrCreate(
blockPropertiesConfigFile,
null,
blockPropertiesConfigUrl,
false,
false
);
blockPropertiesConfigNode = joinFromResourcePack(resourcePack, "blockProperties.json", blockPropertiesConfigNode);
blockPropertiesConfig = new BlockPropertiesConfig(
blockPropertiesConfigNode,
resourcePack
);
//load biomes.json from resources, config-folder and resourcepack
URL biomeConfigUrl = BlueMap.class.getResource("/de/bluecolored/bluemap/" + resourcePack.getMinecraftVersion().getResource().getResourcePrefix() + "/biomes.json");
File biomeConfigFile = new File(configFolder, "biomes.json");
ConfigurationNode biomeConfigNode = loadOrCreate(
biomeConfigFile,
null,
biomeConfigUrl,
false,
false
);
biomeConfigNode = joinFromResourcePack(resourcePack, "biomes.json", biomeConfigNode);
biomeConfig = new BiomeConfig(
biomeConfigNode
);
}
public BlockIdConfig getBlockIdConfig() {
return blockIdConfig;
}
public BlockPropertiesConfig getBlockPropertiesConfig() {
return blockPropertiesConfig;
}
public BiomeConfig getBiomeConfig() {
return biomeConfig;
}
private ConfigurationNode joinFromResourcePack(ResourcePack resourcePack, String configFileName, ConfigurationNode defaultConfig) {
ConfigurationNode joinedNode = null;
for (Resource resource : resourcePack.getConfigAdditions(configFileName)) {
try {
ConfigurationNode node = getLoader(configFileName, resource.read()).load();
if (joinedNode == null) joinedNode = node;
else joinedNode.mergeFrom(node);
} catch (IOException ex) {
Logger.global.logWarning("Failed to load an additional " + configFileName + " from the resource-pack! " + ex);
}
}
if (joinedNode == null) return defaultConfig;
joinedNode.mergeFrom(defaultConfig);
return joinedNode;
}
private ConfigurationLoader<? extends ConfigurationNode> getLoader(String filename, InputStream is){
BufferedReader reader = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8));
if (filename.endsWith(".json")) return GsonConfigurationLoader.builder().source(() -> reader).build();
else return HoconConfigurationLoader.builder().source(() -> reader).build();
}
private ConfigurationLoader<? extends ConfigurationNode> getLoader(URL url){
if (url.getFile().endsWith(".json")) return GsonConfigurationLoader.builder().url(url).build();
else return HoconConfigurationLoader.builder().url(url).build();

View File

@ -1,9 +1,35 @@
/*
* 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.debug;
import com.flowpowered.math.vector.Vector2i;
import com.flowpowered.math.vector.Vector3i;
import de.bluecolored.bluemap.core.mca.mapping.BlockPropertiesMapper;
import de.bluecolored.bluemap.core.world.*;
import de.bluecolored.bluemap.core.world.Chunk;
import de.bluecolored.bluemap.core.world.Grid;
import de.bluecolored.bluemap.core.world.Region;
import de.bluecolored.bluemap.core.world.World;
import java.nio.file.Path;
import java.util.Collection;
@ -62,22 +88,6 @@ public Grid getRegionGrid() {
return delegate.getRegionGrid();
}
@Override
public Biome getBiome(int x, int y, int z) {
return Biome.DEFAULT;
}
@Override
public BlockState getBlockState(int x, int y, int z) {
if (x == 0 && z == 0 && y == 70) return BlockState.MISSING;
return BlockState.AIR;
}
@Override
public BlockProperties getBlockProperties(BlockState blockState) {
return delegate.getBlockProperties(blockState);
}
@Override
public Chunk getChunkAtBlock(int x, int y, int z) {
return delegate.getChunkAtBlock(x, y, z);
@ -113,9 +123,4 @@ public void cleanUpChunkCache() {
delegate.cleanUpChunkCache();
}
@Override
public BlockPropertiesMapper getBlockPropertiesMapper() {
return delegate.getBlockPropertiesMapper();
}
}

View File

@ -1,3 +1,27 @@
/*
* 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.map.hires;
import de.bluecolored.bluemap.core.util.math.MatrixM3f;

View File

@ -29,7 +29,7 @@
import de.bluecolored.bluemap.core.resourcepack.NoSuchResourceException;
import de.bluecolored.bluemap.core.resourcepack.ResourcePack;
import de.bluecolored.bluemap.core.util.math.Color;
import de.bluecolored.bluemap.core.world.Block;
import de.bluecolored.bluemap.core.world.BlockNeighborhood;
import de.bluecolored.bluemap.core.world.BlockState;
import de.bluecolored.bluemap.core.world.World;
@ -54,9 +54,8 @@ public HiresTileMeta render(World world, Vector3i modelMin, Vector3i modelMax, H
BlockStateModelFactory modelFactory = new BlockStateModelFactory(resourcePack, renderSettings);
int maxHeight, minY, maxY;
float dx, dz;
Color columnColor = new Color(), blockColor = new Color();
Block block = new Block(world, 0, 0, 0);
BlockNeighborhood<?> block = new BlockNeighborhood<>(resourcePack, world, 0, 0, 0);
BlockModelView blockModel = new BlockModelView(model);
int x, y, z;
@ -96,13 +95,6 @@ public HiresTileMeta render(World world, Vector3i modelMin, Vector3i modelMax, H
maxHeight = y;
columnColor.overlay(blockColor);
}
//random offset
if (block.getBlockState().isRandomOffset){
dx = (hashToFloat(x, z, 123984) - 0.5f) * 0.75f;
dz = (hashToFloat(x, z, 345542) - 0.5f) * 0.75f;
blockModel.translate(dx, 0, dz);
}
}
tileMeta.setHeight(x, z, maxHeight);
@ -113,20 +105,4 @@ public HiresTileMeta render(World world, Vector3i modelMin, Vector3i modelMax, H
return tileMeta;
}
/**
* 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 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 z, long seed) {
final long hash = x * 73428767 ^ z * 4382893 ^ seed * 457;
return (hash * (hash + 456149) & 0x00ffffff) / (float) 0x01000000;
}
}

View File

@ -1,3 +1,27 @@
/*
* 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.map.hires;
import de.bluecolored.bluemap.core.util.math.Color;
@ -37,7 +61,7 @@ public void setColor(int x, int z, Color color) {
}
private void setColor(int x, int z, float r, float g, float b, float a) {
int index = (x - minX) * sizeZ + (z - minZ) * 4;
int index = ((x - minX) * sizeZ + (z - minZ)) * 4;
colors[index ] = r;
colors[index + 1] = g;
colors[index + 2] = b;
@ -45,7 +69,7 @@ private void setColor(int x, int z, float r, float g, float b, float a) {
}
public Color getColor(int x, int z, Color target) {
int index = (x - minX) * sizeZ + (z - minZ) * 4;
int index = ((x - minX) * sizeZ + (z - minZ)) * 4;
return target.set(
colors[index ],
colors[index + 1],

View File

@ -24,12 +24,14 @@
*/
package de.bluecolored.bluemap.core.map.hires.blockmodel;
import de.bluecolored.bluemap.core.MinecraftVersion;
import de.bluecolored.bluemap.core.map.hires.BlockModelView;
import de.bluecolored.bluemap.core.map.hires.RenderSettings;
import de.bluecolored.bluemap.core.resourcepack.*;
import de.bluecolored.bluemap.core.resourcepack.blockstate.BlockStateResource;
import de.bluecolored.bluemap.core.resourcepack.NoSuchResourceException;
import de.bluecolored.bluemap.core.resourcepack.ResourcePack;
import de.bluecolored.bluemap.core.resourcepack.blockmodel.TransformedBlockModelResource;
import de.bluecolored.bluemap.core.util.math.Color;
import de.bluecolored.bluemap.core.world.Block;
import de.bluecolored.bluemap.core.world.BlockNeighborhood;
import de.bluecolored.bluemap.core.world.BlockState;
import java.util.ArrayList;
@ -46,25 +48,20 @@ public class BlockStateModelFactory {
public BlockStateModelFactory(ResourcePack resourcePack, RenderSettings renderSettings) {
this.resourcePack = resourcePack;
Block[] neighborCache = new Block[3 * 3 * 3];
for (int i = 0; i < neighborCache.length; i++) {
neighborCache[i] = new Block(null, 0, 0, 0);
}
this.resourceModelBuilder = new ResourceModelBuilder(resourcePack, renderSettings, neighborCache);
this.liquidModelBuilder = new LiquidModelBuilder(resourcePack, renderSettings, neighborCache);
this.resourceModelBuilder = new ResourceModelBuilder(resourcePack, renderSettings);
this.liquidModelBuilder = new LiquidModelBuilder(resourcePack, renderSettings);
this.bmrs = new ArrayList<>();
}
public void render(Block block, BlockModelView blockModel, Color blockColor) throws NoSuchResourceException {
public void render(BlockNeighborhood<?> block, BlockModelView blockModel, Color blockColor) throws NoSuchResourceException {
render(block, block.getBlockState(), blockModel, blockColor);
}
public void render(Block block, BlockState blockState, BlockModelView blockModel, Color blockColor) throws NoSuchResourceException {
public void render(BlockNeighborhood<?> block, BlockState blockState, BlockModelView blockModel, Color blockColor) throws NoSuchResourceException {
//shortcut for air
if (blockState.isAir) return;
if (blockState.isAir()) return;
int modelStart = blockModel.getStart();
@ -72,15 +69,14 @@ public void render(Block block, BlockState blockState, BlockModelView blockModel
renderModel(block, blockState, blockModel.initialize(), blockColor);
// add water if block is waterlogged
if (blockState.isWaterlogged) {
if (blockState.isWaterlogged() || block.getProperties().isAlwaysWaterlogged()) {
renderModel(block, WATERLOGGED_BLOCKSTATE, blockModel.initialize(), blockColor);
}
blockModel.initialize(modelStart);
}
private void renderModel(Block block, BlockState blockState, BlockModelView blockModel, Color blockColor) throws NoSuchResourceException {
private void renderModel(BlockNeighborhood<?> block, BlockState blockState, BlockModelView blockModel, Color blockColor) throws NoSuchResourceException {
int modelStart = blockModel.getStart();
BlockStateResource resource = resourcePack.getBlockStateResource(blockState);
@ -98,6 +94,6 @@ private void renderModel(Block block, BlockState blockState, BlockModelView bloc
blockModel.initialize(modelStart);
}
private final static BlockState WATERLOGGED_BLOCKSTATE = new BlockState(MinecraftVersion.LATEST_SUPPORTED, "minecraft:water");
private final static BlockState WATERLOGGED_BLOCKSTATE = new BlockState("minecraft:water");
}

View File

@ -26,22 +26,21 @@
import com.flowpowered.math.TrigMath;
import com.flowpowered.math.vector.Vector3i;
import de.bluecolored.bluemap.core.MinecraftVersion;
import de.bluecolored.bluemap.core.logger.Logger;
import de.bluecolored.bluemap.core.map.hires.BlockModelView;
import de.bluecolored.bluemap.core.map.hires.HiresTileModel;
import de.bluecolored.bluemap.core.map.hires.RenderSettings;
import de.bluecolored.bluemap.core.resourcepack.BlockColorCalculatorFactory;
import de.bluecolored.bluemap.core.resourcepack.ResourcePack;
import de.bluecolored.bluemap.core.resourcepack.Texture;
import de.bluecolored.bluemap.core.resourcepack.TransformedBlockModelResource;
import de.bluecolored.bluemap.core.resourcepack.blockmodel.TransformedBlockModelResource;
import de.bluecolored.bluemap.core.resourcepack.texture.Texture;
import de.bluecolored.bluemap.core.util.Direction;
import de.bluecolored.bluemap.core.util.math.Color;
import de.bluecolored.bluemap.core.util.math.MatrixM3f;
import de.bluecolored.bluemap.core.util.math.VectorM2f;
import de.bluecolored.bluemap.core.util.math.VectorM3f;
import de.bluecolored.bluemap.core.world.Block;
import de.bluecolored.bluemap.core.world.BlockNeighborhood;
import de.bluecolored.bluemap.core.world.BlockState;
import de.bluecolored.bluemap.core.world.ResourcePackBlock;
/**
* A model builder for all liquid blocks
@ -57,24 +56,19 @@ public class LiquidModelBuilder {
private final BlockColorCalculatorFactory.BlockColorCalculator blockColorCalculator;
private final RenderSettings renderSettings;
private final boolean useWaterColorMap;
private final VectorM3f[] corners;
private final Block[] blocksAround;
private final VectorM2f[] uvs = new VectorM2f[4];
private Block block;
private BlockNeighborhood<?> block;
private BlockState blockState;
private TransformedBlockModelResource blockModelResource;
private BlockModelView blockModel;
private Color blockColor;
public LiquidModelBuilder(ResourcePack resourcePack, RenderSettings renderSettings, Block[] neighborCache) {
public LiquidModelBuilder(ResourcePack resourcePack, RenderSettings renderSettings) {
this.blockColorCalculator = resourcePack.getBlockColorCalculatorFactory().createCalculator();
this.renderSettings = renderSettings;
this.useWaterColorMap = resourcePack.getMinecraftVersion().isAtLeast(new MinecraftVersion(1, 13));
corners = new VectorM3f[]{
new VectorM3f( 0, 0, 0 ),
new VectorM3f( 0, 0, 16 ),
@ -86,12 +80,10 @@ public LiquidModelBuilder(ResourcePack resourcePack, RenderSettings renderSettin
new VectorM3f( 16, 16, 16 ),
};
this.blocksAround = neighborCache;
for (int i = 0; i < uvs.length; i++) uvs[i] = new VectorM2f(0, 0);
}
public void build(Block block, BlockState blockState, TransformedBlockModelResource bmr, BlockModelView blockModel, Color color) {
public void build(BlockNeighborhood<?> block, BlockState blockState, TransformedBlockModelResource bmr, BlockModelView blockModel, Color color) {
this.block = block;
this.blockState = blockState;
this.blockModelResource = bmr;
@ -107,7 +99,7 @@ private void build() {
int level = getLiquidLevel(blockState);
if (level < 8 && !(level == 0 && isSameLiquid(getNeighborBlock(0, 1, 0).getBlockState()))){
if (level < 8 && !(level == 0 && isSameLiquid(block.getNeighborBlock(0, 1, 0)))){
corners[4].y = getLiquidCornerHeight(-1, -1);
corners[5].y = getLiquidCornerHeight(-1, 0);
corners[6].y = getLiquidCornerHeight(0, -1);
@ -126,7 +118,7 @@ private void build() {
int flowTextureId = flowTexture.getId();
tintcolor.set(1f, 1f, 1f, 1f, true);
if (useWaterColorMap && blockState.isWater) {
if (blockState.isWater()) {
blockColorCalculator.getWaterAverageColor(block, tintcolor);
}
@ -166,7 +158,7 @@ private float getLiquidCornerHeight(int x, int z){
for (ix = x; ix <= x+1; ix++){
for (iz = z; iz<= z+1; iz++){
if (isSameLiquid(getNeighborBlock(ix, 1, iz).getBlockState())){
if (isSameLiquid(block.getNeighborBlock(ix, 1, iz))){
return 16f;
}
}
@ -174,12 +166,14 @@ private float getLiquidCornerHeight(int x, int z){
float sumHeight = 0f;
int count = 0;
ResourcePackBlock<?> neighbor;
BlockState neighborBlockState;
for (ix = x; ix <= x+1; ix++){
for (iz = z; iz<= z+1; iz++){
neighborBlockState = getNeighborBlock(ix, 0, iz).getBlockState();
if (isSameLiquid(neighborBlockState)){
neighbor = block.getNeighborBlock(ix, 0, iz);
neighborBlockState = neighbor.getBlockState();
if (isSameLiquid(neighbor)){
if (getLiquidLevel(neighborBlockState) == 0) return 14f;
sumHeight += getLiquidBaseHeight(neighborBlockState);
@ -203,9 +197,9 @@ private boolean isLiquidBlockingBlock(BlockState blockState){
return !blockState.equals(BlockState.AIR);
}
private boolean isSameLiquid(BlockState blockState){
if (blockState.getFullId().equals(this.blockState.getFullId())) return true;
return this.blockState.isWater && blockState.isWaterlogged;
private boolean isSameLiquid(ResourcePackBlock<?> block){
if (block.getBlockState().getFullId().equals(this.blockState.getFullId())) return true;
return this.blockState.isWater() && (block.getBlockState().isWaterlogged() || block.getProperties().isAlwaysWaterlogged());
}
private float getLiquidBaseHeight(BlockState block){
@ -223,13 +217,13 @@ private boolean createElementFace(Direction faceDir, VectorM3f c0, VectorM3f c1,
Vector3i faceDirVector = faceDir.toVector();
//face culling
Block bl = getNeighborBlock(
ResourcePackBlock<?> bl = block.getNeighborBlock(
faceDirVector.getX(),
faceDirVector.getY(),
faceDirVector.getZ()
);
if (isSameLiquid(bl.getBlockState()) || (faceDir != Direction.UP && bl.isCullingNeighborFaces())) return false;
if (isSameLiquid(bl) || (faceDir != Direction.UP && bl.getProperties().isCulling())) return false;
// initialize the faces
blockModel.initialize();
@ -327,17 +321,6 @@ private boolean createElementFace(Direction faceDir, VectorM3f c0, VectorM3f c1,
return true;
}
private Block getNeighborBlock(int dx, int dy, int dz) {
int i = (dx + 1) * 9 + (dy + 1) * 3 + (dz + 1);
if (i == 13) return block;
return blocksAround[i].set(
block.getWorld(),
block.getX() + dx,
block.getY() + dy,
block.getZ() + dz
);
}
private final VectorM2f flowingVector = new VectorM2f(0, 0);
private int getFlowingAngle() {
float own = getLiquidBaseHeight(blockState) * BLOCK_SCALE;
@ -358,11 +341,11 @@ private int getFlowingAngle() {
}
private float compareLiquidHeights(float ownHeight, int dx, int dz) {
BlockState state = getNeighborBlock(dx, 0, dz).getBlockState();
if (state.isAir) return 0;
if (!isSameLiquid(state)) return 0;
ResourcePackBlock<?> neighbor = block.getNeighborBlock(dx, 0, dz);
if (neighbor.getBlockState().isAir()) return 0;
if (!isSameLiquid(neighbor)) return 0;
float otherHeight = getLiquidBaseHeight(state) * BLOCK_SCALE;
float otherHeight = getLiquidBaseHeight(neighbor.getBlockState()) * BLOCK_SCALE;
return otherHeight - ownHeight;
}

View File

@ -32,13 +32,19 @@
import de.bluecolored.bluemap.core.map.hires.BlockModelView;
import de.bluecolored.bluemap.core.map.hires.HiresTileModel;
import de.bluecolored.bluemap.core.map.hires.RenderSettings;
import de.bluecolored.bluemap.core.resourcepack.*;
import de.bluecolored.bluemap.core.resourcepack.BlockColorCalculatorFactory;
import de.bluecolored.bluemap.core.resourcepack.ResourcePack;
import de.bluecolored.bluemap.core.resourcepack.blockmodel.BlockModelResource;
import de.bluecolored.bluemap.core.resourcepack.blockmodel.TransformedBlockModelResource;
import de.bluecolored.bluemap.core.resourcepack.texture.Texture;
import de.bluecolored.bluemap.core.util.Direction;
import de.bluecolored.bluemap.core.util.math.Color;
import de.bluecolored.bluemap.core.util.math.MatrixM4f;
import de.bluecolored.bluemap.core.util.math.VectorM2f;
import de.bluecolored.bluemap.core.util.math.VectorM3f;
import de.bluecolored.bluemap.core.world.Block;
import de.bluecolored.bluemap.core.world.BlockNeighborhood;
import de.bluecolored.bluemap.core.world.LightData;
import de.bluecolored.bluemap.core.world.ResourcePackBlock;
/**
* This model builder creates a BlockStateModel using the information from parsed resource-pack json files.
@ -54,24 +60,22 @@ public class ResourceModelBuilder {
private final VectorM2f[] uvs = new VectorM2f[4];
private final Color tintColor = new Color();
private final Color mapColor = new Color();
private final Block[] blocksAround;
private Block block;
private BlockNeighborhood<?> block;
private TransformedBlockModelResource blockModelResource;
private BlockModelView blockModel;
private Color blockColor;
public ResourceModelBuilder(ResourcePack resourcePack, RenderSettings renderSettings, Block[] neighborCache) {
public ResourceModelBuilder(ResourcePack resourcePack, RenderSettings renderSettings) {
this.blockColorCalculator = resourcePack.getBlockColorCalculatorFactory().createCalculator();
this.renderSettings = renderSettings;
this.blocksAround = neighborCache;
for (int i = 0; i < corners.length; i++) corners[i] = new VectorM3f(0, 0, 0);
for (int i = 0; i < uvs.length; i++) rawUvs[i] = new VectorM2f(0, 0);
}
private final MatrixM4f modelTransform = new MatrixM4f();
public void build(Block block, TransformedBlockModelResource bmr, BlockModelView blockModel, Color color) {
public void build(BlockNeighborhood<?> block, TransformedBlockModelResource bmr, BlockModelView blockModel, Color color) {
this.block = block;
this.blockModel = blockModel;
this.blockColor = color;
@ -97,6 +101,13 @@ public void build(Block block, TransformedBlockModelResource bmr, BlockModelView
);
}
//random offset
if (block.getProperties().isRandomOffset()){
float dx = (hashToFloat(block.getX(), block.getZ(), 123984) - 0.5f) * 0.75f;
float dz = (hashToFloat(block.getX(), block.getZ(), 345542) - 0.5f) * 0.75f;
blockModel.translate(dx, 0, dz);
}
}
private final MatrixM4f modelElementTransform = new MatrixM4f();
@ -149,14 +160,17 @@ private void createElementFace(BlockModelResource.Element element, Direction fac
// face culling
if (face.getCullface() != null) {
Block b = getRotationRelativeBlock(face.getCullface());
if (b.isCullingNeighborFaces()) return;
ResourcePackBlock<?> b = getRotationRelativeBlock(face.getCullface());
if (b.getProperties().isCulling()) return;
}
// light calculation
Block facedBlockNeighbor = getRotationRelativeBlock(faceDir);
int sunLight = facedBlockNeighbor.getPassedSunLight();
int blockLight = facedBlockNeighbor.getPassedBlockLight();
ResourcePackBlock<?> facedBlockNeighbor = getRotationRelativeBlock(faceDir);
LightData blockLightData = block.getLightData();
LightData facedLightData = facedBlockNeighbor.getLightData();
int sunLight = Math.max(blockLightData.getSkyLight(), facedLightData.getSkyLight());
int blockLight = Math.max(blockLightData.getBlockLight(), facedLightData.getBlockLight());
// filter out faces that are not sun-lighted
if (sunLight == 0f && renderSettings.isExcludeFacesWithoutSunlight()) return;
@ -302,23 +316,11 @@ private void createElementFace(BlockModelResource.Element element, Direction fac
}
}
private Block getNeighborBlock(int dx, int dy, int dz) {
int i = (dx + 1) * 9 + (dy + 1) * 3 + (dz + 1);
if (i == 13) return block;
return blocksAround[i].set(
block.getWorld(),
block.getX() + dx,
block.getY() + dy,
block.getZ() + dz
);
}
private Block getRotationRelativeBlock(Direction direction){
private ResourcePackBlock<?> getRotationRelativeBlock(Direction direction){
return getRotationRelativeBlock(direction.toVector());
}
private Block getRotationRelativeBlock(Vector3i direction){
private ResourcePackBlock<?> getRotationRelativeBlock(Vector3i direction){
return getRotationRelativeBlock(
direction.getX(),
direction.getY(),
@ -327,11 +329,11 @@ private Block getRotationRelativeBlock(Vector3i direction){
}
private final VectorM3f rotationRelativeBlockDirection = new VectorM3f(0, 0, 0);
private Block getRotationRelativeBlock(int dx, int dy, int dz){
private ResourcePackBlock<?> getRotationRelativeBlock(int dx, int dy, int dz){
rotationRelativeBlockDirection.set(dx, dy, dz);
makeRotationRelative(rotationRelativeBlockDirection);
return getNeighborBlock(
return block.getNeighborBlock(
Math.round(rotationRelativeBlockDirection.x),
Math.round(rotationRelativeBlockDirection.y),
Math.round(rotationRelativeBlockDirection.z)
@ -369,23 +371,28 @@ private float testAo(VectorM3f vertex, Direction dir){
if (x * dirVec.getX() + y * dirVec.getY() > 0){
if (getRotationRelativeBlock(x, y, 0).isOccludingNeighborFaces()) occluding++;
if (getRotationRelativeBlock(x, y, 0).getProperties().isOccluding()) occluding++;
}
if (x * dirVec.getX() + z * dirVec.getZ() > 0){
if (getRotationRelativeBlock(x, 0, z).isOccludingNeighborFaces()) occluding++;
if (getRotationRelativeBlock(x, 0, z).getProperties().isOccluding()) occluding++;
}
if (y * dirVec.getY() + z * dirVec.getZ() > 0){
if (getRotationRelativeBlock(0, y, z).isOccludingNeighborFaces()) occluding++;
if (getRotationRelativeBlock(0, y, z).getProperties().isOccluding()) occluding++;
}
if (x * dirVec.getX() + y * dirVec.getY() + z * dirVec.getZ() > 0){
if (getRotationRelativeBlock(x, y, z).isOccludingNeighborFaces()) occluding++;
if (getRotationRelativeBlock(x, y, z).getProperties().isOccluding()) occluding++;
}
if (occluding > 3) occluding = 3;
return Math.max(0f, Math.min(1f - occluding * 0.25f, 1f));
}
private static float hashToFloat(int x, int z, long seed) {
final long hash = x * 73428767 ^ z * 4382893 ^ seed * 457;
return (hash * (hash + 456149) & 0x00ffffff) / (float) 0x01000000;
}
}

View File

@ -1,233 +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.mca;
import com.flowpowered.math.vector.Vector3i;
import de.bluecolored.bluemap.core.mca.mapping.BiomeMapper;
import de.bluecolored.bluemap.core.mca.mapping.BlockIdMapper;
import de.bluecolored.bluemap.core.world.Biome;
import de.bluecolored.bluemap.core.world.BlockState;
import de.bluecolored.bluemap.core.world.LightData;
import net.querz.nbt.CompoundTag;
import net.querz.nbt.ListTag;
import net.querz.nbt.NumberTag;
import java.util.Arrays;
import java.util.function.IntFunction;
public class ChunkAnvil112 extends MCAChunk {
private final BiomeMapper biomeIdMapper;
private final BlockIdMapper blockIdMapper;
private final IntFunction<String> forgeBlockIdMapper;
private boolean isGenerated;
private boolean hasLight;
private Section[] sections;
private byte[] biomes;
@SuppressWarnings("unchecked")
public ChunkAnvil112(CompoundTag chunkTag, boolean ignoreMissingLightData, BiomeMapper biomeIdMapper, BlockIdMapper blockIdMapper, IntFunction<String> forgeBlockIdMapper) {
super(chunkTag);
this.blockIdMapper = blockIdMapper;
this.biomeIdMapper = biomeIdMapper;
this.forgeBlockIdMapper = forgeBlockIdMapper;
CompoundTag levelData = chunkTag.getCompoundTag("Level");
hasLight = levelData.getBoolean("LightPopulated");
isGenerated =
(hasLight || ignoreMissingLightData) &&
levelData.getBoolean("TerrainPopulated");
sections = new Section[32]; //32 supports a max world-height of 512 which is the max that the hightmaps of Minecraft V1.13+ can store with 9 bits, i believe?
if (levelData.containsKey("Sections")) {
for (CompoundTag sectionTag : ((ListTag<CompoundTag>) levelData.getListTag("Sections"))) {
Section section = new Section(sectionTag);
if (section.getSectionY() >= 0 && section.getSectionY() < sections.length) sections[section.getSectionY()] = section;
}
}
biomes = levelData.getByteArray("Biomes");
if (biomes == null || biomes.length == 0) {
biomes = new byte[256];
}
if (biomes.length < 256) {
biomes = Arrays.copyOf(biomes, 256);
}
}
@Override
public boolean isGenerated() {
return isGenerated;
}
@Override
public BlockState getBlockState(int x, int y, int z) {
int sectionY = y >> 4;
if (sectionY < 0 || sectionY >= this.sections.length) return BlockState.AIR;
Section section = this.sections[sectionY];
if (section == null) return BlockState.AIR;
return section.getBlockState(x, y, z);
}
public String getBlockIdMeta(Vector3i pos) {
int sectionY = pos.getY() >> 4;
if (sectionY < 0 || sectionY >= this.sections.length) return "0:0";
Section section = this.sections[sectionY];
if (section == null) return "0:0";
return section.getBlockIdMeta(pos);
}
@Override
public LightData getLightData(int x, int y, int z, LightData target) {
if (!hasLight) return target.set(15, 0);
int sectionY = y >> 4;
if (sectionY < 0 || sectionY >= this.sections.length)
return (y < 0) ? target.set(0, 0) : target.set(15, 0);
Section section = this.sections[sectionY];
if (section == null) return target.set(15, 0);
return section.getLightData(x, y, z, target);
}
@Override
public Biome getBiome(int x, int y, int z) {
x = x & 0xF; // Math.floorMod(pos.getX(), 16)
z = z & 0xF;
int biomeByteIndex = z * 16 + x;
if (biomeByteIndex >= this.biomes.length) return Biome.DEFAULT;
return biomeIdMapper.get(biomes[biomeByteIndex] & 0xFF);
}
private class Section {
private int sectionY;
private byte[] blocks;
private byte[] add;
private byte[] blockLight;
private byte[] skyLight;
private byte[] data;
public Section(CompoundTag sectionData) {
this.sectionY = sectionData.get("Y", NumberTag.class).asInt();
this.blocks = sectionData.getByteArray("Blocks");
this.add = sectionData.getByteArray("Add");
this.blockLight = sectionData.getByteArray("BlockLight");
this.skyLight = sectionData.getByteArray("SkyLight");
this.data = sectionData.getByteArray("Data");
if (blocks.length < 4096) blocks = Arrays.copyOf(blocks, 4096);
if (blockLight.length < 2048) blockLight = Arrays.copyOf(blockLight, 2048);
if (skyLight.length < 2048) skyLight = Arrays.copyOf(skyLight, 2048);
if (data.length < 2048) data = Arrays.copyOf(data, 2048);
}
public int getSectionY() {
return sectionY;
}
public BlockState getBlockState(int x, int y, int z) {
x &= 0xF; y &= 0xF; z &= 0xF; // Math.floorMod(pos.getX(), 16)
int blockByteIndex = y * 256 + z * 16 + x;
int blockHalfByteIndex = blockByteIndex >> 1; // blockByteIndex / 2
boolean largeHalf = (blockByteIndex & 0x1) != 0; // (blockByteIndex % 2) == 0
int blockId = this.blocks[blockByteIndex] & 0xFF;
if (this.add.length > blockHalfByteIndex) {
blockId = blockId | (getByteHalf(this.add[blockHalfByteIndex], largeHalf) << 8);
}
int blockData = getByteHalf(this.data[blockHalfByteIndex], largeHalf);
String forgeIdMapping = forgeBlockIdMapper.apply(blockId);
if (forgeIdMapping != null) {
return blockIdMapper.get(forgeIdMapping, blockId, blockData);
} else {
return blockIdMapper.get(blockId, blockData);
}
}
public String getBlockIdMeta(Vector3i pos) {
int x = pos.getX() & 0xF; // Math.floorMod(pos.getX(), 16)
int y = pos.getY() & 0xF;
int z = pos.getZ() & 0xF;
int blockByteIndex = y * 256 + z * 16 + x;
int blockHalfByteIndex = blockByteIndex >> 1; // blockByteIndex / 2
boolean largeHalf = (blockByteIndex & 0x1) != 0; // (blockByteIndex % 2) == 0
int blockId = this.blocks[blockByteIndex] & 0xFF;
if (this.add.length > blockHalfByteIndex) {
blockId = blockId | (getByteHalf(this.add[blockHalfByteIndex], largeHalf) << 8);
}
int blockData = getByteHalf(this.data[blockHalfByteIndex], largeHalf);
String forgeIdMapping = forgeBlockIdMapper.apply(blockId);
return blockId + ":" + blockData + " " + forgeIdMapping;
}
public LightData getLightData(int x, int y, int z, LightData target) {
x &= 0xF; y &= 0xF; z &= 0xF; // Math.floorMod(pos.getX(), 16)
int blockByteIndex = y * 256 + z * 16 + x;
int blockHalfByteIndex = blockByteIndex >> 1; // blockByteIndex / 2
boolean largeHalf = (blockByteIndex & 0x1) != 0; // (blockByteIndex % 2) == 0
int blockLight = getByteHalf(this.blockLight[blockHalfByteIndex], largeHalf);
int skyLight = getByteHalf(this.skyLight[blockHalfByteIndex], largeHalf);
return target.set(skyLight, blockLight);
}
/**
* Extracts the 4 bits of the left (largeHalf = <code>true</code>) or the right (largeHalf = <code>false</code>) side of the byte stored in <code>value</code>.<br>
* The value is treated as an unsigned byte.
*/
private int getByteHalf(int value, boolean largeHalf) {
value = value & 0xFF;
if (largeHalf) {
value = value >> 4;
}
value = value & 0xF;
return value;
}
}
}

View File

@ -24,9 +24,7 @@
*/
package de.bluecolored.bluemap.core.mca;
import de.bluecolored.bluemap.core.MinecraftVersion;
import de.bluecolored.bluemap.core.logger.Logger;
import de.bluecolored.bluemap.core.mca.mapping.BiomeMapper;
import de.bluecolored.bluemap.core.world.Biome;
import de.bluecolored.bluemap.core.world.BlockState;
import de.bluecolored.bluemap.core.world.LightData;
@ -38,21 +36,15 @@
import java.util.Map.Entry;
public class ChunkAnvil113 extends MCAChunk {
private static final MinecraftVersion VERSION = new MinecraftVersion(1, 13);
private BiomeMapper biomeIdMapper;
private boolean isGenerated;
private boolean hasLight;
private Section[] sections;
private int[] biomes;
@SuppressWarnings("unchecked")
public ChunkAnvil113(CompoundTag chunkTag, boolean ignoreMissingLightData, BiomeMapper biomeIdMapper) {
public ChunkAnvil113(CompoundTag chunkTag, boolean ignoreMissingLightData) {
super(chunkTag);
this.biomeIdMapper = biomeIdMapper;
CompoundTag levelData = chunkTag.getCompoundTag("Level");
String status = levelData.getString("Status");
@ -124,14 +116,14 @@ public LightData getLightData(int x, int y, int z, LightData target) {
}
@Override
public Biome getBiome(int x, int y, int z) {
public int getBiome(int x, int y, int z) {
x = x & 0xF; // Math.floorMod(pos.getX(), 16)
z = z & 0xF;
int biomeIntIndex = z * 16 + x;
if (biomeIntIndex >= this.biomes.length) return Biome.DEFAULT;
if (biomeIntIndex >= this.biomes.length) return Biome.DEFAULT.getNumeralId();
return biomeIdMapper.get(biomes[biomeIntIndex]);
return biomes[biomeIntIndex];
}
private class Section {
@ -178,7 +170,7 @@ public Section(CompoundTag sectionData) {
}
}
palette[i] = new BlockState(VERSION, id, properties);
palette[i] = new BlockState(id, properties);
}
} else {
this.palette = new BlockState[0];

View File

@ -24,9 +24,7 @@
*/
package de.bluecolored.bluemap.core.mca;
import de.bluecolored.bluemap.core.MinecraftVersion;
import de.bluecolored.bluemap.core.logger.Logger;
import de.bluecolored.bluemap.core.mca.mapping.BiomeMapper;
import de.bluecolored.bluemap.core.world.Biome;
import de.bluecolored.bluemap.core.world.BlockState;
import de.bluecolored.bluemap.core.world.LightData;
@ -38,21 +36,15 @@
import java.util.Map.Entry;
public class ChunkAnvil115 extends MCAChunk {
private static final MinecraftVersion VERSION = new MinecraftVersion(1, 15);
private BiomeMapper biomeIdMapper;
private boolean isGenerated;
private boolean hasLight;
private Section[] sections;
private int[] biomes;
@SuppressWarnings("unchecked")
public ChunkAnvil115(CompoundTag chunkTag, boolean ignoreMissingLightData, BiomeMapper biomeIdMapper) {
public ChunkAnvil115(CompoundTag chunkTag, boolean ignoreMissingLightData) {
super(chunkTag);
this.biomeIdMapper = biomeIdMapper;
CompoundTag levelData = chunkTag.getCompoundTag("Level");
String status = levelData.getString("Status");
@ -124,16 +116,16 @@ public LightData getLightData(int x, int y, int z, LightData target) {
}
@Override
public Biome getBiome(int x, int y, int z) {
public int getBiome(int x, int y, int z) {
x = (x & 0xF) / 4; // Math.floorMod(pos.getX(), 16)
z = (z & 0xF) / 4;
y = y / 4;
int biomeIntIndex = y * 16 + z * 4 + x;
if (biomeIntIndex < 0) return Biome.DEFAULT;
if (biomeIntIndex >= this.biomes.length) return Biome.DEFAULT;
if (biomeIntIndex < 0) return Biome.DEFAULT.getNumeralId();
if (biomeIntIndex >= this.biomes.length) return Biome.DEFAULT.getNumeralId();
return biomeIdMapper.get(biomes[biomeIntIndex]);
return biomes[biomeIntIndex];
}
private static class Section {
@ -180,7 +172,7 @@ public Section(CompoundTag sectionData) {
}
}
palette[i] = new BlockState(VERSION, id, properties);
palette[i] = new BlockState(id, properties);
}
} else {
this.palette = new BlockState[0];

View File

@ -24,9 +24,7 @@
*/
package de.bluecolored.bluemap.core.mca;
import de.bluecolored.bluemap.core.MinecraftVersion;
import de.bluecolored.bluemap.core.logger.Logger;
import de.bluecolored.bluemap.core.mca.mapping.BiomeMapper;
import de.bluecolored.bluemap.core.world.Biome;
import de.bluecolored.bluemap.core.world.BlockState;
import de.bluecolored.bluemap.core.world.LightData;
@ -39,10 +37,6 @@
import java.util.Map.Entry;
public class ChunkAnvil116 extends MCAChunk {
private static final MinecraftVersion VERSION = new MinecraftVersion(1, 16);
private BiomeMapper biomeIdMapper;
private boolean isGenerated;
private boolean hasLight;
@ -52,11 +46,9 @@ public class ChunkAnvil116 extends MCAChunk {
private int[] biomes;
@SuppressWarnings("unchecked")
public ChunkAnvil116(CompoundTag chunkTag, boolean ignoreMissingLightData, BiomeMapper biomeIdMapper) {
public ChunkAnvil116(CompoundTag chunkTag, boolean ignoreMissingLightData) {
super(chunkTag);
this.biomeIdMapper = biomeIdMapper;
CompoundTag levelData = chunkTag.getCompoundTag("Level");
String status = levelData.getString("Status");
@ -138,8 +130,8 @@ public LightData getLightData(int x, int y, int z, LightData target) {
}
@Override
public Biome getBiome(int x, int y, int z) {
if (biomes.length < 16) return Biome.DEFAULT;
public int getBiome(int x, int y, int z) {
if (biomes.length < 16) return Biome.DEFAULT.getNumeralId();
x = (x & 0xF) / 4; // Math.floorMod(pos.getX(), 16)
z = (z & 0xF) / 4;
@ -150,7 +142,7 @@ public Biome getBiome(int x, int y, int z) {
if (biomeIntIndex >= biomes.length) biomeIntIndex -= (((biomeIntIndex - biomes.length) >> 4) + 1) * 16;
if (biomeIntIndex < 0) biomeIntIndex -= (biomeIntIndex >> 4) * 16;
return biomeIdMapper.get(biomes[biomeIntIndex]);
return biomes[biomeIntIndex];
}
@Override
@ -213,7 +205,7 @@ public Section(CompoundTag sectionData) {
}
}
palette[i] = new BlockState(VERSION, id, properties);
palette[i] = new BlockState(id, properties);
}
} else {
this.palette = new BlockState[0];

View File

@ -48,8 +48,8 @@ public LightData getLightData(int x, int y, int z, LightData target) {
}
@Override
public Biome getBiome(int x, int y, int z) {
return Biome.DEFAULT;
public int getBiome(int x, int y, int z) {
return Biome.DEFAULT.getNumeralId();
}
}

View File

@ -24,7 +24,6 @@
*/
package de.bluecolored.bluemap.core.mca;
import de.bluecolored.bluemap.core.world.Biome;
import de.bluecolored.bluemap.core.world.BlockState;
import de.bluecolored.bluemap.core.world.Chunk;
import de.bluecolored.bluemap.core.world.LightData;
@ -59,7 +58,7 @@ public int getDataVersion() {
public abstract LightData getLightData(int x, int y, int z, LightData target);
@Override
public abstract Biome getBiome(int x, int y, int z);
public abstract int getBiome(int x, int y, int z);
@Override
public int getMaxY(int x, int z) {
@ -74,10 +73,9 @@ public int getMinY(int x, int z) {
public static MCAChunk create(MCAWorld world, CompoundTag chunkTag, boolean ignoreMissingLightData) throws IOException {
int version = chunkTag.getInt("DataVersion");
if (version < 1400) return new ChunkAnvil112(chunkTag, ignoreMissingLightData, world.getBiomeIdMapper(), world.getBlockIdMapper(), world::getForgeBlockIdMapping);
if (version < 2200) return new ChunkAnvil113(chunkTag, ignoreMissingLightData, world.getBiomeIdMapper());
if (version < 2500) return new ChunkAnvil115(chunkTag, ignoreMissingLightData, world.getBiomeIdMapper());
return new ChunkAnvil116(chunkTag, ignoreMissingLightData, world.getBiomeIdMapper());
if (version < 2200) return new ChunkAnvil113(chunkTag, ignoreMissingLightData);
if (version < 2500) return new ChunkAnvil115(chunkTag, ignoreMissingLightData);
return new ChunkAnvil116(chunkTag, ignoreMissingLightData);
}
public static MCAChunk empty() {

View File

@ -29,20 +29,13 @@
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import de.bluecolored.bluemap.core.BlueMap;
import de.bluecolored.bluemap.core.MinecraftVersion;
import de.bluecolored.bluemap.core.debug.DebugDump;
import de.bluecolored.bluemap.core.logger.Logger;
import de.bluecolored.bluemap.core.mca.extensions.*;
import de.bluecolored.bluemap.core.mca.mapping.BiomeMapper;
import de.bluecolored.bluemap.core.mca.mapping.BlockIdMapper;
import de.bluecolored.bluemap.core.mca.mapping.BlockPropertiesMapper;
import de.bluecolored.bluemap.core.util.ArrayPool;
import de.bluecolored.bluemap.core.util.math.VectorM2i;
import de.bluecolored.bluemap.core.world.*;
import de.bluecolored.bluemap.core.world.BlockState;
import de.bluecolored.bluemap.core.world.Grid;
import de.bluecolored.bluemap.core.world.World;
import net.querz.nbt.CompoundTag;
import net.querz.nbt.ListTag;
import net.querz.nbt.NBTUtil;
import net.querz.nbt.Tag;
import java.io.File;
import java.io.FileNotFoundException;
@ -58,62 +51,28 @@ public class MCAWorld implements World {
@DebugDump private final UUID uuid;
@DebugDump private final Path worldFolder;
private final MinecraftVersion minecraftVersion;
@DebugDump private final String name;
@DebugDump private final Vector3i spawnPoint;
@DebugDump private final boolean ignoreMissingLightData;
private final LoadingCache<Vector2i, MCARegion> regionCache;
private final LoadingCache<Vector2i, MCAChunk> chunkCache;
private BlockIdMapper blockIdMapper;
private BlockPropertiesMapper blockPropertiesMapper;
private BiomeMapper biomeMapper;
private final Map<String, List<BlockStateExtension>> blockStateExtensions;
@DebugDump private final boolean ignoreMissingLightData;
private final Map<Integer, String> forgeBlockMappings;
private MCAWorld(
Path worldFolder,
UUID uuid,
MinecraftVersion minecraftVersion,
String name,
Vector3i spawnPoint,
BlockIdMapper blockIdMapper,
BlockPropertiesMapper blockPropertiesMapper,
BiomeMapper biomeMapper,
boolean ignoreMissingLightData
) {
this.uuid = uuid;
this.worldFolder = worldFolder;
this.minecraftVersion = minecraftVersion;
this.name = name;
this.spawnPoint = spawnPoint;
this.blockIdMapper = blockIdMapper;
this.blockPropertiesMapper = blockPropertiesMapper;
this.biomeMapper = biomeMapper;
this.ignoreMissingLightData = ignoreMissingLightData;
this.forgeBlockMappings = new HashMap<>();
this.blockStateExtensions = new HashMap<>();
registerBlockStateExtension(new SnowyExtension(minecraftVersion));
registerBlockStateExtension(new StairShapeExtension());
registerBlockStateExtension(new FireExtension());
registerBlockStateExtension(new RedstoneExtension());
registerBlockStateExtension(new DoorExtension(minecraftVersion));
registerBlockStateExtension(new NetherFenceConnectExtension());
registerBlockStateExtension(new TripwireConnectExtension());
registerBlockStateExtension(new WallConnectExtension());
registerBlockStateExtension(new WoodenFenceConnectExtension(minecraftVersion));
registerBlockStateExtension(new GlassPaneConnectExtension());
registerBlockStateExtension(new DoublePlantExtension(minecraftVersion));
registerBlockStateExtension(new DoubleChestExtension());
this.regionCache = Caffeine.newBuilder()
.executor(BlueMap.THREAD_POOL)
.maximumSize(100)
@ -131,34 +90,6 @@ public BlockState getBlockState(Vector3i pos) {
return getChunk(pos.getX() >> 4, pos.getZ() >> 4).getBlockState(pos.getX(), pos.getY(), pos.getZ());
}
@Override
public Biome getBiome(int x, int y, int z) {
return getChunk(x >> 4, z >> 4).getBiome(x, y, z);
}
@Override
public BlockState getBlockState(int x, int y, int z) {
MCAChunk chunk = getChunk(x >> 4, z >> 4);
BlockState blockState = chunk.getBlockState(x, y, z);
if (chunk instanceof ChunkAnvil112) { // only use extensions if old format chunk (1.12) in the new format block-states are saved with extensions
List<BlockStateExtension> applicableExtensions = blockStateExtensions.getOrDefault(blockState.getFullId(), Collections.emptyList());
if (!applicableExtensions.isEmpty()) {
Vector3i pos = new Vector3i(x, y, z);
for (BlockStateExtension ext : applicableExtensions) {
blockState = ext.extend(this, pos, blockState);
}
}
}
return blockState;
}
@Override
public BlockProperties getBlockProperties(BlockState blockState) {
return blockPropertiesMapper.get(blockState);
}
@Override
public MCAChunk getChunkAtBlock(int x, int y, int z) {
return getChunk(new Vector2i(x >> 4, z >> 4));
@ -166,19 +97,19 @@ public MCAChunk getChunkAtBlock(int x, int y, int z) {
@Override
public MCAChunk getChunk(int x, int z) {
return getChunk(new Vector2i(x, z));
return getChunk(vec2i(x, z));
}
public MCAChunk getChunk(Vector2i pos) {
private MCAChunk getChunk(Vector2i pos) {
return chunkCache.get(pos);
}
@Override
public MCARegion getRegion(int x, int z) {
return getRegion(new Vector2i(x, z));
return getRegion(vec2i(x, z));
}
public MCARegion getRegion(Vector2i pos) {
private MCARegion getRegion(Vector2i pos) {
return regionCache.get(pos);
}
@ -265,42 +196,10 @@ public void cleanUpChunkCache() {
chunkCache.cleanUp();
}
public BlockIdMapper getBlockIdMapper() {
return blockIdMapper;
}
public BlockPropertiesMapper getBlockPropertiesMapper() {
return blockPropertiesMapper;
}
public BiomeMapper getBiomeIdMapper() {
return biomeMapper;
}
public void setBlockIdMapper(BlockIdMapper blockIdMapper) {
this.blockIdMapper = blockIdMapper;
}
public void setBlockPropertiesMapper(BlockPropertiesMapper blockPropertiesMapper) {
this.blockPropertiesMapper = blockPropertiesMapper;
}
public void setBiomeMapper(BiomeMapper biomeMapper) {
this.biomeMapper = biomeMapper;
}
public Path getWorldFolder() {
return worldFolder;
}
public String getForgeBlockIdMapping(int id) {
return forgeBlockMappings.get(id);
}
public MinecraftVersion getMinecraftVersion() {
return minecraftVersion;
}
private Path getRegionFolder() {
return worldFolder.resolve("region");
}
@ -309,12 +208,6 @@ private File getMCAFile(int regionX, int regionZ) {
return getRegionFolder().resolve("r." + regionX + "." + regionZ + ".mca").toFile();
}
private void registerBlockStateExtension(BlockStateExtension extension) {
for (String id : extension.getAffectedBlockIds()) {
this.blockStateExtensions.computeIfAbsent(id, t -> new ArrayList<>()).add(extension);
}
}
private MCARegion loadRegion(Vector2i regionPos) {
return loadRegion(regionPos.getX(), regionPos.getY());
}
@ -356,11 +249,11 @@ private MCAChunk loadChunk(int x, int z) {
return MCAChunk.empty();
}
public static MCAWorld load(Path worldFolder, UUID uuid, MinecraftVersion version, BlockIdMapper blockIdMapper, BlockPropertiesMapper blockPropertiesMapper, BiomeMapper biomeIdMapper) throws IOException {
return load(worldFolder, uuid, version, blockIdMapper, blockPropertiesMapper, biomeIdMapper, null, false);
public static MCAWorld load(Path worldFolder, UUID uuid) throws IOException {
return load(worldFolder, uuid, null, false);
}
public static MCAWorld load(Path worldFolder, UUID uuid, MinecraftVersion version, BlockIdMapper blockIdMapper, BlockPropertiesMapper blockPropertiesMapper, BiomeMapper biomeIdMapper, String name, boolean ignoreMissingLightData) throws IOException {
public static MCAWorld load(Path worldFolder, UUID uuid, String name, boolean ignoreMissingLightData) throws IOException {
try {
StringBuilder subDimensionName = new StringBuilder();
@ -394,35 +287,13 @@ public static MCAWorld load(Path worldFolder, UUID uuid, MinecraftVersion versio
levelData.getInt("SpawnZ")
);
MCAWorld world = new MCAWorld(
return new MCAWorld(
worldFolder,
uuid,
version,
name,
spawnPoint,
blockIdMapper,
blockPropertiesMapper,
biomeIdMapper,
ignoreMissingLightData
);
try {
CompoundTag fmlTag = level.getCompoundTag("FML");
if (fmlTag == null) fmlTag = level.getCompoundTag("fml");
ListTag<? extends Tag<?>> blockIdReg = fmlTag.getCompoundTag("Registries").getCompoundTag("minecraft:blocks").getListTag("ids");
for (Tag<?> tag : blockIdReg) {
if (tag instanceof CompoundTag) {
CompoundTag entry = (CompoundTag) tag;
String blockId = entry.getString("K");
int numeralId = entry.getInt("V");
world.forgeBlockMappings.put(numeralId, blockId);
}
}
} catch (NullPointerException ignore) {}
return world;
} catch (ClassCastException | NullPointerException ex) {
throw new IOException("Invaid level.dat format!", ex);
}
@ -437,4 +308,17 @@ public String toString() {
'}';
}
private static final int VEC_2I_CACHE_SIZE = 0x4000;
private static final int VEC_2I_CACHE_MASK = VEC_2I_CACHE_SIZE - 1;
private static final Vector2i[] VEC_2I_CACHE = new Vector2i[VEC_2I_CACHE_SIZE];
private static Vector2i vec2i(int x, int y) {
int cacheIndex = (x * 1456 ^ y * 948375892) & VEC_2I_CACHE_MASK;
Vector2i possibleMatch = VEC_2I_CACHE[cacheIndex];
if (possibleMatch != null && possibleMatch.getX() == x && possibleMatch.getY() == y)
return possibleMatch;
return VEC_2I_CACHE[cacheIndex] = new Vector2i(x, y);
}
}

View File

@ -1,57 +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.mca.extensions;
import java.util.Set;
import com.flowpowered.math.vector.Vector3i;
import de.bluecolored.bluemap.core.mca.MCAWorld;
import de.bluecolored.bluemap.core.util.Direction;
import de.bluecolored.bluemap.core.world.BlockState;
public abstract class ConnectExtension implements BlockStateExtension {
@Override
public BlockState extend(MCAWorld world, Vector3i pos, BlockState state) {
return state
.with("north", String.valueOf(connectsTo(world, pos.add(Direction.NORTH.toVector()))))
.with("east", String.valueOf(connectsTo(world, pos.add(Direction.EAST.toVector()))))
.with("south", String.valueOf(connectsTo(world, pos.add(Direction.SOUTH.toVector()))))
.with("west", String.valueOf(connectsTo(world, pos.add(Direction.WEST.toVector()))));
}
public boolean connectsTo(MCAWorld world, Vector3i pos) {
return connectsTo(world, pos, world.getBlockState(pos));
}
public boolean connectsTo(MCAWorld world, Vector3i pos, BlockState block) {
return getAffectedBlockIds().contains(block.getFullId());
}
@Override
public abstract Set<String> getAffectedBlockIds();
}

View File

@ -1,41 +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.mca.extensions;
import com.flowpowered.math.vector.Vector3i;
import de.bluecolored.bluemap.core.mca.MCAWorld;
import de.bluecolored.bluemap.core.world.BlockState;
public abstract class ConnectSameOrFullBlockExtension extends ConnectExtension {
@Override
public boolean connectsTo(MCAWorld world, Vector3i pos, BlockState block) {
if (super.connectsTo(world, pos, block)) return true;
return world.getBlockPropertiesMapper().get(block).isCulling();
}
}

View File

@ -1,99 +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.mca.extensions;
import com.flowpowered.math.vector.Vector3i;
import de.bluecolored.bluemap.core.MinecraftVersion;
import de.bluecolored.bluemap.core.mca.MCAWorld;
import de.bluecolored.bluemap.core.util.Direction;
import de.bluecolored.bluemap.core.world.BlockState;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
public class DoorExtension implements BlockStateExtension {
private final Set<String> affectedBlockIds;
public DoorExtension(MinecraftVersion version) {
if (version.isBefore(MinecraftVersion.THE_FLATTENING)) {
affectedBlockIds = new HashSet<>(Arrays.asList(
"minecraft:wooden_door",
"minecraft:iron_door",
"minecraft:spruce_door",
"minecraft:birch_door",
"minecraft:jungle_door",
"minecraft:acacia_door",
"minecraft:dark_oak_door"
));
} else {
affectedBlockIds = new HashSet<>(Arrays.asList(
"minecraft:oak_door",
"minecraft:iron_door",
"minecraft:spruce_door",
"minecraft:birch_door",
"minecraft:jungle_door",
"minecraft:acacia_door",
"minecraft:dark_oak_door"
));
}
}
@Override
public BlockState extend(MCAWorld world, Vector3i pos, BlockState state) {
BlockState otherDoor;
boolean isLower = Objects.equals(state.getProperties().get("half"), "lower");
if (isLower) {
otherDoor = world.getBlockState(pos.add(Direction.UP.toVector()));
} else {
otherDoor = world.getBlockState(pos.add(Direction.DOWN.toVector()));
}
//copy all properties from the other door
for (Entry<String, String> prop : otherDoor.getProperties().entrySet()) {
if (
!state.getProperties().containsKey(prop.getKey()) ||
(isLower && prop.getKey().equals("hinge")) ||
(isLower && prop.getKey().equals("powered")) ||
(!isLower && prop.getKey().equals("open")) ||
(!isLower && prop.getKey().equals("facing"))
) {
state = state.with(prop.getKey(), prop.getValue());
}
}
return state;
}
@Override
public Set<String> getAffectedBlockIds() {
return affectedBlockIds;
}
}

View File

@ -1,61 +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.mca.extensions;
import com.flowpowered.math.vector.Vector3i;
import de.bluecolored.bluemap.core.mca.MCAWorld;
import de.bluecolored.bluemap.core.util.Direction;
import de.bluecolored.bluemap.core.world.BlockState;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
public class DoubleChestExtension implements BlockStateExtension {
private static final Set<String> AFFECTED_BLOCK_IDS = new HashSet<>(Arrays.asList(
"minecraft:chest",
"minecraft:trapped_chest"
));
@Override
public BlockState extend(MCAWorld world, Vector3i pos, BlockState state) {
Direction dir = Direction.fromString(state.getProperties().getOrDefault("facing", "north"));
BlockState left = world.getBlockState(pos.add(dir.left().toVector()));
if (left.getFullId().equals(state.getFullId())) return state.with("type", "right");
BlockState right = world.getBlockState(pos.add(dir.right().toVector()));
if (right.getFullId().equals(state.getFullId())) return state.with("type", "left");
return state.with("type", "single");
}
@Override
public Set<String> getAffectedBlockIds() {
return AFFECTED_BLOCK_IDS;
}
}

View File

@ -1,71 +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.mca.extensions;
import com.flowpowered.math.vector.Vector3i;
import de.bluecolored.bluemap.core.MinecraftVersion;
import de.bluecolored.bluemap.core.mca.MCAWorld;
import de.bluecolored.bluemap.core.util.Direction;
import de.bluecolored.bluemap.core.world.BlockState;
import java.util.*;
public class DoublePlantExtension implements BlockStateExtension {
private final Set<String> affectedBlockIds;
public DoublePlantExtension(MinecraftVersion version) {
if (version.isBefore(MinecraftVersion.THE_FLATTENING)) {
affectedBlockIds = new HashSet<>(Collections.singletonList(
"minecraft:double_plant"
));
} else {
affectedBlockIds = new HashSet<>(Arrays.asList(
"minecraft:sunflower",
"minecraft:lilac",
"minecraft:tall_grass",
"minecraft:large_fern",
"minecraft:rose_bush",
"minecraft:peony"
));
}
}
@Override
public BlockState extend(MCAWorld world, Vector3i pos, BlockState state) {
if (Objects.equals(state.getProperties().get("half"), "upper")) {
BlockState otherPlant = world.getBlockState(pos.add(Direction.DOWN.toVector()));
return otherPlant.with("half", "upper");
}
return state;
}
@Override
public Set<String> getAffectedBlockIds() {
return affectedBlockIds;
}
}

View File

@ -1,67 +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.mca.extensions;
import com.flowpowered.math.vector.Vector3i;
import de.bluecolored.bluemap.core.mca.MCAWorld;
import de.bluecolored.bluemap.core.util.Direction;
import de.bluecolored.bluemap.core.world.BlockState;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
public class FireExtension implements BlockStateExtension {
private static final Set<String> AFFECTED_BLOCK_IDS = new HashSet<>(Collections.singletonList(
"minecraft:fire"
));
@Override
public BlockState extend(MCAWorld world, Vector3i pos, BlockState state) {
BlockState below = world.getBlockState(pos.add(0, -1, 0));
boolean isOnGround = world.getBlockPropertiesMapper().get(below).isCulling();
for (Direction dir : Direction.values()) {
if (dir != Direction.DOWN) {
if (!isOnGround) {
BlockState neighbor = world.getBlockState(pos.add(dir.toVector()));
state = state.with(dir.name().toLowerCase(), String.valueOf(!world.getBlockPropertiesMapper().get(neighbor).isCulling()));
} else {
state = state.with(dir.name().toLowerCase(), "false");
}
}
}
return state;
}
@Override
public Set<String> getAffectedBlockIds() {
return AFFECTED_BLOCK_IDS;
}
}

View File

@ -1,58 +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.mca.extensions;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
public class GlassPaneConnectExtension extends ConnectSameOrFullBlockExtension {
private static final HashSet<String> AFFECTED_BLOCK_IDS = new HashSet<>(Arrays.asList(
"minecraft:glass_pane",
"minecraft:white_stained_glass_pane",
"minecraft:orange_stained_glass_pane",
"minecraft:magenta_stained_glass_pane",
"minecraft:light_blue_white_stained_glass_pane",
"minecraft:yellow_stained_glass_pane",
"minecraft:lime_stained_glass_pane",
"minecraft:pink_stained_glass_pane",
"minecraft:gray_stained_glass_pane",
"minecraft:light_gray_stained_glass_pane",
"minecraft:cyan_stained_glass_pane",
"minecraft:purple_stained_glass_pane",
"minecraft:blue_stained_glass_pane",
"minecraft:green_stained_glass_pane",
"minecraft:red_stained_glass_pane",
"minecraft:black_stained_glass_pane",
"minecraft:iron_bars"
));
@Override
public Set<String> getAffectedBlockIds() {
return AFFECTED_BLOCK_IDS;
}
}

View File

@ -1,43 +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.mca.extensions;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
public class NetherFenceConnectExtension extends ConnectSameOrFullBlockExtension {
private static final HashSet<String> AFFECTED_BLOCK_IDS = new HashSet<>(Collections.singletonList(
"minecraft:nether_brick_fence"
));
@Override
public Set<String> getAffectedBlockIds() {
return AFFECTED_BLOCK_IDS;
}
}

View File

@ -1,96 +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.mca.extensions;
import com.flowpowered.math.vector.Vector3i;
import de.bluecolored.bluemap.core.mca.MCAWorld;
import de.bluecolored.bluemap.core.util.Direction;
import de.bluecolored.bluemap.core.world.BlockState;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
public class RedstoneExtension implements BlockStateExtension {
private static final Set<String> AFFECTED_BLOCK_IDS = new HashSet<>(Collections.singletonList(
"minecraft:redstone_wire"
));
private static final Set<String> CONNECTIBLE = new HashSet<>(Arrays.asList(
"minecraft:redstone_wire",
"minecraft:redstone_wall_torch",
"minecraft:redstone_torch",
"minecraft:stone_button",
"minecraft:oak_button",
"minecraft:stone_button",
"minecraft:lever",
"minecraft:stone_pressure_plate",
"minecraft:oak_pressure_plate",
"minecraft:light_weighted_pressure_plate",
"minecraft:heavy_weighted_pressure_plate"
));
@Override
public BlockState extend(MCAWorld world, Vector3i pos, BlockState state) {
BlockState up = world.getBlockState(pos.add(0, 1, 0));
boolean upBlocking = !up.equals(BlockState.AIR);
state = state
.with("north", connection(world, pos, upBlocking, Direction.NORTH))
.with("east", connection(world, pos, upBlocking, Direction.EAST))
.with("south", connection(world, pos, upBlocking, Direction.SOUTH))
.with("west", connection(world, pos, upBlocking, Direction.WEST));
return state;
}
private String connection(MCAWorld world, Vector3i pos, boolean upBlocking, Direction direction) {
Vector3i directionVector = direction.toVector();
BlockState next = world.getBlockState(pos.add(directionVector));
if (CONNECTIBLE.contains(next.getFullId())) return "side";
if (next.equals(BlockState.AIR)) {
BlockState nextdown = world.getBlockState(pos.add(directionVector.getX(), directionVector.getY() - 1, directionVector.getZ()));
if (nextdown.getFullId().equals("minecraft:redstone_wire")) return "side";
}
if (!upBlocking) {
BlockState nextup = world.getBlockState(pos.add(directionVector.getX(), directionVector.getY() + 1, directionVector.getZ()));
if (nextup.getFullId().equals("minecraft:redstone_wire")) return "up";
}
return "none";
}
@Override
public Set<String> getAffectedBlockIds() {
return AFFECTED_BLOCK_IDS;
}
}

View File

@ -1,77 +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.mca.extensions;
import com.flowpowered.math.vector.Vector3i;
import de.bluecolored.bluemap.core.MinecraftVersion;
import de.bluecolored.bluemap.core.mca.MCAWorld;
import de.bluecolored.bluemap.core.world.BlockState;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
public class SnowyExtension implements BlockStateExtension {
private final Set<String> affectedBlockIds;
private final String snowLayerId;
private final String snowBlockId;
public SnowyExtension(MinecraftVersion version) {
if (version.isBefore(MinecraftVersion.THE_FLATTENING)) {
affectedBlockIds = new HashSet<>(Arrays.asList(
"minecraft:grass",
"minecraft:mycelium"
));
snowLayerId = "minecraft:snow_layer";
snowBlockId = "minecraft:snow";
} else {
affectedBlockIds = new HashSet<>(Arrays.asList(
"minecraft:grass_block",
"minecraft:podzol"
));
snowLayerId = "minecraft:snow";
snowBlockId = "minecraft:snow_block";
}
}
@Override
public BlockState extend(MCAWorld world, Vector3i pos, BlockState state) {
BlockState above = world.getBlockState(pos.add(0, 1, 0));
if (above.getFullId().equals(snowLayerId) || above.getFullId().equals(snowBlockId)) {
return state.with("snowy", "true");
} else {
return state.with("snowy", "false");
}
}
@Override
public Set<String> getAffectedBlockIds() {
return affectedBlockIds;
}
}

View File

@ -1,118 +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.mca.extensions;
import com.flowpowered.math.vector.Vector3i;
import de.bluecolored.bluemap.core.mca.MCAWorld;
import de.bluecolored.bluemap.core.util.Direction;
import de.bluecolored.bluemap.core.world.BlockState;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
public class StairShapeExtension implements BlockStateExtension {
private static final Set<String> AFFECTED_BLOCK_IDS = new HashSet<>(Arrays.asList(
"minecraft:oak_stairs",
"minecraft:cobblestone_stairs",
"minecraft:brick_stairs",
"minecraft:stone_brick_stairs",
"minecraft:nether_brick_stairs",
"minecraft:sandstone_stairs",
"minecraft:spruce_stairs",
"minecraft:birch_stairs",
"minecraft:jungle_stairs",
"minecraft:quartz_stairs",
"minecraft:acacia_stairs",
"minecraft:dark_oak_stairs",
"minecraft:red_sandstone_stairs",
"minecraft:purpur_stairs"
));
@Override
public BlockState extend(MCAWorld world, Vector3i pos, BlockState state) {
try {
Direction facing = Direction.fromString(state.getProperties().get("facing"));
BlockState back = world.getBlockState(pos.add(facing.toVector()));
if (isStairs(back) && state.getProperties().get("half").equals(back.getProperties().get("half"))) {
Direction backFacing = Direction.fromString(back.getProperties().get("facing"));
if (facing.getAxis() != backFacing.getAxis()){
BlockState next = world.getBlockState(pos.add(backFacing.opposite().toVector()));
if (!isStairs(next) || !isEqualStairs(state, next)) {
if (backFacing == facing.left()){
return state.with("shape", "outer_left");
}
return state.with("shape", "outer_right");
}
}
}
BlockState front = world.getBlockState(pos.add(facing.opposite().toVector()));
if (isStairs(front) && state.getProperties().get("half").equals(front.getProperties().get("half"))) {
Direction frontFacing = Direction.fromString(front.getProperties().get("facing"));
if (facing.getAxis() != frontFacing.getAxis()){
BlockState next = world.getBlockState(pos.add(frontFacing.toVector()));
if (!isStairs(next) || !isEqualStairs(state, next)) {
if (frontFacing == facing.left()){
return state.with("shape", "inner_left");
}
return state.with("shape", "inner_right");
}
}
}
return state.with("shape", "straight");
} catch (IllegalArgumentException | NullPointerException ex) {
return state.with("shape", "straight");
}
}
private boolean isStairs(BlockState state) {
return AFFECTED_BLOCK_IDS.contains(state.getFullId());
}
private boolean isEqualStairs(BlockState stair1, BlockState stair2) {
return
stair1.getProperties().get("facing").equals(stair2.getProperties().get("facing")) &&
stair1.getProperties().get("half").equals(stair2.getProperties().get("half"));
}
@Override
public Set<String> getAffectedBlockIds() {
return AFFECTED_BLOCK_IDS;
}
}

View File

@ -1,65 +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.mca.extensions;
import com.flowpowered.math.vector.Vector3i;
import de.bluecolored.bluemap.core.mca.MCAWorld;
import de.bluecolored.bluemap.core.util.Direction;
import de.bluecolored.bluemap.core.world.BlockState;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
public class WallConnectExtension extends ConnectSameOrFullBlockExtension {
private static final HashSet<String> AFFECTED_BLOCK_IDS = new HashSet<>(Arrays.asList(
"minecraft:cobblestone_wall",
"minecraft:mossy_cobblestone_wall"
));
@Override
public BlockState extend(MCAWorld world, Vector3i pos, BlockState state) {
state = super.extend(world, pos, state);
if (
Objects.equals(state.getProperties().get("north"), state.getProperties().get("south")) &&
Objects.equals(state.getProperties().get("east"), state.getProperties().get("west")) &&
!Objects.equals(state.getProperties().get("north"), state.getProperties().get("east")) &&
!connectsTo(world, pos.add(Direction.UP.toVector()))
) {
return state.with("up", "false");
} else {
return state.with("up", "true");
}
}
@Override
public Set<String> getAffectedBlockIds() {
return AFFECTED_BLOCK_IDS;
}
}

View File

@ -1,64 +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.mca.extensions;
import de.bluecolored.bluemap.core.MinecraftVersion;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
public class WoodenFenceConnectExtension extends ConnectSameOrFullBlockExtension {
private final Set<String> affectedBlockIds;
public WoodenFenceConnectExtension(MinecraftVersion version) {
if (version.isBefore(MinecraftVersion.THE_FLATTENING)) {
affectedBlockIds = new HashSet<>(Arrays.asList(
"minecraft:fence",
"minecraft:spruce_fence",
"minecraft:birch_fence",
"minecraft:jungle_fence",
"minecraft:dark_oak_fence",
"minecraft:acacia_fence"
));
} else {
affectedBlockIds = new HashSet<>(Arrays.asList(
"minecraft:oak_fence",
"minecraft:spruce_fence",
"minecraft:birch_fence",
"minecraft:jungle_fence",
"minecraft:dark_oak_fence",
"minecraft:acacia_fence"
));
}
}
@Override
public Set<String> getAffectedBlockIds() {
return affectedBlockIds;
}
}

View File

@ -1,35 +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.mca.mapping;
import de.bluecolored.bluemap.core.world.BlockState;
public interface BlockIdMapper {
BlockState get(int id, int meta);
BlockState get(String id, int numeralId, int meta);
}

View File

@ -1,35 +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.mca.mapping;
import de.bluecolored.bluemap.core.world.BlockProperties;
import de.bluecolored.bluemap.core.world.BlockState;
@FunctionalInterface
public interface BlockPropertiesMapper {
BlockProperties get(BlockState blockState);
}

View File

@ -28,7 +28,6 @@
import com.flowpowered.math.matrix.Matrix3f;
import com.flowpowered.math.vector.Vector2f;
import com.flowpowered.math.vector.Vector3f;
import de.bluecolored.bluemap.core.util.MathUtils;
@Deprecated
public class Face {
@ -51,7 +50,7 @@ public Face(Vector3f p1, Vector3f p2, Vector3f p3, Vector2f uv1, Vector2f uv2, V
this.materialIndex = materialIndex;
this.n1 = getFaceNormal();
this.n1 = calculateSurfaceNormal(new VectorM3f(0, 0, 0));
this.n2 = new VectorM3f(n1);
this.n3 = new VectorM3f(n1);
this.normalizedNormals = true;
@ -202,10 +201,6 @@ public void setMaterialIndex(int materialIndex) {
this.materialIndex = materialIndex;
}
private VectorM3f getFaceNormal() {
return MathUtils.getSurfaceNormal(p1, p2, p3);
}
private void normalizeNormals() {
if (normalizedNormals) return;
@ -216,4 +211,31 @@ private void normalizeNormals() {
normalizedNormals = true;
}
private VectorM3f calculateSurfaceNormal(
VectorM3f target
){
double
p1x = p1.x, p1y = p1.y, p1z = p1.z,
p2x = p2.x, p2y = p2.y, p2z = p2.z,
p3x = p3.x, p3y = p3.y, p3z = p3.z;
p2x -= p1x; p2y -= p1y; p2z -= p1z;
p3x -= p1x; p3y -= p1y; p3z -= p1z;
p1x = p2y * p3z - p2z * p3y;
p1y = p2z * p3x - p2x * p3z;
p1z = p2x * p3y - p2y * p3x;
double length = Math.sqrt(p1x * p1x + p1y * p1y + p1z * p1z);
p1x /= length;
p1y /= length;
p1z /= length;
target.x = (float) p1x;
target.y = (float) p1y;
target.z = (float) p1z;
return target;
}
}

View File

@ -22,13 +22,52 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package de.bluecolored.bluemap.core.mca.mapping;
package de.bluecolored.bluemap.core.resourcepack;
import de.bluecolored.bluemap.core.world.Biome;
import org.spongepowered.configurate.ConfigurationNode;
@FunctionalInterface
public interface BiomeMapper {
import java.util.Map.Entry;
Biome get(int id);
public class BiomeConfig {
private Biome[] biomes;
public BiomeConfig() {
biomes = new Biome[10];
}
public void load(ConfigurationNode node) {
for (Entry<Object, ? extends ConfigurationNode> e : node.childrenMap().entrySet()){
String id = e.getKey().toString();
Biome biome = Biome.create(id, e.getValue());
int numeralId = biome.getNumeralId();
ensureAvailability(numeralId);
biomes[numeralId] = biome;
}
}
public Biome getBiome(int id) {
if (id < biomes.length) {
Biome biome = biomes[id];
return biome != null ? biome : Biome.DEFAULT;
}
return Biome.DEFAULT;
}
private void ensureAvailability(int id) {
if (id >= biomes.length) {
int newSize = biomes.length;
do {
newSize = (int) (newSize * 1.5) + 1;
} while (id >= newSize);
Biome[] newArray = new Biome[newSize];
System.arraycopy(biomes, 0, newArray, 0, biomes.length);
biomes = newArray;
}
}
}

View File

@ -29,7 +29,7 @@
import de.bluecolored.bluemap.core.util.ConfigUtils;
import de.bluecolored.bluemap.core.util.math.Color;
import de.bluecolored.bluemap.core.world.Biome;
import de.bluecolored.bluemap.core.world.Block;
import de.bluecolored.bluemap.core.world.BlockNeighborhood;
import org.spongepowered.configurate.ConfigurationNode;
import java.awt.image.BufferedImage;
@ -40,21 +40,16 @@
@DebugDump
public class BlockColorCalculatorFactory {
private BufferedImage foliageMap;
private BufferedImage grassMap;
private final int[] foliageMap = new int[65536];
private final int[] grassMap = new int[65536];
private final Map<String, ColorFunction> blockColorMap;
public BlockColorCalculatorFactory(BufferedImage foliageMap, BufferedImage grassMap) {
this.foliageMap = foliageMap;
this.grassMap = grassMap;
public BlockColorCalculatorFactory() {
this.blockColorMap = new HashMap<>();
}
public void loadColorConfig(ConfigurationNode colorConfig) {
blockColorMap.clear();
public void load(ConfigurationNode colorConfig) {
for (Entry<Object, ? extends ConfigurationNode> entry : colorConfig.childrenMap().entrySet()){
String key = entry.getKey().toString();
String value = entry.getValue().getString("");
@ -85,19 +80,11 @@ public void loadColorConfig(ConfigurationNode colorConfig) {
}
public void setFoliageMap(BufferedImage foliageMap) {
this.foliageMap = foliageMap;
}
public BufferedImage getFoliageMap() {
return foliageMap;
foliageMap.getRGB(0, 0, 256, 256, this.foliageMap, 0, 256);
}
public void setGrassMap(BufferedImage grassMap) {
this.grassMap = grassMap;
}
public BufferedImage getGrassMap() {
return grassMap;
grassMap.getRGB(0, 0, 256, 256, this.grassMap, 0, 256);
}
public BlockColorCalculator createCalculator() {
@ -106,14 +93,14 @@ public BlockColorCalculator createCalculator() {
@FunctionalInterface
private interface ColorFunction {
Color invoke(BlockColorCalculator calculator, Block block, Color target);
Color invoke(BlockColorCalculator calculator, BlockNeighborhood block, Color target);
}
public class BlockColorCalculator {
private final Color tempColor = new Color();
public Color getBlockColor(Block block, Color target) {
public Color getBlockColor(BlockNeighborhood block, Color target) {
String blockId = block.getBlockState().getFullId();
ColorFunction colorFunction = blockColorMap.get(blockId);
@ -123,7 +110,7 @@ public Color getBlockColor(Block block, Color target) {
return colorFunction.invoke(this, block, target);
}
public Color getRedstoneColor(Block block, Color target) {
public Color getRedstoneColor(BlockNeighborhood block, Color target) {
String powerString = block.getBlockState().getProperties().get("power");
int power = 15;
@ -137,54 +124,23 @@ public Color getRedstoneColor(Block block, Color target) {
);
}
public Color getWaterAverageColor(Block block, Color target) {
public Color getWaterAverageColor(BlockNeighborhood block, Color target) {
target.set(0, 0, 0, 0, true);
int x, y, z,
minX = block.getX() - 2,
maxX = block.getX() + 2,
minY = block.getY() - 1,
maxY = block.getY() + 1,
minZ = block.getZ() - 2,
maxZ = block.getZ() + 2;
minX = - 2,
maxX = + 2,
minY = - 1,
maxY = + 1,
minZ = - 2,
maxZ = + 2;
for (x = minX; x <= maxX; x++) {
for (y = minY; y <= maxY; y++) {
for (z = minZ; z <= maxZ; z++) {
target.add(block
.getWorld()
.getBiome(x, y, z)
.getWaterColor()
);
}
}
}
return target.flatten();
}
public Color getFoliageAverageColor(Block block, Color target) {
target.set(0, 0, 0, 0, true);
int x, y, z,
minX = block.getX() - 2,
maxX = block.getX() + 2,
minY = block.getY() - 1,
maxY = block.getY() + 1,
minZ = block.getZ() - 2,
maxZ = block.getZ() + 2;
int seaLevel = block.getWorld().getSeaLevel();
int blocksAboveSeaLevel;
Biome biome;
for (y = minY; y <= maxY; y++) {
blocksAboveSeaLevel = Math.max(block.getY() - seaLevel, 0);
for (x = minX; x <= maxX; x++) {
for (y = minY; y <= maxY; y++) {
for (z = minZ; z <= maxZ; z++) {
biome = block.getWorld().getBiome(x, y, z);
target.add(getFoliageColor(biome, blocksAboveSeaLevel, tempColor));
biome = block.getNeighborBlock(x, y, z).getBiome();
target.add(biome.getWaterColor());
}
}
}
@ -192,33 +148,52 @@ public Color getFoliageAverageColor(Block block, Color target) {
return target.flatten();
}
public Color getFoliageColor(Biome biome, int blocksAboveSeaLevel, Color target) {
getColorFromMap(biome, blocksAboveSeaLevel, foliageMap, target);
public Color getFoliageAverageColor(BlockNeighborhood block, Color target) {
target.set(0, 0, 0, 0, true);
int x, y, z,
minX = - 2,
maxX = + 2,
minY = - 1,
maxY = + 1,
minZ = - 2,
maxZ = + 2;
Biome biome;
for (y = minY; y <= maxY; y++) {
for (x = minX; x <= maxX; x++) {
for (z = minZ; z <= maxZ; z++) {
biome = block.getNeighborBlock(x, y, z).getBiome();
target.add(getFoliageColor(biome, tempColor));
}
}
}
return target.flatten();
}
public Color getFoliageColor(Biome biome, Color target) {
getColorFromMap(biome, foliageMap, 4764952, target);
return target.overlay(biome.getOverlayFoliageColor());
}
public Color getGrassAverageColor(Block block, Color target) {
public Color getGrassAverageColor(BlockNeighborhood block, Color target) {
target.set(0, 0, 0, 0, true);
int x, y, z,
minX = block.getX() - 2,
maxX = block.getX() + 2,
minY = block.getY() - 1,
maxY = block.getY() + 1,
minZ = block.getZ() - 2,
maxZ = block.getZ() + 2;
minX = - 2,
maxX = + 2,
minY = - 1,
maxY = + 1,
minZ = - 2,
maxZ = + 2;
int seaLevel = block.getWorld().getSeaLevel();
int blocksAboveSeaLevel;
Biome biome;
for (y = minY; y <= maxY; y++) {
blocksAboveSeaLevel = Math.max(block.getY() - seaLevel, 0);
for (x = minX; x <= maxX; x++) {
for (z = minZ; z <= maxZ; z++) {
biome = block.getWorld().getBiome(x, y, z);
target.add(getGrassColor(biome, blocksAboveSeaLevel, tempColor));
biome = block.getNeighborBlock(x, y, z).getBiome();
target.add(getGrassColor(biome, tempColor));
}
}
}
@ -226,24 +201,22 @@ public Color getGrassAverageColor(Block block, Color target) {
return target.flatten();
}
public Color getGrassColor(Biome biome, int blocksAboveSeaLevel, Color target) {
getColorFromMap(biome, blocksAboveSeaLevel, grassMap, target);
public Color getGrassColor(Biome biome, Color target) {
getColorFromMap(biome, grassMap, 0xff52952f, target);
return target.overlay(biome.getOverlayGrassColor());
}
private void getColorFromMap(Biome biome, int blocksAboveSeaLevel, BufferedImage map, Color target) {
float adjTemp = (float) GenericMath.clamp(biome.getTemp() - (0.00166667 * blocksAboveSeaLevel), 0, 1);
float adjHumidity = (float) GenericMath.clamp(biome.getHumidity(), 0, 1) * adjTemp;
private void getColorFromMap(Biome biome, int[] colorMap, int defaultColor, Color target) {
double temperature = GenericMath.clamp(biome.getTemp(), 0, 1);
double humidity = GenericMath.clamp(biome.getHumidity(), 0, 1) * temperature;
int x = (int) ((1 - adjTemp) * map.getWidth());
int y = (int) ((1 - adjHumidity) * map.getHeight());
int x = (int) ((1.0 - temperature) * 255.0);
int y = (int) ((1.0 - humidity) * 255.0);
int cValue = map.getRGB(
GenericMath.clamp(x, 0, map.getWidth() - 1),
GenericMath.clamp(y, 0, map.getHeight() - 1)
);
int index = y << 8 | x;
int color = index >= colorMap.length ? defaultColor : colorMap[index];
target.set(cValue);
target.set(color);
}
}

View File

@ -0,0 +1,82 @@
/*
* 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 de.bluecolored.bluemap.core.logger.Logger;
import de.bluecolored.bluemap.core.world.BlockProperties;
import de.bluecolored.bluemap.core.world.BlockState;
import org.spongepowered.configurate.ConfigurationNode;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
public class BlockPropertiesConfig {
private final Map<String, List<BlockStateMapping<BlockProperties>>> mappings;
public BlockPropertiesConfig() {
mappings = new ConcurrentHashMap<>();
}
public void load(ConfigurationNode node) {
for (Entry<Object, ? extends ConfigurationNode> e : node.childrenMap().entrySet()){
String key = e.getKey().toString();
try {
BlockState bsKey = BlockState.fromString(key);
BlockProperties.Builder bsValueBuilder = BlockProperties.builder();
readBool(e.getValue().node("culling"), bsValueBuilder::culling);
readBool(e.getValue().node("occluding"), bsValueBuilder::occluding);
readBool(e.getValue().node("alwaysWaterlogged"), bsValueBuilder::alwaysWaterlogged);
readBool(e.getValue().node("randomOffset"), bsValueBuilder::randomOffset);
BlockStateMapping<BlockProperties> mapping = new BlockStateMapping<>(bsKey, bsValueBuilder.build());
mappings.computeIfAbsent(bsKey.getFullId(), k -> new LinkedList<>()).add(0, mapping);
} catch (IllegalArgumentException ex) {
Logger.global.logWarning("Loading BlockPropertiesConfig: Failed to parse BlockState from key '" + key + "'");
}
}
}
private void readBool(ConfigurationNode node, Consumer<Boolean> target) {
if (!node.virtual()) target.accept(node.getBoolean());
}
public BlockProperties getBlockProperties(BlockState from){
for (BlockStateMapping<BlockProperties> bm : mappings.getOrDefault(from.getFullId(), Collections.emptyList())){
if (bm.fitsTo(from)){
return bm.getMapping();
}
}
return BlockProperties.DEFAULT;
}
}

View File

@ -22,12 +22,12 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package de.bluecolored.bluemap.core.config;
import java.util.Map.Entry;
package de.bluecolored.bluemap.core.resourcepack;
import de.bluecolored.bluemap.core.world.BlockState;
import java.util.Map.Entry;
class BlockStateMapping<T> {
private BlockState blockState;
private T mapping;

View File

@ -24,21 +24,34 @@
*/
package de.bluecolored.bluemap.core.resourcepack;
import de.bluecolored.bluemap.core.MinecraftVersion;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import de.bluecolored.bluemap.core.BlueMap;
import de.bluecolored.bluemap.core.debug.DebugDump;
import de.bluecolored.bluemap.core.logger.Logger;
import de.bluecolored.bluemap.core.resourcepack.BlockStateResource.Builder;
import de.bluecolored.bluemap.core.resourcepack.blockmodel.BlockModelResource;
import de.bluecolored.bluemap.core.resourcepack.blockmodel.TransformedBlockModelResource;
import de.bluecolored.bluemap.core.resourcepack.blockstate.BlockStateResource;
import de.bluecolored.bluemap.core.resourcepack.blockstate.BlockStateResource.Builder;
import de.bluecolored.bluemap.core.resourcepack.fileaccess.BluemapAssetOverrideFileAccess;
import de.bluecolored.bluemap.core.resourcepack.fileaccess.CaseInsensitiveFileAccess;
import de.bluecolored.bluemap.core.resourcepack.fileaccess.CombinedFileAccess;
import de.bluecolored.bluemap.core.resourcepack.fileaccess.FileAccess;
import de.bluecolored.bluemap.core.resourcepack.texture.Texture;
import de.bluecolored.bluemap.core.resourcepack.texture.TextureGallery;
import de.bluecolored.bluemap.core.util.Tristate;
import de.bluecolored.bluemap.core.world.Biome;
import de.bluecolored.bluemap.core.world.BlockProperties;
import de.bluecolored.bluemap.core.world.BlockState;
import org.apache.commons.io.output.ByteArrayOutputStream;
import org.spongepowered.configurate.gson.GsonConfigurationLoader;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
/**
* Represents all resources (BlockStates / BlockModels and Textures) that are loaded and used to generate map-models.
@ -46,61 +59,29 @@
@DebugDump
public class ResourcePack {
private static final String[] CONFIG_FILES = {
"blockColors.json",
"blockIds.json",
"blockProperties.json",
"biomes.json"
};
private final MinecraftVersion minecraftVersion;
protected final Map<String, BlockStateResource> blockStateResources;
protected final Map<String, BlockModelResource> blockModelResources;
protected final TextureGallery textures;
private final Map<String, BlockStateResource> blockStateResources;
private final Map<String, BlockModelResource> blockModelResources;
private final TextureGallery textures;
private final BlockPropertiesConfig blockPropertiesConfig;
private final BiomeConfig biomeConfig;
private final BlockColorCalculatorFactory blockColorCalculatorFactory;
private final Map<String, List<Resource>> configs;
private BufferedImage foliageMap;
private BufferedImage grassMap;
public ResourcePack(MinecraftVersion minecraftVersion) {
this.minecraftVersion = minecraftVersion;
private final LoadingCache<BlockState, BlockProperties> blockPropertiesCache;
public ResourcePack() {
blockStateResources = new HashMap<>();
blockModelResources = new HashMap<>();
textures = new TextureGallery();
foliageMap = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
foliageMap.setRGB(0, 0, 0xFF00FF00);
grassMap = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
grassMap.setRGB(0, 0, 0xFF00FF00);
blockColorCalculatorFactory = new BlockColorCalculatorFactory(foliageMap, grassMap);
configs = new HashMap<>();
}
/**
* Returns all config-files found in the namespaces of the ResourcePack with that filename
*/
public Collection<Resource> getConfigAdditions(String configFileName){
return configs.getOrDefault(configFileName, Collections.emptyList());
}
blockPropertiesConfig = new BlockPropertiesConfig();
biomeConfig = new BiomeConfig();
blockColorCalculatorFactory = new BlockColorCalculatorFactory();
/**
* See {@link TextureGallery#loadTextureFile(File)}
* @see TextureGallery#loadTextureFile(File)
*/
public void loadTextureFile(File file) throws IOException, ParseResourceException {
textures.loadTextureFile(file);
}
/**
* See {@link TextureGallery#saveTextureFile(File)}
* @see TextureGallery#saveTextureFile(File)
*/
public void saveTextureFile(File file) throws IOException {
textures.saveTextureFile(file);
blockPropertiesCache = Caffeine.newBuilder()
.executor(BlueMap.THREAD_POOL)
.maximumSize(10000)
.build(this::getBlockPropertiesNoCache);
}
/**
@ -159,37 +140,61 @@ public void load(File... sources) throws InterruptedException {
String filename = FileAccess.getFileName(blockstateFile);
if (!filename.endsWith(".json")) continue;
String jsonFileName = filename.substring(0, filename.length() - 5);
try {
blockStateResources.put(namespace + ":" + filename.substring(0, filename.length() - 5), builder.build(blockstateFile));
blockStateResources.put(namespace + ":" + jsonFileName, builder.build(blockstateFile));
} catch (IOException ex) {
Logger.global.logError("Failed to load blockstate: " + namespace + ":" + filename.substring(0, filename.length() - 5), ex);
Logger.global.logError("Failed to load blockstate: " + namespace + ":" + jsonFileName, ex);
}
}
//load configs
for (String configName : CONFIG_FILES) {
//load biomes
try {
Resource config = new Resource(sourcesAccess.readFile(
"assets/" + namespace + "/" + configName));
configs.computeIfAbsent(configName, t -> new ArrayList<>()).add(config);
} catch (FileNotFoundException ignore) {
GsonConfigurationLoader loader = GsonConfigurationLoader.builder()
.source(() -> new BufferedReader(new InputStreamReader(sourcesAccess.readFile(
"assets/" + namespace + "/biomes.json"))))
.build();
biomeConfig.load(loader.load());
} catch (IOException ex) {
Logger.global.logError("Failed to load config for " + namespace + ": " + configName, ex);
Logger.global.logError("Failed to load biomes.conf from: " + namespace, ex);
}
//load block properties
try {
GsonConfigurationLoader loader = GsonConfigurationLoader.builder()
.source(() -> new BufferedReader(new InputStreamReader(sourcesAccess.readFile(
"assets/" + namespace + "/blockProperties.json"))))
.build();
blockPropertiesConfig.load(loader.load());
} catch (IOException ex) {
Logger.global.logError("Failed to load biomes.conf from: " + namespace, ex);
}
//load block colors
try {
GsonConfigurationLoader loader = GsonConfigurationLoader.builder()
.source(() -> new BufferedReader(new InputStreamReader(sourcesAccess.readFile(
"assets/" + namespace + "/blockColors.json"))))
.build();
blockColorCalculatorFactory.load(loader.load());
} catch (IOException ex) {
Logger.global.logError("Failed to load biomes.conf from: " + namespace, ex);
}
}
try {
foliageMap = ImageIO.read(sourcesAccess.readFile("assets/minecraft/textures/colormap/foliage.png"));
blockColorCalculatorFactory.setFoliageMap(foliageMap);
} catch (IOException ex) {
blockColorCalculatorFactory.setFoliageMap(
ImageIO.read(sourcesAccess.readFile("assets/minecraft/textures/colormap/foliage.png"))
);
} catch (IOException | ArrayIndexOutOfBoundsException ex) {
Logger.global.logError("Failed to load foliagemap!", ex);
}
try {
grassMap = ImageIO.read(sourcesAccess.readFile("assets/minecraft/textures/colormap/grass.png"));
blockColorCalculatorFactory.setGrassMap(grassMap);
} catch (IOException ex) {
blockColorCalculatorFactory.setGrassMap(
ImageIO.read(sourcesAccess.readFile("assets/minecraft/textures/colormap/grass.png"))
);
} catch (IOException | ArrayIndexOutOfBoundsException ex) {
Logger.global.logError("Failed to load grassmap!", ex);
}
@ -198,6 +203,22 @@ public void load(File... sources) throws InterruptedException {
}
}
/**
* See {@link TextureGallery#loadTextureFile(File)}
* @see TextureGallery#loadTextureFile(File)
*/
public void loadTextureFile(File file) throws IOException, ParseResourceException {
textures.loadTextureFile(file);
}
/**
* See {@link TextureGallery#saveTextureFile(File)}
* @see TextureGallery#saveTextureFile(File)
*/
public void saveTextureFile(File file) throws IOException {
textures.saveTextureFile(file);
}
/**
* Returns a {@link BlockStateResource} for the given {@link BlockState} if found.
* @param state The {@link BlockState}
@ -210,15 +231,47 @@ public BlockStateResource getBlockStateResource(BlockState state) throws NoSuchR
return resource;
}
public BlockProperties getBlockProperties(BlockState state) {
return blockPropertiesCache.get(state);
}
private BlockProperties getBlockPropertiesNoCache(BlockState state) {
BlockProperties.Builder props = blockPropertiesConfig.getBlockProperties(state).toBuilder();
if (props.isOccluding() == Tristate.UNDEFINED || props.isCulling() == Tristate.UNDEFINED) {
try {
BlockStateResource resource = getBlockStateResource(state);
for (TransformedBlockModelResource bmr : resource.getModels(state, new ArrayList<>())) {
if (props.isOccluding() == Tristate.UNDEFINED) props.occluding(bmr.getModel().isOccluding());
if (props.isCulling() == Tristate.UNDEFINED) props.culling(bmr.getModel().isCulling());
}
} catch (NoSuchResourceException ignore) {}
}
return props.build();
}
public Biome getBiome(int id) {
return biomeConfig.getBiome(id);
}
public Map<String, BlockStateResource> getBlockStateResources() {
return blockStateResources;
}
public Map<String, BlockModelResource> getBlockModelResources() {
return blockModelResources;
}
public TextureGallery getTextures() {
return textures;
}
public BlockColorCalculatorFactory getBlockColorCalculatorFactory() {
return blockColorCalculatorFactory;
}
public MinecraftVersion getMinecraftVersion() {
return minecraftVersion;
}
protected static String namespacedToAbsoluteResourcePath(String namespacedPath, String resourceTypeFolder) {
public static String namespacedToAbsoluteResourcePath(String namespacedPath, String resourceTypeFolder) {
String path = namespacedPath;
resourceTypeFolder = FileAccess.normalize(resourceTypeFolder);
@ -246,7 +299,7 @@ public static class Resource {
private final byte[] data;
public Resource(InputStream data) throws FileNotFoundException, IOException {
public Resource(InputStream data) throws IOException {
try (ByteArrayOutputStream bout = new ByteArrayOutputStream()) {
bout.write(data);
this.data = bout.toByteArray();

View File

@ -22,13 +22,16 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package de.bluecolored.bluemap.core.resourcepack;
package de.bluecolored.bluemap.core.resourcepack.blockmodel;
import com.flowpowered.math.TrigMath;
import com.flowpowered.math.vector.Vector3f;
import com.flowpowered.math.vector.Vector3i;
import com.flowpowered.math.vector.Vector4f;
import de.bluecolored.bluemap.core.resourcepack.BlockModelResource.Element.Face;
import de.bluecolored.bluemap.core.resourcepack.ParseResourceException;
import de.bluecolored.bluemap.core.resourcepack.ResourcePack;
import de.bluecolored.bluemap.core.resourcepack.texture.Texture;
import de.bluecolored.bluemap.core.resourcepack.blockmodel.BlockModelResource.Element.Face;
import de.bluecolored.bluemap.core.resourcepack.fileaccess.FileAccess;
import de.bluecolored.bluemap.core.util.Direction;
import de.bluecolored.bluemap.core.util.math.Axis;
@ -49,9 +52,9 @@ public class BlockModelResource {
private boolean culling = false;
private boolean occluding = false;
private boolean ambientOcclusion = true;
private Collection<Element> elements = new ArrayList<>();
private Map<String, Texture> textures = new HashMap<>();
private final boolean ambientOcclusion = true; //TODO: wat?
private final Collection<Element> elements = new ArrayList<>();
private final Map<String, Texture> textures = new HashMap<>();
private BlockModelResource() {}
@ -477,9 +480,9 @@ private Texture getTexture(String key, int depth) throws NoSuchElementException,
Texture texture;
try {
texture = resourcePack.textures.get(path);
texture = resourcePack.getTextures().get(path);
} catch (NoSuchElementException ex) {
texture = resourcePack.textures.loadTexture(sourcesAccess, path);
texture = resourcePack.getTextures().loadTexture(sourcesAccess, path);
}
return texture;

View File

@ -22,7 +22,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package de.bluecolored.bluemap.core.resourcepack;
package de.bluecolored.bluemap.core.resourcepack.blockmodel;
public enum ModelType {

View File

@ -22,7 +22,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package de.bluecolored.bluemap.core.resourcepack;
package de.bluecolored.bluemap.core.resourcepack.blockmodel;
import com.flowpowered.math.vector.Vector2f;
import de.bluecolored.bluemap.core.util.math.MatrixM3f;

View File

@ -22,19 +22,20 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package de.bluecolored.bluemap.core.resourcepack;
package de.bluecolored.bluemap.core.resourcepack.blockstate;
import com.flowpowered.math.vector.Vector2f;
import com.flowpowered.math.vector.Vector3i;
import de.bluecolored.bluemap.core.MinecraftVersion;
import de.bluecolored.bluemap.core.logger.Logger;
import de.bluecolored.bluemap.core.resourcepack.PropertyCondition.All;
import de.bluecolored.bluemap.core.resourcepack.ParseResourceException;
import de.bluecolored.bluemap.core.resourcepack.ResourcePack;
import de.bluecolored.bluemap.core.resourcepack.blockmodel.BlockModelResource;
import de.bluecolored.bluemap.core.resourcepack.blockmodel.TransformedBlockModelResource;
import de.bluecolored.bluemap.core.resourcepack.blockstate.PropertyCondition.All;
import de.bluecolored.bluemap.core.resourcepack.fileaccess.FileAccess;
import de.bluecolored.bluemap.core.util.MathUtils;
import de.bluecolored.bluemap.core.world.BlockState;
import org.apache.commons.lang3.StringUtils;
import org.spongepowered.configurate.ConfigurationNode;
import org.spongepowered.configurate.gson.GsonConfigurationLoader;
import org.apache.commons.lang3.StringUtils;
import java.io.BufferedReader;
import java.io.IOException;
@ -46,8 +47,6 @@
public class BlockStateResource {
private static final MinecraftVersion NEW_MODEL_PATH_VERSION = new MinecraftVersion(1, 13);
private final List<Variant> variants = new ArrayList<>(0);
private final Collection<Variant> multipart = new ArrayList<>(0);
@ -92,53 +91,6 @@ public Collection<TransformedBlockModelResource> getModels(BlockState blockState
return targetCollection;
}
private static class Variant {
private PropertyCondition condition = PropertyCondition.all();
private Collection<Weighted<TransformedBlockModelResource>> models = new ArrayList<>();
private double totalWeight;
private Variant() {
}
public TransformedBlockModelResource getModel(int x, int y, int z) {
if (models.isEmpty()) throw new IllegalStateException("A variant must have at least one model!");
double selection = MathUtils.hashToFloat(x, y, z, 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 checkValid() throws ParseResourceException {
if (models.isEmpty()) throw new ParseResourceException("A variant must have at least one model!");
}
public void updateTotalWeight() {
totalWeight = 0d;
for (Weighted<?> w : models) {
totalWeight += w.weight;
}
}
}
private static class Weighted<T> {
private final T value;
private final 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);
}
@ -242,15 +194,8 @@ private Weighted<TransformedBlockModelResource> loadModel(ConfigurationNode node
if (namespacedModelPath == null)
throw new ParseResourceException("No model defined!");
String modelPath;
if (resourcePack.getMinecraftVersion().isBefore(NEW_MODEL_PATH_VERSION)) {
modelPath = ResourcePack.namespacedToAbsoluteResourcePath(namespacedModelPath, "models/block") + ".json";
}else {
modelPath = ResourcePack.namespacedToAbsoluteResourcePath(namespacedModelPath, "models") + ".json";
}
BlockModelResource model = resourcePack.blockModelResources.get(modelPath);
String modelPath = ResourcePack.namespacedToAbsoluteResourcePath(namespacedModelPath, "models") + ".json";
BlockModelResource model = resourcePack.getBlockModelResources().get(modelPath);
if (model == null) {
BlockModelResource.Builder builder = BlockModelResource.builder(sourcesAccess, resourcePack);
try {
@ -260,7 +205,7 @@ private Weighted<TransformedBlockModelResource> loadModel(ConfigurationNode node
throw new ParseResourceException("Failed to load model " + modelPath, e);
}
resourcePack.blockModelResources.put(modelPath, model);
resourcePack.getBlockModelResources().put(modelPath, model);
}
Vector2f rotation = new Vector2f(node.node("x").getFloat(0), node.node("y").getFloat(0));

View File

@ -22,21 +22,24 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package de.bluecolored.bluemap.core.mca.extensions;
package de.bluecolored.bluemap.core.resourcepack.blockstate;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
public class Conditional<T> {
public class TripwireConnectExtension extends ConnectExtension {
private final PropertyCondition condition;
private final T value;
private static final HashSet<String> AFFECTED_BLOCK_IDS = new HashSet<>(Collections.singletonList(
"minecraft:tripwire"
));
public Conditional(PropertyCondition condition, T value) {
this.condition = condition;
this.value = value;
}
@Override
public Set<String> getAffectedBlockIds() {
return AFFECTED_BLOCK_IDS;
public PropertyCondition getCondition() {
return condition;
}
public T getValue() {
return value;
}
}

View File

@ -22,12 +22,14 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package de.bluecolored.bluemap.core.resourcepack;
package de.bluecolored.bluemap.core.resourcepack.blockstate;
import de.bluecolored.bluemap.core.util.Preconditions;
import de.bluecolored.bluemap.core.world.BlockState;
import java.util.Map;
@FunctionalInterface
public interface PropertyCondition {
@ -152,4 +154,17 @@ static PropertyCondition property(String key, String... possibleValues) {
return or(conditions);
}
static PropertyCondition blockState(BlockState state) {
Map<String, String> props = state.getProperties();
if (props.isEmpty()) return all();
PropertyCondition[] conditions = new Property[props.size()];
int i = 0;
for (Map.Entry<String, String> prop : props.entrySet()) {
conditions[i++] = property(prop.getKey(), prop.getValue());
}
return and(conditions);
}
}

View File

@ -0,0 +1,70 @@
/*
* This file is part of BlueMap, licensed under the MIT License (MIT).
*
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package de.bluecolored.bluemap.core.resourcepack.blockstate;
import de.bluecolored.bluemap.core.resourcepack.ParseResourceException;
import de.bluecolored.bluemap.core.resourcepack.blockmodel.TransformedBlockModelResource;
import java.util.ArrayList;
import java.util.Collection;
public class Variant {
PropertyCondition condition = PropertyCondition.all();
Collection<Weighted<TransformedBlockModelResource>> models = new ArrayList<>(0);
private double totalWeight;
Variant() {}
public TransformedBlockModelResource getModel(int x, int y, int z) {
if (models.isEmpty()) throw new IllegalStateException("A variant must have at least one model!");
double selection = hashToFloat(x, y, z) * totalWeight; // random based on position
for (Weighted<TransformedBlockModelResource> w : models) {
selection -= w.getWeight();
if (selection <= 0) return w.getValue();
}
throw new RuntimeException("This line should never be reached!");
}
public void checkValid() throws ParseResourceException {
if (models.isEmpty()) throw new ParseResourceException("A variant must have at least one model!");
}
public void updateTotalWeight() {
totalWeight = 0d;
for (Weighted<?> w : models) {
totalWeight += w.getWeight();
}
}
private static float hashToFloat(int x, int y, int z) {
final long hash = x * 73438747 ^ y * 9357269 ^ z * 4335792;
return (hash * (hash + 456149) & 0x00ffffff) / (float) 0x01000000;
}
}

View File

@ -22,19 +22,24 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package de.bluecolored.bluemap.core.mca.extensions;
package de.bluecolored.bluemap.core.resourcepack.blockstate;
import java.util.Set;
public class Weighted<T> {
import com.flowpowered.math.vector.Vector3i;
private final T value;
private final double weight;
import de.bluecolored.bluemap.core.mca.MCAWorld;
import de.bluecolored.bluemap.core.world.BlockState;
public Weighted(T value, double weight) {
this.value = value;
this.weight = weight;
}
public interface BlockStateExtension {
public double getWeight() {
return weight;
}
BlockState extend(MCAWorld world, Vector3i pos, BlockState state);
Set<String> getAffectedBlockIds();
public T getValue() {
return value;
}
}

View File

@ -22,7 +22,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package de.bluecolored.bluemap.core.resourcepack;
package de.bluecolored.bluemap.core.resourcepack.texture;
import de.bluecolored.bluemap.core.util.math.Color;

View File

@ -22,10 +22,11 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package de.bluecolored.bluemap.core.resourcepack;
package de.bluecolored.bluemap.core.resourcepack.texture;
import com.google.gson.*;
import de.bluecolored.bluemap.core.logger.Logger;
import de.bluecolored.bluemap.core.resourcepack.ParseResourceException;
import de.bluecolored.bluemap.core.resourcepack.fileaccess.FileAccess;
import de.bluecolored.bluemap.core.util.FileUtils;
import de.bluecolored.bluemap.core.util.math.Color;
@ -43,8 +44,8 @@ public class TextureGallery {
private static final String EMPTY_BASE64 = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAQAAAC1+jfqAAAAEUlEQVR42mNkIAAYRxWMJAUAE5gAEdz4t9QAAAAASUVORK5CYII=";
private Map<String, Texture> textureMap;
private List<Texture> textureList;
private final Map<String, Texture> textureMap;
private final List<Texture> textureList;
public TextureGallery() {
textureMap = new HashMap<>();
@ -149,13 +150,11 @@ public synchronized void loadTextureFile(File file) throws IOException, ParseRes
String path = texture.get("id").getAsString();
boolean transparent = texture.get("transparent").getAsBoolean();
Color color = readColor(texture.get("color").getAsJsonArray());
textureList.set(i, new Texture(i, path, color, transparent, EMPTY_BASE64));
textureList.set(i, new Texture(i, path, color, transparent, texture.get("texture").getAsString()));
} catch (ParseResourceException | RuntimeException ex) {
Logger.global.logWarning("Failed to load texture with id " + i + " from texture file " + file + "!");
}
}
} catch (IOException ex) {
throw ex;
} catch (RuntimeException ex) {
throw new ParseResourceException("Invalid texture file format!", ex);
} finally {
@ -221,11 +220,7 @@ 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.noFloodWarning("TextureGallery-uiz78tef5", "Failed to reload texture: " + texture.getPath());
Logger.global.noFloodWarning("TextureGallery-89763455h", "This happens if the resource-packs have changed, but you have not deleted your already generated maps. This might result in broken map-models!");
Logger.global.noFloodWarning("TextureGallery-re56ugb56", "(Future warnings of this will be suppressed, so more textures might have failed to load after this)");
}
} catch (IOException ignored) {}
}
}

View File

@ -1,59 +0,0 @@
package de.bluecolored.bluemap.core.util;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Supplier;
/**
* A pool of objects that (lazily) maintains a specific size of objects.
* that threads can take, use and return.
* It discards excessive objects and creates new ones when needed.
*/
public class ArrayPool<T> {
private final Object[] objects;
private final int capacity, maxConcurrency;
private final Supplier<T> supplier;
private final AtomicInteger head, tail; // head is excluded, tail included
private int size;
public ArrayPool(int capacity, int maxConcurrency, Supplier<T> supplier) {
this.capacity = capacity;
this.objects = new Object[capacity + maxConcurrency * 2];
this.maxConcurrency = maxConcurrency;
this.supplier = supplier;
this.head = new AtomicInteger(0);
this.tail = new AtomicInteger(0);
this.size = 0;
}
@SuppressWarnings("unchecked")
public T take() {
while (size < maxConcurrency) add(supplier.get());
size --;
return (T) objects[tail.getAndUpdate(this::nextPointer)];
}
public void add(T resource) {
while (size > capacity + maxConcurrency) take();
objects[head.getAndUpdate(this::nextPointer)] = resource;
size ++;
}
public int size() {
return size;
}
public int capacity() {
return capacity;
}
private int nextPointer(int prev) {
return (prev + 1) % objects.length;
}
}

View File

@ -28,6 +28,7 @@
import com.flowpowered.math.vector.Vector3f;
import com.flowpowered.math.vector.Vector3i;
import com.flowpowered.math.vector.Vector4f;
import de.bluecolored.bluemap.core.util.math.VectorM3f;
@Deprecated //TODO
public class MathUtils {

View File

@ -22,48 +22,51 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package de.bluecolored.bluemap.sponge;
package de.bluecolored.bluemap.core.util;
import org.slf4j.Logger;
import java.util.function.BooleanSupplier;
import de.bluecolored.bluemap.core.logger.AbstractLogger;
public enum Tristate {
public class Slf4jLogger extends AbstractLogger {
private Logger out;
public Slf4jLogger(Logger out) {
this.out = out;
TRUE (true),
UNDEFINED (false) {
@Override
public Tristate getOr(Tristate other) {
return other;
}
@Override
public void logError(String message, Throwable throwable) {
out.error(message, throwable);
public boolean getOr(BooleanSupplier other) {
return other.getAsBoolean();
}
@Override
public void logWarning(String message) {
out.warn(message);
public boolean getOr(boolean defaultValue) {
return defaultValue;
}
},
FALSE (false);
private final boolean value;
Tristate(boolean value ) {
this.value = value;
}
public Tristate getOr(Tristate other) {
return this;
}
public boolean getOr(BooleanSupplier other) {
return value;
}
public boolean getOr(boolean defaultValue) {
return value;
}
@Override
public void logInfo(String message) {
out.info(message);
public String toString() {
return "Tristate." + name();
}
@Override
public void logDebug(String 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);
}
}

View File

@ -1,3 +1,27 @@
/*
* This file is part of BlueMap, licensed under the MIT License (MIT).
*
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package de.bluecolored.bluemap.core.util.math;
public class Color {

View File

@ -24,10 +24,7 @@
*/
package de.bluecolored.bluemap.core.world;
import com.flowpowered.math.vector.Vector3i;
import de.bluecolored.bluemap.core.util.Direction;
public class Block {
public class Block<T extends Block<T>> {
private World world;
private int x, y, z;
@ -35,23 +32,19 @@ public class Block {
private Chunk chunk;
private BlockState blockState;
private BlockProperties properties;
private LightData lightData;
private Biome biome;
private int sunLight;
private int blockLight;
private final transient LightData tempLight;
private int biomeId;
public Block(World world, int x, int y, int z) {
tempLight = new LightData(0, 0);
set(world, x, y, z);
}
public Block set(World world, int x, int y, int z) {
if (this.x == x && this.y == y && this.z == z && this.world == world) return this;
public T set(World world, int x, int y, int z) {
if (this.x == x && this.z == z && this.world == world){
if (this.y == y) return self();
} else {
this.chunk = null; //only reset the chunk if x or z have changed
}
this.world = world;
this.x = x;
@ -60,11 +53,15 @@ public Block set(World world, int x, int y, int z) {
reset();
return this;
return self();
}
public Block set(int x, int y, int z) {
if (this.x == x && this.y == y && this.z == z) return this;
public T set(int x, int y, int z) {
if (this.x == x && this.z == z){
if (this.y == y) return self();
} else {
this.chunk = null; //only reset the chunk if x or z have changed
}
this.x = x;
this.y = y;
@ -72,27 +69,49 @@ public Block set(int x, int y, int z) {
reset();
return this;
return self();
}
private void reset() {
this.chunk = null;
protected void reset() {
this.blockState = null;
this.properties = null;
this.lightData = new LightData(-1, -1);
this.biome = null;
this.blockLight = -1;
this.sunLight = -1;
this.biomeId = -1;
}
public Block add(int dx, int dy, int dz) {
public T add(int dx, int dy, int dz) {
return set(x + dx, y + dy, z + dz);
}
public Block copy(Block source) {
return set(source.world, source.x, source.y, source.z);
public T copy(Block<?> source) {
this.world = source.world;
this.chunk = source.chunk;
this.x = source.x;
this.y = source.y;
this.z = source.z;
reset();
this.blockState = source.blockState;
this.lightData = new LightData(source.lightData.getSkyLight(), source.lightData.getBlockLight());
this.biomeId = source.biomeId;
return self();
}
/**
* copy with offset
*/
public T copy(Block<?> source, int dx, int dy, int dz) {
this.world = source.world;
this.x = source.x + dx;
this.y = source.y + dy;
this.z = source.z + dz;
this.chunk = null;
reset();
return self();
}
public World getWorld() {
@ -121,19 +140,14 @@ public BlockState getBlockState() {
return blockState;
}
public BlockProperties getProperties() {
if (properties == null) properties = world.getBlockProperties(getBlockState());
return properties;
}
public LightData getLightData() {
if (lightData.getSkyLight() < 0) getChunk().getLightData(x, y, z, lightData);
return lightData;
}
public Biome getBiome() {
if (biome == null) biome = getChunk().getBiome(x, y, z);
return biome;
public int getBiomeId() {
if (biomeId == -1) biomeId = getChunk().getBiome(x, y, z);
return biomeId;
}
public int getSunLightLevel() {
@ -144,67 +158,32 @@ public int getBlockLightLevel() {
return getLightData().getBlockLight();
}
public boolean isCullingNeighborFaces() {
return getProperties().isCulling();
}
public boolean isFlammable() {
return getProperties().isFlammable();
}
public boolean isOccludingNeighborFaces(){
return getProperties().isOccluding();
}
/**
* This is internally used for light rendering
* It is basically the sun light that is projected onto adjacent faces
*/
public int getPassedSunLight() {
if (sunLight < 0) calculateLight();
return sunLight;
}
/**
* This is internally used for light rendering
* It is basically the block light that is projected onto adjacent faces
*/
public int getPassedBlockLight() {
if (blockLight < 0) calculateLight();
return blockLight;
}
private void calculateLight() {
sunLight = getSunLightLevel();
blockLight = getBlockLightLevel();
if (blockLight > 0 || sunLight > 0) return;
Vector3i dirV;
int nx, ny, nz;
for (Direction direction : Direction.values()) {
dirV = direction.toVector();
nx = dirV.getX() + x;
ny = dirV.getY() + y;
nz = dirV.getZ() + z;
world.getLightData(nx, ny, nz, tempLight);
sunLight = Math.max(tempLight.getSkyLight(), sunLight);
blockLight = Math.max(tempLight.getBlockLight(), blockLight);
}
}
@Override
public String toString() {
if (world != null) {
return "Block{" +
"world=" + world +
", x=" + x +
", y=" + y +
", z=" + z +
", sunLight=" + sunLight +
", blockLight=" + blockLight +
", chunk=" + getChunk() +
", blockState=" + getBlockState() +
", lightData=" + getLightData() +
", biomeId=" + getBiomeId() +
'}';
} else {
return "Block{" +
"world=" + world +
", x=" + x +
", y=" + y +
", z=" + z +
'}';
}
}
@SuppressWarnings("unchecked")
protected T self() {
return (T) this;
}
}

View File

@ -0,0 +1,90 @@
/*
* This file is part of BlueMap, licensed under the MIT License (MIT).
*
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package de.bluecolored.bluemap.core.world;
import de.bluecolored.bluemap.core.resourcepack.ResourcePack;
public class BlockNeighborhood<T extends BlockNeighborhood<T>> extends ResourcePackBlock<T> {
private static final int DIAMETER = 8;
private static final int DIAMETER_MASK = DIAMETER - 1;
private static final int DIAMETER_SQUARED = DIAMETER * DIAMETER;
private final ResourcePackBlock<?>[] neighborhood;
private int thisIndex;
public BlockNeighborhood(ResourcePackBlock<?> center) {
super(center.getResourcePack(), null, 0, 0, 0);
copy(center);
neighborhood = new ResourcePackBlock[DIAMETER * DIAMETER * DIAMETER];
init();
}
public BlockNeighborhood(ResourcePack resourcePack, World world, int x, int y, int z) {
super(resourcePack, world, x, y, z);
neighborhood = new ResourcePackBlock[DIAMETER * DIAMETER * DIAMETER];
init();
}
@Override
protected void reset() {
super.reset();
this.thisIndex = -1;
}
private void init() {
this.thisIndex = -1;
for (int i = 0; i < neighborhood.length; i++) {
neighborhood[i] = new ResourcePackBlock<>(this.getResourcePack(), null, 0, 0, 0);
}
}
public ResourcePackBlock<?> getNeighborBlock(int dx, int dy, int dz) {
int i = neighborIndex(dx, dy, dz);
if (i == thisIndex()) return this;
return neighborhood[i].set(
getWorld(),
getX() + dx,
getY() + dy,
getZ() + dz
);
}
private int thisIndex() {
if (thisIndex == -1) thisIndex = neighborIndex(0, 0, 0);
return thisIndex;
}
private int neighborIndex(int dx, int dy, int dz) {
return ((getX() + dx) & DIAMETER_MASK) * DIAMETER_SQUARED +
((getY() + dy) & DIAMETER_MASK) * DIAMETER +
((getZ() + dz) & DIAMETER_MASK);
}
}

View File

@ -24,29 +24,122 @@
*/
package de.bluecolored.bluemap.core.world;
import de.bluecolored.bluemap.core.util.Tristate;
public class BlockProperties {
public static final BlockProperties SOLID = new BlockProperties(true, true, false);
public static final BlockProperties TRANSPARENT = new BlockProperties(false, false, false);
public static final BlockProperties DEFAULT = new BlockProperties();
private final boolean culling, occluding, flammable;
private Tristate culling, occluding, alwaysWaterlogged, randomOffset;
public BlockProperties(boolean culling, boolean occluding, boolean flammable) {
public BlockProperties() {
this.culling = Tristate.UNDEFINED;
this.occluding = Tristate.UNDEFINED;
this.alwaysWaterlogged = Tristate.UNDEFINED;
this.randomOffset = Tristate.UNDEFINED;
}
public BlockProperties(
Tristate culling,
Tristate occluding,
Tristate alwaysWaterlogged,
Tristate randomOffset
) {
this.culling = culling;
this.occluding = occluding;
this.flammable = flammable;
this.alwaysWaterlogged = alwaysWaterlogged;
this.randomOffset = randomOffset;
}
public boolean isCulling() {
return culling;
return culling.getOr(true);
}
public boolean isOccluding() {
return occluding.getOr(true);
}
public boolean isAlwaysWaterlogged() {
return alwaysWaterlogged.getOr(false);
}
public boolean isRandomOffset() {
return randomOffset.getOr(false);
}
public Builder toBuilder() {
return new BlockProperties(
culling,
occluding,
alwaysWaterlogged,
randomOffset
).new Builder();
}
public static Builder builder() {
return new BlockProperties().new Builder();
}
public class Builder {
public Builder culling(boolean culling) {
BlockProperties.this.culling = culling ? Tristate.TRUE : Tristate.FALSE;
return this;
}
public Builder occluding(boolean occluding) {
BlockProperties.this.occluding = occluding ? Tristate.TRUE : Tristate.FALSE;
return this;
}
public Builder alwaysWaterlogged(boolean alwaysWaterlogged) {
BlockProperties.this.alwaysWaterlogged = alwaysWaterlogged ? Tristate.TRUE : Tristate.FALSE;
return this;
}
public Builder randomOffset(boolean randomOffset) {
BlockProperties.this.randomOffset = randomOffset ? Tristate.TRUE : Tristate.FALSE;
return this;
}
public Builder from(BlockProperties other) {
culling = other.culling.getOr(culling);
occluding = other.occluding.getOr(occluding);
alwaysWaterlogged = other.alwaysWaterlogged.getOr(alwaysWaterlogged);
randomOffset = other.randomOffset.getOr(randomOffset);
return this;
}
public BlockProperties build() {
return BlockProperties.this;
}
public Tristate isCulling() {
return culling;
}
public Tristate isOccluding() {
return occluding;
}
public boolean isFlammable() {
return flammable;
public Tristate isAlwaysWaterlogged() {
return alwaysWaterlogged;
}
public Tristate isRandomOffset() {
return randomOffset;
}
}
@Override
public String toString() {
return "BlockProperties{" +
"culling=" + culling +
", occluding=" + occluding +
", alwaysWaterlogged=" + alwaysWaterlogged +
", randomOffset=" + randomOffset +
'}';
}
}

View File

@ -24,8 +24,6 @@
*/
package de.bluecolored.bluemap.core.world;
import de.bluecolored.bluemap.core.MinecraftVersion;
import java.util.*;
import java.util.Map.Entry;
import java.util.regex.Matcher;
@ -39,46 +37,10 @@
*/
public class BlockState {
private static final Pattern BLOCKSTATE_SERIALIZATION_PATTERN = Pattern.compile("^(.+?)(?:\\[(.*)\\])?$");
private static final Pattern BLOCKSTATE_SERIALIZATION_PATTERN = Pattern.compile("^(.+?)(?:\\[(.*)])?$");
private static final HashSet<String> DEFAULT_WATERLOGGED_BLOCK_IDS = new HashSet<>(Arrays.asList(
"minecraft:seagrass",
"minecraft:tall_seagrass",
"minecraft:kelp",
"minecraft:kelp_plant",
"minecraft:bubble_column"
));
private static final HashSet<String> OFFSET_BLOCK_IDS = new HashSet<>(Arrays.asList(
"minecraft:grass",
"minecraft:tall_grass",
"minecraft:fern",
"minecraft:dandelion",
"minecraft:cornflower",
"minecraft:poppy",
"minecraft:blue_orchid",
"minecraft:allium",
"minecraft:azure_bluet",
"minecraft:red_tulip",
"minecraft:orange_tulip",
"minecraft:white_tulip",
"minecraft:pink_tulip",
"minecraft:oxeye_daisy",
"minecraft:lily_of_the_valley",
"minecraft:wither_rose",
"minecraft:crimson_roots",
"minecraft:warped_roots",
"minecraft:nether_sprouts",
"minecraft:rose_bush",
"minecraft:peony",
"minecraft:lilac",
"minecraft:sunflower",
"minecraft:hanging_roots",
"minecraft:small_dripleaf"
));
public static final BlockState AIR = new BlockState(MinecraftVersion.LATEST_SUPPORTED, "minecraft:air", Collections.emptyMap());
public static final BlockState MISSING = new BlockState(MinecraftVersion.LATEST_SUPPORTED, "bluemap:missing", Collections.emptyMap());
public static final BlockState AIR = new BlockState("minecraft:air");
public static final BlockState MISSING = new BlockState("bluemap:missing");
private boolean hashed;
private int hash;
@ -88,14 +50,13 @@ public class BlockState {
private final String fullId;
private final Map<String, String> properties;
// special fast-access properties
public final boolean isAir, isWater, isWaterlogged, isRandomOffset;
private final boolean isAir, isWater, isWaterlogged;
public BlockState(MinecraftVersion version, String id) {
this(version, id, Collections.emptyMap());
public BlockState(String id) {
this(id, Collections.emptyMap());
}
public BlockState(MinecraftVersion version, String id, Map<String, String> properties) {
public BlockState(String id, Map<String, String> properties) {
this.hashed = false;
this.hash = 0;
@ -121,38 +82,7 @@ public BlockState(MinecraftVersion version, String id, Map<String, String> prope
"minecraft:void_air".equals(this.fullId);
this.isWater = "minecraft:water".equals(this.fullId);
this.isWaterlogged =
DEFAULT_WATERLOGGED_BLOCK_IDS.contains(this.fullId) ||
"true".equals(this.properties.get("waterlogged"));
if (version.isAtLeast(MinecraftVersion.THE_FLATTENING)) {
this.isRandomOffset = OFFSET_BLOCK_IDS.contains(this.fullId);
} else {
this.isRandomOffset =
"minecraft:tall_grass".equals(this.fullId);
}
}
private BlockState(BlockState blockState, String withKey, String withValue) {
this.hashed = false;
this.hash = 0;
Map<String, String> props = new HashMap<>(blockState.getProperties());
props.put(withKey, withValue);
this.id = blockState.getId();
this.namespace = blockState.getNamespace();
this.fullId = namespace + ":" + id;
this.properties = props;
// special fast-access properties
this.isAir = blockState.isAir;
this.isWater = blockState.isWater;
this.isWaterlogged =
DEFAULT_WATERLOGGED_BLOCK_IDS.contains(this.fullId) ||
"true".equals(this.properties.get("waterlogged"));
this.isRandomOffset = blockState.isRandomOffset;
this.isWaterlogged = "true".equals(properties.get("waterlogged"));
}
/**
@ -191,11 +121,16 @@ public Map<String, String> getProperties() {
return properties;
}
/**
* Returns a new BlockState with the given property changed
*/
public BlockState with(String property, String value) {
return new BlockState(this, property, value);
public boolean isAir() {
return isAir;
}
public boolean isWater() {
return isWater;
}
public boolean isWaterlogged() {
return isWaterlogged;
}
@Override
@ -205,8 +140,7 @@ public boolean equals(Object obj) {
if (!(obj instanceof BlockState)) return false;
BlockState b = (BlockState) obj;
if (!Objects.equals(getFullId(), b.getFullId())) return false;
if (!Objects.equals(getProperties(), b.getProperties())) return false;
return true;
return Objects.equals(getProperties(), b.getProperties());
}
@Override
@ -229,10 +163,12 @@ public String toString() {
return getFullId() + "[" + sj.toString() + "]";
}
public static BlockState fromString(MinecraftVersion version, String serializedBlockState) throws IllegalArgumentException {
public static BlockState fromString(String serializedBlockState) throws IllegalArgumentException {
try {
Matcher m = BLOCKSTATE_SERIALIZATION_PATTERN.matcher(serializedBlockState);
m.find();
if (!m.find())
throw new IllegalArgumentException("'" + serializedBlockState + "' could not be parsed to a BlockState!");
Map<String, String> pt = new HashMap<>();
String g2 = m.group(2);
@ -246,7 +182,7 @@ public static BlockState fromString(MinecraftVersion version, String serializedB
String blockId = m.group(1).trim();
return new BlockState(version, blockId, pt);
return new BlockState(blockId, pt);
} catch (RuntimeException ex) {
throw new IllegalArgumentException("'" + serializedBlockState + "' could not be parsed to a BlockState!");
}

View File

@ -34,7 +34,7 @@ public interface Chunk {
LightData getLightData(int x, int y, int z, LightData target);
Biome getBiome(int x, int y, int z);
int getBiome(int x, int y, int z);
int getMaxY(int x, int z);

View File

@ -22,36 +22,43 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package de.bluecolored.bluemap.sponge;
package de.bluecolored.bluemap.core.world;
import de.bluecolored.bluemap.common.plugin.serverinterface.ServerEventListener;
import de.bluecolored.bluemap.common.plugin.text.Text;
import org.spongepowered.api.event.Listener;
import org.spongepowered.api.event.Order;
import org.spongepowered.api.event.message.MessageChannelEvent;
import org.spongepowered.api.event.network.ClientConnectionEvent;
import de.bluecolored.bluemap.core.resourcepack.ResourcePack;
public class EventForwarder {
import java.util.Objects;
private final ServerEventListener listener;
public class ResourcePackBlock<T extends ResourcePackBlock<T>> extends Block<T> {
public EventForwarder(ServerEventListener listener) {
this.listener = listener;
private final ResourcePack resourcePack;
private BlockProperties properties;
private Biome biome;
public ResourcePackBlock(ResourcePack resourcePack, World world, int x, int y, int z) {
super(world, x, y, z);
this.resourcePack = Objects.requireNonNull(resourcePack);
}
@Listener(order = Order.POST)
public void onPlayerJoin(ClientConnectionEvent.Join evt) {
listener.onPlayerJoin(evt.getTargetEntity().getUniqueId());
@Override
protected void reset() {
super.reset();
this.properties = null;
this.biome = null;
}
@Listener(order = Order.POST)
public void onPlayerLeave(ClientConnectionEvent.Disconnect evt) {
listener.onPlayerJoin(evt.getTargetEntity().getUniqueId());
public BlockProperties getProperties() {
if (properties == null) properties = resourcePack.getBlockProperties(getBlockState());
return properties;
}
@Listener(order = Order.POST)
public void onPlayerChat(MessageChannelEvent.Chat evt) {
listener.onChatMessage(Text.of(evt.getMessage().toPlain()));
public Biome getBiome() {
if (biome == null) biome = resourcePack.getBiome(getBiomeId());
return biome;
}
public ResourcePack getResourcePack() {
return resourcePack;
}
}

View File

@ -26,7 +26,6 @@
import com.flowpowered.math.vector.Vector2i;
import com.flowpowered.math.vector.Vector3i;
import de.bluecolored.bluemap.core.mca.mapping.BlockPropertiesMapper;
import java.nio.file.Path;
import java.util.ArrayList;
@ -93,22 +92,6 @@ public Grid getRegionGrid() {
return world.getRegionGrid();
}
@Override
public Biome getBiome(int x, int y, int z) {
return world.getBiome(x, y, z);
}
@Override
public BlockProperties getBlockProperties(BlockState blockState) {
return world.getBlockProperties(blockState);
}
@Override
public BlockState getBlockState(int x, int y, int z) {
if (!isInside(x, y, z)) return BlockState.AIR;
return world.getBlockState(x, y, z);
}
@Override
public Chunk getChunk(int x, int z) {
return world.getChunk(x, z);
@ -153,11 +136,6 @@ public void cleanUpChunkCache() {
world.cleanUpChunkCache();
}
@Override
public BlockPropertiesMapper getBlockPropertiesMapper() {
return world.getBlockPropertiesMapper();
}
private boolean isInside(int x, int z) {
return
x >= min.getX() &&

View File

@ -26,7 +26,6 @@
import com.flowpowered.math.vector.Vector2i;
import com.flowpowered.math.vector.Vector3i;
import de.bluecolored.bluemap.core.mca.mapping.BlockPropertiesMapper;
import java.nio.file.Path;
import java.util.Collection;
@ -57,21 +56,6 @@ public interface World {
Grid getRegionGrid();
/**
* Returns the {@link Biome} on the specified position or the default biome if the block is not generated yet.
*/
Biome getBiome(int x, int y, int z);
/**
* Returns the {@link BlockState} on the specified position or an air-block if the block is not generated yet.
*/
BlockState getBlockState(int x, int y, int z);
/**
* Returns the BlockProperties for a block-state
*/
BlockProperties getBlockProperties(BlockState blockState);
/**
* Returns the {@link Chunk} on the specified block-position
*/
@ -83,7 +67,7 @@ public interface World {
Chunk getChunk(int x, int z);
/**
* Returns the Chunk on the specified chunk-position
* Returns the {@link Region} on the specified region-position
*/
Region getRegion(int x, int z);
@ -108,9 +92,4 @@ public interface World {
*/
void cleanUpChunkCache();
/**
* Returns the block-properties manager used for this world
*/
BlockPropertiesMapper getBlockPropertiesMapper();
}

View File

@ -1,5 +0,0 @@
{
"variants": {
"": { "model": "bluemap:missing" }
}
}

View File

@ -1,6 +0,0 @@
{
"parent": "block/cube_all",
"textures": {
"all": "bluemap:blocks/missing"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

View File

@ -1,5 +0,0 @@
{
"variants": {
"": { "model": "barrier" }
}
}

View File

@ -1,12 +0,0 @@
{
"variants": {
"part=head,facing=north": { "model": "bed/black_head" },
"part=head,facing=east": { "model": "bed/black_head", "y": 90 },
"part=head,facing=south": { "model": "bed/black_head", "y": 180 },
"part=head,facing=west": { "model": "bed/black_head", "y": 270 },
"part=foot,facing=north": { "model": "bed/black_foot" },
"part=foot,facing=east": { "model": "bed/black_foot", "y": 90 },
"part=foot,facing=south": { "model": "bed/black_foot", "y": 180 },
"part=foot,facing=west": { "model": "bed/black_foot", "y": 270 }
}
}

View File

@ -1,12 +0,0 @@
{
"variants": {
"part=head,facing=north": { "model": "bed/blue_head" },
"part=head,facing=east": { "model": "bed/blue_head", "y": 90 },
"part=head,facing=south": { "model": "bed/blue_head", "y": 180 },
"part=head,facing=west": { "model": "bed/blue_head", "y": 270 },
"part=foot,facing=north": { "model": "bed/blue_foot" },
"part=foot,facing=east": { "model": "bed/blue_foot", "y": 90 },
"part=foot,facing=south": { "model": "bed/blue_foot", "y": 180 },
"part=foot,facing=west": { "model": "bed/blue_foot", "y": 270 }
}
}

View File

@ -1,12 +0,0 @@
{
"variants": {
"part=head,facing=north": { "model": "bed/brown_head" },
"part=head,facing=east": { "model": "bed/brown_head", "y": 90 },
"part=head,facing=south": { "model": "bed/brown_head", "y": 180 },
"part=head,facing=west": { "model": "bed/brown_head", "y": 270 },
"part=foot,facing=north": { "model": "bed/brown_foot" },
"part=foot,facing=east": { "model": "bed/brown_foot", "y": 90 },
"part=foot,facing=south": { "model": "bed/brown_foot", "y": 180 },
"part=foot,facing=west": { "model": "bed/brown_foot", "y": 270 }
}
}

View File

@ -1,5 +0,0 @@
{
"variants": {
"": { "model": "bubble_column" }
}
}

View File

@ -1,13 +0,0 @@
{
"variants": {
"type=single,facing=north": { "model": "chest/normal", "y": 180 },
"type=single,facing=east": { "model": "chest/normal", "y": 270 },
"type=single,facing=south": { "model": "chest/normal" },
"type=single,facing=west": { "model": "chest/normal", "y":90 },
"type=right,facing=north": { "model": "chest/normal_double", "y": 180 },
"type=right,facing=east": { "model": "chest/normal_double", "y": 270 },
"type=right,facing=south": { "model": "chest/normal_double" },
"type=right,facing=west": { "model": "chest/normal_double", "y":90 },
"type=left": { "model": "chest/left" }
}
}

View File

@ -1,12 +0,0 @@
{
"variants": {
"part=head,facing=north": { "model": "bed/cyan_head" },
"part=head,facing=east": { "model": "bed/cyan_head", "y": 90 },
"part=head,facing=south": { "model": "bed/cyan_head", "y": 180 },
"part=head,facing=west": { "model": "bed/cyan_head", "y": 270 },
"part=foot,facing=north": { "model": "bed/cyan_foot" },
"part=foot,facing=east": { "model": "bed/cyan_foot", "y": 90 },
"part=foot,facing=south": { "model": "bed/cyan_foot", "y": 180 },
"part=foot,facing=west": { "model": "bed/cyan_foot", "y": 270 }
}
}

View File

@ -1,8 +0,0 @@
{
"variants": {
"facing=north": { "model": "chest/ender", "y": 180 },
"facing=east": { "model": "chest/ender", "y": 270 },
"facing=south": { "model": "chest/ender" },
"facing=west": { "model": "chest/ender", "y":90 }
}
}

View File

@ -1,12 +0,0 @@
{
"variants": {
"part=head,facing=north": { "model": "bed/gray_head" },
"part=head,facing=east": { "model": "bed/gray_head", "y": 90 },
"part=head,facing=south": { "model": "bed/gray_head", "y": 180 },
"part=head,facing=west": { "model": "bed/gray_head", "y": 270 },
"part=foot,facing=north": { "model": "bed/gray_foot" },
"part=foot,facing=east": { "model": "bed/gray_foot", "y": 90 },
"part=foot,facing=south": { "model": "bed/gray_foot", "y": 180 },
"part=foot,facing=west": { "model": "bed/gray_foot", "y": 270 }
}
}

View File

@ -1,12 +0,0 @@
{
"variants": {
"part=head,facing=north": { "model": "bed/green_head" },
"part=head,facing=east": { "model": "bed/green_head", "y": 90 },
"part=head,facing=south": { "model": "bed/green_head", "y": 180 },
"part=head,facing=west": { "model": "bed/green_head", "y": 270 },
"part=foot,facing=north": { "model": "bed/green_foot" },
"part=foot,facing=east": { "model": "bed/green_foot", "y": 90 },
"part=foot,facing=south": { "model": "bed/green_foot", "y": 180 },
"part=foot,facing=west": { "model": "bed/green_foot", "y": 270 }
}
}

View File

@ -1,5 +0,0 @@
{
"variants": {
"": { "model": "lava" }
}
}

View File

@ -1,12 +0,0 @@
{
"variants": {
"part=head,facing=north": { "model": "bed/light_blue_head" },
"part=head,facing=east": { "model": "bed/light_blue_head", "y": 90 },
"part=head,facing=south": { "model": "bed/light_blue_head", "y": 180 },
"part=head,facing=west": { "model": "bed/light_blue_head", "y": 270 },
"part=foot,facing=north": { "model": "bed/light_blue_foot" },
"part=foot,facing=east": { "model": "bed/light_blue_foot", "y": 90 },
"part=foot,facing=south": { "model": "bed/light_blue_foot", "y": 180 },
"part=foot,facing=west": { "model": "bed/light_blue_foot", "y": 270 }
}
}

View File

@ -1,12 +0,0 @@
{
"variants": {
"part=head,facing=north": { "model": "bed/light_gray_head" },
"part=head,facing=east": { "model": "bed/light_gray_head", "y": 90 },
"part=head,facing=south": { "model": "bed/light_gray_head", "y": 180 },
"part=head,facing=west": { "model": "bed/light_gray_head", "y": 270 },
"part=foot,facing=north": { "model": "bed/light_gray_foot" },
"part=foot,facing=east": { "model": "bed/light_gray_foot", "y": 90 },
"part=foot,facing=south": { "model": "bed/light_gray_foot", "y": 180 },
"part=foot,facing=west": { "model": "bed/light_gray_foot", "y": 270 }
}
}

View File

@ -1,12 +0,0 @@
{
"variants": {
"part=head,facing=north": { "model": "bed/lime_head" },
"part=head,facing=east": { "model": "bed/lime_head", "y": 90 },
"part=head,facing=south": { "model": "bed/lime_head", "y": 180 },
"part=head,facing=west": { "model": "bed/lime_head", "y": 270 },
"part=foot,facing=north": { "model": "bed/lime_foot" },
"part=foot,facing=east": { "model": "bed/lime_foot", "y": 90 },
"part=foot,facing=south": { "model": "bed/lime_foot", "y": 180 },
"part=foot,facing=west": { "model": "bed/lime_foot", "y": 270 }
}
}

View File

@ -1,12 +0,0 @@
{
"variants": {
"part=head,facing=north": { "model": "bed/magenta_head" },
"part=head,facing=east": { "model": "bed/magenta_head", "y": 90 },
"part=head,facing=south": { "model": "bed/magenta_head", "y": 180 },
"part=head,facing=west": { "model": "bed/magenta_head", "y": 270 },
"part=foot,facing=north": { "model": "bed/magenta_foot" },
"part=foot,facing=east": { "model": "bed/magenta_foot", "y": 90 },
"part=foot,facing=south": { "model": "bed/magenta_foot", "y": 180 },
"part=foot,facing=west": { "model": "bed/magenta_foot", "y": 270 }
}
}

View File

@ -1,20 +0,0 @@
{
"variants": {
"rotation=0": { "model": "sign/oak" },
"rotation=1": { "model": "sign/oak", "y": 22.5 },
"rotation=2": { "model": "sign/oak", "y": 45 },
"rotation=3": { "model": "sign/oak", "y": 67.5 },
"rotation=4": { "model": "sign/oak", "y": 90 },
"rotation=5": { "model": "sign/oak", "y": 112.5 },
"rotation=6": { "model": "sign/oak", "y": 135 },
"rotation=7": { "model": "sign/oak", "y": 157.5 },
"rotation=8": { "model": "sign/oak", "y": 180 },
"rotation=9": { "model": "sign/oak", "y": 202.5 },
"rotation=10": { "model": "sign/oak", "y": 225 },
"rotation=11": { "model": "sign/oak", "y": 247.5 },
"rotation=12": { "model": "sign/oak", "y": 270 },
"rotation=13": { "model": "sign/oak", "y": 292.5 },
"rotation=14": { "model": "sign/oak", "y": 315 },
"rotation=15": { "model": "sign/oak", "y": 337.5 }
}
}

View File

@ -1,8 +0,0 @@
{
"variants": {
"facing=south": { "model": "sign/wall_oak" },
"facing=west": { "model": "sign/wall_oak", "y": 90 },
"facing=north": { "model": "sign/wall_oak", "y": 180 },
"facing=east": { "model": "sign/wall_oak", "y": 270 }
}
}

View File

@ -1,12 +0,0 @@
{
"variants": {
"part=head,facing=north": { "model": "bed/orange_head" },
"part=head,facing=east": { "model": "bed/orange_head", "y": 90 },
"part=head,facing=south": { "model": "bed/orange_head", "y": 180 },
"part=head,facing=west": { "model": "bed/orange_head", "y": 270 },
"part=foot,facing=north": { "model": "bed/orange_foot" },
"part=foot,facing=east": { "model": "bed/orange_foot", "y": 90 },
"part=foot,facing=south": { "model": "bed/orange_foot", "y": 180 },
"part=foot,facing=west": { "model": "bed/orange_foot", "y": 270 }
}
}

View File

@ -1,12 +0,0 @@
{
"variants": {
"part=head,facing=north": { "model": "bed/pink_head" },
"part=head,facing=east": { "model": "bed/pink_head", "y": 90 },
"part=head,facing=south": { "model": "bed/pink_head", "y": 180 },
"part=head,facing=west": { "model": "bed/pink_head", "y": 270 },
"part=foot,facing=north": { "model": "bed/pink_foot" },
"part=foot,facing=east": { "model": "bed/pink_foot", "y": 90 },
"part=foot,facing=south": { "model": "bed/pink_foot", "y": 180 },
"part=foot,facing=west": { "model": "bed/pink_foot", "y": 270 }
}
}

View File

@ -1,12 +0,0 @@
{
"variants": {
"part=head,facing=north": { "model": "bed/purple_head" },
"part=head,facing=east": { "model": "bed/purple_head", "y": 90 },
"part=head,facing=south": { "model": "bed/purple_head", "y": 180 },
"part=head,facing=west": { "model": "bed/purple_head", "y": 270 },
"part=foot,facing=north": { "model": "bed/purple_foot" },
"part=foot,facing=east": { "model": "bed/purple_foot", "y": 90 },
"part=foot,facing=south": { "model": "bed/purple_foot", "y": 180 },
"part=foot,facing=west": { "model": "bed/purple_foot", "y": 270 }
}
}

View File

@ -1,12 +0,0 @@
{
"variants": {
"part=head,facing=north": { "model": "bed/red_head" },
"part=head,facing=east": { "model": "bed/red_head", "y": 90 },
"part=head,facing=south": { "model": "bed/red_head", "y": 180 },
"part=head,facing=west": { "model": "bed/red_head", "y": 270 },
"part=foot,facing=north": { "model": "bed/red_foot" },
"part=foot,facing=east": { "model": "bed/red_foot", "y": 90 },
"part=foot,facing=south": { "model": "bed/red_foot", "y": 180 },
"part=foot,facing=west": { "model": "bed/red_foot", "y": 270 }
}
}

View File

@ -1,13 +0,0 @@
{
"variants": {
"type=single,facing=north": { "model": "chest/trapped", "y": 180 },
"type=single,facing=east": { "model": "chest/trapped", "y": 270 },
"type=single,facing=south": { "model": "chest/trapped" },
"type=single,facing=west": { "model": "chest/trapped", "y":90 },
"type=right,facing=north": { "model": "chest/trapped_double", "y": 180 },
"type=right,facing=east": { "model": "chest/trapped_double", "y": 270 },
"type=right,facing=south": { "model": "chest/trapped_double" },
"type=right,facing=west": { "model": "chest/trapped_double", "y":90 },
"type=left": { "model": "chest/left" }
}
}

View File

@ -1,5 +0,0 @@
{
"variants": {
"": { "model": "water" }
}
}

View File

@ -1,12 +0,0 @@
{
"variants": {
"part=head,facing=north": { "model": "bed/white_head" },
"part=head,facing=east": { "model": "bed/white_head", "y": 90 },
"part=head,facing=south": { "model": "bed/white_head", "y": 180 },
"part=head,facing=west": { "model": "bed/white_head", "y": 270 },
"part=foot,facing=north": { "model": "bed/white_foot" },
"part=foot,facing=east": { "model": "bed/white_foot", "y": 90 },
"part=foot,facing=south": { "model": "bed/white_foot", "y": 180 },
"part=foot,facing=west": { "model": "bed/white_foot", "y": 270 }
}
}

View File

@ -1,12 +0,0 @@
{
"variants": {
"part=head,facing=north": { "model": "bed/yellow_head" },
"part=head,facing=east": { "model": "bed/yellow_head", "y": 90 },
"part=head,facing=south": { "model": "bed/yellow_head", "y": 180 },
"part=head,facing=west": { "model": "bed/yellow_head", "y": 270 },
"part=foot,facing=north": { "model": "bed/yellow_foot" },
"part=foot,facing=east": { "model": "bed/yellow_foot", "y": 90 },
"part=foot,facing=south": { "model": "bed/yellow_foot", "y": 180 },
"part=foot,facing=west": { "model": "bed/yellow_foot", "y": 270 }
}
}

View File

@ -1,39 +0,0 @@
{
"elements": [
{
"from": [0, 0, 13],
"to": [3, 3, 16],
"faces": {
"north": {"uv": [14.75, 0.75, 15.5, 1.5], "texture": "#bed"},
"east": {"uv": [14, 0.75, 14.75, 1.5], "texture": "#bed"},
"south": {"uv": [13.25, 0.75, 14, 1.5], "texture": "#bed"},
"west": {"uv": [12.5, 0.75, 13.25, 1.5], "texture": "#bed"},
"up": {"uv": [13.25, 0, 14, 0.75], "texture": "#bed"},
"down": {"uv": [14, 0, 14.75, 0.75], "texture": "#bed"}
}
},
{
"from": [13, 0, 13],
"to": [16, 3, 16],
"faces": {
"north": {"uv": [14, 3.75, 14.75, 4.5], "texture": "#bed"},
"east": {"uv": [13.25, 3.75, 14, 4.5], "texture": "#bed"},
"south": {"uv": [12.5, 3.75, 13.25, 4.5], "texture": "#bed"},
"west": {"uv": [14.75, 3.75, 15.5, 4.5], "texture": "#bed"},
"up": {"uv": [13.25, 3, 14, 3.75], "texture": "#bed"},
"down": {"uv": [14, 3, 14.75, 3.75], "texture": "#bed"}
}
},
{
"from": [0, 3, 0],
"to": [16, 9, 16],
"faces": {
"east": {"uv": [5.5, 7, 7, 11], "rotation": 90, "texture": "#bed"},
"south": {"uv": [5.5, 7, 9.5, 5.5], "texture": "#bed"},
"west": {"uv": [0, 7, 1.5, 11], "rotation": 270, "texture": "#bed"},
"up": {"uv": [1.5, 7, 5.5, 11], "texture": "#bed"},
"down": {"uv": [7, 7, 11, 11], "rotation": 180, "texture": "#bed"}
}
}
]
}

View File

@ -1,39 +0,0 @@
{
"elements": [
{
"from": [0, 0, 0],
"to": [3, 3, 3],
"faces": {
"north": {"uv": [12.5, 2.25, 13.25, 3], "texture": "#bed"},
"east": {"uv": [14.75, 2.25, 15.5, 3], "texture": "#bed"},
"south": {"uv": [14, 2.25, 14.75, 3], "texture": "#bed"},
"west": {"uv": [13.25, 2.25, 14, 3], "texture": "#bed"},
"up": {"uv": [13.25, 1.5, 14, 2.25], "texture": "#bed"},
"down": {"uv": [14, 1.5, 14.75, 2.25], "texture": "#bed"}
}
},
{
"from": [13, 0, 0],
"to": [16, 3, 3],
"faces": {
"north": {"uv": [13.25, 5.25, 14, 6], "texture": "#bed"},
"east": {"uv": [12.5, 5.25, 13.25, 6], "texture": "#bed"},
"south": {"uv": [14.75, 5.25, 15.5, 6], "texture": "#bed"},
"west": {"uv": [14, 5.25, 14.75, 6], "texture": "#bed"},
"up": {"uv": [13.25, 4.5, 14, 5.25], "texture": "#bed"},
"down": {"uv": [14, 4.5, 14.75, 5.25], "texture": "#bed"}
}
},
{
"from": [0, 3, 0],
"to": [16, 9, 16],
"faces": {
"north": {"uv": [1.5, 1.5, 5.5, 0], "texture": "#bed"},
"east": {"uv": [5.5, 1.5, 7, 5.5], "rotation": 90, "texture": "#bed"},
"west": {"uv": [0, 1.5, 1.5, 5.5], "rotation": 270, "texture": "#bed"},
"up": {"uv": [1.5, 1.5, 5.5, 5.5], "texture": "#bed"},
"down": {"uv": [7, 1.5, 11, 5.5], "rotation": 180, "texture": "#bed"}
}
}
]
}

View File

@ -1,6 +0,0 @@
{
"parent":"block/bed/bed_foot",
"textures": {
"bed": "entity/bed/black"
}
}

View File

@ -1,6 +0,0 @@
{
"parent":"block/bed/bed_head",
"textures": {
"bed": "entity/bed/black"
}
}

View File

@ -1,6 +0,0 @@
{
"parent":"block/bed/bed_foot",
"textures": {
"bed": "entity/bed/blue"
}
}

View File

@ -1,6 +0,0 @@
{
"parent":"block/bed/bed_head",
"textures": {
"bed": "entity/bed/blue"
}
}

View File

@ -1,6 +0,0 @@
{
"parent":"block/bed/bed_foot",
"textures": {
"bed": "entity/bed/brown"
}
}

View File

@ -1,6 +0,0 @@
{
"parent":"block/bed/bed_head",
"textures": {
"bed": "entity/bed/brown"
}
}

Some files were not shown because too many files have changed in this diff Show More