home *** CD-ROM | disk | FTP | other *** search
- //
- // File name: 3Dclass.CPP
- //
- // Description: The support file for Polydemo
- //
- // Author: John De Goes
- //
- // Project: Cutting Edge 3D Game Programming
- //
-
- // ------------------------------------------------------------
- // | Global include files: |
- // ------------------------------------------------------------
-
- #include <Math.h>
- #include <CType.h>
- #include <Conio.h>
- #include <Stdio.h>
- #include <Stdlib.h>
- #include <Iostream.h>
-
- // ------------------------------------------------------------
- // | Local includes: |
- // ------------------------------------------------------------
-
- #include "3DClass.HPP"
-
- // ------------------------------------------------------------
- // | Global variables/constants: |
- // ------------------------------------------------------------
-
- double CosTable [ DEGREECOUNT ];
- double SinTable [ DEGREECOUNT ];
-
- // ------------------------------------------------------------
- // | Local structs/classes: |
- // ------------------------------------------------------------
-
- // A structure representing a screen edge:
- struct EdgeScreen {
- long X;
- } Left [ 200 ], Right [ 200 ];
-
- // ------------------------------------------------------------
- // | Function section: |
- // ------------------------------------------------------------
-
- void inline CalcFloorRemainder ( long Numerator, long Denominator,
- long &Floor, long &Remainder )
- {
- // If Numerator is less than zero, result will not
- // be handled properly; calulate the corrected
- // values:
- if ( Numerator < 0 )
- {
- // Calculate a temporary floor:
- Floor = -((-Numerator) / Denominator);
-
- // Calculate the remainder of Numerator / Denominator
- Remainder = (-Numerator) % Denominator;
-
- if ( Remainder )
- {
- --Floor; Remainder = Denominator - Remainder;
- }
- }
- // Else Numerator is positive; result handled
- // properly:
- else {
- Floor = Numerator / Denominator;
- Remainder = Numerator % Denominator;
- }
- }
-
-
- // Initiate math function - calculates trig tables:
- void InitMath()
- {
- long double Unit = (long double)( PI * 2.0F ) /
- (long double)DEGREECOUNT;
- for ( unsigned int i = 0; i < DEGREECOUNT; i++ )
- {
- long double Degree = (long double)i;
- CosTable[i] = cos ( Unit * Degree );
- SinTable[i] = sin ( Unit * Degree );
- }
- }
-
-
- // Function designed to get a word from a file:
- char *GetWord ( FILE *File, char *String )
- {
- int NextChar = fgetc ( File );
- int Index = 0;
- // Skip the spaces:
- while ( isspace ( NextChar ) )
- {
- NextChar = fgetc ( File );
- if ( NextChar == EOF )
- {
- String [ Index ] = NULL;
- return NULL;
- }
- }
- // Record the characters:
- while ( !isspace ( NextChar ) )
- {
- String [ Index++ ] = ( char ) NextChar;
- NextChar = fgetc ( File );
- if ( NextChar == EOF )
- {
- String [ Index ] = NULL;
- return NULL;
- }
- }
- String [ Index ] = NULL;
- return String;
- }
-
- // Function designed to count the number of triangles
- // in a RAW file:
- int CountTriangles ( char *FileName )
- {
- char Dummy [ 100 ];
- unsigned int VertCount = 0;
- FILE *File;
- if ( ( File = fopen ( FileName, "rt" ) ) == 0 )
- return 0;
- while ( GetWord ( File, Dummy ) != NULL )
- ++VertCount;
- fclose ( File );
- unsigned int TriCount = VertCount / 9; // Nine verts per
- return TriCount; // triangle
- }
-
- void inline ClipX1X2 ( long &X1, long &X2 )
- {
- // Clip a horizontal line:
- if ( X1 < 0 )
- X1 = 0;
- if ( X1 > 319 )
- X1 = 319;
- if ( X2 < 0 )
- X2 = 0;
- if ( X2 > 319 )
- X2 = 319;
- }
-
- void inline ClipY1Y2 ( long &Y1, long &Y2 )
- {
- // Clip a vertical line:
- if ( Y1 < 0 )
- Y1 = 0;
- if ( Y1 > 199 )
- Y1 = 199;
- if ( Y2 < 0 )
- Y2 = 0;
- if ( Y2 > 199 )
- Y2 = 199;
- }
-
- // Function designed to step along polygon edge:
- void inline PolyEdge::Step ()
- {
- X += StepX; Y += StepY;
- ErrorTerm += Numerator;
- if ( ErrorTerm >= Denominator )
- {
- ++X;
- ErrorTerm -= Denominator;
- }
- --EdgeHeight;
- }
-
- // Function designed to initialize stepping values for
- // a polygon edge:
- void PolyEdge::Initialize ( ScreenVertex &P1,
- ScreenVertex &P2 )
- {
- long Width = P2.X - P1.X;
- Y = P1.Y; StepY = 1;
- EdgeHeight = P2.Y - P1.Y;
- // Make sure the polygon edge has a definite height
- // before we make some rather complex calculations:
- if ( EdgeHeight > 0 )
- {
- CalcFloorRemainder ( -1, EdgeHeight, X, ErrorTerm);
- X += P1.X + 1;
- CalcFloorRemainder ( Width, EdgeHeight, StepX, Numerator );
- Denominator = EdgeHeight;
- }
- }
-
- // Function designed to set matrix to identity matrix:
- void Matrix3D::Initialize ()
- {
- Matrix[0][0] = 1; Matrix[0][1] = 0; Matrix[0][2] = 0; Matrix[0][3] = 0;
- Matrix[1][0] = 0; Matrix[1][1] = 1; Matrix[1][2] = 0; Matrix[1][3] = 0;
- Matrix[2][0] = 0; Matrix[2][1] = 0; Matrix[2][2] = 1; Matrix[2][3] = 0;
- Matrix[3][0] = 0; Matrix[3][1] = 0; Matrix[3][2] = 0; Matrix[3][3] = 1;
- }
-
- // Function that merges two matrices - try to avoid calling this function
- // very often.
- void Matrix3D::MergeMatrix ( double NewMatrix [ 4 ] [ 4 ] )
- {
- // Multiply NewMatirx by Matrix; store result in TempMatrix
- double TempMatrix [ 4 ] [ 4 ];
- for (short unsigned int i = 0; i < 4; i++)
- for (short unsigned int j = 0; j < 4; j++)
- TempMatrix[i][j] = (Matrix[i][0] * NewMatrix[0][j])
- + (Matrix[i][1] * NewMatrix[1][j])
- + (Matrix[i][2] * NewMatrix[2][j])
- + (Matrix[i][3] * NewMatrix[3][j]);
- // Copy TempMatrix to Matrix
- for (i = 0; i < 4; i++)
- {
- Matrix[i][0] = TempMatrix[i][0];
- Matrix[i][1] = TempMatrix[i][1];
- Matrix[i][2] = TempMatrix[i][2];
- Matrix[i][3] = TempMatrix[i][3];
- }
- }
-
- // Function designed to merge rotation matrices with master
- // matrix:
- void Matrix3D::Rotate ( int Xa, int Ya, int Za )
- {
- Xr = Xa; Yr = Ya; Zr = Za;
- double Rmat [ 4 ] [ 4 ];
-
- // Initialize Z rotation matrix - Note: we perform Z
- // rotation first to align the 3D Z axis with the 2D Z axis.
- Rmat[0][0]=COS(Za); Rmat[0][1]=SIN(Za); Rmat[0][2]=0; Rmat[0][3]=0;
- Rmat[1][0]=-SIN(Za); Rmat[1][1]=COS(Za); Rmat[1][2]=0; Rmat[1][3]=0;
- Rmat[2][0]=0; Rmat[2][1]=0; Rmat[2][2]=1; Rmat[2][3]=0;
- Rmat[3][0]=0; Rmat[3][1]=0; Rmat[3][2]=0; Rmat[3][3]=1;
-
- // Merge matrix with master matrix:
- MergeMatrix ( Rmat );
-
- // Initialize X rotation matrix:
- Rmat[0][0]=1; Rmat[0][1]=0; Rmat[0][2]=0; Rmat[0][3]=0;
- Rmat[1][0]=0; Rmat[1][1]=COS(Xa); Rmat[1][2]=SIN(Xa); Rmat[1][3]=0;
- Rmat[2][0]=0; Rmat[2][1]=-SIN(Xa); Rmat[2][2]=COS(Xa); Rmat[2][3]=0;
- Rmat[3][0]=0; Rmat[3][1]=0; Rmat[3][2]=0; Rmat[3][3]=1;
-
- // Merge matrix with master matrix:
- MergeMatrix ( Rmat );
-
- // Initialize Y rotation matrix:
- Rmat[0][0]=COS(Ya); Rmat[0][1]=0; Rmat[0][2]=-SIN(Ya); Rmat[0][3]=0;
- Rmat[1][0]=0; Rmat[1][1]=1; Rmat[1][2]=0; Rmat[1][3]=0;
- Rmat[2][0]=SIN(Ya); Rmat[2][1]=0; Rmat[2][2]=COS(Ya); Rmat[2][3]=0;
- Rmat[3][0]=0; Rmat[3][1]=0; Rmat[3][2]=0; Rmat[3][3]=1;
-
- // Merge matrix with master matrix:
- MergeMatrix ( Rmat );
- }
-
- // Function designed to merge translation matrix with master
- // matrix:
- void Matrix3D::Translate ( double Xt, double Yt, double Zt )
- {
- double Tmat [ 4 ] [ 4 ];
-
- // Initialize translation matrix:
- Tmat[0][0]=1; Tmat[0][1]=0; Tmat[0][2]=0; Tmat[0][3]=0;
- Tmat[1][0]=0; Tmat[1][1]=1; Tmat[1][2]=0; Tmat[1][3]=0;
- Tmat[2][0]=0; Tmat[2][1]=0; Tmat[2][2]=1; Tmat[2][3]=0;
- Tmat[3][0]=Xt; Tmat[3][1]=Yt; Tmat[3][2]=Zt; Tmat[3][3]=1;
-
- // Merge matrix with master matrix:
- MergeMatrix ( Tmat );
- }
-
- // Function designed to merge scaling matrix with master
- // matrix:
- void Matrix3D::Scale ( double Xs, double Ys, double Zs )
- {
- double Smat [ 4 ] [ 4 ];
-
- // Initialize scaling matrix:
- Smat[0][0] = Xs; Smat[0][1] = 0; Smat[0][2] = 0; Smat[0][3] = 0;
- Smat[1][0] = 0; Smat[1][1] = Ys; Smat[1][2] = 0; Smat[1][3] = 0;
- Smat[2][0] = 0; Smat[2][1] = 0; Smat[2][2] = Zs; Smat[2][3] = 0;
- Smat[3][0] = 0; Smat[3][1] = 0; Smat[3][2] = 0; Smat[3][3] = 1;
-
- // Merge matrix with master matrix:
- MergeMatrix ( Smat );
- }
-
- // Function designed to merge shearing matrix with master
- // matrix - warps true x, y values:
- void Matrix3D::Shear ( double Xs, double Ys )
- {
- double Smat [ 4 ] [ 4 ];
-
- // Initialize shearing matrix:
- Smat[0][0] = 1; Smat[0][1] = 0; Smat[0][2] = Xs; Smat[0][3] = 0;
- Smat[1][0] = 0; Smat[1][1] = 1; Smat[1][2] = Ys; Smat[1][3] = 0;
- Smat[2][0] = 0; Smat[2][1] = 0; Smat[2][2] = 1; Smat[2][3] = 0;
- Smat[3][0] = 0; Smat[3][1] = 0; Smat[3][2] = 0; Smat[3][3] = 1;
-
- // Merge matrix with master matrix:
- MergeMatrix ( Smat );
- }
-
- // Function designed to multiply a vertex by the master
- // matrix:
- Vertex inline &Matrix3D::Transform ( Vertex &V )
- {
- // Initialize temporary variables:
- double OldX = V.Wx;
- double OldY = V.Wy;
- double OldZ = V.Wz;
-
- // Transform vertex by master matrix:
- V.Wx = ( ( OldX * Matrix[0][0]) )
- + ( (OldY * Matrix[1][0]) )
- + ( (OldZ * Matrix[2][0]) )
- + Matrix[3][0];
-
- V.Wy = ( (OldX * Matrix[0][1]) )
- + ( (OldY * Matrix[1][1]) )
- + ( (OldZ * Matrix[2][1]) )
- + Matrix[3][1];
-
- V.Wz = ( (OldX * Matrix[0][2]) )
- + ( (OldY * Matrix[1][2]) )
- + ( (OldZ * Matrix[2][2]) )
- + Matrix[3][2];
- return V;
- }
-
- // Function designed to multiply a vector by the master
- // matrix:
- Vector inline &Matrix3D::Transform ( Vector &V )
- {
- // Initialize temporary variables:
- double OldX = V.X;
- double OldY = V.Y;
- double OldZ = V.Z;
-
- // Rotate normal around Z axis:
- V.X = OldX * COS ( Zr ) - OldY * SIN ( Zr );
- V.Y = OldX * SIN ( Zr ) + OldY * COS ( Zr );
-
- // Rotate normal around X axis:
- OldY = V.Y;
- V.Y = OldY * COS ( Xr ) - OldZ * SIN ( Xr );
- V.Z = OldY * SIN ( Xr ) + OldZ * COS ( Xr );
-
- // Rotate normal around Y axis:
- OldX = V.X; OldZ = V.Z;
- V.X = OldZ * SIN ( Yr ) + OldX * COS ( Yr );
- V.Z = OldZ * COS ( Yr ) - OldX * SIN ( Yr );
-
- return V;
- }
-
- // Function designed to multiply a triangle's vertices by matrix
- // "Matrix":
- void Triangle::Transform ( Matrix3D &Matrix )
- {
- // Transform the surface normal:
- Matrix.Transform ( Normal );
-
- // Update the triangle's vertices:
- Matrix.Transform ( VPoint [ 0 ] );
- Matrix.Transform ( VPoint [ 1 ] );
- Matrix.Transform ( VPoint [ 2 ] );
- }
-
- // Function designed to determine if triangle is a back-
- // face:
- void Triangle::Backface ()
- {
- const TRUE = 1, FALSE = 0;
- double VX = ( VPoint [ 0 ].Wx );
- double VY = ( VPoint [ 0 ].Wy );
- double VZ = ( VPoint [ 0 ].Wz );
-
- double CosA = ( VX * Normal.X + VY * Normal.Y +
- VZ * Normal.Z );
- if ( CosA > 0.0F )
- Visible = FALSE;
-
- else Visible = TRUE;
- }
-
- // Function designed to calculate a polygon normal:
- void Triangle::CalculateNormal ()
- {
- long double X1 = VPoint [0].Wx;
- long double Y1 = VPoint [0].Wy;
- long double Z1 = VPoint [0].Wz;
-
- long double X2 = VPoint [1].Wx;
- long double Y2 = VPoint [1].Wy;
- long double Z2 = VPoint [1].Wz;
-
- long double X3 = VPoint [2].Wx;
- long double Y3 = VPoint [2].Wy;
- long double Z3 = VPoint [2].Wz;
-
- // Use plane equation to determine plane's orientation:
- long double A = Y1 * ( Z2 - Z3 ) + Y2 * ( Z3 - Z1 ) + Y3 * ( Z1 - Z2 );
- long double B = Z1 * ( X2 - X3 ) + Z2 * ( X3 - X1 ) + Z3 * ( X1 - X2 );
- long double C = X1 * ( Y2 - Y3 ) + X2 * ( Y3 - Y1 ) + X3 * ( Y1 - Y2 );
-
- long double Distance = sqrt ( A*A + B*B + C*C );
-
- // Normalize the normal:
- Normal.X = A / Distance;
- Normal.Y = B / Distance;
- Normal.Z = C / Distance;
- }
-
- // Function designed to load a triangle from a
- // RAW file:
- int Triangle::Load ( FILE *File )
- {
- int RValue = 1;
- char String [ 100 ];
- // Load nine floating-point values:
- for ( unsigned int Index = 0; Index < 3; Index++ )
- {
- VPoint [ Index ].Wx = atof ( GetWord ( File, String ) );
- VPoint [ Index ].Wy = atof ( GetWord ( File, String ) );
- VPoint [ Index ].Wz = atof ( GetWord ( File, String ) );
- if ( String == NULL )
- {
- RValue = 0;
- break;
- }
- }
-
- // Assign a random shade of gray to the triangle:
- Color = ( unsigned char ) ( random ( 5 ) + 20 );
-
- // Calculate triangle's normal:
- CalculateNormal ();
-
- return RValue;
- }
-
- // Function designed to project a triangle onto
- // the viewport:
- void Triangle::Project ()
- {
- for ( unsigned int Count = 0; Count < 3; Count++ )
- {
- double X = VPoint [ Count ].Wx;
- double Y = VPoint [ Count ].Wy;
- double Z = VPoint [ Count ].Wz;
- // If Z is less than or equal to zero...
- if ( Z <= 0.0F )
- {
- // ...cheat! No one will ever know...
- Z = 1.0F;
- }
- // Perform perspective projection:
- double OneOverZ = 1.0F / Z;
- SPoint [ Count ].X = X * 120.0F * OneOverZ + 160;
- SPoint [ Count ].Y = Y * -120.0F * OneOverZ + 100;
- }
- }
-
-
- // Function designed to draw a triangle on a linear buffer:
- void Triangle::Rasterize ( unsigned char *Dest )
- {
- PolyEdge ScanEdge;
- // Assume element 0 is top/bottom of polygon:
-
- long TopE = 0, BotE = 0, VertCount = 3, Vert1, Vert2;
- long Top, Bot, X1, X2, Width, YIndex, Y, Temp, Check;
- unsigned Height, Yh;
-
- // Assume SPoint[0] is top/bottom of polygon:
- long TopY = SPoint [ 0 ].Y, BotY = SPoint [ 0 ].Y;
-
- // Attempt to verify previous assumptions:
- for ( Check = 1; Check < VertCount; Check++ )
- {
- if ( TopY > SPoint [ Check ].Y )
- {
- TopY = SPoint [ Check ].Y;
- TopE = Check;
- }
- else if ( BotY < SPoint [ Check ].Y )
- {
- BotY = SPoint [ Check ].Y;
- BotE = Check;
- }
- }
-
- Vert1 = TopE, Vert2 = TopE - 1;
-
- // Check for wrap:
- if ( Vert2 < 0 )
- Vert2 = (VertCount - 1);
-
- // Scan convert the left polygon edge:
- while ( Vert1 != BotE )
- {
- ScanEdge.Initialize ( SPoint [ Vert1 ],
- SPoint [ Vert2 ] );
- Height = ScanEdge.Height ();
- // Loop for the height of the polygon edge:
- for ( Yh = 0; Yh < Height; Yh++ )
- {
- Y = ScanEdge.GetY ();
- // If the scanline's on the scren....
- if ( Y >= 0 )
- {
- if ( Y <= 199 )
- {
- // ...record it:
- Left [ Y ].X = ScanEdge.GetX ();
- }
- else break;
- }
- ++ScanEdge;
- }
- Temp = Vert1;
- Vert1 = Vert2;
- Vert2 = Temp - 1;
- // Check for wrap:
- if ( Vert2 < 0 )
- Vert2 = (VertCount - 1);
- }
-
- Vert1 = TopE, Vert2 = ( TopE + 1 );
-
- // Check for wrap:
- if ( Vert2 > ( VertCount - 1 ) )
- Vert2 = 0;
-
- // Scan convert the right polygon edge:
- while ( Vert1 != BotE )
- {
- ScanEdge.Initialize ( SPoint [ Vert1 ],
- SPoint [ Vert2 ] );
- Height = ScanEdge.Height ();
- // Loop for the height of the polygon edge:
- for ( Yh = 0; Yh < Height; Yh++ )
- {
- Y = ScanEdge.GetY ();
- // If the scanline's on the screen....
- if ( Y >= 0 )
- {
- if ( Y <= 199 )
- {
- // ...record it:
- Right [ Y ].X = ScanEdge.GetX ();
- }
- else break;
- }
- ++ScanEdge;
- }
- Temp = Vert1;
- Vert1 = Vert2;
- Vert2 = ( Temp + 1 );
- // Check for wrap:
- if ( Vert2 > ( VertCount - 1 ) )
- Vert2 = 0;
- }
-
- Top = TopY; Bot = BotY;
-
- // Clip the top and bottom coordinates:
- ClipY1Y2 ( Top, Bot );
-
- // Locate the Y start of the first scanline:
- YIndex = Top * 320;
-
- // Loop for the height of the polygon:
- for ( Y = Top; Y < Bot; Y++ )
- {
- // Find the coordinates of the left and right
- // polygons edges:
- X1 = Left [ Y ].X;
- X2 = Right [ Y ].X;
-
- // Clip same:
- ClipX1X2 ( X1, X2 );
-
- // Determine the width of the scanline:
- Width = ( X2 - X1 );
-
- // Make sure there's something to draw:
- if ( Width > 0 )
- {
- // Draw the scanline:
- unsigned int Index = YIndex + X1;
- setmem ( Dest + Index, Width, Color );
- }
-
- // Increment the buffer index:
- YIndex += 320;
- }
- }
-
-
- // Function designed to display a triangle on a linear buffer:
- void inline Triangle::Display ( unsigned char *Buffer )
- {
- Backface ();
- if ( Visible )
- {
- Project ();
- Rasterize ( Buffer );
- }
- }
-
- // Function designed to load an entire triangle world:
- int TriangleWorld::Load ( char *FileName )
- {
- FILE *File;
- TriCount = CountTriangles ( FileName );
- if ( TriCount <= 0 )
- return 0;
- if ( ( File = fopen ( FileName, "rt" ) ) == 0 )
- return 0;
- if ( ( World = new Triangle [ TriCount ] ) == 0 )
- {
- fclose ( File );
- return 0;
- }
- for ( unsigned int Count = 0; Count < TriCount; Count++ )
- {
- if ( !World [ Count ].Load ( File ) )
- {
- fclose ( File );
- delete [] World;
- return 0;
- }
- }
- fclose ( File );
- return 1;
- }
-
- // Function designed to transform an entire triangle world:
- void TriangleWorld::Transform ( Matrix3D &Matrix )
- {
- for ( unsigned int Count = 0; Count < TriCount; Count++ )
- {
- World [ Count ].Transform ( Matrix );
- }
- }
-
- // Function designed to display an entire world:
- void TriangleWorld::Display ( unsigned char *Buffer )
- {
- for ( unsigned int Count = 0; Count < TriCount; Count++ )
- {
- World [ Count ].Display ( Buffer );
- }
- }
-