initial support for map overlays
This commit is contained in:
1
chunk.py
1
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():
|
||||
|
||||
@@ -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}
|
||||
];
|
||||
*/
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
|
||||
8
setup.py
8
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/)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
75
src/rendermode-overlay.c
Normal file
75
src/rendermode-overlay.c
Normal file
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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,
|
||||
};
|
||||
@@ -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);
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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 + "<br/>";
|
||||
d.appendChild(textNode);
|
||||
}
|
||||
}
|
||||
|
||||
function drawMapControls() {
|
||||
|
||||
@@ -43,68 +98,42 @@ 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;
|
||||
var items = [];
|
||||
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";
|
||||
items.push({"label": item.label, "checked": item.checked,
|
||||
"action": function(n, l, checked) {
|
||||
jQuery.each(markerCollection[l], function(i,elem) {elem.setVisible(checked);});
|
||||
}});
|
||||
}
|
||||
createDropDown("Signposts", items);
|
||||
}
|
||||
|
||||
$(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 (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 (item.checked) {
|
||||
n.checked = true;
|
||||
jQuery.each(markerCollection[label], function(i,elem) {elem.setVisible(n.checked);});
|
||||
if (idx_to_delete >= 0) {
|
||||
map.overlayMapTypes.removeAt(idx_to_delete);
|
||||
}
|
||||
dropdownDiv.appendChild(d);
|
||||
d.appendChild(n)
|
||||
var textNode = document.createElement("text");
|
||||
textNode.innerHTML = label + "<br/>";
|
||||
d.appendChild(textNode);
|
||||
|
||||
}
|
||||
|
||||
|
||||
}});
|
||||
}
|
||||
createDropDown("Overlays", items);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,11 +457,16 @@ 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 (view.overlay) {
|
||||
overlayMapTypes.push(MCMapType[view.label]);
|
||||
} else {
|
||||
if (mapTypeIdDefault == null) {
|
||||
mapTypeIdDefault = 'mcmap' + view.label;
|
||||
}
|
||||
mapTypeIds.push('mcmap' + view.label);
|
||||
}
|
||||
}
|
||||
|
||||
function CoordMapType() {
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user