Files
GTASource/game/debug/DebugAnimation.cpp
expvintl 419f2e4752 init
2025-02-23 17:40:52 +08:00

1024 lines
29 KiB
C++

#include "DebugAnimation.h"
#if DR_ANIMATIONS_ENABLED
#include "animation/debug/AnimViewer.h"
#include "cranimation/framedata.h"
#include "fwanimation/movedump.h"
#include "move/move_network.h"
#include "move/move_statedef.h"
#include "move/move_types_internal.h"
#include "peds/ped.h"
#include "physics/debugEvents.h"
#include "physics/debugplayback.h"
#include "scene/Entity.h"
#include "system/timemgr.h"
static DR_TrackMoveInterface s_TrackMoveInterface;
int AnimMotionTreeTracker::sm_LineOffset;
u32 AnimMotionTreeTracker::sm_iHoveredFrame;
using namespace debugPlayback;
CEntity* GetSelectedEntity()
{
debugPlayback::phInstId rSelected(debugPlayback::GetCurrentSelectedEntity());
if (!rSelected.IsValid())
return 0;
return (CEntity*)rSelected->GetUserData();
}
struct MoveSetValue : public SetTaggedFloatEvent
{
MoveSetValue()
{ }
PAR_PARSABLE;
DR_EVENT_DECL(MoveSetValue)
};
struct MoveGetValue : public SetTaggedFloatEvent
{
MoveGetValue()
{ }
PAR_PARSABLE;
DR_EVENT_DECL(MoveGetValue)
};
struct MoveTransitionStringCollection : public SetTaggedDataCollectionEvent
{
MoveTransitionStringCollection()
{ }
PAR_PARSABLE;
DR_EVENT_DECL(MoveTransitionStringCollection)
};
struct MoveSetValueStringCollection : public SetTaggedDataCollectionEvent
{
MoveSetValueStringCollection()
{ }
PAR_PARSABLE;
DR_EVENT_DECL(MoveSetValueStringCollection)
};
struct MoveConditionStringCollection : public SetTaggedDataCollectionEvent
{
MoveConditionStringCollection()
{ }
PAR_PARSABLE;
DR_EVENT_DECL(MoveConditionStringCollection)
};
void UpdateAnimEventRecordingState()
{
mvDRTrackInterface::sm_bEnabled =
DR_EVENT_ENABLED(MoveTransitionStringCollection)
|| DR_EVENT_ENABLED(MoveSetValue)
|| DR_EVENT_ENABLED(MoveGetValue)
|| DR_EVENT_ENABLED(MoveSetValueStringCollection)
|| DR_EVENT_ENABLED(MoveConditionStringCollection);
}
struct MoveDumpPrinterTextOutput : public fwMoveDumpPrinterBase
{
debugPlayback::TextOutputVisitor *mp_Printer;
int m_iIndent;
int m_iTotalLines;
int m_iLinesToSkip;
bool m_bLineHovered;
MoveDumpPrinterTextOutput(debugPlayback::TextOutputVisitor &rOutput)
{
m_iIndent = 0;
m_iTotalLines = 0;
m_iLinesToSkip = 0;
m_bLineHovered = false;
mp_Printer = &rOutput;
}
void PrintLine(int iIndent, const char *pText)
{
//Hack to support indenting
int iPopit = iIndent;
while (iIndent)
{
mp_Printer->PushIndent();
--iIndent;
}
if (m_iTotalLines > m_iLinesToSkip)
{
if (mp_Printer->AddLine( pText ))
{
m_bLineHovered = true;
}
}
while (iPopit)
{
mp_Printer->PopIndent();
--iPopit;
}
++m_iTotalLines;
}
};
void AnimMotionTreeTracker::PrintToTTY() const
{
fwMoveDumpRenderFlags renderFlags(fwMotionTreeVisualiser::ms_bRenderTree,
fwMotionTreeVisualiser::ms_bRenderClass,
fwMotionTreeVisualiser::ms_bRenderWeight,
fwMotionTreeVisualiser::ms_bRenderLocalWeight,
fwMotionTreeVisualiser::ms_bRenderStateActiveName,
fwMotionTreeVisualiser::ms_bRenderRequests,
fwMotionTreeVisualiser::ms_bRenderFlags,
fwMotionTreeVisualiser::ms_bRenderInputParams,
fwMotionTreeVisualiser::ms_bRenderOutputParams,
fwMotionTreeVisualiser::ms_bRenderInvalidNodes,
fwMotionTreeVisualiser::ms_bRenderSynchronizerWeights,
fwMotionTreeVisualiser::ms_bRenderSynchronizerGraph,
fwMotionTreeVisualiser::ms_bRenderDofs,
fwMotionTreeVisualiser::ms_bRenderAddress);
Displayf("----------------------------------------");
Displayf("Motion tree dump - '%s' event index %d", m_Label.TryGetCStr(), m_iEventIndex);
Displayf("----------------------------------------");
int iHorz=0;
fwMoveDumpPrinterTTY printer;
m_DumpNetwork->Print(printer, iHorz, renderFlags);
Displayf("----------------------------------------\n");
}
struct MoveDumpPrinterCountLines : public fwMoveDumpPrinterBase
{
int m_iLines;
MoveDumpPrinterCountLines():m_iLines(0){}
void PrintLine(int, const char *) { ++m_iLines; }
};
void AnimMotionTreeTracker::AddEventOptions(const debugPlayback::Frame &frame, debugPlayback::TextOutputVisitor &rText, bool &bMouseDown) const
{
PhysicsEvent::AddEventOptions(frame, rText, bMouseDown);
if (rText.AddLine("[Print MT To TTY]") && bMouseDown)
{
PrintToTTY();
bMouseDown = false;
}
fwMoveDumpRenderFlags renderFlags(fwMotionTreeVisualiser::ms_bRenderTree,
fwMotionTreeVisualiser::ms_bRenderClass,
fwMotionTreeVisualiser::ms_bRenderWeight,
fwMotionTreeVisualiser::ms_bRenderLocalWeight,
fwMotionTreeVisualiser::ms_bRenderStateActiveName,
fwMotionTreeVisualiser::ms_bRenderRequests,
fwMotionTreeVisualiser::ms_bRenderFlags,
fwMotionTreeVisualiser::ms_bRenderInputParams,
fwMotionTreeVisualiser::ms_bRenderOutputParams,
fwMotionTreeVisualiser::ms_bRenderInvalidNodes,
fwMotionTreeVisualiser::ms_bRenderSynchronizerWeights,
fwMotionTreeVisualiser::ms_bRenderSynchronizerGraph,
fwMotionTreeVisualiser::ms_bRenderDofs,
fwMotionTreeVisualiser::ms_bRenderAddress);
int iHorz=0;
MoveDumpPrinterCountLines counter;
m_DumpNetwork->Print(counter, iHorz, renderFlags);
}
bool AnimMotionTreeTracker::DebugEventText(debugPlayback::TextOutputVisitor &rOutput) const
{
PhysicsEvent::PrintInstName(rOutput);
fwMoveDumpRenderFlags renderFlags(fwMotionTreeVisualiser::ms_bRenderTree,
fwMotionTreeVisualiser::ms_bRenderClass,
fwMotionTreeVisualiser::ms_bRenderWeight,
fwMotionTreeVisualiser::ms_bRenderLocalWeight,
fwMotionTreeVisualiser::ms_bRenderStateActiveName,
fwMotionTreeVisualiser::ms_bRenderRequests,
fwMotionTreeVisualiser::ms_bRenderFlags,
fwMotionTreeVisualiser::ms_bRenderInputParams,
fwMotionTreeVisualiser::ms_bRenderOutputParams,
fwMotionTreeVisualiser::ms_bRenderInvalidNodes,
fwMotionTreeVisualiser::ms_bRenderSynchronizerWeights,
fwMotionTreeVisualiser::ms_bRenderSynchronizerGraph,
fwMotionTreeVisualiser::ms_bRenderDofs,
fwMotionTreeVisualiser::ms_bRenderAddress);
int iHorz=0;
MoveDumpPrinterTextOutput printer(rOutput);
printer.m_iLinesToSkip = sm_LineOffset;
m_DumpNetwork->Print(printer, iHorz, renderFlags);
if (sm_LineOffset > printer.m_iTotalLines-10)
{
sm_LineOffset = Max(0, printer.m_iTotalLines-10);
}
if (printer.m_bLineHovered)
{
//Store off that we were hovered this frame - means that we can track mouse dragging in some other code that
sm_iHoveredFrame = TIME.GetFrameCount();
if (ioMouse::HasWheel() && (ioMouse::GetDZ()!=0))
{
if (ioMouse::GetDZ() < 0)
{
if (sm_LineOffset < printer.m_iTotalLines-10)
{
sm_LineOffset+=3;
}
}
else if (sm_LineOffset)
{
sm_LineOffset=Max(0, sm_LineOffset-3);
}
}
}
return true;
}
void AnimMotionTreeTracker::Init()
{
if (m_rInst.IsValid())
{
fwEntity *pEntity = (fwEntity *)m_rInst->GetUserData();
if (pEntity)
{
const char *szFilter = NULL;
if(CAnimViewer::m_iCaptureFilterNameIndex >= 0 && CAnimViewer::m_iCaptureFilterNameIndex < CAnimViewer::m_iCaptureFilterNameCount)
{
szFilter = CAnimViewer::m_szCaptureFilterNames[CAnimViewer::m_iCaptureFilterNameIndex];
}
else
{
szFilter = "BONEMASK_LOD_LO";
}
m_DumpNetwork = rage_new fwMoveDumpNetwork(pEntity, CAnimViewer::m_bCaptureDofs, szFilter, CAnimViewer::m_bRelativeToParent);
}
}
}
AnimMotionTreeTracker::~AnimMotionTreeTracker()
{
delete m_DumpNetwork;
}
bool StoreSkeleton::DebugEventText(debugPlayback::TextOutputVisitor &rOutput) const
{
//Just display the cached name, this is all about the 3d display
PhysicsEvent::PrintInstName(rOutput);
rOutput.AddLine("Pos: <%f, %f, %f>", m_SkeletonName.GetCStr(), m_Pos.GetXf(), m_Pos.GetYf(), m_Pos.GetZf());
rOutput.AddLine("SkeletonName: %s", m_SkeletonName.GetCStr());
rOutput.AddLine("BonePosCount: %d", m_BonePositions.GetCount());
rOutput.AddLine("BoneRotCount: %d", m_BoneRotations.GetCount());
rOutput.AddLine("Signature: 0x%x", m_DataSignature);
if (GameRecorder::GetAppLevelInstance())
{
const StoredSkelData *pSkelData = GameRecorder::GetAppLevelInstance()->GetSkelData(m_DataSignature);
if (pSkelData)
{
rOutput.AddLine("SkelSignature: 0x%x", pSkelData->m_SkelSignature);
rOutput.AddLine("SkelBoneCount: %d", pSkelData->m_BoneIndexToFilteredIndex.GetCount());
}
}
/*
rOutput.AddLine("FULL DATA");
rOutput.PushIndent();
PhysicsEvent::DebugEventText(rOutput);
rOutput.PopIndent();
*/
return true;
}
void StoredSkelData::Create(const crSkeletonData &rData, crFrameFilter *pFilter)
{
//Count the number of links we're going to need to display
m_SkelSignature = rData.GetSignature();
int iLinks = 0;
int iNumBones = rData.GetNumBones();
int iNumFilteredBones = pFilter ? 0 : iNumBones;
float fInOutWeight;
for (u32 i = 0; i < iNumBones; i++)
{
const crBoneData *boneData = rData.GetBoneData(i);
u16 boneId = boneData->GetBoneId();
if (pFilter)
{
if ( pFilter->FilterDof(kTrackBoneTranslation, boneId, fInOutWeight)
|| pFilter->FilterDof(kTrackBoneRotation, boneId, fInOutWeight)
)
{
if (boneData->GetParent())
{
u16 parentBoneId = boneData->GetParent()->GetBoneId();
if ( pFilter->FilterDof(kTrackBoneTranslation, parentBoneId, fInOutWeight)
|| pFilter->FilterDof(kTrackBoneRotation, parentBoneId, fInOutWeight)
)
{
++iLinks;
}
}
++iNumFilteredBones;
}
}
else
{
if (boneData->GetParent())
{
++iLinks;
}
++iNumFilteredBones;
}
}
//Store a map from bones filtered to actual bones
m_FilteredIndexToBoneIndex.Resize( iNumFilteredBones );
m_BoneIndexToFilteredIndex.Resize( iNumBones );
int iNumBonesMapped = 0;
for (u32 i = 0; i < iNumBones; i++)
{
const crBoneData *boneData = rData.GetBoneData(i);
if (pFilter)
{
u16 boneId = boneData->GetBoneId();
if ( pFilter->FilterDof(kTrackBoneTranslation, boneId, fInOutWeight)
|| pFilter->FilterDof(kTrackBoneRotation, boneId, fInOutWeight)
)
{
Assign(m_FilteredIndexToBoneIndex[iNumBonesMapped], i);
Assign(m_BoneIndexToFilteredIndex[i], iNumBonesMapped);
++iNumBonesMapped;
}
else
{
Assign(m_BoneIndexToFilteredIndex[i], 0xffff);
}
}
else
{
Assign(m_FilteredIndexToBoneIndex[iNumBonesMapped], i);
Assign(m_BoneIndexToFilteredIndex[i], i);
++iNumBonesMapped;
}
}
//And store links between actual bones
m_BoneLinks.Resize( iLinks );
int iLinksStored = 0;
for (u32 i = 0; i < iNumBones; i++)
{
const crBoneData *boneData = rData.GetBoneData(i);
if (boneData->GetParent())
{
u16 iFilteredIndex0 = m_BoneIndexToFilteredIndex[ boneData->GetParent()->GetIndex() ];
if (iFilteredIndex0 != 0xffff)
{
u16 iFilteredIndex1 = m_BoneIndexToFilteredIndex[ boneData->GetIndex() ];
if (iFilteredIndex1 != 0xffff)
{
Assign(m_BoneLinks[iLinksStored].m_iBone0, iFilteredIndex0);
Assign(m_BoneLinks[iLinksStored].m_iBone1, iFilteredIndex1);
++iLinksStored;
}
}
}
}
}
void StoreSkeleton::CustomDebugDraw3d(Color32 colLine, Color32 colJoint) const
{
//Draw the skeleton using the stored data and the skeleton data structure
const u32 iNumBonePositions = m_BonePositions.GetCount();
colJoint = debugPlayback::DebugRecorder::ModifyAlpha(colJoint);
colLine = debugPlayback::DebugRecorder::ModifyAlpha(colLine);
grcColor(colJoint);
for (u32 iBone = 0; iBone < iNumBonePositions; iBone++)
{
Vec3V vPos = m_BonePositions[ iBone ].Get();
grcDrawSphere(0.02f, vPos + m_Pos, 4, true, true);
}
grcWorldIdentity(); //Recover from grcDrawSpheres abuse
GameRecorder *pRecorder = GameRecorder::GetAppLevelInstance();
if (!pRecorder->HasSkelData(m_DataSignature))
{
grcColor(Color32(255,0,0,debugPlayback::DebugRecorder::GetGlobalAlphaScale()));
grcDrawLabel(m_Pos, "Failed to find matching skel data", true);
//Perhaps we can draw the archetype here?
return;
}
StoredSkelData *pSkelData = pRecorder->GetSkelData(m_DataSignature);
if(pSkelData)
{
//And draw all the links
const u32 iTotalLinks = pSkelData->m_BoneLinks.GetCount();
for (u32 iBoneLink = 0; iBoneLink < iTotalLinks; iBoneLink++)
{
Vec3V vPos0 = m_BonePositions[ pSkelData->m_BoneLinks[iBoneLink].m_iBone0 ].Get();
vPos0 += m_Pos;
Vec3V vPos1 = m_BonePositions[ pSkelData->m_BoneLinks[iBoneLink].m_iBone1 ].Get();
vPos1 += m_Pos;
grcDrawLine(vPos0, vPos1, colLine);
}
}
}
template<typename T>
static T Quantize10(float fValue, float fMin, float fMax)
{
fValue = Clamp(fValue, fMin, fMax);
fValue -= fMin;
fValue *= 1.0f/( (fMax - fMin) );
fValue += 0.00048875855327468231f ; // 0.5f * 0.00097751710654936461f; //Reduce rounding errors
fValue *= 1023.0f; // (1<<10 - 1) = 1023 ---> 1.0f / 1023.f = 0.00097751710654936461
return (T)fValue;
}
template<typename T>
static float DeQuantize10(T iValue, float fMin, float fMax)
{
float fValue = iValue * 0.00097751710654936461f; // (1<<10 - 1) = 1023 ---> 1.0f / 1023.f = 0.00097751710654936461
fValue *= (fMax-fMin);
fValue += fMin;
return fValue;
}
void StoreSkeleton::u32Quaternion::Set(QuatV_In r)
{
float vx = r.GetXf();
float vy = r.GetYf();
float vz = r.GetZf();
float vw = r.GetWf();
//Smallest 3 quantization (Quaternion assumed normalized)
u32 iLargest = 0;
float fLargest = vx;
float fLargestAbs = Abs(fLargest);
float fTest = vy;
float fTestAbs = Abs(fTest);
if (fTestAbs > fLargestAbs)
{
iLargest = 1;
fLargest = fTest;
fLargestAbs = fTestAbs;
}
fTest = vz;
fTestAbs = Abs(fTest);
if (fTestAbs > fLargestAbs)
{
iLargest = 2;
fLargest = fTest;
fLargestAbs = fTestAbs;
}
fTest = vw;
fTestAbs = Abs(fTest);
if (fTestAbs > fLargestAbs)
{
iLargest = 3;
fLargest = fTest;
fLargestAbs = fTestAbs;
}
//Do we need to flip the quaternion?
if ((fLargest > 0) ^ (fLargestAbs > 0))
{
vx = -vx;
vy = -vy;
vz = -vz;
vw = -vw;
}
//Serialize the index of the larget value
m_uPackedData = iLargest<<30;
float fRemaining[3];
switch(iLargest)
{
case 0:
fRemaining[0] = vy;
fRemaining[1] = vz;
fRemaining[2] = vw;
break;
case 1:
fRemaining[0] = vx;
fRemaining[1] = vz;
fRemaining[2] = vw;
break;
case 2:
fRemaining[0] = vx;
fRemaining[1] = vy;
fRemaining[2] = vw;
break;
default:
fRemaining[0] = vx;
fRemaining[1] = vy;
fRemaining[2] = vz;
break;
}
//Quantize each value from -0.707 to 0.707
//const float fOneOverSqrt2 = 1.0f / sqrtf(2.0f); // disabled by Svetli - replaced with hardcoded value
const float fOneOverSqrt2 = 0.7071067811865475f; // 1.0f / sqrtf(2.0f) (1.4142135623730950488f)
Assert(Abs(fRemaining[0]) <= fOneOverSqrt2);
Assert(Abs(fRemaining[1]) <= fOneOverSqrt2);
Assert(Abs(fRemaining[2]) <= fOneOverSqrt2);
u32 iTmpVal;
iTmpVal = Quantize10<u32>(fRemaining[0], -fOneOverSqrt2, fOneOverSqrt2);
m_uPackedData |= iTmpVal<<20;
iTmpVal = Quantize10<u32>(fRemaining[1], -fOneOverSqrt2, fOneOverSqrt2);
m_uPackedData |= iTmpVal<<10;
iTmpVal = Quantize10<u32>(fRemaining[2], -fOneOverSqrt2, fOneOverSqrt2);
m_uPackedData |= iTmpVal;
}
QuatV_Out StoreSkeleton::u32Quaternion::Get() const
{
//Smallest 3 quantization (Quaternion assumed normalized)
//Serialize the index of the largest value
u32 iLargest = m_uPackedData>>30;
float fOthers[3];
//Quantize each value from -0.707 to 0.707
//const float fOneOverSqrt2 = 1.0f / sqrtf(2.0f); // disabled by Svetli - replaced with hardcoded value
const float fOneOverSqrt2 = 0.7071067811865475f; // 1.0f / sqrtf(2.0f) (1.4142135623730950488f)
float fTotal2=0.0f;
u32 iTmpVal;
float fValue;
//const int iTenBits = ((1<<10) - 1); // disabled by Svetli - replaced with hardcoded value
const int iTenBits = 1023;
iTmpVal = (m_uPackedData>>20) & iTenBits;
fValue = DeQuantize10<u32>(iTmpVal, -fOneOverSqrt2, fOneOverSqrt2);
fTotal2 += fValue * fValue;
fOthers[0] = fValue;
iTmpVal = (m_uPackedData>>10) & iTenBits;
fValue = DeQuantize10<u32>(iTmpVal, -fOneOverSqrt2, fOneOverSqrt2);
fTotal2 += fValue * fValue;
fOthers[1] = fValue;
iTmpVal = m_uPackedData & iTenBits;
fValue = DeQuantize10<u32>(iTmpVal, -fOneOverSqrt2, fOneOverSqrt2);
fTotal2 += fValue * fValue;
fOthers[2] = fValue;
//Recalculate the largest value (quantization defined it as being positive by flipping the quaternion if needed)
float fLargest = sqrtf(1.0f - fTotal2);
//Fill the quaternion
switch(iLargest)
{
case 0:
return QuatV(fLargest, fOthers[0], fOthers[1], fOthers[2]);
case 1:
return QuatV(fOthers[0], fLargest, fOthers[1], fOthers[2]);
case 2:
return QuatV(fOthers[0], fOthers[1], fLargest, fOthers[2]);
}
return QuatV(fOthers[0], fOthers[1], fOthers[2], fLargest);
}
bool StoreSkeleton::ms_bUseLowLodSkeletons = true;
bool StoreSkeleton::Replay(phInst *pInst) const
{
if (!pInst)
{
return false;
}
GameRecorder *pRecorder = GameRecorder::GetAppLevelInstance();
if (!pRecorder->HasSkelData(m_DataSignature))
{
return false;
}
fwEntity *pEntity = (fwEntity *)pInst->GetUserData();
if (!pEntity)
{
return false;
}
crSkeleton *pSkel = pEntity->GetSkeleton();
if (!pSkel)
{
return false;
}
const StoredSkelData *pSkelData = pRecorder->GetSkelData(m_DataSignature);
if (pSkelData)
{
if (Verifyf(pSkelData->m_SkelSignature == pSkel->GetSkeletonData().GetSignature(), "Recorded skel does not match current skel, cannot play"))
{
const int kBoneCount = m_BonePositions.GetCount();
if (m_BoneRotations.GetCount() == kBoneCount)
{
//This has the potentially useful but likely annoying side effect of leaving the ped at this location.
//setting the matrix throws assertions when attached.
//if (!pEntity->GetIsAttached())
//{
// //Set the position for draw culling purposes
// pEntity->SetPosition(RCC_VECTOR3(m_Pos));
//}
for (int i=0 ; i<kBoneCount ; i++)
{
Mat34V mat;
QuatV vRot = m_BoneRotations[i].Get();
Mat34VFromQuatV(mat, vRot, m_BonePositions[ i ].Get() + m_Pos);
pSkel->SetGlobalMtx(pSkelData->m_FilteredIndexToBoneIndex[i], mat);
}
//Fill in the rest, hoping all parent matrices are filled out in order
for (int i=0 ; i<pSkel->GetSkeletonData().GetNumBones() ; i++)
{
if (pSkelData->m_BoneIndexToFilteredIndex[i] == 0xffff)
{
const crBoneData *boneData = pSkel->GetSkeletonData().GetBoneData(i);
if (boneData)
{
if (Verifyf(boneData->GetParent() && (boneData->GetParent()->GetIndex() < i), "Need parent earlier in list to fill in data"))
{
Mat34V matLocal;
QuatV vRot = boneData->GetDefaultRotation();
Mat34VFromQuatV(matLocal, vRot, boneData->GetDefaultTranslation());
//Need to multiply this into the parent bone matrix
Mat34V matParent;
pSkel->GetGlobalMtx(boneData->GetParent()->GetIndex(), matParent);
Mat34V matBone;
Transform(matBone, matParent, matLocal);
pSkel->SetGlobalMtx(i, matBone);
}
}
}
}
}
}
}
return false;
}
void StoreSkeleton::DebugDraw3d(debugPlayback::eDrawFlags drawFlags) const
{
Color32 colLine(m_iColor);
Color32 colJoint(0,0,200,255);
if ((TIME.GetFrameCount() & 0x7) < 0x3)
{
if (drawFlags & eDrawSelected)
{
colJoint.SetRed(255);
colLine.SetBlue(0);
}
if (drawFlags & eDrawHovered)
{
colJoint.SetGreen(255);
colLine.SetBlue(128);
}
GameRecorder *pRecorder = GameRecorder::GetAppLevelInstance();
if (pRecorder)
{
const EventBase *pSelected = pRecorder->GetSelectedEvent();
if (pSelected)
{
if(pSelected->IsPhysicsEvent())
{
const PhysicsEvent *pPhysEv = static_cast<const PhysicsEvent*>(pSelected);
if (pPhysEv->IsInstInvolved(m_rInst))
{
//Looks like we're browsing an event that is this inst, highlight the skeleton
colJoint.Set(255,0,0);
}
}
}
const EventBase *pHovered = pRecorder->GetWasHoveredEvent();
if (pHovered)
{
if(pHovered->IsPhysicsEvent())
{
const PhysicsEvent *pPhysEv = static_cast<const PhysicsEvent*>(pHovered);
if (pPhysEv->IsInstInvolved(m_rInst))
{
//Looks like we're browsing an event that is this inst, highlight the skeleton
colJoint.Set(0,255,0);
}
}
}
}
}
DebugRecorder::ModifyAlpha(colJoint);
DebugRecorder::ModifyAlpha(colLine);
CustomDebugDraw3d(colLine, colJoint);
}
bool StoreSkeleton::IsSkeletonEvent(const debugPlayback::EventBase *pEvent)
{
const char *pEventTypeId = pEvent->GetEventType();
return (StoreSkeletonPreUpdate::GetStaticEventType() == pEventTypeId)
|| (StoreSkeletonPostUpdate::GetStaticEventType() == pEventTypeId);
}
StoreSkeleton::StoreSkeleton(const debugPlayback::CallstackHelper rCallstack, const phInst *pInst, const fwEntity *pEntity, const char *pSkeletonName, Color32 color)
:debugPlayback::PhysicsEvent(rCallstack, pInst)
,m_SkeletonName(pSkeletonName)
,m_DataSignature(0)
,m_iColor(color.GetColor())
{
if (pEntity)
{
//Cache the skeleton temp data (global matrices for easy playback
const crSkeleton *pSkel = pEntity->GetSkeleton();
if (pSkel)
{
const int kBoneCount = pSkel->GetBoneCount();
const crSkeletonData &rSkelData = pSkel->GetSkeletonData();
m_DataSignature = rSkelData.GetSignature();
crFrameFilter *filter = ms_bUseLowLodSkeletons ? pEntity->GetLowLODFilter() : 0;
m_DataSignature ^= filter ? 0x12345678 : 0;
GameRecorder *pRecorder = GameRecorder::GetAppLevelInstance();
if (!pRecorder->HasSkelData(m_DataSignature))
{
StoredSkelData *pSkelData = rage_new StoredSkelData;
pSkelData->Create( rSkelData, filter );
pRecorder->AddSkelData(pSkelData, m_DataSignature);
}
u32 iNumBonesToSave = kBoneCount;
if (filter)
{
iNumBonesToSave = 0;
for (int boneIndex = 0; boneIndex < kBoneCount; boneIndex++)
{
const crBoneData *boneData = rSkelData.GetBoneData(boneIndex);
float inOutWeight;
if (filter->FilterDof(kTrackBoneTranslation, boneData->GetBoneId(), inOutWeight) || filter->FilterDof(kTrackBoneRotation, boneData->GetBoneId(), inOutWeight))
{
++iNumBonesToSave;
}
}
}
m_BonePositions.Resize( iNumBonesToSave );
if (pRecorder->PosePedsIfPossible())
{
m_BoneRotations.Resize( iNumBonesToSave );
}
int iNumSavedBones = 0;
for (int boneIndex=0 ; boneIndex<kBoneCount ; boneIndex++)
{
if(filter)
{
const crBoneData *boneData = rSkelData.GetBoneData(boneIndex);
float inOutWeight;
if (!filter->FilterDof(kTrackBoneTranslation, boneData->GetBoneId(), inOutWeight) && !filter->FilterDof(kTrackBoneRotation, boneData->GetBoneId(), inOutWeight))
{
continue;
}
}
Mat34V mat;
pSkel->GetGlobalMtx(boneIndex, mat);
if (pRecorder->PosePedsIfPossible())
{
QuatV vQ = QuatVFromMat34V(mat);
vQ = Normalize( vQ );
m_BoneRotations[iNumSavedBones].Set( vQ );
}
m_BonePositions[iNumSavedBones].Set(mat.GetCol3() - m_Pos);
++iNumSavedBones;
}
}
}
}
DR_EVENT_IMP(MoveGetValue);
DR_EVENT_IMP(MoveSetValue);
DR_EVENT_IMP(MoveTransitionStringCollection);
DR_EVENT_IMP(MoveSetValueStringCollection);
DR_EVENT_IMP(MoveConditionStringCollection);
DR_EVENT_IMP(AnimMotionTreeTracker);
DR_EVENT_IMP(StoreSkeletonPreUpdate);
DR_EVENT_IMP(StoreSkeletonPostUpdate);
//With a mvNetwork we need to find a creature object. This can be compared with entity
//associated with the current selected phInst's creature object.
const char *GetStringForCondition(int iConditionType)
{
switch(iConditionType)
{
case mvConstant::kConditionInRange:
return "InRange";
case mvConstant::kConditionOutOfRange:
return "OutOfRange";
case mvConstant::kConditionOnRequest:
return "OnRequest";
case mvConstant::kConditionOnFlag:
return "OnFlag";
case mvConstant::kConditionAtEvent:
return "AtEvent";
case mvConstant::kConditionGreaterThan:
return ">";
case mvConstant::kConditionGreaterThanEqual:
return ">=";
case mvConstant::kConditionLessThan:
return "<";
case mvConstant::kConditionLessThanEqual:
return "<=";
case mvConstant::kConditionLifetimeGreaterThan:
return "Lifetime>";
case mvConstant::kConditionLifetimeLessThan:
return "Lifetime<";
case mvConstant::kConditionBoolEquals:
return "BoolEquals";
default:
Assertf(0, "Unknown condition type %d", iConditionType);
}
return "<unknown condition>";
}
const char *GetStringForResult(char *buffer, int iBufferSize, int iConditionIndex, int iConditionResult)
{
const char *pStr = "passed";
switch(iConditionResult)
{
case -1:
pStr = "prepassed";
break;
case 0:
pStr = "failed";
break;
default:
break;
}
formatf(buffer, iBufferSize, "[%d] %s", iConditionIndex, pStr);
return buffer;
}
//Find a nice way of making these threadsafe...
#if __DEV || __BANK
__THREAD const mvNodeStateBaseDef* lastFrom;
__THREAD const mvNodeStateBaseDef* lastTo;
__THREAD u32 iLastFrame;
__THREAD MoveConditionStringCollection *pCurrentCollection;
void DR_TrackMoveInterface::RecordEndConditions() const
{
pCurrentCollection = 0;
}
void DR_TrackMoveInterface::RecordCondition(const mvNetworkDef* networkDef, const crCreature* creature, int iPassed, int iConditionType, int iConditionIndex, const mvNodeStateBaseDef* from, const mvNodeStateBaseDef* to)
{
const phInst *pInst = debugPlayback::GetInstFromCreature(creature);
if (!pInst)
{
return;
}
if ( (pCurrentCollection)
&& (lastFrom == from)
&& (lastTo == to)
&& (iLastFrame == TIME.GetFrameCount())
)
{
//Just leave pCurrentCollection as is
}
else
{
//New collection
pCurrentCollection = AddPhysicsEvent<MoveConditionStringCollection>(pInst);
if (pCurrentCollection)
{
atString networkNameString(networkDef->GetName());
if(networkNameString.StartsWith("memory:$"))
{
int index = networkNameString.LastIndexOf(":");
if(index != -1)
{
atString tempString(&networkNameString.c_str()[index + 1]);
networkNameString = tempString;
}
}
atHashString toStr(to->m_id);
atVarString name("MV: %s ?-> %s", networkNameString.c_str(), toStr.TryGetCStr());
pCurrentCollection->SetName(name.c_str());
//Cache the network name
pCurrentCollection->AddString("network", networkDef->GetName());
//Notate the nodes being potentially transitioned
char bufferFrom[64];
atHashString fromStr(from->m_id);
formatf(bufferFrom,"from %s", fromStr.TryGetCStr());
char bufferTo[64];
formatf(bufferTo,"to %s", toStr.TryGetCStr());
pCurrentCollection->AddString(bufferFrom, bufferTo);
//Store information that allows us to make sure we continue to track the same
//transition. TODO - replace with a token stored in the calling code.
iLastFrame = TIME.GetFrameCount();
lastFrom = from;
lastTo = to;
}
}
if (pCurrentCollection)
{
//Add conditionals to the list
char buffer[64];
pCurrentCollection->AddString(GetStringForResult(buffer, sizeof(buffer), iConditionIndex, iPassed), GetStringForCondition(iConditionType));
}
}
void DR_TrackMoveInterface::RecordTransition(const mvNetworkDef* networkDef, const crCreature* creature, const mvNodeStateBaseDef* stateMachine, const mvNodeStateBaseDef* from, const mvNodeStateBaseDef* to)
{
const phInst *pInst = debugPlayback::GetInstFromCreature(creature);
if (!pInst)
{
return;
}
MoveTransitionStringCollection *pEvent = AddPhysicsEvent<MoveTransitionStringCollection>(pInst);
if (pEvent)
{
atString networkNameString(networkDef->GetName());
if(networkNameString.StartsWith("memory:$"))
{
int index = networkNameString.LastIndexOf(":");
if(index != -1)
{
atString tempString(&networkNameString.c_str()[index + 1]);
networkNameString = tempString;
}
}
atVarString name("%s: TRANSITION", networkNameString.c_str());
pEvent->SetName(name.c_str());
pEvent->AddString("network", networkNameString);
//Notate the nodes being potentially transitioned
char bufferFrom[64];
char bufferTo[128];
atHashString machineStr(stateMachine->m_id);
atHashString fromStr(from->m_id);
atHashString toStr(to->m_id);
formatf(bufferFrom,"in %s", machineStr.TryGetCStr());
formatf(bufferTo,"from %s to %s", fromStr.TryGetCStr(), toStr.TryGetCStr());
pEvent->AddString(bufferFrom, bufferTo);
}
}
void DR_TrackMoveInterface::RecordSetAnimValue(const phInst *pInst, const char *pValueName, float fValue) const
{
MoveSetValue *pEvent = AddPhysicsEvent<MoveSetValue>(pInst);
if (pEvent)
{
char buffer[64];
formatf(buffer,"MV:SET: %s", pValueName);
pEvent->SetData(fValue, buffer);
}
}
void DR_TrackMoveInterface::RecordGetAnimValue(const phInst *pInst, const char *pValueName, float fValue) const
{
MoveGetValue *pEvent = AddPhysicsEvent<MoveGetValue>(pInst);
if (pEvent)
{
char buffer[64];
formatf(buffer,"MV:GET: %s", pValueName);
pEvent->SetData(fValue, buffer);
}
}
void DR_TrackMoveInterface::RecordSetAnimValue(const phInst *pInst, const char *pStringName,
const char *pString1id, const char *pString1,
const char *pString2id, const char *pString2) const
{
MoveSetValueStringCollection *pEvent = AddPhysicsEvent<MoveSetValueStringCollection>(pInst);
if (pEvent)
{
pEvent->SetName(pStringName);
pEvent->AddString(pString1id, pString1);
if (pString2id && Verifyf(pString2, "%s identified but no value string passed in", pString2id))
{
pEvent->AddString(pString2id, pString2);
}
}
}
#endif
//Included last to get all the class declarations within the CPP file
using namespace rage;
using namespace debugPlayback;
#include "DebugAnimation_parser.h"
#endif //DR_ANIMATIONS_ENABLED