MD2 Loading woes (DirectX and c++)

Started by
8 comments, last by hughiecoles 15 years, 9 months ago
ok, i wote an md2 loader, and it works fine, but i have no idea how to apply normals, and texture coords. does anyone have any hints or suggestions?
--------------------------------------Not All Martyrs See Divinity, But At Least You Tried
Advertisement
DISLAIMER: This is a very generalized outline.[smile]

Assuming you want to display a vertexbuffer with the data and want to use vertex positions, normals and texture coords:

1. define a vertex format similar to D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_TEX1
2. create a vertex buffer of sufficient size for all the vertices, with the vertex format that you defined.
3. load the vertex positions, normals and tex coords into the vertex buffer.
4. load the texture.
5. to render: set the texture, set the stream source, set the FVF and call DrawPrimitive().

If any of these steps are unfamiliar to you, review the examples in the SDK which create and display vertex buffers.

A similar sequence would be followed to load the data into a mesh rather than a vertex buffer.

I'm not familiar with the md2 format or, obviously, how you're loading it, but an index buffer may be needed, also. If you're not familiar with index buffers, read up on them and review the examples in the SDK which create and use an index buffer for a vertex buffer or mesh.

Please don't PM me with questions. Post them in the forums for everyone's benefit, and I can embarrass myself publicly.

You don't forget how to play when you grow old; you grow old when you forget how to play.

The MD2 format uses a large table of normals, instead of storing each normal in a file.

In your MD2Frame struct, which should be defined as


