From 93424c296d1b608ea8dd98613aa840aa8c72e8f1 Mon Sep 17 00:00:00 2001 From: Aaron Griffith Date: Fri, 13 May 2011 12:30:13 -0400 Subject: [PATCH 01/26] textures are now only generated when actual *rendering* is done --- rendernode.py | 14 ++++++++++++-- src/iterate.c | 42 ++++++++++++++++++++---------------------- src/main.c | 8 ++------ src/overviewer.h | 4 ++-- textures.py | 43 +++++++++++++++++++++++++++---------------- 5 files changed, 63 insertions(+), 48 deletions(-) diff --git a/rendernode.py b/rendernode.py index 9dce5cc..f99c087 100644 --- a/rendernode.py +++ b/rendernode.py @@ -26,6 +26,8 @@ import collections import json import logging import util +import textures +import c_overviewer import cPickle import stat import errno @@ -59,14 +61,22 @@ def pool_initializer(rendernode): logging.debug("Child process {0}".format(os.getpid())) #stash the quadtree objects in a global variable after fork() for windows compat. global child_rendernode - child_rendernode = rendernode + child_rendernode = rendernode + + # make sure textures are generated for this process + # and initialize c_overviewer + textures.generate() + c_overviewer.init_chunk_render() + + # load biome data in each process, if needed for quadtree in rendernode.quadtrees: if quadtree.world.useBiomeData: - import textures # make sure we've at least *tried* to load the color arrays in this process... textures.prepareBiomeData(quadtree.world.worlddir) if not textures.grasscolor or not textures.foliagecolor: raise Exception("Can't find grasscolor.png or foliagecolor.png") + # only load biome data once + break #http://docs.python.org/library/itertools.html def roundrobin(iterables): diff --git a/src/iterate.c b/src/iterate.c index 9f9be9a..b55e3df 100644 --- a/src/iterate.c +++ b/src/iterate.c @@ -24,43 +24,41 @@ static PyObject *special_blocks = NULL; static PyObject *specialblockmap = NULL; static PyObject *transparent_blocks = NULL; -int init_chunk_render(void) { +PyObject *init_chunk_render(PyObject *self, PyObject *args) { - /* if blockmap (or any of these) is not NULL, then that means that we've - * somehow called this function twice. error out so we can notice this - * */ - if (blockmap) return 1; + /* this function only needs to be called once, anything more is an + * error... */ + if (blockmap) { + PyErr_SetString(PyExc_RuntimeError, "init_chunk_render should only be called once per process."); + return NULL; + } textures = PyImport_ImportModule("textures"); /* ensure none of these pointers are NULL */ if ((!textures)) { - fprintf(stderr, "\ninit_chunk_render failed to load; textures\n"); - PyErr_Print(); - return 1; + return NULL; } chunk_mod = PyImport_ImportModule("chunk"); /* ensure none of these pointers are NULL */ if ((!chunk_mod)) { - fprintf(stderr, "\ninit_chunk_render failed to load; chunk\n"); - PyErr_Print(); - return 1; + return NULL; } blockmap = PyObject_GetAttrString(textures, "blockmap"); + if (!blockmap) + return NULL; special_blocks = PyObject_GetAttrString(textures, "special_blocks"); + if (!special_blocks) + return NULL; specialblockmap = PyObject_GetAttrString(textures, "specialblockmap"); + if (!specialblockmap) + return NULL; transparent_blocks = PyObject_GetAttrString(chunk_mod, "transparent_blocks"); + if (!transparent_blocks) + return NULL; - /* ensure none of these pointers are NULL */ - if ((!transparent_blocks) || (!blockmap) || (!special_blocks) || (!specialblockmap)) { - fprintf(stderr, "\ninit_chunk_render failed\n"); - PyErr_Print(); - return 1; - } - - return 0; - + Py_RETURN_NONE; } int @@ -310,7 +308,7 @@ chunk_render(PyObject *self, PyObject *args) { PyObject *t = NULL; if (!PyArg_ParseTuple(args, "OOiiO", &state.self, &state.img, &xoff, &yoff, &blockdata_expanded)) - return Py_BuildValue("i", "-1"); + return NULL; /* fill in important modules */ state.textures = textures; @@ -435,7 +433,7 @@ chunk_render(PyObject *self, PyObject *args) { blockid = NULL; } } - } + } /* free up the rendermode info */ rendermode->finish(rm_data, &state); diff --git a/src/main.c b/src/main.c index 4de7eec..19d3612 100644 --- a/src/main.c +++ b/src/main.c @@ -26,6 +26,8 @@ static PyMethodDef COverviewerMethods[] = { {"alpha_over", alpha_over_wrap, METH_VARARGS, "alpha over composite function"}, + {"init_chunk_render", init_chunk_render, METH_VARARGS, + "Initializes the stuffs renderer."}, {"render_loop", chunk_render, METH_VARARGS, "Renders stuffs"}, @@ -53,12 +55,6 @@ initc_overviewer(void) (void)Py_InitModule("c_overviewer", COverviewerMethods); /* for numpy */ import_array(); - - /* initialize some required variables in iterage.c */ - if (init_chunk_render()) { - fprintf(stderr, "failed to init_chunk_render\n"); - exit(1); // TODO better way to indicate error? - } init_endian(); } diff --git a/src/overviewer.h b/src/overviewer.h index 55243a5..5ee89dc 100644 --- a/src/overviewer.h +++ b/src/overviewer.h @@ -26,7 +26,7 @@ // increment this value if you've made a change to the c extesion // and want to force users to rebuild -#define OVERVIEWER_EXTENSION_VERSION 5 +#define OVERVIEWER_EXTENSION_VERSION 6 /* Python PIL, and numpy headers */ #include @@ -76,7 +76,7 @@ typedef struct { PyObject *left_blocks; PyObject *right_blocks; } RenderState; -int init_chunk_render(void); +PyObject *init_chunk_render(PyObject *self, PyObject *args); int is_transparent(unsigned char b); PyObject *chunk_render(PyObject *self, PyObject *args); diff --git a/textures.py b/textures.py index b740420..8a63d3c 100644 --- a/textures.py +++ b/textures.py @@ -112,9 +112,6 @@ def _split_terrain(terrain): return textures -# This maps terainids to 16x16 images -terrain_images = _split_terrain(_get_terrain_image()) - def transform_image(img, blockID=None): """Takes a PIL image and rotates it left 45 degrees and shrinks the y axis by a factor of 2. Returns the resulting image, which will be 24x12 pixels @@ -460,7 +457,6 @@ def _build_blockimages(): while len(allimages) < 256: allimages.append(None) return allimages -blockmap = _build_blockimages() def load_water(): """Evidentially, the water and lava textures are not loaded from any files @@ -482,8 +478,6 @@ def load_water(): lavablock = _build_block(lavatexture, lavatexture) blockmap[10] = lavablock.convert("RGB"), lavablock blockmap[11] = blockmap[10] -load_water() - def generate_special_texture(blockID, data): """Generates a special texture, such as a correctly facing minecraft track""" @@ -1541,11 +1535,6 @@ def tintTexture(im, c): i.putalpha(im.split()[3]); # copy the alpha band back in. assuming RGBA return i -# generate biome (still grayscale) leaf, grass textures -biome_grass_texture = _build_block(terrain_images[0], terrain_images[38], 2) -biome_leaf_texture = _build_block(terrain_images[52], terrain_images[52], 18) - - currentBiomeFile = None currentBiomeData = None grasscolor = None @@ -1665,9 +1654,31 @@ special_map[2] = range(11) + [0x10,] # grass, grass has not ancildata but is # and is harmless for normal maps special_map[18] = range(16) # leaves, birch, normal or pine leaves (not implemented) +# placeholders that are generated in generate() +terrain_images = None +blockmap = None +biome_grass_texture = None +biome_leaf_texture = None +specialblockmap = None -specialblockmap = {} - -for blockID in special_blocks: - for data in special_map[blockID]: - specialblockmap[(blockID, data)] = generate_special_texture(blockID, data) +def generate(): + # This maps terainids to 16x16 images + global terrain_images + terrain_images = _split_terrain(_get_terrain_image()) + + # generate the normal blocks + global blockmap + blockmap = _build_blockimages() + load_water() + + # generate biome (still grayscale) leaf, grass textures + global biome_grass_texture, biome_leaf_texture + biome_grass_texture = _build_block(terrain_images[0], terrain_images[38], 2) + biome_leaf_texture = _build_block(terrain_images[52], terrain_images[52], 18) + + # generate the special blocks + global specialblockmap, special_blocks + specialblockmap = {} + for blockID in special_blocks: + for data in special_map[blockID]: + specialblockmap[(blockID, data)] = generate_special_texture(blockID, data) From dacd45e5d32aab58258a0722495c6b86c9dc0534 Mon Sep 17 00:00:00 2001 From: Aaron Griffith Date: Fri, 13 May 2011 17:21:48 -0400 Subject: [PATCH 02/26] configurable settings.py, web_assets, textures paths --- configParser.py | 11 +++++- googlemap.py | 34 +++++++++++-------- overviewer.py | 4 ++- rendernode.py | 5 +-- textures.py | 29 +++++++++------- .../overviewerConfig.js | 0 6 files changed, 52 insertions(+), 31 deletions(-) rename overviewerConfig.js => web_assets/overviewerConfig.js (100%) diff --git a/configParser.py b/configParser.py index 5b275f0..c779fc2 100644 --- a/configParser.py +++ b/configParser.py @@ -22,6 +22,9 @@ class ConfigOptionParser(object): self.customArgs = ["required", "commandLineOnly", "default", "listify", "listdelim", "choices"] self.requiredArgs = [] + + # add the *very* special config-file path option + self.add_option("--config-file", dest="config_file", help="Specifies a configuration file to load, by name. This file's format is discussed in the README.", metavar="PATH", type="string", commandLineOnly=True) def display_config(self): logging.info("Using the following settings:") @@ -68,8 +71,14 @@ class ConfigOptionParser(object): # if this has a default, use that to seed the globals dict if a.get("default", None): g[n] = a['default'] g['args'] = args - + try: + if options.config_file: + self.configFile = options.config_file + elif os.path.exists(self.configFile): + # warn about automatic loading + logging.warning("Automatic settings.py loading is DEPRECATED, and may be removed in the future. Please use --config-file instead.") + if os.path.exists(self.configFile): execfile(self.configFile, g, l) except NameError, ex: diff --git a/googlemap.py b/googlemap.py index 331a4ea..daf0e0e 100644 --- a/googlemap.py +++ b/googlemap.py @@ -68,6 +68,7 @@ class MapGen(object): self.skipjs = configInfo.get('skipjs', None) 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') if not len(quadtrees) > 0: @@ -81,14 +82,28 @@ class MapGen(object): raise ValueError("all the given quadtrees must have the same destdir and world") self.quadtrees = quadtrees - + def go(self, procs): """Writes out config.js, marker.js, and region.js Copies web assets into the destdir""" zoomlevel = self.p - configpath = os.path.join(util.get_program_path(), "overviewerConfig.js") - config = open(configpath, 'r').read() + bgcolor = (int(self.bg_color[1:3],16), int(self.bg_color[3:5],16), int(self.bg_color[5:7],16), 0) + blank = Image.new("RGBA", (1,1), bgcolor) + # Write a blank image + for quadtree in self.quadtrees: + tileDir = os.path.join(self.destdir, quadtree.tiledir) + if not os.path.exists(tileDir): os.mkdir(tileDir) + blank.save(os.path.join(tileDir, "blank."+quadtree.imgformat)) + + # copy web assets into destdir: + mirror_dir(os.path.join(util.get_program_path(), "web_assets"), self.destdir) + # do the same with the local copy, if we have it + if self.web_assets_path: + mirror_dir(self.web_assets_path, self.destdir) + + # replace the config js stuff + config = open(os.path.join(self.destdir, 'overviewerConfig.js'), 'r').read() config = config.replace( "{minzoom}", str(0)) config = config.replace( @@ -111,18 +126,7 @@ class MapGen(object): with open(os.path.join(self.destdir, "overviewerConfig.js"), 'w') as output: output.write(config) - bgcolor = (int(self.bg_color[1:3],16), int(self.bg_color[3:5],16), int(self.bg_color[5:7],16), 0) - blank = Image.new("RGBA", (1,1), bgcolor) - # Write a blank image - for quadtree in self.quadtrees: - tileDir = os.path.join(self.destdir, quadtree.tiledir) - if not os.path.exists(tileDir): os.mkdir(tileDir) - blank.save(os.path.join(tileDir, "blank."+quadtree.imgformat)) - - # copy web assets into destdir: - mirror_dir(os.path.join(util.get_program_path(), "web_assets"), self.destdir) - - # Add time in index.html + # Add time and version in index.html indexpath = os.path.join(self.destdir, "index.html") index = open(indexpath, 'r').read() diff --git a/overviewer.py b/overviewer.py index bd4ef7b..396cee8 100755 --- a/overviewer.py +++ b/overviewer.py @@ -101,6 +101,8 @@ def main(): parser.add_option("--bg_color", dest="bg_color", help="Configures the background color for the GoogleMap output. Specify in #RRGGBB format", configFileOnly=True, type="string", default="#1A1A1A") parser.add_option("--optimize-img", dest="optimizeimg", help="If using png, perform image file size optimizations on the output. Specify 1 for pngcrush, 2 for pngcrush+optipng+advdef. This may double (or more) render times, but will produce up to 30% smaller images. NOTE: requires corresponding programs in $PATH or %PATH%", configFileOnly=True) parser.add_option("--web-assets-hook", dest="web_assets_hook", help="If provided, run this function after the web assets have been copied, but before actual tile rendering begins. It should accept a QuadtreeGen object as its only argument.", action="store", metavar="SCRIPT", type="function", configFileOnly=True) + parser.add_option("--web-assets-path", dest="web_assets_path", help="Specifies a non-standard web_assets directory to use. Files here will overwrite the default web assets.", metavar="PATH", type="string", configFileOnly=True) + parser.add_option("--textures-path", dest="textures_path", help="Specifies a non-standard textures path, from which terrain.png and other textures are loaded.", metavar="PATH", type="string", configFileOnly=True) parser.add_option("-q", "--quiet", dest="quiet", action="count", default=0, help="Print less output. You can specify this option multiple times.") parser.add_option("-v", "--verbose", dest="verbose", action="count", default=0, help="Print more output. You can specify this option multiple times.") parser.add_option("--skip-js", dest="skipjs", action="store_true", help="Don't output marker.js or regions.js") @@ -242,7 +244,7 @@ def main(): qtree.go(options.procs) # create the distributed render - r = rendernode.RenderNode(q) + r = rendernode.RenderNode(q, options) # write out the map and web assets m = googlemap.MapGen(q, configInfo=options) diff --git a/rendernode.py b/rendernode.py index f99c087..6f6c76e 100644 --- a/rendernode.py +++ b/rendernode.py @@ -65,7 +65,7 @@ def pool_initializer(rendernode): # make sure textures are generated for this process # and initialize c_overviewer - textures.generate() + textures.generate(path=rendernode.options.get('textures_path', None)) c_overviewer.init_chunk_render() # load biome data in each process, if needed @@ -94,12 +94,13 @@ def roundrobin(iterables): class RenderNode(object): - def __init__(self, quadtrees): + def __init__(self, quadtrees, options): """Distributes the rendering of a list of quadtrees.""" if not len(quadtrees) > 0: raise ValueError("there must be at least one quadtree to work on") + self.options = options self.quadtrees = quadtrees #bind an index value to the quadtree so we can find it again #and figure out which worlds are where diff --git a/textures.py b/textures.py index 8a63d3c..611353a 100644 --- a/textures.py +++ b/textures.py @@ -26,11 +26,14 @@ from PIL import Image, ImageEnhance, ImageOps, ImageDraw import util import composite +_find_file_local_path = None def _find_file(filename, mode="rb"): """Searches for the given file and returns an open handle to it. This searches the following locations in this order: + * the textures_path given in the config file (if present) * The program dir (same dir as this file) + * The program dir / textures * On Darwin, in /Applications/Minecraft * Inside minecraft.jar, which is looked for at these locations @@ -38,14 +41,21 @@ def _find_file(filename, mode="rb"): * On Darwin, at $HOME/Library/Application Support/minecraft/bin/minecraft.jar * at $HOME/.minecraft/bin/minecraft.jar - * The current working directory - * The program dir / textures - """ + + if _find_file_local_path: + path = os.path.join(_find_file_local_path, filename) + if os.path.exists(path): + return open(path, mode) + programdir = util.get_program_path() path = os.path.join(programdir, filename) if os.path.exists(path): return open(path, mode) + + path = os.path.join(programdir, "textures", filename) + if os.path.exists(path): + return open(path, mode) if sys.platform == "darwin": path = os.path.join("/Applications/Minecraft", filename) @@ -73,14 +83,6 @@ def _find_file(filename, mode="rb"): except (KeyError, IOError): pass - path = filename - if os.path.exists(path): - return open(path, mode) - - path = os.path.join(programdir, "textures", filename) - if os.path.exists(path): - return open(path, mode) - raise IOError("Could not find the file {0}. Is Minecraft installed? If so, I couldn't find the minecraft.jar file.".format(filename)) def _load_image(filename): @@ -1661,7 +1663,10 @@ biome_grass_texture = None biome_leaf_texture = None specialblockmap = None -def generate(): +def generate(path=None): + global _find_file_local_path + _find_file_local_path = path + # This maps terainids to 16x16 images global terrain_images terrain_images = _split_terrain(_get_terrain_image()) diff --git a/overviewerConfig.js b/web_assets/overviewerConfig.js similarity index 100% rename from overviewerConfig.js rename to web_assets/overviewerConfig.js From 21de6222ca80a251e4fc92514e7278e7442b8e84 Mon Sep 17 00:00:00 2001 From: Aaron Griffith Date: Fri, 13 May 2011 18:22:00 -0400 Subject: [PATCH 03/26] renamed --config-file to --settings, updated README with new options --- README.rst | 23 +++++++++++++++++++++-- configParser.py | 4 ++-- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/README.rst b/README.rst index 1d0e9ab..11f9f10 100644 --- a/README.rst +++ b/README.rst @@ -191,11 +191,18 @@ Options --list-rendermodes List the available render modes, and a short description of each. +--settings=PATH + Use this option to load settings from a file. The format of this file is + given below. + Settings -------- -You can optionally store settings in a file named settings.py. It is a regular -python script, so you can use any python functions or modules you want. + +You can optionally store settings in a file named settings.py (or really, +anything you want). It is a regular python script, so you can use any python +functions or modules you want. To use a settings file, use the --settings +command line option. For a sample settings file, look at 'sample.settings.py'. Note that this file is not meant to be used directly, but instead it should be used as a @@ -241,6 +248,18 @@ web_assets_hook This function should accept one argument: a QuadtreeGen object. +web_assets_path + This option lets you provide alternative web assets to use when + rendering. The contents of this folder will be copied into the output folder + during render, and will overwrite any default files already copied by + Overviewer. See the web_assets folder included with Overviewer for the + default assets. + +textures_path + This is like web_assets_path, but instead it provides an alternative texture + source. Overviewer looks in here for terrain.png and other textures before + it looks anywhere else. + Viewing the Results ------------------- Within the output directory you will find two things: an index.html file, and a diff --git a/configParser.py b/configParser.py index c779fc2..01e6a14 100644 --- a/configParser.py +++ b/configParser.py @@ -24,7 +24,7 @@ class ConfigOptionParser(object): self.requiredArgs = [] # add the *very* special config-file path option - self.add_option("--config-file", dest="config_file", help="Specifies a configuration file to load, by name. This file's format is discussed in the README.", metavar="PATH", type="string", commandLineOnly=True) + self.add_option("--settings", dest="config_file", help="Specifies a settings file to load, by name. This file's format is discussed in the README.", metavar="PATH", type="string", commandLineOnly=True) def display_config(self): logging.info("Using the following settings:") @@ -77,7 +77,7 @@ class ConfigOptionParser(object): self.configFile = options.config_file elif os.path.exists(self.configFile): # warn about automatic loading - logging.warning("Automatic settings.py loading is DEPRECATED, and may be removed in the future. Please use --config-file instead.") + logging.warning("Automatic settings.py loading is DEPRECATED, and may be removed in the future. Please use --settings instead.") if os.path.exists(self.configFile): execfile(self.configFile, g, l) From 6dd3ac067c8d635ed91c5366633dcfeacbb62818 Mon Sep 17 00:00:00 2001 From: Aaron Griffith Date: Fri, 13 May 2011 21:30:26 -0400 Subject: [PATCH 04/26] fixed setup.py info to account for overviewerConfig.js move --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 415e1a4..a5baa97 100644 --- a/setup.py +++ b/setup.py @@ -28,7 +28,7 @@ setup_kwargs['cmdclass'] = {} if py2exe is not None: setup_kwargs['console'] = ['overviewer.py'] setup_kwargs['data_files'] = [('textures', ['textures/lava.png', 'textures/water.png', 'textures/fire.png', 'textures/portal.png']), - ('', ['overviewerConfig.js', 'COPYING.txt', 'README.rst']), + ('', ['COPYING.txt', 'README.rst']), ('web_assets', glob.glob('web_assets/*'))] setup_kwargs['zipfile'] = None if platform.system() == 'Windows' and '64bit' in platform.architecture(): From 7c5a809e713184a316d1ab4e2bd2395b731637da Mon Sep 17 00:00:00 2001 From: CounterPillow Date: Fri, 20 May 2011 14:18:05 +0200 Subject: [PATCH 05/26] Added maptypes to live hash updating, implemented some checks to catch incomplete hashes --- web_assets/overviewer.js | 64 +++++++++++++++++++++++++++------------- 1 file changed, 43 insertions(+), 21 deletions(-) diff --git a/web_assets/overviewer.js b/web_assets/overviewer.js index 4cbfc11..ae8b0b7 100644 --- a/web_assets/overviewer.js +++ b/web_assets/overviewer.js @@ -225,27 +225,28 @@ var overviewer = { overviewer.collections.mapTypes[i].name, overviewer.collections.mapTypes[i]); } - - // Make the link again whenever the map changes + + // Jump to the hash if given + overviewer.util.initHash(); + + // Add live hash update listeners + // Note: It is important to add them after jumping to the hash + google.maps.event.addListener(overviewer.map, 'dragend', function() { + overviewer.util.updateHash(); + }); + + google.maps.event.addListener(overviewer.map, 'zoom_changed', function() { + overviewer.util.updateHash(); + }); + + // Make the link again whenever the map changes google.maps.event.addListener(overviewer.map, 'maptypeid_changed', function() { $('#'+overviewerConfig.CONST.mapDivId).css( 'background-color', overviewer.util.getMapTypeBackgroundColor( overviewer.map.getMapTypeId())); + //smuggled this one in here for maptypeid hash generation --CounterPillow + overviewer.util.updateHash(); }); - - // Add live hash update listener - google.maps.event.addListener(overviewer.map, 'dragend', function() { - overviewer.util.updateHash(); - }); - google.maps.event.addListener(overviewer.map, 'zoom_changed', function() { - overviewer.util.updateHash(); - }); - // Jump to the hash if given - overviewer.util.initHash(); - - - // We can now set the map to use the 'coordinate' map type - overviewer.map.setMapTypeId(overviewer.util.getDefaultMapTypeId()); }, /** * Read through overviewer.collections.markerDatas and create Marker @@ -782,6 +783,8 @@ var overviewer = { 'initHash': function() { if(window.location.hash.split("/").length > 1) { overviewer.util.goToHash(); + // Clean up the hash. + overviewer.util.updateHash(); // Add a marker indicating the user-supplied position var coordinates = overviewer.util.fromLatLngToWorld(overviewer.map.getCenter().lat(), overviewer.map.getCenter().lng()); @@ -791,14 +794,15 @@ var overviewer = { 'y': coordinates.y, 'z': coordinates.z, 'type': 'querypos'}]); - } + } }, - 'setHash': function(x, y, z, zoom) { - window.location.replace("#/" + Math.floor(x) + "/" + Math.floor(y) + "/" + Math.floor(z) + "/" + zoom); + 'setHash': function(x, y, z, zoom, maptype) { + window.location.replace("#/" + Math.floor(x) + "/" + Math.floor(y) + "/" + Math.floor(z) + "/" + zoom + "/" + maptype); }, 'updateHash': function() { var coordinates = overviewer.util.fromLatLngToWorld(overviewer.map.getCenter().lat(), overviewer.map.getCenter().lng()); var zoom = overviewer.map.getZoom(); + var maptype = overviewer.map.getMapTypeId(); if (zoom == overviewerConfig.map.maxZoom) { zoom = 'max'; } else if (zoom == overviewerConfig.map.minZoom) { @@ -807,12 +811,22 @@ var overviewer = { // default to (map-update friendly) negative zooms zoom -= overviewerConfig.map.maxZoom; } - overviewer.util.setHash(coordinates.x, coordinates.y, coordinates.z, zoom); + overviewer.util.setHash(coordinates.x, coordinates.y, coordinates.z, zoom, maptype); }, 'goToHash': function() { + // Note: the actual data begins at coords[1], coords[0] is empty. var coords = window.location.hash.split("/"); var latlngcoords = overviewer.util.fromWorldToLatLng(parseInt(coords[1]), parseInt(coords[2]), parseInt(coords[3])); - var zoom = coords[4]; + var zoom; + var maptype = ''; + // The if-statements try to prevent unexpected behaviour when using incomplete hashes, e.g. older links + if (coords.length > 4) { + zoom = coords[4]; + } + if (coords.length > 5) { + maptype = coords[5]; + } + if (zoom == 'max') { zoom = overviewerConfig.map.maxZoom; } else if (zoom == 'min') { @@ -827,6 +841,14 @@ var overviewer = { zoom = overviewerConfig.map.defaultZoom; } } + // If the maptype isn't set, set the default one. + if (maptype == '') { + // We can now set the map to use the 'coordinate' map type + overviewer.map.setMapTypeId(overviewer.util.getDefaultMapTypeId()); + } else { + overviewer.map.setMapTypeId(maptype); + } + overviewer.map.setCenter(latlngcoords); overviewer.map.setZoom(zoom); }, From f82911e04e607894a922d9de9c579f4a80842ba7 Mon Sep 17 00:00:00 2001 From: Aaron Griffith Date: Tue, 24 May 2011 18:22:08 -0400 Subject: [PATCH 06/26] added overview map control (tiny, zoomed out map in corner) (hidden by default) --- web_assets/overviewer.js | 1 + 1 file changed, 1 insertion(+) diff --git a/web_assets/overviewer.js b/web_assets/overviewer.js index 4cbfc11..13bd106 100644 --- a/web_assets/overviewer.js +++ b/web_assets/overviewer.js @@ -194,6 +194,7 @@ var overviewer = { }, mapTypeId: overviewer.util.getDefaultMapTypeId(), streetViewControl: false, + overviewMapControl: true, zoomControl: overviewerConfig.map.controls.zoom, backgroundColor: overviewer.util.getMapTypeBackgroundColor( overviewer.util.getDefaultMapTypeId()) From dc12ba2751e48a82ea610574569f4e1b7fdf2892 Mon Sep 17 00:00:00 2001 From: aheadley Date: Thu, 26 May 2011 16:35:47 -0400 Subject: [PATCH 07/26] fixed issue #374 --- web_assets/overviewerConfig.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web_assets/overviewerConfig.js b/web_assets/overviewerConfig.js index acb4529..ce38479 100644 --- a/web_assets/overviewerConfig.js +++ b/web_assets/overviewerConfig.js @@ -79,7 +79,7 @@ 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 }, /** * Group definitions for objects that are partially selectable (signs and From d3551324b7560f54344ee6691b9b3c3b6e09794c Mon Sep 17 00:00:00 2001 From: Andrew Chin Date: Thu, 26 May 2011 20:44:45 -0400 Subject: [PATCH 08/26] --regionlist should be working now --- overviewer.py | 10 +++++----- quadtree.py | 4 +++- world.py | 13 ++++++++++--- 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/overviewer.py b/overviewer.py index 396cee8..5f3400c 100755 --- a/overviewer.py +++ b/overviewer.py @@ -93,7 +93,7 @@ def main(): parser.add_option("-p", "--processes", dest="procs", help="How many worker processes to start. Default %s" % cpus, default=cpus, action="store", type="int") parser.add_option("-z", "--zoom", dest="zoom", help="Sets the zoom level manually instead of calculating it. This can be useful if you have outlier chunks that make your world too big. This value will make the highest zoom level contain (2**ZOOM)^2 tiles", action="store", type="int", configFileOnly=True) parser.add_option("-d", "--delete", dest="delete", help="Clear all caches. Next time you render your world, it will have to start completely over again. This is probably not a good idea for large worlds. Use this if you change texture packs and want to re-render everything.", action="store_true", commandLineOnly=True) - parser.add_option("--chunklist", dest="chunklist", help="A file containing, on each line, a path to a chunkfile to update. Instead of scanning the world directory for chunks, it will just use this list. Normal caching rules still apply.") + parser.add_option("--regionlist", dest="regionlist", help="A file containing, on each line, a path to a regionlist to update. Instead of scanning the world directory for regions, it will just use this list. Normal caching rules still apply.") parser.add_option("--rendermodes", dest="rendermode", help="Specifies the render types, separated by commas. Use --list-rendermodes to list them all.", type="choice", choices=avail_rendermodes, required=True, default=avail_rendermodes[0], listify=True) parser.add_option("--list-rendermodes", dest="list_rendermodes", action="store_true", help="List available render modes and exit.", commandLineOnly=True) parser.add_option("--imgformat", dest="imgformat", help="The image output format to use. Currently supported: png(default), jpg.", configFileOnly=True ) @@ -189,10 +189,10 @@ def main(): if options.delete: return delete_all(worlddir, destdir) - if options.chunklist: - chunklist = open(options.chunklist, 'r') + if options.regionlist: + regionlist = map(str.strip, open(options.regionlist, 'r')) else: - chunklist = None + regionlist = None if options.imgformat: if options.imgformat not in ('jpg','png'): @@ -221,7 +221,7 @@ def main(): logging.info("Notice: Not using biome data for tinting") # First do world-level preprocessing - w = world.World(worlddir, useBiomeData=useBiomeData) + w = world.World(worlddir, useBiomeData=useBiomeData, regionlist=regionlist) w.go(options.procs) logging.info("Rending the following tilesets: %s", ",".join(options.rendermode)) diff --git a/quadtree.py b/quadtree.py index 688db93..5d240db 100644 --- a/quadtree.py +++ b/quadtree.py @@ -246,7 +246,7 @@ class QuadtreeGen(object): regiony = regiony_ _, _, c, mcr = get_region((regionx, regiony),(None,None,None,None)) - if c is not None and mcr.chunkExists(chunkx,chunky): + if c is not None and mcr.chunkExists(chunkx,chunky): chunklist.append((col, row, chunkx, chunky, c)) return chunklist @@ -427,6 +427,8 @@ class QuadtreeGen(object): for col, row, chunkx, chunky, regionfile in chunks: # check region file mtime first. region,regionMtime = get_region_mtime(regionfile) + if self.world.regionlist and region._filename not in self.world.regionlist: + continue if regionMtime <= tile_mtime: continue diff --git a/world.py b/world.py index 7f7bab1..348aff3 100644 --- a/world.py +++ b/world.py @@ -78,7 +78,8 @@ class World(object): logging.info("Scanning regions") regionfiles = {} self.regions = {} - for x, y, regionfile in self._iterate_regionfiles(): + self.regionlist = regionlist # a list of paths + for x, y, regionfile in self._iterate_regionfiles(): mcr = self.reload_region(regionfile) mcr.get_chunk_info() regionfiles[(x,y)] = (x,y,regionfile,mcr) @@ -277,16 +278,22 @@ class World(object): """Returns an iterator of all of the region files, along with their coordinates + Note: the regionlist here will be used to determinte the size of the + world. + Returns (regionx, regiony, filename)""" join = os.path.join if regionlist is not None: for path in regionlist: - if path.endswith("\n"): - path = path[:-1] + path = path.strip() f = os.path.basename(path) 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)) + else: + logging.warning("Ignore path '%s' in regionlist", f) + else: for path in glob(os.path.join(self.worlddir, 'region') + "/r.*.*.mcr"): dirpath, f = os.path.split(path) From a58f23d128365a56487019d8eb83bf0ec1a949da Mon Sep 17 00:00:00 2001 From: Alejandro Aguilera Date: Sat, 28 May 2011 01:02:33 +0200 Subject: [PATCH 09/26] Fix bad looking waterfalls. --- src/iterate.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/iterate.c b/src/iterate.c index b55e3df..89ae0a2 100644 --- a/src/iterate.c +++ b/src/iterate.c @@ -160,13 +160,13 @@ generate_pseudo_data(RenderState *state, unsigned char ancilData) { return ancilData; } else if (state->block == 9) { /* water */ /* an aditional bit for top is added to the 4 bits of check_adjacent_blocks */ - if ((ancilData == 0) || (ancilData >= 10)) { /* static water, only top, and unkown ancildata values */ + if (ancilData == 0) { /* static water, only top, and unkown ancildata values */ data = 16; return data; /* = 0b10000 */ } else if ((ancilData > 0) && (ancilData < 8)) { /* flowing water */ data = (check_adjacent_blocks(state, x, y, z, state->block) ^ 0x0f) | 0x10; return data; - } else if ((ancilData == 8) || (ancilData == 9)) { /* falling water */ + } else if (ancilData >= 8) { /* falling water */ data = (check_adjacent_blocks(state, x, y, z, state->block) ^ 0x0f); return data; } From e0a3f0c4ce31251db98f94a96e7cc1483f33739b Mon Sep 17 00:00:00 2001 From: Alejandro Aguilera Date: Sat, 28 May 2011 01:08:55 +0200 Subject: [PATCH 10/26] Make seas more like in-game (draw only the top surface). --- src/iterate.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/iterate.c b/src/iterate.c index 89ae0a2..84de2d9 100644 --- a/src/iterate.c +++ b/src/iterate.c @@ -160,8 +160,12 @@ generate_pseudo_data(RenderState *state, unsigned char ancilData) { return ancilData; } else if (state->block == 9) { /* water */ /* an aditional bit for top is added to the 4 bits of check_adjacent_blocks */ - if (ancilData == 0) { /* static water, only top, and unkown ancildata values */ - data = 16; + if (ancilData == 0) { /* static water */ + if ((z != 127) && (getArrayByte3D(state->blocks, x, y, z+1) == 9)) { + data = 0; + } else { + data = 16; + } return data; /* = 0b10000 */ } else if ((ancilData > 0) && (ancilData < 8)) { /* flowing water */ data = (check_adjacent_blocks(state, x, y, z, state->block) ^ 0x0f) | 0x10; From 7ff6a4833b20c31f0c8a5a3b7ce3d9e8c99e1d58 Mon Sep 17 00:00:00 2001 From: Alejandro Aguilera Date: Sun, 29 May 2011 23:31:04 +0200 Subject: [PATCH 11/26] Add another element to the texture tuple with a mask for the lighting. Make needed changes in textures.py. Because some textures have garbage in the alpha channel, this new method has some problems. --- src/iterate.c | 7 +- src/rendermode-cave.c | 4 +- src/rendermode-lighting.c | 6 +- src/rendermode-night.c | 4 +- src/rendermode-normal.c | 2 +- src/rendermode-overlay.c | 2 +- src/rendermode-spawn.c | 4 +- src/rendermodes.h | 2 +- textures.py | 198 ++++++++++++++++---------------------- 9 files changed, 99 insertions(+), 130 deletions(-) diff --git a/src/iterate.c b/src/iterate.c index 84de2d9..e829373 100644 --- a/src/iterate.c +++ b/src/iterate.c @@ -421,14 +421,15 @@ chunk_render(PyObject *self, PyObject *args) { /* if we found a proper texture, render it! */ if (t != NULL && t != Py_None) { - PyObject *src, *mask; + PyObject *src, *mask, *mask_light; src = PyTuple_GetItem(t, 0); mask = PyTuple_GetItem(t, 1); - + mask_light = PyTuple_GetItem(t, 2); + if (mask == Py_None) mask = src; - rendermode->draw(rm_data, &state, src, mask); + rendermode->draw(rm_data, &state, src, mask, mask_light); } } diff --git a/src/rendermode-cave.c b/src/rendermode-cave.c index f8334b2..c5a2e07 100644 --- a/src/rendermode-cave.c +++ b/src/rendermode-cave.c @@ -202,7 +202,7 @@ rendermode_cave_finish(void *data, RenderState *state) { } static void -rendermode_cave_draw(void *data, RenderState *state, PyObject *src, PyObject *mask) { +rendermode_cave_draw(void *data, RenderState *state, PyObject *src, PyObject *mask, PyObject *mask_light) { RenderModeCave* self; int z, r, g, b; self = (RenderModeCave *)data; @@ -211,7 +211,7 @@ rendermode_cave_draw(void *data, RenderState *state, PyObject *src, PyObject *ma r = 0, g = 0, b = 0; /* draw the normal block */ - rendermode_normal.draw(data, state, src, mask); + rendermode_normal.draw(data, state, src, mask, mask_light); /* get the colors and tint and tint */ /* TODO TODO for a nether mode there isn't tinting! */ diff --git a/src/rendermode-lighting.c b/src/rendermode-lighting.c index 83b38d3..e342c7d 100644 --- a/src/rendermode-lighting.c +++ b/src/rendermode-lighting.c @@ -206,19 +206,19 @@ rendermode_lighting_occluded(void *data, RenderState *state) { } static void -rendermode_lighting_draw(void *data, RenderState *state, PyObject *src, PyObject *mask) { +rendermode_lighting_draw(void *data, RenderState *state, PyObject *src, PyObject *mask, PyObject *mask_light) { RenderModeLighting* self; int x, y, z; /* first, chain up */ - rendermode_normal.draw(data, state, src, mask); + rendermode_normal.draw(data, state, src, mask, mask_light); self = (RenderModeLighting *)data; x = state->x, y = state->y, z = state->z; if (is_transparent(state->block)) { /* transparent: do shading on whole block */ - do_shading_with_mask(self, state, x, y, z, mask); + do_shading_with_mask(self, state, x, y, z, mask_light); } else { /* opaque: do per-face shading */ do_shading_with_mask(self, state, x, y, z+1, self->facemasks[0]); diff --git a/src/rendermode-night.c b/src/rendermode-night.c index 46ca7c2..81fc38f 100644 --- a/src/rendermode-night.c +++ b/src/rendermode-night.c @@ -54,9 +54,9 @@ rendermode_night_occluded(void *data, RenderState *state) { } static void -rendermode_night_draw(void *data, RenderState *state, PyObject *src, PyObject *mask) { +rendermode_night_draw(void *data, RenderState *state, PyObject *src, PyObject *mask, PyObject *mask_light) { /* nothing special to do */ - rendermode_lighting.draw(data, state, src, mask); + rendermode_lighting.draw(data, state, src, mask, mask_light); } RenderModeInterface rendermode_night = { diff --git a/src/rendermode-normal.c b/src/rendermode-normal.c index b4118e3..0e06e0e 100644 --- a/src/rendermode-normal.c +++ b/src/rendermode-normal.c @@ -116,7 +116,7 @@ rendermode_normal_occluded(void *data, RenderState *state) { } static void -rendermode_normal_draw(void *data, RenderState *state, PyObject *src, PyObject *mask) { +rendermode_normal_draw(void *data, RenderState *state, PyObject *src, PyObject *mask, PyObject *mask_light) { RenderModeNormal *self = (RenderModeNormal *)data; /* first, check to see if we should use biome-compatible src, mask */ diff --git a/src/rendermode-overlay.c b/src/rendermode-overlay.c index fb4e765..04c87bf 100644 --- a/src/rendermode-overlay.c +++ b/src/rendermode-overlay.c @@ -71,7 +71,7 @@ rendermode_overlay_occluded(void *data, RenderState *state) { } static void -rendermode_overlay_draw(void *data, RenderState *state, PyObject *src, PyObject *mask) { +rendermode_overlay_draw(void *data, RenderState *state, PyObject *src, PyObject *mask, PyObject *mask_light) { RenderModeOverlay *self = (RenderModeOverlay *)data; unsigned char r, g, b, a; PyObject *top_block_py, *block_py; diff --git a/src/rendermode-spawn.c b/src/rendermode-spawn.c index 498ed17..2781a2a 100644 --- a/src/rendermode-spawn.c +++ b/src/rendermode-spawn.c @@ -102,9 +102,9 @@ rendermode_spawn_occluded(void *data, RenderState *state) { } static void -rendermode_spawn_draw(void *data, RenderState *state, PyObject *src, PyObject *mask) { +rendermode_spawn_draw(void *data, RenderState *state, PyObject *src, PyObject *mask, PyObject *mask_light) { /* draw normally */ - rendermode_overlay.draw(data, state, src, mask); + rendermode_overlay.draw(data, state, src, mask, mask_light); } RenderModeInterface rendermode_spawn = { diff --git a/src/rendermodes.h b/src/rendermodes.h index fd0b479..d6de559 100644 --- a/src/rendermodes.h +++ b/src/rendermodes.h @@ -56,7 +56,7 @@ struct _RenderModeInterface { /* returns non-zero to skip rendering this block */ int (*occluded)(void *, RenderState *); /* last two arguments are img and mask, from texture lookup */ - void (*draw)(void *, RenderState *, PyObject *, PyObject *); + void (*draw)(void *, RenderState *, PyObject *, PyObject *, PyObject *); }; /* figures out the render mode to use from the given ChunkRenderer */ diff --git a/textures.py b/textures.py index 611353a..ad54a41 100644 --- a/textures.py +++ b/textures.py @@ -453,7 +453,7 @@ def _build_blockimages(): ## of the block or the texture ID img = _build_block(toptexture, sidetexture, blockID) - allimages.append((img.convert("RGB"), img.split()[3])) + allimages.append(generate_texture_tuple(img, blockID)) # Future block types: while len(allimages) < 256: @@ -472,15 +472,32 @@ def load_water(): watertexture = _load_image("water.png") w1 = _build_block(watertexture, None) - blockmap[9] = w1.convert("RGB"), w1 + blockmap[9] = generate_texture_tuple(w1,9) w2 = _build_block(watertexture, watertexture) - blockmap[8] = w2.convert("RGB"), w2 + blockmap[8] = generate_texture_tuple(w2,8) lavatexture = _load_image("lava.png") lavablock = _build_block(lavatexture, lavatexture) - blockmap[10] = lavablock.convert("RGB"), lavablock + blockmap[10] = generate_texture_tuple(lavablock,10) blockmap[11] = blockmap[10] +def generate_opaque_mask(img): + alpha = img.split()[3] + pixel = alpha.load() + + for x in range(img.size[0]): + for y in range(img.size[1]): + if pixel[x,y] != 0: + pixel[x,y] = 255 + + return alpha + +def generate_texture_tuple(img, blockid): + if blockid in (8,9,79,85): + return (img.convert("RGB"), img.split()[3], generate_opaque_mask(img)) + else: + return (img.convert("RGB"), img.split()[3], img.split()[3]) + def generate_special_texture(blockID, data): """Generates a special texture, such as a correctly facing minecraft track""" #print "%s has ancillary data: %X" %(blockID, data) @@ -496,7 +513,7 @@ def generate_special_texture(blockID, data): if not data & 0x10: colored = tintTexture(biome_grass_texture, (115, 175, 71)) composite.alpha_over(img, colored, (0, 0), colored) - return (img.convert("RGB"), img.split()[3]) + return generate_texture_tuple(img, blockID) if blockID == 6: # saplings @@ -520,7 +537,7 @@ def generate_special_texture(blockID, data): sidetexture = terrain_images[15] img = _build_block(toptexture, sidetexture, blockID) - return (img.convert("RGB"),img.split()[3]) + return generate_texture_tuple(img, blockID) if blockID == 9: # spring water, flowing water and waterfall water @@ -548,9 +565,9 @@ def generate_special_texture(blockID, data): side4 = watertexture # bottom right else: side4 = None - img = _build_full_block(top,side1,side2,side3,side4) + img = _build_full_block(top,None,None,side3,side4) - return (img.convert("RGB"),img.split()[3]) + return generate_texture_tuple(img, blockID) if blockID == 17: # wood: normal, birch and pines @@ -558,21 +575,20 @@ def generate_special_texture(blockID, data): if data == 0: side = terrain_images[20] img = _build_block(top, side, 17) - return (img.convert("RGB"), img.split()[3]) if data == 1: side = terrain_images[116] img = _build_block(top, side, 17) - return (img.convert("RGB"), img.split()[3]) if data == 2: side = terrain_images[117] img = _build_block(top, side, 17) - return (img.convert("RGB"), img.split()[3]) + + return generate_texture_tuple(img, blockID) if blockID == 18: # leaves t = tintTexture(terrain_images[52], (37, 118, 25)) img = _build_block(t, t, 18) - return (img.convert("RGB"), img.split()[3]) + return generate_texture_tuple(img, blockID) if blockID == 26: # bed @@ -618,74 +634,45 @@ def generate_special_texture(blockID, data): top = (top, increment) img = _build_full_block(top, None, None, left_face, right_face) - return (img.convert("RGB"), img.split()[3]) + return generate_texture_tuple(img, blockID) if blockID == 35: # wool if data == 0: # white top = side = terrain_images[64] - img = _build_block(top, side, 35) - return (img.convert("RGB"), img.split()[3]) - if data == 1: # orange + elif data == 1: # orange top = side = terrain_images[210] - img = _build_block(top, side, 35) - return (img.convert("RGB"), img.split()[3]) - if data == 2: # magenta + elif data == 2: # magenta top = side = terrain_images[194] - img = _build_block(top, side, 35) - return (img.convert("RGB"), img.split()[3]) - if data == 3: # light blue + elif data == 3: # light blue top = side = terrain_images[178] - img = _build_block(top, side, 35) - return (img.convert("RGB"), img.split()[3]) - if data == 4: # yellow + elif data == 4: # yellow top = side = terrain_images[162] - img = _build_block(top, side, 35) - return (img.convert("RGB"), img.split()[3]) - if data == 5: # light green + elif data == 5: # light green top = side = terrain_images[146] - img = _build_block(top, side, 35) - return (img.convert("RGB"), img.split()[3]) - if data == 6: # pink + elif data == 6: # pink top = side = terrain_images[130] - img = _build_block(top, side, 35) - return (img.convert("RGB"), img.split()[3]) - if data == 7: # grey + elif data == 7: # grey top = side = terrain_images[114] - img = _build_block(top, side, 35) - return (img.convert("RGB"), img.split()[3]) - if data == 8: # light grey + elif data == 8: # light grey top = side = terrain_images[225] - img = _build_block(top, side, 35) - return (img.convert("RGB"), img.split()[3]) - if data == 9: # cyan + elif data == 9: # cyan top = side = terrain_images[209] - img = _build_block(top, side, 35) - return (img.convert("RGB"), img.split()[3]) - if data == 10: # purple + elif data == 10: # purple top = side = terrain_images[193] - img = _build_block(top, side, 35) - return (img.convert("RGB"), img.split()[3]) - if data == 11: # blue + elif data == 11: # blue top = side = terrain_images[177] - img = _build_block(top, side, 35) - return (img.convert("RGB"), img.split()[3]) - if data == 12: # brown + elif data == 12: # brown top = side = terrain_images[161] - img = _build_block(top, side, 35) - return (img.convert("RGB"), img.split()[3]) - if data == 13: # dark green + elif data == 13: # dark green top = side = terrain_images[145] - img = _build_block(top, side, 35) - return (img.convert("RGB"), img.split()[3]) - if data == 14: # red + elif data == 14: # red top = side = terrain_images[129] - img = _build_block(top, side, 35) - return (img.convert("RGB"), img.split()[3]) - if data == 15: # black + elif data == 15: # black top = side = terrain_images[113] - img = _build_block(top, side, 35) - return (img.convert("RGB"), img.split()[3]) + + img = _build_block(top, side, 35) + return generate_texture_tuple(img, blockID) if blockID in (43,44): # slab and double-slab @@ -693,24 +680,16 @@ def generate_special_texture(blockID, data): if data == 0: # stone slab top = terrain_images[6] side = terrain_images[5] - img = _build_block(top, side, blockID) - return (img.convert("RGB"), img.split()[3]) - if data == 1: # stone slab top = terrain_images[176] side = terrain_images[192] - img = _build_block(top, side, blockID) - return (img.convert("RGB"), img.split()[3]) - if data == 2: # wooden slab top = side = terrain_images[4] - img = _build_block(top, side, blockID) - return (img.convert("RGB"), img.split()[3]) - if data == 3: # cobblestone slab top = side = terrain_images[16] - img = _build_block(top, side, blockID) - return (img.convert("RGB"), img.split()[3]) + + img = _build_block(top, side, blockID) + return generate_texture_tuple(img, blockID) if blockID in (50,75,76): # torch, off redstone torch, on redstone torch @@ -763,7 +742,7 @@ def generate_special_texture(blockID, data): composite.alpha_over(img, small_crop, (6,5)) composite.alpha_over(img, slice, (6,6)) - return (img.convert("RGB"), img.split()[3]) + return generate_texture_tuple(img, blockID) if blockID == 51: # fire @@ -779,7 +758,7 @@ def generate_special_texture(blockID, data): composite.alpha_over(img, side1, (0,6), side1) composite.alpha_over(img, side2, (12,6), side2) - return (img.convert("RGB"), img.split()[3]) + return generate_texture_tuple(img, blockID) if blockID in (53,67): # wooden and cobblestone stairs. @@ -848,7 +827,7 @@ def generate_special_texture(blockID, data): # touch up a (horrible) pixel img.putpixel((18,3),(0,0,0,0)) - return (img.convert("RGB"), img.split()[3]) + return generate_texture_tuple(img, blockID) if blockID == 54: # chests # First to bits of the pseudo data store if it's a single chest @@ -890,7 +869,7 @@ def generate_special_texture(blockID, data): else: img = _build_full_block(top, None, None, back, side) - return (img.convert("RGB"), img.split()[3]) + return generate_texture_tuple(img, blockID) if blockID == 55: # redstone wire @@ -968,7 +947,7 @@ def generate_special_texture(blockID, data): img = _build_full_block(None,side1,side2,None,None,bottom) - return (img.convert("RGB"),img.split()[3]) + return generate_texture_tuple(img, blockID) if blockID == 58: # crafting table @@ -977,7 +956,7 @@ def generate_special_texture(blockID, data): side4 = terrain_images[43+16+1] img = _build_full_block(top, None, None, side3, side4, None, 58) - return (img.convert("RGB"), img.split()[3]) + return generate_texture_tuple(img, blockID) if blockID == 59: # crops @@ -990,7 +969,7 @@ def generate_special_texture(blockID, data): composite.alpha_over(img, crop1, (0,12), crop1) composite.alpha_over(img, crop2, (6,3), crop2) composite.alpha_over(img, crop3, (6,3), crop3) - return (img.convert("RGB"), img.split()[3]) + return generate_texture_tuple(img, blockID) if blockID in (61, 62, 23): #furnace and burning furnace @@ -1015,7 +994,7 @@ def generate_special_texture(blockID, data): else: # in any other direction the front can't be seen img = _build_full_block(top, None, None, side, side) - return (img.convert("RGB"), img.split()[3]) + return generate_texture_tuple(img, blockID) if blockID == 63: # singposts @@ -1058,7 +1037,7 @@ def generate_special_texture(blockID, data): composite.alpha_over(img, post2,(incrementx, -3),post2) composite.alpha_over(img, post, (0,-2), post) - return (img.convert("RGB"), img.split()[3]) + return generate_texture_tuple(img, blockID) if blockID in (64,71): #wooden door, or iron door @@ -1110,10 +1089,11 @@ def generate_special_texture(blockID, data): tex = transform_image_side(raw_door.transpose(Image.FLIP_LEFT_RIGHT)) composite.alpha_over(img, tex, (0,6), tex) - return (img.convert("RGB"), img.split()[3]) + return generate_texture_tuple(img, blockID) if blockID == 65: # ladder + img = Image.new("RGBA", (24,24), (38,92,255,0)) raw_texture = terrain_images[83] #print "ladder is facing: %d" % data if data == 5: @@ -1121,28 +1101,25 @@ def generate_special_texture(blockID, data): # but since ladders can apparently be placed on transparent blocks, we # have to render this thing anyway. same for data == 2 tex = transform_image_side(raw_texture) - img = Image.new("RGBA", (24,24), (38,92,255,0)) composite.alpha_over(img, tex, (0,6), tex) - return (img.convert("RGB"), img.split()[3]) + return generate_texture_tuple(img, blockID) if data == 2: tex = transform_image_side(raw_texture).transpose(Image.FLIP_LEFT_RIGHT) - img = Image.new("RGBA", (24,24), (38,92,255,0)) composite.alpha_over(img, tex, (12,6), tex) - return (img.convert("RGB"), img.split()[3]) + return generate_texture_tuple(img, blockID) if data == 3: tex = transform_image_side(raw_texture).transpose(Image.FLIP_LEFT_RIGHT) - img = Image.new("RGBA", (24,24), (38,92,255,0)) composite.alpha_over(img, tex, (0,0), tex) - return (img.convert("RGB"), img.split()[3]) + return generate_texture_tuple(img, blockID) if data == 4: tex = transform_image_side(raw_texture) - img = Image.new("RGBA", (24,24), (38,92,255,0)) composite.alpha_over(img, tex, (12,0), tex) - return (img.convert("RGB"), img.split()[3]) + return generate_texture_tuple(img, blockID) if blockID in (27, 28, 66): # minetrack: - + img = Image.new("RGBA", (24,24), (38,92,255,0)) + if blockID == 27: # powered rail if data & 0x8 == 0: # unpowered raw_straight = terrain_images[163] @@ -1165,58 +1142,48 @@ def generate_special_texture(blockID, data): ## use transform_image to scale and shear if data == 0: track = transform_image(raw_straight, blockID) + composite.alpha_over(img, track, (0,12), track) elif data == 6: track = transform_image(raw_corner, blockID) + composite.alpha_over(img, track, (0,12), track) elif data == 7: track = transform_image(raw_corner.rotate(270), blockID) + composite.alpha_over(img, track, (0,12), track) elif data == 8: # flip track = transform_image(raw_corner.transpose(Image.FLIP_TOP_BOTTOM).rotate(90), blockID) + composite.alpha_over(img, track, (0,12), track) elif data == 9: track = transform_image(raw_corner.transpose(Image.FLIP_TOP_BOTTOM), blockID) + composite.alpha_over(img, track, (0,12), track) elif data == 1: track = transform_image(raw_straight.rotate(90), blockID) + composite.alpha_over(img, track, (0,12), track) #slopes elif data == 2: # slope going up in +x direction track = transform_image_slope(raw_straight,blockID) track = track.transpose(Image.FLIP_LEFT_RIGHT) - img = Image.new("RGBA", (24,24), (38,92,255,0)) composite.alpha_over(img, track, (2,0), track) # the 2 pixels move is needed to fit with the adjacent tracks - return (img.convert("RGB"), img.split()[3]) elif data == 3: # slope going up in -x direction # tracks are sprites, in this case we are seeing the "side" of # the sprite, so draw a line to make it beautiful. - img = Image.new("RGBA", (24,24), (38,92,255,0)) ImageDraw.Draw(img).line([(11,11),(23,17)],fill=(164,164,164)) # grey from track texture (exterior grey). # the track doesn't start from image corners, be carefull drawing the line! - return (img.convert("RGB"), img.split()[3]) - elif data == 4: # slope going up in -y direction track = transform_image_slope(raw_straight,blockID) - img = Image.new("RGBA", (24,24), (38,92,255,0)) composite.alpha_over(img, track, (0,0), track) - return (img.convert("RGB"), img.split()[3]) - + elif data == 5: # slope going up in +y direction # same as "data == 3" - img = Image.new("RGBA", (24,24), (38,92,255,0)) ImageDraw.Draw(img).line([(1,17),(12,11)],fill=(164,164,164)) - return (img.convert("RGB"), img.split()[3]) - - else: # just in case - track = transform_image(raw_straight, blockID) - - img = Image.new("RGBA", (24,24), (38,92,255,0)) - composite.alpha_over(img, track, (0,12), track) - - return (img.convert("RGB"), img.split()[3]) + return generate_texture_tuple(img, blockID) if blockID == 68: # wall sign @@ -1254,7 +1221,7 @@ def generate_special_texture(blockID, data): composite.alpha_over(img, sign2,(incrementx, 2),sign2) composite.alpha_over(img, sign, (0,3), sign) - return (img.convert("RGB"), img.split()[3]) + return generate_texture_tuple(img, blockID) if blockID == 85: # fences # create needed images for Big stick fence @@ -1349,7 +1316,8 @@ def generate_special_texture(blockID, data): if (data & 0b0100) == 4: composite.alpha_over(img,fence_small_side, pos_bottom_right,fence_small_side) # bottom right - return (img.convert("RGB"),img.split()[3]) + img.save("fence" + str(data) + ".png") + return generate_texture_tuple(img, blockID) if blockID in (86,91): # pumpkins, jack-o-lantern @@ -1367,7 +1335,7 @@ def generate_special_texture(blockID, data): else: # in any other direction the front can't be seen img = _build_full_block(top, None, None, side, side) - return (img.convert("RGB"), img.split()[3]) + return generate_texture_tuple(img, blockID) if blockID == 90: # portal @@ -1383,7 +1351,7 @@ def generate_special_texture(blockID, data): if data in (2,8): composite.alpha_over(img, otherside, (5,4), otherside) - return (img.convert("RGB"), img.split()[3]) + return generate_texture_tuple(img, blockID) if blockID == 92: # cake! (without bites, at the moment) @@ -1408,7 +1376,7 @@ def generate_special_texture(blockID, data): composite.alpha_over(img, otherside, (12,12), otherside) composite.alpha_over(img, top, (0,6), top) - return (img.convert("RGB"), img.split()[3]) + return generate_texture_tuple(img, blockID) if blockID in (93, 94): # redstone repeaters, ON and OFF @@ -1526,7 +1494,7 @@ def generate_special_texture(blockID, data): composite.alpha_over(img, torch, static_torch, torch) composite.alpha_over(img, torch, moving_torch, torch) - return (img.convert("RGB"), img.split()[3]) + return generate_texture_tuple(img, blockID) return None @@ -1612,7 +1580,7 @@ special_map = {} special_map[6] = range(16) # saplings: usual, spruce, birch and future ones (rendered as usual saplings) special_map[9] = range(32) # water: spring,flowing, waterfall, and others (unknown) ancildata values, uses pseudo data -special_map[17] = range(4) # wood: normal, birch and pine +special_map[17] = range(3) # wood: normal, birch and pine special_map[26] = range(12) # bed, orientation special_map[23] = range(6) # dispensers, orientation special_map[27] = range(14) # powered rail, orientation/slope and powered/unpowered From fb0a393c140dbf281ceb48e6a3f43b309245de2f Mon Sep 17 00:00:00 2001 From: Alejandro Aguilera Date: Sun, 29 May 2011 23:46:22 +0200 Subject: [PATCH 12/26] Delete saving image... --- textures.py | 1 - 1 file changed, 1 deletion(-) diff --git a/textures.py b/textures.py index ad54a41..c585885 100644 --- a/textures.py +++ b/textures.py @@ -1316,7 +1316,6 @@ def generate_special_texture(blockID, data): if (data & 0b0100) == 4: composite.alpha_over(img,fence_small_side, pos_bottom_right,fence_small_side) # bottom right - img.save("fence" + str(data) + ".png") return generate_texture_tuple(img, blockID) From 4cc957c16b5b13da8e89da241d80c4fd734f10db Mon Sep 17 00:00:00 2001 From: Alejandro Aguilera Date: Mon, 30 May 2011 00:48:58 +0200 Subject: [PATCH 13/26] Fix problem with fence textures. --- textures.py | 58 ++++++++++++++++++++++++++--------------------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/textures.py b/textures.py index c585885..f632472 100644 --- a/textures.py +++ b/textures.py @@ -1225,24 +1225,26 @@ def generate_special_texture(blockID, data): if blockID == 85: # fences # create needed images for Big stick fence - raw_texture = terrain_images[4] - raw_fence_top = Image.new("RGBA", (16,16), (38,92,255,0)) - raw_fence_side = Image.new("RGBA", (16,16), (38,92,255,0)) - fence_top_mask = Image.new("RGBA", (16,16), (38,92,255,0)) - fence_side_mask = Image.new("RGBA", (16,16), (38,92,255,0)) - - # generate the masks images for textures of the fence - ImageDraw.Draw(fence_top_mask).rectangle((6,6,9,9),outline=(0,0,0),fill=(0,0,0)) - ImageDraw.Draw(fence_side_mask).rectangle((6,1,9,15),outline=(0,0,0),fill=(0,0,0)) - # create textures top and side for fence big stick - composite.alpha_over(raw_fence_top,raw_texture,(0,0),fence_top_mask) - composite.alpha_over(raw_fence_side,raw_texture,(0,0),fence_side_mask) + fence_top = terrain_images[4].copy() + fence_side = terrain_images[4].copy() + + # generate the textures of the fence + ImageDraw.Draw(fence_top).rectangle((0,0,5,15),outline=(0,0,0,0),fill=(0,0,0,0)) + ImageDraw.Draw(fence_top).rectangle((10,0,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) + ImageDraw.Draw(fence_top).rectangle((0,0,15,5),outline=(0,0,0,0),fill=(0,0,0,0)) + ImageDraw.Draw(fence_top).rectangle((0,10,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) + fence_top.save("fence_top.png") + + ImageDraw.Draw(fence_side).rectangle((0,0,15,0),outline=(0,0,0,0),fill=(0,0,0,0)) + ImageDraw.Draw(fence_side).rectangle((0,0,5,15),outline=(0,0,0,0),fill=(0,0,0,0)) + ImageDraw.Draw(fence_side).rectangle((10,0,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) + fence_side.save("fence_side.png") # Create the sides and the top of the big stick - fence_side = transform_image_side(raw_fence_side,85) + fence_side = transform_image_side(fence_side,85) fence_other_side = fence_side.transpose(Image.FLIP_LEFT_RIGHT) - fence_top = transform_image(raw_fence_top,85) + fence_top = transform_image(fence_top,85) # Darken the sides slightly. These methods also affect the alpha layer, # so save them first (we don't want to "darken" the alpha layer making @@ -1262,18 +1264,18 @@ def generate_special_texture(blockID, data): # Now render the small sticks. # Create needed images - raw_fence_small_side = Image.new("RGBA", (16,16), (38,92,255,0)) - fence_small_side_mask = Image.new("RGBA", (16,16), (38,92,255,0)) + fence_small_side = terrain_images[4].copy() # Generate mask - ImageDraw.Draw(fence_small_side_mask).rectangle((10,1,15,3),outline=(0,0,0),fill=(0,0,0)) - ImageDraw.Draw(fence_small_side_mask).rectangle((10,7,15,9),outline=(0,0,0),fill=(0,0,0)) - - # create the texture for the side of small sticks fence - composite.alpha_over(raw_fence_small_side,raw_texture,(0,0),fence_small_side_mask) - + ImageDraw.Draw(fence_small_side).rectangle((0,0,15,0),outline=(0,0,0,0),fill=(0,0,0,0)) + ImageDraw.Draw(fence_small_side).rectangle((0,4,15,6),outline=(0,0,0,0),fill=(0,0,0,0)) + ImageDraw.Draw(fence_small_side).rectangle((0,10,15,16),outline=(0,0,0,0),fill=(0,0,0,0)) + ImageDraw.Draw(fence_small_side).rectangle((0,0,4,15),outline=(0,0,0,0),fill=(0,0,0,0)) + ImageDraw.Draw(fence_small_side).rectangle((11,0,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) + fence_small_side.save("fence_small_side.png") + # Create the sides and the top of the small sticks - fence_small_side = transform_image_side(raw_fence_small_side,85) + fence_small_side = transform_image_side(fence_small_side,85) fence_small_other_side = fence_small_side.transpose(Image.FLIP_LEFT_RIGHT) # Darken the sides slightly. These methods also affect the alpha layer, @@ -1286,18 +1288,16 @@ def generate_special_texture(blockID, data): fence_small_side = ImageEnhance.Brightness(fence_small_side).enhance(0.9) fence_small_side.putalpha(sidealpha) - # Create img to compose the fence - img = Image.new("RGBA", (24,24), (38,92,255,0)) # Position of fence small sticks in img. # These postitions are strange because the small sticks of the # fence are at the very left and at the very right of the 16x16 images - pos_top_left = (-2,0) - pos_top_right = (14,0) - pos_bottom_right = (6,4) - pos_bottom_left = (6,4) + pos_top_left = (2,3) + pos_top_right = (10,3) + pos_bottom_right = (10,7) + pos_bottom_left = (2,7) # +x axis points top right direction # +y axis points bottom right direction From 490e1f26103ef1c77c11e84a2114ace5fde3ec97 Mon Sep 17 00:00:00 2001 From: Alejandro Aguilera Date: Tue, 31 May 2011 15:24:07 +0200 Subject: [PATCH 14/26] Clean and comment. --- textures.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/textures.py b/textures.py index f632472..54c713e 100644 --- a/textures.py +++ b/textures.py @@ -482,21 +482,23 @@ def load_water(): blockmap[11] = blockmap[10] def generate_opaque_mask(img): + """ Takes the alpha channel of the image and generates a mask + (used for lighting the block) that deprecates values of alpha + smallers than 50, and sets every other value to 255. """ + alpha = img.split()[3] pixel = alpha.load() - for x in range(img.size[0]): for y in range(img.size[1]): - if pixel[x,y] != 0: + if pixel[x,y] > 25: pixel[x,y] = 255 return alpha def generate_texture_tuple(img, blockid): - if blockid in (8,9,79,85): - return (img.convert("RGB"), img.split()[3], generate_opaque_mask(img)) - else: - return (img.convert("RGB"), img.split()[3], img.split()[3]) + """ This takes a image and returns the needed tuple for the blockmap + list and specialblockmap dictionary.""" + return (img.convert("RGB"), img.split()[3], generate_opaque_mask(img)) def generate_special_texture(blockID, data): """Generates a special texture, such as a correctly facing minecraft track""" @@ -1234,12 +1236,10 @@ def generate_special_texture(blockID, data): ImageDraw.Draw(fence_top).rectangle((10,0,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) ImageDraw.Draw(fence_top).rectangle((0,0,15,5),outline=(0,0,0,0),fill=(0,0,0,0)) ImageDraw.Draw(fence_top).rectangle((0,10,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) - fence_top.save("fence_top.png") ImageDraw.Draw(fence_side).rectangle((0,0,15,0),outline=(0,0,0,0),fill=(0,0,0,0)) ImageDraw.Draw(fence_side).rectangle((0,0,5,15),outline=(0,0,0,0),fill=(0,0,0,0)) ImageDraw.Draw(fence_side).rectangle((10,0,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) - fence_side.save("fence_side.png") # Create the sides and the top of the big stick fence_side = transform_image_side(fence_side,85) @@ -1272,7 +1272,6 @@ def generate_special_texture(blockID, data): ImageDraw.Draw(fence_small_side).rectangle((0,10,15,16),outline=(0,0,0,0),fill=(0,0,0,0)) ImageDraw.Draw(fence_small_side).rectangle((0,0,4,15),outline=(0,0,0,0),fill=(0,0,0,0)) ImageDraw.Draw(fence_small_side).rectangle((11,0,15,15),outline=(0,0,0,0),fill=(0,0,0,0)) - fence_small_side.save("fence_small_side.png") # Create the sides and the top of the small sticks fence_small_side = transform_image_side(fence_small_side,85) From 87cd001b9130b8a79a59635ab9842c1fd6ce39c3 Mon Sep 17 00:00:00 2001 From: Alejandro Aguilera Date: Tue, 31 May 2011 16:01:54 +0200 Subject: [PATCH 15/26] Add tall grass and dead shrubs. --- chunk.py | 8 ++++---- textures.py | 32 +++++++++++++++++++++++--------- 2 files changed, 27 insertions(+), 13 deletions(-) diff --git a/chunk.py b/chunk.py index 048b650..7eb61ab 100644 --- a/chunk.py +++ b/chunk.py @@ -114,10 +114,10 @@ def get_tileentity_data(level): return data # 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, 37, 38, 39, 40, - 44, 50, 51, 52, 53, 55, 59, 63, 64, 65, 66, 67, 68, 69, - 70, 71, 72, 74, 75, 76, 77, 78, 79, 81, 83, 85, 90, 92, - 93, 94]) +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, + 68, 69, 70, 71, 72, 74, 75, 76, 77, 78, 79, 81, 83, 85, + 90, 92, 93, 94]) # This set holds block ids that are solid blocks solid_blocks = set([1, 2, 3, 4, 5, 7, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, diff --git a/textures.py b/textures.py index 54c713e..c19fc09 100644 --- a/textures.py +++ b/textures.py @@ -257,7 +257,7 @@ def _build_block(top, side, blockID=None): otherside.putalpha(othersidealpha) ## special case for non-block things - if blockID in (37,38,6,39,40,83,30): ## flowers, sapling, mushrooms, reeds, web + if blockID in (31,32,37,38,6,39,40,83,30): ## tall grass, dead shrubs, flowers, sapling, mushrooms, reeds, web # # instead of pasting these blocks at the cube edges, place them in the middle: # and omit the top @@ -410,7 +410,7 @@ def _build_blockimages(): # 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 34, -1, 52, 48, 49,160,144, -1,176, 74, -1, -1, -1, -1, 11, -1, # 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 - -1, -1, -1, -1, -1, 13, 12, 29, 28, 23, 22, -1, -1, 7, 9, 4, + 55, -1, -1, -1, -1, 13, 12, 29, 28, 23, 22, -1, -1, 7, 9, 4, # 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 36, 37, -1, -1, 65, -1, -1, -1, 50, 24, -1, -1, 86, -1, -1, -1, # 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 @@ -427,7 +427,7 @@ def _build_blockimages(): # 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 34, -1, 52, 48, 49,160,144, -1,192, 74, -1, -1,- 1, -1, 11, -1, # 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 - -1, -1, -1, -1, -1, 13, 12, 29, 28, 23, 22, -1, -1, 7, 8, 35, + 55, -1, -1, -1, -1, 13, 12, 29, 28, 23, 22, -1, -1, 7, 8, 35, # 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 36, 37, -1, -1, 65, -1, -1,101, 50, 24, -1, -1, 86, -1, -1, -1, # 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 @@ -496,8 +496,8 @@ def generate_opaque_mask(img): return alpha def generate_texture_tuple(img, blockid): - """ This takes a image and returns the needed tuple for the blockmap - list and specialblockmap dictionary.""" + """ This takes an image and returns the needed tuple for the + blockmap list and specialblockmap dictionary.""" return (img.convert("RGB"), img.split()[3], generate_opaque_mask(img)) def generate_special_texture(blockID, data): @@ -638,7 +638,20 @@ def generate_special_texture(blockID, data): return generate_texture_tuple(img, blockID) - + if blockID == 31: # tall grass + if data == 0: # dead shrub + texture = terrain_images[55] + elif data == 1: # tall grass + texture = terrain_images[39].copy() + texture = tintTexture(texture, (115, 175, 71)) + elif data == 2: # fern + texture = terrain_images[56].copy() + texture = tintTexture(texture, (115, 175, 71)) + + img = _build_block(texture, texture, blockID) + return generate_texture_tuple(img,31) + + if blockID == 35: # wool if data == 0: # white top = side = terrain_images[64] @@ -1567,9 +1580,9 @@ def getBiomeData(worlddir, chunkX, chunkY): # (when adding new blocks here and in generate_special_textures, # please, if possible, keep the ascending order of blockid value) -special_blocks = set([ 2, 6, 9, 17, 18, 26, 23, 27, 28, 35, 43, 44, 50, - 51, 53, 54, 55, 58, 59, 61, 62, 63, 64, 65, 66, 67, - 68, 71, 75, 76, 85, 86, 90, 91, 92, 93, 94]) +special_blocks = set([ 2, 6, 9, 17, 18, 26, 23, 27, 28, 31, 35, 43, 44, + 50, 51, 53, 54, 55, 58, 59, 61, 62, 63, 64, 65, 66, + 67, 68, 71, 75, 76, 85, 86, 90, 91, 92, 93, 94]) # this is a map of special blockIDs to a list of all # possible values for ancillary data that it might have. @@ -1621,6 +1634,7 @@ special_map[2] = range(11) + [0x10,] # grass, grass has not ancildata but is # small fix shows the map as expected, # and is harmless for normal maps special_map[18] = range(16) # leaves, birch, normal or pine leaves (not implemented) +special_map[31] = range(3) # tall grass, dead shrub, fern and tall grass itself # placeholders that are generated in generate() terrain_images = None From fcd29234fffd339fb38afc2db5c6ab95b4de4322 Mon Sep 17 00:00:00 2001 From: Alejandro Aguilera Date: Tue, 31 May 2011 17:03:18 +0200 Subject: [PATCH 16/26] Add trapdoors. --- chunk.py | 2 +- textures.py | 21 ++++++++++++++++++++- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/chunk.py b/chunk.py index 7eb61ab..f8aa1f6 100644 --- a/chunk.py +++ b/chunk.py @@ -117,7 +117,7 @@ def get_tileentity_data(level): 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, 68, 69, 70, 71, 72, 74, 75, 76, 77, 78, 79, 81, 83, 85, - 90, 92, 93, 94]) + 90, 92, 93, 94, 96]) # This set holds block ids that are solid blocks solid_blocks = set([1, 2, 3, 4, 5, 7, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, diff --git a/textures.py b/textures.py index c19fc09..17bce0e 100644 --- a/textures.py +++ b/textures.py @@ -1506,6 +1506,24 @@ def generate_special_texture(blockID, data): composite.alpha_over(img, torch, moving_torch, torch) return generate_texture_tuple(img, blockID) + + + if blockID == 96: # trapdoor + texture = terrain_images[84] + if data & 0x4 == 0x4: # opened trapdoor + if data & 0x3 == 0: # west + img = _build_full_block(None, None, None, None, texture) + if data & 0x3 == 1: # east + img = _build_full_block(None, texture, None, None, None) + if data & 0x3 == 2: # south + img = _build_full_block(None, None, texture, None, None) + if data & 0x3 == 3: # north + img = _build_full_block(None, None, None, texture, None) + + elif data & 0x4 == 0: # closed trapdoor + img = _build_full_block((texture, 9), None, None, texture, texture) + + return generate_texture_tuple(img, blockID) return None @@ -1582,7 +1600,7 @@ def getBiomeData(worlddir, chunkX, chunkY): special_blocks = set([ 2, 6, 9, 17, 18, 26, 23, 27, 28, 31, 35, 43, 44, 50, 51, 53, 54, 55, 58, 59, 61, 62, 63, 64, 65, 66, - 67, 68, 71, 75, 76, 85, 86, 90, 91, 92, 93, 94]) + 67, 68, 71, 75, 76, 85, 86, 90, 91, 92, 93, 94, 96]) # this is a map of special blockIDs to a list of all # possible values for ancillary data that it might have. @@ -1624,6 +1642,7 @@ special_map[91] = range(5) # jack-o-lantern, orientation special_map[92] = range(6) # cake! special_map[93] = range(16) # OFF redstone repeater, orientation and delay (delay not implemented) special_map[94] = range(16) # ON redstone repeater, orientation and delay (delay not implemented) +special_map[96] = range(8) # trapdoor, open, closed, orientation # grass and leaves are graysacle in terrain.png # we treat them as special so we can manually tint them From d95ae73286e12b5b3ee7a034ccf5fcc20f933805 Mon Sep 17 00:00:00 2001 From: Alejandro Aguilera Date: Tue, 31 May 2011 18:21:47 +0200 Subject: [PATCH 17/26] Tall grass is now biome tinted. --- src/iterate.c | 5 ++--- src/overviewer.h | 1 + src/rendermode-normal.c | 26 ++++++++++++++++++++++++-- src/rendermodes.h | 2 +- textures.py | 6 +++++- 5 files changed, 33 insertions(+), 7 deletions(-) diff --git a/src/iterate.c b/src/iterate.c index e829373..d86ecbf 100644 --- a/src/iterate.c +++ b/src/iterate.c @@ -293,7 +293,6 @@ PyObject* chunk_render(PyObject *self, PyObject *args) { RenderState state; - PyObject *blockdata_expanded; int xoff, yoff; PyObject *imgsize, *imgsize0_py, *imgsize1_py; @@ -311,7 +310,7 @@ chunk_render(PyObject *self, PyObject *args) { PyObject *t = NULL; - if (!PyArg_ParseTuple(args, "OOiiO", &state.self, &state.img, &xoff, &yoff, &blockdata_expanded)) + if (!PyArg_ParseTuple(args, "OOiiO", &state.self, &state.img, &xoff, &yoff, &state.blockdata_expanded)) return NULL; /* fill in important modules */ @@ -402,7 +401,7 @@ chunk_render(PyObject *self, PyObject *args) { } else { PyObject *tmp; - unsigned char ancilData = getArrayByte3D(blockdata_expanded, state.x, state.y, state.z); + unsigned char ancilData = getArrayByte3D(state.blockdata_expanded, state.x, state.y, state.z); if ((state.block == 85) || (state.block == 9) || (state.block == 55) || (state.block == 54) || (state.block == 2) || (state.block == 90)) { ancilData = generate_pseudo_data(&state, ancilData); } diff --git a/src/overviewer.h b/src/overviewer.h index 5ee89dc..184cd4e 100644 --- a/src/overviewer.h +++ b/src/overviewer.h @@ -70,6 +70,7 @@ typedef struct { /* the block position and type, and the block array */ int x, y, z; unsigned char block; + PyObject *blockdata_expanded; PyObject *blocks; PyObject *up_left_blocks; PyObject *up_right_blocks; diff --git a/src/rendermode-normal.c b/src/rendermode-normal.c index 0e06e0e..e4247fa 100644 --- a/src/rendermode-normal.c +++ b/src/rendermode-normal.c @@ -56,6 +56,7 @@ rendermode_normal_start(void *data, RenderState *state) { self->leaf_texture = NULL; self->grass_texture = NULL; + self->tall_grass_texture = NULL; self->facemask_top = NULL; } else { @@ -64,6 +65,7 @@ rendermode_normal_start(void *data, RenderState *state) { self->leaf_texture = PyObject_GetAttrString(state->textures, "biome_leaf_texture"); self->grass_texture = PyObject_GetAttrString(state->textures, "biome_grass_texture"); + self->tall_grass_texture = PyObject_GetAttrString(state->textures, "biome_tall_grass_texture"); facemasks_py = PyObject_GetAttrString(state->chunk, "facemasks"); /* borrowed reference, needs to be incref'd if we keep it */ @@ -78,6 +80,7 @@ rendermode_normal_start(void *data, RenderState *state) { self->leaf_texture = NULL; self->grass_texture = NULL; + self->tall_grass_texture = NULL; self->facemask_top = NULL; } @@ -98,6 +101,7 @@ rendermode_normal_finish(void *data, RenderState *state) { Py_XDECREF(self->grasscolor); Py_XDECREF(self->leaf_texture); Py_XDECREF(self->grass_texture); + Py_XDECREF(self->tall_grass_texture); Py_XDECREF(self->facemask_top); } @@ -120,8 +124,17 @@ rendermode_normal_draw(void *data, RenderState *state, PyObject *src, PyObject * RenderModeNormal *self = (RenderModeNormal *)data; /* first, check to see if we should use biome-compatible src, mask */ - if (self->biome_data && state->block == 18) { - src = mask = self->leaf_texture; + if (self->biome_data) { + if (state->block == 18) { + src = mask = self->leaf_texture; + } else if (state->block == 31) { + unsigned char data = getArrayByte3D(state->blockdata_expanded, state->x, state->y, state->z); + if (data == 1) { + src = mask = self->tall_grass_texture; + } else if (data == 2) { + src = mask = self->tall_fern_texture; + } + } } /* draw the block! */ @@ -150,6 +163,15 @@ rendermode_normal_draw(void *data, RenderState *state, PyObject *src, PyObject * color = PySequence_GetItem(self->foliagecolor, index); facemask = mask; break; + case 31: + /* tall grass */ + if ( getArrayByte3D(state->blockdata_expanded, state->x, state->y, state->z) != 0 ) + { /* do not tint dead shrubs */ + color = PySequence_GetItem(self->grasscolor, index); + facemask = mask; + break; + } + break; default: break; }; diff --git a/src/rendermodes.h b/src/rendermodes.h index d6de559..80126a4 100644 --- a/src/rendermodes.h +++ b/src/rendermodes.h @@ -79,7 +79,7 @@ typedef struct { /* grasscolor and foliagecolor lookup tables */ PyObject *grasscolor, *foliagecolor; /* biome-compatible grass/leaf textures */ - PyObject *grass_texture, *leaf_texture; + PyObject *grass_texture, *leaf_texture, *tall_grass_texture, *tall_fern_texture; /* top facemask for grass biome tinting */ PyObject *facemask_top; } RenderModeNormal; diff --git a/textures.py b/textures.py index 17bce0e..02c26c3 100644 --- a/textures.py +++ b/textures.py @@ -1659,6 +1659,8 @@ special_map[31] = range(3) # tall grass, dead shrub, fern and tall grass itself terrain_images = None blockmap = None biome_grass_texture = None +biome_tall_grass_texture = None +biome_tall_fern_texture = None biome_leaf_texture = None specialblockmap = None @@ -1676,9 +1678,11 @@ def generate(path=None): load_water() # generate biome (still grayscale) leaf, grass textures - global biome_grass_texture, biome_leaf_texture + global biome_grass_texture, biome_leaf_texture, biome_tall_grass_texture biome_grass_texture = _build_block(terrain_images[0], terrain_images[38], 2) biome_leaf_texture = _build_block(terrain_images[52], terrain_images[52], 18) + biome_tall_grass_texture = _build_block(terrain_images[39], terrain_images[39], 31) + biome_tall_fern_texture = _build_block(terrain_images[56], terrain_images[56], 31) # generate the special blocks global specialblockmap, special_blocks From 9d4b48b5ae8a388bc833cfc249d5c09841dbddd6 Mon Sep 17 00:00:00 2001 From: Alejandro Aguilera Date: Wed, 1 Jun 2011 00:19:23 +0200 Subject: [PATCH 18/26] Add the agrif's fix for stairs. With 1.6 this is no more a quick fix (I think they are now as slabs are). --- src/rendermode-lighting.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rendermode-lighting.c b/src/rendermode-lighting.c index e342c7d..c38fd1e 100644 --- a/src/rendermode-lighting.c +++ b/src/rendermode-lighting.c @@ -91,7 +91,7 @@ get_lighting_coefficient(RenderModeLighting *self, RenderState *state, /* only do special half-step handling if no authoratative pointer was passed in, which is a sign that we're recursing */ - if (block == 44 && authoratative == NULL) { + if ((block == 44 || block == 53 || block == 67) && authoratative == NULL) { float average_gather = 0.0f; unsigned int average_count = 0; int auth; From 8565919c898a6981e603a60936fceae69eaa9058 Mon Sep 17 00:00:00 2001 From: Aaron Griffith Date: Tue, 31 May 2011 20:05:45 -0400 Subject: [PATCH 19/26] fixed uninitialized texture pointer, and bumped extension version --- src/overviewer.h | 2 +- src/rendermode-normal.c | 3 +++ textures.py | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/overviewer.h b/src/overviewer.h index 184cd4e..49e70fa 100644 --- a/src/overviewer.h +++ b/src/overviewer.h @@ -26,7 +26,7 @@ // increment this value if you've made a change to the c extesion // and want to force users to rebuild -#define OVERVIEWER_EXTENSION_VERSION 6 +#define OVERVIEWER_EXTENSION_VERSION 7 /* Python PIL, and numpy headers */ #include diff --git a/src/rendermode-normal.c b/src/rendermode-normal.c index e4247fa..d975ed9 100644 --- a/src/rendermode-normal.c +++ b/src/rendermode-normal.c @@ -66,6 +66,7 @@ rendermode_normal_start(void *data, RenderState *state) { self->leaf_texture = PyObject_GetAttrString(state->textures, "biome_leaf_texture"); self->grass_texture = PyObject_GetAttrString(state->textures, "biome_grass_texture"); self->tall_grass_texture = PyObject_GetAttrString(state->textures, "biome_tall_grass_texture"); + self->tall_fern_texture = PyObject_GetAttrString(state->textures, "biome_tall_fern_texture"); facemasks_py = PyObject_GetAttrString(state->chunk, "facemasks"); /* borrowed reference, needs to be incref'd if we keep it */ @@ -81,6 +82,7 @@ rendermode_normal_start(void *data, RenderState *state) { self->leaf_texture = NULL; self->grass_texture = NULL; self->tall_grass_texture = NULL; + self->tall_fern_texture = NULL; self->facemask_top = NULL; } @@ -102,6 +104,7 @@ rendermode_normal_finish(void *data, RenderState *state) { Py_XDECREF(self->leaf_texture); Py_XDECREF(self->grass_texture); Py_XDECREF(self->tall_grass_texture); + Py_XDECREF(self->tall_fern_texture); Py_XDECREF(self->facemask_top); } diff --git a/textures.py b/textures.py index 02c26c3..101b933 100644 --- a/textures.py +++ b/textures.py @@ -1678,7 +1678,7 @@ def generate(path=None): load_water() # generate biome (still grayscale) leaf, grass textures - global biome_grass_texture, biome_leaf_texture, biome_tall_grass_texture + global biome_grass_texture, biome_leaf_texture, biome_tall_grass_texture, biome_tall_fern_texture biome_grass_texture = _build_block(terrain_images[0], terrain_images[38], 2) biome_leaf_texture = _build_block(terrain_images[52], terrain_images[52], 18) biome_tall_grass_texture = _build_block(terrain_images[39], terrain_images[39], 31) From 733e816dbc0fd6454c2f1bc13c70abd790933e4b Mon Sep 17 00:00:00 2001 From: Aaron Griffith Date: Tue, 31 May 2011 19:48:07 -0400 Subject: [PATCH 20/26] fixed biome loading order --- world.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/world.py b/world.py index 348aff3..f02098c 100644 --- a/world.py +++ b/world.py @@ -100,9 +100,6 @@ class World(object): logging.error("Sorry, This version of Minecraft-Overviewer only works with the new McRegion chunk format") sys.exit(1) - if self.useBiomeData: - textures.prepareBiomeData(worlddir) - # stores Points Of Interest to be mapped with markers # a list of dictionaries, see below for an example self.POI = [] From e65b77ac579f9e96ccce682725265b25da0a666d Mon Sep 17 00:00:00 2001 From: Alejandro Aguilera Date: Thu, 2 Jun 2011 00:57:43 +0200 Subject: [PATCH 21/26] Fix optimizeimg=2 transparency issue not messing with the color type in optipng. --- optimizeimages.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/optimizeimages.py b/optimizeimages.py index e1699b0..01799e4 100644 --- a/optimizeimages.py +++ b/optimizeimages.py @@ -45,7 +45,8 @@ def optimize_image(imgpath, imgformat, optimizeimg): os.rename(imgpath+".tmp", imgpath) if optimizeimg >= 2: - subprocess.Popen([optipng, imgpath], stderr=subprocess.STDOUT, + # the "-nc" it's needed to no broke the transparency of tiles + subprocess.Popen([optipng, "-nc", imgpath], stderr=subprocess.STDOUT, stdout=subprocess.PIPE).communicate()[0] subprocess.Popen([advdef, "-z4",imgpath], stderr=subprocess.STDOUT, stdout=subprocess.PIPE).communicate()[0] From 5249aca936785e2ecbf37b20311dacbaa26d58bd Mon Sep 17 00:00:00 2001 From: Andrew Chin Date: Wed, 1 Jun 2011 23:15:45 -0400 Subject: [PATCH 22/26] Tweak fromLatLngToWorld to fix issue #381 --- web_assets/overviewer.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web_assets/overviewer.js b/web_assets/overviewer.js index d8673c1..94bb8cc 100644 --- a/web_assets/overviewer.js +++ b/web_assets/overviewer.js @@ -560,8 +560,8 @@ var overviewer = { // Adjust for the fact that we we can't figure out what Y is given // only latitude and longitude, so assume Y=64. - point.x += 64 + 1; - point.z -= 64 + 2; + point.x += 64; + point.z -= 64; return point; }, From f47e7fe4063846da2aa68e62b26027be169b11ed Mon Sep 17 00:00:00 2001 From: Andrew Chin Date: Thu, 2 Jun 2011 13:06:12 -0400 Subject: [PATCH 23/26] Fixed rerenderBlocks contrib script --- contrib/rerenderBlocks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/rerenderBlocks.py b/contrib/rerenderBlocks.py index d0fa0df..eb93871 100644 --- a/contrib/rerenderBlocks.py +++ b/contrib/rerenderBlocks.py @@ -20,7 +20,7 @@ import sys sys.path.insert(0,".") import nbt -from chunk import get_blockarray_fromfile +from chunk import get_blockarray_fromfile, get_blockarray import os import re From 91c3e74ef213203a452cf7a3fac84bb914a84087 Mon Sep 17 00:00:00 2001 From: Andrew Chin Date: Fri, 3 Jun 2011 21:56:04 -0400 Subject: [PATCH 24/26] Updated sample settings file * Causes Overviewer to exit if someone uses the sample settings file directly * Fixed up the regionlist example so it doesn't throw errors --- sample.settings.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/sample.settings.py b/sample.settings.py index 2872506..863b266 100644 --- a/sample.settings.py +++ b/sample.settings.py @@ -50,10 +50,13 @@ zoom = 9 ## Example: Dynamically create regionlist of only regions older than 2 days import os, time +# the following two lines are needed to the lambda to work +globals()['os'] = os +globals()['time'] = time regionDir = os.path.join(args[0], "region") regionFiles = filter(lambda x: x.endswith(".mcr"), os.listdir(regionDir)) def olderThanTwoDays(f): - return time.time() - os.stat(f).st_mtime > (60*60*24*2) + return time.time() - os.stat(os.path.join(args[0], 'region',f)).st_mtime > (60*60*24*2) oldRegionFiles = filter(olderThanTwoDays, regionFiles) with open("regionlist.txt", "w") as f: f.write("\n".join(oldRegionFiles)) @@ -148,3 +151,9 @@ if "web_assets_hook" in locals(): skipjs = True + + +### As a reminder, don't use this file verbatim, it should only be used as +### a guide. +import sys +sys.exit("This sample-settings file shouldn't be used directly!") From 36df63caa4a6edc7978b7e4df8f9778e681bae32 Mon Sep 17 00:00:00 2001 From: TJ09 Date: Sat, 4 Jun 2011 13:28:24 -0400 Subject: [PATCH 25/26] Change ids to classes. Fixes #387 --- web_assets/overviewer.css | 8 ++++---- web_assets/overviewer.js | 12 ++++++------ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/web_assets/overviewer.css b/web_assets/overviewer.css index 19b9b34..14585e6 100644 --- a/web_assets/overviewer.css +++ b/web_assets/overviewer.css @@ -29,13 +29,13 @@ body { font-family: monospace; } -#customControl { +.customControl { padding: 5px; height: 15px; font-family: Arial, sans-serif; } -#customControl > div#top { +.customControl > div.top { background-color: #fff; border: 2px solid #000; text-align: center; @@ -45,14 +45,14 @@ body { cursor: pointer; } -#customControl > div#dropDown { +.customControl > div.dropDown { border: 1px solid #000; font-size: 12px; background-color: #fff; display: none; } -#customControl > div#button { +.customControl > div.button { border: 1px solid #000; font-size: 12px; background-color: #fff; diff --git a/web_assets/overviewer.js b/web_assets/overviewer.js index 94bb8cc..467affb 100644 --- a/web_assets/overviewer.js +++ b/web_assets/overviewer.js @@ -585,7 +585,7 @@ var overviewer = { // Spawn button var homeControlDiv = document.createElement('DIV'); var homeControl = new overviewer.classes.HomeControl(homeControlDiv); - homeControlDiv.id = 'customControl'; + $(homeControlDiv).addClass('customControl'); homeControlDiv.index = 1; if (overviewerConfig.map.controls.spawn) { overviewer.map.controls[google.maps.ControlPosition.TOP_RIGHT].push(homeControlDiv); @@ -680,18 +680,18 @@ var overviewer = { 'createDropDown': function(title, items) { var control = document.createElement('DIV'); // let's let a style sheet do most of the styling here - control.id = 'customControl'; + $(control).addClass('customControl'); var controlText = document.createElement('DIV'); controlText.innerHTML = title; var controlBorder = document.createElement('DIV'); - controlBorder.id='top'; + $(controlBorder).addClass('top'); control.appendChild(controlBorder); controlBorder.appendChild(controlText); var dropdownDiv = document.createElement('DIV'); - dropdownDiv.id='dropDown'; + $(dropdownDiv).addClass('dropDown'); control.appendChild(dropdownDiv); dropdownDiv.innerHTML=''; @@ -868,14 +868,14 @@ var overviewer = { controlDiv.style.padding = '5px'; // Set CSS for the control border var control = document.createElement('DIV'); - control.id='top'; + $(control).addClass('top'); control.title = 'Click to center the map on the Spawn'; controlDiv.appendChild(control); // Set CSS for the control interior var controlText = document.createElement('DIV'); controlText.innerHTML = 'Spawn'; - controlText.id='button'; + $(controlText).addClass('button'); control.appendChild(controlText); // Setup the click event listeners: simply set the map to map center From ac91268c668f0ce1d0cb8b27136502fe03c9b482 Mon Sep 17 00:00:00 2001 From: Aaron Griffith Date: Sat, 4 Jun 2011 20:05:09 -0400 Subject: [PATCH 26/26] added --forcerender option --- overviewer.py | 3 ++- quadtree.py | 14 +++++++++++--- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/overviewer.py b/overviewer.py index 5f3400c..e217949 100755 --- a/overviewer.py +++ b/overviewer.py @@ -94,6 +94,7 @@ def main(): parser.add_option("-z", "--zoom", dest="zoom", help="Sets the zoom level manually instead of calculating it. This can be useful if you have outlier chunks that make your world too big. This value will make the highest zoom level contain (2**ZOOM)^2 tiles", action="store", type="int", configFileOnly=True) parser.add_option("-d", "--delete", dest="delete", help="Clear all caches. Next time you render your world, it will have to start completely over again. This is probably not a good idea for large worlds. Use this if you change texture packs and want to re-render everything.", action="store_true", commandLineOnly=True) parser.add_option("--regionlist", dest="regionlist", help="A file containing, on each line, a path to a regionlist to update. Instead of scanning the world directory for regions, it will just use this list. Normal caching rules still apply.") + parser.add_option("--forcerender", dest="forcerender", help="Force re-rendering the entire map (or the given regionlist). Useful for re-rendering without deleting the entire map with --delete.", action="store_true") parser.add_option("--rendermodes", dest="rendermode", help="Specifies the render types, separated by commas. Use --list-rendermodes to list them all.", type="choice", choices=avail_rendermodes, required=True, default=avail_rendermodes[0], listify=True) parser.add_option("--list-rendermodes", dest="list_rendermodes", action="store_true", help="List available render modes and exit.", commandLineOnly=True) parser.add_option("--imgformat", dest="imgformat", help="The image output format to use. Currently supported: png(default), jpg.", configFileOnly=True ) @@ -231,7 +232,7 @@ def main(): # create the quadtrees # TODO chunklist q = [] - qtree_args = {'depth' : options.zoom, 'imgformat' : imgformat, 'imgquality' : options.imgquality, 'optimizeimg' : optimizeimg, 'bgcolor' : bgcolor} + 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/quadtree.py b/quadtree.py index 5d240db..f1c4b1d 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, 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"): """Generates a quadtree from the world given into the given dest directory @@ -60,6 +60,7 @@ class QuadtreeGen(object): """ assert(imgformat) + self.forcerender = forcerender self.imgformat = imgformat self.imgquality = imgquality self.optimizeimg = optimizeimg @@ -425,10 +426,17 @@ class QuadtreeGen(object): needs_rerender = False get_region_mtime = world.get_region_mtime for col, row, chunkx, chunky, regionfile in chunks: - # check region file mtime first. - region,regionMtime = get_region_mtime(regionfile) + # don't even check if it's not in the regionlist if self.world.regionlist and region._filename not in self.world.regionlist: continue + + # bail early if forcerender is set + if self.forcerender: + needs_rerender = True + break + + # check region file mtime first. + region,regionMtime = get_region_mtime(regionfile) if regionMtime <= tile_mtime: continue