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