struct MD2Frame{	float Scale[3];	float Translate[3];	char Name[16];	MD2Triangle AliasVertices[1];//or similar name};


there is member AliasVertices, which should be defined as...

struct MD2Triangle{	unsigned char Vertex[3];	unsigned char NormalIndex;};


The member "NormalIndex" holds the index into the giant normal table, which is...

static const float MD2_NORMAL_TABLE[162][3] = {	{-0.525731f, 0.000000f, 0.850651f},	{-0.442863f, 0.238856f, 0.864188f},	{-0.295242f, 0.000000f, 0.955423f},	{-0.309017f, 0.500000f, 0.809017f},	{-0.162460f, 0.262866f, 0.951056f},	{0.000000f, 0.000000f, 1.000000f},	{0.000000f, 0.850651f, 0.525731f},	{-0.147621f, 0.716567f, 0.681718f},	{0.147621f, 0.716567f, 0.681718f},	{0.000000f, 0.525731f, 0.850651f},	{0.309017f, 0.500000f, 0.809017f},	{0.525731f, 0.000000f, 0.850651f},	{0.295242f, 0.000000f, 0.955423f},	{0.442863f, 0.238856f, 0.864188f},	{0.162460f, 0.262866f, 0.951056f},	{-0.681718f, 0.147621f, 0.716567f},	{-0.809017f, 0.309017f, 0.500000f},	{-0.587785f, 0.425325f, 0.688191f},	{-0.850651f, 0.525731f, 0.000000f},	{-0.864188f, 0.442863f, 0.238856f},	{-0.716567f, 0.681718f, 0.147621f},	{-0.688191f, 0.587785f, 0.425325f},	{-0.500000f, 0.809017f, 0.309017f},	{-0.238856f, 0.864188f, 0.442863f},	{-0.425325f, 0.688191f, 0.587785f},	{-0.716567f, 0.681718f, -0.147621f},	{-0.500000f, 0.809017f, -0.309017f},	{-0.525731f, 0.850651f, 0.000000f},	{0.000000f, 0.850651f, -0.525731f},	{-0.238856f, 0.864188f, -0.442863f},	{0.000000f, 0.955423f, -0.295242f},	{-0.262866f, 0.951056f, -0.162460f},	{0.000000f, 1.000000f, 0.000000f},	{0.000000f, 0.955423f, 0.295242f},	{-0.262866f, 0.951056f, 0.162460f},	{0.238856f, 0.864188f, 0.442863f},	{0.262866f, 0.951056f, 0.162460f},	{0.500000f, 0.809017f, 0.309017f},	{0.238856f, 0.864188f, -0.442863f},	{0.262866f, 0.951056f, -0.162460f},	{0.500000f, 0.809017f, -0.309017f},	{0.850651f, 0.525731f, 0.000000f},	{0.716567f, 0.681718f, 0.147621f},	{0.716567f, 0.681718f, -0.147621f},	{0.525731f, 0.850651f, 0.000000f},	{0.425325f, 0.688191f, 0.587785f},	{0.864188f, 0.442863f, 0.238856f},	{0.688191f, 0.587785f, 0.425325f},	{0.809017f, 0.309017f, 0.500000f},	{0.681718f, 0.147621f, 0.716567f},	{0.587785f, 0.425325f, 0.688191f},	{0.955423f, 0.295242f, 0.000000f},	{1.000000f, 0.000000f, 0.000000f},	{0.951056f, 0.162460f, 0.262866f},	{0.850651f, -0.525731f, 0.000000f},	{0.955423f, -0.295242f, 0.000000f},	{0.864188f, -0.442863f, 0.238856f},	{0.951056f, -0.162460f, 0.262866f},	{0.809017f, -0.309017f, 0.500000f},	{0.681718f, -0.147621f, 0.716567f},	{0.850651f, 0.000000f, 0.525731f},	{0.864188f, 0.442863f, -0.238856f},	{0.809017f, 0.309017f, -0.500000f},	{0.951056f, 0.162460f, -0.262866f},	{0.525731f, 0.000000f, -0.850651f},	{0.681718f, 0.147621f, -0.716567f},	{0.681718f, -0.147621f, -0.716567f},	{0.850651f, 0.000000f, -0.525731f},	{0.809017f, -0.309017f, -0.500000f},	{0.864188f, -0.442863f, -0.238856f},	{0.951056f, -0.162460f, -0.262866f},	{0.147621f, 0.716567f, -0.681718f},	{0.309017f, 0.500000f, -0.809017f},	{0.425325f, 0.688191f, -0.587785f},	{0.442863f, 0.238856f, -0.864188f},	{0.587785f, 0.425325f, -0.688191f},	{0.688191f, 0.587785f, -0.425325f},	{-0.147621f, 0.716567f, -0.681718f},	{-0.309017f, 0.500000f, -0.809017f},	{0.000000f, 0.525731f, -0.850651f},	{-0.525731f, 0.000000f, -0.850651f},	{-0.442863f, 0.238856f, -0.864188f},	{-0.295242f, 0.000000f, -0.955423f},	{-0.162460f, 0.262866f, -0.951056f},	{0.000000f, 0.000000f, -1.000000f},	{0.295242f, 0.000000f, -0.955423f},	{0.162460f, 0.262866f, -0.951056f},	{-0.442863f, -0.238856f, -0.864188f},	{-0.309017f, -0.500000f, -0.809017f},	{-0.162460f, -0.262866f, -0.951056f},	{0.000000f, -0.850651f, -0.525731f},	{-0.147621f, -0.716567f, -0.681718f},	{0.147621f, -0.716567f, -0.681718f},	{0.000000f, -0.525731f, -0.850651f},	{0.309017f, -0.500000f, -0.809017f},	{0.442863f, -0.238856f, -0.864188f},	{0.162460f, -0.262866f, -0.951056f},	{0.238856f, -0.864188f, -0.442863f},	{0.500000f, -0.809017f, -0.309017f},	{0.425325f, -0.688191f, -0.587785f},	{0.716567f, -0.681718f, -0.147621f},	{0.688191f, -0.587785f, -0.425325f},	{0.587785f, -0.425325f, -0.688191f},	{0.000000f, -0.955423f, -0.295242f},	{0.000000f, -1.000000f, 0.000000f},	{0.262866f, -0.951056f, -0.162460f},	{0.000000f, -0.850651f, 0.525731f},	{0.000000f, -0.955423f, 0.295242f},	{0.238856f, -0.864188f, 0.442863f},	{0.262866f, -0.951056f, 0.162460f},	{0.500000f, -0.809017f, 0.309017f},	{0.716567f, -0.681718f, 0.147621f},	{0.525731f, -0.850651f, 0.000000f},	{-0.238856f, -0.864188f, -0.442863f},	{-0.500000f, -0.809017f, -0.309017f},	{-0.262866f, -0.951056f, -0.162460f},	{-0.850651f, -0.525731f, 0.000000f},	{-0.716567f, -0.681718f, -0.147621f},	{-0.716567f, -0.681718f, 0.147621f},	{-0.525731f, -0.850651f, 0.000000f},	{-0.500000f, -0.809017f, 0.309017f},	{-0.238856f, -0.864188f, 0.442863f},	{-0.262866f, -0.951056f, 0.162460f},	{-0.864188f, -0.442863f, 0.238856f},	{-0.809017f, -0.309017f, 0.500000f},	{-0.688191f, -0.587785f, 0.425325f},	{-0.681718f, -0.147621f, 0.716567f},	{-0.442863f, -0.238856f, 0.864188f},	{-0.587785f, -0.425325f, 0.688191f},	{-0.309017f, -0.500000f, 0.809017f},	{-0.147621f, -0.716567f, 0.681718f},	{-0.425325f, -0.688191f, 0.587785f},	{-0.162460f, -0.262866f, 0.951056f},	{0.442863f, -0.238856f, 0.864188f},	{0.162460f, -0.262866f, 0.951056f},	{0.309017f, -0.500000f, 0.809017f},	{0.147621f, -0.716567f, 0.681718f},	{0.000000f, -0.525731f, 0.850651f},	{0.425325f, -0.688191f, 0.587785f},	{0.587785f, -0.425325f, 0.688191f},	{0.688191f, -0.587785f, 0.425325f},	{-0.955423f, 0.295242f, 0.000000f},	{-0.951056f, 0.162460f, 0.262866f},	{-1.000000f, 0.000000f, 0.000000f},	{-0.850651f, 0.000000f, 0.525731f},	{-0.955423f, -0.295242f, 0.000000f},	{-0.951056f, -0.162460f, 0.262866f},	{-0.864188f, 0.442863f, -0.238856f},	{-0.951056f, 0.162460f, -0.262866f},	{-0.809017f, 0.309017f, -0.500000f},	{-0.864188f, -0.442863f, -0.238856f},	{-0.951056f, -0.162460f, -0.262866f},	{-0.809017f, -0.309017f, -0.500000f},	{-0.681718f, 0.147621f, -0.716567f},	{-0.681718f, -0.147621f, -0.716567f},	{-0.850651f, 0.000000f, -0.525731f},	{-0.688191f, 0.587785f, -0.425325f},	{-0.587785f, 0.425325f, -0.688191f},	{-0.425325f, 0.688191f, -0.587785f},	{-0.425325f, -0.688191f, -0.587785f},	{-0.587785f, -0.425325f, -0.688191f},	{-0.688191f, -0.587785f, -0.425325f},};


Hope that helps.
Quote:Original post by godsenddeath
ok, i wote an md2 loader, and it works fine, but i have no idea how to apply normals, and texture coords. does anyone have any hints or suggestions?
The MD2 format is designed for use with OpenGL, where you can send data in a different way to D3D. To render a MD2 model with D3D, you'll want to put the vertex position, normal, and texture coordinates into a single vertex struct. The MD2 file format specs have all the information you should need to get those variables.
well this is what i did, I load the vertex list into a vertex buffer(dynamic, that way, to animate I just re-fill the VB)and use teh triangle list as an index buffer. So i guess teh question would be, how would I apply the list of texture coords and normals to the model using this technique?


EDIT.
or is it not possible/practical to use an index buffer when you need to include conflicting normals and tex coords?

[Edited by - godsenddeath on July 22, 2008 4:12:04 PM]
--------------------------------------Not All Martyrs See Divinity, But At Least You Tried
Quote:conflicting normals and tex coords

If you setup a vertex structure as Evil Steve and I suggested, you won't have any "conflicts."

Each vertex (don't confuse that with vertex position) will have stored in it: a position for the vertex, the normal for the vertex and the tex coords for the vertex.

Three such vertices will form a face (a triangle). Each vertex in the triangle will have a different position, different tex coords and possibly a different normal.

If you have 2 faces which share vertices and you want the vertices in one face to have different normals or texture coordinates than the vertices in the adjacent face, you'll have to create separate vertices for each face.

Please don't PM me with questions. Post them in the forums for everyone's benefit, and I can embarrass myself publicly.

You don't forget how to play when you grow old; you grow old when you forget how to play.

ok, thanks, one more question, the number of texture coords(590) is different from the number of triangle indices(720) and vertices(395), do I just apply them til I run out of text coords?
--------------------------------------Not All Martyrs See Divinity, But At Least You Tried
I think each triangle has 3 vertex indices and 3 tex coord indices, so it's possible that you will have the "conflict" mentioned above where one face will index 3 vertices with one set of tex coords and another face will index 1 or more of the same vertices but with a different set of tex coords.

One option is to detect that condition and create additional vertices.

Unlike OpenGL, DirectX doesn't have a specific function to set separate tex coords for each vertex. You could possibly do it with a vertex shader but I'm not an expert on shaders.

The easiest process would be to create a vertex structure as described above and load each vertex with the MD2 indexed vertex position, normal and texture coords, rather than trying to sort out which vertices are used in different faces with different normals or tex coords.

You'll probably end up with more vertices than in the MD2 file but you won't have to try to resolve the "conflicts."

Please don't PM me with questions. Post them in the forums for everyone's benefit, and I can embarrass myself publicly.

You don't forget how to play when you grow old; you grow old when you forget how to play.

Quote:Original post by godsenddeath
ok, thanks, one more question, the number of texture coords(590) is different from the number of triangle indices(720) and vertices(395), do I just apply them til I run out of text coords?
What Buckeye said - some vertices share texture coordinates.

Quote:Original post by Buckeye
Unlike OpenGL, DirectX doesn't have a specific function to set separate tex coords for each vertex. You could possibly do it with a vertex shader but I'm not an expert on shaders.
I don't think this is possible, since the vertex shader acts on each vertex in turn; it doesn't have random access to anything. You could use the vertex texture thingy, and use a texture as a lookup, but I can't see that being very efficient, easy to do, or working on older hardware.

Quote:Original post by Buckeye
The easiest process would be to create a vertex structure as described above and load each vertex with the MD2 indexed vertex position, normal and texture coords, rather than trying to sort out which vertices are used in different faces with different normals or tex coords.

You'll probably end up with more vertices than in the MD2 file but you won't have to try to resolve the "conflicts."
I agree, this is exactly what I do.

In my engine, I have a converter program that I use to convert various model formats (Currently only MD2 and MD5) into my own engine's formats. THis may be useful to you (A lot of it can be ignored though):

Structs.h:
//============================================================================// Structs.h - Common structs//============================================================================#ifndef __STRUCTS_H__#define __STRUCTS_H__#include <string>#include <vector>//============================================================================// Basic types defined in engine code//============================================================================typedef unsigned int u32;typedef unsigned short u16;typedef unsigned char u8;struct EVector3{	EVector3() {}	EVector3(float _x, float _y, float _z) : x(_x), y(_y), z(_z) {}	EVector3& operator+=(const EVector3& rhs) {x+=rhs.x; y+=rhs.y; z+=rhs.z; return *this;}	EVector3& operator*=(const EVector3& rhs) {x*=rhs.x; y*=rhs.y; z*=rhs.z; return *this;}	float x, y, z;};inline EVector3 operator+(const EVector3& a, const EVector3& b)	{ return EVector3(a.x+b.x, a.y+b.y, a.z+b.z); }inline EVector3 operator/(const EVector3& a, float f)	{ return EVector3(a.x/f, a.y/f, a.z/f); }//============================================================================// Command line parameters//============================================================================struct CommandLineParams{	CommandLineParams(const std::wstring& skin) :		xTexScale(1.0f),		yTexScale(1.0f),		strSkinTexture(skin)	{	}	float xTexScale;				// X-scale for texture coordinates	float yTexScale;				// Y-scale for texture coordinates	std::wstring strSkinTexture;	// Skin texture	std::vector<Buffer> vAnims;		// List of animations to include};//============================================================================// Actual model structs//============================================================================struct ModelHeader{	static const u32 s_nHeaderMagic = 'DOMD';	static const size_t s_cnMaxStringLen = 64;	static const size_t s_cnVersionKeyframe = 0x00000000;	static const size_t s_cnVersionSkinned = 0x80000000;	u32 nMagic;			// 'DMOD'	u32 nVersion;	u32 nNumFrames;	u32 nNumVertices;	u32 nNumJoints;	u32 nNumIndices;	wchar_t szTexture[s_cnMaxStringLen];	wchar_t szShader[s_cnMaxStringLen];};struct ModelVertex{	EVector3 vPos;	EVector3 vNormal;	float tu;	float tv;};struct ModelFrameKeyframe{	char szName[64];			// Null terminated	EVector3 vBBoxMin;	EVector3 vBBoxMax;	ModelVertex pVertices[1];	// Array of ModelHeader::nNumVertices entries};//============================================================================// Inline helper functions//============================================================================inline u32 RoundUpToPowerOfTwo(u32 n){	n--;	n |= n >> 1;	n |= n >> 2;	n |= n >> 4;	n |= n >> 8;	n |= n >> 16;	n++;	return n;}inline bool IsPowerOfTwo(u32 n){	return (n & ((~n)+1)) == n;}#endif // __STRUCTS_H__


And the important bit; MD2Convert.cpp:
//============================================================================// MD2Convert.cpp - Conversion routine for Quake 2 MD2 models// Conversion isn't optimal, as it doesn't share vertices. However, since MD2// models have vertex coordinates seperate from texture coordinates, this// could be a pain to do. And I'm lazy.//============================================================================#include "Structs.h"#include <stdio.h>#include <string>static const wchar_t* s_szShaderName = L"KeyframeModel";//==========================================================================// MD2 file header struct#pragma pack(push, 1)struct MD2FileHeader{	u32 ident;			// magic number: "IDP2"	u32 version;		// version: must be 8	u32 skinwidth;		// texture width	u32 skinheight;		// texture height	u32 framesize;		// size in bytes of a frame	u32 num_skins;		// number of skins	u32 num_vertices;	// number of vertices per frame	u32 num_st;			// number of texture coordinates	u32 num_tris;		// number of triangles	u32 num_glcmds;		// number of opengl commands	u32 num_frames;		// number of frames	u32 offset_skins;	// offset skin data	u32 offset_st;		// offset texture coordinate data	u32 offset_tris;	// offset triangle data	u32 offset_frames;	// offset frame data	u32 offset_glcmds;	// offset OpenGL command data	u32 offset_end;		// offset end of file};// MD2 skin structstruct MD2Skin{	char name[64];		// texture file name};// MD2 texture coordsstruct MD2TexCoord{	short s, t;};// MD2 trianglestruct MD2Triangle{	u16 vertex[3];	// vertex indices of the triangle	u16 st[3];		// tex. coord. indices};// MD2 vertexstruct MD2Vertex{	u8 v[3];		// position	u8 normalIndex;	// normal vector index};// MD2 framestruct MD2Frame{	EVector3	scale;			// scale factor	EVector3	translate;		// translation vector	char		name[16];		// frame name	MD2Vertex	verts[1];		// list of frame's vertices};#pragma pack(pop)// Normals tablestatic const EVector3 s_vNormals[] = {	EVector3(-0.525731f,  0.000000f,  0.850651f), EVector3(-0.442863f,  0.238856f,  0.864188f),	EVector3(-0.295242f,  0.000000f,  0.955423f), EVector3(-0.309017f,  0.500000f,  0.809017f),	EVector3(-0.162460f,  0.262866f,  0.951056f), EVector3( 0.000000f,  0.000000f,  1.000000f),	EVector3( 0.000000f,  0.850651f,  0.525731f), EVector3(-0.147621f,  0.716567f,  0.681718f),	EVector3( 0.147621f,  0.716567f,  0.681718f), EVector3( 0.000000f,  0.525731f,  0.850651f),	EVector3( 0.309017f,  0.500000f,  0.809017f), EVector3( 0.525731f,  0.000000f,  0.850651f),	EVector3( 0.295242f,  0.000000f,  0.955423f), EVector3( 0.442863f,  0.238856f,  0.864188f),	EVector3( 0.162460f,  0.262866f,  0.951056f), EVector3(-0.681718f,  0.147621f,  0.716567f),	EVector3(-0.809017f,  0.309017f,  0.500000f), EVector3(-0.587785f,  0.425325f,  0.688191f),	EVector3(-0.850651f,  0.525731f,  0.000000f), EVector3(-0.864188f,  0.442863f,  0.238856f),	EVector3(-0.716567f,  0.681718f,  0.147621f), EVector3(-0.688191f,  0.587785f,  0.425325f),	EVector3(-0.500000f,  0.809017f,  0.309017f), EVector3(-0.238856f,  0.864188f,  0.442863f),	EVector3(-0.425325f,  0.688191f,  0.587785f), EVector3(-0.716567f,  0.681718f, -0.147621f),	EVector3(-0.500000f,  0.809017f, -0.309017f), EVector3(-0.525731f,  0.850651f,  0.000000f),	EVector3( 0.000000f,  0.850651f, -0.525731f), EVector3(-0.238856f,  0.864188f, -0.442863f),	EVector3( 0.000000f,  0.955423f, -0.295242f), EVector3(-0.262866f,  0.951056f, -0.162460f),	EVector3( 0.000000f,  1.000000f,  0.000000f), EVector3( 0.000000f,  0.955423f,  0.295242f),	EVector3(-0.262866f,  0.951056f,  0.162460f), EVector3( 0.238856f,  0.864188f,  0.442863f),	EVector3( 0.262866f,  0.951056f,  0.162460f), EVector3( 0.500000f,  0.809017f,  0.309017f),	EVector3( 0.238856f,  0.864188f, -0.442863f), EVector3( 0.262866f,  0.951056f, -0.162460f),	EVector3( 0.500000f,  0.809017f, -0.309017f), EVector3( 0.850651f,  0.525731f,  0.000000f),	EVector3( 0.716567f,  0.681718f,  0.147621f), EVector3( 0.716567f,  0.681718f, -0.147621f),	EVector3( 0.525731f,  0.850651f,  0.000000f), EVector3( 0.425325f,  0.688191f,  0.587785f),	EVector3( 0.864188f,  0.442863f,  0.238856f), EVector3( 0.688191f,  0.587785f,  0.425325f),	EVector3( 0.809017f,  0.309017f,  0.500000f), EVector3( 0.681718f,  0.147621f,  0.716567f),	EVector3( 0.587785f,  0.425325f,  0.688191f), EVector3( 0.955423f,  0.295242f,  0.000000f),	EVector3( 1.000000f,  0.000000f,  0.000000f), EVector3( 0.951056f,  0.162460f,  0.262866f),	EVector3( 0.850651f, -0.525731f,  0.000000f), EVector3( 0.955423f, -0.295242f,  0.000000f),	EVector3( 0.864188f, -0.442863f,  0.238856f), EVector3( 0.951056f, -0.162460f,  0.262866f),	EVector3( 0.809017f, -0.309017f,  0.500000f), EVector3( 0.681718f, -0.147621f,  0.716567f),	EVector3( 0.850651f,  0.000000f,  0.525731f), EVector3( 0.864188f,  0.442863f, -0.238856f),	EVector3( 0.809017f,  0.309017f, -0.500000f), EVector3( 0.951056f,  0.162460f, -0.262866f),	EVector3( 0.525731f,  0.000000f, -0.850651f), EVector3( 0.681718f,  0.147621f, -0.716567f),	EVector3( 0.681718f, -0.147621f, -0.716567f), EVector3( 0.850651f,  0.000000f, -0.525731f),	EVector3( 0.809017f, -0.309017f, -0.500000f), EVector3( 0.864188f, -0.442863f, -0.238856f),	EVector3( 0.951056f, -0.162460f, -0.262866f), EVector3( 0.147621f,  0.716567f, -0.681718f),	EVector3( 0.309017f,  0.500000f, -0.809017f), EVector3( 0.425325f,  0.688191f, -0.587785f),	EVector3( 0.442863f,  0.238856f, -0.864188f), EVector3( 0.587785f,  0.425325f, -0.688191f),	EVector3( 0.688191f,  0.587785f, -0.425325f), EVector3(-0.147621f,  0.716567f, -0.681718f),	EVector3(-0.309017f,  0.500000f, -0.809017f), EVector3( 0.000000f,  0.525731f, -0.850651f),	EVector3(-0.525731f,  0.000000f, -0.850651f), EVector3(-0.442863f,  0.238856f, -0.864188f),	EVector3(-0.295242f,  0.000000f, -0.955423f), EVector3(-0.162460f,  0.262866f, -0.951056f),	EVector3( 0.000000f,  0.000000f, -1.000000f), EVector3( 0.295242f,  0.000000f, -0.955423f),	EVector3( 0.162460f,  0.262866f, -0.951056f), EVector3(-0.442863f, -0.238856f, -0.864188f),	EVector3(-0.309017f, -0.500000f, -0.809017f), EVector3(-0.162460f, -0.262866f, -0.951056f),	EVector3( 0.000000f, -0.850651f, -0.525731f), EVector3(-0.147621f, -0.716567f, -0.681718f),	EVector3( 0.147621f, -0.716567f, -0.681718f), EVector3( 0.000000f, -0.525731f, -0.850651f),	EVector3( 0.309017f, -0.500000f, -0.809017f), EVector3( 0.442863f, -0.238856f, -0.864188f),	EVector3( 0.162460f, -0.262866f, -0.951056f), EVector3( 0.238856f, -0.864188f, -0.442863f),	EVector3( 0.500000f, -0.809017f, -0.309017f), EVector3( 0.425325f, -0.688191f, -0.587785f),	EVector3( 0.716567f, -0.681718f, -0.147621f), EVector3( 0.688191f, -0.587785f, -0.425325f),	EVector3( 0.587785f, -0.425325f, -0.688191f), EVector3( 0.000000f, -0.955423f, -0.295242f),	EVector3( 0.000000f, -1.000000f,  0.000000f), EVector3( 0.262866f, -0.951056f, -0.162460f),	EVector3( 0.000000f, -0.850651f,  0.525731f), EVector3( 0.000000f, -0.955423f,  0.295242f),	EVector3( 0.238856f, -0.864188f,  0.442863f), EVector3( 0.262866f, -0.951056f,  0.162460f),	EVector3( 0.500000f, -0.809017f,  0.309017f), EVector3( 0.716567f, -0.681718f,  0.147621f),	EVector3( 0.525731f, -0.850651f,  0.000000f), EVector3(-0.238856f, -0.864188f, -0.442863f),	EVector3(-0.500000f, -0.809017f, -0.309017f), EVector3(-0.262866f, -0.951056f, -0.162460f),	EVector3(-0.850651f, -0.525731f,  0.000000f), EVector3(-0.716567f, -0.681718f, -0.147621f),	EVector3(-0.716567f, -0.681718f,  0.147621f), EVector3(-0.525731f, -0.850651f,  0.000000f),	EVector3(-0.500000f, -0.809017f,  0.309017f), EVector3(-0.238856f, -0.864188f,  0.442863f),	EVector3(-0.262866f, -0.951056f,  0.162460f), EVector3(-0.864188f, -0.442863f,  0.238856f),	EVector3(-0.809017f, -0.309017f,  0.500000f), EVector3(-0.688191f, -0.587785f,  0.425325f),	EVector3(-0.681718f, -0.147621f,  0.716567f), EVector3(-0.442863f, -0.238856f,  0.864188f),	EVector3(-0.587785f, -0.425325f,  0.688191f), EVector3(-0.309017f, -0.500000f,  0.809017f),	EVector3(-0.147621f, -0.716567f,  0.681718f), EVector3(-0.425325f, -0.688191f,  0.587785f),	EVector3(-0.162460f, -0.262866f,  0.951056f), EVector3( 0.442863f, -0.238856f,  0.864188f),	EVector3( 0.162460f, -0.262866f,  0.951056f), EVector3( 0.309017f, -0.500000f,  0.809017f),	EVector3( 0.147621f, -0.716567f,  0.681718f), EVector3( 0.000000f, -0.525731f,  0.850651f),	EVector3( 0.425325f, -0.688191f,  0.587785f), EVector3( 0.587785f, -0.425325f,  0.688191f),	EVector3( 0.688191f, -0.587785f,  0.425325f), EVector3(-0.955423f,  0.295242f,  0.000000f),	EVector3(-0.951056f,  0.162460f,  0.262866f), EVector3(-1.000000f,  0.000000f,  0.000000f),	EVector3(-0.850651f,  0.000000f,  0.525731f), EVector3(-0.955423f, -0.295242f,  0.000000f),	EVector3(-0.951056f, -0.162460f,  0.262866f), EVector3(-0.864188f,  0.442863f, -0.238856f),	EVector3(-0.951056f,  0.162460f, -0.262866f), EVector3(-0.809017f,  0.309017f, -0.500000f),	EVector3(-0.864188f, -0.442863f, -0.238856f), EVector3(-0.951056f, -0.162460f, -0.262866f),	EVector3(-0.809017f, -0.309017f, -0.500000f), EVector3(-0.681718f,  0.147621f, -0.716567f),	EVector3(-0.681718f, -0.147621f, -0.716567f), EVector3(-0.850651f,  0.000000f, -0.525731f),	EVector3(-0.688191f,  0.587785f, -0.425325f), EVector3(-0.587785f,  0.425325f, -0.688191f),	EVector3(-0.425325f,  0.688191f, -0.587785f), EVector3(-0.425325f, -0.688191f, -0.587785f),	EVector3(-0.587785f, -0.425325f, -0.688191f), EVector3(-0.688191f, -0.587785f, -0.425325f),	// Dummy values from here down	EVector3(-0.587785f, -0.425325f, -0.688191f), EVector3(-0.688191f, -0.587785f, -0.425325f)};static const u32 s_nNumNormals = sizeof(s_vNormals) / sizeof(s_vNormals[0]);//============================================================================bool ConvertMD2(const char* szFilename, const u8* pBuffer, size_t nLen, u8** ppOut, size_t* pnOutLen, const CommandLineParams& params){	// Validate length, ID and version	if(nLen < sizeof(MD2FileHeader))	{		printf("File '%s' is too short to be a MD2 file\n", szFilename);		return false;	}	const MD2FileHeader* pHeader = (const MD2FileHeader*)pBuffer;	if(pHeader->ident != '2PDI')	{		printf("'%s': Invalid header (0x%08x, expected 0x%08x)\n", szFilename, pHeader->ident, '2PDI');		return false;	}	if(pHeader->version != 8)	{		printf("'%s': Invalid version (%d, expected 8)\n", szFilename, pHeader->version);		return false;	}	// Validate various parts	if((size_t)pHeader->offset_skins + sizeof(MD2Skin)*pHeader->num_skins > nLen)	{		printf("'%s': Invalid skin offset/size\n", szFilename);		return false;	}	if((size_t)pHeader->offset_st + sizeof(MD2TexCoord)*pHeader->num_st > nLen)	{		printf("'%s': Invalid tex coord offset/size\n", szFilename);		return false;	}	if((size_t)pHeader->offset_tris + sizeof(MD2Triangle)*pHeader->num_tris > nLen)	{		printf("'%s': Invalid triangle offset/size\n", szFilename);		return false;	}	if((size_t)pHeader->framesize != sizeof(MD2Frame) + sizeof(MD2Vertex)*pHeader->num_vertices - sizeof(MD2Vertex*))	{		printf("'%s': Mismatched frame size (%d, expected %d)\n", szFilename, pHeader->framesize,			sizeof(MD2Frame) + sizeof(MD2Vertex)*pHeader->num_vertices - sizeof(MD2Vertex*));		return false;	}	if(pHeader->num_frames == 0)	{		printf("'%s': Mesh does not contain any frames!\n", szFilename);		return false;	}	if((size_t)pHeader->offset_frames + pHeader->framesize*pHeader->num_frames > nLen)	{		printf("'%s': Invalid frame offset/size\n", szFilename);		return false;	}	if(pHeader->num_vertices > 0xffff)	{		printf("'%s': Invalid vertex count (%d, max is 65535)\n", szFilename, pHeader->num_vertices);		return false;	}	if(pHeader->num_st > 0xffff)	{		printf("'%s': Invalid texture coordinate count (%d, max is 65535)\n", szFilename, pHeader->num_st);		return false;	}	if(pHeader->num_frames > 0xffff)	{		printf("'%s': Invalid frame count (%d, max is 65535)\n", szFilename, pHeader->num_frames);		return false;	}	if(pHeader->num_tris > 0xffff)	{		printf("'%s': Invalid triangle count (%d, max is 65535)\n", szFilename, pHeader->num_tris);		return false;	}	// See if skin needs resized to power of 2	float tuScale = 1.0f;	float tvScale = 1.0f;	u32 nSkinW = pHeader->skinwidth;	u32 nSkinH = pHeader->skinheight;	if(!IsPowerOfTwo(nSkinW))	{		nSkinW = RoundUpToPowerOfTwo(pHeader->skinwidth);		tuScale = (float)pHeader->skinwidth / (float)nSkinW;	}	if(!IsPowerOfTwo(nSkinH))	{		nSkinH = RoundUpToPowerOfTwo(pHeader->skinheight);		tvScale = (float)pHeader->skinheight / (float)nSkinH;	}	tuScale *= params.xTexScale;	tvScale *= params.yTexScale;	// Allocate data	size_t nFileSize = sizeof(ModelHeader);	nFileSize += pHeader->num_frames * (sizeof(ModelFrameKeyframe)-sizeof(ModelVertex));	nFileSize += pHeader->num_frames * pHeader->num_tris*3 * sizeof(ModelVertex);	nFileSize += pHeader->num_tris * 3 * 2;	*ppOut = new u8[nFileSize];	if(!*ppOut)	{		printf("'%s': Out of memory\n", szFilename);		return false;	}	*pnOutLen = nFileSize;	// Grab some pointers	ModelHeader* pOutHeader = (ModelHeader*)(*ppOut);	ModelFrameKeyframe* pOutFrames = (ModelFrameKeyframe*)(pOutHeader+1);	u16* pIndices = (u16*)((*ppOut) + nFileSize - pHeader->num_tris * 3 * 2);	size_t nFrameStride = sizeof(ModelFrameKeyframe)-sizeof(ModelVertex) + sizeof(ModelVertex)*pHeader->num_tris*3;	// Populate frames	for(u32 nFrame=0; nFrame<pHeader->num_frames; ++nFrame)	{		EVector3 vMin(10000.0f, 10000.0f, 10000.0f);		EVector3 vMax(-10000.0f, -10000.0f, -10000.0f);		// Get source and dest frame		const MD2Frame* pFrame = (const MD2Frame*)(pBuffer+pHeader->offset_frames + nFrame*pHeader->framesize);		ModelFrameKeyframe* pOutFrame = (ModelFrameKeyframe*)((u8*)pOutFrames + nFrameStride*nFrame);		// Do a robust name copy		memcpy(pOutFrame->szName, pFrame->name, 16);		pOutFrame->szName[16] = '\0';		// Fill in vertices		ModelVertex* pOutVert = pOutFrame->pVertices;		MD2Triangle* pTriangle = (MD2Triangle*)(pBuffer + pHeader->offset_tris);		MD2TexCoord* pTexCoords = (MD2TexCoord*)(pBuffer + pHeader->offset_st);		for(u32 nTri=0; nTri<pHeader->num_tris; ++nTri)		{			for(int i=0; i<3; ++i)			{				// Check indices are valid				if(pTriangle->vertex >= pHeader->num_vertices)				{					delete[] *ppOut;					*ppOut = 0;					printf("'%s': Invalid vertex index (%d) for triangle %d. Only %d vertices in model\n",						szFilename, pTriangle->vertex, nTri, pHeader->num_vertices);					return false;				}				if(pTriangle->st >= pHeader->num_st)				{					delete[] *ppOut;					*ppOut = 0;					printf("'%s': Invalid tex coord index (%d) for triangle %d. Only %d tex coords in model\n",						szFilename, pTriangle->st, nTri, pHeader->num_st);					return false;				}				const MD2Vertex* pInVert = &pFrame->verts[0] + pTriangle->vertex;				if((size_t)pInVert->normalIndex >= s_nNumNormals)				{					delete[] *ppOut;					*ppOut = 0;					printf("'%s': Invalid normal index (%d) for frame %d, vertex %d (Only %d available)\n",						szFilename, (int)pInVert->normalIndex, nFrame, pTriangle->vertex, s_nNumNormals);					return false;				}				MD2TexCoord* pInTexCoord = pTexCoords + pTriangle->st;				// Setup vertex				pOutVert->vPos.x = pFrame->scale.x * (float)pInVert->v[0] + pFrame->translate.x;				pOutVert->vPos.z = pFrame->scale.y * (float)pInVert->v[1] + pFrame->translate.y;				pOutVert->vPos.y = pFrame->scale.z * (float)pInVert->v[2] + pFrame->translate.z;				pOutVert->vNormal = s_vNormals[pInVert->normalIndex];				pOutVert->tu = ((float)pInTexCoord->s / (float)pHeader->skinwidth) * tuScale;				pOutVert->tv = ((float)pInTexCoord->t / (float)pHeader->skinheight) * tvScale;				// Update AABB				if(pOutVert->vPos.x > vMax.x) vMax.x = pOutVert->vPos.x;				else if(pOutVert->vPos.x < vMin.x) vMin.x = pOutVert->vPos.x;				if(pOutVert->vPos.y > vMax.y) vMax.y = pOutVert->vPos.y;				else if(pOutVert->vPos.y < vMin.y) vMin.y = pOutVert->vPos.y;				if(pOutVert->vPos.z > vMax.z) vMax.z = pOutVert->vPos.z;				else if(pOutVert->vPos.z < vMin.z) vMin.z = pOutVert->vPos.z;				// Move onto the next vertex				pInVert++;				pOutVert++;			}			++pTriangle;		}		// Fill in bounding box		pOutFrame->vBBoxMin = vMin;		pOutFrame->vBBoxMax = vMax;	}	// Fill in indices	u16 wIndex = 0;	for(size_t i=0; i<pHeader->num_tris; ++i)	{		*pIndices++ = wIndex++;		*pIndices++ = wIndex++;		*pIndices++ = wIndex++;	}	// Log if skin needs resized	if(nSkinW != pHeader->skinwidth || nSkinH != pHeader->skinheight)	{		printf("WARNING: Skin needs resized to %dx%d, texture coords were adjusted\n", nSkinW, nSkinH);	}	// Fill in output header	pOutHeader->nMagic = ModelHeader::s_nHeaderMagic;	pOutHeader->nVersion = ModelHeader::s_cnVersionKeyframe;	pOutHeader->nNumFrames = pHeader->num_frames;	pOutHeader->nNumVertices = pHeader->num_tris*3;	pOutHeader->nNumJoints = 0;	pOutHeader->nNumIndices = pHeader->num_tris*3;	wcsncpy(pOutHeader->szTexture, params.strSkinTexture.c_str(), sizeof(pOutHeader->szTexture));	wcscpy(pOutHeader->szShader, s_szShaderName);	return true;}//============================================================================

The reason there's some dummy normals in that array is that some test models had normals that were outside the range of that array. You can probably ignore them.

What that code does, is parse the MD2 file and spits out a series of frames, where each frame has D3D-style vertices (And it swaps the winding order to make sure that the triangles are in clockwise order).
Since MD2 skin files aren't always powers of 2, it also adjusts the texture coordinates so that the skin can be loaded as a power of 2 texture with the right and bottom edges unused (Which is the default for D3DXCreateTextureFromFileEx and the like).

That code isn't designed to drop into your own app, more as a reference (Although feel free to copy whatever you like).
thanks alot guys, i'm gonna read over the code sample, and try to pick it apart, along with the other ideas you gave me,


thanks again
--------------------------------------Not All Martyrs See Divinity, But At Least You Tried

This topic is closed to new replies.

Advertisement