diff --git a/source/codeblocks/AssaultCube.cbp b/source/codeblocks/AssaultCube.cbp
index cd907543..56c47b66 100644
--- a/source/codeblocks/AssaultCube.cbp
+++ b/source/codeblocks/AssaultCube.cbp
@@ -166,6 +166,7 @@
+
diff --git a/source/src/Makefile b/source/src/Makefile
index 5a741f7e..7615a326 100644
--- a/source/src/Makefile
+++ b/source/src/Makefile
@@ -29,6 +29,7 @@ CLIENT_OBJS= \
server.o \
serverbrowser.o \
serverms.o \
+ shadow.o \
sound.o \
texture.o \
tools.o \
diff --git a/source/src/cube.h b/source/src/cube.h
index fd288256..b0f21110 100644
--- a/source/src/cube.h
+++ b/source/src/cube.h
@@ -38,6 +38,8 @@ extern int curtime; // current frame time
extern int gamemode, nextmode;
extern int gamespeed;
extern int xtraverts;
+extern float fovy, aspect;
+extern int farplane;
extern bool minimap, reflecting, refracting;
extern int stenciling, stencilshadow;
extern bool intermission;
diff --git a/source/src/protos.h b/source/src/protos.h
index 916910f2..a7e6e74a 100644
--- a/source/src/protos.h
+++ b/source/src/protos.h
@@ -221,7 +221,10 @@ extern void renderaboveheadicon(playerent *p);
extern void drawcrosshair(playerent *p, bool showteamwarning);
extern void drawscope();
extern float dynfov();
-extern bool addshadowbox(const vec &bbmin, const vec &bbmax, const glmatrixf &mat);
+
+// shadow
+extern bool addshadowbox(const vec &bbmin, const vec &bbmax, const vec &extrude, const glmatrixf &mat);
+extern void drawstencilshadows();
// texture
struct Texture
diff --git a/source/src/rendergl.cpp b/source/src/rendergl.cpp
index d3ae1fa6..5bff2424 100644
--- a/source/src/rendergl.cpp
+++ b/source/src/rendergl.cpp
@@ -830,267 +830,6 @@ void readdepth(int w, int h, vec &pos)
pos.z = (float)worldz;
}
-VARP(stencilshadow, 0, 40, 100);
-
-int stenciling = 0;
-
-VAR(shadowcasters, 1, 0, 0);
-
-#define SHADOWTILES 32
-#define SHADOWTILEMASK (0xFFFFFFFFU>>(32-SHADOWTILES))
-uint shadowtiles[SHADOWTILES+1];
-float shadowx1 = 1, shadowy1 = 1, shadowx2 = -1, shadowy2 = -1;
-
-bool addshadowtiles(float x1, float y1, float x2, float y2)
-{
- if(x1 >= 1 || y1 >= 1 || x2 <= -1 || y2 <= -1) return false;
-
- shadowcasters++;
-
- shadowx1 = min(shadowx1, max(x1, -1.0f));
- shadowy1 = min(shadowy1, max(y1, -1.0f));
- shadowx2 = max(shadowx2, min(x2, 1.0f));
- shadowy2 = max(shadowy2, min(y2, 1.0f));
-
- int tx1 = clamp(int((x1 + 1)/2 * SHADOWTILES), 0, SHADOWTILES - 1),
- ty1 = clamp(int((y1 + 1)/2 * SHADOWTILES), 0, SHADOWTILES - 1),
- tx2 = clamp(int((x2 + 1)/2 * SHADOWTILES), 0, SHADOWTILES - 1),
- ty2 = clamp(int((y2 + 1)/2 * SHADOWTILES), 0, SHADOWTILES - 1);
-
- uint mask = (SHADOWTILEMASK>>(SHADOWTILES - (tx2+1))) & (SHADOWTILEMASK<= 0)
- {
- float x = pv.x / pv.w, y = pv.y / pv.w;
- sx1 = min(sx1, x);
- sy1 = min(sy1, y);
- sx2 = max(sx2, x);
- sy2 = max(sy2, y);
- }
- }
- if(sx1 >= sx2 || sy1 >= sy2) return false;
- loopi(8)
- {
- const vec4 &p = v[i];
- if(p.z >= 0) continue;
- loopj(3)
- {
- const vec4 &o = v[i^(1<= shadowx2 || shadowy1 >= shadowy2) return;
-
- float clipx1 = (shadowx1 + 1) / 2,
- clipy1 = (shadowy1 + 1) / 2,
- clipx2 = (shadowx2 + 1) / 2,
- clipy2 = (shadowy2 + 1) / 2;
- if(!shadowclip)
- {
- clipx1 = clipy1 = 0;
- clipx2 = clipy2 = 1;
- }
-
- if(!shadowtile)
- {
- glBegin(GL_QUADS);
- glVertex2f(clipx1, clipy2);
- glVertex2f(clipx2, clipy2);
- glVertex2f(clipx2, clipy1);
- glVertex2f(clipx1, clipy1);
- glEnd();
- return;
- }
-
- glBegin(GL_QUADS);
- float tsz = 1.0f/SHADOWTILES;
- loop(y, SHADOWTILES+1)
- {
- uint mask = shadowtiles[y];
- int x = 0;
- while(mask)
- {
- while(!(mask&0xFF)) { mask >>= 8; x += 8; }
- while(!(mask&1)) { mask >>= 1; x++; }
- int xstart = x;
- do { mask >>= 1; x++; } while(mask&1);
- uint strip = (SHADOWTILEMASK>>(SHADOWTILES - x)) & (SHADOWTILEMASK<>(32-SHADOWTILES))
+uint shadowtiles[SHADOWTILES+1];
+float shadowx1 = 1, shadowy1 = 1, shadowx2 = -1, shadowy2 = -1;
+
+static void extrudeshadowtiles(int x1, int y1, int x2, int y2, int x3, int y3)
+{
+ if(y1 > y2) { swap(x1, x2); swap(y1, y2); }
+ if(y2 > y3) { swap(x2, x3); swap(y2, y3); }
+ if(y1 > y2) { swap(x1, x2); swap(y1, y2); }
+
+ if(y3 < 0 || y1 >= SHADOWTILES) return;
+
+ int lx = x1, rx = x1,
+ fracl = 0, fracr = 0,
+ dlx, dly, drx, dry;
+ if(x2 <= x3) { dlx = x2; dly = y2; drx = x3; dry = y3; }
+ else { dlx = x3; dly = y3; drx = x2; dry = y2; }
+ dlx -= x1; dly -= y1;
+ drx -= x1; dry -= y1;
+ int ldir = 1, rdir = 1;
+ if(dlx < 0)
+ {
+ ldir = -1;
+ dlx = -dlx;
+ }
+ if(drx < 0)
+ {
+ rdir = -1;
+ drx = -drx;
+ }
+
+ int cy = y1;
+ y2 = min(y2, SHADOWTILES);
+ if(cy < 0 && y2 > cy)
+ {
+ int dy = min(y2, 0) - cy;
+ cy += dy;
+ fracl += dy*dlx;
+ lx += ldir*(fracl/dly);
+ fracl %= dly;
+ fracr += dy*drx;
+ rx += rdir*(fracr/dry);
+ fracr %= dry;
+ }
+
+ for(; cy < y2; cy++)
+ {
+ if(lx < SHADOWTILES && rx >= 0) shadowtiles[cy] |= (SHADOWTILEMASK>>(SHADOWTILES - (min(rx, SHADOWTILES-1)+1))) & (SHADOWTILEMASK<= dly) { lx += ldir; fracl -= dly; }
+ fracr += drx;
+ while(fracr >= dry) { rx += rdir; fracr -= dry; }
+ }
+
+ if(cy >= SHADOWTILES) return;
+
+ if(x2 < x3 || y1 == cy)
+ {
+ if(x2 < lx) lx = x2;
+ dlx = x3 - lx; dly = y3 - cy;
+ ldir = 1;
+ if(dlx < 0)
+ {
+ ldir = -1;
+ dlx = -dlx;
+ }
+ fracl = 0;
+ }
+ if(x2 > x3 || y1 == cy)
+ {
+ if(x2 > rx) rx = x2;
+ drx = x3 - rx; dry = y3 - cy;
+ rdir = 1;
+ if(drx < 0)
+ {
+ rdir = -1;
+ drx = -drx;
+ }
+ fracr = 0;
+ }
+
+ y3 = min(y3, SHADOWTILES);
+ if(cy < 0 && y3 > cy)
+ {
+ int dy = min(y3, 0) - cy;
+ cy += dy;
+ fracl += dy*dlx;
+ lx += ldir*(fracl/dly);
+ fracl %= dly;
+ fracr += dy*drx;
+ rx += rdir*(fracr/dry);
+ fracr %= dry;
+ }
+
+ for(; cy < y3; cy++)
+ {
+ if(lx < SHADOWTILES && rx >= 0) shadowtiles[cy] |= (SHADOWTILEMASK>>(SHADOWTILES - (min(rx, SHADOWTILES-1)+1))) & (SHADOWTILEMASK<= dly) { lx += ldir; fracl -= dly; }
+ fracr += drx;
+ while(fracr >= dry) { rx += rdir; fracr -= dry; }
+ }
+
+ if(cy < SHADOWTILES && lx < SHADOWTILES && rx >= 0) shadowtiles[cy] |= (SHADOWTILEMASK>>(SHADOWTILES - (min(rx, SHADOWTILES-1)+1))) & (SHADOWTILEMASK<>(SHADOWTILES - (tx2+1))) & (SHADOWTILEMASK<= 0)
+ {
+ float x = p.x / p.w, y = p.y / p.w;
+ sx1 = min(sx1, x);
+ sy1 = min(sy1, y);
+ sx2 = max(sx2, x);
+ sy2 = max(sy2, y);
+ front++;
+ }
+ }
+ vec4 ev;
+ mat.transform(extrude, ev);
+ if(ev.z < 0 && !front) return false;
+ if(front < 8 || ev.z < 0) loopi(8)
+ {
+ const vec4 &p = v[i];
+ if(p.z >= 0)
+ {
+ if(ev.z >= 0) continue;
+ float t = ev.z/(ev.z - p.z),
+ w = ev.w + t*(p.w - ev.w),
+ x = (ev.x + t*(p.x - ev.x))/w,
+ y = (ev.y + t*(p.y - ev.y))/w;
+ sx1 = min(sx1, x);
+ sy1 = min(sy1, y);
+ sx2 = max(sx2, x);
+ sy2 = max(sy2, y);
+ continue;
+ }
+ loopj(3)
+ {
+ const vec4 &o = v[i^(1<= 0)
+ {
+ float x = ev.x / ev.w, y = ev.y / ev.w,
+ tx1, ty1, tx2, ty2;
+ if((sx1 >= 1 && x >= 1) || (sy1 >= 1 && y >= 1) || (sx2 <= -1 && x <= -1) || (sy2 <= -1 && y <= -1)) return false;
+ if(x < sx1)
+ {
+ if(y < sy1) { tx1 = sx1; ty1 = sy2; tx2 = sx2; ty2 = sy1; }
+ else if(y > sy2) { tx1 = sx1; ty1 = sy1; tx2 = sx2; ty2 = sy2; }
+ else { tx1 = sx1; ty1 = sy1; tx2 = sx1; ty2 = sy2; }
+ }
+ else if(x <= sx2)
+ {
+ if(y < sy1) { tx1 = sx1; ty1 = sy1; tx2 = sx2; ty2 = sy1; }
+ else if(y > sy2) { tx1 = sx1; ty1 = sy2; tx2 = sx2; ty2 = sy2; }
+ else goto noextrusion;
+ }
+ else
+ {
+ if(y < sy1) { tx1 = sx1; ty1 = sy1; tx2 = sx2; ty2 = sy2; }
+ else if(y > sy2) { tx1 = sx2; ty1 = sy1; tx2 = sx1; ty2 = sy2; }
+ else { tx1 = sx2; ty1 = sy1; tx2 = sx2; ty2 = sy2; }
+ }
+ extrudeshadowtiles(int(floor(SHADOWTILES * (x + 1) / 2)), int(floor(SHADOWTILES * (y + 1) / 2)),
+ int(floor(SHADOWTILES * (tx1 + 1) / 2)), int(floor(SHADOWTILES * (ty1 + 1) / 2)),
+ int(floor(SHADOWTILES * (tx2 + 1) / 2)), int(floor(SHADOWTILES * (ty2 + 1) / 2)));
+ shadowx1 = min(x, shadowx1);
+ shadowy1 = min(y, shadowy1);
+ shadowx2 = max(x, shadowx2);
+ shadowy2 = max(y, shadowy2);
+ noextrusion:;
+ }
+ else if(sx1 >= 1 || sy1 >= 1 || sx2 <= -1 || sy2 <= -1) return false;
+ shadowcasters++;
+ addshadowtiles(sx1, sy1, sx2, sy2);
+ return true;
+}
+
+void rendershadowtiles()
+{
+ shadowx1 = clamp(shadowx1, -1.0f, 1.0f);
+ shadowy1 = clamp(shadowy1, -1.0f, 1.0f);
+ shadowx2 = clamp(shadowx2, -1.0f, 1.0f);
+ shadowy2 = clamp(shadowy2, -1.0f, 1.0f);
+
+ if(shadowx1 >= shadowx2 || shadowy1 >= shadowy2) return;
+
+ float clipx1 = (shadowx1 + 1) / 2,
+ clipy1 = (shadowy1 + 1) / 2,
+ clipx2 = (shadowx2 + 1) / 2,
+ clipy2 = (shadowy2 + 1) / 2;
+ if(!shadowclip)
+ {
+ clipx1 = clipy1 = 0;
+ clipx2 = clipy2 = 1;
+ }
+
+ if(!shadowtile)
+ {
+ glBegin(GL_QUADS);
+ glVertex2f(clipx1, clipy2);
+ glVertex2f(clipx2, clipy2);
+ glVertex2f(clipx2, clipy1);
+ glVertex2f(clipx1, clipy1);
+ xtraverts += 4;
+ glEnd();
+ return;
+ }
+
+ glBegin(GL_QUADS);
+ float tsz = 1.0f/SHADOWTILES;
+ loop(y, SHADOWTILES+1)
+ {
+ uint mask = shadowtiles[y];
+ int x = 0;
+ while(mask)
+ {
+ while(!(mask&0xFF)) { mask >>= 8; x += 8; }
+ while(!(mask&1)) { mask >>= 1; x++; }
+ int xstart = x;
+ do { mask >>= 1; x++; } while(mask&1);
+ uint strip = (SHADOWTILEMASK>>(SHADOWTILES - x)) & (SHADOWTILEMASK<