1 /*
2 ===========================================================================
3
4 Doom 3 GPL Source Code
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
6
7 This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code").
8
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.
13
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.
18
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/>.
21
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.
23
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.
25
26 ===========================================================================
27 */
28
29 #include "sys/platform.h"
30 #include "framework/Session.h"
31 #include "renderer/tr_local.h"
32
33 #include "renderer/Model_local.h"
34
35 static const char *MD5_SnapshotName = "_MD5_Snapshot_";
36
37 /***********************************************************************
38
39 idMD5Mesh
40
41 ***********************************************************************/
42
43 static int c_numVerts = 0;
44 static int c_numWeights = 0;
45 static int c_numWeightJoints = 0;
46
47 typedef struct vertexWeight_s {
48 int vert;
49 int joint;
50 idVec3 offset;
51 float jointWeight;
52 } vertexWeight_t;
53
54 /*
55 ====================
56 idMD5Mesh::idMD5Mesh
57 ====================
58 */
idMD5Mesh()59 idMD5Mesh::idMD5Mesh() {
60 scaledWeights = NULL;
61 weightIndex = NULL;
62 shader = NULL;
63 numTris = 0;
64 deformInfo = NULL;
65 surfaceNum = 0;
66 }
67
68 /*
69 ====================
70 idMD5Mesh::~idMD5Mesh
71 ====================
72 */
~idMD5Mesh()73 idMD5Mesh::~idMD5Mesh() {
74 Mem_Free16( scaledWeights );
75 Mem_Free16( weightIndex );
76 if ( deformInfo ) {
77 R_FreeDeformInfo( deformInfo );
78 deformInfo = NULL;
79 }
80 }
81
82 /*
83 ====================
84 idMD5Mesh::ParseMesh
85 ====================
86 */
ParseMesh(idLexer & parser,int numJoints,const idJointMat * joints)87 void idMD5Mesh::ParseMesh( idLexer &parser, int numJoints, const idJointMat *joints ) {
88 idToken token;
89 idToken name;
90 int num;
91 int count;
92 int jointnum;
93 idStr shaderName;
94 int i, j;
95 idList<int> tris;
96 idList<int> firstWeightForVertex;
97 idList<int> numWeightsForVertex;
98 int maxweight;
99 idList<vertexWeight_t> tempWeights;
100
101 parser.ExpectTokenString( "{" );
102
103 //
104 // parse name
105 //
106 if ( parser.CheckTokenString( "name" ) ) {
107 parser.ReadToken( &name );
108 }
109
110 //
111 // parse shader
112 //
113 parser.ExpectTokenString( "shader" );
114
115 parser.ReadToken( &token );
116 shaderName = token;
117
118 shader = declManager->FindMaterial( shaderName );
119
120 //
121 // parse texture coordinates
122 //
123 parser.ExpectTokenString( "numverts" );
124 count = parser.ParseInt();
125 if ( count < 0 ) {
126 parser.Error( "Invalid size: %s", token.c_str() );
127 }
128
129 texCoords.SetNum( count );
130 firstWeightForVertex.SetNum( count );
131 numWeightsForVertex.SetNum( count );
132
133 numWeights = 0;
134 maxweight = 0;
135 for( i = 0; i < texCoords.Num(); i++ ) {
136 parser.ExpectTokenString( "vert" );
137 parser.ParseInt();
138
139 parser.Parse1DMatrix( 2, texCoords[ i ].ToFloatPtr() );
140
141 firstWeightForVertex[ i ] = parser.ParseInt();
142 numWeightsForVertex[ i ] = parser.ParseInt();
143
144 if ( !numWeightsForVertex[ i ] ) {
145 parser.Error( "Vertex without any joint weights." );
146 }
147
148 numWeights += numWeightsForVertex[ i ];
149 if ( numWeightsForVertex[ i ] + firstWeightForVertex[ i ] > maxweight ) {
150 maxweight = numWeightsForVertex[ i ] + firstWeightForVertex[ i ];
151 }
152 }
153
154 //
155 // parse tris
156 //
157 parser.ExpectTokenString( "numtris" );
158 count = parser.ParseInt();
159 if ( count < 0 ) {
160 parser.Error( "Invalid size: %d", count );
161 }
162
163 tris.SetNum( count * 3 );
164 numTris = count;
165 for( i = 0; i < count; i++ ) {
166 parser.ExpectTokenString( "tri" );
167 parser.ParseInt();
168
169 tris[ i * 3 + 0 ] = parser.ParseInt();
170 tris[ i * 3 + 1 ] = parser.ParseInt();
171 tris[ i * 3 + 2 ] = parser.ParseInt();
172 }
173
174 //
175 // parse weights
176 //
177 parser.ExpectTokenString( "numweights" );
178 count = parser.ParseInt();
179 if ( count < 0 ) {
180 parser.Error( "Invalid size: %d", count );
181 }
182
183 if ( maxweight > count ) {
184 parser.Warning( "Vertices reference out of range weights in model (%d of %d weights).", maxweight, count );
185 }
186
187 tempWeights.SetNum( count );
188
189 for( i = 0; i < count; i++ ) {
190 parser.ExpectTokenString( "weight" );
191 parser.ParseInt();
192
193 jointnum = parser.ParseInt();
194 if ( ( jointnum < 0 ) || ( jointnum >= numJoints ) ) {
195 parser.Error( "Joint Index out of range(%d): %d", numJoints, jointnum );
196 }
197
198 tempWeights[ i ].joint = jointnum;
199 tempWeights[ i ].jointWeight = parser.ParseFloat();
200
201 parser.Parse1DMatrix( 3, tempWeights[ i ].offset.ToFloatPtr() );
202 }
203
204 // create pre-scaled weights and an index for the vertex/joint lookup
205 scaledWeights = (idVec4 *) Mem_Alloc16( numWeights * sizeof( scaledWeights[0] ) );
206 weightIndex = (int *) Mem_Alloc16( numWeights * 2 * sizeof( weightIndex[0] ) );
207 memset( weightIndex, 0, numWeights * 2 * sizeof( weightIndex[0] ) );
208
209 count = 0;
210 for( i = 0; i < texCoords.Num(); i++ ) {
211 num = firstWeightForVertex[i];
212 for( j = 0; j < numWeightsForVertex[i]; j++, num++, count++ ) {
213 scaledWeights[count].ToVec3() = tempWeights[num].offset * tempWeights[num].jointWeight;
214 scaledWeights[count].w = tempWeights[num].jointWeight;
215 weightIndex[count * 2 + 0] = tempWeights[num].joint * sizeof( idJointMat );
216 }
217 weightIndex[count * 2 - 1] = 1;
218 }
219
220 tempWeights.Clear();
221 numWeightsForVertex.Clear();
222 firstWeightForVertex.Clear();
223
224 parser.ExpectTokenString( "}" );
225
226 // update counters
227 c_numVerts += texCoords.Num();
228 c_numWeights += numWeights;
229 c_numWeightJoints++;
230 for ( i = 0; i < numWeights; i++ ) {
231 c_numWeightJoints += weightIndex[i*2+1];
232 }
233
234 //
235 // build the information that will be common to all animations of this mesh:
236 // silhouette edge connectivity and normal / tangent generation information
237 //
238 idDrawVert *verts = (idDrawVert *) _alloca16( texCoords.Num() * sizeof( idDrawVert ) );
239 for ( i = 0; i < texCoords.Num(); i++ ) {
240 verts[i].Clear();
241 verts[i].st = texCoords[i];
242 }
243 TransformVerts( verts, joints );
244 deformInfo = R_BuildDeformInfo( texCoords.Num(), verts, tris.Num(), tris.Ptr(), shader->UseUnsmoothedTangents() );
245 }
246
247 /*
248 ====================
249 idMD5Mesh::TransformVerts
250 ====================
251 */
TransformVerts(idDrawVert * verts,const idJointMat * entJoints)252 void idMD5Mesh::TransformVerts( idDrawVert *verts, const idJointMat *entJoints ) {
253 SIMDProcessor->TransformVerts( verts, texCoords.Num(), entJoints, scaledWeights, weightIndex, numWeights );
254 }
255
256 /*
257 ====================
258 idMD5Mesh::TransformScaledVerts
259
260 Special transform to make the mesh seem fat or skinny. May be used for zombie deaths
261 ====================
262 */
TransformScaledVerts(idDrawVert * verts,const idJointMat * entJoints,float scale)263 void idMD5Mesh::TransformScaledVerts( idDrawVert *verts, const idJointMat *entJoints, float scale ) {
264 idVec4 *scaledWeights = (idVec4 *) _alloca16( numWeights * sizeof( scaledWeights[0] ) );
265 SIMDProcessor->Mul( scaledWeights[0].ToFloatPtr(), scale, scaledWeights[0].ToFloatPtr(), numWeights * 4 );
266 SIMDProcessor->TransformVerts( verts, texCoords.Num(), entJoints, scaledWeights, weightIndex, numWeights );
267 }
268
269 /*
270 ====================
271 idMD5Mesh::UpdateSurface
272 ====================
273 */
UpdateSurface(const struct renderEntity_s * ent,const idJointMat * entJoints,modelSurface_t * surf)274 void idMD5Mesh::UpdateSurface( const struct renderEntity_s *ent, const idJointMat *entJoints, modelSurface_t *surf ) {
275 int i, base;
276 srfTriangles_t *tri;
277
278 tr.pc.c_deformedSurfaces++;
279 tr.pc.c_deformedVerts += deformInfo->numOutputVerts;
280 tr.pc.c_deformedIndexes += deformInfo->numIndexes;
281
282 surf->shader = shader;
283
284 if ( surf->geometry ) {
285 // if the number of verts and indexes are the same we can re-use the triangle surface
286 // the number of indexes must be the same to assure the correct amount of memory is allocated for the facePlanes
287 if ( surf->geometry->numVerts == deformInfo->numOutputVerts && surf->geometry->numIndexes == deformInfo->numIndexes ) {
288 R_FreeStaticTriSurfVertexCaches( surf->geometry );
289 } else {
290 R_FreeStaticTriSurf( surf->geometry );
291 surf->geometry = R_AllocStaticTriSurf();
292 }
293 } else {
294 surf->geometry = R_AllocStaticTriSurf();
295 }
296
297 tri = surf->geometry;
298
299 // note that some of the data is references, and should not be freed
300 tri->deformedSurface = true;
301 tri->tangentsCalculated = false;
302 tri->facePlanesCalculated = false;
303
304 tri->numIndexes = deformInfo->numIndexes;
305 tri->indexes = deformInfo->indexes;
306 tri->silIndexes = deformInfo->silIndexes;
307 tri->numMirroredVerts = deformInfo->numMirroredVerts;
308 tri->mirroredVerts = deformInfo->mirroredVerts;
309 tri->numDupVerts = deformInfo->numDupVerts;
310 tri->dupVerts = deformInfo->dupVerts;
311 tri->numSilEdges = deformInfo->numSilEdges;
312 tri->silEdges = deformInfo->silEdges;
313 tri->dominantTris = deformInfo->dominantTris;
314 tri->numVerts = deformInfo->numOutputVerts;
315
316 if ( tri->verts == NULL ) {
317 R_AllocStaticTriSurfVerts( tri, tri->numVerts );
318 for ( i = 0; i < deformInfo->numSourceVerts; i++ ) {
319 tri->verts[i].Clear();
320 tri->verts[i].st = texCoords[i];
321 }
322 }
323
324 if ( ent->shaderParms[ SHADERPARM_MD5_SKINSCALE ] != 0.0f ) {
325 TransformScaledVerts( tri->verts, entJoints, ent->shaderParms[ SHADERPARM_MD5_SKINSCALE ] );
326 } else {
327 TransformVerts( tri->verts, entJoints );
328 }
329
330 // replicate the mirror seam vertexes
331 base = deformInfo->numOutputVerts - deformInfo->numMirroredVerts;
332 for ( i = 0; i < deformInfo->numMirroredVerts; i++ ) {
333 tri->verts[base + i] = tri->verts[deformInfo->mirroredVerts[i]];
334 }
335
336 R_BoundTriSurf( tri );
337
338 // If a surface is going to be have a lighting interaction generated, it will also have to call
339 // R_DeriveTangents() to get normals, tangents, and face planes. If it only
340 // needs shadows generated, it will only have to generate face planes. If it only
341 // has ambient drawing, or is culled, no additional work will be necessary
342 if ( !r_useDeferredTangents.GetBool() ) {
343 // set face planes, vertex normals, tangents
344 R_DeriveTangents( tri );
345 }
346 }
347
348 /*
349 ====================
350 idMD5Mesh::CalcBounds
351 ====================
352 */
CalcBounds(const idJointMat * entJoints)353 idBounds idMD5Mesh::CalcBounds( const idJointMat *entJoints ) {
354 idBounds bounds;
355 idDrawVert *verts = (idDrawVert *) _alloca16( texCoords.Num() * sizeof( idDrawVert ) );
356
357 TransformVerts( verts, entJoints );
358
359 SIMDProcessor->MinMax( bounds[0], bounds[1], verts, texCoords.Num() );
360
361 return bounds;
362 }
363
364 /*
365 ====================
366 idMD5Mesh::NearestJoint
367 ====================
368 */
NearestJoint(int a,int b,int c) const369 int idMD5Mesh::NearestJoint( int a, int b, int c ) const {
370 int i, bestJoint, vertNum, weightVertNum;
371 float bestWeight;
372
373 // duplicated vertices might not have weights
374 if ( a >= 0 && a < texCoords.Num() ) {
375 vertNum = a;
376 } else if ( b >= 0 && b < texCoords.Num() ) {
377 vertNum = b;
378 } else if ( c >= 0 && c < texCoords.Num() ) {
379 vertNum = c;
380 } else {
381 // all vertices are duplicates which shouldn't happen
382 return 0;
383 }
384
385 // find the first weight for this vertex
386 weightVertNum = 0;
387 for( i = 0; weightVertNum < vertNum; i++ ) {
388 weightVertNum += weightIndex[i*2+1];
389 }
390
391 // get the joint for the largest weight
392 bestWeight = scaledWeights[i].w;
393 bestJoint = weightIndex[i*2+0] / sizeof( idJointMat );
394 for( ; weightIndex[i*2+1] == 0; i++ ) {
395 if ( scaledWeights[i].w > bestWeight ) {
396 bestWeight = scaledWeights[i].w;
397 bestJoint = weightIndex[i*2+0] / sizeof( idJointMat );
398 }
399 }
400 return bestJoint;
401 }
402
403 /*
404 ====================
405 idMD5Mesh::NumVerts
406 ====================
407 */
NumVerts(void) const408 int idMD5Mesh::NumVerts( void ) const {
409 return texCoords.Num();
410 }
411
412 /*
413 ====================
414 idMD5Mesh::NumTris
415 ====================
416 */
NumTris(void) const417 int idMD5Mesh::NumTris( void ) const {
418 return numTris;
419 }
420
421 /*
422 ====================
423 idMD5Mesh::NumWeights
424 ====================
425 */
NumWeights(void) const426 int idMD5Mesh::NumWeights( void ) const {
427 return numWeights;
428 }
429
430 /***********************************************************************
431
432 idRenderModelMD5
433
434 ***********************************************************************/
435
436 /*
437 ====================
438 idRenderModelMD5::ParseJoint
439 ====================
440 */
ParseJoint(idLexer & parser,idMD5Joint * joint,idJointQuat * defaultPose)441 void idRenderModelMD5::ParseJoint( idLexer &parser, idMD5Joint *joint, idJointQuat *defaultPose ) {
442 idToken token;
443 int num;
444
445 //
446 // parse name
447 //
448 parser.ReadToken( &token );
449 joint->name = token;
450
451 //
452 // parse parent
453 //
454 num = parser.ParseInt();
455 if ( num < 0 ) {
456 joint->parent = NULL;
457 } else {
458 if ( num >= joints.Num() - 1 ) {
459 parser.Error( "Invalid parent for joint '%s'", joint->name.c_str() );
460 }
461 joint->parent = &joints[ num ];
462 }
463
464 //
465 // parse default pose
466 //
467 parser.Parse1DMatrix( 3, defaultPose->t.ToFloatPtr() );
468 parser.Parse1DMatrix( 3, defaultPose->q.ToFloatPtr() );
469 defaultPose->q.w = defaultPose->q.CalcW();
470 }
471
472 /*
473 ====================
474 idRenderModelMD5::InitFromFile
475 ====================
476 */
InitFromFile(const char * fileName)477 void idRenderModelMD5::InitFromFile( const char *fileName ) {
478 name = fileName;
479 LoadModel();
480 }
481
482 /*
483 ====================
484 idRenderModelMD5::LoadModel
485
486 used for initial loads, reloadModel, and reloading the data of purged models
487 Upon exit, the model will absolutely be valid, but possibly as a default model
488 ====================
489 */
LoadModel()490 void idRenderModelMD5::LoadModel() {
491 int version;
492 int i;
493 int num;
494 int parentNum;
495 idToken token;
496 idLexer parser( LEXFL_ALLOWPATHNAMES | LEXFL_NOSTRINGESCAPECHARS );
497 idJointQuat *pose;
498 idMD5Joint *joint;
499 idJointMat *poseMat3;
500
501 if ( !purged ) {
502 PurgeModel();
503 }
504 purged = false;
505
506 if ( !parser.LoadFile( name ) ) {
507 MakeDefaultModel();
508 return;
509 }
510
511 parser.ExpectTokenString( MD5_VERSION_STRING );
512 version = parser.ParseInt();
513
514 if ( version != MD5_VERSION ) {
515 parser.Error( "Invalid version %d. Should be version %d\n", version, MD5_VERSION );
516 }
517
518 //
519 // skip commandline
520 //
521 parser.ExpectTokenString( "commandline" );
522 parser.ReadToken( &token );
523
524 // parse num joints
525 parser.ExpectTokenString( "numJoints" );
526 num = parser.ParseInt();
527 joints.SetGranularity( 1 );
528 joints.SetNum( num );
529 defaultPose.SetGranularity( 1 );
530 defaultPose.SetNum( num );
531 poseMat3 = ( idJointMat * )_alloca16( num * sizeof( *poseMat3 ) );
532
533 // parse num meshes
534 parser.ExpectTokenString( "numMeshes" );
535 num = parser.ParseInt();
536 if ( num < 0 ) {
537 parser.Error( "Invalid size: %d", num );
538 }
539 meshes.SetGranularity( 1 );
540 meshes.SetNum( num );
541
542 //
543 // parse joints
544 //
545 parser.ExpectTokenString( "joints" );
546 parser.ExpectTokenString( "{" );
547 pose = defaultPose.Ptr();
548 joint = joints.Ptr();
549 for( i = 0; i < joints.Num(); i++, joint++, pose++ ) {
550 ParseJoint( parser, joint, pose );
551 poseMat3[ i ].SetRotation( pose->q.ToMat3() );
552 poseMat3[ i ].SetTranslation( pose->t );
553 if ( joint->parent ) {
554 parentNum = joint->parent - joints.Ptr();
555 pose->q = ( poseMat3[ i ].ToMat3() * poseMat3[ parentNum ].ToMat3().Transpose() ).ToQuat();
556 pose->t = ( poseMat3[ i ].ToVec3() - poseMat3[ parentNum ].ToVec3() ) * poseMat3[ parentNum ].ToMat3().Transpose();
557 }
558 }
559 parser.ExpectTokenString( "}" );
560
561 for( i = 0; i < meshes.Num(); i++ ) {
562 parser.ExpectTokenString( "mesh" );
563 meshes[ i ].ParseMesh( parser, defaultPose.Num(), poseMat3 );
564 }
565
566 //
567 // calculate the bounds of the model
568 //
569 CalculateBounds( poseMat3 );
570
571 // set the timestamp for reloadmodels
572 fileSystem->ReadFile( name, NULL, &timeStamp );
573 }
574
575 /*
576 ==============
577 idRenderModelMD5::Print
578 ==============
579 */
Print() const580 void idRenderModelMD5::Print() const {
581 const idMD5Mesh *mesh;
582 int i;
583
584 common->Printf( "%s\n", name.c_str() );
585 common->Printf( "Dynamic model.\n" );
586 common->Printf( "Generated smooth normals.\n" );
587 common->Printf( " verts tris weights material\n" );
588 int totalVerts = 0;
589 int totalTris = 0;
590 int totalWeights = 0;
591 for( mesh = meshes.Ptr(), i = 0; i < meshes.Num(); i++, mesh++ ) {
592 totalVerts += mesh->NumVerts();
593 totalTris += mesh->NumTris();
594 totalWeights += mesh->NumWeights();
595 common->Printf( "%2i: %5i %5i %7i %s\n", i, mesh->NumVerts(), mesh->NumTris(), mesh->NumWeights(), mesh->shader->GetName() );
596 }
597 common->Printf( "-----\n" );
598 common->Printf( "%4i verts.\n", totalVerts );
599 common->Printf( "%4i tris.\n", totalTris );
600 common->Printf( "%4i weights.\n", totalWeights );
601 common->Printf( "%4i joints.\n", joints.Num() );
602 }
603
604 /*
605 ==============
606 idRenderModelMD5::List
607 ==============
608 */
List() const609 void idRenderModelMD5::List() const {
610 int i;
611 const idMD5Mesh *mesh;
612 int totalTris = 0;
613 int totalVerts = 0;
614
615 for( mesh = meshes.Ptr(), i = 0; i < meshes.Num(); i++, mesh++ ) {
616 totalTris += mesh->numTris;
617 totalVerts += mesh->NumVerts();
618 }
619 common->Printf( " %4ik %3i %4i %4i %s(MD5)", Memory()/1024, meshes.Num(), totalVerts, totalTris, Name() );
620
621 if ( defaulted ) {
622 common->Printf( " (DEFAULTED)" );
623 }
624
625 common->Printf( "\n" );
626 }
627
628 /*
629 ====================
630 idRenderModelMD5::CalculateBounds
631 ====================
632 */
CalculateBounds(const idJointMat * entJoints)633 void idRenderModelMD5::CalculateBounds( const idJointMat *entJoints ) {
634 int i;
635 idMD5Mesh *mesh;
636
637 bounds.Clear();
638 for( mesh = meshes.Ptr(), i = 0; i < meshes.Num(); i++, mesh++ ) {
639 bounds.AddBounds( mesh->CalcBounds( entJoints ) );
640 }
641 }
642
643 /*
644 ====================
645 idRenderModelMD5::Bounds
646
647 This calculates a rough bounds by using the joint radii without
648 transforming all the points
649 ====================
650 */
Bounds(const renderEntity_t * ent) const651 idBounds idRenderModelMD5::Bounds( const renderEntity_t *ent ) const {
652 #if 0
653 // we can't calculate a rational bounds without an entity,
654 // because joints could be positioned to deform it into an
655 // arbitrarily large shape
656 if ( !ent ) {
657 common->Error( "idRenderModelMD5::Bounds: called without entity" );
658 }
659 #endif
660
661 if ( !ent ) {
662 // this is the bounds for the reference pose
663 return bounds;
664 }
665
666 return ent->bounds;
667 }
668
669 /*
670 ====================
671 idRenderModelMD5::DrawJoints
672 ====================
673 */
DrawJoints(const renderEntity_t * ent,const struct viewDef_s * view) const674 void idRenderModelMD5::DrawJoints( const renderEntity_t *ent, const struct viewDef_s *view ) const {
675 int i;
676 int num;
677 idVec3 pos;
678 const idJointMat *joint;
679 const idMD5Joint *md5Joint;
680 int parentNum;
681
682 num = ent->numJoints;
683 joint = ent->joints;
684 md5Joint = joints.Ptr();
685 for( i = 0; i < num; i++, joint++, md5Joint++ ) {
686 pos = ent->origin + joint->ToVec3() * ent->axis;
687 if ( md5Joint->parent ) {
688 parentNum = md5Joint->parent - joints.Ptr();
689 session->rw->DebugLine( colorWhite, ent->origin + ent->joints[ parentNum ].ToVec3() * ent->axis, pos );
690 }
691
692 session->rw->DebugLine( colorRed, pos, pos + joint->ToMat3()[ 0 ] * 2.0f * ent->axis );
693 session->rw->DebugLine( colorGreen, pos, pos + joint->ToMat3()[ 1 ] * 2.0f * ent->axis );
694 session->rw->DebugLine( colorBlue, pos, pos + joint->ToMat3()[ 2 ] * 2.0f * ent->axis );
695 }
696
697 idBounds bounds;
698
699 bounds.FromTransformedBounds( ent->bounds, vec3_zero, ent->axis );
700 session->rw->DebugBounds( colorMagenta, bounds, ent->origin );
701
702 if ( ( r_jointNameScale.GetFloat() != 0.0f ) && ( bounds.Expand( 128.0f ).ContainsPoint( view->renderView.vieworg - ent->origin ) ) ) {
703 idVec3 offset( 0, 0, r_jointNameOffset.GetFloat() );
704 float scale;
705
706 scale = r_jointNameScale.GetFloat();
707 joint = ent->joints;
708 num = ent->numJoints;
709 for( i = 0; i < num; i++, joint++ ) {
710 pos = ent->origin + joint->ToVec3() * ent->axis;
711 session->rw->DrawText( joints[ i ].name, pos + offset, scale, colorWhite, view->renderView.viewaxis, 1 );
712 }
713 }
714 }
715
716 /*
717 ====================
718 idRenderModelMD5::InstantiateDynamicModel
719 ====================
720 */
InstantiateDynamicModel(const struct renderEntity_s * ent,const struct viewDef_s * view,idRenderModel * cachedModel)721 idRenderModel *idRenderModelMD5::InstantiateDynamicModel( const struct renderEntity_s *ent, const struct viewDef_s *view, idRenderModel *cachedModel ) {
722 int i, surfaceNum;
723 idMD5Mesh *mesh;
724 idRenderModelStatic *staticModel;
725
726 if ( cachedModel && !r_useCachedDynamicModels.GetBool() ) {
727 delete cachedModel;
728 cachedModel = NULL;
729 }
730
731 if ( purged ) {
732 common->DWarning( "model %s instantiated while purged", Name() );
733 LoadModel();
734 }
735
736 if ( !ent->joints ) {
737 common->Printf( "idRenderModelMD5::InstantiateDynamicModel: NULL joints on renderEntity for '%s'\n", Name() );
738 delete cachedModel;
739 return NULL;
740 } else if ( ent->numJoints != joints.Num() ) {
741 common->Printf( "idRenderModelMD5::InstantiateDynamicModel: renderEntity has different number of joints than model for '%s'\n", Name() );
742 delete cachedModel;
743 return NULL;
744 }
745
746 tr.pc.c_generateMd5++;
747
748 if ( cachedModel ) {
749 assert( dynamic_cast<idRenderModelStatic *>(cachedModel) != NULL );
750 assert( idStr::Icmp( cachedModel->Name(), MD5_SnapshotName ) == 0 );
751 staticModel = static_cast<idRenderModelStatic *>(cachedModel);
752 } else {
753 staticModel = new idRenderModelStatic;
754 staticModel->InitEmpty( MD5_SnapshotName );
755 }
756
757 staticModel->bounds.Clear();
758
759 if ( r_showSkel.GetInteger() ) {
760 if ( ( view != NULL ) && ( !r_skipSuppress.GetBool() || !ent->suppressSurfaceInViewID || ( ent->suppressSurfaceInViewID != view->renderView.viewID ) ) ) {
761 // only draw the skeleton
762 DrawJoints( ent, view );
763 }
764
765 if ( r_showSkel.GetInteger() > 1 ) {
766 // turn off the model when showing the skeleton
767 staticModel->InitEmpty( MD5_SnapshotName );
768 return staticModel;
769 }
770 }
771
772 // create all the surfaces
773 for( mesh = meshes.Ptr(), i = 0; i < meshes.Num(); i++, mesh++ ) {
774 // avoid deforming the surface if it will be a nodraw due to a skin remapping
775 // FIXME: may have to still deform clipping hulls
776 const idMaterial *shader = mesh->shader;
777
778 shader = R_RemapShaderBySkin( shader, ent->customSkin, ent->customShader );
779
780 if ( !shader || ( !shader->IsDrawn() && !shader->SurfaceCastsShadow() ) ) {
781 staticModel->DeleteSurfaceWithId( i );
782 mesh->surfaceNum = -1;
783 continue;
784 }
785
786 modelSurface_t *surf;
787
788 if ( staticModel->FindSurfaceWithId( i, surfaceNum ) ) {
789 mesh->surfaceNum = surfaceNum;
790 surf = &staticModel->surfaces[surfaceNum];
791 } else {
792
793 // Remove Overlays before adding new surfaces
794 idRenderModelOverlay::RemoveOverlaySurfacesFromModel( staticModel );
795
796 mesh->surfaceNum = staticModel->NumSurfaces();
797 surf = &staticModel->surfaces.Alloc();
798 surf->geometry = NULL;
799 surf->shader = NULL;
800 surf->id = i;
801 }
802
803 mesh->UpdateSurface( ent, ent->joints, surf );
804
805 staticModel->bounds.AddPoint( surf->geometry->bounds[0] );
806 staticModel->bounds.AddPoint( surf->geometry->bounds[1] );
807 }
808
809 return staticModel;
810 }
811
812 /*
813 ====================
814 idRenderModelMD5::IsDynamicModel
815 ====================
816 */
IsDynamicModel() const817 dynamicModel_t idRenderModelMD5::IsDynamicModel() const {
818 return DM_CACHED;
819 }
820
821 /*
822 ====================
823 idRenderModelMD5::NumJoints
824 ====================
825 */
NumJoints(void) const826 int idRenderModelMD5::NumJoints( void ) const {
827 return joints.Num();
828 }
829
830 /*
831 ====================
832 idRenderModelMD5::GetJoints
833 ====================
834 */
GetJoints(void) const835 const idMD5Joint *idRenderModelMD5::GetJoints( void ) const {
836 return joints.Ptr();
837 }
838
839 /*
840 ====================
841 idRenderModelMD5::GetDefaultPose
842 ====================
843 */
GetDefaultPose(void) const844 const idJointQuat *idRenderModelMD5::GetDefaultPose( void ) const {
845 return defaultPose.Ptr();
846 }
847
848 /*
849 ====================
850 idRenderModelMD5::GetJointHandle
851 ====================
852 */
GetJointHandle(const char * name) const853 jointHandle_t idRenderModelMD5::GetJointHandle( const char *name ) const {
854 const idMD5Joint *joint;
855 int i;
856
857 joint = joints.Ptr();
858 for( i = 0; i < joints.Num(); i++, joint++ ) {
859 if ( idStr::Icmp( joint->name.c_str(), name ) == 0 ) {
860 return ( jointHandle_t )i;
861 }
862 }
863
864 return INVALID_JOINT;
865 }
866
867 /*
868 =====================
869 idRenderModelMD5::GetJointName
870 =====================
871 */
GetJointName(jointHandle_t handle) const872 const char *idRenderModelMD5::GetJointName( jointHandle_t handle ) const {
873 if ( ( handle < 0 ) || ( handle >= joints.Num() ) ) {
874 return "<invalid joint>";
875 }
876
877 return joints[ handle ].name;
878 }
879
880 /*
881 ====================
882 idRenderModelMD5::NearestJoint
883 ====================
884 */
NearestJoint(int surfaceNum,int a,int b,int c) const885 int idRenderModelMD5::NearestJoint( int surfaceNum, int a, int b, int c ) const {
886 int i;
887 const idMD5Mesh *mesh;
888
889 if ( surfaceNum > meshes.Num() ) {
890 common->Error( "idRenderModelMD5::NearestJoint: surfaceNum > meshes.Num()" );
891 }
892
893 for ( mesh = meshes.Ptr(), i = 0; i < meshes.Num(); i++, mesh++ ) {
894 if ( mesh->surfaceNum == surfaceNum ) {
895 return mesh->NearestJoint( a, b, c );
896 }
897 }
898 return 0;
899 }
900
901 /*
902 ====================
903 idRenderModelMD5::TouchData
904
905 models that are already loaded at level start time
906 will still touch their materials to make sure they
907 are kept loaded
908 ====================
909 */
TouchData()910 void idRenderModelMD5::TouchData() {
911 idMD5Mesh *mesh;
912 int i;
913
914 for( mesh = meshes.Ptr(), i = 0; i < meshes.Num(); i++, mesh++ ) {
915 declManager->FindMaterial( mesh->shader->GetName() );
916 }
917 }
918
919 /*
920 ===================
921 idRenderModelMD5::PurgeModel
922
923 frees all the data, but leaves the class around for dangling references,
924 which can regenerate the data with LoadModel()
925 ===================
926 */
PurgeModel()927 void idRenderModelMD5::PurgeModel() {
928 purged = true;
929 joints.Clear();
930 defaultPose.Clear();
931 meshes.Clear();
932 }
933
934 /*
935 ===================
936 idRenderModelMD5::Memory
937 ===================
938 */
Memory() const939 int idRenderModelMD5::Memory() const {
940 int total, i;
941
942 total = sizeof( *this );
943 total += joints.MemoryUsed() + defaultPose.MemoryUsed() + meshes.MemoryUsed();
944
945 // count up strings
946 for ( i = 0; i < joints.Num(); i++ ) {
947 total += joints[i].name.DynamicMemoryUsed();
948 }
949
950 // count up meshes
951 for ( i = 0 ; i < meshes.Num() ; i++ ) {
952 const idMD5Mesh *mesh = &meshes[i];
953
954 total += mesh->texCoords.MemoryUsed() + mesh->numWeights * ( sizeof( mesh->scaledWeights[0] ) + sizeof( mesh->weightIndex[0] ) * 2 );
955
956 // sum up deform info
957 total += sizeof( mesh->deformInfo );
958 total += R_DeformInfoMemoryUsed( mesh->deformInfo );
959 }
960 return total;
961 }
962