diff --git a/overviewer_core/rendermodes.py b/overviewer_core/rendermodes.py index 0c988d2..2a58255 100644 --- a/overviewer_core/rendermodes.py +++ b/overviewer_core/rendermodes.py @@ -30,15 +30,30 @@ class RenderPrimitive(object): 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" +class Nether(RenderPrimitive): + name = "nether" + +class HeightFading(RenderPrimitive): + name = "height-fading" + + black_color = Image.new("RGB", (24,24), (0,0,0)) + white_color = Image.new("RGB", (24,24), (255,255,255)) + +class Depth(RenderPrimitive): + name = "depth" + options = { + "min": "lowest level of blocks to render (default: 0)", + "max": "highest level of blocks to render (default: 127)", + } + +class EdgeLines(RenderPrimitive): + name = "edge-lines" + options = { + "opacity": "darkness of the edge lines, from 0.0 to 1.0 (default: 0.15)", + } + # Render 3 blending masks for lighting # first is top (+Z), second is left (-X), third is right (+Y) def generate_facemasks(): @@ -70,8 +85,6 @@ def generate_facemasks(): return (top, left, right) facemasks = generate_facemasks() -black_color = Image.new("RGB", (24,24), (0,0,0)) -white_color = Image.new("RGB", (24,24), (255,255,255)) # Render 128 different color images for color coded depth blending in cave mode def generate_depthcolors(): diff --git a/overviewer_core/src/primitives/base.c b/overviewer_core/src/primitives/base.c index 2ed70fc..4f13c47 100644 --- a/overviewer_core/src/primitives/base.c +++ b/overviewer_core/src/primitives/base.c @@ -26,49 +26,12 @@ typedef struct { 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 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(support, "edge_opacity", "f", &(self->edge_opacity))) - return 1; - - self->min_depth = 0; - if (!render_mode_parse_option(support, "min_depth", "I", &(self->min_depth))) - return 1; - - self->max_depth = 127; - 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(support, "height_fading", "i", &(self->height_fading))) - return 1;*/ - - self->nether = 0; - if (!render_mode_parse_option(support, "nether", "i", &(self->nether))) - return 1; - - /*if (self->height_fading) { - self->black_color = PyObject_GetAttrString(state->chunk, "black_color"); - self->white_color = PyObject_GetAttrString(state->chunk, "white_color"); - }*/ - /* biome-compliant grass mask (includes sides!) */ self->grass_texture = PyObject_GetAttrString(state->textures, "biome_grass_texture"); @@ -123,8 +86,6 @@ base_finish(void *data, RenderState *state) { Py_XDECREF(self->grasscolor); Py_XDECREF(self->watercolor); Py_XDECREF(self->grass_texture); - Py_XDECREF(self->black_color); - Py_XDECREF(self->white_color); } static int @@ -146,30 +107,6 @@ static int 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; - } - - if (self->nether) - { - - /* hide all blocks above all air blocks */ - int below_air = 0; - - while (z < 128) - { - if (getArrayByte3D(state->blocks, x, y, z) == 0) - { - below_air = 1; - break; - } - z++; - } - - if (!below_air) - return 1; - } - return 0; } @@ -319,65 +256,6 @@ base_draw(void *data, RenderState *state, PyObject *src, PyObject *mask, PyObjec if (facemask) 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, 255 * self->edge_opacity}; - - int increment=0; - if (state->block == 44) // half-step - increment=6; - else if ((state->block == 78) || (state->block == 93) || (state->block == 94)) // snow, redstone repeaters (on and off) - increment=9; - - if ((state->x == 15) && (state->up_right_blocks != Py_None)) { - unsigned char side_block = getArrayByte3D(state->up_right_blocks, 0, state->y, state->z); - if (side_block != state->block && is_transparent(side_block)) { - ImagingDrawLine(img_i, state->imgx+12, state->imgy+1+increment, state->imgx+22+1, state->imgy+5+1+increment, &ink, 1); - ImagingDrawLine(img_i, state->imgx+12, state->imgy+increment, state->imgx+22+1, state->imgy+5+increment, &ink, 1); - } - } else if (state->x != 15) { - unsigned char side_block = getArrayByte3D(state->blocks, state->x+1, state->y, state->z); - if (side_block != state->block && is_transparent(side_block)) { - ImagingDrawLine(img_i, state->imgx+12, state->imgy+1+increment, state->imgx+22+1, state->imgy+5+1+increment, &ink, 1); - ImagingDrawLine(img_i, state->imgx+12, state->imgy+increment, state->imgx+22+1, state->imgy+5+increment, &ink, 1); - } - } - // if y != 0 and blocks[x,y-1,z] == 0 - - // chunk boundries are annoying - if ((state->y == 0) && (state->up_left_blocks != Py_None)) { - unsigned char side_block = getArrayByte3D(state->up_left_blocks, state->x, 15, state->z); - if (side_block != state->block && is_transparent(side_block)) { - ImagingDrawLine(img_i, state->imgx, state->imgy+6+1+increment, state->imgx+12+1, state->imgy+1+increment, &ink, 1); - ImagingDrawLine(img_i, state->imgx, state->imgy+6+increment, state->imgx+12+1, state->imgy+increment, &ink, 1); - } - } else if (state->y != 0) { - unsigned char side_block = getArrayByte3D(state->blocks, state->x, state->y-1, state->z); - if (side_block != state->block && is_transparent(side_block)) { - // draw.line(((imgx,imgy+6+increment), (imgx+12,imgy+increment)), fill=(0,0,0), width=1) - ImagingDrawLine(img_i, state->imgx, state->imgy+6+1+increment, state->imgx+12+1, state->imgy+1+increment, &ink, 1); - ImagingDrawLine(img_i, state->imgx, state->imgy+6+increment, state->imgx+12+1, state->imgy+increment, &ink, 1); - } - } - } } RenderPrimitiveInterface primitive_base = { diff --git a/overviewer_core/src/primitives/depth.c b/overviewer_core/src/primitives/depth.c new file mode 100644 index 0000000..4a2899c --- /dev/null +++ b/overviewer_core/src/primitives/depth.c @@ -0,0 +1,56 @@ +/* + * 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 "../overviewer.h" + +typedef struct { + unsigned int min; + unsigned int max; +} PrimitiveDepth; + +static int +depth_start(void *data, RenderState *state, PyObject *support) { + PrimitiveDepth *self = (PrimitiveDepth *)data; + + self->min = 0; + if (!render_mode_parse_option(support, "min", "I", &(self->min))) + return 1; + + self->max = 127; + if (!render_mode_parse_option(support, "max", "I", &(self->max))) + return 1; + + return 0; +} + +static int +depth_hidden(void *data, RenderState *state, int x, int y, int z) { + PrimitiveDepth *self = (PrimitiveDepth *)data; + if (z > self->max || z < self->min) { + return 1; + } + return 0; +} + +RenderPrimitiveInterface primitive_depth = { + "depth", sizeof(PrimitiveDepth), + depth_start, + NULL, + NULL, + depth_hidden, + NULL, +}; diff --git a/overviewer_core/src/primitives/edge-lines.c b/overviewer_core/src/primitives/edge-lines.c new file mode 100644 index 0000000..54b7407 --- /dev/null +++ b/overviewer_core/src/primitives/edge-lines.c @@ -0,0 +1,90 @@ +/* + * 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 "../overviewer.h" + +typedef struct { + float opacity; +} PrimitiveEdgeLines; + +static int +edge_lines_start(void *data, RenderState *state, PyObject *support) { + PrimitiveEdgeLines *self = (PrimitiveEdgeLines *)data; + + self->opacity = 0.15; + if (!render_mode_parse_option(support, "opacity", "f", &(self->opacity))) + return 1; + return 0; +} + +static void +edge_lines_draw(void *data, RenderState *state, PyObject *src, PyObject *mask, PyObject *mask_light) { + PrimitiveEdgeLines *self = (PrimitiveEdgeLines *)data; + + /* 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, 255 * self->opacity}; + + int increment=0; + if (state->block == 44) // half-step + increment=6; + else if ((state->block == 78) || (state->block == 93) || (state->block == 94)) // snow, redstone repeaters (on and off) + increment=9; + + if ((state->x == 15) && (state->up_right_blocks != Py_None)) { + unsigned char side_block = getArrayByte3D(state->up_right_blocks, 0, state->y, state->z); + if (side_block != state->block && is_transparent(side_block)) { + ImagingDrawLine(img_i, state->imgx+12, state->imgy+1+increment, state->imgx+22+1, state->imgy+5+1+increment, &ink, 1); + ImagingDrawLine(img_i, state->imgx+12, state->imgy+increment, state->imgx+22+1, state->imgy+5+increment, &ink, 1); + } + } else if (state->x != 15) { + unsigned char side_block = getArrayByte3D(state->blocks, state->x+1, state->y, state->z); + if (side_block != state->block && is_transparent(side_block)) { + ImagingDrawLine(img_i, state->imgx+12, state->imgy+1+increment, state->imgx+22+1, state->imgy+5+1+increment, &ink, 1); + ImagingDrawLine(img_i, state->imgx+12, state->imgy+increment, state->imgx+22+1, state->imgy+5+increment, &ink, 1); + } + } + // if y != 0 and blocks[x,y-1,z] == 0 + + // chunk boundries are annoying + if ((state->y == 0) && (state->up_left_blocks != Py_None)) { + unsigned char side_block = getArrayByte3D(state->up_left_blocks, state->x, 15, state->z); + if (side_block != state->block && is_transparent(side_block)) { + ImagingDrawLine(img_i, state->imgx, state->imgy+6+1+increment, state->imgx+12+1, state->imgy+1+increment, &ink, 1); + ImagingDrawLine(img_i, state->imgx, state->imgy+6+increment, state->imgx+12+1, state->imgy+increment, &ink, 1); + } + } else if (state->y != 0) { + unsigned char side_block = getArrayByte3D(state->blocks, state->x, state->y-1, state->z); + if (side_block != state->block && is_transparent(side_block)) { + // draw.line(((imgx,imgy+6+increment), (imgx+12,imgy+increment)), fill=(0,0,0), width=1) + ImagingDrawLine(img_i, state->imgx, state->imgy+6+1+increment, state->imgx+12+1, state->imgy+1+increment, &ink, 1); + ImagingDrawLine(img_i, state->imgx, state->imgy+6+increment, state->imgx+12+1, state->imgy+increment, &ink, 1); + } + } + } +} + +RenderPrimitiveInterface primitive_edge_lines = { + "edge-lines", sizeof(PrimitiveEdgeLines), + edge_lines_start, + NULL, + NULL, + NULL, + edge_lines_draw, +}; diff --git a/overviewer_core/src/primitives/height-fading.c b/overviewer_core/src/primitives/height-fading.c new file mode 100644 index 0000000..589111e --- /dev/null +++ b/overviewer_core/src/primitives/height-fading.c @@ -0,0 +1,68 @@ +/* + * 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 "../overviewer.h" + +typedef struct { + PyObject *black_color; + PyObject *white_color; +} PrimitiveHeightFading; + +static int +height_fading_start(void *data, RenderState *state, PyObject *support) { + PrimitiveHeightFading *self = (PrimitiveHeightFading *)data; + + self->black_color = PyObject_GetAttrString(support, "black_color"); + self->white_color = PyObject_GetAttrString(support, "white_color"); + + return 0; +} + +static void +height_fading_finish(void *data, RenderState *state) { + PrimitiveHeightFading *self = (PrimitiveHeightFading *)data; + + Py_DECREF(self->black_color); + Py_DECREF(self->white_color); +} + +static void +height_fading_draw(void *data, RenderState *state, PyObject *src, PyObject *mask, PyObject *mask_light) { + PrimitiveHeightFading *self = (PrimitiveHeightFading *)data; + + /* 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); +} + +RenderPrimitiveInterface primitive_height_fading = { + "height-fading", sizeof(PrimitiveHeightFading), + height_fading_start, + height_fading_finish, + NULL, + NULL, + height_fading_draw, +}; diff --git a/overviewer_core/src/primitives/nether.c b/overviewer_core/src/primitives/nether.c new file mode 100644 index 0000000..3844d1c --- /dev/null +++ b/overviewer_core/src/primitives/nether.c @@ -0,0 +1,42 @@ +/* + * 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 "../overviewer.h" + +static int +nether_hidden(void *data, RenderState *state, int x, int y, int z) { + /* hide all blocks above all air blocks */ + while (z < 128) + { + if (getArrayByte3D(state->blocks, x, y, z) == 0) + { + return 0; + break; + } + z++; + } + return 1; +} + +RenderPrimitiveInterface primitive_nether = { + "nether", 0, + NULL, + NULL, + NULL, + nether_hidden, + NULL, +}; diff --git a/overviewer_core/src/rendermodes.c b/overviewer_core/src/rendermodes.c index fe69983..3e75073 100644 --- a/overviewer_core/src/rendermodes.c +++ b/overviewer_core/src/rendermodes.c @@ -20,12 +20,20 @@ #include extern RenderPrimitiveInterface primitive_base; +extern RenderPrimitiveInterface primitive_nether; +extern RenderPrimitiveInterface primitive_height_fading; +extern RenderPrimitiveInterface primitive_depth; +extern RenderPrimitiveInterface primitive_edge_lines; /* 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, + &primitive_nether, + &primitive_height_fading, + &primitive_depth, + &primitive_edge_lines, //&rendermode_lighting, //&rendermode_smooth_lighting, //&rendermode_cave, @@ -59,25 +67,29 @@ RenderPrimitive *render_primitive_create(PyObject *prim, RenderState *state) { Py_DECREF(pyname); if (iface == NULL) - return NULL; + return (RenderPrimitive *)PyErr_Format(PyExc_RuntimeError, "invalid primitive name: %s", name); ret = calloc(1, sizeof(RenderPrimitive)); if (ret == NULL) { return (RenderPrimitive *)PyErr_Format(PyExc_RuntimeError, "Failed to alloc a render primitive"); } - ret->primitive = calloc(1, iface->data_size); - if (ret->primitive == NULL) { - free(ret); - return (RenderPrimitive *)PyErr_Format(PyExc_RuntimeError, "Failed to alloc render primitive data"); + if (iface->data_size > 0) { + ret->primitive = calloc(1, iface->data_size); + if (ret->primitive == NULL) { + free(ret); + return (RenderPrimitive *)PyErr_Format(PyExc_RuntimeError, "Failed to alloc render primitive data"); + } } ret->iface = iface; - if (iface->start(ret->primitive, state, prim)) { - free(ret->primitive); - free(ret); - return NULL; + if (iface->start) { + if (iface->start(ret->primitive, state, prim)) { + free(ret->primitive); + free(ret); + return NULL; + } } return ret; @@ -120,8 +132,12 @@ void render_mode_destroy(RenderMode *self) { /* 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); + if (prim->iface->finish) { + prim->iface->finish(prim->primitive, self->state); + } + if (prim->primitive) { + free(prim->primitive); + } free(prim); } } @@ -134,7 +150,10 @@ int render_mode_occluded(RenderMode *self, int x, int y, int z) { 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 (prim->iface->occluded) { + occluded |= prim->iface->occluded(prim->primitive, self->state, x, y, z); + } + if (occluded) return occluded; } @@ -146,7 +165,10 @@ int render_mode_hidden(RenderMode *self, int x, int y, int z) { 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 (prim->iface->hidden) { + hidden |= prim->iface->hidden(prim->primitive, self->state, x, y, z); + } + if (hidden) return hidden; } @@ -157,7 +179,9 @@ void render_mode_draw(RenderMode *self, PyObject *img, PyObject *mask, PyObject 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); + if (prim->iface->draw) { + prim->iface->draw(prim->primitive, self->state, img, mask, mask_light); + } } } diff --git a/setup.py b/setup.py index ef01ea4..3c82fc2 100755 --- a/setup.py +++ b/setup.py @@ -151,7 +151,7 @@ except Exception: # used to figure out what files to compile #render_modes = ['normal', 'lighting', 'smooth-lighting', 'cave'] #render_modes += ['overlay', 'spawn', 'mineral'] -primitives = ['base',] +primitives = ['base', 'nether', 'height-fading', 'depth', 'edge-lines'] c_overviewer_files = ['main.c', 'composite.c', 'iterate.c', 'endian.c', 'rendermodes.c'] c_overviewer_files += map(lambda mode: 'primitives/%s.c' % (mode,), primitives)