From 9f69f099adc49dacbaff364dd85510715b830af4 Mon Sep 17 00:00:00 2001 From: Aaron Griffith Date: Mon, 28 Mar 2011 02:50:36 -0400 Subject: [PATCH 01/25] initial support for map overlays --- chunk.py | 1 + config.js | 3 +- googlemap.py | 6 +- overviewer.py | 3 +- setup.py | 8 +- src/composite.c | 10 ++- src/overviewer.h | 5 +- src/rendermode-normal.c | 2 +- src/rendermode-overlay.c | 75 ++++++++++++++++++ src/rendermodes.c | 4 + src/rendermodes.h | 7 ++ web_assets/functions.js | 165 ++++++++++++++++++++++++--------------- web_assets/style.css | 6 +- 13 files changed, 216 insertions(+), 79 deletions(-) create mode 100644 src/rendermode-overlay.c diff --git a/chunk.py b/chunk.py index 99019b6..56e58ef 100644 --- a/chunk.py +++ b/chunk.py @@ -503,6 +503,7 @@ def generate_facemasks(): facemasks = generate_facemasks() black_color = Image.new("RGB", (24,24), (0,0,0)) red_color = Image.new("RGB", (24,24), (229,36,38)) +white_color = Image.new("RGB", (24,24), (255,255,255)) # 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 9296853..f52c170 100644 --- a/config.js +++ b/config.js @@ -44,7 +44,8 @@ var mapTypeData=[ {'label': 'Unlit', 'path': 'tiles'}, // {'label': 'Day', 'path': 'lighting/tiles'}, // {'label': 'Night', 'path': 'night/tiles'}, -// {'label': 'Spawn', 'path': 'spawn/tiles', 'base': 'http://example.cdn.amazon.com/'} +// {'label': 'Spawn', 'path': 'spawn/tiles', 'base': 'http://example.cdn.amazon.com/'}, +// {'label': 'Overlay', 'path': 'overlay/tiles', 'overlay': true} ]; */ diff --git a/googlemap.py b/googlemap.py index 840d2ac..9ae846a 100644 --- a/googlemap.py +++ b/googlemap.py @@ -95,8 +95,12 @@ class MapGen(object): "{imgformat}", str(imgformat)) # create generated map type data, from given quadtrees + # FIXME hook this into render_modes in setup.py, somehow + overlay_types = ['overlay'] maptypedata = map(lambda q: {'label' : q.rendermode.capitalize(), - 'path' : q.tiledir}, self.quadtrees) + 'path' : q.tiledir, + 'overlay' : q.rendermode in overlay_types}, + self.quadtrees) config = config.replace("{maptypedata}", json.dumps(maptypedata)) with open(os.path.join(self.destdir, "config.js"), 'w') as output: diff --git a/overviewer.py b/overviewer.py index b2aa275..7082d0c 100755 --- a/overviewer.py +++ b/overviewer.py @@ -64,7 +64,8 @@ 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("--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("--rendermodes", dest="rendermode", help="Specifies the render type: normal (default), lighting, night, or spawn.", type="choice", choices=["normal", "lighting", "night", "spawn"], required=True, default="normal", listify=True) + # TODO hook this up to render_modes in setup.py + parser.add_option("--rendermodes", dest="rendermode", help="Specifies the render type: normal (default), lighting, night, or spawn.", type="choice", choices=["normal", "lighting", "night", "spawn", "overlay"], required=True, default="normal", listify=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.", configFileOnly=True ) 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) diff --git a/setup.py b/setup.py index 34f646b..27bb755 100644 --- a/setup.py +++ b/setup.py @@ -49,9 +49,13 @@ try: except AttributeError: numpy_include = numpy.get_numpy_include() +# used to figure out what files to compile +# TODO and, potentially, to check which are available +render_modes = ['normal', 'lighting', 'night', 'spawn', 'overlay'] + +c_overviewer_files = ['src/main.c', 'src/composite.c', 'src/iterate.c', 'src/endian.c', 'src/rendermodes.c'] +c_overviewer_files += map(lambda mode: 'src/rendermode-%s.c' % (mode,), render_modes) -c_overviewer_files = ['src/main.c', 'src/composite.c', 'src/iterate.c', 'src/endian.c'] -c_overviewer_files += ['src/rendermodes.c', 'src/rendermode-normal.c', 'src/rendermode-lighting.c', 'src/rendermode-night.c', 'src/rendermode-spawn.c'] setup_kwargs['ext_modules'].append(Extension('c_overviewer', c_overviewer_files, include_dirs=['.', numpy_include], extra_link_args=[])) # tell build_ext to build the extension in-place # (NOT in build/) diff --git a/src/composite.c b/src/composite.c index 4a03d23..432ece8 100644 --- a/src/composite.c +++ b/src/composite.c @@ -273,7 +273,8 @@ alpha_over_wrap(PyObject *self, PyObject *args) * also, it multiplies instead of doing an over operation */ PyObject * -tint_with_mask(PyObject *dest, unsigned char sr, unsigned char sg, unsigned char sb, +tint_with_mask(PyObject *dest, unsigned char sr, unsigned char sg, + unsigned char sb, unsigned char sa, PyObject *mask, int dx, int dy, int xsize, int ysize) { /* libImaging handles */ Imaging imDest, imMask; @@ -332,9 +333,11 @@ tint_with_mask(PyObject *dest, unsigned char sr, unsigned char sg, unsigned char out++; *out = MULDIV255(*out, sb, tmp1); out++; + *out = MULDIV255(*out, sa, tmp1); + out++; } else if (*inmask == 0) { /* do nothing -- source is fully transparent */ - out += 3; + out += 4; } else { /* general case */ @@ -345,9 +348,10 @@ tint_with_mask(PyObject *dest, unsigned char sr, unsigned char sg, unsigned char out++; *out = MULDIV255(*out, (255 - *inmask) + MULDIV255(sb, *inmask, tmp1), tmp2); out++; + *out = MULDIV255(*out, (255 - *inmask) + MULDIV255(sa, *inmask, tmp1), tmp2); + out++; } - out++; inmask += mask_stride; } } diff --git a/src/overviewer.h b/src/overviewer.h index 3475fdf..4045313 100644 --- a/src/overviewer.h +++ b/src/overviewer.h @@ -44,7 +44,8 @@ PyObject *alpha_over(PyObject *dest, PyObject *src, PyObject *mask, PyObject *alpha_over_full(PyObject *dest, PyObject *src, PyObject *mask, float overall_alpha, int dx, int dy, int xsize, int ysize); PyObject *alpha_over_wrap(PyObject *self, PyObject *args); -PyObject *tint_with_mask(PyObject *dest, unsigned char sr, unsigned char sg, unsigned char sb, +PyObject *tint_with_mask(PyObject *dest, unsigned char sr, unsigned char sg, + unsigned char sb, unsigned char sa, PyObject *mask, int dx, int dy, int xsize, int ysize); /* in iterate.c */ @@ -79,7 +80,7 @@ PyObject *chunk_render(PyObject *self, PyObject *args); #include "rendermodes.h" /* in endian.c */ -void init_endian(); +void init_endian(void); unsigned short big_endian_ushort(unsigned short in); unsigned int big_endian_uint(unsigned int in); diff --git a/src/rendermode-normal.c b/src/rendermode-normal.c index d7a1367..f0cdb96 100644 --- a/src/rendermode-normal.c +++ b/src/rendermode-normal.c @@ -158,7 +158,7 @@ rendermode_normal_draw(void *data, RenderState *state, PyObject *src, PyObject * b = PyInt_AsLong(PyTuple_GET_ITEM(color, 2)); Py_DECREF(color); - tint_with_mask(state->img, r, g, b, facemask, state->imgx, state->imgy, 0, 0); + tint_with_mask(state->img, r, g, b, 255, facemask, state->imgx, state->imgy, 0, 0); } } } diff --git a/src/rendermode-overlay.c b/src/rendermode-overlay.c new file mode 100644 index 0000000..32cb03a --- /dev/null +++ b/src/rendermode-overlay.c @@ -0,0 +1,75 @@ +/* + * This file is part of the Minecraft Overviewer. + * + * Minecraft Overviewer is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation, either version 3 of the License, or (at + * your option) any later version. + * + * Minecraft Overviewer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with the Overviewer. If not, see . + */ + +#include "overviewer.h" + +static int +rendermode_overlay_start(void *data, RenderState *state) { + PyObject *facemasks_py; + RenderModeOverlay *self = (RenderModeOverlay *)data; + + facemasks_py = PyObject_GetAttrString(state->chunk, "facemasks"); + /* borrowed reference, needs to be incref'd if we keep it */ + self->facemask_top = PyTuple_GetItem(facemasks_py, 0); + Py_INCREF(self->facemask_top); + Py_DECREF(facemasks_py); + + self->white_color = PyObject_GetAttrString(state->chunk, "white_color"); + + return 0; +} + +static void +rendermode_overlay_finish(void *data, RenderState *state) { + RenderModeOverlay *self = (RenderModeOverlay *)data; + + Py_XDECREF(self->facemask_top); + Py_XDECREF(self->white_color); +} + +static int +rendermode_overlay_occluded(void *data, RenderState *state) { + int x = state->x, y = state->y, z = state->z; + + if ( (x != 0) && (y != 15) && (z != 127) && + !is_transparent(getArrayByte3D(state->blocks, x-1, y, z)) && + !is_transparent(getArrayByte3D(state->blocks, x, y, z+1)) && + !is_transparent(getArrayByte3D(state->blocks, x, y+1, z))) { + return 1; + } + + return 0; +} + +static void +rendermode_overlay_draw(void *data, RenderState *state, PyObject *src, PyObject *mask) { + RenderModeOverlay *self = (RenderModeOverlay *)data; + + /* clear the draw space -- set alpha to 0 within mask */ + tint_with_mask(state->img, 255, 255, 255, 0, mask, state->imgx, state->imgy, 0, 0); + + /* do the overlay */ + alpha_over_full(state->img, self->white_color, self->facemask_top, 0.5, state->imgx, state->imgy, 0, 0); +} + +RenderModeInterface rendermode_overlay = { + sizeof(RenderModeOverlay), + rendermode_overlay_start, + rendermode_overlay_finish, + rendermode_overlay_occluded, + rendermode_overlay_draw, +}; diff --git a/src/rendermodes.c b/src/rendermodes.c index f4fbe89..46cc432 100644 --- a/src/rendermodes.c +++ b/src/rendermodes.c @@ -31,6 +31,10 @@ RenderModeInterface *get_render_mode(RenderState *state) { iface = &rendermode_night; } else if (strcmp(rendermode, "spawn") == 0) { iface = &rendermode_spawn; + } else if (strcmp(rendermode, "overlay") == 0) { + /* TODO temporarily use overlay directly, but later on + you want to use overlay-derived modes */ + iface = &rendermode_overlay; } Py_DECREF(rendermode_py); diff --git a/src/rendermodes.h b/src/rendermodes.h index d26091d..87c7cb1 100644 --- a/src/rendermodes.h +++ b/src/rendermodes.h @@ -71,6 +71,13 @@ typedef struct { } RenderModeNormal; extern RenderModeInterface rendermode_normal; +/* OVERLAY */ +typedef struct { + /* top facemask and white color image, for drawing overlays */ + PyObject *facemask_top, *white_color; +} RenderModeOverlay; +extern RenderModeInterface rendermode_overlay; + /* LIGHTING */ typedef struct { /* inherits from normal render mode */ diff --git a/web_assets/functions.js b/web_assets/functions.js index 174a43a..9b7ac12 100644 --- a/web_assets/functions.js +++ b/web_assets/functions.js @@ -14,6 +14,61 @@ function prepareSignMarker(marker, item) { } +function createDropDown(title, items) { + var control = document.createElement("DIV"); + control.id = "customControl"; // let's let a style sheet do most of the styling here + + var controlBorder = document.createElement("DIV"); + controlBorder.id="top"; + control.appendChild(controlBorder); + + var controlText = document.createElement("DIV"); + + controlBorder.appendChild(controlText); + + controlText.innerHTML = title; + + var dropdownDiv = document.createElement("DIV"); + + + $(controlText).click(function() { + $(dropdownDiv).toggle(); + + }); + + + dropdownDiv.id="dropDown"; + control.appendChild(dropdownDiv); + dropdownDiv.innerHTML=""; + + map.controls[google.maps.ControlPosition.TOP_RIGHT].push(control); + + for (idx in items) { + var item = items[idx]; + //console.log(item); + label = item.label; + action = item.action; + var d = document.createElement("div"); + var n = document.createElement("input"); + n.type="checkbox"; + + $(n).data("label",label); + jQuery(n).click(function(e) { + var t = $(e.target); + action(idx, label, e.target.checked); + }); + + if (item.checked) { + n.checked = true; + action(idx, label, item.checked); + } + dropdownDiv.appendChild(d); + d.appendChild(n) + var textNode = document.createElement("text"); + textNode.innerHTML = label + "
"; + d.appendChild(textNode); + } +} function drawMapControls() { @@ -42,69 +97,43 @@ function drawMapControls() { if (signGroups.length > 0) { - // signpost display control - // - - var signControl = document.createElement("DIV"); - signControl.id = "signControl"; // let's let a style sheet do most of the styling here - - var controlBorder = document.createElement("DIV"); - controlBorder.id="top"; - signControl.appendChild(controlBorder); - - var controlText = document.createElement("DIV"); - - controlBorder.appendChild(controlText); - - controlText.innerHTML = "Signposts"; - - var dropdownDiv = document.createElement("DIV"); - - - $(controlText).click(function() { - $(dropdownDiv).toggle(); - - }); - - - dropdownDiv.id="dropDown"; - signControl.appendChild(dropdownDiv); - dropdownDiv.innerHTML=""; - - map.controls[google.maps.ControlPosition.TOP_RIGHT].push(signControl); - - - - var hasSignGroup = false; - for (idx in signGroups) { - var item = signGroups[idx]; - //console.log(item); - label = item.label; - hasSignGroup = true; - var d = document.createElement("div"); - var n = document.createElement("input"); - n.type="checkbox"; - - $(n).data("label",label); - jQuery(n).click(function(e) { - var t = $(e.target); - jQuery.each(markerCollection[t.data("label")], function(i,elem) {elem.setVisible(e.target.checked);}); - }); - - - if (item.checked) { - n.checked = true; - jQuery.each(markerCollection[label], function(i,elem) {elem.setVisible(n.checked);}); + // signpost display control + + var items = []; + for (idx in signGroups) { + var item = signGroups[idx]; + items.push({"label": item.label, "checked": item.checked, + "action": function(n, l, checked) { + jQuery.each(markerCollection[l], function(i,elem) {elem.setVisible(checked);}); + }}); } - dropdownDiv.appendChild(d); - d.appendChild(n) - var textNode = document.createElement("text"); - textNode.innerHTML = label + "
"; - d.appendChild(textNode); - + createDropDown("Signposts", items); } - - + + if (overlayMapTypes.length > 0) { + // overlay maps control + + var items = []; + for (idx in overlayMapTypes) { + var overlay = overlayMapTypes[idx]; + items.push({"label": overlay.name, "checked": false, + "action": function(i, l, checked) { + if (checked) { + map.overlayMapTypes.push(overlay); + } else { + var idx_to_delete = -1; + map.overlayMapTypes.forEach(function(e, j) { + if (e == overlay) { + idx_to_delete = j; + } + }); + if (idx_to_delete >= 0) { + map.overlayMapTypes.removeAt(idx_to_delete); + } + } + }}); + } + createDropDown("Overlays", items); } } @@ -303,7 +332,7 @@ function initialize() { 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(mapTypeIdDefault); @@ -412,6 +441,7 @@ var MCMapOptions = new Array; var MCMapType = new Array; var mapTypeIdDefault = null; var mapTypeIds = []; +var overlayMapTypes = []; for (idx in mapTypeData) { var view = mapTypeData[idx]; @@ -427,10 +457,15 @@ for (idx in mapTypeData) { 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; + + if (view.overlay) { + overlayMapTypes.push(MCMapType[view.label]); + } else { + if (mapTypeIdDefault == null) { + mapTypeIdDefault = 'mcmap' + view.label; + } + mapTypeIds.push('mcmap' + view.label); } - mapTypeIds.push('mcmap' + view.label); } function CoordMapType() { diff --git a/web_assets/style.css b/web_assets/style.css index e6029dc..32389bf 100644 --- a/web_assets/style.css +++ b/web_assets/style.css @@ -17,13 +17,13 @@ body { height: 100%; margin: 0px; padding: 0px ; background-color: #000; } font-family: monospace; } -#signControl { +#customControl { padding: 5px; height: 15px; font-family: Arial, sans-serif; } -#signControl > div#top { +#customControl > div#top { background-color: #fff; border: 2px solid #000; text-align: center; @@ -33,7 +33,7 @@ body { height: 100%; margin: 0px; padding: 0px ; background-color: #000; } cursor: pointer; } -#signControl > div#dropDown { +#customControl > div#dropDown { border: 1px solid #000; font-size: 12px; background-color: #fff; From d4bd1d713c88e18569a3c1cb83a0f1fd2c7c4069 Mon Sep 17 00:00:00 2001 From: Aaron Griffith Date: Mon, 28 Mar 2011 03:09:00 -0400 Subject: [PATCH 02/25] overlay mode now only considers blocks where overlays make sense --- src/rendermode-overlay.c | 41 +++++++++++++++++++++++++++++++++++++--- src/rendermodes.h | 2 ++ 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/src/rendermode-overlay.c b/src/rendermode-overlay.c index 32cb03a..27321ce 100644 --- a/src/rendermode-overlay.c +++ b/src/rendermode-overlay.c @@ -30,6 +30,9 @@ rendermode_overlay_start(void *data, RenderState *state) { self->white_color = PyObject_GetAttrString(state->chunk, "white_color"); + self->solid_blocks = PyObject_GetAttrString(state->chunk, "solid_blocks"); + self->fluid_blocks = PyObject_GetAttrString(state->chunk, "fluid_blocks"); + return 0; } @@ -37,8 +40,10 @@ static void rendermode_overlay_finish(void *data, RenderState *state) { RenderModeOverlay *self = (RenderModeOverlay *)data; - Py_XDECREF(self->facemask_top); - Py_XDECREF(self->white_color); + Py_DECREF(self->facemask_top); + Py_DECREF(self->white_color); + Py_DECREF(self->solid_blocks); + Py_DECREF(self->fluid_blocks); } static int @@ -51,7 +56,7 @@ rendermode_overlay_occluded(void *data, RenderState *state) { !is_transparent(getArrayByte3D(state->blocks, x, y+1, z))) { return 1; } - + return 0; } @@ -61,7 +66,37 @@ rendermode_overlay_draw(void *data, RenderState *state, PyObject *src, PyObject /* clear the draw space -- set alpha to 0 within mask */ tint_with_mask(state->img, 255, 255, 255, 0, mask, state->imgx, state->imgy, 0, 0); + + /* skip rendering the overlay if we can't see it */ + if (state->z != 127) { + unsigned char top_block = getArrayByte3D(state->blocks, state->x, state->y, state->z+1); + if (!is_transparent(top_block)) { + return; + } + + /* check to be sure this block is solid/fluid */ + PyObject *top_block_py = PyInt_FromLong(top_block); + if (PySequence_Contains(self->solid_blocks, top_block_py) || + PySequence_Contains(self->fluid_blocks, top_block_py)) { + + /* top block is fluid or solid, skip drawing */ + Py_DECREF(top_block_py); + return; + } + Py_DECREF(top_block_py); + } + /* check to be sure this block is solid/fluid */ + PyObject *block_py = PyInt_FromLong(state->block); + if (!PySequence_Contains(self->solid_blocks, block_py) && + !PySequence_Contains(self->fluid_blocks, block_py)) { + + /* not fluid or solid, skip drawing the overlay */ + Py_DECREF(block_py); + return; + } + Py_DECREF(block_py); + /* do the overlay */ alpha_over_full(state->img, self->white_color, self->facemask_top, 0.5, state->imgx, state->imgy, 0, 0); } diff --git a/src/rendermodes.h b/src/rendermodes.h index 87c7cb1..848c070 100644 --- a/src/rendermodes.h +++ b/src/rendermodes.h @@ -75,6 +75,8 @@ extern RenderModeInterface rendermode_normal; typedef struct { /* top facemask and white color image, for drawing overlays */ PyObject *facemask_top, *white_color; + /* only show overlay on top of solid or fluid blocks */ + PyObject *solid_blocks, *fluid_blocks; } RenderModeOverlay; extern RenderModeInterface rendermode_overlay; From 83db528d818b2ff02ea02a1cb08251179487b9ba Mon Sep 17 00:00:00 2001 From: Aaron Griffith Date: Mon, 28 Mar 2011 03:20:16 -0400 Subject: [PATCH 03/25] added callback for derived rendermodes to easily provide overlay color --- src/rendermode-overlay.c | 19 ++++++++++++++++++- src/rendermodes.h | 5 +++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/rendermode-overlay.c b/src/rendermode-overlay.c index 27321ce..08e3b30 100644 --- a/src/rendermode-overlay.c +++ b/src/rendermode-overlay.c @@ -17,6 +17,14 @@ #include "overviewer.h" +static void get_color(void *data, RenderState *state, + unsigned char *r, unsigned char *g, unsigned char *b, unsigned char *a) { + *r = 200; + *g = 200; + *b = 255; + *a = 155; +} + static int rendermode_overlay_start(void *data, RenderState *state) { PyObject *facemasks_py; @@ -33,6 +41,8 @@ rendermode_overlay_start(void *data, RenderState *state) { self->solid_blocks = PyObject_GetAttrString(state->chunk, "solid_blocks"); self->fluid_blocks = PyObject_GetAttrString(state->chunk, "fluid_blocks"); + self->get_color = get_color; + return 0; } @@ -63,6 +73,7 @@ rendermode_overlay_occluded(void *data, RenderState *state) { static void rendermode_overlay_draw(void *data, RenderState *state, PyObject *src, PyObject *mask) { RenderModeOverlay *self = (RenderModeOverlay *)data; + unsigned char r, g, b, a; /* clear the draw space -- set alpha to 0 within mask */ tint_with_mask(state->img, 255, 255, 255, 0, mask, state->imgx, state->imgy, 0, 0); @@ -97,8 +108,14 @@ rendermode_overlay_draw(void *data, RenderState *state, PyObject *src, PyObject } Py_DECREF(block_py); + /* get our color info */ + self->get_color(data, state, &r, &g, &b, &a); + /* do the overlay */ - alpha_over_full(state->img, self->white_color, self->facemask_top, 0.5, state->imgx, state->imgy, 0, 0); + if (a > 0) { + alpha_over(state->img, self->white_color, self->facemask_top, state->imgx, state->imgy, 0, 0); + tint_with_mask(state->img, r, g, b, a, self->facemask_top, state->imgx, state->imgy, 0, 0); + } } RenderModeInterface rendermode_overlay = { diff --git a/src/rendermodes.h b/src/rendermodes.h index 848c070..6129980 100644 --- a/src/rendermodes.h +++ b/src/rendermodes.h @@ -77,6 +77,11 @@ typedef struct { PyObject *facemask_top, *white_color; /* only show overlay on top of solid or fluid blocks */ PyObject *solid_blocks, *fluid_blocks; + /* can be overridden in derived classes to control + overlay alpha and color + last four vars are r, g, b, a out */ + void (*get_color)(void *, RenderState *, + unsigned char *, unsigned char *, unsigned char *, unsigned char *); } RenderModeOverlay; extern RenderModeInterface rendermode_overlay; From f23d3ddac9b5d59da1ad649b3787c816edcef38d Mon Sep 17 00:00:00 2001 From: Aaron Griffith Date: Mon, 28 Mar 2011 03:40:02 -0400 Subject: [PATCH 04/25] converted spawn into a overlay-based rendermode --- chunk.py | 1 - googlemap.py | 2 +- overviewer.py | 2 +- src/rendermode-spawn.c | 117 ++++++++++++++++++----------------------- src/rendermodes.c | 4 -- src/rendermodes.h | 9 ++-- 6 files changed, 58 insertions(+), 77 deletions(-) diff --git a/chunk.py b/chunk.py index 56e58ef..bfe7e24 100644 --- a/chunk.py +++ b/chunk.py @@ -502,7 +502,6 @@ 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)) white_color = Image.new("RGB", (24,24), (255,255,255)) # Render 128 different color images for color coded depth blending in cave mode diff --git a/googlemap.py b/googlemap.py index 9ae846a..29e197f 100644 --- a/googlemap.py +++ b/googlemap.py @@ -96,7 +96,7 @@ class MapGen(object): # create generated map type data, from given quadtrees # FIXME hook this into render_modes in setup.py, somehow - overlay_types = ['overlay'] + overlay_types = ['spawn'] maptypedata = map(lambda q: {'label' : q.rendermode.capitalize(), 'path' : q.tiledir, 'overlay' : q.rendermode in overlay_types}, diff --git a/overviewer.py b/overviewer.py index 7082d0c..4ac0e8d 100755 --- a/overviewer.py +++ b/overviewer.py @@ -65,7 +65,7 @@ def main(): 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.") # TODO hook this up to render_modes in setup.py - parser.add_option("--rendermodes", dest="rendermode", help="Specifies the render type: normal (default), lighting, night, or spawn.", type="choice", choices=["normal", "lighting", "night", "spawn", "overlay"], required=True, default="normal", listify=True) + parser.add_option("--rendermodes", dest="rendermode", help="Specifies the render type: normal (default), lighting, night, or spawn.", type="choice", choices=["normal", "lighting", "night", "spawn"], required=True, default="normal", listify=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.", configFileOnly=True ) 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) diff --git a/src/rendermode-spawn.c b/src/rendermode-spawn.c index 1a7c96a..b0b78a4 100644 --- a/src/rendermode-spawn.c +++ b/src/rendermode-spawn.c @@ -18,21 +18,63 @@ #include "overviewer.h" #include +static void get_color(void *data, RenderState *state, + unsigned char *r, unsigned char *g, unsigned char *b, unsigned char *a) { + + RenderModeSpawn* self = (RenderModeSpawn *)data; + int x = state->x, y = state->y, z = state->z; + unsigned char blocklight, skylight; + PyObject *block_py; + + /* set a nice, pretty red color */ + *r = 229; + *g = 36; + *b = 38; + + /* default to no overlay, until told otherwise */ + *a = 0; + + /* if we're at the top, skip */ + if (z == 127) + return; + + block_py = PyInt_FromLong(state->block); + if (PySequence_Contains(self->nospawn_blocks, block_py)) { + /* nothing can spawn on this */ + Py_DECREF(block_py); + return; + } + Py_DECREF(block_py); + + blocklight = getArrayByte3D(self->blocklight, x, y, z+1); + skylight = getArrayByte3D(self->skylight, x, y, z+1); + + if (MAX(blocklight, skylight) <= 7) { + /* hostile mobs spawn in daylight */ + *a = 240; + } else if (MAX(blocklight, skylight - 11) <= 7) { + /* hostile mobs spawn at night */ + *a = 150; + } +} + static int rendermode_spawn_start(void *data, RenderState *state) { RenderModeSpawn* self; /* first, chain up */ - int ret = rendermode_night.start(data, state); + int ret = rendermode_overlay.start(data, state); if (ret != 0) return ret; /* now do custom initializations */ self = (RenderModeSpawn *)data; - self->solid_blocks = PyObject_GetAttrString(state->chunk, "solid_blocks"); self->nospawn_blocks = PyObject_GetAttrString(state->chunk, "nospawn_blocks"); - self->fluid_blocks = PyObject_GetAttrString(state->chunk, "fluid_blocks"); - self->red_color = PyObject_GetAttrString(state->chunk, "red_color"); + self->blocklight = PyObject_GetAttrString(state->self, "blocklight"); + self->skylight = PyObject_GetAttrString(state->self, "skylight"); + + /* setup custom color */ + self->parent.get_color = get_color; return 0; } @@ -40,81 +82,26 @@ rendermode_spawn_start(void *data, RenderState *state) { static void rendermode_spawn_finish(void *data, RenderState *state) { /* first free all *our* stuff */ - RenderModeSpawn* self = (RenderModeSpawn *)data; + RenderModeSpawn* self = (RenderModeSpawn *)data; - Py_DECREF(self->solid_blocks); Py_DECREF(self->nospawn_blocks); - Py_DECREF(self->fluid_blocks); + Py_DECREF(self->blocklight); + Py_DECREF(self->skylight); /* now, chain up */ - rendermode_night.finish(data, state); + rendermode_overlay.finish(data, state); } static int rendermode_spawn_occluded(void *data, RenderState *state) { /* no special occlusion here */ - return rendermode_night.occluded(data, state); + return rendermode_overlay.occluded(data, state); } static void rendermode_spawn_draw(void *data, RenderState *state, PyObject *src, PyObject *mask) { - /* different versions of self (spawn, lighting) */ - RenderModeSpawn* self = (RenderModeSpawn *)data; - RenderModeLighting *lighting = (RenderModeLighting *)self; - - int x = state->x, y = state->y, z = state->z; - PyObject *old_black_color = NULL; - - /* figure out the appropriate darkness: - this block for transparents, the block above for non-transparent */ - float darkness = 0.0; - if (is_transparent(state->block)) { - darkness = get_lighting_coefficient((RenderModeLighting *)self, state, x, y, z, NULL); - } else { - darkness = get_lighting_coefficient((RenderModeLighting *)self, state, x, y, z+1, NULL); - } - - /* if it's dark enough... */ - if (darkness > 0.8) { - PyObject *block_py = PyInt_FromLong(state->block); - - /* make sure it's solid */ - if (PySequence_Contains(self->solid_blocks, block_py)) { - int spawnable = 1; - - /* not spawnable if its in the nospawn list */ - if (PySequence_Contains(self->nospawn_blocks, block_py)) - spawnable = 0; - - /* check the block above for solid or fluid */ - if (spawnable && z != 127) { - PyObject *top_block_py = PyInt_FromLong(getArrayByte3D(state->blocks, x, y, z+1)); - if (PySequence_Contains(self->solid_blocks, top_block_py) || - PySequence_Contains(self->fluid_blocks, top_block_py)) { - - spawnable = 0; - } - - Py_DECREF(top_block_py); - } - - /* if we passed all the checks, replace black_color with red_color */ - if (spawnable) { - old_black_color = lighting->black_color; - lighting->black_color = self->red_color; - } - } - - Py_DECREF(block_py); - } - /* draw normally */ - rendermode_night.draw(data, state, src, mask); - - /* reset black_color, if needed */ - if (old_black_color != NULL) { - lighting->black_color = old_black_color; - } + rendermode_overlay.draw(data, state, src, mask); } RenderModeInterface rendermode_spawn = { diff --git a/src/rendermodes.c b/src/rendermodes.c index 46cc432..f4fbe89 100644 --- a/src/rendermodes.c +++ b/src/rendermodes.c @@ -31,10 +31,6 @@ RenderModeInterface *get_render_mode(RenderState *state) { iface = &rendermode_night; } else if (strcmp(rendermode, "spawn") == 0) { iface = &rendermode_spawn; - } else if (strcmp(rendermode, "overlay") == 0) { - /* TODO temporarily use overlay directly, but later on - you want to use overlay-derived modes */ - iface = &rendermode_overlay; } Py_DECREF(rendermode_py); diff --git a/src/rendermodes.h b/src/rendermodes.h index 6129980..a0d18a4 100644 --- a/src/rendermodes.h +++ b/src/rendermodes.h @@ -115,13 +115,12 @@ extern RenderModeInterface rendermode_night; /* SPAWN */ typedef struct { - /* inherits from night */ - RenderModeNight parent; + /* inherits from overlay */ + RenderModeOverlay parent; /* used to figure out which blocks are spawnable */ - PyObject *solid_blocks, *nospawn_blocks, *fluid_blocks; - /* replacement for black_color */ - PyObject *red_color; + PyObject *nospawn_blocks; + PyObject *skylight, *blocklight; } RenderModeSpawn; extern RenderModeInterface rendermode_spawn; From b24ae6c00ab185cd645bbc9b5310d4873d988efa Mon Sep 17 00:00:00 2001 From: Aaron Griffith Date: Mon, 28 Mar 2011 05:18:41 -0400 Subject: [PATCH 05/25] signposts controls no longer toggle overlays --- web_assets/functions.js | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/web_assets/functions.js b/web_assets/functions.js index 9b7ac12..87e538f 100644 --- a/web_assets/functions.js +++ b/web_assets/functions.js @@ -46,26 +46,23 @@ function createDropDown(title, items) { for (idx in items) { var item = items[idx]; //console.log(item); - label = item.label; - action = item.action; var d = document.createElement("div"); var n = document.createElement("input"); n.type="checkbox"; - $(n).data("label",label); + $(n).data("label",item.label); jQuery(n).click(function(e) { - var t = $(e.target); - action(idx, label, e.target.checked); + item.action(idx, item.label, e.target.checked); }); if (item.checked) { n.checked = true; - action(idx, label, item.checked); + item.action(idx, item.label, item.checked); } dropdownDiv.appendChild(d); d.appendChild(n) var textNode = document.createElement("text"); - textNode.innerHTML = label + "
"; + textNode.innerHTML = item.label + "
"; d.appendChild(textNode); } } From a2782a0499269f512bf232b3c07b7da0f0c7014a Mon Sep 17 00:00:00 2001 From: Andrew Chin Date: Tue, 12 Apr 2011 23:55:17 -0400 Subject: [PATCH 06/25] MSVC is strict about variable declaration --- src/rendermode-overlay.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/rendermode-overlay.c b/src/rendermode-overlay.c index 69d7b04..2a15302 100644 --- a/src/rendermode-overlay.c +++ b/src/rendermode-overlay.c @@ -74,6 +74,7 @@ static void rendermode_overlay_draw(void *data, RenderState *state, PyObject *src, PyObject *mask) { RenderModeOverlay *self = (RenderModeOverlay *)data; unsigned char r, g, b, a; + PyObject *top_block_py, *block_py; /* clear the draw space -- set alpha to 0 within mask */ tint_with_mask(state->img, 255, 255, 255, 0, mask, state->imgx, state->imgy, 0, 0); @@ -86,7 +87,7 @@ rendermode_overlay_draw(void *data, RenderState *state, PyObject *src, PyObject } /* check to be sure this block is solid/fluid */ - PyObject *top_block_py = PyInt_FromLong(top_block); + top_block_py = PyInt_FromLong(top_block); if (PySequence_Contains(self->solid_blocks, top_block_py) || PySequence_Contains(self->fluid_blocks, top_block_py)) { @@ -98,7 +99,7 @@ rendermode_overlay_draw(void *data, RenderState *state, PyObject *src, PyObject } /* check to be sure this block is solid/fluid */ - PyObject *block_py = PyInt_FromLong(state->block); + block_py = PyInt_FromLong(state->block); if (!PySequence_Contains(self->solid_blocks, block_py) && !PySequence_Contains(self->fluid_blocks, block_py)) { From 4036c4a55000aee2f1aead11fece339a16bc4c6d Mon Sep 17 00:00:00 2001 From: Michael Writhe Date: Wed, 13 Apr 2011 10:56:24 -0700 Subject: [PATCH 07/25] indentation and comments added. --- web_assets/functions.js | 262 ++++++++++++++++++++-------------------- 1 file changed, 130 insertions(+), 132 deletions(-) diff --git a/web_assets/functions.js b/web_assets/functions.js index 7bb170f..6c9bda7 100644 --- a/web_assets/functions.js +++ b/web_assets/functions.js @@ -1,14 +1,15 @@ +// var def +var map; // god of the overviewer... bow before the google api. var markerCollection = {}; // holds groups of markers - -var map; - -var markersInit = false; -var regionsInit = false; +var markersInit = false; // only have to load the markers once, this just makes sure we only do it once +var regionsInit = false; // same thing for the regions var prevInfoWindow = null; +// add a popup info window to the marker and then the marker to the map. +// marker is the clickable image on the map with all data. +// item is just the same item in the markers.js function prepareSignMarker(marker, item) { - var c = "

