From d210076479a36f7c7571351da3d4e04a715bb426 Mon Sep 17 00:00:00 2001 From: Jenny Date: Tue, 14 Dec 2010 02:39:01 -0800 Subject: [PATCH] Adds spawn output rendering (variant of night which shows dark areas in red) and support for multiple map types with buttons to switch between them. --- chunk.py | 38 +++++++++++++++++++++++++---- config.js | 16 ++++++++++++- gmap.py | 4 +++- web_assets/functions.js | 53 ++++++++++++++++++++++++++++++----------- web_assets/style.css | 2 +- world.py | 7 +++--- 6 files changed, 96 insertions(+), 24 deletions(-) diff --git a/chunk.py b/chunk.py index b43c070..3dd0e15 100644 --- a/chunk.py +++ b/chunk.py @@ -112,6 +112,18 @@ def iterate_chunkblocks(xoff,yoff): transparent_blocks = set([0, 6, 8, 9, 18, 20, 37, 38, 39, 40, 44, 50, 51, 52, 53, 59, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 74, 75, 76, 77, 78, 79, 81, 83, 85]) +# 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, 35, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 53, 54, 56, 57, 58, 60, 61, 62, 64, 65, + 66, 67, 71, 73, 74, 78, 79, 80, 81, 82, 84, 86, 87, 88, 89, 91]) + +# This set holds block ids that are fluid blocks +fluid_blocks = set([8,9,10,11]) + +# This set holds block ids that are not candidates for spawning mobs on +# (glass, half blocks) +nospawn_blocks = set([20,44]) + def render_and_save(chunkfile, cachedir, worldobj, cave=False, queue=None): """Used as the entry point for the multiprocessing workers (since processes can't target bound methods) or to easily render and save one chunk @@ -617,7 +629,14 @@ class ChunkRenderer(object): # block shaded with the current # block's light black_coeff, _ = self.get_lighting_coefficient(x, y, z) - composite.alpha_over(img, Image.blend(t[0], black_color, black_coeff), (imgx, imgy), t[1]) + if self.world.spawn and black_coeff > 0.8 and blockid in solid_blocks and not ( + blockid in nospawn_blocks or ( + z != 127 and (blocks[x,y,z+1] in solid_blocks or blocks[x,y,z+1] in fluid_blocks) + ) + ): + composite.alpha_over(img, Image.blend(t[0], red_color, black_coeff), (imgx, imgy), t[1]) + else: + composite.alpha_over(img, Image.blend(t[0], black_color, black_coeff), (imgx, imgy), t[1]) else: # draw each face lit appropriately, # but first just draw the block @@ -625,18 +644,28 @@ class ChunkRenderer(object): # top face black_coeff, face_occlude = self.get_lighting_coefficient(x, y, z + 1) + # Use red instead of black for spawnable blocks + if self.world.spawn and black_coeff > 0.8 and blockid in solid_blocks and not ( + blockid in nospawn_blocks or ( + z != 127 and (blocks[x,y,z+1] in solid_blocks or blocks[x,y,z+1] in fluid_blocks) + ) + ): + over_color = red_color + else: + over_color = black_color + if not face_occlude: - composite.alpha_over(img, black_color, (imgx, imgy), ImageEnhance.Brightness(facemasks[0]).enhance(black_coeff)) + composite.alpha_over(img, over_color, (imgx, imgy), ImageEnhance.Brightness(facemasks[0]).enhance(black_coeff)) # left face black_coeff, face_occlude = self.get_lighting_coefficient(x - 1, y, z) if not face_occlude: - composite.alpha_over(img, black_color, (imgx, imgy), ImageEnhance.Brightness(facemasks[1]).enhance(black_coeff)) + composite.alpha_over(img, over_color, (imgx, imgy), ImageEnhance.Brightness(facemasks[1]).enhance(black_coeff)) # right face black_coeff, face_occlude = self.get_lighting_coefficient(x, y + 1, z) if not face_occlude: - composite.alpha_over(img, black_color, (imgx, imgy), ImageEnhance.Brightness(facemasks[2]).enhance(black_coeff)) + composite.alpha_over(img, over_color, (imgx, imgy), ImageEnhance.Brightness(facemasks[2]).enhance(black_coeff)) # Draw edge lines if blockid in (44,): # step block @@ -705,6 +734,7 @@ def generate_facemasks(): return (top, left, right) facemasks = generate_facemasks() black_color = Image.new("RGB", (24,24), (0,0,0)) +red_color = Image.new("RGB", (24,24), (229,36,38)) # Render 128 different color images for color coded depth blending in cave mode def generate_depthcolors(): diff --git a/config.js b/config.js index d9aa1d9..b339192 100644 --- a/config.js +++ b/config.js @@ -1,6 +1,5 @@ var config = { - path: 'tiles', fileExt: '{imgformat}', tileSize: 384, defaultZoom: 1, @@ -28,6 +27,21 @@ var signGroups = [ {label: "All", match: function(s) {return true}} ]; +/* mapTypeData -- a list of alternate map renderings available. At least one rendering must be + * listed. When more than one are provided, controls to switch between them are provided, with + * the first one being the default. + * + * Required: + * label : string. Displayed on the control. + * path : string. Location of the rendered tiles. + */ +var mapTypeData=[ + {'label': 'Unlit', 'path': 'tiles'}, +// {'label': 'Day', 'path': 'lighting/tiles'}, +// {'label': 'Night', 'path': 'night/tiles'}, +// {'label': 'Spawn', 'path': 'spawn/tiles'} +]; + // Please leave the following variables here: var markerCollection = {}; // holds groups of markers diff --git a/gmap.py b/gmap.py index ec0719d..ef865fa 100755 --- a/gmap.py +++ b/gmap.py @@ -51,6 +51,7 @@ def main(): 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("--lighting", dest="lighting", help="Renders shadows using light data from each chunk.", action="store_true") parser.add_option("--night", dest="night", help="Renders shadows using light data from each chunk, as if it were night. Implies --lighting.", action="store_true") + parser.add_option("--spawn", dest="spawn", help="Renders shadows using light data from each chunk, as if it were night, while also highlighting areas that are dark enough to spawn mobs. Implies --lighting and --night.", action="store_true") parser.add_option("--imgformat", dest="imgformat", help="The image output format to use. Currently supported: png(default), jpg. NOTE: png will always be used as the intermediate image format.") 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%") parser.add_option("-q", "--quiet", dest="quiet", action="count", default=0, help="Print less output. You can specify this option multiple times.") @@ -123,7 +124,8 @@ def main(): logging.info("Notice: Not using biome data for tinting") # First generate the world's chunk images - w = world.WorldRenderer(worlddir, cachedir, chunklist=chunklist, lighting=options.lighting, night=options.night, useBiomeData=useBiomeData) + w = world.WorldRenderer(worlddir, cachedir, chunklist=chunklist, lighting=options.lighting, night=options.night, spawn=options.spawn, useBiomeData=useBiomeData) + w.go(options.procs) # Now generate the tiles diff --git a/web_assets/functions.js b/web_assets/functions.js index 4b68832..6d9537a 100644 --- a/web_assets/functions.js +++ b/web_assets/functions.js @@ -37,6 +37,7 @@ function drawMapControls() { compassImg.src="compass.png"; compassDiv.appendChild(compassImg); + compassDiv.index = 0; map.controls[google.maps.ControlPosition.TOP_RIGHT].push(compassDiv); @@ -260,16 +261,23 @@ function initialize() { if (argname == "zoom") {zoom = parseInt(value);} } + var mapTyepControlToggle = false + if (mapTypeIds.length > 1) { + mapTyepControlToggle = true + } var mapOptions = { zoom: zoom, center: new google.maps.LatLng(lat, lng), navigationControl: true, scaleControl: false, - mapTypeControl: false, + mapTypeControl: mapTyepControlToggle, + mapTypeControlOptions: { + mapTypeIds: mapTypeIds + }, + mapTypeId: mapTypeIdDefault streetViewControl: false, - mapTypeId: 'mcmap' }; - map = new google.maps.Map(document.getElementById("mcmap"), mapOptions); + map = new google.maps.Map(document.getElementById('mcmap'), mapOptions); if(config.debug) { map.overlayMapTypes.insertAt(0, new CoordMapType(new google.maps.Size(config.tileSize, config.tileSize))); @@ -287,11 +295,12 @@ function initialize() { } // Now attach the coordinate map type to the map's registry - map.mapTypes.set('mcmap', MCMapType); + for (idx in MCMapType) { + map.mapTypes.set('mcmap' + MCMapType[idx].name, MCMapType[idx]); + } // We can now set the map to use the 'coordinate' map type - map.setMapTypeId('mcmap'); - + map.setMapTypeId(mapTypeIdDefault); // initialize the markers and regions initMarkers(); @@ -370,9 +379,9 @@ function initialize() { return new google.maps.LatLng(lat, lng); } - var MCMapOptions = { - getTileUrl: function(tile, zoom) { - var url = config.path; +function getTileUrlGenerator(path) { + return function(tile, zoom) { + var url = path; if(tile.x < 0 || tile.x >= Math.pow(2, zoom) || tile.y < 0 || tile.y >= Math.pow(2, zoom)) { url += '/blank'; } else if(zoom == 0) { @@ -390,17 +399,33 @@ function initialize() { url += '?c=' + Math.floor(d.getTime() / (1000 * 60 * config.cacheMinutes)); } return(url); - }, + } +} + +var MCMapOptions = new Array; +var MCMapType = new Array; +var mapTypeIdDefault = null; +var mapTypeIds = []; +for (idx in mapTypeData) { + var view = mapTypeData[idx]; + + MCMapOptions[view.label] = { + getTileUrl: getTileUrlGenerator(view.path), tileSize: new google.maps.Size(config.tileSize, config.tileSize), maxZoom: config.maxZoom, minZoom: 0, isPng: !(config.fileExt.match(/^png$/i) == null) }; - var MCMapType = new google.maps.ImageMapType(MCMapOptions); - MCMapType.name = "MC Map"; - MCMapType.alt = "Minecraft Map"; - MCMapType.projection = new MCMapProjection(); + MCMapType[view.label] = new google.maps.ImageMapType(MCMapOptions[view.label]); + MCMapType[view.label].name = view.label; + MCMapType[view.label].alt = "Minecraft " + view.label + " Map"; + MCMapType[view.label].projection = new MCMapProjection(); + if (mapTypeIdDefault == null) { + mapTypeIdDefault = 'mcmap' + view.label; + } + mapTypeIds.push('mcmap' + view.label); +} function CoordMapType() { } diff --git a/web_assets/style.css b/web_assets/style.css index c0d0a6b..e6029dc 100644 --- a/web_assets/style.css +++ b/web_assets/style.css @@ -25,7 +25,7 @@ body { height: 100%; margin: 0px; padding: 0px ; background-color: #000; } #signControl > div#top { background-color: #fff; - border: 1px solid #000; + border: 2px solid #000; text-align: center; width: 70px; font-size: 12px; diff --git a/world.py b/world.py index 1165053..7e77b0b 100644 --- a/world.py +++ b/world.py @@ -95,11 +95,12 @@ class WorldRenderer(object): files to update. If it includes a trailing newline, it is stripped, so you can pass in file handles just fine. """ - def __init__(self, worlddir, cachedir, chunklist=None, lighting=False, night=False, useBiomeData=False): + def __init__(self, worlddir, cachedir, chunklist=None, lighting=False, night=False, spawn=False, useBiomeData=False): self.worlddir = worlddir self.caves = False - self.lighting = lighting or night - self.night = night + self.lighting = lighting or night or spawn + self.night = night or spawn + self.spawn = spawn self.cachedir = cachedir self.useBiomeData = useBiomeData