// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. #pragma once #include "Engine/Level/Actor.h" #include "Engine/Core/Collections/Dictionary.h" #include "Engine/Content/AssetReference.h" #include "Engine/Level/Actors/PostFxVolume.h" #include "SceneAnimation.h" /// /// The scene animation playback actor. /// API_CLASS(Attributes="ActorContextMenu(\"New/Other/Scene Animation\"), ActorToolbox(\"Other\", \"Scene Animation\")") class FLAXENGINE_API SceneAnimationPlayer : public Actor, public IPostFxSettingsProvider { DECLARE_SCENE_OBJECT(SceneAnimationPlayer); /// /// Describes the scene animation updates frequency. /// API_ENUM() enum class UpdateModes { /// /// Animation will be updated every game logic update. /// EveryUpdate = 0, /// /// Animation can be updated manually by the user scripts. /// Manual = 1, }; private: enum class PlayState { Stopped, Paused, Playing, }; struct TrackInstance { ScriptingObjectReference Object; MObject* ManagedObject = nullptr; MProperty* Property = nullptr; MField* Field = nullptr; MMethod* Method = nullptr; int32 RestoreStateIndex = -1; bool Warn = true; TrackInstance() { } }; float _time = 0.0f; float _lastTime = 0.0f; PlayState _state = PlayState::Stopped; Array _tracks; Array _tracksDataStack; Array _subActors; Array _restoreData; Camera* _cameraCutCam = nullptr; bool _isUsingCameraCuts = false; Dictionary _objectsMapping; // PostFx settings to use struct { CameraArtifactsSettings CameraArtifacts; PostFxMaterialsSettings PostFxMaterials; } _postFxSettings; public: /// /// The scene animation to play. /// API_FIELD(Attributes="EditorDisplay(\"Scene Animation\"), EditorOrder(0), DefaultValue(null)") AssetReference Animation; /// /// The animation playback speed factor. Scales the timeline update delta time. Can be used to speed up or slow down the sequence. /// API_FIELD(Attributes="EditorDisplay(\"Scene Animation\"), EditorOrder(10), DefaultValue(1.0f)") float Speed = 1.0f; /// /// The animation start time. Can be used to skip part of the sequence on begin. /// API_FIELD(Attributes="EditorDisplay(\"Scene Animation\"), EditorOrder(20), DefaultValue(0.0f)") float StartTime = 0.0f; /// /// Determines whether the scene animation should take into account the global game time scale for simulation updates. /// API_FIELD(Attributes="EditorDisplay(\"Scene Animation\"), EditorOrder(30), DefaultValue(true)") bool UseTimeScale = true; /// /// Determines whether the scene animation should loop when it finishes playing. /// API_FIELD(Attributes="EditorDisplay(\"Scene Animation\"), EditorOrder(40), DefaultValue(false)") bool Loop = false; /// /// Determines whether the scene animation should auto play on game start. /// API_FIELD(Attributes="EditorDisplay(\"Scene Animation\", \"Play On Start\"), EditorOrder(50), DefaultValue(false)") bool PlayOnStart = false; /// /// Determines whether the scene animation should randomize the start time on play begin. /// API_FIELD(Attributes="EditorDisplay(\"Scene Animation\"), EditorOrder(60), DefaultValue(false)") bool RandomStartTime = false; /// /// Determines whether the scene animation should restore initial state on playback stop. State is cached when animation track starts play after being stopped (not paused). /// API_FIELD(Attributes="EditorDisplay(\"Scene Animation\", \"Restore State On Stop\"), EditorOrder(70), DefaultValue(false)") bool RestoreStateOnStop = false; /// /// The animation update mode. /// API_FIELD(Attributes="EditorDisplay(\"Scene Animation\"), EditorOrder(80), DefaultValue(UpdateModes.EveryUpdate)") UpdateModes UpdateMode = UpdateModes::EveryUpdate; /// /// Determines whether the scene animation should automatically map prefab objects from scene animation into prefab instances. Useful for reusable animations to automatically link prefab objects. /// API_FIELD(Attributes="EditorDisplay(\"Scene Animation\"), EditorOrder(100)") bool UsePrefabObjects = false; public: /// /// Gets the value that determines whether the scene animation is playing. /// API_PROPERTY() FORCE_INLINE bool IsPlaying() const { return _state == PlayState::Playing; } /// /// Gets the value that determines whether the scene animation is paused. /// API_PROPERTY() FORCE_INLINE bool IsPaused() const { return _state == PlayState::Paused; } /// /// Gets the value that determines whether the scene animation is stopped. /// API_PROPERTY() FORCE_INLINE bool IsStopped() const { return _state == PlayState::Stopped; } /// /// Gets the current animation playback time position (seconds). /// /// The animation playback time position (seconds). API_PROPERTY(Attributes="NoSerialize, HideInEditor") float GetTime() const; /// /// Sets the current animation playback time position (seconds). /// /// The value. API_PROPERTY() void SetTime(float value); /// /// Starts playing the animation. Has no effect if animation is already playing. /// API_FUNCTION() void Play(); /// /// Pauses the animation. Has no effect if animation is not playing. /// API_FUNCTION() void Pause(); /// /// Stops playing the animation. Has no effect if animation is already stopped. /// API_FUNCTION() void Stop(); /// /// Manually ticks the animation by performing the playback update with a given time delta. /// /// The update delta time (in seconds). It does not get scaled by player Speed parameter. API_FUNCTION() void Tick(float dt); /// /// Adds an object mapping. The object `from` represented by it's unique ID will be redirected to the specified `to`. Can be used to reuse the same animation for different objects. /// /// The source object from the scene animation asset to replace. /// The destination object to animate. API_FUNCTION() void MapObject(const Guid& from, const Guid& to); /// /// Adds an object mapping for the object track. The track name `from` will be redirected to the specified object `to`. Can be used to reuse the same animation for different objects. /// /// The source track name from the scene animation asset to replace. /// The destination object to animate. API_FUNCTION() void MapTrack(const StringView& from, const Guid& to); private: void Restore(SceneAnimation* anim, int32 stateIndexOffset); bool TickPropertyTrack(int32 trackIndex, int32 stateIndexOffset, SceneAnimation* anim, float time, const SceneAnimation::Track& track, TrackInstance& state, void* target); typedef Array> CallStack; void Tick(SceneAnimation* anim, float time, float dt, int32 stateIndexOffset, CallStack& callStack); void Tick(); void OnAnimationModified(); void ResetState(); public: // [Actor] bool HasContentLoaded() const override; void Serialize(SerializeStream& stream, const void* otherObj) override; void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override; #if USE_EDITOR BoundingBox GetEditorBox() const override { const Vector3 size(50); return BoundingBox(_transform.Translation - size, _transform.Translation + size); } #endif // [IPostFxSettingsProvider] void Collect(RenderContext& renderContext) override; void Blend(PostProcessSettings& other, float weight) override; protected: // [Actor] void BeginPlay(SceneBeginData* data) override; void EndPlay() override; void OnEnable() override; void OnDisable() override; void OnTransformChanged() override; };