git mv Code\Sandbox\Editor Code/Editor

Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com>
This commit is contained in:
Esteban Papp
2021-06-29 12:41:59 -07:00
parent 9f0bbf3b74
commit e34e36cb35
1415 changed files with 0 additions and 0 deletions
@@ -0,0 +1,146 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "EditorDefs.h"
// Editor
#include "TrackViewKeyPropertiesDlg.h"
#include "Controls/ReflectedPropertyControl/ReflectedPropertyItem.h"
//////////////////////////////////////////////////////////////////////////
class C2DBezierKeyUIControls
: public CTrackViewKeyUIControls
{
public:
C2DBezierKeyUIControls()
: m_skipOnUIChange(false)
{}
CSmartVariableArray mv_table;
CSmartVariable<float> mv_value;
virtual void OnCreateVars()
{
AddVariable(mv_table, "Key Properties");
AddVariable(mv_table, mv_value, "Value");
}
bool SupportTrackType([[maybe_unused]] const CAnimParamType& paramType, EAnimCurveType trackType, [[maybe_unused]] AnimValueType valueType) const
{
return trackType == eAnimCurveType_BezierFloat;
}
virtual bool OnKeySelectionChange(CTrackViewKeyBundle& selectedKeys);
virtual void OnUIChange(IVariable* pVar, CTrackViewKeyBundle& selectedKeys);
virtual unsigned int GetPriority() const { return 0; }
static const GUID& GetClassID()
{
// {DBD76F4B-8EFC-45b6-AFB8-56F171FA150A}
static const GUID guid =
{
0xdbd76f4b, 0x8efc, 0x45b6, { 0xaf, 0xb8, 0x56, 0xf1, 0x71, 0xfa, 0x15, 0xa }
};
return guid;
}
bool m_skipOnUIChange;
};
//////////////////////////////////////////////////////////////////////////
bool C2DBezierKeyUIControls::OnKeySelectionChange(CTrackViewKeyBundle& selectedKeys)
{
if (!selectedKeys.AreAllKeysOfSameType())
{
return false;
}
bool bAssigned = false;
if (selectedKeys.GetKeyCount() == 1)
{
const CTrackViewKeyHandle& keyHandle = selectedKeys.GetKey(0);
float fMin = 0.0f;
float fMax = 0.0f;
const CTrackViewTrack* pTrack = keyHandle.GetTrack();
pTrack->GetKeyValueRange(fMin, fMax);
if (fMin != fMax)
{
float curMin, curMax, step;
bool curMinHardLimit, curMaxHardLimit;
// need to call GetLimits to retrieve/maintain *HardLimit boolean values
mv_value.GetVar()->GetLimits(curMin, curMax, step, curMinHardLimit, curMaxHardLimit);
step = ReflectedPropertyItem::ComputeSliderStep(fMin, fMax);
mv_value.GetVar()->SetLimits(fMin, fMax, step, curMinHardLimit, curMaxHardLimit);
}
else
{
mv_value.GetVar()->ClearLimits();
}
EAnimCurveType trType = keyHandle.GetTrack()->GetCurveType();
if (trType == eAnimCurveType_BezierFloat)
{
I2DBezierKey bezierKey;
keyHandle.GetKey(&bezierKey);
m_skipOnUIChange = true;
SyncValue(mv_value, bezierKey.value.y, true);
m_skipOnUIChange = false;
bAssigned = true;
}
}
return bAssigned;
}
// Called when UI variable changes.
void C2DBezierKeyUIControls::OnUIChange(IVariable* pVar, CTrackViewKeyBundle& selectedKeys)
{
CTrackViewSequence* sequence = GetIEditor()->GetAnimation()->GetSequence();
if (!sequence || !selectedKeys.AreAllKeysOfSameType() || m_skipOnUIChange)
{
return;
}
for (unsigned int keyIndex = 0; keyIndex < selectedKeys.GetKeyCount(); ++keyIndex)
{
CTrackViewKeyHandle keyHandle = selectedKeys.GetKey(keyIndex);
EAnimCurveType trType = keyHandle.GetTrack()->GetCurveType();
if (trType == eAnimCurveType_BezierFloat)
{
I2DBezierKey bezierKey;
keyHandle.GetKey(&bezierKey);
SyncValue(mv_value, bezierKey.value.y, false, pVar);
bool isDuringUndo = false;
AzToolsFramework::ToolsApplicationRequests::Bus::BroadcastResult(isDuringUndo, &AzToolsFramework::ToolsApplicationRequests::Bus::Events::IsDuringUndoRedo);
if (isDuringUndo)
{
keyHandle.SetKey(&bezierKey);
}
else
{
AzToolsFramework::ScopedUndoBatch undoBatch("Set Key Value");
keyHandle.SetKey(&bezierKey);
undoBatch.MarkEntityDirty(sequence->GetSequenceComponentEntityId());
}
}
}
}
REGISTER_QT_CLASS_DESC(C2DBezierKeyUIControls, "TrackView.KeyUI.2DBezier", "TrackViewKeyUI");
@@ -0,0 +1,223 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "EditorDefs.h"
// AzCore
#include <AzCore/Asset/AssetManagerBus.h>
// CryCommon
#include <CryCommon/Maestro/Types/AnimNodeType.h>
#include <CryCommon/Maestro/Types/AnimValueType.h>
#include <CryCommon/Maestro/Bus/SequenceComponentBus.h>
#include <CryCommon/Maestro/Types/AssetBlendKey.h>
// Editor
#include "TrackViewKeyPropertiesDlg.h"
#include "Controls/ReflectedPropertyControl/ReflectedPropertyItem.h"
//////////////////////////////////////////////////////////////////////////
class CAssetBlendKeyUIControls
: public CTrackViewKeyUIControls
{
public:
AZ::EntityId m_entityId;
AZ::ComponentId m_componentId;
CSmartVariableArray mv_table;
CSmartVariable<QString> mv_asset;
CSmartVariable<bool> mv_loop;
CSmartVariable<float> mv_startTime;
CSmartVariable<float> mv_endTime;
CSmartVariable<float> mv_timeScale;
CSmartVariable<float> mv_blendInTime;
CSmartVariable<float> mv_blendOutTime;
virtual void OnCreateVars()
{
// Init to an invalid id
AZ::Data::AssetId assetId;
assetId.SetInvalid();
mv_asset->SetUserData(assetId.m_subId);
mv_asset->SetDisplayValue(assetId.m_guid.ToString<AZStd::string>().c_str());
AddVariable(mv_table, "Key Properties");
// In the future, we may have different types of AssetBlends supported. Right now
// "motion" for the Simple Motion Component is the only instance.
AddVariable(mv_table, mv_asset, "Motion", IVariable::DT_MOTION);
AddVariable(mv_table, mv_loop, "Loop");
AddVariable(mv_table, mv_startTime, "Start Time");
AddVariable(mv_table, mv_endTime, "End Time");
AddVariable(mv_table, mv_timeScale, "Time Scale");
AddVariable(mv_table, mv_blendInTime, "Blend In Time");
AddVariable(mv_table, mv_blendOutTime, "Blend Out Time");
mv_timeScale->SetLimits(0.001f, 100.f);
}
bool SupportTrackType([[maybe_unused]] const CAnimParamType& paramType, [[maybe_unused]] EAnimCurveType trackType, AnimValueType valueType) const
{
return valueType == AnimValueType::AssetBlend;
}
virtual bool OnKeySelectionChange(CTrackViewKeyBundle& selectedKeys);
virtual void OnUIChange(IVariable* pVar, CTrackViewKeyBundle& selectedKeys);
virtual unsigned int GetPriority() const { return 1; }
static const GUID& GetClassID()
{
// {5DC82D28-6C50-4406-8993-06770C640F98}
static const GUID guid =
{
0x5DC82D28, 0x6C50, 0x4406, { 0x89, 0x93, 0x06, 0x77, 0x0C, 0x64, 0x0F, 0x98 }
};
return guid;
}
protected:
void ResetStartEndLimits(float AssetBlendKeyDuration);
};
//////////////////////////////////////////////////////////////////////////
void CAssetBlendKeyUIControls::ResetStartEndLimits(float assetBlendKeyDuration)
{
const float time_zero = .0f;
float step = ReflectedPropertyItem::ComputeSliderStep(time_zero, assetBlendKeyDuration);
mv_startTime.GetVar()->SetLimits(time_zero, assetBlendKeyDuration, step, true, true);
mv_endTime.GetVar()->SetLimits(time_zero, assetBlendKeyDuration, step, true, true);
mv_blendInTime.GetVar()->SetLimits(time_zero, assetBlendKeyDuration, step, true, true);
mv_blendOutTime.GetVar()->SetLimits(time_zero, assetBlendKeyDuration, step, true, true);
}
bool CAssetBlendKeyUIControls::OnKeySelectionChange(CTrackViewKeyBundle& selectedKeys)
{
if (!selectedKeys.AreAllKeysOfSameType())
{
return false;
}
bool bAssigned = false;
if (selectedKeys.GetKeyCount() == 1)
{
const CTrackViewKeyHandle& keyHandle = selectedKeys.GetKey(0);
if (keyHandle.GetTrack()->GetValueType() == AnimValueType::AssetBlend)
{
AZ::IAssetBlendKey assetBlendKey;
keyHandle.GetKey(&assetBlendKey);
// Find editor object who owns this node.
const CTrackViewTrack* pTrack = keyHandle.GetTrack();
if (pTrack && pTrack->GetAnimNode()->GetType() == AnimNodeType::Component)
{
m_componentId = pTrack->GetAnimNode()->GetComponentId();
// try to get the AZ::EntityId from the component node's parent
CTrackViewAnimNode* parentNode = static_cast<CTrackViewAnimNode*>(pTrack->GetAnimNode()->GetParentNode());
if (parentNode)
{
m_entityId = parentNode->GetAzEntityId();
}
}
mv_asset->SetUserData(assetBlendKey.m_assetId.m_subId);
mv_asset->SetDisplayValue(assetBlendKey.m_assetId.m_guid.ToString<AZStd::string>().c_str());
mv_loop = assetBlendKey.m_bLoop;
mv_endTime = assetBlendKey.m_endTime;
mv_startTime = assetBlendKey.m_startTime;
mv_timeScale = assetBlendKey.m_speed;
mv_blendInTime = assetBlendKey.m_blendInTime;
mv_blendOutTime = assetBlendKey.m_blendOutTime;
bAssigned = true;
}
}
return bAssigned;
}
// Called when UI variable changes.
void CAssetBlendKeyUIControls::OnUIChange(IVariable* pVar, CTrackViewKeyBundle& selectedKeys)
{
CTrackViewSequence* pSequence = GetIEditor()->GetAnimation()->GetSequence();
if (!pSequence || !selectedKeys.AreAllKeysOfSameType())
{
return;
}
for (unsigned int keyIndex = 0, num = (int)selectedKeys.GetKeyCount(); keyIndex < num; keyIndex++)
{
CTrackViewKeyHandle keyHandle = selectedKeys.GetKey(keyIndex);
CTrackViewTrack* pTrack = keyHandle.GetTrack();
if (keyHandle.GetTrack()->GetValueType() == AnimValueType::AssetBlend)
{
AZ::IAssetBlendKey assetBlendKey;
keyHandle.GetKey(&assetBlendKey);
if (mv_asset.GetVar() == pVar)
{
AZStd::string stringGuid = mv_asset->GetDisplayValue().toLatin1().data();
if (!stringGuid.empty())
{
AZ::Uuid guid(stringGuid.c_str(), stringGuid.length());
AZ::u32 subId = mv_asset->GetUserData().value<AZ::u32>();
assetBlendKey.m_assetId = AZ::Data::AssetId(guid, subId);
// Lookup Filename by assetId and get the filename part of the description
AZStd::string assetPath;
EBUS_EVENT_RESULT(assetPath, AZ::Data::AssetCatalogRequestBus, GetAssetPathById, assetBlendKey.m_assetId);
assetBlendKey.m_description = "";
if (!assetPath.empty())
{
AZStd::string filename;
if (AzFramework::StringFunc::Path::GetFileName(assetPath.c_str(), filename))
{
assetBlendKey.m_description = filename;
}
}
}
// This call is required to make sure that the newly set animation is properly triggered.
pTrack->GetSequence()->Reset(false);
}
SyncValue(mv_loop, assetBlendKey.m_bLoop, false, pVar);
SyncValue(mv_startTime, assetBlendKey.m_startTime, false, pVar);
SyncValue(mv_endTime, assetBlendKey.m_endTime, false, pVar);
SyncValue(mv_timeScale, assetBlendKey.m_speed, false, pVar);
SyncValue(mv_blendInTime, assetBlendKey.m_blendInTime, false, pVar);
SyncValue(mv_blendOutTime, assetBlendKey.m_blendOutTime, false, pVar);
if (assetBlendKey.m_assetId.IsValid())
{
// Ask entity id this asset blend is bound to, to
// get the duration of this asset in an async way.
Maestro::SequenceComponentRequests::AnimatedFloatValue currValue = 0.0f;
Maestro::SequenceComponentRequestBus::Event(
pSequence->GetSequenceComponentEntityId(),
&Maestro::SequenceComponentRequestBus::Events::GetAssetDuration,
currValue,
m_entityId,
m_componentId,
assetBlendKey.m_assetId
);
assetBlendKey.m_duration = currValue.GetFloatValue();
ResetStartEndLimits(assetBlendKey.m_duration);
}
keyHandle.SetKey(&assetBlendKey);
}
}
}
REGISTER_QT_CLASS_DESC(CAssetBlendKeyUIControls, "TrackView.KeyUI.AssetBlends", "TrackViewKeyUI");
@@ -0,0 +1,119 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "AtomOutputFrameCapture.h"
#include <Atom/RPI.Public/Pass/Specific/RenderToTexturePass.h>
#include <Atom/RPI.Public/RenderPipeline.h>
#include <Atom/RPI.Public/Scene.h>
#include <Atom/RPI.Public/View.h>
#include <Atom/RPI.Reflect/System/RenderPipelineDescriptor.h>
#include <AzCore/Component/TransformBus.h>
#include <AzCore/Math/MatrixUtils.h>
#include <AzCore/Name/Name.h>
#include <AzFramework/Entity/GameEntityContextBus.h>
#include <AzFramework/Scene/Scene.h>
#include <AzFramework/Scene/SceneSystemInterface.h>
namespace TrackView
{
void AtomOutputFrameCapture::CreatePipeline(
AZ::RPI::Scene& scene, const AZStd::string& pipelineName, const uint32_t width, const uint32_t height)
{
AZ::RPI::RenderPipelineDescriptor pipelineDesc;
pipelineDesc.m_mainViewTagName = "MainCamera"; // must be "MainCamera"
pipelineDesc.m_name = pipelineName;
pipelineDesc.m_rootPassTemplate = "MainPipelineRenderToTexture";
pipelineDesc.m_renderSettings.m_multisampleState.m_samples = 4;
m_renderPipeline = AZ::RPI::RenderPipeline::CreateRenderPipeline(pipelineDesc);
if (auto renderToTexturePass = azrtti_cast<AZ::RPI::RenderToTexturePass*>(m_renderPipeline->GetRootPass().get()))
{
renderToTexturePass->ResizeOutput(width, height);
}
scene.AddRenderPipeline(m_renderPipeline);
// rendering pipeline has a tree structure
m_passHierarchy.push_back(pipelineName);
m_passHierarchy.push_back("CopyToSwapChain");
// retrieve View from the camera that's animating
AZ::Name viewName = AZ::Name("MainCamera");
m_view = AZ::RPI::View::CreateView(viewName, AZ::RPI::View::UsageCamera);
m_renderPipeline->SetDefaultView(m_view);
}
void AtomOutputFrameCapture::DestroyPipeline(AZ::RPI::Scene& scene)
{
scene.RemoveRenderPipeline(m_renderPipeline->GetId());
m_passHierarchy.clear();
m_renderPipeline.reset();
m_view.reset();
}
void AtomOutputFrameCapture::UpdateView(const AZ::Matrix3x4& cameraTransform, const AZ::Matrix4x4& cameraProjection)
{
m_view->SetCameraTransform(cameraTransform);
m_view->SetViewToClipMatrix(cameraProjection);
}
bool AtomOutputFrameCapture::BeginCapture(
const AZ::RPI::AttachmentReadback::CallbackFunction& attachmentReadbackCallback, CaptureFinishedCallback captureFinishedCallback)
{
AZ::Render::FrameCaptureNotificationBus::Handler::BusConnect();
m_captureFinishedCallback = AZStd::move(captureFinishedCallback);
// note: "Output" (slot name) maps to MainPipeline.pass CopyToSwapChain
bool startedCapture = false;
AZ::Render::FrameCaptureRequestBus::BroadcastResult(
startedCapture, &AZ::Render::FrameCaptureRequestBus::Events::CapturePassAttachmentWithCallback, m_passHierarchy,
AZStd::string("Output"), attachmentReadbackCallback);
return startedCapture;
}
void AtomOutputFrameCapture::OnCaptureFinished(
[[maybe_unused]] AZ::Render::FrameCaptureResult result, [[maybe_unused]] const AZStd::string& info)
{
m_captureFinishedCallback();
AZ::Render::FrameCaptureNotificationBus::Handler::BusDisconnect();
}
AZ::Matrix3x4 TransformFromEntityId(const AZ::EntityId entityId)
{
AZ::Transform cameraTransform = AZ::Transform::CreateIdentity();
AZ::TransformBus::EventResult(cameraTransform, entityId, &AZ::TransformBus::Events::GetWorldTM);
return AZ::Matrix3x4::CreateFromTransform(cameraTransform);
}
AZ::Matrix4x4 ProjectionFromCameraEntityId(const AZ::EntityId entityId, const float outputWidth, const float outputHeight)
{
float nearDist = 0.0f;
Camera::CameraRequestBus::EventResult(nearDist, entityId, &Camera::CameraRequestBus::Events::GetNearClipDistance);
float farDist = 0.0f;
Camera::CameraRequestBus::EventResult(farDist, entityId, &Camera::CameraRequestBus::Events::GetFarClipDistance);
float fovRad = 0.0f;
Camera::CameraRequestBus::EventResult(fovRad, entityId, &Camera::CameraRequestBus::Events::GetFovRadians);
const float aspectRatio = outputWidth / outputHeight;
AZ::Matrix4x4 viewToClipMatrix;
AZ::MakePerspectiveFovMatrixRH(viewToClipMatrix, fovRad, aspectRatio, nearDist, farDist, /*reverseDepth=*/true);
return viewToClipMatrix;
}
AZ::RPI::Scene* SceneFromGameEntityContext()
{
AzFramework::EntityContextId entityContextId;
AzFramework::GameEntityContextRequestBus::BroadcastResult(
entityContextId, &AzFramework::GameEntityContextRequestBus::Events::GetGameEntityContextId);
return AZ::RPI::Scene::GetSceneForEntityContextId(entityContextId);
}
} // namespace TrackView
@@ -0,0 +1,69 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <Atom/Feature/Utils/FrameCaptureBus.h>
#include <AzFramework/Components/CameraBus.h>
namespace TrackView
{
//! Provides functionality to capture frames from the "MainCamera".
//! A new pipeline is created (and associated with the scene provided), a callback can be
//! provided to handle the attachment readback (what to do with the captured frame) and also
//! what to do after an individual capture fully completes (called in OnCaptureFinished).
class AtomOutputFrameCapture : private AZ::Render::FrameCaptureNotificationBus::Handler
{
public:
AtomOutputFrameCapture() = default;
using CaptureFinishedCallback = AZStd::function<void()>;
//! Create a new pipeline associated with a given scene.
//! @note "MainCamera" is the view that is captured.
void CreatePipeline(AZ::RPI::Scene& scene, const AZStd::string& pipelineName, uint32_t width, uint32_t height);
//! Removes the pipeline from the scene provided and then destroys it.
//! @note scene must be the same scene used to create the pipeline.
void DestroyPipeline(AZ::RPI::Scene& scene);
//! Request a capture to start.
//! @param attachmentReadbackCallback Handles the returned attachment (image data returned by the renderer).
//! @param captureFinishedCallback Logic to run once the capture has completed fully.
bool BeginCapture(
const AZ::RPI::AttachmentReadback::CallbackFunction& attachmentReadbackCallback,
CaptureFinishedCallback captureFinishedCallback);
//! Update the internal view that is associated with the created pipeline.
void UpdateView(const AZ::Matrix3x4& cameraTransform, const AZ::Matrix4x4& cameraProjection);
private:
AZ::RPI::RenderPipelinePtr m_renderPipeline; //!< The internal render pipeline.
AZ::RPI::ViewPtr m_view; //!< The view associated with the render pipeline.
AZStd::vector<AZStd::string> m_passHierarchy; //!< Pass hierarchy (includes pipelineName and CopyToSwapChain).
CaptureFinishedCallback m_captureFinishedCallback; //!< Stored callback called from OnCaptureFinished.
// FrameCaptureNotificationBus overrides ...
void OnCaptureFinished(AZ::Render::FrameCaptureResult result, const AZStd::string& info) override;
};
inline AZ::EntityId ActiveCameraEntityId()
{
AZ::EntityId activeCameraId;
Camera::CameraSystemRequestBus::BroadcastResult(activeCameraId, &Camera::CameraSystemRequests::GetActiveCamera);
return activeCameraId;
}
//! Returns the transform for the given EntityId.
AZ::Matrix3x4 TransformFromEntityId(AZ::EntityId entityId);
//! Returns the projection matrix for the given camera EntityId.
//! @note Must provide a valid camera entity.
AZ::Matrix4x4 ProjectionFromCameraEntityId(AZ::EntityId entityId, float outputWidth, float outputHeight);
//! Helper to return the GameEntityContext scene.
AZ::RPI::Scene* SceneFromGameEntityContext();
} // namespace TrackView
@@ -0,0 +1,146 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "EditorDefs.h"
// CryCommon
#include <CryCommon/Maestro/Types/AnimParamType.h>
// Editor
#include "TrackViewKeyPropertiesDlg.h"
//////////////////////////////////////////////////////////////////////////
class CCaptureKeyUIControls
: public CTrackViewKeyUIControls
{
public:
CSmartVariableArray mv_table;
CSmartVariable<float> mv_duration;
CSmartVariable<float> mv_timeStep;
CSmartVariable<QString> mv_prefix;
CSmartVariable<QString> mv_folder;
CSmartVariable<bool> mv_once;
virtual void OnCreateVars()
{
mv_duration.GetVar()->SetLimits(0, 100000.0f);
mv_timeStep.GetVar()->SetLimits(0.001f, 1.0f);
AddVariable(mv_table, "Key Properties");
AddVariable(mv_table, mv_duration, "Duration");
AddVariable(mv_table, mv_timeStep, "Time Step");
AddVariable(mv_table, mv_prefix, "Output Prefix");
AddVariable(mv_table, mv_folder, "Output Folder");
AddVariable(mv_table, mv_once, "Just one frame?");
}
bool SupportTrackType(const CAnimParamType& paramType, [[maybe_unused]] EAnimCurveType trackType, [[maybe_unused]] AnimValueType valueType) const
{
return paramType == AnimParamType::Capture;
}
virtual bool OnKeySelectionChange(CTrackViewKeyBundle& selectedKeys);
virtual void OnUIChange(IVariable* pVar, CTrackViewKeyBundle& selectedKeys);
virtual unsigned int GetPriority() const { return 1; }
static const GUID& GetClassID()
{
// {543197BF-5E43-4abc-8F07-B84078846E4C}
static const GUID guid =
{
0x543197bf, 0x5e43, 0x4abc, { 0x8f, 0x7, 0xb8, 0x40, 0x78, 0x84, 0x6e, 0x4c }
};
return guid;
}
};
//////////////////////////////////////////////////////////////////////////
bool CCaptureKeyUIControls::OnKeySelectionChange(CTrackViewKeyBundle& selectedKeys)
{
if (!selectedKeys.AreAllKeysOfSameType())
{
return false;
}
bool bAssigned = false;
if (selectedKeys.GetKeyCount() == 1)
{
const CTrackViewKeyHandle& keyHandle = selectedKeys.GetKey(0);
CAnimParamType paramType = keyHandle.GetTrack()->GetParameterType();
if (paramType == AnimParamType::Capture)
{
ICaptureKey captureKey;
keyHandle.GetKey(&captureKey);
mv_duration = captureKey.duration;
mv_timeStep = captureKey.timeStep;
mv_prefix = captureKey.prefix.c_str();
mv_folder = captureKey.folder.c_str();
mv_once = captureKey.once;
bAssigned = true;
}
}
return bAssigned;
}
// Called when UI variable changes.
void CCaptureKeyUIControls::OnUIChange(IVariable* pVar, CTrackViewKeyBundle& selectedKeys)
{
CTrackViewSequence* sequence = GetIEditor()->GetAnimation()->GetSequence();
if (!sequence || !selectedKeys.AreAllKeysOfSameType())
{
return;
}
for (unsigned int keyIndex = 0; keyIndex < selectedKeys.GetKeyCount(); ++keyIndex)
{
CTrackViewKeyHandle keyHandle = selectedKeys.GetKey(keyIndex);
CAnimParamType paramType = keyHandle.GetTrack()->GetParameterType();
if (paramType == AnimParamType::Capture)
{
ICaptureKey captureKey;
keyHandle.GetKey(&captureKey);
SyncValue(mv_duration, captureKey.duration, false, pVar);
SyncValue(mv_timeStep, captureKey.timeStep, false, pVar);
if (pVar == mv_folder.GetVar())
{
QString sFolder = mv_folder;
captureKey.folder = sFolder.toUtf8().data();
}
if (pVar == mv_prefix.GetVar())
{
QString sPrefix = mv_prefix;
captureKey.prefix = sPrefix.toUtf8().data();
}
SyncValue(mv_once, captureKey.once, false, pVar);
bool isDuringUndo = false;
AzToolsFramework::ToolsApplicationRequests::Bus::BroadcastResult(
isDuringUndo, &AzToolsFramework::ToolsApplicationRequests::Bus::Events::IsDuringUndoRedo);
if (isDuringUndo)
{
keyHandle.SetKey(&captureKey);
}
else
{
AzToolsFramework::ScopedUndoBatch undoBatch("Set Key Value");
keyHandle.SetKey(&captureKey);
undoBatch.MarkEntityDirty(sequence->GetSequenceComponentEntityId());
}
}
}
}
REGISTER_QT_CLASS_DESC(CCaptureKeyUIControls, "TrackView.KeyUI.Capture", "TrackViewKeyUI");
@@ -0,0 +1,167 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "EditorDefs.h"
// CryCommon
#include <CryCommon/Maestro/Types/AnimNodeType.h> // for AnimNodeType
#include <CryCommon/Maestro/Types/AnimValueType.h> // for AnimValueType
#include <CryCommon/Maestro/Types/AnimParamType.h> // for AnimParamType
// Editor
#include "Controls/ReflectedPropertyControl/ReflectedPropertyItem.h" // for ReflectedPropertyItem
#include "TrackViewKeyPropertiesDlg.h" // for CTrackViewKeyUIControls
//////////////////////////////////////////////////////////////////////////
class CCharacterKeyUIControls
: public CTrackViewKeyUIControls
{
public:
CSmartVariableArray mv_table;
CSmartVariable<QString> mv_animation;
CSmartVariable<bool> mv_loop;
CSmartVariable<bool> mv_blendGap;
CSmartVariable<bool> mv_inplace;
CSmartVariable<float> mv_startTime;
CSmartVariable<float> mv_endTime;
CSmartVariable<float> mv_timeScale;
virtual void OnCreateVars()
{
AddVariable(mv_table, "Key Properties");
AddVariable(mv_table, mv_animation, "Animation", IVariable::DT_ANIMATION);
AddVariable(mv_table, mv_loop, "Loop");
AddVariable(mv_table, mv_blendGap, "Blend Gap");
AddVariable(mv_table, mv_inplace, "In Place");
AddVariable(mv_table, mv_startTime, "Start Time");
AddVariable(mv_table, mv_endTime, "End Time");
AddVariable(mv_table, mv_timeScale, "Time Scale");
mv_timeScale->SetLimits(0.001f, 100.f);
}
bool SupportTrackType(const CAnimParamType& paramType, [[maybe_unused]] EAnimCurveType trackType, AnimValueType valueType) const
{
return paramType == AnimParamType::Animation || valueType == AnimValueType::CharacterAnim;
}
virtual bool OnKeySelectionChange(CTrackViewKeyBundle& selectedKeys);
virtual void OnUIChange(IVariable* pVar, CTrackViewKeyBundle& selectedKeys);
virtual unsigned int GetPriority() const { return 1; }
static const GUID& GetClassID()
{
// {EAA26453-6B74-4771-8FD1-14CDFF88E723}
static const GUID guid =
{
0xeaa26453, 0x6b74, 0x4771, { 0x8f, 0xd1, 0x14, 0xcd, 0xff, 0x88, 0xe7, 0x23 }
};
return guid;
}
protected:
void ResetStartEndLimits(float characterKeyDuration);
};
//////////////////////////////////////////////////////////////////////////
void CCharacterKeyUIControls::ResetStartEndLimits(float characterKeyDuration)
{
const float time_zero = .0f;
float step = ReflectedPropertyItem::ComputeSliderStep(time_zero, characterKeyDuration);
mv_startTime.GetVar()->SetLimits(time_zero, characterKeyDuration, step, true, true);
mv_endTime.GetVar()->SetLimits(time_zero, characterKeyDuration, step, true, true);
}
bool CCharacterKeyUIControls::OnKeySelectionChange(CTrackViewKeyBundle& selectedKeys)
{
if (!selectedKeys.AreAllKeysOfSameType())
{
return false;
}
bool bAssigned = false;
if (selectedKeys.GetKeyCount() == 1)
{
const CTrackViewKeyHandle& keyHandle = selectedKeys.GetKey(0);
CAnimParamType paramType = keyHandle.GetTrack()->GetParameterType();
if (paramType == AnimParamType::Animation || keyHandle.GetTrack()->GetValueType() == AnimValueType::CharacterAnim)
{
ICharacterKey charKey;
keyHandle.GetKey(&charKey);
// Find editor object who owns this node.
const CTrackViewTrack* pTrack = keyHandle.GetTrack();
if (pTrack->GetAnimNode()->GetType() == AnimNodeType::Component)
{
// no legacy entity was returned and the track's animNode is a component - try to get the AZ::EntityId from the component node's parent
CTrackViewAnimNode* parentNode = static_cast<CTrackViewAnimNode*>(pTrack->GetAnimNode()->GetParentNode());
if (parentNode)
{
AZ::EntityId azEntityId = parentNode->GetAzEntityId();
if (azEntityId.IsValid())
{
static_assert(sizeof(AZ::EntityId) <= sizeof(AZ::u64), "Can't pack AZ::EntityId into a AZ::u64 in CCharacterKeyUIControls::OnKeySelectionChange.");
mv_animation->SetUserData(static_cast<AZ::u64>(azEntityId));
}
}
}
mv_animation = charKey.m_animation.c_str();
mv_loop = charKey.m_bLoop;
mv_blendGap = charKey.m_bBlendGap;
mv_inplace = charKey.m_bInPlace;
mv_endTime = charKey.m_endTime;
mv_startTime = charKey.m_startTime;
mv_timeScale = charKey.m_speed;
bAssigned = true;
}
}
return bAssigned;
}
// Called when UI variable changes.
void CCharacterKeyUIControls::OnUIChange(IVariable* pVar, CTrackViewKeyBundle& selectedKeys)
{
CTrackViewSequence* pSequence = GetIEditor()->GetAnimation()->GetSequence();
if (!pSequence || !selectedKeys.AreAllKeysOfSameType())
{
return;
}
for (unsigned int keyIndex = 0, num = (int)selectedKeys.GetKeyCount(); keyIndex < num; keyIndex++)
{
CTrackViewKeyHandle keyHandle = selectedKeys.GetKey(keyIndex);
CTrackViewTrack* pTrack = keyHandle.GetTrack();
const CAnimParamType paramType = pTrack->GetParameterType();
if (paramType == AnimParamType::Animation || keyHandle.GetTrack()->GetValueType() == AnimValueType::CharacterAnim)
{
ICharacterKey charKey;
keyHandle.GetKey(&charKey);
if (mv_animation.GetVar() == pVar)
{
charKey.m_animation = ((QString)mv_animation).toUtf8().data();
// This call is required to make sure that the newly set animation is properly triggered.
pTrack->GetSequence()->Reset(false);
}
SyncValue(mv_loop, charKey.m_bLoop, false, pVar);
SyncValue(mv_blendGap, charKey.m_bBlendGap, false, pVar);
SyncValue(mv_inplace, charKey.m_bInPlace, false, pVar);
SyncValue(mv_startTime, charKey.m_startTime, false, pVar);
SyncValue(mv_endTime, charKey.m_endTime, false, pVar);
SyncValue(mv_timeScale, charKey.m_speed, false, pVar);
keyHandle.SetKey(&charKey);
}
}
}
REGISTER_QT_CLASS_DESC(CCharacterKeyUIControls, "TrackView.KeyUI.Character", "TrackViewKeyUI");
@@ -0,0 +1,172 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "EditorDefs.h"
// CryCommon
#include <CryCommon/Maestro/Types/AnimParamType.h> // for AnimParamType
// Editor
#include "TrackViewKeyPropertiesDlg.h"
//////////////////////////////////////////////////////////////////////////
class CCommentKeyUIControls
: public CTrackViewKeyUIControls
{
public:
CSmartVariableArray mv_table;
CSmartVariable<QString> mv_comment;
CSmartVariable<float> mv_duration;
CSmartVariable<float> mv_size;
CSmartVariable<Vec3> mv_color;
CSmartVariableEnum<int> mv_align;
CSmartVariableEnum<QString> mv_font;
virtual void OnCreateVars()
{
AddVariable(mv_table, "Key Properties");
AddVariable(mv_table, mv_comment, "Comment");
AddVariable(mv_table, mv_duration, "Duration");
mv_size->SetLimits(1.f, 10.f);
AddVariable(mv_table, mv_size, "Size");
AddVariable(mv_table, mv_color, "Color", IVariable::DT_COLOR);
mv_align->SetEnumList(NULL);
mv_align->AddEnumItem("Left", ICommentKey::eTA_Left);
mv_align->AddEnumItem("Center", ICommentKey::eTA_Center);
mv_align->AddEnumItem("Right", ICommentKey::eTA_Right);
AddVariable(mv_table, mv_align, "Align");
mv_font->SetEnumList(NULL);
IFileUtil::FileArray fa;
CFileUtil::ScanDirectory((Path::GetEditingGameDataFolder() + "/Fonts/").c_str(), "*.xml", fa, true);
for (size_t i = 0; i < fa.size(); ++i)
{
string name = fa[i].filename.toUtf8().data();
PathUtil::RemoveExtension(name);
mv_font->AddEnumItem(name.c_str(), name.c_str());
}
AddVariable(mv_table, mv_font, "Font");
}
bool SupportTrackType(const CAnimParamType& paramType, [[maybe_unused]] EAnimCurveType trackType, [[maybe_unused]] AnimValueType valueType) const
{
return paramType == AnimParamType::CommentText;
}
virtual bool OnKeySelectionChange(CTrackViewKeyBundle& selectedKeys);
virtual void OnUIChange(IVariable* pVar, CTrackViewKeyBundle& selectedKeys);
virtual unsigned int GetPriority() const { return 1; }
static const GUID& GetClassID()
{
// {FA250B8B-FC2A-43b1-AF7A-8C3B6672B49D}
static const GUID guid =
{
0xfa250b8b, 0xfc2a, 0x43b1, { 0xaf, 0x7a, 0x8c, 0x3b, 0x66, 0x72, 0xb4, 0x9d }
};
return guid;
}
};
//////////////////////////////////////////////////////////////////////////
bool CCommentKeyUIControls::OnKeySelectionChange(CTrackViewKeyBundle& selectedKeys)
{
if (!selectedKeys.AreAllKeysOfSameType())
{
return false;
}
bool bAssigned = false;
if (selectedKeys.GetKeyCount() == 1)
{
const CTrackViewKeyHandle& keyHandle = selectedKeys.GetKey(0);
CAnimParamType paramType = keyHandle.GetTrack()->GetParameterType();
if (paramType == AnimParamType::CommentText)
{
ICommentKey commentKey;
keyHandle.GetKey(&commentKey);
mv_comment = commentKey.m_strComment.c_str();
mv_duration = commentKey.m_duration;
mv_size = commentKey.m_size;
mv_font = commentKey.m_strFont.c_str();
mv_color = Vec3(commentKey.m_color.GetR(), commentKey.m_color.GetG(), commentKey.m_color.GetB());
mv_align = commentKey.m_align;
bAssigned = true;
}
}
return bAssigned;
}
//////////////////////////////////////////////////////////////////////////
// Called when UI variable changes.
void CCommentKeyUIControls::OnUIChange(IVariable* pVar, CTrackViewKeyBundle& selectedKeys)
{
CTrackViewSequence* sequence = GetIEditor()->GetAnimation()->GetSequence();
if (!sequence || !selectedKeys.AreAllKeysOfSameType())
{
return;
}
for (size_t keyIndex = 0, num = selectedKeys.GetKeyCount(); keyIndex < num; keyIndex++)
{
CTrackViewKeyHandle keyHandle = selectedKeys.GetKey(keyIndex);
CAnimParamType paramType = keyHandle.GetTrack()->GetParameterType();
if (paramType == AnimParamType::CommentText)
{
ICommentKey commentKey;
keyHandle.GetKey(&commentKey);
if (!pVar || pVar == mv_comment.GetVar())
{
commentKey.m_strComment = ((QString)mv_comment).toUtf8().data();
}
if (!pVar || pVar == mv_font.GetVar())
{
QString sFont = mv_font;
commentKey.m_strFont = sFont.toUtf8().data();
}
if (!pVar || pVar == mv_align.GetVar())
{
commentKey.m_align = (ICommentKey::ETextAlign)((int)mv_align);
}
SyncValue(mv_duration, commentKey.m_duration, false, pVar);
Vec3 color(commentKey.m_color.GetR(), commentKey.m_color.GetG(), commentKey.m_color.GetB());
SyncValue(mv_color, color, false, pVar);
commentKey.m_color.Set(color.x, color.y, color.z, commentKey.m_color.GetA());
SyncValue(mv_size, commentKey.m_size, false, pVar);
bool isDuringUndo = false;
AzToolsFramework::ToolsApplicationRequests::Bus::BroadcastResult(isDuringUndo, &AzToolsFramework::ToolsApplicationRequests::Bus::Events::IsDuringUndoRedo);
if (isDuringUndo)
{
keyHandle.SetKey(&commentKey);
}
else
{
// Let the AZ Undo system manage the nodes on the sequence entity
AzToolsFramework::ScopedUndoBatch undoBatch("Change key");
keyHandle.SetKey(&commentKey);
undoBatch.MarkEntityDirty(sequence->GetSequenceComponentEntityId());
}
}
}
}
REGISTER_QT_CLASS_DESC(CCommentKeyUIControls, "TrackView.KeyUI.Comment", "TrackViewKeyUI");
@@ -0,0 +1,209 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "EditorDefs.h"
#include "CommentNodeAnimator.h"
// CryCommon
#include <CryCommon/Maestro/Types/AnimParamType.h>
// Editor
#include "Settings.h"
CCommentNodeAnimator::CCommentNodeAnimator(CTrackViewAnimNode* pCommentNode)
{
assert(pCommentNode);
m_pCommentNode = pCommentNode;
}
CCommentNodeAnimator::~CCommentNodeAnimator()
{
m_pCommentNode = 0;
}
void CCommentNodeAnimator::Animate(CTrackViewAnimNode* pNode, const SAnimContext& ac)
{
if (pNode != m_pCommentNode || pNode->IsDisabled())
{
return;
}
CTrackViewTrackBundle tracks = pNode->GetAllTracks();
int trackCount = tracks.GetCount();
Vec2 pos(0, 0);
for (int i = 0; i < trackCount; ++i)
{
CTrackViewTrack* pTrack = tracks.GetTrack(i);
if (pTrack->IsMasked(ac.trackMask))
{
continue;
}
switch (pTrack->GetParameterType().GetType())
{
case AnimParamType::CommentText:
{
AnimateCommentTextTrack(pTrack, ac);
}
break;
case AnimParamType::PositionX:
{
pTrack->GetValue(ac.time, pos.x);
}
break;
case AnimParamType::PositionY:
{
pTrack->GetValue(ac.time, pos.y);
}
break;
}
}
// Position mapping from [0,100] to [-1,1]
pos = (pos - Vec2(50.0f, 50.0f)) / 50.0f;
m_commentContext.m_unitPos = pos;
}
void CCommentNodeAnimator::AnimateCommentTextTrack(CTrackViewTrack* pTrack, const SAnimContext& ac)
{
if (pTrack->GetKeyCount() == 0)
{
return;
}
CTrackViewKeyHandle keyHandle = GetActiveKeyHandle(pTrack, ac.time);
if (keyHandle.IsValid())
{
ICommentKey commentKey;
keyHandle.GetKey(&commentKey);
if (commentKey.m_duration > 0 && ac.time < keyHandle.GetTime() + commentKey.m_duration)
{
m_commentContext.m_strComment = commentKey.m_strComment;
cry_strcpy(m_commentContext.m_strFont, commentKey.m_strFont.c_str());
m_commentContext.m_color = commentKey.m_color;
m_commentContext.m_align = commentKey.m_align;
m_commentContext.m_size = commentKey.m_size;
}
else
{
m_commentContext.m_strComment.clear();
}
}
else
{
m_commentContext.m_strComment.clear();
}
}
CTrackViewKeyHandle CCommentNodeAnimator::GetActiveKeyHandle(CTrackViewTrack* pTrack, float fTime)
{
const int nkeys = pTrack->GetKeyCount();
if (nkeys == 0)
{
return CTrackViewKeyHandle();
}
const CTrackViewKeyHandle& firstKeyHandle = pTrack->GetKey(0);
// Time is before first key.
if (firstKeyHandle.GetTime() > fTime)
{
return CTrackViewKeyHandle();
}
for (int i = 0; i < nkeys; i++)
{
const CTrackViewKeyHandle& keyHandle = pTrack->GetKey(i);
if (fTime >= keyHandle.GetTime())
{
if ((i == nkeys - 1) || (fTime < pTrack->GetKey(i + 1).GetTime()))
{
return keyHandle;
}
}
else
{
break;
}
}
return CTrackViewKeyHandle();
}
void CCommentNodeAnimator::Render(CTrackViewAnimNode* pNode, [[maybe_unused]] const SAnimContext& ac)
{
if (!pNode->IsDisabled())
{
CCommentContext* cc = &m_commentContext;
if (!cc->m_strComment.empty())
{
Vec3 color(cc->m_color.GetR(), cc->m_color.GetG(), cc->m_color.GetB());
DrawText(cc->m_strFont, cc->m_size, cc->m_unitPos, color, cc->m_strComment.c_str(), cc->m_align);
}
}
}
Vec2 CCommentNodeAnimator::GetScreenPosFromNormalizedPos(const Vec2& unitPos)
{
const CCamera& cam = gEnv->pSystem->GetViewCamera();
float width = (float)cam.GetViewSurfaceX();
int height = cam.GetViewSurfaceZ();
float fAspectRatio = gSettings.viewports.fDefaultAspectRatio;
float camWidth = height * fAspectRatio;
float x = 0.5f * width + 0.5f * camWidth * unitPos.x;
float y = 0.5f * height * (1.f - unitPos.y);
return Vec2(x, y);
}
void CCommentNodeAnimator::DrawText(const char* szFontName, float fSize, const Vec2& unitPos, const ColorF col, const char* szText, int align)
{
IFFont* pFont = gEnv->pCryFont->GetFont(szFontName);
if (!pFont)
{
pFont = gEnv->pCryFont->GetFont("default");
}
if (pFont)
{
STextDrawContext ctx;
ctx.SetSizeIn800x600(false);
ctx.SetSize(Vec2(UIDRAW_TEXTSIZEFACTOR * fSize, UIDRAW_TEXTSIZEFACTOR * fSize));
ctx.SetCharWidthScale(0.5f);
ctx.SetProportional(false);
ctx.SetFlags(align);
// alignment
Vec2 pos = GetScreenPosFromNormalizedPos(unitPos);
if (align & eDrawText_Center)
{
pos.x -= pFont->GetTextSize(szText, true, ctx).x * 0.5f;
}
else if (align & eDrawText_Right)
{
pos.x -= pFont->GetTextSize(szText, true, ctx).x;
}
// Color
ctx.SetColor(col);
pFont->DrawString(pos.x, pos.y, szText, true, ctx);
}
}
@@ -0,0 +1,67 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
// Description : Comment node animator class
/*
CCommentContext stores information about comment track.
The Comment Track is activated only in the editor.
*/
#ifndef CRYINCLUDE_EDITOR_TRACKVIEW_COMMENTNODEANIMATOR_H
#define CRYINCLUDE_EDITOR_TRACKVIEW_COMMENTNODEANIMATOR_H
#pragma once
#include "TrackViewAnimNode.h"
class CTrackViewTrack;
struct CCommentContext
{
CCommentContext()
: m_nLastActiveKeyIndex(-1)
, m_size(1.0f)
, m_align(0)
, m_color(0.f, 0.f, 0.f, 1.f)
{
sprintf_s(m_strFont, sizeof(m_strFont), "default");
m_unitPos = Vec2(0.f, 0.f);
}
int m_nLastActiveKeyIndex;
AZStd::string m_strComment;
char m_strFont[64];
Vec2 m_unitPos;
AZ::Color m_color;
float m_size;
int m_align;
};
class CCommentNodeAnimator
: public IAnimNodeAnimator
{
public:
CCommentNodeAnimator(CTrackViewAnimNode* pCommentNode);
virtual void Animate(CTrackViewAnimNode* pNode, const SAnimContext& ac);
virtual void Render(CTrackViewAnimNode* pNode, const SAnimContext& ac);
private:
virtual ~CCommentNodeAnimator();
void AnimateCommentTextTrack(CTrackViewTrack* pTrack, const SAnimContext& ac);
CTrackViewKeyHandle GetActiveKeyHandle(CTrackViewTrack* pTrack, float fTime);
Vec2 GetScreenPosFromNormalizedPos(const Vec2& unitPos);
void DrawText(const char* szFontName, float fSize, const Vec2& unitPos, const ColorF col, const char* szText, int align);
CTrackViewAnimNode* m_pCommentNode;
CCommentContext m_commentContext;
};
#endif // CRYINCLUDE_EDITOR_TRACKVIEW_COMMENTNODEANIMATOR_H
@@ -0,0 +1,121 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "EditorDefs.h"
// CryCommon
#include <CryCommon/Maestro/Types/AnimParamType.h> // for AnimParamType
// Editor
#include "TrackViewKeyPropertiesDlg.h" // for CTrackViewKeyUIControls
//////////////////////////////////////////////////////////////////////////
class CConsoleKeyUIControls
: public CTrackViewKeyUIControls
{
public:
CSmartVariableArray mv_table;
CSmartVariable<QString> mv_command;
virtual void OnCreateVars()
{
AddVariable(mv_table, "Key Properties");
AddVariable(mv_table, mv_command, "Command");
}
bool SupportTrackType(const CAnimParamType& paramType, [[maybe_unused]] EAnimCurveType trackType, [[maybe_unused]] AnimValueType valueType) const
{
return paramType == AnimParamType::Console;
}
virtual bool OnKeySelectionChange(CTrackViewKeyBundle& selectedKeys);
virtual void OnUIChange(IVariable* pVar, CTrackViewKeyBundle& selectedKeys);
virtual unsigned int GetPriority() const { return 1; }
static const GUID& GetClassID()
{
// {3E9D2C57-BFB1-42f9-82AC-A393C1062634}
static const GUID guid =
{
0x3e9d2c57, 0xbfb1, 0x42f9, { 0x82, 0xac, 0xa3, 0x93, 0xc1, 0x6, 0x26, 0x34 }
};
return guid;
}
};
//////////////////////////////////////////////////////////////////////////
bool CConsoleKeyUIControls::OnKeySelectionChange(CTrackViewKeyBundle& selectedKeys)
{
if (!selectedKeys.AreAllKeysOfSameType())
{
return false;
}
bool bAssigned = false;
if (selectedKeys.GetKeyCount() == 1)
{
const CTrackViewKeyHandle& keyHandle = selectedKeys.GetKey(0);
CAnimParamType paramType = keyHandle.GetTrack()->GetParameterType();
if (paramType == AnimParamType::Console)
{
IConsoleKey consoleKey;
keyHandle.GetKey(&consoleKey);
mv_command = ((QString)consoleKey.command.c_str()).toUtf8().data();
bAssigned = true;
}
}
return bAssigned;
}
// Called when UI variable changes.
void CConsoleKeyUIControls::OnUIChange(IVariable* pVar, CTrackViewKeyBundle& selectedKeys)
{
CTrackViewSequence* sequence = GetIEditor()->GetAnimation()->GetSequence();
if (!sequence || !selectedKeys.AreAllKeysOfSameType())
{
return;
}
for (unsigned int keyIndex = 0; keyIndex < selectedKeys.GetKeyCount(); ++keyIndex)
{
CTrackViewKeyHandle keyHandle = selectedKeys.GetKey(keyIndex);
CAnimParamType paramType = keyHandle.GetTrack()->GetParameterType();
if (paramType == AnimParamType::Console)
{
IConsoleKey consoleKey;
keyHandle.GetKey(&consoleKey);
if (pVar == mv_command.GetVar())
{
consoleKey.command = ((QString)mv_command).toUtf8().data();
}
bool isDuringUndo = false;
AzToolsFramework::ToolsApplicationRequests::Bus::BroadcastResult(isDuringUndo, &AzToolsFramework::ToolsApplicationRequests::Bus::Events::IsDuringUndoRedo);
if (isDuringUndo)
{
keyHandle.SetKey(&consoleKey);
}
else
{
AzToolsFramework::ScopedUndoBatch undoBatch("Set Key Value");
keyHandle.SetKey(&consoleKey);
undoBatch.MarkEntityDirty(sequence->GetSequenceComponentEntityId());
}
}
}
}
REGISTER_QT_CLASS_DESC(CConsoleKeyUIControls, "TrackView.KeyUI.Console", "TrackViewKeyUI");
@@ -0,0 +1,246 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "EditorDefs.h"
#include "DirectorNodeAnimator.h"
// CryCommon
#include <CryCommon/Maestro/Types/AnimParamType.h> // for AnimParamType
// Editor
#include "TrackView/TrackViewSequenceManager.h" // for CTrackViewSequence
////////////////////////////////////////////////////////////////////////////
CDirectorNodeAnimator::CDirectorNodeAnimator(CTrackViewAnimNode* pDirectorNode)
: m_pDirectorNode(pDirectorNode)
{
assert(m_pDirectorNode != nullptr);
}
////////////////////////////////////////////////////////////////////////////
void CDirectorNodeAnimator::Animate(CTrackViewAnimNode* pNode, const SAnimContext& ac)
{
if (!pNode->IsActiveDirector())
{
// Don't animate if it's not the sequence track of the active director
return;
}
CTrackViewTrack* pSequenceTrack = pNode->GetTrackForParameter(AnimParamType::Sequence);
if (pSequenceTrack && !pSequenceTrack->IsDisabled())
{
std::vector<CTrackViewSequence*> inactiveSequences;
std::vector<CTrackViewSequence*> activeSequences;
// Construct sets of sequences that need to be bound/unbound at this point
const float time = ac.time;
const unsigned int numKeys = pSequenceTrack->GetKeyCount();
for (unsigned int i = 0; i < numKeys; ++i)
{
CTrackViewKeyHandle keyHandle = pSequenceTrack->GetKey(i);
ISequenceKey sequenceKey;
keyHandle.GetKey(&sequenceKey);
CTrackViewSequence* pSequence = GetSequenceFromSequenceKey(sequenceKey);
if (pSequence)
{
if (sequenceKey.time <= time)
{
stl::push_back_unique(activeSequences, pSequence);
stl::find_and_erase(inactiveSequences, pSequence);
}
else
{
if (!stl::find(activeSequences, pSequence))
{
stl::push_back_unique(inactiveSequences, pSequence);
}
}
}
}
// Unbind must occur before binding, because entities can be referenced in multiple sequences
for (auto iter = inactiveSequences.begin(); iter != inactiveSequences.end(); ++iter)
{
CTrackViewSequence* pSequence = *iter;
if (pSequence->IsBoundToEditorObjects())
{
// No notifications because unbinding would call ForceAnimation again
CTrackViewSequenceNoNotificationContext context(pSequence);
pSequence->UnBindFromEditorObjects();
}
}
// Now bind sequences
for (auto iter = activeSequences.begin(); iter != activeSequences.end(); ++iter)
{
CTrackViewSequence* pSequence = *iter;
if (!pSequence->IsBoundToEditorObjects())
{
// No notifications because binding would call ForceAnimation again
CTrackViewSequenceNoNotificationContext context(pSequence);
pSequence->BindToEditorObjects();
// Make sure the sequence is active, harmless to call if the sequences is already
// active. The sequence may not be active in the Editor if this key was just created.
pSequence->Activate();
}
}
// Animate sub sequences
ForEachActiveSequence(ac, pSequenceTrack, true,
[&](CTrackViewSequence* pSequence, const SAnimContext& newAnimContext)
{
pSequence->Animate(newAnimContext);
},
[&](CTrackViewSequence* pSequence, [[maybe_unused]] const SAnimContext& newAnimContext)
{
pSequence->Reset(false);
}
);
}
}
////////////////////////////////////////////////////////////////////////////
void CDirectorNodeAnimator::Render(CTrackViewAnimNode* pNode, const SAnimContext& ac)
{
if (!pNode->IsActiveDirector())
{
// Don't animate if it's not the sequence track of the active director
return;
}
CTrackViewTrack* pSequenceTrack = pNode->GetTrackForParameter(AnimParamType::Sequence);
if (pSequenceTrack && !pSequenceTrack->IsDisabled())
{
// Render sub sequences
ForEachActiveSequence(ac, pSequenceTrack, false,
[&](CTrackViewSequence* pSequence, [[maybe_unused]] const SAnimContext& newAnimContext)
{
pSequence->Render(newAnimContext);
},
[&]([[maybe_unused]] CTrackViewSequence* pSequence, [[maybe_unused]] const SAnimContext& newAnimContext) {}
);
}
}
////////////////////////////////////////////////////////////////////////////
void CDirectorNodeAnimator::ForEachActiveSequence(const SAnimContext& ac, CTrackViewTrack* pSequenceTrack,
const bool bHandleOtherKeys, std::function<void(CTrackViewSequence*, const SAnimContext&)> animateFunction,
std::function<void(CTrackViewSequence*, const SAnimContext&)> resetFunction)
{
const float time = ac.time;
const unsigned int numKeys = pSequenceTrack->GetKeyCount();
if (bHandleOtherKeys)
{
// Reset all non-active sequences first
for (unsigned int i = 0; i < numKeys; ++i)
{
CTrackViewKeyHandle keyHandle = pSequenceTrack->GetKey(i);
ISequenceKey sequenceKey;
keyHandle.GetKey(&sequenceKey);
CTrackViewSequence* pSequence = GetSequenceFromSequenceKey(sequenceKey);
if (pSequence)
{
SAnimContext newAnimContext = ac;
const float duration = sequenceKey.fDuration;
const float sequenceTime = ac.time - sequenceKey.time + sequenceKey.fStartTime;
const float sequenceDuration = duration + sequenceKey.fStartTime;
newAnimContext.time = std::min(sequenceTime, sequenceDuration);
const bool bInsideKeyRange = (sequenceTime >= 0.0f) && (sequenceTime <= sequenceDuration);
if (!bInsideKeyRange)
{
if (ac.forcePlay && sequenceTime >= 0.0f && newAnimContext.time != pSequence->GetTime())
{
// If forcing animation force previous keys to their last playback position
animateFunction(pSequence, newAnimContext);
}
resetFunction(pSequence, newAnimContext);
}
}
}
}
for (unsigned int i = 0; i < numKeys; ++i)
{
CTrackViewKeyHandle keyHandle = pSequenceTrack->GetKey(i);
ISequenceKey sequenceKey;
keyHandle.GetKey(&sequenceKey);
CTrackViewSequence* pSequence = GetSequenceFromSequenceKey(sequenceKey);
if (pSequence)
{
SAnimContext newAnimContext = ac;
const float duration = sequenceKey.fDuration;
const float sequenceTime = ac.time - sequenceKey.time + sequenceKey.fStartTime;
const float sequenceDuration = duration + sequenceKey.fStartTime;
newAnimContext.time = std::min(sequenceTime, sequenceDuration);
const bool bInsideKeyRange = (sequenceTime >= 0.0f) && (sequenceTime <= sequenceDuration);
if ((bInsideKeyRange && (newAnimContext.time != pSequence->GetTime() || ac.forcePlay)))
{
animateFunction(pSequence, newAnimContext);
}
}
}
}
////////////////////////////////////////////////////////////////////////////
void CDirectorNodeAnimator::UnBind([[maybe_unused]] CTrackViewAnimNode* pNode)
{
const CTrackViewSequenceManager* pSequenceManager = GetIEditor()->GetSequenceManager();
const unsigned int numSequences = pSequenceManager->GetCount();
for (unsigned int sequenceIndex = 0; sequenceIndex < numSequences; ++sequenceIndex)
{
CTrackViewSequence* pSequence = pSequenceManager->GetSequenceByIndex(sequenceIndex);
if (pSequence->IsActiveSequence())
{
// Don't care about the active sequence
continue;
}
if (pSequence->IsBoundToEditorObjects())
{
pSequence->UnBindFromEditorObjects();
}
}
}
/*static*/ CTrackViewSequence* CDirectorNodeAnimator::GetSequenceFromSequenceKey(const ISequenceKey& sequenceKey)
{
CTrackViewSequence* retSequence = nullptr;
const CTrackViewSequenceManager* sequenceManager = GetIEditor()->GetSequenceManager();
if (sequenceManager)
{
if (sequenceKey.sequenceEntityId.IsValid())
{
retSequence = sequenceManager->GetSequenceByEntityId(sequenceKey.sequenceEntityId);
AZ_Assert(retSequence, "Null sequence returned when a Sequence Component was expected.");
}
}
return retSequence;
}
@@ -0,0 +1,39 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef CRYINCLUDE_EDITOR_TRACKVIEW_DIRECTORNODEANIMATOR_H
#define CRYINCLUDE_EDITOR_TRACKVIEW_DIRECTORNODEANIMATOR_H
#pragma once
#include "TrackViewAnimNode.h"
// This is used to bind/unbind sub sequences in director nodes
// when the sequence time changes. A sequence only gets bound if it was already
// referred in time before.
class CDirectorNodeAnimator
: public IAnimNodeAnimator
{
public:
CDirectorNodeAnimator(CTrackViewAnimNode* pDirectorNode);
virtual void Animate(CTrackViewAnimNode* pNode, const SAnimContext& ac) override;
virtual void Render(CTrackViewAnimNode* pNode, const SAnimContext& ac) override;
virtual void UnBind(CTrackViewAnimNode* pNode) override;
// Utility function to find a CTrackViewSequence* from an ISequenceKey
static CTrackViewSequence* GetSequenceFromSequenceKey(const ISequenceKey& sequenceKey);
private:
void ForEachActiveSequence(const SAnimContext& ac, CTrackViewTrack* pSequenceTrack,
const bool bHandleOtherKeys, std::function<void(CTrackViewSequence*, const SAnimContext&)> animateFunction,
std::function<void(CTrackViewSequence*, const SAnimContext&)> resetFunction);
CTrackViewAnimNode* m_pDirectorNode;
};
#endif // CRYINCLUDE_EDITOR_TRACKVIEW_DIRECTORNODEANIMATOR_H
@@ -0,0 +1,133 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzCore/std/any.h>
namespace AzToolsFramework
{
/**
* This bus can be used to send commands to the track view.
*/
class EditorLayerTrackViewRequests
: public AZ::ComponentBus
{
public:
/**
* Gets the number of sequences.
*/
virtual int GetNumSequences() = 0;
/**
* Creates a new sequence of the given type(0 = Object Entity Sequence(Legacy), 1 = Component Entity Sequence(PREVIEW)) with the given name.
*/
virtual void NewSequence(const char* name, int sequenceType) = 0;
/**
* Plays the current sequence in TrackView.
*/
virtual void PlaySequence() = 0;
/**
* Stops any sequence currently playing in TrackView.
*/
virtual void StopSequence() = 0;
/**
* Sets the time of the sequence currently playing in TrackView.
*/
virtual void SetSequenceTime(float time) = 0;
/**
* Adds an entity node(s) from viewport selection to the current sequence.
*/
virtual void AddSelectedEntities() = 0;
/**
* Adds a layer node from the current layer to the current sequence.
*/
virtual void AddLayerNode() = 0;
/**
* Adds a track of the given parameter ID to the node.
*/
virtual void AddTrack(const char* paramName, const char* nodeName, const char* parentDirectorName) = 0;
/**
* Deletes a track of the given parameter ID (in the given index in case of a multi-track) from the node.
*/
virtual void DeleteTrack(const char* paramName, uint32 index, const char* nodeName, const char* parentDirectorName) = 0;
/**
* Gets number of keys of the specified track.
*/
virtual int GetNumTrackKeys(const char* paramName, int trackIndex, const char* nodeName, const char* parentDirectorName) = 0;
/**
* Activates/deactivates TrackView recording mode.
*/
virtual void SetRecording(bool bRecording) = 0;
/**
* Deletes the specified sequence.
*/
virtual void DeleteSequence(const char* name) = 0;
/**
* Sets the specified sequence as a current one in TrackView.
*/
virtual void SetCurrentSequence(const char* name) = 0;
/**
* Gets the name of a sequence by its index.
*/
virtual AZStd::string GetSequenceName(unsigned int index) = 0;
/**
* Gets the time range of a sequence as a pair.
*/
virtual TRange<float> GetSequenceTimeRange(const char* name) = 0;
/**
* Adds a new node with the given type & name to the current sequence.
*/
virtual void AddNode(const char* nodeTypeString, const char* nodeName) = 0;
/**
* Deletes the specified node from the current sequence.
*/
virtual void DeleteNode(AZStd::string_view nodeName, AZStd::string_view parentDirectorName) = 0;
/**
* Gets the number of nodes.
*/
virtual int GetNumNodes(AZStd::string_view parentDirectorName) = 0;
/**
* Gets the name of a sequence by its index.
*/
virtual AZStd::string GetNodeName(int index, AZStd::string_view parentDirectorName) = 0;
/**
* Gets the value of the specified key.
*/
virtual AZStd::any GetKeyValue(const char* paramName, int trackIndex, int keyIndex, const char* nodeName, const char* parentDirectorName) = 0;
/**
* Gets the interpolated value of a track at the specified time.
*/
virtual AZStd::any GetInterpolatedValue(const char* paramName, int trackIndex, float time, const char* nodeName, const char* parentDirectorName) = 0;
/**
* Sets the time range of a sequence.
*/
virtual void SetSequenceTimeRange(const char* name, float start, float end) = 0;
};
using EditorLayerTrackViewRequestBus = AZ::EBus<EditorLayerTrackViewRequests>;
}
@@ -0,0 +1,152 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "EditorDefs.h"
#include "TrackViewKeyPropertiesDlg.h" // for CTrackViewKeyUIControls
#include <CryCommon/Maestro/Types/AnimParamType.h> // AnimParamType
//////////////////////////////////////////////////////////////////////////
class CEventKeyUIControls
: public CTrackViewKeyUIControls
{
public:
CSmartVariableArray mv_table;
CSmartVariableArray mv_deprecated;
CSmartVariableEnum<QString> mv_animation;
CSmartVariableEnum<QString> mv_event;
CSmartVariable<QString> mv_value;
CSmartVariable<bool> mv_notrigger_in_scrubbing;
virtual void OnCreateVars()
{
AddVariable(mv_table, "Key Properties");
AddVariable(mv_table, mv_event, "Event");
AddVariable(mv_table, mv_value, "Value");
AddVariable(mv_table, mv_notrigger_in_scrubbing, "No trigger in scrubbing");
AddVariable(mv_deprecated, "Deprecated");
AddVariable(mv_deprecated, mv_animation, "Animation");
}
bool SupportTrackType(const CAnimParamType& paramType, [[maybe_unused]] EAnimCurveType trackType, [[maybe_unused]] AnimValueType valueType) const
{
return paramType == AnimParamType::Event;
}
virtual bool OnKeySelectionChange(CTrackViewKeyBundle& selectedKeys);
virtual void OnUIChange(IVariable* pVar, CTrackViewKeyBundle& selectedKeys);
virtual unsigned int GetPriority() const { return 1; }
static const GUID& GetClassID()
{
// {ED5A2023-EDE1-4a47-BBE6-7D7BA0E4001D}
static const GUID guid =
{
0xed5a2023, 0xede1, 0x4a47, { 0xbb, 0xe6, 0x7d, 0x7b, 0xa0, 0xe4, 0x0, 0x1d }
};
return guid;
}
private:
};
//////////////////////////////////////////////////////////////////////////
bool CEventKeyUIControls::OnKeySelectionChange(CTrackViewKeyBundle& selectedKeys)
{
if (!selectedKeys.AreAllKeysOfSameType())
{
return false;
}
bool bAssigned = false;
if (selectedKeys.GetKeyCount() == 1)
{
const CTrackViewKeyHandle& keyHandle = selectedKeys.GetKey(0);
CAnimParamType paramType = keyHandle.GetTrack()->GetParameterType();
if (paramType == AnimParamType::Event)
{
mv_event.SetEnumList(NULL);
mv_animation.SetEnumList(NULL);
// Add <None> for empty, unset event
mv_event->AddEnumItem(QObject::tr("<None>"), "");
mv_animation->AddEnumItem(QObject::tr("<None>"), "");
IEventKey eventKey;
keyHandle.GetKey(&eventKey);
mv_event = eventKey.event.c_str();
mv_value = eventKey.eventValue.c_str();
mv_animation = eventKey.animation.c_str();
mv_notrigger_in_scrubbing = eventKey.bNoTriggerInScrubbing;
bAssigned = true;
}
}
return bAssigned;
}
// Called when UI variable changes.
void CEventKeyUIControls::OnUIChange(IVariable* pVar, CTrackViewKeyBundle& selectedKeys)
{
CTrackViewSequence* sequence = GetIEditor()->GetAnimation()->GetSequence();
if (!sequence || !selectedKeys.AreAllKeysOfSameType())
{
return;
}
for (unsigned int keyIndex = 0; keyIndex < selectedKeys.GetKeyCount(); ++keyIndex)
{
CTrackViewKeyHandle keyHandle = selectedKeys.GetKey(keyIndex);
CAnimParamType paramType = keyHandle.GetTrack()->GetParameterType();
if (paramType == AnimParamType::Event)
{
IEventKey eventKey;
keyHandle.GetKey(&eventKey);
QByteArray event, value, animation;
event = static_cast<QString>(mv_event).toUtf8();
value = static_cast<QString>(mv_value).toUtf8();
animation = static_cast<QString>(mv_animation).toUtf8();
if (pVar == mv_event.GetVar())
{
eventKey.event = event.data();
}
if (pVar == mv_value.GetVar())
{
eventKey.eventValue = value.data();
}
if (pVar == mv_animation.GetVar())
{
eventKey.animation = animation.data();
}
SyncValue(mv_notrigger_in_scrubbing, eventKey.bNoTriggerInScrubbing, false, pVar);
bool isDuringUndo = false;
AzToolsFramework::ToolsApplicationRequests::Bus::BroadcastResult(isDuringUndo, &AzToolsFramework::ToolsApplicationRequests::Bus::Events::IsDuringUndoRedo);
if (isDuringUndo)
{
keyHandle.SetKey(&eventKey);
}
else
{
AzToolsFramework::ScopedUndoBatch undoBatch("Set Key Value");
keyHandle.SetKey(&eventKey);
undoBatch.MarkEntityDirty(sequence->GetSequenceComponentEntityId());
}
}
}
}
REGISTER_QT_CLASS_DESC(CEventKeyUIControls, "TrackView.KeyUI.Event", "TrackViewKeyUI");
+124
View File
@@ -0,0 +1,124 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "EditorDefs.h"
// CryCommon
#include <CryCommon/Maestro/Types/AnimParamType.h>
// Editor
#include "TrackViewKeyPropertiesDlg.h"
//////////////////////////////////////////////////////////////////////////
class CGotoKeyUIControls
: public CTrackViewKeyUIControls
{
public:
CSmartVariableArray mv_table;
CSmartVariable<float> mv_command;
virtual void OnCreateVars()
{
AddVariable(mv_table, "Key Properties");
AddVariable(mv_table, mv_command, "Goto Time");
}
bool SupportTrackType(const CAnimParamType& paramType, [[maybe_unused]] EAnimCurveType trackType, [[maybe_unused]] AnimValueType valueType) const
{
if (paramType == AnimParamType::Goto)
{
return true;
}
else
{
return false;
}
}
virtual bool OnKeySelectionChange(CTrackViewKeyBundle& selectedKeys);
virtual void OnUIChange(IVariable* pVar, CTrackViewKeyBundle& selectedKeys);
virtual unsigned int GetPriority() const { return 1; }
static const GUID& GetClassID()
{
// {3E9D2C57-BFB1-42f9-82AC-A393C1062634}
static const GUID guid =
{
0x9b79c8b6, 0xe332, 0x4b9b, { 0xb2, 0x63, 0xef, 0x7e, 0x82, 0x7, 0xa4, 0x47 }
};
return guid;
}
};
//////////////////////////////////////////////////////////////////////////
bool CGotoKeyUIControls::OnKeySelectionChange(CTrackViewKeyBundle& selectedKeys)
{
if (!selectedKeys.AreAllKeysOfSameType())
{
return false;
}
bool bAssigned = false;
if (selectedKeys.GetKeyCount() == 1)
{
const CTrackViewKeyHandle& keyHandle = selectedKeys.GetKey(0);
CAnimParamType paramType = keyHandle.GetTrack()->GetParameterType();
if (paramType == AnimParamType::Goto)
{
IDiscreteFloatKey discreteFloatKey;
keyHandle.GetKey(&discreteFloatKey);
mv_command = discreteFloatKey.m_fValue;
bAssigned = true;
}
}
return bAssigned;
}
//////////////////////////////////////////////////////////////////////////
// Called when UI variable changes.
void CGotoKeyUIControls::OnUIChange(IVariable* pVar, CTrackViewKeyBundle& selectedKeys)
{
CTrackViewSequence* sequence = GetIEditor()->GetAnimation()->GetSequence();
if (!sequence || !selectedKeys.AreAllKeysOfSameType())
{
return;
}
for (unsigned int keyIndex = 0; keyIndex < selectedKeys.GetKeyCount(); ++keyIndex)
{
CTrackViewKeyHandle keyHandle = selectedKeys.GetKey(keyIndex);
CAnimParamType paramType = keyHandle.GetTrack()->GetParameterType();
if (paramType == AnimParamType::Goto)
{
IDiscreteFloatKey discreteFloatKey;
keyHandle.GetKey(&discreteFloatKey);
SyncValue(mv_command, discreteFloatKey.m_fValue, false, pVar);
bool isDuringUndo = false;
AzToolsFramework::ToolsApplicationRequests::Bus::BroadcastResult(isDuringUndo, &AzToolsFramework::ToolsApplicationRequests::Bus::Events::IsDuringUndoRedo);
if (isDuringUndo)
{
keyHandle.SetKey(&discreteFloatKey);
}
else
{
AzToolsFramework::ScopedUndoBatch undoBatch("Set Key Value");
keyHandle.SetKey(&discreteFloatKey);
undoBatch.MarkEntityDirty(sequence->GetSequenceComponentEntityId());
}
}
}
}
REGISTER_QT_CLASS_DESC(CGotoKeyUIControls, "TrackView.KeyUI.Goto", "TrackViewKeyUI");
@@ -0,0 +1,170 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "EditorDefs.h"
// CryCommon
#include <CryCommon/Maestro/Types/AnimParamType.h>
// Editor
#include "TrackViewKeyPropertiesDlg.h"
//-----------------------------------------------------------------------------
//!
class CScreenFaderKeyUIControls
: public CTrackViewKeyUIControls
{
CSmartVariableArray mv_table;
CSmartVariable<float> mv_fadeTime;
CSmartVariable<Vec3> mv_fadeColor;
CSmartVariable<QString> mv_strTexture;
CSmartVariable<bool> mv_bUseCurColor;
CSmartVariableEnum<int> mv_fadeType;
CSmartVariableEnum<int> mv_fadechangeType;
public:
//-----------------------------------------------------------------------------
//!
virtual bool SupportTrackType(const CAnimParamType& paramType, [[maybe_unused]] EAnimCurveType trackType, [[maybe_unused]] AnimValueType valueType) const
{
return paramType == AnimParamType::ScreenFader;
}
//-----------------------------------------------------------------------------
//!
virtual void OnCreateVars()
{
AddVariable(mv_table, "Key Properties");
mv_fadeType->SetEnumList(NULL);
mv_fadeType->AddEnumItem("FadeIn", IScreenFaderKey::eFT_FadeIn);
mv_fadeType->AddEnumItem("FadeOut", IScreenFaderKey::eFT_FadeOut);
AddVariable(mv_table, mv_fadeType, "Type");
mv_fadechangeType->SetEnumList(NULL);
mv_fadechangeType->AddEnumItem("Linear", IScreenFaderKey::eFCT_Linear);
mv_fadechangeType->AddEnumItem("Square", IScreenFaderKey::eFCT_Square);
mv_fadechangeType->AddEnumItem("Cubic Square", IScreenFaderKey::eFCT_CubicSquare);
mv_fadechangeType->AddEnumItem("Square Root", IScreenFaderKey::eFCT_SquareRoot);
mv_fadechangeType->AddEnumItem("Sin", IScreenFaderKey::eFCT_Sin);
AddVariable(mv_table, mv_fadechangeType, "ChangeType");
AddVariable(mv_table, mv_fadeColor, "Color", IVariable::DT_COLOR);
mv_fadeTime->SetLimits(0.f, 100.f);
AddVariable(mv_table, mv_fadeTime, "Duration");
AddVariable(mv_table, mv_strTexture, "Texture", IVariable::DT_TEXTURE);
AddVariable(mv_table, mv_bUseCurColor, "Use Current Color");
}
//-----------------------------------------------------------------------------
//!
virtual bool OnKeySelectionChange(CTrackViewKeyBundle& keys);
//-----------------------------------------------------------------------------
//!
virtual void OnUIChange(IVariable* pVar, CTrackViewKeyBundle& keys);
virtual unsigned int GetPriority() const { return 1; }
static const GUID& GetClassID()
{
// {FBBC2407-C36B-45b2-9A54-0CF9CD3908FD}
static const GUID guid =
{
0xfbbc2407, 0xc36b, 0x45b2, { 0x9a, 0x54, 0xc, 0xf9, 0xcd, 0x39, 0x8, 0xfd }
};
return guid;
}
};
//-----------------------------------------------------------------------------
bool CScreenFaderKeyUIControls::OnKeySelectionChange(CTrackViewKeyBundle& keys)
{
if (!keys.AreAllKeysOfSameType())
{
return false;
}
bool bAssigned = false;
if (keys.GetKeyCount() == 1)
{
const CTrackViewKeyHandle& keyHandle = keys.GetKey(0);
CAnimParamType paramType = keyHandle.GetTrack()->GetParameterType();
if (paramType == AnimParamType::ScreenFader)
{
IScreenFaderKey screenFaderKey;
keyHandle.GetKey(&screenFaderKey);
mv_fadeTime = screenFaderKey.m_fadeTime;
mv_fadeColor = Vec3(screenFaderKey.m_fadeColor.GetR(), screenFaderKey.m_fadeColor.GetG(), screenFaderKey.m_fadeColor.GetB());
mv_strTexture = screenFaderKey.m_strTexture.c_str();
mv_bUseCurColor = screenFaderKey.m_bUseCurColor;
mv_fadeType = (int)screenFaderKey.m_fadeType;
mv_fadechangeType = (int)screenFaderKey.m_fadeChangeType;
bAssigned = true;
}
}
return bAssigned;
}
//-----------------------------------------------------------------------------
void CScreenFaderKeyUIControls::OnUIChange(IVariable* pVar, CTrackViewKeyBundle& selectedKeys)
{
if (!selectedKeys.AreAllKeysOfSameType())
{
return;
}
for (size_t keyIndex = 0, num = selectedKeys.GetKeyCount(); keyIndex < num; ++keyIndex)
{
CTrackViewKeyHandle selectedKey = selectedKeys.GetKey(keyIndex);
CAnimParamType paramType = selectedKey.GetTrack()->GetParameterType();
if (paramType == AnimParamType::ScreenFader)
{
IScreenFaderKey screenFaderKey;
selectedKey.GetKey(&screenFaderKey);
SyncValue(mv_fadeTime, screenFaderKey.m_fadeTime, false, pVar);
SyncValue(mv_bUseCurColor, screenFaderKey.m_bUseCurColor, false, pVar);
if (pVar == mv_fadeTime.GetVar())
{
screenFaderKey.m_fadeTime = MAX((float)mv_fadeTime, 0.f);
}
else if (pVar == mv_strTexture.GetVar())
{
QString sTexture = mv_strTexture;
screenFaderKey.m_strTexture = sTexture.toUtf8().data();
}
else if (pVar == mv_fadeType.GetVar())
{
screenFaderKey.m_fadeType = IScreenFaderKey::EFadeType((int)mv_fadeType);
}
else if (pVar == mv_fadechangeType.GetVar())
{
screenFaderKey.m_fadeChangeType = IScreenFaderKey::EFadeChangeType((int)mv_fadechangeType);
}
else if (pVar == mv_fadeColor.GetVar())
{
Vec3 color = mv_fadeColor;
screenFaderKey.m_fadeColor = AZ::Color(color.x, color.y, color.z, screenFaderKey.m_fadeType == IScreenFaderKey::eFT_FadeIn ? 1.f : 0.f);
}
selectedKey.SetKey(&screenFaderKey);
}
}
}
REGISTER_QT_CLASS_DESC(CScreenFaderKeyUIControls, "TrackView.KeyUI.ScreenFader", "TrackViewKeyUI");
@@ -0,0 +1,272 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "EditorDefs.h"
#include "TrackViewKeyPropertiesDlg.h" // for CTrackViewKeyUIControls
#include <AzCore/Component/EntityBus.h> // for AZ::EntitySystemBus
#include <AzFramework/Components/CameraBus.h> // for Camera::CameraNotificationBus
#include <CryCommon/Maestro/Types/AnimValueType.h> // for AnimValueType
//////////////////////////////////////////////////////////////////////////
class CSelectKeyUIControls
: public CTrackViewKeyUIControls
, protected Camera::CameraNotificationBus::Handler
, protected AZ::EntitySystemBus::Handler
{
public:
CSelectKeyUIControls() {}
~CSelectKeyUIControls() override;
CSmartVariableArray mv_table;
CSmartVariableEnum<QString> mv_camera;
CSmartVariable<float> mv_BlendTime;
virtual void OnCreateVars()
{
AddVariable(mv_table, "Key Properties");
AddVariable(mv_table, mv_camera, "Camera");
AddVariable(mv_table, mv_BlendTime, "Blend time");
Camera::CameraNotificationBus::Handler::BusConnect();
AZ::EntitySystemBus::Handler::BusConnect();
}
bool SupportTrackType([[maybe_unused]] const CAnimParamType& paramType, [[maybe_unused]] EAnimCurveType trackType, AnimValueType valueType) const
{
return valueType == AnimValueType::Select;
}
virtual bool OnKeySelectionChange(CTrackViewKeyBundle& selectedKeys);
virtual void OnUIChange(IVariable* pVar, CTrackViewKeyBundle& selectedKeys);
virtual unsigned int GetPriority() const { return 1; }
static const GUID& GetClassID()
{
// {9018D0D1-24CC-45e5-9D3D-16D3F9E591B2}
static const GUID guid =
{
0x9018d0d1, 0x24cc, 0x45e5, { 0x9d, 0x3d, 0x16, 0xd3, 0xf9, 0xe5, 0x91, 0xb2 }
};
return guid;
}
protected:
////////////////////////////////////////////////////////////////////////
// CameraNotificationBus interface implementation
void OnCameraAdded(const AZ::EntityId& cameraId) override;
void OnCameraRemoved(const AZ::EntityId& cameraId) override;
//////////////////////////////////////////////////////////////////////////
// AZ::EntitySystemBus::Handler
void OnEntityNameChanged(const AZ::EntityId& entityId, const AZStd::string& name) override;
private:
void ResetCameraEntries();
};
CSelectKeyUIControls::~CSelectKeyUIControls()
{
AZ::EntitySystemBus::Handler::BusDisconnect();
Camera::CameraNotificationBus::Handler::BusDisconnect();
}
//////////////////////////////////////////////////////////////////////////
bool CSelectKeyUIControls::OnKeySelectionChange(CTrackViewKeyBundle& selectedKeys)
{
if (!selectedKeys.AreAllKeysOfSameType())
{
return false;
}
bool bAssigned = false;
if (selectedKeys.GetKeyCount() == 1)
{
const CTrackViewKeyHandle& keyHandle = selectedKeys.GetKey(0);
AnimValueType valueType = keyHandle.GetTrack()->GetValueType();
if (valueType == AnimValueType::Select)
{
ResetCameraEntries();
// Get All cameras.
mv_camera.SetEnumList(NULL);
mv_camera->AddEnumItem(QObject::tr("<None>"), QString::number(static_cast<AZ::u64>(AZ::EntityId::InvalidEntityId)));
// Find all Component Entity Cameras
AZ::EBusAggregateResults<AZ::EntityId> cameraComponentEntities;
Camera::CameraBus::BroadcastResult(cameraComponentEntities, &Camera::CameraRequests::GetCameras);
// add names of all found entities with Camera Components
for (int i = 0; i < cameraComponentEntities.values.size(); i++)
{
AZ::Entity* entity = nullptr;
AZ::ComponentApplicationBus::BroadcastResult(entity, &AZ::ComponentApplicationBus::Events::FindEntity, cameraComponentEntities.values[i]);
if (entity)
{
// For Camera Components the enum value is the stringified AZ::EntityId of the entity with the Camera Component
QString entityIdString = QString::number(static_cast<AZ::u64>(entity->GetId()));
mv_camera->AddEnumItem(entity->GetName().c_str(), entityIdString);
}
}
ISelectKey selectKey;
keyHandle.GetKey(&selectKey);
mv_camera = QString::number(static_cast<AZ::u64>(selectKey.cameraAzEntityId));
mv_BlendTime.GetVar()->SetLimits(0.0f, selectKey.fDuration > .0f ? selectKey.fDuration : 1.0f, 0.1f, true, false);
mv_BlendTime = selectKey.fBlendTime;
bAssigned = true;
}
}
return bAssigned;
}
// Called when UI variable changes.
void CSelectKeyUIControls::OnUIChange(IVariable* pVar, CTrackViewKeyBundle& selectedKeys)
{
CTrackViewSequence* sequence = GetIEditor()->GetAnimation()->GetSequence();
if (!sequence || !selectedKeys.AreAllKeysOfSameType())
{
return;
}
for (unsigned int keyIndex = 0; keyIndex < selectedKeys.GetKeyCount(); ++keyIndex)
{
CTrackViewKeyHandle keyHandle = selectedKeys.GetKey(keyIndex);
AnimValueType valueType = keyHandle.GetTrack()->GetValueType();
if (valueType == AnimValueType::Select)
{
ISelectKey selectKey;
keyHandle.GetKey(&selectKey);
if (pVar == mv_camera.GetVar())
{
QString entityIdString = mv_camera;
selectKey.cameraAzEntityId = AZ::EntityId(entityIdString.toULongLong());
selectKey.szSelection = mv_camera.GetVar()->GetDisplayValue().toUtf8().data();
}
if (pVar == mv_BlendTime.GetVar())
{
if (mv_BlendTime < 0.0f)
{
mv_BlendTime = 0.0f;
}
selectKey.fBlendTime = mv_BlendTime;
}
if (!selectKey.szSelection.empty())
{
IAnimSequence* pSequence = GetIEditor()->GetSystem()->GetIMovieSystem()->FindLegacySequenceByName(selectKey.szSelection.c_str());
if (pSequence)
{
selectKey.fDuration = pSequence->GetTimeRange().Length();
}
}
bool isDuringUndo = false;
AzToolsFramework::ToolsApplicationRequests::Bus::BroadcastResult(isDuringUndo, &AzToolsFramework::ToolsApplicationRequests::Bus::Events::IsDuringUndoRedo);
if (isDuringUndo)
{
keyHandle.SetKey(&selectKey);
}
else
{
AzToolsFramework::ScopedUndoBatch undoBatch("Set Key Value");
keyHandle.SetKey(&selectKey);
undoBatch.MarkEntityDirty(sequence->GetSequenceComponentEntityId());
}
}
}
}
void CSelectKeyUIControls::OnCameraAdded(const AZ::EntityId & cameraId)
{
// Add a single camera component
AZ::Entity* entity = nullptr;
AZ::ComponentApplicationBus::BroadcastResult(entity, &AZ::ComponentApplicationBus::Events::FindEntity, cameraId);
if (entity)
{
// For Camera Components the enum value is the stringified AZ::EntityId of the entity with the Camera Component
QString entityIdString = QString::number(static_cast<AZ::u64>(entity->GetId()));
mv_camera->AddEnumItem(entity->GetName().c_str(), entityIdString);
}
}
void CSelectKeyUIControls::OnCameraRemoved(const AZ::EntityId & cameraId)
{
mv_camera->EnableUpdateCallbacks(false);
// We can't iterate or remove an item from the enum list, and Camera::CameraRequests::GetCameras
// still includes the deleted camera at this point. Reset the list anyway and filter out the
// deleted camera.
mv_camera->SetEnumList(NULL);
mv_camera->AddEnumItem(QObject::tr("<None>"), QString::number(static_cast<AZ::u64>(AZ::EntityId::InvalidEntityId)));
AZ::EBusAggregateResults<AZ::EntityId> cameraComponentEntities;
Camera::CameraBus::BroadcastResult(cameraComponentEntities, &Camera::CameraRequests::GetCameras);
for (int i = 0; i < cameraComponentEntities.values.size(); i++)
{
if (cameraId == cameraComponentEntities.values[i])
{
continue;
}
OnCameraAdded(cameraComponentEntities.values[i]);
}
mv_camera->EnableUpdateCallbacks(true);
}
void CSelectKeyUIControls::OnEntityNameChanged(const AZ::EntityId & entityId, [[maybe_unused]] const AZStd::string & name)
{
AZ::Entity* entity = nullptr;
AZ::ComponentApplicationBus::BroadcastResult(entity, &AZ::ComponentApplicationBus::Events::FindEntity, entityId);
if (entity == nullptr)
{
return;
}
AZ::Entity::ComponentArrayType cameraComponents = entity->FindComponents(EditorCameraComponentTypeId);
if (cameraComponents.size() == 0)
{
return;
}
mv_camera->EnableUpdateCallbacks(false);
ResetCameraEntries();
mv_camera->EnableUpdateCallbacks(true);
}
void CSelectKeyUIControls::ResetCameraEntries()
{
mv_camera.SetEnumList(NULL);
mv_camera->AddEnumItem(QObject::tr("<None>"), QString::number(static_cast<AZ::u64>(AZ::EntityId::InvalidEntityId)));
// Find all Component Entity Cameras
AZ::EBusAggregateResults<AZ::EntityId> cameraComponentEntities;
Camera::CameraBus::BroadcastResult(cameraComponentEntities, &Camera::CameraRequests::GetCameras);
// add names of all found entities with Camera Components
for (int i = 0; i < cameraComponentEntities.values.size(); i++)
{
OnCameraAdded(cameraComponentEntities.values[i]);
}
}
REGISTER_QT_CLASS_DESC(CSelectKeyUIControls, "TrackView.KeyUI.Select", "TrackViewKeyUI");
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,226 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
// Description : A dialog for batch-rendering sequences
#pragma once
#include "AtomOutputFrameCapture.h"
#include <AzFramework/StringFunc/StringFunc.h>
#include <QDialog>
#include <QTimer>
#include <QFutureWatcher>
#include <QValidator>
class QStringListModel;
namespace Ui
{
class SequenceBatchRenderDialog;
}
class CSequenceBatchRenderDialog
: public QDialog
, public IMovieListener
{
public:
CSequenceBatchRenderDialog(float fps, QWidget* pParent = nullptr);
virtual ~CSequenceBatchRenderDialog();
void reject() override; // overriding so Qt doesn't cancel
protected:
void OnInitDialog();
void OnAddRenderItem();
void OnRemoveRenderItem();
void OnClearRenderItems();
void OnUpdateRenderItem();
void OnLoadPreset();
void OnSavePreset();
void OnGo();
void OnDone();
void OnSequenceSelected();
void OnRenderItemSelChange();
void OnFPSEditChange();
void OnFPSChange(int itemIndex);
void OnImageFormatChange();
void OnResolutionSelected();
void OnStartFrameChange();
void OnEndFrameChange();
void OnLoadBatch();
void OnSaveBatch();
void OnKickIdle();
void OnCancelRender();
void SaveOutputOptions(const QString& pathname) const;
bool LoadOutputOptions(const QString& pathname);
QString m_ffmpegPluginStatusMsg;
bool m_bFFMPEGCommandAvailable;
float m_fpsForTimeToFrameConversion; // FPS setting in TrackView
struct SRenderItem
{
IAnimSequence* pSequence;
IAnimNode* pDirectorNode;
Range frameRange;
int resW, resH;
int fps;
QString folder;
QString prefix;
QStringList cvars;
bool disableDebugInfo;
bool bCreateVideo;
SRenderItem()
: pSequence(NULL)
, pDirectorNode(NULL)
, disableDebugInfo(false)
, bCreateVideo(false) {}
bool operator==(const SRenderItem& item)
{
if (pSequence == item.pSequence
&& pDirectorNode == item.pDirectorNode
&& frameRange == item.frameRange
&& resW == item.resW && resH == item.resH
&& fps == item.fps
&& folder == item.folder
&& prefix == item.prefix
&& cvars == item.cvars
&& disableDebugInfo == item.disableDebugInfo
&& bCreateVideo == item.bCreateVideo)
{
return true;
}
else
{
return false;
}
}
};
std::vector<SRenderItem> m_renderItems;
// Capture States
enum class CaptureState
{
Idle,
WarmingUpAfterResChange,
EnteringGameMode,
BeginPlayingSequence,
Capturing,
End,
FFMPEGProcessing,
Finalize
};
struct SRenderContext
{
int currentItemIndex;
float expectedTotalTime;
float spentTime;
int flagBU;
Range rangeBU;
int cvarCustomResWidthBU, cvarCustomResHeightBU;
int cvarDisplayInfoBU;
int framesSpentInCurrentPhase;
IAnimNode* pActiveDirectorBU;
ICaptureKey captureOptions;
bool processingFFMPEG;
// Signals when an mpeg is finished being processed.
QFutureWatcher<void> processingFFMPEGWatcher;
// True if the user canceled a render.
bool canceled;
// The sequence that triggered the CaptureState::Ending.
IAnimSequence* endingSequence;
// Current capture state.
CaptureState captureState;
// Is an individual frame currently being captured.
bool capturingFrame;
// Current frame being captured
int frameNumber;
bool IsInRendering() const
{ return currentItemIndex >= 0; }
SRenderContext()
: currentItemIndex(-1)
, expectedTotalTime(0)
, spentTime(0)
, flagBU(0)
, pActiveDirectorBU(NULL)
, cvarCustomResWidthBU(0)
, cvarCustomResHeightBU(0)
, cvarDisplayInfoBU(0)
, framesSpentInCurrentPhase(0)
, processingFFMPEG(false)
, canceled(false)
, endingSequence(nullptr)
, captureState(CaptureState::Idle)
, capturingFrame(false)
, frameNumber(0) {}
};
SRenderContext m_renderContext;
// Custom validator to make sure the prefix is a valid part of a filename.
class CPrefixValidator : public QValidator
{
public:
CPrefixValidator(QObject* parent) : QValidator(parent) {}
QValidator::State validate(QString& input, [[maybe_unused]] int& pos) const override
{
bool valid = input.isEmpty() || AzFramework::StringFunc::Path::IsValid(input.toUtf8().data());
return valid ? QValidator::State::Acceptable : QValidator::State::Invalid;
}
};
// Custom values from resolution/FPS combo boxes
int m_customResW, m_customResH;
int m_customFPS;
void InitializeContext();
virtual void OnMovieEvent(IMovieListener::EMovieEvent event, IAnimSequence* pSequence);
void CaptureItemStart();
// Capture State Updates
void OnUpdateWarmingUpAfterResChange();
void OnUpdateEnteringGameMode();
void OnUpdateBeginPlayingSequence();
void OnUpdateCapturing();
void OnUpdateEnd(IAnimSequence* pSequence);
void OnUpdateFFMPEGProcessing();
void OnUpdateFinalize();
bool SetUpNewRenderItem(SRenderItem& item);
void AddItem(const SRenderItem& item);
QString GetCaptureItemString(const SRenderItem& item) const;
protected slots:
void OnKickIdleTimout();
bool GetResolutionFromCustomResText(const char* customResText, int& retCustomWidth, int& retCustomHeight) const;
private:
void CheckForEnableUpdateButton();
void stashActiveViewportResolution();
void UpdateSpinnerProgressMessage(const char* description);
void EnterCaptureState(CaptureState captureState);
void SetEnableEditorIdleProcessing(bool enabled);
QScopedPointer<Ui::SequenceBatchRenderDialog> m_ui;
QStringListModel* m_renderListModel;
QTimer m_renderTimer;
bool m_editorIdleProcessingEnabled;
int32 CV_TrackViewRenderOutputCapturing;
QScopedPointer<CPrefixValidator> m_prefixValidator;
TrackView::AtomOutputFrameCapture m_atomOutputFrameCapture;
};
@@ -0,0 +1,406 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>SequenceBatchRenderDialog</class>
<widget class="QDialog" name="SequenceBatchRenderDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>572</width>
<height>614</height>
</rect>
</property>
<property name="windowTitle">
<string>Render Output</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="QGroupBox" name="BATCH_RENDER_INPUT_GROUP_BOX">
<property name="title">
<string>Input</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="1" column="5" colspan="2">
<widget class="QLabel" name="BATCH_RENDER_FRAME_IN_FPS">
<property name="text">
<string>In 30 FPS</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Sequence:</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
</widget>
</item>
<item row="1" column="3">
<widget class="QLabel" name="label_2">
<property name="text">
<string>End frame:</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
</widget>
</item>
<item row="0" column="7">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Director:</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
</widget>
</item>
<item row="0" column="8">
<widget class="QComboBox" name="m_shotCombo"/>
</item>
<item row="1" column="7" colspan="2">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="2">
<widget class="QSpinBox" name="m_startFrame"/>
</item>
<item row="0" column="2" colspan="5">
<widget class="QComboBox" name="m_sequenceCombo"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Start frame:</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
</widget>
</item>
<item row="1" column="4">
<widget class="QSpinBox" name="m_endFrame"/>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="BATCH_RENDER_OUTPUT_GROUP_BOX">
<property name="title">
<string>Output</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QLabel" name="label_5">
<property name="text">
<string>Resolution:</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="m_resolutionCombo"/>
</item>
<item>
<widget class="QLabel" name="label_6">
<property name="text">
<string>FPS:</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="m_fpsCombo">
<property name="editable">
<bool>true</bool>
</property>
<property name="insertPolicy">
<enum>QComboBox::NoInsert</enum>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QGroupBox" name="groupBox_4">
<property name="title">
<string>Capture Options</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="3" column="0" colspan="3">
<widget class="QCheckBox" name="m_createVideoCheckBox">
<property name="text">
<string>Create a video (mp4)</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
<string>File prefix:</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
</widget>
</item>
<item row="1" column="1" colspan="2">
<widget class="QLineEdit" name="BATCH_RENDER_FILE_PREFIX">
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Format:</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
</widget>
</item>
<item row="2" column="0" colspan="3">
<widget class="QCheckBox" name="m_disableDebugInfoCheckBox">
<property name="text">
<string>Disable Debug Info</string>
</property>
</widget>
</item>
<item row="0" column="1" colspan="2">
<widget class="QComboBox" name="m_imageFormatCombo">
<property name="sizeAdjustPolicy">
<enum>QComboBox::AdjustToContentsOnFirstShow</enum>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_5">
<property name="title">
<string>Custom Configs</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QLabel" name="label_10">
<property name="text">
<string>Input cvars for further customization:</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
</widget>
</item>
<item>
<widget class="QTextEdit" name="m_cvarsEdit">
<property name="html">
<string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:4.125pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8.25pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="acceptRichText">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="label_11">
<property name="text">
<string>Destination:</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="m_destinationEdit">
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="BATCH_RENDER_LOAD_PRESET">
<property name="text">
<string>Load Preset...</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="BATCH_RENDER_SAVE_PRESET">
<property name="text">
<string>Save Preset...</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="BATCH_RENDER_LIST_GROUP_BOX">
<property name="title">
<string>Batch</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QListView" name="m_renderList"/>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>5</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<item>
<widget class="QPushButton" name="BATCH_RENDER_ADD_SEQ">
<property name="text">
<string>Add</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="BATCH_RENDER_REMOVE_SEQ">
<property name="text">
<string>Remove</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="BATCH_RENDER_CLEAR_SEQ">
<property name="text">
<string>Clear</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="m_updateBtn">
<property name="text">
<string>Update</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QPushButton" name="BATCH_RENDER_LOAD_BATCH">
<property name="text">
<string>Load Batch...</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="BATCH_RENDER_SAVE_BATCH">
<property name="text">
<string>Save Batch...</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QProgressBar" name="m_progressBar">
<property name="textVisible">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="m_progressStatusMsg">
<property name="text">
<string>Static</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4" stretch="1,0,0">
<item>
<widget class="QLabel" name="BATCH_RENDER_PRESS_ESC_TO_CANCEL">
<property name="text">
<string>Press ESC to cancel</string>
</property>
<property name="alignment">
<set>Qt::AlignHCenter|Qt::AlignTop</set>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="m_pGoBtn">
<property name="text">
<string>Start</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="CANCEL">
<property name="text">
<string>Done</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,218 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "EditorDefs.h"
// CryCommon
#include <CryCommon/Maestro/Types/AnimParamType.h>
// Editor
#include "TrackViewDialog.h"
//////////////////////////////////////////////////////////////////////////
class CSequenceKeyUIControls
: public CTrackViewKeyUIControls
{
public:
CSmartVariableArray mv_table;
CSmartVariableEnum<QString> mv_sequence;
CSmartVariable<bool> mv_overrideTimes;
CSmartVariable<float> mv_startTime;
CSmartVariable<float> mv_endTime;
virtual void OnCreateVars()
{
AddVariable(mv_table, "Key Properties");
AddVariable(mv_table, mv_sequence, "Sequence");
AddVariable(mv_table, mv_overrideTimes, "Override Start/End Times");
AddVariable(mv_table, mv_startTime, "Start Time");
AddVariable(mv_table, mv_endTime, "End Time");
}
bool SupportTrackType(const CAnimParamType& paramType, [[maybe_unused]] EAnimCurveType trackType, [[maybe_unused]] AnimValueType valueType) const
{
return paramType == AnimParamType::Sequence;
}
virtual bool OnKeySelectionChange(CTrackViewKeyBundle& selectedKeys);
virtual void OnUIChange(IVariable* pVar, CTrackViewKeyBundle& selectedKeys);
virtual unsigned int GetPriority() const { return 1; }
static const GUID& GetClassID()
{
// {68030C46-1402-45d1-91B3-8EC6F29C0FED}
static const GUID guid =
{
0x68030c46, 0x1402, 0x45d1, { 0x91, 0xb3, 0x8e, 0xc6, 0xf2, 0x9c, 0xf, 0xed }
};
return guid;
}
private:
bool m_skipOnUIChange = false;
};
//////////////////////////////////////////////////////////////////////////
bool CSequenceKeyUIControls::OnKeySelectionChange(CTrackViewKeyBundle& selectedKeys)
{
if (!selectedKeys.AreAllKeysOfSameType())
{
return false;
}
bool bAssigned = false;
if (selectedKeys.GetKeyCount() == 1)
{
const CTrackViewKeyHandle& keyHandle = selectedKeys.GetKey(0);
CAnimParamType paramType = keyHandle.GetTrack()->GetParameterType();
if (paramType == AnimParamType::Sequence)
{
CTrackViewSequence* pSequence = GetIEditor()->GetAnimation()->GetSequence();
/////////////////////////////////////////////////////////////////////////////////
// fill sequence comboBox with available sequences
mv_sequence.SetEnumList(NULL);
// Insert '<None>' empty enum
mv_sequence->AddEnumItem(QObject::tr("<None>"), CTrackViewDialog::GetEntityIdAsString(AZ::EntityId(AZ::EntityId::InvalidEntityId)));
const CTrackViewSequenceManager* pSequenceManager = GetIEditor()->GetSequenceManager();
for (int i = 0; i < pSequenceManager->GetCount(); ++i)
{
CTrackViewSequence* pCurrentSequence = pSequenceManager->GetSequenceByIndex(i);
bool bNotMe = pCurrentSequence != pSequence;
bool bNotParent = !bNotMe || pCurrentSequence->IsAncestorOf(pSequence) == false;
if (bNotMe && bNotParent)
{
string seqName = pCurrentSequence->GetName();
QString ownerIdString = CTrackViewDialog::GetEntityIdAsString(pCurrentSequence->GetSequenceComponentEntityId());
mv_sequence->AddEnumItem(seqName.c_str(), ownerIdString);
}
}
/////////////////////////////////////////////////////////////////////////////////
// fill Key Properties with selected key values
ISequenceKey sequenceKey;
keyHandle.GetKey(&sequenceKey);
QString entityIdString = CTrackViewDialog::GetEntityIdAsString((sequenceKey.sequenceEntityId));
mv_sequence = entityIdString;
mv_overrideTimes = sequenceKey.bOverrideTimes;
if (!sequenceKey.bOverrideTimes)
{
IAnimSequence* pSequence2 = GetIEditor()->GetSystem()->GetIMovieSystem()->FindSequence(sequenceKey.sequenceEntityId);
if (pSequence2)
{
sequenceKey.fStartTime = pSequence2->GetTimeRange().start;
sequenceKey.fEndTime = pSequence2->GetTimeRange().end;
}
else
{
sequenceKey.fStartTime = 0.0f;
sequenceKey.fEndTime = 0.0f;
}
}
// Don't trigger an OnUIChange event, since this code is the one
// updating the start/end ui elements, not the user setting new values.
m_skipOnUIChange = true;
mv_startTime = sequenceKey.fStartTime;
mv_endTime = sequenceKey.fEndTime;
m_skipOnUIChange = false;
bAssigned = true;
}
}
return bAssigned;
}
// Called when UI variable changes.
void CSequenceKeyUIControls::OnUIChange(IVariable* pVar, CTrackViewKeyBundle& selectedKeys)
{
CTrackViewSequence* sequence = GetIEditor()->GetAnimation()->GetSequence();
if (!sequence || !selectedKeys.AreAllKeysOfSameType() || m_skipOnUIChange)
{
return;
}
for (unsigned int keyIndex = 0; keyIndex < selectedKeys.GetKeyCount(); ++keyIndex)
{
CTrackViewKeyHandle keyHandle = selectedKeys.GetKey(keyIndex);
CAnimParamType paramType = keyHandle.GetTrack()->GetParameterType();
if (paramType == AnimParamType::Sequence)
{
ISequenceKey sequenceKey;
keyHandle.GetKey(&sequenceKey);
AZ::EntityId seqOwnerId;
if (pVar == mv_sequence.GetVar())
{
QString entityIdString = mv_sequence;
seqOwnerId = AZ::EntityId(static_cast<AZ::u64>(entityIdString.toULongLong()));
sequenceKey.szSelection.clear(); // clear deprecated legacy data
sequenceKey.sequenceEntityId = seqOwnerId;
}
SyncValue(mv_overrideTimes, sequenceKey.bOverrideTimes, false, pVar);
IAnimSequence* pSequence = GetIEditor()->GetSystem()->GetIMovieSystem()->FindSequence(seqOwnerId);
if (!sequenceKey.bOverrideTimes)
{
if (pSequence)
{
sequenceKey.fStartTime = pSequence->GetTimeRange().start;
sequenceKey.fEndTime = pSequence->GetTimeRange().end;
}
else
{
sequenceKey.fStartTime = 0.0f;
sequenceKey.fEndTime = 0.0f;
}
}
else
{
SyncValue(mv_startTime, sequenceKey.fStartTime, false, pVar);
SyncValue(mv_endTime, sequenceKey.fEndTime, false, pVar);
}
sequenceKey.fDuration = sequenceKey.fEndTime - sequenceKey.fStartTime > 0 ? sequenceKey.fEndTime - sequenceKey.fStartTime : 0.0f;
IMovieSystem* pMovieSystem = GetIEditor()->GetSystem()->GetIMovieSystem();
if (pMovieSystem != NULL)
{
pMovieSystem->SetStartEndTime(pSequence, sequenceKey.fStartTime, sequenceKey.fEndTime);
}
bool isDuringUndo = false;
AzToolsFramework::ToolsApplicationRequests::Bus::BroadcastResult(isDuringUndo, &AzToolsFramework::ToolsApplicationRequests::Bus::Events::IsDuringUndoRedo);
if (isDuringUndo)
{
keyHandle.SetKey(&sequenceKey);
}
else
{
AzToolsFramework::ScopedUndoBatch undoBatch("Set Key Value");
keyHandle.SetKey(&sequenceKey);
undoBatch.MarkEntityDirty(sequence->GetSequenceComponentEntityId());
}
}
}
}
REGISTER_QT_CLASS_DESC(CSequenceKeyUIControls, "TrackView.KeyUI.Sequence", "TrackViewKeyUI");
@@ -0,0 +1,133 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "EditorDefs.h"
// CryCommon
#include <CryCommon/Maestro/Types/AnimParamType.h> // for AnimParamType
// Editor
#include "TrackViewKeyPropertiesDlg.h" // for CTrackViewKeyUIControls
//////////////////////////////////////////////////////////////////////////
class CSoundKeyUIControls
: public CTrackViewKeyUIControls
{
public:
CSmartVariableArray mv_table;
CSmartVariableArray mv_options;
CSmartVariable<QString> mv_startTrigger;
CSmartVariable<QString> mv_stopTrigger;
CSmartVariable<float> mv_duration;
CSmartVariable<Vec3> mv_customColor;
virtual void OnCreateVars()
{
AddVariable(mv_table, "Key Properties");
AddVariable(mv_table, mv_startTrigger, "StartTrigger", IVariable::DT_AUDIO_TRIGGER);
AddVariable(mv_table, mv_stopTrigger, "StopTrigger", IVariable::DT_AUDIO_TRIGGER);
AddVariable(mv_table, mv_duration, "Duration");
AddVariable(mv_options, "Options");
AddVariable(mv_options, mv_customColor, "Custom Color", IVariable::DT_COLOR);
}
bool SupportTrackType(const CAnimParamType& paramType, [[maybe_unused]] EAnimCurveType trackType, [[maybe_unused]] AnimValueType valueType) const
{
return paramType == AnimParamType::Sound;
}
virtual bool OnKeySelectionChange(CTrackViewKeyBundle& selectedKeys);
virtual void OnUIChange(IVariable* pVar, CTrackViewKeyBundle& selectedKeys);
virtual unsigned int GetPriority() const { return 1; }
static const GUID& GetClassID()
{
// {AB2226E5-D593-49d2-B7CB-989412CAAEDE}
static const GUID guid =
{
0xab2226e5, 0xd593, 0x49d2, { 0xb7, 0xcb, 0x98, 0x94, 0x12, 0xca, 0xae, 0xde }
};
return guid;
}
};
//////////////////////////////////////////////////////////////////////////
bool CSoundKeyUIControls::OnKeySelectionChange(CTrackViewKeyBundle& selectedKeys)
{
if (!selectedKeys.AreAllKeysOfSameType())
{
return false;
}
bool bAssigned = false;
if (selectedKeys.GetKeyCount() == 1)
{
const CTrackViewKeyHandle& keyHandle = selectedKeys.GetKey(0);
CAnimParamType paramType = keyHandle.GetTrack()->GetParameterType();
if (paramType == AnimParamType::Sound)
{
ISoundKey soundKey;
keyHandle.GetKey(&soundKey);
mv_startTrigger = soundKey.sStartTrigger.c_str();
mv_stopTrigger = soundKey.sStopTrigger.c_str();
mv_duration = soundKey.fDuration;
mv_customColor = soundKey.customColor;
bAssigned = true;
}
}
return bAssigned;
}
// Called when UI variable changes.
void CSoundKeyUIControls::OnUIChange(IVariable* pVar, CTrackViewKeyBundle& selectedKeys)
{
CTrackViewSequence* pSequence = GetIEditor()->GetAnimation()->GetSequence();
if (!pSequence || !selectedKeys.AreAllKeysOfSameType())
{
return;
}
for (unsigned int keyIndex = 0; keyIndex < selectedKeys.GetKeyCount(); ++keyIndex)
{
CTrackViewKeyHandle keyHandle = selectedKeys.GetKey(keyIndex);
CAnimParamType paramType = keyHandle.GetTrack()->GetParameterType();
if (paramType == AnimParamType::Sound)
{
ISoundKey soundKey;
keyHandle.GetKey(&soundKey);
bool bChangedSoundFile = false;
if (pVar == mv_startTrigger.GetVar())
{
QString sFilename = mv_startTrigger;
bChangedSoundFile = sFilename != soundKey.sStartTrigger.c_str();
soundKey.sStartTrigger = sFilename.toUtf8().data();
}
else if (pVar == mv_stopTrigger.GetVar())
{
QString sFilename = mv_stopTrigger;
bChangedSoundFile = sFilename != soundKey.sStopTrigger.c_str();
soundKey.sStopTrigger = sFilename.toUtf8().data();
}
SyncValue(mv_duration, soundKey.fDuration, false, pVar);
SyncValue(mv_customColor, soundKey.customColor, false, pVar);
keyHandle.SetKey(&soundKey);
}
}
}
REGISTER_QT_CLASS_DESC(CSoundKeyUIControls, "TrackView.KeyUI.Sound", "TrackViewKeyUI");
@@ -0,0 +1,117 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>TVCustomizeTrackColorsDialog</class>
<widget class="QDialog" name="TVCustomizeTrackColorsDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>555</width>
<height>100</height>
</rect>
</property>
<property name="windowTitle">
<string>Customize Track Colors</string>
</property>
<layout class="QGridLayout" name="gridLayout" columnstretch="1,0,0,0,0,0">
<item row="0" column="0" colspan="6">
<widget class="QWidget" name="frame" native="true"/>
</item>
<item row="1" column="0">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="3">
<widget class="QPushButton" name="buttonResetAll">
<property name="text">
<string>Reset All</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QPushButton" name="buttonExport">
<property name="text">
<string>Export</string>
</property>
</widget>
</item>
<item row="1" column="5">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QPushButton" name="buttonImport">
<property name="text">
<string>Import</string>
</property>
</widget>
</item>
<item row="1" column="4">
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>5</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>TVCustomizeTrackColorsDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>TVCustomizeTrackColorsDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>
@@ -0,0 +1,402 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
// Description : A dialog for customizing track colors
#include "EditorDefs.h"
#include "TVCustomizeTrackColorsDlg.h"
// Qt
#include <QLabel>
#include <QMessageBox>
#include <QSettings>
// CryCommon
#include <CryCommon/Maestro/Types/AnimParamType.h>
// Editor
#include "TrackViewDialog.h"
#include "QtUI/ColorButton.h"
AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
#include <TrackView/ui_TVCustomizeTrackColorsDialog.h>
AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING
#define TRACKCOLOR_ENTRY_PREFIX ("TrackColor")
#define TRACKCOLOR_FOR_OTHERS_ENTRY ("TrackColorForOthers")
#define TRACKCOLOR_FOR_DISABLED_ENTRY ("TrackColorForDisabled")
#define TRACKCOLOR_FOR_MUTED_ENTRY ("TrackColorForMuted")
struct STrackEntry
{
CAnimParamType paramType;
QString name;
QColor defaultColor;
};
namespace
{
const STrackEntry g_trackEntries[] = {
// Color for tracks
{ AnimParamType::FOV, "FOV", QColor(220, 220, 220) },
{ AnimParamType::Position, "Pos", QColor(90, 150, 90) },
{ AnimParamType::Rotation, "Rot", QColor(90, 150, 90) },
{ AnimParamType::Scale, "Scale", QColor(90, 150, 90) },
{ AnimParamType::Event, "Event", QColor(220, 220, 220) },
{ AnimParamType::Visibility, "Visibility", QColor(220, 220, 220) },
{ AnimParamType::Camera, "Camera", QColor(220, 220, 220) },
{ AnimParamType::Sound, "Sound", QColor(220, 220, 220) },
{ AnimParamType::Animation, "Animation", QColor(220, 220, 220) },
{ AnimParamType::Sequence, "Sequence", QColor(220, 220, 220) },
{ AnimParamType::Console, "Console", QColor(220, 220, 220) },
{ AnimParamType::LookAt, "LookAt", QColor(220, 220, 220) },
{ AnimParamType::TrackEvent, "TrackEvent", QColor(220, 220, 220) },
{ AnimParamType::ShakeMultiplier, "ShakeMult", QColor(90, 150, 90) },
{ AnimParamType::TransformNoise, "Noise", QColor(90, 150, 90) },
{ AnimParamType::TimeWarp, "Timewarp", QColor(220, 220, 220) },
{ AnimParamType::FixedTimeStep, "FixedTimeStep", QColor(220, 220, 220) },
{ AnimParamType::DepthOfField, "DepthOfField", QColor(90, 150, 90) },
{ AnimParamType::CommentText, "CommentText", QColor(220, 220, 220) },
{ AnimParamType::ScreenFader, "ScreenFader", QColor(220, 220, 220) },
{ AnimParamType::LightDiffuse, "LightDiffuseColor", QColor(90, 150, 90) },
{ AnimParamType::LightRadius, "LightRadius", QColor(220, 220, 220) },
{ AnimParamType::LightDiffuseMult, "LightDiffuseMult", QColor(220, 220, 220) },
{ AnimParamType::LightHDRDynamic, "LightHDRDynamic", QColor(220, 220, 220) },
{ AnimParamType::LightSpecularMult, "LightSpecularMult", QColor(220, 220, 220) },
{ AnimParamType::LightSpecPercentage, "LightSpecularPercent", QColor(220, 220, 220) },
{ AnimParamType::FocusDistance, "FocusDistance", QColor(220, 220, 220) },
{ AnimParamType::FocusRange, "FocusRange", QColor(220, 220, 220) },
{ AnimParamType::BlurAmount, "BlurAmount", QColor(220, 220, 220) },
{ AnimParamType::PositionX, "PosX", QColor(220, 220, 220) },
{ AnimParamType::PositionY, "PosY", QColor(220, 220, 220) },
{ AnimParamType::PositionZ, "PosZ", QColor(220, 220, 220) },
{ AnimParamType::RotationX, "RotX", QColor(220, 220, 220) },
{ AnimParamType::RotationY, "RotY", QColor(220, 220, 220) },
{ AnimParamType::RotationZ, "RotZ", QColor(220, 220, 220) },
{ AnimParamType::ScaleX, "ScaleX", QColor(220, 220, 220) },
{ AnimParamType::ScaleY, "ScaleY", QColor(220, 220, 220) },
{ AnimParamType::ScaleZ, "ScaleZ", QColor(220, 220, 220) },
{ AnimParamType::ShakeAmpAMult, "ShakeMultAmpA", QColor(220, 220, 220) },
{ AnimParamType::ShakeAmpBMult, "ShakeMultAmpB", QColor(220, 220, 220) },
{ AnimParamType::ShakeFreqAMult, "ShakeMultFreqA", QColor(220, 220, 220) },
{ AnimParamType::ShakeFreqBMult, "ShakeMultFreqB", QColor(220, 220, 220) },
{ AnimParamType::ColorR, "ColorR", QColor(220, 220, 220) },
{ AnimParamType::ColorG, "ColorG", QColor(220, 220, 220) },
{ AnimParamType::ColorB, "ColorB", QColor(220, 220, 220) },
{ AnimParamType::MaterialOpacity, "MaterialOpacity", QColor(220, 220, 220) },
{ AnimParamType::MaterialSmoothness, "MaterialGlossiness", QColor(220, 220, 220) },
{ AnimParamType::MaterialEmissive, "MaterialEmission", QColor(220, 220, 220) },
{ AnimParamType::MaterialEmissiveIntensity, "MaterialEmissionIntensity", QColor(220, 220, 220) },
{ AnimParamType::NearZ, "NearZ", QColor(220, 220, 220) },
{ AnimParamType::User, "", QColor(0, 0, 0) }, // An empty string means a separator row.
// Misc colors for special states of a track
{ AnimParamType::User, "Others", QColor(220, 220, 220) },
{ AnimParamType::User, "Disabled/Inactive", QColor(255, 224, 224) },
{ AnimParamType::User, "Muted", QColor(255, 224, 224) },
};
const int kButtonsIdBase = 0x7fff;
const int kMaxRows = 20;
const int kColumnWidth = 300;
const int kRowHeight = 24;
const int kOthersEntryIndex = arraysize(g_trackEntries) - 3;
const int kDisabledEntryIndex = arraysize(g_trackEntries) - 2;
const int kMutedEntryIndex = arraysize(g_trackEntries) - 1;
}
std::map<CAnimParamType, QColor> CTVCustomizeTrackColorsDlg::s_trackColors;
QColor CTVCustomizeTrackColorsDlg::s_colorForDisabled;
QColor CTVCustomizeTrackColorsDlg::s_colorForMuted;
QColor CTVCustomizeTrackColorsDlg::s_colorForOthers;
CTVCustomizeTrackColorsDlg::CTVCustomizeTrackColorsDlg(QWidget* pParent)
: QDialog(pParent)
, m_aLabels(arraysize(g_trackEntries))
, m_colorButtons(arraysize(g_trackEntries))
, m_ui(new Ui::TVCustomizeTrackColorsDialog)
{
OnInitDialog();
}
CTVCustomizeTrackColorsDlg::~CTVCustomizeTrackColorsDlg()
{
}
void CTVCustomizeTrackColorsDlg::OnInitDialog()
{
m_ui->setupUi(this);
connect(m_ui->buttonBox, &QDialogButtonBox::accepted, this, &CTVCustomizeTrackColorsDlg::accept);
connect(m_ui->buttonBox, &QDialogButtonBox::rejected, this, &CTVCustomizeTrackColorsDlg::reject);
connect(m_ui->buttonBox->button(QDialogButtonBox::Apply), &QPushButton::clicked, this, &CTVCustomizeTrackColorsDlg::OnApply);
connect(m_ui->buttonResetAll, &QPushButton::clicked, this, &CTVCustomizeTrackColorsDlg::OnResetAll);
connect(m_ui->buttonExport, &QPushButton::clicked, this, &CTVCustomizeTrackColorsDlg::OnExport);
connect(m_ui->buttonImport, &QPushButton::clicked, this, &CTVCustomizeTrackColorsDlg::OnImport);
QRect labelRect(QPoint(30, 30), QPoint(150, 50));
QRect buttonRect(QPoint(180, 30), QPoint(280, 50));
// Create a label and a color button for each track.
int col = 0, i = 0;
std::for_each(g_trackEntries, g_trackEntries + arraysize(g_trackEntries), [&](const STrackEntry& entry)
{
const QString labelText = entry.name;
if(!labelText.isEmpty())
{
m_aLabels[i] = new QLabel(m_ui->frame);
m_aLabels[i]->setGeometry(labelRect);
m_aLabels[i]->setText(labelText);
m_colorButtons[i] = new ColorButton(m_ui->frame);
m_colorButtons[i]->setGeometry(buttonRect);
if(entry.paramType.GetType() == AnimParamType::User)
{
assert(kOthersEntryIndex <= i);
if (i == kOthersEntryIndex)
{
m_colorButtons[i]->SetColor(s_colorForOthers);
}
else if(i == kDisabledEntryIndex)
{
m_colorButtons[i]->SetColor(s_colorForDisabled);
}
else if(i == kMutedEntryIndex)
{
m_colorButtons[i]->SetColor(s_colorForMuted);
}
}
else
{
m_colorButtons[i]->SetColor(s_trackColors[entry.paramType]);
}
}
if(i % kMaxRows == kMaxRows - 1)
{
++col;
labelRect.moveTopLeft(QPoint(30+kColumnWidth*col, 30));
buttonRect.moveTopLeft(QPoint(180+kColumnWidth*col, 30));
}
else
{
labelRect.translate(0, kRowHeight);
buttonRect.translate(0, kRowHeight);
}
++i;
});
// Resize this dialog to fit the contents.
const QSize size(60 + kColumnWidth * (col + 1), 100 + kMaxRows * kRowHeight);
m_ui->frame->setFixedSize(size);
setFixedSize(sizeHint());
}
void CTVCustomizeTrackColorsDlg::accept()
{
OnApply();
QDialog::accept();
}
void CTVCustomizeTrackColorsDlg::OnApply()
{
int i = 0;
std::for_each(g_trackEntries, g_trackEntries + arraysize(g_trackEntries), [&](const STrackEntry& entry)
{
if(entry.paramType.GetType() != AnimParamType::User)
{
s_trackColors[entry.paramType] = m_colorButtons[i]->Color();
}
++i;
});
s_colorForOthers = m_colorButtons[kOthersEntryIndex]->Color();
s_colorForDisabled = m_colorButtons[kDisabledEntryIndex]->Color();
s_colorForMuted = m_colorButtons[kMutedEntryIndex]->Color();
CTrackViewDialog::GetCurrentInstance()->InvalidateDopeSheet();
}
void CTVCustomizeTrackColorsDlg::OnResetAll()
{
int i = 0;
std::for_each(g_trackEntries, g_trackEntries + arraysize(g_trackEntries), [&](const STrackEntry& entry)
{
const QString labelText = entry.name;
if(!labelText.isEmpty())
{
m_colorButtons[i]->SetColor(entry.defaultColor);
}
++i;
});
}
void CTVCustomizeTrackColorsDlg::SaveColors(const char* sectionName)
{
QSettings settings;
for (auto g : QString(sectionName).split('\\'))
{
settings.beginGroup(g);
}
std::for_each(begin(s_trackColors), end(s_trackColors),
[&](const std::pair<CAnimParamType, QColor>& pair)
{
const QString trackColorEntry = QString::fromLatin1("%1%2").arg(TRACKCOLOR_ENTRY_PREFIX).arg(static_cast<int>(pair.first.GetType()));
settings.setValue(trackColorEntry, pair.second.rgb());
});
settings.setValue(TRACKCOLOR_FOR_OTHERS_ENTRY, s_colorForOthers.rgb());
settings.setValue(TRACKCOLOR_FOR_DISABLED_ENTRY, s_colorForDisabled.rgb());
settings.setValue(TRACKCOLOR_FOR_MUTED_ENTRY, s_colorForMuted.rgb());
}
void CTVCustomizeTrackColorsDlg::LoadColors(const char* sectionName)
{
QSettings settings;
for (auto g : QString(sectionName).split('\\'))
{
settings.beginGroup(g);
}
std::for_each(g_trackEntries, g_trackEntries + arraysize(g_trackEntries), [&](const STrackEntry& entry)
{
if (entry.paramType.GetType() != AnimParamType::User)
{
s_trackColors[entry.paramType] = QColor::fromRgb(settings.value(QStringLiteral("%2%3").arg(TRACKCOLOR_ENTRY_PREFIX).arg(static_cast<int>(entry.paramType.GetType())), entry.defaultColor.rgb()).toInt());
}
});
s_colorForOthers = QColor::fromRgb(settings.value(TRACKCOLOR_FOR_OTHERS_ENTRY, g_trackEntries[kOthersEntryIndex].defaultColor.rgb()).toInt());
s_colorForDisabled = QColor::fromRgb(settings.value(TRACKCOLOR_FOR_DISABLED_ENTRY, g_trackEntries[kDisabledEntryIndex].defaultColor.rgb()).toInt());
s_colorForMuted = QColor::fromRgb(settings.value(TRACKCOLOR_FOR_MUTED_ENTRY, g_trackEntries[kMutedEntryIndex].defaultColor.rgb()).toInt());
}
void CTVCustomizeTrackColorsDlg::OnExport()
{
QString savePath;
if (CFileUtil::SelectSaveFile("Custom Track Colors Files (*.ctc)", "ctc",
Path::GetUserSandboxFolder(), savePath))
{
Export(savePath);
}
}
void CTVCustomizeTrackColorsDlg::OnImport()
{
QString loadPath;
if (CFileUtil::SelectFile("Custom Track Colors Files (*.ctc)",
Path::GetUserSandboxFolder(), loadPath))
{
if (Import(loadPath))
{
// since the user is explicitly pressing 'Import', we assume he or she wants to apply this import
// to see the result immediately, based on a customer feedback sample of one
OnApply();
}
else
{
QMessageBox::critical(this, tr("Cannot import"), tr("The file format is invalid!"));
}
}
}
void CTVCustomizeTrackColorsDlg::Export(const QString& fullPath) const
{
XmlNodeRef customTrackColorsNode = XmlHelpers::CreateXmlNode("customtrackcolors");
int i = 0;
std::for_each(g_trackEntries, g_trackEntries + arraysize(g_trackEntries), [&](const STrackEntry& entry)
{
if(entry.paramType.GetType() != AnimParamType::User)
{
XmlNodeRef entryNode = customTrackColorsNode->newChild("entry");
// Serialization is const safe
CAnimParamType &paramType = const_cast<CAnimParamType&>( entry.paramType );
paramType.Serialize( entryNode, false );
entryNode->setAttr("color", m_colorButtons[i]->Color().rgb());
}
++i;
});
XmlNodeRef othersNode = customTrackColorsNode->newChild("others");
othersNode->setAttr("color", m_colorButtons[kOthersEntryIndex]->Color().rgb());
XmlNodeRef disabledNode = customTrackColorsNode->newChild("disabled");
disabledNode->setAttr("color", m_colorButtons[kDisabledEntryIndex]->Color().rgb());
XmlNodeRef mutedNode = customTrackColorsNode->newChild("muted");
mutedNode->setAttr("color", m_colorButtons[kMutedEntryIndex]->Color().rgb());
XmlHelpers::SaveXmlNode(GetIEditor()->GetFileUtil(), customTrackColorsNode, fullPath.toStdString().c_str());
}
bool CTVCustomizeTrackColorsDlg::Import(const QString& fullPath)
{
XmlNodeRef customTrackColorsNode = XmlHelpers::LoadXmlFromFile(fullPath.toStdString().c_str());
if (customTrackColorsNode == NULL)
{
return false;
}
QColor color;
for (int i = 0; i < customTrackColorsNode->getChildCount(); ++i)
{
XmlNodeRef childNode = customTrackColorsNode->getChild(i);
if (QString(childNode->getTag()) != "entry")
{
continue;
}
CAnimParamType paramType;
paramType.Serialize(childNode, true);
// Get the entry index for this param type.
const STrackEntry* pEntry = std::find_if(g_trackEntries, g_trackEntries + arraysize(g_trackEntries),
[=](const STrackEntry& entry)
{
return entry.paramType == paramType;
});
int entryIndex = pEntry - g_trackEntries;
if (entryIndex >= arraysize(g_trackEntries)) // If not found, skip this.
{
continue;
}
GetQColorFromXmlNode(color, childNode);
m_colorButtons[entryIndex]->SetColor(color);
}
XmlNodeRef othersNode = customTrackColorsNode->findChild("others");
if (othersNode)
{
GetQColorFromXmlNode(color, othersNode);
m_colorButtons[kOthersEntryIndex]->SetColor(color);
}
XmlNodeRef disabledNode = customTrackColorsNode->findChild("disabled");
if (disabledNode)
{
GetQColorFromXmlNode(color, disabledNode);
m_colorButtons[kDisabledEntryIndex]->SetColor(color);
}
XmlNodeRef mutedNode = customTrackColorsNode->findChild("muted");
if (mutedNode)
{
GetQColorFromXmlNode(color, mutedNode);
m_colorButtons[kMutedEntryIndex]->SetColor(color);
}
return true;
}
#include <TrackView/moc_TVCustomizeTrackColorsDlg.cpp>
@@ -0,0 +1,87 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
// Description : A dialog for customizing track colors
#ifndef CRYINCLUDE_EDITOR_TRACKVIEW_TVCUSTOMIZETRACKCOLORSDLG_H
#define CRYINCLUDE_EDITOR_TRACKVIEW_TVCUSTOMIZETRACKCOLORSDLG_H
#pragma once
#if !defined(Q_MOC_RUN)
#include <QDialog>
#endif
namespace Ui
{
class TVCustomizeTrackColorsDialog;
}
class QLabel;
class ColorButton;
class CTVCustomizeTrackColorsDlg
: public QDialog
{
Q_OBJECT
friend class CTrackViewDialog;
public:
CTVCustomizeTrackColorsDlg(QWidget* pParent = nullptr);
virtual ~CTVCustomizeTrackColorsDlg();
static QColor GetTrackColor(CAnimParamType paramType)
{
auto itr = s_trackColors.find(paramType);
if (itr != end(s_trackColors))
{
return itr->second;
}
else
{
return s_colorForOthers;
}
}
static QColor GetColorForDisabledTracks()
{ return s_colorForDisabled; }
static QColor GetColorForMutedTracks()
{ return s_colorForMuted; }
private:
inline void GetQColorFromXmlNode(QColor& colorOut, const XmlNodeRef& xmlNode) const
{
QRgb rgb = -1;
xmlNode->getAttr("color", rgb);
colorOut.setRgb(rgb);
};
virtual void OnInitDialog();
void OnApply();
void OnResetAll();
void OnExport();
void OnImport();
void accept() override;
void Export(const QString& fullPath) const;
bool Import(const QString& fullPath);
static void SaveColors(const char* sectionName);
static void LoadColors(const char* sectionName);
QVector<QLabel*> m_aLabels;
QVector<ColorButton*> m_colorButtons;
QScopedPointer<Ui::TVCustomizeTrackColorsDialog> m_ui;
static std::map<CAnimParamType, QColor> s_trackColors;
static QColor s_colorForDisabled;
static QColor s_colorForMuted;
static QColor s_colorForOthers;
};
#endif // CRYINCLUDE_EDITOR_TRACKVIEW_TVCUSTOMIZETRACKCOLORSDLG_H
+391
View File
@@ -0,0 +1,391 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "EditorDefs.h"
#include "TVEventsDialog.h"
// Qt
#include <QInputDialog>
#include <QMessageBox>
// CryCommon
#include <CryCommon/Maestro/Types/AnimNodeType.h>
#include <CryCommon/Maestro/Types/AnimParamType.h>
// Editor
#include "AnimationContext.h"
AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
#include <TrackView/ui_TVEventsDialog.h>
AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING
// CTVEventsDialog dialog
namespace
{
const int kCountSubItemIndex = 1;
const int kTimeSubItemIndex = 2;
}
class TVEventsModel
: public QAbstractTableModel
{
public:
TVEventsModel(QObject* parent = nullptr)
: QAbstractTableModel(parent)
{
}
int rowCount(const QModelIndex& parent = QModelIndex()) const override
{
if (parent.isValid())
{
return 0;
}
CTrackViewSequence* sequence = GetIEditor()->GetAnimation()->GetSequence();
assert(sequence);
return sequence->GetTrackEventsCount();
}
int columnCount(const QModelIndex& parent = QModelIndex()) const override
{
return parent.isValid() ? 0 : 3;
}
bool removeRows(int row, int count, const QModelIndex& parent = QModelIndex()) override
{
if (parent.isValid())
{
return false;
}
bool result = true;
CTrackViewSequence* sequence = GetIEditor()->GetAnimation()->GetSequence();
assert(sequence);
AzToolsFramework::ScopedUndoBatch undo("Remove Track Event");
for (int r = row; r < row + count; ++r)
{
const QString eventName = index(r, 0).data().toString();
beginRemoveRows(QModelIndex(), r, r);
result &= sequence->RemoveTrackEvent(eventName.toUtf8().data());
endRemoveRows();
undo.MarkEntityDirty(sequence->GetSequenceComponentEntityId());
}
return result;
}
bool addRow(const QString& name)
{
CTrackViewSequence* sequence = GetIEditor()->GetAnimation()->GetSequence();
assert(sequence);
const int index = rowCount();
beginInsertRows(QModelIndex(), index, index);
bool result = false;
AzToolsFramework::ScopedUndoBatch undo("Add Track Event");
result = sequence->AddTrackEvent(name.toUtf8().data());
undo.MarkEntityDirty(sequence->GetSequenceComponentEntityId());
endInsertRows();
if (!result)
{
beginRemoveRows(QModelIndex(), index, index);
endRemoveRows();
}
return result;
}
bool moveRow(const QModelIndex& index, bool up)
{
CTrackViewSequence* sequence = GetIEditor()->GetAnimation()->GetSequence();
assert(sequence);
if (!index.isValid() || (up && index.row() == 0) || (!up && index.row() == rowCount() - 1))
{
return false;
}
bool result = false;
AzToolsFramework::ScopedUndoBatch undo("Move Track Event");
if (up)
{
beginMoveRows(QModelIndex(), index.row(), index.row(), QModelIndex(), index.row() - 1);
result = sequence->MoveUpTrackEvent(index.sibling(index.row(), 0).data().toString().toUtf8().data());
}
else
{
beginMoveRows(QModelIndex(), index.row() + 1, index.row() + 1, QModelIndex(), index.row());
result = sequence->MoveDownTrackEvent(index.sibling(index.row(), 0).data().toString().toUtf8().data());
}
undo.MarkEntityDirty(sequence->GetSequenceComponentEntityId());
endMoveRows();
return result;
}
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override
{
CTrackViewSequence* sequence = GetIEditor()->GetAnimation()->GetSequence();
assert(sequence);
if (role != Qt::DisplayRole)
{
return QVariant();
}
float timeFirstUsed;
int usageCount = GetNumberOfUsageAndFirstTimeUsed(sequence->GetTrackEvent(index.row()), timeFirstUsed);
switch (index.column())
{
case 0:
return QString::fromLatin1(sequence->GetTrackEvent(index.row()));
case 1:
return usageCount;
case 2:
return usageCount > 0 ? QString::number(timeFirstUsed, 'f', 3) : QString();
default:
return QVariant();
}
}
bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override
{
CTrackViewSequence* sequence = GetIEditor()->GetAnimation()->GetSequence();
assert(sequence);
if (role != Qt::DisplayRole && role != Qt::EditRole)
{
return false;
}
if (index.column() != 0 || value.toString().isEmpty())
{
return false;
}
bool result = false;
const QString oldName = index.data().toString();
const QString newName = value.toString();
AzToolsFramework::ScopedUndoBatch undo("Set Track Event Data");
result = sequence->RenameTrackEvent(oldName.toUtf8().data(), newName.toUtf8().data());
undo.MarkEntityDirty(sequence->GetSequenceComponentEntityId());
emit dataChanged(index, index);
return result;
}
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override
{
if (role != Qt::DisplayRole || orientation != Qt::Horizontal)
{
return QVariant();
}
switch (section)
{
case 0:
return tr("Event");
case 1:
return tr("# of use");
case 2:
return tr("Time of first usage");
default:
return QVariant();
}
}
int GetNumberOfUsageAndFirstTimeUsed(const char* eventName, float& timeFirstUsed) const;
};
CTVEventsDialog::CTVEventsDialog(QWidget* pParent /*=NULL*/)
: QDialog(pParent)
, m_ui(new Ui::TVEventsDialog)
{
m_ui->setupUi(this);
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
OnInitDialog();
connect(m_ui->buttonAddEvent, &QPushButton::clicked, this, &CTVEventsDialog::OnBnClickedButtonAddEvent);
connect(m_ui->buttonRemoveEvent, &QPushButton::clicked, this, &CTVEventsDialog::OnBnClickedButtonRemoveEvent);
connect(m_ui->buttonRenameEvent, &QPushButton::clicked, this, &CTVEventsDialog::OnBnClickedButtonRenameEvent);
connect(m_ui->buttonUpEvent, &QPushButton::clicked, this, &CTVEventsDialog::OnBnClickedButtonUpEvent);
connect(m_ui->buttonDownEvent, &QPushButton::clicked, this, &CTVEventsDialog::OnBnClickedButtonDownEvent);
connect(m_ui->m_List->selectionModel(), &QItemSelectionModel::selectionChanged, this, &CTVEventsDialog::OnListItemChanged);
}
CTVEventsDialog::~CTVEventsDialog()
{
}
// CTVEventsDialog message handlers
void CTVEventsDialog::OnBnClickedButtonAddEvent()
{
const QString add = QInputDialog::getText(this, tr("Track Event Name"), QString());
if (!add.isEmpty() && static_cast<TVEventsModel*>(m_ui->m_List->model())->addRow(add))
{
m_lastAddedEvent = add;
m_ui->m_List->setCurrentIndex(m_ui->m_List->model()->index(m_ui->m_List->model()->rowCount() - 1, 0));
}
m_ui->m_List->setFocus();
}
void CTVEventsDialog::OnBnClickedButtonRemoveEvent()
{
QList<QPersistentModelIndex> indexes;
for (auto index : m_ui->m_List->selectionModel()->selectedRows())
{
indexes.push_back(index);
}
for (auto index : indexes)
{
if (QMessageBox::warning(this, tr("Remove Event"), tr("This removal might cause some link breakages in Flow Graph.\nStill continue?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes)
{
m_ui->m_List->model()->removeRow(index.row());
}
}
m_ui->m_List->setFocus();
}
void CTVEventsDialog::OnBnClickedButtonRenameEvent()
{
const QModelIndex index = m_ui->m_List->currentIndex();
if (index.isValid())
{
const QString newName = QInputDialog::getText(this, tr("Track Event Name"), QString());
if (!newName.isEmpty())
{
m_ui->m_List->model()->setData(index.sibling(index.row(), 0), newName);
}
}
m_ui->m_List->setFocus();
}
void CTVEventsDialog::OnBnClickedButtonUpEvent()
{
static_cast<TVEventsModel*>(m_ui->m_List->model())->moveRow(m_ui->m_List->currentIndex(), true);
UpdateButtons();
m_ui->m_List->setFocus();
}
void CTVEventsDialog::OnBnClickedButtonDownEvent()
{
static_cast<TVEventsModel*>(m_ui->m_List->model())->moveRow(m_ui->m_List->currentIndex(), false);
UpdateButtons();
m_ui->m_List->setFocus();
}
void CTVEventsDialog::OnInitDialog()
{
m_ui->m_List->setModel(new TVEventsModel(this));
m_ui->m_List->header()->resizeSections(QHeaderView::ResizeToContents);
assert(GetIEditor()->GetAnimation()->GetSequence());
UpdateButtons();
}
void CTVEventsDialog::OnListItemChanged()
{
UpdateButtons();
}
void CTVEventsDialog::UpdateButtons()
{
bool bRemove = false, bRename = false, bUp = false, bDown = false;
int nSelected = m_ui->m_List->selectionModel()->selectedRows().count();
if (nSelected > 1)
{
bRemove = true;
bRename = false;
}
else if (nSelected > 0)
{
bRemove = bRename = true;
const QModelIndex index = m_ui->m_List->selectionModel()->selectedRows().first();
if (index.row() > 0)
{
bUp = true;
}
if (index.row() < m_ui->m_List->model()->rowCount() - 1)
{
bDown = true;
}
}
m_ui->buttonRemoveEvent->setEnabled(bRemove);
m_ui->buttonRenameEvent->setEnabled(bRename);
m_ui->buttonUpEvent->setEnabled(bUp);
m_ui->buttonDownEvent->setEnabled(bDown);
}
const QString& CTVEventsDialog::GetLastAddedEvent()
{
return m_lastAddedEvent;
}
int TVEventsModel::GetNumberOfUsageAndFirstTimeUsed(const char* eventName, float& timeFirstUsed) const
{
CTrackViewSequence* sequence = GetIEditor()->GetAnimation()->GetSequence();
int usageCount = 0;
float firstTime = std::numeric_limits<float>::max();
CTrackViewAnimNodeBundle nodeBundle = sequence->GetAnimNodesByType(AnimNodeType::Event);
const unsigned int numNodes = nodeBundle.GetCount();
for (unsigned int currentNode = 0; currentNode < numNodes; ++currentNode)
{
CTrackViewAnimNode* pCurrentNode = nodeBundle.GetNode(currentNode);
CTrackViewTrackBundle tracks = pCurrentNode->GetTracksByParam(AnimParamType::TrackEvent);
const unsigned int numTracks = tracks.GetCount();
for (unsigned int currentTrack = 0; currentTrack < numTracks; ++currentTrack)
{
CTrackViewTrack* pTrack = tracks.GetTrack(currentTrack);
for (int currentKey = 0; currentKey < pTrack->GetKeyCount(); ++currentKey)
{
CTrackViewKeyHandle keyHandle = pTrack->GetKey(currentKey);
IEventKey key;
keyHandle.GetKey(&key);
if (strcmp(key.event.c_str(), eventName) == 0) // If it has a key with the specified event set
{
++usageCount;
if (key.time < firstTime)
{
firstTime = key.time;
}
}
}
}
}
if (usageCount > 0)
{
timeFirstUsed = firstTime;
}
return usageCount;
}
#include <TrackView/moc_TVEventsDialog.cpp>
+54
View File
@@ -0,0 +1,54 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef CRYINCLUDE_EDITOR_TRACKVIEW_TVEVENTSDIALOG_H
#define CRYINCLUDE_EDITOR_TRACKVIEW_TVEVENTSDIALOG_H
#pragma once
#if !defined(Q_MOC_RUN)
#include <IMovieSystem.h>
#include <QDialog>
#endif
namespace Ui
{
class TVEventsDialog;
}
// CTVEventsDialog dialog
class CTVEventsDialog
: public QDialog
{
Q_OBJECT
public:
CTVEventsDialog(QWidget* pParent = nullptr); // standard constructor
virtual ~CTVEventsDialog();
void OnBnClickedButtonAddEvent();
void OnBnClickedButtonRemoveEvent();
void OnBnClickedButtonRenameEvent();
void OnBnClickedButtonUpEvent();
void OnBnClickedButtonDownEvent();
void OnListItemChanged();
const QString& GetLastAddedEvent();
protected:
void OnInitDialog();
void UpdateButtons();
private:
QScopedPointer<Ui::TVEventsDialog> m_ui;
QString m_lastAddedEvent;
};
#endif // CRYINCLUDE_EDITOR_TRACKVIEW_TVEVENTSDIALOG_H
+99
View File
@@ -0,0 +1,99 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>TVEventsDialog</class>
<widget class="QDialog" name="TVEventsDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>427</width>
<height>308</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>350</width>
<height>0</height>
</size>
</property>
<property name="windowTitle">
<string>TrackView Events</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0" colspan="5">
<widget class="QTreeView" name="m_List">
<property name="selectionMode">
<enum>QAbstractItemView::ExtendedSelection</enum>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QPushButton" name="buttonAddEvent">
<property name="sizePolicy">
<sizepolicy hsizetype="Ignored" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Add</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QPushButton" name="buttonRemoveEvent">
<property name="sizePolicy">
<sizepolicy hsizetype="Ignored" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Remove</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QPushButton" name="buttonRenameEvent">
<property name="sizePolicy">
<sizepolicy hsizetype="Ignored" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Rename</string>
</property>
</widget>
</item>
<item row="1" column="3">
<widget class="QPushButton" name="buttonUpEvent">
<property name="sizePolicy">
<sizepolicy hsizetype="Ignored" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Up</string>
</property>
</widget>
</item>
<item row="1" column="4">
<widget class="QPushButton" name="buttonDownEvent">
<property name="sizePolicy">
<sizepolicy hsizetype="Ignored" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Down</string>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
+308
View File
@@ -0,0 +1,308 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
// Description : implementation file
#include "EditorDefs.h"
#include "TVSequenceProps.h"
// Qt
#include <QMessageBox>
// Editor
#include "TrackViewSequence.h"
#include "TrackViewSequenceManager.h"
#include "AnimationContext.h"
AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
#include <TrackView/ui_TVSequenceProps.h>
AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING
CTVSequenceProps::CTVSequenceProps(CTrackViewSequence* pSequence, float fps, QWidget* pParent)
: QDialog(pParent)
, m_FPS(fps)
, m_outOfRange(0)
, m_timeUnit(Seconds)
, ui(new Ui::CTVSequenceProps)
{
ui->setupUi(this);
assert(pSequence);
m_pSequence = pSequence;
connect(ui->buttonBox, &QDialogButtonBox::accepted, this, &CTVSequenceProps::OnOK);
connect(ui->CUT_SCENE, &QCheckBox::toggled, this, &CTVSequenceProps::ToggleCutsceneOptions);
connect(ui->TO_SECONDS, &QRadioButton::toggled, this, &CTVSequenceProps::OnBnClickedToSeconds);
connect(ui->TO_FRAMES, &QRadioButton::toggled, this, &CTVSequenceProps::OnBnClickedToFrames);
OnInitDialog();
}
CTVSequenceProps::~CTVSequenceProps()
{
}
// CTVSequenceProps message handlers
BOOL CTVSequenceProps::OnInitDialog()
{
ui->NAME->setText(m_pSequence->GetName());
int seqFlags = m_pSequence->GetFlags();
ui->ALWAYS_PLAY->setChecked((seqFlags & IAnimSequence::eSeqFlags_PlayOnReset));
ui->CUT_SCENE->setChecked((seqFlags & IAnimSequence::eSeqFlags_CutScene));
ui->DISABLEPLAYER->setChecked((seqFlags & IAnimSequence::eSeqFlags_NoPlayer));
ui->DISABLESOUNDS->setChecked((seqFlags & IAnimSequence::eSeqFlags_NoGameSounds));
ui->NOSEEK->setChecked((seqFlags & IAnimSequence::eSeqFlags_NoSeek));
ui->NOABORT->setChecked((seqFlags & IAnimSequence::eSeqFlags_NoAbort));
ui->EARLYMOVIEUPDATE->setChecked((seqFlags & IAnimSequence::eSeqFlags_EarlyMovieUpdate));
ToggleCutsceneOptions(ui->CUT_SCENE->isChecked());
ui->MOVE_SCALE_KEYS->setChecked(BST_UNCHECKED);
ui->START_TIME->setRange(0.0, (1e+5));
ui->END_TIME->setRange(0.0, (1e+5));
Range timeRange = m_pSequence->GetTimeRange();
float invFPS = 1.0f / m_FPS;
m_timeUnit = Seconds;
ui->START_TIME->setValue(timeRange.start);
ui->START_TIME->setSingleStep(invFPS);
ui->END_TIME->setValue(timeRange.end);
ui->END_TIME->setSingleStep(invFPS);
m_outOfRange = 0;
if (m_pSequence->GetFlags() & IAnimSequence::eSeqFlags_OutOfRangeConstant)
{
m_outOfRange = 1;
ui->ORT_CONSTANT->setChecked(true);
}
else if (m_pSequence->GetFlags() & IAnimSequence::eSeqFlags_OutOfRangeLoop)
{
m_outOfRange = 2;
ui->ORT_LOOP->setChecked(true);
}
else
{
ui->ORT_ONCE->setChecked(true);
}
return TRUE; // return TRUE unless you set the focus to a control
// EXCEPTION: OCX Property Pages should return FALSE
}
void CTVSequenceProps::MoveScaleKeys()
{
// Move/Rescale the sequence to a new time range.
Range timeRangeOld = m_pSequence->GetTimeRange();
Range timeRangeNew;
timeRangeNew.start = ui->START_TIME->value();
timeRangeNew.end = ui->END_TIME->value();
if (!(timeRangeNew == timeRangeOld))
{
m_pSequence->AdjustKeysToTimeRange(timeRangeNew);
}
}
void CTVSequenceProps::UpdateSequenceProps(const QString& name)
{
if (ui->MOVE_SCALE_KEYS->isChecked())
{
MoveScaleKeys();
}
Range timeRange;
timeRange.start = ui->START_TIME->value();
timeRange.end = ui->END_TIME->value();
if (m_timeUnit == Frames)
{
float invFPS = 1.0f / m_FPS;
timeRange.start = ui->START_TIME->value() * invFPS;
timeRange.end = ui->END_TIME->value() * invFPS;
}
m_pSequence->SetTimeRange(timeRange);
CAnimationContext* ac = GetIEditor()->GetAnimation();
if (ac)
{
ac->UpdateTimeRange();
}
QString seqName = m_pSequence->GetName();
if (name != seqName)
{
// Rename sequence.
const CTrackViewSequenceManager* sequenceManager = GetIEditor()->GetSequenceManager();
sequenceManager->RenameNode(m_pSequence, name.toUtf8().data());
}
int seqFlags = m_pSequence->GetFlags();
seqFlags &= ~(IAnimSequence::eSeqFlags_OutOfRangeConstant | IAnimSequence::eSeqFlags_OutOfRangeLoop);
if (ui->ALWAYS_PLAY->isChecked())
{
seqFlags |= IAnimSequence::eSeqFlags_PlayOnReset;
}
else
{
seqFlags &= ~IAnimSequence::eSeqFlags_PlayOnReset;
}
if (ui->CUT_SCENE->isChecked())
{
seqFlags |= IAnimSequence::eSeqFlags_CutScene;
}
else
{
seqFlags &= ~IAnimSequence::eSeqFlags_CutScene;
}
if (ui->DISABLEPLAYER->isChecked())
{
seqFlags |= IAnimSequence::eSeqFlags_NoPlayer;
}
else
{
seqFlags &= (~IAnimSequence::eSeqFlags_NoPlayer);
}
if (ui->ORT_CONSTANT->isChecked())
{
seqFlags |= IAnimSequence::eSeqFlags_OutOfRangeConstant;
}
else if (ui->ORT_LOOP->isChecked())
{
seqFlags |= IAnimSequence::eSeqFlags_OutOfRangeLoop;
}
if (ui->DISABLESOUNDS->isChecked())
{
seqFlags |= IAnimSequence::eSeqFlags_NoGameSounds;
}
else
{
seqFlags &= (~IAnimSequence::eSeqFlags_NoGameSounds);
}
if (ui->NOSEEK->isChecked())
{
seqFlags |= IAnimSequence::eSeqFlags_NoSeek;
}
else
{
seqFlags &= (~IAnimSequence::eSeqFlags_NoSeek);
}
if (ui->NOABORT->isChecked())
{
seqFlags |= IAnimSequence::eSeqFlags_NoAbort;
}
else
{
seqFlags &= (~IAnimSequence::eSeqFlags_NoAbort);
}
if (ui->EARLYMOVIEUPDATE->isChecked())
{
seqFlags |= IAnimSequence::eSeqFlags_EarlyMovieUpdate;
}
else
{
seqFlags &= (~IAnimSequence::eSeqFlags_EarlyMovieUpdate);
}
if (static_cast<IAnimSequence::EAnimSequenceFlags>(seqFlags) != m_pSequence->GetFlags())
{
AzToolsFramework::ScopedUndoBatch undoBatch("Change TrackView Sequence Flags");
m_pSequence->SetFlags(static_cast<IAnimSequence::EAnimSequenceFlags>(seqFlags));
undoBatch.MarkEntityDirty(m_pSequence->GetSequenceComponentEntityId());
}
}
void CTVSequenceProps::OnOK()
{
QString name = ui->NAME->text();
if (name.isEmpty())
{
QMessageBox::warning(this, "Sequence Properties", "A sequence name cannot be empty!");
return;
}
else if (name.contains('/'))
{
QMessageBox::warning(this, "Sequence Properties", "A sequence name cannot contain a '/' character!");
return;
}
if (m_pSequence != nullptr)
{
AzToolsFramework::ScopedUndoBatch undoBatch("Change TrackView Sequence Settings");
UpdateSequenceProps(name);
undoBatch.MarkEntityDirty(m_pSequence->GetSequenceComponentEntityId());
}
accept();
}
void CTVSequenceProps::ToggleCutsceneOptions(bool bActivated)
{
if (bActivated == FALSE)
{
ui->NOABORT->setChecked(false);
ui->DISABLEPLAYER->setChecked(false);
ui->DISABLESOUNDS->setChecked(false);
}
ui->NOABORT->setEnabled(bActivated);
ui->DISABLEPLAYER->setEnabled(bActivated);
ui->DISABLESOUNDS->setEnabled(bActivated);
}
void CTVSequenceProps::OnBnClickedToFrames(bool v)
{
if (!v)
{
return;
}
ui->START_TIME->setSingleStep(1.0f);
ui->END_TIME->setSingleStep(1.0f);
ui->START_TIME->setValue(std::round(ui->START_TIME->value() * static_cast<double>(m_FPS)));
ui->END_TIME->setValue(std::round(ui->END_TIME->value() * static_cast<double>(m_FPS)));
m_timeUnit = Frames;
}
void CTVSequenceProps::OnBnClickedToSeconds(bool v)
{
if (!v)
{
return;
}
float fInvFPS = 1.0f / m_FPS;
ui->START_TIME->setSingleStep(fInvFPS);
ui->END_TIME->setSingleStep(fInvFPS);
ui->START_TIME->setValue(ui->START_TIME->value() * fInvFPS);
ui->END_TIME->setValue(ui->END_TIME->value() * fInvFPS);
m_timeUnit = Seconds;
}
#include <TrackView/moc_TVSequenceProps.cpp>
+60
View File
@@ -0,0 +1,60 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef CRYINCLUDE_EDITOR_TRACKVIEW_TVSEQUENCEPROPS_H
#define CRYINCLUDE_EDITOR_TRACKVIEW_TVSEQUENCEPROPS_H
#pragma once
class CTrackViewSequence;
#if !defined(Q_MOC_RUN)
#include <QDialog>
#include <QScopedPointer>
#endif
namespace Ui {
class CTVSequenceProps;
}
class CTVSequenceProps
: public QDialog
{
Q_OBJECT
public:
CTVSequenceProps(CTrackViewSequence* pSequence, float fps, QWidget* pParent = NULL); // standard constructor
~CTVSequenceProps();
private:
enum SequenceTimeUnit
{
Seconds = 0,
Frames
};
CTrackViewSequence* m_pSequence;
virtual BOOL OnInitDialog();
virtual void OnOK();
void MoveScaleKeys();
float m_FPS;
int m_outOfRange;
SequenceTimeUnit m_timeUnit;
QScopedPointer<Ui::CTVSequenceProps> ui;
public slots:
void OnBnClickedToFrames(bool);
void OnBnClickedToSeconds(bool);
private slots:
void ToggleCutsceneOptions(bool);
void UpdateSequenceProps(const QString& name);
};
#endif // CRYINCLUDE_EDITOR_TRACKVIEW_TVSEQUENCEPROPS_H
+270
View File
@@ -0,0 +1,270 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>CTVSequenceProps</class>
<widget class="QDialog" name="CTVSequenceProps">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>310</width>
<height>485</height>
</rect>
</property>
<property name="windowTitle">
<string>Edit Sequence</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<widget class="QGroupBox" name="groupBox2">
<property name="title">
<string>Properties</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="3" column="0" colspan="3">
<widget class="QCheckBox" name="EARLYMOVIEUPDATE">
<property name="text">
<string>Update Movie System First</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QCheckBox" name="NOSEEK">
<property name="text">
<string>NoSeek</string>
</property>
</widget>
</item>
<item row="1" column="0" colspan="3">
<widget class="QLineEdit" name="NAME">
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label2">
<property name="text">
<string>Sequence Name</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="ALWAYS_PLAY">
<property name="text">
<string>Autostart</string>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QCheckBox" name="CUT_SCENE">
<property name="text">
<string>Cut-Scene</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox3">
<property name="title">
<string>Cut-Scene Toggles</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QCheckBox" name="NOABORT">
<property name="text">
<string>Non-Skippable</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="DISABLEPLAYER">
<property name="text">
<string>Disable Player</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="DISABLESOUNDS">
<property name="text">
<string>Disable Sounds</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QGroupBox" name="groupBox4">
<property name="title">
<string>Timing</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="label1">
<property name="text">
<string>Start Time:</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
</widget>
</item>
<item>
<widget class="QDoubleSpinBox" name="START_TIME">
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>EndTime:</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
</widget>
</item>
<item>
<widget class="QDoubleSpinBox" name="END_TIME">
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Display Start/End Time As:</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QRadioButton" name="TO_FRAMES">
<property name="text">
<string>Frames</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="TO_SECONDS">
<property name="text">
<string>Seconds</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QCheckBox" name="MOVE_SCALE_KEYS">
<property name="text">
<string>Move/Scale Keys</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="QGroupBox" name="groupBox1">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>Out Of Range</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QRadioButton" name="ORT_ONCE">
<property name="text">
<string>Once</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="ORT_CONSTANT">
<property name="text">
<string>Constant</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="ORT_LOOP">
<property name="text">
<string>Loop</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>CTVSequenceProps</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>196</x>
<y>465</y>
</hint>
<hint type="destinationlabel">
<x>239</x>
<y>407</y>
</hint>
</hints>
</connection>
</connections>
</ui>
@@ -0,0 +1,125 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "EditorDefs.h"
// CryCommon
#include <CryCommon/Maestro/Types/AnimParamType.h> // for AnimParamType
// Editor
#include "TrackViewKeyPropertiesDlg.h" // for CTrackViewKeyUIControls// Editor
//////////////////////////////////////////////////////////////////////////
class CTimeRangeKeyUIControls
: public CTrackViewKeyUIControls
{
public:
CSmartVariableArray mv_table;
CSmartVariable<float> mv_startTime;
CSmartVariable<float> mv_endTime;
CSmartVariable<float> mv_timeScale;
CSmartVariable<bool> mv_bLoop;
virtual void OnCreateVars()
{
AddVariable(mv_table, "Key Properties");
AddVariable(mv_table, mv_startTime, "Start Time");
AddVariable(mv_table, mv_endTime, "End Time");
AddVariable(mv_table, mv_timeScale, "Time Scale");
AddVariable(mv_table, mv_bLoop, "Loop");
mv_timeScale->SetLimits(0.001f, 100.f);
}
bool SupportTrackType(const CAnimParamType& paramType, [[maybe_unused]] EAnimCurveType trackType, [[maybe_unused]] AnimValueType valueType) const
{
return paramType == AnimParamType::TimeRanges;
}
virtual bool OnKeySelectionChange(CTrackViewKeyBundle& selectedKeys);
virtual void OnUIChange(IVariable* pVar, CTrackViewKeyBundle& selectedKeys);
virtual unsigned int GetPriority() const { return 1; }
static const GUID& GetClassID()
{
// {E977A6F4-CEC1-4c67-8735-28721B3F6FEF}
static const GUID guid = {
0xe977a6f4, 0xcec1, 0x4c67, { 0x87, 0x35, 0x28, 0x72, 0x1b, 0x3f, 0x6f, 0xef }
};
return guid;
}
};
//////////////////////////////////////////////////////////////////////////
bool CTimeRangeKeyUIControls::OnKeySelectionChange(CTrackViewKeyBundle& selectedKeys)
{
if (!selectedKeys.AreAllKeysOfSameType())
{
return false;
}
bool bAssigned = false;
if (selectedKeys.GetKeyCount() == 1)
{
const CTrackViewKeyHandle& keyHandle = selectedKeys.GetKey(0);
CAnimParamType paramType = keyHandle.GetTrack()->GetParameterType();
if (paramType == AnimParamType::TimeRanges)
{
ICharacterKey timeRangeKey;
keyHandle.GetKey(&timeRangeKey);
mv_endTime = timeRangeKey.m_endTime;
mv_startTime = timeRangeKey.m_startTime;
mv_timeScale = timeRangeKey.m_speed;
mv_bLoop = timeRangeKey.m_bLoop;
bAssigned = true;
}
}
return bAssigned;
}
// Called when UI variable changes.
void CTimeRangeKeyUIControls::OnUIChange(IVariable* pVar, CTrackViewKeyBundle& selectedKeys)
{
CTrackViewSequence* pSequence = GetIEditor()->GetAnimation()->GetSequence();
if (!pSequence || !selectedKeys.AreAllKeysOfSameType())
{
return;
}
for (unsigned int keyIndex = 0, num = (int)selectedKeys.GetKeyCount(); keyIndex < num; keyIndex++)
{
CTrackViewKeyHandle keyHandle = selectedKeys.GetKey(keyIndex);
CAnimParamType paramType = keyHandle.GetTrack()->GetParameterType();
if (paramType == AnimParamType::TimeRanges)
{
ITimeRangeKey timeRangeKey;
keyHandle.GetKey(&timeRangeKey);
SyncValue(mv_startTime, timeRangeKey.m_startTime, false, pVar);
SyncValue(mv_endTime, timeRangeKey.m_endTime, false, pVar);
SyncValue(mv_timeScale, timeRangeKey.m_speed, false, pVar);
SyncValue(mv_bLoop, timeRangeKey.m_bLoop, false, pVar);
// Clamp values
if (!timeRangeKey.m_bLoop)
{
timeRangeKey.m_endTime = std::min(timeRangeKey.m_duration, timeRangeKey.m_endTime);
}
timeRangeKey.m_startTime = std::min(timeRangeKey.m_duration, timeRangeKey.m_startTime);
timeRangeKey.m_startTime = std::min(timeRangeKey.m_endTime, timeRangeKey.m_startTime);
keyHandle.SetKey(&timeRangeKey);
}
}
}
REGISTER_QT_CLASS_DESC(CTimeRangeKeyUIControls, "TrackView.KeyUI.TimeRange", "TrackViewKeyUI");
@@ -0,0 +1,238 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "EditorDefs.h"
// CryCommon
#include <CryCommon/Maestro/Types/AnimParamType.h>
// Editor
#include "TrackViewKeyPropertiesDlg.h"
#include "TVEventsDialog.h"
//////////////////////////////////////////////////////////////////////////
class CTrackEventKeyUIControls
: public CTrackViewKeyUIControls
{
public:
CSmartVariableArray mv_table;
CSmartVariableEnum<QString> mv_event;
CSmartVariable<QString> mv_value;
virtual void OnCreateVars()
{
AddVariable(mv_table, "Key Properties");
AddVariable(mv_table, mv_event, "Track Event");
mv_event->SetFlags(mv_event->GetFlags() | IVariable::UI_UNSORTED);
AddVariable(mv_table, mv_value, "Value");
}
bool SupportTrackType(const CAnimParamType& paramType, [[maybe_unused]] EAnimCurveType trackType, [[maybe_unused]] AnimValueType valueType) const
{
return paramType == AnimParamType::TrackEvent;
}
virtual bool OnKeySelectionChange(CTrackViewKeyBundle& selectedKeys);
virtual void OnUIChange(IVariable* pVar, CTrackViewKeyBundle& selectedKeys);
virtual unsigned int GetPriority() const { return 1; }
static const GUID& GetClassID()
{
// {F7D002EB-1FEA-46fa-B857-FC2B1B990B7F}
static const GUID guid =
{
0xf7d002eb, 0x1fea, 0x46fa, { 0xb8, 0x57, 0xfc, 0x2b, 0x1b, 0x99, 0xb, 0x7f }
};
return guid;
}
private:
void OnEventEdit();
void BuildEventDropDown(QString& curEvent, const QString& addedEvent = "");
QString m_lastEvent;
static const char* GetAddEventString()
{
static const char* addEventString = "Add a new event...";
return addEventString;
}
};
//////////////////////////////////////////////////////////////////////////
bool CTrackEventKeyUIControls::OnKeySelectionChange(CTrackViewKeyBundle& selectedKeys)
{
if (!selectedKeys.AreAllKeysOfSameType())
{
return false;
}
bool bAssigned = false;
if (selectedKeys.GetKeyCount() == 1)
{
const CTrackViewKeyHandle& keyHandle = selectedKeys.GetKey(0);
CAnimParamType paramType = keyHandle.GetTrack()->GetParameterType();
if (paramType == AnimParamType::TrackEvent)
{
IEventKey eventKey;
keyHandle.GetKey(&eventKey);
// Provide builder with current event value to ensure
// dropdown is displayed properly and value is updated if not found
QString event = eventKey.event.c_str();
BuildEventDropDown(event);
mv_event = event;
mv_value = eventKey.eventValue.c_str();
bAssigned = true;
}
}
m_lastEvent = mv_event;
return bAssigned;
}
// Called when UI variable changes.
void CTrackEventKeyUIControls::OnUIChange(IVariable* pVar, CTrackViewKeyBundle& selectedKeys)
{
CTrackViewSequence* pSequence = GetIEditor()->GetAnimation()->GetSequence();
if (!pSequence || !selectedKeys.AreAllKeysOfSameType())
{
return;
}
if (mv_event == GetAddEventString())
{
mv_event = m_lastEvent;
OnEventEdit();
return;
}
if (mv_event == "___spacer___")
{
mv_event = m_lastEvent;
return;
}
for (unsigned int keyIndex = 0; keyIndex < selectedKeys.GetKeyCount(); ++keyIndex)
{
CTrackViewKeyHandle keyHandle = selectedKeys.GetKey(keyIndex);
CAnimParamType paramType = keyHandle.GetTrack()->GetParameterType();
if (paramType == AnimParamType::TrackEvent)
{
IEventKey eventKey;
keyHandle.GetKey(&eventKey);
QByteArray event, value;
event = static_cast<QString>(mv_event).toUtf8();
value = static_cast<QString>(mv_value).toUtf8();
if (pVar == mv_event.GetVar())
{
eventKey.event = event.data();
}
if (pVar == mv_value.GetVar())
{
eventKey.eventValue = value.data();
}
eventKey.animation = "";
eventKey.duration = 0;
keyHandle.SetKey(&eventKey);
}
}
m_lastEvent = mv_event;
}
void CTrackEventKeyUIControls::OnEventEdit()
{
// Create dialog
CTVEventsDialog dlg;
dlg.exec();
QString event = mv_event;
BuildEventDropDown(event, dlg.GetLastAddedEvent());
// The step below is necessary to make the event drop-down up-to-date.
mv_event.GetVar()->EnableNotifyWithoutValueChange(true);
mv_event = event;
mv_event.GetVar()->EnableNotifyWithoutValueChange(false);
}
void CTrackEventKeyUIControls::BuildEventDropDown(QString& curEvent, const QString& addedEvent)
{
if (CAnimationContext* context = GetIEditor()->GetAnimation())
{
CTrackViewSequence* sequence = context->GetSequence();
if (sequence)
{
bool curEventExists = false;
bool addedEventExists = false;
mv_event.SetEnumList(NULL);
const int eventCount = sequence->GetTrackEventsCount();
// Need to check if event exists before adding all events
// This handles the case where the current event got deleted in the dialog but no new events were added
for (int i = 0; i < eventCount; ++i)
{
const char* trackEvent = sequence->GetTrackEvent(i);
if (curEvent == trackEvent)
{
curEventExists = true;
}
if (addedEvent == trackEvent)
{
addedEventExists = true;
}
if (curEventExists && addedEventExists)
{
break;
}
}
if (!curEventExists)
{
if (addedEventExists)
{
// Set added event if key not set
curEvent = addedEvent;
}
else
{
mv_event->AddEnumItem(QObject::tr("<None>"), "");
curEvent = "";
}
}
// Add Events
for (int i = 0; i < eventCount; ++i)
{
const char* trackEvent = sequence->GetTrackEvent(i);
mv_event->AddEnumItem(trackEvent, trackEvent);
}
// Used as a spacer to make Add a new event... standout
mv_event->AddEnumItem(QObject::tr(""), "___spacer___");
// Add a new event... to open event editor when selected
mv_event->AddEnumItem(QObject::tr(GetAddEventString()), GetAddEventString());
}
}
}
REGISTER_QT_CLASS_DESC(CTrackEventKeyUIControls, "TrackView.KeyUI.TrackEvent", "TrackViewKeyUI");
File diff suppressed because it is too large Load Diff
+351
View File
@@ -0,0 +1,351 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzCore/std/containers/map.h>
#include <AzCore/Component/EntityId.h>
#include <AzCore/Component/EntityBus.h>
#include <AzFramework/Entity/EntityContextBus.h>
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
#include <AzCore/Component/TransformBus.h>
#include "TrackViewNode.h"
#include "TrackViewTrack.h"
#include "Objects/TrackGizmo.h"
class CTrackViewAnimNode;
class QWidget;
// Represents a bundle of anim nodes
class CTrackViewAnimNodeBundle
{
public:
unsigned int GetCount() const { return m_animNodes.size(); }
CTrackViewAnimNode* GetNode(const unsigned int index) { return m_animNodes[index]; }
const CTrackViewAnimNode* GetNode(const unsigned int index) const { return m_animNodes[index]; }
void Clear();
const bool DoesContain(const CTrackViewNode* pTargetNode);
void AppendAnimNode(CTrackViewAnimNode* pNode);
void AppendAnimNodeBundle(const CTrackViewAnimNodeBundle& bundle);
void ExpandAll(bool bAlsoExpandParentNodes = true);
void CollapseAll();
private:
std::vector<CTrackViewAnimNode*> m_animNodes;
};
// Callback called by animation node when its animated.
class IAnimNodeAnimator
{
public:
virtual ~IAnimNodeAnimator() {}
virtual void Animate(CTrackViewAnimNode* pNode, const SAnimContext& ac) = 0;
virtual void Render([[maybe_unused]] CTrackViewAnimNode* pNode, [[maybe_unused]] const SAnimContext& ac) {}
// Called when binding/unbinding the owning node
virtual void Bind([[maybe_unused]] CTrackViewAnimNode* pNode) {}
virtual void UnBind([[maybe_unused]] CTrackViewAnimNode* pNode) {}
};
////////////////////////////////////////////////////////////////////////////
//
// This class represents a IAnimNode in TrackView and contains
// the editor side code for changing it
//
// It does *not* have ownership of the IAnimNode, therefore deleting it
// will not destroy the CryMovie track
//
////////////////////////////////////////////////////////////////////////////
class CTrackViewAnimNode
: public CTrackViewNode
, public IAnimNodeOwner
, public ITransformDelegate
, public AzToolsFramework::EditorEntityContextNotificationBus::Handler
, public AZ::EntityBus::Handler
, private AZ::TransformNotificationBus::Handler
, private AzToolsFramework::EntitySelectionEvents::Bus::Handler
{
public:
CTrackViewAnimNode(IAnimSequence* pSequence, IAnimNode* pAnimNode, CTrackViewNode* pParentNode);
~CTrackViewAnimNode();
// Rendering
virtual void Render(const SAnimContext& ac);
// Playback
virtual void Animate(const SAnimContext& animContext);
// Binding/Unbinding
virtual void BindToEditorObjects();
virtual void UnBindFromEditorObjects();
virtual bool IsBoundToEditorObjects() const;
// Console sync
virtual void SyncToConsole(SAnimContext& animContext);
// CTrackViewAnimNode
virtual ETrackViewNodeType GetNodeType() const override { return eTVNT_AnimNode; }
// Create & remove sub anim nodes
virtual CTrackViewAnimNode* CreateSubNode(
const QString& name, const AnimNodeType animNodeType, AZ::EntityId entityId = AZ::EntityId(),
AZ::Uuid componentTypeId = AZ::Uuid::CreateNull(), AZ::ComponentId componenId=AZ::InvalidComponentId);
virtual void RemoveSubNode(CTrackViewAnimNode* pSubNode);
// Create & remove sub tracks
virtual CTrackViewTrack* CreateTrack(const CAnimParamType& paramType);
virtual void RemoveTrack(CTrackViewTrack* pTrack);
// Add selected entities from scene to group node
virtual CTrackViewAnimNodeBundle AddSelectedEntities(const AZStd::vector<AnimParamType>& tracks);
// Add current layer to group node
virtual void AddCurrentLayer();
// Director related
virtual void SetAsActiveDirector();
virtual bool IsActiveDirector() const;
// Checks if anim node is part of active sequence and of an active director
virtual bool IsActive();
// Set as view camera
virtual void SetAsViewCamera();
// Name setter/getter
virtual const char* GetName() const override { return m_animNode->GetName(); }
virtual bool SetName(const char* pName) override;
virtual bool CanBeRenamed() const override;
// Node owner setter/getter
virtual void SetNodeEntityId(AZ::EntityId entityId);
virtual AZ::EntityId GetNodeEntityId(const bool bSearch = true);
AZ::EntityId GetAzEntityId() const { return m_animNode ? m_animNode->GetAzEntityId() : AZ::EntityId(); }
bool IsBoundToAzEntity() const { return m_animNode ? m_animNode->GetAzEntityId().IsValid(): false; }
// Snap time value to prev/next key in sequence
virtual bool SnapTimeToPrevKey(float& time) const override;
virtual bool SnapTimeToNextKey(float& time) const override;
// Expanded state interface
void SetExpanded(bool expanded) override;
bool GetExpanded() const override;
// Node getters
CTrackViewAnimNodeBundle GetAllAnimNodes();
CTrackViewAnimNodeBundle GetSelectedAnimNodes();
CTrackViewAnimNodeBundle GetAllOwnedNodes(AZ::EntityId entityId);
CTrackViewAnimNodeBundle GetAnimNodesByType(AnimNodeType animNodeType);
CTrackViewAnimNodeBundle GetAnimNodesByName(const char* pName);
// Track getters
virtual CTrackViewTrackBundle GetAllTracks();
virtual CTrackViewTrackBundle GetSelectedTracks();
virtual CTrackViewTrackBundle GetTracksByParam(const CAnimParamType& paramType) const;
// Key getters
virtual CTrackViewKeyBundle GetAllKeys() override;
virtual CTrackViewKeyBundle GetSelectedKeys() override;
virtual CTrackViewKeyBundle GetKeysInTimeRange(const float t0, const float t1) override;
// Type getters
AnimNodeType GetType() const;
// Flags
EAnimNodeFlags GetFlags() const;
bool AreFlagsSetOnNodeOrAnyParent(EAnimNodeFlags flagsToCheck) const;
// Disabled state
virtual void SetDisabled(bool bDisabled) override;
virtual bool IsDisabled() const override;
bool CanBeEnabled() const override;
// Return track assigned to the specified parameter.
CTrackViewTrack* GetTrackForParameter(const CAnimParamType& paramType, uint32 index = 0) const;
// Rotation/Position & Scale
void SetPos(const Vec3& position);
Vec3 GetPos() const { return m_animNode->GetPos(); }
void SetScale(const Vec3& scale);
Vec3 GetScale() const { return m_animNode->GetScale(); }
void SetRotation(const Quat& rotation);
Quat GetRotation() const { return m_animNode->GetRotate(); }
Quat GetRotation(float time) const { return m_animNode != nullptr ? m_animNode->GetRotate(time) : Quat(0,0,0,0); }
// Param
unsigned int GetParamCount() const;
CAnimParamType GetParamType(unsigned int index) const;
const char* GetParamName(const CAnimParamType& paramType) const;
bool IsParamValid(const CAnimParamType& param) const;
IAnimNode::ESupportedParamFlags GetParamFlags(const CAnimParamType& paramType) const;
AnimValueType GetParamValueType(const CAnimParamType& paramType) const;
void UpdateDynamicParams();
// Parameter getters/setters
template <class Type>
bool SetParamValue(const float time, const CAnimParamType& param, const Type& value)
{
AZ_Assert(m_animNode, "Expected valid m_animNode");
return m_animNode->SetParamValue(time, param, value);
}
template <class Type>
bool GetParamValue(const float time, const CAnimParamType& param, Type& value)
{
AZ_Assert(m_animNode, "Expected valid m_animNode");
return m_animNode->GetParamValue(time, param, value);
}
// Check if it's a group node
virtual bool IsGroupNode() const override;
// Generate a new node name
virtual QString GetAvailableNodeNameStartingWith(const QString& name) const;
// Copy/Paste nodes
virtual void CopyNodesToClipboard(const bool bOnlySelected, QWidget* context);
virtual bool PasteNodesFromClipboard(QWidget* context);
// Set new parent
virtual void SetNewParent(CTrackViewAnimNode* pNewParent);
// Check if this node may be moved to new parent
virtual bool IsValidReparentingTo(CTrackViewAnimNode* pNewParent);
int GetDefaultKeyTangentFlags() const { return m_animNode ? m_animNode->GetDefaultKeyTangentFlags() : SPLINE_KEY_TANGENT_UNIFIED; }
void SetComponent(AZ::ComponentId componentId, const AZ::Uuid& componentTypeId);
// returns the AZ::ComponentId of the component associated with this node if it is of type AnimNodeType::Component, InvalidComponentId otherwise
AZ::ComponentId GetComponentId() const;
// IAnimNodeOwner
void MarkAsModified() override;
// ~IAnimNodeOwner
// Compares all of the node's track values at the given time with the associated property value and
// sets a key at that time if they are different to match the latter
// Returns the number of keys set
int SetKeysForChangedTrackValues(float time) { return m_animNode->SetKeysForChangedTrackValues(time); }
// returns true if this node is associated with an AnimNodeType::AzEntity node and contains a component with the given id
bool ContainsComponentWithId(AZ::ComponentId componentId) const;
//////////////////////////////////////////////////////////////////////////
// AzToolsFramework::EditorEntityContextNotificationBus implementation
void OnStartPlayInEditor() override;
void OnStopPlayInEditor() override;
//~AzToolsFramework::EditorEntityContextNotificationBus implementation
//////////////////////////////////////////////////////////////////////////
// AZ::EntityBus
void OnEntityActivated(const AZ::EntityId& entityId) override;
void OnEntityDestruction(const AZ::EntityId& entityId) override;
//~AZ::EntityBus
//////////////////////////////////////////////////////////////////////////
//! AZ::TransformNotificationBus::Handler
void OnTransformChanged(const AZ::Transform& local, const AZ::Transform& world) override;
void OnParentChanged(AZ::EntityId oldParent, AZ::EntityId newParent) override;
void OnParentTransformWillChange(AZ::Transform oldTransform, AZ::Transform newTransform) override;
//////////////////////////////////////////////////////////////////////////
void OnEntityRemoved();
// Creates a sub-node for the given component. Returns a pointer to the created component sub-node
CTrackViewAnimNode* AddComponent(const AZ::Component* component, bool disabled);
// Depth-first search for TrackViewAnimNode associated with the given animNode. Returns the first match found or nullptr if not found
CTrackViewAnimNode* FindNodeByAnimNode(const IAnimNode* animNode);
protected:
IAnimNode* GetAnimNode() { return m_animNode.get(); }
private:
// Copy selected keys to XML representation for clipboard
virtual void CopyKeysToClipboard(XmlNodeRef& xmlNode, const bool bOnlySelectedKeys, const bool bOnlyFromSelectedTracks) override;
void CopyNodesToClipboardRec(CTrackViewAnimNode* pCurrentAnimNode, XmlNodeRef& xmlNode, const bool bOnlySelected);
void PasteTracksFrom(XmlNodeRef& xmlNodeWithTracks);
bool HasObsoleteTrackRec(CTrackViewNode* pCurrentNode) const;
CTrackViewTrackBundle GetTracks(const bool bOnlySelected, const CAnimParamType& paramType) const;
void PasteNodeFromClipboard(AZStd::map<int, IAnimNode*>& copiedIdToNodeMap, XmlNodeRef xmlNode);
void SetPosRotScaleTracksDefaultValues(bool positionAllowed = true, bool rotationAllowed = true, bool scaleAllowed = true);
void UpdateTrackGizmo();
bool CheckTrackAnimated(const CAnimParamType& paramType) const;
// IAnimNodeOwner
void OnNodeVisibilityChanged(IAnimNode* pNode, const bool bHidden) override;
void OnNodeReset(IAnimNode* pNode) override;
// ~IAnimNodeOwner
// ITransformDelegate
void MatrixInvalidated() override;
Vec3 GetTransformDelegatePos(const Vec3& realPos) const override;
Quat GetTransformDelegateRotation(const Quat& realRotation) const override;
Vec3 GetTransformDelegateScale(const Vec3& realScale) const override;
void SetTransformDelegatePos(const Vec3& position) override;
void SetTransformDelegateRotation(const Quat& rotation) override;
void SetTransformDelegateScale(const Vec3& scale) override;
// If those return true the base object uses its own transform instead
bool IsPositionDelegated() const override;
bool IsRotationDelegated() const override;
bool IsScaleDelegated() const override;
// ~ITransformDelegate
// Helper for Is<Position/Rotation/Scale>Delegated to call internally
bool IsTransformAnimParamTypeDelegated(AnimParamType animParamType) const;
// EntitySelectionEvents
void OnSelected() override;
void OnDeselected() override;
void OnSelectionChanged(bool selected);
void UpdateKeyDataAfterParentChanged(const AZ::Transform& oldParentWorldTM, const AZ::Transform& newParentWorldTM);
// Used to track Editor object listener registration
void RegisterEditorObjectListeners(AZ::EntityId entityId);
void UnRegisterEditorObjectListeners();
// Helper functions
static void RemoveChildNode(CTrackViewAnimNode* child);
static AZ::Transform GetEntityWorldTM(AZ::EntityId entityId);
static void SetParentsInChildren(CTrackViewAnimNode* currentNode);
IAnimSequence* m_animSequence;
AZStd::intrusive_ptr<IAnimNode> m_animNode;
AZ::EntityId m_nodeEntityId;
AZStd::unique_ptr<IAnimNodeAnimator> m_pNodeAnimator;
_smart_ptr<CGizmo> m_trackGizmo;
// used to stash the Editor sequence and node entity Ids when we switch to game mode from the editor
AZ::EntityId m_stashedAnimNodeEditorAzEntityId;
AZ::EntityId m_stashedAnimSequenceEditorAzEntityId;
// Used to track Editor object listener registration
AZ::EntityId m_entityIdListenerRegistered;
// used to return a const reference to a null Uuid
static const AZ::Uuid s_nullUuid;
};
@@ -0,0 +1,425 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "EditorDefs.h"
#include "TrackViewCurveEditor.h"
AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
#include "TrackView/ui_TrackViewCurveEditor.h"
AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING
#define IDC_TRACKVIEWGRAPH_CURVE 1
#define IDC_TIMELINE 2
#define IDC_HORIZON_SLIDER 3
#define IDC_VERTICAL_SLIDER 4
//! It's for mapping from a slider control range to a real zoom range, and vice versa.
#define SLIDER_MULTIPLIER 100.f
#define SLIDERRANGE_TO_ZOOM(SLIDERVALUE) (float)SLIDERVALUE / SLIDER_MULTIPLIER
#define ZOOMRANGE_TO_SLIDER(ZOOMVALUE) (int)(ZOOMVALUE * SLIDER_MULTIPLIER)
//////////////////////////////////////////////////////////////////////////
TrackViewCurveEditorDialog::TrackViewCurveEditorDialog(QWidget* parent)
: QWidget(parent)
{
m_widget = new CTrackViewCurveEditor(this);
QVBoxLayout* l = new QVBoxLayout;
l->setMargin(0);
l->addWidget(m_widget);
setLayout(l);
}
//////////////////////////////////////////////////////////////////////////
CTrackViewCurveEditor::CTrackViewCurveEditor(QWidget* parent)
: QWidget(parent)
, m_ui(new Ui::TrackViewCurveEditor)
{
m_ui->setupUi(this);
m_bLevelClosing = false;
m_bIgnoreSelfEvents = false;
GetIEditor()->RegisterNotifyListener(this);
GetIEditor()->GetAnimation()->AddListener(this);
m_timelineCtrl.SetTimeRange(Range(0, 1));
m_timelineCtrl.SetTicksTextScale(1.0f);
m_ui->m_wndSpline->SetTimelineCtrl(&m_timelineCtrl);
connect(&m_timelineCtrl, &TimelineWidget::change, this, &CTrackViewCurveEditor::OnTimelineChange);
connect(m_ui->m_wndSpline, &SplineWidget::change, this, &CTrackViewCurveEditor::OnSplineChange);
connect(m_ui->m_wndSpline, &SplineWidget::timeChange, this, &CTrackViewCurveEditor::OnSplineTimeMarkerChange);
connect(m_ui->buttonTangentAuto, &QToolButton::clicked, this, [&]() {OnSplineCmd(ID_TANGENT_AUTO); });
connect(m_ui->buttonTangentInZero, &QToolButton::clicked, this, [&]() {OnSplineCmd(ID_TANGENT_IN_ZERO); });
connect(m_ui->buttonTangentInStep, &QToolButton::clicked, this, [&]() {OnSplineCmd(ID_TANGENT_IN_STEP); });
connect(m_ui->buttonTangentInLinear, &QToolButton::clicked, this, [&]() {OnSplineCmd(ID_TANGENT_IN_LINEAR); });
connect(m_ui->buttonTangentOutZero, &QToolButton::clicked, this, [&]() {OnSplineCmd(ID_TANGENT_OUT_ZERO); });
connect(m_ui->buttonTangentOutStep, &QToolButton::clicked, this, [&]() {OnSplineCmd(ID_TANGENT_OUT_STEP); });
connect(m_ui->buttonTangentOutLinear, &QToolButton::clicked, this, [&]() {OnSplineCmd(ID_TANGENT_OUT_LINEAR); });
connect(m_ui->buttonSplineFitX, &QToolButton::clicked, this, [&]() {OnSplineCmd(ID_SPLINE_FIT_X); });
connect(m_ui->buttonSplineFitY, &QToolButton::clicked, this, [&]() {OnSplineCmd(ID_SPLINE_FIT_Y); });
connect(m_ui->buttonSplineSnapGridX, &QToolButton::clicked, this, [&]() {OnSplineCmd(ID_SPLINE_SNAP_GRID_X); });
connect(m_ui->buttonSplineSnapGridY, &QToolButton::clicked, this, [&]() {OnSplineCmd(ID_SPLINE_SNAP_GRID_Y); });
connect(m_ui->buttonTangentUnify, &QToolButton::clicked, this, [&]() {OnSplineCmd(ID_TANGENT_UNIFY); });
connect(m_ui->buttonFreezeKeys, &QToolButton::clicked, this, [&]() {OnSplineCmd(ID_FREEZE_KEYS); });
connect(m_ui->buttonFreezeTangents, &QToolButton::clicked, this, [&]() {OnSplineCmd(ID_FREEZE_TANGENTS); });
}
//////////////////////////////////////////////////////////////////////////
CTrackViewCurveEditor::~CTrackViewCurveEditor()
{
GetIEditor()->GetAnimation()->RemoveListener(this);
GetIEditor()->UnregisterNotifyListener(this);
}
// CTrackViewGraph message handlers
//////////////////////////////////////////////////////////////////////////
void CTrackViewCurveEditor::OnSequenceChanged([[maybe_unused]] CTrackViewSequence* pSequence)
{
UpdateSplines();
update();
}
//////////////////////////////////////////////////////////////////////////
void CTrackViewCurveEditor::OnEditorNotifyEvent(EEditorNotifyEvent event)
{
if (m_bIgnoreSelfEvents)
{
return;
}
switch (event)
{
case eNotify_OnCloseScene:
m_ui->m_wndSpline->RemoveAllSplines();
m_bLevelClosing = true;
break;
case eNotify_OnBeginNewScene:
case eNotify_OnBeginSceneOpen:
m_bLevelClosing = false;
break;
}
}
//////////////////////////////////////////////////////////////////////////
void CTrackViewCurveEditor::UpdateSplines()
{
CTrackViewSequence* pSequence = GetIEditor()->GetAnimation()->GetSequence();
if (!pSequence || m_bLevelClosing)
{
// No sequence selected, remove any splines.
if (nullptr != m_ui)
{
if (nullptr != m_ui->m_wndSpline)
{
m_ui->m_wndSpline->RemoveAllSplines();
}
}
return;
}
CTrackViewTrackBundle selectedTracks;
selectedTracks = pSequence->GetSelectedTracks();
std::set<CTrackViewTrack*> oldTracks;
for (auto iter = m_ui->m_wndSpline->GetTracks().begin(); iter != m_ui->m_wndSpline->GetTracks().end(); ++iter)
{
CTrackViewTrack* pTrack = *iter;
oldTracks.insert(pTrack);
}
std::set<CTrackViewTrack*> newTracks;
if (selectedTracks.AreAllOfSameType())
{
for (int i = 0; i < selectedTracks.GetCount(); i++)
{
CTrackViewTrack* pTrack = selectedTracks.GetTrack(i);
if (pTrack->IsCompoundTrack())
{
unsigned int numChildTracks = pTrack->GetChildCount();
for (unsigned int ii = 0; ii < numChildTracks; ++ii)
{
CTrackViewTrack* pChildTrack = static_cast<CTrackViewTrack*>(pTrack->GetChild(ii));
newTracks.insert(pChildTrack);
}
}
else
{
newTracks.insert(pTrack);
}
}
}
if (oldTracks == newTracks)
{
return;
}
m_ui->m_wndSpline->RemoveAllSplines();
for (auto iter = newTracks.begin(); iter != newTracks.end(); ++iter)
{
AddSpline(*iter);
}
UpdateTimeRange(pSequence);
// If it is a rotation track, adjust the default value range properly to accommodate some degree values.
if (selectedTracks.HasRotationTrack())
{
m_ui->m_wndSpline->SetDefaultValueRange(Range(-180.0f, 180.0f));
}
else
{
m_ui->m_wndSpline->SetDefaultValueRange(Range(-1.1f, 1.1f));
}
ResetSplineCtrlZoomLevel();
return;
}
//////////////////////////////////////////////////////////////////////////
void CTrackViewCurveEditor::AddSpline(CTrackViewTrack* pTrack)
{
if (!pTrack->GetSpline())
{
return;
}
const int subTrackIndex = pTrack->GetSubTrackIndex();
if (subTrackIndex >= 0)
{
QColor trackColor = QColor(255, 0, 0);
switch (subTrackIndex)
{
case 0:
trackColor = QColor(255, 0, 0);
break;
case 1:
trackColor = QColor(0, 255, 0);
break;
case 2:
trackColor = QColor(0, 0, 255);
break;
case 3:
trackColor = QColor(255, 255, 0);
break;
}
m_ui->m_wndSpline->AddSpline(pTrack->GetSpline(), pTrack, trackColor);
}
else
{
QColor afColorArray[4];
afColorArray[0] = QColor(255, 0, 0);
afColorArray[1] = QColor(0, 255, 0);
afColorArray[2] = QColor(0, 0, 255);
afColorArray[3] = QColor(255, 0, 255); //Pink... so you know it's wrong if you see it.
m_ui->m_wndSpline->AddSpline(pTrack->GetSpline(), pTrack, afColorArray);
}
}
void CTrackViewCurveEditor::showEvent(QShowEvent* event)
{
QWidget::showEvent(event);
OnSplineCmdUpdateUI();
}
//////////////////////////////////////////////////////////////////////////
void CTrackViewCurveEditor::OnSplineChange()
{
CTrackViewSequence* pSequence = GetIEditor()->GetAnimation()->GetSequence();
if (pSequence)
{
pSequence->OnKeysChanged();
}
// In the end, focus this again in order to properly catch 'KeyDown' messages.
m_ui->m_wndSpline->setFocus();
}
//////////////////////////////////////////////////////////////////////////
void CTrackViewCurveEditor::OnSplineCmd(UINT cmd)
{
m_ui->m_wndSpline->OnUserCommand(cmd);
OnSplineCmdUpdateUI();
}
//////////////////////////////////////////////////////////////////////////
void CTrackViewCurveEditor::OnSplineCmdUpdateUI()
{
CTrackViewSequence* pSequence = GetIEditor()->GetAnimation()->GetSequence();
if (m_bLevelClosing || !pSequence)
{
return;
}
m_ui->buttonSplineSnapGridX->setChecked(m_ui->m_wndSpline->IsSnapTime());
m_ui->buttonSplineSnapGridY->setChecked(m_ui->m_wndSpline->IsSnapValue());
m_ui->buttonTangentUnify->setChecked(m_ui->m_wndSpline->IsUnifiedKeyCurrentlySelected());
m_ui->buttonFreezeKeys->setChecked(m_ui->m_wndSpline->IsKeysFrozen());
m_ui->buttonFreezeTangents->setChecked(m_ui->m_wndSpline->IsTangentsFrozen());
}
//////////////////////////////////////////////////////////////////////////
void CTrackViewCurveEditor::OnTimeChanged(float newTime)
{
m_ui->m_wndSpline->SetTimeMarker(newTime);
m_ui->m_wndSpline->update();
}
//////////////////////////////////////////////////////////////////////////
void CTrackViewCurveEditor::SetEditLock(bool bLock)
{
m_ui->m_wndSpline->SetEditLock(bLock);
}
//////////////////////////////////////////////////////////////////////////
void CTrackViewCurveEditor::OnTimelineChange()
{
float fTime = m_timelineCtrl.GetTimeMarker();
GetIEditor()->GetAnimation()->SetTime(fTime);
}
//////////////////////////////////////////////////////////////////////////
void CTrackViewCurveEditor::OnSplineTimeMarkerChange()
{
float fTime = m_ui->m_wndSpline->GetTimeMarker();
GetIEditor()->GetAnimation()->SetTime(fTime);
}
//////////////////////////////////////////////////////////////////////////
void CTrackViewCurveEditor::SetFPS(float fps)
{
m_timelineCtrl.SetFPS(fps);
}
//////////////////////////////////////////////////////////////////////////
float CTrackViewCurveEditor::GetFPS() const
{
return m_timelineCtrl.GetFPS();
}
//////////////////////////////////////////////////////////////////////////
void CTrackViewCurveEditor::SetTickDisplayMode(ETVTickMode mode)
{
if (mode == eTVTickMode_InFrames)
{
m_timelineCtrl.SetMarkerStyle(TimelineWidget::MARKER_STYLE_FRAMES);
m_ui->m_wndSpline->SetTooltipValueScale(GetFPS(), 1.0f);
}
else if (mode == eTVTickMode_InSeconds)
{
m_timelineCtrl.SetMarkerStyle(TimelineWidget::MARKER_STYLE_SECONDS);
m_ui->m_wndSpline->SetTooltipValueScale(1.0f, 1.0f);
}
m_timelineCtrl.update();
}
//////////////////////////////////////////////////////////////////////////
void CTrackViewCurveEditor::ResetSplineCtrlZoomLevel()
{
m_ui->m_wndSpline->FitSplineToViewHeight();
m_ui->m_wndSpline->FitSplineToViewWidth();
}
//////////////////////////////////////////////////////////////////////////
void CTrackViewCurveEditor::OnKeysChanged([[maybe_unused]] CTrackViewSequence* pSequence)
{
m_ui->m_wndSpline->update();
}
//////////////////////////////////////////////////////////////////////////
void CTrackViewCurveEditor::OnKeyAdded(CTrackViewKeyHandle& addedKeyHandle)
{
EAnimCurveType trType = addedKeyHandle.GetTrack()->GetCurveType();
if (trType == eAnimCurveType_BezierFloat)
{
// we query the added key's track to find the default tangent flags to use for newly created keys
const int tangentFlagsForNewKeys = addedKeyHandle.GetTrack()->GetAnimNode()->GetDefaultKeyTangentFlags();
I2DBezierKey bezierKey;
addedKeyHandle.GetKey(&bezierKey);
// clear any existing in and out tangent flags
bezierKey.flags &= ~SPLINE_KEY_TANGENT_ALL_MASK;
// set tangent flags to the default tangent flags used for the track's animNode and save them
bezierKey.flags |= tangentFlagsForNewKeys;
addedKeyHandle.SetKey(&bezierKey);
}
}
//////////////////////////////////////////////////////////////////////////
void CTrackViewCurveEditor::OnKeySelectionChanged([[maybe_unused]] CTrackViewSequence* pSequence)
{
if (isVisible())
{
m_ui->m_wndSpline->update();
m_ui->buttonTangentUnify->setChecked(m_ui->m_wndSpline->IsUnifiedKeyCurrentlySelected());
}
}
//////////////////////////////////////////////////////////////////////////
void CTrackViewCurveEditor::OnNodeChanged([[maybe_unused]] CTrackViewNode* pNode, ENodeChangeType type)
{
if (isVisible() && type == ITrackViewSequenceListener::eNodeChangeType_Removed)
{
UpdateSplines();
}
}
//////////////////////////////////////////////////////////////////////////
void CTrackViewCurveEditor::OnNodeSelectionChanged([[maybe_unused]] CTrackViewSequence* pSequence)
{
if (isVisible())
{
UpdateSplines();
}
}
void CTrackViewCurveEditor::OnSequenceSettingsChanged(CTrackViewSequence* pSequence)
{
if (isVisible())
{
UpdateTimeRange(pSequence);
m_timelineCtrl.update();
m_ui->m_wndSpline->update();
}
}
void CTrackViewCurveEditor::UpdateTimeRange(CTrackViewSequence* pSequence)
{
Range timeRange = pSequence->GetTimeRange();
m_ui->m_wndSpline->SetTimeRange(timeRange);
m_timelineCtrl.SetTimeRange(timeRange);
m_ui->m_wndSpline->SetValueRange(Range(-2000.0f, 2000.0f));
}
void CTrackViewCurveEditor::SetPlayCallback(const std::function<void()>& callback)
{
m_ui->m_wndSpline->SetPlayCallback(callback);
m_timelineCtrl.SetPlayCallback(callback);
}
CTrackViewSplineCtrl& CTrackViewCurveEditor::GetSplineCtrl()
{
return *m_ui->m_wndSpline;
}
@@ -0,0 +1,127 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef CRYINCLUDE_EDITOR_TRACKVIEW_TRACKVIEWCURVEEDITOR_H
#define CRYINCLUDE_EDITOR_TRACKVIEW_TRACKVIEWCURVEEDITOR_H
#pragma once
#include "TrackViewDopeSheetBase.h"
#include "TrackViewSplineCtrl.h"
#include "Controls/TimelineCtrl.h"
#include "TrackViewTimeline.h"
#include <QWidget>
namespace Ui
{
class TrackViewCurveEditor;
}
/** CTrackViewGraph dialog.
Placed at the same position as tracks dialog, and display spline graphs of track.
*/
class CTrackViewCurveEditor
: public QWidget
, public IAnimationContextListener
, public IEditorNotifyListener
, public ITrackViewSequenceListener
{
friend class TrackViewCurveEditorDialog;
public:
CTrackViewCurveEditor(QWidget* parent);
virtual ~CTrackViewCurveEditor();
void SetEditLock(bool bLock);
void SetFPS(float fps);
float GetFPS() const;
void SetTickDisplayMode(ETVTickMode mode);
CTrackViewSplineCtrl& GetSplineCtrl();
void ResetSplineCtrlZoomLevel();
void SetPlayCallback(const std::function<void()>& callback);
// IAnimationContextListener
virtual void OnSequenceChanged(CTrackViewSequence* pNewSequence);
virtual void OnTimeChanged(float newTime);
protected:
void showEvent(QShowEvent* event) override;
private:
void OnSplineChange();
void OnSplineCmd(UINT cmd);
void OnSplineCmdUpdateUI();
void OnTimelineChange();
void OnSplineTimeMarkerChange();
// IEditorNotifyListener
virtual void OnEditorNotifyEvent(EEditorNotifyEvent event) override;
//ITrackViewSequenceListener
void OnKeysChanged(CTrackViewSequence* pSequence) override;
void OnKeyAdded(CTrackViewKeyHandle& addedKeyHandle) override;
void OnKeySelectionChanged(CTrackViewSequence* pSequence) override;
void OnNodeChanged(CTrackViewNode* pNode, ENodeChangeType type) override;
void OnNodeSelectionChanged(CTrackViewSequence* pSequence) override;
void OnSequenceSettingsChanged(CTrackViewSequence* pSequence) override;
//~ITrackViewSequenceListener
void UpdateSplines();
void UpdateTimeRange(CTrackViewSequence* pSequence);
void AddSpline(CTrackViewTrack* pTrack);
TrackView::CTrackViewTimelineWidget m_timelineCtrl;
bool m_bIgnoreSelfEvents;
bool m_bLevelClosing;
QScopedPointer<Ui::TrackViewCurveEditor> m_ui;
};
class TrackViewCurveEditorDialog
: public QWidget
, public ITrackViewSequenceListener
, public IAnimationContextListener
{
public:
TrackViewCurveEditorDialog(QWidget* parent);
virtual ~TrackViewCurveEditorDialog() {}
void SetPlayCallback(const std::function<void()>& callback) { m_widget->SetPlayCallback(callback); }
void SetEditLock(bool bLock) { m_widget->SetEditLock(bLock); }
CTrackViewSplineCtrl& GetSplineCtrl(){ return m_widget->GetSplineCtrl(); }
void SetFPS(float fps) { m_widget->SetFPS(fps); }
float GetFPS() const { return m_widget->GetFPS(); }
void SetTickDisplayMode(ETVTickMode mode) { m_widget->SetTickDisplayMode(mode); }
virtual void OnSequenceChanged(CTrackViewSequence* pNewSequence) { m_widget->OnSequenceChanged(pNewSequence); }
virtual void OnTimeChanged(float newTime) { m_widget->OnTimeChanged(newTime); }
// ITrackViewSequenceListener delegation to m_widget
void OnKeysChanged(CTrackViewSequence* pSequence) override { m_widget->OnKeysChanged(pSequence); }
void OnKeyAdded(CTrackViewKeyHandle& addedKeyHandle) override { m_widget->OnKeyAdded(addedKeyHandle); }
void OnKeySelectionChanged(CTrackViewSequence* pSequence) override { m_widget->OnKeySelectionChanged(pSequence); }
void OnNodeChanged(CTrackViewNode* pNode, ENodeChangeType type) override { m_widget->OnNodeChanged(pNode, type); }
void OnNodeSelectionChanged(CTrackViewSequence* pSequence) override { m_widget->OnNodeSelectionChanged(pSequence); }
void OnSequenceSettingsChanged(CTrackViewSequence* pSequence) override { m_widget->OnSequenceSettingsChanged(pSequence); }
//~ITrackViewSequenceListener
private:
CTrackViewCurveEditor* m_widget;
};
#endif // CRYINCLUDE_EDITOR_TRACKVIEW_TRACKVIEWCURVEEDITOR_H
@@ -0,0 +1,431 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>TrackViewCurveEditor</class>
<widget class="QWidget" name="TrackViewCurveEditor">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>588</width>
<height>285</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="0">
<widget class="CTrackViewSplineCtrl" name="m_wndSpline" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="0" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>0</number>
</property>
<item>
<widget class="QToolButton" name="buttonTangentAuto">
<property name="toolTip">
<string>Set In/Out Tangents to Auto</string>
</property>
<property name="icon">
<iconset resource="TrackViewDialog.qrc">
<normaloff>:/TrackViewCurveEditor/spline_edit_bar_00.png</normaloff>:/TrackViewCurveEditor/spline_edit_bar_00.png</iconset>
</property>
<property name="iconSize">
<size>
<width>18</width>
<height>18</height>
</size>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line">
<property name="minimumSize">
<size>
<width>6</width>
<height>0</height>
</size>
</property>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="buttonTangentInZero">
<property name="toolTip">
<string>Set In Tangent to Zero</string>
</property>
<property name="icon">
<iconset resource="TrackViewDialog.qrc">
<normaloff>:/TrackViewCurveEditor/spline_edit_bar_01.png</normaloff>:/TrackViewCurveEditor/spline_edit_bar_01.png</iconset>
</property>
<property name="iconSize">
<size>
<width>18</width>
<height>18</height>
</size>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="buttonTangentInStep">
<property name="toolTip">
<string>Set In Tangent to Step</string>
</property>
<property name="icon">
<iconset resource="TrackViewDialog.qrc">
<normaloff>:/TrackViewCurveEditor/spline_edit_bar_02.png</normaloff>:/TrackViewCurveEditor/spline_edit_bar_02.png</iconset>
</property>
<property name="iconSize">
<size>
<width>18</width>
<height>18</height>
</size>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="buttonTangentInLinear">
<property name="toolTip">
<string>Set In Tangent to Linear</string>
</property>
<property name="icon">
<iconset resource="TrackViewDialog.qrc">
<normaloff>:/TrackViewCurveEditor/spline_edit_bar_03.png</normaloff>:/TrackViewCurveEditor/spline_edit_bar_03.png</iconset>
</property>
<property name="iconSize">
<size>
<width>18</width>
<height>18</height>
</size>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line_2">
<property name="minimumSize">
<size>
<width>6</width>
<height>0</height>
</size>
</property>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="buttonTangentOutZero">
<property name="toolTip">
<string>Set Out Tangent to Zero</string>
</property>
<property name="icon">
<iconset resource="TrackViewDialog.qrc">
<normaloff>:/TrackViewCurveEditor/spline_edit_bar_04.png</normaloff>:/TrackViewCurveEditor/spline_edit_bar_04.png</iconset>
</property>
<property name="iconSize">
<size>
<width>18</width>
<height>18</height>
</size>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="buttonTangentOutStep">
<property name="toolTip">
<string>Set Out Tangent to Step</string>
</property>
<property name="icon">
<iconset resource="TrackViewDialog.qrc">
<normaloff>:/TrackViewCurveEditor/spline_edit_bar_05.png</normaloff>:/TrackViewCurveEditor/spline_edit_bar_05.png</iconset>
</property>
<property name="iconSize">
<size>
<width>18</width>
<height>18</height>
</size>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="buttonTangentOutLinear">
<property name="toolTip">
<string>Set Out Tangent to Linear</string>
</property>
<property name="icon">
<iconset resource="TrackViewDialog.qrc">
<normaloff>:/TrackViewCurveEditor/spline_edit_bar_06.png</normaloff>:/TrackViewCurveEditor/spline_edit_bar_06.png</iconset>
</property>
<property name="iconSize">
<size>
<width>18</width>
<height>18</height>
</size>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line_3">
<property name="minimumSize">
<size>
<width>6</width>
<height>0</height>
</size>
</property>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="buttonSplineFitX">
<property name="toolTip">
<string>Fit splines to the visible width</string>
</property>
<property name="icon">
<iconset resource="TrackViewDialog.qrc">
<normaloff>:/TrackViewCurveEditor/spline_edit_bar_07.png</normaloff>:/TrackViewCurveEditor/spline_edit_bar_07.png</iconset>
</property>
<property name="iconSize">
<size>
<width>18</width>
<height>18</height>
</size>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="buttonSplineFitY">
<property name="toolTip">
<string>Fit splines to the visible height</string>
</property>
<property name="icon">
<iconset resource="TrackViewDialog.qrc">
<normaloff>:/TrackViewCurveEditor/spline_edit_bar_08.png</normaloff>:/TrackViewCurveEditor/spline_edit_bar_08.png</iconset>
</property>
<property name="iconSize">
<size>
<width>18</width>
<height>18</height>
</size>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line_4">
<property name="minimumSize">
<size>
<width>6</width>
<height>0</height>
</size>
</property>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="buttonSplineSnapGridX">
<property name="toolTip">
<string>Snap to time grid</string>
</property>
<property name="icon">
<iconset resource="TrackViewDialog.qrc">
<normaloff>:/TrackViewCurveEditor/spline_edit_bar_09.png</normaloff>:/TrackViewCurveEditor/spline_edit_bar_09.png</iconset>
</property>
<property name="iconSize">
<size>
<width>18</width>
<height>18</height>
</size>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="buttonSplineSnapGridY">
<property name="toolTip">
<string>Snap to value grid</string>
</property>
<property name="icon">
<iconset resource="TrackViewDialog.qrc">
<normaloff>:/TrackViewCurveEditor/spline_edit_bar_10.png</normaloff>:/TrackViewCurveEditor/spline_edit_bar_10.png</iconset>
</property>
<property name="iconSize">
<size>
<width>18</width>
<height>18</height>
</size>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line_5">
<property name="minimumSize">
<size>
<width>6</width>
<height>0</height>
</size>
</property>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="buttonTangentUnify">
<property name="toolTip">
<string>Unify/Break Tangent Handles</string>
</property>
<property name="icon">
<iconset resource="TrackViewDialog.qrc">
<normaloff>:/TrackViewCurveEditor/spline_edit_bar_11.png</normaloff>:/TrackViewCurveEditor/spline_edit_bar_11.png</iconset>
</property>
<property name="iconSize">
<size>
<width>18</width>
<height>18</height>
</size>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line_6">
<property name="minimumSize">
<size>
<width>6</width>
<height>0</height>
</size>
</property>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="buttonFreezeKeys">
<property name="toolTip">
<string>Freeze/Unfreeze all keys</string>
</property>
<property name="icon">
<iconset resource="TrackViewDialog.qrc">
<normaloff>:/TrackViewCurveEditor/spline_edit_bar_12.png</normaloff>:/TrackViewCurveEditor/spline_edit_bar_12.png</iconset>
</property>
<property name="iconSize">
<size>
<width>18</width>
<height>18</height>
</size>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="buttonFreezeTangents">
<property name="toolTip">
<string>Freeze/Unfreeze all tangent handles</string>
</property>
<property name="icon">
<iconset resource="TrackViewDialog.qrc">
<normaloff>:/TrackViewCurveEditor/spline_edit_bar_13.png</normaloff>:/TrackViewCurveEditor/spline_edit_bar_13.png</iconset>
</property>
<property name="iconSize">
<size>
<width>18</width>
<height>18</height>
</size>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>CTrackViewSplineCtrl</class>
<extends>QWidget</extends>
<header>TrackView/TrackViewSplineCtrl.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources>
<include location="TrackViewDialog.qrc"/>
</resources>
<connections/>
</ui>
File diff suppressed because it is too large Load Diff
+276
View File
@@ -0,0 +1,276 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
// Description : CTrackViewDialog Implementation file.
#ifndef CRYINCLUDE_EDITOR_TRACKVIEW_TRACKVIEWDIALOG_H
#define CRYINCLUDE_EDITOR_TRACKVIEW_TRACKVIEWDIALOG_H
#pragma once
#if !defined(Q_MOC_RUN)
#include "IMovieSystem.h"
#include "TrackViewNodes.h"
#include "TrackViewDopeSheetBase.h"
#include "TrackViewCurveEditor.h"
#include "TrackViewKeyPropertiesDlg.h"
#include "TrackViewSequence.h"
#include "TrackViewSequenceManager.h"
#include "AnimationContext.h"
#include <AzCore/Component/EntityBus.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
#include <QMainWindow>
#endif
class QSplitter;
class QComboBox;
class QLabel;
class CMovieCallback;
class CTrackViewFindDlg;
class CTrackViewDialog
: public QMainWindow
, public IAnimationContextListener
, public IEditorNotifyListener
, public ITrackViewSequenceListener
, public ITrackViewSequenceManagerListener
, public AZ::EntitySystemBus::Handler
, private AzToolsFramework::ToolsApplicationNotificationBus::Handler
, IUndoManagerListener
{
Q_OBJECT
public:
friend CMovieCallback;
CTrackViewDialog(QWidget* pParent = NULL);
~CTrackViewDialog();
static void RegisterViewClass();
static const GUID& GetClassID();
static CTrackViewDialog* GetCurrentInstance() { return s_pTrackViewDialog; }
void InvalidateDopeSheet();
void Update();
void ReloadSequences();
void InvalidateSequence();
void UpdateSequenceLockStatus();
// IAnimationContextListener
virtual void OnSequenceChanged(CTrackViewSequence* pNewSequence) override;
// ITrackViewSequenceListener
virtual void OnSequenceSettingsChanged(CTrackViewSequence* pSequence) override;
void UpdateDopeSheetTime(CTrackViewSequence* pSequence);
const CTrackViewDopeSheetBase& GetTrackViewDopeSheet() const { return *m_wndDopeSheet; }
const AZStd::vector<AnimParamType>& GetDefaultTracksForEntityNode() const { return m_defaultTracksForEntityNode; }
bool IsDoingUndoOperation() const { return m_bDoingUndoOperation; }
//////////////////////////////////////////////////////////////////////////
// AZ::EntitySystemBus
void OnEntityDestruction(const AZ::EntityId& entityId) override;
//~AZ::EntitySystemBus
public: // static functions
static QString GetEntityIdAsString(const AZ::EntityId& entityId) { return QString::number(static_cast<AZ::u64>(entityId)); }
protected slots:
void OnGoToPrevKey();
void OnGoToNextKey();
void OnAddKey();
void OnDelKey();
void OnMoveKey();
void OnSlideKey();
void OnScaleKey();
void OnSyncSelectedTracksToBase();
void OnSyncSelectedTracksFromBase();
void OnAddSequence();
void OnExportFBXSequence();
void OnExportNodeKeysGlobalTime();
void OnDelSequence();
void OnEditSequence();
void OnSequenceComboBox();
void OnAddSelectedNode();
void OnAddDirectorNode();
void OnFindNode();
void OnRecord();
void OnAutoRecord();
void OnAutoRecordStep();
void OnGoToStart();
void OnGoToEnd();
void OnPlay();
void OnPlaySetScale();
void OnStop();
void OnStopHardReset();
void OnPause();
void OnLoop();
void OnSnapNone();
void OnSnapMagnet();
void OnSnapFrame();
void OnSnapTick();
void OnSnapFPS();
void OnCustomizeTrackColors();
void OnBatchRender();
void OnModeDopeSheet();
void OnModeCurveEditor();
void OnOpenCurveEditor();
void OnViewTickInSeconds();
void OnViewTickInFrames();
void OnTracksToolBar();
void OnToggleDisable();
void OnToggleMute();
void OnMuteAll();
void OnUnmuteAll();
protected:
void keyPressEvent(QKeyEvent* event) override;
#if defined(AZ_PLATFORM_WINDOWS)
bool nativeEvent(const QByteArray &eventType, void *message, long *result) override;
#endif
bool event(QEvent* event) override;
private slots:
void ReadLayouts();
void FillAddSelectedEntityMenu();
private:
enum class ViewMode
{
TrackView = 1,
CurveEditor = 2,
Both = 3
};
void setViewMode(ViewMode);
void UpdateActions();
void ReloadSequencesComboBox();
void UpdateTracksToolBar();
void ClearTracksToolBar();
void AddButtonToTracksToolBar(const CAnimParamType& paramId, const QIcon& hIcon, const QString& title);
void SetNodeForTracksToolBar(CTrackViewAnimNode* pNode) { m_pNodeForTracksToolBar = pNode; }
void SetEditLock(bool bLock);
void OnGameOrSimModeLock(bool lock);
void InitMenu();
void InitToolbar();
void InitSequences();
void OnAddEntityNodeMenu();
void OnEditorNotifyEvent(EEditorNotifyEvent event) override;
BOOL OnInitDialog();
void SaveLayouts();
void SaveMiscSettings() const;
void ReadMiscSettings();
void SaveTrackColors() const;
void ReadTrackColors();
void SetCursorPosText(float fTime);
#if defined(AZ_PLATFORM_WINDOWS)
bool processRawInput(MSG* pMsg);
#endif
virtual void OnNodeSelectionChanged(CTrackViewSequence* pSequence) override;
virtual void OnNodeRenamed(CTrackViewNode* pNode, const char* pOldName) override;
void OnSequenceAdded(CTrackViewSequence* pSequence) override;
void OnSequenceRemoved(CTrackViewSequence* pSequence) override;
void AddSequenceListeners(CTrackViewSequence* sequence);
void RemoveSequenceListeners(CTrackViewSequence* sequence);
void AddDialogListeners();
void RemoveDialogListeners();
virtual void BeginUndoTransaction();
virtual void EndUndoTransaction();
void SaveCurrentSequenceToFBX();
void SaveSequenceTimingToXML();
// ToolsApplicationNotificationBus ...
void AfterEntitySelectionChanged(
const AzToolsFramework::EntityIdList& newlySelectedEntities,
const AzToolsFramework::EntityIdList& newlyDeselectedEntities) override;
// Instance
static CTrackViewDialog* s_pTrackViewDialog;
// GUI
QSplitter* m_wndSplitter;
CTrackViewNodesCtrl* m_wndNodesCtrl;
CTrackViewDopeSheetBase* m_wndDopeSheet;
QDockWidget* m_wndCurveEditorDock;
TrackViewCurveEditorDialog* m_wndCurveEditor;
CTrackViewKeyPropertiesDlg* m_wndKeyProperties;
CTrackViewFindDlg* m_findDlg;
QToolBar* m_mainToolBar;
QToolBar* m_keysToolBar;
QToolBar* m_playToolBar;
QToolBar* m_viewToolBar;
QToolBar* m_tracksToolBar;
QComboBox* m_sequencesComboBox;
QLabel* m_cursorPos;
QLabel* m_activeCamStatic;
// CryMovie
CMovieCallback* m_pMovieCallback;
// Current sequence
AZ::EntityId m_currentSequenceEntityId;
// State
bool m_bRecord;
bool m_bAutoRecord;
bool m_bPlay;
bool m_bPause;
bool m_bNeedReloadSequence;
bool m_bIgnoreUpdates;
bool m_bDoingUndoOperation;
bool m_lazyInitDone;
bool m_bEditLock;
bool m_enteringGameOrSimModeLock = false;
bool m_needReAddListeners = false;
float m_fLastTime;
float m_fAutoRecordStep;
CTrackViewAnimNode* m_pNodeForTracksToolBar;
int m_currentToolBarParamTypeId;
std::vector<CAnimParamType> m_toolBarParamTypes;
// Default tracks menu
AZStd::vector<AnimParamType> m_defaultTracksForEntityNode;
QHash<int, QAction*> m_actions;
ViewMode m_lastMode = ViewMode::TrackView;
};
#endif // CRYINCLUDE_EDITOR_TRACKVIEW_TRACKVIEWDIALOG_H
+110
View File
@@ -0,0 +1,110 @@
<RCC>
<qresource prefix="/nodes">
<file>tvnodes-00.png</file>
<file>tvnodes-01.png</file>
<file>tvnodes-02.png</file>
<file>tvnodes-03.png</file>
<file>tvnodes-04.png</file>
<file>tvnodes-05.png</file>
<file>tvnodes-06.png</file>
<file>tvnodes-07.png</file>
<file>tvnodes-08.png</file>
<file>tvnodes-09.png</file>
<file>tvnodes-10.png</file>
<file>tvnodes-11.png</file>
<file>tvnodes-12.png</file>
<file>tvnodes-13.png</file>
<file>tvnodes-14.png</file>
<file>tvnodes-15.png</file>
<file>tvnodes-16.png</file>
<file>tvnodes-17.png</file>
<file>tvnodes-18.png</file>
<file>tvnodes-19.png</file>
<file>tvnodes-20.png</file>
<file>tvnodes-21.png</file>
<file>tvnodes-22.png</file>
<file>tvnodes-23.png</file>
<file>tvnodes-24.png</file>
<file>tvnodes-25.png</file>
<file>tvnodes-26.png</file>
<file>tvnodes-27.png</file>
<file>tvnodes-28.png</file>
<file>tvnodes-29.png</file>
</qresource>
<qresource prefix="/Trackview/main">
<file>tvmain-00.png</file>
<file>tvmain-01.png</file>
<file>tvmain-02.png</file>
<file>tvmain-03.png</file>
<file>tvmain-04.png</file>
<file>tvmain-05.png</file>
<file>tvmain-06.png</file>
<file>tvmain-07.png</file>
<file>tvmain-08.png</file>
<file>tvmain-09.png</file>
</qresource>
<qresource prefix="/Trackview/view">
<file>tvview-00.png</file>
<file>tvview-01.png</file>
<file>tvview-02.png</file>
</qresource>
<qresource prefix="/Trackview/play">
<file>tvplay-00.png</file>
<file>tvplay-01.png</file>
<file>tvplay-02.png</file>
<file>tvplay-03.png</file>
<file>tvplay-04.png</file>
<file>tvplay-05.png</file>
<file>tvplay-06.png</file>
<file>tvplay-07.png</file>
<file>tvplay-08.png</file>
<file>tvplay-09.png</file>
<file>tvplay-10.png</file>
</qresource>
<qresource prefix="/Trackview/keys">
<file>tvkeys-00.png</file>
<file>tvkeys-01.png</file>
<file>tvkeys-02.png</file>
<file>tvkeys-03.png</file>
<file>tvkeys-04.png</file>
<file>tvkeys-05.png</file>
<file>tvkeys-06.png</file>
<file>tvkeys-07.png</file>
<file>tvkeys-08.png</file>
<file>tvkeys-09.png</file>
<file>tvkeys-10.png</file>
<file>tvkeys-11.png</file>
<file>tvkeys-12.png</file>
</qresource>
<qresource prefix="/Trackview/marker">
<file>bmp00016_00.png</file>
<file>bmp00016_01.png</file>
</qresource>
<qresource prefix="/Trackview">
<file>trackview_keys_00.png</file>
<file>trackview_keys_01.png</file>
<file>trackview_keys_02.png</file>
<file>trackview_keys_03.png</file>
<file>clapperboard_cancel.png</file>
<file>clapperboard_ready.png</file>
</qresource>
<qresource prefix="/TrackViewCurveEditor">
<file>spline_edit_bar_00.png</file>
<file>spline_edit_bar_01.png</file>
<file>spline_edit_bar_02.png</file>
<file>spline_edit_bar_03.png</file>
<file>spline_edit_bar_04.png</file>
<file>spline_edit_bar_05.png</file>
<file>spline_edit_bar_06.png</file>
<file>spline_edit_bar_07.png</file>
<file>spline_edit_bar_08.png</file>
<file>spline_edit_bar_09.png</file>
<file>spline_edit_bar_10.png</file>
<file>spline_edit_bar_11.png</file>
<file>spline_edit_bar_12.png</file>
<file>spline_edit_bar_13.png</file>
<file>spline_edit_bar_14.png</file>
<file>spline_edit_bar_15.png</file>
<file>spline_edit_bar_16.png</file>
</qresource>
</RCC>
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,352 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef CRYINCLUDE_EDITOR_TRACKVIEW_TRACKVIEWDOPESHEETBASE_H
#define CRYINCLUDE_EDITOR_TRACKVIEW_TRACKVIEWDOPESHEETBASE_H
#pragma once
#include <IMovieSystem.h>
#include "TrackViewNode.h"
#include "TrackViewSequence.h"
#include "AnimationContext.h"
#include <QWidget>
class CTVTrackPropsDialog;
class CTrackViewNodesCtrl;
class CTrackViewKeyPropertiesDlg;
class CTrackViewNode;
class CTrackViewTrack;
class CTrackViewAnimNode;
class QRubberBand;
class QScrollBar;
class ReflectedPropertyControl;
namespace AZ
{
class Color;
}
enum ETVActionMode
{
eTVActionMode_MoveKey = 1,
eTVActionMode_AddKeys,
eTVActionMode_SlideKey,
eTVActionMode_ScaleKey,
};
enum ESnappingMode
{
eSnappingMode_SnapNone = 0,
eSnappingMode_SnapTick,
eSnappingMode_SnapMagnet,
eSnappingMode_SnapFrame,
};
enum ETVTickMode
{
eTVTickMode_InSeconds = 0,
eTVTickMode_InFrames,
};
/** TrackView DopeSheet interface
*/
class CTrackViewDopeSheetBase
: public QWidget
, public IAnimationContextListener
, public ITrackViewSequenceListener
{
public:
CTrackViewDopeSheetBase(QWidget* parent = 0);
virtual ~CTrackViewDopeSheetBase();
void SetNodesCtrl(CTrackViewNodesCtrl* pNodesCtrl) { m_pNodesCtrl = pNodesCtrl; }
void SetTimeScale(float timeScale, float fAnchorTime);
float GetTimeScale() { return m_timeScale; }
void SetScrollOffset(int hpos);
int GetScrollPos() const;
void SetTimeRange(float start, float end);
void SetStartMarker(float fTime);
void SetEndMarker(float fTime);
void SetMouseActionMode(ETVActionMode mode);
void SetKeyPropertiesDlg(CTrackViewKeyPropertiesDlg* dlg) { m_keyPropertiesDlg = dlg; }
void SetSnappingMode(ESnappingMode mode) { m_snappingMode = mode; }
ESnappingMode GetSnappingMode() const { return m_snappingMode; }
void SetSnapFPS(UINT fps);
ETVTickMode GetTickDisplayMode() const { return m_tickDisplayMode; }
void SetTickDisplayMode(ETVTickMode mode);
void SetEditLock(bool bLock) { m_bEditLock = bLock; }
// IAnimationContextListener
virtual void OnTimeChanged(float newTime) override;
float TickSnap(float time) const;
protected:
void showEvent(QShowEvent* event) override;
void resizeEvent(QResizeEvent* event) override;
void wheelEvent(QWheelEvent* event) override;
void mousePressEvent(QMouseEvent* event) override;
void mouseReleaseEvent(QMouseEvent* event) override;
void mouseDoubleClickEvent(QMouseEvent* event) override;
private:
void OnHScroll();
void OnLButtonDown(Qt::KeyboardModifiers modifiers, const QPoint& point);
void OnLButtonDblClk(Qt::KeyboardModifiers modifiers, const QPoint& point);
void OnRButtonDown(Qt::KeyboardModifiers modifiers, const QPoint& point);
void OnLButtonUp(Qt::KeyboardModifiers modifiers, const QPoint& point);
void OnMButtonDown(Qt::KeyboardModifiers modifiers, const QPoint& point);
void OnMButtonUp(Qt::KeyboardModifiers modifiers, const QPoint& point);
void mouseMoveEvent(QMouseEvent* event) override;
void paintEvent(QPaintEvent* event) override;
void keyPressEvent(QKeyEvent* event) override;
bool event(QEvent* event) override;
void OnRButtonUp(Qt::KeyboardModifiers modifiers, const QPoint& point);
void OnCaptureChanged();
private slots:
void OnCurrentColorChange(const AZ::Color& color);
private:
void UpdateColorKey(const QColor& color, bool addToUndo);
void UpdateColorKeyHelper(const ColorF& color);
void AddKeys(const QPoint& point, const bool bTryAddKeysInGroup);
void ShowKeyPropertyCtrlOnSpot(int x, int y, bool bMultipleKeysSelected, bool bKeyChangeInSameTrack);
void HideKeyPropertyCtrlOnSpot();
// Utility functions to change the selected track(s) in the given track(s)
void ChangeSequenceTrackSelection(CTrackViewSequence* sequence, CTrackViewTrack* trackToSelect) const;
void ChangeSequenceTrackSelection(CTrackViewSequence* sequence, CTrackViewTrackBundle trackBundle, bool multiTrackSelection) const;
XmlNodeRef GetKeysInClickboard();
void StartPasteKeys();
CTrackViewKeyHandle FirstKeyFromPoint(const QPoint& point);
CTrackViewKeyHandle DurationKeyFromPoint(const QPoint& point);
CTrackViewKeyHandle CheckCursorOnStartEndTimeAdjustBar(const QPoint& point, bool& bStart);
void SelectKeys(const QRect& rc, const bool bMultiSelection);
int NumKeysFromPoint(const QPoint& point);
//! Select all keys within time frame defined by this client rectangle.
void SelectAllKeysWithinTimeFrame(const QRect& rc, const bool bMultiSelection);
//! Return time snapped to time step,
double GetTickTime() const;
float MagnetSnap(float time, const CTrackViewAnimNode* pNode) const;
float FrameSnap(float time) const;
//! Return move time offset snapped with current snap settings
float ComputeSnappedMoveOffset();
//! Returns visible time range.
Range GetVisibleRange() const;
Range GetTimeRange(const QRect& rc) const;
void SetHorizontalExtent(int min, int max);
void SetCurrTime(float time);
//! Return client position for given time.
int TimeToClient(float time) const;
float TimeFromPoint(const QPoint& point) const;
float TimeFromPointUnsnapped(const QPoint& point) const;
void SetLeftOffset(int ofs) { m_leftOffset = ofs; };
void SetMouseCursor(const QCursor& cursor);
void ShowKeyTooltip(CTrackViewKeyHandle& keyHandle, const QPoint& point);
bool IsOkToAddKeyHere(const CTrackViewTrack* pTrack, float time) const;
void MouseMoveSelect(const QPoint& point);
void MouseMoveMove(const QPoint& point, Qt::KeyboardModifiers modifiers);
void MouseMoveDragTime(const QPoint& point, Qt::KeyboardModifiers modifiers);
void MouseMoveOver(const QPoint& point);
void MouseMoveDragEndMarker(const QPoint& point, Qt::KeyboardModifiers modifiers);
void CancelDrag();
float SnapTime(Qt::KeyboardModifiers modifiers, const QPoint& p);
void MouseMoveDragStartMarker(const QPoint& point, Qt::KeyboardModifiers modifiers);
void MouseMoveStartEndTimeAdjust(const QPoint& point, bool bStart);
CTrackViewNode* GetNodeFromPointRec(CTrackViewNode* pCurrentNode, const QPoint& point);
CTrackViewNode* GetNodeFromPoint(const QPoint& point);
CTrackViewAnimNode* GetAnimNodeFromPoint(const QPoint& point);
CTrackViewTrack* GetTrackFromPoint(const QPoint& point);
void LButtonDownOnTimeAdjustBar(const QPoint& point, CTrackViewKeyHandle& keyHandle, bool bStart);
void LButtonDownOnKey(const QPoint& point, CTrackViewKeyHandle& keyHandle, Qt::KeyboardModifiers modifiers);
bool CreateColorKey(CTrackViewTrack* pTrack, float keyTime);
void EditSelectedColorKey(CTrackViewTrack* pTrack);
void AcceptUndo();
// Returns the snapping mode modified active keys
ESnappingMode GetKeyModifiedSnappingMode();
QRect GetNodeRect(const CTrackViewNode* pNode) const;
void StoreMementoForTracksWithSelectedKeys();
// Drawing methods.
void DrawControl(QPainter* pDC, const QRect& rcUpdate);
void DrawNodesRecursive(CTrackViewNode* pNode, QPainter* pDC, const QRect& rcUpdate);
void DrawTimeline(QPainter* pDC, const QRect& rcUpdate);
void DrawSummary(QPainter* pDC, const QRect& rcUpdate);
void DrawSelectedKeyIndicators(QPainter* pDC);
void DrawTicks(QPainter* pDC, const QRect& rc, Range& timeRange);
void DrawNodeTrack(CTrackViewAnimNode* pAnimNode, QPainter* pDC, const QRect& trackRect);
void DrawTrack(CTrackViewTrack* pTrack, QPainter* pDC, const QRect& trackRect);
void DrawKeys(CTrackViewTrack* pTrack, QPainter* pDC, QRect& rc, Range& timeRange);
void DrawSequenceTrack(const Range& timeRange, QPainter* pDC, CTrackViewTrack* pTrack, const QRect& rc);
void DrawBoolTrack(const Range& timeRange, QPainter* pDC, CTrackViewTrack* pTrack, const QRect& rc);
void DrawSelectTrack(const Range& timeRange, QPainter* pDC, CTrackViewTrack* pTrack, const QRect& rc);
void DrawKeyDuration(CTrackViewTrack* pTrack, QPainter* pDC, const QRect& rc, int keyIndex);
void DrawGoToTrackArrow(CTrackViewTrack* pTrack, QPainter* pDC, const QRect& rc);
void DrawColorGradient(QPainter* pDC, const QRect& rc, const CTrackViewTrack* pTrack);
void DrawClipboardKeys(QPainter* pDC, const QRect& rc);
void DrawTrackClipboardKeys(QPainter* pDC, CTrackViewTrack* pTrack, XmlNodeRef trackNode, const float timeOffset);
CTrackViewNodesCtrl* m_pNodesCtrl;
void ComputeFrameSteps(const Range& VisRange);
void DrawTimeLineInFrames(QPainter* dc, const QRect& rc, const QColor& lineCol, const QColor& textCol, double step);
void DrawTimeLineInSeconds(QPainter* dc, const QRect& rc, const QColor& lineCol, const QColor& textCol, double step);
static bool CompareKeyHandleByTime(const CTrackViewKeyHandle &a, const CTrackViewKeyHandle &b);
QBrush m_bkgrBrush;
QBrush m_bkgrBrushEmpty;
QBrush m_selectedBrush;
QBrush m_timeBkgBrush;
QBrush m_timeHighlightBrush;
QBrush m_visibilityBrush;
QBrush m_selectTrackBrush;
QCursor m_currCursor;
QCursor m_crsLeftRight;
QCursor m_crsAddKey;
QCursor m_crsCross;
QCursor m_crsAdjustLR;
QRect m_rcClient;
QPoint m_scrollOffset;
QRect m_rcSelect;
QRect m_rcTimeline;
QRect m_rcSummary;
QPoint m_lastTooltipPos;
QPoint m_mouseDownPos;
QPoint m_mouseOverPos;
QPixmap m_offscreenBitmap;
QRubberBand* m_rubberBand;
QScrollBar* m_scrollBar;
// Time
float m_timeScale;
float m_currentTime;
float m_storedTime;
Range m_timeRange;
Range m_timeMarked;
// This is how often to place ticks.
// value of 10 means place ticks every 10 second.
double m_ticksStep;
CTrackViewKeyPropertiesDlg* m_keyPropertiesDlg;
ReflectedPropertyControl* m_wndPropsOnSpot;
const CTrackViewTrack* m_pLastTrackSelectedOnSpot;
QFont m_descriptionFont;
// Mouse interaction state
int m_mouseMode;
int m_mouseActionMode;
bool m_bZoomDrag;
bool m_bMoveDrag;
bool m_bCursorWasInKey;
bool m_bJustSelected;
bool m_bMouseMovedAfterRButtonDown;
bool m_bKeysMoved;
bool m_stashedRecordModeWhileTimeDragging;
// Offset for keys while moving/pasting
float m_keyTimeOffset;
// If control is locked for editing
bool m_bEditLock;
// Fast redraw: Only redraw time slider. Everything else is buffered.
bool m_bFastRedraw;
// Scrolling
int m_leftOffset;
int m_scrollMin;
int m_scrollMax;
// Snapping
ESnappingMode m_snappingMode;
float m_snapFrameTime;
// Ticks in frames or seconds
ETVTickMode m_tickDisplayMode;
double m_fFrameTickStep;
double m_fFrameLabelStep;
// Key for time adjust
CTrackViewKeyHandle m_keyForTimeAdjust;
// Cached clipboard XML for eTVMouseMode_Paste
XmlNodeRef m_clipboardKeys;
// Store current track whose color is being updated
CTrackViewTrack* m_colorUpdateTrack;
// Store the key time of that track
float m_colorUpdateKeyTime;
// Mementos of unchanged tracks for Move/Scale/Slide etc.
struct TrackMemento
{
CTrackViewTrackMemento m_memento;
// Also need to store key selection states,
// because RestoreMemento will destroy them
std::vector<bool> m_keySelectionStates;
};
std::unordered_map<CTrackViewTrack*, TrackMemento> m_trackMementos;
#ifdef DEBUG
unsigned int m_redrawCount;
#endif
};
#endif // CRYINCLUDE_EDITOR_TRACKVIEW_TRACKVIEWDOPESHEETBASE_H
@@ -0,0 +1,26 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "EditorDefs.h"
#include "TrackViewDoubleSpinBox.h"
CTrackViewDoubleSpinBox::CTrackViewDoubleSpinBox(QWidget* parent)
: AzQtComponents::DoubleSpinBox(parent)
{
}
CTrackViewDoubleSpinBox::~CTrackViewDoubleSpinBox()
{
}
void CTrackViewDoubleSpinBox::stepBy(int steps)
{
AzQtComponents::DoubleSpinBox::stepBy(steps);
Q_EMIT stepByFinished();
}
#include <TrackView/moc_TrackViewDoubleSpinBox.cpp>
@@ -0,0 +1,26 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#if !defined(Q_MOC_RUN)
#include <AzQtComponents/Components/Widgets/SpinBox.h>
#endif
class CTrackViewDoubleSpinBox
: public AzQtComponents::DoubleSpinBox
{
Q_OBJECT
public:
CTrackViewDoubleSpinBox(QWidget* parent = nullptr);
~CTrackViewDoubleSpinBox() override;
protected:
virtual void stepBy(int steps) override;
signals:
void stepByFinished();
};
@@ -0,0 +1,89 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "EditorDefs.h"
#include "TrackViewEventNode.h"
// CryCommon
#include <CryCommon/Maestro/Types/AnimParamType.h>
//////////////////////////////////////////////////////////////////////////
CTrackViewEventNode::CTrackViewEventNode(IAnimSequence* pSequence, IAnimNode* pAnimNode, CTrackViewNode* pParentNode)
: CTrackViewAnimNode(pSequence, pAnimNode, pParentNode)
{
if (GetAnimNode() && GetAnimNode()->GetSequence())
{
GetAnimNode()->GetSequence()->AddTrackEventListener(this);
}
}
//////////////////////////////////////////////////////////////////////////
CTrackViewEventNode::~CTrackViewEventNode()
{
if (GetAnimNode() && GetAnimNode()->GetSequence())
{
GetAnimNode()->GetSequence()->RemoveTrackEventListener(this);
}
}
//////////////////////////////////////////////////////////////////////////
void CTrackViewEventNode::OnTrackEvent([[maybe_unused]] IAnimSequence* pSequence, int reason, const char* event, void* pUserData)
{
ITrackEventListener::ETrackEventReason eReason = static_cast<ITrackEventListener::ETrackEventReason>(reason);
switch (eReason)
{
case ITrackEventListener::eTrackEventReason_Renamed:
RenameTrackEvent(event, static_cast<const char*>(pUserData));
break;
case ITrackEventListener::eTrackEventReason_Removed:
RemoveTrackEvent(event);
default:
// do nothing for events we're not interested in
break;
}
}
//////////////////////////////////////////////////////////////////////////
void CTrackViewEventNode::RenameTrackEvent(const char* fromName, const char* toName)
{
CTrackViewTrackBundle eventTracks = GetTracksByParam(AnimParamType::TrackEvent);
const uint numEventTracks = eventTracks.GetCount();
for (uint i = 0; i < numEventTracks; ++i)
{
CTrackViewTrack* eventTrack = eventTracks.GetTrack(i);
if (eventTrack)
{
// Go through all keys searching for match to the fromName and re-set these keys to use the toName
CTrackViewKeyBundle allKeys = eventTrack->GetAllKeys();
const uint numKeys = allKeys.GetKeyCount();
for (uint k = 0; k < numKeys; ++k)
{
CTrackViewKeyHandle keyHandle = allKeys.GetKey(k);
IEventKey eventKey;
keyHandle.GetKey(&eventKey);
if (strcmp(eventKey.event.c_str(), fromName) == 0)
{
// we have a match - re-set the eventKey with the toName
eventKey.event = toName;
keyHandle.SetKey(&eventKey);
}
}
}
}
}
//////////////////////////////////////////////////////////////////////////
void CTrackViewEventNode::RemoveTrackEvent(const char* removedEventName)
{
// rename the removedEventName keys to the empty string, which represents an unset event key
RenameTrackEvent(removedEventName, "");
}
@@ -0,0 +1,39 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef CRYINCLUDE_EDITOR_TRACKVIEW_TRACKVIEWEVENTNODE_H
#define CRYINCLUDE_EDITOR_TRACKVIEW_TRACKVIEWEVENTNODE_H
#pragma once
#include "IMovieSystem.h"
#include "TrackViewAnimNode.h"
////////////////////////////////////////////////////////////////////////////
// This class represents an IAnimNode deditcated to firing Track Events
////////////////////////////////////////////////////////////////////////////
class CTrackViewEventNode
: public CTrackViewAnimNode
, public ITrackEventListener
{
public:
CTrackViewEventNode(IAnimSequence* pSequence, IAnimNode* pAnimNode, CTrackViewNode* pParentNode);
virtual ~CTrackViewEventNode();
// overrides from ITrackEventListener
void OnTrackEvent(IAnimSequence* pSequence, int reason, const char* event, void* pUserData) override;
protected:
// updates existing keys using fromName events, changes them to use the toName events instead
void RenameTrackEvent(const char* fromName, const char* toName);
// updates existing keys using removedEventName events to use the empty string (representing no event)
void RemoveTrackEvent(const char* removedEventName);
};
#endif // CRYINCLUDE_EDITOR_TRACKVIEW_TRACKVIEWEVENTNODE_H
+167
View File
@@ -0,0 +1,167 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "EditorDefs.h"
#include "TrackViewFindDlg.h"
// Editor
#include "TrackViewSequenceManager.h"
#include "AnimationContext.h"
AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
#include <TrackView/ui_TrackViewFindDlg.h>
AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING
#include "Maestro/Types/AnimNodeType.h"
/////////////////////////////////////////////////////////////////////////////
// CTrackViewFindDlg dialog
CTrackViewFindDlg::CTrackViewFindDlg(const char* title, QWidget* pParent /*=NULL*/)
: QDialog(pParent)
, ui(new Ui::TrackViewFindDlg)
{
setWindowTitle(title);
m_tvDlg = 0;
m_numSeqs = 0;
ui->setupUi(this);
ui->LIST->setSelectionMode(QAbstractItemView::SingleSelection);
connect(ui->OK, &QPushButton::clicked, this, &CTrackViewFindDlg::OnOK);
connect(ui->CANCEL, &QPushButton::clicked, this, &CTrackViewFindDlg::OnCancel);
connect(ui->FILTER, &QLineEdit::textEdited, this, &CTrackViewFindDlg::OnFilterChange);
connect(ui->LIST, &QListWidget::itemDoubleClicked, this, &CTrackViewFindDlg::OnItemDoubleClicked);
FillData();
}
CTrackViewFindDlg::~CTrackViewFindDlg()
{
}
void CTrackViewFindDlg::FillData()
{
m_numSeqs = 0;
m_objs.resize(0);
for (int k = 0; k < GetIEditor()->GetMovieSystem()->GetNumSequences(); ++k)
{
IAnimSequence* seq = GetIEditor()->GetMovieSystem()->GetSequence(k);
for (int i = 0; i < seq->GetNodeCount(); i++)
{
IAnimNode* pNode = seq->GetNode(i);
ObjName obj;
obj.m_objName = pNode->GetName();
obj.m_directorName = pNode->HasDirectorAsParent() ? pNode->HasDirectorAsParent()->GetName() : "";
string fullname = seq->GetName();
obj.m_seqName = fullname.c_str();
m_objs.push_back(obj);
}
m_numSeqs++;
}
FillList();
}
void CTrackViewFindDlg::Init(CTrackViewDialog* tvDlg)
{
m_tvDlg = tvDlg;
}
void CTrackViewFindDlg::FillList()
{
QString filter = ui->FILTER->text();
ui->LIST->clear();
for (int i = 0; i < m_objs.size(); i++)
{
ObjName pObj = m_objs[i];
if (filter.isEmpty() || pObj.m_objName.contains(filter, Qt::CaseInsensitive))
{
QString text = pObj.m_objName;
if (!pObj.m_directorName.isEmpty())
{
text += " (";
text += pObj.m_directorName;
text += ")";
}
if (m_numSeqs > 1)
{
text += " / ";
text += pObj.m_seqName;
}
ui->LIST->addItem(text);
}
}
ui->LIST->setCurrentRow(0);
}
void CTrackViewFindDlg::OnOK()
{
ProcessSel();
accept();
}
void CTrackViewFindDlg::OnCancel()
{
reject();
}
void CTrackViewFindDlg::OnFilterChange([[maybe_unused]] const QString& text)
{
FillList();
}
void CTrackViewFindDlg::ProcessSel()
{
QList<QListWidgetItem*> selection = ui->LIST->selectedItems();
if (selection.size() != 1)
{
return;
}
int index = ui->LIST->row(selection.first());
if (index >= 0 && m_tvDlg)
{
ObjName object = m_objs[index];
const CTrackViewSequenceManager* pSequenceManager = GetIEditor()->GetSequenceManager();
CTrackViewSequence* pSequence = pSequenceManager->GetSequenceByName(object.m_seqName);
if (pSequence)
{
CAnimationContext* pAnimationContext = GetIEditor()->GetAnimation();
pAnimationContext->SetSequence(pSequence, false, false);
CTrackViewAnimNode* pParentDirector = pSequence;
CTrackViewAnimNodeBundle foundDirectorNodes = pSequence->GetAnimNodesByName(object.m_directorName.toUtf8().data());
if (foundDirectorNodes.GetCount() > 0 && foundDirectorNodes.GetNode(0)->GetType() == AnimNodeType::Director)
{
pParentDirector = foundDirectorNodes.GetNode(0);
}
CTrackViewAnimNodeBundle foundNodes = pParentDirector->GetAnimNodesByName(object.m_objName.toUtf8().data());
const uint numNodes = foundNodes.GetCount();
for (uint i = 0; i < numNodes; ++i)
{
foundNodes.GetNode(i)->SetSelected(true);
}
}
}
}
void CTrackViewFindDlg::OnItemDoubleClicked()
{
ProcessSel();
}
#include <TrackView/moc_TrackViewFindDlg.cpp>
+65
View File
@@ -0,0 +1,65 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef CRYINCLUDE_EDITOR_TRACKVIEW_TRACKVIEWFINDDLG_H
#define CRYINCLUDE_EDITOR_TRACKVIEW_TRACKVIEWFINDDLG_H
#pragma once
// TrackViewFindDlg.h : header file
//
#if !defined(Q_MOC_RUN)
#include <QDialog>
#include <QScopedPointer>
#endif
class CTrackViewDialog;
namespace Ui {
class TrackViewFindDlg;
}
/////////////////////////////////////////////////////////////////////////////
// CTrackViewFindDlg dialog
class CTrackViewFindDlg
: public QDialog
{
Q_OBJECT
// Construction
public:
CTrackViewFindDlg(const char* title = NULL, QWidget* pParent = NULL); // standard constructor
~CTrackViewFindDlg();
//Functions
void FillData();
void FillList();
void Init(CTrackViewDialog* tvDlg);
void ProcessSel();
protected slots:
void OnOK();
void OnCancel();
void OnFilterChange(const QString& text);
void OnItemDoubleClicked();
protected:
struct ObjName
{
QString m_objName;
QString m_directorName;
QString m_seqName;
};
std::vector<ObjName> m_objs;
CTrackViewDialog* m_tvDlg;
int m_numSeqs;
QScopedPointer<Ui::TrackViewFindDlg> ui;
};
#endif // CRYINCLUDE_EDITOR_TRACKVIEW_TRACKVIEWFINDDLG_H
+101
View File
@@ -0,0 +1,101 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>TrackViewFindDlg</class>
<widget class="QDialog" name="TrackViewFindDlg">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>312</width>
<height>410</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="STATIC">
<property name="geometry">
<rect>
<x>10</x>
<y>11</y>
<width>57</width>
<height>13</height>
</rect>
</property>
<property name="text">
<string>Enter filter:</string>
</property>
<property name="alignment">
<set>Qt::AlignTop|Qt::AlignLeft</set>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="FILTER">
<property name="geometry">
<rect>
<x>10</x>
<y>28</y>
<width>291</width>
<height>19</height>
</rect>
</property>
<property name="alignment">
<set>Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QListWidget" name="LIST">
<property name="geometry">
<rect>
<x>10</x>
<y>57</y>
<width>291</width>
<height>305</height>
</rect>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="leftMargin">
<number>200</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<item>
<widget class="QPushButton" name="CANCEL">
<property name="geometry">
<rect>
<x>139</x>
<y>376</y>
<width>75</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>Close</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="OK">
<property name="geometry">
<rect>
<x>226</x>
<y>376</y>
<width>75</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>Find</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</ui>
@@ -0,0 +1,408 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "EditorDefs.h"
#include "TrackViewKeyPropertiesDlg.h"
// Qt
#include <QMessageBox>
// CryCommon
#include <CryCommon/Maestro/Types/AnimValueType.h>
// Editor
#include "Controls/ReflectedPropertyControl/ReflectedPropertyCtrl.h"
AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
#include <TrackView/ui_TrackViewTrackPropsDlg.h>
AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING
//////////////////////////////////////////////////////////////////////////
void CTrackViewKeyUIControls::OnInternalVariableChange(IVariable* var)
{
CTrackViewSequence* sequence = GetIEditor()->GetAnimation()->GetSequence();
AZ_Assert(sequence, "Expected valid sequence.");
if (sequence)
{
CTrackViewKeyBundle keys = sequence->GetSelectedKeys();
OnUIChange(var, keys);
}
}
//////////////////////////////////////////////////////////////////////////
CTrackViewKeyPropertiesDlg::CTrackViewKeyPropertiesDlg(QWidget* hParentWnd)
: QWidget(hParentWnd)
, m_pLastTrackSelected(nullptr)
, m_sequence(nullptr)
{
QVBoxLayout* l = new QVBoxLayout();
l->setMargin(0);
m_wndTrackProps = new CTrackViewTrackPropsDlg(this);
l->addWidget(m_wndTrackProps);
m_wndProps = new ReflectedPropertyControl(this);
m_wndProps->Setup(true, 120);
m_wndProps->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding);
l->addWidget(m_wndProps);
m_wndProps->SetStoreUndoByItems(false);
setLayout(l);
m_pVarBlock = new CVarBlock;
// Add key UI classes
std::vector<IClassDesc*> classes;
GetIEditor()->GetClassFactory()->GetClassesByCategory("TrackViewKeyUI", classes); // BySystemID(ESYSTEM_CLASS_TRACKVIEW_KEYUI, classes);
for (IClassDesc* iclass : classes)
{
if (QObject* pObj = iclass->CreateQObject())
{
auto keyControl = qobject_cast<CTrackViewKeyUIControls*>(pObj);
Q_ASSERT(keyControl);
m_keyControls.push_back(keyControl);
}
}
// Sort key controls by descending priority
std::stable_sort(m_keyControls.begin(), m_keyControls.end(),
[](const _smart_ptr<CTrackViewKeyUIControls>& a, const _smart_ptr<CTrackViewKeyUIControls>& b)
{
return a->GetPriority() > b->GetPriority();
}
);
CreateAllVars();
}
//////////////////////////////////////////////////////////////////////////
void CTrackViewKeyPropertiesDlg::OnVarChange(IVariable* pVar)
{
// If it was a motion that just changed, we need to rebuild the controls
// so the min/max on the sliders update correctly.
if (m_sequence && pVar->GetDataType() == IVariable::DT_MOTION)
{
OnKeySelectionChanged(m_sequence);
}
}
//////////////////////////////////////////////////////////////////////////
void CTrackViewKeyPropertiesDlg::CreateAllVars()
{
for (int i = 0; i < (int)m_keyControls.size(); i++)
{
m_keyControls[i]->SetKeyPropertiesDlg(this);
m_keyControls[i]->OnCreateVars();
}
}
//////////////////////////////////////////////////////////////////////////
void CTrackViewKeyPropertiesDlg::PopulateVariables()
{
// Must first clear any selection in properties window.
m_wndProps->RemoveAllItems();
m_wndProps->AddVarBlock(m_pVarBlock);
m_wndProps->SetUpdateCallback(AZStd::bind(&CTrackViewKeyPropertiesDlg::OnVarChange, this, AZStd::placeholders::_1));
//m_wndProps->m_props.ExpandAll();
ReloadValues();
}
//////////////////////////////////////////////////////////////////////////
void CTrackViewKeyPropertiesDlg::PopulateVariables(ReflectedPropertyControl* propCtrl)
{
propCtrl->RemoveAllItems();
propCtrl->AddVarBlock(m_pVarBlock);
propCtrl->ReloadValues();
}
//////////////////////////////////////////////////////////////////////////
void CTrackViewKeyPropertiesDlg::OnKeysChanged(CTrackViewSequence* pSequence)
{
CTrackViewKeyBundle selectedKeys = pSequence->GetSelectedKeys();
if (selectedKeys.GetKeyCount() > 0 && selectedKeys.AreAllKeysOfSameType())
{
CTrackViewTrack* pTrack = selectedKeys.GetKey(0).GetTrack();
CAnimParamType paramType = pTrack->GetParameterType();
EAnimCurveType trackType = pTrack->GetCurveType();
AnimValueType valueType = pTrack->GetValueType();
for (int i = 0; i < (int)m_keyControls.size(); i++)
{
if (m_keyControls[i]->SupportTrackType(paramType, trackType, valueType))
{
m_keyControls[i]->OnKeySelectionChange(selectedKeys);
break;
}
}
}
}
//////////////////////////////////////////////////////////////////////////
void CTrackViewKeyPropertiesDlg::OnKeySelectionChanged(CTrackViewSequence* sequence)
{
m_sequence = sequence;
if (nullptr == sequence)
{
m_wndProps->ClearSelection();
m_pVarBlock->DeleteAllVariables();
m_wndProps->setEnabled(false);
m_wndTrackProps->setEnabled(false);
return;
}
CTrackViewKeyBundle selectedKeys = sequence->GetSelectedKeys();
m_wndTrackProps->OnKeySelectionChange(selectedKeys);
bool bSelectChangedInSameTrack
= m_pLastTrackSelected
&& selectedKeys.GetKeyCount() == 1
&& selectedKeys.GetKey(0).GetTrack() == m_pLastTrackSelected;
// Every Key in an Asset Blend track can have different min/max values on the float sliders
// because it's based on the duration of the motion that is set. So don't try to
// reuse the controls when the selection changes, otherwise the tooltips may be wrong.
bool reuseControls = bSelectChangedInSameTrack && m_pLastTrackSelected && (m_pLastTrackSelected->GetValueType() != AnimValueType::AssetBlend);
if (selectedKeys.GetKeyCount() == 1)
{
m_pLastTrackSelected = selectedKeys.GetKey(0).GetTrack();
}
else
{
m_pLastTrackSelected = nullptr;
}
if (reuseControls)
{
m_wndProps->ClearSelection();
}
else
{
m_pVarBlock->DeleteAllVariables();
}
m_wndProps->setEnabled(false);
m_wndTrackProps->setEnabled(false);
bool bAssigned = false;
if (selectedKeys.GetKeyCount() > 0 && selectedKeys.AreAllKeysOfSameType())
{
CTrackViewTrack* pTrack = selectedKeys.GetKey(0).GetTrack();
CAnimParamType paramType = pTrack->GetParameterType();
EAnimCurveType trackType = pTrack->GetCurveType();
AnimValueType valueType = pTrack->GetValueType();
for (int i = 0; i < (int)m_keyControls.size(); i++)
{
if (m_keyControls[i]->SupportTrackType(paramType, trackType, valueType))
{
if (!reuseControls)
{
AddVars(m_keyControls[i]);
}
if (m_keyControls[i]->OnKeySelectionChange(selectedKeys))
{
bAssigned = true;
}
break;
}
}
m_wndProps->setEnabled(true);
m_wndTrackProps->setEnabled(true);
}
else
{
m_wndProps->setEnabled(false);
m_wndTrackProps->setEnabled(false);
}
if (reuseControls)
{
ReloadValues();
}
else
{
PopulateVariables();
}
}
//////////////////////////////////////////////////////////////////////////
void CTrackViewKeyPropertiesDlg::AddVars(CTrackViewKeyUIControls* pUI)
{
CVarBlock* pVB = pUI->GetVarBlock();
for (int i = 0, num = pVB->GetNumVariables(); i < num; i++)
{
IVariable* pVar = pVB->GetVariable(i);
m_pVarBlock->AddVariable(pVar);
}
}
//////////////////////////////////////////////////////////////////////////
void CTrackViewKeyPropertiesDlg::ReloadValues()
{
m_wndProps->ReloadValues();
}
void CTrackViewKeyPropertiesDlg::OnSequenceChanged(CTrackViewSequence* sequence)
{
OnKeySelectionChanged(sequence);
m_wndTrackProps->OnSequenceChanged();
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
CTrackViewTrackPropsDlg::CTrackViewTrackPropsDlg(QWidget* parent /* = 0 */)
: QWidget(parent)
, ui(new Ui::CTrackViewTrackPropsDlg)
{
ui->setupUi(this);
// Use editingFinished and a custom signal stepByFinished (and not valueChanged)
// so the time will be updated when the user finishes editing the time field (hits enter)
// or if the arrow keys (or mouse click on the arrow buttons) in the spinner box are
// pressed. Don't just use valueChanged because we don't want intermediate values
// like 1 as the user is typing 10 to register as updates to the key values. Keys
// are identified by time, so the keys jumping around like that can stomp existing keys
// that happen to live at the intermediate values.
connect(ui->TIME, SIGNAL(editingFinished()), this, SLOT(OnUpdateTime()));
connect(ui->TIME, SIGNAL(stepByFinished()), this, SLOT(OnUpdateTime()));
}
CTrackViewTrackPropsDlg::~CTrackViewTrackPropsDlg()
{
}
//////////////////////////////////////////////////////////////////////////
void CTrackViewTrackPropsDlg::OnSequenceChanged()
{
CTrackViewSequence* pSequence = GetIEditor()->GetAnimation()->GetSequence();
if (pSequence)
{
Range range = pSequence->GetTimeRange();
ui->TIME->setRange(range.start, range.end);
}
}
//////////////////////////////////////////////////////////////////////////
bool CTrackViewTrackPropsDlg::OnKeySelectionChange(CTrackViewKeyBundle& selectedKeys)
{
m_keyHandle = CTrackViewKeyHandle();
if (selectedKeys.GetKeyCount() == 1)
{
m_keyHandle = selectedKeys.GetKey(0);
}
if (m_keyHandle.IsValid())
{
// Block the callback, the values is already set in m_keyHandle.GetTime(), no need to
// reset it and create an undo even like the user was setting it via the UI.
ui->TIME->blockSignals(true);
ui->TIME->setValue(m_keyHandle.GetTime());
ui->TIME->blockSignals(false);
ui->PREVNEXT->setText(QString::number(m_keyHandle.GetIndex() + 1));
ui->PREVNEXT->setEnabled(true);
ui->TIME->setEnabled(true);
}
else
{
ui->PREVNEXT->setEnabled(FALSE);
ui->TIME->setEnabled(FALSE);
}
return true;
}
void CTrackViewTrackPropsDlg::OnUpdateTime()
{
if (!m_keyHandle.IsValid())
{
return;
}
const float time = (float)ui->TIME->value();
// Check if the sequence is legacy
CTrackViewTrack* track = m_keyHandle.GetTrack();
if (nullptr != track)
{
CTrackViewSequence* sequence = track->GetSequence();
if (nullptr != sequence && !AZ::IsClose(m_keyHandle.GetTime(), time, AZ::Constants::FloatEpsilon))
{
bool isDuringUndo = false;
AzToolsFramework::ToolsApplicationRequests::Bus::BroadcastResult(isDuringUndo, &AzToolsFramework::ToolsApplicationRequests::Bus::Events::IsDuringUndoRedo);
if (isDuringUndo)
{
m_keyHandle.SetTime(time);
}
else
{
// Let the AZ Undo system manage the nodes on the sequence entity
AzToolsFramework::ScopedUndoBatch undoBatch("Change key time");
CTrackViewKeyHandle existingKey = track->GetKeyByTime(time);
// If there is an existing key at this time, remove it so the
// new key at this time is the only one here. Make sure it's actually a different
// key, because time can "change" but then be quantized (or snapped) to the same time by track->GetKeyByTime(time).
if (existingKey.IsValid() && (existingKey.GetIndex() != m_keyHandle.GetIndex()))
{
// Save the old time before we set the new time so we
// can reselect the m_keyHandle after the Delete.
float currentTime = m_keyHandle.GetTime();
// There is a bug in QT where editingFinished will get fired a second time if we show a QMessageBox
// so work around it by blocking signal before we do it.
ui->TIME->blockSignals(true);
QString msgBody = "There is an existing key at the specified time. If you continue, the existing key will be removed.";
if (QMessageBox::warning(this, "Overwrite Existing Key?", msgBody, QMessageBox::Cancel | QMessageBox::Yes) == QMessageBox::Cancel)
{
// Restore the old value and return.
ui->TIME->setValue(currentTime);
ui->TIME->blockSignals(false);
return;
}
else
{
ui->TIME->blockSignals(false);
}
// Delete the key that is able to get replaced. This will
// cause a sort and may cause m_keyHandle to become invalid.
existingKey.Delete();
// Reselect the key handle by time.
m_keyHandle = track->GetKeyByTime(currentTime);
}
m_keyHandle.SetTime(time);
undoBatch.MarkEntityDirty(sequence->GetSequenceComponentEntityId());
}
}
}
}
#include <TrackView/moc_TrackViewKeyPropertiesDlg.cpp>
@@ -0,0 +1,192 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef CRYINCLUDE_EDITOR_TRACKVIEW_TRACKVIEWKEYPROPERTIESDLG_H
#define CRYINCLUDE_EDITOR_TRACKVIEW_TRACKVIEWKEYPROPERTIESDLG_H
#pragma once
#pragma once
#if !defined(Q_MOC_RUN)
#include "TrackViewSequence.h"
#include "TrackViewNode.h"
#include "Plugin.h"
#include "TrackViewDopeSheetBase.h"
#include "QtViewPane.h"
#include <QScopedPointer>
#include <QDockWidget>
#endif
namespace Ui {
class CTrackViewTrackPropsDlg;
class CTrackViewKeyPropertiesDlg;
}
class ReflectedPropertyControl;
class CTrackViewKeyPropertiesDlg;
//////////////////////////////////////////////////////////////////////////
class CTrackViewKeyUIControls
: public QObject
, public _i_reference_target_t
{
Q_OBJECT
public:
CTrackViewKeyUIControls()
{
m_pVarBlock = new CVarBlock;
m_onSetCallback = AZStd::bind(&CTrackViewKeyUIControls::OnInternalVariableChange, this, AZStd::placeholders::_1);
};
void SetKeyPropertiesDlg(CTrackViewKeyPropertiesDlg* pDlg) { m_pKeyPropertiesDlg = pDlg; }
// Return internal variable block.
CVarBlock* GetVarBlock() const { return m_pVarBlock; }
//////////////////////////////////////////////////////////////////////////
// Callbacks that must be implemented in derived class
//////////////////////////////////////////////////////////////////////////
// Returns true if specified animation track type is supported by this UI.
virtual bool SupportTrackType(const CAnimParamType& paramType, EAnimCurveType trackType, AnimValueType valueType) const = 0;
// Called when UI variable changes.
virtual void OnCreateVars() = 0;
// Called when user changes selected keys.
// Return true if control update UI values
virtual bool OnKeySelectionChange(CTrackViewKeyBundle& keys) = 0;
// Called when UI variable changes.
virtual void OnUIChange(IVariable* pVar, CTrackViewKeyBundle& keys) = 0;
// Get priority of key UI control, so that specializations can have precedence
virtual unsigned int GetPriority() const = 0;
protected:
//////////////////////////////////////////////////////////////////////////
// Helper functions.
//////////////////////////////////////////////////////////////////////////
template <class T>
void SyncValue(CSmartVariable<T>& var, T& value, bool bCopyToUI, IVariable* pSrcVar = NULL)
{
if (bCopyToUI)
{
var = value;
}
else
{
if (!pSrcVar || pSrcVar == var.GetVar())
{
value = var;
}
}
}
void AddVariable(CVariableBase& varArray, CVariableBase& var, const char* varName, unsigned char dataType = IVariable::DT_SIMPLE)
{
if (varName)
{
var.SetName(varName);
}
var.SetDataType(dataType);
var.AddOnSetCallback(&m_onSetCallback);
varArray.AddVariable(&var);
m_registeredVariables.push_back(&var);
}
//////////////////////////////////////////////////////////////////////////
void AddVariable(CVariableBase& var, const char* varName, unsigned char dataType = IVariable::DT_SIMPLE)
{
if (varName)
{
var.SetName(varName);
}
var.SetDataType(dataType);
var.AddOnSetCallback(&m_onSetCallback);
m_pVarBlock->AddVariable(&var);
m_registeredVariables.push_back(&var);
}
void OnInternalVariableChange(IVariable* pVar);
protected:
_smart_ptr<CVarBlock> m_pVarBlock;
std::vector<_smart_ptr<IVariable> > m_registeredVariables;
CTrackViewKeyPropertiesDlg* m_pKeyPropertiesDlg;
IVariable::OnSetCallback m_onSetCallback;
};
//////////////////////////////////////////////////////////////////////////
class CTrackViewTrackPropsDlg
: public QWidget
{
Q_OBJECT
public:
CTrackViewTrackPropsDlg(QWidget* parent = 0);
~CTrackViewTrackPropsDlg();
void OnSequenceChanged();
bool OnKeySelectionChange(CTrackViewKeyBundle& keys);
void ReloadKey();
protected slots:
void OnUpdateTime();
protected:
CTrackViewKeyHandle m_keyHandle;
QScopedPointer<Ui::CTrackViewTrackPropsDlg> ui;
};
//////////////////////////////////////////////////////////////////////////
class TrackViewKeys;
class CTrackViewKeyPropertiesDlg
: public QWidget
, public ITrackViewSequenceListener
{
public:
CTrackViewKeyPropertiesDlg(QWidget* hParentWnd);
void SetKeysCtrl(CTrackViewDopeSheetBase* pKeysCtrl)
{
m_keysCtrl = pKeysCtrl;
if (m_keysCtrl)
{
m_keysCtrl->SetKeyPropertiesDlg(this);
}
}
void OnSequenceChanged(CTrackViewSequence* sequence);
void PopulateVariables();
void PopulateVariables(ReflectedPropertyControl* propCtrl);
// ITrackViewSequenceListener
virtual void OnKeysChanged(CTrackViewSequence* pSequence) override;
virtual void OnKeySelectionChanged(CTrackViewSequence* pSequence) override;
protected:
//////////////////////////////////////////////////////////////////////////
void OnVarChange(IVariable* pVar);
void CreateAllVars();
void AddVars(CTrackViewKeyUIControls* pUI);
void ReloadValues();
protected:
std::vector< _smart_ptr<CTrackViewKeyUIControls> > m_keyControls;
_smart_ptr<CVarBlock> m_pVarBlock;
ReflectedPropertyControl* m_wndProps;
CTrackViewTrackPropsDlg* m_wndTrackProps;
CTrackViewDopeSheetBase* m_keysCtrl;
CTrackViewTrack* m_pLastTrackSelected;
CTrackViewSequence* m_sequence;
};
#endif // CRYINCLUDE_EDITOR_TRACKVIEW_TRACKVIEWKEYPROPERTIESDLG_H
+713
View File
@@ -0,0 +1,713 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "EditorDefs.h"
#include "TrackViewNode.h"
// CryCommon
#include <CryCommon/Maestro/Types/AnimNodeType.h>
// Editor
#include "TrackView/TrackViewTrack.h"
#include "TrackView/TrackViewSequence.h"
////////////////////////////////////////////////////////////////////////////
void CTrackViewKeyConstHandle::GetKey(IKey* pKey) const
{
assert(m_bIsValid);
m_pTrack->GetKey(m_keyIndex, pKey);
}
////////////////////////////////////////////////////////////////////////////
float CTrackViewKeyConstHandle::GetTime() const
{
assert(m_bIsValid);
return m_pTrack->GetKeyTime(m_keyIndex);
}
////////////////////////////////////////////////////////////////////////////
void CTrackViewKeyHandle::SetKey(IKey* pKey)
{
assert(m_bIsValid);
m_pTrack->SetKey(m_keyIndex, pKey);
}
////////////////////////////////////////////////////////////////////////////
void CTrackViewKeyHandle::GetKey(IKey* pKey) const
{
assert(m_bIsValid);
m_pTrack->GetKey(m_keyIndex, pKey);
}
////////////////////////////////////////////////////////////////////////////
void CTrackViewKeyHandle::Select(bool bSelect)
{
assert(m_bIsValid);
m_pTrack->SelectKey(m_keyIndex, bSelect);
}
////////////////////////////////////////////////////////////////////////////
bool CTrackViewKeyHandle::IsSelected() const
{
assert(m_bIsValid);
return m_pTrack->IsKeySelected(m_keyIndex);
}
////////////////////////////////////////////////////////////////////////////
void CTrackViewKeyHandle::SetTime(float time, bool notifyListeners)
{
AZ_Assert(m_bIsValid, "Expected a valid key handle.");
// Flag the current key, because the key handle may become invalid
// after the time is set and it is potentially sorted into a different
// index.
m_pTrack->SetSortMarkerKey(m_keyIndex, true);
// set the new time, this may cause a sort that reorders the keys, making
// m_keyIndex incorrect.
m_pTrack->SetKeyTime(m_keyIndex, time, notifyListeners);
// If the key at this index changed because of the key sort by time.
// We need to search through the keys now and find the marker.
if (!m_pTrack->IsSortMarkerKey(m_keyIndex))
{
CTrackViewKeyBundle allKeys = m_pTrack->GetAllKeys();
for (int x = 0; x < allKeys.GetKeyCount(); x++)
{
unsigned int curIndex = allKeys.GetKey(x).GetIndex();
if (m_pTrack->IsSortMarkerKey(curIndex))
{
m_keyIndex = curIndex;
break;
}
}
}
// clear the sort marker
m_pTrack->SetSortMarkerKey(m_keyIndex, false);
}
////////////////////////////////////////////////////////////////////////////
float CTrackViewKeyHandle::GetTime() const
{
assert(m_bIsValid);
return m_pTrack->GetKeyTime(m_keyIndex);
}
////////////////////////////////////////////////////////////////////////////
float CTrackViewKeyHandle::GetDuration() const
{
assert(m_bIsValid);
const char* desc = nullptr;
float duration = 0;
m_pTrack->m_pAnimTrack->GetKeyInfo(m_keyIndex, desc, duration);
return duration;
}
////////////////////////////////////////////////////////////////////////////
const char* CTrackViewKeyHandle::GetDescription() const
{
assert(m_bIsValid);
const char* desc = "";
float duration = 0;
m_pTrack->m_pAnimTrack->GetKeyInfo(m_keyIndex, desc, duration);
return desc;
}
////////////////////////////////////////////////////////////////////////////
void CTrackViewKeyHandle::Offset(float offset, bool notifyListeners)
{
AZ_Assert(m_bIsValid, "Expected key handle to be in a valid state.");
float newTime = m_pTrack->GetKeyTime(m_keyIndex) + offset;
m_pTrack->SetKeyTime(m_keyIndex, newTime, notifyListeners);
}
////////////////////////////////////////////////////////////////////////////
void CTrackViewKeyHandle::Delete()
{
assert(m_bIsValid);
m_pTrack->RemoveKey(m_keyIndex);
m_bIsValid = false;
}
////////////////////////////////////////////////////////////////////////////
CTrackViewKeyHandle CTrackViewKeyHandle::Clone()
{
assert(m_bIsValid);
unsigned int newKeyIndex = m_pTrack->CloneKey(m_keyIndex);
return CTrackViewKeyHandle(m_pTrack, newKeyIndex);
}
////////////////////////////////////////////////////////////////////////////
CTrackViewKeyHandle CTrackViewKeyHandle::GetNextKey()
{
return m_pTrack->GetNextKey(GetTime());
}
////////////////////////////////////////////////////////////////////////////
CTrackViewKeyHandle CTrackViewKeyHandle::GetPrevKey()
{
return m_pTrack->GetPrevKey(GetTime());
}
////////////////////////////////////////////////////////////////////////////
CTrackViewKeyHandle CTrackViewKeyHandle::GetAboveKey() const
{
// Search for track above that has keys
for (CTrackViewNode* pCurrentNode = m_pTrack->GetAboveNode(); pCurrentNode; pCurrentNode = pCurrentNode->GetAboveNode())
{
if (pCurrentNode->GetNodeType() == eTVNT_Track)
{
CTrackViewTrack* pCurrentTrack = static_cast<CTrackViewTrack*>(pCurrentNode);
const unsigned int keyCount = pCurrentTrack->GetKeyCount();
if (keyCount > 0)
{
// Return key with nearest time to this key
return pCurrentTrack->GetNearestKeyByTime(GetTime());
}
}
}
return CTrackViewKeyHandle();
}
////////////////////////////////////////////////////////////////////////////
CTrackViewKeyHandle CTrackViewKeyHandle::GetBelowKey() const
{
// Search for track below that has keys
for (CTrackViewNode* pCurrentNode = m_pTrack->GetBelowNode(); pCurrentNode; pCurrentNode = pCurrentNode->GetBelowNode())
{
if (pCurrentNode->GetNodeType() == eTVNT_Track)
{
CTrackViewTrack* pCurrentTrack = static_cast<CTrackViewTrack*>(pCurrentNode);
const unsigned int keyCount = pCurrentTrack->GetKeyCount();
if (keyCount > 0)
{
// Return key with nearest time to this key
return pCurrentTrack->GetNearestKeyByTime(GetTime());
}
}
}
return CTrackViewKeyHandle();
}
////////////////////////////////////////////////////////////////////////////
bool CTrackViewKeyHandle::operator==(const CTrackViewKeyHandle& keyHandle) const
{
return m_pTrack == keyHandle.m_pTrack && m_keyIndex == keyHandle.m_keyIndex;
}
////////////////////////////////////////////////////////////////////////////
bool CTrackViewKeyHandle::operator!=(const CTrackViewKeyHandle& keyHandle) const
{
return !(*this == keyHandle);
}
////////////////////////////////////////////////////////////////////////////
void CTrackViewKeyBundle::AppendKey(const CTrackViewKeyHandle& keyHandle)
{
// Check if newly added key has different type than existing ones
if (m_bAllOfSameType && m_keys.size() > 0)
{
const CTrackViewKeyHandle& lastKey = m_keys.back();
const CTrackViewTrack* pMyTrack = keyHandle.GetTrack();
const CTrackViewTrack* pOtherTrack = lastKey.GetTrack();
// Check if keys are from sub tracks, always compare types of parent track
if (pMyTrack->IsSubTrack())
{
pMyTrack = static_cast<const CTrackViewTrack*>(pMyTrack->GetParentNode());
}
if (pOtherTrack->IsSubTrack())
{
pOtherTrack = static_cast<const CTrackViewTrack*>(pOtherTrack->GetParentNode());
}
// Do comparison
if (pMyTrack->GetParameterType() != pOtherTrack->GetParameterType()
|| pMyTrack->GetCurveType() != pOtherTrack->GetCurveType()
|| pMyTrack->GetValueType() != pOtherTrack->GetValueType())
{
m_bAllOfSameType = false;
}
}
m_keys.push_back(keyHandle);
}
////////////////////////////////////////////////////////////////////////////
void CTrackViewKeyBundle::AppendKeyBundle(const CTrackViewKeyBundle& bundle)
{
for (auto iter = bundle.m_keys.begin(); iter != bundle.m_keys.end(); ++iter)
{
AppendKey(*iter);
}
}
////////////////////////////////////////////////////////////////////////////
void CTrackViewKeyBundle::SelectKeys(const bool bSelected)
{
const unsigned int numKeys = GetKeyCount();
for (unsigned int i = 0; i < numKeys; ++i)
{
GetKey(i).Select(bSelected);
}
}
//////////////////////////////////////////////////////////////////////////
CTrackViewKeyHandle CTrackViewKeyBundle::GetSingleSelectedKey()
{
const unsigned int keyCount = GetKeyCount();
if (keyCount == 1)
{
return m_keys[0];
}
else if (keyCount > 1 && keyCount <= 4)
{
// All keys must have same time & same parent track
CTrackViewNode* pFirstParent = m_keys[0].GetTrack()->GetParentNode();
const float firstTime = m_keys[0].GetTime();
// Parent must be a track
if (pFirstParent->GetNodeType() != eTVNT_Track)
{
return CTrackViewKeyHandle();
}
// Check other keys for equality
for (unsigned int i = 0; i < keyCount; ++i)
{
if (m_keys[i].GetTrack()->GetParentNode() != pFirstParent || m_keys[i].GetTime() != firstTime)
{
return CTrackViewKeyHandle();
}
}
return static_cast<CTrackViewTrack*>(pFirstParent)->GetKeyByTime(firstTime);
}
return CTrackViewKeyHandle();
}
//////////////////////////////////////////////////////////////////////////
CTrackViewNode::CTrackViewNode(CTrackViewNode* pParent)
: m_pParentNode(pParent)
, m_bSelected(false)
, m_bHidden(false)
{
}
//////////////////////////////////////////////////////////////////////////
bool CTrackViewNode::HasObsoleteTrack() const
{
return HasObsoleteTrackRec(this);
}
//////////////////////////////////////////////////////////////////////////
bool CTrackViewNode::HasObsoleteTrackRec(const CTrackViewNode* pCurrentNode) const
{
if (pCurrentNode->GetNodeType() == eTVNT_Track)
{
const CTrackViewTrack* pTrack = static_cast<const CTrackViewTrack*>(pCurrentNode);
EAnimCurveType trackType = pTrack->GetCurveType();
if (trackType == eAnimCurveType_TCBFloat || trackType == eAnimCurveType_TCBQuat || trackType == eAnimCurveType_TCBVector)
{
return true;
}
}
for (unsigned int i = 0; i < pCurrentNode->GetChildCount(); ++i)
{
CTrackViewNode* pNode = pCurrentNode->GetChild(i);
if (HasObsoleteTrackRec(pNode))
{
return true;
}
}
return false;
}
//////////////////////////////////////////////////////////////////////////
void CTrackViewNode::ClearSelection()
{
CTrackViewSequenceNotificationContext context(GetSequence());
SetSelected(false);
const unsigned int numChilds = GetChildCount();
for (unsigned int childIndex = 0; childIndex < numChilds; ++childIndex)
{
GetChild(childIndex)->ClearSelection();
}
}
//////////////////////////////////////////////////////////////////////////
CTrackViewNode* CTrackViewNode::GetAboveNode() const
{
CTrackViewNode* pParent = GetParentNode();
if (!pParent)
{
// The root does not have an above node
return nullptr;
}
CTrackViewNode* pPrevSibling = GetPrevSibling();
if (!pPrevSibling)
{
// First sibling -> parent is above node
return pParent;
}
// Find last node in sibling tree
CTrackViewNode* pCurrentNode = pPrevSibling;
while (pCurrentNode && pCurrentNode->GetChildCount() > 0 && pCurrentNode->GetExpanded())
{
pCurrentNode = pCurrentNode->GetChild(pCurrentNode->GetChildCount() - 1);
}
return pCurrentNode;
}
//////////////////////////////////////////////////////////////////////////
CTrackViewNode* CTrackViewNode::GetBelowNode() const
{
const unsigned int childCount = GetChildCount();
if (childCount > 0 && GetExpanded())
{
return GetChild(0);
}
CTrackViewNode* pParent = GetParentNode();
if (!pParent)
{
// Root without children
return nullptr;
}
// If there is a next sibling return it
CTrackViewNode* pNextSibling = GetNextSibling();
if (pNextSibling)
{
return pNextSibling;
}
// Otherwise we need to go up the tree and check
// the parent nodes for next siblings
CTrackViewNode* pCurrentNode = pParent;
while (pCurrentNode)
{
CTrackViewNode* pNextParentSibling = pCurrentNode->GetNextSibling();
if (pNextParentSibling)
{
return pNextParentSibling;
}
pCurrentNode = pCurrentNode->GetParentNode();
}
return nullptr;
}
//////////////////////////////////////////////////////////////////////////
CTrackViewNode* CTrackViewNode::GetPrevSibling() const
{
CTrackViewNode* pParent = GetParentNode();
if (!pParent)
{
// The root does not have siblings
return nullptr;
}
// Search for prev sibling
unsigned int siblingCount = pParent->GetChildCount();
assert(siblingCount > 0);
for (unsigned int i = 1; i < siblingCount; ++i)
{
CTrackViewNode* pSibling = pParent->GetChild(i);
if (pSibling == this)
{
return pParent->GetChild(i - 1);
}
}
return nullptr;
}
//////////////////////////////////////////////////////////////////////////
CTrackViewNode* CTrackViewNode::GetNextSibling() const
{
CTrackViewNode* pParent = GetParentNode();
if (!pParent)
{
// The root does not have siblings
return nullptr;
}
// Search for next sibling
unsigned int siblingCount = pParent->GetChildCount();
assert(siblingCount > 0);
for (unsigned int i = 0; i < siblingCount - 1; ++i)
{
CTrackViewNode* pSibling = pParent->GetChild(i);
if (pSibling == this)
{
return pParent->GetChild(i + 1);
}
}
return nullptr;
}
//////////////////////////////////////////////////////////////////////////
void CTrackViewNode::SetSelected(bool bSelected)
{
if (bSelected != m_bSelected)
{
m_bSelected = bSelected;
if (m_bSelected)
{
GetSequence()->OnNodeChanged(this, ITrackViewSequenceListener::eNodeChangeType_Selected);
}
else
{
GetSequence()->OnNodeChanged(this, ITrackViewSequenceListener::eNodeChangeType_Deselected);
}
GetSequence()->OnNodeSelectionChanged();
}
}
//////////////////////////////////////////////////////////////////////////
CTrackViewSequence* CTrackViewNode::GetSequence()
{
for (CTrackViewNode* pCurrentNode = this; pCurrentNode; pCurrentNode = pCurrentNode->GetParentNode())
{
if (pCurrentNode->GetNodeType() == eTVNT_Sequence)
{
return static_cast<CTrackViewSequence*>(pCurrentNode);
}
}
// Every node belongs to a sequence
assert(false);
return nullptr;
}
//////////////////////////////////////////////////////////////////////////
const CTrackViewSequence* CTrackViewNode::GetSequenceConst() const
{
for (const CTrackViewNode* pCurrentNode = this; pCurrentNode; pCurrentNode = pCurrentNode->GetParentNode())
{
if (pCurrentNode->GetNodeType() == eTVNT_Sequence)
{
return static_cast<const CTrackViewSequence*>(pCurrentNode);
}
}
AZ_Assert(false, "Every node belongs to a sequence");
return nullptr;
}
//////////////////////////////////////////////////////////////////////////
void CTrackViewNode::AddNode(CTrackViewNode* pNode)
{
assert (pNode->GetNodeType() != eTVNT_Sequence);
m_childNodes.push_back(std::unique_ptr<CTrackViewNode>(pNode));
SortNodes();
pNode->m_pParentNode = this;
GetSequence()->OnNodeChanged(pNode, ITrackViewSequenceListener::eNodeChangeType_Added);
}
//////////////////////////////////////////////////////////////////////////
void CTrackViewNode::SortNodes()
{
// Sort with operator<
std::stable_sort(m_childNodes.begin(), m_childNodes.end(),
[&](const std::unique_ptr<CTrackViewNode>& a, const std::unique_ptr<CTrackViewNode>& b) -> bool
{
const CTrackViewNode* pA = a.get();
const CTrackViewNode* pB = b.get();
return *pA < *pB;
}
);
}
namespace
{
static int GetNodeOrder(AnimNodeType nodeType)
{
AZ_Assert(nodeType < AnimNodeType::Num, "Expected nodeType to be less than AnimNodeType::Num");
// note: this array gets over-allocated and is sparsely populated because the eAnimNodeType enums are not sequential in IMovieSystem.h
// I wonder if the original authors intended this? Not a big deal, just some trivial memory wastage.
static int nodeOrder[static_cast<int>(AnimNodeType::Num)];
nodeOrder[static_cast<int>(AnimNodeType::Invalid)] = 0;
nodeOrder[static_cast<int>(AnimNodeType::Director)] = 1;
nodeOrder[static_cast<int>(AnimNodeType::Alembic)] = 4;
nodeOrder[static_cast<int>(AnimNodeType::CVar)] = 6;
nodeOrder[static_cast<int>(AnimNodeType::ScriptVar)] = 7;
nodeOrder[static_cast<int>(AnimNodeType::Material)] = 8;
nodeOrder[static_cast<int>(AnimNodeType::Event)] = 9;
nodeOrder[static_cast<int>(AnimNodeType::Layer)] = 10;
nodeOrder[static_cast<int>(AnimNodeType::Comment)] = 11;
nodeOrder[static_cast<int>(AnimNodeType::RadialBlur)] = 12;
nodeOrder[static_cast<int>(AnimNodeType::ColorCorrection)] = 13;
nodeOrder[static_cast<int>(AnimNodeType::DepthOfField)] = 14;
nodeOrder[static_cast<int>(AnimNodeType::ScreenFader)] = 15;
nodeOrder[static_cast<int>(AnimNodeType::Light)] = 16;
nodeOrder[static_cast<int>(AnimNodeType::ShadowSetup)] = 17;
nodeOrder[static_cast<int>(AnimNodeType::Environment)] = 18;
nodeOrder[static_cast<int>(AnimNodeType::Group)] = 19;
return nodeOrder[static_cast<int>(nodeType)];
}
}
bool CTrackViewNode::operator<(const CTrackViewNode& otherNode) const
{
// Order nodes before tracks
if (GetNodeType() < otherNode.GetNodeType())
{
return true;
}
else if (GetNodeType() > otherNode.GetNodeType())
{
return false;
}
// Same node type
switch (GetNodeType())
{
case eTVNT_AnimNode:
{
const CTrackViewAnimNode& thisAnimNode = static_cast<const CTrackViewAnimNode&>(*this);
const CTrackViewAnimNode& otherAnimNode = static_cast<const CTrackViewAnimNode&>(otherNode);
const int thisTypeOrder = GetNodeOrder(thisAnimNode.GetType());
const int otherTypeOrder = GetNodeOrder(otherAnimNode.GetType());
if (thisTypeOrder == otherTypeOrder)
{
// Same node type, sort by name
return azstricmp(thisAnimNode.GetName(), otherAnimNode.GetName()) < 0;
}
return thisTypeOrder < otherTypeOrder;
}
case eTVNT_Track:
const CTrackViewTrack& thisTrack = static_cast<const CTrackViewTrack&>(*this);
const CTrackViewTrack& otherTrack = static_cast<const CTrackViewTrack&>(otherNode);
if (thisTrack.GetParameterType() == otherTrack.GetParameterType())
{
// Same parameter type, sort by name
return azstricmp(thisTrack.GetName(), otherTrack.GetName()) < 0;
}
return thisTrack.GetParameterType() < otherTrack.GetParameterType();
}
return false;
}
//////////////////////////////////////////////////////////////////////////
void CTrackViewNode::SetHidden(bool bHidden)
{
bool bWasHidden = m_bHidden;
m_bHidden = bHidden;
if (bHidden && !bWasHidden)
{
GetSequence()->OnNodeChanged(this, ITrackViewSequenceListener::eNodeChangeType_Hidden);
}
else if (!bHidden && bWasHidden)
{
GetSequence()->OnNodeChanged(this, ITrackViewSequenceListener::eNodeChangeType_Unhidden);
}
}
//////////////////////////////////////////////////////////////////////////
bool CTrackViewNode::IsHidden() const
{
return m_bHidden;
}
//////////////////////////////////////////////////////////////////////////
CTrackViewNode* CTrackViewNode::GetFirstSelectedNode()
{
if (IsSelected())
{
return this;
}
const unsigned int numChilds = GetChildCount();
for (unsigned int childIndex = 0; childIndex < numChilds; ++childIndex)
{
CTrackViewNode* pSelectedNode = GetChild(childIndex)->GetFirstSelectedNode();
if (pSelectedNode)
{
return pSelectedNode;
}
}
return nullptr;
}
//////////////////////////////////////////////////////////////////////////
CTrackViewAnimNode* CTrackViewNode::GetDirector()
{
for (CTrackViewNode* pCurrentNode = GetParentNode(); pCurrentNode; pCurrentNode = pCurrentNode->GetParentNode())
{
if (pCurrentNode->GetNodeType() == eTVNT_AnimNode)
{
CTrackViewAnimNode* pParentAnimNode = static_cast<CTrackViewAnimNode*>(pCurrentNode);
if (pParentAnimNode->GetType() == AnimNodeType::Director)
{
return pParentAnimNode;
}
}
else if (pCurrentNode->GetNodeType() == eTVNT_Sequence)
{
return static_cast<CTrackViewAnimNode*>(pCurrentNode);
}
}
return nullptr;
}
+246
View File
@@ -0,0 +1,246 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef CRYINCLUDE_EDITOR_TRACKVIEW_TRACKVIEWNODE_H
#define CRYINCLUDE_EDITOR_TRACKVIEW_TRACKVIEWNODE_H
#pragma once
class CTrackViewTrack;
class CTrackViewSequence;
struct IKey;
class CTrackViewAnimNode;
class CTrackViewKeyConstHandle
{
public:
CTrackViewKeyConstHandle()
: m_bIsValid(false)
, m_keyIndex(0)
, m_pTrack(nullptr) {}
CTrackViewKeyConstHandle(const CTrackViewTrack* pTrack, unsigned int keyIndex)
: m_bIsValid(true)
, m_keyIndex(keyIndex)
, m_pTrack(pTrack) {}
void GetKey(IKey* pKey) const;
float GetTime() const;
const CTrackViewTrack* GetTrack() const { return m_pTrack; }
private:
bool m_bIsValid;
unsigned int m_keyIndex;
const CTrackViewTrack* m_pTrack;
};
// Represents one CryMovie key
class CTrackViewKeyHandle
{
public:
CTrackViewKeyHandle()
: m_bIsValid(false)
, m_keyIndex(0)
, m_pTrack(nullptr) {}
CTrackViewKeyHandle(CTrackViewTrack* pTrack, unsigned int keyIndex)
: m_bIsValid(true)
, m_keyIndex(keyIndex)
, m_pTrack(pTrack) {}
void SetKey(IKey* pKey);
void GetKey(IKey* pKey) const;
CTrackViewTrack* GetTrack() { return m_pTrack; }
const CTrackViewTrack* GetTrack() const { return m_pTrack; }
bool IsValid() const { return m_bIsValid; }
unsigned int GetIndex() const { return m_keyIndex; }
void Select(bool bSelect);
bool IsSelected() const;
void SetTime(float time, bool notifyListeners = true);
float GetTime() const;
float GetDuration() const;
const char* GetDescription() const;
void Offset(float offset, bool notifyListeners);
bool operator==(const CTrackViewKeyHandle& keyHandle) const;
bool operator!=(const CTrackViewKeyHandle& keyHandle) const;
// Deletes key. Note that handle will be invalid afterwards
void Delete();
CTrackViewKeyHandle Clone();
// Get next/prev/above/below key in expanded node tree
// Note: Key is assumed to be already visible
CTrackViewKeyHandle GetNextKey();
CTrackViewKeyHandle GetPrevKey();
CTrackViewKeyHandle GetAboveKey() const;
CTrackViewKeyHandle GetBelowKey() const;
private:
bool m_bIsValid;
unsigned int m_keyIndex;
CTrackViewTrack* m_pTrack;
};
// Abstract base class that defines common
// operations for key bundles and tracks
class ITrackViewKeyBundle
{
public:
virtual bool AreAllKeysOfSameType() const = 0;
virtual unsigned int GetKeyCount() const = 0;
virtual CTrackViewKeyHandle GetKey(unsigned int index) = 0;
virtual void SelectKeys(const bool bSelected) = 0;
};
// Represents a bundle of keys
class CTrackViewKeyBundle
: public ITrackViewKeyBundle
{
friend class CTrackViewTrack;
friend class CTrackViewAnimNode;
public:
CTrackViewKeyBundle()
: m_bAllOfSameType(true) {}
virtual bool AreAllKeysOfSameType() const override { return m_bAllOfSameType; }
virtual unsigned int GetKeyCount() const override { return m_keys.size(); }
virtual CTrackViewKeyHandle GetKey(unsigned int index) override { return m_keys[index]; }
virtual void SelectKeys(const bool bSelected) override;
CTrackViewKeyHandle GetSingleSelectedKey();
private:
void AppendKey(const CTrackViewKeyHandle& keyHandle);
void AppendKeyBundle(const CTrackViewKeyBundle& bundle);
bool m_bAllOfSameType;
std::vector<CTrackViewKeyHandle> m_keys;
};
// Types of nodes that derive from CTrackViewNode
enum ETrackViewNodeType
{
eTVNT_Sequence,
eTVNT_AnimNode,
eTVNT_Track
};
////////////////////////////////////////////////////////////////////////////
//
// This is the base class for all sequences, nodes and tracks in TrackView,
// which provides a interface for common operations
//
////////////////////////////////////////////////////////////////////////////
class CTrackViewNode
{
public:
CTrackViewNode(CTrackViewNode* pParent);
virtual ~CTrackViewNode() {}
// Name
virtual const char* GetName() const = 0;
virtual bool SetName([[maybe_unused]] const char* pName) { return false; };
virtual bool CanBeRenamed() const { return false; }
// CryMovie node type
virtual ETrackViewNodeType GetNodeType() const = 0;
// Get the sequence of this node
CTrackViewSequence* GetSequence();
const CTrackViewSequence* GetSequenceConst() const;
// Get parent
CTrackViewNode* GetParentNode() const { return m_pParentNode; }
// Children
unsigned int GetChildCount() const { return m_childNodes.size(); }
CTrackViewNode* GetChild(unsigned int index) const { return m_childNodes[index].get(); }
// Snap time value to prev/next key in sequence
virtual bool SnapTimeToPrevKey(float& time) const = 0;
virtual bool SnapTimeToNextKey(float& time) const = 0;
// Selection state
virtual void SetSelected(bool bSelected);
virtual bool IsSelected() const { return m_bSelected; }
// Clear selection of this node and all sub nodes
void ClearSelection();
// Expanded state interface
virtual void SetExpanded(bool expanded) = 0;
virtual bool GetExpanded() const = 0;
// Disabled state
virtual bool CanBeEnabled() const { return true; }
virtual void SetDisabled([[maybe_unused]] bool bDisabled) {}
virtual bool IsDisabled() const { return false; }
// Hidden state
void SetHidden(bool bHidden);
bool IsHidden() const;
// Key getters
virtual CTrackViewKeyBundle GetSelectedKeys() = 0;
virtual CTrackViewKeyBundle GetAllKeys() = 0;
virtual CTrackViewKeyBundle GetKeysInTimeRange(const float t0, const float t1) = 0;
// Check if node itself is obsolete, or any child is an obsolete track
bool HasObsoleteTrack() const;
// Get above/below nodes in pCurrentNode node tree
CTrackViewNode* GetAboveNode() const;
CTrackViewNode* GetBelowNode() const;
// Get previous or next sibling of this node
CTrackViewNode* GetPrevSibling() const;
CTrackViewNode* GetNextSibling() const;
// Check if it's a group node
virtual bool IsGroupNode() const { return false; }
// Copy selected keys to XML representation for clipboard
virtual void CopyKeysToClipboard(XmlNodeRef& xmlNode, const bool bOnlySelectedKeys, const bool bOnlyFromSelectedTracks) = 0;
// Sorting
bool operator<(const CTrackViewNode& pOtherNode) const;
// Get first selected node in tree
CTrackViewNode* GetFirstSelectedNode();
// Get director of this node
CTrackViewAnimNode* GetDirector();
protected:
void AddNode(CTrackViewNode* pNode);
void SortNodes();
bool HasObsoleteTrackRec(const CTrackViewNode* pCurrentNode) const;
CTrackViewNode* m_pParentNode;
std::vector<std::unique_ptr<CTrackViewNode> > m_childNodes;
bool m_bSelected;
bool m_bHidden;
};
#endif // CRYINCLUDE_EDITOR_TRACKVIEW_TRACKVIEWNODE_H
@@ -0,0 +1,41 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "EditorDefs.h"
#include "TrackViewNodeFactories.h"
// CryCommon
#include <CryCommon/Maestro/Types/AnimNodeType.h>
#include <CryCommon/Maestro/Types/AnimParamType.h>
// Editor
#include "TrackViewEventNode.h"
CTrackViewAnimNode* CTrackViewAnimNodeFactory::BuildAnimNode(IAnimSequence* pSequence, IAnimNode* pAnimNode, CTrackViewNode* pParentNode)
{
CTrackViewAnimNode* retNode = nullptr;
if (pAnimNode->GetType() == AnimNodeType::Event)
{
retNode = new CTrackViewEventNode(pSequence, pAnimNode, pParentNode);
}
else
{
retNode = new CTrackViewAnimNode(pSequence, pAnimNode, pParentNode);
}
return retNode;
}
CTrackViewTrack* CTrackViewTrackFactory::BuildTrack(IAnimTrack* pTrack, CTrackViewAnimNode* pTrackAnimNode,
CTrackViewNode* pParentNode, bool bIsSubTrack, unsigned int subTrackIndex)
{
return new CTrackViewTrack(pTrack, pTrackAnimNode, pParentNode, bIsSubTrack, subTrackIndex);
}
@@ -0,0 +1,30 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef CRYINCLUDE_EDITOR_TRACKVIEW_TRACKVIEWNODEFACTORIES_H
#define CRYINCLUDE_EDITOR_TRACKVIEW_TRACKVIEWNODEFACTORIES_H
#pragma once
class CTrackViewTrack;
class CTrackViewAnimNode;
class CTrackViewNode;
class CTrackViewAnimNodeFactory
{
public:
CTrackViewAnimNode* BuildAnimNode(IAnimSequence* pSequence, IAnimNode* pAnimNode, CTrackViewNode* pParentNode);
};
class CTrackViewTrackFactory
{
public:
CTrackViewTrack* BuildTrack(IAnimTrack* pTrack, CTrackViewAnimNode* pTrackAnimNode,
CTrackViewNode* pParentNode, bool bIsSubTrack = false, unsigned int subTrackIndex = 0);
};
#endif // CRYINCLUDE_EDITOR_TRACKVIEW_TRACKVIEWNODEFACTORIES_H
File diff suppressed because it is too large Load Diff
+224
View File
@@ -0,0 +1,224 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
// Description : TrackView's tree control.
#ifndef CRYINCLUDE_EDITOR_TRACKVIEW_TRACKVIEWNODES_H
#define CRYINCLUDE_EDITOR_TRACKVIEW_TRACKVIEWNODES_H
#pragma once
#if !defined(Q_MOC_RUN)
#include <AzCore/Component/Entity.h>
#include "TrackViewNode.h"
#include "TrackViewSequence.h"
#include "Undo/Undo.h"
#include "Export/ExportManager.h"
#include <IMovieSystem.h>
#include <QMap>
#include <QTreeWidgetItem>
#include <QWidget>
#endif
// forward declarations.
class CTrackViewNode;
class CTrackViewAnimNode;
class CTrackViewTrack;
class CTrackViewSequence;
class CTrackViewDopeSheetBase;
class CTrackViewDialog;
class QLineEdit;
namespace Ui {
class CTrackViewNodesCtrl;
}
//////////////////////////////////////////////////////////////////////////
class CTrackViewNodesCtrl
: public QWidget
, public ITrackViewSequenceListener
, public IUndoManagerListener
{
Q_OBJECT
public:
class CRecord
: public QTreeWidgetItem
{
friend class CTrackViewNodesCtrl;
public:
enum Roles
{
EnableRole = Qt::UserRole + 1
};
CRecord(CTrackViewNode* pNode = nullptr);
CTrackViewNode* GetNode() const { return m_pNode; }
bool IsGroup() const { return m_pNode->GetChildCount() != 0; }
const QString GetName() const { return m_pNode->GetName(); }
// Workaround: CXTPReportRecord::IsVisible is
// unreliable after the last visible element
bool IsVisible() const { return m_bVisible; }
QRect GetRect() const { return treeWidget()->visualItemRect(this); }
private:
bool m_bVisible;
CTrackViewNode* m_pNode;
};
CTrackViewNodesCtrl(QWidget* hParentWnd, CTrackViewDialog* parent = 0);
~CTrackViewNodesCtrl();
void SetTrackViewDialog(CTrackViewDialog* dlg) { m_pTrackViewDialog = dlg; }
void OnSequenceChanged();
void SetDopeSheet(CTrackViewDopeSheetBase* keysCtrl);
void SetEditLock(bool bLock) { m_bEditLock = bLock; }
float SaveVerticalScrollPos() const;
void RestoreVerticalScrollPos(float fScrollPos);
CRecord* GetNodeRecord(const CTrackViewNode* pNode) const;
virtual void Reload();
virtual void OnFillItems();
// ITrackViewSequenceListener
virtual void OnNodeChanged(CTrackViewNode* pNode, ITrackViewSequenceListener::ENodeChangeType type) override;
virtual void OnNodeRenamed(CTrackViewNode* pNode, const char* pOldName) override;
virtual void OnNodeSelectionChanged(CTrackViewSequence* pSequence) override;
virtual void OnKeysChanged(CTrackViewSequence* pSequence) override;
virtual void OnKeySelectionChanged(CTrackViewSequence* pSequence) override;
// IUndoManagerListener
virtual void BeginUndoTransaction() override;
virtual void EndUndoTransaction() override;
// Helper for dialog
QIcon GetIconForTrack(const CTrackViewTrack* pTrack);
void ShowNextResult();
void Update();
protected:
void paintEvent(QPaintEvent* event) override;
void keyPressEvent(QKeyEvent* event) override;
bool event(QEvent* event) override;
bool eventFilter(QObject* o, QEvent* e) override;
private slots:
void OnNMRclick(QPoint pos);
void OnItemExpanded(QTreeWidgetItem*);
void OnSelectionChanged();
void OnItemDblClick(QTreeWidgetItem* item, int);
void OnFilterChange(const QString& text);
private:
void CreateFolder(CTrackViewAnimNode* pGroupNode);
void EditEvents();
void ImportFromFBX();
CTrackViewTrack* GetTrackViewTrack(const Export::EntityAnimData* pAnimData, CTrackViewTrackBundle trackBundle, const QString& nodeName);
void AddMenuSeperatorConditional(QMenu& menu, bool& bAppended);
void AddGroupNodeAddItems(struct SContextMenu& contextMenu, CTrackViewAnimNode* pAnimNode);
int ShowPopupMenuSingleSelection(struct SContextMenu& contextMenu, CTrackViewSequence* pSequence, CTrackViewNode* pNode);
int ShowPopupMenuMultiSelection(struct SContextMenu& contextMenu);
int ShowPopupMenu(QPoint point, const CRecord* pItemInfo);
bool FillAddTrackMenu(struct STrackMenuTreeNode& menuAddTrack, const CTrackViewAnimNode* pAnimNode);
void CreateAddTrackMenuRec(QMenu& parent, const QString& name, CTrackViewAnimNode* animNode, struct STrackMenuTreeNode& node, unsigned int& currentId);
void SetPopupMenuLock(QMenu* menu);
void CreateSetAnimationLayerPopupMenu(QMenu& menuSetLayer, CTrackViewTrack* pTrack) const;
int GetIconIndexForTrack(const CTrackViewTrack* pTrack) const;
int GetIconIndexForNode(AnimNodeType type) const;
void AddNodeRecord(CRecord* pParentRecord, CTrackViewNode* pNode);
CRecord* AddTrackRecord(CRecord* pParentRecord, CTrackViewTrack* pTrack);
CRecord* AddAnimNodeRecord(CRecord* pParentRecord, CTrackViewAnimNode* pNode);
void FillNodesRec(CRecord* pRecord, CTrackViewNode* pCurrentNode);
void EraseNodeRecordRec(CTrackViewNode* pNode);
void UpdateNodeRecord(CRecord* pRecord);
void UpdateTrackRecord(CRecord* pRecord, CTrackViewTrack* pTrack);
void UpdateAnimNodeRecord(CRecord* pRecord, CTrackViewAnimNode* pAnimNode);
void FillAutoCompletionListForFilter();
// Utility function for handling material nodes
// It'll return -1 if the found material isn't a multi-material.
static int GetMatNameAndSubMtlIndexFromName(QString& matName, const char* nodeName);
void CustomizeTrackColor(CTrackViewTrack* pTrack);
void ClearCustomTrackColor(CTrackViewTrack* pTrack);
// For drawing dope sheet
void UpdateRecordVisibility();
void UpdateDopeSheet();
int GetInsertPosition(CRecord* pParentRecord, CTrackViewNode* pNode);
void SelectRow(CTrackViewNode* pNode, const bool bEnsureVisible, const bool bDeselectOtherRows);
void DeselectRow(CTrackViewNode* pNode);
CTrackViewDopeSheetBase* m_pDopeSheet;
CTrackViewDialog* m_pTrackViewDialog;
typedef std::vector<CRecord*> ItemInfos;
ItemInfos m_itemInfos;
bool m_bSelectionChanging;
bool m_bEditLock;
QCursor m_arrowCursor;
QCursor m_noIcon;
UINT m_currentMatchIndex;
UINT m_matchCount;
bool m_bIgnoreNotifications;
bool m_bNeedReload;
float m_storedScrollPosition;
// Drag and drop
CTrackViewAnimNodeBundle m_draggedNodes;
CTrackViewAnimNode* m_pDragTarget;
std::unordered_map<unsigned int, CAnimParamType> m_menuParamTypeMap;
std::unordered_map<const CTrackViewNode*, CRecord*> m_nodeToRecordMap;
QMap<int, QIcon> m_imageList;
QScopedPointer<Ui::CTrackViewNodesCtrl> ui;
//! Cached map of component icons.
//! Key: Component's RTTI type
//! Value: Icon for this component
AZStd::unordered_map<AZ::Uuid, QIcon> m_componentTypeToIconMap;
};
typedef CTrackViewNode* CTrackViewNodePtr;
Q_DECLARE_METATYPE(CTrackViewNodePtr);
QDataStream& operator<<(QDataStream& out, const CTrackViewNodePtr& obj);
QDataStream& operator>>(QDataStream& in, CTrackViewNodePtr& obj);
#endif // CRYINCLUDE_EDITOR_TRACKVIEW_TRACKVIEWNODES_H
+91
View File
@@ -0,0 +1,91 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>CTrackViewNodesCtrl</class>
<widget class="QWidget" name="CTrackViewNodesCtrl">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<property name="leftMargin">
<number>2</number>
</property>
<property name="topMargin">
<number>2</number>
</property>
<property name="rightMargin">
<number>2</number>
</property>
<property name="bottomMargin">
<number>2</number>
</property>
<item row="2" column="0">
<widget class="QLineEdit" name="searchField">
<property name="focusPolicy">
<enum>Qt::ClickFocus</enum>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="searchCount">
<property name="text">
<string>0/0</string>
</property>
</widget>
</item>
<item row="1" column="0" colspan="2">
<widget class="CTrackViewNodesTreeWidget" name="treeWidget">
<property name="dragEnabled">
<bool>true</bool>
</property>
<property name="dragDropMode">
<enum>QAbstractItemView::DragDrop</enum>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::ExtendedSelection</enum>
</property>
<attribute name="headerVisible">
<bool>false</bool>
</attribute>
<column>
<property name="text">
<string notr="true">1</string>
</property>
</column>
</widget>
</item>
<item row="0" column="0" colspan="2">
<widget class="QLabel" name="noitems">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>There are no items to show</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>CTrackViewNodesTreeWidget</class>
<extends>QTreeWidget</extends>
<header location="global">TrackView/TrackViewNodes.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,706 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "EditorDefs.h"
#include "TrackViewPythonFuncs.h"
// CryCommon
#include <CryCommon/Maestro/Types/AnimNodeType.h>
#include <CryCommon/Maestro/Types/AnimParamType.h>
#include <CryCommon/Maestro/Types/AnimValueType.h>
// Editor
#include "AnimationContext.h"
namespace
{
CTrackViewSequence* GetSequenceByEntityIdOrName(const CTrackViewSequenceManager* pSequenceManager, const char* entityIdOrName)
{
// the "name" string will be an AZ::EntityId in string form if this was called from
// TrackView code. But for backward compatibility we also support a sequence name.
bool isNameAValidU64 = false;
QString entityIdString = entityIdOrName;
AZ::u64 nameAsU64 = entityIdString.toULongLong(&isNameAValidU64);
CTrackViewSequence* pSequence = nullptr;
if (isNameAValidU64)
{
// "name" string was a valid u64 represented as a string. Use as an entity Id to search for sequence.
pSequence = pSequenceManager->GetSequenceByEntityId(AZ::EntityId(nameAsU64));
}
if (!pSequence)
{
// name passed in could not find a sequence by using it as an EntityId. Use it as a
// sequence name for backward compatibility
pSequence = pSequenceManager->GetSequenceByName(entityIdOrName);
}
return pSequence;
}
}
namespace
{
//////////////////////////////////////////////////////////////////////////
// Misc
//////////////////////////////////////////////////////////////////////////
void PyTrackViewSetRecording(bool bRecording)
{
CAnimationContext* pAnimationContext = GetIEditor()->GetAnimation();
if (pAnimationContext)
{
pAnimationContext->SetRecording(bRecording);
}
}
//////////////////////////////////////////////////////////////////////////
// Sequences
//////////////////////////////////////////////////////////////////////////
void PyTrackViewNewSequence(const char* name, int sequenceType)
{
CTrackViewSequenceManager* pSequenceManager = GetIEditor()->GetSequenceManager();
CTrackViewSequence* pSequence = pSequenceManager->GetSequenceByName(name);
if (pSequence)
{
throw std::runtime_error("A sequence with this name already exists");
}
CUndo undo("Create TrackView sequence");
pSequenceManager->CreateSequence(name, static_cast<SequenceType>(sequenceType));
}
void PyTrackViewDeleteSequence(const char* name)
{
CTrackViewSequenceManager* pSequenceManager = GetIEditor()->GetSequenceManager();
CTrackViewSequence* pSequence = GetSequenceByEntityIdOrName(pSequenceManager, name);
if (pSequence)
{
pSequenceManager->DeleteSequence(pSequence);
return;
}
throw std::runtime_error("Could not find sequence");
}
void PyTrackViewSetCurrentSequence(const char* name)
{
const CTrackViewSequenceManager* sequenceManager = GetIEditor()->GetSequenceManager();
CTrackViewSequence* sequence = GetSequenceByEntityIdOrName(sequenceManager, name);
CAnimationContext* animationContext = GetIEditor()->GetAnimation();
bool force = false;
bool noNotify = false;
bool user = true;
animationContext->SetSequence(sequence, force, noNotify, user);
}
int PyTrackViewGetNumSequences()
{
const CTrackViewSequenceManager* pSequenceManager = GetIEditor()->GetSequenceManager();
AZ_TracePrintf("", "PyTrackViewGetNumSequences called")
return pSequenceManager->GetCount();
}
AZStd::string PyTrackViewGetSequenceName(unsigned int index)
{
if (index < PyTrackViewGetNumSequences())
{
const CTrackViewSequenceManager* pSequenceManager = GetIEditor()->GetSequenceManager();
return pSequenceManager->GetSequenceByIndex(index)->GetName();
}
throw std::runtime_error("Could not find sequence");
}
Range PyTrackViewGetSequenceTimeRange(const char* name)
{
const CTrackViewSequenceManager* pSequenceManager = GetIEditor()->GetSequenceManager();
CTrackViewSequence* pSequence = GetSequenceByEntityIdOrName(pSequenceManager, name);
if (!pSequence)
{
throw std::runtime_error("A sequence with this name doesn't exists");
}
return pSequence->GetTimeRange();
}
void PyTrackViewSetSequenceTimeRange(const char* name, float start, float end)
{
const CTrackViewSequenceManager* pSequenceManager = GetIEditor()->GetSequenceManager();
CTrackViewSequence* pSequence = GetSequenceByEntityIdOrName(pSequenceManager, name);
if (!pSequence)
{
throw std::runtime_error("A sequence with this name doesn't exists");
}
CUndo undo("Set sequence time range");
pSequence->SetTimeRange(Range(start, end));
pSequence->MarkAsModified();
}
void PyTrackViewPlaySequence()
{
CAnimationContext* pAnimationContext = GetIEditor()->GetAnimation();
if (pAnimationContext->IsPlaying())
{
throw std::runtime_error("A sequence is already playing");
}
pAnimationContext->SetPlaying(true);
}
void PyTrackViewStopSequence()
{
CAnimationContext* pAnimationContext = GetIEditor()->GetAnimation();
if (!pAnimationContext->IsPlaying())
{
throw std::runtime_error("No sequence is playing");
}
pAnimationContext->SetPlaying(false);
}
void PyTrackViewSetSequenceTime(float time)
{
CAnimationContext* pAnimationContext = GetIEditor()->GetAnimation();
pAnimationContext->SetTime(time);
}
//////////////////////////////////////////////////////////////////////////
// Nodes
//////////////////////////////////////////////////////////////////////////
void PyTrackViewAddNode(const char* nodeTypeString, const char* nodeName)
{
CTrackViewSequence* pSequence = GetIEditor()->GetAnimation()->GetSequence();
if (!pSequence)
{
throw std::runtime_error("No sequence is active");
}
const AnimNodeType nodeType = GetIEditor()->GetMovieSystem()->GetNodeTypeFromString(nodeTypeString);
if (nodeType == AnimNodeType::Invalid)
{
throw std::runtime_error("Invalid node type");
}
CUndo undo("Create anim node");
pSequence->CreateSubNode(nodeName, nodeType);
}
void PyTrackViewAddSelectedEntities()
{
CAnimationContext* animationContext = GetIEditor()->GetAnimation();
CTrackViewSequence* sequence = animationContext->GetSequence();
if (!sequence)
{
throw std::runtime_error("No sequence is active");
}
AZStd::vector<AnimParamType> tracks = {
AnimParamType::Position,
AnimParamType::Rotation
};
AzToolsFramework::ScopedUndoBatch undoBatch("Add entities to Track View");
sequence->AddSelectedEntities(tracks);
sequence->BindToEditorObjects();
undoBatch.MarkEntityDirty(sequence->GetSequenceComponentEntityId());
}
void PyTrackViewAddLayerNode()
{
CAnimationContext* pAnimationContext = GetIEditor()->GetAnimation();
CTrackViewSequence* pSequence = pAnimationContext->GetSequence();
if (!pSequence)
{
throw std::runtime_error("No sequence is active");
}
CUndo undo("Add current layer to TrackView");
pSequence->AddCurrentLayer();
}
CTrackViewAnimNode* GetNodeFromName(const char* nodeName, const char* parentDirectorName)
{
CAnimationContext* pAnimationContext = GetIEditor()->GetAnimation();
CTrackViewSequence* pSequence = pAnimationContext->GetSequence();
if (!pSequence)
{
throw std::runtime_error("No sequence is active");
}
CTrackViewAnimNode* pParentDirector = pSequence;
if (strlen(parentDirectorName) > 0)
{
CTrackViewAnimNodeBundle foundNodes = pSequence->GetAnimNodesByName(parentDirectorName);
if (foundNodes.GetCount() == 0 || foundNodes.GetNode(0)->GetType() != AnimNodeType::Director)
{
throw std::runtime_error("Director node not found");
}
pParentDirector = foundNodes.GetNode(0);
}
CTrackViewAnimNodeBundle foundNodes = pParentDirector->GetAnimNodesByName(nodeName);
return (foundNodes.GetCount() > 0) ? foundNodes.GetNode(0) : nullptr;
}
void PyTrackViewDeleteNode(AZStd::string_view nodeName, AZStd::string_view parentDirectorName)
{
CTrackViewAnimNode* pNode = GetNodeFromName(nodeName.data(), parentDirectorName.data());
if (pNode == nullptr)
{
throw std::runtime_error("Couldn't find node");
}
CTrackViewAnimNode* pParentNode = static_cast<CTrackViewAnimNode*>(pNode->GetParentNode());
CUndo undo("Delete TrackView Node");
pParentNode->RemoveSubNode(pNode);
}
void PyTrackViewAddTrack(const char* paramName, const char* nodeName, const char* parentDirectorName)
{
CTrackViewAnimNode* pNode = GetNodeFromName(nodeName, parentDirectorName);
if (!pNode)
{
throw std::runtime_error("Couldn't find node");
}
// Add tracks to menu, that can be added to animation node.
const int paramCount = pNode->GetParamCount();
for (int i = 0; i < paramCount; ++i)
{
CAnimParamType paramType = pNode->GetParamType(i);
if (paramType == AnimParamType::Invalid)
{
continue;
}
IAnimNode::ESupportedParamFlags paramFlags = pNode->GetParamFlags(paramType);
CTrackViewTrack* pTrack = pNode->GetTrackForParameter(paramType);
if (!pTrack || (paramFlags & IAnimNode::eSupportedParamFlags_MultipleTracks))
{
const char* name = pNode->GetParamName(paramType);
if (_stricmp(name, paramName) == 0)
{
CUndo undo("Create track");
if (!pNode->CreateTrack(paramType))
{
undo.Cancel();
throw std::runtime_error("Could not create track");
}
pNode->SetSelected(true);
return;
}
}
}
throw std::runtime_error("Could not create track");
}
void PyTrackViewDeleteTrack(const char* paramName, uint32 index, const char* nodeName, const char* parentDirectorName)
{
CTrackViewAnimNode* pNode = GetNodeFromName(nodeName, parentDirectorName);
if (!pNode)
{
throw std::runtime_error("Couldn't find node");
}
const CAnimParamType paramType = GetIEditor()->GetMovieSystem()->GetParamTypeFromString(paramName);
CTrackViewTrack* pTrack = pNode->GetTrackForParameter(paramType, index);
if (!pTrack)
{
throw std::runtime_error("Could not find track");
}
CUndo undo("Delete TrackView track");
pNode->RemoveTrack(pTrack);
}
int PyTrackViewGetNumNodes(AZStd::string_view parentDirectorName)
{
CAnimationContext* pAnimationContext = GetIEditor()->GetAnimation();
CTrackViewSequence* pSequence = pAnimationContext->GetSequence();
if (!pSequence)
{
throw std::runtime_error("No sequence is active");
}
CTrackViewAnimNode* pParentDirector = pSequence;
if (!parentDirectorName.empty())
{
CTrackViewAnimNodeBundle foundNodes = pSequence->GetAnimNodesByName(parentDirectorName.data());
if (foundNodes.GetCount() == 0 || foundNodes.GetNode(0)->GetType() != AnimNodeType::Director)
{
throw std::runtime_error("Director node not found");
}
pParentDirector = foundNodes.GetNode(0);
}
CTrackViewAnimNodeBundle foundNodes = pParentDirector->GetAllAnimNodes();
return foundNodes.GetCount();
}
AZStd::string PyTrackViewGetNodeName(int index, AZStd::string_view parentDirectorName)
{
CAnimationContext* pAnimationContext = GetIEditor()->GetAnimation();
CTrackViewSequence* pSequence = pAnimationContext->GetSequence();
if (!pSequence)
{
throw std::runtime_error("No sequence is active");
}
CTrackViewAnimNode* pParentDirector = pSequence;
if (!parentDirectorName.empty())
{
CTrackViewAnimNodeBundle foundNodes = pSequence->GetAnimNodesByName(parentDirectorName.data());
if (foundNodes.GetCount() == 0 || foundNodes.GetNode(0)->GetType() != AnimNodeType::Director)
{
throw std::runtime_error("Director node not found");
}
pParentDirector = foundNodes.GetNode(0);
}
CTrackViewAnimNodeBundle foundNodes = pParentDirector->GetAllAnimNodes();
if (index < 0 || index >= foundNodes.GetCount())
{
throw std::runtime_error("Invalid node index");
}
return foundNodes.GetNode(index)->GetName();
}
//////////////////////////////////////////////////////////////////////////
// Tracks
//////////////////////////////////////////////////////////////////////////
CTrackViewTrack* GetTrack(const char* paramName, uint32 index, const char* nodeName, const char* parentDirectorName)
{
CTrackViewAnimNode* pNode = GetNodeFromName(nodeName, parentDirectorName);
if (!pNode)
{
throw std::runtime_error("Couldn't find node");
}
const CAnimParamType paramType = GetIEditor()->GetMovieSystem()->GetParamTypeFromString(paramName);
CTrackViewTrack* pTrack = pNode->GetTrackForParameter(paramType, index);
if (!pTrack)
{
throw std::runtime_error("Track doesn't exist");
}
return pTrack;
}
std::set<float> GetKeyTimeSet(CTrackViewTrack* pTrack)
{
std::set<float> keyTimeSet;
for (uint i = 0; i < pTrack->GetKeyCount(); ++i)
{
CTrackViewKeyHandle keyHandle = pTrack->GetKey(i);
keyTimeSet.insert(keyHandle.GetTime());
}
return keyTimeSet;
}
int PyTrackViewGetNumTrackKeys(const char* paramName, int trackIndex, const char* nodeName, const char* parentDirectorName)
{
CTrackViewTrack* pTrack = GetTrack(paramName, trackIndex, nodeName, parentDirectorName);
return (int)GetKeyTimeSet(pTrack).size();
}
AZStd::any PyTrackViewGetInterpolatedValue(const char* paramName, int trackIndex, float time, const char* nodeName, const char* parentDirectorName)
{
CTrackViewTrack* pTrack = GetTrack(paramName, trackIndex, nodeName, parentDirectorName);
switch (pTrack->GetValueType())
{
case AnimValueType::Float:
case AnimValueType::DiscreteFloat:
{
float value;
pTrack->GetValue(time, value);
return AZStd::make_any<float>(value);
}
break;
case AnimValueType::Bool:
{
bool value;
pTrack->GetValue(time, value);
return AZStd::make_any<bool>(value);
}
break;
case AnimValueType::Quat:
{
Quat value;
pTrack->GetValue(time, value);
Ang3 rotation(value);
return AZStd::make_any<AZ::Vector3>(rotation.x, rotation.y, rotation.z);
}
case AnimValueType::Vector:
{
Vec3 value;
pTrack->GetValue(time, value);
return AZStd::make_any<AZ::Vector3>(value.x, value.y, value.z);
}
break;
case AnimValueType::Vector4:
{
Vec4 value;
pTrack->GetValue(time, value);
return AZStd::make_any<AZ::Vector4>(value.x, value.y, value.z, value.w);
}
break;
case AnimValueType::RGB:
{
Vec3 value;
pTrack->GetValue(time, value);
return AZStd::make_any<AZ::Color>(value.x, value.y, value.z, 0.0f);
}
break;
default:
throw std::runtime_error("Unsupported key type");
}
}
AZStd::any PyTrackViewGetKeyValue(const char* paramName, int trackIndex, int keyIndex, const char* nodeName, const char* parentDirectorName)
{
CTrackViewTrack* pTrack = GetTrack(paramName, trackIndex, nodeName, parentDirectorName);
std::set<float> keyTimeSet = GetKeyTimeSet(pTrack);
if (keyIndex < 0 || keyIndex >= keyTimeSet.size())
{
throw std::runtime_error("Invalid key index");
}
auto keyTimeIter = keyTimeSet.begin();
std::advance(keyTimeIter, keyIndex);
const float keyTime = *keyTimeIter;
return PyTrackViewGetInterpolatedValue(paramName, trackIndex, keyTime, nodeName, parentDirectorName);
}
}
namespace AzToolsFramework
{
void TrackViewComponent::Reflect(AZ::ReflectContext* context)
{
if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
behaviorContext->EBus<EditorLayerTrackViewRequestBus>("EditorLayerTrackViewRequestBus")
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation)
->Attribute(AZ::Script::Attributes::Module, "track_view")
->Event("AddNode", &EditorLayerTrackViewRequestBus::Events::AddNode)
->Event("AddTrack", &EditorLayerTrackViewRequestBus::Events::AddTrack)
->Event("AddLayerNode", &EditorLayerTrackViewRequestBus::Events::AddLayerNode)
->Event("AddSelectedEntities", &EditorLayerTrackViewRequestBus::Events::AddSelectedEntities)
->Event("DeleteNode", &EditorLayerTrackViewRequestBus::Events::DeleteNode)
->Event("DeleteTrack", &EditorLayerTrackViewRequestBus::Events::DeleteTrack)
->Event("DeleteSequence", &EditorLayerTrackViewRequestBus::Events::DeleteSequence)
->Event("GetInterpolatedValue", &EditorLayerTrackViewRequestBus::Events::GetInterpolatedValue)
->Event("GetKeyValue", &EditorLayerTrackViewRequestBus::Events::GetKeyValue)
->Event("GetNodeName", &EditorLayerTrackViewRequestBus::Events::GetNodeName)
->Event("GetNumNodes", &EditorLayerTrackViewRequestBus::Events::GetNumNodes)
->Event("GetNumSequences", &EditorLayerTrackViewRequestBus::Events::GetNumSequences)
->Event("GetNumTrackKeys", &EditorLayerTrackViewRequestBus::Events::GetNumTrackKeys)
->Event("GetSequenceName", &EditorLayerTrackViewRequestBus::Events::GetSequenceName)
->Event("GetSequenceTimeRange", &EditorLayerTrackViewRequestBus::Events::GetSequenceTimeRange)
->Event("NewSequence", &EditorLayerTrackViewRequestBus::Events::NewSequence)
->Event("PlaySequence", &EditorLayerTrackViewRequestBus::Events::PlaySequence)
->Event("SetCurrentSequence", &EditorLayerTrackViewRequestBus::Events::SetCurrentSequence)
->Event("SetRecording", &EditorLayerTrackViewRequestBus::Events::SetRecording)
->Event("SetSequenceTimeRange", &EditorLayerTrackViewRequestBus::Events::SetSequenceTimeRange)
->Event("SetTime", &EditorLayerTrackViewRequestBus::Events::SetSequenceTime)
->Event("StopSequence", &EditorLayerTrackViewRequestBus::Events::StopSequence)
;
}
}
void TrackViewComponent::Activate()
{
EditorLayerTrackViewRequestBus::Handler::BusConnect(GetEntityId());
}
void TrackViewComponent::Deactivate()
{
EditorLayerTrackViewRequestBus::Handler::BusDisconnect();
}
int TrackViewComponent::GetNumSequences()
{
return PyTrackViewGetNumSequences();
}
void TrackViewComponent::NewSequence(const char* name, int sequenceType)
{
return PyTrackViewNewSequence(name, sequenceType);
}
void TrackViewComponent::PlaySequence()
{
return PyTrackViewPlaySequence();
}
void TrackViewComponent::StopSequence()
{
return PyTrackViewStopSequence();
}
void TrackViewComponent::SetSequenceTime(float time)
{
return PyTrackViewSetSequenceTime(time);
}
void TrackViewComponent::AddSelectedEntities()
{
return PyTrackViewAddSelectedEntities();
}
void TrackViewComponent::AddLayerNode()
{
return PyTrackViewAddLayerNode();
}
void TrackViewComponent::AddTrack(const char* paramName, const char* nodeName, const char* parentDirectorName)
{
return PyTrackViewAddTrack(paramName, nodeName, parentDirectorName);
}
void TrackViewComponent::DeleteTrack(const char* paramName, uint32 index, const char* nodeName, const char* parentDirectorName)
{
return PyTrackViewDeleteTrack(paramName, index, nodeName, parentDirectorName);
}
int TrackViewComponent::GetNumTrackKeys(const char* paramName, int trackIndex, const char* nodeName, const char* parentDirectorName)
{
return PyTrackViewGetNumTrackKeys(paramName, trackIndex, nodeName, parentDirectorName);
}
void TrackViewComponent::SetRecording(bool bRecording)
{
return PyTrackViewSetRecording(bRecording);
}
void TrackViewComponent::DeleteSequence(const char* name)
{
return PyTrackViewDeleteSequence(name);
}
void TrackViewComponent::SetCurrentSequence(const char* name)
{
return PyTrackViewSetCurrentSequence(name);
}
AZStd::string TrackViewComponent::GetSequenceName(unsigned int index)
{
return PyTrackViewGetSequenceName(index);
}
Range TrackViewComponent::GetSequenceTimeRange(const char* name)
{
return PyTrackViewGetSequenceTimeRange(name);
}
void TrackViewComponent::AddNode(const char* nodeTypeString, const char* nodeName)
{
return PyTrackViewAddNode(nodeTypeString, nodeName);
}
void TrackViewComponent::DeleteNode(AZStd::string_view nodeName, AZStd::string_view parentDirectorName)
{
return PyTrackViewDeleteNode(nodeName, parentDirectorName);
}
int TrackViewComponent::GetNumNodes(AZStd::string_view parentDirectorName)
{
return PyTrackViewGetNumNodes(parentDirectorName);
}
AZStd::string TrackViewComponent::GetNodeName(int index, AZStd::string_view parentDirectorName)
{
return PyTrackViewGetNodeName(index, parentDirectorName);
}
AZStd::any TrackViewComponent::GetKeyValue(const char* paramName, int trackIndex, int keyIndex, const char* nodeName, const char* parentDirectorName)
{
return PyTrackViewGetKeyValue(paramName, trackIndex, keyIndex, nodeName, parentDirectorName);
}
AZStd::any TrackViewComponent::GetInterpolatedValue(const char* paramName, int trackIndex, float time, const char* nodeName, const char* parentDirectorName)
{
return PyTrackViewGetInterpolatedValue(paramName, trackIndex, time, nodeName, parentDirectorName);
}
void TrackViewComponent::SetSequenceTimeRange(const char* name, float start, float end)
{
return PyTrackViewSetSequenceTimeRange(name, start, end);
}
}
namespace AzToolsFramework
{
void TrackViewFuncsHandler::Reflect(AZ::ReflectContext* context)
{
if (auto behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
behaviorContext->Class<Range>("CryRange")
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation)
->Attribute(AZ::Script::Attributes::Module, "legacy.trackview")
->Property("start", BehaviorValueProperty(&Range::start))
->Property("end", BehaviorValueProperty(&Range::end))
;
// this will put these methods into the 'azlmbr.legacy.trackview' module
auto addLegacyTrackview = [](AZ::BehaviorContext::GlobalMethodBuilder methodBuilder)
{
methodBuilder->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation)
->Attribute(AZ::Script::Attributes::Category, "Legacy/TrackView")
->Attribute(AZ::Script::Attributes::Module, "legacy.trackview");
};
addLegacyTrackview(behaviorContext->Method("set_recording", PyTrackViewSetRecording, nullptr, "Activates/deactivates TrackView recording mode."));
addLegacyTrackview(behaviorContext->Method("new_sequence", PyTrackViewNewSequence, nullptr, "Creates a new sequence of the given type (0=Object Entity Sequence (Legacy), 1=Component Entity Sequence (PREVIEW)) with the given name."));
addLegacyTrackview(behaviorContext->Method("delete_sequence", PyTrackViewDeleteSequence, nullptr, "Deletes the specified sequence."));
addLegacyTrackview(behaviorContext->Method("set_current_sequence", PyTrackViewSetCurrentSequence, nullptr, "Sets the specified sequence as a current one in TrackView."));
addLegacyTrackview(behaviorContext->Method("get_num_sequences", PyTrackViewGetNumSequences, nullptr, "Gets the number of sequences."));
addLegacyTrackview(behaviorContext->Method("get_sequence_name", PyTrackViewGetSequenceName, nullptr, "Gets the name of a sequence by its index."));
addLegacyTrackview(behaviorContext->Method("get_sequence_time_range", PyTrackViewGetSequenceTimeRange, nullptr, "Gets the time range of a sequence as a pair."));
addLegacyTrackview(behaviorContext->Method("set_sequence_time_range", PyTrackViewSetSequenceTimeRange, nullptr, "Sets the time range of a sequence."));
addLegacyTrackview(behaviorContext->Method("play_sequence", PyTrackViewPlaySequence, nullptr, "Plays the current sequence in TrackView."));
addLegacyTrackview(behaviorContext->Method("stop_sequence", PyTrackViewStopSequence, nullptr, "Stops any sequence currently playing in TrackView."));
addLegacyTrackview(behaviorContext->Method("set_time", PyTrackViewSetSequenceTime, nullptr, "Sets the time of the sequence currently playing in TrackView."));
addLegacyTrackview(behaviorContext->Method("add_node", PyTrackViewAddNode, nullptr, "Adds a new node with the given type & name to the current sequence."));
addLegacyTrackview(behaviorContext->Method("add_selected_entities", PyTrackViewAddSelectedEntities, nullptr, "Adds an entity node(s) from viewport selection to the current sequence."));
addLegacyTrackview(behaviorContext->Method("add_layer_node", PyTrackViewAddLayerNode, nullptr, "Adds a layer node from the current layer to the current sequence."));
addLegacyTrackview(behaviorContext->Method("delete_node", PyTrackViewDeleteNode, nullptr, "Deletes the specified node from the current sequence."));
addLegacyTrackview(behaviorContext->Method("add_track", PyTrackViewAddTrack, nullptr, "Adds a track of the given parameter ID to the node."));
addLegacyTrackview(behaviorContext->Method("delete_track", PyTrackViewDeleteTrack, nullptr, "Deletes a track of the given parameter ID (in the given index in case of a multi-track) from the node."));
addLegacyTrackview(behaviorContext->Method("get_num_nodes", PyTrackViewGetNumNodes, nullptr, "Gets the number of nodes."));
addLegacyTrackview(behaviorContext->Method("get_node_name", PyTrackViewGetNodeName, nullptr, "Gets the name of a sequence by its index."));
addLegacyTrackview(behaviorContext->Method("get_num_track_keys", PyTrackViewGetNumTrackKeys, nullptr, "Gets number of keys of the specified track."));
addLegacyTrackview(behaviorContext->Method("get_key_value", PyTrackViewGetKeyValue, nullptr, "Gets the value of the specified key."));
addLegacyTrackview(behaviorContext->Method("get_interpolated_value", PyTrackViewGetInterpolatedValue, nullptr, "Gets the interpolated value of a track at the specified time."));
}
}
}
@@ -0,0 +1,97 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzCore/Component/Component.h>
#include <Include/SandboxAPI.h>
#include "EditorTrackViewEventsBus.h"
namespace AzToolsFramework
{
//! A legacy component to reflect scriptable commands for the Editor
class TrackViewFuncsHandler
: public AZ::Component
{
public:
AZ_COMPONENT(TrackViewFuncsHandler, "{5315678D-2951-4CF6-A9DC-CE21CD23C9C9}")
SANDBOX_API static void Reflect(AZ::ReflectContext* context);
// AZ::Component ...
void Activate() override {}
void Deactivate() override {}
};
//! Component to access the TrackView
AZ_PUSH_DISABLE_DLL_EXPORT_BASECLASS_WARNING
AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
class SANDBOX_API TrackViewComponent final
: public AZ::Component
, public EditorLayerTrackViewRequestBus::Handler
{
AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
AZ_POP_DISABLE_DLL_EXPORT_BASECLASS_WARNING
public:
AZ_COMPONENT(TrackViewComponent, "{3CF943CC-6F10-4B19-88FC-CFB697558FFD}")
TrackViewComponent() = default;
~TrackViewComponent() override = default;
static void Reflect(AZ::ReflectContext* context);
// Component...
void Activate() override;
void Deactivate() override;
int GetNumSequences() override;
void NewSequence(const char* name, int sequenceType) override;
void PlaySequence() override;
void StopSequence() override;
void SetSequenceTime(float time) override;
void AddSelectedEntities() override;
void AddLayerNode() override;
void AddTrack(const char* paramName, const char* nodeName, const char* parentDirectorName) override;
void DeleteTrack(const char* paramName, uint32 index, const char* nodeName, const char* parentDirectorName) override;
int GetNumTrackKeys(const char* paramName, int trackIndex, const char* nodeName, const char* parentDirectorName) override;
void SetRecording(bool bRecording) override;
void DeleteSequence(const char* name) override;
void SetCurrentSequence(const char* name) override;
AZStd::string GetSequenceName(unsigned int index) override;
TRange<float> GetSequenceTimeRange(const char* name) override;
void AddNode(const char* nodeTypeString, const char* nodeName) override;
void DeleteNode(AZStd::string_view nodeName, AZStd::string_view parentDirectorName) override;
int GetNumNodes(AZStd::string_view parentDirectorName) override;
AZStd::string GetNodeName(int index, AZStd::string_view parentDirectorName) override;
AZStd::any GetKeyValue(const char* paramName, int trackIndex, int keyIndex, const char* nodeName, const char* parentDirectorName) override;
AZStd::any GetInterpolatedValue(const char* paramName, int trackIndex, float time, const char* nodeName, const char* parentDirectorName) override;
void SetSequenceTimeRange(const char* name, float start, float end) override;
};
} // namespace AzToolsFramework
File diff suppressed because it is too large Load Diff
+408
View File
@@ -0,0 +1,408 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef CRYINCLUDE_EDITOR_TRACKVIEW_TRACKVIEWSEQUENCE_H
#define CRYINCLUDE_EDITOR_TRACKVIEW_TRACKVIEWSEQUENCE_H
#pragma once
#include "IMovieSystem.h"
#include <AzToolsFramework/UI/PropertyEditor/PropertyEditorAPI.h>
#include "TrackViewAnimNode.h"
#include "Undo/Undo.h"
struct ITrackViewSequenceListener
{
// Called when sequence settings (time range, flags) have changed
virtual void OnSequenceSettingsChanged([[maybe_unused]] CTrackViewSequence* pSequence) {}
enum ENodeChangeType
{
eNodeChangeType_Added,
eNodeChangeType_Removed,
eNodeChangeType_Expanded,
eNodeChangeType_Collapsed,
eNodeChangeType_Hidden,
eNodeChangeType_Unhidden,
eNodeChangeType_Enabled,
eNodeChangeType_Disabled,
eNodeChangeType_Muted,
eNodeChangeType_Unmuted,
eNodeChangeType_Selected,
eNodeChangeType_Deselected,
eNodeChangeType_SetAsActiveDirector,
eNodeChangeType_NodeOwnerChanged
};
// Called when a node is changed
virtual void OnNodeChanged([[maybe_unused]] CTrackViewNode* pNode, [[maybe_unused]] ENodeChangeType type) {}
// Called when a node is added
virtual void OnNodeRenamed([[maybe_unused]] CTrackViewNode* pNode, [[maybe_unused]] const char* pOldName) {}
// Called when selection of nodes changed
virtual void OnNodeSelectionChanged([[maybe_unused]] CTrackViewSequence* pSequence) {}
// Called when selection of keys changed.
virtual void OnKeySelectionChanged([[maybe_unused]] CTrackViewSequence* pSequence) {}
// Called when keys in a track changed
virtual void OnKeysChanged([[maybe_unused]] CTrackViewSequence* pSequence) {}
// Called when a new key is added to a track
virtual void OnKeyAdded([[maybe_unused]] CTrackViewKeyHandle& addedKeyHandle) {}
};
struct ITrackViewSequenceManagerListener
{
virtual void OnSequenceAdded([[maybe_unused]] CTrackViewSequence* pSequence) {}
virtual void OnSequenceRemoved([[maybe_unused]] CTrackViewSequence* pSequence) {}
};
////////////////////////////////////////////////////////////////////////////
//
// This class represents a IAnimSequence in TrackView and contains
// the editor side code for changing it
//
////////////////////////////////////////////////////////////////////////////
class CTrackViewSequence
: public CTrackViewAnimNode
, public IUndoManagerListener
, public ITrackViewSequence
, public ITrackViewSequenceManagerListener
, public AzToolsFramework::PropertyEditorEntityChangeNotificationBus::MultiHandler
{
friend class CTrackViewSequenceManager;
friend class CTrackViewNode;
friend class CTrackViewAnimNode;
friend class CTrackViewTrack;
friend class CTrackViewSequenceNotificationContext;
friend class CTrackViewSequenceNoNotificationContext;
// Undo friends
friend class CUndoAnimNodeReparent;
friend class CUndoTrackObject;
friend class CUndoComponentEntityTrackObject;
public:
CTrackViewSequence(IAnimSequence* pSequence);
CTrackViewSequence(AZStd::intrusive_ptr<IAnimSequence>& sequence);
~CTrackViewSequence();
// Called after de-serialization of IAnimSequence
void Load() override;
// ITrackViewNode
virtual ETrackViewNodeType GetNodeType() const override { return eTVNT_Sequence; }
virtual const char* GetName() const override { return m_pAnimSequence->GetName(); }
virtual bool SetName(const char* pName) override;
virtual bool CanBeRenamed() const override { return true; }
// Binding/Unbinding
virtual void BindToEditorObjects() override;
virtual void UnBindFromEditorObjects() override;
virtual bool IsBoundToEditorObjects() const override;
// Time range
void SetTimeRange(Range timeRange);
Range GetTimeRange() const;
// Current time in sequence. Note that this can be different from the time
// of the animation context, if this sequence is used as a sub sequence
const float GetTime() const;
// CryMovie Flags
void SetFlags(IAnimSequence::EAnimSequenceFlags flags);
IAnimSequence::EAnimSequenceFlags GetFlags() const;
// Get sequence object in scene
AZ::EntityId GetSequenceComponentEntityId() const { return m_pAnimSequence.get() ? m_pAnimSequence->GetSequenceEntityId() : AZ::EntityId(); }
// Check if this node belongs to a sequence
bool IsAncestorOf(CTrackViewSequence* pSequence) const;
// Get single selected key if only one key is selected
CTrackViewKeyHandle FindSingleSelectedKey();
// Get CryMovie sequence ID
uint32 GetCryMovieId() const { return m_pAnimSequence->GetId(); }
// Rendering
virtual void Render(const SAnimContext& animContext) override;
// Playback control
virtual void Animate(const SAnimContext& animContext) override;
void Resume() { m_pAnimSequence->Resume(); }
void Pause() { m_pAnimSequence->Pause(); }
void StillUpdate() { m_pAnimSequence->StillUpdate(); }
void OnLoop() { m_pAnimSequence->OnLoop(); }
// Active & deactivate
void Activate() { m_pAnimSequence->Activate(); }
void Deactivate() { m_pAnimSequence->Deactivate(); }
void PrecacheData(const float time) { m_pAnimSequence->PrecacheData(time); }
// Begin & end cut scene
void BeginCutScene(const bool bResetFx) const;
void EndCutScene() const;
// Reset
void Reset(const bool bSeekToStart) { m_pAnimSequence->Reset(bSeekToStart); }
void ResetHard() { m_pAnimSequence->ResetHard(); }
void TimeChanged(float newTime) { m_pAnimSequence->TimeChanged(newTime); }
// Check if it's a group node
virtual bool IsGroupNode() const override { return true; }
// Track Events (TODO: Undo?)
int GetTrackEventsCount() const { return m_pAnimSequence->GetTrackEventsCount(); }
const char* GetTrackEvent(int index) { return m_pAnimSequence->GetTrackEvent(index); }
bool AddTrackEvent(const char* szEvent) { MarkAsModified(); return m_pAnimSequence->AddTrackEvent(szEvent); }
bool RemoveTrackEvent(const char* szEvent) { MarkAsModified(); return m_pAnimSequence->RemoveTrackEvent(szEvent); }
bool RenameTrackEvent(const char* szEvent, const char* szNewEvent) { MarkAsModified(); return m_pAnimSequence->RenameTrackEvent(szEvent, szNewEvent); }
bool MoveUpTrackEvent(const char* szEvent) { MarkAsModified(); return m_pAnimSequence->MoveUpTrackEvent(szEvent); }
bool MoveDownTrackEvent(const char* szEvent) { MarkAsModified(); return m_pAnimSequence->MoveDownTrackEvent(szEvent); }
void ClearTrackEvents() { MarkAsModified(); m_pAnimSequence->ClearTrackEvents(); }
// Deletes all selected nodes (re-parents childs if group node gets deleted)
void DeleteSelectedNodes();
// Select selected nodes in viewport
void SelectSelectedNodesInViewport();
// Deletes all selected keys
void DeleteSelectedKeys();
// Sync from/to base
void SyncSelectedTracksToBase();
void SyncSelectedTracksFromBase();
// Listeners
void AddListener(ITrackViewSequenceListener* pListener);
void RemoveListener(ITrackViewSequenceListener* pListener);
// Checks if this is the active sequence in TV
bool IsActiveSequence() const;
// The root sequence node is always an active director
virtual bool IsActiveDirector() const override { return true; }
// Copy keys to clipboard (in XML form)
void CopyKeysToClipboard(const bool bOnlySelectedKeys, const bool bOnlyFromSelectedTracks);
// Paste keys from clipboard. Tries to match the given data to the target track first,
// then the target anim node and finally the whole sequence. If it doesn't find any
// matching location, nothing will be pasted. Before pasting the given time offset is
// applied to the keys.
void PasteKeysFromClipboard(CTrackViewAnimNode* pTargetNode, CTrackViewTrack* pTargetTrack, const float timeOffset = 0.0f);
// Returns a vector of pairs that match the XML track nodes in the clipboard to the tracks in the sequence for pasting.
// It is used by PasteKeysFromClipboard directly and to preview the locations of the to be pasted keys.
typedef std::pair<CTrackViewTrack*, XmlNodeRef> TMatchedTrackLocation;
std::vector<TMatchedTrackLocation> GetMatchedPasteLocations(XmlNodeRef clipboardContent, CTrackViewAnimNode* pTargetNode, CTrackViewTrack* pTargetTrack);
// Adjust the time range
void AdjustKeysToTimeRange(Range newTimeRange);
// Clear all key selection
void DeselectAllKeys();
// Offset all key selection
void OffsetSelectedKeys(const float timeOffset);
// Scale all selected keys by this offset.
void ScaleSelectedKeys(const float timeOffset);
//! Push all the keys which come after the first key in time among selected ones by this offset.
void SlideKeys(const float timeOffset);
//! Clone all selected keys
void CloneSelectedKeys();
// Limit the time offset so as to keep all involved keys in range when offsetting.
float ClipTimeOffsetForOffsetting(const float timeOffset);
// Limit the time offset so as to keep all involved keys in range when scaling.
float ClipTimeOffsetForScaling(const float timeOffset);
// Limit the time offset so as to keep all involved keys in range when sliding.
float ClipTimeOffsetForSliding(const float timeOffset);
// Notifications
void OnSequenceSettingsChanged();
void OnKeySelectionChanged();
void OnKeysChanged();
void OnKeyAdded(CTrackViewKeyHandle& addedKeyHandle);
void OnNodeSelectionChanged();
void OnNodeChanged(CTrackViewNode* pNode, ITrackViewSequenceListener::ENodeChangeType type);
void OnNodeRenamed(CTrackViewNode* pNode, const char* pOldName);
// IAnimNodeOwner
void MarkAsModified() override;
// ~IAnimNodeOwner
SequenceType GetSequenceType() const
{
if (m_pAnimSequence.get())
{
return m_pAnimSequence->GetSequenceType();
}
else
{
return kSequenceTypeDefault;
}
}
void SetExpanded(bool expanded) override
{
if (m_pAnimSequence)
{
m_pAnimSequence->SetExpanded(expanded);
}
}
bool GetExpanded() const override
{
return m_pAnimSequence ? m_pAnimSequence->GetExpanded() : true;
}
// Called when the 'Record' button is pressed in the toolbar
void SetRecording(bool enableRecording);
/////////////////////////////////////////////////////////////////////////////////////////////////////
// PropertyEditorEntityChangeNotificationBus handler
void OnEntityComponentPropertyChanged(AZ::ComponentId /*changedComponentId*/) override;
/////////////////////////////////////////////////////////////////////////////////////////////////////
CTrackViewTrack* FindTrackById(unsigned int trackId);
std::vector<bool> SaveKeyStates() const;
void RestoreKeyStates(const std::vector<bool>& keyStates);
// Helper function to find a sequence by entity id
static CTrackViewSequence* LookUpSequenceByEntityId(const AZ::EntityId& sequenceId);
private:
// These are used to avoid listener notification spam via CTrackViewSequenceNotificationContext.
// For recursion there is a counter that increases on QueueListenerNotifications
// and decreases on SubmitPendingListenerNotifcations
// Only when the counter reaches 0 again SubmitPendingListenerNotifcations
// will submit the notifications
void QueueNotifications();
// Used to cancel a previously queued notification.
void DequeueNotifications();
void SubmitPendingNotifcations(bool force = false);
/////////////////////////////////////////////////////////////////////////
// overrides for ITrackViewSequenceManagerListener
void OnSequenceRemoved(CTrackViewSequence* pSequence) override;
void OnSequenceAdded(CTrackViewSequence* pSequence) override;
// Called when an animation updates needs to be schedules
void ForceAnimation();
virtual void CopyKeysToClipboard(XmlNodeRef& xmlNode, const bool bOnlySelectedKeys, const bool bOnlyFromSelectedTracks) override;
std::deque<CTrackViewTrack*> GetMatchingTracks(CTrackViewAnimNode* pAnimNode, XmlNodeRef trackNode);
void GetMatchedPasteLocationsRec(std::vector<TMatchedTrackLocation>& locations, CTrackViewNode* pCurrentNode, XmlNodeRef clipboardNode);
virtual void BeginUndoTransaction();
virtual void EndUndoTransaction();
virtual void BeginRestoreTransaction();
virtual void EndRestoreTransaction();
// For record mode on AZ::Entities - connect (or disconnect) to buses for notification of property changes
void ConnectToBusesForRecording(const AZ::EntityId& entityIdForBus, bool enableConnection);
// Searches for current property vs. Track values for the given node and sets a key for all values that differ.
// Returns the number of keys set
int RecordTrackChangesForNode(CTrackViewAnimNode* componentNode);
// Current time when animated
float m_time;
// Stores if sequence is bound
bool m_bBoundToEditorObjects = false;
AZStd::intrusive_ptr<IAnimSequence> m_pAnimSequence;
std::vector<ITrackViewSequenceListener*> m_sequenceListeners;
// Notification queuing
unsigned int m_selectionRecursionLevel = 0;
bool m_bNoNotifications = false;
bool m_bQueueNotifications = false;
bool m_bNodeSelectionChanged = false;
bool m_bForceAnimation = false;
bool m_bKeySelectionChanged = false;
bool m_bKeysChanged = false;
};
////////////////////////////////////////////////////////////////////////////
class CTrackViewSequenceNotificationContext
{
public:
CTrackViewSequenceNotificationContext(CTrackViewSequence* pSequence)
: m_pSequence(pSequence)
{
if (m_pSequence)
{
m_pSequence->QueueNotifications();
}
}
~CTrackViewSequenceNotificationContext()
{
if (m_pSequence)
{
m_pSequence->SubmitPendingNotifcations();
}
}
void Cancel()
{
if (m_pSequence)
{
m_pSequence->DequeueNotifications();
}
m_pSequence = nullptr;
}
private:
CTrackViewSequence* m_pSequence;
};
////////////////////////////////////////////////////////////////////////////
class CTrackViewSequenceNoNotificationContext
{
public:
CTrackViewSequenceNoNotificationContext(CTrackViewSequence* pSequence)
: m_pSequence(pSequence)
, m_bNoNotificationsPreviously(false)
{
if (m_pSequence)
{
m_bNoNotificationsPreviously = m_pSequence->m_bNoNotifications;
m_pSequence->m_bNoNotifications = true;
}
}
~CTrackViewSequenceNoNotificationContext()
{
if (m_pSequence)
{
m_pSequence->m_bNoNotifications = m_bNoNotificationsPreviously;
}
}
private:
CTrackViewSequence* m_pSequence;
// Reentrance could happen if there are overlapping sub-sequences controlling
// the same camera.
bool m_bNoNotificationsPreviously;
};
#endif // CRYINCLUDE_EDITOR_TRACKVIEW_TRACKVIEWSEQUENCE_H
@@ -0,0 +1,507 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "EditorDefs.h"
#include "TrackViewSequenceManager.h"
// AzToolsFramework
#include <AzToolsFramework/API/ComponentEntityObjectBus.h>
#include <AzToolsFramework/API/EntityCompositionRequestBus.h>
// CryCommon
#include <CryCommon/Maestro/Bus/EditorSequenceComponentBus.h>
#include <CryCommon/Maestro/Types/SequenceType.h>
// Editor
#include "AnimationContext.h"
#include "GameEngine.h"
#include "Include/IObjectManager.h"
#include "Objects/ObjectManager.h"
////////////////////////////////////////////////////////////////////////////
CTrackViewSequenceManager::CTrackViewSequenceManager()
{
GetIEditor()->RegisterNotifyListener(this);
AZ::EntitySystemBus::Handler::BusConnect();
}
////////////////////////////////////////////////////////////////////////////
CTrackViewSequenceManager::~CTrackViewSequenceManager()
{
AZ::EntitySystemBus::Handler::BusDisconnect();
GetIEditor()->UnregisterNotifyListener(this);
}
////////////////////////////////////////////////////////////////////////////
void CTrackViewSequenceManager::OnEditorNotifyEvent(EEditorNotifyEvent event)
{
switch (event)
{
case eNotify_OnBeginGameMode:
ResumeAllSequences();
break;
case eNotify_OnCloseScene:
// Fall through
case eNotify_OnBeginLoad:
m_bUnloadingLevel = true;
break;
case eNotify_OnEndNewScene:
// Fall through
case eNotify_OnEndSceneOpen:
// Fall through
case eNotify_OnEndLoad:
// Fall through
case eNotify_OnLayerImportEnd:
m_bUnloadingLevel = false;
SortSequences();
break;
}
}
////////////////////////////////////////////////////////////////////////////
CTrackViewSequence* CTrackViewSequenceManager::GetSequenceByName(QString name) const
{
for (auto iter = m_sequences.begin(); iter != m_sequences.end(); ++iter)
{
CTrackViewSequence* sequence = (*iter).get();
if (sequence->GetName() == name)
{
return sequence;
}
}
return nullptr;
}
////////////////////////////////////////////////////////////////////////////
CTrackViewSequence* CTrackViewSequenceManager::GetSequenceByEntityId(const AZ::EntityId& entityId) const
{
for (auto iter = m_sequences.begin(); iter != m_sequences.end(); ++iter)
{
CTrackViewSequence* sequence = (*iter).get();
if (sequence->GetSequenceComponentEntityId() == entityId)
{
return sequence;
}
}
return nullptr;
}
////////////////////////////////////////////////////////////////////////////
CTrackViewSequence* CTrackViewSequenceManager::GetSequenceByAnimSequence(IAnimSequence* pAnimSequence) const
{
for (auto iter = m_sequences.begin(); iter != m_sequences.end(); ++iter)
{
CTrackViewSequence* sequence = (*iter).get();
if (sequence->m_pAnimSequence == pAnimSequence)
{
return sequence;
}
}
return nullptr;
}
////////////////////////////////////////////////////////////////////////////
CTrackViewSequence* CTrackViewSequenceManager::GetSequenceByIndex(unsigned int index) const
{
if (index >= m_sequences.size())
{
return nullptr;
}
return m_sequences[index].get();
}
////////////////////////////////////////////////////////////////////////////
void CTrackViewSequenceManager::CreateSequence(QString name, [[maybe_unused]] SequenceType sequenceType)
{
CGameEngine* pGameEngine = GetIEditor()->GetGameEngine();
if (!pGameEngine || !pGameEngine->IsLevelLoaded())
{
return;
}
CTrackViewSequence* pExistingSequence = GetSequenceByName(name);
if (pExistingSequence)
{
return;
}
AzToolsFramework::ScopedUndoBatch undoBatch("Create TrackView Sequence");
// create AZ::Entity at the current center of the viewport, but don't select it
// Store the current selection for selection restore after the sequence component is created
AzToolsFramework::EntityIdList selectedEntities;
AzToolsFramework::ToolsApplicationRequests::Bus::BroadcastResult(selectedEntities, &AzToolsFramework::ToolsApplicationRequests::Bus::Events::GetSelectedEntities);
AZ::EntityId newEntityId; // initialized with InvalidEntityId
EBUS_EVENT_RESULT(newEntityId, AzToolsFramework::EditorRequests::Bus, CreateNewEntity, AZ::EntityId());
if (newEntityId.IsValid())
{
// set the entity name
AZ::Entity* entity = nullptr;
EBUS_EVENT_RESULT(entity, AZ::ComponentApplicationBus, FindEntity, newEntityId);
if (entity)
{
entity->SetName(static_cast<const char*>(name.toUtf8().data()));
}
// add the SequenceComponent. The SequenceComponent's Init() method will call OnCreateSequenceObject() which will actually create
// the sequence and connect it
// #TODO LY-21846: Use "SequenceService" to find component, rather than specific component-type.
AzToolsFramework::EntityCompositionRequestBus::Broadcast(&AzToolsFramework::EntityCompositionRequests::AddComponentsToEntities, AzToolsFramework::EntityIdList{ newEntityId }, AZ::ComponentTypeList{ "{C02DC0E2-D0F3-488B-B9EE-98E28077EC56}" });
// restore the Editor selection
AzToolsFramework::ToolsApplicationRequests::Bus::Broadcast(&AzToolsFramework::ToolsApplicationRequests::Bus::Events::SetSelectedEntities, selectedEntities);
undoBatch.MarkEntityDirty(newEntityId);
}
}
////////////////////////////////////////////////////////////////////////////
IAnimSequence* CTrackViewSequenceManager::OnCreateSequenceObject(QString name, bool isLegacySequence, AZ::EntityId entityId)
{
// Drop legacy sequences on the floor, they are no longer supported.
if (isLegacySequence)
{
GetIEditor()->GetMovieSystem()->LogUserNotificationMsg(AZStd::string::format("Legacy Sequences are no longer supported. Skipping '%s'.", name.toUtf8().data()));
return nullptr;
}
IAnimSequence* sequence = GetIEditor()->GetMovieSystem()->CreateSequence(name.toUtf8().data(), /*bload =*/ false, /*id =*/ 0U, SequenceType::SequenceComponent, entityId);
AZ_Assert(sequence, "Failed to create sequence");
AddTrackViewSequence(new CTrackViewSequence(sequence));
return sequence;
}
////////////////////////////////////////////////////////////////////////////
void CTrackViewSequenceManager::OnSequenceActivated(const AZ::EntityId& entityId)
{
CAnimationContext* pAnimationContext = GetIEditor()->GetAnimation();
if (pAnimationContext != nullptr)
{
pAnimationContext->OnSequenceActivated(entityId);
}
}
////////////////////////////////////////////////////////////////////////////
void CTrackViewSequenceManager::OnCreateSequenceComponent(AZStd::intrusive_ptr<IAnimSequence>& sequence)
{
// Fix up the internal pointers in the sequence to match the deserialized structure
sequence->InitPostLoad();
// Add the sequence to the movie system
GetIEditor()->GetMovieSystem()->AddSequence(sequence.get());
// Create the TrackView Sequence
CTrackViewSequence* newTrackViewSequence = new CTrackViewSequence(sequence);
AddTrackViewSequence(newTrackViewSequence);
}
////////////////////////////////////////////////////////////////////////////
void CTrackViewSequenceManager::AddTrackViewSequence(CTrackViewSequence* sequenceToAdd)
{
m_sequences.push_back(std::unique_ptr<CTrackViewSequence>(sequenceToAdd));
SortSequences();
OnSequenceAdded(sequenceToAdd);
}
////////////////////////////////////////////////////////////////////////////
void CTrackViewSequenceManager::DeleteSequence(CTrackViewSequence* sequence)
{
const int numSequences = m_sequences.size();
for (int sequenceIndex = 0; sequenceIndex < numSequences; ++sequenceIndex)
{
if (m_sequences[sequenceIndex].get() == sequence)
{
AzToolsFramework::ScopedUndoBatch undoBatch("Delete TrackView Sequence");
// delete Sequence Component (and entity if there's no other components left on the entity except for the Transform Component)
AZ::Entity* entity = nullptr;
AZ::EntityId entityId = sequence->m_pAnimSequence->GetSequenceEntityId();
AZ::ComponentApplicationBus::BroadcastResult(entity, &AZ::ComponentApplicationBus::Events::FindEntity, entityId);
if (entity)
{
const AZ::Uuid editorSequenceComponentTypeId(EditorSequenceComponentTypeId);
AZ::Component* sequenceComponent = entity->FindComponent(editorSequenceComponentTypeId);
if (sequenceComponent)
{
AZ::ComponentTypeList requiredComponents;
AzToolsFramework::EditorEntityContextRequestBus::BroadcastResult(requiredComponents, &AzToolsFramework::EditorEntityContextRequestBus::Events::GetRequiredComponentTypes);
const int numComponentToDeleteEntity = requiredComponents.size() + 1;
AZ::Entity::ComponentArrayType entityComponents = entity->GetComponents();
if (entityComponents.size() == numComponentToDeleteEntity)
{
// if the entity only has required components + 1 (the found sequenceComponent), delete the Entity. No need to start undo here
// AzToolsFramework::ToolsApplicationRequests::DeleteEntities will take care of that
AzToolsFramework::EntityIdList entitiesToDelete;
entitiesToDelete.push_back(entityId);
AzToolsFramework::ToolsApplicationRequests::Bus::Broadcast(&AzToolsFramework::ToolsApplicationRequests::DeleteEntities, entitiesToDelete);
}
else
{
// just remove the sequence component from the entity
CUndo undo("Delete TrackView Sequence");
AzToolsFramework::EntityCompositionRequestBus::Broadcast(&AzToolsFramework::EntityCompositionRequests::RemoveComponents, AZ::Entity::ComponentArrayType{ sequenceComponent });
}
undoBatch.MarkEntityDirty(entityId);
}
}
// sequence was deleted, we can stop searching
break;
}
}
}
//////////////////////////////////////////////////////////////////////////
void CTrackViewSequenceManager::RenameNode(CTrackViewAnimNode* animNode, const char* newName) const
{
AZ::EntityId entityId;
CTrackViewSequence* sequence = animNode->GetSequence();
AZ_Assert(sequence, "Nodes should never have a null sequence.");
if (animNode->IsBoundToEditorObjects())
{
if (animNode->GetNodeType() == eTVNT_Sequence)
{
CTrackViewSequence* sequenceNode = static_cast<CTrackViewSequence*>(animNode);
entityId = sequenceNode->GetSequenceComponentEntityId();
}
else if (animNode->GetNodeType() == eTVNT_AnimNode)
{
entityId = animNode->GetNodeEntityId();
}
}
if (entityId.IsValid())
{
AzToolsFramework::ScopedUndoBatch undoBatch("ModifyEntityName");
AZ::Entity* entity = nullptr;
AZ::ComponentApplicationBus::BroadcastResult(entity, &AZ::ComponentApplicationBus::Events::FindEntity, entityId);
entity->SetName(newName);
undoBatch.MarkEntityDirty(sequence->GetSequenceComponentEntityId());
}
else
{
AzToolsFramework::ScopedUndoBatch undoBatch("Rename TrackView Node");
animNode->SetName(newName);
undoBatch.MarkEntityDirty(sequence->GetSequenceComponentEntityId());
}
}
void CTrackViewSequenceManager::RemoveSequenceInternal(CTrackViewSequence* sequence)
{
std::unique_ptr<CTrackViewSequence> storedTrackViewSequence;
for (auto iter = m_sequences.begin(); iter != m_sequences.end(); ++iter)
{
std::unique_ptr<CTrackViewSequence>& currentSequence = *iter;
if (currentSequence.get() == sequence)
{
// Hang onto this until we finish this function.
currentSequence.swap(storedTrackViewSequence);
// Remove from CryMovie and TrackView
m_sequences.erase(iter);
IMovieSystem* pMovieSystem = GetIEditor()->GetMovieSystem();
pMovieSystem->RemoveSequence(sequence->m_pAnimSequence.get());
break;
}
}
OnSequenceRemoved(sequence);
}
////////////////////////////////////////////////////////////////////////////
void CTrackViewSequenceManager::OnDeleteSequenceEntity(const AZ::EntityId& entityId)
{
CTrackViewSequence* sequence = GetSequenceByEntityId(entityId);
assert(sequence);
if (sequence)
{
const bool bUndoWasSuspended = GetIEditor()->IsUndoSuspended();
bool isDuringUndo = false;
AzToolsFramework::ToolsApplicationRequests::Bus::BroadcastResult(isDuringUndo, &AzToolsFramework::ToolsApplicationRequests::Bus::Events::IsDuringUndoRedo);
if (bUndoWasSuspended)
{
GetIEditor()->ResumeUndo();
}
RemoveSequenceInternal(sequence);
if (bUndoWasSuspended)
{
GetIEditor()->SuspendUndo();
}
}
}
////////////////////////////////////////////////////////////////////////////
void CTrackViewSequenceManager::SortSequences()
{
std::stable_sort(m_sequences.begin(), m_sequences.end(),
[](const std::unique_ptr<CTrackViewSequence>& a, const std::unique_ptr<CTrackViewSequence>& b) -> bool
{
QString aName = a.get()->GetName();
QString bName = b.get()->GetName();
return aName < bName;
});
}
////////////////////////////////////////////////////////////////////////////
void CTrackViewSequenceManager::ResumeAllSequences()
{
for (auto iter = m_sequences.begin(); iter != m_sequences.end(); ++iter)
{
CTrackViewSequence* sequence = (*iter).get();
if (sequence)
{
sequence->Resume();
}
}
}
////////////////////////////////////////////////////////////////////////////
void CTrackViewSequenceManager::OnSequenceAdded(CTrackViewSequence* sequence)
{
for (auto iter = m_listeners.begin(); iter != m_listeners.end(); ++iter)
{
(*iter)->OnSequenceAdded(sequence);
}
}
////////////////////////////////////////////////////////////////////////////
void CTrackViewSequenceManager::OnSequenceRemoved(CTrackViewSequence* sequence)
{
for (auto iter = m_listeners.begin(); iter != m_listeners.end(); ++iter)
{
(*iter)->OnSequenceRemoved(sequence);
}
}
////////////////////////////////////////////////////////////////////////////
void CTrackViewSequenceManager::OnDataBaseItemEvent([[maybe_unused]] IDataBaseItem* pItem, EDataBaseItemEvent event)
{
if (event != EDataBaseItemEvent::EDB_ITEM_EVENT_ADD)
{
const uint numSequences = m_sequences.size();
for (uint i = 0; i < numSequences; ++i)
{
m_sequences[i]->UpdateDynamicParams();
}
}
}
////////////////////////////////////////////////////////////////////////////
CTrackViewAnimNodeBundle CTrackViewSequenceManager::GetAllRelatedAnimNodes(const AZ::EntityId entityId) const
{
CTrackViewAnimNodeBundle nodeBundle;
const uint sequenceCount = GetCount();
for (uint sequenceIndex = 0; sequenceIndex < sequenceCount; ++sequenceIndex)
{
CTrackViewSequence* sequence = GetSequenceByIndex(sequenceIndex);
nodeBundle.AppendAnimNodeBundle(sequence->GetAllOwnedNodes(entityId));
}
return nodeBundle;
}
////////////////////////////////////////////////////////////////////////////
CTrackViewAnimNode* CTrackViewSequenceManager::GetActiveAnimNode(const AZ::EntityId entityId) const
{
CTrackViewAnimNodeBundle nodeBundle = GetAllRelatedAnimNodes(entityId);
const uint nodeCount = nodeBundle.GetCount();
for (uint nodeIndex = 0; nodeIndex < nodeCount; ++nodeIndex)
{
CTrackViewAnimNode* animNode = nodeBundle.GetNode(nodeIndex);
if (animNode->IsActive())
{
return animNode;
}
}
return nullptr;
}
void CTrackViewSequenceManager::OnEntityNameChanged(const AZ::EntityId& entityId, const AZStd::string& name)
{
CTrackViewAnimNodeBundle bundle;
// entity or component entity sequence object
bundle = GetAllRelatedAnimNodes(entityId);
// GetAllRelatedAnimNodes only accounts for entities in the sequences, not the sequence entities themselves. We additionally check
// for sequence entities that have object as their entity object for renaming
const uint sequenceCount = GetCount();
for (uint sequenceIndex = 0; sequenceIndex < sequenceCount; ++sequenceIndex)
{
CTrackViewSequence* sequence = GetSequenceByIndex(sequenceIndex);
if (entityId == sequence->GetSequenceComponentEntityId())
{
bundle.AppendAnimNode(sequence);
}
}
const uint numAffectedNodes = bundle.GetCount();
for (uint i = 0; i < numAffectedNodes; ++i)
{
CTrackViewAnimNode* animNode = bundle.GetNode(i);
animNode->SetName(name.c_str());
}
if (numAffectedNodes > 0)
{
GetIEditor()->Notify(eNotify_OnReloadTrackView);
}
}
void CTrackViewSequenceManager::OnEntityDestruction(const AZ::EntityId& entityId)
{
// we handle pre-delete instead of delete because GetAllRelatedAnimNodes() uses the ObjectManager to find node owners
CTrackViewAnimNodeBundle bundle = GetAllRelatedAnimNodes(entityId);
const uint numAffectedAnimNodes = bundle.GetCount();
for (uint i = 0; i < numAffectedAnimNodes; ++i)
{
CTrackViewAnimNode* animNode = bundle.GetNode(i);
animNode->OnEntityRemoved();
}
if (numAffectedAnimNodes > 0)
{
// Only reload track view if the object being deleted has related anim nodes.
GetIEditor()->Notify(eNotify_OnReloadTrackView);
}
}
@@ -0,0 +1,84 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef CRYINCLUDE_EDITOR_TRACKVIEW_TRACKVIEWSEQUENCEMANAGER_H
#define CRYINCLUDE_EDITOR_TRACKVIEW_TRACKVIEWSEQUENCEMANAGER_H
#pragma once
#include "TrackViewSequence.h"
#include "IDataBaseManager.h"
#include <AzCore/Component/EntityBus.h>
class CTrackViewSequenceManager
: public IEditorNotifyListener
, public IDataBaseManagerListener
, public ITrackViewSequenceManager
, public AZ::EntitySystemBus::Handler
{
public:
CTrackViewSequenceManager();
~CTrackViewSequenceManager();
virtual void OnEditorNotifyEvent(EEditorNotifyEvent event);
unsigned int GetCount() const { return m_sequences.size(); }
void CreateSequence(QString name, SequenceType sequenceType);
void DeleteSequence(CTrackViewSequence* pSequence);
void RenameNode(CTrackViewAnimNode* pAnimNode, const char* newName) const;
CTrackViewSequence* GetSequenceByName(QString name) const override;
CTrackViewSequence* GetSequenceByEntityId(const AZ::EntityId& entityId) const override;
CTrackViewSequence* GetSequenceByIndex(unsigned int index) const;
CTrackViewSequence* GetSequenceByAnimSequence(IAnimSequence* pAnimSequence) const;
CTrackViewAnimNodeBundle GetAllRelatedAnimNodes(AZ::EntityId entityId) const;
CTrackViewAnimNode* GetActiveAnimNode(AZ::EntityId entityId) const;
void AddListener(ITrackViewSequenceManagerListener* pListener) { stl::push_back_unique(m_listeners, pListener); }
void RemoveListener(ITrackViewSequenceManagerListener* pListener) { stl::find_and_erase(m_listeners, pListener); }
// ITrackViewSequenceManager Overrides
// Callback from SequenceObject
IAnimSequence* OnCreateSequenceObject(QString name, bool isLegacySequence = true, AZ::EntityId entityId = AZ::EntityId()) override;
void OnDeleteSequenceEntity(const AZ::EntityId& entityId) override;
void OnCreateSequenceComponent(AZStd::intrusive_ptr<IAnimSequence>& sequence) override;
void OnSequenceActivated(const AZ::EntityId& entityId) override;
//~ ITrackViewSequenceManager Overrides
private:
void AddTrackViewSequence(CTrackViewSequence* sequenceToAdd);
void RemoveSequenceInternal(CTrackViewSequence* sequence);
void SortSequences();
void ResumeAllSequences();
void OnSequenceAdded(CTrackViewSequence* pSequence);
void OnSequenceRemoved(CTrackViewSequence* pSequence);
virtual void OnDataBaseItemEvent(IDataBaseItem* pItem, EDataBaseItemEvent event);
// AZ::EntitySystemBus
void OnEntityNameChanged(const AZ::EntityId& entityId, const AZStd::string& name) override;
void OnEntityDestruction(const AZ::EntityId& entityId) override;
std::vector<ITrackViewSequenceManagerListener*> m_listeners;
std::vector<std::unique_ptr<CTrackViewSequence> > m_sequences;
// Set to hold sequences that existed when undo transaction began
std::set<CTrackViewSequence*> m_transactionSequences;
bool m_bUnloadingLevel;
// Used to handle object attach/detach
std::unordered_map<CTrackViewNode*, Matrix34> m_prevTransforms;
};
#endif // CRYINCLUDE_EDITOR_TRACKVIEW_TRACKVIEWSEQUENCEMANAGER_H
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,78 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef CRYINCLUDE_EDITOR_TRACKVIEW_TRACKVIEWSPLINECTRL_H
#define CRYINCLUDE_EDITOR_TRACKVIEW_TRACKVIEWSPLINECTRL_H
#pragma once
#include <IMovieSystem.h>
#include "Controls/SplineCtrlEx.h"
#include <functional>
class CTrackViewTrack;
/** A customized spline control for CTrackViewGraph.
*/
class CTrackViewSplineCtrl
: public SplineWidget
{
friend class CUndoTrackViewSplineCtrl;
public:
CTrackViewSplineCtrl(QWidget* parent);
virtual ~CTrackViewSplineCtrl();
virtual void ClearSelection();
void AddSpline(ISplineInterpolator* pSpline, CTrackViewTrack* pTrack, const QColor& color);
void AddSpline(ISplineInterpolator * pSpline, CTrackViewTrack * pTrack, QColor anColorArray[4]);
const std::vector<CTrackViewTrack*>& GetTracks() const { return m_tracks; }
void RemoveAllSplines();
void OnUserCommand(UINT cmd);
bool IsUnifiedKeyCurrentlySelected() const;
bool IsKeysFrozen() const { return m_bKeysFreeze; }
bool IsTangentsFrozen() const { return m_bTangentsFreeze; }
void SetPlayCallback(const std::function<void()>& callback);
protected:
void mouseMoveEvent(QMouseEvent* event) override;
void mousePressEvent(QMouseEvent* event) override;
void mouseReleaseEvent(QMouseEvent* event) override;
void mouseDoubleClickEvent(QMouseEvent* event) override;
void keyPressEvent(QKeyEvent* event) override;
bool event(QEvent* event) override;
void wheelEvent(QWheelEvent* event) override;
private:
virtual void SelectKey(ISplineInterpolator* pSpline, int nKey, int nDimension, bool bSelect) override;
virtual void SelectRectangle(const QRect& rc, bool bSelect) override;
std::vector<CTrackViewTrack*> m_tracks;
virtual bool GetTangentHandlePts(QPoint& inTangentPt, QPoint& pt, QPoint& outTangentPt,
int nSpline, int nKey, int nDimension) override;
void ComputeIncomingTangentAndEaseTo(float& ds, float& easeTo, QPoint inTangentPt,
int nSpline, int nKey, int nDimension);
void ComputeOutgoingTangentAndEaseFrom(float& dd, float& easeFrom, QPoint outTangentPt,
int nSpline, int nKey, int nDimension);
void AdjustTCB(float d_tension, float d_continuity, float d_bias);
void MoveSelectedTangentHandleTo(const QPoint& point);
virtual ISplineCtrlUndo* CreateSplineCtrlUndoObject(std::vector<ISplineInterpolator*>& splineContainer);
bool m_bKeysFreeze;
bool m_bTangentsFreeze;
bool m_stashedRecordModeWhenDraggingTime;
std::function<void()> m_playCallback;
};
#endif // CRYINCLUDE_EDITOR_TRACKVIEW_TRACKVIEWSPLINECTRL_H
@@ -0,0 +1,36 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "EditorDefs.h"
#include "TrackViewTimeline.h"
// Editor
#include "AnimationContext.h"
namespace TrackView
{
void CTrackViewTimelineWidget::mousePressEvent(QMouseEvent* event)
{
m_stashedRecordModeWhileTimeDragging = GetIEditor()->GetAnimation()->IsRecordMode();
GetIEditor()->GetAnimation()->SetRecording(false); // disable recording while dragging time
TimelineWidget::mousePressEvent(event);
}
void CTrackViewTimelineWidget::mouseReleaseEvent(QMouseEvent* event)
{
TimelineWidget::mouseReleaseEvent(event);
if (m_stashedRecordModeWhileTimeDragging)
{
GetIEditor()->GetAnimation()->SetRecording(true); // restore recording
m_stashedRecordModeWhileTimeDragging = false; // reset stash
}
}
} // namespace TrackView
+33
View File
@@ -0,0 +1,33 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include "Controls/TimelineCtrl.h"
namespace TrackView
{
/** A customized timeline widget for CTrackViewGraph.
*/
class CTrackViewTimelineWidget
: public TimelineWidget
{
public:
CTrackViewTimelineWidget(QWidget* parent = nullptr)
: TimelineWidget(parent)
, m_stashedRecordModeWhileTimeDragging(false) {};
virtual ~CTrackViewTimelineWidget() = default;
protected:
void mousePressEvent(QMouseEvent* event) override;
void mouseReleaseEvent(QMouseEvent* event) override;
private:
bool m_stashedRecordModeWhileTimeDragging;
};
} // namespace TrackView
+829
View File
@@ -0,0 +1,829 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "EditorDefs.h"
#include "TrackViewTrack.h"
// CryCommon
#include <CryCommon/Maestro/Types/AnimParamType.h>
// Editor
#include "TrackViewSequence.h"
#include "TrackViewNodeFactories.h"
#include "TrackViewUndo.h"
//////////////////////////////////////////////////////////////////////////
void CTrackViewTrackBundle::AppendTrack(CTrackViewTrack* pTrack)
{
// Check if newly added key has different type than existing ones
if (m_bAllOfSameType && m_tracks.size() > 0)
{
const CTrackViewTrack* pLastTrack = m_tracks.back();
if (pTrack->GetParameterType() != pLastTrack->GetParameterType()
|| pTrack->GetCurveType() != pLastTrack->GetCurveType()
|| pTrack->GetValueType() != pLastTrack->GetValueType())
{
m_bAllOfSameType = false;
}
}
stl::push_back_unique(m_tracks, pTrack);
}
//////////////////////////////////////////////////////////////////////////
void CTrackViewTrackBundle::AppendTrackBundle(const CTrackViewTrackBundle& bundle)
{
for (auto iter = bundle.m_tracks.begin(); iter != bundle.m_tracks.end(); ++iter)
{
AppendTrack(*iter);
}
}
bool CTrackViewTrackBundle::RemoveTrack(CTrackViewTrack* trackToRemove)
{
return stl::find_and_erase(m_tracks, trackToRemove);
}
//////////////////////////////////////////////////////////////////////////
CTrackViewTrack::CTrackViewTrack(IAnimTrack* pTrack, CTrackViewAnimNode* pTrackAnimNode,
CTrackViewNode* pParentNode, bool bIsSubTrack, unsigned int subTrackIndex)
: CTrackViewNode(pParentNode)
, m_pAnimTrack(pTrack)
, m_pTrackAnimNode(pTrackAnimNode)
, m_bIsSubTrack(bIsSubTrack)
, m_subTrackIndex(subTrackIndex)
{
// Search for child tracks
const unsigned int subTrackCount = m_pAnimTrack->GetSubTrackCount();
for (unsigned int subTrackI = 0; subTrackI < subTrackCount; ++subTrackI)
{
IAnimTrack* pSubTrack = m_pAnimTrack->GetSubTrack(subTrackI);
CTrackViewTrackFactory trackFactory;
CTrackViewTrack* pNewTVTrack = trackFactory.BuildTrack(pSubTrack, pTrackAnimNode, this, true, subTrackI);
m_childNodes.push_back(std::unique_ptr<CTrackViewNode>(pNewTVTrack));
}
m_bIsCompoundTrack = subTrackCount > 0;
// Connect bus to listen for OnStart/StopPlayInEditor events
AzToolsFramework::EditorEntityContextNotificationBus::Handler::BusConnect();
}
CTrackViewTrack::~CTrackViewTrack()
{
AzToolsFramework::EditorEntityContextNotificationBus::Handler::BusDisconnect();
}
//////////////////////////////////////////////////////////////////////////
CTrackViewAnimNode* CTrackViewTrack::GetAnimNode() const
{
return m_pTrackAnimNode;
}
//////////////////////////////////////////////////////////////////////////
bool CTrackViewTrack::SnapTimeToPrevKey(float& time) const
{
CTrackViewKeyHandle prevKey = const_cast<CTrackViewTrack*>(this)->GetPrevKey(time);
if (prevKey.IsValid())
{
time = prevKey.GetTime();
return true;
}
return false;
}
//////////////////////////////////////////////////////////////////////////
bool CTrackViewTrack::SnapTimeToNextKey(float& time) const
{
CTrackViewKeyHandle prevKey = const_cast<CTrackViewTrack*>(this)->GetNextKey(time);
if (prevKey.IsValid())
{
time = prevKey.GetTime();
return true;
}
return false;
}
//////////////////////////////////////////////////////////////////////////
void CTrackViewTrack::SetExpanded(bool expanded)
{
if (m_pAnimTrack)
{
CTrackViewSequence* sequence = GetSequence();
if (nullptr != sequence)
{
if (GetExpanded() != expanded)
{
m_pAnimTrack->SetExpanded(expanded);
if (expanded)
{
sequence->OnNodeChanged(this, ITrackViewSequenceListener::eNodeChangeType_Expanded);
}
else
{
sequence->OnNodeChanged(this, ITrackViewSequenceListener::eNodeChangeType_Collapsed);
}
}
}
}
}
//////////////////////////////////////////////////////////////////////////
bool CTrackViewTrack::GetExpanded() const
{
return (m_pAnimTrack) ? m_pAnimTrack->GetExpanded() : false;
}
//////////////////////////////////////////////////////////////////////////
CTrackViewKeyHandle CTrackViewTrack::GetPrevKey(const float time)
{
CTrackViewKeyHandle keyHandle;
#ifdef max
#undef max
#endif
const float startTime = time;
float closestTime = -std::numeric_limits<float>::max();
const int numKeys = m_pAnimTrack->GetNumKeys();
for (int i = 0; i < numKeys; ++i)
{
const float keyTime = m_pAnimTrack->GetKeyTime(i);
if (keyTime < startTime && keyTime > closestTime)
{
keyHandle = CTrackViewKeyHandle(this, i);
closestTime = keyTime;
}
}
return keyHandle;
}
//////////////////////////////////////////////////////////////////////////
CTrackViewKeyHandle CTrackViewTrack::GetNextKey(const float time)
{
CTrackViewKeyHandle keyHandle;
const float startTime = time;
float closestTime = std::numeric_limits<float>::max();
const int numKeys = m_pAnimTrack->GetNumKeys();
for (int i = 0; i < numKeys; ++i)
{
const float keyTime = m_pAnimTrack->GetKeyTime(i);
if (keyTime > startTime && keyTime < closestTime)
{
keyHandle = CTrackViewKeyHandle(this, i);
closestTime = keyTime;
}
}
return keyHandle;
}
//////////////////////////////////////////////////////////////////////////
CTrackViewKeyBundle CTrackViewTrack::GetSelectedKeys()
{
CTrackViewKeyBundle bundle;
if (m_bIsCompoundTrack)
{
for (auto iter = m_childNodes.begin(); iter != m_childNodes.end(); ++iter)
{
bundle.AppendKeyBundle((*iter)->GetSelectedKeys());
}
}
else
{
bundle = GetKeys(true, -std::numeric_limits<float>::max(), std::numeric_limits<float>::max());
}
return bundle;
}
//////////////////////////////////////////////////////////////////////////
CTrackViewKeyBundle CTrackViewTrack::GetAllKeys()
{
CTrackViewKeyBundle bundle;
if (m_bIsCompoundTrack)
{
for (auto iter = m_childNodes.begin(); iter != m_childNodes.end(); ++iter)
{
bundle.AppendKeyBundle((*iter)->GetAllKeys());
}
}
else
{
bundle = GetKeys(false, -std::numeric_limits<float>::max(), std::numeric_limits<float>::max());
}
return bundle;
}
//////////////////////////////////////////////////////////////////////////
CTrackViewKeyBundle CTrackViewTrack::GetKeysInTimeRange(const float t0, const float t1)
{
CTrackViewKeyBundle bundle;
if (m_bIsCompoundTrack)
{
for (auto iter = m_childNodes.begin(); iter != m_childNodes.end(); ++iter)
{
bundle.AppendKeyBundle((*iter)->GetKeysInTimeRange(t0, t1));
}
}
else
{
bundle = GetKeys(false, t0, t1);
}
return bundle;
}
//////////////////////////////////////////////////////////////////////////
CTrackViewKeyBundle CTrackViewTrack::GetKeys(const bool bOnlySelected, const float t0, const float t1)
{
CTrackViewKeyBundle bundle;
const int keyCount = m_pAnimTrack->GetNumKeys();
for (int keyIndex = 0; keyIndex < keyCount; ++keyIndex)
{
const float keyTime = m_pAnimTrack->GetKeyTime(keyIndex);
const bool timeRangeOk = (t0 <= keyTime && keyTime <= t1);
if ((!bOnlySelected || IsKeySelected(keyIndex)) && timeRangeOk)
{
CTrackViewKeyHandle keyHandle(this, keyIndex);
bundle.AppendKey(keyHandle);
}
}
return bundle;
}
//////////////////////////////////////////////////////////////////////////
CTrackViewKeyHandle CTrackViewTrack::CreateKey(const float time)
{
const int keyIndex = m_pAnimTrack->CreateKey(time);
GetSequence()->OnKeysChanged();
CTrackViewKeyHandle createdKeyHandle(this, keyIndex);
GetSequence()->OnKeyAdded(createdKeyHandle);
return createdKeyHandle;
}
//////////////////////////////////////////////////////////////////////////
void CTrackViewTrack::SlideKeys(const float time0, const float timeOffset)
{
for (int i = 0; i < m_pAnimTrack->GetNumKeys(); ++i)
{
float keyTime = m_pAnimTrack->GetKeyTime(i);
if (keyTime >= time0)
{
m_pAnimTrack->SetKeyTime(i, keyTime + timeOffset);
}
}
}
//////////////////////////////////////////////////////////////////////////
void CTrackViewTrack::OffsetKeyPosition(const Vec3& offset)
{
// Use the CUndoComponentEntityTrackObject here and not the AZ Undo system because
// the Editor movement system uses CUndo as part of its move function (canceling last frame of undo whilst dragging).
CUndo::Record(new CUndoComponentEntityTrackObject(this));
m_pAnimTrack->OffsetKeyPosition(offset);
}
//////////////////////////////////////////////////////////////////////////
void CTrackViewTrack::UpdateKeyDataAfterParentChanged(const AZ::Transform& oldParentWorldTM, const AZ::Transform& newParentWorldTM)
{
AZStd::unique_ptr<AzToolsFramework::ScopedUndoBatch> undoBatch;
if (!AzToolsFramework::UndoRedoOperationInProgress())
{
undoBatch = AZStd::make_unique<AzToolsFramework::ScopedUndoBatch>("Update Key Data After Parent Changed");
}
m_pAnimTrack->UpdateKeyDataAfterParentChanged(oldParentWorldTM, newParentWorldTM);
if (undoBatch.get())
{
undoBatch->MarkEntityDirty(GetSequence()->GetSequenceComponentEntityId());
}
}
//////////////////////////////////////////////////////////////////////////
CTrackViewKeyHandle CTrackViewTrack::GetKey(unsigned int index)
{
if (index < GetKeyCount())
{
return CTrackViewKeyHandle(this, index);
}
return CTrackViewKeyHandle();
}
//////////////////////////////////////////////////////////////////////////
CTrackViewKeyConstHandle CTrackViewTrack::GetKey(unsigned int index) const
{
if (index < GetKeyCount())
{
return CTrackViewKeyConstHandle(this, index);
}
return CTrackViewKeyConstHandle();
}
//////////////////////////////////////////////////////////////////////////
CTrackViewKeyHandle CTrackViewTrack::GetKeyByTime(const float time)
{
if (m_bIsCompoundTrack)
{
// Search key in sub tracks
unsigned int currentIndex = 0;
unsigned int childCount = GetChildCount();
for (unsigned int i = 0; i < childCount; ++i)
{
CTrackViewTrack* pChildTrack = static_cast<CTrackViewTrack*>(GetChild(i));
int keyIndex = pChildTrack->m_pAnimTrack->FindKey(time);
if (keyIndex >= 0)
{
return CTrackViewKeyHandle(this, currentIndex + keyIndex);
}
currentIndex += pChildTrack->GetKeyCount();
}
}
int keyIndex = m_pAnimTrack->FindKey(time);
if (keyIndex < 0)
{
return CTrackViewKeyHandle();
}
return CTrackViewKeyHandle(this, keyIndex);
}
//////////////////////////////////////////////////////////////////////////
CTrackViewKeyHandle CTrackViewTrack::GetNearestKeyByTime(const float time)
{
int minDelta = std::numeric_limits<int>::max();
const unsigned int keyCount = GetKeyCount();
for (unsigned int i = 0; i < keyCount; ++i)
{
CTrackViewKeyHandle keyHandle = GetKey(i);
const int deltaT = abs((int)keyHandle.GetTime() - (int)time);
// If deltaT got larger since last key, then the last key
// was the key with minimum temporal distance to the given time
if (deltaT > minDelta)
{
return CTrackViewKeyHandle(this, i - 1);
}
minDelta = std::min(minDelta, deltaT);
}
// If we didn't return above and there are keys, then the
// last key needs to be the one with minimum distance
if (keyCount > 0)
{
return CTrackViewKeyHandle(this, keyCount - 1);
}
// No keys
return CTrackViewKeyHandle();
}
//////////////////////////////////////////////////////////////////////////
void CTrackViewTrack::GetKeyValueRange(float& min, float& max) const
{
m_pAnimTrack->GetKeyValueRange(min, max);
}
//////////////////////////////////////////////////////////////////////////
ColorB CTrackViewTrack::GetCustomColor() const
{
return m_pAnimTrack->GetCustomColor();
}
//////////////////////////////////////////////////////////////////////////
void CTrackViewTrack::SetCustomColor(ColorB color)
{
m_pAnimTrack->SetCustomColor(color);
}
//////////////////////////////////////////////////////////////////////////
bool CTrackViewTrack::HasCustomColor() const
{
return m_pAnimTrack->HasCustomColor();
}
//////////////////////////////////////////////////////////////////////////
void CTrackViewTrack::ClearCustomColor()
{
m_pAnimTrack->ClearCustomColor();
}
//////////////////////////////////////////////////////////////////////////
IAnimTrack::EAnimTrackFlags CTrackViewTrack::GetFlags() const
{
return (IAnimTrack::EAnimTrackFlags)m_pAnimTrack->GetFlags();
}
//////////////////////////////////////////////////////////////////////////
CTrackViewTrackMemento CTrackViewTrack::GetMemento() const
{
CTrackViewTrackMemento memento;
memento.m_serializedTrackState = XmlHelpers::CreateXmlNode("TrackState");
m_pAnimTrack->Serialize(memento.m_serializedTrackState, false);
return memento;
}
//////////////////////////////////////////////////////////////////////////
void CTrackViewTrack::RestoreFromMemento(const CTrackViewTrackMemento& memento)
{
// We're going to de-serialize, so this is const safe
XmlNodeRef& xmlNode = const_cast<XmlNodeRef&>(memento.m_serializedTrackState);
m_pAnimTrack->Serialize(xmlNode, true);
}
//////////////////////////////////////////////////////////////////////////
const char* CTrackViewTrack::GetName() const
{
CTrackViewNode* pParentNode = GetParentNode();
if (pParentNode->GetNodeType() == eTVNT_Track)
{
CTrackViewTrack* pParentTrack = static_cast<CTrackViewTrack*>(pParentNode);
return pParentTrack->m_pAnimTrack->GetSubTrackName(m_subTrackIndex);
}
return GetAnimNode()->GetParamName(GetParameterType());
}
//////////////////////////////////////////////////////////////////////////
void CTrackViewTrack::SetDisabled(bool bDisabled)
{
if (bDisabled)
{
m_pAnimTrack->SetFlags(m_pAnimTrack->GetFlags() | IAnimTrack::eAnimTrackFlags_Disabled);
GetSequence()->OnNodeChanged(this, ITrackViewSequenceListener::eNodeChangeType_Disabled);
}
else
{
m_pAnimTrack->SetFlags(m_pAnimTrack->GetFlags() & ~IAnimTrack::eAnimTrackFlags_Disabled);
GetSequence()->OnNodeChanged(this, ITrackViewSequenceListener::eNodeChangeType_Enabled);
}
}
//////////////////////////////////////////////////////////////////////////
bool CTrackViewTrack::IsDisabled() const
{
return m_pAnimTrack->GetFlags() & IAnimTrack::eAnimTrackFlags_Disabled;
}
//////////////////////////////////////////////////////////////////////////
void CTrackViewTrack::SetMuted(bool bMuted)
{
if (UsesMute())
{
if (bMuted)
{
m_pAnimTrack->SetFlags(m_pAnimTrack->GetFlags() | IAnimTrack::eAnimTrackFlags_Muted);
GetSequence()->OnNodeChanged(this, ITrackViewSequenceListener::eNodeChangeType_Muted);
}
else
{
m_pAnimTrack->SetFlags(m_pAnimTrack->GetFlags() & ~IAnimTrack::eAnimTrackFlags_Muted);
GetSequence()->OnNodeChanged(this, ITrackViewSequenceListener::eNodeChangeType_Unmuted);
}
}
}
//////////////////////////////////////////////////////////////////////////
// Returns whether the track is muted, or false if the track does not use muting
bool CTrackViewTrack::IsMuted() const
{
return m_pAnimTrack->UsesMute() ? (m_pAnimTrack->GetFlags() & IAnimTrack::eAnimTrackFlags_Muted) : false;
}
//////////////////////////////////////////////////////////////////////////
void CTrackViewTrack::SetKey(unsigned int keyIndex, IKey* pKey)
{
m_pAnimTrack->SetKey(keyIndex, pKey);
m_pTrackAnimNode->GetSequence()->OnKeysChanged();
}
//////////////////////////////////////////////////////////////////////////
void CTrackViewTrack::GetKey(unsigned int keyIndex, IKey* pKey) const
{
m_pAnimTrack->GetKey(keyIndex, pKey);
}
//////////////////////////////////////////////////////////////////////////
void CTrackViewTrack::SelectKey(unsigned int keyIndex, bool bSelect)
{
const bool bWasSelected = m_pAnimTrack->IsKeySelected(keyIndex);
m_pAnimTrack->SelectKey(keyIndex, bSelect);
if (bSelect != bWasSelected)
{
m_pTrackAnimNode->GetSequence()->OnKeySelectionChanged();
}
}
//////////////////////////////////////////////////////////////////////////
void CTrackViewTrack::SetKeyTime(const int index, const float time, bool notifyListeners)
{
const float bOldTime = m_pAnimTrack->GetKeyTime(index);
m_pAnimTrack->SetKeyTime(index, time);
if (notifyListeners && (bOldTime != time))
{
// The keys were just make invalid by the above SetKeyTime(), so sort them now
// to make sure they are ready to be used. Only do this when notifyListeners
// is set so client callers can batch up a bunch of SetKeyTime calls if desired.
m_pAnimTrack->SortKeys();
m_pTrackAnimNode->GetSequence()->OnKeysChanged();
}
}
//////////////////////////////////////////////////////////////////////////
float CTrackViewTrack::GetKeyTime(const int index) const
{
return m_pAnimTrack->GetKeyTime(index);
}
//////////////////////////////////////////////////////////////////////////
void CTrackViewTrack::RemoveKey(const int index)
{
m_pAnimTrack->RemoveKey(index);
m_pTrackAnimNode->GetSequence()->OnKeysChanged();
}
//////////////////////////////////////////////////////////////////////////
int CTrackViewTrack::CloneKey(const int index)
{
int newIndex = m_pAnimTrack->CloneKey(index);
m_pTrackAnimNode->GetSequence()->OnKeysChanged();
return newIndex;
}
//////////////////////////////////////////////////////////////////////////
void CTrackViewTrack::SelectKeys(const bool bSelected)
{
m_pTrackAnimNode->GetSequence()->QueueNotifications();
if (!m_bIsCompoundTrack)
{
unsigned int keyCount = GetKeyCount();
for (unsigned int i = 0; i < keyCount; ++i)
{
m_pAnimTrack->SelectKey(i, bSelected);
m_pTrackAnimNode->GetSequence()->OnKeySelectionChanged();
}
}
else
{
// Affect sub tracks
unsigned int childCount = GetChildCount();
for (unsigned int childIndex = 0; childIndex < childCount; ++childIndex)
{
CTrackViewTrack* pChildTrack = static_cast<CTrackViewTrack*>(GetChild(childIndex));
pChildTrack->SelectKeys(bSelected);
m_pTrackAnimNode->GetSequence()->OnKeySelectionChanged();
}
}
m_pTrackAnimNode->GetSequence()->SubmitPendingNotifcations();
}
//////////////////////////////////////////////////////////////////////////
bool CTrackViewTrack::IsKeySelected(unsigned int keyIndex) const
{
if (m_pAnimTrack)
{
return m_pAnimTrack->IsKeySelected(keyIndex);
}
return false;
}
//////////////////////////////////////////////////////////////////////////
void CTrackViewTrack::SetSortMarkerKey(unsigned int keyIndex, bool enabled)
{
if (m_pAnimTrack)
{
return m_pAnimTrack->SetSortMarkerKey(keyIndex, enabled);
}
}
//////////////////////////////////////////////////////////////////////////
bool CTrackViewTrack::IsSortMarkerKey(unsigned int keyIndex) const
{
if (m_pAnimTrack)
{
return m_pAnimTrack->IsSortMarkerKey(keyIndex);
}
return false;
}
//////////////////////////////////////////////////////////////////////////
CTrackViewKeyHandle CTrackViewTrack::GetSubTrackKeyHandle(unsigned int index) const
{
// Return handle to sub track key
unsigned int childCount = GetChildCount();
for (unsigned int childIndex = 0; childIndex < childCount; ++childIndex)
{
CTrackViewTrack* pChildTrack = static_cast<CTrackViewTrack*>(GetChild(childIndex));
const unsigned int childKeyCount = pChildTrack->GetKeyCount();
if (index < childKeyCount)
{
return pChildTrack->GetKey(index);
}
index -= childKeyCount;
}
return CTrackViewKeyHandle();
}
//////////////////////////////////////////////////////////////////////////
void CTrackViewTrack::SetAnimationLayerIndex(const int index)
{
if (m_pAnimTrack)
{
m_pAnimTrack->SetAnimationLayerIndex(index);
}
}
//////////////////////////////////////////////////////////////////////////
int CTrackViewTrack::GetAnimationLayerIndex() const
{
return m_pAnimTrack->GetAnimationLayerIndex();
}
//////////////////////////////////////////////////////////////////////////
void CTrackViewTrack::OnStartPlayInEditor()
{
// remap any AZ::EntityId's used in tracks
if (m_pAnimTrack)
{
// OnStopPlayInEditor clears this as well, but we clear it here in case OnStartPlayInEditor() is called multiple times before OnStopPlayInEditor()
m_paramTypeToStashedEntityIdMap.clear();
CAnimParamType trackParamType = m_pAnimTrack->GetParameterType();
const AnimParamType paramType = trackParamType.GetType();
if (paramType == AnimParamType::Camera || paramType == AnimParamType::Sequence)
{
ISelectKey selectKey;
ISequenceKey sequenceKey;
IKey* key = nullptr;
for (int i = 0; i < m_pAnimTrack->GetNumKeys(); i++)
{
AZ::EntityId entityIdToRemap;
if (paramType == AnimParamType::Camera)
{
m_pAnimTrack->GetKey(i, &selectKey);
entityIdToRemap = selectKey.cameraAzEntityId;
key = &selectKey;
}
else if (paramType == AnimParamType::Sequence)
{
m_pAnimTrack->GetKey(i, &sequenceKey);
entityIdToRemap = sequenceKey.sequenceEntityId;
key = &sequenceKey;
}
// stash the entity Id for restore in OnStopPlayInEditor
m_paramTypeToStashedEntityIdMap[trackParamType].push_back(entityIdToRemap);
if (entityIdToRemap.IsValid())
{
AZ::EntityId remappedId;
AzToolsFramework::EditorEntityContextRequestBus::Broadcast(&AzToolsFramework::EditorEntityContextRequestBus::Events::MapEditorIdToRuntimeId, entityIdToRemap, remappedId);
// remap
if (paramType == AnimParamType::Camera)
{
selectKey.cameraAzEntityId = remappedId;
}
else if (paramType == AnimParamType::Sequence)
{
sequenceKey.sequenceEntityId = remappedId;
}
m_pAnimTrack->SetKey(i, key);
}
}
}
}
}
//////////////////////////////////////////////////////////////////////////
void CTrackViewTrack::OnStopPlayInEditor()
{
// restore any AZ::EntityId's remapped in OnStartPlayInEditor
if (m_pAnimTrack && m_paramTypeToStashedEntityIdMap.size())
{
CAnimParamType trackParamType = m_pAnimTrack->GetParameterType();
const AnimParamType paramType = trackParamType.GetType();
if (paramType == AnimParamType::Camera || paramType == AnimParamType::Sequence)
{
for (int i = 0; i < m_pAnimTrack->GetNumKeys(); i++)
{
ISelectKey selectKey;
ISequenceKey sequenceKey;
IKey* key = nullptr;
// restore entityIds
if (paramType == AnimParamType::Camera)
{
m_pAnimTrack->GetKey(i, &selectKey);
selectKey.cameraAzEntityId = m_paramTypeToStashedEntityIdMap[trackParamType][i];
key = &selectKey;
}
else if (paramType == AnimParamType::Sequence)
{
m_pAnimTrack->GetKey(i, &sequenceKey);
sequenceKey.sequenceEntityId = m_paramTypeToStashedEntityIdMap[trackParamType][i];
key = &sequenceKey;
}
m_pAnimTrack->SetKey(i, key);
}
}
// clear the StashedEntityIdMap now that we've consumed it
m_paramTypeToStashedEntityIdMap.clear();
}
}
//////////////////////////////////////////////////////////////////////////
void CTrackViewTrack::CopyKeysToClipboard(XmlNodeRef& xmlNode, const bool bOnlySelectedKeys, const bool bOnlyFromSelectedTracks)
{
if (bOnlyFromSelectedTracks && !IsSelected())
{
return;
}
if (GetKeyCount() == 0)
{
return;
}
if (bOnlySelectedKeys)
{
CTrackViewKeyBundle keyBundle = GetSelectedKeys();
if (keyBundle.GetKeyCount() == 0)
{
return;
}
}
XmlNodeRef childNode = xmlNode->newChild("Track");
childNode->setAttr("name", GetName());
GetParameterType().SaveToXml(childNode);
childNode->setAttr("valueType", static_cast<int>(GetValueType()));
m_pAnimTrack->SerializeSelection(childNode, false, bOnlySelectedKeys);
}
//////////////////////////////////////////////////////////////////////////
void CTrackViewTrack::PasteKeys(XmlNodeRef xmlNode, const float timeOffset)
{
CTrackViewSequence* sequence = GetSequence();
AZ_Assert(sequence, "Expected sequence not to be null.");
AzToolsFramework::ScopedUndoBatch undoBatch("Paste Keys");
m_pAnimTrack->SerializeSelection(xmlNode, true, true, timeOffset);
undoBatch.MarkEntityDirty(sequence->GetSequenceComponentEntityId());
}
+237
View File
@@ -0,0 +1,237 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef CRYINCLUDE_EDITOR_TRACKVIEW_TRACKVIEWTRACK_H
#define CRYINCLUDE_EDITOR_TRACKVIEW_TRACKVIEWTRACK_H
#pragma once
#include "IMovieSystem.h"
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
#include "TrackViewNode.h"
class CTrackViewAnimNode;
enum class AnimValueType;
// Represents a bundle of tracks
class CTrackViewTrackBundle
{
public:
CTrackViewTrackBundle()
: m_bAllOfSameType(true)
, m_bHasRotationTrack(false) {}
unsigned int GetCount() const { return m_tracks.size(); }
CTrackViewTrack* GetTrack(const unsigned int index) { return m_tracks[index]; }
const CTrackViewTrack* GetTrack(const unsigned int index) const { return m_tracks[index]; }
void AppendTrack(CTrackViewTrack* pTrack);
void AppendTrackBundle(const CTrackViewTrackBundle& bundle);
bool RemoveTrack(CTrackViewTrack* pTrackToRemove);
bool IsOneTrack() const;
bool AreAllOfSameType() const { return m_bAllOfSameType; }
bool HasRotationTrack() const { return m_bHasRotationTrack; }
private:
bool m_bAllOfSameType;
bool m_bHasRotationTrack;
std::vector<CTrackViewTrack*> m_tracks;
};
// Track Memento for Undo/Redo
class CTrackViewTrackMemento
{
private:
friend class CTrackViewTrack;
XmlNodeRef m_serializedTrackState;
};
////////////////////////////////////////////////////////////////////////////
//
// This class represents a IAnimTrack in TrackView and contains
// the editor side code for changing it
//
// It does *not* have ownership of the IAnimTrack, therefore deleting it
// will not destroy the CryMovie track
//
////////////////////////////////////////////////////////////////////////////
class CTrackViewTrack
: public CTrackViewNode
, public ITrackViewKeyBundle
, public AzToolsFramework::EditorEntityContextNotificationBus::Handler
{
friend class CTrackViewKeyHandle;
friend class CTrackViewKeyConstHandle;
friend class CTrackViewKeyBundle;
public:
CTrackViewTrack(IAnimTrack* pTrack, CTrackViewAnimNode* pTrackAnimNode, CTrackViewNode* pParentNode,
bool bIsSubTrack = false, unsigned int subTrackIndex = 0);
~CTrackViewTrack();
CTrackViewAnimNode* GetAnimNode() const;
// Name getter
virtual const char* GetName() const;
// CTrackViewNode
virtual ETrackViewNodeType GetNodeType() const override { return eTVNT_Track; }
// Check for compound/sub track
bool IsCompoundTrack() const { return m_bIsCompoundTrack; }
// Sub track index
bool IsSubTrack() const { return m_bIsSubTrack; }
unsigned int GetSubTrackIndex() const { return m_subTrackIndex; }
// Snap time value to prev/next key in track
virtual bool SnapTimeToPrevKey(float& time) const override;
virtual bool SnapTimeToNextKey(float& time) const override;
// Expanded state interface
void SetExpanded(bool expanded) override;
bool GetExpanded() const override;
// Key getters
virtual unsigned int GetKeyCount() const override { return m_pAnimTrack->GetNumKeys(); }
virtual CTrackViewKeyHandle GetKey(unsigned int index) override;
virtual CTrackViewKeyConstHandle GetKey(unsigned int index) const;
virtual CTrackViewKeyHandle GetKeyByTime(const float time);
virtual CTrackViewKeyHandle GetNearestKeyByTime(const float time);
virtual CTrackViewKeyBundle GetSelectedKeys() override;
virtual CTrackViewKeyBundle GetAllKeys() override;
virtual CTrackViewKeyBundle GetKeysInTimeRange(const float t0, const float t1) override;
// Key modifications
virtual CTrackViewKeyHandle CreateKey(const float time);
virtual void SlideKeys(const float time0, const float timeOffset);
void OffsetKeyPosition(const Vec3& offset);
void UpdateKeyDataAfterParentChanged(const AZ::Transform& oldParentWorldTM, const AZ::Transform& newParentWorldTM);
// Value getters
template <class Type>
void GetValue(const float time, Type& value, bool applyMultiplier) const
{
assert (m_pAnimTrack.get());
return m_pAnimTrack->GetValue(time, value, applyMultiplier);
}
template <class Type>
void GetValue(const float time, Type& value) const
{
assert(m_pAnimTrack.get());
return m_pAnimTrack->GetValue(time, value);
}
void GetKeyValueRange(float& min, float& max) const;
// Type getters
const CAnimParamType& GetParameterType() const { return m_pAnimTrack->GetParameterType(); }
AnimValueType GetValueType() const { return m_pAnimTrack->GetValueType(); }
EAnimCurveType GetCurveType() const { return m_pAnimTrack->GetCurveType(); }
// Mask
bool IsMasked(uint32 mask) const { return m_pAnimTrack->IsMasked(mask); }
// Flag getter
IAnimTrack::EAnimTrackFlags GetFlags() const;
// Spline getter
ISplineInterpolator* GetSpline() const { return m_pAnimTrack->GetSpline(); }
// Color
ColorB GetCustomColor() const;
void SetCustomColor(ColorB color);
bool HasCustomColor() const;
void ClearCustomColor();
// Memento
virtual CTrackViewTrackMemento GetMemento() const;
virtual void RestoreFromMemento(const CTrackViewTrackMemento& memento);
// Disabled state
virtual void SetDisabled(bool bDisabled) override;
virtual bool IsDisabled() const override;
// Muted state
void SetMuted(bool bMuted);
bool IsMuted() const;
// Returns if the contained AnimTrack responds to muting
bool UsesMute() const { return m_pAnimTrack.get() ? m_pAnimTrack->UsesMute() : false; }
// Key selection
virtual void SelectKeys(const bool bSelected) override;
// Paste from XML representation with time offset
void PasteKeys(XmlNodeRef xmlNode, const float timeOffset);
// Key types
virtual bool AreAllKeysOfSameType() const override { return true; }
// Animation layer index
void SetAnimationLayerIndex(const int index);
int GetAnimationLayerIndex() const;
//////////////////////////////////////////////////////////////////////////
// AzToolsFramework::EditorEntityContextNotificationBus implementation
void OnStartPlayInEditor() override;
void OnStopPlayInEditor() override;
//~AzToolsFramework::EditorEntityContextNotificationBus implementation
IAnimTrack* GetAnimTrack() const { return m_pAnimTrack.get(); }
unsigned int GetId() const
{
return m_pAnimTrack->GetId();
}
void SetId(unsigned int id)
{
m_pAnimTrack->SetId(id);
}
private:
CTrackViewKeyHandle GetPrevKey(const float time);
CTrackViewKeyHandle GetNextKey(const float time);
// Those are called from CTrackViewKeyHandle
void SetKey(unsigned int keyIndex, IKey* pKey);
void GetKey(unsigned int keyIndex, IKey* pKey) const;
void SelectKey(unsigned int keyIndex, bool bSelect);
bool IsKeySelected(unsigned int keyIndex) const;
void SetSortMarkerKey(unsigned int keyIndex, bool enabled);
bool IsSortMarkerKey(unsigned int keyIndex) const;
void SetKeyTime(const int index, const float time, bool notifyListeners = true);
float GetKeyTime(const int index) const;
void RemoveKey(const int index);
int CloneKey(const int index);
CTrackViewKeyBundle GetKeys(bool bOnlySelected, float t0, float t1);
CTrackViewKeyHandle GetSubTrackKeyHandle(unsigned int index) const;
// Copy selected keys to XML representation for clipboard
virtual void CopyKeysToClipboard(XmlNodeRef& xmlNode, const bool bOnlySelectedKeys, const bool bOnlyFromSelectedTracks) override;
bool m_bIsCompoundTrack;
bool m_bIsSubTrack;
unsigned int m_subTrackIndex;
AZStd::intrusive_ptr<IAnimTrack> m_pAnimTrack;
CTrackViewAnimNode* m_pTrackAnimNode;
// used to stash AZ Entity ID's stored in track keys when entering/exiting AI/Physic or Ctrl-G game modes
AZStd::unordered_map<CAnimParamType, AZStd::vector<AZ::EntityId>> m_paramTypeToStashedEntityIdMap;
};
#endif // CRYINCLUDE_EDITOR_TRACKVIEW_TRACKVIEWTRACK_H
@@ -0,0 +1,97 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>CTrackViewTrackPropsDlg</class>
<widget class="QWidget" name="CTrackViewTrackPropsDlg">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>112</width>
<height>35</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QGridLayout" name="gridLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<property name="verticalSpacing">
<number>2</number>
</property>
<item row="1" column="1">
<widget class="CTrackViewDoubleSpinBox" name="TIME">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="maximum">
<double>0.000000000000000</double>
</property>
<property name="singleStep">
<double>0.100000000000000</double>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="label">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Time:</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLineEdit" name="PREVNEXT">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>0</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>CTrackViewDoubleSpinBox</class>
<extends>QDoubleSpinBox</extends>
<header location="global">TrackView/TrackViewDoubleSpinBox.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
+138
View File
@@ -0,0 +1,138 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "EditorDefs.h"
#include "TrackViewUndo.h"
// CryCommon
#include <CryCommon/Maestro/Types/AnimNodeType.h>
// Editor
#include "TrackView/TrackViewAnimNode.h"
#include "TrackView/TrackViewSequence.h"
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
CUndoComponentEntityTrackObject::CUndoComponentEntityTrackObject(CTrackViewTrack* track)
{
AZ_Assert(track, "Expected a valid track");
if (track)
{
m_trackName = track->GetName();
AZ_Assert(!m_trackName.empty(), "Expected a valid track name");
CTrackViewAnimNode* animNode = track->GetAnimNode();
AZ_Assert(animNode, "Expected a valid anim node");
if (animNode)
{
m_trackComponentId = animNode->GetComponentId();
AZ_Assert(m_trackComponentId != AZ::InvalidComponentId, "Expected a valid track component id");
CTrackViewSequence* sequence = track->GetSequence();
AZ_Assert(sequence, "Expected to find the sequence");
if (sequence)
{
m_sequenceId = sequence->GetSequenceComponentEntityId();
AZ_Assert(m_sequenceId.IsValid(), "Expected a valid sequence id");
AnimNodeType nodeType = animNode->GetType();
AZ_Assert(nodeType == AnimNodeType::Component, "Expected a this node to be a AnimNodeType::Component type");
if (nodeType == AnimNodeType::Component)
{
CTrackViewAnimNode* parentAnimNode = static_cast<CTrackViewAnimNode*>(animNode->GetParentNode());
AZ_Assert(parentAnimNode, "Expected a valid parent node");
if (parentAnimNode)
{
m_entityId = parentAnimNode->GetAzEntityId();
AZ_Assert(m_entityId.IsValid(), "Expected a valid sequence id");
// Store undo info.
m_undo = track->GetMemento();
}
}
}
}
}
}
//////////////////////////////////////////////////////////////////////////
CTrackViewTrack* CUndoComponentEntityTrackObject::FindTrack(CTrackViewSequence* sequence)
{
AZ_Assert(sequence, "Expected to find the sequence");
CTrackViewTrack* track = nullptr;
CTrackViewTrackBundle allTracks = sequence->GetAllTracks();
for (int trackIndex = 0; trackIndex < allTracks.GetCount(); trackIndex++)
{
CTrackViewTrack* curTrack = allTracks.GetTrack(trackIndex);
if (curTrack->GetAnimNode() && curTrack->GetAnimNode()->GetComponentId() == m_trackComponentId)
{
if (0 == azstricmp(curTrack->GetName(), m_trackName.c_str()))
{
CTrackViewAnimNode* parentAnimNode = static_cast<CTrackViewAnimNode*>(curTrack->GetAnimNode()->GetParentNode());
if (parentAnimNode && parentAnimNode->GetAzEntityId() == m_entityId)
{
track = curTrack;
break;
}
}
}
}
return track;
}
//////////////////////////////////////////////////////////////////////////
void CUndoComponentEntityTrackObject::Undo(bool bUndo)
{
CTrackViewSequence* sequence = CTrackViewSequence::LookUpSequenceByEntityId(m_sequenceId);
AZ_Assert(sequence, "Expected to find the sequence");
if (sequence)
{
CTrackViewTrack* track = FindTrack(sequence);
AZ_Assert(track, "Expected to find track");
{
CTrackViewSequenceNoNotificationContext context(sequence);
if (bUndo)
{
m_redo = track->GetMemento();
}
// Undo track state.
track->RestoreFromMemento(m_undo);
}
if (bUndo)
{
sequence->OnKeysChanged();
}
else
{
sequence->ForceAnimation();
}
}
}
//////////////////////////////////////////////////////////////////////////
void CUndoComponentEntityTrackObject::Redo()
{
CTrackViewSequence* sequence = CTrackViewSequence::LookUpSequenceByEntityId(m_sequenceId);
AZ_Assert(sequence, "Expected to find the sequence");
if (sequence)
{
CTrackViewTrack* track = FindTrack(sequence);
AZ_Assert(track, "Expected to find track");
// Redo track state.
track->RestoreFromMemento(m_redo);
sequence->OnKeysChanged();
}
}
+53
View File
@@ -0,0 +1,53 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#ifndef CRYINCLUDE_EDITOR_TRACKVIEW_TRACKVIEWUNDO_H
#define CRYINCLUDE_EDITOR_TRACKVIEW_TRACKVIEWUNDO_H
#pragma once
#include "TrackViewTrack.h"
#include "Undo/IUndoObject.h"
class CTrackViewSequence;
/** Undo object stored when track is modified for component entity.
* Stores ids, not raw pointers.
*/
class CUndoComponentEntityTrackObject
: public IUndoObject
{
public:
CUndoComponentEntityTrackObject(CTrackViewTrack* track);
protected:
virtual int GetSize() override { return sizeof(*this); }
virtual QString GetDescription() override { return "Undo Component Entity Track Modify"; };
virtual void Undo(bool bUndo) override;
virtual void Redo() override;
private:
// Helper function to get a pointer to the track based.
CTrackViewTrack* FindTrack(CTrackViewSequence* sequence);
// Internal state are id's used to uniquely identify
// a track. This does not store pointers to tracks because
// those can change when an AZ::Undo event happens and the entity
// is reloaded.
AZ::EntityId m_sequenceId;
AZ::EntityId m_entityId;
AZStd::string m_trackName;
AZ::ComponentId m_trackComponentId;
CTrackViewTrackMemento m_undo;
CTrackViewTrackMemento m_redo;
};
#endif // CRYINCLUDE_EDITOR_TRACKVIEW_TRACKVIEWUNDO_H
+3
View File
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:29d6b3e5d2288365b6038fc8ddeadedb4998924eceb9feb9f124eb46c31da4ef
size 143
+3
View File
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:72115aee4f8baa0ee796170de94461624fba38675a3e9138d6ca0efd3fcd733b
size 164
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:a064e400b30d4de5d96ce15d54c3a55a1ec655a025479ec34966903d87c1e945
size 446
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:3a9123cfc2ec39e086593e0e4c5edfefeb81e55f7742a1fe55107b7d8b8116b1
size 364
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:98d6abe0694969813e40777c30ed08b37ee8b1e21d3b101c6ae1cc73f0f7f203
size 144
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:ba30d39cc19a5f154334ed58b0c620adc0d2b8699817ab47140e67ca817fb946
size 143
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:3ccfd91d032179c9275309a63d0418bfacc57764153947affc25b502bde79b93
size 147
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:3394b7e757f82234362c12f2dc5b032b1030c448768a453642b324d05ac21504
size 151
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:a80f1e8aaf99a56e71af5acee471c44e39862710bbd42bbdc71ad2ed36ca19c9
size 147
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:2c1141caf31b2d01f193ff5dcf9b1edab8ccbb0fc824c6ebc4594f0ea4032284
size 138
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:9e58c21845c00ecc27dcf06b33b08e4975e06ff123161730324fb5f99de369f8
size 158
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b52d11739a7e9cca6788151806e7824e9b6c847dad3a201962f3f3daadc67474
size 159
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:7bc6d1fe7f8184f230a1e438c0bcfec266f2ac77f7b3364d654006ff01ac031c
size 152
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:56d75cb3865d0eefa9d8c0794f2bfaaa73cd8a366310d8032eff6de63932901b
size 130
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:3f127ce50a93699fac340cc1cf162f35b4d93dc9ec74e3653ade5f0aef2c0e77
size 142
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:96e1d00b1654e25446db63fb9375a908dd7415644bed13c626b45a4ec1e5d9e1
size 172
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:5e13418febcdd2e86b10e2bea62fe026b4009b649e0d1013dc84c6c4a4f401ed
size 145
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:49a61fefdb2a0a7d218a7064a1afa1584511ff3b2a3787a176a2d4783b69eb0d
size 125
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:03a118a63529650f7d0734b385d15431e1c70fe51b324601c6765981521b5479
size 140
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:3537913eb762f1c4bd8bcd87b5b195e76074dd0e3efd9a41af27717bdcce9b3d
size 138
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f121ac550ea7128d744ecb142676302875b6ae7ecf76ca8a933bf332cbdf28e9
size 189
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:49ac756007e7f372f1f8b9c3e5254f5bb88a5d66c2317e70423c9c91703fc530
size 206
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:466b762cadb338b348be0fc7e5eab2473c3305e964f16c0bbf4f7d6691f3b987
size 226
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f8f29f4af9cc488160dd9c179995b175696b2cfb77a14d4c3e39b8b0b1d8ff6b
size 227
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:df0c0ef69e861fc95391b63231ab378e32bd8634f1646d7c3442d8d6ded498b2
size 226
+3
View File
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:51121c832d78c50a390e5fdc676d8a100f9271415365a174ab6f30d92d520ab5
size 394

Some files were not shown because too many files have changed in this diff Show More