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]
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
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
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)
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.
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.
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.
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