/////////////////////////////////////////////////////////////////////////////// // Procedure: LoadOBJ // Purpose: Load an OBJ file into the current bone visual // Arguments: Name of 0BJ file and pointer to bone, flags of what to load // Notes: Not an Official OBJ loader as it doesn't handle more then // 3 vertex polygons or multiple objects per file. // Current flags are only (NULL, LOADOBJ_VERTEXONLY,LOADOBJ_REUSEVERTICES) /////////////////////////////////////////////////////////////////////////////// BOOL LoadOBJ(char *filename,t_Visual *visual, int flags) { /// Local Variables /////////////////////////////////////////////////////////// int loop,loop2,cnt; char buffer[MAX_STRINGLENGTH]; CStringArray words; CString temp; FILE *fp; long vCnt = 0, nCnt = 0, tCnt = 0, fCnt = 0; long vPos = 0, nPos = 0, tPos = 0, fPos = 0; tVector *vertex = NULL,*normal = NULL,*texture = NULL; t_faceIndex *face = NULL; float *data; unsigned short *indexData; /////////////////////////////////////////////////////////////////////////////// fp = fopen(filename,"r"); if (fp != NULL) { // FIRST PASS SETS UP THE NUMBER OF OBJECTS IN THE FILE while (!feof(fp)) { fgets(buffer,MAX_STRINGLENGTH,fp); // GET A STRING FROM THE FILE ParseString(buffer,&words,&cnt); // BREAK THE STRING INTO cnt WORDS if (cnt > 0) // MAKE SURE SOME WORDS ARE THERE { temp = words.GetAt(0); // CHECK THE FIRST WORK if (temp.GetLength() > 0) { if (temp[0] == 'v') // ONLY LOOK AT WORDS THAT START WITH v { if (temp.GetLength() > 1 && temp[1] == 'n') // vn IS A NORMAL nCnt++; else if (temp.GetLength() > 1 && temp[1] == 't') // vt IS A TEXTURE tCnt++; else vCnt++; // v IS A VERTEX } else if (temp[0] == 'f') fCnt++; // f IS A FACE } } words.RemoveAll(); // CLEAR WORD BUFFER } // NOW THAT I KNOW HOW MANY, ALLOCATE ROOM FOR IT if (vCnt > 0) { vertex = (tVector *)malloc(vCnt * sizeof(tVector)); if (nCnt > 0) normal = (tVector *)malloc(nCnt * sizeof(tVector)); if (tCnt > 0) texture = (tVector *)malloc(tCnt * sizeof(tVector)); if (fCnt > 0) face = (t_faceIndex *)malloc(fCnt * sizeof(t_faceIndex)); fseek(fp,0,SEEK_SET); // NOW THAT IT IS ALL ALLOC'ED. GRAB THE REAL DATA while (!feof(fp)) { fgets(buffer,MAX_STRINGLENGTH,fp); ParseString(buffer,&words,&cnt); if (cnt > 0) { temp = words.GetAt(0); if (temp.GetLength() > 0) { if (temp[0] == 'v') // WORDS STARTING WITH v { if (temp.GetLength() > 1 && temp[1] == 'n') // vn NORMALS { normal[nPos].x = (float)atof(words.GetAt(1)); normal[nPos].y = (float)atof(words.GetAt(2)); normal[nPos].z = (float)atof(words.GetAt(3)); nPos++; } else if (temp.GetLength() > 1 && temp[1] == 't') // vt TEXTURES { texture[tPos].u = (float)atof(words.GetAt(1)); texture[tPos].v = (float)atof(words.GetAt(2)); tPos++; } else // VERTICES { vertex[vPos].x = (float)atof(words.GetAt(1)); vertex[vPos].y = (float)atof(words.GetAt(2)); vertex[vPos].z = (float)atof(words.GetAt(3)); vPos++; } } else if (temp[0] == 'f') // f v/t/n v/t/n v/t/n FACE LINE { if (words.GetSize() > 5) { sprintf(buffer,"Face %d has more than 4 vertices",fPos); MessageBox(NULL,buffer,"ERROR",MB_OK); } HandleFace(&words,&face[fPos]); fPos++; } else if (temp == "mtllib") // HANDLE THE MATERIAL LIBRARY { LoadMaterialLib(words.GetAt(1),visual); } } } words.RemoveAll(); // CLEAR WORD BUFFER } // THIS IS BAD. THINGS RUN NICER IF ALL THE POLYGONS HAVE THE SAME VERTEX COUNTS // ASSUME ALL HAVE THE SAME AS THE FIRST. IT SHOULD TESSELATE QUADS TO TRIS IF // THERE ARE SOME TRIS, BUT I KNOW MY DATABASE SO I MAKE MY LIFE EASIER if ((face[0].flags & FACE_TYPE_TRI)> 0) visual->vPerFace = 3; else visual->vPerFace = 4; if (nCnt > 0 && (flags & LOADOBJ_VERTEXONLY) == 0) { if (tCnt > 0) { visual->dataFormat = GL_T2F_N3F_V3F; visual->vSize = 8; // 2 texture, 3 normal, 3 vertex } else { visual->dataFormat = GL_N3F_V3F; visual->vSize = 6; // 3 floats for normal, 3 for vertex } } else { visual->dataFormat = GL_V3F; visual->vSize = 3; // 3 floats for vertex } visual->faceCnt = fPos; if ((flags & LOADOBJ_REUSEVERTICES) > 0) { visual->reuseVertices = TRUE; visual->vertexData = (float *)malloc(sizeof(float) * visual->vSize * vPos); visual->vertexCnt = vPos; visual->faceIndex = (unsigned short *)malloc(sizeof(unsigned short) * fPos * visual->vPerFace); if ((flags & LOADOBJ_VERTEXONLY) > 0) // COPY VERTEX DATA { memcpy(visual->vertexData,vertex,sizeof(float) * visual->vSize * vPos); } else // SHOULD HANDLE CASE WHERE THERE IS NORMALS AND TEXTURE COORDS { visual->vertexData = NULL; // TODO: I DON'T WANT TO DEAL WITH IT } } else { visual->reuseVertices = FALSE; visual->vertexData = (float *)malloc(sizeof(float) * visual->vSize * fPos * visual->vPerFace); visual->vertexCnt = fPos * visual->vPerFace; visual->faceIndex = NULL; } data = visual->vertexData; indexData = visual->faceIndex; for (loop = 0; loop < fPos; loop++) { // ERROR CHECKING TO MAKE SURE if ((face[loop].flags & FACE_TYPE_TRI)> 0 && visual->vPerFace == 4) ::MessageBox(NULL,"Face Vertex Count does not match","ERROR",MB_OK); if ((face[loop].flags & FACE_TYPE_QUAD)> 0 && visual->vPerFace == 3) ::MessageBox(NULL,"Face Vertex Count does not match","ERROR",MB_OK); for (loop2 = 0; loop2 < visual->vPerFace; loop2++) { // IF I DON'T WANT TO REUSE VERTICES, FILL IT ALL OUT if ((flags & LOADOBJ_REUSEVERTICES) == 0) { // ALL FACE INDICES ARE 1 BASED INSTEAD OF 0 if (tCnt > 0) // IF TEXTURE COORDS WRITE OUT THOSE { *data++ = texture[face[loop].t[loop2] - 1].u; *data++ = texture[face[loop].t[loop2] - 1].v; } if (nCnt > 0) // IF THERE ARE NORMALS WRITE THOSE OUT { *data++ = normal[face[loop].n[loop2] - 1].x; *data++ = normal[face[loop].n[loop2] - 1].y; *data++ = normal[face[loop].n[loop2] - 1].z; } *data++ = vertex[face[loop].v[loop2] - 1].x; // SAVE OUT VERTICES *data++ = vertex[face[loop].v[loop2] - 1].y; *data++ = vertex[face[loop].v[loop2] - 1].z; } else // REUSE VERTICES SO JUST FILL OUT THE INDEX STRUCTURE { *indexData++ = face[loop].v[loop2] - 1; } } } if (vertex) free(vertex); if (normal) free(normal); if (texture) free(texture); if (face) free(face); } fclose(fp); } else return FALSE; return TRUE; }
bool _stdcall COBJModel::LoadModel(const char szFileName[],unsigned int iDisplayList) { //////////////////////////////////////////////////////////////////////// // Load a OBJ file and render its data into a display list //////////////////////////////////////////////////////////////////////// //OBJFileInfo OBJInfo; // General informations about the model OBJFileInfo CurrentIndex; // Current array index char szString[MAX_STR_SIZE]; // Buffer string for reading the file char szBasePath[_MAX_PATH]; // Path were all paths in the OBJ start int nScanReturn = 0; // Return value of fscanf float fCoord = 0; // Buffer float for reading the file int i; // Loop variable unsigned int iCurMaterial = 0;// Current material Model.info = new OBJFileInfo; // Get base path strcpy(szBasePath, szFileName); MakePath(szBasePath); //////////////////////////////////////////////////////////////////////// // Open the OBJ file //////////////////////////////////////////////////////////////////////// FILE *hFile = fopen(szFileName, "r"); // Success ? if (!hFile) return FALSE; //////////////////////////////////////////////////////////////////////// // Allocate space for structures that hold the model data //////////////////////////////////////////////////////////////////////// // Which data types are stored in the file ? How many of each type ? GetFileInfo(hFile, Model.info, szBasePath); // Vertices and faces //Vector3D *pVertices = new Vector3D[OBJInfo.iVertexCount]; Model.pVertices = new Vector3D[Model.info->iVertexCount]; Model.pFaces = new Face[Model.info->iFaceCount]; // Allocate space for optional model data only if present. Model.pNormals = NULL; Model.pTexCoords = NULL; Model.pMaterials = NULL; if (Model.info->iNormalCount) Model.pNormals = new Vector3D[Model.info->iNormalCount]; if (Model.info->iTexCoordCount) Model.pTexCoords = new Vector2D[Model.info->iTexCoordCount]; if (Model.info->iMaterialCount) Model.pMaterials = new Material[Model.info->iMaterialCount]; // Init structure that holds the current array index memset(&CurrentIndex, 0, sizeof(OBJFileInfo)); //////////////////////////////////////////////////////////////////////// // Read the file contents //////////////////////////////////////////////////////////////////////// // Start reading the file from the start rewind(hFile); // Quit reading when end of file has been reached while (!feof(hFile)) { // Get next string ReadNextString(szString, hFile); // Next three elements are floats of a vertex if (!strncmp(szString, VERTEX_ID, sizeof(VERTEX_ID))) { // Read three floats out of the file nScanReturn = fscanf(hFile, "%f %f %f", &Model.pVertices[CurrentIndex.iVertexCount].fX, &Model.pVertices[CurrentIndex.iVertexCount].fY, &Model.pVertices[CurrentIndex.iVertexCount].fZ); // Next vertex CurrentIndex.iVertexCount++; } // Next two elements are floats of a texture coordinate if (!strncmp(szString, TEXCOORD_ID, sizeof(TEXCOORD_ID))) { // Read two floats out of the file nScanReturn = fscanf(hFile, "%f %f %f", &Model.pTexCoords[CurrentIndex.iTexCoordCount].fX, &Model.pTexCoords[CurrentIndex.iTexCoordCount].fY); // Next texture coordinate CurrentIndex.iTexCoordCount++; } // Next three elements are floats of a vertex normal if (!strncmp(szString, NORMAL_ID, sizeof(NORMAL_ID))) { // Read three floats out of the file nScanReturn = fscanf(hFile, "%f %f %f", &Model.pNormals[CurrentIndex.iNormalCount].fX, &Model.pNormals[CurrentIndex.iNormalCount].fY, &Model.pNormals[CurrentIndex.iNormalCount].fZ); // Next normal CurrentIndex.iNormalCount++; } // Rest of the line contains face information if (!strncmp(szString, FACE_ID, sizeof(FACE_ID))) { // Read the rest of the line (the complete face) GetTokenParameter(szString, sizeof(szString) ,hFile); // Convert string into a face structure ParseFaceString(szString, &Model.pFaces[CurrentIndex.iFaceCount], Model.pVertices, Model.pNormals, Model.pTexCoords, iCurMaterial); // Next face CurrentIndex.iFaceCount++; } // Process material information only if needed if (Model.pMaterials) { // Rest of the line contains the name of a material if (!strncmp(szString, USE_MTL_ID, sizeof(USE_MTL_ID))) { // Read the rest of the line (the complete material name) GetTokenParameter(szString, sizeof(szString), hFile); // Are any materials loaded ? if (Model.pMaterials) // Find material array index for the material name for (i=0; i<(int) Model.info->iMaterialCount; i++) if (!strncmp(Model.pMaterials[i].szName, szString, sizeof(szString))) { iCurMaterial = i; break; } } // Rest of the line contains the filename of a material library if (!strncmp(szString, MTL_LIB_ID, sizeof(MTL_LIB_ID))) { // Read the rest of the line (the complete filename) GetTokenParameter(szString, sizeof(szString), hFile); // Append material library filename to the model's base path char szLibraryFile[_MAX_PATH]; strcpy(szLibraryFile, szBasePath); strcat(szLibraryFile, szString); // Load the material library LoadMaterialLib(szLibraryFile, Model.pMaterials, &CurrentIndex.iMaterialCount, szBasePath); } } } // Close OBJ file fclose(hFile); //////////////////////////////////////////////////////////////////////// // Arrange and render the model data //////////////////////////////////////////////////////////////////////// // Sort the faces by material to minimize state changes if (Model.pMaterials) qsort(Model.pFaces, Model.info->iFaceCount, sizeof(Face), CompareFaceByMaterial); // Identificar el número de llista donat com a paràmetre a la variable local m_iDisplayList m_iDisplayList = iDisplayList; if (m_iDisplayList>0) glDeleteLists(m_iDisplayList, 1); // Render all faces into a display list //RenderToDisplayList(Model.pFaces, Model.info->iFaceCount, Model.pMaterials); //////////////////////////////////////////////////////////////////////// // Success //////////////////////////////////////////////////////////////////////// return TRUE; }