diff --git a/overviewer_core/src/rendermode-lighting.c b/overviewer_core/src/rendermode-lighting.c index 476d1d2..395061a 100644 --- a/overviewer_core/src/rendermode-lighting.c +++ b/overviewer_core/src/rendermode-lighting.c @@ -18,10 +18,37 @@ #include "overviewer.h" #include -/* figures out the black_coeff from a given skylight and blocklight, +/* figures out the color 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)); +static void +calculate_light_color(void *data, + unsigned char skylight, unsigned char blocklight, + unsigned char *r, unsigned char *g, unsigned char *b) { + unsigned char v = 255 * powf(0.8f, 15.0 - MAX(blocklight, skylight)); + *r = v; + *g = v; + *b = v; +} + +/* fancy version that uses the colored light texture */ +static void +calculate_light_color_fancy(void *data, + unsigned char skylight, unsigned char blocklight, + unsigned char *r, unsigned char *g, unsigned char *b) { + RenderModeLighting *mode = (RenderModeLighting *)(data); + unsigned int index; + PyObject *color; + + blocklight = MAX(blocklight, skylight); + + index = skylight + blocklight * 16; + color = PySequence_GetItem(mode->lightcolor, index); + + *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); } /* loads the appropriate light data for the given (possibly non-local) @@ -124,9 +151,10 @@ estimate_blocklevel(RenderModeLighting *self, RenderState *state, return blocklevel; } -inline float -get_lighting_coefficient(RenderModeLighting *self, RenderState *state, - int x, int y, int z) { +inline void +get_lighting_color(RenderModeLighting *self, RenderState *state, + int x, int y, int z, + unsigned char *r, unsigned char *g, unsigned char *b) { /* placeholders for later data arrays, coordinates */ PyObject *blocks = NULL; @@ -157,7 +185,8 @@ get_lighting_coefficient(RenderModeLighting *self, RenderState *state, local_y >= 0 && local_y < 16 && local_z >= 0 && local_z < 128)) { - return self->calculate_darkness(15, 0); + self->calculate_light_color(self, 15, 0, r, g, b); + return; } /* also, make sure we have enough info to correctly calculate lighting */ @@ -165,7 +194,8 @@ get_lighting_coefficient(RenderModeLighting *self, RenderState *state, skylight == Py_None || skylight == NULL || blocklight == Py_None || blocklight == NULL) { - return self->calculate_darkness(15, 0); + self->calculate_light_color(self, 15, 0, r, g, b); + return; } block = getArrayByte3D(blocks, local_x, local_y, local_z); @@ -203,15 +233,16 @@ get_lighting_coefficient(RenderModeLighting *self, RenderState *state, return 0.0f; } - return self->calculate_darkness(skylevel, blocklevel); + self->calculate_light_color(self, MIN(skylevel, 15), MIN(blocklevel, 15), r, g, b); } -/* shades the drawn block with the given facemask/black_color, based on the +/* shades the drawn block with the given facemask, based on the lighting results from (x, y, z) */ static inline void do_shading_with_mask(RenderModeLighting *self, RenderState *state, int x, int y, int z, PyObject *mask) { - float black_coeff; + unsigned char r, g, b; + float comp_shade_strength; /* 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) { @@ -241,9 +272,14 @@ do_shading_with_mask(RenderModeLighting *self, RenderState *state, } } - black_coeff = get_lighting_coefficient(self, state, x, y, z); - black_coeff *= self->shade_strength; - alpha_over_full(state->img, self->black_color, mask, black_coeff, state->imgx, state->imgy, 0, 0); + get_lighting_color(self, state, x, y, z, &r, &g, &b); + comp_shade_strength = 1.0 - self->shade_strength; + + r += (255 - r) * comp_shade_strength; + g += (255 - g) * comp_shade_strength; + b += (255 - b) * comp_shade_strength; + + tint_with_mask(state->img, r, g, b, 255, mask, state->imgx, state->imgy, 0, 0); } static int @@ -263,8 +299,11 @@ rendermode_lighting_start(void *data, RenderState *state, PyObject *options) { self->shade_strength = 1.0; if (!render_mode_parse_option(options, "shade_strength", "f", &(self->shade_strength))) return 1; + + self->color_light = 0; + if (!render_mode_parse_option(options, "color_light", "i", &(self->color_light))) + return 1; - self->black_color = PyObject_GetAttrString(state->chunk, "black_color"); self->facemasks_py = PyObject_GetAttrString(state->chunk, "facemasks"); // borrowed references, don't need to be decref'd self->facemasks[0] = PyTuple_GetItem(self->facemasks_py, 0); @@ -278,7 +317,20 @@ rendermode_lighting_start(void *data, RenderState *state, PyObject *options) { self->right_skylight = PyObject_GetAttrString(state->self, "right_skylight"); self->right_blocklight = PyObject_GetAttrString(state->self, "right_blocklight"); - self->calculate_darkness = calculate_darkness; + self->calculate_light_color = calculate_light_color; + + if (self->color_light) { + self->lightcolor = PyObject_CallMethod(state->textures, "loadLightColor", ""); + if (self->lightcolor == Py_None) { + Py_DECREF(self->lightcolor); + self->lightcolor = NULL; + self->color_light = 0; + } else { + self->calculate_light_color = calculate_light_color_fancy; + } + } else { + self->lightcolor = NULL; + } return 0; } @@ -287,7 +339,6 @@ static void rendermode_lighting_finish(void *data, RenderState *state) { RenderModeLighting *self = (RenderModeLighting *)data; - Py_DECREF(self->black_color); Py_DECREF(self->facemasks_py); Py_DECREF(self->skylight); @@ -352,6 +403,7 @@ rendermode_lighting_draw(void *data, RenderState *state, PyObject *src, PyObject const RenderModeOption rendermode_lighting_options[] = { {"shade_strength", "how dark to make the shadows, from 0.0 to 1.0 (default: 1.0)"}, + {"color_light", "whether to use colored light (default: False)"}, {NULL, NULL} }; diff --git a/overviewer_core/src/rendermode-night.c b/overviewer_core/src/rendermode-night.c index 6e08aaf..d4c1e6c 100644 --- a/overviewer_core/src/rendermode-night.c +++ b/overviewer_core/src/rendermode-night.c @@ -18,11 +18,36 @@ #include "overviewer.h" #include -/* figures out the black_coeff from a given skylight and blocklight, used in +/* figures out the color from a given skylight and blocklight, used in lighting calculations -- note this is *different* from the one in rendermode-lighting.c (the "skylight - 11" part) */ -static float calculate_darkness(unsigned char skylight, unsigned char blocklight) { - return 1.0f - powf(0.8f, 15.0 - MAX(blocklight, skylight - 11)); +static void +calculate_light_color(void *data, + unsigned char skylight, unsigned char blocklight, + unsigned char *r, unsigned char *g, unsigned char *b) { + unsigned char v = 255 * powf(0.8f, 15.0 - MAX(blocklight, skylight - 11)); + *r = v; + *g = v; + *b = v; +} + +/* fancy version that uses the colored light texture */ +static void +calculate_light_color_fancy(void *data, + unsigned char skylight, unsigned char blocklight, + unsigned char *r, unsigned char *g, unsigned char *b) { + RenderModeLighting *mode = (RenderModeLighting *)(data); + unsigned int index; + PyObject *color; + + index = skylight + blocklight * 16; + color = PySequence_GetItem(mode->lightcolor, index); + + *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); } static int @@ -36,7 +61,9 @@ rendermode_night_start(void *data, RenderState *state, PyObject *options) { /* override the darkness function with our night version! */ self = (RenderModeNight *)data; - self->parent.calculate_darkness = calculate_darkness; + self->parent.calculate_light_color = calculate_light_color; + if (self->parent.color_light) + self->parent.calculate_light_color = calculate_light_color_fancy; return 0; } diff --git a/overviewer_core/src/rendermodes.h b/overviewer_core/src/rendermodes.h index d65426c..1a145cf 100644 --- a/overviewer_core/src/rendermodes.h +++ b/overviewer_core/src/rendermodes.h @@ -158,7 +158,7 @@ typedef struct { /* inherits from normal render mode */ RenderModeNormal parent; - PyObject *black_color, *facemasks_py; + PyObject *facemasks_py; PyObject *facemasks[3]; /* extra data, loaded off the chunk class */ @@ -166,9 +166,12 @@ typedef struct { PyObject *left_skylight, *left_blocklight; PyObject *right_skylight, *right_blocklight; + /* light color image, loaded if color_light is True */ + PyObject *lightcolor; + /* can be overridden in derived rendermodes to control lighting - arguments are skylight, blocklight */ - float (*calculate_darkness)(unsigned char, unsigned char); + arguments are data, skylight, blocklight, return RGB */ + void (*calculate_light_color)(void *, unsigned char, unsigned char, unsigned char *, unsigned char *, unsigned char *); /* can be set to 0 in derived modes to indicate that lighting the chunk * sides is actually important. Right now, this is used in cave mode @@ -176,6 +179,7 @@ typedef struct { int skip_sides; float shade_strength; + int color_light; } RenderModeLighting; extern RenderModeInterface rendermode_lighting; diff --git a/overviewer_core/textures.py b/overviewer_core/textures.py index 0fac858..4914201 100644 --- a/overviewer_core/textures.py +++ b/overviewer_core/textures.py @@ -91,7 +91,7 @@ def _find_file(filename, mode="rb", verbose=False): for jarpath in jarpaths: if os.path.exists(jarpath): jar = zipfile.ZipFile(jarpath) - for jarfilename in [filename, 'misc/' + filename]: + for jarfilename in [filename, 'misc/' + filename, 'environment/' + filename]: try: if verbose: logging.info("Found %s in '%s'", jarfilename, jarpath) return jar.open(jarfilename) @@ -2278,6 +2278,20 @@ def getBiomeData(worlddir, chunkX, chunkY): currentBiomeData = data return data +lightcolor = None +lightcolor_checked = False +def loadLightColor(): + global lightcolor, lightcolor_checked + + if not lightcolor_checked: + lightcolor_checked = True + try: + lightcolor = list(_load_image("light_normal.png").getdata()) + except: + logging.warning("Light color image could not be found.") + lightcolor = None + return lightcolor + # This set holds block ids that require special pre-computing. These are typically # things that require ancillary data to render properly (i.e. ladder plus orientation) # A good source of information is: