// leave this as first line for PCH reasons... // #include "../server/exe_headers.h" #pragma warning( disable : 4786) #pragma warning( disable : 4100) #pragma warning( disable : 4511) #pragma warning (push, 3) //go back down to 3 for the stl include #include #include #pragma warning (pop) #ifndef __Q_SHARED_H #include "../game/q_shared.h" #endif #if !defined(TR_LOCAL_H) #include "../renderer/tr_local.h" #endif #if !defined(G2_H_INC) #include "G2.h" #endif #if !defined(MINIHEAP_H_INC) #include "..\qcommon\MiniHeap.h" #endif #ifdef FINAL_BUILD #define G2API_DEBUG (0) // please don't change this #else #ifdef _DEBUG #define G2API_DEBUG (1) #else #define G2API_DEBUG (0) // change this to test g2api in release #endif #endif using namespace std; extern mdxaBone_t worldMatrix; extern mdxaBone_t worldMatrixInv; extern cvar_t *r_Ghoul2TimeBase; #define G2_MODEL_OK(g) ((g)&&(g)->mValid&&(g)->aHeader&&(g)->currentModel&&(g)->animModel) #define G2_DEBUG_TIME (0) static int G2TimeBases[NUM_G2T_TIME]; bool G2_TestModelPointers(CGhoul2Info *ghlInfo); #if G2API_DEBUG #include //for isnan #define MAX_ERROR_PRINTS (3) class ErrorReporter { string mName; map mErrors; public: ErrorReporter(const string &name) : mName(name) { } ~ErrorReporter() { char mess[1000]; int total=0; sprintf(mess,"****** %s Error Report Begin******\n",mName.c_str()); OutputDebugString(mess); map::iterator i; for (i=mErrors.begin();i!=mErrors.end();i++) { total+=(*i).second; sprintf(mess,"%s (hits %d)\n",(*i).first.c_str(),(*i).second); OutputDebugString(mess); } sprintf(mess,"****** %s Error Report End %d errors of %d kinds******\n",mName.c_str(),total,mErrors.size()); OutputDebugString(mess); } int AnimTest(CGhoul2Info_v &ghoul2,const char *m,const char *, int line) { if (G2_SetupModelPointers(ghoul2)) { int i; for (i=0; ianimModel->name); strcpy(GLAName2,ghlInfo->aHeader->name); strcpy(GLMName1,ghlInfo->mFileName); strcpy(GLMName2,ghlInfo->currentModel->name); int numFramesInFile=ghlInfo->aHeader->numFrames; int numActiveBones=0; for (i=0;imBlist.size();i++) { if (ghlInfo->mBlist[i].boneNumber!=-1) // slot used? { if (ghlInfo->mBlist[i].flags&BONE_ANIM_TOTAL) // anim on this? { numActiveBones++; bool loop=!!(ghlInfo->mBlist[i].flags&BONE_ANIM_OVERRIDE_LOOP); bool not_loop=!!(ghlInfo->mBlist[i].flags&BONE_ANIM_OVERRIDE); if (loop==not_loop) { Error("Unusual animation flags, should have some sort of override, but not both",1,0,line); } bool freeze=(ghlInfo->mBlist[i].flags&BONE_ANIM_OVERRIDE_FREEZE) == BONE_ANIM_OVERRIDE_FREEZE; if (loop&&freeze) { Error("Unusual animation flags, loop and freeze",1,0,line); } bool no_lerp=!!(ghlInfo->mBlist[i].flags&BONE_ANIM_NO_LERP); bool blend=!!(ghlInfo->mBlist[i].flags&BONE_ANIM_BLEND); //comments according to jake int startFrame=ghlInfo->mBlist[i].startFrame; // start frame for animation int endFrame=ghlInfo->mBlist[i].endFrame; // end frame for animation NOTE anim actually ends on endFrame+1 int startTime=ghlInfo->mBlist[i].startTime; // time we started this animation int pauseTime=ghlInfo->mBlist[i].pauseTime; // time we paused this animation - 0 if not paused float animSpeed=ghlInfo->mBlist[i].animSpeed; // speed at which this anim runs. 1.0f means full speed of animation incoming - ie if anim is 20hrtz, we run at 20hrts. If 5hrts, we run at 5 hrts float blendFrame=0.0f; // frame PLUS LERP value to blend int blendLerpFrame=0; // frame to lerp the blend frame with. if (blend) { blendFrame=ghlInfo->mBlist[i].blendFrame; blendLerpFrame=ghlInfo->mBlist[i].blendLerpFrame; if (floor(blendFrame)<0.0f) { Error("negative blendFrame",1,0,line); } if (ceil(blendFrame)>=float(numFramesInFile)) { Error("blendFrame >= numFramesInFile",1,0,line); } if (blendLerpFrame<0) { Error("negative blendLerpFrame",1,0,line); } if (blendLerpFrame>=numFramesInFile) { Error("blendLerpFrame >= numFramesInFile",1,0,line); } } if (startFrame<0) { Error("negative startFrame",1,0,line); } if (startFrame>=numFramesInFile) { Error("startFrame >= numFramesInFile",1,0,line); } if (endFrame<0) { Error("negative endFrame",1,0,line); } if (endFrame==0&&animSpeed>0.0f) { Error("Zero endFrame",1,0,line); } if (endFrame>numFramesInFile) { Error("endFrame > numFramesInFile",1,0,line); } // mikeg call out here for further checks. ret=(int)startTime+(int)pauseTime+(int)no_lerp; // quiet VC. } } } return ret; } int Error(const char *m,int kind,const char *, int line) { char mess[1000]; assert(m); string full=mName; if (kind==2) { full+=":NOTE: "; } else if (kind==1) { // assert(!"G2API Warning"); full+=":WARNING: "; } else { // assert(!"G2API Error"); full+=":ERROR : "; } full+=m; sprintf(mess," [line %d]",line); full+=mess; // assert(0); int ret=0; //place a breakpoint here map::iterator f=mErrors.find(full); if (f==mErrors.end()) { ret++; // or a breakpoint here for the first occurance mErrors.insert(pair(full,0)); f=mErrors.find(full); } assert(f!=mErrors.end()); (*f).second++; if ((*f).second==1000) { ret*=-1; // breakpoint to find a specific occurance of an error } if ((*f).second<=MAX_ERROR_PRINTS&&kind<2) { Com_Printf("%s (hit # %d)\n",full.c_str(),(*f).second); if (1) { sprintf(mess,"%s (hit # %d)\n",full.c_str(),(*f).second); OutputDebugString(mess); } } return ret; } }; #include "assert.h" ErrorReporter &G2APIError() { static ErrorReporter singleton("G2API"); return singleton; } #define G2ERROR(exp,m) (void)( (exp) || (G2APIError().Error(m,0,__FILE__,__LINE__), 0) ) #define G2WARNING(exp,m) (void)( (exp) || (G2APIError().Error(m,1,__FILE__,__LINE__), 0) ) #define G2NOTE(exp,m) (void)( (exp) || (G2APIError().Error(m,2,__FILE__,__LINE__), 0) ) #define G2ANIM(ghlInfo,m) (void)((G2APIError().AnimTest(ghlInfo,m,__FILE__,__LINE__), 0) ) #else #define G2ERROR(exp,m) ((void)0) #define G2WARNING(exp,m) ((void)0) #define G2NOTE(exp,m) ((void)0) #define G2ANIM(ghlInfo,m) ((void)0) #endif #if G2API_DEBUG void G2_Bone_Not_Found(const char *boneName,const char *modName) { G2ERROR(boneName,"NULL Bone Name"); G2ERROR(boneName[0],"Empty Bone Name"); if (boneName) { G2NOTE(0,va("Bone Not Found (%s:%s)",boneName,modName)); } } void G2_Bolt_Not_Found(const char *boneName,const char *modName) { G2ERROR(boneName,"NULL Bolt/Bone Name"); G2ERROR(boneName[0],"Empty Bolt/Bone Name"); if (boneName) { G2NOTE(0,va("Bolt/Bone Not Found (%s:%s)",boneName,modName)); } } #endif void G2API_SetTime(int currentTime,int clock) { assert(clock>=0&&clockG2TimeBases[0]+200) { G2TimeBases[1]=0; // use server time instead return; } #if G2_DEBUG_TIME Com_Printf(" after c%6d s%6d\n",G2TimeBases[1],G2TimeBases[0]); #endif } int G2API_GetTime(int argTime) // this may or may not return arg depending on ghoul2_time cvar { int ret=G2TimeBases[1]; if ( !ret ) { ret = G2TimeBases[0]; } return ret; } // must be a power of two #define MAX_G2_MODELS (512) #define G2_MODEL_BITS (9) #define G2_INDEX_MASK (MAX_G2_MODELS-1) void RemoveBoneCache(CBoneCache *boneCache); class Ghoul2InfoArray : public IGhoul2InfoArray { vector mInfos[MAX_G2_MODELS]; int mIds[MAX_G2_MODELS]; list mFreeIndecies; void DeleteLow(int idx) { { int model; for (model=0; model< mInfos[idx].size(); model++) { RemoveBoneCache(mInfos[idx][model].mBoneCache); mInfos[idx][model].mBoneCache=0; } } mInfos[idx].clear(); if ((mIds[idx]>>G2_MODEL_BITS)>(1<<(31-G2_MODEL_BITS))) { mIds[idx]=MAX_G2_MODELS+idx; //rollover reset id to minimum value mFreeIndecies.push_back(idx); } else { mIds[idx]+=MAX_G2_MODELS; mFreeIndecies.push_front(idx); } } public: Ghoul2InfoArray() { int i; for (i=0;i::iterator j; for (j=mFreeIndecies.begin();j!=mFreeIndecies.end();j++) { if (*j==i) break; } if (j==mFreeIndecies.end()) { sprintf(mess,"Leaked Info idx=%d id=%d sz=%d\n", i, mIds[i], mInfos[i].size()); OutputDebugString(mess); if (mInfos[i].size()) { sprintf(mess,"%s\n", mInfos[i][0].mFileName); OutputDebugString(mess); } } } } else { OutputDebugString("No ghoul2 info slots leaked\n"); } } #endif int New() { if (mFreeIndecies.empty()) { assert(0); Com_Error(ERR_FATAL, "Out of ghoul2 info slots"); } // gonna pull from the front, doing a int idx=*mFreeIndecies.begin(); mFreeIndecies.erase(mFreeIndecies.begin()); return mIds[idx]; } bool IsValid(int handle) const { if (!handle) { return false; } assert(handle>0); //negative handle??? assert((handle&G2_INDEX_MASK)>=0&&(handle&G2_INDEX_MASK)0); //null handle assert((handle&G2_INDEX_MASK)>=0&&(handle&G2_INDEX_MASK) &Get(int handle) { static vector null; assert(handle>0); //null handle assert((handle&G2_INDEX_MASK)>=0&&(handle&G2_INDEX_MASK)=MAX_G2_MODELS||mIds[handle&G2_INDEX_MASK]!=handle) { null.clear(); return null; } return mInfos[handle&G2_INDEX_MASK]; } const vector &Get(int handle) const { assert(handle>0); assert(mIds[handle&G2_INDEX_MASK]==handle); // not a valid handle, could be old or garbage return mInfos[handle&G2_INDEX_MASK]; } #if G2API_DEBUG vector &GetDebug(int handle) { static vector null; if (handle<=0||(handle&G2_INDEX_MASK)<0||(handle&G2_INDEX_MASK)>=MAX_G2_MODELS||mIds[handle&G2_INDEX_MASK]!=handle) { return *(vector *)0; // null reference, intentional } return mInfos[handle&G2_INDEX_MASK]; } void TestAllAnims() { int j; for (j=0;j &ghoul2=mInfos[j]; int i; for (i=0; i &DebugG2Info(int handle) { return ((Ghoul2InfoArray *)(&TheGhoul2InfoArray()))->GetDebug(handle); } CGhoul2Info &DebugG2InfoI(int handle,int item) { return ((Ghoul2InfoArray *)(&TheGhoul2InfoArray()))->GetDebug(handle)[item]; } void TestAllGhoul2Anims() { ((Ghoul2InfoArray *)(&TheGhoul2InfoArray()))->TestAllAnims(); } #endif // this is the ONLY function to read entity states directly void G2API_CleanGhoul2Models(CGhoul2Info_v &ghoul2) { ghoul2.~CGhoul2Info_v(); } qhandle_t G2API_PrecacheGhoul2Model(const char *fileName) { return RE_RegisterModel((char *)fileName); } // initialise all that needs to be on a new Ghoul II model int G2API_InitGhoul2Model(CGhoul2Info_v &ghoul2, const char *fileName, int, qhandle_t customSkin, qhandle_t customShader, int modelFlags, int lodBias) { int model = -1; G2ERROR(fileName&&fileName[0],"NULL filename"); if (!fileName||!fileName[0]) { assert(fileName[0]); return -1; } // find a free spot in the list for (model=0; model< ghoul2.size(); model++) { if (ghoul2[model].mModelindex == -1) { ghoul2[model]=CGhoul2Info(); break; } } if (model==ghoul2.size()) { ghoul2.push_back(CGhoul2Info()); } strcpy(ghoul2[model].mFileName, fileName); ghoul2[model].mModelindex = model; if (!G2_TestModelPointers(&ghoul2[model])) { ghoul2[model].mFileName[0]=0; ghoul2[model].mModelindex = -1; } else { G2_Init_Bone_List(ghoul2[model].mBlist); G2_Init_Bolt_List(ghoul2[model].mBltlist); ghoul2[model].mCustomShader = customShader; ghoul2[model].mCustomSkin = customSkin; ghoul2[model].mLodBias = lodBias; ghoul2[model].mAnimFrameDefault = 0; ghoul2[model].mFlags = 0; ghoul2[model].mModelBoltLink = -1; } return ghoul2[model].mModelindex; } qboolean G2API_SetLodBias(CGhoul2Info *ghlInfo, int lodBias) { G2ERROR(ghlInfo,"NULL ghlInfo"); if (ghlInfo) { ghlInfo->mLodBias = lodBias; return qtrue; } return qfalse; } qboolean G2API_SetSkin(CGhoul2Info *ghlInfo, qhandle_t customSkin) { G2ERROR(ghlInfo,"NULL ghlInfo"); if (ghlInfo) { ghlInfo->mCustomSkin = customSkin; return qtrue; } return qfalse; } qboolean G2API_SetShader(CGhoul2Info *ghlInfo, qhandle_t customShader) { G2ERROR(ghlInfo,"NULL ghlInfo"); if (ghlInfo) { ghlInfo->mCustomShader = customShader; return qtrue; } return qfalse; } qboolean G2API_SetSurfaceOnOff(CGhoul2Info *ghlInfo, const char *surfaceName, const int flags) { if (G2_SetupModelPointers(ghlInfo)) { G2ERROR(!(flags&~(G2SURFACEFLAG_OFF | G2SURFACEFLAG_NODESCENDANTS)),"G2API_SetSurfaceOnOff Illegal Flags"); // ensure we flush the cache ghlInfo->mMeshFrameNum = 0; return G2_SetSurfaceOnOff(ghlInfo, ghlInfo->mSlist, surfaceName, flags); } return qfalse; } qboolean G2API_SetRootSurface(CGhoul2Info_v &ghlInfo, const int modelIndex, const char *surfaceName) { G2ERROR(ghlInfo.IsValid(),"Invalid ghlInfo"); G2ERROR(surfaceName,"Invalid surfaceName"); if (G2_SetupModelPointers(ghlInfo)) { G2ERROR(modelIndex>=0&&modelIndex=0&&modelIndexmMeshFrameNum = 0; return G2_AddSurface(ghlInfo, surfaceNumber, polyNumber, BarycentricI, BarycentricJ, lod); } return -1; } qboolean G2API_RemoveSurface(CGhoul2Info *ghlInfo, const int index) { if (G2_SetupModelPointers(ghlInfo)) { // ensure we flush the cache ghlInfo->mMeshFrameNum = 0; return G2_RemoveSurface(ghlInfo->mSlist, index); } return qfalse; } int G2API_GetParentSurface(CGhoul2Info *ghlInfo, const int index) { if (G2_SetupModelPointers(ghlInfo)) { return G2_GetParentSurface(ghlInfo, index); } return -1; } int G2API_GetSurfaceRenderStatus(CGhoul2Info *ghlInfo, const char *surfaceName) { G2ERROR(surfaceName,"Invalid surfaceName"); if (G2_SetupModelPointers(ghlInfo)) { return G2_IsSurfaceRendered(ghlInfo, surfaceName, ghlInfo->mSlist); } return -1; } qboolean G2API_RemoveGhoul2Model(CGhoul2Info_v &ghlInfo, const int modelIndex) { // sanity check if (!ghlInfo.size() || (ghlInfo.size() <= modelIndex) || modelIndex < 0 || (ghlInfo[modelIndex].mModelindex <0)) { // if we hit this assert then we are trying to delete a ghoul2 model on a ghoul2 instance that // one way or another is already gone. G2ERROR(0,"Remove Nonexistant Model"); assert(0 && "remove non existing model"); return qfalse; } RemoveBoneCache(ghlInfo[modelIndex].mBoneCache); ghlInfo[modelIndex].mBoneCache=0; // set us to be the 'not active' state ghlInfo[modelIndex].mModelindex = -1; ghlInfo[modelIndex].mFileName[0]=0; ghlInfo[modelIndex]=CGhoul2Info(); return qtrue; } qboolean G2API_SetBoneAnimIndex(CGhoul2Info *ghlInfo, const int index, const int startFrame, const int endFrame, const int flags, const float animSpeed, const int AcurrentTime, const float setFrame, const int blendTime) { qboolean ret=qfalse; if (G2_SetupModelPointers(ghlInfo)) { G2ERROR(startFrame>=0,"startframe<0"); G2ERROR(startFrameaHeader->numFrames,"startframe>=numframes"); G2ERROR(endFrame>0,"endframe<=0"); G2ERROR(endFrame<=ghlInfo->aHeader->numFrames,"endframe>numframes"); G2ERROR(setFrameaHeader->numFrames,"setframe>=numframes"); G2ERROR(setFrame==-1.0f||setFrame>=0.0f,"setframe<0 but not -1"); if (startFrame<0||startFrame>=ghlInfo->aHeader->numFrames) { *(int *)&startFrame=0; // cast away const } if (endFrame<=0||endFrame>ghlInfo->aHeader->numFrames) { *(int *)&endFrame=1; } if (setFrame!=-1.0f&&(setFrame<0.0f||setFrame>=(float)ghlInfo->aHeader->numFrames)) { *(float *)&setFrame=0.0f; } ghlInfo->mSkelFrameNum = 0; G2ERROR(index>=0&&indexmBlist.size(),va("Out of Range Bone Index (%s)",ghlInfo->mFileName)); if (index>=0&&indexmBlist.size()) { G2ERROR(ghlInfo->mBlist[index].boneNumber>=0,va("Bone Index is not Active (%s)",ghlInfo->mFileName)); int currentTime=G2API_GetTime(AcurrentTime); #if 0 /* if ( ge->ValidateAnimRange( startFrame, endFrame, animSpeed ) == -1 ) { int wtf = 1; } */ #endif ret=G2_Set_Bone_Anim_Index(ghlInfo->mBlist, index, startFrame, endFrame, flags, animSpeed, currentTime, setFrame, blendTime,ghlInfo->aHeader->numFrames); G2ANIM(ghlInfo,"G2API_SetBoneAnimIndex"); } } G2WARNING(ret,va("G2API_SetBoneAnimIndex Failed (%s)",ghlInfo->mFileName)); return ret; } qboolean G2API_SetBoneAnim(CGhoul2Info *ghlInfo, const char *boneName, const int startFrame, const int endFrame, const int flags, const float animSpeed, const int AcurrentTime, const float setFrame, const int blendTime) { qboolean ret=qfalse; G2ERROR(boneName,"NULL boneName"); if (boneName&&G2_SetupModelPointers(ghlInfo)) { G2ERROR(startFrame>=0,"startframe<0"); G2ERROR(startFrameaHeader->numFrames,"startframe>=numframes"); G2ERROR(endFrame>0,"endframe<=0"); G2ERROR(endFrame<=ghlInfo->aHeader->numFrames,"endframe>numframes"); G2ERROR(setFrameaHeader->numFrames,"setframe>=numframes"); G2ERROR(setFrame==-1.0f||setFrame>=0.0f,"setframe<0 but not -1"); if (startFrame<0||startFrame>=ghlInfo->aHeader->numFrames) { *(int *)&startFrame=0; // cast away const } if (endFrame<=0||endFrame>ghlInfo->aHeader->numFrames) { *(int *)&endFrame=1; } if (setFrame!=-1.0f&&(setFrame<0.0f||setFrame>=(float)ghlInfo->aHeader->numFrames)) { *(float *)&setFrame=0.0f; } ghlInfo->mSkelFrameNum = 0; int currentTime=G2API_GetTime(AcurrentTime); ret=G2_Set_Bone_Anim(ghlInfo, ghlInfo->mBlist, boneName, startFrame, endFrame, flags, animSpeed, currentTime, setFrame, blendTime); G2ANIM(ghlInfo,"G2API_SetBoneAnim"); } G2WARNING(ret,"G2API_SetBoneAnim Failed"); return ret; } qboolean G2API_GetBoneAnim(CGhoul2Info *ghlInfo, const char *boneName, const int AcurrentTime, float *currentFrame, int *startFrame, int *endFrame, int *flags, float *animSpeed, int *) { qboolean ret=qfalse; G2ERROR(boneName,"NULL boneName"); if (G2_SetupModelPointers(ghlInfo)) { int currentTime=G2API_GetTime(AcurrentTime); ret=G2_Get_Bone_Anim(ghlInfo, ghlInfo->mBlist, boneName, currentTime, currentFrame, startFrame, endFrame, flags, animSpeed); } G2WARNING(ret,"G2API_GetBoneAnim Failed"); return ret; } qboolean G2API_GetBoneAnimIndex(CGhoul2Info *ghlInfo, const int iBoneIndex, const int AcurrentTime, float *currentFrame, int *startFrame, int *endFrame, int *flags, float *animSpeed, int *) { qboolean ret=qfalse; if (G2_SetupModelPointers(ghlInfo)) { int currentTime=G2API_GetTime(AcurrentTime); G2NOTE(iBoneIndex>=0&&iBoneIndexmBlist.size(),va("Bad Bone Index (%d:%s)",iBoneIndex,ghlInfo->mFileName)); if (iBoneIndex>=0&&iBoneIndexmBlist.size()) { G2NOTE(ghlInfo->mBlist[iBoneIndex].flags & (BONE_ANIM_OVERRIDE_LOOP | BONE_ANIM_OVERRIDE),"GetBoneAnim on non-animating bone."); if ((ghlInfo->mBlist[iBoneIndex].flags & (BONE_ANIM_OVERRIDE_LOOP | BONE_ANIM_OVERRIDE))) { int sf,ef; ret=G2_Get_Bone_Anim_Index( ghlInfo->mBlist,// boneInfo_v &blist, iBoneIndex, // const int index, currentTime, // const int currentTime, currentFrame, // float *currentFrame, &sf, // int *startFrame, &ef, // int *endFrame, flags, // int *flags, animSpeed, // float *retAnimSpeed, ghlInfo->aHeader->numFrames ); G2ERROR(sf>=0,"returning startframe<0"); G2ERROR(sfaHeader->numFrames,"returning startframe>=numframes"); G2ERROR(ef>0,"returning endframe<=0"); G2ERROR(ef<=ghlInfo->aHeader->numFrames,"returning endframe>numframes"); G2ERROR(*currentFrame>=0.0f,"returning currentframe<0"); G2ERROR(((int)(*currentFrame))aHeader->numFrames,"returning currentframe>=numframes"); *endFrame=ef; *startFrame=sf; G2ANIM(ghlInfo,"G2API_GetBoneAnimIndex"); } } } if (!ret) { *endFrame=1; *startFrame=0; *flags=0; *currentFrame=0.0f; *animSpeed=1.0f; } G2NOTE(ret,"G2API_GetBoneAnimIndex Failed"); return ret; } qboolean G2API_GetAnimRange(CGhoul2Info *ghlInfo, const char *boneName, int *startFrame, int *endFrame) { qboolean ret=qfalse; G2ERROR(boneName,"NULL boneName"); if (boneName&&G2_SetupModelPointers(ghlInfo)) { ret=G2_Get_Bone_Anim_Range(ghlInfo, ghlInfo->mBlist, boneName, startFrame, endFrame); G2ANIM(ghlInfo,"G2API_GetAnimRange"); } // looks like the game checks the return value // G2WARNING(ret,"G2API_GetAnimRange Failed"); return ret; } qboolean G2API_GetAnimRangeIndex(CGhoul2Info *ghlInfo, const int boneIndex, int *startFrame, int *endFrame) { qboolean ret=qfalse; if (G2_SetupModelPointers(ghlInfo)) { G2ERROR(boneIndex>=0&&boneIndexmBlist.size(),"Bad Bone Index"); if (boneIndex>=0&&boneIndexmBlist.size()) { ret=G2_Get_Bone_Anim_Range_Index(ghlInfo->mBlist, boneIndex, startFrame, endFrame); G2ANIM(ghlInfo,"G2API_GetAnimRange"); } } // looks like the game checks the return value // G2WARNING(ret,"G2API_GetAnimRangeIndex Failed"); return ret; } qboolean G2API_PauseBoneAnim(CGhoul2Info *ghlInfo, const char *boneName, const int AcurrentTime) { qboolean ret=qfalse; G2ERROR(boneName,"NULL boneName"); if (boneName&&G2_SetupModelPointers(ghlInfo)) { int currentTime=G2API_GetTime(AcurrentTime); ret=G2_Pause_Bone_Anim(ghlInfo, ghlInfo->mBlist, boneName, currentTime); G2ANIM(ghlInfo,"G2API_PauseBoneAnim"); } G2NOTE(ret,"G2API_PauseBoneAnim Failed"); return ret; } qboolean G2API_PauseBoneAnimIndex(CGhoul2Info *ghlInfo, const int boneIndex, const int AcurrentTime) { qboolean ret=qfalse; if (G2_SetupModelPointers(ghlInfo)) { int currentTime=G2API_GetTime(AcurrentTime); G2ERROR(boneIndex>=0&&boneIndexmBlist.size(),"Bad Bone Index"); if (boneIndex>=0&&boneIndexmBlist.size()) { ret=G2_Pause_Bone_Anim_Index(ghlInfo->mBlist, boneIndex, currentTime,ghlInfo->aHeader->numFrames); G2ANIM(ghlInfo,"G2API_PauseBoneAnimIndex"); } } G2WARNING(ret,"G2API_PauseBoneAnimIndex Failed"); return ret; } qboolean G2API_IsPaused(CGhoul2Info *ghlInfo, const char *boneName) { qboolean ret=qfalse; G2ERROR(boneName,"NULL boneName"); if (boneName&&G2_SetupModelPointers(ghlInfo)) { ret=G2_IsPaused(ghlInfo, ghlInfo->mBlist, boneName); } G2WARNING(ret,"G2API_IsPaused Failed"); return ret; } qboolean G2API_StopBoneAnimIndex(CGhoul2Info *ghlInfo, const int index) { qboolean ret=qfalse; G2ERROR(ghlInfo,"NULL ghlInfo"); if (G2_SetupModelPointers(ghlInfo)) { G2ERROR(index>=0&&indexmBlist.size(),"Bad Bone Index"); if (index>=0&&indexmBlist.size()) { ret=G2_Stop_Bone_Anim_Index(ghlInfo->mBlist, index); G2ANIM(ghlInfo,"G2API_StopBoneAnimIndex"); } } //G2WARNING(ret,"G2API_StopBoneAnimIndex Failed"); return ret; } qboolean G2API_StopBoneAnim(CGhoul2Info *ghlInfo, const char *boneName) { qboolean ret=qfalse; G2ERROR(boneName,"NULL boneName"); if (boneName&&G2_SetupModelPointers(ghlInfo)) { ret=G2_Stop_Bone_Anim(ghlInfo, ghlInfo->mBlist, boneName); G2ANIM(ghlInfo,"G2API_StopBoneAnim"); } G2WARNING(ret,"G2API_StopBoneAnim Failed"); return ret; } qboolean G2API_SetBoneAnglesIndex(CGhoul2Info *ghlInfo, const int index, const vec3_t angles, const int flags, const Eorientations yaw, const Eorientations pitch, const Eorientations roll, qhandle_t *, int blendTime, int AcurrentTime) { qboolean ret=qfalse; if (G2_SetupModelPointers(ghlInfo)) { int currentTime=G2API_GetTime(AcurrentTime); // ensure we flush the cache ghlInfo->mSkelFrameNum = 0; G2ERROR(index>=0&&indexmBlist.size(),"G2API_SetBoneAnglesIndex:Invalid bone index"); if (index>=0&&indexmBlist.size()) { ret=G2_Set_Bone_Angles_Index(ghlInfo, ghlInfo->mBlist, index, angles, flags, yaw, pitch, roll,blendTime, currentTime); } } G2WARNING(ret,"G2API_SetBoneAnglesIndex Failed"); return ret; } qboolean G2API_SetBoneAngles(CGhoul2Info *ghlInfo, const char *boneName, const vec3_t angles, const int flags, const Eorientations up, const Eorientations left, const Eorientations forward, qhandle_t *, int blendTime, int AcurrentTime ) { qboolean ret=qfalse; G2ERROR(boneName,"NULL boneName"); if (boneName&&G2_SetupModelPointers(ghlInfo)) { int currentTime=G2API_GetTime(AcurrentTime); // ensure we flush the cache ghlInfo->mSkelFrameNum = 0; ret=G2_Set_Bone_Angles(ghlInfo, ghlInfo->mBlist, boneName, angles, flags, up, left, forward, blendTime, currentTime); } G2WARNING(ret,"G2API_SetBoneAngles Failed"); return ret; } qboolean G2API_SetBoneAnglesMatrixIndex(CGhoul2Info *ghlInfo, const int index, const mdxaBone_t &matrix, const int flags, qhandle_t *, int blendTime, int AcurrentTime) { qboolean ret=qfalse; if (G2_SetupModelPointers(ghlInfo)) { int currentTime=G2API_GetTime(AcurrentTime); // ensure we flush the cache ghlInfo->mSkelFrameNum = 0; G2ERROR(index>=0&&indexmBlist.size(),"Bad Bone Index"); if (index>=0&&indexmBlist.size()) { ret=G2_Set_Bone_Angles_Matrix_Index(ghlInfo->mBlist, index, matrix, flags, blendTime, currentTime); } } G2WARNING(ret,"G2API_SetBoneAnglesMatrixIndex Failed"); return ret; } qboolean G2API_SetBoneAnglesMatrix(CGhoul2Info *ghlInfo, const char *boneName, const mdxaBone_t &matrix, const int flags, qhandle_t *modelList, int blendTime, int AcurrentTime) { qboolean ret=qfalse; G2ERROR(boneName,"NULL boneName"); if (boneName&&G2_SetupModelPointers(ghlInfo)) { int currentTime=G2API_GetTime(AcurrentTime); // ensure we flush the cache ghlInfo->mSkelFrameNum = 0; ret=G2_Set_Bone_Angles_Matrix(ghlInfo, ghlInfo->mBlist, boneName, matrix, flags, blendTime, currentTime); } G2WARNING(ret,"G2API_SetBoneAnglesMatrix Failed"); return ret; } qboolean G2API_StopBoneAnglesIndex(CGhoul2Info *ghlInfo, const int index) { qboolean ret=qfalse; if (G2_SetupModelPointers(ghlInfo)) { // ensure we flush the cache ghlInfo->mSkelFrameNum = 0; G2ERROR(index>=0&&indexmBlist.size(),"Bad Bone Index"); if (index>=0&&indexmBlist.size()) { ret=G2_Stop_Bone_Angles_Index(ghlInfo->mBlist, index); } } G2WARNING(ret,"G2API_StopBoneAnglesIndex Failed"); return ret; } qboolean G2API_StopBoneAngles(CGhoul2Info *ghlInfo, const char *boneName) { qboolean ret=qfalse; G2ERROR(boneName,"NULL boneName"); if (boneName&&G2_SetupModelPointers(ghlInfo)) { // ensure we flush the cache ghlInfo->mSkelFrameNum = 0; ret=G2_Stop_Bone_Angles(ghlInfo, ghlInfo->mBlist, boneName); } G2WARNING(ret,"G2API_StopBoneAngles Failed"); return ret; } qboolean G2API_RemoveBone(CGhoul2Info *ghlInfo, const char *boneName) { qboolean ret=qfalse; G2ERROR(boneName,"NULL boneName"); if (boneName&&G2_SetupModelPointers(ghlInfo)) { // ensure we flush the cache ghlInfo->mSkelFrameNum = 0; ret=G2_Remove_Bone(ghlInfo, ghlInfo->mBlist, boneName); G2ANIM(ghlInfo,"G2API_RemoveBone"); } G2WARNING(ret,"G2API_RemoveBone Failed"); return ret; } void G2API_AnimateG2Models(CGhoul2Info_v &, float) { } qboolean G2API_RemoveBolt(CGhoul2Info *ghlInfo, const int index) { qboolean ret=qfalse; if (G2_SetupModelPointers(ghlInfo)) { ret=G2_Remove_Bolt( ghlInfo->mBltlist, index); } G2WARNING(ret,"G2API_RemoveBolt Failed"); return ret; } int G2API_AddBolt(CGhoul2Info *ghlInfo, const char *boneName) { int ret=-1; G2ERROR(boneName,"NULL boneName"); if (boneName&&G2_SetupModelPointers(ghlInfo)) { ret=G2_Add_Bolt(ghlInfo, ghlInfo->mBltlist, ghlInfo->mSlist, boneName); G2NOTE(ret>=0,va("G2API_AddBolt Failed (%s:%s)",boneName,ghlInfo->mFileName)); } return ret; } int G2API_AddBoltSurfNum(CGhoul2Info *ghlInfo, const int surfIndex) { int ret=-1; if (G2_SetupModelPointers(ghlInfo)) { ret=G2_Add_Bolt_Surf_Num(ghlInfo, ghlInfo->mBltlist, ghlInfo->mSlist, surfIndex); } G2WARNING(ret>=0,"G2API_AddBoltSurfNum Failed"); return ret; } qboolean G2API_AttachG2Model(CGhoul2Info *ghlInfo, CGhoul2Info *ghlInfoTo, int toBoltIndex, int toModel) { qboolean ret=qfalse; if (G2_SetupModelPointers(ghlInfo)&&G2_SetupModelPointers(ghlInfoTo)) { G2ERROR(toBoltIndex>=0&&toBoltIndexmBltlist.size(),"Invalid Bolt Index"); assert( toBoltIndex >= 0 ); if ( toBoltIndex >= 0 ) { // make sure we have a model to attach, a model to attach to, and a bolt on that model if (((ghlInfoTo->mBltlist[toBoltIndex].boneNumber != -1) || (ghlInfoTo->mBltlist[toBoltIndex].surfaceNumber != -1))) { // encode the bolt address into the model bolt link toModel &= MODEL_AND; toBoltIndex &= BOLT_AND; ghlInfo->mModelBoltLink = (toModel << MODEL_SHIFT) | (toBoltIndex << BOLT_SHIFT); ret=qtrue; } } } G2WARNING(ret,"G2API_AttachG2Model Failed"); return ret; } qboolean G2API_DetachG2Model(CGhoul2Info *ghlInfo) { if (G2_SetupModelPointers(ghlInfo)) { ghlInfo->mModelBoltLink = -1; return qtrue; } return qfalse; } qboolean G2API_AttachEnt(int *boltInfo, CGhoul2Info *ghlInfoTo, int toBoltIndex, int entNum, int toModelNum) { qboolean ret=qfalse; G2ERROR(boltInfo,"NULL boltInfo"); if (boltInfo&&G2_SetupModelPointers(ghlInfoTo)) { // make sure we have a model to attach, a model to attach to, and a bolt on that model if (((ghlInfoTo->mBltlist[toBoltIndex].boneNumber != -1) || (ghlInfoTo->mBltlist[toBoltIndex].surfaceNumber != -1))) { // encode the bolt address into the model bolt link toModelNum &= MODEL_AND; toBoltIndex &= BOLT_AND; entNum &= ENTITY_AND; *boltInfo = (toBoltIndex << BOLT_SHIFT) | (toModelNum << MODEL_SHIFT) | (entNum << ENTITY_SHIFT); ret=qtrue; } } G2WARNING(ret,"G2API_AttachEnt Failed"); return ret; } void G2API_DetachEnt(int *boltInfo) { G2ERROR(boltInfo,"NULL boltInfo"); if (boltInfo) { *boltInfo = 0; } } bool G2_NeedsRecalc(CGhoul2Info *ghlInfo,int frameNum); qboolean G2API_GetBoltMatrix(CGhoul2Info_v &ghoul2, const int modelIndex, const int boltIndex, mdxaBone_t *matrix, const vec3_t angles, const vec3_t position, const int AframeNum, qhandle_t *modelList, const vec3_t scale ) { G2ERROR(ghoul2.IsValid(),"Invalid ghlInfo"); G2ERROR(matrix,"NULL matrix"); G2ERROR(modelIndex>=0&&modelIndex=0&&modelIndex= 0 && (boltIndex < ghlInfo->mBltlist.size()),va("Invalid Bolt Index (%d:%s)",boltIndex,ghlInfo->mFileName)); if (boltIndex >= 0 && ghlInfo && (boltIndex < ghlInfo->mBltlist.size()) ) { mdxaBone_t bolt; if (G2_NeedsRecalc(ghlInfo,frameNum)) { G2_ConstructGhoulSkeleton(ghoul2,frameNum,true,scale); } vec3_t newScale; VectorClear(newScale); G2_GetBoltMatrixLow(*ghlInfo,boltIndex,scale,bolt); // scale the bolt position by the scale factor for this model since at this point its still in model space if (scale[0]) { bolt.matrix[0][3] *= scale[0]; } if (scale[1]) { bolt.matrix[1][3] *= scale[1]; } if (scale[2]) { bolt.matrix[2][3] *= scale[2]; } VectorNormalize((float*)&bolt.matrix[0]); VectorNormalize((float*)&bolt.matrix[1]); VectorNormalize((float*)&bolt.matrix[2]); Multiply_3x4Matrix(matrix, &worldMatrix, &bolt); #if G2API_DEBUG for ( int i = 0; i < 3; i++ ) { for ( int j = 0; j < 4; j++ ) { assert( !_isnan(matrix->matrix[i][j])); } } #endif// _DEBUG G2ANIM(ghlInfo,"G2API_GetBoltMatrix"); return qtrue; } } } else { G2WARNING(0,"G2API_GetBoltMatrix Failed on empty or bad model"); } Multiply_3x4Matrix(matrix, &worldMatrix, &identityMatrix); return qfalse; } void G2API_ListSurfaces(CGhoul2Info *ghlInfo) { if (G2_SetupModelPointers(ghlInfo)) { G2_List_Model_Surfaces(ghlInfo->mFileName); } } void G2API_ListBones(CGhoul2Info *ghlInfo, int frame) { if (G2_SetupModelPointers(ghlInfo)) { G2_List_Model_Bones(ghlInfo->mFileName, frame); } } // decide if we have Ghoul2 models associated with this ghoul list or not qboolean G2API_HaveWeGhoul2Models(CGhoul2Info_v &ghoul2) { return !!ghoul2.IsValid(); } // run through the Ghoul2 models and set each of the mModel values to the correct one from the cgs.gameModel offset lsit void G2API_SetGhoul2ModelIndexes(CGhoul2Info_v &ghoul2, qhandle_t *modelList, qhandle_t *skinList) { G2ERROR(ghoul2.IsValid(),"Invalid ghlInfo"); int i; for (i=0; imdxm,"Bad Model"); if (mod_m&&mod_m->mdxm) { return mod_m->mdxm->animName; } return ""; } /************************************************************************************************ * G2API_GetAnimFileName * obtains the name of a model's .gla (animation) file * * Input * pointer to list of CGhoul2Info's, WraithID of specific model in that list * * Output * true if a good filename was obtained, false otherwise * ************************************************************************************************/ qboolean G2API_GetAnimFileName(CGhoul2Info *ghlInfo, char **filename) { qboolean ret=qfalse; if (G2_SetupModelPointers(ghlInfo)) { ret=G2_GetAnimFileName(ghlInfo->mFileName, filename); } G2WARNING(ret,"G2API_GetAnimFileName Failed"); return ret; } /* ======================= SV_QsortEntityNumbers ======================= */ static int QDECL QsortDistance( const void *a, const void *b ) { const float &ea = ((CCollisionRecord*)a)->mDistance; const float &eb = ((CCollisionRecord*)b)->mDistance; if ( ea < eb ) { return -1; } return 1; } void G2API_CollisionDetect(CCollisionRecord *collRecMap, CGhoul2Info_v &ghoul2, const vec3_t angles, const vec3_t position, int AframeNumber, int entNum, vec3_t rayStart, vec3_t rayEnd, vec3_t scale, CMiniHeap *, EG2_Collision eG2TraceType, int useLod, float fRadius) { G2ERROR(ghoul2.IsValid(),"Invalid ghlInfo"); G2ERROR(collRecMap,"NULL Collision Rec"); if (G2_SetupModelPointers(ghoul2)&&collRecMap) { int frameNumber=G2API_GetTime(AframeNumber); vec3_t transRayStart, transRayEnd; // make sure we have transformed the whole skeletons for each model G2_ConstructGhoulSkeleton(ghoul2, frameNumber,true, scale); // pre generate the world matrix - used to transform the incoming ray G2_GenerateWorldMatrix(angles, position); G2VertSpaceServer->ResetHeap(); // now having done that, time to build the model G2_TransformModel(ghoul2, frameNumber, scale,G2VertSpaceServer, useLod); // model is built. Lets check to see if any triangles are actually hit. // first up, translate the ray to model space TransformAndTranslatePoint(rayStart, transRayStart, &worldMatrixInv); TransformAndTranslatePoint(rayEnd, transRayEnd, &worldMatrixInv); // now walk each model and check the ray against each poly - sigh, this is SO expensive. I wish there was a better way to do this. G2_TraceModels(ghoul2, transRayStart, transRayEnd, collRecMap, entNum, eG2TraceType, useLod, fRadius); G2VertSpaceServer->ResetHeap(); // now sort the resulting array of collision records so they are distance ordered qsort( collRecMap, MAX_G2_COLLISIONS, sizeof( CCollisionRecord ), QsortDistance ); G2ANIM(ghoul2,"G2API_CollisionDetect"); } } qboolean G2API_SetGhoul2ModelFlags(CGhoul2Info *ghlInfo, const int flags) { if (G2_SetupModelPointers(ghlInfo)) { ghlInfo->mFlags &= GHOUL2_NEWORIGIN; ghlInfo->mFlags |= flags; return qtrue; } return qfalse; } int G2API_GetGhoul2ModelFlags(CGhoul2Info *ghlInfo) { if (G2_SetupModelPointers(ghlInfo)) { return (ghlInfo->mFlags & ~GHOUL2_NEWORIGIN); } return 0; } // given a boltmatrix, return in vec a normalised vector for the axis requested in flags void G2API_GiveMeVectorFromMatrix(mdxaBone_t &boltMatrix, Eorientations flags, vec3_t &vec) { switch (flags) { case ORIGIN: vec[0] = boltMatrix.matrix[0][3]; vec[1] = boltMatrix.matrix[1][3]; vec[2] = boltMatrix.matrix[2][3]; break; case POSITIVE_Y: vec[0] = boltMatrix.matrix[0][1]; vec[1] = boltMatrix.matrix[1][1]; vec[2] = boltMatrix.matrix[2][1]; break; case POSITIVE_X: vec[0] = boltMatrix.matrix[0][0]; vec[1] = boltMatrix.matrix[1][0]; vec[2] = boltMatrix.matrix[2][0]; break; case POSITIVE_Z: vec[0] = boltMatrix.matrix[0][2]; vec[1] = boltMatrix.matrix[1][2]; vec[2] = boltMatrix.matrix[2][2]; break; case NEGATIVE_Y: vec[0] = -boltMatrix.matrix[0][1]; vec[1] = -boltMatrix.matrix[1][1]; vec[2] = -boltMatrix.matrix[2][1]; break; case NEGATIVE_X: vec[0] = -boltMatrix.matrix[0][0]; vec[1] = -boltMatrix.matrix[1][0]; vec[2] = -boltMatrix.matrix[2][0]; break; case NEGATIVE_Z: vec[0] = -boltMatrix.matrix[0][2]; vec[1] = -boltMatrix.matrix[1][2]; vec[2] = -boltMatrix.matrix[2][2]; break; } } // copy a model from one ghoul2 instance to another, and reset the root surface on the new model if need be // NOTE if modelIndex = -1 then copy all the models void G2API_CopyGhoul2Instance(CGhoul2Info_v &ghoul2From, CGhoul2Info_v &ghoul2To, int modelIndex) { assert(modelIndex==-1); // copy individual bolted parts is not used in jk2 and I didn't want to deal with it // if ya want it, we will add it back correctly G2ERROR(ghoul2From.IsValid(),"Invalid ghlInfo"); if (ghoul2From.IsValid()) { ghoul2To.DeepCopy(ghoul2From); G2ANIM(ghoul2From,"G2API_CopyGhoul2Instance (source)"); G2ANIM(ghoul2To,"G2API_CopyGhoul2Instance (dest)"); } } char *G2API_GetSurfaceName(CGhoul2Info *ghlInfo, int surfNumber) { static char noSurface[1] = ""; if (G2_SetupModelPointers(ghlInfo)) { mdxmSurface_t *surf = 0; mdxmSurfHierarchy_t *surfInfo = 0; surf = (mdxmSurface_t *)G2_FindSurface(ghlInfo->currentModel, surfNumber, 0); if (surf) { assert(G2_MODEL_OK(ghlInfo)); mdxmHierarchyOffsets_t *surfIndexes = (mdxmHierarchyOffsets_t *)((byte *)ghlInfo->currentModel->mdxm + sizeof(mdxmHeader_t)); surfInfo = (mdxmSurfHierarchy_t *)((byte *)surfIndexes + surfIndexes->offsets[surf->thisSurfaceIndex]); return surfInfo->name; } } G2WARNING(0,"Surface Not Found"); return noSurface; } int G2API_GetSurfaceIndex(CGhoul2Info *ghlInfo, const char *surfaceName) { int ret=-1; G2ERROR(surfaceName,"NULL surfaceName"); if (surfaceName&&G2_SetupModelPointers(ghlInfo)) { ret=G2_GetSurfaceIndex(ghlInfo, surfaceName); } G2WARNING(ret>=0,"G2API_GetSurfaceIndex Failed"); return ret; } char *G2API_GetGLAName(CGhoul2Info *ghlInfo) { if (G2_SetupModelPointers(ghlInfo)) { assert(G2_MODEL_OK(ghlInfo)); return ghlInfo->currentModel->mdxm->animName; } return 0; } qboolean G2API_SetNewOrigin(CGhoul2Info *ghlInfo, const int boltIndex) { if (G2_SetupModelPointers(ghlInfo)) { G2ERROR(boltIndex>=0&&boltIndexmBltlist.size(),"NULL ghlInfo"); if (boltIndex>=0&&boltIndexmBltlist.size()) { ghlInfo->mNewOrigin = boltIndex; ghlInfo->mFlags |= GHOUL2_NEWORIGIN; } return qtrue; } return qfalse; } int G2API_GetBoneIndex(CGhoul2Info *ghlInfo, const char *boneName, qboolean bAddIfNotFound) { int ret=-1; G2ERROR(boneName,"NULL boneName"); if (boneName&&G2_SetupModelPointers(ghlInfo)) { ret=G2_Get_Bone_Index(ghlInfo, boneName, bAddIfNotFound); G2ANIM(ghlInfo,"G2API_GetBoneIndex"); } G2NOTE(ret>=0,"G2API_GetBoneIndex Failed"); return ret; } qboolean G2API_SaveGhoul2Models(CGhoul2Info_v &ghoul2, char **buffer, int *size) { G2ANIM(ghoul2,"G2API_SaveGhoul2Models"); return G2_SaveGhoul2Models(ghoul2, buffer, size); } void G2API_LoadGhoul2Models(CGhoul2Info_v &ghoul2, char *buffer) { G2_LoadGhoul2Model(ghoul2, buffer); G2ANIM(ghoul2,"G2API_LoadGhoul2Models"); // G2ERROR(ghoul2.IsValid(),"Invalid ghlInfo after load"); } void G2API_FreeSaveBuffer(char *buffer) { Z_Free(buffer); } // this is kinda sad, but I need to call the destructor in this module (exe), not the game.dll... // void G2API_LoadSaveCodeDestructGhoul2Info(CGhoul2Info_v &ghoul2) { ghoul2.~CGhoul2Info_v(); // so I can load junk over it then memset to 0 without orphaning } bool G2_TestModelPointers(CGhoul2Info *ghlInfo) // returns true if the model is properly set up { G2ERROR(ghlInfo,"NULL ghlInfo"); if (!ghlInfo) { return false; } ghlInfo->mValid=false; if (ghlInfo->mModelindex != -1) { ghlInfo->mModel = RE_RegisterModel(ghlInfo->mFileName); ghlInfo->currentModel = R_GetModelByHandle(ghlInfo->mModel); if (ghlInfo->currentModel) { if (ghlInfo->currentModel->mdxm) { if (ghlInfo->currentModelSize) { if (ghlInfo->currentModelSize!=ghlInfo->currentModel->mdxm->ofsEnd) { Com_Error(ERR_DROP, "Ghoul2 model was reloaded and has changed, map must be restarted.\n"); } } ghlInfo->currentModelSize=ghlInfo->currentModel->mdxm->ofsEnd; ghlInfo->animModel = R_GetModelByHandle(ghlInfo->currentModel->mdxm->animIndex); if (ghlInfo->animModel) { ghlInfo->aHeader =ghlInfo->animModel->mdxa; if (ghlInfo->aHeader) { if (ghlInfo->currentAnimModelSize) { if (ghlInfo->currentAnimModelSize!=ghlInfo->aHeader->ofsEnd) { Com_Error(ERR_DROP, "Ghoul2 model was reloaded and has changed, map must be restarted.\n"); } } ghlInfo->currentAnimModelSize=ghlInfo->aHeader->ofsEnd; ghlInfo->mValid=true; } } } } } if (!ghlInfo->mValid) { ghlInfo->currentModel=0; ghlInfo->currentModelSize=0; ghlInfo->animModel=0; ghlInfo->currentAnimModelSize=0; ghlInfo->aHeader=0; } return ghlInfo->mValid; } bool G2_SetupModelPointers(CGhoul2Info *ghlInfo) // returns true if the model is properly set up { G2ERROR(ghlInfo,"NULL ghlInfo"); if (!ghlInfo) { return false; } ghlInfo->mValid=false; // G2WARNING(ghlInfo->mModelindex != -1,"Setup request on non-used info slot?"); if (ghlInfo->mModelindex != -1) { G2ERROR(ghlInfo->mFileName[0],"empty ghlInfo->mFileName"); ghlInfo->mModel = RE_RegisterModel(ghlInfo->mFileName); ghlInfo->currentModel = R_GetModelByHandle(ghlInfo->mModel); G2ERROR(ghlInfo->currentModel,va("NULL Model (glm) %s",ghlInfo->mFileName)); if (ghlInfo->currentModel) { G2ERROR(ghlInfo->currentModel->mdxm,va("Model has no mdxm (glm) %s",ghlInfo->mFileName)); if (ghlInfo->currentModel->mdxm) { if (ghlInfo->currentModelSize) { if (ghlInfo->currentModelSize!=ghlInfo->currentModel->mdxm->ofsEnd) { Com_Error(ERR_DROP, "Ghoul2 model was reloaded and has changed, map must be restarted.\n"); } } ghlInfo->currentModelSize=ghlInfo->currentModel->mdxm->ofsEnd; G2ERROR(ghlInfo->currentModelSize,va("Zero sized Model? (glm) %s",ghlInfo->mFileName)); ghlInfo->animModel = R_GetModelByHandle(ghlInfo->currentModel->mdxm->animIndex); G2ERROR(ghlInfo->animModel,va("NULL Model (gla) %s",ghlInfo->mFileName)); if (ghlInfo->animModel) { ghlInfo->aHeader =ghlInfo->animModel->mdxa; G2ERROR(ghlInfo->aHeader,va("Model has no mdxa (gla) %s",ghlInfo->mFileName)); if (ghlInfo->aHeader) { if (ghlInfo->currentAnimModelSize) { if (ghlInfo->currentAnimModelSize!=ghlInfo->aHeader->ofsEnd) { Com_Error(ERR_DROP, "Ghoul2 model was reloaded and has changed, map must be restarted.\n"); } } ghlInfo->currentAnimModelSize=ghlInfo->aHeader->ofsEnd; G2ERROR(ghlInfo->currentAnimModelSize,va("Zero sized Model? (gla) %s",ghlInfo->mFileName)); ghlInfo->mValid=true; } } } } } if (!ghlInfo->mValid) { ghlInfo->currentModel=0; ghlInfo->currentModelSize=0; ghlInfo->animModel=0; ghlInfo->currentAnimModelSize=0; ghlInfo->aHeader=0; } return ghlInfo->mValid; } bool G2_SetupModelPointers(CGhoul2Info_v &ghoul2) // returns true if any model is properly set up { bool ret=false; int i; for (i=0; i