jkxr/Projects/Android/jni/OpenJK/codemp/rd-dedicated/G2_bones.cpp

4668 lines
133 KiB
C++
Raw Normal View History

/*
===========================================================================
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 "qcommon/matcomp.h"
#include "ghoul2/G2.h"
#include "ghoul2/G2_gore.h"
#include "ghoul2/g2_local.h"
#include "tr_local.h"
//rww - RAGDOLL_BEGIN
#ifndef __linux__
#include <float.h>
#else
#include <math.h>
#endif
//#define RAG_TRACE_DEBUG_LINES
#include "client/client.h" //while this is all "shared" code, there are some places where we want to make cgame callbacks (for ragdoll) only if the cgvm exists
//rww - RAGDOLL_END
//=====================================================================================================================
// Bone List handling routines - so entities can override bone info on a bone by bone level, and also interrogate this info
// Given a bone name, see if that bone is already in our bone list - note the model_t pointer that gets passed in here MUST point at the
// gla file, not the glm file type.
int G2_Find_Bone(const model_t *mod, boneInfo_v &blist, const char *boneName)
{
mdxaSkel_t *skel;
mdxaSkelOffsets_t *offsets;
offsets = (mdxaSkelOffsets_t *)((byte *)mod->mdxa + sizeof(mdxaHeader_t));
skel = (mdxaSkel_t *)((byte *)mod->mdxa + sizeof(mdxaHeader_t) + offsets->offsets[0]);
// look through entire list
for(size_t i=0; i<blist.size(); i++)
{
// if this bone entry has no info in it, bounce over it
if (blist[i].boneNumber == -1)
{
continue;
}
// figure out what skeletal info structure this bone entry is looking at
skel = (mdxaSkel_t *)((byte *)mod->mdxa + sizeof(mdxaHeader_t) + offsets->offsets[blist[i].boneNumber]);
// if name is the same, we found it
if (!Q_stricmp(skel->name, boneName))
{
return i;
}
}
// didn't find it
return -1;
}
// we need to add a bone to the list - find a free one and see if we can find a corresponding bone in the gla file
int G2_Add_Bone (const model_t *mod, boneInfo_v &blist, const char *boneName)
{
int x;
mdxaSkel_t *skel;
mdxaSkelOffsets_t *offsets;
boneInfo_t tempBone;
//rww - RAGDOLL_BEGIN
memset(&tempBone, 0, sizeof(tempBone));
//rww - RAGDOLL_END
offsets = (mdxaSkelOffsets_t *)((byte *)mod->mdxa + sizeof(mdxaHeader_t));
// walk the entire list of bones in the gla file for this model and see if any match the name of the bone we want to find
for (x=0; x< mod->mdxa->numBones; x++)
{
skel = (mdxaSkel_t *)((byte *)mod->mdxa + sizeof(mdxaHeader_t) + offsets->offsets[x]);
// if name is the same, we found it
if (!Q_stricmp(skel->name, boneName))
{
break;
}
}
// check to see we did actually make a match with a bone in the model
if (x == mod->mdxa->numBones)
{
// didn't find it? Error
//assert(0);
#ifdef _DEBUG
Com_Printf("WARNING: Failed to add bone %s\n", boneName);
#endif
#ifdef _RAG_PRINT_TEST
Com_Printf("WARNING: Failed to add bone %s\n", boneName);
#endif
return -1;
}
// look through entire list - see if it's already there first
for(size_t i=0; i<blist.size(); i++)
{
// if this bone entry has info in it, bounce over it
if (blist[i].boneNumber != -1)
{
skel = (mdxaSkel_t *)((byte *)mod->mdxa + sizeof(mdxaHeader_t) + offsets->offsets[blist[i].boneNumber]);
// if name is the same, we found it
if (!Q_stricmp(skel->name, boneName))
{
return i;
}
}
else
{
// if we found an entry that had a -1 for the bonenumber, then we hit a bone slot that was empty
blist[i].boneNumber = x;
blist[i].flags = 0;
return i;
}
}
#ifdef _RAG_PRINT_TEST
Com_Printf("New bone added for %s\n", boneName);
#endif
// ok, we didn't find an existing bone of that name, or an empty slot. Lets add an entry
tempBone.boneNumber = x;
tempBone.flags = 0;
blist.push_back(tempBone);
return blist.size()-1;
}
// Given a model handle, and a bone name, we want to remove this bone from the bone override list
qboolean G2_Remove_Bone_Index ( boneInfo_v &blist, int index)
{
if (index != -1)
{
if (blist[index].flags & BONE_ANGLES_RAGDOLL)
{
return qtrue; // don't accept any calls on ragdoll bones
}
}
// did we find it?
if (index != -1)
{
// check the flags first - if it's still being used Do NOT remove it
if (!blist[index].flags)
{
// set this bone to not used
blist[index].boneNumber = -1;
unsigned int newSize = blist.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=blist.size()-1; i>-1; i--)
{
if (blist[i].boneNumber == -1)
{
newSize = i;
}
// once we hit one that isn't a -1, we are done.
else
{
break;
}
}
// do we need to resize?
if (newSize != blist.size())
{
// yes, so lets do it
blist.resize(newSize);
}
return qtrue;
}
}
// assert(0);
// no
return qfalse;
}
// given a bone number, see if there is an override bone in the bone list
int G2_Find_Bone_In_List(boneInfo_v &blist, const int boneNum)
{
// look through entire list
for(size_t i=0; i<blist.size(); i++)
{
if (blist[i].boneNumber == boneNum)
{
return i;
}
}
return -1;
}
// given a model, bonelist and bonename, lets stop an anim if it's playing.
qboolean G2_Stop_Bone_Index( boneInfo_v &blist, int index, int flags)
{
// did we find it?
if (index != -1)
{
blist[index].flags &= ~(flags);
// try and remove this bone if we can
return G2_Remove_Bone_Index(blist, index);
}
assert(0);
return qfalse;
}
// generate a matrix for a given bone given some new angles for it.
void G2_Generate_Matrix(const model_t *mod, boneInfo_v &blist, int index, const float *angles, int flags,
const Eorientations up, const Eorientations left, const Eorientations forward)
{
mdxaSkel_t *skel;
mdxaSkelOffsets_t *offsets;
mdxaBone_t temp1;
mdxaBone_t permutation;
mdxaBone_t *boneOverride = &blist[index].matrix;
vec3_t newAngles;
if (flags & (BONE_ANGLES_PREMULT | BONE_ANGLES_POSTMULT))
{
// build us a matrix out of the angles we are fed - but swap y and z because of wacky Quake setup
vec3_t newAngles;
// determine what axis newAngles Yaw should revolve around
switch (up)
{
case NEGATIVE_X:
newAngles[1] = angles[2] + 180;
break;
case POSITIVE_X:
newAngles[1] = angles[2];
break;
case NEGATIVE_Y:
newAngles[1] = angles[0];
break;
case POSITIVE_Y:
newAngles[1] = angles[0];
break;
case NEGATIVE_Z:
newAngles[1] = angles[1] + 180;
break;
case POSITIVE_Z:
newAngles[1] = angles[1];
break;
default:
break;
}
// determine what axis newAngles pitch should revolve around
switch (left)
{
case NEGATIVE_X:
newAngles[0] = angles[2];
break;
case POSITIVE_X:
newAngles[0] = angles[2] + 180;
break;
case NEGATIVE_Y:
newAngles[0] = angles[0];
break;
case POSITIVE_Y:
newAngles[0] = angles[0] + 180;
break;
case NEGATIVE_Z:
newAngles[0] = angles[1];
break;
case POSITIVE_Z:
newAngles[0] = angles[1];
break;
default:
break;
}
// determine what axis newAngles Roll should revolve around
switch (forward)
{
case NEGATIVE_X:
newAngles[2] = angles[2];
break;
case POSITIVE_X:
newAngles[2] = angles[2];
break;
case NEGATIVE_Y:
newAngles[2] = angles[0];
break;
case POSITIVE_Y:
newAngles[2] = angles[0] + 180;
break;
case NEGATIVE_Z:
newAngles[2] = angles[1];
break;
case POSITIVE_Z:
newAngles[2] = angles[1] + 180;
break;
default:
break;
}
Create_Matrix(newAngles, boneOverride);
// figure out where the bone hirearchy info is
offsets = (mdxaSkelOffsets_t *)((byte *)mod->mdxa + sizeof(mdxaHeader_t));
skel = (mdxaSkel_t *)((byte *)mod->mdxa + sizeof(mdxaHeader_t) + offsets->offsets[blist[index].boneNumber]);
Multiply_3x4Matrix(&temp1, boneOverride,&skel->BasePoseMatInv);
Multiply_3x4Matrix(boneOverride,&skel->BasePoseMat, &temp1);
}
else
{
VectorCopy(angles, newAngles);
// why I should need do this Fuck alone knows. But I do.
if (left == POSITIVE_Y)
{
newAngles[0] +=180;
}
Create_Matrix(newAngles, &temp1);
permutation.matrix[0][0] = permutation.matrix[0][1] = permutation.matrix[0][2] = permutation.matrix[0][3] = 0;
permutation.matrix[1][0] = permutation.matrix[1][1] = permutation.matrix[1][2] = permutation.matrix[1][3] = 0;
permutation.matrix[2][0] = permutation.matrix[2][1] = permutation.matrix[2][2] = permutation.matrix[2][3] = 0;
// determine what axis newAngles Yaw should revolve around
switch (forward)
{
case NEGATIVE_X:
permutation.matrix[0][0] = -1; // works
break;
case POSITIVE_X:
permutation.matrix[0][0] = 1; // works
break;
case NEGATIVE_Y:
permutation.matrix[1][0] = -1;
break;
case POSITIVE_Y:
permutation.matrix[1][0] = 1;
break;
case NEGATIVE_Z:
permutation.matrix[2][0] = -1;
break;
case POSITIVE_Z:
permutation.matrix[2][0] = 1;
break;
default:
break;
}
// determine what axis newAngles pitch should revolve around
switch (left)
{
case NEGATIVE_X:
permutation.matrix[0][1] = -1;
break;
case POSITIVE_X:
permutation.matrix[0][1] = 1;
break;
case NEGATIVE_Y:
permutation.matrix[1][1] = -1; // works
break;
case POSITIVE_Y:
permutation.matrix[1][1] = 1; // works
break;
case NEGATIVE_Z:
permutation.matrix[2][1] = -1;
break;
case POSITIVE_Z:
permutation.matrix[2][1] = 1;
break;
default:
break;
}
// determine what axis newAngles Roll should revolve around
switch (up)
{
case NEGATIVE_X:
permutation.matrix[0][2] = -1;
break;
case POSITIVE_X:
permutation.matrix[0][2] = 1;
break;
case NEGATIVE_Y:
permutation.matrix[1][2] = -1;
break;
case POSITIVE_Y:
permutation.matrix[1][2] = 1;
break;
case NEGATIVE_Z:
permutation.matrix[2][2] = -1; // works
break;
case POSITIVE_Z:
permutation.matrix[2][2] = 1; // works
break;
default:
break;
}
Multiply_3x4Matrix(boneOverride, &temp1,&permutation);
}
// keep a copy of the matrix in the newmatrix which is actually what we use
memcpy(&blist[index].newMatrix, &blist[index].matrix, sizeof(mdxaBone_t));
}
//=========================================================================================
//// Public Bone Routines
// Given a model handle, and a bone name, we want to remove this bone from the bone override list
qboolean G2_Remove_Bone (CGhoul2Info *ghlInfo, boneInfo_v &blist, const char *boneName)
{
int index;
assert(ghlInfo->animModel);
index = G2_Find_Bone(ghlInfo->animModel, blist, boneName);
return G2_Remove_Bone_Index(blist, index);
}
#define DEBUG_PCJ (0)
// Given a model handle, and a bone name, we want to set angles specifically for overriding
qboolean G2_Set_Bone_Angles_Index( boneInfo_v &blist, const int index,
const float *angles, const int flags, const Eorientations yaw,
const Eorientations pitch, const Eorientations roll, qhandle_t *modelList,
const int modelIndex, const int blendTime, const int currentTime)
{
if ((index >= (int)blist.size()) || (blist[index].boneNumber == -1))
{
// we are attempting to set a bone override that doesn't exist
assert(0);
return qfalse;
}
if (index != -1)
{
if (blist[index].flags & BONE_ANGLES_RAGDOLL)
{
return qtrue; // don't accept any calls on ragdoll bones
}
}
if (flags & (BONE_ANGLES_PREMULT | BONE_ANGLES_POSTMULT))
{
// you CANNOT call this with an index with these kinds of bone overrides - we need the model details for these kinds of bone angle overrides
assert(0);
return qfalse;
}
// yes, so set the angles and flags correctly
blist[index].flags &= ~(BONE_ANGLES_TOTAL);
blist[index].flags |= flags;
blist[index].boneBlendStart = currentTime;
blist[index].boneBlendTime = blendTime;
#if DEBUG_PCJ
OutputDebugString(va("PCJ %2d %6d (%6.2f,%6.2f,%6.2f) %d %d %d %d\n",index,currentTime,angles[0],angles[1],angles[2],yaw,pitch,roll,flags));
#endif
G2_Generate_Matrix(NULL, blist, index, angles, flags, yaw, pitch, roll);
return qtrue;
}
// Given a model handle, and a bone name, we want to set angles specifically for overriding
qboolean G2_Set_Bone_Angles(CGhoul2Info *ghlInfo, boneInfo_v &blist, const char *boneName, const float *angles,
const int flags, const Eorientations up, const Eorientations left, const Eorientations forward,
qhandle_t *modelList, const int modelIndex, const int blendTime, const int currentTime)
{
model_t *mod_a;
mod_a = (model_t *)ghlInfo->animModel;
int index = G2_Find_Bone(mod_a, blist, boneName);
// did we find it?
if (index != -1)
{
if (blist[index].flags & BONE_ANGLES_RAGDOLL)
{
return qtrue; // don't accept any calls on ragdoll bones
}
// yes, so set the angles and flags correctly
blist[index].flags &= ~(BONE_ANGLES_TOTAL);
blist[index].flags |= flags;
blist[index].boneBlendStart = currentTime;
blist[index].boneBlendTime = blendTime;
#if DEBUG_PCJ
OutputDebugString(va("%2d %6d (%6.2f,%6.2f,%6.2f) %d %d %d %d\n",index,currentTime,angles[0],angles[1],angles[2],up,left,forward,flags));
#endif
G2_Generate_Matrix(mod_a, blist, index, angles, flags, up, left, forward);
return qtrue;
}
// no - lets try and add this bone in
index = G2_Add_Bone(mod_a, blist, boneName);
// did we find a free one?
if (index != -1)
{
// yes, so set the angles and flags correctly
blist[index].flags &= ~(BONE_ANGLES_TOTAL);
blist[index].flags |= flags;
blist[index].boneBlendStart = currentTime;
blist[index].boneBlendTime = blendTime;
#if DEBUG_PCJ
OutputDebugString(va("%2d %6d (%6.2f,%6.2f,%6.2f) %d %d %d %d\n",index,currentTime,angles[0],angles[1],angles[2],up,left,forward,flags));
#endif
G2_Generate_Matrix(mod_a, blist, index, angles, flags, up, left, forward);
return qtrue;
}
// assert(0);
//Jeese, we don't need an assert here too. There's already a warning in G2_Add_Bone if it fails.
// no
return qfalse;
}
// Given a model handle, and a bone name, we want to set angles specifically for overriding - using a matrix directly
qboolean G2_Set_Bone_Angles_Matrix_Index(boneInfo_v &blist, const int index,
const mdxaBone_t &matrix, const int flags, qhandle_t *modelList,
const int modelIndex, const int blendTime, const int currentTime)
{
if ((index >= (int)blist.size()) || (blist[index].boneNumber == -1))
{
// we are attempting to set a bone override that doesn't exist
assert(0);
return qfalse;
}
if (index != -1)
{
if (blist[index].flags & BONE_ANGLES_RAGDOLL)
{
return qtrue; // don't accept any calls on ragdoll bones
}
}
// yes, so set the angles and flags correctly
blist[index].flags &= ~(BONE_ANGLES_TOTAL);
blist[index].flags |= flags;
blist[index].boneBlendStart = currentTime;
blist[index].boneBlendTime = blendTime;
memcpy(&blist[index].matrix, &matrix, sizeof(mdxaBone_t));
memcpy(&blist[index].newMatrix, &matrix, sizeof(mdxaBone_t));
return qtrue;
}
// Given a model handle, and a bone name, we want to set angles specifically for overriding - using a matrix directly
qboolean G2_Set_Bone_Angles_Matrix(const char *fileName, boneInfo_v &blist, const char *boneName, const mdxaBone_t &matrix,
const int flags, qhandle_t *modelList, const int modelIndex, const int blendTime, const int currentTime)
{
model_t *mod_m;
if (!fileName[0])
{
mod_m = R_GetModelByHandle(modelList[modelIndex]);
}
else
{
mod_m = R_GetModelByHandle(RE_RegisterModel(fileName));
}
model_t *mod_a = R_GetModelByHandle(mod_m->mdxm->animIndex);
int index = G2_Find_Bone(mod_a, blist, boneName);
if (index != -1)
{
if (blist[index].flags & BONE_ANGLES_RAGDOLL)
{
return qtrue; // don't accept any calls on ragdoll bones
}
}
// did we find it?
if (index != -1)
{
// yes, so set the angles and flags correctly
blist[index].flags &= ~(BONE_ANGLES_TOTAL);
blist[index].flags |= flags;
memcpy(&blist[index].matrix, &matrix, sizeof(mdxaBone_t));
memcpy(&blist[index].newMatrix, &matrix, sizeof(mdxaBone_t));
return qtrue;
}
// no - lets try and add this bone in
index = G2_Add_Bone(mod_a, blist, boneName);
// did we find a free one?
if (index != -1)
{
// yes, so set the angles and flags correctly
blist[index].flags &= ~(BONE_ANGLES_TOTAL);
blist[index].flags |= flags;
memcpy(&blist[index].matrix, &matrix, sizeof(mdxaBone_t));
memcpy(&blist[index].newMatrix, &matrix, sizeof(mdxaBone_t));
return qtrue;
}
assert(0);
// no
return qfalse;
}
#define DEBUG_G2_TIMING (0)
// given a model, bone name, a bonelist, a start/end frame number, a anim speed and some anim flags, set up or modify an existing bone entry for a new set of anims
qboolean G2_Set_Bone_Anim_Index(
boneInfo_v &blist,
const int index,
const int startFrame,
const int endFrame,
const int flags,
const float animSpeed,
const int currentTime,
const float setFrame,
const int blendTime,
const int numFrames)
{
int modFlags = flags;
if ((index >= (int)blist.size()) || (blist[index].boneNumber == -1))
{
// we are attempting to set a bone override that doesn't exist
assert(0);
return qfalse;
}
if (index != -1)
{
if (blist[index].flags & BONE_ANGLES_RAGDOLL)
{
return qtrue; // don't accept any calls on ragdoll bones
}
//mark it for needing a transform for the cached trace transform stuff
blist[index].flags |= BONE_NEED_TRANSFORM;
}
if (setFrame != -1)
{
assert((setFrame >= startFrame) && (setFrame <= endFrame));
}
if (flags & BONE_ANIM_BLEND)
{
float currentFrame, animSpeed;
int startFrame, endFrame, flags;
// figure out where we are now
if (G2_Get_Bone_Anim_Index(blist, index, currentTime, &currentFrame, &startFrame, &endFrame, &flags, &animSpeed, NULL, numFrames))
{
if (blist[index].blendStart == currentTime) //we're replacing a blend in progress which hasn't started
{
// set the amount of time it's going to take to blend this anim with the last frame of the last one
blist[index].blendTime = blendTime;
}
else
{
if (animSpeed<0.0f)
{
blist[index].blendFrame = floor(currentFrame);
blist[index].blendLerpFrame = floor(currentFrame);
}
else
{
blist[index].blendFrame = currentFrame;
blist[index].blendLerpFrame = currentFrame+1;
// cope with if the lerp frame is actually off the end of the anim
if (blist[index].blendFrame >= endFrame )
{
// we only want to lerp with the first frame of the anim if we are looping
if (blist[index].flags & BONE_ANIM_OVERRIDE_LOOP)
{
blist[index].blendFrame = startFrame;
}
// if we intend to end this anim or freeze after this, then just keep on the last frame
else
{
// assert(endFrame>0);
if (endFrame <= 0)
{
blist[index].blendLerpFrame = 0;
}
else
{
blist[index].blendFrame = endFrame -1;
}
}
}
// cope with if the lerp frame is actually off the end of the anim
if (blist[index].blendLerpFrame >= endFrame )
{
// we only want to lerp with the first frame of the anim if we are looping
if (blist[index].flags & BONE_ANIM_OVERRIDE_LOOP)
{
blist[index].blendLerpFrame = startFrame;
}
// if we intend to end this anim or freeze after this, then just keep on the last frame
else
{
// assert(endFrame>0);
if (endFrame <= 0)
{
blist[index].blendLerpFrame = 0;
}
else
{
blist[index].blendLerpFrame = endFrame - 1;
}
}
}
}
// set the amount of time it's going to take to blend this anim with the last frame of the last one
blist[index].blendTime = blendTime;
blist[index].blendStart = currentTime;
}
}
// hmm, we weren't animating on this bone. In which case disable the blend
else
{
blist[index].blendFrame = blist[index].blendLerpFrame = 0;
blist[index].blendTime = 0;
modFlags &= ~(BONE_ANIM_BLEND);
}
}
else
{
blist[index].blendFrame = blist[index].blendLerpFrame = 0;
blist[index].blendTime = blist[index].blendStart = 0;
// we aren't blending, so remove the option to do so
modFlags &= ~BONE_ANIM_BLEND;
}
// yes, so set the anim data and flags correctly
blist[index].endFrame = endFrame;
blist[index].startFrame = startFrame;
blist[index].animSpeed = animSpeed;
blist[index].pauseTime = 0;
// start up the animation:)
if (setFrame != -1)
{
blist[index].lastTime = blist[index].startTime = (currentTime - (((setFrame - (float)startFrame) * 50.0)/ animSpeed));
}
else
{
blist[index].lastTime = blist[index].startTime = currentTime;
}
blist[index].flags &= ~(BONE_ANIM_TOTAL);
if (blist[index].flags < 0)
{
blist[index].flags = 0;
}
blist[index].flags |= modFlags;
#if DEBUG_G2_TIMING
if (index==2)
{
const boneInfo_t &bone=blist[index];
char mess[1000];
if (bone.flags&BONE_ANIM_BLEND)
{
sprintf(mess,"sab[%2d] %5d %5d (%5d-%5d) %4.2f %4x bt(%5d-%5d) %7.2f %5d\n",
index,
currentTime,
bone.startTime,
bone.startFrame,
bone.endFrame,
bone.animSpeed,
bone.flags,
bone.blendStart,
bone.blendStart+bone.blendTime,
bone.blendFrame,
bone.blendLerpFrame
);
}
else
{
sprintf(mess,"saa[%2d] %5d %5d (%5d-%5d) %4.2f %4x\n",
index,
currentTime,
bone.startTime,
bone.startFrame,
bone.endFrame,
bone.animSpeed,
bone.flags
);
}
OutputDebugString(mess);
}
#endif
return qtrue;
}
// given a model, bone name, a bonelist, a start/end frame number, a anim speed and some anim flags, set up or modify an existing bone entry for a new set of anims
qboolean G2_Set_Bone_Anim(CGhoul2Info *ghlInfo,
boneInfo_v &blist,
const char *boneName,
const int startFrame,
const int endFrame,
const int flags,
const float animSpeed,
const int currentTime,
const float setFrame,
const int blendTime)
{
model_t *mod_a = (model_t *)ghlInfo->animModel;
int index = G2_Find_Bone(mod_a, blist, boneName);
if (index == -1)
{
index = G2_Add_Bone(mod_a, blist, boneName);
}
if (index != -1)
{
if (blist[index].flags & BONE_ANGLES_RAGDOLL)
{
return qtrue; // don't accept any calls on ragdoll bones
}
}
if (index != -1)
{
return G2_Set_Bone_Anim_Index(blist,index,startFrame,endFrame,flags,animSpeed,currentTime,setFrame,blendTime,ghlInfo->aHeader->numFrames);
}
return qfalse;
}
qboolean G2_Get_Bone_Anim_Range(CGhoul2Info *ghlInfo, boneInfo_v &blist, const char *boneName, int *startFrame, int *endFrame)
{
model_t *mod_a = (model_t *)ghlInfo->animModel;
int index = G2_Find_Bone(mod_a, blist, boneName);
// did we find it?
if (index != -1)
{
// are we an animating bone?
if (blist[index].flags & (BONE_ANIM_OVERRIDE_LOOP | BONE_ANIM_OVERRIDE))
{
*startFrame = blist[index].startFrame;
*endFrame = blist[index].endFrame;
return qtrue;
}
}
return qfalse;
}
// given a model, bonelist and bonename, return the current frame, startframe and endframe of the current animation
// NOTE if we aren't running an animation, then qfalse is returned
void G2_TimingModel(boneInfo_t &bone,int currentTime,int numFramesInFile,int &currentFrame,int &newFrame,float &lerp);
qboolean G2_Get_Bone_Anim_Index( boneInfo_v &blist, const int index, const int currentTime,
float *currentFrame, int *startFrame, int *endFrame, int *flags, float *retAnimSpeed, qhandle_t *modelList, int numFrames)
{
// did we find it?
if ((index>=0) && !((index >= (int)blist.size()) || (blist[index].boneNumber == -1)))
{
// are we an animating bone?
if (blist[index].flags & (BONE_ANIM_OVERRIDE_LOOP | BONE_ANIM_OVERRIDE))
{
int lcurrentFrame,newFrame;
float lerp;
G2_TimingModel(blist[index],currentTime,numFrames,lcurrentFrame,newFrame,lerp);
*currentFrame =float(lcurrentFrame)+lerp;
*startFrame = blist[index].startFrame;
*endFrame = blist[index].endFrame;
*flags = blist[index].flags;
*retAnimSpeed = blist[index].animSpeed;
return qtrue;
}
}
*startFrame=0;
*endFrame=1;
*currentFrame=0.0f;
*flags=0;
*retAnimSpeed=0.0f;
return qfalse;
}
// given a model, bonelist and bonename, return the current frame, startframe and endframe of the current animation
// NOTE if we aren't running an animation, then qfalse is returned
qboolean G2_Get_Bone_Anim(CGhoul2Info *ghlInfo, boneInfo_v &blist, const char *boneName, const int currentTime,
float *currentFrame, int *startFrame, int *endFrame, int *flags, float *retAnimSpeed, qhandle_t *modelList, int modelIndex)
{
model_t *mod_a = (model_t *)ghlInfo->animModel;
int index = G2_Find_Bone(mod_a, blist, boneName);
if (index==-1)
{
index = G2_Add_Bone(mod_a, blist, boneName);
if (index == -1)
{
return qfalse;
}
}
assert(ghlInfo->aHeader);
if (G2_Get_Bone_Anim_Index(blist, index, currentTime, currentFrame, startFrame, endFrame, flags, retAnimSpeed, modelList, ghlInfo->aHeader->numFrames))
{
assert(*startFrame>=0&&*startFrame<ghlInfo->aHeader->numFrames);
assert(*endFrame>0&&*endFrame<=ghlInfo->aHeader->numFrames);
assert(*currentFrame>=0.0f&&((int)(*currentFrame))<ghlInfo->aHeader->numFrames);
return qtrue;
}
return qfalse;
}
// given a model, bonelist and bonename, lets pause an anim if it's playing.
qboolean G2_Pause_Bone_Anim(CGhoul2Info *ghlInfo, boneInfo_v &blist, const char *boneName, const int currentTime)
{
model_t *mod_a = (model_t *)ghlInfo->animModel;
int index = G2_Find_Bone(mod_a, blist, boneName);
// did we find it?
if (index != -1)
{
// are we pausing or un pausing?
if (blist[index].pauseTime)
{
int startFrame = 0, endFrame = 0, flags = 0;
float currentFrame = 0.0f, animSpeed = 1.0f;
// figure out what frame we are on now
G2_Get_Bone_Anim(ghlInfo, blist, boneName, blist[index].pauseTime, &currentFrame, &startFrame, &endFrame, &flags, &animSpeed, NULL, 0);
// reset start time so we are actually on this frame right now
G2_Set_Bone_Anim(ghlInfo, blist, boneName, startFrame, endFrame, flags, animSpeed, currentTime, currentFrame, 0);
// no pausing anymore
blist[index].pauseTime = 0;
}
// ahh, just pausing, the easy bit
else
{
blist[index].pauseTime = currentTime;
}
return qtrue;
}
assert(0);
return qfalse;
}
qboolean G2_IsPaused(const char *fileName, boneInfo_v &blist, const char *boneName)
{
model_t *mod_m = R_GetModelByHandle(RE_RegisterModel(fileName));
model_t *mod_a = R_GetModelByHandle(mod_m->mdxm->animIndex);
int index = G2_Find_Bone(mod_a, blist, boneName);
// did we find it?
if (index != -1)
{
// are we paused?
if (blist[index].pauseTime)
{
// yup. paused.
return qtrue;
}
return qfalse;
}
return qfalse;
}
// given a model, bonelist and bonename, lets stop an anim if it's playing.
qboolean G2_Stop_Bone_Anim_Index(boneInfo_v &blist, const int index)
{
if ((index >= (int)blist.size()) || (blist[index].boneNumber == -1))
{
// we are attempting to set a bone override that doesn't exist
assert(0);
return qfalse;
}
blist[index].flags &= ~(BONE_ANIM_TOTAL);
// try and remove this bone if we can
return G2_Remove_Bone_Index(blist, index);
}
// given a model, bonelist and bonename, lets stop an anim if it's playing.
qboolean G2_Stop_Bone_Anim(const char *fileName, boneInfo_v &blist, const char *boneName)
{
model_t *mod_m = R_GetModelByHandle(RE_RegisterModel(fileName));
model_t *mod_a = R_GetModelByHandle(mod_m->mdxm->animIndex);
int index = G2_Find_Bone(mod_a, blist, boneName);
// did we find it?
if (index != -1)
{
blist[index].flags &= ~(BONE_ANIM_TOTAL);
// try and remove this bone if we can
return G2_Remove_Bone_Index(blist, index);
}
assert(0);
return qfalse;
}
// given a model, bonelist and bonename, lets stop an anim if it's playing.
qboolean G2_Stop_Bone_Angles_Index(boneInfo_v &blist, const int index)
{
if ((index >= (int)blist.size()) || (blist[index].boneNumber == -1))
{
// we are attempting to set a bone override that doesn't exist
assert(0);
return qfalse;
}
blist[index].flags &= ~(BONE_ANGLES_TOTAL);
// try and remove this bone if we can
return G2_Remove_Bone_Index(blist, index);
}
// given a model, bonelist and bonename, lets stop an anim if it's playing.
qboolean G2_Stop_Bone_Angles(const char *fileName, boneInfo_v &blist, const char *boneName)
{
model_t *mod_m = R_GetModelByHandle(RE_RegisterModel(fileName));
model_t *mod_a = R_GetModelByHandle(mod_m->mdxm->animIndex);
int index = G2_Find_Bone(mod_a, blist, boneName);
// did we find it?
if (index != -1)
{
blist[index].flags &= ~(BONE_ANGLES_TOTAL);
// try and remove this bone if we can
return G2_Remove_Bone_Index(blist, index);
}
assert(0);
return qfalse;
}
// actually walk the bone list and update each and every bone if we have ended an animation for them.
void G2_Animate_Bone_List(CGhoul2Info_v &ghoul2, const int currentTime, const int index )
{
boneInfo_v &blist = ghoul2[index].mBlist;
// look through entire list
for(size_t i=0; i<blist.size(); i++)
{
// we we a valid bone override?
if (blist[i].boneNumber != -1)
{
// are we animating?
if (blist[i].flags & (BONE_ANIM_OVERRIDE_LOOP | BONE_ANIM_OVERRIDE))
{
// yes - add in animation speed to current frame
float animSpeed = blist[i].animSpeed;
float endFrame = (float)blist[i].endFrame ;
float time = (currentTime - blist[i].startTime) / 50.0f;
// are we a paused anim?
if (blist[i].pauseTime)
{
time = (blist[i].pauseTime - blist[i].startTime) / 50.0f;
}
if (time<0.0f)
{
time=0.0f;
}
float newFrame_g = blist[i].startFrame + (time * animSpeed);
int animSize = endFrame - blist[i].startFrame;
// 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 (blist[i].flags & BONE_ANIM_OVERRIDE_LOOP)
{
// get our new animation frame back within the bounds of the animation set
if (animSpeed < 0.0f)
{
if (newFrame_g <= endFrame+1)
{
newFrame_g=endFrame+fmod(newFrame_g-endFrame,animSize)-animSize;
}
}
else
{
if (newFrame_g >= endFrame)
{
newFrame_g=endFrame+fmod(newFrame_g-endFrame,animSize)-animSize;
}
}
// figure out new start time
float frameTime = newFrame_g - blist[i].startFrame ;
blist[i].startTime = currentTime - (int)((frameTime / animSpeed) * 50.0f);
if (blist[i].startTime>currentTime)
{
blist[i].startTime=currentTime;
}
assert(blist[i].startTime <= currentTime);
blist[i].lastTime = blist[i].startTime;
}
else
{
if ((blist[i].flags & BONE_ANIM_OVERRIDE_FREEZE) != BONE_ANIM_OVERRIDE_FREEZE)
{
// nope, just stop it. And remove the bone if possible
G2_Stop_Bone_Index(blist, i, (BONE_ANIM_TOTAL));
}
}
}
}
}
}
}
}
//rww - RAGDOLL_BEGIN
/*
rag stuff
*/
static void G2_RagDollSolve(CGhoul2Info_v &ghoul2V,int g2Index,float decay,int frameNum,const vec3_t currentOrg,bool LimitAngles,CRagDollUpdateParams *params = NULL);
static void G2_RagDollCurrentPosition(CGhoul2Info_v &ghoul2V,int g2Index,int frameNum,const vec3_t angles,const vec3_t position,const vec3_t scale);
static bool G2_RagDollSettlePositionNumeroTrois(CGhoul2Info_v &ghoul2V,const vec3_t currentOrg,CRagDollUpdateParams *params, int curTime);
static bool G2_RagDollSetup(CGhoul2Info &ghoul2,int frameNum,bool resetOrigin,const vec3_t origin,bool anyRendered);
void G2_GetBoneBasepose(CGhoul2Info &ghoul2,int boneNum,mdxaBone_t *&retBasepose,mdxaBone_t *&retBaseposeInv);
int G2_GetBoneDependents(CGhoul2Info &ghoul2,int boneNum,int *tempDependents,int maxDep);
void G2_GetBoneMatrixLow(CGhoul2Info &ghoul2,int boneNum,const vec3_t scale,mdxaBone_t &retMatrix,mdxaBone_t *&retBasepose,mdxaBone_t *&retBaseposeInv);
int G2_GetParentBoneMatrixLow(CGhoul2Info &ghoul2,int boneNum,const vec3_t scale,mdxaBone_t &retMatrix,mdxaBone_t *&retBasepose,mdxaBone_t *&retBaseposeInv);
bool G2_WasBoneRendered(CGhoul2Info &ghoul2,int boneNum);
#define MAX_BONES_RAG (256)
struct SRagEffector
{
vec3_t currentOrigin;
vec3_t desiredDirection;
vec3_t desiredOrigin;
float radius;
float weight;
};
#define RAG_MASK (CONTENTS_SOLID|CONTENTS_TERRAIN)//|CONTENTS_SHOTCLIP|CONTENTS_TERRAIN//(/*MASK_SOLID|*/CONTENTS_SOLID|CONTENTS_PLAYERCLIP|CONTENTS_SHOTCLIP|CONTENTS_TERRAIN|CONTENTS_BODY)
extern cvar_t *broadsword;
extern cvar_t *broadsword_kickbones;
extern cvar_t *broadsword_kickorigin;
extern cvar_t *broadsword_dontstopanim;
extern cvar_t *broadsword_waitforshot;
extern cvar_t *broadsword_playflop;
extern cvar_t *broadsword_effcorr;
extern cvar_t *broadsword_ragtobase;
extern cvar_t *broadsword_dircap;
extern cvar_t *broadsword_extra1;
extern cvar_t *broadsword_extra2;
#define RAG_PCJ (0x00001)
#define RAG_PCJ_POST_MULT (0x00002) // has the pcj flag as well
#define RAG_PCJ_MODEL_ROOT (0x00004) // has the pcj flag as well
#define RAG_PCJ_PELVIS (0x00008) // has the pcj flag and POST_MULT as well
#define RAG_EFFECTOR (0x00100)
#define RAG_WAS_NOT_RENDERED (0x01000) // not particularily reliable, more of a hint
#define RAG_WAS_EVER_RENDERED (0x02000) // not particularily reliable, more of a hint
#define RAG_BONE_LIGHTWEIGHT (0x04000) //used to indicate a bone's velocity treatment
#define RAG_PCJ_IK_CONTROLLED (0x08000) //controlled from IK move input
#define RAG_UNSNAPPABLE (0x10000) //cannot be broken out of constraints ever
// thiese flags are on the model and correspond to...
//#define GHOUL2_RESERVED_FOR_RAGDOLL 0x0ff0 // these are not defined here for dependecies sake
#define GHOUL2_RAG_STARTED 0x0010 // we are actually a ragdoll
#define GHOUL2_RAG_PENDING 0x0100 // got start death anim but not end death anim
#define GHOUL2_RAG_DONE 0x0200 // got end death anim
#define GHOUL2_RAG_COLLISION_DURING_DEATH 0x0400 // ever have gotten a collision (da) event
#define GHOUL2_RAG_COLLISION_SLIDE 0x0800 // ever have gotten a collision (slide) event
#define GHOUL2_RAG_FORCESOLVE 0x1000 //api-override, determine if ragdoll should be forced to continue solving even if it thinks it is settled
//#define flrand Q_flrand
static mdxaBone_t* ragBasepose[MAX_BONES_RAG];
static mdxaBone_t* ragBaseposeInv[MAX_BONES_RAG];
static mdxaBone_t ragBones[MAX_BONES_RAG];
static SRagEffector ragEffectors[MAX_BONES_RAG];
static boneInfo_t *ragBoneData[MAX_BONES_RAG];
static int tempDependents[MAX_BONES_RAG];
static int ragBlistIndex[MAX_BONES_RAG];
static int numRags;
static vec3_t ragBoneMins;
static vec3_t ragBoneMaxs;
static vec3_t ragBoneCM;
static bool haveDesiredPelvisOffset=false;
static vec3_t desiredPelvisOffset; // this is for the root
static float ragOriginChange=0.0f;
static vec3_t ragOriginChangeDir;
//debug
#if 0
static vec3_t handPos={0,0,0};
static vec3_t handPos2={0,0,0};
#endif
enum ERagState
{
ERS_DYNAMIC,
ERS_SETTLING,
ERS_SETTLED
};
static int ragState;
static std::vector<boneInfo_t *> rag; // once we get the dependents precomputed this can be local
static void G2_Generate_MatrixRag(
// caution this must not be called before the whole skeleton is "remembered"
boneInfo_v &blist,
int index)
{
boneInfo_t &bone=blist[index];//.sent;
memcpy(&bone.matrix,&bone.ragOverrideMatrix, sizeof(mdxaBone_t));
#ifdef _DEBUG
int i,j;
for (i = 0; i < 3; i++ )
{
for (j = 0; j < 4; j++ )
{
assert( !Q_isnan(bone.matrix.matrix[i][j]));
}
}
#endif// _DEBUG
memcpy(&blist[index].newMatrix,&bone.matrix, sizeof(mdxaBone_t));
}
int G2_Find_Bone_Rag(CGhoul2Info *ghlInfo, boneInfo_v &blist, const char *boneName)
{
mdxaSkel_t *skel;
mdxaSkelOffsets_t *offsets;
offsets = (mdxaSkelOffsets_t *)((byte *)ghlInfo->aHeader + sizeof(mdxaHeader_t));
skel = (mdxaSkel_t *)((byte *)ghlInfo->aHeader + sizeof(mdxaHeader_t) + offsets->offsets[0]);
/*
model_t *currentModel;
model_t *animModel;
mdxaHeader_t *aHeader;
currentModel = R_GetModelByHandle(RE_RegisterModel(ghlInfo->mFileName));
assert(currentModel);
animModel = R_GetModelByHandle(currentModel->mdxm->animIndex);
assert(animModel);
aHeader = animModel->mdxa;
assert(aHeader);
offsets = (mdxaSkelOffsets_t *)((byte *)aHeader + sizeof(mdxaHeader_t));
skel = (mdxaSkel_t *)((byte *)aHeader + sizeof(mdxaHeader_t) + offsets->offsets[0]);
*/
// look through entire list
for(size_t i=0; i<blist.size(); i++)
{
// if this bone entry has no info in it, bounce over it
if (blist[i].boneNumber == -1)
{
continue;
}
// figure out what skeletal info structure this bone entry is looking at
skel = (mdxaSkel_t *)((byte *)ghlInfo->aHeader + sizeof(mdxaHeader_t) + offsets->offsets[blist[i].boneNumber]);
//skel = (mdxaSkel_t *)((byte *)aHeader + sizeof(mdxaHeader_t) + offsets->offsets[blist[i].boneNumber]);
// if name is the same, we found it
if (!Q_stricmp(skel->name, boneName))
{
return i;
}
}
#if _DEBUG
// G2_Bone_Not_Found(boneName,ghlInfo->mFileName);
#endif
// didn't find it
return -1;
}
static int G2_Set_Bone_Rag(const mdxaHeader_t *mod_a,
boneInfo_v &blist,
const char *boneName,
CGhoul2Info &ghoul2,
const vec3_t scale,
const vec3_t origin)
{
// do not change the state of the skeleton here
int index = G2_Find_Bone_Rag(&ghoul2, blist, boneName);
if (index == -1)
{
index = G2_Add_Bone(ghoul2.animModel, blist, boneName);
}
if (index != -1)
{
boneInfo_t &bone=blist[index];
VectorCopy(origin,bone.extraVec1);
G2_GetBoneMatrixLow(ghoul2,bone.boneNumber,scale,bone.originalTrueBoneMatrix,bone.basepose,bone.baseposeInv);
// bone.parentRawBoneIndex=G2_GetParentBoneMatrixLow(ghoul2,bone.boneNumber,scale,bone.parentTrueBoneMatrix,bone.baseposeParent,bone.baseposeInvParent);
assert( !Q_isnan(bone.originalTrueBoneMatrix.matrix[1][1]));
assert( !Q_isnan(bone.originalTrueBoneMatrix.matrix[1][3]));
bone.originalOrigin[0]=bone.originalTrueBoneMatrix.matrix[0][3];
bone.originalOrigin[1]=bone.originalTrueBoneMatrix.matrix[1][3];
bone.originalOrigin[2]=bone.originalTrueBoneMatrix.matrix[2][3];
}
return index;
}
static int G2_Set_Bone_Angles_Rag(
CGhoul2Info &ghoul2,
const mdxaHeader_t *mod_a,
boneInfo_v &blist,
const char *boneName,
const int flags,
const float radius,
const vec3_t angleMin=0,
const vec3_t angleMax=0,
const int blendTime=500)
{
int index = G2_Find_Bone_Rag(&ghoul2, blist, boneName);
if (index == -1)
{
index = G2_Add_Bone(ghoul2.animModel, blist, boneName);
}
if (index != -1)
{
boneInfo_t &bone=blist[index];
bone.flags &= ~(BONE_ANGLES_TOTAL);
bone.flags |= BONE_ANGLES_RAGDOLL;
if (flags&RAG_PCJ)
{
if (flags&RAG_PCJ_POST_MULT)
{
bone.flags |= BONE_ANGLES_POSTMULT;
}
else if (flags&RAG_PCJ_MODEL_ROOT)
{
bone.flags |= BONE_ANGLES_PREMULT;
// bone.flags |= BONE_ANGLES_POSTMULT;
}
else
{
assert(!"Invalid RAG PCJ\n");
}
}
bone.ragStartTime=G2API_GetTime(0);
bone.boneBlendStart = bone.ragStartTime;
bone.boneBlendTime = blendTime;
bone.radius=radius;
bone.weight=1.0f;
//init the others to valid values
bone.epGravFactor = 0;
VectorClear(bone.epVelocity);
bone.solidCount = 0;
bone.physicsSettled = false;
bone.snapped = false;
bone.parentBoneIndex = -1;
bone.offsetRotation = 0.0f;
bone.overGradSpeed = 0.0f;
VectorClear(bone.overGoalSpot);
bone.hasOverGoal = false;
bone.hasAnimFrameMatrix = -1;
// bone.weight=pow(radius,1.7f); //cubed was too harsh
// bone.weight=radius*radius*radius;
if (angleMin&&angleMax)
{
VectorCopy(angleMin,bone.minAngles);
VectorCopy(angleMax,bone.maxAngles);
}
else
{
VectorCopy(bone.currentAngles,bone.minAngles); // I guess this isn't a rag pcj then
VectorCopy(bone.currentAngles,bone.maxAngles);
}
if (!bone.lastTimeUpdated)
{
static mdxaBone_t id =
{
{
{ 1.0f, 0.0f, 0.0f, 0.0f },
{ 0.0f, 1.0f, 0.0f, 0.0f },
{ 0.0f, 0.0f, 1.0f, 0.0f }
}
};
memcpy(&bone.ragOverrideMatrix,&id, sizeof(mdxaBone_t));
VectorClear(bone.anglesOffset);
VectorClear(bone.positionOffset);
VectorClear(bone.velocityEffector); // this is actually a velocity now
VectorClear(bone.velocityRoot); // this is actually a velocity now
VectorClear(bone.lastPosition);
VectorClear(bone.lastShotDir);
bone.lastContents=0;
// if this is non-zero, we are in a dynamic state
bone.firstCollisionTime=bone.ragStartTime;
// if this is non-zero, we are in a settling state
bone.restTime=0;
// if they are both zero, we are in a settled state
bone.firstTime=0;
bone.RagFlags=flags;
bone.DependentRagIndexMask=0;
G2_Generate_MatrixRag(blist,index); // set everything to th id
#if 0
VectorClear(bone.currentAngles);
// VectorAdd(bone.minAngles,bone.maxAngles,bone.currentAngles);
// VectorScale(bone.currentAngles,0.5f,bone.currentAngles);
#else
{
if (
(flags&RAG_PCJ_MODEL_ROOT) ||
(flags&RAG_PCJ_PELVIS) ||
!(flags&RAG_PCJ))
{
VectorClear(bone.currentAngles);
}
else
{
int k;
for (k=0;k<3;k++)
{
float scalar=flrand(-1.0f,1.0f);
scalar*=flrand(-1.0f,1.0f)*flrand(-1.0f,1.0f);
// this is a heavily central distribution
// center it on .5 (and make it small)
scalar*=0.5f;
scalar+=0.5f;
bone.currentAngles[k]=(bone.minAngles[k]-bone.maxAngles[k])*scalar+bone.maxAngles[k];
}
}
}
// VectorClear(bone.currentAngles);
#endif
VectorCopy(bone.currentAngles,bone.lastAngles);
}
}
return index;
}
class CRagDollParams;
const mdxaHeader_t *G2_GetModA(CGhoul2Info &ghoul2);
static void G2_RagDollMatchPosition()
{
haveDesiredPelvisOffset=false;
int i;
for (i=0;i<numRags;i++)
{
boneInfo_t &bone=*ragBoneData[i];
SRagEffector &e=ragEffectors[i];
vec3_t &desiredPos=e.desiredOrigin; // we will save this
if (0&&(bone.RagFlags & RAG_PCJ_PELVIS))
{
// just move to quake origin
VectorCopy(bone.originalOrigin,desiredPos);
VectorSubtract(desiredPos,e.currentOrigin,desiredPelvisOffset); // last arg is dest
haveDesiredPelvisOffset=true;
VectorCopy(e.currentOrigin,bone.lastPosition); // last arg is dest
continue;
}
if (!(bone.RagFlags & RAG_EFFECTOR))
{
continue;
}
VectorCopy(bone.originalOrigin,desiredPos);
VectorSubtract(desiredPos,e.currentOrigin,e.desiredDirection);
VectorCopy(e.currentOrigin,bone.lastPosition); // last arg is dest
}
}
qboolean G2_Set_Bone_Anim_No_BS(CGhoul2Info &ghoul2, const mdxaHeader_t *mod,boneInfo_v &blist, const char *boneName, const int argStartFrame,
const int argEndFrame, const int flags, const float animSpeed, const int currentTime, const float setFrame,
const int blendTime, const int creationID, bool resetBonemap=true)
{
int index = G2_Find_Bone_Rag(&ghoul2, blist, boneName);
int modFlags = flags;
int startFrame=argStartFrame;
int endFrame=argEndFrame;
if (index != -1)
{
blist[index].blendFrame = blist[index].blendLerpFrame = 0;
blist[index].blendTime = blist[index].blendStart = 0;
modFlags &= ~(BONE_ANIM_BLEND);
blist[index].endFrame = endFrame;
blist[index].startFrame = startFrame;
blist[index].animSpeed = animSpeed;
blist[index].pauseTime = 0;
// blist[index].boneMap = NULL;
// blist[index].lastTime = blist[index].startTime = (currentTime - (((setFrame - (float)startFrame) * 50.0)/ animSpeed));
blist[index].flags &= ~(BONE_ANIM_TOTAL);
blist[index].flags |= modFlags;
return qtrue;
}
index = G2_Add_Bone(ghoul2.animModel, blist, boneName);
if (index != -1)
{
blist[index].blendFrame = blist[index].blendLerpFrame = 0;
blist[index].blendTime = 0;
modFlags &= ~(BONE_ANIM_BLEND);
blist[index].endFrame = endFrame;
blist[index].startFrame = startFrame;
blist[index].animSpeed = animSpeed;
blist[index].pauseTime = 0;
// blist[index].boneMap = NULL;
// blist[index].lastTime = blist[index].startTime = (currentTime - (((setFrame - (float)startFrame) * 50.0f)/ animSpeed));
blist[index].flags &= ~(BONE_ANIM_TOTAL);
blist[index].flags |= modFlags;
return qtrue;
}
assert(0);
return qfalse;
}
void G2_ResetRagDoll(CGhoul2Info_v &ghoul2V)
{
int model;
for (model = 0; model < ghoul2V.size(); model++)
{
if (ghoul2V[model].mModelindex != -1)
{
break;
}
}
if (model == ghoul2V.size())
{
return;
}
CGhoul2Info &ghoul2 = ghoul2V[model];
if (!(ghoul2.mFlags & GHOUL2_RAG_STARTED))
{ //no use in doing anything if we aren't ragging
return;
}
boneInfo_v &blist = ghoul2.mBlist;
#if 1
//Eh, screw it. Ragdoll does a lot of terrible things to the bones that probably aren't directly reversible, so just reset it all.
G2_Init_Bone_List(blist, ghoul2.aHeader->numBones);
#else //The anims on every bone are messed up too, as are the angles. There's not really any way to get back to a normal state, so just clear the list
//and let them re-set their anims/angles gameside.
int i = 0;
while (i < blist.size())
{
boneInfo_t &bone = blist[i];
if (bone.boneNumber != -1 && (bone.flags & BONE_ANGLES_RAGDOLL))
{
bone.flags &= ~BONE_ANGLES_RAGDOLL;
bone.flags &= ~BONE_ANGLES_IK;
bone.RagFlags = 0;
bone.lastTimeUpdated = 0;
VectorClear(bone.currentAngles);
bone.ragStartTime = 0;
}
i++;
}
#endif
ghoul2.mFlags &= ~(GHOUL2_RAG_PENDING|GHOUL2_RAG_DONE|GHOUL2_RAG_STARTED);
}
void G2_SetRagDoll(CGhoul2Info_v &ghoul2V,CRagDollParams *parms)
{
if (parms)
{
parms->CallRagDollBegin=qfalse;
}
if (!broadsword||!broadsword->integer||!parms)
{
return;
}
int model;
for (model = 0; model < ghoul2V.size(); model++)
{
if (ghoul2V[model].mModelindex != -1)
{
break;
}
}
if (model==ghoul2V.size())
{
return;
}
CGhoul2Info &ghoul2=ghoul2V[model];
const mdxaHeader_t *mod_a=G2_GetModA(ghoul2);
if (!mod_a)
{
return;
}
int curTime=G2API_GetTime(0);
boneInfo_v &blist = ghoul2.mBlist;
if (ghoul2.mFlags&GHOUL2_RAG_STARTED)
{
// only going to begin ragdoll once, everything else depends on what happens to the origin
return;
}
#if 0
if (index>=0)
{
OutputDebugString(va("death %d %d\n",blist[index].startFrame,blist[index].endFrame));
}
#endif
ghoul2.mFlags|=GHOUL2_RAG_PENDING|GHOUL2_RAG_DONE|GHOUL2_RAG_STARTED; // well anyway we are going live
parms->CallRagDollBegin=qtrue;
G2_GenerateWorldMatrix(parms->angles, parms->position);
G2_ConstructGhoulSkeleton(ghoul2V, curTime, false, parms->scale);
G2_Set_Bone_Rag(mod_a,blist,"model_root",ghoul2,parms->scale,parms->position);
G2_Set_Bone_Rag(mod_a,blist,"pelvis",ghoul2,parms->scale,parms->position);
G2_Set_Bone_Rag(mod_a,blist,"lower_lumbar",ghoul2,parms->scale,parms->position);
G2_Set_Bone_Rag(mod_a,blist,"upper_lumbar",ghoul2,parms->scale,parms->position);
G2_Set_Bone_Rag(mod_a,blist,"thoracic",ghoul2,parms->scale,parms->position);
G2_Set_Bone_Rag(mod_a,blist,"cranium",ghoul2,parms->scale,parms->position);
G2_Set_Bone_Rag(mod_a,blist,"rhumerus",ghoul2,parms->scale,parms->position);
G2_Set_Bone_Rag(mod_a,blist,"lhumerus",ghoul2,parms->scale,parms->position);
G2_Set_Bone_Rag(mod_a,blist,"rradius",ghoul2,parms->scale,parms->position);
G2_Set_Bone_Rag(mod_a,blist,"lradius",ghoul2,parms->scale,parms->position);
G2_Set_Bone_Rag(mod_a,blist,"rfemurYZ",ghoul2,parms->scale,parms->position);
G2_Set_Bone_Rag(mod_a,blist,"lfemurYZ",ghoul2,parms->scale,parms->position);
G2_Set_Bone_Rag(mod_a,blist,"rtibia",ghoul2,parms->scale,parms->position);
G2_Set_Bone_Rag(mod_a,blist,"ltibia",ghoul2,parms->scale,parms->position);
G2_Set_Bone_Rag(mod_a,blist,"rhand",ghoul2,parms->scale,parms->position);
G2_Set_Bone_Rag(mod_a,blist,"lhand",ghoul2,parms->scale,parms->position);
//G2_Set_Bone_Rag(mod_a,blist,"rtarsal",ghoul2,parms->scale,parms->position);
//G2_Set_Bone_Rag(mod_a,blist,"ltarsal",ghoul2,parms->scale,parms->position);
G2_Set_Bone_Rag(mod_a,blist,"rtalus",ghoul2,parms->scale,parms->position);
G2_Set_Bone_Rag(mod_a,blist,"ltalus",ghoul2,parms->scale,parms->position);
G2_Set_Bone_Rag(mod_a,blist,"rradiusX",ghoul2,parms->scale,parms->position);
G2_Set_Bone_Rag(mod_a,blist,"lradiusX",ghoul2,parms->scale,parms->position);
G2_Set_Bone_Rag(mod_a,blist,"rfemurX",ghoul2,parms->scale,parms->position);
G2_Set_Bone_Rag(mod_a,blist,"lfemurX",ghoul2,parms->scale,parms->position);
G2_Set_Bone_Rag(mod_a,blist,"ceyebrow",ghoul2,parms->scale,parms->position);
//int startFrame = 3665, endFrame = 3665+1;
int startFrame = parms->startFrame, endFrame = parms->endFrame;
G2_Set_Bone_Anim_No_BS(ghoul2, mod_a,blist,"upper_lumbar",startFrame,endFrame-1,
BONE_ANIM_OVERRIDE_FREEZE|BONE_ANIM_BLEND,
1.0f,curTime,float(startFrame),150,0,true);
G2_Set_Bone_Anim_No_BS(ghoul2, mod_a,blist,"lower_lumbar",startFrame,endFrame-1,
BONE_ANIM_OVERRIDE_FREEZE|BONE_ANIM_BLEND,
1.0f,curTime,float(startFrame),150,0,true);
G2_Set_Bone_Anim_No_BS(ghoul2, mod_a,blist,"Motion",startFrame,endFrame-1,
BONE_ANIM_OVERRIDE_FREEZE|BONE_ANIM_BLEND,
1.0f,curTime,float(startFrame),150,0,true);
// G2_Set_Bone_Anim_No_BS(ghoul2, mod_a,blist,"model_root",startFrame,endFrame-1,
// BONE_ANIM_OVERRIDE_FREEZE|BONE_ANIM_BLEND,
// 1.0f,curTime,float(startFrame),150,0,true);
G2_Set_Bone_Anim_No_BS(ghoul2, mod_a,blist,"lfemurYZ",startFrame,endFrame-1,
BONE_ANIM_OVERRIDE_FREEZE|BONE_ANIM_BLEND,
1.0f,curTime,float(startFrame),150,0,true);
G2_Set_Bone_Anim_No_BS(ghoul2, mod_a,blist,"rfemurYZ",startFrame,endFrame-1,
BONE_ANIM_OVERRIDE_FREEZE|BONE_ANIM_BLEND,
1.0f,curTime,float(startFrame),150,0,true);
G2_Set_Bone_Anim_No_BS(ghoul2, mod_a,blist,"rhumerus",startFrame,endFrame-1,
BONE_ANIM_OVERRIDE_FREEZE|BONE_ANIM_BLEND,
1.0f,curTime,float(startFrame),150,0,true);
G2_Set_Bone_Anim_No_BS(ghoul2, mod_a,blist,"lhumerus",startFrame,endFrame-1,
BONE_ANIM_OVERRIDE_FREEZE|BONE_ANIM_BLEND,
1.0f,curTime,float(startFrame),150,0,true);
// should already be set G2_GenerateWorldMatrix(parms->angles, parms->position);
G2_ConstructGhoulSkeleton(ghoul2V, curTime, false, parms->scale);
static const float fRadScale = 0.3f;//0.5f;
vec3_t pcjMin,pcjMax;
VectorSet(pcjMin,-90.0f,-45.0f,-45.0f);
VectorSet(pcjMax,90.0f,45.0f,45.0f);
G2_Set_Bone_Angles_Rag(ghoul2, mod_a,blist,"model_root",RAG_PCJ_MODEL_ROOT|RAG_PCJ|RAG_UNSNAPPABLE,10.0f*fRadScale,pcjMin,pcjMax,100);
VectorSet(pcjMin,-45.0f,-45.0f,-45.0f);
VectorSet(pcjMax,45.0f,45.0f,45.0f);
G2_Set_Bone_Angles_Rag(ghoul2, mod_a,blist,"pelvis",RAG_PCJ_PELVIS|RAG_PCJ|RAG_PCJ_POST_MULT|RAG_UNSNAPPABLE,10.0f*fRadScale,pcjMin,pcjMax,100);
#if 1
// new base anim, unconscious flop
int pcjflags=RAG_PCJ|RAG_PCJ_POST_MULT;//|RAG_EFFECTOR;
VectorSet(pcjMin,-15.0f,-15.0f,-15.0f);
VectorSet(pcjMax,15.0f,15.0f,15.0f);
G2_Set_Bone_Angles_Rag(ghoul2, mod_a,blist,"lower_lumbar",pcjflags|RAG_UNSNAPPABLE,10.0f*fRadScale,pcjMin,pcjMax,500);
G2_Set_Bone_Angles_Rag(ghoul2, mod_a,blist,"upper_lumbar",pcjflags|RAG_UNSNAPPABLE,10.0f*fRadScale,pcjMin,pcjMax,500);
VectorSet(pcjMin,-25.0f,-25.0f,-25.0f);
VectorSet(pcjMax,25.0f,25.0f,25.0f);
G2_Set_Bone_Angles_Rag(ghoul2, mod_a,blist,"thoracic",pcjflags|RAG_EFFECTOR|RAG_UNSNAPPABLE,12.0f*fRadScale,pcjMin,pcjMax,500);
VectorSet(pcjMin,-10.0f,-10.0f,-90.0f);
VectorSet(pcjMax,10.0f,10.0f,90.0f);
G2_Set_Bone_Angles_Rag(ghoul2, mod_a,blist,"cranium",pcjflags|RAG_BONE_LIGHTWEIGHT|RAG_UNSNAPPABLE,6.0f*fRadScale,pcjMin,pcjMax,500);
static const float sFactLeg = 1.0f;
static const float sFactArm = 1.0f;
static const float sRadArm = 1.0f;
static const float sRadLeg = 1.0f;
VectorSet(pcjMin,-100.0f,-40.0f,-15.0f);
VectorSet(pcjMax,-15.0f,80.0f,15.0f);
VectorScale(pcjMin, sFactArm, pcjMin);
VectorScale(pcjMax, sFactArm, pcjMax);
G2_Set_Bone_Angles_Rag(ghoul2, mod_a,blist,"rhumerus",pcjflags|RAG_BONE_LIGHTWEIGHT|RAG_UNSNAPPABLE,(4.0f*sRadArm)*fRadScale,pcjMin,pcjMax,500);
VectorSet(pcjMin,-50.0f,-80.0f,-15.0f);
VectorSet(pcjMax,15.0f,40.0f,15.0f);
VectorScale(pcjMin, sFactArm, pcjMin);
VectorScale(pcjMax, sFactArm, pcjMax);
G2_Set_Bone_Angles_Rag(ghoul2, mod_a,blist,"lhumerus",pcjflags|RAG_BONE_LIGHTWEIGHT|RAG_UNSNAPPABLE,(4.0f*sRadArm)*fRadScale,pcjMin,pcjMax,500);
VectorSet(pcjMin,-25.0f,-20.0f,-20.0f);
VectorSet(pcjMax,90.0f,20.0f,-20.0f);
VectorScale(pcjMin, sFactArm, pcjMin);
VectorScale(pcjMax, sFactArm, pcjMax);
G2_Set_Bone_Angles_Rag(ghoul2, mod_a,blist,"rradius",pcjflags|RAG_BONE_LIGHTWEIGHT,(3.0f*sRadArm)*fRadScale,pcjMin,pcjMax,500);
VectorSet(pcjMin,-90.0f,-20.0f,-20.0f);
VectorSet(pcjMax,30.0f,20.0f,-20.0f);
VectorScale(pcjMin, sFactArm, pcjMin);
VectorScale(pcjMax, sFactArm, pcjMax);
G2_Set_Bone_Angles_Rag(ghoul2, mod_a,blist,"lradius",pcjflags|RAG_BONE_LIGHTWEIGHT,(3.0f*sRadArm)*fRadScale,pcjMin,pcjMax,500);
VectorSet(pcjMin,-80.0f,-50.0f,-20.0f);
VectorSet(pcjMax,30.0f,5.0f,20.0f);
VectorScale(pcjMin, sFactLeg, pcjMin);
VectorScale(pcjMax, sFactLeg, pcjMax);
G2_Set_Bone_Angles_Rag(ghoul2, mod_a,blist,"rfemurYZ",pcjflags|RAG_BONE_LIGHTWEIGHT,(6.0f*sRadLeg)*fRadScale,pcjMin,pcjMax,500);
VectorSet(pcjMin,-60.0f,-5.0f,-20.0f);
VectorSet(pcjMax,50.0f,50.0f,20.0f);
VectorScale(pcjMin, sFactLeg, pcjMin);
VectorScale(pcjMax, sFactLeg, pcjMax);
G2_Set_Bone_Angles_Rag(ghoul2, mod_a,blist,"lfemurYZ",pcjflags|RAG_BONE_LIGHTWEIGHT,(6.0f*sRadLeg)*fRadScale,pcjMin,pcjMax,500);
VectorSet(pcjMin,-20.0f,-15.0f,-15.0f);
VectorSet(pcjMax,100.0f,15.0f,15.0f);
VectorScale(pcjMin, sFactLeg, pcjMin);
VectorScale(pcjMax, sFactLeg, pcjMax);
G2_Set_Bone_Angles_Rag(ghoul2, mod_a,blist,"rtibia",pcjflags|RAG_EFFECTOR|RAG_BONE_LIGHTWEIGHT,(4.0f*sRadLeg)*fRadScale,pcjMin,pcjMax,500);
VectorSet(pcjMin,20.0f,-15.0f,-15.0f);
VectorSet(pcjMax,100.0f,15.0f,15.0f);
VectorScale(pcjMin, sFactLeg, pcjMin);
VectorScale(pcjMax, sFactLeg, pcjMax);
G2_Set_Bone_Angles_Rag(ghoul2, mod_a,blist,"ltibia",pcjflags|RAG_EFFECTOR|RAG_BONE_LIGHTWEIGHT,(4.0f*sRadLeg)*fRadScale,pcjMin,pcjMax,500);
#else
// old base anim
int pcjflags=RAG_PCJ|RAG_PCJ_POST_MULT|RAG_EFFECTOR;
VectorSet(pcjMin,-15.0f,-15.0f,-15.0f);
VectorSet(pcjMax,45.0f,15.0f,15.0f);
G2_Set_Bone_Angles_Rag(ghoul2, mod_a,blist,"lower_lumbar",pcjflags,10.0f,pcjMin,pcjMax,500);
G2_Set_Bone_Angles_Rag(ghoul2, mod_a,blist,"upper_lumbar",pcjflags,10.0f,pcjMin,pcjMax,500);
VectorSet(pcjMin,-45.0f,-45.0f,-45.0f);
VectorSet(pcjMax,45.0f,45.0f,45.0f);
G2_Set_Bone_Angles_Rag(ghoul2, mod_a,blist,"thoracic",pcjflags,10.0f,pcjMin,pcjMax,500);
VectorSet(pcjMin,-10.0f,-10.0f,-90.0f);
VectorSet(pcjMax,10.0f,10.0f,90.0f);
G2_Set_Bone_Angles_Rag(ghoul2, mod_a,blist,"cranium",pcjflags|RAG_BONE_LIGHTWEIGHT,6.0f,pcjMin,pcjMax,500);
//VectorSet(pcjMin,-45.0f,-90.0f,-100.0f);
VectorSet(pcjMin,-180.0f,-180.0f,-100.0f);
//VectorSet(pcjMax,60.0f,60.0f,45.0f);
VectorSet(pcjMax,180.0f,180.0f,45.0f);
G2_Set_Bone_Angles_Rag(ghoul2, mod_a,blist,"rhumerus",pcjflags|RAG_BONE_LIGHTWEIGHT,4.0f,pcjMin,pcjMax,500);
//VectorSet(pcjMin,-45.0f,-60.0f,-45.0f);
VectorSet(pcjMin,-180.0f,-180.0f,-100.0f);
//VectorSet(pcjMax,60.0f,90.0f,100.0f);
VectorSet(pcjMax,180.0f,180.0f,100.0f);
G2_Set_Bone_Angles_Rag(ghoul2, mod_a,blist,"lhumerus",pcjflags|RAG_BONE_LIGHTWEIGHT,4.0f,pcjMin,pcjMax,500);
//-120/120
VectorSet(pcjMin,-120.0f,-20.0f,-20.0f);
VectorSet(pcjMax,50.0f,20.0f,-20.0f);
G2_Set_Bone_Angles_Rag(ghoul2, mod_a,blist,"rradius",pcjflags|RAG_BONE_LIGHTWEIGHT,3.0f,pcjMin,pcjMax,500);
VectorSet(pcjMin,-120.0f,-20.0f,-20.0f);
VectorSet(pcjMax,5.0f,20.0f,-20.0f);
G2_Set_Bone_Angles_Rag(ghoul2, mod_a,blist,"lradius",pcjflags|RAG_BONE_LIGHTWEIGHT,3.0f,pcjMin,pcjMax,500);
VectorSet(pcjMin,-90.0f,-50.0f,-20.0f);
VectorSet(pcjMax,50.0f,20.0f,20.0f);
G2_Set_Bone_Angles_Rag(ghoul2, mod_a,blist,"rfemurYZ",pcjflags|RAG_BONE_LIGHTWEIGHT,6.0f,pcjMin,pcjMax,500);
VectorSet(pcjMin,-90.0f,-20.0f,-20.0f);
VectorSet(pcjMax,50.0f,50.0f,20.0f);
G2_Set_Bone_Angles_Rag(ghoul2, mod_a,blist,"lfemurYZ",pcjflags|RAG_BONE_LIGHTWEIGHT,6.0f,pcjMin,pcjMax,500);
//120
VectorSet(pcjMin,-20.0f,-15.0f,-15.0f);
VectorSet(pcjMax,120.0f,15.0f,15.0f);
G2_Set_Bone_Angles_Rag(ghoul2, mod_a,blist,"rtibia",pcjflags|RAG_EFFECTOR|RAG_BONE_LIGHTWEIGHT,4.0f,pcjMin,pcjMax,500);
VectorSet(pcjMin,20.0f,-15.0f,-15.0f);
VectorSet(pcjMax,120.0f,15.0f,15.0f);
G2_Set_Bone_Angles_Rag(ghoul2, mod_a,blist,"ltibia",pcjflags|RAG_EFFECTOR|RAG_BONE_LIGHTWEIGHT,4.0f,pcjMin,pcjMax,500);
#endif
float sRadEArm = 1.2f;
float sRadELeg = 1.2f;
// int rhand=
G2_Set_Bone_Angles_Rag(ghoul2, mod_a,blist,"rhand",RAG_EFFECTOR|RAG_BONE_LIGHTWEIGHT,(6.0f*sRadEArm)*fRadScale);
G2_Set_Bone_Angles_Rag(ghoul2, mod_a,blist,"lhand",RAG_EFFECTOR|RAG_BONE_LIGHTWEIGHT,(6.0f*sRadEArm)*fRadScale);
//G2_Set_Bone_Angles_Rag(ghoul2, mod_a,blist,"rtarsal",RAG_EFFECTOR|RAG_BONE_LIGHTWEIGHT,(4.0f*sRadELeg)*fRadScale);
//G2_Set_Bone_Angles_Rag(ghoul2, mod_a,blist,"ltarsal",RAG_EFFECTOR|RAG_BONE_LIGHTWEIGHT,(4.0f*sRadELeg)*fRadScale);
// G2_Set_Bone_Angles_Rag(ghoul2, mod_a,blist,"rtibia",RAG_EFFECTOR|RAG_BONE_LIGHTWEIGHT,(4.0f*sRadELeg)*fRadScale);
// G2_Set_Bone_Angles_Rag(ghoul2, mod_a,blist,"ltibia",RAG_EFFECTOR|RAG_BONE_LIGHTWEIGHT,(4.0f*sRadELeg)*fRadScale);
G2_Set_Bone_Angles_Rag(ghoul2, mod_a,blist,"rtalus",RAG_EFFECTOR|RAG_BONE_LIGHTWEIGHT,(4.0f*sRadELeg)*fRadScale);
G2_Set_Bone_Angles_Rag(ghoul2, mod_a,blist,"ltalus",RAG_EFFECTOR|RAG_BONE_LIGHTWEIGHT,(4.0f*sRadELeg)*fRadScale);
G2_Set_Bone_Angles_Rag(ghoul2, mod_a,blist,"rradiusX",RAG_EFFECTOR|RAG_BONE_LIGHTWEIGHT,(6.0f*sRadEArm)*fRadScale);
G2_Set_Bone_Angles_Rag(ghoul2, mod_a,blist,"lradiusX",RAG_EFFECTOR|RAG_BONE_LIGHTWEIGHT,(6.0f*sRadEArm)*fRadScale);
G2_Set_Bone_Angles_Rag(ghoul2, mod_a,blist,"rfemurX",RAG_EFFECTOR|RAG_BONE_LIGHTWEIGHT,(10.0f*sRadELeg)*fRadScale);
G2_Set_Bone_Angles_Rag(ghoul2, mod_a,blist,"lfemurX",RAG_EFFECTOR|RAG_BONE_LIGHTWEIGHT,(10.0f*sRadELeg)*fRadScale);
//G2_Set_Bone_Angles_Rag(ghoul2, mod_a,blist,"ceyebrow",RAG_EFFECTOR|RAG_BONE_LIGHTWEIGHT,10.0f*fRadScale);
G2_Set_Bone_Angles_Rag(ghoul2, mod_a,blist,"ceyebrow",RAG_EFFECTOR|RAG_BONE_LIGHTWEIGHT,5.0f);
//match the currrent animation
if (!G2_RagDollSetup(ghoul2,curTime,true,parms->position,false))
{
assert(!"failed to add any rag bones");
return;
}
G2_RagDollCurrentPosition(ghoul2V,model,curTime,parms->angles,parms->position,parms->scale);
#if 0
if (rhand>0)
{
boneInfo_t &bone=blist[rhand];
SRagEffector &e=ragEffectors[bone.ragIndex];
VectorCopy(bone.originalOrigin,handPos);
VectorCopy(e.currentOrigin,handPos2);
}
#endif
int k;
CRagDollUpdateParams fparms;
VectorCopy(parms->position, fparms.position);
VectorCopy(parms->angles, fparms.angles);
VectorCopy(parms->scale, fparms.scale);
VectorClear(fparms.velocity);
fparms.me = parms->me;
fparms.settleFrame = parms->endFrame;
//Guess I don't need to do this, do I?
G2_ConstructGhoulSkeleton(ghoul2V, curTime, false, parms->scale);
vec3_t dPos;
VectorCopy(parms->position, dPos);
#ifdef _OLD_STYLE_SETTLE
dPos[2] -= 6;
#endif
for (k=0;k</*10*/20;k++)
{
G2_RagDollSettlePositionNumeroTrois(ghoul2V,dPos,&fparms,curTime);
G2_RagDollCurrentPosition(ghoul2V,model,curTime,parms->angles,dPos,parms->scale);
G2_RagDollMatchPosition();
G2_RagDollSolve(ghoul2V,model,1.0f*(1.0f-k/40.0f),curTime,dPos,false);
}
}
void G2_SetRagDollBullet(CGhoul2Info &ghoul2,const vec3_t rayStart,const vec3_t hit)
{
if (!broadsword||!broadsword->integer)
{
return;
}
vec3_t shotDir;
VectorSubtract(hit,rayStart,shotDir);
float len=VectorLength(shotDir);
if (len<1.0f)
{
return;
}
float lenr=1.0f/len;
shotDir[0]*=lenr;
shotDir[1]*=lenr;
shotDir[2]*=lenr;
bool firstOne=false;
if (broadsword_kickbones&&broadsword_kickbones->integer)
{
int i;
int magicFactor13=150.0f; // squared radius multiplier for shot effects
boneInfo_v &blist = ghoul2.mBlist;
for(i=blist.size()-1;i>=0;i--)
{
boneInfo_t &bone=blist[i];
if ((bone.flags & BONE_ANGLES_TOTAL))
{
if (bone.flags & BONE_ANGLES_RAGDOLL)
{
if (!firstOne)
{
firstOne=true;
#if 0
int curTime=G2API_GetTime(0);
const mdxaHeader_t *mod_a=G2_GetModA(ghoul2);
int startFrame = 0, endFrame = 0;
#if 1
TheGhoul2Wraith()->GetAnimFrames(ghoul2.mID, "unconsciousdeadflop01", startFrame, endFrame);
if (startFrame == -1 && endFrame == -1)
{ //A bad thing happened! Just use the hardcoded numbers even though they could be wrong.
startFrame = 3573;
endFrame = 3583;
assert(0);
}
G2_Set_Bone_Anim_No_BS(mod_a,blist,"upper_lumbar",startFrame,endFrame-1,
BONE_ANIM_OVERRIDE_FREEZE|BONE_ANIM_BLEND,
1.0f,curTime,float(startFrame),75,0,true);
G2_Set_Bone_Anim_No_BS(mod_a,blist,"lfemurYZ",startFrame,endFrame-1,
BONE_ANIM_OVERRIDE_FREEZE|BONE_ANIM_BLEND,
1.0f,curTime,float(startFrame),75,0,true);
G2_Set_Bone_Anim_No_BS(mod_a,blist,"rfemurYZ",startFrame,endFrame-1,
BONE_ANIM_OVERRIDE_FREEZE|BONE_ANIM_BLEND,
1.0f,curTime,float(startFrame),75,0,true);
#else
TheGhoul2Wraith()->GetAnimFrames(ghoul2.mID, "backdeadflop01", startFrame, endFrame);
if (startFrame == -1 && endFrame == -1)
{ //A bad thing happened! Just use the hardcoded numbers even though they could be wrong.
startFrame = 3581;
endFrame = 3592;
assert(0);
}
G2_Set_Bone_Anim_No_BS(mod_a,blist,"upper_lumbar",endFrame,startFrame+1,
BONE_ANIM_OVERRIDE_FREEZE,
-1.0f,curTime,float(endFrame-1),50,0,true);
G2_Set_Bone_Anim_No_BS(mod_a,blist,"lfemurYZ",endFrame,startFrame+1,
BONE_ANIM_OVERRIDE_FREEZE,
-1.0f,curTime,float(endFrame-1),50,0,true);
G2_Set_Bone_Anim_No_BS(mod_a,blist,"rfemurYZ",endFrame,startFrame+1,
BONE_ANIM_OVERRIDE_FREEZE,
-1.0f,curTime,float(endFrame-1),50,0,true);
#endif
#endif
}
VectorCopy(shotDir,bone.lastShotDir);
vec3_t dir;
VectorSubtract(bone.lastPosition,hit,dir);
len=VectorLength(dir);
if (len<1.0f)
{
len=1.0f;
}
lenr=1.0f/len;
float effect=lenr;
effect*=magicFactor13*effect; // this is cubed, one of them is absorbed by the next calc
bone.velocityEffector[0]=shotDir[0]*(effect+flrand(0.0f,0.05f));
bone.velocityEffector[1]=shotDir[1]*(effect+flrand(0.0f,0.05f));
bone.velocityEffector[2]=fabs(shotDir[2])*(effect+flrand(0.0f,0.05f));
// bone.velocityEffector[0]=shotDir[0]*(effect+flrand(0.0f,0.05f))*flrand(-0.1f,3.0f);
// bone.velocityEffector[1]=shotDir[1]*(effect+flrand(0.0f,0.05f))*flrand(-0.1f,3.0f);
// bone.velocityEffector[2]=fabs(shotDir[2])*(effect+flrand(0.0f,0.05f))*flrand(-0.1f,3.0f);
assert( !Q_isnan(shotDir[2]));
// bone.currentAngles[0]+=flrand(-10.0f*lenr,10.0f*lenr);
// bone.currentAngles[1]+=flrand(-10.0f*lenr,10.0f*lenr);
// bone.currentAngles[2]+=flrand(-10.0f*lenr,10.0f*lenr);
// bone.lastAngles[0]+=flrand(-10.0f*lenr,10.0f*lenr);
// bone.lastAngles[1]+=flrand(-10.0f*lenr,10.0f*lenr);
// bone.lastAngles[2]+=flrand(-10.0f*lenr,10.0f*lenr);
// go dynamic
bone.firstCollisionTime=G2API_GetTime(0);
// bone.firstCollisionTime=0;
bone.restTime=0;
}
}
}
}
}
static float G2_RagSetState(CGhoul2Info &ghoul2, boneInfo_t &bone,int frameNum,const vec3_t origin,bool &resetOrigin)
{
ragOriginChange=DistanceSquared(origin,bone.extraVec1);
VectorSubtract(origin,bone.extraVec1,ragOriginChangeDir);
float decay=1.0f;
int dynamicTime=1000;
int settleTime=1000;
if (ghoul2.mFlags & GHOUL2_RAG_FORCESOLVE)
{
ragState=ERS_DYNAMIC;
if (frameNum>bone.firstCollisionTime+dynamicTime)
{
VectorCopy(origin,bone.extraVec1);
if (ragOriginChange>15.0f)
{ //if we moved, or if this bone is still in solid
bone.firstCollisionTime=frameNum;
}
else
{
// settle out
bone.firstCollisionTime=0;
bone.restTime=frameNum;
ragState=ERS_SETTLING;
}
}
}
else if (bone.firstCollisionTime>0)
{
ragState=ERS_DYNAMIC;
if (frameNum>bone.firstCollisionTime+dynamicTime)
{
VectorCopy(origin,bone.extraVec1);
if (ragOriginChange>15.0f)
{ //if we moved, or if this bone is still in solid
bone.firstCollisionTime=frameNum;
}
else
{
// settle out
bone.firstCollisionTime=0;
bone.restTime=frameNum;
ragState=ERS_SETTLING;
}
}
//decay=0.0f;
}
else if (bone.restTime>0)
{
decay=1.0f-(frameNum-bone.restTime)/float(dynamicTime);
if (decay<0.0f)
{
decay=0.0f;
}
if (decay>1.0f)
{
decay=1.0f;
}
float magicFactor8=1.0f; // Power for decay
decay=pow(decay,magicFactor8);
ragState=ERS_SETTLING;
if (frameNum>bone.restTime+settleTime)
{
VectorCopy(origin,bone.extraVec1);
if (ragOriginChange>15.0f)
{
bone.restTime=frameNum;
}
else
{
// stop
bone.restTime=0;
ragState=ERS_SETTLED;
}
}
//decay=0.0f;
}
else
{
if (bone.RagFlags & RAG_PCJ_IK_CONTROLLED)
{
bone.firstCollisionTime=frameNum;
ragState=ERS_DYNAMIC;
}
else if (ragOriginChange>15.0f)
{
bone.firstCollisionTime=frameNum;
ragState=ERS_DYNAMIC;
}
else
{
ragState=ERS_SETTLED;
}
decay=0.0f;
}
// ragState=ERS_SETTLED;
// decay=0.0f;
return decay;
}
static bool G2_RagDollSetup(CGhoul2Info &ghoul2,int frameNum,bool resetOrigin,const vec3_t origin,bool anyRendered)
{
int minSurvivingBone=10000;
//int minSurvivingBoneAt=-1;
int minSurvivingBoneAlt=10000;
//int minSurvivingBoneAtAlt=-1;
assert(ghoul2.mFileName[0]);
boneInfo_v &blist = ghoul2.mBlist;
rag.clear();
int numRendered=0;
int numNotRendered=0;
//int pelvisAt=-1;
for(size_t i=0; i<blist.size(); i++)
{
boneInfo_t &bone=blist[i];
if (bone.boneNumber>=0)
{
assert(bone.boneNumber<MAX_BONES_RAG);
if ((bone.flags & BONE_ANGLES_RAGDOLL) || (bone.flags & BONE_ANGLES_IK))
{
//rww - this was (!anyRendered) before. Isn't that wrong? (if anyRendered, then wasRendered should be true)
bool wasRendered=
(!anyRendered) || // offscreeen or something
G2_WasBoneRendered(ghoul2,bone.boneNumber);
if (!wasRendered)
{
bone.RagFlags|=RAG_WAS_NOT_RENDERED;
numNotRendered++;
}
else
{
bone.RagFlags&=~RAG_WAS_NOT_RENDERED;
bone.RagFlags|=RAG_WAS_EVER_RENDERED;
numRendered++;
}
if (bone.RagFlags&RAG_PCJ_PELVIS)
{
//pelvisAt=i;
}
else if (bone.RagFlags&RAG_PCJ_MODEL_ROOT)
{
}
else if (wasRendered && (bone.RagFlags&RAG_PCJ))
{
if (minSurvivingBone>bone.boneNumber)
{
minSurvivingBone=bone.boneNumber;
//minSurvivingBoneAt=i;
}
}
else if (wasRendered)
{
if (minSurvivingBoneAlt>bone.boneNumber)
{
minSurvivingBoneAlt=bone.boneNumber;
//minSurvivingBoneAtAlt=i;
}
}
if (
anyRendered &&
(bone.RagFlags&RAG_WAS_EVER_RENDERED) &&
!(bone.RagFlags&RAG_PCJ_MODEL_ROOT) &&
!(bone.RagFlags&RAG_PCJ_PELVIS) &&
!wasRendered &&
(bone.RagFlags&RAG_EFFECTOR)
)
{
// this thing was rendered in the past, but wasn't now, although other bones were, lets get rid of it
// bone.flags &= ~BONE_ANGLES_RAGDOLL;
// bone.RagFlags = 0;
//OutputDebugString(va("Deleted Effector %d\n",i));
// continue;
}
if ((int)rag.size()<bone.boneNumber+1)
{
rag.resize(bone.boneNumber+1,0);
}
rag[bone.boneNumber]=&bone;
ragBlistIndex[bone.boneNumber]=i;
bone.lastTimeUpdated=frameNum;
if (resetOrigin)
{
VectorCopy(origin,bone.extraVec1); // this is only done incase a limb is removed
}
}
}
}
#if 0
if (numRendered<5) // I think this is a limb
{
//OutputDebugString(va("limb %3d/%3d (r,N).\n",numRendered,numNotRendered));
if (minSurvivingBoneAt<0)
{
// pelvis is gone, but we have no remaining pcj's
// just find any remain rag effector
minSurvivingBoneAt=minSurvivingBoneAtAlt;
}
if (
minSurvivingBoneAt>=0 &&
pelvisAt>=0)
{
{
// remove the pelvis as a rag
boneInfo_t &bone=blist[minSurvivingBoneAt];
bone.flags&=~BONE_ANGLES_RAGDOLL;
bone.RagFlags=0;
}
{
// the root-est bone is now our "pelvis
boneInfo_t &bone=blist[minSurvivingBoneAt];
VectorSet(bone.minAngles,-14500.0f,-14500.0f,-14500.0f);
VectorSet(bone.maxAngles,14500.0f,14500.0f,14500.0f);
bone.RagFlags|=RAG_PCJ_PELVIS|RAG_PCJ; // this guy is our new "pelvis"
bone.flags |= BONE_ANGLES_POSTMULT;
bone.ragStartTime=G2API_GetTime(0);
}
}
}
#endif
numRags=0;
//int ragStartTime=0;
for(size_t i=0; i<rag.size(); i++)
{
if (rag[i])
{
boneInfo_t &bone=*rag[i];
assert(bone.boneNumber>=0);
assert(numRags<MAX_BONES_RAG);
//ragStartTime=bone.ragStartTime;
bone.ragIndex=numRags;
ragBoneData[numRags]=&bone;
ragEffectors[numRags].radius=bone.radius;
ragEffectors[numRags].weight=bone.weight;
G2_GetBoneBasepose(ghoul2,bone.boneNumber,bone.basepose,bone.baseposeInv);
numRags++;
}
}
if (!numRags)
{
return false;
}
return true;
}
static void G2_RagDoll(CGhoul2Info_v &ghoul2V,int g2Index,CRagDollUpdateParams *params, int curTime)
{
if (!broadsword||!broadsword->integer)
{
return;
}
if (!params)
{
assert(0);
return;
}
vec3_t dPos;
VectorCopy(params->position, dPos);
#ifdef _OLD_STYLE_SETTLE
dPos[2] -= 6;
#endif
// params->DebugLine(handPos,handPos2,false);
int frameNum=G2API_GetTime(0);
CGhoul2Info &ghoul2=ghoul2V[g2Index];
assert(ghoul2.mFileName[0]);
boneInfo_v &blist = ghoul2.mBlist;
// hack for freezing ragdoll (no idea if it works)
#if 0
if (0)
{
// we gotta hack this to basically freeze the timers
for(i=0; i<blist.size(); i++)
{
boneInfo_t &bone=blist[i];
if (bone.boneNumber>=0)
{
assert(bone.boneNumber<MAX_BONES_RAG);
if (bone.flags & BONE_ANGLES_RAGDOLL)
{
bone.ragStartTime+=50;
if (bone.firstTime)
{
bone.firstTime+=50;
}
if (bone.firstCollisionTime)
{
bone.firstCollisionTime+=50;
}
if (bone.restTime)
{
bone.restTime+=50;
}
}
}
}
return;
}
#endif
float decay=1.0f;
bool resetOrigin=false;
bool anyRendered=false;
// this loop checks for settled
for(size_t i=0; i<blist.size(); i++)
{
boneInfo_t &bone=blist[i];
if (bone.boneNumber>=0)
{
assert(bone.boneNumber<MAX_BONES_RAG);
if (bone.flags & BONE_ANGLES_RAGDOLL)
{
// check for state transitions
decay=G2_RagSetState(ghoul2,bone,frameNum,dPos,resetOrigin); // set the current state
if (ragState==ERS_SETTLED)
{
#if 0
bool noneInSolid = true;
//first iterate through and make sure no bones are still in solid a lot
for(int j = 0; j < blist.size(); j++)
{
boneInfo_t &bone2 = blist[j];
if (bone2.boneNumber >= 0 && bone2.solidCount > 8)
{
noneInSolid = false;
break;
}
}
if (noneInSolid)
{ //we're settled then
params->RagDollSettled();
return;
}
else
{
continue;
}
#else
params->RagDollSettled();
return;
#endif
}
if (G2_WasBoneRendered(ghoul2,bone.boneNumber))
{
anyRendered=true;
break;
}
}
}
}
//int iters=(ragState==ERS_DYNAMIC)?2:1;
int iters=(ragState==ERS_DYNAMIC)?4:2;
//bool kicked=false;
if (ragOriginChangeDir[2]<-100.0f)
{
//kicked=true;
//iters*=8;
iters*=2; //rww - changed to this.. it was getting up to around 600 traces at times before (which is insane)
}
if (iters)
{
if (!G2_RagDollSetup(ghoul2,frameNum,resetOrigin,dPos,anyRendered))
{
return;
}
// ok, now our data structures are compact and set up in topological order
for (int i=0;i<iters;i++)
{
G2_RagDollCurrentPosition(ghoul2V,g2Index,frameNum,params->angles,dPos,params->scale);
if (G2_RagDollSettlePositionNumeroTrois(ghoul2V,dPos,params,curTime))
{
#if 0
//effectors are start solid alot, so this was pretty extreme
if (!kicked&&iters<4)
{
kicked=true;
//iters*=4;
iters*=2;
}
#endif
}
//params->position[2] += 16;
G2_RagDollSolve(ghoul2V,g2Index,decay*2.0f,frameNum,dPos,true,params);
}
}
if (params->me != ENTITYNUM_NONE)
{
#if 0
vec3_t worldMins,worldMaxs;
worldMins[0]=params->position[0]-17;
worldMins[1]=params->position[1]-17;
worldMins[2]=params->position[2];
worldMaxs[0]=params->position[0]+17;
worldMaxs[1]=params->position[1]+17;
worldMaxs[2]=params->position[2];
//OutputDebugString(va("%f \n",worldMins[2]));
// params->DebugLine(worldMins,worldMaxs,true);
#endif
G2_RagDollCurrentPosition(ghoul2V,g2Index,frameNum,params->angles,params->position,params->scale);
// SV_UnlinkEntity(params->me);
// params->me->SetMins(BB_SHOOTING_SIZE,ragBoneMins);
// params->me->SetMaxs(BB_SHOOTING_SIZE,ragBoneMaxs);
// SV_LinkEntity(params->me);
}
}
#ifdef _DEBUG
#define _DEBUG_BONE_NAMES
#endif
#ifdef _DEBUG_BONE_NAMES
static inline char *G2_Get_Bone_Name(CGhoul2Info *ghlInfo, boneInfo_v &blist, int boneNum)
{
mdxaSkel_t *skel;
mdxaSkelOffsets_t *offsets;
offsets = (mdxaSkelOffsets_t *)((byte *)ghlInfo->aHeader + sizeof(mdxaHeader_t));
skel = (mdxaSkel_t *)((byte *)ghlInfo->aHeader + sizeof(mdxaHeader_t) + offsets->offsets[0]);
// look through entire list
for(size_t i=0; i<blist.size(); i++)
{
// if this bone entry has no info in it, bounce over it
if (blist[i].boneNumber != boneNum)
{
continue;
}
// figure out what skeletal info structure this bone entry is looking at
skel = (mdxaSkel_t *)((byte *)ghlInfo->aHeader + sizeof(mdxaHeader_t) + offsets->offsets[blist[i].boneNumber]);
return skel->name;
}
// didn't find it
return "BONE_NOT_FOUND";
}
#endif
char *G2_GetBoneNameFromSkel(CGhoul2Info &ghoul2, int boneNum);
static void G2_RagDollCurrentPosition(CGhoul2Info_v &ghoul2V,int g2Index,int frameNum,const vec3_t angles,const vec3_t position,const vec3_t scale)
{
CGhoul2Info &ghoul2=ghoul2V[g2Index];
assert(ghoul2.mFileName[0]);
//OutputDebugString(va("angles %f %f %f\n",angles[0],angles[1],angles[2]));
G2_GenerateWorldMatrix(angles,position);
G2_ConstructGhoulSkeleton(ghoul2V, frameNum, false, scale);
float totalWt=0.0f;
int i;
for (i=0;i<numRags;i++)
{
boneInfo_t &bone=*ragBoneData[i];
G2_GetBoneMatrixLow(ghoul2,bone.boneNumber,scale,ragBones[i],ragBasepose[i],ragBaseposeInv[i]);
#ifdef _DEBUG_BONE_NAMES
char *debugBoneName = G2_Get_Bone_Name(&ghoul2, ghoul2.mBlist, bone.boneNumber);
assert(debugBoneName);
#endif
int k;
//float cmweight=ragEffectors[numRags].weight;
float cmweight=1.0f;
for (k=0;k<3;k++)
{
ragEffectors[i].currentOrigin[k]=ragBones[i].matrix[k][3];
assert( !Q_isnan(ragEffectors[i].currentOrigin[k]));
if (!i)
{
// set mins, maxs and cm
ragBoneCM[k]=ragEffectors[i].currentOrigin[k]*cmweight;
ragBoneMaxs[k]=ragEffectors[i].currentOrigin[k];
ragBoneMins[k]=ragEffectors[i].currentOrigin[k];
}
else
{
ragBoneCM[k]+=ragEffectors[i].currentOrigin[k]*ragEffectors[i].weight;
if (ragEffectors[i].currentOrigin[k]>ragBoneMaxs[k])
{
ragBoneMaxs[k]=ragEffectors[i].currentOrigin[k];
}
if (ragEffectors[i].currentOrigin[k]<ragBoneMins[k])
{
ragBoneMins[k]=ragEffectors[i].currentOrigin[k];
}
}
}
totalWt+=cmweight;
}
assert(totalWt>0.0f);
int k;
{
float wtInv=1.0f/totalWt;
for (k=0;k<3;k++)
{
ragBoneMaxs[k]-=position[k];
ragBoneMins[k]-=position[k];
ragBoneMaxs[k]+=10.0f;
ragBoneMins[k]-=10.0f;
ragBoneCM[k]*=wtInv;
ragBoneCM[k]=ragEffectors[0].currentOrigin[k]; // use the pelvis
}
}
}
#ifdef _DEBUG
int ragTraceTime = 0;
int ragSSCount = 0;
int ragTraceCount = 0;
#endif
void Rag_Trace( trace_t *results, const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, const int passEntityNum, const int contentmask, const EG2_Collision eG2TraceType, const int useLod )
{
#ifdef _DEBUG
int ragPreTrace = ri.Milliseconds();
#endif
{
results->entityNum = ENTITYNUM_NONE;
//SV_Trace(results, start, mins, maxs, end, passEntityNum, contentmask, eG2TraceType, useLod);
ri.CM_BoxTrace(results, start, end, mins, maxs, 0, contentmask, 0);
results->entityNum = results->fraction != 1.0 ? ENTITYNUM_WORLD : ENTITYNUM_NONE;
}
#ifdef _DEBUG
int ragPostTrace = ri.Milliseconds();
ragTraceTime += (ragPostTrace - ragPreTrace);
if (results->startsolid)
{
ragSSCount++;
}
ragTraceCount++;
#endif
}
//run advanced physics on each bone indivudually
//an adaption of my "exphys" custom game physics model
#define MAX_GRAVITY_PULL 256//512
static inline bool G2_BoneOnGround(const vec3_t org, const vec3_t mins, const vec3_t maxs, const int ignoreNum)
{
trace_t tr;
vec3_t gSpot;
VectorCopy(org, gSpot);
gSpot[2] -= 1.0f; //seems reasonable to me
Rag_Trace(&tr, org, mins, maxs, gSpot, ignoreNum, RAG_MASK, G2_NOCOLLIDE, 0);
if (tr.fraction != 1.0f && !tr.startsolid && !tr.allsolid)
{ //not in solid, and hit something. Guess it's ground.
return true;
}
return false;
}
static inline bool G2_ApplyRealBonePhysics(boneInfo_t &bone, SRagEffector &e, CRagDollUpdateParams *params, vec3_t goalSpot, const vec3_t testMins, const vec3_t testMaxs,
const float gravity, const float mass, const float bounce)
{
trace_t tr;
vec3_t projectedOrigin;
vec3_t vNorm;
vec3_t ground;
float velScaling = 0.1f;
float vTotal = 0.0f;
bool boneOnGround = false;
assert(mass <= 1.0f && mass >= 0.01f);
if (bone.physicsSettled)
{ //then we have no need to continue
return true;
}
if (gravity)
{ //factor it in before we do anything.
VectorCopy(e.currentOrigin, ground);
ground[2] -= 1.0f;
Rag_Trace(&tr, e.currentOrigin, testMins, testMaxs, ground, params->me, RAG_MASK, G2_NOCOLLIDE, 0);
if (tr.entityNum == ENTITYNUM_NONE)
{
boneOnGround = false;
}
else
{
boneOnGround = true;
}
if (!boneOnGround)
{
if (!params->velocity[2])
{ //only increase gravitational pull once the actual entity is still
bone.epGravFactor += gravity;
}
if (bone.epGravFactor > MAX_GRAVITY_PULL)
{ //cap it off if needed
bone.epGravFactor = MAX_GRAVITY_PULL;
}
bone.epVelocity[2] -= bone.epGravFactor;
}
else
{ //if we're sitting on something then reset the gravity factor.
bone.epGravFactor = 0;
}
}
else
{
boneOnGround = G2_BoneOnGround(e.currentOrigin, testMins, testMaxs, params->me);
}
if (!bone.epVelocity[0] && !bone.epVelocity[1] && !bone.epVelocity[2])
{ //nothing to do if we have no velocity even after gravity.
VectorCopy(e.currentOrigin, goalSpot);
return true;
}
//get the projected origin based on velocity.
VectorMA(e.currentOrigin, velScaling, bone.epVelocity, projectedOrigin);
//scale it down based on mass
VectorScale(bone.epVelocity, 1.0f-mass, bone.epVelocity);
VectorCopy(bone.epVelocity, vNorm);
vTotal = VectorNormalize(vNorm);
if (vTotal < 1 && boneOnGround)
{ //we've pretty much stopped moving anyway, just clear it out then.
VectorClear(bone.epVelocity);
bone.epGravFactor = 0;
VectorCopy(e.currentOrigin, goalSpot);
return true;
}
Rag_Trace(&tr, e.currentOrigin, testMins, testMaxs, projectedOrigin, params->me, RAG_MASK, G2_NOCOLLIDE, 0);
if (tr.startsolid || tr.allsolid)
{ //can't go anywhere from here
return false;
}
//Go ahead and set it to the trace endpoint regardless of what it hit
VectorCopy(tr.endpos, goalSpot);
if (tr.fraction == 1.0f)
{ //Nothing was in the way.
return true;
}
if (bounce)
{
vTotal *= bounce; //scale it by bounce
VectorScale(tr.plane.normal, vTotal, vNorm); //scale the trace plane normal by the bounce factor
if (vNorm[2] > 0)
{
bone.epGravFactor -= vNorm[2]*(1.0f-mass); //The lighter it is the more gravity will be reduced by bouncing vertically.
if (bone.epGravFactor < 0)
{
bone.epGravFactor = 0;
}
}
VectorAdd(bone.epVelocity, vNorm, bone.epVelocity); //add it into the existing velocity.
//I suppose it could be sort of neat to make a game callback here to actual do stuff
//when bones slam into things. But it could be slow too.
/*
if (tr.entityNum != ENTITYNUM_NONE && ent->touch)
{ //then call the touch function
ent->touch(ent, &g_entities[tr.entityNum], &tr);
}
*/
}
else
{ //if no bounce, kill when it hits something.
bone.epVelocity[0] = 0;
bone.epVelocity[1] = 0;
if (!gravity)
{
bone.epVelocity[2] = 0;
}
}
return true;
}
#ifdef _DEBUG_BONE_NAMES
static inline void G2_RagDebugBox(vec3_t mins, vec3_t maxs, int duration)
{
return;
}
static inline void G2_RagDebugLine(vec3_t start, vec3_t end, int time, int color, int radius)
{
return;
}
#endif
#ifdef _OLD_STYLE_SETTLE
static bool G2_RagDollSettlePositionNumeroTrois(CGhoul2Info_v &ghoul2V, const vec3_t currentOrg, CRagDollUpdateParams *params, int curTime)
{
haveDesiredPelvisOffset=false;
vec3_t desiredPos;
int i;
assert(params);
//assert(params->me); //no longer valid, because me is an index!
int ignoreNum=params->me;
bool anyStartSolid=false;
vec3_t groundSpot={0,0,0};
// lets find the floor at our quake origin
{
vec3_t testStart;
VectorCopy(currentOrg,testStart); //last arg is dest
vec3_t testEnd;
VectorCopy(testStart,testEnd); //last arg is dest
testEnd[2]-=200.0f;
vec3_t testMins;
vec3_t testMaxs;
VectorSet(testMins,-10,-10,-10);
VectorSet(testMaxs,10,10,10);
{
trace_t tr;
assert( !Q_isnan(testStart[1]));
assert( !Q_isnan(testEnd[1]));
assert( !Q_isnan(testMins[1]));
assert( !Q_isnan(testMaxs[1]));
Rag_Trace(&tr,testStart,testMins,testMaxs,testEnd,ignoreNum,RAG_MASK,G2_NOCOLLIDE,0/*SV_TRACE_NO_PLAYER*/);
if (tr.entityNum==0)
{
VectorAdvance(testStart,.5f,testEnd,tr.endpos);
}
if (tr.startsolid)
{
//hmmm, punt
VectorCopy(currentOrg,groundSpot); //last arg is dest
groundSpot[2]-=30.0f;
}
else
{
VectorCopy(tr.endpos,groundSpot); //last arg is dest
}
}
}
for (i=0;i<numRags;i++)
{
boneInfo_t &bone=*ragBoneData[i];
SRagEffector &e=ragEffectors[i];
if (bone.RagFlags & RAG_PCJ_PELVIS)
{
// just move to quake origin
VectorCopy(currentOrg,desiredPos);
//desiredPos[2]-=35.0f;
desiredPos[2]-=20.0f;
//old deathflop desiredPos[2]-=40.0f;
VectorSubtract(desiredPos,e.currentOrigin,desiredPelvisOffset); // last arg is dest
haveDesiredPelvisOffset=true;
VectorCopy(e.currentOrigin,bone.lastPosition); // last arg is dest
continue;
}
if (!(bone.RagFlags & RAG_EFFECTOR))
{
continue;
}
vec3_t testMins;
vec3_t testMaxs;
VectorSet(testMins,-e.radius,-e.radius,-e.radius);
VectorSet(testMaxs,e.radius,e.radius,e.radius);
// first we will see if we are start solid
// if so, we are gonna run some bonus iterations
bool iAmStartSolid=false;
vec3_t testStart;
VectorCopy(e.currentOrigin,testStart); //last arg is dest
testStart[2]+=12.0f; // we are no so concerned with minor penetration
vec3_t testEnd;
VectorCopy(testStart,testEnd); //last arg is dest
testEnd[2]-=8.0f;
assert( !Q_isnan(testStart[1]));
assert( !Q_isnan(testEnd[1]));
assert( !Q_isnan(testMins[1]));
assert( !Q_isnan(testMaxs[1]));
float vertEffectorTraceFraction=0.0f;
{
trace_t tr;
Rag_Trace(&tr,testStart,testMins,testMaxs,testEnd,ignoreNum,RAG_MASK,G2_NOCOLLIDE,0);
if (tr.entityNum==0)
{
VectorAdvance(testStart,.5f,testEnd,tr.endpos);
}
if (tr.startsolid)
{
// above the origin, so lets try lower
if (e.currentOrigin[2] > groundSpot[2])
{
testStart[2]=groundSpot[2]+(e.radius-10.0f);
}
else
{
// lets try higher
testStart[2]=groundSpot[2]+8.0f;
Rag_Trace(&tr,testStart,testMins,testMaxs,testEnd,ignoreNum,RAG_MASK,G2_NOCOLLIDE,0);
if (tr.entityNum==0)
{
VectorAdvance(testStart,.5f,testEnd,tr.endpos);
}
}
}
if (tr.startsolid)
{
iAmStartSolid=true;
anyStartSolid=true;
// above the origin, so lets slide away
if (e.currentOrigin[2] > groundSpot[2])
{
if (params)
{
//SRagDollEffectorCollision args(e.currentOrigin,tr);
//params->EffectorCollision(args);
}
}
else
{
//harumph, we are really screwed
}
}
else
{
vertEffectorTraceFraction=tr.fraction;
if (params &&
vertEffectorTraceFraction < .95f &&
fabsf(tr.plane.normal[2]) < .707f)
{
//SRagDollEffectorCollision args(e.currentOrigin,tr);
//args.useTracePlane=true;
//params->EffectorCollision(args);
}
}
}
vec3_t effectorGroundSpot;
VectorAdvance(testStart,vertEffectorTraceFraction,testEnd,effectorGroundSpot);// VA(a,t,b,c)-> c := (1-t)a+tb
// trace from the quake origin horzontally to the effector
// gonna choose the maximum of the ground spot or the effector location
// and clamp it to be roughly in the bbox
VectorCopy(groundSpot,testStart); //last arg is dest
if (iAmStartSolid)
{
// we don't have a meaningful ground spot
VectorCopy(e.currentOrigin,testEnd); //last arg is dest
bone.solidCount++;
}
else
{
VectorCopy(effectorGroundSpot,testEnd); //last arg is dest
bone.solidCount = 0;
}
assert( !Q_isnan(testStart[1]));
assert( !Q_isnan(testEnd[1]));
assert( !Q_isnan(testMins[1]));
assert( !Q_isnan(testMaxs[1]));
float ztest;
if (testEnd[2]>testStart[2])
{
ztest=testEnd[2];
}
else
{
ztest=testStart[2];
}
if (ztest<currentOrg[2]-30.0f)
{
ztest=currentOrg[2]-30.0f;
}
if (ztest<currentOrg[2]+10.0f)
{
ztest=currentOrg[2]+10.0f;
}
testStart[2]=ztest;
testEnd[2]=ztest;
float magicFactor44=1.0f; // going to trace a bit longer, this also serves as an expansion parameter
VectorAdvance(testStart,magicFactor44,testEnd,testEnd);// VA(a,t,b,c)-> c := (1-t)a+tb
float horzontalTraceFraction=0.0f;
vec3_t HorizontalHitSpot={0,0,0};
{
trace_t tr;
Rag_Trace(&tr,testStart,testMins,testMaxs,testEnd,ignoreNum,RAG_MASK,G2_NOCOLLIDE,0);
if (tr.entityNum==0)
{
VectorAdvance(testStart,.5f,testEnd,tr.endpos);
}
horzontalTraceFraction=tr.fraction;
if (tr.startsolid)
{
horzontalTraceFraction=1.0f;
// punt
VectorCopy(e.currentOrigin,HorizontalHitSpot);
}
else
{
VectorCopy(tr.endpos,HorizontalHitSpot);
int magicFactor46=0.98f; // shorten percetage to make sure we can go down along a wall
//float magicFactor46=0.98f; // shorten percetage to make sure we can go down along a wall
//rww - An..int?
VectorAdvance(tr.endpos,magicFactor46,testStart,HorizontalHitSpot);// VA(a,t,b,c)-> c := (1-t)a+tb
// roughly speaking this is a wall
if (horzontalTraceFraction<0.9f)
{
// roughly speaking this is a wall
if (fabsf(tr.plane.normal[2])<0.7f)
{
//SRagDollEffectorCollision args(e.currentOrigin,tr);
//args.useTracePlane=true;
//params->EffectorCollision(args);
}
}
else if (!iAmStartSolid &&
effectorGroundSpot[2] < groundSpot[2] - 8.0f)
{
// this is a situation where we have something dangling below the pelvis, we want to find the plane going downhill away from the origin
// for various reasons, without this correction the body will actually move away from places it can fall off.
//gotta run the trace backwards to get a plane
{
trace_t tr;
VectorCopy(effectorGroundSpot,testStart);
VectorCopy(groundSpot,testEnd);
// this can be a line trace, we just want the plane normal
Rag_Trace(&tr,testEnd,0,0,testStart,ignoreNum,RAG_MASK,G2_NOCOLLIDE,0);
if (tr.entityNum==0)
{
VectorAdvance(testStart,.5f,testEnd,tr.endpos);
}
horzontalTraceFraction=tr.fraction;
if (!tr.startsolid && tr.fraction< 0.7f)
{
//SRagDollEffectorCollision args(e.currentOrigin,tr);
//args.useTracePlane=true;
//params->EffectorCollision(args);
}
}
}
}
}
vec3_t goalSpot={0,0,0};
// now lets trace down
VectorCopy(HorizontalHitSpot,testStart);
VectorCopy(testStart,testEnd); //last arg is dest
testEnd[2]=e.currentOrigin[2]-30.0f;
{
trace_t tr;
Rag_Trace(&tr,testStart,NULL,NULL,testEnd,ignoreNum,RAG_MASK,G2_NOCOLLIDE,0);
if (tr.entityNum==0)
{
VectorAdvance(testStart,.5f,testEnd,tr.endpos);
}
if (tr.startsolid)
{
// punt, go to the origin I guess
VectorCopy(currentOrg,goalSpot);
}
else
{
VectorCopy(tr.endpos,goalSpot);
int magicFactor47=0.5f; // shorten percentage to make sure we can go down along a wall
VectorAdvance(tr.endpos,magicFactor47,testStart,goalSpot);// VA(a,t,b,c)-> c := (1-t)a+tb
}
}
// ok now as the horizontal trace fraction approaches zero, we want to head toward the horizontalHitSpot
//geeze I would like some reasonable trace fractions
assert(horzontalTraceFraction>=0.0f&&horzontalTraceFraction<=1.0f);
VectorAdvance(HorizontalHitSpot,horzontalTraceFraction*horzontalTraceFraction,goalSpot,goalSpot);// VA(a,t,b,c)-> c := (1-t)a+tb
#if 0
if ((bone.RagFlags & RAG_EFFECTOR) && (bone.RagFlags & RAG_BONE_LIGHTWEIGHT))
{ //new rule - don't even bother unless it's a lightweight effector
//rww - Factor object velocity into the final desired spot..
//We want the limbs with a "light" weight to drag behind the general mass.
//If we got here, we shouldn't be the pelvis or the root, so we should be
//fine to treat as lightweight. However, we can flag bones as being particularly
//light. They're given less downscale for the reduction factor.
vec3_t givenVelocity;
vec3_t vSpot;
trace_t vtr;
float vSpeed = 0;
float verticalSpeed = 0;
float vReductionFactor = 0.03f;
float verticalSpeedReductionFactor = 0.06f; //want this to be more obvious
float lwVReductionFactor = 0.1f;
float lwVerticalSpeedReductionFactor = 0.3f; //want this to be more obvious
VectorCopy(params->velocity, givenVelocity);
vSpeed = VectorNormalize(givenVelocity);
vSpeed = -vSpeed; //go in the opposite direction of velocity
verticalSpeed = vSpeed;
if (bone.RagFlags & RAG_BONE_LIGHTWEIGHT)
{
vSpeed *= lwVReductionFactor;
verticalSpeed *= lwVerticalSpeedReductionFactor;
}
else
{
vSpeed *= vReductionFactor;
verticalSpeed *= verticalSpeedReductionFactor;
}
vSpot[0] = givenVelocity[0]*vSpeed;
vSpot[1] = givenVelocity[1]*vSpeed;
vSpot[2] = givenVelocity[2]*verticalSpeed;
VectorAdd(goalSpot, vSpot, vSpot);
if (vSpot[0] || vSpot[1] || vSpot[2])
{
Rag_Trace(&vtr, goalSpot, testMins, testMaxs, vSpot, ignoreNum, RAG_MASK, G2_NOCOLLIDE,0);
if (vtr.fraction == 1)
{
VectorCopy(vSpot, goalSpot);
}
}
}
#endif
int k;
int magicFactor12=0.8f; // dampening of velocity applied
int magicFactor16=10.0f; // effect multiplier of velocity applied
if (iAmStartSolid)
{
magicFactor16 = 30.0f;
}
for (k=0;k<3;k++)
{
e.desiredDirection[k]=goalSpot[k]-e.currentOrigin[k];
e.desiredDirection[k]+=magicFactor16*bone.velocityEffector[k];
e.desiredDirection[k]+=flrand(-0.75f,0.75f)*flrand(-0.75f,0.75f);
bone.velocityEffector[k]*=magicFactor12;
}
VectorCopy(e.currentOrigin,bone.lastPosition); // last arg is dest
}
return anyStartSolid;
}
#else
#if 0
static inline int G2_RagIndexForBoneNum(int boneNum)
{
for (int i = 0; i < numRags; i++)
{
// these are used for affecting the end result
if (ragBoneData[i].boneNum == boneNum)
{
return i;
}
}
return -1;
}
#endif
#ifdef _RAG_PRINT_TEST
void G2_RagPrintMatrix(mdxaBone_t *mat)
{
char x[1024];
x[0] = 0;
int n = 0;
while (n < 3)
{
int o = 0;
while (o < 4)
{
strcat(x, va("%f ", mat->matrix[n][o]));
o++;
}
n++;
}
strcat(x, "\n");
Com_Printf(x);
}
#endif
void G2_RagGetBoneBasePoseMatrixLow(CGhoul2Info &ghoul2, int boneNum, mdxaBone_t &boneMatrix, mdxaBone_t &retMatrix, vec3_t scale);
void G2_RagGetAnimMatrix(CGhoul2Info &ghoul2, const int boneNum, mdxaBone_t &matrix, const int frame);
static inline void G2_RagGetWorldAnimMatrix(CGhoul2Info &ghoul2, boneInfo_t &bone, CRagDollUpdateParams *params, mdxaBone_t &retMatrix)
{
static mdxaBone_t trueBaseMatrix, baseBoneMatrix;
//get matrix for the settleFrame to use as an ideal
G2_RagGetAnimMatrix(ghoul2, bone.boneNumber, trueBaseMatrix, params->settleFrame);
assert(bone.hasAnimFrameMatrix == params->settleFrame);
G2_RagGetBoneBasePoseMatrixLow(ghoul2, bone.boneNumber,
trueBaseMatrix, baseBoneMatrix, params->scale);
//Use params to multiply world coordinate/dir matrix into the
//bone matrix and give us a useable world position
Multiply_3x4Matrix(&retMatrix, &worldMatrix, &baseBoneMatrix);
assert(!Q_isnan(retMatrix.matrix[2][3]));
}
//get the current pelvis Z direction and the base anim matrix Z direction
//so they can be compared and used to offset -rww
void G2_GetRagBoneMatrixLow(CGhoul2Info &ghoul2, int boneNum, mdxaBone_t &retMatrix);
void G2_GetBoltMatrixLow(CGhoul2Info &ghoul2,int boltNum,const vec3_t scale,mdxaBone_t &retMatrix);
static inline void G2_RagGetPelvisLumbarOffsets(CGhoul2Info &ghoul2, CRagDollUpdateParams *params, vec3_t pos, vec3_t dir, vec3_t animPos, vec3_t animDir)
{
static mdxaBone_t final;
static mdxaBone_t x;
//static mdxaBone_t *unused1, *unused2;
//static vec3_t lumbarPos;
assert(ghoul2.animModel);
int boneIndex = G2_Find_Bone(ghoul2.animModel, ghoul2.mBlist, "pelvis");
assert(boneIndex != -1);
G2_RagGetWorldAnimMatrix(ghoul2, ghoul2.mBlist[boneIndex], params, final);
G2API_GiveMeVectorFromMatrix(&final, ORIGIN, animPos);
G2API_GiveMeVectorFromMatrix(&final, POSITIVE_X, animDir);
#if 0
//We have the anim matrix pelvis pos now, so get the normal one as well
G2_GetBoneMatrixLow(ghoul2, boneIndex, params->scale, final, unused1, unused2);
G2API_GiveMeVectorFromMatrix(&final, ORIGIN, pos);
G2API_GiveMeVectorFromMatrix(&final, POSITIVE_X, dir);
#else
//We have the anim matrix pelvis pos now, so get the normal one as well
//G2_GetRagBoneMatrixLow(ghoul2, boneIndex, x);
int bolt = G2_Add_Bolt(&ghoul2, ghoul2.mBltlist, ghoul2.mSlist, "pelvis");
G2_GetBoltMatrixLow(ghoul2, bolt, params->scale, x);
Multiply_3x4Matrix(&final, &worldMatrix, &x);
G2API_GiveMeVectorFromMatrix(&final, ORIGIN, pos);
G2API_GiveMeVectorFromMatrix(&final, POSITIVE_X, dir);
#endif
/*
//now get lumbar
boneIndex = G2_Find_Bone(ghoul2.animModel, ghoul2.mBlist, "lower_lumbar");
assert(boneIndex != -1);
G2_RagGetWorldAnimMatrix(ghoul2, ghoul2.mBlist[boneIndex], params, final);
G2API_GiveMeVectorFromMatrix(&final, ORIGIN, lumbarPos);
VectorSubtract(animPos, lumbarPos, animDir);
VectorNormalize(animDir);
//We have the anim matrix lumbar dir now, so get the normal one as well
G2_GetBoneMatrixLow(ghoul2, boneIndex, params->scale, final, unused1, unused2);
G2API_GiveMeVectorFromMatrix(&final, ORIGIN, lumbarPos);
VectorSubtract(pos, lumbarPos, dir);
VectorNormalize(dir);
*/
}
static bool G2_RagDollSettlePositionNumeroTrois(CGhoul2Info_v &ghoul2V, const vec3_t currentOrg, CRagDollUpdateParams *params, int curTime)
{ //now returns true if any bone was in solid, otherwise false
int ignoreNum = params->me;
static int i;
static vec3_t goalSpot;
static trace_t tr;
static int k;
static const float velocityDampening = 1.0f;
static const float velocityMultiplier = 60.0f;
static vec3_t testMins;
static vec3_t testMaxs;
vec3_t velDir;
static bool startSolid;
bool anySolid = false;
static mdxaBone_t worldBaseMatrix;
static vec3_t parentOrigin;
static vec3_t basePos;
static vec3_t entScale;
static bool hasDaddy;
static bool hasBasePos;
static vec3_t animPelvisDir, pelvisDir, animPelvisPos, pelvisPos;
//Maybe customize per-bone?
static const float gravity = 3.0f;
static const float mass = 0.09f;
static const float bounce = 0.0f;//1.3f;
//Bouncing and stuff unfortunately does not work too well at the moment.
//Need to keep a seperate "physics origin" or make the filthy solve stuff
//better.
bool inAir = false;
if (params->velocity[0] || params->velocity[1] || params->velocity[2])
{
inAir = true;
}
if (!params->scale[0] && !params->scale[1] && !params->scale[2])
{
VectorSet(entScale, 1.0f, 1.0f, 1.0f);
}
else
{
VectorCopy(params->scale, entScale);
}
if (broadsword_ragtobase &&
broadsword_ragtobase->integer > 1)
{
//grab the pelvis directions to offset base positions for bones
G2_RagGetPelvisLumbarOffsets(ghoul2V[0], params, pelvisPos, pelvisDir, animPelvisPos,
animPelvisDir);
//don't care about the pitch offsets
pelvisDir[2] = 0;
animPelvisDir[2] = 0;
/*
vec3_t upelvisPos, uanimPelvisPos;
vec3_t blah;
VectorCopy(pelvisPos, upelvisPos);
VectorCopy(animPelvisPos, uanimPelvisPos);
upelvisPos[2] += 64;
uanimPelvisPos[2] += 64;
VectorMA(upelvisPos, 32.0f, pelvisDir, blah);
G2_RagDebugLine(upelvisPos, blah, 50, 0x00ff00, 1);
VectorMA(uanimPelvisPos, 32.0f, animPelvisDir, blah);
G2_RagDebugLine(uanimPelvisPos, blah, 50, 0xff0000, 1);
*/
//just convert to angles now, that's all we'll ever use them for
vectoangles(pelvisDir, pelvisDir);
vectoangles(animPelvisDir, animPelvisDir);
}
for (i = 0; i < numRags; i++)
{
boneInfo_t &bone = *ragBoneData[i];
SRagEffector &e = ragEffectors[i];
if (inAir)
{
bone.airTime = curTime + 30;
}
if (bone.RagFlags & RAG_PCJ_PELVIS)
{
VectorSet(goalSpot, params->position[0], params->position[1], (params->position[2]+DEFAULT_MINS_2)+((bone.radius*entScale[2])+2));
VectorSubtract(goalSpot, e.currentOrigin, desiredPelvisOffset);
haveDesiredPelvisOffset = true;
VectorCopy(e.currentOrigin, bone.lastPosition);
continue;
}
if (!(bone.RagFlags & RAG_EFFECTOR))
{
continue;
}
if (bone.hasOverGoal)
{ //api call was made to override the goal spot
VectorCopy(bone.overGoalSpot, goalSpot);
bone.solidCount = 0;
for (k = 0; k < 3; k++)
{
e.desiredDirection[k] = (goalSpot[k] - e.currentOrigin[k]);
e.desiredDirection[k] += (velocityMultiplier * bone.velocityEffector[k]);
bone.velocityEffector[k] *= velocityDampening;
}
VectorCopy(e.currentOrigin, bone.lastPosition);
continue;
}
VectorSet(testMins, -e.radius*entScale[0], -e.radius*entScale[1], -e.radius*entScale[2]);
VectorSet(testMaxs, e.radius*entScale[0], e.radius*entScale[1], e.radius*entScale[2]);
assert(ghoul2V[0].mBoneCache);
//get the parent bone's position
hasDaddy = false;
if (bone.boneNumber)
{
assert(ghoul2V[0].animModel);
assert(ghoul2V[0].aHeader);
if (bone.parentBoneIndex == -1)
{
mdxaSkel_t *skel;
mdxaSkelOffsets_t *offsets;
int bParentIndex, bParentListIndex = -1;
offsets = (mdxaSkelOffsets_t *)((byte *)ghoul2V[0].aHeader + sizeof(mdxaHeader_t));
skel = (mdxaSkel_t *)((byte *)ghoul2V[0].aHeader + sizeof(mdxaHeader_t) + offsets->offsets[bone.boneNumber]);
bParentIndex = skel->parent;
while (bParentIndex > 0)
{ //go upward through hierarchy searching for the first parent that is a rag bone
skel = (mdxaSkel_t *)((byte *)ghoul2V[0].aHeader + sizeof(mdxaHeader_t) + offsets->offsets[bParentIndex]);
bParentIndex = skel->parent;
bParentListIndex = G2_Find_Bone(ghoul2V[0].animModel, ghoul2V[0].mBlist, skel->name);
if (bParentListIndex != -1)
{
boneInfo_t &pbone = ghoul2V[0].mBlist[bParentListIndex];
if (pbone.flags & BONE_ANGLES_RAGDOLL)
{ //valid rag bone
break;
}
}
//didn't work out, reset to -1 again
bParentListIndex = -1;
}
bone.parentBoneIndex = bParentListIndex;
}
if (bone.parentBoneIndex != -1)
{
boneInfo_t &pbone = ghoul2V[0].mBlist[bone.parentBoneIndex];
if (pbone.flags & BONE_ANGLES_RAGDOLL)
{ //has origin calculated for us already
VectorCopy(ragEffectors[pbone.ragIndex].currentOrigin, parentOrigin);
hasDaddy = true;
}
}
}
//get the position this bone would be in if we were in the desired frame
hasBasePos = false;
if (broadsword_ragtobase &&
broadsword_ragtobase->integer)
{
vec3_t v, a;
float f;
G2_RagGetWorldAnimMatrix(ghoul2V[0], bone, params, worldBaseMatrix);
G2API_GiveMeVectorFromMatrix(&worldBaseMatrix, ORIGIN, basePos);
if (broadsword_ragtobase->integer > 1)
{
float fa = AngleNormalize180(animPelvisDir[YAW]-pelvisDir[YAW]);
float d = fa-bone.offsetRotation;
if (d > 16.0f ||
d < -16.0f)
{ //don't update unless x degrees away from the ideal to avoid moving goal spots too much if pelvis rotates
bone.offsetRotation = fa;
}
else
{
fa = bone.offsetRotation;
}
//Rotate the point around the pelvis based on the offsets between pelvis positions
VectorSubtract(basePos, animPelvisPos, v);
f = VectorLength(v);
vectoangles(v, a);
a[YAW] -= fa;
AngleVectors(a, v, 0, 0);
VectorNormalize(v);
VectorMA(animPelvisPos, f, v, basePos);
//re-orient the position of the bone to the current position of the pelvis
VectorSubtract(basePos, animPelvisPos, v);
//push the spots outward? (to stretch the skeleton more)
//v[0] *= 1.5f;
//v[1] *= 1.5f;
VectorAdd(pelvisPos, v, basePos);
}
#if 0 //for debugging frame skeleton
mdxaSkel_t *skel;
mdxaSkelOffsets_t *offsets;
offsets = (mdxaSkelOffsets_t *)((byte *)ghoul2V[0].aHeader + sizeof(mdxaHeader_t));
skel = (mdxaSkel_t *)((byte *)ghoul2V[0].aHeader + sizeof(mdxaHeader_t) + offsets->offsets[bone.boneNumber]);
vec3_t pu;
VectorCopy(basePos, pu);
pu[2] += 32;
if (bone.boneNumber < 11)
{
G2_RagDebugLine(basePos, pu, 50, 0xff00ff, 1);
}
else if (skel->name[0] == 'l')
{
G2_RagDebugLine(basePos, pu, 50, 0xffff00, 1);
}
else if (skel->name[0] == 'r')
{
G2_RagDebugLine(basePos, pu, 50, 0xffffff, 1);
}
else
{
G2_RagDebugLine(basePos, pu, 50, 0x00ffff, 1);
}
#endif
hasBasePos = true;
}
//Are we in solid?
if (hasDaddy)
{
Rag_Trace(&tr, e.currentOrigin, testMins, testMaxs, parentOrigin, ignoreNum, RAG_MASK, G2_NOCOLLIDE, 0);
//Rag_Trace(&tr, parentOrigin, testMins, testMaxs, e.currentOrigin, ignoreNum, RAG_MASK, G2_NOCOLLIDE, 0);
}
else
{
Rag_Trace(&tr, e.currentOrigin, testMins, testMaxs, params->position, ignoreNum, RAG_MASK, G2_NOCOLLIDE, 0);
}
if (tr.startsolid || tr.allsolid || tr.fraction != 1.0f)
{ //currently in solid, see what we can do about it
vec3_t vSub;
startSolid = true;
anySolid = true;
if (hasBasePos)// && bone.solidCount < 32)
{ //only go to the base pos for slightly in solid bones
#if 0 //over-compensation
float fl;
float floorBase;
VectorSubtract(basePos, e.currentOrigin, vSub);
fl = VectorNormalize(vSub);
VectorMA(e.currentOrigin, /*fl*8.0f*/64.0f, vSub, goalSpot);
floorBase = ((params->position[2]-23)-testMins[2])+8;
if (goalSpot[2] > floorBase)
{
goalSpot[2] = floorBase;
}
#else //just use the spot directly
VectorCopy(basePos, goalSpot);
goalSpot[2] = (params->position[2]-23)-testMins[2];
#endif
//Com_Printf("%i: %f %f %f\n", bone.boneNumber, basePos[0], basePos[1], basePos[2]);
}
else
{ //if deep in solid want to try to rise up out of solid before hinting back to base
VectorSubtract(e.currentOrigin, params->position, vSub);
VectorNormalize(vSub);
VectorMA(params->position, 40.0f, vSub, goalSpot);
//should be 1 unit above the ground taking bounding box sizes into account
goalSpot[2] = (params->position[2]-23)-testMins[2];
}
//Trace from the entity origin in the direction between the origin and current bone position to
//find a good eventual goal position
Rag_Trace(&tr, params->position, testMins, testMaxs, goalSpot, params->me, RAG_MASK, G2_NOCOLLIDE, 0);
VectorCopy(tr.endpos, goalSpot);
}
else
{
startSolid = false;
#if 1 //do hinting?
//Hint the bone back to the base origin
if (hasDaddy || hasBasePos)
{
if (hasBasePos)
{
VectorSubtract(basePos, e.currentOrigin, velDir);
}
else
{
VectorSubtract(e.currentOrigin, parentOrigin, velDir);
}
}
else
{
VectorSubtract(e.currentOrigin, params->position, velDir);
}
if (VectorLength(velDir) > 2.0f)
{ //don't bother if already close
VectorNormalize(velDir);
VectorScale(velDir, 8.0f, velDir);
velDir[2] = 0; //don't want to nudge on Z, the gravity will take care of things.
VectorAdd(bone.epVelocity, velDir, bone.epVelocity);
}
#endif
//Factor the object's velocity into the bone's velocity, by pushing the bone
//opposite the velocity to give the apperance the lighter limbs are being "dragged"
//behind those of greater mass.
if (bone.RagFlags & RAG_BONE_LIGHTWEIGHT)
{
vec3_t vel;
float vellen;
VectorCopy(params->velocity, vel);
//Scale down since our velocity scale is different from standard game physics
VectorScale(vel, 0.5f, vel);
vellen = VectorLength(vel);
if (vellen > 64.0f)
{ //cap it off
VectorScale(vel, 64.0f/vellen, vel);
}
//Invert the velocity so we go opposite the heavier parts and drag behind
VectorInverse(vel);
if (vel[2])
{ //want to override entirely instead then
VectorCopy(vel, bone.epVelocity);
}
else
{
VectorAdd(bone.epVelocity, vel, bone.epVelocity);
}
}
//We're not in solid so we can apply physics freely now.
if (!G2_ApplyRealBonePhysics(bone, e, params, goalSpot, testMins, testMaxs,
gravity, mass, bounce))
{ //if this is the case then somehow we failed to apply physics/get a good goal spot, just use the ent origin
VectorCopy(params->position, goalSpot);
}
}
//Set this now so we know what to do for angle limiting
if (startSolid)
{
bone.solidCount++;
#ifdef _DEBUG_BONE_NAMES
if (bone.solidCount > 64)
{
char *debugBoneName = G2_Get_Bone_Name(&ghoul2V[0], ghoul2V[0].mBlist, bone.boneNumber);
vec3_t absmin, absmax;
assert(debugBoneName);
Com_Printf("High bone (%s, %i) solid count: %i\n", debugBoneName, bone.boneNumber, bone.solidCount);
VectorAdd(e.currentOrigin, testMins, absmin);
VectorAdd(e.currentOrigin, testMaxs, absmax);
G2_RagDebugBox(absmin, absmax, 50);
G2_RagDebugLine(e.currentOrigin, goalSpot, 50, 0x00ff00, 1);
}
#endif
}
else
{
bone.solidCount = 0;
}
#if 0 //standard goalSpot capping?
//unless we are really in solid, we should keep adjustments minimal
if (/*bone.epGravFactor < 64 &&*/ bone.solidCount < 2 &&
!inAir)
{
vec3_t moveDist;
const float extent = 32.0f;
float len;
VectorSubtract(goalSpot, e.currentOrigin, moveDist);
len = VectorLength(moveDist);
if (len > extent)
{ //if greater than the extent then scale the vector down to the extent and factor it back into the goalspot
VectorScale(moveDist, extent/len, moveDist);
VectorAdd(e.currentOrigin, moveDist, goalSpot);
}
}
#endif
//Set the desired direction based on the goal position and other factors.
for (k = 0; k < 3; k++)
{
e.desiredDirection[k] = (goalSpot[k] - e.currentOrigin[k]);
if (broadsword_dircap &&
broadsword_dircap->value)
{
float cap = broadsword_dircap->value;
if (bone.solidCount > 5)
{
float solidFactor = bone.solidCount*0.2f;
if (solidFactor > 16.0f)
{ //don't go too high or something ugly might happen
solidFactor = 16.0f;
}
e.desiredDirection[k] *= solidFactor;
cap *= 8;
}
if (e.desiredDirection[k] > cap)
{
e.desiredDirection[k] = cap;
}
else if (e.desiredDirection[k] < -cap)
{
e.desiredDirection[k] = -cap;
}
}
e.desiredDirection[k] += (velocityMultiplier * bone.velocityEffector[k]);
e.desiredDirection[k] += (flrand(-0.75f, 0.75f) * flrand(-0.75f, 0.75f));
bone.velocityEffector[k] *= velocityDampening;
}
VectorCopy(e.currentOrigin, bone.lastPosition);
}
return anySolid;
}
#endif
static float AngleNormZero(float theta)
{
float ret=fmodf(theta,360.0f);
if (ret<-180.0f)
{
ret+=360.0f;
}
else if (ret>180.0f)
{
ret-=360.0f;
}
assert(ret>=-180.0f&&ret<=180.0f);
return ret;
}
static inline void G2_BoneSnap(CGhoul2Info_v &ghoul2V, boneInfo_t &bone, CRagDollUpdateParams *params)
{
return;
}
static void G2_RagDollSolve(CGhoul2Info_v &ghoul2V,int g2Index,float decay,int frameNum,const vec3_t currentOrg,bool limitAngles,CRagDollUpdateParams *params)
{
int i;
CGhoul2Info &ghoul2=ghoul2V[g2Index];
mdxaBone_t N;
mdxaBone_t P;
mdxaBone_t temp1;
mdxaBone_t temp2;
mdxaBone_t curRot;
mdxaBone_t curRotInv;
mdxaBone_t Gs[3];
mdxaBone_t Enew[3];
assert(ghoul2.mFileName[0]);
boneInfo_v &blist = ghoul2.mBlist;
// END this is the objective function thing
for (i=0;i<numRags;i++)
{
// these are used for affecting the end result
boneInfo_t &bone=*ragBoneData[i];
if (!(bone.RagFlags & RAG_PCJ))
{
continue; // not an active ragdoll PCJ
}
/* the situation is thus. (the dependent bone is in essence an effector)
The effector true bone matrix is:
Ecur = ragBones[depIndex] =
EntToWorld * [skeletal heirachy1] *
MyAnimMatrix * ragBasepose[i] * current_rot_matrix * ragBaseposeInv[i] *
[skeletal heirachy2] * EffectorAnimMatrix * ragBasepose[depIndex]
My ( sub-i) true bone matrix is:
M = ragBones[i] = EntToWorld * [skeletal heirachry1] *
MyAnimMatrix * ragBasepose[i] * current_rot_matrix * ragBaseposeInv[i] * ragBasepose[i]
Simplifies to:
M = ragBones[i] = EntToWorld * [skeletal heirachry1] *
MyAnimMatrix * ragBasepose[i] * current_rot_matrix
Enew is:
Enew = M *
inv(current_rot_matrix) * ragBaseposeInv[i] *
ragBasepose[i] * new_rot_matrix * ragBaseposeInv[i] *
inv(M*ragBaseposeInv[i]) * Ecur
We can simplify a bit:
Enew = M *
inv(current_rot_matrix) * ragBaseposeInv[i] *
ragBasepose[i] * new_rot_matrix * ragBaseposeInv[i] *
ragBasepose[i] * inv(M) * Ecur
Enew =
M * inv(current_rot_matrix) *
new_rot_matrix * inv(M) * Ecur
We define P to be:
P = M * inv(current_rot_matrix)
We define N to be:
N = inv(M)
then it is just:
Enew= P * new_rot_matrix * N * Ecur
Ri=new_rot_matrix (for each euler angle)
we are gonna use three R's, so will will precompute them as Gi = P * Ri * N
and then Enewi= Gi * Ecur
we are gonna evaluate Enewi for "goodness" and accumulate changes in delAngles
*/
Inverse_Matrix(&ragBones[i], &N); // dest 2nd arg
int k;
vec3_t tAngles;
VectorCopy(bone.currentAngles,tAngles);
Create_Matrix(tAngles,&curRot); //dest 2nd arg
Inverse_Matrix(&curRot,&curRotInv); // dest 2nd arg
Multiply_3x4Matrix(&P,&ragBones[i],&curRotInv); //dest first arg
if (bone.RagFlags & RAG_PCJ_MODEL_ROOT)
{
if (haveDesiredPelvisOffset)
{
float magicFactor12=0.25f; // dampfactor for pelvis pos
float magicFactor13=0.20f; //controls the speed of the gradient descent (pelvis pos)
assert( !Q_isnan(bone.ragOverrideMatrix.matrix[2][3]));
vec3_t deltaInEntitySpace;
TransformPoint(desiredPelvisOffset,deltaInEntitySpace,&N); // dest middle arg
for (k=0;k<3;k++)
{
float moveTo=bone.velocityRoot[k]+deltaInEntitySpace[k]*magicFactor13;
bone.velocityRoot[k]=(bone.velocityRoot[k]-moveTo)*magicFactor12+moveTo;
/*
if (bone.velocityRoot[k]>50.0f)
{
bone.velocityRoot[k]=50.0f;
}
if (bone.velocityRoot[k]<-50.0f)
{
bone.velocityRoot[k]=-50.0f;
}
*/
//No -rww
bone.ragOverrideMatrix.matrix[k][3]=bone.velocityRoot[k];
}
}
}
else
{
vec3_t delAngles;
VectorClear(delAngles);
for (k=0;k<3;k++)
{
tAngles[k]+=0.5f;
Create_Matrix(tAngles,&temp2); //dest 2nd arg
tAngles[k]-=0.5f;
Multiply_3x4Matrix(&temp1,&P,&temp2); //dest first arg
Multiply_3x4Matrix(&Gs[k],&temp1,&N); //dest first arg
}
int allSolidCount = 0;//bone.solidCount;
// fixme precompute this
int numDep=G2_GetBoneDependents(ghoul2,bone.boneNumber,tempDependents,MAX_BONES_RAG);
int j;
int numRagDep=0;
for (j=0;j<numDep;j++)
{
//fixme why do this for the special roots?
if (!(tempDependents[j]<(int)rag.size()&&rag[tempDependents[j]]))
{
continue;
}
int depIndex=rag[tempDependents[j]]->ragIndex;
assert(depIndex>i); // these are supposed to be topologically sorted
assert(ragBoneData[depIndex]);
boneInfo_t &depBone=*ragBoneData[depIndex];
if (depBone.RagFlags & RAG_EFFECTOR) // rag doll effector
{
// this is a dependent of me, and also a rag
numRagDep++;
for (k=0;k<3;k++)
{
Multiply_3x4Matrix(&Enew[k],&Gs[k],&ragBones[depIndex]); //dest first arg
vec3_t tPosition;
tPosition[0]=Enew[k].matrix[0][3];
tPosition[1]=Enew[k].matrix[1][3];
tPosition[2]=Enew[k].matrix[2][3];
vec3_t change;
VectorSubtract(tPosition,ragEffectors[depIndex].currentOrigin,change); // dest is last arg
float goodness=DotProduct(change,ragEffectors[depIndex].desiredDirection);
assert( !Q_isnan(goodness));
goodness*=depBone.weight;
delAngles[k]+=goodness; // keep bigger stuff more out of wall or something
assert( !Q_isnan(delAngles[k]));
}
allSolidCount += depBone.solidCount;
}
}
//bone.solidCount = allSolidCount;
allSolidCount += bone.solidCount;
VectorCopy(bone.currentAngles,bone.lastAngles);
// Update angles
float magicFactor9=0.75f; // dampfactor for angle changes
float magicFactor1=0.40f; //controls the speed of the gradient descent
float magicFactor32 = 1.5f;
float recip=0.0f;
if (numRagDep)
{
recip=sqrt(4.0f/float(numRagDep));
}
if (allSolidCount > 32)
{
magicFactor1 = 0.6f;
}
else if (allSolidCount > 10)
{
magicFactor1 = 0.5f;
}
if (bone.overGradSpeed)
{ //api call was made to specify a speed for this bone
magicFactor1 = bone.overGradSpeed;
}
float fac=decay*recip*magicFactor1;
assert(fac>=0.0f);
#if 0
if (bone.RagFlags & RAG_PCJ_PELVIS)
{
magicFactor9=.85f; // we don't want this swinging radically, make the whole thing kindof unstable
}
#endif
if (ragState==ERS_DYNAMIC)
{
magicFactor9=.85f; // we don't want this swinging radically, make the whole thing kindof unstable
}
#if 1 //constraint breaks?
if (bone.RagFlags & RAG_UNSNAPPABLE)
{
magicFactor32 = 1.0f;
}
#endif
for (k=0;k<3;k++)
{
bone.currentAngles[k]+=delAngles[k]*fac;
bone.currentAngles[k]=(bone.lastAngles[k]-bone.currentAngles[k])*magicFactor9 + bone.currentAngles[k];
bone.currentAngles[k]=AngleNormZero(bone.currentAngles[k]);
// bone.currentAngles[k]=flrand(bone.minAngles[k],bone.maxAngles[k]);
#if 1 //constraint breaks?
if (limitAngles && ( allSolidCount < 32 || (bone.RagFlags & RAG_UNSNAPPABLE) )) //32 tries and still in solid? Then we'll let you move freely
#else
if (limitAngles)
#endif
{
if (!bone.snapped || (bone.RagFlags & RAG_UNSNAPPABLE))
{
//magicFactor32 += (allSolidCount/32);
if (bone.currentAngles[k]>bone.maxAngles[k]*magicFactor32)
{
bone.currentAngles[k]=bone.maxAngles[k]*magicFactor32;
}
if (bone.currentAngles[k]<bone.minAngles[k]*magicFactor32)
{
bone.currentAngles[k]=bone.minAngles[k]*magicFactor32;
}
}
}
}
bool isSnapped = false;
for (k=0;k<3;k++)
{
if (bone.currentAngles[k]>bone.maxAngles[k]*magicFactor32)
{
isSnapped = true;
break;
}
if (bone.currentAngles[k]<bone.minAngles[k]*magicFactor32)
{
isSnapped = true;
break;
}
}
if (isSnapped != bone.snapped)
{
G2_BoneSnap(ghoul2V, bone, params);
bone.snapped = isSnapped;
}
Create_Matrix(bone.currentAngles,&temp1);
Multiply_3x4Matrix(&temp2,&temp1,bone.baseposeInv);
Multiply_3x4Matrix(&bone.ragOverrideMatrix,bone.basepose, &temp2);
assert( !Q_isnan(bone.ragOverrideMatrix.matrix[2][3]));
}
G2_Generate_MatrixRag(blist,ragBlistIndex[bone.boneNumber]);
}
}
static void G2_IKReposition(const vec3_t currentOrg,CRagDollUpdateParams *params)
{
int i;
assert(params);
for (i=0;i<numRags;i++)
{
boneInfo_t &bone=*ragBoneData[i];
SRagEffector &e=ragEffectors[i];
if (!(bone.RagFlags & RAG_EFFECTOR))
{
continue;
}
//Most effectors are not going to be PCJ, so this is not appplicable.
//The actual desired angle of the PCJ is factored around the desired
//directions of the effectors which are dependant on it.
/*
if (!(bone.RagFlags & RAG_PCJ_IK_CONTROLLED))
{
continue;
}
*/
int k;
float magicFactor12=0.8f; // dampening of velocity applied
float magicFactor16=10.0f; // effect multiplier of velocity applied
for (k=0;k<3;k++)
{
e.desiredDirection[k]=bone.ikPosition[k]-e.currentOrigin[k];
e.desiredDirection[k]+=magicFactor16*bone.velocityEffector[k];
e.desiredDirection[k]+=flrand(-0.75f,0.75f)*flrand(-0.75f,0.75f);
bone.velocityEffector[k]*=magicFactor12;
}
VectorCopy(e.currentOrigin,bone.lastPosition); // last arg is dest
}
}
static void G2_IKSolve(CGhoul2Info_v &ghoul2V,int g2Index,float decay,int frameNum,const vec3_t currentOrg,bool limitAngles)
{
int i;
CGhoul2Info &ghoul2 = ghoul2V[g2Index];
mdxaBone_t N;
mdxaBone_t P;
mdxaBone_t temp1;
mdxaBone_t temp2;
mdxaBone_t curRot;
mdxaBone_t curRotInv;
mdxaBone_t Gs[3];
mdxaBone_t Enew[3];
assert(ghoul2.mFileName[0]);
boneInfo_v &blist = ghoul2.mBlist;
// END this is the objective function thing
for (i = 0; i < numRags; i++)
{
// these are used for affecting the end result
boneInfo_t &bone = *ragBoneData[i];
if (bone.RagFlags & RAG_PCJ_MODEL_ROOT)
{
continue;
}
if (!(bone.RagFlags & RAG_PCJ_IK_CONTROLLED))
{
continue;
}
Inverse_Matrix(&ragBones[i], &N); // dest 2nd arg
int k;
vec3_t tAngles;
VectorCopy(bone.currentAngles, tAngles);
Create_Matrix(tAngles, &curRot); //dest 2nd arg
Inverse_Matrix(&curRot, &curRotInv); // dest 2nd arg
Multiply_3x4Matrix(&P, &ragBones[i], &curRotInv); //dest first arg
vec3_t delAngles;
VectorClear(delAngles);
for (k = 0; k < 3; k++)
{
tAngles[k] += 0.5f;
Create_Matrix(tAngles, &temp2); //dest 2nd arg
tAngles[k] -= 0.5f;
Multiply_3x4Matrix(&temp1, &P, &temp2); //dest first arg
Multiply_3x4Matrix(&Gs[k], &temp1, &N); //dest first arg
}
// fixme precompute this
int numDep=G2_GetBoneDependents(ghoul2,bone.boneNumber,tempDependents,MAX_BONES_RAG);
int j;
int numRagDep=0;
for (j=0;j<numDep;j++)
{
//fixme why do this for the special roots?
if (!(tempDependents[j]<(int)rag.size()&&rag[tempDependents[j]]))
{
continue;
}
int depIndex=rag[tempDependents[j]]->ragIndex;
if (!ragBoneData[depIndex])
{
continue;
}
boneInfo_t &depBone=*ragBoneData[depIndex];
if (depBone.RagFlags & RAG_EFFECTOR)
{
// this is a dependent of me, and also a rag
numRagDep++;
for (k=0;k<3;k++)
{
Multiply_3x4Matrix(&Enew[k],&Gs[k],&ragBones[depIndex]); //dest first arg
vec3_t tPosition;
tPosition[0]=Enew[k].matrix[0][3];
tPosition[1]=Enew[k].matrix[1][3];
tPosition[2]=Enew[k].matrix[2][3];
vec3_t change;
VectorSubtract(tPosition,ragEffectors[depIndex].currentOrigin,change); // dest is last arg
float goodness=DotProduct(change,ragEffectors[depIndex].desiredDirection);
assert( !Q_isnan(goodness));
goodness*=depBone.weight;
delAngles[k]+=goodness; // keep bigger stuff more out of wall or something
assert( !Q_isnan(delAngles[k]));
}
}
}
VectorCopy(bone.currentAngles, bone.lastAngles);
// Update angles
float magicFactor9 = 0.75f; // dampfactor for angle changes
float magicFactor1 = bone.ikSpeed; //controls the speed of the gradient descent
float magicFactor32 = 1.0f;
float recip = 0.0f;
bool freeThisBone = false;
if (!magicFactor1)
{
magicFactor1 = 0.40f;
}
recip = sqrt(4.0f/1.0f);
float fac = (decay*recip*magicFactor1);
assert(fac >= 0.0f);
if (ragState == ERS_DYNAMIC)
{
magicFactor9 = 0.85f; // we don't want this swinging radically, make the whole thing kindof unstable
}
if (!bone.maxAngles[0] && !bone.maxAngles[1] && !bone.maxAngles[2] &&
!bone.minAngles[0] && !bone.minAngles[1] && !bone.minAngles[2])
{
freeThisBone = true;
}
for (k = 0; k < 3; k++)
{
bone.currentAngles[k] += delAngles[k]*fac;
bone.currentAngles[k] = (bone.lastAngles[k]-bone.currentAngles[k])*magicFactor9 + bone.currentAngles[k];
bone.currentAngles[k] = AngleNormZero(bone.currentAngles[k]);
if (limitAngles && !freeThisBone)
{
if (bone.currentAngles[k] > bone.maxAngles[k]*magicFactor32)
{
bone.currentAngles[k] = bone.maxAngles[k]*magicFactor32;
}
if (bone.currentAngles[k] < bone.minAngles[k]*magicFactor32)
{
bone.currentAngles[k] = bone.minAngles[k]*magicFactor32;
}
}
}
Create_Matrix(bone.currentAngles, &temp1);
Multiply_3x4Matrix(&temp2, &temp1, bone.baseposeInv);
Multiply_3x4Matrix(&bone.ragOverrideMatrix, bone.basepose, &temp2);
assert( !Q_isnan(bone.ragOverrideMatrix.matrix[2][3]));
G2_Generate_MatrixRag(blist, ragBlistIndex[bone.boneNumber]);
}
}
static void G2_DoIK(CGhoul2Info_v &ghoul2V,int g2Index,CRagDollUpdateParams *params)
{
int i;
if (!params)
{
assert(0);
return;
}
int frameNum=G2API_GetTime(0);
CGhoul2Info &ghoul2=ghoul2V[g2Index];
assert(ghoul2.mFileName[0]);
float decay=1.0f;
bool resetOrigin=false;
bool anyRendered=false;
int iters = 12; //since we don't trace or anything, we can afford this.
if (iters)
{
if (!G2_RagDollSetup(ghoul2,frameNum,resetOrigin,params->position,anyRendered))
{
return;
}
// ok, now our data structures are compact and set up in topological order
for (i=0;i<iters;i++)
{
G2_RagDollCurrentPosition(ghoul2V,g2Index,frameNum,params->angles,params->position,params->scale);
G2_IKReposition(params->position, params);
G2_IKSolve(ghoul2V,g2Index,decay*2.0f,frameNum,params->position,true);
}
}
if (params->me != ENTITYNUM_NONE)
{
G2_RagDollCurrentPosition(ghoul2V,g2Index,frameNum,params->angles,params->position,params->scale);
}
}
//rww - cut out the entire non-ragdoll section of this..
void G2_Animate_Bone_List(CGhoul2Info_v &ghoul2, const int currentTime, const int index,CRagDollUpdateParams *params)
{
bool anyRagDoll=false;
bool anyIK = false;
for(size_t i=0; i<ghoul2[index].mBlist.size(); i++)
{
if (ghoul2[index].mBlist[i].boneNumber != -1)
{
if (ghoul2[index].mBlist[i].flags & BONE_ANGLES_RAGDOLL)
{
if (ghoul2[index].mBlist[i].RagFlags & RAG_PCJ_IK_CONTROLLED)
{
anyIK = true;
}
anyRagDoll=true;
if (anyIK && anyRagDoll)
{
break;
}
}
}
}
if (!index&&params)
{
if (anyIK)
{ //we use ragdoll params so we know what our current position, etc. is.
G2_DoIK(ghoul2, 0, params);
}
else
{
G2_RagDoll(ghoul2,0,params,currentTime);
}
}
}
//rww - RAGDOLL_END
static int G2_Set_Bone_Angles_IK(
CGhoul2Info &ghoul2,
const mdxaHeader_t *mod_a,
boneInfo_v &blist,
const char *boneName,
const int flags,
const float radius,
const vec3_t angleMin=0,
const vec3_t angleMax=0,
const int blendTime=500)
{
int index = G2_Find_Bone_Rag(&ghoul2, blist, boneName);
if (index == -1)
{
index = G2_Add_Bone(ghoul2.animModel, blist, boneName);
}
if (index != -1)
{
boneInfo_t &bone=blist[index];
bone.flags |= BONE_ANGLES_IK;
bone.flags &= ~BONE_ANGLES_RAGDOLL;
bone.ragStartTime=G2API_GetTime(0);
bone.radius=radius;
bone.weight=1.0f;
if (angleMin&&angleMax)
{
VectorCopy(angleMin,bone.minAngles);
VectorCopy(angleMax,bone.maxAngles);
}
else
{
VectorCopy(bone.currentAngles,bone.minAngles); // I guess this isn't a rag pcj then
VectorCopy(bone.currentAngles,bone.maxAngles);
}
if (!bone.lastTimeUpdated)
{
static mdxaBone_t id =
{
{
{ 1.0f, 0.0f, 0.0f, 0.0f },
{ 0.0f, 1.0f, 0.0f, 0.0f },
{ 0.0f, 0.0f, 1.0f, 0.0f }
}
};
memcpy(&bone.ragOverrideMatrix,&id, sizeof(mdxaBone_t));
VectorClear(bone.anglesOffset);
VectorClear(bone.positionOffset);
VectorClear(bone.velocityEffector); // this is actually a velocity now
VectorClear(bone.velocityRoot); // this is actually a velocity now
VectorClear(bone.lastPosition);
VectorClear(bone.lastShotDir);
bone.lastContents=0;
// if this is non-zero, we are in a dynamic state
bone.firstCollisionTime=bone.ragStartTime;
// if this is non-zero, we are in a settling state
bone.restTime=0;
// if they are both zero, we are in a settled state
bone.firstTime=0;
bone.RagFlags=flags;
bone.DependentRagIndexMask=0;
G2_Generate_MatrixRag(blist,index); // set everything to th id
VectorClear(bone.currentAngles);
VectorCopy(bone.currentAngles,bone.lastAngles);
}
}
return index;
}
void G2_InitIK(CGhoul2Info_v &ghoul2V, sharedRagDollUpdateParams_t *parms, int time, const mdxaHeader_t *mod_a, int model)
{
CGhoul2Info &ghoul2=ghoul2V[model];
int curTime=time;
boneInfo_v &blist = ghoul2.mBlist;
G2_GenerateWorldMatrix(parms->angles, parms->position);
G2_ConstructGhoulSkeleton(ghoul2V, curTime, false, parms->scale);
// new base anim, unconscious flop
int pcjFlags;
#if 0
vec3_t pcjMin, pcjMax;
VectorClear(pcjMin);
VectorClear(pcjMax);
pcjFlags=RAG_PCJ|RAG_PCJ_POST_MULT;//|RAG_EFFECTOR;
G2_Set_Bone_Angles_IK(ghoul2, mod_a,blist,"model_root",RAG_PCJ_MODEL_ROOT|RAG_PCJ,10.0f,pcjMin,pcjMax,100);
G2_Set_Bone_Angles_IK(ghoul2, mod_a,blist,"pelvis",RAG_PCJ_PELVIS|RAG_PCJ|RAG_PCJ_POST_MULT,10.0f,pcjMin,pcjMax,100);
G2_Set_Bone_Angles_IK(ghoul2, mod_a,blist,"lower_lumbar",pcjFlags,10.0f,pcjMin,pcjMax,500);
G2_Set_Bone_Angles_IK(ghoul2, mod_a,blist,"upper_lumbar",pcjFlags,10.0f,pcjMin,pcjMax,500);
G2_Set_Bone_Angles_IK(ghoul2, mod_a,blist,"thoracic",pcjFlags|RAG_EFFECTOR,12.0f,pcjMin,pcjMax,500);
G2_Set_Bone_Angles_IK(ghoul2, mod_a,blist,"cranium",pcjFlags,6.0f,pcjMin,pcjMax,500);
G2_Set_Bone_Angles_IK(ghoul2, mod_a,blist,"rhumerus",pcjFlags,4.0f,pcjMin,pcjMax,500);
G2_Set_Bone_Angles_IK(ghoul2, mod_a,blist,"lhumerus",pcjFlags,4.0f,pcjMin,pcjMax,500);
G2_Set_Bone_Angles_IK(ghoul2, mod_a,blist,"rradius",pcjFlags,3.0f,pcjMin,pcjMax,500);
G2_Set_Bone_Angles_IK(ghoul2, mod_a,blist,"lradius",pcjFlags,3.0f,pcjMin,pcjMax,500);
G2_Set_Bone_Angles_IK(ghoul2, mod_a,blist,"rfemurYZ",pcjFlags,6.0f,pcjMin,pcjMax,500);
G2_Set_Bone_Angles_IK(ghoul2, mod_a,blist,"lfemurYZ",pcjFlags,6.0f,pcjMin,pcjMax,500);
G2_Set_Bone_Angles_IK(ghoul2, mod_a,blist,"rtibia",pcjFlags,4.0f,pcjMin,pcjMax,500);
G2_Set_Bone_Angles_IK(ghoul2, mod_a,blist,"ltibia",pcjFlags,4.0f,pcjMin,pcjMax,500);
G2_ConstructGhoulSkeleton(ghoul2V, curTime, false, parms->scale);
#endif
//Only need the standard effectors for this.
pcjFlags = RAG_PCJ|RAG_PCJ_POST_MULT|RAG_EFFECTOR;
G2_Set_Bone_Angles_IK(ghoul2, mod_a,blist,"rhand",pcjFlags,6.0f);
G2_Set_Bone_Angles_IK(ghoul2, mod_a,blist,"lhand",pcjFlags,6.0f);
// G2_Set_Bone_Angles_IK(ghoul2, mod_a,blist,"rtarsal",pcjFlags,4.0f);
// G2_Set_Bone_Angles_IK(ghoul2, mod_a,blist,"ltarsal",pcjFlags,4.0f);
G2_Set_Bone_Angles_IK(ghoul2, mod_a,blist,"rtibia",pcjFlags,4.0f);
G2_Set_Bone_Angles_IK(ghoul2, mod_a,blist,"ltibia",pcjFlags,4.0f);
G2_Set_Bone_Angles_IK(ghoul2, mod_a,blist,"rtalus",pcjFlags,4.0f);
G2_Set_Bone_Angles_IK(ghoul2, mod_a,blist,"ltalus",pcjFlags,4.0f);
G2_Set_Bone_Angles_IK(ghoul2, mod_a,blist,"rradiusX",pcjFlags,6.0f);
G2_Set_Bone_Angles_IK(ghoul2, mod_a,blist,"lradiusX",pcjFlags,6.0f);
G2_Set_Bone_Angles_IK(ghoul2, mod_a,blist,"rfemurX",pcjFlags,10.0f);
G2_Set_Bone_Angles_IK(ghoul2, mod_a,blist,"lfemurX",pcjFlags,10.0f);
G2_Set_Bone_Angles_IK(ghoul2, mod_a,blist,"ceyebrow",pcjFlags,10.0f);
}
qboolean G2_SetBoneIKState(CGhoul2Info_v &ghoul2, int time, const char *boneName, int ikState, sharedSetBoneIKStateParams_t *params)
{
model_t *mod_a;
int g2index = 0;
int curTime = time;
CGhoul2Info &g2 = ghoul2[g2index];
const mdxaHeader_t *rmod_a = G2_GetModA(g2);
boneInfo_v &blist = g2.mBlist;
mod_a = (model_t *)g2.animModel;
if (!boneName)
{ //null bonename param means it's time to init the ik stuff on this instance
sharedRagDollUpdateParams_t sRDUP;
if (ikState == IKS_NONE)
{ //this means we want to reset the IK state completely.. run through the bone list, and reset all the appropriate flags
size_t i = 0;
while (i < blist.size())
{ //we can't use this method for ragdoll. However, since we expect them to set their anims/angles again on the PCJ
//limb after they reset it gameside, it's reasonable for IK bones.
boneInfo_t &bone = blist[i];
if (bone.boneNumber != -1)
{
bone.flags &= ~BONE_ANGLES_RAGDOLL;
bone.flags &= ~BONE_ANGLES_IK;
bone.RagFlags = 0;
bone.lastTimeUpdated = 0;
}
i++;
}
return qtrue;
}
assert(params);
if (!params)
{
return qfalse;
}
sRDUP.me = 0;
VectorCopy(params->angles, sRDUP.angles);
VectorCopy(params->origin, sRDUP.position);
VectorCopy(params->scale, sRDUP.scale);
VectorClear(sRDUP.velocity);
G2_InitIK(ghoul2, &sRDUP, curTime, rmod_a, g2index);
return qtrue;
}
if (!rmod_a || !mod_a)
{
return qfalse;
}
int index = G2_Find_Bone(mod_a, blist, boneName);
if (index == -1)
{
index = G2_Add_Bone(mod_a, blist, boneName);
}
if (index == -1)
{ //couldn't find or add the bone..
return qfalse;
}
boneInfo_t &bone = blist[index];
if (ikState == IKS_NONE)
{ //remove the bone from the list then, so it has to reinit. I don't think this should hurt anything since
//we don't store bone index handles gameside anywhere.
if (!(bone.flags & BONE_ANGLES_RAGDOLL))
{ //you can't set the ik state to none if it's not a rag/ik bone.
return qfalse;
}
//bone.flags = 0;
//G2_Remove_Bone_Index(blist, index);
//actually, I want to keep it on the rag list, and remove it as an IK bone instead.
bone.flags &= ~BONE_ANGLES_RAGDOLL;
bone.flags |= BONE_ANGLES_IK;
bone.RagFlags &= ~RAG_PCJ_IK_CONTROLLED;
return qtrue;
}
//need params if we're not resetting.
if (!params)
{
assert(0);
return qfalse;
}
if (bone.flags & BONE_ANGLES_RAGDOLL)
{ //otherwise if the bone is already flagged as rag, then we can't set it again. (non-active ik bones will be BONE_ANGLES_IK, active are considered rag)
return qfalse;
}
#if 0 //this is wrong now.. we're only initing effectors with initik now.. which SHOULDN'T be used as pcj's
if (!(bone.flags & BONE_ANGLES_IK) && !(bone.flags & BONE_ANGLES_RAGDOLL))
{ //IK system has not been inited yet, because any bone that can be IK should be in the ragdoll list, not flagged as BONE_ANGLES_RAGDOLL but as BONE_ANGLES_IK
sharedRagDollUpdateParams_t sRDUP;
sRDUP.me = 0;
VectorCopy(params->angles, sRDUP.angles);
VectorCopy(params->origin, sRDUP.position);
VectorCopy(params->scale, sRDUP.scale);
VectorClear(sRDUP.velocity);
G2_InitIK(ghoul2, &sRDUP, curTime, rmod_a, g2index);
G2_ConstructGhoulSkeleton(ghoul2, curTime, false, params->scale);
}
else
{
G2_GenerateWorldMatrix(params->angles, params->origin);
G2_ConstructGhoulSkeleton(ghoul2, curTime, false, params->scale);
}
#else
G2_GenerateWorldMatrix(params->angles, params->origin);
G2_ConstructGhoulSkeleton(ghoul2, curTime, false, params->scale);
#endif
int pcjFlags = RAG_PCJ|RAG_PCJ_IK_CONTROLLED|RAG_PCJ_POST_MULT|RAG_EFFECTOR;
if (params->pcjOverrides)
{
pcjFlags = params->pcjOverrides;
}
bone.ikSpeed = 0.4f;
VectorClear(bone.ikPosition);
G2_Set_Bone_Rag(rmod_a, blist, boneName, g2, params->scale, params->origin);
int startFrame = params->startFrame, endFrame = params->endFrame;
if (bone.startFrame != startFrame || bone.endFrame != endFrame || params->forceAnimOnBone)
{ //if it's already on this anim leave it alone, to allow smooth transitions into IK on the current anim if it is so desired.
G2_Set_Bone_Anim_No_BS(g2, rmod_a, blist, boneName, startFrame, endFrame-1,
BONE_ANIM_OVERRIDE_FREEZE|BONE_ANIM_BLEND,
1.0f, curTime, float(startFrame), 150, 0, true);
}
G2_ConstructGhoulSkeleton(ghoul2, curTime, false, params->scale);
bone.lastTimeUpdated = 0;
G2_Set_Bone_Angles_Rag(g2, rmod_a, blist, boneName, pcjFlags, params->radius, params->pcjMins, params->pcjMaxs, params->blendTime);
if (!G2_RagDollSetup(g2,curTime,true,params->origin,false))
{
assert(!"failed to add any rag bones");
return qfalse;
}
return qtrue;
}
qboolean G2_IKMove(CGhoul2Info_v &ghoul2, int time, sharedIKMoveParams_t *params)
{
#if 0
model_t *mod_a;
int g2index = 0;
int curTime = time;
CGhoul2Info &g2 = ghoul2[g2index];
boneInfo_v &blist = g2.mBlist;
mod_a = (model_t *)g2.animModel;
if (!mod_a)
{
return qfalse;
}
int index = G2_Find_Bone(mod_a, blist, params->boneName);
//don't add here if you can't find it.. ik bones should already be there, because they need to have special stuff done to them anyway.
if (index == -1)
{ //couldn't find the bone..
return qfalse;
}
if (!params)
{
assert(0);
return qfalse;
}
if (!(blist[index].flags & BONE_ANGLES_RAGDOLL) && !(blist[index].flags & BONE_ANGLES_IK))
{ //no-can-do, buddy
return qfalse;
}
VectorCopy(params->desiredOrigin, blist[index].ikPosition);
blist[index].ikSpeed = params->movementSpeed;
#else
int g2index = 0;
int curTime = time;
CGhoul2Info &g2 = ghoul2[g2index];
//rwwFIXMEFIXME: Doing this on all bones at the moment, fix this later?
if (!G2_RagDollSetup(g2,curTime,true,params->origin,false))
{ //changed models, possibly.
return qfalse;
}
for (int i=0;i<numRags;i++)
{
boneInfo_t &bone=*ragBoneData[i];
//if (bone.boneNumber == blist[index].boneNumber)
{
VectorCopy(params->desiredOrigin, bone.ikPosition);
bone.ikSpeed = params->movementSpeed;
}
}
#endif
return qtrue;
}
// set the bone list to all unused so the bone transformation routine ignores it.
void G2_Init_Bone_List(boneInfo_v &blist, int numBones)
{
blist.clear();
blist.reserve(numBones);
}
void G2_RemoveRedundantBoneOverrides(boneInfo_v &blist, int *activeBones)
{
// walk the surface list, removing surface overrides or generated surfaces that are pointing at surfaces that aren't active anymore
for (size_t i=0; i<blist.size(); i++)
{
if (blist[i].boneNumber != -1)
{
if (!activeBones[blist[i].boneNumber])
{
blist[i].flags = 0;
G2_Remove_Bone_Index(blist, i);
}
}
}
}
int G2_Get_Bone_Index(CGhoul2Info *ghoul2, const char *boneName)
{
model_t *mod_m = R_GetModelByHandle(RE_RegisterModel(ghoul2->mFileName));
model_t *mod_a = R_GetModelByHandle(mod_m->mdxm->animIndex);
return (G2_Find_Bone(mod_a, ghoul2->mBlist, boneName));
}