From cd6d482c58f8cf5eb9585c36cdbe21e92dfb6ba4 Mon Sep 17 00:00:00 2001 From: Ryan Rector Date: Sun, 10 Jul 2011 06:44:36 -0600 Subject: [PATCH 01/44] Add setting to change north direction --- sample.settings.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/sample.settings.py b/sample.settings.py index 3b6aed2..e31cd1f 100644 --- a/sample.settings.py +++ b/sample.settings.py @@ -152,6 +152,13 @@ verbose = 1 if "web_assets_hook" in locals(): skipjs = True +################################################################################ +### north_direction +## Make north point somewhere else! +## default: upper-right +## Type: string +## Example: +north_direction = "upper-right" From 954fb18a4c9263dd52d54210721f85bf5ee00e0b Mon Sep 17 00:00:00 2001 From: Ryan Rector Date: Sun, 10 Jul 2011 06:47:58 -0600 Subject: [PATCH 02/44] Parse commandline for changing north direction --- overviewer.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/overviewer.py b/overviewer.py index 1bf6790..6dfbd68 100755 --- a/overviewer.py +++ b/overviewer.py @@ -109,6 +109,7 @@ def main(): parser.add_option("--skip-js", dest="skipjs", action="store_true", help="Don't output marker.js or regions.js") parser.add_option("--no-signs", dest="nosigns", action="store_true", help="Don't output signs to markers.js") parser.add_option("--display-config", dest="display_config", action="store_true", help="Display the configuration parameters, but don't render the map. Requires all required options to be specified", commandLineOnly=True) + parser.add_option("--north-direction", dest="north_direction", help="Specifies which corner of the screen North will point to.", type="string", default="upper-right") #parser.add_option("--write-config", dest="write_config", action="store_true", help="Writes out a sample config file", commandLineOnly=True) options, args = parser.parse_args() @@ -209,6 +210,11 @@ def main(): optimizeimages.check_programs(optimizeimg) else: optimizeimg = None + + if options.north_direction: + north_direction = options.north_direction + else: + north_direction = 'upper-right' logging.getLogger().setLevel( logging.getLogger().level + 10*options.quiet) @@ -223,7 +229,8 @@ def main(): logging.info("Notice: Not using biome data for tinting") # First do world-level preprocessing - w = world.World(worlddir, useBiomeData=useBiomeData, regionlist=regionlist) + w = world.World(worlddir, useBiomeData=useBiomeData, regionlist=regionlist, + north_direction=north_direction) w.go(options.procs) logging.info("Rending the following tilesets: %s", ",".join(options.rendermode)) @@ -233,7 +240,7 @@ def main(): # create the quadtrees # TODO chunklist q = [] - qtree_args = {'depth' : options.zoom, 'imgformat' : imgformat, 'imgquality' : options.imgquality, 'optimizeimg' : optimizeimg, 'bgcolor' : bgcolor, 'forcerender' : options.forcerender} + qtree_args = {'depth' : options.zoom, 'imgformat' : imgformat, 'imgquality' : options.imgquality, 'optimizeimg' : optimizeimg, 'bgcolor' : bgcolor, 'forcerender' : options.forcerender, 'north_direction' : north_direction} for rendermode in options.rendermode: if rendermode == 'normal': qtree = quadtree.QuadtreeGen(w, destdir, rendermode=rendermode, tiledir='tiles', **qtree_args) From 7380ed08de0f4fb77474e74578abdc210bb849d1 Mon Sep 17 00:00:00 2001 From: Ryan Rector Date: Sun, 10 Jul 2011 06:49:37 -0600 Subject: [PATCH 03/44] Make chunk to tile coords aware of new north --- world.py | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/world.py b/world.py index 1344d10..abc995d 100644 --- a/world.py +++ b/world.py @@ -69,9 +69,11 @@ class World(object): mincol = maxcol = minrow = maxrow = 0 - def __init__(self, worlddir, useBiomeData=False,regionlist=None): + def __init__(self, worlddir, useBiomeData=False,regionlist=None, + north_direction="upper-right"): self.worlddir = worlddir self.useBiomeData = useBiomeData + self.north_direction = north_direction #find region files, or load the region list #this also caches all the region file header info @@ -182,17 +184,27 @@ class World(object): in the chunk coordinate system, and figures out the row and column in the image each one should be. Returns (col, row).""" - # columns are determined by the sum of the chunk coords, rows are the - # difference (TODO: be able to change direction of north) # change this function, and you MUST change unconvert_coords - return (chunkx + chunky, chunky - chunkx) + if self.north_direction == 'lower-left': + return (chunkx + chunky, chunky - chunkx) + elif self.north_direction == 'lower-right': + return (chunkx + chunky, chunkx - chunky) + elif self.north_direction == 'upper-left': + return (chunkx - chunky, chunkx + chunky) + elif self.north_direction == 'upper-right': + return (chunky - chunkx, chunkx + chunky) def unconvert_coords(self, col, row): """Undoes what convert_coords does. Returns (chunkx, chunky).""" - # col + row = chunky + chunky => (col + row)/2 = chunky - # col - row = chunkx + chunkx => (col - row)/2 = chunkx - return ((col - row) / 2, (col + row) / 2) + if self.north_direction == 'lower-left': + return ((col - row) / 2, (row + col) / 2) + if self.north_direction == 'lower-right': + return ((col + row) / 2, (col - row) / 2) + if self.north_direction == 'upper-left': + return ((col + row) / 2, (row - col) / 2) + if self.north_direction == 'upper-right': + return ((row - col) / 2, (col + row) / 2) def findTrueSpawn(self): """Adds the true spawn location to self.POI. The spawn Y coordinate From f2882512e7dd5a43dc942237af10d28c9f5960fd Mon Sep 17 00:00:00 2001 From: Ryan Rector Date: Sun, 10 Jul 2011 07:01:17 -0600 Subject: [PATCH 04/44] Make quadtree flip axis on some north directions This should not be handled here, but maybe in nbt? This was quick to find and modify, that's all. --- quadtree.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/quadtree.py b/quadtree.py index fc00b01..9ccb04f 100644 --- a/quadtree.py +++ b/quadtree.py @@ -49,7 +49,7 @@ def iterate_base4(d): return itertools.product(xrange(4), repeat=d) class QuadtreeGen(object): - def __init__(self, worldobj, destdir, bgcolor, depth=None, tiledir=None, forcerender=False, imgformat=None, imgquality=95, optimizeimg=None, rendermode="normal"): + def __init__(self, worldobj, destdir, bgcolor, depth=None, tiledir=None, forcerender=False, imgformat=None, imgquality=95, optimizeimg=None, rendermode="normal", north_direction='upper-right'): """Generates a quadtree from the world given into the given dest directory @@ -66,6 +66,7 @@ class QuadtreeGen(object): self.optimizeimg = optimizeimg self.bgcolor = bgcolor self.rendermode = rendermode + self.north_direction = north_direction # force png renderformat if we're using an overlay mode if 'overlay' in get_render_mode_inheritance(rendermode): @@ -239,6 +240,10 @@ class QuadtreeGen(object): continue chunkx, chunky = unconvert_coords(col, row) + if self.north_direction == 'upper-right': + chunky = -chunky + elif self.north_direction == 'lower-right': + chunkx = -chunkx regionx_ = chunkx//32 regiony_ = chunky//32 @@ -468,7 +473,7 @@ class QuadtreeGen(object): # draw the chunk! try: - a = chunk.ChunkRenderer((chunkx, chunky), world, rendermode, poi_queue) + a = chunk.ChunkRenderer((chunkx, chunky), world, rendermode, poi_queue, self.north_direction) a.chunk_render(tileimg, xpos, ypos, None) except chunk.ChunkCorrupt: # an error was already printed From f0ea678cb623f78642d7ca34b601386bb0733ca2 Mon Sep 17 00:00:00 2001 From: Ryan Rector Date: Sun, 10 Jul 2011 07:03:46 -0600 Subject: [PATCH 05/44] Google maps js needs to know north-direction --- googlemap.py | 3 +++ web_assets/overviewerConfig.js | 6 +++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/googlemap.py b/googlemap.py index 48fede1..07e082d 100644 --- a/googlemap.py +++ b/googlemap.py @@ -71,6 +71,7 @@ class MapGen(object): self.web_assets_hook = configInfo.get('web_assets_hook', None) self.web_assets_path = configInfo.get('web_assets_path', None) self.bg_color = configInfo.get('bg_color') + self.north_direction = configInfo.get('north_direction', 'upper-right') if not len(quadtrees) > 0: raise ValueError("there must be at least one quadtree to work on") @@ -109,6 +110,8 @@ class MapGen(object): "{minzoom}", str(0)) config = config.replace( "{maxzoom}", str(zoomlevel)) + config = config.replace( + "{north_direction}", self.north_direction) config = config.replace("{spawn_coords}", json.dumps(list(self.world.spawn))) diff --git a/web_assets/overviewerConfig.js b/web_assets/overviewerConfig.js index ce38479..22097d0 100644 --- a/web_assets/overviewerConfig.js +++ b/web_assets/overviewerConfig.js @@ -79,7 +79,11 @@ var overviewerConfig = { * Set to true to turn on debug mode, which adds a grid to the map along * with co-ordinates and a bunch of console output. */ - 'debug': false + 'debug': false, + /** + * Set which way north points. + */ + 'north_direction': '{north_direction}' }, /** * Group definitions for objects that are partially selectable (signs and From f306a2ecffbac51503a11efd4cd3292421994f17 Mon Sep 17 00:00:00 2001 From: Ryan Rector Date: Sun, 10 Jul 2011 07:06:20 -0600 Subject: [PATCH 06/44] Google maps JS handle north_direction Modifies coords conversion to handle configurable north direction --- web_assets/overviewer.js | 48 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 46 insertions(+), 2 deletions(-) diff --git a/web_assets/overviewer.js b/web_assets/overviewer.js index ea87a19..10ec81a 100644 --- a/web_assets/overviewer.js +++ b/web_assets/overviewer.js @@ -492,6 +492,22 @@ var overviewer = { var perPixel = 1.0 / (overviewerConfig.CONST.tileSize * Math.pow(2, overviewerConfig.map.maxZoom)); + if(overviewerConfig.map.north_direction == 'lower-left'){ + x = x; + y = y; + } else if(overviewerConfig.map.north_direction == 'upper-right'){ + x = -x-1; + y = -y-1; + } else if(overviewerConfig.map.north_direction == 'upper-left'){ + temp = x; + x = -y-1; + y = temp-16; + } else if(overviewerConfig.map.north_direction == 'lower-right'){ + temp = x; + x = y; + y = -temp-1+16; + } + // This information about where the center column is may change with // a different drawing implementation -- check it again after any // drawing overhauls! @@ -500,8 +516,14 @@ var overviewer = { // so the Y coordinate is at 0.5, and the X is at 0.5 - // ((tileSize / 2) / (tileSize * 2^maxZoom)) // or equivalently, 0.5 - (1 / 2^(maxZoom + 1)) - var lng = 0.5 - (1.0 / Math.pow(2, overviewerConfig.map.maxZoom + 1)); - var lat = 0.5; + lng = 0.5 - (1.0 / Math.pow(2, overviewerConfig.map.maxZoom + 1)); + lat = 0.5; + if(overviewerConfig.map.north_direction == 'upper-right'){ + lng = 0.5 + (1.0 / Math.pow(2, overviewerConfig.map.maxZoom + 1)); + } else if(overviewerConfig.map.north_direction == 'upper-left'){ + lng = 0.5 + (1.0 / Math.pow(2, overviewerConfig.map.maxZoom + 1)); + } + // the following metrics mimic those in ChunkRenderer.chunk_render // in "chunk.py" or, equivalently, chunk_render in src/iterate.c @@ -549,6 +571,12 @@ var overviewer = { lng -= 0.5 - (1.0 / Math.pow(2, overviewerConfig.map.maxZoom + 1)); lat -= 0.5; + if(overviewerConfig.map.north_direction == 'upper-right'){ + lng -= 0.5 + (1.0 / Math.pow(2, overviewerConfig.map.maxZoom + 1)); + } else if(overviewerConfig.map.north_direction == 'upper-left'){ + lng -= 0.5 + (1.0 / Math.pow(2, overviewerConfig.map.maxZoom + 1)); + } + // I'll admit, I plugged this into Wolfram Alpha: // a = (x * 12 * r) + (z * 12 * r), b = (z * 6 * r) - (x * 6 * r) // And I don't know the math behind solving for for X and Z given @@ -563,6 +591,22 @@ var overviewer = { point.x += 64; point.z -= 64; + if(overviewerConfig.map.north_direction == 'lower-left'){ + point.x = point.x; + point.z = point.z; + } else if(overviewerConfig.map.north_direction == 'upper-right'){ + point.x = -point.x; + point.z = -point.z; + } else if(overviewerConfig.map.north_direction == 'upper-left'){ + temp = point.z; + point.z = -point.x; + point.x = temp+16; + } else if(overviewerConfig.map.north_direction == 'lower-right'){ + temp = point.z; + point.z = point.x; + point.x = -temp+16; + } + return point; }, /** From 722b814873c6f594244e558ff71da688f8a6fdd1 Mon Sep 17 00:00:00 2001 From: Ryan Rector Date: Sun, 10 Jul 2011 07:18:35 -0600 Subject: [PATCH 07/44] Rotate each chunk based on north direction --- chunk.py | 66 +++++++++++++++++++++++++++++++++----------------------- 1 file changed, 39 insertions(+), 27 deletions(-) diff --git a/chunk.py b/chunk.py index f8aa1f6..4ba13c8 100644 --- a/chunk.py +++ b/chunk.py @@ -56,6 +56,7 @@ def get_lvldata(world, filename, x, y, retries=2): try: d = world.load_from_region(filename, x, y) + #TODO should probably do rotation from here except Exception, e: if retries > 0: # wait a little bit, and try again (up to `retries` times) @@ -70,21 +71,21 @@ def get_lvldata(world, filename, x, y, retries=2): if not d: raise NoSuchChunk(x,y) return d -def get_blockarray(level): +def get_blockarray(level, north_direction): """Takes the level struct as returned from get_lvldata, and returns the Block array, which just contains all the block ids""" - return numpy.frombuffer(level['Blocks'], dtype=numpy.uint8).reshape((16,16,128)) + return numpy.rot90(numpy.frombuffer(level['Blocks'], dtype=numpy.uint8).reshape((16,16,128)), get_north_rotations(north_direction)) -def get_blockarray_fromfile(filename): +def get_blockarray_fromfile(filename, north_direction): """Same as get_blockarray except takes a filename. This is a shortcut""" d = nbt.load_from_region(filename, x, y) level = d[1]['Level'] - return get_blockarray(level) + return get_blockarray(level, north_direction) -def get_skylight_array(level): +def get_skylight_array(level, north_direction): """Returns the skylight array. This is 4 bits per block, but it is expanded for you so you may index it normally.""" - skylight = numpy.frombuffer(level['SkyLight'], dtype=numpy.uint8).reshape((16,16,64)) + skylight = numpy.rot90(numpy.frombuffer(level['SkyLight'], dtype=numpy.uint8).reshape((16,16,64)), get_north_rotations(north_direction)) # this array is 2 blocks per byte, so expand it skylight_expanded = numpy.empty((16,16,128), dtype=numpy.uint8) # Even elements get the lower 4 bits @@ -93,26 +94,36 @@ def get_skylight_array(level): skylight_expanded[:,:,1::2] = (skylight & 0xF0) >> 4 return skylight_expanded -def get_blocklight_array(level): +def get_blocklight_array(level, north_direction): """Returns the blocklight array. This is 4 bits per block, but it is expanded for you so you may index it normally.""" # expand just like get_skylight_array() - blocklight = numpy.frombuffer(level['BlockLight'], dtype=numpy.uint8).reshape((16,16,64)) + blocklight = numpy.rot90(numpy.frombuffer(level['BlockLight'], dtype=numpy.uint8).reshape((16,16,64)), get_north_rotations(north_direction)) blocklight_expanded = numpy.empty((16,16,128), dtype=numpy.uint8) blocklight_expanded[:,:,::2] = blocklight & 0x0F blocklight_expanded[:,:,1::2] = (blocklight & 0xF0) >> 4 return blocklight_expanded -def get_blockdata_array(level): +def get_blockdata_array(level, north_direction): """Returns the ancillary data from the 'Data' byte array. Data is packed in a similar manner to skylight data""" - return numpy.frombuffer(level['Data'], dtype=numpy.uint8).reshape((16,16,64)) + return numpy.rot90(numpy.frombuffer(level['Data'], dtype=numpy.uint8).reshape((16,16,64)), get_north_rotations(north_direction)) def get_tileentity_data(level): """Returns the TileEntities TAG_List from chunk dat file""" data = level['TileEntities'] return data +def get_north_rotations(north_direction): + if north_direction == "upper-left": + return 1 + elif north_direction == "upper-right": + return 2 + elif north_direction == "lower-right": + return 3 + elif north_direction == "lower-left": + return 0 + # This set holds blocks ids that can be seen through, for occlusion calculations transparent_blocks = set([ 0, 6, 8, 9, 18, 20, 26, 27, 28, 30, 31, 32, 37, 38, 39, 40, 44, 50, 51, 52, 53, 55, 59, 63, 64, 65, 66, 67, @@ -138,13 +149,14 @@ class NoSuchChunk(Exception): pass class ChunkRenderer(object): - def __init__(self, chunkcoords, worldobj, rendermode, queue): + def __init__(self, chunkcoords, worldobj, rendermode, queue, north_direction): """Make a new chunk renderer for the given chunk coordinates. chunkcoors should be a tuple: (chunkX, chunkY) cachedir is a directory to save the resulting chunk images to """ self.queue = queue + self.north_direction = north_direction self.regionfile = worldobj.get_region_path(*chunkcoords) #if not os.path.exists(self.regionfile): @@ -181,21 +193,21 @@ class ChunkRenderer(object): def _load_blocks(self): """Loads and returns the block array""" if not hasattr(self, "_blocks"): - self._blocks = get_blockarray(self._load_level()) + self._blocks = get_blockarray(self._load_level(), self.north_direction) return self._blocks blocks = property(_load_blocks) def _load_skylight(self): """Loads and returns skylight array""" if not hasattr(self, "_skylight"): - self._skylight = get_skylight_array(self.level) + self._skylight = get_skylight_array(self.level, self.north_direction) return self._skylight skylight = property(_load_skylight) def _load_blocklight(self): """Loads and returns blocklight array""" if not hasattr(self, "_blocklight"): - self._blocklight = get_blocklight_array(self.level) + self._blocklight = get_blocklight_array(self.level, self.north_direction) return self._blocklight blocklight = property(_load_blocklight) @@ -204,9 +216,9 @@ class ChunkRenderer(object): chunk_path = self.world.get_region_path(self.chunkX - 1, self.chunkY) try: chunk_data = get_lvldata(self.world,chunk_path, self.chunkX - 1, self.chunkY) - self._left_skylight = get_skylight_array(chunk_data) - self._left_blocklight = get_blocklight_array(chunk_data) - self._left_blocks = get_blockarray(chunk_data) + self._left_skylight = get_skylight_array(chunk_data, self.north_direction) + self._left_blocklight = get_blocklight_array(chunk_data, self.north_direction) + self._left_blocks = get_blockarray(chunk_data, self.north_direction) except NoSuchChunk: self._left_skylight = None self._left_blocklight = None @@ -238,9 +250,9 @@ class ChunkRenderer(object): chunk_path = self.world.get_region_path(self.chunkX, self.chunkY + 1) try: chunk_data = get_lvldata(self.world,chunk_path, self.chunkX, self.chunkY + 1) - self._right_skylight = get_skylight_array(chunk_data) - self._right_blocklight = get_blocklight_array(chunk_data) - self._right_blocks = get_blockarray(chunk_data) + self._right_skylight = get_skylight_array(chunk_data, self.north_direction) + self._right_blocklight = get_blocklight_array(chunk_data, self.north_direction) + self._right_blocks = get_blockarray(chunk_data, self.north_direction) except NoSuchChunk: self._right_skylight = None self._right_blocklight = None @@ -272,9 +284,9 @@ class ChunkRenderer(object): chunk_path = self.world.get_region_path(self.chunkX + 1, self.chunkY) try: chunk_data = get_lvldata(self.world,chunk_path, self.chunkX + 1, self.chunkY) - self._up_right_skylight = get_skylight_array(chunk_data) - self._up_right_blocklight = get_blocklight_array(chunk_data) - self._up_right_blocks = get_blockarray(chunk_data) + self._up_right_skylight = get_skylight_array(chunk_data, self.north_direction) + self._up_right_blocklight = get_blocklight_array(chunk_data, self.north_direction) + self._up_right_blocks = get_blockarray(chunk_data, self.north_direction) except NoSuchChunk: self._up_right_skylight = None self._up_right_blocklight = None @@ -299,9 +311,9 @@ class ChunkRenderer(object): chunk_path = self.world.get_region_path(self.chunkX, self.chunkY - 1) try: chunk_data = get_lvldata(self.world,chunk_path, self.chunkX, self.chunkY - 1) - self._up_left_skylight = get_skylight_array(chunk_data) - self._up_left_blocklight = get_blocklight_array(chunk_data) - self._up_left_blocks = get_blockarray(chunk_data) + self._up_left_skylight = get_skylight_array(chunk_data, self.north_direction) + self._up_left_blocklight = get_blocklight_array(chunk_data, self.north_direction) + self._up_left_blocks = get_blockarray(chunk_data, self.north_direction) except NoSuchChunk: self._up_left_skylight = None self._up_left_blocklight = None @@ -391,7 +403,7 @@ class ChunkRenderer(object): rendered, and blocks are drawn with a color tint depending on their depth.""" - blockData = get_blockdata_array(self.level) + blockData = get_blockdata_array(self.level, self.north_direction) blockData_expanded = numpy.empty((16,16,128), dtype=numpy.uint8) # Even elements get the lower 4 bits blockData_expanded[:,:,::2] = blockData & 0x0F From e81db44ea991c240de27a1f6fead74d63a891ca6 Mon Sep 17 00:00:00 2001 From: Ryan Rector Date: Sun, 10 Jul 2011 07:42:50 -0600 Subject: [PATCH 08/44] Fix bad coords for upper-left and -right north --- web_assets/overviewer.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/web_assets/overviewer.js b/web_assets/overviewer.js index 10ec81a..08f85b2 100644 --- a/web_assets/overviewer.js +++ b/web_assets/overviewer.js @@ -516,11 +516,11 @@ var overviewer = { // so the Y coordinate is at 0.5, and the X is at 0.5 - // ((tileSize / 2) / (tileSize * 2^maxZoom)) // or equivalently, 0.5 - (1 / 2^(maxZoom + 1)) - lng = 0.5 - (1.0 / Math.pow(2, overviewerConfig.map.maxZoom + 1)); lat = 0.5; - if(overviewerConfig.map.north_direction == 'upper-right'){ - lng = 0.5 + (1.0 / Math.pow(2, overviewerConfig.map.maxZoom + 1)); - } else if(overviewerConfig.map.north_direction == 'upper-left'){ + if(overviewerConfig.map.north_direction == 'lower-left' || + overviewerConfig.map.north_direction == 'lower-right'){ + lng = 0.5 - (1.0 / Math.pow(2, overviewerConfig.map.maxZoom + 1)); + } else{ lng = 0.5 + (1.0 / Math.pow(2, overviewerConfig.map.maxZoom + 1)); } @@ -568,12 +568,12 @@ var overviewer = { // Revert base positioning // See equivalent code in fromWorldToLatLng() - lng -= 0.5 - (1.0 / Math.pow(2, overviewerConfig.map.maxZoom + 1)); lat -= 0.5; - if(overviewerConfig.map.north_direction == 'upper-right'){ - lng -= 0.5 + (1.0 / Math.pow(2, overviewerConfig.map.maxZoom + 1)); - } else if(overviewerConfig.map.north_direction == 'upper-left'){ + if(overviewerConfig.map.north_direction == 'lower-left' || + overviewerConfig.map.north_direction == 'lower-right'){ + lng -= 0.5 - (1.0 / Math.pow(2, overviewerConfig.map.maxZoom + 1)); + } else{ lng -= 0.5 + (1.0 / Math.pow(2, overviewerConfig.map.maxZoom + 1)); } From e732eb6450c2de8dfe847a837d14fb44eb8c13ff Mon Sep 17 00:00:00 2001 From: Ryan Rector Date: Sun, 10 Jul 2011 21:05:45 -0600 Subject: [PATCH 09/44] A smarter (?) attempt at rotating everything. --- chunk.py | 5 ++--- nbt.py | 29 ++++++++++++++++++++++------- quadtree.py | 4 ---- world.py | 25 +++++++------------------ 4 files changed, 31 insertions(+), 32 deletions(-) diff --git a/chunk.py b/chunk.py index 4ba13c8..0921e54 100644 --- a/chunk.py +++ b/chunk.py @@ -55,8 +55,7 @@ def get_lvldata(world, filename, x, y, retries=2): raise NoSuchChunk try: - d = world.load_from_region(filename, x, y) - #TODO should probably do rotation from here + d = world.load_from_region(filename, x, y) except Exception, e: if retries > 0: # wait a little bit, and try again (up to `retries` times) @@ -78,7 +77,7 @@ def get_blockarray(level, north_direction): def get_blockarray_fromfile(filename, north_direction): """Same as get_blockarray except takes a filename. This is a shortcut""" - d = nbt.load_from_region(filename, x, y) + d = nbt.load_from_region(filename, x, y, north_direction) level = d[1]['Level'] return get_blockarray(level, north_direction) diff --git a/nbt.py b/nbt.py index aa30700..7a095e8 100644 --- a/nbt.py +++ b/nbt.py @@ -17,6 +17,7 @@ import gzip, zlib import struct import StringIO import os +import numpy # decorator to handle filename or object as first parameter def _file_loader(func): @@ -34,15 +35,15 @@ def _file_loader(func): def load(fileobj): return NBTFileReader(fileobj).read_all() -def load_from_region(filename, x, y): - nbt = load_region(filename).load_chunk(x, y) +def load_from_region(filename, x, y, north_direction): + nbt = load_region(filename, north_direction).load_chunk(x, y) if nbt is None: return None ## return none. I think this is who we should indicate missing chunks #raise IOError("No such chunk in region: (%i, %i)" % (x, y)) return nbt.read_all() -def load_region(filename): - return MCRFileReader(filename) +def load_region(filename, north_direction): + return MCRFileReader(filename, north_direction) # compile the unpacker's into a classes @@ -199,13 +200,25 @@ class MCRFileReader(object): chunks (as instances of NBTFileReader), getting chunk timestamps, and for listing chunks contained in the file.""" - def __init__(self, filename): + def __init__(self, filename, north_direction): self._file = None self._filename = filename + self.north_direction = north_direction # cache used when the entire header tables are read in get_chunks() self._locations = None self._timestamps = None self._chunks = None + + def get_north_rotations(self): + #Upper-left and lower-right are swapped from chunk.py rots + if self.north_direction == "upper-left": + return 3 + elif self.north_direction == "upper-right": + return 2 + elif self.north_direction == "lower-right": + return 1 + elif self.north_direction == "lower-left": + return 0 def _read_24bit_int(self): """Read in a 24-bit, big-endian int, used in the chunk @@ -328,11 +341,13 @@ class MCRFileReader(object): locations_append = self._locations.append for _ in xrange(32*32): locations_append(self._read_chunk_location()) + self._locations = numpy.reshape(numpy.rot90(numpy.reshape(self._locations, (32,32)),self.get_north_rotations()), -1) # read chunk timestamp table timestamp_append = self._timestamps.append for _ in xrange(32*32): timestamp_append(self._read_chunk_timestamp()) + self._timestamps = numpy.reshape(numpy.rot90(numpy.reshape(self._timestamps, (32,32)),self.get_north_rotations()), -1) if closeFile: self.closefile() @@ -344,10 +359,10 @@ class MCRFileReader(object): load_chunk(), this will wrap x and y into the range [0, 31]. """ x = x % 32 - y = y % 32 + y = y % 32 if self._timestamps is None: self.get_chunk_info() - return self._timestamps[x + y * 32] + return self._timestamps[x + y * 32] def chunkExists(self, x, y): """Determines if a chunk exists without triggering loading of the backend data""" diff --git a/quadtree.py b/quadtree.py index 9ccb04f..fe5002f 100644 --- a/quadtree.py +++ b/quadtree.py @@ -240,10 +240,6 @@ class QuadtreeGen(object): continue chunkx, chunky = unconvert_coords(col, row) - if self.north_direction == 'upper-right': - chunky = -chunky - elif self.north_direction == 'lower-right': - chunkx = -chunkx regionx_ = chunkx//32 regiony_ = chunky//32 diff --git a/world.py b/world.py index abc995d..a53b823 100644 --- a/world.py +++ b/world.py @@ -169,7 +169,7 @@ class World(object): if self.regions.get(filename) is not None: self.regions[filename][0].closefile() chunkcache = {} - mcr = nbt.MCRFileReader(filename) + mcr = nbt.MCRFileReader(filename, self.north_direction) self.regions[filename] = (mcr,os.path.getmtime(filename),chunkcache) return mcr @@ -185,26 +185,12 @@ class World(object): in the image each one should be. Returns (col, row).""" # change this function, and you MUST change unconvert_coords - if self.north_direction == 'lower-left': - return (chunkx + chunky, chunky - chunkx) - elif self.north_direction == 'lower-right': - return (chunkx + chunky, chunkx - chunky) - elif self.north_direction == 'upper-left': - return (chunkx - chunky, chunkx + chunky) - elif self.north_direction == 'upper-right': - return (chunky - chunkx, chunkx + chunky) + return (chunkx + chunky, chunky - chunkx) def unconvert_coords(self, col, row): """Undoes what convert_coords does. Returns (chunkx, chunky).""" - if self.north_direction == 'lower-left': - return ((col - row) / 2, (row + col) / 2) - if self.north_direction == 'lower-right': - return ((col + row) / 2, (col - row) / 2) - if self.north_direction == 'upper-left': - return ((col + row) / 2, (row - col) / 2) - if self.north_direction == 'upper-right': - return ((row - col) / 2, (col + row) / 2) + return ((col - row) / 2, (row + col) / 2) def findTrueSpawn(self): """Adds the true spawn location to self.POI. The spawn Y coordinate @@ -225,7 +211,10 @@ class World(object): chunkFile = self.get_region_path(chunkX, chunkY) if chunkFile is not None: - data = nbt.load_from_region(chunkFile, chunkX, chunkY)[1] + #TODO I broke it + + #data = nbt.load_from_region(chunkFile, chunkX, chunkY, self.north_direction)[1] + data = None if data is not None: level = data['Level'] blockArray = numpy.frombuffer(level['Blocks'], dtype=numpy.uint8).reshape((16,16,128)) From 6124ffd0edcae99c47bfaedc36bfb4781fcded2f Mon Sep 17 00:00:00 2001 From: Ryan Rector Date: Sun, 10 Jul 2011 21:06:09 -0600 Subject: [PATCH 10/44] Early work on rotating stubborn textures --- rendernode.py | 3 ++- textures.py | 69 ++++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 68 insertions(+), 4 deletions(-) diff --git a/rendernode.py b/rendernode.py index 6f6c76e..10db6c6 100644 --- a/rendernode.py +++ b/rendernode.py @@ -65,7 +65,8 @@ def pool_initializer(rendernode): # make sure textures are generated for this process # and initialize c_overviewer - textures.generate(path=rendernode.options.get('textures_path', None)) + textures.generate(north_direction=rendernode.options.get('north_direction', None), + path=rendernode.options.get('textures_path', None)) c_overviewer.init_chunk_render() # load biome data in each process, if needed diff --git a/textures.py b/textures.py index 5e2db3b..61ea8af 100644 --- a/textures.py +++ b/textures.py @@ -500,7 +500,7 @@ def generate_texture_tuple(img, blockid): blockmap list and specialblockmap dictionary.""" return (img.convert("RGB"), img.split()[3], generate_opaque_mask(img)) -def generate_special_texture(blockID, data): +def generate_special_texture(blockID, data, north_direction): """Generates a special texture, such as a correctly facing minecraft track""" #print "%s has ancillary data: %X" %(blockID, data) # TODO ladders, stairs, levers, buttons, and signs @@ -594,6 +594,11 @@ def generate_special_texture(blockID, data): if blockID == 26: # bed + if north_direction == 'upper-right': + if (data & 0x3) == 0: data = data & 0x8 | 0x2 + elif (data & 0x3) == 1: data = data & 0x8 | 0x3 + elif (data & 0x3) == 2: data = data & 0x8 | 0x0 + elif (data & 0x3) == 3: data = data & 0x8 | 0x1 increment = 5 left_face = None right_face = None @@ -708,6 +713,11 @@ def generate_special_texture(blockID, data): if blockID in (50,75,76): # torch, off redstone torch, on redstone torch + if north_direction == 'upper-right': + if data == 1: data = 2 + elif data == 2: data = 1 + elif data == 3: data = 4 + elif data == 4: data = 3 # choose the proper texture if blockID == 50: # torch @@ -777,6 +787,11 @@ def generate_special_texture(blockID, data): if blockID in (53,67): # wooden and cobblestone stairs. + if north_direction == 'upper-right': + if data == 0: data = 1 + elif data == 1: data = 0 + elif data == 2: data = 3 + elif data == 3: data = 2 if blockID == 53: # wooden texture = terrain_images[4] @@ -988,6 +1003,11 @@ def generate_special_texture(blockID, data): if blockID in (61, 62, 23): #furnace and burning furnace + if north_direction == 'upper-right': + if data == 2: data = 3 + elif data == 3: data = 2 + elif data == 4: data = 5 + elif data == 5: data = 4 top = terrain_images[62] side = terrain_images[45] @@ -1018,6 +1038,8 @@ def generate_special_texture(blockID, data): # cut the planks to the size of a signpost ImageDraw.Draw(texture).rectangle((0,12,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) + if north_direction == 'upper-right': + data = (data + 8) % 16 # If the signpost is looking directly to the image, draw some # random dots, they will look as text. if data in (0,1,2,3,4,5,15): @@ -1070,6 +1092,11 @@ def generate_special_texture(blockID, data): # mask out the high bits to figure out the orientation img = Image.new("RGBA", (24,24), (38,92,255,0)) + if north_direction == 'upper-right': + if (data & 0x3) == 0: data = data & 0xc | 0x2 + elif (data & 0x3) == 1: data = data & 0xc | 0x3 + elif (data & 0x3) == 2: data = data & 0xc | 0x0 + elif (data & 0x3) == 3: data = data & 0xc | 0x1 if (data & 0x03) == 0: if not swung: tex = transform_image_side(raw_door) @@ -1108,6 +1135,11 @@ def generate_special_texture(blockID, data): if blockID == 65: # ladder + if north_direction == 'upper-right': + if data == 2: data = 3 + elif data == 3: data = 2 + elif data == 4: data = 5 + elif data == 5: data = 4 img = Image.new("RGBA", (24,24), (38,92,255,0)) raw_texture = terrain_images[83] #print "ladder is facing: %d" % data @@ -1154,6 +1186,17 @@ def generate_special_texture(blockID, data): raw_straight = terrain_images[128] raw_corner = terrain_images[112] + if north_direction == 'upper-right': + if data == 2: data = 3 + elif data == 3: data = 2 + elif data == 4: data = 5 + elif data == 5: data = 4 + + elif data == 6: data = 8 + elif data == 7: data = 9 + elif data == 8: data = 6 + elif data == 9: data = 7 + ## use transform_image to scale and shear if data == 0: track = transform_image(raw_straight, blockID) @@ -1202,6 +1245,11 @@ def generate_special_texture(blockID, data): if blockID == 68: # wall sign + if north_direction == 'upper-right': + if data == 2: data = 3 + elif data == 3: data = 2 + elif data == 4: data = 5 + elif data == 5: data = 4 texture = terrain_images[4].copy() # cut the planks to the size of a signpost ImageDraw.Draw(texture).rectangle((0,12,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) @@ -1332,6 +1380,11 @@ def generate_special_texture(blockID, data): if blockID in (86,91): # pumpkins, jack-o-lantern + if north_direction == 'upper-right': + if data == 0: data = 2 + elif data == 1: data = 3 + elif data == 2: data = 0 + elif data == 3: data = 1 top = terrain_images[102] frontID = 119 if blockID == 86 else 120 front = terrain_images[frontID] @@ -1393,6 +1446,11 @@ def generate_special_texture(blockID, data): if blockID in (93, 94): # redstone repeaters, ON and OFF # NOTE: this function uses the redstone torches generated above, # this must run after the function of the torches. + if north_direction == 'upper-right': + if (data & 0x3) == 0: data = data & 0xc | 0x2 + elif (data & 0x3) == 1: data = data & 0xc | 0x3 + elif (data & 0x3) == 2: data = data & 0xc | 0x0 + elif (data & 0x3) == 3: data = data & 0xc | 0x1 top = terrain_images[131] if blockID == 93 else terrain_images[147] side = terrain_images[5] @@ -1509,6 +1567,11 @@ def generate_special_texture(blockID, data): if blockID == 96: # trapdoor + if north_direction == 'upper-right': + if (data & 0x3) == 0: data = data & 0x4 | 0x1 + elif (data & 0x3) == 1: data = data & 0x4 | 0x0 + elif (data & 0x3) == 2: data = data & 0x4 | 0x3 + elif (data & 0x3) == 3: data = data & 0x4 | 0x2 texture = terrain_images[84] if data & 0x4 == 0x4: # opened trapdoor if data & 0x3 == 0: # west @@ -1664,7 +1727,7 @@ biome_tall_fern_texture = None biome_leaf_texture = None specialblockmap = None -def generate(path=None): +def generate(north_direction, path=None): global _find_file_local_path _find_file_local_path = path @@ -1689,4 +1752,4 @@ def generate(path=None): specialblockmap = {} for blockID in special_blocks: for data in special_map[blockID]: - specialblockmap[(blockID, data)] = generate_special_texture(blockID, data) + specialblockmap[(blockID, data)] = generate_special_texture(blockID, data, north_direction) From 1b06da4e737ed3f9036ec3ff18bc0a776df7e0aa Mon Sep 17 00:00:00 2001 From: Ryan Rector Date: Mon, 11 Jul 2011 10:44:55 -0600 Subject: [PATCH 11/44] Rotate region coords based on configured north --- world.py | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/world.py b/world.py index a53b823..b5ce5a0 100644 --- a/world.py +++ b/world.py @@ -189,7 +189,6 @@ class World(object): def unconvert_coords(self, col, row): """Undoes what convert_coords does. Returns (chunkx, chunky).""" - return ((col - row) / 2, (row + col) / 2) def findTrueSpawn(self): @@ -285,6 +284,7 @@ class World(object): world. Returns (regionx, regiony, filename)""" + join = os.path.join if regionlist is not None: for path in regionlist: @@ -293,7 +293,20 @@ class World(object): if f.startswith("r.") and f.endswith(".mcr"): p = f.split(".") logging.debug("Using path %s from regionlist", f) - yield (int(p[1]), int(p[2]), join(self.worlddir, 'region', f)) + x = int(p[1]) + y = int(p[2]) + if self.north_direction == 'upper-right': + x = -x + y = -y + elif self.north_direction == 'upper-left': + temp = x + x = -y + y = temp + elif self.north_direction == 'lower-right': + temp = x + x = y + y = -temp + yield (x, y, join(self.worlddir, 'region', f)) else: logging.warning("Ignore path '%s' in regionlist", f) @@ -301,7 +314,20 @@ class World(object): for path in glob(os.path.join(self.worlddir, 'region') + "/r.*.*.mcr"): dirpath, f = os.path.split(path) p = f.split(".") - yield (int(p[1]), int(p[2]), join(dirpath, f)) + x = int(p[1]) + y = int(p[2]) + if self.north_direction == 'upper-right': + x = -x + y = -y + elif self.north_direction == 'upper-left': + temp = x + x = -y + y = temp + elif self.north_direction == 'lower-right': + temp = x + x = y + y = -temp + yield (x, y, join(dirpath, f)) def get_save_dir(): """Returns the path to the local saves directory From d32cc2aa1089d30fdf6201b1eb0a1721335e6e38 Mon Sep 17 00:00:00 2001 From: Ryan Rector Date: Mon, 11 Jul 2011 12:21:47 -0600 Subject: [PATCH 12/44] Update coord conversion on map to handle new rotation option --- web_assets/overviewer.js | 45 +++++++++++++--------------------------- 1 file changed, 14 insertions(+), 31 deletions(-) diff --git a/web_assets/overviewer.js b/web_assets/overviewer.js index 08f85b2..424cdc4 100644 --- a/web_assets/overviewer.js +++ b/web_assets/overviewer.js @@ -492,20 +492,17 @@ var overviewer = { var perPixel = 1.0 / (overviewerConfig.CONST.tileSize * Math.pow(2, overviewerConfig.map.maxZoom)); - if(overviewerConfig.map.north_direction == 'lower-left'){ - x = x; - y = y; - } else if(overviewerConfig.map.north_direction == 'upper-right'){ - x = -x-1; - y = -y-1; + if(overviewerConfig.map.north_direction == 'upper-right'){ + x = -x-1+512; + y = -y-1+512; } else if(overviewerConfig.map.north_direction == 'upper-left'){ temp = x; - x = -y-1; - y = temp-16; + x = -y-1+512; + y = temp; } else if(overviewerConfig.map.north_direction == 'lower-right'){ temp = x; x = y; - y = -temp-1+16; + y = -temp-1+512; } // This information about where the center column is may change with @@ -517,13 +514,7 @@ var overviewer = { // ((tileSize / 2) / (tileSize * 2^maxZoom)) // or equivalently, 0.5 - (1 / 2^(maxZoom + 1)) lat = 0.5; - if(overviewerConfig.map.north_direction == 'lower-left' || - overviewerConfig.map.north_direction == 'lower-right'){ - lng = 0.5 - (1.0 / Math.pow(2, overviewerConfig.map.maxZoom + 1)); - } else{ - lng = 0.5 + (1.0 / Math.pow(2, overviewerConfig.map.maxZoom + 1)); - } - + lng = 0.5 - (1.0 / Math.pow(2, overviewerConfig.map.maxZoom + 1)); // the following metrics mimic those in ChunkRenderer.chunk_render // in "chunk.py" or, equivalently, chunk_render in src/iterate.c @@ -570,12 +561,7 @@ var overviewer = { // See equivalent code in fromWorldToLatLng() lat -= 0.5; - if(overviewerConfig.map.north_direction == 'lower-left' || - overviewerConfig.map.north_direction == 'lower-right'){ - lng -= 0.5 - (1.0 / Math.pow(2, overviewerConfig.map.maxZoom + 1)); - } else{ - lng -= 0.5 + (1.0 / Math.pow(2, overviewerConfig.map.maxZoom + 1)); - } + lng -= 0.5 - (1.0 / Math.pow(2, overviewerConfig.map.maxZoom + 1)); // I'll admit, I plugged this into Wolfram Alpha: // a = (x * 12 * r) + (z * 12 * r), b = (z * 6 * r) - (x * 6 * r) @@ -591,20 +577,17 @@ var overviewer = { point.x += 64; point.z -= 64; - if(overviewerConfig.map.north_direction == 'lower-left'){ - point.x = point.x; - point.z = point.z; - } else if(overviewerConfig.map.north_direction == 'upper-right'){ - point.x = -point.x; - point.z = -point.z; + if(overviewerConfig.map.north_direction == 'upper-right'){ + point.x = -point.x+512; + point.z = -point.z+512; } else if(overviewerConfig.map.north_direction == 'upper-left'){ temp = point.z; - point.z = -point.x; - point.x = temp+16; + point.z = -point.x+512; + point.x = temp; } else if(overviewerConfig.map.north_direction == 'lower-right'){ temp = point.z; point.z = point.x; - point.x = -temp+16; + point.x = -temp+512; } return point; From 3f3ca1979f7b1134b37238140296133ae89b0e91 Mon Sep 17 00:00:00 2001 From: Ryan Rector Date: Mon, 11 Jul 2011 15:47:45 -0600 Subject: [PATCH 13/44] Move special texture rotation to it's own function --- textures.py | 156 ++++++++++++++++++++++++++++++---------------------- 1 file changed, 89 insertions(+), 67 deletions(-) diff --git a/textures.py b/textures.py index 61ea8af..97f8b51 100644 --- a/textures.py +++ b/textures.py @@ -505,7 +505,9 @@ def generate_special_texture(blockID, data, north_direction): #print "%s has ancillary data: %X" %(blockID, data) # TODO ladders, stairs, levers, buttons, and signs # all need to behandled here (and in chunkpy) - + + data = convert_data(blockID, data, north_direction) + if blockID == 2: # grass # data & 0x10 means SNOW sides side_img = terrain_images[3] @@ -594,11 +596,6 @@ def generate_special_texture(blockID, data, north_direction): if blockID == 26: # bed - if north_direction == 'upper-right': - if (data & 0x3) == 0: data = data & 0x8 | 0x2 - elif (data & 0x3) == 1: data = data & 0x8 | 0x3 - elif (data & 0x3) == 2: data = data & 0x8 | 0x0 - elif (data & 0x3) == 3: data = data & 0x8 | 0x1 increment = 5 left_face = None right_face = None @@ -713,12 +710,6 @@ def generate_special_texture(blockID, data, north_direction): if blockID in (50,75,76): # torch, off redstone torch, on redstone torch - if north_direction == 'upper-right': - if data == 1: data = 2 - elif data == 2: data = 1 - elif data == 3: data = 4 - elif data == 4: data = 3 - # choose the proper texture if blockID == 50: # torch small = terrain_images[80] @@ -787,11 +778,6 @@ def generate_special_texture(blockID, data, north_direction): if blockID in (53,67): # wooden and cobblestone stairs. - if north_direction == 'upper-right': - if data == 0: data = 1 - elif data == 1: data = 0 - elif data == 2: data = 3 - elif data == 3: data = 2 if blockID == 53: # wooden texture = terrain_images[4] @@ -1003,11 +989,6 @@ def generate_special_texture(blockID, data, north_direction): if blockID in (61, 62, 23): #furnace and burning furnace - if north_direction == 'upper-right': - if data == 2: data = 3 - elif data == 3: data = 2 - elif data == 4: data = 5 - elif data == 5: data = 4 top = terrain_images[62] side = terrain_images[45] @@ -1033,13 +1014,10 @@ def generate_special_texture(blockID, data, north_direction): if blockID == 63: # singposts - texture = terrain_images[4].copy() # cut the planks to the size of a signpost ImageDraw.Draw(texture).rectangle((0,12,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) - if north_direction == 'upper-right': - data = (data + 8) % 16 # If the signpost is looking directly to the image, draw some # random dots, they will look as text. if data in (0,1,2,3,4,5,15): @@ -1092,11 +1070,6 @@ def generate_special_texture(blockID, data, north_direction): # mask out the high bits to figure out the orientation img = Image.new("RGBA", (24,24), (38,92,255,0)) - if north_direction == 'upper-right': - if (data & 0x3) == 0: data = data & 0xc | 0x2 - elif (data & 0x3) == 1: data = data & 0xc | 0x3 - elif (data & 0x3) == 2: data = data & 0xc | 0x0 - elif (data & 0x3) == 3: data = data & 0xc | 0x1 if (data & 0x03) == 0: if not swung: tex = transform_image_side(raw_door) @@ -1135,11 +1108,6 @@ def generate_special_texture(blockID, data, north_direction): if blockID == 65: # ladder - if north_direction == 'upper-right': - if data == 2: data = 3 - elif data == 3: data = 2 - elif data == 4: data = 5 - elif data == 5: data = 4 img = Image.new("RGBA", (24,24), (38,92,255,0)) raw_texture = terrain_images[83] #print "ladder is facing: %d" % data @@ -1186,17 +1154,6 @@ def generate_special_texture(blockID, data, north_direction): raw_straight = terrain_images[128] raw_corner = terrain_images[112] - if north_direction == 'upper-right': - if data == 2: data = 3 - elif data == 3: data = 2 - elif data == 4: data = 5 - elif data == 5: data = 4 - - elif data == 6: data = 8 - elif data == 7: data = 9 - elif data == 8: data = 6 - elif data == 9: data = 7 - ## use transform_image to scale and shear if data == 0: track = transform_image(raw_straight, blockID) @@ -1245,11 +1202,6 @@ def generate_special_texture(blockID, data, north_direction): if blockID == 68: # wall sign - if north_direction == 'upper-right': - if data == 2: data = 3 - elif data == 3: data = 2 - elif data == 4: data = 5 - elif data == 5: data = 4 texture = terrain_images[4].copy() # cut the planks to the size of a signpost ImageDraw.Draw(texture).rectangle((0,12,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) @@ -1380,11 +1332,6 @@ def generate_special_texture(blockID, data, north_direction): if blockID in (86,91): # pumpkins, jack-o-lantern - if north_direction == 'upper-right': - if data == 0: data = 2 - elif data == 1: data = 3 - elif data == 2: data = 0 - elif data == 3: data = 1 top = terrain_images[102] frontID = 119 if blockID == 86 else 120 front = terrain_images[frontID] @@ -1446,11 +1393,6 @@ def generate_special_texture(blockID, data, north_direction): if blockID in (93, 94): # redstone repeaters, ON and OFF # NOTE: this function uses the redstone torches generated above, # this must run after the function of the torches. - if north_direction == 'upper-right': - if (data & 0x3) == 0: data = data & 0xc | 0x2 - elif (data & 0x3) == 1: data = data & 0xc | 0x3 - elif (data & 0x3) == 2: data = data & 0xc | 0x0 - elif (data & 0x3) == 3: data = data & 0xc | 0x1 top = terrain_images[131] if blockID == 93 else terrain_images[147] side = terrain_images[5] @@ -1567,11 +1509,6 @@ def generate_special_texture(blockID, data, north_direction): if blockID == 96: # trapdoor - if north_direction == 'upper-right': - if (data & 0x3) == 0: data = data & 0x4 | 0x1 - elif (data & 0x3) == 1: data = data & 0x4 | 0x0 - elif (data & 0x3) == 2: data = data & 0x4 | 0x3 - elif (data & 0x3) == 3: data = data & 0x4 | 0x2 texture = terrain_images[84] if data & 0x4 == 0x4: # opened trapdoor if data & 0x3 == 0: # west @@ -1588,9 +1525,94 @@ def generate_special_texture(blockID, data, north_direction): return generate_texture_tuple(img, blockID) - return None +def convert_data(blockID, data, north_direction): + if blockID == 26: # bed + if north_direction == 'upper-right': + #Masked to not clobber block head/foot info + if (data & 0b0011) == 0: data = data & 0b1100 | 2 + elif (data & 0b0011) == 1: data = data & 0b1100 | 3 + elif (data & 0b0011) == 2: data = data & 0b1100 | 0 + elif (data & 0b0011) == 3: data = data & 0b1100 | 1 + if blockID in (27, 28, 66): # minetrack: + if north_direction == 'upper-right': + #Masked to not clobber powered rail on/off info + #Ascending + if (data & 0b0111) == 2: data = data & 0b1000 | 3 + elif (data & 0b0111) == 3: data = data & 0b1000 | 2 + elif (data & 0b0111) == 4: data = data & 0b1000 | 5 + elif (data & 0b0111) == 5: data = data & 0b1000 | 4 + if blockID == 66: #normal minetrack only + if north_direction == 'upper-right': + #Corners + if data == 6: data = 8 + elif data == 7: data = 9 + elif data == 8: data = 6 + elif data == 9: data = 7 + if blockID in (50, 75, 76): # torch, off/on redstone torch + if north_direction == 'upper-right': + if data == 1: data = 2 + elif data == 2: data = 1 + elif data == 3: data = 4 + elif data == 4: data = 3 + if blockID in (53,67): # wooden and cobblestone stairs. + if north_direction == 'upper-right': + if data == 0: data = 1 + elif data == 1: data = 0 + elif data == 2: data = 3 + elif data == 3: data = 2 + if blockID in (61, 62, 23): # furnace and burning furnace + if north_direction == 'upper-right': + if data == 2: data = 3 + elif data == 3: data = 2 + elif data == 4: data = 5 + elif data == 5: data = 4 + if blockID == 63: # signposts + if north_direction == 'upper-right': + data = (data + 8) % 16 + if blockID in (64,71): # wooden door, or iron door + if north_direction == 'upper-right': + #Masked to not clobber block top/bottom & swung info + if (data & 0b0011) == 0: data = data & 0b1100 | 2 + elif (data & 0b0011) == 1: data = data & 0b1100 | 3 + elif (data & 0b0011) == 2: data = data & 0b1100 | 0 + elif (data & 0b0011) == 3: data = data & 0b1100 | 1 + if blockID == 65: # ladder + if north_direction == 'upper-right': + if data == 2: data = 3 + elif data == 3: data = 2 + elif data == 4: data = 5 + elif data == 5: data = 4 + if blockID == 68: # wall sign + if north_direction == 'upper-right': + if data == 2: data = 3 + elif data == 3: data = 2 + elif data == 4: data = 5 + elif data == 5: data = 4 + if blockID in (86,91): # pumpkins, jack-o-lantern + if north_direction == 'upper-right': + if data == 0: data = 2 + elif data == 1: data = 3 + elif data == 2: data = 0 + elif data == 3: data = 1 + if blockID in (93, 94): # redstone repeaters, ON and OFF + if north_direction == 'upper-right': + #Masked to not clobber delay info + if (data & 0b0011) == 0: data = data & 0b1100 | 2 + elif (data & 0b0011) == 1: data = data & 0b1100 | 3 + elif (data & 0b0011) == 2: data = data & 0b1100 | 0 + elif (data & 0b0011) == 3: data = data & 0b1100 | 1 + if blockID == 96: # trapdoor + if north_direction == 'upper-right': + #Masked to not clobber opened/closed info + if (data & 0b0011) == 0: data = data & 0b1100 | 1 + elif (data & 0b0011) == 1: data = data & 0b1100 | 0 + elif (data & 0b0011) == 2: data = data & 0b1100 | 3 + elif (data & 0b0011) == 3: data = data & 0b1100 | 2 + + return data + def tintTexture(im, c): # apparently converting to grayscale drops the alpha channel? i = ImageOps.colorize(ImageOps.grayscale(im), (0,0,0), c) From d0f98223e4631b8392301c6c4054e2074af27508 Mon Sep 17 00:00:00 2001 From: Ryan Rector Date: Mon, 11 Jul 2011 16:01:46 -0600 Subject: [PATCH 14/44] Enhance north-direction commandline option --- overviewer.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/overviewer.py b/overviewer.py index 74c5414..79550fe 100755 --- a/overviewer.py +++ b/overviewer.py @@ -96,6 +96,7 @@ def main(): cpus = 1 avail_rendermodes = c_overviewer.get_render_modes() + avail_north_dirs = ['lower-left', 'upper-left', 'upper-right', 'lower-right'] parser = ConfigOptionParser(usage=helptext, config="settings.py") parser.add_option("-V", "--version", dest="version", help="Displays version information and then exits", action="store_true") @@ -118,7 +119,7 @@ def main(): parser.add_option("--skip-js", dest="skipjs", action="store_true", help="Don't output marker.js or regions.js") parser.add_option("--no-signs", dest="nosigns", action="store_true", help="Don't output signs to markers.js") parser.add_option("--display-config", dest="display_config", action="store_true", help="Display the configuration parameters, but don't render the map. Requires all required options to be specified", commandLineOnly=True) - parser.add_option("--north-direction", dest="north_direction", help="Specifies which corner of the screen North will point to.", type="string", default="upper-right") + parser.add_option("--north-direction", dest="north_direction", help="Specifies which corner of the screen north will point to. Valid options are: " + ", ".join(avail_north_dirs) + ".", type="choice", default=avail_north_dirs[0], choices=avail_north_dirs) #parser.add_option("--write-config", dest="write_config", action="store_true", help="Writes out a sample config file", commandLineOnly=True) options, args = parser.parse_args() From 1fdd143a983453c609c7b36954510efa518a1939 Mon Sep 17 00:00:00 2001 From: Ryan Rector Date: Mon, 11 Jul 2011 16:03:28 -0600 Subject: [PATCH 15/44] Explain available north-directions in sample settings --- sample.settings.py | 1 + 1 file changed, 1 insertion(+) diff --git a/sample.settings.py b/sample.settings.py index e31cd1f..01fcf2b 100644 --- a/sample.settings.py +++ b/sample.settings.py @@ -155,6 +155,7 @@ if "web_assets_hook" in locals(): ################################################################################ ### north_direction ## Make north point somewhere else! +## Valid options are 'lower-left', 'upper-left', 'upper-right', 'upper-left' ## default: upper-right ## Type: string ## Example: From eebd0a27409e739c21cb8ee3bb36f92671ce1bfb Mon Sep 17 00:00:00 2001 From: Ryan Rector Date: Mon, 11 Jul 2011 16:17:07 -0600 Subject: [PATCH 16/44] Add info on north-direction to readme --- README.rst | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/README.rst b/README.rst index 942e4cb..5a8ee1a 100644 --- a/README.rst +++ b/README.rst @@ -22,6 +22,28 @@ https://github.com/brownan/Minecraft-Overviewer/wiki/Documentation To contact the developers and other users, go to the site at the top of this README, or go to #overviewer on irc.freenode.net. +Additional Features +------------------- +A configurable north direction! I'm not a big fan of north point to the bottom +left corner of the screen, and I bet you aren't either. So here you go. Either +use the commandline option --north-direction, or in the settings file use +north_direction. Valid options are 'lower-left', 'upper-left', 'upper-right', +and 'lower-right'. + +Additional Bugs +--------------- +The upper-right direction seems to exacerbate an existing bug that will +not let you click on a sign in some cases. The upper-right direction seems to +mostly always trigger this bug. Still investigating. And banging my head on +my desk. + +Some blocks don't rotate properly in the upper-left and lower-right directions. + +Upstream +-------- +Once I've squashed some bugs and cleaned up the code a bit, I will look to +getting this included in the official Minecraft Overviewer. + Features ======== From caef1afaf0a64a8f0e7ad3f4c2d4431b1fe843de Mon Sep 17 00:00:00 2001 From: Ryan Rector Date: Mon, 11 Jul 2011 16:17:07 -0600 Subject: [PATCH 17/44] Add info on north-direction to readme --- README.rst | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/README.rst b/README.rst index 942e4cb..6d2c400 100644 --- a/README.rst +++ b/README.rst @@ -22,6 +22,25 @@ https://github.com/brownan/Minecraft-Overviewer/wiki/Documentation To contact the developers and other users, go to the site at the top of this README, or go to #overviewer on irc.freenode.net. +###Additional Features +A configurable north direction! I'm not a big fan of north point to the bottom +left corner of the screen, and I bet you aren't either. So here you go. Either +use the commandline option --north-direction, or in the settings file use +north_direction. Valid options are 'lower-left', 'upper-left', 'upper-right', +and 'lower-right'. + +###Additional Bugs +The upper-right direction seems to exacerbate an existing bug that will +not let you click on a sign in some cases. The upper-right direction seems to +mostly always trigger this bug. Still investigating. And banging my head on +my desk. + +Some blocks don't rotate properly in the upper-left and lower-right directions. + +###Upstream +Once I've squashed some bugs and cleaned up the code a bit, I will look to +getting this included in the official Minecraft Overviewer. + Features ======== From afe25eed219068f9fb0f269a0bd784fef4503bb6 Mon Sep 17 00:00:00 2001 From: Ryan Rector Date: Mon, 11 Jul 2011 16:23:17 -0600 Subject: [PATCH 18/44] Readme modification again. --- README.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.rst b/README.rst index 6d2c400..d38be21 100644 --- a/README.rst +++ b/README.rst @@ -22,14 +22,14 @@ https://github.com/brownan/Minecraft-Overviewer/wiki/Documentation To contact the developers and other users, go to the site at the top of this README, or go to #overviewer on irc.freenode.net. -###Additional Features +####Additional Features A configurable north direction! I'm not a big fan of north point to the bottom left corner of the screen, and I bet you aren't either. So here you go. Either use the commandline option --north-direction, or in the settings file use north_direction. Valid options are 'lower-left', 'upper-left', 'upper-right', and 'lower-right'. -###Additional Bugs +####Additional Bugs The upper-right direction seems to exacerbate an existing bug that will not let you click on a sign in some cases. The upper-right direction seems to mostly always trigger this bug. Still investigating. And banging my head on @@ -37,7 +37,7 @@ my desk. Some blocks don't rotate properly in the upper-left and lower-right directions. -###Upstream +####Upstream Once I've squashed some bugs and cleaned up the code a bit, I will look to getting this included in the official Minecraft Overviewer. From 3a6194a6f1971bafd251e760e5bba4391d964238 Mon Sep 17 00:00:00 2001 From: Ryan Rector Date: Mon, 11 Jul 2011 16:24:23 -0600 Subject: [PATCH 19/44] I give up. Markdown is apparently not for me. --- README.rst | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/README.rst b/README.rst index d38be21..942e4cb 100644 --- a/README.rst +++ b/README.rst @@ -22,25 +22,6 @@ https://github.com/brownan/Minecraft-Overviewer/wiki/Documentation To contact the developers and other users, go to the site at the top of this README, or go to #overviewer on irc.freenode.net. -####Additional Features -A configurable north direction! I'm not a big fan of north point to the bottom -left corner of the screen, and I bet you aren't either. So here you go. Either -use the commandline option --north-direction, or in the settings file use -north_direction. Valid options are 'lower-left', 'upper-left', 'upper-right', -and 'lower-right'. - -####Additional Bugs -The upper-right direction seems to exacerbate an existing bug that will -not let you click on a sign in some cases. The upper-right direction seems to -mostly always trigger this bug. Still investigating. And banging my head on -my desk. - -Some blocks don't rotate properly in the upper-left and lower-right directions. - -####Upstream -Once I've squashed some bugs and cleaned up the code a bit, I will look to -getting this included in the official Minecraft Overviewer. - Features ======== From 110bd10569d142edad9a32a595d1b9ed46b3f8e6 Mon Sep 17 00:00:00 2001 From: Ryan Rector Date: Mon, 11 Jul 2011 21:42:24 -0600 Subject: [PATCH 20/44] Handle more regions This is much, much slower than numpy would do it. I should probably do this right sometime, but right now I just want it to work. --- nbt.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/nbt.py b/nbt.py index 7a095e8..d5c8e96 100644 --- a/nbt.py +++ b/nbt.py @@ -339,9 +339,21 @@ class MCRFileReader(object): # read chunk location table locations_append = self._locations.append - for _ in xrange(32*32): + locations_index = [] + for x in xrange(32*32): locations_append(self._read_chunk_location()) - self._locations = numpy.reshape(numpy.rot90(numpy.reshape(self._locations, (32,32)),self.get_north_rotations()), -1) + locations_index.append(x) + try: + temp = numpy.reshape(self._locations, (32, 32)) + temp = numpy.rot90(temp, self.get_north_rotations()) + self._locations = numpy.reshape(temp, -1) + except ValueError: + temp = numpy.reshape(locations_index, (32, 32)) + temp = numpy.rot90(temp, self.get_north_rotations()) + temp = numpy.reshape(temp, -1) + ttemp = self._locations[:] + for i, e in enumerate(temp): + self._locations[i] = ttemp[e] # read chunk timestamp table timestamp_append = self._timestamps.append From acfdc5ed4bbbde0490dd43a7ff40805a9949e885 Mon Sep 17 00:00:00 2001 From: Ryan Rector Date: Tue, 12 Jul 2011 11:51:38 -0600 Subject: [PATCH 21/44] Update readme with information on changes --- README.rst | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/README.rst b/README.rst index 942e4cb..c966cc7 100644 --- a/README.rst +++ b/README.rst @@ -22,6 +22,36 @@ https://github.com/brownan/Minecraft-Overviewer/wiki/Documentation To contact the developers and other users, go to the site at the top of this README, or go to #overviewer on irc.freenode.net. +### Additional Features + +A configurable north direction! I'm not a big fan of north pointing to the +bottom left corner of the screen, and I bet you aren't either. So here you go! +Either use the commandline option --north-direction, or use the option +north_direction in the settings file. Valid options are 'lower-left', +'upper-left', 'upper-right', and 'lower-right'. I'm partial to 'upper-right', +myself. + +### Additional Bugs + +With very small testing maps, the upper-right direction seems to exacerbate an +existing bug that will not let you click on a sign in some cases. This is a +bang-head-on-desk sort of thing, I should be able to find it but I cannot. +Somehow it does seem to work fine on my actual SMP map, but there is a good +chance that is just luck. + +Some blocks don't rotate properly in the upper-left and lower-right directions. +This is an easy fix, it just takes time. And besides, the upper-right is what +you are really looking for, anyway. Probably. + +It's slow! One of the rotation methods is very, very inefficient. It needs to be +rewritten, but I don't seem to be smart enough to do it right now. I must +investigate! No hard numbers but I will run a benchmark soon. + +### Upstream + +Once I've squashed some bugs and cleaned up the code a bit, I will see about +getting this included in the official Minecraft Overviewer. + Features ======== From 679b27aba9135376b91b64cd6b7a5d886c810bbb Mon Sep 17 00:00:00 2001 From: Ryan Rector Date: Tue, 12 Jul 2011 10:56:15 -0700 Subject: [PATCH 22/44] Edited README.rst via GitHub --- README.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.rst b/README.rst index c966cc7..fb25d4a 100644 --- a/README.rst +++ b/README.rst @@ -22,6 +22,8 @@ https://github.com/brownan/Minecraft-Overviewer/wiki/Documentation To contact the developers and other users, go to the site at the top of this README, or go to #overviewer on irc.freenode.net. +## Configurable North changes + ### Additional Features A configurable north direction! I'm not a big fan of north pointing to the From 56a5f42cd00fc5ad2dda56933b44c0cbf79635d0 Mon Sep 17 00:00:00 2001 From: Ryan Rector Date: Wed, 13 Jul 2011 13:27:18 -0600 Subject: [PATCH 23/44] Add rough benchmark to Readme --- README.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index fb25d4a..050ea06 100644 --- a/README.rst +++ b/README.rst @@ -47,7 +47,8 @@ you are really looking for, anyway. Probably. It's slow! One of the rotation methods is very, very inefficient. It needs to be rewritten, but I don't seem to be smart enough to do it right now. I must -investigate! No hard numbers but I will run a benchmark soon. +investigate! On my SMP map the initial render took 30% longer vs the original. +Be warned. ### Upstream From e26059be383ec36d1cbba71b2cf0927dc9374014 Mon Sep 17 00:00:00 2001 From: Ryan Rector Date: Wed, 13 Jul 2011 15:01:04 -0600 Subject: [PATCH 24/44] Add special texture rotation for upper-left and lower-right --- textures.py | 172 +++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 150 insertions(+), 22 deletions(-) diff --git a/textures.py b/textures.py index 97f8b51..2713761 100644 --- a/textures.py +++ b/textures.py @@ -1529,87 +1529,215 @@ def generate_special_texture(blockID, data, north_direction): def convert_data(blockID, data, north_direction): if blockID == 26: # bed - if north_direction == 'upper-right': - #Masked to not clobber block head/foot info + #Masked to not clobber block head/foot info + if north_direction == 'upper-left': + if (data & 0b0011) == 0: data = data & 0b1100 | 1 + elif (data & 0b0011) == 1: data = data & 0b1100 | 2 + elif (data & 0b0011) == 2: data = data & 0b1100 | 3 + elif (data & 0b0011) == 3: data = data & 0b1100 | 4 + elif north_direction == 'upper-right': if (data & 0b0011) == 0: data = data & 0b1100 | 2 elif (data & 0b0011) == 1: data = data & 0b1100 | 3 elif (data & 0b0011) == 2: data = data & 0b1100 | 0 elif (data & 0b0011) == 3: data = data & 0b1100 | 1 + elif north_direction == 'lower-right': + if (data & 0b0011) == 0: data = data & 0b1100 | 3 + elif (data & 0b0011) == 1: data = data & 0b1100 | 0 + elif (data & 0b0011) == 2: data = data & 0b1100 | 1 + elif (data & 0b0011) == 3: data = data & 0b1100 | 2 if blockID in (27, 28, 66): # minetrack: - if north_direction == 'upper-right': - #Masked to not clobber powered rail on/off info - #Ascending + #Masked to not clobber powered rail on/off info + #Ascending and flat straight + if north_direction == 'upper-left': + if (data & 0b0111) == 0: data = data & 0b1000 | 1 + elif (data & 0b0111) == 1: data = data & 0b1000 | 0 + elif (data & 0b0111) == 2: data = data & 0b1000 | 5 + elif (data & 0b0111) == 3: data = data & 0b1000 | 4 + elif (data & 0b0111) == 4: data = data & 0b1000 | 2 + elif (data & 0b0111) == 5: data = data & 0b1000 | 3 + elif north_direction == 'upper-right': if (data & 0b0111) == 2: data = data & 0b1000 | 3 elif (data & 0b0111) == 3: data = data & 0b1000 | 2 elif (data & 0b0111) == 4: data = data & 0b1000 | 5 elif (data & 0b0111) == 5: data = data & 0b1000 | 4 - if blockID == 66: #normal minetrack only - if north_direction == 'upper-right': - #Corners + elif north_direction == 'lower-right': + if (data & 0b0111) == 0: data = data & 0b1000 | 1 + elif (data & 0b0111) == 1: data = data & 0b1000 | 0 + elif (data & 0b0111) == 2: data = data & 0b1000 | 4 + elif (data & 0b0111) == 3: data = data & 0b1000 | 5 + elif (data & 0b0111) == 4: data = data & 0b1000 | 3 + elif (data & 0b0111) == 5: data = data & 0b1000 | 2 + if blockID == 66: # normal minetrack only + #Corners + if north_direction == 'upper-left': + if data == 6: data = 7 + elif data == 7: data = 8 + elif data == 8: data = 6 + elif data == 9: data = 9 + elif north_direction == 'upper-right': if data == 6: data = 8 elif data == 7: data = 9 elif data == 8: data = 6 elif data == 9: data = 7 + elif north_direction == 'lower-right': + if data == 6: data = 9 + elif data == 7: data = 6 + elif data == 8: data = 8 + elif data == 9: data = 7 if blockID in (50, 75, 76): # torch, off/on redstone torch - if north_direction == 'upper-right': + if north_direction == 'upper-left': + if data == 1: data = 3 + elif data == 2: data = 4 + elif data == 3: data = 2 + elif data == 4: data = 1 + elif north_direction == 'upper-right': if data == 1: data = 2 elif data == 2: data = 1 elif data == 3: data = 4 elif data == 4: data = 3 + elif north_direction == 'lower-right': + if data == 1: data = 4 + elif data == 2: data = 3 + elif data == 3: data = 1 + elif data == 4: data = 2 if blockID in (53,67): # wooden and cobblestone stairs. - if north_direction == 'upper-right': + if north_direction == 'upper-left': + if data == 0: data = 2 + elif data == 1: data = 3 + elif data == 2: data = 1 + elif data == 3: data = 0 + elif north_direction == 'upper-right': if data == 0: data = 1 elif data == 1: data = 0 elif data == 2: data = 3 elif data == 3: data = 2 + elif north_direction == 'lower-right': + if data == 0: data = 3 + elif data == 1: data = 2 + elif data == 2: data = 0 + elif data == 3: data = 1 if blockID in (61, 62, 23): # furnace and burning furnace - if north_direction == 'upper-right': + if north_direction == 'upper-left': + if data == 2: data = 5 + elif data == 3: data = 4 + elif data == 4: data = 2 + elif data == 5: data = 3 + elif north_direction == 'upper-right': if data == 2: data = 3 elif data == 3: data = 2 elif data == 4: data = 5 elif data == 5: data = 4 + elif north_direction == 'lower-right': + if data == 2: data = 4 + elif data == 3: data = 5 + elif data == 4: data = 3 + elif data == 5: data = 2 if blockID == 63: # signposts - if north_direction == 'upper-right': + if north_direction == 'upper-left': + data = (data + 4) % 16 + elif north_direction == 'upper-right': data = (data + 8) % 16 - if blockID in (64,71): # wooden door, or iron door - if north_direction == 'upper-right': - #Masked to not clobber block top/bottom & swung info + elif north_direction == 'lower-right': + data = (data + 12) % 16 + if blockID in (64,71): # wooden/iron door + #Masked to not clobber block top/bottom & swung info + if north_direction == 'upper-left': + if (data & 0b0011) == 0: data = data & 0b1100 | 1 + elif (data & 0b0011) == 1: data = data & 0b1100 | 2 + elif (data & 0b0011) == 2: data = data & 0b1100 | 3 + elif (data & 0b0011) == 3: data = data & 0b1100 | 0 + elif north_direction == 'upper-right': if (data & 0b0011) == 0: data = data & 0b1100 | 2 elif (data & 0b0011) == 1: data = data & 0b1100 | 3 elif (data & 0b0011) == 2: data = data & 0b1100 | 0 elif (data & 0b0011) == 3: data = data & 0b1100 | 1 + elif north_direction == 'lower-right': + if (data & 0b0011) == 0: data = data & 0b1100 | 3 + elif (data & 0b0011) == 1: data = data & 0b1100 | 0 + elif (data & 0b0011) == 2: data = data & 0b1100 | 1 + elif (data & 0b0011) == 3: data = data & 0b1100 | 2 if blockID == 65: # ladder - if north_direction == 'upper-right': + if north_direction == 'upper-left': + if data == 2: data = 5 + elif data == 3: data = 4 + elif data == 4: data = 2 + elif data == 5: data = 3 + elif north_direction == 'upper-right': if data == 2: data = 3 elif data == 3: data = 2 elif data == 4: data = 5 elif data == 5: data = 4 + elif north_direction == 'lower-right': + if data == 2: data = 4 + elif data == 3: data = 5 + elif data == 4: data = 3 + elif data == 5: data = 2 if blockID == 68: # wall sign - if north_direction == 'upper-right': + if north_direction == 'upper-left': + if data == 2: data = 5 + elif data == 3: data = 4 + elif data == 4: data = 2 + elif data == 5: data = 3 + elif north_direction == 'upper-right': if data == 2: data = 3 elif data == 3: data = 2 elif data == 4: data = 5 elif data == 5: data = 4 + elif north_direction == 'lower-right': + if data == 2: data = 4 + elif data == 3: data = 5 + elif data == 4: data = 3 + elif data == 5: data = 2 if blockID in (86,91): # pumpkins, jack-o-lantern - if north_direction == 'upper-right': + if north_direction == 'upper-left': + if data == 0: data = 1 + elif data == 1: data = 2 + elif data == 2: data = 3 + elif data == 3: data = 0 + elif north_direction == 'upper-right': if data == 0: data = 2 elif data == 1: data = 3 elif data == 2: data = 0 elif data == 3: data = 1 + elif north_direction == 'lower-right': + if data == 0: data = 3 + elif data == 1: data = 0 + elif data == 2: data = 1 + elif data == 3: data = 2 if blockID in (93, 94): # redstone repeaters, ON and OFF - if north_direction == 'upper-right': - #Masked to not clobber delay info + #Masked to not clobber delay info + if north_direction == 'upper-left': + if (data & 0b0011) == 0: data = data & 0b1100 | 1 + elif (data & 0b0011) == 1: data = data & 0b1100 | 2 + elif (data & 0b0011) == 2: data = data & 0b1100 | 3 + elif (data & 0b0011) == 3: data = data & 0b1100 | 0 + elif north_direction == 'upper-right': if (data & 0b0011) == 0: data = data & 0b1100 | 2 elif (data & 0b0011) == 1: data = data & 0b1100 | 3 elif (data & 0b0011) == 2: data = data & 0b1100 | 0 elif (data & 0b0011) == 3: data = data & 0b1100 | 1 + elif north_direction == 'lower-right': + if (data & 0b0011) == 0: data = data & 0b1100 | 3 + elif (data & 0b0011) == 1: data = data & 0b1100 | 0 + elif (data & 0b0011) == 2: data = data & 0b1100 | 1 + elif (data & 0b0011) == 3: data = data & 0b1100 | 2 if blockID == 96: # trapdoor - if north_direction == 'upper-right': - #Masked to not clobber opened/closed info + #Masked to not clobber opened/closed info + if north_direction == 'upper-left': + if (data & 0b0011) == 0: data = data & 0b1100 | 2 + elif (data & 0b0011) == 1: data = data & 0b1100 | 3 + elif (data & 0b0011) == 2: data = data & 0b1100 | 0 + elif (data & 0b0011) == 3: data = data & 0b1100 | 1 + elif north_direction == 'upper-right': if (data & 0b0011) == 0: data = data & 0b1100 | 1 elif (data & 0b0011) == 1: data = data & 0b1100 | 0 elif (data & 0b0011) == 2: data = data & 0b1100 | 3 elif (data & 0b0011) == 3: data = data & 0b1100 | 2 + elif north_direction == 'lower-right': + if (data & 0b0011) == 0: data = data & 0b1100 | 3 + elif (data & 0b0011) == 1: data = data & 0b1100 | 2 + elif (data & 0b0011) == 2: data = data & 0b1100 | 1 + elif (data & 0b0011) == 3: data = data & 0b1100 | 0 return data From 0ffb13eafca5c85494dad8b6151e7f5bf620f123 Mon Sep 17 00:00:00 2001 From: Ryan Rector Date: Wed, 13 Jul 2011 15:10:09 -0600 Subject: [PATCH 25/44] Remove one 'Additional Bug' from README --- README.rst | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.rst b/README.rst index 050ea06..4f066e0 100644 --- a/README.rst +++ b/README.rst @@ -22,9 +22,11 @@ https://github.com/brownan/Minecraft-Overviewer/wiki/Documentation To contact the developers and other users, go to the site at the top of this README, or go to #overviewer on irc.freenode.net. -## Configurable North changes +Configurable North changes +========================== -### Additional Features +Additional Features +------------------- A configurable north direction! I'm not a big fan of north pointing to the bottom left corner of the screen, and I bet you aren't either. So here you go! @@ -33,7 +35,8 @@ north_direction in the settings file. Valid options are 'lower-left', 'upper-left', 'upper-right', and 'lower-right'. I'm partial to 'upper-right', myself. -### Additional Bugs +Additional Bugs +--------------- With very small testing maps, the upper-right direction seems to exacerbate an existing bug that will not let you click on a sign in some cases. This is a @@ -41,16 +44,13 @@ bang-head-on-desk sort of thing, I should be able to find it but I cannot. Somehow it does seem to work fine on my actual SMP map, but there is a good chance that is just luck. -Some blocks don't rotate properly in the upper-left and lower-right directions. -This is an easy fix, it just takes time. And besides, the upper-right is what -you are really looking for, anyway. Probably. - It's slow! One of the rotation methods is very, very inefficient. It needs to be rewritten, but I don't seem to be smart enough to do it right now. I must investigate! On my SMP map the initial render took 30% longer vs the original. Be warned. -### Upstream +Upstream +-------- Once I've squashed some bugs and cleaned up the code a bit, I will see about getting this included in the official Minecraft Overviewer. From 6e3a732eb855977c6ed7ebf4bfe1808dae5625c3 Mon Sep 17 00:00:00 2001 From: Ryan Rector Date: Wed, 13 Jul 2011 15:10:50 -0600 Subject: [PATCH 26/44] Rotate compass rose when rotating map --- .../{compass.png => compass_lower-left.png} | Bin web_assets/compass_lower-right.png | Bin 0 -> 7067 bytes web_assets/compass_upper-left.png | Bin 0 -> 7086 bytes web_assets/compass_upper-right.png | Bin 0 -> 7075 bytes web_assets/overviewerConfig.js | 2 +- 5 files changed, 1 insertion(+), 1 deletion(-) rename web_assets/{compass.png => compass_lower-left.png} (100%) create mode 100644 web_assets/compass_lower-right.png create mode 100644 web_assets/compass_upper-left.png create mode 100644 web_assets/compass_upper-right.png diff --git a/web_assets/compass.png b/web_assets/compass_lower-left.png similarity index 100% rename from web_assets/compass.png rename to web_assets/compass_lower-left.png diff --git a/web_assets/compass_lower-right.png b/web_assets/compass_lower-right.png new file mode 100644 index 0000000000000000000000000000000000000000..8990e7ee563b66f6baa6e134e5b4286f1a9897d7 GIT binary patch literal 7067 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 TSuMrDz`)??>gTe~DWM4fGX%S; literal 0 HcmV?d00001 diff --git a/web_assets/compass_upper-left.png b/web_assets/compass_upper-left.png new file mode 100644 index 0000000000000000000000000000000000000000..60e12d7abb3ae90b26c65a050a27f3eb47677fb5 GIT binary patch literal 7086 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 literal 0 HcmV?d00001 diff --git a/web_assets/overviewerConfig.js b/web_assets/overviewerConfig.js index 22097d0..6a39735 100644 --- a/web_assets/overviewerConfig.js +++ b/web_assets/overviewerConfig.js @@ -14,7 +14,7 @@ var overviewerConfig = { 'image': { 'defaultMarker': 'signpost.png', 'signMarker': 'signpost_icon.png', - 'compass': 'compass.png', + 'compass': 'compass_{north_direction}.png', 'spawnMarker': 'http://google-maps-icons.googlecode.com/files/home.png', 'queryMarker': 'http://google-maps-icons.googlecode.com/files/regroup.png' }, From 32abe88efd0f6c59a3ac7b785cee45fb6863d46f Mon Sep 17 00:00:00 2001 From: Ryan Rector Date: Wed, 13 Jul 2011 15:29:08 -0600 Subject: [PATCH 27/44] Show actual default as default for north-direction --- sample.settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sample.settings.py b/sample.settings.py index 01fcf2b..cccdac8 100644 --- a/sample.settings.py +++ b/sample.settings.py @@ -156,7 +156,7 @@ if "web_assets_hook" in locals(): ### north_direction ## Make north point somewhere else! ## Valid options are 'lower-left', 'upper-left', 'upper-right', 'upper-left' -## default: upper-right +## default: lower-left ## Type: string ## Example: north_direction = "upper-right" From 284199c557c6dd859ac3314bea4e41bf90a29476 Mon Sep 17 00:00:00 2001 From: Ryan Rector Date: Wed, 13 Jul 2011 15:39:58 -0600 Subject: [PATCH 28/44] More info in README --- README.rst | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/README.rst b/README.rst index 4f066e0..1535941 100644 --- a/README.rst +++ b/README.rst @@ -33,27 +33,31 @@ bottom left corner of the screen, and I bet you aren't either. So here you go! Either use the commandline option --north-direction, or use the option north_direction in the settings file. Valid options are 'lower-left', 'upper-left', 'upper-right', and 'lower-right'. I'm partial to 'upper-right', -myself. +myself. Defaults to 'lower-left' to match official behavior. Additional Bugs --------------- -With very small testing maps, the upper-right direction seems to exacerbate an +Sometimes the upper- and lower-right direction seems to exacerbate an existing bug that will not let you click on a sign in some cases. This is a -bang-head-on-desk sort of thing, I should be able to find it but I cannot. -Somehow it does seem to work fine on my actual SMP map, but there is a good -chance that is just luck. +bang-head-on-desk sort of thing, I should be able to find it but I just haven't +yet. It does seem to work fine on my actual SMP map, for what that's worth. It's slow! One of the rotation methods is very, very inefficient. It needs to be rewritten, but I don't seem to be smart enough to do it right now. I must -investigate! On my SMP map the initial render took 30% longer vs the original. -Be warned. +investigate! On my SMP map the initial render took 30% longer vs the official +Minecraft Overviewer. Totally worth it, though. + +So far only blocks where direction is important (minecart tracks, doors, signs, +etc) rotate. Blocks like grass, stone, gravel, etc do not, however for the most +part it isn't noticeable because they look very similar no matter which +direction we are viewing them from. Upstream -------- -Once I've squashed some bugs and cleaned up the code a bit, I will see about -getting this included in the official Minecraft Overviewer. +Once I've squashed the additional bugs and cleaned up the code a bit, I will +see about getting this included in the official Minecraft Overviewer. Features ======== From 59594ee44225305ba6c54ef92ac79599253c6149 Mon Sep 17 00:00:00 2001 From: Ryan Rector Date: Thu, 14 Jul 2011 16:36:10 -0600 Subject: [PATCH 29/44] Clean up region rotation a bit. --- nbt.py | 25 +++++++------------------ 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/nbt.py b/nbt.py index d5c8e96..994b0e8 100644 --- a/nbt.py +++ b/nbt.py @@ -331,35 +331,24 @@ class MCRFileReader(object): self.openfile() self._chunks = None - self._locations = [] + self._locations = [0]*32*32 self._timestamps = [] # go to the beginning of the file self._file.seek(0) # read chunk location table - locations_append = self._locations.append - locations_index = [] - for x in xrange(32*32): - locations_append(self._read_chunk_location()) - locations_index.append(x) - try: - temp = numpy.reshape(self._locations, (32, 32)) - temp = numpy.rot90(temp, self.get_north_rotations()) - self._locations = numpy.reshape(temp, -1) - except ValueError: - temp = numpy.reshape(locations_index, (32, 32)) - temp = numpy.rot90(temp, self.get_north_rotations()) - temp = numpy.reshape(temp, -1) - ttemp = self._locations[:] - for i, e in enumerate(temp): - self._locations[i] = ttemp[e] + locations_index = numpy.reshape(numpy.rot90(numpy.reshape(range(32*32), + (32, 32)), self.get_north_rotations()), -1) + for i in locations_index: + self._locations[i] = self._read_chunk_location() # read chunk timestamp table timestamp_append = self._timestamps.append for _ in xrange(32*32): timestamp_append(self._read_chunk_timestamp()) - self._timestamps = numpy.reshape(numpy.rot90(numpy.reshape(self._timestamps, (32,32)),self.get_north_rotations()), -1) + self._timestamps = numpy.reshape(numpy.rot90(numpy.reshape( + self._timestamps, (32,32)),self.get_north_rotations()), -1) if closeFile: self.closefile() From 077552b53b0200a7805b6052cba053691bd050e8 Mon Sep 17 00:00:00 2001 From: Ryan Rector Date: Sun, 17 Jul 2011 08:47:57 -0600 Subject: [PATCH 30/44] Cache rotated chunk data This results in a small performance gain for rotations, but still noticeably slower than with no rotation. --- chunk.py | 64 ++++++++++++++++++++++------------------------------- nbt.py | 5 ++--- quadtree.py | 2 +- world.py | 22 ++++++++++++++++++ 4 files changed, 52 insertions(+), 41 deletions(-) diff --git a/chunk.py b/chunk.py index 0921e54..6f8ccba 100644 --- a/chunk.py +++ b/chunk.py @@ -70,21 +70,22 @@ def get_lvldata(world, filename, x, y, retries=2): if not d: raise NoSuchChunk(x,y) return d -def get_blockarray(level, north_direction): +def get_blockarray(level): """Takes the level struct as returned from get_lvldata, and returns the Block array, which just contains all the block ids""" - return numpy.rot90(numpy.frombuffer(level['Blocks'], dtype=numpy.uint8).reshape((16,16,128)), get_north_rotations(north_direction)) + return level['Blocks'] def get_blockarray_fromfile(filename, north_direction): """Same as get_blockarray except takes a filename. This is a shortcut""" + #TODO Update this for configurable-north d = nbt.load_from_region(filename, x, y, north_direction) level = d[1]['Level'] - return get_blockarray(level, north_direction) + return get_blockarray(level) -def get_skylight_array(level, north_direction): +def get_skylight_array(level): """Returns the skylight array. This is 4 bits per block, but it is expanded for you so you may index it normally.""" - skylight = numpy.rot90(numpy.frombuffer(level['SkyLight'], dtype=numpy.uint8).reshape((16,16,64)), get_north_rotations(north_direction)) + skylight = level['SkyLight'] # this array is 2 blocks per byte, so expand it skylight_expanded = numpy.empty((16,16,128), dtype=numpy.uint8) # Even elements get the lower 4 bits @@ -93,36 +94,26 @@ def get_skylight_array(level, north_direction): skylight_expanded[:,:,1::2] = (skylight & 0xF0) >> 4 return skylight_expanded -def get_blocklight_array(level, north_direction): +def get_blocklight_array(level): """Returns the blocklight array. This is 4 bits per block, but it is expanded for you so you may index it normally.""" # expand just like get_skylight_array() - blocklight = numpy.rot90(numpy.frombuffer(level['BlockLight'], dtype=numpy.uint8).reshape((16,16,64)), get_north_rotations(north_direction)) + blocklight = level['BlockLight'] blocklight_expanded = numpy.empty((16,16,128), dtype=numpy.uint8) blocklight_expanded[:,:,::2] = blocklight & 0x0F blocklight_expanded[:,:,1::2] = (blocklight & 0xF0) >> 4 return blocklight_expanded -def get_blockdata_array(level, north_direction): +def get_blockdata_array(level): """Returns the ancillary data from the 'Data' byte array. Data is packed in a similar manner to skylight data""" - return numpy.rot90(numpy.frombuffer(level['Data'], dtype=numpy.uint8).reshape((16,16,64)), get_north_rotations(north_direction)) + return level['Data'] def get_tileentity_data(level): """Returns the TileEntities TAG_List from chunk dat file""" data = level['TileEntities'] return data -def get_north_rotations(north_direction): - if north_direction == "upper-left": - return 1 - elif north_direction == "upper-right": - return 2 - elif north_direction == "lower-right": - return 3 - elif north_direction == "lower-left": - return 0 - # This set holds blocks ids that can be seen through, for occlusion calculations transparent_blocks = set([ 0, 6, 8, 9, 18, 20, 26, 27, 28, 30, 31, 32, 37, 38, 39, 40, 44, 50, 51, 52, 53, 55, 59, 63, 64, 65, 66, 67, @@ -148,14 +139,13 @@ class NoSuchChunk(Exception): pass class ChunkRenderer(object): - def __init__(self, chunkcoords, worldobj, rendermode, queue, north_direction): + def __init__(self, chunkcoords, worldobj, rendermode, queue): """Make a new chunk renderer for the given chunk coordinates. chunkcoors should be a tuple: (chunkX, chunkY) cachedir is a directory to save the resulting chunk images to """ self.queue = queue - self.north_direction = north_direction self.regionfile = worldobj.get_region_path(*chunkcoords) #if not os.path.exists(self.regionfile): @@ -192,21 +182,21 @@ class ChunkRenderer(object): def _load_blocks(self): """Loads and returns the block array""" if not hasattr(self, "_blocks"): - self._blocks = get_blockarray(self._load_level(), self.north_direction) + self._blocks = get_blockarray(self._load_level()) return self._blocks blocks = property(_load_blocks) def _load_skylight(self): """Loads and returns skylight array""" if not hasattr(self, "_skylight"): - self._skylight = get_skylight_array(self.level, self.north_direction) + self._skylight = get_skylight_array(self.level) return self._skylight skylight = property(_load_skylight) def _load_blocklight(self): """Loads and returns blocklight array""" if not hasattr(self, "_blocklight"): - self._blocklight = get_blocklight_array(self.level, self.north_direction) + self._blocklight = get_blocklight_array(self.level) return self._blocklight blocklight = property(_load_blocklight) @@ -215,9 +205,9 @@ class ChunkRenderer(object): chunk_path = self.world.get_region_path(self.chunkX - 1, self.chunkY) try: chunk_data = get_lvldata(self.world,chunk_path, self.chunkX - 1, self.chunkY) - self._left_skylight = get_skylight_array(chunk_data, self.north_direction) - self._left_blocklight = get_blocklight_array(chunk_data, self.north_direction) - self._left_blocks = get_blockarray(chunk_data, self.north_direction) + self._left_skylight = get_skylight_array(chunk_data) + self._left_blocklight = get_blocklight_array(chunk_data) + self._left_blocks = get_blockarray(chunk_data) except NoSuchChunk: self._left_skylight = None self._left_blocklight = None @@ -249,9 +239,9 @@ class ChunkRenderer(object): chunk_path = self.world.get_region_path(self.chunkX, self.chunkY + 1) try: chunk_data = get_lvldata(self.world,chunk_path, self.chunkX, self.chunkY + 1) - self._right_skylight = get_skylight_array(chunk_data, self.north_direction) - self._right_blocklight = get_blocklight_array(chunk_data, self.north_direction) - self._right_blocks = get_blockarray(chunk_data, self.north_direction) + self._right_skylight = get_skylight_array(chunk_data) + self._right_blocklight = get_blocklight_array(chunk_data) + self._right_blocks = get_blockarray(chunk_data) except NoSuchChunk: self._right_skylight = None self._right_blocklight = None @@ -283,9 +273,9 @@ class ChunkRenderer(object): chunk_path = self.world.get_region_path(self.chunkX + 1, self.chunkY) try: chunk_data = get_lvldata(self.world,chunk_path, self.chunkX + 1, self.chunkY) - self._up_right_skylight = get_skylight_array(chunk_data, self.north_direction) - self._up_right_blocklight = get_blocklight_array(chunk_data, self.north_direction) - self._up_right_blocks = get_blockarray(chunk_data, self.north_direction) + self._up_right_skylight = get_skylight_array(chunk_data) + self._up_right_blocklight = get_blocklight_array(chunk_data) + self._up_right_blocks = get_blockarray(chunk_data) except NoSuchChunk: self._up_right_skylight = None self._up_right_blocklight = None @@ -310,9 +300,9 @@ class ChunkRenderer(object): chunk_path = self.world.get_region_path(self.chunkX, self.chunkY - 1) try: chunk_data = get_lvldata(self.world,chunk_path, self.chunkX, self.chunkY - 1) - self._up_left_skylight = get_skylight_array(chunk_data, self.north_direction) - self._up_left_blocklight = get_blocklight_array(chunk_data, self.north_direction) - self._up_left_blocks = get_blockarray(chunk_data, self.north_direction) + self._up_left_skylight = get_skylight_array(chunk_data) + self._up_left_blocklight = get_blocklight_array(chunk_data) + self._up_left_blocks = get_blockarray(chunk_data) except NoSuchChunk: self._up_left_skylight = None self._up_left_blocklight = None @@ -402,7 +392,7 @@ class ChunkRenderer(object): rendered, and blocks are drawn with a color tint depending on their depth.""" - blockData = get_blockdata_array(self.level, self.north_direction) + blockData = get_blockdata_array(self.level) blockData_expanded = numpy.empty((16,16,128), dtype=numpy.uint8) # Even elements get the lower 4 bits blockData_expanded[:,:,::2] = blockData & 0x0F diff --git a/nbt.py b/nbt.py index 994b0e8..b027026 100644 --- a/nbt.py +++ b/nbt.py @@ -210,13 +210,12 @@ class MCRFileReader(object): self._chunks = None def get_north_rotations(self): - #Upper-left and lower-right are swapped from chunk.py rots if self.north_direction == "upper-left": - return 3 + return 1 elif self.north_direction == "upper-right": return 2 elif self.north_direction == "lower-right": - return 1 + return 3 elif self.north_direction == "lower-left": return 0 diff --git a/quadtree.py b/quadtree.py index fe5002f..ceb543e 100644 --- a/quadtree.py +++ b/quadtree.py @@ -469,7 +469,7 @@ class QuadtreeGen(object): # draw the chunk! try: - a = chunk.ChunkRenderer((chunkx, chunky), world, rendermode, poi_queue, self.north_direction) + a = chunk.ChunkRenderer((chunkx, chunky), world, rendermode, poi_queue) a.chunk_render(tileimg, xpos, ypos, None) except chunk.ChunkCorrupt: # an error was already printed diff --git a/world.py b/world.py index b5ce5a0..aa1509d 100644 --- a/world.py +++ b/world.py @@ -153,6 +153,18 @@ class World(object): data = nbt.read_all() level = data[1]['Level'] chunk_data = level + chunk_data['Blocks'] = numpy.rot90(numpy.frombuffer( + level['Blocks'], dtype=numpy.uint8).reshape((16,16,128)), + self._get_north_rotations()) + chunk_data['Data'] = numpy.rot90(numpy.frombuffer( + level['Data'], dtype=numpy.uint8).reshape((16,16,64)), + self._get_north_rotations()) + chunk_data['SkyLight'] = numpy.rot90(numpy.frombuffer( + level['SkyLight'], dtype=numpy.uint8).reshape((16,16,64)), + self._get_north_rotations()) + chunk_data['BlockLight'] = numpy.rot90(numpy.frombuffer( + level['BlockLight'], dtype=numpy.uint8).reshape((16,16,64)), + self._get_north_rotations()) #chunk_data = {} #chunk_data['skylight'] = chunk.get_skylight_array(level) #chunk_data['blocklight'] = chunk.get_blocklight_array(level) @@ -276,6 +288,16 @@ class World(object): self.findTrueSpawn() + def _get_north_rotations(self): + if self.north_direction == "upper-left": + return 1 + elif self.north_direction == "upper-right": + return 2 + elif self.north_direction == "lower-right": + return 3 + elif self.north_direction == "lower-left": + return 0 + def _iterate_regionfiles(self,regionlist=None): """Returns an iterator of all of the region files, along with their coordinates From b6a2447b9cfbaa43cb246b8c187e236b8d8520e6 Mon Sep 17 00:00:00 2001 From: Ryan Rector Date: Sat, 23 Jul 2011 07:51:55 -0600 Subject: [PATCH 31/44] Fix trapdoor rotations --- textures.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/textures.py b/textures.py index 2713761..e126c9e 100644 --- a/textures.py +++ b/textures.py @@ -1724,8 +1724,8 @@ def convert_data(blockID, data, north_direction): if blockID == 96: # trapdoor #Masked to not clobber opened/closed info if north_direction == 'upper-left': - if (data & 0b0011) == 0: data = data & 0b1100 | 2 - elif (data & 0b0011) == 1: data = data & 0b1100 | 3 + if (data & 0b0011) == 0: data = data & 0b1100 | 3 + elif (data & 0b0011) == 1: data = data & 0b1100 | 2 elif (data & 0b0011) == 2: data = data & 0b1100 | 0 elif (data & 0b0011) == 3: data = data & 0b1100 | 1 elif north_direction == 'upper-right': @@ -1734,8 +1734,8 @@ def convert_data(blockID, data, north_direction): elif (data & 0b0011) == 2: data = data & 0b1100 | 3 elif (data & 0b0011) == 3: data = data & 0b1100 | 2 elif north_direction == 'lower-right': - if (data & 0b0011) == 0: data = data & 0b1100 | 3 - elif (data & 0b0011) == 1: data = data & 0b1100 | 2 + if (data & 0b0011) == 0: data = data & 0b1100 | 2 + elif (data & 0b0011) == 1: data = data & 0b1100 | 3 elif (data & 0b0011) == 2: data = data & 0b1100 | 1 elif (data & 0b0011) == 3: data = data & 0b1100 | 0 From 5afdff09cc921ba94f989c951e36b7710452c0bb Mon Sep 17 00:00:00 2001 From: Ryan Rector Date: Sat, 23 Jul 2011 11:23:14 -0600 Subject: [PATCH 32/44] Really make lower-left the default everywhere --- googlemap.py | 2 +- overviewer.py | 2 +- quadtree.py | 2 +- world.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/googlemap.py b/googlemap.py index 07e082d..a6c6724 100644 --- a/googlemap.py +++ b/googlemap.py @@ -71,7 +71,7 @@ class MapGen(object): self.web_assets_hook = configInfo.get('web_assets_hook', None) self.web_assets_path = configInfo.get('web_assets_path', None) self.bg_color = configInfo.get('bg_color') - self.north_direction = configInfo.get('north_direction', 'upper-right') + self.north_direction = configInfo.get('north_direction', 'lower-left') if not len(quadtrees) > 0: raise ValueError("there must be at least one quadtree to work on") diff --git a/overviewer.py b/overviewer.py index 79550fe..371793b 100755 --- a/overviewer.py +++ b/overviewer.py @@ -224,7 +224,7 @@ def main(): if options.north_direction: north_direction = options.north_direction else: - north_direction = 'upper-right' + north_direction = 'lower-left' logging.getLogger().setLevel( logging.getLogger().level + 10*options.quiet) diff --git a/quadtree.py b/quadtree.py index ceb543e..4f46879 100644 --- a/quadtree.py +++ b/quadtree.py @@ -49,7 +49,7 @@ def iterate_base4(d): return itertools.product(xrange(4), repeat=d) class QuadtreeGen(object): - def __init__(self, worldobj, destdir, bgcolor, depth=None, tiledir=None, forcerender=False, imgformat=None, imgquality=95, optimizeimg=None, rendermode="normal", north_direction='upper-right'): + def __init__(self, worldobj, destdir, bgcolor, depth=None, tiledir=None, forcerender=False, imgformat=None, imgquality=95, optimizeimg=None, rendermode="normal", north_direction='lower-left'): """Generates a quadtree from the world given into the given dest directory diff --git a/world.py b/world.py index aa1509d..0df5c66 100644 --- a/world.py +++ b/world.py @@ -70,7 +70,7 @@ class World(object): mincol = maxcol = minrow = maxrow = 0 def __init__(self, worlddir, useBiomeData=False,regionlist=None, - north_direction="upper-right"): + north_direction="lower-left"): self.worlddir = worlddir self.useBiomeData = useBiomeData self.north_direction = north_direction From 5728bc4051e17f23d316180bed30197b27c293ea Mon Sep 17 00:00:00 2001 From: Ryan Rector Date: Sat, 23 Jul 2011 11:25:17 -0600 Subject: [PATCH 33/44] Rotate biome data --- textures.py | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/textures.py b/textures.py index e126c9e..7ce64ea 100644 --- a/textures.py +++ b/textures.py @@ -1784,8 +1784,25 @@ def getBiomeData(worlddir, chunkX, chunkY): ''' global currentBiomeFile, currentBiomeData + biomeX = chunkX // 32 + biomeY = chunkY // 32 + rots = 0 + if _north == 'upper-right': + biomeX = -biomeX + biomeY = -biomeY + rots = 2 + elif _north == 'lower-right': + temp = biomeX + biomeX = -biomeY + biomeY = temp + rots = 1 + elif _north == 'upper-left': + temp = biomeX + biomeX = biomeY + biomeY = -temp + rots = 3 - biomeFile = "b.%d.%d.biome" % (chunkX // 32, chunkY // 32) + biomeFile = "b.%d.%d.biome" % (biomeX, biomeY) if biomeFile == currentBiomeFile: return currentBiomeData @@ -1795,7 +1812,9 @@ def getBiomeData(worlddir, chunkX, chunkY): # make sure the file size is correct if not len(rawdata) == 512 * 512 * 2: raise Exception("Biome file %s is not valid." % (biomeFile,)) - data = numpy.frombuffer(rawdata, dtype=numpy.dtype(">u2")) + data = numpy.reshape(numpy.rot90(numpy.reshape( + numpy.frombuffer(rawdata, dtype=numpy.dtype(">u2")), + (512,512)),rots), -1) except IOError: data = None pass # no biome data @@ -1878,6 +1897,8 @@ biome_leaf_texture = None specialblockmap = None def generate(north_direction, path=None): + global _north + _north = north_direction global _find_file_local_path _find_file_local_path = path From 4ac5dfc2a8efc69f87a2034bcf7e7721afa801a7 Mon Sep 17 00:00:00 2001 From: Ryan Rector Date: Wed, 3 Aug 2011 14:37:04 -0600 Subject: [PATCH 34/44] Fix regions being shifted by one during rotation This fixes exaggerated tile counts in progress updates, reduces the calls to rendernode (for a small performance increase), and shifts the problem of unclickable signs in UR orientation further away. --- textures.py | 8 ++++---- web_assets/overviewer.js | 16 ++++++++-------- world.py | 16 ++++++++-------- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/textures.py b/textures.py index 7ce64ea..8662ad1 100644 --- a/textures.py +++ b/textures.py @@ -1788,18 +1788,18 @@ def getBiomeData(worlddir, chunkX, chunkY): biomeY = chunkY // 32 rots = 0 if _north == 'upper-right': - biomeX = -biomeX - biomeY = -biomeY + biomeX = -biomeX-1 + biomeY = -biomeY-1 rots = 2 elif _north == 'lower-right': temp = biomeX - biomeX = -biomeY + biomeX = -biomeY-1 biomeY = temp rots = 1 elif _north == 'upper-left': temp = biomeX biomeX = biomeY - biomeY = -temp + biomeY = -temp-1 rots = 3 biomeFile = "b.%d.%d.biome" % (biomeX, biomeY) diff --git a/web_assets/overviewer.js b/web_assets/overviewer.js index 424cdc4..aca2d8a 100644 --- a/web_assets/overviewer.js +++ b/web_assets/overviewer.js @@ -493,16 +493,16 @@ var overviewer = { Math.pow(2, overviewerConfig.map.maxZoom)); if(overviewerConfig.map.north_direction == 'upper-right'){ - x = -x-1+512; - y = -y-1+512; + x = -x-1; + y = -y-1; } else if(overviewerConfig.map.north_direction == 'upper-left'){ temp = x; - x = -y-1+512; + x = -y-1; y = temp; } else if(overviewerConfig.map.north_direction == 'lower-right'){ temp = x; x = y; - y = -temp-1+512; + y = -temp-1; } // This information about where the center column is may change with @@ -578,16 +578,16 @@ var overviewer = { point.z -= 64; if(overviewerConfig.map.north_direction == 'upper-right'){ - point.x = -point.x+512; - point.z = -point.z+512; + point.x = -point.x; + point.z = -point.z; } else if(overviewerConfig.map.north_direction == 'upper-left'){ temp = point.z; - point.z = -point.x+512; + point.z = -point.x; point.x = temp; } else if(overviewerConfig.map.north_direction == 'lower-right'){ temp = point.z; point.z = point.x; - point.x = -temp+512; + point.x = -temp; } return point; diff --git a/world.py b/world.py index 0df5c66..fe35379 100644 --- a/world.py +++ b/world.py @@ -318,16 +318,16 @@ class World(object): x = int(p[1]) y = int(p[2]) if self.north_direction == 'upper-right': - x = -x - y = -y + x = -x-1 + y = -y-1 elif self.north_direction == 'upper-left': temp = x - x = -y + x = -y-1 y = temp elif self.north_direction == 'lower-right': temp = x x = y - y = -temp + y = -temp-1 yield (x, y, join(self.worlddir, 'region', f)) else: logging.warning("Ignore path '%s' in regionlist", f) @@ -339,16 +339,16 @@ class World(object): x = int(p[1]) y = int(p[2]) if self.north_direction == 'upper-right': - x = -x - y = -y + x = -x-1 + y = -y-1 elif self.north_direction == 'upper-left': temp = x - x = -y + x = -y-1 y = temp elif self.north_direction == 'lower-right': temp = x x = y - y = -temp + y = -temp-1 yield (x, y, join(dirpath, f)) def get_save_dir(): From 80664ba4f1ead73fbf88de697407ece50c0f363d Mon Sep 17 00:00:00 2001 From: Ryan Rector Date: Wed, 3 Aug 2011 15:54:38 -0600 Subject: [PATCH 35/44] Update get_blockarray_fromfile for rotation --- chunk.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/chunk.py b/chunk.py index 6f8ccba..908df1f 100644 --- a/chunk.py +++ b/chunk.py @@ -77,10 +77,22 @@ def get_blockarray(level): def get_blockarray_fromfile(filename, north_direction): """Same as get_blockarray except takes a filename. This is a shortcut""" - #TODO Update this for configurable-north d = nbt.load_from_region(filename, x, y, north_direction) level = d[1]['Level'] - return get_blockarray(level) + chunk_data = level + chunk_data['Blocks'] = numpy.rot90(numpy.frombuffer( + level['Blocks'], dtype=numpy.uint8).reshape((16,16,128)), + self._get_north_rotations()) + chunk_data['Data'] = numpy.rot90(numpy.frombuffer( + level['Data'], dtype=numpy.uint8).reshape((16,16,64)), + self._get_north_rotations()) + chunk_data['SkyLight'] = numpy.rot90(numpy.frombuffer( + level['SkyLight'], dtype=numpy.uint8).reshape((16,16,64)), + self._get_north_rotations()) + chunk_data['BlockLight'] = numpy.rot90(numpy.frombuffer( + level['BlockLight'], dtype=numpy.uint8).reshape((16,16,64)), + self._get_north_rotations()) + return get_blockarray(chunk_data) def get_skylight_array(level): """Returns the skylight array. This is 4 bits per block, but it is From 4725b4b12552a8a8d2f711e7dc8fd963d586bc55 Mon Sep 17 00:00:00 2001 From: Ryan Rector Date: Wed, 3 Aug 2011 15:55:51 -0600 Subject: [PATCH 36/44] Fix findTrueSpawn with rotations --- world.py | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/world.py b/world.py index fe35379..1d580d8 100644 --- a/world.py +++ b/world.py @@ -210,9 +210,20 @@ class World(object): ## read spawn info from level.dat data = nbt.load(os.path.join(self.worlddir, "level.dat"))[1] - spawnX = data['Data']['SpawnX'] + disp_spawnX = spawnX = data['Data']['SpawnX'] spawnY = data['Data']['SpawnY'] - spawnZ = data['Data']['SpawnZ'] + disp_spawnZ = spawnZ = data['Data']['SpawnZ'] + if self.north_direction == 'upper-right': + spawnX = -spawnX + spawnZ = -spawnZ + elif self.north_direction == 'upper-left': + temp = spawnX + spawnX = -spawnZ + spawnZ = temp + elif self.north_direction == 'lower-right': + temp = spawnX + spawnX = spawnZ + spawnZ = -temp ## The chunk that holds the spawn location chunkX = spawnX/16 @@ -222,10 +233,7 @@ class World(object): chunkFile = self.get_region_path(chunkX, chunkY) if chunkFile is not None: - #TODO I broke it - - #data = nbt.load_from_region(chunkFile, chunkX, chunkY, self.north_direction)[1] - data = None + data = nbt.load_from_region(chunkFile, chunkX, chunkY, self.north_direction)[1] if data is not None: level = data['Level'] blockArray = numpy.frombuffer(level['Blocks'], dtype=numpy.uint8).reshape((16,16,128)) @@ -240,9 +248,9 @@ class World(object): if spawnY == 128: break - self.POI.append( dict(x=spawnX, y=spawnY, z=spawnZ, + self.POI.append( dict(x=disp_spawnX, y=spawnY, z=disp_spawnZ, msg="Spawn", type="spawn", chunk=(chunkX, chunkY))) - self.spawn = (spawnX, spawnY, spawnZ) + self.spawn = (disp_spawnX, spawnY, disp_spawnZ) def go(self, procs): """Scan the world directory, to fill in From 0035a367a8970b1c9cb58c896e230991db65d8f8 Mon Sep 17 00:00:00 2001 From: Ryan Rector Date: Wed, 3 Aug 2011 22:15:55 -0600 Subject: [PATCH 37/44] Only rotate Block data when asking for block data --- overviewer_core/chunk.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/overviewer_core/chunk.py b/overviewer_core/chunk.py index b6116dd..3920a13 100644 --- a/overviewer_core/chunk.py +++ b/overviewer_core/chunk.py @@ -83,15 +83,6 @@ def get_blockarray_fromfile(filename, north_direction): chunk_data['Blocks'] = numpy.rot90(numpy.frombuffer( level['Blocks'], dtype=numpy.uint8).reshape((16,16,128)), self._get_north_rotations()) - chunk_data['Data'] = numpy.rot90(numpy.frombuffer( - level['Data'], dtype=numpy.uint8).reshape((16,16,64)), - self._get_north_rotations()) - chunk_data['SkyLight'] = numpy.rot90(numpy.frombuffer( - level['SkyLight'], dtype=numpy.uint8).reshape((16,16,64)), - self._get_north_rotations()) - chunk_data['BlockLight'] = numpy.rot90(numpy.frombuffer( - level['BlockLight'], dtype=numpy.uint8).reshape((16,16,64)), - self._get_north_rotations()) return get_blockarray(chunk_data) def get_skylight_array(level): From 9af931b4a3d91364f7afa6161cae8cab6da04366 Mon Sep 17 00:00:00 2001 From: Ryan Rector Date: Wed, 3 Aug 2011 22:47:25 -0600 Subject: [PATCH 38/44] Super performance fixer! Rotations no longer have any significant performance hit. Allocate rotated array once during rotation instead of during every chunk render. --- README.rst | 5 ----- overviewer_core/world.py | 16 ++++++++-------- 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/README.rst b/README.rst index 1535941..bf3e700 100644 --- a/README.rst +++ b/README.rst @@ -43,11 +43,6 @@ existing bug that will not let you click on a sign in some cases. This is a bang-head-on-desk sort of thing, I should be able to find it but I just haven't yet. It does seem to work fine on my actual SMP map, for what that's worth. -It's slow! One of the rotation methods is very, very inefficient. It needs to be -rewritten, but I don't seem to be smart enough to do it right now. I must -investigate! On my SMP map the initial render took 30% longer vs the official -Minecraft Overviewer. Totally worth it, though. - So far only blocks where direction is important (minecart tracks, doors, signs, etc) rotate. Blocks like grass, stone, gravel, etc do not, however for the most part it isn't noticeable because they look very similar no matter which diff --git a/overviewer_core/world.py b/overviewer_core/world.py index 6ca3d86..2bba9dd 100644 --- a/overviewer_core/world.py +++ b/overviewer_core/world.py @@ -153,18 +153,18 @@ class World(object): data = nbt.read_all() level = data[1]['Level'] chunk_data = level - chunk_data['Blocks'] = numpy.rot90(numpy.frombuffer( + chunk_data['Blocks'] = numpy.array(numpy.rot90(numpy.frombuffer( level['Blocks'], dtype=numpy.uint8).reshape((16,16,128)), - self._get_north_rotations()) - chunk_data['Data'] = numpy.rot90(numpy.frombuffer( + self._get_north_rotations())) + chunk_data['Data'] = numpy.array(numpy.rot90(numpy.frombuffer( level['Data'], dtype=numpy.uint8).reshape((16,16,64)), - self._get_north_rotations()) - chunk_data['SkyLight'] = numpy.rot90(numpy.frombuffer( + self._get_north_rotations())) + chunk_data['SkyLight'] = numpy.array(numpy.rot90(numpy.frombuffer( level['SkyLight'], dtype=numpy.uint8).reshape((16,16,64)), - self._get_north_rotations()) - chunk_data['BlockLight'] = numpy.rot90(numpy.frombuffer( + self._get_north_rotations())) + chunk_data['BlockLight'] = numpy.array(numpy.rot90(numpy.frombuffer( level['BlockLight'], dtype=numpy.uint8).reshape((16,16,64)), - self._get_north_rotations()) + self._get_north_rotations())) #chunk_data = {} #chunk_data['skylight'] = chunk.get_skylight_array(level) #chunk_data['blocklight'] = chunk.get_blocklight_array(level) From 1bc0619cd3ead404d7ce8bef7f8d2bd297c4b83d Mon Sep 17 00:00:00 2001 From: Ryan Rector Date: Wed, 3 Aug 2011 23:46:08 -0600 Subject: [PATCH 39/44] Fix potential bed bug. --- overviewer_core/textures.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/overviewer_core/textures.py b/overviewer_core/textures.py index cd9439a..2cf3658 100644 --- a/overviewer_core/textures.py +++ b/overviewer_core/textures.py @@ -1687,7 +1687,7 @@ def convert_data(blockID, data, north_direction): if (data & 0b0011) == 0: data = data & 0b1100 | 1 elif (data & 0b0011) == 1: data = data & 0b1100 | 2 elif (data & 0b0011) == 2: data = data & 0b1100 | 3 - elif (data & 0b0011) == 3: data = data & 0b1100 | 4 + elif (data & 0b0011) == 3: data = data & 0b1100 | 0 elif north_direction == 'upper-right': if (data & 0b0011) == 0: data = data & 0b1100 | 2 elif (data & 0b0011) == 1: data = data & 0b1100 | 3 From ebbc41abed172ea65e15c1c2909fd79b43cb382b Mon Sep 17 00:00:00 2001 From: Ryan Rector Date: Wed, 3 Aug 2011 23:46:53 -0600 Subject: [PATCH 40/44] Rotate pistons for north-direction --- overviewer_core/textures.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/overviewer_core/textures.py b/overviewer_core/textures.py index 2cf3658..b728b2d 100644 --- a/overviewer_core/textures.py +++ b/overviewer_core/textures.py @@ -1698,6 +1698,23 @@ def convert_data(blockID, data, north_direction): elif (data & 0b0011) == 1: data = data & 0b1100 | 0 elif (data & 0b0011) == 2: data = data & 0b1100 | 1 elif (data & 0b0011) == 3: data = data & 0b1100 | 2 + if blockID in (29, 33, 34): # sticky piston, piston, piston extension + #Masked to not clobber block head/foot info + if north_direction == 'upper-left': + if (data & 0b0111) == 2: data = data & 0b1000 | 5 + elif (data & 0b0111) == 3: data = data & 0b1000 | 4 + elif (data & 0b0111) == 4: data = data & 0b1000 | 2 + elif (data & 0b0111) == 5: data = data & 0b1000 | 3 + elif north_direction == 'upper-right': + if (data & 0b0111) == 2: data = data & 0b1000 | 3 + elif (data & 0b0111) == 3: data = data & 0b1000 | 2 + elif (data & 0b0111) == 4: data = data & 0b1000 | 5 + elif (data & 0b0111) == 5: data = data & 0b1000 | 4 + elif north_direction == 'lower-right': + if (data & 0b0111) == 2: data = data & 0b1000 | 4 + elif (data & 0b0111) == 3: data = data & 0b1000 | 5 + elif (data & 0b0111) == 4: data = data & 0b1000 | 3 + elif (data & 0b0111) == 5: data = data & 0b1000 | 2 if blockID in (27, 28, 66): # minetrack: #Masked to not clobber powered rail on/off info #Ascending and flat straight From 34e0cf0e20e3d98225f3730f70bcad33b6717f11 Mon Sep 17 00:00:00 2001 From: Ryan Rector Date: Thu, 4 Aug 2011 10:33:07 -0600 Subject: [PATCH 41/44] Set default north_direction for get_blockarray_fromfile --- overviewer_core/chunk.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/overviewer_core/chunk.py b/overviewer_core/chunk.py index 3920a13..8af6757 100644 --- a/overviewer_core/chunk.py +++ b/overviewer_core/chunk.py @@ -75,7 +75,7 @@ def get_blockarray(level): Block array, which just contains all the block ids""" return level['Blocks'] -def get_blockarray_fromfile(filename, north_direction): +def get_blockarray_fromfile(filename, north_direction='lower-left'): """Same as get_blockarray except takes a filename. This is a shortcut""" d = nbt.load_from_region(filename, x, y, north_direction) level = d[1]['Level'] From 4ee15e8f449deb4ce7bb8a793111f6b8630c6625 Mon Sep 17 00:00:00 2001 From: Ryan Rector Date: Thu, 4 Aug 2011 12:49:49 -0600 Subject: [PATCH 42/44] Code cleanup to simplify upstream merge --- README.rst | 40 ++----- overviewer.py | 2 +- overviewer_core/chunk.py | 2 +- overviewer_core/data/web_assets/overviewer.js | 16 +-- overviewer_core/nbt.py | 12 +- overviewer_core/quadtree.py | 3 +- overviewer_core/rendernode.py | 4 +- overviewer_core/textures.py | 104 +++++++++--------- overviewer_core/world.py | 34 +++--- 9 files changed, 96 insertions(+), 121 deletions(-) diff --git a/README.rst b/README.rst index bf3e700..90282f5 100644 --- a/README.rst +++ b/README.rst @@ -22,38 +22,6 @@ https://github.com/brownan/Minecraft-Overviewer/wiki/Documentation To contact the developers and other users, go to the site at the top of this README, or go to #overviewer on irc.freenode.net. -Configurable North changes -========================== - -Additional Features -------------------- - -A configurable north direction! I'm not a big fan of north pointing to the -bottom left corner of the screen, and I bet you aren't either. So here you go! -Either use the commandline option --north-direction, or use the option -north_direction in the settings file. Valid options are 'lower-left', -'upper-left', 'upper-right', and 'lower-right'. I'm partial to 'upper-right', -myself. Defaults to 'lower-left' to match official behavior. - -Additional Bugs ---------------- - -Sometimes the upper- and lower-right direction seems to exacerbate an -existing bug that will not let you click on a sign in some cases. This is a -bang-head-on-desk sort of thing, I should be able to find it but I just haven't -yet. It does seem to work fine on my actual SMP map, for what that's worth. - -So far only blocks where direction is important (minecart tracks, doors, signs, -etc) rotate. Blocks like grass, stone, gravel, etc do not, however for the most -part it isn't noticeable because they look very similar no matter which -direction we are viewing them from. - -Upstream --------- - -Once I've squashed the additional bugs and cleaned up the code a bit, I will -see about getting this included in the official Minecraft Overviewer. - Features ======== @@ -227,6 +195,10 @@ Options --list-rendermodes List the available render modes, and a short description of each. +--north-direction=NORTH_DIRECTION + Specifies which corner of the screen north will point to. + Valid options are: lower-left, upper-left, upper-right, lower-right. + --settings=PATH Use this option to load settings from a file. The format of this file is given below. @@ -296,6 +268,10 @@ textures_path source. Overviewer looks in here for terrain.png and other textures before it looks anywhere else. +north_direction + Specifies which corner of the screen north will point to. + Valid options are: lower-left, upper-left, upper-right, lower-right. + Viewing the Results ------------------- Within the output directory you will find two things: an index.html file, and a diff --git a/overviewer.py b/overviewer.py index 80f4b1e..27b1ff1 100755 --- a/overviewer.py +++ b/overviewer.py @@ -255,7 +255,7 @@ def main(): # create the quadtrees # TODO chunklist q = [] - qtree_args = {'depth' : options.zoom, 'imgformat' : imgformat, 'imgquality' : options.imgquality, 'optimizeimg' : optimizeimg, 'bgcolor' : bgcolor, 'forcerender' : options.forcerender, 'north_direction' : north_direction} + qtree_args = {'depth' : options.zoom, 'imgformat' : imgformat, 'imgquality' : options.imgquality, 'optimizeimg' : optimizeimg, 'bgcolor' : bgcolor, 'forcerender' : options.forcerender} for rendermode in options.rendermode: if rendermode == 'normal': qtree = quadtree.QuadtreeGen(w, destdir, rendermode=rendermode, tiledir='tiles', **qtree_args) diff --git a/overviewer_core/chunk.py b/overviewer_core/chunk.py index 8af6757..fcba9bd 100644 --- a/overviewer_core/chunk.py +++ b/overviewer_core/chunk.py @@ -55,7 +55,7 @@ def get_lvldata(world, filename, x, y, retries=2): raise NoSuchChunk try: - d = world.load_from_region(filename, x, y) + d = world.load_from_region(filename, x, y) except Exception, e: if retries > 0: # wait a little bit, and try again (up to `retries` times) diff --git a/overviewer_core/data/web_assets/overviewer.js b/overviewer_core/data/web_assets/overviewer.js index eab2dc8..b3f84e3 100644 --- a/overviewer_core/data/web_assets/overviewer.js +++ b/overviewer_core/data/web_assets/overviewer.js @@ -536,13 +536,13 @@ var overviewer = { var perPixel = 1.0 / (overviewerConfig.CONST.tileSize * Math.pow(2, overviewerConfig.map.zoomLevels)); - if(overviewerConfig.map.north_direction == 'upper-right'){ - x = -x-1; - y = -y-1; - } else if(overviewerConfig.map.north_direction == 'upper-left'){ + if(overviewerConfig.map.north_direction == 'upper-left'){ temp = x; x = -y-1; y = temp; + } else if(overviewerConfig.map.north_direction == 'upper-right'){ + x = -x-1; + y = -y-1; } else if(overviewerConfig.map.north_direction == 'lower-right'){ temp = x; x = y; @@ -620,13 +620,13 @@ var overviewer = { point.x += 64; point.z -= 64; - if(overviewerConfig.map.north_direction == 'upper-right'){ - point.x = -point.x; - point.z = -point.z; - } else if(overviewerConfig.map.north_direction == 'upper-left'){ + if(overviewerConfig.map.north_direction == 'upper-left'){ temp = point.z; point.z = -point.x; point.x = temp; + } else if(overviewerConfig.map.north_direction == 'upper-right'){ + point.x = -point.x; + point.z = -point.z; } else if(overviewerConfig.map.north_direction == 'lower-right'){ temp = point.z; point.z = point.x; diff --git a/overviewer_core/nbt.py b/overviewer_core/nbt.py index b027026..41ba4c5 100644 --- a/overviewer_core/nbt.py +++ b/overviewer_core/nbt.py @@ -210,13 +210,13 @@ class MCRFileReader(object): self._chunks = None def get_north_rotations(self): - if self.north_direction == "upper-left": + if self.north_direction == 'upper-left': return 1 - elif self.north_direction == "upper-right": + elif self.north_direction == 'upper-right': return 2 - elif self.north_direction == "lower-right": + elif self.north_direction == 'lower-right': return 3 - elif self.north_direction == "lower-left": + elif self.north_direction == 'lower-left': return 0 def _read_24bit_int(self): @@ -359,10 +359,10 @@ class MCRFileReader(object): load_chunk(), this will wrap x and y into the range [0, 31]. """ x = x % 32 - y = y % 32 + y = y % 32 if self._timestamps is None: self.get_chunk_info() - return self._timestamps[x + y * 32] + return self._timestamps[x + y * 32] def chunkExists(self, x, y): """Determines if a chunk exists without triggering loading of the backend data""" diff --git a/overviewer_core/quadtree.py b/overviewer_core/quadtree.py index 96ddb01..2e77576 100644 --- a/overviewer_core/quadtree.py +++ b/overviewer_core/quadtree.py @@ -49,7 +49,7 @@ def iterate_base4(d): return itertools.product(xrange(4), repeat=d) class QuadtreeGen(object): - def __init__(self, worldobj, destdir, bgcolor, depth=None, tiledir=None, forcerender=False, imgformat=None, imgquality=95, optimizeimg=None, rendermode="normal", north_direction='lower-left'): + def __init__(self, worldobj, destdir, bgcolor, depth=None, tiledir=None, forcerender=False, imgformat=None, imgquality=95, optimizeimg=None, rendermode="normal"): """Generates a quadtree from the world given into the given dest directory @@ -66,7 +66,6 @@ class QuadtreeGen(object): self.optimizeimg = optimizeimg self.bgcolor = bgcolor self.rendermode = rendermode - self.north_direction = north_direction # force png renderformat if we're using an overlay mode if 'overlay' in get_render_mode_inheritance(rendermode): diff --git a/overviewer_core/rendernode.py b/overviewer_core/rendernode.py index 4303e12..016e287 100644 --- a/overviewer_core/rendernode.py +++ b/overviewer_core/rendernode.py @@ -65,8 +65,8 @@ def pool_initializer(rendernode): # make sure textures are generated for this process # and initialize c_overviewer - textures.generate(north_direction=rendernode.options.get('north_direction', None), - path=rendernode.options.get('textures_path', None)) + textures.generate(path=rendernode.options.get('textures_path', None), + north_direction=rendernode.options.get('north_direction', None)) c_overviewer.init_chunk_render() # load biome data in each process, if needed diff --git a/overviewer_core/textures.py b/overviewer_core/textures.py index b728b2d..3025754 100644 --- a/overviewer_core/textures.py +++ b/overviewer_core/textures.py @@ -511,10 +511,10 @@ def generate_texture_tuple(img, blockid): blockmap list and specialblockmap dictionary.""" return (img.convert("RGB"), img.split()[3], generate_opaque_mask(img)) -def generate_special_texture(blockID, data, north_direction): +def generate_special_texture(blockID, data): """Generates a special texture, such as a correctly facing minecraft track""" - data = convert_data(blockID, data, north_direction) + data = convert_data(blockID, data) # blocks need to be handled here (and in chunk.py) @@ -1680,37 +1680,37 @@ def generate_special_texture(blockID, data, north_direction): return None -def convert_data(blockID, data, north_direction): +def convert_data(blockID, data): if blockID == 26: # bed #Masked to not clobber block head/foot info - if north_direction == 'upper-left': + if _north == 'upper-left': if (data & 0b0011) == 0: data = data & 0b1100 | 1 elif (data & 0b0011) == 1: data = data & 0b1100 | 2 elif (data & 0b0011) == 2: data = data & 0b1100 | 3 elif (data & 0b0011) == 3: data = data & 0b1100 | 0 - elif north_direction == 'upper-right': + elif _north == 'upper-right': if (data & 0b0011) == 0: data = data & 0b1100 | 2 elif (data & 0b0011) == 1: data = data & 0b1100 | 3 elif (data & 0b0011) == 2: data = data & 0b1100 | 0 elif (data & 0b0011) == 3: data = data & 0b1100 | 1 - elif north_direction == 'lower-right': + elif _north == 'lower-right': if (data & 0b0011) == 0: data = data & 0b1100 | 3 elif (data & 0b0011) == 1: data = data & 0b1100 | 0 elif (data & 0b0011) == 2: data = data & 0b1100 | 1 elif (data & 0b0011) == 3: data = data & 0b1100 | 2 if blockID in (29, 33, 34): # sticky piston, piston, piston extension #Masked to not clobber block head/foot info - if north_direction == 'upper-left': + if _north == 'upper-left': if (data & 0b0111) == 2: data = data & 0b1000 | 5 elif (data & 0b0111) == 3: data = data & 0b1000 | 4 elif (data & 0b0111) == 4: data = data & 0b1000 | 2 elif (data & 0b0111) == 5: data = data & 0b1000 | 3 - elif north_direction == 'upper-right': + elif _north == 'upper-right': if (data & 0b0111) == 2: data = data & 0b1000 | 3 elif (data & 0b0111) == 3: data = data & 0b1000 | 2 elif (data & 0b0111) == 4: data = data & 0b1000 | 5 elif (data & 0b0111) == 5: data = data & 0b1000 | 4 - elif north_direction == 'lower-right': + elif _north == 'lower-right': if (data & 0b0111) == 2: data = data & 0b1000 | 4 elif (data & 0b0111) == 3: data = data & 0b1000 | 5 elif (data & 0b0111) == 4: data = data & 0b1000 | 3 @@ -1718,19 +1718,19 @@ def convert_data(blockID, data, north_direction): if blockID in (27, 28, 66): # minetrack: #Masked to not clobber powered rail on/off info #Ascending and flat straight - if north_direction == 'upper-left': + if _north == 'upper-left': if (data & 0b0111) == 0: data = data & 0b1000 | 1 elif (data & 0b0111) == 1: data = data & 0b1000 | 0 elif (data & 0b0111) == 2: data = data & 0b1000 | 5 elif (data & 0b0111) == 3: data = data & 0b1000 | 4 elif (data & 0b0111) == 4: data = data & 0b1000 | 2 elif (data & 0b0111) == 5: data = data & 0b1000 | 3 - elif north_direction == 'upper-right': + elif _north == 'upper-right': if (data & 0b0111) == 2: data = data & 0b1000 | 3 elif (data & 0b0111) == 3: data = data & 0b1000 | 2 elif (data & 0b0111) == 4: data = data & 0b1000 | 5 elif (data & 0b0111) == 5: data = data & 0b1000 | 4 - elif north_direction == 'lower-right': + elif _north == 'lower-right': if (data & 0b0111) == 0: data = data & 0b1000 | 1 elif (data & 0b0111) == 1: data = data & 0b1000 | 0 elif (data & 0b0111) == 2: data = data & 0b1000 | 4 @@ -1739,171 +1739,171 @@ def convert_data(blockID, data, north_direction): elif (data & 0b0111) == 5: data = data & 0b1000 | 2 if blockID == 66: # normal minetrack only #Corners - if north_direction == 'upper-left': + if _north == 'upper-left': if data == 6: data = 7 elif data == 7: data = 8 elif data == 8: data = 6 elif data == 9: data = 9 - elif north_direction == 'upper-right': + elif _north == 'upper-right': if data == 6: data = 8 elif data == 7: data = 9 elif data == 8: data = 6 elif data == 9: data = 7 - elif north_direction == 'lower-right': + elif _north == 'lower-right': if data == 6: data = 9 elif data == 7: data = 6 elif data == 8: data = 8 elif data == 9: data = 7 if blockID in (50, 75, 76): # torch, off/on redstone torch - if north_direction == 'upper-left': + if _north == 'upper-left': if data == 1: data = 3 elif data == 2: data = 4 elif data == 3: data = 2 elif data == 4: data = 1 - elif north_direction == 'upper-right': + elif _north == 'upper-right': if data == 1: data = 2 elif data == 2: data = 1 elif data == 3: data = 4 elif data == 4: data = 3 - elif north_direction == 'lower-right': + elif _north == 'lower-right': if data == 1: data = 4 elif data == 2: data = 3 elif data == 3: data = 1 elif data == 4: data = 2 if blockID in (53,67): # wooden and cobblestone stairs. - if north_direction == 'upper-left': + if _north == 'upper-left': if data == 0: data = 2 elif data == 1: data = 3 elif data == 2: data = 1 elif data == 3: data = 0 - elif north_direction == 'upper-right': + elif _north == 'upper-right': if data == 0: data = 1 elif data == 1: data = 0 elif data == 2: data = 3 elif data == 3: data = 2 - elif north_direction == 'lower-right': + elif _north == 'lower-right': if data == 0: data = 3 elif data == 1: data = 2 elif data == 2: data = 0 elif data == 3: data = 1 if blockID in (61, 62, 23): # furnace and burning furnace - if north_direction == 'upper-left': + if _north == 'upper-left': if data == 2: data = 5 elif data == 3: data = 4 elif data == 4: data = 2 elif data == 5: data = 3 - elif north_direction == 'upper-right': + elif _north == 'upper-right': if data == 2: data = 3 elif data == 3: data = 2 elif data == 4: data = 5 elif data == 5: data = 4 - elif north_direction == 'lower-right': + elif _north == 'lower-right': if data == 2: data = 4 elif data == 3: data = 5 elif data == 4: data = 3 elif data == 5: data = 2 if blockID == 63: # signposts - if north_direction == 'upper-left': + if _north == 'upper-left': data = (data + 4) % 16 - elif north_direction == 'upper-right': + elif _north == 'upper-right': data = (data + 8) % 16 - elif north_direction == 'lower-right': + elif _north == 'lower-right': data = (data + 12) % 16 if blockID in (64,71): # wooden/iron door #Masked to not clobber block top/bottom & swung info - if north_direction == 'upper-left': + if _north == 'upper-left': if (data & 0b0011) == 0: data = data & 0b1100 | 1 elif (data & 0b0011) == 1: data = data & 0b1100 | 2 elif (data & 0b0011) == 2: data = data & 0b1100 | 3 elif (data & 0b0011) == 3: data = data & 0b1100 | 0 - elif north_direction == 'upper-right': + elif _north == 'upper-right': if (data & 0b0011) == 0: data = data & 0b1100 | 2 elif (data & 0b0011) == 1: data = data & 0b1100 | 3 elif (data & 0b0011) == 2: data = data & 0b1100 | 0 elif (data & 0b0011) == 3: data = data & 0b1100 | 1 - elif north_direction == 'lower-right': + elif _north == 'lower-right': if (data & 0b0011) == 0: data = data & 0b1100 | 3 elif (data & 0b0011) == 1: data = data & 0b1100 | 0 elif (data & 0b0011) == 2: data = data & 0b1100 | 1 elif (data & 0b0011) == 3: data = data & 0b1100 | 2 if blockID == 65: # ladder - if north_direction == 'upper-left': + if _north == 'upper-left': if data == 2: data = 5 elif data == 3: data = 4 elif data == 4: data = 2 elif data == 5: data = 3 - elif north_direction == 'upper-right': + elif _north == 'upper-right': if data == 2: data = 3 elif data == 3: data = 2 elif data == 4: data = 5 elif data == 5: data = 4 - elif north_direction == 'lower-right': + elif _north == 'lower-right': if data == 2: data = 4 elif data == 3: data = 5 elif data == 4: data = 3 elif data == 5: data = 2 if blockID == 68: # wall sign - if north_direction == 'upper-left': + if _north == 'upper-left': if data == 2: data = 5 elif data == 3: data = 4 elif data == 4: data = 2 elif data == 5: data = 3 - elif north_direction == 'upper-right': + elif _north == 'upper-right': if data == 2: data = 3 elif data == 3: data = 2 elif data == 4: data = 5 elif data == 5: data = 4 - elif north_direction == 'lower-right': + elif _north == 'lower-right': if data == 2: data = 4 elif data == 3: data = 5 elif data == 4: data = 3 elif data == 5: data = 2 if blockID in (86,91): # pumpkins, jack-o-lantern - if north_direction == 'upper-left': + if _north == 'upper-left': if data == 0: data = 1 elif data == 1: data = 2 elif data == 2: data = 3 elif data == 3: data = 0 - elif north_direction == 'upper-right': + elif _north == 'upper-right': if data == 0: data = 2 elif data == 1: data = 3 elif data == 2: data = 0 elif data == 3: data = 1 - elif north_direction == 'lower-right': + elif _north == 'lower-right': if data == 0: data = 3 elif data == 1: data = 0 elif data == 2: data = 1 elif data == 3: data = 2 if blockID in (93, 94): # redstone repeaters, ON and OFF #Masked to not clobber delay info - if north_direction == 'upper-left': + if _north == 'upper-left': if (data & 0b0011) == 0: data = data & 0b1100 | 1 elif (data & 0b0011) == 1: data = data & 0b1100 | 2 elif (data & 0b0011) == 2: data = data & 0b1100 | 3 elif (data & 0b0011) == 3: data = data & 0b1100 | 0 - elif north_direction == 'upper-right': + elif _north == 'upper-right': if (data & 0b0011) == 0: data = data & 0b1100 | 2 elif (data & 0b0011) == 1: data = data & 0b1100 | 3 elif (data & 0b0011) == 2: data = data & 0b1100 | 0 elif (data & 0b0011) == 3: data = data & 0b1100 | 1 - elif north_direction == 'lower-right': + elif _north == 'lower-right': if (data & 0b0011) == 0: data = data & 0b1100 | 3 elif (data & 0b0011) == 1: data = data & 0b1100 | 0 elif (data & 0b0011) == 2: data = data & 0b1100 | 1 elif (data & 0b0011) == 3: data = data & 0b1100 | 2 if blockID == 96: # trapdoor #Masked to not clobber opened/closed info - if north_direction == 'upper-left': + if _north == 'upper-left': if (data & 0b0011) == 0: data = data & 0b1100 | 3 elif (data & 0b0011) == 1: data = data & 0b1100 | 2 elif (data & 0b0011) == 2: data = data & 0b1100 | 0 elif (data & 0b0011) == 3: data = data & 0b1100 | 1 - elif north_direction == 'upper-right': + elif _north == 'upper-right': if (data & 0b0011) == 0: data = data & 0b1100 | 1 elif (data & 0b0011) == 1: data = data & 0b1100 | 0 elif (data & 0b0011) == 2: data = data & 0b1100 | 3 elif (data & 0b0011) == 3: data = data & 0b1100 | 2 - elif north_direction == 'lower-right': + elif _north == 'lower-right': if (data & 0b0011) == 0: data = data & 0b1100 | 2 elif (data & 0b0011) == 1: data = data & 0b1100 | 3 elif (data & 0b0011) == 2: data = data & 0b1100 | 1 @@ -1957,7 +1957,12 @@ def getBiomeData(worlddir, chunkX, chunkY): biomeX = chunkX // 32 biomeY = chunkY // 32 rots = 0 - if _north == 'upper-right': + if _north == 'upper-left': + temp = biomeX + biomeX = biomeY + biomeY = -temp-1 + rots = 3 + elif _north == 'upper-right': biomeX = -biomeX-1 biomeY = -biomeY-1 rots = 2 @@ -1966,11 +1971,6 @@ def getBiomeData(worlddir, chunkX, chunkY): biomeX = -biomeY-1 biomeY = temp rots = 1 - elif _north == 'upper-left': - temp = biomeX - biomeX = biomeY - biomeY = -temp-1 - rots = 3 biomeFile = "b.%d.%d.biome" % (biomeX, biomeY) if biomeFile == currentBiomeFile: @@ -2103,7 +2103,7 @@ def generate(path=None,texture_size=24,bgc = (26,26,26,0),north_direction='lower specialblockmap = {} for blockID in special_blocks: for data in special_map[blockID]: - specialblockmap[(blockID, data)] = generate_special_texture(blockID, data, north_direction) + specialblockmap[(blockID, data)] = generate_special_texture(blockID, data) if texture_size != 24: # rescale biome textures. diff --git a/overviewer_core/world.py b/overviewer_core/world.py index 2bba9dd..2c5ac0e 100644 --- a/overviewer_core/world.py +++ b/overviewer_core/world.py @@ -218,13 +218,13 @@ class World(object): disp_spawnX = spawnX = data['Data']['SpawnX'] spawnY = data['Data']['SpawnY'] disp_spawnZ = spawnZ = data['Data']['SpawnZ'] - if self.north_direction == 'upper-right': - spawnX = -spawnX - spawnZ = -spawnZ - elif self.north_direction == 'upper-left': + if self.north_direction == 'upper-left': temp = spawnX spawnX = -spawnZ spawnZ = temp + elif self.north_direction == 'upper-right': + spawnX = -spawnX + spawnZ = -spawnZ elif self.north_direction == 'lower-right': temp = spawnX spawnX = spawnZ @@ -304,13 +304,13 @@ class World(object): self.findTrueSpawn() def _get_north_rotations(self): - if self.north_direction == "upper-left": + if self.north_direction == 'upper-left': return 1 - elif self.north_direction == "upper-right": + elif self.north_direction == 'upper-right': return 2 - elif self.north_direction == "lower-right": + elif self.north_direction == 'lower-right': return 3 - elif self.north_direction == "lower-left": + elif self.north_direction == 'lower-left': return 0 def _iterate_regionfiles(self,regionlist=None): @@ -331,18 +331,18 @@ class World(object): logging.debug("Using path %s from regionlist", f) x = int(p[1]) y = int(p[2]) - if self.north_direction == 'upper-right': - x = -x-1 - y = -y-1 - elif self.north_direction == 'upper-left': + if self.north_direction == 'upper-left': temp = x x = -y-1 y = temp + elif self.north_direction == 'upper-right': + x = -x-1 + y = -y-1 elif self.north_direction == 'lower-right': temp = x x = y y = -temp-1 - yield (x, y, join(self.worlddir, 'region', f)) + yield (x, y, join(self.worlddir, 'region', f)) else: logging.warning("Ignore path '%s' in regionlist", f) @@ -352,13 +352,13 @@ class World(object): p = f.split(".") x = int(p[1]) y = int(p[2]) - if self.north_direction == 'upper-right': - x = -x-1 - y = -y-1 - elif self.north_direction == 'upper-left': + if self.north_direction == 'upper-left': temp = x x = -y-1 y = temp + elif self.north_direction == 'upper-right': + x = -x-1 + y = -y-1 elif self.north_direction == 'lower-right': temp = x x = y From 77eb608ccb69b53583af2edde187709dab4b5dd1 Mon Sep 17 00:00:00 2001 From: Ryan Rector Date: Sat, 13 Aug 2011 10:50:50 -0600 Subject: [PATCH 43/44] Make get_blockarray_fromfile rotationally self-sufficient --- overviewer_core/chunk.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/overviewer_core/chunk.py b/overviewer_core/chunk.py index fcba9bd..a15c79b 100644 --- a/overviewer_core/chunk.py +++ b/overviewer_core/chunk.py @@ -80,9 +80,17 @@ def get_blockarray_fromfile(filename, north_direction='lower-left'): d = nbt.load_from_region(filename, x, y, north_direction) level = d[1]['Level'] chunk_data = level + rots = 0 + if self.north_direction == 'upper-left': + rots = 1 + elif self.north_direction == 'upper-right': + rots = 2 + elif self.north_direction == 'lower-right': + rots = 3 + chunk_data['Blocks'] = numpy.rot90(numpy.frombuffer( level['Blocks'], dtype=numpy.uint8).reshape((16,16,128)), - self._get_north_rotations()) + rots) return get_blockarray(chunk_data) def get_skylight_array(level): From 02d2ae0e6a1be2c30ee5c50517133977555591a2 Mon Sep 17 00:00:00 2001 From: Ryan Rector Date: Sat, 13 Aug 2011 10:51:43 -0600 Subject: [PATCH 44/44] Fix tile updates for upper-left and lower-right rotations --- overviewer_core/nbt.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/overviewer_core/nbt.py b/overviewer_core/nbt.py index 41ba4c5..895eca9 100644 --- a/overviewer_core/nbt.py +++ b/overviewer_core/nbt.py @@ -211,11 +211,11 @@ class MCRFileReader(object): def get_north_rotations(self): if self.north_direction == 'upper-left': - return 1 + return 3 elif self.north_direction == 'upper-right': return 2 elif self.north_direction == 'lower-right': - return 3 + return 1 elif self.north_direction == 'lower-left': return 0 @@ -338,7 +338,7 @@ class MCRFileReader(object): # read chunk location table locations_index = numpy.reshape(numpy.rot90(numpy.reshape(range(32*32), - (32, 32)), self.get_north_rotations()), -1) + (32, 32)), -self.get_north_rotations()), -1) for i in locations_index: self._locations[i] = self._read_chunk_location()