/* 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 "md3_model.h" #include "md3_data.h" #include "md3_mesh.h" #include "md3_tag.h" #include "md3_bone_frame.h" #include "md3_animation_cfg.h" #include "material.h" #include "quaternion.h" #include "loading/resource_manager.h" #include "debug.h" namespace md3 { /** * md3 model */ MD3Model::MD3Model(std::string filename, float scaling) { this->autoAssemblePlayerModel(filename, scaling); this->bDrawBones = false; this->bDrawNormals = false; } MD3Model::~MD3Model() { // delete this->tmpBoneFrame; // delete [] this->tmpMesh; ///TODO deleting mesh ///TODO deleting matrices } /** * auto assemples a player model * @param filename is the name to the directory of the modelzzzzzz */ void MD3Model::autoAssemblePlayerModel(std::string filename, float scaling) { // loading the config file std::string cfgName(filename + "/animation.cfg"); this->config = (MD3AnimationCfg*)ResourceManager::getInstance()->load(cfgName, MD3_CONFIG, RP_GAME); //first load the torso or the upper part std::string nameUpper(filename + "/upper.md3"); if( (this->md3Data = (MD3Data*)ResourceManager::getInstance()->load(nameUpper, MD3, RP_GAME, nameUpper, scaling)) == NULL) { std::string nameTorso(filename + "/torso.md3"); this->md3Data = (MD3Data*)ResourceManager::getInstance()->load(nameTorso, MD3, RP_GAME, nameTorso, scaling); } if( this->md3Data == NULL) { PRINTF(1)("Problems loading the MD3Model. Abording\n"); return; } // load lower std::string nameLower(filename + "/lower.md3"); MD3Data* lower = (MD3Data*)ResourceManager::getInstance()->load(nameLower, MD3, RP_GAME, nameLower, scaling); if( lower != NULL) { int tag = this->md3Data->getTagIndexByName("tag_lower"); PRINTF(0)("Loaded the %s model on index %i\n", nameLower.c_str(), tag); if( tag >= 0) this->md3Data->addLinkedModel(tag, lower); else PRINTF(0)("Could not add %s\n", nameLower.c_str()); } // load head std::string nameHead(filename + "/head.md3"); MD3Data* head = (MD3Data*)ResourceManager::getInstance()->load(nameHead, MD3, RP_GAME, nameLower, scaling); if( head != NULL) { int tag = this->md3Data->getTagIndexByName("tag_head"); PRINTF(0)("Loaded the %s model on index %i\n", nameHead.c_str(), tag); if( tag >= 0) this->md3Data->addLinkedModel(tag, head); else PRINTF(0)("Could not add %s\n", nameHead.c_str()); } } /** * tick float * @param time: time elapsed */ void MD3Model::tick(float time) { this->tick(time, this->md3Data); } /** * tick each data */ void MD3Model::tick(float time, MD3Data* data) { // draw the bones if needed if( this->bDrawBones) { // get bone frame, interpolate if necessary if( data->animationState.interpolationFraction != 0.0 && data->animationState.currentFrame != data->animationState.nextFrame) { //interpolate bone frame data->tmpBoneFrame = this->interpolateBoneFrame(data, data->boneFrames[data->animationState.currentFrame], data->boneFrames[data->animationState.nextFrame], data->animationState.interpolationFraction); } else { data->tmpBoneFrame = data->boneFrames[data->animationState.currentFrame]; } } //draw all meshes of current frame of this model for( int i = 0; i < data->header->meshNum; i++) { MD3Mesh* mesh = data->meshes[i]; // get mesh frame, do interpolation if necessary sVec3D* frame; if( data->animationState.interpolationFraction != 0.0 && data->animationState.currentFrame != data->animationState.nextFrame) { //interpolate mesh frame between the 2 current mesh frames frame = this->interpolateMeshFrame( data, data->meshes[data->animationState.currentFrame]->meshFrames, data->meshes[data->animationState.nextFrame]->meshFrames, data->animationState.interpolationFraction, mesh, i); } else { //no interpolation needed, just draw current frame frame = &mesh->meshFrames[data->animationState.currentFrame]; } data->tmpMesh[i] = frame; // draw vertex normals if needed if( this->bDrawNormals) { // get vertex normals, interpolate if necessary if( data->animationState.interpolationFraction != 0.0 && data->animationState.currentFrame != data->animationState.nextFrame) { //interpolate vertex normals this->interpolateVertexNormals(data, &mesh->normals[data->animationState.currentFrame], &mesh->normals[data->animationState.nextFrame], data->animationState.interpolationFraction, mesh, i); } } } // draw all models linked to this model std::map::iterator it = data->sortedMap.begin(); int i = 0; while( it != data->sortedMap.end()) { MD3Data* child = it->second; //build transformation array m from matrix, interpolate if necessary MD3Tag* currFrameTag = data->boneFrames[data->animationState.currentFrame]->tags[child->parentTagIndex]; if( data->animationState.interpolationFraction != 0.0 && data->animationState.currentFrame != data->animationState.nextFrame) { //we need to interpolate MD3Tag* nextFrameTag = data->boneFrames[data->animationState.nextFrame]->tags[child->parentTagIndex]; this->interpolateTransformation(child, currFrameTag, nextFrameTag, data->animationState.interpolationFraction, i); } else { //no interpolation needed, stay with last transformation //OpenGL matrix is in column-major order data->tmpMatrix[i][0] = currFrameTag->matrix[0][0]; data->tmpMatrix[i][1] = currFrameTag->matrix[1][0]; data->tmpMatrix[i][2] = currFrameTag->matrix[2][0]; data->tmpMatrix[i][3] = 0.0f; data->tmpMatrix[i][4] = currFrameTag->matrix[0][1]; data->tmpMatrix[i][5] = currFrameTag->matrix[1][1]; data->tmpMatrix[i][6] = currFrameTag->matrix[2][1]; data->tmpMatrix[i][7] = 0.0f; data->tmpMatrix[i][8] = currFrameTag->matrix[0][2]; data->tmpMatrix[i][9] = currFrameTag->matrix[1][2]; data->tmpMatrix[i][10]= currFrameTag->matrix[2][2]; data->tmpMatrix[i][11]= 0.0f; data->tmpMatrix[i][12] = currFrameTag->position.x; data->tmpMatrix[i][13] = currFrameTag->position.y; data->tmpMatrix[i][14] = currFrameTag->position.z; data->tmpMatrix[i][15] = 1.0f; } // switch to child coord system // and tick child this->tick(time, child); i++; it++; } } /** * this draws the md3 model */ void MD3Model::draw() const { PRINTF(0)("\ndraw========================\n"); //draw current bone frame this->draw(this->md3Data); } /** * draw the md3model * @param data: the data to be drawn */ void MD3Model::draw(MD3Data* data) const { // draw the bones if needed if( this->bDrawBones) { // get bone frame, interpolate if necessary if( data->animationState.interpolationFraction != 0.0 && data->animationState.currentFrame != data->animationState.nextFrame) { //interpolate bone frame this->drawBoneFrame(data->tmpBoneFrame); } else { //stick with current bone frame this->drawBoneFrame(data->boneFrames[data->animationState.currentFrame]); } } //draw all meshes of current frame of this model for( int i = 0; i < data->header->meshNum; i++) { MD3Mesh* mesh = data->meshes[i]; if( mesh->header->textureNum > 0 && &mesh->material[0] != NULL) mesh->material[0].select(); // get mesh frame, do interpolation if necessary sVec3D* frame = data->tmpMesh[i]; this->drawMesh(mesh, frame); // draw vertex normals if needed if( this->bDrawNormals) { // get vertex normals, interpolate if necessary if( data->animationState.interpolationFraction != 0.0 && data->animationState.currentFrame != data->animationState.nextFrame) { //interpolate vertex normals this->drawVertexNormals(frame, data->tmpNormal[i]); } else { //stick with current vertex normals this->drawVertexNormals(frame, &mesh->normals[data->animationState.currentFrame]); } } } // draw all models linked to this model int i = 0; std::map::iterator it = data->sortedMap.begin(); while( it != data->sortedMap.end()) { MD3Data* child = it->second; //switch to child coord system glPushMatrix(); glMultMatrixf(data->tmpMatrix[i]); // and draw child this->draw(child); glPopMatrix(); i++; it++; } } /** * draws the mesh */ void MD3Model::drawMesh(MD3Mesh* mesh, sVec3D* frame) const { PRINTF(0)("drawMesh: %s\n", mesh->header->name); Vector tmpVec1, tmpVec2; glColor3f(1.0f, 1.0f, 1.0f); glBegin( GL_TRIANGLES); // upload all triangles in the frame to OpenGL for( int t = 0; t < mesh->header->triangleNum; t++) { // calc normal vector tmpVec1.x = frame[mesh->triangles[t].vertexOffset[1]][0] - frame[mesh->triangles[t].vertexOffset[0]][0]; tmpVec1.y = frame[mesh->triangles[t].vertexOffset[1]][1] - frame[mesh->triangles[t].vertexOffset[0]][1]; tmpVec1.z = frame[mesh->triangles[t].vertexOffset[1]][2] - frame[mesh->triangles[t].vertexOffset[0]][2]; tmpVec2.x = frame[mesh->triangles[t].vertexOffset[2]][0] - frame[mesh->triangles[t].vertexOffset[0]][0]; tmpVec2.y = frame[mesh->triangles[t].vertexOffset[2]][1] - frame[mesh->triangles[t].vertexOffset[0]][1]; tmpVec2.z = frame[mesh->triangles[t].vertexOffset[2]][2] - frame[mesh->triangles[t].vertexOffset[0]][2]; Vector normal = tmpVec1.cross(tmpVec2); normal.normalize(); // PRINTF(0)("normal: %f, %f, %f\n", normal.x, normal.y, normal.z); glNormal3f(normal.x, normal.y, normal.z); glTexCoord2fv( mesh->texVecs[mesh->triangles[t].vertexOffset[0]].textureCoord); glVertex3f( frame[mesh->triangles[t].vertexOffset[0]][0], frame[mesh->triangles[t].vertexOffset[0]][2], frame[mesh->triangles[t].vertexOffset[0]][1]); glNormal3f(normal.x, normal.y, normal.z); glTexCoord2fv( mesh->texVecs[mesh->triangles[t].vertexOffset[1]].textureCoord); glVertex3f( frame[mesh->triangles[t].vertexOffset[1]][0], frame[mesh->triangles[t].vertexOffset[1]][2], frame[mesh->triangles[t].vertexOffset[1]][1]); glNormal3f(normal.x, normal.y, normal.z); glTexCoord2fv( mesh->texVecs[mesh->triangles[t].vertexOffset[2]].textureCoord); glVertex3f( frame[mesh->triangles[t].vertexOffset[2]][0], frame[mesh->triangles[t].vertexOffset[2]][2], frame[mesh->triangles[t].vertexOffset[2]][1]); } glEnd(); } /** * drawo vertex normals */ void MD3Model::drawVertexNormals(sVec3D* frame, MD3Normal* normals) const {} /** * draw bone frame */ void MD3Model::drawBoneFrame(MD3BoneFrame* frame) const { float x1 = frame->mins.x; float y1 = frame->mins.y; float z1 = frame->mins.z; float x2 = frame->maxs.x; float y2 = frame->maxs.y; float z2 = frame->maxs.z; glPushAttrib(GL_TEXTURE_2D); glPushAttrib(GL_LIGHTING); glColor3f(1.0f,0.0f,0.0f); glPointSize(6.0f); glBegin(GL_POINTS); glVertex3f(frame->position.x, frame->position.y, frame->position.z); glEnd(); glPointSize(1.0f); glColor3f(0.0f,1.0f,0.0f); glBegin(GL_LINE_LOOP); glVertex3f(x1,y1,z1); glVertex3f(x1,y1,z2); glVertex3f(x1,y2,z2); glVertex3f(x1,y2,z1); glEnd(); glBegin(GL_LINE_LOOP); glVertex3f(x2,y2,z2); glVertex3f(x2,y1,z2); glVertex3f(x2,y1,z1); glVertex3f(x2,y2,z1); glEnd(); glBegin(GL_LINES); glVertex3f(x1,y1,z1); glVertex3f(x2,y1,z1); glVertex3f(x1,y1,z2); glVertex3f(x2,y1,z2); glVertex3f(x1,y2,z2); glVertex3f(x2,y2,z2); glVertex3f(x1,y2,z1); glVertex3f(x2,y2,z1); glEnd(); glPopAttrib(); glPopAttrib(); } /** * interpolate bone frame * @param currBoneFrame Start bone frame. * @param nextBoneFrame End bone frame. * @param frac Interpolation fraction, in [0,1]. */ MD3BoneFrame* MD3Model::interpolateBoneFrame(MD3Data* data, MD3BoneFrame* currBoneFrame, MD3BoneFrame* nextBoneFrame, float frac) { data->tmpBoneFrame->mins.x = (1.0f - frac) * currBoneFrame->mins.x + frac * nextBoneFrame->mins.x; data->tmpBoneFrame->maxs.x = (1.0f - frac) * currBoneFrame->maxs.x + frac * nextBoneFrame->maxs.x; data->tmpBoneFrame->position.x = (1.0f - frac) * currBoneFrame->position.x + frac * nextBoneFrame->position.x; data->tmpBoneFrame->mins.y = (1.0f - frac) * currBoneFrame->mins.y + frac * nextBoneFrame->mins.y; data->tmpBoneFrame->maxs.y = (1.0f - frac) * currBoneFrame->maxs.y + frac * nextBoneFrame->maxs.y; data->tmpBoneFrame->position.y = (1.0f - frac) * currBoneFrame->position.y + frac * nextBoneFrame->position.y; data->tmpBoneFrame->mins.z = (1.0f - frac) * currBoneFrame->mins.z + frac * nextBoneFrame->mins.z; data->tmpBoneFrame->maxs.z = (1.0f - frac) * currBoneFrame->maxs.z + frac * nextBoneFrame->maxs.z; data->tmpBoneFrame->position.z = (1.0f - frac) * currBoneFrame->position.z + frac * nextBoneFrame->position.z; return data->tmpBoneFrame; } /** * interpolate mesh frame */ sVec3D* MD3Model::interpolateMeshFrame(MD3Data* data, sVec3D* currMeshFrame, sVec3D* nextMeshFrame, float frac, MD3Mesh* mesh, int i) { int vertexNum = mesh->header->vertexNum; // calc interpolated vertices for( int t = 0; t < vertexNum * 3.0f; t++) { data->tmpMesh[i][t][0] = (1.0f - frac) * currMeshFrame[t][0] + frac * nextMeshFrame[t][0]; data->tmpMesh[i][t][1] = (1.0f - frac) * currMeshFrame[t][1] + frac * nextMeshFrame[t][1]; data->tmpMesh[i][t][2] = (1.0f - frac) * currMeshFrame[t][2] + frac * nextMeshFrame[t][2]; } return data->tmpMesh[i]; } /** * interpolate vertex normal */ MD3Normal* MD3Model::interpolateVertexNormals(MD3Data* data, MD3Normal* currNormals, MD3Normal* nextNormals, float frac, MD3Mesh* mesh, int i) { for( int j = 0; j < mesh->header->vertexNum; j++) { data->tmpNormal[i][j].vertexNormal[0] = (int)((1.0f - frac) * currNormals[j].vertexNormal[0] + frac * nextNormals[j].vertexNormal[0]); data->tmpNormal[i][j].vertexNormal[1] = (int)((1.0f - frac) * currNormals[j].vertexNormal[1] + frac * nextNormals[j].vertexNormal[1]); } return data->tmpNormal[i]; } /** * interpolate transformation */ float* MD3Model::interpolateTransformation(MD3Data* data, MD3Tag* currFrameTag, MD3Tag* nextFrameTag, float frac, int i) { // interpolate position Vector interpolatedPosition = currFrameTag->position * (1.0f - frac) + nextFrameTag->position * frac; // interpolate rotation matrix float currRot[4][4]; float nextRot[4][4]; float interpolatedMatrix[4][4]; Quaternion currQuat(currFrameTag->matrix); currQuat.matrix(currRot); Quaternion nextQuat(nextFrameTag->matrix); nextQuat.matrix(nextRot); Quaternion interpolatedQuat = Quaternion::quatSlerp(currQuat, nextQuat, frac); interpolatedQuat.matrix(interpolatedMatrix); // quaternion code is column based, so use transposed matrix when spitting out to gl data->tmpMatrix[i][0] = interpolatedMatrix[0][0]; data->tmpMatrix[i][4] = interpolatedMatrix[1][0]; data->tmpMatrix[i][8] = interpolatedMatrix[2][0]; data->tmpMatrix[i][12] = interpolatedPosition.x; data->tmpMatrix[i][1] = interpolatedMatrix[0][1]; data->tmpMatrix[i][5] = interpolatedMatrix[1][1]; data->tmpMatrix[i][9] = interpolatedMatrix[2][1]; data->tmpMatrix[i][13] = interpolatedPosition.y; data->tmpMatrix[i][2] = interpolatedMatrix[0][2]; data->tmpMatrix[i][6] = interpolatedMatrix[1][2]; data->tmpMatrix[i][10]= interpolatedMatrix[2][2]; data->tmpMatrix[i][14] = interpolatedPosition.z; data->tmpMatrix[i][3] = 0.0f; data->tmpMatrix[i][7] = 0.0f; data->tmpMatrix[i][11]= 0.0f; data->tmpMatrix[i][15] = 1.0f; return data->tmpMatrix[i]; } /** * Create a new visitor to apply an animation operation (NEXT, REWIND, ...) * to a MD3 model. The operation is executed in the context of the specified * animation. * * @param anim The animation that provides the context for the operation. * @param op The operation to apply. * @param interpolate Should interpolation be done? */ void MD3Model::interpolate(MD3Data* data, MD3Animation* anim, int op, bool interpolate) { // // data-> // this.anim=anim; // if( op == NEXT || op == PREVIOUS || op == REWIND) // this.op=op; // // this.interpolate=interpolate; } /** * calc next frame number */ int MD3Model::next(int nr) { #if 0 if( nr < (upperBound-1)) return nr+1; else { //rewind needed if( anim.num < 0) return anim.first; else { nr = (anim.looping != 0)?(anim.num - anim.looping):0; return anim.first + nr; } } #endif } /** * calc prev frame number */ int MD3Model::prev(int nr) { #if 0 if( nr == anim.first) return upperBound-1; else return nr-1; #endif } /** * apply the specified operation to the animation state data members of the model * taking the specified animation into account */ void MD3Model::doOp(MD3Data* data) { #if 0 //anim to be applied could have illegal data with respect to this model, //ignore anim in this case if( anim.first >= model.boneFrameNum || anim.first < 0) return; //calc upper bound for animation frames in this model if( anim.num < 0) upperBound=model.boneFrameNum; //use all available frames else upperBound=model.boneFrameNum<(anim.first+anim.num)?model.boneFrameNum:(anim.first+anim.num); switch (op) { case NEXT: if (interpolate) { model.interpolationFraction+=FRACTION; if (model.interpolationFraction>=1.0f) { model.currentFrame=model.nextFrame; model.nextFrame=next(model.nextFrame); model.interpolationFraction=0.0f; } } else { model.currentFrame=model.nextFrame; model.nextFrame=next(model.nextFrame); } break; case PREVIOUS: if (interpolate) { model.interpolationFraction-=FRACTION; if (model.interpolationFraction<0.0f) { model.nextFrame=model.currentFrame; model.currentFrame=prev(model.currentFrame); model.interpolationFraction=0.8f; } } else { model.nextFrame=model.currentFrame; model.currentFrame=prev(model.currentFrame); } break; case REWIND: model.currentFrame=anim.first; model.nextFrame=next(model.currentFrame); model.interpolationFraction=0.0f; break; } #endif } }