You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
o3de/Code/Editor/TrackView/TrackViewSequence.h

410 lines
15 KiB
C++

/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#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 AZStd::string 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