0

rewrote get_lighting_coefficient in C

This commit is contained in:
Aaron Griffith
2011-03-23 00:03:26 -04:00
parent 24950f6024
commit f5264e9306
4 changed files with 124 additions and 140 deletions

102
chunk.py
View File

@@ -398,108 +398,6 @@ class ChunkRenderer(object):
return pseudo_data return pseudo_data
def calculate_darkness(self, skylight, blocklight):
"""Takes a raw blocklight and skylight, and returns a value
between 0.0 (fully lit) and 1.0 (fully black) that can be used as
an alpha value for a blend with a black source image. It mimics
Minecraft lighting calculations."""
if not self.quadtree.night:
# Daytime
return 1.0 - pow(0.8, 15 - max(blocklight, skylight))
else:
# Nighttime
return 1.0 - pow(0.8, 15 - max(blocklight, skylight - 11))
def get_lighting_coefficient(self, x, y, z, norecurse=False):
"""Calculates the lighting coefficient for the given
coordinate, using default lighting and peeking into
neighboring chunks, if needed. A lighting coefficient of 1.0
means fully black.
Returns a tuple (coefficient, occluded), where occluded is
True if the given coordinate is filled with a solid block, and
therefore the returned coefficient is just the default."""
# placeholders for later data arrays, coordinates
blocks = None
skylight = None
blocklight = None
local_x = x
local_y = y
local_z = z
is_local_chunk = False
# find out what chunk we're in, and translate accordingly
if x >= 0 and y < 16:
blocks = self.blocks
skylight = self.skylight
blocklight = self.blocklight
is_local_chunk = True
elif x < 0:
local_x += 16
blocks = self.left_blocks
skylight = self.left_skylight
blocklight = self.left_blocklight
elif y >= 16:
local_y -= 16
blocks = self.right_blocks
skylight = self.right_skylight
blocklight = self.right_blocklight
# make sure we have a correctly-ranged coordinates and enough
# info about the chunk
if not (blocks is not None and skylight is not None and blocklight is not None and
local_x >= 0 and local_x < 16 and local_y >= 0 and local_y < 16 and
local_z >= 0 and local_z < 128):
# we have no useful info, return default
return (self.calculate_darkness(15, 0), False)
blocktype = blocks[local_x, local_y, local_z]
# special handling for half-blocks
# (don't recurse more than once!)
if blocktype == 44 and not norecurse:
# average gathering variables
averagegather = 0.0
averagecount = 0
# how bright we need before we consider a side "lit"
threshold = self.calculate_darkness(0, 0)
# iterate through all the sides of the block
sides = [(x-1, y, z), (x+1, y, z), (x, y, z-1), (x, y, z+1), (x, y-1, z), (x, y+1, z)]
for side in sides:
val, occ = self.get_lighting_coefficient(*side, norecurse=True)
if (not occ) and (val < threshold):
averagegather += val
averagecount += 1
# if at least one side was lit, return the average
if averagecount > 0:
return (averagegather / averagecount, False)
# calculate the return...
occluded = not (blocktype in transparent_blocks)
# only calculate the non-default coefficient if we're not occluded
if (blocktype == 10) or (blocktype == 11):
# lava blocks should always be lit!
coefficient = 0.0
elif occluded:
coefficient = self.calculate_darkness(15, 0)
else:
coefficient = self.calculate_darkness(skylight[local_x, local_y, local_z], blocklight[local_x, local_y, local_z])
# only say we're occluded if the point is in the CURRENT
# chunk, so that we don't get obvious inter-chunk dependencies
# (we want this here so we still have the default coefficient
# for occluded blocks, even when we don't report them as
# occluded)
if not is_local_chunk:
occluded = False
return (coefficient, occluded)
def chunk_render(self, img=None, xoff=0, yoff=0, cave=False): def chunk_render(self, img=None, xoff=0, yoff=0, cave=False):
"""Renders a chunk with the given parameters, and returns the image. """Renders a chunk with the given parameters, and returns the image.
If img is given, the chunk is rendered to that image object. Otherwise, If img is given, the chunk is rendered to that image object. Otherwise,

View File

