0

Merge branch 'overlays' into dtt-c-render

Conflicts:
	quadtree.py
	web_assets/functions.js
This commit is contained in:
Aaron Griffith
2011-04-18 18:29:51 -04:00
18 changed files with 720 additions and 295 deletions

View File

@@ -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])
@@ -463,7 +463,7 @@ def generate_facemasks():
return (top, left, right)
facemasks = generate_facemasks()
black_color = Image.new("RGB", (24,24), (0,0,0))
red_color = Image.new("RGB", (24,24), (229,36,38))
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():

View File

@@ -1,6 +1,5 @@
var config = {
fileExt: '{imgformat}',
tileSize: 384,
defaultZoom: 2,
maxZoom: {maxzoom},
@@ -33,6 +32,27 @@ var signGroups = [
{label: "All", match: function(s) {return true}},
];
/* 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.
*
* 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.
* 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.
*
* Optional:
* checked : boolean. Set to true to have the group visible by default
*/
var regionGroups = [
//{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
* listed. When more than one are provided, controls to switch between them are provided, with
* the first one being the default.
@@ -42,12 +62,15 @@ var signGroups = [
* 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.
* 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': 'Spawn', 'path': 'spawn/tiles', 'base': 'http://example.cdn.amazon.com/'}
// {'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}
];
*/

View File

@@ -23,6 +23,7 @@ from time import strftime, gmtime
import json
import util
from c_overviewer import get_render_mode_inheritance
"""
This module has routines related to generating a Google Maps-based
@@ -73,12 +74,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
@@ -86,14 +86,11 @@ 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)))
@@ -102,7 +99,10 @@ class MapGen(object):
# create generated map type data, from given quadtrees
maptypedata = map(lambda q: {'label' : q.rendermode.capitalize(),
'path' : q.tiledir}, self.quadtrees)
'path' : q.tiledir,
'overlay' : 'overlay' in get_render_mode_inheritance(q.rendermode),
'imgformat' : q.imgformat},
self.quadtrees)
config = config.replace("{maptypedata}", json.dumps(maptypedata))
with open(os.path.join(self.destdir, "config.js"), 'w') as output:
@@ -114,7 +114,7 @@ class MapGen(object):
for quadtree in self.quadtrees:
tileDir = os.path.join(self.destdir, quadtree.tiledir)
if not os.path.exists(tileDir): os.mkdir(tileDir)
blank.save(os.path.join(tileDir, "blank."+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)

View File

@@ -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
@@ -62,12 +63,12 @@ class QuadtreeGen(object):
self.imgformat = imgformat
self.optimizeimg = optimizeimg
self.bgcolor = bgcolor
self.lighting = rendermode in ("lighting", "night", "spawn")
self.night = rendermode in ("night", "spawn")
self.spawn = rendermode in ("spawn",)
self.rendermode = rendermode
# force png renderformat if we're using an overlay mode
if 'overlay' in get_render_mode_inheritance(rendermode):
self.imgformat = "png"
# Make the destination dir
if not os.path.exists(destdir):
os.makedirs(os.path.abspath(destdir))

View File

@@ -54,10 +54,14 @@ try:
except:
pil_include = []
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', 'src/rendermode-cave.c']
# used to figure out what files to compile
render_modes = ['normal', 'overlay', 'lighting', 'night', 'spawn', 'cave']
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/Draw.c']
c_overviewer_includes = ['src/overviewer.h', 'src/rendermodes.h']
setup_kwargs['ext_modules'].append(Extension('c_overviewer', c_overviewer_files, include_dirs=['.', numpy_include] + pil_include, depends=c_overviewer_includes, extra_link_args=[]))
# tell build_ext to build the extension in-place

View File

@@ -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;
}
}

View File

@@ -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 */
};

View File

@@ -48,7 +48,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 */

View File

@@ -219,12 +219,13 @@ 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);
}
RenderModeInterface rendermode_cave = {
"cave", "render only caves in normal mode",
&rendermode_normal,
sizeof(RenderModeCave),
rendermode_cave_start,
rendermode_cave_finish,

View File

@@ -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,

View File

@@ -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,

View File

@@ -169,7 +169,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);
}
}
@@ -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,

137
src/rendermode-overlay.c Normal file
View File

@@ -0,0 +1,137 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#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;
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");
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;
}
static void
rendermode_overlay_finish(void *data, RenderState *state) {
RenderModeOverlay *self = (RenderModeOverlay *)data;
Py_DECREF(self->facemask_top);
Py_DECREF(self->white_color);
Py_DECREF(self->solid_blocks);
Py_DECREF(self->fluid_blocks);
}
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;
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);
/* 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 */
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 */
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);
/* get our color info */
self->get_color(data, state, &r, &g, &b, &a);
/* do the overlay */
if (a > 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);
}
}
RenderModeInterface rendermode_overlay = {
"overlay", "base rendermode for informational overlays",
NULL,
sizeof(RenderModeOverlay),
rendermode_overlay_start,
rendermode_overlay_finish,
rendermode_overlay_occluded,
rendermode_overlay_draw,
};

View File

@@ -18,21 +18,66 @@
#include "overviewer.h"
#include <math.h>
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;
int z_light = z + 1;
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;
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, MIN(127, 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 */
*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;
}
@@ -42,83 +87,29 @@ rendermode_spawn_finish(void *data, RenderState *state) {
/* first free all *our* stuff */
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 = {
"spawn", "draws red where monsters can spawn at night",
"spawn", "draws a red overlay where monsters can spawn at night",
&rendermode_overlay,
sizeof(RenderModeSpawn),
rendermode_spawn_start,
rendermode_spawn_finish,

View File

@@ -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;
RenderModeInterface *iface = NULL;
if (!PyArg_ParseTuple(args, "s", &rendermode))
return NULL;
parents = PyList_New(0);
if (!parents)
return 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;
}

View File

@@ -38,12 +38,15 @@
#include <Python.h>
/* 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 */
@@ -79,6 +85,20 @@ typedef struct {
} RenderModeNormal;
extern RenderModeInterface rendermode_normal;
/* OVERLAY */
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;
/* 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;
/* LIGHTING */
typedef struct {
/* inherits from normal render mode */
@@ -109,13 +129,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;

View File

@@ -1,14 +1,16 @@
// 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 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;
// 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 = "<div class=\"infoWindow\"><img src=\"signpost.png\" /><p>" + item.msg.replace(/\n/g,"<br/>") + "</p></div>";
var infowindow = new google.maps.InfoWindow({content: c
});
@@ -18,120 +20,234 @@ function prepareSignMarker(marker, item) {
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
function drawMapControls() {
// viewstate link
var viewStateDiv = document.createElement('DIV');
//<div id="link" style="border:1px solid black;background-color:white;color:black;position:absolute;top:5px;right:5px"></div>
viewStateDiv.id="link";
map.controls[google.maps.ControlPosition.BOTTOM_RIGHT].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;
map.controls[google.maps.ControlPosition.TOP_RIGHT].push(compassDiv);
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 controlText = document.createElement("DIV");
controlText.innerHTML = title;
var controlBorder = document.createElement("DIV");
controlBorder.id="top";
signControl.appendChild(controlBorder);
var controlText = document.createElement("DIV");
control.appendChild(controlBorder);
controlBorder.appendChild(controlText);
controlText.innerHTML = "Signposts";
var dropdownDiv = document.createElement("DIV");
$(controlText).click(function() {
$(dropdownDiv).toggle();
});
dropdownDiv.id="dropDown";
signControl.appendChild(dropdownDiv);
control.appendChild(dropdownDiv);
dropdownDiv.innerHTML="";
map.controls[google.maps.ControlPosition.TOP_RIGHT].push(signControl);
// 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);
var hasSignGroup = false;
for (idx in signGroups) {
var item = signGroups[idx];
//console.log(item);
label = item.label;
hasSignGroup = true;
for (idx in items) {
// create the visible elements of the item
var item = items[idx];
//console.log(item); // debug
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);});
});
// give it a name
$(n).data("label",item.label);
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) {
n.checked = true;
jQuery.each(markerCollection[label], function(i,elem) {elem.setVisible(n.checked);});
item.action(idx, item.label, item.checked);
}
dropdownDiv.appendChild(d);
d.appendChild(n)
var textNode = document.createElement("text");
textNode.innerHTML = label + "<br/>";
d.appendChild(textNode);
if(item.icon) {
textNode.innerHTML = "<img width='15' height='15' src='"+item.icon+"'>" + item.label + "<br/>";
} else {
textNode.innerHTML = item.label + "<br/>";
}
d.appendChild(textNode);
}
}
function HomeControl(controlDiv, map) {
controlDiv.style.padding = '5px';
// Set CSS for the control border
var control = document.createElement('DIV');
control.id='top';
control.title = 'Click to center the map on the Spawn';
controlDiv.appendChild(control);
// Set CSS for the control interior
var controlText = document.createElement('DIV');
controlText.innerHTML = 'Spawn';
controlText.id='button';
control.appendChild(controlText);
// Setup the click event listeners: simply set the map to map center as definned below
google.maps.event.addDomListener(control, 'click', function() {
map.panTo(fromWorldToLatLng(config.center[0],
config.center[1],
config.center[2]));
});
}
// 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 (little link to where you're looking at the map, normally bottom left)
var viewStateDiv = document.createElement('DIV');
viewStateDiv.id="link";
// 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);
// 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);
// 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
var items = [];
for (idx in signGroups) {
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, item, checked) {
jQuery.each(markerCollection[item.label], function(i,elem) {
elem.setVisible(checked);
});
//alert(item.label);
}
});
}
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 regionGroup = regionGroups[idx];
items.push({
"label": regionGroup.label,
"checked": regionGroup.checked,
"action": function(n, item, checked) {
jQuery.each(regionCollection[item.label], function(i,elem) {
elem.setMap(checked ? map : null); // Thanks to LeastWeasel for this line!
});
}
});
}
createDropDown("Regions", 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, "overlay": overlay,
"action": function(i, item, checked) {
if (checked) {
map.overlayMapTypes.push(item.overlay);
} else {
var idx_to_delete = -1;
map.overlayMapTypes.forEach(function(e, j) {
if (e == item.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; }
regionsInit = true;
for (i in regionGroups) {
regionCollection[regionGroups[i].label] = [];
}
for (i in regionData) {
var region = regionData[i];
// 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.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) {
new google.maps.Polygon({clickable: false,
var shape = new google.maps.Polygon({
name: name,
clickable: clickable,
geodesic: false,
map: map,
map: null,
strokeColor: region.color,
strokeOpacity: region.opacity,
strokeWeight: 2,
@@ -141,9 +257,11 @@ function initRegions() {
paths: converted
});
} else {
new google.maps.Polyline({clickable: false,
var shape = new google.maps.Polyline({
name: name,
clickable: clickable,
geodesic: false,
map: map,
map: null,
strokeColor: region.color,
strokeOpacity: region.opacity,
strokeWeight: 2,
@@ -151,21 +269,40 @@ function initRegions() {
path: converted
});
}
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 = "<b>Region: "+this.name+"</b><br />";
contentString += "Clicked Location: <br />" + e.latLng.lat() + "," + e.latLng.lng() + "<br />";
// Replace our Info Window's content and position
infowindow.setContent(contentString);
infowindow.setPosition(e.latLng);
infowindow.open(map);
});
}
}
}
}
// 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) {
markerCollection[signGroups[i].label] = [];
}
for (i in markerData) {
var item = markerData[i];
@@ -181,7 +318,6 @@ function initMarkers() {
title: jQuery.trim(item.msg),
icon: iconURL
});
continue;
}
@@ -208,11 +344,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,
@@ -227,10 +363,7 @@ function initMarkers() {
if (item.type == 'sign') {
prepareSignMarker(marker, item);
}
}
}
if (!matched) {
@@ -256,10 +389,11 @@ function initMarkers() {
}
}
}
// update the link in the viewstate.
function makeLink() {
var displayZoom = map.getZoom();
if (displayZoom == config.maxZoom) {
@@ -277,6 +411,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);
@@ -387,33 +522,33 @@ 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));
@@ -444,12 +579,12 @@ function initialize() {
lng += 12 * perPixel;
return new google.maps.LatLng(lat, lng);
}
}
// NOTE: X, Y and Z in this function are Minecraft world definitions
// (that is, X is horizontal, Y is altitude and Z is vertical).
function fromLatLngToWorld(lat, lng)
{
// NOTE: X, Y and Z in this function are Minecraft world definitions
// (that is, X is horizontal, Y is altitude and Z is vertical).
function fromLatLngToWorld(lat, lng)
{
// Initialize world x/y/z object to be returned
var xyz = Array();
xyz.x = 0;
@@ -479,9 +614,9 @@ function initialize() {
xyz.z -= 64 + 2;
return xyz;
}
}
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 : '' );
@@ -496,7 +631,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));
@@ -509,35 +644,42 @@ var MCMapOptions = new Array;
var MCMapType = new Array;
var mapTypeIdDefault = null;
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]);
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;
}
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 += "<br />";
@@ -549,4 +691,4 @@ for (idx in mapTypeData) {
div.style.borderWidth = '1px';
div.style.borderColor = '#AAAAAA';
return div;
};
};

View File

@@ -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,14 @@ 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;
display: none;
}
#customControl > div#button {
border: 1px solid #000;
font-size: 12px;
background-color: #fff;