1 /*
2 ===========================================================================
3 Copyright (C) 2000 - 2013, Raven Software, Inc.
4 Copyright (C) 2001 - 2013, Activision, Inc.
5 Copyright (C) 2013 - 2015, OpenJK contributors
6
7 This file is part of the OpenJK source code.
8
9 OpenJK is free software; you can redistribute it and/or modify it
10 under the terms of the GNU General Public License version 2 as
11 published by the Free Software Foundation.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, see <http://www.gnu.org/licenses/>.
20 ===========================================================================
21 */
22
23 #include "../server/exe_headers.h"
24
25 #ifndef __Q_SHARED_H
26 #include "../qcommon/q_shared.h"
27 #endif
28
29 #include "tr_common.h"
30
31 #if !defined(TR_LOCAL_H)
32 #include "tr_local.h"
33 #endif
34
35 #include "qcommon/matcomp.h"
36
37 #if !defined(G2_H_INC)
38 #include "../ghoul2/G2.h"
39 #endif
40
41 #if !defined (MINIHEAP_H_INC)
42 #include "../qcommon/MiniHeap.h"
43 #endif
44
45 #define G2_MODEL_OK(g) ((g)&&(g)->mValid&&(g)->aHeader&&(g)->currentModel&&(g)->animModel)
46
47 #include "../server/server.h"
48
49 #include <float.h>
50
51 #include "qcommon/ojk_saved_game_helper.h"
52
53 #ifdef _G2_GORE
54 #include "../ghoul2/ghoul2_gore.h"
55
56 #define GORE_TAG_UPPER (256)
57 #define GORE_TAG_MASK (~255)
58
59 static int CurrentTag=GORE_TAG_UPPER+1;
60 static int CurrentTagUpper=GORE_TAG_UPPER;
61
62 static std::map<int,GoreTextureCoordinates> GoreRecords;
63 static std::map<std::pair<int,int>,int> GoreTagsTemp; // this is a surface index to gore tag map used only
64 // temporarily during the generation phase so we reuse gore tags per LOD
65 int goreModelIndex;
66
67 static cvar_t *cg_g2MarksAllModels=NULL;
68
69 GoreTextureCoordinates *FindGoreRecord(int tag);
DestroyGoreTexCoordinates(int tag)70 static inline void DestroyGoreTexCoordinates(int tag)
71 {
72 GoreTextureCoordinates *gTC = FindGoreRecord(tag);
73 if (!gTC)
74 {
75 return;
76 }
77 (*gTC).~GoreTextureCoordinates();
78 //I don't know what's going on here, it should call the destructor for
79 //this when it erases the record but sometimes it doesn't. -rww
80 }
81
82 //TODO: This needs to be set via a scalability cvar with some reasonable minimum value if pgore is used at all
83 #define MAX_GORE_RECORDS (500)
84
AllocGoreRecord()85 int AllocGoreRecord()
86 {
87 while (GoreRecords.size()>MAX_GORE_RECORDS)
88 {
89 int tagHigh=(*GoreRecords.begin()).first&GORE_TAG_MASK;
90 std::map<int,GoreTextureCoordinates>::iterator it;
91 GoreTextureCoordinates *gTC;
92
93 it = GoreRecords.begin();
94 gTC = &(*it).second;
95
96 if (gTC)
97 {
98 gTC->~GoreTextureCoordinates();
99 }
100 GoreRecords.erase(GoreRecords.begin());
101 while (GoreRecords.size())
102 {
103 if (((*GoreRecords.begin()).first&GORE_TAG_MASK)!=tagHigh)
104 {
105 break;
106 }
107 it = GoreRecords.begin();
108 gTC = &(*it).second;
109
110 if (gTC)
111 {
112 gTC->~GoreTextureCoordinates();
113 }
114 GoreRecords.erase(GoreRecords.begin());
115 }
116 }
117 int ret=CurrentTag;
118 GoreRecords[CurrentTag]=GoreTextureCoordinates();
119 CurrentTag++;
120 return ret;
121 }
122
ResetGoreTag()123 void ResetGoreTag()
124 {
125 GoreTagsTemp.clear();
126 CurrentTag=CurrentTagUpper;
127 CurrentTagUpper+=GORE_TAG_UPPER;
128 }
129
FindGoreRecord(int tag)130 GoreTextureCoordinates *FindGoreRecord(int tag)
131 {
132 std::map<int,GoreTextureCoordinates>::iterator i=GoreRecords.find(tag);
133 if (i!=GoreRecords.end())
134 {
135 return &(*i).second;
136 }
137 return 0;
138 }
139
G2_GetGoreRecord(int tag)140 void *G2_GetGoreRecord(int tag)
141 {
142 return FindGoreRecord(tag);
143 }
144
DeleteGoreRecord(int tag)145 void DeleteGoreRecord(int tag)
146 {
147 DestroyGoreTexCoordinates(tag);
148 GoreRecords.erase(tag);
149 }
150
151 static int CurrentGoreSet=1; // this is a UUID for gore sets
152 static std::map<int,CGoreSet *> GoreSets; // map from uuid to goreset
153
FindGoreSet(int goreSetTag)154 CGoreSet *FindGoreSet(int goreSetTag)
155 {
156 std::map<int,CGoreSet *>::iterator f=GoreSets.find(goreSetTag);
157 if (f!=GoreSets.end())
158 {
159 return (*f).second;
160 }
161 return 0;
162 }
163
NewGoreSet()164 CGoreSet *NewGoreSet()
165 {
166 CGoreSet *ret=new CGoreSet(CurrentGoreSet++);
167 GoreSets[ret->mMyGoreSetTag]=ret;
168 ret->mRefCount = 1;
169 return ret;
170 }
171
DeleteGoreSet(int goreSetTag)172 void DeleteGoreSet(int goreSetTag)
173 {
174 std::map<int,CGoreSet *>::iterator f=GoreSets.find(goreSetTag);
175 if (f!=GoreSets.end())
176 {
177 if ( (*f).second->mRefCount == 0 || (*f).second->mRefCount - 1 == 0 )
178 {
179 delete (*f).second;
180 GoreSets.erase(f);
181 }
182 else
183 {
184 (*f).second->mRefCount--;
185 }
186 }
187 }
188
189
~CGoreSet()190 CGoreSet::~CGoreSet()
191 {
192 std::multimap<int,SGoreSurface>::iterator i;
193 for (i=mGoreRecords.begin();i!=mGoreRecords.end();++i)
194 {
195 DeleteGoreRecord((*i).second.mGoreTag);
196 }
197 };
198 #endif
199
200 extern mdxaBone_t worldMatrix;
201 extern mdxaBone_t worldMatrixInv;
202
203 const mdxaBone_t &EvalBoneCache(int index,CBoneCache *boneCache);
204 class CTraceSurface
205 {
206 public:
207 int surfaceNum;
208 surfaceInfo_v &rootSList;
209 const model_t *currentModel;
210 const int lod;
211 vec3_t rayStart;
212 vec3_t rayEnd;
213 CCollisionRecord *collRecMap;
214 const int entNum;
215 const int modelIndex;
216 const skin_t *skin;
217 const shader_t *cust_shader;
218 intptr_t *TransformedVertsArray;
219 const EG2_Collision eG2TraceType;
220 bool hitOne;
221 float m_fRadius;
222
223 #ifdef _G2_GORE
224 //gore application thing
225 float ssize;
226 float tsize;
227 float theta;
228 int goreShader;
229 CGhoul2Info *ghoul2info;
230
231 // Procedural-gore application things
232 SSkinGoreData *gore;
233 #endif
234
CTraceSurface(int initsurfaceNum,surfaceInfo_v & initrootSList,const model_t * initcurrentModel,int initlod,vec3_t initrayStart,vec3_t initrayEnd,CCollisionRecord * initcollRecMap,int initentNum,int initmodelIndex,const skin_t * initskin,const shader_t * initcust_shader,intptr_t * initTransformedVertsArray,const EG2_Collision einitG2TraceType,float fRadius,float initssize,float inittsize,float inittheta,int initgoreShader,CGhoul2Info * initghoul2info,SSkinGoreData * initgore)235 CTraceSurface(
236 int initsurfaceNum,
237 surfaceInfo_v &initrootSList,
238 const model_t *initcurrentModel,
239 int initlod,
240 vec3_t initrayStart,
241 vec3_t initrayEnd,
242 CCollisionRecord *initcollRecMap,
243 int initentNum,
244 int initmodelIndex,
245 const skin_t *initskin,
246 const shader_t *initcust_shader,
247 intptr_t *initTransformedVertsArray,
248 const EG2_Collision einitG2TraceType,
249 #ifdef _G2_GORE
250 float fRadius,
251 float initssize,
252 float inittsize,
253 float inittheta,
254 int initgoreShader,
255 CGhoul2Info *initghoul2info,
256 SSkinGoreData *initgore
257 #else
258 float fRadius
259 #endif
260 ):
261
262 surfaceNum(initsurfaceNum),
263 rootSList(initrootSList),
264 currentModel(initcurrentModel),
265 lod(initlod),
266 collRecMap(initcollRecMap),
267 entNum(initentNum),
268 modelIndex(initmodelIndex),
269 skin(initskin),
270 cust_shader(initcust_shader),
271 TransformedVertsArray(initTransformedVertsArray),
272 eG2TraceType(einitG2TraceType),
273 hitOne(false),
274 #ifdef _G2_GORE
275 m_fRadius(fRadius),
276 ssize(initssize),
277 tsize(inittsize),
278 theta(inittheta),
279 goreShader(initgoreShader),
280 ghoul2info(initghoul2info),
281 gore(initgore)
282 #else
283 m_fRadius(fRadius)
284 #endif
285 {
286 VectorCopy(initrayStart, rayStart);
287 VectorCopy(initrayEnd, rayEnd);
288 }
289 };
290
291 // assorted Ghoul 2 functions.
292 // list all surfaces associated with a model
G2_List_Model_Surfaces(const char * fileName)293 void G2_List_Model_Surfaces(const char *fileName)
294 {
295 int i, x;
296 model_t *mod_m = R_GetModelByHandle(RE_RegisterModel(fileName));
297 mdxmSurfHierarchy_t *surf;
298
299 surf = (mdxmSurfHierarchy_t *) ( (byte *)mod_m->mdxm + mod_m->mdxm->ofsSurfHierarchy );
300 mdxmSurface_t *surface = (mdxmSurface_t *)((byte *)mod_m->mdxm + mod_m->mdxm->ofsLODs + sizeof(mdxmLOD_t));
301
302 for ( x = 0 ; x < mod_m->mdxm->numSurfaces ; x++)
303 {
304 Com_Printf("Surface %i Name %s\n", x, surf->name);
305 if (r_verbose->value)
306 {
307 Com_Printf("Num Descendants %i\n", surf->numChildren);
308 for (i=0; i<surf->numChildren; i++)
309 {
310 Com_Printf("Descendant %i\n", surf->childIndexes[i]);
311 }
312 }
313 // find the next surface
314 surf = (mdxmSurfHierarchy_t *)( (byte *)surf + (intptr_t)( &((mdxmSurfHierarchy_t *)0)->childIndexes[ surf->numChildren ] ));
315 surface =(mdxmSurface_t *)( (byte *)surface + surface->ofsEnd );
316 }
317
318 }
319
320 // list all bones associated with a model
G2_List_Model_Bones(const char * fileName,int frame)321 void G2_List_Model_Bones(const char *fileName, int frame)
322 {
323 int x, i;
324 mdxaSkel_t *skel;
325 mdxaSkelOffsets_t *offsets;
326 model_t *mod_m = R_GetModelByHandle(RE_RegisterModel(fileName));
327 model_t *mod_a = R_GetModelByHandle(mod_m->mdxm->animIndex);
328 // mdxaFrame_t *aframe=0;
329 // int frameSize;
330 mdxaHeader_t *header = mod_a->mdxa;
331
332 // figure out where the offset list is
333 offsets = (mdxaSkelOffsets_t *)((byte *)header + sizeof(mdxaHeader_t));
334
335 // frameSize = (int)( &((mdxaFrame_t *)0)->boneIndexes[ header->numBones ] );
336
337 // aframe = (mdxaFrame_t *)((byte *)header + header->ofsFrames + (frame * frameSize));
338 // walk each bone and list it's name
339 for (x=0; x< mod_a->mdxa->numBones; x++)
340 {
341 skel = (mdxaSkel_t *)((byte *)header + sizeof(mdxaHeader_t) + offsets->offsets[x]);
342 Com_Printf("Bone %i Name %s\n", x, skel->name);
343
344 Com_Printf("X pos %f, Y pos %f, Z pos %f\n", skel->BasePoseMat.matrix[0][3], skel->BasePoseMat.matrix[1][3], skel->BasePoseMat.matrix[2][3]);
345
346 // if we are in verbose mode give us more details
347 if (r_verbose->value)
348 {
349 Com_Printf("Num Descendants %i\n", skel->numChildren);
350 for (i=0; i<skel->numChildren; i++)
351 {
352 Com_Printf("Num Descendants %i\n", skel->numChildren);
353 }
354 }
355 }
356 }
357
358
359 /************************************************************************************************
360 * G2_GetAnimFileName
361 * obtain the .gla filename for a model
362 *
363 * Input
364 * filename of model
365 *
366 * Output
367 * true if we successfully obtained a filename, false otherwise
368 *
369 ************************************************************************************************/
G2_GetAnimFileName(const char * fileName,char ** filename)370 qboolean G2_GetAnimFileName(const char *fileName, char **filename)
371 {
372 // find the model we want
373 model_t *mod = R_GetModelByHandle(RE_RegisterModel(fileName));
374
375 if (mod && mod->mdxm && (mod->mdxm->animName[0] != 0))
376 {
377 *filename = mod->mdxm->animName;
378 return qtrue;
379 }
380 return qfalse;
381 }
382
383
384 /////////////////////////////////////////////////////////////////////
385 //
386 // Code for collision detection for models gameside
387 //
388 /////////////////////////////////////////////////////////////////////
389
G2_DecideTraceLod(CGhoul2Info & ghoul2,int useLod)390 int G2_DecideTraceLod(CGhoul2Info &ghoul2, int useLod)
391 {
392 int returnLod = useLod;
393
394 // if we are overriding the LOD at top level, then we can afford to only check this level of model
395 if (ghoul2.mLodBias > returnLod)
396 {
397 returnLod = ghoul2.mLodBias;
398 }
399 assert(G2_MODEL_OK(&ghoul2));
400
401 assert(ghoul2.currentModel);
402 assert(ghoul2.currentModel->mdxm);
403 //what about r_lodBias?
404
405 // now ensure that we haven't selected a lod that doesn't exist for this model
406 if ( returnLod >= ghoul2.currentModel->mdxm->numLODs )
407 {
408 returnLod = ghoul2.currentModel->mdxm->numLODs - 1;
409 }
410
411 return returnLod;
412 }
413
R_TransformEachSurface(const mdxmSurface_t * surface,vec3_t scale,CMiniHeap * G2VertSpace,intptr_t * TransformedVertsArray,CBoneCache * boneCache)414 void R_TransformEachSurface( const mdxmSurface_t *surface, vec3_t scale, CMiniHeap *G2VertSpace, intptr_t *TransformedVertsArray,CBoneCache *boneCache)
415 {
416 int j, k;
417 mdxmVertex_t *v;
418 float *TransformedVerts;
419
420 //
421 // deform the vertexes by the lerped bones
422 //
423 int *piBoneReferences = (int*) ((byte*)surface + surface->ofsBoneReferences);
424
425 // alloc some space for the transformed verts to get put in
426 TransformedVerts = (float *)G2VertSpace->MiniHeapAlloc(surface->numVerts * 5 * 4);
427 TransformedVertsArray[surface->thisSurfaceIndex] = (intptr_t)TransformedVerts;
428 if (!TransformedVerts)
429 {
430 assert(TransformedVerts);
431 Com_Error(ERR_DROP, "Ran out of transform space for Ghoul2 Models. Adjust G2_MINIHEAP_SIZE in sv_init.cpp.\n");
432 }
433
434 // whip through and actually transform each vertex
435 const int numVerts = surface->numVerts;
436 v = (mdxmVertex_t *) ((byte *)surface + surface->ofsVerts);
437 mdxmVertexTexCoord_t *pTexCoords = (mdxmVertexTexCoord_t *) &v[numVerts];
438
439 // optimisation issue
440 if ((scale[0] != 1.0) || (scale[1] != 1.0) || (scale[2] != 1.0))
441 {
442 for ( j = 0; j < numVerts; j++ )
443 {
444 vec3_t tempVert, tempNormal;
445 // mdxmWeight_t *w;
446
447 VectorClear( tempVert );
448 VectorClear( tempNormal );
449 // w = v->weights;
450
451 const int iNumWeights = G2_GetVertWeights( v );
452
453 float fTotalWeight = 0.0f;
454 for ( k = 0 ; k < iNumWeights ; k++ )
455 {
456 int iBoneIndex = G2_GetVertBoneIndex( v, k );
457 float fBoneWeight = G2_GetVertBoneWeight( v, k, fTotalWeight, iNumWeights );
458
459 const mdxaBone_t &bone=EvalBoneCache(piBoneReferences[iBoneIndex],boneCache);
460
461 tempVert[0] += fBoneWeight * ( DotProduct( bone.matrix[0], v->vertCoords ) + bone.matrix[0][3] );
462 tempVert[1] += fBoneWeight * ( DotProduct( bone.matrix[1], v->vertCoords ) + bone.matrix[1][3] );
463 tempVert[2] += fBoneWeight * ( DotProduct( bone.matrix[2], v->vertCoords ) + bone.matrix[2][3] );
464
465 tempNormal[0] += fBoneWeight * DotProduct( bone.matrix[0], v->normal );
466 tempNormal[1] += fBoneWeight * DotProduct( bone.matrix[1], v->normal );
467 tempNormal[2] += fBoneWeight * DotProduct( bone.matrix[2], v->normal );
468 }
469 int pos = j * 5;
470
471 // copy tranformed verts into temp space
472 TransformedVerts[pos++] = tempVert[0] * scale[0];
473 TransformedVerts[pos++] = tempVert[1] * scale[1];
474 TransformedVerts[pos++] = tempVert[2] * scale[2];
475 // we will need the S & T coors too for hitlocation and hitmaterial stuff
476 TransformedVerts[pos++] = pTexCoords[j].texCoords[0];
477 TransformedVerts[pos] = pTexCoords[j].texCoords[1];
478
479 v++;// = (mdxmVertex_t *)&v->weights[/*v->numWeights*/surface->maxVertBoneWeights];
480 }
481 }
482 else
483 {
484 int pos = 0;
485 for ( j = 0; j < numVerts; j++ )
486 {
487 vec3_t tempVert, tempNormal;
488 // const mdxmWeight_t *w;
489
490 VectorClear( tempVert );
491 VectorClear( tempNormal );
492 // w = v->weights;
493
494 const int iNumWeights = G2_GetVertWeights( v );
495
496 float fTotalWeight = 0.0f;
497 for ( k = 0 ; k < iNumWeights ; k++ )
498 {
499 int iBoneIndex = G2_GetVertBoneIndex( v, k );
500 float fBoneWeight = G2_GetVertBoneWeight( v, k, fTotalWeight, iNumWeights );
501
502 const mdxaBone_t &bone=EvalBoneCache(piBoneReferences[iBoneIndex],boneCache);
503
504 tempVert[0] += fBoneWeight * ( DotProduct( bone.matrix[0], v->vertCoords ) + bone.matrix[0][3] );
505 tempVert[1] += fBoneWeight * ( DotProduct( bone.matrix[1], v->vertCoords ) + bone.matrix[1][3] );
506 tempVert[2] += fBoneWeight * ( DotProduct( bone.matrix[2], v->vertCoords ) + bone.matrix[2][3] );
507
508 tempNormal[0] += fBoneWeight * DotProduct( bone.matrix[0], v->normal );
509 tempNormal[1] += fBoneWeight * DotProduct( bone.matrix[1], v->normal );
510 tempNormal[2] += fBoneWeight * DotProduct( bone.matrix[2], v->normal );
511 }
512
513 // copy tranformed verts into temp space
514 TransformedVerts[pos++] = tempVert[0];
515 TransformedVerts[pos++] = tempVert[1];
516 TransformedVerts[pos++] = tempVert[2];
517 // we will need the S & T coors too for hitlocation and hitmaterial stuff
518 TransformedVerts[pos++] = pTexCoords[j].texCoords[0];
519 TransformedVerts[pos++] = pTexCoords[j].texCoords[1];
520
521 v++;// = (mdxmVertex_t *)&v->weights[/*v->numWeights*/surface->maxVertBoneWeights];
522 }
523 }
524 }
525
G2_TransformSurfaces(int surfaceNum,surfaceInfo_v & rootSList,CBoneCache * boneCache,const model_t * currentModel,int lod,vec3_t scale,CMiniHeap * G2VertSpace,intptr_t * TransformedVertArray,bool secondTimeAround)526 void G2_TransformSurfaces(int surfaceNum, surfaceInfo_v &rootSList,
527 CBoneCache *boneCache, const model_t *currentModel, int lod, vec3_t scale, CMiniHeap *G2VertSpace, intptr_t *TransformedVertArray, bool secondTimeAround)
528 {
529 int i;
530 assert(currentModel);
531 assert(currentModel->mdxm);
532 // back track and get the surfinfo struct for this surface
533 const mdxmSurface_t *surface = (mdxmSurface_t *)G2_FindSurface(currentModel, surfaceNum, lod);
534 const mdxmHierarchyOffsets_t *surfIndexes = (mdxmHierarchyOffsets_t *)((byte *)currentModel->mdxm + sizeof(mdxmHeader_t));
535 const mdxmSurfHierarchy_t *surfInfo = (mdxmSurfHierarchy_t *)((byte *)surfIndexes + surfIndexes->offsets[surface->thisSurfaceIndex]);
536
537 // see if we have an override surface in the surface list
538 const surfaceInfo_t *surfOverride = G2_FindOverrideSurface(surfaceNum, rootSList);
539
540 // really, we should use the default flags for this surface unless it's been overriden
541 int offFlags = surfInfo->flags;
542
543 if (surfOverride)
544 {
545 offFlags = surfOverride->offFlags;
546 }
547 // if this surface is not off, add it to the shader render list
548 if (!offFlags)
549 {
550
551 R_TransformEachSurface(surface, scale, G2VertSpace, TransformedVertArray, boneCache);
552 }
553
554 // if we are turning off all descendants, then stop this recursion now
555 if (offFlags & G2SURFACEFLAG_NODESCENDANTS)
556 {
557 return;
558 }
559
560 // now recursively call for the children
561 for (i=0; i< surfInfo->numChildren; i++)
562 {
563 G2_TransformSurfaces(surfInfo->childIndexes[i], rootSList, boneCache, currentModel, lod, scale, G2VertSpace, TransformedVertArray, secondTimeAround);
564 }
565 }
566
567 // main calling point for the model transform for collision detection. At this point all of the skeleton has been transformed.
568 #ifdef _G2_GORE
G2_TransformModel(CGhoul2Info_v & ghoul2,const int frameNum,vec3_t scale,CMiniHeap * G2VertSpace,int useLod,bool ApplyGore,SSkinGoreData * gore)569 void G2_TransformModel(CGhoul2Info_v &ghoul2, const int frameNum, vec3_t scale, CMiniHeap *G2VertSpace, int useLod, bool ApplyGore, SSkinGoreData *gore)
570 #else
571 void G2_TransformModel(CGhoul2Info_v &ghoul2, const int frameNum, vec3_t scale, CMiniHeap *G2VertSpace, int useLod)
572 #endif
573 {
574 int i, lod;
575 vec3_t correctScale;
576
577 #if !defined(JK2_MODE) || defined(_G2_GORE)
578 qboolean firstModelOnly = qfalse;
579 #endif // !JK2_MODE || _G2_GORE
580
581 #ifndef JK2_MODE
582 if ( cg_g2MarksAllModels == NULL )
583 {
584 cg_g2MarksAllModels = ri.Cvar_Get( "cg_g2MarksAllModels", "0", 0 );
585 }
586
587 if (cg_g2MarksAllModels == NULL
588 || !cg_g2MarksAllModels->integer )
589 {
590 firstModelOnly = qtrue;
591 }
592 #endif // !JK2_MODE
593
594 #ifdef _G2_GORE
595 if ( gore
596 && gore->firstModel > 0 )
597 {
598 firstModelOnly = qfalse;
599 }
600 #endif
601
602 VectorCopy(scale, correctScale);
603 // check for scales of 0 - that's the default I believe
604 if (!scale[0])
605 {
606 correctScale[0] = 1.0;
607 }
608 if (!scale[1])
609 {
610 correctScale[1] = 1.0;
611 }
612 if (!scale[2])
613 {
614 correctScale[2] = 1.0;
615 }
616
617 // walk each possible model for this entity and try rendering it out
618 for (i=0; i<ghoul2.size(); i++)
619 {
620 CGhoul2Info &g=ghoul2[i];
621 // don't bother with models that we don't care about.
622 if (!g.mValid)
623 {
624 continue;
625 }
626 assert(g.mBoneCache);
627 assert(G2_MODEL_OK(&g));
628 // stop us building this model more than once per frame
629 g.mMeshFrameNum = frameNum;
630
631 // decide the LOD
632 #ifdef _G2_GORE
633 if (ApplyGore)
634 {
635 lod=useLod;
636 assert(g.currentModel);
637 if (lod>=g.currentModel->numLods)
638 {
639 g.mTransformedVertsArray = 0;
640 if ( firstModelOnly )
641 {
642 // we don't really need to do multiple models for gore.
643 return;
644 }
645 //do the rest
646 continue;
647 }
648 }
649 else
650 #endif
651 {
652 lod = G2_DecideTraceLod(g, useLod);
653 }
654
655 // give us space for the transformed vertex array to be put in
656 g.mTransformedVertsArray = (intptr_t *)G2VertSpace->MiniHeapAlloc(g.currentModel->mdxm->numSurfaces * sizeof (intptr_t));
657 if (!g.mTransformedVertsArray)
658 {
659 Com_Error(ERR_DROP, "Ran out of transform space for Ghoul2 Models. Adjust G2_MINIHEAP_SIZE in sv_init.cpp.\n");
660 }
661
662 memset(g.mTransformedVertsArray, 0,(g.currentModel->mdxm->numSurfaces * sizeof (intptr_t)));
663
664 G2_FindOverrideSurface(-1,g.mSlist); //reset the quick surface override lookup;
665 // recursively call the model surface transform
666 G2_TransformSurfaces(g.mSurfaceRoot, g.mSlist, g.mBoneCache, g.currentModel, lod, correctScale, G2VertSpace, g.mTransformedVertsArray, false);
667
668 #ifdef _G2_GORE
669
670 if (ApplyGore && firstModelOnly )
671 {
672 // we don't really need to do multiple models for gore.
673 break;
674 }
675 #endif
676 }
677 }
678
679
680 // work out how much space a triangle takes
G2_AreaOfTri(const vec3_t A,const vec3_t B,const vec3_t C)681 static float G2_AreaOfTri(const vec3_t A, const vec3_t B, const vec3_t C)
682 {
683 vec3_t cross, ab, cb;
684 VectorSubtract(A, B, ab);
685 VectorSubtract(C, B, cb);
686
687 CrossProduct(ab, cb, cross);
688
689 return VectorLength(cross);
690 }
691
692 // actually determine the S and T of the coordinate we hit in a given poly
G2_BuildHitPointST(const vec3_t A,const float SA,const float TA,const vec3_t B,const float SB,const float TB,const vec3_t C,const float SC,const float TC,const vec3_t P,float * s,float * t,float & bary_i,float & bary_j)693 static void G2_BuildHitPointST( const vec3_t A, const float SA, const float TA,
694 const vec3_t B, const float SB, const float TB,
695 const vec3_t C, const float SC, const float TC,
696 const vec3_t P, float *s, float *t,float &bary_i,float &bary_j)
697 {
698 float areaABC = G2_AreaOfTri(A, B, C);
699
700 float i = G2_AreaOfTri(P, B, C) / areaABC;
701 bary_i=i;
702 float j = G2_AreaOfTri(A, P, C) / areaABC;
703 bary_j=j;
704 float k = G2_AreaOfTri(A, B, P) / areaABC;
705
706 *s = SA * i + SB * j + SC * k;
707 *t = TA * i + TB * j + TC * k;
708
709 *s=fmod(*s, 1);
710 if (*s< 0)
711 {
712 *s+= 1.0;
713 }
714
715 *t=fmod(*t, 1);
716 if (*t< 0)
717 {
718 *t+= 1.0;
719 }
720
721 }
722
723
724 // routine that works out given a ray whether or not it hits a poly
G2_SegmentTriangleTest(const vec3_t start,const vec3_t end,const vec3_t A,const vec3_t B,const vec3_t C,qboolean backFaces,qboolean frontFaces,vec3_t returnedPoint,vec3_t returnedNormal,float * denom)725 static inline qboolean G2_SegmentTriangleTest( const vec3_t start, const vec3_t end,
726 const vec3_t A, const vec3_t B, const vec3_t C,
727 qboolean backFaces,qboolean frontFaces,vec3_t returnedPoint,vec3_t returnedNormal, float *denom)
728 {
729 static const float tiny=1E-10f;
730 vec3_t returnedNormalT;
731 vec3_t edgeAC;
732
733 VectorSubtract(C, A, edgeAC);
734 VectorSubtract(B, A, returnedNormalT);
735
736 CrossProduct(returnedNormalT, edgeAC, returnedNormal);
737
738 vec3_t ray;
739 VectorSubtract(end, start, ray);
740
741 *denom=DotProduct(ray, returnedNormal);
742
743 if (Q_fabs(*denom)<tiny|| // triangle parallel to ray
744 (!backFaces && *denom>0)|| // not accepting back faces
745 (!frontFaces && *denom<0)) //not accepting front faces
746 {
747 return qfalse;
748 }
749
750 vec3_t toPlane;
751 VectorSubtract(A, start, toPlane);
752
753 float t=DotProduct(toPlane, returnedNormal)/ *denom;
754
755 if (t<0.0f||t>1.0f)
756 {
757 return qfalse; // off segment
758 }
759
760 VectorScale(ray, t, ray);
761
762 VectorAdd(ray, start, returnedPoint);
763
764 vec3_t edgePA;
765 VectorSubtract(A, returnedPoint, edgePA);
766
767 vec3_t edgePB;
768 VectorSubtract(B, returnedPoint, edgePB);
769
770 vec3_t edgePC;
771 VectorSubtract(C, returnedPoint, edgePC);
772
773 vec3_t temp;
774
775 CrossProduct(edgePA, edgePB, temp);
776 if (DotProduct(temp, returnedNormal)<0.0f)
777 {
778 return qfalse; // off triangle
779 }
780
781 CrossProduct(edgePC, edgePA, temp);
782 if (DotProduct(temp,returnedNormal)<0.0f)
783 {
784 return qfalse; // off triangle
785 }
786
787 CrossProduct(edgePB, edgePC, temp);
788 if (DotProduct(temp, returnedNormal)<0.0f)
789 {
790 return qfalse; // off triangle
791 }
792 return qtrue;
793 }
794
795 #ifdef _G2_GORE
796 struct SVertexTemp
797 {
798 int flags;
799 int touch;
800 int newindex;
801 float tex[2];
SVertexTempSVertexTemp802 SVertexTemp()
803 {
804 touch=0;
805 }
806 };
807
808 #define MAX_GORE_VERTS (3000)
809 static SVertexTemp GoreVerts[MAX_GORE_VERTS];
810 static int GoreIndexCopy[MAX_GORE_VERTS];
811 static int GoreTouch=1;
812
813 #define MAX_GORE_INDECIES (6000)
814 static int GoreIndecies[MAX_GORE_INDECIES];
815
816 #define GORE_MARGIN (0.0f)
817 int G2API_GetTime(int argTime);
818
819 // now we at poly level, check each model space transformed poly against the model world transfomed ray
G2_GorePolys(const mdxmSurface_t * surface,CTraceSurface & TS,const mdxmSurfHierarchy_t * surfInfo)820 static void G2_GorePolys( const mdxmSurface_t *surface, CTraceSurface &TS, const mdxmSurfHierarchy_t *surfInfo)
821 {
822 int j;
823 vec3_t basis1;
824 vec3_t basis2;
825 vec3_t taxis;
826 vec3_t saxis;
827
828 if (!TS.gore)
829 {
830 return;
831 }
832
833 if (!TS.gore->useTheta)
834 {
835 VectorCopy(TS.gore->uaxis,basis2);
836 CrossProduct(TS.rayEnd,basis2,basis1);
837 if (DotProduct(basis1,basis1)<0.005f)
838 { //shot dir and slash dir are too close
839 return;
840 }
841 }
842
843 if (TS.gore->useTheta)
844 {
845 basis2[0]=0.0f;
846 basis2[1]=0.0f;
847 basis2[2]=1.0f;
848
849 CrossProduct(TS.rayEnd,basis2,basis1);
850
851 if (DotProduct(basis1,basis1)<.1f)
852 {
853 basis2[0]=0.0f;
854 basis2[1]=1.0f;
855 basis2[2]=0.0f;
856 CrossProduct(TS.rayEnd,basis2,basis1);
857 }
858 CrossProduct(TS.rayEnd,basis1,basis2);
859 }
860
861 // Give me a shot direction not a bunch of zeros :) -Gil
862 assert(DotProduct(basis1,basis1)>.0001f);
863 assert(DotProduct(basis2,basis2)>.0001f);
864
865 VectorNormalize(basis1);
866 VectorNormalize(basis2);
867
868 float c=cos(TS.theta);
869 float s=sin(TS.theta);
870
871 VectorScale(basis1,.5f*c/TS.tsize,taxis);
872 VectorMA(taxis,.5f*s/TS.tsize,basis2,taxis);
873
874 VectorScale(basis1,-.5f*s/TS.ssize,saxis);
875 VectorMA(saxis,.5f*c/TS.ssize,basis2,saxis);
876
877 //fixme, everything above here should be pre-calculated in G2API_AddSkinGore
878 float *verts = (float *)TS.TransformedVertsArray[surface->thisSurfaceIndex];
879 int numVerts = surface->numVerts;
880 int flags=63;
881 assert(numVerts<MAX_GORE_VERTS);
882 for ( j = 0; j < numVerts; j++ )
883 {
884 int pos=j*5;
885 vec3_t delta;
886 delta[0]=verts[pos+0]-TS.rayStart[0];
887 delta[1]=verts[pos+1]-TS.rayStart[1];
888 delta[2]=verts[pos+2]-TS.rayStart[2];
889 float s=DotProduct(delta,saxis)+0.5f;
890 float t=DotProduct(delta,taxis)+0.5f;
891 float depth = DotProduct(delta,TS.rayEnd);
892 int vflags=0;
893 if (s>GORE_MARGIN)
894 {
895 vflags|=1;
896 }
897 if (s<1.0f-GORE_MARGIN)
898 {
899 vflags|=2;
900 }
901 if (t>GORE_MARGIN)
902 {
903 vflags|=4;
904 }
905 if (t<1.0f-GORE_MARGIN)
906 {
907 vflags|=8;
908 }
909 if (depth > TS.gore->depthStart)
910 {
911 vflags|=16;
912 }
913 if (depth < TS.gore->depthEnd)
914 {
915 vflags|=32;
916 }
917 vflags=(~vflags);
918 flags&=vflags;
919 GoreVerts[j].flags=vflags;
920 GoreVerts[j].tex[0]=s;
921 GoreVerts[j].tex[1]=t;
922 }
923 if (flags)
924 {
925 return; // completely off the gore splotch.
926 }
927 int numTris,newNumTris,newNumVerts;
928 numTris = surface->numTriangles;
929 mdxmTriangle_t *tris;
930 tris = (mdxmTriangle_t *) ((byte *)surface + surface->ofsTriangles);
931 verts = (float *)TS.TransformedVertsArray[surface->thisSurfaceIndex];
932 newNumTris=0;
933 newNumVerts=0;
934 GoreTouch++;
935 for ( j = 0; j < numTris; j++ )
936 {
937 assert(tris[j].indexes[0]>=0&&tris[j].indexes[0]<numVerts);
938 assert(tris[j].indexes[1]>=0&&tris[j].indexes[1]<numVerts);
939 assert(tris[j].indexes[2]>=0&&tris[j].indexes[2]<numVerts);
940 flags=63&
941 GoreVerts[tris[j].indexes[0]].flags&
942 GoreVerts[tris[j].indexes[1]].flags&
943 GoreVerts[tris[j].indexes[2]].flags;
944 if (flags)
945 {
946 continue;
947 }
948 if (!TS.gore->frontFaces || !TS.gore->backFaces)
949 {
950 // we need to back/front face cull
951 vec3_t e1,e2,n;
952
953 VectorSubtract(&verts[tris[j].indexes[1]*5],&verts[tris[j].indexes[0]*5],e1);
954 VectorSubtract(&verts[tris[j].indexes[2]*5],&verts[tris[j].indexes[0]*5],e2);
955 CrossProduct(e1,e2,n);
956 if (DotProduct(TS.rayEnd,n)>0.0f)
957 {
958 if (!TS.gore->frontFaces)
959 {
960 continue;
961 }
962 }
963 else
964 {
965 if (!TS.gore->backFaces)
966 {
967 continue;
968 }
969 }
970
971 }
972
973 int k;
974
975 assert(newNumTris*3+3<MAX_GORE_INDECIES);
976 for (k=0;k<3;k++)
977 {
978 if (GoreVerts[tris[j].indexes[k]].touch==GoreTouch)
979 {
980 GoreIndecies[newNumTris*3+k]=GoreVerts[tris[j].indexes[k]].newindex;
981 }
982 else
983 {
984 GoreVerts[tris[j].indexes[k]].touch=GoreTouch;
985 GoreVerts[tris[j].indexes[k]].newindex=newNumVerts;
986 GoreIndecies[newNumTris*3+k]=newNumVerts;
987 GoreIndexCopy[newNumVerts]=tris[j].indexes[k];
988 newNumVerts++;
989 }
990 }
991 newNumTris++;
992 }
993 if (!newNumVerts)
994 {
995 return;
996 }
997
998 int newTag;
999 std::map<std::pair<int,int>,int>::iterator f=GoreTagsTemp.find(std::make_pair(goreModelIndex,TS.surfaceNum));
1000 if (f==GoreTagsTemp.end()) // need to generate a record
1001 {
1002 newTag=AllocGoreRecord();
1003 CGoreSet *goreSet=0;
1004 if (TS.ghoul2info->mGoreSetTag)
1005 {
1006 goreSet=FindGoreSet(TS.ghoul2info->mGoreSetTag);
1007 }
1008 if (!goreSet)
1009 {
1010 goreSet=NewGoreSet();
1011 TS.ghoul2info->mGoreSetTag=goreSet->mMyGoreSetTag;
1012 }
1013 assert(goreSet);
1014 SGoreSurface add;
1015 add.shader=TS.goreShader;
1016 add.mDeleteTime=0;
1017 if (TS.gore->lifeTime)
1018 {
1019 add.mDeleteTime=G2API_GetTime(0) + TS.gore->lifeTime;
1020 }
1021 add.mFadeTime = TS.gore->fadeOutTime;
1022 add.mFadeRGB = TS.gore->fadeRGB;
1023 add.mGoreTag = newTag;
1024
1025 add.mGoreGrowStartTime=G2API_GetTime(0);
1026 if( TS.gore->growDuration == -1)
1027 {
1028 add.mGoreGrowEndTime = -1; // set this to -1 to disable growing
1029 }
1030 else
1031 {
1032 add.mGoreGrowEndTime = G2API_GetTime(0) + TS.gore->growDuration;
1033 }
1034
1035 assert(TS.gore->growDuration != 0);
1036 add.mGoreGrowFactor = ( 1.0f - TS.gore->goreScaleStartFraction) / (float)(TS.gore->growDuration); //curscale = (curtime-mGoreGrowStartTime)*mGoreGrowFactor;
1037 add.mGoreGrowOffset = TS.gore->goreScaleStartFraction;
1038
1039 goreSet->mGoreRecords.insert(std::make_pair(TS.surfaceNum,add));
1040 GoreTagsTemp[std::make_pair(goreModelIndex,TS.surfaceNum)]=newTag;
1041 }
1042 else
1043 {
1044 newTag=(*f).second;
1045 }
1046 GoreTextureCoordinates *gore=FindGoreRecord(newTag);
1047 if (gore)
1048 {
1049 assert(sizeof(float)==sizeof(int));
1050 // data block format:
1051 unsigned int size=
1052 sizeof(int)+ // num verts
1053 sizeof(int)+ // num tris
1054 sizeof(int)*newNumVerts+ // which verts to copy from original surface
1055 sizeof(float)*4*newNumVerts+ // storgage for deformed verts
1056 sizeof(float)*4*newNumVerts+ // storgage for deformed normal
1057 sizeof(float)*2*newNumVerts+ // texture coordinates
1058 sizeof(int)*newNumTris*3; // new indecies
1059
1060 int *data=(int *)R_Malloc ( sizeof(int)*size, TAG_GHOUL2, qtrue );
1061
1062 if ( gore->tex[TS.lod] )
1063 R_Free(gore->tex[TS.lod]);
1064
1065 gore->tex[TS.lod]=(float *)data;
1066 *data++=newNumVerts;
1067 *data++=newNumTris;
1068
1069 memcpy(data,GoreIndexCopy,sizeof(int)*newNumVerts);
1070 data+=newNumVerts*9; // skip verts and normals
1071 float *fdata=(float *)data;
1072
1073 for (j=0;j<newNumVerts;j++)
1074 {
1075 *fdata++=GoreVerts[GoreIndexCopy[j]].tex[0];
1076 *fdata++=GoreVerts[GoreIndexCopy[j]].tex[1];
1077 }
1078 data=(int *)fdata;
1079 memcpy(data,GoreIndecies,sizeof(int)*newNumTris*3);
1080 data+=newNumTris*3;
1081 assert((data-(int *)gore->tex[TS.lod])*sizeof(int)==size);
1082 fdata = (float *)data;
1083 // build the entity to gore matrix
1084 VectorCopy(saxis,fdata+0);
1085 VectorCopy(taxis,fdata+4);
1086 VectorCopy(TS.rayEnd,fdata+8);
1087 VectorNormalize(fdata+0);
1088 VectorNormalize(fdata+4);
1089 VectorNormalize(fdata+8);
1090 fdata[3]=-0.5f; // subtract texture center
1091 fdata[7]=-0.5f;
1092 fdata[11]=0.0f;
1093 vec3_t shotOriginInCurrentSpace; // unknown space
1094 TransformPoint(TS.rayStart,shotOriginInCurrentSpace,(mdxaBone_t *)fdata); // dest middle arg
1095 // this will insure the shot origin in our unknown space is now the shot origin, making it a known space
1096 fdata[3]-=shotOriginInCurrentSpace[0];
1097 fdata[7]-=shotOriginInCurrentSpace[1];
1098 fdata[11]-=shotOriginInCurrentSpace[2];
1099 Inverse_Matrix((mdxaBone_t *)fdata,(mdxaBone_t *)(fdata+12)); // dest 2nd arg
1100 data+=24;
1101
1102 // assert((data - (int *)gore->tex[TS.lod]) * sizeof(int) == size);
1103 }
1104 }
1105 #else
1106 struct SVertexTemp
1107 {
1108 int flags;
1109 // int touch;
1110 // int newindex;
1111 // float tex[2];
SVertexTempSVertexTemp1112 SVertexTemp()
1113 {
1114 // touch=0;
1115 }
1116 };
1117
1118 #define MAX_GORE_VERTS (3000)
1119 static SVertexTemp GoreVerts[MAX_GORE_VERTS];
1120 #endif
1121
1122 // now we're at poly level, check each model space transformed poly against the model world transfomed ray
G2_TracePolys(const mdxmSurface_t * surface,const mdxmSurfHierarchy_t * surfInfo,CTraceSurface & TS)1123 static bool G2_TracePolys(const mdxmSurface_t *surface, const mdxmSurfHierarchy_t *surfInfo, CTraceSurface &TS)
1124 {
1125 int j, numTris;
1126
1127 // whip through and actually transform each vertex
1128 const mdxmTriangle_t *tris = (mdxmTriangle_t *) ((byte *)surface + surface->ofsTriangles);
1129 const float *verts = (float *)TS.TransformedVertsArray[surface->thisSurfaceIndex];
1130 numTris = surface->numTriangles;
1131 for ( j = 0; j < numTris; j++ )
1132 {
1133 float face;
1134 vec3_t hitPoint, normal;
1135 // determine actual coords for this triangle
1136 const float *point1 = &verts[(tris[j].indexes[0] * 5)];
1137 const float *point2 = &verts[(tris[j].indexes[1] * 5)];
1138 const float *point3 = &verts[(tris[j].indexes[2] * 5)];
1139 // did we hit it?
1140 if (G2_SegmentTriangleTest(TS.rayStart, TS.rayEnd, point1, point2, point3, qtrue, qtrue, hitPoint, normal, &face))
1141 { // find space in the collision records for this record
1142 int i=0;
1143 for (; i<MAX_G2_COLLISIONS;i++)
1144 {
1145 if (TS.collRecMap[i].mEntityNum == -1)
1146 {
1147 CCollisionRecord &newCol = TS.collRecMap[i];
1148 vec3_t distVect;
1149 float x_pos = 0, y_pos = 0;
1150
1151 newCol.mPolyIndex = j;
1152 newCol.mEntityNum = TS.entNum;
1153 newCol.mSurfaceIndex = surface->thisSurfaceIndex;
1154 newCol.mModelIndex = TS.modelIndex;
1155 if (face>0)
1156 {
1157 newCol.mFlags = G2_FRONTFACE;
1158 }
1159 else
1160 {
1161 newCol.mFlags = G2_BACKFACE;
1162 }
1163
1164 VectorSubtract(hitPoint, TS.rayStart, distVect);
1165 newCol.mDistance = VectorLength(distVect);
1166 assert( !Q_isnan(newCol.mDistance) );
1167
1168 // put the hit point back into world space
1169 TransformAndTranslatePoint(hitPoint, newCol.mCollisionPosition, &worldMatrix);
1170
1171 // transform normal (but don't translate) into world angles
1172 TransformPoint(normal, newCol.mCollisionNormal, &worldMatrix);
1173 VectorNormalize(newCol.mCollisionNormal);
1174
1175 newCol.mMaterial = newCol.mLocation = 0;
1176
1177 // Determine our location within the texture, and barycentric coordinates
1178 G2_BuildHitPointST(point1, point1[3], point1[4],
1179 point2, point2[3], point2[4],
1180 point3, point3[3], point3[4],
1181 hitPoint, &x_pos, &y_pos,newCol.mBarycentricI,newCol.mBarycentricJ);
1182
1183 /*
1184 const shader_t *shader = 0;
1185 // now, we know what surface this hit belongs to, we need to go get the shader handle so we can get the correct hit location and hit material info
1186 if ( cust_shader )
1187 {
1188 shader = cust_shader;
1189 }
1190 else if ( skin )
1191 {
1192 int j;
1193
1194 // match the surface name to something in the skin file
1195 shader = tr.defaultShader;
1196 for ( j = 0 ; j < skin->numSurfaces ; j++ )
1197 {
1198 // the names have both been lowercased
1199 if ( !strcmp( skin->surfaces[j]->name, surfInfo->name ) )
1200 {
1201 shader = skin->surfaces[j]->shader;
1202 break;
1203 }
1204 }
1205 }
1206 else
1207 {
1208 shader = R_GetShaderByHandle( surfInfo->shaderIndex );
1209 }
1210
1211 // do we even care to decide what the hit or location area's are? If we don't have them in the shader there is little point
1212 if ((shader->hitLocation) || (shader->hitMaterial))
1213 {
1214 // ok, we have a floating point position. - determine location in data we need to look at
1215 if (shader->hitLocation)
1216 {
1217 newCol.mLocation = *(hitMatReg[shader->hitLocation].loc +
1218 ((int)(y_pos * hitMatReg[shader->hitLocation].height) * hitMatReg[shader->hitLocation].width) +
1219 ((int)(x_pos * hitMatReg[shader->hitLocation].width)));
1220 Com_Printf("G2_TracePolys hit location: %d\n", newCol.mLocation);
1221 }
1222
1223 if (shader->hitMaterial)
1224 {
1225 newCol.mMaterial = *(hitMatReg[shader->hitMaterial].loc +
1226 ((int)(y_pos * hitMatReg[shader->hitMaterial].height) * hitMatReg[shader->hitMaterial].width) +
1227 ((int)(x_pos * hitMatReg[shader->hitMaterial].width)));
1228 }
1229 }
1230 */
1231 // exit now if we should
1232 if (TS.eG2TraceType == G2_RETURNONHIT)
1233 {
1234 TS.hitOne = true;
1235 return true;
1236 }
1237
1238 break;
1239 }
1240 }
1241 if (i == MAX_G2_COLLISIONS)
1242 {
1243 assert(i!=MAX_G2_COLLISIONS); // run out of collision record space - will probalbly never happen
1244 TS.hitOne = true; //force stop recursion
1245 return true; // return true to avoid wasting further time, but no hit will result without a record
1246 }
1247 }
1248 }
1249 return false;
1250 }
1251
1252
1253 // now we're at poly level, check each model space transformed poly against the model world transfomed ray
G2_RadiusTracePolys(const mdxmSurface_t * surface,CTraceSurface & TS)1254 static bool G2_RadiusTracePolys(
1255 const mdxmSurface_t *surface,
1256 CTraceSurface &TS
1257 )
1258 {
1259 int j;
1260 vec3_t basis1;
1261 vec3_t basis2;
1262 vec3_t taxis;
1263 vec3_t saxis;
1264
1265 basis2[0]=0.0f;
1266 basis2[1]=0.0f;
1267 basis2[2]=1.0f;
1268
1269 vec3_t v3RayDir;
1270 VectorSubtract(TS.rayEnd, TS.rayStart, v3RayDir);
1271
1272 CrossProduct(v3RayDir,basis2,basis1);
1273
1274 if (DotProduct(basis1,basis1)<.1f)
1275 {
1276 basis2[0]=0.0f;
1277 basis2[1]=1.0f;
1278 basis2[2]=0.0f;
1279 CrossProduct(v3RayDir,basis2,basis1);
1280 }
1281
1282 CrossProduct(v3RayDir,basis1,basis2);
1283 // Give me a shot direction not a bunch of zeros :) -Gil
1284 // assert(DotProduct(basis1,basis1)>.0001f);
1285 // assert(DotProduct(basis2,basis2)>.0001f);
1286
1287 VectorNormalize(basis1);
1288 VectorNormalize(basis2);
1289
1290 const float c=cos(0.0f);//theta
1291 const float s=sin(0.0f);//theta
1292
1293 VectorScale(basis1, 0.5f * c / TS.m_fRadius,taxis);
1294 VectorMA(taxis, 0.5f * s / TS.m_fRadius,basis2,taxis);
1295
1296 VectorScale(basis1,-0.5f * s /TS.m_fRadius,saxis);
1297 VectorMA( saxis, 0.5f * c /TS.m_fRadius,basis2,saxis);
1298
1299 const float * const verts = (float *)TS.TransformedVertsArray[surface->thisSurfaceIndex];
1300 const int numVerts = surface->numVerts;
1301
1302 int flags=63;
1303 //rayDir/=lengthSquared(raydir);
1304 const float f = VectorLengthSquared(v3RayDir);
1305 v3RayDir[0]/=f;
1306 v3RayDir[1]/=f;
1307 v3RayDir[2]/=f;
1308
1309 for ( j = 0; j < numVerts; j++ )
1310 {
1311 const int pos=j*5;
1312 vec3_t delta;
1313 delta[0]=verts[pos+0]-TS.rayStart[0];
1314 delta[1]=verts[pos+1]-TS.rayStart[1];
1315 delta[2]=verts[pos+2]-TS.rayStart[2];
1316 const float s=DotProduct(delta,saxis)+0.5f;
1317 const float t=DotProduct(delta,taxis)+0.5f;
1318 const float u=DotProduct(delta,v3RayDir);
1319 int vflags=0;
1320
1321 if (s>0)
1322 {
1323 vflags|=1;
1324 }
1325 if (s<1)
1326 {
1327 vflags|=2;
1328 }
1329 if (t>0)
1330 {
1331 vflags|=4;
1332 }
1333 if (t<1)
1334 {
1335 vflags|=8;
1336 }
1337 if (u>0)
1338 {
1339 vflags|=16;
1340 }
1341 if (u<1)
1342 {
1343 vflags|=32;
1344 }
1345
1346 vflags=(~vflags);
1347 flags&=vflags;
1348 GoreVerts[j].flags=vflags;
1349 }
1350
1351 if (flags)
1352 {
1353 return false; // completely off the gore splotch (so presumably hit nothing? -Ste)
1354 }
1355 const int numTris = surface->numTriangles;
1356 const mdxmTriangle_t * const tris = (mdxmTriangle_t *) ((byte *)surface + surface->ofsTriangles);
1357
1358 for ( j = 0; j < numTris; j++ )
1359 {
1360 assert(tris[j].indexes[0]>=0&&tris[j].indexes[0]<numVerts);
1361 assert(tris[j].indexes[1]>=0&&tris[j].indexes[1]<numVerts);
1362 assert(tris[j].indexes[2]>=0&&tris[j].indexes[2]<numVerts);
1363 flags=63&
1364 GoreVerts[tris[j].indexes[0]].flags&
1365 GoreVerts[tris[j].indexes[1]].flags&
1366 GoreVerts[tris[j].indexes[2]].flags;
1367 if (flags)
1368 {
1369 continue;
1370 }
1371 else
1372 {
1373 // we hit a triangle, so init a collision record...
1374 //
1375 int i = 0;
1376 for (; i<MAX_G2_COLLISIONS;i++)
1377 {
1378 if (TS.collRecMap[i].mEntityNum == -1)
1379 {
1380 CCollisionRecord &newCol = TS.collRecMap[i];
1381
1382 newCol.mPolyIndex = j;
1383 newCol.mEntityNum = TS.entNum;
1384 newCol.mSurfaceIndex = surface->thisSurfaceIndex;
1385 newCol.mModelIndex = TS.modelIndex;
1386 // if (face>0)
1387 // {
1388 newCol.mFlags = G2_FRONTFACE;
1389 // }
1390 // else
1391 // {
1392 // newCol.mFlags = G2_BACKFACE;
1393 // }
1394
1395 //get normal from triangle
1396 const float *A = &verts[(tris[j].indexes[0] * 5)];
1397 const float *B = &verts[(tris[j].indexes[1] * 5)];
1398 const float *C = &verts[(tris[j].indexes[2] * 5)];
1399 vec3_t normal;
1400 vec3_t edgeAC, edgeBA;
1401
1402 VectorSubtract(C, A, edgeAC);
1403 VectorSubtract(B, A, edgeBA);
1404 CrossProduct(edgeBA, edgeAC, normal);
1405
1406 // transform normal (but don't translate) into world angles
1407 TransformPoint(normal, newCol.mCollisionNormal, &worldMatrix);
1408 VectorNormalize(newCol.mCollisionNormal);
1409
1410 newCol.mMaterial = newCol.mLocation = 0;
1411 // exit now if we should
1412 if (TS.eG2TraceType == G2_RETURNONHIT)
1413 {
1414 TS.hitOne = true;
1415 return true;
1416 }
1417
1418 vec3_t distVect;
1419 #if 0
1420 //i don't know the hitPoint, but let's just assume it's the first vert for now...
1421 float *hitPoint = (float *)A;
1422 #else
1423 //yeah, I want the collision point. Let's work out the impact point on the triangle. -rww
1424 vec3_t hitPoint;
1425 float side, side2;
1426 float dist;
1427 float third = -(A[0]*(B[1]*C[2] - C[1]*B[2]) + B[0]*(C[1]*A[2] - A[1]*C[2]) + C[0]*(A[1]*B[2] - B[1]*A[2]) );
1428
1429 VectorSubtract(TS.rayEnd, TS.rayStart, distVect);
1430 side = normal[0]*TS.rayStart[0] + normal[1]*TS.rayStart[1] + normal[2]*TS.rayStart[2] + third;
1431 side2 = normal[0]*distVect[0] + normal[1]*distVect[1] + normal[2]*distVect[2];
1432 if (fabsf(side2)<1E-8f)
1433 {
1434 //i don't know the hitPoint, but let's just assume it's the first vert for now...
1435 VectorSubtract(A, TS.rayStart, distVect);
1436 dist = VectorLength(distVect);
1437 VectorSubtract(TS.rayEnd, TS.rayStart, distVect);
1438 VectorMA(TS.rayStart, dist/VectorLength(distVect), distVect, hitPoint);
1439 }
1440 else
1441 {
1442 dist = side/side2;
1443 VectorMA(TS.rayStart, -dist, distVect, hitPoint);
1444 }
1445 #endif
1446
1447 VectorSubtract(hitPoint, TS.rayStart, distVect);
1448 newCol.mDistance = VectorLength(distVect);
1449 assert( !Q_isnan(newCol.mDistance) );
1450
1451 // put the hit point back into world space
1452 TransformAndTranslatePoint(hitPoint, newCol.mCollisionPosition, &worldMatrix);
1453 newCol.mBarycentricI = newCol.mBarycentricJ = 0.0f;
1454
1455 break;
1456 }
1457 }
1458 if (i==MAX_G2_COLLISIONS)
1459 {
1460 //assert(i!=MAX_G2_COLLISIONS); // run out of collision record space - happens OFTEN
1461 TS.hitOne = true; //force stop recursion
1462 return true; // return true to avoid wasting further time, but no hit will result without a record
1463 }
1464 }
1465 }
1466
1467 return false;
1468 }
1469
1470
1471 // look at a surface and then do the trace on each poly
G2_TraceSurfaces(CTraceSurface & TS)1472 static void G2_TraceSurfaces(CTraceSurface &TS)
1473 {
1474 int i;
1475 // back track and get the surfinfo struct for this surface
1476 assert(TS.currentModel);
1477 assert(TS.currentModel->mdxm);
1478 const mdxmSurface_t *surface = (mdxmSurface_t *)G2_FindSurface(TS.currentModel, TS.surfaceNum, TS.lod);
1479 const mdxmHierarchyOffsets_t *surfIndexes = (mdxmHierarchyOffsets_t *)((byte *)TS.currentModel->mdxm + sizeof(mdxmHeader_t));
1480 const mdxmSurfHierarchy_t *surfInfo = (mdxmSurfHierarchy_t *)((byte *)surfIndexes + surfIndexes->offsets[surface->thisSurfaceIndex]);
1481
1482 // see if we have an override surface in the surface list
1483 const surfaceInfo_t *surfOverride = G2_FindOverrideSurface(TS.surfaceNum, TS.rootSList);
1484
1485 // don't allow recursion if we've already hit a polygon
1486 if (TS.hitOne)
1487 {
1488 return;
1489 }
1490
1491 // really, we should use the default flags for this surface unless it's been overriden
1492 int offFlags = surfInfo->flags;
1493
1494 // set the off flags if we have some
1495 if (surfOverride)
1496 {
1497 offFlags = surfOverride->offFlags;
1498 }
1499
1500 // if this surface is not off, try to hit it
1501 if (!offFlags)
1502 {
1503 #ifdef _G2_GORE
1504 if (TS.collRecMap)
1505 {
1506 #endif
1507 if (!(Q_fabs(TS.m_fRadius) < 0.1)) // if not a point-trace
1508 {
1509 // .. then use radius check
1510 //
1511 if (G2_RadiusTracePolys(surface, // const mdxmSurface_t *surface,
1512 TS
1513 )
1514 && (TS.eG2TraceType == G2_RETURNONHIT)
1515 )
1516 {
1517 TS.hitOne = true;
1518 return;
1519 }
1520 }
1521 else
1522 {
1523 // go away and trace the polys in this surface
1524 if (G2_TracePolys(surface, surfInfo, TS)
1525 && (TS.eG2TraceType == G2_RETURNONHIT)
1526 )
1527 {
1528 // ok, we hit one, *and* we want to return instantly because the returnOnHit is set
1529 // so indicate we've hit one, so other surfaces don't get hit and return
1530 TS.hitOne = true;
1531 return;
1532 }
1533 }
1534 #ifdef _G2_GORE
1535 }
1536 else
1537 {
1538 G2_GorePolys(surface, TS, surfInfo);
1539 }
1540 #endif
1541 }
1542
1543 // if we are turning off all descendants, then stop this recursion now
1544 if (offFlags & G2SURFACEFLAG_NODESCENDANTS)
1545 {
1546 return;
1547 }
1548
1549 // now recursively call for the children
1550 for (i=0; i< surfInfo->numChildren && !TS.hitOne; i++)
1551 {
1552 TS.surfaceNum = surfInfo->childIndexes[i];
1553 G2_TraceSurfaces(TS);
1554 }
1555 }
1556
1557 #ifdef _G2_GORE
G2_TraceModels(CGhoul2Info_v & ghoul2,vec3_t rayStart,vec3_t rayEnd,CCollisionRecord * collRecMap,int entNum,EG2_Collision eG2TraceType,int useLod,float fRadius,float ssize,float tsize,float theta,int shader,SSkinGoreData * gore,qboolean skipIfLODNotMatch)1558 void G2_TraceModels(CGhoul2Info_v &ghoul2, vec3_t rayStart, vec3_t rayEnd, CCollisionRecord *collRecMap, int entNum, EG2_Collision eG2TraceType, int useLod, float fRadius, float ssize,float tsize,float theta,int shader, SSkinGoreData *gore, qboolean skipIfLODNotMatch)
1559 #else
1560 void G2_TraceModels(CGhoul2Info_v &ghoul2, vec3_t rayStart, vec3_t rayEnd, CCollisionRecord *collRecMap, int entNum, EG2_Collision eG2TraceType, int useLod, float fRadius)
1561 #endif
1562 {
1563 int i, lod;
1564 skin_t *skin;
1565 shader_t *cust_shader;
1566 #if !defined(JK2_MODE) || defined(_G2_GORE)
1567 qboolean firstModelOnly = qfalse;
1568 #endif // !JK2_MODE || _G2_GORE
1569 int firstModel = 0;
1570
1571 #ifndef JK2_MODE
1572 if ( cg_g2MarksAllModels == NULL )
1573 {
1574 cg_g2MarksAllModels = ri.Cvar_Get( "cg_g2MarksAllModels", "0", 0 );
1575 }
1576
1577 if (cg_g2MarksAllModels == NULL
1578 || !cg_g2MarksAllModels->integer )
1579 {
1580 firstModelOnly = qtrue;
1581 }
1582 #endif // !JK2_MODE
1583
1584 #ifdef _G2_GORE
1585 if ( gore
1586 && gore->firstModel > 0 )
1587 {
1588 firstModel = gore->firstModel;
1589 firstModelOnly = qfalse;
1590 }
1591 #endif
1592
1593 // walk each possible model for this entity and try tracing against it
1594 for (i=firstModel; i<ghoul2.size(); i++)
1595 {
1596 CGhoul2Info &g=ghoul2[i];
1597 #ifdef _G2_GORE
1598 goreModelIndex=i;
1599 // don't bother with models that we don't care about.
1600 if (g.mModelindex == -1)
1601 {
1602 continue;
1603 }
1604 #endif
1605 // don't bother with models that we don't care about.
1606 if (!g.mValid)
1607 {
1608 continue;
1609 }
1610 assert(G2_MODEL_OK(&ghoul2[i]));
1611 // do we really want to collide with this object?
1612 if (g.mFlags & GHOUL2_NOCOLLIDE)
1613 {
1614 continue;
1615 }
1616
1617 if (g.mCustomShader)
1618 {
1619 cust_shader = R_GetShaderByHandle(g.mCustomShader );
1620 }
1621 else
1622 {
1623 cust_shader = NULL;
1624 }
1625
1626 // figure out the custom skin thing
1627 if ( g.mSkin > 0 && g.mSkin < tr.numSkins )
1628 {
1629 skin = R_GetSkinByHandle( g.mSkin );
1630 }
1631 else
1632 {
1633 skin = NULL;
1634 }
1635
1636 lod = G2_DecideTraceLod(g,useLod);
1637
1638 #ifndef JK2_MODE
1639 if ( skipIfLODNotMatch )
1640 {//we only want to hit this SPECIFIC LOD...
1641 if ( lod != useLod )
1642 {//doesn't match, skip this model
1643 continue;
1644 }
1645 }
1646 #endif // !JK2_MODE
1647
1648 //reset the quick surface override lookup
1649 G2_FindOverrideSurface(-1, g.mSlist);
1650
1651 #ifdef _G2_GORE
1652 CTraceSurface TS(g.mSurfaceRoot, g.mSlist, g.currentModel, lod, rayStart, rayEnd, collRecMap, entNum, i, skin, cust_shader, g.mTransformedVertsArray, eG2TraceType, fRadius, ssize, tsize, theta, shader, &g, gore);
1653 #else
1654 CTraceSurface TS(g.mSurfaceRoot, g.mSlist, g.currentModel, lod, rayStart, rayEnd, collRecMap, entNum, i, skin, cust_shader, g.mTransformedVertsArray, eG2TraceType, fRadius);
1655 #endif
1656 // start the surface recursion loop
1657 G2_TraceSurfaces(TS);
1658
1659 // if we've hit one surface on one model, don't bother doing the rest
1660 if (TS.hitOne)
1661 {
1662 break;
1663 }
1664 #ifdef _G2_GORE
1665 if ( !collRecMap && firstModelOnly )
1666 {
1667 // we don't really need to do multiple models for gore.
1668 break;
1669 }
1670 #endif
1671 }
1672 }
1673
TransformPoint(const vec3_t in,vec3_t out,mdxaBone_t * mat)1674 void TransformPoint (const vec3_t in, vec3_t out, mdxaBone_t *mat) {
1675 for (int i=0;i<3;i++)
1676 {
1677 out[i]= in[0]*mat->matrix[i][0] + in[1]*mat->matrix[i][1] + in[2]*mat->matrix[i][2];
1678 }
1679 }
1680
TransformAndTranslatePoint(const vec3_t in,vec3_t out,mdxaBone_t * mat)1681 void TransformAndTranslatePoint (const vec3_t in, vec3_t out, mdxaBone_t *mat) {
1682
1683 for (int i=0;i<3;i++)
1684 {
1685 out[i]= in[0]*mat->matrix[i][0] + in[1]*mat->matrix[i][1] + in[2]*mat->matrix[i][2] + mat->matrix[i][3];
1686 }
1687 }
1688
1689
1690 // create a matrix using a set of angles
Create_Matrix(const float * angle,mdxaBone_t * matrix)1691 void Create_Matrix(const float *angle, mdxaBone_t *matrix)
1692 {
1693 vec3_t axis[3];
1694
1695 // convert angles to axis
1696 AnglesToAxis( angle, axis );
1697 matrix->matrix[0][0] = axis[0][0];
1698 matrix->matrix[1][0] = axis[0][1];
1699 matrix->matrix[2][0] = axis[0][2];
1700
1701 matrix->matrix[0][1] = axis[1][0];
1702 matrix->matrix[1][1] = axis[1][1];
1703 matrix->matrix[2][1] = axis[1][2];
1704
1705 matrix->matrix[0][2] = axis[2][0];
1706 matrix->matrix[1][2] = axis[2][1];
1707 matrix->matrix[2][2] = axis[2][2];
1708
1709 matrix->matrix[0][3] = 0;
1710 matrix->matrix[1][3] = 0;
1711 matrix->matrix[2][3] = 0;
1712
1713
1714 }
1715
1716 // given a matrix, generate the inverse of that matrix
Inverse_Matrix(mdxaBone_t * src,mdxaBone_t * dest)1717 void Inverse_Matrix(mdxaBone_t *src, mdxaBone_t *dest)
1718 {
1719 int i, j;
1720
1721 for (i = 0; i < 3; i++)
1722 {
1723 for (j = 0; j < 3; j++)
1724 {
1725 dest->matrix[i][j]=src->matrix[j][i];
1726 }
1727 }
1728 for (i = 0; i < 3; i++)
1729 {
1730 dest->matrix[i][3]=0;
1731 for (j = 0; j < 3; j++)
1732 {
1733 dest->matrix[i][3]-=dest->matrix[i][j]*src->matrix[j][3];
1734 }
1735 }
1736 }
1737
1738 // generate the world matrix for a given set of angles and origin - called from lots of places
G2_GenerateWorldMatrix(const vec3_t angles,const vec3_t origin)1739 void G2_GenerateWorldMatrix(const vec3_t angles, const vec3_t origin)
1740 {
1741 Create_Matrix(angles, &worldMatrix);
1742 worldMatrix.matrix[0][3] = origin[0];
1743 worldMatrix.matrix[1][3] = origin[1];
1744 worldMatrix.matrix[2][3] = origin[2];
1745
1746 Inverse_Matrix(&worldMatrix, &worldMatrixInv);
1747 }
1748
1749 // go away and determine what the pointer for a specific surface definition within the model definition is
G2_FindSurface(const model_s * mod,int index,int lod)1750 void *G2_FindSurface(const model_s *mod, int index, int lod)
1751 {
1752 assert(mod);
1753 assert(mod->mdxm);
1754
1755 // point at first lod list
1756 byte *current = (byte*)((intptr_t)mod->mdxm + (intptr_t)mod->mdxm->ofsLODs);
1757 int i;
1758
1759 //walk the lods
1760 assert(lod>=0&&lod<mod->mdxm->numLODs);
1761 for (i=0; i<lod; i++)
1762 {
1763 mdxmLOD_t *lodData = (mdxmLOD_t *)current;
1764 current += lodData->ofsEnd;
1765 }
1766
1767 // avoid the lod pointer data structure
1768 current += sizeof(mdxmLOD_t);
1769
1770 mdxmLODSurfOffset_t *indexes = (mdxmLODSurfOffset_t *)current;
1771 // we are now looking at the offset array
1772 assert(index>=0&&index<mod->mdxm->numSurfaces);
1773 current += indexes->offsets[index];
1774
1775 return (void *)current;
1776 }
1777
1778
1779 #define SURFACE_SAVE_BLOCK_SIZE sizeof(surfaceInfo_t)
1780 #define BOLT_SAVE_BLOCK_SIZE sizeof(boltInfo_t)
1781 #define BONE_SAVE_BLOCK_SIZE sizeof(boneInfo_t)
1782
1783
G2_SaveGhoul2Models(CGhoul2Info_v & ghoul2)1784 void G2_SaveGhoul2Models(
1785 CGhoul2Info_v& ghoul2)
1786 {
1787 ojk::SavedGameHelper saved_game(
1788 ::ri.saved_game);
1789
1790 saved_game.reset_buffer();
1791
1792 // is there anything to save?
1793 if (!ghoul2.IsValid() || ghoul2.size() == 0)
1794 {
1795 const int zero_size = 0;
1796
1797 #ifdef JK2_MODE
1798 saved_game.write<int32_t>(
1799 zero_size);
1800
1801 saved_game.write_chunk_and_size<int32_t>(
1802 INT_ID('G', 'L', '2', 'S'),
1803 INT_ID('G', 'H', 'L', '2'));
1804 #else
1805 saved_game.write_chunk<int32_t>(
1806 INT_ID('G', 'H', 'L', '2'),
1807 zero_size); //write out a zero buffer
1808 #endif // JK2_MODE
1809
1810 return;
1811 }
1812
1813
1814 // save out how many ghoul2 models we have
1815 const int model_count = static_cast<int>(ghoul2.size());
1816
1817 saved_game.write<int32_t>(
1818 model_count);
1819
1820 for (int i = 0; i < model_count; ++i)
1821 {
1822 // first save out the ghoul2 details themselves
1823 ghoul2[i].sg_export(
1824 saved_game);
1825
1826 // save out how many surfaces we have
1827 const int surface_count = static_cast<int>(ghoul2[i].mSlist.size());
1828
1829 saved_game.write<int32_t>(
1830 surface_count);
1831
1832 // now save the all the surface list info
1833 for (int x = 0; x < surface_count; ++x)
1834 {
1835 ghoul2[i].mSlist[x].sg_export(
1836 saved_game);
1837 }
1838
1839 // save out how many bones we have
1840 const int bone_count = static_cast<int>(ghoul2[i].mBlist.size());
1841
1842 saved_game.write<int32_t>(
1843 bone_count);
1844
1845 // now save the all the bone list info
1846 for (int x = 0; x < bone_count; ++x)
1847 {
1848 ghoul2[i].mBlist[x].sg_export(
1849 saved_game);
1850 }
1851
1852 // save out how many bolts we have
1853 const int bolt_count = static_cast<int>(ghoul2[i].mBltlist.size());
1854
1855 saved_game.write<int32_t>(
1856 bolt_count);
1857
1858 // lastly save the all the bolt list info
1859 for (int x = 0; x < bolt_count; ++x)
1860 {
1861 ghoul2[i].mBltlist[x].sg_export(
1862 saved_game);
1863 }
1864 }
1865
1866 #ifdef JK2_MODE
1867 saved_game.write_chunk_and_size<int32_t>(
1868 INT_ID('G', 'L', '2', 'S'),
1869 INT_ID('G', 'H', 'L', '2'));
1870 #else
1871 saved_game.write_chunk(
1872 INT_ID('G', 'H', 'L', '2'));
1873 #endif // JK2_MODE
1874 }
1875
1876 // FIXME Remove 'buffer' parameter
G2_LoadGhoul2Model(CGhoul2Info_v & ghoul2,char * buffer)1877 void G2_LoadGhoul2Model(
1878 CGhoul2Info_v& ghoul2,
1879 char* buffer)
1880 {
1881 static_cast<void>(buffer);
1882
1883 ojk::SavedGameHelper saved_game(
1884 ::ri.saved_game);
1885
1886 // first thing, lets see how many ghoul2 models we have, and resize our buffers accordingly
1887 int model_count = 0;
1888
1889 #ifdef JK2_MODE
1890 if (saved_game.get_buffer_size() > 0)
1891 {
1892 #endif // JK2_MODE
1893
1894 saved_game.read<int32_t>(
1895 model_count);
1896
1897 #ifdef JK2_MODE
1898 }
1899 #endif // JK2_MODE
1900
1901
1902 ghoul2.resize(
1903 model_count);
1904
1905 // did we actually resize to a value?
1906 if (model_count == 0)
1907 {
1908 // no, ok, well, done then.
1909 return;
1910 }
1911
1912 // now we have enough instances, lets go through each one and load up the relevant details
1913 for (decltype(model_count) i = 0; i < model_count; ++i)
1914 {
1915 ghoul2[i].mSkelFrameNum = 0;
1916 ghoul2[i].mModelindex = -1;
1917 ghoul2[i].mFileName[0] = 0;
1918 ghoul2[i].mValid = false;
1919
1920 // load the ghoul2 info from the buffer
1921 ghoul2[i].sg_import(
1922 saved_game);
1923
1924 if (ghoul2[i].mModelindex != -1 && ghoul2[i].mFileName[0])
1925 {
1926 ghoul2[i].mModelindex = i;
1927
1928 ::G2_SetupModelPointers(
1929 &ghoul2[i]);
1930 }
1931
1932 // give us enough surfaces to load up the data
1933 int surface_count = 0;
1934
1935 saved_game.read<int32_t>(
1936 surface_count);
1937
1938 ghoul2[i].mSlist.resize(surface_count);
1939
1940 // now load all the surfaces
1941 for (decltype(surface_count) x = 0; x < surface_count; ++x)
1942 {
1943 ghoul2[i].mSlist[x].sg_import(
1944 saved_game);
1945 }
1946
1947 // give us enough bones to load up the data
1948 int bone_count = 0;
1949
1950 saved_game.read<int32_t>(
1951 bone_count);
1952
1953 ghoul2[i].mBlist.resize(
1954 bone_count);
1955
1956 // now load all the bones
1957 for (decltype(bone_count) x = 0; x < bone_count; ++x)
1958 {
1959 ghoul2[i].mBlist[x].sg_import(
1960 saved_game);
1961 }
1962
1963 // give us enough bolts to load up the data
1964 int bolt_count = 0;
1965
1966 saved_game.read<int32_t>(
1967 bolt_count);
1968
1969 ghoul2[i].mBltlist.resize(
1970 bolt_count);
1971
1972 // now load all the bolts
1973 for (decltype(bolt_count) x = 0; x < bolt_count; ++x)
1974 {
1975 ghoul2[i].mBltlist[x].sg_import(
1976 saved_game);
1977 }
1978 }
1979
1980 saved_game.ensure_all_data_read();
1981 }
1982