// 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 using namespace std; extern mdxaBone_t worldMatrix; extern mdxaBone_t worldMatrixInv; extern cvar_t *r_Ghoul2TimeBase; #define G2_DEBUG_TIME (0) static int G2TimeBases[NUM_G2T_TIME]; #if _DEBUG #define MAX_ERROR_PRINTS (25) 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 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) { full+=":WARNING: "; } else { 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) ) #else #define G2ERROR(exp,m) ((void)0) #define G2WARNING(exp,m) ((void)0) #define G2NOTE(exp,m) ((void)0) #endif #ifdef _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) { G2WARNING(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) { G2WARNING(0,va("Bolt/Bone Not Found (%s:%s)",boneName,modName)); } } #endif void G2API_SetTime(int currentTime,int clock) { assert(clock>=0&&clockinteger>0&&r_Ghoul2TimeBase->integer<=NUM_G2T_TIME) { timeBase=r_Ghoul2TimeBase->integer; ret=G2TimeBases[r_Ghoul2TimeBase->integer-1]; } #if _DEBUG if (LastTimeBase!=timeBase) { LastTimeBase=timeBase; if (timeBase==0) { Com_Printf("Ghoul2: Using argument time\n"); } if (timeBase==1) { Com_Printf("Ghoul2: Using server time\n"); } if (timeBase==2) { Com_Printf("Ghoul2: Using client time\n"); } } #endif #if G2_DEBUG_TIME Com_Printf("Ghoul2: Get Time: in%6d c%6d s%6d returning %6d\n",argTime,G2TimeBases[1],G2TimeBases[0],ret); #endif 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 void VidRestart() { int j; for (j=0;j &ghoul2=mInfos[j]; int i; for (i = 0; i < ghoul2.size(); i++) { // if we have a name, re-register us so the model thinks it's in the correct place. if (ghoul2[i].mFileName[0]) { ghoul2[i].mModel = RE_RegisterModel(ghoul2[i].mFileName); assert(ghoul2[i].mModel); } } } } 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]; } }; IGhoul2InfoArray &TheGhoul2InfoArray() { static Ghoul2InfoArray singleton; return singleton; } // 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 modelIndex, qhandle_t customSkin, qhandle_t customShader, int modelFlags, int lodBias) { int model = -1; CGhoul2Info newModel; 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) { // this is only valid and used on the game side. Client side ignores this ghoul2[model].mModelindex = modelIndex; // on the game side this is valid. On the client side it is valid only after it has been filled in by trap_G2_SetGhoul2ModelIndexes ghoul2[model].mModel = RE_RegisterModel((char *)fileName); model_t *mod_m = R_GetModelByHandle(ghoul2[model].mModel); G2ERROR(mod_m->type != MOD_BAD,va("Bad Model Type (%s)",fileName)); if (mod_m->type == MOD_BAD) { return -1; } // init what is necessary for this ghoul2 model G2_Init_Bone_List(ghoul2[model].mBlist); G2_Init_Bolt_List(ghoul2[model].mBltlist); ghoul2[model].mCustomShader = customShader; ghoul2[model].mCustomSkin = customSkin; strcpy(ghoul2[model].mFileName, fileName); ghoul2[model].mLodBias = lodBias; ghoul2[model].mAnimFrameDefault = 0; ghoul2[model].mFlags = 0; // we aren't attached to anyone upfront ghoul2[model].mModelBoltLink = -1; return model; } } // if we got this far, then we didn't find a spare position, so lets insert a new one newModel.mModelindex = modelIndex; // on the game side this is valid. On the client side it is valid only after it has been filled in by trap_G2_SetGhoul2ModelIndexes newModel.mModel = RE_RegisterModel((char *)fileName); model_t *mod_m = R_GetModelByHandle(newModel.mModel); G2ERROR(mod_m->type != MOD_BAD,va("Bad Model Type (%s)",fileName)); if (mod_m->type == MOD_BAD) { return -1; } // init what is necessary for this ghoul2 model G2_Init_Bone_List(newModel.mBlist); G2_Init_Bolt_List(newModel.mBltlist); newModel.mCustomShader = customShader; newModel.mCustomSkin = customSkin; strcpy(newModel.mFileName, fileName); newModel.mLodBias = lodBias; newModel.mAnimFrameDefault = 0; newModel.mFlags = 0; // we aren't attached to anyone upfront newModel.mModelBoltLink = -1; // insert into list. model = ghoul2.size(); ghoul2.push_back(newModel); return model; } 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) { G2ERROR(ghlInfo,"NULL ghlInfo"); if (ghlInfo) { // ensure we flush the cache ghlInfo->mMeshFrameNum = 0; return G2_SetSurfaceOnOff(ghlInfo->mFileName, ghlInfo->mSlist, surfaceName, flags); } return qfalse; } int G2API_GetSurfaceOnOff(CGhoul2Info *ghlInfo, const char *surfaceName) { G2ERROR(ghlInfo,"NULL ghlInfo"); if (ghlInfo) { return G2_IsSurfaceOff(ghlInfo->mFileName, ghlInfo->mSlist, surfaceName); } return -1; } qboolean G2API_SetRootSurface(CGhoul2Info_v &ghlInfo, const int modelIndex, const char *surfaceName) { G2ERROR(ghlInfo.IsValid(),"Invalid ghlInfo"); G2ERROR(surfaceName,"Invalid surfaceName"); if (ghlInfo.IsValid()) { 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) { G2ERROR(ghlInfo,"NULL ghlInfo"); if (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) { G2ERROR(ghlInfo,"NULL ghlInfo"); if (ghlInfo) { return G2_GetParentSurface(ghlInfo->mFileName, index); } return -1; } int G2API_GetSurfaceRenderStatus(CGhoul2Info *ghlInfo, const char *surfaceName) { G2ERROR(ghlInfo,"NULL ghlInfo"); G2ERROR(surfaceName,"Invalid surfaceName"); if (ghlInfo) { return G2_IsSurfaceRendered(ghlInfo->mFileName, surfaceName, ghlInfo->mSlist); } return -1; } qboolean G2API_RemoveGhoul2Model(CGhoul2Info_v &ghlInfo, const int modelIndex) { // sanity check if (!ghlInfo.size() || (ghlInfo.size() <= modelIndex) || (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; } // clear out the vectors this model used. ghlInfo[modelIndex].mBlist.clear(); ghlInfo[modelIndex].mBltlist.clear(); ghlInfo[modelIndex].mSlist.clear(); RemoveBoneCache(ghlInfo[modelIndex].mBoneCache); ghlInfo[modelIndex].mBoneCache=0; // set us to be the 'not active' state ghlInfo[modelIndex].mModelindex = -1; int newSize = ghlInfo.size(); // now look through the list from the back and see if there is a block of -1's we can resize off the end of the list for (int i=ghlInfo.size()-1; i>-1; i--) { if (ghlInfo[i].mModelindex == -1) { newSize = i; } // once we hit one that isn't a -1, we are done. else { break; } } // do we need to resize? if (newSize != ghlInfo.size()) { // yes, so lets do it ghlInfo.resize(newSize); } return qtrue; } qboolean G2API_SetBoneAnimIndex(CGhoul2Info *ghlInfo, const int index, const int startFrame, const int endFrame, const int flags, const float animSpeed, const int currentTime, const float setFrame, const int blendTime) { qboolean ret=qfalse; G2ERROR(ghlInfo,"NULL ghlInfo"); if (ghlInfo) { #if _DEBUG ghlInfo->mModel = RE_RegisterModel(ghlInfo->mFileName); const model_t *currentModel = R_GetModelByHandle(ghlInfo->mModel); G2ERROR(currentModel&¤tModel->mdxm,va("Bad Model (glm) %s",ghlInfo->mFileName)); if (currentModel&¤tModel->mdxm) { const model_t *animModel = R_GetModelByHandle(currentModel->mdxm->animIndex); G2ERROR(animModel,va("Bad Model (gla) %s",ghlInfo->mFileName)); if (animModel) { const mdxaHeader_t *aHeader = animModel->mdxa; G2ERROR(aHeader,va("Bad Model (gla) %s",ghlInfo->mFileName)); if (aHeader) { G2ERROR(startFrame>=0,"startframe<0"); G2ERROR(startFramenumFrames,"startframe>=numframes"); G2ERROR(endFrame>0,"endframe<=0"); G2ERROR(endFrame<=aHeader->numFrames,"endframe>numframes"); G2ERROR(setFramenumFrames,"setframe>=numframes"); G2ERROR(setFrame==-1.0f||setFrame>=0.0f,"setframe<0 but not -1"); #else { { { #endif if (startFrame<0)//||startFrame>=aHeader->numFrames) { *(int *)&startFrame=0; // cast away const } if (endFrame<=0)//||endFrame>aHeader->numFrames) { *(int *)&endFrame=1; } if (setFrame!=-1.0f&&(setFrame<0.0f/*||setFrame>=(float)aHeader->numFrames*/)) { *(float *)&setFrame=0.0f; } ghlInfo->mSkelFrameNum = 0; G2ERROR(index>=0&&indexmBlist.size(),"Bad Bone Index"); if (index>=0&&indexmBlist.size()) { G2ERROR(ghlInfo->mBlist[index].boneNumber>=0,"Bad Bone Index"); ret=G2_Set_Bone_Anim_Index(ghlInfo->mBlist, index, startFrame, endFrame, flags, animSpeed, currentTime, setFrame, blendTime); } } } } } G2WARNING(ret,"G2API_SetBoneAnimIndex Failed"); return ret; } qboolean G2API_SetBoneAnim(CGhoul2Info *ghlInfo, const char *boneName, const int startFrame, const int endFrame, const int flags, const float animSpeed, const int currentTime, const float setFrame, const int blendTime) { qboolean ret=qfalse; G2ERROR(ghlInfo,"NULL ghlInfo"); G2ERROR(boneName,"NULL boneName"); if (ghlInfo&&boneName) { #if _DEBUG ghlInfo->mModel = RE_RegisterModel(ghlInfo->mFileName); const model_t *currentModel = R_GetModelByHandle(ghlInfo->mModel); G2ERROR(currentModel&¤tModel->mdxm,va("Bad Model (glm) %s",ghlInfo->mFileName)); if (currentModel&¤tModel->mdxm) { const model_t *animModel = R_GetModelByHandle(currentModel->mdxm->animIndex); G2ERROR(animModel,va("Bad Model (gla) %s",ghlInfo->mFileName)); if (animModel) { const mdxaHeader_t *aHeader = animModel->mdxa; G2ERROR(aHeader,va("Bad Model (gla) %s",ghlInfo->mFileName)); if (aHeader) { G2ERROR(startFrame>=0,"startframe<0"); G2ERROR(startFramenumFrames,"startframe>=numframes"); G2ERROR(endFrame>0,"endframe<=0"); G2ERROR(endFrame<=aHeader->numFrames,"endframe>numframes"); G2ERROR(setFramenumFrames,"setframe>=numframes"); G2ERROR(setFrame==-1.0f||setFrame>=0.0f,"setframe<0 but not -1"); #else { { { #endif if (startFrame<0)//||startFrame>=aHeader->numFrames) { *(int *)&startFrame=0; // cast away const } if (endFrame<=0)//||endFrame>aHeader->numFrames) { *(int *)&endFrame=1; } if (setFrame!=-1.0f&&(setFrame<0.0f/*||setFrame>=(float)aHeader->numFrames*/)) { *(float *)&setFrame=0.0f; } // ensure we flush the cache ghlInfo->mSkelFrameNum = 0; ret=G2_Set_Bone_Anim(ghlInfo->mFileName, ghlInfo->mBlist, boneName, startFrame, endFrame, flags, animSpeed, currentTime, setFrame, blendTime); } } } } 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 *modelList) { qboolean ret=qfalse; G2ERROR(ghlInfo,"NULL ghlInfo"); G2ERROR(boneName,"NULL boneName"); int currentTime=G2API_GetTime(AcurrentTime); if (ghlInfo&&boneName) { ret=G2_Get_Bone_Anim(ghlInfo->mFileName, ghlInfo->mBlist, boneName, currentTime, currentFrame, startFrame, endFrame, flags, animSpeed, modelList, ghlInfo->mModelindex); } 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 *modelList) { qboolean ret=qfalse; G2ERROR(ghlInfo,"NULL ghlInfo"); int currentTime=G2API_GetTime(AcurrentTime); if (ghlInfo) { G2ERROR(iBoneIndex>=0&&iBoneIndexmBlist.size(),"Bad Bone Index"); G2WARNING(ghlInfo->mBlist[iBoneIndex].flags & (BONE_ANIM_OVERRIDE_LOOP | BONE_ANIM_OVERRIDE),"GetBoneAnim on non-animating bone."); if (iBoneIndex>=0&&iBoneIndexmBlist.size()&&(ghlInfo->mBlist[iBoneIndex].flags & (BONE_ANIM_OVERRIDE_LOOP | BONE_ANIM_OVERRIDE))) { ret=G2_Get_Bone_Anim_Index( ghlInfo->mBlist,// boneInfo_v &blist, iBoneIndex, // const int index, currentTime, // const int currentTime, currentFrame, // float *currentFrame, startFrame, // int *startFrame, endFrame, // int *endFrame, flags, // int *flags, animSpeed, // float *retAnimSpeed, modelList, // qhandle_t *modelList, ghlInfo->mModelindex // int modelIndex ); } } G2NOTE(ret,"G2API_GetBoneAnimIndex Failed"); return ret; } qboolean G2API_GetAnimRange(CGhoul2Info *ghlInfo, const char *boneName, int *startFrame, int *endFrame) { qboolean ret=qfalse; G2ERROR(ghlInfo,"NULL ghlInfo"); G2ERROR(boneName,"NULL boneName"); if (ghlInfo&&boneName) { ret=G2_Get_Bone_Anim_Range(ghlInfo->mFileName, ghlInfo->mBlist, boneName, startFrame, endFrame); } // 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; G2ERROR(ghlInfo,"NULL ghlInfo"); if (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); } } // 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(ghlInfo,"NULL ghlInfo"); G2ERROR(boneName,"NULL boneName"); int currentTime=G2API_GetTime(AcurrentTime); if (ghlInfo&&boneName) { ret=G2_Pause_Bone_Anim(ghlInfo->mFileName, ghlInfo->mBlist, boneName, currentTime); } G2WARNING(ret,"G2API_PauseBoneAnim Failed"); return ret; } qboolean G2API_PauseBoneAnimIndex(CGhoul2Info *ghlInfo, const int boneIndex, const int AcurrentTime) { qboolean ret=qfalse; G2ERROR(ghlInfo,"NULL ghlInfo"); int currentTime=G2API_GetTime(AcurrentTime); if (ghlInfo) { G2ERROR(boneIndex>=0&&boneIndexmBlist.size(),"Bad Bone Index"); if (boneIndex>=0&&boneIndexmBlist.size()) { ret=G2_Pause_Bone_Anim_Index(ghlInfo->mBlist, boneIndex, currentTime); } } G2WARNING(ret,"G2API_PauseBoneAnimIndex Failed"); return ret; } qboolean G2API_IsPaused(CGhoul2Info *ghlInfo, const char *boneName) { qboolean ret=qfalse; G2ERROR(ghlInfo,"NULL ghlInfo"); G2ERROR(boneName,"NULL boneName"); if (ghlInfo&&boneName) { ret=G2_IsPaused(ghlInfo->mFileName, 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 (ghlInfo) { G2ERROR(index>=0&&indexmBlist.size(),"Bad Bone Index"); if (index>=0&&indexmBlist.size()) { ret=G2_Stop_Bone_Anim_Index(ghlInfo->mBlist, index); } } G2WARNING(ret,"G2API_StopBoneAnimIndex Failed"); return ret; } qboolean G2API_StopBoneAnim(CGhoul2Info *ghlInfo, const char *boneName) { qboolean ret=qfalse; G2ERROR(ghlInfo,"NULL ghlInfo"); G2ERROR(boneName,"NULL boneName"); if (ghlInfo&&boneName) { ret=G2_Stop_Bone_Anim(ghlInfo->mFileName, ghlInfo->mBlist, boneName); } 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 *modelList, int blendTime, int AcurrentTime) { qboolean ret=qfalse; G2ERROR(ghlInfo,"NULL ghlInfo"); int currentTime=G2API_GetTime(AcurrentTime); if (ghlInfo) { // ensure we flush the cache ghlInfo->mSkelFrameNum = 0; ret=G2_Set_Bone_Angles_Index(ghlInfo->mFileName, ghlInfo->mBlist, index, angles, flags, yaw, pitch, roll, modelList, ghlInfo->mModelindex, 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 *modelList, int blendTime, int AcurrentTime ) { qboolean ret=qfalse; G2ERROR(ghlInfo,"NULL ghlInfo"); int currentTime=G2API_GetTime(AcurrentTime); G2ERROR(boneName,"NULL boneName"); if (ghlInfo&&boneName) { // ensure we flush the cache ghlInfo->mSkelFrameNum = 0; ret=G2_Set_Bone_Angles(ghlInfo->mFileName, ghlInfo->mBlist, boneName, angles, flags, up, left, forward, modelList, ghlInfo->mModelindex, 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 *modelList, int blendTime, int AcurrentTime) { qboolean ret=qfalse; G2ERROR(ghlInfo,"NULL ghlInfo"); int currentTime=G2API_GetTime(AcurrentTime); if (ghlInfo) { // 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, modelList, ghlInfo->mModelindex, 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(ghlInfo,"NULL ghlInfo"); int currentTime=G2API_GetTime(AcurrentTime); G2ERROR(boneName,"NULL boneName"); if (ghlInfo&&boneName) { // ensure we flush the cache ghlInfo->mSkelFrameNum = 0; ret=G2_Set_Bone_Angles_Matrix(ghlInfo->mFileName, ghlInfo->mBlist, boneName, matrix, flags, modelList, ghlInfo->mModelindex, blendTime, currentTime); } G2WARNING(ret,"G2API_SetBoneAnglesMatrix Failed"); return ret; } qboolean G2API_StopBoneAnglesIndex(CGhoul2Info *ghlInfo, const int index) { qboolean ret=qfalse; G2ERROR(ghlInfo,"NULL ghlInfo"); if (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(ghlInfo,"NULL ghlInfo"); G2ERROR(boneName,"NULL boneName"); if (ghlInfo&&boneName) { // ensure we flush the cache ghlInfo->mSkelFrameNum = 0; ret=G2_Stop_Bone_Angles(ghlInfo->mFileName, ghlInfo->mBlist, boneName); } G2WARNING(ret,"G2API_StopBoneAngles Failed"); return ret; } qboolean G2API_RemoveBone(CGhoul2Info *ghlInfo, const char *boneName) { qboolean ret=qfalse; G2ERROR(ghlInfo,"NULL ghlInfo"); G2ERROR(boneName,"NULL boneName"); if (ghlInfo&&boneName) { // ensure we flush the cache ghlInfo->mSkelFrameNum = 0; ret=G2_Remove_Bone(ghlInfo->mFileName, ghlInfo->mBlist, boneName); } G2WARNING(ret,"G2API_RemoveBone Failed"); return ret; } void G2API_AnimateG2Models(CGhoul2Info_v &ghoul2, float speedVar) { int model; G2ERROR(ghoul2.IsValid(),"Invalid ghlInfo"); // Walk the list and find all models that are active for (model=0; model< ghoul2.size(); model++) { if (ghoul2[model].mModel) { G2_Animate_Bone_List(ghoul2, speedVar, model); } } } qboolean G2API_RemoveBolt(CGhoul2Info *ghlInfo, const int index) { qboolean ret=qfalse; G2ERROR(ghlInfo,"NULL ghlInfo"); if (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(ghlInfo,"NULL ghlInfo"); G2ERROR(boneName,"NULL boneName"); if (ghlInfo&&boneName) { ret=G2_Add_Bolt(ghlInfo->mFileName, ghlInfo->mBltlist, ghlInfo->mSlist, boneName); } G2WARNING(ret>=0,"G2API_AddBolt Failed"); return ret; } int G2API_AddBoltSurfNum(CGhoul2Info *ghlInfo, const int surfIndex) { int ret=-1; G2ERROR(ghlInfo,"NULL ghlInfo"); if (ghlInfo) { ret=G2_Add_Bolt_Surf_Num(ghlInfo->mFileName, 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; G2ERROR(ghlInfo,"NULL ghlInfo"); G2ERROR(ghlInfoTo,"NULL ghlInfo"); G2ERROR(ghlInfo,"NULL ghlInfo"); 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 ((ghlInfo) && (ghlInfoTo) && ((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) { G2ERROR(ghlInfo,"NULL ghlInfo"); if (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"); G2ERROR(ghlInfoTo,"NULL ghlInfo"); // make sure we have a model to attach, a model to attach to, and a bolt on that model if ((ghlInfoTo) && ((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; } } #if _DEBUG class GBM_Test { public: int Hits; int Misses; GBM_Test() { Hits=0; Misses=0; } ~GBM_Test() { char mess[1000]; sprintf(mess,"GBM stats: %d hits, %d misses\n",Hits,Misses); OutputDebugString(mess); } }; static GBM_Test TheGBM_Test; #endif 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()),"Invalid Bolt Index"); G2_GenerateWorldMatrix(angles, position); if (boltIndex >= 0 && ghlInfo && (boltIndex < ghlInfo->mBltlist.size()) ) { mdxaBone_t bolt; if (G2_NeedsRecalc(ghlInfo,frameNum)) { G2_ConstructGhoulSkeleton(ghoul2,frameNum,true,scale); #if _DEBUG TheGBM_Test.Hits++; #endif } #if _DEBUG else { TheGBM_Test.Misses++; } #endif 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); return qtrue; } } Multiply_3x4Matrix(matrix, &worldMatrix, &identityMatrix); return qfalse; } void G2API_ListSurfaces(CGhoul2Info *ghlInfo) { G2ERROR(ghlInfo,"NULL ghlInfo"); if (ghlInfo) { G2_List_Model_Surfaces(ghlInfo->mFileName); } } void G2API_ListBones(CGhoul2Info *ghlInfo, int frame) { G2ERROR(ghlInfo,"NULL ghlInfo"); if (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; G2ERROR(ghlInfo,"NULL ghlInfo"); if (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"); 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 ); } qboolean G2API_SetGhoul2ModelFlags(CGhoul2Info *ghlInfo, const int flags) { G2ERROR(ghlInfo,"NULL ghlInfo"); if (ghlInfo) { ghlInfo->mFlags &= GHOUL2_NEWORIGIN; ghlInfo->mFlags |= flags; return qtrue; } return qfalse; } int G2API_GetGhoul2ModelFlags(CGhoul2Info *ghlInfo) { G2ERROR(ghlInfo,"NULL ghlInfo"); if (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); } } char *G2API_GetSurfaceName(CGhoul2Info *ghlInfo, int surfNumber) { G2ERROR(ghlInfo,"NULL ghlInfo"); static char noSurface[1] = ""; model_t *mod = R_GetModelByHandle(RE_RegisterModel(ghlInfo->mFileName)); mdxmSurface_t *surf = 0; mdxmSurfHierarchy_t *surfInfo = 0; surf = (mdxmSurface_t *)G2_FindSurface((void *)mod, surfNumber, 0); if (surf) { mdxmHierarchyOffsets_t *surfIndexes = (mdxmHierarchyOffsets_t *)((byte *)mod->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(ghlInfo,"NULL ghlInfo"); G2ERROR(surfaceName,"NULL surfaceName"); if (ghlInfo) { ret=G2_GetSurfaceIndex(ghlInfo->mFileName, surfaceName); } G2WARNING(ret>=0,"G2API_GetSurfaceIndex Failed"); return ret; } char *G2API_GetGLAName(CGhoul2Info *ghlInfo) { G2ERROR(ghlInfo,"NULL ghlInfo"); if (ghlInfo) { model_t *mod = R_GetModelByHandle(RE_RegisterModel(ghlInfo->mFileName)); G2ERROR(mod&&mod->mdxm,"Bad Model"); if (mod&&mod->mdxm) { return mod->mdxm->animName; } } return 0; } qboolean G2API_SetNewOrigin(CGhoul2Info *ghlInfo, const int boltIndex) { G2ERROR(ghlInfo,"NULL ghlInfo"); if (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(ghlInfo,"NULL ghlInfo"); G2ERROR(boneName,"NULL boneName"); if (ghlInfo&&boneName) { ret=G2_Get_Bone_Index(ghlInfo, boneName, bAddIfNotFound); } G2WARNING(ret>=0,"G2API_GetBoneIndex Failed"); return ret; } qboolean G2API_SaveGhoul2Models(CGhoul2Info_v &ghoul2, char **buffer, int *size) { G2ERROR(ghoul2.IsValid(),"Invalid ghlInfo"); return G2_SaveGhoul2Models(ghoul2, buffer, size); } void G2API_LoadGhoul2Models(CGhoul2Info_v &ghoul2, char *buffer) { G2_LoadGhoul2Model(ghoul2, buffer); 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 }