Original Post
Hey ive been programming for about 1 year in C/C++ recently ive been trying to implement the 3DS loader from www.gametutials.com into my devloping game. I keep getting these errors tho and cannot fix them
Help Please
Errors =
--------------------Configuration: Drag Race - Win32 Debug--------------------
Compiling...
Camera.cpp
c:\program files\microsoft visual studio\myprojects\drag race\globals.hpp(94) : error C2143: syntax error : missing '';'' before ''*''
c:\program files\microsoft visual studio\myprojects\drag race\globals.hpp(94) : error C2501: ''CVector2'' : missing storage-class or type specifiers
c:\program files\microsoft visual studio\myprojects\drag race\globals.hpp(94) : error C2501: ''pTexVerts'' : missing storage-class or type specifiers
Main.cpp
c:\program files\microsoft visual studio\myprojects\drag race\globals.hpp(94) : error C2143: syntax error : missing '';'' before ''*''
c:\program files\microsoft visual studio\myprojects\drag race\globals.hpp(94) : error C2501: ''CVector2'' : missing storage-class or type specifiers
c:\program files\microsoft visual studio\myprojects\drag race\globals.hpp(94) : error C2501: ''pTexVerts'' : missing storage-class or type specifiers
c:\program files\microsoft visual studio\myprojects\drag race\main.cpp(373) : error C2039: ''pTexVerts'' : is not a member of ''t3DObject''
c:\program files\microsoft visual studio\myprojects\drag race\globals.hpp(85) : see declaration of ''t3DObject''
c:\program files\microsoft visual studio\myprojects\drag race\main.cpp(374) : error C2039: ''pTexVerts'' : is not a member of ''t3DObject''
c:\program files\microsoft visual studio\myprojects\drag race\globals.hpp(85) : see declaration of ''t3DObject''
c:\program files\microsoft visual studio\myprojects\drag race\main.cpp(374) : error C2228: left of ''.x'' must have class/struct/union type
c:\program files\microsoft visual studio\myprojects\drag race\main.cpp(374) : error C2039: ''pTexVerts'' : is not a member of ''t3DObject''
c:\program files\microsoft visual studio\myprojects\drag race\globals.hpp(85) : see declaration of ''t3DObject''
c:\program files\microsoft visual studio\myprojects\drag race\main.cpp(374) : error C2228: left of ''.y'' must have class/struct/union type
c:\program files\microsoft visual studio\myprojects\drag race\main.cpp(495) : error C2039: ''pTexVerts'' : is not a member of ''t3DObject''
c:\program files\microsoft visual studio\myprojects\drag race\globals.hpp(85) : see declaration of ''t3DObject''
3ds.cpp
c:\program files\microsoft visual studio\myprojects\drag race\globals.hpp(94) : error C2143: syntax error : missing '';'' before ''*''
c:\program files\microsoft visual studio\myprojects\drag race\globals.hpp(94) : error C2501: ''CVector2'' : missing storage-class or type specifiers
c:\program files\microsoft visual studio\myprojects\drag race\globals.hpp(94) : error C2501: ''pTexVerts'' : missing storage-class or type specifiers
c:\program files\microsoft visual studio\myprojects\drag race\3ds.cpp(475) : error C2039: ''pTexVerts'' : is not a member of ''t3DObject''
c:\program files\microsoft visual studio\myprojects\drag race\globals.hpp(85) : see declaration of ''t3DObject''
c:\program files\microsoft visual studio\myprojects\drag race\3ds.cpp(478) : error C2039: ''pTexVerts'' : is not a member of ''t3DObject''
c:\program files\microsoft visual studio\myprojects\drag race\globals.hpp(85) : see declaration of ''t3DObject''
c:\program files\microsoft visual studio\myprojects\drag race\3ds.cpp(714) : error C2552: ''vSum'' : non-aggregates cannot be initialized with initializer list
Error executing cl.exe.
Drag Race.exe - 18 error(s), 0 warning(s)
Code Below
COMPILER SAYS PROBLEM IS IN HERE!
//GLOBAL DECLARATIONS GO HERE!!!
#ifndef _GLOBALS_H
#define _GLOBALS_H
//Includes
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <fstream>
#include <vector>
#include <gl\gl.h> // Header File For The OpenGL32 Library
#include <gl\glu.h> // Header File For The GLu32 Library
#include <gl\glaux.h>
#include <crtdbg.h>
using namespace std;
#define SCREEN_WIDTH 800 // We want our screen width 800 pixels
#define SCREEN_HEIGHT 600 // We want our screen height 600 pixels
#define SCREEN_DEPTH 16
#define MAX_TEXTURES 100 // This says how many texture we will be using
//VECTOR CLASS
class CVector3 {
public: //Public Access
// A default constructor
CVector3() {}
// This is our constructor that allows us to initialize our data upon creating an instance
CVector3(float X, float Y, float Z)
{
x = X; y = Y; z = Z;
}
// Here we overload the + operator so we can add vectors together
CVector3 operator+(CVector3 vVector)
{
// Return the added vectors result.
return CVector3(vVector.x + x, vVector.y + y, vVector.z + z);
}
// Here we overload the - operator so we can subtract vectors
CVector3 operator-(CVector3 vVector)
{
// Return the subtracted vectors result
return CVector3(x - vVector.x, y - vVector.y, z - vVector.z);
}
// Here we overload the * operator so we can multiply by scalars
CVector3 operator*(float num)
{
// Return the scaled vector
return CVector3(x * num, y * num, z * num);
}
// Here we overload the / operator so we can divide by a scalar
CVector3 operator/(float num)
{
// Return the scale vector
return CVector3(x / num, y / num, z / num);
}
float x, y, z;
};
struct tFace
{
int vertIndex[3]; // indicies for the verts that make up this triangle
int coordIndex[3]; // indicies for the tex coords to texture this face
};
struct tMaterialInfo
{
char strName[255]; // The texture name
char strFile[255]; // The texture file name (If this is set it''s a texture map)
BYTE color[3]; // The color of the object (R, G, B)
int texureId; // the texture ID
float uTile; // u tiling of texture (Currently not used)
float vTile; // v tiling of texture (Currently not used)
float uOffset; // u offset of texture (Currently not used)
float vOffset; // v offset of texture (Currently not used)
};
struct t3DObject
{
int numOfVerts; // The number of verts in the model
int numOfFaces; // The number of faces in the model
int numTexVertex; // The number of texture coordinates
int materialID; // The texture ID to use, which is the index into our texture array
bool bHasTexture; // This is TRUE if there is a texture map for this object
char strName[255]; // The name of the object
CVector3 *pVerts; // The object''s vertices
CVector3 *pNormals; // The object''s normals
CVector2 *pTexVerts; // The texture''s UV coordinates
tFace *pFaces; // The faces information of the object
};
struct t3DModel
{
int numOfObjects; // The number of objects in the model
int numOfMaterials; // The number of materials for the model
vector<tMaterialInfo> pMaterials; // The list of material information (Textures and colors)
vector<t3DObject> pObject; // The object list for our model
};
class CVector2 //Used to store UV coordinates
{
public:
float x, y;
};
//CAMERA CLASS prototype
class CCamera{
public:
CVector3 m_vPosition; //Camera Position
CVector3 m_vView; //Camera View
CVector3 m_vUpVector; //Up Direction
//m = member Variables
//Camera Constructor
CCamera();
void RotateView(float angle, float x, float y, float z);
void PositionCamera(float positionX,float positionY,float positionZ,
float viewX,float viewY,float viewZ,
float upVectorX, float upVectorY, float upVectorZ);
void MoveCamera(float speed);
void SetViewByMouse();
void Look();
};
extern CCamera g_Camera; //Allows all source files to use this
#endif
Main.cpp
/*Drag Racing Objectives
1)Draw 3D world of track
2)Have single car race for ET and reaction time plus Speed(MPH)
3)Allow modifictions to car and setup */
#pragma comment(lib, "opengl32.lib")
#pragma comment(lib, "glu32.lib")
#pragma comment(lib, "glaux.lib")
#include "Globals.hpp"
#include "3ds.h"
#define CameraSpeed 0.2f
HWND hwnd = NULL;//handle to window
HINSTANCE hInstance;//handle to instance of application
HDC hDC = NULL;//handle to Device Context
HGLRC hRC = NULL;//handle to Rendering Context;
CCamera g_Camera; //our camera!
unsigned int g_Texture[MAX_TEXTURES];
CLoad3DS g_Load3ds; // This is 3DS class. This should go in a good model class.
t3DModel g_3DModel; // This holds the 3D Model info that we load in
void KillOpenGL();
void CreateTexture(UINT textureArray[], LPSTR strFileName, int textureID)
{
AUX_RGBImageRec *pBitmap = NULL;
if(!strFileName) // Return from the function if no file name was passed in
return;
pBitmap = auxDIBImageLoad(strFileName); // Load the bitmap and store the data
if(pBitmap == NULL) // If we can''t load the file, quit!
exit(0);
// Generate a texture with the associative texture ID stored in the array
glGenTextures(1, &textureArray[textureID]);
// Bind the texture to the texture arrays index and init the texture
glBindTexture(GL_TEXTURE_2D, textureArray[textureID]);
gluBuild2DMipmaps(GL_TEXTURE_2D, 3, pBitmap->sizeX, pBitmap->sizeY, GL_RGB, GL_UNSIGNED_BYTE, pBitmap->data);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_NEAREST);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR_MIPMAP_LINEAR);
// Now we need to free the bitmap data that we loaded since openGL stored it as a texture
if (pBitmap) // If we loaded the bitmap
{
if (pBitmap->data) // If there is texture data
{
free(pBitmap->data); // Free the texture data, we don''t need it anymore
}
free(pBitmap); // Free the bitmap structure
}
}
void CheckForMovement()
{
if(GetKeyState(VK_UP) & 0x80)
{
g_Camera.MoveCamera(CameraSpeed);
}
if(GetKeyState(VK_DOWN) & 0x80)
{
g_Camera.MoveCamera(-CameraSpeed);
}
}
void KillOpenGL()
{
glDeleteTextures(1,&g_Texture[0]);
glDeleteTextures(1,&g_Texture[1]);
//deselect rending context and delete it
wglMakeCurrent(hDC,NULL);
wglDeleteContext(hRC);
ShowCursor(true); //Hide Mouse Cursor
//post quit message
PostQuitMessage(0);
}
void SetUpGL()
{
glShadeModel(GL_SMOOTH);//smooth colors blending
glEnable(GL_TEXTURE_2D);
glEnable(GL_DEPTH_TEST);
g_Camera.PositionCamera(0, 0.5f, 6, 0, 0.5f, 0, 0, 1, 0); //Camera Start Position!
ShowCursor(false); //Hide Mouse Cursor
CreateTexture(g_Texture, "Walls.bmp", 0);
CreateTexture(g_Texture, "Track.bmp",1);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
g_Load3ds.Import3DS(&g_3DModel, "CAMARO.3ds"); // Load our .3DS file into our model structure
// Depending on how many textures we found, load each one (Assuming .BMP)
// If you want to load other files than bitmaps, you will need to adjust CreateTexture().
// Below, we go through all of the materials and check if they have a texture map to load.
// Otherwise, the material just holds the color information and we don''t need to load a texture.
// Go through all the materials
for(int i = 0; i < g_3DModel.numOfMaterials; i++)
{
// Check to see if there is a file name to load in this material
if(strlen(g_3DModel.pMaterials[i].strFile) > 0)
{
// Use the name of the texture file to load the bitmap, with a texture ID (i).
// We pass in our global texture array, the name of the texture, and an ID to reference it.
CreateTexture(g_Texture, g_3DModel.pMaterials[i].strFile, i);
}
// Set the texture ID for this material
g_3DModel.pMaterials[i].texureId = i;
}
// Here, we turn on a lighting and enable lighting. We don''t need to
// set anything else for lighting because we will just take the defaults.
// We also want color, so we turn that on
glEnable(GL_LIGHT0); // Turn on a light with defaults set
glEnable(GL_LIGHTING); // Turn on lighting
glEnable(GL_COLOR_MATERIAL); // Allow color
//////////// *** NEW *** ////////// *** NEW *** ///////////// *** NEW *** ////////////////////
}
bool SetupPixelFormat(HDC hDC)
{
int nPixelFormat;
static PIXELFORMATDESCRIPTOR pfd = {
sizeof(PIXELFORMATDESCRIPTOR),//always set to this
1,//version always set to 1
PFD_DRAW_TO_WINDOW| //support window
PFD_SUPPORT_OPENGL| //support opengl
PFD_DOUBLEBUFFER, //support double buffering
PFD_TYPE_RGBA, //color mode
32, //go for 32 bit color mode
0,0,0,0,0,0,//ignore color bits, not used
0,//no alpha buffer
0,//ignore shift bits
0,//no accumulation buffer
0,0,0,0,//ignore accumulation bits
16,//16-bit Z buffer size
0,//no stencil buffer
0,//no auxiliary buffer
PFD_MAIN_PLANE,//main drawing plane
0,//reserved
0,0,0};//layer masks ignored
//choose best matching pixel format, return index as int
nPixelFormat = ChoosePixelFormat(hDC,&pfd);
SetPixelFormat(hDC,nPixelFormat,&pfd);//set it!
return true;
}
//window message handler
LRESULT CALLBACK WndProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)
{
int height, width;//hold window width and height
switch(message)//switch for sent windows message
{
case WM_CREATE://when a window is created
hDC = GetDC(hwnd);//get the device context for the window pointed to by hwnd
SetupPixelFormat(hDC);//setup pixel format according to hDC
//create rendering context and set it as current
hRC = wglCreateContext(hDC);
wglMakeCurrent(hDC,hRC);
return 0;
break;
case WM_CLOSE://window closes
PostQuitMessage(0);//close window
return 0;
break;
case WM_KEYDOWN:
switch(wParam)
{// Check if we hit a key
case VK_ESCAPE: // If we hit the escape key
PostQuitMessage(0); // Send a QUIT message to the window
return 0;
break;
}
break;
case WM_SIZE://case window is resized
height = HIWORD(lParam);//get height from message
width = LOWORD(lParam);//get width from message
if(height == 0)//avoid devide by 0
height = 1;
//reset the viewport to new dimentions
glViewport(0,0,width,height);
glMatrixMode(GL_PROJECTION);//set projection matrix
glLoadIdentity();//reset projection matrix
//calculate aspect ratio of window
gluPerspective(45.0f,(GLfloat)width/(GLfloat)height,1.0f,1000.0f);
glMatrixMode(GL_MODELVIEW);//set modelvie matrix
glLoadIdentity();//load identity Matrix[1,0,0]
// [0,1,0]
// [0,0,1]
return 0;
break;//break out to switch statement
default:
break;
}
return (DefWindowProc(hwnd,message,wParam,lParam));//send all unhandled message to windows
}
void RenderScene()
{
//every.1 = 1 foot
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);//clear buffer
glLoadIdentity();//reset model view matrix
g_Camera.Look();
glBindTexture(GL_TEXTURE_2D,g_Texture[0]);
glPushMatrix();
glTranslatef(-20.0f,0.0f,0.0f);
glBegin(GL_QUADS);
//***TRACK WALLS***//
//LEFT WALL
glTexCoord2d(0.0,0.0);glVertex3f(-0.25f,-1.0f,0.0f);//bottom left
glTexCoord2d(2.5,0.0);glVertex3f(-0.25f,1.0f,0.0f);
glTexCoord2d(2.5,1.0);glVertex3f(0.25f,1.0f,0.0f);
glTexCoord2d(0.0,1.0);glVertex3f(0.25f,-1.0f,0.0f);
glTexCoord2d(0.0,0.0);glVertex3f(0.25f,-1.0f,0.0f);//bottomleft
glTexCoord2d(0.0,1.5);glVertex3f(0.25f,1.0f,0.0f);
glTexCoord2d(200.0,1.5);glVertex3f(0.25f,1.0f,-500.0f);
glTexCoord2d(200,0.0);glVertex3f(0.25f,-1.0f,-500.0f);
glVertex3f(0.25f,-1.0f,-500.0f);
glVertex3f(0.25f,1.0f,-500.0f);
glVertex3f(-0.25f,1.0f,-500.0f);
glVertex3f(-0.25f,-1.0f,-500.0f);
glTexCoord2d(0.0,0.0);glVertex3f(-0.25f,-1.0f,-500.0f);
glTexCoord2d(0.0,1.5);glVertex3f(-0.25f,1.0f,-500.0f);
glTexCoord2d(200.0,1.5);glVertex3f(-0.25f,1.0f,0.0f);
glTexCoord2d(200,0.0);glVertex3f(-0.25f,-1.0f,0.0f);
//***TRACK WALLS END***/
glEnd();
glTranslatef(40.0f,0.0f,0.0f);
glBegin(GL_QUADS);
//***TRACK WALLS***//
//LEFT WALL
glTexCoord2d(0.0,0.0);glVertex3f(-0.25f,-1.0f,0.0f);//bottom left
glTexCoord2d(2.5,0.0);glVertex3f(-0.25f,1.0f,0.0f);
glTexCoord2d(2.5,1.0);glVertex3f(0.25f,1.0f,0.0f);
glTexCoord2d(0.0,1.0);glVertex3f(0.25f,-1.0f,0.0f);
glTexCoord2d(0.0,0.0);glVertex3f(0.25f,-1.0f,0.0f);//bottomleft
glTexCoord2d(0.0,1.5);glVertex3f(0.25f,1.0f,0.0f);
glTexCoord2d(200.0,1.5);glVertex3f(0.25f,1.0f,-500.0f);
glTexCoord2d(200,0.0);glVertex3f(0.25f,-1.0f,-500.0f);
glVertex3f(0.25f,-1.0f,-500.0f);
glVertex3f(0.25f,1.0f,-500.0f);
glVertex3f(-0.25f,1.0f,-500.0f);
glVertex3f(-0.25f,-1.0f,-500.0f);
glTexCoord2d(0.0,0.0);glVertex3f(-0.25f,-1.0f,-500.0f);
glTexCoord2d(0.0,1.5);glVertex3f(-0.25f,1.0f,-500.0f);
glTexCoord2d(200.0,1.5);glVertex3f(-0.25f,1.0f,0.0f);
glTexCoord2d(200,0.0);glVertex3f(-0.25f,-1.0f,0.0f);
//***TRACK WALLS END***/
glEnd();
glBindTexture(GL_TEXTURE_2D,g_Texture[1]);
//**TRACK**//
glBegin(GL_QUADS);
glTexCoord2d(0.0,0.0);glVertex3f(-0.25f,-1.0f,0.0f);
glTexCoord2d(0.0,200.0);glVertex3f(-0.25f,-1.0f,-500.0f);
glTexCoord2d(30.0,200.0);glVertex3f(-39.75f,-1.0f,-500.0f);
glTexCoord2d(30.0,0.0);glVertex3f(-39.75f,-1.0f,0.0f);
glEnd();
glPopMatrix();
// I am going to attempt to explain what is going on below up here as not to clutter the
// code below. We have a model that has a certain amount of objects and textures. We want
// to go through each object in the model, bind it''s texture map to it, then render it.
// To render the current object, we go through all of it''s faces (Polygons).
// What is a face you ask? A face is just (in this case) a triangle of the object.
// For instance, a cube has 12 faces because each side has 2 triangles.
// You might be thinking. Well, if there are 12 faces in a cube, that makes
// 36 vertices that we needed to read in for that object. Not really true. Because
// a lot of the vertices are the same, since they share sides, they only need to save
// 8 vertices, and ignore the duplicates. Then, you have an array of all the
// unique vertices in that object. No 2 vertices will be the same. This cuts down
// on memory. Then, another array is saved, which is the index numbers for each face,
// which index in to that array of vertices. That might sound silly, but it is better
// than saving tons of duplicate vertices. The same thing happens for UV coordinates.
// You don''t save duplicate UV coordinates, you just save the unique ones, then an array
// that index''s into them. This might be confusing, but most 3D files use this format.
// This loop below will stay the same for most file formats that you load, so all you need
// to change is the loading code. You don''t need to change this loop (Except for animation).
// Since we know how many objects our model has, go through each of them.
for(int i = 0; i < g_3DModel.numOfObjects; i++)
{
// Make sure we have valid objects just in case. (size() is in the vector class)
if(g_3DModel.pObject.size() <= 0) break;
// Get the current object that we are displaying
t3DObject *pObject = &g_3DModel.pObject[i];
// Check to see if this object has a texture map, if so bind the texture to it.
if(pObject->bHasTexture) {
// Turn on texture mapping and turn off color
glEnable(GL_TEXTURE_2D);
// Reset the color to normal again
glColor3ub(255, 255, 255);
// Bind the texture map to the object by it''s materialID
glBindTexture(GL_TEXTURE_2D, g_Texture[pObject->materialID]);
} else {
// Turn off texture mapping and turn on color
glDisable(GL_TEXTURE_2D);
// Reset the color to normal again
glColor3ub(255, 255, 255);
}
// This determines if we are in wireframe or normal mode
glBegin(GL_TRIANGLES); // Begin drawing with our selected mode (triangles or lines)
// Go through all of the faces (polygons) of the object and draw them
for(int j = 0; j < pObject->numOfFaces; j++)
{
// Go through each corner of the triangle and draw it.
for(int whichVertex = 0; whichVertex < 3; whichVertex++)
{
// Get the index for each point of the face
int index = pObject->pFaces[j].vertIndex[whichVertex];
// Give OpenGL the normal for this vertex.
glNormal3f(pObject->pNormals[ index ].x, pObject->pNormals[ index ].y, pObject->pNormals[ index ].z);
// If the object has a texture associated with it, give it a texture coordinate.
if(pObject->bHasTexture) {
// Make sure there was a UVW map applied to the object or else it won''t have tex coords.
if(pObject->pTexVerts) {
glTexCoord2f(pObject->pTexVerts[ index ].x, pObject->pTexVerts[ index ].y);
}
} else {
// Make sure there is a valid material/color assigned to this object.
// You should always at least assign a material color to an object,
// but just in case we want to check the size of the material list.
// if the size is at least one, and the material ID != -1,
// then we have a valid material.
if(g_3DModel.pMaterials.size() && pObject->materialID >= 0)
{
// Get and set the color that the object is, since it must not have a texture
BYTE *pColor = g_3DModel.pMaterials[pObject->materialID].color;
// Assign the current color to this model
glColor3ub(pColor[0], pColor[1], pColor[2]);
}
}
// Pass in the current vertex of the object (Corner of current face)
glVertex3f(pObject->pVerts[ index ].x, pObject->pVerts[ index ].y, pObject->pVerts[ index ].z);
}
}
glEnd(); // End the drawing
}
}
//the main entry point
int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nShowCmd)
{
WNDCLASSEX ws;//window class
MSG msg; //message
bool done; //flag saying when we are done
//class structure
ws.cbClsExtra = 0;//nothing extra
ws.cbSize = sizeof(WNDCLASSEX);//always set to this
ws.cbWndExtra = 0;//nothing extra
ws.hbrBackground = NULL;//no need for a backgound
ws.hCursor = LoadCursor(NULL, IDC_ARROW);//default cursor
ws.hIcon = NULL;//no icon
ws.hIconSm = NULL;//no tray icon
ws.hInstance = hInstance;//instance of APP
ws.lpfnWndProc = WndProc;//message handler for window
ws.lpszClassName = "My Class";//class name
ws.lpszMenuName = NULL;//no menu
ws.style = CS_HREDRAW|CS_VREDRAW;//when resized redraw screen
//register the class
if(!RegisterClassEx(&ws))
{
MessageBox(NULL,"ERROR","Unable To Register Class!",MB_OK);
return 0;//fails registering exit
}
//create window
hwnd = CreateWindowEx(NULL,//extended style
"My Class",//class name
"Matt Janas",//app name
WS_OVERLAPPEDWINDOW|WS_VISIBLE|WS_SYSMENU|
WS_CLIPCHILDREN|
WS_CLIPSIBLINGS,//style don''t draw unseen parts of windows
100,100,//x,y
800,600,//width height
NULL,//handle to parent
NULL,//handle to menu
hInstance,//app instance
NULL);//no extra params
//check to see if window was created
if(!hwnd)
{
MessageBox(NULL,"ERROR","Unable To Create Window!",MB_OK);
return 0; //quit
}
ShowWindow(hwnd,SW_SHOW);//display window
UpdateWindow(hwnd);//update window
SetUpGL();//go to set up GL
done = false;//flag to start main loop
//main message loop
while(!done)
{
if(PeekMessage(&msg,NULL,0,0,PM_REMOVE))//check for messages
{
if(msg.message == WM_QUIT)//if there is a QUIT message
done = true;//end APP
TranslateMessage(&msg);
DispatchMessage(&msg);
} //translate and dispatch to event queue
else
{
//rendering in here!
g_Camera.SetViewByMouse();
CheckForMovement(); //Check Keys
RenderScene();//draw stuff
SwapBuffers(hDC);//swap buffer Back to Front
}
}
// When we are done, we need to free all the model data
// We do this by walking through all the objects and freeing their information
// Go through all the objects in the scene
for(int i = 0; i < g_3DModel.numOfObjects; i++)
{
// Free the faces, normals, vertices, and texture coordinates.
delete [] g_3DModel.pObject[i].pFaces;
delete [] g_3DModel.pObject[i].pNormals;
delete [] g_3DModel.pObject[i].pVerts;
delete [] g_3DModel.pObject[i].pTexVerts;
}
KillOpenGL();
return msg.wParam;
}
Camera.cpp
#include "Globals.hpp"
#define SCREEN_HEIGHT 600
#define SCREEN_WIDTH 800
CVector3 Cross(CVector3 vVector1, CVector3 vVector2)
{
CVector3 vNormal; // The vector to hold the cross product
// The X value for the vector is: (V1.y * V2.z) - (V1.z * V2.y) // Get the X value
vNormal.x = ((vVector1.y * vVector2.z) - (vVector1.z * vVector2.y));
// The Y value for the vector is: (V1.z * V2.x) - (V1.x * V2.z)
vNormal.y = ((vVector1.z * vVector2.x) - (vVector1.x * vVector2.z));
// The Z value for the vector is: (V1.x * V2.y) - (V1.y * V2.x)
vNormal.z = ((vVector1.x * vVector2.y) - (vVector1.y * vVector2.x));
// Return the cross product
return vNormal;
}
float Magnitude(CVector3 vNormal)
{
return (float)sqrt( (vNormal.x * vNormal.x) +
(vNormal.y * vNormal.y) +
(vNormal.z * vNormal.z) );
}
CVector3 Normalize(CVector3 vVector)
{
// Get the magnitude of our normal
float magnitude = Magnitude(vVector);
vVector = vVector / magnitude;
// Finally, return our normalized vector
return vVector;
}
//Camera Class Definitions
//Constructor
CCamera::CCamera()
{
CVector3 vZero = CVector3(0.0, 0.0, 0.0); // Init a vVector to 0 0 0 for our position
CVector3 vView = CVector3(0.0, 1.0, 0.5); // Init a starting view vVector (looking up and out the screen)
CVector3 vUp = CVector3(0.0, 0.0, 1.0); // Init a standard up vVector (Rarely ever changes)
m_vPosition = vZero; // Init the position to zero
m_vView = vView; // Init the view to a std starting view
m_vUpVector = vUp; // Init the UpVector
}
GLvoid CCamera::PositionCamera(float positionX,float positionY,float positionZ,
float viewX,float viewY,float viewZ,
float upVectorX,float upVectorY,float upVectorZ)
{
CVector3 vPosition = CVector3(positionX, positionY, positionZ);
CVector3 vView = CVector3(viewX, viewY, viewZ);
CVector3 vUpVector = CVector3(upVectorX, upVectorY, upVectorZ);
m_vPosition = vPosition; // Assign the position
m_vView = vView; // Assign the view
m_vUpVector = vUpVector; // Assign the up vector
}
void CCamera::MoveCamera(float speed)
{
CVector3 vVector = CVector3(0.0f,0.0f,0.0f);
//V = v - p (Get direction were facing!)
vVector.x = m_vView.x - m_vPosition.x; //Get x direction
vVector.y = m_vView.y - m_vPosition.y; //get y direction
vVector.z = m_vView.z - m_vPosition.z; //get z direction
m_vPosition.x += vVector.x * speed; // Add our acceleration to our position''s X
m_vPosition.z += vVector.z * speed; // Add our acceleration to our position''s Z
m_vView.x += vVector.x * speed; // Add our acceleration to our view''s X
m_vView.z += vVector.z * speed; // Add our acceleration to our view''s Z
}
void CCamera::RotateView(float angle,float x,float y,float z)
{
CVector3 vNewView;
CVector3 vView;
//Get Direction we''re facing
vView.x = m_vView.x - m_vPosition.x; // This gets the direction of the X
vView.y = m_vView.y - m_vPosition.y; // This gets the direction of the Y
vView.z = m_vView.z - m_vPosition.z; // This gets the direction of the Z
float cosTheta = (float)cos(angle);
float sinTheta = (float)sin(angle);
// Find the new x position for the new rotated point
vNewView.x = (cosTheta + (1 - cosTheta) * x * x) * vView.x;
vNewView.x += ((1 - cosTheta) * x * y - z * sinTheta) * vView.y;
vNewView.x += ((1 - cosTheta) * x * z + y * sinTheta) * vView.z;
// Find the new y position for the new rotated point
vNewView.y = ((1 - cosTheta) * x * y + z * sinTheta) * vView.x;
vNewView.y += (cosTheta + (1 - cosTheta) * y * y) * vView.y;
vNewView.y += ((1 - cosTheta) * y * z - x * sinTheta) * vView.z;
// Find the new z position for the new rotated point
vNewView.z = ((1 - cosTheta) * x * z - y * sinTheta) * vView.x;
vNewView.z += ((1 - cosTheta) * y * z + x * sinTheta) * vView.y;
vNewView.z += (cosTheta + (1 - cosTheta) * z * z) * vView.z;
// Now we just add the newly rotated vector to our position to set
// our new rotated view of our camera.
m_vView.x = m_vPosition.x + vNewView.x;
m_vView.y = m_vPosition.y + vNewView.y;
m_vView.z = m_vPosition.z + vNewView.z;
}
void CCamera::SetViewByMouse()
{
POINT mousePos; // This is a window structure that holds an X and Y
int middleX = SCREEN_WIDTH >> 1; // This is a binary shift to get half the width
int middleY = SCREEN_HEIGHT >> 1; // This is a binary shift to get half the height
float angleY = 0.0f; // This is the direction for looking up or down
float angleZ = 0.0f; // This will be the value we need to rotate around the Y axis (Left and Right)
static float currentRotX = 0.0f;
// Get the mouse''s current X,Y position
GetCursorPos(&mousePos);
// If our cursor is still in the middle, we never moved... so don''t update the screen
if( (mousePos.x == middleX) && (mousePos.y == middleY) ) return;
// Set the mouse position to the middle of our window
SetCursorPos(middleX, middleY);
// Get the direction the mouse moved in, but bring the number down to a reasonable amount
angleY = (float)( (middleX - mousePos.x) ) / 1000.0f;
angleZ = (float)( (middleY - mousePos.y) ) / 1000.0f;
static float lastRotX = 0.0f;
lastRotX = currentRotX; // We store off the currentRotX and will use it in when the angle is capped
currentRotX += angleZ;
// If the current rotation (in radians) is greater than 1.0, we want to cap it.
if(currentRotX > 1.0f)
{
currentRotX = 1.0f;
// Rotate by remaining angle if there is any
if(lastRotX != 1.0f)
{
CVector3 vAxis = Cross(m_vView - m_vPosition, m_vUpVector);
vAxis = Normalize(vAxis);
// rotate the camera by the remaining angle (1.0f - lastRotX)
RotateView( 1.0f - lastRotX, vAxis.x, vAxis.y, vAxis.z);
}
}
// Check if the rotation is below -1.0, if so we want to make sure it doesn''t continue
else if(currentRotX < -1.0f)
{
currentRotX = -1.0f;
// Rotate by the remaining angle if there is any
if(lastRotX != -1.0f)
{
CVector3 vAxis = Cross(m_vView - m_vPosition, m_vUpVector);
vAxis = Normalize(vAxis);
RotateView( -1.0f - lastRotX, vAxis.x, vAxis.y, vAxis.z);
}
}
// Otherwise, we can rotate the view around our position
else
{
CVector3 vAxis = Cross(m_vView - m_vPosition, m_vUpVector);
vAxis = Normalize(vAxis);
// Rotate around our perpendicular axis
RotateView(angleZ, vAxis.x, vAxis.y, vAxis.z);
}
// Always rotate the camera around the y-axis
RotateView(angleY, 0, 1, 0);
}
void CCamera::Look()
{
gluLookAt(g_Camera.m_vPosition.x, (g_Camera.m_vPosition.y+4.0f), g_Camera.m_vPosition.z,
g_Camera.m_vView.x, g_Camera.m_vView.y+4.0f, g_Camera.m_vView.z,
g_Camera.m_vUpVector.x, g_Camera.m_vUpVector.y, g_Camera.m_vUpVector.z);
}
3ds.cpp
#include "Globals.hpp"
#include "3ds.h"
#include <assert.h>
// Global
int gBuffer[50000] = {0}; // This is used to read past unwanted data
// This file handles all of the code needed to load a .3DS file.
// Basically, how it works is, you load a chunk, then you check
// the chunk ID. Depending on the chunk ID, you load the information
// that is stored in that chunk. If you do not want to read that information,
// you read past it. You know how many bytes to read past the chunk because
// every chunk stores the length in bytes of that chunk.
///////////////////////////////// CLOAD3DS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
///// This constructor initializes the tChunk data
/////
///////////////////////////////// CLOAD3DS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
CLoad3DS::CLoad3DS()
{
m_FilePointer = NULL;
}
///////////////////////////////// IMPORT 3DS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
///// This is called by the client to open the .3ds file, read it, then clean up
/////
///////////////////////////////// IMPORT 3DS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
bool CLoad3DS::Import3DS(t3DModel *pModel, char *strFileName)
{
char strMessage[255] = {0};
tChunk currentChunk = {0};
// Open the 3DS file
m_FilePointer = fopen(strFileName, "rb");
// Make sure we have a valid file pointer (we found the file)
if(!m_FilePointer)
{
sprintf(strMessage, "Unable to find the file: %s!", strFileName);
MessageBox(NULL, strMessage, "Error", MB_OK);
return false;
}
// Once we have the file open, we need to read the very first data chunk
// to see if it''s a 3DS file. That way we don''t read an invalid file.
// If it is a 3DS file, then the first chunk ID will be equal to PRIMARY (some hex num)
// Read the first chuck of the file to see if it''s a 3DS file
ReadChunk(¤tChunk);
// Make sure this is a 3DS file
if (currentChunk.ID != PRIMARY)
{
sprintf(strMessage, "Unable to load PRIMARY chuck from file: %s!", strFileName);
MessageBox(NULL, strMessage, "Error", MB_OK);
return false;
}
// Now we actually start reading in the data. ProcessNextChunk() is recursive
// Begin loading objects, by calling this recursive function
ProcessNextChunk(pModel, ¤tChunk);
// After we have read the whole 3DS file, we want to calculate our own vertex normals.
ComputeNormals(pModel);
// Clean up after everything
CleanUp();
return true;
}
///////////////////////////////// CLEAN UP \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
///// This function cleans up our allocated memory and closes the file
/////
///////////////////////////////// CLEAN UP \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
void CLoad3DS::CleanUp()
{
if (m_FilePointer) {
fclose(m_FilePointer); // Close the current file pointer
m_FilePointer = NULL;
}
}
///////////////////////////////// PROCESS NEXT CHUNK\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
///// This function reads the main sections of the .3DS file, then dives deeper with recursion
/////
///////////////////////////////// PROCESS NEXT CHUNK\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
void CLoad3DS::ProcessNextChunk(t3DModel *pModel, tChunk *pPreviousChunk)
{
t3DObject newObject = {0}; // This is used to add to our object list
tMaterialInfo newTexture = {0}; // This is used to add to our material list
tChunk currentChunk = {0}; // The current chunk to load
tChunk tempChunk = {0}; // A temp chunk for holding data
// Below we check our chunk ID each time we read a new chunk. Then, if
// we want to extract the information from that chunk, we do so.
// If we don''t want a chunk, we just read past it.
// Continue to read the sub chunks until we have reached the length.
// After we read ANYTHING we add the bytes read to the chunk and then check
// check against the length.
while (pPreviousChunk->bytesRead < pPreviousChunk->length)
{
// Read next Chunk
ReadChunk(¤tChunk);
// Check the chunk ID
switch (currentChunk.ID)
{
case VERSION: // This holds the version of the file
// If the file was made in 3D Studio Max, this chunk has an int that
// holds the file version. Since there might be new additions to the 3DS file
// format in 4.0, we give a warning to that problem.
// However, if the file wasn''t made by 3D Studio Max, we don''t 100% what the
// version length will be so we''ll simply ignore the value
// Read the file version and add the bytes read to our bytesRead variable
currentChunk.bytesRead += fread(gBuffer, 1, currentChunk.length - currentChunk.bytesRead, m_FilePointer);
// If the file version is over 3, give a warning that there could be a problem
if ((currentChunk.length - currentChunk.bytesRead == 4) && (gBuffer[0] > 0x03)) {
MessageBox(NULL, "This 3DS file is over version 3 so it may load incorrectly", "Warning", MB_OK);
}
break;
case OBJECTINFO: // This holds the version of the mesh
{
// This chunk holds the version of the mesh. It is also the head of the MATERIAL
// and OBJECT chunks. From here on we start reading in the material and object info.
// Read the next chunk
ReadChunk(&tempChunk);
// Get the version of the mesh
tempChunk.bytesRead += fread(gBuffer, 1, tempChunk.length - tempChunk.bytesRead, m_FilePointer);
// Increase the bytesRead by the bytes read from the last chunk
currentChunk.bytesRead += tempChunk.bytesRead;
// Go to the next chunk, which is the object has a texture, it should be MATERIAL, then OBJECT.
ProcessNextChunk(pModel, ¤tChunk);
break;
}
case MATERIAL: // This holds the material information
// This chunk is the header for the material info chunks
// Increase the number of materials
pModel->numOfMaterials++;
// Add a empty texture structure to our texture list.
// If you are unfamiliar with STL''s "vector" class, all push_back()
// does is add a new node onto the list. I used the vector class
// so I didn''t need to write my own link list functions.
pModel->pMaterials.push_back(newTexture);
// Proceed to the material loading function
ProcessNextMaterialChunk(pModel, ¤tChunk);
break;
case OBJECT: // This holds the name of the object being read
// This chunk is the header for the object info chunks. It also
// holds the name of the object.
// Increase the object count
pModel->numOfObjects++;
// Add a new tObject node to our list of objects (like a link list)
pModel->pObject.push_back(newObject);
// Initialize the object and all it''s data members
memset(&(pModel->pObject[pModel->numOfObjects - 1]), 0, sizeof(t3DObject));
// Get the name of the object and store it, then add the read bytes to our byte counter.
currentChunk.bytesRead += GetString(pModel->pObject[pModel->numOfObjects - 1].strName);
// Now proceed to read in the rest of the object information
ProcessNextObjectChunk(pModel, &(pModel->pObject[pModel->numOfObjects - 1]), ¤tChunk);
break;
case EDITKEYFRAME:
// Because I wanted to make this a SIMPLE tutorial as possible, I did not include
// the key frame information. This chunk is the header for all the animation info.
// In a later tutorial this will be the subject and explained thoroughly.
//ProcessNextKeyFrameChunk(pModel, currentChunk);
// Read past this chunk and add the bytes read to the byte counter
currentChunk.bytesRead += fread(gBuffer, 1, currentChunk.length - currentChunk.bytesRead, m_FilePointer);
break;
default:
// If we didn''t care about a chunk, then we get here. We still need
// to read past the unknown or ignored chunk and add the bytes read to the byte counter.
currentChunk.bytesRead += fread(gBuffer, 1, currentChunk.length - currentChunk.bytesRead, m_FilePointer);
break;
}
// Add the bytes read from the last chunk to the previous chunk passed in.
pPreviousChunk->bytesRead += currentChunk.bytesRead;
}
}
///////////////////////////////// PROCESS NEXT OBJECT CHUNK \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
///// This function handles all the information about the objects in the file
/////
///////////////////////////////// PROCESS NEXT OBJECT CHUNK \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
void CLoad3DS::ProcessNextObjectChunk(t3DModel *pModel, t3DObject *pObject, tChunk *pPreviousChunk)
{
// The current chunk to work with
tChunk currentChunk = {0};
// Continue to read these chunks until we read the end of this sub chunk
while (pPreviousChunk->bytesRead < pPreviousChunk->length)
{
// Read the next chunk
ReadChunk(¤tChunk);
// Check which chunk we just read
switch (currentChunk.ID)
{
case OBJECT_MESH: // This lets us know that we are reading a new object
// We found a new object, so let''s read in it''s info using recursion
ProcessNextObjectChunk(pModel, pObject, ¤tChunk);
break;
case OBJECT_VERTICES: // This is the objects vertices
ReadVertices(pObject, ¤tChunk);
break;
case OBJECT_FACES: // This is the objects face information
ReadVertexIndices(pObject, ¤tChunk);
break;
case OBJECT_MATERIAL: // This holds the material name that the object has
// This chunk holds the name of the material that the object has assigned to it.
// This could either be just a color or a texture map. This chunk also holds
// the faces that the texture is assigned to (In the case that there is multiple
// textures assigned to one object, or it just has a texture on a part of the object.
// Since most of my game objects just have the texture around the whole object, and
// they aren''t multitextured, I just want the material name.
// We now will read the name of the material assigned to this object
ReadObjectMaterial(pModel, pObject, ¤tChunk);
break;
case OBJECT_UV: // This holds the UV texture coordinates for the object
// This chunk holds all of the UV coordinates for our object. Let''s read them in.
ReadUVCoordinates(pObject, ¤tChunk);
break;
default:
// Read past the ignored or unknown chunks
currentChunk.bytesRead += fread(gBuffer, 1, currentChunk.length - currentChunk.bytesRead, m_FilePointer);
break;
}
// Add the bytes read from the last chunk to the previous chunk passed in.
pPreviousChunk->bytesRead += currentChunk.bytesRead;
}
}
///////////////////////////////// PROCESS NEXT MATERIAL CHUNK \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
///// This function handles all the information about the material (Texture)
/////
///////////////////////////////// PROCESS NEXT MATERIAL CHUNK \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
void CLoad3DS::ProcessNextMaterialChunk(t3DModel *pModel, tChunk *pPreviousChunk)
{
// The current chunk to work with
tChunk currentChunk = {0};
// Continue to read these chunks until we read the end of this sub chunk
while (pPreviousChunk->bytesRead < pPreviousChunk->length)
{
// Read the next chunk
ReadChunk(¤tChunk);
// Check which chunk we just read in
switch (currentChunk.ID)
{
case MATNAME: // This chunk holds the name of the material
// Here we read in the material name
currentChunk.bytesRead += fread(pModel->pMaterials[pModel->numOfMaterials - 1].strName, 1, currentChunk.length - currentChunk.bytesRead, m_FilePointer);
break;
case MATDIFFUSE: // This holds the R G B color of our object
ReadColorChunk(&(pModel->pMaterials[pModel->numOfMaterials - 1]), ¤tChunk);
break;
case MATMAP: // This is the header for the texture info
// Proceed to read in the material information
ProcessNextMaterialChunk(pModel, ¤tChunk);
break;
case MATMAPFILE: // This stores the file name of the material
// Here we read in the material''s file name
currentChunk.bytesRead += fread(pModel->pMaterials[pModel->numOfMaterials - 1].strFile, 1, currentChunk.length - currentChunk.bytesRead, m_FilePointer);
break;
default:
// Read past the ignored or unknown chunks
currentChunk.bytesRead += fread(gBuffer, 1, currentChunk.length - currentChunk.bytesRead, m_FilePointer);
break;
}
// Add the bytes read from the last chunk to the previous chunk passed in.
pPreviousChunk->bytesRead += currentChunk.bytesRead;
}
}
///////////////////////////////// READ CHUNK \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
///// This function reads in a chunk ID and it''s length in bytes
/////
///////////////////////////////// READ CHUNK \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
void CLoad3DS::ReadChunk(tChunk *pChunk)
{
// This reads the chunk ID which is 2 bytes.
// The chunk ID is like OBJECT or MATERIAL. It tells what data is
// able to be read in within the chunks section.
pChunk->bytesRead = fread(&pChunk->ID, 1, 2, m_FilePointer);
// Then, we read the length of the chunk which is 4 bytes.
// This is how we know how much to read in, or read past.
pChunk->bytesRead += fread(&pChunk->length, 1, 4, m_FilePointer);
}
///////////////////////////////// GET STRING \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
///// This function reads in a string of characters
/////
///////////////////////////////// GET STRING \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
int CLoad3DS::GetString(char *pBuffer)
{
int index = 0;
// Read 1 byte of data which is the first letter of the string
fread(pBuffer, 1, 1, m_FilePointer);
// Loop until we get NULL
while (*(pBuffer + index++) != 0) {
// Read in a character at a time until we hit NULL.
fread(pBuffer + index, 1, 1, m_FilePointer);
}
// Return the string length, which is how many bytes we read in (including the NULL)
return strlen(pBuffer) + 1;
}
///////////////////////////////// READ COLOR \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
///// This function reads in the RGB color data
/////
///////////////////////////////// READ COLOR \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
void CLoad3DS::ReadColorChunk(tMaterialInfo *pMaterial, tChunk *pChunk)
{
tChunk tempChunk = {0};
// Read the color chunk info
ReadChunk(&tempChunk);
// Read in the R G B color (3 bytes - 0 through 255)
tempChunk.bytesRead += fread(pMaterial->color, 1, tempChunk.length - tempChunk.bytesRead, m_FilePointer);
// Add the bytes read to our chunk
pChunk->bytesRead += tempChunk.bytesRead;
}
///////////////////////////////// READ VERTEX INDECES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
///// This function reads in the indices for the vertex array
/////
///////////////////////////////// READ VERTEX INDECES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
void CLoad3DS::ReadVertexIndices(t3DObject *pObject, tChunk *pPreviousChunk)
{
unsigned short index = 0; // This is used to read in the current face index
// In order to read in the vertex indices for the object, we need to first
// read in the number of them, then read them in. Remember,
// we only want 3 of the 4 values read in for each face. The fourth is
// a visibility flag for 3D Studio Max that doesn''t mean anything to us.
// Read in the number of faces that are in this object (int)
pPreviousChunk->bytesRead += fread(&pObject->numOfFaces, 1, 2, m_FilePointer);
// Alloc enough memory for the faces and initialize the structure
pObject->pFaces = new tFace [pObject->numOfFaces];
memset(pObject->pFaces, 0, sizeof(tFace) * pObject->numOfFaces);
// Go through all of the faces in this object
for(int i = 0; i < pObject->numOfFaces; i++)
{
// Next, we read in the A then B then C index for the face, but ignore the 4th value.
// The fourth value is a visibility flag for 3D Studio Max, we don''t care about this.
for(int j = 0; j < 4; j++)
{
// Read the first vertice index for the current face
pPreviousChunk->bytesRead += fread(&index, 1, sizeof(index), m_FilePointer);
if(j < 3)
{
// Store the index in our face structure.
pObject->pFaces[i].vertIndex[j] = index;
}
}
}
}
///////////////////////////////// READ UV COORDINATES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
///// This function reads in the UV coordinates for the object
/////
///////////////////////////////// READ UV COORDINATES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
void CLoad3DS::ReadUVCoordinates(t3DObject *pObject, tChunk *pPreviousChunk)
{
// In order to read in the UV indices for the object, we need to first
// read in the amount there are, then read them in.
// Read in the number of UV coordinates there are (int)
pPreviousChunk->bytesRead += fread(&pObject->numTexVertex, 1, 2, m_FilePointer);
// Allocate memory to hold the UV coordinates
pObject->pTexVerts = new CVector2 [pObject->numTexVertex];
// Read in the texture coodinates (an array 2 float)
pPreviousChunk->bytesRead += fread(pObject->pTexVerts, 1, pPreviousChunk->length - pPreviousChunk->bytesRead, m_FilePointer);
}
///////////////////////////////// READ VERTICES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
///// This function reads in the vertices for the object
/////
///////////////////////////////// READ VERTICES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
void CLoad3DS::ReadVertices(t3DObject *pObject, tChunk *pPreviousChunk)
{
// Like most chunks, before we read in the actual vertices, we need
// to find out how many there are to read in. Once we have that number
// we then fread() them into our vertice array.
// Read in the number of vertices (int)
pPreviousChunk->bytesRead += fread(&(pObject->numOfVerts), 1, 2, m_FilePointer);
// Allocate the memory for the verts and initialize the structure
pObject->pVerts = new CVector3 [pObject->numOfVerts];
memset(pObject->pVerts, 0, sizeof(CVector3) * pObject->numOfVerts);
// Read in the array of vertices (an array of 3 floats)
pPreviousChunk->bytesRead += fread(pObject->pVerts, 1, pPreviousChunk->length - pPreviousChunk->bytesRead, m_FilePointer);
// Now we should have all of the vertices read in. Because 3D Studio Max
// Models with the Z-Axis pointing up (strange and ugly I know!), we need
// to flip the y values with the z values in our vertices. That way it
// will be normal, with Y pointing up. If you prefer to work with Z pointing
// up, then just delete this next loop. Also, because we swap the Y and Z
// we need to negate the Z to make it come out correctly.
// Go through all of the vertices that we just read and swap the Y and Z values
for(int i = 0; i < pObject->numOfVerts; i++)
{
// Store off the Y value
float fTempY = pObject->pVerts[i].y;
// Set the Y value to the Z value
pObject->pVerts[i].y = pObject->pVerts[i].z;
// Set the Z value to the Y value,
// but negative Z because 3D Studio max does the opposite.
pObject->pVerts[i].z = -fTempY;
}
}
///////////////////////////////// READ OBJECT MATERIAL \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
///// This function reads in the material name assigned to the object and sets the materialID
/////
///////////////////////////////// READ OBJECT MATERIAL \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
void CLoad3DS::ReadObjectMaterial(t3DModel *pModel, t3DObject *pObject, tChunk *pPreviousChunk)
{
char strMaterial[255] = {0}; // This is used to hold the objects material name
// *What is a material?* - A material is either the color or the texture map of the object.
// It can also hold other information like the brightness, shine, etc... Stuff we don''t
// really care about. We just want the color, or the texture map file name really.
// Here we read the material name that is assigned to the current object.
// strMaterial should now have a string of the material name, like "Material #2" etc..
pPreviousChunk->bytesRead += GetString(strMaterial);
// Now that we have a material name, we need to go through all of the materials
// and check the name against each material. When we find a material in our material
// list that matches this name we just read in, then we assign the materialID
// of the object to that material index. You will notice that we passed in the
// model to this function. This is because we need the number of textures.
// Yes though, we could have just passed in the model and not the object too.
// Go through all of the textures
for(int i = 0; i < pModel->numOfMaterials; i++)
{
// If the material we just read in matches the current texture name
if(strcmp(strMaterial, pModel->pMaterials[i].strName) == 0)
{
// Set the material ID to the current index ''i'' and stop checking
pObject->materialID = i;
// Now that we found the material, check if it''s a texture map.
// If the strFile has a string length of 1 and over it''s a texture
if(strlen(pModel->pMaterials[i].strFile) > 0) {
// Set the object''s flag to say it has a texture map to bind.
pObject->bHasTexture = true;
}
break;
}
else
{
// Set the ID to -1 to show there is no material for this object
pObject->materialID = -1;
}
}
// Read past the rest of the chunk since we don''t care about shared vertices
// You will notice we subtract the bytes already read in this chunk from the total length.
pPreviousChunk->bytesRead += fread(gBuffer, 1, pPreviousChunk->length - pPreviousChunk->bytesRead, m_FilePointer);
}
// *Note*
//
// Below are some math functions for calculating vertex normals. We want vertex normals
// because it makes the lighting look really smooth and life like. You probably already
// have these functions in the rest of your engine, so you can delete these and call
// your own. I wanted to add them so I could show how to calculate vertex normals.
////////////////////////////// Math Functions ////////////////////////////////*
// This computes the magnitude of a normal. (magnitude = sqrt(x^2 + y^2 + z^2)
#define Mag(Normal) (sqrt(Normal.x*Normal.x + Normal.y*Normal.y + Normal.z*Normal.z))
// This calculates a vector between 2 points and returns the result
CVector3 Vector(CVector3 vPoint1, CVector3 vPoint2)
{
CVector3 vVector; // The variable to hold the resultant vector
vVector.x = vPoint1.x - vPoint2.x; // Subtract point1 and point2 x''s
vVector.y = vPoint1.y - vPoint2.y; // Subtract point1 and point2 y''s
vVector.z = vPoint1.z - vPoint2.z; // Subtract point1 and point2 z''s
return vVector; // Return the resultant vector
}
// This adds 2 vectors together and returns the result
CVector3 AddVector(CVector3 vVector1, CVector3 vVector2)
{
CVector3 vResult; // The variable to hold the resultant vector
vResult.x = vVector2.x + vVector1.x; // Add Vector1 and Vector2 x''s
vResult.y = vVector2.y + vVector1.y; // Add Vector1 and Vector2 y''s
vResult.z = vVector2.z + vVector1.z; // Add Vector1 and Vector2 z''s
return vResult; // Return the resultant vector
}
// This divides a vector by a single number (scalar) and returns the result
CVector3 DivideVectorByScaler(CVector3 vVector1, float Scaler)
{
CVector3 vResult; // The variable to hold the resultant vector
vResult.x = vVector1.x / Scaler; // Divide Vector1''s x value by the scaler
vResult.y = vVector1.y / Scaler; // Divide Vector1''s y value by the scaler
vResult.z = vVector1.z / Scaler; // Divide Vector1''s z value by the scaler
return vResult; // Return the resultant vector
}
// This returns the cross product between 2 vectors
CVector3 Cross(CVector3 vVector1, CVector3 vVector2)
{
CVector3 vCross; // The vector to hold the cross product
// Get the X value
vCross.x = ((vVector1.y * vVector2.z) - (vVector1.z * vVector2.y));
// Get the Y value
vCross.y = ((vVector1.z * vVector2.x) - (vVector1.x * vVector2.z));
// Get the Z value
vCross.z = ((vVector1.x * vVector2.y) - (vVector1.y * vVector2.x));
return vCross; // Return the cross product
}
// This returns the normal of a vector
CVector3 Normalize(CVector3 vNormal)
{
double Magnitude; // This holds the magitude
Magnitude = Mag(vNormal); // Get the magnitude
vNormal.x /= (float)Magnitude; // Divide the vector''s X by the magnitude
vNormal.y /= (float)Magnitude; // Divide the vector''s Y by the magnitude
vNormal.z /= (float)Magnitude; // Divide the vector''s Z by the magnitude
return vNormal; // Return the normal
}
///////////////////////////////// COMPUTER NORMALS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
///// This function computes the normals and vertex normals of the objects
/////
///////////////////////////////// COMPUTER NORMALS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
void CLoad3DS::ComputeNormals(t3DModel *pModel)
{
CVector3 vVector1, vVector2, vNormal, vPoly[3];
// If there are no objects, we can skip this part
if(pModel->numOfObjects <= 0)
return;
// What are vertex normals? And how are they different from other normals?
// Well, if you find the normal to a triangle, you are finding a "Face Normal".
// If you give OpenGL a face normal for lighting, it will make your object look
// really flat and not very round. If we find the normal for each vertex, it makes
// the smooth lighting look. This also covers up blocky looking objects and they appear
// to have more polygons than they do. Basically, what you do is first
// calculate the face normals, then you take the average of all the normals around each
// vertex. It''s just averaging. That way you get a better approximation for that vertex.
// Go through each of the objects to calculate their normals
for(int index = 0; index < pModel->numOfObjects; index++)
{
// Get the current object
t3DObject *pObject = &(pModel->pObject[index]);
// Here we allocate all the memory we need to calculate the normals
CVector3 *pNormals = new CVector3 [pObject->numOfFaces];
CVector3 *pTempNormals = new CVector3 [pObject->numOfFaces];
pObject->pNormals = new CVector3 [pObject->numOfVerts];
// Go though all of the faces of this object
for(int i=0; i < pObject->numOfFaces; i++)
{
// To cut down LARGE code, we extract the 3 points of this face
vPoly[0] = pObject->pVerts[pObject->pFaces[i].vertIndex[0]];
vPoly[1] = pObject->pVerts[pObject->pFaces[i].vertIndex[1]];
vPoly[2] = pObject->pVerts[pObject->pFaces[i].vertIndex[2]];
// Now let''s calculate the face normals (Get 2 vectors and find the cross product of those 2)
vVector1 = Vector(vPoly[0], vPoly[2]); // Get the vector of the polygon (we just need 2 sides for the normal)
vVector2 = Vector(vPoly[2], vPoly[1]); // Get a second vector of the polygon
vNormal = Cross(vVector1, vVector2); // Return the cross product of the 2 vectors (normalize vector, but not a unit vector)
pTempNormals[i] = vNormal; // Save the un-normalized normal for the vertex normals
vNormal = Normalize(vNormal); // Normalize the cross product to give us the polygons normal
pNormals[i] = vNormal; // Assign the normal to the list of normals
}
//////////////// Now Get The Vertex Normals /////////////////
CVector3 vSum = {0.0, 0.0, 0.0};
CVector3 vZero = vSum;
int shared=0;
for (i = 0; i < pObject->numOfVerts; i++) // Go through all of the vertices
{
for (int j = 0; j < pObject->numOfFaces; j++) // Go through all of the triangles
{ // Check if the vertex is shared by another face
if (pObject->pFaces[j].vertIndex[0] == i ||
pObject->pFaces[j].vertIndex[1] == i ||
pObject->pFaces[j].vertIndex[2] == i)
{
vSum = AddVector(vSum, pTempNormals[j]);// Add the un-normalized normal of the shared face
shared++; // Increase the number of shared triangles
}
}
// Get the normal by dividing the sum by the shared. We negate the shared so it has the normals pointing out.
pObject->pNormals[i] = DivideVectorByScaler(vSum, float(-shared));
// Normalize the normal for the final vertex normal
pObject->pNormals[i] = Normalize(pObject->pNormals[i]);
vSum = vZero; // Reset the sum
shared = 0; // Reset the shared
}
// Free our memory and start over on the next object
delete [] pTempNormals;
delete [] pNormals;
}
}
#ifndef _3DS_H
#define _3DS_H
//>------ Primary Chunk, at the beginning of each file
#define PRIMARY 0x4D4D
//>------ Main Chunks
#define OBJECTINFO 0x3D3D // This gives the version of the mesh and is found right before the material and object information
#define VERSION 0x0002 // This gives the version of the .3ds file
#define EDITKEYFRAME 0xB000 // This is the header for all of the key frame info
//>------ sub defines of OBJECTINFO
#define MATERIAL 0xAFFF // This stored the texture info
#define OBJECT 0x4000 // This stores the faces, vertices, etc...
//>------ sub defines of MATERIAL
#define MATNAME 0xA000 // This holds the material name
#define MATDIFFUSE 0xA020 // This holds the color of the object/material
#define MATMAP 0xA200 // This is a header for a new material
#define MATMAPFILE 0xA300 // This holds the file name of the texture
#define OBJECT_MESH 0x4100 // This lets us know that we are reading a new object
//>------ sub defines of OBJECT_MESH
#define OBJECT_VERTICES 0x4110 // The objects vertices
#define OBJECT_FACES 0x4120 // The objects faces
#define OBJECT_MATERIAL 0x4130 // This is found if the object has a material, either texture map or color
#define OBJECT_UV 0x4140 // The UV texture coordinates
// Here is our structure for our 3DS indicies (since .3DS stores 4 unsigned shorts)
struct tIndices {
unsigned short a, b, c, bVisible; // This will hold point1, 2, and 3 index''s into the vertex array plus a visible flag
};
// This holds the chunk info
struct tChunk
{
unsigned short int ID; // The chunk''s ID
unsigned int length; // The length of the chunk
unsigned int bytesRead; // The amount of bytes read within that chunk
};
// This class handles all of the loading code
class CLoad3DS
{
public:
CLoad3DS(); // This inits the data members
// This is the function that you call to load the 3DS
bool Import3DS(t3DModel *pModel, char *strFileName);
private:
// This reads in a string and saves it in the char array passed in
int GetString(char *);
// This reads the next chunk
void ReadChunk(tChunk *);
// This reads the next large chunk
void ProcessNextChunk(t3DModel *pModel, tChunk *);
// This reads the object chunks
void ProcessNextObjectChunk(t3DModel *pModel, t3DObject *pObject, tChunk *);
// This reads the material chunks
void ProcessNextMaterialChunk(t3DModel *pModel, tChunk *);
// This reads the RGB value for the object''s color
void ReadColorChunk(tMaterialInfo *pMaterial, tChunk *pChunk);
// This reads the objects vertices
void ReadVertices(t3DObject *pObject, tChunk *);
// This reads the objects face information
void ReadVertexIndices(t3DObject *pObject, tChunk *);
// This reads the texture coodinates of the object
void ReadUVCoordinates(t3DObject *pObject, tChunk *);
// This reads in the material name assigned to the object and sets the materialID
void ReadObjectMaterial(t3DModel *pModel, t3DObject *pObject, tChunk *pPreviousChunk);
// This computes the vertex normals for the object (used for lighting)
void ComputeNormals(t3DModel *pModel);
// This frees memory and closes the file
void CleanUp();
// The file pointer
FILE *m_FilePointer;
};
#endif