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

@@ -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,10 +310,8 @@ chunk_render(PyObject *self, PyObject *args) {
PyObject *up_left_blocks_py;
PyObject *up_right_blocks_py;
RenderModeInterface *rendermode;
RenderMode *rendermode;
void *rm_data;
PyObject *t = NULL;
if (!PyArg_ParseTuple(args, "OOiiO", &state.self, &state.img, &xoff, &yoff, &state.blockdata_expanded))
@@ -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) {
if (getArrayByte3D(light, x, y, z+1) != 0) {
return 1;
}
/* check if the block is touching skylight */
if (z != 127) {
if (getArrayByte3D(self->skylight, x, y, z+1) != 0) {
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) {
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 (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 (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 == 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 == 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) {
if (y == 0) {
if (up_left_light != Py_None) {
if (getArrayByte3D(up_left_light, 15, y, z) != 0) {
return 1;
}
}
} else {
if (getArrayByte3D(light, x, y-1, z) != 0) {
return 1;
}
}
return 0;
}
/* check for normal occlusion */
/* use ajacent chunks, if not you get blocks spreaded in chunk edges */
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)) &&
@@ -99,7 +95,7 @@ rendermode_cave_occluded(void *data, RenderState *state) {
return 1;
}
}
if ( (x != 0) && (y == 15) ) {
if (state->right_blocks != Py_None) {
if (!is_transparent(getArrayByte3D(state->blocks, x-1, y, z)) &&
@@ -111,7 +107,7 @@ rendermode_cave_occluded(void *data, RenderState *state) {
return 1;
}
}
if ( (x == 0) && (y == 15) ) {
if ((state->left_blocks != Py_None) &&
(state->right_blocks != Py_None)) {
@@ -124,22 +120,57 @@ rendermode_cave_occluded(void *data, RenderState *state) {
return 1;
}
}
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)) {
@@ -155,33 +186,74 @@ 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");
self->right_skylight = PyObject_GetAttrString(state->self, "right_skylight");
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;
}
@@ -189,16 +261,24 @@ static void
rendermode_cave_finish(void *data, RenderState *state) {
RenderModeCave* self;
self = (RenderModeCave *)data;
Py_DECREF(self->skylight);
Py_DECREF(self->left_skylight);
Py_DECREF(self->right_skylight);
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));
tint_with_mask(state->img, r, g, b, 255, mask, state->imgx, state->imgy, 0, 0);
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);
}
}
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);
@@ -219,15 +212,16 @@ static inline void
do_shading_with_mask(RenderModeLighting *self, RenderState *state,
int x, int y, int z, PyObject *mask) {
float black_coeff;
/* 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;
@@ -192,13 +229,27 @@ rendermode_normal_draw(void *data, RenderState *state, PyObject *src, PyObject *
tint_with_mask(state->img, r, g, b, 255, facemask, state->imgx, state->imgy, 0, 0);
}
}
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);
}
/* 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;
}
}
custom = PyDict_GetItemString(custom_render_modes, rendermode);
if (custom) {
PyObject *tmp, *copy = PyDict_Copy(custom);
Py_DECREF(info);
tmp = PyString_FromString(rendermode);
PyDict_SetItemString(copy, "name", tmp);
Py_DECREF(tmp);
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__ */