Cube  Artifact [3113d251d8]

Artifact 3113d251d85b5e05532f40d5f6792402dacd641eb954c06123f8acde215a1c28:


// worldlight.cpp

#include "cube.h"

#import "DynamicEntity.h"
#import "Entity.h"
#import "Monster.h"

extern bool hasoverbright;

VAR(lightscale, 1, 4, 100);

// done in realtime, needs to be fast
void
lightray(float bx, float by, Entity *light)
{
	float lx = light.x + (rnd(21) - 10) * 0.1f;
	float ly = light.y + (rnd(21) - 10) * 0.1f;
	float dx = bx - lx;
	float dy = by - ly;
	float dist = (float)sqrt(dx * dx + dy * dy);
	if (dist < 1.0f)
		return;
	int reach = light.attr1;
	int steps = (int)(reach * reach * 1.6f /
	    dist); // can change this for speedup/quality?
	const int PRECBITS = 12;
	const float PRECF = 4096.0f;
	int x = (int)(lx * PRECF);
	int y = (int)(ly * PRECF);
	int l = light.attr2 << PRECBITS;
	int stepx = (int)(dx / (float)steps * PRECF);
	int stepy = (int)(dy / (float)steps * PRECF);
	int stepl =
	    fast_f2nat(l / (float)steps); // incorrect: light will fade quicker
	                                  // if near edge of the world

	if (hasoverbright) {
		l /= lightscale;
		stepl /= lightscale;

		// coloured light version, special case because most lights are
		// white
		if (light.attr3 || light.attr4) {
			int dimness = rnd(
			    (255 -
			        (light.attr2 + light.attr3 + light.attr4) / 3) /
			        16 +
			    1);
			x += stepx * dimness;
			y += stepy * dimness;

			if (OUTBORD(x >> PRECBITS, y >> PRECBITS))
				return;

			int g = light.attr3 << PRECBITS;
			int stepg = fast_f2nat(g / (float)steps);
			int b = light.attr4 << PRECBITS;
			int stepb = fast_f2nat(b / (float)steps);
			g /= lightscale;
			stepg /= lightscale;
			b /= lightscale;
			stepb /= lightscale;
			loopi(steps)
			{
				struct sqr *s = S(x >> PRECBITS, y >> PRECBITS);
				int tl = (l >> PRECBITS) + s->r;
				s->r = tl > 255 ? 255 : tl;
				tl = (g >> PRECBITS) + s->g;
				s->g = tl > 255 ? 255 : tl;
				tl = (b >> PRECBITS) + s->b;
				s->b = tl > 255 ? 255 : tl;
				if (SOLID(s))
					return;
				x += stepx;
				y += stepy;
				l -= stepl;
				g -= stepg;
				b -= stepb;
				stepl -= 25;
				stepg -= 25;
				stepb -= 25;
			}
		} else // white light, special optimized version
		{
			int dimness = rnd((255 - light.attr2) / 16 + 1);
			x += stepx * dimness;
			y += stepy * dimness;

			if (OUTBORD(x >> PRECBITS, y >> PRECBITS))
				return;

			loopi(steps)
			{
				struct sqr *s = S(x >> PRECBITS, y >> PRECBITS);
				int tl = (l >> PRECBITS) + s->r;
				s->r = s->g = s->b = tl > 255 ? 255 : tl;
				if (SOLID(s))
					return;
				x += stepx;
				y += stepy;
				l -= stepl;
				stepl -= 25;
			}
		}
	} else // the old (white) light code, here for the few people with old
	       // video cards that don't support overbright
	{
		loopi(steps)
		{
			struct sqr *s = S(x >> PRECBITS, y >> PRECBITS);
			int light = l >> PRECBITS;
			if (light > s->r)
				s->r = s->g = s->b = (uchar)light;
			if (SOLID(s))
				return;
			x += stepx;
			y += stepy;
			l -= stepl;
		}
	}
}

