// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
#pragma once
#include "ModelInstanceActor.h"
#include "Engine/Content/Assets/SkinnedModel.h"
#include "Engine/Content/Assets/AnimationGraph.h"
#include "Engine/Graphics/Models/SkinnedMeshDrawData.h"
///
/// Performs an animation and renders a skinned model.
///
API_CLASS() class FLAXENGINE_API AnimatedModel : public ModelInstanceActor
{
DECLARE_SCENE_OBJECT(AnimatedModel);
friend class AnimationManagerService;
public:
///
/// Describes the animation graph updates frequency for the animated model.
///
API_ENUM() enum class AnimationUpdateMode
{
///
/// The automatic updates will be used (based on platform capabilities, distance to the player, etc.).
///
Auto = 0,
///
/// Animation will be updated every game update.
///
EveryUpdate = 1,
///
/// Animation will be updated every second game update.
///
EverySecondUpdate = 2,
///
/// Animation will be updated every fourth game update.
///
EveryFourthUpdate = 3,
///
/// Animation can be updated manually by the user scripts.
///
Manual = 4,
///
/// Animation won't be updated at all.
///
Never = 5,
};
private:
BoundingBox _boxLocal;
Matrix _world;
GeometryDrawStateData _drawState;
SkinnedMeshDrawData _skinningData;
AnimationUpdateMode _actualMode;
uint32 _counter;
float _lastMinDstSqr;
uint64 _lastUpdateFrame;
BlendShapesInstance _blendShapes;
public:
///
/// The skinned model asset used for rendering.
///
API_FIELD(Attributes="EditorOrder(10), DefaultValue(null), EditorDisplay(\"Skinned Model\")")
AssetReference SkinnedModel;
///
/// The animation graph asset used for the skinned mesh skeleton bones evaluation (controls the animation).
///
API_FIELD(Attributes="EditorOrder(15), DefaultValue(null), EditorDisplay(\"Skinned Model\")")
AssetReference AnimationGraph;
///
/// If true, use per-bone motion blur on this skeletal model. It requires additional rendering, can be disabled to save performance.
///
API_FIELD(Attributes="EditorOrder(20), DefaultValue(true), EditorDisplay(\"Skinned Model\")")
bool PerBoneMotionBlur = true;
///
/// If true, animation speed will be affected by the global time scale parameter.
///
API_FIELD(Attributes="EditorOrder(30), DefaultValue(true), EditorDisplay(\"Skinned Model\")")
bool UseTimeScale = true;
///
/// If true, the animation will be updated even when an actor cannot be seen by any camera. Otherwise, the animations themselves will also stop running when the actor is off-screen.
///
API_FIELD(Attributes="EditorOrder(40), DefaultValue(false), EditorDisplay(\"Skinned Model\")")
bool UpdateWhenOffscreen = false;
///
/// The animation update mode. Can be used to optimize the performance.
///
API_FIELD(Attributes="EditorOrder(50), DefaultValue(AnimationUpdateMode.Auto), EditorDisplay(\"Skinned Model\")")
AnimationUpdateMode UpdateMode = AnimationUpdateMode::Auto;
///
/// The master scale parameter for the actor bounding box. Helps reducing mesh flickering effect on screen edges.
///
API_FIELD(Attributes="EditorOrder(60), DefaultValue(1.5f), Limit(0), EditorDisplay(\"Skinned Model\")")
float BoundsScale = 1.5f;
///
/// The custom bounds(in actor local space). If set to empty bounds then source skinned model bind pose bounds will be used.
///
API_FIELD(Attributes="EditorOrder(70), EditorDisplay(\"Skinned Model\")")
BoundingBox CustomBounds = BoundingBox::Zero;
///
/// The model Level Of Detail bias value. Allows to increase or decrease rendered model quality.
///
API_FIELD(Attributes="EditorOrder(80), DefaultValue(0), Limit(-100, 100, 0.1f), EditorDisplay(\"Skinned Model\", \"LOD Bias\")")
int32 LODBias = 0;
///
/// Gets the model forced Level Of Detail index. Allows to bind the given model LOD to show. Value -1 disables this feature.
///
API_FIELD(Attributes="EditorOrder(90), DefaultValue(-1), Limit(-1, 100, 0.1f), EditorDisplay(\"Skinned Model\", \"Forced LOD\")")
int32 ForcedLOD = -1;
///
/// The draw passes to use for rendering this object.
///
API_FIELD(Attributes="EditorOrder(100), DefaultValue(DrawPass.Default), EditorDisplay(\"Skinned Model\")")
DrawPass DrawModes = DrawPass::Default;
///
/// The shadows casting mode.
///
API_FIELD(Attributes="EditorOrder(110), DefaultValue(ShadowsCastingMode.All), EditorDisplay(\"Skinned Model\")")
ShadowsCastingMode ShadowsMode = ShadowsCastingMode::All;
///
/// The animation root motion apply target. If not specified the animated model will apply it itself.
///
API_FIELD(Attributes="EditorOrder(120), DefaultValue(null), EditorDisplay(\"Skinned Model\")")
ScriptingObjectReference RootMotionTarget;
public:
///
/// The graph instance data container. For dynamic usage only at runtime, not serialized.
///
AnimGraphInstanceData GraphInstance;
///
/// Gets the model world matrix transform.
///
FORCE_INLINE void GetWorld(Matrix* world) const
{
*world = _world;
}
///
/// Resets the animation state (clears the instance state data but preserves the instance parameters values).
///
API_FUNCTION() void ResetAnimation();
///
/// Performs the full animation update.
///
API_FUNCTION() void UpdateAnimation();
///
/// Validates and creates a proper skinning data.
///
API_FUNCTION() void SetupSkinningData();
///
/// Creates and setups the skinning data (writes the identity bones transformations).
///
API_FUNCTION() void PreInitSkinningData();
///
/// Updates the child bone socket actors.
///
API_FUNCTION() void UpdateSockets();
///
/// Gets the per-node final transformations.
///
/// The output per-node final transformation matrices.
/// True if convert matrices into world-space, otherwise returned values will be in local-space of the actor.
API_FUNCTION() void GetCurrentPose(API_PARAM(Out) Array& nodesTransformation, bool worldSpace = false) const;
///
/// Gets the node final transformation.
///
/// The index of the skinned model skeleton node.
/// The output final node transformation matrix.
/// True if convert matrices into world-space, otherwise returned values will be in local-space of the actor.
API_FUNCTION() void GetNodeTransformation(int32 nodeIndex, API_PARAM(Out) Matrix& nodeTransformation, bool worldSpace = false) const;
///
/// Gets the node final transformation.
///
/// The name of the skinned model skeleton node.
/// The output final node transformation matrix.
/// True if convert matrices into world-space, otherwise returned values will be in local-space of the actor.
API_FUNCTION() void GetNodeTransformation(const StringView& nodeName, API_PARAM(Out) Matrix& nodeTransformation, bool worldSpace = false) const;
public:
///
/// Gets the anim graph instance parameters collection.
///
API_PROPERTY() const Array& GetParameters() const
{
return GraphInstance.Parameters;
}
///
/// Gets the anim graph instance parameter by name.
///
/// The parameter name.
/// The parameters.
API_FUNCTION() AnimGraphParameter* GetParameter(const StringView& name);
///
/// Gets the anim graph instance parameter value.
///
/// The parameter name.
/// The value.
API_FUNCTION() Variant GetParameterValue(const StringView& name);
///
/// Sets the anim graph instance parameter value.
///
/// The parameter name.
/// The value to set.
API_FUNCTION() void SetParameterValue(const StringView& name, const Variant& value);
///
/// Gets the anim graph instance parameter value.
///
/// The parameter id.
/// The value.
API_FUNCTION() Variant GetParameterValue(const Guid& id);
///
/// Sets the anim graph instance parameter value.
///
/// The parameter id.
/// The value to set.
API_FUNCTION() void SetParameterValue(const Guid& id, const Variant& value);
public:
///
/// Gets the weight of the blend shape.
///
/// The blend shape name.
/// The normalized weight of the blend shape (in range -1:1).
API_FUNCTION() float GetBlendShapeWeight(const StringView& name);
///
/// Sets the weight of the blend shape.
///
/// The blend shape name.
/// The normalized weight of the blend shape (in range -1:1).
API_FUNCTION() void SetBlendShapeWeight(const StringView& name, float value);
///
/// Clears the weights of the blend shapes (disabled any used blend shapes).
///
API_FUNCTION() void ClearBlendShapeWeights();
private:
///
/// Applies the root motion delta to the target actor.
///
void ApplyRootMotion(const RootMotionData& rootMotionDelta);
///
/// Synchronizes the parameters collection (may lost existing params data on collection change detected).
///
void SyncParameters();
///
/// Updates the local bounds of the actor.
///
void UpdateLocalBounds();
///
/// Called after animation graph update.
///
void OnAnimUpdate();
void OnSkinnedModelChanged();
void OnSkinnedModelLoaded();
void OnGraphChanged();
void OnGraphLoaded();
void Update();
public:
// [ModelInstanceActor]
bool HasContentLoaded() const override;
void Draw(RenderContext& renderContext) override;
void DrawGeneric(RenderContext& renderContext) override;
#if USE_EDITOR
void OnDebugDrawSelected() override;
#endif
bool IntersectsItself(const Ray& ray, float& distance, Vector3& normal) override;
void Serialize(SerializeStream& stream, const void* otherObj) override;
void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override;
bool IntersectsEntry(int32 entryIndex, const Ray& ray, float& distance, Vector3& normal) override;
bool IntersectsEntry(const Ray& ray, float& distance, Vector3& normal, int32& entryIndex) override;
void UpdateBounds() final override;
protected:
// [ModelInstanceActor]
void BeginPlay(SceneBeginData* data) override;
void EndPlay() override;
void OnEnable() override;
void OnDisable() override;
void OnActiveInTreeChanged() override;
void OnTransformChanged() override;
};