" + item.msg.replace(/\n/g,"
") + "

"; var infowindow = new google.maps.InfoWindow({content: c }); @@ -17,51 +18,53 @@ function prepareSignMarker(marker, item) { prevInfoWindow.close() infowindow.open(map,marker); prevInfoWindow = infowindow - }); + }); } +// reusable function for making drop down menus. +// title = string +// items = array function createDropDown(title, items) { var control = document.createElement("DIV"); control.id = "customControl"; // let's let a style sheet do most of the styling here + var controlText = document.createElement("DIV"); + controlText.innerHTML = title; + var controlBorder = document.createElement("DIV"); controlBorder.id="top"; control.appendChild(controlBorder); - - var controlText = document.createElement("DIV"); - controlBorder.appendChild(controlText); - controlText.innerHTML = title; - var dropdownDiv = document.createElement("DIV"); - - - $(controlText).click(function() { - $(dropdownDiv).toggle(); - - }); - - dropdownDiv.id="dropDown"; control.appendChild(dropdownDiv); dropdownDiv.innerHTML=""; + // add the functionality to toggle visibility of the items + $(controlText).click(function() { + $(dropdownDiv).toggle(); + }); + + // add that control box we've made back to the map. map.controls[google.maps.ControlPosition.TOP_RIGHT].push(control); for (idx in items) { + // create the visible elements of the item var item = items[idx]; - //console.log(item); + //console.log(item); // debug var d = document.createElement("div"); var n = document.createElement("input"); n.type="checkbox"; + // give it a name $(n).data("label",item.label); jQuery(n).click(function(e) { - item.action(idx, item.label, e.target.checked); - }); + item.action(idx, item.label, e.target.checked); + }); + // if its checked, its gotta do something, do that here. if (item.checked) { n.checked = true; item.action(idx, item.label, item.checked); @@ -74,32 +77,28 @@ function createDropDown(title, items) { } } +// need to define the controls including the compass and layer controls. top right! +// input variables are for chumps... and reusable functions. this is neither. function drawMapControls() { - // viewstate link + // viewstate link (little link to where you're looking at the map, normally bottom left) var viewStateDiv = document.createElement('DIV'); - - // - viewStateDiv.id="link"; - - - map.controls[google.maps.ControlPosition.BOTTOM_RIGHT].push(viewStateDiv); - + // add it to the map, bottom left. + map.controls[google.maps.ControlPosition.BOTTOM_LEFT].push(viewStateDiv); // compass rose, in the top right corner var compassDiv = document.createElement('DIV'); - compassDiv.style.padding = '5px'; - var compassImg = document.createElement('IMG'); compassImg.src="compass.png"; compassDiv.appendChild(compassImg); - compassDiv.index = 0; + // add it to the map, top right. map.controls[google.maps.ControlPosition.TOP_RIGHT].push(compassDiv); + // only need to create the control if there are items in the list. as definned in config.js if (signGroups.length > 0) { // signpost display control @@ -107,9 +106,9 @@ function drawMapControls() { for (idx in signGroups) { var item = signGroups[idx]; items.push({"label": item.label, "checked": item.checked, - "action": function(n, l, checked) { - jQuery.each(markerCollection[l], function(i,elem) {elem.setVisible(checked);}); - }}); + "action": function(n, l, checked) { + jQuery.each(markerCollection[l], function(i,elem) {elem.setVisible(checked);}); + }}); } createDropDown("Signposts", items); } @@ -121,26 +120,26 @@ function drawMapControls() { for (idx in overlayMapTypes) { var overlay = overlayMapTypes[idx]; items.push({"label": overlay.name, "checked": false, - "action": function(i, l, checked) { - if (checked) { - map.overlayMapTypes.push(overlay); - } else { - var idx_to_delete = -1; - map.overlayMapTypes.forEach(function(e, j) { - if (e == overlay) { - idx_to_delete = j; - } - }); - if (idx_to_delete >= 0) { - map.overlayMapTypes.removeAt(idx_to_delete); - } + "action": function(i, l, checked) { + if (checked) { + map.overlayMapTypes.push(overlay); + } else { + var idx_to_delete = -1; + map.overlayMapTypes.forEach(function(e, j) { + if (e == overlay) { idx_to_delete = j; } + }); + if (idx_to_delete >= 0) { + map.overlayMapTypes.removeAt(idx_to_delete); } - }}); + } + }}); } createDropDown("Overlays", items); } } +// will be recoded by pi, currently always displays all regions all the time. +// parse the data as definned in the regions.js function initRegions() { if (regionsInit) { return; } @@ -180,12 +179,11 @@ function initRegions() { } } - - +// will initalize all the markers data as found in markers.js +// may need to be reviewed by agrif or someone else... little finicky right now. function initMarkers() { - if (markersInit) { return; } - - markersInit = true; + if (markersInit) { return; } // oh, we've already done this? nevermind, exit the function. + markersInit = true; // now that we've started, dont have to do it twice. // first, give all collections an empty array to work with for (i in signGroups) { @@ -206,8 +204,7 @@ function initMarkers() { map: map, title: jQuery.trim(item.msg), icon: iconURL - }); - + }); continue; } @@ -220,11 +217,11 @@ function initMarkers() { if (testfunc(item)) { matched = true; - if (item.type == 'sign') { iconURL = 'signpost_icon.png';} + // can add custom types of images for externally definned item types, like 'command' here. + if (item.type == 'sign') { iconURL = 'signpost_icon.png'; } - //console.log(signGroup.icon); - if (signGroup.icon) { iconURL = signGroup.icon; - } + //console.log(signGroup.icon); //debug + if (signGroup.icon) { iconURL = signGroup.icon; } var converted = fromWorldToLatLng(item.x, item.y, item.z); var marker = new google.maps.Marker({position: converted, @@ -232,7 +229,7 @@ function initMarkers() { title: jQuery.trim(item.msg), icon: iconURL, visible: false - }); + }); markerCollection[label].push(marker); @@ -271,7 +268,7 @@ function initMarkers() { } } - +// update the link in the viewstate. function makeLink() { var a=location.href.substring(0,location.href.lastIndexOf(location.search)) + "?lat=" + map.getCenter().lat().toFixed(6) @@ -280,6 +277,7 @@ function makeLink() { document.getElementById("link").innerHTML = a; } +// load the map up and add all the functions relevant stuff to the map. function initialize() { var query = location.search.substring(1); @@ -364,87 +362,87 @@ function initialize() { } - // our custom projection maps Latitude to Y, and Longitude to X as normal, - // but it maps the range [0.0, 1.0] to [0, tileSize] in both directions - // so it is easier to position markers, etc. based on their position - // (find their position in the lowest-zoom image, and divide by tileSize) - function MCMapProjection() { +// our custom projection maps Latitude to Y, and Longitude to X as normal, +// but it maps the range [0.0, 1.0] to [0, tileSize] in both directions +// so it is easier to position markers, etc. based on their position +// (find their position in the lowest-zoom image, and divide by tileSize) +function MCMapProjection() { this.inverseTileSize = 1.0 / config.tileSize; - } +} - MCMapProjection.prototype.fromLatLngToPoint = function(latLng) { +MCMapProjection.prototype.fromLatLngToPoint = function(latLng) { var x = latLng.lng() * config.tileSize; var y = latLng.lat() * config.tileSize; return new google.maps.Point(x, y); - }; +}; - MCMapProjection.prototype.fromPointToLatLng = function(point) { +MCMapProjection.prototype.fromPointToLatLng = function(point) { var lng = point.x * this.inverseTileSize; var lat = point.y * this.inverseTileSize; return new google.maps.LatLng(lat, lng); - }; +}; - // helper to get map LatLng from world coordinates - // takes arguments in X, Y, Z order - // (arguments are *out of order*, because within the function we use - // the axes like the rest of Minecraft Overviewer -- with the Z and Y - // flipped from normal minecraft usage.) - function fromWorldToLatLng(x, z, y) - { +// helper to get map LatLng from world coordinates +// takes arguments in X, Y, Z order +// (arguments are *out of order*, because within the function we use +// the axes like the rest of Minecraft Overviewer -- with the Z and Y +// flipped from normal minecraft usage.) +function fromWorldToLatLng(x, z, y) +{ // the width and height of all the highest-zoom tiles combined, inverted var perPixel = 1.0 / (config.tileSize * Math.pow(2, config.maxZoom)); - + // This information about where the center column is may change with a different // drawing implementation -- check it again after any drawing overhauls! - + // point (0, 0, 127) is at (0.5, 0.0) of tile (tiles/2 - 1, tiles/2) // so the Y coordinate is at 0.5, and the X is at 0.5 - ((tileSize / 2) / (tileSize * 2^maxZoom)) // or equivalently, 0.5 - (1 / 2^(maxZoom + 1)) var lng = 0.5 - (1.0 / Math.pow(2, config.maxZoom + 1)); var lat = 0.5; - + // the following metrics mimic those in ChunkRenderer.chunk_render in "chunk.py" // or, equivalently, chunk_render in src/iterate.c - + // each block on X axis adds 12px to x and subtracts 6px from y lng += 12 * x * perPixel; lat -= 6 * x * perPixel; - + // each block on Y axis adds 12px to x and adds 6px to y lng += 12 * y * perPixel; lat += 6 * y * perPixel; - + // each block down along Z adds 12px to y lat += 12 * (128 - z) * perPixel; // add on 12 px to the X coordinate to center our point lng += 12 * perPixel; - + return new google.maps.LatLng(lat, lng); - } +} function getTileUrlGenerator(path, path_base) { - return function(tile, zoom) { - var url = path; - var url_base = ( path_base ? path_base : '' ); - 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) { - url += '/base'; - } else { - for(var z = zoom - 1; z >= 0; --z) { - var x = Math.floor(tile.x / Math.pow(2, z)) % 2; - var y = Math.floor(tile.y / Math.pow(2, z)) % 2; - url += '/' + (x + 2 * y); + return function(tile, zoom) { + var url = path; + var url_base = ( path_base ? path_base : '' ); + 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) { + url += '/base'; + } else { + for(var z = zoom - 1; z >= 0; --z) { + var x = Math.floor(tile.x / Math.pow(2, z)) % 2; + var y = Math.floor(tile.y / Math.pow(2, z)) % 2; + url += '/' + (x + 2 * y); + } } - } - url = url + '.' + config.fileExt; - if(config.cacheMinutes > 0) { - var d = new Date(); - url += '?c=' + Math.floor(d.getTime() / (1000 * 60 * config.cacheMinutes)); - } - return(url_base + url); - } + url = url + '.' + config.fileExt; + if(config.cacheMinutes > 0) { + var d = new Date(); + url += '?c=' + Math.floor(d.getTime() / (1000 * 60 * config.cacheMinutes)); + } + return(url_base + url); + } } var MCMapOptions = new Array; @@ -453,39 +451,39 @@ var mapTypeIdDefault = null; var mapTypeIds = []; var overlayMapTypes = []; for (idx in mapTypeData) { - var view = mapTypeData[idx]; + var view = mapTypeData[idx]; - MCMapOptions[view.label] = { - getTileUrl: getTileUrlGenerator(view.path, view.base), - tileSize: new google.maps.Size(config.tileSize, config.tileSize), - maxZoom: config.maxZoom, - minZoom: 0, - isPng: !(config.fileExt.match(/^png$/i) == null) - }; + MCMapOptions[view.label] = { + getTileUrl: getTileUrlGenerator(view.path, view.base), + tileSize: new google.maps.Size(config.tileSize, config.tileSize), + maxZoom: config.maxZoom, + minZoom: 0, + isPng: !(config.fileExt.match(/^png$/i) == null) + }; - 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 (view.overlay) { - overlayMapTypes.push(MCMapType[view.label]); - } else { + 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 (view.overlay) { + overlayMapTypes.push(MCMapType[view.label]); + } else { if (mapTypeIdDefault == null) { - mapTypeIdDefault = 'mcmap' + view.label; + mapTypeIdDefault = 'mcmap' + view.label; } mapTypeIds.push('mcmap' + view.label); } } - function CoordMapType() { - } +function CoordMapType() { +} - function CoordMapType(tileSize) { +function CoordMapType(tileSize) { this.tileSize = tileSize; - } +} - CoordMapType.prototype.getTile = function(coord, zoom, ownerDocument) { +CoordMapType.prototype.getTile = function(coord, zoom, ownerDocument) { var div = ownerDocument.createElement('DIV'); div.innerHTML = "(" + coord.x + ", " + coord.y + ", " + zoom + ")"; div.innerHTML += "
"; @@ -497,4 +495,4 @@ for (idx in mapTypeData) { div.style.borderWidth = '1px'; div.style.borderColor = '#AAAAAA'; return div; - }; +}; \ No newline at end of file From 1b74ab4b4513854bc27150d45235702e9932375a Mon Sep 17 00:00:00 2001 From: Michael Writhe Date: Wed, 13 Apr 2011 14:45:24 -0600 Subject: [PATCH 08/25] Added the spawn button --- web_assets/functions.js | 31 +++++++++++++++++++++++++++++++ web_assets/style.css | 7 +++++++ 2 files changed, 38 insertions(+) diff --git a/web_assets/functions.js b/web_assets/functions.js index 6c9bda7..6cc1efb 100644 --- a/web_assets/functions.js +++ b/web_assets/functions.js @@ -77,6 +77,31 @@ function createDropDown(title, items) { } } +function HomeControl(controlDiv, map) { + + controlDiv.style.padding = '5px'; + + // Set CSS for the control border + var controlUI = document.createElement('DIV'); + control.id='customControl'; + //controlUI.className='controlUI'; + controlUI.title = 'Click to set the map to Spawn'; + controlDiv.appendChild(controlUI); + + // Set CSS for the control interior + var controlText = document.createElement('DIV'); + //controlText.className='controlText'; + controlText.innerHTML = 'Spawn'; + controlUI.appendChild(controlText); + + // Setup the click event listeners: simply set the map to map center as definned below + google.maps.event.addDomListener(controlUI, 'click', function() { + map.panTo(defaultCenter); + }); + +} + + // need to define the controls including the compass and layer controls. top right! // input variables are for chumps... and reusable functions. this is neither. function drawMapControls() { @@ -96,6 +121,12 @@ function drawMapControls() { compassDiv.index = 0; // add it to the map, top right. map.controls[google.maps.ControlPosition.TOP_RIGHT].push(compassDiv); + + // Spawn button + var homeControlDiv = document.createElement('DIV'); + var homeControl = new HomeControl(homeControlDiv, map); + homeControlDiv.index = 1; + map.controls[google.maps.ControlPosition.TOP_RIGHT].push(homeControlDiv); // only need to create the control if there are items in the list. as definned in config.js diff --git a/web_assets/style.css b/web_assets/style.css index 32389bf..8585eb3 100644 --- a/web_assets/style.css +++ b/web_assets/style.css @@ -40,6 +40,13 @@ body { height: 100%; margin: 0px; padding: 0px ; background-color: #000; } display: none; } +#customControl > div#button { + border: 1px solid #000; + font-size: 12px; + background-color: #fff; + display: none; +} + #link { background-color: #fff; /* fallback */ From 33bfdca28a9b508b427f50ed8a077e061c3f6747 Mon Sep 17 00:00:00 2001 From: Michael Writhe Date: Wed, 13 Apr 2011 15:55:49 -0600 Subject: [PATCH 09/25] fixed css references for Spawn Button --- web_assets/functions.js | 42 ++++++++++++++++++++++++++++------------- 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/web_assets/functions.js b/web_assets/functions.js index 6cc1efb..c1430b7 100644 --- a/web_assets/functions.js +++ b/web_assets/functions.js @@ -2,7 +2,8 @@ var map; // god of the overviewer... bow before the google api. var markerCollection = {}; // holds groups of markers var markersInit = false; // only have to load the markers once, this just makes sure we only do it once -var regionsInit = false; // same thing for the regions +var regionCollection = {}; // holds groups of regions +var regionsInit = false; // only have to load the regions once, this just makes sure we only do it once var prevInfoWindow = null; @@ -82,21 +83,22 @@ function HomeControl(controlDiv, map) { controlDiv.style.padding = '5px'; // Set CSS for the control border - var controlUI = document.createElement('DIV'); - control.id='customControl'; - //controlUI.className='controlUI'; - controlUI.title = 'Click to set the map to Spawn'; - controlDiv.appendChild(controlUI); + var control = document.createElement('DIV'); + control.id='top'; + control.title = 'Click to set the map to Spawn'; + controlDiv.appendChild(control); // Set CSS for the control interior var controlText = document.createElement('DIV'); - //controlText.className='controlText'; controlText.innerHTML = 'Spawn'; - controlUI.appendChild(controlText); + controlText.id='button'; + control.appendChild(controlText); // Setup the click event listeners: simply set the map to map center as definned below - google.maps.event.addDomListener(controlUI, 'click', function() { - map.panTo(defaultCenter); + google.maps.event.addDomListener(control, 'click', function() { + map.panTo(fromWorldToLatLng(config.center[0], + config.center[1], + config.center[2])); }); } @@ -125,6 +127,7 @@ function drawMapControls() { // Spawn button var homeControlDiv = document.createElement('DIV'); var homeControl = new HomeControl(homeControlDiv, map); + homeControlDiv.id = "customControl"; homeControlDiv.index = 1; map.controls[google.maps.ControlPosition.TOP_RIGHT].push(homeControlDiv); @@ -173,11 +176,23 @@ function drawMapControls() { // parse the data as definned in the regions.js function initRegions() { if (regionsInit) { return; } - regionsInit = true; + + /* remove comment on this if we decide to add regionGroups in the config.js + this would let us selectivley show groups of regions based on the name of the region, or flags set. + could be good... + + for (i in regionGroups) { + regionCollection[regionGroups[i].label] = []; + } + remove next line if this is kept. + */ + regionCollection['All Regions'] = []; for (i in regionData) { var region = regionData[i]; + + // pull all the points out of the regions file. var converted = new google.maps.MVCArray(); for (j in region.path) { var point = region.path[j]; @@ -185,7 +200,7 @@ function initRegions() { } if (region.closed) { - new google.maps.Polygon({clickable: false, + var region = new google.maps.Polygon({clickable: false, geodesic: false, map: map, strokeColor: region.color, @@ -197,7 +212,7 @@ function initRegions() { paths: converted }); } else { - new google.maps.Polyline({clickable: false, + var region = new google.maps.Polyline({clickable: false, geodesic: false, map: map, strokeColor: region.color, @@ -207,6 +222,7 @@ function initRegions() { path: converted }); } + regionCollection['All Regions'].push(region); //if we add groups to config.js this will need to be changed. } } From 183d278f810b60eabf185fd2450c8e16f844b68b Mon Sep 17 00:00:00 2001 From: Michael Writhe Date: Wed, 13 Apr 2011 21:59:52 -0600 Subject: [PATCH 10/25] first steps to getting toggle for regions working --- config.js | 5 ++ web_assets/functions.js | 101 ++++++++++++++++++++++++++++------------ 2 files changed, 77 insertions(+), 29 deletions(-) diff --git a/config.js b/config.js index 43b4982..0954116 100644 --- a/config.js +++ b/config.js @@ -33,6 +33,11 @@ var signGroups = [ {label: "All", match: function(s) {return true}}, ]; +//piTODO: document this +var regionGroups = [ + {label: "WorldGuard", 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. diff --git a/web_assets/functions.js b/web_assets/functions.js index c1430b7..ea59263 100644 --- a/web_assets/functions.js +++ b/web_assets/functions.js @@ -23,6 +23,21 @@ function prepareSignMarker(marker, item) { } +// add a popup info window to the region and then the region to the map. +// region is the clickable image on the map with all data. +// item is just the same item in the regions.js +function prepareRegionShape(shape, region) { + var c = "

