home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Fujiology Archive
/
fujiology_archive_v1_0.iso
/
!FALCON
/
!BONUS
/
GAMES
/
ENGINES
/
BM214A.ZIP
/
BM214A
/
ORIGINAL.SRC
/
VIEW.CPP
< prev
next >
Wrap
C/C++ Source or Header
|
1994-12-23
|
16KB
|
558 lines
/********************************************************************
FILENAME: VIEW.CPP
AUTHOR : JAKE HILL
DATE : 12/1/94
Copyright (c) 1994 by Jake Hill:
If you use any part of this code in your own project, please credit
me in your documentation and source code. Thanks.
********************************************************************/
#include "VIEW.HPP"
#include "TRIG.HPP"
#include <dos.h>
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#define SKY_COLOR 1
#define FLOOR_COLOR 2
#define WALL_COLOR 3
#define RED_COLOR 4
#define LEDGE_COLOR 5
#define ZMIN 20L
#define UPPER_TYPE 0
#define WALL_TYPE 1
#define LOWER_TYPE 2
// These are some yucky global variables. They should probably
// be moved into the class's member data.
short ColCount;
short WallCount;
short WallRunCount;
short FirstSSector;
// Elements of this array indicate if a screen column is completely drawn.
short far Col_Done[320];
// Elements of this array hold indexes into the wall_run array.
short far intersections[50][320];
// The number of wall_runs visible on a particular screen column.
short far int_count[320];
// MaxY & MinY are the active edge lists for the top & bottom of the screen.
short far MaxY[320];
short far MinY[320];
// This is the wall_run array. It contains all of the wall_runs which
// are visible in a single frame.
wall_run far walls[8000]; // 320*50 = 16000
// The next two arrays are used for both floors and ceilings.
// Elements of this array hold indexes into the floor_run array.
floor_run far floorlist[200][40];
// The number of floor_runs visible on a particular screen column.
short far runcount[200];
// The offscreen buffer.
char *screenbuf;
// The vga memory on the vga card.
char *vgabuf = (char *) 0xA0000000L;
// This function draws a single color column into the
// offscreen buffer.
void ColDraw(short x, short top, short bottom, char color)
{
if (top < 0) top = 0;
if (bottom > 200) bottom = 200;
char *pixel = &screenbuf[((top<<6)+(top<<8)) + x];
for (short y=top; y<bottom; y++)
{
*pixel = color;
pixel += 320;
}
};
// This function draws a single color row into the
// offscreen buffer.
void RowDraw(short y, short left, short right, char color)
{
if (left < 0) left = 0;
if (right > 320) right = 320;
char *pixel = &screenbuf[((y<<6)+(y<<8)) + left];
for (short x=left; x<right; x++)
{
*pixel = color;
pixel++;
}
};
// This is the constructor for the View.
View::View(void)
{
Seg_Array = 0;
Side_Array = 0;
Line_Array = 0;
Node_Array = 0;
PNode_Array = 0;
Sector_Array = 0;
Vertex_Array = 0;
SSector_Array = 0;
Blockmap_Array = 0;
Blockmap_Header = 0;
screenbuf = new char [64000];
};
// The destructor deletes all of the dynamically allocated memory.
View::~View(void)
{
delete [] Seg_Array;
delete [] Side_Array;
delete [] Line_Array;
delete [] Node_Array;
delete [] PNode_Array;
delete [] Sector_Array;
delete [] Vertex_Array;
delete [] SSector_Array;
delete [] Blockmap_Array;
delete [] screenbuf;
};
// This is the main drawing function.
void View::DrawView(void)
{
// Initialize housekeeping variable for each frame.
ColCount = 0;
WallCount = 0;
WallRunCount = 0;
FirstSSector = 1;
for (int i=0; i<320; i++)
{
Col_Done[i] = 0; // No columns have been drawn.
int_count[i] = 0; // No walls have been drawn.
MinY[i] = 0; // Min y value is 0.
MaxY[i] = 200; // Max y value is 200.
}
for (i=0; i<200; i++) // No floors or ceilings have been drawn.
{
runcount[i] = 0;
floorlist[i][0].end = 0;
floorlist[i][0].start = 320;
}
// This is the recursive function.
// It can probably be rewritten to use data recursion.
DrawNode(MaxNode);
// Draw all of the wall_runs and floor_runs onto the offscreen buffer.
DrawSegs();
// Blast the offscreen buffer to display memory.
// I get about 10fps faster when I do this in assy with movsd.
memcpy(vgabuf, screenbuf, 64000);
};
void View::DrawNode( short node_num )
{
// If this node is a SSECTOR then we need to render it.
if ( node_num & 0x8000 )
{
DrawSSector( node_num & 0x7fff );
return;
}
// Once we have rendered 319 columns then we are done,
// so lets stop recursing.
if (ColCount > 318) return;
if ( !OnRight( node_num ) )
{
if ( LeftSideInCone( node_num ) )
DrawNode( PNode_Array[node_num]->left );
if ( RightSideInCone( node_num ) )
DrawNode( PNode_Array[node_num]->right );
}
else
{
if ( RightSideInCone( node_num ) )
DrawNode( PNode_Array[node_num]->right );
if ( LeftSideInCone( node_num ) )
DrawNode( PNode_Array[node_num]->left );
}
};
// This function obtains data common to all segs in the SSector
// as well as doing the backface elimination via OnRight.
void View::DrawSSector( short SS )
{
short i;
short LineSide, Sector;
short SegCount, FirstSeg;
// Load FirstSeg and SegCount locally for speed improvement.
FirstSeg = SSector_Array[SS].first_seg;
SegCount = SSector_Array[SS].num_segs;
segment *ThisSeg = &Seg_Array[FirstSeg];
LineSide = ThisSeg->line_side;
Sector = Side_Array[
Line_Array[
ThisSeg->line ].side[LineSide] ].sector;
// Load the floor and ceiling height.
sector *ThisSector = &Sector_Array[Sector];
floor_ht = ThisSector->floor_ht;
ceiling_ht = ThisSector->ceiling_ht;
// Here we determine the viewers height relative to all the walls, etc.
if ( FirstSSector )
{
FirstSSector = 0;
Ph += floor_ht;
}
// Draw each SEG in the SSECTOR which can be seen.
for (i=0; i<SegCount; i++)
{
if ( OnRight( ThisSeg->from, ThisSeg->to ) )
LoadSeg( FirstSeg + i );
ThisSeg++;
}
};
// This function is where all of the Rotations and Transformations
// are done. This is my very first 3D program, so there is probably
// a LOT here which can be optimized for speed.
void View::LoadSeg( short seg )
{
segment *ThisSeg = &Seg_Array[seg];
short To = ThisSeg->to;
short From = ThisSeg->from;
unsigned short Angle = ThisSeg->angle - Pangle;
// Store the World Space Coordinates
vertex *Vertex = &Vertex_Array[To];
short Wtx = Vertex->x;
short Wty = Vertex->y;
Vertex = &Vertex_Array[From];
short Wfx = Vertex->x;
short Wfy = Vertex->y;
// Rotate the World Space Coordinates relative to player.
long Rfz = (((Wfx-Px)*CosPangle) - ((Wfy-Py)*SinPangle)) >> 16;
long Rtz = (((Wtx-Px)*CosPangle) - ((Wty-Py)*SinPangle)) >> 16;
// If the seg is completely behind the player, exit the fn.
if ((Rfz<ZMIN)&&(Rtz<ZMIN)) return;
// Finish rotating the coordinates.
long Rfx = (((Wfx-Px)*SinPangle) + ((Wfy-Py)*CosPangle)) >> 16;
long Rtx = (((Wtx-Px)*SinPangle) + ((Wty-Py)*CosPangle)) >> 16;
// Perform Z-clipping if necessary.
long TanAngle = tangent(Angle);
if (Rfx > Rfz) // Clip Rfx, Rfz to line x = z.
{
if (TanAngle == 65536L) return; // Prevent a divide by zero.
long XZ = ((Rfx<<16) - (Rfz*TanAngle)) / (65536L-TanAngle);
Rfx = Rfz = XZ;
}
if (Rfz < ZMIN) // Clip Rfx, Rfz to zmin.
{
Rfx = Rfx + (((ZMIN-Rfz)*TanAngle) >> 16);
Rfz = ZMIN;
}
if (Rtz < ZMIN) // Clip Rtx, Rtz to zmin.
{
Rtx = Rtx + (((ZMIN-Rtz)*TanAngle) >> 16);
Rtz = ZMIN;
}
if (Rfz > 9999L) Rfz = 9999L; // We don't want to go out of
if (Rtz > 9999L) Rtz = 9999L; // bounds with these values.
// Project the World Space -X- Coordinates to screen space coordinates.
// MAKE SURE that Z-Clipping is done before this or we may get a
// negative value for Rfz or Rtz - this would be out of bounds.
short x1, x2, sx1, sx2;
sx1 = x1 = (short) (160L - ((Rfx*invdistance(Rfz))>>16));
sx2 = x2 = (short) (160L - ((Rtx*invdistance(Rtz))>>16));
// Check if wall segment is on screen or is wide enough to see.
if ( sx2 <= 0 ) return;
if ( sx1 > 319 ) return;
if ( sx1 == sx2 ) return;
// Check if wall segment is completely occluded.
if ( x1 < 0 ) x1 = 0;
if ( x2 > 320 ) x2 = 320;
char ExitNow = 1;
for (int x=x1; x<x2; x++)
if ( Col_Done[x] == 0 )
{
ExitNow = 0;
break;
}
if ( ExitNow ) return;
//**************************************************************
// Calculate the screen coordinates of the wall segment.
//**************************************************************
short Rt,Rb;
short SingleSided = !(Line_Array[ ThisSeg->line ].flags & 0x0004);
short Side = ThisSeg->line_side;
line *ThisLine = &Line_Array[ ThisSeg->line ];
side *ThisSide = &Side_Array[ ThisLine->side[Side] ];
Wall.opaque = 0;
// If there is a main_tx then there will not be an upper
// or lower, so we can exit when done with this.
if ( ThisSide->main_tx[0] != '-' )
{
Rb = Ph - floor_ht;
Rt = Ph - ceiling_ht;
Wall.type = WALL_TYPE;
AddWall(sx1, sx2, Rb, Rt, (short)Rfz, (short)Rtz);
if ( SingleSided )
{
Wall.opaque = 1;
for (int x=x1; x<x2; x++)
if ( Col_Done[x] == 0 )
{
ColCount++;
Col_Done[x] = 1;
}
}
return;
}
// If there is not a main_tx then there will be both an
// upper_tx, and a lower_tx. One or both may have a height of zero.
side *ThatSide = &Side_Array[ ThisLine->side[!Side] ];
if ( ThisSide->lower_tx[0] != '-' )
Rt = Ph - Sector_Array[ ThatSide->sector ].floor_ht;
else
Rt = Ph - floor_ht;
Rb = Ph - floor_ht;
Wall.type = LOWER_TYPE;
AddWall(sx1, sx2, Rb, Rt, (short)Rfz, (short)Rtz);
if ( ThisSide->upper_tx[0] != '-' )
Rb = Ph - Sector_Array[ ThatSide->sector ].ceiling_ht;
else
Rb = Ph - ceiling_ht;
Rt = Ph - ceiling_ht;
Wall.type = UPPER_TYPE;
AddWall(sx1, sx2, Rb, Rt, (short)Rfz, (short)Rtz);
};
// This function adds the wall_runs and floor_runs to the
// lists so that they may be drawn to the screen. It also does
// the perspective calculations on the wall heights.
void View::AddWall(short sx1, short sx2, short Rb, short Rt, short Rfz, short Rtz)
{
// Project to determine the four y screen coordinates.
long sy1 = 100L + ((Rb*invdistance(Rfz))>>16); // bottom left.
long sy2 = 100L + ((Rb*invdistance(Rtz))>>16); // bottom right.
long sy3 = 100L + ((Rt*invdistance(Rtz))>>16); // top right.
long sy4 = 100L + ((Rt*invdistance(Rfz))>>16); // top left.
Wall.y1 = sy4 << 16;
Wall.y2 = sy1 << 16;
Wall.dy1 = ((sy3-sy4) << 16) / (sx2-sx1);
Wall.dy2 = ((sy2-sy1) << 16) / (sx2-sx1);
if ( sx1 < 0 )
{
Wall.y1 -= sx1 * Wall.dy1;
Wall.y2 -= sx1 * Wall.dy2;
sx1 = 0;
}
if ( sx2 > 320 ) sx2 = 320;
short last_maxy = MaxY[sx1];
short last_bottom = MaxY[sx1];
short top, bottom, miny, maxy;
short last_top = MinY[sx1];
short last_miny = MinY[sx1];
for (short x=sx1; x<sx2; x++)
{
miny = MinY[x];
maxy = MaxY[x];
top = (short) (Wall.y1 >> 16);
bottom = (short) (Wall.y2 >> 16);
if ((Wall.type==WALL_TYPE)||(Wall.type==LOWER_TYPE))
if (bottom < maxy)
{
if ( bottom < last_bottom )
AddFloorUp(last_bottom, bottom, x);
if ( maxy > last_maxy )
AddFloorDown(last_maxy, maxy, x);
if ( bottom > last_bottom )
EndFloorDown(last_bottom, bottom, x);
if ( maxy < last_maxy )
EndFloorUp(last_maxy, maxy, x);
}
else if ( last_bottom < last_maxy )
EndFloorUp(last_maxy, last_bottom, x);
if ((Wall.type==WALL_TYPE)||(Wall.type==UPPER_TYPE))
if (top > miny)
{
if ( top > last_top )
AddFloorDown(last_top, top, x);
if ( miny < last_miny )
AddFloorUp(last_miny, miny, x);
if ( top < last_top )
EndFloorUp(last_top, top, x);
if ( miny > last_miny )
EndFloorDown(last_miny, miny, x);
}
else if ( last_top > last_miny )
EndFloorDown(last_miny, last_top, x);
last_top = top;
last_miny = miny;
last_maxy = maxy;
last_bottom = bottom;
if (miny < maxy)
if ((top<maxy)&&(bottom>miny))
{
if (top < miny) top = miny;
if (bottom > maxy) bottom = maxy;
if (top < bottom)
{
walls[WallRunCount].top = top;
walls[WallRunCount].bottom = bottom;
intersections[ int_count[x] ][x] = WallRunCount;
int_count[x]++;
}
}
if ( Wall.type == UPPER_TYPE )
{
walls[WallRunCount].tex_num = LEDGE_COLOR;
if (bottom>miny) MinY[x] = bottom;
}
else if ( Wall.type == LOWER_TYPE )
{
walls[WallRunCount].tex_num = LEDGE_COLOR;
if (top<maxy) MaxY[x] = top;
}
else if ( Wall.type == WALL_TYPE )
{
walls[WallRunCount].tex_num = WALL_COLOR;
if (bottom>miny) MinY[x] = bottom;
if (top<maxy) MaxY[x] = top;
}
WallRunCount++;
Wall.y1 += Wall.dy1;
Wall.y2 += Wall.dy2;
}
if (( Wall.type == WALL_TYPE )||( Wall.type == LOWER_TYPE ))
if ( last_bottom < last_maxy )
EndFloorUp(last_maxy, last_bottom, x);
if (( Wall.type == WALL_TYPE )||( Wall.type == UPPER_TYPE ))
if ( last_top > last_miny )
EndFloorDown(last_miny, last_top, x);
};
// Here is where we blast all of the runs to the screen buffer.
void View::DrawSegs(void)
{
short row;
for (row=0; row<100; row++)
for (short run=0; run<runcount[row]; run++)
RowDraw(row,
floorlist[row][run].start,
floorlist[row][run].end,SKY_COLOR);
for (row=100; row<200; row++)
for (short run=0; run<runcount[row]; run++)
RowDraw(row,
floorlist[row][run].start,
floorlist[row][run].end,FLOOR_COLOR);
for (short x=0; x<320; x++)
for (short i=0; i<int_count[x]; i++)
{
short Wall = intersections[i][x];
wall_run *ThisWallRun = &walls[Wall];
ColDraw(x, ThisWallRun->top,
ThisWallRun->bottom,
(char) ThisWallRun->tex_num);
}
};
// lb > b
void View::AddFloorUp(short lb, short b, short start)
{
for (short row=b; row<lb; row++)
if ( (row>=0) && (row<200))
floorlist[row][ runcount[row] ].start = start;
}
// b > lb
void View::AddFloorDown(short lb, short b, short start)
{
for (short row=lb; row<b; row++)
if ( (row>=0) && (row<200))
floorlist[row][ runcount[row] ].start = start;
}
// b < lb
void View::EndFloorUp(short lb, short b, short end)
{
for (short row=b; row<lb; row++)
if ( (row>=0) && (row<200))
{
floorlist[row][ runcount[row] ].end = end;
runcount[row]++;
}
}
// b > lb
void View::EndFloorDown(short lb, short b, short end)
{
for (short row=lb; row<b; row++)
if ( (row>=0) && (row<200))
{
floorlist[row][ runcount[row] ].end = end;
runcount[row]++;
}
}