0

Merge branch 'rendermode-options'

This commit is contained in:
Aaron Griffith
2011-09-09 14:30:55 -04:00
18 changed files with 1109 additions and 197 deletions

View File

@@ -104,8 +104,10 @@ def main():
parser.add_option("-z", "--zoom", dest="zoom", helptext="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", advanced=True)
parser.add_option("--regionlist", dest="regionlist", helptext="A file containing, on each line, a path to a regionlist to update. Instead of scanning the world directory for regions, it will just use this list. Normal caching rules still apply.")
parser.add_option("--forcerender", dest="forcerender", helptext="Force re-rendering the entire map (or the given regionlist). Useful for re-rendering without deleting it.", action="store_true")
parser.add_option("--rendermodes", dest="rendermode", helptext="Specifies the render types, separated by ',', ':', or '/'. Use --list-rendermodes to list them all.", type="choice", choices=avail_rendermodes, required=True, default=avail_rendermodes[0], listify=True)
parser.add_option("--rendermodes", dest="rendermode", helptext="Specifies the render types, separated by ',', ':', or '/'. Use --list-rendermodes to list them all.", type="choice", required=True, default=avail_rendermodes[0], listify=True)
parser.add_option("--list-rendermodes", dest="list_rendermodes", action="store_true", helptext="List available render modes and exit.", commandLineOnly=True)
parser.add_option("--rendermode-options", dest="rendermode_options", default={}, advanced=True)
parser.add_option("--custom-rendermodes", dest="custom_rendermodes", default={}, advanced=True)
parser.add_option("--imgformat", dest="imgformat", helptext="The image output format to use. Currently supported: png(default), jpg.", advanced=True )
parser.add_option("--imgquality", dest="imgquality", default=95, helptext="Specify the quality of image output when using imgformat=\"jpg\".", type="int", advanced=True)
parser.add_option("--bg-color", dest="bg_color", helptext="Configures the background color for the GoogleMap output. Specify in #RRGGBB format", advanced=True, type="string", default="#1A1A1A")
@@ -137,11 +139,14 @@ def main():
pass
sys.exit(0)
# setup c_overviewer rendermode customs / options
for mode in options.custom_rendermodes:
c_overviewer.add_custom_render_mode(mode, options.custom_rendermodes[mode])
for mode in options.rendermode_options:
c_overviewer.set_render_mode_options(mode, options.rendermode_options[mode])
if options.list_rendermodes:
rendermode_info = map(c_overviewer.get_render_mode_info, avail_rendermodes)
name_width = max(map(lambda i: len(i['name']), rendermode_info))
for info in rendermode_info:
print "{name:{0}} {description}".format(name_width, **info)
list_rendermodes()
sys.exit(0)
if options.check_terrain:
@@ -161,7 +166,6 @@ def main():
logging.info("Hash of terrain.png file is: %s", h.hexdigest())
sys.exit(0)
if options.advanced_help:
parser.advanced_help()
sys.exit(0)
@@ -329,6 +333,47 @@ dir but you forgot to put quotes around the directory, since it contains spaces.
m.finalize()
def list_rendermodes():
"Prints out a pretty list of supported rendermodes"
def print_mode_tree(line_max, mode, prefix='', last=False):
"Prints out a mode tree for the given mode, with an indent."
try:
info = c_overviewer.get_render_mode_info(mode)
except ValueError:
info = {}
print prefix + '+-', mode,
if 'description' in info:
print " " * (line_max - len(prefix) - len(mode) - 2),
print info['description']
else:
print
children = c_overviewer.get_render_mode_children(mode)
for child in children:
child_last = (child == children[-1])
if last:
child_prefix = ' '
else:
child_prefix = '| '
print_mode_tree(line_max, child, prefix=prefix + child_prefix, last=child_last)
avail_rendermodes = c_overviewer.get_render_modes()
line_lengths = {}
parent_modes = []
for mode in avail_rendermodes:
inherit = c_overviewer.get_render_mode_inheritance(mode)
if not inherit[0] in parent_modes:
parent_modes.append(inherit[0])
line_lengths[mode] = 2 * len(inherit) + 1 + len(mode)
line_length = max(line_lengths.values())
for mode in parent_modes:
print_mode_tree(line_length, mode, last=(mode == parent_modes[-1]))
def list_worlds():
"Prints out a brief summary of saves found in the default directory"
print

View File

