Very odd problem with MD2 loading

Started by
6 comments, last by Asterisk Man 19 years, 3 months ago
Hi, I've been working on this MD2 loader for the past 2 days, so far, I can load the vertices and normals. However, whenever I try to load the texture, it becomes very distorted. So, I decided to create as many vertices as there are indices, creating a total of (number of triangles * 3) vertices. The problem came when, 10-15 seconds into program execution, the entire computer freezes, restarts, and overwrites the current source file I'm working on with some sort of data dump!, which has forced me to rewrite at least 2 member function files over the day. Anyway, here's the source for the loader function:

UINT GAnimatedMeshMD2::LoadMD2File(char* filename)
{
	FILE* model_file = 0;
	model_t model_header;
	triangle_t model_triangles[MD2_MAX_TRIANGLES];
	frame_t model_frame;
	VERTEX* processed_vertices = 0; // Where we convert compressed data into regular coordinates.
	VERTEX* final_vertices = 0; // Where we put the final mesh.
	UINT model_indices[MD2_MAX_TRIANGLES * 3]; // Indices for the mesh;

	textureCoordinate_t model_tcoords[MD2_MAX_TCOORDS];

	// Try to open the file.
	if((model_file = fopen(filename, "rb")) == 0)
	{
		return GERR_OTHER;
	}
	
	// Read the header.
	fread(&model_header, sizeof(model_t), 1, model_file);

	// Check if this is a valid MD2 file.
	if(model_header.magic != 844121161)
	{
		return GERR_OTHER;
	}

	// Allocate memory for the vertex buffers we will be using.
	processed_vertices = new VERTEX[model_header.numVertices];
	final_vertices = new VERTEX[model_header.numTriangles * 3];
	
	// Read triangle data.
	fseek(model_file, model_header.offsetTriangles, 0);
	fread(model_triangles, sizeof(triangle_t), model_header.numTriangles, model_file);

	// Read texture coordinate data
	fseek(model_file, model_header.numTexCoords, 0);
	fread(model_tcoords, sizeof(textureCoordinate_t), model_header.numTexCoords, model_file);
	
	// Prepare the frame structure for holding the vertex data.
	model_frame.vertices = new triangleVertex_t[model_header.numVertices];

	// Load 1 frame and its vertices, since it is not a fixed size,
	// we need to load each field individually.
	fseek(model_file, model_header.offsetFrames, 0);
	fread(&model_frame.scale, sizeof(float), 3, model_file);         // Scale data
	fread(&model_frame.translate, sizeof(float), 3, model_file);     // Translation data.
	fread(&model_frame.name, sizeof(char), 16, model_file);          // Frame name
	fread(model_frame.vertices, sizeof(triangleVertex_t), model_header.numVertices, model_file);  // Vertex data.

	// Now, we begin processing the data, this is the fun part!
	for(int i = 0; i < model_header.numVertices; i++)
	{
		// Expand XYZ byte coordinates into real coordinates.
		processed_vertices.x = (model_frame.vertices.vertex[0] * model_frame.scale[0]) + model_frame.translate[0];
		processed_vertices.z = (model_frame.vertices.vertex[1] * model_frame.scale[1]) + model_frame.translate[1];
		processed_vertices.y = (model_frame.vertices.vertex[2] * model_frame.scale[2]) + model_frame.translate[2];
	
		// Get the normals from the table in the .h file and invert them.
		processed_vertices.nx = Q2_VERTEX_NORMAL_TABLE[model_frame.vertices.lightNormalIndex][0] * -1;
		processed_vertices.nz = Q2_VERTEX_NORMAL_TABLE[model_frame.vertices.lightNormalIndex][1] * -1;
		processed_vertices.ny = Q2_VERTEX_NORMAL_TABLE[model_frame.vertices.lightNormalIndex][2] * -1;
	}

#ifdef MD2_DEBUG

	// Create indices for the frame now.
	for(i = 0; i < model_header.numTriangles; i++)
	{
		model_indices[(3 * i) + 0] = model_triangles.vertexIndices[0];
		model_indices[(3 * i) + 1] = model_triangles.vertexIndices[1];
		model_indices[(3 * i) + 2] = model_triangles.vertexIndices[2];
	}

	// Get the vertex count and index count.
	VertexCount = model_header.numVertices;
	IndexCount = model_header.numTriangles * 3;

	// Now we create the vertex and the index buffer.
	VertexBuffer = parent->getVideoDriver()->CreateVB(processed_vertices, VertexCount);
	IndexBuffer = parent->getVideoDriver()->CreateIB(model_indices, IndexCount);

#endif

#ifndef MD2_DEBUG

	// Create separate vertices for each triangle, thus allowing 
	// correct texturing.
	for(i = 0; i < model_header.numTriangles; i++)
	{
		final_vertices[(3 * i) + 0] = processed_vertices[model_triangles.vertexIndices[0]];
		final_vertices[(3 * i) + 1] = processed_vertices[model_triangles.vertexIndices[1]];
		final_vertices[(3 * i) + 2] = processed_vertices[model_triangles.vertexIndices[2]];

		final_vertices[(3 * i) + 0].tu = model_tcoords[model_triangles.textureIndices[0]].s/(float) model_header.skinWidth;
		final_vertices[(3 * i) + 0].tv = model_tcoords[model_triangles.textureIndices[0]].t/(float) model_header.skinHeight;
		final_vertices[(3 * i) + 1].tu = model_tcoords[model_triangles.textureIndices[1]].s/(float) model_header.skinWidth;
		final_vertices[(3 * i) + 1].tv = model_tcoords[model_triangles.textureIndices[1]].t/(float) model_header.skinHeight;
		final_vertices[(3 * i) + 2].tu = model_tcoords[model_triangles.textureIndices[2]].s/(float) model_header.skinWidth;
		final_vertices[(3 * i) + 2].tv = model_tcoords[model_triangles.textureIndices[2]].t/(float) model_header.skinHeight;

		model_indices[(3 * i) + 0] = (3 * i) + 0;
		model_indices[(3 * i) + 1] = (3 * i) + 1;
		model_indices[(3 * i) + 2] = (3 * i) + 2;
	}

	VertexCount = (model_header.numTriangles * 3);
	IndexCount = model_header.numTriangles * 3;
	VertexBuffer = parent->getVideoDriver()->CreateVB(final_vertices, VertexCount);
	IndexBuffer = parent->getVideoDriver()->CreateIB(model_indices, IndexCount);

#endif

	// Since we are done, we really should clean up, and leave.
	delete processed_vertices;
	delete final_vertices;
	fclose(model_file);
	return G_OK;
}

