From e3610f8ac5386f522d4fb6e879baea4412ee0ec5 Mon Sep 17 00:00:00 2001 From: Aaron Griffith Date: Sat, 7 Jan 2012 22:50:01 -0500 Subject: [PATCH] moved to a layer-based rendermode system, moved normal mode to base primitive options are now handled partially in the python side, in rendermodes.py --- overviewer_core/__init__.py | 7 - overviewer_core/rendermodes.py | 23 +++ overviewer_core/src/iterate.c | 15 +- overviewer_core/src/overviewer.h | 3 - .../base.c} | 77 +++++---- overviewer_core/src/rendermodes.c | 150 +++++++++++++----- overviewer_core/src/rendermodes.h | 80 +++------- setup.py | 7 +- 8 files changed, 204 insertions(+), 158 deletions(-) rename overviewer_core/src/{rendermode-normal.c => primitives/base.c} (87%) diff --git a/overviewer_core/__init__.py b/overviewer_core/__init__.py index c3d3680..e69de29 100644 --- a/overviewer_core/__init__.py +++ b/overviewer_core/__init__.py @@ -1,7 +0,0 @@ -# c_overviewer must be imported first, because it imports other -# modules; leaving this out can lead to bad dependency loops - -try: - import c_overviewer -except ImportError: - pass diff --git a/overviewer_core/rendermodes.py b/overviewer_core/rendermodes.py index e3386a3..0c988d2 100644 --- a/overviewer_core/rendermodes.py +++ b/overviewer_core/rendermodes.py @@ -16,6 +16,29 @@ from PIL import Image import textures +class RenderPrimitive(object): + options = {} + name = None + def __init__(self, **kwargs): + if self.name is None: + raise RuntimeError("RenderPrimitive cannot be used directly") + + self.option_values = {} + for key, val in kwargs.iteritems(): + if not key in self.options: + raise ValueError("primitive `{0}' has no option `{1}'".format(self.name, key)) + self.option_values[key] = val + +class Base(RenderPrimitive): + 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)", + "nether": "if True, remove the roof of the map. Useful on nether maps. (defualt: False)", + } + name = "base" + # Render 3 blending masks for lighting # first is top (+Z), second is left (-X), third is right (+Y) def generate_facemasks(): diff --git a/overviewer_core/src/iterate.c b/overviewer_core/src/iterate.c index 22cfabd..f1e7bc2 100644 --- a/overviewer_core/src/iterate.c +++ b/overviewer_core/src/iterate.c @@ -18,7 +18,6 @@ #include "overviewer.h" static PyObject *textures = NULL; -static PyObject *support = NULL; unsigned int max_blockid = 0; unsigned int max_data = 0; @@ -47,11 +46,6 @@ PyObject *init_chunk_render(void) { return NULL; } - support = PyImport_ImportModule("overviewer_core.rendermodes"); - if (!support) { - return NULL; - } - tmp = PyObject_GetAttrString(textures, "max_blockid"); if (!tmp) return NULL; @@ -388,7 +382,7 @@ chunk_render(PyObject *self, PyObject *args) { RenderState state; PyObject *regionset; int chunkx, chunkz; - const char* rendermode_name = NULL; + PyObject *modeobj; PyObject *blockmap; int xoff, yoff; @@ -406,14 +400,11 @@ chunk_render(PyObject *self, PyObject *args) { PyObject *t = NULL; - if (!PyArg_ParseTuple(args, "OiiOiisO", &state.regionset, &state.chunkx, &state.chunkz, &state.img, &xoff, &yoff, &rendermode_name, &state.textures)) + if (!PyArg_ParseTuple(args, "OiiOiiOO", &state.regionset, &state.chunkx, &state.chunkz, &state.img, &xoff, &yoff, &modeobj, &state.textures)) return NULL; - /* rendermode support */ - state.support = support; - /* set up the render mode */ - state.rendermode = rendermode = render_mode_create(rendermode_name, &state); + state.rendermode = rendermode = render_mode_create(modeobj, &state); if (rendermode == NULL) { return NULL; // note that render_mode_create will // set PyErr. No need to set it here diff --git a/overviewer_core/src/overviewer.h b/overviewer_core/src/overviewer.h index 1155724..517bb5b 100644 --- a/overviewer_core/src/overviewer.h +++ b/overviewer_core/src/overviewer.h @@ -79,9 +79,6 @@ typedef struct { /* the Texture object */ PyObject *textures; - /* the rendermode support module (rendermodes.py) */ - PyObject *support; - /* the block position and type, and the block array */ int x, y, z; unsigned char block; diff --git a/overviewer_core/src/rendermode-normal.c b/overviewer_core/src/primitives/base.c similarity index 87% rename from overviewer_core/src/rendermode-normal.c rename to overviewer_core/src/primitives/base.c index 39bc679..2ed70fc 100644 --- a/overviewer_core/src/rendermode-normal.c +++ b/overviewer_core/src/primitives/base.c @@ -15,33 +15,53 @@ * with the Overviewer. If not, see . */ -#include "overviewer.h" +#include "../overviewer.h" + +typedef struct { + /* coordinates of the chunk, inside its region file */ + int chunk_x, chunk_y; + /* biome data for the region */ + PyObject *biome_data; + /* grasscolor and foliagecolor lookup tables */ + PyObject *grasscolor, *foliagecolor, *watercolor; + /* biome-compatible grass/leaf textures */ + PyObject *grass_texture; + + /* 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; + int nether; +} PrimitiveBase; static int -rendermode_normal_start(void *data, RenderState *state, PyObject *options) { - RenderModeNormal *self = (RenderModeNormal *)data; +base_start(void *data, RenderState *state, PyObject *support) { + PrimitiveBase *self = (PrimitiveBase *)data; /* load up the given options, first */ self->edge_opacity = 0.15; - if (!render_mode_parse_option(options, "edge_opacity", "f", &(self->edge_opacity))) + if (!render_mode_parse_option(support, "edge_opacity", "f", &(self->edge_opacity))) return 1; self->min_depth = 0; - if (!render_mode_parse_option(options, "min_depth", "I", &(self->min_depth))) + if (!render_mode_parse_option(support, "min_depth", "I", &(self->min_depth))) return 1; self->max_depth = 127; - if (!render_mode_parse_option(options, "max_depth", "I", &(self->max_depth))) + if (!render_mode_parse_option(support, "max_depth", "I", &(self->max_depth))) return 1; self->height_fading = 0; /* XXX skip height fading */ - /*if (!render_mode_parse_option(options, "height_fading", "i", &(self->height_fading))) + /*if (!render_mode_parse_option(support, "height_fading", "i", &(self->height_fading))) return 1;*/ self->nether = 0; - if (!render_mode_parse_option(options, "nether", "i", &(self->nether))) + if (!render_mode_parse_option(support, "nether", "i", &(self->nether))) return 1; /*if (self->height_fading) { @@ -95,8 +115,8 @@ rendermode_normal_start(void *data, RenderState *state, PyObject *options) { } static void -rendermode_normal_finish(void *data, RenderState *state) { - RenderModeNormal *self = (RenderModeNormal *)data; +base_finish(void *data, RenderState *state) { + PrimitiveBase *self = (PrimitiveBase *)data; Py_XDECREF(self->biome_data); Py_XDECREF(self->foliagecolor); @@ -108,7 +128,7 @@ rendermode_normal_finish(void *data, RenderState *state) { } static int -rendermode_normal_occluded(void *data, RenderState *state, int x, int y, int z) { +base_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) && @@ -123,8 +143,8 @@ rendermode_normal_occluded(void *data, RenderState *state, int x, int y, int z) } static int -rendermode_normal_hidden(void *data, RenderState *state, int x, int y, int z) { - RenderModeNormal *self = (RenderModeNormal *)data; +base_hidden(void *data, RenderState *state, int x, int y, int z) { + PrimitiveBase *self = (PrimitiveBase *)data; if (z > self->max_depth || z < self->min_depth) { return 1; @@ -154,8 +174,8 @@ rendermode_normal_hidden(void *data, RenderState *state, int x, int y, int z) { } static void -rendermode_normal_draw(void *data, RenderState *state, PyObject *src, PyObject *mask, PyObject *mask_light) { - RenderModeNormal *self = (RenderModeNormal *)data; +base_draw(void *data, RenderState *state, PyObject *src, PyObject *mask, PyObject *mask_light) { + PrimitiveBase *self = (PrimitiveBase *)data; /* draw the block! */ alpha_over(state->img, src, mask, state->imgx, state->imgy, 0, 0); @@ -360,24 +380,11 @@ 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)"}, - {"nether", "if True, remove the roof of the map. Useful on nether maps. (default: False)"}, - {NULL, NULL} -}; - -RenderModeInterface rendermode_normal = { - "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, +RenderPrimitiveInterface primitive_base = { + "base", sizeof(PrimitiveBase), + base_start, + base_finish, + base_occluded, + base_hidden, + base_draw, }; diff --git a/overviewer_core/src/rendermodes.c b/overviewer_core/src/rendermodes.c index f26f629..fe69983 100644 --- a/overviewer_core/src/rendermodes.c +++ b/overviewer_core/src/rendermodes.c @@ -19,97 +19,166 @@ #include #include -/* list of all render modes, ending in NULL - all of these will be available to the user, so DON'T include modes - that are only useful as a base for other modes. */ -static RenderModeInterface *render_modes[] = { - &rendermode_normal, - &rendermode_lighting, - &rendermode_smooth_lighting, - &rendermode_cave, +extern RenderPrimitiveInterface primitive_base; + +/* list of all render primitives, ending in NULL + all of these will be available to the user, so DON'T include primitives + that are only useful as a base for other primitives. */ +static RenderPrimitiveInterface *render_primitives[] = { + &primitive_base, + //&rendermode_lighting, + //&rendermode_smooth_lighting, + //&rendermode_cave, - &rendermode_spawn, - &rendermode_mineral, + //&rendermode_spawn, + //&rendermode_mineral, NULL }; /* rendermode encapsulation */ -RenderMode *render_mode_create(const char *mode, RenderState *state) { - PyObject *options; - RenderMode *ret = NULL; - RenderModeInterface *iface = NULL; +/* helper to create a single primitive */ +RenderPrimitive *render_primitive_create(PyObject *prim, RenderState *state) { + RenderPrimitive *ret = NULL; + RenderPrimitiveInterface *iface = NULL; unsigned int i; + PyObject *pyname; + const char* name; + + pyname = PyObject_GetAttrString(prim, "name"); + if (!pyname) + return NULL; + name = PyString_AsString(pyname); - for (i = 0; render_modes[i] != NULL; i++) { - if (strcmp(render_modes[i]->name, mode) == 0) { - iface = render_modes[i]; + for (i = 0; render_primitives[i] != NULL; i++) { + if (strcmp(render_primitives[i]->name, name) == 0) { + iface = render_primitives[i]; break; } } + Py_DECREF(pyname); if (iface == NULL) return NULL; - options = PyDict_New(); - - ret = calloc(1, sizeof(RenderMode)); + ret = calloc(1, sizeof(RenderPrimitive)); if (ret == NULL) { - Py_DECREF(options); - return PyErr_Format(PyExc_RuntimeError, "Failed to alloc a rendermode"); + return (RenderPrimitive *)PyErr_Format(PyExc_RuntimeError, "Failed to alloc a render primitive"); } - ret->mode = calloc(1, iface->data_size); - if (ret->mode == NULL) { - Py_DECREF(options); + ret->primitive = calloc(1, iface->data_size); + if (ret->primitive == NULL) { free(ret); - return PyErr_Format(PyExc_RuntimeError, "Failed to alloc rendermode data"); + return (RenderPrimitive *)PyErr_Format(PyExc_RuntimeError, "Failed to alloc render primitive data"); } ret->iface = iface; - ret->state = state; - if (iface->start(ret->mode, state, options)) { - Py_DECREF(options); - free(ret->mode); + if (iface->start(ret->primitive, state, prim)) { + free(ret->primitive); free(ret); return NULL; } - Py_DECREF(options); + return ret; +} + +RenderMode *render_mode_create(PyObject *mode, RenderState *state) { + RenderMode *ret = NULL; + PyObject *mode_fast = NULL; + unsigned int i; + + mode_fast = PySequence_Fast(mode, "Mode is not a sequence type"); + if (!mode_fast) + return NULL; + + ret = calloc(1, sizeof(RenderMode)); + ret->state = state; + ret->num_primitives = PySequence_Length(mode); + ret->primitives = calloc(ret->num_primitives, sizeof(RenderPrimitive*)); + for (i = 0; i < ret->num_primitives; i++) { + PyObject *pyprim = PySequence_Fast_GET_ITEM(mode_fast, i); + RenderPrimitive *prim = render_primitive_create(pyprim, state); + + if (!prim) { + render_mode_destroy(ret); + Py_DECREF(mode_fast); + return NULL; + } + + ret->primitives[i] = prim; + } + return ret; } void render_mode_destroy(RenderMode *self) { - self->iface->finish(self->mode, self->state); - free(self->mode); + unsigned int i; + + for (i = 0; i < self->num_primitives; i++) { + RenderPrimitive *prim = self->primitives[i]; + /* we may be destroying a half-constructed mode, so we need this + check */ + if (prim) { + prim->iface->finish(prim->primitive, self->state); + free(prim->primitive); + free(prim); + } + } + free(self->primitives); 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); + unsigned int i; + int occluded = 0; + for (i = 0; i < self->num_primitives; i++) { + RenderPrimitive *prim = self->primitives[i]; + occluded |= prim->iface->occluded(prim->primitive, self->state, x, y, z); + if (occluded) + return occluded; + } + return occluded; } int render_mode_hidden(RenderMode *self, int x, int y, int z) { - return self->iface->hidden(self->mode, self->state, x, y, z); + unsigned int i; + int hidden = 0; + for (i = 0; i < self->num_primitives; i++) { + RenderPrimitive *prim = self->primitives[i]; + hidden |= prim->iface->hidden(prim->primitive, self->state, x, y, z); + if (hidden) + return hidden; + } + return hidden; } void render_mode_draw(RenderMode *self, PyObject *img, PyObject *mask, PyObject *mask_light) { - self->iface->draw(self->mode, self->state, img, mask, mask_light); + unsigned int i; + for (i = 0; i < self->num_primitives; i++) { + RenderPrimitive *prim = self->primitives[i]; + prim->iface->draw(prim->primitive, self->state, img, mask, mask_light); + } } /* options parse helper */ -int render_mode_parse_option(PyObject *dict, const char *name, const char *format, ...) { +int render_mode_parse_option(PyObject *support, const char *name, const char *format, ...) { va_list ap; - PyObject *item; + PyObject *item, *dict; int ret; - if (dict == NULL || name == NULL) + if (support == NULL || name == NULL) + return 1; + + dict = PyObject_GetAttrString(support, "option_values"); + if (!dict) return 1; item = PyDict_GetItemString(dict, name); - if (item == NULL) + if (item == NULL) { + Py_DECREF(dict); return 1; + }; /* make sure the item we're parsing is a tuple for VaParse to work correctly */ @@ -124,6 +193,7 @@ int render_mode_parse_option(PyObject *dict, const char *name, const char *forma va_end(ap); Py_DECREF(item); + Py_DECREF(dict); if (!ret) { PyObject *errtype, *errvalue, *errtraceback; diff --git a/overviewer_core/src/rendermodes.h b/overviewer_core/src/rendermodes.h index 930f82e..add5bf9 100644 --- a/overviewer_core/src/rendermodes.h +++ b/overviewer_core/src/rendermodes.h @@ -16,20 +16,14 @@ */ /* - * To make a new render mode (the C part, at least): + * To make a new render primitive (the C part, at least): * * * add a data struct and extern'd interface declaration below * - * * fill in this interface struct in rendermode-(yourmode).c - * (see rendermodes-normal.c for an example: the "normal" mode) + * * fill in this interface struct in primitives/(yourmode).c + * (see primitives/base.c for an example: the "base" primitive) * - * * if you want to derive from (say) the "normal" mode, put - * a RenderModeNormal entry at the top of your data struct, and - * be sure to call your parent's functions in your own! - * (see rendermode-night.c for a simple example derived from - * the "lighting" mode) - * - * * add your mode to the list in rendermodes.c + * * add your primitive to the list in rendermodes.c */ #ifndef __RENDERMODES_H_INCLUDED__ @@ -38,30 +32,14 @@ #include #include "overviewer.h" +/* render primitive interface */ 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; - /* the label to use in the map */ - const char *label; - /* the short description of this render mode */ - 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; + const char *name; /* the size of the local storage for this rendermode */ unsigned int data_size; - /* may return non-zero on error, last arg is options */ + /* may return non-zero on error, last arg is the python support object */ int (*start)(void *, RenderState *, PyObject *); void (*finish)(void *, RenderState *); /* returns non-zero to skip rendering this block because it's not visible */ @@ -71,7 +49,7 @@ struct _RenderModeInterface { int (*hidden)(void *, RenderState *, int, int, int); /* last two arguments are img and mask, from texture lookup */ void (*draw)(void *, RenderState *, PyObject *, PyObject *, PyObject *); -}; +} RenderPrimitiveInterface; /* A quick note about the difference between occluded and hidden: * @@ -88,47 +66,32 @@ struct _RenderModeInterface { * example, in lighting mode it is called at most 4 times per block. */ +/* convenience wrapper for a single primitive + interface */ +typedef struct { + void *primitive; + RenderPrimitiveInterface *iface; +} RenderPrimitive; + /* wrapper for passing around rendermodes */ struct _RenderMode { - void *mode; - RenderModeInterface *iface; + unsigned int num_primitives; + RenderPrimitive **primitives; RenderState *state; }; /* functions for creating / using rendermodes */ -RenderMode *render_mode_create(const char *mode, RenderState *state); +RenderMode *render_mode_create(PyObject *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, ...); + works like PyArg_ParseTuple on a support object */ +int render_mode_parse_option(PyObject *support, const char *name, const char *format, ...); -/* individual rendermode interface declarations follow */ - -/* NORMAL */ -typedef struct { - /* coordinates of the chunk, inside its region file */ - int chunk_x, chunk_y; - /* biome data for the region */ - PyObject *biome_data; - /* grasscolor and foliagecolor lookup tables */ - PyObject *grasscolor, *foliagecolor, *watercolor; - /* biome-compatible grass/leaf textures */ - PyObject *grass_texture; - - /* 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; - int nether; -} RenderModeNormal; -extern RenderModeInterface rendermode_normal; +/* XXX individual rendermode interface declarations follow */ +#ifdef OLD_MODES /* OVERLAY */ typedef struct { @@ -235,5 +198,6 @@ typedef struct { void *minerals; } RenderModeMineral; extern RenderModeInterface rendermode_mineral; +#endif /* OLD_MODES */ #endif /* __RENDERMODES_H_INCLUDED__ */ diff --git a/setup.py b/setup.py index db2c3d9..ef01ea4 100755 --- a/setup.py +++ b/setup.py @@ -149,11 +149,12 @@ except Exception: # used to figure out what files to compile -render_modes = ['normal', 'lighting', 'smooth-lighting', 'cave'] -render_modes += ['overlay', 'spawn', 'mineral'] +#render_modes = ['normal', 'lighting', 'smooth-lighting', 'cave'] +#render_modes += ['overlay', 'spawn', 'mineral'] +primitives = ['base',] 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) +c_overviewer_files += map(lambda mode: 'primitives/%s.c' % (mode,), primitives) c_overviewer_files += ['Draw.c'] c_overviewer_includes = ['overviewer.h', 'rendermodes.h']