// Project_One.cpp // // Flat Shaded 3D cube using DirectDraw7 and DirectInput8. // Software rendering. Tripple buffering. 640*480*16 only. // // Author:Simon Brown // SYSTEM-DEFINES ///////////////////////////////////////////////////////////////////////////////// #define D3D_OVERLOADS #define WIN32_LEAN_AND_MEAN #define INITGUID // INCLUDES /////////////////////////////////////////////////////////////////////////////////////// #include #include #include #include #include // APP DEFINES //////////////////////////////////////////////////////////////////////////////////// #define NP 12 // Number of triangles #define X 0 #define Y 1 #define Z 2 // USING DECLARATION ////////////////////////////////////////////////////////////////////////////// using namespace std; // DATA STRUCTURES //////////////////////////////////////////////////////////////////////////////// // struct used for storing color of a face struct color { int red, green, blue; // red,green and blue compenent of a color int combined; // int value representing complete RGB color value }; // struct for storing a 4 by 4 matrix struct matrix { // data members float m [4][4]; // member functions void SetIdentityMatrix ( void ); void SetZeroMatrix ( void ); void MultiplyMatrices ( matrix b ); }; // struct describing a camera struct camera { // data members int xPos, yPos, zPos; // camera x,y,z position float xAngle, yAngle, zAngle; // camera s,y,z rotation int fov; // cameras field-of-view }; // vertex struct, holds one vertex, including normal and texture co-ords struct vertex { // data members float x, y, z; // x, y, z co-ordinates of a vertex in object space float gx, gy, gz; // x, y, z co-ordinates of a vertex once in global space float tu, tv; // u, v texture co-ordinates int sc_x, sc_y; // x, y screen co-ordinates }; // struct for storing a triangle struct triangle { // data members vertex v[3]; // 3 vertices bool isVisible; // is triangle visible? color faceColor; // color of face float nx, ny, nz; // x, y, z values for the normal float gnx, gny, gnz; // x, y, z values of rotated normals int x4; // x co-ordinate of 4th point where triangle is split float brightness; // brightness of face // member functions void ComputeNormal ( void ); void RotateNormals ( matrix a ); void TransformTriangle ( matrix a ); void Cull ( void ); void ConvertToScreen ( void ); void Draw ( void ); void DrawFT ( void ); void DrawFB ( void ); void DrawLine ( int x1, int x2, int y ); }; // struct for storing a 3D object struct mesh { // data members int numTriangles; // number of triangles in mesh triangle* faces; // pointer to the triangles themselves, created dynamically int xPos, yPos, zPos; // x,y,z position of mesh float xAngle, yAngle, zAngle; // x,y,z rotation of mesh (locally) int size; // size of mesh (scaling factor) float xVelocity, yVelocity, zVelocity; // movement speed in x,y,z directions float xRSpeed, yRSpeed, zRSpeed; // rotation speed about x,y,z axis // members functions char* LoadMesh ( char* filename ); void DrawMesh ( void ); }; // FUNCTION PROTOTYPES //////////////////////////////////////////////////////////////////////////// char* DI_Start ( void ); char* DD_Start ( void ); void DI_End ( void ); void DD_End ( void ); char* Game_Main ( void ); void ProcessInput ( void ); void SetSineTable ( void ); void SetCosineTable ( void ); void UpdateVariables ( void ); matrix Scale ( int size ); matrix RotateX ( int xa ); matrix RotateY ( int ya ); matrix RotateZ ( int za ); matrix Translate ( float x_disp, float y_disp, float z_disp ); // GLOBALS //////////////////////////////////////////////////////////////////////////////////////// HWND g_hwnd = NULL; // global window handle HINSTANCE g_hinst = NULL; // global instance char str[255] = ""; char* error_string = NULL; // Used to store error messages float speed = 1; int x_rotate, y_rotate, z_rotate; // Rotation flags mesh cube; // mesh object camera cam1; // camera object float SIN [3600] = {0}; // SIN and COS lookup tables float COS [3600] = {0}; // DIRECT DRAW (VERSION 7) //////////////////////////////////////////////////////////////////////// LPDIRECTDRAW7 gpDD = NULL; // DirectDraw Object LPDIRECTDRAWSURFACE7 gpDDSPrimary = NULL; // DirectDraw Primary surface LPDIRECTDRAWSURFACE7 gpDDSBackBuffer = NULL; // DirectDraw Back-buffer surface LPDIRECTDRAWSURFACE7 gpDDSDraw = NULL; // DirectDraw Off-Screen surface LPDIRECTDRAWSURFACE7 gpDDSDigits = NULL; // DirectDraw Z-Buffer surface (software) DDBLTFX ddbltfx; // Blitter object USHORT* video_buffer = NULL; // used to draw onto back-buffer // DIRECT INPUT (VERSION 8) /////////////////////////////////////////////////////////////////////// LPDIRECTINPUT8 gpDI; // Main DInput object LPDIRECTINPUTDEVICE8 gpDIDevice; // Keyboard device UCHAR buffer [256]; int words_per_line = 0; // FUNCTIONS ////////////////////////////////////////////////////////////////////////////////////// // WindowProc() /////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////// // Handles windows messages /////////////////////////////////////////////////////////////////////// LRESULT CALLBACK WindowProc ( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam ) { switch ( msg ) { case WM_DESTROY: { PostQuitMessage(0); return(0); }; default: break; } return ( DefWindowProc ( hwnd, msg, wparam, lparam )); } // WinMain () ///////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////// // Program execution starts here ////////////////////////////////////////////////////////////////// int WINAPI WinMain( HINSTANCE hinstance, HINSTANCE hprevinstance, LPSTR lpcmdline, int ncmdshow) { WNDCLASS wcl; // window class HWND hwnd; // local window handle MSG msg; // message // Complete Window Class Struct wcl.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW; wcl.lpfnWndProc = WindowProc; wcl.cbClsExtra = 0; wcl.cbWndExtra = 0; wcl.hInstance = hinstance; wcl.hIcon = LoadIcon ( NULL, IDI_APPLICATION ); wcl.hCursor = LoadCursor ( NULL, IDC_ARROW ); wcl.hbrBackground = (HBRUSH) GetStockObject ( BLACK_BRUSH ); wcl.lpszMenuName = NULL; wcl.lpszClassName = "DDClass"; // Register the Class if ( !RegisterClass ( &wcl )) { return ( 0 ); } // Create the Window if (!(hwnd = CreateWindow("DDClass", // Class "DD7Console", // Title WS_POPUP | WS_VISIBLE, // Style flags 0,0, // Location x,y 640, // Width 480, // Height NULL, // Handle to parent Window NULL, // Handle to menu hinstance, // Handle to instance NULL ))) // Misc. parameterss { return ( 0 ); } // Save the window handle and instance g_hwnd = hwnd; g_hinst = hinstance; // DirectX Specific Initialization if ( error_string == NULL ) error_string = DI_Start (); if ( error_string == NULL ) error_string = DD_Start (); // Load cube mesh into mesh object if ( error_string == NULL ) error_string = cube.LoadMesh ( "cube.txt" ); // Calculate face normals for ( int n = 0; n < cube.numTriangles; n++ ) (cube.faces + n)->ComputeNormal (); // Check for Initialization errors if ( error_string != NULL ) { MessageBox ( g_hwnd, error_string, "Fatal Error!", MB_OK | MB_ICONSTOP ); PostMessage ( g_hwnd, WM_DESTROY, 0, 0 ); } // Calculate SIN and COS values for look-up tables SetSineTable (); SetCosineTable (); // Main Message Loop while ( true ) { if ( PeekMessage ( &msg, NULL, 0, 0, PM_REMOVE )) { // See if we have a Quit message if (msg.message == WM_QUIT) break; // Translate accelerator keys TranslateMessage(&msg); // Send message for processing by WindowProc() DispatchMessage(&msg); } // Run Main Game Loop error_string = Game_Main(); // Check to see if main loop produced an error if ( error_string != NULL ) { MessageBox ( g_hwnd, error_string, "Run-Time Error", MB_OK | MB_ICONSTOP ); PostMessage ( g_hwnd, WM_DESTROY, 0, 0 ); } } // Release DirectX specific resources DD_End (); DI_End (); // Return to Windows return ( msg.wParam ); } // DI_Start () //////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////// // This function initializes DirectInput ////////////////////////////////////////////////////////// char* DI_Start ( void ) { // Obtain pointer to DI Interface if ( DI_OK != DirectInput8Create ( g_hinst, DIRECTINPUT_VERSION, IID_IDirectInput8, (void**)&gpDI, NULL )) { return ( "Obtain DI Interface Failed." ); } // Create DI Device for Keyboard if ( DI_OK != ( gpDI->CreateDevice(GUID_SysKeyboard, &gpDIDevice, NULL))) { return ( "Create DI Keyboard Device Failed." ); } // Set the Keyboard Data Format if ( DI_OK != ( gpDIDevice->SetDataFormat ( &c_dfDIKeyboard ))) { return ( "Set Keyboard Data Format Failed." ); } // Set the Keyboard Co-operative Level if ( DI_OK != ( gpDIDevice->SetCooperativeLevel ( g_hwnd, DISCL_NONEXCLUSIVE | DISCL_BACKGROUND ))) { return ( "Set Keyboard Co-Operative Level Failed." ); } // Aquire access to the Keyboard if ( DI_OK != ( gpDIDevice->Acquire() )) { return ( "Aquire Keyboard Failed." ); } // Return success return ( NULL ); } // DD_Start () //////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////// // This function initializes DirectDraw /////////////////////////////////////////////////////////// char* DD_Start ( void ) { // Create the DirectDraw Object if ( DD_OK != ( DirectDrawCreateEx ( NULL, (VOID**) &gpDD, IID_IDirectDraw7, NULL ))) { return ( "Create DD Object Failed." ); } // Set the Co-operative level for DirectDraw if ( DD_OK != ( gpDD->SetCooperativeLevel ( g_hwnd, DDSCL_FULLSCREEN | DDSCL_EXCLUSIVE | DDSCL_ALLOWREBOOT ))) { return ( "Set DD Co-operative Level Failed." ); } // Set the Display Mode if ( DD_OK != ( gpDD->SetDisplayMode ( 640, 480, 16, 0, 0 ))) { return ( "Set Display Mode Failed." ); } // Complete a DDSURFACEDESC2 struct for the Primary Surface DDSURFACEDESC2 ddsd; ZeroMemory ( &ddsd, sizeof ( DDSURFACEDESC2 )); ddsd.dwSize = sizeof ( DDSURFACEDESC2 ); ddsd.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT; ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_FLIP | DDSCAPS_COMPLEX; ddsd.dwBackBufferCount = 2; // Create the Primary Surface if ( DD_OK != ( gpDD->CreateSurface ( &ddsd, &gpDDSPrimary, NULL ))) { return ( "Create Primary Surface Failed." ); } // Get Address of Back Buffer DDSCAPS2 ddscaps; ZeroMemory ( &ddscaps, sizeof ( DDSCAPS2 )); ddscaps.dwCaps = DDSCAPS_BACKBUFFER; if ( DD_OK != ( gpDDSPrimary->GetAttachedSurface ( &ddscaps, &gpDDSBackBuffer ))) { return ( "Get Back-buffer Address failed." ); } // Complete a BltFX struct ZeroMemory ( &ddbltfx, sizeof ( ddbltfx ) ); ddbltfx.dwSize = sizeof ( ddbltfx ); ddbltfx.dwFillColor = 0; // Return success return ( NULL ); } // DI_End () ////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////// // This function releases DirectInput Objects ///////////////////////////////////////////////////// void DI_End ( void ) { // Release DI Keyboard Device if ( gpDIDevice != NULL ) { gpDIDevice->Unacquire(); gpDIDevice->Release(); gpDIDevice = NULL; } // Release DI Interface pointer if ( gpDI != NULL ) { gpDI->Release(); gpDI = NULL; } } // DD_End () ////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////// // This function releases DirectDraw Objects ////////////////////////////////////////////////////// void DD_End ( void ) { // Release the Primary Surface if ( gpDDSPrimary != NULL ) { gpDDSPrimary->Release(); gpDDSPrimary = NULL; } // Release the DirectDraw Object if ( gpDD != NULL ) { gpDD->Release(); gpDD = NULL; } } // Game_Main () /////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////// // Main game loop ///////////////////////////////////////////////////////////////////////////////// char* Game_Main ( void ) { // Retrieve Keyboard Input if ( DI_OK != ( gpDIDevice->GetDeviceState ( sizeof (buffer), buffer ))) { return ( "Retrieve Keyboard Input Failed." ); } // Clear the Draw Surface if ( FAILED ( gpDDSBackBuffer->Blt ( NULL, NULL, NULL, DDBLT_COLORFILL | DDBLT_WAIT, &ddbltfx ))) { return ( "Clear Draw Surface with Blit Failed." ); } DDSURFACEDESC2 ddsd; // DirectDraw Surface Description ZeroMemory ( &ddsd, sizeof ( DDSURFACEDESC2 )); ddsd.dwSize = sizeof ( DDSURFACEDESC2 ); // Lock Draw Surface if ( FAILED ( gpDDSBackBuffer->Lock ( NULL, &ddsd, DDLOCK_SURFACEMEMORYPTR | DDLOCK_WAIT, NULL ))) { return ( "Lock Draw Surface failed." ); } // Store pointer to video-memory video_buffer = (USHORT *)ddsd.lpSurface; // Store vertical pitch of BackBuffer surface words_per_line = (ddsd.lPitch >> 1); // Draw cube cube.DrawMesh(); // Process keyboard input ProcessInput(); // Maintain variables within limits UpdateVariables (); // Unlock Draw Surface, we have finished drawing onto it if ( FAILED ( gpDDSBackBuffer->Unlock ( NULL ))) { return ( "Unlock Draw Surface Failed." ); } // Flip BackBuffer to Primary surface if ( FAILED ( gpDDSPrimary->Flip ( NULL, DDFLIP_WAIT ))) { return ( "Flip BackBuffer to Primary Failed" ); } // Return success return ( NULL ); } // ProcessInput () //////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////// // This function checks for input and updates variables /////////////////////////////////////////// void ProcessInput ( void ) { int input_scale = 1; // Escape key to Quit if ( buffer[DIK_ESCAPE] & 0x80 ) PostMessage ( g_hwnd, WM_DESTROY, 0, 0 ); if ( buffer[DIK_RALT] & 0x80 ) input_scale *= 3; if ( buffer[DIK_Z] & 0x80 ) cam1.zAngle += ( 3 * input_scale ); if ( buffer[DIK_X] & 0x80 ) cam1.zAngle -= ( 3 * input_scale ); if ( buffer[DIK_RIGHT] & 0x80 ) cam1.yAngle += ( 3 * input_scale ); if ( buffer[DIK_LEFT] & 0x80 ) cam1.yAngle -= ( 3 * input_scale ); if ( buffer[DIK_UP] & 0x80 ) cam1.xAngle += ( 3 * input_scale ); if ( buffer[DIK_DOWN] & 0x80 ) cam1.xAngle -= ( 3 * input_scale ); if ( buffer[DIK_EQUALS] & 0x80 ) cube.zPos += ( 3 * input_scale ); if ( buffer[DIK_MINUS] & 0x80 ) cube.zPos -= ( 3 * input_scale ); if ( buffer[DIK_Q] & 0x80 ) x_rotate = 1; if ( buffer[DIK_A] & 0x80 ) x_rotate = 0; if ( buffer[DIK_W] & 0x80 ) y_rotate = 1; if ( buffer[DIK_S] & 0x80 ) y_rotate = 0; if ( buffer[DIK_E] & 0x80 ) z_rotate = 1; if ( buffer[DIK_D] & 0x80 ) z_rotate = 0; if ( buffer[DIK_LBRACKET] & 0x80 ) speed -= input_scale; if ( buffer[DIK_RBRACKET] & 0x80 ) speed += input_scale; if ( buffer[DIK_B] & 0x80 ) cube.yPos -= ( 2 * input_scale ); if ( buffer[DIK_T] & 0x80 ) cube.yPos += ( 2 * input_scale ); if ( buffer[DIK_H] & 0x80 ) cube.xPos += ( 2 * input_scale ); if ( buffer[DIK_F] & 0x80 ) cube.xPos -= ( 2 * input_scale ); } // UpdateVariables () ///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////// // This function maintains certain variables within limits //////////////////////////////////////// void UpdateVariables ( void ) { if ( x_rotate == 1 ) cube.xAngle += speed; if ( y_rotate == 1 ) cube.yAngle += speed; if ( z_rotate == 1 ) cube.zAngle += speed; while ( cube.xAngle < 0 ) cube.xAngle += 3600; while ( cube.yAngle < 0 ) cube.yAngle += 3600; while ( cube.zAngle < 0 ) cube.zAngle += 3600; while ( cube.xAngle > 3599 ) cube.xAngle -= 3600; while ( cube.yAngle > 3599 ) cube.yAngle -= 3600; while ( cube.zAngle > 3599 ) cube.zAngle -= 3600; while ( cam1.xAngle < 0 ) cam1.xAngle += 3600; while ( cam1.yAngle < 0 ) cam1.yAngle += 3600; while ( cam1.zAngle < 0 ) cam1.zAngle += 3600; while ( cam1.xAngle > 3599 ) cam1.xAngle -= 3600; while ( cam1.yAngle > 3599 ) cam1.yAngle -= 3600; while ( cam1.zAngle > 3599 ) cam1.zAngle -= 3600; } // mesh::DrawMesh () ////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////// // Automated mesh draw function /////////////////////////////////////////////////////////////////// void mesh::DrawMesh ( void ) { // Set all triangles to be drawn for ( int n = 0; n < numTriangles; n++ ) { (faces + n)->isVisible = true; } // temporary matrix variables matrix a, b; a.SetIdentityMatrix (); b.SetIdentityMatrix (); // calculate matrix for vertex processing a.MultiplyMatrices ( Scale ( size) ); a.MultiplyMatrices ( RotateX ( xAngle ) ); a.MultiplyMatrices ( RotateY ( yAngle ) ); a.MultiplyMatrices ( RotateZ ( zAngle ) ); a.MultiplyMatrices ( Translate ( xPos, yPos, zPos ) ); a.MultiplyMatrices ( RotateX ( cam1.xAngle ) ); a.MultiplyMatrices ( RotateY ( cam1.yAngle ) ); a.MultiplyMatrices ( RotateZ ( cam1.zAngle ) ); // calculate matrix for normal processing b.MultiplyMatrices ( RotateX ( xAngle ) ); b.MultiplyMatrices ( RotateY ( yAngle ) ); b.MultiplyMatrices ( RotateZ ( zAngle ) ); b.MultiplyMatrices ( RotateX ( cam1.xAngle ) ); b.MultiplyMatrices ( RotateY ( cam1.yAngle ) ); b.MultiplyMatrices ( RotateZ ( cam1.zAngle ) ); for ( n = 0; n < numTriangles; n++ ) { (faces + n)->TransformTriangle ( a ); (faces + n)->RotateNormals ( b ); (faces + n)->Cull (); if ( (faces + n)->isVisible ) if ( (faces + n)->v[0].gz > 0 ) if ( (faces + n)->v[1].gz > 0 ) if ( (faces + n)->v[2].gz > 0 ) { (faces + n)->ConvertToScreen (); (faces + n)->Draw (); } } } // Scale () /////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////// // This function calculates a matrix to scale a point in 3D space by a factor of 'size' /////////// matrix Scale ( int size ) { matrix temp; temp.SetZeroMatrix(); temp.m [0][0] = size; temp.m [1][1] = size; temp.m [2][2] = size; temp.m [3][3] = 1; return temp; } // RotateX () ///////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////// // This function returns an x-axis rotation matrix //////////////////////////////////////////////// matrix RotateX ( int xa ) { matrix temp; temp.SetIdentityMatrix (); temp.m [1][1] = COS [xa]; temp.m [2][2] = COS [xa]; temp.m [2][1] = -SIN [xa]; temp.m [1][2] = SIN [xa]; return temp; } // RotateY () ///////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////// // This function returns a y-axis rotation matrix ///////////////////////////////////////////////// matrix RotateY ( int ya ) { matrix temp; temp.SetIdentityMatrix (); temp.m [0][0] = COS [ya]; temp.m [0][2] = -SIN [ya]; temp.m [2][0] = SIN [ya]; temp.m [2][2] = COS [ya]; return temp; } // RotateZ () ///////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////// // This function returns a z-axis rotation matrix ///////////////////////////////////////////////// matrix RotateZ ( int za ) { matrix temp; temp.SetIdentityMatrix (); temp.m [0][0] = COS [za]; temp.m [1][1] = COS [za]; temp.m [0][1] = SIN [za]; temp.m [1][0] = -SIN [za]; return temp; } // Translate () /////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////// // This function returns a translation matrix ///////////////////////////////////////////////////// matrix Translate ( float x_disp, float y_disp, float z_disp ) { matrix temp; temp.SetIdentityMatrix (); temp.m [3][0] = x_disp; temp.m [3][1] = y_disp; temp.m [3][2] = z_disp; return temp; } // matrix::MultiplyMatrices () //////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////// // This function will multiply two matrices /////////////////////////////////////////////////////// void matrix::MultiplyMatrices ( matrix b ) { matrix temp; temp.SetZeroMatrix (); for ( int i = 0; i < 4; i++ ) for ( int j = 0; j < 4; j++ ) for ( int k = 0; k < 4; k++ ) temp.m [i][j] += b.m [k][j] * m [i][k]; for ( i = 0; i < 4; i++ ) for ( int j = 0; j < 4; j++ ) m [i][j] = temp.m [i][j]; } // matrix::SetZeroMatrix () /////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////// // This function will set a zero matrix /////////////////////////////////////////////////////////// void matrix::SetZeroMatrix ( void ) { for ( int i = 0; i < 4; i++ ) for ( int j = 0; j < 4; j++ ) m [i][j] = 0; } // matrix::SetIdentityMatrix () /////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////// // This function sets the m [4][4] to the identity matrix ///////////////////////////////////////// void matrix::SetIdentityMatrix ( void ) { for ( int i = 0; i < 4; i++ ) for ( int j = 0; j < 4; j++ ) m [i][j] = 0; m [0][0] = 1; m [1][1] = 1; m [2][2] = 1; m [3][3] = 1; } // triangle::TransformTriangle () ///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////// // This function transforms triangles using the already worked out matrix called m [][] /////////// void triangle::TransformTriangle ( matrix a ) { for ( int n = 0; n < 3; n++ ) { v[n].gx = a.m [0][0] * v[n].x + a.m [1][0] * v[n].y + a.m [2][0] * v[n].z + a.m [3][0]; v[n].gy = a.m [0][1] * v[n].x + a.m [1][1] * v[n].y + a.m [2][1] * v[n].z + a.m [3][1]; v[n].gz = a.m [0][2] * v[n].x + a.m [1][2] * v[n].y + a.m [2][2] * v[n].z + a.m [3][2]; } } // triangle::RotateNormals () ///////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////// // This function transforms the vertex normals using the rotation matrix m [][] /////////////////// void triangle::RotateNormals ( matrix b ) { gnx = b.m [0][0] * nx + b.m [1][0] * ny + b.m [2][0] * nz + b.m [3][0]; gny = b.m [0][1] * nx + b.m [1][1] * ny + b.m [2][1] * nz + b.m [3][1]; gnz = b.m [0][2] * nx + b.m [1][2] * ny + b.m [2][2] * nz + b.m [3][2]; } // triangle::Cull () ////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////// // This function culls triangles facing away from the viewer ////////////////////////////////////// void triangle::Cull ( void ) { // Local variables float v1 [3]; float result; // Take Vector from eye to point on triangle v1 [X] = ( v[0].gx ); v1 [Y] = ( v[0].gy ); v1 [Z] = ( v[0].gz ); // Calculate angle result = ( gnx * v1[X] ) + ( gny * v1[Y] ) + ( gnz * v1[Z] ); // Automatically set triangle to be drawn isVisible = true; // Check if triangle is facing away from viewer if ( result > 0 ) isVisible = false; } // triangle::ComputeNormal () ///////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////// // This function uses cross_product to compute a triangles surface normal ///////////////////////// void triangle::ComputeNormal ( void ) { // Local variables float v1[3]; float v2[3]; // Calculate Vector from point 1 to 0 v1 [X] = v[0].x - v[1].x; v1 [Y] = v[0].y - v[1].y; v1 [Z] = v[0].z - v[1].z; // Calculate Vector from point 2 to 1 v2 [X] = v[1].x - v[2].x; v2 [Y] = v[1].y - v[2].y; v2 [Z] = v[1].z - v[2].z; // Compute magnitude of both Vectors float mag1 = sqrt ( (v1[X] * v1[X]) + (v1[Y] * v1[Y]) + (v1[Z] * v1[Z]) ); float mag2 = sqrt ( (v2[X] * v2[X]) + (v2[Y] * v2[Y]) + (v2[Z] * v2[Z]) ); // Normalize both Vectors for ( int i = 0; i < 3; i++ ) { v1 [i] = ( v1 [i] / mag1 ); v2 [i] = ( v2 [i] / mag2 ); } // Compute Cross Product to give surface normal nx = ( v1[Y] * v2[Z] ) - ( v1[Z] * v2[Y] ); ny = ( v1[Z] * v2[X] ) - ( v1[X] * v2[Z] ); nz = ( v1[X] * v2[Y] ) - ( v1[Y] * v2[X] ); } // triangle::ConvertToScreen () /////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////// // This function converts global co-ordinates of a triangle to screen x, y co-ordinates /////////// void triangle::ConvertToScreen ( void ) { for ( int i = 0; i < 3; i++ ) { if ( v[i].gz > 0.1f ) { v[i].sc_x = ( ( v[i].gx * 620.0f ) / ( v[i].gz ) ) + 320.0f; v[i].sc_y = ( ( - ( v[i].gy * 600.0f ) ) / ( v[i].gz ) ) + 240.0f; } } } // triangle::DrawLine () ////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////// // This function draws a single color horizontal line ///////////////////////////////////////////// void triangle::DrawLine ( int x1, int x2, int y ) { // Check line is visible if ( !( x1 < 0 && x2 < 0 ) && !( x1 > 639 && x2 > 639 ) ) { // Make sure we are drawing from left to right if ( x1 > x2 ) { int temp_x = x1; x1 = x2; x2 = temp_x; } // Make sure left point is on screen if ( x1 < 0 ) x1 = 0; // Make sure right point is on screen if ( x2 > 639 ) x2 = 639; // Offset to top left screen pixel int offset = x1 + y * words_per_line; // Draw the line pixel by pixel for ( int i = x1; i <= x2; i++ ) { *(video_buffer + offset) = faceColor.combined; offset++; } } } // triangle::DrawFB () //////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////// // This function draws a flat-bottomed triangle ////////////////////////////////////////////////// void triangle::DrawFB ( void ) { float dx_left = 0; // Change in x value of left sloping line per scaline (per y) float dx_right = 0; // right float cur_left = 0; // Current left end of line float cur_right = 0; // right float height = v[1].sc_y - v[0].sc_y; // height of triangle, positive only if ( height < 0.5 && height > -0.5 ) return; // do not draw a zero height triangle dx_left = ( x4 - v[0].sc_x ) / height; dx_right = ( v[1].sc_x - v[0].sc_x ) / height; cur_left = cur_right = v[0].sc_x; // first line will be a single dot on x1 for ( int y = v[0].sc_y; y <= v[1].sc_y; y++ ) // go from top to bottom { if ( y > -1 && y < 480 ) // Only draw line if it's on the screen vertically { DrawLine ( cur_left, cur_right, y ); } cur_left += dx_left; cur_right += dx_right; } } // triangle::DrawFT () //////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////// // This function draws a flat-topped Triangle ///////////////////////////////////////////////////// void triangle::DrawFT ( void ) { float dx_left = 0; float dx_right = 0; float cur_left = 0; float cur_right = 0; float height = v[2].sc_y - v[1].sc_y; // height of triangle, positive only if ( height < 0.5 && height > -0.5 ) return; // do not draw a zero height triangle dx_left = ( x4 - v[2].sc_x ) / height; dx_right = ( v[1].sc_x - v[2].sc_x ) / height; cur_left = cur_right = v[2].sc_x; for ( int y = v[2].sc_y; y >= v[1].sc_y; y-- ) // go from top to bottom { if ( y > -1 && y < 480 ) // Only draw line if on screen vertically { DrawLine ( cur_left, cur_right, y ); } cur_left += dx_left; cur_right += dx_right; } } // triangle::Draw () ////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////// // This function starts the process of drawing a triangle ///////////////////////////////////////// void triangle::Draw ( void ) { // Local variables int temp_x = 0; int temp_y = 0; // Sort screen so-ordinates in ascending y order // Make sure point 1 is below (on screen) point 0 if ( v[1].sc_y < v[0].sc_y ) { temp_x = v[1].sc_x; temp_y = v[1].sc_y; v[1].sc_x = v[0].sc_x; v[1].sc_y = v[0].sc_y; v[0].sc_x = temp_x; v[0].sc_y = temp_y; } // Make sure point 2 is below (on screen) point 0 if ( v[2].sc_y < v[0].sc_y ) { temp_x = v[2].sc_x; temp_y = v[2].sc_y; v[2].sc_x = v[0].sc_x; v[2].sc_y = v[0].sc_y; v[0].sc_x = temp_x; v[0].sc_y = temp_y; } // Make sure point 2 is below (on screen) point 1 if ( v[2].sc_y < v[1].sc_y ) { temp_x = v[2].sc_x; temp_y = v[2].sc_y; v[2].sc_x = v[1].sc_x; v[2].sc_y = v[1].sc_y; v[1].sc_x = temp_x; v[1].sc_y = temp_y; } // Calculate top height divided by total height float ratio = float ( v[1].sc_y - v[0].sc_y ) / float ( v[2].sc_y - v[0].sc_y ); // Calculate fourth x point x4 = ( (v[2].sc_x - v[0].sc_x) * ratio ) + v[0].sc_x; DrawFT (); DrawFB (); } // SetSineTable () //////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////// // This function enters the float values into our sine trig table ///////////////////////////////// void SetSineTable ( void ) { for ( int angle = 0; angle < 3600; angle++ ) SIN [angle] = float ( sin ( float ( angle ) / 572.9577951f ) ); } // SetCosineTable () ////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////// // This function enters the float values into our cosine trig table /////////////////////////////// void SetCosineTable ( void ) { for ( int angle = 0; angle < 3600; angle++ ) COS [angle] = float ( cos ( float ( angle ) / 572.9577951f ) ); } // mesh::LoadMesh () ////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////// // This function loads a mesh from disk /////////////////////////////////////////////////////////// char* mesh::LoadMesh ( char* filename ) { // Create ifstream object associates with correct file ifstream in( filename ); // Return error if file wasn't openned if (!in) { return ( "Couldn't find mesh file." ); } // Get number of triangles from file in >> numTriangles; // Allocate space for correct number of triangles dynamically faces = new triangle [ cube.numTriangles ]; // Get x,y,z position of mesh in >> xPos; in >> yPos; in >> zPos; // Get x,y,z rotation of mesh in >> xAngle; in >> yAngle; in >> zAngle; // Get mesh size in >> size; // Load vertices for ( int i = 0; i < numTriangles; i++ ) { // Loop through the three vertices for ( int j = 0; j < 3; j++ ) { // Load x,y,z values of vertex in >> (faces + i)->v[j].x; in >> (faces + i)->v[j].y; in >> (faces + i)->v[j].z; // Load texture co-ordinates in >> (faces + i)->v[j].tu; in >> (faces + i)->v[j].tv; } // Load x,y,z values of normal in >> (faces + i)->nx; in >> (faces + i)->ny; in >> (faces + i)->nz; // Load face color in >> (faces + i)->faceColor.red; in >> (faces + i)->faceColor.green; in >> (faces + i)->faceColor.blue; // Set comibned face color (faces + i)->faceColor.combined = ( ( (faces + i)->faceColor.blue % 32 ) + ( ( (faces + i)->faceColor.green % 64 ) << 6 ) + ( ( (faces + i)->faceColor.red % 32 ) << 11 ) ); } // Return success return ( NULL ); }