3567 lines
97 KiB
C++
3567 lines
97 KiB
C++
#include "debugrecorder.h"
|
|
|
|
#if DR_ENABLED
|
|
|
|
//RAGE
|
|
#include "ai/task/task.h"
|
|
#include "ai/task/taskspew.h"
|
|
#include "entity/entity.h"
|
|
#include "fwanimation/move.h"
|
|
#include "input/keyboard.h"
|
|
#include "input/keys.h"
|
|
#include "move/move_state.h"
|
|
#include "physics/debugEvents.h"
|
|
#include "system/param.h"
|
|
#include "vectormath/vec3v.h"
|
|
|
|
//GAME
|
|
#include "camera/CamInterface.h"
|
|
#include "camera/debug/DebugDirector.h"
|
|
#include "camera/viewports/ViewportManager.h"
|
|
#include "debug/DebugWanted.h"
|
|
#include "debug/DebugAnimation.h"
|
|
#include "network/debug/NetworkDebug.h"
|
|
#include "peds/ped.h"
|
|
#include "peds/pedfactory.h"
|
|
#include "peds/pedIntelligence.h"
|
|
#include "peds/horse/horseComponent.h"
|
|
#include "physics/gtaInst.h"
|
|
#include "scene/world/GameWorld.h"
|
|
#include "streaming/streaming.h"
|
|
#include "task/motion/taskMotionBase.h"
|
|
#include "ai/task/taskspew.h"
|
|
#include "vehicles/vehicle.h"
|
|
#include "vehicles/vehiclefactory.h"
|
|
#include "vehicleAi/VehicleIntelligence.h"
|
|
#include "vehicleAi/task/TaskVehicleGoToAutomobile.h"
|
|
|
|
PARAM(DR_AllInsts, "Default to recording all instances rather than the focus inst");
|
|
PARAM(DR_LoadFilter, "Load a file describing the filters to switch on initially");
|
|
PARAM(DR_Autostart, "Automatically start recording");
|
|
PARAM(DR_TrackMode, "Set mode to start tracking at");
|
|
|
|
#if __WIN32
|
|
#pragma warning(push)
|
|
#pragma warning(disable:4201) // nonstandard extension used : nameless struct/union
|
|
#endif
|
|
|
|
void DisableAllEventTypes();
|
|
|
|
namespace rage
|
|
{
|
|
EXT_PFD_DECLARE_ITEM_SLIDER(BoundDrawDistance);
|
|
}
|
|
extern void FixupObject(CEntity *pObject, const Matrix34 &testMat);
|
|
atFixedArray<debugPlayback::phInstId, GameRecorder::kMaxInstsPerFrame> GameRecorder::m_FrameInsts;
|
|
atFixedArray<debugPlayback::phInstId, GameRecorder::kMaxInstsPerFrame> GameRecorder::m_LastFrameInsts;
|
|
atArray<DebugReplayObjectInfo*> DebugReplayObjectInfo::sm_ChainStarts;
|
|
atArray<debugPlayback::phInstId> DebugReplayObjectInfo::sm_PendingChainListFrom;
|
|
atArray<debugPlayback::phInstId> DebugReplayObjectInfo::sm_PendingChainListTo;
|
|
|
|
static bool sbTrackPlayer=false;
|
|
static float sfTrackRange = 16.0f;
|
|
|
|
DebugReplayObjectInfo::DebugReplayObjectInfo()
|
|
: m_vBBMax(V_ZERO)
|
|
, m_vBBMin(V_ZERO)
|
|
, mp_NextInChain(0)
|
|
, mp_UserData(0)
|
|
, m_ClassType(0)
|
|
, m_ChainIndex(-1)
|
|
, m_bIsPlayer(false)
|
|
, m_bPlayerIsDriving(false)
|
|
{
|
|
}
|
|
|
|
DebugReplayObjectInfo::DebugReplayObjectInfo(phInst *pInst)
|
|
: debugPlayback::NewObjectData(pInst)
|
|
, mp_NextInChain(0)
|
|
, mp_UserData(pInst->GetUserData())
|
|
, m_ClassType(0)
|
|
, m_ChainIndex(-1)
|
|
{
|
|
if(pInst)
|
|
{
|
|
m_ClassType = pInst->GetClassType();
|
|
switch (m_ClassType)
|
|
{
|
|
case PH_INST_FRAG_VEH:
|
|
case PH_INST_FRAG_PED:
|
|
case PH_INST_FRAG_OBJECT:
|
|
case PH_INST_VEH:
|
|
case PH_INST_PED:
|
|
case PH_INST_OBJECT: {
|
|
CEntity *pObject = (CEntity*)pInst->GetUserData();
|
|
if (pObject)
|
|
{
|
|
m_ModelName = pObject->GetModelName();
|
|
|
|
//Store information about the player being involved
|
|
if (pObject->GetIsTypePed())
|
|
{
|
|
CPed *pPed = (CPed*)pObject;
|
|
m_bIsPlayer = pPed->IsLocalPlayer();
|
|
#if ENABLE_HORSE
|
|
if (!m_bIsPlayer)
|
|
{
|
|
const CHorseComponent* pHrs = pPed->GetHorseComponent();
|
|
if (pHrs)
|
|
{
|
|
if (pHrs->GetFreshnessCtrl().IsMountedByPlayer())
|
|
{
|
|
m_bPlayerIsDriving = true;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
else if (pObject->GetIsTypeVehicle())
|
|
{
|
|
CVehicle *pVeh = (CVehicle*)pObject;
|
|
CPed *pDriver = pVeh->GetDriver();
|
|
m_bPlayerIsDriving = pDriver && pDriver->IsPlayer();
|
|
}
|
|
|
|
if (pObject->GetArchetype())
|
|
{
|
|
m_vBBMax = RCC_VEC3V(pObject->GetArchetype()->GetBoundingBoxMax());
|
|
m_vBBMin = RCC_VEC3V(pObject->GetArchetype()->GetBoundingBoxMin());
|
|
}
|
|
else
|
|
{
|
|
m_vBBMax = RCC_VEC3V(pObject->GetBoundingBoxMax());
|
|
m_vBBMin = RCC_VEC3V(pObject->GetBoundingBoxMin());
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case PH_INST_FRAG_GTA: // marker for start of GTA fragInst's
|
|
case PH_INST_GTA:
|
|
case PH_INST_PROJECTILE:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void DebugReplayObjectInfo::ClearReplayObject()
|
|
{
|
|
if (m_rReplayObject)
|
|
{
|
|
CGameWorld::Remove(m_rReplayObject);
|
|
|
|
if(m_rReplayObject->GetIsTypeVehicle())
|
|
CVehicleFactory::GetFactory()->Destroy((CVehicle*)m_rReplayObject.Get());
|
|
else if(m_rReplayObject->GetIsTypePed())
|
|
CPedFactory::GetFactory()->DestroyPed((CPed*)m_rReplayObject.Get());
|
|
else if(m_rReplayObject->GetIsTypeObject())
|
|
CObjectPopulation::DestroyObject((CObject*)m_rReplayObject.Get());
|
|
else
|
|
delete m_rReplayObject;
|
|
}
|
|
}
|
|
|
|
void DebugReplayObjectInfo::RebuildChainsFromScratchCB(debugPlayback::DataBase& rData)
|
|
{
|
|
DebugReplayObjectInfo *pNOD = dynamic_cast<DebugReplayObjectInfo *>(&rData);
|
|
if (pNOD && (pNOD->m_ChainIndex>-1))
|
|
{
|
|
Assert(!pNOD->mp_NextInChain);
|
|
pNOD->mp_NextInChain = sm_ChainStarts[ pNOD->m_ChainIndex ];
|
|
sm_ChainStarts[ pNOD->m_ChainIndex ] = pNOD;
|
|
}
|
|
}
|
|
|
|
static int iHighestIndex=-1;
|
|
void DebugReplayObjectInfo::GetHighestChainIndexCB(debugPlayback::DataBase& rData)
|
|
{
|
|
DebugReplayObjectInfo *pNOD = dynamic_cast<DebugReplayObjectInfo *>(&rData);
|
|
if (pNOD && pNOD->m_ChainIndex)
|
|
{
|
|
iHighestIndex = Max(pNOD->m_ChainIndex, iHighestIndex);
|
|
}
|
|
}
|
|
|
|
void DebugReplayObjectInfo::RebuildChainsFromScratch()
|
|
{
|
|
sm_ChainStarts.Reset();
|
|
debugPlayback::DebugRecorder *pInstance = debugPlayback::DebugRecorder::GetInstance();
|
|
if(pInstance)
|
|
{
|
|
iHighestIndex = -1;
|
|
pInstance->CallOnAllStoredData(GetHighestChainIndexCB);
|
|
if (iHighestIndex >= 0)
|
|
{
|
|
sm_ChainStarts.Resize(iHighestIndex + 1);
|
|
pInstance->CallOnAllStoredData(RebuildChainsFromScratchCB);
|
|
}
|
|
}
|
|
}
|
|
|
|
void DebugReplayObjectInfo::UpdateChains()
|
|
{
|
|
debugPlayback::DebugRecorder *pInstance = debugPlayback::DebugRecorder::GetInstance();
|
|
if(pInstance)
|
|
{
|
|
const int kCount = sm_PendingChainListFrom.GetCount();
|
|
for (int i=0 ; i<kCount ; i++)
|
|
{
|
|
DebugReplayObjectInfo *pFromInfo = dynamic_cast<DebugReplayObjectInfo*>(pInstance->GetSharedData(sm_PendingChainListFrom[i].ToU32()));
|
|
DebugReplayObjectInfo *pToInfo = dynamic_cast<DebugReplayObjectInfo*>(pInstance->GetSharedData(sm_PendingChainListTo[i].ToU32()));
|
|
|
|
if (pFromInfo && pToInfo)
|
|
{
|
|
Assert(pFromInfo->mp_UserData == pToInfo->mp_UserData);
|
|
if ((pFromInfo->m_ChainIndex > -1) && (pToInfo->m_ChainIndex > -1))
|
|
{
|
|
Assertf(pFromInfo->m_ChainIndex == pToInfo->m_ChainIndex, "Mismatched chains? [%d != %d]", pToInfo->m_ChainIndex, pFromInfo->m_ChainIndex);
|
|
}
|
|
else
|
|
{
|
|
if (pFromInfo->m_ChainIndex > -1)
|
|
{
|
|
//Add pTo to the same chain at the head
|
|
pToInfo->mp_NextInChain = sm_ChainStarts[ pFromInfo->m_ChainIndex ];
|
|
sm_ChainStarts[ pFromInfo->m_ChainIndex ] = pToInfo;
|
|
pToInfo->m_ChainIndex = pFromInfo->m_ChainIndex;
|
|
}
|
|
else if (pToInfo->m_ChainIndex > -1)
|
|
{
|
|
//Add pFrom to the same chain at the head
|
|
pFromInfo->mp_NextInChain = sm_ChainStarts[ pToInfo->m_ChainIndex ];
|
|
sm_ChainStarts[ pToInfo->m_ChainIndex ] = pFromInfo;
|
|
pFromInfo->m_ChainIndex = pToInfo->m_ChainIndex;
|
|
}
|
|
else
|
|
{
|
|
//New chain, put both in
|
|
int iNewIndex = sm_ChainStarts.GetCount();
|
|
sm_ChainStarts.Grow(16) = pToInfo;
|
|
pToInfo->mp_NextInChain = pFromInfo;
|
|
pToInfo->m_ChainIndex = iNewIndex;
|
|
pFromInfo->m_ChainIndex = iNewIndex;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
sm_PendingChainListFrom.Reset();
|
|
sm_PendingChainListTo.Reset();
|
|
}
|
|
|
|
void DebugReplayObjectInfo::SetNextInst(const phInst *pNew)
|
|
{
|
|
sm_PendingChainListFrom.Grow(16) = m_rInst;
|
|
sm_PendingChainListTo.Grow(16) = debugPlayback::phInstId(pNew);
|
|
}
|
|
|
|
void DebugReplayObjectInfo::DrawBox(Mat34V &rMat, Color32 col) const
|
|
{
|
|
Vec3V vMin, vMax;
|
|
GetBoundingMinMax(vMin, vMax);
|
|
|
|
const Mat34V prevWorldMtx(grcWorldMtx(rMat));
|
|
grcColor(col);
|
|
Vec3V a(vMin.GetXf(),vMin.GetYf(),vMin.GetZf());
|
|
Vec3V b(vMax.GetXf(),vMin.GetYf(),vMin.GetZf());
|
|
Vec3V c(vMax.GetXf(),vMax.GetYf(),vMin.GetZf());
|
|
Vec3V d(vMin.GetXf(),vMax.GetYf(),vMin.GetZf());
|
|
Vec3V e(vMin.GetXf(),vMin.GetYf(),vMax.GetZf());
|
|
Vec3V f(vMax.GetXf(),vMin.GetYf(),vMax.GetZf());
|
|
Vec3V g(vMax.GetXf(),vMax.GetYf(),vMax.GetZf());
|
|
Vec3V h(vMin.GetXf(),vMax.GetYf(),vMax.GetZf());
|
|
grcBegin(drawLines,24);
|
|
grcVertex3f(a); grcVertex3f(b);
|
|
grcVertex3f(b); grcVertex3f(c);
|
|
grcVertex3f(c); grcVertex3f(d);
|
|
grcVertex3f(d); grcVertex3f(a);
|
|
|
|
grcVertex3f(a); grcVertex3f(e);
|
|
grcVertex3f(b); grcVertex3f(f);
|
|
grcVertex3f(c); grcVertex3f(g);
|
|
grcVertex3f(d); grcVertex3f(h);
|
|
|
|
grcVertex3f(e); grcVertex3f(f);
|
|
grcVertex3f(f); grcVertex3f(g);
|
|
grcVertex3f(g); grcVertex3f(h);
|
|
grcVertex3f(h); grcVertex3f(e);
|
|
grcEnd();
|
|
grcWorldMtx(prevWorldMtx);
|
|
}
|
|
|
|
phInst *DebugReplayObjectInfo::CreateObject(debugPlayback::TextOutputVisitor &rText, Mat34V &initialMatrix, bool bInvolvePlayer)
|
|
{
|
|
//Create an object that matches the original recorded as closely as possible
|
|
|
|
fwModelId modelId;
|
|
/*CBaseModelInfo* pModelInfo =*/ CModelInfo::GetBaseModelInfoFromName(m_ModelName.c_str(), &modelId);
|
|
|
|
rText.AddLine("Creating %s", m_ModelName.c_str());
|
|
rText.PushIndent();
|
|
switch(m_ClassType)
|
|
{
|
|
case PH_INST_FRAG_PED:
|
|
case PH_INST_PED:
|
|
{
|
|
if (m_bIsPlayer && bInvolvePlayer)
|
|
{
|
|
//Clear the current player ped
|
|
CPed* pPlayerPed = CGameWorld::FindLocalPlayer();
|
|
if (pPlayerPed)
|
|
{
|
|
rText.AddLine("Destroying old player");
|
|
CPedFactory::GetFactory()->DestroyPed(pPlayerPed);
|
|
}
|
|
|
|
//Create a new one.
|
|
const CControlledByInfo localPlayerControl(false, true);
|
|
m_rReplayObject = pPlayerPed = CPedFactory::GetFactory()->CreatePlayerPed(localPlayerControl, modelId.GetModelIndex(), &RCC_MATRIX34(initialMatrix));
|
|
if (pPlayerPed)
|
|
{
|
|
rText.AddLine("Setting up new player");
|
|
pPlayerPed->PopTypeSet(POPTYPE_PERMANENT);
|
|
pPlayerPed->SetDefaultDecisionMaker();
|
|
pPlayerPed->SetCharParamsBasedOnManagerType();
|
|
pPlayerPed->GetPedIntelligence()->SetDefaultRelationshipGroup();
|
|
|
|
//CGameWorld::AddPlayerToWorld(pPlayerPed, pos);
|
|
// CStreaming::SetMissionDoesntRequireObject(index, CModelInfo::GetStreamingModuleId());
|
|
|
|
CModelInfo::SetAssetsAreDeletable(modelId);
|
|
|
|
pPlayerPed->GetPedIntelligence()->AddTaskDefault(pPlayerPed->GetCurrentMotionTask()->CreatePlayerControlTask());
|
|
|
|
pPlayerPed->InitSpecialAbility();
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
//Create an AI ped
|
|
rText.AddLine("Creating AI actor");
|
|
const CControlledByInfo localAiControl(false, false);
|
|
CPed *pNewPed = CPedFactory::GetFactory()->CreatePed(localAiControl, modelId, &RCC_MATRIX34(initialMatrix), true, true, false);
|
|
m_rReplayObject = pNewPed;
|
|
|
|
if (m_bPlayerIsDriving && bInvolvePlayer)
|
|
{
|
|
CPed* pPlayerPed = CGameWorld::FindLocalPlayer();
|
|
if (pPlayerPed)
|
|
{
|
|
rText.AddLine("Involving player");
|
|
rText.PushIndent();
|
|
|
|
//Remove from their current vehicle!
|
|
if (pPlayerPed->GetIsAttachedInCar())
|
|
{
|
|
rText.AddLine("Remove player from old vehicle");
|
|
pPlayerPed->SetPedOutOfVehicle();
|
|
}
|
|
else if (pPlayerPed->GetMyMount())
|
|
{
|
|
rText.AddLine("Remove player from old mount");
|
|
pPlayerPed->SetPedOffMount();
|
|
}
|
|
|
|
//Update interior state from the vehicle being warped into
|
|
if (pPlayerPed->m_nFlags.bInMloRoom || pPlayerPed->GetIsRetainedByInteriorProxy() )
|
|
{
|
|
CGameWorld::Remove(pPlayerPed);
|
|
CGameWorld::Add(pPlayerPed, m_rReplayObject->GetInteriorLocation());
|
|
}
|
|
|
|
rText.AddLine("Set on new mount");
|
|
pPlayerPed->GetPedIntelligence()->FlushImmediately();
|
|
pPlayerPed->SetPedOnMount(pNewPed);
|
|
|
|
rText.PopIndent();
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
break;
|
|
case PH_INST_FRAG_OBJECT:
|
|
case PH_INST_OBJECT:
|
|
{
|
|
m_rReplayObject = CObjectPopulation::CreateObject(modelId, ENTITY_OWNEDBY_RANDOM, true);
|
|
}
|
|
break;
|
|
case PH_INST_FRAG_VEH:
|
|
case PH_INST_VEH:
|
|
{
|
|
rText.AddLine("Creating new vehicle");
|
|
CVehicle *pNewVehicle = CVehicleFactory::GetFactory()->Create(modelId, ENTITY_OWNEDBY_DEBUG, POPTYPE_TOOL, &RCC_MATRIX34(initialMatrix));
|
|
m_rReplayObject = pNewVehicle;
|
|
if (pNewVehicle && m_bPlayerIsDriving && bInvolvePlayer)
|
|
{
|
|
rText.AddLine("Putting player in vehicle!");
|
|
//Grab player and put them in the vehicle
|
|
CPed* pPlayerPed = CGameWorld::FindLocalPlayer();
|
|
if (pPlayerPed)
|
|
{
|
|
rText.PushIndent();
|
|
//Remove from their current vehicle!
|
|
//Remove from their current vehicle!
|
|
if (pPlayerPed->GetIsAttachedInCar())
|
|
{
|
|
rText.AddLine("Remove from old vehicle");
|
|
pPlayerPed->SetPedOutOfVehicle();
|
|
}
|
|
else if (pPlayerPed->GetIsAttachedOnMount())
|
|
{
|
|
rText.AddLine("Remove from old mount");
|
|
pPlayerPed->SetPedOffMount();
|
|
}
|
|
|
|
//Update interior state from the vehicle being warped into
|
|
if (pPlayerPed->m_nFlags.bInMloRoom || pPlayerPed->GetIsRetainedByInteriorProxy() )
|
|
{
|
|
CGameWorld::Remove(pPlayerPed);
|
|
CGameWorld::Add(pPlayerPed, pNewVehicle->GetInteriorLocation());
|
|
}
|
|
|
|
rText.AddLine("Set in new vehicle");
|
|
pPlayerPed->GetPedIntelligence()->FlushImmediately();
|
|
pPlayerPed->SetPedInVehicle(pNewVehicle,0,0); //Drivers seat is zero?
|
|
|
|
rText.PopIndent();
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case PH_INST_PROJECTILE:
|
|
Errorf("Replay with new object not yet supported for PH_INST_PROJECTILE");
|
|
break;
|
|
case PH_INST_FRAG_GTA:
|
|
Errorf("Replay with new object not yet supported for PH_INST_FRAG_GTA");
|
|
break;
|
|
case PH_INST_GTA:
|
|
Errorf("Replay with new object not yet supported for PH_INST_GTA");
|
|
break;
|
|
default:
|
|
Errorf("Replay with new object not yet supported for '%d'", m_ClassType);
|
|
break;
|
|
}
|
|
|
|
if (m_rReplayObject)
|
|
{
|
|
rText.AddLine("Fixup object");
|
|
FixupObject(m_rReplayObject, RCC_MATRIX34(initialMatrix));
|
|
}
|
|
rText.PopIndent();
|
|
return m_rReplayObject ? m_rReplayObject->GetCurrentPhysicsInst() : 0;
|
|
}
|
|
|
|
|
|
const char *DebugReplayObjectInfo::GetObjectDescription(char *pBuffer, int iBuffersize) const
|
|
{
|
|
if(m_rInst.IsValid())
|
|
{
|
|
if (m_ModelName.c_str())
|
|
{
|
|
formatf(pBuffer, iBuffersize, "Class: %d [%s]", m_ClassType, m_ModelName.c_str());
|
|
}
|
|
else
|
|
{
|
|
formatf(pBuffer, iBuffersize, "Class: %d [0x%x]", m_ClassType, m_rInst.GetObject());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
strncpy(pBuffer, "<<Invalid>>", iBuffersize);
|
|
}
|
|
return pBuffer;
|
|
}
|
|
|
|
debugPlayback::NewObjectData *DebugReplayObjectInfo::CreateNewObjectData(phInst *pInst)
|
|
{
|
|
DR_MEMORY_HEAP();
|
|
return rage_new DebugReplayObjectInfo(pInst);
|
|
}
|
|
|
|
RegdEnt DebugReplayObjectInfo::m_rReplayObject;
|
|
|
|
GameRecorder::GameRecorder()
|
|
:m_vTeleportLocation(V_ZERO)
|
|
,mp_ReplayFrame(0)
|
|
,m_iLastModeSelected(-1)
|
|
,m_iDrawnFrameNetTime(0)
|
|
,m_bSyncNetDisplays(false)
|
|
,m_bSelectMode(false)
|
|
,m_bPauseOnEventReplay(false)
|
|
,m_bDestroyReplayObject(false)
|
|
,m_bCreateObjectForReplay(true)
|
|
,m_bInvolvePlayer(false)
|
|
,m_bStoreDataThisFrame(false)
|
|
,m_CachedCreatureLookupInst(0)
|
|
,m_CachedCreatureLookupCreature(0)
|
|
,m_bHasTeleportLocation(false)
|
|
,m_bHighlightSelected(true)
|
|
,m_bShowHelpText(false)
|
|
,m_bUseScriptCallstack(false)
|
|
,m_bReplayCamera(false)
|
|
,m_bPosePedsIfPossible(true)
|
|
,m_bRecordSkeletonsEveryFrame(false)
|
|
{
|
|
//Setup the base system
|
|
debugPlayback::Init();
|
|
|
|
debugPlayback::NewObjectData::smp_CreateNewObjectDescription = DebugReplayObjectInfo::CreateNewObjectData;
|
|
|
|
debugPlayback::PhysicsEvent::sm_AppRecordInst = NewInstFunc;
|
|
|
|
CPhysical::sm_RecordAttachmentsFunc = RecordEntityAttachment;
|
|
}
|
|
|
|
void GameRecorder::Init()
|
|
{
|
|
if (PARAM_DR_AllInsts.Get())
|
|
{
|
|
debugPlayback::SetTrackAllEntities(true);
|
|
}
|
|
|
|
const char *pFilterFileNames=0;
|
|
if (PARAM_DR_LoadFilter.Get(pFilterFileNames))
|
|
{
|
|
if (pFilterFileNames && pFilterFileNames[0])
|
|
{
|
|
ClearFilter();
|
|
|
|
//Allow multiple filters to be loaded
|
|
char buffer[512];
|
|
safecpy(buffer, pFilterFileNames);
|
|
const char* seps = " ,;";
|
|
char* pFilterName = strtok(buffer, seps);
|
|
while(pFilterName)
|
|
{
|
|
//Load each filter
|
|
LoadFilter(pFilterName);
|
|
|
|
// Try to read the token as a float.
|
|
pFilterName = strtok(NULL, seps);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (PARAM_DR_Autostart.Get())
|
|
{
|
|
StartRecording();
|
|
}
|
|
|
|
const char *modeString = 0;
|
|
if (PARAM_DR_TrackMode.Get(modeString))
|
|
{
|
|
if (!stricmp(modeString, "Selected"))
|
|
{
|
|
debugPlayback::SetTrackMode(debugPlayback::eTrackOne);
|
|
}
|
|
else if (!stricmp(modeString, "Player"))
|
|
{
|
|
debugPlayback::SetTrackMode(debugPlayback::eTrackOne);
|
|
sbTrackPlayer = true;
|
|
}
|
|
else if (!stricmp(modeString, "All"))
|
|
{
|
|
debugPlayback::SetTrackMode(debugPlayback::eTrackAll);
|
|
}
|
|
else if (!stricmp(modeString, "Frustrum"))
|
|
{
|
|
debugPlayback::SetTrackMode(debugPlayback::eTrackInFrustrum);
|
|
}
|
|
else if (!stricmp(modeString, "InRange"))
|
|
{
|
|
debugPlayback::SetTrackMode(debugPlayback::eTrackInRange);
|
|
}
|
|
}
|
|
}
|
|
|
|
GameRecorder::~GameRecorder()
|
|
{
|
|
}
|
|
|
|
void GameRecorder::OnSave(DebugRecorder::DebugRecorderData & rData)
|
|
{
|
|
AppLevelSaveData *pAppData = (AppLevelSaveData *)&rData;
|
|
pAppData->m_StoredSkeletonData.Resize( m_StoredSkeletonData.GetNumUsed());
|
|
pAppData->m_StoredSkeletonDataIDs.Resize( m_StoredSkeletonData.GetNumUsed());
|
|
atMap<size_t, StoredSkelData*>::Iterator itr = m_StoredSkeletonData.CreateIterator();
|
|
int iCounter = 0;
|
|
while (!itr.AtEnd())
|
|
{
|
|
pAppData->m_StoredSkeletonData[iCounter] = itr.GetData();
|
|
pAppData->m_StoredSkeletonDataIDs[iCounter] = itr.GetKey();
|
|
++iCounter;
|
|
++itr;
|
|
}
|
|
}
|
|
|
|
void GameRecorder::OnLoad(DebugRecorder::DebugRecorderData & rData)
|
|
{
|
|
AppLevelSaveData *pAppData = (AppLevelSaveData *)&rData;
|
|
const int iMax = Min(pAppData->m_StoredSkeletonDataIDs.GetCount(), pAppData->m_StoredSkeletonData.GetCount());
|
|
Assertf(pAppData->m_StoredSkeletonDataIDs.GetCount() == pAppData->m_StoredSkeletonData.GetCount(), "Error loading save data: why are these values different (%d != %d)", pAppData->m_StoredSkeletonDataIDs.GetCount(), pAppData->m_StoredSkeletonData.GetCount());
|
|
for (int i=0 ; i<iMax ; i++)
|
|
{
|
|
m_StoredSkeletonData.Insert( pAppData->m_StoredSkeletonDataIDs[i], pAppData->m_StoredSkeletonData[i] );
|
|
}
|
|
|
|
m_bPosePedsIfPossible = false;
|
|
}
|
|
|
|
void GameRecorder::OnClearRecording()
|
|
{
|
|
atMap<size_t, StoredSkelData*>::Iterator itr = m_StoredSkeletonData.CreateIterator();
|
|
while (!itr.AtEnd())
|
|
{
|
|
delete itr.GetData();
|
|
++itr;
|
|
}
|
|
m_StoredSkeletonData.Kill();
|
|
}
|
|
|
|
static float sfBoundDrawDist=0.0f;
|
|
void GameRecorder::OnStartDebugDraw()
|
|
{
|
|
#if __PFDRAW
|
|
sfBoundDrawDist = PFD_BoundDrawDistance.GetValue();
|
|
PFD_BoundDrawDistance.SetValue(10000.0f);
|
|
|
|
//Draw the selected object if we have one
|
|
if (m_bHighlightSelected)
|
|
{
|
|
debugPlayback::phInstId rSelected(debugPlayback::GetCurrentSelectedEntity());
|
|
switch (debugPlayback::GetTrackMode())
|
|
{
|
|
case debugPlayback::eTrackOne:
|
|
if (rSelected.IsValid())
|
|
{
|
|
grcColor(Color32(0.0f, 0.5f, 0.5f, 0.5f));
|
|
rSelected->GetArchetype()->GetBound()->Draw(rSelected->GetMatrix());
|
|
}
|
|
break;
|
|
case debugPlayback::eTrackMany: {
|
|
int iCount = debugPlayback::GetNumSelected();
|
|
bool bDrewSelected = false;
|
|
for (int i=0 ; i<iCount ; i++)
|
|
{
|
|
debugPlayback::phInstId rThisSelected = debugPlayback::GetSelected(i);
|
|
if (rThisSelected.IsValid())
|
|
{
|
|
if (rThisSelected == rSelected)
|
|
{
|
|
grcColor(Color32(0.0f, 0.5f, 0.5f, 0.5f));
|
|
bDrewSelected = true;
|
|
}
|
|
else
|
|
{
|
|
grcColor(Color32(0.0f, 0.25f, 0.25f, 0.25f));
|
|
}
|
|
|
|
rThisSelected->GetArchetype()->GetBound()->Draw(rThisSelected->GetMatrix());
|
|
}
|
|
}
|
|
if (!bDrewSelected && rSelected.IsValid())
|
|
{
|
|
//Flash until we've actually selected it
|
|
if ((TIME.GetFrameCount() & 0x7) > 3)
|
|
{
|
|
grcColor(Color32(0.0f, 0.5f, 0.5f, 0.5f));
|
|
rSelected->GetArchetype()->GetBound()->Draw(rSelected->GetMatrix());
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
if (rSelected.IsValid())
|
|
{
|
|
grcColor(Color32(0.25f, 0.25f, 0.25f, 0.5f));
|
|
rSelected->GetArchetype()->GetBound()->Draw(rSelected->GetMatrix());
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
#endif // __PFDRAW
|
|
}
|
|
|
|
u32 m_LastMouseTrackFrame=0;
|
|
float m_fLastMouseY=0.0f;
|
|
float m_fMouseYOffset=0.0f;
|
|
bool m_bDragging=false;
|
|
void GameRecorder::OnEndDebugDraw(bool bMenuItemSelected)
|
|
{
|
|
#if __PFDRAW
|
|
PFD_BoundDrawDistance.SetValue(sfBoundDrawDist);
|
|
#endif
|
|
|
|
debugPlayback::UpdatePerhapsSelected(!bMenuItemSelected);
|
|
|
|
if (AnimMotionTreeTracker::sm_iHoveredFrame == TIME.GetFrameCount())
|
|
{
|
|
if (ioMouse::GetButtons() & ioMouse::MOUSE_LEFT)
|
|
{
|
|
m_bDragging = true;
|
|
}
|
|
}
|
|
else if (!(ioMouse::GetButtons() & ioMouse::MOUSE_LEFT))
|
|
{
|
|
m_bDragging = false;
|
|
}
|
|
|
|
if (m_bDragging)
|
|
{
|
|
float fMouseY = float(ioMouse::GetY());
|
|
if (m_LastMouseTrackFrame == (TIME.GetFrameCount() - 1))
|
|
{
|
|
float fDeltaY = fMouseY - m_fLastMouseY;
|
|
m_fMouseYOffset += fDeltaY;
|
|
}
|
|
|
|
m_LastMouseTrackFrame = TIME.GetFrameCount();
|
|
m_fLastMouseY = fMouseY;
|
|
|
|
if (m_fMouseYOffset > 10.0f)
|
|
{
|
|
AnimMotionTreeTracker::sm_LineOffset = Max(AnimMotionTreeTracker::sm_LineOffset-1, 0);
|
|
m_fMouseYOffset -= 10.0f;
|
|
}
|
|
if (m_fMouseYOffset < -10.0f)
|
|
{
|
|
AnimMotionTreeTracker::sm_LineOffset++;
|
|
m_fMouseYOffset += 10.0f;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_fMouseYOffset = 0.0f;
|
|
}
|
|
}
|
|
|
|
void GameRecorder::OnStartRecording()
|
|
{
|
|
//Data should have been cleared
|
|
Assert(!m_StoredSkeletonData.GetNumUsed());
|
|
|
|
//Fill out some meta data
|
|
|
|
//Teleport location
|
|
m_bHasTeleportLocation = false;
|
|
CPed* pPlayerPed = CGameWorld::FindLocalPlayer();
|
|
if (pPlayerPed)
|
|
{
|
|
m_vTeleportLocation = pPlayerPed->GetMatrixPrePhysicsUpdate().GetCol3ConstRef();
|
|
m_bHasTeleportLocation = true;
|
|
}
|
|
|
|
//Reset data as needed
|
|
m_LastFrameInsts.Reset();
|
|
m_FrameInsts.Reset();
|
|
}
|
|
|
|
void GameRecorder::OnDebugDraw3d(const debugPlayback::EventBase *pHovered)
|
|
{
|
|
debugWanted::OnDebugDraw3d(GetSelectedFrame(), GetSelectedEvent(), pHovered);
|
|
}
|
|
|
|
void GameRecorder::OnDrawFrame(const debugPlayback::Frame *pFrame)
|
|
{
|
|
if (pFrame)
|
|
{
|
|
m_iDrawnFrameNetTime = pFrame->m_NetworkTime;
|
|
}
|
|
}
|
|
|
|
void GameRecorder::ClearFilter()
|
|
{
|
|
DisableAllEventTypes();
|
|
m_bRecordSkeletonsEveryFrame = false;
|
|
m_bPosePedsIfPossible = false;
|
|
StoreSkeleton::ms_bUseLowLodSkeletons = false;
|
|
}
|
|
|
|
bool GameRecorder::LoadFilter(const char *pFilerFileName)
|
|
{
|
|
FilterInfo *pFilterInfo = 0;
|
|
if (Verifyf(PARSER.LoadObjectPtr(pFilerFileName, "meta", pFilterInfo), "Failed to load debug filter %s", pFilerFileName))
|
|
{
|
|
for (int i=0 ; i<pFilterInfo->m_Filters.GetCount() ; i++)
|
|
{
|
|
debugPlayback::DR_EventType::Enable( pFilterInfo->m_Filters[i] );
|
|
}
|
|
m_bRecordSkeletonsEveryFrame |= pFilterInfo->m_bRecordSkeletonsEveryFrame;
|
|
m_bPosePedsIfPossible |= pFilterInfo->m_bPosePedsIfPossible;
|
|
StoreSkeleton::ms_bUseLowLodSkeletons |= pFilterInfo->m_bUseLowLodSkeletons;
|
|
delete pFilterInfo;
|
|
return true;
|
|
}
|
|
Errorf("Failed to load debug filter %s", pFilerFileName);
|
|
return false;
|
|
}
|
|
|
|
bool GameRecorder::SaveFilter(const char *pFilerFileName)
|
|
{
|
|
FilterInfo tmpInfo;
|
|
debugPlayback::DR_EventType::AddEnabled(tmpInfo.m_Filters);
|
|
tmpInfo.m_bRecordSkeletonsEveryFrame = m_bRecordSkeletonsEveryFrame;
|
|
tmpInfo.m_bPosePedsIfPossible = m_bPosePedsIfPossible;
|
|
tmpInfo.m_bUseLowLodSkeletons = StoreSkeleton::ms_bUseLowLodSkeletons;
|
|
return PARSER.SaveObject(pFilerFileName, "meta", &tmpInfo);
|
|
}
|
|
|
|
bool AreInstsFromSameEntity(debugPlayback::phInstId rCurrentInst, debugPlayback::phInstId rOtherInst)
|
|
{
|
|
GameRecorder *pInst = GameRecorder::GetAppLevelInstance();
|
|
return pInst->DoInstsShareAnEntity(rCurrentInst, rOtherInst);
|
|
}
|
|
|
|
void FilterToSelectedEntity(debugPlayback::DataBase& rData)
|
|
{
|
|
debugPlayback::phInstId rSelected(debugPlayback::GetCurrentSelectedEntity());
|
|
if (rSelected.IsValid())
|
|
{
|
|
if (rData.IsNewObjectData())
|
|
{
|
|
debugPlayback::NewObjectData *pNOD = static_cast<debugPlayback::NewObjectData *>(&rData);
|
|
if (AreInstsFromSameEntity(rSelected, pNOD->m_rInst))
|
|
{
|
|
if (!pNOD->IsFilterActive())
|
|
{
|
|
pNOD->OnFilterToggle();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (pNOD->IsFilterActive())
|
|
{
|
|
pNOD->OnFilterToggle();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static debugPlayback::phInstId toEnable;
|
|
void AddSelectedEntityToFilter(debugPlayback::DataBase& rData)
|
|
{
|
|
if (toEnable.IsAssigned())
|
|
{
|
|
if (rData.IsNewObjectData())
|
|
{
|
|
debugPlayback::NewObjectData *pNOD = static_cast<debugPlayback::NewObjectData *>(&rData);
|
|
if (AreInstsFromSameEntity(toEnable, pNOD->m_rInst))
|
|
{
|
|
if (!pNOD->IsFilterActive())
|
|
{
|
|
pNOD->OnFilterToggle();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void FilterToAllEntity(debugPlayback::DataBase& rData)
|
|
{
|
|
if (rData.IsNewObjectData())
|
|
{
|
|
debugPlayback::NewObjectData *pNOD = static_cast<debugPlayback::NewObjectData *>(&rData);
|
|
if (!pNOD->IsFilterActive())
|
|
{
|
|
pNOD->OnFilterToggle();
|
|
}
|
|
}
|
|
}
|
|
|
|
void ClearDataFilter(debugPlayback::DataBase& rData)
|
|
{
|
|
if (rData.IsNewObjectData())
|
|
{
|
|
debugPlayback::NewObjectData *pNOD = static_cast<debugPlayback::NewObjectData *>(&rData);
|
|
if (pNOD->IsFilterActive())
|
|
{
|
|
pNOD->OnFilterToggle();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
bool GameRecorder::AppOptions(debugPlayback::TextOutputVisitor &rMenuText, debugPlayback::TextOutputVisitor &rInfoText, bool bMouseDown, bool bPaused)
|
|
{
|
|
bool bMenuItemSelected = false;
|
|
if (rMenuText.AddLine("ShowHelp[%s]", m_bShowHelpText ? "X" : " ") && bMouseDown)
|
|
{
|
|
m_bShowHelpText = !m_bShowHelpText;
|
|
bMenuItemSelected = true;
|
|
}
|
|
|
|
if (m_bShowHelpText)
|
|
{
|
|
rInfoText.AddLine("CTRL + C on your debug keyboard will toggle continuous mode");
|
|
rInfoText.AddLine("CTRL + M on your debug keyboard will toggle the mode menu and cycle modes");
|
|
rInfoText.AddLine("CTRL + S on your debug keyboard will start/stop a new recording");
|
|
rInfoText.AddLine("CTRL + A auto selects the player / player's vehicle");
|
|
}
|
|
|
|
debugPlayback::phInstId rSelected(debugPlayback::GetCurrentSelectedEntity());
|
|
if (debugPlayback::GetTrackMode() == debugPlayback::eTrackOne)
|
|
{
|
|
if (rSelected.IsValid())
|
|
{
|
|
switch (rSelected->GetClassType())
|
|
{
|
|
case PH_INST_FRAG_VEH:
|
|
case PH_INST_FRAG_PED:
|
|
case PH_INST_FRAG_OBJECT:
|
|
case PH_INST_VEH:
|
|
case PH_INST_PED:
|
|
case PH_INST_OBJECT: {
|
|
CEntity *pObject = (CEntity*)rSelected->GetUserData();
|
|
if (pObject)
|
|
{
|
|
rInfoText.AddLine("Selected: %s", pObject->GetModelName());
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
rInfoText.AddLine("Selected: %x [%s]", rSelected.GetObject(), rSelected->GetArchetype()->GetFilename());
|
|
break;
|
|
}
|
|
|
|
//if (rMenuText.AddLine("Highlight[%s]", m_bHighlightSelected ? "X" : " ") && bMouseDown)
|
|
//{
|
|
// m_bHighlightSelected = !m_bHighlightSelected;
|
|
// bMenuItemSelected = true;
|
|
//}
|
|
}
|
|
}
|
|
|
|
//if we're not already recording.
|
|
//Shortcut to modes list
|
|
if (ioMapper::DebugKeyPressed(KEY_M) && ioMapper::DebugKeyDown(KEY_CONTROL))
|
|
{
|
|
if (GetFilterIndex() == 4)
|
|
{
|
|
++m_iLastModeSelected;
|
|
m_bSelectMode = true;
|
|
}
|
|
else
|
|
{
|
|
SetFilterIndex(4);
|
|
}
|
|
}
|
|
|
|
if (ioMapper::DebugKeyPressed(KEY_S) && ioMapper::DebugKeyDown(KEY_CONTROL))
|
|
{
|
|
if (IsRecording())
|
|
{
|
|
StopRecording();
|
|
}
|
|
else
|
|
{
|
|
StartRecording();
|
|
}
|
|
}
|
|
|
|
//And some other nice menu options to get rec
|
|
if (ioMapper::DebugKeyPressed(KEY_A) && ioMapper::DebugKeyDown(KEY_CONTROL))
|
|
{
|
|
CPed* pPlayerPed = CGameWorld::FindLocalPlayer();
|
|
if (pPlayerPed)
|
|
{
|
|
if (pPlayerPed->GetVehiclePedInside())
|
|
{
|
|
debugPlayback::TrackEntity(pPlayerPed->GetVehiclePedInside(), true);
|
|
}
|
|
else if (pPlayerPed->GetMyMount())
|
|
{
|
|
debugPlayback::TrackEntity(pPlayerPed->GetMyMount(), true);
|
|
}
|
|
else
|
|
{
|
|
debugPlayback::TrackEntity(pPlayerPed, true);
|
|
}
|
|
debugPlayback::SetTrackMode( debugPlayback::eTrackOne );
|
|
bMenuItemSelected = true;
|
|
}
|
|
}
|
|
|
|
|
|
//Allow us to record ALL entities
|
|
const char *pTracking="Unknown";
|
|
char bufferIfNeeded[256];
|
|
debugPlayback::eTrackMode nextMode = debugPlayback::eTrackAll;
|
|
switch (debugPlayback::GetTrackMode())
|
|
{
|
|
case debugPlayback::eTrackAll:
|
|
pTracking="All";
|
|
nextMode = debugPlayback::eTrackInFrustrum;
|
|
break;
|
|
case debugPlayback::eTrackInFrustrum:
|
|
pTracking="Frustrum";
|
|
nextMode = debugPlayback::eTrackMany;
|
|
break;
|
|
case debugPlayback::eTrackMany:
|
|
pTracking="InstList";
|
|
nextMode = debugPlayback::eTrackOne;
|
|
break;
|
|
case debugPlayback::eTrackOne:
|
|
pTracking="Selected";
|
|
nextMode = debugPlayback::eTrackInRange;
|
|
break;
|
|
case debugPlayback::eTrackInRange:
|
|
formatf(bufferIfNeeded, "InRange[%f]", sfTrackRange);
|
|
pTracking=bufferIfNeeded;
|
|
nextMode = debugPlayback::eTrackAll;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (rMenuText.AddLine("[TrackInsts:%s]", pTracking))
|
|
{
|
|
if (bMouseDown)
|
|
{
|
|
debugPlayback::SetTrackMode(nextMode);
|
|
bMenuItemSelected = true;
|
|
bMouseDown = false;
|
|
}
|
|
else
|
|
{
|
|
if (debugPlayback::GetTrackMode() == debugPlayback::eTrackInRange)
|
|
{
|
|
//Hovering, allow mouse wheel to change range
|
|
if (ioMouse::GetDZ() > 0)
|
|
{
|
|
sfTrackRange *= 2.0f;
|
|
}
|
|
else if(ioMouse::GetDZ() < 0)
|
|
{
|
|
sfTrackRange *= 0.5f;
|
|
if (sfTrackRange < 0.5f)
|
|
{
|
|
sfTrackRange = 0.5f;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
if (bPaused || !IsRecording())
|
|
{
|
|
if (NetworkInterface::IsGameInProgress())
|
|
{
|
|
if (rMenuText.AddLine("SyncDisplay[%s]", m_bSyncNetDisplays ? "X" : " ") && bMouseDown)
|
|
{
|
|
m_bSyncNetDisplays = !m_bSyncNetDisplays;
|
|
}
|
|
}
|
|
|
|
rMenuText.AddLine("Display Filter");
|
|
rMenuText.PushIndent();
|
|
if (rMenuText.AddLine("Clear") && bMouseDown)
|
|
{
|
|
CallOnAllStoredData(ClearDataFilter);
|
|
SetDataFilterOn(false);
|
|
RefreshFilteredState();
|
|
bMenuItemSelected = true;
|
|
bMouseDown = false;
|
|
}
|
|
|
|
|
|
const debugPlayback::EventBase *pSelectedEvent = GetSelectedEvent();
|
|
if (pSelectedEvent)
|
|
{
|
|
if (pSelectedEvent->IsPhysicsEvent())
|
|
{
|
|
const debugPlayback::PhysicsEvent *pPhysEvent = static_cast<const debugPlayback::PhysicsEvent *>(pSelectedEvent);
|
|
if (rMenuText.AddLine("+Selected Event") && bMouseDown)
|
|
{
|
|
EnableDataFilterForEvent(*pSelectedEvent);
|
|
SetDataFilterOn(true);
|
|
|
|
//Also any data for related insts
|
|
toEnable = pPhysEvent->m_rInst;
|
|
CallOnAllStoredData(AddSelectedEntityToFilter);
|
|
bMenuItemSelected = true;
|
|
bMouseDown = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (rSelected.IsValid())
|
|
{
|
|
if (rMenuText.AddLine("+Focus Entity") && bMouseDown)
|
|
{
|
|
toEnable = debugPlayback::GetCurrentSelectedEntity();
|
|
CallOnAllStoredData(AddSelectedEntityToFilter);
|
|
SetDataFilterOn(true);
|
|
RefreshFilteredState();
|
|
bMenuItemSelected = true;
|
|
bMouseDown = false;
|
|
}
|
|
|
|
if (rMenuText.AddLine("=Focus Entity") && bMouseDown)
|
|
{
|
|
CallOnAllStoredData(ClearDataFilter);
|
|
toEnable = debugPlayback::GetCurrentSelectedEntity();
|
|
CallOnAllStoredData(AddSelectedEntityToFilter);
|
|
SetDataFilterOn(true);
|
|
RefreshFilteredState();
|
|
bMenuItemSelected = true;
|
|
bMouseDown = false;
|
|
}
|
|
}
|
|
rMenuText.PopIndent();
|
|
}
|
|
|
|
rMenuText.AddLine("Record Filter");
|
|
rMenuText.PushIndent();
|
|
if (rMenuText.AddLine("Clear") && bMouseDown)
|
|
{
|
|
debugPlayback::ClearSelectedEntities();
|
|
bMenuItemSelected = true;
|
|
bMouseDown = false;
|
|
}
|
|
|
|
if (rSelected.IsValid())
|
|
{
|
|
if (rMenuText.AddLine("+Focus Entity") && bMouseDown)
|
|
{
|
|
debugPlayback::AddSelectedEntity( *rSelected.GetObject() );
|
|
bMenuItemSelected = true;
|
|
bMouseDown = false;
|
|
}
|
|
|
|
if (rMenuText.AddLine("=Focus Entity") && bMouseDown)
|
|
{
|
|
debugPlayback::SetTrackMode(debugPlayback::eTrackOne);
|
|
debugPlayback::SetCurrentSelectedEntity( rSelected.GetObject(), true );
|
|
bMenuItemSelected = true;
|
|
bMouseDown = false;
|
|
}
|
|
}
|
|
rMenuText.PopIndent();
|
|
|
|
if (bPaused || !IsRecording())
|
|
{
|
|
if (GetSelectedFrame())
|
|
{
|
|
const debugPlayback::Frame &rFrame = *GetSelectedFrame();
|
|
static bool bShowReplayOptions = false;
|
|
if (rMenuText.AddLine("Replay") && bMouseDown)
|
|
{
|
|
bShowReplayOptions = !bShowReplayOptions;
|
|
bMenuItemSelected = true;
|
|
bMouseDown = false;
|
|
}
|
|
if(bShowReplayOptions)
|
|
{
|
|
rMenuText.PushIndent();
|
|
|
|
if (rMenuText.AddLine("Trigger frame") && bMouseDown)
|
|
{
|
|
bMenuItemSelected = true;
|
|
bMouseDown = false;
|
|
mp_ReplayFrame = &rFrame;
|
|
|
|
m_bPauseOnEventReplay = false;
|
|
if (fwTimer::IsUserPaused())
|
|
{
|
|
fwTimer::EndUserPause();
|
|
m_bPauseOnEventReplay = true;
|
|
}
|
|
}
|
|
|
|
//These two lines toggle the check box
|
|
bool bSelected = rMenuText.AddLine("New object[%s]", m_bCreateObjectForReplay ? "X" : " ");
|
|
bSelected |= rMenuText.AddLine("Use selected[%s]", m_bCreateObjectForReplay ? " " : "X");
|
|
if (bSelected && bMouseDown)
|
|
{
|
|
m_bCreateObjectForReplay = !m_bCreateObjectForReplay;
|
|
bMenuItemSelected = true;
|
|
bMouseDown = false;
|
|
}
|
|
|
|
if (rMenuText.AddLine("Involve player[%s]", m_bInvolvePlayer ? "X" : " ") && bMouseDown)
|
|
{
|
|
m_bInvolvePlayer = !m_bInvolvePlayer;
|
|
bMenuItemSelected = true;
|
|
bMouseDown = false;
|
|
}
|
|
|
|
if (CEntity *pEnt = DebugReplayObjectInfo::m_rReplayObject)
|
|
{
|
|
//Don't offer destroy option if the replay object is the player
|
|
bool bIsPlayer = false;
|
|
if (pEnt->GetIsTypePed())
|
|
{
|
|
CPed *pPed = (CPed*)pEnt;
|
|
if (pPed->IsLocalPlayer())
|
|
{
|
|
bIsPlayer = false;
|
|
}
|
|
}
|
|
|
|
if ((!bIsPlayer) && rMenuText.AddLine("Destroy prop") && bMouseDown)
|
|
{
|
|
m_bDestroyReplayObject = true;
|
|
bMenuItemSelected = true;
|
|
bMouseDown = false;
|
|
}
|
|
}
|
|
|
|
rMenuText.PopIndent();
|
|
}
|
|
}
|
|
|
|
if (rMenuText.AddLine("TrackCamera[%s]", m_bReplayCamera ? "X" : " ") && bMouseDown)
|
|
{
|
|
bMenuItemSelected = true;
|
|
bMouseDown = false;
|
|
m_bReplayCamera = !m_bReplayCamera;
|
|
}
|
|
}
|
|
|
|
//if (DR_EVENT_ENABLED(AnimMotionTreeTracker))
|
|
{
|
|
if (rMenuText.AddLine("PoseEntites[%s]", m_bPosePedsIfPossible ? "X" : " ") && bMouseDown)
|
|
{
|
|
bMenuItemSelected = true;
|
|
bMouseDown = false;
|
|
m_bPosePedsIfPossible = !m_bPosePedsIfPossible;
|
|
}
|
|
}
|
|
|
|
if (DR_EVENT_ENABLED(StoreSkeletonPostUpdate) || DR_EVENT_ENABLED(StoreSkeletonPreUpdate))
|
|
{
|
|
if (rMenuText.AddLine("Skeletons Every Frame[%s]", m_bRecordSkeletonsEveryFrame ? "X" : " ") && bMouseDown)
|
|
{
|
|
bMenuItemSelected = true;
|
|
bMouseDown = false;
|
|
m_bRecordSkeletonsEveryFrame = !m_bRecordSkeletonsEveryFrame;
|
|
}
|
|
|
|
if (rMenuText.AddLine("Low LOD Skeletons[%s]", StoreSkeleton::ms_bUseLowLodSkeletons ? "X" : " ") && bMouseDown)
|
|
{
|
|
bMenuItemSelected = true;
|
|
bMouseDown = false;
|
|
StoreSkeleton::ms_bUseLowLodSkeletons = !StoreSkeleton::ms_bUseLowLodSkeletons;
|
|
}
|
|
}
|
|
|
|
return bMenuItemSelected;
|
|
}
|
|
|
|
void GameRecorder::RecordEntityAttachment(const phInst *pAttached, const phInst *pAttachedTo, const Matrix34 &matNew)
|
|
{
|
|
debugPlayback::RecordEntityAttachment(pAttached, pAttachedTo, matNew);
|
|
}
|
|
|
|
const phInst *GameRecorder::GetInstFromCreature(const crCreature *pCreature)
|
|
{
|
|
if (pCreature == m_CachedCreatureLookupCreature)
|
|
{
|
|
return m_CachedCreatureLookupInst;
|
|
}
|
|
const phInst * const *ppInst = m_CreatureToInstMap.Access(pCreature);
|
|
m_CachedCreatureLookupCreature = pCreature;
|
|
m_CachedCreatureLookupInst = ppInst ? *ppInst : 0;
|
|
return m_CachedCreatureLookupInst;
|
|
}
|
|
|
|
void GameRecorder::NewInstFunc(debugPlayback::phInstId id)
|
|
{
|
|
///Store some information on the object we'll need to save for the future
|
|
DebugRecorder &rRecorder = *DebugRecorder::GetInstance();
|
|
if (!rRecorder.GetSharedData(id.ToU32()))
|
|
{
|
|
DR_MEMORY_HEAP();
|
|
debugPlayback::NewObjectData *pOb = debugPlayback::NewObjectData::CreateNewObjectData(id);
|
|
rRecorder.AddSharedData(*pOb, id.ToU32());
|
|
}
|
|
|
|
//Mark for storing any end of frame data (currently skeletons and aiTask trees)
|
|
if (m_FrameInsts.GetCount() < m_FrameInsts.GetMaxCount())
|
|
{
|
|
for (int i=0 ; i<m_FrameInsts.GetCount() ; i++)
|
|
{
|
|
if (m_FrameInsts[i] == id)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
m_FrameInsts.Append() = id;
|
|
}
|
|
}
|
|
|
|
const DebugReplayObjectInfo* GameRecorder::GetObjectInfo(debugPlayback::phInstId rInst) const
|
|
{
|
|
if (rInst.IsAssigned())
|
|
{
|
|
debugPlayback::DebugRecorder *pInstance = debugPlayback::DebugRecorder::GetInstance();
|
|
if(pInstance)
|
|
{
|
|
debugPlayback::DataBase *pData = pInstance->GetSharedData(rInst.ToU32());
|
|
if (pData)
|
|
{
|
|
return dynamic_cast<DebugReplayObjectInfo*>(pData);
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
bool GameRecorder::DoInstsShareAnEntity(debugPlayback::phInstId rCurrentInst, debugPlayback::phInstId rOtherInst) const
|
|
{
|
|
if (rCurrentInst.IsAssigned())
|
|
{
|
|
if (rCurrentInst == rOtherInst)
|
|
{
|
|
return true;
|
|
}
|
|
const debugPlayback::DataBase *pData0Base = GetSharedData(rCurrentInst.ToU32());
|
|
if (pData0Base)
|
|
{
|
|
const DebugReplayObjectInfo *pData0 = dynamic_cast<const DebugReplayObjectInfo *>(pData0Base);
|
|
if (pData0)
|
|
{
|
|
const debugPlayback::DataBase *pData1Base = GetSharedData(rOtherInst.ToU32());
|
|
if (pData1Base)
|
|
{
|
|
const DebugReplayObjectInfo *pData1 = dynamic_cast<const DebugReplayObjectInfo *>(pData1Base);
|
|
if (pData1)
|
|
{
|
|
return (pData1->GetFirstInChain() == pData0->GetFirstInChain());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool GameRecorder::HasSkelData(u32 iSig)
|
|
{
|
|
return !!m_StoredSkeletonData.Access( iSig );
|
|
}
|
|
|
|
StoredSkelData *GameRecorder::GetSkelData(u32 iSig)
|
|
{
|
|
return m_StoredSkeletonData[iSig];
|
|
}
|
|
|
|
void GameRecorder::AddSkelData(StoredSkelData *pData, u32 iSig)
|
|
{
|
|
Assertf(!HasSkelData(iSig), "Trying to add skeleton data multiple times - will leak");
|
|
m_StoredSkeletonData[iSig] = pData;
|
|
}
|
|
|
|
void EnableCorePhyics()
|
|
{
|
|
//Enable the physics specific types
|
|
debugPlayback::DR_EventType::Enable("RecordedContact");
|
|
debugPlayback::DR_EventType::Enable("RecordedContactWithMatrices");
|
|
debugPlayback::DR_EventType::Enable("ApplyForceEvent");
|
|
debugPlayback::DR_EventType::Enable("ApplyForceCGEvent");
|
|
debugPlayback::DR_EventType::Enable("ApplyImpulseCGEvent");
|
|
debugPlayback::DR_EventType::Enable("ApplyTorqueEvent");
|
|
debugPlayback::DR_EventType::Enable("ApplyImpulseEvent");
|
|
debugPlayback::DR_EventType::Enable("SetVelocityEvent");
|
|
debugPlayback::DR_EventType::Enable("SetAngularMomentumEvent");
|
|
debugPlayback::DR_EventType::Enable("SetAngularVelocityEvent");
|
|
debugPlayback::DR_EventType::Enable("UpdateLocation");
|
|
debugPlayback::DR_EventType::Enable("SetMatrixEvent");
|
|
debugPlayback::DR_EventType::Enable("EntityAttachmentUpdate");
|
|
debugPlayback::DR_EventType::Enable("PedGroundInstanceEvent");
|
|
debugPlayback::DR_EventType::Enable("PedCapsuleInfoEvent");
|
|
}
|
|
|
|
void EnableVehiclePhyics()
|
|
{
|
|
//Enable the physics specific types
|
|
debugPlayback::DR_EventType::Enable("RecordedContact");
|
|
debugPlayback::DR_EventType::Enable("RecordedContactWithMatrices");
|
|
debugPlayback::DR_EventType::Enable("ApplyForceEvent");
|
|
debugPlayback::DR_EventType::Enable("ApplyForceCGEvent");
|
|
debugPlayback::DR_EventType::Enable("ApplyImpulseCGEvent");
|
|
debugPlayback::DR_EventType::Enable("ApplyTorqueEvent");
|
|
debugPlayback::DR_EventType::Enable("ApplyImpulseEvent");
|
|
debugPlayback::DR_EventType::Enable("SetVelocityEvent");
|
|
debugPlayback::DR_EventType::Enable("SetAngularMomentumEvent");
|
|
debugPlayback::DR_EventType::Enable("SetAngularVelocityEvent");
|
|
debugPlayback::DR_EventType::Enable("UpdateLocation");
|
|
debugPlayback::DR_EventType::Enable("SetMatrixEvent");
|
|
debugPlayback::DR_EventType::Enable("EntityAttachmentUpdate");
|
|
debugPlayback::DR_EventType::Enable("PedGroundInstanceEvent");
|
|
debugPlayback::DR_EventType::Enable("PedCapsuleInfoEvent");
|
|
debugPlayback::DR_EventType::Enable("SetTaggedVectorEvent");
|
|
}
|
|
|
|
void DisableAllEventTypes()
|
|
{
|
|
debugWanted::StopWantedRecording();
|
|
debugPlayback::DR_EventType::DisableAll();
|
|
}
|
|
|
|
void EnableWantedRecording()
|
|
{
|
|
debugPlayback::DR_EventType::Enable("LawEntityEvent");
|
|
debugPlayback::DR_EventType::Enable("LawUpdateEvent");
|
|
debugPlayback::DR_EventType::Enable("StoreSkeletonPostUpdate");
|
|
debugWanted::StartWantedRecording(10); //TODO: Make this variable
|
|
}
|
|
|
|
void EnableAnimationRecording(bool
|
|
#if DR_ANIMATIONS_ENABLED
|
|
bEnableLowLevel
|
|
#endif
|
|
)
|
|
{
|
|
//Enable tasks useful for animation debugging
|
|
debugPlayback::DR_EventType::Enable("CTaskFlowDebugEvent");
|
|
debugPlayback::DR_EventType::Enable("MoveTransitionStringCollection");
|
|
if (bEnableLowLevel)
|
|
{
|
|
debugPlayback::DR_EventType::Enable("MoveConditionStringCollection");
|
|
debugPlayback::DR_EventType::Enable("MoveSetValueStringCollection");
|
|
}
|
|
debugPlayback::DR_EventType::Enable("MoveGetValue");
|
|
debugPlayback::DR_EventType::Enable("MoveSetValue");
|
|
debugPlayback::DR_EventType::Enable("StoreSkeletonPostUpdate");
|
|
debugPlayback::DR_EventType::Enable("EntityAttachmentUpdate");
|
|
|
|
//?
|
|
debugPlayback::DR_EventType::Enable("PedDesiredVelocityEvent");
|
|
|
|
debugPlayback::DR_EventType::Enable("PedAnimatedVelocityEvent");
|
|
|
|
}
|
|
|
|
bool GameRecorder::AddAppFilters(debugPlayback::OnScreenOutput &rOutput, debugPlayback::OnScreenOutput &
|
|
#if !PDR_ENABLED
|
|
rInfoText
|
|
#endif
|
|
, int &iFilterIndex, bool bMouseDown)
|
|
{
|
|
int iMode=0;
|
|
bool bSelected = false; //Set to true to consume mouse input... bad design choice that will need revisiting
|
|
//with better input handling.... one day....maybe
|
|
|
|
switch(iFilterIndex)
|
|
{
|
|
case 4:
|
|
if (rOutput.AddLine("<-MODES->") && bMouseDown)
|
|
{
|
|
++iFilterIndex;
|
|
return true;
|
|
}
|
|
|
|
rOutput.m_bDrawBackground = iMode == m_iLastModeSelected;
|
|
if((rOutput.AddLine("[Physics]") && bMouseDown) || ((iMode == m_iLastModeSelected) && m_bSelectMode))
|
|
{
|
|
bMouseDown = false;
|
|
bSelected = true;
|
|
m_iLastModeSelected = iMode;
|
|
m_bSelectMode = false;
|
|
|
|
//Disable all event types
|
|
DisableAllEventTypes();
|
|
|
|
EnableCorePhyics();
|
|
}
|
|
|
|
++iMode;
|
|
|
|
rOutput.m_bDrawBackground = iMode == m_iLastModeSelected;
|
|
if((rOutput.AddLine("[Vehicle Physics]") && bMouseDown) || ((iMode == m_iLastModeSelected) && m_bSelectMode))
|
|
{
|
|
bMouseDown = false;
|
|
bSelected = true;
|
|
m_iLastModeSelected = iMode;
|
|
m_bSelectMode = false;
|
|
|
|
//Disable all event types
|
|
DisableAllEventTypes();
|
|
|
|
EnableVehiclePhyics();
|
|
}
|
|
|
|
|
|
#if !PDR_ENABLED
|
|
if (rOutput.m_bDrawBackground) //I.e. is the selected mode
|
|
{
|
|
//Show info text letting the user know physics needs to be enabled
|
|
rInfoText.m_bDrawBackground = (TIME.GetFrameCount() & 0xf) > 7;
|
|
rInfoText.AddLine("Physics recording requires PDR_ENABLED on in code!");
|
|
rInfoText.m_bDrawBackground = false;
|
|
}
|
|
#endif
|
|
++iMode;
|
|
|
|
rOutput.m_bDrawBackground = iMode == m_iLastModeSelected;
|
|
if((rOutput.AddLine("[Ped Move]") && bMouseDown) || ((iMode == m_iLastModeSelected) && m_bSelectMode))
|
|
{
|
|
bMouseDown = false;
|
|
bSelected = true;
|
|
m_bSelectMode = false;
|
|
m_iLastModeSelected = iMode;
|
|
|
|
//Disable all event types
|
|
DisableAllEventTypes();
|
|
|
|
debugPlayback::DR_EventType::Enable("QPedSpringEvent");
|
|
debugPlayback::DR_EventType::Enable("PedDesiredVelocityEvent");
|
|
debugPlayback::DR_EventType::Enable("PedAnimatedVelocityEvent");
|
|
debugPlayback::DR_EventType::Enable("RecordedContact");
|
|
|
|
debugPlayback::DR_EventType::Enable("InstLabelEvent");
|
|
debugPlayback::DR_EventType::Enable("SetTaggedFloatEvent");
|
|
debugPlayback::DR_EventType::Enable("SetTaggedVectorEvent");
|
|
debugPlayback::DR_EventType::Enable("SetTaggedMatrixEvent");
|
|
}
|
|
++iMode;
|
|
|
|
rOutput.m_bDrawBackground = iMode == m_iLastModeSelected;
|
|
if((rOutput.AddLine("[AI]") && bMouseDown) || ((iMode == m_iLastModeSelected) && m_bSelectMode))
|
|
{
|
|
bMouseDown = false;
|
|
bSelected = true;
|
|
m_bSelectMode = false;
|
|
m_iLastModeSelected = iMode;
|
|
|
|
//Disable all event types
|
|
DisableAllEventTypes();
|
|
|
|
//Enable the AI specific event types
|
|
debugPlayback::DR_EventType::Enable("CTaskFlowDebugEvent");
|
|
debugPlayback::DR_EventType::Enable("CStoreTasks");
|
|
}
|
|
++iMode;
|
|
|
|
#ifdef MOVE_DR_ENABLED
|
|
rOutput.m_bDrawBackground = iMode == m_iLastModeSelected;
|
|
if ((rOutput.AddLine("[Anim-high]") && bMouseDown) || ((iMode == m_iLastModeSelected) && m_bSelectMode))
|
|
{
|
|
bMouseDown = false;
|
|
bSelected = true;
|
|
m_bSelectMode = false;
|
|
m_iLastModeSelected = iMode;
|
|
|
|
DisableAllEventTypes();
|
|
|
|
EnableAnimationRecording(false);
|
|
}
|
|
++iMode;
|
|
|
|
rOutput.m_bDrawBackground = iMode == m_iLastModeSelected;
|
|
if ((rOutput.AddLine("[Anim-low]") && bMouseDown) || ((iMode == m_iLastModeSelected) && m_bSelectMode))
|
|
{
|
|
bMouseDown = false;
|
|
bSelected = true;
|
|
m_bSelectMode = false;
|
|
m_iLastModeSelected = iMode;
|
|
|
|
DisableAllEventTypes();
|
|
|
|
EnableAnimationRecording(true);
|
|
}
|
|
++iMode;
|
|
#endif //#ifdef MOVE_DR_ENABLED
|
|
rOutput.m_bDrawBackground = iMode == m_iLastModeSelected;
|
|
if ((rOutput.AddLine("[Wanted]") && bMouseDown) || ((iMode == m_iLastModeSelected) && m_bSelectMode))
|
|
{
|
|
bMouseDown = false;
|
|
bSelected = true;
|
|
m_bSelectMode = false;
|
|
m_iLastModeSelected = iMode;
|
|
|
|
DisableAllEventTypes();
|
|
|
|
EnableWantedRecording();
|
|
}
|
|
++iMode;
|
|
|
|
if (m_iLastModeSelected > iMode)
|
|
{
|
|
if (m_bSelectMode)
|
|
{
|
|
DisableAllEventTypes();
|
|
}
|
|
m_iLastModeSelected = 0;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
iFilterIndex = 0;
|
|
return false;
|
|
}
|
|
|
|
return bSelected;
|
|
}
|
|
|
|
|
|
|
|
namespace rage
|
|
{
|
|
namespace debugPlayback
|
|
{
|
|
struct VehicleAvoidenceDataEvent : public debugPlayback::PhysicsEvent
|
|
{
|
|
struct StoredObstructionItem
|
|
{
|
|
//<Float16 name="m_Dist"/>
|
|
// <s8 name="m_Direction"/>
|
|
// <u8 name="m_Type"/>
|
|
union {
|
|
struct {
|
|
u16 m_Dist;
|
|
s16 m_Direction : 12;
|
|
s16 m_Type : 4;
|
|
};
|
|
u32 m_AsU32;
|
|
};
|
|
|
|
void Extract(float fMinRange, float fMaxRange, float fCentreAngle, float /*fMaxAngle*/, float &rRange, float &rAngle) const
|
|
{
|
|
rAngle = fCentreAngle + (float(m_Direction) * (PI / 2047.0f));
|
|
if (fMaxRange > fMinRange)
|
|
{
|
|
rRange = float(m_Dist) * 256.0f / 65535.0f;
|
|
}
|
|
else
|
|
{
|
|
rRange = fMaxRange;
|
|
}
|
|
}
|
|
|
|
PAR_SIMPLE_PARSABLE;
|
|
};
|
|
|
|
Vec3V m_vRight;
|
|
Vec3V m_vForwards;
|
|
atArray<StoredObstructionItem> m_Obstructions;
|
|
float m_fMinRange;
|
|
float m_fMaxRange;
|
|
float m_fCentreAngle;
|
|
float m_fMaxAngle;
|
|
float m_fVehDriveOrientation;
|
|
|
|
virtual void DebugDraw3d(eDrawFlags /*drawFlags*/) const
|
|
{
|
|
for (int i=0 ; i<m_Obstructions.GetCount() ; i++)
|
|
{
|
|
Color32 col(255,0,0);
|
|
switch(m_Obstructions[i].m_Type)
|
|
{
|
|
case 0:
|
|
col.Set(255,0,0);
|
|
break;
|
|
case 1:
|
|
col.Set(0,255,0);
|
|
break;
|
|
case 2:
|
|
col.Set(0,0,255);
|
|
break;
|
|
case 3:
|
|
col.Set(255,255,255);
|
|
break;
|
|
}
|
|
col.SetAlpha(DebugRecorder::GetGlobalAlphaScale());
|
|
float fAngle = 0.0f, fRange = 0.0f;
|
|
m_Obstructions[i].Extract(m_fMinRange, m_fMaxRange, m_fCentreAngle, m_fMaxAngle, fRange, fAngle);
|
|
|
|
Vec3V right = m_vRight * ScalarV(fRange * Sinf(m_fVehDriveOrientation - fAngle));
|
|
Vec3V forwards = m_vForwards * ScalarV(fRange * Cosf(m_fVehDriveOrientation - fAngle));
|
|
|
|
grcDrawLine(m_Pos, m_Pos+right+forwards, col);
|
|
}
|
|
}
|
|
|
|
virtual bool DebugEventText(TextOutputVisitor &rOutput) const
|
|
{
|
|
PrintInstName(rOutput);
|
|
rOutput.PushIndent();
|
|
for (int i=0 ; i<m_Obstructions.GetCount() ; i++)
|
|
{
|
|
float fAngle = 0.0f, fRange = 0.0f;
|
|
m_Obstructions[i].Extract(m_fMinRange, m_fMaxRange, 0.0f, m_fMaxAngle, fRange, fAngle); //TMS: printing angles relative to car
|
|
const char *pType="unknown";
|
|
switch(m_Obstructions[i].m_Type)
|
|
{
|
|
case 0:
|
|
pType="Veh";
|
|
break;
|
|
case 1:
|
|
pType="Ped";
|
|
break;
|
|
case 2:
|
|
pType="Obj";
|
|
break;
|
|
case 3:
|
|
pType="Edge";
|
|
break;
|
|
}
|
|
rOutput.AddLine("%s: Dir %f, Range %f", pType, fAngle, fRange);
|
|
}
|
|
rOutput.PopIndent();
|
|
return true;
|
|
}
|
|
|
|
VehicleAvoidenceDataEvent(const CallstackHelper rCallstack, const phInst *pInst)
|
|
:PhysicsEvent(rCallstack, pInst)
|
|
,m_fMinRange(0.0f)
|
|
,m_fMaxRange(0.0f)
|
|
,m_fCentreAngle(0.0f)
|
|
,m_fMaxAngle(0.0f)
|
|
,m_fVehDriveOrientation(0.0f)
|
|
{
|
|
}
|
|
|
|
VehicleAvoidenceDataEvent()
|
|
:m_fMinRange(0.0f)
|
|
,m_fMaxRange(0.0f)
|
|
,m_fCentreAngle(0.0f)
|
|
,m_fMaxAngle(0.0f)
|
|
,m_fVehDriveOrientation(0.0f)
|
|
{
|
|
}
|
|
|
|
void SetData(float fMinDist, float fMaxDist, float fCentreAngle, float fMaxAngle, float fDriveOrientation, Vec3V_In vRight, Vec3V_In vVehForwards)
|
|
{
|
|
m_fMinRange = fMinDist;
|
|
m_fMaxRange = fMaxDist;
|
|
m_fCentreAngle = fCentreAngle;
|
|
m_fMaxAngle = fMaxAngle;
|
|
m_fVehDriveOrientation = fDriveOrientation;
|
|
m_vRight = vRight;
|
|
m_vForwards = vVehForwards;
|
|
}
|
|
|
|
PAR_PARSABLE;
|
|
DR_EVENT_DECL(VehicleAvoidenceDataEvent)
|
|
};
|
|
|
|
struct VehicleTargetPointEvent : public debugPlayback::PhysicsEvent
|
|
{
|
|
virtual void DebugDraw3d(eDrawFlags drawFlags) const
|
|
{
|
|
Color32 col(255,0,0,DebugRecorder::GetGlobalAlphaScale());
|
|
u32 utask = m_Task.GetHash();
|
|
col.SetBytes(u8(utask >> 24), u8(utask >> 16), u8(utask >> 8), DebugRecorder::GetGlobalAlphaScale());
|
|
if (drawFlags & eDrawSelected)
|
|
{
|
|
if ((TIME.GetFrameCount() & 0x15) > 0x7)
|
|
{
|
|
col.SetRed(0);
|
|
col.SetGreen(0);
|
|
col.SetBlue(0);
|
|
}
|
|
}
|
|
if (drawFlags & eDrawHovered)
|
|
{
|
|
if ((TIME.GetFrameCount() & 0x15) > 0x7)
|
|
{
|
|
col.SetRed(255);
|
|
col.SetGreen(255);
|
|
col.SetBlue(255);
|
|
}
|
|
}
|
|
|
|
grcDrawLine(m_TargetPos, m_Pos, col);
|
|
grcDrawLine(m_TargetPos, m_TargetPos+Vec3V(0.0f,0.0f,2.5f), col);
|
|
grcDrawLine(m_Pos, m_TargetPos+Vec3V(0.0f,0.0f,2.5f), col);
|
|
grcColor(col);
|
|
}
|
|
|
|
//Click on far end to select line
|
|
virtual bool GetEventPos(Vec3V &pos) const
|
|
{
|
|
pos = m_TargetPos;
|
|
return true;
|
|
}
|
|
|
|
virtual const char *GetEventSubType() const
|
|
{
|
|
return m_Task.GetCStr();
|
|
}
|
|
|
|
void SetData(const char *pTaskName, Vec3V_In targetPos)
|
|
{
|
|
m_TargetPos = targetPos;
|
|
m_Task = pTaskName;
|
|
}
|
|
|
|
VehicleTargetPointEvent(const CallstackHelper rCallstack, const phInst *pInst)
|
|
:PhysicsEvent(rCallstack, pInst)
|
|
{
|
|
}
|
|
|
|
VehicleTargetPointEvent()
|
|
:m_TargetPos(V_ZERO)
|
|
{
|
|
}
|
|
|
|
atHashString m_Task;
|
|
Vec3V m_TargetPos;
|
|
|
|
PAR_PARSABLE;
|
|
DR_EVENT_DECL(VehicleTargetPointEvent)
|
|
};
|
|
|
|
struct PedAnimatedVelocityEvent : public debugPlayback::PhysicsEvent
|
|
{
|
|
virtual void DebugDraw3d(eDrawFlags drawFlags) const
|
|
{
|
|
Color32 col(255,0,0,DebugRecorder::GetGlobalAlphaScale());
|
|
if (drawFlags & eDrawSelected)
|
|
{
|
|
col.SetGreen(128);
|
|
}
|
|
if (drawFlags & eDrawHovered)
|
|
{
|
|
col.SetGreen(255);
|
|
}
|
|
|
|
DrawVelocity(m_Linear, m_Pos, ScalarV(V_ONE), col);
|
|
}
|
|
|
|
void SetData(Vec3V_In linear)
|
|
{
|
|
m_Linear = linear;
|
|
}
|
|
|
|
PedAnimatedVelocityEvent(const CallstackHelper rCallstack, const phInst *pInst)
|
|
:PhysicsEvent(rCallstack, pInst)
|
|
{
|
|
}
|
|
|
|
PedAnimatedVelocityEvent()
|
|
:m_Linear(V_ZERO)
|
|
{ }
|
|
|
|
Vec3V m_Linear;
|
|
|
|
PAR_PARSABLE;
|
|
DR_EVENT_DECL(PedAnimatedVelocityEvent)
|
|
};
|
|
|
|
struct DRNetBlenderStateEvent : public debugPlayback::PhysicsEvent
|
|
{
|
|
virtual void DebugDraw3d(debugPlayback::eDrawFlags drawFlags) const
|
|
{
|
|
Color32 col(255,0,128,DebugRecorder::GetGlobalAlphaScale());
|
|
if (drawFlags & eDrawSelected)
|
|
{
|
|
col.SetGreen(128);
|
|
}
|
|
if (drawFlags & eDrawHovered)
|
|
{
|
|
col.SetGreen(255);
|
|
}
|
|
|
|
DrawAxis(0.25f, m_Latest);
|
|
|
|
grcWorldIdentity();
|
|
grcDrawLine(m_Latest.GetCol0(), m_Pos, col);
|
|
}
|
|
|
|
void SetData(Mat34V_In lastRecieved, bool positionUpdated, bool orientationUpdated, bool velocityUpdated, bool angVelocityUpdated)
|
|
{
|
|
m_Latest = lastRecieved;
|
|
m_PositionUpdated = positionUpdated;
|
|
m_OrientationUpdated = orientationUpdated;
|
|
m_VelocityUpdated = velocityUpdated;
|
|
m_AngVelocityUpdated = angVelocityUpdated;
|
|
}
|
|
|
|
DRNetBlenderStateEvent(const debugPlayback::CallstackHelper rCallstack, const phInst *pInst)
|
|
:PhysicsEvent(rCallstack, pInst)
|
|
{
|
|
}
|
|
|
|
DRNetBlenderStateEvent()
|
|
: m_Latest(V_ZERO)
|
|
, m_PositionUpdated(false)
|
|
, m_OrientationUpdated(false)
|
|
, m_VelocityUpdated(false)
|
|
, m_AngVelocityUpdated(false) {}
|
|
|
|
Mat34V m_Latest;
|
|
bool m_PositionUpdated;
|
|
bool m_OrientationUpdated;
|
|
bool m_VelocityUpdated;
|
|
bool m_AngVelocityUpdated;
|
|
|
|
PAR_PARSABLE;
|
|
DR_EVENT_DECL(DRNetBlenderStateEvent)
|
|
};
|
|
|
|
struct PedDesiredVelocityEvent : public debugPlayback::PhysicsEvent
|
|
{
|
|
virtual void DebugDraw3d(eDrawFlags drawFlags) const
|
|
{
|
|
Color32 col(255,0,128,DebugRecorder::GetGlobalAlphaScale());
|
|
if (drawFlags & eDrawSelected)
|
|
{
|
|
col.SetGreen(128);
|
|
}
|
|
if (drawFlags & eDrawHovered)
|
|
{
|
|
col.SetGreen(255);
|
|
}
|
|
|
|
DrawVelocity(m_Linear, m_Pos, ScalarV(V_ONE), col);
|
|
col.SetBlue(128);
|
|
DrawRotation(m_Angular, m_Pos, ScalarV(V_ONE), col);
|
|
}
|
|
|
|
void SetData(Vec3V_In linear, Vec3V_In angular)
|
|
{
|
|
m_Linear = linear;
|
|
m_Angular = angular;
|
|
}
|
|
|
|
PedDesiredVelocityEvent(const CallstackHelper rCallstack, const phInst *pInst)
|
|
:PhysicsEvent(rCallstack, pInst)
|
|
{
|
|
}
|
|
|
|
PedDesiredVelocityEvent():m_Linear(V_ZERO), m_Angular(V_ZERO){}
|
|
|
|
Vec3V m_Linear;
|
|
Vec3V m_Angular;
|
|
|
|
PAR_PARSABLE;
|
|
DR_EVENT_DECL(PedDesiredVelocityEvent)
|
|
};
|
|
|
|
struct VehicleDataEvent : public debugPlayback::PhysicsEvent
|
|
{
|
|
virtual void DebugDraw3d(eDrawFlags drawFlags) const
|
|
{
|
|
Color32 col(255,0,128,DebugRecorder::GetGlobalAlphaScale());
|
|
if (drawFlags & eDrawSelected)
|
|
{
|
|
col.SetGreen(128);
|
|
col.SetBlue(0);
|
|
}
|
|
if (drawFlags & eDrawHovered)
|
|
{
|
|
col.SetGreen(255);
|
|
}
|
|
|
|
QuatV vRot = m_Quat.Get();
|
|
Mat34V mat;
|
|
Mat34VFromQuatV(mat, vRot, m_Pos);
|
|
DrawAxis(1.0f, mat);
|
|
|
|
const DebugReplayObjectInfo *pObject = GameRecorder::GetAppLevelInstance()->GetObjectInfo( m_rInst );
|
|
if (pObject)
|
|
{
|
|
pObject->DrawBox(mat, col);
|
|
}
|
|
|
|
DrawVelocity(m_Linear, m_Pos, ScalarV(V_ONE), col);
|
|
col.SetRed(128);
|
|
DrawVelocity(m_vVehicleVelocity, m_Pos, ScalarV(V_ONE), col);
|
|
col.SetBlue(128);
|
|
DrawRotation(m_Angular, m_Pos, ScalarV(V_ONE), col);
|
|
|
|
if(m_InterpolationTargetAndTime.GetWi())
|
|
{
|
|
Mat34V matLocal;
|
|
Mat34VFromQuatV(matLocal, m_InterpolationTargetOrientation.Get(), m_InterpolationTargetAndTime.GetXYZ());
|
|
DrawAxis(1.0f, matLocal);
|
|
}
|
|
}
|
|
|
|
void SetData(Vec3V_In linear, Vec3V_In angular)
|
|
{
|
|
m_Linear = linear;
|
|
m_Angular = angular;
|
|
}
|
|
|
|
VehicleDataEvent(const CallstackHelper rCallstack, const phInst *pInst)
|
|
:PhysicsEvent(rCallstack, pInst)
|
|
,m_Linear(V_ZERO)
|
|
,m_Angular(V_ZERO)
|
|
,m_InterpolationTargetAndTime(V_ZERO)
|
|
,m_fSteerAngle(0.0f)
|
|
,m_fSecondSteerAngle(0.0f)
|
|
,m_fCheatPowerIncrease(0.0f)
|
|
,m_fThrottle(0.0f)
|
|
,m_fBrake(0.0f)
|
|
,m_fForwardSpeed(0.0f)
|
|
,m_fDesiredSpeed(0.0f)
|
|
,m_fSmoothedSpeed(0.0f)
|
|
,m_bHandBrake(false)
|
|
,m_bCheatFlagGrip2(false)
|
|
,m_bLodEventScanning(false)
|
|
,m_bLodDummy(false)
|
|
,m_bLodSuperDummy(false)
|
|
,m_bLodTimeSlicing(false)
|
|
{
|
|
if (pInst && (pInst->GetClassType() == PH_INST_FRAG_VEH))
|
|
{
|
|
const phCollider *pCollider=PHSIM->GetCollider(pInst);
|
|
if (pCollider)
|
|
{
|
|
m_Linear = pCollider->GetVelocity();
|
|
m_Angular = pCollider->GetAngVelocity();
|
|
}
|
|
|
|
CVehicle *pVehicle = (CVehicle*)pInst->GetUserData();
|
|
if (pVehicle)
|
|
{
|
|
//Store vehicle specific data here!
|
|
m_vVehicleVelocity = RCC_VEC3V( pVehicle->GetVelocity() );
|
|
m_fSteerAngle = pVehicle->GetSteerAngle();
|
|
m_fSecondSteerAngle = pVehicle->GetSecondSteerAngle();
|
|
m_fCheatPowerIncrease = pVehicle->GetCheatPowerIncrease();
|
|
m_fThrottle = pVehicle->GetThrottle();
|
|
m_fBrake = pVehicle->GetBrake();
|
|
m_bHandBrake = pVehicle->GetHandBrake();
|
|
m_bCheatFlagGrip2 = pVehicle->GetCheatFlag(VEH_CHEAT_GRIP2);
|
|
m_InterpolationTargetAndTime = pVehicle->GetInterpolationTargetAndTime();
|
|
if(m_InterpolationTargetAndTime.GetWi())
|
|
{
|
|
m_InterpolationTargetOrientation.Set(pVehicle->GetInterpolationTargetOrientation());
|
|
}
|
|
else
|
|
{
|
|
m_InterpolationTargetOrientation.Set(QuatV(V_IDENTITY));
|
|
}
|
|
|
|
m_fForwardSpeed = DotProduct(pVehicle->GetVelocity(), VEC3V_TO_VECTOR3(pVehicle->GetTransform().GetB()));
|
|
m_fDesiredSpeed = 0.0f;
|
|
if( pVehicle->GetIntelligence()->GetActiveTaskSimplest() )
|
|
{
|
|
m_fDesiredSpeed = pVehicle->GetIntelligence()->GetActiveTaskSimplest()->GetCruiseSpeed();
|
|
}
|
|
m_fSmoothedSpeed = pVehicle->GetIntelligence()->GetSmoothedSpeedSq();
|
|
m_bLodEventScanning = pVehicle->GetVehicleAiLod().IsLodFlagSet(CVehicleAILod::AL_LodEventScanning);
|
|
m_bLodDummy = pVehicle->GetVehicleAiLod().IsLodFlagSet(CVehicleAILod::AL_LodDummy);
|
|
m_bLodSuperDummy = pVehicle->GetVehicleAiLod().IsLodFlagSet(CVehicleAILod::AL_LodSuperDummy);
|
|
m_bLodTimeSlicing = pVehicle->GetVehicleAiLod().IsLodFlagSet(CVehicleAILod::AL_LodTimeslicing);
|
|
}
|
|
|
|
QuatV vQ = QuatVFromMat34V(pInst->GetMatrix());
|
|
vQ = Normalize( vQ );
|
|
m_Quat.Set(vQ);
|
|
}
|
|
}
|
|
|
|
VehicleDataEvent()
|
|
:m_Linear(V_ZERO)
|
|
,m_vVehicleVelocity(V_ZERO)
|
|
,m_Angular(V_ZERO)
|
|
,m_InterpolationTargetAndTime(V_ZERO)
|
|
,m_fSteerAngle(0.0f)
|
|
,m_fSecondSteerAngle(0.0f)
|
|
,m_fCheatPowerIncrease(0.0f)
|
|
,m_fThrottle(0.0f)
|
|
,m_fBrake(0.0f)
|
|
,m_fForwardSpeed(0.0f)
|
|
,m_fDesiredSpeed(0.0f)
|
|
,m_fSmoothedSpeed(0.0f)
|
|
,m_bHandBrake(false)
|
|
,m_bCheatFlagGrip2(false)
|
|
,m_bLodEventScanning(false)
|
|
,m_bLodDummy(false)
|
|
,m_bLodSuperDummy(false)
|
|
,m_bLodTimeSlicing(false)
|
|
{}
|
|
|
|
Vec3V m_Linear;
|
|
Vec3V m_vVehicleVelocity;
|
|
Vec3V m_Angular;
|
|
Vec4V m_InterpolationTargetAndTime;
|
|
StoreSkeleton::u32Quaternion m_InterpolationTargetOrientation;
|
|
float m_fSteerAngle;
|
|
float m_fSecondSteerAngle;
|
|
float m_fCheatPowerIncrease;
|
|
float m_fThrottle;
|
|
float m_fBrake;
|
|
float m_fForwardSpeed;
|
|
float m_fDesiredSpeed;
|
|
float m_fSmoothedSpeed;
|
|
StoreSkeleton::u32Quaternion m_Quat;
|
|
bool m_bHandBrake;
|
|
bool m_bCheatFlagGrip2;
|
|
bool m_bLodEventScanning;
|
|
bool m_bLodDummy;
|
|
bool m_bLodSuperDummy;
|
|
bool m_bLodTimeSlicing;
|
|
|
|
PAR_PARSABLE;
|
|
DR_EVENT_DECL(VehicleDataEvent)
|
|
};
|
|
|
|
struct PedDataEvent : public debugPlayback::PhysicsEvent
|
|
{
|
|
virtual void DebugDraw3d(eDrawFlags drawFlags) const
|
|
{
|
|
Color32 col(0,255,128,DebugRecorder::GetGlobalAlphaScale());
|
|
if (drawFlags & eDrawSelected)
|
|
{
|
|
col.SetRed(128);
|
|
}
|
|
if (drawFlags & eDrawHovered)
|
|
{
|
|
col.SetBlue(255);
|
|
}
|
|
|
|
DrawVelocity(m_Linear, m_Pos, ScalarV(V_ONE), col);
|
|
col.SetBlue(128);
|
|
DrawRotation(m_Angular, m_Pos, ScalarV(V_ONE), col);
|
|
|
|
QuatV vRot = m_Quat.Get();
|
|
Mat34V matLocal;
|
|
Mat34VFromQuatV(matLocal, vRot, m_Pos);
|
|
DrawAxis(1.0f, matLocal);
|
|
|
|
const DebugReplayObjectInfo *pObject = GameRecorder::GetAppLevelInstance()->GetObjectInfo( m_rInst );
|
|
if (pObject)
|
|
{
|
|
pObject->DrawBox(matLocal, col);
|
|
}
|
|
}
|
|
|
|
void SetData(Vec3V_In linear, Vec3V_In angular)
|
|
{
|
|
m_Linear = linear;
|
|
m_Angular = angular;
|
|
}
|
|
|
|
PedDataEvent(const CallstackHelper rCallstack, const phInst *pInst)
|
|
:PhysicsEvent(rCallstack, pInst)
|
|
,m_Linear(V_ZERO)
|
|
,m_Angular(V_ZERO)
|
|
{
|
|
if (pInst && (pInst->GetClassType() == PH_INST_FRAG_PED || pInst->GetClassType() == PH_INST_PED))
|
|
{
|
|
const phCollider *pCollider=PHSIM->GetCollider(pInst);
|
|
if (pCollider)
|
|
{
|
|
m_Linear = pCollider->GetVelocity();
|
|
m_Angular = pCollider->GetAngVelocity();
|
|
}
|
|
|
|
QuatV vQ = QuatVFromMat34V(pInst->GetMatrix());
|
|
vQ = Normalize( vQ );
|
|
m_Quat.Set(vQ);
|
|
|
|
//CPed *pPed = (CPed*)pInst->GetUserData();
|
|
//if (pPed)
|
|
//{
|
|
// //Store any ped specific data here!
|
|
//}
|
|
}
|
|
}
|
|
|
|
PedDataEvent()
|
|
:m_Linear(V_ZERO)
|
|
,m_Angular(V_ZERO)
|
|
{}
|
|
|
|
Vec3V m_Linear;
|
|
Vec3V m_Angular;
|
|
StoreSkeleton::u32Quaternion m_Quat;
|
|
|
|
PAR_PARSABLE;
|
|
DR_EVENT_DECL(PedDataEvent)
|
|
};
|
|
|
|
void PedCapsuleInfoEvent::DebugDraw3d(eDrawFlags drawFlags) const
|
|
{
|
|
Color32 col(128,0,128,DebugRecorder::GetGlobalAlphaScale());
|
|
if (drawFlags & eDrawSelected)
|
|
{
|
|
col.SetGreen(128);
|
|
}
|
|
if (drawFlags & eDrawHovered)
|
|
{
|
|
col.SetGreen(255);
|
|
}
|
|
|
|
grcDebugDraw::Capsule(m_Start, m_End, m_Radius, col, false);
|
|
Vector3 vMin(RCC_VECTOR3(m_Ground));
|
|
Vector3 vMax(vMin);
|
|
vMin.x -= 0.05f;
|
|
vMin.y -= 0.05f;
|
|
vMin.z -= 0.05f;
|
|
vMax.x += 0.05f;
|
|
vMax.y += 0.05f;
|
|
vMax.z += 0.05f;
|
|
grcDrawSolidBox(vMin, vMax, col);
|
|
}
|
|
|
|
struct PedGroundInstanceEvent : public debugPlayback::PhysicsEvent
|
|
{
|
|
Mat34V m_GroundMatrix;
|
|
Vec3V m_vExtent;
|
|
float m_fMinSize;
|
|
float m_fSpeedDiff;
|
|
float m_fSpeedAllowance;
|
|
debugPlayback::phInstId m_GroundInstance;
|
|
int m_iPassedAxisCount;
|
|
int m_iComponent;
|
|
|
|
virtual bool IsInstInvolved(phInstId rInst) const
|
|
{
|
|
return (m_GroundInstance == rInst) || debugPlayback::PhysicsEvent::IsInstInvolved(rInst);
|
|
}
|
|
|
|
virtual void DebugDraw3d(eDrawFlags drawFlags) const
|
|
{
|
|
Color32 col(255,0,0,DebugRecorder::GetGlobalAlphaScale());
|
|
if (drawFlags & eDrawSelected)
|
|
{
|
|
col.SetRed(0);
|
|
}
|
|
if (drawFlags & eDrawHovered)
|
|
{
|
|
col.SetGreen(255);
|
|
}
|
|
|
|
//Draw the instance
|
|
#if __STATS && __PFDRAW
|
|
if (m_GroundInstance.IsValid())
|
|
{
|
|
grcColor(col);
|
|
m_GroundInstance->GetArchetype()->GetBound()->Draw(m_GroundMatrix);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
grcDrawAxis(0.2f, m_GroundMatrix, true);
|
|
}
|
|
}
|
|
|
|
PedGroundInstanceEvent()
|
|
:m_GroundMatrix(V_IDENTITY)
|
|
,m_vExtent(V_ZERO)
|
|
,m_fMinSize(0.0f)
|
|
,m_fSpeedDiff(0.0f)
|
|
,m_fSpeedAllowance(0.0f)
|
|
,m_iPassedAxisCount(0)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
void SetData(const phInst *pGroundInst, int iComponent, Vec3V_In extent, float fMinSize, int iPassedAxisCount, float fSpeedDiff, float fSpeedAllowance)
|
|
{
|
|
m_GroundInstance.Set(pGroundInst);
|
|
if (pGroundInst)
|
|
{
|
|
RecordInst(m_GroundInstance);
|
|
m_GroundMatrix = pGroundInst->GetMatrix();
|
|
}
|
|
m_vExtent = extent;
|
|
m_fMinSize = fMinSize;
|
|
m_fSpeedDiff = fSpeedDiff;
|
|
m_fSpeedAllowance = fSpeedAllowance;
|
|
Assign(m_iComponent, iComponent);
|
|
Assign(m_iPassedAxisCount, iPassedAxisCount);
|
|
}
|
|
|
|
PAR_PARSABLE;
|
|
DR_EVENT_DECL(PedGroundInstanceEvent)
|
|
};
|
|
|
|
struct PedSpringEvent : public debugPlayback::PhysicsEvent
|
|
{
|
|
static float sm_fAxisScale;
|
|
Vec3V m_vForce;
|
|
Vec3V m_vGroundNormal;
|
|
Vec3V m_vGroundVelocity;
|
|
float m_fSpringCompression;
|
|
float m_fSpringDamping;
|
|
float m_fMass;
|
|
|
|
PedSpringEvent()
|
|
:m_vForce(V_ZERO)
|
|
,m_vGroundNormal(V_ZERO)
|
|
,m_vGroundVelocity(V_ZERO)
|
|
,m_fSpringCompression(0.0f)
|
|
,m_fSpringDamping(0.0f)
|
|
,m_fMass(1.0f)
|
|
{
|
|
}
|
|
void AddEventOptions(const Frame &frame, TextOutputVisitor &rText, bool &bMouseDown) const
|
|
{
|
|
PhysicsEvent::AddEventOptions(frame, rText, bMouseDown);
|
|
if (rText.AddLine("Scale [%f]", sm_fAxisScale) && bMouseDown)
|
|
{
|
|
sm_fAxisScale *= .5f;
|
|
if(sm_fAxisScale<0.005f)
|
|
{
|
|
sm_fAxisScale = 0.1f;
|
|
}
|
|
bMouseDown = false;
|
|
}
|
|
}
|
|
|
|
virtual void DebugDraw3d(eDrawFlags drawFlags) const
|
|
{
|
|
Color32 col(255,0,0,DebugRecorder::GetGlobalAlphaScale());
|
|
if (drawFlags & eDrawSelected)
|
|
{
|
|
col.SetRed(0);
|
|
}
|
|
if (drawFlags & eDrawHovered)
|
|
{
|
|
col.SetGreen(255);
|
|
}
|
|
|
|
DrawVelocity(m_vForce, m_Pos, ScalarV(sm_fAxisScale / m_fMass), col);
|
|
|
|
col.SetBlue(255);
|
|
Vec3V damping(0.0f, 0.0f, m_fSpringDamping);
|
|
DrawVelocity(damping, m_Pos, ScalarV(sm_fAxisScale / m_fMass), col);
|
|
}
|
|
|
|
void SetData(Vec3V_In force, Vec3V_In groundNormal, Vec3V_In groundVelocity, float fSpringCompression, float fSpringDamping)
|
|
{
|
|
m_vForce = force;
|
|
m_vGroundNormal = groundNormal;
|
|
m_vGroundVelocity = groundVelocity;
|
|
m_fSpringCompression = fSpringCompression;
|
|
m_fSpringDamping = fSpringDamping;
|
|
if (m_rInst.IsValid() && m_rInst->GetArchetype())
|
|
{
|
|
m_fMass = m_rInst->GetArchetype()->GetMass();
|
|
}
|
|
else
|
|
{
|
|
m_fMass = 1.0f;
|
|
}
|
|
}
|
|
|
|
PAR_PARSABLE;
|
|
DR_EVENT_DECL(PedSpringEvent)
|
|
};
|
|
|
|
struct QPedSpringEvent : public debugPlayback::PhysicsEvent
|
|
{
|
|
static float sm_fAxisScale;
|
|
Vec3V m_Force;
|
|
Vec3V m_Damping;
|
|
float m_fMaxForce;
|
|
float m_fCompression;
|
|
float m_fMass;
|
|
|
|
void AddEventOptions(const Frame &frame, TextOutputVisitor &rText, bool &bMouseDown) const
|
|
{
|
|
PhysicsEvent::AddEventOptions(frame, rText, bMouseDown);
|
|
if (rText.AddLine("Scale [%f]", sm_fAxisScale) && ioMouse::GetDZ())
|
|
{
|
|
const float kScaleProp = 2.0f;
|
|
const float kScaleProp2 = kScaleProp*kScaleProp;
|
|
const float kScaleProp8 = kScaleProp2*kScaleProp2*kScaleProp2*kScaleProp2;
|
|
const float kScaleProp32 = kScaleProp8*kScaleProp8*kScaleProp8*kScaleProp8;
|
|
const float kScalePropMax = kScaleProp32;
|
|
const float kScalePropMin = 1.0f / kScalePropMax;
|
|
sm_fAxisScale *= ioMouse::GetDZ() > 0 ? kScaleProp : 1.0f /kScaleProp;
|
|
sm_fAxisScale = Clamp(sm_fAxisScale, kScalePropMin, kScalePropMax);
|
|
bMouseDown = false;
|
|
}
|
|
}
|
|
|
|
virtual void DebugDraw3d(eDrawFlags drawFlags) const
|
|
{
|
|
Color32 col(255,0,0,DebugRecorder::GetGlobalAlphaScale());
|
|
if (drawFlags & eDrawSelected)
|
|
{
|
|
col.SetRed(0);
|
|
}
|
|
if (drawFlags & eDrawHovered)
|
|
{
|
|
col.SetGreen(255);
|
|
}
|
|
|
|
DrawVelocity(m_Force, m_Pos, ScalarV(sm_fAxisScale / m_fMass), col);
|
|
|
|
col.SetBlue(255);
|
|
DrawVelocity(m_Damping, m_Pos, ScalarV(sm_fAxisScale / m_fMass), col);
|
|
}
|
|
|
|
void SetData(Vec3V_In force, Vec3V_In damping, Vec3V_In forcePos, float fMaxForce, float fCompression)
|
|
{
|
|
m_Force = force;
|
|
m_Damping = damping;
|
|
m_Pos = forcePos;
|
|
m_fMaxForce = fMaxForce;
|
|
m_fCompression = fCompression;
|
|
if (m_rInst.IsValid() && m_rInst->GetArchetype())
|
|
{
|
|
m_Pos += m_rInst->GetMatrix().GetCol3();
|
|
m_fMass = m_rInst->GetArchetype()->GetMass();
|
|
}
|
|
else
|
|
{
|
|
m_fMass = 1.0f;
|
|
}
|
|
}
|
|
|
|
QPedSpringEvent();
|
|
|
|
PAR_PARSABLE;
|
|
DR_EVENT_DECL(QPedSpringEvent)
|
|
};
|
|
|
|
QPedSpringEvent::QPedSpringEvent() : m_Force(V_ZERO), m_Damping(V_ZERO), m_fMaxForce(0.0f), m_fCompression(0.0f) {}
|
|
|
|
struct EntityAttachmentUpdate : public debugPlayback::PhysicsEvent
|
|
{
|
|
Mat34V m_NewMat;
|
|
debugPlayback::phInstId m_AttachedTo;
|
|
virtual bool IsInstInvolved(phInstId rInst) const
|
|
{
|
|
return (m_AttachedTo == rInst) || debugPlayback::PhysicsEvent::IsInstInvolved(rInst);
|
|
}
|
|
|
|
virtual void DebugDraw3d(eDrawFlags drawFlags) const
|
|
{
|
|
Color32 col(255,0,0,DebugRecorder::GetGlobalAlphaScale());
|
|
if (drawFlags & eDrawSelected)
|
|
{
|
|
col.SetRed(0);
|
|
}
|
|
if (drawFlags & eDrawHovered)
|
|
{
|
|
col.SetGreen(255);
|
|
}
|
|
|
|
//Draw the location
|
|
grcDrawAxis(0.2f, m_NewMat, true);
|
|
}
|
|
|
|
EntityAttachmentUpdate() : m_NewMat(V_IDENTITY) {}
|
|
|
|
void SetData(const phInst &rAttachedTo, Mat34V_In matNew)
|
|
{
|
|
m_AttachedTo.Set(&rAttachedTo);
|
|
if (rAttachedTo.IsInLevel())
|
|
{
|
|
if(m_AttachedTo.IsValid())
|
|
{
|
|
RecordInst(m_AttachedTo);
|
|
}
|
|
}
|
|
m_NewMat = matNew;
|
|
}
|
|
|
|
PAR_PARSABLE;
|
|
DR_EVENT_DECL(EntityAttachmentUpdate)
|
|
};
|
|
|
|
struct ScriptEntityEvent : public debugPlayback::PhysicsEvent
|
|
{
|
|
atHashString m_FuncName;
|
|
int m_iEntityAccessCount;
|
|
ScriptEntityEvent():m_iEntityAccessCount(-1){}
|
|
void SetData(const char *pFuncName, int iFuncEntityCount)
|
|
{
|
|
m_FuncName = pFuncName;
|
|
m_iEntityAccessCount = iFuncEntityCount;
|
|
}
|
|
virtual const char* GetEventSubType() const
|
|
{
|
|
return m_FuncName.GetCStr();
|
|
}
|
|
PAR_PARSABLE;
|
|
DR_EVENT_DECL(ScriptEntityEvent)
|
|
};
|
|
|
|
class CStoreTasks : public debugPlayback::PhysicsEvent
|
|
{
|
|
public:
|
|
struct TaskInfo
|
|
{ atHashString m_TaskState;
|
|
u8 m_TaskPriority;
|
|
PAR_SIMPLE_PARSABLE;
|
|
};
|
|
private:
|
|
atArray<TaskInfo> m_TaskList;
|
|
atHashString m_Title;
|
|
public:
|
|
CStoreTasks(const CallstackHelper rCallstack, const phInst *pInst, const fwEntity *pEntity, const char *pString)
|
|
:debugPlayback::PhysicsEvent(rCallstack, pInst)
|
|
,m_Title(pString)
|
|
{
|
|
//Need to roll through any tasks applied to this entity
|
|
const CTaskManager* pTaskManager = NULL;
|
|
|
|
switch(pEntity->GetType())
|
|
{
|
|
case ENTITY_TYPE_PED:
|
|
pTaskManager = static_cast<const CPed*>(pEntity)->GetPedIntelligence() ? static_cast<const CPed*>(pEntity)->GetPedIntelligence()->GetTaskManager() : NULL;
|
|
break;
|
|
case ENTITY_TYPE_VEHICLE:
|
|
pTaskManager = static_cast<const CVehicle*>(pEntity)->GetIntelligence() ? static_cast<const CVehicle*>(pEntity)->GetIntelligence()->GetTaskManager() : NULL;
|
|
break;
|
|
case ENTITY_TYPE_OBJECT:
|
|
pTaskManager = static_cast<const CObject*>(pEntity)->GetObjectIntelligence() ? static_cast<const CObject*>(pEntity)->GetObjectIntelligence()->GetTaskManager() : NULL;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if(pTaskManager)
|
|
{
|
|
int iTotalTaskCount = 0;
|
|
for(s32 i = 0; i < pTaskManager->GetTreeCount() ; i++)
|
|
{
|
|
aiTaskTree* pTree = pTaskManager->GetTree(i);
|
|
if(pTree)
|
|
{
|
|
for(s32 j = 0; j < pTree->GetPriorityCount() ; j++)
|
|
{
|
|
aiTask* pSubTask = pTree->GetTask(j);
|
|
while(pSubTask)
|
|
{
|
|
++iTotalTaskCount;
|
|
pSubTask = pSubTask->GetSubTask();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
m_TaskList.Reset();
|
|
m_TaskList.Resize(iTotalTaskCount);
|
|
|
|
int iTaskCount = 0;
|
|
for(s32 i = 0; i < pTaskManager->GetTreeCount() ; i++)
|
|
{
|
|
aiTaskTree* pTree = pTaskManager->GetTree(i);
|
|
if(pTree)
|
|
{
|
|
for(s32 j = 0; j < pTree->GetPriorityCount() ; j++)
|
|
{
|
|
aiTask* pSubTask = pTree->GetTask(j);
|
|
while(pSubTask)
|
|
{
|
|
char buff[256];
|
|
formatf(buff, "%s : %s", pSubTask->GetTaskName(), pSubTask->GetStateName(pSubTask->GetState()));
|
|
m_TaskList[ iTaskCount ].m_TaskState = buff;
|
|
Assign(m_TaskList[ iTaskCount ].m_TaskPriority, j);
|
|
++iTaskCount;
|
|
pSubTask = pSubTask->GetSubTask();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool DebugEventText(TextOutputVisitor &rOutput) const
|
|
{
|
|
PrintInstName(rOutput);
|
|
rOutput.AddLine("%s", m_Title.GetCStr());
|
|
rOutput.PushIndent();
|
|
for (int i=m_TaskList.GetCount()-1 ; i>=0 ; i--)
|
|
{
|
|
rOutput.AddLine("%d - %s", m_TaskList[i].m_TaskPriority, m_TaskList[i].m_TaskState.GetCStr());
|
|
}
|
|
rOutput.PopIndent();
|
|
return true;
|
|
}
|
|
|
|
const char *GetEventSubType() const
|
|
{
|
|
return m_Title.GetCStr();
|
|
}
|
|
|
|
CStoreTasks(){}
|
|
~CStoreTasks(){}
|
|
PAR_PARSABLE;
|
|
DR_EVENT_DECL(CStoreTasks);
|
|
};
|
|
|
|
CameraEvent::CameraEvent()
|
|
:m_vCamPos(V_ZERO)
|
|
,m_vCamUp(V_ZERO)
|
|
,m_vCamFront(V_ZERO)
|
|
,m_fFarClip(0.0f)
|
|
,m_fTanFOV(0.0f)
|
|
,m_fTanVFOV(0.0f)
|
|
{
|
|
}
|
|
|
|
CameraEvent::CameraEvent(const debugPlayback::CallstackHelper rCallstack)
|
|
:debugPlayback::EventBase(rCallstack)
|
|
{
|
|
//Get current camera information
|
|
const grcViewport &vp = g_SceneToGBufferPhaseNew->GetGrcViewport();
|
|
m_vCamPos = vp.GetCameraPosition();
|
|
m_vCamUp = vp.GetCameraMtx().GetCol1();
|
|
m_vCamFront = vp.GetCameraMtx().GetCol2();
|
|
m_fFarClip = vp.GetFarClip();
|
|
m_fTanFOV = vp.GetTanHFOV();
|
|
m_fTanVFOV = vp.GetTanVFOV();
|
|
}
|
|
|
|
void CameraEvent::DebugDraw3d(debugPlayback::eDrawFlags /*drawFlags*/) const
|
|
{
|
|
//Draw frustrum?
|
|
static const float kFrustrumSize = 100.0f;
|
|
static const float fOffsets[4][2] = {
|
|
{-kFrustrumSize, kFrustrumSize},
|
|
{ kFrustrumSize, kFrustrumSize},
|
|
{ kFrustrumSize, -kFrustrumSize},
|
|
{-kFrustrumSize, -kFrustrumSize}
|
|
};
|
|
|
|
Vec3V vCamLeft = Cross(m_vCamUp, m_vCamFront);
|
|
for (int i=0 ; i<4 ; i++)
|
|
{
|
|
Vec3V vEnd = m_vCamPos;
|
|
vEnd += m_vCamFront * ScalarV(-kFrustrumSize); //Cam front points backwards?
|
|
vEnd += m_vCamUp * ScalarV(fOffsets[i][0]);
|
|
vEnd += vCamLeft * ScalarV(fOffsets[i][1]);
|
|
grcDrawLine(m_vCamPos, vEnd, Color32(255,255,255));
|
|
}
|
|
}
|
|
|
|
bool CameraEvent::Replay(phInst *) const
|
|
{
|
|
camDebugDirector& dbgDirector = camInterface::GetDebugDirector();
|
|
CControlMgr::SetDebugPadOn(true);
|
|
dbgDirector.ActivateFreeCam();
|
|
camFrame& freeCamFrame = dbgDirector.GetFreeCamFrameNonConst();
|
|
freeCamFrame.SetPosition(RCC_VECTOR3(m_vCamPos));
|
|
Vector3 vfront = VEC3V_TO_VECTOR3(-m_vCamFront);
|
|
freeCamFrame.SetWorldMatrixFromFrontAndUp(vfront, RCC_VECTOR3(m_vCamUp));
|
|
freeCamFrame.SetFarClip(m_fFarClip);
|
|
return false;
|
|
}
|
|
|
|
float QPedSpringEvent::sm_fAxisScale = 0.1f;
|
|
float PedSpringEvent::sm_fAxisScale = 0.1f;
|
|
|
|
DR_EVENT_IMP(QPedSpringEvent);
|
|
DR_EVENT_IMP(CStoreTasks);
|
|
DR_EVENT_IMP(CameraEvent);
|
|
DR_EVENT_IMP(PedSpringEvent);
|
|
DR_EVENT_IMP(PedGroundInstanceEvent);
|
|
DR_EVENT_IMP(PedDesiredVelocityEvent);
|
|
DR_EVENT_IMP(DRNetBlenderStateEvent);
|
|
DR_EVENT_IMP(VehicleDataEvent);
|
|
DR_EVENT_IMP(PedDataEvent);
|
|
DR_EVENT_IMP(PedCapsuleInfoEvent);
|
|
DR_EVENT_IMP(PedAnimatedVelocityEvent);
|
|
DR_EVENT_IMP(VehicleTargetPointEvent);
|
|
DR_EVENT_IMP(VehicleAvoidenceDataEvent);
|
|
DR_EVENT_IMP(EntityAttachmentUpdate);
|
|
DR_EVENT_IMP(ScriptEntityEvent);
|
|
|
|
void CurrentInstChange(phInstId rOld, const phInst *pNew)
|
|
{
|
|
if (rOld.IsAssigned())
|
|
{
|
|
//Keep link from the old NewObjectData to the new one that will be formed.
|
|
debugPlayback::DebugRecorder *pInstance = debugPlayback::DebugRecorder::GetInstance();
|
|
if(pInstance)
|
|
{
|
|
debugPlayback::DataBase *pData = pInstance->GetSharedData(rOld.ToU32());
|
|
if (pData)
|
|
{
|
|
DebugReplayObjectInfo *pNewObjectData = dynamic_cast<DebugReplayObjectInfo*>(pData);
|
|
if (pNewObjectData)
|
|
{
|
|
pNewObjectData->SetNextInst(pNew);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
debugPlayback::InstSwappedOut(rOld, pNew);
|
|
}
|
|
|
|
const phInst *GetInstFromCreature(const crCreature *pCreature)
|
|
{
|
|
GameRecorder *pInstance = GameRecorder::GetAppLevelInstance();
|
|
if(pInstance)
|
|
{
|
|
return pInstance->GetInstFromCreature(pCreature);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
void SyncToNet(const msgSyncDRDisplay &rMsg)
|
|
{
|
|
GameRecorder *pInst = GameRecorder::GetAppLevelInstance();
|
|
if (pInst)
|
|
{
|
|
if (rMsg.m_bDisplayOn != pInst->IsDisplayOn())
|
|
{
|
|
pInst->ToggleDisplay();
|
|
}
|
|
|
|
//Sync event time line display
|
|
pInst->SetZoomDataZoom(rMsg.m_fZoom);
|
|
pInst->SetZoomDataOffset(rMsg.m_fOffset);
|
|
|
|
//TODO (probably worth scrubbing from current location)
|
|
const Frame *pFrame = pInst->GetFirstFramePtr();
|
|
if (!pFrame || (pFrame->m_NetworkTime > rMsg.m_iNetTime))
|
|
{
|
|
return;
|
|
}
|
|
|
|
const Frame *pLastFrame = pFrame;
|
|
while (pFrame)
|
|
{
|
|
if (pFrame->m_NetworkTime > rMsg.m_iNetTime)
|
|
{
|
|
if (pLastFrame != pInst->GetSelectedFrame())
|
|
{
|
|
pInst->Select(pLastFrame);
|
|
}
|
|
return;
|
|
}
|
|
pLastFrame = pFrame;
|
|
pFrame = pFrame->mp_Next;
|
|
}
|
|
}
|
|
}
|
|
|
|
#if __DEV
|
|
void TrackScriptGetsEntityForMod(fwEntity *pEntity)
|
|
{
|
|
if (pEntity && DR_EVENT_ENABLED(ScriptEntityEvent))
|
|
{
|
|
scrThread *pThread = scrThread::GetActiveThread();
|
|
if (pThread && pEntity->GetCurrentPhysicsInst())
|
|
{
|
|
const char *pFuncName = scrThread::GetCurrentCmdName();
|
|
if (pFuncName)
|
|
{
|
|
//TMS: We can spam this multiple times when calling a single script function so try and filter down to
|
|
// a single event per entity involved. What would be cool would be to have a single event with
|
|
// all the entities involved attached but that is a bit complicated to do at the moment.
|
|
static u32 s_CmdCounter=0xffffffff;
|
|
static u32 s_CmdEntityListCount;
|
|
const int kMaxEntitiesToTrack = 8;
|
|
static fwEntity *s_CmdEntityList[kMaxEntitiesToTrack];
|
|
if (scrThread::sm_DevCurrentCmdNameCounter != s_CmdCounter)
|
|
{
|
|
s_CmdEntityListCount = 1;
|
|
s_CmdCounter = scrThread::sm_DevCurrentCmdNameCounter;
|
|
s_CmdEntityList[0] = pEntity;
|
|
|
|
}
|
|
else
|
|
{
|
|
for (int i=0 ; i<s_CmdEntityListCount ; i++)
|
|
{
|
|
//Did we already track this entity for this call?
|
|
if (s_CmdEntityList[i] == pEntity)
|
|
return;
|
|
}
|
|
|
|
if (s_CmdEntityListCount < kMaxEntitiesToTrack)
|
|
{
|
|
s_CmdEntityList[s_CmdEntityListCount] = pEntity;
|
|
++s_CmdEntityListCount;
|
|
}
|
|
}
|
|
|
|
debugPlayback::SetUseScriptCallstack(true);
|
|
|
|
ScriptEntityEvent *pEvent = AddPhysicsEvent<ScriptEntityEvent>(pEntity->GetCurrentPhysicsInst());
|
|
if (pEvent)
|
|
{
|
|
pEvent->SetData(pFuncName, s_CmdEntityListCount);
|
|
}
|
|
|
|
debugPlayback::SetUseScriptCallstack(false);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void RecordQuadrupedSpringForce(const phInst *pInst, Vec3V_In pos, Vec3V_In force, Vec3V_In damping, float fMaxForce, float fCompression)
|
|
{
|
|
QPedSpringEvent *pEvent = AddPhysicsEvent<QPedSpringEvent>(pInst);
|
|
if (pEvent)
|
|
{
|
|
pEvent->SetData(force, damping, pos, fMaxForce, fCompression);
|
|
}
|
|
}
|
|
|
|
void RecordPedSpringForce(const phInst *pInst, Vec3V_In force, Vec3V_In groundNormal, Vec3V_In groundVelocity, float fSpringCompression, float fSpringDamping)
|
|
{
|
|
PedSpringEvent *pEvent = AddPhysicsEvent<PedSpringEvent>(pInst);
|
|
if (pEvent)
|
|
{
|
|
pEvent->SetData(force, groundNormal, groundVelocity, fSpringCompression, fSpringDamping);
|
|
}
|
|
}
|
|
|
|
void RecordPedFlag(const phInst *pInst, const char *pFlagName, bool bFlag)
|
|
{
|
|
SetTaggedFloatEvent *pEvent = AddPhysicsEvent<SetTaggedFloatEvent>(pInst);
|
|
if (pEvent)
|
|
{
|
|
DR_MEMORY_HEAP();
|
|
pEvent->SetData(bFlag ? 1.0f : 0.0f, pFlagName);
|
|
}
|
|
}
|
|
|
|
void RecordPedDesiredVelocity(const phInst *pInst, Vec3V_In linear, Vec3V_In angular)
|
|
{
|
|
PedDesiredVelocityEvent *pEvent = AddPhysicsEvent<PedDesiredVelocityEvent>(pInst);
|
|
if (pEvent)
|
|
{
|
|
pEvent->SetData(linear, angular);
|
|
}
|
|
}
|
|
|
|
void RecordPedCapsuleInfo(const phInst *pInst, Vec3V_In start, Vec3V_In end, Vec3V_In ground, float radius, Vec3V_In boundOffset)
|
|
{
|
|
PedCapsuleInfoEvent *pEvent = AddPhysicsEvent<PedCapsuleInfoEvent>(pInst);
|
|
if (pEvent)
|
|
{
|
|
pEvent->SetData(start, end, ground, radius, boundOffset);
|
|
}
|
|
}
|
|
|
|
void RecordBlenderState(const phInst *pInst, Mat34V_In lastReceivedMatrix, bool positionUpdated, bool orientationUpdated, bool velocityUpdated, bool angVelocityUpdated)
|
|
{
|
|
DRNetBlenderStateEvent *pEvent = AddPhysicsEvent<DRNetBlenderStateEvent>(pInst);
|
|
if (pEvent)
|
|
{
|
|
pEvent->SetData(lastReceivedMatrix, positionUpdated, orientationUpdated, velocityUpdated, angVelocityUpdated);
|
|
}
|
|
}
|
|
|
|
void RecordPedAnimatedVelocity(const phInst *pInst, Vec3V_In linear)
|
|
{
|
|
PedAnimatedVelocityEvent *pEvent = AddPhysicsEvent<PedAnimatedVelocityEvent>(pInst);
|
|
if (pEvent)
|
|
{
|
|
pEvent->SetData(linear);
|
|
}
|
|
}
|
|
|
|
void RecordPedGroundInstance(const phInst *pInst, const phInst *pGroundInst, int iComponent, Vec3V_In extent, float fMinSize, int iPassedAxisCount, float fSpeedDiff, float fSpeedAllowance)
|
|
{
|
|
//if (pGroundInst)
|
|
{
|
|
PedGroundInstanceEvent *pEvent = AddPhysicsEvent<PedGroundInstanceEvent>(pInst);
|
|
if (pEvent)
|
|
{
|
|
pEvent->SetData(pGroundInst, iComponent, extent, fMinSize, iPassedAxisCount, fSpeedDiff, fSpeedAllowance);
|
|
}
|
|
}
|
|
}
|
|
|
|
void RecordEntityAttachment(const phInst *attached, const phInst *attachedTo, const Matrix34 &rNew)
|
|
{
|
|
if (attached && attachedTo)
|
|
{
|
|
EntityAttachmentUpdate *pEvent = AddPhysicsEvent<EntityAttachmentUpdate>(attached);
|
|
if (pEvent)
|
|
{
|
|
pEvent->SetData(*attachedTo, RCC_MAT34V(rNew));
|
|
}
|
|
}
|
|
}
|
|
|
|
void RecordVehicleObstructionArray(const phInst *pVehicleInst, const void* pVoid, int iArraySize, float fVehicleDriveOrientation, Vec3V_In vRight, Vec3V_In vVehForwards)
|
|
{
|
|
if (!DR_EVENT_ENABLED(VehicleAvoidenceDataEvent))
|
|
{
|
|
return;
|
|
}
|
|
|
|
int iObsCount=0;
|
|
float fMinObstructionRange=FLT_MAX;
|
|
float fMaxObstructionRange=0;
|
|
float fAngleSpread = 0.0f;
|
|
|
|
const CTaskVehicleGoToPointWithAvoidanceAutomobile::AvoidanceInfo *AvoidanceInfoArray = static_cast<const CTaskVehicleGoToPointWithAvoidanceAutomobile::AvoidanceInfo*>(pVoid);
|
|
for (int i = 0; i < iArraySize; i++)
|
|
{
|
|
if (AvoidanceInfoArray[i].ObstructionType != CTaskVehicleGoToPointWithAvoidanceAutomobile::AvoidanceInfo::Invalid_Obstruction)
|
|
{
|
|
float fDist = AvoidanceInfoArray[i].fDistToObstruction;
|
|
fMinObstructionRange = Min(fMinObstructionRange, fDist);
|
|
fMaxObstructionRange = Max(fMaxObstructionRange, fDist);
|
|
fAngleSpread = Max(fAngleSpread, Abs(AvoidanceInfoArray[i].fDirection));
|
|
++iObsCount;
|
|
}
|
|
}
|
|
|
|
if (iObsCount == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
VehicleAvoidenceDataEvent *pNewEvent = AddPhysicsEvent<VehicleAvoidenceDataEvent>(pVehicleInst);
|
|
if (pNewEvent)
|
|
{
|
|
DR_MEMORY_HEAP();
|
|
float fCentralDirection = AvoidanceInfoArray[0].fDirection;
|
|
|
|
pNewEvent->SetData(fMinObstructionRange, fMaxObstructionRange, fCentralDirection, fAngleSpread, fVehicleDriveOrientation, vRight, vVehForwards);
|
|
pNewEvent->m_Obstructions.Resize(iObsCount);
|
|
iObsCount = 0;
|
|
for (int i = 0; i < iArraySize; i++)
|
|
{
|
|
CTaskVehicleGoToPointWithAvoidanceAutomobile::AvoidanceInfo::eObstructionType eObType = AvoidanceInfoArray[i].ObstructionType;
|
|
if (eObType != CTaskVehicleGoToPointWithAvoidanceAutomobile::AvoidanceInfo::Invalid_Obstruction)
|
|
{
|
|
float fDirDelta = AvoidanceInfoArray[i].fDirection - fCentralDirection;
|
|
if (fDirDelta > PI)
|
|
{
|
|
fDirDelta -= 2.0f*PI;
|
|
}
|
|
else if (fDirDelta < -PI)
|
|
{
|
|
fDirDelta += 2.0f*PI;
|
|
}
|
|
float fQuantizedDirection = (Clamp(fDirDelta/PI, -1.0f, 1.0f)) * 2047.0f;
|
|
pNewEvent->m_Obstructions[iObsCount].m_Direction = u16(fQuantizedDirection);
|
|
pNewEvent->m_Obstructions[iObsCount].m_Dist = u16((65535.0f * Min(256.0f, AvoidanceInfoArray[i].fDistToObstruction)) / 256.0f);
|
|
pNewEvent->m_Obstructions[iObsCount].m_Type = eObType;
|
|
Assert(pNewEvent->m_Obstructions[iObsCount].m_Type == eObType);
|
|
++iObsCount;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void SetUseScriptCallstack(bool bUseIt)
|
|
{
|
|
GameRecorder *pGameRecorder = GameRecorder::GetAppLevelInstance();
|
|
if (pGameRecorder)
|
|
{
|
|
pGameRecorder->SetUseScriptCallstack(bUseIt);
|
|
}
|
|
}
|
|
|
|
void SetVisualSkeletonIfPossible(fwEntity *pEntity)
|
|
{
|
|
GameRecorder *pInstance = GameRecorder::GetAppLevelInstance();
|
|
if (pInstance)
|
|
{
|
|
pInstance->ReposePedIfPossible(pEntity);
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
debugPlayback::Callstack *spCallStack = 0;
|
|
void ScriptCallstackPrintStackTrace(const char *fmt, ...)
|
|
{
|
|
if (spCallStack && spCallStack->m_SymbolArray.GetCount() < spCallStack->m_SymbolArray.GetCapacity())
|
|
{
|
|
va_list args;
|
|
va_start(args, fmt);
|
|
|
|
char buff[256];
|
|
vformatf(buff, sizeof(buff), fmt, args);
|
|
va_end(args);
|
|
spCallStack->m_SymbolArray.Append() = buff;
|
|
}
|
|
}
|
|
|
|
size_t GameRecorder::RegisterCallstack(int iIgnorelevels)
|
|
{
|
|
if (m_bUseScriptCallstack && scrThread::GetActiveThread())
|
|
{
|
|
scrThread *pActiveThread = scrThread::GetActiveThread();
|
|
|
|
//Cache strings etc right now
|
|
|
|
const int kCallstackDepth = 12;
|
|
int temp[kCallstackDepth];
|
|
for (int i=0 ; i<kCallstackDepth ; i++)
|
|
{
|
|
temp[i] = 0;
|
|
}
|
|
|
|
pActiveThread->CaptureStackTrace(temp,kCallstackDepth);
|
|
size_t hash = 0;
|
|
for (int i=0; i<kCallstackDepth; i++)
|
|
hash ^= temp[i];
|
|
|
|
//New callstack?
|
|
sysCriticalSection section(m_CallstackCSToken);
|
|
if (!m_Callstacks.Access(hash))
|
|
{
|
|
DR_MEMORY_HEAP();
|
|
debugPlayback::Callstack *pCallstack = rage_new debugPlayback::Callstack;
|
|
m_Callstacks[hash] = pCallstack;
|
|
|
|
//Common array's use size_t so copy data across
|
|
size_t temp2[kCallstackDepth];
|
|
for (int i=0 ; i<kCallstackDepth ; i++)
|
|
{
|
|
temp2[i] = temp[i];
|
|
}
|
|
pCallstack->Init(temp2, kCallstackDepth);
|
|
pCallstack->m_iSignature = hash;
|
|
|
|
//Cache the strings right now while we've got the script program loaded (will only do once per new callstack)
|
|
pCallstack->m_SymbolArray.Reserve(pCallstack->m_AddressArray.GetCount());
|
|
spCallStack = pCallstack;
|
|
pActiveThread->PrintStackTrace(ScriptCallstackPrintStackTrace);
|
|
spCallStack = 0;
|
|
return hash;
|
|
}
|
|
|
|
return hash;
|
|
}
|
|
|
|
return DebugRecorder::RegisterCallstack(iIgnorelevels + 1);
|
|
}
|
|
|
|
//Make sure we highlight related events when selecting one
|
|
void GameRecorder::OnEventSelection(const debugPlayback::EventBase *pEvent)
|
|
{
|
|
if (pEvent && pEvent->IsPhysicsEvent())
|
|
{
|
|
const debugPlayback::PhysicsEvent *pPhEvent = static_cast<const debugPlayback::PhysicsEvent*>(pEvent);
|
|
const DebugReplayObjectInfo *pObject = GetObjectInfo(pPhEvent->m_rInst);
|
|
if (pObject)
|
|
{
|
|
SetDataToHighlight(pObject);
|
|
return;
|
|
}
|
|
}
|
|
|
|
//Make sure its clear if we selected an event and it has no data to display
|
|
//HOWEVER don't clear it if we are clearing the selected event as this "loses the trail" when debugging
|
|
if (pEvent)
|
|
{
|
|
SetDataToHighlight(0);
|
|
}
|
|
}
|
|
|
|
void GameRecorder::OnNewDataSelection()
|
|
{
|
|
const DebugReplayObjectInfo *pObject = GetObjectInfo(debugPlayback::GetCurrentSelectedEntity());
|
|
if (pObject)
|
|
{
|
|
SetDataToHighlight(pObject);
|
|
}
|
|
}
|
|
|
|
//Moved down here to understand classes declared above
|
|
void GameRecorder::PreUpdate()
|
|
{
|
|
static int iLastFrameTrapped = 0;
|
|
int iGetCurrentFrame = fwTimer::GetFrameCount();
|
|
m_bStoreDataThisFrame = (!fwTimer::IsGamePaused()) || (iGetCurrentFrame > iLastFrameTrapped);
|
|
iLastFrameTrapped = iGetCurrentFrame;
|
|
|
|
#if DR_ANIMATIONS_ENABLED
|
|
//Map for "quick" lookups between creature ptrs and insts
|
|
m_CreatureToInstMap.Kill();
|
|
m_CachedCreatureLookupCreature = 0;
|
|
m_CachedCreatureLookupInst = 0;
|
|
if (mvDRTrackInterface::sm_bEnabled)
|
|
{
|
|
for (s32 pedIndex = 0; pedIndex < CPed::GetPool()->GetSize(); ++pedIndex)
|
|
{
|
|
CPed* pPed = CPed::GetPool()->GetSlot(pedIndex);
|
|
if(pPed && pPed->GetCreature() && pPed->GetCurrentPhysicsInst() && debugPlayback::PhysicsEvent::IsSelected(*pPed->GetCurrentPhysicsInst()))
|
|
{
|
|
m_CreatureToInstMap.Insert(pPed->GetCreature() , pPed->GetCurrentPhysicsInst());
|
|
}
|
|
}
|
|
}
|
|
|
|
if (DR_EVENT_ENABLED( StoreSkeletonPreUpdate ))
|
|
{
|
|
if (m_bStoreDataThisFrame)
|
|
{
|
|
debugPlayback::DebugRecorder *pInstance = debugPlayback::DebugRecorder::GetInstance();
|
|
if (pInstance)
|
|
{
|
|
debugPlayback::CallstackHelper ch;
|
|
if (pInstance->IsRecording())
|
|
{
|
|
Color32 startFrameColor(255,255,0);
|
|
|
|
if (m_bRecordSkeletonsEveryFrame)
|
|
{
|
|
DR_MEMORY_HEAP();
|
|
for (s32 pedIndex = 0; pedIndex < CPed::GetPool()->GetSize(); ++pedIndex)
|
|
{
|
|
CPed* pPed = CPed::GetPool()->GetSlot(pedIndex);
|
|
if(pPed && pPed->GetCurrentPhysicsInst() && debugPlayback::PhysicsEvent::IsSelected(*pPed->GetCurrentPhysicsInst()))
|
|
{
|
|
pInstance->AddEvent(*rage_new StoreSkeletonPreUpdate(ch, pPed->GetCurrentPhysicsInst(), pPed, "Skel:StartOfFrame", startFrameColor));
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DR_MEMORY_HEAP();
|
|
//Record skeletons for all insts with other data recorded
|
|
for (int i=0 ; i<m_LastFrameInsts.GetCount() ; i++)
|
|
{
|
|
if(m_LastFrameInsts[i].IsValid())
|
|
{
|
|
CEntity *pEntity = (CEntity *)m_LastFrameInsts[i]->GetUserData();
|
|
if(pEntity)
|
|
{
|
|
if (DR_EVENT_ENABLED( StoreSkeletonPreUpdate ))
|
|
{
|
|
pInstance->AddEvent(*rage_new StoreSkeletonPreUpdate(ch, m_LastFrameInsts[i], pEntity, "Skel:StartOfFrame", startFrameColor));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void GameRecorder::ReposePedIfPossible(fwEntity *pEntity)
|
|
{
|
|
if (m_bPosePedsIfPossible && (!IsRecording() || fwTimer::IsUserPaused()))
|
|
{
|
|
sysCriticalSection critSection(m_DataCSToken); //Stop data being modified while we're in here, probably won't be anyway
|
|
const debugPlayback::Frame *pCurrentFrame = GetHoveredFrame();
|
|
if (!pCurrentFrame)
|
|
{
|
|
pCurrentFrame = GetSelectedFrame();
|
|
}
|
|
if (pCurrentFrame)
|
|
{
|
|
//Quick roll through entities
|
|
const debugPlayback::EventBase *pEvent = pCurrentFrame->mp_LastEvent;
|
|
while (pEvent)
|
|
{
|
|
if (StoreSkeleton::IsSkeletonEvent(pEvent))
|
|
{
|
|
phInst *pInst = ((StoreSkeleton*)pEvent)->m_rInst;
|
|
if (pInst && pInst->GetUserData() == (void*)pEntity)
|
|
{
|
|
pEvent->Replay(pEntity->GetCurrentPhysicsInst());
|
|
return;
|
|
}
|
|
}
|
|
pEvent = pEvent->mp_Prev;
|
|
}
|
|
|
|
//Slower, check history to allow for inst changes (mover -> ragdoll -> mover etc)
|
|
pEvent = pCurrentFrame->mp_LastEvent;
|
|
while (pEvent)
|
|
{
|
|
if (StoreSkeleton::IsSkeletonEvent(pEvent))
|
|
{
|
|
debugPlayback::phInstId rCurrentInst(pEntity->GetCurrentPhysicsInst());
|
|
if (DoInstsShareAnEntity(rCurrentInst, ((StoreSkeleton*)pEvent)->m_rInst))
|
|
{
|
|
pEvent->Replay(pEntity->GetCurrentPhysicsInst());
|
|
return;
|
|
}
|
|
}
|
|
pEvent = pEvent->mp_Prev;
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
void GameRecorder::Update()
|
|
{
|
|
if (m_bSyncNetDisplays)
|
|
{
|
|
msgSyncDRDisplay tmpMsg(m_iDrawnFrameNetTime, IsDisplayOn(), GetZoomDataZoom(), GetZoomDataOffset());
|
|
if (tmpMsg != m_LastSyncMsg)
|
|
{
|
|
NetworkDebug::SyncDRFrame(tmpMsg);
|
|
m_LastSyncMsg = tmpMsg;
|
|
}
|
|
}
|
|
|
|
//High level gates to minimize performance impact
|
|
UpdateAnimEventRecordingState();
|
|
CPhysical::smb_RecordAttachments = DR_EVENT_ENABLED(debugPlayback::EntityAttachmentUpdate);
|
|
debugPlayback::UpdateTaskTransitionHighLevelGate();
|
|
|
|
if (sbTrackPlayer)
|
|
{
|
|
//Make sure the selected inst is the players current inst (note we might
|
|
//lose a few events on the switch, would need to hook into the switch code itself
|
|
//to fix that).
|
|
CPed* pPlayer = CGameWorld::FindLocalPlayer();
|
|
if (pPlayer)
|
|
{
|
|
phInst *pCurrentInst = pPlayer->GetCurrentPhysicsInst();
|
|
debugPlayback::SetCurrentSelectedEntity(pCurrentInst, true);
|
|
}
|
|
}
|
|
|
|
//Blast position and range through
|
|
if (debugPlayback::GetTrackMode() == debugPlayback::eTrackInRange)
|
|
{
|
|
CPed* pPlayer = CGameWorld::FindLocalPlayer();
|
|
if (pPlayer)
|
|
{
|
|
ScalarV vRange(sfTrackRange);
|
|
Vec3V vPos(pPlayer->GetTransform().GetPosition());
|
|
debugPlayback::SetTrackDistToRecord(vPos, vRange);
|
|
}
|
|
}
|
|
|
|
if (m_bStoreDataThisFrame)
|
|
{
|
|
#if DR_ANIMATIONS_ENABLED
|
|
if (DR_EVENT_ENABLED( StoreSkeletonPostUpdate ) || DR_EVENT_ENABLED( debugPlayback::CStoreTasks ))
|
|
{
|
|
debugPlayback::DebugRecorder *pInstance = debugPlayback::DebugRecorder::GetInstance();
|
|
if (pInstance)
|
|
{
|
|
debugPlayback::CallstackHelper ch;
|
|
if (pInstance->IsRecording())
|
|
{
|
|
DR_MEMORY_HEAP();
|
|
Color32 startFrameColor(0,255,255);
|
|
|
|
//Record skeletons for all insts recorded
|
|
for (int i=0 ; i<m_FrameInsts.GetCount() ; i++)
|
|
{
|
|
if(m_FrameInsts[i].IsValid())
|
|
{
|
|
CEntity *pEntity = (CEntity *)m_FrameInsts[i]->GetUserData();
|
|
if(pEntity)
|
|
{
|
|
if (DR_EVENT_ENABLED( StoreSkeletonPostUpdate ) && !m_bRecordSkeletonsEveryFrame)
|
|
{
|
|
pInstance->AddEvent(*rage_new StoreSkeletonPostUpdate(ch, m_FrameInsts[i], pEntity, "Skel:EndOfFrame", startFrameColor));
|
|
}
|
|
if (DR_EVENT_ENABLED( debugPlayback::CStoreTasks ))
|
|
{
|
|
pInstance->AddEvent(*rage_new debugPlayback::CStoreTasks(ch, m_FrameInsts[i], pEntity, "Tasks:EndOfFrame"));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//Record every frame ped information
|
|
if (DR_EVENT_ENABLED(StoreSkeletonPostUpdate) && m_bRecordSkeletonsEveryFrame)
|
|
{
|
|
debugPlayback::DebugRecorder *pInstance = debugPlayback::DebugRecorder::GetInstance();
|
|
if (pInstance)
|
|
{
|
|
for (s32 pedIndex = 0; pedIndex < CPed::GetPool()->GetSize(); ++pedIndex)
|
|
{
|
|
CPed* pPed = CPed::GetPool()->GetSlot(pedIndex);
|
|
if(pPed && pPed->GetCurrentPhysicsInst() && debugPlayback::PhysicsEvent::IsSelected(*pPed->GetCurrentPhysicsInst()))
|
|
{
|
|
pInstance->AddEvent(*rage_new StoreSkeletonPostUpdate(ch, pPed->GetCurrentPhysicsInst(), pPed, "Skel:EndOfFrame", startFrameColor));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif //DR_ANIMATIONS_ENABLED
|
|
|
|
//Record every frame vehicle information
|
|
if (DR_EVENT_ENABLED(debugPlayback::VehicleDataEvent) || DR_EVENT_ENABLED(debugPlayback::VehicleTargetPointEvent))
|
|
{
|
|
debugPlayback::DebugRecorder *pInstance = debugPlayback::DebugRecorder::GetInstance();
|
|
if (pInstance)
|
|
{
|
|
debugPlayback::CallstackHelper ch;
|
|
if (pInstance->IsRecording())
|
|
{
|
|
DR_MEMORY_HEAP();
|
|
for (s32 vehIndex = 0; vehIndex < CVehicle::GetPool()->GetSize(); ++vehIndex)
|
|
{
|
|
CVehicle* pVehicle = CVehicle::GetPool()->GetSlot(vehIndex);
|
|
if(pVehicle && pVehicle->GetCurrentPhysicsInst() && debugPlayback::PhysicsEvent::IsSelected(*pVehicle->GetCurrentPhysicsInst()))
|
|
{
|
|
if (DR_EVENT_ENABLED(debugPlayback::VehicleDataEvent))
|
|
{
|
|
pInstance->AddEvent(*rage_new debugPlayback::VehicleDataEvent(ch, pVehicle->GetCurrentPhysicsInst()));
|
|
}
|
|
|
|
if (DR_EVENT_ENABLED(debugPlayback::VehicleTargetPointEvent))
|
|
{
|
|
CTask* pActiveTask = pVehicle->GetIntelligence()->GetActiveTask();
|
|
while(pActiveTask)
|
|
{
|
|
if(pActiveTask->IsVehicleMissionTask())
|
|
{
|
|
Vector3 vCoors;
|
|
static_cast<CTaskVehicleMissionBase *>(pActiveTask)->FindTargetCoors(pVehicle, vCoors);
|
|
if (vCoors.Mag2() > SMALL_FLOAT)
|
|
{
|
|
debugPlayback::VehicleTargetPointEvent *pTPE = rage_new debugPlayback::VehicleTargetPointEvent(ch, pVehicle->GetCurrentPhysicsInst());
|
|
char eventName[128];
|
|
formatf(eventName, "%s : TARGET", pActiveTask->GetName().c_str());
|
|
pTPE->SetData(eventName, RCC_VEC3V(vCoors));
|
|
pInstance->AddEvent(*pTPE);
|
|
}
|
|
}
|
|
pActiveTask = pActiveTask->GetSubTask();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//Record every frame ped information
|
|
if (DR_EVENT_ENABLED(debugPlayback::PedDataEvent))
|
|
{
|
|
debugPlayback::DebugRecorder *pInstance = debugPlayback::DebugRecorder::GetInstance();
|
|
if (pInstance)
|
|
{
|
|
debugPlayback::CallstackHelper ch;
|
|
if (pInstance->IsRecording())
|
|
{
|
|
DR_MEMORY_HEAP();
|
|
for (s32 pedIndex = 0; pedIndex < CPed::GetPool()->GetSize(); ++pedIndex)
|
|
{
|
|
CPed* pPed = CPed::GetPool()->GetSlot(pedIndex);
|
|
if(pPed && pPed->GetCurrentPhysicsInst() && debugPlayback::PhysicsEvent::IsSelected(*pPed->GetCurrentPhysicsInst()))
|
|
{
|
|
pInstance->AddEvent(*rage_new debugPlayback::PedDataEvent(ch, pPed->GetCurrentPhysicsInst()));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
if (DR_EVENT_ENABLED( AnimMotionTreeTracker ))
|
|
{
|
|
debugPlayback::DebugRecorder *pInstance = debugPlayback::DebugRecorder::GetInstance();
|
|
if (pInstance)
|
|
{
|
|
debugPlayback::CallstackHelper ch;
|
|
if (pInstance->IsRecording())
|
|
{
|
|
DR_MEMORY_HEAP();
|
|
for (s32 pedIndex = 0; pedIndex < CPed::GetPool()->GetSize(); ++pedIndex)
|
|
{
|
|
CPed* pPed = CPed::GetPool()->GetSlot(pedIndex);
|
|
if(pPed && pPed->GetCurrentPhysicsInst() && debugPlayback::PhysicsEvent::IsSelected(*pPed->GetCurrentPhysicsInst()))
|
|
{
|
|
pInstance->AddEvent(*rage_new AnimMotionTreeTracker(ch, pPed->GetCurrentPhysicsInst(), "MotionTree:EndOfFrame"));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (DR_EVENT_ENABLED(debugPlayback::CameraEvent))
|
|
{
|
|
debugPlayback::DebugRecorder *pInstance = debugPlayback::DebugRecorder::GetInstance();
|
|
if (pInstance)
|
|
{
|
|
debugPlayback::CallstackHelper ch;
|
|
if (pInstance->IsRecording())
|
|
{
|
|
DR_MEMORY_HEAP();
|
|
pInstance->AddEvent(*rage_new debugPlayback::CameraEvent(ch));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (GetFirstFramePtr())
|
|
{
|
|
//Not recording data try and set the camera up?
|
|
if (m_bReplayCamera)
|
|
{
|
|
const debugPlayback::Frame *pCurrentFrame = GetHoveredFrame();
|
|
if (!pCurrentFrame)
|
|
{
|
|
pCurrentFrame = GetSelectedFrame();
|
|
}
|
|
if (pCurrentFrame)
|
|
{
|
|
const debugPlayback::EventBase *pEvent = pCurrentFrame->mp_LastEvent;
|
|
while (pEvent)
|
|
{
|
|
if (pEvent->GetEventType() == debugPlayback::CameraEvent::GetStaticEventType())
|
|
{
|
|
pEvent->Replay(0);
|
|
break;
|
|
}
|
|
pEvent = pEvent->mp_Prev;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
m_LastFrameInsts.SetCount(m_FrameInsts.GetCount());
|
|
for (int i=0 ; i<m_FrameInsts.GetCount() ; i++)
|
|
{
|
|
m_LastFrameInsts[i] = m_FrameInsts[i];
|
|
}
|
|
m_FrameInsts.Reset();
|
|
|
|
//Make sure our inst change histories are connected up if needed (needed when we add backwards links to objects, )
|
|
DebugReplayObjectInfo::UpdateChains();
|
|
|
|
if (!mp_ReplayFrame)
|
|
{
|
|
if (m_bPauseOnEventReplay)
|
|
{
|
|
//Pause the game update as we've now updated physics since a reset
|
|
fwTimer::StartUserPause();
|
|
m_bPauseOnEventReplay = false;
|
|
}
|
|
|
|
if (m_bDestroyReplayObject)
|
|
{
|
|
DebugReplayObjectInfo::ClearReplayObject();
|
|
m_bDestroyReplayObject = false;
|
|
}
|
|
return;
|
|
}
|
|
|
|
//Build a list of the previous unique physics events
|
|
debugPlayback::phInstId rPlaybackId;
|
|
Mat34V initialMatrix; //TODO: Recover the initial matrix
|
|
bool bHaveMatrix = false;
|
|
debugPlayback::TTYTextOutputVisitor tty;
|
|
debugPlayback::EventBase *pEvent = mp_ReplayFrame->mp_LastEvent;
|
|
while (pEvent)
|
|
{
|
|
//Is the event a physics event?
|
|
if (pEvent->IsPhysicsEvent())
|
|
{
|
|
const debugPlayback::PhysicsEvent *pPhEvent = static_cast<const debugPlayback::PhysicsEvent*>(pEvent);
|
|
if (!rPlaybackId.IsAssigned())
|
|
{
|
|
const DebugReplayObjectInfo *pObject = GetObjectInfo(pPhEvent->m_rInst);
|
|
if (pObject)
|
|
{
|
|
rPlaybackId = pPhEvent->m_rInst;
|
|
tty.AddLine("------------------------");
|
|
tty.AddLine("Object for replay from event %d = %s", pPhEvent->m_iEventIndex, pObject->GetModelName());
|
|
}
|
|
}
|
|
|
|
//Create the object at the right location if possible!
|
|
const debugPlayback::SetMatrixEvent *pMatEvent = dynamic_cast<const debugPlayback::SetMatrixEvent*>(pPhEvent);
|
|
if (pMatEvent)
|
|
{
|
|
initialMatrix = pMatEvent->m_Matrix;
|
|
bHaveMatrix = true;
|
|
}
|
|
}
|
|
pEvent = pEvent->mp_Prev;
|
|
}
|
|
|
|
if (!bHaveMatrix)
|
|
{
|
|
Errorf("Failed to find matrix to hit replay at");
|
|
mp_ReplayFrame = 0;
|
|
return;
|
|
}
|
|
|
|
//Teleport to the initial matrix!
|
|
if (m_bHasTeleportLocation)
|
|
{
|
|
CPed* pPlayerPed = CGameWorld::FindLocalPlayer();
|
|
if (pPlayerPed)
|
|
{
|
|
pPlayerPed->Teleport( RCC_VECTOR3(m_vTeleportLocation), pPlayerPed->GetCurrentHeading() );
|
|
|
|
// Load all objects
|
|
CStreaming::LoadAllRequestedObjects( );
|
|
}
|
|
}
|
|
|
|
//Only allow one replay object at a time!
|
|
DebugReplayObjectInfo::ClearReplayObject();
|
|
|
|
//Create the replay object if needed.
|
|
phInst *pReplayOnThis = m_bCreateObjectForReplay ? 0 : debugPlayback::PhysicsEvent::GetSelectedInst();
|
|
if (m_bCreateObjectForReplay && rPlaybackId.IsAssigned())
|
|
{
|
|
debugPlayback::DebugRecorder *pInstance = debugPlayback::DebugRecorder::GetInstance();
|
|
if(pInstance)
|
|
{
|
|
debugPlayback::DataBase *pData = pInstance->GetSharedData(rPlaybackId.ToU32());
|
|
if (pData)
|
|
{
|
|
DebugReplayObjectInfo *pNewObjectData = dynamic_cast<DebugReplayObjectInfo*>(pData);
|
|
if (pNewObjectData)
|
|
{
|
|
tty.AddLine("------------------------");
|
|
tty.PushIndent();
|
|
pNewObjectData->DebugText(tty);
|
|
tty.PopIndent();
|
|
tty.AddLine("------------------------");
|
|
pReplayOnThis = pNewObjectData->CreateObject(tty, initialMatrix, m_bInvolvePlayer);
|
|
tty.AddLine("------------------------");
|
|
tty.AddLine("Created replay object 0x%x", pReplayOnThis);
|
|
|
|
if(!pReplayOnThis)
|
|
{
|
|
tty.AddLine("------------------------");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//Play them back as commands, print useful info as we go.
|
|
//Play the events of the replay frame in order
|
|
if(pReplayOnThis)
|
|
{
|
|
tty.AddLine("------------------------");
|
|
tty.AddLine(" Event replay");
|
|
tty.AddLine("------------------------");
|
|
tty.PushIndent();
|
|
debugPlayback::EventBase *pEvent = mp_ReplayFrame->mp_LastEvent;
|
|
while (pEvent)
|
|
{
|
|
if (pEvent->Replay(pReplayOnThis))
|
|
{
|
|
tty.AddLine("Replay from %s", pEvent->GetEventType());
|
|
pEvent->DebugText(tty);
|
|
}
|
|
pEvent = pEvent->mp_Next;
|
|
}
|
|
tty.PopIndent();
|
|
tty.AddLine("------------------------");
|
|
}
|
|
else
|
|
{
|
|
Errorf("Failed to get an object to run a replay with!");
|
|
}
|
|
|
|
//Thoughts for trapping a specific event on a frame... might work, also BreakOnEvent might just work
|
|
//- Tag the event type we want to trap, and the count into the frame
|
|
//- Trap the event when it happens...
|
|
//- Clear the replay settings
|
|
mp_ReplayFrame = 0;
|
|
}
|
|
|
|
#if __WIN32
|
|
#pragma warning(pop) // nonstandard extension used : nameless struct/union
|
|
#endif
|
|
|
|
//PARSER
|
|
using namespace rage;
|
|
using namespace debugPlayback;
|
|
#include "debugRecorder_parser.h"
|
|
|
|
#endif
|