void
calclightsource(Entity *l)
{
	int reach = l.attr1;
	int sx = l.x - reach;
	int ex = l.x + reach;
	int sy = l.y - reach;
	int ey = l.y + reach;

	rndreset();

	const float s = 0.8f;

	for (float sx2 = (float)sx; sx2 <= ex; sx2 += s * 2) {
		lightray(sx2, (float)sy, l);
		lightray(sx2, (float)ey, l);
	}
	for (float sy2 = sy + s; sy2 <= ey - s; sy2 += s * 2) {
		lightray((float)sx, sy2, l);
		lightray((float)ex, sy2, l);
	}

	rndtime();
}

// median filter, smooths out random noise in light and makes it more mipable
void
postlightarea(const struct block *a)
{
	loop(x, a->xs) loop(y, a->ys) // assumes area not on edge of world
	{
		struct sqr *s = S(x + a->x, y + a->y);
#define median(m)                                                            \
	s->m =                                                               \
	    (s->m * 2 + SW(s, 1, 0)->m * 2 + SW(s, 0, 1)->m * 2 +            \
	        SW(s, -1, 0)->m * 2 + SW(s, 0, -1)->m * 2 + SW(s, 1, 1)->m + \
	        SW(s, 1, -1)->m + SW(s, -1, 1)->m + SW(s, -1, -1)->m) /      \
	    14; // median is 4/2/1 instead
		median(r);
		median(g);
		median(b);
	}

	remip(a, 0);
}

void
calclight()
{
	loop(x, ssize) loop(y, ssize)
	{
		struct sqr *s = S(x, y);
		s->r = s->g = s->b = 10;
	}

	for (Entity *e in ents)
		if (e.type == LIGHT)
			calclightsource(e);

	struct block b = { 1, 1, ssize - 2, ssize - 2 };
	postlightarea(&b);
	setvar(@"fullbright", 0);
}

VARP(dynlight, 0, 16, 32);

static OFMutableData *dlights;

void
cleardlights()
{
	while (dlights.count > 0) {
		struct block *backup = *(struct block **)[dlights lastItem];
		[dlights removeLastItem];
		blockpaste(backup);
		OFFreeMemory(backup);
	}
}

void
dodynlight(const OFVector3D *vold, const OFVector3D *v, int reach, int strength,
    DynamicEntity *owner)
{
	if (!reach)
		reach = dynlight;
	if ([owner isKindOfClass:Monster.class])
		reach = reach / 2;
	if (!reach)
		return;
	if (v->x < 0 || v->y < 0 || v->x > ssize || v->y > ssize)
		return;

	int creach = reach + 16; // dependant on lightray random offsets!
	struct block b = { (int)v->x - creach, (int)v->y - creach,
		creach * 2 + 1, creach * 2 + 1 };

	if (b.x < 1)
		b.x = 1;
	if (b.y < 1)
		b.y = 1;
	if (b.xs + b.x > ssize - 2)
		b.xs = ssize - 2 - b.x;
	if (b.ys + b.y > ssize - 2)
		b.ys = ssize - 2 - b.y;

	if (dlights == nil)
		dlights = [[OFMutableData alloc]
		    initWithItemSize:sizeof(struct block *)];

	// backup area before rendering in dynlight
	struct block *copy = blockcopy(&b);
	[dlights addItem:&copy];

	Entity *l = [Entity entity];
	l.x = v->x;
	l.y = v->y;
	l.z = v->z;
	l.attr1 = reach;
	l.type = LIGHT;
	l.attr2 = strength;
	calclightsource(l);
	postlightarea(&b);
}

// utility functions also used by editing code

struct block *
blockcopy(const struct block *s)
{
	struct block *b = OFAllocZeroedMemory(
	    1, sizeof(struct block) + s->xs * s->ys * sizeof(struct sqr));
	*b = *s;
	struct sqr *q = (struct sqr *)(b + 1);
	for (int x = s->x; x < s->xs + s->x; x++)
		for (int y = s->y; y < s->ys + s->y; y++)
			*q++ = *S(x, y);
	return b;
}

void
blockpaste(const struct block *b)
{
	struct sqr *q = (struct sqr *)(b + 1);
	for (int x = b->x; x < b->xs + b->x; x++)
		for (int y = b->y; y < b->ys + b->y; y++)
			*S(x, y) = *q++;
	remipmore(b, 0);
}