summaryrefslogtreecommitdiff
path: root/src/engine/dynlight.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/engine/dynlight.cpp')
-rw-r--r--src/engine/dynlight.cpp227
1 files changed, 227 insertions, 0 deletions
diff --git a/src/engine/dynlight.cpp b/src/engine/dynlight.cpp
new file mode 100644
index 0000000..835c631
--- /dev/null
+++ b/src/engine/dynlight.cpp
@@ -0,0 +1,227 @@
+#include "engine.h"
+
+VARP(maxdynlights, 0, min(3, MAXDYNLIGHTS), MAXDYNLIGHTS);
+VARP(dynlightdist, 0, 1024, 10000);
+
+struct dynlight
+{
+ vec o, hud;
+ float radius, initradius, curradius, dist;
+ vec color, initcolor, curcolor;
+ int fade, peak, expire, flags;
+ physent *owner;
+
+ void calcradius()
+ {
+ if(fade + peak > 0)
+ {
+ int remaining = expire - lastmillis;
+ if(flags&DL_EXPAND)
+ curradius = initradius + (radius - initradius) * (1.0f - remaining/float(fade + peak));
+ else if(!(flags&DL_FLASH) && remaining > fade)
+ curradius = initradius + (radius - initradius) * (1.0f - float(remaining - fade)/peak);
+ else if(flags&DL_SHRINK)
+ curradius = (radius*remaining)/fade;
+ else curradius = radius;
+ }
+ else curradius = radius;
+ }
+
+ void calccolor()
+ {
+ if(flags&DL_FLASH || peak <= 0) curcolor = color;
+ else
+ {
+ int peaking = expire - lastmillis - fade;
+ if(peaking <= 0) curcolor = color;
+ else curcolor.lerp(initcolor, color, 1.0f - float(peaking)/peak);
+ }
+
+ float intensity = 1.0f;
+ if(fade > 0)
+ {
+ int fading = expire - lastmillis;
+ if(fading < fade) intensity = float(fading)/fade;
+ }
+ curcolor.mul(intensity);
+ // KLUGE: this prevents nvidia drivers from trying to recompile dynlight fragment programs
+ loopk(3) if(fmod(curcolor[k], 1.0f/256) < 0.001f) curcolor[k] += 0.001f;
+ }
+};
+
+vector<dynlight> dynlights;
+vector<dynlight *> closedynlights;
+
+void adddynlight(const vec &o, float radius, const vec &color, int fade, int peak, int flags, float initradius, const vec &initcolor, physent *owner)
+{
+ if(!maxdynlights) return;
+ if(o.dist(camera1->o) > dynlightdist || radius <= 0) return;
+
+ int insert = 0, expire = fade + peak + lastmillis;
+ loopvrev(dynlights) if(expire>=dynlights[i].expire) { insert = i+1; break; }
+ dynlight d;
+ d.o = d.hud = o;
+ d.radius = radius;
+ d.initradius = initradius;
+ d.color = color;
+ d.initcolor = initcolor;
+ d.fade = fade;
+ d.peak = peak;
+ d.expire = expire;
+ d.flags = flags;
+ d.owner = owner;
+ dynlights.insert(insert, d);
+}
+
+void cleardynlights()
+{
+ int faded = -1;
+ loopv(dynlights) if(lastmillis<dynlights[i].expire) { faded = i; break; }
+ if(faded<0) dynlights.setsize(0);
+ else if(faded>0) dynlights.remove(0, faded);
+}
+
+void removetrackeddynlights(physent *owner)
+{
+ loopvrev(dynlights) if(owner ? dynlights[i].owner == owner : dynlights[i].owner != NULL) dynlights.remove(i);
+}
+
+void updatedynlights()
+{
+ cleardynlights();
+ game::adddynlights();
+
+ loopv(dynlights)
+ {
+ dynlight &d = dynlights[i];
+ if(d.owner) game::dynlighttrack(d.owner, d.o, d.hud);
+ d.calcradius();
+ d.calccolor();
+ }
+}
+
+int finddynlights()
+{
+ closedynlights.setsize(0);
+ if(!maxdynlights) return 0;
+ physent e;
+ e.type = ENT_CAMERA;
+ loopvj(dynlights)
+ {
+ dynlight &d = dynlights[j];
+ if(d.curradius <= 0) continue;
+ d.dist = camera1->o.dist(d.o) - d.curradius;
+ if(d.dist > dynlightdist || isfoggedsphere(d.curradius, d.o) || pvsoccludedsphere(d.o, d.curradius))
+ continue;
+ if(reflecting || refracting > 0)
+ {
+ if(d.o.z + d.curradius < reflectz) continue;
+ }
+ else if(refracting < 0 && d.o.z - d.curradius > reflectz) continue;
+ e.o = d.o;
+ e.radius = e.xradius = e.yradius = e.eyeheight = e.aboveeye = d.curradius;
+ if(!collide(&e, vec(0, 0, 0), 0, false)) continue;
+
+ int insert = 0;
+ loopvrev(closedynlights) if(d.dist >= closedynlights[i]->dist) { insert = i+1; break; }
+ closedynlights.insert(insert, &d);
+ if(closedynlights.length() >= DYNLIGHTMASK) break;
+ }
+ return closedynlights.length();
+}
+
+bool getdynlight(int n, vec &o, float &radius, vec &color)
+{
+ if(!closedynlights.inrange(n)) return false;
+ dynlight &d = *closedynlights[n];
+ o = d.o;
+ radius = d.curradius;
+ color = d.curcolor;
+ return true;
+}
+
+void dynlightreaching(const vec &target, vec &color, vec &dir, bool hud)
+{
+ vec dyncolor(0, 0, 0);//, dyndir(0, 0, 0);
+ loopv(dynlights)
+ {
+ dynlight &d = dynlights[i];
+ if(d.curradius<=0) continue;
+
+ vec ray(hud ? d.hud : d.o);
+ ray.sub(target);
+ float mag = ray.squaredlen();
+ if(mag >= d.curradius*d.curradius) continue;
+
+ vec color = d.curcolor;
+ color.mul(1 - sqrtf(mag)/d.curradius);
+ dyncolor.add(color);
+ //dyndir.add(ray.mul(intensity/mag));
+ }
+#if 0
+ if(!dyndir.iszero())
+ {
+ dyndir.normalize();
+ float x = dyncolor.magnitude(), y = color.magnitude();
+ if(x+y>0)
+ {
+ dir.mul(x);
+ dyndir.mul(y);
+ dir.add(dyndir).div(x+y);
+ if(dir.iszero()) dir = vec(0, 0, 1);
+ else dir.normalize();
+ }
+ }
+#endif
+ color.add(dyncolor);
+}
+
+void calcdynlightmask(vtxarray *va)
+{
+ uint mask = 0;
+ int offset = 0;
+ loopv(closedynlights)
+ {
+ dynlight &d = *closedynlights[i];
+ if(d.o.dist_to_bb(va->geommin, va->geommax) >= d.curradius) continue;
+
+ mask |= (i+1)<<offset;
+ offset += DYNLIGHTBITS;
+ if(offset >= maxdynlights*DYNLIGHTBITS) break;
+ }
+ va->dynlightmask = mask;
+}
+
+int setdynlights(vtxarray *va)
+{
+ if(closedynlights.empty() || !va->dynlightmask) return 0;
+
+ extern bool minimizedynlighttcusage();
+
+ static vec4 posv[MAXDYNLIGHTS];
+ static vec colorv[MAXDYNLIGHTS];
+
+ int index = 0;
+ for(uint mask = va->dynlightmask; mask; mask >>= DYNLIGHTBITS, index++)
+ {
+ dynlight &d = *closedynlights[(mask&DYNLIGHTMASK)-1];
+
+ float scale = 1.0f/d.curradius;
+ vec origin = vec(d.o).mul(-scale);
+
+ if(index>0 && minimizedynlighttcusage())
+ {
+ scale /= posv[0].w;
+ origin.sub(vec(posv[0]).mul(scale));
+ }
+
+ posv[index] = vec4(origin, scale);
+ colorv[index] = d.curcolor;
+ }
+
+ GLOBALPARAMV(dynlightpos, posv, index);
+ GLOBALPARAMV(dynlightcolor, colorv, index);
+
+ return index;
+}
+