From 086820ac7226880c38eb8cdbb632fee0ea023fc1 Mon Sep 17 00:00:00 2001 From: Franz Dietrich Date: Wed, 3 Sep 2014 11:28:47 +0200 Subject: [PATCH] Adding StructureOverlay an overlay to color the map according to structures. A usecase to demonstrate a possible application of the extended functionality: **"Rails Overlay that draws only the rails that are on Cobblestone for a subway map."** With this patch it is very easy to achive that: ```python MineralOverlay(minerals=[(((0, 0, 0, 66), (0, -1, 0, 4)), (255, 0, 0, 255)), (((0, 0, 0, 27), (0, -1, 0, 4)), (0, 255, 0, 255))]) ``` In this case the overlay will be red for rails on cobblestone and green for powerrails on cobblestone. The syntax is `(, )` * where `` is a 4 tuple with a `(r, g, b, a)` color * and `` is a tuple with an arbitrary number of conditions with the following syntax: `((relx, rely, relz, blkid), ...)` where the `rel<>` parameters specify the relative coordinates to the block that is checked if it matches bklid. In the example the fist tuple `(0,0,0,66)` checks if at the current position is a rail while `(0,-1,0,4)` checks if at one below the current position is a cobblestone. If both are true then the color `(255, 0, 0, 255)` is used. A Sample Config file exploiting the capabilities: ``` python worlds['My World'] = "~/.minecraft/saves/test/" outputdir = "/tmp/test_render" rendermode = "lighting" renders["render1"] = { 'world': 'My World', 'title': 'A regular render', } renders["render_overlay_dafault_rails"] = { 'world': 'My World', 'title': 'Default Rails', 'rendermode': [ClearBase(), StructureOverlay()], 'overlay': ['render1'], } renders["render_overlay_cust_rails"] = { 'world': 'My World', 'title': 'Custom Rails', #relative coordinates [[(relx, rely, relz, mineral)], (red, green, blue, alpha)] 'rendermode': [ClearBase(), StructureOverlay(structures=[(((0, 0, 0, 66), (0, -1, 0, 4)), (255, 0, 0, 255)), (((0, 0, 0, 27), (0, -1, 0, 4)), (0, 255, 0, 255))])], 'overlay': ['render1'], } ``` The "Default Rails" overlay uses default coloring of the structures overlay. "Custom Rails" uses some custom coloring. fixes overviewer/Minecraft-Overviewer#556 and fixes overviewer/Minecraft-Overviewer#787 --- docs/config.rst | 26 ++ overviewer_core/rendermodes.py | 13 + .../src/primitives/overlay-structure.c | 242 ++++++++++++++++++ 3 files changed, 281 insertions(+) create mode 100644 overviewer_core/src/primitives/overlay-structure.c diff --git a/docs/config.rst b/docs/config.rst index c617f5214..812b4eeb0 100644 --- a/docs/config.rst +++ b/docs/config.rst @@ -1099,6 +1099,32 @@ MineralOverlay MineralOverlay(minerals=[(64,(255,255,0)), (13,(127,0,127))]) +StructureOverlay + Color the map according to patterns of blocks. With this rail overlays + or overlays for other small structures can be realized. It can also be + a MineralOverlay with alpha support. + + This Overlay colors according to a patterns that are specified as + multiple tuples of the form ``(relx, rely, relz, blockid)``. So + by specifying ``(0, -1, 0, 4)`` the block below the current one has to + be a cobblestone. + + One color is then specified as + ``((relblockid1, relblockid2, ...), (r, g, b, a))`` where the + ``relblockid*`` are relative coordinates and the blockid as specified + above. The ``relblockid*`` must match all at the same time for the + color to apply. + + Example:: + + MineralOverlay(minerals=[(((0, 0, 0, 66), (0, -1, 0, 4)), (255, 0, 0, 255)), + (((0, 0, 0, 27), (0, -1, 0, 4)), (0, 255, 0, 255))]) + + In this example all rails(66) on top of cobblestone are rendered in + pure red. And all powerrails(27) are rendered in green. + + If ``minerals`` is not provided, a default rail coloring is used. + BiomeOverlay Color the map according to the biome at that point. Either use on top of other modes or on top of ClearBase to create a pure overlay. diff --git a/overviewer_core/rendermodes.py b/overviewer_core/rendermodes.py index 8fff82485..00cac4a9c 100644 --- a/overviewer_core/rendermodes.py +++ b/overviewer_core/rendermodes.py @@ -205,6 +205,19 @@ class SpawnOverlay(Overlay): class SlimeOverlay(Overlay): name = "overlay-slime" + +class StructureOverlay(Overlay): + name = "overlay-structure" + options = { + 'structures': ('a list of ((((relx, rely, relz), blockid), ...), (r, g, b, a)) tuples for coloring minerals', + [(((0, 0, 0, 66), (0, -1, 0, 4)), (255, 0, 0, 255)), + (((0, 0, 0, 27), (0, -1, 0, 4)), (0, 255, 0, 255)), + (((0, 0, 0, 28), (0, -1, 0, 4)), (255, 255, 0, 255)), + (((0, 0, 0, 157), (0, -1, 0, 4)), (255, 100, 0, 255)), + ]), + } + + class MineralOverlay(Overlay): name = "overlay-mineral" options = { diff --git a/overviewer_core/src/primitives/overlay-structure.c b/overviewer_core/src/primitives/overlay-structure.c new file mode 100644 index 000000000..b0292cb7a --- /dev/null +++ b/overviewer_core/src/primitives/overlay-structure.c @@ -0,0 +1,242 @@ +/* + * 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 . + */ + +#include "overlay.h" + +typedef enum { false, true } bool; + +typedef struct { + /* inherits from overlay */ + RenderPrimitiveOverlay parent; + void *structures; + int numcolors; +} RenderPrimitiveStructure; + +struct Condition{ + int relx, rely, relz; + unsigned char block; +}; + +struct Color { + int numconds; + struct Condition *conditions; + unsigned char r, g, b, a; +}; + +static void get_color(void *data, + RenderState *state, + unsigned char *r, + unsigned char *g, + unsigned char *b, + unsigned char *a) { + /** + * Calculate the color at the current position and store the values to r,g,b,a. + **/ + RenderPrimitiveStructure *self = (RenderPrimitiveStructure *)data; + int x = state->x, z = state->z, y_max, y, col, cond; + struct Color *structures = (struct Color *)(self->structures); + struct Condition * c = NULL; + bool all = true; + y_max = state->y + 1; + + /** + * Check for every color in every y level if all its Conditions are met. + * If all conditions are met for one y level set r,b,g,a accordingly. + **/ + // iterate over all the colors + for ( col = 0; col < self->numcolors; col++) { + // iterate over all y levels + for (y = state->chunky * -16; y <= y_max; y++) { + // iterate over all the conditions + for (cond = 0; cond < structures[col].numconds; cond++) { + all = true; + c = (struct Condition *)&structures[col].conditions[cond]; + // check if the condition does apply and break from the conditions loop if not. + if(!(c->block == get_data(state, BLOCKS, x+c->relx, y+c->rely, z+c->relz))) { + all = false; + break; + } + } + if (all){ + // set the color + *r = structures[col].r; + *g = structures[col].g; + *b = structures[col].b; + *a = structures[col].a; + return; + } + } + } + return; +} + +static int overlay_structure_start(void *data, RenderState *state, PyObject *support) { + /** + * Initializing the search for structures by parsing the arguments and storing them into + * appropriate structures. If no arguments are passed create and use default values. + **/ + PyObject *opt; + RenderPrimitiveStructure* self; + + /* first, chain up */ + int ret = primitive_overlay.start(data, state, support); + if (ret != 0) + return ret; + + /* now do custom initializations */ + self = (RenderPrimitiveStructure *)data; + + // opt is a borrowed reference. do not deref + // store the structures python object into opt. + if (!render_mode_parse_option(support, "structures", "O", &(opt))) + return 1; + + /** + * Check if a sane option was passed. + **/ + if (opt && opt != Py_None) { + struct Color *structures = NULL; + struct Condition *cond = NULL; + Py_ssize_t structures_size = 0, i, cond_size = 0, n = 0; + bool cont = true; + + opt = PySequence_Fast(opt, "expected a sequence"); + if (!opt) { + PyErr_SetString(PyExc_TypeError, "'structures' must be a a sequence"); + return 1; + } + + structures_size = PySequence_Fast_GET_SIZE(opt); + // Getting space on the heap and do not forget to set self->numcolors. + structures = self->structures = calloc(structures_size, sizeof(struct Color)); + self->numcolors = structures_size; + if (structures == NULL) { + PyErr_SetString(PyExc_MemoryError, "failed to allocate memory"); + return 1; + } + + /** + * Try to parse the definitions of conditions and colors. + **/ + if (cont) { + for (i = 0; i < structures_size; i++) { + PyObject *structure = PyList_GET_ITEM(opt, i); + // condspy holding the conditions tuple of variable length (python object) + PyObject *condspy; + // colorpy holding the 4 tuple with r g b a values of the color + PyObject *colorpy; + + // getting the condspy and colorpy out of the structures. + if (!PyArg_ParseTuple(structure, "OO", &condspy, &colorpy)) { + // Exception set automatically + free(structures); + self->structures = NULL; + return 1; + } + + // Parse colorpy into a c-struct. + if (!PyArg_ParseTuple( colorpy, "bbbb", + &structures[i].r, + &structures[i].g, + &structures[i].b, + &structures[i].a)) { + free(structures); + self->structures = NULL; + return 1; + } + + // Convert condspy to a fast sequence + condspy = PySequence_Fast(condspy, "Failed to parse conditions"); + if(condspy == NULL) { + free(structures); + self->structures = NULL; + return 1; + } + + // get the number of conditions. + structures[i].numconds = PySequence_Fast_GET_SIZE(condspy); + // reserve enough memory for the conditions. + cond = calloc(structures[i].numconds, sizeof(struct Condition)); + structures[i].conditions = cond; + + if (structures[i].conditions == NULL) { + PyErr_SetString(PyExc_MemoryError, "failed to allocate memory"); + free(structures); + self->structures = NULL; + return 1; + } + + // iterate over all the conditions and read them. + for (n = 0; n < structures[i].numconds; n++) { + PyObject *ccond = PySequence_Fast_GET_ITEM(condspy, n); + if(!PyArg_ParseTuple( ccond, "iiib", + &cond[n].relx, + &cond[n].rely, + &cond[n].relz, + &cond[n].block)){ + int x = 0; + for(x = 0; x < structures_size; x++){ + free(structures[x].conditions); + } + free(structures); + self->structures = NULL; + return 1; + } + } + } + } + } + + /* setup custom color */ + self->parent.get_color = get_color; + + return 0; +} + +static void overlay_structure_finish(void *data, RenderState *state) { + /* first free all *our* stuff */ + RenderPrimitiveStructure* self = (RenderPrimitiveStructure *)data; + int i = 0; + + if(self->structures) { + // freeing the nested structure + struct Color * m = self->structures; + for(i = 0; i < self->numcolors; i++){ + if(m[i].conditions) + free(m[i].conditions); + } + } + + if (self->structures) { + free(self->structures); + self->structures = NULL; + } + + /* now, chain up */ + primitive_overlay.finish(data, state); +} + +RenderPrimitiveInterface primitive_overlay_structure = { + "overlay-structure", + sizeof(RenderPrimitiveStructure), + overlay_structure_start, + overlay_structure_finish, + NULL, + NULL, + overlay_draw, +}; +