greed/CODE/R_RENDER.C
2014-12-12 00:00:00 +00:00

384 lines
13 KiB
C

/***************************************************************************/
/* */
/* */
/* Raven 3D Engine */
/* Copyright (C) 1996 by Softdisk Publishing */
/* */
/* Original Design: */
/* John Carmack of id Software */
/* */
/* Enhancements by: */
/* Robert Morgan of Channel 7............................Main Engine Code */
/* Todd Lewis of Softdisk Publishing......Tools,Utilities,Special Effects */
/* John Bianca of Softdisk Publishing..............Low-level Optimization */
/* Carlos Hasan..........................................Music/Sound Code */
/* */
/* */
/***************************************************************************/
#include <STDIO.H>
#include <MATH.H>
#include <STRING.H>
#include <STDLIB.H>
#include "d_global.h"
#include "r_refdef.h"
#include "d_misc.h"
#include "d_ints.h"
/**** VARIABLES ****/
#define MAXENTRIES 1024
byte westwall[MAPROWS*MAPCOLS];
byte westflags[MAPROWS*MAPCOLS];
byte northwall[MAPROWS*MAPCOLS];
byte northflags[MAPROWS*MAPCOLS];
byte floorpic[MAPROWS*MAPCOLS];
byte floorflags[MAPROWS*MAPCOLS];
byte ceilingpic[MAPROWS*MAPCOLS];
byte ceilingflags[MAPROWS*MAPCOLS];
byte floorheight[MAPROWS*MAPCOLS];
byte ceilingheight[MAPROWS*MAPCOLS];
byte floordef[MAPROWS*MAPCOLS];
byte floordefflags[MAPROWS*MAPCOLS];
byte ceilingdef[MAPROWS*MAPCOLS];
byte ceilingdefflags[MAPROWS*MAPCOLS];
byte maplights[MAPROWS*MAPCOLS];
byte mapsprites[MAPROWS*MAPCOLS];
byte mapslopes[MAPROWS*MAPCOLS];
byte mapeffects[MAPROWS*MAPCOLS];
byte mapflags[MAPROWS*MAPCOLS];
int reallight[MAPROWS*MAPCOLS];
int actionflag;
int wallglow, wallglowindex;
int wallrotate;
int maplight;
int wallflicker1, wallflicker2, wallflicker3, wallflicker4, wallflags, wallcycle;
// each visible vertex is used up to four times, so to prevent recalculation
// the vertex info is reused if it has been calculated previously that f
// The calculated flag is also used to determine if a moving sprite is i
// is at least partially visable.
//
// frameon is incremented at the start of each frame, so it is 1 on the
// framevalid[][] holds the frameon number for which vertex[][] is valid
// set to 0 at initialization, so no points are valid
// cornervertex[][] is a pointer into vertexlist[]
// vertexlist[] holds the currently valid transformed vertexes
// vertexlist_p is set to vertexlist[0] at the start of each frame, and
// after transforming a new vertex
int frameon;
int framevalid[MAPROWS*MAPCOLS];
int framech[MAPROWS*MAPCOLS];
int framefl[MAPROWS*MAPCOLS];
vertex_t *cornervertex[MAPROWS*MAPCOLS];
vertex_t vertexlist[MAXVISVERTEXES], *vertexlist_p;
fixed_t costable[ANGLES+1];
fixed_t sintable[ANGLES+1];
pixel_t viewbuffer[MAX_VIEW_WIDTH*MAX_VIEW_HEIGHT];
pixel_t *viewylookup[MAX_VIEW_HEIGHT];
fixed_t yslope[MAX_VIEW_HEIGHT+MAXSCROLL2], xslope[MAX_VIEW_WIDTH+1];
byte **wallposts;
byte *colormaps;
int numcolormaps;
byte *zcolormap[(MAXZ>>FRACBITS)+1];
fixed_t viewx, viewy, viewz;
fixed_t viewcos, viewsin;
fixed_t xscale, yscale; // SCALE/viewcos , SCALE/viewsin
int viewangle, viewfineangle;
int viewtilex, viewtiley;
vertex_t *vertex[4]; // points to the for corner vertexes in vert
vertex_t *p1, *p2;
int side; // wall number 0-3
int walltype; // wall number (picture) of p1-p2 edge
int wallshadow; // degree of shadow for a tile
int xclipl, xcliph; // clip window for current tile
int tilex, tiley; // coordinates of the tile being rendered
int mapspot; // tiley*MAPSIZE+tilex
int *flattranslation; // global animation tables
int *walltranslation;
int spritelump, walllump, flatlump;
int numsprites, numwalls, numflats;
boolean doortile; // true if the tile being renderd has a door
int adjacentx[4]= { 0, 1, 0, -1 };
int adjacenty[4]= { -1, 0, 1, 0 };
entry_t entries[MAXENTRIES], *entry_p;
int entrymap[MAPCOLS*MAPROWS], entrycount[MAPCOLS*MAPROWS];
int entrycounter;
int fxtimecount;
extern int rtimecount;
vertex_t *TransformVertex(int tilex, int tiley)
/* Returns a pointer to the vertex for a given coordinate
tx,tz will be the transformed coordinates
px, floorheight, ceilingheight will be valid if tz >= MINZ */
{
fixed_t trx, try, scale;
vertex_t *point;
int mapspot2, fl, ch;
mapspot2=tiley*MAPROWS+tilex;
if (mapspot!=mapspot2)
{
if (mapflags[mapspot]&FL_FLOOR) fl=(floorheight[mapspot2]<<FRACBITS)-viewz;
else fl=((floorheight[mapspot])<<FRACBITS)-viewz;
if (mapflags[mapspot]&FL_CEILING) ch=(ceilingheight[mapspot2]<<FRACBITS)-viewz;
else ch=((ceilingheight[mapspot])<<FRACBITS)-viewz;
}
else
{
fl=((floorheight[mapspot2])<<FRACBITS)-viewz;
ch=((ceilingheight[mapspot2])<<FRACBITS)-viewz;
}
if (framevalid[mapspot2]==frameon && framefl[mapspot2]==fl && framech[mapspot2]==ch)
return cornervertex[mapspot2];
point=vertexlist_p++;
#ifdef VALIDATE
if (point==&vertexlist[MAXVISVERTEXES]) MS_Error("Vertexlist overflow (%i>=%i)",vertexlist_p-vertexlist,MAXVISVERTEXES);
#endif
point->floorheight=fl;
point->ceilingheight=ch;
trx=(tilex<<(FRACBITS+TILESHIFT))-viewx;
try=(tiley<<(FRACBITS+TILESHIFT))-viewy;
point->tx=FIXEDMUL(trx,viewsin)+FIXEDMUL(try,viewcos);
point->tz=FIXEDMUL(trx,viewcos)-FIXEDMUL(try,viewsin);
if (point->tz>=MINZ)
{
scale=FIXEDDIV(SCALE,point->tz);
point->px=CENTERX+(FIXEDMUL(point->tx,scale)>>FRACBITS);
point->floory=CENTERY-(FIXEDMUL(point->floorheight,scale)>>FRACBITS);
point->ceilingy=CENTERY-(FIXEDMUL(point->ceilingheight,scale)>>FRACBITS);
}
framevalid[mapspot2]=frameon;
cornervertex[mapspot2]=point;
framefl[mapspot2]=fl;
framech[mapspot2]=ch;
return point;
}
boolean ClipEdge(void)
/* Sets p1->px and p2->px correctly for Z values < MINZ
Returns false if entire edge is too close or far away */
{
fixed_t leftfrac, rightfrac, clipz, dx, dz;
if (p1->tz>MAXZ && p2->tz>MAXZ) return false; // entire face is too far away
if (p1->tz<=0 && p2->tz<=0) return false; // totally behind the projection plane
if (p1->tz<MINZ || p2->tz<MINZ)
{
dx = p2->tx - p1->tx;
dz = p2->tz - p1->tz;
if (p1->tz<MINZ)
{
if (labs(dx+dz)<1024) return false;
leftfrac = FIXEDDIV(-p1->tx - p1->tz , dx+dz);
}
if (p2->tz<MINZ)
{
if (labs(dz-dx)<1024) return false;
rightfrac = FIXEDDIV(p1->tx - p1->tz , dz-dx);
if (p1->tz<MINZ && rightfrac<leftfrac) return false; // back face
clipz = p1->tz + FIXEDMUL(dz,rightfrac);
if (clipz<0) return false;
p2->px = windowWidth;
}
}
if (p1->tz<MINZ)
{
clipz = p1->tz + FIXEDMUL(dz,leftfrac);
if (clipz<0) return false;
p1->px = 0;
}
if (p1->px==p2->px) return false;
return true;
}
void RenderTileWalls(entry_t *e)
{
int xl, xh, tx, ty, x1, x2;
tilex=e->tilex;
tiley=e->tiley;
xclipl=e->xmin;
xcliph=e->xmax;
//#ifdef VALIDATE
// if ((tilex<0)||(tilex>=MAPCOLS)||(tiley<0)||(tiley>=MAPROWS)||(xclipl<0)||
// (xclipl>=windowWidth)||(xcliph<0)||(xcliph>=windowWidth)||(xclipl>xcliph))
// MS_Error("Invalid RenderTile (%i, %i, %i, %i)\n", e->tilex, e->tiley,
// e->xmin, e->xmax);
//#endif
mapspot=tiley*MAPCOLS+tilex;
maplight=((int)maplights[mapspot]<<3)+reallight[mapspot];
wallshadow=mapeffects[mapspot];
// validate or transform the four corner vertexes
vertex[0]=TransformVertex(tilex,tiley);
vertex[1]=TransformVertex(tilex+1,tiley);
vertex[2]=TransformVertex(tilex+1,tiley+1);
vertex[3]=TransformVertex(tilex,tiley+1);
// handle a door if present
if (mapflags[mapspot]&FL_DOOR)
{
doortile=true;
RenderDoor(); // sets doorxl / doorxh
}
else doortile=false;
// draw or flow through the walls
for (side=0; side<4; side++)
{
p1=vertex[side];
p2=vertex[(side+1)&3];
if (!ClipEdge()) continue;
if (p1->px>=p2->px) continue;
switch (side)
{
case 0: // north
walltype=northwall[mapspot];
wallflags=northflags[mapspot];
break;
case 1: // east
walltype=westwall[mapspot+1];
wallflags=westflags[mapspot+1];
break;
case 2: // south
walltype=northwall[mapspot+MAPCOLS];
wallflags=northflags[mapspot+MAPCOLS];
break;
case 3: // west
walltype=westwall[mapspot];
wallflags=westflags[mapspot];
}
x1=p1->px<xclipl?xclipl : p1->px;
x2=p2->px-1>xcliph?xcliph : p2->px-1;
if (x1<=x2)
{ // totally clipped off side
if (walltype) DrawWall(x1,x2);
DrawSteps(x1,x2);
}
if (walltype==0 || (wallflags&F_TRANSPARENT))
{
// restrict outward flow by the door, if present
xl=p1->px;
xh=p2->px-1;
// restrict by clipping window
if (xl<xclipl) xl=xclipl;
if (xh>xcliph) xh=xcliph;
// flow into the adjacent tile if there is at least a one pix
if (xh>=xl)
{
tx=tilex+adjacentx[side];
ty=tiley+adjacenty[side];
if (tx<0 || tx>=MAPCOLS-1 || ty<0 || ty>=MAPROWS-1) continue;
entry_p->tilex=tx;
entry_p->tiley=ty;
entry_p->xmin=xl;
entry_p->xmax=xh;
entry_p->mapspot=(ty<<6)+tx;
++entrycounter;
entry_p->counter=entrycounter;
entrycount[entry_p->mapspot]=entrycounter;
++entry_p;
#ifdef VALIDATE
if (entry_p>=&entries[MAXENTRIES]) MS_Error("Entry Array OverFlow (%i>=%i)",entry_p-entries,MAXENTRIES);
#endif
}
}
}
}
void SetupFrame(void)
{
int i;
memset(viewbuffer,0,windowSize);
/* Clears the wallz array, so posts that fade out into the distance won't block sprites */
for(i=0;i<windowWidth;i++)
wallz[i]=MAXZ+1;
// reset span counters
numspans=0;
transparentposts=0;
++frameon;
vertexlist_p=vertexlist; // put the first transformed vertex
// special effects
if (rtimecount>fxtimecount)
{
if (++wallglowindex==32) wallglowindex=0;
if (wallglowindex<16) wallglow=wallglowindex<<1;
else wallglow=(32-wallglowindex)<<1;
if (wallrotate==63) wallrotate=0;
else wallrotate++;
wallflicker1=(MS_RndT()&63);
wallflicker2=(MS_RndT()&63);
wallflicker3=(MS_RndT()&63);
if (frameon&1) wallflicker4=(MS_RndT()%63)-32;
wallcycle++;
wallcycle&=3;
fxtimecount=timecount+5;
}
viewtilex=viewx>>TILEFRACSHIFT;
viewtiley=viewy>>TILEFRACSHIFT;
viewfineangle=viewangle<<FINESHIFT;
viewcos=costable[viewangle];
viewsin=sintable[viewangle];
xscale=FIXEDDIV(viewsin,SCALE);
yscale=FIXEDDIV(viewcos,SCALE);
}
void FlowView()
{
entry_t *process_p, *nextprocess_p;
process_p=entries;
process_p->tilex=viewtilex;
process_p->tiley=viewtiley;
process_p->mapspot=(viewtiley<<6)+viewtilex;
process_p->xmin=0;
process_p->xmax=windowWidth-1;
entry_p=process_p+1;
memset(entrycount,0,MAPCOLS*MAPROWS*4);
entrycounter=1;
while (process_p<entry_p)
{
if (process_p->mapspot==-1) // entry has been merged
{
process_p++;
continue;
}
/* check for mergeable entries */
if (entrycount[process_p->mapspot]>process_p->counter) // mergeable tile
for (nextprocess_p=process_p+1;nextprocess_p<entry_p;nextprocess_p++) // scan for mergeable entries
if (nextprocess_p->mapspot==process_p->mapspot)
{
if (nextprocess_p->xmin==process_p->xmax+1) process_p->xmax=nextprocess_p->xmax;
else if (nextprocess_p->xmax==process_p->xmin-1) process_p->xmin=nextprocess_p->xmin;
else // bad merge!
MS_Error("Bad tile event combination:\n"
" nextprocess_p=%d process_p=%d\n"
" nextprocess_p->xmin=%d nextprocess_p->xmax=%d\n"
" process_p->xmin=%d process_p->xmax=%d\n",
(int)nextprocess_p,(int)process_p,
nextprocess_p->xmin,nextprocess_p->xmax,
process_p->xmin,process_p->xmax);
entrycount[nextprocess_p->mapspot]=0;
nextprocess_p->mapspot=-1;
}
/* check for a dublicate entry */
if (entrymap[process_p->mapspot]==frameon) goto end;
entrymap[process_p->mapspot]=frameon;
RenderTileWalls(process_p);
RenderTileEnds();
end:
process_p++;
}
}