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