mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2024-11-18 10:41:40 +00:00
418 lines
9.9 KiB
C
418 lines
9.9 KiB
C
/*
|
|
d_sprite.c
|
|
|
|
software top-level rasterization driver module for drawing sprites
|
|
|
|
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:
|
|
|
|
Free Software Foundation, Inc.
|
|
59 Temple Place - Suite 330
|
|
Boston, MA 02111-1307, USA
|
|
|
|
*/
|
|
#ifdef HAVE_CONFIG_H
|
|
# include "config.h"
|
|
#endif
|
|
|
|
static __attribute__ ((used)) const char rcsid[] =
|
|
"$Id$";
|
|
|
|
#include "QF/render.h"
|
|
|
|
#include "d_local.h"
|
|
|
|
static int sprite_height;
|
|
static int minindex, maxindex;
|
|
static sspan_t *sprite_spans;
|
|
|
|
|
|
#ifdef PIC
|
|
#undef USE_INTEL_ASM //XXX asm pic hack
|
|
#endif
|
|
|
|
|
|
#ifndef USE_INTEL_ASM
|
|
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
|
|
|
|
static 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);
|
|
}
|
|
|
|
static 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
|
|
}
|
|
|
|
static 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);
|
|
VectorNegate (p_taxis, 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;
|
|
}
|
|
|
|
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 = &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);
|
|
}
|