diff --git a/Source/Editor/Viewport/Previews/AnimatedModelPreview.cs b/Source/Editor/Viewport/Previews/AnimatedModelPreview.cs index ec9fa9b58..771fc4db1 100644 --- a/Source/Editor/Viewport/Previews/AnimatedModelPreview.cs +++ b/Source/Editor/Viewport/Previews/AnimatedModelPreview.cs @@ -336,7 +336,8 @@ namespace FlaxEditor.Viewport.Previews if (_showNodes) { // Draw bounding box at the node locations - var localBox = new OrientedBoundingBox(new Vector3(-1.0f), new Vector3(1.0f)); + var boxSize = Mathf.Min(1.0f, _previewModel.Sphere.Radius / 100.0f); + var localBox = new OrientedBoundingBox(new Vector3(-boxSize), new Vector3(boxSize)); for (int nodeIndex = 0; nodeIndex < pose.Length; nodeIndex++) { if (nodesMask != null && !nodesMask[nodeIndex]) diff --git a/Source/Engine/Level/Actors/AnimatedModel.cpp b/Source/Engine/Level/Actors/AnimatedModel.cpp index 6ab60a317..ccd5fa95c 100644 --- a/Source/Engine/Level/Actors/AnimatedModel.cpp +++ b/Source/Engine/Level/Actors/AnimatedModel.cpp @@ -7,6 +7,8 @@ #include "Engine/Animations/Animations.h" #include "Engine/Engine/Engine.h" #if USE_EDITOR +#include "Engine/Core/Math/OrientedBoundingBox.h" +#include "Engine/Core/Math/Matrix3x3.h" #include "Editor/Editor.h" #endif #include "Engine/Graphics/GPUContext.h" @@ -1018,6 +1020,45 @@ void AnimatedModel::OnDebugDrawSelected() ModelInstanceActor::OnDebugDrawSelected(); } +void AnimatedModel::OnDebugDraw() +{ + if (ShowDebugDrawSkeleton && SkinnedModel && AnimationGraph) + { + if (GraphInstance.NodesPose.IsEmpty()) + PreInitSkinningData(); + Matrix world; + GetLocalToWorldMatrix(world); + + // Draw bounding box at the node locations + const float boxSize = Math::Min(1.0f, _sphere.Radius / 100.0f); + OrientedBoundingBox localBox(Vector3(-boxSize), Vector3(boxSize)); + for (int32 nodeIndex = 0; nodeIndex < GraphInstance.NodesPose.Count(); nodeIndex++) + { + Matrix transform = GraphInstance.NodesPose[nodeIndex] * world; + Float3 scale, translation; + Matrix3x3 rotation; + transform.Decompose(scale, rotation, translation); + transform = Matrix::Invert(Matrix::Scaling(scale)) * transform; + OrientedBoundingBox box = localBox * transform; + DEBUG_DRAW_WIRE_BOX(box, Color::Green, 0, false); + } + + // Nodes connections + for (int32 nodeIndex = 0; nodeIndex < SkinnedModel->Skeleton.Nodes.Count(); nodeIndex++) + { + int32 parentIndex = SkinnedModel->Skeleton.Nodes[nodeIndex].ParentIndex; + if (parentIndex != -1) + { + Float3 parentPos = (GraphInstance.NodesPose[parentIndex] * world).GetTranslation(); + Float3 bonePos = (GraphInstance.NodesPose[nodeIndex] * world).GetTranslation(); + DEBUG_DRAW_LINE(parentPos, bonePos, Color::Green, 0, false); + } + } + } + + ModelInstanceActor::OnDebugDraw(); +} + BoundingBox AnimatedModel::GetEditorBox() const { if (SkinnedModel) diff --git a/Source/Engine/Level/Actors/AnimatedModel.h b/Source/Engine/Level/Actors/AnimatedModel.h index 009ee948d..bb475722a 100644 --- a/Source/Engine/Level/Actors/AnimatedModel.h +++ b/Source/Engine/Level/Actors/AnimatedModel.h @@ -168,6 +168,13 @@ public: API_FIELD(Attributes="EditorOrder(120), DefaultValue(null), EditorDisplay(\"Skinned Model\")") ScriptingObjectReference RootMotionTarget; +#if USE_EDITOR + /// + /// If checked, the skeleton pose will be shawn during debug shapes drawing. + /// + API_FIELD(Attributes="EditorOrder(200), EditorDisplay(\"Skinned Model\")") bool ShowDebugDrawSkeleton = false; +#endif + public: /// /// The graph instance data container. For dynamic usage only at runtime, not serialized. @@ -416,6 +423,7 @@ public: void Draw(RenderContextBatch& renderContextBatch) override; #if USE_EDITOR void OnDebugDrawSelected() override; + void OnDebugDraw() override; BoundingBox GetEditorBox() const override; #endif bool IntersectsItself(const Ray& ray, Real& distance, Vector3& normal) override;