@@ -32,6 +32,10 @@
/* macro for getting a value out of a 3D numpy byte array */ /* macro for getting a value out of a 3D numpy byte array */
#define getArrayByte3D(array, x,y,z) (*(unsigned char *)(PyArray_GETPTR3((array), (x), (y), (z)))) #define getArrayByte3D(array, x,y,z) (*(unsigned char *)(PyArray_GETPTR3((array), (x), (y), (z))))
/* generally useful MAX / MIN macros */
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define MIN(a, b) ((a) < (b) ? (a) : (b))
/* in composite.c */ /* in composite.c */
Imaging imaging_python_to_c(PyObject *obj); Imaging imaging_python_to_c(PyObject *obj);
PyObject *alpha_over(PyObject *dest, PyObject *src, PyObject *mask, int dx, PyObject *alpha_over(PyObject *dest, PyObject *src, PyObject *mask, int dx,

View File

@@ -16,37 +16,94 @@
*/ */
#include "overviewer.h" #include "overviewer.h"
#include <math.h>
/* figures out the black_coeff from a given skylight and blocklight,
used in lighting calculations */
static float calculate_darkness(unsigned char skylight, unsigned char blocklight) {
return 1.0f - powf(0.8f, 15.0 - MAX(blocklight, skylight));
}
/* loads the appropriate light data for the given (possibly non-local)
coordinates, and returns a black_coeff */
static inline float
get_lighting_coefficient(RenderModeLighting *self, RenderState *state, int x, int y, int z) {
/* placeholders for later data arrays, coordinates */
PyObject *blocks = NULL;
PyObject *skylight = NULL;
PyObject *blocklight = NULL;
int local_x = x, local_y = y, local_z = z;
/* find out what chunk we're in, and translate accordingly */
if (x >= 0 && y < 16) {
blocks = state->blocks;
skylight = self->skylight;
blocklight = self->blocklight;
} else if (x < 0) {
local_x += 16;
blocks = self->left_blocks;
skylight = self->left_skylight;
blocklight = self->left_blocklight;
} else if (y >= 16) {
local_y -= 16;
blocks = self->right_blocks;
skylight = self->right_skylight;
blocklight = self->right_blocklight;
}
/* make sure we have correctly-ranged coordinates */
if (!(local_x >= 0 && local_x < 16 &&
local_y >= 0 && local_y < 16 &&
local_z >= 0 && local_z < 128)) {
return self->calculate_darkness(15, 0);
}
/* also, make sure we have enough info to correctly calculate lighting */
if (blocks == Py_None || blocks == NULL ||
skylight == Py_None || skylight == NULL ||
blocklight == Py_None || blocklight == NULL) {
return self->calculate_darkness(15, 0);
}
unsigned char block = getArrayByte3D(blocks, local_x, local_y, local_z);
if (block == 44) {
/* TODO special handling for half-blocks! */
}
if (block == 10 || block == 11) {
/* lava blocks should always be lit! */
return 0.0f;
}
unsigned char skylevel = getArrayByte3D(skylight, local_x, local_y, local_z);
unsigned char blocklevel = getArrayByte3D(blocklight, local_x, local_y, local_z);
return self->calculate_darkness(skylevel, blocklevel);
}
/* shades the drawn block with the given facemask/black_color, based on the /* shades the drawn block with the given facemask/black_color, based on the
lighting results from (x, y, z) */ lighting results from (x, y, z) */
static inline void static inline void
do_shading_for_face(PyObject *chunk, int x, int y, int z, PyObject *facemask, PyObject *black_color, do_shading_for_face(RenderModeLighting *self, RenderState *state,
PyObject *img, int imgx, int imgy) { int x, int y, int z, PyObject *facemask) {
// returns new references /* first, check for occlusion if the block is in the local chunk */
PyObject* light_tup = PyObject_CallMethod(chunk, "get_lighting_coefficient", "iii", x, y, z); if (x >= 0 && x < 16 && y >= 0 && y < 16 && z >= 0 && z < 128) {
PyObject *black_coeff_py = PySequence_GetItem(light_tup, 0); unsigned char block = getArrayByte3D(state->blocks, x, y, z);
double black_coeff = PyFloat_AsDouble(black_coeff_py); if (!is_transparent(block)) {
Py_DECREF(black_coeff_py); /* this face isn't visible, so don't draw anything */
return;
}
}
PyObject *face_occlude_py = PySequence_GetItem(light_tup, 1); float black_coeff = get_lighting_coefficient(self, state, x, y, z);
int face_occlude = PyInt_AsLong(face_occlude_py);
Py_DECREF(face_occlude_py);
Py_DECREF(light_tup);
if (!face_occlude) {
//#composite.alpha_over(img, over_color, (imgx, imgy), ImageEnhance.Brightness(facemasks[0]).enhance(black_coeff))
PyObject *mask = PyObject_CallMethod(facemask, "copy", NULL); // new ref PyObject *mask = PyObject_CallMethod(facemask, "copy", NULL); // new ref
//printf("black_coeff: %f\n", black_coeff);
brightness(mask, black_coeff); brightness(mask, black_coeff);
//printf("done with brightness\n"); alpha_over(state->img, self->black_color, mask, state->imgx, state->imgy, 0, 0);
alpha_over(img, black_color, mask, imgx, imgy, 0, 0);
//printf("done with alpha_over\n");
Py_DECREF(mask); Py_DECREF(mask);
}
} }
static int static int
@@ -65,6 +122,19 @@ rendermode_lighting_start(void *data, RenderState *state) {
self->facemasks[1] = PyTuple_GetItem(self->facemasks_py, 1); self->facemasks[1] = PyTuple_GetItem(self->facemasks_py, 1);
self->facemasks[2] = PyTuple_GetItem(self->facemasks_py, 2); self->facemasks[2] = PyTuple_GetItem(self->facemasks_py, 2);
self->skylight = PyObject_GetAttrString(state->self, "skylight");
self->blocklight = PyObject_GetAttrString(state->self, "blocklight");
self->left_blocks = PyObject_GetAttrString(state->self, "left_blocks");
self->left_skylight = PyObject_GetAttrString(state->self, "left_skylight");
self->left_blocklight = PyObject_GetAttrString(state->self, "left_blocklight");
self->right_blocks = PyObject_GetAttrString(state->self, "right_blocks");
self->right_skylight = PyObject_GetAttrString(state->self, "right_skylight");
self->right_blocklight = PyObject_GetAttrString(state->self, "right_blocklight");
self->calculate_darkness = calculate_darkness;
return 0; return 0;
} }
@@ -75,6 +145,17 @@ rendermode_lighting_finish(void *data, RenderState *state) {
Py_DECREF(self->black_color); Py_DECREF(self->black_color);
Py_DECREF(self->facemasks_py); Py_DECREF(self->facemasks_py);
Py_DECREF(self->skylight);
Py_DECREF(self->blocklight);
Py_DECREF(self->left_blocks);
Py_DECREF(self->left_skylight);
Py_DECREF(self->left_blocklight);
Py_DECREF(self->right_blocks);
Py_DECREF(self->right_skylight);
Py_DECREF(self->right_blocklight);
/* now chain up */ /* now chain up */
rendermode_normal.finish(data, state); rendermode_normal.finish(data, state);
} }
@@ -91,20 +172,12 @@ rendermode_lighting_draw(void *data, RenderState *state, PyObject *src, PyObject
rendermode_normal.draw(data, state, src, mask); rendermode_normal.draw(data, state, src, mask);
RenderModeLighting* self = (RenderModeLighting *)data; RenderModeLighting* self = (RenderModeLighting *)data;
PyObject *chunk = state->self;
int x = state->x, y = state->y, z = state->z; int x = state->x, y = state->y, z = state->z;
PyObject **facemasks = self->facemasks;
PyObject *black_color = self->black_color, *img = state->img;
int imgx = state->imgx, imgy = state->imgy;
// FIXME whole-block shading for transparent blocks // TODO whole-block shading for transparent blocks
do_shading_for_face(chunk, x, y, z+1, facemasks[0], black_color, do_shading_for_face(self, state, x, y, z+1, self->facemasks[0]);
img, imgx, imgy); do_shading_for_face(self, state, x-1, y, z, self->facemasks[1]);
do_shading_for_face(chunk, x-1, y, z, facemasks[1], black_color, do_shading_for_face(self, state, x, y+1, z, self->facemasks[2]);
img, imgx, imgy);
do_shading_for_face(chunk, x, y+1, z, facemasks[2], black_color,
img, imgx, imgy);
} }
RenderModeInterface rendermode_lighting = { RenderModeInterface rendermode_lighting = {

View File

@@ -70,6 +70,15 @@ typedef struct {
PyObject *black_color, *facemasks_py; PyObject *black_color, *facemasks_py;
PyObject *facemasks[3]; PyObject *facemasks[3];
/* extra block data, loaded off the chunk class */
PyObject *skylight, *blocklight;
PyObject *left_blocks, *left_skylight, *left_blocklight;
PyObject *right_blocks, *right_skylight, *right_blocklight;
/* can be overridden in derived rendermodes to control lighting
arguments are skylight, blocklight */
float (*calculate_darkness)(unsigned char, unsigned char);
} RenderModeLighting; } RenderModeLighting;
extern RenderModeInterface rendermode_lighting; extern RenderModeInterface rendermode_lighting;