ref: 35e90d35971631b97c76cb3a28ae222f8e6ba79b
dir: /rend.c/
#include <u.h> #include <libc.h> #include "dat.h" #include "fns.h" s32int sint[360+90], *cost; int vwsize; typedef struct Vis Vis; typedef struct Scaler Scaler; enum{ Nvis = 50<<2, /* raised from original 50 */ Nscal = 256+1, Wdxy = 64, Fineθ = 3600, Dfoclen = 0x5700, Dglob = 0x10000, Dtile = 0x2000, Dobj = 0x4000 }; struct Vis{ s16int vwx; s16int vwdy; Spr *spr; }; #define Pi 3.141592657 static float Rad = (float)Fineθ / 2 / Pi; static s32int ftan[Fineθ/4]; static s16int midθ; static int Δvwθ[Vw]; static int prjw, prjh; static s32int xin, yin; static int dtx, dty; static u16int ∂xdown, ∂xup, ∂ydown, ∂yup; struct Scaler{ int skip; int ps; int pe; int dx; }; static Scaler scals[Nscal][Wdxy+1], *sce; static uchar *scps, *scts; static int scx, scdx, scdy; static int waldy[Vw]; static s32int lastin; static int lasttile, lastside; static void scalscol(Scaler *ss, Sprc *c, int dx) { int ps, pe, w; uchar n, *d, *sp, *ds; Scaler *s, *se; ds = pxb + vw.ofs + scx; se = ss + c->e; while(se != ss){ sp = c->p; s = ss + c->s; for(; s<se; s++, sp++){ if(s->skip) continue; n = *sp; for(ps=s->ps, pe=s->pe; ps<pe; ps++){ if(ps >= vw.dy) break; if(ps < 0) continue; d = ds + ps * Vw; w = dx; while(w-- > 0) *d++ = n; } } c++; se = ss + c->e; } } static void scalvis(Vis *v) { int x, dx, lx, rx, *lw, *rw; Scaler *ss, *s, *se; Spr *spr; Sprc **c; ss = scals[v->vwdy >> 3]; if(ss == scals[0] || ss > sce) return; spr = v->spr; lx = spr->lx; rx = spr->rx; scx = v->vwx; if(lx < 32){ s = ss + lx; se = ss + 32; while(s < se) scx -= s++->dx; }else{ s = ss + 32; se = ss + lx; while(s < se) scx += s++->dx; } c = spr->cs; for(s=ss+lx, se=ss+rx+1; s<se && scx<vw.dx; c++, scx+=dx){ dx = s++->dx; if(dx == 0) continue; if(dx == 1){ if(scx >= 0 && waldy[scx] < v->vwdy) scalscol(ss, *c, 1); continue; } x = scx + dx; if(scx < 0){ if(x <= 0) continue; dx = x; scx = 0; }else if(x > vw.dx) dx = vw.dx - scx; lw = waldy + scx; rw = lw + dx - 1; if(*lw < v->vwdy){ if(*rw < v->vwdy){ scalscol(ss, *c, dx); continue; } while(*rw >= v->vwdy) rw--, dx--; scalscol(ss, *c, dx); break; }else{ if(*rw >= v->vwdy) continue; while(*lw >= v->vwdy){ lw++, scx++; dx--; } scalscol(ss, *c, dx); } } } static void scalcol(void) { int i, n, x, dx; uchar c, *ds, *d; Scaler *s; s = scals[(waldy[scx] & 0xfff8) >> 3]; if(s > sce) s = sce; ds = pxb + vw.ofs + scx; for(x=0; x<Wdxy; x++, s++){ if(s->skip) continue; c = scps[x]; for(i=s->ps, n=s->pe; i<n; i++){ if(i >= vw.dy) break; if(i < 0) continue; d = ds + i * Vw; dx = scdx; while(dx-- > 0) *d++ = c; } } } static void topspr(void) { if(ver < SDM && gm.won){ if(oplr->s == stt+GSplrcam && qtc & 32) scalspr(SPcam, vw.dx/2, vw.dy+1); return; } if(gm.w != WPnone) scalspr(wspr[gm.w] + gm.wfrm, vw.dx/2, vw.dy+1); if(gm.record || gm.demo) scalspr(SPdemo, vw.dx/2, vw.dy+1); } static int rot(Obj *o) { int θ; θ = oplr->θ + (vw.mid - o->vwdx) / 8 + 180 + 360 - o->θ + 45/2; θ %= 360; if(o->s->rot == 2) return 4 * (θ / 180); return θ / 45; } static int projtl(Tile *tl, Vis *v) { int x, y, cx, cy; x = ((tl-tiles) % Mapdxy << Dtlshift) + 0x8000 - vw.x; y = ((tl-tiles) / Mapdxy << Dtlshift) + 0x8000 - vw.y; cx = ffs(x, vw.cos); cx -= ffs(y, vw.sin) + Dtile; cy = ffs(y, vw.cos); cy += ffs(x, vw.sin); if(cx < Dmin){ v->vwdy = 0; return 0; } v->vwx = vw.mid + cy * prjw / cx; /* low 3 bits are fractional */ v->vwdy = prjh / (cx >> 8); return cx < Dtlglobal && abs(cy) < Dtlglobal / 2; } static void projob(Obj *o) { int x, y, cx, cy; x = o->x - vw.x; y = o->y - vw.y; cx = ffs(x, vw.cos); cx -= ffs(y, vw.sin) + Dobj; cy = ffs(x, vw.sin); cy += ffs(y, vw.cos); o->vwx = cx; if(cx < Dmin){ o->vwdy = 0; return; } o->vwdx = vw.mid + cy * prjw / cx; o->vwdy = prjh / (cx >> 8); } static void scalobj(void) { int i, n, min; Obj *o; Tile *tl; Static *st; Vis viss[Nvis], *v, *e, *m; memset(viss, 0, sizeof viss); e = viss; for(st=stcs; st<stce; st++){ if(st->tl == nil || st->tl->vis == 0) continue; if(projtl(st->tl, e) && st->f & OFbonus){ bonus(st); continue; } if(e->vwdy == 0) continue; e->spr = st->spr; if(e < viss + nelem(viss)-1) e++; } for(o=oplr->n; o!=objs; o=o->n){ if(o->s->spr == nil) continue; tl = tiles + o->ty * Mapdxy + o->tx; if(tl[0].vis || tl[-1].vis && tl[-1].tl == 0 || tl[+1].vis && tl[+1].tl == 0 || tl[-Mapdxy-1].vis && tl[-Mapdxy-1].tl == 0 || tl[-Mapdxy].vis && tl[-Mapdxy].tl == 0 || tl[-Mapdxy+1].vis && tl[-Mapdxy+1].tl == 0 || tl[Mapdxy+1].vis && tl[Mapdxy+1].tl == 0 || tl[Mapdxy].vis && tl[Mapdxy].tl == 0 || tl[Mapdxy-1].vis && tl[Mapdxy-1].tl == 0){ o->on = 1; projob(o); if(o->vwdy == 0) continue; o->f |= OFvis; e->vwx = o->vwdx; e->vwdy = o->vwdy; e->spr = o->s->spr; if(o->s->rot) e->spr += rot(o); if(e < viss + nelem(viss)-1) e++; }else o->f &= ~OFvis; } for(i=0, n=e-viss; i<n; i++){ min = 32000; v = m = viss; while(v < e){ if(v->vwdy < min){ min = v->vwdy; m = v; } v++; } scalvis(m); m->vwdy = 32000; } } static s16int walldy(void) { s32int cx, dy; cx = ffs(xin - vw.x, vw.cos); dy = cx - ffs(yin - vw.y, vw.sin); if(dy < Dmin) dy = Dmin; return prjh / (dy >> 8); } static void vwall(int i, int tx, int ty, int tile) { int n; u16int tex; tex = yin >> 4 & 0xfc0; if(dtx == -1){ tex = 0xfc0 - tex; xin += Dtlglobal; } waldy[i] = walldy(); if(lastside == 1 && lastin == tx && lasttile == tile){ if(scps == scts + tex){ scdx++; waldy[i] = waldy[i-1]; }else{ scalcol(); scps = scts + tex; scdx = 1; scx = i; } return; } if(lastside != -1) scalcol(); lastside = 1; lastin = tx; lasttile = tile; scx = i; scdx = 1; if(tile & 0x40){ if(tiles[ty*Mapdxy + tx-dtx].tl & 0x80) n = drofs + 3; else n = ((tile & ~0x40) - 1) * 2 + 1; }else n = (tile - 1) * 2 + 1; scts = wals[n]; if(scts == nil) sysfatal("sparse wall %d\n", n); scps = scts + tex; } static void hwall(int i, int tx, int ty, int tile) { int n; u16int tex; tex = xin >> 4 & 0xfc0; if(dty == -1) yin += Dtlglobal; else tex = 0xfc0 - tex; waldy[i] = walldy(); if(lastside == 0 && lastin == ty && lasttile == tile){ if(scps == scts + tex){ scdx++; waldy[i] = waldy[i-1]; }else{ scalcol(); scps = scts + tex; scdx = 1; scx = i; } return; } if(lastside != -1) scalcol(); lastside = 0; lastin = ty; lasttile = tile; scx = i; scdx = 1; if(tile & 0x40){ tx = xin >> Dtlshift; if(tiles[(ty-dty)*Mapdxy + tx].tl & 0x80) n = drofs + 2; else n = ((tile & ~0x40) - 1) * 2; }else n = (tile - 1) * 2; scts = wals[n]; if(scts == nil) sysfatal("sparse wall %d\n", n); scps = scts + tex; } static void vdoor(int i, int tile) { int n; u16int tex; Door *d; waldy[i] = walldy(); d = doors + (tile & 0x7f); tex = yin - d->dopen >> 4 & 0xfc0; if(lasttile == tile){ if(scps == scts + tex){ scdx++; waldy[i] = waldy[i-1]; }else{ scalcol(); scps = scts + tex; scdx = 1; scx = i; } return; } if(lastside != -1) scalcol(); lastside = 2; lasttile = tile; scx = i; scdx = 1; n = 1; switch(d->lock){ case DRunlk: n += drofs; break; case DRlock1: case DRlock2: case DRlock3: case DRlock4: n += drofs + 6; break; case DRup: n += drofs + 4; break; } scts = wals[n]; if(scts == nil) sysfatal("sparse wall %d\n", n); scps = scts + tex; } static void hdoor(int i, int tile) { int n; u16int tex; Door *d; waldy[i] = walldy(); d = doors + (tile & 0x7f); tex = xin - d->dopen >> 4 & 0xfc0; if(lasttile == tile){ if(scps == scts + tex){ scdx++; waldy[i] = waldy[i-1]; }else{ scalcol(); scps = scts + tex; scdx = 1; scx = i; } return; } if(lastside != -1) scalcol(); lastside = 2; lasttile = tile; scx = i; scdx = 1; n = 0; switch(d->lock){ case DRunlk: n = drofs; break; case DRlock1: case DRlock2: case DRlock3: case DRlock4: n = drofs + 6; break; case DRup: n = drofs + 4; break; } scts = wals[n]; if(scts == nil) sysfatal("sparse wall %d\n", n); scps = scts + tex; } static void vpush(int i, int tile) { int n; u16int tex, ofs; tex = yin >> 4 & 0xfc0; ofs = pusher.dopen << 10; if(dtx == -1){ xin += Dtlglobal - ofs; tex = 0xfc0 - tex; }else xin += ofs; waldy[i] = walldy(); if(lasttile == tile){ if(scps == scts + tex){ scdx++; waldy[i] = waldy[i-1]; }else{ scalcol(); scps = scts + tex; scdx = 1; scx = i; } return; } if(lastside != -1) scalcol(); lasttile = tile; scx = i; scdx = 1; n = ((tile & 63) - 1) * 2 + 1; scts = wals[n]; if(scts == nil) sysfatal("sparse wall %d\n", n); scps = scts + tex; } static void hpush(int i, int tile) { int n; u16int tex, ofs; tex = xin >> 4 & 0xfc0; ofs = pusher.dopen << 10; if(dty == -1) yin += Dtlglobal - ofs; else{ tex = 0xfc0 - tex; yin += ofs; } waldy[i] = walldy(); if(lasttile == tile){ if(scps == scts + tex){ scdx++; waldy[i] = waldy[i-1]; }else{ scalcol(); scps = scts + tex; scdx = 1; scx = i; } return; } if(lastside != -1) scalcol(); lasttile = tile; scx = i; scdx = 1; n = ((tile & 63) - 1) * 2; scts = wals[n]; if(scts == nil) sysfatal("sparse wall %d\n", n); scps = scts + tex; } static void raytrace(void) { int i, θ, tx, ty; u8int n; s16int in, xinh, yinh; u16int dr, x, y, ∂x, ∂y, tilehit; s32int rs, dx, dy; vw.θ = oplr->θ; midθ = vw.θ * (Fineθ / 360); vw.sin = sint[vw.θ]; vw.cos = cost[vw.θ]; vw.x = oplr->x - ffs(Dfoclen, vw.cos); vw.y = oplr->y + ffs(Dfoclen, vw.sin); vw.tx = vw.x >> Dtlshift; vw.ty = vw.y >> Dtlshift; ∂xdown = vw.x & Dtlglobal - 1; ∂xup = Dtlglobal - ∂xdown; ∂ydown = vw.y & Dtlglobal - 1; ∂yup = Dtlglobal - ∂ydown; lastside = -1; i = 0; tilehit = 0; loop: θ = (midθ + Δvwθ[i]) % Fineθ; if(θ < 0) θ += Fineθ; if(θ < 900){ dtx = 1; dty = -1; dx = ftan[900-1-θ]; dy = -ftan[θ]; ∂x = ∂xup; ∂y = ∂ydown; }else if(θ < 1800){ dtx = -1; dty = -1; dx = -ftan[θ-900]; dy = -ftan[1800-1-θ]; ∂x = ∂xdown; ∂y = ∂ydown; }else if(θ < 2700){ dtx = -1; dty = 1; dx = -ftan[2700-1-θ]; dy = ftan[θ-1800]; ∂x = ∂xdown; ∂y = ∂yup; }else{ dtx = 1; dty = 1; dx = ftan[θ-2700]; dy = ftan[3600-1-θ]; ∂x = ∂xup; ∂y = ∂yup; } yin = ffs(dy, ∂x) + vw.y; yinh = yin >> 16; tx = vw.tx + dtx; x = yinh * Mapdxy + tx; xin = ffs(dx, ∂y) + vw.x; xinh = xin >> 16; ty = vw.ty + dty; y = ty * Mapdxy + xinh; vcheck: if(dty * (yinh - ty) >= 0) goto hentry; ventry: n = tiles[x].tl; if(n == 0){ vpass: tiles[x].vis = 1; tx += dtx; rs = (yinh << 16 | yin & 0xffff) + dy; yinh = rs >> 16; yin = yin & 0xffff0000 | rs & 0xffff; x = yinh * Mapdxy + tx; goto vcheck; } tilehit = tilehit & 0xff00 | n; if(~n & 1<<7){ xin = tx << 16; ty = yinh; yin = yinh << 16 | yin & 0xffff; vwall(i, tx, ty, tilehit); goto next; } yin = yinh << 16 | yin & 0xffff; if(n & 1<<6){ rs = ((s32int)pusher.dopen * dy >> 6) + yin; /* sar */ in = rs >> Dtlshift; if(in != yinh) goto vpass; yin = rs; xin = tx << 16; vpush(i, tilehit); goto next; } rs = yin + (dy >> 1); /* sar */ in = rs >> Dtlshift; dr = rs & 0xffff; if(yinh != in || dr < doors[n&0x7f].dopen) goto vpass; yin = yinh << 16 | dr; xin = tx << 16 | 0x8000; /* intercept in middle of tile */ vdoor(i, tilehit); goto next; hcheck: if(dtx * (xinh - tx) >= 0) goto ventry; hentry: n = tiles[y].tl; if(n == 0){ hpass: tiles[y].vis = 1; ty += dty; rs = (xinh << 16 | xin & 0xffff) + dx; xinh = rs >> 16; xin = xin & 0xffff0000 | rs & 0xffff; y = ty * Mapdxy + xinh; goto hcheck; } tilehit = tilehit & 0xff00 | n; if(~n & 1<<7){ xin = xinh << 16 | xin & 0xffff; tx = xinh; yin = ty << 16; hwall(i, tx, ty, tilehit); goto next; } yin = yinh << 16 | yin & 0xffff; if(n & 1<<6){ rs = ((s32int)pusher.dopen * dx >> 6) + (xinh << 16 | xin & 0xffff); /* sar */ in = rs >> Dtlshift; if(in != xinh) goto hpass; xin = rs; yin = ty << 16; hpush(i, tilehit); goto next; } rs = (xinh << 16 | xin & 0xffff) + (dx >> 1); /* sar */ in = rs >> Dtlshift; dr = rs & 0xffff; if(xinh != in || dr < doors[n&0x7f].dopen) goto hpass; xin = xinh << 16 | dr; yin = ty << 16 | 0x8000; /* intercept in middle of tile */ hdoor(i, tilehit); next: if(++i < vw.dx) goto loop; scalcol(); } static void calcscal(Scaler *s, int dy) { int i, step, fix, top, ps, pe; top = (vw.dy - dy) / 2; step = (dy << 16) / 64; fix = 0; for(i=0; i<nelem(scals[0]); i++, s++){ ps = (fix >> 16) + top; fix += step; pe = (fix >> 16) + top; s->dx = pe > ps ? pe - ps : 0; s->skip = ps == pe || pe < 0 || ps >= vw.dy || i == nelem(scals[0])-1; if(s->skip) continue; s->ps = ps; s->pe = pe; } } static void scaltab(int maxdy) { int dy; Scaler (*s)[nelem(scals[0])]; dy = 1; memset(scals, 0, sizeof scals); s = scals + 1; while(dy <= maxdy) calcscal(*s++, dy++ * 2); memcpy(scals, scals+1, sizeof *scals); sce = scals[dy-1]; } void scalspr(int n, int x, int dy) { int dx, lx, rx; Scaler *ss, *s, *se; Spr *spr; Sprc **c; spr = sprs + n; if(spr == nil) sysfatal("scalspr: missing sprite %d\n", n); dy >>= 1; ss = scals[dy]; lx = spr->lx; rx = spr->rx; scx = x; if(lx < 32){ s = ss + lx; se = ss + 32; while(s < se) scx -= s++->dx; }else{ s = ss + 32; se = ss + lx; while(s < se) scx += s++->dx; } c = spr->cs; for(s=ss+lx, se=ss+rx+1; s<se; c++, scx+=dx){ dx = s++->dx; if(dx != 0) scalscol(ss, *c, dx); } } s32int ffs(s32int a, s32int b) { int s; u32int r; uvlong h, l; s = 0; if(a < 0){ a = -a; s ^= 1; } if(b < 0) s ^= 1; b &= 0xffff; h = a >> 16; l = a & 0xffff; r = h * b + (l * b >> 16 & 0xffff); return s ? -r : r; } void render(void) { Tile *tl; for(tl=tiles; tl<tiles+nelem(tiles); tl++) tl->vis = 0; clear(); raytrace(); scalobj(); topspr(); } void setvw(void) { int i, an, dx, *p, *q, *e; double dface; vw.dx = vwsize * 16 & ~15; vw.dy = vwsize * 16 / 2 & ~1; vw.mid = vw.dx / 2 - 1; vw.Δhit = vw.dx / 10; vw.ofs = Vw * (160 - vw.dy) / 2 + (Vw - vw.dx) / 2; scaltab((vw.dx * 1.5) / 2); dx = vw.dx / 2; dface = Dfoclen + Dmin; i = 0; p = Δvwθ + dx; e = p + dx; q = p - 1; while(p < e){ /* start 0.5px over so vw.θ bisects two middle pixels */ an = (float)atan(i++ * Dglob / vw.dx / dface) * Rad; *p++ = -an; *q-- = an; } prjw = dx * dface / (Dglob/2); prjh = prjw * Dtlglobal >> 6; } void tab(void) { s32int i, *f, *fe; float a; double t; i = 0; f = ftan; fe = ftan + nelem(ftan) - 1; while(f < fe){ t = tan((i++ + 0.5) / (double)Rad); *f++ = t * Dtlglobal; *fe-- = 1/t * Dtlglobal; } /* low word: fraction; high word: mbz except 1<<31, sign bit */ a = 0; f = fe = sint; while(f < sint + 91){ i = Dtlglobal * sin(a); f[0] = f[360] = fe[180] = i; fe[360] = f[180] = i | 1<<31; a += Pi / 2 / 90; f++, fe--; } cost = sint + 90; }