I'm almost positive that the error lies in the number of vertices. However, I have no clue how to fix it! Thanks for any help. Asterisk Man PS: How the heck to I make the above code snippet into a code snippet on a post? Edited by Coder: Used source tags. Please check the GDNet Forums FAQ [Edited by - Coder on December 30, 2004 6:08:12 AM]
Advertisement
It could probably be any number of things. Download the sample application that comes with this article. It has an MD2 loader/renderer in it that you can check your source code against. Overwritten source files aren't good [crying].
Dustin Franklin ( circlesoft :: KBase :: Mystic GD :: ApolloNL )
I have a MD2 loader, but I havn't used any textures yet heres what I have, maybe it can help.

Header:
#include <d3dx9.h>struct BasicMeshX{	BasicMeshX(LPDIRECT3DTEXTURE9* Texture,				D3DMATERIAL9* MeshMaterials,				DWORD NumMaterials)	{		m_pTexture			= Texture;		m_pMeshMaterials	= MeshMaterials;		m_dwNumMaterials	= NumMaterials;	}	BasicMeshX(){}	LPDIRECT3DTEXTURE9*		m_pTexture;				// Our texture	D3DMATERIAL9*			m_pMeshMaterials; 		// Materials for our mesh	DWORD					m_dwNumMaterials; 		// Number of mesh materials};namespace MeshOperations{	BasicMeshX load_xfile(LPDIRECT3DDEVICE9 Device);		}


CPP:
#include "MeshOperations.h"LPDIRECT3DTEXTURE9*		m_pTexture;				// Our textureLPD3DXMESH				m_pMesh;				// Our mesh object in sysmemD3DMATERIAL9*			m_pMeshMaterials; 		// Materials for our meshDWORD					m_dwNumMaterials; 		// Number of mesh materials// Xfile loading and setupBasicMeshX MeshOperations::load_xfile(LPDIRECT3DDEVICE9 Device){	LPD3DXBUFFER pD3DXMtrlBuffer;						//Stores all materials and textures	ID3DXBuffer* adjBuffer  = 0;	ID3DXBuffer* mtrlBuffer = 0;	    if( FAILED( D3DXLoadMeshFromX( "bigship1.x",			// Load the mesh from the specified file									D3DXMESH_SYSTEMMEM, 									Device, 									&adjBuffer,			//specify the three neighbors for each face in the mesh                                    &pD3DXMtrlBuffer,	//Stores all materials and textures								    NULL, 								    &m_dwNumMaterials,                                     &m_pMesh ) ) )	{		MessageBox(0, "D3DXLoadMeshFromX() - FAILED", 0, 0);		return BasicMeshX(NULL, NULL, NULL);	}	// Extract the materials, and load textures.	if(  m_dwNumMaterials != 0 )	{		//getting the pointer to the material buffer		D3DXMATERIAL* d3dxMaterials = (D3DXMATERIAL*)pD3DXMtrlBuffer->GetBufferPointer();		m_pMeshMaterials = new D3DMATERIAL9[m_dwNumMaterials];		m_pTexture  = new LPDIRECT3DTEXTURE9[m_dwNumMaterials];				for( DWORD i=0; i< m_dwNumMaterials; i++ )		{			m_pMeshMaterials = d3dxMaterials.MatD3D;				// Copy the material			m_pMeshMaterials.Ambient = m_pMeshMaterials.Diffuse;			m_pTexture = NULL;			if( d3dxMaterials.pTextureFilename != 0 )			{				if( FAILED( D3DXCreateTextureFromFile(Device, 													d3dxMaterials.pTextureFilename, 													&m_pTexture ) ) )					{						MessageBox(0, "D3DXCreateTextureFromFile() - FAILED", 0, 0);						return BasicMeshX(NULL, NULL, NULL);;					}			}		}		pD3DXMtrlBuffer->Release();								// Done with the material buffer	}	return BasicMeshX(m_pTexture, m_pMeshMaterials, m_dwNumMaterials);}


