Files
Catacomb3D/C3_DRAW.C
2026-03-12 19:22:23 +01:00

1593 lines
33 KiB
C

/* Catacomb 3-D Source Code
* Copyright (C) 1993-2014 Flat Rock Software
*
* 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.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
// C3_DRAW.C
#include "C3_DEF.H"
#pragma hdrstop
//#define DRAWEACH // draw walls one at a time for debugging
unsigned highest;
unsigned mostwalls,numwalls;
/*
=============================================================================
LOCAL CONSTANTS
=============================================================================
*/
#define PI 3.141592657
#define ANGLEQUAD (ANGLES/4)
unsigned oldend;
#define FINEANGLES 3600
#define MINRATIO 16
const unsigned MAXSCALEHEIGHT = (VIEWWIDTH/2);
const unsigned MAXVISHEIGHT = (VIEWHEIGHT/2);
const unsigned BASESCALE = 32;
/*
=============================================================================
GLOBAL VARIABLES
=============================================================================
*/
//
// calculate location of screens in video memory so they have the
// maximum possible distance seperating them (for scaling overflow)
//
unsigned screenloc[3]= {0x900,0x2000,0x3700};
unsigned freelatch = 0x4e00;
boolean fizzlein;
long scaleshapecalll;
long scaletablecall;
/*
=============================================================================
LOCAL VARIABLES
=============================================================================
*/
long bytecount,endcount; // for profiling
int animframe;
int pixelangle[VIEWWIDTH];
int far finetangent[FINEANGLES+1];
int fineviewangle;
unsigned viewxpix,viewypix;
/*
============================================================================
3 - D DEFINITIONS
============================================================================
*/
fixed tileglobal = TILEGLOBAL;
fixed focallength = FOCALLENGTH;
fixed mindist = MINDIST;
int viewheight = VIEWHEIGHT;
fixed scale;
tilept tile,lasttile, // tile of wall being followed
focal, // focal point in tiles
left,mid,right; // rightmost tile in view
globpt edge,view;
int segstart[VIEWHEIGHT], // addline tracks line segment and draws
segend[VIEWHEIGHT],
segcolor[VIEWHEIGHT]; // only when the color changes
walltype walls[MAXWALLS],*leftwall,*rightwall;
//==========================================================================
//
// refresh stuff
//
int screenpage;
long lasttimecount;
//
// rendering stuff
//
int firstangle,lastangle;
fixed prestep;
fixed sintable[ANGLES+ANGLES/4],*costable = sintable+(ANGLES/4);
fixed viewx,viewy; // the focal point
int viewangle;
fixed viewsin,viewcos;
int zbuffer[VIEWXH+1]; // holds the height of the wall at that point
//==========================================================================
void DrawLine (int xl, int xh, int y,int color);
void DrawWall (walltype *wallptr);
void TraceRay (unsigned angle);
fixed FixedByFrac (fixed a, fixed b);
fixed FixedAdd (void);
fixed TransformX (fixed gx, fixed gy);
int FollowTrace (fixed tracex, fixed tracey, long deltax, long deltay, int max);
int BackTrace (int finish);
void ForwardTrace (void);
int TurnClockwise (void);
int TurnCounterClockwise (void);
void FollowWall (void);
void NewScene (void);
void BuildTables (void);
//==========================================================================
/*
==================
=
= DrawLine
=
= Must be in write mode 2 with all planes enabled
= The bit mask is left set to the end value, so clear it after all lines are
= drawn
=
= draws a black dot at the left edge of the line
=
==================
*/
unsigned static char dotmask[8] = {0x80,0x40,0x20,0x10,8,4,2,1};
unsigned static char leftmask[8] = {0xff,0x7f,0x3f,0x1f,0xf,7,3,1};
unsigned static char rightmask[8] = {0x80,0xc0,0xe0,0xf0,0xf8,0xfc,0xfe,0xff};
void DrawLine (int xl, int xh, int y,int color)
{
unsigned dest,xlp,xlb,xhb,maskleft,maskright,maskdot,mid;
xlb=xl/8;
xhb=xh/8;
if (xh<xl)
Quit("DrawLine: xh<xl");
if (y<VIEWY)
Quit("DrawLine: y<VIEWY");
if (y>VIEWYH)
Quit("DrawLine: y>VIEWYH");
xlp = xl&7;
maskleft = leftmask[xlp];
maskright = rightmask[xh&7];
mid = xhb-xlb-1;
dest = bufferofs+ylookup[y]+xlb;
//
// set the GC index register to point to the bit mask register
//
asm mov al,GC_BITMASK
asm mov dx,GC_INDEX
asm out dx,al
if (xlb==xhb)
{
//
// entire line is in one byte
//
maskleft&=maskright;
asm mov es,[screenseg]
asm mov di,[dest]
asm mov dx,GC_INDEX+1
asm mov al,[BYTE PTR maskleft]
asm out dx,al // mask off pixels
asm mov al,[BYTE PTR color]
asm xchg al,[es:di] // load latches and write pixels
return;
}
asm mov es,[screenseg]
asm mov di,[dest]
asm mov dx,GC_INDEX+1
asm mov bh,[BYTE PTR color]
//
// draw left side
//
asm mov al,[BYTE PTR maskleft]
asm out dx,al // mask off pixels
asm mov al,bh
asm xchg al,[es:di] // load latches and write pixels
asm inc di
//
// draw middle
//
asm mov al,255
asm out dx,al // no masking
asm mov al,bh
asm mov cx,[mid]
asm rep stosb
//
// draw right side
//
asm mov al,[BYTE PTR maskright]
asm out dx,al // mask off pixels
asm xchg bh,[es:di] // load latches and write pixels
}
//==========================================================================
void DrawLineDot (int xl, int xh, int y,int color)
{
unsigned dest,xlp,xlb,xhb,maskleft,maskright,maskdot,mid;
xlb=xl/8;
xhb=xh/8;
if (xh<xl)
Quit("DrawLine: xh<xl");
if (y<VIEWY)
Quit("DrawLine: y<VIEWY");
if (y>VIEWYH)
Quit("DrawLine: y>VIEWYH");
xlp = xl&7;
maskdot = dotmask[xlp];
maskleft = leftmask[xlp];
maskright = rightmask[xh&7];
mid = xhb-xlb-1;
dest = bufferofs+ylookup[y]+xlb;
//
// set the GC index register to point to the bit mask register
//
asm mov al,GC_BITMASK
asm mov dx,GC_INDEX
asm out dx,al
if (xlb==xhb)
{
//
// entire line is in one byte
//
maskleft&=maskright;
asm mov es,[screenseg]
asm mov di,[dest]
asm mov dx,GC_INDEX+1
asm mov al,[BYTE PTR maskleft]
asm out dx,al // mask off pixels
asm mov al,[BYTE PTR color]
asm xchg al,[es:di] // load latches and write pixels
//
// write the black dot at the start
//
asm mov al,[BYTE PTR maskdot]
asm out dx,al // mask off pixels
asm xor al,al
asm xchg al,[es:di] // load latches and write pixels
return;
}
asm mov es,[screenseg]
asm mov di,[dest]
asm mov dx,GC_INDEX+1
asm mov bh,[BYTE PTR color]
//
// draw left side
//
asm mov al,[BYTE PTR maskleft]
asm out dx,al // mask off pixels
asm mov al,bh
asm xchg al,[es:di] // load latches and write pixels
//
// write the black dot at the start
//
asm mov al,[BYTE PTR maskdot]
asm out dx,al // mask off pixels
asm xor al,al
asm xchg al,[es:di] // load latches and write pixels
asm inc di
//
// draw middle
//
asm mov al,255
asm out dx,al // no masking
asm mov al,bh
asm mov cx,[mid]
asm rep stosb
//
// draw right side
//
asm mov al,[BYTE PTR maskright]
asm out dx,al // mask off pixels
asm xchg bh,[es:di] // load latches and write pixels
}
//==========================================================================
long wallscalesource;
#ifdef DRAWEACH
/*
====================
=
= ScaleOneWall
=
====================
*/
void near ScaleOneWall (int xl, int xh)
{
int x,pixwidth,height;
*(((unsigned *)&wallscalesource)+1) = wallseg[xl];
for (x=xl;x<=xh;x+=pixwidth)
{
height = wallheight[x];
pixwidth = wallwidth[x];
(unsigned)wallscalesource = wallofs[x];
*(((unsigned *)&scaletablecall)+1) = (unsigned)scaledirectory[height];
(unsigned)scaletablecall = scaledirectory[height]->codeofs[0];
//
// scale a byte wide strip of wall
//
asm mov bx,[x]
asm mov di,bx
asm shr di,1
asm shr di,1
asm shr di,1 // X in bytes
asm add di,[bufferofs]
asm and bx,7
asm shl bx,1
asm shl bx,1
asm shl bx,1
asm add bx,[pixwidth] // bx = pixel*8+pixwidth-1
asm dec bx
asm mov al,BYTE PTR [bitmasks1+bx]
asm mov dx,GC_INDEX+1
asm out dx,al // set bit mask register
asm mov es,[screenseg]
asm lds si,[wallscalesource]
asm call [DWORD PTR ss:scaletablecall] // scale the line of pixels
asm mov al,BYTE PTR [ss:bitmasks2+bx]
asm or al,al
asm jz nosecond
//
// draw a second byte for vertical strips that cross two bytes
//
asm inc di
asm out dx,al // set bit mask register
asm call [DWORD PTR ss:scaletablecall] // scale the line of pixels
nosecond:
asm mov ax,ss
asm mov ds,ax
}
}
#endif
int walllight1[NUMFLOORS] = {0,
WALL1LPIC,WALL2LPIC,WALL3LPIC,WALL4LPIC,WALL5LPIC,WALL6LPIC,WALL7LPIC,
WALL1LPIC,WALL2LPIC,WALL3LPIC,WALL4LPIC,WALL5LPIC,WALL6LPIC,WALL7LPIC,
EXPWALL1PIC,EXPWALL2PIC,EXPWALL3PIC,
RDOOR1PIC,RDOOR2PIC,RDOOR1PIC,RDOOR2PIC,
YDOOR1PIC,YDOOR2PIC,YDOOR1PIC,YDOOR2PIC,
GDOOR1PIC,GDOOR2PIC,GDOOR1PIC,GDOOR2PIC,
BDOOR1PIC,BDOOR2PIC,BDOOR1PIC,BDOOR2PIC};
int walldark1[NUMFLOORS] = {0,
WALL1DPIC,WALL2DPIC,WALL3DPIC,WALL4DPIC,WALL5DPIC,WALL6DPIC,WALL7DPIC,
WALL1DPIC,WALL2DPIC,WALL3DPIC,WALL4DPIC,WALL5DPIC,WALL6DPIC,WALL7DPIC,
EXPWALL1PIC,EXPWALL2PIC,EXPWALL3PIC,
RDOOR1PIC,RDOOR2PIC,RDOOR1PIC,RDOOR2PIC,
YDOOR1PIC,YDOOR2PIC,YDOOR1PIC,YDOOR2PIC,
GDOOR1PIC,GDOOR2PIC,GDOOR1PIC,GDOOR2PIC,
BDOOR1PIC,BDOOR2PIC,BDOOR1PIC,BDOOR2PIC};
int walllight2[NUMFLOORS] = {0,
WALL1LPIC,WALL2LPIC,WALL3LPIC,WALL4LPIC,WALL5LPIC,WALL6LPIC,WALL7LPIC,
WALL1LPIC,WALL2LPIC,WALL3LPIC,WALL4LPIC,WALL5LPIC,WALL6LPIC,WALL7LPIC,
EXPWALL1PIC,EXPWALL2PIC,EXPWALL3PIC,
RDOOR2PIC,RDOOR1PIC,RDOOR2PIC,RDOOR1PIC,
YDOOR2PIC,YDOOR1PIC,YDOOR2PIC,YDOOR1PIC,
GDOOR2PIC,GDOOR1PIC,GDOOR2PIC,GDOOR1PIC,
BDOOR2PIC,BDOOR1PIC,BDOOR2PIC,BDOOR1PIC};
int walldark2[NUMFLOORS] = {0,
WALL1DPIC,WALL2DPIC,WALL3DPIC,WALL4DPIC,WALL5DPIC,WALL6DPIC,WALL7DPIC,
WALL1DPIC,WALL2DPIC,WALL3DPIC,WALL4DPIC,WALL5DPIC,WALL6DPIC,WALL7DPIC,
EXPWALL1PIC,EXPWALL2PIC,EXPWALL3PIC,
RDOOR2PIC,RDOOR1PIC,RDOOR2PIC,RDOOR1PIC,
YDOOR2PIC,YDOOR1PIC,YDOOR2PIC,YDOOR1PIC,
GDOOR2PIC,GDOOR1PIC,GDOOR2PIC,GDOOR1PIC,
BDOOR2PIC,BDOOR1PIC,BDOOR2PIC,BDOOR1PIC};
/*
=====================
=
= DrawVWall
=
= Draws a wall by vertical segments, for texture mapping!
=
= wallptr->side is true for east/west walls (constant x)
=
= fracheight and fracstep are 16.16 bit fractions
=
=====================
*/
void DrawVWall (walltype *wallptr)
{
int x,i;
unsigned source;
unsigned width,sourceint;
unsigned wallpic,wallpicseg;
unsigned skip;
long fracheight,fracstep,longheightchange;
unsigned height;
int heightchange;
unsigned slope,distance;
int traceangle,angle;
int mapadd;
unsigned lastpix,lastsource,lastwidth;
if (wallptr->rightclip < wallptr->leftclip)
Quit ("DrawVWall: Right < Left");
//
// setup for height calculation
//
wallptr->height1 >>= 1;
wallptr->height2 >>= 1;
wallptr->planecoord>>=10; // remove non significant bits
width = wallptr->x2 - wallptr->x1;
if (width)
{
heightchange = wallptr->height2 - wallptr->height1;
asm mov ax,[heightchange]
asm mov WORD PTR [longheightchange+2],ax
asm mov WORD PTR [longheightchange],0 // avoid long shift by 16
fracstep = longheightchange/width;
}
fracheight = ((long)wallptr->height1<<16)+0x8000;
skip = wallptr->leftclip - wallptr->x1;
if (skip)
fracheight += fracstep*skip;
//
// setup for texture mapping
//
// mapadd is 64*64 (to keep source positive) + the origin wall intercept
// distance has 6 unit bits, and 6 frac bits
// traceangle is the center view angle in FINEANGLES, moved to be in
// the +-90 degree range (to thew right of origin)
//
traceangle = fineviewangle;
//
// find wall picture to map from
//
if (wallptr->side)
{ // east or west wall
if (animframe)
wallpic = walllight2[wallptr->color];
else
wallpic = walllight1[wallptr->color];
if (wallptr->planecoord < viewxpix)
{
distance = viewxpix-wallptr->planecoord;
traceangle -= FINEANGLES/2;
mapadd = (64-viewypix&63); // the pixel spot of the origin
}
else
{
distance = wallptr->planecoord-viewxpix;
// traceangle is correct
mapadd = viewypix&63; // the pixel spot of the origin
}
}
else
{ // north or south wall
if (animframe)
wallpic = walldark2[wallptr->color];
else
wallpic = walldark1[wallptr->color];
if (wallptr->planecoord < viewypix)
{
distance = viewypix-wallptr->planecoord;
traceangle -= FINEANGLES/4;
mapadd = viewxpix&63; // the pixel spot of the origin
}
else
{
distance = wallptr->planecoord-viewypix;
traceangle -= FINEANGLES*3/4;
mapadd = (64-viewxpix&63); // the pixel spot of the origin
}
}
mapadd = 64*64-mapadd; // make sure it stays positive
wallpicseg = (unsigned)walldirectory[wallpic-FIRSTWALLPIC];
if (traceangle > FINEANGLES/2)
traceangle -= FINEANGLES;
//
// calculate everything
//
// IMPORTANT! This loop is executed around 5000 times / second!
//
lastpix = lastsource = (unsigned)-1;
for (x = wallptr->leftclip ; x <= wallptr->rightclip ; x++)
{
//
// height
//
asm mov ax,WORD PTR [fracheight]
asm mov dx,WORD PTR [fracheight+2]
asm mov cx,dx
asm add ax,WORD PTR [fracstep]
asm adc dx,WORD PTR [fracstep+2]
asm mov WORD PTR [fracheight],ax
asm mov WORD PTR [fracheight+2],dx
asm mov bx,[x]
asm shl bx,1
asm cmp cx,MAXSCALEHEIGHT
asm jbe storeheight
asm mov cx,MAXSCALEHEIGHT
storeheight:
asm mov WORD PTR [wallheight+bx],cx
asm mov WORD PTR [zbuffer+bx],cx
// height = fracheight>>16;
// fracheight += fracstep;
// if (height > MAXSCALEHEIGHT)
// height = MAXSCALEHEIGHT;
// wallheight[x] = zbuffer[x] = height;
//
// texture map
//
angle = pixelangle[x]+traceangle;
if (angle<0)
angle+=FINEANGLES;
slope = finetangent[angle];
//
// distance is an unsigned 6.6 bit number (12 pixel bits)
// slope is a signed 5.10 bit number
// result is a signed 11.16 bit number
//
#if 0
source = distance*slope;
source >>=20;
source += mapadd;
source &= 63; // mask off the unused units
source = 63-source;
source <<= 6; // multiply by 64 for offset into pic
#endif
asm mov ax,[distance]
asm imul [slope] // ax is the source pixel
asm mov al,ah
asm shr al,1
asm shr al,1 // low 6 bits is now pixel number
asm add ax,[mapadd]
asm and ax,63
asm mov dx,63
asm sub dx,ax // otherwise it is backwards
asm shl dx,1
asm shl dx,1
asm shl dx,1
asm shl dx,1
asm shl dx,1
asm shl dx,1 // *64 to index into shape
asm mov [source],dx
if (source != lastsource)
{
if (lastpix != (unsigned)-1)
{
wallofs[lastpix] = lastsource;
wallseg[lastpix] = wallpicseg;
wallwidth[lastpix] = lastwidth;
}
lastpix = x;
lastsource = source;
lastwidth = 1;
}
else
lastwidth++; // optimized draw, same map as last one
}
wallofs[lastpix] = lastsource;
wallseg[lastpix] = wallpicseg;
wallwidth[lastpix] = lastwidth;
}
//==========================================================================
/*
=================
=
= TraceRay
=
= Used to find the left and rightmost tile in the view area to be traced from
= Follows a ray of the given angle from viewx,viewy in the global map until
= it hits a solid tile
= sets:
= tile.x,tile.y : tile coordinates of contacted tile
= tilecolor : solid tile's color
=
==================
*/
int tilecolor;
void TraceRay (unsigned angle)
{
long tracex,tracey,tracexstep,traceystep,searchx,searchy;
fixed fixtemp;
int otx,oty,searchsteps;
tracexstep = costable[angle];
traceystep = sintable[angle];
//
// advance point so it is even with the view plane before we start checking
//
fixtemp = FixedByFrac(prestep,tracexstep);
tracex = viewx+fixtemp;
fixtemp = FixedByFrac(prestep,traceystep);
tracey = viewy-fixtemp;
tile.x = tracex>>TILESHIFT; // starting point in tiles
tile.y = tracey>>TILESHIFT;
if (tracexstep<0) // use 2's complement, not signed magnitude
tracexstep = -(tracexstep&0x7fffffff);
if (traceystep<0) // use 2's complement, not signed magnitude
traceystep = -(traceystep&0x7fffffff);
//
// we assume viewx,viewy is not inside a solid tile, so go ahead one step
//
do // until a solid tile is hit
{
otx = tile.x;
oty = tile.y;
spotvis[otx][oty] = true;
tracex += tracexstep;
tracey -= traceystep;
tile.x = tracex>>TILESHIFT;
tile.y = tracey>>TILESHIFT;
if (tile.x!=otx && tile.y!=oty && (tilemap[otx][tile.y] || tilemap[tile.x][oty]) )
{
//
// trace crossed two solid tiles, so do a binary search along the line
// to find a spot where only one tile edge is crossed
//
searchsteps = 0;
searchx = tracexstep;
searchy = traceystep;
do
{
searchx/=2;
searchy/=2;
if (tile.x!=otx && tile.y!=oty)
{
// still too far
tracex -= searchx;
tracey += searchy;
}
else
{
// not far enough, no tiles crossed
tracex += searchx;
tracey -= searchy;
}
//
// if it is REAL close, go for the most clockwise intersection
//
if (++searchsteps == 16)
{
tracex = (long)otx<<TILESHIFT;
tracey = (long)oty<<TILESHIFT;
if (tracexstep>0)
{
if (traceystep<0)
{
tracex += TILEGLOBAL-1;
tracey += TILEGLOBAL;
}
else
{
tracex += TILEGLOBAL;
}
}
else
{
if (traceystep<0)
{
tracex --;
tracey += TILEGLOBAL-1;
}
else
{
tracey --;
}
}
}
tile.x = tracex>>TILESHIFT;
tile.y = tracey>>TILESHIFT;
} while (( tile.x!=otx && tile.y!=oty) || (tile.x==otx && tile.y==oty) );
}
} while (!(tilecolor = tilemap[tile.x][tile.y]) );
}
//==========================================================================
/*
========================
=
= FixedByFrac
=
= multiply a 16/16 bit, 2's complement fixed point number by a 16 bit
= fraction, passed as a signed magnitude 32 bit number
=
========================
*/
#pragma warn -rvl // I stick the return value in with ASMs
fixed FixedByFrac (fixed a, fixed b)
{
fixed value;
//
// setup
//
asm mov si,[WORD PTR b+2] // sign of result = sign of fraction
asm mov ax,[WORD PTR a]
asm mov cx,[WORD PTR a+2]
asm or cx,cx
asm jns aok: // negative?
asm not ax
asm not cx
asm add ax,1
asm adc cx,0
asm xor si,0x8000 // toggle sign of result
aok:
//
// multiply cx:ax by bx
//
asm mov bx,[WORD PTR b]
asm mul bx // fraction*fraction
asm mov di,dx // di is low word of result
asm mov ax,cx //
asm mul bx // units*fraction
asm add ax,di
asm adc dx,0
//
// put result dx:ax in 2's complement
//
asm test si,0x8000 // is the result negative?
asm jz ansok:
asm not ax
asm not dx
asm add ax,1
asm adc dx,0
ansok:;
}
#pragma warn +rvl
//==========================================================================
/*
========================
=
= TransformPoint
=
= Takes paramaters:
= gx,gy : globalx/globaly of point
=
= globals:
= viewx,viewy : point of view
= viewcos,viewsin : sin/cos of viewangle
=
=
= defines:
= CENTERX : pixel location of center of view window
= TILEGLOBAL : size of one
= FOCALLENGTH : distance behind viewx/y for center of projection
= scale : conversion from global value to screen value
=
= returns:
= screenx,screenheight: projected edge location and size
=
========================
*/
void TransformPoint (fixed gx, fixed gy, int *screenx, unsigned *screenheight)
{
int ratio;
fixed gxt,gyt,nx,ny;
//
// translate point to view centered coordinates
//
gx = gx-viewx;
gy = gy-viewy;
//
// calculate newx
//
gxt = FixedByFrac(gx,viewcos);
gyt = FixedByFrac(gy,viewsin);
nx = gxt-gyt;
//
// calculate newy
//
gxt = FixedByFrac(gx,viewsin);
gyt = FixedByFrac(gy,viewcos);
ny = gyt+gxt;
//
// calculate perspective ratio
//
if (nx<0)
nx = 0;
ratio = nx*scale/FOCALLENGTH;
if (ratio<=MINRATIO)
ratio = MINRATIO;
*screenx = CENTERX + ny/ratio;
*screenheight = TILEGLOBAL/ratio;
}
//
// transform actor
//
void TransformActor (objtype *ob)
{
int ratio;
fixed gx,gy,gxt,gyt,nx,ny;
//
// translate point to view centered coordinates
//
gx = ob->x-viewx;
gy = ob->y-viewy;
//
// calculate newx
//
gxt = FixedByFrac(gx,viewcos);
gyt = FixedByFrac(gy,viewsin);
nx = gxt-gyt-ob->size;
//
// calculate newy
//
gxt = FixedByFrac(gx,viewsin);
gyt = FixedByFrac(gy,viewcos);
ny = gyt+gxt;
//
// calculate perspective ratio
//
if (nx<0)
nx = 0;
ratio = nx*scale/FOCALLENGTH;
if (ratio<=MINRATIO)
ratio = MINRATIO;
ob->viewx = CENTERX + ny/ratio;
ob->viewheight = TILEGLOBAL/ratio;
}
//==========================================================================
fixed TransformX (fixed gx, fixed gy)
{
int ratio;
fixed gxt,gyt,nx,ny;
//
// translate point to view centered coordinates
//
gx = gx-viewx;
gy = gy-viewy;
//
// calculate newx
//
gxt = FixedByFrac(gx,viewcos);
gyt = FixedByFrac(gy,viewsin);
return gxt-gyt;
}
//==========================================================================
/*
==================
=
= BuildTables
=
= Calculates:
=
= scale projection constant
= sintable/costable overlapping fractional tables
= firstangle/lastangle angles from focalpoint to left/right view edges
= prestep distance from focal point before checking for tiles
=
==================
*/
void BuildTables (void)
{
int i;
long intang;
long x;
float angle,anglestep,radtoint;
double tang;
fixed value;
//
// calculate the angle offset from view angle of each pixel's ray
//
radtoint = (float)FINEANGLES/2/PI;
for (i=0;i<VIEWWIDTH/2;i++)
{
// start 1/2 pixel over, so viewangle bisects two middle pixels
x = (TILEGLOBAL*i+TILEGLOBAL/2)/VIEWWIDTH;
tang = (float)x/(FOCALLENGTH+MINDIST);
angle = atan(tang);
intang = angle*radtoint;
pixelangle[VIEWWIDTH/2-1-i] = intang;
pixelangle[VIEWWIDTH/2+i] = -intang;
}
//
// calculate fine tangents
// 1 sign bit, 5 units (clipped to), 10 fracs
//
#define MININT (-MAXINT)
for (i=0;i<FINEANGLES/4;i++)
{
intang = tan(i/radtoint)*(1l<<10);
//
// if the tangent is not reprentable in this many bits, bound the
// units part ONLY
//
if (intang>MAXINT)
intang = 0x8f00 | (intang & 0xff);
else if (intang<MININT)
intang = 0xff00 | (intang & 0xff);
finetangent[i] = intang;
// finetangent[FINEANGLES/2+i] = intang;
// finetangent[FINEANGLES/2-i-1] = -intang;
finetangent[FINEANGLES-i-1] = -intang;
}
//
// calculate scale value so one tile at mindist allmost fills the view horizontally
//
scale = GLOBAL1/VIEWWIDTH;
scale *= focallength;
scale /= (focallength+mindist);
//
// costable overlays sintable with a quarter phase shift
// ANGLES is assumed to be divisable by four
//
// The low word of the value is the fraction, the high bit is the sign bit,
// bits 16-30 should be 0
//
angle = 0;
anglestep = PI/2/ANGLEQUAD;
for (i=0;i<=ANGLEQUAD;i++)
{
value=GLOBAL1*sin(angle);
sintable[i]=
sintable[i+ANGLES]=
sintable[ANGLES/2-i] = value;
sintable[ANGLES-i]=
sintable[ANGLES/2+i] = value | 0x80000000l;
angle += anglestep;
}
//
// figure trace angles for first and last pixel on screen
//
angle = atan((float)VIEWWIDTH/2*scale/FOCALLENGTH);
angle *= ANGLES/(PI*2);
intang = (int)angle+1;
firstangle = intang;
lastangle = -intang;
prestep = GLOBAL1*((float)FOCALLENGTH/costable[firstangle]);
//
// misc stuff
//
walls[0].x2 = VIEWX-1;
walls[0].height2 = 32000;
}
//==========================================================================
/*
=====================
=
= ClearScreen
=
=====================
*/
void ClearScreen (void)
{
//
// clear the screen
//
asm mov dx,GC_INDEX
asm mov ax,GC_MODE + 256*2 // read mode 0, write mode 2
asm out dx,ax
asm mov ax,GC_BITMASK + 255*256
asm out dx,ax
asm mov dx,40-VIEWWIDTH/8
asm mov bl,VIEWWIDTH/16
asm mov bh,CENTERY+1
asm xor ax,ax
asm mov es,[screenseg]
asm mov di,[bufferofs]
toploop:
asm mov cl,bl
asm rep stosw
asm stosb
asm add di,dx
asm dec bh
asm jnz toploop
asm mov bh,CENTERY+1
asm mov ax,0x0808
bottomloop:
asm mov cl,bl
asm rep stosw
asm stosb
asm add di,dx
asm dec bh
asm jnz bottomloop
asm mov dx,GC_INDEX
asm mov ax,GC_MODE + 256*10 // read mode 1, write mode 2
asm out dx,ax
asm mov al,GC_BITMASK
asm out dx,al
}
//==========================================================================
/*
=====================
=
= DrawWallList
=
= Clips and draws all the walls traced this refresh
=
=====================
*/
void DrawWallList (void)
{
int i,leftx,newleft,rightclip;
walltype *wall, *check;
asm mov ax,ds
asm mov es,ax
asm mov di,OFFSET wallwidth
asm xor ax,ax
asm mov cx,VIEWWIDTH/2
asm rep stosw
ClearScreen ();
rightwall->x1 = VIEWXH+1;
rightwall->height1 = 32000;
(rightwall+1)->x1 = 32000;
leftx = -1;
for (wall=&walls[1];wall<rightwall && leftx<=VIEWXH ;wall++)
{
if (leftx >= wall->x2)
continue;
rightclip = wall->x2;
check = wall+1;
while (check->x1 <= rightclip && check->height1 >= wall->height2)
{
rightclip = check->x1-1;
check++;
}
if (rightclip>VIEWXH)
rightclip=VIEWXH;
if (leftx < wall->x1 - 1)
newleft = wall->x1-1; // there was black space between walls
else
newleft = leftx;
if (rightclip > newleft)
{
wall->leftclip = newleft+1;
wall->rightclip = rightclip;
DrawVWall (wall);
leftx = rightclip;
}
}
#ifndef DRAWEACH
ScaleWalls (); // draw all the walls
#endif
}
//==========================================================================
/*
=====================
=
= DrawScaleds
=
= Draws all objects that are visable
=
=====================
*/
objtype *depthsort[MAXACTORS];
void DrawScaleds (void)
{
int i,j,least,numvisable,height;
objtype *obj,**vislist,*farthest;
memptr shape;
byte *tilespot,*visspot;
numvisable = 0;
//
// calculate base positions of all objects
//
vislist = &depthsort[0];
for (obj = player->next;obj;obj=obj->next)
{
if (!obj->state->shapenum)
continue;
tilespot = &tilemap[0][0]+(obj->tilex<<6)+obj->tiley;
visspot = &spotvis[0][0]+(obj->tilex<<6)+obj->tiley;
//
// could be in any of the nine surrounding tiles
//
if (*visspot
|| ( *(visspot-1) && !*(tilespot-1) )
|| ( *(visspot+1) && !*(tilespot+1) )
|| ( *(visspot-65) && !*(tilespot-65) )
|| ( *(visspot-64) && !*(tilespot-64) )
|| ( *(visspot-63) && !*(tilespot-63) )
|| ( *(visspot+65) && !*(tilespot+65) )
|| ( *(visspot+64) && !*(tilespot+64) )
|| ( *(visspot+63) && !*(tilespot+63) ) )
{
obj->active = true;
TransformActor (obj);
if (!obj->viewheight || obj->viewheight > VIEWWIDTH)
continue; // too close or far away
*vislist++ = obj;
numvisable++;
}
}
if (vislist == &depthsort[0])
return; // no visable objects
//
// draw from back to front
//
for (i = 0; i<numvisable; i++)
{
least = 32000;
for (j=0;j<numvisable;j++)
{
height = depthsort[j]->viewheight;
if (height < least)
{
least = height;
farthest = depthsort[j];
}
}
//
// draw farthest
//
shape = shapedirectory[farthest->state->shapenum-FIRSTSCALEPIC];
ScaleShape(farthest->viewx,shape,farthest->viewheight);
farthest->viewheight = 32000;
}
}
//==========================================================================
/*
=====================
=
= CalcTics
=
=====================
*/
void CalcTics (void)
{
long newtime,oldtimecount;
#ifdef PROFILE
tics = 1;
return;
#endif
//
// calculate tics since last refresh for adaptive timing
//
if (lasttimecount > TimeCount)
TimeCount = lasttimecount; // if the game was paused a LONG time
if (DemoMode) // demo recording and playback needs
{ // to be constant
//
// take DEMOTICS or more tics, and modify Timecount to reflect time taken
//
oldtimecount = lasttimecount;
while (TimeCount<oldtimecount+DEMOTICS*2)
;
lasttimecount = oldtimecount + DEMOTICS;
TimeCount = lasttimecount + DEMOTICS;
tics = DEMOTICS;
}
else
{
//
// non demo, so report actual time
//
newtime = TimeCount;
tics = newtime-lasttimecount;
lasttimecount = newtime;
#ifdef FILEPROFILE
strcpy (scratch,"\tTics:");
itoa (tics,str,10);
strcat (scratch,str);
strcat (scratch,"\n");
write (profilehandle,scratch,strlen(scratch));
#endif
if (tics>MAXTICS)
{
TimeCount -= (tics-MAXTICS);
tics = MAXTICS;
}
}
}
//==========================================================================
/*
========================
=
= DrawHand
=
========================
*/
void DrawHand (void)
{
int picnum;
memptr source;
unsigned dest,width,height;
picnum = HAND1PICM;
if (gamestate.shotpower || boltsleft)
picnum += (((unsigned)TimeCount>>3)&1);
source = grsegs[picnum];
dest = ylookup[VIEWHEIGHT-handheight]+12+bufferofs;
width = picmtable[picnum-STARTPICM].width;
height = picmtable[picnum-STARTPICM].height;
VW_MaskBlock(source,0,dest,width,handheight,width*height);
EGAMAPMASK(15);
}
//==========================================================================
/*
========================
=
= ThreeDRefresh
=
========================
*/
void ThreeDRefresh (void)
{
int tracedir;
restart:
aborttrace = false;
//
// clear out the traced array
//
asm mov ax,ds
asm mov es,ax
asm mov di,OFFSET spotvis
asm xor ax,ax
asm mov cx,[mapwidth] // mapheight*32 words
asm shl cx,1
asm shl cx,1
asm shl cx,1
asm shl cx,1
asm shl cx,1
asm rep stosw
//
// set up variables for this view
//
viewangle = player->angle;
fineviewangle = viewangle*(FINEANGLES/ANGLES);
viewsin = sintable[viewangle];
viewcos = costable[viewangle];
viewx = player->x - FixedByFrac(FOCALLENGTH,viewcos);
viewy = player->y + FixedByFrac(FOCALLENGTH,viewsin);
viewx &= 0xfffffc00; // stop on a pixel boundary
viewy &= 0xfffffc00;
viewx += 0x180;
viewy += 0x180;
viewxpix = viewx>>10;
viewypix = viewy>>10;
focal.x = viewx>>TILESHIFT;
focal.y = viewy>>TILESHIFT;
//
// find the rightmost visable tile in view
//
tracedir = viewangle + lastangle;
if (tracedir<0)
tracedir+=ANGLES;
else if (tracedir>=ANGLES)
tracedir-=ANGLES;
TraceRay( tracedir );
right.x = tile.x;
right.y = tile.y;
//
// find the leftmost visable tile in view
//
tracedir = viewangle + firstangle;
if (tracedir<0)
tracedir+=ANGLES;
else if (tracedir>=ANGLES)
tracedir-=ANGLES;
TraceRay( tracedir );
//
// follow the walls from there to the right
//
rightwall = &walls[1];
FollowWalls ();
if (aborttrace)
goto restart;
//
// actually draw stuff
//
if (++screenpage == 3)
screenpage = 0;
bufferofs = screenloc[screenpage];
EGAWRITEMODE(2);
EGAMAPMASK(15);
//
// draw the wall list saved be FollowWalls ()
//
animframe = (TimeCount&8)>>3;
//
// draw all the scaled images
//
asm mov dx,GC_INDEX
asm mov ax,GC_COLORDONTCARE
asm out dx,ax // don't look at any of the planes
asm mov ax,GC_MODE + 256*(10) // read mode 1, write mode 2
asm out dx,ax
asm mov al,GC_BITMASK
asm out dx,al
DrawWallList();
DrawScaleds();
EGAWRITEMODE(0);
EGABITMASK(0xff);
//
// draw hand
//
if (handheight)
DrawHand ();
//
// show screen and time last cycle
//
if (fizzlein)
{
fizzlein = false;
FizzleFade(bufferofs,displayofs,VIEWWIDTH,VIEWHEIGHT,true);
lasttimecount = TimeCount;
if (MousePresent) Mouse(MDelta); // Clear accumulated mouse movement
}
asm cli
asm mov cx,[bufferofs]
asm mov dx,3d4h // CRTC address register
asm mov al,0ch // start address high register
asm out dx,al
asm inc dx
asm mov al,ch
asm out dx,al // set the high byte
asm dec dx
asm mov al,0dh // start address low register
asm out dx,al
asm inc dx
asm mov al,cl
asm out dx,al // set the low byte
asm sti
displayofs = bufferofs;
CalcTics ();
}