added support for tinted textures

This commit is contained in:
Lukas 2020-08-12 18:17:43 +02:00
parent c4f3c25d2a
commit 95ad0f0535
11 changed files with 298 additions and 194 deletions

View File

@ -48,6 +48,7 @@ public class MainWindow {
renderMode = MAIN_MENU;
mainMenu = new MainMenu(openGLWindow.getWidth(), openGLWindow.getHeight());
mainLoop();
System.exit(0);
});
guiThread.start();
}

View File

@ -20,7 +20,6 @@ import de.bixilon.minosoft.logging.Log;
import de.bixilon.minosoft.render.blockModels.BlockModelLoader;
import de.bixilon.minosoft.render.blockModels.Face;
import de.bixilon.minosoft.render.fullFace.FaceOrientation;
import de.bixilon.minosoft.render.texture.TextureLoader;
import java.util.HashMap;
import java.util.HashSet;
@ -30,13 +29,10 @@ import static de.bixilon.minosoft.render.fullFace.RenderConstants.faceDir;
import static org.lwjgl.opengl.GL11.*;
public class WorldRenderer {
private final TextureLoader textureLoader;
private final HashMap<BlockPosition, HashSet<Face>> faces;
private final int faceCount = 0;
private BlockModelLoader modelLoader;
public WorldRenderer() {
textureLoader = new TextureLoader(MainWindow.getOpenGLWindow().getWindow());
faces = new HashMap<>();
}
@ -87,7 +83,7 @@ public class WorldRenderer {
public void draw() {
glPushMatrix();
glBindTexture(GL_TEXTURE_2D, textureLoader.getTextureID());
glBindTexture(GL_TEXTURE_2D, modelLoader.getTextureLoader().getTextureID());
glBegin(GL_QUADS);
synchronized (faces) {
for (Map.Entry<BlockPosition, HashSet<Face>> entry : faces.entrySet()) {
@ -100,10 +96,6 @@ public class WorldRenderer {
glPopMatrix();
}
public TextureLoader getTextureLoader() {
return textureLoader;
}
public BlockModelLoader getModelLoader() {
return modelLoader;
}

View File

@ -18,6 +18,7 @@ import com.google.gson.JsonObject;
import de.bixilon.minosoft.Config;
import de.bixilon.minosoft.game.datatypes.objectLoader.blocks.Block;
import de.bixilon.minosoft.render.fullFace.FaceOrientation;
import de.bixilon.minosoft.render.texture.TextureLoader;
import java.io.IOException;
import java.util.HashMap;
@ -31,23 +32,29 @@ public class BlockDescription {
boolean isFull;
public BlockDescription(JsonElement child, String identifier, String mod) {
if (child.getAsString().equals("invisible")) {
blockConfigurationStates = new HashMap<>();
if (child.isJsonPrimitive() && child.getAsString().equals("invisible")) {
defaultState = new HashSet<>();
return;
} else if (child.getAsString().equals("regular")) {
defaultState = load(mod, identifier, new HashMap<>());
} else {
} else if (child.isJsonPrimitive() && child.getAsString().equals("regular")) {
defaultState = load(mod, identifier);
} else if (child.isJsonPrimitive()) {
defaultState = load(mod, child.getAsString());
} else if (child.isJsonObject()) {
JsonObject childJson = child.getAsJsonObject();
for (String state : childJson.keySet()) {
if (state.equals("else")) {
defaultState = load(mod, childJson.get("else").getAsString(), new HashMap<>());
}
blockConfigurationStates.put(new BlockConfiguration(state),
BlockConfiguration configuration = new BlockConfiguration(state);
blockConfigurationStates.put(configuration,
load(mod, childJson.get(state).getAsString()));
}
}
for (SubBlock subBlock : defaultState) {
if (subBlock.isFull()) {
isFull = true;
break;
}
}
}
@ -102,4 +109,18 @@ public class BlockDescription {
}
return result;
}
public HashSet<String> getAllTextures() {
HashSet<String> result = new HashSet<>();
for (SubBlock subBlock : defaultState) {
result.addAll(subBlock.getTextures());
}
return result;
}
public void applyTextures(String mod, TextureLoader loader) {
for (SubBlock subBlock : defaultState) {
subBlock.applyTextures(mod, loader);
}
}
}

View File

@ -20,51 +20,88 @@ import de.bixilon.minosoft.game.datatypes.objectLoader.blocks.Block;
import de.bixilon.minosoft.game.datatypes.objectLoader.blocks.Blocks;
import de.bixilon.minosoft.logging.Log;
import de.bixilon.minosoft.render.fullFace.FaceOrientation;
import de.bixilon.minosoft.render.texture.TextureLoader;
import org.apache.commons.collections.primitives.ArrayFloatList;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import static de.bixilon.minosoft.util.Util.readJsonFromFile;
public class BlockModelLoader {
private final HashMap<String, HashMap<String, BlockDescription>> blockDescriptionMap;
TextureLoader textureLoader;
public BlockModelLoader() {
blockDescriptionMap = new HashMap<>();
HashMap<String, HashMap<String, float[]>> tints = new HashMap<>();
HashMap<String, HashSet<String>> textures = new HashMap<>();
try {
String folderPath = Config.homeDir + "assets/mapping/blockModels/";
for (File file : new File(folderPath).listFiles()) {
JsonObject blockList = readJsonFromFile(file.getAbsolutePath());
JsonObject json = readJsonFromFile(file.getAbsolutePath());
String mod = file.getName().substring(0, file.getName().lastIndexOf('.'));
loadModels(blockList, mod);
tints.put(mod, readTints(json));
textures.put(mod, loadModels(json.get("blocks").getAsJsonObject(), mod));
}
} catch (IOException | NullPointerException e) {
textureLoader = new TextureLoader(textures, tints);
applyTextures();
} catch (IOException e) {
e.printStackTrace();
}
Log.info("finished loading all block descriptions");
Log.info("finished loading all blocks");
}
final HashMap<String, HashMap<String, BlockDescription>> blockDescriptionMap;
private void applyTextures() {
for (Map.Entry<String, HashMap<String, BlockDescription>> mod : blockDescriptionMap.entrySet()) {
for (Map.Entry<String, BlockDescription> block : mod.getValue().entrySet()) {
block.getValue().applyTextures(mod.getKey(), textureLoader);
}
}
}
private void loadModels(JsonObject blockList, String mod) {
private HashMap<String, float[]> readTints(JsonObject json) {
HashMap<String, float[]> result = new HashMap<>();
if (json.has("tinted_textures")) {
JsonObject textures = json.get("tinted_textures").getAsJsonObject();
for (String textureName : textures.keySet()) {
ArrayFloatList colorValues = new ArrayFloatList();
for (JsonElement colorValue : textures.get(textureName).getAsJsonArray()) {
colorValues.add(colorValue.getAsFloat());
}
float[] color = colorValues.toArray();
result.put(textureName, color);
}
}
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()) {
JsonElement child = blockList.get(identifier);
loadModel(mod, identifier, child);
result.addAll(loadModel(mod, identifier, child));
}
return result;
}
private void loadModel(String mod, String identifier, JsonElement child) {
private HashSet<String> loadModel(String mod, String identifier, JsonElement child) {
HashSet<String> result = new HashSet<>();
try {
HashMap<String, BlockDescription> modList = blockDescriptionMap.get(mod);
BlockDescription description = new BlockDescription(child, identifier, mod);
result.addAll(description.getAllTextures());
modList.put(identifier, description);
} catch (Exception e) {
e.printStackTrace();
System.out.println(mod + ":" + identifier);
System.exit(-1);
}
return result;
}
public BlockDescription getBlockDescription(Block block) {
@ -98,4 +135,8 @@ public class BlockModelLoader {
}
return description.prepare(block, adjacentBlocks);
}
public TextureLoader getTextureLoader() {
return textureLoader;
}
}

View File

@ -1,18 +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.blockModels;
public interface BlockRenderInterface {
void draw();
}

View File

@ -36,7 +36,7 @@ public class DrawDescription {
Map<FaceOrientation, Pair<Float, Float>> faces;
boolean full = false; // is the block a completely filled block?
public DrawDescription(JsonObject json) {
public DrawDescription(JsonObject json, String mod) {
if (!(json.has("parent") && json.has("textures"))) return;
faces = new HashMap<>();
@ -49,7 +49,7 @@ public class DrawDescription {
Pair<Float, Float> texturePair;
try {
texturePair = MainWindow.getRenderer().getTextureLoader().getTexture(texture);
texturePair = MainWindow.getRenderer().getModelLoader().getTextureLoader().getTexture(mod, texture);
} catch (Exception e) {
continue;
}

View File

@ -32,7 +32,7 @@ public class Face {
public Face(FaceOrientation orientation, Pair<Float, Float> texture, InFaceUV uv, SubBlock subBlock) {
this.orientation = orientation;
float step = MainWindow.getRenderer().getTextureLoader().getStep();
float step = MainWindow.getRenderer().getModelLoader().getTextureLoader().getStep();
u1 = texture.getKey();// + (float) uv.u1 / (float) texturePackRes * step;
u2 = texture.getValue();// - (float) (texturePackRes - uv.u2) / (float) texturePackRes * step;
v1 = (float) uv.v1 / (float) texturePackRes;

View File

@ -1,29 +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.blockModels;
import de.bixilon.minosoft.game.datatypes.objectLoader.blocks.Block;
import de.bixilon.minosoft.game.datatypes.world.BlockPosition;
import de.bixilon.minosoft.game.datatypes.world.World;
public class FullBlock implements BlockRenderInterface {
public FullBlock(BlockPosition position, Block block, World world) {
}
@Override
public void draw() {
}
}

View File

@ -14,19 +14,21 @@
package de.bixilon.minosoft.render.blockModels;
import com.google.gson.JsonObject;
import de.bixilon.minosoft.render.MainWindow;
import de.bixilon.minosoft.render.fullFace.FaceOrientation;
import de.bixilon.minosoft.render.fullFace.InFaceUV;
import de.bixilon.minosoft.render.texture.TextureLoader;
import javafx.util.Pair;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
public class SubBlock {
SubBlockPosition pos1; // the most negative Point of the SubBlock
SubBlockPosition pos2; // the most positive Point of the SubBlock
HashMap<FaceOrientation, Pair<Float, Float>> textures;
HashMap<FaceOrientation, Pair<Float, Float>> textureCoordinates;
HashMap<FaceOrientation, String> textures;
HashMap<FaceOrientation, Boolean> cullFaceTextures;
HashMap<FaceOrientation, InFaceUV> uv;
@ -35,6 +37,7 @@ public class SubBlock {
public SubBlock(JsonObject json, HashMap<String, String> variables) {
uv = new HashMap<>();
textures = new HashMap<>();
textureCoordinates = new HashMap<>();
cullFaceTextures = new HashMap<>();
pos1 = new SubBlockPosition(json.getAsJsonArray("from"));
@ -50,6 +53,7 @@ public class SubBlock {
}
private static String getRealTextureName(String textureName, HashMap<String, String> variables) {
// read the variables and find the real texture name
if (textureName.contains("#")) {
if (variables.containsKey(textureName)) {
String newName = variables.get(textureName);
@ -68,6 +72,15 @@ public class SubBlock {
}
}
public void applyTextures(String mod, TextureLoader loader) {
for (Map.Entry<FaceOrientation, String> entry : textures.entrySet()) {
Pair<Float, Float> texture = loader.getTexture(mod, entry.getValue());
textureCoordinates.put(entry.getKey(), texture);
}
// clean up
textures.clear();
}
private void applyTexture(JsonObject faceJson, FaceOrientation orientation, HashMap<String, String> variables) {
try {
uv.put(orientation, new InFaceUV(faceJson.getAsJsonArray("uv")));
@ -76,21 +89,18 @@ public class SubBlock {
}
String textureName = getRealTextureName(faceJson.get("texture").getAsString(), variables);
Pair<Float, Float> texture = MainWindow.getRenderer().getTextureLoader().getTexture(textureName);
textures.put(orientation, textureName);
cullFaceTextures.put(orientation, faceJson.has("cullface"));
textures.put(orientation, texture);
}
public HashSet<Face> getFaces(HashMap<FaceOrientation, Boolean> adjacentBlocks) {
HashSet<Face> result = new HashSet<>();
for (FaceOrientation orientation : FaceOrientation.values()) {
if (!textures.containsKey(orientation)) {
if (!textureCoordinates.containsKey(orientation)) {
continue;
}
if (!(adjacentBlocks.get(orientation) && cullFaceTextures.get(orientation))) {
result.add(new Face(orientation, textures.get(orientation),
result.add(new Face(orientation, textureCoordinates.get(orientation),
uv.get(orientation), this));
}
}
@ -100,4 +110,12 @@ public class SubBlock {
public boolean isFull() {
return isFull;
}
public HashSet<String> getTextures() {
HashSet<String> result = new HashSet<>();
for (Map.Entry<FaceOrientation, String> texture : textures.entrySet()) {
result.add(texture.getValue());
}
return result;
}
}

View File

@ -18,34 +18,34 @@ import de.matthiasmann.twl.utils.PNGDecoder;
import javafx.util.Pair;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.HashSet;
import java.util.Map;
import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.opengl.GL30.glGenerateMipmap;
public class TextureLoader {
int textureID;
int length; //describes the amount of loaded textures
private final int TEXTURE_PACK_RES = 16;
private HashMap<String, Integer> textureCoordinates;
int imageLength = 1;
private final HashMap<String, HashMap<String, Integer>> textureCoordinates;
int textureID;
float step;
int totalTextures = 0;
HashMap<String, HashMap<String, BufferedImage>> images;
public TextureLoader(long window) {
try {
textureCoordinates = new HashMap<>();
loadTextures(Config.homeDir + "assets/minecraft/textures/block");
} catch (IOException ioException) {
ioException.printStackTrace();
public TextureLoader(HashMap<String, HashSet<String>> textures, HashMap<String, HashMap<String, float[]>> tints) {
textureCoordinates = new HashMap<>();
images = new HashMap<>();
for (String mod : textures.keySet()) {
loadTextures(mod, textures.get(mod), tints.get(mod));
}
combineTextures();
try {
PNGDecoder decoder = new PNGDecoder(new FileInputStream(
Config.homeDir + "assets/allTextures.png"));
@ -54,59 +54,77 @@ public class TextureLoader {
textureID = bindTexture(buf, decoder.getWidth(), decoder.getHeight());
} catch (IOException e) {
e.printStackTrace();
System.exit(5);
}
}
private void loadTextures(String textureFolder) throws IOException {
// Any animated block will be stationary
File[] textureFiles = new File(textureFolder).listFiles();
if (textureFiles == null) {
throw new IOException("Failed to load textures: Texture folder empty");
}
List<Pair<BufferedImage, String>> allTextures = new ArrayList<>();
for (int i = 0; i < textureFiles.length; i++) {
String fileName = textureFiles[i].getName();
String textureName = fileName.substring(0, fileName.lastIndexOf('.'));
String fileExtension = fileName.substring(fileName.lastIndexOf('.') + 1);
if (fileExtension.equals("png")) {
InputStream textureInputStream = new FileInputStream(textureFiles[i]);
BufferedImage img = ImageIO.read(textureInputStream);
allTextures.add(new Pair<>(img, textureName));
private static void tintImage(BufferedImage image, float[] tintColor) {
for (int x = 0; x < image.getWidth(); x++) {
for (int y = 0; y < image.getHeight(); y++) {
Color color = new Color(image.getRGB(x, y));
int r = (int) (color.getRed() * tintColor[0]);
int g = (int) (color.getGreen() * tintColor[1]);
int b = (int) (color.getBlue() * tintColor[2]);
int rgba = (color.getAlpha() << 24) | (r << 16) | (g << 8) | b;
image.setRGB(x, y, rgba);
}
//else we have a .mcmeta file describing animated blocks
}
}
private void loadTextures(String mod, HashSet<String> textureNames, HashMap<String, float[]> tint) {
HashMap<String, BufferedImage> modTextureMap = new HashMap<>();
for (String textureName : textureNames) {
String path = Config.homeDir + "assets/" + mod + "/textures/" + textureName + ".png";
try {
BufferedImage image = ImageIO.read(new File(path));
if (tint.containsKey(textureName)) {
tintImage(image, tint.get(textureName));
}
modTextureMap.put(textureName, image);
} catch (IOException e) {
System.out.println(textureName);
System.out.println(path);
e.printStackTrace();
System.exit(6);
}
totalTextures++;
}
images.put(mod, modTextureMap);
}
private void combineTextures() {
// CONVERT ALL THE IMAGES INTO A SINGLE, VERY LONG IMAGE
// greatly improves performance in opengl
// TEXTURE_PACK_RESxTEXTURE_PACK_RES textures only
length = allTextures.size();
int imageLength = 1;
while (totalTextures * TEXTURE_PACK_RES > imageLength) {
imageLength *= 2; //figure out the right length for the image
}
BufferedImage totalImage = new BufferedImage(imageLength, TEXTURE_PACK_RES,
BufferedImage.TYPE_4BYTE_ABGR);
while (length * TEXTURE_PACK_RES > imageLength) imageLength *= 2; //figure out the right length for the image
BufferedImage totalImage = new BufferedImage(imageLength, TEXTURE_PACK_RES, BufferedImage.TYPE_4BYTE_ABGR);
for (int xPos = 0; xPos < length; xPos++) {
//copy the image into a part of the long image
BufferedImage img = allTextures.get(xPos).getKey();
for (int y = 0; y < TEXTURE_PACK_RES; y++) {
for (int xPixel = 0; xPixel < TEXTURE_PACK_RES; xPixel++) {
int rgb = img.getRGB(xPixel, y);
totalImage.setRGB(xPos * TEXTURE_PACK_RES + xPixel, y, rgb);
int currentPos = 0;
for (Map.Entry<String, HashMap<String, BufferedImage>> mod : images.entrySet()) {
HashMap<String, Integer> modMap = new HashMap<>();
for (Map.Entry<String, BufferedImage> texture : mod.getValue().entrySet()) {
for (int y = 0; y < TEXTURE_PACK_RES; y++) {
for (int xPixel = 0; xPixel < TEXTURE_PACK_RES; xPixel++) {
int rgb = texture.getValue().getRGB(xPixel, y);
totalImage.setRGB(currentPos * TEXTURE_PACK_RES + xPixel, y, rgb);
}
}
modMap.put(texture.getKey(), currentPos++);
}
String textureName = allTextures.get(xPos).getValue();
textureCoordinates.put(textureName, xPos);
textureCoordinates.put(mod.getKey(), modMap);
}
try {
// save our long image to reload it later
File outputFile = new File(Config.homeDir + "assets/allTextures.png");
ImageIO.write(totalImage, "png", outputFile);
} catch (IOException e) {
e.printStackTrace();
}
step = (float) 1 / (float) imageLength;
step = (float) 1 / (float) imageLength * TEXTURE_PACK_RES;
}
private int bindTexture(ByteBuffer buf, int width, int height) {
@ -123,24 +141,23 @@ public class TextureLoader {
return textureID;
}
public Pair<Float, Float> getTexture(String name) {
public Pair<Float, Float> getTexture(String mod, String textureName) {
// returns the start and end u-coordinate of a specific texture to access it
if (name == null) {
throw new NullPointerException("received null string as texture name");
}
if (name.contains("block/")) {
name = name.substring(name.lastIndexOf('/') + 1);
HashMap<String, Integer> modMap = textureCoordinates.get(mod);
if (modMap == null) {
System.out.println("no mod " + mod + " loaded");
System.exit(9);
}
Integer pos = modMap.get(textureName);
Integer pos = textureCoordinates.get(name);
if (pos == null) {
// the texture does not exist
throw new IllegalArgumentException(String.format("could not find texture %s", name));
System.out.println("failed to find texture " + textureName);
System.exit(10);
}
return new Pair<Float, Float>(
(float) pos / ((float) imageLength / (float) TEXTURE_PACK_RES),
(float) (pos + 1) / ((float) imageLength / (float) TEXTURE_PACK_RES)
return new Pair<>(
pos * step,
(pos + 1) * step
);
}

View File

@ -1,59 +1,120 @@
{
"stone": "regular",
"granite": "regular",
"polished_granite": "regular",
"diorite": "regular",
"polished_diorite": "regular",
"andesite": "regular",
"polished_andesite": "regular",
"grass_block": "regular",
"dirt": "regular",
"coarse_dirt": "regular",
"podzol": "regular",
"cobblestone": "regular",
"oak_planks": "regular",
"spruce_planks": "regular",
"birch_planks": "regular",
"jungle_planks": "regular",
"acacia_planks": "regular",
"dark_oak_planks": "regular",
"oak_sapling": "regular",
"spruce_sapling": "regular",
"birch_sapling": "regular",
"jungle_sapling": "regular",
"acacia_sapling": "regular",
"dark_oak_sapling": "regular",
"bedrock": "regular",
"sand": "regular",
"red_sand": "regular",
"gravel": "regular",
"gold_ore": "regular",
"iron_ore": "regular",
"coal_ore": "regular",
"spruce_log": "regular",
"birch_log": "regular",
"jungle_log": "regular",
"dark_oak_log": "regular",
"oak_leaves": "regular",
"spruce_leaves": "regular",
"jungle_leaves": "regular",
"birch_leaves": "regular",
"dark_oak_leaves": "regular",
"sponge": "regular",
"wet_sponge": "regular",
"glass": "regular",
"lapis_ore": "regular",
"lapis_block": "regular",
"dispenser": {
"orientation:vertical": "dispenser_vertical",
"else": "dispenser"
"blocks": {
"stone": "regular",
"granite": "regular",
"polished_granite": "regular",
"diorite": "regular",
"polished_diorite": "regular",
"andesite": "regular",
"polished_andesite": "regular",
"grass_block": "regular",
"dirt": "regular",
"coarse_dirt": "regular",
"podzol": "regular",
"cobblestone": "regular",
"oak_planks": "regular",
"spruce_planks": "regular",
"birch_planks": "regular",
"jungle_planks": "regular",
"acacia_planks": "regular",
"dark_oak_planks": "regular",
"oak_sapling": "regular",
"spruce_sapling": "regular",
"birch_sapling": "regular",
"jungle_sapling": "regular",
"acacia_sapling": "regular",
"dark_oak_sapling": "regular",
"bedrock": "regular",
"sand": "regular",
"red_sand": "regular",
"gravel": "regular",
"gold_ore": "regular",
"iron_ore": "regular",
"coal_ore": "regular",
"spruce_log": "regular",
"birch_log": "regular",
"jungle_log": "regular",
"dark_oak_log": "regular",
"oak_leaves": "regular",
"spruce_leaves": "regular",
"jungle_leaves": "regular",
"birch_leaves": "regular",
"dark_oak_leaves": "regular",
"sponge": "regular",
"wet_sponge": "regular",
"glass": "regular",
"lapis_ore": "regular",
"lapis_block": "regular",
"dispenser": {
"orientation:vertical": "dispenser_vertical",
"else": "dispenser"
},
"sandstone": "regular",
"chiseled_sandstone": "regular",
"smooth_sandstone": "regular",
"note_block": "regular",
"bed": "invisible",
"powered_rail": {
"else": "powered_rail",
"not_powered": "powered_rail",
"powered": "powered_rail_on",
"powered,ascending_south": "powered_rail_on_raised_ne",
"powered,ascending_north": "powered_rail_on_raised_ne",
"powered,ascending_east": "powered_rail_on_raised_sw",
"powered,ascending_west": "powered_rail_on_raised_sw",
"not_powered,ascending_south": "powered_rail_raised_ne",
"not_powered,ascending_north": "powered_rail_raised_ne",
"not_powered,ascending_east": "powered_rail_raised_sw",
"not_powered,ascending_west": "powered_rail_raised_sw"
},
"detector_rail": {
"else": "detector_rail",
"not_powered": "detector_rail",
"powered": "detector_rail_on",
"powered,ascending_south": "detector_rail_on_raised_ne",
"powered,ascending_north": "detector_rail_on_raised_ne",
"powered,ascending_east": "detector_rail_on_raised_sw",
"powered,ascending_west": "detector_rail_on_raised_sw",
"not_powered,ascending_south": "detector_rail_raised_ne",
"not_powered,ascending_north": "detector_rail_raised_ne",
"not_powered,ascending_east": "detector_rail_raised_sw",
"not_powered,ascending_west": "detector_rail_raised_sw"
},
"sticky_piston": "regular",
"cobweb": "regular",
"shrub": "dead_bush",
"grass": "regular",
"fern": "regular",
"dead_bush": "regular",
"piston": "regular",
"piston_head": "regular",
"white_wool": "regular",
"orange_wool": "regular",
"magenta_wool": "regular",
"light_blue_wool": "regular",
"yellow_wool": "regular",
"lime_wool": "regular",
"pink_wool": "regular",
"gray_wool": "regular",
"light_gray_wool": "regular",
"cyan_wool": "regular",
"purple_wool": "regular",
"blue_wool": "regular",
"brown_wool": "regular",
"green_wool": "regular",
"red_wool": "regular",
"black_wool": "regular",
"dandelion": "regular",
"poppy": "regular",
"blue_orchid": "regular",
"allium": "regular",
"azure_bluet": "regular"
},
"sandstone": "regular",
"chiseled_sandstone": "regular",
"smooth_sandstone": "regular",
"note_block": "regular",
"bed": "regular",
"powered_rail": {
""
"tinted_textures": {
"block/grass_block_top": [
0,
1,
0
]
}
}