// shadow.cpp: stencil shadow rendering #include "cube.h" VARP(stencilshadow, 0, 40, 100); int stenciling = 0; VAR(shadowclip, 0, 1, 1); VAR(shadowtile, 0, 1, 1); VAR(dbgtiles, 0, 0, 1); VAR(shadowcasters, 1, 0, 0); #define SHADOWROWS 64 #define SHADOWCOLUMNS 32 #define SHADOWCOLUMNMASK (0xFFFFFFFFU>>(32-SHADOWCOLUMNS)) uint shadowtiles[SHADOWROWS+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 >= SHADOWROWS) 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, SHADOWROWS); 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++) { int cx1 = lx, cx2 = rx; fracl += dlx; while(fracl >= dly) { lx += ldir; if(ldir < 0) cx1 = lx; fracl -= dly; } fracr += drx; while(fracr >= dry) { rx += rdir; if(rdir > 0) cx2 = rx; fracr -= dry; } if(cx1 < SHADOWCOLUMNS && cx2 >= 0) shadowtiles[cy] |= (SHADOWCOLUMNMASK>>(SHADOWCOLUMNS - (min(cx2, SHADOWCOLUMNS-1)+1))) & (SHADOWCOLUMNMASK<= SHADOWROWS) 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, SHADOWROWS); 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++) { int cx1 = lx, cx2 = rx; fracl += dlx; while(fracl >= dly) { lx += ldir; if(ldir < 0) cx1 = lx; fracl -= dly; } fracr += drx; while(fracr >= dry) { rx += rdir; if(rdir > 0) cx2 = rx; fracr -= dry; } if(cx1 < SHADOWCOLUMNS && cx2 >= 0) shadowtiles[cy] |= (SHADOWCOLUMNMASK>>(SHADOWCOLUMNS - (min(cx2, SHADOWCOLUMNS-1)+1))) & (SHADOWCOLUMNMASK<= dly) { lx += ldir; if(ldir < 0) cx1 = lx; fracl -= dly; } } if(dry) { fracr += drx; while(fracr >= dry) { rx += rdir; if(rdir > 0) cx2 = rx; fracr -= dry; } } if(cx1 < SHADOWCOLUMNS && cx2 >= 0) shadowtiles[cy] |= (SHADOWCOLUMNMASK>>(SHADOWCOLUMNS - (min(cx2, SHADOWCOLUMNS-1)+1))) & (SHADOWCOLUMNMASK<>(SHADOWCOLUMNS - (tx2+1))) & (SHADOWCOLUMNMASK<= 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; if((sx1 >= 1 && x >= 1) || (sy1 >= 1 && y >= 1) || (sx2 <= -1 && x <= -1) || (sy2 <= -1 && y <= -1)) return false; int tx = int(floor(SHADOWCOLUMNS * (y + 1) / 2)), ty = int(floor(SHADOWROWS * (x + 1) / 2)), tx1 = int(floor(SHADOWCOLUMNS * (sy1 + 1) / 2)), ty1 = int(floor(SHADOWROWS * (sx1 + 1) / 2)), tx2 = int(floor(SHADOWCOLUMNS * (sy2 + 1) / 2)), ty2 = int(floor(SHADOWROWS * (sx2 + 1) / 2)); if(tx < tx1) { if(ty < ty1) { swap(ty1, ty2); tx1--; ty1--; } else if(ty > ty2) { tx1--; ty1++; } else { tx2 = tx1; tx1--; tx2--; } } else if(tx > tx2) { if(ty < ty1) { ty1--; tx2++; } else if(ty > ty2) { swap(ty1, ty2); ty1++; tx2++; } else { tx1 = tx2; tx1++; tx2++; } } else { if(ty < ty1) { ty2 = ty1; ty1--; ty2--; } else if(ty > ty2) { ty1 = ty2; ty1++; ty2++; } else goto noextrusion; } extrudeshadowtiles(tx, ty, tx1, ty1, tx2, ty2); 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; } static 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 = (shadowy1 + 1) / 2, clipy1 = (shadowx1 + 1) / 2, clipx2 = (shadowy2 + 1) / 2, clipy2 = (shadowx2 + 1) / 2; if(!shadowclip) { clipx1 = clipy1 = 0; clipx2 = clipy2 = 1; } if(!shadowtile) { glBegin(GL_TRIANGLE_STRIP); glVertex2f(clipy1, clipx1); glVertex2f(clipy1, clipx2); glVertex2f(clipy2, clipx1); glVertex2f(clipy2, clipx2); xtraverts += 4; glEnd(); return; } glBegin(GL_QUADS); float tw = 1.0f/SHADOWCOLUMNS, th = 1.0f/SHADOWROWS; loop(y, SHADOWROWS+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 = (SHADOWCOLUMNMASK>>(SHADOWCOLUMNS - x)) & (SHADOWCOLUMNMASK<