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 >
C/C++ Source or Header  |  1994-12-23  |  16KB  |  558 lines

  1. /********************************************************************
  2.  FILENAME: VIEW.CPP
  3.  AUTHOR  : JAKE HILL
  4.  DATE    : 12/1/94
  5.  
  6.  Copyright (c) 1994 by Jake Hill:
  7.  If you use any part of this code in your own project, please credit
  8.  me in your documentation and source code.  Thanks.
  9. ********************************************************************/
  10.  
  11. #include "VIEW.HPP"
  12. #include "TRIG.HPP"
  13.  
  14. #include <dos.h>
  15. #include <stdio.h>
  16. #include <stdlib.h>
  17. #include <memory.h>
  18.             
  19. #define SKY_COLOR       1
  20. #define FLOOR_COLOR     2
  21. #define WALL_COLOR      3
  22. #define RED_COLOR       4
  23. #define LEDGE_COLOR     5
  24. #define ZMIN            20L  
  25.  
  26. #define UPPER_TYPE      0
  27. #define WALL_TYPE       1
  28. #define LOWER_TYPE      2
  29.  
  30. // These are some yucky global variables.  They should probably
  31. // be moved into the class's member data.
  32. short ColCount;
  33. short WallCount;
  34. short WallRunCount;
  35. short FirstSSector;
  36.  
  37. // Elements of this array indicate if a screen column is completely drawn.
  38. short     far Col_Done[320];
  39. // Elements of this array hold indexes into the wall_run array.
  40. short     far intersections[50][320];
  41. // The number of wall_runs visible on a particular screen column.
  42. short     far int_count[320];
  43.  
  44. // MaxY & MinY are the active edge lists for the top & bottom of the screen.
  45. short     far MaxY[320];
  46. short     far MinY[320];
  47.  
  48. // This is the wall_run array.  It contains all of the wall_runs which
  49. // are visible in a single frame.
  50. wall_run  far walls[8000];  // 320*50 = 16000
  51.  
  52. // The next two arrays are used for both floors and ceilings.
  53. // Elements of this array hold indexes into the floor_run array.
  54. floor_run far floorlist[200][40];
  55. // The number of floor_runs visible on a particular screen column.
  56. short     far runcount[200];
  57.  
  58. // The offscreen buffer.
  59. char *screenbuf;
  60. // The vga memory on the vga card.
  61. char *vgabuf = (char *) 0xA0000000L;
  62.  
  63.  
  64.  
  65.  
  66. // This function draws a single color column into the
  67. // offscreen buffer.
  68. void ColDraw(short x, short top, short bottom, char color)
  69. {
  70.    if (top < 0) top = 0;
  71.    if (bottom > 200) bottom = 200;
  72.  
  73.    char *pixel = &screenbuf[((top<<6)+(top<<8)) + x];
  74.    for (short y=top; y<bottom; y++)
  75.    {
  76.       *pixel = color;
  77.       pixel += 320;
  78.    }
  79. };
  80.  
  81. // This function draws a single color row into the
  82. // offscreen buffer.
  83. void RowDraw(short y, short left, short right, char color)
  84. {
  85.    if (left < 0) left = 0;
  86.    if (right > 320) right = 320;
  87.  
  88.    char *pixel = &screenbuf[((y<<6)+(y<<8)) + left];
  89.    for (short x=left; x<right; x++)
  90.    {
  91.       *pixel = color;
  92.       pixel++;
  93.    }
  94. };
  95.  
  96. // This is the constructor for the View.
  97. View::View(void)
  98. {
  99.    Seg_Array       = 0;
  100.    Side_Array      = 0;
  101.    Line_Array      = 0;
  102.    Node_Array      = 0;
  103.    PNode_Array     = 0;
  104.    Sector_Array    = 0;
  105.    Vertex_Array    = 0;
  106.    SSector_Array   = 0;
  107.    Blockmap_Array  = 0;
  108.    Blockmap_Header = 0;
  109.  
  110.    screenbuf = new char [64000];
  111. };
  112.  
  113. // The destructor deletes all of the dynamically allocated memory.
  114. View::~View(void)
  115. {
  116.    delete [] Seg_Array;
  117.    delete [] Side_Array;
  118.    delete [] Line_Array;
  119.    delete [] Node_Array;
  120.    delete [] PNode_Array;
  121.    delete [] Sector_Array;
  122.    delete [] Vertex_Array;
  123.    delete [] SSector_Array;
  124.    delete [] Blockmap_Array;
  125.  
  126.    delete [] screenbuf;
  127. };
  128.  
  129. // This is the main drawing function.
  130. void View::DrawView(void)
  131. {
  132. // Initialize housekeeping variable for each frame.
  133.    ColCount = 0;
  134.    WallCount = 0;
  135.    WallRunCount = 0;
  136.    FirstSSector = 1;
  137.  
  138.    for (int i=0; i<320; i++)
  139.    {
  140.       Col_Done[i] = 0;   // No columns have been drawn.
  141.       int_count[i] = 0;  // No walls have been drawn.
  142.       MinY[i] = 0;       // Min y value is 0.
  143.       MaxY[i] = 200;     // Max y value is 200.
  144.    }
  145.  
  146.    for (i=0; i<200; i++) // No floors or ceilings have been drawn.
  147.    {
  148.       runcount[i] = 0;
  149.       floorlist[i][0].end = 0;
  150.       floorlist[i][0].start = 320;
  151.    }
  152.  
  153. // This is the recursive function.
  154. // It can probably be rewritten to use data recursion.
  155.    DrawNode(MaxNode);
  156.  
  157. // Draw all of the wall_runs and floor_runs onto the offscreen buffer.
  158.    DrawSegs();
  159. // Blast the offscreen buffer to display memory.
  160. // I get about 10fps faster when I do this in assy with movsd.
  161.    memcpy(vgabuf, screenbuf, 64000);
  162. };
  163.  
  164.  
  165.  
  166. void View::DrawNode( short node_num )
  167. {
  168. // If this node is a SSECTOR then we need to render it.
  169.    if ( node_num & 0x8000 )
  170.    {
  171.       DrawSSector( node_num & 0x7fff );
  172.       return;
  173.    }
  174. // Once we have rendered 319 columns then we are done,
  175. // so lets stop recursing.
  176.    if (ColCount > 318) return;
  177.  
  178.    if ( !OnRight( node_num ) )
  179.    {
  180.       if ( LeftSideInCone( node_num ) )
  181.          DrawNode( PNode_Array[node_num]->left );
  182.       if ( RightSideInCone( node_num ) )
  183.          DrawNode( PNode_Array[node_num]->right );
  184.    }
  185.    else
  186.    {
  187.       if ( RightSideInCone( node_num ) )
  188.          DrawNode( PNode_Array[node_num]->right );
  189.       if ( LeftSideInCone( node_num ) )
  190.          DrawNode( PNode_Array[node_num]->left );
  191.    }
  192. };
  193.  
  194. // This function obtains data common to all segs in the SSector
  195. // as well as doing the backface elimination via OnRight.
  196. void View::DrawSSector( short SS )
  197. {
  198.    short i;
  199.    short LineSide, Sector;
  200.    short SegCount, FirstSeg;
  201.  
  202. // Load FirstSeg and SegCount locally for speed improvement.
  203.    FirstSeg = SSector_Array[SS].first_seg;
  204.    SegCount = SSector_Array[SS].num_segs;
  205.  
  206.    segment *ThisSeg = &Seg_Array[FirstSeg];
  207.    LineSide = ThisSeg->line_side;
  208.  
  209.    Sector   = Side_Array[
  210.               Line_Array[
  211.               ThisSeg->line ].side[LineSide] ].sector;
  212.  
  213. // Load the floor and ceiling height.
  214.    sector *ThisSector = &Sector_Array[Sector];
  215.    floor_ht   = ThisSector->floor_ht;
  216.    ceiling_ht = ThisSector->ceiling_ht;
  217.  
  218. // Here we determine the viewers height relative to all the walls, etc.
  219.    if ( FirstSSector )
  220.    {
  221.       FirstSSector = 0;
  222.       Ph += floor_ht;
  223.    }
  224.  
  225. // Draw each SEG in the SSECTOR which can be seen.
  226.    for (i=0; i<SegCount; i++)
  227.    {
  228.       if ( OnRight( ThisSeg->from, ThisSeg->to ) )
  229.          LoadSeg( FirstSeg + i );
  230.       ThisSeg++;
  231.    }
  232. };
  233.  
  234.  
  235. // This function is where all of the Rotations and Transformations
  236. // are done.  This is my very first 3D program, so there is probably
  237. // a LOT here which can be optimized for speed.
  238. void View::LoadSeg( short seg )
  239. {
  240.    segment *ThisSeg = &Seg_Array[seg];
  241.    short To = ThisSeg->to;
  242.    short From = ThisSeg->from;
  243.    unsigned short Angle = ThisSeg->angle - Pangle;
  244.  
  245. // Store the World Space Coordinates
  246.    vertex *Vertex = &Vertex_Array[To];
  247.    short Wtx = Vertex->x;
  248.    short Wty = Vertex->y;
  249.    Vertex = &Vertex_Array[From];
  250.    short Wfx = Vertex->x;
  251.    short Wfy = Vertex->y;
  252.  
  253. // Rotate the World Space Coordinates relative to player.
  254.    long Rfz = (((Wfx-Px)*CosPangle) - ((Wfy-Py)*SinPangle)) >> 16;
  255.    long Rtz = (((Wtx-Px)*CosPangle) - ((Wty-Py)*SinPangle)) >> 16;
  256. // If the seg is completely behind the player, exit the fn.
  257.    if ((Rfz<ZMIN)&&(Rtz<ZMIN)) return;
  258.  
  259. // Finish rotating the coordinates.
  260.    long Rfx = (((Wfx-Px)*SinPangle) + ((Wfy-Py)*CosPangle)) >> 16;
  261.    long Rtx = (((Wtx-Px)*SinPangle) + ((Wty-Py)*CosPangle)) >> 16;
  262.  
  263. // Perform Z-clipping if necessary.
  264.    long TanAngle = tangent(Angle);
  265.  
  266.    if (Rfx > Rfz)       // Clip Rfx, Rfz to line x = z.
  267.    {
  268.       if (TanAngle == 65536L) return; // Prevent a divide by zero.
  269.  
  270.       long XZ = ((Rfx<<16) - (Rfz*TanAngle)) / (65536L-TanAngle);
  271.       Rfx = Rfz = XZ;
  272.    }
  273.    if (Rfz < ZMIN)       // Clip Rfx, Rfz to zmin.
  274.    {
  275.       Rfx = Rfx + (((ZMIN-Rfz)*TanAngle) >> 16);
  276.       Rfz = ZMIN;
  277.    }
  278.    if (Rtz < ZMIN)      // Clip Rtx, Rtz to zmin.
  279.    {
  280.       Rtx = Rtx + (((ZMIN-Rtz)*TanAngle) >> 16);
  281.       Rtz = ZMIN;
  282.    }
  283.  
  284.    if (Rfz > 9999L) Rfz = 9999L;  // We don't want to go out of
  285.    if (Rtz > 9999L) Rtz = 9999L;  // bounds with these values.
  286.  
  287. // Project the World Space -X- Coordinates to screen space coordinates.
  288. // MAKE SURE that Z-Clipping is done before this or we may get a
  289. // negative value for Rfz or Rtz - this would be out of bounds.
  290.  
  291.    short x1, x2, sx1, sx2;
  292.    sx1 = x1 = (short) (160L - ((Rfx*invdistance(Rfz))>>16));
  293.    sx2 = x2 = (short) (160L - ((Rtx*invdistance(Rtz))>>16));
  294.  
  295. // Check if wall segment is on screen or is wide enough to see.
  296.    if ( sx2 <= 0 ) return;
  297.    if ( sx1 > 319 ) return;
  298.    if ( sx1 == sx2 ) return;
  299.  
  300. // Check if wall segment is completely occluded.
  301.    if ( x1 < 0 ) x1 = 0;
  302.    if ( x2 > 320 ) x2 = 320;
  303.  
  304.    char ExitNow = 1;
  305.    for (int x=x1; x<x2; x++)
  306.       if ( Col_Done[x] == 0 )
  307.       {
  308.          ExitNow = 0;
  309.          break;
  310.       }
  311.    if ( ExitNow ) return;
  312.  
  313.  
  314. //**************************************************************
  315. // Calculate the screen coordinates of the wall segment.
  316. //**************************************************************
  317.    short Rt,Rb;
  318.    short SingleSided = !(Line_Array[ ThisSeg->line ].flags & 0x0004);
  319.  
  320.    short Side = ThisSeg->line_side;
  321.    line *ThisLine = &Line_Array[ ThisSeg->line ];
  322.    side *ThisSide = &Side_Array[ ThisLine->side[Side] ];
  323.    Wall.opaque    = 0;
  324.  
  325. // If there is a main_tx then there will not be an upper
  326. // or lower, so we can exit when done with this.
  327.    if ( ThisSide->main_tx[0] != '-' )
  328.    {
  329.       Rb = Ph - floor_ht;
  330.       Rt = Ph - ceiling_ht;
  331.  
  332.       Wall.type   = WALL_TYPE;
  333.       AddWall(sx1, sx2, Rb, Rt, (short)Rfz, (short)Rtz);
  334.  
  335.       if ( SingleSided )
  336.       {
  337.          Wall.opaque = 1;
  338.          for (int x=x1; x<x2; x++)
  339.             if ( Col_Done[x] == 0 )
  340.             {
  341.                ColCount++;
  342.                Col_Done[x] = 1;
  343.             }
  344.       }
  345.       return;
  346.    }
  347.  
  348. // If there is not a main_tx then there will be both an
  349. // upper_tx, and a lower_tx.  One or both may have a height of zero.
  350.  
  351.    side *ThatSide = &Side_Array[ ThisLine->side[!Side] ];
  352.  
  353.    if ( ThisSide->lower_tx[0] != '-' )
  354.       Rt = Ph - Sector_Array[ ThatSide->sector ].floor_ht;
  355.    else
  356.       Rt = Ph - floor_ht;
  357.  
  358.    Rb = Ph - floor_ht;
  359.    Wall.type   = LOWER_TYPE;
  360.    AddWall(sx1, sx2, Rb, Rt, (short)Rfz, (short)Rtz);
  361.  
  362.    if ( ThisSide->upper_tx[0] != '-' )
  363.       Rb = Ph - Sector_Array[ ThatSide->sector ].ceiling_ht;
  364.    else
  365.       Rb = Ph - ceiling_ht;
  366.  
  367.    Rt = Ph - ceiling_ht;
  368.    Wall.type   = UPPER_TYPE;
  369.    AddWall(sx1, sx2, Rb, Rt, (short)Rfz, (short)Rtz);
  370. };
  371.  
  372. // This function adds the wall_runs and floor_runs to the
  373. // lists so that they may be drawn to the screen.  It also does
  374. // the perspective calculations on the wall heights.
  375.  
  376. void View::AddWall(short sx1, short sx2, short Rb, short Rt, short Rfz, short Rtz)
  377. {
  378. // Project to determine the four y screen coordinates.
  379.    long sy1 = 100L + ((Rb*invdistance(Rfz))>>16); // bottom left.
  380.    long sy2 = 100L + ((Rb*invdistance(Rtz))>>16); // bottom right.
  381.    long sy3 = 100L + ((Rt*invdistance(Rtz))>>16); // top right.
  382.    long sy4 = 100L + ((Rt*invdistance(Rfz))>>16); // top left.
  383.  
  384.    Wall.y1  = sy4 << 16;
  385.    Wall.y2  = sy1 << 16;
  386.    Wall.dy1 = ((sy3-sy4) << 16) / (sx2-sx1);
  387.    Wall.dy2 = ((sy2-sy1) << 16) / (sx2-sx1);
  388.  
  389.    if ( sx1 < 0 )
  390.    {
  391.       Wall.y1 -= sx1 * Wall.dy1;
  392.       Wall.y2 -= sx1 * Wall.dy2;
  393.       sx1 = 0;
  394.    }
  395.    if ( sx2 > 320 ) sx2 = 320;
  396.  
  397.    short last_maxy = MaxY[sx1];
  398.    short last_bottom = MaxY[sx1];
  399.    short top, bottom, miny, maxy;
  400.    short last_top = MinY[sx1];
  401.    short last_miny = MinY[sx1];
  402.  
  403.    for (short x=sx1; x<sx2; x++)
  404.    {
  405.       miny   = MinY[x];
  406.       maxy   = MaxY[x];
  407.       top    = (short) (Wall.y1 >> 16);
  408.       bottom = (short) (Wall.y2 >> 16);
  409.  
  410.       if ((Wall.type==WALL_TYPE)||(Wall.type==LOWER_TYPE))
  411.       if (bottom < maxy)
  412.       {
  413.          if ( bottom < last_bottom )
  414.             AddFloorUp(last_bottom, bottom, x);
  415.          if ( maxy > last_maxy )
  416.             AddFloorDown(last_maxy, maxy, x);
  417.  
  418.          if ( bottom > last_bottom )
  419.             EndFloorDown(last_bottom, bottom, x);
  420.          if ( maxy < last_maxy )
  421.             EndFloorUp(last_maxy, maxy, x);
  422.       }
  423.       else if ( last_bottom < last_maxy )
  424.               EndFloorUp(last_maxy, last_bottom, x);
  425.  
  426.       if ((Wall.type==WALL_TYPE)||(Wall.type==UPPER_TYPE))
  427.       if (top > miny)
  428.       {
  429.          if ( top > last_top )
  430.             AddFloorDown(last_top, top, x);
  431.          if ( miny < last_miny )
  432.             AddFloorUp(last_miny, miny, x);
  433.  
  434.          if ( top < last_top )
  435.             EndFloorUp(last_top, top, x);
  436.          if ( miny > last_miny )
  437.             EndFloorDown(last_miny, miny, x);
  438.       }
  439.       else if ( last_top > last_miny )
  440.               EndFloorDown(last_miny, last_top, x);
  441.  
  442.       last_top    = top;
  443.       last_miny   = miny;
  444.       last_maxy   = maxy;
  445.       last_bottom = bottom;
  446.  
  447.       if (miny < maxy)
  448.       if ((top<maxy)&&(bottom>miny))
  449.       {
  450.          if (top < miny) top = miny;
  451.          if (bottom > maxy) bottom = maxy;
  452.  
  453.          if (top < bottom)
  454.          {
  455.             walls[WallRunCount].top = top;
  456.             walls[WallRunCount].bottom = bottom;
  457.             intersections[ int_count[x] ][x] = WallRunCount;
  458.             int_count[x]++;
  459.          }
  460.       }
  461.  
  462.       if ( Wall.type == UPPER_TYPE )
  463.       {
  464.          walls[WallRunCount].tex_num = LEDGE_COLOR;
  465.          if (bottom>miny) MinY[x] = bottom;
  466.       }
  467.       else if ( Wall.type == LOWER_TYPE )
  468.       {
  469.          walls[WallRunCount].tex_num = LEDGE_COLOR;
  470.          if (top<maxy) MaxY[x] = top;
  471.       }
  472.       else if ( Wall.type == WALL_TYPE )
  473.       {
  474.          walls[WallRunCount].tex_num = WALL_COLOR;
  475.          if (bottom>miny) MinY[x] = bottom;
  476.          if (top<maxy) MaxY[x] = top;
  477.       }
  478.  
  479.       WallRunCount++;
  480.       Wall.y1 += Wall.dy1;
  481.       Wall.y2 += Wall.dy2;
  482.    }
  483.  
  484.    if (( Wall.type == WALL_TYPE )||( Wall.type == LOWER_TYPE ))
  485.    if ( last_bottom < last_maxy )
  486.       EndFloorUp(last_maxy, last_bottom, x);
  487.  
  488.    if (( Wall.type == WALL_TYPE )||( Wall.type == UPPER_TYPE ))
  489.    if ( last_top > last_miny )
  490.       EndFloorDown(last_miny, last_top, x);
  491. };
  492.  
  493. // Here is where we blast all of the runs to the screen buffer.
  494. void View::DrawSegs(void)
  495. {
  496.    short row;
  497.  
  498.    for (row=0; row<100; row++)
  499.       for (short run=0; run<runcount[row]; run++)
  500.          RowDraw(row,
  501.                  floorlist[row][run].start,
  502.                  floorlist[row][run].end,SKY_COLOR);
  503.  
  504.    for (row=100; row<200; row++)
  505.       for (short run=0; run<runcount[row]; run++)
  506.          RowDraw(row,
  507.                  floorlist[row][run].start,
  508.                  floorlist[row][run].end,FLOOR_COLOR);
  509.  
  510.    for (short x=0; x<320; x++)
  511.       for (short i=0; i<int_count[x]; i++)
  512.       {
  513.          short Wall = intersections[i][x];
  514.          wall_run *ThisWallRun = &walls[Wall];
  515.          ColDraw(x, ThisWallRun->top,
  516.                     ThisWallRun->bottom,
  517.                     (char) ThisWallRun->tex_num);
  518.       }
  519. };
  520.  
  521. // lb > b
  522. void View::AddFloorUp(short lb, short b, short start)
  523. {
  524.    for (short row=b; row<lb; row++)
  525.       if ( (row>=0) && (row<200))
  526.          floorlist[row][ runcount[row] ].start = start;
  527. }
  528.  
  529. // b > lb
  530. void View::AddFloorDown(short lb, short b, short start)
  531. {
  532.    for (short row=lb; row<b; row++)
  533.       if ( (row>=0) && (row<200))
  534.          floorlist[row][ runcount[row] ].start = start;
  535. }
  536.  
  537. // b < lb
  538. void View::EndFloorUp(short lb, short b, short end)
  539. {
  540.    for (short row=b; row<lb; row++)
  541.       if ( (row>=0) && (row<200))
  542.       {
  543.          floorlist[row][ runcount[row] ].end = end;
  544.          runcount[row]++;
  545.       }
  546. }
  547.  
  548. // b > lb
  549. void View::EndFloorDown(short lb, short b, short end)
  550. {
  551.    for (short row=lb; row<b; row++)
  552.       if ( (row>=0) && (row<200))
  553.       {
  554.          floorlist[row][ runcount[row] ].end = end;
  555.          runcount[row]++;
  556.       }
  557. }
  558.