Minecraft-Overviewer/overviewer_core/src/primitives/smooth-lighting.c

243 lines
8.0 KiB
C

/*
* 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 <math.h>
#include "../block_class.h"
#include "../mc_id.h"
#include "../overviewer.h"
#include "lighting.h"
typedef struct {
/* inherits from lighting */
RenderPrimitiveLighting parent;
} RenderPrimitiveSmoothLighting;
/* structure representing one corner of a face (see below) */
struct SmoothLightingCorner {
/* where this corner shows up on each block texture */
int imgx, imgy;
/* the two block offsets that (together) determine the 4 blocks to use */
int dx1, dy1, dz1;
int dx2, dy2, dz2;
};
/* structure for rule table handling lighting */
struct SmoothLightingFace {
/* offset from current coordinate to the block this face points towards
used for occlusion calculations, and as a base for later */
int dx, dy, dz;
/* the points that form the corners of this face */
struct SmoothLightingCorner corners[4];
/* pairs of (x,y) in order, as touch-up points, or NULL for none */
int* touch_up_points;
unsigned int num_touch_up_points;
};
/* top face touchups, pulled from textures.py (_build_block) */
static int top_touchups[] = {1, 5, 3, 4, 5, 3, 7, 2, 9, 1, 11, 0};
/* the lighting face rule list! */
static struct SmoothLightingFace lighting_rules[] = {
/* since this is getting a little insane, here's the general layout:
{dx, dy, dz, { // direction this face is towards
// now, a list of 4 corners...
{imgx, imgy, // where the corner is on the block image
x1, y1, z1, // two vectors, describing the 4 (!!!)
x2, y2, z2}, // blocks neighboring this corner
// ...
},
{x, y, x, y}, 2}, // touch-up points, and how many there are (may be NULL)
// ...
*/
/* top */
{0, 1, 0, {
{0, 6, -1, 0, 0, 0, 0, -1},
{12, 0, 1, 0, 0, 0, 0, -1},
{24, 6, 1, 0, 0, 0, 0, 1},
{12, 12, -1, 0, 0, 0, 0, 1},
},
top_touchups,
6},
/* left */
{-1, 0, 0, {
{0, 18, 0, 0, -1, 0, -1, 0},
{0, 6, 0, 0, -1, 0, 1, 0},
{12, 12, 0, 0, 1, 0, 1, 0},
{12, 24, 0, 0, 1, 0, -1, 0},
},
NULL,
0},
/* right */
{0, 0, 1, {
{24, 6, 1, 0, 0, 0, 1, 0},
{12, 12, -1, 0, 0, 0, 1, 0},
{12, 24, -1, 0, 0, 0, -1, 0},
{24, 18, 1, 0, 0, 0, -1, 0},
},
NULL,
0},
};
/* helpers for indexing the rule list */
enum {
FACE_TOP = 0,
FACE_LEFT = 1,
FACE_RIGHT = 2,
};
static void
do_shading_with_rule(RenderPrimitiveSmoothLighting* self, RenderState* state, struct SmoothLightingFace face) {
int i;
RenderPrimitiveLighting* lighting = (RenderPrimitiveLighting*)self;
int x = state->imgx, y = state->imgy;
struct SmoothLightingCorner* pts = face.corners;
float comp_shade_strength = 1.0 - lighting->strength;
unsigned char pts_r[4] = {0, 0, 0, 0};
unsigned char pts_g[4] = {0, 0, 0, 0};
unsigned char pts_b[4] = {0, 0, 0, 0};
int cx = state->x + face.dx;
int cy = state->y + face.dy;
int cz = state->z + face.dz;
/* first, check for occlusion if the block is in the local chunk */
if (lighting_is_face_occluded(state, 0, cx, cy, cz))
return;
/* calculate the lighting colors for each point */
for (i = 0; i < 4; i++) {
unsigned char r, g, b;
unsigned int rgather = 0, ggather = 0, bgather = 0;
get_lighting_color(lighting, state, cx, cy, cz,
&r, &g, &b);
rgather += r;
ggather += g;
bgather += b;
get_lighting_color(lighting, state,
cx + pts[i].dx1, cy + pts[i].dy1, cz + pts[i].dz1,
&r, &g, &b);
rgather += r;
ggather += g;
bgather += b;
get_lighting_color(lighting, state,
cx + pts[i].dx2, cy + pts[i].dy2, cz + pts[i].dz2,
&r, &g, &b);
rgather += r;
ggather += g;
bgather += b;
/* FIXME special far corner handling */
get_lighting_color(lighting, state,
cx + pts[i].dx1 + pts[i].dx2, cy + pts[i].dy1 + pts[i].dy2, cz + pts[i].dz1 + pts[i].dz2,
&r, &g, &b);
rgather += r;
ggather += g;
bgather += b;
rgather += (255 * 4 - rgather) * comp_shade_strength;
ggather += (255 * 4 - ggather) * comp_shade_strength;
bgather += (255 * 4 - bgather) * comp_shade_strength;
pts_r[i] = rgather / 4;
pts_g[i] = ggather / 4;
pts_b[i] = bgather / 4;
}
/* draw the face */
draw_triangle(state->img, 1,
x + pts[0].imgx, y + pts[0].imgy, pts_r[0], pts_g[0], pts_b[0],
x + pts[1].imgx, y + pts[1].imgy, pts_r[1], pts_g[1], pts_b[1],
x + pts[2].imgx, y + pts[2].imgy, pts_r[2], pts_g[2], pts_b[2],
x, y, face.touch_up_points, face.num_touch_up_points);
draw_triangle(state->img, 0,
x + pts[0].imgx, y + pts[0].imgy, pts_r[0], pts_g[0], pts_b[0],
x + pts[2].imgx, y + pts[2].imgy, pts_r[2], pts_g[2], pts_b[2],
x + pts[3].imgx, y + pts[3].imgy, pts_r[3], pts_g[3], pts_b[3],
x, y, NULL, 0);
}
static int
smooth_lighting_start(void* data, RenderState* state, PyObject* support) {
/* first, chain up */
int ret = primitive_lighting.start(data, state, support);
if (ret != 0)
return ret;
return 0;
}
static void
smooth_lighting_finish(void* data, RenderState* state) {
/* nothing special to do */
primitive_lighting.finish(data, state);
}
static void
smooth_lighting_draw(void* data, RenderState* state, PyObject* src, PyObject* mask, PyObject* mask_light) {
int light_top = 1;
int light_left = 1;
int light_right = 1;
RenderPrimitiveSmoothLighting* self = (RenderPrimitiveSmoothLighting*)data;
/* special case for leaves, water 8, water 9, ice 79
-- these are also smooth-lit! */
if (!block_class_is_subset(state->block, (mc_block_t[]){block_leaves, block_flowing_water, block_water, block_ice}, 4) && is_transparent(state->block)) {
/* transparent blocks are rendered as usual, with flat lighting */
primitive_lighting.draw(data, state, src, mask, mask_light);
return;
}
/* non-transparent blocks get the special smooth treatment */
/* special code for water */
if (state->block == block_water) {
if (!(state->block_pdata & (1 << 4)))
light_top = 0;
if (!(state->block_pdata & (1 << 1)))
light_left = 0;
if (!(state->block_pdata & (1 << 2)))
light_right = 0;
}
if (light_top)
do_shading_with_rule(self, state, lighting_rules[FACE_TOP]);
if (light_left)
do_shading_with_rule(self, state, lighting_rules[FACE_LEFT]);
if (light_right)
do_shading_with_rule(self, state, lighting_rules[FACE_RIGHT]);
}
RenderPrimitiveInterface primitive_smooth_lighting = {
"smooth-lighting",
sizeof(RenderPrimitiveSmoothLighting),
smooth_lighting_start,
smooth_lighting_finish,
NULL,
NULL,
smooth_lighting_draw,
};