quakeforge/libs/video/renderer/sw/sw_redge.c
Bill Currie e4f63fba6c Fix some integer shift overflows.
Thanks to Ozkan Sezer for pointing them out.
2012-01-30 09:13:06 +09:00

565 lines
13 KiB
C

/*
sw_redge.c
(description)
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 "QF/sound.h"
#include "d_ifacea.h"
#include "r_local.h"
/*
FIXME
the complex cases add new polys on most lines, so dont optimize for
keeping them the same have multiple free span lists to try to get better
coherence ? low depth complexity-- 1 to 3 or so this breaks spans at every
edge, even hidden ones (bad)
have a sentinal at both ends?
*/
edge_t *auxedges;
edge_t *r_edges, *edge_p, *edge_max;
surf_t *surfaces, *surface_p, *surf_max;
/*
surfaces are generated in back to front order by the bsp, so if a surf
pointer is greater than another one, it should be drawn in front
surfaces[1] is the background, and is used as the active surface stack
*/
edge_t *newedges[MAXHEIGHT];
edge_t *removeedges[MAXHEIGHT];
espan_t *span_p, *max_span_p;
int r_currentkey;
int current_iv;
int edge_head_u_shift20, edge_tail_u_shift20;
static void (*pdrawfunc) (void);
edge_t edge_head;
edge_t edge_tail;
edge_t edge_aftertail;
edge_t edge_sentinel;
float fv;
static void
R_DrawCulledPolys (void)
{
surf_t *s;
msurface_t *pface;
currententity = &r_worldentity;
if (r_worldpolysbacktofront) {
for (s = surface_p - 1; s > &surfaces[1]; s--) {
if (!s->spans)
continue;
if (!(s->flags & SURF_DRAWBACKGROUND)) {
pface = (msurface_t *) s->data;
R_RenderPoly (pface, 15);
}
}
} else {
for (s = &surfaces[1]; s < surface_p; s++) {
if (!s->spans)
continue;
if (!(s->flags & SURF_DRAWBACKGROUND)) {
pface = (msurface_t *) s->data;
R_RenderPoly (pface, 15);
}
}
}
}
void
R_BeginEdgeFrame (void)
{
int v;
edge_p = r_edges;
edge_max = &r_edges[r_numallocatededges];
surface_p = &surfaces[2]; // background is surface 1,
// surface 0 is a dummy
surfaces[1].spans = NULL; // no background spans yet
surfaces[1].flags = SURF_DRAWBACKGROUND;
// put the background behind everything in the world
pdrawfunc = R_GenerateSpans;
surfaces[1].key = 0x7FFFFFFF;
r_currentkey = 0;
// FIXME: set with memset
for (v = r_refdef.vrect.y; v < r_refdef.vrectbottom; v++) {
newedges[v] = removeedges[v] = NULL;
}
}
#ifdef PIC
# undef USE_INTEL_ASM //XXX asm pic hack
#endif
#ifndef USE_INTEL_ASM
/*
R_InsertNewEdges
Adds the edges in the linked list edgestoadd, adding them to the edges
in the linked list edgelist. edgestoadd is assumed to be sorted on u,
and non-empty (this is actually newedges[v]). edgelist is assumed to
be sorted on u, with a sentinel at the end (actually, this is the
active edge table starting at edge_head.next).
*/
void
R_InsertNewEdges (edge_t *edgestoadd, edge_t *edgelist)
{
edge_t *next_edge;
do {
next_edge = edgestoadd->next;
edgesearch:
if (edgelist->u >= edgestoadd->u)
goto addedge;
edgelist = edgelist->next;
if (edgelist->u >= edgestoadd->u)
goto addedge;
edgelist = edgelist->next;
if (edgelist->u >= edgestoadd->u)
goto addedge;
edgelist = edgelist->next;
if (edgelist->u >= edgestoadd->u)
goto addedge;
edgelist = edgelist->next;
goto edgesearch;
// insert edgestoadd before edgelist
addedge:
edgestoadd->next = edgelist;
edgestoadd->prev = edgelist->prev;
edgelist->prev->next = edgestoadd;
edgelist->prev = edgestoadd;
} while ((edgestoadd = next_edge) != NULL);
}
void
R_RemoveEdges (edge_t *pedge)
{
do {
pedge->next->prev = pedge->prev;
pedge->prev->next = pedge->next;
} while ((pedge = pedge->nextremove) != NULL);
}
void
R_StepActiveU (edge_t *pedge)
{
edge_t *pnext_edge, *pwedge;
while (1) {
nextedge:
pedge->u += pedge->u_step;
if (pedge->u < pedge->prev->u)
goto pushback;
pedge = pedge->next;
pedge->u += pedge->u_step;
if (pedge->u < pedge->prev->u)
goto pushback;
pedge = pedge->next;
pedge->u += pedge->u_step;
if (pedge->u < pedge->prev->u)
goto pushback;
pedge = pedge->next;
pedge->u += pedge->u_step;
if (pedge->u < pedge->prev->u)
goto pushback;
pedge = pedge->next;
goto nextedge;
pushback:
if (pedge == &edge_aftertail)
return;
// push it back to keep it sorted
pnext_edge = pedge->next;
// pull the edge out of the edge list
pedge->next->prev = pedge->prev;
pedge->prev->next = pedge->next;
// find out where the edge goes in the edge list
pwedge = pedge->prev->prev;
while (pwedge->u > pedge->u) {
pwedge = pwedge->prev;
}
// put the edge back into the edge list
pedge->next = pwedge->next;
pedge->prev = pwedge;
pedge->next->prev = pedge;
pwedge->next = pedge;
pedge = pnext_edge;
if (pedge == &edge_tail)
return;
}
}
#endif // !USE_INTEL_ASM
#ifndef USE_INTEL_ASM
static void
R_LeadingEdge (edge_t *edge)
{
espan_t *span;
surf_t *surf, *surf2;
int iu;
double fu, newzi, testzi, newzitop, newzibottom;
if (edge->surfs[1]) {
// it's adding a new surface in, so find the correct place
surf = &surfaces[edge->surfs[1]];
// don't start a span if this is an inverted span, with the end edge
// preceding the start edge (that is, we've already seen the end edge)
if (++surf->spanstate == 1) {
if (surf->insubmodel)
r_bmodelactive++;
surf2 = surfaces[1].next;
if (surf->key < surf2->key)
goto newtop;
// if it's two surfaces on the same plane, the one that's already
// active is in front, so keep going unless it's a bmodel
if (surf->insubmodel && (surf->key == surf2->key)) {
// must be two bmodels in the same leaf; sort on 1/z
fu = (float) (edge->u - 0xFFFFF) * (1.0 / 0x100000);
newzi = surf->d_ziorigin + fv * surf->d_zistepv +
fu * surf->d_zistepu;
newzibottom = newzi * 0.99;
testzi = surf2->d_ziorigin + fv * surf2->d_zistepv +
fu * surf2->d_zistepu;
if (newzibottom >= testzi) {
goto newtop;
}
newzitop = newzi * 1.01;
if (newzitop >= testzi) {
if (surf->d_zistepu >= surf2->d_zistepu) {
goto newtop;
}
}
}
continue_search:
do {
surf2 = surf2->next;
} while (surf->key > surf2->key);
if (surf->key == surf2->key) {
// if it's two surfaces on the same plane, the already active
// one is in front, so keep going unless it's a bmodel
if (!surf->insubmodel)
goto continue_search;
// must be two bmodels in the same leaf; sort on 1/z
fu = (float) (edge->u - 0xFFFFF) * (1.0 / 0x100000);
newzi = surf->d_ziorigin + fv * surf->d_zistepv +
fu * surf->d_zistepu;
newzibottom = newzi * 0.99;
testzi = surf2->d_ziorigin + fv * surf2->d_zistepv +
fu * surf2->d_zistepu;
if (newzibottom >= testzi) {
goto gotposition;
}
newzitop = newzi * 1.01;
if (newzitop >= testzi) {
if (surf->d_zistepu >= surf2->d_zistepu) {
goto gotposition;
}
}
goto continue_search;
}
goto gotposition;
newtop:
// emit a span (obscures current top)
iu = edge->u >> 20;
if (iu > surf2->last_u) {
span = span_p++;
span->u = surf2->last_u;
span->count = iu - span->u;
span->v = current_iv;
span->pnext = surf2->spans;
surf2->spans = span;
}
// set last_u on the new span
surf->last_u = iu;
gotposition:
// insert before surf2
surf->next = surf2;
surf->prev = surf2->prev;
surf2->prev->next = surf;
surf2->prev = surf;
}
}
}
static void
R_TrailingEdge (surf_t *surf, edge_t *edge)
{
espan_t *span;
int iu;
// don't generate a span if this is an inverted span, with the end edge
// preceding the start edge (that is, we haven't seen the start edge yet)
if (--surf->spanstate == 0) {
if (surf->insubmodel)
r_bmodelactive--;
if (surf == surfaces[1].next) {
// emit a span (current top going away)
iu = edge->u >> 20;
if (iu > surf->last_u) {
span = span_p++;
span->u = surf->last_u;
span->count = iu - span->u;
span->v = current_iv;
span->pnext = surf->spans;
surf->spans = span;
}
// set last_u on the surface below
surf->next->last_u = iu;
}
surf->prev->next = surf->next;
surf->next->prev = surf->prev;
}
}
static void
R_CleanupSpan (void)
{
surf_t *surf;
int iu;
espan_t *span;
// now that we've reached the right edge of the screen, we're done with any
// unfinished surfaces, so emit a span for whatever's on top
surf = surfaces[1].next;
iu = edge_tail_u_shift20;
if (iu > surf->last_u) {
span = span_p++;
span->u = surf->last_u;
span->count = iu - span->u;
span->v = current_iv;
span->pnext = surf->spans;
surf->spans = span;
}
// reset spanstate for all surfaces in the surface stack
do {
surf->spanstate = 0;
surf = surf->next;
} while (surf != &surfaces[1]);
}
void
R_GenerateSpans (void)
{
edge_t *edge;
surf_t *surf;
r_bmodelactive = 0;
// clear active surfaces to just the background surface
surfaces[1].next = surfaces[1].prev = &surfaces[1];
surfaces[1].last_u = edge_head_u_shift20;
// generate spans
for (edge = edge_head.next; edge != &edge_tail; edge = edge->next) {
if (edge->surfs[0]) {
// it has a left surface, so a surface is going away for this span
surf = &surfaces[edge->surfs[0]];
R_TrailingEdge (surf, edge);
if (!edge->surfs[1])
continue;
}
R_LeadingEdge (edge);
}
R_CleanupSpan ();
}
#endif // !USE_INTEL_ASM
/*
R_ScanEdges
Input:
newedges[] array
this has links to edges, which have links to surfaces
Output:
Each surface has a linked list of its visible spans
*/
void
R_ScanEdges (void)
{
int iv, bottom;
byte basespans[MAXSPANS * sizeof (espan_t) + CACHE_SIZE];
espan_t *basespan_p;
surf_t *s;
basespan_p = (espan_t *)
((intptr_t) (basespans + CACHE_SIZE - 1) & ~(CACHE_SIZE - 1));
max_span_p = &basespan_p[MAXSPANS - r_refdef.vrect.width];
span_p = basespan_p;
// clear active edges to just the background edges around the whole screen
// FIXME: most of this needs to be set up only once
edge_head.u = r_refdef.vrect.x << 20;
edge_head_u_shift20 = edge_head.u >> 20;
edge_head.u_step = 0;
edge_head.prev = NULL;
edge_head.next = &edge_tail;
edge_head.surfs[0] = 0;
edge_head.surfs[1] = 1;
edge_tail.u = (r_refdef.vrectright << 20) + 0xFFFFF;
edge_tail_u_shift20 = edge_tail.u >> 20;
edge_tail.u_step = 0;
edge_tail.prev = &edge_head;
edge_tail.next = &edge_aftertail;
edge_tail.surfs[0] = 1;
edge_tail.surfs[1] = 0;
edge_aftertail.u = -1; // force a move
edge_aftertail.u_step = 0;
edge_aftertail.next = &edge_sentinel;
edge_aftertail.prev = &edge_tail;
// FIXME: do we need this now that we clamp x in r_draw.c?
edge_sentinel.u = 32767 << 16; // make sure nothing sorts past this
edge_sentinel.prev = &edge_aftertail;
// process all scan lines
bottom = r_refdef.vrectbottom - 1;
for (iv = r_refdef.vrect.y; iv < bottom; iv++) {
current_iv = iv;
fv = (float) iv;
// mark that the head (background start) span is pre-included
surfaces[1].spanstate = 1;
if (newedges[iv]) {
R_InsertNewEdges (newedges[iv], edge_head.next);
}
(*pdrawfunc) ();
// flush the span list if we can't be sure we have enough spans left
// for the next scan
if (span_p > max_span_p) {
VID_UnlockBuffer ();
S_ExtraUpdate (); // don't let sound get messed up if going slow
VID_LockBuffer ();
if (r_drawculledpolys)
R_DrawCulledPolys ();
else
D_DrawSurfaces ();
// clear the surface span pointers
for (s = &surfaces[1]; s < surface_p; s++)
s->spans = NULL;
span_p = basespan_p;
}
if (removeedges[iv])
R_RemoveEdges (removeedges[iv]);
if (edge_head.next != &edge_tail)
R_StepActiveU (edge_head.next);
}
// do the last scan (no need to step or sort or remove on the last scan)
current_iv = iv;
fv = (float) iv;
// mark that the head (background start) span is pre-included
surfaces[1].spanstate = 1;
if (newedges[iv])
R_InsertNewEdges (newedges[iv], edge_head.next);
(*pdrawfunc) ();
// draw whatever's left in the span list
if (r_drawculledpolys)
R_DrawCulledPolys ();
else
D_DrawSurfaces ();
}