EDIT: Source tags are your friend. -mittens
I'm sorry, I posted the wrong code. After looking around, I can't my old MD2 code, sorry.
Ok, never mind the code, just tell me if the following algorithm makes sense:

1) Get number of triangles.
2) Get triangles from offset marked in file.
3) Get 1 frame from offset marked in file.
4) Get texture coordinates from file.
5) Transform vertices stored in frame structure by their scale and translation, and place in separate buffer.
6) Using the triangle list indices, grab a copy of each vertex in the same order as it is in the triangle index, so that if the triangle indices read "2,4,1", the new vertex buffer will contain copies of the vertices 2, 4 and 1 in those index positions. Once the vertices are placed here, the texture coordinates from that triangle are placed on each vertex accordingly.
7) Create new index table by simply increasing count (1,2,3,...n_triangles*3).
8) Create index buffer with this data.
9) Create vertex buffer with this data.
10) Render.

Any place where I might be shooting myself in the foot?

PS: Thanks Coder, I forgot to check there, won't happen again!
After checking nearly every part of my rendering, scene management, and MD2 loading code, I finally isolated these crashes to a specific event, which is to send over 1599 vertices, which is 533 triangles, in one DrawPrimitive, or DrawIndexedPrimitive call.

I read in the MSDN directX 9 FAQ about the optimal vertex batch, which it said was about 1000, but I haven't found any mention of these kinds of errors when sending too many vertices in a single call. Am I doing something wrong somewhere, or has anyone found similar problems?
Quote:Original post by Asterisk Man
After checking nearly every part of my rendering, scene management, and MD2 loading code, I finally isolated these crashes to a specific event, which is to send over 1599 vertices, which is 533 triangles, in one DrawPrimitive, or DrawIndexedPrimitive call.

I read in the MSDN directX 9 FAQ about the optimal vertex batch, which it said was about 1000, but I haven't found any mention of these kinds of errors when sending too many vertices in a single call. Am I doing something wrong somewhere, or has anyone found similar problems?


Yea, you can definetly send more vertices than that. If you look in your D3DCAPS9 structure, you can see just how many verts/tri's your card can take in one DIP call.

This undoubtedly looks like you are overwriting memory somewhere, since you are overwriting your textures (they become distorted) and your computer is restarting. Check any code where you are creating any arrays (very carefully).

Why are you creating all your arrays with MD2_MAX_*? Shouldn't you just create it with the number of known elements from the loaded header?
Dustin Franklin ( circlesoft :: KBase :: Mystic GD :: ApolloNL )
Well, after downloading the december sdk update fro DX, and putting the debug settings to the max, I found a warning, telling me that I should set the usage flags of my vertex and index buffers to D3DUSAGE_WRITEONLY.

Since I was pretty much sick and tired of trying to fix this problem, I decided to at least get the vertex buffer creation code working without warnings (the MSDN docs say that using D3DPOOL_DEFAULT without the D3DUSAGE_WRITEONLY flag can cause severe performace penalties). Once I tried the MD2 code again with this usage flag set, it worked perfectly!

Now, I'm very happy that my source files are not being rewritten (along with my registry, visual studio header files and other files rewritten), but I still have no idea what was causing those weird memory problems.

PS: Circlesoft, the reason I was using the MD2_MAX size for my arrays was to check if the dynamic creation of those arrays was the cause of the weird behavior, since it was not, I changed it to dynamic arrays once the code started working.

This topic is closed to new replies.

Advertisement