Original Post
When I try to load my model in to my game i use Draw_character(). It should load a model named "girl.obj" and then I could move around with it but when I compile, it compiles fine but when I run it it throws back the assert. Here is the code. It's a copy-paste heap with copyed code from different sites and so on so expect a total mess. Main.cpp ObjLoader.cpp That's a lot of code to go trough and I can't even say that I understand everything there. Hope somebody can help me out from this mess or even tell me where I did wrong. Thanks Arangol
#include "main.h"
#include "ObjLoader.h"
HDC hDC=NULL; // Private GDI Device Context
HGLRC hRC=NULL; // Permanent Rendering Context
HWND hWnd=NULL; // Holds Our Window Handle
HINSTANCE hInstance; // Holds The Instance Of The Application
bool keys[256]; // Array Used For The Keyboard Routine
bool active=TRUE; // Window Active Flag Set To TRUE By Default
bool fullscreen=TRUE; // Fullscreen Flag Set To Fullscreen Mode By Default
typedef unsigned int ObjFile;
extern ObjFile LoadOBJ(const char*);
extern void DrawOBJ(ObjFile);
extern void DeleteOBJ(ObjFile);
extern void CleanUpOBJ(void);
extern void ConvertMeshTo(ObjFile,unsigned char);
#define DISPLAY_LIST 1
#define VERTEX_ARRAY 2
#define VERTEX_BUFFER_OBJECT 3
//NEW//////////////////NEW//////////////////NEW//////////////////NEW////////////////
ObjFile Objects[20];
int NumObjects=0;
CCamera objCamera;
void Draw_Character();
void Draw_Grid();
//NEW//////////////////NEW//////////////////NEW//////////////////NEW////////////////
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); // Declaration For WndProc
/////////////////////////////////////////////////////////////////////////////////////////////////
// THE RESIZE GL SCENE
/////////////////////////////////////////////////////////////////////////////////////////////////
GLvoid ReSizeGLScene(GLsizei width, GLsizei height) // Resize And Initialize The GL Window
{
if (height==0) // Prevent A Divide By Zero By
{
height=1; // Making Height Equal One
}
glViewport(0,0,width,height); // Reset The Current Viewport
glMatrixMode(GL_PROJECTION); // Select The Projection Matrix
glLoadIdentity(); // Reset The Projection Matrix
// Calculate The Aspect Ratio Of The Window
gluPerspective(45.0f,(GLfloat)width/(GLfloat)height,0.1f,100.0f);
glMatrixMode(GL_MODELVIEW); // Select The Modelview Matrix
glLoadIdentity(); // Reset The Modelview Matrix
}
/////////////////////////////////////////////////////////////////////////////////////////////////
// THE OPENGL INIT
/////////////////////////////////////////////////////////////////////////////////////////////////
int InitGL(GLvoid) // All Setup For OpenGL Goes Here
{
glShadeModel(GL_SMOOTH); // Enable Smooth Shading
glClearColor(0.0f, 0.0f, 0.0f, 0.5f); // Black Background
glClearDepth(1.0f); // Depth Buffer Setup
glEnable(GL_DEPTH_TEST); // Enables Depth Testing
glDepthFunc(GL_LEQUAL); // The Type Of Depth Testing To Do
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // Really Nice Perspective Calculations
glEnable(GL_CULL_FACE);
//NEW//////////////////NEW//////////////////NEW//////////////////NEW////////////////
// Position View(target) Up
objCamera.Position_Camera(0, 3.0f, 4.0f, 0, 1.5f, -5.0f, 0, 1.0f, 0);
//NEW//////////////////NEW//////////////////NEW//////////////////NEW////////////////
return TRUE; // Initialization Went OK
}
/////////////////////////////////////////////////////////////////////////////////////////////////
// THE OPENGL RENDER
/////////////////////////////////////////////////////////////////////////////////////////////////
int DrawGLScene(GLvoid) // Here's Where We Do All The Drawing
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear Screen And Depth Buffer
glLoadIdentity(); // Reset The Current Modelview Matrix
// use this function for opengl target camera
gluLookAt(objCamera.mPos.x, objCamera.mPos.y, objCamera.mPos.z,
objCamera.mView.x, objCamera.mView.y, objCamera.mView.z,
objCamera.mUp.x, objCamera.mUp.y, objCamera.mUp.z);
//NEW//////////////////NEW//////////////////NEW//////////////////NEW////////////////
glPushMatrix();
// Always keep the character in the view
glTranslatef(objCamera.mView.x,0.0f,objCamera.mView.z);
Draw_Character();
glPopMatrix();
Draw_Grid();
//NEW//////////////////NEW//////////////////NEW//////////////////NEW////////////////
return TRUE; // Keep Going
}
/////////////////////////////////////////////////////////////////////////////////////////////////
// THE KILL GL WINDOW
/////////////////////////////////////////////////////////////////////////////////////////////////
GLvoid KillGLWindow(GLvoid) // Properly Kill The Window
{
if (fullscreen) // Are We In Fullscreen Mode?
{
ChangeDisplaySettings(NULL,0); // If So Switch Back To The Desktop
ShowCursor(TRUE); // Show Mouse Pointer
}
if (hRC) // Do We Have A Rendering Context?
{
if (!wglMakeCurrent(NULL,NULL)) // Are We Able To Release The DC And RC Contexts?
{
MessageBox(NULL,"Release Of DC And RC Failed.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
}
if (!wglDeleteContext(hRC)) // Are We Able To Delete The RC?
{
MessageBox(NULL,"Release Rendering Context Failed.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
}
hRC=NULL; // Set RC To NULL
}
if (hDC && !ReleaseDC(hWnd,hDC)) // Are We Able To Release The DC
{
MessageBox(NULL,"Release Device Context Failed.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
hDC=NULL; // Set DC To NULL
}
if (hWnd && !DestroyWindow(hWnd)) // Are We Able To Destroy The Window?
{
MessageBox(NULL,"Could Not Release hWnd.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
hWnd=NULL; // Set hWnd To NULL
}
if (!UnregisterClass("OpenGL",hInstance)) // Are We Able To Unregister Class
{
MessageBox(NULL,"Could Not Unregister Class.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
hInstance=NULL; // Set hInstance To NULL
}
}
/* This Code Creates Our OpenGL Window. Parameters Are: *
* title - Title To Appear At The Top Of The Window *
* width - Width Of The GL Window Or Fullscreen Mode *
* height - Height Of The GL Window Or Fullscreen Mode *
* bits - Number Of Bits To Use For Color (8/16/24/32) *
* fullscreenflag - Use Fullscreen Mode (TRUE) Or Windowed Mode (FALSE) */
/////////////////////////////////////////////////////////////////////////////////////////////////
// THE CREATE GL WINDOW
/////////////////////////////////////////////////////////////////////////////////////////////////
BOOL CreateGLWindow(char* title, int width, int height, int bits, bool fullscreenflag)
{
GLuint PixelFormat; // Holds The Results After Searching For A Match
WNDCLASS wc; // Windows Class Structure
DWORD dwExStyle; // Window Extended Style
DWORD dwStyle; // Window Style
RECT WindowRect; // Grabs Rectangle Upper Left / Lower Right Values
WindowRect.left=(long)0; // Set Left Value To 0
WindowRect.right=(long)width; // Set Right Value To Requested Width
WindowRect.top=(long)0; // Set Top Value To 0
WindowRect.bottom=(long)height; // Set Bottom Value To Requested Height
fullscreen=fullscreenflag; // Set The Global Fullscreen Flag
hInstance = GetModuleHandle(NULL); // Grab An Instance For Our Window
wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; // Redraw On Size, And Own DC For Window.
wc.lpfnWndProc = (WNDPROC) WndProc; // WndProc Handles Messages
wc.cbClsExtra = 0; // No Extra Window Data
wc.cbWndExtra = 0; // No Extra Window Data
wc.hInstance = hInstance; // Set The Instance
wc.hIcon = LoadIcon(NULL, IDI_WINLOGO); // Load The Default Icon
wc.hCursor = LoadCursor(NULL, IDC_ARROW); // Load The Arrow Pointer
wc.hbrBackground = NULL; // No Background Required For GL
wc.lpszMenuName = NULL; // We Don't Want A Menu
wc.lpszClassName = "OpenGL"; // Set The Class Name
if (!RegisterClass(&wc)) // Attempt To Register The Window Class
{
MessageBox(NULL,"Failed To Register The Window Class.","ERROR",MB_OK|MB_ICONEXCLAMATION);
return FALSE; // Return FALSE
}
if (fullscreen) // Attempt Fullscreen Mode?
{
DEVMODE dmScreenSettings; // Device Mode
memset(&dmScreenSettings,0,sizeof(dmScreenSettings)); // Makes Sure Memory's Cleared
dmScreenSettings.dmSize=sizeof(dmScreenSettings); // Size Of The Devmode Structure
dmScreenSettings.dmPelsWidth = width; // Selected Screen Width
dmScreenSettings.dmPelsHeight = height; // Selected Screen Height
dmScreenSettings.dmBitsPerPel = bits; // Selected Bits Per Pixel
dmScreenSettings.dmFields=DM_BITSPERPEL|DM_PELSWIDTH|DM_PELSHEIGHT;
// Try To Set Selected Mode And Get Results. NOTE: CDS_FULLSCREEN Gets Rid Of Start Bar.
if (ChangeDisplaySettings(&dmScreenSettings,CDS_FULLSCREEN)!=DISP_CHANGE_SUCCESSFUL)
{
// If The Mode Fails, Offer Two Options. Quit Or Use Windowed Mode.
if (MessageBox(NULL,"The Requested Fullscreen Mode Is Not Supported By\nYour Video Card. Use Windowed Mode Instead?","OPENGL",MB_YESNO|MB_ICONEXCLAMATION)==IDYES)
{
fullscreen=FALSE; // Windowed Mode Selected. Fullscreen = FALSE
}
else
{
// Pop Up A Message Box Letting User Know The Program Is Closing.
MessageBox(NULL,"Program Will Now Close.","ERROR",MB_OK|MB_ICONSTOP);
return FALSE; // Return FALSE
}
}
}
if (fullscreen) // Are We Still In Fullscreen Mode?
{
dwExStyle=WS_EX_APPWINDOW; // Window Extended Style
dwStyle=WS_POPUP; // Windows Style
ShowCursor(FALSE); // Hide Mouse Pointer
}
else
{
dwExStyle=WS_EX_APPWINDOW | WS_EX_WINDOWEDGE; // Window Extended Style
dwStyle=WS_OVERLAPPEDWINDOW; // Windows Style
}
AdjustWindowRectEx(&WindowRect, dwStyle, FALSE, dwExStyle); // Adjust Window To True Requested Size
// Create The Window
if (!(hWnd=CreateWindowEx( dwExStyle, // Extended Style For The Window
"OpenGL", // Class Name
title, // Window Title
dwStyle | // Defined Window Style
WS_CLIPSIBLINGS | // Required Window Style
WS_CLIPCHILDREN, // Required Window Style
0, 0, // Window Position
WindowRect.right-WindowRect.left, // Calculate Window Width
WindowRect.bottom-WindowRect.top, // Calculate Window Height
NULL, // No Parent Window
NULL, // No Menu
hInstance, // Instance
NULL))) // Dont Pass Anything To WM_CREATE
{
KillGLWindow(); // Reset The Display
MessageBox(NULL,"Window Creation Error.","ERROR",MB_OK|MB_ICONEXCLAMATION);
return FALSE; // Return FALSE
}
static PIXELFORMATDESCRIPTOR pfd= // pfd Tells Windows How We Want Things To Be
{
sizeof(PIXELFORMATDESCRIPTOR), // Size Of This Pixel Format Descriptor
1, // Version Number
PFD_DRAW_TO_WINDOW | // Format Must Support Window
PFD_SUPPORT_OPENGL | // Format Must Support OpenGL
PFD_DOUBLEBUFFER, // Must Support Double Buffering
PFD_TYPE_RGBA, // Request An RGBA Format
bits, // Select Our Color Depth
0, 0, 0, 0, 0, 0, // Color Bits Ignored
0, // No Alpha Buffer
0, // Shift Bit Ignored
0, // No Accumulation Buffer
0, 0, 0, 0, // Accumulation Bits Ignored
16, // 16Bit Z-Buffer (Depth Buffer)
0, // No Stencil Buffer
0, // No Auxiliary Buffer
PFD_MAIN_PLANE, // Main Drawing Layer
0, // Reserved
0, 0, 0 // Layer Masks Ignored
};
if (!(hDC=GetDC(hWnd))) // Did We Get A Device Context?
{
KillGLWindow(); // Reset The Display
MessageBox(NULL,"Can't Create A GL Device Context.","ERROR",MB_OK|MB_ICONEXCLAMATION);
return FALSE; // Return FALSE
}
if (!(PixelFormat=ChoosePixelFormat(hDC,&pfd))) // Did Windows Find A Matching Pixel Format?
{
KillGLWindow(); // Reset The Display
MessageBox(NULL,"Can't Find A Suitable PixelFormat.","ERROR",MB_OK|MB_ICONEXCLAMATION);
return FALSE; // Return FALSE
}
if(!SetPixelFormat(hDC,PixelFormat,&pfd)) // Are We Able To Set The Pixel Format?
{
KillGLWindow(); // Reset The Display
MessageBox(NULL,"Can't Set The PixelFormat.","ERROR",MB_OK|MB_ICONEXCLAMATION);
return FALSE; // Return FALSE
}
if (!(hRC=wglCreateContext(hDC))) // Are We Able To Get A Rendering Context?
{
KillGLWindow(); // Reset The Display
MessageBox(NULL,"Can't Create A GL Rendering Context.","ERROR",MB_OK|MB_ICONEXCLAMATION);
return FALSE; // Return FALSE
}
if(!wglMakeCurrent(hDC,hRC)) // Try To Activate The Rendering Context
{
KillGLWindow(); // Reset The Display
MessageBox(NULL,"Can't Activate The GL Rendering Context.","ERROR",MB_OK|MB_ICONEXCLAMATION);
return FALSE; // Return FALSE
}
ShowWindow(hWnd,SW_SHOW); // Show The Window
SetForegroundWindow(hWnd); // Slightly Higher Priority
SetFocus(hWnd); // Sets Keyboard Focus To The Window
ReSizeGLScene(width, height); // Set Up Our Perspective GL Screen
if (!InitGL()) // Initialize Our Newly Created GL Window
{
KillGLWindow(); // Reset The Display
MessageBox(NULL,"Initialization Failed.","ERROR",MB_OK|MB_ICONEXCLAMATION);
return FALSE; // Return FALSE
}
return TRUE; // Success
}
/////////////////////////////////////////////////////////////////////////////////////////////////
// THE WINDOW PROCEDURE
/////////////////////////////////////////////////////////////////////////////////////////////////
LRESULT CALLBACK WndProc( HWND hWnd, // Handle For This Window
UINT uMsg, // Message For This Window
WPARAM wParam, // Additional Message Information
LPARAM lParam) // Additional Message Information
{
switch (uMsg) // Check For Windows Messages
{
case WM_ACTIVATE: // Watch For Window Activate Message
{
if (!HIWORD(wParam)) // Check Minimization State
{
active=TRUE; // Program Is Active
}
else
{
active=FALSE; // Program Is No Longer Active
}
return 0; // Return To The Message Loop
}
case WM_SYSCOMMAND: // Intercept System Commands
{
switch (wParam) // Check System Calls
{
case SC_SCREENSAVE: // Screensaver Trying To Start?
case SC_MONITORPOWER: // Monitor Trying To Enter Powersave?
return 0; // Prevent From Happening
}
break; // Exit
}
case WM_CLOSE: // Did We Receive A Close Message?
{
PostQuitMessage(0); // Send A Quit Message
return 0; // Jump Back
}
case WM_KEYDOWN:
switch(wParam)
{
case VK_ESCAPE:
PostQuitMessage(0);
break;
}
break;
case WM_KEYUP: // Has A Key Been Released?
{
keys[wParam] = FALSE; // If So, Mark It As FALSE
return 0; // Jump Back
}
case WM_SIZE: // Resize The OpenGL Window
{
ReSizeGLScene(LOWORD(lParam),HIWORD(lParam)); // LoWord=Width, HiWord=Height
return 0; // Jump Back
}
}
// Pass All Unhandled Messages To DefWindowProc
return DefWindowProc(hWnd,uMsg,wParam,lParam);
}
/////////////////////////////////////////////////////////////////////////////////////////////////
// THE WINMAIN
/////////////////////////////////////////////////////////////////////////////////////////////////
int WINAPI WinMain( HINSTANCE hInstance, // Instance
HINSTANCE hPrevInstance, // Previous Instance
LPSTR lpCmdLine, // Command Line Parameters
int nCmdShow) // Window Show State
{
MSG msg; // Windows Message Structure
BOOL done=FALSE; // Bool Variable To Exit Loop
// Ask The User Which Screen Mode They Prefer
if (MessageBox(NULL,"Would You Like To Run In Fullscreen Mode?", "Start FullScreen?",MB_YESNO|MB_ICONQUESTION)==IDNO)
{
fullscreen=FALSE; // Windowed Mode
}
// Create Our OpenGL Window
if (!CreateGLWindow("Test App 0.0.5",640,480,16,fullscreen))
{
return 0; // Quit If Window Was Not Created
}
while(!done) // Loop That Runs While done=FALSE
{
if (PeekMessage(&msg,NULL,0,0,PM_REMOVE)) // Is There A Message Waiting?
{
if (msg.message==WM_QUIT) // Have We Received A Quit Message?
{
done=TRUE; // If So done=TRUE
}
else // If Not, Deal With Window Messages
{
TranslateMessage(&msg); // Translate The Message
DispatchMessage(&msg); // Dispatch The Message
}
}
else // If There Are No Messages
{
//NEW//////////////////NEW//////////////////NEW//////////////////NEW////////////////
Keyboard_Input();
//NEW//////////////////NEW//////////////////NEW//////////////////NEW////////////////
// Draw The Scene. Watch For ESC Key And Quit Messages From DrawGLScene()
if ((active && !DrawGLScene()) || keys[VK_ESCAPE]) // Active? Was There A Quit Received?
{
done=TRUE; // ESC or DrawGLScene Signalled A Quit
}
else // Not Time To Quit, Update Screen
{
SwapBuffers(hDC); // Swap Buffers (Double Buffering)
}
if (keys[VK_F1]) // Is F1 Being Pressed?
{
keys[VK_F1]=FALSE; // If So Make Key FALSE
KillGLWindow(); // Kill Our Current Window
fullscreen=!fullscreen; // Toggle Fullscreen / Windowed Mode
// Recreate Our OpenGL Window
if (!CreateGLWindow("Test App 0.0.5",640,480,16,fullscreen))
{
return 0; // Quit If Window Was Not Created
}
}
}
}
// Shutdown
KillGLWindow(); // Kill The Window
return (msg.wParam); // Exit The Program
}
/////////////////////////////////////////////////////////////////////////////////////////////////
// THE KEYBOARD INPUT
/////////////////////////////////////////////////////////////////////////////////////////////////
void Keyboard_Input()
{
if((GetKeyState(VK_UP) & 0x80) || (GetKeyState('W') & 0x80))
{
objCamera.Move_Camera( CAMERASPEED);
}
if((GetKeyState(VK_DOWN) & 0x80) || (GetKeyState('S') & 0x80))
{
objCamera.Move_Camera(-CAMERASPEED);
}
if((GetKeyState(VK_LEFT) & 0x80) || (GetKeyState('A') & 0x80))
{
objCamera.Rotate_Position(-CAMERASPEED);
}
if((GetKeyState(VK_RIGHT) & 0x80) || (GetKeyState('D') & 0x80))
{
objCamera.Rotate_Position( CAMERASPEED);
}
}
void Draw_Character() // This object will symbolize our character
{
glScalef(0.3f,1.0f,0.3f);
glTranslatef(0,1.0f,0);
Objects[ NumObjects ] = LoadOBJ("girl.obj");
for(int i=0;i!=NumObjects;++i)
{
DrawOBJ(Objects);
}
/* if file loaded ok */
}
void Draw_Grid()
{
glLoadIdentity();
glTranslatef(0,-1.0f,0);
glBegin(GL_QUADS);
glColor3f(1.0f,1.0f,0.5f);
glVertex3f(-500, 0,0 );
glVertex3f(500, 0,0 );
glVertex3f(0, 0, -500);
glVertex3f(0, 0, 500);
glEnd();
}
#ifdef WIN32
#include <windows.h>
#endif
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include "ObjLoader.h"
/* Ignored until Version6 */
void ConvertMeshTo(ObjFile id,unsigned char What)
{
}
void SetLightPosition(float lx,float ly,float lz)
{
}
void SetTextures(ObjFile id,const char BumpTex[],const char BaseTex[])
{
}
/*===================================================================================================
**
** Internal Data Structures
**
**=================================================================================================*/
/*
** To start with, only deal with the obj file as a list of points. Essentually,
** if we are able to draw the points out they should roughly describe the mesh.
** So, lets look for the 'v' flag that says that the 3 floats after it describes a
** Vertex. Essentually then, we need to have a structure for a vertex.
*/
typedef struct
{
float x, /* The x component of the vertex position */
y, /* The y component of the vertex position */
z; /* The z component of the vertex position */
} ObjVertex;
/*
** Now, the normal vector flag 'vn' could also do with a structure to represent it,
** this will ultimately look very similar to the Vertex structure seeing as they have
** similar data
*/
typedef struct
{
float x, /* The x component of the normal vector */
y, /* The y component of the normal vector */
z; /* The z component of the normal vector */
} ObjNormal;
/*
** A Texturing co-ordinate usually has only two values, the u and the v. Make a third
** struct for this fact.
*/
typedef struct
{
float u, /* The u parametric texturing co-ordinate */
v; /* The v parametric texturing co-ordinate */
} ObjTexCoord;
/*
** Each face is composed of a number of `corners`. At each `corner` there will be at a
** minimum, one vertex coordinate, and possibly a normal vector and a texturing co-ordinate.
** Seeing as quite often a specific vertex is used by a number of faces, rather than store the
** vertices in the face structure itself, we can use an integer reference to the position of the
** vertex in the array. Basically it allows us the ability of storing the data as a 4byte value
** rather than the 12 needed for a vertex.
**
** To summaraise,
**
** Each face is comprised of a number of `corners`, in this case the number will be held in
** the value "m_iVertexCount". Taking into account the worst case scenario, for each corner, we
** could have a vertex, normal and a texturing co-ordinate (usual actually). So we may need to hold
** 3 * m_iVertexCount number of indices for our faces. It kindof makes sense to basically dynamically
** allocate an array to hold the vertex indicies, an array for the normal indices and one for the uv
** coords. These are m_aVertexIndices, m_aNormalIndices, and m_aTexCoordIndicies respectivley.
**
** So our full face structure looks a bit like this :
*/
typedef struct
{
unsigned int m_aVertexIndices[3], /* array of indicies that reference the vertex array in the mesh */
m_aNormalIndices[3], /* array of indicies that reference the normal array in the mesh */
m_aTexCoordIndicies[3]; /* array of indicies that reference the uv coordinate array in the mesh */
} ObjFace;
/*
** Each mesh is held as a structure with arrays of it's vertices, normals and texturing co-ordinates.
** An Array of faces then references the arrays. There is also a pointer to the next node in the linked
** list.
*/
typedef struct _ObjMesh
{
ObjVertex *m_aVertexArray; /* Array of vertices that make up this mesh */
ObjNormal *m_aNormalArray; /* Array of normals that make up this mesh */
ObjTexCoord *m_aTexCoordArray; /* Array of texturing co-ordinates that make up this mesh */
ObjFace *m_aFaces; /* Array of faces that make up this mesh */
unsigned int m_iNumberOfVertices, /* The number of vertices in the m_aVertexArray array */
m_iNumberOfNormals, /* The number of normals in the m_aNormalArray array */
m_iNumberOfTexCoords, /* The number of uv's in the m_aTexCoordArray array */
m_iNumberOfFaces; /* The number of faces in the m_aFaces array */
struct _ObjMesh *m_pNext; /* Each mesh will be stored internally as a node on a linked list */
ObjFile m_iMeshID; /* the ID of the mesh */
} ObjMesh;
/*
** This function will take a pointer to the mesh and the number of extra triangles that
** are required. It will then resize the polygon face array within the mesh and return
** the index of the first new face that was allocated.
*/
unsigned int ResizeTriangleArray(ObjMesh *pMesh,unsigned int iExtraTriangleCount)
{
/*
** Create a temporary pointer to the old face array in the mesh
*/
ObjFace *pTempFaces = pMesh->m_aFaces;
/*
** Keep track of the old face count
*/
unsigned int iTemp = pMesh->m_iNumberOfFaces;
/*
** Increase the number of faces that will be stored in the mesh
*/
pMesh->m_iNumberOfFaces += iExtraTriangleCount;
/*
** Allocate the number of faces now required by this mesh. Note, you could use
** realloc for this, but it's just my personal preference to do it this way.
*/
pMesh->m_aFaces = (ObjFace*)malloc(pMesh->m_iNumberOfFaces*sizeof(ObjFace));
/*
** If we assert here then the memory allocation for the meshes failed
*/
assert(pMesh->m_aFaces);
/*
** Copy across the data that was stored in the old array
*/
memcpy(pMesh->m_aFaces,pTempFaces,iTemp*sizeof(ObjFace));
/*
** Delete the data held in the previous array
*/
free(pTempFaces);
memset(&pMesh->m_aFaces[ iTemp ],0,iExtraTriangleCount*sizeof(ObjFace) );
/*
** return the old size of the array (thats the same as the index of the first new triangle).
*/
return iTemp;
}
/*===================================================================================================
**
** Global Variables
**
**=================================================================================================*/
/*
** The global head of the linked list of meshes. This is a linked list because it is possible that you will be
** loading and deleting meshes during the course of the programs execution.
*/
ObjMesh *g_LinkedListHead = NULL;
/*
** This is used to generate a unique ID for each Obj File
*/
unsigned int g_ObjIdGenerator=0;
/*
** This function is only called from within the *.c file and is used to create an ObjMesh structure and
** initialise its values (adds the mesh to the linked list also).
*/
ObjMesh *MakeOBJ( void )
{
/*
** The pointer we will create the mesh at the end of
*/
ObjMesh *pMesh = NULL;
pMesh = (ObjMesh*) malloc (sizeof(ObjMesh));
/* If the program asserts here, then there was a memory allocation failure */
assert(pMesh);
/*
** Initialise all pointers to NULL
*/
pMesh->m_aFaces = NULL;
pMesh->m_aNormalArray = NULL;
pMesh->m_aTexCoordArray = NULL;
pMesh->m_aVertexArray = NULL;
pMesh->m_iNumberOfFaces = 0;
pMesh->m_iNumberOfNormals = 0;
pMesh->m_iNumberOfTexCoords = 0;
pMesh->m_iNumberOfVertices = 0;
pMesh->m_iMeshID = ++g_ObjIdGenerator;
/*
** Insert the mesh at the beginning of the linked list
*/
pMesh->m_pNext = g_LinkedListHead;
g_LinkedListHead = pMesh;
return pMesh;
}
void GetIndices(char *pString, unsigned int *pVert, unsigned int *pNorm, unsigned int *pUV)
{
/*
** Use 3 pointers to hold references to the bits of the string where the indices start
** for the vertex, normal and texturing co-ordinate respectivly
*/
char *pV = pString,
*pN = NULL,
*pT = NULL;
/*
** Walk through the string a character at a time looking for the '/' character. The first one
** we find will may have the uv - coord index behind it. The '/' character will be removed by
** setting it to NULL, If for example the texturing co-ord is missing, then you'd expect "//" to
** appear. This while loop would set both of those to NULL, that would give you a NULL string
** for the uv coord. This allows us to test the string later to see if there is or isn't a
** value there.
*/
while( *pString != '\0' )
{
if(*pString == '/')
{
*pString = '\0';
if(!pT)
{
pT = pString+1;
}
else
{
pN = pString+1;
}
}
pString++;
}
/*
** Read the vertex index
*/
sscanf(pV,"%d",pVert);
/*
** If a normal is there, then read it's value, otherwise set the default of 1
** (1 is used because we will decrease the value by 1 later thus giving us 0)
*/
if( pN == NULL || *pN == '\0' )
{
*pNorm = 1;
}
else
{
sscanf(pN,"%d",pNorm);
}
/*
** If a texture co-ordinate is there, then read it's value, otherwise set the default of 1
** (1 is used because we will decrease the value by 1 later thus giving us 0)
*/
if( pT == NULL || *pT == '\0' )
{
*pUV = 1;
}
else
{
sscanf(pT,"%d",pUV);
}
}
ObjFile LoadOBJ(const char *filename)
{
ObjMesh *pMesh=NULL;
unsigned int vc=0,nc=0,tc=0,fc=0;
char buffer[256];
FILE *fp = NULL;
/*
** Open the file for reading
*/
fp = fopen(filename,"r");
/*
** If the program asserted here, then the file could not be found or opened
*/
assert(fp);
/*
** Create the mesh structure and add it to the linked list
*/
pMesh = MakeOBJ();
/*
** Run through the whole file looking for the various flags so that we can count
** up how many data elements there are. This is done so that we can make one memory
** allocation for the meshes data and then run through the file once more, this time
** reading in the data. It's purely done to reduce system overhead of memory allocation due
** to otherwise needing to reallocate data everytime we read in a new element.
*/
while(!feof(fp))
{
memset(buffer,0,200);
/* Grab a line at a time */
fgets(buffer,256,fp);
/* look for the 'vn' - vertex normal - flag */
if( strncmp("vn ",buffer,3) == 0 )
{
++pMesh->m_iNumberOfNormals;
}
else
/* look for the 'vt' - texturing co-ordinate - flag */
if( strncmp("vt ",buffer,3) == 0 )
{
++pMesh->m_iNumberOfTexCoords;
}
else
/* look for the 'v ' - vertex co-ordinate - flag */
if( strncmp("v ",buffer,2) == 0 )
{
++pMesh->m_iNumberOfVertices;
}
else
/* look for the 'f ' - face - flag */
if( strncmp("f ",buffer,2) == 0 )
{
++pMesh->m_iNumberOfFaces;
}
}
/*
** close the file for now....
*/
fclose(fp);
/*
** Allocate the memory for the data arrays and check that it allocated ok
*/
pMesh->m_aVertexArray = (ObjVertex* )malloc( pMesh->m_iNumberOfVertices * sizeof(ObjVertex) );
assert(pMesh->m_aVertexArray);
/* there are occasionally times when the obj does not have any normals in it */
if( pMesh->m_iNumberOfNormals > 0 )
{
pMesh->m_aNormalArray = (ObjNormal* )malloc( pMesh->m_iNumberOfNormals * sizeof(ObjNormal) );
assert(pMesh->m_aNormalArray);
}
/* there are occasionally times when the obj does not have any tex coords in it */
if( pMesh->m_iNumberOfTexCoords > 0 )
{
pMesh->m_aTexCoordArray = (ObjTexCoord*)malloc( pMesh->m_iNumberOfTexCoords * sizeof(ObjTexCoord) );
assert(pMesh->m_aTexCoordArray);
}
pMesh->m_aFaces = (ObjFace* )malloc( pMesh->m_iNumberOfFaces * sizeof(ObjFace) );
assert(pMesh->m_aFaces);
/*
** Now we know how much data is contained in the file and we've allocated memory to hold it all.
** What we can therefore do now, is load up all of the data in the file easily.
*/
fp = fopen(filename,"r");
while(!feof(fp))
{
memset(buffer,0,255);
/* Grab a line at a time */
fgets(buffer,256,fp);
/* look for the 'vn' - vertex normal - flag */
if( strncmp("vn ",buffer,3) == 0 )
{
sscanf((buffer+2),"%f%f%f",
&pMesh->m_aNormalArray[ nc ].x,
&pMesh->m_aNormalArray[ nc ].y,
&pMesh->m_aNormalArray[ nc ].z);
++nc;
}
else
/* look for the 'vt' - texturing co-ordinate - flag */
if( strncmp("vt ",buffer,3) == 0 )
{
sscanf((buffer+2),"%f%f",
&pMesh->m_aTexCoordArray[ tc ].u,
&pMesh->m_aTexCoordArray[ tc ].v);
++tc;
}
else
/* look for the 'v ' - vertex co-ordinate - flag */
if( strncmp("v ",buffer,2) == 0 )
{
sscanf((buffer+1),"%f%f%f",
&pMesh->m_aVertexArray[ vc ].x,
&pMesh->m_aVertexArray[ vc ].y,
&pMesh->m_aVertexArray[ vc ].z);
++vc;
}
else
/* look for the 'f ' - face - flag */
if( strncmp("f ",buffer,2) == 0 )
{
/*
** some data for later....
*/
char *pSplitString = NULL;
unsigned int i,ii = 0;
unsigned int iNewTrianglesIndex = 0,
iNumberOfExtraTriangles = 0;
unsigned int aFirstIndices[3],
aSecondIndices[3];
ObjFace *pf = NULL;
/*
** These next few lines are used to figure out how many '/' characters there
** are in the string. This gives us the information we need to find out how
** many vertices are used in this face (by dividing by two)
*/
for(i=0;i<strlen(buffer);i++)
{
if(buffer == '/')
ii++;
}
ii/=2;
/*
** This is where it all gets confusing, What I'm going to do is to convert all polys
** With vertices above 3 to triangles. The number of triangles I require will be (ii-2).
** For example, with a poly that has vertices a,b,c,d,e; there will be 3 triangles comprised
** of verts (a,b,c), (a,c,d) and (a,d,e) respectively. We need to check the amount of triangles
** that will be needed for this polygon and resize the array accordingly.
*/
if( (ii-2) > 1 )
{
iNumberOfExtraTriangles = ii-3;
iNewTrianglesIndex = ResizeTriangleArray(pMesh,iNumberOfExtraTriangles);
}
/*
** Pointer to the face we are currently dealing with. It's only used so that
** the code becomes more readable and I have less to type.
*/
pf = &pMesh->m_aFaces[ fc ];
/*
** tokenise the string using strtok(). Basically this splits the string up
** and removes the spaces from each chunk. This way we only have to deal with
** one set of indices at a time for each of the poly's vertices.
*/
pSplitString = strtok((buffer+2)," \t\n");
i=0;
/*
** Get the vertex structures that we know WILL exists (there has to be a minimum of three)
*/
for(i=0;i<3;i++)
{
GetIndices( pSplitString,
&pf->m_aVertexIndices,
&pf->m_aNormalIndices,
&pf->m_aTexCoordIndicies);
/* need to reduce the indices by 1 because array indices start at 0, obj starts at 1 */
--pf->m_aTexCoordIndicies;
--pf->m_aNormalIndices;
--pf->m_aVertexIndices;
/*
** In order to start constructing our triangles that are created after ww split the poly,
** we need to store som of the indices that are going to be used for the extra additional
** triangles after this. Basically we store the first and last sets of indices from this
** first triangle becasue they will be used as the first & second sets for the next triangle.
*/
switch(i)
{
case 0:
aFirstIndices[0] = pf->m_aVertexIndices;
aFirstIndices[1] = pf->m_aNormalIndices;
aFirstIndices[2] = pf->m_aTexCoordIndicies;
break;
case 2:
aSecondIndices[0] = pf->m_aVertexIndices;
aSecondIndices[1] = pf->m_aNormalIndices;
aSecondIndices[2] = pf->m_aTexCoordIndicies;
break;
default:
break;
}
pSplitString = strtok(NULL," \t\n");
}
++fc;
/*
** Get the additional triangles that may be there
*/
for(i=0;i<iNumberOfExtraTriangles;i++)
{
pf = &pMesh->m_aFaces[ iNewTrianglesIndex + i ];
GetIndices( pSplitString,
&pf->m_aVertexIndices[2],
&pf->m_aNormalIndices[2],
&pf->m_aTexCoordIndicies[2]);
/* need to reduce the indices by 1 because array indices start at 0, obj starts at 1 */
--pf->m_aTexCoordIndicies[2];
--pf->m_aNormalIndices[2];
--pf->m_aVertexIndices[2];
/*
** Now assign the other indices for the triangle
*/
pf->m_aVertexIndices[0] = aFirstIndices[0];
pf->m_aNormalIndices[0] = aFirstIndices[1];
pf->m_aTexCoordIndicies[0] = aFirstIndices[2];
pf->m_aVertexIndices[1] = aSecondIndices[0];
pf->m_aNormalIndices[1] = aSecondIndices[1];
pf->m_aTexCoordIndicies[1] = aSecondIndices[2];
/*
** Copy over the indices for the next triangle.
*/
aSecondIndices[0] = pf->m_aVertexIndices[2];
aSecondIndices[1] = pf->m_aNormalIndices[2];
aSecondIndices[2] = pf->m_aTexCoordIndicies[2];
pSplitString = strtok(NULL," \t\n");
}
}
}
fclose(fp);
return pMesh->m_iMeshID;
}
void DrawOBJ(ObjFile id)
{
/*
** Because the meshes are on a linked list, we first need to find the
** mesh with the specified ID number so traverse the list.
*/
ObjMesh *pMesh = g_LinkedListHead;
while(pMesh && pMesh->m_iMeshID != id)
{
pMesh = pMesh->m_pNext;
}
/*
** Check to see if the mesh ID is valid.
*/
if(pMesh != NULL)
{
/*
** In order to draw our mesh, we would basically like something of the following :
**
** for_each_polygon_in_mesh
** {
** start_drawing_poly;
** for_each_vertex_in_the_poly
** {
** use_the_vertex_index_to_find_vertex_in_the_mesh's_array
** }
** }
*/
if( pMesh->m_aNormalArray != NULL &&
pMesh->m_aTexCoordArray != NULL )
{
unsigned int i;
glBegin(GL_TRIANGLES);
for(i=0;i<pMesh->m_iNumberOfFaces;i++)
{
unsigned int j;
ObjFace *pf = &pMesh->m_aFaces;
/*
** Draw the polygons with normals & uv co-ordinates
*/
for(j=0;j<3;j++)
{
glTexCoord2f( pMesh->m_aTexCoordArray[ pf->m_aTexCoordIndicies[j] ].u,
pMesh->m_aTexCoordArray[ pf->m_aTexCoordIndicies[j] ].v);
glNormal3f( pMesh->m_aNormalArray[ pf->m_aNormalIndices[j] ].x,
pMesh->m_aNormalArray[ pf->m_aNormalIndices[j] ].y,
pMesh->m_aNormalArray[ pf->m_aNormalIndices[j] ].z);
glVertex3f( pMesh->m_aVertexArray[ pf->m_aVertexIndices[j] ].x,
pMesh->m_aVertexArray[ pf->m_aVertexIndices[j] ].y,
pMesh->m_aVertexArray[ pf->m_aVertexIndices[j] ].z);
}
}
glEnd();
}
else
if( pMesh->m_aNormalArray != NULL )
{
unsigned int i;
glBegin(GL_TRIANGLES);
for(i=0;i<pMesh->m_iNumberOfFaces;i++)
{
unsigned int j;
ObjFace *pf = &pMesh->m_aFaces;
/*
** Draw the polygons with Normals only
*/
for(j=0;j<3;j++)
{
glNormal3f( pMesh->m_aNormalArray[ pf->m_aNormalIndices[j] ].x,
pMesh->m_aNormalArray[ pf->m_aNormalIndices[j] ].y,
pMesh->m_aNormalArray[ pf->m_aNormalIndices[j] ].z);
glVertex3f( pMesh->m_aVertexArray[ pf->m_aVertexIndices[j] ].x,
pMesh->m_aVertexArray[ pf->m_aVertexIndices[j] ].y,
pMesh->m_aVertexArray[ pf->m_aVertexIndices[j] ].z);
}
}
glEnd();
}
else
if( pMesh->m_aTexCoordArray != NULL )
{
unsigned int i;
glBegin(GL_TRIANGLES);
for(i=0;i<pMesh->m_iNumberOfFaces;i++)
{
unsigned int j;
ObjFace *pf = &pMesh->m_aFaces;
/*
** Draw the polygons with Texturing co-ordinates only
*/
for(j=0;j<3;j++)
{
glTexCoord2f( pMesh->m_aTexCoordArray[ pf->m_aTexCoordIndicies[j] ].u,
pMesh->m_aTexCoordArray[ pf->m_aTexCoordIndicies[j] ].v);
glVertex3f( pMesh->m_aVertexArray[ pf->m_aVertexIndices[j] ].x,
pMesh->m_aVertexArray[ pf->m_aVertexIndices[j] ].y,
pMesh->m_aVertexArray[ pf->m_aVertexIndices[j] ].z);
}
}
glEnd();
}
else
{
unsigned int i;
glBegin(GL_TRIANGLES);
for(i=0;i<pMesh->m_iNumberOfFaces;i++)
{
unsigned int j;
ObjFace *pf = &pMesh->m_aFaces;
/*
** Draw the polygons with Texturing co-ordinates only
*/
for(j=0;j<3;j++)
{
glVertex3f( pMesh->m_aVertexArray[ pf->m_aVertexIndices[j] ].x,
pMesh->m_aVertexArray[ pf->m_aVertexIndices[j] ].y,
pMesh->m_aVertexArray[ pf->m_aVertexIndices[j] ].z);
}
}
glEnd();
}
}
}
/*
** Calling free() on NULL is VERY BAD in C, so make sure we
** check all pointers before calling free.
*/
void DeleteMesh(ObjMesh* pMesh)
{
/*
** If the pointer is valid
*/
if(pMesh)
{
/* delete the face array */
if(pMesh->m_aFaces)
{
free(pMesh->m_aFaces);
pMesh->m_aFaces = NULL;
}
/* delete the vertex array */
if(pMesh->m_aVertexArray)
{
free(pMesh->m_aVertexArray);
pMesh->m_aVertexArray = NULL;
}
/* delete the normal array */
if(pMesh->m_aNormalArray)
{
free(pMesh->m_aNormalArray);
pMesh->m_aNormalArray = NULL;
}
/* delete the texturing co-ordinate array */
if(pMesh->m_aTexCoordArray)
{
free(pMesh->m_aTexCoordArray);
pMesh->m_aTexCoordArray = NULL;
}
/* free the mesh */
free( pMesh );
}
}
void DeleteOBJ(ObjFile id)
{
/*
** Create two pointers to walk through the linked list
*/
ObjMesh *pCurr,
*pPrev = NULL;
/*
** Start traversing the list from the start
*/
pCurr = g_LinkedListHead;
/*
** Walk through the list until we either reach the end, or
** we find the node we are looking for
*/
while(pCurr != NULL && pCurr->m_iMeshID != id)
{
pPrev = pCurr;
pCurr = pCurr->m_pNext;
}
/*
** If we found the node that needs to be deleted
*/
if(pCurr != NULL)
{
/*
** If the pointer before it is NULL, then we need to
** remove the first node in the list
*/
if(pPrev == NULL)
{
g_LinkedListHead = pCurr->m_pNext;
}
/*
** Otherwise we are removing a node from somewhere else
*/
else
{
pPrev->m_pNext = pCurr->m_pNext;
}
/*
** Free the memory allocated for this mesh
*/
DeleteMesh(pCurr);
}
}
/*
** Delete all of the meshes starting from the front.
*/
void CleanUpOBJ(void)
{
ObjMesh *pCurr;
while(g_LinkedListHead != NULL)
{
pCurr = g_LinkedListHead;
g_LinkedListHead = g_LinkedListHead->m_pNext;
DeleteMesh(pCurr);
}
}