" + region.label + "

"; + var infowindow = new google.maps.InfoWindow({content: c }); + google.maps.event.addListener(shape, 'click', function() { + if (prevInfoWindow) + prevInfoWindow.close() + infowindow.open(map,shape); + prevInfoWindow = infowindow + }); + +} + // reusable function for making drop down menus. // title = string // items = array @@ -85,7 +100,7 @@ function HomeControl(controlDiv, map) { // Set CSS for the control border var control = document.createElement('DIV'); control.id='top'; - control.title = 'Click to set the map to Spawn'; + control.title = 'Click to center the map on the Spawn'; controlDiv.appendChild(control); // Set CSS for the control interior @@ -147,6 +162,29 @@ function drawMapControls() { createDropDown("Signposts", items); } + // if there are any regions data, lets show the option to hide/show them. + if (regionGroups.length > 0) { + // region display control + + var items = []; + for (idx in regionGroups) { + var item = regionGroups[idx]; + items.push({"label": item.label, "checked": item.checked, + "action": function(n, l, checked) { + if (checked) { + jQuery.each(regionCollection[l], function(i,elem) { + elem.setVisible('visible'); + }); + } else { + jQuery.each(regionCollection[l], function(i,elem) { + elem.setVisible('hidden'); + }); + } + }}); + } + createDropDown("Regions", items); + } + if (overlayMapTypes.length > 0) { // overlay maps control @@ -178,16 +216,9 @@ function initRegions() { if (regionsInit) { return; } regionsInit = true; - /* remove comment on this if we decide to add regionGroups in the config.js - this would let us selectivley show groups of regions based on the name of the region, or flags set. - could be good... - for (i in regionGroups) { regionCollection[regionGroups[i].label] = []; } - remove next line if this is kept. - */ - regionCollection['All Regions'] = []; for (i in regionData) { var region = regionData[i]; @@ -198,31 +229,43 @@ function initRegions() { var point = region.path[j]; converted.push(fromWorldToLatLng(point.x, point.y, point.z)); } + + for (idx in regionGroups) { + var regionGroup = regionGroups[idx]; + var testfunc = regionGroup.match; + var label = regionGroup.label; - if (region.closed) { - var region = new google.maps.Polygon({clickable: false, - geodesic: false, - map: map, - strokeColor: region.color, - strokeOpacity: region.opacity, - strokeWeight: 2, - fillColor: region.color, - fillOpacity: region.opacity * 0.25, - zIndex: i, - paths: converted + if (region.closed) { + var shape = new google.maps.Polygon({ + name: region.label, + clickable: false, + geodesic: false, + map: map, + strokeColor: region.color, + strokeOpacity: region.opacity, + strokeWeight: 2, + fillColor: region.color, + fillOpacity: region.opacity * 0.25, + zIndex: i, + paths: converted }); - } else { - var region = new google.maps.Polyline({clickable: false, - geodesic: false, - map: map, - strokeColor: region.color, - strokeOpacity: region.opacity, - strokeWeight: 2, - zIndex: i, - path: converted + } else { + var shape = new google.maps.Polyline({ + name: region.label, + clickable: false, + geodesic: false, + map: map, + strokeColor: region.color, + strokeOpacity: region.opacity, + strokeWeight: 2, + zIndex: i, + path: converted }); + } + regionCollection[label].push(shape); + prepareRegionShape(shape, region); + } - regionCollection['All Regions'].push(region); //if we add groups to config.js this will need to be changed. } } From d51d863bed28d1c8eed8c685a30c168e279bbfeb Mon Sep 17 00:00:00 2001 From: Michael Writhe Date: Thu, 14 Apr 2011 08:30:56 -0600 Subject: [PATCH 11/25] toggle regions --- web_assets/functions.js | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/web_assets/functions.js b/web_assets/functions.js index ea59263..c109603 100644 --- a/web_assets/functions.js +++ b/web_assets/functions.js @@ -171,15 +171,9 @@ function drawMapControls() { var item = regionGroups[idx]; items.push({"label": item.label, "checked": item.checked, "action": function(n, l, checked) { - if (checked) { - jQuery.each(regionCollection[l], function(i,elem) { - elem.setVisible('visible'); - }); - } else { - jQuery.each(regionCollection[l], function(i,elem) { - elem.setVisible('hidden'); - }); - } + jQuery.each(regionCollection[l], function(i,elem) { + elem.setMap(checked ? map : null); + }); }}); } createDropDown("Regions", items); @@ -240,7 +234,7 @@ function initRegions() { name: region.label, clickable: false, geodesic: false, - map: map, + map: null, strokeColor: region.color, strokeOpacity: region.opacity, strokeWeight: 2, @@ -254,7 +248,7 @@ function initRegions() { name: region.label, clickable: false, geodesic: false, - map: map, + map: null, strokeColor: region.color, strokeOpacity: region.opacity, strokeWeight: 2, @@ -263,8 +257,6 @@ function initRegions() { }); } regionCollection[label].push(shape); - prepareRegionShape(shape, region); - } } } From 5cc1c55ff11871f07673ae777b1e0f27ea4a9146 Mon Sep 17 00:00:00 2001 From: Michael Writhe Date: Thu, 14 Apr 2011 08:32:49 -0600 Subject: [PATCH 12/25] regions toggle --- web_assets/functions.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web_assets/functions.js b/web_assets/functions.js index c109603..e2d1123 100644 --- a/web_assets/functions.js +++ b/web_assets/functions.js @@ -172,7 +172,7 @@ function drawMapControls() { items.push({"label": item.label, "checked": item.checked, "action": function(n, l, checked) { jQuery.each(regionCollection[l], function(i,elem) { - elem.setMap(checked ? map : null); + elem.setMap(checked ? map : null); // Thanks to LeastWeasel for this line! }); }}); } From eac56a6513a8f1942b962cae93d2050e3ec3a1fc Mon Sep 17 00:00:00 2001 From: Michael Writhe Date: Thu, 14 Apr 2011 09:32:47 -0600 Subject: [PATCH 13/25] clickable and now toggleable regions --- config.js | 20 ++++++++++++++++++-- web_assets/functions.js | 24 ++++++++++++++++++++++-- 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/config.js b/config.js index 0954116..90415f4 100644 --- a/config.js +++ b/config.js @@ -33,9 +33,25 @@ var signGroups = [ {label: "All", match: function(s) {return true}}, ]; -//piTODO: document this +/* regionGroups -- A list of region groups. A region can fall into zero, one, or more than one + * group. See below for some examples. + * regions have been designed to work with the + * WorldGuard Overviewer Region importer at https://github.com/pironic/WG2OvR But your host must support php in order + * to run WG2OvR. You can also continue to use any other region format, but your regions + * must now have a label definned in the regions.js. + * + * Required: + * label : string. Displayed in the drop down menu control. + * clickable : boolean. Will determine if we should generate an experimental info window + * that shows details about the clicked region. + * match : function. Applied to each region (from region.js). It returns true if the region + * Should be part of the group. + * + * Optional: + * checked : boolean. Set to true to have the group visible by default + */ var regionGroups = [ - {label: "WorldGuard", match: function(s) {return true}}, + //{label: "WorldGuard", clickable: false, checked: false, match: function(s) {return true}}, ]; /* mapTypeData -- a list of alternate map renderings available. At least one rendering must be diff --git a/web_assets/functions.js b/web_assets/functions.js index e2d1123..1ad1714 100644 --- a/web_assets/functions.js +++ b/web_assets/functions.js @@ -219,20 +219,23 @@ function initRegions() { // pull all the points out of the regions file. var converted = new google.maps.MVCArray(); + var infoPoint = ""; for (j in region.path) { var point = region.path[j]; converted.push(fromWorldToLatLng(point.x, point.y, point.z)); + } for (idx in regionGroups) { var regionGroup = regionGroups[idx]; var testfunc = regionGroup.match; + var clickable = regionGroup.clickable var label = regionGroup.label; if (region.closed) { var shape = new google.maps.Polygon({ name: region.label, - clickable: false, + clickable: clickable, geodesic: false, map: null, strokeColor: region.color, @@ -246,7 +249,7 @@ function initRegions() { } else { var shape = new google.maps.Polyline({ name: region.label, - clickable: false, + clickable: clickable, geodesic: false, map: null, strokeColor: region.color, @@ -257,6 +260,23 @@ function initRegions() { }); } regionCollection[label].push(shape); + + if (clickable) { + // add the region infowindow popup + infowindow = new google.maps.InfoWindow(); + google.maps.event.addListener(shape, 'click', function(e,i) { + + var contentString = "Region: "+this.name+"
"; + contentString += "Clicked Location:
" + e.latLng.lat() + "," + e.latLng.lng() + "
"; + + // Replace our Info Window's content and position + infowindow.setContent(contentString); + infowindow.setPosition(e.latLng); + + infowindow.open(map); + + }); + } } } } From 26c6b686b3f505e7366c65ac503362d96bc50480 Mon Sep 17 00:00:00 2001 From: Michael Writhe Date: Thu, 14 Apr 2011 14:14:31 -0600 Subject: [PATCH 14/25] region label is now optional --- config.js | 8 ++++---- web_assets/functions.js | 28 +++++++--------------------- 2 files changed, 11 insertions(+), 25 deletions(-) diff --git a/config.js b/config.js index 90415f4..ed28985 100644 --- a/config.js +++ b/config.js @@ -37,13 +37,13 @@ var signGroups = [ * group. See below for some examples. * regions have been designed to work with the * WorldGuard Overviewer Region importer at https://github.com/pironic/WG2OvR But your host must support php in order - * to run WG2OvR. You can also continue to use any other region format, but your regions - * must now have a label definned in the regions.js. + * to run WG2OvR. You can also continue to use any other region format. * * Required: * label : string. Displayed in the drop down menu control. * clickable : boolean. Will determine if we should generate an experimental info window - * that shows details about the clicked region. + * that shows details about the clicked region. + * NOTE: if a region (as definned in region.js) does not have a label, this will default to false. * match : function. Applied to each region (from region.js). It returns true if the region * Should be part of the group. * @@ -51,7 +51,7 @@ var signGroups = [ * checked : boolean. Set to true to have the group visible by default */ var regionGroups = [ - //{label: "WorldGuard", clickable: false, checked: false, match: function(s) {return true}}, + //{label: "All", clickable: false, checked: false, match: function(s) {return true}}, ]; /* mapTypeData -- a list of alternate map renderings available. At least one rendering must be diff --git a/web_assets/functions.js b/web_assets/functions.js index 1ad1714..3fb40d6 100644 --- a/web_assets/functions.js +++ b/web_assets/functions.js @@ -20,22 +20,6 @@ function prepareSignMarker(marker, item) { infowindow.open(map,marker); prevInfoWindow = infowindow }); - -} - -// add a popup info window to the region and then the region to the map. -// region is the clickable image on the map with all data. -// item is just the same item in the regions.js -function prepareRegionShape(shape, region) { - var c = "

