• Advertisement

Archived

This topic is now archived and is closed to further replies.

.MD2 model rendering problems

This topic is 5294 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi, The past couple of days of have been playing around with a md2 model loading/rendering class in c++ and vb and I am getting some strange rendering problems in both cases. It seems to be leaving out some polygons. When I change the render state to points only, it appears to be rendering the points correctly and their position seems to be right and there seem to be the right amount of vertices, so I'm guessing its something to do with the order im rendering them in. I have basically followed this tutorial ( here) but I haven't used any of the texture coordinates code yet as I just wanted to get the polygons rendering right first. I hate to post a heap of code but I think the problem might be in the following.
  for(framenum = 0;framenum < m_HeaderInfo.m_numframes;framenum++)
    {
        
        for(verticenum = 0;verticenum < m_HeaderInfo.m_numvertices; verticenum++)
        {
            m_ConvertedFrames[framenum].m_vertices[verticenum].m_Position.x = (m_Frames[framenum].m_vertices[verticenum].m_vertex[0] * 
                m_Frames[framenum].m_scale[0]) + m_Frames[framenum].m_translate[0];
                
                sprintf(tmpstring,"m_ConvertedFrames[framenum].m_vertices[verticenum].m_Position.x = %f", m_ConvertedFrames[framenum].m_vertices[verticenum].m_Position.x);
               

            m_ConvertedFrames[framenum].m_vertices[verticenum].m_Position.y = (m_Frames[framenum].m_vertices[verticenum].m_vertex[1] * 
                m_Frames[framenum].m_scale[1]) + m_Frames[framenum].m_translate[1];

            m_ConvertedFrames[framenum].m_vertices[verticenum].m_Position.z = (m_Frames[framenum].m_vertices[verticenum].m_vertex[2] * 
                m_Frames[framenum].m_scale[2]) + m_Frames[framenum].m_translate[2];

        }
    }    

    int i = 0; //Vertice counter

    for(framenum = 0;framenum< m_HeaderInfo.m_numframes;framenum++)
    {
        i = 0;
        for(trianglenum = 0;trianglenum < m_HeaderInfo.m_numtriangles; trianglenum++)
        {            
            m_RenderVertices[framenum].m_vertices[i].m_Position.x = m_ConvertedFrames[framenum].m_vertices[m_index_list[trianglenum].m_a].m_Position.x;
            m_RenderVertices[framenum].m_vertices[i].m_Position.y = m_ConvertedFrames[framenum].m_vertices[m_index_list[trianglenum].m_a].m_Position.z;
            m_RenderVertices[framenum].m_vertices[i].m_Position.z = m_ConvertedFrames[framenum].m_vertices[m_index_list[trianglenum].m_a].m_Position.y;
            m_RenderVertices[framenum].m_vertices[i].m_DiffuseColor = D3DCOLOR_XRGB(255,255,255);
            sprintf(tmpstring,"m_RenderVertices[framenum].m_vertices[i].m_Position.x = %f", m_RenderVertices[framenum].m_vertices[i].m_Position.x);
            
            i++;
           
            m_RenderVertices[framenum].m_vertices[i].m_Position.x = m_ConvertedFrames[framenum].m_vertices[m_index_list[trianglenum].m_b].m_Position.x;
            m_RenderVertices[framenum].m_vertices[i].m_Position.y = m_ConvertedFrames[framenum].m_vertices[m_index_list[trianglenum].m_b].m_Position.z;
            m_RenderVertices[framenum].m_vertices[i].m_Position.z = m_ConvertedFrames[framenum].m_vertices[m_index_list[trianglenum].m_b].m_Position.y;
            m_RenderVertices[framenum].m_vertices[i].m_DiffuseColor = D3DCOLOR_XRGB(255,255,255);
            i++;
            
            m_RenderVertices[framenum].m_vertices[i].m_Position.x = m_ConvertedFrames[framenum].m_vertices[m_index_list[trianglenum].m_c].m_Position.x;
            m_RenderVertices[framenum].m_vertices[i].m_Position.y = m_ConvertedFrames[framenum].m_vertices[m_index_list[trianglenum].m_c].m_Position.z;
            m_RenderVertices[framenum].m_vertices[i].m_Position.z = m_ConvertedFrames[framenum].m_vertices[m_index_list[trianglenum].m_c].m_Position.y;
            m_RenderVertices[framenum].m_vertices[i].m_DiffuseColor = D3DCOLOR_XRGB(255,255,255);
        }    
    }   

Like I said all of the vertice data seems ok and all the header info is loading correctly so it is possibly something to do with the order the polygons are being rendered. And if you are interested the actual rendering code:
void CMD2::Render(long frame)
{   
    g_pD3DDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE);
    g_pD3DDevice->SetRenderState(D3DRS_FILLMODE,D3DFILL_WIREFRAME);
    g_pD3DDevice->SetVertexShader(D3DFVF_CUSTOMVERTEX);
    g_pD3DDevice->DrawPrimitiveUP(D3DPT_TRIANGLELIST, m_HeaderInfo.m_numtriangles, &m_RenderVertices[frame].m_vertices[0], sizeof(CVertex));
  
}
m_ConvertedFrames is the vertice data after the scaling/translating from the compressed format has been made. m_RenderedVertices is the final vertex data to be rendered. Slightly dodgy variable names but its fairly unplanned I have searched these forums and it doesn't seem like anyone else has had this problem as far as I could see. I have looked at other MD2 loading code and the only thing that is different from what I can see is that some of the other code uses the glcommands which i'm not. Any help is greatly appreciated. If you require any more info please let me know. Thanks, Harley. BreakoutX [edited by - klown9 on August 16, 2003 8:19:46 AM] [edited by - klown9 on August 16, 2003 8:25:45 AM] [edited by - klown9 on August 16, 2003 8:34:28 AM]

Share this post


Link to post
Share on other sites
Advertisement
I have downloaded some time ago the MD2 file format. I code in Visualbasic, so I can''t help you with source code, but i also had some problems rendering Md2 animation.

I have created an array of vertex, based on the information inside the md2 file, then render this.

Also, you might try to set up a blank texture for your geometry,
especially if you don''t handle texturecoordinates.
I also had this problem, because my mesh was black, and I did not see the model, although it was there.

Oh, and try to clean up your code, look for either a small mistake, or rewrite the code entirely. (i had to rewrite myself)

ANyiway, for further info, try to be more specific.

Share this post


Link to post
Share on other sites
I am currently having this problem (Im using VC++ and DX8.1).
I used the turorial from gametutorials.com, i used the MD2 loading and animation tutorial.

Since all of the tutorials there are in OpenGL, i convertd it to direct3d. no matter what i tried i get the same problem as you - a 3d md2 model with incorrect polygon drawing.
i tried disabling culling, CW, CCW, triangle lists, strips, fans, and went back to the code to figure out what could be wrong.

Sadly, I cant see anything wrong.
Im afraid that I will have to look into the format of the MD2 file to narrow down the problem''s source.

Share this post


Link to post
Share on other sites
Dark_Guy,
I dont suppose you have some code for vb do you? I am also coding this in VB with exactly the same rendering problems (Im making a simple viewer in VB just for learning purposes and making a class in c++ for the game im currently working on)

Sorry I cant really be more specific other than it looks like its drawing them in the wrong order. If i change renderstate to solid polygons are missing, but also it looks like some polygon's vertices are joined to others it shouldn't (in either renderstate). If you would like a screen shot just let me know and I will put one on my site (unless it can be done in the forums?)

GekkoCube - hehe at least someone else IS having the same troubles as me. I have looked at about 3 different tuts on this and cant figure out what im doing wrong.


[edited by - klown9 on August 16, 2003 8:10:29 PM]

Share this post


Link to post
Share on other sites
Dark_Guy,
Just curious but what would be the purpose of setting a blank texture for geometry when im not using textures. In fact i would be happy just to get the wireframe rendering properly! Do the vertices need it?
This wasn''t intended as a flame by the way.

