FossilOrigin-Name: 0ee94739cea768327664b83a23470a366e90ac45fd6c51537b5188268aa8e97c
270 lines
6.1 KiB
Objective-C
270 lines
6.1 KiB
Objective-C
// worldlight.cpp
|
|
|
|
#include "cube.h"
|
|
|
|
#import "DynamicEntity.h"
|
|
#import "Entity.h"
|
|
#import "Monster.h"
|
|
#import "Variable.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);
|
|
// incorrect: light will fade quicker if near edge of the world
|
|
int stepl = l / (float)steps;
|
|
|
|
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 = g / (float)steps;
|
|
int b = light.attr4 << PRECBITS;
|
|
int stepb = b / (float)steps;
|
|
g /= lightscale;
|
|
stepg /= lightscale;
|
|
b /= lightscale;
|
|
stepb /= lightscale;
|
|
for (int i = 0; i < steps; i++) {
|
|
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;
|
|
|
|
for (int i = 0; i < steps; i++) {
|
|
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
|
|
{
|
|
for (int i = 0; i < steps; i++) {
|
|
struct sqr *s = S(x >> PRECBITS, y >> PRECBITS);
|
|
int light = l >> PRECBITS;
|
|
if (light > s->r)
|
|
s->r = s->g = s->b = (unsigned char)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)
|
|
{
|
|
// assumes area not on edge of world
|
|
for (int x = 0; x < a->xs; x++)
|
|
for (int y = 0; y < a->ys; y++) {
|
|
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()
|
|
{
|
|
for (int x = 0; x < ssize; x++) {
|
|
for (int y = 0; y < ssize; y++) {
|
|
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(OFVector3D vold, 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:©];
|
|
|
|
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);
|
|
}
|