rewrote get_lighting_coefficient in C
This commit is contained in:
102
chunk.py
102
chunk.py
@@ -398,108 +398,6 @@ class ChunkRenderer(object):
|
||||
|
||||
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):
|
||||
"""Renders a chunk with the given parameters, and returns the image.
|
||||
If img is given, the chunk is rendered to that image object. Otherwise,
|
||||
|
||||
@@ -32,6 +32,10 @@
|
||||
/* 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))))
|
||||
|
||||
/* generally useful MAX / MIN macros */
|
||||
#define MAX(a, b) ((a) > (b) ? (a) : (b))
|
||||
#define MIN(a, b) ((a) < (b) ? (a) : (b))
|
||||
|
||||
/* in composite.c */
|
||||
Imaging imaging_python_to_c(PyObject *obj);
|
||||
PyObject *alpha_over(PyObject *dest, PyObject *src, PyObject *mask, int dx,
|
||||
|
||||
@@ -16,37 +16,94 @@
|
||||
*/
|
||||
|
||||
#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
|
||||
lighting results from (x, y, z) */
|
||||
static inline void
|
||||
do_shading_for_face(PyObject *chunk, int x, int y, int z, PyObject *facemask, PyObject *black_color,
|
||||
PyObject *img, int imgx, int imgy) {
|
||||
// returns new references
|
||||
PyObject* light_tup = PyObject_CallMethod(chunk, "get_lighting_coefficient", "iii", x, y, z);
|
||||
PyObject *black_coeff_py = PySequence_GetItem(light_tup, 0);
|
||||
double black_coeff = PyFloat_AsDouble(black_coeff_py);
|
||||
Py_DECREF(black_coeff_py);
|
||||
|
||||
PyObject *face_occlude_py = PySequence_GetItem(light_tup, 1);
|
||||
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
|
||||
//printf("black_coeff: %f\n", black_coeff);
|
||||
brightness(mask, black_coeff);
|
||||
//printf("done with brightness\n");
|
||||
alpha_over(img, black_color, mask, imgx, imgy, 0, 0);
|
||||
//printf("done with alpha_over\n");
|
||||
Py_DECREF(mask);
|
||||
|
||||
do_shading_for_face(RenderModeLighting *self, RenderState *state,
|
||||
int x, int y, int z, PyObject *facemask) {
|
||||
/* first, check for occlusion if the block is in the local chunk */
|
||||
if (x >= 0 && x < 16 && y >= 0 && y < 16 && z >= 0 && z < 128) {
|
||||
unsigned char block = getArrayByte3D(state->blocks, x, y, z);
|
||||
if (!is_transparent(block)) {
|
||||
/* this face isn't visible, so don't draw anything */
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
float black_coeff = get_lighting_coefficient(self, state, x, y, z);
|
||||
|
||||
PyObject *mask = PyObject_CallMethod(facemask, "copy", NULL); // new ref
|
||||
brightness(mask, black_coeff);
|
||||
alpha_over(state->img, self->black_color, mask, state->imgx, state->imgy, 0, 0);
|
||||
Py_DECREF(mask);
|
||||
}
|
||||
|
||||
static int
|
||||
@@ -65,6 +122,19 @@ rendermode_lighting_start(void *data, RenderState *state) {
|
||||
self->facemasks[1] = PyTuple_GetItem(self->facemasks_py, 1);
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -75,6 +145,17 @@ rendermode_lighting_finish(void *data, RenderState *state) {
|
||||
Py_DECREF(self->black_color);
|
||||
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 */
|
||||
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);
|
||||
|
||||
RenderModeLighting* self = (RenderModeLighting *)data;
|
||||
|
||||
PyObject *chunk = state->self;
|
||||
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
|
||||
do_shading_for_face(chunk, x, y, z+1, facemasks[0], black_color,
|
||||
img, imgx, imgy);
|
||||
do_shading_for_face(chunk, x-1, y, z, facemasks[1], black_color,
|
||||
img, imgx, imgy);
|
||||
do_shading_for_face(chunk, x, y+1, z, facemasks[2], black_color,
|
||||
img, imgx, imgy);
|
||||
// TODO whole-block shading for transparent blocks
|
||||
do_shading_for_face(self, state, x, y, z+1, self->facemasks[0]);
|
||||
do_shading_for_face(self, state, x-1, y, z, self->facemasks[1]);
|
||||
do_shading_for_face(self, state, x, y+1, z, self->facemasks[2]);
|
||||
}
|
||||
|
||||
RenderModeInterface rendermode_lighting = {
|
||||
|
||||
@@ -70,6 +70,15 @@ typedef struct {
|
||||
|
||||
PyObject *black_color, *facemasks_py;
|
||||
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;
|
||||
extern RenderModeInterface rendermode_lighting;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user