/* 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 "material.h" #include "resource_manager.h" #include using namespace std; sVec3D MD2Model::anorms[NUM_VERTEX_NORMALS] = { #include "anorms.h" }; float MD2Model::anormsDots[SHADEDOT_QUANT][256] = { #include "anormtab.h" }; static float *shadeDots = MD2Model::anormsDots[0]; float md2Angle = 0.0f; sAnim MD2Model::animationList[21] = { // begin, end, fps { 0, 39, 9 }, // STAND { 40, 45, 10 }, // RUN { 46, 53, 10 }, // ATTACK { 54, 57, 7 }, // PAIN_A { 58, 61, 7 }, // PAIN_B { 62, 65, 7 }, // PAIN_C { 66, 71, 7 }, // JUMP { 72, 83, 7 }, // FLIP { 84, 94, 7 }, // SALUTE { 95, 111, 10 }, // FALLBACK { 112, 122, 7 }, // WAVE { 123, 134, 6 }, // POINTT { 135, 153, 10 }, // CROUCH_STAND { 154, 159, 7 }, // CROUCH_WALK { 160, 168, 10 }, // CROUCH_ATTACK { 196, 172, 7 }, // CROUCH_PAIN { 173, 177, 5 }, // CROUCH_DEATH { 178, 183, 7 }, // DEATH_FALLBACK { 184, 189, 7 }, // DEATH_FALLFORWARD { 190, 197, 7 }, // DEATH_FALLBACKSLOW { 198, 198, 5 }, // BOOM }; /******************************************************************************** * MD2Model * ********************************************************************************/ /* \brief simple constructor initializing all variables */ MD2Model::MD2Model() { /* this creates the data container via ressource manager */ this->data = new MD2Data(); this->scaleFactor = this->data->scaleFactor; this->setAnim(BOOM); } /* \brief simple destructor, dereferencing all variables this is where the ressource manager is cleaning the stuff */ MD2Model::~MD2Model() { } /* \brief load model \param name of the model file \return true if everything worked out smoothly */ bool MD2Model::loadModel(const char* fileName) { return this->data->loadModel(fileName); } /* \brief load the skin as a material \param name of the texture file \return true if ok */ bool MD2Model::loadSkin(const char* fileName) { return this->data->loadSkin(fileName); } /** \brief initializes an array of vert with the current frame scaled vertices we won't use the pVertices array directly, since its much easier and we need saving of data anyway */ void MD2Model::interpolate(sVec3D* verticesList) { sVec3D* currVec; sVec3D* nextVec; currVec = &this->data->pVertices[this->data->numVertices * this->animationState.currentFrame]; nextVec = &this->data->pVertices[this->data->numVertices * this->animationState.nextFrame]; for(int i = 0; i < this->data->numFrames; ++i) { verticesList[i][0] = (currVec[i][0] + this->animationState.interpolationState * (nextVec[i][0] - currVec[i][0])) * this->scaleFactor; verticesList[i][1] = (currVec[i][1] + this->animationState.interpolationState * (nextVec[i][1] - currVec[i][1])) * this->scaleFactor; verticesList[i][2] = (currVec[i][2] + this->animationState.interpolationState * (nextVec[i][2] - currVec[i][2])) * this->scaleFactor; } } /* \brief sets the animation type \param animation type the animation types can be looked up in the animationType table */ void MD2Model::setAnim(int type) { if( (type < 0) || (type > MAX_ANIMATIONS) ) type = 0; this->animationState.startFrame = animationList[type].firstFrame; this->animationState.endFrame = animationList[type].lastFrame; this->animationState.nextFrame = animationList[type].firstFrame + 1; this->animationState.fps = animationList[type].fps; this->animationState.type = type; this->animationState.interpolationState = 0.0; this->animationState.localTime = 0.0; this->animationState.lastTime = 0.0; this->animationState.currentFrame = animationList[type].firstFrame; } /* \brief sets the time in seconds passed since the last tick \param time in sec */ void MD2Model::tick(float time) { this->animationState.localTime += time; } /* \brief draws the model: interface for all other classes out in the world */ void MD2Model::draw() { if( likely(this->animationState.localTime > 0.0)) this->animate(); glPushMatrix(); this->renderFrame(); glPopMatrix(); } /* \brief this is an internal function to render this special frame selected by animate() */ void MD2Model::renderFrame() { static sVec3D verticesList[MD2_MAX_VERTICES]; /* performance: created only once in a lifetime */ int* pCommands = this->data->pGLCommands; /* some face culling stuff */ glPushAttrib(GL_POLYGON_BIT); glFrontFace(GL_CW); glEnable(GL_CULL_FACE); glCullFace(GL_BACK); this->processLighting(); this->interpolate(verticesList); this->data->material->select(); /* draw the triangles */ while( int i = *(pCommands++)) /* strange looking while loop for maximum performance */ { if( i < 0) { glBegin(GL_TRIANGLE_FAN); i = -i; } else { glBegin(GL_TRIANGLE_STRIP); } for(; i > 0; i--, pCommands += 3) /* down counting for loop, next 3 gl commands */ { glNormal3fv(anorms[this->data->pLightNormals[pCommands[2]]]); glTexCoord2f( ((float *)pCommands)[0], ((float *)pCommands)[1] ); glVertex3fv(verticesList[pCommands[2]]); } glEnd(); } glDisable(GL_CULL_FACE); glPopAttrib(); } /* \brief animates the current model depending on the time passed (tick function), the player will select another model */ void MD2Model::animate() { if( this->animationState.localTime - this->animationState.lastTime > (1.0f / this->animationState.fps)) { this->animationState.currentFrame = this->animationState.nextFrame; this->animationState.nextFrame++; if( this->animationState.nextFrame > this->animationState.endFrame) this->animationState.nextFrame = this->animationState.startFrame; this->animationState.lastTime = this->animationState.localTime; } if( this->animationState.currentFrame > (this->data->numFrames - 1) ) this->animationState.currentFrame = 0; if( this->animationState.nextFrame > (this->data->numFrames - 1) ) this->animationState.nextFrame = 0; this->animationState.interpolationState = this->animationState.fps * (this->animationState.localTime - this->animationState.lastTime); } /* \brief this is how id is precessing their lightning the details of how the whole lighting process is beeing handled - i have no idea... :) */ void MD2Model::processLighting() { shadeDots = anormsDots[((int)(md2Angle*(SHADEDOT_QUANT / 360.0)))&(SHADEDOT_QUANT - 1)]; } /* \brief prints out debug informations */ void MD2Model::debug() { PRINT(0)("\n==========================| MD2Model::debug() |===\n"); PRINT(0)("= Model FileName:\t%s\n", this->data->fileName); PRINT(0)("= Skin FileName:\t%s\n", this->data->skinFileName); PRINT(0)("= Size in Memory:\t%i Bytes\n", this->data->header->frameSize * this->data->header->numFrames + 64); // 64bytes is the header size PRINT(0)("= Number of Vertices:\t%i\n", this->data->header->numVertices); PRINT(0)("= Number of Frames: \t%i\n", this->data->header->numFrames); PRINT(0)("= Height, Width:\t%i, %i\n", this->data->header->skinHeight, this->data->header->skinWidth); PRINT(0)("===================================================\n\n"); } /******************************************************************************** * MD2Data * ********************************************************************************/ /* \brief simple constructor */ MD2Data::MD2Data() { this->pVertices = NULL; this->pGLCommands = NULL; this->pLightNormals = NULL; this->numFrames = 0; this->numVertices = 0; this->numGLCommands = 0; this->scaleFactor = 1.0f; } /* \brief simple destructor this will clean out all the necessary data for a specific md2model */ MD2Data::~MD2Data() { delete [] this->pVertices; delete [] this->pGLCommands; delete [] this->pLightNormals; } /* \brief this will load the whole model data (vertices, opengl command list, ...) \param name to the model file \return true if success */ bool MD2Data::loadModel(const char* fileName) { FILE *pFile; //file stream char* buffer; //buffer for frame data sFrame* frame; //temp frame sVec3D *pVertex; int* pNormals; pFile = fopen(fileName, "rb"); if( unlikely(!pFile)) { PRINTF(1)("Couldn't open the MD2 File for loading. Exiting.\n"); return false; } this->header = new MD2Header; fread(this->header, 1, sizeof(MD2Header), pFile); /* check for the header version: make sure its a md2 file :) */ if( unlikely(this->header->version != MD2_VERSION) && unlikely(this->header->ident != MD2_IDENT)) { PRINTF(1)("Couldn't load file %s: invalid file format: stop loading\n", fileName); return false; } this->fileName = new char[strlen(fileName)+1]; strcpy(this->fileName, fileName); /* got the data: map it to locals */ this->numFrames = this->header->numFrames; this->numVertices = this->header->numVertices; this->numTriangles = this->header->numTriangles; this->numGLCommands = this->header->numGlCommands; /* allocate memory for the data storage */ this->pVertices = new sVec3D[this->numVertices * this->numFrames]; this->pGLCommands = new int[this->numGLCommands]; this->pLightNormals = new int[this->numVertices * this->numFrames]; buffer = new char[this->numFrames * this->header->frameSize]; /* read frame data from the file to a temp buffer */ fseek(pFile, this->header->offsetFrames, SEEK_SET); fread(buffer, this->header->frameSize, this->numFrames, pFile); /* read opengl commands */ fseek(pFile, this->header->offsetGlCommands, SEEK_SET); fread(this->pGLCommands, sizeof(int), this->numGLCommands, pFile); for(int i = 0; i < this->numFrames; ++i) { frame = (sFrame*)(buffer + this->header->frameSize * i); pVertex = this->pVertices + this->numVertices * i; pNormals = this->pLightNormals + this->numVertices * i; for(int j = 0; j < this->numVertices; ++j) { /* SPEEDUP: *(pVerts + i + 0) = (*(frame->pVertices + i + 0)... */ pVertex[j][0] = (frame->pVertices[j].v[0] * frame->scale[0]) + frame->translate[0]; pVertex[j][1] = (frame->pVertices[j].v[2] * frame->scale[2]) + frame->translate[2]; pVertex[j][2] = -1.0 * (frame->pVertices[j].v[1] * frame->scale[1] + frame->translate[1]); pNormals[j] = frame->pVertices[j].lightNormalIndex; } } delete [] buffer; fclose(pFile); } /* \brief loads the skin/material stuff \param name of the skin file \return true if success */ bool MD2Data::loadSkin(const char* fileName) { this->skinFileName = new char[strlen(fileName)+1]; strcpy(this->skinFileName, fileName); this->material = new Material("md2ModelTest"); this->material->setDiffuseMap(fileName); this->material->setIllum(3); this->material->setAmbient(1.0, 1.0, 1.0); }