diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorDebugDraw.cpp b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorDebugDraw.cpp index a079cb660c..a6dc818fc5 100644 --- a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorDebugDraw.cpp +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorDebugDraw.cpp @@ -6,6 +6,8 @@ * */ +#include + #include #include #include @@ -25,6 +27,7 @@ namespace AZ::Render { AtomActorDebugDraw::AtomActorDebugDraw(AZ::EntityId entityId) + : m_entityId(entityId) { m_auxGeomFeatureProcessor = RPI::Scene::GetFeatureProcessorForEntity(entityId); } @@ -42,9 +45,11 @@ namespace AZ::Render return; } - const RPI::Scene* scene = RPI::Scene::GetSceneForEntityId(instance->GetEntityId()); + const RPI::Scene* scene = RPI::Scene::GetSceneForEntityId(m_entityId); const RPI::ViewportContextPtr viewport = AZ::Interface::Get()->GetViewportContextByScene(scene); + AzFramework::DebugDisplayRequests* debugDisplay = GetDebugDisplay(viewport->GetId()); const AZ::Render::RenderActorSettings& renderActorSettings = EMotionFX::GetRenderActorSettings(); + const float scaleMultiplier = CalculateScaleMultiplier(instance); // Render aabb if (renderFlags[EMotionFX::ActorRenderFlag::RENDER_AABB]) @@ -75,6 +80,12 @@ namespace AZ::Render RenderEMFXDebugDraw(instance); } + // Render + if (renderFlags[EMotionFX::ActorRenderFlag::RENDER_NODEORIENTATION]) + { + RenderNodeOrientations(instance, debugDisplay, renderActorSettings.m_nodeOrientationScale * scaleMultiplier); + } + // Render vertex normal, face normal, tagent and wireframe. const bool renderVertexNormals = renderFlags[EMotionFX::ActorRenderFlag::RENDER_VERTEXNORMALS]; const bool renderFaceNormals = renderFlags[EMotionFX::ActorRenderFlag::RENDER_FACENORMALS]; @@ -87,7 +98,6 @@ namespace AZ::Render const EMotionFX::Pose* pose = instance->GetTransformData()->GetCurrentPose(); const size_t geomLODLevel = instance->GetLODLevel(); const size_t numEnabled = instance->GetNumEnabledNodes(); - const float scaleMultiplier = CalculateScaleMultiplier(instance); for (size_t i = 0; i < numEnabled; ++i) { EMotionFX::Node* node = instance->GetActor()->GetSkeleton()->GetNode(instance->GetEnabledNode(i)); @@ -175,6 +185,13 @@ namespace AZ::Render } } + AzFramework::DebugDisplayRequests* AtomActorDebugDraw::GetDebugDisplay(AzFramework::ViewportId viewportId) + { + AzFramework::DebugDisplayRequestBus::BusPtr debugDisplayBus; + AzFramework::DebugDisplayRequestBus::Bind(debugDisplayBus, viewportId); + return AzFramework::DebugDisplayRequestBus::FindFirstHandler(debugDisplayBus); + } + void AtomActorDebugDraw::RenderAABB(EMotionFX::ActorInstance* instance, const AZ::Color& aabbColor) { RPI::AuxGeomDrawPtr auxGeom = m_auxGeomFeatureProcessor->GetDrawQueue(); @@ -610,4 +627,89 @@ namespace AZ::Render m_fontDrawInterface->DrawScreenAlignedText3d(m_drawParams, joint->GetName()); } } + + void AtomActorDebugDraw::RenderNodeOrientations(EMotionFX::ActorInstance* actorInstance, + AzFramework::DebugDisplayRequests* debugDisplay, float scale) + { + // Get the actor and the transform data + const float unitScale = + 1.0f / (float)MCore::Distance::ConvertValue(1.0f, MCore::Distance::UNITTYPE_METERS, EMotionFX::GetEMotionFX().GetUnitType()); + const EMotionFX::Actor* actor = actorInstance->GetActor(); + const EMotionFX::Skeleton* skeleton = actor->GetSkeleton(); + const EMotionFX::TransformData* transformData = actorInstance->GetTransformData(); + const EMotionFX::Pose* pose = transformData->GetCurrentPose(); + const float constPreScale = scale * unitScale * 3.0f; + + const size_t numEnabled = actorInstance->GetNumEnabledNodes(); + for (size_t i = 0; i < numEnabled; ++i) + { + EMotionFX::Node* joint = skeleton->GetNode(actorInstance->GetEnabledNode(i)); + const size_t jointIndex = joint->GetNodeIndex(); + + static const float axisBoneScale = 50.0f; + const float size = CalculateBoneScale(actorInstance, joint) * constPreScale * axisBoneScale; + AZ::Transform worldTM = pose->GetWorldSpaceTransform(jointIndex).ToAZTransform(); + RenderLineAxis(debugDisplay, worldTM, size, false /*selected*/); + } + } + + void AtomActorDebugDraw::RenderLineAxis( + AzFramework::DebugDisplayRequests* debugDisplay, + AZ::Transform worldTM, + float size, + bool selected, + bool renderAxisName) + { + const float axisHeight = size * 0.7f; + const float frontSize = size * 5.0f + 0.2f; + const AZ::Vector3 position = worldTM.GetTranslation(); + + // Render x axis + { + AZ::Color xSelectedColor = selected ? AZ::Colors::Orange : AZ::Colors::Red; + + const AZ::Vector3 xAxisDir = (worldTM.TransformPoint(AZ::Vector3(size, 0.0f, 0.0f)) - position).GetNormalized(); + const AZ::Vector3 xAxisArrowStart = position + xAxisDir * axisHeight; + debugDisplay->SetColor(xSelectedColor); + debugDisplay->DrawArrow(position, xAxisArrowStart, size); + + if (renderAxisName) + { + const AZ::Vector3 xNamePos = position + xAxisDir * (size * 1.15f); + debugDisplay->DrawTextLabel(xNamePos, frontSize, "X"); + } + } + + // Render y axis + { + AZ::Color ySelectedColor = selected ? AZ::Colors::Orange : AZ::Colors::Blue; + + const AZ::Vector3 yAxisDir = (worldTM.TransformPoint(AZ::Vector3(0.0f, size, 0.0f)) - position).GetNormalized(); + const AZ::Vector3 yAxisArrowStart = position + yAxisDir * axisHeight; + debugDisplay->SetColor(ySelectedColor); + debugDisplay->DrawArrow(position, yAxisArrowStart, size); + + if (renderAxisName) + { + const AZ::Vector3 yNamePos = position + yAxisDir * (size * 1.15f); + debugDisplay->DrawTextLabel(yNamePos, frontSize, "Y"); + } + } + + // Render z axis + { + AZ::Color zSelectedColor = selected ? AZ::Colors::Orange : AZ::Colors::Green; + + const AZ::Vector3 zAxisDir = (worldTM.TransformPoint(AZ::Vector3(0.0f, 0.0f, size)) - position).GetNormalized(); + const AZ::Vector3 zAxisArrowStart = position + zAxisDir * axisHeight; + debugDisplay->SetColor(zSelectedColor); + debugDisplay->DrawArrow(position, zAxisArrowStart, size); + + if (renderAxisName) + { + const AZ::Vector3 zNamePos = position + zAxisDir * (size * 1.15f); + debugDisplay->DrawTextLabel(zNamePos, frontSize, "Z"); + } + } + } } // namespace AZ::Render diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorDebugDraw.h b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorDebugDraw.h index 6289077fe2..d3a6bc44f3 100644 --- a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorDebugDraw.h +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorDebugDraw.h @@ -15,6 +15,11 @@ #include #include +namespace AzFramework +{ + class DebugDisplayRequests; +} + namespace EMotionFX { class Mesh; @@ -42,6 +47,7 @@ namespace AZ::Render float CalculateBoneScale(EMotionFX::ActorInstance* actorInstance, EMotionFX::Node* node); float CalculateScaleMultiplier(EMotionFX::ActorInstance* instance) const; void PrepareForMesh(EMotionFX::Mesh* mesh, const AZ::Transform& worldTM); + AzFramework::DebugDisplayRequests* GetDebugDisplay(AzFramework::ViewportId viewportId); void RenderAABB(EMotionFX::ActorInstance* instance, const AZ::Color& aabbColor); void RenderLineSkeleton(EMotionFX::ActorInstance* instance, const AZ::Color& skeletonColor); @@ -63,11 +69,18 @@ namespace AZ::Render void RenderWireframe(EMotionFX::Mesh* mesh, const AZ::Transform& worldTM, float wireframeScale, float scaleMultiplier, const AZ::Color& wireframeColor); void RenderJointNames(EMotionFX::ActorInstance* actorInstance, RPI::ViewportContextPtr viewportContext, const AZ::Color& jointNameColor); + void RenderNodeOrientations(EMotionFX::ActorInstance* actorInstance, AzFramework::DebugDisplayRequests* debugDisplay, float scale = 1.0f); + void RenderLineAxis( + AzFramework::DebugDisplayRequests* debugDisplay, + AZ::Transform worldTM, //!< The world space transformation matrix to visualize. */ + float size, //!< The size value in units is used to control the scaling of the axis. */ + bool selected, //!< Set to true if you want to render the axis using the selection color. */ + bool renderAxisName = false); - EMotionFX::Mesh* m_currentMesh = nullptr; /**< A pointer to the mesh whose world space positions are in the pre-calculated positions buffer. - NULL in case we haven't pre-calculated any positions yet. */ - AZStd::vector m_worldSpacePositions; /**< The buffer used to store world space positions for rendering normals - tangents and the wireframe. */ + EMotionFX::Mesh* m_currentMesh = nullptr; //!< A pointer to the mesh whose world space positions are in the pre-calculated positions buffer. + //!< NULL in case we haven't pre-calculated any positions yet. + AZStd::vector m_worldSpacePositions; //!< The buffer used to store world space positions for rendering normals + //!< tangents and the wireframe. static constexpr float BaseFontSize = 0.7f; const Vector3 TopRightBorderPadding = AZ::Vector3(-40.0f, 22.0f, 0.0f); @@ -75,6 +88,7 @@ namespace AZ::Render RPI::AuxGeomFeatureProcessorInterface* m_auxGeomFeatureProcessor = nullptr; AZStd::vector m_auxVertices; AZStd::vector m_auxColors; + EntityId m_entityId; AzFramework::TextDrawParameters m_drawParams; AzFramework::FontDrawInterface* m_fontDrawInterface = nullptr; diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportToolBar.cpp b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportToolBar.cpp index 4fb59afd1b..7bb8d49418 100644 --- a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportToolBar.cpp +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Tools/EMStudio/AnimViewportToolBar.cpp @@ -46,8 +46,8 @@ namespace EMStudio CreateViewOptionEntry(contextMenu, "Line Skeleton", EMotionFX::ActorRenderFlag::RENDER_LINESKELETON); CreateViewOptionEntry(contextMenu, "Solid Skeleton", EMotionFX::ActorRenderFlag::RENDER_SKELETON); CreateViewOptionEntry(contextMenu, "Joint Names", EMotionFX::ActorRenderFlag::RENDER_NODENAMES); + CreateViewOptionEntry(contextMenu, "Joint Orientations", EMotionFX::ActorRenderFlag::RENDER_NODEORIENTATION); // [EMFX-TODO] Add those option once implemented. - // CreateViewOptionEntry(contextMenu, "Joint Orientations", EMotionFX::ActorRenderFlag::RENDER_NODEORIENTATION); // CreateViewOptionEntry(contextMenu, "Actor Bind Pose", EMotionFX::ActorRenderFlag::RENDER_ACTORBINDPOSE); contextMenu->addSeparator(); CreateViewOptionEntry(contextMenu, "Hit Detection Colliders", EMotionFX::ActorRenderFlag::RENDER_HITDETECTION_COLLIDERS); diff --git a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderOptions.cpp b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderOptions.cpp index c7cdecafe0..241412c6c8 100644 --- a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderOptions.cpp +++ b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderOptions.cpp @@ -1063,6 +1063,7 @@ namespace EMStudio settings.m_vertexNormalsScale = m_vertexNormalsScale; settings.m_faceNormalsScale = m_faceNormalsScale; settings.m_tangentsScale = m_tangentsScale; + settings.m_nodeOrientationScale = m_nodeOrientationScale; settings.m_vertexNormalsColor = m_vertexNormalsColor; settings.m_faceNormalsColor = m_faceNormalsColor; @@ -1112,6 +1113,7 @@ namespace EMStudio void RenderOptions::OnNodeOrientationScaleChangedCallback() const { PluginOptionsNotificationsBus::Event(s_nodeOrientationScaleOptionName, &PluginOptionsNotificationsBus::Events::OnOptionChanged, s_nodeOrientationScaleOptionName); + CopyToRenderActorSettings(EMotionFX::GetRenderActorSettings()); } void RenderOptions::OnScaleBonesOnLengthChangedCallback() const diff --git a/Gems/EMotionFX/Code/Source/Integration/Rendering/RenderActorSettings.h b/Gems/EMotionFX/Code/Source/Integration/Rendering/RenderActorSettings.h index 0fa7842ded..0260d1aae5 100644 --- a/Gems/EMotionFX/Code/Source/Integration/Rendering/RenderActorSettings.h +++ b/Gems/EMotionFX/Code/Source/Integration/Rendering/RenderActorSettings.h @@ -26,6 +26,7 @@ namespace AZ::Render float m_faceNormalsScale = 1.0f; float m_tangentsScale = 1.0f; float m_wireframeScale = 1.0f; + float m_nodeOrientationScale = 1.0f; AZ::Color m_hitDetectionColliderColor{0.44f, 0.44f, 0.44f, 1.0f}; AZ::Color m_selectedHitDetectionColliderColor{ 0.3f, 0.56f, 0.88f, 1.0f };