// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. #pragma once #include "../Actor.h" #include "Engine/Core/Collections/Dictionary.h" /// /// Actor that synchronizes Animated Model skeleton pose with physical bones bodies simulated with physics. Child rigidbodies are used for per-bone simulation - rigidbodies names must match skeleton bone name and should be ordered based on importance in the skeleton tree (parents first). /// API_CLASS(Attributes="ActorToolbox(\"Physics\")") class FLAXENGINE_API Ragdoll : public Actor { DECLARE_SCENE_OBJECT(Ragdoll); API_AUTO_SERIALIZATION(); private: AnimatedModel* _animatedModel = nullptr; Dictionary _bonesOffsets; public: /// /// The default bones weight where 0 means fully animated bone and 1 means fully simulate bones. Can be used to control all bones simulation mode but is overriden by per-bone BonesWeights. /// API_FIELD(Attributes="EditorOrder(10), EditorDisplay(\"Ragdoll\"), Limit(0, 1)") float BonesWeight = 1.0f; /// /// The per-bone weights for ragdoll simulation. Key is bone name, value is the blend weight where 0 means fully animated bone and 1 means fully simulated bone. Can be used to control per-bone simulation. /// API_FIELD(Attributes="EditorOrder(20), EditorDisplay(\"Ragdoll\")") Dictionary BonesWeights; /// /// The minimum number of position iterations the physics solver should perform for bodies in this ragdoll. Higher values improve stability but affect performance. /// /// API_FIELD(Attributes="EditorOrder(100), EditorDisplay(\"Ragdoll\"), Limit(1, 255)") uint8 PositionSolverIterations = 8; /// /// The minimum number of velocity iterations the physics solver should perform for bodies in this ragdoll. Higher values improve stability but affect performance. /// /// API_FIELD(Attributes="EditorOrder(110), EditorDisplay(\"Ragdoll\"), Limit(1, 255)") uint8 VelocitySolverIterations = 2; /// /// The maximum depenetration velocity when ragdoll's rigidbody moving out of penetrating state. Using this property can smooth objects moving out of colliding state and prevent unstable motion. /// /// API_FIELD(Attributes="EditorOrder(120), EditorDisplay(\"Ragdoll\"), Limit(0)") float MaxDepenetrationVelocity = MAX_float; public: /// /// Calculates the total mass of all ragdoll bodies. /// API_PROPERTY() float GetTotalMass() const; /// /// Sets the linear velocity of all rigidbodies in the ragdoll. /// API_FUNCTION() void SetLinearVelocity(const Vector3& value) const; /// /// Sets the angular velocity of all rigidbodies in the ragdoll (in radians per second). /// API_FUNCTION() void SetAngularVelocity(const Vector3& value) const; private: float InitBone(RigidBody* rigidBody, int32& nodeIndex, Transform& localPose); void OnFixedUpdate(); void OnAnimationUpdating(struct AnimGraphImpulse* localPose); public: // [Actor] void OnEnable() override; void OnDisable() override; void OnParentChanged() override; void OnTransformChanged() override; #if USE_EDITOR void OnDebugDrawSelected() override; #endif };