This section is under re-development. It is not finished, and not fully functional. The current
development stage is pre-alpha and as such, not every feature is even implemented yet.
MD2 Loading/Rendering
Started: 7th October 2008
(Note - if this looks familiar, it is probably because we both have the same book - "Focus on 3D models". I used it's MD2 model for developing this loader for. I have indicated where I have used the book as I go along.) I wanted more...character to the engine. The MD3 rendering seems just a bit too out of reach at the moment, so the idea is to get more understanding with models, using simpler formats first. MD2 was a good candidate for this. Loading was straightforward except for the vertices. Ill expand on this in a moment. The model file itself is simple – the header, which contains the ID, version, offsets, and count of each corresponding data sections. I simply read in the entire file, then used an unsigned char pointer to start at each offset and work its way to the count of each type. For triangles, this seems to be a simple memcpy of iNumTriangles * sizeof(SMD2Triangle). Frames were the tricky ones, and ended up with me looking up a solution in "Focus on 3D Models". There are 'iNumFrames' frames in the model, and each has 'iNumVertices' vertices each. I had trouble thinking out the best approach to memcpy this data – the solution was to do it in small chunks – read in the scale, then the translation, then the char array for the name. Then its onto looping over iNumVertices, and reading in the vertices like so: pFrame[i].pVerts[j].fVert[0] = ucpReadBuffer[0] * pFrame[i].fScale[0] + pFrame[i].fTrans[0]; pFrame[i].pVerts[j].fVert[1] = ucpReadBuffer[2] * pFrame[i].fScale[2] + pFrame[i].fTrans[2]; pFrame[i].pVerts[j].fVert[2] = ucpReadBuffer[1] * pFrame[i].fScale[1] + pFrame[i].fTrans[1]; ucpReadBuffer += 4; ...Which is inside a for() loop, inside the for() loop which iterates over each frame. The vertex values are essentially compressed – we multiply the raw values by the scale + translation to get the real coords (converting from short to float). Finally, we increment the read buffer by the number of bytes just read in (plus 1 byte for a reserved value), so that the next loop iteration ends up with the necessary data, and not the data we just used. Rendering was simple for now – I just used the first frame for testing purposes, and looped over the count of vertices: glVertex3f(pFrame[0].pVerts[j].fVert[0], pFrame[0].pVerts[j].fVert[1], pFrame[0].pVerts[j].fVert[2]); Of course, this doesnt render the model – just its vertices (which was done in GL_POINTS mode). Rendering a more physically solid model was done by implementing the triangle data, which holds indexes into the vertex struct array, within the current frame. It is not as complicated as it sounds: for(int i = 0; i < header.iNumTriangles; i++) { glVertex3fv(pFrame[0].pVerts[ pTriangle[i].sVertexIndicies[0] ].fVert); glVertex3fv(pFrame[0].pVerts[ pTriangle[i].sVertexIndicies[1] ].fVert); glVertex3fv(pFrame[0].pVerts[ pTriangle[i].sVertexIndicies[2] ].fVert); } As it stands though, it seems errornous. The feet are connecting to the tail of the model, which should not be happening: Improved 8th October 2008 I fixed the problem with the triangles connecting the tail to the toes, and so forth. I was offsetting the read pointer by the number of triangles, instead of the offset value: ucpReadBuffer = ucpFileBuffer; ucpReadBuffer += header.iOffsetTriangles; (The second line used to be ucpReadBuffer += header.iNumTriangles). The model now renders fine – just, it needs texturing, and animating. (MD2 rendering successfully, but without textures) Initially, texturing came as a suprise. The model I am using apparently has a skin count of 0, so the loop I was using to read in each skin (texture), wasnt looping. I added an entry to the skins for it to account for the default skin. This seems a cheap, nasty workaround, but I havent read much into it yet. Initially, mapping the texture to the model was full of errors. The coordinates are calculated as follows: pTexCoord = new SMD2TexCoord[header.iNumTexCoords]; for(int i = 0; i < header.iNumTexCoords; i++) { pTexCoord[i].fTex[0] = (float)ucpReadBuffer[0] / header.iSkinWidthPx; pTexCoord[i].fTex[1] = (float)ucpReadBuffer[2] / header.iSkinHeightPx; ucpReadBuffer += 4; } The coordinates follow ID's little compression idea, of converting the vertices to short values which take up less memory than a float. They have provided the values to divide by to get the float, which is the skin width and skin height from the file's header. We are incrementing the 'read buffer' by 4 bytes, which is the size of two short variables. We deal with both the s and t (u and v) texcoords one pair at a time. The texcoords are mapped out as follows: glTexCoord2fv(pTexCoord[ pTriangle[i].sTexIndicies[0] ].fTex); glTexCoord2fv(pTexCoord[ pTriangle[i].sTexIndicies[1] ].fTex); glTexCoord2fv(pTexCoord[ pTriangle[i].sTexIndicies[2] ].fTex); In amongst the vertex calls in the same render loop. The problems I had were: -Not referring to the correct TexIndicies[] -Not putting the TexIndicies as the index of the pTexCoord -Texture image was upside down All were fairly quick to fix – the last problem took the longest – Initially I thought I had calculated the coordinates wrong, or placed them wrongly in the render loop. I noticed that the model's tail underside was where it's mouth was, which in the texture itself, is the wrong way around. The texture was originally a bitmap, which is stored upside down, so I flipped the image vertically, and finally, the model is showing as it should be. As a note, I saved the image as a tga, as at this point, I dont have a bitmap reader. (Not referring to the correct TexIndicies[]) (Texture image was upside down) Now, it just has to be animated! Improved 10th October 2008 I have added a bounding box to the model. As the vertex data is loading in, I did a number of checks to find out which was the lowest and highest xyz values. Those values are then passed to a 'cube' struct which is then used to calulate a vertex array for drawing the actual bounding box. I did it this way as I seem to keep coming across the need to draw a cube, so I made a function to calculate and draw it. (Gun turret model with bounding box) |