/* =========================================================================== Doom 3 BFG Edition GPL Source Code Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). Doom 3 BFG Edition Source Code 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 3 of the License, or (at your option) any later version. Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Doom 3 BFG Edition Source Code. If not, see . In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below. If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. =========================================================================== */ #pragma hdrstop #include "precompiled.h" #include "tr_local.h" #include "Model_local.h" #include "Model_md3.h" /*********************************************************************** idMD3Mesh ***********************************************************************/ #define LL(x) x=LittleLong(x) /* ================= idRenderModelMD3::InitFromFile ================= */ void idRenderModelMD3::InitFromFile( const char* fileName ) { int i, j; md3Header_t* pinmodel; md3Frame_t* frame; md3Surface_t* surf; md3Shader_t* shader; md3Triangle_t* tri; md3St_t* st; md3XyzNormal_t* xyz; md3Tag_t* tag; void* buffer; int version; int size; name = fileName; size = fileSystem->ReadFile( fileName, &buffer, NULL ); if( !size || size < 0 ) { return; } pinmodel = ( md3Header_t* )buffer; version = LittleLong( pinmodel->version ); if( version != MD3_VERSION ) { fileSystem->FreeFile( buffer ); common->Warning( "InitFromFile: %s has wrong version (%i should be %i)", fileName, version, MD3_VERSION ); return; } size = LittleLong( pinmodel->ofsEnd ); dataSize += size; md3 = ( md3Header_t* )Mem_Alloc( size, TAG_MODEL ); memcpy( md3, buffer, LittleLong( pinmodel->ofsEnd ) ); LL( md3->ident ); LL( md3->version ); LL( md3->numFrames ); LL( md3->numTags ); LL( md3->numSurfaces ); LL( md3->ofsFrames ); LL( md3->ofsTags ); LL( md3->ofsSurfaces ); LL( md3->ofsEnd ); if( md3->numFrames < 1 ) { common->Warning( "InitFromFile: %s has no frames", fileName ); fileSystem->FreeFile( buffer ); return; } // swap all the frames frame = ( md3Frame_t* )( ( byte* )md3 + md3->ofsFrames ); for( i = 0 ; i < md3->numFrames ; i++, frame++ ) { frame->radius = LittleFloat( frame->radius ); for( j = 0 ; j < 3 ; j++ ) { frame->bounds[0][j] = LittleFloat( frame->bounds[0][j] ); frame->bounds[1][j] = LittleFloat( frame->bounds[1][j] ); frame->localOrigin[j] = LittleFloat( frame->localOrigin[j] ); } } // swap all the tags tag = ( md3Tag_t* )( ( byte* )md3 + md3->ofsTags ); for( i = 0 ; i < md3->numTags * md3->numFrames ; i++, tag++ ) { for( j = 0 ; j < 3 ; j++ ) { tag->origin[j] = LittleFloat( tag->origin[j] ); tag->axis[0][j] = LittleFloat( tag->axis[0][j] ); tag->axis[1][j] = LittleFloat( tag->axis[1][j] ); tag->axis[2][j] = LittleFloat( tag->axis[2][j] ); } } // swap all the surfaces surf = ( md3Surface_t* )( ( byte* )md3 + md3->ofsSurfaces ); for( i = 0 ; i < md3->numSurfaces ; i++ ) { LL( surf->ident ); LL( surf->flags ); LL( surf->numFrames ); LL( surf->numShaders ); LL( surf->numTriangles ); LL( surf->ofsTriangles ); LL( surf->numVerts ); LL( surf->ofsShaders ); LL( surf->ofsSt ); LL( surf->ofsXyzNormals ); LL( surf->ofsEnd ); if( surf->numVerts > SHADER_MAX_VERTEXES ) { common->Error( "InitFromFile: %s has more than %i verts on a surface (%i)", fileName, SHADER_MAX_VERTEXES, surf->numVerts ); } if( surf->numTriangles * 3 > SHADER_MAX_INDEXES ) { common->Error( "InitFromFile: %s has more than %i triangles on a surface (%i)", fileName, SHADER_MAX_INDEXES / 3, surf->numTriangles ); } // change to surface identifier surf->ident = 0; //SF_MD3; // lowercase the surface name so skin compares are faster int slen = ( int )strlen( surf->name ); for( j = 0; j < slen; j++ ) { surf->name[j] = tolower( surf->name[j] ); } // strip off a trailing _1 or _2 // this is a crutch for q3data being a mess j = strlen( surf->name ); if( j > 2 && surf->name[j - 2] == '_' ) { surf->name[j - 2] = 0; } // register the shaders shader = ( md3Shader_t* )( ( byte* )surf + surf->ofsShaders ); for( j = 0 ; j < surf->numShaders ; j++, shader++ ) { const idMaterial* sh; sh = declManager->FindMaterial( shader->name ); shader->shader = sh; } // swap all the triangles tri = ( md3Triangle_t* )( ( byte* )surf + surf->ofsTriangles ); for( j = 0 ; j < surf->numTriangles ; j++, tri++ ) { LL( tri->indexes[0] ); LL( tri->indexes[1] ); LL( tri->indexes[2] ); } // swap all the ST st = ( md3St_t* )( ( byte* )surf + surf->ofsSt ); for( j = 0 ; j < surf->numVerts ; j++, st++ ) { st->st[0] = LittleFloat( st->st[0] ); st->st[1] = LittleFloat( st->st[1] ); } // swap all the XyzNormals xyz = ( md3XyzNormal_t* )( ( byte* )surf + surf->ofsXyzNormals ); for( j = 0 ; j < surf->numVerts * surf->numFrames ; j++, xyz++ ) { xyz->xyz[0] = LittleShort( xyz->xyz[0] ); xyz->xyz[1] = LittleShort( xyz->xyz[1] ); xyz->xyz[2] = LittleShort( xyz->xyz[2] ); xyz->normal = LittleShort( xyz->normal ); } // find the next surface surf = ( md3Surface_t* )( ( byte* )surf + surf->ofsEnd ); } fileSystem->FreeFile( buffer ); } /* ================= idRenderModelMD3::IsDynamicModel ================= */ dynamicModel_t idRenderModelMD3::IsDynamicModel() const { return DM_CACHED; } /* ================= idRenderModelMD3::LerpMeshVertexes ================= */ void idRenderModelMD3::LerpMeshVertexes( srfTriangles_t* tri, const struct md3Surface_s* surf, const float backlerp, const int frame, const int oldframe ) const { short* oldXyz, *newXyz; float oldXyzScale, newXyzScale; int vertNum; int numVerts; newXyz = ( short* )( ( byte* )surf + surf->ofsXyzNormals ) + ( frame * surf->numVerts * 4 ); newXyzScale = MD3_XYZ_SCALE * ( 1.0 - backlerp ); numVerts = surf->numVerts; if( backlerp == 0 ) { // // just copy the vertexes // for( vertNum = 0 ; vertNum < numVerts ; vertNum++, newXyz += 4 ) { idDrawVert* outvert = &tri->verts[tri->numVerts]; outvert->xyz.x = newXyz[0] * newXyzScale; outvert->xyz.y = newXyz[1] * newXyzScale; outvert->xyz.z = newXyz[2] * newXyzScale; tri->numVerts++; } } else { // // interpolate and copy the vertexes // oldXyz = ( short* )( ( byte* )surf + surf->ofsXyzNormals ) + ( oldframe * surf->numVerts * 4 ); oldXyzScale = MD3_XYZ_SCALE * backlerp; for( vertNum = 0 ; vertNum < numVerts ; vertNum++, oldXyz += 4, newXyz += 4 ) { idDrawVert* outvert = &tri->verts[tri->numVerts]; // interpolate the xyz outvert->xyz.x = oldXyz[0] * oldXyzScale + newXyz[0] * newXyzScale; outvert->xyz.y = oldXyz[1] * oldXyzScale + newXyz[1] * newXyzScale; outvert->xyz.z = oldXyz[2] * oldXyzScale + newXyz[2] * newXyzScale; tri->numVerts++; } } } /* ============= idRenderModelMD3::InstantiateDynamicModel ============= */ idRenderModel* idRenderModelMD3::InstantiateDynamicModel( const struct renderEntity_s* ent, const viewDef_t* view, idRenderModel* cachedModel ) { int i, j; float backlerp; int* triangles; int indexes; int numVerts; md3Surface_t* surface; int frame, oldframe; idRenderModelStatic* staticModel; if( cachedModel ) { delete cachedModel; cachedModel = NULL; } staticModel = new( TAG_MODEL ) idRenderModelStatic; staticModel->bounds.Clear(); surface = ( md3Surface_t* )( ( byte* )md3 + md3->ofsSurfaces ); // TODO: these need set by an entity frame = ent->shaderParms[SHADERPARM_MD3_FRAME]; // probably want to keep frames < 1000 or so oldframe = ent->shaderParms[SHADERPARM_MD3_LASTFRAME]; backlerp = ent->shaderParms[SHADERPARM_MD3_BACKLERP]; for( i = 0; i < md3->numSurfaces; i++ ) { srfTriangles_t* tri = R_AllocStaticTriSurf(); R_AllocStaticTriSurfVerts( tri, surface->numVerts ); R_AllocStaticTriSurfIndexes( tri, surface->numTriangles * 3 ); tri->bounds.Clear(); modelSurface_t surf; surf.geometry = tri; md3Shader_t* shaders = ( md3Shader_t* )( ( byte* )surface + surface->ofsShaders ); surf.shader = shaders->shader; LerpMeshVertexes( tri, surface, backlerp, frame, oldframe ); triangles = ( int* )( ( byte* )surface + surface->ofsTriangles ); indexes = surface->numTriangles * 3; for( j = 0 ; j < indexes ; j++ ) { tri->indexes[j] = triangles[j]; } tri->numIndexes += indexes; const idVec2* texCoords = ( idVec2* )( ( byte* )surface + surface->ofsSt ); numVerts = surface->numVerts; for( j = 0; j < numVerts; j++ ) { tri->verts[j].SetTexCoord( texCoords[j] ); } R_BoundTriSurf( tri ); staticModel->AddSurface( surf ); staticModel->bounds.AddPoint( surf.geometry->bounds[0] ); staticModel->bounds.AddPoint( surf.geometry->bounds[1] ); // find the next surface surface = ( md3Surface_t* )( ( byte* )surface + surface->ofsEnd ); } return staticModel; } /* ===================== idRenderModelMD3::Bounds ===================== */ idBounds idRenderModelMD3::Bounds( const struct renderEntity_s* ent ) const { idBounds ret; ret.Clear(); if( !ent || !md3 ) { // just give it the editor bounds ret.AddPoint( idVec3( -10, -10, -10 ) ); ret.AddPoint( idVec3( 10, 10, 10 ) ); return ret; } md3Frame_t* frame = ( md3Frame_t* )( ( byte* )md3 + md3->ofsFrames ); ret.AddPoint( frame->bounds[0] ); ret.AddPoint( frame->bounds[1] ); return ret; }