///////////////////////////////////////////////////////////////////////////////
// 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;
}
Пример #2
0
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;
}