/* =========================================================================== 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 "../idlib/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; }