/* | |
Copyright (C) 1996-1997 Id Software, Inc. | |
This program is free software; you can redistribute it and/or | |
modify it under the terms of the GNU General Public License | |
as published by the Free Software Foundation; either version 2 | |
of the License, or (at your option) any later version. | |
This program is distributed in the hope that it will be useful, | |
but WITHOUT ANY WARRANTY; without even the implied warranty of | |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | |
See the GNU General Public License for more details. | |
You should have received a copy of the GNU General Public License | |
along with this program; if not, write to the Free Software | |
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |
*/ | |
// d_sprite.c: software top-level rasterization driver module for drawing | |
// sprites | |
#include "quakedef.h" | |
#include "d_local.h" | |
static int sprite_height; | |
static int minindex, maxindex; | |
static sspan_t *sprite_spans; | |
#if !id386 | |
/* | |
===================== | |
D_SpriteDrawSpans | |
===================== | |
*/ | |
void D_SpriteDrawSpans (sspan_t *pspan) | |
{ | |
int count, spancount, izistep; | |
int izi; | |
byte *pbase, *pdest; | |
fixed16_t s, t, snext, tnext, sstep, tstep; | |
float sdivz, tdivz, zi, z, du, dv, spancountminus1; | |
float sdivz8stepu, tdivz8stepu, zi8stepu; | |
byte btemp; | |
short *pz; | |
sstep = 0; // keep compiler happy | |
tstep = 0; // ditto | |
pbase = cacheblock; | |
sdivz8stepu = d_sdivzstepu * 8; | |
tdivz8stepu = d_tdivzstepu * 8; | |
zi8stepu = d_zistepu * 8; | |
// we count on FP exceptions being turned off to avoid range problems | |
izistep = (int)(d_zistepu * 0x8000 * 0x10000); | |
do | |
{ | |
pdest = (byte *)d_viewbuffer + (screenwidth * pspan->v) + pspan->u; | |
pz = d_pzbuffer + (d_zwidth * pspan->v) + pspan->u; | |
count = pspan->count; | |
if (count <= 0) | |
goto NextSpan; | |
// calculate the initial s/z, t/z, 1/z, s, and t and clamp | |
du = (float)pspan->u; | |
dv = (float)pspan->v; | |
sdivz = d_sdivzorigin + dv*d_sdivzstepv + du*d_sdivzstepu; | |
tdivz = d_tdivzorigin + dv*d_tdivzstepv + du*d_tdivzstepu; | |
zi = d_ziorigin + dv*d_zistepv + du*d_zistepu; | |
z = (float)0x10000 / zi; // prescale to 16.16 fixed-point | |
// we count on FP exceptions being turned off to avoid range problems | |
izi = (int)(zi * 0x8000 * 0x10000); | |
s = (int)(sdivz * z) + sadjust; | |
if (s > bbextents) | |
s = bbextents; | |
else if (s < 0) | |
s = 0; | |
t = (int)(tdivz * z) + tadjust; | |
if (t > bbextentt) | |
t = bbextentt; | |
else if (t < 0) | |
t = 0; | |
do | |
{ | |
// calculate s and t at the far end of the span | |
if (count >= 8) | |
spancount = 8; | |
else | |
spancount = count; | |
count -= spancount; | |
if (count) | |
{ | |
// calculate s/z, t/z, zi->fixed s and t at far end of span, | |
// calculate s and t steps across span by shifting | |
sdivz += sdivz8stepu; | |
tdivz += tdivz8stepu; | |
zi += zi8stepu; | |
z = (float)0x10000 / zi; // prescale to 16.16 fixed-point | |
snext = (int)(sdivz * z) + sadjust; | |
if (snext > bbextents) | |
snext = bbextents; | |
else if (snext < 8) | |
snext = 8; // prevent round-off error on <0 steps from | |
// from causing overstepping & running off the | |
// edge of the texture | |
tnext = (int)(tdivz * z) + tadjust; | |
if (tnext > bbextentt) | |
tnext = bbextentt; | |
else if (tnext < 8) | |
tnext = 8; // guard against round-off error on <0 steps | |
sstep = (snext - s) >> 3; | |
tstep = (tnext - t) >> 3; | |
} | |
else | |
{ | |
// calculate s/z, t/z, zi->fixed s and t at last pixel in span (so | |
// can't step off polygon), clamp, calculate s and t steps across | |
// span by division, biasing steps low so we don't run off the | |
// texture | |
spancountminus1 = (float)(spancount - 1); | |
sdivz += d_sdivzstepu * spancountminus1; | |
tdivz += d_tdivzstepu * spancountminus1; | |
zi += d_zistepu * spancountminus1; | |
z = (float)0x10000 / zi; // prescale to 16.16 fixed-point | |
snext = (int)(sdivz * z) + sadjust; | |
if (snext > bbextents) | |
snext = bbextents; | |
else if (snext < 8) | |
snext = 8; // prevent round-off error on <0 steps from | |
// from causing overstepping & running off the | |
// edge of the texture | |
tnext = (int)(tdivz * z) + tadjust; | |
if (tnext > bbextentt) | |
tnext = bbextentt; | |
else if (tnext < 8) | |
tnext = 8; // guard against round-off error on <0 steps | |
if (spancount > 1) | |
{ | |
sstep = (snext - s) / (spancount - 1); | |
tstep = (tnext - t) / (spancount - 1); | |
} | |
} | |
do | |
{ | |
btemp = *(pbase + (s >> 16) + (t >> 16) * cachewidth); | |
if (btemp != 255) | |
{ | |
if (*pz <= (izi >> 16)) | |
{ | |
*pz = izi >> 16; | |
*pdest = btemp; | |
} | |
} | |
izi += izistep; | |
pdest++; | |
pz++; | |
s += sstep; | |
t += tstep; | |
} while (--spancount > 0); | |
s = snext; | |
t = tnext; | |
} while (count > 0); | |
NextSpan: | |
pspan++; | |
} while (pspan->count != DS_SPAN_LIST_END); | |
} | |
#endif | |
/* | |
===================== | |
D_SpriteScanLeftEdge | |
===================== | |
*/ | |
void D_SpriteScanLeftEdge (void) | |
{ | |
int i, v, itop, ibottom, lmaxindex; | |
emitpoint_t *pvert, *pnext; | |
sspan_t *pspan; | |
float du, dv, vtop, vbottom, slope; | |
fixed16_t u, u_step; | |
pspan = sprite_spans; | |
i = minindex; | |
if (i == 0) | |
i = r_spritedesc.nump; | |
lmaxindex = maxindex; | |
if (lmaxindex == 0) | |
lmaxindex = r_spritedesc.nump; | |
vtop = ceil (r_spritedesc.pverts[i].v); | |
do | |
{ | |
pvert = &r_spritedesc.pverts[i]; | |
pnext = pvert - 1; | |
vbottom = ceil (pnext->v); | |
if (vtop < vbottom) | |
{ | |
du = pnext->u - pvert->u; | |
dv = pnext->v - pvert->v; | |
slope = du / dv; | |
u_step = (int)(slope * 0x10000); | |
// adjust u to ceil the integer portion | |
u = (int)((pvert->u + (slope * (vtop - pvert->v))) * 0x10000) + | |
(0x10000 - 1); | |
itop = (int)vtop; | |
ibottom = (int)vbottom; | |
for (v=itop ; v<ibottom ; v++) | |
{ | |
pspan->u = u >> 16; | |
pspan->v = v; | |
u += u_step; | |
pspan++; | |
} | |
} | |
vtop = vbottom; | |
i--; | |
if (i == 0) | |
i = r_spritedesc.nump; | |
} while (i != lmaxindex); | |
} | |
/* | |
===================== | |
D_SpriteScanRightEdge | |
===================== | |
*/ | |
void D_SpriteScanRightEdge (void) | |
{ | |
int i, v, itop, ibottom; | |
emitpoint_t *pvert, *pnext; | |
sspan_t *pspan; | |
float du, dv, vtop, vbottom, slope, uvert, unext, vvert, vnext; | |
fixed16_t u, u_step; | |
pspan = sprite_spans; | |
i = minindex; | |
vvert = r_spritedesc.pverts[i].v; | |
if (vvert < r_refdef.fvrecty_adj) | |
vvert = r_refdef.fvrecty_adj; | |
if (vvert > r_refdef.fvrectbottom_adj) | |
vvert = r_refdef.fvrectbottom_adj; | |
vtop = ceil (vvert); | |
do | |
{ | |
pvert = &r_spritedesc.pverts[i]; | |
pnext = pvert + 1; | |
vnext = pnext->v; | |
if (vnext < r_refdef.fvrecty_adj) | |
vnext = r_refdef.fvrecty_adj; | |
if (vnext > r_refdef.fvrectbottom_adj) | |
vnext = r_refdef.fvrectbottom_adj; | |
vbottom = ceil (vnext); | |
if (vtop < vbottom) | |
{ | |
uvert = pvert->u; | |
if (uvert < r_refdef.fvrectx_adj) | |
uvert = r_refdef.fvrectx_adj; | |
if (uvert > r_refdef.fvrectright_adj) | |
uvert = r_refdef.fvrectright_adj; | |
unext = pnext->u; | |
if (unext < r_refdef.fvrectx_adj) | |
unext = r_refdef.fvrectx_adj; | |
if (unext > r_refdef.fvrectright_adj) | |
unext = r_refdef.fvrectright_adj; | |
du = unext - uvert; | |
dv = vnext - vvert; | |
slope = du / dv; | |
u_step = (int)(slope * 0x10000); | |
// adjust u to ceil the integer portion | |
u = (int)((uvert + (slope * (vtop - vvert))) * 0x10000) + | |
(0x10000 - 1); | |
itop = (int)vtop; | |
ibottom = (int)vbottom; | |
for (v=itop ; v<ibottom ; v++) | |
{ | |
pspan->count = (u >> 16) - pspan->u; | |
u += u_step; | |
pspan++; | |
} | |
} | |
vtop = vbottom; | |
vvert = vnext; | |
i++; | |
if (i == r_spritedesc.nump) | |
i = 0; | |
} while (i != maxindex); | |
pspan->count = DS_SPAN_LIST_END; // mark the end of the span list | |
} | |
/* | |
===================== | |
D_SpriteCalculateGradients | |
===================== | |
*/ | |
void D_SpriteCalculateGradients (void) | |
{ | |
vec3_t p_normal, p_saxis, p_taxis, p_temp1; | |
float distinv; | |
TransformVector (r_spritedesc.vpn, p_normal); | |
TransformVector (r_spritedesc.vright, p_saxis); | |
TransformVector (r_spritedesc.vup, p_taxis); | |
VectorInverse (p_taxis); | |
distinv = 1.0 / (-DotProduct (modelorg, r_spritedesc.vpn)); | |
d_sdivzstepu = p_saxis[0] * xscaleinv; | |
d_tdivzstepu = p_taxis[0] * xscaleinv; | |
d_sdivzstepv = -p_saxis[1] * yscaleinv; | |
d_tdivzstepv = -p_taxis[1] * yscaleinv; | |
d_zistepu = p_normal[0] * xscaleinv * distinv; | |
d_zistepv = -p_normal[1] * yscaleinv * distinv; | |
d_sdivzorigin = p_saxis[2] - xcenter * d_sdivzstepu - | |
ycenter * d_sdivzstepv; | |
d_tdivzorigin = p_taxis[2] - xcenter * d_tdivzstepu - | |
ycenter * d_tdivzstepv; | |
d_ziorigin = p_normal[2] * distinv - xcenter * d_zistepu - | |
ycenter * d_zistepv; | |
TransformVector (modelorg, p_temp1); | |
sadjust = ((fixed16_t)(DotProduct (p_temp1, p_saxis) * 0x10000 + 0.5)) - | |
(-(cachewidth >> 1) << 16); | |
tadjust = ((fixed16_t)(DotProduct (p_temp1, p_taxis) * 0x10000 + 0.5)) - | |
(-(sprite_height >> 1) << 16); | |
// -1 (-epsilon) so we never wander off the edge of the texture | |
bbextents = (cachewidth << 16) - 1; | |
bbextentt = (sprite_height << 16) - 1; | |
} | |
/* | |
===================== | |
D_DrawSprite | |
===================== | |
*/ | |
void D_DrawSprite (void) | |
{ | |
int i, nump; | |
float ymin, ymax; | |
emitpoint_t *pverts; | |
sspan_t spans[MAXHEIGHT+1]; | |
sprite_spans = spans; | |
// find the top and bottom vertices, and make sure there's at least one scan to | |
// draw | |
ymin = 999999.9; | |
ymax = -999999.9; | |
pverts = r_spritedesc.pverts; | |
for (i=0 ; i<r_spritedesc.nump ; i++) | |
{ | |
if (pverts->v < ymin) | |
{ | |
ymin = pverts->v; | |
minindex = i; | |
} | |
if (pverts->v > ymax) | |
{ | |
ymax = pverts->v; | |
maxindex = i; | |
} | |
pverts++; | |
} | |
ymin = ceil (ymin); | |
ymax = ceil (ymax); | |
if (ymin >= ymax) | |
return; // doesn't cross any scans at all | |
cachewidth = r_spritedesc.pspriteframe->width; | |
sprite_height = r_spritedesc.pspriteframe->height; | |
cacheblock = (byte *)&r_spritedesc.pspriteframe->pixels[0]; | |
// copy the first vertex to the last vertex, so we don't have to deal with | |
// wrapping | |
nump = r_spritedesc.nump; | |
pverts = r_spritedesc.pverts; | |
pverts[nump] = pverts[0]; | |
D_SpriteCalculateGradients (); | |
D_SpriteScanLeftEdge (); | |
D_SpriteScanRightEdge (); | |
D_SpriteDrawSpans (sprite_spans); | |
} | |