diff --git a/models.txt b/models.txt index fe75c7d4..2816a091 100644 --- a/models.txt +++ b/models.txt @@ -39,61 +39,127 @@ layer:0 # Torch - up # Redstone torch on - up # Redstone torch off - up -block:id=50,id=75,id=76,data=5,data=0,scale=8 -layer:0,1,2,3,4 --------- --------- --------- ----**--- ----**--- --------- --------- --------- +block:id=50,id=75,id=76,data=5,data=0,scale=16 +layer:0,1,2,3,4,5,6,7,8,9 +---------------- +---------------- +---------------- +---------------- +---------------- +---------------- +---------------- +-------**------- +-------**------- +---------------- +---------------- +---------------- +---------------- +---------------- +---------------- +---------------- # Torch - pointing south # Redstone torch on - pointing south # Redstone torch off - pointing south -block:id=50,id=75,id=76,data=1,scale=8 -layer:2 ----**--- --------- --------- --------- --------- --------- --------- --------- +block:id=50,id=75,id=76,data=1,scale=16 layer:3,4 ----**--- ----**--- --------- --------- --------- --------- --------- --------- -layer:5 --------- ----**--- ----**--- --------- --------- --------- --------- --------- +-------**------- +---------------- +---------------- +---------------- +---------------- +---------------- +---------------- +---------------- +---------------- +---------------- +---------------- +---------------- +---------------- +---------------- +---------------- +---------------- +layer:5,6 +-------**------- +-------**------- +---------------- +---------------- +---------------- +---------------- +---------------- +---------------- +---------------- +---------------- +---------------- +---------------- +---------------- +---------------- +---------------- +---------------- +layer:7,8 +---------------- +-------**------- +-------**------- +---------------- +---------------- +---------------- +---------------- +---------------- +---------------- +---------------- +---------------- +---------------- +---------------- +---------------- +---------------- +---------------- +layer:9,10 +---------------- +---------------- +-------**------- +-------**------- +---------------- +---------------- +---------------- +---------------- +---------------- +---------------- +---------------- +---------------- +---------------- +---------------- +---------------- +---------------- +layer:11,12 +---------------- +---------------- +---------------- +-------**------- +-------**------- +---------------- +---------------- +---------------- +---------------- +---------------- +---------------- +---------------- +---------------- +---------------- +---------------- +---------------- # Torch - pointing north # Redstone torch on - pointing north # Redstone torch off - pointing north -block:id=50,id=75,id=76,data=2,scale=8 +block:id=50,id=75,id=76,data=2,scale=16 rotate:id=50,data=1,rot=180 # Torch - pointing west # Redstone torch on - pointing west # Redstone torch off - pointing west -block:id=50,id=75,id=76,data=3,scale=8 +block:id=50,id=75,id=76,data=3,scale=16 rotate:id=50,data=1,rot=90 # Torch - pointing east # Redstone torch on - pointing east # Redstone torch off - pointing east -block:id=50,id=75,id=76,data=4,scale=8 +block:id=50,id=75,id=76,data=4,scale=16 rotate:id=50,data=1,rot=270 # Fence - (data is faked: 1=north,2=east,4=south,8=west) # Fence - no neighbors @@ -1232,57 +1298,74 @@ layer:6,7 -------- Tall grass block:id=31,data=1,scale=16 -layer:0,1 --*-*-*-*-*-*-*-* -*-*-*-*-*-*-*-*- --*-*-*-*-*-*-*-* -*-*-*-*-*-*-*-*- --*-*-*-*-*-*-*-* -*-*-*-*-*-*-*-*- --*-*-*-*-*-*-*-* -*-*-*-*-*-*-*-*- --*-*-*-*-*-*-*-* -*-*-*-*-*-*-*-*- --*-*-*-*-*-*-*-* -*-*-*-*-*-*-*-*- --*-*-*-*-*-*-*-* -*-*-*-*-*-*-*-*- --*-*-*-*-*-*-*-* -*-*-*-*-*-*-*-*- -layer:2,3 ----*---*---*---* --*---*---*---*-- ----*---*---*---* --*---*---*---*-- ----*---*---*---* --*---*---*---*-- ----*---*---*---* --*---*---*---*-- ----*---*---*---* --*---*---*---*-- ----*---*---*---* --*---*---*---*-- ----*---*---*---* --*---*---*---*-- ----*---*---*---* --*---*---*---*-- -layer:4 ------*-------*-- --*-------*------ ------*-------*-- --*-------*------ ------*-------*-- --*-------*------ ------*-------*-- --*-------*------ ------*-------*-- --*-------*------ ------*-------*-- --*-------*------ ------*-------*-- --*-------*------ ------*-------*-- --*-------*------ +layer:0,1,2,3,4,5,6,7,8,9 +*--------------* +-*------------*- +--*----------*-- +---*--------*--- +----*------*---- +-----*----*----- +------*--*------ +-------**------- +-------**------- +------*--*------ +-----*----*----- +----*------*---- +---*--------*--- +--*----------*-- +-*------------*- +*--------------* +layer:10,11 +---------------- +-*------------*- +--*----------*-- +---*--------*--- +----*------*---- +-----*----*----- +------*--*------ +-------**------- +-------**------- +------*--*------ +-----*----*----- +----*------*---- +---*--------*--- +--*----------*-- +-*------------*- +---------------- +layer:12,13 +---------------- +---------------- +---------------- +---*--------*--- +----*------*---- +-----*----*----- +------*--*------ +---------------- +---------------- +------*--*------ +-----*----*----- +----*------*---- +---*--------*--- +---------------- +---------------- +---------------- +layer:14,15 +---------------- +---------------- +---------------- +---*--------*--- +---------------- +---------------- +------*--*------ +---------------- +---------------- +------*--*------ +---------------- +---------------- +---*--------*--- +---------------- +---------------- +---------------- # Sapling block:id=6,data=*,scale=8 layer:0 @@ -1340,71 +1423,111 @@ layer:5 -**-**-- -------- # Bed - head - pointing west -block:id=26,data=8,scale=8 -layer:0,1 --------- -*------- -*------- -*------- -*------- -*------- -*------- --------- -layer:2,3 --------- -******** -******** -******** -******** -******** -******** --------- -layer:4 --------- -*------- -*-**---- -*-**---- -*-**---- -*-**---- -*------- --------- +block:id=26,data=8,scale=16 +layer:0,1,2 +***------------- +***------------- +***------------- +---------------- +---------------- +---------------- +---------------- +---------------- +---------------- +---------------- +---------------- +---------------- +---------------- +***------------- +***------------- +***------------- +layer:3,4,5,6,7,8 +**************** +**************** +**************** +**************** +**************** +**************** +**************** +**************** +**************** +**************** +**************** +**************** +**************** +**************** +**************** +**************** +layer:9 +---------------- +-******--------- +-******--------- +-******--------- +-******--------- +-******--------- +-******--------- +-******--------- +-******--------- +-******--------- +-******--------- +-******--------- +-******--------- +-******--------- +-******--------- +---------------- # Bed - head - pointing north -block:id=26,data=9,scale=8 +block:id=26,data=9,scale=16 rotate:id=26,data=8,rot=90 # Bed - head - pointing east -block:id=26,data=10,scale=8 +block:id=26,data=10,scale=16 rotate:id=26,data=8,rot=180 # Bed - head - pointing south -block:id=26,data=11,scale=8 +block:id=26,data=11,scale=16 rotate:id=26,data=8,rot=270 # Bed - foot - pointing west -block:id=26,data=0,scale=8 -layer:0,1 --------- -------*- -------*- -------*- -------*- -------*- -------*- --------- -layer:2,3 --------- -*******- -*******- -*******- -*******- -*******- -*******- --------- +block:id=26,data=0,scale=16 +layer:0,1,2 +-------------*** +-------------*** +-------------*** +---------------- +---------------- +---------------- +---------------- +---------------- +---------------- +---------------- +---------------- +---------------- +---------------- +-------------*** +-------------*** +-------------*** +layer:3,4,5,6,7,8 +**************** +**************** +**************** +**************** +**************** +**************** +**************** +**************** +**************** +**************** +**************** +**************** +**************** +**************** +**************** +**************** # Bed - foot - pointing north -block:id=26,data=1,scale=8 +block:id=26,data=1,scale=16 rotate:id=26,data=0,rot=90 # Bed - foot - pointing east -block:id=26,data=2,scale=8 +block:id=26,data=2,scale=16 rotate:id=26,data=0,rot=180 # Bed - foot - pointing south -block:id=26,data=3,scale=8 +block:id=26,data=3,scale=16 rotate:id=26,data=0,rot=270 # Wooden/Iron Door - bottom - northeast corner hinge, not swung # Wooden/Iron Door - bottom - northwest corner hinge, swung diff --git a/shaders.txt b/shaders.txt index 0754d1d6..1ed6eb86 100644 --- a/shaders.txt +++ b/shaders.txt @@ -34,3 +34,8 @@ shaders: - class: org.dynmap.hdmap.CaveHDShader name: cave + + - class: org.dynmap.hdmap.TexturePackHDShader + name: stdtexture + texturepack: standard + \ No newline at end of file diff --git a/src/main/assembly/package.xml b/src/main/assembly/package.xml index 6335a1fd..417ad924 100644 --- a/src/main/assembly/package.xml +++ b/src/main/assembly/package.xml @@ -32,7 +32,11 @@ perspectives.txt lightings.txt configuration.txt.sample-hd - models.txt + models.txt + texture.txt + + ${project.basedir}/texturepacks + /dynmap/texturepacks diff --git a/src/main/java/org/dynmap/Color.java b/src/main/java/org/dynmap/Color.java index 636d5267..8269c67c 100644 --- a/src/main/java/org/dynmap/Color.java +++ b/src/main/java/org/dynmap/Color.java @@ -55,4 +55,20 @@ public class Color { public final void setAlpha(int v) { val = (val & 0x00FFFFFF) | (v << 24); } + /** + * Scale each color component, based on the corresponding component + */ + public final void blendColor(Color c) { + blendColor(c.val); + } + /** + * Scale each color component, based on the corresponding component + */ + public final void blendColor(int argb) { + int nval = (((((val >> 24) & 0xFF) * ((argb >> 24) & 0xFF)) / 255) << 24); + nval = nval | (((((val >> 16) & 0xFF) * ((argb >> 16) & 0xFF)) / 255) << 16); + nval = nval | (((((val >> 8) & 0xFF) * ((argb >> 8) & 0xFF)) / 255) << 8); + nval = nval | (((val & 0xFF) * (argb & 0xFF)) / 255); + val = nval; + } } diff --git a/src/main/java/org/dynmap/DynmapPlugin.java b/src/main/java/org/dynmap/DynmapPlugin.java index 6e9a7fb5..c61f8ff5 100644 --- a/src/main/java/org/dynmap/DynmapPlugin.java +++ b/src/main/java/org/dynmap/DynmapPlugin.java @@ -35,6 +35,7 @@ import org.bukkit.plugin.java.JavaPlugin; import org.dynmap.debug.Debug; import org.dynmap.debug.Debugger; import org.dynmap.hdmap.HDBlockModels; +import org.dynmap.hdmap.TexturePack; import org.dynmap.permissions.NijikokunPermissions; import org.dynmap.permissions.OpPermissions; import org.dynmap.permissions.PermissionProvider; @@ -79,6 +80,9 @@ public class DynmapPlugin extends JavaPlugin { dataDirectory = this.getDataFolder(); /* Load block models */ HDBlockModels.loadModels(dataDirectory); + /* Load texture mappings */ + TexturePack.loadTextureMapping(dataDirectory); + org.bukkit.util.config.Configuration bukkitConfiguration = new org.bukkit.util.config.Configuration(new File(this.getDataFolder(), "configuration.txt")); bukkitConfiguration.load(); configuration = new ConfigurationNode(bukkitConfiguration); diff --git a/src/main/java/org/dynmap/MapManager.java b/src/main/java/org/dynmap/MapManager.java index 350f2c84..aa04e1cf 100644 --- a/src/main/java/org/dynmap/MapManager.java +++ b/src/main/java/org/dynmap/MapManager.java @@ -317,7 +317,8 @@ public class MapManager { private class DoZoomOutProcessing implements Runnable { public void run() { Debug.debug("DoZoomOutProcessing started"); - for(DynmapWorld w : worlds) { + ArrayList wl = new ArrayList(worlds); + for(DynmapWorld w : wl) { w.freshenZoomOutFiles(); } renderpool.schedule(this, zoomout_period, TimeUnit.SECONDS); @@ -362,7 +363,7 @@ public class MapManager { } scheduler.scheduleSyncRepeatingTask(plugin, new CheckWorldTimes(), 5*20, 5*20); /* Check very 5 seconds */ - scheduler.scheduleSyncRepeatingTask(plugin, new ProcessChunkLoads(), 1, 1); /* Chunk loader task */ + scheduler.scheduleSyncRepeatingTask(plugin, new ProcessChunkLoads(), 1, 2); /* Chunk loader task - do every 2 to work around bukkit issue */ } diff --git a/src/main/java/org/dynmap/hdmap/HDBlockModels.java b/src/main/java/org/dynmap/hdmap/HDBlockModels.java index 9781d00d..5eb7b72f 100644 --- a/src/main/java/org/dynmap/hdmap/HDBlockModels.java +++ b/src/main/java/org/dynmap/hdmap/HDBlockModels.java @@ -1,7 +1,9 @@ package org.dynmap.hdmap; import java.io.File; +import java.io.FileOutputStream; import java.io.FileReader; +import java.io.FileWriter; import java.io.IOException; import java.io.LineNumberReader; import java.util.ArrayList; @@ -133,6 +135,7 @@ public class HDBlockModels { } else { /* Else, see how much is in first one */ weights[idx] = (offsets[idx] + res) - v; + weights[idx] = (offsets[idx]*res + res) - v; } } /* Now, use weights and indices to fill in scaled map */ @@ -182,7 +185,7 @@ public class HDBlockModels { weights[idx] = res; } else { /* Else, see how much is in first one */ - weights[idx] = (offsets[idx] + nativeres) - v; + weights[idx] = (offsets[idx]*nativeres + nativeres) - v; } } /* Now, use weights and indices to fill in scaled map */ @@ -255,9 +258,28 @@ public class HDBlockModels { return model; } /** - * Load models from model.txt file + * Load models */ - public static void loadModels(File plugindir) { + public static void loadModels(File datadir) { + /* Load block models */ + HDBlockModels.loadModelFile(new File(datadir, "models.txt")); + File custom = new File(datadir, "custom-models.txt"); + if(custom.canRead()) { + HDBlockModels.loadModels(custom); + } + else { + try { + FileWriter fw = new FileWriter(custom); + fw.write("# The user is free to add new and custom models here - Dynmap's install will not overwrite it\n"); + fw.close(); + } catch (IOException iox) { + } + } + } + /** + * Load models from file + */ + private static void loadModelFile(File modelfile) { LineNumberReader rdr = null; int cnt = 0; try { @@ -266,7 +288,7 @@ public class HDBlockModels { int layerbits = 0; int rownum = 0; int scale = 0; - rdr = new LineNumberReader(new FileReader(new File(plugindir, "models.txt"))); + rdr = new LineNumberReader(new FileReader(modelfile)); while((line = rdr.readLine()) != null) { if(line.startsWith("block:")) { ArrayList blkids = new ArrayList(); @@ -299,7 +321,7 @@ public class HDBlockModels { } } else { - Log.severe("Block model missing required parameters = line " + rdr.getLineNumber() + " of models.txt"); + Log.severe("Block model missing required parameters = line " + rdr.getLineNumber() + " of " + modelfile.getPath()); } layerbits = 0; } @@ -378,11 +400,11 @@ public class HDBlockModels { } } } - Log.info("Loaded " + cnt + " block models"); + Log.verboseinfo("Loaded " + cnt + " block models from " + modelfile.getPath()); } catch (IOException iox) { Log.severe("Error reading models.txt - " + iox.toString()); } catch (NumberFormatException nfx) { - Log.severe("Format error - line " + rdr.getLineNumber() + " of models.txt"); + Log.severe("Format error - line " + rdr.getLineNumber() + " of " + modelfile.getPath()); } finally { if(rdr != null) { try { diff --git a/src/main/java/org/dynmap/hdmap/HDMapManager.java b/src/main/java/org/dynmap/hdmap/HDMapManager.java index 590d76f3..654a6317 100644 --- a/src/main/java/org/dynmap/hdmap/HDMapManager.java +++ b/src/main/java/org/dynmap/hdmap/HDMapManager.java @@ -1,5 +1,8 @@ package org.dynmap.hdmap; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -23,7 +26,7 @@ public class HDMapManager { public HashMap lightings = new HashMap(); public HashSet maps = new HashSet(); public HashMap> maps_by_world_perspective = new HashMap>(); - + public void loadHDShaders(ConfigurationNode shadercfg) { Log.verboseinfo("Loading shaders..."); for(HDShader shader : shadercfg.createInstances("shaders", new Class[0], new Object[0])) { diff --git a/src/main/java/org/dynmap/hdmap/HDPerspective.java b/src/main/java/org/dynmap/hdmap/HDPerspective.java index af8b1d92..b6bbe31a 100644 --- a/src/main/java/org/dynmap/hdmap/HDPerspective.java +++ b/src/main/java/org/dynmap/hdmap/HDPerspective.java @@ -26,6 +26,7 @@ public interface HDPerspective { public boolean isBlockTypeDataNeeded(); double getScale(); + int getModelScale(); public void addClientConfiguration(JSONObject mapObject); } diff --git a/src/main/java/org/dynmap/hdmap/HDPerspectiveState.java b/src/main/java/org/dynmap/hdmap/HDPerspectiveState.java index 4903766c..12b52cfd 100644 --- a/src/main/java/org/dynmap/hdmap/HDPerspectiveState.java +++ b/src/main/java/org/dynmap/hdmap/HDPerspectiveState.java @@ -56,4 +56,8 @@ public interface HDPerspectiveState { * Return submodel alpha value (-1 if no submodel rendered) */ int getSubmodelAlpha(); + /** + * Return subblock coordinates of current ray position + */ + int[] getSubblockCoord(); } diff --git a/src/main/java/org/dynmap/hdmap/IsoHDPerspective.java b/src/main/java/org/dynmap/hdmap/IsoHDPerspective.java index 012030fa..03677759 100644 --- a/src/main/java/org/dynmap/hdmap/IsoHDPerspective.java +++ b/src/main/java/org/dynmap/hdmap/IsoHDPerspective.java @@ -88,6 +88,8 @@ public class IsoHDPerspective implements HDPerspective { double t_next_y, t_next_x, t_next_z; boolean nonairhit; int subalpha; + double mt; + int[] subblock_xyz = new int[3]; /** * Get sky light level - only available if shader requested it */ @@ -363,7 +365,7 @@ public class IsoHDPerspective implements HDPerspective { private boolean raytraceSubblock(short[] model) { int mx = 0, my = 0, mz = 0; double xx, yy, zz; - double mt = t + 0.0000001; + mt = t + 0.0000001; xx = top.x + mt *(bottom.x - top.x); yy = top.y + mt *(bottom.y - top.y); zz = top.z + mt *(bottom.z - top.z); @@ -453,7 +455,18 @@ public class IsoHDPerspective implements HDPerspective { } return true; } - + public int[] getSubblockCoord() { + double tt = t + 0.000001; + if(subalpha >= 0) + tt = mt; + double xx = top.x + tt * (bottom.x - top.x); + double yy = top.y + tt * (bottom.y - top.y); + double zz = top.z + tt * (bottom.z - top.z); + subblock_xyz[0] = (int)((xx - Math.floor(xx)) * modscale); + subblock_xyz[1] = (int)((yy - Math.floor(yy)) * modscale); + subblock_xyz[2] = (int)((zz - Math.floor(zz)) * modscale); + return subblock_xyz; + } } public IsoHDPerspective(ConfigurationNode configuration) { @@ -856,6 +869,10 @@ public class IsoHDPerspective implements HDPerspective { return scale; } + public int getModelScale() { + return modscale; + } + @Override public String getName() { return name; diff --git a/src/main/java/org/dynmap/hdmap/TexturePack.java b/src/main/java/org/dynmap/hdmap/TexturePack.java new file mode 100644 index 00000000..a72d0f2c --- /dev/null +++ b/src/main/java/org/dynmap/hdmap/TexturePack.java @@ -0,0 +1,805 @@ +package org.dynmap.hdmap; + +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.LineNumberReader; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; + +import javax.imageio.ImageIO; + +import org.dynmap.Color; +import org.dynmap.DynmapPlugin; +import org.dynmap.Log; +import org.dynmap.hdmap.HDPerspectiveState.BlockStep; +import org.dynmap.kzedmap.KzedMap; +import org.dynmap.utils.MapIterator; + +/** + * Loader and processor class for minecraft texture packs + * Texture packs are found in dynmap/texturepacks directory, and either are either ZIP files + * or are directories whose content matches the structure of a zipped texture pack: + * ./terrain.png - main color data (required) + * misc/water.png - still water tile (required)) + * misc/grasscolor.png - tone for grass color, biome sensitive (required) + * misc/foliagecolor.png - tone for leaf color, biome sensitive (required) + * misc/watercolor.png - tone for water color, biome sensitive (required) + * custom_lava_still.png - custom still lava animation (optional) + * custom_lava_flowing.png - custom flowing lava animation (optional) + * custom_water_still.png - custom still water animation (optional) + * custom_water_flowing.png - custom flowing water animation (optional) + * BetterGlass/*.png - mod-based improved windows (future optional) + */ +public class TexturePack { + /* Loaded texture packs */ + private static HashMap packs = new HashMap(); + + private static final String TERRAIN_PNG = "terrain.png"; + private static final String GRASSCOLOR_PNG = "misc/grasscolor.png"; + private static final String FOLIAGECOLOR_PNG = "misc/foliagecolor.png"; + private static final String WATERCOLOR_PNG = "misc/watercolor.png"; + private static final String WATER_PNG = "misc/water.png"; + private static final String CUSTOMLAVASTILL_PNG = "custom_lava_still.png"; + private static final String CUSTOMLAVAFLOWING_PNG = "custom_lava_flowing.png"; + private static final String CUSTOMWATERSTILL_PNG = "custom_water_still.png"; + private static final String CUSTOMWATERFLOWING_PNG = "custom_water_flowing.png"; + + /* Color modifier codes (x1000 for value in mapping code) */ + private static final int COLORMOD_GRASSTONED = 1; + private static final int COLORMOD_FOLIAGETONED = 2; + private static final int COLORMOD_WATERTONED = 3; + private static final int COLORMOD_ROT90 = 4; + private static final int COLORMOD_ROT180 = 5; + private static final int COLORMOD_ROT270 = 6; + private static final int COLORMOD_FLIPHORIZ = 7; + private static final int COLORMOD_SHIFTDOWNHALF = 8; + private static final int COLORMOD_SHIFTDOWNHALFANDFLIPHORIZ = 9; + private static final int COLORMOD_INCLINEDTORCH = 10; + private static final int COLORMOD_GRASSSIDE = 11; + private static final int COLORMOD_CLEARINSIDE = 12; + + /* Special tile index values */ + private static final int BLOCKINDEX_BLANK = -1; + private static final int BLOCKINDEX_STATIONARYWATER = 257; + private static final int BLOCKINDEX_MOVINGWATER = 258; + private static final int BLOCKINDEX_STATIONARYLAVA = 259; + private static final int BLOCKINDEX_MOVINGLAVA = 260; + private static final int MAX_BLOCKINDEX = 260; + private static final int BLOCKTABLELEN = MAX_BLOCKINDEX+1; + + private int[][] terrain_argb; + private int terrain_width, terrain_height; + private int native_scale; + + private int[] grasscolor_argb; + private int grasscolor_width, grasscolor_height; + private int trivial_grasscolor; + + private int[] foliagecolor_argb; + private int foliagecolor_width, foliagecolor_height; + private int trivial_foliagecolor; + + private int[] watercolor_argb; + private int watercolor_width, watercolor_height; + private int trivial_watercolor; + + private int[] water_argb; + private int water_width, water_height; + + private HashMap scaled_textures; + + + public static class HDTextureMap { + private int faces[]; /* index in terrain.png of image for each face (indexed by BlockStep.ordinal()) */ + private List blockids; + private int databits; + private static HDTextureMap[] texmaps; + + private static void initializeTable() { + texmaps = new HDTextureMap[16*BLOCKTABLELEN]; + HDTextureMap blank = new HDTextureMap(); + for(int i = 0; i < texmaps.length; i++) + texmaps[i] = blank; + } + + private HDTextureMap() { + blockids = Collections.singletonList(Integer.valueOf(0)); + databits = 0xFFFF; + faces = new int[] { -1, -1, -1, -1, -1, -1 }; + + for(int i = 0; i < texmaps.length; i++) { + texmaps[i] = this; + } + } + + public HDTextureMap(List blockids, int databits, int[] faces) { + this.faces = faces; + this.blockids = blockids; + this.databits = databits; + } + + public void addToTable() { + /* Add entries to lookup table */ + for(Integer blkid : blockids) { + for(int i = 0; i < 16; i++) { + if((databits & (1 << i)) != 0) { + texmaps[16*blkid + i] = this; + } + } + } + } + + public static HDTextureMap getMap(int blkid, int blkdata) { + return texmaps[(blkid<<4) + blkdata]; + } + } + /** Get or load texture pack */ + public static TexturePack getTexturePack(String tpname) { + TexturePack tp = packs.get(tpname); + if(tp != null) + return tp; + try { + tp = new TexturePack(tpname); /* Attempt to load pack */ + packs.put(tpname, tp); + return tp; + } catch (FileNotFoundException fnfx) { + Log.severe("Error loading texture pack '" + tpname + "' - not found"); + } + return null; + } + /** + * Constructor for texture pack, by name + */ + private TexturePack(String tpname) throws FileNotFoundException { + ZipFile zf = null; + File texturedir = getTexturePackDirectory(); + try { + /* Try to open zip */ + zf = new ZipFile(new File(texturedir, tpname + ".zip")); + /* Find and load terrain.png */ + ZipEntry ze = zf.getEntry(TERRAIN_PNG); /* Try to find terrain.png */ + if(ze == null) { + throw new FileNotFoundException(); + } + InputStream is = zf.getInputStream(ze); /* Get input stream for terrain.png */ + loadTerrainPNG(is); + is.close(); + /* Try to find and load misc/grasscolor.png */ + ze = zf.getEntry(GRASSCOLOR_PNG); + if(ze == null) + throw new FileNotFoundException(); + is = zf.getInputStream(ze); + loadGrassColorPNG(is); + is.close(); + /* Try to find and load misc/foliagecolor.png */ + ze = zf.getEntry(FOLIAGECOLOR_PNG); + if(ze == null) + throw new FileNotFoundException(); + is = zf.getInputStream(ze); + loadFoliageColorPNG(is); + is.close(); + /* Try to find and load misc/watercolor.png */ + ze = zf.getEntry(WATERCOLOR_PNG); + if(ze == null) + throw new FileNotFoundException(); + is = zf.getInputStream(ze); + loadWaterColorPNG(is); + is.close(); + /* Try to find and load misc/water.png */ + ze = zf.getEntry(WATER_PNG); + if(ze == null) + throw new FileNotFoundException(); + is = zf.getInputStream(ze); + loadWaterPNG(is); + is.close(); + + zf.close(); + return; + } catch (IOException iox) { + if(zf != null) { + try { zf.close(); } catch (IOException io) {} + } + /* No zip, or bad - try directory next */ + } + /* Try loading terrain.png from directory of name */ + File f = null; + FileInputStream fis = null; + try { + /* Open and load terrain.png */ + f = new File(texturedir, tpname + "/" + TERRAIN_PNG); + fis = new FileInputStream(f); + loadTerrainPNG(fis); + fis.close(); + /* Check for misc/grasscolor.png */ + f = new File(texturedir, tpname + "/" + GRASSCOLOR_PNG); + fis = new FileInputStream(f); + loadGrassColorPNG(fis); + fis.close(); + /* Check for misc/foliagecolor.png */ + f = new File(texturedir, tpname + "/" + FOLIAGECOLOR_PNG); + fis = new FileInputStream(f); + loadFoliageColorPNG(fis); + fis.close(); + /* Check for misc/waterecolor.png */ + f = new File(texturedir, tpname + "/" + WATERCOLOR_PNG); + fis = new FileInputStream(f); + loadWaterColorPNG(fis); + fis.close(); + /* Check for misc/water.png */ + f = new File(texturedir, tpname + "/" + WATER_PNG); + fis = new FileInputStream(f); + loadWaterPNG(fis); + fis.close(); + } catch (IOException iox) { + if(fis != null) { + try { fis.close(); } catch (IOException io) {} + } + throw new FileNotFoundException(); + } + } + /* Copy texture pack */ + private TexturePack(TexturePack tp) { + this.terrain_argb = new int[tp.terrain_argb.length][]; + System.arraycopy(tp.terrain_argb, 0, this.terrain_argb, 0, this.terrain_argb.length); + this.terrain_width = tp.terrain_width; + this.terrain_height = tp.terrain_height; + this.native_scale = tp.native_scale; + + this.grasscolor_argb = tp.grasscolor_argb; + this.grasscolor_height = tp.grasscolor_height; + this.grasscolor_width = tp.grasscolor_width; + this.trivial_grasscolor = tp.trivial_grasscolor; + + this.watercolor_argb = tp.watercolor_argb; + this.watercolor_height = tp.watercolor_height; + this.watercolor_width = tp.watercolor_width; + this.trivial_watercolor = tp.trivial_watercolor; + + this.foliagecolor_argb = tp.foliagecolor_argb; + this.foliagecolor_height = tp.foliagecolor_height; + this.foliagecolor_width = tp.foliagecolor_width; + this.trivial_foliagecolor = tp.trivial_foliagecolor; + + this.water_argb = tp.water_argb; + this.water_height = tp.water_height; + this.water_width = tp.water_width; + } + + /* Load terrain.png */ + private void loadTerrainPNG(InputStream is) throws IOException { + int i; + /* Load image */ + BufferedImage img = ImageIO.read(is); + if(img == null) { throw new FileNotFoundException(); } + terrain_width = img.getWidth(); + terrain_height = img.getHeight(); + native_scale = terrain_width / 16; + terrain_argb = new int[BLOCKTABLELEN][]; + for(i = 0; i < 256; i++) { + terrain_argb[i] = new int[native_scale*native_scale]; + img.getRGB((i & 0xF)*native_scale, (i>>4)*native_scale, native_scale, native_scale, terrain_argb[i], 0, native_scale); + } + int[] blank = new int[native_scale*native_scale]; + for(i = 256; i < BLOCKTABLELEN; i++) { + terrain_argb[i] = blank; + } + /* Fallbacks */ + terrain_argb[BLOCKINDEX_STATIONARYLAVA] = terrain_argb[255]; + terrain_argb[BLOCKINDEX_MOVINGLAVA] = terrain_argb[255]; + + img.flush(); + } + + /* Load misc/grasscolor.png */ + private void loadGrassColorPNG(InputStream is) throws IOException { + /* Load image */ + BufferedImage img = ImageIO.read(is); + if(img == null) { throw new FileNotFoundException(); } + grasscolor_width = img.getWidth(); + grasscolor_height = img.getHeight(); + grasscolor_argb = new int[grasscolor_width * grasscolor_height]; + img.getRGB(0, 0, grasscolor_width, grasscolor_height, grasscolor_argb, 0, grasscolor_width); + img.flush(); + /* Figure out trivial color */ + trivial_grasscolor = grasscolor_argb[grasscolor_height*grasscolor_width*3/4 + grasscolor_width/2]; + boolean same = true; + for(int j = 0; same && (j < grasscolor_height); j++) { + for(int i = 0; same && (i <= j); i++) { + if(grasscolor_argb[grasscolor_width*j+i] != trivial_grasscolor) + same = false; + } + } + /* All the same - no biome lookup needed */ + if(same) + grasscolor_argb = null; + } + + /* Load misc/foliagecolor.png */ + private void loadFoliageColorPNG(InputStream is) throws IOException { + /* Load image */ + BufferedImage img = ImageIO.read(is); + if(img == null) { throw new FileNotFoundException(); } + foliagecolor_width = img.getWidth(); + foliagecolor_height = img.getHeight(); + foliagecolor_argb = new int[foliagecolor_width * foliagecolor_height]; + img.getRGB(0, 0, foliagecolor_width, foliagecolor_height, foliagecolor_argb, 0, foliagecolor_width); + img.flush(); + /* Figure out trivial color */ + trivial_foliagecolor = foliagecolor_argb[foliagecolor_height*foliagecolor_width*3/4 + foliagecolor_width/2]; + boolean same = true; + for(int j = 0; same && (j < foliagecolor_height); j++) { + for(int i = 0; same && (i <= j); i++) { + if(foliagecolor_argb[foliagecolor_width*j+i] != trivial_foliagecolor) + same = false; + } + } + /* All the same - no biome lookup needed */ + if(same) + foliagecolor_argb = null; + } + + /* Load misc/watercolor.png */ + private void loadWaterColorPNG(InputStream is) throws IOException { + /* Load image */ + BufferedImage img = ImageIO.read(is); + if(img == null) { throw new FileNotFoundException(); } + watercolor_width = img.getWidth(); + watercolor_height = img.getHeight(); + watercolor_argb = new int[watercolor_width * watercolor_height]; + img.getRGB(0, 0, watercolor_width, watercolor_height, watercolor_argb, 0, watercolor_width); + img.flush(); + /* Figure out trivial color */ + trivial_watercolor = watercolor_argb[watercolor_height*watercolor_width*3/4 + watercolor_width/2]; + boolean same = true; + for(int j = 0; same && (j < watercolor_height); j++) { + for(int i = 0; same && (i <= j); i++) { + if(watercolor_argb[watercolor_width*j+i] != trivial_watercolor) + same = false; + } + } + /* All the same - no biome lookup needed */ + if(same) + watercolor_argb = null; + } + + /* Load misc/water.png */ + private void loadWaterPNG(InputStream is) throws IOException { + /* Load image */ + BufferedImage img = ImageIO.read(is); + if(img == null) { throw new FileNotFoundException(); } + water_width = img.getWidth(); + water_height = img.getHeight(); + water_argb = new int[water_width * water_height]; + img.getRGB(0, 0, water_width, water_height, water_argb, 0, water_width); + img.flush(); + /* Now, patch in to block table */ + int new_water_argb[] = new int[native_scale*native_scale]; + scaleTerrainPNGSubImage(water_width, native_scale, water_argb, new_water_argb); + terrain_argb[BLOCKINDEX_STATIONARYWATER] = new_water_argb; + terrain_argb[BLOCKINDEX_MOVINGWATER] = new_water_argb; + } + /* Get texture pack directory */ + private static File getTexturePackDirectory() { + return new File(DynmapPlugin.dataDirectory, "texturepacks"); + } + + /** + * Resample terrain pack for given scale, and return copy using that scale + */ + public TexturePack resampleTexturePack(int scale) { + if(scaled_textures == null) scaled_textures = new HashMap(); + TexturePack stp = scaled_textures.get(scale); + if(stp != null) + return stp; + stp = new TexturePack(this); /* Make copy */ + /* Scale terrain.png, if needed */ + if(stp.native_scale != scale) { + stp.native_scale = scale; + stp.terrain_height = 16*scale; + stp.terrain_width = 16*scale; + scaleTerrainPNG(stp); + } + /* Remember it */ + scaled_textures.put(scale, stp); + return stp; + } + /** + * Scale out terrain_argb into the terrain_argb of the provided destination, matching the scale of that destination + * @param tp + */ + private void scaleTerrainPNG(TexturePack tp) { + tp.terrain_argb = new int[256][]; + /* Terrain.png is 16x16 array of images : process one at a time */ + for(int idx = 0; idx < 256; idx++) { + tp.terrain_argb[idx] = new int[tp.native_scale*tp.native_scale]; + scaleTerrainPNGSubImage(native_scale, tp.native_scale, terrain_argb[idx], tp.terrain_argb[idx]); + } + } + private static void scaleTerrainPNGSubImage(int srcscale, int destscale, int[] src_argb, int[] dest_argb) { + int nativeres = srcscale; + int res = destscale; + Color c = new Color(); + /* Same size, so just copy */ + if(res == nativeres) { + System.arraycopy(src_argb, 0, dest_argb, 0, src_argb.length); + } + /* If we're scaling larger source pixels into smaller pixels, each destination pixel + * receives input from 1 or 2 source pixels on each axis + */ + else if(res > nativeres) { + int weights[] = new int[res]; + int offsets[] = new int[res]; + /* LCM of resolutions is used as length of line (res * nativeres) + * Each native block is (res) long, each scaled block is (nativeres) long + * Each scaled block overlaps 1 or 2 native blocks: starting with native block 'offsets[]' with + * 'weights[]' of its (res) width in the first, and the rest in the second + */ + for(int v = 0, idx = 0; v < res*nativeres; v += nativeres, idx++) { + offsets[idx] = (v/res); /* Get index of the first native block we draw from */ + if((v+nativeres-1)/res == offsets[idx]) { /* If scaled block ends in same native block */ + weights[idx] = nativeres; + } + else { /* Else, see how much is in first one */ + weights[idx] = (offsets[idx]*res + res) - v; + } + } + /* Now, use weights and indices to fill in scaled map */ + for(int y = 0, off = 0; y < res; y++) { + int ind_y = offsets[y]; + int wgt_y = weights[y]; + for(int x = 0; x < res; x++, off++) { + int ind_x = offsets[x]; + int wgt_x = weights[x]; + int accum_red = 0; + int accum_green = 0; + int accum_blue = 0; + int accum_alpha = 0; + for(int xx = 0; xx < 2; xx++) { + int wx = (xx==0)?wgt_x:(nativeres-wgt_x); + if(wx == 0) continue; + for(int yy = 0; yy < 2; yy++) { + int wy = (yy==0)?wgt_y:(nativeres-wgt_y); + if(wy == 0) continue; + /* Accumulate */ + c.setARGB(src_argb[(ind_y+yy)*nativeres + ind_x + xx]); + accum_red += c.getRed() * wx * wy; + accum_green += c.getGreen() * wx * wy; + accum_blue += c.getBlue() * wx * wy; + accum_alpha += c.getAlpha() * wx * wy; + } + } + /* Generate weighted compnents into color */ + c.setRGBA(accum_red / (nativeres*nativeres), accum_green / (nativeres*nativeres), + accum_blue / (nativeres*nativeres), accum_alpha / (nativeres*nativeres)); + dest_argb[(y*res) + x] = c.getARGB(); + } + } + } + else { /* nativeres > res */ + int weights[] = new int[nativeres]; + int offsets[] = new int[nativeres]; + /* LCM of resolutions is used as length of line (res * nativeres) + * Each native block is (res) long, each scaled block is (nativeres) long + * Each native block overlaps 1 or 2 scaled blocks: starting with scaled block 'offsets[]' with + * 'weights[]' of its (res) width in the first, and the rest in the second + */ + for(int v = 0, idx = 0; v < res*nativeres; v += res, idx++) { + offsets[idx] = (v/nativeres); /* Get index of the first scaled block we draw to */ + if((v+res-1)/nativeres == offsets[idx]) { /* If native block ends in same scaled block */ + weights[idx] = res; + } + else { /* Else, see how much is in first one */ + weights[idx] = (offsets[idx]*nativeres + nativeres) - v; + } + } + int accum_red[] = new int[res*res]; + int accum_green[] = new int[res*res]; + int accum_blue[] = new int[res*res]; + int accum_alpha[] = new int[res*res]; + + /* Now, use weights and indices to fill in scaled map */ + for(int y = 0; y < nativeres; y++) { + int ind_y = offsets[y]; + int wgt_y = weights[y]; + for(int x = 0; x < nativeres; x++) { + int ind_x = offsets[x]; + int wgt_x = weights[x]; + c.setARGB(src_argb[(y*nativeres) + x]); + for(int xx = 0; xx < 2; xx++) { + int wx = (xx==0)?wgt_x:(res-wgt_x); + if(wx == 0) continue; + for(int yy = 0; yy < 2; yy++) { + int wy = (yy==0)?wgt_y:(res-wgt_y); + if(wy == 0) continue; + accum_red[(ind_y+yy)*res + (ind_x+xx)] += c.getRed() * wx * wy; + accum_green[(ind_y+yy)*res + (ind_x+xx)] += c.getGreen() * wx * wy; + accum_blue[(ind_y+yy)*res + (ind_x+xx)] += c.getBlue() * wx * wy; + accum_alpha[(ind_y+yy)*res + (ind_x+xx)] += c.getAlpha() * wx * wy; + } + } + } + } + /* Produce normalized scaled values */ + for(int y = 0; y < res; y++) { + for(int x = 0; x < res; x++) { + int off = (y*res) + x; + c.setRGBA(accum_red[off]/(nativeres*nativeres), accum_green[off]/(nativeres*nativeres), + accum_blue[off]/(nativeres*nativeres), accum_alpha[off]/(nativeres*nativeres)); + dest_argb[y*res + x] = c.getARGB(); + } + } + } + } + public void saveTerrainPNG(File f) throws IOException { + int[] outbuf = new int[256*native_scale*native_scale]; + for(int i = 0; i < 256; i++) { + for(int y = 0; y < native_scale; y++) { + System.arraycopy(terrain_argb[i],native_scale*y,outbuf,((i>>4)*native_scale+y)*terrain_width + (i & 0xF)*native_scale, native_scale); + } + } + BufferedImage img = KzedMap.createBufferedImage(outbuf, terrain_width, terrain_height); + ImageIO.write(img, "png", f); + } + + /** + * Load texture pack mappings + */ + public static void loadTextureMapping(File datadir) { + /* Load block models */ + loadTextureFile(new File(datadir, "texture.txt")); + File custom = new File(datadir, "custom-texture.txt"); + if(custom.canRead()) { + loadTextureFile(custom); + } + else { + try { + FileWriter fw = new FileWriter(custom); + fw.write("# The user is free to add new and custom texture mappings here - Dynmap's install will not overwrite it\n"); + fw.close(); + } catch (IOException iox) { + } + } + } + + /** + * Load texture pack mappings from texture.txt file + */ + private static void loadTextureFile(File txtfile) { + LineNumberReader rdr = null; + int cnt = 0; + /* Initialize map with blank map for all entries */ + HDTextureMap.initializeTable(); + + try { + String line; + rdr = new LineNumberReader(new FileReader(txtfile)); + while((line = rdr.readLine()) != null) { + if(line.startsWith("block:")) { + ArrayList blkids = new ArrayList(); + int databits = 0; + int faces[] = new int[] { -1, -1, -1, -1, -1, -1 }; + line = line.substring(6); + String[] args = line.split(","); + for(String a : args) { + String[] av = a.split("="); + if(av.length < 2) continue; + if(av[0].equals("id")) { + blkids.add(Integer.parseInt(av[1])); + } + else if(av[0].equals("data")) { + if(av[1].equals("*")) + databits = 0xFFFF; + else + databits |= (1 << Integer.parseInt(av[1])); + } + else if(av[0].equals("top") || av[0].equals("y-")) { + faces[BlockStep.Y_MINUS.ordinal()] = Integer.parseInt(av[1]); + } + else if(av[0].equals("bottom") || av[0].equals("y+")) { + faces[BlockStep.Y_PLUS.ordinal()] = Integer.parseInt(av[1]); + } + else if(av[0].equals("north") || av[0].equals("x+")) { + faces[BlockStep.X_PLUS.ordinal()] = Integer.parseInt(av[1]); + } + else if(av[0].equals("south") || av[0].equals("x-")) { + faces[BlockStep.X_MINUS.ordinal()] = Integer.parseInt(av[1]); + } + else if(av[0].equals("west") || av[0].equals("z-")) { + faces[BlockStep.Z_MINUS.ordinal()] = Integer.parseInt(av[1]); + } + else if(av[0].equals("east") || av[0].equals("z+")) { + faces[BlockStep.Z_PLUS.ordinal()] = Integer.parseInt(av[1]); + } + else if(av[0].equals("allfaces")) { + int id = Integer.parseInt(av[1]); + for(int i = 0; i < 6; i++) { + faces[i] = id; + } + } + else if(av[0].equals("allsides")) { + short id = Short.parseShort(av[1]); + faces[BlockStep.X_PLUS.ordinal()] = id; + faces[BlockStep.X_MINUS.ordinal()] = id; + faces[BlockStep.Z_PLUS.ordinal()] = id; + faces[BlockStep.Z_MINUS.ordinal()] = id; + } + else if(av[0].equals("topbottom")) { + faces[BlockStep.Y_MINUS.ordinal()] = + faces[BlockStep.Y_PLUS.ordinal()] = Integer.parseInt(av[1]); + } + } + /* If we have everything, build block */ + if((blkids.size() > 0) && (databits != 0)) { + HDTextureMap map = new HDTextureMap(blkids, databits, faces); + map.addToTable(); + cnt++; + } + else { + Log.severe("Texture mapping missing required parameters = line " + rdr.getLineNumber() + " of " + txtfile.getPath()); + } + } + else if(line.startsWith("#") || line.startsWith(";")) { + } + } + Log.verboseinfo("Loaded " + cnt + " texture mappings from " + txtfile.getPath()); + } catch (IOException iox) { + Log.severe("Error reading " + txtfile.getPath() + " - " + iox.toString()); + } catch (NumberFormatException nfx) { + Log.severe("Format error - line " + rdr.getLineNumber() + " of " + txtfile.getPath()); + } finally { + if(rdr != null) { + try { + rdr.close(); + rdr = null; + } catch (IOException e) { + } + } + } + } + /** + * Read color for given subblock coordinate, with given block id and data and face + */ + public void readColor(HDPerspectiveState ps, MapIterator mapiter, Color rslt, int blkid, int lastblocktype) { + int blkdata = ps.getBlockData(); + HDTextureMap map = HDTextureMap.getMap(blkid, blkdata); + BlockStep laststep = ps.getLastBlockStep(); + int textid = map.faces[laststep.ordinal()]; /* Get index of texture source */ + if(textid < 0) { + rslt.setTransparent(); + return; + } + /* See if not basic block texture */ + int textop = textid / 1000; + textid = textid % 1000; + + /* If clear-inside op, get out early */ + if(textop == COLORMOD_CLEARINSIDE) { + /* Check if previous block is same block type as we are: surface is transparent if it is */ + if(blkid == lastblocktype) { + rslt.setTransparent(); + return; + } + } + + int[] texture = terrain_argb[textid]; + int[] xyz = ps.getSubblockCoord(); + /* Get texture coordinates (U=horizontal(left=0),V=vertical(top=0)) */ + int u = 0, v = 0, tmp; + + switch(laststep) { + case X_MINUS: /* South face: U = East (Z-), V = Down (Y-) */ + u = native_scale-xyz[2]-1; v = native_scale-xyz[1]-1; + break; + case X_PLUS: /* North face: U = West (Z+), V = Down (Y-) */ + u = xyz[2]; v = native_scale-xyz[1]-1; + break; + case Z_MINUS: /* West face: U = South (X+), V = Down (Y-) */ + u = xyz[0]; v = native_scale-xyz[1]-1; + break; + case Z_PLUS: /* East face: U = North (X-), V = Down (Y-) */ + u = native_scale-xyz[0]-1; v = native_scale-xyz[1]-1; + break; + case Y_MINUS: /* U = East(Z-), V = South(X+) */ + case Y_PLUS: + u = native_scale-xyz[2]-1; v = xyz[0]; + break; + } + /* Handle U-V transorms before fetching color */ + if(textop > 0) { + switch(textop) { + case COLORMOD_ROT90: + tmp = u; u = native_scale - v - 1; v = tmp; + break; + case COLORMOD_ROT180: + u = native_scale - u - 1; v = native_scale - v - 1; + break; + case COLORMOD_ROT270: + tmp = u; u = v; v = native_scale - tmp - 1; + break; + case COLORMOD_FLIPHORIZ: + u = native_scale - u - 1; + break; + case COLORMOD_SHIFTDOWNHALF: + case COLORMOD_SHIFTDOWNHALFANDFLIPHORIZ: + if(v < native_scale/2) { + rslt.setTransparent(); + return; + } + v -= native_scale/2; + if(textop == COLORMOD_SHIFTDOWNHALFANDFLIPHORIZ) + u = native_scale - u - 1; + break; + case COLORMOD_INCLINEDTORCH: + if(v >= (3*native_scale/4)) { + rslt.setTransparent(); + return; + } + v += native_scale/4; + if(u < native_scale/2) u = native_scale/2-1; + if(u > native_scale/2) u = native_scale/2; + break; + case COLORMOD_GRASSSIDE: + /* Check if snow above block */ + if(mapiter.getBlockTypeIDAbove() == 78) { + texture = terrain_argb[68]; /* Snow block */ + textid = 68; + } + else { /* Else, check the grass color overlay */ + int ovclr = terrain_argb[38][v*native_scale+u]; + if((ovclr & 0xFF000000) != 0) { /* Hit? */ + texture = terrain_argb[38]; /* Use it */ + textop = COLORMOD_GRASSTONED; /* Force grass toning */ + } + } + break; + } + } + /* Read color from texture */ + rslt.setARGB(texture[v*native_scale + u]); + if(textop > 0) { + /* Switch based on texture modifier */ + switch(textop) { + case COLORMOD_GRASSTONED: + if(grasscolor_argb == null) { + rslt.blendColor(trivial_grasscolor); + } + else { + rslt.blendColor(biomeLookup(grasscolor_argb, grasscolor_width, mapiter.getRawBiomeRainfall(), mapiter.getRawBiomeTemperature())); + } + break; + case COLORMOD_FOLIAGETONED: + if(foliagecolor_argb == null) { + rslt.blendColor(trivial_foliagecolor); + } + else { + rslt.blendColor(biomeLookup(foliagecolor_argb, foliagecolor_width, mapiter.getRawBiomeRainfall(), mapiter.getRawBiomeTemperature())); + } + break; + case COLORMOD_WATERTONED: + if(watercolor_argb == null) { + rslt.blendColor(trivial_watercolor); + } + else { + rslt.blendColor(biomeLookup(watercolor_argb, watercolor_width, mapiter.getRawBiomeRainfall(), mapiter.getRawBiomeTemperature())); + } + break; + } + } + } + + private static final int biomeLookup(int[] argb, int width, double rainfall, double temp) { + int t = (int)((1.0-temp)*(width-1)); + int h = width - (int)(temp*rainfall*(width-1)) - 1; + return argb[width*h + t]; + } +} diff --git a/src/main/java/org/dynmap/hdmap/TexturePackHDShader.java b/src/main/java/org/dynmap/hdmap/TexturePackHDShader.java new file mode 100644 index 00000000..07f88466 --- /dev/null +++ b/src/main/java/org/dynmap/hdmap/TexturePackHDShader.java @@ -0,0 +1,214 @@ +package org.dynmap.hdmap; + +import static org.dynmap.JSONUtils.s; +import org.bukkit.block.Biome; +import org.dynmap.Color; +import org.dynmap.ColorScheme; +import org.dynmap.ConfigurationNode; +import org.dynmap.Log; +import org.dynmap.hdmap.HDPerspectiveState.BlockStep; +import org.dynmap.utils.MapChunkCache; +import org.dynmap.utils.MapIterator; +import org.json.simple.JSONObject; + +public class TexturePackHDShader implements HDShader { + private String tpname; + private String name; + private TexturePack tp; + + public TexturePackHDShader(ConfigurationNode configuration) { + tpname = configuration.getString("texturepack", "minecraft"); + name = configuration.getString("name", tpname); + tp = TexturePack.getTexturePack(tpname); + if(tp == null) { + Log.severe("Error: shader '" + name + "' cannot load texture pack '" + tpname + "'"); + } + } + + @Override + public boolean isBiomeDataNeeded() { + return false; + } + + @Override + public boolean isRawBiomeDataNeeded() { + return true; + } + + @Override + public boolean isHightestBlockYDataNeeded() { + return false; + } + + @Override + public boolean isBlockTypeDataNeeded() { + return true; + } + + @Override + public boolean isSkyLightLevelNeeded() { + return false; + } + + @Override + public boolean isEmittedLightLevelNeeded() { + return false; + } + + @Override + public String getName() { + return name; + } + + private class OurShaderState implements HDShaderState { + private Color color[]; + private Color tmpcolor[]; + private Color c; + protected MapIterator mapiter; + protected HDMap map; + private TexturePack scaledtp; + private HDLighting lighting; + private int lastblkid; + + private OurShaderState(MapIterator mapiter, HDMap map) { + this.mapiter = mapiter; + this.map = map; + this.lighting = map.getLighting(); + if(lighting.isNightAndDayEnabled()) { + color = new Color[] { new Color(), new Color() }; + tmpcolor = new Color[] { new Color(), new Color() }; + } + else { + color = new Color[] { new Color() }; + tmpcolor = new Color[] { new Color() }; + } + c = new Color(); + scaledtp = tp.resampleTexturePack(map.getPerspective().getModelScale()); + } + /** + * Get our shader + */ + public HDShader getShader() { + return TexturePackHDShader.this; + } + + /** + * Get our map + */ + public HDMap getMap() { + return map; + } + + /** + * Get our lighting + */ + public HDLighting getLighting() { + return lighting; + } + + /** + * Reset renderer state for new ray + */ + public void reset(HDPerspectiveState ps) { + for(Color c: color) + c.setTransparent(); + lastblkid = 0; + } + + /** + * Process next ray step - called for each block on route + * @return true if ray is done, false if ray needs to continue + */ + public boolean processBlock(HDPerspectiveState ps) { + int blocktype = ps.getBlockTypeID(); + int lastblocktype = lastblkid; + lastblkid = blocktype; + + if(blocktype == 0) { + return false; + } + /* Get color from textures */ + scaledtp.readColor(ps, mapiter, c, blocktype, lastblocktype); + + if (c.getAlpha() > 0) { + int subalpha = ps.getSubmodelAlpha(); + /* Scale brightness depending upon face */ + switch(ps.getLastBlockStep()) { + case X_MINUS: + case X_PLUS: + /* 60% brightness */ + c.blendColor(0xFFA0A0A0); + break; + case Y_MINUS: + case Y_PLUS: + /* 85% brightness for even, 90% for even*/ + if((mapiter.getY() & 0x01) == 0) + c.blendColor(0xFFD9D9D9); + else + c.blendColor(0xFFE6E6E6); + break; + } + /* Handle light level, if needed */ + lighting.applyLighting(ps, this, c, tmpcolor); + /* If we got alpha from subblock model, use it instead */ + if(subalpha >= 0) { + for(Color clr : tmpcolor) + clr.setAlpha(Math.max(subalpha,clr.getAlpha())); + } + /* If no previous color contribution, use new color */ + if(color[0].isTransparent()) { + for(int i = 0; i < color.length; i++) + color[i].setColor(tmpcolor[i]); + return (color[0].getAlpha() == 255); + } + /* Else, blend and generate new alpha */ + else { + int alpha = color[0].getAlpha(); + int alpha2 = tmpcolor[0].getAlpha() * (255-alpha) / 255; + int talpha = alpha + alpha2; + for(int i = 0; i < color.length; i++) + color[i].setRGBA((tmpcolor[i].getRed()*alpha2 + color[i].getRed()*alpha) / talpha, + (tmpcolor[i].getGreen()*alpha2 + color[i].getGreen()*alpha) / talpha, + (tmpcolor[i].getBlue()*alpha2 + color[i].getBlue()*alpha) / talpha, talpha); + return (talpha >= 254); /* If only one short, no meaningful contribution left */ + } + } + + return false; + } + /** + * Ray ended - used to report that ray has exited map (called if renderer has not reported complete) + */ + public void rayFinished(HDPerspectiveState ps) { + } + /** + * Get result color - get resulting color for ray + * @param c - object to store color value in + * @param index - index of color to request (renderer specific - 0=default, 1=day for night/day renderer + */ + public void getRayColor(Color c, int index) { + c.setColor(color[index]); + } + /** + * Clean up state object - called after last ray completed + */ + public void cleanup() { + } + } + + /** + * Get renderer state object for use rendering a tile + * @param map - map being rendered + * @param cache - chunk cache containing data for tile to be rendered + * @param mapiter - iterator used when traversing rays in tile + * @return state object to use for all rays in tile + */ + public HDShaderState getStateInstance(HDMap map, MapChunkCache cache, MapIterator mapiter) { + return new OurShaderState(mapiter, map); + } + + /* Add shader's contributions to JSON for map object */ + public void addClientConfiguration(JSONObject mapObject) { + s(mapObject, "shader", name); + } +} diff --git a/src/main/java/org/dynmap/utils/LegacyMapChunkCache.java b/src/main/java/org/dynmap/utils/LegacyMapChunkCache.java index 30c86a5d..35ebc8ca 100644 --- a/src/main/java/org/dynmap/utils/LegacyMapChunkCache.java +++ b/src/main/java/org/dynmap/utils/LegacyMapChunkCache.java @@ -145,6 +145,12 @@ public class LegacyMapChunkCache implements MapChunkCache { public final int getZ() { return z; } + public final int getBlockTypeIDAbove() { + if(y < 127) + return snap.getBlockTypeId(x & 0xF, y+1, z & 0xF); + return 0; + } + } /** diff --git a/src/main/java/org/dynmap/utils/MapIterator.java b/src/main/java/org/dynmap/utils/MapIterator.java index 9585df11..fa038504 100644 --- a/src/main/java/org/dynmap/utils/MapIterator.java +++ b/src/main/java/org/dynmap/utils/MapIterator.java @@ -93,4 +93,11 @@ public interface MapIterator { * Get Z coordinate */ int getZ(); + /** + * Get block ID at one above current coordinates + * + * @return block id + */ + int getBlockTypeIDAbove(); + } diff --git a/src/main/java/org/dynmap/utils/NewMapChunkCache.java b/src/main/java/org/dynmap/utils/NewMapChunkCache.java index a350f0d5..ead6cfc0 100644 --- a/src/main/java/org/dynmap/utils/NewMapChunkCache.java +++ b/src/main/java/org/dynmap/utils/NewMapChunkCache.java @@ -145,6 +145,11 @@ public class NewMapChunkCache implements MapChunkCache { public final int getZ() { return z; } + public final int getBlockTypeIDAbove() { + if(y < 127) + return snap.getBlockTypeId(x & 0xF, y+1, z & 0xF); + return 0; + } } /** diff --git a/texture.txt b/texture.txt new file mode 100644 index 00000000..15a39d5b --- /dev/null +++ b/texture.txt @@ -0,0 +1,365 @@ +# Mapping of texture resources to block ID and data values +# block:id=,data=,top=,bottom=,north=,south=,east=,west=,allfaces=,allsides= +# =0-255 (index of patch in terrain.png), -1=clear, 1xxx=biome tint from grasscolor.png,257=stationary water,258=moving water, +# 259=stationary lava,260=moving lava,2xxx=biome tint from foliagecolor.png,3xxx=biome tint from watercolor.png,4xxx=rotate texture 90, +# 5xxx=rotate texture 180, 6xxx=rotate texture 270, 7xxx=flip texture horizontally, 8xxx=shift down 1/2 block, 9=shift down 1/2,flip horiz, +# 10xxx=inclined-torch,11xxx=grass-side,12xxx=clear if same block +###### +# Stone +block:id=1,data=*,allfaces=1 +# Grass +block:id=2,data=*,allsides=11003,top=1000,bottom=2 +# Dirt +block:id=3,data=*,allfaces=2 +# Cobblestone +block:id=4,data=*,allfaces=16 +# Wooden Plank +block:id=5,data=*,allsides=4,topbottom=4004 +# Sapling +block:id=6,data=0,data=3,allsides=15 +# Sapling (Spruce) +block:id=6,data=1,allsides=63 +# Sapling (Birch) +block:id=6,data=2,allsides=79 +# Bedrock +block:id=7,data=*,allfaces=16 +# Water +block:id=8,data=*,allfaces=12258 +# Stationary water +block:id=9,data=*,allfaces=12257 +# Lava +block:id=10,data=*,allfaces=260 +# Stationary Lava +block:id=11,data=*,allfaces=259 +# Sand +block:id=12,data=*,allfaces=18 +# Gravel +block:id=13,data=*,allfaces=19 +# Gold Ore +block:id=14,data=*,allfaces=32 +# Iron Ore +block:id=15,data=*,allfaces=33 +# Coal Ore +block:id=16,data=*,allfaces=34 +# Wood (std) +block:id=17,data=0,allsides=20,top=21,bottom=21 +# Wood (spruce/pine) +block:id=17,data=1,allsides=116,top=21,bottom=21 +# Wood (birch) +block:id=17,data=2,allsides=117,top=21,bottom=21 +# Leaves (std) +block:id=18,data=0,allfaces=2052 +# Leaves (spruce/pine) +block:id=18,data=1,allfaces=2132 +# Leaves (birch) +block:id=18,data=2,allfaces=2052 +# Sponge +block:id=19,data=*,allfaces=48 +# Glass +block:id=20,data=*,allfaces=12049 +# Lapis Lazuli Ore +block:id=21,data=*,allfaces=160 +# Lapis Lazuli Block +block:id=22,data=*,allfaces=144 +# Dispenser (facing east) +block:id=23,data=2,top=62,east=46,south=45,north=45,west=45,bottom=62 +# Dispenser (facing west) +block:id=23,data=3,top=62,west=46,south=45,north=45,east=45,bottom=62 +# Dispenser (facing north) +block:id=23,data=4,top=62,north=46,south=45,east=45,west=45,bottom=62 +# Dispenser (facing south) +block:id=23,data=5,top=62,south=46,north=45,east=45,west=45,bottom=62 +# Sandstone +block:id=24,data=*,top=176,bottom=208,allsides=192 +# Note Block +block:id=25,allfaces=74 +# Bed - head - pointing west +block:id=26,data=8,top=5135,bottom=5135,south=7151,north=151,west=152 +# Bed - foot - pointing west +block:id=26,data=0,top=5134,bottom=5134,south=7150,north=150,east=149 +# Bed - head - pointing north +block:id=26,data=9,top=4135,bottom=4135,north=152,east=151,west=7151 +# Bed - foot - pointing north +block:id=26,data=1,top=4134,bottom=4134,south=149,east=150,west=7150 +# Bed - head - pointing east +block:id=26,data=10,top=135,bottom=135,south=151,north=7151,east=152 +# Bed - foot - pointing east +block:id=26,data=2,top=134,bottom=134,south=150,north=7150,west=149 +# Bed - head - pointing south +block:id=26,data=11,top=6135,bottom=6135,south=152,east=7151,west=151 +# Bed - foot - pointing south +block:id=26,data=3,top=6134,bottom=6134,north=149,east=7150,west=150 +# Powered rail - heading east-west - unpowered +# Powered rail - incline to east - unpowered +# Powered rail - incline to west - unpowered +block:id=27,data=0,data=4,data=5,top=4163,bottom=4163,allsides=4 +# Powered rail - heading east-west - powered +# Powered rail - incline to east - powered +# Powered rail - incline to west - powered +block:id=27,data=8,data=12,data=13,top=4179,bottom=4179,allsides=4 +# Powered rail - heading north-south - unpowered +# Powered rail - inclined to north - unpowered +# Powered rail - inclined to south - unpowered +block:id=27,data=1,data=2,data=3,top=163,bottom=163,allsides=4 +# Powered rail - heading north-sout - powered +# Powered rail - inclined to north - powered +# Powered rail - inclined to south - powered +block:id=27,data=9,data=10,data=11,top=179,bottom=179,allsides=4 +# Detector rail - heading east-west +# Detector rail - incline to east +# Detector rail - incline to west +block:id=28,data=0,data=4,data=5,top=4195,bottom=4195,allsides=4 +# Detector rail - heading north-south +# Detector rail - incline to north +# Detector rail - incline to south +block:id=28,data=1,data=2,data=3,top=195,bottom=195,allsides=4 +# Sticky piston - facing down +block:id=29,data=0,data=8,top=109,bottom=106,allsides=5108 +# Sticky piston - facing up +block:id=29,data=1,data=9,top=106,bottom=109,allsides=108 +# Sticky piston - facing east +block:id=29,data=2,data=10,east=106,west=109,top=6108,bottom=4108,north=4108,south=6108 +# Sticky piston - facing west +block:id=29,data=3,data=11,west=106,east=109,top=4108,bottom=6108,north=6108,south=4108 +# Sticky piston - facing north +block:id=29,data=4,data=12,north=106,south=109,top=108,bottom=5108,east=6108,west=4108 +# Sticky piston - facing south +block:id=29,data=5,data=13,north=109,south=106,top=5108,bottom=108,east=4108,west=6108 +# Web +block:id=30,data=*,allfaces=11 +# Dead shrub +block:id=31,data=0,allsides=55,top=20 +# Tall Grass +block:id=31,data=1,allfaces=1039 +# Fern +block:id=31,data=2,allsides=1056,top=1000 +# Dead shrub +block:id=32,data=*,allsides=55,top=20 +# Piston - facing down +block:id=33,data=0,data=8,top=109,bottom=107,allsides=5108 +# Piston - facing up +block:id=33,data=1,data=9,top=107,bottom=109,allsides=108 +# Piston - facing east +block:id=33,data=2,data=10,east=107,west=109,top=6108,bottom=4108,north=4108,south=6108 +# Piston - facing west +block:id=33,data=3,data=11,west=107,east=109,top=4108,bottom=6108,north=6108,south=4108 +# Piston - facing north +block:id=33,data=4,data=12,north=107,south=109,top=108,bottom=5108,east=6108,west=4108 +# Piston - facing south +block:id=33,data=5,data=13,north=109,south=107,top=5108,bottom=108,east=4108,west=6108 +# Piston extesions - skipped - render all as if closed +block:id=34,data=* +# Wool - white +block:id=35,data=0,allfaces=64 +# Wool - orange +block:id=35,data=1,allfaces=210 +# Wool - Magenta +block:id=35,data=2,allfaces=194 +# Wool - Light Blue +block:id=35,data=3,allfaces=178 +# Wool - Yellow +block:id=35,data=4,allfaces=162 +# Wool - Light Green +block:id=35,data=5,allfaces=146 +# Wool - Pink +block:id=35,data=6,allfaces=130 +# Wool - Gray +block:id=35,data=7,allfaces=114 +# Wool - Light Gray +block:id=35,data=8,allfaces=225 +# Wool - Cyan +block:id=35,data=9,allfaces=209 +# Wool - Purple +block:id=35,data=10,allfaces=193 +# Wool - Blue +block:id=35,data=11,allfaces=177 +# Wool - Brown +block:id=35,data=12,allfaces=161 +# Wool - Dark Green +block:id=35,data=13,allfaces=145 +# Wool - Red +block:id=35,data=14,allfaces=129 +# Wool - Black +block:id=35,data=15,allfaces=113 +# Block move by piston - don't render +block:id=36,data=* +# Dandelion +block:id=37,data=*,allsides=13,top=162 +# Rose +block:id=38,data=*,allsides=12,top=129 +# Brown mushroom +block:id=39,data=*,allsides=29,top=161 +# Red mushroom +block:id=40,data=*,allsides=28,top=129 +# Gold block +block:id=41,data=*,allfaces=23 +# Iron block +block:id=42,data=*,allfaces=22 +# Double Slab - stone +block:id=43,data=0,allsides=5,topbottom=6 +# Double Slab - Sandstone +block:id=43,data=1,top=176,bottom=208,allsides=192 +# Double Slab - Wood +block:id=43,data=2,allsides=4,topbottom=4004 +# Double Slab - Cobblestone +block:id=43,data=3,allfaces=16 +# Slab - stone +block:id=44,data=0,allsides=5,topbottom=6 +# Slab - Sandstone +block:id=44,data=1,top=176,bottom=208,allsides=192 +# Slab - Wood +block:id=44,data=2,allsides=4,topbottom=4004 +# Slab - Cobblestone +block:id=44,data=3,allfaces=16 +# Brick Block +block:id=45,data=*,allfaces=7 +# TNT +block:id=46,data=*,top=9,bottom=10,allsides=8 +# Bookshelf +block:id=47,data=*,topbottom=4,allsides=35 +# Mossy Cobblestone +block:id=48,data=*,allfaces=36 +# Obsidian +block:id=49,data=*,allfaces=37 +# Torch - inclined +block:id=50,data=1,data=2,data=3,data=4,allsides=10080,top=162,bottom=4 +# Torch - up +block:id=50,data=5,allsides=80,top=162,bottom=4 +# Fire +block:id=51,data=*,allsides=129,top=162 +# Monster spawner +block:id=52,data=*,allfaces=65 +# Wooden stairs +block:id=53,data=*,allsides=4,topbottom=4004 +# Chest - TODO: get entity data so we can see orientation +block:id=54,data=*,top=25,south=27,north=27,east=26,west=26 +# Redstone wire (model handling shape - use red wool for color) +block:id=55,data=*,allfaces=129 +# Diamond ore +block:id=56,data=*,allfaces=50 +# Diamond block +block:id=57,data=*,allfaces=24 +# Crafting table +block:id=58,data=*,topbottom=43,east=59,west=59,north=60,south=60 +# Crops (size 1) +block:id=59,data=0,allsides=88,top=1000 +# Crops (size 2) +block:id=59,data=1,allsides=89,top=1000 +# Crops (size 3) +block:id=59,data=2,allsides=90,top=1000 +# Crops (size 4) +block:id=59,data=3,allsides=91,top=1000 +# Crops (size 5) +block:id=59,data=4,allsides=92,top=1000 +# Crops (size 6) +block:id=59,data=5,allsides=93,top=1000 +# Crops (size 7) +block:id=59,data=6,allsides=94,top=1000 +# Crops (size 8) +block:id=59,data=7,allsides=95,top=1000 +# Farmland (dry) +block:id=60,data=0,allfaces=87 +# Farmland (wet) +block:id=60,data=1,data=2,data=3,data=4,data=5,data=6,data=7,data=8,allfaces=88 +# Furnace (facing east) +block:id=61,data=2,top=62,east=61,south=45,north=45,west=45,bottom=62 +# Furnace (facing west) +block:id=61,data=3,top=62,west=61,south=45,north=45,east=45,bottom=62 +# Furnace (facing north) +block:id=61,data=4,top=62,north=61,south=45,east=45,west=45,bottom=62 +# Furnace (facing south) +block:id=61,data=5,top=62,south=61,north=45,east=45,west=45,bottom=62 +# Signpost +block:id=62,data=*,allsides=4,topbottom=4004 +# Wooden Door - bottom +block:id=64,data=0,data=1,data=2,data=3,data=4,data=5,data=6,data=7,allsides=97,topbottom=4 +# Wooden Door - top +block:id=64,data=8,data=9,data=10,data=11,data=12,data=13,data=14,data=15,allsides=81,topbottom=4 +# Ladders +block:id=65,data=*,allsides=83,topbottom=4 +# Rail - heading east-west +# Rail - incline to east +# Rail - incline to west +block:id=66,data=0,data=4,data=5,top=4128,bottom=4128,allsides=4 +# Rail - heading north-south +# Rail - incline to north +# Rail - incline to south +block:id=66,data=1,data=2,data=3,top=128,bottom=128,allsides=4 +# Rails - northeast corner +block:id=66,data=6,topbottom=4112,allsides=4 +# Rails - southeast corner +block:id=66,data=7,topbottom=5112,allsides=4 +# Rails - southwest corner +block:id=66,data=8,topbottom=6112,allsides=4 +# Rails - northwest corner +block:id=66,data=9,topbottom=112,allsides=4 +# Cobblestone Stairs +block:id=67,data=*,allfaces=16 +# Wall sign +block:id=68,data=*,allsides=4,topbottom=4004 +# Switch (just do stone for now) +block:id=69,data=*,allfaces=1 +# Stone pressure plate +block:id=70,data=*,allfaces=1 +# Iron Door - bottom +block:id=71,data=0,data=1,data=2,data=3,data=4,data=5,data=6,data=7,allsides=98,topbottom=22 +# Iron Door - top +block:id=71,data=8,data=9,data=10,data=11,data=12,data=13,data=14,data=15,allsides=82,topbottom=22 +# Wooden pressure plate +block:id=72,data=*,allsides=4,topbottom=4004 +# Redstone Ore +block:id=73,id=74,data=*,allfaces=51 +# Redstone Torch - unlit - inclined +block:id=75,data=1,data=2,data=3,data=4,allsides=10115,top=162,bottom=4 +# Redstone Torch - unlit - up +block:id=75,data=5,allsides=115,top=162,bottom=4 +# Redstone Torch - lit - inclined +block:id=76,data=1,data=2,data=3,data=4,allsides=10099,top=162,bottom=4 +# Redstone Torch - lit - up +block:id=76,data=5,allsides=99,top=162,bottom=4 +# Stone button +block:id=77,data=*,allfaces=1 +# Snow +block:id=78,data=*,allfaces=66 +# Ice +block:id=79,data=*,allfaces=12067 +# Snow block +block:id=80,data=*,allfaces=66 +# Cactus +block:id=81,data=*,top=69,allsides=70,bottom=71 +# Clay block +block:id=82,data=*,allfaces=72 +# Sugar Cane +block:id=83,data=*,allsides=73,topbottom=21 +# Jukebox +block:id=84,data=*,allsides=74,topbottom=75 +# Fence +block:id=85,data=*,allsides=4,topbottom=4004 +# Pumpkin +block:id=86,data=*,allsides=118,topbottom=102 +# Netherrock +block:id=87,data=*,allfaces=103 +# SoulSand +block:id=88,data=*,allfaces=104 +# Glowstone Block +block:id=89,data=*,allfaces=105 +# Portal (no texture for this - using purple wool) +block:id=90,data=*,allfaces=193 +# Jack O Lantern (east) +block:id=91,data=0,north=118,south=118,east=120,west=118,topbottom=102 +# Jack O Lantern (south) +block:id=91,data=1,north=118,south=120,east=118,west=118,topbottom=102 +# Jack O Lantern (west) +block:id=91,data=2,north=118,south=118,east=118,west=120,topbottom=102 +# Jack O Lantern (north) +block:id=91,data=3,north=120,south=118,east=118,west=118,topbottom=102 +# Cake Block +block:id=92,data=*,allsides=122,top=121,bottom=124 +# Repeater (off) +block:id=93,data=*,top=131,allsides=1,bottom=1 +# Repeater (on) +block:id=94,data=*,top=147,allsides=1,bottom=1 +# Locked Chest - TODO: get entity data so we can see orientation +block:id=95,data=*,top=25,south=27,north=27,east=26,west=26 +# Trap door +block:id=96,data=*,topbottom=84,allsides=4 diff --git a/texturepacks/standard/misc/foliagecolor.png b/texturepacks/standard/misc/foliagecolor.png new file mode 100644 index 00000000..81673cae Binary files /dev/null and b/texturepacks/standard/misc/foliagecolor.png differ diff --git a/texturepacks/standard/misc/grasscolor.png b/texturepacks/standard/misc/grasscolor.png new file mode 100644 index 00000000..a6d9c209 Binary files /dev/null and b/texturepacks/standard/misc/grasscolor.png differ diff --git a/texturepacks/standard/misc/water.png b/texturepacks/standard/misc/water.png new file mode 100644 index 00000000..8b92f9bc Binary files /dev/null and b/texturepacks/standard/misc/water.png differ diff --git a/texturepacks/standard/misc/watercolor.png b/texturepacks/standard/misc/watercolor.png new file mode 100644 index 00000000..38bff05e Binary files /dev/null and b/texturepacks/standard/misc/watercolor.png differ diff --git a/texturepacks/standard/terrain.png b/texturepacks/standard/terrain.png new file mode 100644 index 00000000..244f7668 Binary files /dev/null and b/texturepacks/standard/terrain.png differ