/* orxonox - the future of 3D-vertical-scrollers Copyright (C) 2004 orx This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. ### File Specific: main-programmer: Patrick Boenzli */ #define DEBUG_SPECIAL_MODULE DEBUG_MODULE_IMPORTER #include "md2Model.h" #include #include #include using namespace std; /******************************************************************************** * MD2MODEL * ********************************************************************************/ /** \brief standard constructor creates a new model */ MD2Model::MD2Model () { this->setClassName ("MD2Model"); } /** \brief standard deconstructor */ MD2Model::~MD2Model () { // delete what has to be deleted here } /** \brief draw function these function will take NO argument in the final version, just for testing */ void MD2Model::draw(t3DModel *pModel) { if( pModel->objectList.size() <= 0) return; glEnable(GL_TEXTURE_2D); //glBindTexture(GL_TEXTURE_2D, this->diffuseTexture->getTexture()); t3DObject *pObject = &pModel->objectList[0]; glBegin(GL_TRIANGLES); for(int j = 0; j < pObject->numOfFaces; j++) { for(int whichVertex = 0; whichVertex < 3; whichVertex++) { int index = pObject->pFaces[j].vertIndex[whichVertex]; int index2 = pObject->pFaces[j].coordIndex[whichVertex]; /* we invert the normals since the md2 file format uses different style */ /* FIX FIX FIX: ther are actualy no reasons to compute the normals every frame: change this later*/ glNormal3f(-pObject->pNormals[index].x, -pObject->pNormals[index].y, -pObject->pNormals[index].z); if( likely(pObject->pTexVerts != NULL)) { glTexCoord2f(pObject->pTexVerts[index2].x, pObject->pTexVerts[index2].y); } glVertex3f(pObject->pVerts[index].x, pObject->pVerts[index].y, pObject->pVerts[index].z); } } glEnd(); glDisable(GL_TEXTURE_2D); } /******************************************************************************** * MD2LOADER * ********************************************************************************/ /** \brief standard deconstructor creates a new model loader */ MD2Loader::MD2Loader() { this->setClassName ("MD2Loader"); /* initialize all data to initial state */ memset(&this->header, 0, sizeof(tMd2Header)); this->pSkins = NULL; this->pTexCoords = NULL; this->pTriangles = NULL; this->pFrames = NULL; } /** \brief standard deconstructor */ MD2Loader::~MD2Loader() {} /** \brief this is called by the client to open the .Md2 file, read it, then clean up \param model to load in \param file name to load \param texture name to load */ bool MD2Loader::importMD2(t3DModel *pModel, char *fileName, char *textureName) { this->pFile = fopen(fileName, "rb"); if( unlikely(!pFile)) { PRINTF(1)("Couldn't open the MD2 File for loading. Exiting.\n"); return false; } fread(&this->header, 1, sizeof(tMd2Header), pFile); /* check for the header version: make sure its a md2 file :) */ if( likely(this->header.version != 8)) { PRINTF(1)("Couldn't load file %s: invalid file format: stop loading\n", fileName); return false; } this->readMD2Data(); this->convertDataStructures(pModel); this->computeNormals(pModel); if( likely((int)textureName)) { tMaterialInfo textureInfo; strcpy(textureInfo.strFile, textureName); /* since there is only one texture for a .Md2 file, the ID is always 0 */ textureInfo.texureId = 0; textureInfo.uTile = 1; /* we only have 1 material for a model */ pModel->numOfMaterials = 1; pModel->materialList.push_back(textureInfo); } this->cleanUp(); return true; } /** \brief This function reads in all of the model's data, except the animation frames */ void MD2Loader::readMD2Data() { unsigned char buffer[MD2_MAX_FRAMESIZE]; this->pSkins = new tMd2Skin[this->header.numSkins]; this->pTexCoords = new tMd2TexCoord[this->header.numTexCoords]; this->pTriangles = new tMd2Face[this->header.numTriangles]; this->pFrames = new tMd2Frame[this->header.numFrames]; /* we read the skins */ fseek(this->pFile, this->header.offsetSkins, SEEK_SET); fread(this->pSkins, sizeof(tMd2Skin), this->header.numSkins, this->pFile); /* read all vertex data */ fseek(this->pFile, this->header.offsetTexCoords, SEEK_SET); fread(this->pTexCoords, sizeof(tMd2TexCoord), this->header.numTexCoords, this->pFile); /* read face data for each triangle (normals) */ fseek(this->pFile, this->header.offsetTriangles, SEEK_SET); fread(this->pTriangles, sizeof(tMd2Face), this->header.numTriangles, this->pFile); /* reading all frame data */ fseek(this->pFile, this->header.offsetFrames, SEEK_SET); for( int i = 0; i < this->header.numFrames; i++) { tMd2AliasFrame *pFrame = (tMd2AliasFrame *) buffer; this->pFrames[i].pVertices = new tMd2Triangle [this->header.numVertices]; /* read the frame animation data */ fread(pFrame, 1, this->header.frameSize, this->pFile); strcpy(this->pFrames[i].strName, pFrame->name); tMd2Triangle *pVertices = this->pFrames[i].pVertices; /* scale translations, store vertices: since id used a non-opengl xyz notation, we have to swap y and z and negate z axis */ for( int j = 0; j < this->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]; } } } /** \brief this function fills in the animation list for each animation by name and frame \param model */ void MD2Loader::parseAnimations(t3DModel *pModel) { tAnimationInfo animationInfo; string strLastName = ""; /* the animation parse process looks a little bit wired: this is because there are no fix bounds for the animation lengths, so the frames are destingushed using their names which is normaly composed of */ for(int i = 0; i < pModel->numOfObjects; i++) { string strName = this->pFrames[i].strName; int frameNum = 0; /* erease the frame number from the frame-name */ for(unsigned int j = 0; j < strName.length(); j++) { if( isdigit(strName[j]) && j >= strName.length() - 2) { frameNum = atoi(&strName[j]); strName.erase(j, strName.length() - j); break; } } printf("current: %s, last: %s\n", strName.c_str(), strLastName.c_str()); /* animations are sorted through their names: this is how its been done: */ if( strName != strLastName || i == pModel->numOfObjects - 1) { if( strLastName != "") { strcpy(animationInfo.strName, strLastName.c_str()); animationInfo.endFrame = i; pModel->animationList.push_back(animationInfo); memset(&animationInfo, 0, sizeof(tAnimationInfo)); pModel->numOfAnimations++; } animationInfo.startFrame = frameNum - 1 + i; } strLastName = strName; } } /** \brief this function converts the .md2 structures to our own model and object structures: decompress \param model */ void MD2Loader::convertDataStructures(t3DModel *pModel) { int j = 0, i = 0; memset(pModel, 0, sizeof(t3DModel)); pModel->numOfObjects = this->header.numFrames; this->parseAnimations(pModel); for (i = 0; i < pModel->numOfObjects; i++) { t3DObject currentFrame; currentFrame.numOfVerts = this->header.numVertices; currentFrame.numTexVertex = this->header.numTexCoords; currentFrame.numOfFaces = this->header.numTriangles; currentFrame.pVerts = new CVector3[currentFrame.numOfVerts]; for (j = 0; j < currentFrame.numOfVerts; j++) { currentFrame.pVerts[j].x = this->pFrames[i].pVertices[j].vertex[0]; currentFrame.pVerts[j].y = this->pFrames[i].pVertices[j].vertex[1]; currentFrame.pVerts[j].z = this->pFrames[i].pVertices[j].vertex[2]; } delete this->pFrames[i].pVertices; if( likely(i > 0)) { pModel->objectList.push_back(currentFrame); continue; } currentFrame.pTexVerts = new CVector2[currentFrame.numTexVertex]; currentFrame.pFaces = new tFace[currentFrame.numOfFaces]; for(j = 0; j < currentFrame.numTexVertex; j++) { currentFrame.pTexVerts[j].x = this->pTexCoords[j].u / float(this->header.skinWidth); currentFrame.pTexVerts[j].y = 1 - this->pTexCoords[j].v / float(this->header.skinHeight); } for(j = 0; j < currentFrame.numOfFaces; j++) { currentFrame.pFaces[j].vertIndex[0] = this->pTriangles[j].vertexIndices[0]; currentFrame.pFaces[j].vertIndex[1] = this->pTriangles[j].vertexIndices[1]; currentFrame.pFaces[j].vertIndex[2] = this->pTriangles[j].vertexIndices[2]; currentFrame.pFaces[j].coordIndex[0] = this->pTriangles[j].textureIndices[0]; currentFrame.pFaces[j].coordIndex[1] = this->pTriangles[j].textureIndices[1]; currentFrame.pFaces[j].coordIndex[2] = this->pTriangles[j].textureIndices[2]; } pModel->objectList.push_back(currentFrame); } } /** \brief this function conputes the normals of the model \param model */ void MD2Loader::computeNormals(t3DModel *pModel) { CVector3 vVector1, vVector2, vNormal, vPoly[3]; if( unlikely(pModel->numOfObjects <= 0)) return; /* now computing face normals: this means just averaging the vertex normals of a face */ /* so for every object: */ for(int index = 0; index < pModel->numOfObjects; index++) { t3DObject *pObject = &(pModel->objectList[index]); /* allocate all the memory we need to calculate the normals */ CVector3 *pNormals = new CVector3 [pObject->numOfFaces]; CVector3 *pTempNormals = new CVector3 [pObject->numOfFaces]; pObject->pNormals = new CVector3 [pObject->numOfVerts]; for(int i=0; i < pObject->numOfFaces; i++) { /* cache the points to make coding easier :) */ vPoly[0] = pObject->pVerts[pObject->pFaces[i].vertIndex[0]]; vPoly[1] = pObject->pVerts[pObject->pFaces[i].vertIndex[1]]; vPoly[2] = pObject->pVerts[pObject->pFaces[i].vertIndex[2]]; vVector1 = MathHelp::VectorDiff(vPoly[0], vPoly[2]); vVector2 = MathHelp::VectorDiff(vPoly[2], vPoly[1]); vNormal = MathHelp::CrossProduct(vVector1, vVector2); pTempNormals[i] = vNormal; vNormal = MathHelp::NormalizeVector(vNormal); pNormals[i] = vNormal; } /* now calculating vertex normals */ CVector3 vSum = {0.0, 0.0, 0.0}; CVector3 vZero = vSum; int shared=0; for (int i = 0; i < pObject->numOfVerts; i++) { for (int j = 0; j < pObject->numOfFaces; j++) { if (pObject->pFaces[j].vertIndex[0] == i || pObject->pFaces[j].vertIndex[1] == i || pObject->pFaces[j].vertIndex[2] == i) { vSum = MathHelp::AddVector(vSum, pTempNormals[j]); shared++; } } pObject->pNormals[i] = MathHelp::DivideVectorByScaler(vSum, float(-shared)); pObject->pNormals[i] = MathHelp::NormalizeVector(pObject->pNormals[i]); vSum = vZero; shared = 0; } delete [] pTempNormals; delete [] pNormals; } } /** \brief This function cleans up our allocated memory and closes the file */ void MD2Loader::cleanUp() { fclose(this->pFile); if( this->pSkins) delete [] this->pSkins; if( this->pTexCoords) delete this->pTexCoords; if( this->pTriangles) delete this->pTriangles; if( this->pFrames) delete this->pFrames; }