home *** CD-ROM | disk | FTP | other *** search
- /********************************************************************
- 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]++;
- }
- }
-