// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "CoreTypes.h" #include "Containers/SparseArray.h" #include "Containers/SortedMap.h" #include "Evaluation/MovieSceneEvaluationField.h" #include "MovieSceneTracksComponentTypes.h" #include "UObject/GCObject.h" #include "UObject/ObjectKey.h" #include "EntitySystem/MovieSceneEntitySystemTypes.h" #include "EntitySystem/MovieSceneEntitySystemLinkerExtension.h" #include "EntitySystem/Interrogation/MovieSceneInterrogationExtension.h" #include "Evaluation/MovieSceneSequenceHierarchy.h" struct FGuid; struct FMovieSceneBinding; class UMovieSceneTrack; class IMovieScenePlayer; class UMovieSceneEntitySystemLinker; namespace UE { namespace MovieScene { struct FInitialValueCache; struct FSystemInterrogatorEntityTracker; enum class EEntitySystemCategory : uint32; struct FInterrogationParams { FFrameTime Time; FInterrogationParams(FFrameTime InTime) : Time(InTime) {} FInterrogationParams(FFrameNumber InTime) : Time(InTime) {} }; /** * A class specialized for interrogating Sequencer entity data without applying any state to objects. * Currently only tracks within the same time-base are supported. * Will not link systems that are in the custom "ExcludedFromInterrogation" category. * Multiple different outputs can be interrogated simultaneously by Importing tracks onto separate channels * allocated through AllocateChannel. * * Systems may implement their own interrogation logic that can be run after updates to allow third-party interrogation * behavior for specific channels or time. * * Example usage: * Interrogator.ImportTrack(MyTrack, FInterrogationChannel::Default()); * * for (int32 FrameNumber = 0; FrameNumber < 100; ++FrameNumber) * Interrogator.InterrogateTime(FrameNumber); * * Interrogator.Update(); * * UMyTrackSystem* MySystem = Interrogator.GetLinker()->FindSystem(); * if (MySystem) * { * TArray OutData; * MySystem->Interrogate(FInterogationKey::Default(), OutData); * } */ class FInterrogationChannels { public: MOVIESCENETRACKS_API FInterrogationChannels(); MOVIESCENETRACKS_API ~FInterrogationChannels(); MOVIESCENETRACKS_API void Reset(); const FSparseInterrogationChannelInfo& GetSparseChannelInfo() const { return SparseChannelInfo; } /** * Allocate a new interrogation channel that can be used to uniquely identify groups of tracks that animate the same property or output. * * @param ParentChannel The channel that should be considered this channel's parent, or FInterrogationChannel::Invalid if there is none * @return A new interrogation channel */ MOVIESCENETRACKS_API FInterrogationChannel AllocateChannel(FInterrogationChannel ParentChannel, const FMovieScenePropertyBinding& PropertyBinding); /** * Allocate a new interrogation channel that relates to a specific object */ MOVIESCENETRACKS_API FInterrogationChannel AllocateChannel(UObject* Object, const FMovieScenePropertyBinding& PropertyBinding); /** * Allocate a new interrogation channel that relates to a specific object */ MOVIESCENETRACKS_API FInterrogationChannel AllocateChannel(UObject* Object, FInterrogationChannel ParentChannel, const FMovieScenePropertyBinding& PropertyBinding); /** * Allocate a new channel for a set of transform tracks that isn't bound to any particular object (but can still exist within a hierarchy) * * @param ParentChannel The channel that should be considered this channel's parent, or FInterrogationChannel::Invalid if there is none * @param CurrentValue A value to use if this channel has now animated data after the interrogation * @return A new interrogation channel that can be passed to ImportUnboundTrack */ MOVIESCENETRACKS_API FInterrogationChannel AllocateUnboundChannel(FInterrogationChannel ParentChannel, const FTransform& CurrentValueLocalSpace); /** * Import the entire transform hierarchy for the specified component, including all attached parents and tracks relating to them. * * @param SceneComponent The scene component to import. A new channel will be allocated for this if one does not already exist. * @param InPlayer The player interface to use for looking up object binding IDs pertaining to scene components or actors * @param SequenceID The current sequence ID for the interrogation * @return The channel that was either pre-existing or allocated for SceneComponent */ MOVIESCENETRACKS_API FInterrogationChannel ImportTransformHierarchy(USceneComponent* SceneComponent); /** * Find the existing interrogation channel for the specified object * * @param Object The object to find a channel for * @return The (potentially invalid) interrogation channel that the specified object is on */ MOVIESCENETRACKS_API FInterrogationChannel FindChannel(UObject* Object); /** * Called to activate the specified channel. Inactive channels will only ever use their object's current value. * * @param InChannel The channel to activate */ MOVIESCENETRACKS_API void ActivateChannel(FInterrogationChannel InChannel); /** * Called to deactivate the specified channel. Inactive channels will only ever use their object's current value. * * @param InChannel The channel to deactivate */ MOVIESCENETRACKS_API void DeactivateChannel(FInterrogationChannel InChannel); /** * Add a new time to interrogate this linker at, in the time-base of the imported tracks. * * @param Params The desired time to interrogate at * @return A unique index identifier for the specified time, or INDEX_NONE if the maximum number have been reached */ MOVIESCENETRACKS_API int32 AddInterrogation(const FInterrogationParams& Params); public: /** * Retrieve the number of channels allocated */ int32 GetNumChannels() const { return ActiveChannelBits.Num(); } /** * Retrieve the current interrogations */ TArrayView GetInterrogations() const { return Interrogations; } public: /** * Query local space transforms * * @param SceneComponent The scene component to query * @param OutTransforms Array to output transforms into, one per Interrogation */ MOVIESCENETRACKS_API void QueryLocalSpaceTransforms(UMovieSceneEntitySystemLinker* Linker, USceneComponent* SceneComponent, TArray& OutTransforms) const; /** * Query local space transforms * * @param InChannel The channel to query * @param OutTransforms Array to output transforms into, one per Interrogation */ MOVIESCENETRACKS_API void QueryLocalSpaceTransforms(UMovieSceneEntitySystemLinker* Linker, FInterrogationChannel InChannel, TArray& OutTransforms) const; /** * Query all local space transforms, even including channels that do not have any variable track data * * @param OutTransformsByChannel Sparse array to receive transforms allocated by their Channel index */ MOVIESCENETRACKS_API void QueryLocalSpaceTransforms(UMovieSceneEntitySystemLinker* Linker, TSparseArray>& OutTransformsByChannel) const; /** * Query a specific set of channels for their local space transforms as defined by set bits within ChannelsToQuery * * @param ChannelsToQuery Bit array containing set bits for each channel to query * @param OutTransformsByChannel Sparse array to receive transforms allocated by their Channel index */ MOVIESCENETRACKS_API void QueryLocalSpaceTransforms(UMovieSceneEntitySystemLinker* Linker, const TBitArray<>& ChannelsToQuery, TSparseArray>& OutTransformsByChannel) const; public: /** * Query world space transforms for a component * * @param SceneComponent The scene component to query * @param OutTransforms Array to output transforms into, one per Interrogation */ MOVIESCENETRACKS_API void QueryWorldSpaceTransforms(UMovieSceneEntitySystemLinker* Linker, USceneComponent* SceneComponent, TArray& OutTransforms) const; /** * Query world space transforms for a channel * * @param InChannel The channel to query * @param OutTransforms Array to output transforms into, one per Interrogation */ MOVIESCENETRACKS_API void QueryWorldSpaceTransforms(UMovieSceneEntitySystemLinker* Linker, FInterrogationChannel InChannel, TArray& OutTransforms) const; /** * Query all world space transforms, even including channels that do not have any variable track data * * @param OutTransformsByChannel Sparse array to receive transforms allocated by their Channel index */ MOVIESCENETRACKS_API void QueryWorldSpaceTransforms(UMovieSceneEntitySystemLinker* Linker, TSparseArray>& OutTransformsByChannel) const; /** * Query a specific set of channels for their world space transforms as defined by set bits within ChannelsToQuery * * @param ChannelsToQuery Bit array containing set bits for each channel to query * @param OutTransformsByChannel Sparse array to receive transforms allocated by their Channel index */ MOVIESCENETRACKS_API void QueryWorldSpaceTransforms(UMovieSceneEntitySystemLinker* Linker, const TBitArray<>& ChannelsToQuery, TSparseArray>& OutTransformsByChannel) const; MOVIESCENETRACKS_API void QueryTransformOrigins(UMovieSceneEntitySystemLinker* Linker, const FMovieSceneSequenceHierarchy* Hierarchy, TArray& OutTransformOrigins, TArray& SubsequenceHierarchy, const UObject* InstanceData) const; private: /** Query implementation functions */ template void QueryWorldSpaceTransforms(UMovieSceneEntitySystemLinker* Linker, const TBitArray<>& ChannelsToQuery, GetOutputForChannelType&& OnGetOutputForChannel) const; template void QueryLocalSpaceTransforms(UMovieSceneEntitySystemLinker* Linker, const TBitArray<>& ChannelsToQuery, GetOutputForChannelType&& OnGetOutputForChannel) const; protected: /** Map from an object to its interrogation channel */ TMap ObjectToChannel; /** Array of information pertaining to a given channel */ FSparseInterrogationChannelInfo SparseChannelInfo; /** BitArray containing set bits for any channel that has data associated with it. The number of bits (0 or 1) in this array defines how many channels are allocated */ TBitArray<> ActiveChannelBits; /** An array of interrogation times */ TArray Interrogations; }; class FSystemInterrogator : FGCObject , IInterrogationExtension { public: MOVIESCENETRACKS_API FSystemInterrogator(); MOVIESCENETRACKS_API ~FSystemInterrogator(); /** * Gets the custom system category for interrogation-specific systems */ static MOVIESCENETRACKS_API EEntitySystemCategory GetInterrogationCategory(); /** * Gets the custom system category for systems who should be excluded from interrogation linkers */ static MOVIESCENETRACKS_API EEntitySystemCategory GetExcludedFromInterrogationCategory(); /** * Allocate a new interrogation channel that relates to a specific object */ FInterrogationChannel AllocateChannel(UObject* Object, const FMovieScenePropertyBinding& PropertyBinding) { return Channels.AllocateChannel(Object, PropertyBinding); } /** * Import a track into this linker. This will add the track to the linker's evaluation field and * cause entities to be created for it at each interrogation channel (if it is relevant at such times) * Must be called before InterrogateTime() and Update(). * * @param Track The track to import * @param InChannel The channel to import this track onto. FInterrogationChannel::Default() can be used if this interrogator is only being used for a single output. * @param SequenceID The SequenceID of the subsequence if importing one. */ MOVIESCENETRACKS_API void ImportTrack(UMovieSceneTrack* Track, FInterrogationChannel InChannel, FMovieSceneSequenceID SequenceID = MovieSceneSequenceID::Invalid); /** * Import a track into this linker. This will add the track to the linker's evaluation field and * cause entities to be created for it at each interrogation channel (if it is relevant at such times) * Must be called before InterrogateTime() and Update(). * * @param Track The track to import * @param ObjectBindingID The binding ID for the object binding that this track resides within * @param InChannel The channel to import this track onto. FInterrogationChannel::Default() can be used if this interrogator is only being used for a single output. * @param SequenceID The SequenceID of the subsequence if importing one. */ MOVIESCENETRACKS_API void ImportTrack(UMovieSceneTrack* Track, const FGuid& ObjectBindingID, FInterrogationChannel InChannel, FMovieSceneSequenceID SequenceID = MovieSceneSequenceID::Invalid); /** * Import the entire transform hierarchy for the specified component, including all attached parents and tracks relating to them. * * @param SceneComponent The scene component to import. A new channel will be allocated for this if one does not already exist. * @param InPlayer The player interface to use for looking up object binding IDs pertaining to scene components or actors * @param SequenceID The current sequence ID for the interrogation * @return The channel that was either pre-existing or allocated for SceneComponent */ MOVIESCENETRACKS_API FInterrogationChannel ImportTransformHierarchy(USceneComponent* SceneComponent, IMovieScenePlayer* InPlayer, FMovieSceneSequenceID SequenceID); /** * Import any transform tracks that relate to the specified scene component, or it's AActor if it is the root * * @param SceneComponent The scene component to import. A new channel will be allocated for this if one does not already exist. * @param InPlayer The player interface to use for looking up object binding IDs pertaining to scene components or actors * @param SequenceID The current sequence ID for the interrogation * @return The channel that was either pre-existing or allocated for SceneComponent */ MOVIESCENETRACKS_API FInterrogationChannel ImportLocalTransforms(USceneComponent* SceneComponent, IMovieScenePlayer* InPlayer, FMovieSceneSequenceID SequenceID); /** * Import multiple tracks into this linker. See ImporTrack above. */ void ImportTracks(TArrayView Tracks, FInterrogationChannel InChannel) { for (UMovieSceneTrack* Track : Tracks) { ImportTrack(Track, InChannel); } } /** * Import multiple tracks into this linker. See ImporTrack above. */ void ImportTracks(TArrayView Tracks, const FGuid& ObjectBindingID, FInterrogationChannel InChannel) { for (UMovieSceneTrack* Track : Tracks) { ImportTrack(Track, ObjectBindingID, InChannel); } } /** * Add a new time to interrogate this linker at, in the time-base of the imported tracks. * * @param Params The desired time to interrogate at * @return A unique index identifier for the specified time, or INDEX_NONE if the maximum number have been reached */ MOVIESCENETRACKS_API int32 AddInterrogation(const FInterrogationParams& Params); /** * Flush this interrogator by running all the systems relevant to the current data and populating the interrogation outputs. */ MOVIESCENETRACKS_API void Update(); /** * Reset this linker back to its original state */ MOVIESCENETRACKS_API void Reset(); public: /** * Indicate that consumers of this class require a reverse-lookup table for imported entities to be maintained such that the various FindEntity functions can be called. * (Not enabled by default due to performance cost with high interrogation counts) */ MOVIESCENETRACKS_API void TrackImportedEntities(bool bInTrackImportedEntities); /** * Find an entity given the entity's owner. * @note: Must call TrackImportedEntities(true) prior to calling ImportTrack for this function to return the correct entity */ MOVIESCENETRACKS_API FMovieSceneEntityID FindEntityFromOwner(FInterrogationKey InterrogationKey, UObject* Owner, uint32 EntityID) const; /** * Access the underlying linker used for interrogation. */ UMovieSceneEntitySystemLinker* GetLinker() const { return Linker; } /** * Retrieve the number of channels allocated */ int32 GetNumChannels() const { return Channels.GetNumChannels(); } /** * Retrieve the current interrogations */ TArrayView GetInterrogations() const { return Channels.GetInterrogations(); } private: MOVIESCENETRACKS_API void FindPropertyOutputEntityIDs(const FPropertyDefinition& PropertyDefinition, FInterrogationChannel Channel, TArray& OutEntityIDs) const; public: /** * Query local space transforms * * @param SceneComponent The scene component to query * @param OutTransforms Array to output transforms into, one per Interrogation */ void QueryLocalSpaceTransforms(USceneComponent* SceneComponent, TArray& OutTransforms) const { Channels.QueryLocalSpaceTransforms(Linker, SceneComponent, OutTransforms); } /** * Query local space transforms * * @param InChannel The channel to query * @param OutTransforms Array to output transforms into, one per Interrogation */ void QueryLocalSpaceTransforms(FInterrogationChannel InChannel, TArray& OutTransforms) const { Channels.QueryLocalSpaceTransforms(Linker, InChannel, OutTransforms); } /** * Query all local space transforms, even including channels that do not have any variable track data * * @param OutTransformsByChannel Sparse array to receive transforms allocated by their Channel index */ void QueryLocalSpaceTransforms(TSparseArray>& OutTransformsByChannel) const { Channels.QueryLocalSpaceTransforms(Linker, OutTransformsByChannel); } /** * Query a specific set of channels for their local space transforms as defined by set bits within ChannelsToQuery * * @param ChannelsToQuery Bit array containing set bits for each channel to query * @param OutTransformsByChannel Sparse array to receive transforms allocated by their Channel index */ void QueryLocalSpaceTransforms(const TBitArray<>& ChannelsToQuery, TSparseArray>& OutTransformsByChannel) const { Channels.QueryLocalSpaceTransforms(Linker, ChannelsToQuery, OutTransformsByChannel); } public: /** * Query world space transforms for a component * * @param SceneComponent The scene component to query * @param OutTransforms Array to output transforms into, one per Interrogation */ void QueryWorldSpaceTransforms(USceneComponent* SceneComponent, TArray& OutTransforms) const { Channels.QueryWorldSpaceTransforms(Linker, SceneComponent, OutTransforms); } /** * Query world space transforms for a channel * * @param InChannel The channel to query * @param OutTransforms Array to output transforms into, one per Interrogation */ void QueryWorldSpaceTransforms(FInterrogationChannel InChannel, TArray& OutTransforms) const { Channels.QueryWorldSpaceTransforms(Linker, InChannel, OutTransforms); } /** * Query all world space transforms, even including channels that do not have any variable track data * * @param OutTransformsByChannel Sparse array to receive transforms allocated by their Channel index */ void QueryWorldSpaceTransforms(TSparseArray>& OutTransformsByChannel) const { Channels.QueryWorldSpaceTransforms(Linker, OutTransformsByChannel); } /** * Query a specific set of channels for their world space transforms as defined by set bits within ChannelsToQuery * * @param ChannelsToQuery Bit array containing set bits for each channel to query * @param OutTransformsByChannel Sparse array to receive transforms allocated by their Channel index */ void QueryWorldSpaceTransforms(const TBitArray<>& ChannelsToQuery, TSparseArray>& OutTransformsByChannel) const { Channels.QueryWorldSpaceTransforms(Linker, ChannelsToQuery, OutTransformsByChannel); } /** * Query the computed value of an animated property. * * See the other QueryPropertyValues method description. * * @param InPropertyComponent The type of property being animated on the default channel. * @param OutValues The animated values, one for each interrogation time. */ template void QueryPropertyValues(const TPropertyComponents& InPropertyComponents, TArray& OutValues) const { return QueryPropertyValues(InPropertyComponents, FInterrogationChannel::Default(), OutValues); } /** * Query the computed value of an animated property. * * All the tracks imported on the given channel are expected to be animating a property of the type described * by the InPropertyComponents parameter. * * @param InPropertyComponent The type of property being animated on the given channel. * @param InChannel The channel on which the property is being animated. * @param OutValues The animated values, one for each interrogation time. */ template void QueryPropertyValues(const TPropertyComponents& InPropertyComponents, FInterrogationChannel InChannel, TArray& OutValues) const { FBuiltInComponentTypes* Components = FBuiltInComponentTypes::Get(); const FPropertyDefinition& PropertyDefinition = Components->PropertyRegistry.GetDefinition(InPropertyComponents.CompositeID); TArrayView PropertyComposites = Components->PropertyRegistry.GetComposites(PropertyDefinition); TArray ValueEntityIDs; FindPropertyOutputEntityIDs(PropertyDefinition, InChannel, ValueEntityIDs); const int32 NumPropertyValues = Channels.GetInterrogations().Num(); check(ValueEntityIDs.Num() == NumPropertyValues); OutValues.SetNum(NumPropertyValues); PropertyDefinition.Handler->RebuildOperational(PropertyDefinition, PropertyComposites, ValueEntityIDs, Linker, OutValues); } /** * Query the transform origin of a subsection based on the imported hiearchy * * @param OutTransformOrigins The interrogated transforms for the currently focused sequence. * @param SubsequenceHierarchy The currently focused sequence hierarchy * @param InstanceData The object that contains the origin overrides for the world context object of the root sequence. * */ void QueryTransformOrigins(TArray& OutTransformOrigins, TArray& SubsequenceHierarchy, const UObject* InstanceData) const { Channels.QueryTransformOrigins(Linker, Hierarchy, OutTransformOrigins, SubsequenceHierarchy, InstanceData); } public: const FSparseInterrogationChannelInfo& GetSparseChannelInfo() const override { return Channels.GetSparseChannelInfo(); } virtual const FMovieSceneSequenceHierarchy* GetHierarchy() const override { return Hierarchy; } void SetHierarchy(FMovieSceneSequenceHierarchy* InHierarchy) { Hierarchy = InHierarchy; } private: /** * Import transform tracks from this binding */ MOVIESCENETRACKS_API void ImportTransformTracks(const FMovieSceneBinding& Binding, FInterrogationChannel Channel); /** * Import an entity for the specified query and index */ MOVIESCENETRACKS_API void InterrogateEntity(int32 InterrogationIndex, const FMovieSceneEvaluationFieldEntityQuery& Query); private: MOVIESCENETRACKS_API virtual void AddReferencedObjects(FReferenceCollector& Collector); MOVIESCENETRACKS_API virtual FString GetReferencerName() const; protected: /** Object <-> Channel tracker */ FInterrogationChannels Channels; /** Scratch buffer used for generating entities for interrogation times */ FMovieSceneEvaluationFieldEntitySet EntitiesScratch; /** Entity component field containing all the entity owners relevant at specific times */ FMovieSceneEntityComponentField EntityComponentField; /** Tracker class that is used for keeping track of imported entities */ TUniquePtr EntityTracker; /** The linker we own */ TObjectPtr Linker; /** Initial value cache */ TSharedPtr InitialValueCache; /** SubSection Hiearchy, for interrogating subsequence data e.g. subsequence transform origins */ FMovieSceneSequenceHierarchy* Hierarchy; /** Array of shared extra metadata on entities */ struct FExtraMetaData { UE::MovieScene::FInterrogationInstance InterrogationInstance; UE::MovieScene::FInterrogationChannel InterrogationChannel; FMovieSceneSequenceID InterrogationSequenceID; }; TSparseArray ExtraMetaData; }; } // namespace MovieScene } // namespace UE