// 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;
};