Wolf3D-Mac/RefBsp.c

1 line
12 KiB
C

#include "WolfDef.h"
static Word checkcoord[11][4] = { /* Indexs to the bspcoord table */
{3,0,2,1},
{3,0,2,0},
{3,1,2,0},
{0,0,0,0},
{2,0,2,1},
{0,0,0,0}, /* Not valid */
{3,1,3,0},
{0,0,0,0},
{2,0,3,1},
{2,1,3,1},
{2,1,3,0}};
/**********************************
Draw a 3-D textured polygon, must be done FAST!
**********************************/
void RenderWallLoop(Word x1,Word x2,Word distance)
{
fixed_t texturecolumn;
Word tile,scaler, angle;
/* calculate and draw each column */
if (rw_downside) {
while (x1 < x2) { /* Time to draw? */
scaler = rw_scale >> FRACBITS; /* Get the draw scale */
xscale[x1] = scaler; /* Save the scale factor */
angle = xtoviewangle[x1]+rw_centerangle;
texturecolumn = rw_midpoint - SUFixedMul(finetangent[angle],distance); /* Which texture to use? */
if ((Word)texturecolumn < rw_mintex) {
texturecolumn = rw_mintex;
} else if ((Word)texturecolumn >= rw_maxtex) {
texturecolumn = rw_maxtex-1;
}
tile = rw_texture[texturecolumn>>8]; /* Get the tile to use */
IO_ScaleWallColumn(x1,scaler,tile,(texturecolumn>>1)&127); /* Draw the line */
++x1; /* Next x */
rw_scale+=rw_scalestep; /* Step the scale factor for the wall */
}
return;
}
while (x1 < x2) { /* Time to draw? */
scaler = rw_scale >> FRACBITS; /* Get the draw scale */
xscale[x1] = scaler; /* Save the scale factor */
angle = xtoviewangle[x1]+rw_centerangle;
texturecolumn = SUFixedMul(finetangent[angle],distance)+rw_midpoint; /* Which texture to use? */
if ((Word)texturecolumn < rw_mintex) {
texturecolumn = rw_mintex;
} else if ((Word)texturecolumn >= rw_maxtex) {
texturecolumn = rw_maxtex-1;
}
tile = rw_texture[texturecolumn>>8]; /* Get the tile to use */
if (!(WallListPtr[tile+1] & 0x4000)) {
texturecolumn^=0xff; /* Reverse the tile for N,W walls */
}
IO_ScaleWallColumn(x1,scaler,tile,(texturecolumn>>1)&127); /* Draw the line */
++x1; /* Next x */
rw_scale+=rw_scalestep; /* Step the scale factor for the wall */
}
}
/*
=====================
=
= RenderWallRange
=
= Draw a wall segment between start and stop angles (inclusive) (short angles)
= No clipping is needed
=
======================
*/
void RenderWallRange (Word start,Word stop,saveseg_t *seg,Word distance)
{
LongWord scale2;
Word vangle;
Word x1,x2;
/* mark the segment as visible for auto map*/
seg->dir |= DIR_SEENFLAG; /* for automap*/
areavis[seg->area] = 1; /* for sprite drawing*/
start -= ANGLE180; /* Adjust the start angle */
stop -= ANGLE180; /* Adjust the stop angle */
vangle = (Word)(start+ANGLE90)>>ANGLETOFINESHIFT;
x1 = viewangletox[vangle];
vangle = (Word)(stop+ANGLE90-1)>>ANGLETOFINESHIFT; /* make non inclusive*/
x2 = viewangletox[vangle];
if (x2 == x1) {
return; /* less than one column wide*/
}
rw_scale = (long) ScaleFromGlobalAngle(start+centershort,distance)<<FRACBITS;
if (x2>x1+1) {
scale2 = (long) ScaleFromGlobalAngle(stop+centershort,distance)<<FRACBITS;
rw_scalestep = (long)(scale2-rw_scale)/(long)(x2-x1);
}
RenderWallLoop(x1,x2,distance);
}
/*
===============================================================================
=
= ClipWallSegment
=
= Clips the given screenpost and includes it in newcolumn
===============================================================================
*/
/* a screenpost_t is a solid range of visangles, used to clip and detect*/
/* span exposures / hidings*/
typedef struct {
Word top, bottom;
} screenpost_t;
#define MAXSEGS 16
screenpost_t solidsegs[MAXSEGS], *newend; /* newend is one past the last valid seg */
void ClipWallSegment(Word top,Word bottom,saveseg_t *seg,Word distance)
{
screenpost_t *next, *start;
/* find the first clippost that touches the source post (adjacent pixels are touching)*/
start = solidsegs;
while (start->bottom > top+1) {
start++;
}
if (top > start->top) {
if (bottom > start->top+1) { /* post is entirely visible (above start), so insert a new clippost*/
RenderWallRange(top, bottom,seg,distance);
next = newend;
newend++;
while (next != start) {
*next = *(next-1);
next--;
}
next->top = top;
next->bottom = bottom;
return;
}
/* there is a fragment above *start*/
RenderWallRange (top, start->top + 1,seg,distance);
start->top = top; /* adjust the clip size*/
}
if (bottom >= start->bottom)
return; /* bottom contained in start*/
next = start;
while (bottom <= (next+1)->top+1) {
/* there is a fragment between two posts*/
RenderWallRange (next->bottom - 1, (next+1)->top + 1,seg,distance);
next++;
if (bottom >= next->bottom) { /* bottom is contained in next*/
start->bottom = next->bottom; /* adjust the clip size*/
goto crunch;
}
}
/* there is a fragment after *next*/
RenderWallRange (next->bottom - 1, bottom,seg,distance);
start->bottom = bottom; /* adjust the clip size*/
/* remove start+1 to next from the clip list, because start now covers their area*/
crunch:
if (next == start) {
return; /* post just extended past the bottom of one post*/
}
while (next++ != newend) /* remove a post*/
*++start = *next;
newend = start+1;
}
/**********************************
Clear out the edge segments for the ray cast
(Assume full viewing angle)
**********************************/
void ClearClipSegs(void)
{
solidsegs[0].top = -1; /* Maximum angle */
solidsegs[0].bottom = ANGLE180 + clipshortangle; /* First edge */
solidsegs[1].top = ANGLE180 - clipshortangle; /* Left edge */
solidsegs[1].bottom = 0; /* Minimum angle */
newend = solidsegs+2;
}
/**********************************
Clip and draw a given wall segment
**********************************/
void P_DrawSeg (saveseg_t *seg)
{
Word segplane;
Word door;
door_t *door_p;
unsigned short span, tspan;
unsigned short angle1, angle2;
int texslide;
int distance;
if (seg->dir & DIR_DISABLEDFLAG) { /* Segment shut down? */
return; /* pushwall part*/
}
segplane = (Word)seg->plane << 7;
rw_mintex = (Word)seg->min << 7;
rw_maxtex = (Word)seg->max << 7;
/* adjust pushwall segs */
if (seg == pwallseg) { /* Is this the active pushwall? */
if (seg->dir&1) { /* east/west*/
segplane += PushWallRec.pwallychange;
} else { /* north/south*/
segplane += PushWallRec.pwallxchange;
}
}
/* get texture*/
if (seg->texture >= 129) { /* segment is a door */
door = seg->texture - 129; /* Which door is this? */
door_p = &doors[door];
rw_texture = &textures[129 + (door_p->info>>1)][0];
texslide = door_p->position;
rw_mintex += texslide;
} else {
texslide = 0;
rw_texture = &textures[seg->texture][0];
}
switch (seg->dir&3) { /* mask off the flags*/
case di_north:
distance = viewx - segplane;
if (distance <= 0) {
return; /* back side*/
}
rw_downside = FALSE;
rw_midpoint = viewy;
normalangle = 2*FINEANGLES/4;
angle1 = PointToAngle(segplane,rw_maxtex);
angle2 = PointToAngle(segplane,rw_mintex);
break;
case di_south:
distance = segplane - viewx;
if (distance <= 0) {
return; /* back side*/
}
rw_downside = TRUE;
rw_midpoint = viewy;
normalangle = 0*FINEANGLES/4;
angle1 = PointToAngle(segplane,rw_mintex);
angle2 = PointToAngle(segplane,rw_maxtex);
break;
case di_east:
distance = viewy - segplane;
if (distance <= 0) {
return; /* back side*/
}
rw_downside = TRUE;
rw_midpoint = viewx;
normalangle = 1*FINEANGLES/4;
angle1 = PointToAngle(rw_mintex,segplane);
angle2 = PointToAngle(rw_maxtex,segplane);
break;
case di_west:
distance = segplane - viewy;
if (distance <= 0) {
return; /* back side*/
}
rw_downside = FALSE;
rw_midpoint = viewx;
normalangle = 3*FINEANGLES/4;
angle1 = PointToAngle(rw_maxtex,segplane);
angle2 = PointToAngle(rw_mintex,segplane);
break;
}
/* clip to view edges*/
span = angle1 - angle2;
if (span >= 0x8000) { /* Test for negative (32 bit clean) */
return;
}
angle1 -= centershort;
angle2 -= centershort;
++angle2; /* make angle 2 non inclusive*/
tspan = angle1 + clipshortangle;
if (tspan > clipshortangle2) {
tspan -= clipshortangle2;
if (tspan >= span) {
return; /* totally off the left edge*/
}
angle1 = clipshortangle;
}
tspan = clipshortangle - angle2;
if (tspan > clipshortangle2) {
tspan -= clipshortangle2;
if (tspan >= span) {
return; /* totally off the left edge*/
}
angle2 = -clipshortangle;
}
/* calc center angle for texture mapping*/
rw_centerangle = (centerangle-normalangle)&FINEMASK;
if (rw_centerangle > FINEANGLES/2) {
rw_centerangle -= FINEANGLES;
}
rw_centerangle += FINEANGLES/4;
rw_midpoint -= texslide;
rw_mintex -= texslide;
angle1 += ANGLE180; /* adjust so angles are unsigned*/
angle2 += ANGLE180;
ClipWallSegment(angle1, angle2,seg,distance);
}
/**********************************
Returns True if some part of the BSP dividing line might be visible
**********************************/
Boolean CheckBSPNode(Word boxpos)
{
short angle1, angle2;
unsigned short span, tspan;
unsigned short uangle1, uangle2;
screenpost_t *start;
Word *PosPtr;
int x1,y1,x2,y2;
PosPtr = &checkcoord[boxpos][0];
x1 = bspcoord[PosPtr[0]];
y1 = bspcoord[PosPtr[1]];
x2 = bspcoord[PosPtr[2]];
y2 = bspcoord[PosPtr[3]];
angle1 = PointToAngle(x1,y1) - centershort;
angle2 = PointToAngle(x2,y2) - centershort;
/* check clip list for an open space */
span = angle1 - angle2;
if (span >= 0x8000) {
return TRUE; /* sitting on a line*/
}
tspan = angle1 + clipshortangle;
if (tspan > clipshortangle2) {
tspan -= clipshortangle2;
if (tspan >= span) {
return FALSE; /* totally off the left edge*/
}
angle1 = clipshortangle;
}
tspan = clipshortangle - angle2;
if (tspan > clipshortangle2) {
tspan -= clipshortangle2;
if (tspan >= span) {
return FALSE; /* totally off the left edge*/
}
angle2 = -clipshortangle;
}
/* find the first clippost that touches the source post (adjacent pixels are touching)*/
uangle1 = angle1 + ANGLE180;
uangle2 = angle2 + ANGLE180;
start = solidsegs;
while (start->bottom > uangle1+1) {
++start;
}
if (uangle1 <= start->top && uangle2 >= start->bottom) {
return FALSE; /* the clippost contains the new span*/
}
return TRUE;
}
/**********************************
Draw one or more wall segments
**********************************/
void TerminalNode (saveseg_t *seg)
{
for (;;) { /* Forever? */
P_DrawSeg(seg); /* Draw the wall segment (If visible) */
if (seg->dir & DIR_LASTSEGFLAG) { /* Last segment in list? */
return; /* Exit now */
}
++seg; /* Index to the next wall segment */
}
}
/**********************************
Render the 3D view by recursivly following the BSP tree
**********************************/
void RenderBSPNode(Word bspnum)
{
savenode_t *bsp; /* Pointer to the current BSP node */
Word side; /* decision line */
Word coordinate; /* Current coord */
Word savednum; /* Save index */
Word savedcoordinate; /* Save value */
Word boxpos; /* Index to the coord table */
bsp = &nodes[bspnum]; /* Get pointer to the current tree node */
if (bsp->dir & DIR_SEGFLAG) { /* There is a segment here... */
TerminalNode((saveseg_t *)bsp); /* Render it */
return; /* Exit */
}
/* decision node */
coordinate = bsp->plane<<7; /* stored as half tiles*/
if (bsp->dir) { /* True for vertical tiles */
side = viewx > coordinate; /* vertical decision line*/
savednum = BSPLEFT + (side^1); /* Left or right */
} else {
side = viewy > coordinate; /* horizontal decision line*/
savednum = BSPTOP + (side^1); /* Top or bottom */
}
savedcoordinate = bspcoord[savednum]; /* Save this coord */
bspcoord[savednum] = coordinate; /* Set my new coord boundary */
RenderBSPNode(bsp->children[side^1]); /* recursively divide front space*/
bspcoord[savednum] = savedcoordinate; /* Restore the coord */
savednum ^= 1; /* Negate the index */
savedcoordinate = bspcoord[savednum]; /* Save the other side */
bspcoord[savednum] = coordinate; /* Set the new side */
/* if the back side node is a single seg, don't bother explicitly checking visibility */
if ( ! ( nodes[bsp->children[side]].dir & DIR_LASTSEGFLAG ) ) {
/* don't flow into the back space if it is not going to be visible */
if (viewx <= bspcoord[BSPLEFT]) {
boxpos = 0;
} else if (viewx < bspcoord[BSPRIGHT]) {
boxpos = 1;
} else {
boxpos = 2;
}
if (viewy > bspcoord[BSPTOP]) {
if (viewy < bspcoord[BSPBOTTOM]) {
boxpos += 4;
} else {
boxpos += 8;
}
}
if (!CheckBSPNode(boxpos)) { /* Intersect possible? */
goto skipback; /* Exit now then */
}
}
RenderBSPNode(bsp->children[side]); /* recursively divide back space */
skipback:
bspcoord[savednum] = savedcoordinate;
}