another new block loading system

This commit is contained in:
Lukas 2020-10-17 17:34:20 +02:00
parent 51e6b4a7f2
commit 3e34ec040c
27 changed files with 574 additions and 6080 deletions

View File

@ -49,6 +49,30 @@ public class Block {
this.rotation = BlockRotations.NONE;
}
public Block(String mod, String identifier, String properties) {
this.mod = mod;
this.identifier = identifier;
this.properties = new HashSet<>();
BlockRotations rot = BlockRotations.NONE;
for (String part : properties.split(",")) {
if (part.equals("")) {
continue;
}
String[] subParts = part.split("=");
if (!(subParts.length == 2)) {
throw new IllegalArgumentException("too many or few = in " + part);
}
String key = subParts[0];
String value = subParts[1];
if (Blocks.getPropertiesMapping().containsKey(key)) {
this.properties.add(Blocks.getPropertiesMapping().get(key).get(value));
} else if (Blocks.getRotationMapping().containsKey(key)) {
rot = Blocks.getRotationMapping().get(value);
}
}
rotation = rot;
}
public String getMod() {
return mod;
}

View File

@ -1,44 +0,0 @@
/*
* Codename Minosoft
* Copyright (C) 2020 Lukas Eisenhauer
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.render;
import de.bixilon.minosoft.render.blockModels.BlockModelLoader;
import de.bixilon.minosoft.render.entityModels.EntityModelLoader;
import de.bixilon.minosoft.render.texture.TextureLoader;
public class AssetsLoader {
TextureLoader textureLoader;
BlockModelLoader blockModelLoader;
EntityModelLoader entityModelLoader;
public AssetsLoader() {
blockModelLoader = new BlockModelLoader();
entityModelLoader = new EntityModelLoader();
textureLoader = new TextureLoader(blockModelLoader.getTextures(), blockModelLoader.getTints());
blockModelLoader.applyTextures(textureLoader);
}
public TextureLoader getTextureLoader() {
return textureLoader;
}
public BlockModelLoader getBlockModelLoader() {
return blockModelLoader;
}
public EntityModelLoader getEntityModelLoader() {
return entityModelLoader;
}
}

View File

@ -16,6 +16,7 @@ package de.bixilon.minosoft.render;
import de.bixilon.minosoft.data.mappings.blocks.Block;
import de.bixilon.minosoft.data.world.*;
import de.bixilon.minosoft.protocol.network.Connection;
import de.bixilon.minosoft.render.blockModels.BlockModelLoader;
import de.bixilon.minosoft.render.blockModels.Face.FaceOrientation;
import de.bixilon.minosoft.render.blockModels.Face.RenderConstants;
import org.apache.commons.collections.primitives.ArrayFloatList;
@ -29,7 +30,7 @@ import static org.lwjgl.opengl.GL11.*;
public class WorldRenderer {
private final ConcurrentHashMap<ChunkLocation, ConcurrentHashMap<Byte, ArrayFloatList>> faces;
private AssetsLoader assetsLoader;
private BlockModelLoader modelLoader;
private LinkedBlockingQueue<Runnable> queuedMapData;
@ -39,7 +40,7 @@ public class WorldRenderer {
public void init() {
queuedMapData = new LinkedBlockingQueue<>();
assetsLoader = new AssetsLoader();
modelLoader = new BlockModelLoader();
}
public void startChunkPreparation(Connection connection) {
@ -167,12 +168,12 @@ public class WorldRenderer {
yield nibbleBlocks.get(new ChunkNibbleLocation(location.getX(), location.getY(), location.getZ() + 1));
}
};
if (dependedBlock == null || !assetsLoader.getBlockModelLoader().isFull(dependedBlock)) {
if (dependedBlock == null || modelLoader.isFull(dependedBlock, FaceOrientation.inverse(orientation))) {
facesToDraw.add(orientation);
}
}
if (!facesToDraw.isEmpty()) {
nibbleMap.addAll(assetsLoader.getBlockModelLoader().prepare(block, facesToDraw, new BlockPosition(chunkLocation, sectionHeight, location)));
nibbleMap.addAll(modelLoader.prepare(block, facesToDraw, new BlockPosition(chunkLocation, sectionHeight, location)));
}
});
return nibbleMap;
@ -181,7 +182,7 @@ public class WorldRenderer {
public void draw() {
glPushMatrix();
glBindTexture(GL_TEXTURE_2D, assetsLoader.getTextureLoader().getTextureID());
glBindTexture(GL_TEXTURE_2D, modelLoader.getTextureLoader().getTextureID());
glBegin(GL_QUADS);
for (ConcurrentHashMap<Byte, ArrayFloatList> chunk : faces.values()) {
for (ArrayFloatList nibble : chunk.values()) {
@ -195,10 +196,6 @@ public class WorldRenderer {
glEnd();
}
public AssetsLoader getAssetsLoader() {
return assetsLoader;
}
private ChunkNibble getChunkNibbleOfWorld(ConcurrentHashMap<ChunkLocation, Chunk> world, ChunkLocation location, byte sectionHeight) {
if (world.containsKey(location) && world.get(location).getNibbles().containsKey(sectionHeight)) {
return world.get(location).getNibbles().get(sectionHeight);
@ -206,4 +203,7 @@ public class WorldRenderer {
return null;
}
public BlockModelLoader getBlockModelLoader() {
return modelLoader;
}
}

View File

@ -1,83 +0,0 @@
/*
* Codename Minosoft
* Copyright (C) 2020 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.render.blockModels;
import com.google.gson.JsonObject;
import de.bixilon.minosoft.data.mappings.blocks.Block;
import de.bixilon.minosoft.data.mappings.blocks.BlockProperties;
import de.bixilon.minosoft.data.mappings.blocks.BlockRotations;
import de.bixilon.minosoft.data.mappings.blocks.Blocks;
import java.util.HashMap;
import java.util.HashSet;
public class BlockConfiguration {
private BlockRotations rotation;
private HashSet<BlockProperties> blockProperties;
public BlockConfiguration(JsonObject json) {
if (json.has("facing")) {
rotation = Blocks.getRotationMapping().get(json.get("facing").getAsString());
json.remove("facing");
}
if (json.has("rotation")) {
rotation = Blocks.getRotationMapping().get(json.get("rotation").getAsString());
json.remove("rotation");
}
if (json.has("axis")) {
rotation = Blocks.getRotationMapping().get(json.get("axis").getAsString());
json.remove("axis");
}
blockProperties = new HashSet<>();
for (String propertyName : json.keySet()) {
HashMap<String, BlockProperties> properties = Blocks.getPropertiesMapping().get(propertyName);
if (properties == null) {
throw new RuntimeException(String.format("Unknown block property: %s", propertyName));
}
BlockProperties property = properties.get(json.get(propertyName).getAsString());
if (property == null) {
throw new RuntimeException(String.format("Unknown block property: %s -> %s", propertyName, json.get(propertyName).getAsString()));
}
blockProperties.add(property);
}
}
public BlockConfiguration() {
}
public BlockRotations getRotation() {
return rotation;
}
public HashSet<BlockProperties> getBlockProperties() {
return blockProperties;
}
public boolean equals(BlockConfiguration blockConfiguration) {
return rotation.equals(blockConfiguration.getRotation()) && blockProperties.equals(blockConfiguration.getBlockProperties());
}
public boolean contains(Block block) {
if (rotation != null && block.getRotation() != rotation) {
return false;
}
for (BlockProperties property : blockProperties) {
if (!block.getProperties().contains(property)) {
return false;
}
}
return true;
}
}

View File

@ -1,23 +0,0 @@
/*
* Codename Minosoft
* Copyright (C) 2020 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.render.blockModels;
import de.bixilon.minosoft.data.mappings.blocks.Block;
public class BlockConfigurationTrue extends BlockConfiguration {
@Override
public boolean contains(Block block) {
return true;
}
}

View File

@ -0,0 +1,153 @@
/*
* Codename Minosoft
* Copyright (C) 2020 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.render.blockModels;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import de.bixilon.minosoft.data.world.BlockPosition;
import de.bixilon.minosoft.render.blockModels.Face.Axis;
import de.bixilon.minosoft.render.blockModels.Face.FaceOrientation;
import de.bixilon.minosoft.render.blockModels.subBlocks.SubBlock;
import de.bixilon.minosoft.render.texture.TextureLoader;
import org.apache.commons.collections.primitives.ArrayFloatList;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
public class BlockModel {
private final ArrayList<SubBlock> subBlocks;
private final boolean[] full; // minor memory improvement over a Map
public BlockModel(JsonObject block, JsonObject allModels) {
subBlocks = load(block, allModels);
full = new boolean[]{true, true, true, true, true, true};
}
public BlockModel(BlockModel blockModel, JsonObject json) {
if (blockModel != null) {
subBlocks = blockModel.getSubBlocks();
} else {
subBlocks = new ArrayList<>();
}
if (json.has("x")) {
for (SubBlock subBlock : subBlocks) {
subBlock.rotate(Axis.X, json.get("x").getAsInt());
}
}
if (json.has("y")) {
for (SubBlock subBlock : subBlocks) {
subBlock.rotate(Axis.X, json.get("y").getAsInt());
}
}
if (json.has("z")) {
for (SubBlock subBlock : subBlocks) {
subBlock.rotate(Axis.X, json.get("z").getAsInt());
}
}
full = createFullValues();
}
public BlockModel(ArrayList<BlockModel> models) {
subBlocks = new ArrayList<>();
for (BlockModel model : models) {
subBlocks.addAll(model.getSubBlocks());
}
full = createFullValues();
}
static ArrayList<SubBlock> load(JsonObject json, JsonObject allModels, HashMap<String, String> variables) {
ArrayList<SubBlock> result = new ArrayList<>();
if (json.has("textures")) {
// read the textures into a variable hashmap
JsonObject textures = json.getAsJsonObject("textures");
for (String texture : textures.keySet()) {
if (texture.contains("#") && variables.containsKey(texture)) {
variables.put("#" + texture, variables.get(texture));
} else {
variables.put("#" + texture, textures.get(texture).getAsString());
}
}
}
if (json.has("elements")) {
for (JsonElement subBlockJson : json.get("elements").getAsJsonArray()) {
result.add(new SubBlock(subBlockJson.getAsJsonObject(), variables));
}
} else if (json.has("parent") && !json.get("parent").getAsString().equals("block/block")) {
String parent = json.get("parent").getAsString();
if (parent.equals("block/block")) {
return result;
}
String parentIdentifier = parent.substring(parent.lastIndexOf("/") + 1);
result.addAll(load(allModels.get(parentIdentifier).getAsJsonObject(), allModels, variables));
}
return result;
}
static ArrayList<SubBlock> load(JsonObject json, JsonObject allModels) {
return load(json, allModels, new HashMap<>());
}
private boolean[] createFullValues() {
boolean[] result = new boolean[6];
outer:
for (FaceOrientation orientation : FaceOrientation.values()) {
for (SubBlock subBlock : subBlocks) {
if (subBlock.getFull()[orientation.getId()]) {
result[orientation.getId()] = true;
continue outer;
}
}
}
return result;
}
public boolean isFull(FaceOrientation orientation) {
return full[orientation.getId()];
}
public ArrayFloatList prepare(HashSet<FaceOrientation> facesToDraw, BlockPosition position) {
ArrayFloatList result = new ArrayFloatList();
for (SubBlock subBlock : subBlocks) {
result.addAll(subBlock.getFaces(facesToDraw, position));
}
return result;
}
public boolean isFull() {
return true;
}
public HashSet<String> getAllTextures() {
HashSet<String> result = new HashSet<>();
for (SubBlock subBlock : subBlocks) {
result.addAll(subBlock.getTextures());
}
return result;
}
public void applyTextures(String mod, TextureLoader loader) {
for (SubBlock subBlock : subBlocks) {
subBlock.applyTextures(mod, loader);
}
}
public ArrayList<SubBlock> getSubBlocks() {
return subBlocks;
}
public boolean[] getFull() {
return full;
}
}

View File

@ -0,0 +1,52 @@
/*
* Codename Minosoft
* Copyright (C) 2020 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.render.blockModels;
import de.bixilon.minosoft.data.mappings.blocks.Block;
import de.bixilon.minosoft.data.mappings.blocks.BlockRotations;
public class BlockModelBlockWrapper {
Block block;
public BlockModelBlockWrapper(Block block) {
this.block = block;
}
public Block getBlock() {
return block;
}
@Override
public int hashCode() {
return block.getMod().hashCode() * block.getIdentifier().hashCode();
}
@Override
public boolean equals(Object obj) {
if (super.equals(obj)) {
return true;
}
BlockModelBlockWrapper their = (BlockModelBlockWrapper) obj;
if (block.equals(their.getBlock())) {
return true;
}
if (!(block.getMod().equals(their.getBlock().getMod()) && block.getIdentifier().equals(their.getBlock().getIdentifier()))) {
return false;
}
if (block.getRotation() == BlockRotations.NONE || their.getBlock().getRotation() == BlockRotations.NONE || block.getProperties().size() == 0 || their.getBlock().getProperties().size() == 0) {
return true;
}
return block.getProperties().equals(their.getBlock().getProperties()) && block.getMod().equals(their.getBlock().getMod());
}
}

View File

@ -1,105 +0,0 @@
/*
* Codename Minosoft
* Copyright (C) 2020 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.render.blockModels;
import com.google.common.collect.HashBiMap;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import de.bixilon.minosoft.Config;
import de.bixilon.minosoft.data.mappings.blocks.Block;
import de.bixilon.minosoft.data.mappings.blocks.BlockRotations;
import de.bixilon.minosoft.data.world.BlockPosition;
import de.bixilon.minosoft.logging.Log;
import de.bixilon.minosoft.render.blockModels.Face.FaceOrientation;
import de.bixilon.minosoft.render.blockModels.subBlocks.SubBlock;
import de.bixilon.minosoft.render.texture.TextureLoader;
import de.bixilon.minosoft.util.Util;
import org.apache.commons.collections.primitives.ArrayFloatList;
import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
public interface BlockModelInterface {
HashBiMap<BlockRotations, BlockRotations> rotationAdjust = HashBiMap.create(Map.of(BlockRotations.EAST, BlockRotations.SOUTH, BlockRotations.SOUTH, BlockRotations.WEST, BlockRotations.WEST, BlockRotations.NORTH, BlockRotations.NORTH, BlockRotations.EAST));
static void applyConfigurationTextures(HashSet<SubBlock> subBlocks, String mod, TextureLoader loader) {
for (SubBlock subBlock : subBlocks) {
subBlock.applyTextures(mod, loader);
}
}
static ArrayFloatList prepareState(HashSet<SubBlock> subBlocks, BlockRotations rotation, HashSet<FaceOrientation> facesToDraw, BlockPosition position) {
ArrayFloatList result = new ArrayFloatList();
for (SubBlock subBlock : subBlocks) {
result.addAll(subBlock.getFaces(new Block("", "", rotation), facesToDraw, position));
}
return result;
}
static HashSet<String> getTextures(HashSet<SubBlock> subBlocks) {
HashSet<String> result = new HashSet<>();
for (SubBlock subBlock : subBlocks) {
result.addAll(subBlock.getTextures());
}
return result;
}
static HashSet<SubBlock> load(String mod, String identifier, HashMap<String, String> variables) {
String path = Config.homeDir + "assets/" + mod + "/models/block/" + identifier + ".json";
JsonObject json = null;
try {
json = Util.readJsonFromFile(path);
} catch (IOException e) {
e.printStackTrace();
Log.warn("File not found: " + path);
return null;
}
HashSet<SubBlock> result = new HashSet<>();
if (json.has("textures")) {
// read the textures into a variable hashmap
JsonObject textures = json.getAsJsonObject("textures");
for (String texture : textures.keySet()) {
if (texture.contains("#") && variables.containsKey(texture)) {
variables.put("#" + texture, variables.get(texture));
} else {
variables.put("#" + texture, textures.get(texture).getAsString());
}
}
}
if (json.has("elements")) {
for (JsonElement subBlockJson : json.get("elements").getAsJsonArray()) {
result.add(new SubBlock(subBlockJson.getAsJsonObject(), variables));
}
} else if (json.has("parent") && !json.get("parent").getAsString().equals("block/block")) {
String parent = json.get("parent").getAsString();
String parentIdentifier = parent.substring(parent.lastIndexOf("/") + 1);
result.addAll(load(mod, parentIdentifier, variables));
}
return result;
}
static HashSet<SubBlock> load(String mod, String identifier) {
return load(mod, identifier, new HashMap<>());
}
ArrayFloatList prepare(Block block, HashSet<FaceOrientation> facesToDraw, BlockPosition position);
boolean isFull();
HashSet<String> getAllTextures();
void applyTextures(String mod, TextureLoader loader);
}

View File

@ -19,47 +19,89 @@ import de.bixilon.minosoft.data.mappings.blocks.Block;
import de.bixilon.minosoft.data.mappings.blocks.Blocks;
import de.bixilon.minosoft.data.world.BlockPosition;
import de.bixilon.minosoft.render.blockModels.Face.FaceOrientation;
import de.bixilon.minosoft.render.blockModels.specialModels.*;
import de.bixilon.minosoft.render.texture.TextureLoader;
import de.bixilon.minosoft.util.Util;
import org.apache.commons.collections.primitives.ArrayFloatList;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
public class BlockModelLoader {
private final HashMap<String, HashMap<String, BlockModelInterface>> blockDescriptionMap;
private final HashMap<String, HashSet<String>> textures;
HashMap<String, HashMap<String, float[]>> tints;
HashMap<BlockModelBlockWrapper, BlockModel> blockMap;
TextureLoader loader;
public BlockModelLoader() {
blockDescriptionMap = new HashMap<>();
tints = new HashMap<>();
textures = new HashMap<>();
blockMap = new HashMap<>();
HashMap<String, JsonObject> mods = new HashMap<>();
HashMap<String, HashMap<String, float[]>> tints = new HashMap<>();
try {
JsonObject json = Util.readJsonAsset("mapping/blockModels.json");
String mod = "minecraft";
tints.put(mod, readTints(json));
textures.put(mod, loadModels(json.get("blocks").getAsJsonObject(), mod));
//TODO: modding
mods.put("minecraft", Util.readJsonAsset("mapping/blockModels.json"));
} catch (IOException e) {
e.printStackTrace();
}
HashMap<String, HashMap<String, BlockModel>> blockModels = new HashMap<>();
for (Map.Entry<String, JsonObject> mod : mods.entrySet()) {
blockModels.put(mod.getKey(), loadModels(mod));
tints.put(mod.getKey(), readTints(mod.getValue()));
}
loader = new TextureLoader(getTextures(blockModels), tints);
applyTextures(blockModels);
for (Map.Entry<String, JsonObject> mod : mods.entrySet()) {
loadBlocks(mod, blockModels.get(mod.getKey()));
}
}
public HashMap<String, HashMap<String, float[]>> getTints() {
return tints;
private void loadBlocks(Map.Entry<String, JsonObject> mod, HashMap<String, BlockModel> blockModels) {
for (Map.Entry<String, JsonElement> blockEntry : mod.getValue().get("blockStates").getAsJsonObject().entrySet()) {
JsonObject block = blockEntry.getValue().getAsJsonObject();
if (block.has("variants")) {
JsonObject variants = block.get("variants").getAsJsonObject();
for (Map.Entry<String, JsonElement> variant : variants.entrySet()) {
if (variant.getValue().isJsonArray()) {
ArrayList<BlockModel> models = new ArrayList<>();
for (JsonElement model : variant.getValue().getAsJsonArray()) {
models.add(new BlockModel(blockModels.get(blockEntry.getKey()), model.getAsJsonObject()));
}
blockMap.put(new BlockModelBlockWrapper(new Block(mod.getKey(), blockEntry.getKey(), variant.getKey())), new BlockModel(models));
} else {
String fullModel = variant.getValue().getAsJsonObject().get("model").getAsString();
String modelName = fullModel.substring(fullModel.indexOf("/") + 1);
BlockModel model = blockModels.get(modelName);
blockMap.put(new BlockModelBlockWrapper(new Block(mod.getKey(), blockEntry.getKey(), variant.getKey())), new BlockModel(model, variant.getValue().getAsJsonObject()));
}
}
}
}
}
public HashMap<String, HashSet<String>> getTextures() {
private HashMap<String, BlockModel> loadModels(Map.Entry<String, JsonObject> mod) {
HashMap<String, BlockModel> modMap = new HashMap<>();
for (Map.Entry<String, JsonElement> block : mod.getValue().get("blockModels").getAsJsonObject().entrySet()) {
modMap.put(block.getKey(), new BlockModel(block.getValue().getAsJsonObject(), mod.getValue().get("blockModels").getAsJsonObject()));
}
return modMap;
}
public HashMap<String, HashSet<String>> getTextures(HashMap<String, HashMap<String, BlockModel>> blockModels) {
HashMap<String, HashSet<String>> textures = new HashMap<>();
for (Map.Entry<String, HashMap<String, BlockModel>> mod : blockModels.entrySet()) {
HashSet<String> modTextures = new HashSet<>();
for (BlockModel blockModel : mod.getValue().values()) {
modTextures.addAll(blockModel.getAllTextures());
}
textures.put(mod.getKey(), modTextures);
}
return textures;
}
public void applyTextures(TextureLoader loader) {
for (Map.Entry<String, HashMap<String, BlockModelInterface>> mod : blockDescriptionMap.entrySet()) {
for (Map.Entry<String, BlockModelInterface> block : mod.getValue().entrySet()) {
public void applyTextures(HashMap<String, HashMap<String, BlockModel>> blockModels) {
for (Map.Entry<String, HashMap<String, BlockModel>> mod : blockModels.entrySet()) {
for (Map.Entry<String, BlockModel> block : mod.getValue().entrySet()) {
block.getValue().applyTextures(mod.getKey(), loader);
}
}
@ -81,57 +123,30 @@ public class BlockModelLoader {
return result;
}
private HashSet<String> loadModels(JsonObject blockList, String mod) {
HashSet<String> result = new HashSet<>();
blockDescriptionMap.put(mod, new HashMap<>());
for (String identifier : blockList.keySet()) {
JsonObject block = blockList.get(identifier).getAsJsonObject();
result.addAll(loadModel(mod, identifier, block));
public BlockModel getBlockModel(Block block) {
BlockModel model = blockMap.get(new BlockModelBlockWrapper(block));
if (model == null) {
throw new RuntimeException("block " + block + " could not be found");
}
return result;
return blockMap.get(new BlockModelBlockWrapper(block));
}
private HashSet<String> loadModel(String mod, String identifier, JsonObject block) {
HashSet<String> result = new HashSet<>();
try {
String type = "";
if (block.has("type")) {
type = block.get("type").getAsString();
}
BlockModelInterface model = switch (type) {
case "fire" -> new FireModel(block, mod);
case "stairs" -> new StairsModel(block, mod);
case "wire" -> new WireModel(block, mod);
case "crop" -> new CropModel(block, mod);
case "door" -> new DoorModel(block, mod);
case "fence" -> new FenceModel(block, mod);
case "mushroom" -> new MushroomModel(block, mod);
default -> new BlockModel(block, mod);
};
result.addAll(model.getAllTextures());
HashMap<String, BlockModelInterface> modMap = blockDescriptionMap.get(mod);
modMap.put(identifier, model);
} catch (Exception e) {
e.printStackTrace();
System.out.println(mod + ":" + identifier);
System.exit(-1);
}
return result;
}
public BlockModelInterface getBlockDescription(Block block) {
HashMap<String, BlockModelInterface> modList = blockDescriptionMap.get(block.getMod());
return modList.get(block.getIdentifier());
}
public boolean isFull(Block block) {
public boolean isFull(Block block, FaceOrientation orientation) {
if (block == null || block.equals(Blocks.nullBlock)) {
return false;
}
return getBlockDescription(block).isFull();
return getBlockModel(block).isFull(orientation);
}
public boolean isFull(Block block) {
return block != null && !block.equals(Blocks.nullBlock);
}
public ArrayFloatList prepare(Block block, HashSet<FaceOrientation> facesToDraw, BlockPosition position) {
return getBlockDescription(block).prepare(block, facesToDraw, position);
return getBlockModel(block).prepare(facesToDraw, position);
}
public TextureLoader getTextureLoader() {
return loader;
}
}

View File

@ -14,10 +14,31 @@
package de.bixilon.minosoft.render.blockModels.Face;
public enum FaceOrientation {
EAST,
WEST,
UP,
DOWN,
SOUTH,
NORTH
EAST(0),
WEST(1),
UP(2),
DOWN(3),
SOUTH(4),
NORTH(5);
private final int id;
FaceOrientation(int id) {
this.id = id;
}
public static FaceOrientation inverse(FaceOrientation orientation) {
return switch (orientation) {
case EAST -> WEST;
case WEST -> EAST;
case UP -> DOWN;
case DOWN -> UP;
case NORTH -> SOUTH;
case SOUTH -> NORTH;
};
}
public int getId() {
return id;
}
}

View File

@ -1,90 +0,0 @@
/*
* Codename Minosoft
* Copyright (C) 2020 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.render.blockModels.specialModels;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import de.bixilon.minosoft.data.mappings.blocks.Block;
import de.bixilon.minosoft.data.world.BlockPosition;
import de.bixilon.minosoft.logging.Log;
import de.bixilon.minosoft.render.blockModels.BlockConfiguration;
import de.bixilon.minosoft.render.blockModels.BlockConfigurationTrue;
import de.bixilon.minosoft.render.blockModels.BlockModelInterface;
import de.bixilon.minosoft.render.blockModels.Face.FaceOrientation;
import de.bixilon.minosoft.render.blockModels.subBlocks.SubBlock;
import de.bixilon.minosoft.render.texture.TextureLoader;
import org.apache.commons.collections.primitives.ArrayFloatList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
public class BlockModel implements BlockModelInterface {
private final HashMap<BlockConfiguration, HashSet<SubBlock>> blockConfigurationStates;
private final boolean isFull;
public BlockModel(JsonObject block, String mod) {
blockConfigurationStates = new HashMap<>();
if (block.has("blockModel")) {
blockConfigurationStates.put(new BlockConfigurationTrue(), BlockModelInterface.load(mod, block.get("blockModel").getAsString()));
} else if (block.has("states")) {
for (JsonElement element : block.get("states").getAsJsonArray()) {
JsonObject state = element.getAsJsonObject();
BlockConfiguration configuration = new BlockConfiguration(state.get("properties").getAsJsonObject());
blockConfigurationStates.put(configuration, BlockModelInterface.load(mod, state.get("blockModel").getAsString()));
}
}
// TODO
isFull = true;
}
public static ArrayFloatList prepareBlockState(HashSet<SubBlock> subBlocks, HashSet<FaceOrientation> facesToDraw, Block block, BlockPosition position) {
ArrayFloatList result = new ArrayFloatList();
for (SubBlock subBlock : subBlocks) {
result.addAll(subBlock.getFaces(block, facesToDraw, position));
}
return result;
}
public boolean isFull() {
return isFull;
}
public ArrayFloatList prepare(Block block, HashSet<FaceOrientation> facesToDraw, BlockPosition position) {
for (Map.Entry<BlockConfiguration, HashSet<SubBlock>> entry : blockConfigurationStates.entrySet()) {
if (entry.getKey().contains(block)) {
return prepareBlockState(entry.getValue(), facesToDraw, block, position);
}
}
Log.warn("no matching blockConfiguration found! Block: " + block.toString());
return new ArrayFloatList();
}
public HashSet<String> getAllTextures() {
HashSet<String> result = new HashSet<>();
for (HashSet<SubBlock> subBlocks : blockConfigurationStates.values()) {
for (SubBlock subBlock : subBlocks) {
result.addAll(subBlock.getTextures());
}
}
return result;
}
public void applyTextures(String mod, TextureLoader loader) {
for (HashSet<SubBlock> subBlocks : blockConfigurationStates.values()) {
BlockModelInterface.applyConfigurationTextures(subBlocks, mod, loader);
}
}
}

View File

@ -1,70 +0,0 @@
/*
* Codename Minosoft
* Copyright (C) 2020 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.render.blockModels.specialModels;
import com.google.gson.JsonObject;
import de.bixilon.minosoft.data.mappings.blocks.Block;
import de.bixilon.minosoft.data.mappings.blocks.BlockProperties;
import de.bixilon.minosoft.data.world.BlockPosition;
import de.bixilon.minosoft.render.blockModels.BlockModelInterface;
import de.bixilon.minosoft.render.blockModels.Face.FaceOrientation;
import de.bixilon.minosoft.render.blockModels.subBlocks.SubBlock;
import de.bixilon.minosoft.render.texture.TextureLoader;
import org.apache.commons.collections.primitives.ArrayFloatList;
import java.util.HashMap;
import java.util.HashSet;
import static de.bixilon.minosoft.render.blockModels.specialModels.BlockModel.prepareBlockState;
public class CropModel implements BlockModelInterface {
private final HashMap<String, HashSet<SubBlock>> modelMap;
public CropModel(JsonObject block, String mod) {
int stages = block.get("stages").getAsInt();
modelMap = new HashMap<>();
for (int i = 0; i < stages; i++) {
modelMap.put(String.format("%s%d", "AGE_", i), BlockModelInterface.load(mod, String.format("%s%d", block.get("base_name").getAsString(), i)));
}
}
public ArrayFloatList prepare(Block block, HashSet<FaceOrientation> facesToDraw, BlockPosition position) {
for (BlockProperties property : block.getProperties()) {
if (modelMap.containsKey(property.name())) {
return prepareBlockState(modelMap.get(property.name()), facesToDraw, block, position);
}
}
throw new RuntimeException("Failed to prepare block: " + block.toString());
}
public boolean isFull() {
return false;
}
public HashSet<String> getAllTextures() {
HashSet<String> result = new HashSet<>();
for (HashSet<SubBlock> subBlocks : modelMap.values()) {
for (SubBlock subBlock : subBlocks) {
result.addAll(subBlock.getTextures());
}
}
return result;
}
public void applyTextures(String mod, TextureLoader loader) {
for (HashSet<SubBlock> subBlocks : modelMap.values()) {
BlockModelInterface.applyConfigurationTextures(subBlocks, mod, loader);
}
}
}

View File

@ -1,91 +0,0 @@
/*
* Codename Minosoft
* Copyright (C) 2020 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.render.blockModels.specialModels;
import com.google.gson.JsonObject;
import de.bixilon.minosoft.data.mappings.blocks.Block;
import de.bixilon.minosoft.data.mappings.blocks.BlockProperties;
import de.bixilon.minosoft.data.mappings.blocks.BlockRotations;
import de.bixilon.minosoft.data.world.BlockPosition;
import de.bixilon.minosoft.logging.Log;
import de.bixilon.minosoft.render.blockModels.BlockModelInterface;
import de.bixilon.minosoft.render.blockModels.Face.FaceOrientation;
import de.bixilon.minosoft.render.blockModels.subBlocks.SubBlock;
import de.bixilon.minosoft.render.texture.TextureLoader;
import org.apache.commons.collections.primitives.ArrayFloatList;
import java.util.HashSet;
import static de.bixilon.minosoft.render.blockModels.specialModels.BlockModel.prepareBlockState;
public class DoorModel implements BlockModelInterface {
private final HashSet<SubBlock> bottom;
private final HashSet<SubBlock> bottom_hinge;
private final HashSet<SubBlock> top;
private final HashSet<SubBlock> top_hinge;
public DoorModel(JsonObject block, String mod) {
bottom = BlockModelInterface.load(mod, block.get("bottom").getAsString());
bottom_hinge = BlockModelInterface.load(mod, block.get("bottom_hinge").getAsString());
top = BlockModelInterface.load(mod, block.get("top").getAsString());
top_hinge = BlockModelInterface.load(mod, block.get("top_hinge").getAsString());
}
private static ArrayFloatList prepareHinge(HashSet<SubBlock> bottom, HashSet<SubBlock> top, Block block, HashSet<FaceOrientation> facesToDraw, BlockPosition position) {
if (block.getProperties().contains(BlockProperties.OPEN)) {
return prepareHalf(bottom, top, rotationAdjust.inverse().get(block.getRotation()), block, facesToDraw, position);
} else {
return prepareHalf(bottom, top, block.getRotation(), block, facesToDraw, position);
}
}
private static ArrayFloatList prepareHalf(HashSet<SubBlock> bottom, HashSet<SubBlock> top, BlockRotations rotation, Block block, HashSet<FaceOrientation> facesToDraw, BlockPosition position) {
if (block.getProperties().contains(BlockProperties.HALF_LOWER)) {
return prepareBlockState(bottom, facesToDraw, new Block("", "", rotation), position);
} else if (block.getProperties().contains(BlockProperties.HALF_UPPER)) {
return prepareBlockState(top, facesToDraw, new Block("", "", rotation), position);
}
Log.warn("now");
return null;
}
public ArrayFloatList prepare(Block block, HashSet<FaceOrientation> facesToDraw, BlockPosition position) {
if (block.getProperties().contains(BlockProperties.HINGE_LEFT)) {
return prepareHinge(bottom, top, block, facesToDraw, position);
}
return prepareHinge(bottom_hinge, top_hinge, block, facesToDraw, position);
}
public boolean isFull() {
return false;
}
public HashSet<String> getAllTextures() {
HashSet<String> result = new HashSet<>();
result.addAll(BlockModelInterface.getTextures(bottom));
result.addAll(BlockModelInterface.getTextures(bottom_hinge));
result.addAll(BlockModelInterface.getTextures(top));
result.addAll(BlockModelInterface.getTextures(top_hinge));
return result;
}
public void applyTextures(String mod, TextureLoader loader) {
BlockModelInterface.applyConfigurationTextures(bottom, mod, loader);
BlockModelInterface.applyConfigurationTextures(bottom_hinge, mod, loader);
BlockModelInterface.applyConfigurationTextures(top, mod, loader);
BlockModelInterface.applyConfigurationTextures(top_hinge, mod, loader);
}
}

View File

@ -1,60 +0,0 @@
/*
* Codename Minosoft
* Copyright (C) 2020 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.render.blockModels.specialModels;
import com.google.gson.JsonObject;
import de.bixilon.minosoft.data.mappings.blocks.Block;
import de.bixilon.minosoft.data.world.BlockPosition;
import de.bixilon.minosoft.render.blockModels.BlockModelInterface;
import de.bixilon.minosoft.render.blockModels.Face.FaceOrientation;
import de.bixilon.minosoft.render.blockModels.subBlocks.SubBlock;
import de.bixilon.minosoft.render.texture.TextureLoader;
import org.apache.commons.collections.primitives.ArrayFloatList;
import java.util.HashSet;
public class FenceModel implements BlockModelInterface {
private final HashSet<SubBlock> post;
private final HashSet<SubBlock> side;
public FenceModel(JsonObject block, String mod) {
post = BlockModelInterface.load(mod, block.get("post").getAsString());
side = BlockModelInterface.load(mod, block.get("side").getAsString());
}
public ArrayFloatList prepare(Block block, HashSet<FaceOrientation> facesToDraw, BlockPosition position) {
ArrayFloatList result = new ArrayFloatList();
// TODO
return result;
}
@Override
public boolean isFull() {
return false;
}
@Override
public HashSet<String> getAllTextures() {
HashSet<String> result = new HashSet<>();
result.addAll(BlockModelInterface.getTextures(post));
result.addAll(BlockModelInterface.getTextures(side));
return result;
}
@Override
public void applyTextures(String mod, TextureLoader loader) {
BlockModelInterface.applyConfigurationTextures(post, mod, loader);
BlockModelInterface.applyConfigurationTextures(side, mod, loader);
}
}

View File

@ -1,85 +0,0 @@
/*
* Codename Minosoft
* Copyright (C) 2020 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.render.blockModels.specialModels;
import com.google.gson.JsonObject;
import de.bixilon.minosoft.data.mappings.blocks.Block;
import de.bixilon.minosoft.data.mappings.blocks.BlockProperties;
import de.bixilon.minosoft.data.mappings.blocks.BlockRotations;
import de.bixilon.minosoft.data.world.BlockPosition;
import de.bixilon.minosoft.render.blockModels.BlockModelInterface;
import de.bixilon.minosoft.render.blockModels.Face.FaceOrientation;
import de.bixilon.minosoft.render.blockModels.subBlocks.SubBlock;
import de.bixilon.minosoft.render.texture.TextureLoader;
import org.apache.commons.collections.primitives.ArrayFloatList;
import java.util.HashSet;
public class FireModel implements BlockModelInterface {
private final HashSet<SubBlock> floor;
private final HashSet<SubBlock> side;
private final HashSet<SubBlock> up;
public FireModel(JsonObject block, String mod) {
floor = BlockModelInterface.load(mod, block.get("floor").getAsString());
side = BlockModelInterface.load(mod, block.get("side").getAsString());
up = BlockModelInterface.load(mod, block.get("up").getAsString());
}
public ArrayFloatList prepare(Block block, HashSet<FaceOrientation> facesToDraw, BlockPosition position) {
HashSet<BlockProperties> properties = block.getProperties();
ArrayFloatList result = new ArrayFloatList();
if (properties.contains(BlockProperties.EAST)) {
result.addAll(BlockModelInterface.prepareState(side, BlockRotations.EAST, facesToDraw, position));
}
if (properties.contains(BlockProperties.WEST)) {
result.addAll(BlockModelInterface.prepareState(side, BlockRotations.WEST, facesToDraw, position));
}
if (properties.contains(BlockProperties.NORTH)) {
result.addAll(BlockModelInterface.prepareState(side, BlockRotations.NORTH, facesToDraw, position));
}
if (properties.contains(BlockProperties.SOUTH)) {
result.addAll(BlockModelInterface.prepareState(side, BlockRotations.SOUTH, facesToDraw, position));
}
if (properties.contains(BlockProperties.UP)) {
result.addAll(BlockModelInterface.prepareState(up, BlockRotations.UP, facesToDraw, position));
}
if (result.size() == 0) {
result.addAll(BlockModelInterface.prepareState(floor, BlockRotations.NONE, facesToDraw, position));
}
return result;
}
@Override
public boolean isFull() {
return false;
}
@Override
public HashSet<String> getAllTextures() {
HashSet<String> result = new HashSet<>();
result.addAll(BlockModelInterface.getTextures(floor));
result.addAll(BlockModelInterface.getTextures(side));
result.addAll(BlockModelInterface.getTextures(up));
return result;
}
@Override
public void applyTextures(String mod, TextureLoader loader) {
BlockModelInterface.applyConfigurationTextures(floor, mod, loader);
BlockModelInterface.applyConfigurationTextures(side, mod, loader);
BlockModelInterface.applyConfigurationTextures(up, mod, loader);
}
}

View File

@ -1,73 +0,0 @@
/*
* Codename Minosoft
* Copyright (C) 2020 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.render.blockModels.specialModels;
import com.google.gson.JsonObject;
import de.bixilon.minosoft.data.mappings.blocks.Block;
import de.bixilon.minosoft.data.mappings.blocks.BlockProperties;
import de.bixilon.minosoft.data.mappings.blocks.BlockRotations;
import de.bixilon.minosoft.data.world.BlockPosition;
import de.bixilon.minosoft.render.blockModels.BlockModelInterface;
import de.bixilon.minosoft.render.blockModels.Face.FaceOrientation;
import de.bixilon.minosoft.render.blockModels.subBlocks.SubBlock;
import de.bixilon.minosoft.render.texture.TextureLoader;
import org.apache.commons.collections.primitives.ArrayFloatList;
import java.util.HashSet;
public class MushroomModel implements BlockModelInterface {
private final HashSet<SubBlock> subBlocks;
public MushroomModel(JsonObject block, String mod) {
subBlocks = BlockModelInterface.load(mod, block.get("block").getAsString());
}
public ArrayFloatList prepare(Block block, HashSet<FaceOrientation> facesToDraw, BlockPosition position) {
ArrayFloatList result = new ArrayFloatList();
if (block.getProperties().contains(BlockProperties.DOWN)) {
result.addAll(BlockModelInterface.prepareState(subBlocks, BlockRotations.DOWN, facesToDraw, position));
}
if (block.getProperties().contains(BlockProperties.UP)) {
result.addAll(BlockModelInterface.prepareState(subBlocks, BlockRotations.UP, facesToDraw, position));
}
if (block.getProperties().contains(BlockProperties.EAST)) {
result.addAll(BlockModelInterface.prepareState(subBlocks, BlockRotations.EAST, facesToDraw, position));
}
if (block.getProperties().contains(BlockProperties.WEST)) {
result.addAll(BlockModelInterface.prepareState(subBlocks, BlockRotations.WEST, facesToDraw, position));
}
if (block.getProperties().contains(BlockProperties.NORTH)) {
result.addAll(BlockModelInterface.prepareState(subBlocks, BlockRotations.NORTH, facesToDraw, position));
}
if (block.getProperties().contains(BlockProperties.SOUTH)) {
result.addAll(BlockModelInterface.prepareState(subBlocks, BlockRotations.SOUTH, facesToDraw, position));
}
return result;
}
@Override
public boolean isFull() {
return true;
}
@Override
public HashSet<String> getAllTextures() {
return BlockModelInterface.getTextures(subBlocks);
}
@Override
public void applyTextures(String mod, TextureLoader loader) {
BlockModelInterface.applyConfigurationTextures(subBlocks, mod, loader);
}
}

View File

@ -1,80 +0,0 @@
/*
* Codename Minosoft
* Copyright (C) 2020 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.render.blockModels.specialModels;
import com.google.gson.JsonObject;
import de.bixilon.minosoft.data.mappings.blocks.Block;
import de.bixilon.minosoft.data.mappings.blocks.BlockProperties;
import de.bixilon.minosoft.data.mappings.blocks.BlockRotations;
import de.bixilon.minosoft.data.world.BlockPosition;
import de.bixilon.minosoft.render.blockModels.BlockModelInterface;
import de.bixilon.minosoft.render.blockModels.Face.FaceOrientation;
import de.bixilon.minosoft.render.blockModels.subBlocks.SubBlock;
import de.bixilon.minosoft.render.texture.TextureLoader;
import org.apache.commons.collections.primitives.ArrayFloatList;
import java.util.HashSet;
public class StairsModel implements BlockModelInterface {
private final HashSet<SubBlock> straight;
private final HashSet<SubBlock> inner;
private final HashSet<SubBlock> outer;
public StairsModel(JsonObject block, String mod) {
straight = BlockModelInterface.load(mod, block.get("straight").getAsString());
inner = BlockModelInterface.load(mod, block.get("inner").getAsString());
outer = BlockModelInterface.load(mod, block.get("outer").getAsString());
}
public static ArrayFloatList prepareCorner(HashSet<SubBlock> subBlocks, BlockProperties property, BlockRotations rotation, HashSet<FaceOrientation> facesToDraw, BlockPosition position) {
if (property.name().contains("LEFT")) {
return BlockModelInterface.prepareState(subBlocks, rotation, facesToDraw, position);
}
return BlockModelInterface.prepareState(subBlocks, rotationAdjust.get(rotation), facesToDraw, position);
}
public ArrayFloatList prepare(Block block, HashSet<FaceOrientation> facesToDraw, BlockPosition position) {
HashSet<BlockProperties> properties = block.getProperties();
for (BlockProperties property : properties) {
if (property.name().contains("INNER")) {
return prepareCorner(outer, property, block.getRotation(), facesToDraw, position);
} else if (property.name().contains("OUTER")) {
return prepareCorner(inner, property, block.getRotation(), facesToDraw, position);
}
}
return BlockModelInterface.prepareState(straight, rotationAdjust.get(block.getRotation()), facesToDraw, position);
}
@Override
public boolean isFull() {
return false;
}
@Override
public HashSet<String> getAllTextures() {
HashSet<String> result = new HashSet<>();
result.addAll(BlockModelInterface.getTextures(straight));
result.addAll(BlockModelInterface.getTextures(inner));
result.addAll(BlockModelInterface.getTextures(outer));
return result;
}
@Override
public void applyTextures(String mod, TextureLoader loader) {
BlockModelInterface.applyConfigurationTextures(straight, mod, loader);
BlockModelInterface.applyConfigurationTextures(inner, mod, loader);
BlockModelInterface.applyConfigurationTextures(outer, mod, loader);
}
}

View File

@ -1,63 +0,0 @@
/*
* Codename Minosoft
* Copyright (C) 2020 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.render.blockModels.specialModels;
import com.google.gson.JsonObject;
import de.bixilon.minosoft.data.mappings.blocks.Block;
import de.bixilon.minosoft.data.world.BlockPosition;
import de.bixilon.minosoft.render.blockModels.BlockModelInterface;
import de.bixilon.minosoft.render.blockModels.Face.FaceOrientation;
import de.bixilon.minosoft.render.blockModels.subBlocks.SubBlock;
import de.bixilon.minosoft.render.texture.TextureLoader;
import org.apache.commons.collections.primitives.ArrayFloatList;
import java.util.HashSet;
public class WireModel implements BlockModelInterface {
private final HashSet<SubBlock> dot;
private final HashSet<SubBlock> side;
private final HashSet<SubBlock> up;
public WireModel(JsonObject block, String mod) {
dot = BlockModelInterface.load(mod, block.get("dot").getAsString());
side = BlockModelInterface.load(mod, block.get("side").getAsString());
up = BlockModelInterface.load(mod, block.get("up").getAsString());
}
public ArrayFloatList prepare(Block block, HashSet<FaceOrientation> facesToDraw, BlockPosition position) {
// TODO: REDSTONE
return new ArrayFloatList();
}
@Override
public boolean isFull() {
return false;
}
@Override
public HashSet<String> getAllTextures() {
HashSet<String> result = new HashSet<>();
result.addAll(BlockModelInterface.getTextures(dot));
result.addAll(BlockModelInterface.getTextures(side));
result.addAll(BlockModelInterface.getTextures(up));
return result;
}
@Override
public void applyTextures(String mod, TextureLoader loader) {
BlockModelInterface.applyConfigurationTextures(dot, mod, loader);
BlockModelInterface.applyConfigurationTextures(side, mod, loader);
BlockModelInterface.applyConfigurationTextures(up, mod, loader);
}
}

View File

@ -1,6 +1,6 @@
/*
* Codename Minosoft
* Copyright (C) 2020 Moritz Zwerger
* Copyright (C) 2020 Lukas Eisenhauer
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
@ -15,13 +15,14 @@ package de.bixilon.minosoft.render.blockModels.subBlocks;
// some 3d object with 8 corners, 6 faces and 12 edges (example: cube, but can be deformed)
import de.bixilon.minosoft.data.mappings.blocks.Block;
import de.bixilon.minosoft.data.mappings.blocks.BlockRotations;
import de.bixilon.minosoft.render.blockModels.Face.Axis;
import de.bixilon.minosoft.render.blockModels.Face.FaceOrientation;
import java.util.HashMap;
import java.util.Map;
import static de.bixilon.minosoft.render.blockModels.Face.RenderConstants.BLOCK_RESOLUTION;
public class Cuboid {
public static final Map<FaceOrientation, int[]> facePositionMapTemplate = Map.of(FaceOrientation.EAST, new int[]{7, 5, 1, 3}, FaceOrientation.WEST, new int[]{4, 6, 2, 0}, FaceOrientation.UP, new int[]{4, 5, 7, 6}, FaceOrientation.DOWN, new int[]{2, 3, 1, 0}, FaceOrientation.SOUTH, new int[]{6, 7, 3, 2}, FaceOrientation.NORTH, new int[]{5, 4, 0, 1});
@ -54,15 +55,27 @@ public class Cuboid {
}
}
public SubBlockPosition[] getFacePositions(FaceOrientation orientation, Block block) {
SubBlockPosition[] positions = facePositionMap.get(orientation);
if (block.getRotation() == BlockRotations.NONE || block.getRotation() == BlockRotations.NORTH) {
return positions;
public SubBlockPosition[] getFacePositions(FaceOrientation orientation) {
return facePositionMap.get(orientation);
}
public boolean isFull(FaceOrientation orientation) {
SubBlockPosition[] positions = getFacePositions(orientation);
return switch (orientation) {
case EAST -> (positions[0].x == 0 && positions[1].x == 0 && positions[2].x == 0 && positions[3].x == 0);
case WEST -> (positions[0].x == BLOCK_RESOLUTION && positions[1].x == BLOCK_RESOLUTION && positions[2].x == BLOCK_RESOLUTION && positions[3].x == BLOCK_RESOLUTION);
case UP -> (positions[0].y == 0 && positions[1].y == 0 && positions[2].y == 0 && positions[3].y == 0);
case DOWN -> (positions[0].y == BLOCK_RESOLUTION && positions[1].y == BLOCK_RESOLUTION && positions[2].y == BLOCK_RESOLUTION && positions[3].y == BLOCK_RESOLUTION);
case SOUTH -> (positions[0].z == 0 && positions[1].z == 0 && positions[2].z == 0 && positions[3].z == 0);
case NORTH -> (positions[0].z == BLOCK_RESOLUTION && positions[1].z == BLOCK_RESOLUTION && positions[2].z == BLOCK_RESOLUTION && positions[3].z == BLOCK_RESOLUTION);
};
}
public void rotate(Axis axis, int rotation) {
for (SubBlockPosition[] positions : facePositionMap.values()) {
for (SubBlockPosition position : positions) {
position = position.rotated(axis, rotation);
}
}
SubBlockPosition[] result = new SubBlockPosition[positions.length];
for (int i = 0; i < positions.length; i++) {
result[i] = positions[i].rotated(block);
}
return result;
}
}

View File

@ -14,8 +14,8 @@
package de.bixilon.minosoft.render.blockModels.subBlocks;
import com.google.gson.JsonObject;
import de.bixilon.minosoft.data.mappings.blocks.Block;
import de.bixilon.minosoft.data.world.BlockPosition;
import de.bixilon.minosoft.render.blockModels.Face.Axis;
import de.bixilon.minosoft.render.blockModels.Face.FaceOrientation;
import de.bixilon.minosoft.render.texture.InFaceUV;
import de.bixilon.minosoft.render.texture.TextureLoader;
@ -29,10 +29,9 @@ public class SubBlock {
private final HashMap<FaceOrientation, Float> textureCoordinates;
private final HashMap<FaceOrientation, String> textures;
private final HashMap<FaceOrientation, Integer> textureRotations;
private final HashSet<FaceOrientation> cullFaceTextures;
private final boolean[] full;
private final HashMap<FaceOrientation, InFaceUV> uv;
private final Cuboid cuboid;
private final boolean isFull;
private SubBlockRotation rotation;
public SubBlock(JsonObject json, HashMap<String, String> variables) {
@ -40,7 +39,6 @@ public class SubBlock {
textures = new HashMap<>();
textureRotations = new HashMap<>();
textureCoordinates = new HashMap<>();
cullFaceTextures = new HashSet<>();
SubBlockPosition from = new SubBlockPosition(json.getAsJsonArray("from"));
SubBlockPosition to = new SubBlockPosition(json.getAsJsonArray("to"));
@ -55,7 +53,7 @@ public class SubBlock {
putTexture(faces.getAsJsonObject(orientation.name().toLowerCase()), orientation, variables);
}
}
isFull = (from.x == 0 && from.y == 0 && from.z == 0) && (to.x == 16 && to.y == 16 && to.z == 16) && rotation == null;
full = createFull();
}
private static String getRealTextureName(String textureName, HashMap<String, String> variables) {
@ -71,13 +69,17 @@ public class SubBlock {
}
return newName;
} else {
throw new IllegalArgumentException("could not resolve variable " + textureName);
return "";
}
} else {
return textureName;
}
}
private boolean[] createFull() {
return new boolean[]{cuboid.isFull(FaceOrientation.EAST), cuboid.isFull(FaceOrientation.WEST), cuboid.isFull(FaceOrientation.UP), cuboid.isFull(FaceOrientation.DOWN), cuboid.isFull(FaceOrientation.NORTH), cuboid.isFull(FaceOrientation.SOUTH)};
}
public void applyTextures(String mod, TextureLoader loader) {
for (Map.Entry<FaceOrientation, String> entry : textures.entrySet()) {
float texture = loader.getTexture(mod, entry.getValue());
@ -106,10 +108,10 @@ public class SubBlock {
textures.put(orientation, textureName);
}
public ArrayFloatList getFaces(Block block, HashSet<FaceOrientation> facesToDraw, BlockPosition position) {
public ArrayFloatList getFaces(HashSet<FaceOrientation> facesToDraw, BlockPosition position) {
ArrayFloatList result = new ArrayFloatList();
facesToDraw.forEach((faceOrientation -> {
ArrayFloatList face = prepareFace(faceOrientation, block, position);
ArrayFloatList face = prepareFace(faceOrientation, position);
if (face != null) {
result.addAll(face);
}
@ -117,12 +119,12 @@ public class SubBlock {
return result;
}
private ArrayFloatList prepareFace(FaceOrientation faceDirection, Block block, BlockPosition position) {
if (cullFaceTextures.contains(faceDirection) || !textureCoordinates.containsKey(faceDirection)) {
private ArrayFloatList prepareFace(FaceOrientation faceDirection, BlockPosition position) {
if (full[faceDirection.getId()] || !textureCoordinates.containsKey(faceDirection)) {
return null;
}
ArrayFloatList result = new ArrayFloatList();
SubBlockPosition[] positions = cuboid.getFacePositions(faceDirection, block);
SubBlockPosition[] positions = cuboid.getFacePositions(faceDirection);
InFaceUV inFaceUV = uv.get(faceDirection);
inFaceUV.prepare(textureCoordinates.get(faceDirection));
int rotation = textureRotations.get(faceDirection);
@ -133,10 +135,6 @@ public class SubBlock {
return result;
}
public boolean isFull() {
return isFull;
}
public HashSet<String> getTextures() {
HashSet<String> result = new HashSet<>();
for (Map.Entry<FaceOrientation, String> texture : textures.entrySet()) {
@ -144,4 +142,12 @@ public class SubBlock {
}
return result;
}
public void rotate(Axis axis, int rotation) {
cuboid.rotate(axis, rotation);
}
public boolean[] getFull() {
return full;
}
}

View File

@ -14,7 +14,6 @@
package de.bixilon.minosoft.render.blockModels.subBlocks;
import com.google.gson.JsonArray;
import de.bixilon.minosoft.data.mappings.blocks.Block;
import de.bixilon.minosoft.data.world.BlockPosition;
import de.bixilon.minosoft.render.blockModels.Face.Axis;
import org.apache.commons.collections.primitives.ArrayFloatList;
@ -23,14 +22,6 @@ import static de.bixilon.minosoft.render.blockModels.Face.RenderConstants.BLOCK_
public class SubBlockPosition {
private static final SubBlockPosition middlePos = new SubBlockPosition(8, 8, 8);
private static final SubBlockRotation westRotator = new SubBlockRotation(middlePos, Axis.Y, 90);
private static final SubBlockRotation eastRotator = new SubBlockRotation(middlePos, Axis.Y, 270);
private static final SubBlockRotation southRotator = new SubBlockRotation(middlePos, Axis.Y, 180);
private static final SubBlockRotation xAxisRotator = new SubBlockRotation(middlePos, Axis.Z, 90);
private static final SubBlockRotation zAxisRotator = new SubBlockRotation(middlePos, Axis.X, 90);
private static final SubBlockRotation downRotator = new SubBlockRotation(middlePos, Axis.X, 90);
private static final SubBlockRotation downAltRotator = new SubBlockRotation(middlePos, Axis.X, 180);
private static final SubBlockRotation upRotator = new SubBlockRotation(middlePos, Axis.X, -90);
public float x;
public float y;
public float z;
@ -56,35 +47,6 @@ public class SubBlockPosition {
return new SubBlockPosition(pos1.x - pos2.x, pos1.y - pos2.y, pos1.z - pos2.z);
}
public SubBlockPosition rotated(Block block) {
if (block.getRotation() == null) {
return this;
}
switch (block.getRotation()) {
case EAST:
return eastRotator.apply(this);
case WEST:
return westRotator.apply(this);
case SOUTH:
return southRotator.apply(this);
case UP:
if (block.getIdentifier().equals("dispenser") || block.getIdentifier().equals("dropper")) {
return this;
}
return upRotator.apply(this);
case DOWN:
if (block.getIdentifier().equals("dispenser") || block.getIdentifier().equals("dropper")) {
return downAltRotator.apply(this);
}
return downRotator.apply(this);
case AXIS_X:
return xAxisRotator.apply(this);
case AXIS_Z:
return zAxisRotator.apply(this);
}
return this;
}
public ArrayFloatList getFloats(BlockPosition position) {
ArrayFloatList result = new ArrayFloatList();
result.add(x / BLOCK_RESOLUTION + position.getX());
@ -92,4 +54,8 @@ public class SubBlockPosition {
result.add(z / BLOCK_RESOLUTION + position.getZ());
return result;
}
public SubBlockPosition rotated(Axis axis, int rotation) {
return new SubBlockRotation(middlePos, axis, rotation).apply(this);
}
}

View File

@ -27,7 +27,7 @@ public class CollisionHandler {
public CollisionHandler(PlayerController controller) {
world = GameWindow.getConnection().getPlayer().getWorld();
modelLoader = GameWindow.getRenderer().getAssetsLoader().getBlockModelLoader();
modelLoader = GameWindow.getRenderer().getBlockModelLoader();
this.controller = controller;
}
@ -88,14 +88,14 @@ public class CollisionHandler {
controller.playerVelocity.z = 0;
}
private boolean isPositionValid(Vec3 testPos) {
private boolean isPositionValid(Vec3 testPosition) {
float width = controller.getPlayerWidth();
int[] xPositions = AdditionalMath.valuesBetween(AdditionalMath.betterRound(testPos.x + width), AdditionalMath.betterRound(testPos.x - width));
int[] xPositions = AdditionalMath.valuesBetween(AdditionalMath.betterRound(testPosition.x + width), AdditionalMath.betterRound(testPosition.x - width));
int[] yPositions = AdditionalMath.valuesBetween(AdditionalMath.betterRound(testPos.y), AdditionalMath.betterRound(testPos.y + controller.getPlayerHeight()));
int[] yPositions = AdditionalMath.valuesBetween(AdditionalMath.betterRound(testPosition.y), AdditionalMath.betterRound(testPosition.y + controller.getPlayerHeight()));
int[] zPositions = AdditionalMath.valuesBetween(AdditionalMath.betterRound(testPos.z + width), AdditionalMath.betterRound(testPos.z - width));
int[] zPositions = AdditionalMath.valuesBetween(AdditionalMath.betterRound(testPosition.z + width), AdditionalMath.betterRound(testPosition.z - width));
for (int xPos : xPositions) {
for (int yPos : yPositions) {

View File

@ -19,7 +19,7 @@ import de.bixilon.minosoft.render.utility.Vec3;
import static org.lwjgl.glfw.GLFW.*;
public class PlayerMovement {
private static final float FLY_SPEED = 0.1f;
private static final float FLY_SPEED = 0.2f;
private static final Vec3 CAMERA_UP = new Vec3(0f, 1f, 0f);
private final long window;

View File

@ -36,8 +36,8 @@ public class InFaceUV {
}
public void prepare(float texture) {
realU1 = texture + u1 * GameWindow.getRenderer().getAssetsLoader().getTextureLoader().getStep() / RenderConstants.TEXTURE_PACK_RESOLUTION;
realU2 = texture + u2 * GameWindow.getRenderer().getAssetsLoader().getTextureLoader().getStep() / RenderConstants.TEXTURE_PACK_RESOLUTION;
realU1 = texture + u1 * GameWindow.getRenderer().getBlockModelLoader().getTextureLoader().getStep() / RenderConstants.TEXTURE_PACK_RESOLUTION;
realU2 = texture + u2 * GameWindow.getRenderer().getBlockModelLoader().getTextureLoader().getStep() / RenderConstants.TEXTURE_PACK_RESOLUTION;
realV1 = (float) v1 / RenderConstants.TEXTURE_PACK_RESOLUTION;
realV2 = (float) v2 / RenderConstants.TEXTURE_PACK_RESOLUTION;
}

View File

@ -72,13 +72,13 @@ public class TextureLoader {
private void loadTextures(String mod, HashSet<String> textureNames, HashMap<String, float[]> tint) {
HashMap<String, BufferedImage> modTextureMap = new HashMap<>();
for (String textureName : textureNames) {
if (textureName.contains("overlay")) {
if (textureName.contains("overlay") || textureName.equals("")) {
continue;
}
String path = Config.homeDir + "assets/" + mod + "/textures/" + textureName + ".png";
try {
BufferedImage image = ImageIO.read(new File(path));
if (tint.containsKey(textureName)) {
if (tint != null && tint.containsKey(textureName)) {
tintImage(image, tint.get(textureName));
}
modTextureMap.put(textureName, image);
@ -141,7 +141,7 @@ public class TextureLoader {
}
public float getTexture(String mod, String textureName) {
if (textureName.contains("overlay")) {
if (textureName.contains("overlay") || textureName.equals("")) {
return -1;
}
@ -153,11 +153,6 @@ public class TextureLoader {
}
Integer pos = modMap.get(textureName);
if (pos == null) {
System.out.println("failed to find texture " + textureName);
System.exit(10);
}
return pos * step;
}

File diff suppressed because one or more lines are too long

172
util/blockModelCombinder.py Normal file
View File

@ -0,0 +1,172 @@
"""
* Codename Minosoft
* Copyright (C) 2020 Lukas Eisenhauer
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
"""
# Codename Minosoft
# Copyright (C) 2020 Moritz Zwerger
#
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
#
# This software is not affiliated with Mojang AB, the original developer of Minecraft.
# The blockModels are contained in the minecraft.jar file. Extract the assets/minecraft folder and place this file in the same folder
import \
json
import \
os
blockStates = {}
blockModels = {}
modName = "minecraft"
blockStatesDir = modName + "/blockstates/"
blockModelsDir = modName + "/models/block/"
print(
"loading blockstates...")
for blockStateFile in os.listdir(
blockStatesDir):
with open(
blockStatesDir + blockStateFile,
"r") as file:
data = json.load(
file)
blockStates[
blockStateFile.split(
".")[
0]] = data
print(
"counting models...")
blockModelList = []
for block in blockStates:
if "variants" in \
blockStates[
block]:
for variant in \
blockStates[
block][
"variants"]:
if type(
blockStates[
block][
"variants"][
variant]) == type(
{}):
if not \
blockStates[
block][
"variants"][
variant][
"model"] in blockModelList:
blockModelList.append(
blockStates[
block][
"variants"][
variant][
"model"])
elif type(
blockStates[
block][
"variants"][
variant]) == type(
[]):
for subVariant in variant:
if not subVariant in blockModelList:
blockModelList.append(
subVariant)
elif "multipart" in \
blockStates[
block]:
for part in \
blockStates[
block][
"multipart"]:
if type(
part[
"apply"]) == type(
{}):
if not \
part[
"apply"][
"model"] in blockModelList:
blockModelList.append(
part[
"apply"][
"model"])
else:
for subPart in \
part[
"apply"]:
if not \
subPart[
"model"] in blockModelList:
blockModelList.append(
subPart[
"model"])
else:
print(
"FAILDED TO GET BLOCK MODELS FOR BLOCK " + block)
print(
"loading models...")
for blockModelFile in os.listdir(
blockModelsDir):
with open(
blockModelsDir + blockModelFile,
"r") as file:
data = json.load(
file)
blockModels[
blockModelFile.split(
".")[
0]] = data
print(
"combining files...")
finalJson = {
"blockStates": blockStates,
"blockModels": blockModels,
"tinted_textures": {
"block/grass_block_top": [
0,
1,
0],
"block/grass": [
0,
1,
0],
"block/water_still": [
0,
0,
1]
}
}
print(
"saving...")
with open(
"blockModels.json",
"w+") as file:
json.dump(
finalJson,
file)
print(
"finished succesfully")