mirror of
https://github.com/DrBeef/JKXR.git
synced 2024-11-29 23:42:38 +00:00
2754 lines
68 KiB
C++
2754 lines
68 KiB
C++
/*
|
|
===========================================================================
|
|
Copyright (C) 2000 - 2013, Raven Software, Inc.
|
|
Copyright (C) 2001 - 2013, Activision, Inc.
|
|
Copyright (C) 2013 - 2015, OpenJK contributors
|
|
|
|
This file is part of the OpenJK source code.
|
|
|
|
OpenJK is free software; you can redistribute it and/or modify it
|
|
under the terms of the GNU General Public License version 2 as
|
|
published by the Free Software Foundation.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, see <http://www.gnu.org/licenses/>.
|
|
===========================================================================
|
|
*/
|
|
|
|
#include "ghoul2/G2.h"
|
|
#include "ghoul2/g2_local.h"
|
|
#include "ghoul2/G2_gore.h"
|
|
|
|
#include "qcommon/MiniHeap.h"
|
|
#include "tr_local.h"
|
|
|
|
#include <set>
|
|
#include <list>
|
|
|
|
#ifdef _FULL_G2_LEAK_CHECKING
|
|
int g_Ghoul2Allocations = 0;
|
|
int g_G2ServerAlloc = 0;
|
|
int g_G2ClientAlloc = 0;
|
|
int g_G2AllocServer = 0;
|
|
|
|
//stupid debug crap to track leaks in case they happen.
|
|
//we used to shove everything into a map and delete it all and not care about
|
|
//leaks, but that was not the Right Thing. -rww
|
|
#define MAX_TRACKED_ALLOC 4096
|
|
static bool g_G2AllocTrackInit = false; //want to keep this thing contained
|
|
static CGhoul2Info_v *g_G2AllocTrack[MAX_TRACKED_ALLOC];
|
|
|
|
void G2_DEBUG_InitPtrTracker(void)
|
|
{
|
|
memset(g_G2AllocTrack, 0, sizeof(g_G2AllocTrack));
|
|
g_G2AllocTrackInit = true;
|
|
}
|
|
|
|
void G2_DEBUG_ReportLeaks(void)
|
|
{
|
|
int i = 0;
|
|
|
|
if (!g_G2AllocTrackInit)
|
|
{
|
|
ri.Printf( PRINT_ALL, "g2 leak tracker was never initialized!\n");
|
|
return;
|
|
}
|
|
|
|
while (i < MAX_TRACKED_ALLOC)
|
|
{
|
|
if (g_G2AllocTrack[i])
|
|
{
|
|
ri.Printf( PRINT_ALL, "Bad guy found in slot %i, attempting to access...", i);
|
|
CGhoul2Info_v &g2v = *g_G2AllocTrack[i];
|
|
CGhoul2Info &g2 = g2v[0];
|
|
|
|
if (g2v.IsValid() && g2.mFileName && g2.mFileName[0])
|
|
{
|
|
ri.Printf( PRINT_ALL, "Bad guy's filename is %s\n", g2.mFileName);
|
|
}
|
|
else
|
|
{
|
|
ri.Printf( PRINT_ALL, "He's not valid! BURN HIM!\n");
|
|
}
|
|
}
|
|
i++;
|
|
}
|
|
}
|
|
|
|
void G2_DEBUG_ShovePtrInTracker(CGhoul2Info_v *g2)
|
|
{
|
|
int i = 0;
|
|
|
|
if (!g_G2AllocTrackInit)
|
|
{
|
|
G2_DEBUG_InitPtrTracker();
|
|
}
|
|
|
|
if (!g_G2AllocTrackInit)
|
|
{
|
|
G2_DEBUG_InitPtrTracker();
|
|
}
|
|
|
|
while (i < MAX_TRACKED_ALLOC)
|
|
{
|
|
if (!g_G2AllocTrack[i])
|
|
{
|
|
g_G2AllocTrack[i] = g2;
|
|
return;
|
|
}
|
|
i++;
|
|
}
|
|
|
|
CGhoul2Info_v &g2v = *g2;
|
|
|
|
if (g2v[0].currentModel && g2v[0].currentModel->name && g2v[0].currentModel->name[0])
|
|
{
|
|
ri.Printf( PRINT_ALL, "%s could not be fit into g2 debug instance tracker.\n", g2v[0].currentModel->name);
|
|
}
|
|
else
|
|
{
|
|
ri.Printf( PRINT_ALL, "Crap g2 instance passed to instance tracker (in).\n");
|
|
}
|
|
}
|
|
|
|
void G2_DEBUG_RemovePtrFromTracker(CGhoul2Info_v *g2)
|
|
{
|
|
int i = 0;
|
|
|
|
if (!g_G2AllocTrackInit)
|
|
{
|
|
G2_DEBUG_InitPtrTracker();
|
|
}
|
|
|
|
while (i < MAX_TRACKED_ALLOC)
|
|
{
|
|
if (g_G2AllocTrack[i] == g2)
|
|
{
|
|
g_G2AllocTrack[i] = NULL;
|
|
return;
|
|
}
|
|
i++;
|
|
}
|
|
|
|
CGhoul2Info_v &g2v = *g2;
|
|
|
|
if (g2v[0].currentModel && g2v[0].currentModel->name && g2v[0].currentModel->name[0])
|
|
{
|
|
ri.Printf( PRINT_ALL, "%s not in g2 debug instance tracker.\n", g2v[0].currentModel->name);
|
|
}
|
|
else
|
|
{
|
|
ri.Printf( PRINT_ALL, "Crap g2 instance passed to instance tracker (out).\n");
|
|
}
|
|
}
|
|
#endif
|
|
|
|
qboolean G2_SetupModelPointers(CGhoul2Info *ghlInfo);
|
|
qboolean G2_SetupModelPointers(CGhoul2Info_v &ghoul2);
|
|
qboolean G2_TestModelPointers(CGhoul2Info *ghlInfo);
|
|
|
|
//rww - RAGDOLL_BEGIN
|
|
#define NUM_G2T_TIME (2)
|
|
static int G2TimeBases[NUM_G2T_TIME];
|
|
|
|
void G2API_SetTime(int currentTime,int clock)
|
|
{
|
|
assert(clock>=0&&clock<NUM_G2T_TIME);
|
|
#if G2_DEBUG_TIME
|
|
ri.Printf( PRINT_ALL, "Set Time: before c%6d s%6d",G2TimeBases[1],G2TimeBases[0]);
|
|
#endif
|
|
G2TimeBases[clock]=currentTime;
|
|
if (G2TimeBases[1]>G2TimeBases[0]+200)
|
|
{
|
|
G2TimeBases[1]=0; // use server time instead
|
|
return;
|
|
}
|
|
#if G2_DEBUG_TIME
|
|
ri.Printf( PRINT_ALL, " 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;
|
|
}
|
|
//rww - RAGDOLL_END
|
|
|
|
//rww - Stuff to allow association of ghoul2 instances to entity numbers.
|
|
//This way, on listen servers when both the client and server are doing
|
|
//ghoul2 operations, we can copy relevant data off the client instance
|
|
//directly onto the server instance and slash the transforms and whatnot
|
|
//right in half.
|
|
#ifdef _G2_LISTEN_SERVER_OPT
|
|
CGhoul2Info_v *g2ClientAttachments[MAX_GENTITIES];
|
|
#endif
|
|
|
|
void G2API_AttachInstanceToEntNum(CGhoul2Info_v &ghoul2, int entityNum, qboolean server)
|
|
{ //Assign the pointers in the arrays
|
|
#ifdef _G2_LISTEN_SERVER_OPT
|
|
if (server)
|
|
{
|
|
ghoul2[0].entityNum = entityNum;
|
|
}
|
|
else
|
|
{
|
|
g2ClientAttachments[entityNum] = &ghoul2;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void G2API_ClearAttachedInstance(int entityNum)
|
|
{
|
|
#ifdef _G2_LISTEN_SERVER_OPT
|
|
g2ClientAttachments[entityNum] = NULL;
|
|
#endif
|
|
}
|
|
|
|
void G2API_CleanEntAttachments(void)
|
|
{
|
|
#ifdef _G2_LISTEN_SERVER_OPT
|
|
int i = 0;
|
|
|
|
while (i < MAX_GENTITIES)
|
|
{
|
|
g2ClientAttachments[i] = NULL;
|
|
i++;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#ifdef _G2_LISTEN_SERVER_OPT
|
|
void CopyBoneCache(CBoneCache *to, CBoneCache *from);
|
|
#endif
|
|
|
|
qboolean G2API_OverrideServerWithClientData(CGhoul2Info_v& ghoul2, int modelIndex)
|
|
{
|
|
#ifndef _G2_LISTEN_SERVER_OPT
|
|
return qfalse;
|
|
#else
|
|
CGhoul2Info *serverInstance = &ghoul2[modelIndex];
|
|
CGhoul2Info *clientInstance;
|
|
|
|
if (ri.Cvar_VariableIntegerValue( "dedicated" ))
|
|
{ //No client to get from!
|
|
return qfalse;
|
|
}
|
|
|
|
if (!g2ClientAttachments[serverInstance->entityNum])
|
|
{ //No clientside instance is attached to this entity
|
|
return qfalse;
|
|
}
|
|
|
|
CGhoul2Info_v &g2Ref = *g2ClientAttachments[serverInstance->entityNum];
|
|
clientInstance = &g2Ref[0];
|
|
|
|
int frameNum = G2API_GetTime(0);
|
|
|
|
if (clientInstance->mSkelFrameNum != frameNum)
|
|
{ //it has to be constructed already
|
|
return qfalse;
|
|
}
|
|
|
|
if (!clientInstance->mBoneCache)
|
|
{ //that just won't do
|
|
return qfalse;
|
|
}
|
|
|
|
//Just copy over the essentials
|
|
serverInstance->aHeader = clientInstance->aHeader;
|
|
serverInstance->animModel = clientInstance->animModel;
|
|
serverInstance->currentAnimModelSize = clientInstance->currentAnimModelSize;
|
|
serverInstance->currentModel = clientInstance->currentModel;
|
|
serverInstance->currentModelSize = clientInstance->currentModelSize;
|
|
serverInstance->mAnimFrameDefault = clientInstance->mAnimFrameDefault;
|
|
serverInstance->mModel = clientInstance->mModel;
|
|
serverInstance->mModelindex = clientInstance->mModelindex;
|
|
serverInstance->mSurfaceRoot = clientInstance->mSurfaceRoot;
|
|
serverInstance->mTransformedVertsArray = clientInstance->mTransformedVertsArray;
|
|
|
|
if (!serverInstance->mBoneCache)
|
|
{ //if this is the case.. I guess we can use the client one instead
|
|
serverInstance->mBoneCache = clientInstance->mBoneCache;
|
|
}
|
|
|
|
//Copy the contents of the client cache over the contents of the server cache
|
|
if (serverInstance->mBoneCache != clientInstance->mBoneCache)
|
|
{
|
|
CopyBoneCache(serverInstance->mBoneCache, clientInstance->mBoneCache);
|
|
}
|
|
|
|
serverInstance->mSkelFrameNum = clientInstance->mSkelFrameNum;
|
|
return qtrue;
|
|
#endif
|
|
}
|
|
|
|
// must be a power of two
|
|
#define MAX_G2_MODELS (1024)
|
|
#define G2_MODEL_BITS (10)
|
|
|
|
#define G2_INDEX_MASK (MAX_G2_MODELS-1)
|
|
|
|
class Ghoul2InfoArray : public IGhoul2InfoArray
|
|
{
|
|
std::vector<CGhoul2Info> mInfos[MAX_G2_MODELS];
|
|
int mIds[MAX_G2_MODELS];
|
|
std::list<int> mFreeIndecies;
|
|
void DeleteLow(int idx)
|
|
{
|
|
for (size_t model=0; model< mInfos[idx].size(); model++)
|
|
{
|
|
if (mInfos[idx][model].mBoneCache)
|
|
{
|
|
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<MAX_G2_MODELS;i++)
|
|
{
|
|
mIds[i]=MAX_G2_MODELS+i;
|
|
mFreeIndecies.push_back(i);
|
|
}
|
|
}
|
|
#if G2API_DEBUG
|
|
~Ghoul2InfoArray()
|
|
{
|
|
if (mFreeIndecies.size()<MAX_G2_MODELS)
|
|
{
|
|
Com_OPrintf("************************\nLeaked %d ghoul2info slots\n", MAX_G2_MODELS - mFreeIndecies.size());
|
|
int i;
|
|
for (i=0;i<MAX_G2_MODELS;i++)
|
|
{
|
|
list<int>::iterator j;
|
|
for (j=mFreeIndecies.begin();j!=mFreeIndecies.end();++j)
|
|
{
|
|
if (*j==i)
|
|
break;
|
|
}
|
|
if (j==mFreeIndecies.end())
|
|
{
|
|
Com_OPrintf("Leaked Info idx=%d id=%d sz=%d\n", i, mIds[i], mInfos[i].size());
|
|
if (mInfos[i].size())
|
|
{
|
|
Com_OPrintf("%s\n", mInfos[i][0].mFileName);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Com_OPrintf("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 <= 0 )
|
|
{
|
|
return false;
|
|
}
|
|
assert((handle&G2_INDEX_MASK)>=0&&(handle&G2_INDEX_MASK)<MAX_G2_MODELS); //junk handle
|
|
if (mIds[handle&G2_INDEX_MASK]!=handle) // not a valid handle, could be old
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
void Delete(int handle)
|
|
{
|
|
if (handle <= 0)
|
|
{
|
|
return;
|
|
}
|
|
assert((handle&G2_INDEX_MASK)>=0&&(handle&G2_INDEX_MASK)<MAX_G2_MODELS); //junk handle
|
|
assert(mIds[handle&G2_INDEX_MASK]==handle); // not a valid handle, could be old or garbage
|
|
if (mIds[handle&G2_INDEX_MASK]==handle)
|
|
{
|
|
DeleteLow(handle&G2_INDEX_MASK);
|
|
}
|
|
}
|
|
std::vector<CGhoul2Info> &Get(int handle)
|
|
{
|
|
assert(handle>0); //null handle
|
|
assert((handle&G2_INDEX_MASK)>=0&&(handle&G2_INDEX_MASK)<MAX_G2_MODELS); //junk handle
|
|
assert(mIds[handle&G2_INDEX_MASK]==handle); // not a valid handle, could be old or garbage
|
|
assert(!(handle<=0||(handle&G2_INDEX_MASK)<0||(handle&G2_INDEX_MASK)>=MAX_G2_MODELS||mIds[handle&G2_INDEX_MASK]!=handle));
|
|
|
|
return mInfos[handle&G2_INDEX_MASK];
|
|
}
|
|
const std::vector<CGhoul2Info> &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<CGhoul2Info> &GetDebug(int handle)
|
|
{
|
|
static vector<CGhoul2Info> null;
|
|
if (handle<=0||(handle&G2_INDEX_MASK)<0||(handle&G2_INDEX_MASK)>=MAX_G2_MODELS||mIds[handle&G2_INDEX_MASK]!=handle)
|
|
{
|
|
return *(vector<CGhoul2Info> *)0; // null reference, intentional
|
|
}
|
|
return mInfos[handle&G2_INDEX_MASK];
|
|
}
|
|
void TestAllAnims()
|
|
{
|
|
int j;
|
|
for (j=0;j<MAX_G2_MODELS;j++)
|
|
{
|
|
vector<CGhoul2Info> &ghoul2=mInfos[j];
|
|
int i;
|
|
for (i=0; i<ghoul2.size(); i++)
|
|
{
|
|
if (G2_SetupModelPointers(&ghoul2[i]))
|
|
{
|
|
G2ANIM(&ghoul2[i],"Test All");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif
|
|
};
|
|
|
|
static Ghoul2InfoArray *singleton = NULL;
|
|
IGhoul2InfoArray &TheGhoul2InfoArray()
|
|
{
|
|
if(!singleton) {
|
|
singleton = new Ghoul2InfoArray;
|
|
}
|
|
return *singleton;
|
|
}
|
|
|
|
|
|
void Ghoul2InfoArray_Free(void)
|
|
{
|
|
if(singleton) {
|
|
delete singleton;
|
|
singleton = NULL;
|
|
}
|
|
}
|
|
|
|
// this is the ONLY function to read entity states directly
|
|
void G2API_CleanGhoul2Models(CGhoul2Info_v **ghoul2Ptr)
|
|
{
|
|
if (*ghoul2Ptr)
|
|
{
|
|
CGhoul2Info_v &ghoul2 = *(*ghoul2Ptr);
|
|
|
|
#if 0 //rwwFIXMEFIXME: Disable this before release!!!!!! I am just trying to find a crash bug.
|
|
extern int R_GetRNumEntities(void);
|
|
extern void R_SetRNumEntities(int num);
|
|
//check if this instance is actually on a refentity
|
|
int i = 0;
|
|
int r = R_GetRNumEntities();
|
|
|
|
while (i < r)
|
|
{
|
|
if ((CGhoul2Info_v *)backEndData->entities[i].e.ghoul2 == *ghoul2Ptr)
|
|
{
|
|
char fName[MAX_QPATH];
|
|
char mName[MAX_QPATH];
|
|
|
|
if (ghoul2[0].currentModel)
|
|
{
|
|
strcpy(mName, ghoul2[0].currentModel->name);
|
|
}
|
|
else
|
|
{
|
|
strcpy(mName, "NULL!");
|
|
}
|
|
|
|
if (ghoul2[0].mFileName && ghoul2[0].mFileName[0])
|
|
{
|
|
strcpy(fName, ghoul2[0].mFileName);
|
|
}
|
|
else
|
|
{
|
|
strcpy(fName, "None?!");
|
|
}
|
|
|
|
ri.Printf( PRINT_ALL, "ERROR, GHOUL2 INSTANCE BEING REMOVED BELONGS TO A REFENTITY!\nThis is in caps because it's important. Tell Rich and save the following text.\n\n");
|
|
ri.Printf( PRINT_ALL, "Ref num: %i\nModel: %s\nFilename: %s\n", i, mName, fName);
|
|
|
|
R_SetRNumEntities(0); //avoid recursive error
|
|
Com_Error(ERR_DROP, "Write down or save this error message, show it to Rich\nRef num: %i\nModel: %s\nFilename: %s\n", i, mName, fName);
|
|
}
|
|
i++;
|
|
}
|
|
#endif
|
|
|
|
#ifdef _G2_GORE
|
|
G2API_ClearSkinGore ( ghoul2 );
|
|
#endif
|
|
|
|
#ifdef _FULL_G2_LEAK_CHECKING
|
|
if (g_G2AllocServer)
|
|
{
|
|
g_G2ServerAlloc -= sizeof(*ghoul2Ptr);
|
|
}
|
|
else
|
|
{
|
|
g_G2ClientAlloc -= sizeof(*ghoul2Ptr);
|
|
}
|
|
g_Ghoul2Allocations -= sizeof(*ghoul2Ptr);
|
|
G2_DEBUG_RemovePtrFromTracker(*ghoul2Ptr);
|
|
#endif
|
|
|
|
delete *ghoul2Ptr;
|
|
*ghoul2Ptr = NULL;
|
|
}
|
|
}
|
|
|
|
qboolean G2_ShouldRegisterServer(void)
|
|
{
|
|
if ( !ri.GetCurrentVM )
|
|
return qfalse;
|
|
|
|
vm_t *currentVM = ri.GetCurrentVM();
|
|
|
|
if ( currentVM && currentVM->slot == VM_GAME )
|
|
{
|
|
if ( ri.Cvar_VariableIntegerValue( "cl_running" ) &&
|
|
ri.Com_TheHunkMarkHasBeenMade() && ShaderHashTableExists())
|
|
{ //if the hunk has been marked then we are now loading client assets so don't load on server.
|
|
return qfalse;
|
|
}
|
|
|
|
return qtrue;
|
|
}
|
|
return qfalse;
|
|
}
|
|
|
|
qhandle_t G2API_PrecacheGhoul2Model( const char *fileName )
|
|
{
|
|
if ( G2_ShouldRegisterServer() )
|
|
return RE_RegisterServerModel( fileName );
|
|
else
|
|
return RE_RegisterModel( fileName );
|
|
}
|
|
|
|
// initialise all that needs to be on a new Ghoul II model
|
|
int G2API_InitGhoul2Model(CGhoul2Info_v **ghoul2Ptr, const char *fileName, int modelIndex, qhandle_t customSkin,
|
|
qhandle_t customShader, int modelFlags, int lodBias)
|
|
{
|
|
int model;
|
|
|
|
// are we actually asking for a model to be loaded.
|
|
if (!fileName || !fileName[0])
|
|
{
|
|
assert(0);
|
|
return -1;
|
|
}
|
|
|
|
if (!(*ghoul2Ptr))
|
|
{
|
|
*ghoul2Ptr = new CGhoul2Info_v;
|
|
#ifdef _FULL_G2_LEAK_CHECKING
|
|
if (g_G2AllocServer)
|
|
{
|
|
g_G2ServerAlloc += sizeof(CGhoul2Info_v);
|
|
}
|
|
else
|
|
{
|
|
g_G2ClientAlloc += sizeof(CGhoul2Info_v);
|
|
}
|
|
g_Ghoul2Allocations += sizeof(CGhoul2Info_v);
|
|
G2_DEBUG_ShovePtrInTracker(*ghoul2Ptr);
|
|
#endif
|
|
}
|
|
|
|
CGhoul2Info_v &ghoul2 = *(*ghoul2Ptr);
|
|
|
|
// 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())
|
|
{ //init should not be used to create additional models, only the first one
|
|
assert(ghoul2.size() < 4); //use G2API_CopySpecificG2Model to add models
|
|
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, ghoul2[model].aHeader->numBones);
|
|
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)
|
|
{
|
|
if (G2_SetupModelPointers(ghlInfo))
|
|
{
|
|
ghlInfo->mLodBias = lodBias;
|
|
return qtrue;
|
|
}
|
|
return qfalse;
|
|
}
|
|
|
|
void G2_SetSurfaceOnOffFromSkin (CGhoul2Info *ghlInfo, qhandle_t renderSkin);
|
|
qboolean G2API_SetSkin(CGhoul2Info_v& ghoul2, int modelIndex, qhandle_t customSkin, qhandle_t renderSkin)
|
|
{
|
|
CGhoul2Info *ghlInfo = &ghoul2[modelIndex];
|
|
|
|
if (G2_SetupModelPointers(ghlInfo))
|
|
{
|
|
ghlInfo->mCustomSkin = customSkin;
|
|
if (renderSkin)
|
|
{//this is going to set the surfs on/off matching the skin file
|
|
G2_SetSurfaceOnOffFromSkin( ghlInfo, renderSkin );
|
|
}
|
|
|
|
return qtrue;
|
|
}
|
|
return qfalse;
|
|
}
|
|
|
|
qboolean G2API_SetShader(CGhoul2Info *ghlInfo, qhandle_t customShader)
|
|
{
|
|
if (G2_SetupModelPointers(ghlInfo))
|
|
{
|
|
ghlInfo->mCustomShader = customShader;
|
|
return qtrue;
|
|
}
|
|
return qfalse;
|
|
}
|
|
|
|
qboolean G2API_SetSurfaceOnOff(CGhoul2Info_v &ghoul2, const char *surfaceName, const int flags)
|
|
{
|
|
CGhoul2Info *ghlInfo = NULL;
|
|
|
|
if (ghoul2.size()>0)
|
|
{
|
|
ghlInfo = &ghoul2[0];
|
|
}
|
|
|
|
if (G2_SetupModelPointers(ghlInfo))
|
|
{
|
|
// ensure we flush the cache
|
|
ghlInfo->mMeshFrameNum = 0;
|
|
return G2_SetSurfaceOnOff(ghlInfo, ghlInfo->mSlist, surfaceName, flags);
|
|
}
|
|
return qfalse;
|
|
}
|
|
|
|
int G2API_GetSurfaceOnOff(CGhoul2Info *ghlInfo, const char *surfaceName)
|
|
{
|
|
if (G2_SetupModelPointers(ghlInfo))
|
|
{
|
|
return G2_IsSurfaceOff(ghlInfo, ghlInfo->mSlist, surfaceName);
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
qboolean G2API_SetRootSurface(CGhoul2Info_v &ghoul2, const int modelIndex, const char *surfaceName)
|
|
{
|
|
if (G2_SetupModelPointers(ghoul2))
|
|
{
|
|
return G2_SetRootSurface(ghoul2, modelIndex, surfaceName);
|
|
}
|
|
|
|
return qfalse;
|
|
}
|
|
|
|
int G2API_AddSurface(CGhoul2Info *ghlInfo, int surfaceNumber, int polyNumber, float BarycentricI, float BarycentricJ, int lod )
|
|
{
|
|
if (G2_SetupModelPointers(ghlInfo))
|
|
{
|
|
// ensure we flush the cache
|
|
ghlInfo->mMeshFrameNum = 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_v& ghoul2, int modelIndex, const char *surfaceName)
|
|
{
|
|
CGhoul2Info *ghlInfo = &ghoul2[modelIndex];
|
|
|
|
if (G2_SetupModelPointers(ghlInfo))
|
|
{
|
|
return G2_IsSurfaceRendered(ghlInfo, surfaceName, ghlInfo->mSlist);
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
qboolean G2API_HasGhoul2ModelOnIndex(CGhoul2Info_v **ghlRemove, const int modelIndex)
|
|
{
|
|
CGhoul2Info_v &ghlInfo = **ghlRemove;
|
|
|
|
if (!ghlInfo.size() || (ghlInfo.size() <= modelIndex) || (ghlInfo[modelIndex].mModelindex == -1))
|
|
{
|
|
return qfalse;
|
|
}
|
|
|
|
return qtrue;
|
|
}
|
|
|
|
qboolean G2API_RemoveGhoul2Model(CGhoul2Info_v **ghlRemove, const int modelIndex)
|
|
{
|
|
CGhoul2Info_v &ghlInfo = **ghlRemove;
|
|
|
|
// sanity check
|
|
if (!ghlInfo.size() || (ghlInfo.size() <= modelIndex) || (ghlInfo[modelIndex].mModelindex == -1))
|
|
{
|
|
// 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.
|
|
assert(0);
|
|
return qfalse;
|
|
}
|
|
|
|
if (ghlInfo.size() > modelIndex)
|
|
{
|
|
#ifdef _G2_GORE
|
|
// Cleanup the gore attached to this model
|
|
if ( ghlInfo[modelIndex].mGoreSetTag )
|
|
{
|
|
DeleteGoreSet ( ghlInfo[modelIndex].mGoreSetTag );
|
|
ghlInfo[modelIndex].mGoreSetTag = 0;
|
|
}
|
|
#endif
|
|
|
|
if (ghlInfo[modelIndex].mBoneCache)
|
|
{
|
|
RemoveBoneCache(ghlInfo[modelIndex].mBoneCache);
|
|
ghlInfo[modelIndex].mBoneCache=0;
|
|
}
|
|
|
|
// clear out the vectors this model used.
|
|
ghlInfo[modelIndex].mBlist.clear();
|
|
ghlInfo[modelIndex].mBltlist.clear();
|
|
ghlInfo[modelIndex].mSlist.clear();
|
|
|
|
// 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);
|
|
}
|
|
|
|
// if we are not using any space, just delete the ghoul2 vector entirely
|
|
if (!ghlInfo.size())
|
|
{
|
|
#ifdef _FULL_G2_LEAK_CHECKING
|
|
if (g_G2AllocServer)
|
|
{
|
|
g_G2ServerAlloc -= sizeof(*ghlRemove);
|
|
}
|
|
else
|
|
{
|
|
g_G2ClientAlloc -= sizeof(*ghlRemove);
|
|
}
|
|
g_Ghoul2Allocations -= sizeof(*ghlRemove);
|
|
#endif
|
|
delete *ghlRemove;
|
|
*ghlRemove = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
return qtrue;
|
|
}
|
|
|
|
qboolean G2API_RemoveGhoul2Models(CGhoul2Info_v **ghlRemove)
|
|
{//remove 'em ALL!
|
|
CGhoul2Info_v &ghlInfo = **ghlRemove;
|
|
int modelIndex = 0;
|
|
int newSize = 0;
|
|
int i;
|
|
|
|
// sanity check
|
|
if ( !ghlInfo.size() )
|
|
{// if we hit this then we are trying to delete a ghoul2 model on a ghoul2 instance that
|
|
// one way or another is already gone.
|
|
return qfalse;
|
|
}
|
|
|
|
for ( modelIndex = 0; modelIndex < ghlInfo.size(); modelIndex++ )
|
|
{
|
|
if ( ghlInfo[modelIndex].mModelindex == -1 )
|
|
{
|
|
continue;
|
|
}
|
|
#ifdef _G2_GORE
|
|
// Cleanup the gore attached to this model
|
|
if ( ghlInfo[modelIndex].mGoreSetTag )
|
|
{
|
|
DeleteGoreSet ( ghlInfo[modelIndex].mGoreSetTag );
|
|
ghlInfo[modelIndex].mGoreSetTag = 0;
|
|
}
|
|
#endif
|
|
|
|
if (ghlInfo[modelIndex].mBoneCache)
|
|
{
|
|
RemoveBoneCache(ghlInfo[modelIndex].mBoneCache);
|
|
ghlInfo[modelIndex].mBoneCache=0;
|
|
}
|
|
|
|
// clear out the vectors this model used.
|
|
ghlInfo[modelIndex].mBlist.clear();
|
|
ghlInfo[modelIndex].mBltlist.clear();
|
|
ghlInfo[modelIndex].mSlist.clear();
|
|
|
|
// set us to be the 'not active' state
|
|
ghlInfo[modelIndex].mModelindex = -1;
|
|
}
|
|
|
|
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 (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);
|
|
}
|
|
|
|
// if we are not using any space, just delete the ghoul2 vector entirely
|
|
if (!ghlInfo.size())
|
|
{
|
|
#ifdef _FULL_G2_LEAK_CHECKING
|
|
if (g_G2AllocServer)
|
|
{
|
|
g_G2ServerAlloc -= sizeof(*ghlRemove);
|
|
}
|
|
else
|
|
{
|
|
g_G2ClientAlloc -= sizeof(*ghlRemove);
|
|
}
|
|
g_Ghoul2Allocations -= sizeof(*ghlRemove);
|
|
#endif
|
|
delete *ghlRemove;
|
|
*ghlRemove = NULL;
|
|
}
|
|
return qtrue;
|
|
}
|
|
|
|
//check if a bone exists on skeleton without actually adding to the bone list -rww
|
|
qboolean G2API_DoesBoneExist(CGhoul2Info_v& ghoul2, int modelIndex, const char *boneName)
|
|
{
|
|
CGhoul2Info *ghlInfo = &ghoul2[modelIndex];
|
|
|
|
if (G2_SetupModelPointers(ghlInfo))
|
|
{ //model is valid
|
|
mdxaHeader_t *mdxa = ghlInfo->currentModel->mdxa;
|
|
if (mdxa)
|
|
{ //get the skeleton data and iterate through the bones
|
|
int i;
|
|
mdxaSkel_t *skel;
|
|
mdxaSkelOffsets_t *offsets;
|
|
|
|
offsets = (mdxaSkelOffsets_t *)((byte *)mdxa + sizeof(mdxaHeader_t));
|
|
|
|
for (i = 0; i < mdxa->numBones; i++)
|
|
{
|
|
skel = (mdxaSkel_t *)((byte *)mdxa + sizeof(mdxaHeader_t) + offsets->offsets[i]);
|
|
if (!Q_stricmp(skel->name, boneName))
|
|
{ //got it
|
|
return qtrue;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//guess it doesn't exist
|
|
return qfalse;
|
|
}
|
|
|
|
//rww - RAGDOLL_BEGIN
|
|
#define GHOUL2_RAG_STARTED 0x0010
|
|
#define GHOUL2_RAG_FORCESOLVE 0x1000 //api-override, determine if ragdoll should be forced to continue solving even if it thinks it is settled
|
|
//rww - RAGDOLL_END
|
|
|
|
qboolean G2API_SetBoneAnimIndex(CGhoul2Info *ghlInfo, const int index, const int AstartFrame, const int AendFrame, const int flags, const float animSpeed, const int currentTime, const float AsetFrame, const int blendTime)
|
|
{
|
|
qboolean setPtrs = qfalse;
|
|
qboolean res = qfalse;
|
|
|
|
//rww - RAGDOLL_BEGIN
|
|
if (ghlInfo)
|
|
{
|
|
res = G2_SetupModelPointers(ghlInfo);
|
|
setPtrs = qtrue;
|
|
|
|
if (res)
|
|
{
|
|
if (ghlInfo->mFlags & GHOUL2_RAG_STARTED)
|
|
{
|
|
return qfalse;
|
|
}
|
|
}
|
|
}
|
|
//rww - RAGDOLL_END
|
|
|
|
int endFrame=AendFrame;
|
|
int startFrame=AstartFrame;
|
|
float setFrame=AsetFrame;
|
|
assert(endFrame>0);
|
|
assert(startFrame>=0);
|
|
assert(endFrame<100000);
|
|
assert(startFrame<100000);
|
|
assert(setFrame>=0.0f||setFrame==-1.0f);
|
|
assert(setFrame<=100000.0f);
|
|
if (endFrame<=0)
|
|
{
|
|
endFrame=1;
|
|
}
|
|
if (endFrame>=100000)
|
|
{
|
|
endFrame=1;
|
|
}
|
|
if (startFrame<0)
|
|
{
|
|
startFrame=0;
|
|
}
|
|
if (startFrame>=100000)
|
|
{
|
|
startFrame=0;
|
|
}
|
|
if (setFrame<0.0f&&setFrame!=-1.0f)
|
|
{
|
|
setFrame=0.0f;
|
|
}
|
|
if (setFrame>100000.0f)
|
|
{
|
|
setFrame=0.0f;
|
|
}
|
|
if (!setPtrs)
|
|
{
|
|
res = G2_SetupModelPointers(ghlInfo);
|
|
}
|
|
|
|
if (res)
|
|
{
|
|
// ensure we flush the cache
|
|
ghlInfo->mSkelFrameNum = 0;
|
|
return G2_Set_Bone_Anim_Index(ghlInfo->mBlist, index, startFrame, endFrame, flags, animSpeed, currentTime, setFrame, blendTime, ghlInfo->aHeader->numFrames);
|
|
}
|
|
return qfalse;
|
|
}
|
|
|
|
#define _PLEASE_SHUT_THE_HELL_UP
|
|
|
|
qboolean G2API_SetBoneAnim(CGhoul2Info_v &ghoul2, const int modelIndex, const char *boneName, const int AstartFrame, const int AendFrame, const int flags, const float animSpeed, const int currentTime, const float AsetFrame, const int blendTime)
|
|
{
|
|
int endFrame=AendFrame;
|
|
int startFrame=AstartFrame;
|
|
float setFrame=AsetFrame;
|
|
#ifndef _PLEASE_SHUT_THE_HELL_UP
|
|
assert(endFrame>0);
|
|
assert(startFrame>=0);
|
|
assert(endFrame<100000);
|
|
assert(startFrame<100000);
|
|
assert(setFrame>=0.0f||setFrame==-1.0f);
|
|
assert(setFrame<=100000.0f);
|
|
#endif
|
|
if (endFrame<=0)
|
|
{
|
|
endFrame=1;
|
|
}
|
|
if (endFrame>=100000)
|
|
{
|
|
endFrame=1;
|
|
}
|
|
if (startFrame<0)
|
|
{
|
|
startFrame=0;
|
|
}
|
|
if (startFrame>=100000)
|
|
{
|
|
startFrame=0;
|
|
}
|
|
if (setFrame<0.0f&&setFrame!=-1.0f)
|
|
{
|
|
setFrame=0.0f;
|
|
}
|
|
if (setFrame>100000.0f)
|
|
{
|
|
setFrame=0.0f;
|
|
}
|
|
if (ghoul2.size()>modelIndex)
|
|
{
|
|
CGhoul2Info *ghlInfo = &ghoul2[modelIndex];
|
|
qboolean setPtrs = qfalse;
|
|
qboolean res = qfalse;
|
|
|
|
//rww - RAGDOLL_BEGIN
|
|
if (ghlInfo)
|
|
{
|
|
res = G2_SetupModelPointers(ghlInfo);
|
|
setPtrs = qtrue;
|
|
|
|
if (res)
|
|
{
|
|
if (ghlInfo->mFlags & GHOUL2_RAG_STARTED)
|
|
{
|
|
return qfalse;
|
|
}
|
|
}
|
|
}
|
|
//rww - RAGDOLL_END
|
|
|
|
if (!setPtrs)
|
|
{
|
|
res = G2_SetupModelPointers(ghlInfo);
|
|
}
|
|
|
|
if (res)
|
|
{
|
|
// ensure we flush the cache
|
|
ghlInfo->mSkelFrameNum = 0;
|
|
return G2_Set_Bone_Anim(ghlInfo, ghlInfo->mBlist, boneName, startFrame, endFrame, flags, animSpeed, currentTime, setFrame, blendTime);
|
|
}
|
|
}
|
|
return qfalse;
|
|
}
|
|
|
|
qboolean G2API_GetBoneAnim(CGhoul2Info_v& ghoul2, int modelIndex, const char *boneName, const int currentTime, float *currentFrame,
|
|
int *startFrame, int *endFrame, int *flags, float *animSpeed, int *modelList)
|
|
{
|
|
assert(startFrame!=endFrame); //this is bad
|
|
assert(startFrame!=flags); //this is bad
|
|
assert(endFrame!=flags); //this is bad
|
|
assert(currentFrame!=animSpeed); //this is bad
|
|
|
|
CGhoul2Info *ghlInfo = &ghoul2[modelIndex];
|
|
|
|
if (G2_SetupModelPointers(ghlInfo))
|
|
{
|
|
int aCurrentTime=G2API_GetTime(currentTime);
|
|
qboolean ret=G2_Get_Bone_Anim(ghlInfo, ghlInfo->mBlist, boneName, aCurrentTime, currentFrame,
|
|
startFrame, endFrame, flags, animSpeed, modelList, ghlInfo->mModelindex);
|
|
#ifdef _DEBUG
|
|
/*
|
|
assert(*endFrame>0);
|
|
assert(*endFrame<100000);
|
|
assert(*startFrame>=0);
|
|
assert(*startFrame<100000);
|
|
assert(*currentFrame>=0.0f);
|
|
assert(*currentFrame<100000.0f);
|
|
*/
|
|
if (*endFrame<1)
|
|
{
|
|
*endFrame=1;
|
|
}
|
|
if (*endFrame>100000)
|
|
{
|
|
*endFrame=1;
|
|
}
|
|
if (*startFrame<0)
|
|
{
|
|
*startFrame=0;
|
|
}
|
|
if (*startFrame>100000)
|
|
{
|
|
*startFrame=1;
|
|
}
|
|
if (*currentFrame<0.0f)
|
|
{
|
|
*currentFrame=0.0f;
|
|
}
|
|
if (*currentFrame>100000)
|
|
{
|
|
*currentFrame=1;
|
|
}
|
|
#endif
|
|
return ret;
|
|
}
|
|
return qfalse;
|
|
}
|
|
|
|
qboolean G2API_GetAnimRange(CGhoul2Info *ghlInfo, const char *boneName, int *startFrame, int *endFrame)
|
|
{
|
|
assert(startFrame!=endFrame); //this is bad
|
|
if (G2_SetupModelPointers(ghlInfo))
|
|
{
|
|
qboolean ret=G2_Get_Bone_Anim_Range(ghlInfo, ghlInfo->mBlist, boneName, startFrame, endFrame);
|
|
#ifdef _DEBUG
|
|
assert(*endFrame>0);
|
|
assert(*endFrame<100000);
|
|
assert(*startFrame>=0);
|
|
assert(*startFrame<100000);
|
|
if (*endFrame<1)
|
|
{
|
|
*endFrame=1;
|
|
}
|
|
if (*endFrame>100000)
|
|
{
|
|
*endFrame=1;
|
|
}
|
|
if (*startFrame<0)
|
|
{
|
|
*startFrame=0;
|
|
}
|
|
if (*startFrame>100000)
|
|
{
|
|
*startFrame=1;
|
|
}
|
|
#endif
|
|
return ret;
|
|
}
|
|
return qfalse;
|
|
}
|
|
|
|
|
|
qboolean G2API_PauseBoneAnim(CGhoul2Info *ghlInfo, const char *boneName, const int currentTime)
|
|
{
|
|
if (G2_SetupModelPointers(ghlInfo))
|
|
{
|
|
return G2_Pause_Bone_Anim(ghlInfo, ghlInfo->mBlist, boneName, currentTime);
|
|
}
|
|
return qfalse;
|
|
}
|
|
|
|
qboolean G2API_IsPaused(CGhoul2Info *ghlInfo, const char *boneName)
|
|
{
|
|
if (G2_SetupModelPointers(ghlInfo))
|
|
{
|
|
return G2_IsPaused(ghlInfo->mFileName, ghlInfo->mBlist, boneName);
|
|
}
|
|
return qfalse;
|
|
}
|
|
|
|
qboolean G2API_StopBoneAnimIndex(CGhoul2Info *ghlInfo, const int index)
|
|
{
|
|
if (G2_SetupModelPointers(ghlInfo))
|
|
{
|
|
return G2_Stop_Bone_Anim_Index(ghlInfo->mBlist, index);
|
|
}
|
|
return qfalse;
|
|
}
|
|
|
|
qboolean G2API_StopBoneAnim(CGhoul2Info *ghlInfo, const char *boneName)
|
|
{
|
|
if (G2_SetupModelPointers(ghlInfo))
|
|
{
|
|
return G2_Stop_Bone_Anim(ghlInfo->mFileName, ghlInfo->mBlist, boneName);
|
|
}
|
|
return qfalse;
|
|
}
|
|
|
|
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 currentTime)
|
|
{
|
|
qboolean setPtrs = qfalse;
|
|
qboolean res = qfalse;
|
|
|
|
//rww - RAGDOLL_BEGIN
|
|
if (ghlInfo)
|
|
{
|
|
res = G2_SetupModelPointers(ghlInfo);
|
|
setPtrs = qtrue;
|
|
|
|
if (res)
|
|
{
|
|
if (ghlInfo->mFlags & GHOUL2_RAG_STARTED)
|
|
{
|
|
return qfalse;
|
|
}
|
|
}
|
|
}
|
|
//rww - RAGDOLL_END
|
|
|
|
if (!setPtrs)
|
|
{
|
|
res = G2_SetupModelPointers(ghlInfo);
|
|
}
|
|
|
|
if (res)
|
|
{
|
|
// ensure we flush the cache
|
|
ghlInfo->mSkelFrameNum = 0;
|
|
return G2_Set_Bone_Angles_Index( ghlInfo->mBlist, index, angles, flags, yaw, pitch, roll, modelList, ghlInfo->mModelindex, blendTime, currentTime);
|
|
}
|
|
return qfalse;
|
|
}
|
|
|
|
qboolean G2API_SetBoneAngles(CGhoul2Info_v &ghoul2, const int modelIndex, 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 currentTime )
|
|
{
|
|
if (ghoul2.size()>modelIndex)
|
|
{
|
|
CGhoul2Info *ghlInfo = &ghoul2[modelIndex];
|
|
qboolean setPtrs = qfalse;
|
|
qboolean res = qfalse;
|
|
|
|
//rww - RAGDOLL_BEGIN
|
|
if (ghlInfo)
|
|
{
|
|
res = G2_SetupModelPointers(ghlInfo);
|
|
setPtrs = qtrue;
|
|
|
|
if (res)
|
|
{
|
|
if (ghlInfo->mFlags & GHOUL2_RAG_STARTED)
|
|
{
|
|
return qfalse;
|
|
}
|
|
}
|
|
}
|
|
//rww - RAGDOLL_END
|
|
|
|
if (!setPtrs)
|
|
{
|
|
res = G2_SetupModelPointers(ghoul2);
|
|
}
|
|
|
|
if (res)
|
|
{
|
|
// ensure we flush the cache
|
|
ghlInfo->mSkelFrameNum = 0;
|
|
return G2_Set_Bone_Angles(ghlInfo, ghlInfo->mBlist, boneName, angles, flags, up, left, forward, modelList, ghlInfo->mModelindex, blendTime, currentTime);
|
|
}
|
|
}
|
|
return qfalse;
|
|
}
|
|
|
|
qboolean G2API_SetBoneAnglesMatrixIndex(CGhoul2Info *ghlInfo, const int index, const mdxaBone_t &matrix,
|
|
const int flags, qhandle_t *modelList, int blendTime, int currentTime)
|
|
{
|
|
if (G2_SetupModelPointers(ghlInfo))
|
|
{
|
|
// ensure we flush the cache
|
|
ghlInfo->mSkelFrameNum = 0;
|
|
return G2_Set_Bone_Angles_Matrix_Index(ghlInfo->mBlist, index, matrix, flags, modelList, ghlInfo->mModelindex, blendTime, currentTime);
|
|
}
|
|
return qfalse;
|
|
}
|
|
|
|
qboolean G2API_SetBoneAnglesMatrix(CGhoul2Info *ghlInfo, const char *boneName, const mdxaBone_t &matrix,
|
|
const int flags, qhandle_t *modelList, int blendTime, int currentTime)
|
|
{
|
|
if (G2_SetupModelPointers(ghlInfo))
|
|
{
|
|
// ensure we flush the cache
|
|
ghlInfo->mSkelFrameNum = 0;
|
|
return G2_Set_Bone_Angles_Matrix(ghlInfo->mFileName, ghlInfo->mBlist, boneName, matrix, flags, modelList, ghlInfo->mModelindex, blendTime, currentTime);
|
|
}
|
|
return qfalse;
|
|
}
|
|
|
|
qboolean G2API_StopBoneAnglesIndex(CGhoul2Info *ghlInfo, const int index)
|
|
{
|
|
if (G2_SetupModelPointers(ghlInfo))
|
|
{
|
|
// ensure we flush the cache
|
|
ghlInfo->mSkelFrameNum = 0;
|
|
return G2_Stop_Bone_Angles_Index(ghlInfo->mBlist, index);
|
|
}
|
|
return qfalse;
|
|
}
|
|
|
|
qboolean G2API_StopBoneAngles(CGhoul2Info *ghlInfo, const char *boneName)
|
|
{
|
|
if (G2_SetupModelPointers(ghlInfo))
|
|
{
|
|
// ensure we flush the cache
|
|
ghlInfo->mSkelFrameNum = 0;
|
|
return G2_Stop_Bone_Angles(ghlInfo->mFileName, ghlInfo->mBlist, boneName);
|
|
}
|
|
return qfalse;
|
|
}
|
|
|
|
|
|
void G2API_AbsurdSmoothing(CGhoul2Info_v &ghoul2, qboolean status)
|
|
{
|
|
assert(ghoul2.size());
|
|
CGhoul2Info *ghlInfo = &ghoul2[0];
|
|
|
|
if (status)
|
|
{ //turn it on
|
|
ghlInfo->mFlags |= GHOUL2_CRAZY_SMOOTH;
|
|
}
|
|
else
|
|
{ //off
|
|
ghlInfo->mFlags &= ~GHOUL2_CRAZY_SMOOTH;
|
|
}
|
|
}
|
|
|
|
//rww - RAGDOLL_BEGIN
|
|
class CRagDollParams;
|
|
void G2_SetRagDoll(CGhoul2Info_v &ghoul2V,CRagDollParams *parms);
|
|
void G2API_SetRagDoll(CGhoul2Info_v &ghoul2,CRagDollParams *parms)
|
|
{
|
|
G2_SetRagDoll(ghoul2,parms);
|
|
}
|
|
|
|
void G2_ResetRagDoll(CGhoul2Info_v &ghoul2V);
|
|
void G2API_ResetRagDoll(CGhoul2Info_v &ghoul2)
|
|
{
|
|
G2_ResetRagDoll(ghoul2);
|
|
}
|
|
//rww - RAGDOLL_END
|
|
|
|
qboolean G2API_RemoveBone(CGhoul2Info_v& ghoul2, int modelIndex, const char *boneName)
|
|
{
|
|
CGhoul2Info *ghlInfo = &ghoul2[modelIndex];
|
|
|
|
if (G2_SetupModelPointers(ghlInfo))
|
|
{
|
|
// ensure we flush the cache
|
|
ghlInfo->mSkelFrameNum = 0;
|
|
return G2_Remove_Bone(ghlInfo, ghlInfo->mBlist, boneName);
|
|
}
|
|
return qfalse;
|
|
}
|
|
|
|
//rww - RAGDOLL_BEGIN
|
|
#ifdef _DEBUG
|
|
extern int ragTraceTime;
|
|
extern int ragSSCount;
|
|
extern int ragTraceCount;
|
|
#endif
|
|
|
|
void G2API_AnimateG2ModelsRag(CGhoul2Info_v &ghoul2, int AcurrentTime,CRagDollUpdateParams *params)
|
|
{
|
|
int model;
|
|
int currentTime=G2API_GetTime(AcurrentTime);
|
|
|
|
#ifdef _DEBUG
|
|
ragTraceTime = 0;
|
|
ragSSCount = 0;
|
|
ragTraceCount = 0;
|
|
#endif
|
|
|
|
// 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,currentTime,model,params);
|
|
}
|
|
}
|
|
#ifdef _DEBUG
|
|
/*
|
|
if (ragTraceTime)
|
|
{
|
|
ri.Printf( PRINT_ALL, "Rag trace time: %i (%i STARTSOLID, %i TOTAL)\n", ragTraceTime, ragSSCount, ragTraceCount);
|
|
}
|
|
*/
|
|
|
|
//keep sane limits here, if it gets too slow an assert is proper.
|
|
// assert(ragTraceTime < 150);
|
|
// assert(ragTraceCount < 1500);
|
|
#endif
|
|
}
|
|
//rww - RAGDOLL_END
|
|
|
|
int G2_Find_Bone_Rag(CGhoul2Info *ghlInfo, boneInfo_v &blist, const char *boneName);
|
|
#define RAG_PCJ (0x00001)
|
|
#define RAG_EFFECTOR (0x00100)
|
|
|
|
static inline boneInfo_t *G2_GetRagBoneConveniently(CGhoul2Info_v &ghoul2, const char *boneName)
|
|
{
|
|
assert(ghoul2.size());
|
|
CGhoul2Info *ghlInfo = &ghoul2[0];
|
|
|
|
if (!(ghlInfo->mFlags & GHOUL2_RAG_STARTED))
|
|
{ //can't do this if not in ragdoll
|
|
return NULL;
|
|
}
|
|
|
|
int boneIndex = G2_Find_Bone_Rag(ghlInfo, ghlInfo->mBlist, boneName);
|
|
|
|
if (boneIndex < 0)
|
|
{ //bad bone specification
|
|
return NULL;
|
|
}
|
|
|
|
boneInfo_t *bone = &ghlInfo->mBlist[boneIndex];
|
|
|
|
if (!(bone->flags & BONE_ANGLES_RAGDOLL))
|
|
{ //only want to return rag bones
|
|
return NULL;
|
|
}
|
|
|
|
return bone;
|
|
}
|
|
|
|
qboolean G2API_RagPCJConstraint(CGhoul2Info_v &ghoul2, const char *boneName, vec3_t min, vec3_t max)
|
|
{
|
|
boneInfo_t *bone = G2_GetRagBoneConveniently(ghoul2, boneName);
|
|
|
|
if (!bone)
|
|
{
|
|
return qfalse;
|
|
}
|
|
|
|
if (!(bone->RagFlags & RAG_PCJ))
|
|
{ //this function is only for PCJ bones
|
|
return qfalse;
|
|
}
|
|
|
|
VectorCopy(min, bone->minAngles);
|
|
VectorCopy(max, bone->maxAngles);
|
|
|
|
return qtrue;
|
|
}
|
|
|
|
qboolean G2API_RagPCJGradientSpeed(CGhoul2Info_v &ghoul2, const char *boneName, const float speed)
|
|
{
|
|
boneInfo_t *bone = G2_GetRagBoneConveniently(ghoul2, boneName);
|
|
|
|
if (!bone)
|
|
{
|
|
return qfalse;
|
|
}
|
|
|
|
if (!(bone->RagFlags & RAG_PCJ))
|
|
{ //this function is only for PCJ bones
|
|
return qfalse;
|
|
}
|
|
|
|
bone->overGradSpeed = speed;
|
|
|
|
return qtrue;
|
|
}
|
|
|
|
qboolean G2API_RagEffectorGoal(CGhoul2Info_v &ghoul2, const char *boneName, vec3_t pos)
|
|
{
|
|
boneInfo_t *bone = G2_GetRagBoneConveniently(ghoul2, boneName);
|
|
|
|
if (!bone)
|
|
{
|
|
return qfalse;
|
|
}
|
|
|
|
if (!(bone->RagFlags & RAG_EFFECTOR))
|
|
{ //this function is only for effectors
|
|
return qfalse;
|
|
}
|
|
|
|
if (!pos)
|
|
{ //go back to none in case we have one then
|
|
bone->hasOverGoal = false;
|
|
}
|
|
else
|
|
{
|
|
VectorCopy(pos, bone->overGoalSpot);
|
|
bone->hasOverGoal = true;
|
|
}
|
|
return qtrue;
|
|
}
|
|
|
|
qboolean G2API_GetRagBonePos(CGhoul2Info_v &ghoul2, const char *boneName, vec3_t pos, vec3_t entAngles, vec3_t entPos, vec3_t entScale)
|
|
{ //do something?
|
|
return qfalse;
|
|
}
|
|
|
|
qboolean G2API_RagEffectorKick(CGhoul2Info_v &ghoul2, const char *boneName, vec3_t velocity)
|
|
{
|
|
boneInfo_t *bone = G2_GetRagBoneConveniently(ghoul2, boneName);
|
|
|
|
if (!bone)
|
|
{
|
|
return qfalse;
|
|
}
|
|
|
|
if (!(bone->RagFlags & RAG_EFFECTOR))
|
|
{ //this function is only for effectors
|
|
return qfalse;
|
|
}
|
|
|
|
bone->epVelocity[2] = 0;
|
|
VectorAdd(bone->epVelocity, velocity, bone->epVelocity);
|
|
bone->physicsSettled = false;
|
|
|
|
return qtrue;
|
|
}
|
|
|
|
qboolean G2API_RagForceSolve(CGhoul2Info_v &ghoul2, qboolean force)
|
|
{
|
|
assert(ghoul2.size());
|
|
CGhoul2Info *ghlInfo = &ghoul2[0];
|
|
|
|
if (!(ghlInfo->mFlags & GHOUL2_RAG_STARTED))
|
|
{ //can't do this if not in ragdoll
|
|
return qfalse;
|
|
}
|
|
|
|
if (force)
|
|
{
|
|
ghlInfo->mFlags |= GHOUL2_RAG_FORCESOLVE;
|
|
}
|
|
else
|
|
{
|
|
ghlInfo->mFlags &= ~GHOUL2_RAG_FORCESOLVE;
|
|
}
|
|
|
|
return qtrue;
|
|
}
|
|
|
|
qboolean G2_SetBoneIKState(CGhoul2Info_v &ghoul2, int time, const char *boneName, int ikState, sharedSetBoneIKStateParams_t *params);
|
|
qboolean G2API_SetBoneIKState(CGhoul2Info_v &ghoul2, int time, const char *boneName, int ikState, sharedSetBoneIKStateParams_t *params)
|
|
{
|
|
return G2_SetBoneIKState(ghoul2, time, boneName, ikState, params);
|
|
}
|
|
|
|
qboolean G2_IKMove(CGhoul2Info_v &ghoul2, int time, sharedIKMoveParams_t *params);
|
|
qboolean G2API_IKMove(CGhoul2Info_v &ghoul2, int time, sharedIKMoveParams_t *params)
|
|
{
|
|
return G2_IKMove(ghoul2, time, params);
|
|
}
|
|
|
|
qboolean G2API_RemoveBolt(CGhoul2Info *ghlInfo, const int index)
|
|
{
|
|
if (G2_SetupModelPointers(ghlInfo))
|
|
{
|
|
return G2_Remove_Bolt( ghlInfo->mBltlist, index);
|
|
}
|
|
return qfalse;
|
|
}
|
|
|
|
int G2API_AddBolt(CGhoul2Info_v &ghoul2, const int modelIndex, const char *boneName)
|
|
{
|
|
// assert(ghoul2.size()>modelIndex);
|
|
|
|
if (ghoul2.size()>modelIndex)
|
|
{
|
|
CGhoul2Info *ghlInfo = &ghoul2[modelIndex];
|
|
if (G2_SetupModelPointers(ghlInfo))
|
|
{
|
|
return G2_Add_Bolt(ghlInfo, ghlInfo->mBltlist, ghlInfo->mSlist, boneName);
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
int G2API_AddBoltSurfNum(CGhoul2Info *ghlInfo, const int surfIndex)
|
|
{
|
|
if (G2_SetupModelPointers(ghlInfo))
|
|
{
|
|
return G2_Add_Bolt_Surf_Num(ghlInfo, ghlInfo->mBltlist, ghlInfo->mSlist, surfIndex);
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
|
|
qboolean G2API_AttachG2Model(CGhoul2Info_v &ghoul2From, int modelFrom, CGhoul2Info_v &ghoul2To, int toBoltIndex, int toModel)
|
|
{
|
|
assert( toBoltIndex >= 0 );
|
|
if ( toBoltIndex < 0 )
|
|
{
|
|
return qfalse;
|
|
}
|
|
if (G2_SetupModelPointers(ghoul2From)&&G2_SetupModelPointers(ghoul2To))
|
|
{
|
|
// make sure we have a model to attach, a model to attach to, and a bolt on that model
|
|
if ((ghoul2From.size() > modelFrom) &&
|
|
(ghoul2To.size() > toModel) &&
|
|
((ghoul2To[toModel].mBltlist[toBoltIndex].boneNumber != -1) || (ghoul2To[toModel].mBltlist[toBoltIndex].surfaceNumber != -1)))
|
|
{
|
|
// encode the bolt address into the model bolt link
|
|
toModel &= MODEL_AND;
|
|
toBoltIndex &= BOLT_AND;
|
|
ghoul2From[modelFrom].mModelBoltLink = (toModel << MODEL_SHIFT) | (toBoltIndex << BOLT_SHIFT);
|
|
return qtrue;
|
|
}
|
|
}
|
|
return qfalse;
|
|
}
|
|
|
|
void G2API_SetBoltInfo(CGhoul2Info_v &ghoul2, int modelIndex, int boltInfo)
|
|
{
|
|
if (ghoul2.size() > modelIndex)
|
|
{
|
|
ghoul2[modelIndex].mModelBoltLink = boltInfo;
|
|
}
|
|
}
|
|
|
|
qboolean G2API_DetachG2Model(CGhoul2Info *ghlInfo)
|
|
{
|
|
if (G2_SetupModelPointers(ghlInfo))
|
|
{
|
|
ghlInfo->mModelBoltLink = -1;
|
|
return qtrue;
|
|
}
|
|
return qfalse;
|
|
}
|
|
|
|
qboolean G2API_AttachEnt(int *boltInfo, CGhoul2Info_v& ghoul2, int modelIndex, int toBoltIndex, int entNum, int toModelNum)
|
|
{
|
|
CGhoul2Info *ghlInfoTo = &ghoul2[modelIndex];
|
|
|
|
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.size() && ((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);
|
|
return qtrue;
|
|
}
|
|
}
|
|
return qfalse;
|
|
|
|
}
|
|
|
|
qboolean gG2_GBMNoReconstruct;
|
|
qboolean gG2_GBMUseSPMethod;
|
|
|
|
qboolean G2API_GetBoltMatrix_SPMethod(CGhoul2Info_v &ghoul2, const int modelIndex, const int boltIndex, mdxaBone_t *matrix, const vec3_t angles,
|
|
const vec3_t position, const int frameNum, qhandle_t *modelList, const vec3_t scale )
|
|
{
|
|
assert(ghoul2.size() > modelIndex);
|
|
|
|
if ((ghoul2.size() > modelIndex))
|
|
{
|
|
CGhoul2Info *ghlInfo = &ghoul2[modelIndex];
|
|
|
|
//assert(boltIndex < ghlInfo->mBltlist.size());
|
|
|
|
if (ghlInfo && (boltIndex < (int)ghlInfo->mBltlist.size()) && boltIndex >= 0 )
|
|
{
|
|
// make sure we have transformed the skeleton
|
|
if (!gG2_GBMNoReconstruct)
|
|
{
|
|
G2_ConstructGhoulSkeleton(ghoul2, frameNum, true, scale);
|
|
}
|
|
|
|
gG2_GBMNoReconstruct = qfalse;
|
|
|
|
mdxaBone_t scaled;
|
|
mdxaBone_t *use;
|
|
use=&ghlInfo->mBltlist[boltIndex].position;
|
|
|
|
if (scale[0]||scale[1]||scale[2])
|
|
{
|
|
scaled=*use;
|
|
use=&scaled;
|
|
|
|
// scale the bolt position by the scale factor for this model since at this point its still in model space
|
|
if (scale[0])
|
|
{
|
|
scaled.matrix[0][3] *= scale[0];
|
|
}
|
|
if (scale[1])
|
|
{
|
|
scaled.matrix[1][3] *= scale[1];
|
|
}
|
|
if (scale[2])
|
|
{
|
|
scaled.matrix[2][3] *= scale[2];
|
|
}
|
|
}
|
|
// pre generate the world matrix
|
|
G2_GenerateWorldMatrix(angles, position);
|
|
|
|
VectorNormalize((float*)use->matrix[0]);
|
|
VectorNormalize((float*)use->matrix[1]);
|
|
VectorNormalize((float*)use->matrix[2]);
|
|
|
|
Multiply_3x4Matrix(matrix, &worldMatrix, use);
|
|
return qtrue;
|
|
}
|
|
}
|
|
return qfalse;
|
|
}
|
|
|
|
#define G2ERROR(exp,m) ((void)0) //rwwFIXMEFIXME: This is because I'm lazy.
|
|
#define G2WARNING(exp,m) ((void)0)
|
|
#define G2NOTE(exp,m) ((void)0)
|
|
#define G2ANIM(ghlInfo,m) ((void)0)
|
|
bool G2_NeedsRecalc(CGhoul2Info *ghlInfo,int frameNum);
|
|
void G2_GetBoltMatrixLow(CGhoul2Info &ghoul2,int boltNum,const vec3_t scale,mdxaBone_t &retMatrix);
|
|
void G2_GetBoneMatrixLow(CGhoul2Info &ghoul2,int boneNum,const vec3_t scale,mdxaBone_t &retMatrix,mdxaBone_t *&retBasepose,mdxaBone_t *&retBaseposeInv);
|
|
|
|
//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 )
|
|
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 frameNum, qhandle_t *modelList, vec3_t scale )
|
|
{
|
|
// G2ERROR(ghoul2.IsValid(),"Invalid ghlInfo");
|
|
G2ERROR(matrix,"NULL matrix");
|
|
G2ERROR(modelIndex>=0&&modelIndex<ghoul2.size(),"Invalid ModelIndex");
|
|
const static mdxaBone_t identityMatrix =
|
|
{
|
|
{
|
|
{ 0.0f, -1.0f, 0.0f, 0.0f },
|
|
{ 1.0f, 0.0f, 0.0f, 0.0f },
|
|
{ 0.0f, 0.0f, 1.0f, 0.0f }
|
|
}
|
|
};
|
|
G2_GenerateWorldMatrix(angles, position);
|
|
if (G2_SetupModelPointers(ghoul2))
|
|
{
|
|
if (matrix&&modelIndex>=0&&modelIndex<ghoul2.size())
|
|
{
|
|
int tframeNum=G2API_GetTime(frameNum);
|
|
CGhoul2Info *ghlInfo = &ghoul2[modelIndex];
|
|
G2ERROR(boltIndex >= 0 && (boltIndex < ghlInfo->mBltlist.size()),va("Invalid Bolt Index (%d:%s)",boltIndex,ghlInfo->mFileName));
|
|
|
|
if (boltIndex >= 0 && ghlInfo && (boltIndex < (int)ghlInfo->mBltlist.size()) )
|
|
{
|
|
mdxaBone_t bolt;
|
|
|
|
#if 0 //yeah, screw it
|
|
if (!gG2_GBMNoReconstruct)
|
|
{ //This should only be used when you know what you're doing.
|
|
if (G2_NeedsRecalc(ghlInfo,tframeNum))
|
|
{
|
|
G2_ConstructGhoulSkeleton(ghoul2,tframeNum,true,scale);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
gG2_GBMNoReconstruct = qfalse;
|
|
}
|
|
#else
|
|
if (G2_NeedsRecalc(ghlInfo,tframeNum))
|
|
{
|
|
G2_ConstructGhoulSkeleton(ghoul2,tframeNum,true,scale);
|
|
}
|
|
#endif
|
|
|
|
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");
|
|
|
|
if (!gG2_GBMUseSPMethod)
|
|
{ //this is horribly stupid and I hate it. But lots of game code is written to assume this 90 degree offset thing.
|
|
float ftemp;
|
|
ftemp = matrix->matrix[0][0];
|
|
matrix->matrix[0][0] = -matrix->matrix[0][1];
|
|
matrix->matrix[0][1] = ftemp;
|
|
|
|
ftemp = matrix->matrix[1][0];
|
|
matrix->matrix[1][0] = -matrix->matrix[1][1];
|
|
matrix->matrix[1][1] = ftemp;
|
|
|
|
ftemp = matrix->matrix[2][0];
|
|
matrix->matrix[2][0] = -matrix->matrix[2][1];
|
|
matrix->matrix[2][1] = ftemp;
|
|
}
|
|
else
|
|
{ //reset it
|
|
gG2_GBMUseSPMethod = qfalse;
|
|
}
|
|
|
|
return qtrue;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
G2WARNING(0,"G2API_GetBoltMatrix Failed on empty or bad model");
|
|
}
|
|
Multiply_3x4Matrix(matrix, &worldMatrix, (mdxaBone_t *)&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)
|
|
{
|
|
for (int i=0; i<ghoul2.size();i++)
|
|
{
|
|
if (ghoul2[i].mModelindex != -1)
|
|
{
|
|
return qtrue;
|
|
}
|
|
}
|
|
|
|
return qfalse;
|
|
}
|
|
|
|
// 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)
|
|
{
|
|
}
|
|
|
|
|
|
char *G2API_GetAnimFileNameIndex(qhandle_t modelIndex)
|
|
{
|
|
model_t *mod_m = R_GetModelByHandle(modelIndex);
|
|
return mod_m->mdxm->animName;
|
|
}
|
|
|
|
/************************************************************************************************
|
|
* 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)
|
|
{
|
|
if (G2_SetupModelPointers(ghlInfo))
|
|
{
|
|
return G2_GetAnimFileName(ghlInfo->mFileName, filename);
|
|
}
|
|
return qfalse;
|
|
}
|
|
|
|
/*
|
|
=======================
|
|
SV_QsortEntityNumbers
|
|
=======================
|
|
*/
|
|
static int QDECL QsortDistance( const void *a, const void *b ) {
|
|
const float &ea = ((CollisionRecord_t*)a)->mDistance;
|
|
const float &eb = ((CollisionRecord_t*)b)->mDistance;
|
|
|
|
if ( ea < eb ) {
|
|
return -1;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static inline bool G2_NeedRetransform(CGhoul2Info *g2, int frameNum)
|
|
{ //see if we need to do another transform
|
|
size_t i = 0;
|
|
bool needTrans = false;
|
|
while (i < g2->mBlist.size())
|
|
{
|
|
float time;
|
|
boneInfo_t &bone = g2->mBlist[i];
|
|
|
|
if (bone.pauseTime)
|
|
{
|
|
time = (bone.pauseTime - bone.startTime) / 50.0f;
|
|
}
|
|
else
|
|
{
|
|
time = (frameNum - bone.startTime) / 50.0f;
|
|
}
|
|
int newFrame = bone.startFrame + (time * bone.animSpeed);
|
|
|
|
if (newFrame < bone.endFrame ||
|
|
(bone.flags & BONE_ANIM_OVERRIDE_LOOP) ||
|
|
(bone.flags & BONE_NEED_TRANSFORM))
|
|
{ //ok, we're gonna have to do it. bone is apparently animating.
|
|
bone.flags &= ~BONE_NEED_TRANSFORM;
|
|
needTrans = true;
|
|
}
|
|
i++;
|
|
}
|
|
|
|
return needTrans;
|
|
}
|
|
|
|
void G2API_CollisionDetectCache(CollisionRecord_t *collRecMap, CGhoul2Info_v &ghoul2, const vec3_t angles, const vec3_t position,
|
|
int frameNumber, int entNum, vec3_t rayStart, vec3_t rayEnd, vec3_t scale, IHeapAllocator *G2VertSpace, int traceFlags, int useLod, float fRadius)
|
|
{ //this will store off the transformed verts for the next trace - this is slower, but for models that do not animate
|
|
//frequently it is much much faster. -rww
|
|
#if 0 // UNUSED
|
|
int *test = ghoul2[0].mTransformedVertsArray;
|
|
#endif
|
|
if (G2_SetupModelPointers(ghoul2))
|
|
{
|
|
vec3_t transRayStart, transRayEnd;
|
|
|
|
int tframeNum=G2API_GetTime(frameNumber);
|
|
// make sure we have transformed the whole skeletons for each model
|
|
if (G2_NeedRetransform(&ghoul2[0], tframeNum) || !ghoul2[0].mTransformedVertsArray)
|
|
{ //optimization, only create new transform space if we need to, otherwise
|
|
//store it off!
|
|
int i = 0;
|
|
while (i < ghoul2.size())
|
|
{
|
|
CGhoul2Info &g2 = ghoul2[i];
|
|
|
|
/*
|
|
if ((g2.mFlags & GHOUL2_ZONETRANSALLOC) && g2.mTransformedVertsArray)
|
|
{ //clear it out, yo.
|
|
Z_Free(g2.mTransformedVertsArray);
|
|
g2.mTransformedVertsArray = 0;
|
|
}
|
|
*/
|
|
if (!g2.mTransformedVertsArray || !(g2.mFlags & GHOUL2_ZONETRANSALLOC))
|
|
{ //reworked so we only alloc once!
|
|
//if we have a pointer, but not a ghoul2_zonetransalloc flag, then that means
|
|
//it is a miniheap pointer. Just stomp over it.
|
|
int iSize = g2.currentModel->mdxm->numSurfaces * 4;
|
|
g2.mTransformedVertsArray = (size_t *)Z_Malloc(iSize, TAG_GHOUL2, qtrue);
|
|
}
|
|
|
|
g2.mFlags |= GHOUL2_ZONETRANSALLOC;
|
|
|
|
i++;
|
|
}
|
|
G2_ConstructGhoulSkeleton(ghoul2, frameNumber, true, scale);
|
|
G2VertSpace->ResetHeap();
|
|
|
|
// now having done that, time to build the model
|
|
#ifdef _G2_GORE
|
|
G2_TransformModel(ghoul2, frameNumber, scale, G2VertSpace, useLod, false);
|
|
#else
|
|
G2_TransformModel(ghoul2, frameNumber, scale, G2VertSpace, useLod);
|
|
#endif
|
|
|
|
//don't need to do this anymore now that I am using a flag for zone alloc.
|
|
/*
|
|
i = 0;
|
|
while (i < ghoul2.size())
|
|
{
|
|
CGhoul2Info &g2 = ghoul2[i];
|
|
int iSize = g2.currentModel->mdxm->numSurfaces * 4;
|
|
|
|
int *zoneMem = (int *)Z_Malloc(iSize, TAG_GHOUL2, qtrue);
|
|
memcpy(zoneMem, g2.mTransformedVertsArray, iSize);
|
|
g2.mTransformedVertsArray = zoneMem;
|
|
g2.mFlags |= GHOUL2_ZONETRANSALLOC;
|
|
i++;
|
|
}
|
|
*/
|
|
}
|
|
|
|
// pre generate the world matrix - used to transform the incoming ray
|
|
G2_GenerateWorldMatrix(angles, position);
|
|
|
|
// 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.
|
|
#ifdef _G2_GORE
|
|
G2_TraceModels(ghoul2, transRayStart, transRayEnd, collRecMap, entNum, traceFlags, useLod, fRadius,0,0,0,0,0,qfalse);
|
|
#else
|
|
G2_TraceModels(ghoul2, transRayStart, transRayEnd, collRecMap, entNum, traceFlags, useLod, fRadius);
|
|
#endif
|
|
int i;
|
|
for ( i = 0; i < MAX_G2_COLLISIONS && collRecMap[i].mEntityNum != -1; i ++ );
|
|
|
|
// now sort the resulting array of collision records so they are distance ordered
|
|
qsort( collRecMap, i,
|
|
sizeof( CollisionRecord_t ), QsortDistance );
|
|
}
|
|
}
|
|
|
|
|
|
void G2API_CollisionDetect(CollisionRecord_t *collRecMap, CGhoul2Info_v &ghoul2, const vec3_t angles, const vec3_t position,
|
|
int frameNumber, int entNum, vec3_t rayStart, vec3_t rayEnd, vec3_t scale, IHeapAllocator *G2VertSpace, int traceFlags, int useLod, float fRadius)
|
|
{
|
|
/*
|
|
if (1)
|
|
{
|
|
G2API_CollisionDetectCache(collRecMap, ghoul2, angles, position, frameNumber, entNum,
|
|
rayStart, rayEnd, scale, G2VertSpace, traceFlags, useLod, fRadius);
|
|
return;
|
|
}
|
|
*/
|
|
|
|
if (G2_SetupModelPointers(ghoul2))
|
|
{
|
|
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);
|
|
|
|
G2VertSpace->ResetHeap();
|
|
|
|
// now having done that, time to build the model
|
|
#ifdef _G2_GORE
|
|
G2_TransformModel(ghoul2, frameNumber, scale, G2VertSpace, useLod, false);
|
|
#else
|
|
G2_TransformModel(ghoul2, frameNumber, scale, G2VertSpace, useLod);
|
|
#endif
|
|
|
|
// 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.
|
|
#ifdef _G2_GORE
|
|
G2_TraceModels(ghoul2, transRayStart, transRayEnd, collRecMap, entNum, traceFlags, useLod, fRadius,0,0,0,0,0,qfalse);
|
|
#else
|
|
G2_TraceModels(ghoul2, transRayStart, transRayEnd, collRecMap, entNum, traceFlags, useLod, fRadius);
|
|
#endif
|
|
int i;
|
|
for ( i = 0; i < MAX_G2_COLLISIONS && collRecMap[i].mEntityNum != -1; i ++ );
|
|
|
|
// now sort the resulting array of collision records so they are distance ordered
|
|
qsort( collRecMap, i,
|
|
sizeof( CollisionRecord_t ), QsortDistance );
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
|
|
int G2API_CopyGhoul2Instance(CGhoul2Info_v &g2From, CGhoul2Info_v &g2To, 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 (g2From.IsValid())
|
|
{
|
|
#ifdef _DEBUG
|
|
if (g2To.IsValid())
|
|
{
|
|
assert(!"Copying to a valid g2 instance?!");
|
|
|
|
if (g2To[0].mBoneCache)
|
|
{
|
|
assert(!"Instance has a bonecache too.. it's gonna get stomped");
|
|
}
|
|
}
|
|
#endif
|
|
g2To.DeepCopy(g2From);
|
|
|
|
#ifdef _G2_GORE //check through gore stuff then, as well.
|
|
int model = 0;
|
|
|
|
while (model < g2To.size())
|
|
{
|
|
if ( g2To[model].mGoreSetTag )
|
|
{
|
|
CGoreSet* gore = FindGoreSet ( g2To[model].mGoreSetTag );
|
|
assert(gore);
|
|
gore->mRefCount++;
|
|
}
|
|
|
|
model++;
|
|
}
|
|
#endif
|
|
//G2ANIM(ghoul2From,"G2API_CopyGhoul2Instance (source)");
|
|
//G2ANIM(ghoul2To,"G2API_CopyGhoul2Instance (dest)");
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
void G2API_CopySpecificG2Model(CGhoul2Info_v &ghoul2From, int modelFrom, CGhoul2Info_v &ghoul2To, int modelTo)
|
|
{
|
|
#if 0
|
|
qboolean forceReconstruct = qtrue;
|
|
#endif //model1 was not getting reconstructed like it should for thrown sabers?
|
|
//might have been a bug in the reconstruct checking which has since been
|
|
//mangled and probably fixed. -rww
|
|
|
|
// assume we actually have a model to copy from
|
|
if (ghoul2From.size() > modelFrom)
|
|
{
|
|
// if we don't have enough models on the to side, resize us so we do
|
|
if (ghoul2To.size() <= modelTo)
|
|
{
|
|
assert (modelTo < 5);
|
|
ghoul2To.resize(modelTo + 1);
|
|
#if 0
|
|
forceReconstruct = qtrue;
|
|
#endif
|
|
}
|
|
// do the copy
|
|
|
|
if (ghoul2To.IsValid() && ghoul2To.size() >= modelTo)
|
|
{ //remove the bonecache before we stomp over this instance.
|
|
if (ghoul2To[modelTo].mBoneCache)
|
|
{
|
|
RemoveBoneCache(ghoul2To[modelTo].mBoneCache);
|
|
ghoul2To[modelTo].mBoneCache = 0;
|
|
}
|
|
}
|
|
ghoul2To[modelTo] = ghoul2From[modelFrom];
|
|
|
|
#if 0
|
|
if (forceReconstruct)
|
|
{ //rww - we should really do this shouldn't we? If we don't mark a reconstruct after this,
|
|
//and we do a GetBoltMatrix in the same frame, it doesn't reconstruct the skeleton and returns
|
|
//a completely invalid matrix
|
|
ghoul2To[0].mSkelFrameNum = 0;
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
// This version will automatically copy everything about this model, and make a new one if necessary.
|
|
void G2API_DuplicateGhoul2Instance(CGhoul2Info_v &g2From, CGhoul2Info_v **g2To)
|
|
{
|
|
//int ignore;
|
|
|
|
if (*g2To)
|
|
{ // This is bad. We only want to do this if there is not yet a to declared.
|
|
assert(0);
|
|
return;
|
|
}
|
|
|
|
*g2To = new CGhoul2Info_v;
|
|
#ifdef _FULL_G2_LEAK_CHECKING
|
|
if (g_G2AllocServer)
|
|
{
|
|
g_G2ServerAlloc += sizeof(CGhoul2Info_v);
|
|
}
|
|
else
|
|
{
|
|
g_G2ClientAlloc += sizeof(CGhoul2Info_v);
|
|
}
|
|
g_Ghoul2Allocations += sizeof(CGhoul2Info_v);
|
|
G2_DEBUG_ShovePtrInTracker(*g2To);
|
|
#endif
|
|
CGhoul2Info_v &ghoul2 = *(*g2To);
|
|
|
|
/*ignore = */G2API_CopyGhoul2Instance(g2From, ghoul2, -1);
|
|
|
|
return;
|
|
}
|
|
|
|
char *G2API_GetSurfaceName(CGhoul2Info_v& ghoul2, int modelIndex, int surfNumber)
|
|
{
|
|
static char noSurface[1] = "";
|
|
CGhoul2Info *ghlInfo = &ghoul2[modelIndex];
|
|
|
|
if (G2_SetupModelPointers(ghlInfo))
|
|
{
|
|
model_t *mod = (model_t *)ghlInfo->currentModel;
|
|
mdxmSurface_t *surf = 0;
|
|
mdxmSurfHierarchy_t *surfInfo = 0;
|
|
|
|
#ifndef FINAL_BUILD
|
|
if (!mod || !mod->mdxm)
|
|
{
|
|
Com_Error(ERR_DROP, "G2API_GetSurfaceName: Bad model on instance %s.", ghlInfo->mFileName);
|
|
}
|
|
#endif
|
|
|
|
//ok, I guess it's semi-valid for the user to be passing in surface > numSurfs because they don't know how many surfs a model
|
|
//may have.. but how did they get that surf index to begin with? Oh well.
|
|
if (surfNumber < 0 || surfNumber >= mod->mdxm->numSurfaces)
|
|
{
|
|
ri.Printf( PRINT_ALL, "G2API_GetSurfaceName: You passed in an invalid surface number (%i) for model %s.\n", surfNumber, ghlInfo->mFileName);
|
|
return noSurface;
|
|
}
|
|
|
|
|
|
surf = (mdxmSurface_t *)G2_FindSurface((void *)mod, surfNumber, 0);
|
|
if (surf)
|
|
{
|
|
#ifndef FINAL_BUILD
|
|
if (surf->thisSurfaceIndex < 0 || surf->thisSurfaceIndex >= mod->mdxm->numSurfaces)
|
|
{
|
|
Com_Error(ERR_DROP, "G2API_GetSurfaceName: Bad surf num (%i) on surf for instance %s.", surf->thisSurfaceIndex, ghlInfo->mFileName);
|
|
}
|
|
#endif
|
|
mdxmHierarchyOffsets_t *surfIndexes = (mdxmHierarchyOffsets_t *)((byte *)mod->mdxm + sizeof(mdxmHeader_t));
|
|
surfInfo = (mdxmSurfHierarchy_t *)((byte *)surfIndexes + surfIndexes->offsets[surf->thisSurfaceIndex]);
|
|
return surfInfo->name;
|
|
}
|
|
}
|
|
return noSurface;
|
|
}
|
|
|
|
|
|
int G2API_GetSurfaceIndex(CGhoul2Info *ghlInfo, const char *surfaceName)
|
|
{
|
|
if (G2_SetupModelPointers(ghlInfo))
|
|
{
|
|
return G2_GetSurfaceIndex(ghlInfo, surfaceName);
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
char *G2API_GetGLAName(CGhoul2Info_v &ghoul2, int modelIndex)
|
|
{
|
|
if (G2_SetupModelPointers(ghoul2))
|
|
{
|
|
if (ghoul2.size() > modelIndex)
|
|
{
|
|
//model_t *mod = R_GetModelByHandle(RE_RegisterModel(ghoul2[modelIndex].mFileName));
|
|
//return mod->mdxm->animName;
|
|
|
|
assert(ghoul2[modelIndex].currentModel && ghoul2[modelIndex].currentModel->mdxm);
|
|
return ghoul2[modelIndex].currentModel->mdxm->animName;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
qboolean G2API_SetNewOrigin(CGhoul2Info_v &ghoul2, const int boltIndex)
|
|
{
|
|
CGhoul2Info *ghlInfo = NULL;
|
|
|
|
if (ghoul2.size()>0)
|
|
{
|
|
ghlInfo = &ghoul2[0];
|
|
}
|
|
|
|
if (G2_SetupModelPointers(ghlInfo))
|
|
{
|
|
if (boltIndex < 0)
|
|
{
|
|
char modelName[MAX_QPATH];
|
|
if (ghlInfo->currentModel &&
|
|
ghlInfo->currentModel->name[0])
|
|
{
|
|
strcpy(modelName, ghlInfo->currentModel->name);
|
|
}
|
|
else
|
|
{
|
|
strcpy(modelName, "None?!");
|
|
}
|
|
|
|
Com_Error(ERR_DROP, "Bad boltindex (%i) trying to SetNewOrigin (naughty naughty!)\nModel %s\n", boltIndex, modelName);
|
|
}
|
|
|
|
ghlInfo->mNewOrigin = boltIndex;
|
|
ghlInfo->mFlags |= GHOUL2_NEWORIGIN;
|
|
return qtrue;
|
|
}
|
|
return qfalse;
|
|
}
|
|
|
|
int G2API_GetBoneIndex(CGhoul2Info *ghlInfo, const char *boneName)
|
|
{
|
|
if (G2_SetupModelPointers(ghlInfo))
|
|
{
|
|
return G2_Get_Bone_Index(ghlInfo, boneName);
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
qboolean G2API_SaveGhoul2Models(CGhoul2Info_v &ghoul2, char **buffer, int *size)
|
|
{
|
|
return G2_SaveGhoul2Models(ghoul2, buffer, size);
|
|
}
|
|
|
|
void G2API_LoadGhoul2Models(CGhoul2Info_v &ghoul2, char *buffer)
|
|
{
|
|
G2_LoadGhoul2Model(ghoul2, buffer);
|
|
}
|
|
|
|
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)
|
|
{
|
|
|
|
#ifdef _G2_GORE
|
|
G2API_ClearSkinGore ( ghoul2 );
|
|
#endif
|
|
ghoul2.~CGhoul2Info_v(); // so I can load junk over it then memset to 0 without orphaning
|
|
}
|
|
|
|
//see if surfs have any shader info...
|
|
qboolean G2API_SkinlessModel(CGhoul2Info_v& ghoul2, int modelIndex)
|
|
{
|
|
CGhoul2Info *g2 = &ghoul2[modelIndex];
|
|
|
|
if (G2_SetupModelPointers(g2))
|
|
{
|
|
model_t *mod = (model_t *)g2->currentModel;
|
|
|
|
if (mod &&
|
|
mod->mdxm)
|
|
{
|
|
mdxmSurfHierarchy_t *surf;
|
|
int i;
|
|
|
|
surf = (mdxmSurfHierarchy_t *) ( (byte *)mod->mdxm + mod->mdxm->ofsSurfHierarchy );
|
|
|
|
for (i = 0; i < mod->mdxm->numSurfaces; i++)
|
|
{
|
|
if (surf->shader[0])
|
|
{ //found a surface with a shader name, ok.
|
|
return qfalse;
|
|
}
|
|
|
|
surf = (mdxmSurfHierarchy_t *)( (byte *)surf + (intptr_t)( &((mdxmSurfHierarchy_t *)0)->childIndexes[ surf->numChildren ] ));
|
|
}
|
|
}
|
|
}
|
|
|
|
//found nothing.
|
|
return qtrue;
|
|
}
|
|
|
|
//#ifdef _SOF2
|
|
#ifdef _G2_GORE
|
|
void ResetGoreTag(); // put here to reduce coupling
|
|
|
|
//way of seeing how many marks are on a model currently -rww
|
|
int G2API_GetNumGoreMarks(CGhoul2Info_v& ghoul2, int modelIndex)
|
|
{
|
|
CGhoul2Info *g2 = &ghoul2[modelIndex];
|
|
|
|
if (g2->mGoreSetTag)
|
|
{
|
|
CGoreSet *goreSet = FindGoreSet(g2->mGoreSetTag);
|
|
|
|
if (goreSet)
|
|
{
|
|
return goreSet->mGoreRecords.size();
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void G2API_ClearSkinGore ( CGhoul2Info_v &ghoul2 )
|
|
{
|
|
int i;
|
|
|
|
for (i=0; i<ghoul2.size(); i++)
|
|
{
|
|
if ( ghoul2[i].mGoreSetTag )
|
|
{
|
|
DeleteGoreSet ( ghoul2[i].mGoreSetTag );
|
|
ghoul2[i].mGoreSetTag = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
int G2API_Ghoul2Size ( CGhoul2Info_v &ghoul2 )
|
|
{
|
|
return ghoul2.size();
|
|
}
|
|
|
|
extern int G2_DecideTraceLod(CGhoul2Info &ghoul2, int useLod);
|
|
void G2API_AddSkinGore(CGhoul2Info_v &ghoul2,SSkinGoreData &gore)
|
|
{
|
|
if (VectorLength(gore.rayDirection)<.1f)
|
|
{
|
|
assert(0); // can't add gore without a shot direction
|
|
return;
|
|
}
|
|
|
|
// make sure we have transformed the whole skeletons for each model
|
|
//G2_ConstructGhoulSkeleton(ghoul2, gore.currentTime, NULL, true, gore.angles, gore.position, gore.scale, false);
|
|
G2_ConstructGhoulSkeleton(ghoul2, gore.currentTime, true, gore.scale);
|
|
|
|
// pre generate the world matrix - used to transform the incoming ray
|
|
G2_GenerateWorldMatrix(gore.angles, gore.position);
|
|
|
|
// first up, translate the ray to model space
|
|
vec3_t transRayDirection, transHitLocation;
|
|
TransformAndTranslatePoint(gore.hitLocation, transHitLocation, &worldMatrixInv);
|
|
TransformPoint(gore.rayDirection, transRayDirection, &worldMatrixInv);
|
|
|
|
int lod;
|
|
ResetGoreTag();
|
|
const int lodbias=Com_Clamp ( 0, 2,G2_DecideTraceLod(ghoul2[0], ri.Cvar_VariableIntegerValue( "r_lodbias" )));
|
|
const int maxLod =Com_Clamp (0,ghoul2[0].currentModel->numLods,3); //limit to the number of lods the main model has
|
|
for(lod=lodbias;lod<maxLod;lod++)
|
|
{
|
|
// now having done that, time to build the model
|
|
ri.GetG2VertSpaceServer()->ResetHeap();
|
|
|
|
G2_TransformModel(ghoul2, gore.currentTime, gore.scale,ri.GetG2VertSpaceServer(),lod,true);
|
|
|
|
// now walk each model and compute new texture coordinates
|
|
G2_TraceModels(ghoul2, transHitLocation, transRayDirection, 0, gore.entNum, 0,lod,0.0f,gore.SSize,gore.TSize,gore.theta,gore.shader,&gore,qtrue);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
qboolean G2_TestModelPointers(CGhoul2Info *ghlInfo) // returns true if the model is properly set up
|
|
{
|
|
G2ERROR(ghlInfo,"NULL ghlInfo");
|
|
if (!ghlInfo)
|
|
{
|
|
return qfalse;
|
|
}
|
|
ghlInfo->mValid=false;
|
|
if (ghlInfo->mModelindex != -1)
|
|
{
|
|
if (ri.Cvar_VariableIntegerValue( "dedicated" ) ||
|
|
(G2_ShouldRegisterServer())) //supreme hackery!
|
|
{
|
|
ghlInfo->mModel = RE_RegisterServerModel(ghlInfo->mFileName);
|
|
}
|
|
else
|
|
{
|
|
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 (qboolean)ghlInfo->mValid;
|
|
}
|
|
|
|
#ifdef G2_PERFORMANCE_ANALYSIS
|
|
#include "qcommon/timing.h"
|
|
extern timing_c G2PerformanceTimer_G2_SetupModelPointers;
|
|
extern int G2Time_G2_SetupModelPointers;
|
|
#endif
|
|
|
|
qboolean G2_SetupModelPointers(CGhoul2Info *ghlInfo) // returns true if the model is properly set up
|
|
{
|
|
#ifdef G2_PERFORMANCE_ANALYSIS
|
|
G2PerformanceTimer_G2_SetupModelPointers.Start();
|
|
#endif
|
|
G2ERROR(ghlInfo,"NULL ghlInfo");
|
|
if (!ghlInfo)
|
|
{
|
|
return qfalse;
|
|
}
|
|
|
|
// if (ghlInfo->mValid && ghlInfo->currentModel)
|
|
if (0)
|
|
{ //rww - Why are we bothering with all this? We can't change models like this anyway.
|
|
//This function goes over 200k on the precision timer (in debug, but still), so I'm
|
|
//cutting it off here because it gets called constantly.
|
|
#ifdef G2_PERFORMANCE_ANALYSIS
|
|
G2Time_G2_SetupModelPointers += G2PerformanceTimer_G2_SetupModelPointers.End();
|
|
#endif
|
|
return qtrue;
|
|
}
|
|
|
|
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");
|
|
|
|
// RJ - experimental optimization!
|
|
if (!ghlInfo->mModel || 1)
|
|
{
|
|
if (ri.Cvar_VariableIntegerValue( "dedicated" ) ||
|
|
(G2_ShouldRegisterServer())) //supreme hackery!
|
|
{
|
|
ghlInfo->mModel = RE_RegisterServerModel(ghlInfo->mFileName);
|
|
}
|
|
else
|
|
{
|
|
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;
|
|
}
|
|
|
|
#ifdef G2_PERFORMANCE_ANALYSIS
|
|
G2Time_G2_SetupModelPointers += G2PerformanceTimer_G2_SetupModelPointers.End();
|
|
#endif
|
|
return (qboolean)ghlInfo->mValid;
|
|
}
|
|
|
|
qboolean G2_SetupModelPointers(CGhoul2Info_v &ghoul2) // returns true if any model is properly set up
|
|
{
|
|
bool ret=false;
|
|
int i;
|
|
for (i=0; i<ghoul2.size(); i++)
|
|
{
|
|
qboolean r = G2_SetupModelPointers(&ghoul2[i]);
|
|
ret=ret||r;
|
|
}
|
|
return (qboolean)ret;
|
|
}
|
|
|
|
qboolean G2API_IsGhoul2InfovValid (CGhoul2Info_v& ghoul2)
|
|
{
|
|
return (qboolean)ghoul2.IsValid();
|
|
}
|
|
|
|
const char *G2API_GetModelName ( CGhoul2Info_v& ghoul2, int modelIndex )
|
|
{
|
|
return ghoul2[modelIndex].mFileName;
|
|
}
|