2 ===========================================================================
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
9 Doom 3 Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
14 Doom 3 Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
22 In addition, the Doom 3 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 Source Code. If not, please request a copy in writing from id Software at the address below.
24 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.
26 ===========================================================================
29 #include "../idlib/precompiled.h"
33 #include "Model_local.h"
35 static const char *MD5_SnapshotName = "_MD5_Snapshot_";
38 /***********************************************************************
42 ***********************************************************************/
44 static int c_numVerts = 0;
45 static int c_numWeights = 0;
46 static int c_numWeightJoints = 0;
48 typedef struct vertexWeight_s {
60 idMD5Mesh::idMD5Mesh() {
74 idMD5Mesh::~idMD5Mesh() {
75 Mem_Free16( scaledWeights );
76 Mem_Free16( weightIndex );
78 R_FreeDeformInfo( deformInfo );
88 void idMD5Mesh::ParseMesh( idLexer &parser, int numJoints, const idJointMat *joints ) {
97 idList<int> firstWeightForVertex;
98 idList<int> numWeightsForVertex;
100 idList<vertexWeight_t> tempWeights;
102 parser.ExpectTokenString( "{" );
107 if ( parser.CheckTokenString( "name" ) ) {
108 parser.ReadToken( &name );
114 parser.ExpectTokenString( "shader" );
116 parser.ReadToken( &token );
119 shader = declManager->FindMaterial( shaderName );
122 // parse texture coordinates
124 parser.ExpectTokenString( "numverts" );
125 count = parser.ParseInt();
127 parser.Error( "Invalid size: %s", token.c_str() );
130 texCoords.SetNum( count );
131 firstWeightForVertex.SetNum( count );
132 numWeightsForVertex.SetNum( count );
136 for( i = 0; i < texCoords.Num(); i++ ) {
137 parser.ExpectTokenString( "vert" );
140 parser.Parse1DMatrix( 2, texCoords[ i ].ToFloatPtr() );
142 firstWeightForVertex[ i ] = parser.ParseInt();
143 numWeightsForVertex[ i ] = parser.ParseInt();
145 if ( !numWeightsForVertex[ i ] ) {
146 parser.Error( "Vertex without any joint weights." );
149 numWeights += numWeightsForVertex[ i ];
150 if ( numWeightsForVertex[ i ] + firstWeightForVertex[ i ] > maxweight ) {
151 maxweight = numWeightsForVertex[ i ] + firstWeightForVertex[ i ];
158 parser.ExpectTokenString( "numtris" );
159 count = parser.ParseInt();
161 parser.Error( "Invalid size: %d", count );
164 tris.SetNum( count * 3 );
166 for( i = 0; i < count; i++ ) {
167 parser.ExpectTokenString( "tri" );
170 tris[ i * 3 + 0 ] = parser.ParseInt();
171 tris[ i * 3 + 1 ] = parser.ParseInt();
172 tris[ i * 3 + 2 ] = parser.ParseInt();
178 parser.ExpectTokenString( "numweights" );
179 count = parser.ParseInt();
181 parser.Error( "Invalid size: %d", count );
184 if ( maxweight > count ) {
185 parser.Warning( "Vertices reference out of range weights in model (%d of %d weights).", maxweight, count );
188 tempWeights.SetNum( count );
190 for( i = 0; i < count; i++ ) {
191 parser.ExpectTokenString( "weight" );
194 jointnum = parser.ParseInt();
195 if ( ( jointnum < 0 ) || ( jointnum >= numJoints ) ) {
196 parser.Error( "Joint Index out of range(%d): %d", numJoints, jointnum );
199 tempWeights[ i ].joint = jointnum;
200 tempWeights[ i ].jointWeight = parser.ParseFloat();
202 parser.Parse1DMatrix( 3, tempWeights[ i ].offset.ToFloatPtr() );
205 // create pre-scaled weights and an index for the vertex/joint lookup
206 scaledWeights = (idVec4 *) Mem_Alloc16( numWeights * sizeof( scaledWeights[0] ) );
207 weightIndex = (int *) Mem_Alloc16( numWeights * 2 * sizeof( weightIndex[0] ) );
208 memset( weightIndex, 0, numWeights * 2 * sizeof( weightIndex[0] ) );
211 for( i = 0; i < texCoords.Num(); i++ ) {
212 num = firstWeightForVertex[i];
213 for( j = 0; j < numWeightsForVertex[i]; j++, num++, count++ ) {
214 scaledWeights[count].ToVec3() = tempWeights[num].offset * tempWeights[num].jointWeight;
215 scaledWeights[count].w = tempWeights[num].jointWeight;
216 weightIndex[count * 2 + 0] = tempWeights[num].joint * sizeof( idJointMat );
218 weightIndex[count * 2 - 1] = 1;
222 numWeightsForVertex.Clear();
223 firstWeightForVertex.Clear();
225 parser.ExpectTokenString( "}" );
228 c_numVerts += texCoords.Num();
229 c_numWeights += numWeights;
231 for ( i = 0; i < numWeights; i++ ) {
232 c_numWeightJoints += weightIndex[i*2+1];
236 // build the information that will be common to all animations of this mesh:
237 // silhouette edge connectivity and normal / tangent generation information
239 idDrawVert *verts = (idDrawVert *) _alloca16( texCoords.Num() * sizeof( idDrawVert ) );
240 for ( i = 0; i < texCoords.Num(); i++ ) {
242 verts[i].st = texCoords[i];
244 TransformVerts( verts, joints );
245 deformInfo = R_BuildDeformInfo( texCoords.Num(), verts, tris.Num(), tris.Ptr(), shader->UseUnsmoothedTangents() );
250 idMD5Mesh::TransformVerts
253 void idMD5Mesh::TransformVerts( idDrawVert *verts, const idJointMat *entJoints ) {
254 SIMDProcessor->TransformVerts( verts, texCoords.Num(), entJoints, scaledWeights, weightIndex, numWeights );
259 idMD5Mesh::TransformScaledVerts
261 Special transform to make the mesh seem fat or skinny. May be used for zombie deaths
264 void idMD5Mesh::TransformScaledVerts( idDrawVert *verts, const idJointMat *entJoints, float scale ) {
265 idVec4 *scaledWeights = (idVec4 *) _alloca16( numWeights * sizeof( scaledWeights[0] ) );
266 SIMDProcessor->Mul( scaledWeights[0].ToFloatPtr(), scale, scaledWeights[0].ToFloatPtr(), numWeights * 4 );
267 SIMDProcessor->TransformVerts( verts, texCoords.Num(), entJoints, scaledWeights, weightIndex, numWeights );
272 idMD5Mesh::UpdateSurface
275 void idMD5Mesh::UpdateSurface( const struct renderEntity_s *ent, const idJointMat *entJoints, modelSurface_t *surf ) {
279 tr.pc.c_deformedSurfaces++;
280 tr.pc.c_deformedVerts += deformInfo->numOutputVerts;
281 tr.pc.c_deformedIndexes += deformInfo->numIndexes;
283 surf->shader = shader;
285 if ( surf->geometry ) {
286 // if the number of verts and indexes are the same we can re-use the triangle surface
287 // the number of indexes must be the same to assure the correct amount of memory is allocated for the facePlanes
288 if ( surf->geometry->numVerts == deformInfo->numOutputVerts && surf->geometry->numIndexes == deformInfo->numIndexes ) {
289 R_FreeStaticTriSurfVertexCaches( surf->geometry );
291 R_FreeStaticTriSurf( surf->geometry );
292 surf->geometry = R_AllocStaticTriSurf();
295 surf->geometry = R_AllocStaticTriSurf();
298 tri = surf->geometry;
300 // note that some of the data is references, and should not be freed
301 tri->deformedSurface = true;
302 tri->tangentsCalculated = false;
303 tri->facePlanesCalculated = false;
305 tri->numIndexes = deformInfo->numIndexes;
306 tri->indexes = deformInfo->indexes;
307 tri->silIndexes = deformInfo->silIndexes;
308 tri->numMirroredVerts = deformInfo->numMirroredVerts;
309 tri->mirroredVerts = deformInfo->mirroredVerts;
310 tri->numDupVerts = deformInfo->numDupVerts;
311 tri->dupVerts = deformInfo->dupVerts;
312 tri->numSilEdges = deformInfo->numSilEdges;
313 tri->silEdges = deformInfo->silEdges;
314 tri->dominantTris = deformInfo->dominantTris;
315 tri->numVerts = deformInfo->numOutputVerts;
317 if ( tri->verts == NULL ) {
318 R_AllocStaticTriSurfVerts( tri, tri->numVerts );
319 for ( i = 0; i < deformInfo->numSourceVerts; i++ ) {
320 tri->verts[i].Clear();
321 tri->verts[i].st = texCoords[i];
325 if ( ent->shaderParms[ SHADERPARM_MD5_SKINSCALE ] != 0.0f ) {
326 TransformScaledVerts( tri->verts, entJoints, ent->shaderParms[ SHADERPARM_MD5_SKINSCALE ] );
328 TransformVerts( tri->verts, entJoints );
331 // replicate the mirror seam vertexes
332 base = deformInfo->numOutputVerts - deformInfo->numMirroredVerts;
333 for ( i = 0; i < deformInfo->numMirroredVerts; i++ ) {
334 tri->verts[base + i] = tri->verts[deformInfo->mirroredVerts[i]];
337 R_BoundTriSurf( tri );
339 // If a surface is going to be have a lighting interaction generated, it will also have to call
340 // R_DeriveTangents() to get normals, tangents, and face planes. If it only
341 // needs shadows generated, it will only have to generate face planes. If it only
342 // has ambient drawing, or is culled, no additional work will be necessary
343 if ( !r_useDeferredTangents.GetBool() ) {
344 // set face planes, vertex normals, tangents
345 R_DeriveTangents( tri );
351 idMD5Mesh::CalcBounds
354 idBounds idMD5Mesh::CalcBounds( const idJointMat *entJoints ) {
356 idDrawVert *verts = (idDrawVert *) _alloca16( texCoords.Num() * sizeof( idDrawVert ) );
358 TransformVerts( verts, entJoints );
360 SIMDProcessor->MinMax( bounds[0], bounds[1], verts, texCoords.Num() );
367 idMD5Mesh::NearestJoint
370 int idMD5Mesh::NearestJoint( int a, int b, int c ) const {
371 int i, bestJoint, vertNum, weightVertNum;
374 // duplicated vertices might not have weights
375 if ( a >= 0 && a < texCoords.Num() ) {
377 } else if ( b >= 0 && b < texCoords.Num() ) {
379 } else if ( c >= 0 && c < texCoords.Num() ) {
382 // all vertices are duplicates which shouldn't happen
386 // find the first weight for this vertex
388 for( i = 0; weightVertNum < vertNum; i++ ) {
389 weightVertNum += weightIndex[i*2+1];
392 // get the joint for the largest weight
393 bestWeight = scaledWeights[i].w;
394 bestJoint = weightIndex[i*2+0] / sizeof( idJointMat );
395 for( ; weightIndex[i*2+1] == 0; i++ ) {
396 if ( scaledWeights[i].w > bestWeight ) {
397 bestWeight = scaledWeights[i].w;
398 bestJoint = weightIndex[i*2+0] / sizeof( idJointMat );
409 int idMD5Mesh::NumVerts( void ) const {
410 return texCoords.Num();
418 int idMD5Mesh::NumTris( void ) const {
424 idMD5Mesh::NumWeights
427 int idMD5Mesh::NumWeights( void ) const {
431 /***********************************************************************
435 ***********************************************************************/
439 idRenderModelMD5::ParseJoint
442 void idRenderModelMD5::ParseJoint( idLexer &parser, idMD5Joint *joint, idJointQuat *defaultPose ) {
449 parser.ReadToken( &token );
455 num = parser.ParseInt();
457 joint->parent = NULL;
459 if ( num >= joints.Num() - 1 ) {
460 parser.Error( "Invalid parent for joint '%s'", joint->name.c_str() );
462 joint->parent = &joints[ num ];
466 // parse default pose
468 parser.Parse1DMatrix( 3, defaultPose->t.ToFloatPtr() );
469 parser.Parse1DMatrix( 3, defaultPose->q.ToFloatPtr() );
470 defaultPose->q.w = defaultPose->q.CalcW();
475 idRenderModelMD5::InitFromFile
478 void idRenderModelMD5::InitFromFile( const char *fileName ) {
485 idRenderModelMD5::LoadModel
487 used for initial loads, reloadModel, and reloading the data of purged models
488 Upon exit, the model will absolutely be valid, but possibly as a default model
491 void idRenderModelMD5::LoadModel() {
497 idLexer parser( LEXFL_ALLOWPATHNAMES | LEXFL_NOSTRINGESCAPECHARS );
500 idJointMat *poseMat3;
507 if ( !parser.LoadFile( name ) ) {
512 parser.ExpectTokenString( MD5_VERSION_STRING );
513 version = parser.ParseInt();
515 if ( version != MD5_VERSION ) {
516 parser.Error( "Invalid version %d. Should be version %d\n", version, MD5_VERSION );
522 parser.ExpectTokenString( "commandline" );
523 parser.ReadToken( &token );
526 parser.ExpectTokenString( "numJoints" );
527 num = parser.ParseInt();
528 joints.SetGranularity( 1 );
529 joints.SetNum( num );
530 defaultPose.SetGranularity( 1 );
531 defaultPose.SetNum( num );
532 poseMat3 = ( idJointMat * )_alloca16( num * sizeof( *poseMat3 ) );
535 parser.ExpectTokenString( "numMeshes" );
536 num = parser.ParseInt();
538 parser.Error( "Invalid size: %d", num );
540 meshes.SetGranularity( 1 );
541 meshes.SetNum( num );
546 parser.ExpectTokenString( "joints" );
547 parser.ExpectTokenString( "{" );
548 pose = defaultPose.Ptr();
549 joint = joints.Ptr();
550 for( i = 0; i < joints.Num(); i++, joint++, pose++ ) {
551 ParseJoint( parser, joint, pose );
552 poseMat3[ i ].SetRotation( pose->q.ToMat3() );
553 poseMat3[ i ].SetTranslation( pose->t );
554 if ( joint->parent ) {
555 parentNum = joint->parent - joints.Ptr();
556 pose->q = ( poseMat3[ i ].ToMat3() * poseMat3[ parentNum ].ToMat3().Transpose() ).ToQuat();
557 pose->t = ( poseMat3[ i ].ToVec3() - poseMat3[ parentNum ].ToVec3() ) * poseMat3[ parentNum ].ToMat3().Transpose();
560 parser.ExpectTokenString( "}" );
562 for( i = 0; i < meshes.Num(); i++ ) {
563 parser.ExpectTokenString( "mesh" );
564 meshes[ i ].ParseMesh( parser, defaultPose.Num(), poseMat3 );
568 // calculate the bounds of the model
570 CalculateBounds( poseMat3 );
572 // set the timestamp for reloadmodels
573 fileSystem->ReadFile( name, NULL, &timeStamp );
578 idRenderModelMD5::Print
581 void idRenderModelMD5::Print() const {
582 const idMD5Mesh *mesh;
585 common->Printf( "%s\n", name.c_str() );
586 common->Printf( "Dynamic model.\n" );
587 common->Printf( "Generated smooth normals.\n" );
588 common->Printf( " verts tris weights material\n" );
591 int totalWeights = 0;
592 for( mesh = meshes.Ptr(), i = 0; i < meshes.Num(); i++, mesh++ ) {
593 totalVerts += mesh->NumVerts();
594 totalTris += mesh->NumTris();
595 totalWeights += mesh->NumWeights();
596 common->Printf( "%2i: %5i %5i %7i %s\n", i, mesh->NumVerts(), mesh->NumTris(), mesh->NumWeights(), mesh->shader->GetName() );
598 common->Printf( "-----\n" );
599 common->Printf( "%4i verts.\n", totalVerts );
600 common->Printf( "%4i tris.\n", totalTris );
601 common->Printf( "%4i weights.\n", totalWeights );
602 common->Printf( "%4i joints.\n", joints.Num() );
607 idRenderModelMD5::List
610 void idRenderModelMD5::List() const {
612 const idMD5Mesh *mesh;
616 for( mesh = meshes.Ptr(), i = 0; i < meshes.Num(); i++, mesh++ ) {
617 totalTris += mesh->numTris;
618 totalVerts += mesh->NumVerts();
620 common->Printf( " %4ik %3i %4i %4i %s(MD5)", Memory()/1024, meshes.Num(), totalVerts, totalTris, Name() );
623 common->Printf( " (DEFAULTED)" );
626 common->Printf( "\n" );
631 idRenderModelMD5::CalculateBounds
634 void idRenderModelMD5::CalculateBounds( const idJointMat *entJoints ) {
639 for( mesh = meshes.Ptr(), i = 0; i < meshes.Num(); i++, mesh++ ) {
640 bounds.AddBounds( mesh->CalcBounds( entJoints ) );
646 idRenderModelMD5::Bounds
648 This calculates a rough bounds by using the joint radii without
649 transforming all the points
652 idBounds idRenderModelMD5::Bounds( const renderEntity_t *ent ) const {
654 // we can't calculate a rational bounds without an entity,
655 // because joints could be positioned to deform it into an
656 // arbitrarily large shape
658 common->Error( "idRenderModelMD5::Bounds: called without entity" );
663 // this is the bounds for the reference pose
672 idRenderModelMD5::DrawJoints
675 void idRenderModelMD5::DrawJoints( const renderEntity_t *ent, const struct viewDef_s *view ) const {
679 const idJointMat *joint;
680 const idMD5Joint *md5Joint;
683 num = ent->numJoints;
685 md5Joint = joints.Ptr();
686 for( i = 0; i < num; i++, joint++, md5Joint++ ) {
687 pos = ent->origin + joint->ToVec3() * ent->axis;
688 if ( md5Joint->parent ) {
689 parentNum = md5Joint->parent - joints.Ptr();
690 session->rw->DebugLine( colorWhite, ent->origin + ent->joints[ parentNum ].ToVec3() * ent->axis, pos );
693 session->rw->DebugLine( colorRed, pos, pos + joint->ToMat3()[ 0 ] * 2.0f * ent->axis );
694 session->rw->DebugLine( colorGreen, pos, pos + joint->ToMat3()[ 1 ] * 2.0f * ent->axis );
695 session->rw->DebugLine( colorBlue, pos, pos + joint->ToMat3()[ 2 ] * 2.0f * ent->axis );
700 bounds.FromTransformedBounds( ent->bounds, vec3_zero, ent->axis );
701 session->rw->DebugBounds( colorMagenta, bounds, ent->origin );
703 if ( ( r_jointNameScale.GetFloat() != 0.0f ) && ( bounds.Expand( 128.0f ).ContainsPoint( view->renderView.vieworg - ent->origin ) ) ) {
704 idVec3 offset( 0, 0, r_jointNameOffset.GetFloat() );
707 scale = r_jointNameScale.GetFloat();
709 num = ent->numJoints;
710 for( i = 0; i < num; i++, joint++ ) {
711 pos = ent->origin + joint->ToVec3() * ent->axis;
712 session->rw->DrawText( joints[ i ].name, pos + offset, scale, colorWhite, view->renderView.viewaxis, 1 );
719 idRenderModelMD5::InstantiateDynamicModel
722 idRenderModel *idRenderModelMD5::InstantiateDynamicModel( const struct renderEntity_s *ent, const struct viewDef_s *view, idRenderModel *cachedModel ) {
725 idRenderModelStatic *staticModel;
727 if ( cachedModel && !r_useCachedDynamicModels.GetBool() ) {
733 common->DWarning( "model %s instantiated while purged", Name() );
737 if ( !ent->joints ) {
738 common->Printf( "idRenderModelMD5::InstantiateDynamicModel: NULL joints on renderEntity for '%s'\n", Name() );
741 } else if ( ent->numJoints != joints.Num() ) {
742 common->Printf( "idRenderModelMD5::InstantiateDynamicModel: renderEntity has different number of joints than model for '%s'\n", Name() );
747 tr.pc.c_generateMd5++;
750 assert( dynamic_cast<idRenderModelStatic *>(cachedModel) != NULL );
751 assert( idStr::Icmp( cachedModel->Name(), MD5_SnapshotName ) == 0 );
752 staticModel = static_cast<idRenderModelStatic *>(cachedModel);
754 staticModel = new idRenderModelStatic;
755 staticModel->InitEmpty( MD5_SnapshotName );
758 staticModel->bounds.Clear();
760 if ( r_showSkel.GetInteger() ) {
761 if ( ( view != NULL ) && ( !r_skipSuppress.GetBool() || !ent->suppressSurfaceInViewID || ( ent->suppressSurfaceInViewID != view->renderView.viewID ) ) ) {
762 // only draw the skeleton
763 DrawJoints( ent, view );
766 if ( r_showSkel.GetInteger() > 1 ) {
767 // turn off the model when showing the skeleton
768 staticModel->InitEmpty( MD5_SnapshotName );
773 // create all the surfaces
774 for( mesh = meshes.Ptr(), i = 0; i < meshes.Num(); i++, mesh++ ) {
775 // avoid deforming the surface if it will be a nodraw due to a skin remapping
776 // FIXME: may have to still deform clipping hulls
777 const idMaterial *shader = mesh->shader;
779 shader = R_RemapShaderBySkin( shader, ent->customSkin, ent->customShader );
781 if ( !shader || ( !shader->IsDrawn() && !shader->SurfaceCastsShadow() ) ) {
782 staticModel->DeleteSurfaceWithId( i );
783 mesh->surfaceNum = -1;
787 modelSurface_t *surf;
789 if ( staticModel->FindSurfaceWithId( i, surfaceNum ) ) {
790 mesh->surfaceNum = surfaceNum;
791 surf = &staticModel->surfaces[surfaceNum];
794 // Remove Overlays before adding new surfaces
795 idRenderModelOverlay::RemoveOverlaySurfacesFromModel( staticModel );
797 mesh->surfaceNum = staticModel->NumSurfaces();
798 surf = &staticModel->surfaces.Alloc();
799 surf->geometry = NULL;
804 mesh->UpdateSurface( ent, ent->joints, surf );
806 staticModel->bounds.AddPoint( surf->geometry->bounds[0] );
807 staticModel->bounds.AddPoint( surf->geometry->bounds[1] );
815 idRenderModelMD5::IsDynamicModel
818 dynamicModel_t idRenderModelMD5::IsDynamicModel() const {
824 idRenderModelMD5::NumJoints
827 int idRenderModelMD5::NumJoints( void ) const {
833 idRenderModelMD5::GetJoints
836 const idMD5Joint *idRenderModelMD5::GetJoints( void ) const {
842 idRenderModelMD5::GetDefaultPose
845 const idJointQuat *idRenderModelMD5::GetDefaultPose( void ) const {
846 return defaultPose.Ptr();
851 idRenderModelMD5::GetJointHandle
854 jointHandle_t idRenderModelMD5::GetJointHandle( const char *name ) const {
855 const idMD5Joint *joint;
858 joint = joints.Ptr();
859 for( i = 0; i < joints.Num(); i++, joint++ ) {
860 if ( idStr::Icmp( joint->name.c_str(), name ) == 0 ) {
861 return ( jointHandle_t )i;
865 return INVALID_JOINT;
869 =====================
870 idRenderModelMD5::GetJointName
871 =====================
873 const char *idRenderModelMD5::GetJointName( jointHandle_t handle ) const {
874 if ( ( handle < 0 ) || ( handle >= joints.Num() ) ) {
875 return "<invalid joint>";
878 return joints[ handle ].name;
883 idRenderModelMD5::NearestJoint
886 int idRenderModelMD5::NearestJoint( int surfaceNum, int a, int b, int c ) const {
888 const idMD5Mesh *mesh;
890 if ( surfaceNum > meshes.Num() ) {
891 common->Error( "idRenderModelMD5::NearestJoint: surfaceNum > meshes.Num()" );
894 for ( mesh = meshes.Ptr(), i = 0; i < meshes.Num(); i++, mesh++ ) {
895 if ( mesh->surfaceNum == surfaceNum ) {
896 return mesh->NearestJoint( a, b, c );
904 idRenderModelMD5::TouchData
906 models that are already loaded at level start time
907 will still touch their materials to make sure they
911 void idRenderModelMD5::TouchData() {
915 for( mesh = meshes.Ptr(), i = 0; i < meshes.Num(); i++, mesh++ ) {
916 declManager->FindMaterial( mesh->shader->GetName() );
922 idRenderModelMD5::PurgeModel
924 frees all the data, but leaves the class around for dangling references,
925 which can regenerate the data with LoadModel()
928 void idRenderModelMD5::PurgeModel() {
937 idRenderModelMD5::Memory
940 int idRenderModelMD5::Memory() const {
943 total = sizeof( *this );
944 total += joints.MemoryUsed() + defaultPose.MemoryUsed() + meshes.MemoryUsed();
947 for ( i = 0; i < joints.Num(); i++ ) {
948 total += joints[i].name.DynamicMemoryUsed();
952 for ( i = 0 ; i < meshes.Num() ; i++ ) {
953 const idMD5Mesh *mesh = &meshes[i];
955 total += mesh->texCoords.MemoryUsed() + mesh->numWeights * ( sizeof( mesh->scaledWeights[0] ) + sizeof( mesh->weightIndex[0] ) * 2 );
957 // sum up deform info
958 total += sizeof( mesh->deformInfo );
959 total += R_DeformInfoMemoryUsed( mesh->deformInfo );