home *** CD-ROM | disk | FTP | other *** search
/ Cutting-Edge 3D Game Programming with C++ / CE3DC++.ISO / BOOK / CHAP08 / PANEL3D.CPP < prev    next >
Encoding:
C/C++ Source or Header  |  1996-05-16  |  16.9 KB  |  546 lines

  1. //
  2. // File name: Panel3D.CPP
  3. //
  4. // Description: The support files for the Panel3D.HPP header
  5. //
  6. // Author: John De Goes
  7. //
  8. // Project: Cutting SPoint 3D Game Programming
  9. //
  10.  
  11. #include <Math.H>
  12. #include <Stdlib.H>
  13. #include <String.H>
  14.  
  15. #include "Panel3D.HPP"
  16. #include "LineType.HPP"
  17. #include "FixASM.HPP"
  18. #include "3DCLass.HPP"
  19. #include "LightP.HPP"
  20.  
  21. #define COLOR_RANGE 255
  22. #define COLOR_START 1
  23.  
  24. void inline ClipHLine ( long &X1, long &X2, long &Z, long ZStep )
  25.    {
  26.    // Clip a horizontal "Z-buffered" line:
  27.    if ( X1 < MINX )
  28.       {
  29.       // Take advantage of the fact that ( a * ( b * f ) / f ) 
  30.       // is equal to ( a * b );
  31.       Z += ZStep * ( MINX - X1 );
  32.       X1 = MINX;
  33.       }
  34.    else if ( X1 > MAXX )
  35.            X1 = MAXX;
  36.    if ( X2 < MINX )
  37.       X2 = MINX;
  38.    else if ( X2 > MAXX )
  39.            X2 = MAXX;
  40.    }
  41.  
  42. void Panel3D::CalcRadius ()
  43.    {
  44.    // Calculate the radius of the panel:
  45.  
  46.    // Initialize/Declare variables:
  47.    Point3D TempPoint [ 4 ], Center;
  48.    unsigned int Count;
  49.    double Distance [ 4 ], Dist;
  50.  
  51.    // Create a temporary vertex list:
  52.    for ( Count = 0; Count < 4; Count++ )
  53.        {
  54.        TempPoint [ Count ] = *VPoint[ Count ];
  55.        }
  56.  
  57.    // Calculate center of polygon:
  58.    for ( Count = 0; Count < 4; Count++ )
  59.        {
  60.        Center += TempPoint [ Count ];
  61.        }
  62.    Center /= 4.0F;
  63.  
  64.    // Translate polygon to it's center:
  65.    for ( Count = 0; Count < 4; Count++ )
  66.        {
  67.        TempPoint [ Count ] -= Center;
  68.        }
  69.  
  70.    // Calculate the distance to each of the vertices:
  71.    for ( Count = 0; Count < 4; Count++ )
  72.        {
  73.        Dist = TempPoint [ Count ].Mag ();
  74.        Distance [ Count ] = Dist;
  75.        }
  76.  
  77.    // Determine the maximum distance:
  78.    Dist = Distance [ 0 ];
  79.    for ( Count = 1; Count < 4; Count++ )
  80.        {
  81.        if ( Distance [ Count ] > Dist )
  82.           Dist = Distance [ Count ];
  83.        }
  84.    // Dist holds the maximum radius of the panel:
  85.    Radius = Dist;
  86.    }
  87.  
  88. void Panel3D::CalcInten ()
  89.    {
  90.    double Mag = ( sqrt ( Light.X * Light.X +
  91.                          Light.Y * Light.Y +
  92.                          Light.Z * Light.Z ) );
  93.    // Assign a color based on normal:
  94.    double CosA = ( ( Normal.X - VPoint [ 0 ]->Lx ) * Light.X +
  95.                    ( Normal.Y - VPoint [ 0 ]->Ly ) * Light.Y +
  96.                    ( Normal.Z - VPoint [ 0 ]->Lz ) * Light.Z ) / Mag;
  97.    Color = ( CosA * ( double ) COLOR_RANGE + COLOR_START );
  98.    }
  99.  
  100. void Panel3D::Project ()
  101.    {
  102.    // Perform front Z-clipping and project the panel's 3-dimensional points 
  103.    // onto the screen:
  104.    SPCount = 4;
  105.    unsigned int Count, OutCount = 0, StartI, EndI;
  106.    double OneOverZ;
  107.    Point3D ZClipPoint [ 5 ]; // Maximum of 5 clipped Z points
  108.  
  109.    // Initialize pointer to last vertex:
  110.    StartI = SPCount - 1;
  111.  
  112.    // Loop through all edges of panel using S&H algorithm:
  113.    for ( EndI = 0; EndI < SPCount; EndI++ )
  114.        {
  115.        if ( VPoint [ StartI ]->Wz >= MINZ )
  116.           {
  117.           if ( VPoint [ EndI ]->Wz >= MINZ )
  118.              {
  119.              // Entirely inside front view volume (case 1) - output unchanged 
  120.              // vertex:
  121.              ZClipPoint [ OutCount ].Wx = VPoint [ EndI ]->Wx;
  122.              ZClipPoint [ OutCount ].Wy = VPoint [ EndI ]->Wy;
  123.              ZClipPoint [ OutCount ].Wz = VPoint [ EndI ]->Wz;
  124.  
  125.              ++OutCount;
  126.              }
  127.           else {
  128.                // SPoint is leaving view volume (case 2) -
  129.                // clip using parametric form of line:
  130.                double DeltaZ = ( VPoint [ EndI ]->Wz - 
  131.                                  VPoint [ StartI ]->Wz );
  132.  
  133.                double t = ( double )( MINZ - VPoint [ StartI ]->Wz ) / 
  134.                                     DeltaZ;
  135.                ZClipPoint [ OutCount ].Wx = VPoint [ StartI ]->Wx + ( double )
  136.                                       ( VPoint [ EndI ]->Wx - 
  137.                                         VPoint [ StartI ]->Wx )
  138.                                       * t;
  139.                ZClipPoint [ OutCount ].Wy = VPoint [ StartI ]->Wy + ( double )
  140.                                       ( VPoint [ EndI ]->Wy - 
  141.                                         VPoint [ StartI ]->Wy )
  142.                                       * t;
  143.                ZClipPoint [ OutCount ].Wz = MINZ;
  144.  
  145.                // Update index:
  146.                ++OutCount;
  147.                }
  148.           }   
  149.        else {
  150.             if ( VPoint [ EndI ]->Wz >= MINZ ) 
  151.                {
  152.                // SPoint is entering view volume (case 3) - clip
  153.                // using parametric form of line:
  154.                double DeltaZ = ( VPoint [ EndI ]->Wz - 
  155.                                  VPoint [ StartI ]->Wz );
  156.  
  157.                double t = ( double )( MINZ - VPoint [ StartI ]->Wz ) / 
  158.                                     DeltaZ;
  159.                ZClipPoint [ OutCount ].Wx = VPoint [ StartI ]->Wx + ( double )
  160.                                       ( VPoint [ EndI ]->Wx - 
  161.                                         VPoint [ StartI ]->Wx )
  162.                                       * t;
  163.             
  164.                ZClipPoint [ OutCount ].Wy = VPoint [ StartI ]->Wy + ( double )
  165.                                       ( VPoint [ EndI ]->Wy - 
  166.                                         VPoint [ StartI ]->Wy )
  167.                                       * t;
  168.                                           
  169.                ZClipPoint [ OutCount ].Wz =  MINZ;
  170.  
  171.                // Update index:
  172.                ++OutCount;
  173.                
  174.                // Add an extra edge to list:
  175.                ZClipPoint [ OutCount ].Wx =  VPoint [ EndI ]->Wx;
  176.                ZClipPoint [ OutCount ].Wy =  VPoint [ EndI ]->Wy;
  177.                ZClipPoint [ OutCount ].Wz =  VPoint [ EndI ]->Wz;
  178.  
  179.                // Update index:
  180.                ++OutCount;
  181.                }
  182.             else {
  183.                  // No operation is necessary for case 4
  184.                  }
  185.             }   
  186.        // Advance to next vertex:
  187.        StartI = EndI;
  188.        }
  189.  
  190.    // Store the number of vertices in OutCount:
  191.    SPCount = ( WORD ) OutCount;
  192.  
  193.    // Project panel's points:
  194.    for ( Count = 0; Count < OutCount; Count++ )
  195.        {
  196.        OneOverZ = 1.0F / ZClipPoint [ Count ].Wz;
  197.        SPoint [ Count ].X  = ZClipPoint [ Count ].Wx * XSCALE * 
  198.                              OneOverZ + 160.0F;
  199.        SPoint [ Count ].Y  = ZClipPoint [ Count ].Wy * YSCALE * 
  200.                              OneOverZ + 100.0F;
  201.        SPoint [ Count ].Z  = ( ( double ) OneOverZ * 
  202.                              ( ( double ) ( 1 << ZSTEP_PREC ) ) );
  203.        }
  204.    }
  205.    
  206. void Panel3D::CalcNormal ()
  207.    {
  208.    // Calculate the normal of the panel:
  209.    long double X1, Y1, Z1, X2, Y2, Z2, X3, Y3, Z3, Distance, A, B, C;
  210.    Point3D UniqueVerts [ 4 ], TVert;
  211.    unsigned short Count, Range = 0;
  212.    // Create a list of unique vertices:
  213.    for ( Count = 0; Count < 4; Count++ )
  214.        {
  215.        TVert = *VPoint [ Count ];
  216.        if ( Range == 0 )
  217.           UniqueVerts [ Range++ ] = TVert;
  218.        else if ( UniqueVert ( TVert, UniqueVerts, Range ) )
  219.                {
  220.                UniqueVerts [ Range++ ] = TVert;
  221.                }
  222.        }
  223.  
  224.    X1 = UniqueVerts [ 0 ].Lx;
  225.    Y1 = UniqueVerts [ 0 ].Ly;
  226.    Z1 = UniqueVerts [ 0 ].Lz;
  227.  
  228.    X2 = UniqueVerts [ 1 ].Lx;
  229.    Y2 = UniqueVerts [ 1 ].Ly;
  230.    Z2 = UniqueVerts [ 1 ].Lz;
  231.  
  232.    X3 = UniqueVerts [ 2 ].Lx;
  233.    Y3 = UniqueVerts [ 2 ].Ly;
  234.    Z3 = UniqueVerts [ 2 ].Lz;
  235.  
  236.    // Use plane equation to determine plane's orientation:
  237.    A = Y1 * ( Z2 - Z3 ) + Y2 * ( Z3 - Z1 ) + Y3 * ( Z1 - Z2 );
  238.    B = Z1 * ( X2 - X3 ) + Z2 * ( X3 - X1 ) + Z3 * ( X1 - X2 );
  239.    C = X1 * ( Y2 - Y3 ) + X2 * ( Y3 - Y1 ) + X3 * ( Y1 - Y2 );
  240.  
  241.    // Get the distance to the vector:
  242.    Distance = sqrt ( A*A + B*B + C*C );
  243.    
  244.    // Normalize the normal to 1 and create a point:
  245.    Normal.X = ( A / Distance ) + VPoint [ 0 ]->Lx;
  246.    Normal.Y = ( B / Distance ) + VPoint [ 0 ]->Ly;
  247.    Normal.Z = ( C / Distance ) + VPoint [ 0 ]->Lz;
  248.    }
  249.  
  250. int inline Panel3D::CalcBFace ()
  251.   {
  252.   // Determine if polygon is a backface:
  253.   int Visible = 1; Invis = 0;
  254.   Point3D V = *VPoint [ 0 ];
  255.   double Direction = ( V.Wx * ( Normal.Tx - VPoint [ 0 ]->Wx ) + 
  256.                        V.Wy * ( Normal.Ty - VPoint [ 0 ]->Wy ) + 
  257.                        V.Wz * ( Normal.Tz - VPoint [ 0 ]->Wz ) );
  258.   if ( Direction > 0.0F )
  259.      {
  260.      // Get the cosine of the angle between the viewer and the polygon 
  261.      // normal:
  262.      Direction /= V.Mag ();
  263.      // Assume panel will remain a time proportional to the angle between
  264.      // the viewer to the polygon normal:
  265.      Invis = ( double ) Direction * ( double ) 25.0F;
  266.      // Set the invisible flag for this frame:
  267.      Visible = 0;
  268.      }
  269.   return Visible;
  270.   }
  271.  
  272. int Panel3D::CalcVisible3D ()
  273.   {
  274.   // Perform 3D culling:
  275.   int Visible;
  276.  
  277.   // Perform a back-face culling operation:
  278.   Visible = CalcBFace ();
  279.    
  280.   // If panel still visible, perform extent test:
  281.   if ( Visible )
  282.      Visible = CheckExtents ();
  283.   return Visible;
  284.   }
  285.  
  286. long Panel3D::CalcCenterZ ()
  287.    {
  288.    // Calculate the polygon's center Z:
  289.    long SummedComponents = ( double ) VPoint [ 0 ] -> Wz + 
  290.                            ( double ) VPoint [ 1 ] -> Wz +
  291.                            ( double ) VPoint [ 2 ] -> Wz +
  292.                            ( double ) VPoint [ 3 ] -> Wz;
  293.    long CenterZ = ( SummedComponents  >> 2 );
  294.    return CenterZ;
  295.    }
  296.  
  297. int Panel3D::CalcVisible2D ()
  298.   {
  299.   // Perform 2D culling:
  300.  
  301.   // Assume panel is visible:
  302.   long XMinInVis = 0, XMaxInVis = 0, YMinInVis = 0, YMaxInVis = 0;
  303.   long Visible = 1, AveX = 0, AveY = 0, N;
  304.   Invis = 0;
  305.  
  306.   // Make sure the panel has more than two points:
  307.   if ( SPCount < 3 )
  308.      {
  309.      // If not, flag panel as invisible:
  310.      Visible = 0;
  311.  
  312.      // Assume panel will remain invisible for four more frames:
  313.      Invis = 4;
  314.      return Visible;
  315.      }
  316.  
  317.    // Determine location of panel's 2D points:
  318.    for ( N = 0; N < SPCount; N++ )
  319.        {
  320.        if ( SPoint [ N ].X < MINX )
  321.           ++XMinInVis;
  322.  
  323.        if ( SPoint [ N ].X > MAXX )
  324.           ++XMaxInVis;
  325.  
  326.        if ( SPoint [ N ].Y < MINY )
  327.           ++YMinInVis;
  328.  
  329.        if ( SPoint [ N ].Y > MAXY )
  330.           ++YMaxInVis;
  331.  
  332.        AveX += SPoint [ N ].X;
  333.        AveY += SPoint [ N ].Y;
  334.        }
  335.  
  336.    if ( XMinInVis >= SPCount )
  337.       {
  338.       // Assume panel will remain invisible for a time 
  339.       // proportional to the distance from the edge of the 
  340.       // panel to the edge of the viewport:
  341.       AveX /= SPCount;
  342.       Invis = ( WORD ) ( abs ( AveX ) / ( 320 * 26 ) );
  343.       Visible = 0;
  344.       }
  345.    else if ( YMinInVis >= SPCount )
  346.            {
  347.            // Assume panel will remain invisible for a time 
  348.            // proportional to the distance from the edge of the 
  349.            // panel to the edge of the viewport:
  350.            AveY /= SPCount;
  351.            Invis = ( WORD ) ( abs ( AveY ) / ( 200 * 26 ) );
  352.            Visible = 0;
  353.            }
  354.    else if ( XMaxInVis >= SPCount )
  355.            {
  356.            // Assume panel will remain invisible for a time 
  357.            // proportional to the distance from the edge of the 
  358.            // panel to the edge of the viewport:
  359.            AveX /= SPCount;
  360.            Invis = ( WORD ) ( ( AveX - MAXX ) / ( 320 * 26 ) );
  361.            Visible = 0;
  362.            }
  363.    else if ( YMaxInVis >= SPCount )
  364.            {
  365.            // Assume panel will remain invisible for a time 
  366.            // proportional to the distance from the edge of the 
  367.            // panel to the edge of the viewport:
  368.            AveY /= SPCount;
  369.            Invis = ( WORD ) ( ( AveY - MAXY ) / ( 200 * 26 ) );
  370.            Visible = 0;
  371.            }
  372.   return Visible;
  373.   }
  374.    
  375. int Panel3D::CheckExtents ()
  376.   {
  377.   // Determine if panel is in the range of MINZ to MAXZ:
  378.   long Visible = 0, Count;
  379.   double MinZ;
  380.   for ( Count = 0; Count < 4; Count++ )
  381.       {
  382.       if ( VPoint [ Count ]->Wz > MINZ )
  383.          {
  384.          Visible = 1;
  385.          Invis = 0;
  386.          break;
  387.          }
  388.       }
  389.   if ( Visible )
  390.      {
  391.      MinZ = VPoint [ 0 ]->Wz;
  392.      for ( Count = 1; Count < 4; Count++ )
  393.          {
  394.          if ( VPoint [ Count ]->Wz < MinZ )
  395.             MinZ = VPoint [ Count ]->Wz;
  396.          }
  397.      if ( MinZ > MAXZ )
  398.         {
  399.         // Set the invisible flag for this frame:
  400.         Visible = 0;
  401.  
  402.         // Assume panel will remain invisible for a time 
  403.         // proportional to the distance from the viewer:
  404.         Invis = ( WORD ) ( ( MinZ - MAXZ ) / 50 );
  405.         }
  406.      }
  407.   else {
  408.        // Assume panel will remain invisible for a time 
  409.        // proportional to the distance from the viewer:
  410.        Invis = ( WORD ) ( abs ( CalcCenterZ () ) / 50 );
  411.        }
  412.   return Visible;
  413.   }
  414.  
  415. void Panel3D::Update ( Matrix3D &M )
  416.    {
  417.    // Transform the normal:
  418.    M.Transform ( Normal );
  419.  
  420.    // Update the invisibility flag:
  421.    if ( Invis > 0 )
  422.       --Invis;
  423.    }
  424.  
  425. void Panel3D::Display ( unsigned char *Dest )
  426.    {
  427.    // Display the panel in the screen buffer "Dest":
  428.    unsigned char RColor, *DPtr;
  429.    CeilLine LeftSeg, RightSeg;
  430.    long Top = 0, N, RightPos, LeftPos, NewRightPos, NewLeftPos, 
  431.         Height, EdgeCount, YIndex, Width, XStart, XEnd, DeltaZ, ZStep;
  432.    long *ZPtr, Z;
  433.    RColor = ( unsigned char ) Color; RColor;
  434.    EdgeCount = SPCount;
  435.  
  436.    // Search for lowest Y coordinate (top of polygon):
  437.    for ( N = 1; N < SPCount; N++ )
  438.        {
  439.        if ( SPoint [ N ].Y < SPoint [ Top ].Y )
  440.           Top = N;
  441.        }
  442.    RightPos = Top;
  443.    LeftPos = Top;
  444.  
  445.    // Calculate the index to the buffer:
  446.    YIndex = SPoint [ Top ].Y * 320;
  447.  
  448.    // Loop for all polygon edges:
  449.    while ( EdgeCount > 0 )
  450.          {
  451.          // Determine if the right side of the polygon needs 
  452.          // (re)initializing:
  453.          if ( RightSeg.Height () <= 0 )
  454.             {
  455.             NewRightPos = RightPos + 1;
  456.             if ( NewRightPos >= SPCount )
  457.                NewRightPos = 0;
  458.             RightSeg.Init ( SPoint [ RightPos ], SPoint [ NewRightPos ] );
  459.             RightPos = NewRightPos;
  460.             --EdgeCount;
  461.             // Perform object-precision clip on top of edge 
  462.             // (if necessary):
  463.             if ( RightSeg.GetY () < MINY )
  464.                {
  465.                RightSeg.ClipTop ( MINY );
  466.                YIndex = MINY * 320;
  467.                }
  468.             }
  469.          // Determine if the left side of the polygon needs 
  470.          // (re)initializing:
  471.          if ( LeftSeg.Height () <= 0 )
  472.             {
  473.             NewLeftPos = LeftPos - 1;
  474.             if ( NewLeftPos < 0 )
  475.                NewLeftPos = ( SPCount - 1 );
  476.             LeftSeg.Init ( SPoint [ LeftPos ], SPoint [ NewLeftPos ] );
  477.             LeftPos = NewLeftPos;
  478.             --EdgeCount;
  479.             // Perform object-precision clip on top of edge 
  480.             // (if necessary):
  481.             if ( LeftSeg.GetY () < MINY )
  482.                {
  483.                LeftSeg.ClipTop ( MINY );
  484.                YIndex = MINY * 320;
  485.                }
  486.             }
  487.          // Subdivide polygon into trapezoid:
  488.          if ( LeftSeg.Height () < RightSeg.Height () )
  489.             {
  490.             Height = LeftSeg.Height ();
  491.             if ( ( LeftSeg.GetY () + Height ) > MAXY )
  492.                {
  493.                Height = MAXY - LeftSeg.GetY ();
  494.                EdgeCount = 0;
  495.                }
  496.             }
  497.          else {
  498.               Height = RightSeg.Height ();
  499.               if ( ( RightSeg.GetY () + Height ) > MAXY )
  500.                  {
  501.                  Height = MAXY - RightSeg.GetY ();
  502.                  EdgeCount = 0;
  503.                  }
  504.               }
  505.  
  506.          // Loop for the height of the trapezoid:
  507.          while ( Height-- > 0 )
  508.                {
  509.                // Calculate initial values:
  510.                XStart = LeftSeg.GetX ();
  511.                XEnd = RightSeg.GetX ();
  512.                Width = XEnd - XStart;
  513.                if ( Width > 0 )
  514.                   {
  515.                   Z = LeftSeg.GetZ ();
  516.                   DeltaZ = ( RightSeg.GetZ () - LeftSeg.GetZ () );
  517.                   ZStep = DeltaZ / Width;
  518.  
  519.                   // Clip the scan-line:
  520.                   ClipHLine ( XStart, XEnd, Z, ZStep );
  521.                   Width = XEnd - XStart;
  522.                   DPtr = &Dest [ YIndex + XStart ];
  523.                   ZPtr = &ZBuffer [ YIndex + XStart ];
  524.  
  525.                   // Loop for width of scan-line:
  526.                   while ( Width-- > 0 )
  527.                         {
  528.                         if ( *ZPtr < Z )
  529.                            {
  530.                            *ZPtr = Z;
  531.                            *DPtr = ( unsigned char ) 
  532.                            ( 255 - ( ( 1 << ZSTEP_PREC ) / 
  533.                            ( Z - ZTrans ) / ( MAXZ / 256 ) ) );
  534.                            }
  535.                         Z += ZStep;
  536.                         ++DPtr;
  537.                         ++ZPtr;
  538.                         }
  539.                   }
  540.                ++RightSeg;
  541.                ++LeftSeg;
  542.                YIndex += 320;
  543.                }
  544.          }
  545.    }
  546.