// WL_DRAW.C
#include "WL_DEF.H"
#include DOS.H
#pragma hdrstop
//#define DEBUGWALLS
//#define DEBUGTICS
/*
=============================================================================
LOCAL CONSTANTS
=============================================================================
*/
// the door is the last picture before the sprites
#define DOORWALL (PMSpriteStart-8)
#define ACTORSIZE 0x4000
/*
=============================================================================
GLOBAL VARIABLES
=============================================================================
*/
#ifdef DEBUGWALLS
unsigned screenloc[3]= {0,0,0};
#else
unsigned screenloc[3]= {PAGE1START,PAGE2START,PAGE3START};
#endif
unsigned freelatch = FREESTART;
long lasttimecount;
long frameon;
unsigned wallheight[MAXVIEWWIDTH];
fixed tileglobal = TILEGLOBAL;
fixed mindist = MINDIST;
//
// math tables
//
int pixelangle[MAXVIEWWIDTH];
long far finetangent[FINEANGLES/4];
fixed far sintable[ANGLES+ANGLES/4],far *costable = sintable+(ANGLES/4);
//
// refresh variables
//
fixed viewx,viewy; // the focal point
int viewangle;
fixed viewsin,viewcos;
fixed FixedByFrac (fixed a, fixed b);
void TransformActor (objtype *ob);
void BuildTables (void);
void ClearScreen (void);
int CalcRotate (objtype *ob);
void DrawScaleds (void);
void CalcTics (void);
void FixOfs (void);
void ThreeDRefresh (void);
//
// wall optimization variables
//
int lastside; // true for vertical
long lastintercept;
int lasttilehit;
//
// ray tracing variables
//
int focaltx,focalty,viewtx,viewty;
int midangle,angle;
unsigned xpartial,ypartial;
unsigned xpartialup,xpartialdown,ypartialup,ypartialdown;
unsigned xinttile,yinttile;
unsigned tilehit;
unsigned pixx;
int xtile,ytile;
int xtilestep,ytilestep;
long xintercept,yintercept;
long xstep,ystep;
int horizwall[MAXWALLTILES],vertwall[MAXWALLTILES];
/*
=============================================================================
LOCAL VARIABLES
=============================================================================
*/
void AsmRefresh (void); // in WL_DR_A.ASM
/*
============================================================================
3 - D DEFINITIONS
============================================================================
*/
//==========================================================================
/*
========================
=
= 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)
{
//
// 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 neg cx
asm neg ax
asm sbb 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 neg dx
asm neg ax
asm sbb dx,0
ansok:;
}
#pragma warn +rvl
//==========================================================================
/*
========================
=
= TransformActor
=
= Takes paramaters:
= gx,gy : globalx/globaly of point
=
= globals:
= viewx,viewy : point of view
= viewcos,viewsin : sin/cos of viewangle
= scale : conversion from global value to screen value
=
= sets:
= screenx,transx,transy,screenheight: projected edge location and size
=
========================
*/
//
// transform actor
//
void TransformActor (objtype *ob)
{
int ratio;
fixed gx,gy,gxt,gyt,nx,ny;
long temp;
//
// 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-ACTORSIZE; // fudge the shape forward a bit, because
// the midpoint could put parts of the shape
// into an adjacent wall
//
// calculate newy
//
gxt = FixedByFrac(gx,viewsin);
gyt = FixedByFrac(gy,viewcos);
ny = gyt+gxt;
//
// calculate perspective ratio
//
ob->transx = nx;
ob->transy = ny;
if (nxmindist) // too close, don't overflow the divide
{
ob->viewheight = 0;
return;
}
ob->viewx = centerx + ny*scale/nx; // DEBUG: use assembly divide
//
// calculate height (heightnumerator/(nx>>8))
//
asm mov ax,[WORD PTR heightnumerator]
asm mov dx,[WORD PTR heightnumerator+2]
asm idiv [WORD PTR nx+1] // nx>>8
asm mov [WORD PTR temp],ax
asm mov [WORD PTR temp+2],dx
ob->viewheight = temp;
}
//==========================================================================
/*
========================
=
= TransformTile
=
= Takes paramaters:
= tx,ty : tile the object is centered in
=
= globals:
= viewx,viewy : point of view
= viewcos,viewsin : sin/cos of viewangle
= scale : conversion from global value to screen value
=
= sets:
= screenx,transx,transy,screenheight: projected edge location and size
=
= Returns true if the tile is withing getting distance
=
========================
*/
boolean TransformTile (int tx, int ty, int *dispx, int *dispheight)
{
int ratio;
fixed gx,gy,gxt,gyt,nx,ny;
long temp;
//
// translate point to view centered coordinates
//
gx = ((long)tx TILESHIFT)+0x8000-viewx;
gy = ((long)tyTILESHIFT)+0x8000-viewy;
//
// calculate newx
//
gxt = FixedByFrac(gx,viewcos);
gyt = FixedByFrac(gy,viewsin);
nx = gxt-gyt-0x2000; // 0x2000 is size of object
//
// calculate newy
//
gxt = FixedByFrac(gx,viewsin);
gyt = FixedByFrac(gy,viewcos);
ny = gyt+gxt;
//
// calculate perspective ratio
//
if (nxmindist) // too close, don't overflow the divide
{
*dispheight = 0;
return false;
}
*dispx = centerx + ny*scale/nx; // DEBUG: use assembly divide
//
// calculate height (heightnumerator/(nx>>8))
//
asm mov ax,[WORD PTR heightnumerator]
asm mov dx,[WORD PTR heightnumerator+2]
asm idiv [WORD PTR nx+1] // nx>>8
asm mov [WORD PTR temp],ax
asm mov [WORD PTR temp+2],dx
*dispheight = temp;
//
// see if it should be grabbed
//
if (nx TILEGLOBAL && ny>-TILEGLOBAL/2 && ny TILEGLOBAL/2)
return true;
else
return false;
}
//==========================================================================
/*
====================
=
= CalcHeight
=
= Calculates the height of xintercept,yintercept from viewx,viewy
=
====================
*/
#pragma warn -rvl // I stick the return value in with ASMs
int CalcHeight (void)
{
int transheight;
int ratio;
fixed gxt,gyt,nx,ny;
long gx,gy;
gx = xintercept-viewx;
gxt = FixedByFrac(gx,viewcos);
gy = yintercept-viewy;
gyt = FixedByFrac(gy,viewsin);
nx = gxt-gyt;
//
// calculate perspective ratio (heightnumerator/(nx>>8))
//
if (nxmindist)
nx=mindist; // don't let divide overflow
asm mov ax,[WORD PTR heightnumerator]
asm mov dx,[WORD PTR heightnumerator+2]
asm idiv [WORD PTR nx+1] // nx>>8
}
//==========================================================================
/*
===================
=
= ScalePost
=
===================
*/
long postsource;
unsigned postx;
unsigned postwidth;
void near ScalePost (void) // VGA version
{
asm mov ax,SCREENSEG
asm mov es,ax
asm mov bx,[postx]
asm shl bx,1
asm mov bp,WORD PTR [wallheight+bx] // fractional height (low 3 bits frac)
asm and bp,0xfff8 // bp = heightscaler*4
asm shr bp,1
asm cmp bp,[maxscaleshl2]
asm jle heightok
asm mov bp,[maxscaleshl2]
heightok:
asm add bp,OFFSET fullscalefarcall
//
// scale a byte wide strip of wall
//
asm mov bx,[postx]
asm mov di,bx
asm shr di,2 // X in bytes
asm add di,[bufferofs]
asm and bx,3
asm shl bx,3 // bx = pixel*8+pixwidth
asm add bx,[postwidth]
asm mov al,BYTE PTR [mapmasks1-1+bx] // -1 because no widths of 0
asm mov dx,SC_INDEX+1
asm out dx,al // set bit mask register
asm lds si,DWORD PTR [postsource]
asm call DWORD PTR [bp] // scale the line of pixels
asm mov al,BYTE PTR [ss:mapmasks2-1+bx] // -1 because no widths of 0
asm or al,al
asm jz nomore
//
// 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 [bp] // scale the line of pixels
asm mov al,BYTE PTR [ss:mapmasks3-1+bx] // -1 because no widths of 0
asm or al,al
asm jz nomore
//
// draw a third byte for vertical strips that cross three bytes
//
asm inc di
asm out dx,al // set bit mask register
asm call DWORD PTR [bp] // scale the line of pixels
nomore:
asm mov ax,ss
asm mov ds,ax
}
void FarScalePost (void) // just so other files can call
{
ScalePost ();
}
/*
====================
=
= HitVertWall
=
= tilehit bit 7 is 0, because it's not a door tile
= if bit 6 is 1 and the adjacent tile is a door tile, use door side pic
=
====================
*/
void HitVertWall (void)
{
int wallpic;
unsigned texture;
texture = (yintercept>>4)&0xfc0;
if (xtilestep == -1)
{
texture = 0xfc0-texture;
xintercept += TILEGLOBAL;
}
wallheight[pixx] = CalcHeight();
if (lastside==1 && lastintercept == xtile && lasttilehit == tilehit)
{
// in the same wall type as last time, so check for optimized draw
if (texture == (unsigned)postsource)
{
// wide scale
postwidth++;
wallheight[pixx] = wallheight[pixx-1];
return;
}
else
{
ScalePost ();
(unsigned)postsource = texture;
postwidth = 1;
postx = pixx;
}
}
else
{
// new wall
if (lastside != -1) // if not the first scaled post
ScalePost ();
lastside = true;
lastintercept = xtile;
lasttilehit = tilehit;
postx = pixx;
postwidth = 1;
if (tilehit & 0x40)
{ // check for adjacent doors
ytile = yintercept>>TILESHIFT;
if ( tilemap[xtile-xtilestep][ytile]&0x80 )
wallpic = DOORWALL+3;
else
wallpic = vertwall[tilehit & ~0x40];
}
else
wallpic = vertwall[tilehit];
*( ((unsigned *)&postsource)+1) = (unsigned)PM_GetPage(wallpic);
(unsigned)postsource = texture;
}
}
/*
====================
=
= HitHorizWall
=
= tilehit bit 7 is 0, because it's not a door tile
= if bit 6 is 1 and the adjacent tile is a door tile, use door side pic
=
====================
*/
void HitHorizWall (void)
{
int wallpic;
unsigned texture;
texture = (xintercept>>4)&0xfc0;
if (ytilestep == -1)
yintercept += TILEGLOBAL;
else
texture = 0xfc0-texture;
wallheight[pixx] = CalcHeight();
if (lastside==0 && lastintercept == ytile && lasttilehit == tilehit)
{
// in the same wall type as last time, so check for optimized draw
if (texture == (unsigned)postsource)
{
// wide scale
postwidth++;
wallheight[pixx] = wallheight[pixx-1];
return;
}
else
{
ScalePost ();
(unsigned)postsource = texture;
postwidth = 1;
postx = pixx;
}
}
else
{
// new wall
if (lastside != -1) // if not the first scaled post
ScalePost ();
lastside = 0;
lastintercept = ytile;
lasttilehit = tilehit;
postx = pixx;
postwidth = 1;
if (tilehit & 0x40)
{ // check for adjacent doors
xtile = xintercept>>TILESHIFT;
if ( tilemap[xtile][ytile-ytilestep]&0x80 )
wallpic = DOORWALL+2;
else
wallpic = horizwall[tilehit & ~0x40];
}
else
wallpic = horizwall[tilehit];
*( ((unsigned *)&postsource)+1) = (unsigned)PM_GetPage(wallpic);
(unsigned)postsource = texture;
}
}
//==========================================================================
/*
====================
=
= HitHorizDoor
=
====================
*/
void HitHorizDoor (void)
{
unsigned texture,doorpage,doornum;
doornum = tilehit&0x7f;
texture = ( (xintercept-doorposition[doornum]) >> 4) &0xfc0;
wallheight[pixx] = CalcHeight();
if (lasttilehit == tilehit)
{
// in the same door as last time, so check for optimized draw
if (texture == (unsigned)postsource)
{
// wide scale
postwidth++;
wallheight[pixx] = wallheight[pixx-1];
return;
}
else
{
ScalePost ();
(unsigned)postsource = texture;
postwidth = 1;
postx = pixx;
}
}
else
{
if (lastside != -1) // if not the first scaled post
ScalePost (); // draw last post
// first pixel in this door
lastside = 2;
lasttilehit = tilehit;
postx = pixx;
postwidth = 1;
switch (doorobjlist[doornum].lock)
{
case dr_normal:
doorpage = DOORWALL;
break;
case dr_lock1:
case dr_lock2:
case dr_lock3:
case dr_lock4:
doorpage = DOORWALL+6;
break;
case dr_elevator:
doorpage = DOORWALL+4;
break;
}
*( ((unsigned *)&postsource)+1) = (unsigned)PM_GetPage(doorpage);
(unsigned)postsource = texture;
}
}
//==========================================================================
/*
====================
=
= HitVertDoor
=
====================
*/
void HitVertDoor (void)
{
unsigned texture,doorpage,doornum;
doornum = tilehit&0x7f;
texture = ( (yintercept-doorposition[doornum]) >> 4) &0xfc0;
wallheight[pixx] = CalcHeight();
if (lasttilehit == tilehit)
{
// in the same door as last time, so check for optimized draw
if (texture == (unsigned)postsource)
{
// wide scale
postwidth++;
wallheight[pixx] = wallheight[pixx-1];
return;
}
else
{
ScalePost ();
(unsigned)postsource = texture;
postwidth = 1;
postx = pixx;
}
}
else
{
if (lastside != -1) // if not the first scaled post
ScalePost (); // draw last post
// first pixel in this door
lastside = 2;
lasttilehit = tilehit;
postx = pixx;
postwidth = 1;
switch (doorobjlist[doornum].lock)
{
case dr_normal:
doorpage = DOORWALL;
break;
case dr_lock1:
case dr_lock2:
case dr_lock3:
case dr_lock4:
doorpage = DOORWALL+6;
break;
case dr_elevator:
doorpage = DOORWALL+4;
break;
}
*( ((unsigned *)&postsource)+1) = (unsigned)PM_GetPage(doorpage+1);
(unsigned)postsource = texture;
}
}