" + region.label + "

"; - var infowindow = new google.maps.InfoWindow({content: c }); - google.maps.event.addListener(shape, 'click', function() { - if (prevInfoWindow) - prevInfoWindow.close() - infowindow.open(map,shape); - prevInfoWindow = infowindow - }); - } // reusable function for making drop down menus. @@ -232,6 +216,13 @@ function initRegions() { var clickable = regionGroup.clickable var label = regionGroup.label; + if(region.label) { + var name = region.label + } else { + var name = 'rawr'; + clickable = false; // if it doesn't have a name, we dont have to show it. + } + if (region.closed) { var shape = new google.maps.Polygon({ name: region.label, @@ -338,10 +329,7 @@ function initMarkers() { if (item.type == 'sign') { prepareSignMarker(marker, item); } - } - - } if (!matched) { @@ -365,8 +353,6 @@ function initMarkers() { prepareSignMarker(marker, item); } } - - } } From 428b3b937e86fb83361a5f4617a7a5a2f7206cd5 Mon Sep 17 00:00:00 2001 From: Michael Writhe Date: Thu, 14 Apr 2011 17:16:25 -0600 Subject: [PATCH 15/25] Issue brownan/master/323 implemented, icons --- web_assets/functions.js | 35 ++++++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/web_assets/functions.js b/web_assets/functions.js index 3fb40d6..c89a95e 100644 --- a/web_assets/functions.js +++ b/web_assets/functions.js @@ -72,7 +72,7 @@ function createDropDown(title, items) { dropdownDiv.appendChild(d); d.appendChild(n) var textNode = document.createElement("text"); - textNode.innerHTML = item.label + "
"; + textNode.innerHTML = "" + item.label + "
"; d.appendChild(textNode); } } @@ -137,11 +137,20 @@ function drawMapControls() { var items = []; for (idx in signGroups) { - var item = signGroups[idx]; - items.push({"label": item.label, "checked": item.checked, + var signGroup = signGroups[idx]; + var iconURL = signGroup.icon; + if (!iconURL) { iconURL = 'signpost.png'; } + items.push({ + "label": signGroup.label, + "checked": signGroup.checked, + "icon": iconURL, "action": function(n, l, checked) { - jQuery.each(markerCollection[l], function(i,elem) {elem.setVisible(checked);}); - }}); + jQuery.each(markerCollection[l], function(i,elem) { + elem.setVisible(checked); + }); + //alert(signGroup.label); + } + }); } createDropDown("Signposts", items); } @@ -152,8 +161,8 @@ function drawMapControls() { var items = []; for (idx in regionGroups) { - var item = regionGroups[idx]; - items.push({"label": item.label, "checked": item.checked, + var regionGroup = regionGroups[idx]; + items.push({"label": regionGroup.label, "checked": regionGroup.checked, "action": function(n, l, checked) { jQuery.each(regionCollection[l], function(i,elem) { elem.setMap(checked ? map : null); // Thanks to LeastWeasel for this line! @@ -215,7 +224,7 @@ function initRegions() { var testfunc = regionGroup.match; var clickable = regionGroup.clickable var label = regionGroup.label; - + if(region.label) { var name = region.label } else { @@ -225,7 +234,7 @@ function initRegions() { if (region.closed) { var shape = new google.maps.Polygon({ - name: region.label, + name: name, clickable: clickable, geodesic: false, map: null, @@ -239,7 +248,7 @@ function initRegions() { }); } else { var shape = new google.maps.Polyline({ - name: region.label, + name: name, clickable: clickable, geodesic: false, map: null, @@ -282,6 +291,7 @@ function initMarkers() { for (i in signGroups) { markerCollection[signGroups[i].label] = []; } + for (i in markerData) { var item = markerData[i]; @@ -331,7 +341,7 @@ function initMarkers() { } } } - + if (!matched) { // is this signpost doesn't match any of the groups in config.js, add it automatically to the "__others__" group if (item.type == 'sign') { iconURL = 'signpost_icon.png';} @@ -353,6 +363,9 @@ function initMarkers() { prepareSignMarker(marker, item); } } + + + } } From 3f8b445f8e8b77a9872d953db6220f149da095fe Mon Sep 17 00:00:00 2001 From: Michael Writhe Date: Thu, 14 Apr 2011 17:25:03 -0600 Subject: [PATCH 16/25] corrected icons for signposts, regions and overlays dont have icons --- web_assets/functions.js | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/web_assets/functions.js b/web_assets/functions.js index c89a95e..09b0172 100644 --- a/web_assets/functions.js +++ b/web_assets/functions.js @@ -72,7 +72,12 @@ function createDropDown(title, items) { dropdownDiv.appendChild(d); d.appendChild(n) var textNode = document.createElement("text"); - textNode.innerHTML = "" + item.label + "
"; + if(item.icon) { + textNode.innerHTML = "" + item.label + "
"; + } else { + textNode.innerHTML = item.label + "
"; + } + d.appendChild(textNode); } } @@ -162,12 +167,15 @@ function drawMapControls() { var items = []; for (idx in regionGroups) { var regionGroup = regionGroups[idx]; - items.push({"label": regionGroup.label, "checked": regionGroup.checked, + items.push({ + "label": regionGroup.label, + "checked": regionGroup.checked, "action": function(n, l, checked) { - jQuery.each(regionCollection[l], function(i,elem) { - elem.setMap(checked ? map : null); // Thanks to LeastWeasel for this line! - }); - }}); + jQuery.each(regionCollection[l], function(i,elem) { + elem.setMap(checked ? map : null); // Thanks to LeastWeasel for this line! + }); + } + }); } createDropDown("Regions", items); } From 9cc01ddc218d633c363f8384c8cda3c14e89fbf2 Mon Sep 17 00:00:00 2001 From: Aaron Griffith Date: Fri, 15 Apr 2011 21:36:31 -0400 Subject: [PATCH 17/25] removed some blocks from solid_blocks, fixed overlays on snow, half-steps --- chunk.py | 2 +- src/rendermode-overlay.c | 11 +++++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/chunk.py b/chunk.py index dd06ffe..8ed85b8 100644 --- a/chunk.py +++ b/chunk.py @@ -120,7 +120,7 @@ transparent_blocks = set([0, 6, 8, 9, 18, 20, 37, 38, 39, 40, 44, 50, 51, 52, 53 # 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, 23, 24, 25, 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, 92]) + 61, 62, 67, 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]) diff --git a/src/rendermode-overlay.c b/src/rendermode-overlay.c index 2a15302..4029ae9 100644 --- a/src/rendermode-overlay.c +++ b/src/rendermode-overlay.c @@ -76,6 +76,13 @@ rendermode_overlay_draw(void *data, RenderState *state, PyObject *src, PyObject unsigned char r, g, b, a; PyObject *top_block_py, *block_py; + // exactly analogous to edge-line code for these special blocks + int increment=0; + if (state->block == 44) // half-step + increment=6; + else if (state->block == 78) // snow + increment=9; + /* clear the draw space -- set alpha to 0 within mask */ tint_with_mask(state->img, 255, 255, 255, 0, mask, state->imgx, state->imgy, 0, 0); @@ -114,8 +121,8 @@ rendermode_overlay_draw(void *data, RenderState *state, PyObject *src, PyObject /* do the overlay */ if (a > 0) { - alpha_over(state->img, self->white_color, self->facemask_top, state->imgx, state->imgy, 0, 0); - tint_with_mask(state->img, r, g, b, a, self->facemask_top, state->imgx, state->imgy, 0, 0); + alpha_over(state->img, self->white_color, self->facemask_top, state->imgx, state->imgy + increment, 0, 0); + tint_with_mask(state->img, r, g, b, a, self->facemask_top, state->imgx, state->imgy + increment, 0, 0); } } From ad358fa7fdb08f890b532a71239254f188f5964d Mon Sep 17 00:00:00 2001 From: Aaron Griffith Date: Fri, 15 Apr 2011 21:44:59 -0400 Subject: [PATCH 18/25] fixed spawn mode for chunk tops --- src/rendermode-spawn.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/rendermode-spawn.c b/src/rendermode-spawn.c index ebbdc25..0d5138e 100644 --- a/src/rendermode-spawn.c +++ b/src/rendermode-spawn.c @@ -23,6 +23,7 @@ static void get_color(void *data, RenderState *state, RenderModeSpawn* self = (RenderModeSpawn *)data; int x = state->x, y = state->y, z = state->z; + int z_light = z + 1; unsigned char blocklight, skylight; PyObject *block_py; @@ -34,10 +35,6 @@ static void get_color(void *data, RenderState *state, /* default to no overlay, until told otherwise */ *a = 0; - /* if we're at the top, skip */ - if (z == 127) - return; - block_py = PyInt_FromLong(state->block); if (PySequence_Contains(self->nospawn_blocks, block_py)) { /* nothing can spawn on this */ @@ -46,8 +43,12 @@ static void get_color(void *data, RenderState *state, } Py_DECREF(block_py); - blocklight = getArrayByte3D(self->blocklight, x, y, z+1); - skylight = getArrayByte3D(self->skylight, x, y, z+1); + /* if we're at the top, use the top-most light instead */ + if (z_light == 128) + z_light--; + + blocklight = getArrayByte3D(self->blocklight, x, y, z_light); + skylight = getArrayByte3D(self->skylight, x, y, z_light); if (MAX(blocklight, skylight) <= 7) { /* hostile mobs spawn in daylight */ From 558ebe0899efc305c86682dace8f4091b88a32b1 Mon Sep 17 00:00:00 2001 From: Aaron Griffith Date: Sat, 16 Apr 2011 12:03:12 -0400 Subject: [PATCH 19/25] fixed for-loop scoping on createDropDown() closures --- web_assets/functions.js | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/web_assets/functions.js b/web_assets/functions.js index 09b0172..038f0c2 100644 --- a/web_assets/functions.js +++ b/web_assets/functions.js @@ -60,9 +60,11 @@ function createDropDown(title, items) { // give it a name $(n).data("label",item.label); - jQuery(n).click(function(e) { - item.action(idx, item.label, e.target.checked); - }); + jQuery(n).click((function(local_idx, local_item) { + return function(e) { + item.action(local_idx, local_item, e.target.checked); + }; + })(idx, item)); // if its checked, its gotta do something, do that here. if (item.checked) { @@ -149,11 +151,11 @@ function drawMapControls() { "label": signGroup.label, "checked": signGroup.checked, "icon": iconURL, - "action": function(n, l, checked) { - jQuery.each(markerCollection[l], function(i,elem) { + "action": function(n, item, checked) { + jQuery.each(markerCollection[item.label], function(i,elem) { elem.setVisible(checked); }); - //alert(signGroup.label); + //alert(item.label); } }); } @@ -170,8 +172,8 @@ function drawMapControls() { items.push({ "label": regionGroup.label, "checked": regionGroup.checked, - "action": function(n, l, checked) { - jQuery.each(regionCollection[l], function(i,elem) { + "action": function(n, item, checked) { + jQuery.each(regionCollection[item.label], function(i,elem) { elem.setMap(checked ? map : null); // Thanks to LeastWeasel for this line! }); } @@ -186,14 +188,14 @@ function drawMapControls() { var items = []; for (idx in overlayMapTypes) { var overlay = overlayMapTypes[idx]; - items.push({"label": overlay.name, "checked": false, - "action": function(i, l, checked) { + items.push({"label": overlay.name, "checked": false, "overlay": overlay, + "action": function(i, item, checked) { if (checked) { - map.overlayMapTypes.push(overlay); + map.overlayMapTypes.push(item.overlay); } else { var idx_to_delete = -1; map.overlayMapTypes.forEach(function(e, j) { - if (e == overlay) { idx_to_delete = j; } + if (e == item.overlay) { idx_to_delete = j; } }); if (idx_to_delete >= 0) { map.overlayMapTypes.removeAt(idx_to_delete); From 26b35906a41b86018bc54202feb776d19041cabf Mon Sep 17 00:00:00 2001 From: Aaron Griffith Date: Sat, 16 Apr 2011 19:20:21 -0400 Subject: [PATCH 20/25] fix for cave mode merge --- src/rendermode-cave.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rendermode-cave.c b/src/rendermode-cave.c index ae5b565..e51742c 100644 --- a/src/rendermode-cave.c +++ b/src/rendermode-cave.c @@ -219,7 +219,7 @@ rendermode_cave_draw(void *data, RenderState *state, PyObject *src, PyObject *ma g = PyInt_AsLong(PyList_GetItem(self->depth_colors, 1 + z*3)); b = PyInt_AsLong(PyList_GetItem(self->depth_colors, 2 + z*3)); - tint_with_mask(state->img, r, g, b, mask, state->imgx, state->imgy, 0, 0); + tint_with_mask(state->img, r, g, b, 255, mask, state->imgx, state->imgy, 0, 0); } From 05c4083b7e1d15ae79ab9de5adf8165b2ec28123 Mon Sep 17 00:00:00 2001 From: Aaron Griffith Date: Sat, 16 Apr 2011 19:25:40 -0400 Subject: [PATCH 21/25] support for mixed png/jpg tilesets, and overlays with imgformat=jpg --- chunk.py | 4 ++++ config.js | 11 ++++++----- googlemap.py | 16 ++++++---------- quadtree.py | 10 +++++----- web_assets/functions.js | 9 +++++---- 5 files changed, 26 insertions(+), 24 deletions(-) diff --git a/chunk.py b/chunk.py index baf9efc..0fdce6c 100644 --- a/chunk.py +++ b/chunk.py @@ -129,6 +129,10 @@ fluid_blocks = set([8,9,10,11]) # (glass, half blocks) nospawn_blocks = set([20,44]) +# overlay rendermodes +# FIXME hook this into render_modes in setup.py, somehow +overlay_rendermodes = ['spawn'] + class ChunkCorrupt(Exception): pass diff --git a/config.js b/config.js index ed28985..8627c1e 100644 --- a/config.js +++ b/config.js @@ -1,6 +1,5 @@ var config = { - fileExt: '{imgformat}', tileSize: 384, defaultZoom: 2, maxZoom: {maxzoom}, @@ -59,15 +58,17 @@ var regionGroups = [ * the first one being the default. * * Required: - * label : string. Displayed on the control. - * path : string. Location of the rendered tiles. + * label : string. Displayed on the control. + * path : string. Location of the rendered tiles. * Optional: - * base : string. Base of the url path for tile locations, useful for serving tiles from a different server than the js/html server. + * base : string. Base of the url path for tile locations, useful for serving tiles from a different server than the js/html server. + * imgformat : string. File extension used for these tiles. Defaults to png. + * overlay : bool. If true, this tile set will be treated like an overlay var mapTypeData=[ {'label': 'Unlit', 'path': 'tiles'}, // {'label': 'Day', 'path': 'lighting/tiles'}, -// {'label': 'Night', 'path': 'night/tiles'}, +// {'label': 'Night', 'path': 'night/tiles', 'imgformat': 'jpg'}, // {'label': 'Spawn', 'path': 'spawn/tiles', 'base': 'http://example.cdn.amazon.com/'}, // {'label': 'Overlay', 'path': 'overlay/tiles', 'overlay': true} ]; diff --git a/googlemap.py b/googlemap.py index dde8ad7..0f883d3 100644 --- a/googlemap.py +++ b/googlemap.py @@ -23,6 +23,7 @@ from time import strftime, gmtime import json import util +from chunk import overlay_rendermodes """ This module has routines related to generating a Google Maps-based @@ -72,12 +73,11 @@ class MapGen(object): raise ValueError("there must be at least one quadtree to work on") self.destdir = quadtrees[0].destdir - self.imgformat = quadtrees[0].imgformat self.world = quadtrees[0].world self.p = quadtrees[0].p for i in quadtrees: - if i.destdir != self.destdir or i.imgformat != self.imgformat or i.world != self.world: - raise ValueError("all the given quadtrees must have the same destdir") + if i.destdir != self.destdir or i.world != self.world: + raise ValueError("all the given quadtrees must have the same destdir and world") self.quadtrees = quadtrees @@ -85,24 +85,20 @@ class MapGen(object): """Writes out config.js, marker.js, and region.js Copies web assets into the destdir""" zoomlevel = self.p - imgformat = self.imgformat configpath = os.path.join(util.get_program_path(), "config.js") config = open(configpath, 'r').read() config = config.replace( "{maxzoom}", str(zoomlevel)) - config = config.replace( - "{imgformat}", str(imgformat)) config = config.replace("{spawn_coords}", json.dumps(list(self.world.spawn))) # create generated map type data, from given quadtrees - # FIXME hook this into render_modes in setup.py, somehow - overlay_types = ['spawn'] maptypedata = map(lambda q: {'label' : q.rendermode.capitalize(), 'path' : q.tiledir, - 'overlay' : q.rendermode in overlay_types}, + 'overlay' : q.rendermode in overlay_rendermodes, + 'imgformat' : q.imgformat}, self.quadtrees) config = config.replace("{maptypedata}", json.dumps(maptypedata)) @@ -114,7 +110,7 @@ class MapGen(object): blank = Image.new("RGBA", (1,1)) tileDir = os.path.join(self.destdir, quadtree.tiledir) if not os.path.exists(tileDir): os.mkdir(tileDir) - blank.save(os.path.join(tileDir, "blank."+self.imgformat)) + 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) diff --git a/quadtree.py b/quadtree.py index 51cb46e..40773e9 100644 --- a/quadtree.py +++ b/quadtree.py @@ -60,12 +60,12 @@ class QuadtreeGen(object): """ assert(imgformat) self.imgformat = imgformat - self.optimizeimg = optimizeimg - - self.lighting = rendermode in ("lighting", "night", "spawn") - self.night = rendermode in ("night", "spawn") - self.spawn = rendermode in ("spawn",) + self.optimizeimg = optimizeimg self.rendermode = rendermode + + # force png renderformat if we're using an overlay mode + if rendermode in chunk.overlay_rendermodes: + self.imgformat = "png" # Make the destination dir if not os.path.exists(destdir): diff --git a/web_assets/functions.js b/web_assets/functions.js index 038f0c2..9c8b46a 100644 --- a/web_assets/functions.js +++ b/web_assets/functions.js @@ -532,7 +532,7 @@ function fromWorldToLatLng(x, z, y) return new google.maps.LatLng(lat, lng); } -function getTileUrlGenerator(path, path_base) { +function getTileUrlGenerator(path, path_base, path_ext) { return function(tile, zoom) { var url = path; var url_base = ( path_base ? path_base : '' ); @@ -547,7 +547,7 @@ function getTileUrlGenerator(path, path_base) { url += '/' + (x + 2 * y); } } - url = url + '.' + config.fileExt; + url = url + '.' + path_ext; if(config.cacheMinutes > 0) { var d = new Date(); url += '?c=' + Math.floor(d.getTime() / (1000 * 60 * config.cacheMinutes)); @@ -563,13 +563,14 @@ var mapTypeIds = []; var overlayMapTypes = []; for (idx in mapTypeData) { var view = mapTypeData[idx]; + var imgformat = view.imgformat ? view.imgformat : 'png'; MCMapOptions[view.label] = { - getTileUrl: getTileUrlGenerator(view.path, view.base), + getTileUrl: getTileUrlGenerator(view.path, view.base, imgformat), tileSize: new google.maps.Size(config.tileSize, config.tileSize), maxZoom: config.maxZoom, minZoom: 0, - isPng: !(config.fileExt.match(/^png$/i) == null) + isPng: !(imgformat.match(/^png$/i) == null) }; MCMapType[view.label] = new google.maps.ImageMapType(MCMapOptions[view.label]); From 5294b88df9463cc8b8c46f1093d081c5332a43af Mon Sep 17 00:00:00 2001 From: Aaron Griffith Date: Sat, 16 Apr 2011 19:33:21 -0400 Subject: [PATCH 22/25] more fixes for spawn overlay at z == 127 --- src/rendermode-spawn.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/rendermode-spawn.c b/src/rendermode-spawn.c index 0d5138e..ff73780 100644 --- a/src/rendermode-spawn.c +++ b/src/rendermode-spawn.c @@ -43,12 +43,14 @@ static void get_color(void *data, RenderState *state, } Py_DECREF(block_py); - /* if we're at the top, use the top-most light instead */ - if (z_light == 128) - z_light--; + blocklight = getArrayByte3D(self->blocklight, x, y, MAX(127, z_light)); - blocklight = getArrayByte3D(self->blocklight, x, y, z_light); - skylight = getArrayByte3D(self->skylight, x, y, z_light); + /* if we're at the top, force 15 (brightest!) skylight */ + if (z_light == 128) { + skylight = 15; + } else { + skylight = getArrayByte3D(self->skylight, x, y, z_light); + } if (MAX(blocklight, skylight) <= 7) { /* hostile mobs spawn in daylight */ From 5c3b3be41861d08476d3f2261e03cba1dcf42e94 Mon Sep 17 00:00:00 2001 From: Aaron Griffith Date: Sat, 16 Apr 2011 20:08:38 -0400 Subject: [PATCH 23/25] basic rendermode inheritance introspection (no more hacky overlay list) --- chunk.py | 4 -- googlemap.py | 4 +- quadtree.py | 3 +- src/main.c | 10 +++++ src/rendermode-cave.c | 1 + src/rendermode-lighting.c | 1 + src/rendermode-night.c | 1 + src/rendermode-normal.c | 1 + src/rendermode-overlay.c | 1 + src/rendermode-spawn.c | 1 + src/rendermodes.c | 84 ++++++++++++++++++++++++++++++++++++++- src/rendermodes.h | 10 ++++- 12 files changed, 111 insertions(+), 10 deletions(-) diff --git a/chunk.py b/chunk.py index 0fdce6c..baf9efc 100644 --- a/chunk.py +++ b/chunk.py @@ -129,10 +129,6 @@ fluid_blocks = set([8,9,10,11]) # (glass, half blocks) nospawn_blocks = set([20,44]) -# overlay rendermodes -# FIXME hook this into render_modes in setup.py, somehow -overlay_rendermodes = ['spawn'] - class ChunkCorrupt(Exception): pass diff --git a/googlemap.py b/googlemap.py index 0f883d3..b8416b5 100644 --- a/googlemap.py +++ b/googlemap.py @@ -23,7 +23,7 @@ from time import strftime, gmtime import json import util -from chunk import overlay_rendermodes +from c_overviewer import get_render_mode_inheritance """ This module has routines related to generating a Google Maps-based @@ -97,7 +97,7 @@ class MapGen(object): # create generated map type data, from given quadtrees maptypedata = map(lambda q: {'label' : q.rendermode.capitalize(), 'path' : q.tiledir, - 'overlay' : q.rendermode in overlay_rendermodes, + 'overlay' : 'overlay' in get_render_mode_inheritance(q.rendermode), 'imgformat' : q.imgformat}, self.quadtrees) config = config.replace("{maptypedata}", json.dumps(maptypedata)) diff --git a/quadtree.py b/quadtree.py index 40773e9..4b2cc68 100644 --- a/quadtree.py +++ b/quadtree.py @@ -34,6 +34,7 @@ from PIL import Image import nbt import chunk +from c_overviewer import get_render_mode_inheritance from optimizeimages import optimize_image import composite @@ -64,7 +65,7 @@ class QuadtreeGen(object): self.rendermode = rendermode # force png renderformat if we're using an overlay mode - if rendermode in chunk.overlay_rendermodes: + if 'overlay' in get_render_mode_inheritance(rendermode): self.imgformat = "png" # Make the destination dir diff --git a/src/main.c b/src/main.c index ddbe0af..4de7eec 100644 --- a/src/main.c +++ b/src/main.c @@ -25,14 +25,24 @@ PyObject *get_extension_version(PyObject *self, PyObject *args) { static PyMethodDef COverviewerMethods[] = { {"alpha_over", alpha_over_wrap, METH_VARARGS, "alpha over composite function"}, + {"render_loop", chunk_render, METH_VARARGS, "Renders stuffs"}, + {"get_render_modes", get_render_modes, METH_VARARGS, "returns available render modes"}, {"get_render_mode_info", get_render_mode_info, METH_VARARGS, "returns info for a particular render mode"}, + {"get_render_mode_parent", get_render_mode_parent, METH_VARARGS, + "returns parent for a particular render mode"}, + {"get_render_mode_inheritance", get_render_mode_inheritance, METH_VARARGS, + "returns inheritance chain for a particular render mode"}, + {"get_render_mode_children", get_render_mode_children, METH_VARARGS, + "returns (direct) children for a particular render mode"}, + {"extension_version", get_extension_version, METH_VARARGS, "Returns the extension version"}, + {NULL, NULL, 0, NULL} /* Sentinel */ }; diff --git a/src/rendermode-cave.c b/src/rendermode-cave.c index e51742c..f8334b2 100644 --- a/src/rendermode-cave.c +++ b/src/rendermode-cave.c @@ -225,6 +225,7 @@ rendermode_cave_draw(void *data, RenderState *state, PyObject *src, PyObject *ma RenderModeInterface rendermode_cave = { "cave", "render only caves in normal mode", + &rendermode_normal, sizeof(RenderModeCave), rendermode_cave_start, rendermode_cave_finish, diff --git a/src/rendermode-lighting.c b/src/rendermode-lighting.c index 628e5c4..83b38d3 100644 --- a/src/rendermode-lighting.c +++ b/src/rendermode-lighting.c @@ -229,6 +229,7 @@ rendermode_lighting_draw(void *data, RenderState *state, PyObject *src, PyObject RenderModeInterface rendermode_lighting = { "lighting", "draw shadows from the lighting data", + &rendermode_normal, sizeof(RenderModeLighting), rendermode_lighting_start, rendermode_lighting_finish, diff --git a/src/rendermode-night.c b/src/rendermode-night.c index 1e54f05..46ca7c2 100644 --- a/src/rendermode-night.c +++ b/src/rendermode-night.c @@ -61,6 +61,7 @@ rendermode_night_draw(void *data, RenderState *state, PyObject *src, PyObject *m RenderModeInterface rendermode_night = { "night", "like \"lighting\", except at night", + &rendermode_lighting, sizeof(RenderModeNight), rendermode_night_start, rendermode_night_finish, diff --git a/src/rendermode-normal.c b/src/rendermode-normal.c index 983050c..9f139a3 100644 --- a/src/rendermode-normal.c +++ b/src/rendermode-normal.c @@ -221,6 +221,7 @@ rendermode_normal_draw(void *data, RenderState *state, PyObject *src, PyObject * RenderModeInterface rendermode_normal = { "normal", "nothing special, just render the blocks", + NULL, sizeof(RenderModeNormal), rendermode_normal_start, rendermode_normal_finish, diff --git a/src/rendermode-overlay.c b/src/rendermode-overlay.c index 4029ae9..fb4e765 100644 --- a/src/rendermode-overlay.c +++ b/src/rendermode-overlay.c @@ -128,6 +128,7 @@ rendermode_overlay_draw(void *data, RenderState *state, PyObject *src, PyObject RenderModeInterface rendermode_overlay = { "overlay", "base rendermode for informational overlays", + NULL, sizeof(RenderModeOverlay), rendermode_overlay_start, rendermode_overlay_finish, diff --git a/src/rendermode-spawn.c b/src/rendermode-spawn.c index ff73780..edd9c44 100644 --- a/src/rendermode-spawn.c +++ b/src/rendermode-spawn.c @@ -109,6 +109,7 @@ rendermode_spawn_draw(void *data, RenderState *state, PyObject *src, PyObject *m RenderModeInterface rendermode_spawn = { "spawn", "draws a red overlay where monsters can spawn at night", + &rendermode_overlay, sizeof(RenderModeSpawn), rendermode_spawn_start, rendermode_spawn_finish, diff --git a/src/rendermodes.c b/src/rendermodes.c index 08ac140..5ee7d54 100644 --- a/src/rendermodes.c +++ b/src/rendermodes.c @@ -98,5 +98,87 @@ PyObject *get_render_mode_info(PyObject *self, PyObject *args) { } Py_DECREF(info); - Py_RETURN_NONE; + return PyErr_Format(PyExc_ValueError, "invalid rendermode: \"%s\"", rendermode); +} + +/* bindings -- get parent's name */ +PyObject *get_render_mode_parent(PyObject *self, PyObject *args) { + const char *rendermode; + unsigned int i; + if (!PyArg_ParseTuple(args, "s", &rendermode)) + return NULL; + + for (i = 0; render_modes[i] != NULL; i++) { + if (strcmp(render_modes[i]->name, rendermode) == 0) { + if (render_modes[i]->parent) { + /* has parent */ + return PyString_FromString(render_modes[i]->parent->name); + } else { + /* no parent */ + Py_RETURN_NONE; + } + } + } + + return PyErr_Format(PyExc_ValueError, "invalid rendermode: \"%s\"", rendermode); +} + +/* bindings -- get list of inherited parents */ +PyObject *get_render_mode_inheritance(PyObject *self, PyObject *args) { + const char *rendermode; + PyObject *parents; + unsigned int i; + if (!PyArg_ParseTuple(args, "s", &rendermode)) + return NULL; + + parents = PyList_New(0); + if (!parents) + return NULL; + + RenderModeInterface *iface = NULL; + for (i = 0; render_modes[i] != NULL; i++) { + if (strcmp(render_modes[i]->name, rendermode) == 0) { + iface = render_modes[i]; + break; + } + } + + if (!iface) { + Py_DECREF(parents); + return PyErr_Format(PyExc_ValueError, "invalid rendermode: \"%s\"", rendermode); + } + + while (iface) { + PyObject *name = PyString_FromString(iface->name); + PyList_Append(parents, name); + Py_DECREF(name); + + iface = iface->parent; + } + + PyList_Reverse(parents); + return parents; +} + +/* bindings -- get list of (direct) children */ +PyObject *get_render_mode_children(PyObject *self, PyObject *args) { + const char *rendermode; + PyObject *children; + unsigned int i; + if (!PyArg_ParseTuple(args, "s", &rendermode)) + return NULL; + + children = PyList_New(0); + if (!children) + return NULL; + + for (i = 0; render_modes[i] != NULL; i++) { + if (render_modes[i]->parent && strcmp(render_modes[i]->parent->name, rendermode) == 0) { + PyObject *child_name = PyString_FromString(render_modes[i]->name); + PyList_Append(children, child_name); + Py_DECREF(child_name); + } + } + + return children; } diff --git a/src/rendermodes.h b/src/rendermodes.h index 980968c..fd0b479 100644 --- a/src/rendermodes.h +++ b/src/rendermodes.h @@ -38,12 +38,15 @@ #include /* rendermode interface */ -typedef struct { +typedef struct _RenderModeInterface RenderModeInterface; +struct _RenderModeInterface { /* the name of this mode */ const char* name; /* the short description of this render mode */ const char* description; + /* the rendermode this is derived from, or NULL */ + RenderModeInterface *parent; /* the size of the local storage for this rendermode */ unsigned int data_size; @@ -54,13 +57,16 @@ typedef struct { int (*occluded)(void *, RenderState *); /* last two arguments are img and mask, from texture lookup */ void (*draw)(void *, RenderState *, PyObject *, PyObject *); -} RenderModeInterface; +}; /* figures out the render mode to use from the given ChunkRenderer */ RenderModeInterface *get_render_mode(RenderState *state); /* python bindings */ PyObject *get_render_modes(PyObject *self, PyObject *args); PyObject *get_render_mode_info(PyObject *self, PyObject *args); +PyObject *get_render_mode_parent(PyObject *self, PyObject *args); +PyObject *get_render_mode_inheritance(PyObject *self, PyObject *args); +PyObject *get_render_mode_children(PyObject *self, PyObject *args); /* individual rendermode interface declarations follow */ From c790ad51020d4b6899ebcadf8bc13895938b471d Mon Sep 17 00:00:00 2001 From: Aaron Griffith Date: Mon, 18 Apr 2011 11:03:29 -0400 Subject: [PATCH 24/25] windows build fixes --- src/rendermodes.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rendermodes.c b/src/rendermodes.c index 5ee7d54..43658e1 100644 --- a/src/rendermodes.c +++ b/src/rendermodes.c @@ -128,6 +128,7 @@ PyObject *get_render_mode_inheritance(PyObject *self, PyObject *args) { const char *rendermode; PyObject *parents; unsigned int i; + RenderModeInterface *iface = NULL; if (!PyArg_ParseTuple(args, "s", &rendermode)) return NULL; @@ -135,7 +136,6 @@ PyObject *get_render_mode_inheritance(PyObject *self, PyObject *args) { if (!parents) return NULL; - RenderModeInterface *iface = NULL; for (i = 0; render_modes[i] != NULL; i++) { if (strcmp(render_modes[i]->name, rendermode) == 0) { iface = render_modes[i]; From 5f6ceebc8156aec7eb9a8e5de0ae5d7f84687af1 Mon Sep 17 00:00:00 2001 From: Aaron Griffith Date: Mon, 18 Apr 2011 11:02:39 -0700 Subject: [PATCH 25/25] fixed MIN/MAX mixup that prevented spawn overlay from working --- src/rendermode-spawn.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rendermode-spawn.c b/src/rendermode-spawn.c index edd9c44..498ed17 100644 --- a/src/rendermode-spawn.c +++ b/src/rendermode-spawn.c @@ -43,7 +43,7 @@ static void get_color(void *data, RenderState *state, } Py_DECREF(block_py); - blocklight = getArrayByte3D(self->blocklight, x, y, MAX(127, z_light)); + blocklight = getArrayByte3D(self->blocklight, x, y, MIN(127, z_light)); /* if we're at the top, force 15 (brightest!) skylight */ if (z_light == 128) {