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
This commit is contained in:
390
overviewer_core/src/primitives/base.c
Normal file
390
overviewer_core/src/primitives/base.c
Normal file
@@ -0,0 +1,390 @@
|
||||
/*
|
||||
* 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"
|
||||
|
||||
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
|
||||
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");
|
||||
|
||||
/* careful now -- C's % operator works differently from python's
|
||||
we can't just do x % 32 like we did before */
|
||||
self->chunk_x = state->chunkx;
|
||||
self->chunk_y = state->chunkz;
|
||||
|
||||
while (self->chunk_x < 0)
|
||||
self->chunk_x += 32;
|
||||
while (self->chunk_y < 0)
|
||||
self->chunk_y += 32;
|
||||
|
||||
self->chunk_x %= 32;
|
||||
self->chunk_y %= 32;
|
||||
|
||||
/* XXX ignore biomes for now :( */
|
||||
/*if (PyObject_IsTrue(use_biomes)) {
|
||||
self->biome_data = PyObject_CallMethod(state->textures, "getBiomeData", "OOO",
|
||||
worlddir, chunk_x_py, chunk_y_py);
|
||||
if (self->biome_data == Py_None) {
|
||||
Py_DECREF(self->biome_data);
|
||||
self->biome_data = NULL;
|
||||
self->foliagecolor = NULL;
|
||||
self->grasscolor = NULL;
|
||||
} else {
|
||||
self->foliagecolor = PyObject_GetAttrString(state->textures, "foliagecolor");
|
||||
self->grasscolor = PyObject_GetAttrString(state->textures, "grasscolor");
|
||||
self->watercolor = PyObject_GetAttrString(state->textures, "watercolor");
|
||||
if (self->watercolor == Py_None)
|
||||
{
|
||||
Py_DECREF(self->watercolor);
|
||||
self->watercolor = NULL;
|
||||
}
|
||||
}
|
||||
} else {*/
|
||||
self->biome_data = NULL;
|
||||
self->foliagecolor = NULL;
|
||||
self->grasscolor = NULL;
|
||||
self->watercolor = NULL;
|
||||
/*}*/
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
base_finish(void *data, RenderState *state) {
|
||||
PrimitiveBase *self = (PrimitiveBase *)data;
|
||||
|
||||
Py_XDECREF(self->biome_data);
|
||||
Py_XDECREF(self->foliagecolor);
|
||||
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
|
||||
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) &&
|
||||
!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))) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static void
|
||||
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);
|
||||
|
||||
/* check for biome-compatible blocks
|
||||
*
|
||||
* NOTES for maintainers:
|
||||
*
|
||||
* To add a biome-compatible block, add an OR'd condition to this
|
||||
* following if block, a case to the first switch statement to handle when
|
||||
* biome info IS available, and another case to the second switch
|
||||
* statement for when biome info ISN'T available.
|
||||
*
|
||||
* Make sure that in textures.py, the generated textures are the
|
||||
* biome-compliant ones! The tinting is now all done here.
|
||||
*/
|
||||
if (/* grass, but not snowgrass */
|
||||
(state->block == 2 && !(state->z < 127 && getArrayByte3D(state->blocks, state->x, state->y, state->z+1) == 78)) ||
|
||||
/* water */
|
||||
state->block == 8 || state->block == 9 ||
|
||||
/* leaves */
|
||||
state->block == 18 ||
|
||||
/* tallgrass, but not dead shrubs */
|
||||
(state->block == 31 && state->block_data != 0) ||
|
||||
/* pumpkin/melon stem, not fully grown. Fully grown stems
|
||||
* get constant brown color (see textures.py) */
|
||||
(((state->block == 104) || (state->block == 105)) && (state->block_data != 7)) ||
|
||||
/* vines */
|
||||
state->block == 106 ||
|
||||
/* lily pads */
|
||||
state->block == 111)
|
||||
{
|
||||
/* do the biome stuff! */
|
||||
PyObject *facemask = mask;
|
||||
unsigned char r, g, b;
|
||||
|
||||
if (state->block == 2) {
|
||||
/* grass needs a special facemask */
|
||||
facemask = self->grass_texture;
|
||||
}
|
||||
|
||||
if (self->biome_data) {
|
||||
/* we have data, so use it! */
|
||||
unsigned int index;
|
||||
PyObject *color = NULL;
|
||||
|
||||
index = ((self->chunk_y * 16) + state->y) * 16 * 32 + (self->chunk_x * 16) + state->x;
|
||||
index = big_endian_ushort(getArrayShort1D(self->biome_data, index));
|
||||
|
||||
switch (state->block) {
|
||||
case 2:
|
||||
/* grass */
|
||||
color = PySequence_GetItem(self->grasscolor, index);
|
||||
break;
|
||||
case 8:
|
||||
case 9:
|
||||
/* water */
|
||||
if (self->watercolor)
|
||||
{
|
||||
color = PySequence_GetItem(self->watercolor, index);
|
||||
} else {
|
||||
color = NULL;
|
||||
facemask = NULL;
|
||||
}
|
||||
break;
|
||||
case 18:
|
||||
/* leaves */
|
||||
if (state->block_data != 2)
|
||||
{
|
||||
/* not birch! */
|
||||
color = PySequence_GetItem(self->foliagecolor, index);
|
||||
} else {
|
||||
/* birch!
|
||||
birch foliage color is flipped XY-ways */
|
||||
unsigned int index_x = 255 - (index % 256);
|
||||
unsigned int index_y = 255 - (index / 256);
|
||||
index = index_y * 256 + index_x;
|
||||
|
||||
color = PySequence_GetItem(self->foliagecolor, index);
|
||||
}
|
||||
break;
|
||||
case 31:
|
||||
/* tall grass */
|
||||
color = PySequence_GetItem(self->grasscolor, index);
|
||||
break;
|
||||
case 104:
|
||||
/* pumpkin stem */
|
||||
color = PySequence_GetItem(self->grasscolor, index);
|
||||
break;
|
||||
case 105:
|
||||
/* melon stem */
|
||||
color = PySequence_GetItem(self->grasscolor, index);
|
||||
break;
|
||||
case 106:
|
||||
/* vines */
|
||||
color = PySequence_GetItem(self->grasscolor, index);
|
||||
break;
|
||||
case 111:
|
||||
/* lily padas */
|
||||
color = PySequence_GetItem(self->grasscolor, index);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
};
|
||||
|
||||
if (color)
|
||||
{
|
||||
/* we've got work to do */
|
||||
|
||||
r = PyInt_AsLong(PyTuple_GET_ITEM(color, 0));
|
||||
g = PyInt_AsLong(PyTuple_GET_ITEM(color, 1));
|
||||
b = PyInt_AsLong(PyTuple_GET_ITEM(color, 2));
|
||||
Py_DECREF(color);
|
||||
}
|
||||
} else {
|
||||
if (state->block == 2 || state->block == 31 ||
|
||||
state->block == 104 || state->block == 105)
|
||||
/* grass and pumpkin/melon stems */
|
||||
{
|
||||
r = 115;
|
||||
g = 175;
|
||||
b = 71;
|
||||
}
|
||||
|
||||
if (state->block == 8 || state->block == 9)
|
||||
/* water */
|
||||
{
|
||||
/* by default water is fine with nothing */
|
||||
facemask = NULL;
|
||||
}
|
||||
|
||||
if (state->block == 18 || state->block == 106 || state->block == 111)
|
||||
/* leaves, vines and lyli pads */
|
||||
{
|
||||
r = 37;
|
||||
g = 118;
|
||||
b = 25;
|
||||
}
|
||||
}
|
||||
|
||||
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 = {
|
||||
"base", sizeof(PrimitiveBase),
|
||||
base_start,
|
||||
base_finish,
|
||||
base_occluded,
|
||||
base_hidden,
|
||||
base_draw,
|
||||
};
|
||||
Reference in New Issue
Block a user