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 `(<tuple of conditions>, <target color>)`
* where `<target color>` is a 4 tuple with a `(r, g, b, a)` color
* and `<tuple of conditions>` 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
This commit is contained in:
@@ -1099,6 +1099,32 @@ MineralOverlay
|
|||||||
|
|
||||||
MineralOverlay(minerals=[(64,(255,255,0)), (13,(127,0,127))])
|
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
|
BiomeOverlay
|
||||||
Color the map according to the biome at that point. Either use on
|
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.
|
top of other modes or on top of ClearBase to create a pure overlay.
|
||||||
|
|||||||
@@ -205,6 +205,19 @@ class SpawnOverlay(Overlay):
|
|||||||
class SlimeOverlay(Overlay):
|
class SlimeOverlay(Overlay):
|
||||||
name = "overlay-slime"
|
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):
|
class MineralOverlay(Overlay):
|
||||||
name = "overlay-mineral"
|
name = "overlay-mineral"
|
||||||
options = {
|
options = {
|
||||||
|
|||||||
242
overviewer_core/src/primitives/overlay-structure.c
Normal file
242
overviewer_core/src/primitives/overlay-structure.c
Normal file
@@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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,
|
||||||
|
};
|
||||||
|
|
||||||
Reference in New Issue
Block a user