scriptcontext and isolatecontext now allow plain text mnemonics for context level arguments (one of CORE, CFG, PROMPT, MAPCFG, MDLCFG) also: - cleanup (some dead code and dead prototypes removed) - some stuff renamed - script errors now show context levels - conc is no longer thread-safe (but slightly faster) DOCREF scriptcontext isolatecontext
441 lines
12 KiB
C++
441 lines
12 KiB
C++
// worldlight.cpp
|
|
|
|
#include "cube.h"
|
|
|
|
#define LIGHTSCALE 4
|
|
|
|
void lightray(float bx, float by, const persistent_entity &light, float fade = 1, bool flicker = false) // done in realtime, needs to be fast
|
|
{
|
|
float lx = light.x+(flicker ? (rnd(21)-10)*0.1f : 0);
|
|
float ly = light.y+(flicker ? (rnd(21)-10)*0.1f : 0);
|
|
float dx = bx-lx;
|
|
float dy = by-ly;
|
|
float dist = sqrtf(dx*dx+dy*dy);
|
|
if(dist<1.0f) return;
|
|
int reach = light.attr1;
|
|
int steps = (int)(reach*reach*1.6f/dist);
|
|
const int PRECBITS = 12;
|
|
const float PRECF = 4096.0f;
|
|
int x = (int)(lx*PRECF);
|
|
int y = (int)(ly*PRECF);
|
|
int fadescale = (int)(fade*PRECF);
|
|
int l = light.attr2*fadescale;
|
|
int stepx = (int)(dx/(float)steps*PRECF);
|
|
int stepy = (int)(dy/(float)steps*PRECF);
|
|
int stepl = (int)(l/(float)steps);
|
|
|
|
if(maxtmus)
|
|
{
|
|
l /= LIGHTSCALE;
|
|
stepl /= LIGHTSCALE;
|
|
|
|
if(light.attr3 || light.attr4) // coloured light version, special case because most lights are white
|
|
{
|
|
if(flicker)
|
|
{
|
|
int dimness = rnd((((255<<PRECBITS)-(int(light.attr2)+int(light.attr3)+int(light.attr4))*fadescale/3)>>(PRECBITS+4))+1);
|
|
x += stepx*dimness;
|
|
y += stepy*dimness;
|
|
}
|
|
|
|
if(OUTBORD(x>>PRECBITS, y>>PRECBITS)) return;
|
|
|
|
int g = light.attr3*fadescale;
|
|
int stepg = (int)(g/(float)steps);
|
|
int b = light.attr4*fadescale;
|
|
int stepb = (int)(b/(float)steps);
|
|
g /= LIGHTSCALE;
|
|
stepg /= LIGHTSCALE;
|
|
b /= LIGHTSCALE;
|
|
stepb /= LIGHTSCALE;
|
|
loopi(steps)
|
|
{
|
|
sqr *s = S(x>>PRECBITS, y>>PRECBITS);
|
|
s->r = min((l>>PRECBITS)+s->r, 255);
|
|
s->g = min((g>>PRECBITS)+s->g, 255);
|
|
s->b = min((b>>PRECBITS)+s->b, 255);
|
|
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
|
|
{
|
|
if(flicker)
|
|
{
|
|
int dimness = rnd((((255<<PRECBITS)-(light.attr2*fadescale))>>(PRECBITS+4))+1);
|
|
x += stepx*dimness;
|
|
y += stepy*dimness;
|
|
}
|
|
|
|
if(OUTBORD(x>>PRECBITS, y>>PRECBITS)) return;
|
|
|
|
if(hdr.ambient > 0xFF) loopi(steps)
|
|
{
|
|
sqr *s = S(x>>PRECBITS, y>>PRECBITS);
|
|
s->r = min((l>>PRECBITS)+s->r, 255);
|
|
s->g = min((l>>PRECBITS)+s->g, 255);
|
|
s->b = min((l>>PRECBITS)+s->b, 255);
|
|
if(SOLID(s)) return;
|
|
x += stepx;
|
|
y += stepy;
|
|
l -= stepl;
|
|
stepl -= 25;
|
|
}
|
|
else loopi(steps)
|
|
{
|
|
sqr *s = S(x>>PRECBITS, y>>PRECBITS);
|
|
s->r = s->g = s->b = min((l>>PRECBITS)+s->r, 255);
|
|
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)
|
|
{
|
|
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(const persistent_entity &l, float fade = 1, bool flicker = true)
|
|
{
|
|
int reach = l.attr1;
|
|
int sx = l.x-reach;
|
|
int ex = l.x+reach;
|
|
int sy = l.y-reach;
|
|
int ey = l.y+reach;
|
|
|
|
const float s = 0.8f;
|
|
|
|
for(float sx2 = (float)sx; sx2<=ex; sx2+=s*2) { lightray(sx2, (float)sy, l, fade, flicker); lightray(sx2, (float)ey, l, fade, flicker); }
|
|
for(float sy2 = sy+s; sy2<=ey-s; sy2+=s*2) { lightray((float)sx, sy2, l, fade, flicker); lightray((float)ex, sy2, l, fade, flicker); }
|
|
}
|
|
|
|
void postlightarealine(sqr *s, int len)
|
|
{
|
|
loopirev(len)
|
|
{
|
|
#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);
|
|
s += 2;
|
|
}
|
|
}
|
|
|
|
void postlightarea(const block &a) // median filter, smooths out random noise in light and makes it more mipable
|
|
{
|
|
int ia = (a.xs + 1) >> 1, ib = a.xs - ia;;
|
|
for(int y = a.ys - 1; y >= 0; y -= 2)
|
|
{
|
|
sqr *s = S(a.x,y+a.y);
|
|
postlightarealine(s, ia);
|
|
postlightarealine(s + 1, ib);
|
|
}
|
|
for(int y = a.ys - 2; y >= 0; y -= 2)
|
|
{
|
|
sqr *s = S(a.x,y+a.y);
|
|
postlightarealine(s, ia);
|
|
postlightarealine(s + 1, ib);
|
|
}
|
|
remip(a);
|
|
}
|
|
|
|
int lastcalclight = 0;
|
|
|
|
VARP(fullbrightlevel, 0, 176, 255);
|
|
|
|
void fullbrightlight(int level)
|
|
{
|
|
if(level < 0) level = fullbrightlevel;
|
|
|
|
loopi(mipsize) world[i].r = world[i].g = world[i].b = level;
|
|
lastcalclight = totalmillis;
|
|
}
|
|
|
|
VARF(ambient, 0, 0, 0xFFFFFF, if(!noteditmode("ambient")) { hdr.ambient = ambient; calclight(); unsavededits++;});
|
|
|
|
void calclight()
|
|
{
|
|
if(editmode)
|
|
{
|
|
servsqr *servworld = createservworld(world, cubicsize);
|
|
calcmapdims(clmapdims, servworld, ssize);
|
|
delete[] servworld;
|
|
}
|
|
uchar r = (hdr.ambient>>16) & 0xFF, g = (hdr.ambient>>8) & 0xFF, b = hdr.ambient & 0xFF;
|
|
if(!r && !g)
|
|
{
|
|
if(!b) b = 10;
|
|
r = g = b;
|
|
}
|
|
else if(!maxtmus) r = g = b = max(max(r, g), b); // the old (white) light code, here for the few people with old video cards that don't support overbright
|
|
sqr *s = S(0,0);
|
|
loopirev(cubicsize)
|
|
{
|
|
s->r = r;
|
|
s->g = g;
|
|
s->b = b;
|
|
s++;
|
|
}
|
|
|
|
seedMT(ents.length() + hdr.maprevision); // static seed -> nothing random here
|
|
|
|
loopv(ents)
|
|
{
|
|
entity &e = ents[i];
|
|
if(e.type==LIGHT) calclightsource(e);
|
|
}
|
|
|
|
popMT(); // undo the static seedMT() from above
|
|
|
|
block bb = { clmapdims.x1 - 1, clmapdims.y1 - 1, clmapdims.xspan + 2, clmapdims.yspan + 2 };
|
|
postlightarea(bb);
|
|
setvar("fullbright", 0);
|
|
lastcalclight = totalmillis;
|
|
}
|
|
|
|
struct dlight
|
|
{
|
|
physent *owner;
|
|
vec offset, o;
|
|
block *area;
|
|
int reach, fade, expire;
|
|
uchar r, g, b;
|
|
|
|
float calcintensity() const
|
|
{
|
|
if(!fade || lastmillis < expire - fade) return 1.0f;
|
|
return max(float(expire - lastmillis)/fade, 0.0f);
|
|
}
|
|
};
|
|
|
|
vector<dlight> dlights;
|
|
|
|
VARP(dynlight, 0, 1, 1);
|
|
|
|
static inline bool insidearea(const block &a, const block &b)
|
|
{
|
|
return b.x >= a.x && b.y >= a.y && b.x+b.xs <= a.x+a.xs && b.y+b.ys <= a.y+a.ys;
|
|
}
|
|
|
|
void preparedynlight(dlight &d)
|
|
{
|
|
block area = { (int)d.o.x-d.reach, (int)d.o.y-d.reach, d.reach*2+1, d.reach*2+1 };
|
|
|
|
if(area.x<1) { area.xs = max(area.xs - (1 - area.x), 0); area.x = 1; }
|
|
else if(area.x>ssize-2) { area.x = ssize-2; area.xs = 0; }
|
|
if(area.y<1) { area.ys = max(area.ys - (1 - area.y), 0); area.y = 1; }
|
|
else if(area.y>ssize-2) { area.y = ssize-2; area.ys = 0; }
|
|
if(area.x+area.xs>ssize-2) area.xs = ssize-2-area.x;
|
|
if(area.y+area.ys>ssize-2) area.ys = ssize-2-area.y;
|
|
|
|
if(d.area)
|
|
{
|
|
if(insidearea(*d.area, area)) return;
|
|
|
|
freeblock(d.area);
|
|
}
|
|
d.area = blockcopy(area); // backup area before rendering in dynlight
|
|
}
|
|
|
|
void adddynlight(physent *owner, const vec &o, int reach, int expire, int fade, uchar r, uchar g, uchar b)
|
|
{
|
|
if(!dynlight) return;
|
|
|
|
dlight &d = dlights.add();
|
|
d.owner = owner;
|
|
d.o = o;
|
|
if(d.owner)
|
|
{
|
|
d.offset = d.o;
|
|
d.offset.sub(d.owner->o);
|
|
}
|
|
else d.offset = vec(0, 0, 0);
|
|
d.reach = reach;
|
|
d.fade = fade;
|
|
d.expire = lastmillis + expire;
|
|
d.r = r;
|
|
d.g = g;
|
|
d.b = b;
|
|
|
|
d.area = NULL;
|
|
preparedynlight(d);
|
|
}
|
|
|
|
void cleardynlights()
|
|
{
|
|
loopv(dlights) freeblock(dlights[i].area);
|
|
dlights.shrink(0);
|
|
}
|
|
|
|
void removedynlights(physent *owner)
|
|
{
|
|
loopv(dlights) if(dlights[i].owner==owner)
|
|
{
|
|
freeblock(dlights[i].area);
|
|
dlights.remove(i--);
|
|
}
|
|
}
|
|
|
|
void dodynlights()
|
|
{
|
|
if(dlights.empty()) return;
|
|
const block *area = NULL;
|
|
loopv(dlights)
|
|
{
|
|
dlight &d = dlights[i];
|
|
if(lastmillis >= d.expire)
|
|
{
|
|
freeblock(d.area);
|
|
dlights.remove(i--);
|
|
continue;
|
|
}
|
|
if(d.owner)
|
|
{
|
|
vec oldo(d.o);
|
|
d.o = d.owner->o;
|
|
d.o.add(d.offset);
|
|
if(d.o != oldo) preparedynlight(dlights[i]);
|
|
}
|
|
}
|
|
loopv(dlights)
|
|
{
|
|
dlight &d = dlights[i];
|
|
persistent_entity l((int)d.o.x, (int)d.o.y, (int)d.o.z, LIGHT, d.reach, d.r, d.g, d.b);
|
|
calclightsource(l, d.calcintensity(), false);
|
|
if(area)
|
|
{
|
|
if(insidearea(*area, *d.area)) continue;
|
|
if(!insidearea(*d.area, *area)) postlightarea(*area);
|
|
}
|
|
area = d.area;
|
|
}
|
|
if(area) postlightarea(*area);
|
|
lastcalclight = totalmillis;
|
|
}
|
|
|
|
void undodynlights()
|
|
{
|
|
if(dlights.empty()) return;
|
|
const block *area = NULL;
|
|
loopvrev(dlights)
|
|
{
|
|
const dlight &d = dlights[i];
|
|
if(area)
|
|
{
|
|
if(insidearea(*area, *d.area)) continue;
|
|
if(!insidearea(*d.area, *area)) blockpaste(*area);
|
|
}
|
|
area = d.area;
|
|
}
|
|
if(area) blockpaste(*area);
|
|
}
|
|
|
|
// utility functions also used by editing code
|
|
|
|
block *blockcopy(const block &s)
|
|
{
|
|
block *b = (block *)new uchar[sizeof(block)+s.xs*s.ys*sizeof(sqr)];
|
|
*b = s;
|
|
sqr *q = (sqr *)(b+1), *r = S(s.x,s.y);
|
|
size_t bs = s.xs * sizeof(sqr);
|
|
loopirev(s.ys) { memcpy(q, r, bs); q += s.xs; r += ssize; }
|
|
return b;
|
|
}
|
|
|
|
void blocktexusage(const block &b, uchar *used)
|
|
{
|
|
const sqr *q = (const sqr *)((&b)+1);
|
|
loopirev(b.xs * b.ys)
|
|
{ // collect used texture slots in block
|
|
used[q->wtex] = 1;
|
|
if(q->type != SOLID)
|
|
{
|
|
used[q->ctex] = 1;
|
|
used[q->ftex] = 1;
|
|
used[q->utex] = 1;
|
|
}
|
|
q++;
|
|
}
|
|
}
|
|
|
|
void blockpaste(const block &b, int bx, int by, bool light, uchar *texmap) // slow version, editmode only
|
|
{
|
|
const sqr *q = (const sqr *)((&b)+1);
|
|
sqr *dest = 0;
|
|
uchar tr, tg, tb;
|
|
|
|
for(int y = by; y<b.ys+by; y++)
|
|
for(int x = bx; x<b.xs+bx; x++)
|
|
{
|
|
dest = S(x,y);
|
|
|
|
// retain light info for edit mode paste
|
|
tr = dest->r;
|
|
tg = dest->g;
|
|
tb = dest->b;
|
|
|
|
*dest = *q;
|
|
|
|
if(texmap)
|
|
{ // translate texture slot numbers
|
|
dest->wtex = texmap[q->wtex];
|
|
dest->ctex = texmap[q->ctex];
|
|
dest->ftex = texmap[q->ftex];
|
|
dest->utex = texmap[q->utex];
|
|
}
|
|
|
|
if (light) //edit mode paste
|
|
{
|
|
dest->r = tr;
|
|
dest->g = tg;
|
|
dest->b = tb;
|
|
}
|
|
q++;
|
|
}
|
|
block bb = { bx, by, b.xs, b.ys };
|
|
remipmore(bb);
|
|
}
|
|
|
|
void blockpaste(const block &b) // fast version, used by dynlight
|
|
{
|
|
const sqr *q = (const sqr *)((&b)+1);
|
|
sqr *r = S(b.x, b.y);
|
|
const size_t bs = b.xs * sizeof(sqr);
|
|
loopirev(b.ys) { memcpy(r, q, bs); r += ssize; q += b.xs; }
|
|
remipmore(b);
|
|
}
|
|
|
|
void freeblockp(block *b)
|
|
{
|
|
delete[] (uchar *)b;
|
|
}
|
|
|
|
void freeblock(block *&b)
|
|
{
|
|
if(b) { freeblockp(b); b = NULL; }
|
|
}
|
|
|