@@ -306,6 +306,13 @@ class ChunkRenderer(object):
return self._up_right_skylight
up_right_skylight = property(_load_up_right_skylight)
def _load_up_right_blocklight(self):
"""Loads and returns lower-right blocklight array"""
if not hasattr(self, "_up_right_blocklight"):
self._load_up_right()
return self._up_right_blocklight
up_right_blocklight = property(_load_up_right_blocklight)
def _load_up_left(self):
"""Loads and sets data from upper-left chunk"""
chunk_path = self.world.get_region_path(self.chunkX, self.chunkY - 1)
@@ -333,6 +340,13 @@ class ChunkRenderer(object):
return self._up_left_skylight
up_left_skylight = property(_load_up_left_skylight)
def _load_up_left_blocklight(self):
"""Loads and returns lower-left blocklight array"""
if not hasattr(self, "_up_left_blocklight"):
self._load_up_left()
return self._up_left_blocklight
up_left_blocklight = property(_load_up_left_blocklight)
def chunk_render(self, img=None, xoff=0, yoff=0, cave=False):
"""Renders a chunk with the given parameters, and returns the image.
If img is given, the chunk is rendered to that image object. Otherwise,
@@ -413,6 +427,10 @@ def generate_facemasks():
for x,y in [(3,4), (7,2), (11,0)]:
top.putpixel((x,y), 255)
# special fix for chunk boundary stipple
for x,y in [(13,11), (17,9), (21,7)]:
right.putpixel((x,y), 0)
return (top, left, right)
facemasks = generate_facemasks()
black_color = Image.new("RGB", (24,24), (0,0,0))

View File

@@ -191,7 +191,7 @@ class ConfigOptionParser(object):
elif a['type'] == "long":
return long(value)
elif a['type'] == "choice":
if value not in a['choices']:
if ('choices' in a) and (value not in a['choices']):
logging.error("The value '%s' is not valid for config parameter '%s'" % (value, a['dest']))
sys.exit(1)
return value

View File

@@ -23,7 +23,7 @@ from time import strftime, localtime
import json
import util
from c_overviewer import get_render_mode_inheritance
from c_overviewer import get_render_mode_inheritance, get_render_mode_info
import overviewer_version
"""
@@ -130,8 +130,15 @@ class MapGen(object):
#config = config.replace("{bg_color}", self.bg_color)
# helper function to get a label for the given rendermode
def get_render_mode_label(rendermode):
info = get_render_mode_info(rendermode)
if 'label' in info:
return info['label']
return rendermode.capitalize()
# create generated map type data, from given quadtrees
maptypedata = map(lambda q: {'label' : q.rendermode.capitalize(),
maptypedata = map(lambda q: {'label' : get_render_mode_label(q.rendermode),
'path' : q.tiledir,
'bg_color': self.bg_color,
'overlay' : 'overlay' in get_render_mode_inheritance(q.rendermode),

View File

@@ -69,6 +69,12 @@ def pool_initializer(rendernode):
north_direction=rendernode.options.get('north_direction', None))
c_overviewer.init_chunk_render()
# setup c_overviewer rendermode customs / options
for mode in rendernode.options.custom_rendermodes:
c_overviewer.add_custom_render_mode(mode, rendernode.options.custom_rendermodes[mode])
for mode in rendernode.options.rendermode_options:
c_overviewer.set_render_mode_options(mode, rendernode.options.rendermode_options[mode])
# load biome data in each process, if needed
for quadtree in rendernode.quadtrees:
if quadtree.world.useBiomeData:
@@ -145,7 +151,9 @@ class RenderNode(object):
pool = FakePool()
pool_initializer(self)
else:
pool_initializer(self)
pool = multiprocessing.Pool(processes=procs,initializer=pool_initializer,initargs=(self,))
#warm up the pool so it reports all the worker id's
if logging.getLogger().level >= 10:
pool.map(bool,xrange(multiprocessing.cpu_count()),1)

View File

@@ -26,11 +26,10 @@ static PyObject *transparent_blocks = NULL;
PyObject *init_chunk_render(PyObject *self, PyObject *args) {
/* this function only needs to be called once, anything more is an
* error... */
/* this function only needs to be called once, anything more should be
* ignored */
if (blockmap) {
PyErr_SetString(PyExc_RuntimeError, "init_chunk_render should only be called once per process.");
return NULL;
Py_RETURN_NONE;
}
textures = PyImport_ImportModule("overviewer_core.textures");
@@ -298,6 +297,7 @@ generate_pseudo_data(RenderState *state, unsigned char ancilData) {
PyObject*
chunk_render(PyObject *self, PyObject *args) {
RenderState state;
PyObject *rendermode_py;
int xoff, yoff;
@@ -310,9 +310,7 @@ chunk_render(PyObject *self, PyObject *args) {
PyObject *up_left_blocks_py;
PyObject *up_right_blocks_py;
RenderModeInterface *rendermode;
void *rm_data;
RenderMode *rendermode;
PyObject *t = NULL;
@@ -324,11 +322,11 @@ chunk_render(PyObject *self, PyObject *args) {
state.chunk = chunk_mod;
/* set up the render mode */
rendermode = get_render_mode(&state);
rm_data = calloc(1, rendermode->data_size);
if (rendermode->start(rm_data, &state)) {
free(rm_data);
return Py_BuildValue("i", "-1");
rendermode_py = PyObject_GetAttrString(state.self, "rendermode");
state.rendermode = rendermode = render_mode_create(PyString_AsString(rendermode_py), &state);
Py_DECREF(rendermode_py);
if (rendermode == NULL) {
return NULL;
}
/* get the image size */
@@ -378,7 +376,7 @@ chunk_render(PyObject *self, PyObject *args) {
/* get blockid */
state.block = getArrayByte3D(blocks_py, state.x, state.y, state.z);
if (state.block == 0) {
if (state.block == 0 || render_mode_hidden(rendermode, state.x, state.y, state.z)) {
continue;
}
@@ -398,7 +396,7 @@ chunk_render(PyObject *self, PyObject *args) {
blockid = PyInt_FromLong(state.block);
// check for occlusion
if (rendermode->occluded(rm_data, &state)) {
if (render_mode_occluded(rendermode, state.x, state.y, state.z)) {
continue;
}
@@ -457,7 +455,7 @@ chunk_render(PyObject *self, PyObject *args) {
state.imgy += randy;
}
rendermode->draw(rm_data, &state, src, mask, mask_light);
render_mode_draw(rendermode, src, mask, mask_light);
if (state.block == 31) {
/* undo the random offsets */
@@ -475,8 +473,7 @@ chunk_render(PyObject *self, PyObject *args) {
}
/* free up the rendermode info */
rendermode->finish(rm_data, &state);
free(rm_data);
render_mode_destroy(rendermode);
Py_DECREF(blocks_py);
Py_XDECREF(left_blocks_py);

View File

@@ -17,6 +17,10 @@
#include "overviewer.h"
/* global variables from rendermodes.c -- both are dictionaries */
extern PyObject *render_mode_options;
extern PyObject *custom_render_modes;
PyObject *get_extension_version(PyObject *self, PyObject *args) {
return Py_BuildValue("i", OVERVIEWER_EXTENSION_VERSION);
@@ -35,13 +39,16 @@ static PyMethodDef COverviewerMethods[] = {
"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"},
{"set_render_mode_options", set_render_mode_options, METH_VARARGS,
"sets the default options for a given render mode"},
{"add_custom_render_mode", add_custom_render_mode, METH_VARARGS,
"add a new rendermode derived from an existing mode"},
{"extension_version", get_extension_version, METH_VARARGS,
"Returns the extension version"},
@@ -52,9 +59,22 @@ static PyMethodDef COverviewerMethods[] = {
PyMODINIT_FUNC
initc_overviewer(void)
{
(void)Py_InitModule("c_overviewer", COverviewerMethods);
PyObject *mod = Py_InitModule("c_overviewer", COverviewerMethods);
/* for numpy */
import_array();
/* create the render mode data structures, and attatch them to the module
* so that the Python garbage collector doesn't freak out
*/
render_mode_options = PyDict_New();
PyObject_SetAttrString(mod, "_render_mode_options", render_mode_options);
Py_DECREF(render_mode_options);
custom_render_modes = PyDict_New();
PyObject_SetAttrString(mod, "_custom_render_modes", custom_render_modes);
Py_DECREF(custom_render_modes);
init_endian();
}

View File

@@ -26,7 +26,7 @@
// increment this value if you've made a change to the c extesion
// and want to force users to rebuild
#define OVERVIEWER_EXTENSION_VERSION 10
#define OVERVIEWER_EXTENSION_VERSION 11
/* Python PIL, and numpy headers */
#include <Python.h>
@@ -52,6 +52,9 @@ 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);
/* forward declaration of RenderMode object */
typedef struct _RenderMode RenderMode;
/* in iterate.c */
typedef struct {
/* the ChunkRenderer object */
@@ -61,6 +64,9 @@ typedef struct {
PyObject *textures;
PyObject *chunk;
/* the current render mode in use */
RenderMode *rendermode;
/* the rest only make sense for occluded() and draw() !! */
/* the tile image and destination */

View File

@@ -17,77 +17,73 @@
#include "overviewer.h"
#include <math.h>
//~
//~ /* figures out the black_coeff from a given skylight and blocklight, used in
//~ lighting calculations -- note this is *different* from the one in
//~ rendermode-lighting.c (the "skylight - 11" part) */
//~ static float calculate_darkness(unsigned char skylight, unsigned char blocklight) {
//~ return 1.0f - powf(0.8f, 15.0 - MAX(blocklight, skylight - 11));
//~ }
static int
rendermode_cave_occluded(void *data, RenderState *state) {
int x = state->x, y = state->y, z = state->z, dz = 0;
RenderModeCave* self;
self = (RenderModeCave *)data;
static inline int
touches_light(unsigned int x, unsigned int y, unsigned int z,
PyObject *light, PyObject *left_light, PyObject *right_light,
PyObject *up_left_light, PyObject *up_right_light) {
/* check if the block is touching skylight */
if (z != 127) {
if (getArrayByte3D(self->skylight, x, y, z+1) != 0) {
if (getArrayByte3D(light, x, y, z+1) != 0) {
return 1;
}
if ((x == 15)) {
if (up_right_light != Py_None) {
if (getArrayByte3D(up_right_light, 0, y, z) != 0) {
return 1;
}
}
} else {
if (getArrayByte3D(light, x+1, y, z) != 0) {
return 1;
}
}
if ((x == 15)) {
if (self->up_right_skylight != Py_None) {
if (getArrayByte3D(self->up_right_skylight, 0, y, z) != 0) {
return 1;
}
}
} else {
if (getArrayByte3D(self->skylight, x+1, y, z) != 0) {
if (x == 0) {
if (left_light != Py_None) {
if (getArrayByte3D(left_light, 15, y, z) != 0) {
return 1;
}
}
} else {
if (getArrayByte3D(light, x-1, y, z) != 0) {
return 1;
}
}
if (x == 0) {
if (self->left_skylight != Py_None) {
if (getArrayByte3D(self->left_skylight, 15, y, z) != 0) {
return 1;
}
}
} else {
if (getArrayByte3D(self->skylight, x-1, y, z) != 0) {
if (y == 15) {
if (right_light != Py_None) {
if (getArrayByte3D(right_light, 0, y, z) != 0) {
return 1;
}
}
} else {
if (getArrayByte3D(light, x, y+1, z) != 0) {
return 1;
}
}
if (y == 15) {
if (self->right_skylight != Py_None) {
if (getArrayByte3D(self->right_skylight, 0, y, z) != 0) {
return 1;
}
}
} else {
if (getArrayByte3D(self->skylight, x, y+1, z) != 0) {
if (y == 0) {
if (up_left_light != Py_None) {
if (getArrayByte3D(up_left_light, 15, y, z) != 0) {
return 1;
}
}
if (y == 0) {
if (self->up_left_skylight != Py_None) {
if (getArrayByte3D(self->up_left_skylight, 15, y, z) != 0) {
return 1;
}
}
} else {
if (getArrayByte3D(self->skylight, x, y-1, z) != 0) {
return 1;
}
} else {
if (getArrayByte3D(light, x, y-1, z) != 0) {
return 1;
}
}
/* check for normal occlusion */
/* use ajacent chunks, if not you get blocks spreaded in chunk edges */
return 0;
}
static inline int
rendermode_cave_adjacent_occluded(void *data, RenderState *state, int x, int y, int z) {
/* check for occlusion of edge blocks, using adjacent block data */
if (z != 127) {
if ( (x == 0) && (y != 15) ) {
if (state->left_blocks != Py_None) {
if (!is_transparent(getArrayByte3D(state->left_blocks, 15, y, z)) &&
@@ -126,20 +122,55 @@ rendermode_cave_occluded(void *data, RenderState *state) {
}
if ( (x != 0) && (y != 15) &&
!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))) {
!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 int
rendermode_cave_occluded(void *data, RenderState *state, int x, int y, int z) {
/* first, check to see if it's "normally" occluded */
if (rendermode_lighting.occluded(data, state, x, y, z))
return 1;
/* check for normal occlusion */
/* use ajacent chunks, if not you get blocks spreaded in chunk edges */
return rendermode_cave_adjacent_occluded(data, state, x, y, z);
}
static int
rendermode_cave_hidden(void *data, RenderState *state, int x, int y, int z) {
RenderModeCave* self;
int dz = 0;
self = (RenderModeCave *)data;
/* first, check to see if it's "normally" hidden */
if (rendermode_lighting.hidden(data, state, x, y, z))
return 1;
/* check if the block is touching skylight */
if (z != 127) {
if (touches_light(x, y, z, self->skylight, self->left_skylight, self->right_skylight, self->up_left_skylight, self->up_right_skylight)) {
return 1;
}
if (self->only_lit && !touches_light(x, y, z, self->blocklight, self->left_blocklight, self->right_blocklight, self->up_left_blocklight, self->up_right_blocklight)) {
return 1;
}
} else { /* if z == 127 skip */
return 1;
}
/* check for lakes and seas and don't render them */
/* at this point of the code the block has no skylight
* and is not occluded, but a deep sea can fool these
* 2 tests */
/* check for lakes and seas and don't render them
* at this point of the code the block has no skylight
* but a deep sea can be completely dark
*/
if ((getArrayByte3D(state->blocks, x, y, z) == 9) ||
(getArrayByte3D(state->blocks, x, y, z+1) == 9)) {
@@ -156,21 +187,55 @@ rendermode_cave_occluded(void *data, RenderState *state) {
}
}
/* unfortunate side-effect of lit cave mode: we need to count occluded
* blocks as hidden for the lighting to look right, since technically our
* hiding depends on occlusion as well
*
* We leave out this check otherwise because it's fairly expensive.
*/
if (self->lighting) {
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 rendermode_cave_adjacent_occluded(data, state, x, y, z);
}
return 0;
}
static int
rendermode_cave_start(void *data, RenderState *state) {
rendermode_cave_start(void *data, RenderState *state, PyObject *options) {
RenderModeCave* self;
int ret;
self = (RenderModeCave *)data;
/* first, chain up */
ret = rendermode_normal.start(data, state);
ret = rendermode_lighting.start(data, state, options);
if (ret != 0)
return ret;
self->depth_tinting = 1;
if (!render_mode_parse_option(options, "depth_tinting", "i", &(self->depth_tinting)))
return 1;
self->only_lit = 0;
if (!render_mode_parse_option(options, "only_lit", "i", &(self->only_lit)))
return 1;
self->lighting = 0;
if (!render_mode_parse_option(options, "lighting", "i", &(self->lighting)))
return 1;
if (self->lighting)
{
/* we can't skip lighting the sides in cave mode, it looks too weird */
self->parent.skip_sides = 0;
}
/* if there's skylight we are in the surface! */
self->skylight = PyObject_GetAttrString(state->self, "skylight");
self->left_skylight = PyObject_GetAttrString(state->self, "left_skylight");
@@ -178,10 +243,17 @@ rendermode_cave_start(void *data, RenderState *state) {
self->up_left_skylight = PyObject_GetAttrString(state->self, "up_left_skylight");
self->up_right_skylight = PyObject_GetAttrString(state->self, "up_right_skylight");
if (self->only_lit) {
self->blocklight = PyObject_GetAttrString(state->self, "blocklight");
self->left_blocklight = PyObject_GetAttrString(state->self, "left_blocklight");
self->right_blocklight = PyObject_GetAttrString(state->self, "right_blocklight");
self->up_left_blocklight = PyObject_GetAttrString(state->self, "up_left_blocklight");
self->up_right_blocklight = PyObject_GetAttrString(state->self, "up_right_blocklight");
}
/* colors for tinting */
self->depth_colors = PyObject_GetAttrString(state->chunk, "depth_colors");
return 0;
}
@@ -196,9 +268,17 @@ rendermode_cave_finish(void *data, RenderState *state) {
Py_DECREF(self->up_left_skylight);
Py_DECREF(self->up_right_skylight);
if (self->only_lit) {
Py_DECREF(self->blocklight);
Py_DECREF(self->left_blocklight);
Py_DECREF(self->right_blocklight);
Py_DECREF(self->up_left_blocklight);
Py_DECREF(self->up_right_blocklight);
}
Py_DECREF(self->depth_colors);
rendermode_normal.finish(data, state);
rendermode_lighting.finish(data, state);
}
static void
@@ -211,24 +291,39 @@ rendermode_cave_draw(void *data, RenderState *state, PyObject *src, PyObject *ma
r = 0, g = 0, b = 0;
/* draw the normal block */
rendermode_normal.draw(data, state, src, mask, mask_light);
if (self->lighting) {
rendermode_lighting.draw(data, state, src, mask, mask_light);
} else {
rendermode_normal.draw(data, state, src, mask, mask_light);
}
/* get the colors and tint and tint */
/* TODO TODO for a nether mode there isn't tinting! */
r = PyInt_AsLong(PyList_GetItem(self->depth_colors, 0 + z*3));
g = PyInt_AsLong(PyList_GetItem(self->depth_colors, 1 + z*3));
b = PyInt_AsLong(PyList_GetItem(self->depth_colors, 2 + z*3));
if (self->depth_tinting) {
/* get the colors and tint and tint */
r = PyInt_AsLong(PyList_GetItem(self->depth_colors, 0 + z*3));
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, 255, mask, state->imgx, state->imgy, 0, 0);
tint_with_mask(state->img, r, g, b, 255, mask, state->imgx, state->imgy, 0, 0);
}
}
const RenderModeOption rendermode_cave_options[] = {
{"depth_tinting", "tint caves based on how deep they are (default: True)"},
{"only_lit", "only render lit caves (default: False)"},
{"lighting", "render caves with lighting enabled (default: False)"},
{NULL, NULL}
};
RenderModeInterface rendermode_cave = {
"cave", "render only caves in normal mode",
&rendermode_normal,
"cave", "Cave",
"render only caves",
rendermode_cave_options,
&rendermode_lighting,
sizeof(RenderModeCave),
rendermode_cave_start,
rendermode_cave_finish,
rendermode_cave_occluded,
rendermode_cave_hidden,
rendermode_cave_draw,
};

View File

@@ -169,13 +169,6 @@ get_lighting_coefficient(RenderModeLighting *self, RenderState *state,
}
block = getArrayByte3D(blocks, local_x, local_y, local_z);
/* if this block is opaque, use a fully-lit coeff instead
to prevent stippled lines along chunk boundaries! */
if (!is_transparent(block)) {
return self->calculate_darkness(15, 0);
}
skylevel = getArrayByte3D(skylight, local_x, local_y, local_z);
blocklevel = getArrayByte3D(blocklight, local_x, local_y, local_z);
@@ -223,11 +216,12 @@ do_shading_with_mask(RenderModeLighting *self, RenderState *state,
/* first, check for occlusion if the block is in the local chunk */
if (x >= 0 && x < 16 && y >= 0 && y < 16 && z >= 0 && z < 128) {
unsigned char block = getArrayByte3D(state->blocks, x, y, z);
if (!is_transparent(block)) {
if (!is_transparent(block) && !render_mode_hidden(state->rendermode, x, y, z)) {
/* this face isn't visible, so don't draw anything */
return;
}
} else if ((x == -1) && (state->left_blocks != Py_None)) {
} else if (self->skip_sides && (x == -1) && (state->left_blocks != Py_None)) {
unsigned char block = getArrayByte3D(state->left_blocks, 15, state->y, state->z);
if (!is_transparent(block)) {
/* the same thing but for adjacent chunks, this solves an
@@ -236,7 +230,7 @@ do_shading_with_mask(RenderModeLighting *self, RenderState *state,
tessellate-able */
return;
}
} else if ((y == 16) && (state->right_blocks != Py_None)) {
} else if (self->skip_sides && (y == 16) && (state->right_blocks != Py_None)) {
unsigned char block = getArrayByte3D(state->right_blocks, state->x, 0, state->z);
if (!is_transparent(block)) {
/* the same thing but for adjacent chunks, this solves an
@@ -248,20 +242,28 @@ do_shading_with_mask(RenderModeLighting *self, RenderState *state,
}
black_coeff = get_lighting_coefficient(self, state, x, y, z);
black_coeff *= self->shade_strength;
alpha_over_full(state->img, self->black_color, mask, black_coeff, state->imgx, state->imgy, 0, 0);
}
static int
rendermode_lighting_start(void *data, RenderState *state) {
rendermode_lighting_start(void *data, RenderState *state, PyObject *options) {
RenderModeLighting* self;
/* first, chain up */
int ret = rendermode_normal.start(data, state);
int ret = rendermode_normal.start(data, state, options);
if (ret != 0)
return ret;
self = (RenderModeLighting *)data;
/* skip sides by default */
self->skip_sides = 1;
self->shade_strength = 1.0;
if (!render_mode_parse_option(options, "shade_strength", "f", &(self->shade_strength)))
return 1;
self->black_color = PyObject_GetAttrString(state->chunk, "black_color");
self->facemasks_py = PyObject_GetAttrString(state->chunk, "facemasks");
// borrowed references, don't need to be decref'd
@@ -300,9 +302,15 @@ rendermode_lighting_finish(void *data, RenderState *state) {
}
static int
rendermode_lighting_occluded(void *data, RenderState *state) {
rendermode_lighting_occluded(void *data, RenderState *state, int x, int y, int z) {
/* no special occlusion here */
return rendermode_normal.occluded(data, state);
return rendermode_normal.occluded(data, state, x, y, z);
}
static int
rendermode_lighting_hidden(void *data, RenderState *state, int x, int y, int z) {
/* no special hiding here */
return rendermode_normal.hidden(data, state, x, y, z);
}
static void
@@ -342,12 +350,20 @@ rendermode_lighting_draw(void *data, RenderState *state, PyObject *src, PyObject
}
}
const RenderModeOption rendermode_lighting_options[] = {
{"shade_strength", "how dark to make the shadows, from 0.0 to 1.0 (default: 1.0)"},
{NULL, NULL}
};
RenderModeInterface rendermode_lighting = {
"lighting", "draw shadows from the lighting data",
"lighting", "Lighting",
"draw shadows from the lighting data",
rendermode_lighting_options,
&rendermode_normal,
sizeof(RenderModeLighting),
rendermode_lighting_start,
rendermode_lighting_finish,
rendermode_lighting_occluded,
rendermode_lighting_hidden,
rendermode_lighting_draw,
};

View File

@@ -0,0 +1,167 @@
/*
* 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"
struct MineralColor {
unsigned char blockid;
unsigned char r, g, b;
};
/* put more valuable ores first -- they take precedence */
static struct MineralColor default_minerals[] = {
{48 /* Mossy Stone */, 31, 153, 9},
{56 /* Diamond Ore */, 32, 230, 220},
{21 /* Lapis Lazuli */, 0, 23, 176},
{14 /* Gold Ore */, 255, 234, 0},
{15 /* Iron Ore */, 204, 204, 204},
{73 /* Redstone */, 186, 0, 0},
{74 /* Lit Redstone */, 186, 0, 0},
{16 /* Coal Ore */, 54, 54, 54},
/* end of list marker */
{0, 0, 0, 0}
};
static void get_color(void *data, RenderState *state,
unsigned char *r, unsigned char *g, unsigned char *b, unsigned char *a) {
int x = state->x, y = state->y, z_max = state->z + 1, z;
int max_i = -1;
RenderModeMineral* self = (RenderModeMineral *)data;
struct MineralColor *minerals = (struct MineralColor *)(self->minerals);
*a = 0;
for (z = 0; z <= z_max; z++) {
int i, tmp;
unsigned char blockid = getArrayByte3D(state->blocks, x, y, z);
for (i = 0; (max_i == -1 || i < max_i) && minerals[i].blockid != 0; i++) {
if (minerals[i].blockid == blockid) {
*r = minerals[i].r;
*g = minerals[i].g;
*b = minerals[i].b;
tmp = (128 - z_max + z) * 2 - 40;
*a = MIN(MAX(0, tmp), 255);
max_i = i;
break;
}
}
}
}
static int
rendermode_mineral_start(void *data, RenderState *state, PyObject *options) {
PyObject *opt;
RenderModeMineral* self;
/* first, chain up */
int ret = rendermode_overlay.start(data, state, options);
if (ret != 0)
return ret;
/* now do custom initializations */
self = (RenderModeMineral *)data;
opt = PyDict_GetItemString(options, "minerals");
if (opt) {
struct MineralColor *minerals = NULL;
Py_ssize_t minerals_size = 0, i;
/* create custom minerals */
if (!PyList_Check(opt)) {
PyErr_SetString(PyExc_TypeError, "'minerals' must be a list");
return 1;
}
minerals_size = PyList_GET_SIZE(opt);
minerals = self->minerals = calloc(minerals_size + 1, sizeof(struct MineralColor));
if (minerals == NULL) {
return 1;
}
for (i = 0; i < minerals_size; i++) {
PyObject *mineral = PyList_GET_ITEM(opt, i);
if (!PyArg_ParseTuple(mineral, "b(bbb)", &(minerals[i].blockid), &(minerals[i].r), &(minerals[i].g), &(minerals[i].b))) {
free(minerals);
self->minerals = NULL;
return 1;
}
}
} else {
self->minerals = default_minerals;
}
/* setup custom color */
self->parent.get_color = get_color;
return 0;
}
static void
rendermode_mineral_finish(void *data, RenderState *state) {
/* first free all *our* stuff */
RenderModeMineral* self = (RenderModeMineral *)data;
if (self->minerals && self->minerals != default_minerals) {
free(self->minerals);
}
/* now, chain up */
rendermode_overlay.finish(data, state);
}
static int
rendermode_mineral_occluded(void *data, RenderState *state, int x, int y, int z) {
/* no special occlusion here */
return rendermode_overlay.occluded(data, state, x, y, z);
}
static int
rendermode_mineral_hidden(void *data, RenderState *state, int x, int y, int z) {
/* no special hiding here */
return rendermode_overlay.hidden(data, state, x, y, z);
}
static void
rendermode_mineral_draw(void *data, RenderState *state, PyObject *src, PyObject *mask, PyObject *mask_light) {
/* draw normally */
rendermode_overlay.draw(data, state, src, mask, mask_light);
}
const RenderModeOption rendermode_mineral_options[] = {
{"minerals", "a list of (blockid, (r, g, b)) tuples for coloring minerals"},
{NULL, NULL}
};
RenderModeInterface rendermode_mineral = {
"mineral", "Mineral",
"draws a colored overlay showing where ores are located",
rendermode_mineral_options,
&rendermode_overlay,
sizeof(RenderModeMineral),
rendermode_mineral_start,
rendermode_mineral_finish,
rendermode_mineral_occluded,
rendermode_mineral_hidden,
rendermode_mineral_draw,
};

View File

@@ -26,11 +26,11 @@ static float calculate_darkness(unsigned char skylight, unsigned char blocklight
}
static int
rendermode_night_start(void *data, RenderState *state) {
rendermode_night_start(void *data, RenderState *state, PyObject *options) {
RenderModeNight* self;
/* first, chain up */
int ret = rendermode_lighting.start(data, state);
int ret = rendermode_lighting.start(data, state, options);
if (ret != 0)
return ret;
@@ -48,9 +48,15 @@ rendermode_night_finish(void *data, RenderState *state) {
}
static int
rendermode_night_occluded(void *data, RenderState *state) {
rendermode_night_occluded(void *data, RenderState *state, int x, int y, int z) {
/* no special occlusion here */
return rendermode_lighting.occluded(data, state);
return rendermode_lighting.occluded(data, state, x, y, z);
}
static int
rendermode_night_hidden(void *data, RenderState *state, int x, int y, int z) {
/* no special hiding here */
return rendermode_lighting.hidden(data, state, x, y, z);
}
static void
@@ -60,11 +66,14 @@ rendermode_night_draw(void *data, RenderState *state, PyObject *src, PyObject *m
}
RenderModeInterface rendermode_night = {
"night", "like \"lighting\", except at night",
"night", "Night",
"like \"lighting\", except at night",
NULL,
&rendermode_lighting,
sizeof(RenderModeNight),
rendermode_night_start,
rendermode_night_finish,
rendermode_night_occluded,
rendermode_night_hidden,
rendermode_night_draw,
};

View File

@@ -18,10 +18,33 @@
#include "overviewer.h"
static int
rendermode_normal_start(void *data, RenderState *state) {
rendermode_normal_start(void *data, RenderState *state, PyObject *options) {
PyObject *chunk_x_py, *chunk_y_py, *world, *use_biomes, *worlddir;
RenderModeNormal *self = (RenderModeNormal *)data;
/* load up the given options, first */
self->edge_opacity = 0.15;
if (!render_mode_parse_option(options, "edge_opacity", "f", &(self->edge_opacity)))
return 1;
self->min_depth = 0;
if (!render_mode_parse_option(options, "min_depth", "I", &(self->min_depth)))
return 1;
self->max_depth = 127;
if (!render_mode_parse_option(options, "max_depth", "I", &(self->max_depth)))
return 1;
self->height_fading = 0;
if (!render_mode_parse_option(options, "height_fading", "i", &(self->height_fading)))
return 1;
if (self->height_fading) {
self->black_color = PyObject_GetAttrString(state->chunk, "black_color");
self->white_color = PyObject_GetAttrString(state->chunk, "white_color");
}
chunk_x_py = PyObject_GetAttrString(state->self, "chunkX");
chunk_y_py = PyObject_GetAttrString(state->self, "chunkY");
@@ -106,13 +129,16 @@ rendermode_normal_finish(void *data, RenderState *state) {
Py_XDECREF(self->tall_grass_texture);
Py_XDECREF(self->tall_fern_texture);
Py_XDECREF(self->facemask_top);
Py_XDECREF(self->black_color);
Py_XDECREF(self->white_color);
}
static int
rendermode_normal_occluded(void *data, RenderState *state) {
int x = state->x, y = state->y, z = state->z;
rendermode_normal_occluded(void *data, RenderState *state, int x, int y, int z) {
if ( (x != 0) && (y != 15) && (z != 127) &&
!render_mode_hidden(state->rendermode, x-1, y, z) &&
!render_mode_hidden(state->rendermode, x, y, z+1) &&
!render_mode_hidden(state->rendermode, x, y+1, z) &&
!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))) {
@@ -122,6 +148,17 @@ rendermode_normal_occluded(void *data, RenderState *state) {
return 0;
}
static int
rendermode_normal_hidden(void *data, RenderState *state, int x, int y, int z) {
RenderModeNormal *self = (RenderModeNormal *)data;
if (z > self->max_depth || z < self->min_depth) {
return 1;
}
return 0;
}
static void
rendermode_normal_draw(void *data, RenderState *state, PyObject *src, PyObject *mask, PyObject *mask_light) {
RenderModeNormal *self = (RenderModeNormal *)data;
@@ -193,12 +230,26 @@ rendermode_normal_draw(void *data, RenderState *state, PyObject *src, PyObject *
}
}
if (self->height_fading) {
/* do some height fading */
PyObject *height_color = self->white_color;
/* negative alpha => darkness, positive => light */
float alpha = (1.0 / (1 + expf((70 - state->z) / 11.0))) * 0.6 - 0.55;
if (alpha < 0.0) {
alpha *= -1;
height_color = self->black_color;
}
alpha_over_full(state->img, height_color, mask_light, alpha, state->imgx, state->imgy, 0, 0);
}
/* Draw some edge lines! */
// draw.line(((imgx+12,imgy+increment), (imgx+22,imgy+5+increment)), fill=(0,0,0), width=1)
if (state->block == 44 || state->block == 78 || !is_transparent(state->block)) {
Imaging img_i = imaging_python_to_c(state->img);
unsigned char ink[] = {0,0,0,40};
unsigned char ink[] = {0, 0, 0, 255 * self->edge_opacity};
int increment=0;
if (state->block == 44) // half-step
@@ -239,12 +290,23 @@ rendermode_normal_draw(void *data, RenderState *state, PyObject *src, PyObject *
}
}
const RenderModeOption rendermode_normal_options[] = {
{"edge_opacity", "darkness of the edge lines, from 0.0 to 1.0 (default: 0.15)"},
{"min_depth", "lowest level of blocks to render (default: 0)"},
{"max_depth", "highest level of blocks to render (default: 127)"},
{"height_fading", "darken or lighten blocks based on height (default: False)"},
{NULL, NULL}
};
RenderModeInterface rendermode_normal = {
"normal", "nothing special, just render the blocks",
"normal", "Normal",
"nothing special, just render the blocks",
rendermode_normal_options,
NULL,
sizeof(RenderModeNormal),
rendermode_normal_start,
rendermode_normal_finish,
rendermode_normal_occluded,
rendermode_normal_hidden,
rendermode_normal_draw,
};

View File

@@ -26,7 +26,7 @@ static void get_color(void *data, RenderState *state,
}
static int
rendermode_overlay_start(void *data, RenderState *state) {
rendermode_overlay_start(void *data, RenderState *state, PyObject *options) {
PyObject *facemasks_py;
RenderModeOverlay *self = (RenderModeOverlay *)data;
@@ -57,10 +57,11 @@ rendermode_overlay_finish(void *data, RenderState *state) {
}
static int
rendermode_overlay_occluded(void *data, RenderState *state) {
int x = state->x, y = state->y, z = state->z;
rendermode_overlay_occluded(void *data, RenderState *state, int x, int y, int z) {
if ( (x != 0) && (y != 15) && (z != 127) &&
!render_mode_hidden(state->rendermode, x-1, y, z) &&
!render_mode_hidden(state->rendermode, x, y, z+1) &&
!render_mode_hidden(state->rendermode, x, y+1, z) &&
!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))) {
@@ -70,6 +71,12 @@ rendermode_overlay_occluded(void *data, RenderState *state) {
return 0;
}
static int
rendermode_overlay_hidden(void *data, RenderState *state, int x, int y, int z) {
/* overlays hide nothing by default */
return 0;
}
static void
rendermode_overlay_draw(void *data, RenderState *state, PyObject *src, PyObject *mask, PyObject *mask_light) {
RenderModeOverlay *self = (RenderModeOverlay *)data;
@@ -127,11 +134,14 @@ rendermode_overlay_draw(void *data, RenderState *state, PyObject *src, PyObject
}
RenderModeInterface rendermode_overlay = {
"overlay", "base rendermode for informational overlays",
"overlay", "Overlay",
"base rendermode for informational overlays",
NULL,
NULL,
sizeof(RenderModeOverlay),
rendermode_overlay_start,
rendermode_overlay_finish,
rendermode_overlay_occluded,
rendermode_overlay_hidden,
rendermode_overlay_draw,
};

View File

@@ -62,11 +62,11 @@ static void get_color(void *data, RenderState *state,
}
static int
rendermode_spawn_start(void *data, RenderState *state) {
rendermode_spawn_start(void *data, RenderState *state, PyObject *options) {
RenderModeSpawn* self;
/* first, chain up */
int ret = rendermode_overlay.start(data, state);
int ret = rendermode_overlay.start(data, state, options);
if (ret != 0)
return ret;
@@ -96,9 +96,15 @@ rendermode_spawn_finish(void *data, RenderState *state) {
}
static int
rendermode_spawn_occluded(void *data, RenderState *state) {
rendermode_spawn_occluded(void *data, RenderState *state, int x, int y, int z) {
/* no special occlusion here */
return rendermode_overlay.occluded(data, state);
return rendermode_overlay.occluded(data, state, x, y, z);
}
static int
rendermode_spawn_hidden(void *data, RenderState *state, int x, int y, int z) {
/* no special hiding here */
return rendermode_overlay.hidden(data, state, x, y, z);
}
static void
@@ -108,11 +114,14 @@ 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",
"spawn", "Spawn",
"draws a red overlay where monsters can spawn at night",
NULL,
&rendermode_overlay,
sizeof(RenderModeSpawn),
rendermode_spawn_start,
rendermode_spawn_finish,
rendermode_spawn_occluded,
rendermode_spawn_hidden,
rendermode_spawn_draw,
};

View File

@@ -17,6 +17,7 @@
#include "overviewer.h"
#include <string.h>
#include <stdarg.h>
/* list of all render modes, ending in NULL
all of these will be available to the user, so DON'T include modes
@@ -27,32 +28,204 @@ static RenderModeInterface *render_modes[] = {
&rendermode_night,
&rendermode_spawn,
&rendermode_cave,
&rendermode_mineral,
NULL
};
/* decides which render mode to use */
RenderModeInterface *get_render_mode(RenderState *state) {
unsigned int i;
/* default: NULL --> an error */
RenderModeInterface *iface = NULL;
PyObject *rendermode_py = PyObject_GetAttrString(state->self, "rendermode");
const char *rendermode = PyString_AsString(rendermode_py);
PyObject *render_mode_options = NULL;
PyObject *custom_render_modes = NULL;
/* rendermode encapsulation */
/* helper to recursively find options for a given mode */
static inline PyObject *
render_mode_create_options(const char *mode) {
const char *parent = NULL;
PyObject *base_options, *ret, *parent_options;
unsigned int i, found_concrete;
base_options = PyDict_GetItemString(render_mode_options, mode);
if (base_options) {
ret = PyDict_Copy(base_options);
} else {
ret = PyDict_New();
}
/* figure out the parent mode name */
found_concrete = 0;
for (i = 0; render_modes[i] != NULL; i++) {
if (strcmp(render_modes[i]->name, rendermode) == 0) {
iface = render_modes[i];
if (strcmp(render_modes[i]->name, mode) == 0) {
found_concrete = 1;
if (render_modes[i]->parent) {
parent = render_modes[i]->parent->name;
}
break;
}
}
Py_DECREF(rendermode_py);
return iface;
/* check custom mode info if needed */
if (found_concrete == 0) {
PyObject *custom = PyDict_GetItemString(custom_render_modes, mode);
if (custom) {
custom = PyDict_GetItemString(custom, "parent");
if (custom) {
parent = PyString_AsString(custom);
}
}
}
/* merge parent options, if the parent was found */
if (parent) {
parent_options = render_mode_create_options(parent);
if (parent_options) {
if (PyDict_Merge(ret, parent_options, 0) == -1) {
Py_DECREF(ret);
Py_DECREF(parent_options);
return NULL;
}
Py_DECREF(parent_options);
}
}
return ret;
}
/* helper to find the first concrete, C interface for a given mode */
inline static RenderModeInterface *
render_mode_find_interface(const char *mode) {
PyObject *custom;
const char *custom_parent;
unsigned int i;
/* if it is *itself* concrete, we're done */
for (i = 0; render_modes[i] != NULL; i++) {
if (strcmp(render_modes[i]->name, mode) == 0)
return render_modes[i];
}
/* check for custom modes */
custom = PyDict_GetItemString(custom_render_modes, mode);
if (custom == NULL)
return NULL;
custom = PyDict_GetItemString(custom, "parent");
if (custom == NULL)
return NULL;
custom_parent = PyString_AsString(custom);
if (custom_parent == NULL)
return NULL;
return render_mode_find_interface(custom_parent);
}
RenderMode *render_mode_create(const char *mode, RenderState *state) {
PyObject *options;
RenderMode *ret = NULL;
RenderModeInterface *iface = NULL;
iface = render_mode_find_interface(mode);
if (iface == NULL)
return NULL;
options = render_mode_create_options(mode);
if (options == NULL)
return NULL;
ret = calloc(1, sizeof(RenderMode));
if (ret == NULL) {
Py_DECREF(options);
return NULL;
}
ret->mode = calloc(1, iface->data_size);
if (ret->mode == NULL) {
Py_DECREF(options);
free(ret);
return NULL;
}
ret->iface = iface;
ret->state = state;
if (iface->start(ret->mode, state, options)) {
Py_DECREF(options);
free(ret->mode);
free(ret);
return NULL;
}
Py_DECREF(options);
return ret;
}
void render_mode_destroy(RenderMode *self) {
self->iface->finish(self->mode, self->state);
free(self->mode);
free(self);
}
int render_mode_occluded(RenderMode *self, int x, int y, int z) {
return self->iface->occluded(self->mode, self->state, x, y, z);
}
int render_mode_hidden(RenderMode *self, int x, int y, int z) {
return self->iface->hidden(self->mode, self->state, x, y, z);
}
void render_mode_draw(RenderMode *self, PyObject *img, PyObject *mask, PyObject *mask_light) {
self->iface->draw(self->mode, self->state, img, mask, mask_light);
}
/* options parse helper */
int render_mode_parse_option(PyObject *dict, const char *name, const char *format, ...) {
va_list ap;
PyObject *item;
int ret;
if (dict == NULL || name == NULL)
return 1;
item = PyDict_GetItemString(dict, name);
if (item == NULL)
return 1;
/* make sure the item we're parsing is a tuple
for VaParse to work correctly */
if (!PyTuple_Check(item)) {
item = PyTuple_Pack(1, item);
} else {
Py_INCREF(item);
}
va_start(ap, format);
ret = PyArg_VaParse(item, format, ap);
va_end(ap);
Py_DECREF(item);
if (!ret) {
PyObject *errtype, *errvalue, *errtraceback;
const char *errstring;
PyErr_Fetch(&errtype, &errvalue, &errtraceback);
errstring = PyString_AsString(errvalue);
PyErr_Format(PyExc_TypeError, "rendermode option \"%s\" has incorrect type (%s)", name, errstring);
Py_DECREF(errtype);
Py_DECREF(errvalue);
Py_XDECREF(errtraceback);
}
return ret;
}
/* bindings for python -- get all the rendermode names */
PyObject *get_render_modes(PyObject *self, PyObject *args) {
PyObject *modes;
unsigned int i;
PyObject *key, *value;
Py_ssize_t pos = 0;
if (!PyArg_ParseTuple(args, ""))
return NULL;
@@ -66,14 +239,64 @@ PyObject *get_render_modes(PyObject *self, PyObject *args) {
Py_DECREF(name);
}
while (PyDict_Next(custom_render_modes, &pos, &key, &value)) {
PyList_Append(modes, key);
}
return modes;
}
/* helper, get the list of options for a render mode */
static inline PyObject *
get_render_mode_options(const char *rendermode)
{
PyObject *options;
unsigned int i, j;
options = PyList_New(0);
if (!options)
return NULL;
for (i = 0; render_modes[i] != NULL; i++) {
if (strcmp(render_modes[i]->name, rendermode) == 0) {
if (render_modes[i]->options == NULL)
break;
for (j = 0; render_modes[i]->options[j].name != NULL; j++) {
RenderModeOption opt = render_modes[i]->options[j];
PyObject *name = PyString_FromString(opt.name);
PyObject *description = PyString_FromString(opt.description);
PyObject *option = PyDict_New();
if (!name || !description || !option) {
Py_XDECREF(name);
Py_XDECREF(description);
Py_XDECREF(option);
Py_DECREF(options);
return NULL;
}
PyDict_SetItemString(option, "name", name);
PyDict_SetItemString(option, "description", description);
PyList_Append(options, option);
Py_DECREF(name);
Py_DECREF(description);
Py_DECREF(option);
}
break;
}
}
return options;
}
/* more bindings -- return info for a given rendermode name */
PyObject *get_render_mode_info(PyObject *self, PyObject *args) {
const char* rendermode;
PyObject *info;
unsigned int i;
PyObject *custom;
if (!PyArg_ParseTuple(args, "s", &rendermode))
return NULL;
@@ -89,37 +312,45 @@ PyObject *get_render_mode_info(PyObject *self, PyObject *args) {
PyDict_SetItemString(info, "name", tmp);
Py_DECREF(tmp);
tmp = PyString_FromString(render_modes[i]->label);
PyDict_SetItemString(info, "label", tmp);
Py_DECREF(tmp);
tmp = PyString_FromString(render_modes[i]->description);
PyDict_SetItemString(info, "description", tmp);
Py_DECREF(tmp);
tmp = get_render_mode_options(rendermode);
PyDict_SetItemString(info, "options", tmp);
Py_DECREF(tmp);
if (render_modes[i]->parent != NULL) {
tmp = PyString_FromString(render_modes[i]->parent->name);
PyDict_SetItemString(info, "parent", tmp);
Py_DECREF(tmp);
}
return info;
}
}
Py_DECREF(info);
return PyErr_Format(PyExc_ValueError, "invalid rendermode: \"%s\"", rendermode);
}
custom = PyDict_GetItemString(custom_render_modes, rendermode);
if (custom) {
PyObject *tmp, *copy = PyDict_Copy(custom);
Py_DECREF(info);
/* 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;
tmp = PyString_FromString(rendermode);
PyDict_SetItemString(copy, "name", tmp);
Py_DECREF(tmp);
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;
}
}
tmp = PyList_New(0);
PyDict_SetItemString(copy, "options", tmp);
Py_DECREF(tmp);
return copy;
}
Py_DECREF(info);
return PyErr_Format(PyExc_ValueError, "invalid rendermode: \"%s\"", rendermode);
}
@@ -129,6 +360,8 @@ PyObject *get_render_mode_inheritance(PyObject *self, PyObject *args) {
PyObject *parents;
unsigned int i;
RenderModeInterface *iface = NULL;
PyObject *custom;
if (!PyArg_ParseTuple(args, "s", &rendermode))
return NULL;
@@ -136,6 +369,19 @@ PyObject *get_render_mode_inheritance(PyObject *self, PyObject *args) {
if (!parents)
return NULL;
/* take care of the chain of custom modes, if there are any */
custom = PyDict_GetItemString(custom_render_modes, rendermode);
while (custom != NULL) {
PyObject *name = PyString_FromString(rendermode);
PyList_Append(parents, name);
Py_DECREF(name);
custom = PyDict_GetItemString(custom, "parent");
rendermode = PyString_AsString(custom);
custom = PyDict_GetItem(custom_render_modes, custom);
}
/* now handle concrete modes */
for (i = 0; render_modes[i] != NULL; i++) {
if (strcmp(render_modes[i]->name, rendermode) == 0) {
iface = render_modes[i];
@@ -165,6 +411,9 @@ PyObject *get_render_mode_children(PyObject *self, PyObject *args) {
const char *rendermode;
PyObject *children;
unsigned int i;
PyObject *key, *value;
Py_ssize_t pos = 0;
if (!PyArg_ParseTuple(args, "s", &rendermode))
return NULL;
@@ -180,5 +429,119 @@ PyObject *get_render_mode_children(PyObject *self, PyObject *args) {
}
}
while (PyDict_Next(custom_render_modes, &pos, &key, &value)) {
PyObject *pyparent = PyDict_GetItemString(value, "parent");
const char *parent = PyString_AsString(pyparent);
if (strcmp(parent, rendermode) == 0) {
PyList_Append(children, key);
}
}
return children;
}
/* helper to decide if a rendermode supports a given option */
static inline int
render_mode_supports_option(RenderModeInterface *iface, const char *name) {
unsigned int i;
if (iface->options != NULL) {
for (i = 0; iface->options[i].name != NULL; i++) {
if (strcmp(iface->options[i].name, name) == 0) {
return 1;
}
}
}
if (iface->parent != NULL)
return render_mode_supports_option(iface->parent, name);
return 0;
}
/* python rendermode options bindings */
PyObject *set_render_mode_options(PyObject *self, PyObject *args) {
const char *rendermode;
PyObject *opts, *key, *value;
Py_ssize_t pos = 0;
RenderModeInterface *iface = NULL;
if (!PyArg_ParseTuple(args, "sO!", &rendermode, &PyDict_Type, &opts))
return NULL;
iface = render_mode_find_interface(rendermode);
if (iface == NULL) {
return PyErr_Format(PyExc_ValueError, "'%s' is not a valid rendermode name", rendermode);
}
/* check options to make sure they're available */
while (PyDict_Next(opts, &pos, &key, &value)) {
const char *name = PyString_AsString(key);
if (name == NULL)
return NULL;
if (!render_mode_supports_option(iface, name)) {
return PyErr_Format(PyExc_ValueError, "'%s' is not a valid option for rendermode '%s'", name, rendermode);
}
}
PyDict_SetItemString(render_mode_options, rendermode, opts);
Py_RETURN_NONE;
}
PyObject *add_custom_render_mode(PyObject *self, PyObject *args) {
const char *rendermode, *parentmode;
PyObject *opts, *options, *pyparent;
if (!PyArg_ParseTuple(args, "sO!", &rendermode, &PyDict_Type, &opts))
return NULL;
/* first, make sure the parent is set correctly */
pyparent = PyDict_GetItemString(opts, "parent");
if (pyparent == NULL)
return PyErr_Format(PyExc_ValueError, "'%s' does not have a parent mode", rendermode);
parentmode = PyString_AsString(pyparent);
if (parentmode == NULL)
return PyErr_Format(PyExc_ValueError, "'%s' does not have a valid parent", rendermode);
/* check that parentmode exists */
if (PyDict_GetItemString(custom_render_modes, parentmode) == NULL) {
unsigned int parent_valid = 0, i;
for (i = 0; render_modes[i] != NULL; i++) {
if (strcmp(render_modes[i]->name, parentmode) == 0) {
parent_valid = 1;
}
}
if (parent_valid == 0)
return PyErr_Format(PyExc_ValueError, "'%s' parent '%s' is not valid", rendermode, parentmode);
}
/* remove and handle options seperately, if needed */
options = PyDict_GetItemString(opts, "options");
if (options != NULL) {
PyObject *opts_copy, *set_opts_args;
opts_copy = PyDict_Copy(opts);
if (opts_copy == NULL)
return NULL;
PyDict_DelItemString(opts_copy, "options");
PyDict_SetItemString(custom_render_modes, rendermode, opts_copy);
Py_DECREF(opts_copy);
/* call set_render_mode_options */
set_opts_args = Py_BuildValue("sO", rendermode, options);
if (set_opts_args == NULL)
return NULL;
if (set_render_mode_options(NULL, set_opts_args) == NULL) {
Py_DECREF(set_opts_args);
return NULL;
}
Py_DECREF(set_opts_args);
} else {
PyDict_SetItemString(custom_render_modes, rendermode, opts);
}
Py_RETURN_NONE;
}

View File

@@ -36,38 +36,86 @@
#define __RENDERMODES_H_INCLUDED__
#include <Python.h>
#include "overviewer.h"
typedef struct {
const char *name;
const char *description;
} RenderModeOption;
/* rendermode interface */
typedef struct _RenderModeInterface RenderModeInterface;
struct _RenderModeInterface {
/* the name of this mode */
const char* name;
const char *name;
/* the label to use in the map */
const char *label;
/* the short description of this render mode */
const char* description;
const char *description;
/* a NULL-terminated list of render mode options, or NULL */
RenderModeOption *options;
/* the rendermode this is derived from, or NULL */
RenderModeInterface *parent;
/* the size of the local storage for this rendermode */
unsigned int data_size;
/* may return non-zero on error */
int (*start)(void *, RenderState *);
/* may return non-zero on error, last arg is options */
int (*start)(void *, RenderState *, PyObject *);
void (*finish)(void *, RenderState *);
/* returns non-zero to skip rendering this block */
int (*occluded)(void *, RenderState *);
/* returns non-zero to skip rendering this block because it's not visible */
int (*occluded)(void *, RenderState *, int, int, int);
/* returns non-zero to skip rendering this block because the user doesn't
* want it visible */
int (*hidden)(void *, RenderState *, int, int, int);
/* last two arguments are img and mask, from texture lookup */
void (*draw)(void *, RenderState *, PyObject *, PyObject *, PyObject *);
};
/* figures out the render mode to use from the given ChunkRenderer */
RenderModeInterface *get_render_mode(RenderState *state);
/* python bindings */
/* A quick note about the difference between occluded and hidden:
*
* Occluded should be used to tell the renderer that a block will not be
* visible in the final image because other blocks will be drawn on top of
* it. This is a potentially *expensive* check that should be used rarely,
* usually only once per block. The idea is this check is expensive, but not
* as expensive as drawing the block itself.
*
* Hidden is used to tell the renderer not to draw the block, usually because
* the current rendermode depends on those blocks being hidden to do its
* job. For example, cave mode uses this to hide non-cave blocks. This check
* should be *cheap*, as it's potentially called many times per block. For
* example, in lighting mode it is called at most 4 times per block.
*/
/* wrapper for passing around rendermodes */
struct _RenderMode {
void *mode;
RenderModeInterface *iface;
RenderState *state;
};
/* functions for creating / using rendermodes */
RenderMode *render_mode_create(const char *mode, RenderState *state);
void render_mode_destroy(RenderMode *self);
int render_mode_occluded(RenderMode *self, int x, int y, int z);
int render_mode_hidden(RenderMode *self, int x, int y, int z);
void render_mode_draw(RenderMode *self, PyObject *img, PyObject *mask, PyObject *mask_light);
/* helper function for reading in rendermode options
works like PyArg_ParseTuple on a dictionary item */
int render_mode_parse_option(PyObject *dict, const char *name, const char *format, ...);
/* python metadata 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);
/* python rendermode options bindings */
PyObject *set_render_mode_options(PyObject *self, PyObject *args);
PyObject *add_custom_render_mode(PyObject *self, PyObject *args);
/* individual rendermode interface declarations follow */
/* NORMAL */
@@ -82,6 +130,14 @@ typedef struct {
PyObject *grass_texture, *leaf_texture, *tall_grass_texture, *tall_fern_texture;
/* top facemask for grass biome tinting */
PyObject *facemask_top;
/* black and white colors for height fading */
PyObject *black_color, *white_color;
float edge_opacity;
unsigned int min_depth;
unsigned int max_depth;
int height_fading;
} RenderModeNormal;
extern RenderModeInterface rendermode_normal;
@@ -115,10 +171,15 @@ typedef struct {
/* can be overridden in derived rendermodes to control lighting
arguments are skylight, blocklight */
float (*calculate_darkness)(unsigned char, unsigned char);
/* can be set to 0 in derived modes to indicate that lighting the chunk
* sides is actually important. Right now, this is used in cave mode
*/
int skip_sides;
float shade_strength;
} RenderModeLighting;
extern RenderModeInterface rendermode_lighting;
inline float get_lighting_coefficient(RenderModeLighting *self, RenderState *state,
int x, int y, int z);
/* NIGHT */
typedef struct {
@@ -141,7 +202,7 @@ extern RenderModeInterface rendermode_spawn;
/* CAVE */
typedef struct {
/* render blocks with lighting mode */
RenderModeNormal parent;
RenderModeLighting parent;
/* data used to know where the surface is */
PyObject *skylight;
@@ -150,10 +211,29 @@ typedef struct {
PyObject *up_left_skylight;
PyObject *up_right_skylight;
/* data used to know where the surface is */
PyObject *blocklight;
PyObject *left_blocklight;
PyObject *right_blocklight;
PyObject *up_left_blocklight;
PyObject *up_right_blocklight;
/* colors used for tinting */
PyObject *depth_colors;
int depth_tinting;
int only_lit;
int lighting;
} RenderModeCave;
extern RenderModeInterface rendermode_cave;
/* MINERAL */
typedef struct {
/* inherits from overlay */
RenderModeOverlay parent;
void *minerals;
} RenderModeMineral;
extern RenderModeInterface rendermode_mineral;
#endif /* __RENDERMODES_H_INCLUDED__ */

View File

@@ -149,7 +149,7 @@ except:
# used to figure out what files to compile
render_modes = ['normal', 'overlay', 'lighting', 'night', 'spawn', 'cave']
render_modes = ['normal', 'overlay', 'lighting', 'night', 'spawn', 'cave', 'mineral']
c_overviewer_files = ['main.c', 'composite.c', 'iterate.c', 'endian.c', 'rendermodes.c']
c_overviewer_files += map(lambda mode: 'rendermode-%s.c' % (mode,), render_modes)