From e6c0474a72c8e0d24ed6d30792294a75c2438e97 Mon Sep 17 00:00:00 2001 From: "Robert C. Martin" Date: Sun, 2 Mar 2014 02:04:39 -0600 Subject: [PATCH 01/16] add colors for Mesa, acacia, dark oak. Allow spaces in colormap file --- .gitignore | 6 ++++++ Makefile | 2 +- src/togos/minecraft/maprend/ColorMap.java | 2 +- src/togos/minecraft/maprend/block-colors.txt | 6 +++++- 4 files changed, 13 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 6118bc9..109f145 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,10 @@ out bin .src.lst TMCMR.jar +TMCMR.jar.old .idea +MakeMap.sh +makeMap +MakeMapForce.sh +MakeMapForce + diff --git a/Makefile b/Makefile index 0d32387..8ca6391 100755 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ .PHONY: all clean -all: TMCMR.jar +all: clean TMCMR.jar clean: rm -rf bin TMCMR.jar .src.lst diff --git a/src/togos/minecraft/maprend/ColorMap.java b/src/togos/minecraft/maprend/ColorMap.java index 02cd8ff..dbf3cb3 100644 --- a/src/togos/minecraft/maprend/ColorMap.java +++ b/src/togos/minecraft/maprend/ColorMap.java @@ -35,7 +35,7 @@ public static ColorMap load( BufferedReader s, String filename ) throws IOExcept if( line.trim().isEmpty() ) continue; if( line.trim().startsWith("#") ) continue; - String[] v = line.split("\t", 3); + String[] v = line.split("\\s+", 3); if( v.length < 2 ) { System.err.println("Invalid color map line at "+filename+":"+lineNum+": "+line); continue; diff --git a/src/togos/minecraft/maprend/block-colors.txt b/src/togos/minecraft/maprend/block-colors.txt index 802a68c..7a219e1 100644 --- a/src/togos/minecraft/maprend/block-colors.txt +++ b/src/togos/minecraft/maprend/block-colors.txt @@ -13,7 +13,7 @@ default 0xFFFF00FF 0x0000 0x00000000 # Air 0x0001 0xFF888888 # Stone -0x0002 0xFF008800 # Grass Block +0x0002 0xFF008800 # Grass Block 0x0003 0xFF884400 # Dirt 0x0004 0xFF666666 # Cobblestone 0x0005 0xFFAA8844 # Wooden Planks @@ -102,6 +102,7 @@ default 0xFFFF00FF 0x006B 0x8F463822 # Fence Gate 0x006C 0xFF8A5C50 # Brick Stairs 0x006D 0xFE7E7E80 # Stone Brick Stairs +0x006E 0xFFB19AB5 # Mycelium 0x006F 0x00214418 # Lily Pad 0x0070 0xFE643130 # Nether Brick 0x0071 0xFE643130 # Nether Brick Fence @@ -119,3 +120,6 @@ default 0xFFFF00FF 0x008B 0xFF757575 # Cobblestone Wall 0x0091 0x9F282525 # Anvil 0x009D 0x9B4C443E # Activator Rail +0x009F 0xFFA34224 # Stained Clay +0x00A1 0xFF818511 # Leaves2 (Dark Oak, Acacia) +0x00AC 0xFFA34224 # Hardened Clay From 692a649203a6db8ebe5a36bf2b1a991f1a9dce2a Mon Sep 17 00:00:00 2001 From: "Robert C. Martin" Date: Sun, 2 Mar 2014 11:11:39 +0000 Subject: [PATCH 02/16] add BlockMap tests and allow spaces in BlockMap files --- src/togos/minecraft/maprend/BlockMap.java | 321 +++++++++++------- src/togos/minecraft/maprend/block-colors.txt | 1 - .../togos/minecraft/maprend/BlockMapTest.java | 95 ++++++ 3 files changed, 287 insertions(+), 130 deletions(-) create mode 100644 test/togos/minecraft/maprend/BlockMapTest.java diff --git a/src/togos/minecraft/maprend/BlockMap.java b/src/togos/minecraft/maprend/BlockMap.java index 9d80d1a..f7e7f39 100644 --- a/src/togos/minecraft/maprend/BlockMap.java +++ b/src/togos/minecraft/maprend/BlockMap.java @@ -4,133 +4,196 @@ import static togos.minecraft.maprend.IDUtil.parseInt; -public final class BlockMap -{ - /** - * Holds the default color for a block and, optionally, - * a color for specific 'block data' values. - */ - public static class Block { - protected static final int[] EMPTY_INT_ARRAY = new int[0]; - protected static final boolean[] EMPTY_BOOLEAN_ARRAY = new boolean[0]; - public static final int SUB_COLOR_COUNT = 0x10; - - public int baseColor; - public int baseInfluence; - public boolean isDefault; - - public int[] subColors = EMPTY_INT_ARRAY; - public int[] subColorInfluences = EMPTY_INT_ARRAY; - public boolean[] hasSubColors = EMPTY_BOOLEAN_ARRAY; - - private Block(int baseColor, int baseInfluence, boolean isDefault) { - this.baseColor = baseColor; - this.baseInfluence = baseInfluence; - this.isDefault = isDefault; - } - - private void setSubColor( int blockData, int color, int influence ) { - if( blockData < 0 || blockData >= SUB_COLOR_COUNT ) { - throw new RuntimeException("Block data value out of bounds: "+blockData); - } - if( subColors.length == 0 ) { - hasSubColors = new boolean[SUB_COLOR_COUNT]; - subColors = new int[SUB_COLOR_COUNT]; - subColorInfluences = new int[SUB_COLOR_COUNT]; - } - hasSubColors[blockData] = true; - subColors[blockData] = color; - subColorInfluences[blockData] = influence; - } - - private void setBaseColor( int color, int influence, boolean isDefault ) { - this.baseColor = color; - this.baseInfluence = influence; - this.isDefault = isDefault; - } - } - - public static final int INDEX_MASK = 0xFFFF; - public static final int SIZE = INDEX_MASK+1; - - public static final int INF_NONE = 0; - public static final int INF_GRASS = 1; - public static final int INF_FOLIAGE = 2; - public static final int INF_WATER = 3; - - public final Block[] blocks; - public BlockMap( Block[] blocks ) { - assert blocks != null; - assert blocks.length == SIZE; - - this.blocks = blocks; - } - - public static BlockMap load( BufferedReader s, String filename ) throws IOException { - Block[] blocks = new Block[SIZE]; - for( int i=0; i 2){ - if (v[2].equals( "biome_grass" )) { - influence = INF_GRASS; - } else if (v[2].equals( "biome_foliage" )) { - influence = INF_FOLIAGE; - } else if (v[2].equals( "biome_water" )) { - influence = INF_WATER; - } - } - - if( blockData < 0 ) { - blocks[blockId&INDEX_MASK].setBaseColor( color, influence, false ); - } else { - blocks[blockId&INDEX_MASK].setSubColor( blockData, color, influence ); - blocks[blockId&INDEX_MASK].isDefault = false; - } - } - } - return new BlockMap(blocks); - } - - public static BlockMap load( File f ) throws IOException { - BufferedReader br = new BufferedReader(new FileReader(f)); - try { - return load(br, f.getPath()); - } finally { - br.close(); - } - } - - public static BlockMap loadDefault() { - try { - BufferedReader br = new BufferedReader(new InputStreamReader(BlockMap.class.getResourceAsStream("block-colors.txt"))); - try { - return load(br, "(default block colors)"); - } finally { - br.close(); - } - } catch( IOException e ) { - throw new RuntimeException("Error loading built-in color map", e); - } - } +public final class BlockMap { + public static final int INDEX_MASK = 0xFFFF; + public static final int SIZE = INDEX_MASK + 1; + public static final int INF_NONE = 0; + public static final int INF_GRASS = 1; + public static final int INF_FOLIAGE = 2; + public static final int INF_WATER = 3; + public final Block[] blocks; + + public BlockMap(Block[] blocks) { + assert blocks != null; + assert blocks.length == SIZE; + + this.blocks = blocks; + } + + public static BlockMap load(File f) throws IOException { + BufferedReader br = new BufferedReader(new FileReader(f)); + try { + return load(br, f.getPath()); + } finally { + br.close(); + } + } + + public static BlockMap load(BufferedReader s, String filename) throws IOException { + return new BlockMapLoader().load(s, filename); + } + + public static BlockMap loadDefault() { + try { + BufferedReader br = new BufferedReader(new InputStreamReader(BlockMap.class.getResourceAsStream("block-colors.txt"))); + try { + return load(br, "(default block colors)"); + } finally { + br.close(); + } + } catch (IOException e) { + throw new RuntimeException("Error loading built-in color map", e); + } + } + + /** + * Holds the default color for a block and, optionally, + * a color for specific 'block data' values. + */ + public static class Block { + public static final int SUB_COLOR_COUNT = 0x10; + protected static final int[] EMPTY_INT_ARRAY = new int[0]; + protected static final boolean[] EMPTY_BOOLEAN_ARRAY = new boolean[0]; + public int baseColor; + public int baseInfluence; + public boolean isDefault; + public int[] subColors = EMPTY_INT_ARRAY; + public int[] subColorInfluences = EMPTY_INT_ARRAY; + public boolean[] hasSubColors = EMPTY_BOOLEAN_ARRAY; + + private Block(int baseColor, int baseInfluence, boolean isDefault) { + this.baseColor = baseColor; + this.baseInfluence = baseInfluence; + this.isDefault = isDefault; + } + + private void setSubColor(int blockData, int color, int influence) { + if (blockData < 0 || blockData >= SUB_COLOR_COUNT) { + throw new RuntimeException("Block data value out of bounds: " + blockData); + } + if (subColors.length == 0) { + hasSubColors = new boolean[SUB_COLOR_COUNT]; + subColors = new int[SUB_COLOR_COUNT]; + subColorInfluences = new int[SUB_COLOR_COUNT]; + } + hasSubColors[blockData] = true; + subColors[blockData] = color; + subColorInfluences[blockData] = influence; + } + + private void setBaseColor(int color, int influence, boolean isDefault) { + this.baseColor = color; + this.baseInfluence = influence; + this.isDefault = isDefault; + } + } + + protected static class BlockMapLoader { + private static Block[] blocks; + + public BlockMap load(BufferedReader s, String filename) throws IOException { + clearMap(); + + String line; + for (int lineNum = 1; (line = s.readLine()) != null; ++lineNum) + if (parseBlockMapLine(line) == false) + System.err.println("Invalid color map line at " + filename + ":" + lineNum + ": " + line); + + return new BlockMap(blocks); + } + + private Block[] clearMap() { + blocks = new Block[SIZE]; + for (int i = 0; i < SIZE; ++i) { + blocks[i] = new Block(0, 0, true); + } + return blocks; + } + + private boolean parseBlockMapLine(String line) { + boolean validLine = false; + try { + String[] mapTokens = line.trim().split("\\s+", 4); + switch (identifyLineType(mapTokens)) { + case EMPTY: + case COMMENT: + validLine = true; + break; + case ERROR: + validLine = false; + break; + case DEFAULT: + setMapDefaultColor(parseInt(mapTokens[1])); + validLine = true; + break; + case COLOR_LINE: + parseBlockColorLine(mapTokens); + validLine = true; + break; + } + } catch (Exception e) { + validLine = false; + } + return validLine; + } + + ; + + private LineType identifyLineType(String[] mapTokens) { + if (mapTokens.length == 0) + return LineType.EMPTY; + else if (mapTokens[0].startsWith("#")) + return LineType.COMMENT; + else if (mapTokens.length < 2) + return LineType.ERROR; + else if (mapTokens[0].equalsIgnoreCase("default")) + return LineType.DEFAULT; + else + return LineType.COLOR_LINE; + } + + protected void parseBlockColorLine(String[] mapTokens) { + int color = parseInt(mapTokens[1]); + String[] idTokens = mapTokens[0].split(":", 2); + int blockId = parseInt(idTokens[0]); + int blockData = idTokens.length == 2 ? parseInt(idTokens[1]) : -1; + int influence = determineInfluence(mapTokens); + + setBlockMap(blockId, blockData, color, influence); + } + + private int determineInfluence(String[] mapTokens) { + if (mapTokens.length > 2) + return selectSpecifiedInfluence(mapTokens[2]); + return INF_NONE; + } + + private int selectSpecifiedInfluence(String influenceToken) { + if (influenceToken.equals("biome_grass")) { + return INF_GRASS; + } else if (influenceToken.equals("biome_foliage")) { + return INF_FOLIAGE; + } else if (influenceToken.equals("biome_water")) { + return INF_WATER; + } + return INF_NONE; + } + + protected void setBlockMap(int blockId, int blockData, int color, int influence) { + int blockIndex = blockId & INDEX_MASK; + if (blockData < 0) { + blocks[blockIndex].setBaseColor(color, influence, false); + } else { + blocks[blockIndex].setSubColor(blockData, color, influence); + blocks[blockIndex].isDefault = false; + } + } + + private void setMapDefaultColor(int defaultColor) { + for (int i = 0; i < blocks.length; ++i) + blocks[i].setBaseColor(defaultColor, 0, true); + } + + private enum LineType {EMPTY, COMMENT, ERROR, DEFAULT, COLOR_LINE} + } } diff --git a/src/togos/minecraft/maprend/block-colors.txt b/src/togos/minecraft/maprend/block-colors.txt index 27cb442..f67edc8 100644 --- a/src/togos/minecraft/maprend/block-colors.txt +++ b/src/togos/minecraft/maprend/block-colors.txt @@ -2,7 +2,6 @@ # You can use your own color map using the -color-map argument. # # The format is block ID, optionally colon and metadata, tab, color, optionally followed by another tab, a pound, and a comment. -# Tabs are important; don't use spaces or commas! # # Empty lines and lines starting with # are ignored, too. # diff --git a/test/togos/minecraft/maprend/BlockMapTest.java b/test/togos/minecraft/maprend/BlockMapTest.java new file mode 100644 index 0000000..2c64071 --- /dev/null +++ b/test/togos/minecraft/maprend/BlockMapTest.java @@ -0,0 +1,95 @@ +package togos.minecraft.maprend; + +import org.junit.Before; +import org.junit.Test; + +import java.io.BufferedReader; +import java.io.StringReader; + +import static junit.framework.Assert.*; + +public class BlockMapTest { + + private TestBlockLoader loader; + + @Before + public void setUp() throws Exception { + loader = new TestBlockLoader(); + } + + @Test + public void blockMapLoad_loadsLines() throws Exception { + String testLines = + "\n" + + "#Comment\n" + + "default 0xEEEEEEEE\n" + + "0x0003:6 0xFEED biome_grass"; + BufferedReader lines = new BufferedReader(new StringReader(testLines)); + BlockMap loadedMap = BlockMap.load(lines, "test"); + BlockMap.Block unmappedBlock = loadedMap.blocks[0x100]; + BlockMap.Block mappedBlock = loadedMap.blocks[0x0001]; + BlockMap.Block subColorBlock = loadedMap.blocks[0x0002]; + BlockMap.Block grassSubColorBlock = loadedMap.blocks[0x0003]; + + assertTrue(unmappedBlock.isDefault); + assertEquals(0xEEEEEEEE, unmappedBlock.baseColor); + + assertFalse(grassSubColorBlock.isDefault); + assertTrue(grassSubColorBlock.hasSubColors[6]); + assertEquals(0xFEED, grassSubColorBlock.subColors[6]); + assertEquals(BlockMap.INF_GRASS, grassSubColorBlock.subColorInfluences[6]); + } + + private void assertBlockAttributes(String[] tokens, int id, int data, int color, int influence) { + loader.parseBlockColorLine(tokens); + assertEquals(id, loader.blockId); + assertEquals(data, loader.blockData); + assertEquals(color, loader.color); + assertEquals(influence, loader.influence); + } + + @Test + public void idAndColor() throws Exception { + String[] tokens = {"1", "2"}; + assertBlockAttributes(tokens, 1, -1, 2, BlockMap.INF_NONE); + } + + @Test + public void idTypeAndColor() throws Exception { + String[] tokens = {"10:9", "8"}; + assertBlockAttributes(tokens, 10, 9, 8, BlockMap.INF_NONE); + } + + @Test + public void idTypeColorAndGrass() throws Exception { + String[] tokens = {"20:19", "18", "biome_grass"}; + assertBlockAttributes(tokens, 20, 19, 18, BlockMap.INF_GRASS); + } + + @Test + public void idTypeColorAndFoliage() throws Exception { + String[] tokens = {"20:19", "18", "biome_foliage"}; + assertBlockAttributes(tokens, 20, 19, 18, BlockMap.INF_FOLIAGE); + } + + @Test + public void idTypeColorAndWater() throws Exception { + String[] tokens = {"20:19", "18", "biome_water"}; + assertBlockAttributes(tokens, 20, 19, 18, BlockMap.INF_WATER); + } + +} + +class TestBlockLoader extends BlockMap.BlockMapLoader { + int blockId; + int blockData; + int color; + int influence; + + protected void setBlockMap(int blockId, int blockData, int color, int influence) { + this.blockId = blockId; + this.blockData = blockData; + this.color = color; + this.influence = influence; + } +} \ No newline at end of file From aa35917396e317ced862a2f66d52302b6f66071e Mon Sep 17 00:00:00 2001 From: "Robert C. Martin" Date: Sun, 2 Mar 2014 11:15:43 +0000 Subject: [PATCH 03/16] detect empty lines properly --- src/togos/minecraft/maprend/BlockMap.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/togos/minecraft/maprend/BlockMap.java b/src/togos/minecraft/maprend/BlockMap.java index f7e7f39..a5aacf1 100644 --- a/src/togos/minecraft/maprend/BlockMap.java +++ b/src/togos/minecraft/maprend/BlockMap.java @@ -140,7 +140,7 @@ private boolean parseBlockMapLine(String line) { ; private LineType identifyLineType(String[] mapTokens) { - if (mapTokens.length == 0) + if (mapTokens.length == 1 && mapTokens[0].isEmpty()) return LineType.EMPTY; else if (mapTokens[0].startsWith("#")) return LineType.COMMENT; From 87862b47766b66ffc6be17fc7ed657b04ec134ff Mon Sep 17 00:00:00 2001 From: "Robert C. Martin" Date: Sun, 2 Mar 2014 11:23:17 +0000 Subject: [PATCH 04/16] removed old ColorMap left over from merge --- src/togos/minecraft/maprend/ColorMap.java | 133 ---------------------- 1 file changed, 133 deletions(-) delete mode 100644 src/togos/minecraft/maprend/ColorMap.java diff --git a/src/togos/minecraft/maprend/ColorMap.java b/src/togos/minecraft/maprend/ColorMap.java deleted file mode 100644 index dbf3cb3..0000000 --- a/src/togos/minecraft/maprend/ColorMap.java +++ /dev/null @@ -1,133 +0,0 @@ -package togos.minecraft.maprend; - -import java.io.BufferedReader; -import java.io.File; -import java.io.FileReader; -import java.io.IOException; -import java.io.InputStreamReader; - -public final class ColorMap -{ - public static final int INDEX_MASK = 0xFFFF; - public static final int SIZE = INDEX_MASK+1; - - public final BlockColors[] colors; - public ColorMap( BlockColors[] colors ) { - this.colors = colors; - } - - protected static int parseInt( String s ) { - // Integer.parseInt pukes if the number is too big for a signed integer! - // So use Long.parseLong and cast, instead. - if( s.startsWith("0x") ) return (int)Long.parseLong(s.substring(2), 16); - return (int)Long.parseLong(s); - } - - public static ColorMap load( BufferedReader s, String filename ) throws IOException { - BlockColors[] colors = new BlockColors[SIZE]; - for( int i=0; i= subColors.length) ? - baseColor : - hasSubColors[blockData] ? subColors[blockData] : baseColor; - } - - private void setSubColor( int blockData, int color ) { - if( blockData < 0 || blockData >= SUB_COLOR_COUNT ) { - throw new RuntimeException("Block data value out of bounds: "+blockData); - } - if( subColors == null ) { - hasSubColors = new boolean[SUB_COLOR_COUNT]; - subColors = new int[SUB_COLOR_COUNT]; - } - hasSubColors[blockData] = true; - subColors[blockData] = color; - } - - private void setBaseColor( int color ) { - baseColor = color; - } - } -} From 1f6787a84f69f1395746d922ef8d2ccf8c4357af Mon Sep 17 00:00:00 2001 From: "Robert C. Martin" Date: Thu, 6 Mar 2014 07:05:35 +0000 Subject: [PATCH 05/16] added new command arguments to test --- src/togos/minecraft/maprend/BoundingRect.java | 12 +++++++++ .../maprend/RegionRendererMainTest.java | 25 +++++++++++++++---- 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/src/togos/minecraft/maprend/BoundingRect.java b/src/togos/minecraft/maprend/BoundingRect.java index cd29e9e..9ebcf1a 100644 --- a/src/togos/minecraft/maprend/BoundingRect.java +++ b/src/togos/minecraft/maprend/BoundingRect.java @@ -17,4 +17,16 @@ public BoundingRect( int minX, int minY, int maxX, int maxY ) { this.maxX = maxX; this.maxY = maxY; } + + public boolean equals(Object obj) { + if (obj instanceof BoundingRect) { + BoundingRect r = (BoundingRect) obj; + return r.minX == minX && r.minY == minY && r.maxX == maxX && r.maxY == maxY; + } + return false; + } + + public String toString() { + return String.format("[min:(%d,%d) max:(%d,%d)]",minX, minY, maxX, maxY); + } } diff --git a/test/togos/minecraft/maprend/RegionRendererMainTest.java b/test/togos/minecraft/maprend/RegionRendererMainTest.java index 6a7835f..8212672 100644 --- a/test/togos/minecraft/maprend/RegionRendererMainTest.java +++ b/test/togos/minecraft/maprend/RegionRendererMainTest.java @@ -1,11 +1,11 @@ package togos.minecraft.maprend; -import java.io.File; - import org.junit.Test; +import togos.minecraft.maprend.RegionRenderer.RegionRendererCommand; + +import java.io.File; import static junit.framework.Assert.*; -import togos.minecraft.maprend.RegionRenderer.RegionRendererCommand; public class RegionRendererMainTest { private RegionRenderer.RegionRendererCommand main; @@ -16,10 +16,14 @@ public void defaultArguments() throws Exception { assertNull(main.outputDir); assertNull(main.createImageTree); assertNull(main.colorMapFile); + assertNull(main.biomeMapFile); assertNull(main.createTileHtml); assertFalse(main.forceReRender); assertFalse(main.debug); + assertFalse(main.createBigImage); + assertFalse(main.printHelpAndExit); assertEquals(0, main.regionFiles.size()); + assertEquals(BoundingRect.INFINITE, main.regionLimitRect); } private static String[] toArgs(String argString) { @@ -49,17 +53,28 @@ public void moreThanOneInputArgument() throws Exception { @Test public void flagArguments() throws Exception { - extractAndAssertValidArgs("in -o out -f -debug -create-tile-html -create-image-tree"); + extractAndAssertValidArgs("in -o out -f " + + "-debug -create-tile-html -create-image-tree " + + "-create-big-image -h"); assertTrue(main.forceReRender); assertTrue(main.debug); assertTrue(main.createTileHtml); assertTrue(main.createImageTree); + assertTrue(main.createBigImage); + assertTrue(main.printHelpAndExit); } @Test public void colorMapArgument() throws Exception { - extractAndAssertValidArgs("in -o out -color-map cm"); + extractAndAssertValidArgs("in -o out -color-map cm -biome-map bm"); assertEquals("cm", main.colorMapFile.getName()); + assertEquals("bm", main.biomeMapFile.getName()); + } + + @Test + public void regionLimitRectangle() throws Exception { + extractAndAssertValidArgs("in -o out -region-limit-rect 1 2 3 4"); + assertEquals(new BoundingRect(1, 2, 3, 4), main.regionLimitRect); } @Test From 20fed293df216bd3224c20416fca3b9b62ce59e6 Mon Sep 17 00:00:00 2001 From: "Robert C. Martin" Date: Thu, 6 Mar 2014 08:08:27 +0000 Subject: [PATCH 06/16] changed if(debug) System.err.print to debugMessage. Passed RegionRendererCommand into Renderer --- .../minecraft/maprend/RegionRenderer.java | 75 +++++++++++-------- .../maprend/RegionRendererMainTest.java | 4 +- 2 files changed, 46 insertions(+), 33 deletions(-) diff --git a/src/togos/minecraft/maprend/RegionRenderer.java b/src/togos/minecraft/maprend/RegionRenderer.java index d2bf6c8..fe1f900 100644 --- a/src/togos/minecraft/maprend/RegionRenderer.java +++ b/src/togos/minecraft/maprend/RegionRenderer.java @@ -14,7 +14,10 @@ public class RegionRenderer { - static class Timer { + + private RegionRendererCommand rendererCommand; + + static class Timer { public long regionLoading; public long preRendering; public long postProcessing; @@ -34,7 +37,6 @@ protected String formatTime( String name, long millis ) { public final Set defaultedBlockIds = new HashSet(); public final Set defaultedBlockIdDataValues = new HashSet(); public final Set defaultedBiomeIds = new HashSet(); - public final boolean debug; public final BlockMap blockMap; public final BiomeMap biomeMap; public final int air16Color; // Color of 16 air blocks stacked @@ -44,14 +46,14 @@ protected String formatTime( String name, long millis ) { */ private int shadeOpacityCutoff = 0x20; - public RegionRenderer( BlockMap blockMap, BiomeMap biomeMap, boolean debug ) { + public RegionRenderer( BlockMap blockMap, BiomeMap biomeMap, RegionRendererCommand cmd ) { + rendererCommand = cmd; assert blockMap != null; assert biomeMap != null; this.blockMap = blockMap; this.biomeMap = biomeMap; this.air16Color = Color.overlay( 0, getColor(0, 0, 0), 16 ); - this.debug = debug; } /** @@ -322,20 +324,20 @@ public void renderAll( RegionMap rm, File outputDir, boolean force ) throws IOEx for( Region r : rm.regions ) { if( r == null ) continue; - - if( debug ) System.err.print("Region "+pad(r.rx, 4)+", "+pad(r.rz, 4)+"..."); - - String imageFilename = "tile."+r.rx+"."+r.rz+".png"; + + debugMessage("Region " + pad(r.rx, 4) + ", " + pad(r.rz, 4) + "..."); + + String imageFilename = "tile."+r.rx+"."+r.rz+".png"; File imageFile = r.imageFile = new File( outputDir+"/"+imageFilename ); if( imageFile.exists() ) { if( !force && imageFile.lastModified() > r.regionFile.lastModified() ) { - if( debug ) System.err.println("image already up-to-date"); + debugMessage("image already up-to-date\n"); continue; } imageFile.delete(); } - if( debug ) System.err.println("generating "+imageFilename+"..."); + debugMessage("generating " + imageFilename + "...\n"); RegionFile rf = new RegionFile( r.regionFile ); BufferedImage bi; @@ -357,14 +359,20 @@ public void renderAll( RegionMap rm, File outputDir, boolean force ) throws IOEx } timer.total += System.currentTimeMillis() - startTime; } - - /** + + private void debugMessage(String debugMessage) { + if( rendererCommand.debug ) { + System.err.print(debugMessage); + } + } + + /** * Create a "tiles.html" file containing a table with * all region images (tile...png) that exist in outDir * within the given bounds (inclusive) */ public void createTileHtml( int minX, int minZ, int maxX, int maxZ, File outputDir ) { - if( debug ) System.err.println("Writing HTML tiles..."); + debugMessage("Writing HTML tiles...\n"); try { Writer w = new OutputStreamWriter(new FileOutputStream(new File(outputDir+"/tiles.html"))); w.write("\n"); @@ -393,15 +401,15 @@ public void createTileHtml( int minX, int minZ, int maxX, int maxZ, File outputD } public void createImageTree( RegionMap rm ) { - if( debug ) System.err.println("Composing image tree..."); + debugMessage("Composing image tree...\n"); ImageTreeComposer itc = new ImageTreeComposer(new ContentStore()); System.out.println( itc.compose( rm ) ); } public void createBigImage( RegionMap rm, File outputDir) { - if( debug ) System.err.println( "Creating big image..." ); + debugMessage("Creating big image...\n"); BigImageMerger bic = new BigImageMerger(); - bic.createBigImage( rm, outputDir, debug ); + bic.createBigImage( rm, outputDir, rendererCommand.debug ); } public static final String USAGE = @@ -437,7 +445,20 @@ protected static boolean singleDirectoryGiven( List files ) { static class RegionRendererCommand { - public static RegionRendererCommand fromArguments( String...args ) { + File outputDir = null; + boolean forceReRender = false; + boolean debug = false; + boolean printHelpAndExit = false; + File colorMapFile = null; + File biomeMapFile = null; + ArrayList regionFiles = new ArrayList(); + Boolean createTileHtml = null; + Boolean createImageTree = null; + boolean createBigImage = false; + BoundingRect regionLimitRect = BoundingRect.INFINITE; + public boolean overlayGrid = false; + + public static RegionRendererCommand fromArguments( String...args ) { RegionRendererCommand m = new RegionRendererCommand(); for( int i = 0; i < args.length; ++i ) { if( args[i].charAt(0) != '-' ) { @@ -448,6 +469,8 @@ public static RegionRendererCommand fromArguments( String...args ) { m.forceReRender = true; } else if( "-debug".equals(args[i]) ) { m.debug = true; + } else if ("-grid".equals(args[i])) { + m.overlayGrid = true; } else if( "-create-tile-html".equals(args[i]) ) { m.createTileHtml = Boolean.TRUE; } else if( "-create-image-tree".equals(args[i]) ) { @@ -474,7 +497,7 @@ public static RegionRendererCommand fromArguments( String...args ) { m.errorMessage = validateSettings(m); return m; } - + private static String validateSettings( RegionRendererCommand m ) { if( m.regionFiles.size() == 0 ) return "No regions or directories specified."; @@ -483,19 +506,7 @@ else if( m.outputDir == null ) else return null; } - - File outputDir = null; - boolean forceReRender = false; - boolean debug = false; - boolean printHelpAndExit = false; - File colorMapFile = null; - File biomeMapFile = null; - ArrayList regionFiles = new ArrayList(); - Boolean createTileHtml = null; - Boolean createImageTree = null; - boolean createBigImage = false; - BoundingRect regionLimitRect = BoundingRect.INFINITE; - + String errorMessage = null; static boolean getDefault( Boolean b, boolean defaultValue ) { @@ -528,7 +539,7 @@ public int run() throws IOException { BiomeMap.load( biomeMapFile ); RegionMap rm = RegionMap.load(regionFiles, regionLimitRect); - RegionRenderer rr = new RegionRenderer(colorMap, biomeMap, debug); + RegionRenderer rr = new RegionRenderer(colorMap, biomeMap, this); rr.renderAll(rm, outputDir, forceReRender); if( debug ) { diff --git a/test/togos/minecraft/maprend/RegionRendererMainTest.java b/test/togos/minecraft/maprend/RegionRendererMainTest.java index 8212672..9ef4e41 100644 --- a/test/togos/minecraft/maprend/RegionRendererMainTest.java +++ b/test/togos/minecraft/maprend/RegionRendererMainTest.java @@ -24,6 +24,7 @@ public void defaultArguments() throws Exception { assertFalse(main.printHelpAndExit); assertEquals(0, main.regionFiles.size()); assertEquals(BoundingRect.INFINITE, main.regionLimitRect); + assertFalse(main.overlayGrid); } private static String[] toArgs(String argString) { @@ -55,13 +56,14 @@ public void moreThanOneInputArgument() throws Exception { public void flagArguments() throws Exception { extractAndAssertValidArgs("in -o out -f " + "-debug -create-tile-html -create-image-tree " + - "-create-big-image -h"); + "-create-big-image -h -grid"); assertTrue(main.forceReRender); assertTrue(main.debug); assertTrue(main.createTileHtml); assertTrue(main.createImageTree); assertTrue(main.createBigImage); assertTrue(main.printHelpAndExit); + assertTrue(main.overlayGrid); } @Test From 9b2459c2ef8b81db5dd3cf22e9e9d466453edf64 Mon Sep 17 00:00:00 2001 From: "Robert C. Martin" Date: Thu, 6 Mar 2014 09:40:45 +0000 Subject: [PATCH 07/16] separate RegionRendererCommand --- .../minecraft/maprend/RegionRenderer.java | 191 +----------------- .../maprend/RegionRendererCommand.java | 186 +++++++++++++++++ ...st.java => RegionRendererCommandTest.java} | 5 +- 3 files changed, 191 insertions(+), 191 deletions(-) create mode 100644 src/togos/minecraft/maprend/RegionRendererCommand.java rename test/togos/minecraft/maprend/{RegionRendererMainTest.java => RegionRendererCommandTest.java} (95%) diff --git a/src/togos/minecraft/maprend/RegionRenderer.java b/src/togos/minecraft/maprend/RegionRenderer.java index fe1f900..004879c 100644 --- a/src/togos/minecraft/maprend/RegionRenderer.java +++ b/src/togos/minecraft/maprend/RegionRenderer.java @@ -411,201 +411,16 @@ public void createBigImage( RegionMap rm, File outputDir) { BigImageMerger bic = new BigImageMerger(); bic.createBigImage( rm, outputDir, rendererCommand.debug ); } - - public static final String USAGE = - "Usage: TMCMR [options] -o \n" + - " -h, -? ; print usage instructions and exit\n" + - " -f ; force re-render even when images are newer than regions\n" + - " -debug ; be chatty\n" + - " -color-map ; load a custom color map from the specified file\n" + - " -biome-map ; load a custom biome color map from the specified file\n" + - " -create-tile-html ; generate tiles.html in the output directory\n" + - " -create-image-tree ; generate a PicGrid-compatible image tree\n" + - " -create-big-image ; merges all rendered images into a single file\n" + - " -region-limit-rect ; limit which regions are rendered\n" + - " ; to those between the given region coordinates, e.g.\n" + - " ; 0 0 2 2 to render the 4 regions southeast of the origin.\n" + - "\n" + - "Input files may be 'region/' directories or individual '.mca' files.\n" + - "\n" + - "tiles.html will always be generated if a single directory is given as input.\n" + - "\n" + - "Compound image tree blobs will be written to ~/.ccouch/data/tmcmr/\n" + - "Compound images can then be rendered with PicGrid."; - - protected static final boolean booleanValue( Boolean b, boolean defalt ) { + + protected static final boolean booleanValue( Boolean b, boolean defalt ) { return b == null ? defalt : b.booleanValue(); } protected static boolean singleDirectoryGiven( List files ) { return files.size() == 1 && files.get(0).isDirectory(); } - - //// Command-line processing //// - - static class RegionRendererCommand - { - File outputDir = null; - boolean forceReRender = false; - boolean debug = false; - boolean printHelpAndExit = false; - File colorMapFile = null; - File biomeMapFile = null; - ArrayList regionFiles = new ArrayList(); - Boolean createTileHtml = null; - Boolean createImageTree = null; - boolean createBigImage = false; - BoundingRect regionLimitRect = BoundingRect.INFINITE; - public boolean overlayGrid = false; - public static RegionRendererCommand fromArguments( String...args ) { - RegionRendererCommand m = new RegionRendererCommand(); - for( int i = 0; i < args.length; ++i ) { - if( args[i].charAt(0) != '-' ) { - m.regionFiles.add(new File(args[i])); - } else if( "-o".equals(args[i]) ) { - m.outputDir = new File(args[++i]); - } else if( "-f".equals(args[i]) ) { - m.forceReRender = true; - } else if( "-debug".equals(args[i]) ) { - m.debug = true; - } else if ("-grid".equals(args[i])) { - m.overlayGrid = true; - } else if( "-create-tile-html".equals(args[i]) ) { - m.createTileHtml = Boolean.TRUE; - } else if( "-create-image-tree".equals(args[i]) ) { - m.createImageTree = Boolean.TRUE; - } else if( "-region-limit-rect".equals(args[i] ) ) { - int minX = Integer.parseInt(args[++i]); - int minY = Integer.parseInt(args[++i]); - int maxX = Integer.parseInt(args[++i]); - int maxY = Integer.parseInt(args[++i]); - m.regionLimitRect = new BoundingRect( minX, minY, maxX, maxY ); - } else if( "-create-big-image".equals(args[i]) ) { - m.createBigImage = true; - } else if( "-color-map".equals(args[i]) ) { - m.colorMapFile = new File(args[++i]); - } else if( "-biome-map".equals(args[i]) ) { - m.biomeMapFile = new File(args[++i]); - } else if( "-h".equals(args[i]) || "-?".equals(args[i]) || "--help".equals(args[i]) || "-help".equals(args[i]) ) { - m.printHelpAndExit = true; - } else { - m.errorMessage = "Unrecognised argument: " + args[i]; - return m; - } - } - m.errorMessage = validateSettings(m); - return m; - } - - private static String validateSettings( RegionRendererCommand m ) { - if( m.regionFiles.size() == 0 ) - return "No regions or directories specified."; - else if( m.outputDir == null ) - return "Output directory unspecified."; - else - return null; - } - - String errorMessage = null; - - static boolean getDefault( Boolean b, boolean defaultValue ) { - return b != null ? b.booleanValue() : defaultValue; - } - - public boolean shouldCreateTileHtml() { - return getDefault(this.createTileHtml, singleDirectoryGiven(regionFiles)); - } - - public boolean shouldCreateImageTree() { - return getDefault(this.createImageTree, false); - } - - public int run() throws IOException { - if( errorMessage != null ) { - System.err.println( "Error: "+errorMessage ); - System.err.println( USAGE ); - return 1; - } - if( printHelpAndExit ) { - System.out.println( USAGE ); - return 0; - } - - final BlockMap colorMap = colorMapFile == null ? BlockMap.loadDefault() : - BlockMap.load(colorMapFile); - - final BiomeMap biomeMap = biomeMapFile == null ? BiomeMap.loadDefault() : - BiomeMap.load( biomeMapFile ); - - RegionMap rm = RegionMap.load(regionFiles, regionLimitRect); - RegionRenderer rr = new RegionRenderer(colorMap, biomeMap, this); - - rr.renderAll(rm, outputDir, forceReRender); - if( debug ) { - final Timer tim = rr.timer; - System.err.println("Rendered " + tim.regionCount + " regions, " + tim.sectionCount + " sections in " + (tim.total) + "ms"); - System.err.println("The following times lines indicate milliseconds total, per region, and per section"); - System.err.println(tim.formatTime("Loading", tim.regionLoading)); - System.err.println(tim.formatTime("Pre-rendering", tim.preRendering)); - System.err.println(tim.formatTime("Post-processing", tim.postProcessing)); - System.err.println(tim.formatTime("Image saving", tim.imageSaving)); - System.err.println(tim.formatTime("Total", tim.total)); - System.err.println(); - - if( rr.defaultedBlockIds.size() > 0 ) { - System.err.println("The following block IDs were not explicitly mapped to colors:"); - int z=0; - for( int blockId : rr.defaultedBlockIds ) { - System.err.print(z == 0 ? " " : z % 10 == 0 ? ",\n " : ", "); - System.err.print(IDUtil.blockIdString(blockId)); - ++z; - } - System.err.println(); - } else { - System.err.println("All block IDs encountered were accounted for in the block color map."); - } - System.err.println(); - - if( rr.defaultedBlockIdDataValues.size() > 0 ) { - System.err.println("The following block ID + data value pairs were not explicitly mapped to colors"); - System.err.println("(this is not necessarily a problem, as the base IDs were mapped to a color):"); - int z=0; - for( int blockId : rr.defaultedBlockIdDataValues ) { - System.err.print(z == 0 ? " " : z % 10 == 0 ? ",\n " : ", "); - System.err.print(IDUtil.blockIdString(blockId)); - ++z; - } - System.err.println(); - } else { - System.err.println("All block ID + data value pairs encountered were accounted for in the block color map."); - } - System.err.println(); - - if( rr.defaultedBiomeIds.size() > 0 ) { - System.err.println("The following biome IDs were not explicitly mapped to colors:"); - int z = 0; - for( int biomeId : rr.defaultedBiomeIds ) { - System.err.print(z == 0 ? " " : z % 10 == 0 ? ",\n " : ", "); - System.err.print(String.format("0x%02X", biomeId)); - ++z; - } - System.err.println(); - } else { - System.err.println("All biome IDs encountered were accounted for in the biome color map."); - } - System.err.println(); - } - - if( shouldCreateTileHtml() ) rr.createTileHtml(rm.minX, rm.minZ, rm.maxX, rm.maxZ, outputDir); - if( shouldCreateImageTree() ) rr.createImageTree(rm); - if( createBigImage ) rr.createBigImage(rm, outputDir); - - return 0; - } - } - - public static void main( String[] args ) throws Exception { + public static void main( String[] args ) throws Exception { System.exit( RegionRendererCommand.fromArguments( args ).run() ); } } diff --git a/src/togos/minecraft/maprend/RegionRendererCommand.java b/src/togos/minecraft/maprend/RegionRendererCommand.java new file mode 100644 index 0000000..de5ce40 --- /dev/null +++ b/src/togos/minecraft/maprend/RegionRendererCommand.java @@ -0,0 +1,186 @@ +package togos.minecraft.maprend; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; + +class RegionRendererCommand { + File outputDir = null; + boolean forceReRender = false; + boolean debug = false; + boolean printHelpAndExit = false; + File colorMapFile = null; + File biomeMapFile = null; + ArrayList regionFiles = new ArrayList(); + Boolean createTileHtml = null; + Boolean createImageTree = null; + boolean createBigImage = false; + BoundingRect regionLimitRect = BoundingRect.INFINITE; + public boolean overlayGrid = false; + public static RegionRendererCommand fromArguments(String... args) { + RegionRendererCommand m = new RegionRendererCommand(); + for (int i = 0; i < args.length; ++i) { + if (args[i].charAt(0) != '-') { + m.regionFiles.add(new File(args[i])); + } else if ("-o".equals(args[i])) { + m.outputDir = new File(args[++i]); + } else if ("-f".equals(args[i])) { + m.forceReRender = true; + } else if ("-debug".equals(args[i])) { + m.debug = true; + } else if ("-grid".equals(args[i])) { + m.overlayGrid = true; + } else if ("-create-tile-html".equals(args[i])) { + m.createTileHtml = Boolean.TRUE; + } else if ("-create-image-tree".equals(args[i])) { + m.createImageTree = Boolean.TRUE; + } else if ("-region-limit-rect".equals(args[i])) { + int minX = Integer.parseInt(args[++i]); + int minY = Integer.parseInt(args[++i]); + int maxX = Integer.parseInt(args[++i]); + int maxY = Integer.parseInt(args[++i]); + m.regionLimitRect = new BoundingRect(minX, minY, maxX, maxY); + } else if ("-create-big-image".equals(args[i])) { + m.createBigImage = true; + } else if ("-color-map".equals(args[i])) { + m.colorMapFile = new File(args[++i]); + } else if ("-biome-map".equals(args[i])) { + m.biomeMapFile = new File(args[++i]); + } else if ("-h".equals(args[i]) || "-?".equals(args[i]) || "--help".equals(args[i]) || "-help".equals(args[i])) { + m.printHelpAndExit = true; + } else { + m.errorMessage = "Unrecognised argument: " + args[i]; + return m; + } + } + m.errorMessage = validateSettings(m); + return m; + } + + private static String validateSettings(RegionRendererCommand m) { + if (m.regionFiles.size() == 0) + return "No regions or directories specified."; + else if (m.outputDir == null) + return "Output directory unspecified."; + else + return null; + } + + String errorMessage = null; + + static boolean getDefault(Boolean b, boolean defaultValue) { + return b != null ? b.booleanValue() : defaultValue; + } + + public boolean shouldCreateTileHtml() { + return getDefault(this.createTileHtml, RegionRenderer.singleDirectoryGiven(regionFiles)); + } + + public boolean shouldCreateImageTree() { + return getDefault(this.createImageTree, false); + } + + public int run() throws IOException { + if (errorMessage != null) { + System.err.println("Error: " + errorMessage); + System.err.println(USAGE); + return 1; + } + if (printHelpAndExit) { + System.out.println(USAGE); + return 0; + } + + final BlockMap colorMap = colorMapFile == null ? BlockMap.loadDefault() : + BlockMap.load(colorMapFile); + + final BiomeMap biomeMap = biomeMapFile == null ? BiomeMap.loadDefault() : + BiomeMap.load(biomeMapFile); + + RegionMap rm = RegionMap.load(regionFiles, regionLimitRect); + RegionRenderer rr = new RegionRenderer(colorMap, biomeMap, this); + + rr.renderAll(rm, outputDir, forceReRender); + if (debug) { + final RegionRenderer.Timer tim = rr.timer; + System.err.println("Rendered " + tim.regionCount + " regions, " + tim.sectionCount + " sections in " + (tim.total) + "ms"); + System.err.println("The following times lines indicate milliseconds total, per region, and per section"); + System.err.println(tim.formatTime("Loading", tim.regionLoading)); + System.err.println(tim.formatTime("Pre-rendering", tim.preRendering)); + System.err.println(tim.formatTime("Post-processing", tim.postProcessing)); + System.err.println(tim.formatTime("Image saving", tim.imageSaving)); + System.err.println(tim.formatTime("Total", tim.total)); + System.err.println(); + + if (rr.defaultedBlockIds.size() > 0) { + System.err.println("The following block IDs were not explicitly mapped to colors:"); + int z = 0; + for (int blockId : rr.defaultedBlockIds) { + System.err.print(z == 0 ? " " : z % 10 == 0 ? ",\n " : ", "); + System.err.print(IDUtil.blockIdString(blockId)); + ++z; + } + System.err.println(); + } else { + System.err.println("All block IDs encountered were accounted for in the block color map."); + } + System.err.println(); + + if (rr.defaultedBlockIdDataValues.size() > 0) { + System.err.println("The following block ID + data value pairs were not explicitly mapped to colors"); + System.err.println("(this is not necessarily a problem, as the base IDs were mapped to a color):"); + int z = 0; + for (int blockId : rr.defaultedBlockIdDataValues) { + System.err.print(z == 0 ? " " : z % 10 == 0 ? ",\n " : ", "); + System.err.print(IDUtil.blockIdString(blockId)); + ++z; + } + System.err.println(); + } else { + System.err.println("All block ID + data value pairs encountered were accounted for in the block color map."); + } + System.err.println(); + + if (rr.defaultedBiomeIds.size() > 0) { + System.err.println("The following biome IDs were not explicitly mapped to colors:"); + int z = 0; + for (int biomeId : rr.defaultedBiomeIds) { + System.err.print(z == 0 ? " " : z % 10 == 0 ? ",\n " : ", "); + System.err.print(String.format("0x%02X", biomeId)); + ++z; + } + System.err.println(); + } else { + System.err.println("All biome IDs encountered were accounted for in the biome color map."); + } + System.err.println(); + } + + if (shouldCreateTileHtml()) rr.createTileHtml(rm.minX, rm.minZ, rm.maxX, rm.maxZ, outputDir); + if (shouldCreateImageTree()) rr.createImageTree(rm); + if (createBigImage) rr.createBigImage(rm, outputDir); + + return 0; + } + + public static final String USAGE = + "Usage: TMCMR [options] -o \n" + + " -h, -? ; print usage instructions and exit\n" + + " -f ; force re-render even when images are newer than regions\n" + + " -debug ; be chatty\n" + + " -color-map ; load a custom color map from the specified file\n" + + " -biome-map ; load a custom biome color map from the specified file\n" + + " -create-tile-html ; generate tiles.html in the output directory\n" + + " -create-image-tree ; generate a PicGrid-compatible image tree\n" + + " -create-big-image ; merges all rendered images into a single file\n" + + " -region-limit-rect ; limit which regions are rendered\n" + + " ; to those between the given region coordinates, e.g.\n" + + " ; 0 0 2 2 to render the 4 regions southeast of the origin.\n" + + "\n" + + "Input files may be 'region/' directories or individual '.mca' files.\n" + + "\n" + + "tiles.html will always be generated if a single directory is given as input.\n" + + "\n" + + "Compound image tree blobs will be written to ~/.ccouch/data/tmcmr/\n" + + "Compound images can then be rendered with PicGrid."; +} diff --git a/test/togos/minecraft/maprend/RegionRendererMainTest.java b/test/togos/minecraft/maprend/RegionRendererCommandTest.java similarity index 95% rename from test/togos/minecraft/maprend/RegionRendererMainTest.java rename to test/togos/minecraft/maprend/RegionRendererCommandTest.java index 9ef4e41..a63845c 100644 --- a/test/togos/minecraft/maprend/RegionRendererMainTest.java +++ b/test/togos/minecraft/maprend/RegionRendererCommandTest.java @@ -1,14 +1,13 @@ package togos.minecraft.maprend; import org.junit.Test; -import togos.minecraft.maprend.RegionRenderer.RegionRendererCommand; import java.io.File; import static junit.framework.Assert.*; -public class RegionRendererMainTest { - private RegionRenderer.RegionRendererCommand main; +public class RegionRendererCommandTest { + private RegionRendererCommand main; @Test public void defaultArguments() throws Exception { From 53776c8f06dfab8f7968f4a1b829b45949393da7 Mon Sep 17 00:00:00 2001 From: "Robert C. Martin" Date: Thu, 6 Mar 2014 12:33:19 +0000 Subject: [PATCH 08/16] got grid overlay working --- src/togos/minecraft/maprend/RegionGrid.java | 69 +++++++++ .../minecraft/maprend/RegionRenderer.java | 9 +- .../minecraft/maprend/RegionGridTest.java | 142 ++++++++++++++++++ 3 files changed, 219 insertions(+), 1 deletion(-) create mode 100644 src/togos/minecraft/maprend/RegionGrid.java create mode 100644 test/togos/minecraft/maprend/RegionGridTest.java diff --git a/src/togos/minecraft/maprend/RegionGrid.java b/src/togos/minecraft/maprend/RegionGrid.java new file mode 100644 index 0000000..61dee6c --- /dev/null +++ b/src/togos/minecraft/maprend/RegionGrid.java @@ -0,0 +1,69 @@ +package togos.minecraft.maprend; + +import java.awt.*; +import java.awt.Color; +import java.awt.image.BufferedImage; +import java.util.HashSet; +import java.util.Set; + +class RegionGrid { + private int rx; + private int rz; + private int gridSize; + private int REGION_DIMENSION = 512; + private final int CROSS_SIZE = 3; + + public RegionGrid(int rx, int rz, int gridSize) { + this.rx = rx; + this.rz = rz; + this.gridSize = gridSize; + } + + void overlayGridOnImage(BufferedImage bi) { + Set crosses = getCrosses(); + Graphics g = bi.getGraphics(); + markCrosses(g, crosses); + } + + void markCrosses(Graphics g, Set crosses) { + for (Point cross : crosses) + markCross(g, cross); + } + + private void markCross(Graphics g, Point cross) { + int x = cross.x; + int y = cross.y; + g.setColor(Color.RED); + g.drawLine(x - CROSS_SIZE, y, x + CROSS_SIZE, y); + g.setColor(Color.BLACK); + g.drawLine(x, y - CROSS_SIZE, x, y + CROSS_SIZE); + } + + Set getCrosses() { + HashSet crosses = new HashSet(); + for (int x = toRegionOffset(rx); x < REGION_DIMENSION; x += gridSize) + for (int z = toRegionOffset(rz); z < REGION_DIMENSION; z += gridSize) + addCross(crosses, x, z); + return crosses; + } + + private int toRegionOffset(int regionCoordinate) { + int mapCoordinate = regionCoordinate * REGION_DIMENSION; + int remainder = mapCoordinate % gridSize; + int overshooot = remainder < 0 ? remainder + gridSize : remainder; + int offset = gridSize - overshooot; + return offset % gridSize; + } + + private boolean addCross(HashSet crosses, int x, int z) { + return crosses.add(new Point(x, z)); + } + + public int getRegionDimension() { + return REGION_DIMENSION; + } + + public void setRegionDimension(int regionDimension) { + this.REGION_DIMENSION = regionDimension; + } +} diff --git a/src/togos/minecraft/maprend/RegionRenderer.java b/src/togos/minecraft/maprend/RegionRenderer.java index 004879c..885fc82 100644 --- a/src/togos/minecraft/maprend/RegionRenderer.java +++ b/src/togos/minecraft/maprend/RegionRenderer.java @@ -10,7 +10,10 @@ import javax.imageio.ImageIO; import java.awt.image.BufferedImage; import java.io.*; -import java.util.*; +import java.util.Date; +import java.util.HashSet; +import java.util.List; +import java.util.Set; public class RegionRenderer { @@ -346,6 +349,9 @@ public void renderAll( RegionMap rm, File outputDir, boolean force ) throws IOEx } finally { rf.close(); } + + if (rendererCommand.overlayGrid) + new RegionGrid(r.rx, r.rz, 100).overlayGridOnImage(bi); try { resetInterval(); @@ -423,4 +429,5 @@ protected static boolean singleDirectoryGiven( List files ) { public static void main( String[] args ) throws Exception { System.exit( RegionRendererCommand.fromArguments( args ).run() ); } + } diff --git a/test/togos/minecraft/maprend/RegionGridTest.java b/test/togos/minecraft/maprend/RegionGridTest.java new file mode 100644 index 0000000..7e500cb --- /dev/null +++ b/test/togos/minecraft/maprend/RegionGridTest.java @@ -0,0 +1,142 @@ +package togos.minecraft.maprend; + +import org.junit.Test; + +import java.awt.*; +import java.util.HashSet; +import java.util.Set; + +import static junit.framework.Assert.assertEquals; +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + + +public class RegionGridTest { + + private Set crosses; + + Set set(int... coords) { + Set pointSet = new HashSet(); + for (int i = 0; i < coords.length; i += 2) + pointSet.add(new Point(coords[i], coords[i + 1])); + return pointSet; + } + + private void makeCrossesForRegion(int rx, int rz, int regionDimension, int gridSize) { + RegionGrid grid = new RegionGrid(rx, rz, gridSize); + grid.setRegionDimension(regionDimension); + crosses = grid.getCrosses(); + } + + private void assertCrosses(int... coords) { + assertThat(crosses, is(set(coords))); + } + + @Test + public void degenerateOriginRegion() throws Exception { + makeCrossesForRegion(0, 0, 1, 1); + assertCrosses(0, 0); + } + + @Test + public void originDimension2GridSize1() throws Exception { + makeCrossesForRegion(0, 0, 2, 1); + assertCrosses(0, 0, 0, 1, 1, 0, 1, 1); + } + + @Test + public void originDimension2GridSize2() throws Exception { + makeCrossesForRegion(0, 0, 2, 2); + assertCrosses(0, 0); + } + + @Test + public void OriginDimension3GridSize2() throws Exception { + makeCrossesForRegion(0, 0, 3, 2); + assertCrosses(0, 0, 0, 2, 2, 0, 2, 2); + } + + @Test + public void OneZeroDimension3GridSize2() throws Exception { + // 012 012 + //0 x.x .x. + //1 ... ... + //2 x.x .x. + makeCrossesForRegion(1, 0, 3, 2); + assertCrosses(1, 0, 1, 2); + } + + @Test + public void ZeroOneDimension3GridSize2() throws Exception { + // 012 + //0 x.x + //1 ... + //2 x.x + // + //0 ... + //1 x.x + //2 ... + makeCrossesForRegion(0, 1, 3, 2); + assertCrosses(0, 1, 2, 1); + } + + @Test + public void OneOneDimension3GridSize2() throws Exception { + // 012 + //0 x.x + //1 ... + //2 x.x + // 012 + //0 ... ... + //1 x.x .x. + //2 ... ... + makeCrossesForRegion(1, 1, 3, 2); + assertCrosses(1,1); + } + + @Test + public void M1M1Dimension3GridSize2() throws Exception { + //012 012 + //... 0 ... + //.x. 1 x.x + //... 2 ... + //----+---- + //.x. 0 x.x .x. + //... 1 ... ... + //.x. 2 x.x .x. + makeCrossesForRegion(-1, -1, 3, 2); + assertCrosses(1,1); + } + + @Test + public void M1M1Dimension5GridSize4() throws Exception { + // 01234 01234 + // ..... 0 ..... + // .x... 1 x...x + // ..... 2 ..... + // ..... 3 ..... + // ..... 4 ..... + //-------+------ + // .x... 0 x...x + // ..... 1 ..... + // ..... 2 ..... + // ..... 3 ..... + // .x... 4 x...x + makeCrossesForRegion(-1, -1, 5, 4); + assertCrosses(1,1); + makeCrossesForRegion(0,-1,5,4); + assertCrosses(0,1,4,1); + makeCrossesForRegion(-2,-2,5,4); + assertCrosses(2,2); + makeCrossesForRegion(1,1,5,4); + assertCrosses(3,3); + } + + @Test + public void modulus() throws Exception { + assertEquals(-1, -3 % 2); + assertEquals(-1, -5 % 2); + assertEquals(-3, -10 % 7); + } + +} From eb5983b705688458d27c970e4784c56c9f454ae6 Mon Sep 17 00:00:00 2001 From: "Robert C. Martin" Date: Mon, 31 Oct 2016 17:16:04 +0000 Subject: [PATCH 09/16] new block values --- src/togos/minecraft/maprend/block-colors.txt | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/togos/minecraft/maprend/block-colors.txt b/src/togos/minecraft/maprend/block-colors.txt index 3cc4c97..5881dcc 100644 --- a/src/togos/minecraft/maprend/block-colors.txt +++ b/src/togos/minecraft/maprend/block-colors.txt @@ -474,3 +474,13 @@ default 0xFFFF00FF 0x00C3 0xCF9A6E4D # Jungle Door 0x00C4 0xCFAD5D32 # Acacia Door 0x00C5 0xCF3E2912 # Dark Oak Door +0x00D0 0xFF808000 # Grass Path +0x00D8 0xFFFFFFF0 # Bone Block +0x00CF:0x0 0x0910E010 # Beets +0x00CF:0x1 0x0920D010 # Beets +0x00CF:0x2 0x1730C010 # Beets +0x00CF:0x3 0x1750A010 # Beets +0x00CF:0x4 0x36808010 # Beets +0x00CF:0x5 0x36FF2010 # Beets +0x00CF:0x6 0x36FF1010 # Beets +0x00CF:0x7 0x6FFF0010 # Beets From 5026f82362b871cfaadefd16715922d63656faf6 Mon Sep 17 00:00:00 2001 From: "Robert C. Martin" Date: Mon, 31 Oct 2016 23:37:30 +0000 Subject: [PATCH 10/16] More block colors --- src/togos/minecraft/maprend/block-colors.txt | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/togos/minecraft/maprend/block-colors.txt b/src/togos/minecraft/maprend/block-colors.txt index 5881dcc..5b9d6de 100644 --- a/src/togos/minecraft/maprend/block-colors.txt +++ b/src/togos/minecraft/maprend/block-colors.txt @@ -11,10 +11,17 @@ default 0xFFFF00FF 0x0000 0x00000000 # Air -0x0001 0xFF7D7D7D # Stone +0x0001:0 0xFF7D7D7D # Stone +0x0001:1 0xFFB9826E # Granite +0x0001:2 0xFFB9826E # Polished Granite +0x0001:3 0xFF9D9D9D # Diorite +0x0001:4 0xFF9D9D9D # Polished Diorite +0x0001:5 0xFF75766A # Andesite +0x0001:6 0xFF75766A # Polished Andesite + 0x0002 0xFF939393 biome_grass # Grass Block 0x0003:0x0 0xFF866043 # Dirt -0x0003:0x1 0xFF866043 # Grassless Dirt +0x0003:0x1 0xFF805840 # Grassless Dirt 0x0003:0x2 0xFF47381B # Podzol 0x0004 0xFF7A7A7A # Cobblestone 0x0005:0x0 0xFF9C7F4E # Oak Wood Planks @@ -46,7 +53,7 @@ default 0xFFFF00FF 0x000D 0xFF7E7C7A # Gravel 0x000E 0xFF8F8B7C # Gold Ore 0x000F 0xFF87827E # Iron Ore -0x0010 0xFF737373 # Coal Ore +0x0010 0xFF202020 # Coal Ore 0x0011:0x0 0xFF9A7D4D # Oak Wood 0x0011:0x1 0xFF9A7D4D # Spruce Wood 0x0011:0x2 0xFF9A7D4D # Birch Wood From 01290b88135d8ba59ba895c395e0329c996831b9 Mon Sep 17 00:00:00 2001 From: "Robert C. Martin" Date: Tue, 1 Nov 2016 00:06:24 +0000 Subject: [PATCH 11/16] show diamons --- src/togos/minecraft/maprend/RegionRenderer.java | 5 ++++- src/togos/minecraft/maprend/RegionRendererCommand.java | 5 +++++ test/togos/minecraft/maprend/RegionRendererCommandTest.java | 4 +++- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/togos/minecraft/maprend/RegionRenderer.java b/src/togos/minecraft/maprend/RegionRenderer.java index 885fc82..417c7c5 100644 --- a/src/togos/minecraft/maprend/RegionRenderer.java +++ b/src/togos/minecraft/maprend/RegionRenderer.java @@ -242,6 +242,7 @@ protected void preRender( RegionFile rf, int[] colors, short[] heights ) { for( int x=0; x<16; ++x ) { int pixelColor = 0; short pixelHeight = 0; + boolean diamond = false; int biomeId = biomeIds[z*16+x]&0xFF; for( int s=0; s= shadeOpacityCutoff ) { @@ -264,7 +267,7 @@ protected void preRender( RegionFile rf, int[] colors, short[] heights ) { } final int dIdx = 512*(cz*16+z)+16*cx+x; - colors[dIdx] = pixelColor; + colors[dIdx] = diamond ? 0xFF00FFFF : pixelColor; heights[dIdx] = pixelHeight; } } diff --git a/src/togos/minecraft/maprend/RegionRendererCommand.java b/src/togos/minecraft/maprend/RegionRendererCommand.java index de5ce40..a1a84be 100644 --- a/src/togos/minecraft/maprend/RegionRendererCommand.java +++ b/src/togos/minecraft/maprend/RegionRendererCommand.java @@ -17,6 +17,7 @@ class RegionRendererCommand { boolean createBigImage = false; BoundingRect regionLimitRect = BoundingRect.INFINITE; public boolean overlayGrid = false; + public boolean showDiamonds = false; public static RegionRendererCommand fromArguments(String... args) { RegionRendererCommand m = new RegionRendererCommand(); for (int i = 0; i < args.length; ++i) { @@ -30,6 +31,8 @@ public static RegionRendererCommand fromArguments(String... args) { m.debug = true; } else if ("-grid".equals(args[i])) { m.overlayGrid = true; + } else if ("-D".equals(args[i])) { + m.showDiamonds = true; } else if ("-create-tile-html".equals(args[i])) { m.createTileHtml = Boolean.TRUE; } else if ("-create-image-tree".equals(args[i])) { @@ -167,7 +170,9 @@ public int run() throws IOException { "Usage: TMCMR [options] -o \n" + " -h, -? ; print usage instructions and exit\n" + " -f ; force re-render even when images are newer than regions\n" + + " -grid ; Overlay a 100x100 grid on the map.\n" + " -debug ; be chatty\n" + + " -D ; show diamonds\n" + " -color-map ; load a custom color map from the specified file\n" + " -biome-map ; load a custom biome color map from the specified file\n" + " -create-tile-html ; generate tiles.html in the output directory\n" + diff --git a/test/togos/minecraft/maprend/RegionRendererCommandTest.java b/test/togos/minecraft/maprend/RegionRendererCommandTest.java index a63845c..3e2c50e 100644 --- a/test/togos/minecraft/maprend/RegionRendererCommandTest.java +++ b/test/togos/minecraft/maprend/RegionRendererCommandTest.java @@ -24,6 +24,7 @@ public void defaultArguments() throws Exception { assertEquals(0, main.regionFiles.size()); assertEquals(BoundingRect.INFINITE, main.regionLimitRect); assertFalse(main.overlayGrid); + assertFalse(main.showDiamonds); } private static String[] toArgs(String argString) { @@ -55,7 +56,7 @@ public void moreThanOneInputArgument() throws Exception { public void flagArguments() throws Exception { extractAndAssertValidArgs("in -o out -f " + "-debug -create-tile-html -create-image-tree " + - "-create-big-image -h -grid"); + "-create-big-image -h -grid -D"); assertTrue(main.forceReRender); assertTrue(main.debug); assertTrue(main.createTileHtml); @@ -63,6 +64,7 @@ public void flagArguments() throws Exception { assertTrue(main.createBigImage); assertTrue(main.printHelpAndExit); assertTrue(main.overlayGrid); + assertTrue(main.showDiamonds); } @Test From 66bd5d8bd12a77fd56fe392bb155cab44ac42737 Mon Sep 17 00:00:00 2001 From: "Robert C. Martin" Date: Tue, 1 Nov 2016 13:09:46 +0000 Subject: [PATCH 12/16] refactor argument parser to use lambdas --- .../maprend/RegionRendererCommand.java | 104 +++++++++++------- 1 file changed, 64 insertions(+), 40 deletions(-) diff --git a/src/togos/minecraft/maprend/RegionRendererCommand.java b/src/togos/minecraft/maprend/RegionRendererCommand.java index a1a84be..79f959e 100644 --- a/src/togos/minecraft/maprend/RegionRendererCommand.java +++ b/src/togos/minecraft/maprend/RegionRendererCommand.java @@ -18,48 +18,74 @@ class RegionRendererCommand { BoundingRect regionLimitRect = BoundingRect.INFINITE; public boolean overlayGrid = false; public boolean showDiamonds = false; + + private int argIndex; + private String[] args; + String errorMessage = null; + + public RegionRendererCommand(String[] args) { + this.args = args; + } + public static RegionRendererCommand fromArguments(String... args) { - RegionRendererCommand m = new RegionRendererCommand(); - for (int i = 0; i < args.length; ++i) { - if (args[i].charAt(0) != '-') { - m.regionFiles.add(new File(args[i])); - } else if ("-o".equals(args[i])) { - m.outputDir = new File(args[++i]); - } else if ("-f".equals(args[i])) { - m.forceReRender = true; - } else if ("-debug".equals(args[i])) { - m.debug = true; - } else if ("-grid".equals(args[i])) { - m.overlayGrid = true; - } else if ("-D".equals(args[i])) { - m.showDiamonds = true; - } else if ("-create-tile-html".equals(args[i])) { - m.createTileHtml = Boolean.TRUE; - } else if ("-create-image-tree".equals(args[i])) { - m.createImageTree = Boolean.TRUE; - } else if ("-region-limit-rect".equals(args[i])) { - int minX = Integer.parseInt(args[++i]); - int minY = Integer.parseInt(args[++i]); - int maxX = Integer.parseInt(args[++i]); - int maxY = Integer.parseInt(args[++i]); - m.regionLimitRect = new BoundingRect(minX, minY, maxX, maxY); - } else if ("-create-big-image".equals(args[i])) { - m.createBigImage = true; - } else if ("-color-map".equals(args[i])) { - m.colorMapFile = new File(args[++i]); - } else if ("-biome-map".equals(args[i])) { - m.biomeMapFile = new File(args[++i]); - } else if ("-h".equals(args[i]) || "-?".equals(args[i]) || "--help".equals(args[i]) || "-help".equals(args[i])) { - m.printHelpAndExit = true; - } else { - m.errorMessage = "Unrecognised argument: " + args[i]; - return m; - } - } - m.errorMessage = validateSettings(m); + RegionRendererCommand m = new RegionRendererCommand(args); + if (m.parseArguments()) + m.errorMessage = validateSettings(m); + return m; } + private boolean parseArguments() { + for (argIndex = 0; argIndex < args.length; ++argIndex) { + if (args[argIndex].charAt(0) == '-') { + if (!parseArg()) { + errorMessage = "Unrecognised argument: " + args[argIndex]; + return false; + } + } else + regionFiles.add(new File(args[argIndex])); + } + return true; + } + + private boolean parseArg() { + return doArg("-o", () -> outputDir = new File(args[++argIndex])) + || doArg("-f", () -> forceReRender = true) + || doArg("-debug", () -> debug = true) + || doArg("-grid", () -> overlayGrid = true) + || doArg("-D", () -> showDiamonds = true) + || doArg("-create-tile-html", () -> createTileHtml = true) + || doArg("-create-image-tree", () -> createImageTree = true) + || doArg("-create-big-image", () -> createBigImage = true) + || doArg("-color-map", () -> colorMapFile = new File(args[++argIndex])) + || doArg("-biome-map", () -> biomeMapFile = new File(args[++argIndex])) + || doArg("-h", () -> printHelpAndExit = true) + || doArg("-?", () -> printHelpAndExit = true) + || doArg("--help", () -> printHelpAndExit = true) + || doArg("-help", () -> printHelpAndExit = true) + || doArg("-region-limit-rect", () -> setRegionLimitRect()); + } + + private void setRegionLimitRect() { + int minX = Integer.parseInt(args[++argIndex]); + int minY = Integer.parseInt(args[++argIndex]); + int maxX = Integer.parseInt(args[++argIndex]); + int maxY = Integer.parseInt(args[++argIndex]); + regionLimitRect = new BoundingRect(minX, minY, maxX, maxY); + } + + private boolean doArg(String arg, Runnable r) { + if (argIs(arg)) { + r.run(); + return true; + } + return false; + } + + private boolean argIs(String arg) { + return arg.equals(args[argIndex]); + } + private static String validateSettings(RegionRendererCommand m) { if (m.regionFiles.size() == 0) return "No regions or directories specified."; @@ -69,8 +95,6 @@ else if (m.outputDir == null) return null; } - String errorMessage = null; - static boolean getDefault(Boolean b, boolean defaultValue) { return b != null ? b.booleanValue() : defaultValue; } From c64b7e22af62be6c12a9e650550301e200ebe93c Mon Sep 17 00:00:00 2001 From: "Robert C. Martin" Date: Thu, 3 Nov 2016 13:53:44 +0000 Subject: [PATCH 13/16] sort unmapped block ids --- Makefile | 2 +- .../minecraft/maprend/RegionRenderer.java | 5 +- .../maprend/RegionRendererCommand.java | 174 ++++++++++++------ .../togos/minecraft/maprend/BlockMapTest.java | 3 +- 4 files changed, 120 insertions(+), 64 deletions(-) diff --git a/Makefile b/Makefile index 727914e..6dbfb52 100755 --- a/Makefile +++ b/Makefile @@ -9,7 +9,7 @@ TMCMR.jar: rm -rf bin TMCMR.jar cp -r src bin find src -name *.java >.src.lst - javac -source 1.6 -target 1.6 -d bin @.src.lst + javac -source 1.8 -target 1.8 -d bin @.src.lst mkdir -p bin/META-INF echo 'Version: 1.0' >bin/META-INF/MANIFEST.MF echo 'Main-Class: togos.minecraft.maprend.RegionRenderer' >>bin/META-INF/MANIFEST.MF diff --git a/src/togos/minecraft/maprend/RegionRenderer.java b/src/togos/minecraft/maprend/RegionRenderer.java index 417c7c5..ec9290e 100644 --- a/src/togos/minecraft/maprend/RegionRenderer.java +++ b/src/togos/minecraft/maprend/RegionRenderer.java @@ -319,7 +319,8 @@ protected static String pad( int v, int targetLength ) { return pad( ""+v, targetLength ); } - public void renderAll( RegionMap rm, File outputDir, boolean force ) throws IOException { + public void renderAll(RegionMap rm) throws IOException { + File outputDir = rendererCommand.outputDir; long startTime = System.currentTimeMillis(); if( !outputDir.exists() ) outputDir.mkdirs(); @@ -337,7 +338,7 @@ public void renderAll( RegionMap rm, File outputDir, boolean force ) throws IOEx File imageFile = r.imageFile = new File( outputDir+"/"+imageFilename ); if( imageFile.exists() ) { - if( !force && imageFile.lastModified() > r.regionFile.lastModified() ) { + if( !rendererCommand.forceReRender && imageFile.lastModified() > r.regionFile.lastModified() ) { debugMessage("image already up-to-date\n"); continue; } diff --git a/src/togos/minecraft/maprend/RegionRendererCommand.java b/src/togos/minecraft/maprend/RegionRendererCommand.java index 79f959e..1209056 100644 --- a/src/togos/minecraft/maprend/RegionRendererCommand.java +++ b/src/togos/minecraft/maprend/RegionRendererCommand.java @@ -2,7 +2,7 @@ import java.io.File; import java.io.IOException; -import java.util.ArrayList; +import java.util.*; class RegionRendererCommand { File outputDir = null; @@ -22,6 +22,7 @@ class RegionRendererCommand { private int argIndex; private String[] args; String errorMessage = null; + private boolean argsOk; public RegionRendererCommand(String[] args) { this.args = args; @@ -108,86 +109,126 @@ public boolean shouldCreateImageTree() { } public int run() throws IOException { + int status = 0; if (errorMessage != null) { - System.err.println("Error: " + errorMessage); - System.err.println(USAGE); - return 1; - } - if (printHelpAndExit) { + System.err.println("Error: " + errorMessage + "\n" + USAGE); + status = 1; + } else if (printHelpAndExit) System.out.println(USAGE); - return 0; - } + else + renderRegions(); + return status; + } - final BlockMap colorMap = colorMapFile == null ? BlockMap.loadDefault() : + private void renderRegions() throws IOException { + BlockMap colorMap = makeColorMap(); + BiomeMap biomeMap = makeBiomeMap(); + RegionMap rm = makeRegionMap(); + RegionRenderer rr = new RegionRenderer(colorMap, biomeMap, this); + rr.renderAll(rm); + generateSummaries(rm, rr); + } + + private BlockMap makeColorMap() throws IOException { + return colorMapFile == null ? BlockMap.loadDefault() : BlockMap.load(colorMapFile); + } - final BiomeMap biomeMap = biomeMapFile == null ? BiomeMap.loadDefault() : + private BiomeMap makeBiomeMap() throws IOException { + return biomeMapFile == null ? BiomeMap.loadDefault() : BiomeMap.load(biomeMapFile); + } - RegionMap rm = RegionMap.load(regionFiles, regionLimitRect); - RegionRenderer rr = new RegionRenderer(colorMap, biomeMap, this); + private RegionMap makeRegionMap() { + return RegionMap.load(regionFiles, regionLimitRect); + } + + private void generateSummaries(RegionMap rm, RegionRenderer rr) { + printDebugMessages(rr); + if (shouldCreateTileHtml()) + rr.createTileHtml(rm.minX, rm.minZ, rm.maxX, rm.maxZ, outputDir); + if (shouldCreateImageTree()) + rr.createImageTree(rm); + if (createBigImage) + rr.createBigImage(rm, outputDir); + } - rr.renderAll(rm, outputDir, forceReRender); + private void printDebugMessages(RegionRenderer rr) { if (debug) { - final RegionRenderer.Timer tim = rr.timer; - System.err.println("Rendered " + tim.regionCount + " regions, " + tim.sectionCount + " sections in " + (tim.total) + "ms"); - System.err.println("The following times lines indicate milliseconds total, per region, and per section"); - System.err.println(tim.formatTime("Loading", tim.regionLoading)); - System.err.println(tim.formatTime("Pre-rendering", tim.preRendering)); - System.err.println(tim.formatTime("Post-processing", tim.postProcessing)); - System.err.println(tim.formatTime("Image saving", tim.imageSaving)); - System.err.println(tim.formatTime("Total", tim.total)); + printTimings(rr); + printUnmappedBlockIds(rr); + printUnmappedBiomes(rr); System.err.println(); + } + } - if (rr.defaultedBlockIds.size() > 0) { - System.err.println("The following block IDs were not explicitly mapped to colors:"); - int z = 0; - for (int blockId : rr.defaultedBlockIds) { - System.err.print(z == 0 ? " " : z % 10 == 0 ? ",\n " : ", "); - System.err.print(IDUtil.blockIdString(blockId)); - ++z; - } - System.err.println(); - } else { - System.err.println("All block IDs encountered were accounted for in the block color map."); + private void printUnmappedBlockIds(RegionRenderer rr) { + printUnmappedBlocks(rr); + printUnmappedBlockDataPairs(rr); + } + + private void printUnmappedBlocks(RegionRenderer rr) { + List defaultedBlockIds = new ArrayList<>(rr.defaultedBlockIds); + Collections.sort(defaultedBlockIds); + if (defaultedBlockIds.size() > 0) { + System.err.println("The following block IDs were not explicitly mapped to colors:"); + int z = 0; + for (int blockId : defaultedBlockIds) { + System.err.print(z == 0 ? " " : z % 10 == 0 ? ",\n " : ", "); + System.err.print(IDUtil.blockIdString(blockId)); + ++z; } System.err.println(); + } else { + System.err.println("All block IDs encountered were accounted for in the block color map."); + } + System.err.println(); + } - if (rr.defaultedBlockIdDataValues.size() > 0) { - System.err.println("The following block ID + data value pairs were not explicitly mapped to colors"); - System.err.println("(this is not necessarily a problem, as the base IDs were mapped to a color):"); - int z = 0; - for (int blockId : rr.defaultedBlockIdDataValues) { - System.err.print(z == 0 ? " " : z % 10 == 0 ? ",\n " : ", "); - System.err.print(IDUtil.blockIdString(blockId)); - ++z; - } - System.err.println(); - } else { - System.err.println("All block ID + data value pairs encountered were accounted for in the block color map."); + private void printUnmappedBlockDataPairs(RegionRenderer rr) { + List defaultedBlockIdDataValues = new ArrayList<>(rr.defaultedBlockIdDataValues); + Collections.sort(defaultedBlockIdDataValues, new BlockIdComparator()); + if (defaultedBlockIdDataValues.size() > 0) { + System.err.println("The following block ID + data value pairs were not explicitly mapped to colors"); + System.err.println("(this is not necessarily a problem, as the base IDs were mapped to a color):"); + int z = 0; + for (int blockId : defaultedBlockIdDataValues) { + System.err.print(z == 0 ? " " : z % 10 == 0 ? ",\n " : ", "); + System.err.print(IDUtil.blockIdString(blockId)); + ++z; } System.err.println(); + } else { + System.err.println("All block ID + data value pairs encountered were accounted for in the block color map."); + } + System.err.println(); + } - if (rr.defaultedBiomeIds.size() > 0) { - System.err.println("The following biome IDs were not explicitly mapped to colors:"); - int z = 0; - for (int biomeId : rr.defaultedBiomeIds) { - System.err.print(z == 0 ? " " : z % 10 == 0 ? ",\n " : ", "); - System.err.print(String.format("0x%02X", biomeId)); - ++z; - } - System.err.println(); - } else { - System.err.println("All biome IDs encountered were accounted for in the biome color map."); + private void printUnmappedBiomes(RegionRenderer rr) { + if (rr.defaultedBiomeIds.size() > 0) { + System.err.println("The following biome IDs were not explicitly mapped to colors:"); + int z = 0; + for (int biomeId : rr.defaultedBiomeIds) { + System.err.print(z == 0 ? " " : z % 10 == 0 ? ",\n " : ", "); + System.err.print(String.format("0x%02X", biomeId)); + ++z; } System.err.println(); + } else { + System.err.println("All biome IDs encountered were accounted for in the biome color map."); } + } - if (shouldCreateTileHtml()) rr.createTileHtml(rm.minX, rm.minZ, rm.maxX, rm.maxZ, outputDir); - if (shouldCreateImageTree()) rr.createImageTree(rm); - if (createBigImage) rr.createBigImage(rm, outputDir); - - return 0; + private void printTimings(RegionRenderer rr) { + final RegionRenderer.Timer tim = rr.timer; + System.err.println("Rendered " + tim.regionCount + " regions, " + tim.sectionCount + " sections in " + (tim.total) + "ms"); + System.err.println("The following times lines indicate milliseconds total, per region, and per section"); + System.err.println(tim.formatTime("Loading", tim.regionLoading)); + System.err.println(tim.formatTime("Pre-rendering", tim.preRendering)); + System.err.println(tim.formatTime("Post-processing", tim.postProcessing)); + System.err.println(tim.formatTime("Image saving", tim.imageSaving)); + System.err.println(tim.formatTime("Total", tim.total)); + System.err.println(); } public static final String USAGE = @@ -212,4 +253,17 @@ public int run() throws IOException { "\n" + "Compound image tree blobs will be written to ~/.ccouch/data/tmcmr/\n" + "Compound images can then be rendered with PicGrid."; + + private static class BlockIdComparator implements Comparator { + public int compare(Integer o1, Integer o2) { + o1 = repack(o1); + o2 = repack(o2); + return o1.compareTo(o2); + } + + private Integer repack(Integer i) { + i = (i>>16 & 0xF) | ((i&0xffff)<<4); + return i; + } + } } diff --git a/test/togos/minecraft/maprend/BlockMapTest.java b/test/togos/minecraft/maprend/BlockMapTest.java index 2c64071..4ad0f00 100644 --- a/test/togos/minecraft/maprend/BlockMapTest.java +++ b/test/togos/minecraft/maprend/BlockMapTest.java @@ -1,12 +1,13 @@ package togos.minecraft.maprend; +import org.junit.Assert; import org.junit.Before; import org.junit.Test; import java.io.BufferedReader; import java.io.StringReader; -import static junit.framework.Assert.*; +import static org.junit.Assert.*; public class BlockMapTest { From 24f098de1b73377878213c4c4037e1982db53494 Mon Sep 17 00:00:00 2001 From: "Robert C. Martin" Date: Sat, 5 Nov 2016 15:43:03 +0000 Subject: [PATCH 14/16] air and torches --- .../minecraft/maprend/RegionRenderer.java | 18 +++++++++++++++--- .../maprend/RegionRendererCommand.java | 6 ++++++ .../maprend/RegionRendererCommandTest.java | 4 +++- 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/togos/minecraft/maprend/RegionRenderer.java b/src/togos/minecraft/maprend/RegionRenderer.java index ec9290e..443feef 100644 --- a/src/togos/minecraft/maprend/RegionRenderer.java +++ b/src/togos/minecraft/maprend/RegionRenderer.java @@ -243,6 +243,9 @@ protected void preRender( RegionFile rf, int[] colors, short[] heights ) { int pixelColor = 0; short pixelHeight = 0; boolean diamond = false; + boolean air = false; + boolean buriedAir = false; + boolean torch = false; int biomeId = biomeIds[z*16+x]&0xFF; for( int s=0; s= shadeOpacityCutoff ) { @@ -266,8 +276,10 @@ protected void preRender( RegionFile rf, int[] colors, short[] heights ) { } } - final int dIdx = 512*(cz*16+z)+16*cx+x; - colors[dIdx] = diamond ? 0xFF00FFFF : pixelColor; + final int dIdx = 512*(cz*16+z)+16*cx+x; + colors[dIdx] = buriedAir ? 0xFFFFFFFF : pixelColor; + colors[dIdx] = torch ? 0xFFFFFF00 : colors[dIdx]; + colors[dIdx] = diamond ? 0xFF00FFFF : colors[dIdx]; heights[dIdx] = pixelHeight; } } diff --git a/src/togos/minecraft/maprend/RegionRendererCommand.java b/src/togos/minecraft/maprend/RegionRendererCommand.java index 1209056..6cb351c 100644 --- a/src/togos/minecraft/maprend/RegionRendererCommand.java +++ b/src/togos/minecraft/maprend/RegionRendererCommand.java @@ -17,6 +17,8 @@ class RegionRendererCommand { boolean createBigImage = false; BoundingRect regionLimitRect = BoundingRect.INFINITE; public boolean overlayGrid = false; + public boolean showAir = false; + public boolean showTorches = false; public boolean showDiamonds = false; private int argIndex; @@ -55,6 +57,8 @@ private boolean parseArg() { || doArg("-debug", () -> debug = true) || doArg("-grid", () -> overlayGrid = true) || doArg("-D", () -> showDiamonds = true) + || doArg("-A", () -> showAir = true) + || doArg("-T", () -> showTorches = true) || doArg("-create-tile-html", () -> createTileHtml = true) || doArg("-create-image-tree", () -> createImageTree = true) || doArg("-create-big-image", () -> createBigImage = true) @@ -238,6 +242,8 @@ private void printTimings(RegionRenderer rr) { " -grid ; Overlay a 100x100 grid on the map.\n" + " -debug ; be chatty\n" + " -D ; show diamonds\n" + + " -A ; show underground air\n" + + " -T ; show torches\n" + " -color-map ; load a custom color map from the specified file\n" + " -biome-map ; load a custom biome color map from the specified file\n" + " -create-tile-html ; generate tiles.html in the output directory\n" + diff --git a/test/togos/minecraft/maprend/RegionRendererCommandTest.java b/test/togos/minecraft/maprend/RegionRendererCommandTest.java index 3e2c50e..177dfc1 100644 --- a/test/togos/minecraft/maprend/RegionRendererCommandTest.java +++ b/test/togos/minecraft/maprend/RegionRendererCommandTest.java @@ -56,7 +56,7 @@ public void moreThanOneInputArgument() throws Exception { public void flagArguments() throws Exception { extractAndAssertValidArgs("in -o out -f " + "-debug -create-tile-html -create-image-tree " + - "-create-big-image -h -grid -D"); + "-create-big-image -h -grid -D -A -T"); assertTrue(main.forceReRender); assertTrue(main.debug); assertTrue(main.createTileHtml); @@ -65,6 +65,8 @@ public void flagArguments() throws Exception { assertTrue(main.printHelpAndExit); assertTrue(main.overlayGrid); assertTrue(main.showDiamonds); + assertTrue(main.showAir); + assertTrue(main.showTorches); } @Test From 43a80adfb675d7a5536fbcdc868b09533c6a9d43 Mon Sep 17 00:00:00 2001 From: "Robert C. Martin" Date: Sat, 5 Nov 2016 16:11:29 +0000 Subject: [PATCH 15/16] refactor --- .../minecraft/maprend/RegionRenderer.java | 880 ++++++++++-------- 1 file changed, 467 insertions(+), 413 deletions(-) diff --git a/src/togos/minecraft/maprend/RegionRenderer.java b/src/togos/minecraft/maprend/RegionRenderer.java index 443feef..08d1499 100644 --- a/src/togos/minecraft/maprend/RegionRenderer.java +++ b/src/togos/minecraft/maprend/RegionRenderer.java @@ -15,435 +15,489 @@ import java.util.List; import java.util.Set; -public class RegionRenderer -{ +public class RegionRenderer { private RegionRendererCommand rendererCommand; static class Timer { - public long regionLoading; - public long preRendering; - public long postProcessing; - public long imageSaving; - public long total; - - public int regionCount; - public int sectionCount; - - protected String formatTime( String name, long millis ) { - return String.format("%20s: % 8d % 8.2f % 8.4f", name, millis, millis/(double)regionCount, millis/(double)sectionCount); - } - } - - public static final short BASE_HEIGHT = 64; - - public final Set defaultedBlockIds = new HashSet(); - public final Set defaultedBlockIdDataValues = new HashSet(); - public final Set defaultedBiomeIds = new HashSet(); - public final BlockMap blockMap; - public final BiomeMap biomeMap; - public final int air16Color; // Color of 16 air blocks stacked - /** - * Alpha below which blocks are considered transparent for purposes of shading - * (i.e. blocks with alpha < this will not be shaded, but blocks below them will be) - */ - private int shadeOpacityCutoff = 0x20; - - public RegionRenderer( BlockMap blockMap, BiomeMap biomeMap, RegionRendererCommand cmd ) { + public long regionLoading; + public long preRendering; + public long postProcessing; + public long imageSaving; + public long total; + + public int regionCount; + public int sectionCount; + + protected String formatTime(String name, long millis) { + return String.format("%20s: % 8d % 8.2f % 8.4f", name, millis, millis / (double) regionCount, millis / (double) sectionCount); + } + } + + public static final short BASE_HEIGHT = 64; + + public final Set defaultedBlockIds = new HashSet(); + public final Set defaultedBlockIdDataValues = new HashSet(); + public final Set defaultedBiomeIds = new HashSet(); + public final BlockMap blockMap; + public final BiomeMap biomeMap; + public final int air16Color; // Color of 16 air blocks stacked + /** + * Alpha below which blocks are considered transparent for purposes of shading + * (i.e. blocks with alpha < this will not be shaded, but blocks below them will be) + */ + private int shadeOpacityCutoff = 0x20; + + public RegionRenderer(BlockMap blockMap, BiomeMap biomeMap, RegionRendererCommand cmd) { rendererCommand = cmd; - assert blockMap != null; - assert biomeMap != null; - - this.blockMap = blockMap; - this.biomeMap = biomeMap; - this.air16Color = Color.overlay( 0, getColor(0, 0, 0), 16 ); - } - - /** - * Extract a 4-bit integer from a byte in an array, where the first nybble - * in each byte (even nybble indexes) occupies the lower 4 bits and the second - * (odd nybble indexes) occupies the high bits. - * - * @param arr the source array - * @param index the index (in nybbles) of the desired 4 bits - * @return the desired 4 bits as the lower bits of a byte - */ - protected static final byte nybble( byte[] arr, int index ) { - return (byte)((index % 2 == 0 ? arr[index/2] : (arr[index/2]>>4))&0x0F); - } - - /** - * @param levelTag - * @param maxSectionCount - * @param sectionBlockIds block IDs for non-empty sections will be written to sectionBlockIds[sectionIndex][blockIndex] - * @param sectionBlockData block data for non-empty sections will be written to sectionBlockData[sectionIndex][blockIndex] - * @param sectionsUsed sectionsUsed[sectionIndex] will be set to true for non-empty sections - */ - protected static void loadChunkData( CompoundTag levelTag, int maxSectionCount, short[][] sectionBlockIds, byte[][] sectionBlockData, boolean[] sectionsUsed, byte[] biomeIds ) { - for( int i=0; i= 0 && blockId < blockMap.blocks.length; - assert blockDatum >= 0; - - int blockColor; - int biomeInfluence; - - Block bc = blockMap.blocks[blockId]; - if( bc.hasSubColors.length > blockDatum && bc.hasSubColors[blockDatum] ) { - blockColor = bc.subColors[blockDatum]; - biomeInfluence = bc.subColorInfluences[blockDatum]; - } else { - if( blockDatum != 0 ) { - defaultedSubBlockColor(blockId, blockDatum); - } - blockColor = bc.baseColor; - biomeInfluence = bc.baseInfluence; - } - if( bc.isDefault ) { - defaultedBlockColor(blockId); - } - - Biome biome = biomeMap.getBiome(biomeId); - int biomeColor = biome.getMultiplier( biomeInfluence ); - if( biome.isDefault ) defaultedBiomeColor(biomeId); - - return Color.multiplySolid( blockColor, biomeColor ); - } - - //// Handy color-manipulation functions //// - - protected static void demultiplyAlpha( int[] color ) { - for( int i=color.length-1; i>=0; --i ) color[i] = Color.demultiplyAlpha(color[i]); - } - - protected void shade( short[] height, int[] color ) { - int width=512, depth=512; - - int idx = 0; - for( int z=0; z 10 ) shade = 10; - if( shade < -10 ) shade = -10; - - shade += (height[idx] - BASE_HEIGHT) / 7.0; - - color[idx] = Color.shade( color[idx], (int)(shade*8) ); - } - } - } - - //// Rendering //// - - Timer timer = new Timer(); - protected long startTime; - protected void resetInterval() { startTime = System.currentTimeMillis(); } - protected long getInterval() { return System.currentTimeMillis() - startTime; } - - /** - * Load color and height data from a region. - * @param rf - * @param colors color data will be written here - * @param heights height data (height of top of topmost non-transparent block) will be written here - */ - protected void preRender( RegionFile rf, int[] colors, short[] heights ) { - int maxSectionCount = 16; - short[][] sectionBlockIds = new short[maxSectionCount][16*16*16]; - byte[][] sectionBlockData = new byte[maxSectionCount][16*16*16]; - boolean[] usedSections = new boolean[maxSectionCount]; - byte[] biomeIds = new byte[16*16]; - - for( int cz=0; cz<32; ++cz ) { - for( int cx=0; cx<32; ++cx ) { - resetInterval(); - DataInputStream cis = rf.getChunkDataInputStream(cx,cz); - if( cis == null ) continue; - NBTInputStream nis = null; - try { - nis = new NBTInputStream(cis); - CompoundTag rootTag = (CompoundTag)nis.readTag(); - CompoundTag levelTag = (CompoundTag)rootTag.getValue().get("Level"); - loadChunkData( levelTag, maxSectionCount, sectionBlockIds, sectionBlockData, usedSections, biomeIds ); - timer.regionLoading += getInterval(); - - for( int s=0; s= shadeOpacityCutoff ) { - pixelHeight = (short)absY; - } - } - } else { - pixelColor = Color.overlay( pixelColor, air16Color ); - } - } - - final int dIdx = 512*(cz*16+z)+16*cx+x; - colors[dIdx] = buriedAir ? 0xFFFFFFFF : pixelColor; - colors[dIdx] = torch ? 0xFFFFFF00 : colors[dIdx]; - colors[dIdx] = diamond ? 0xFF00FFFF : colors[dIdx]; - heights[dIdx] = pixelHeight; - } - } - timer.preRendering += getInterval(); - } catch( IOException e ) { - System.err.println("Error reading chunk from "+rf.getFile()+" at "+cx+","+cz); - e.printStackTrace(System.err); - } finally { - if( nis != null ) { - try { - nis.close(); - } catch( IOException e ) { - System.err.println("Failed to close NBTInputStream!"); - e.printStackTrace(System.err); - } - } - } - } - } - } - - public BufferedImage render( RegionFile rf ) { - resetInterval(); - int width=512, depth=512; - - int[] surfaceColor = new int[width*depth]; - short[] surfaceHeight = new short[width*depth]; - - preRender( rf, surfaceColor, surfaceHeight ); - demultiplyAlpha( surfaceColor ); - shade( surfaceHeight, surfaceColor ); - - BufferedImage bi = new BufferedImage( width, depth, BufferedImage.TYPE_INT_ARGB ); - - for( int z=0; z> 4)) & 0x0F); + } + + /** + * @param levelTag + * @param maxSectionCount + * @param sectionBlockIds block IDs for non-empty sections will be written to sectionBlockIds[sectionIndex][blockIndex] + * @param sectionBlockData block data for non-empty sections will be written to sectionBlockData[sectionIndex][blockIndex] + * @param sectionsUsed sectionsUsed[sectionIndex] will be set to true for non-empty sections + */ + protected static void loadChunkData(CompoundTag levelTag, int maxSectionCount, short[][] sectionBlockIds, byte[][] sectionBlockData, boolean[] sectionsUsed, byte[] biomeIds) { + for (int i = 0; i < maxSectionCount; ++i) { + sectionsUsed[i] = false; + } + + Tag biomesTag = levelTag.getValue().get("Biomes"); + if (biomesTag != null) { + System.arraycopy(((ByteArrayTag) biomesTag).getValue(), 0, biomeIds, 0, 16 * 16); + } else { + for (int i = 0; i < 16 * 16; i++) { + biomeIds[i] = -1; + } + } + + for (Tag t : ((ListTag) levelTag.getValue().get("Sections")).getValue()) { + CompoundTag sectionInfo = (CompoundTag) t; + int sectionIndex = ((ByteTag) sectionInfo.getValue().get("Y")).getValue().intValue(); + byte[] blockIdsLow = ((ByteArrayTag) sectionInfo.getValue().get("Blocks")).getValue(); + byte[] blockData = ((ByteArrayTag) sectionInfo.getValue().get("Data")).getValue(); + Tag addTag = sectionInfo.getValue().get("Add"); + byte[] blockAdd = null; + if (addTag != null) { + blockAdd = ((ByteArrayTag) addTag).getValue(); + } + short[] destSectionBlockIds = sectionBlockIds[sectionIndex]; + byte[] destSectionData = sectionBlockData[sectionIndex]; + sectionsUsed[sectionIndex] = true; + for (int y = 0; y < 16; ++y) { + for (int z = 0; z < 16; ++z) { + for (int x = 0; x < 16; ++x) { + int index = y * 256 + z * 16 + x; + short blockType = (short) (blockIdsLow[index] & 0xFF); + if (blockAdd != null) { + blockType |= nybble(blockAdd, index) << 8; + } + destSectionBlockIds[index] = blockType; + destSectionData[index] = nybble(blockData, index); + } + } + } + } + } + + //// Color look-up //// + + protected void defaultedBlockColor(int blockId) { + defaultedBlockIds.add(blockId); + } + + protected void defaultedSubBlockColor(int blockId, int blockDatum) { + defaultedBlockIdDataValues.add(blockId | blockDatum << 16); + } + + protected void defaultedBiomeColor(int biomeId) { + defaultedBiomeIds.add(biomeId); + } + + protected int getColor(int blockId, int blockDatum, int biomeId) { + assert blockId >= 0 && blockId < blockMap.blocks.length; + assert blockDatum >= 0; + + int blockColor; + int biomeInfluence; + + Block bc = blockMap.blocks[blockId]; + if (bc.hasSubColors.length > blockDatum && bc.hasSubColors[blockDatum]) { + blockColor = bc.subColors[blockDatum]; + biomeInfluence = bc.subColorInfluences[blockDatum]; + } else { + if (blockDatum != 0) { + defaultedSubBlockColor(blockId, blockDatum); + } + blockColor = bc.baseColor; + biomeInfluence = bc.baseInfluence; + } + if (bc.isDefault) { + defaultedBlockColor(blockId); + } + + Biome biome = biomeMap.getBiome(biomeId); + int biomeColor = biome.getMultiplier(biomeInfluence); + if (biome.isDefault) defaultedBiomeColor(biomeId); + + return Color.multiplySolid(blockColor, biomeColor); + } + + //// Handy color-manipulation functions //// + + protected static void demultiplyAlpha(int[] color) { + for (int i = color.length - 1; i >= 0; --i) color[i] = Color.demultiplyAlpha(color[i]); + } + + protected void shade(short[] height, int[] color) { + int width = 512, depth = 512; + + int idx = 0; + for (int z = 0; z < depth; ++z) { + for (int x = 0; x < width; ++x, ++idx) { + float dyx, dyz; + + if (color[idx] == 0) continue; + + if (x == 0) dyx = height[idx + 1] - height[idx]; + else if (x == width - 1) dyx = height[idx] - height[idx - 1]; + else dyx = (height[idx + 1] - height[idx - 1]) * 2; + + if (z == 0) dyz = height[idx + width] - height[idx]; + else if (z == depth - 1) dyz = height[idx] - height[idx - width]; + else dyz = (height[idx + width] - height[idx - width]) * 2; + + float shade = dyx + dyz; + if (shade > 10) shade = 10; + if (shade < -10) shade = -10; + + shade += (height[idx] - BASE_HEIGHT) / 7.0; + + color[idx] = Color.shade(color[idx], (int) (shade * 8)); + } + } + } + + //// Rendering //// + + Timer timer = new Timer(); + protected long startTime; + + protected void resetInterval() { + startTime = System.currentTimeMillis(); + } + + protected long getInterval() { + return System.currentTimeMillis() - startTime; + } + + /** + * Load color and height data from a region. + * + * @param rf + * @param colors color data will be written here + * @param heights height data (height of top of topmost non-transparent block) will be written here + */ + protected void preRender(RegionFile rf, int[] colors, short[] heights) { + new PreRenderer(rf, colors, heights).preRender(); + } + + public BufferedImage render(RegionFile rf) { + resetInterval(); + int width = 512, depth = 512; + + int[] surfaceColor = new int[width * depth]; + short[] surfaceHeight = new short[width * depth]; + + preRender(rf, surfaceColor, surfaceHeight); + demultiplyAlpha(surfaceColor); + shade(surfaceHeight, surfaceColor); + + BufferedImage bi = new BufferedImage(width, depth, BufferedImage.TYPE_INT_ARGB); + + for (int z = 0; z < depth; ++z) { + bi.setRGB(0, z, width, 1, surfaceColor, width * z, width); + } + timer.postProcessing += getInterval(); + + return bi; + } + + protected static String pad(String v, int targetLength) { + while (v.length() < targetLength) v = " " + v; + return v; + } + + protected static String pad(int v, int targetLength) { + return pad("" + v, targetLength); + } + + public void renderAll(RegionMap rm) throws IOException { + File outputDir = rendererCommand.outputDir; + long startTime = System.currentTimeMillis(); + + if (!outputDir.exists()) outputDir.mkdirs(); + + if (rm.regions.size() == 0) { + System.err.println("Warning: no regions found!"); + } + + for (Region r : rm.regions) { + if (r == null) continue; debugMessage("Region " + pad(r.rx, 4) + ", " + pad(r.rz, 4) + "..."); - String imageFilename = "tile."+r.rx+"."+r.rz+".png"; - File imageFile = r.imageFile = new File( outputDir+"/"+imageFilename ); - - if( imageFile.exists() ) { - if( !rendererCommand.forceReRender && imageFile.lastModified() > r.regionFile.lastModified() ) { - debugMessage("image already up-to-date\n"); - continue; - } - imageFile.delete(); - } - debugMessage("generating " + imageFilename + "...\n"); - - RegionFile rf = new RegionFile( r.regionFile ); - BufferedImage bi; - try { - bi = render( rf ); - } finally { - rf.close(); - } - + String imageFilename = "tile." + r.rx + "." + r.rz + ".png"; + File imageFile = r.imageFile = new File(outputDir + "/" + imageFilename); + + if (imageFile.exists()) { + if (!rendererCommand.forceReRender && imageFile.lastModified() > r.regionFile.lastModified()) { + debugMessage("image already up-to-date\n"); + continue; + } + imageFile.delete(); + } + debugMessage("generating " + imageFilename + "...\n"); + + RegionFile rf = new RegionFile(r.regionFile); + BufferedImage bi; + try { + bi = render(rf); + } finally { + rf.close(); + } + if (rendererCommand.overlayGrid) new RegionGrid(r.rx, r.rz, 100).overlayGridOnImage(bi); - - try { - resetInterval(); - ImageIO.write(bi, "png", imageFile); - timer.imageSaving += getInterval(); - } catch( IOException e ) { - System.err.println("Error writing PNG to "+imageFile); - e.printStackTrace(); - } - ++timer.regionCount; - } - timer.total += System.currentTimeMillis() - startTime; - } + + try { + resetInterval(); + ImageIO.write(bi, "png", imageFile); + timer.imageSaving += getInterval(); + } catch (IOException e) { + System.err.println("Error writing PNG to " + imageFile); + e.printStackTrace(); + } + ++timer.regionCount; + } + timer.total += System.currentTimeMillis() - startTime; + } private void debugMessage(String debugMessage) { - if( rendererCommand.debug ) { + if (rendererCommand.debug) { System.err.print(debugMessage); } } /** - * Create a "tiles.html" file containing a table with - * all region images (tile...png) that exist in outDir - * within the given bounds (inclusive) - */ - public void createTileHtml( int minX, int minZ, int maxX, int maxZ, File outputDir ) { - debugMessage("Writing HTML tiles...\n"); - try { - Writer w = new OutputStreamWriter(new FileOutputStream(new File(outputDir+"/tiles.html"))); - w.write("
\n"); - - for( int z=minZ; z<=maxZ; ++z ) { - w.write(""); - for( int x=minX; x<=maxX; ++x ) { - w.write(""); - } - w.write("\n"); - } - - w.write("
"); - String imageFilename = "tile."+x+"."+z+".png"; - File imageFile = new File( outputDir+"/"+imageFilename ); - if( imageFile.exists() ) { - w.write(""); - } - w.write("
\n"); - w.write("Page rendered at "+ new Date().toString()); - w.write("\n"); - w.close(); - } catch( IOException e ) { - throw new RuntimeException(e); - } - } - - public void createImageTree( RegionMap rm ) { - debugMessage("Composing image tree...\n"); - ImageTreeComposer itc = new ImageTreeComposer(new ContentStore()); - System.out.println( itc.compose( rm ) ); - } - - public void createBigImage( RegionMap rm, File outputDir) { - debugMessage("Creating big image...\n"); - BigImageMerger bic = new BigImageMerger(); - bic.createBigImage( rm, outputDir, rendererCommand.debug ); - } - - protected static final boolean booleanValue( Boolean b, boolean defalt ) { - return b == null ? defalt : b.booleanValue(); - } - - protected static boolean singleDirectoryGiven( List files ) { - return files.size() == 1 && files.get(0).isDirectory(); - } - - public static void main( String[] args ) throws Exception { - System.exit( RegionRendererCommand.fromArguments( args ).run() ); - } + * Create a "tiles.html" file containing a table with + * all region images (tile...png) that exist in outDir + * within the given bounds (inclusive) + */ + public void createTileHtml(int minX, int minZ, int maxX, int maxZ, File outputDir) { + debugMessage("Writing HTML tiles...\n"); + try { + Writer w = new OutputStreamWriter(new FileOutputStream(new File(outputDir + "/tiles.html"))); + w.write("\n"); + + for (int z = minZ; z <= maxZ; ++z) { + w.write(""); + for (int x = minX; x <= maxX; ++x) { + w.write(""); + } + w.write("\n"); + } + + w.write("
"); + String imageFilename = "tile." + x + "." + z + ".png"; + File imageFile = new File(outputDir + "/" + imageFilename); + if (imageFile.exists()) { + w.write(""); + } + w.write("
\n"); + w.write("Page rendered at " + new Date().toString()); + w.write("\n"); + w.close(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + public void createImageTree(RegionMap rm) { + debugMessage("Composing image tree...\n"); + ImageTreeComposer itc = new ImageTreeComposer(new ContentStore()); + System.out.println(itc.compose(rm)); + } + + public void createBigImage(RegionMap rm, File outputDir) { + debugMessage("Creating big image...\n"); + BigImageMerger bic = new BigImageMerger(); + bic.createBigImage(rm, outputDir, rendererCommand.debug); + } + + protected static final boolean booleanValue(Boolean b, boolean defalt) { + return b == null ? defalt : b.booleanValue(); + } + + protected static boolean singleDirectoryGiven(List files) { + return files.size() == 1 && files.get(0).isDirectory(); + } + + public static void main(String[] args) throws Exception { + System.exit(RegionRendererCommand.fromArguments(args).run()); + } + + private class PreRenderer { + private RegionFile rf; + private int[] colors; + private short[] heights; + private final int maxSectionCount = 16; + private final short[][] sectionBlockIds = new short[maxSectionCount][16 * 16 * 16]; + private final byte[][] sectionBlockData = new byte[maxSectionCount][16 * 16 * 16]; + private final boolean[] usedSections = new boolean[maxSectionCount]; + private final byte[] biomeIds = new byte[16 * 16]; + + public PreRenderer(RegionFile rf, int[] colors, short... heights) { + this.rf = rf; + this.colors = colors; + this.heights = heights; + } + + public void preRender() { + for (int cz = 0; cz < 32; ++cz) { + for (int cx = 0; cx < 32; ++cx) { + preRenderChunk(cz, cx); + } + } + } + + private void preRenderChunk(int cz, int cx) { + resetInterval(); + DataInputStream cis = rf.getChunkDataInputStream(cx, cz); + if (cis == null) return; + NBTInputStream nis = null; + try { + nis = new NBTInputStream(cis); + CompoundTag rootTag = (CompoundTag) nis.readTag(); + CompoundTag levelTag = (CompoundTag) rootTag.getValue().get("Level"); + loadChunkData(levelTag, maxSectionCount, sectionBlockIds, sectionBlockData, usedSections, biomeIds); + timer.regionLoading += getInterval(); + + for (int s = 0; s < maxSectionCount; ++s) { + if (usedSections[s]) { + ++timer.sectionCount; + } + } + + resetInterval(); + for (int z = 0; z < 16; ++z) { + for (int x = 0; x < 16; ++x) { + new ColumnPreRenderer(cz, cx, z, x).preRender(); + } + } + timer.preRendering += getInterval(); + } catch (IOException e) { + System.err.println("Error reading chunk from " + rf.getFile() + " at " + cx + "," + cz); + e.printStackTrace(System.err); + } finally { + if (nis != null) { + try { + nis.close(); + } catch (IOException e) { + System.err.println("Failed to close NBTInputStream!"); + e.printStackTrace(System.err); + } + } + } + } + + private class ColumnPreRenderer { + private int cz; + private int cx; + private int z; + private int x; + private int pixelColor = 0; + private short pixelHeight = 0; + private boolean diamond = false; + private boolean air = false; + private boolean buriedAir = false; + private boolean torch = false; + private int biomeId; + + public ColumnPreRenderer(int cz, int cx, int z, int x) { + this.cz = cz; + this.cx = cx; + this.z = z; + this.x = x; + } + + public void preRender() { + biomeId = biomeIds[z * 16 + x] & 0xFF; + + for (int s = 0; s < maxSectionCount; ++s) { + renderSection(s); + } + + final int dIdx = 512 * (cz * 16 + z) + 16 * cx + x; + colors[dIdx] = buriedAir ? 0xFFFFFFFF : pixelColor; + colors[dIdx] = torch ? 0xFFFFFF00 : colors[dIdx]; + colors[dIdx] = diamond ? 0xFF00FFFF : colors[dIdx]; + heights[dIdx] = pixelHeight; + } + + private void renderSection(int s) { + if (usedSections[s]) { + short[] blockIds = sectionBlockIds[s]; + byte[] blockData = sectionBlockData[s]; + + for (int idx = z * 16 + x, y = 0, absY = s * 16; y < 16; ++y, idx += 256, ++absY) { + int blockId = blockIds[idx] & 0xFFFF; + byte blockDatum = blockData[idx]; + int blockColor = getBlockColor(blockId, blockDatum); + if (Color.alpha(blockColor) >= shadeOpacityCutoff) { + pixelHeight = (short) absY; + } + } + } else { + pixelColor = Color.overlay(pixelColor, air16Color); + } + } + + private int getBlockColor(int blockId, byte blockDatum) { + if (rendererCommand.showDiamonds && blockId == 56) // diamond + diamond = true; + if (air && (blockId == 1)) // stone over air + buriedAir = true; + if (rendererCommand.showAir && blockId == 0) // air + air = true; + if (rendererCommand.showTorches && blockId == 50) { // torch + torch = true; + } + int blockColor = getColor(blockId, blockDatum, biomeId); + pixelColor = Color.overlay(pixelColor, blockColor); + return blockColor; + } + } + } } From 6fd38184ca8ede17a4d1e08ee4f1826ab7c4d7bd Mon Sep 17 00:00:00 2001 From: "Robert C. Martin" Date: Sat, 5 Nov 2016 14:27:52 -0500 Subject: [PATCH 16/16] make torches red --- src/togos/minecraft/maprend/RegionRenderer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/togos/minecraft/maprend/RegionRenderer.java b/src/togos/minecraft/maprend/RegionRenderer.java index 08d1499..d8fb8f5 100644 --- a/src/togos/minecraft/maprend/RegionRenderer.java +++ b/src/togos/minecraft/maprend/RegionRenderer.java @@ -461,7 +461,7 @@ public void preRender() { final int dIdx = 512 * (cz * 16 + z) + 16 * cx + x; colors[dIdx] = buriedAir ? 0xFFFFFFFF : pixelColor; - colors[dIdx] = torch ? 0xFFFFFF00 : colors[dIdx]; + colors[dIdx] = torch ? 0xFFFF0000 : colors[dIdx]; colors[dIdx] = diamond ? 0xFF00FFFF : colors[dIdx]; heights[dIdx] = pixelHeight; }