You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
o3de/Gems/Maestro/Code/Source/Cinematics/SceneNode.cpp

1138 lines
39 KiB
C++

/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Math/Quaternion.h>
#include <AzCore/Math/Transform.h>
#include <AzCore/Component/ComponentApplicationBus.h>
#include <AzCore/Component/TransformBus.h>
#include <AzFramework/Components/CameraBus.h>
#include "MathConversion.h"
#include "SceneNode.h"
#include "AnimSequence.h"
#include "AnimTrack.h"
#include "EventTrack.h"
#include "ConsoleTrack.h"
#include "SequenceTrack.h"
#include "GotoTrack.h"
#include "CaptureTrack.h"
#include "ISystem.h"
#include "ITimer.h"
#include "AnimAZEntityNode.h"
#include "AnimComponentNode.h"
#include "Movie.h"
#include "Maestro/Types/AnimNodeType.h"
#include "Maestro/Types/AnimValueType.h"
#include "Maestro/Types/AnimParamType.h"
#include <AzCore/Math/MathUtils.h>
#include <IAudioSystem.h>
#include <IConsole.h>
#include <IViewSystem.h>
#define s_nodeParamsInitialized s_nodeParamsInitializedScene
#define s_nodeParams s_nodeParamsSene
#define AddSupportedParam AddSupportedParamScene
float const kDefaultCameraFOV = 60.0f;
namespace {
bool s_nodeParamsInitialized = false;
StaticInstance<std::vector<CAnimNode::SParamInfo>> s_nodeParams;
void AddSupportedParam(const char* sName, AnimParamType paramId, AnimValueType valueType, int flags = 0)
{
CAnimNode::SParamInfo param;
param.name = sName;
param.paramType = paramId;
param.valueType = valueType;
param.flags = (IAnimNode::ESupportedParamFlags)flags;
s_nodeParams.push_back(param);
}
class CComponentEntitySceneCamera
: public CAnimSceneNode::ISceneCamera
{
public:
CComponentEntitySceneCamera(const AZ::EntityId& entityId)
: m_cameraEntityId(entityId) {}
virtual ~CComponentEntitySceneCamera() = default;
const Vec3& GetPosition() const override
{
AZ::Vector3 pos;
AZ::TransformBus::EventResult(pos, m_cameraEntityId, &AZ::TransformBus::Events::GetWorldTranslation);
m_vec3Buffer.Set(pos.GetX(), pos.GetY(), pos.GetZ());
return m_vec3Buffer;
}
const Quat& GetRotation() const override
{
AZ::Quaternion quat(AZ::Quaternion::CreateIdentity());
AZ::TransformBus::EventResult(quat, m_cameraEntityId, &AZ::TransformBus::Events::GetWorldRotationQuaternion);
m_quatBuffer = AZQuaternionToLYQuaternion(quat);
return m_quatBuffer;
}
void SetPosition(const Vec3& localPosition) override
{
AZ::Vector3 pos(localPosition.x, localPosition.y, localPosition.z);
AZ::TransformBus::Event(m_cameraEntityId, &AZ::TransformBus::Events::SetWorldTranslation, pos);
}
void SetRotation(const Quat& localRotation) override
{
AZ::Quaternion quat = LYQuaternionToAZQuaternion(localRotation);
AZ::TransformBus::Event(m_cameraEntityId, &AZ::TransformBus::Events::SetLocalRotationQuaternion, quat);
}
float GetFoV() const
{
float retFoV = DEFAULT_FOV;
Camera::CameraRequestBus::EventResult(retFoV, m_cameraEntityId, &Camera::CameraComponentRequests::GetFovDegrees);
return retFoV;
}
float GetNearZ() const
{
float retNearZ = DEFAULT_NEAR;
Camera::CameraRequestBus::EventResult(retNearZ, m_cameraEntityId, &Camera::CameraComponentRequests::GetNearClipDistance);
return retNearZ;
}
void SetNearZAndFOVIfChanged(float fov, float nearZ) override
{
float degFoV = AZ::RadToDeg(fov);
if (!AZ::IsClose(GetFoV(), degFoV, FLT_EPSILON))
{
Camera::CameraRequestBus::Event(m_cameraEntityId, &Camera::CameraComponentRequests::SetFovDegrees, degFoV);
}
if (!AZ::IsClose(GetNearZ(), nearZ, FLT_EPSILON))
{
Camera::CameraRequestBus::Event(m_cameraEntityId, &Camera::CameraComponentRequests::SetNearClipDistance, nearZ);
}
}
void TransformPositionFromLocalToWorldSpace(Vec3& position) override
{
AZ::EntityId parentId;
AZ::TransformBus::EventResult(parentId, m_cameraEntityId, &AZ::TransformBus::Events::GetParentId);
if (parentId.IsValid())
{
AZ::Vector3 pos(position.x, position.y, position.z);
AZ::Transform worldTM;
AZ::TransformBus::EventResult(worldTM, parentId, &AZ::TransformBus::Events::GetWorldTM);
pos = worldTM.TransformPoint(pos);
position.Set(pos.GetX(), pos.GetY(), pos.GetZ());
}
}
void TransformPositionFromWorldToLocalSpace(Vec3& position) override
{
AZ::EntityId parentId;
AZ::TransformBus::EventResult(parentId, m_cameraEntityId, &AZ::TransformBus::Events::GetParentId);
if (parentId.IsValid())
{
AZ::Vector3 pos(position.x, position.y, position.z);
AZ::Transform worldTM;
AZ::TransformBus::EventResult(worldTM, parentId, &AZ::TransformBus::Events::GetWorldTM);
worldTM = worldTM.GetInverse();
pos = worldTM.TransformPoint(pos);
position.Set(pos.GetX(), pos.GetY(), pos.GetZ());
}
}
void TransformRotationFromLocalToWorldSpace(Quat& rotation) override
{
AZ::EntityId parentId;
AZ::TransformBus::EventResult(parentId, m_cameraEntityId, &AZ::TransformBus::Events::GetParentId);
if (parentId.IsValid())
{
AZ::Quaternion rot = LYQuaternionToAZQuaternion(rotation);
AZ::Transform worldTM;
AZ::TransformBus::EventResult(worldTM, parentId, &AZ::TransformBus::Events::GetWorldTM);
AZ::Quaternion worldRot = worldTM.GetRotation();
rot = worldRot * rot;
rotation = AZQuaternionToLYQuaternion(rot);
}
}
void SetWorldRotation(const Quat& rotation) override
{
AZ::EntityId parentId;
AZ::TransformBus::EventResult(parentId, m_cameraEntityId, &AZ::TransformBus::Events::GetParentId);
if (parentId.IsValid())
{
AZ::Quaternion rot = LYQuaternionToAZQuaternion(rotation);
AZ::Transform parentWorldTM;
AZ::Transform worldTM;
AZ::TransformBus::EventResult(parentWorldTM, parentId, &AZ::TransformBus::Events::GetWorldTM);
AZ::TransformBus::EventResult(worldTM, m_cameraEntityId, &AZ::TransformBus::Events::GetWorldTM);
parentWorldTM.SetRotation(rot);
parentWorldTM.SetTranslation(worldTM.GetTranslation());
AZ::TransformBus::Event(m_cameraEntityId, &AZ::TransformBus::Events::SetWorldTM, parentWorldTM);
}
else
{
SetRotation(rotation);
}
}
bool HasParent() const override
{
AZ::EntityId parentId;
AZ::TransformBus::EventResult(parentId, m_cameraEntityId, &AZ::TransformBus::Events::GetParentId);
return parentId.IsValid();
}
private:
AZ::EntityId m_cameraEntityId;
mutable Vec3 m_vec3Buffer; // buffer for returning references
mutable Quat m_quatBuffer; // buffer for returning references
};
}
//////////////////////////////////////////////////////////////////////////
CAnimSceneNode::CAnimSceneNode(const int id)
: CAnimNode(id, AnimNodeType::Director)
{
m_lastCameraKey = -1;
m_lastEventKey = -1;
m_lastConsoleKey = -1;
m_lastSequenceKey = -1;
m_nLastGotoKey = -1;
m_lastCaptureKey = -1;
m_bLastCapturingEnded = true;
m_captureFrameCount = 0;
m_cvar_t_FixedStep = NULL;
m_pCamNodeOnHoldForInterp = 0;
m_CurrentSelectTrack = 0;
m_CurrentSelectTrackKeyNumber = 0;
m_lastPrecachePoint = -1.f;
SetName("Scene");
CAnimSceneNode::Initialize();
SetFlags(GetFlags() | eAnimNodeFlags_CanChangeName);
}
//////////////////////////////////////////////////////////////////////////
CAnimSceneNode::CAnimSceneNode()
: CAnimSceneNode(0)
{
}
//////////////////////////////////////////////////////////////////////////
CAnimSceneNode::~CAnimSceneNode()
{
ReleaseSounds();
}
//////////////////////////////////////////////////////////////////////////
void CAnimSceneNode::Initialize()
{
if (!s_nodeParamsInitialized)
{
s_nodeParamsInitialized = true;
s_nodeParams.reserve(9);
AddSupportedParam("Camera", AnimParamType::Camera, AnimValueType::Select);
AddSupportedParam("Event", AnimParamType::Event, AnimValueType::Unknown);
AddSupportedParam("Sound", AnimParamType::Sound, AnimValueType::Unknown);
AddSupportedParam("Sequence", AnimParamType::Sequence, AnimValueType::Unknown);
AddSupportedParam("Console", AnimParamType::Console, AnimValueType::Unknown);
AddSupportedParam("GoTo", AnimParamType::Goto, AnimValueType::DiscreteFloat);
AddSupportedParam("Capture", AnimParamType::Capture, AnimValueType::Unknown);
AddSupportedParam("Timewarp", AnimParamType::TimeWarp, AnimValueType::Float);
AddSupportedParam("FixedTimeStep", AnimParamType::FixedTimeStep, AnimValueType::Float);
}
}
//////////////////////////////////////////////////////////////////////////
void CAnimSceneNode::CreateDefaultTracks()
{
CreateTrack(AnimParamType::Camera);
};
//////////////////////////////////////////////////////////////////////////
unsigned int CAnimSceneNode::GetParamCount() const
{
return s_nodeParams.size();
}
//////////////////////////////////////////////////////////////////////////
CAnimParamType CAnimSceneNode::GetParamType(unsigned int nIndex) const
{
if (nIndex >= 0 && nIndex < (int)s_nodeParams.size())
{
return s_nodeParams[nIndex].paramType;
}
return AnimParamType::Invalid;
}
//////////////////////////////////////////////////////////////////////////
bool CAnimSceneNode::GetParamInfoFromType(const CAnimParamType& paramId, SParamInfo& info) const
{
for (int i = 0; i < (int)s_nodeParams.size(); i++)
{
if (s_nodeParams[i].paramType == paramId)
{
info = s_nodeParams[i];
return true;
}
}
return false;
}
//////////////////////////////////////////////////////////////////////////
void CAnimSceneNode::Activate(bool bActivate)
{
CAnimNode::Activate(bActivate);
int trackCount = NumTracks();
for (int paramIndex = 0; paramIndex < trackCount; paramIndex++)
{
CAnimParamType paramId = m_tracks[paramIndex]->GetParameterType();
IAnimTrack* pTrack = m_tracks[paramIndex].get();
if (paramId.GetType() != AnimParamType::Sequence)
{
continue;
}
CSequenceTrack* pSequenceTrack = (CSequenceTrack*)pTrack;
for (int currKey = 0; currKey < pSequenceTrack->GetNumKeys(); currKey++)
{
ISequenceKey key;
pSequenceTrack->GetKey(currKey, &key);
IAnimSequence* pSequence = GetSequenceFromSequenceKey(key);
if (pSequence)
{
if (bActivate)
{
pSequence->Activate();
if (key.bOverrideTimes)
{
key.fDuration = (key.fEndTime - key.fStartTime) > 0.0f ? (key.fEndTime - key.fStartTime) : 0.0f;
}
else
{
key.fDuration = pSequence->GetTimeRange().Length();
}
pTrack->SetKey(currKey, &key);
}
else
{
pSequence->Deactivate();
}
}
}
}
if (m_cvar_t_FixedStep == NULL)
{
m_cvar_t_FixedStep = gEnv->pConsole->GetCVar("t_FixedStep");
}
}
//////////////////////////////////////////////////////////////////////////
void CAnimSceneNode::Animate(SAnimContext& ec)
{
if (ec.resetting)
{
return;
}
CSelectTrack* cameraTrack = NULL;
CEventTrack* pEventTrack = NULL;
CSequenceTrack* pSequenceTrack = NULL;
CConsoleTrack* pConsoleTrack = NULL;
CGotoTrack* pGotoTrack = NULL;
CCaptureTrack* pCaptureTrack = NULL;
/*
bool bTimeJump = false;
if (ec.time < m_time)
bTimeJump = true;
*/
if (gEnv->IsEditor() && m_time > ec.time)
{
m_lastPrecachePoint = -1.f;
}
PrecacheDynamic(ec.time);
size_t nNumAudioTracks = 0;
int trackCount = NumTracks();
for (int paramIndex = 0; paramIndex < trackCount; paramIndex++)
{
CAnimParamType paramId = m_tracks[paramIndex]->GetParameterType();
IAnimTrack* pTrack = m_tracks[paramIndex].get();
if (pTrack->GetFlags() & IAnimTrack::eAnimTrackFlags_Disabled)
{
continue;
}
if (pTrack->IsMasked(ec.trackMask))
{
continue;
}
switch (paramId.GetType())
{
case AnimParamType::Camera:
cameraTrack = (CSelectTrack*)pTrack;
break;
case AnimParamType::Event:
pEventTrack = (CEventTrack*)pTrack;
break;
case AnimParamType::Sequence:
pSequenceTrack = (CSequenceTrack*)pTrack;
break;
case AnimParamType::Console:
pConsoleTrack = (CConsoleTrack*)pTrack;
break;
case AnimParamType::Capture:
pCaptureTrack = (CCaptureTrack*)pTrack;
break;
case AnimParamType::Goto:
pGotoTrack = (CGotoTrack*)pTrack;
break;
case AnimParamType::Sound:
++nNumAudioTracks;
if (nNumAudioTracks > m_SoundInfo.size())
{
m_SoundInfo.resize(nNumAudioTracks);
}
AnimateSound(m_SoundInfo, ec, pTrack, nNumAudioTracks);
break;
case AnimParamType::TimeWarp:
{
float timeScale = 1.0f;
pTrack->GetValue(ec.time, timeScale);
if (timeScale < .0f)
{
timeScale = .0f;
}
// if set, disable fixed time step cvar so timewarping will have an affect. We never set it back though - that is
// likely a bug!
if (m_cvar_t_FixedStep && m_cvar_t_FixedStep->GetFVal() != .0f)
{
m_cvar_t_FixedStep->Set(.0f);
}
gEnv->pTimer->SetTimeScale(timeScale, ITimer::eTSC_Trackview);
}
break;
case AnimParamType::FixedTimeStep:
{
float timeStep = 0;
pTrack->GetValue(ec.time, timeStep);
if (timeStep < 0)
{
timeStep = 0;
}
if (m_cvar_t_FixedStep)
{
m_cvar_t_FixedStep->Set(timeStep);
}
}
break;
}
}
// Animate Camera Track (aka Select Track)
// Check if a camera override is set by CVar
const char* overrideCamName = gEnv->pMovieSystem->GetOverrideCamName();
AZ::EntityId overrideCamId;
if (overrideCamName != 0 && strlen(overrideCamName) > 0)
{
// overriding with a Camera Component entity is done by entityId (as names are not unique among AZ::Entities) - try to convert string to u64 to see if it's an id
AZ::u64 u64Id = strtoull(overrideCamName, nullptr, /*base (radix)*/ 10);
if (u64Id)
{
overrideCamId = AZ::EntityId(u64Id);
}
}
if (overrideCamId.IsValid()) // There is a valid overridden camera.
{
if (overrideCamId != gEnv->pMovieSystem->GetCameraParams().cameraEntityId)
{
ISelectKey key;
key.szSelection = overrideCamName;
key.cameraAzEntityId = overrideCamId;
ApplyCameraKey(key, ec);
}
}
else if (cameraTrack) // No camera override by CVar, use the camera track
{
ISelectKey key;
int cameraKey = cameraTrack->GetActiveKey(ec.time, &key);
m_CurrentSelectTrackKeyNumber = cameraKey;
m_CurrentSelectTrack = cameraTrack;
ApplyCameraKey(key, ec);
m_lastCameraKey = cameraKey;
}
if (pEventTrack)
{
IEventKey key;
int nEventKey = pEventTrack->GetActiveKey(ec.time, &key);
if (nEventKey != m_lastEventKey && nEventKey >= 0)
{
bool bNotTrigger = key.bNoTriggerInScrubbing && ec.singleFrame && key.time != ec.time;
if (!bNotTrigger)
{
ApplyEventKey(key, ec);
}
}
m_lastEventKey = nEventKey;
}
if (pConsoleTrack)
{
IConsoleKey key;
int nConsoleKey = pConsoleTrack->GetActiveKey(ec.time, &key);
if (nConsoleKey != m_lastConsoleKey && nConsoleKey >= 0)
{
if (!ec.singleFrame || key.time == ec.time) // If Single frame update key time must match current time.
{
ApplyConsoleKey(key, ec);
}
}
m_lastConsoleKey = nConsoleKey;
}
if (pSequenceTrack)
{
ISequenceKey key;
int nSequenceKey = pSequenceTrack->GetActiveKey(ec.time, &key);
IAnimSequence* pSequence = GetSequenceFromSequenceKey(key);
if (!gEnv->IsEditing() && (nSequenceKey != m_lastSequenceKey || !GetMovieSystem()->IsPlaying(pSequence)))
{
ApplySequenceKey(pSequenceTrack, m_lastSequenceKey, nSequenceKey, key, ec);
}
m_lastSequenceKey = nSequenceKey;
}
if (pGotoTrack)
{
ApplyGotoKey(pGotoTrack, ec);
}
if (pCaptureTrack && gEnv->pMovieSystem->IsInBatchRenderMode() == false)
{
ICaptureKey key;
int nCaptureKey = pCaptureTrack->GetActiveKey(ec.time, &key);
bool justEnded = false;
if (!m_bLastCapturingEnded && key.time + key.duration < ec.time)
{
justEnded = true;
}
if (!ec.singleFrame && !(gEnv->IsEditor() && gEnv->IsEditing()))
{
if (nCaptureKey != m_lastCaptureKey && nCaptureKey >= 0)
{
if (m_bLastCapturingEnded == false)
{
assert(0);
gEnv->pMovieSystem->EndCapture();
m_bLastCapturingEnded = true;
}
gEnv->pMovieSystem->EnableFixedStepForCapture(key.timeStep);
gEnv->pMovieSystem->StartCapture(key, m_captureFrameCount);
if (key.once == false)
{
m_bLastCapturingEnded = false;
}
m_lastCaptureKey = nCaptureKey;
}
else if (justEnded)
{
gEnv->pMovieSystem->DisableFixedStepForCapture();
gEnv->pMovieSystem->EndCapture();
m_bLastCapturingEnded = true;
}
}
m_captureFrameCount++;
}
m_time = ec.time;
if (m_pOwner)
{
m_pOwner->OnNodeAnimated(this);
}
}
//////////////////////////////////////////////////////////////////////////
void CAnimSceneNode::OnReset()
{
if (m_lastSequenceKey >= 0)
{
{
int trackCount = NumTracks();
for (int paramIndex = 0; paramIndex < trackCount; paramIndex++)
{
CAnimParamType paramId = m_tracks[paramIndex]->GetParameterType();
IAnimTrack* pTrack = m_tracks[paramIndex].get();
if (paramId.GetType() != AnimParamType::Sequence)
{
continue;
}
CSequenceTrack* pSequenceTrack = (CSequenceTrack*)pTrack;
ISequenceKey prevKey;
pSequenceTrack->GetKey(m_lastSequenceKey, &prevKey);
IAnimSequence* sequence = GetSequenceFromSequenceKey(prevKey);
if (sequence)
{
GetMovieSystem()->StopSequence(sequence);
}
}
}
}
// If the last capturing hasn't finished properly, end it here.
if (m_bLastCapturingEnded == false)
{
GetMovieSystem()->EndCapture();
m_bLastCapturingEnded = true;
}
m_lastEventKey = -1;
m_lastConsoleKey = -1;
m_lastSequenceKey = -1;
m_nLastGotoKey = -1;
m_lastCaptureKey = -1;
m_bLastCapturingEnded = true;
m_captureFrameCount = 0;
if (GetTrackForParameter(AnimParamType::TimeWarp))
{
gEnv->pTimer->SetTimeScale(1.0f, ITimer::eTSC_Trackview);
if (m_cvar_t_FixedStep)
{
m_cvar_t_FixedStep->Set(0);
}
}
if (GetTrackForParameter(AnimParamType::FixedTimeStep) && m_cvar_t_FixedStep)
{
m_cvar_t_FixedStep->Set(0);
}
}
//////////////////////////////////////////////////////////////////////////
void CAnimSceneNode::OnStart()
{
ResetSounds();
}
void CAnimSceneNode::OnPause()
{
}
void CAnimSceneNode::OnLoop()
{
ResetSounds();
}
void CAnimSceneNode::OnStop()
{
ReleaseSounds();
}
//////////////////////////////////////////////////////////////////////////
void CAnimSceneNode::ResetSounds()
{
for (int i = m_SoundInfo.size(); --i >= 0; )
{
m_SoundInfo[i].Reset();
}
}
//////////////////////////////////////////////////////////////////////////
void CAnimSceneNode::ReleaseSounds()
{
// Stop all sounds on the global audio object,
// but we want to have it filter based on the owner (this)
// so we don't stop sounds that didn't originate with track view.
Audio::SAudioRequest request;
request.nFlags = Audio::eARF_PRIORITY_HIGH;
request.pOwner = this;
Audio::SAudioObjectRequestData<Audio::eAORT_STOP_ALL_TRIGGERS> requestData(/*filterByOwner = */ true);
request.pData = &requestData;
Audio::AudioSystemRequestBus::Broadcast(&Audio::AudioSystemRequestBus::Events::PushRequest, request);
}
//////////////////////////////////////////////////////////////////////////
// InterpolateCameras()
//
// This rather long function takes care of the interpolation (or blending) of
// two camera keys, specifically FoV, nearZ, position and rotation blending.
//
void CAnimSceneNode::InterpolateCameras(SCameraParams& retInterpolatedCameraParams, ISceneCamera* firstCamera,
ISelectKey& firstKey, ISelectKey& secondKey, float time)
{
if (!secondKey.cameraAzEntityId.IsValid())
{
// abort - can't interpolate if there isn't a valid Id for a component entity camera
return;
}
static const float EPSILON_TIME = 0.01f; // consider times within EPSILON_TIME of beginning of blend time to be at the beginning of blend time
float interpolatedFoV;
ISceneCamera* secondCamera = static_cast<ISceneCamera*>(new CComponentEntitySceneCamera(secondKey.cameraAzEntityId));
float t = 1 - ((secondKey.time - time) / firstKey.fBlendTime);
t = min(t, 1.0f);
t = aznumeric_cast<float>(pow(t, 3) * (t * (t * 6 - 15) + 10)); // use a cubic curve for the camera blend
bool haveStashedInterpData = (m_InterpolatingCameraStartStates.find(m_CurrentSelectTrackKeyNumber) != m_InterpolatingCameraStartStates.end());
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
// at the start of the blend, stash the starting point first camera data to use throughout the interpolation
if (!haveStashedInterpData)
{
InterpolatingCameraStartState camData;
camData.m_interpolatedCamFirstPos = firstCamera->GetPosition();
camData.m_interpolatedCamFirstRot = firstCamera->GetRotation();
// stash FoV from the first camera entity
camData.m_FoV = firstCamera->GetFoV();
// stash nearZ
camData.m_nearZ = firstCamera->GetNearZ();
m_InterpolatingCameraStartStates.insert(AZStd::make_pair(m_CurrentSelectTrackKeyNumber, camData));
}
const auto& retStashedInterpCamData = m_InterpolatingCameraStartStates.find(m_CurrentSelectTrackKeyNumber);
InterpolatingCameraStartState stashedInterpCamData = retStashedInterpCamData->second;
// interpolate FOV
float secondCameraFOV = secondCamera->GetFoV();
interpolatedFoV = stashedInterpCamData.m_FoV + (secondCameraFOV - stashedInterpCamData.m_FoV) * t;
// store the interpolated FoV to be returned, in radians
retInterpolatedCameraParams.fov = DEG2RAD(interpolatedFoV);
// interpolate NearZ
float secondCameraNearZ = secondCamera->GetNearZ();
retInterpolatedCameraParams.nearZ = stashedInterpCamData.m_nearZ + (secondCameraNearZ - stashedInterpCamData.m_nearZ) * t;
// update the Camera entity's component FOV and nearZ directly if needed (if they weren't set via anim node SetParamValue() above)
firstCamera->SetNearZAndFOVIfChanged(retInterpolatedCameraParams.fov, retInterpolatedCameraParams.nearZ);
////////////////////////
// interpolate Position
Vec3 vFirstCamPos = stashedInterpCamData.m_interpolatedCamFirstPos;
Vec3 secondKeyPos = secondCamera->GetPosition();
Vec3 interpolatedPos = vFirstCamPos + (secondKeyPos - vFirstCamPos) * t;
firstCamera->SetPosition(interpolatedPos);
////////////////////////
// interpolate Rotation
Quat firstCameraRotation = stashedInterpCamData.m_interpolatedCamFirstRot;
Quat secondCameraRotation = secondCamera->GetRotation();
Quat interpolatedRotation;
interpolatedRotation.SetSlerp(firstCameraRotation, secondCameraRotation, t);
firstCamera->SetWorldRotation(interpolatedRotation);
// clean-up
if (secondCamera)
{
delete secondCamera;
}
}
//////////////////////////////////////////////////////////////////////////
void CAnimSceneNode::ApplyCameraKey(ISelectKey& key, SAnimContext& ec)
{
ISelectKey nextKey;
int nextCameraKeyNumber = m_CurrentSelectTrackKeyNumber + 1;
bool bInterpolateCamera = false;
if (nextCameraKeyNumber < m_CurrentSelectTrack->GetNumKeys())
{
m_CurrentSelectTrack->GetKey(nextCameraKeyNumber, &nextKey);
float fInterTime = nextKey.time - ec.time;
if (fInterTime >= 0 && fInterTime <= key.fBlendTime)
{
bInterpolateCamera = true;
}
}
// check if we're finished interpolating and there is a camera node on hold for interpolation. If so, unset it from hold.
if (!bInterpolateCamera && m_pCamNodeOnHoldForInterp)
{
m_pCamNodeOnHoldForInterp->SetSkipInterpolatedCameraNode(false);
m_pCamNodeOnHoldForInterp = 0;
}
SCameraParams cameraParams;
cameraParams.cameraEntityId.SetInvalid();
cameraParams.fov = 0;
cameraParams.justActivated = true;
// Init the defaults with the current view settings.
// With component entities, the fov and near plane may be animated on an
// entity with a Camera component. Don't stomp the values if this update happens
// after those properties are animated.
AZ_Assert(gEnv && gEnv->pSystem, "Expected valid gEnv->pSystem");
IViewSystem* viewSystem = gEnv->pSystem->GetIViewSystem();
if (viewSystem)
{
IView* view = viewSystem->GetActiveView();
if (view)
{
SViewParams params = *view->GetCurrentParams();
cameraParams.fov = params.fov;
cameraParams.nearZ = params.nearplane;
}
}
///////////////////////////////////////////////////////////////////
// find the Scene Camera (Camera Component Camera)
ISceneCamera* firstSceneCamera = nullptr;
if (key.cameraAzEntityId.IsValid())
{
// camera component entity
cameraParams.cameraEntityId = key.cameraAzEntityId;
firstSceneCamera = static_cast<ISceneCamera*>(new CComponentEntitySceneCamera(key.cameraAzEntityId));
}
if (firstSceneCamera)
{
cameraParams.fov = DEG2RAD(firstSceneCamera->GetFoV());
}
if (bInterpolateCamera && firstSceneCamera)
{
InterpolateCameras(cameraParams, firstSceneCamera, key, nextKey, ec.time);
}
// Broadcast camera changes
const SCameraParams& lastCameraParams = gEnv->pMovieSystem->GetCameraParams();
if (lastCameraParams.cameraEntityId != cameraParams.cameraEntityId)
{
Maestro::SequenceComponentNotificationBus::Event(
m_pSequence->GetSequenceEntityId(), &Maestro::SequenceComponentNotificationBus::Events::OnCameraChanged,
lastCameraParams.cameraEntityId, cameraParams.cameraEntityId);
// note: only update the active view if we're currently exporting/capturing a sequence
if (gEnv->pMovieSystem->IsInBatchRenderMode())
{
Camera::CameraRequestBus::Event(
cameraParams.cameraEntityId, &Camera::CameraRequestBus::Events::MakeActiveView);
}
}
gEnv->pMovieSystem->SetCameraParams(cameraParams);
// This detects when we've switched from one Camera to another on the Camera Track
// If cameras were interpolated (blended), reset cameras to their pre-interpolated positions and
// clean up cached data used for the interpolation
if (m_lastCameraKey != m_CurrentSelectTrackKeyNumber && m_lastCameraKey >= 0)
{
const auto& retStashedData = m_InterpolatingCameraStartStates.find(m_lastCameraKey);
if (retStashedData != m_InterpolatingCameraStartStates.end())
{
InterpolatingCameraStartState stashedData = retStashedData->second;
ISelectKey prevKey;
ISceneCamera* prevSceneCamera = nullptr;
m_CurrentSelectTrack->GetKey(m_lastCameraKey, &prevKey);
if (prevKey.cameraAzEntityId.IsValid())
{
prevSceneCamera = static_cast<ISceneCamera*>(new CComponentEntitySceneCamera(prevKey.cameraAzEntityId));
}
if (prevSceneCamera)
{
prevSceneCamera->SetPosition(stashedData.m_interpolatedCamFirstPos);
prevSceneCamera->SetRotation(stashedData.m_interpolatedCamFirstRot);
}
IAnimNode* prevCameraAnimNode = m_pSequence->FindNodeByName(prevKey.szSelection.c_str(), this);
if (prevCameraAnimNode == NULL)
{
prevCameraAnimNode = m_pSequence->FindNodeByName(prevKey.szSelection.c_str(), NULL);
}
if (prevCameraAnimNode && prevCameraAnimNode->GetType() == AnimNodeType::Camera && prevCameraAnimNode->GetTrackForParameter(AnimParamType::FOV))
{
prevCameraAnimNode->SetParamValue(ec.time, AnimParamType::FOV, stashedData.m_FoV);
}
else if (prevSceneCamera)
{
prevSceneCamera->SetNearZAndFOVIfChanged(DEG2RAD(stashedData.m_FoV), stashedData.m_nearZ);
}
m_InterpolatingCameraStartStates.erase(m_lastCameraKey);
// clean up
if (prevSceneCamera)
{
delete prevSceneCamera;
}
}
}
// clean up
if (firstSceneCamera)
{
delete firstSceneCamera;
}
}
//////////////////////////////////////////////////////////////////////////
void CAnimSceneNode::ApplyEventKey(IEventKey& key, [[maybe_unused]] SAnimContext& ec)
{
char funcName[1024];
azstrcpy(funcName, "Event_");
cry_strcat(funcName, key.event.c_str());
gEnv->pMovieSystem->SendGlobalEvent(funcName);
}
//////////////////////////////////////////////////////////////////////////
void CAnimSceneNode::ApplyAudioKey(char const* const sTriggerName, bool const bPlay /* = true */)
{
Audio::TAudioControlID nAudioTriggerID = INVALID_AUDIO_CONTROL_ID;
Audio::AudioSystemRequestBus::BroadcastResult(nAudioTriggerID, &Audio::AudioSystemRequestBus::Events::GetAudioTriggerID, sTriggerName);
if (nAudioTriggerID != INVALID_AUDIO_CONTROL_ID)
{
Audio::SAudioRequest oRequest;
oRequest.nFlags = Audio::eARF_PRIORITY_HIGH;
oRequest.pOwner = this;
if (bPlay)
{
Audio::SAudioObjectRequestData<Audio::eAORT_EXECUTE_TRIGGER> oRequestData(nAudioTriggerID, 0.0f);
oRequest.pData = &oRequestData;
Audio::AudioSystemRequestBus::Broadcast(&Audio::AudioSystemRequestBus::Events::PushRequest, oRequest);
}
else
{
Audio::SAudioObjectRequestData<Audio::eAORT_STOP_TRIGGER> oRequestData(nAudioTriggerID);
oRequest.pData = &oRequestData;
Audio::AudioSystemRequestBus::Broadcast(&Audio::AudioSystemRequestBus::Events::PushRequest, oRequest);
}
}
}
//////////////////////////////////////////////////////////////////////////
void CAnimSceneNode::ApplySequenceKey(IAnimTrack* pTrack, [[maybe_unused]] int nPrevKey, int nCurrKey, ISequenceKey& key, SAnimContext& ec)
{
if (nCurrKey >= 0)
{
IAnimSequence* pSequence = GetSequenceFromSequenceKey(key);
if (pSequence)
{
float startTime = -FLT_MAX;
float endTime = -FLT_MAX;
if (key.bOverrideTimes)
{
key.fDuration = (key.fEndTime - key.fStartTime) > 0.0f ? (key.fEndTime - key.fStartTime) : 0.0f;
startTime = key.fStartTime;
endTime = key.fEndTime;
}
else
{
key.fDuration = pSequence->GetTimeRange().Length();
}
pTrack->SetKey(nCurrKey, &key);
SAnimContext newAnimContext = ec;
newAnimContext.time = std::min(ec.time - key.time + key.fStartTime, key.fDuration + key.fStartTime);
if (static_cast<CAnimSequence*>(pSequence)->GetTime() != newAnimContext.time)
{
pSequence->Animate(newAnimContext);
}
}
}
}
//////////////////////////////////////////////////////////////////////////
void CAnimSceneNode::ApplyConsoleKey(IConsoleKey& key, [[maybe_unused]] SAnimContext& ec)
{
if (!key.command.empty())
{
gEnv->pConsole->ExecuteString(key.command.c_str());
}
}
void CAnimSceneNode::ApplyGotoKey(CGotoTrack* poGotoTrack, SAnimContext& ec)
{
IDiscreteFloatKey stDiscreteFloadKey;
int nCurrentActiveKeyIndex(-1);
nCurrentActiveKeyIndex = poGotoTrack->GetActiveKey(ec.time, &stDiscreteFloadKey);
if (nCurrentActiveKeyIndex != m_nLastGotoKey && nCurrentActiveKeyIndex >= 0)
{
if (!ec.singleFrame)
{
if (stDiscreteFloadKey.m_fValue >= 0)
{
string fullname = m_pSequence->GetName();
GetMovieSystem()->GoToFrame(fullname.c_str(), stDiscreteFloadKey.m_fValue);
}
}
}
m_nLastGotoKey = nCurrentActiveKeyIndex;
}
/// @deprecated Serialization for Sequence data in Component Entity Sequences now occurs through AZ::SerializeContext and the Sequence Component
void CAnimSceneNode::Serialize(XmlNodeRef& xmlNode, bool bLoading, bool bLoadEmptyTracks)
{
CAnimNode::Serialize(xmlNode, bLoading, bLoadEmptyTracks);
// To enable renaming even for previously saved director nodes
SetFlags(GetFlags() | eAnimNodeFlags_CanChangeName);
}
void CAnimSceneNode::Reflect(AZ::ReflectContext* context)
{
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<CAnimSceneNode, CAnimNode>()
->Version(1);
}
}
void CAnimSceneNode::PrecacheStatic(float startTime)
{
m_lastPrecachePoint = -1.f;
const uint numTracks = GetTrackCount();
for (uint trackIndex = 0; trackIndex < numTracks; ++trackIndex)
{
IAnimTrack* pAnimTrack = GetTrackByIndex(trackIndex);
if (pAnimTrack->GetParameterType() == AnimParamType::Sequence)
{
CSequenceTrack* pSequenceTrack = static_cast<CSequenceTrack*>(pAnimTrack);
const uint numKeys = pSequenceTrack->GetNumKeys();
for (uint keyIndex = 0; keyIndex < numKeys; ++keyIndex)
{
ISequenceKey key;
pSequenceTrack->GetKey(keyIndex, &key);
CAnimSequence* pSubSequence = static_cast<CAnimSequence*>(GetSequenceFromSequenceKey(key));
if (pSubSequence)
{
pSubSequence->PrecacheStatic(startTime - (key.fStartTime + key.time));
}
}
}
}
}
void CAnimSceneNode::PrecacheDynamic(float time)
{
const uint numTracks = GetTrackCount();
float fLastPrecachePoint = m_lastPrecachePoint;
for (uint trackIndex = 0; trackIndex < numTracks; ++trackIndex)
{
IAnimTrack* pAnimTrack = GetTrackByIndex(trackIndex);
if (pAnimTrack->GetParameterType() == AnimParamType::Sequence)
{
CSequenceTrack* pSequenceTrack = static_cast<CSequenceTrack*>(pAnimTrack);
const uint numKeys = pSequenceTrack->GetNumKeys();
for (uint keyIndex = 0; keyIndex < numKeys; ++keyIndex)
{
ISequenceKey key;
pSequenceTrack->GetKey(keyIndex, &key);
CAnimSequence* pSubSequence = static_cast<CAnimSequence*>(GetSequenceFromSequenceKey(key));
if (pSubSequence)
{
pSubSequence->PrecacheDynamic(time - (key.fStartTime + key.time));
}
}
}
else if (pAnimTrack->GetParameterType() == AnimParamType::Camera)
{
const float fPrecacheCameraTime = CMovieSystem::m_mov_cameraPrecacheTime;
if (fPrecacheCameraTime > 0.f)
{
CSelectTrack* pCameraTrack = static_cast<CSelectTrack*>(pAnimTrack);
ISelectKey key;
pCameraTrack->GetActiveKey(time + fPrecacheCameraTime, &key);
if (time < key.time && (time + fPrecacheCameraTime) > key.time && key.time > m_lastPrecachePoint)
{
fLastPrecachePoint = max(key.time, fLastPrecachePoint);
}
}
}
}
m_lastPrecachePoint = fLastPrecachePoint;
}
void CAnimSceneNode::InitializeTrackDefaultValue(IAnimTrack* pTrack, const CAnimParamType& paramType)
{
if (paramType.GetType() == AnimParamType::TimeWarp)
{
pTrack->SetValue(0.0f, 1.0f, true);
}
}
/*static*/ IAnimSequence* CAnimSceneNode::GetSequenceFromSequenceKey(const ISequenceKey& sequenceKey)
{
IAnimSequence* retSequence = nullptr;
if (gEnv && gEnv->pMovieSystem)
{
if (sequenceKey.sequenceEntityId.IsValid())
{
retSequence = gEnv->pMovieSystem->FindSequence(sequenceKey.sequenceEntityId);
}
else if (!sequenceKey.szSelection.empty())
{
// legacy Deprecate ISequenceKey used names to identify sequences
retSequence = gEnv->pMovieSystem->FindLegacySequenceByName(sequenceKey.szSelection.c_str());
}
}
return retSequence;
}
#undef s_nodeParamsInitialized
#undef s_nodeParams
#undef AddSupportedParam