BTW I just redid the code based on another md2 loader I downloaded from somewhere (cant remember, just found it on my hdd) and STILL get the same results. Its driving me nuts.

Thanks
Harley


BreakoutX

Share this post


Link to post
Share on other sites
Klown9, why dont u use indexed primitives?

Your first code will load the vertexbuffers of each frame.

next copy the indices into a index buffer, heres my code
   
for(j=0; j < currentFrame->numFaces; j++)
{

currentFrame->pFaces[j].vertIndex[0] = m_pTriangles[j].vertexIndices[0];
currentFrame->pFaces[j].vertIndex[1] = m_pTriangles[j].vertexIndices[1];
currentFrame->pFaces[j].vertIndex[2] = m_pTriangles[j].vertexIndices[2]; // vertex indices


currentFrame->pTexFaces[j].coordIndex[0] = m_pTriangles[j].textureIndices[0];
currentFrame->pTexFaces[j].coordIndex[1] = m_pTriangles[j].textureIndices[1];
currentFrame->pTexFaces[j].coordIndex[2] = m_pTriangles[j].textureIndices[2]; // tex coord indices

}


To render this u have to render each face individually, unless u change the tex coord indices so they represent the tex coords of the vertices in the index buffer.
Or in D3D u can assign the texcoords to the vertices (in the MD2 the texcoords are stored only for frame 0 so you'll have to copy them for the other frames). If u do this then animation will be just vertex blending between 2 VBs





Death to the gaming industry! Long live games. /*ilici*/

[edited by - Ilici on August 17, 2003 5:33:38 AM]

Share this post


Link to post
Share on other sites
Ilici,
Thanks for the suggestion but im not too sure it will fix my problem however (i could be wrong, im certainly no expert here)
Im just using the DrawPrimitiveUP function for simplicity and then once thats working i will look at optimisations. I have tried looping through each triangle in the frame and calling DrawPrimitiveUP for each triangle (slooooooow hehe) but that didn't make a difference.

I know im asking a lot here, but is someone able to maybe explain the steps involved in loading the models? I think maybe im getting confused with the triangle index lists. Or maybe im missing a step or something. I'm sure that my code matches the others ive looked at but I must be missing something. Bah I feel so frustated and stupid. 3 tuts and I still cant get it!!!
Thanks for listening to me whinge,
Harley.

[edited by - klown9 on August 17, 2003 6:20:57 AM]

Share this post


Link to post
Share on other sites
IIRC, md2 stores the triangles as indices into the vertex-list, so if you just use indexed primitives, you should be alright.
Until you get to texturing, once there you''ll have to duplicate vertices that has more than one set of texture coordinates, but that''s later.

Share this post


Link to post
Share on other sites

ok, i''ll post the entire MD2 class so u can get an ideea about the loading (i actually modified the MD2 loading tutorial on gametutorials)

But, about the MD2 format
Each frame contains the vertices, the vertex indices and the tex coord indices. Only the first frame contains the tex coords.

Loading is quite easy (as in the MD2 tutorial, i first load the data with MD2 format structures so it''s easyer, and then convert them to my structures, which are very similar to the ones in the tutorial)
So:
1. Load the header

fread(&m_Header, 1, sizeof(tMd2Header), fp);

if(m_Header.version != 8)
{
lprintf("Invalid file format (Version not 8): %s!", strFileName);
return SYS_ERR;
}

2. Load the skins, tex coords, indices and vertices

unsigned char buffer[MD2_MAX_FRAMESIZE];

m_pSkins = new tMd2Skin [m_Header.numSkins];
m_pTexCoords = new tMd2TexCoord [m_Header.numTexCoords];
m_pTriangles = new tMd2Face [m_Header.numTriangles];
m_pFrames = new tMd2Frame [m_Header.numFrames];

if (!m_pSkins || !m_pTexCoords || !m_pTriangles || !m_pFrames)
{
lprintf("ERROR: Cannot allocate memory for model\n");
return SYS_ERR;
}

fseek(fp, m_Header.offsetSkins, SEEK_SET);
fread(m_pSkins, sizeof(tMd2Skin), m_Header.numSkins, fp);

fseek(fp, m_Header.offsetTexCoords, SEEK_SET);
fread(m_pTexCoords, sizeof(tMd2TexCoord), m_Header.numTexCoords, fp);

fseek(fp, m_Header.offsetTriangles, SEEK_SET);
fread(m_pTriangles, sizeof(tMd2Face), m_Header.numTriangles, fp);

fseek(fp, m_Header.offsetFrames, SEEK_SET);

for (i = 0; i < m_Header.numFrames; i++)
{
tMd2AliasFrame *pFrame = (tMd2AliasFrame *) buffer;

m_pFrames[i].pVertices = NULL;
m_pFrames[i].pVertices = new tMd2Triangle [m_Header.numVertices];
if (!m_pFrames[i].pVertices)
{
lprintf("ERROR: Cannot allocate memory for model\n");
return SYS_ERR;
}

fread(pFrame, 1, m_Header.frameSize, fp);

strcpy(m_pFrames[i].strName, pFrame->name);

tMd2Triangle *pVertices = m_pFrames[i].pVertices;

for (int j=0; j < m_Header.numVertices; j++)
{
pVertices[j].vertex[0] = pFrame->aliasVertices[j].vertex[0] * pFrame->scale[0] + pFrame->translate[0];
pVertices[j].vertex[2] = -1 * (pFrame->aliasVertices[j].vertex[1] * pFrame->scale[1] + pFrame->translate[1]);
pVertices[j].vertex[1] = pFrame->aliasVertices[j].vertex[2] * pFrame->scale[2] + pFrame->translate[2];
}
}


3. Parse the anims

memset(pModel, 0, sizeof(t3DModel));
pModel->numObjects = m_Header.numFrames;

tAnimationInfo animation;
string strLastName = "";

for(i = 0; i < pModel->numObjects; i++)
{
string strName = m_pFrames[i].strName;
int frameNum = 0;

for(j = 0; j < strName.length(); j++)
{
if( isdigit(strName[j]) && j >= strName.length() - 2)
{
frameNum = atoi(&strName[j]);
strName.erase(j, strName.length() - j);
for (int k = 0; k < strName.size(); k++) strName[k] = tolower(strName[k]);
break;
}
}

if(strName != strLastName || i == pModel->numObjects - 1)
{
if(strLastName != "
")
{
strcpy(animation.strName, strLastName.c_str());
animation.endFrame = i - 1;
pModel->pAnimations.push_back(animation);
memset(&animation, 0, sizeof(tAnimationInfo));

pModel->numOfAnimations++;
}

animation.startFrame = frameNum - 1 + i;
}
strLastName = strName;
}


4. Convert the structures to the app''s structures

Model.numObjects = pModel->numObjects;
Model.numMaterials = pModel->numMaterials;

Model.pObjects = new t3DObject[Model.numObjects];

for (i = 0; i < pModel->numObjects; i++)
{
t3DObject* currentFrame = &Model.pObjects[i];

currentFrame->numVerts = m_Header.numVertices;
currentFrame->numTexCoords = m_Header.numTexCoords;
currentFrame->numFaces = m_Header.numTriangles;

currentFrame->pVerts = new CVector3 [currentFrame->numVerts];
if (!currentFrame->pVerts)
{
lprintf("ERROR: Cannot allocate memory for model\n");
return SYS_ERR;
}

for (j=0; j < currentFrame->numVerts; j++)
{
currentFrame->pVerts[j].x = m_pFrames[i].pVertices[j].vertex[0];
currentFrame->pVerts[j].y = m_pFrames[i].pVertices[j].vertex[1];
currentFrame->pVerts[j].z = m_pFrames[i].pVertices[j].vertex[2];
}

delete [] m_pFrames[i].pVertices;
m_pFrames[i].pVertices = NULL;

currentFrame->pFaces = new tFace [currentFrame->numFaces];
currentFrame->pTexFaces = new tTexFace [ currentFrame->numFaces];
for(j=0; j < currentFrame->numFaces; j++)
{

currentFrame->pFaces[j].vertIndex[0] = m_pTriangles[j].vertexIndices[0];
currentFrame->pFaces[j].vertIndex[1] = m_pTriangles[j].vertexIndices[1];
currentFrame->pFaces[j].vertIndex[2] = m_pTriangles[j].vertexIndices[2];

currentFrame->pTexFaces[j].coordIndex[0] = m_pTriangles[j].textureIndices[0];
currentFrame->pTexFaces[j].coordIndex[1] = m_pTriangles[j].textureIndices[1];
currentFrame->pTexFaces[j].coordIndex[2] = m_pTriangles[j].textureIndices[2];
}

if(i > 0)
{
continue;
}

currentFrame->pTexCoords = new CVector2 [currentFrame->numTexCoords];
for(j = 0; j < currentFrame->numTexCoords; j++)
{
currentFrame->pTexCoords[j].x = (float)m_pTexCoords[j].u / float(m_Header.skinWidth);
currentFrame->pTexCoords[j].y = 1.0f - (float)m_pTexCoords[j].v / float(m_Header.skinHeight);
}
}

fclose(fp); // Close the current file pointer


5. Free temp stuff

int last = strlen(strFileName) - 1;

strFileName[last ] = ''p'';
strFileName[last - 1] = ''m'';
strFileName[last - 2] = ''b'';

if (tex.LoadFromFile(strFileName)) return SYS_ERR;

if(m_pSkins) delete [] m_pSkins;
if(m_pTexCoords) delete m_pTexCoords;
if(m_pTriangles) delete m_pTriangles;
if(m_pFrames) delete m_pFrames;

m_pSkins = NULL;
m_pTexCoords = NULL;
m_pTriangles = NULL;
m_pFrames = NULL;

return SYS_OK;


The rendering uses GL so u have to convert to D3D:

void CModelMD2::Render()
{
tAnim3DModel* pModel = &Model;
if(pModel->numObjects <= 0) return;

tAnimationInfo *pAnim = &(pModel->pAnimations[pModel->currentAnim]);

int nextFrame;

if(pModel->currentFrame == pAnim->endFrame)
{
if (loopAnim)
{
nextFrame = pAnim->startFrame;
}
else
{
nextFrame = pAnim->endFrame;
}
}
else
{
nextFrame = pModel->currentFrame + 1;
}

t3DObject *pFrame = &pModel->pObjects[pModel->currentFrame];
t3DObject *pNextFrame = &pModel->pObjects[nextFrame];
t3DObject *pFirstFrame = &pModel->pObjects[0];

CVector3 vPoint1;
CVector3 vPoint2;

glFrontFace(GL_CW);
glPushMatrix();
glTranslatef(vPos.x, vPos.y + 32, vPos.z);
glRotatef(ang, 0, 1, 0);

glDisable(GL_LIGHTING);

glEnable(GL_TEXTURE_2D);
glColor3f(1.0f, 1.0f, 1.0f);
glBindTexture(GL_TEXTURE_2D, tex.texnum);

glBegin(GL_TRIANGLES);
for(int j = 0; j < pFrame->numFaces; j++)
{
for(int whichVertex = 0; whichVertex < 3; whichVertex++)
{
int vertIndex = pFirstFrame->pFaces[j].vertIndex[whichVertex];
int texIndex = pFirstFrame->pTexFaces[j].coordIndex[whichVertex];

float f = t; //(1 - cos(t * PI)) / 2 ;


vPoint1 = pFrame->pVerts[ vertIndex ];
vPoint2 = pNextFrame->pVerts[ vertIndex ];

glTexCoord2fv((float *)&pFirstFrame->pTexCoords[ texIndex ]);
glVertex3fv((float *)&(vPoint1 + (vPoint2 - vPoint1) * f));
}
}
glEnd();

glEnable(GL_LIGHTING);

glPopMatrix();
glFrontFace(GL_CCW);

}


To do it in D3D u have to assign the tex coords to each of the verts: loop across the texcoord indices and assign them to the verices.


for (i = 0; i < Model.NumObjects; i++) // for each object

{
pObj = &Mode.pObjects[i]; //obj pointer

for(int j = 0; j < pObj->numFaces; j++)
{
for(int whichVertex = 0; whichVertex < 3; whichVertex++)
{
//now assign the tex coord to the vertex

int vertIndex = pFirstFrame->pFaces[j].vertIndex[whichVertex];
// pFirstFrame is a pointer to the first frame obj

int texIndex = pFirstFrame->pTexFaces[j].coordIndex[whichVertex];
//in the D3DCustomVertex u have to put the u and v coords

/* Start: This is code for D3D (pVertices is a D3DCUSTOMVERTEX array */
pObject->pVertices[vertIndex].u = pFirstFrame->pTexCoords[texindex].x;
pObject->pVertices[vertIndex].v = pFirstFrame->pTexCoords[texindex].y; //

/*end code for d3d*/
}
}


the entire source for MD2.cpp:


#include "stdafx.h"
#include "Md2.h"
#include "log.h"
#include "opengl.h"
#include <string>
#include "3dm.h"

struct tMd2Header
{
int magic; // This is used to identify the file

int version; // The version number of the file (Must be 8)

int skinWidth; // The skin width in pixels

int skinHeight; // The skin height in pixels

int frameSize; // The size in bytes the frames are

int numSkins; // The number of skins associated with the model

int numVertices; // The number of vertices (constant for each frame)

int numTexCoords; // The number of texture coordinates

int numTriangles; // The number of faces (polygons)

int numGlCommands; // The number of gl commands

int numFrames; // The number of animation frames

int offsetSkins; // The offset in the file for the skin data

int offsetTexCoords; // The offset in the file for the texture data

int offsetTriangles; // The offset in the file for the face data

int offsetFrames; // The offset in the file for the frames data

int offsetGlCommands; // The offset in the file for the gl commands data

int offsetEnd; // The end of the file offset

};


struct tMd2AliasTriangle
{
byte vertex[3];
byte lightNormalIndex;
};

struct tMd2Triangle
{
float vertex[3];
float normal[3];
};

struct tMd2Face
{
short vertexIndices[3];
short textureIndices[3];
};

struct tMd2TexCoord
{
short u, v;
};

struct tMd2AliasFrame
{
float scale[3];
float translate[3];
char name[16];
tMd2AliasTriangle aliasVertices[1];
};

struct tMd2Frame
{
char strName[16];
tMd2Triangle *pVertices;
};

typedef char tMd2Skin[64];

void CModelMD2::Render()
{
tAnim3DModel* pModel = &Model;
if(pModel->numObjects <= 0) return;

tAnimationInfo *pAnim = &(pModel->pAnimations[pModel->currentAnim]);

int nextFrame;

if(pModel->currentFrame == pAnim->endFrame)
{
if (loopAnim)
{
nextFrame = pAnim->startFrame;
}
else
{
nextFrame = pAnim->endFrame;
}
}
else
{
nextFrame = pModel->currentFrame + 1;
}

t3DObject *pFrame = &pModel->pObjects[pModel->currentFrame];
t3DObject *pNextFrame = &pModel->pObjects[nextFrame];
t3DObject *pFirstFrame = &pModel->pObjects[0];

CVector3 vPoint1;
CVector3 vPoint2;

glFrontFace(GL_CW);
glPushMatrix();
glTranslatef(vPos.x, vPos.y + 32, vPos.z);
glRotatef(ang, 0, 1, 0);

glDisable(GL_LIGHTING);

glEnable(GL_TEXTURE_2D);
glColor3f(1.0f, 1.0f, 1.0f);
glBindTexture(GL_TEXTURE_2D, tex.texnum);

glBegin(GL_TRIANGLES);
for(int j = 0; j < pFrame->numFaces; j++)
{
for(int whichVertex = 0; whichVertex < 3; whichVertex++)
{
int vertIndex = pFirstFrame->pFaces[j].vertIndex[whichVertex];
int texIndex = pFirstFrame->pTexFaces[j].coordIndex[whichVertex];

float f = t; //(1 - cos(t * PI)) / 2 ;


vPoint1 = pFrame->pVerts[ vertIndex ];
vPoint2 = pNextFrame->pVerts[ vertIndex ];

glTexCoord2fv((float *)&pFirstFrame->pTexCoords[ texIndex ]);
glVertex3fv((float *)&(vPoint1 + (vPoint2 - vPoint1) * f));
}
}
glEnd();

glEnable(GL_LIGHTING);

glPopMatrix();
glFrontFace(GL_CCW);

}

void CModelMD2::SetPosition(CVector3 v)
{
vPos = v;
}

void CModelMD2::SetRotation(float a)
{
ang = a;
}

CModelMD2::CModelMD2()
{
t = 0.0f;
loopAnim = true;
}

void CModelMD2::Destroy()
{
DestroyModel(&Model);
}

void CModelMD2::SetAnimation(int anim)
{
char aName[255];

switch (anim)
{
case MD2_ANIM_STAND: strcpy(aName, "stand"); loopAnim = true; break;
case MD2_ANIM_WALK: strcpy(aName, "run"); loopAnim = true; break;
case MD2_ANIM_DEATH1: strcpy(aName, "death1"); loopAnim = false; break;
case MD2_ANIM_DEATH2: strcpy(aName, "death2"); loopAnim = false; break;
case MD2_ANIM_DEATH3: strcpy(aName, "death3"); loopAnim = false; break;
}

for (int i = 0; i < Model.pAnimations.size(); i++)
{
if (strcmp(Model.pAnimations[i].strName, aName) == 0)
{
Model.currentFrame = Model.pAnimations[i].startFrame;
Model.currentAnim = i;
return;
}
}
}

int CModelMD2::GetAnimation()
{
char str[255];
strcpy(str, Model.pAnimations[Model.currentAnim].strName);

if (strcmp(str, "stand") == 0) return MD2_ANIM_STAND;
if (strcmp(str, "run") == 0) return MD2_ANIM_WALK;
if (strcmp(str, "death1") == 0) return MD2_ANIM_DEATH1;
if (strcmp(str, "death2") == 0) return MD2_ANIM_DEATH2;
if (strcmp(str, "death3") == 0) return MD2_ANIM_DEATH3;

return -1;
}

void CModelMD2::SetAnimLoop(bool b)
{
loopAnim = b;
}

void CModelMD2::Update()
{
tAnimationInfo* pAnim = &Model.pAnimations[Model.currentAnim];

if (t > 1.0f)
{
if (Model.currentFrame == pAnim->endFrame)
{
if (loopAnim)
{
Model.currentFrame = pAnim->startFrame;
t = 0.0f;
}
else
{
Model.currentFrame = pAnim->endFrame;
t = 1.1f;
}
}
else
{
Model.currentFrame++;
t = 0.0f;
}
}
else
{
t += rFrameInterval * 5.0f;
}
}

int CModelMD2::LoadFromFile(char* fname)
{
tMd2Header m_Header;
tMd2Skin *m_pSkins = NULL;
tMd2TexCoord *m_pTexCoords = NULL;
tMd2Face *m_pTriangles = NULL;
tMd2Frame *m_pFrames = NULL;
tAnim3DModel* pModel = &Model;

char strFileName[255];
int i, j;

strcpy(strFileName, fname);

FILE* fp = fopen(strFileName, "rb");

if(!fp)
{
lprintf("Unable to find the file: %s!", strFileName);
return SYS_ERR;
}

fread(&m_Header, 1, sizeof(tMd2Header), fp);

if(m_Header.version != 8)
{
lprintf("Invalid file format (Version not 8): %s!", strFileName);
return SYS_ERR;
}

lprintf("Loading MD2 model %s", strFileName);

unsigned char buffer[MD2_MAX_FRAMESIZE];

m_pSkins = new tMd2Skin [m_Header.numSkins];
m_pTexCoords = new tMd2TexCoord [m_Header.numTexCoords];
m_pTriangles = new tMd2Face [m_Header.numTriangles];
m_pFrames = new tMd2Frame [m_Header.numFrames];

if (!m_pSkins || !m_pTexCoords || !m_pTriangles || !m_pFrames)
{
lprintf("ERROR: Cannot allocate memory for model\n");
return SYS_ERR;
}

fseek(fp, m_Header.offsetSkins, SEEK_SET);
fread(m_pSkins, sizeof(tMd2Skin), m_Header.numSkins, fp);

fseek(fp, m_Header.offsetTexCoords, SEEK_SET);
fread(m_pTexCoords, sizeof(tMd2TexCoord), m_Header.numTexCoords, fp);

fseek(fp, m_Header.offsetTriangles, SEEK_SET);
fread(m_pTriangles, sizeof(tMd2Face), m_Header.numTriangles, fp);

fseek(fp, m_Header.offsetFrames, SEEK_SET);

for (i = 0; i < m_Header.numFrames; i++)
{
tMd2AliasFrame *pFrame = (tMd2AliasFrame *) buffer;

m_pFrames[i].pVertices = NULL;
m_pFrames[i].pVertices = new tMd2Triangle [m_Header.numVertices];
if (!m_pFrames[i].pVertices)
{
lprintf("ERROR: Cannot allocate memory for model\n");
return SYS_ERR;
}

fread(pFrame, 1, m_Header.frameSize, fp);

strcpy(m_pFrames[i].strName, pFrame->name);

tMd2Triangle *pVertices = m_pFrames[i].pVertices;

for (int j=0; j < m_Header.numVertices; j++)
{
pVertices[j].vertex[0] = pFrame->aliasVertices[j].vertex[0] * pFrame->scale[0] + pFrame->translate[0];
pVertices[j].vertex[2] = -1 * (pFrame->aliasVertices[j].vertex[1] * pFrame->scale[1] + pFrame->translate[1]);
pVertices[j].vertex[1] = pFrame->aliasVertices[j].vertex[2] * pFrame->scale[2] + pFrame->translate[2];
}
}

memset(pModel, 0, sizeof(t3DModel));
pModel->numObjects = m_Header.numFrames;

tAnimationInfo animation;
string strLastName = "";

for(i = 0; i < pModel->numObjects; i++)
{
string strName = m_pFrames[i].strName;
int frameNum = 0;

for(j = 0; j < strName.length(); j++)
{
if( isdigit(strName[j]) && j >= strName.length() - 2)
{
frameNum = atoi(&strName[j]);
strName.erase(j, strName.length() - j);
for (int k = 0; k < strName.size(); k++) strName[k] = tolower(strName[k]);
break;
}
}

if(strName != strLastName || i == pModel->numObjects - 1)
{
if(strLastName != "
")
{
strcpy(animation.strName, strLastName.c_str());
animation.endFrame = i - 1;
pModel->pAnimations.push_back(animation);
memset(&animation, 0, sizeof(tAnimationInfo));

pModel->numOfAnimations++;
}

animation.startFrame = frameNum - 1 + i;
}
strLastName = strName;
}

Model.numObjects = pModel->numObjects;
Model.numMaterials = pModel->numMaterials;

Model.pObjects = new t3DObject[Model.numObjects];

for (i = 0; i < pModel->numObjects; i++)
{
t3DObject* currentFrame = &Model.pObjects[i];

currentFrame->numVerts = m_Header.numVertices;
currentFrame->numTexCoords = m_Header.numTexCoords;
currentFrame->numFaces = m_Header.numTriangles;

currentFrame->pVerts = new CVector3 [currentFrame->numVerts];
if (!currentFrame->pVerts)
{
lprintf("ERROR: Cannot allocate memory for model\n");
return SYS_ERR;
}

for (j=0; j < currentFrame->numVerts; j++)
{
currentFrame->pVerts[j].x = m_pFrames[i].pVertices[j].vertex[0];
currentFrame->pVerts[j].y = m_pFrames[i].pVertices[j].vertex[1];
currentFrame->pVerts[j].z = m_pFrames[i].pVertices[j].vertex[2];
}

delete [] m_pFrames[i].pVertices;
m_pFrames[i].pVertices = NULL;

currentFrame->pFaces = new tFace [currentFrame->numFaces];
currentFrame->pTexFaces = new tTexFace [ currentFrame->numFaces];
for(j=0; j < currentFrame->numFaces; j++)
{

currentFrame->pFaces[j].vertIndex[0] = m_pTriangles[j].vertexIndices[0];
currentFrame->pFaces[j].vertIndex[1] = m_pTriangles[j].vertexIndices[1];
currentFrame->pFaces[j].vertIndex[2] = m_pTriangles[j].vertexIndices[2];

currentFrame->pTexFaces[j].coordIndex[0] = m_pTriangles[j].textureIndices[0];
currentFrame->pTexFaces[j].coordIndex[1] = m_pTriangles[j].textureIndices[1];
currentFrame->pTexFaces[j].coordIndex[2] = m_pTriangles[j].textureIndices[2];
}

if(i > 0)
{
continue;
}

currentFrame->pTexCoords = new CVector2 [currentFrame->numTexCoords];
for(j = 0; j < currentFrame->numTexCoords; j++)
{
currentFrame->pTexCoords[j].x = (float)m_pTexCoords[j].u / float(m_Header.skinWidth);
currentFrame->pTexCoords[j].y = 1.0f - (float)m_pTexCoords[j].v / float(m_Header.skinHeight);
}
}

fclose(fp); // Close the current file pointer


int last = strlen(strFileName) - 1;

strFileName[last ] = ''p'';
strFileName[last - 1] = ''m'';
strFileName[last - 2] = ''b'';

if (tex.LoadFromFile(strFileName)) return SYS_ERR;

if(m_pSkins) delete [] m_pSkins;
if(m_pTexCoords) delete m_pTexCoords;
if(m_pTriangles) delete m_pTriangles;
if(m_pFrames) delete m_pFrames;

m_pSkins = NULL;
m_pTexCoords = NULL;
m_pTriangles = NULL;
m_pFrames = NULL;

return SYS_OK;
}




and for MD2.h

#ifndef _MD2_H
#define _MD2_H

#include "3dm.h"
#include "textures.h"
#include "physics.h"

#define MD2_MAX_TRIANGLES 4096
#define MD2_MAX_VERTICES 2048
#define MD2_MAX_TEXCOORDS 2048
#define MD2_MAX_FRAMES 512
#define MD2_MAX_SKINS 32
#define MD2_MAX_FRAMESIZE (MD2_MAX_VERTICES * 4 + 128)

#define kAnimationSpeed 5.0f

struct tAnimationInfo
{
char strName[255]; // This stores the name of the animation (Jump, Pain, etc..)

int startFrame; // This stores the first frame number for this animation

int endFrame; // This stores the last frame number for this animation

};

struct tAnim3DModel : public t3DModel
{
int numOfAnimations; // The number of animations in this model (NEW)

int currentAnim; // The current index into pAnimations list (NEW)

int currentFrame; // The current frame of the current animation (NEW)


vector<tAnimationInfo> pAnimations; // The list of animations (NEW)

};

#define MD2_ANIM_STAND 0
#define MD2_ANIM_WALK 1
#define MD2_ANIM_DEATH1 2
#define MD2_ANIM_DEATH2 3
#define MD2_ANIM_DEATH3 4

class CModelMD2
{
public:
CModelMD2();

int LoadFromFile(char* fname);
void Destroy();

void Render();
void Update();

void SetAnimation(int anim);
int GetAnimation();
void SetPosition(CVector3 vPos);
void SetRotation(float ang);

void SetAnimLoop(bool);
private:

tAnim3DModel Model;
CTexture tex;

float t;

bool loopAnim;

float ang;
CVector3 vPos;
};



#endif



For the anims: add some #defines for each anim (i only used run, stand, death0-3)

Hope this will make it clear 4 U


Death to the gaming industry! Long live games. /*ilici*/

Share this post


Link to post
Share on other sites
Thanks for putting in the time and effort Ilici to post that. I will have a look through this as it is different again from any of the tuts I have gone through. Really appreciate it.
Below are the steps in how I load and render the models. Im wondering if someone could pick something up that im not?

-retrieve header info (NumTriangles, NumVertices etc - all working correctly)
-retrieve triangle data (vertex indices)
-retrieve frame data (compressed vertex coords)
-Convert the compressed coords to the right coordinates by mulitplying scale and adding the translation values in the frame data, using the triangle indices in the vertex arrays as the array element (if i'm not making sense, just let me know). I put these values into my own D3d structure to make it a bit cleaner
-Render my using DrawPrimitiveUP

Is this right? Is there anything that I should be aware of in any of these steps? I could just copy and paste code if I wanted to but as I ultimately want to make my own format I would like to get this working first (and I hate giving up on things)

I will look through your code now and see if I can see what im doing wrong.

Thanks again,
Harley.





[edited by - klown9 on August 19, 2003 6:58:59 AM]

Share this post


Link to post
Share on other sites
quote:

-Convert the compressed coords to the right coordinates by mulitplying scale and adding the translation values in the frame data, using the triangle indices in the vertice arrays as the array element (if i'm not making sense, just let me know). I put these values into my own D3d structure to make it a bit cleaner



U mean that u are translating the vertices. The coords are not compressed -> the tex coords and texcoord indices are not in equal number with the vertex coords so u'll have to assign the texcoords to the vertices. (as i said in the last post).

I think u should use DrawIndexedPrimitiveUP using the "Faces" member of the MD2 format.
The Faces member contains face information using indices: for each face 3 indices are stored. My source does not use vertex arrays as u would use in direct3D because i didnt do the
quote:

To do it in D3D u have to assign the tex coords to each of the verts: loop across the texcoord indices and assign them to the verices.


part.

Ok here is how i think u render it (well using my data structs):

D3DDev->DrawIndexedPrimitiveUP(D3DPT_TRIANGLELIST, 0, pObject->NumFaces * 3, pObject->pFaces, D3DFMT_INDEX16, pObject->pVertices, 0);
not sure about the "* 3" part in the 3rd param
(the SDK says
quote:

PrimitiveCount
[in] Number of primitives to render. The number of indices used is a function of the primitive count and the primitive type. The maximum number of primitives allowed is determined by checking the MaxPrimitiveCount member of the D3DCAPS8 structure.


so i'm not sure its about how many faces or how many indices experiment with this ).











[edited by - Ilici on August 18, 2003 10:38:34 AM]

Share this post


Link to post
Share on other sites
Well I used the term ''compressed'' as that was the term used in a tutorial i was reading. Its actually a scaled down number so that it fits in a BYTE data type. Supposedly.

The ''*3'' is because each face has 3 vertices, so you need to make enough room for each vertex. My maximum was around 65000 going by the D3DCAPS8 structure.

I think i will go with this method but it just annoys me that I cant get it to work just using DrawPrimitiveUP when the tutorial I was following (not very well obviously) was using it with no problems. Ah well.

I haven''t used indexed primitives for a while so I best read up on how to set them up etc.

I just noticed another thing too. In the game I have a plain textured quad as a floor (temp thing). From one of the corners a line is attached to the MD2 model!(the MD2 file is rendered in wireframe for now) Its like all of the vertices are joined by string. I can clearly see the model and looks almost right, but its like a spider has leaped from some of the vertices and strung webs over the model joining some vertices up that shouldn''t. If you can picture that then congrats

Share this post


Link to post
Share on other sites
Sorry guys, I haven''t read all the replys here, I just saw, someone requesting the code for an MD2 loader in VB.
I have written it, based upon the file format. No tutorials here, so the code might be a little screwy. Anyway, here it is, hope you find good use for it


Dim i As Single
Dim j As Single
Dim Renderable As Boolean

Private Type Model_t
Magic As Long
Version As Long
skinWidth As Long
skinHeight As Long
frameSize As Long
numSkins As Long
numVertices As Long
numTexCoords As Long
numTriangles As Long
numGlCommands As Long
numFrames As Long
offsetSkins As Long
offsetTexCoords As Long
offsetTriangles As Long
offsetFrames As Long
offsetGlCommands As Long
offsetEnd As Long
End Type
Dim MODEL As Model_t

Private Type triangleVertex_t
vertex(0 To 2) As Byte
lightNormalIndex As Byte
End Type

Private Type Frame_t
Scale2(0 To 2) As Single
Translate(0 To 2) As Single
Name As String * 16
Vertices() As triangleVertex_t
End Type
Dim Frame() As Frame_t

Private Type Triangle_T
VertexIndices(0 To 2) As Integer
textureIndices(0 To 2) As Integer
End Type
Dim TRIANGLE() As Triangle_T

Private Type textureCoordinate_t
S As Integer
T As Integer
End Type
Dim TEXTURECOORDINATE() As textureCoordinate_t

Private Type glCommandVertex_t
S As String * 6
T As String * 6
VertexIndex As Integer
End Type
Dim GLCOMMANDVERTEX() As glCommandVertex_t


Const FVF_Md2 As Long = (D3DFVF_XYZ Or D3DFVF_NORMAL Or D3DFVF_TEX1)
Private Type Vertex_type
p As D3DVECTOR
N As D3DVECTOR
T As D3DVECTOR2
End Type
Dim MeshVerts() As D3DVERTEX
Dim MaxSize As Single
Dim matTemp As D3DMATRIX
Dim matWorld As D3DMATRIX
Const Pi = 3.14159
Const Rad = Pi / 180
Const DEG = 180 / Pi

Dim Fr As Single
Dim md2Tex As Direct3DTexture8
Dim aTimer As Long
Dim animTimer As Long
Dim prevFrame As Single
Dim nextFrame As Single
Dim InterpolateAmount As Single
Dim vTemp3D As D3DVECTOR, vTemp2D As D3DVECTOR2
Dim Segit1 As D3DVECTOR
Dim Segit2 As D3DVECTOR
Dim Segit1_2d As D3DVECTOR2
Dim Segit2_2d As D3DVECTOR2

Private Type Md2_t
Texture As Direct3DTexture8
nFrames As Single
VertexList() As D3DVERTEX
animSpeed As Single
aSpeed As Single
nTriangles As Single
cFrame As Single
End Type

Public Md2_Models(0 To 10) As Md2_t

Public Sub Init(fName As String, texFname As String, animSpeed As Single, id As Single)
Open UserINfo.MediaPath & "\3d art\md2\" & fName For Binary As #1
Seek 1, 1
Get #1, , MODEL
ReDim Frame(0 To MODEL.numFrames - 1) As Frame_t
ReDim TEXTURECOORDINATE(0 To MODEL.numTexCoords - 1) As textureCoordinate_t
ReDim TRIANGLE(0 To MODEL.numTriangles - 1) As Triangle_T
ReDim GLCOMMANDVERTEX(0 To MODEL.numGlCommands - 1) As glCommandVertex_t
For i = 0 To MODEL.numFrames - 1
ReDim Frame(i).Vertices(0 To MODEL.numVertices - 1) As triangleVertex_t
Next i
Seek 1, MODEL.offsetGlCommands + 1
Get #1, , GLCOMMANDVERTEX
Seek #1, MODEL.offsetTriangles + 1
Get #1, , TRIANGLE
Seek 1, MODEL.offsetFrames + 1
For i = 0 To MODEL.numFrames - 1
Get #1, , Frame(i).Scale2
Get #1, , Frame(i).Translate
Get #1, , Frame(i).Name
Get #1, , Frame(i).Vertices
Next i
Seek #1, MODEL.offsetTexCoords + 1
Get #1, , TEXTURECOORDINATE
Close #1
''ReDim MeshVerts(0 To MODEL.numTriangles * 3 + 12) As D3DVERTEX
Fr = 0

''### Setting up the array ###
ReDim Md2_Models(id).VertexList(0 To MODEL.numFrames - 1, 0 To MODEL.numTriangles * 3 + 12) As D3DVERTEX
Set Md2_Models(id).Texture = D3DX.CreateTextureFromFile(D3DDevice, UserINfo.MediaPath & "\2d art\textures\" & texFname)
For j = 0 To MODEL.numFrames - 1
For i = 0 To MODEL.numTriangles - 1
vTemp3D.X = Frame(j).Vertices(TRIANGLE(i).VertexIndices(0)).vertex(0) * Frame(j).Scale2(0) + Frame(j).Translate(0)
vTemp3D.Z = Frame(j).Vertices(TRIANGLE(i).VertexIndices(0)).vertex(1) * Frame(j).Scale2(1) + Frame(j).Translate(1)
vTemp3D.Y = Frame(j).Vertices(TRIANGLE(i).VertexIndices(0)).vertex(2) * Frame(j).Scale2(2) + Frame(j).Translate(2)
Md2_Models(id).VertexList(j, i * 3).X = vTemp3D.X
Md2_Models(id).VertexList(j, i * 3).Y = vTemp3D.Y
Md2_Models(id).VertexList(j, i * 3).Z = vTemp3D.Z
vTemp3D.X = Frame(j).Vertices(TRIANGLE(i).VertexIndices(1)).vertex(0) * Frame(j).Scale2(0) + Frame(j).Translate(0)
vTemp3D.Z = Frame(j).Vertices(TRIANGLE(i).VertexIndices(1)).vertex(1) * Frame(j).Scale2(1) + Frame(j).Translate(1)
vTemp3D.Y = Frame(j).Vertices(TRIANGLE(i).VertexIndices(1)).vertex(2) * Frame(j).Scale2(2) + Frame(j).Translate(2)
Md2_Models(id).VertexList(j, i * 3 + 1).X = vTemp3D.X
Md2_Models(id).VertexList(j, i * 3 + 1).Y = vTemp3D.Y
Md2_Models(id).VertexList(j, i * 3 + 1).Z = vTemp3D.Z
vTemp3D.X = Frame(j).Vertices(TRIANGLE(i).VertexIndices(2)).vertex(0) * Frame(j).Scale2(0) + Frame(j).Translate(0)
vTemp3D.Z = Frame(j).Vertices(TRIANGLE(i).VertexIndices(2)).vertex(1) * Frame(j).Scale2(1) + Frame(j).Translate(1)
vTemp3D.Y = Frame(j).Vertices(TRIANGLE(i).VertexIndices(2)).vertex(2) * Frame(j).Scale2(2) + Frame(j).Translate(2)
Md2_Models(id).VertexList(j, i * 3 + 2).X = vTemp3D.X
Md2_Models(id).VertexList(j, i * 3 + 2).Y = vTemp3D.Y
Md2_Models(id).VertexList(j, i * 3 + 2).Z = vTemp3D.Z

Md2_Models(id).VertexList(j, i * 3).tu = TEXTURECOORDINATE(TRIANGLE(i).textureIndices(0)).S / MODEL.skinWidth
Md2_Models(id).VertexList(j, i * 3).tv = TEXTURECOORDINATE(TRIANGLE(i).textureIndices(0)).T / MODEL.skinHeight
Md2_Models(id).VertexList(j, i * 3 + 1).tu = TEXTURECOORDINATE(TRIANGLE(i).textureIndices(1)).S / MODEL.skinWidth
Md2_Models(id).VertexList(j, i * 3 + 1).tv = TEXTURECOORDINATE(TRIANGLE(i).textureIndices(1)).T / MODEL.skinHeight
Md2_Models(id).VertexList(j, i * 3 + 2).tu = TEXTURECOORDINATE(TRIANGLE(i).textureIndices(2)).S / MODEL.skinWidth
Md2_Models(id).VertexList(j, i * 3 + 2).tv = TEXTURECOORDINATE(TRIANGLE(i).textureIndices(2)).T / MODEL.skinHeight
Next i
Next j

Md2_Models(id).nTriangles = (MODEL.numTriangles)
Md2_Models(id).nFrames = MODEL.numFrames
Md2_Models(id).animSpeed = animSpeed
Md2_Models(id).cFrame = -1

If MODEL.numTriangles * 3 + 12 > MaxSize Then
ReDim MeshVerts(0 To MODEL.numTriangles * 3 + 12) As D3DVERTEX
MaxSize = MODEL.numTriangles * 3 + 12
End If

animTimer = animSpeed
Renderable = False ''True
End Sub

Public Sub RenderMD2(id As Single, S As Single, E As Single, p As Single)
If Trees(p).cFrame < S Then Trees(p).cFrame = S
If GetTickCount - Trees(p).animTimer > Md2_Models(id).animSpeed Then
Trees(p).animTimer = GetTickCount
Trees(p).cFrame = Trees(p).cFrame + 1
If Trees(p).cFrame > E Then Trees(p).cFrame = S
End If
prevFrame = Trees(p).cFrame
nextFrame = Trees(p).cFrame + 1
If nextFrame > E Then nextFrame = S
InterpolateAmount = (GetTickCount - Trees(p).animTimer) / Md2_Models(id).animSpeed
For i = 0 To (Md2_Models(id).nTriangles) ''- 1
Segit1.X = Md2_Models(id).VertexList(prevFrame, i * 3).X
Segit1.Y = Md2_Models(id).VertexList(prevFrame, i * 3).Y
Segit1.Z = Md2_Models(id).VertexList(prevFrame, i * 3).Z
Segit2.X = Md2_Models(id).VertexList(nextFrame, i * 3).X
Segit2.Y = Md2_Models(id).VertexList(nextFrame, i * 3).Y
Segit2.Z = Md2_Models(id).VertexList(nextFrame, i * 3).Z
D3DXVec3Lerp vTemp3D, Segit1, Segit2, InterpolateAmount
MeshVerts(i * 3).X = vTemp3D.X '' Md2_Models(id).VertexList(Md2_Models(id).cFrame, i)
MeshVerts(i * 3).Y = vTemp3D.Y '' Md2_Models(id).VertexList(Md2_Models(id).cFrame, i)
MeshVerts(i * 3).Z = vTemp3D.Z '' Md2_Models(id).VertexList(Md2_Models(id).cFrame, i)

Segit1.X = Md2_Models(id).VertexList(prevFrame, i * 3 + 1).X
Segit1.Y = Md2_Models(id).VertexList(prevFrame, i * 3 + 1).Y
Segit1.Z = Md2_Models(id).VertexList(prevFrame, i * 3 + 1).Z
Segit2.X = Md2_Models(id).VertexList(nextFrame, i * 3 + 1).X
Segit2.Y = Md2_Models(id).VertexList(nextFrame, i * 3 + 1).Y
Segit2.Z = Md2_Models(id).VertexList(nextFrame, i * 3 + 1).Z
D3DXVec3Lerp vTemp3D, Segit1, Segit2, InterpolateAmount
MeshVerts(i * 3 + 1).X = vTemp3D.X '' Md2_Models(id).VertexList(Md2_Models(id).cFrame, i)
MeshVerts(i * 3 + 1).Y = vTemp3D.Y '' Md2_Models(id).VertexList(Md2_Models(id).cFrame, i)
MeshVerts(i * 3 + 1).Z = vTemp3D.Z '' Md2_Models(id).VertexList(Md2_Models(id).cFrame, i)

Segit1.X = Md2_Models(id).VertexList(prevFrame, i * 3 + 2).X
Segit1.Y = Md2_Models(id).VertexList(prevFrame, i * 3 + 2).Y
Segit1.Z = Md2_Models(id).VertexList(prevFrame, i * 3 + 2).Z
Segit2.X = Md2_Models(id).VertexList(nextFrame, i * 3 + 2).X
Segit2.Y = Md2_Models(id).VertexList(nextFrame, i * 3 + 2).Y
Segit2.Z = Md2_Models(id).VertexList(nextFrame, i * 3 + 2).Z
D3DXVec3Lerp vTemp3D, Segit1, Segit2, InterpolateAmount
MeshVerts(i * 3 + 2).X = vTemp3D.X ''Md2_Models(id).VertexList(Md2_Models(id).cFrame, i)
MeshVerts(i * 3 + 2).Y = vTemp3D.Y ''Md2_Models(id).VertexList(Md2_Models(id).cFrame, i)
MeshVerts(i * 3 + 2).Z = vTemp3D.Z ''Md2_Models(id).VertexList(Md2_Models(id).cFrame, i)

Segit1_2d.X = Md2_Models(id).VertexList(prevFrame, i * 3).tu
Segit1_2d.Y = Md2_Models(id).VertexList(prevFrame, i * 3).tv
Segit2_2d.X = Md2_Models(id).VertexList(nextFrame, i * 3).tu
Segit2_2d.Y = Md2_Models(id).VertexList(nextFrame, i * 3).tv
D3DXVec2Lerp vTemp2D, Segit1_2d, Segit2_2d, InterpolateAmount
MeshVerts(i * 3).tu = vTemp2D.X
MeshVerts(i * 3).tv = vTemp2D.Y

Segit1_2d.X = Md2_Models(id).VertexList(prevFrame, i * 3 + 1).tu
Segit1_2d.Y = Md2_Models(id).VertexList(prevFrame, i * 3 + 1).tv
Segit2_2d.X = Md2_Models(id).VertexList(nextFrame, i * 3 + 1).tu
Segit2_2d.Y = Md2_Models(id).VertexList(nextFrame, i * 3 + 1).tv
D3DXVec2Lerp vTemp2D, Segit1_2d, Segit2_2d, InterpolateAmount
MeshVerts(i * 3 + 1).tu = vTemp2D.X
MeshVerts(i * 3 + 1).tv = vTemp2D.Y

Segit1_2d.X = Md2_Models(id).VertexList(prevFrame, i * 3 + 2).tu
Segit1_2d.Y = Md2_Models(id).VertexList(prevFrame, i * 3 + 2).tv
Segit2_2d.X = Md2_Models(id).VertexList(nextFrame, i * 3 + 2).tu
Segit2_2d.Y = Md2_Models(id).VertexList(nextFrame, i * 3 + 2).tv
D3DXVec2Lerp vTemp2D, Segit1_2d, Segit2_2d, InterpolateAmount
MeshVerts(i * 3 + 2).tu = vTemp2D.X
MeshVerts(i * 3 + 2).tv = vTemp2D.Y

Next i

D3DDevice.SetVertexShader FVF_Md2
D3DDevice.SetRenderState D3DRS_LIGHTING, 0
D3DDevice.SetTexture 0, Md2_Models(id).Texture
D3DDevice.DrawPrimitiveUP D3DPT_TRIANGLELIST, Md2_Models(id).nTriangles, MeshVerts(0), Len(MeshVerts(0))

'' If Renderable Then
'' Animate_md2
'' D3DDevice.SetVertexShader FVF_Md2
'' D3DDevice.SetRenderState D3DRS_LIGHTING, 0
'' D3DDevice.SetTexture 0, md2Tex
'' j = MODEL.numTriangles '' CInt((MODEL.numVertices - 1) / 3) - 1
'' D3DDevice.DrawPrimitiveUP D3DPT_TRIANGLELIST, j, MeshVerts(0), Len(MeshVerts(0))
'' End If
End Sub


Public Sub RenderMesh_Md2(M As Scene_Object, o_id As Single)

D3DXMatrixIdentity matWorld
D3DXMatrixIdentity matTemp
D3DXMatrixRotationX matTemp, M.RotX * Rad
D3DXMatrixMultiply matWorld, matWorld, matTemp
D3DXMatrixIdentity matTemp
D3DXMatrixRotationY matTemp, M.RotY * Rad
D3DXMatrixMultiply matWorld, matWorld, matTemp
D3DXMatrixIdentity matTemp
D3DXMatrixRotationZ matTemp, M.rotZ * Rad
D3DXMatrixMultiply matWorld, matWorld, matTemp

D3DXMatrixIdentity matTemp
D3DXMatrixScaling matTemp, M.scaleX, M.scaleY, M.scaleZ
D3DXMatrixMultiply matWorld, matWorld, matTemp
D3DXMatrixIdentity matTemp
D3DXMatrixTranslation matTemp, M.posX, M.posY, M.posZ
D3DXMatrixMultiply matWorld, matWorld, matTemp
D3DDevice.SetTransform D3DTS_WORLD, matWorld

''Md2_Models(id).cFrame = M.s_Frame
RenderMD2 M.MeshID, M.s_Frame, M.e_Frame, o_id

D3DXMatrixIdentity matWorld
D3DXMatrixIdentity matTemp
D3DXMatrixTranslation matTemp, 0, 0, 0
D3DXMatrixMultiply matWorld, matWorld, matTemp
D3DDevice.SetTransform D3DTS_WORLD, matWorld
End Sub





"Find the path, follow the Master... Follow the master, understand the master... Overcome the master !"

Share this post


Link to post
Share on other sites
Anyway, here''s the file format:


.md2 File Format Specification

by Daniel E. Schoenblum



INTRO

This page will try and give some sort of technical documentation on the Quake2 model format (.md2).

These specs can be used freely for whatever you want. I only ask that people send me corrections, suggestions, etc.

Quake2 models are stored in files with the .md2 extension. This is a custom format used only by Quake2 and (probably) Quake2 mission packs. md2 files can be generated from various other file formats by tools provided freely by id, in original and modified form. A single md2 file contains the model''s geometry, frame information, skin filename(s), and texture coordinates. The file is little-endian (intel byte ordering).

HEADER

The header comes right at the start of the file. The information in the header is needed to load different parts of the model.

typedef struct
{
int magic;
int version;
int skinWidth;
int skinHeight;
int frameSize;
int numSkins;
int numVertices;
int numTexCoords;
int numTriangles;
int numGlCommands;
int numFrames;
int offsetSkins;
int offsetTexCoords;
int offsetTriangles;
int offsetFrames;
int offsetGlCommands;
int offsetEnd;
} model_t;

int magic: A "magic number" used to identify the file. The magic number is 844121161 in decimal (0x32504449 in hexadecimal). The magic number is equal to the int "IDP2" (id polygon 2), which is formed by (''I'' + (''D'' << 8) + (''P'' << 16) + (''2'' << 24)).

int version: Version number of the file. Always 8.

int skinWidth: Width of the skin(s) in pixels.

int skinHeight: Height of the skin(s) in pixels.

int frameSize: Size of each frame in bytes.

int numSkins: Number of skins associated with this model.

int numVertices: Number of vertices in each frame.

int numTexCoords: Number of texture coordinates (not necessarily the same as the number of vertices).

int numTriangles: Number of triangles in each frame.

int numGlCommands: Number of dwords (4 bytes) in the gl command list.

int numFrames: Number of frames.

int offsetSkins: Offset, in bytes from the start of the file, to the list of skin names.

int offsetTexCoords: Offset, in bytes from the start of the file, to the list of texture coordinates.

int offsetTriangles: Offset, in bytes from the start of the file, to the list of triangles.

int offsetFrames: Offset, in bytes from the start of the file, to the list of frames.

int offsetGlCommands: Offset, in bytes from the start of the file, to the gl command list.

int offsetEnd: Offset, in bytes from the start of the file, to the end (size of the file).

FRAMES

Each frame contains the positions in 3D space for each vertex of each triangle that makes up the model. Quake 2 (and Quake) models contain only triangles.

typdef struct
{
byte vertex[3];
byte lightNormalIndex;
} triangleVertex_t;

byte vertex[3]: The three bytes represent the x, y, and z coordinates of this vertex. This is not the "real" vertex coordinate. This is a scaled version of the coordinate, scaled so that each of the three numbers fit within one byte. To scale the vertex back to the "real" coordinate, you need to first multiply each of the bytes by their respective float scale in the frame_t structure, and then add the respective float translation< /a>, also in the frame_t structure. This will give you the vertex coordinate relative to the model''s origin, which is at the origin, (0, 0, 0).

byte lightNormalIndex: This is an index into a table of normals kept by Quake2. To get the table, you need to download this zip file (1.7 MB), released by id, that has the source code to all of the tools they used for quake2.

typedef struct
{
float scale[3];
float translate[3];
char name[16];
triangleVertex_t vertices[1];
} frame_t;


frame_t is a variable sized structure, however all frame_t structures within the same file will have the same size (numVertices in the header)

float scale[3]: This is a scale used by the vertex member of the triangleVertex_t structure.

float translate[3]: This is a translation used by the vertex member of the triangleVertex_t structure.

char name[16]: This is a name for the frame.

triangleVertex_t vertices[1]: An array of numVertices triangleVertex_t structures.

TRIANGLES

Quake 2 models are made up of only triangles. At offsetTriangles in the file is an array of triangle_t structures. The array has numTriangles structures in it.

typedef struct
{
short vertexIndices[3];
short textureIndices[3];
} triangle_t;


short vertexIndices: These three shorts are indices into the array of vertices in each frames. In other words, the number of triangles in a md2 file is fixed, and each triangle is always made of the same three indices into each frame''s array of vertices. So, in each frame, the triangles themselves stay intact, their vertices are just moved around.

short textureIndices: These three shorts are indices into the array of texture coordinates.

SKINS

There is an array of numSkins skin names stored at offsetSkins into the file. Each skin name is a char[64]. The name is really a path to the skin, relative to the base game directory (baseq2 f or "standard" Quake2). The skin files are regular pcx files.

typedef struct
{
short s, t;
} textureCoordinate_t;


short s, t: These two shorts are used to map a vertex onto a skin. The horizontal axis position is given by s, and the vertical axis position is given by t. The range for s is greater than or equal to 0 and less than skinWidth< /a> (0 <= s < skinWidth). The range for t is greater than or equal to 0 and less than skinHeight (0 <= s < skinHeight). N ote that the ranges are different than in the s and t members of the glCommandVertex structure.

GL COMMANDS

At offsetGlCommands bytes into the file, there is the gl command list, which is made up of a series of numGlCommands int''s and float''s, organized into groups. Each group starts with an int. If it is positive, it is followed by that many glCommandVertex_t structures, which form a triangle strip. If it is negative, it is followed by -x glCommandVertex_t structures, which fo rm a triangle fan. A 0 indicates the end of the list. The list is an optimized way of issuing commands when rendering with OpenGl.
typedef struct
{
float s, t;
int vertexIndex;
} glCommandVertex_t;


float s, t: These two floats are used to map a vertex onto a skin. The horizontal axis position is given by s, and the vertical axis position is given by t. The range for s and for t is 0.0 to 1.0. Note that the ranges are different than in the textureCoordinate_t structure. They are stored as floats here because that''s the way Quake2 passes them to OpenGl.

int vertexIndex: Index into the array of vertices stored in each frame.

MAXIMUMS

Quake2 has some pre-defined limits, so that dynamic memory does not need to be used. You can use these to your advantage to speed up loading if you want.


Triangles: 4096
Vertices: 2048
Texture Coordinates: 2048
Frames: 512
Skins: 32



Quake and Quake2 are trademarks of id Software.
All trademarks used are properties of their respective owners.

Share this post


Link to post
Share on other sites
Dark_Guy,
thanks heaps for your code. I printed yours and my code out, and compared but didn't see any differences (your rendering code is different with the interpolation etc, but that doesn't have anything to do with the problems i was having) so I decided to start from scratch using your code as a base and just changing a few things to suit my needs a bit better. AND IT WORKS!!! Even now, i still cant see what I was doing wrong so it was probably a simple variable value out by a little bit (most likely a looping variable, still think its to do with how I was using the vertex indices). So a big thankyou to Dark_Guy and everyone else who replied. I haven't tried to do texturing yet and still need to convert it to c++ so you will probably here from me again soon
Anyone that is interested, I will be posting my MD2 viewer in VB with code once I get texturing working, on my site and also a c++ class. I will try to comment them well enough so someone as simple as me can understand them
Thanks again,
Harley





BreakoutX

[edited by - klown9 on August 24, 2003 7:14:08 AM]

Share this post


Link to post
Share on other sites

  • Advertisement