/*
===========================================================================
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 .
===========================================================================
*/
#include "client/client.h" //FIXME!! EVIL - just include the definitions needed
#include "tr_local.h"
#include "qcommon/matcomp.h"
#include "qcommon/qcommon.h"
#include "ghoul2/G2.h"
#include "ghoul2/g2_local.h"
#ifdef _G2_GORE
#include "ghoul2/G2_gore.h"
#endif
#include "qcommon/disablewarnings.h"
#define LL(x) x=LittleLong(x)
#define LS(x) x=LittleShort(x)
#define LF(x) x=LittleFloat(x)
#ifdef G2_PERFORMANCE_ANALYSIS
#include "qcommon/timing.h"
timing_c G2PerformanceTimer_RenderSurfaces;
timing_c G2PerformanceTimer_R_AddGHOULSurfaces;
timing_c G2PerformanceTimer_G2_TransformGhoulBones;
timing_c G2PerformanceTimer_G2_ProcessGeneratedSurfaceBolts;
timing_c G2PerformanceTimer_ProcessModelBoltSurfaces;
timing_c G2PerformanceTimer_G2_ConstructGhoulSkeleton;
timing_c G2PerformanceTimer_RB_SurfaceGhoul;
timing_c G2PerformanceTimer_G2_SetupModelPointers;
timing_c G2PerformanceTimer_PreciseFrame;
int G2PerformanceCounter_G2_TransformGhoulBones = 0;
int G2Time_RenderSurfaces = 0;
int G2Time_R_AddGHOULSurfaces = 0;
int G2Time_G2_TransformGhoulBones = 0;
int G2Time_G2_ProcessGeneratedSurfaceBolts = 0;
int G2Time_ProcessModelBoltSurfaces = 0;
int G2Time_G2_ConstructGhoulSkeleton = 0;
int G2Time_RB_SurfaceGhoul = 0;
int G2Time_G2_SetupModelPointers = 0;
int G2Time_PreciseFrame = 0;
void G2Time_ResetTimers(void)
{
G2Time_RenderSurfaces = 0;
G2Time_R_AddGHOULSurfaces = 0;
G2Time_G2_TransformGhoulBones = 0;
G2Time_G2_ProcessGeneratedSurfaceBolts = 0;
G2Time_ProcessModelBoltSurfaces = 0;
G2Time_G2_ConstructGhoulSkeleton = 0;
G2Time_RB_SurfaceGhoul = 0;
G2Time_G2_SetupModelPointers = 0;
G2Time_PreciseFrame = 0;
G2PerformanceCounter_G2_TransformGhoulBones = 0;
}
void G2Time_ReportTimers(void)
{
ri.Printf( PRINT_ALL, "\n---------------------------------\nRenderSurfaces: %i\nR_AddGhoulSurfaces: %i\nG2_TransformGhoulBones: %i\nG2_ProcessGeneratedSurfaceBolts: %i\nProcessModelBoltSurfaces: %i\nG2_ConstructGhoulSkeleton: %i\nRB_SurfaceGhoul: %i\nG2_SetupModelPointers: %i\n\nPrecise frame time: %i\nTransformGhoulBones calls: %i\n---------------------------------\n\n",
G2Time_RenderSurfaces,
G2Time_R_AddGHOULSurfaces,
G2Time_G2_TransformGhoulBones,
G2Time_G2_ProcessGeneratedSurfaceBolts,
G2Time_ProcessModelBoltSurfaces,
G2Time_G2_ConstructGhoulSkeleton,
G2Time_RB_SurfaceGhoul,
G2Time_G2_SetupModelPointers,
G2Time_PreciseFrame,
G2PerformanceCounter_G2_TransformGhoulBones
);
}
#endif
//rww - RAGDOLL_BEGIN
#ifdef __linux__
#include
#else
#include
#endif
//rww - RAGDOLL_END
bool HackadelicOnClient=false; // means this is a render traversal
qboolean G2_SetupModelPointers(CGhoul2Info *ghlInfo);
qboolean G2_SetupModelPointers(CGhoul2Info_v &ghoul2);
extern cvar_t *r_Ghoul2AnimSmooth;
extern cvar_t *r_Ghoul2UnSqashAfterSmooth;
#if 0
static inline int G2_Find_Bone_ByNum(const model_t *mod, boneInfo_v &blist, const int boneNum)
{
size_t i = 0;
while (i < blist.size())
{
if (blist[i].boneNumber == boneNum)
{
return i;
}
i++;
}
return -1;
}
#endif
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 }
}
};
// I hate doing this, but this is the simplest way to get this into the routines it needs to be
mdxaBone_t worldMatrix;
mdxaBone_t worldMatrixInv;
#ifdef _G2_GORE
qhandle_t goreShader=-1;
#endif
class CConstructBoneList
{
public:
int surfaceNum;
int *boneUsedList;
surfaceInfo_v &rootSList;
model_t *currentModel;
boneInfo_v &boneList;
CConstructBoneList(
int initsurfaceNum,
int *initboneUsedList,
surfaceInfo_v &initrootSList,
model_t *initcurrentModel,
boneInfo_v &initboneList):
surfaceNum(initsurfaceNum),
boneUsedList(initboneUsedList),
rootSList(initrootSList),
currentModel(initcurrentModel),
boneList(initboneList) { }
};
class CTransformBone
{
public:
int touch; // for minimal recalculation
//rww - RAGDOLL_BEGIN
int touchRender;
//rww - RAGDOLL_END
mdxaBone_t boneMatrix; //final matrix
int parent; // only set once
CTransformBone()
{
touch=0;
//rww - RAGDOLL_BEGIN
touchRender = 0;
//rww - RAGDOLL_END
}
};
struct SBoneCalc
{
int newFrame;
int currentFrame;
float backlerp;
float blendFrame;
int blendOldFrame;
bool blendMode;
float blendLerp;
};
class CBoneCache;
void G2_TransformBone(int index,CBoneCache &CB);
class CBoneCache
{
void SetRenderMatrix(CTransformBone *bone) {
}
void EvalLow(int index)
{
assert(index>=0&&index<(int)mBones.size());
if (mFinalBones[index].touch!=mCurrentTouch)
{
// need to evaluate the bone
assert((mFinalBones[index].parent>=0&&mFinalBones[index].parent<(int)mFinalBones.size())||(index==0&&mFinalBones[index].parent==-1));
if (mFinalBones[index].parent>=0)
{
EvalLow(mFinalBones[index].parent); // make sure parent is evaluated
SBoneCalc &par=mBones[mFinalBones[index].parent];
mBones[index].newFrame=par.newFrame;
mBones[index].currentFrame=par.currentFrame;
mBones[index].backlerp=par.backlerp;
mBones[index].blendFrame=par.blendFrame;
mBones[index].blendOldFrame=par.blendOldFrame;
mBones[index].blendMode=par.blendMode;
mBones[index].blendLerp=par.blendLerp;
}
G2_TransformBone(index,*this);
mFinalBones[index].touch=mCurrentTouch;
}
}
//rww - RAGDOLL_BEGIN
void SmoothLow(int index)
{
if (mSmoothBones[index].touch==mLastTouch)
{
int i;
float *oldM=&mSmoothBones[index].boneMatrix.matrix[0][0];
float *newM=&mFinalBones[index].boneMatrix.matrix[0][0];
#if 0 //this is just too slow. I need a better way.
static float smoothFactor;
smoothFactor = mSmoothFactor;
//Special rag smoothing -rww
if (smoothFactor < 0)
{ //I need a faster way to do this but I do not want to store more in the bonecache
static int blistIndex;
assert(mod);
assert(rootBoneList);
blistIndex = G2_Find_Bone_ByNum(mod, *rootBoneList, index);
assert(blistIndex != -1);
boneInfo_t &bone = (*rootBoneList)[blistIndex];
if (bone.flags & BONE_ANGLES_RAGDOLL)
{
if ((bone.RagFlags & (0x00008)) || //pelvis
(bone.RagFlags & (0x00004))) //model_root
{ //pelvis and root do not smooth much
smoothFactor = 0.2f;
}
else if (bone.solidCount > 4)
{ //if stuck in solid a lot then snap out quickly
smoothFactor = 0.1f;
}
else
{ //otherwise smooth a bunch
smoothFactor = 0.8f;
}
}
else
{ //not a rag bone
smoothFactor = 0.3f;
}
}
#endif
for (i=0;i<12;i++,oldM++,newM++)
{
*oldM=mSmoothFactor*(*oldM-*newM)+*newM;
}
}
else
{
memcpy(&mSmoothBones[index].boneMatrix,&mFinalBones[index].boneMatrix,sizeof(mdxaBone_t));
}
mdxaSkelOffsets_t *offsets = (mdxaSkelOffsets_t *)((byte *)header + sizeof(mdxaHeader_t));
mdxaSkel_t *skel = (mdxaSkel_t *)((byte *)header + sizeof(mdxaHeader_t) + offsets->offsets[index]);
mdxaBone_t tempMatrix;
Multiply_3x4Matrix(&tempMatrix,&mSmoothBones[index].boneMatrix, &skel->BasePoseMat);
float maxl;
maxl=VectorLength(&skel->BasePoseMat.matrix[0][0]);
VectorNormalize(&tempMatrix.matrix[0][0]);
VectorNormalize(&tempMatrix.matrix[1][0]);
VectorNormalize(&tempMatrix.matrix[2][0]);
VectorScale(&tempMatrix.matrix[0][0],maxl,&tempMatrix.matrix[0][0]);
VectorScale(&tempMatrix.matrix[1][0],maxl,&tempMatrix.matrix[1][0]);
VectorScale(&tempMatrix.matrix[2][0],maxl,&tempMatrix.matrix[2][0]);
Multiply_3x4Matrix(&mSmoothBones[index].boneMatrix,&tempMatrix,&skel->BasePoseMatInv);
mSmoothBones[index].touch=mCurrentTouch;
#ifdef _DEBUG
for ( int i = 0; i < 3; i++ )
{
for ( int j = 0; j < 4; j++ )
{
assert( !Q_isnan(mSmoothBones[index].boneMatrix.matrix[i][j]));
}
}
#endif// _DEBUG
}
//rww - RAGDOLL_END
public:
int frameSize;
const mdxaHeader_t *header;
const model_t *mod;
// these are split for better cpu cache behavior
std::vector mBones;
std::vector mFinalBones;
std::vector mSmoothBones; // for render smoothing
//vector mSkels;
boneInfo_v *rootBoneList;
mdxaBone_t rootMatrix;
int incomingTime;
int mCurrentTouch;
//rww - RAGDOLL_BEGIN
int mCurrentTouchRender;
int mLastTouch;
int mLastLastTouch;
//rww - RAGDOLL_END
// for render smoothing
bool mSmoothingActive;
bool mUnsquash;
float mSmoothFactor;
CBoneCache(const model_t *amod,const mdxaHeader_t *aheader) :
header(aheader),
mod(amod)
{
assert(amod);
assert(aheader);
mSmoothingActive=false;
mUnsquash=false;
mSmoothFactor=0.0f;
int numBones=header->numBones;
mBones.resize(numBones);
mFinalBones.resize(numBones);
mSmoothBones.resize(numBones);
// mSkels.resize(numBones);
//rww - removed mSkels
mdxaSkelOffsets_t *offsets;
mdxaSkel_t *skel;
offsets = (mdxaSkelOffsets_t *)((byte *)header + sizeof(mdxaHeader_t));
int i;
for (i=0;ioffsets[i]);
//mSkels[i]=skel;
//ditto
mFinalBones[i].parent=skel->parent;
}
mCurrentTouch=3;
//rww - RAGDOLL_BEGIN
mLastTouch=2;
mLastLastTouch=1;
//rww - RAGDOLL_END
}
SBoneCalc &Root()
{
assert(mBones.size());
return mBones[0];
}
const mdxaBone_t &EvalUnsmooth(int index)
{
EvalLow(index);
if (mSmoothingActive&&mSmoothBones[index].touch)
{
return mSmoothBones[index].boneMatrix;
}
return mFinalBones[index].boneMatrix;
}
const mdxaBone_t &Eval(int index)
{
/*
bool wasEval=EvalLow(index);
if (mSmoothingActive)
{
if (mSmoothBones[index].touch!=incomingTime||wasEval)
{
float dif=float(incomingTime)-float(mSmoothBones[index].touch);
if (mSmoothBones[index].touch&&dif<300.0f)
{
if (dif<16.0f) // 60 fps
{
dif=16.0f;
}
if (dif>100.0f) // 10 fps
{
dif=100.0f;
}
float f=1.0f-pow(1.0f-mSmoothFactor,16.0f/dif);
int i;
float *oldM=&mSmoothBones[index].boneMatrix.matrix[0][0];
float *newM=&mFinalBones[index].boneMatrix.matrix[0][0];
for (i=0;i<12;i++,oldM++,newM++)
{
*oldM=f*(*oldM-*newM)+*newM;
}
if (mUnsquash)
{
mdxaBone_t tempMatrix;
Multiply_3x4Matrix(&tempMatrix,&mSmoothBones[index].boneMatrix, &mSkels[index]->BasePoseMat);
float maxl;
maxl=VectorLength(&mSkels[index]->BasePoseMat.matrix[0][0]);
VectorNormalize(&tempMatrix.matrix[0][0]);
VectorNormalize(&tempMatrix.matrix[1][0]);
VectorNormalize(&tempMatrix.matrix[2][0]);
VectorScale(&tempMatrix.matrix[0][0],maxl,&tempMatrix.matrix[0][0]);
VectorScale(&tempMatrix.matrix[1][0],maxl,&tempMatrix.matrix[1][0]);
VectorScale(&tempMatrix.matrix[2][0],maxl,&tempMatrix.matrix[2][0]);
Multiply_3x4Matrix(&mSmoothBones[index].boneMatrix,&tempMatrix,&mSkels[index]->BasePoseMatInv);
}
}
else
{
memcpy(&mSmoothBones[index].boneMatrix,&mFinalBones[index].boneMatrix,sizeof(mdxaBone_t));
}
mSmoothBones[index].touch=incomingTime;
}
return mSmoothBones[index].boneMatrix;
}
return mFinalBones[index].boneMatrix;
*/
//Hey, this is what sof2 does. Let's try it out.
assert(index>=0&&index<(int)mBones.size());
if (mFinalBones[index].touch!=mCurrentTouch)
{
EvalLow(index);
}
return mFinalBones[index].boneMatrix;
}
//rww - RAGDOLL_BEGIN
const inline mdxaBone_t &EvalRender(int index)
{
assert(index>=0&&index<(int)mBones.size());
if (mFinalBones[index].touch!=mCurrentTouch)
{
mFinalBones[index].touchRender=mCurrentTouchRender;
EvalLow(index);
}
if (mSmoothingActive)
{
if (mSmoothBones[index].touch!=mCurrentTouch)
{
SmoothLow(index);
}
return mSmoothBones[index].boneMatrix;
}
return mFinalBones[index].boneMatrix;
}
//rww - RAGDOLL_END
//rww - RAGDOLL_BEGIN
bool WasRendered(int index)
{
assert(index>=0&&index<(int)mBones.size());
return mFinalBones[index].touchRender==mCurrentTouchRender;
}
int GetParent(int index)
{
if (index==0)
{
return -1;
}
assert(index>=0&&index<(int)mBones.size());
return mFinalBones[index].parent;
}
//rww - RAGDOLL_END
};
void RemoveBoneCache(CBoneCache *boneCache)
{
#ifdef _FULL_G2_LEAK_CHECKING
g_Ghoul2Allocations -= sizeof(*boneCache);
#endif
delete boneCache;
}
#ifdef _G2_LISTEN_SERVER_OPT
void CopyBoneCache(CBoneCache *to, CBoneCache *from)
{
memcpy(to, from, sizeof(CBoneCache));
}
#endif
const mdxaBone_t &EvalBoneCache(int index,CBoneCache *boneCache)
{
assert(boneCache);
return boneCache->Eval(index);
}
//rww - RAGDOLL_BEGIN
const mdxaHeader_t *G2_GetModA(CGhoul2Info &ghoul2)
{
if (!ghoul2.mBoneCache)
{
return 0;
}
CBoneCache &boneCache=*ghoul2.mBoneCache;
return boneCache.header;
}
int G2_GetBoneDependents(CGhoul2Info &ghoul2,int boneNum,int *tempDependents,int maxDep)
{
// fixme, these should be precomputed
if (!ghoul2.mBoneCache||!maxDep)
{
return 0;
}
CBoneCache &boneCache=*ghoul2.mBoneCache;
mdxaSkel_t *skel;
mdxaSkelOffsets_t *offsets;
offsets = (mdxaSkelOffsets_t *)((byte *)boneCache.header + sizeof(mdxaHeader_t));
skel = (mdxaSkel_t *)((byte *)boneCache.header + sizeof(mdxaHeader_t) + offsets->offsets[boneNum]);
int i;
int ret=0;
for (i=0;inumChildren;i++)
{
if (!maxDep)
{
return i; // number added
}
*tempDependents=skel->children[i];
assert(*tempDependents>0&&*tempDependentsnumBones);
maxDep--;
tempDependents++;
ret++;
}
for (i=0;inumChildren;i++)
{
int num=G2_GetBoneDependents(ghoul2,skel->children[i],tempDependents,maxDep);
tempDependents+=num;
ret+=num;
maxDep-=num;
assert(maxDep>=0);
if (!maxDep)
{
break;
}
}
return ret;
}
bool G2_WasBoneRendered(CGhoul2Info &ghoul2,int boneNum)
{
if (!ghoul2.mBoneCache)
{
return false;
}
CBoneCache &boneCache=*ghoul2.mBoneCache;
return boneCache.WasRendered(boneNum);
}
void G2_GetBoneBasepose(CGhoul2Info &ghoul2,int boneNum,mdxaBone_t *&retBasepose,mdxaBone_t *&retBaseposeInv)
{
if (!ghoul2.mBoneCache)
{
// yikes
retBasepose=const_cast(&identityMatrix);
retBaseposeInv=const_cast(&identityMatrix);
return;
}
assert(ghoul2.mBoneCache);
CBoneCache &boneCache=*ghoul2.mBoneCache;
assert(boneCache.mod);
assert(boneNum>=0&&boneNumnumBones);
mdxaSkel_t *skel;
mdxaSkelOffsets_t *offsets;
offsets = (mdxaSkelOffsets_t *)((byte *)boneCache.header + sizeof(mdxaHeader_t));
skel = (mdxaSkel_t *)((byte *)boneCache.header + sizeof(mdxaHeader_t) + offsets->offsets[boneNum]);
retBasepose=&skel->BasePoseMat;
retBaseposeInv=&skel->BasePoseMatInv;
}
char *G2_GetBoneNameFromSkel(CGhoul2Info &ghoul2, int boneNum)
{
if (!ghoul2.mBoneCache)
{
return NULL;
}
CBoneCache &boneCache=*ghoul2.mBoneCache;
assert(boneCache.mod);
assert(boneNum>=0&&boneNumnumBones);
mdxaSkel_t *skel;
mdxaSkelOffsets_t *offsets;
offsets = (mdxaSkelOffsets_t *)((byte *)boneCache.header + sizeof(mdxaHeader_t));
skel = (mdxaSkel_t *)((byte *)boneCache.header + sizeof(mdxaHeader_t) + offsets->offsets[boneNum]);
return skel->name;
}
void G2_RagGetBoneBasePoseMatrixLow(CGhoul2Info &ghoul2, int boneNum, mdxaBone_t &boneMatrix, mdxaBone_t &retMatrix, vec3_t scale)
{
assert(ghoul2.mBoneCache);
CBoneCache &boneCache=*ghoul2.mBoneCache;
assert(boneCache.mod);
assert(boneNum>=0&&boneNumnumBones);
mdxaSkel_t *skel;
mdxaSkelOffsets_t *offsets;
offsets = (mdxaSkelOffsets_t *)((byte *)boneCache.header + sizeof(mdxaHeader_t));
skel = (mdxaSkel_t *)((byte *)boneCache.header + sizeof(mdxaHeader_t) + offsets->offsets[boneNum]);
Multiply_3x4Matrix(&retMatrix, &boneMatrix, &skel->BasePoseMat);
if (scale[0])
{
retMatrix.matrix[0][3] *= scale[0];
}
if (scale[1])
{
retMatrix.matrix[1][3] *= scale[1];
}
if (scale[2])
{
retMatrix.matrix[2][3] *= scale[2];
}
VectorNormalize((float*)&retMatrix.matrix[0]);
VectorNormalize((float*)&retMatrix.matrix[1]);
VectorNormalize((float*)&retMatrix.matrix[2]);
}
void G2_GetBoneMatrixLow(CGhoul2Info &ghoul2,int boneNum,const vec3_t scale,mdxaBone_t &retMatrix,mdxaBone_t *&retBasepose,mdxaBone_t *&retBaseposeInv)
{
if (!ghoul2.mBoneCache)
{
retMatrix=identityMatrix;
// yikes
retBasepose=const_cast(&identityMatrix);
retBaseposeInv=const_cast(&identityMatrix);
return;
}
mdxaBone_t bolt;
assert(ghoul2.mBoneCache);
CBoneCache &boneCache=*ghoul2.mBoneCache;
assert(boneCache.mod);
assert(boneNum>=0&&boneNumnumBones);
mdxaSkel_t *skel;
mdxaSkelOffsets_t *offsets;
offsets = (mdxaSkelOffsets_t *)((byte *)boneCache.header + sizeof(mdxaHeader_t));
skel = (mdxaSkel_t *)((byte *)boneCache.header + sizeof(mdxaHeader_t) + offsets->offsets[boneNum]);
Multiply_3x4Matrix(&bolt, (mdxaBone_t *)&boneCache.Eval(boneNum), &skel->BasePoseMat); // DEST FIRST ARG
retBasepose=&skel->BasePoseMat;
retBaseposeInv=&skel->BasePoseMatInv;
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(&retMatrix,&worldMatrix, &bolt);
#ifdef _DEBUG
for ( int i = 0; i < 3; i++ )
{
for ( int j = 0; j < 4; j++ )
{
assert( !Q_isnan(retMatrix.matrix[i][j]));
}
}
#endif// _DEBUG
}
int G2_GetParentBoneMatrixLow(CGhoul2Info &ghoul2,int boneNum,const vec3_t scale,mdxaBone_t &retMatrix,mdxaBone_t *&retBasepose,mdxaBone_t *&retBaseposeInv)
{
int parent=-1;
if (ghoul2.mBoneCache)
{
CBoneCache &boneCache=*ghoul2.mBoneCache;
assert(boneCache.mod);
assert(boneNum>=0&&boneNumnumBones);
parent=boneCache.GetParent(boneNum);
if (parent<0||parent>=boneCache.header->numBones)
{
parent=-1;
retMatrix=identityMatrix;
// yikes
retBasepose=const_cast(&identityMatrix);
retBaseposeInv=const_cast(&identityMatrix);
}
else
{
G2_GetBoneMatrixLow(ghoul2,parent,scale,retMatrix,retBasepose,retBaseposeInv);
}
}
return parent;
}
//rww - RAGDOLL_END
class CRenderSurface
{
public:
int surfaceNum;
surfaceInfo_v &rootSList;
shader_t *cust_shader;
int fogNum;
qboolean personalModel;
CBoneCache *boneCache;
int renderfx;
skin_t *skin;
model_t *currentModel;
int lod;
boltInfo_v &boltList;
#ifdef _G2_GORE
shader_t *gore_shader;
CGoreSet *gore_set;
#endif
CRenderSurface(
int initsurfaceNum,
surfaceInfo_v &initrootSList,
shader_t *initcust_shader,
int initfogNum,
qboolean initpersonalModel,
CBoneCache *initboneCache,
int initrenderfx,
skin_t *initskin,
model_t *initcurrentModel,
int initlod,
#ifdef _G2_GORE
boltInfo_v &initboltList,
shader_t *initgore_shader,
CGoreSet *initgore_set):
#else
boltInfo_v &initboltList):
#endif
surfaceNum(initsurfaceNum),
rootSList(initrootSList),
cust_shader(initcust_shader),
fogNum(initfogNum),
personalModel(initpersonalModel),
boneCache(initboneCache),
renderfx(initrenderfx),
skin(initskin),
currentModel(initcurrentModel),
lod(initlod),
#ifdef _G2_GORE
boltList(initboltList),
gore_shader(initgore_shader),
gore_set(initgore_set)
#else
boltList(initboltList)
#endif
{}
};
#ifdef _G2_GORE
#define MAX_RENDER_SURFACES (2048)
static CRenderableSurface RSStorage[MAX_RENDER_SURFACES];
static unsigned int NextRS=0;
CRenderableSurface *AllocRS()
{
CRenderableSurface *ret=&RSStorage[NextRS];
ret->Init();
NextRS++;
NextRS%=MAX_RENDER_SURFACES;
return ret;
}
#endif
/*
All bones should be an identity orientation to display the mesh exactly
as it is specified.
For all other frames, the bones represent the transformation from the
orientation of the bone in the base frame to the orientation in this
frame.
*/
/*
=============
R_ACullModel
=============
*/
static int R_GCullModel( trRefEntity_t *ent ) {
// scale the radius if need be
float largestScale = ent->e.modelScale[0];
if (ent->e.modelScale[1] > largestScale)
{
largestScale = ent->e.modelScale[1];
}
if (ent->e.modelScale[2] > largestScale)
{
largestScale = ent->e.modelScale[2];
}
if (!largestScale)
{
largestScale = 1;
}
// cull bounding sphere
switch ( R_CullLocalPointAndRadius( vec3_origin, ent->e.radius * largestScale) )
{
case CULL_OUT:
tr.pc.c_sphere_cull_md3_out++;
return CULL_OUT;
case CULL_IN:
tr.pc.c_sphere_cull_md3_in++;
return CULL_IN;
case CULL_CLIP:
tr.pc.c_sphere_cull_md3_clip++;
return CULL_IN;
}
return CULL_IN;
}
/*
=================
R_AComputeFogNum
=================
*/
static int R_GComputeFogNum( trRefEntity_t *ent ) {
int i, j;
fog_t *fog;
if ( tr.refdef.rdflags & RDF_NOWORLDMODEL ) {
return 0;
}
for ( i = 1 ; i < tr.world->numfogs ; i++ ) {
fog = &tr.world->fogs[i];
for ( j = 0 ; j < 3 ; j++ ) {
if ( ent->e.origin[j] - ent->e.radius >= fog->bounds[1][j] ) {
break;
}
if ( ent->e.origin[j] + ent->e.radius <= fog->bounds[0][j] ) {
break;
}
}
if ( j == 3 ) {
return i;
}
}
return 0;
}
// work out lod for this entity.
static int G2_ComputeLOD( trRefEntity_t *ent, const model_t *currentModel, int lodBias )
{
float flod, lodscale;
float projectedRadius;
int lod;
if ( currentModel->numLods < 2 )
{ // model has only 1 LOD level, skip computations and bias
return(0);
}
if ( r_lodbias->integer > lodBias )
{
lodBias = r_lodbias->integer;
}
// scale the radius if need be
float largestScale = ent->e.modelScale[0];
if (ent->e.modelScale[1] > largestScale)
{
largestScale = ent->e.modelScale[1];
}
if (ent->e.modelScale[2] > largestScale)
{
largestScale = ent->e.modelScale[2];
}
if (!largestScale)
{
largestScale = 1;
}
if ( ( projectedRadius = ProjectRadius( 0.75*largestScale*ent->e.radius, ent->e.origin ) ) != 0 ) //we reduce the radius to make the LOD match other model types which use the actual bound box size
{
lodscale = (r_lodscale->value+r_autolodscalevalue->value);
if ( lodscale > 20 )
{
lodscale = 20;
}
else if ( lodscale < 0 )
{
lodscale = 0;
}
flod = 1.0f - projectedRadius * lodscale;
}
else
{
// object intersects near view plane, e.g. view weapon
flod = 0;
}
flod *= currentModel->numLods;
lod = Q_ftol( flod );
if ( lod < 0 )
{
lod = 0;
}
else if ( lod >= currentModel->numLods )
{
lod = currentModel->numLods - 1;
}
lod += lodBias;
if ( lod >= currentModel->numLods )
lod = currentModel->numLods - 1;
if ( lod < 0 )
lod = 0;
return lod;
}
//======================================================================
//
// Bone Manipulation code
void G2_CreateQuaterion(mdxaBone_t *mat, vec4_t quat)
{
// this is revised for the 3x4 matrix we use in G2.
float t = 1 + mat->matrix[0][0] + mat->matrix[1][1] + mat->matrix[2][2];
float s;
//If the trace of the matrix is greater than zero, then
//perform an "instant" calculation.
//Important note wrt. rouning errors:
//Test if ( T > 0.00000001 ) to avoid large distortions!
if (t > 0.00000001)
{
s = sqrt(t) * 2;
quat[0] = ( mat->matrix[1][2] - mat->matrix[2][1] ) / s;
quat[1] = ( mat->matrix[2][0] - mat->matrix[0][2] ) / s;
quat[2] = ( mat->matrix[0][1] - mat->matrix[1][0] ) / s;
quat[3] = 0.25 * s;
}
else
{
//If the trace of the matrix is equal to zero then identify
//which major diagonal element has the greatest value.
//Depending on this, calculate the following:
if ( mat->matrix[0][0] > mat->matrix[1][1] && mat->matrix[0][0] > mat->matrix[2][2] ) { // Column 0:
s = sqrt( 1.0 + mat->matrix[0][0] - mat->matrix[1][1] - mat->matrix[2][2])* 2;
quat[0] = 0.25 * s;
quat[1] = (mat->matrix[0][1] + mat->matrix[1][0] ) / s;
quat[2] = (mat->matrix[2][0] + mat->matrix[0][2] ) / s;
quat[3] = (mat->matrix[1][2] - mat->matrix[2][1] ) / s;
} else if ( mat->matrix[1][1] > mat->matrix[2][2] ) { // Column 1:
s = sqrt( 1.0 + mat->matrix[1][1] - mat->matrix[0][0] - mat->matrix[2][2] ) * 2;
quat[0] = (mat->matrix[0][1] + mat->matrix[1][0] ) / s;
quat[1] = 0.25 * s;
quat[2] = (mat->matrix[1][2] + mat->matrix[2][1] ) / s;
quat[3] = (mat->matrix[2][0] - mat->matrix[0][2] ) / s;
} else { // Column 2:
s = sqrt( 1.0 + mat->matrix[2][2] - mat->matrix[0][0] - mat->matrix[1][1] ) * 2;
quat[0] = (mat->matrix[2][0]+ mat->matrix[0][2] ) / s;
quat[1] = (mat->matrix[1][2] + mat->matrix[2][1] ) / s;
quat[2] = 0.25 * s;
quat[3] = (mat->matrix[0][1] - mat->matrix[1][0] ) / s;
}
}
}
void G2_CreateMatrixFromQuaterion(mdxaBone_t *mat, vec4_t quat)
{
float xx = quat[0] * quat[0];
float xy = quat[0] * quat[1];
float xz = quat[0] * quat[2];
float xw = quat[0] * quat[3];
float yy = quat[1] * quat[1];
float yz = quat[1] * quat[2];
float yw = quat[1] * quat[3];
float zz = quat[2] * quat[2];
float zw = quat[2] * quat[3];
mat->matrix[0][0] = 1 - 2 * ( yy + zz );
mat->matrix[1][0] = 2 * ( xy - zw );
mat->matrix[2][0] = 2 * ( xz + yw );
mat->matrix[0][1] = 2 * ( xy + zw );
mat->matrix[1][1] = 1 - 2 * ( xx + zz );
mat->matrix[2][1] = 2 * ( yz - xw );
mat->matrix[0][2] = 2 * ( xz - yw );
mat->matrix[1][2] = 2 * ( yz + xw );
mat->matrix[2][2] = 1 - 2 * ( xx + yy );
mat->matrix[0][3] = mat->matrix[1][3] = mat->matrix[2][3] = 0;
}
// nasty little matrix multiply going on here..
void Multiply_3x4Matrix(mdxaBone_t *out, mdxaBone_t *in2, mdxaBone_t *in)
{
// first row of out
out->matrix[0][0] = (in2->matrix[0][0] * in->matrix[0][0]) + (in2->matrix[0][1] * in->matrix[1][0]) + (in2->matrix[0][2] * in->matrix[2][0]);
out->matrix[0][1] = (in2->matrix[0][0] * in->matrix[0][1]) + (in2->matrix[0][1] * in->matrix[1][1]) + (in2->matrix[0][2] * in->matrix[2][1]);
out->matrix[0][2] = (in2->matrix[0][0] * in->matrix[0][2]) + (in2->matrix[0][1] * in->matrix[1][2]) + (in2->matrix[0][2] * in->matrix[2][2]);
out->matrix[0][3] = (in2->matrix[0][0] * in->matrix[0][3]) + (in2->matrix[0][1] * in->matrix[1][3]) + (in2->matrix[0][2] * in->matrix[2][3]) + in2->matrix[0][3];
// second row of outf out
out->matrix[1][0] = (in2->matrix[1][0] * in->matrix[0][0]) + (in2->matrix[1][1] * in->matrix[1][0]) + (in2->matrix[1][2] * in->matrix[2][0]);
out->matrix[1][1] = (in2->matrix[1][0] * in->matrix[0][1]) + (in2->matrix[1][1] * in->matrix[1][1]) + (in2->matrix[1][2] * in->matrix[2][1]);
out->matrix[1][2] = (in2->matrix[1][0] * in->matrix[0][2]) + (in2->matrix[1][1] * in->matrix[1][2]) + (in2->matrix[1][2] * in->matrix[2][2]);
out->matrix[1][3] = (in2->matrix[1][0] * in->matrix[0][3]) + (in2->matrix[1][1] * in->matrix[1][3]) + (in2->matrix[1][2] * in->matrix[2][3]) + in2->matrix[1][3];
// third row of out out
out->matrix[2][0] = (in2->matrix[2][0] * in->matrix[0][0]) + (in2->matrix[2][1] * in->matrix[1][0]) + (in2->matrix[2][2] * in->matrix[2][0]);
out->matrix[2][1] = (in2->matrix[2][0] * in->matrix[0][1]) + (in2->matrix[2][1] * in->matrix[1][1]) + (in2->matrix[2][2] * in->matrix[2][1]);
out->matrix[2][2] = (in2->matrix[2][0] * in->matrix[0][2]) + (in2->matrix[2][1] * in->matrix[1][2]) + (in2->matrix[2][2] * in->matrix[2][2]);
out->matrix[2][3] = (in2->matrix[2][0] * in->matrix[0][3]) + (in2->matrix[2][1] * in->matrix[1][3]) + (in2->matrix[2][2] * in->matrix[2][3]) + in2->matrix[2][3];
}
static int G2_GetBonePoolIndex(const mdxaHeader_t *pMDXAHeader, int iFrame, int iBone)
{
const int iOffsetToIndex = (iFrame * pMDXAHeader->numBones * 3) + (iBone * 3);
mdxaIndex_t *pIndex = (mdxaIndex_t *)((byte*)pMDXAHeader + pMDXAHeader->ofsFrames + iOffsetToIndex);
return (pIndex->iIndex[2] << 16) + (pIndex->iIndex[1] << 8) + (pIndex->iIndex[0]);
}
/*static inline*/ void UnCompressBone(float mat[3][4], int iBoneIndex, const mdxaHeader_t *pMDXAHeader, int iFrame)
{
mdxaCompQuatBone_t *pCompBonePool = (mdxaCompQuatBone_t *) ((byte *)pMDXAHeader + pMDXAHeader->ofsCompBonePool);
MC_UnCompressQuat(mat, pCompBonePool[ G2_GetBonePoolIndex( pMDXAHeader, iFrame, iBoneIndex ) ].Comp);
}
#define DEBUG_G2_TIMING (0)
#define DEBUG_G2_TIMING_RENDER_ONLY (1)
void G2_TimingModel(boneInfo_t &bone,int currentTime,int numFramesInFile,int ¤tFrame,int &newFrame,float &lerp)
{
assert(bone.startFrame>=0);
assert(bone.startFrame<=numFramesInFile);
assert(bone.endFrame>=0);
assert(bone.endFrame<=numFramesInFile);
// yes - add in animation speed to current frame
float animSpeed = bone.animSpeed;
float time;
if (bone.pauseTime)
{
time = (bone.pauseTime - bone.startTime) / 50.0f;
}
else
{
time = (currentTime - bone.startTime) / 50.0f;
}
if (time<0.0f)
{
time=0.0f;
}
float newFrame_g = bone.startFrame + (time * animSpeed);
int animSize = bone.endFrame - bone.startFrame;
float endFrame = (float)bone.endFrame;
// we are supposed to be animating right?
if (animSize)
{
// did we run off the end?
if (((animSpeed > 0.0f) && (newFrame_g > endFrame - 1)) ||
((animSpeed < 0.0f) && (newFrame_g < endFrame+1)))
{
// yep - decide what to do
if (bone.flags & BONE_ANIM_OVERRIDE_LOOP)
{
// get our new animation frame back within the bounds of the animation set
if (animSpeed < 0.0f)
{
// we don't use this case, or so I am told
// if we do, let me know, I need to insure the mod works
// should we be creating a virtual frame?
if ((newFrame_g < endFrame+1) && (newFrame_g >= endFrame))
{
// now figure out what we are lerping between
// delta is the fraction between this frame and the next, since the new anim is always at a .0f;
lerp = float(endFrame+1)-newFrame_g;
// frames are easy to calculate
currentFrame = endFrame;
assert(currentFrame>=0&¤tFrame=0&&newFrame=0&¤tFrame=0&&newFrame=0&&newFrame endFrame - 1) && (newFrame_g < endFrame))
{
// now figure out what we are lerping between
// delta is the fraction between this frame and the next, since the new anim is always at a .0f;
lerp = (newFrame_g - (int)newFrame_g);
// frames are easy to calculate
currentFrame = (int)newFrame_g;
assert(currentFrame>=0&¤tFrame=0&&newFrame= endFrame)
{
newFrame_g=endFrame+fmod(newFrame_g-endFrame,animSize)-animSize;
}
// now figure out what we are lerping between
// delta is the fraction between this frame and the next, since the new anim is always at a .0f;
lerp = (newFrame_g - (int)newFrame_g);
// frames are easy to calculate
currentFrame = (int)newFrame_g;
assert(currentFrame>=0&¤tFrame= endFrame - 1)
{
newFrame = bone.startFrame;
assert(newFrame>=0&&newFrame=0&&newFrame= bone.startFrame)) || (animSize < 10));
}
else
{
if (((bone.flags & (BONE_ANIM_OVERRIDE_FREEZE)) == (BONE_ANIM_OVERRIDE_FREEZE)))
{
// if we are supposed to reset the default anim, then do so
if (animSpeed > 0.0f)
{
currentFrame = bone.endFrame - 1;
assert(currentFrame>=0&¤tFrame=0&¤tFrame=0&&newFrame 0.0)
{
// frames are easy to calculate
currentFrame = (int)newFrame_g;
// figure out the difference between the two frames - we have to decide what frame and what percentage of that
// frame we want to display
lerp = (newFrame_g - currentFrame);
assert(currentFrame>=0&¤tFrame= (int)endFrame)
{
// we only want to lerp with the first frame of the anim if we are looping
if (bone.flags & BONE_ANIM_OVERRIDE_LOOP)
{
newFrame = bone.startFrame;
assert(newFrame>=0&&newFrame=0&&newFrame=0&&newFramebone.startFrame)
{
currentFrame=bone.startFrame;
newFrame = currentFrame;
lerp=0.0f;
}
else
{
newFrame=currentFrame-1;
// are we now on the end frame?
if (newFrame < endFrame+1)
{
// we only want to lerp with the first frame of the anim if we are looping
if (bone.flags & BONE_ANIM_OVERRIDE_LOOP)
{
newFrame = bone.startFrame;
assert(newFrame>=0&&newFrame=0&&newFrame=0&¤tFrame=0&&newFrame=0&¤tFrame=0&&newFrame=0&¤tFrame=0&&newFrame=0.0f&&lerp<=1.0f);
}
#ifdef _RAG_PRINT_TEST
void G2_RagPrintMatrix(mdxaBone_t *mat);
#endif
//basically construct a seperate skeleton with full hierarchy to store a matrix
//off which will give us the desired settling position given the frame in the skeleton
//that should be used -rww
int G2_Add_Bone (const model_t *mod, boneInfo_v &blist, const char *boneName);
int G2_Find_Bone(const model_t *mod, boneInfo_v &blist, const char *boneName);
void G2_RagGetAnimMatrix(CGhoul2Info &ghoul2, const int boneNum, mdxaBone_t &matrix, const int frame)
{
mdxaBone_t animMatrix;
mdxaSkel_t *skel;
mdxaSkel_t *pskel;
mdxaSkelOffsets_t *offsets;
int parent;
int bListIndex;
int parentBlistIndex;
#ifdef _RAG_PRINT_TEST
bool actuallySet = false;
#endif
assert(ghoul2.mBoneCache);
assert(ghoul2.animModel);
offsets = (mdxaSkelOffsets_t *)((byte *)ghoul2.mBoneCache->header + sizeof(mdxaHeader_t));
skel = (mdxaSkel_t *)((byte *)ghoul2.mBoneCache->header + sizeof(mdxaHeader_t) + offsets->offsets[boneNum]);
//find/add the bone in the list
if (!skel->name[0])
{
bListIndex = -1;
}
else
{
bListIndex = G2_Find_Bone(ghoul2.animModel, ghoul2.mBlist, skel->name);
if (bListIndex == -1)
{
#ifdef _RAG_PRINT_TEST
ri.Printf( PRINT_ALL, "Attempting to add %s\n", skel->name);
#endif
bListIndex = G2_Add_Bone(ghoul2.animModel, ghoul2.mBlist, skel->name);
}
}
assert(bListIndex != -1);
boneInfo_t &bone = ghoul2.mBlist[bListIndex];
if (bone.hasAnimFrameMatrix == frame)
{ //already calculated so just grab it
matrix = bone.animFrameMatrix;
return;
}
//get the base matrix for the specified frame
UnCompressBone(animMatrix.matrix, boneNum, ghoul2.mBoneCache->header, frame);
parent = skel->parent;
if (boneNum > 0 && parent > -1)
{
//recursively call to assure all parent matrices are set up
G2_RagGetAnimMatrix(ghoul2, parent, matrix, frame);
//assign the new skel ptr for our parent
pskel = (mdxaSkel_t *)((byte *)ghoul2.mBoneCache->header + sizeof(mdxaHeader_t) + offsets->offsets[parent]);
//taking bone matrix for the skeleton frame and parent's animFrameMatrix into account, determine our final animFrameMatrix
if (!pskel->name[0])
{
parentBlistIndex = -1;
}
else
{
parentBlistIndex = G2_Find_Bone(ghoul2.animModel, ghoul2.mBlist, pskel->name);
if (parentBlistIndex == -1)
{
parentBlistIndex = G2_Add_Bone(ghoul2.animModel, ghoul2.mBlist, pskel->name);
}
}
assert(parentBlistIndex != -1);
boneInfo_t &pbone = ghoul2.mBlist[parentBlistIndex];
assert(pbone.hasAnimFrameMatrix == frame); //this should have been calc'd in the recursive call
Multiply_3x4Matrix(&bone.animFrameMatrix, &pbone.animFrameMatrix, &animMatrix);
#ifdef _RAG_PRINT_TEST
if (parentBlistIndex != -1 && bListIndex != -1)
{
actuallySet = true;
}
else
{
ri.Printf( PRINT_ALL, "BAD LIST INDEX: %s, %s [%i]\n", skel->name, pskel->name, parent);
}
#endif
}
else
{ //root
Multiply_3x4Matrix(&bone.animFrameMatrix, &ghoul2.mBoneCache->rootMatrix, &animMatrix);
#ifdef _RAG_PRINT_TEST
if (bListIndex != -1)
{
actuallySet = true;
}
else
{
ri.Printf( PRINT_ALL, "BAD LIST INDEX: %s\n", skel->name);
}
#endif
//bone.animFrameMatrix = ghoul2.mBoneCache->mFinalBones[boneNum].boneMatrix;
//Maybe use this for the root, so that the orientation is in sync with the current
//root matrix? However this would require constant recalculation of this base
//skeleton which I currently do not want.
}
//never need to figure it out again
bone.hasAnimFrameMatrix = frame;
#ifdef _RAG_PRINT_TEST
if (!actuallySet)
{
ri.Printf( PRINT_ALL, "SET FAILURE\n");
G2_RagPrintMatrix(&bone.animFrameMatrix);
}
#endif
matrix = bone.animFrameMatrix;
}
void G2_TransformBone (int child,CBoneCache &BC)
{
SBoneCalc &TB=BC.mBones[child];
static mdxaBone_t tbone[6];
// mdxaFrame_t *aFrame=0;
// mdxaFrame_t *bFrame=0;
// mdxaFrame_t *aoldFrame=0;
// mdxaFrame_t *boldFrame=0;
static mdxaSkel_t *skel;
static mdxaSkelOffsets_t *offsets;
boneInfo_v &boneList = *BC.rootBoneList;
static int j, boneListIndex;
int angleOverride = 0;
#if DEBUG_G2_TIMING
bool printTiming=false;
#endif
// should this bone be overridden by a bone in the bone list?
boneListIndex = G2_Find_Bone_In_List(boneList, child);
if (boneListIndex != -1)
{
// we found a bone in the list - we need to override something here.
// do we override the rotational angles?
if ((boneList[boneListIndex].flags) & (BONE_ANGLES_TOTAL))
{
angleOverride = (boneList[boneListIndex].flags) & (BONE_ANGLES_TOTAL);
}
// set blending stuff if we need to
if (boneList[boneListIndex].flags & BONE_ANIM_BLEND)
{
float blendTime = BC.incomingTime - boneList[boneListIndex].blendStart;
// only set up the blend anim if we actually have some blend time left on this bone anim - otherwise we might corrupt some blend higher up the hiearchy
if (blendTime>=0.0f&&blendTime < boneList[boneListIndex].blendTime)
{
TB.blendFrame = boneList[boneListIndex].blendFrame;
TB.blendOldFrame = boneList[boneListIndex].blendLerpFrame;
TB.blendLerp = (blendTime / boneList[boneListIndex].blendTime);
TB.blendMode = true;
}
else
{
TB.blendMode = false;
}
}
else if (/*r_Ghoul2NoBlend->integer||*/((boneList[boneListIndex].flags) & (BONE_ANIM_OVERRIDE_LOOP | BONE_ANIM_OVERRIDE)))
// turn off blending if we are just doing a straing animation override
{
TB.blendMode = false;
}
// should this animation be overridden by an animation in the bone list?
if ((boneList[boneListIndex].flags) & (BONE_ANIM_OVERRIDE_LOOP | BONE_ANIM_OVERRIDE))
{
G2_TimingModel(boneList[boneListIndex],BC.incomingTime,BC.header->numFrames,TB.currentFrame,TB.newFrame,TB.backlerp);
}
#if DEBUG_G2_TIMING
printTiming=true;
#endif
/*
if ((r_Ghoul2NoLerp->integer)||((boneList[boneListIndex].flags) & (BONE_ANIM_NO_LERP)))
{
TB.backlerp = 0.0f;
}
*/
//rwwFIXMEFIXME: Use?
}
// figure out where the location of the bone animation data is
assert(TB.newFrame>=0&&TB.newFramenumFrames);
if (!(TB.newFrame>=0&&TB.newFramenumFrames))
{
TB.newFrame=0;
}
// aFrame = (mdxaFrame_t *)((byte *)BC.header + BC.header->ofsFrames + TB.newFrame * BC.frameSize );
assert(TB.currentFrame>=0&&TB.currentFramenumFrames);
if (!(TB.currentFrame>=0&&TB.currentFramenumFrames))
{
TB.currentFrame=0;
}
// aoldFrame = (mdxaFrame_t *)((byte *)BC.header + BC.header->ofsFrames + TB.currentFrame * BC.frameSize );
// figure out where the location of the blended animation data is
assert(!(TB.blendFrame < 0.0 || TB.blendFrame >= (BC.header->numFrames+1)));
if (TB.blendFrame < 0.0 || TB.blendFrame >= (BC.header->numFrames+1) )
{
TB.blendFrame=0.0;
}
// bFrame = (mdxaFrame_t *)((byte *)BC.header + BC.header->ofsFrames + (int)TB.blendFrame * BC.frameSize );
assert(TB.blendOldFrame>=0&&TB.blendOldFramenumFrames);
if (!(TB.blendOldFrame>=0&&TB.blendOldFramenumFrames))
{
TB.blendOldFrame=0;
}
#if DEBUG_G2_TIMING
#if DEBUG_G2_TIMING_RENDER_ONLY
if (!HackadelicOnClient)
{
printTiming=false;
}
#endif
if (printTiming)
{
char mess[1000];
if (TB.blendMode)
{
sprintf(mess,"b %2d %5d %4d %4d %4d %4d %f %f\n",boneListIndex,BC.incomingTime,(int)TB.newFrame,(int)TB.currentFrame,(int)TB.blendFrame,(int)TB.blendOldFrame,TB.backlerp,TB.blendLerp);
}
else
{
sprintf(mess,"a %2d %5d %4d %4d %f\n",boneListIndex,BC.incomingTime,TB.newFrame,TB.currentFrame,TB.backlerp);
}
Com_OPrintf("%s",mess);
const boneInfo_t &bone=boneList[boneListIndex];
if (bone.flags&BONE_ANIM_BLEND)
{
sprintf(mess," bfb[%2d] %5d %5d (%5d-%5d) %4.2f %4x bt(%5d-%5d) %7.2f %5d\n",
boneListIndex,
BC.incomingTime,
bone.startTime,
bone.startFrame,
bone.endFrame,
bone.animSpeed,
bone.flags,
bone.blendStart,
bone.blendStart+bone.blendTime,
bone.blendFrame,
bone.blendLerpFrame
);
}
else
{
sprintf(mess," bfa[%2d] %5d %5d (%5d-%5d) %4.2f %4x\n",
boneListIndex,
BC.incomingTime,
bone.startTime,
bone.startFrame,
bone.endFrame,
bone.animSpeed,
bone.flags
);
}
// Com_OPrintf("%s",mess);
}
#endif
// boldFrame = (mdxaFrame_t *)((byte *)BC.header + BC.header->ofsFrames + TB.blendOldFrame * BC.frameSize );
// mdxaCompBone_t *compBonePointer = (mdxaCompBone_t *)((byte *)BC.header + BC.header->ofsCompBonePool);
assert(child>=0&&childnumBones);
// assert(bFrame->boneIndexes[child]>=0);
// assert(boldFrame->boneIndexes[child]>=0);
// assert(aFrame->boneIndexes[child]>=0);
// assert(aoldFrame->boneIndexes[child]>=0);
// decide where the transformed bone is going
// are we blending with another frame of anim?
if (TB.blendMode)
{
float backlerp = TB.blendFrame - (int)TB.blendFrame;
float frontlerp = 1.0 - backlerp;
// MC_UnCompress(tbone[3].matrix,compBonePointer[bFrame->boneIndexes[child]].Comp);
// MC_UnCompress(tbone[4].matrix,compBonePointer[boldFrame->boneIndexes[child]].Comp);
UnCompressBone(tbone[3].matrix, child, BC.header, TB.blendFrame);
UnCompressBone(tbone[4].matrix, child, BC.header, TB.blendOldFrame);
for ( j = 0 ; j < 12 ; j++ )
{
((float *)&tbone[5])[j] = (backlerp * ((float *)&tbone[3])[j])
+ (frontlerp * ((float *)&tbone[4])[j]);
}
}
//
// lerp this bone - use the temp space on the ref entity to put the bone transforms into
//
if (!TB.backlerp)
{
// MC_UnCompress(tbone[2].matrix,compBonePointer[aoldFrame->boneIndexes[child]].Comp);
UnCompressBone(tbone[2].matrix, child, BC.header, TB.currentFrame);
// blend in the other frame if we need to
if (TB.blendMode)
{
float blendFrontlerp = 1.0 - TB.blendLerp;
for ( j = 0 ; j < 12 ; j++ )
{
((float *)&tbone[2])[j] = (TB.blendLerp * ((float *)&tbone[2])[j])
+ (blendFrontlerp * ((float *)&tbone[5])[j]);
}
}
if (!child)
{
// now multiply by the root matrix, so we can offset this model should we need to
Multiply_3x4Matrix(&BC.mFinalBones[child].boneMatrix, &BC.rootMatrix, &tbone[2]);
}
}
else
{
float frontlerp = 1.0 - TB.backlerp;
// MC_UnCompress(tbone[0].matrix,compBonePointer[aFrame->boneIndexes[child]].Comp);
// MC_UnCompress(tbone[1].matrix,compBonePointer[aoldFrame->boneIndexes[child]].Comp);
UnCompressBone(tbone[0].matrix, child, BC.header, TB.newFrame);
UnCompressBone(tbone[1].matrix, child, BC.header, TB.currentFrame);
for ( j = 0 ; j < 12 ; j++ )
{
((float *)&tbone[2])[j] = (TB.backlerp * ((float *)&tbone[0])[j])
+ (frontlerp * ((float *)&tbone[1])[j]);
}
// blend in the other frame if we need to
if (TB.blendMode)
{
float blendFrontlerp = 1.0 - TB.blendLerp;
for ( j = 0 ; j < 12 ; j++ )
{
((float *)&tbone[2])[j] = (TB.blendLerp * ((float *)&tbone[2])[j])
+ (blendFrontlerp * ((float *)&tbone[5])[j]);
}
}
if (!child)
{
// now multiply by the root matrix, so we can offset this model should we need to
Multiply_3x4Matrix(&BC.mFinalBones[child].boneMatrix, &BC.rootMatrix, &tbone[2]);
}
}
// figure out where the bone hirearchy info is
offsets = (mdxaSkelOffsets_t *)((byte *)BC.header + sizeof(mdxaHeader_t));
skel = (mdxaSkel_t *)((byte *)BC.header + sizeof(mdxaHeader_t) + offsets->offsets[child]);
// skel = BC.mSkels[child];
//rww - removed mSkels
int parent=BC.mFinalBones[child].parent;
assert((parent==-1&&child==0)||(parent>=0&&parent<(int)BC.mBones.size()));
if (angleOverride & BONE_ANGLES_REPLACE)
{
bool isRag=!!(angleOverride & BONE_ANGLES_RAGDOLL);
if (!isRag)
{ //do the same for ik.. I suppose.
isRag = !!(angleOverride & BONE_ANGLES_IK);
}
mdxaBone_t &bone = BC.mFinalBones[child].boneMatrix;
boneInfo_t &boneOverride = boneList[boneListIndex];
if (isRag)
{
mdxaBone_t temp, firstPass;
// give us the matrix the animation thinks we should have, so we can get the correct X&Y coors
Multiply_3x4Matrix(&firstPass, &BC.mFinalBones[parent].boneMatrix, &tbone[2]);
// this is crazy, we are gonna drive the animation to ID while we are doing post mults to compensate.
Multiply_3x4Matrix(&temp,&firstPass, &skel->BasePoseMat);
float matrixScale = VectorLength((float*)&temp);
static mdxaBone_t toMatrix =
{
{
{ 1.0f, 0.0f, 0.0f, 0.0f },
{ 0.0f, 1.0f, 0.0f, 0.0f },
{ 0.0f, 0.0f, 1.0f, 0.0f }
}
};
toMatrix.matrix[0][0]=matrixScale;
toMatrix.matrix[1][1]=matrixScale;
toMatrix.matrix[2][2]=matrixScale;
toMatrix.matrix[0][3]=temp.matrix[0][3];
toMatrix.matrix[1][3]=temp.matrix[1][3];
toMatrix.matrix[2][3]=temp.matrix[2][3];
Multiply_3x4Matrix(&temp, &toMatrix,&skel->BasePoseMatInv); //dest first arg
float blendTime = BC.incomingTime - boneList[boneListIndex].boneBlendStart;
float blendLerp = (blendTime / boneList[boneListIndex].boneBlendTime);
if (blendLerp>0.0f)
{
// has started
if (blendLerp>1.0f)
{
// done
// Multiply_3x4Matrix(&bone, &BC.mFinalBones[parent].boneMatrix,&temp);
memcpy (&bone,&temp, sizeof(mdxaBone_t));
}
else
{
// mdxaBone_t lerp;
// now do the blend into the destination
float blendFrontlerp = 1.0 - blendLerp;
for ( j = 0 ; j < 12 ; j++ )
{
((float *)&bone)[j] = (blendLerp * ((float *)&temp)[j])
+ (blendFrontlerp * ((float *)&tbone[2])[j]);
}
// Multiply_3x4Matrix(&bone, &BC.mFinalBones[parent].boneMatrix,&lerp);
}
}
}
else
{
mdxaBone_t temp, firstPass;
// give us the matrix the animation thinks we should have, so we can get the correct X&Y coors
Multiply_3x4Matrix(&firstPass, &BC.mFinalBones[parent].boneMatrix, &tbone[2]);
// are we attempting to blend with the base animation? and still within blend time?
if (boneOverride.boneBlendTime && (((boneOverride.boneBlendTime + boneOverride.boneBlendStart) < BC.incomingTime)))
{
// ok, we are supposed to be blending. Work out lerp
float blendTime = BC.incomingTime - boneList[boneListIndex].boneBlendStart;
float blendLerp = (blendTime / boneList[boneListIndex].boneBlendTime);
if (blendLerp <= 1)
{
if (blendLerp < 0)
{
assert(0);
}
// now work out the matrix we want to get *to* - firstPass is where we are coming *from*
Multiply_3x4Matrix(&temp, &firstPass, &skel->BasePoseMat);
float matrixScale = VectorLength((float*)&temp);
mdxaBone_t newMatrixTemp;
if (HackadelicOnClient)
{
for (int i=0; i<3;i++)
{
for(int x=0;x<3; x++)
{
newMatrixTemp.matrix[i][x] = boneOverride.newMatrix.matrix[i][x]*matrixScale;
}
}
newMatrixTemp.matrix[0][3] = temp.matrix[0][3];
newMatrixTemp.matrix[1][3] = temp.matrix[1][3];
newMatrixTemp.matrix[2][3] = temp.matrix[2][3];
}
else
{
for (int i=0; i<3;i++)
{
for(int x=0;x<3; x++)
{
newMatrixTemp.matrix[i][x] = boneOverride.matrix.matrix[i][x]*matrixScale;
}
}
newMatrixTemp.matrix[0][3] = temp.matrix[0][3];
newMatrixTemp.matrix[1][3] = temp.matrix[1][3];
newMatrixTemp.matrix[2][3] = temp.matrix[2][3];
}
Multiply_3x4Matrix(&temp, &newMatrixTemp,&skel->BasePoseMatInv);
// now do the blend into the destination
float blendFrontlerp = 1.0 - blendLerp;
for ( j = 0 ; j < 12 ; j++ )
{
((float *)&bone)[j] = (blendLerp * ((float *)&temp)[j])
+ (blendFrontlerp * ((float *)&firstPass)[j]);
}
}
else
{
bone = firstPass;
}
}
// no, so just override it directly
else
{
Multiply_3x4Matrix(&temp,&firstPass, &skel->BasePoseMat);
float matrixScale = VectorLength((float*)&temp);
mdxaBone_t newMatrixTemp;
if (HackadelicOnClient)
{
for (int i=0; i<3;i++)
{
for(int x=0;x<3; x++)
{
newMatrixTemp.matrix[i][x] = boneOverride.newMatrix.matrix[i][x]*matrixScale;
}
}
newMatrixTemp.matrix[0][3] = temp.matrix[0][3];
newMatrixTemp.matrix[1][3] = temp.matrix[1][3];
newMatrixTemp.matrix[2][3] = temp.matrix[2][3];
}
else
{
for (int i=0; i<3;i++)
{
for(int x=0;x<3; x++)
{
newMatrixTemp.matrix[i][x] = boneOverride.matrix.matrix[i][x]*matrixScale;
}
}
newMatrixTemp.matrix[0][3] = temp.matrix[0][3];
newMatrixTemp.matrix[1][3] = temp.matrix[1][3];
newMatrixTemp.matrix[2][3] = temp.matrix[2][3];
}
Multiply_3x4Matrix(&bone, &newMatrixTemp,&skel->BasePoseMatInv);
}
}
}
else if (angleOverride & BONE_ANGLES_PREMULT)
{
if ((angleOverride&BONE_ANGLES_RAGDOLL) || (angleOverride&BONE_ANGLES_IK))
{
mdxaBone_t tmp;
if (!child)
{
if (HackadelicOnClient)
{
Multiply_3x4Matrix(&tmp, &BC.rootMatrix, &boneList[boneListIndex].newMatrix);
}
else
{
Multiply_3x4Matrix(&tmp, &BC.rootMatrix, &boneList[boneListIndex].matrix);
}
}
else
{
if (HackadelicOnClient)
{
Multiply_3x4Matrix(&tmp, &BC.mFinalBones[parent].boneMatrix, &boneList[boneListIndex].newMatrix);
}
else
{
Multiply_3x4Matrix(&tmp, &BC.mFinalBones[parent].boneMatrix, &boneList[boneListIndex].matrix);
}
}
Multiply_3x4Matrix(&BC.mFinalBones[child].boneMatrix,&tmp, &tbone[2]);
}
else
{
if (!child)
{
// use the in coming root matrix as our basis
if (HackadelicOnClient)
{
Multiply_3x4Matrix(&BC.mFinalBones[child].boneMatrix, &BC.rootMatrix, &boneList[boneListIndex].newMatrix);
}
else
{
Multiply_3x4Matrix(&BC.mFinalBones[child].boneMatrix, &BC.rootMatrix, &boneList[boneListIndex].matrix);
}
}
else
{
// convert from 3x4 matrix to a 4x4 matrix
if (HackadelicOnClient)
{
Multiply_3x4Matrix(&BC.mFinalBones[child].boneMatrix, &BC.mFinalBones[parent].boneMatrix, &boneList[boneListIndex].newMatrix);
}
else
{
Multiply_3x4Matrix(&BC.mFinalBones[child].boneMatrix, &BC.mFinalBones[parent].boneMatrix, &boneList[boneListIndex].matrix);
}
}
}
}
else
// now transform the matrix by it's parent, asumming we have a parent, and we aren't overriding the angles absolutely
if (child)
{
Multiply_3x4Matrix(&BC.mFinalBones[child].boneMatrix, &BC.mFinalBones[parent].boneMatrix, &tbone[2]);
}
// now multiply our resulting bone by an override matrix should we need to
if (angleOverride & BONE_ANGLES_POSTMULT)
{
mdxaBone_t tempMatrix;
memcpy (&tempMatrix,&BC.mFinalBones[child].boneMatrix, sizeof(mdxaBone_t));
if (HackadelicOnClient)
{
Multiply_3x4Matrix(&BC.mFinalBones[child].boneMatrix, &tempMatrix, &boneList[boneListIndex].newMatrix);
}
else
{
Multiply_3x4Matrix(&BC.mFinalBones[child].boneMatrix, &tempMatrix, &boneList[boneListIndex].matrix);
}
}
/*
if (r_Ghoul2UnSqash->integer)
{
mdxaBone_t tempMatrix;
Multiply_3x4Matrix(&tempMatrix,&BC.mFinalBones[child].boneMatrix, &skel->BasePoseMat);
float maxl;
maxl=VectorLength(&skel->BasePoseMat.matrix[0][0]);
VectorNormalize(&tempMatrix.matrix[0][0]);
VectorNormalize(&tempMatrix.matrix[1][0]);
VectorNormalize(&tempMatrix.matrix[2][0]);
VectorScale(&tempMatrix.matrix[0][0],maxl,&tempMatrix.matrix[0][0]);
VectorScale(&tempMatrix.matrix[1][0],maxl,&tempMatrix.matrix[1][0]);
VectorScale(&tempMatrix.matrix[2][0],maxl,&tempMatrix.matrix[2][0]);
Multiply_3x4Matrix(&BC.mFinalBones[child].boneMatrix,&tempMatrix,&skel->BasePoseMatInv);
}
*/
//rwwFIXMEFIXME: Care?
}
void G2_SetUpBolts( mdxaHeader_t *header, CGhoul2Info &ghoul2, mdxaBone_v &bonePtr, boltInfo_v &boltList)
{
mdxaSkel_t *skel;
mdxaSkelOffsets_t *offsets;
offsets = (mdxaSkelOffsets_t *)((byte *)header + sizeof(mdxaHeader_t));
for (size_t i=0; ioffsets[boltList[i].boneNumber]);
Multiply_3x4Matrix(&boltList[i].position, &bonePtr[boltList[i].boneNumber].second, &skel->BasePoseMat);
}
}
}
//rww - RAGDOLL_BEGIN
#define GHOUL2_RAG_STARTED 0x0010
//rww - RAGDOLL_END
//rwwFIXMEFIXME: Move this into the stupid header or something.
void G2_TransformGhoulBones(boneInfo_v &rootBoneList,mdxaBone_t &rootMatrix, CGhoul2Info &ghoul2, int time,bool smooth=true)
{
#ifdef G2_PERFORMANCE_ANALYSIS
G2PerformanceTimer_G2_TransformGhoulBones.Start();
G2PerformanceCounter_G2_TransformGhoulBones++;
#endif
/*
model_t *currentModel;
model_t *animModel;
mdxaHeader_t *aHeader;
//currentModel = R_GetModelByHandle(RE_RegisterModel(ghoul2.mFileName));
currentModel = R_GetModelByHandle(ghoul2.mModel);
assert(currentModel);
assert(currentModel->mdxm);
animModel = R_GetModelByHandle(currentModel->mdxm->animIndex);
assert(animModel);
aHeader = animModel->mdxa;
assert(aHeader);
*/
model_t *currentModel = (model_t *)ghoul2.currentModel;
mdxaHeader_t *aHeader = (mdxaHeader_t *)ghoul2.aHeader;
assert(ghoul2.aHeader);
assert(ghoul2.currentModel);
assert(ghoul2.currentModel->mdxm);
if (!aHeader->numBones)
{
assert(0); // this would be strange
return;
}
if (!ghoul2.mBoneCache)
{
ghoul2.mBoneCache=new CBoneCache(currentModel,aHeader);
#ifdef _FULL_G2_LEAK_CHECKING
g_Ghoul2Allocations += sizeof(*ghoul2.mBoneCache);
#endif
}
ghoul2.mBoneCache->mod=currentModel;
ghoul2.mBoneCache->header=aHeader;
assert(ghoul2.mBoneCache->mBones.size()==(unsigned)aHeader->numBones);
ghoul2.mBoneCache->mSmoothingActive=false;
ghoul2.mBoneCache->mUnsquash=false;
// master smoothing control
if (HackadelicOnClient && smooth && !ri.Cvar_VariableIntegerValue( "dedicated" ))
{
ghoul2.mBoneCache->mLastTouch=ghoul2.mBoneCache->mLastLastTouch;
/*
float val=r_Ghoul2AnimSmooth->value;
if (smooth&&val>0.0f&&val<1.0f)
{
// if (HackadelicOnClient)
// {
ghoul2.mBoneCache->mLastTouch=ghoul2.mBoneCache->mLastLastTouch;
// }
ghoul2.mBoneCache->mSmoothFactor=val;
ghoul2.mBoneCache->mSmoothingActive=true;
if (r_Ghoul2UnSqashAfterSmooth->integer)
{
ghoul2.mBoneCache->mUnsquash=true;
}
}
else
{
ghoul2.mBoneCache->mSmoothFactor=1.0f;
}
*/
// master smoothing control
float val=r_Ghoul2AnimSmooth->value;
if (val>0.0f&&val<1.0f)
{
//if (ghoul2.mFlags&GHOUL2_RESERVED_FOR_RAGDOLL)
#if 1
if(ghoul2.mFlags & GHOUL2_CRAZY_SMOOTH)
{
val = 0.9f;
}
else if(ghoul2.mFlags & GHOUL2_RAG_STARTED)
{
for (size_t k=0;ktime-250 &&
bone.firstCollisionTime