AtomActorDebugDraw.cpp 57 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include <AzFramework/Entity/EntityDebugDisplayBus.h>
  9. #include <AtomActorDebugDraw.h>
  10. #include <Atom/RPI.Public/Scene.h>
  11. #include <Atom/RPI.Public/AuxGeom/AuxGeomDraw.h>
  12. #include <Atom/RPI.Public/AuxGeom/AuxGeomFeatureProcessorInterface.h>
  13. #include <Atom/RPI.Public/ViewportContextBus.h>
  14. #include <Integration/Rendering/RenderActorInstance.h>
  15. #include <Integration/Rendering/RenderActorSettings.h>
  16. #include <EMotionFX/Source/ActorInstance.h>
  17. #include <EMotionFX/Source/DebugDraw.h>
  18. #include <EMotionFX/Source/EMotionFXManager.h>
  19. #include <EMotionFX/Source/RagdollInstance.h>
  20. #include <EMotionFX/Source/SubMesh.h>
  21. #include <EMotionFX/Source/TransformData.h>
  22. #include <EMotionFX/Source/Mesh.h>
  23. #include <EMotionFX/Source/Node.h>
  24. #include <EMotionFX/Source/JointSelectionBus.h>
  25. namespace AZ::Render
  26. {
  27. AtomActorDebugDraw::AtomActorDebugDraw(AZ::EntityId entityId)
  28. : m_entityId(entityId)
  29. {
  30. m_auxGeomFeatureProcessor = RPI::Scene::GetFeatureProcessorForEntity<RPI::AuxGeomFeatureProcessorInterface>(entityId);
  31. AZ_Assert(m_auxGeomFeatureProcessor, "AuxGeomFeatureProcessor doesn't exist. Check if it is missing from AnimViewport.setreg file.");
  32. }
  33. // Function for providing data required for debug drawing colliders
  34. Physics::CharacterPhysicsDebugDraw::NodeDebugDrawData GetNodeDebugDrawData(
  35. const Physics::CharacterColliderNodeConfiguration& colliderNodeConfig,
  36. const EMotionFX::ActorInstance* instance,
  37. const AZStd::unordered_set<size_t>* cachedSelectedJointIndices,
  38. size_t cachedHoveredJointIndex)
  39. {
  40. Physics::CharacterPhysicsDebugDraw::NodeDebugDrawData nodeDebugDrawData;
  41. const EMotionFX::Actor* actor = instance->GetActor();
  42. const EMotionFX::Node* joint = actor->GetSkeleton()->FindNodeByName(colliderNodeConfig.m_name.c_str());
  43. if (!joint)
  44. {
  45. nodeDebugDrawData.m_valid = false;
  46. return nodeDebugDrawData;
  47. }
  48. const size_t nodeIndex = joint->GetNodeIndex();
  49. nodeDebugDrawData.m_selected = cachedSelectedJointIndices &&
  50. (cachedSelectedJointIndices->empty() || cachedSelectedJointIndices->find(nodeIndex) != cachedSelectedJointIndices->end());
  51. nodeDebugDrawData.m_hovered = (nodeIndex == cachedHoveredJointIndex);
  52. const EMotionFX::Transform& actorInstanceGlobalTransform = instance->GetWorldSpaceTransform();
  53. const EMotionFX::Transform& emfxNodeGlobalTransform =
  54. instance->GetTransformData()->GetCurrentPose()->GetModelSpaceTransform(nodeIndex);
  55. nodeDebugDrawData.m_worldTransform = (emfxNodeGlobalTransform * actorInstanceGlobalTransform).ToAZTransform();
  56. nodeDebugDrawData.m_valid = true;
  57. return nodeDebugDrawData;
  58. };
  59. // Function for proviuding data required for debug drawing joint limits
  60. Physics::CharacterPhysicsDebugDraw::JointDebugDrawData GetJointDebugDrawData(
  61. const Physics::RagdollNodeConfiguration& ragdollNodeConfig,
  62. const EMotionFX::ActorInstance* instance,
  63. const AZStd::unordered_set<size_t>* cachedSelectedJointIndices,
  64. [[maybe_unused]] size_t cachedHoveredJointIndex)
  65. {
  66. Physics::CharacterPhysicsDebugDraw::JointDebugDrawData jointDebugDrawData;
  67. const EMotionFX::Actor* actor = instance->GetActor();
  68. const EMotionFX::Node* joint = actor->GetSkeleton()->FindNodeByName(ragdollNodeConfig.m_debugName.c_str());
  69. if (!joint)
  70. {
  71. jointDebugDrawData.m_valid = false;
  72. return jointDebugDrawData;
  73. }
  74. jointDebugDrawData.m_valid = true;
  75. const size_t nodeIndex = joint->GetNodeIndex();
  76. const bool jointSelected = cachedSelectedJointIndices &&
  77. (cachedSelectedJointIndices->empty() || cachedSelectedJointIndices->find(nodeIndex) != cachedSelectedJointIndices->end());
  78. if (!jointSelected)
  79. {
  80. jointDebugDrawData.m_visible = false;
  81. return jointDebugDrawData;
  82. }
  83. const EMotionFX::Node* ragdollParentNode = instance->GetActor()->GetPhysicsSetup()->FindRagdollParentNode(joint);
  84. if (!ragdollParentNode)
  85. {
  86. jointDebugDrawData.m_valid = false;
  87. return jointDebugDrawData;
  88. }
  89. const size_t ragdollParentNodeIndex = ragdollParentNode->GetNodeIndex();
  90. const EMotionFX::Pose* currentPose = instance->GetTransformData()->GetCurrentPose();
  91. const EMotionFX::Transform& childModelSpaceTransform = currentPose->GetModelSpaceTransform(nodeIndex);
  92. jointDebugDrawData.m_childModelSpaceOrientation = childModelSpaceTransform.m_rotation;
  93. jointDebugDrawData.m_parentModelSpaceOrientation = currentPose->GetModelSpaceTransform(ragdollParentNodeIndex).m_rotation;
  94. EMotionFX::Transform parentModelSpaceTransform = currentPose->GetModelSpaceTransform(ragdollParentNodeIndex);
  95. parentModelSpaceTransform.m_position = currentPose->GetModelSpaceTransform(nodeIndex).m_position;
  96. jointDebugDrawData.m_parentWorldTransform = (parentModelSpaceTransform * instance->GetWorldSpaceTransform()).ToAZTransform();
  97. jointDebugDrawData.m_childWorldTransform = (childModelSpaceTransform * instance->GetWorldSpaceTransform()).ToAZTransform();
  98. jointDebugDrawData.m_visible = true;
  99. jointDebugDrawData.m_selected = true;
  100. return jointDebugDrawData;
  101. }
  102. void AtomActorDebugDraw::DebugDraw(const EMotionFX::ActorRenderFlags& renderFlags, EMotionFX::ActorInstance* instance)
  103. {
  104. if (!m_auxGeomFeatureProcessor || !instance)
  105. {
  106. return;
  107. }
  108. RPI::AuxGeomDrawPtr auxGeom = m_auxGeomFeatureProcessor->GetDrawQueue();
  109. if (!auxGeom)
  110. {
  111. return;
  112. }
  113. using AZ::RHI::CheckBitsAny;
  114. // Update the mesh deformers (perform cpu skinning and morphing) when needed.
  115. if (CheckBitsAny(renderFlags,
  116. EMotionFX::ActorRenderFlags::AABB | EMotionFX::ActorRenderFlags::FaceNormals | EMotionFX::ActorRenderFlags::Tangents |
  117. EMotionFX::ActorRenderFlags::VertexNormals | EMotionFX::ActorRenderFlags::Wireframe))
  118. {
  119. instance->UpdateMeshDeformers(0.0f, true);
  120. }
  121. const RPI::Scene* scene = RPI::Scene::GetSceneForEntityId(m_entityId);
  122. const RPI::ViewportContextPtr viewport = AZ::Interface<AZ::RPI::ViewportContextRequestsInterface>::Get()->GetViewportContextByScene(scene);
  123. AzFramework::DebugDisplayRequests* debugDisplay = GetDebugDisplay(viewport->GetId());
  124. const AZ::Render::RenderActorSettings& renderActorSettings = EMotionFX::GetRenderActorSettings();
  125. const float scaleMultiplier = CalculateScaleMultiplier(instance);
  126. // Render aabb
  127. if (CheckBitsAny(renderFlags, EMotionFX::ActorRenderFlags::AABB))
  128. {
  129. RenderAABB(instance,
  130. renderActorSettings.m_enabledNodeBasedAabb, renderActorSettings.m_nodeAABBColor,
  131. renderActorSettings.m_enabledMeshBasedAabb, renderActorSettings.m_meshAABBColor,
  132. renderActorSettings.m_enabledStaticBasedAabb, renderActorSettings.m_staticAABBColor);
  133. }
  134. // Render simple line skeleton
  135. if (CheckBitsAny(renderFlags, EMotionFX::ActorRenderFlags::LineSkeleton))
  136. {
  137. RenderLineSkeleton(debugDisplay, instance, renderActorSettings.m_lineSkeletonColor);
  138. }
  139. // Render advanced skeleton
  140. if (CheckBitsAny(renderFlags, EMotionFX::ActorRenderFlags::Skeleton))
  141. {
  142. RenderSkeleton(debugDisplay, instance, renderActorSettings.m_skeletonColor);
  143. }
  144. if (CheckBitsAny(renderFlags, EMotionFX::ActorRenderFlags::NodeNames))
  145. {
  146. RenderJointNames(instance, viewport, renderActorSettings.m_jointNameColor);
  147. }
  148. // Render internal EMFX debug lines.
  149. if (CheckBitsAny(renderFlags, EMotionFX::ActorRenderFlags::EmfxDebug))
  150. {
  151. RenderEMFXDebugDraw(instance);
  152. }
  153. if (CheckBitsAny(renderFlags, EMotionFX::ActorRenderFlags::NodeOrientation))
  154. {
  155. RenderNodeOrientations(instance, debugDisplay, renderActorSettings.m_nodeOrientationScale * scaleMultiplier);
  156. }
  157. if (CheckBitsAny(renderFlags, EMotionFX::ActorRenderFlags::MotionExtraction))
  158. {
  159. RenderTrajectoryPath(debugDisplay, instance, renderActorSettings.m_trajectoryHeadColor, renderActorSettings.m_trajectoryPathColor);
  160. }
  161. if (CheckBitsAny(renderFlags, EMotionFX::ActorRenderFlags::RootMotion))
  162. {
  163. RenderRootMotion(debugDisplay, instance, AZ::Colors::Red);
  164. }
  165. // Render vertex normal, face normal, tagent and wireframe.
  166. const bool renderVertexNormals = CheckBitsAny(renderFlags, EMotionFX::ActorRenderFlags::VertexNormals);
  167. const bool renderFaceNormals = CheckBitsAny(renderFlags, EMotionFX::ActorRenderFlags::FaceNormals);
  168. const bool renderTangents = CheckBitsAny(renderFlags, EMotionFX::ActorRenderFlags::Tangents);
  169. const bool renderWireframe = CheckBitsAny(renderFlags, EMotionFX::ActorRenderFlags::Wireframe);
  170. if (renderVertexNormals || renderFaceNormals || renderTangents || renderWireframe)
  171. {
  172. // Iterate through all enabled nodes
  173. const EMotionFX::Pose* pose = instance->GetTransformData()->GetCurrentPose();
  174. const size_t geomLODLevel = instance->GetLODLevel();
  175. const size_t numEnabled = instance->GetNumEnabledNodes();
  176. for (size_t i = 0; i < numEnabled; ++i)
  177. {
  178. EMotionFX::Node* node = instance->GetActor()->GetSkeleton()->GetNode(instance->GetEnabledNode(i));
  179. EMotionFX::Mesh* mesh = instance->GetActor()->GetMesh(geomLODLevel, node->GetNodeIndex());
  180. const AZ::Transform globalTM = pose->GetMeshNodeWorldSpaceTransform(geomLODLevel, node->GetNodeIndex()).ToAZTransform();
  181. m_currentMesh = nullptr;
  182. if (!mesh)
  183. {
  184. continue;
  185. }
  186. RenderNormals(mesh, globalTM, renderVertexNormals, renderFaceNormals, renderActorSettings.m_vertexNormalsScale,
  187. renderActorSettings.m_faceNormalsScale, scaleMultiplier, renderActorSettings.m_vertexNormalsColor, renderActorSettings.m_faceNormalsColor);
  188. if (renderTangents)
  189. {
  190. RenderTangents(mesh, globalTM, renderActorSettings.m_tangentsScale, scaleMultiplier,
  191. renderActorSettings.m_tangentsColor, renderActorSettings.m_mirroredBitangentsColor, renderActorSettings.m_bitangentsColor);
  192. }
  193. if (renderWireframe)
  194. {
  195. RenderWireframe(mesh, globalTM, renderActorSettings.m_wireframeScale * scaleMultiplier, renderActorSettings.m_wireframeColor);
  196. }
  197. }
  198. }
  199. // Data required for debug drawing colliders and ragdolls
  200. const AZStd::unordered_set<size_t>* cachedSelectedJointIndices;
  201. EMotionFX::JointSelectionRequestBus::BroadcastResult(
  202. cachedSelectedJointIndices, &EMotionFX::JointSelectionRequests::FindSelectedJointIndices, instance);
  203. size_t cachedHoveredJointIndex = InvalidIndex;
  204. EMotionFX::JointSelectionRequestBus::BroadcastResult(
  205. cachedHoveredJointIndex, &EMotionFX::JointSelectionRequests::FindHoveredJointIndex, instance);
  206. Physics::CharacterPhysicsDebugDraw::NodeDebugDrawDataFunction nodeDebugDrawDataFunction =
  207. [instance, cachedSelectedJointIndices, cachedHoveredJointIndex](
  208. const Physics::CharacterColliderNodeConfiguration& colliderNodeConfig)
  209. {
  210. return GetNodeDebugDrawData(colliderNodeConfig, instance, cachedSelectedJointIndices, cachedHoveredJointIndex);
  211. };
  212. Physics::CharacterPhysicsDebugDraw::JointDebugDrawDataFunction jointDebugDrawDataFunction =
  213. [instance, cachedSelectedJointIndices, cachedHoveredJointIndex](const Physics::RagdollNodeConfiguration& ragdollNodeConfig)
  214. {
  215. return GetJointDebugDrawData(ragdollNodeConfig, instance, cachedSelectedJointIndices, cachedHoveredJointIndex);
  216. };
  217. // Hit detection colliders
  218. if (CheckBitsAny(renderFlags, EMotionFX::ActorRenderFlags::HitDetectionColliders))
  219. {
  220. m_characterPhysicsDebugDraw.RenderColliders(
  221. debugDisplay,
  222. instance->GetActor()->GetPhysicsSetup()->GetColliderConfigByType(EMotionFX::PhysicsSetup::HitDetection),
  223. nodeDebugDrawDataFunction,
  224. Physics::CharacterPhysicsDebugDraw::ColorSettings{ renderActorSettings.m_hitDetectionColliderColor,
  225. renderActorSettings.m_selectedHitDetectionColliderColor,
  226. renderActorSettings.m_hoveredHitDetectionColliderColor });
  227. }
  228. // Cloth colliders
  229. if (CheckBitsAny(renderFlags, EMotionFX::ActorRenderFlags::ClothColliders))
  230. {
  231. m_characterPhysicsDebugDraw.RenderColliders(
  232. debugDisplay,
  233. instance->GetActor()->GetPhysicsSetup()->GetColliderConfigByType(EMotionFX::PhysicsSetup::Cloth),
  234. nodeDebugDrawDataFunction,
  235. Physics::CharacterPhysicsDebugDraw::ColorSettings{ renderActorSettings.m_clothColliderColor,
  236. renderActorSettings.m_selectedClothColliderColor,
  237. renderActorSettings.m_hoveredClothColliderColor });
  238. }
  239. // Simulated object colliders
  240. if (CheckBitsAny(renderFlags, EMotionFX::ActorRenderFlags::SimulatedObjectColliders))
  241. {
  242. m_characterPhysicsDebugDraw.RenderColliders(
  243. debugDisplay,
  244. instance->GetActor()->GetPhysicsSetup()->GetColliderConfigByType(EMotionFX::PhysicsSetup::SimulatedObjectCollider),
  245. nodeDebugDrawDataFunction,
  246. Physics::CharacterPhysicsDebugDraw::ColorSettings{ renderActorSettings.m_simulatedObjectColliderColor,
  247. renderActorSettings.m_selectedSimulatedObjectColliderColor,
  248. renderActorSettings.m_hoveredSimulatedObjectColliderColor });
  249. }
  250. // Ragdoll
  251. if (AZ::RHI::CheckBitsAny(renderFlags, EMotionFX::ActorRenderFlags::RagdollColliders))
  252. {
  253. Physics::CharacterColliderConfiguration* ragdollColliderConfiguration =
  254. instance->GetActor()->GetPhysicsSetup()->GetColliderConfigByType(EMotionFX::PhysicsSetup::Ragdoll);
  255. Physics::ParentIndices parentIndices;
  256. parentIndices.reserve(ragdollColliderConfiguration->m_nodes.size());
  257. for (const auto& nodeConfiguration : ragdollColliderConfiguration->m_nodes)
  258. {
  259. AZ::Outcome<size_t> parentIndexOutcome;
  260. const EMotionFX::Skeleton* skeleton = instance->GetActor()->GetSkeleton();
  261. EMotionFX::Node* childNode = skeleton->FindNodeByName(nodeConfiguration.m_name);
  262. if (childNode)
  263. {
  264. const EMotionFX::Node* parentNode = childNode->GetParentNode();
  265. if (parentNode)
  266. {
  267. parentIndexOutcome = ragdollColliderConfiguration->FindNodeConfigIndexByName(parentNode->GetNameString());
  268. }
  269. }
  270. parentIndices.push_back(parentIndexOutcome.GetValueOr(SIZE_MAX));
  271. }
  272. m_characterPhysicsDebugDraw.RenderRagdollColliders(
  273. debugDisplay,
  274. ragdollColliderConfiguration,
  275. nodeDebugDrawDataFunction,
  276. parentIndices,
  277. Physics::CharacterPhysicsDebugDraw::ColorSettings{ renderActorSettings.m_ragdollColliderColor,
  278. renderActorSettings.m_selectedRagdollColliderColor,
  279. renderActorSettings.m_hoveredRagdollColliderColor,
  280. renderActorSettings.m_violatedRagdollColliderColor });
  281. }
  282. if (AZ::RHI::CheckBitsAny(renderFlags, EMotionFX::ActorRenderFlags::RagdollJointLimits))
  283. {
  284. m_characterPhysicsDebugDraw.RenderJointLimits(
  285. debugDisplay,
  286. instance->GetActor()->GetPhysicsSetup()->GetRagdollConfig(),
  287. jointDebugDrawDataFunction,
  288. Physics::CharacterPhysicsDebugDraw::ColorSettings{ renderActorSettings.m_ragdollColliderColor,
  289. renderActorSettings.m_selectedRagdollColliderColor,
  290. renderActorSettings.m_hoveredRagdollColliderColor,
  291. renderActorSettings.m_violatedJointLimitColor });
  292. }
  293. }
  294. float AtomActorDebugDraw::CalculateScaleMultiplier(EMotionFX::ActorInstance* instance) const
  295. {
  296. const AZ::Aabb aabb = instance->GetAabb();
  297. const float aabbRadius = aabb.GetExtents().GetLength() * 0.5f;
  298. // Scale the multiplier down to 1% of the character size, that looks pretty nice on most of the models.
  299. return aabbRadius * 0.01f;
  300. }
  301. float AtomActorDebugDraw::CalculateBoneScale(EMotionFX::ActorInstance* actorInstance, EMotionFX::Node* node)
  302. {
  303. // Get the transform data
  304. EMotionFX::TransformData* transformData = actorInstance->GetTransformData();
  305. const EMotionFX::Pose* pose = transformData->GetCurrentPose();
  306. const size_t nodeIndex = node->GetNodeIndex();
  307. const size_t parentIndex = node->GetParentIndex();
  308. const AZ::Vector3 nodeWorldPos = pose->GetWorldSpaceTransform(nodeIndex).m_position;
  309. if (parentIndex != InvalidIndex)
  310. {
  311. const AZ::Vector3 parentWorldPos = pose->GetWorldSpaceTransform(parentIndex).m_position;
  312. const AZ::Vector3 bone = parentWorldPos - nodeWorldPos;
  313. const float boneLength = bone.GetLengthEstimate();
  314. // 10% of the bone length is the sphere size
  315. return boneLength * 0.1f;
  316. }
  317. return 0.0f;
  318. }
  319. void AtomActorDebugDraw::PrepareForMesh(EMotionFX::Mesh* mesh, const AZ::Transform& worldTM)
  320. {
  321. // Check if we have already prepared for the given mesh
  322. if (m_currentMesh == mesh)
  323. {
  324. return;
  325. }
  326. // Set our new current mesh
  327. m_currentMesh = mesh;
  328. // Get the number of vertices and the data
  329. const uint32 numVertices = m_currentMesh->GetNumVertices();
  330. AZ::Vector3* positions = (AZ::Vector3*)m_currentMesh->FindVertexData(EMotionFX::Mesh::ATTRIB_POSITIONS);
  331. // Check if the vertices fits in our buffer
  332. if (m_worldSpacePositions.size() < numVertices)
  333. {
  334. m_worldSpacePositions.resize(numVertices);
  335. }
  336. // Pre-calculate the world space positions
  337. for (uint32 i = 0; i < numVertices; ++i)
  338. {
  339. m_worldSpacePositions[i] = worldTM.TransformPoint(positions[i]);
  340. }
  341. }
  342. AzFramework::DebugDisplayRequests* AtomActorDebugDraw::GetDebugDisplay(AzFramework::ViewportId viewportId)
  343. {
  344. AzFramework::DebugDisplayRequestBus::BusPtr debugDisplayBus;
  345. AzFramework::DebugDisplayRequestBus::Bind(debugDisplayBus, viewportId);
  346. return AzFramework::DebugDisplayRequestBus::FindFirstHandler(debugDisplayBus);
  347. }
  348. void AtomActorDebugDraw::RenderAABB(EMotionFX::ActorInstance* instance,
  349. bool enableNodeAabb,
  350. const AZ::Color& nodeAabbColor,
  351. bool enableMeshAabb,
  352. const AZ::Color& meshAabbColor,
  353. bool enableStaticAabb,
  354. const AZ::Color& staticAabbColor)
  355. {
  356. RPI::AuxGeomDrawPtr auxGeom = m_auxGeomFeatureProcessor->GetDrawQueue();
  357. if (enableNodeAabb)
  358. {
  359. AZ::Aabb aabb;
  360. instance->CalcNodeBasedAabb(&aabb);
  361. EMotionFX::ActorInstance::ExpandBounds(aabb, instance->GetExpandBoundsBy());
  362. if (aabb.IsValid())
  363. {
  364. auxGeom->DrawAabb(aabb, nodeAabbColor, RPI::AuxGeomDraw::DrawStyle::Line);
  365. }
  366. }
  367. if (enableMeshAabb)
  368. {
  369. AZ::Aabb aabb;
  370. const size_t lodLevel = instance->GetLODLevel();
  371. instance->CalcMeshBasedAabb(lodLevel, &aabb);
  372. EMotionFX::ActorInstance::ExpandBounds(aabb, instance->GetExpandBoundsBy());
  373. if (aabb.IsValid())
  374. {
  375. auxGeom->DrawAabb(aabb, meshAabbColor, RPI::AuxGeomDraw::DrawStyle::Line);
  376. }
  377. }
  378. if (enableStaticAabb)
  379. {
  380. auto& aabb = instance->GetAabb();
  381. if (aabb.IsValid())
  382. {
  383. auxGeom->DrawAabb(aabb, staticAabbColor, RPI::AuxGeomDraw::DrawStyle::Line);
  384. }
  385. }
  386. }
  387. AZ::Color AtomActorDebugDraw::GetModifiedColor(
  388. const AZ::Color& color,
  389. size_t jointIndex,
  390. const AZStd::unordered_set<size_t>* cachedSelectedJointIndices,
  391. size_t cachedHoveredJointIndex) const
  392. {
  393. if (cachedSelectedJointIndices && cachedSelectedJointIndices->find(jointIndex) != cachedSelectedJointIndices->end())
  394. {
  395. return SelectedColor;
  396. }
  397. else if (cachedHoveredJointIndex == jointIndex)
  398. {
  399. return HoveredColor;
  400. }
  401. else
  402. {
  403. return color;
  404. }
  405. }
  406. void AtomActorDebugDraw::RenderLineSkeleton(AzFramework::DebugDisplayRequests* debugDisplay, EMotionFX::ActorInstance* instance, const AZ::Color& color) const
  407. {
  408. const EMotionFX::TransformData* transformData = instance->GetTransformData();
  409. const EMotionFX::Skeleton* skeleton = instance->GetActor()->GetSkeleton();
  410. const EMotionFX::Pose* pose = transformData->GetCurrentPose();
  411. const size_t lodLevel = instance->GetLODLevel();
  412. const size_t numJoints = skeleton->GetNumNodes();
  413. const AZStd::unordered_set<size_t>* cachedSelectedJointIndices;
  414. EMotionFX::JointSelectionRequestBus::BroadcastResult(
  415. cachedSelectedJointIndices, &EMotionFX::JointSelectionRequests::FindSelectedJointIndices, instance);
  416. size_t cachedHoveredJointIndex = InvalidIndex;
  417. EMotionFX::JointSelectionRequestBus::BroadcastResult(
  418. cachedHoveredJointIndex, &EMotionFX::JointSelectionRequests::FindHoveredJointIndex, instance);
  419. const AZ::u32 oldState = debugDisplay->GetState();
  420. debugDisplay->DepthTestOff();
  421. AZ::Color renderColor;
  422. for (size_t jointIndex = 0; jointIndex < numJoints; ++jointIndex)
  423. {
  424. const EMotionFX::Node* joint = skeleton->GetNode(jointIndex);
  425. if (!joint->GetSkeletalLODStatus(lodLevel))
  426. {
  427. continue;
  428. }
  429. const size_t parentIndex = joint->GetParentIndex();
  430. if (parentIndex == InvalidIndex)
  431. {
  432. continue;
  433. }
  434. renderColor = GetModifiedColor(color, parentIndex, cachedSelectedJointIndices, cachedHoveredJointIndex);
  435. const AZ::Vector3 parentPos = pose->GetWorldSpaceTransform(parentIndex).m_position;
  436. const AZ::Vector3 bonePos = pose->GetWorldSpaceTransform(jointIndex).m_position;
  437. debugDisplay->SetColor(renderColor);
  438. debugDisplay->DrawLine(parentPos, bonePos);
  439. }
  440. debugDisplay->SetState(oldState);
  441. }
  442. void AtomActorDebugDraw::RenderSkeleton(AzFramework::DebugDisplayRequests* debugDisplay, EMotionFX::ActorInstance* instance, const AZ::Color& color)
  443. {
  444. const EMotionFX::TransformData* transformData = instance->GetTransformData();
  445. const EMotionFX::Skeleton* skeleton = instance->GetActor()->GetSkeleton();
  446. const EMotionFX::Pose* pose = transformData->GetCurrentPose();
  447. const size_t numEnabled = instance->GetNumEnabledNodes();
  448. const AZStd::unordered_set<size_t>* cachedSelectedJointIndices;
  449. EMotionFX::JointSelectionRequestBus::BroadcastResult(
  450. cachedSelectedJointIndices, &EMotionFX::JointSelectionRequests::FindSelectedJointIndices, instance);
  451. size_t cachedHoveredJointIndex = InvalidIndex;
  452. EMotionFX::JointSelectionRequestBus::BroadcastResult(
  453. cachedHoveredJointIndex, &EMotionFX::JointSelectionRequests::FindHoveredJointIndex, instance);
  454. const AZ::u32 oldState = debugDisplay->GetState();
  455. debugDisplay->DepthTestOff();
  456. AZ::Color renderColor;
  457. for (size_t i = 0; i < numEnabled; ++i)
  458. {
  459. EMotionFX::Node* joint = skeleton->GetNode(instance->GetEnabledNode(i));
  460. const size_t jointIndex = joint->GetNodeIndex();
  461. const size_t parentIndex = joint->GetParentIndex();
  462. // check if this node has a parent and is a bone, if not skip it
  463. if (parentIndex == InvalidIndex)
  464. {
  465. continue;
  466. }
  467. const AZ::Vector3 nodeWorldPos = pose->GetWorldSpaceTransform(jointIndex).m_position;
  468. const AZ::Vector3 parentWorldPos = pose->GetWorldSpaceTransform(parentIndex).m_position;
  469. const AZ::Vector3 bone = parentWorldPos - nodeWorldPos;
  470. const AZ::Vector3 boneDirection = bone.GetNormalizedEstimate();
  471. const AZ::Vector3 centerWorldPos = bone / 2 + nodeWorldPos;
  472. const float maxBoneScale = 0.05f;
  473. const float boneLength = bone.GetLengthEstimate();
  474. const float boneScale = AZStd::min(CalculateBoneScale(instance, joint), maxBoneScale);
  475. const float parentBoneScale = AZStd::min(CalculateBoneScale(instance, skeleton->GetNode(parentIndex)), maxBoneScale);
  476. const float cylinderSize = boneLength - boneScale - parentBoneScale;
  477. renderColor = GetModifiedColor(color, parentIndex, cachedSelectedJointIndices, cachedHoveredJointIndex);
  478. renderColor.SetA(0.5f);
  479. debugDisplay->SetColor(renderColor);
  480. // Render the bone cylinder, the cylinder will be directed towards the node's parent and must fit between the spheres
  481. debugDisplay->DrawSolidCylinder(centerWorldPos, boneDirection, boneScale, cylinderSize);
  482. debugDisplay->DrawBall(nodeWorldPos, boneScale);
  483. }
  484. debugDisplay->SetState(oldState);
  485. }
  486. void AtomActorDebugDraw::RenderEMFXDebugDraw(EMotionFX::ActorInstance* instance)
  487. {
  488. RPI::AuxGeomDrawPtr auxGeom = m_auxGeomFeatureProcessor->GetDrawQueue();
  489. EMotionFX::DebugDraw& debugDraw = EMotionFX::GetDebugDraw();
  490. debugDraw.Lock();
  491. EMotionFX::DebugDraw::ActorInstanceData* actorInstanceData = debugDraw.GetActorInstanceData(instance);
  492. actorInstanceData->Lock();
  493. const AZStd::vector<EMotionFX::DebugDraw::Line>& lines = actorInstanceData->GetLines();
  494. if (lines.empty())
  495. {
  496. actorInstanceData->Unlock();
  497. debugDraw.Unlock();
  498. return;
  499. }
  500. m_auxVertices.clear();
  501. m_auxVertices.reserve(lines.size() * 2);
  502. m_auxColors.clear();
  503. m_auxColors.reserve(m_auxVertices.size());
  504. for (const EMotionFX::DebugDraw::Line& line : actorInstanceData->GetLines())
  505. {
  506. m_auxVertices.emplace_back(line.m_start);
  507. m_auxColors.emplace_back(line.m_startColor);
  508. m_auxVertices.emplace_back(line.m_end);
  509. m_auxColors.emplace_back(line.m_endColor);
  510. }
  511. AZ_Assert(m_auxVertices.size() == m_auxColors.size(), "Number of vertices and number of colors need to match.");
  512. actorInstanceData->Unlock();
  513. debugDraw.Unlock();
  514. RPI::AuxGeomDraw::AuxGeomDynamicDrawArguments lineArgs;
  515. lineArgs.m_verts = m_auxVertices.data();
  516. lineArgs.m_vertCount = aznumeric_cast<uint32_t>(m_auxVertices.size());
  517. lineArgs.m_colors = m_auxColors.data();
  518. lineArgs.m_colorCount = aznumeric_cast<uint32_t>(m_auxColors.size());
  519. lineArgs.m_depthTest = RPI::AuxGeomDraw::DepthTest::Off;
  520. auxGeom->DrawLines(lineArgs);
  521. }
  522. void AtomActorDebugDraw::RenderNormals(
  523. EMotionFX::Mesh* mesh,
  524. const AZ::Transform& worldTM,
  525. bool vertexNormals,
  526. bool faceNormals,
  527. float vertexNormalsScale,
  528. float faceNormalsScale,
  529. float scaleMultiplier,
  530. const AZ::Color& vertexNormalsColor,
  531. const AZ::Color& faceNormalsColor)
  532. {
  533. if (!mesh)
  534. {
  535. return;
  536. }
  537. if (!vertexNormals && !faceNormals)
  538. {
  539. return;
  540. }
  541. RPI::AuxGeomDrawPtr auxGeom = m_auxGeomFeatureProcessor->GetDrawQueue();
  542. if (!auxGeom)
  543. {
  544. return;
  545. }
  546. PrepareForMesh(mesh, worldTM);
  547. AZ::Vector3* normals = (AZ::Vector3*)mesh->FindVertexData(EMotionFX::Mesh::ATTRIB_NORMALS);
  548. // Render face normals
  549. if (faceNormals)
  550. {
  551. const size_t numSubMeshes = mesh->GetNumSubMeshes();
  552. for (uint32 subMeshIndex = 0; subMeshIndex < numSubMeshes; ++subMeshIndex)
  553. {
  554. EMotionFX::SubMesh* subMesh = mesh->GetSubMesh(subMeshIndex);
  555. const uint32 numTriangles = subMesh->GetNumPolygons();
  556. const uint32 startVertex = subMesh->GetStartVertex();
  557. const uint32* indices = subMesh->GetIndices();
  558. m_auxVertices.clear();
  559. m_auxVertices.reserve(numTriangles * 2);
  560. for (uint32 triangleIndex = 0; triangleIndex < numTriangles; ++triangleIndex)
  561. {
  562. const uint32 triangleStartIndex = triangleIndex * 3;
  563. const uint32 indexA = indices[triangleStartIndex + 0] + startVertex;
  564. const uint32 indexB = indices[triangleStartIndex + 1] + startVertex;
  565. const uint32 indexC = indices[triangleStartIndex + 2] + startVertex;
  566. const AZ::Vector3& posA = m_worldSpacePositions[indexA];
  567. const AZ::Vector3& posB = m_worldSpacePositions[indexB];
  568. const AZ::Vector3& posC = m_worldSpacePositions[indexC];
  569. const AZ::Vector3 normalDir = (posB - posA).Cross(posC - posA).GetNormalized();
  570. // Calculate the center pos
  571. const AZ::Vector3 normalPos = (posA + posB + posC) * (1.0f / 3.0f);
  572. m_auxVertices.emplace_back(normalPos);
  573. m_auxVertices.emplace_back(normalPos + (normalDir * faceNormalsScale * scaleMultiplier));
  574. }
  575. RPI::AuxGeomDraw::AuxGeomDynamicDrawArguments lineArgs;
  576. lineArgs.m_verts = m_auxVertices.data();
  577. lineArgs.m_vertCount = aznumeric_cast<uint32_t>(m_auxVertices.size());
  578. lineArgs.m_colors = &faceNormalsColor;
  579. lineArgs.m_colorCount = 1;
  580. lineArgs.m_depthTest = RPI::AuxGeomDraw::DepthTest::On;
  581. auxGeom->DrawLines(lineArgs);
  582. }
  583. }
  584. // render vertex normals
  585. if (vertexNormals)
  586. {
  587. const size_t numSubMeshes = mesh->GetNumSubMeshes();
  588. for (uint32 subMeshIndex = 0; subMeshIndex < numSubMeshes; ++subMeshIndex)
  589. {
  590. EMotionFX::SubMesh* subMesh = mesh->GetSubMesh(subMeshIndex);
  591. const uint32 numVertices = subMesh->GetNumVertices();
  592. const uint32 startVertex = subMesh->GetStartVertex();
  593. m_auxVertices.clear();
  594. m_auxVertices.reserve(numVertices * 2);
  595. for (uint32 j = 0; j < numVertices; ++j)
  596. {
  597. const uint32 vertexIndex = j + startVertex;
  598. const AZ::Vector3& position = m_worldSpacePositions[vertexIndex];
  599. const AZ::Vector3 normal = worldTM.TransformVector(normals[vertexIndex]).GetNormalizedSafe() *
  600. vertexNormalsScale * scaleMultiplier;
  601. m_auxVertices.emplace_back(position);
  602. m_auxVertices.emplace_back(position + normal);
  603. }
  604. RPI::AuxGeomDraw::AuxGeomDynamicDrawArguments lineArgs;
  605. lineArgs.m_verts = m_auxVertices.data();
  606. lineArgs.m_vertCount = aznumeric_cast<uint32_t>(m_auxVertices.size());
  607. lineArgs.m_colors = &vertexNormalsColor;
  608. lineArgs.m_colorCount = 1;
  609. lineArgs.m_depthTest = RPI::AuxGeomDraw::DepthTest::On;
  610. auxGeom->DrawLines(lineArgs);
  611. }
  612. }
  613. }
  614. void AtomActorDebugDraw::RenderTangents(
  615. EMotionFX::Mesh* mesh,
  616. const AZ::Transform& worldTM,
  617. float tangentsScale,
  618. float scaleMultiplier,
  619. const AZ::Color& tangentsColor,
  620. const AZ::Color& mirroredBitangentsColor,
  621. const AZ::Color& bitangentsColor)
  622. {
  623. if (!mesh)
  624. {
  625. return;
  626. }
  627. RPI::AuxGeomDrawPtr auxGeom = m_auxGeomFeatureProcessor->GetDrawQueue();
  628. if (!auxGeom)
  629. {
  630. return;
  631. }
  632. // Get the tangents and check if this mesh actually has tangents
  633. AZ::Vector4* tangents = static_cast<AZ::Vector4*>(mesh->FindVertexData(EMotionFX::Mesh::ATTRIB_TANGENTS));
  634. if (!tangents)
  635. {
  636. return;
  637. }
  638. AZ::Vector3* bitangents = static_cast<AZ::Vector3*>(mesh->FindVertexData(EMotionFX::Mesh::ATTRIB_BITANGENTS));
  639. PrepareForMesh(mesh, worldTM);
  640. AZ::Vector3* normals = (AZ::Vector3*)mesh->FindVertexData(EMotionFX::Mesh::ATTRIB_NORMALS);
  641. const uint32 numVertices = mesh->GetNumVertices();
  642. m_auxVertices.clear();
  643. m_auxVertices.reserve(numVertices * 2);
  644. m_auxColors.clear();
  645. m_auxColors.reserve(m_auxVertices.size());
  646. // Render the tangents and bitangents
  647. AZ::Vector3 orgTangent, tangent, bitangent;
  648. for (uint32 i = 0; i < numVertices; ++i)
  649. {
  650. orgTangent.Set(tangents[i].GetX(), tangents[i].GetY(), tangents[i].GetZ());
  651. tangent = (worldTM.TransformVector(orgTangent)).GetNormalized();
  652. if (bitangents)
  653. {
  654. bitangent = bitangents[i];
  655. }
  656. else
  657. {
  658. bitangent = tangents[i].GetW() * normals[i].Cross(orgTangent);
  659. }
  660. bitangent = (worldTM.TransformVector(bitangent)).GetNormalizedSafe();
  661. m_auxVertices.emplace_back(m_worldSpacePositions[i]);
  662. m_auxColors.emplace_back(tangentsColor);
  663. m_auxVertices.emplace_back(m_worldSpacePositions[i] + (tangent * tangentsScale * scaleMultiplier));
  664. m_auxColors.emplace_back(tangentsColor);
  665. if (tangents[i].GetW() < 0.0f)
  666. {
  667. m_auxVertices.emplace_back(m_worldSpacePositions[i]);
  668. m_auxColors.emplace_back(mirroredBitangentsColor);
  669. m_auxVertices.emplace_back(m_worldSpacePositions[i] + (bitangent * tangentsScale * scaleMultiplier));
  670. m_auxColors.emplace_back(mirroredBitangentsColor);
  671. }
  672. else
  673. {
  674. m_auxVertices.emplace_back(m_worldSpacePositions[i]);
  675. m_auxColors.emplace_back(bitangentsColor);
  676. m_auxVertices.emplace_back(m_worldSpacePositions[i] + (bitangent * tangentsScale * scaleMultiplier));
  677. m_auxColors.emplace_back(bitangentsColor);
  678. }
  679. }
  680. RPI::AuxGeomDraw::AuxGeomDynamicDrawArguments lineArgs;
  681. lineArgs.m_verts = m_auxVertices.data();
  682. lineArgs.m_vertCount = aznumeric_cast<uint32_t>(m_auxVertices.size());
  683. lineArgs.m_colors = m_auxColors.data();
  684. lineArgs.m_colorCount = aznumeric_cast<uint32_t>(m_auxColors.size());
  685. lineArgs.m_depthTest = RPI::AuxGeomDraw::DepthTest::On;
  686. auxGeom->DrawLines(lineArgs);
  687. }
  688. void AtomActorDebugDraw::RenderWireframe(EMotionFX::Mesh* mesh, const AZ::Transform& worldTM,
  689. float scale, const AZ::Color& color)
  690. {
  691. // Check if the mesh is valid and skip the node in case it's not
  692. if (!mesh)
  693. {
  694. return;
  695. }
  696. RPI::AuxGeomDrawPtr auxGeom = m_auxGeomFeatureProcessor->GetDrawQueue();
  697. if (!auxGeom)
  698. {
  699. return;
  700. }
  701. PrepareForMesh(mesh, worldTM);
  702. const AZ::Vector3* normals = (AZ::Vector3*)mesh->FindVertexData(EMotionFX::Mesh::ATTRIB_NORMALS);
  703. const size_t numSubMeshes = mesh->GetNumSubMeshes();
  704. for (uint32 subMeshIndex = 0; subMeshIndex < numSubMeshes; ++subMeshIndex)
  705. {
  706. EMotionFX::SubMesh* subMesh = mesh->GetSubMesh(subMeshIndex);
  707. const uint32 numTriangles = subMesh->GetNumPolygons();
  708. const uint32 startVertex = subMesh->GetStartVertex();
  709. const uint32* indices = subMesh->GetIndices();
  710. m_auxVertices.clear();
  711. m_auxVertices.reserve(numTriangles * 6);
  712. for (uint32 triangleIndex = 0; triangleIndex < numTriangles; ++triangleIndex)
  713. {
  714. const uint32 triangleStartIndex = triangleIndex * 3;
  715. const uint32 indexA = indices[triangleStartIndex + 0] + startVertex;
  716. const uint32 indexB = indices[triangleStartIndex + 1] + startVertex;
  717. const uint32 indexC = indices[triangleStartIndex + 2] + startVertex;
  718. const AZ::Vector3 posA = m_worldSpacePositions[indexA] + normals[indexA] * scale;
  719. const AZ::Vector3 posB = m_worldSpacePositions[indexB] + normals[indexB] * scale;
  720. const AZ::Vector3 posC = m_worldSpacePositions[indexC] + normals[indexC] * scale;
  721. m_auxVertices.emplace_back(posA);
  722. m_auxVertices.emplace_back(posB);
  723. m_auxVertices.emplace_back(posB);
  724. m_auxVertices.emplace_back(posC);
  725. m_auxVertices.emplace_back(posC);
  726. m_auxVertices.emplace_back(posA);
  727. }
  728. RPI::AuxGeomDraw::AuxGeomDynamicDrawArguments lineArgs;
  729. lineArgs.m_verts = m_auxVertices.data();
  730. lineArgs.m_vertCount = aznumeric_cast<uint32_t>(m_auxVertices.size());
  731. lineArgs.m_colors = &color;
  732. lineArgs.m_colorCount = 1;
  733. lineArgs.m_depthTest = RPI::AuxGeomDraw::DepthTest::On;
  734. auxGeom->DrawLines(lineArgs);
  735. }
  736. }
  737. void AtomActorDebugDraw::RenderJointNames(EMotionFX::ActorInstance* actorInstance,
  738. RPI::ViewportContextPtr viewportContext, const AZ::Color& jointNameColor)
  739. {
  740. if (!m_fontDrawInterface)
  741. {
  742. auto fontQueryInterface = AZ::Interface<AzFramework::FontQueryInterface>::Get();
  743. if (!fontQueryInterface)
  744. {
  745. return;
  746. }
  747. m_fontDrawInterface = fontQueryInterface->GetDefaultFontDrawInterface();
  748. }
  749. if (!m_fontDrawInterface || !viewportContext || !viewportContext->GetRenderScene() ||
  750. !AZ::Interface<AzFramework::FontQueryInterface>::Get())
  751. {
  752. return;
  753. }
  754. const AZStd::unordered_set<size_t>* cachedSelectedJointIndices;
  755. EMotionFX::JointSelectionRequestBus::BroadcastResult(
  756. cachedSelectedJointIndices, &EMotionFX::JointSelectionRequests::FindSelectedJointIndices, actorInstance);
  757. const EMotionFX::Actor* actor = actorInstance->GetActor();
  758. const EMotionFX::Skeleton* skeleton = actor->GetSkeleton();
  759. const EMotionFX::TransformData* transformData = actorInstance->GetTransformData();
  760. const EMotionFX::Pose* pose = transformData->GetCurrentPose();
  761. const size_t numEnabledNodes = actorInstance->GetNumEnabledNodes();
  762. m_drawParams.m_drawViewportId = viewportContext->GetId();
  763. AzFramework::WindowSize viewportSize = viewportContext->GetViewportSize();
  764. m_drawParams.m_position = AZ::Vector3(aznumeric_cast<float>(viewportSize.m_width), 0.0f, 1.0f) +
  765. TopRightBorderPadding * viewportContext->GetDpiScalingFactor();
  766. m_drawParams.m_scale = AZ::Vector2(BaseFontSize);
  767. m_drawParams.m_hAlign = AzFramework::TextHorizontalAlignment::Right;
  768. m_drawParams.m_monospace = false;
  769. m_drawParams.m_depthTest = false;
  770. m_drawParams.m_virtual800x600ScreenSize = false;
  771. m_drawParams.m_scaleWithWindow = false;
  772. m_drawParams.m_multiline = true;
  773. m_drawParams.m_lineSpacing = 0.5f;
  774. for (size_t i = 0; i < numEnabledNodes; ++i)
  775. {
  776. const EMotionFX::Node* joint = skeleton->GetNode(actorInstance->GetEnabledNode(i));
  777. const size_t jointIndex = joint->GetNodeIndex();
  778. const AZ::Vector3 worldPos = pose->GetWorldSpaceTransform(jointIndex).m_position;
  779. m_drawParams.m_position = worldPos;
  780. if (cachedSelectedJointIndices && cachedSelectedJointIndices->find(jointIndex) != cachedSelectedJointIndices->end())
  781. {
  782. m_drawParams.m_color = SelectedColor;
  783. }
  784. else
  785. {
  786. m_drawParams.m_color = jointNameColor;
  787. }
  788. m_fontDrawInterface->DrawScreenAlignedText3d(m_drawParams, joint->GetName());
  789. }
  790. }
  791. void AtomActorDebugDraw::RenderNodeOrientations(EMotionFX::ActorInstance* actorInstance,
  792. AzFramework::DebugDisplayRequests* debugDisplay, float scale)
  793. {
  794. // Get the actor and the transform data
  795. const float unitScale =
  796. 1.0f / (float)MCore::Distance::ConvertValue(1.0f, MCore::Distance::UNITTYPE_METERS, EMotionFX::GetEMotionFX().GetUnitType());
  797. const EMotionFX::Actor* actor = actorInstance->GetActor();
  798. const EMotionFX::Skeleton* skeleton = actor->GetSkeleton();
  799. const EMotionFX::TransformData* transformData = actorInstance->GetTransformData();
  800. const EMotionFX::Pose* pose = transformData->GetCurrentPose();
  801. const float constPreScale = scale * unitScale * 3.0f;
  802. const AZStd::unordered_set<size_t>* cachedSelectedJointIndices;
  803. EMotionFX::JointSelectionRequestBus::BroadcastResult(
  804. cachedSelectedJointIndices, &EMotionFX::JointSelectionRequests::FindSelectedJointIndices, actorInstance);
  805. const int oldState = debugDisplay->GetState();
  806. debugDisplay->DepthTestOff();
  807. const size_t numEnabled = actorInstance->GetNumEnabledNodes();
  808. for (size_t i = 0; i < numEnabled; ++i)
  809. {
  810. EMotionFX::Node* joint = skeleton->GetNode(actorInstance->GetEnabledNode(i));
  811. const size_t jointIndex = joint->GetNodeIndex();
  812. static const float axisBoneScale = 50.0f;
  813. const float size = CalculateBoneScale(actorInstance, joint) * constPreScale * axisBoneScale;
  814. AZ::Transform worldTM = pose->GetWorldSpaceTransform(jointIndex).ToAZTransform();
  815. bool selected = false;
  816. if (cachedSelectedJointIndices && cachedSelectedJointIndices->find(jointIndex) != cachedSelectedJointIndices->end())
  817. {
  818. selected = true;
  819. }
  820. RenderLineAxis(debugDisplay, worldTM, size, selected);
  821. }
  822. debugDisplay->SetState(oldState);
  823. }
  824. void AtomActorDebugDraw::UpdateActorInstance(EMotionFX::ActorInstance* actorInstance, float deltaTime)
  825. {
  826. // Find the corresponding trajectory trace path for the given actor instance
  827. auto trajectoryPath = FindTrajectoryPath(actorInstance);
  828. if (!trajectoryPath)
  829. {
  830. return;
  831. }
  832. const EMotionFX::Actor* actor = actorInstance->GetActor();
  833. const EMotionFX::Node* motionExtractionNode = actor->GetMotionExtractionNode();
  834. const uint32 particleSampleRate = 30;
  835. const float minLengthEstimate = 0.0001f;
  836. const float maxdeltaRot = 0.99;
  837. const uint32 maxNumberParticles = 50;
  838. if (motionExtractionNode)
  839. {
  840. const EMotionFX::Transform& worldTM = actorInstance->GetWorldSpaceTransform();
  841. // Add a particle to the trajectory path once we travel certain distance.
  842. bool distanceTravelledEnough = false;
  843. if (trajectoryPath->m_traceParticles.empty())
  844. {
  845. distanceTravelledEnough = true;
  846. }
  847. else
  848. {
  849. const size_t numParticles = trajectoryPath->m_traceParticles.size();
  850. const EMotionFX::Transform& oldWorldTM = trajectoryPath->m_traceParticles[numParticles - 1].m_worldTm;
  851. const AZ::Vector3& oldPos = oldWorldTM.m_position;
  852. const AZ::Quaternion oldRot = oldWorldTM.m_rotation.GetNormalized();
  853. const AZ::Quaternion rotation = worldTM.m_rotation.GetNormalized();
  854. const AZ::Vector3 deltaPos = worldTM.m_position - oldPos;
  855. const float deltaRot = AZStd::abs(rotation.Dot(oldRot));
  856. if (deltaPos.GetLengthEstimate() > minLengthEstimate || deltaRot < maxdeltaRot)
  857. {
  858. distanceTravelledEnough = true;
  859. }
  860. }
  861. // Add the time delta to the time passed since the last add
  862. trajectoryPath->m_timePassed += deltaTime;
  863. if (trajectoryPath->m_timePassed >= (1.0f / particleSampleRate) && distanceTravelledEnough)
  864. {
  865. // Create the particle, fill its data and add it to the trajectory trace path
  866. TrajectoryPathParticle trajectoryParticle;
  867. trajectoryParticle.m_worldTm = worldTM;
  868. trajectoryPath->m_traceParticles.emplace_back(trajectoryParticle);
  869. // Reset the time passed as we just added a new particle
  870. trajectoryPath->m_timePassed = 0.0f;
  871. }
  872. }
  873. // Make sure we don't have too many items in our array
  874. if (trajectoryPath->m_traceParticles.size() > maxNumberParticles)
  875. {
  876. trajectoryPath->m_traceParticles.erase(begin(trajectoryPath->m_traceParticles));
  877. }
  878. }
  879. void AtomActorDebugDraw::RenderLineAxis(
  880. AzFramework::DebugDisplayRequests* debugDisplay,
  881. AZ::Transform worldTM,
  882. float size,
  883. bool selected,
  884. bool renderAxisName)
  885. {
  886. const float axisHeight = size * 0.7f;
  887. const float frontSize = size * 5.0f + 0.2f;
  888. const AZ::Vector3 position = worldTM.GetTranslation();
  889. // Render x axis
  890. {
  891. AZ::Color xSelectedColor = selected ? AZ::Colors::Orange : AZ::Colors::Red;
  892. const AZ::Vector3 xAxisDir = (worldTM.TransformPoint(AZ::Vector3(size, 0.0f, 0.0f)) - position).GetNormalized();
  893. const AZ::Vector3 xAxisArrowStart = position + xAxisDir * axisHeight;
  894. debugDisplay->SetColor(xSelectedColor);
  895. debugDisplay->DrawArrow(position, xAxisArrowStart, size);
  896. if (renderAxisName)
  897. {
  898. const AZ::Vector3 xNamePos = position + xAxisDir * (size * 1.15f);
  899. debugDisplay->DrawTextLabel(xNamePos, frontSize, "X");
  900. }
  901. }
  902. // Render y axis
  903. {
  904. AZ::Color ySelectedColor = selected ? AZ::Colors::Orange : AZ::Colors::Blue;
  905. const AZ::Vector3 yAxisDir = (worldTM.TransformPoint(AZ::Vector3(0.0f, size, 0.0f)) - position).GetNormalized();
  906. const AZ::Vector3 yAxisArrowStart = position + yAxisDir * axisHeight;
  907. debugDisplay->SetColor(ySelectedColor);
  908. debugDisplay->DrawArrow(position, yAxisArrowStart, size);
  909. if (renderAxisName)
  910. {
  911. const AZ::Vector3 yNamePos = position + yAxisDir * (size * 1.15f);
  912. debugDisplay->DrawTextLabel(yNamePos, frontSize, "Y");
  913. }
  914. }
  915. // Render z axis
  916. {
  917. AZ::Color zSelectedColor = selected ? AZ::Colors::Orange : AZ::Colors::Green;
  918. const AZ::Vector3 zAxisDir = (worldTM.TransformPoint(AZ::Vector3(0.0f, 0.0f, size)) - position).GetNormalized();
  919. const AZ::Vector3 zAxisArrowStart = position + zAxisDir * axisHeight;
  920. debugDisplay->SetColor(zSelectedColor);
  921. debugDisplay->DrawArrow(position, zAxisArrowStart, size);
  922. if (renderAxisName)
  923. {
  924. const AZ::Vector3 zNamePos = position + zAxisDir * (size * 1.15f);
  925. debugDisplay->DrawTextLabel(zNamePos, frontSize, "Z");
  926. }
  927. }
  928. }
  929. // Find the trajectory path for a given actor instance
  930. AtomActorDebugDraw::TrajectoryTracePath* AtomActorDebugDraw::FindTrajectoryPath(const EMotionFX::ActorInstance* actorInstance)
  931. {
  932. for (const auto& trajectoryPath : m_trajectoryTracePaths)
  933. {
  934. if (trajectoryPath->m_actorInstance == actorInstance)
  935. {
  936. return trajectoryPath.get();
  937. }
  938. }
  939. // We haven't created a path for the given actor instance yet, do so
  940. auto tracePath = AZStd::make_unique<TrajectoryTracePath>();
  941. tracePath->m_actorInstance = actorInstance;
  942. tracePath->m_traceParticles.reserve(512);
  943. return m_trajectoryTracePaths.emplace_back(AZStd::move(tracePath)).get();
  944. }
  945. void AtomActorDebugDraw::RenderTrajectoryPath(AzFramework::DebugDisplayRequests* debugDisplay,
  946. const EMotionFX::ActorInstance* actorInstance,
  947. const AZ::Color& headColor,
  948. const AZ::Color& pathColor)
  949. {
  950. auto trajectoryPath = FindTrajectoryPath(actorInstance);
  951. if (!trajectoryPath)
  952. {
  953. return;
  954. }
  955. EMotionFX::Actor* actor = actorInstance->GetActor();
  956. EMotionFX::Node* extractionNode = actor->GetMotionExtractionNode();
  957. if (!extractionNode)
  958. {
  959. return;
  960. }
  961. // Fast access to the trajectory trace particles
  962. const AZStd::vector<TrajectoryPathParticle>& traceParticles = trajectoryPath->m_traceParticles;
  963. if (traceParticles.empty())
  964. {
  965. return;
  966. }
  967. const size_t numTraceParticles = traceParticles.size();
  968. const float trailWidthHalf = 0.25f;
  969. const float trailLength = 2.0f;
  970. const float arrowWidthHalf = 0.75f;
  971. const float arrowLength = 1.5f;
  972. const AZ::Vector3 liftFromGround(0.0f, 0.0f, 0.0001f);
  973. const AZ::Transform trajectoryWorldTM = actorInstance->GetWorldSpaceTransform().ToAZTransform();
  974. //////////////////////////////////////////////////////////////////////////////////////////////////////
  975. // Render arrow head
  976. //////////////////////////////////////////////////////////////////////////////////////////////////////
  977. // Get the position and some direction vectors of the trajectory node matrix
  978. EMotionFX::Transform worldTM = traceParticles[numTraceParticles - 1].m_worldTm;
  979. const AZ::Vector3 right = trajectoryWorldTM.GetBasisX().GetNormalized();
  980. const AZ::Vector3 center = trajectoryWorldTM.GetTranslation();
  981. const AZ::Vector3 forward = trajectoryWorldTM.GetBasisY().GetNormalized();
  982. const AZ::Vector3 up(0.0f, 0.0f, 1.0f);
  983. AZ::Vector3 vertices[7];
  984. AZ::Vector3 oldLeft, oldRight;
  985. /*
  986. 4
  987. / \
  988. / \
  989. / \
  990. / \
  991. / \
  992. 5-----6 2-----3
  993. | |
  994. | |
  995. | |
  996. | |
  997. | |
  998. 0-------1
  999. */
  1000. // Construct the arrow vertices
  1001. float scale = 0.2f;
  1002. vertices[0] = center + (-right * trailWidthHalf - forward * trailLength) * scale;
  1003. vertices[1] = center + (right * trailWidthHalf - forward * trailLength) * scale;
  1004. vertices[2] = center + (right * trailWidthHalf) * scale;
  1005. vertices[3] = center + (right * arrowWidthHalf) * scale;
  1006. vertices[4] = center + (forward * arrowLength) * scale;
  1007. vertices[5] = center + (-right * arrowWidthHalf) * scale;
  1008. vertices[6] = center + (-right * trailWidthHalf) * scale;
  1009. oldLeft = vertices[6];
  1010. oldRight = vertices[2];
  1011. AZ::Vector3 arrowOldLeft = oldLeft;
  1012. AZ::Vector3 arrowOldRight = oldRight;
  1013. // Render the solid arrow
  1014. debugDisplay->SetColor(headColor);
  1015. debugDisplay->DrawTri(vertices[3] + liftFromGround, vertices[4] + liftFromGround, vertices[2] + liftFromGround);
  1016. debugDisplay->DrawTri(vertices[2] + liftFromGround, vertices[4] + liftFromGround, vertices[6] + liftFromGround);
  1017. debugDisplay->DrawTri(vertices[6] + liftFromGround, vertices[4] + liftFromGround, vertices[5] + liftFromGround);
  1018. //////////////////////////////////////////////////////////////////////////////////////////////////////
  1019. // Render arrow tail (actual path)
  1020. //////////////////////////////////////////////////////////////////////////////////////////////////////
  1021. AZ::Vector3 a, b;
  1022. AZ::Color color = pathColor;
  1023. // Render the path from the arrow head towards the tail
  1024. for (size_t i = numTraceParticles - 1; i > 0; i--)
  1025. {
  1026. // Calculate the normalized distance to the head, this value also represents the alpha value as it fades away while getting
  1027. // closer to the end
  1028. float normalizedDistance = (float)i / numTraceParticles;
  1029. // Get the start and end point of the line segment and calculate the delta between them
  1030. worldTM = traceParticles[i].m_worldTm;
  1031. a = worldTM.m_position;
  1032. b = traceParticles[i - 1].m_worldTm.m_position;
  1033. AZ::Vector3 particleRight = worldTM.ToAZTransform().GetBasisX().GetNormalized();
  1034. if (i > 1 && i < numTraceParticles - 3)
  1035. {
  1036. const AZ::Vector3 deltaA = traceParticles[i - 2].m_worldTm.m_position - traceParticles[i - 1].m_worldTm.m_position;
  1037. const AZ::Vector3 deltaB = traceParticles[i - 1].m_worldTm.m_position - traceParticles[i].m_worldTm.m_position;
  1038. const AZ::Vector3 deltaC = traceParticles[i].m_worldTm.m_position - traceParticles[i + 1].m_worldTm.m_position;
  1039. const AZ::Vector3 deltaD = traceParticles[i + 1].m_worldTm.m_position - traceParticles[i + 2].m_worldTm.m_position;
  1040. AZ::Vector3 delta = deltaA + deltaB + deltaC + deltaD;
  1041. delta = delta.GetNormalizedSafe();
  1042. particleRight = up.Cross(delta);
  1043. }
  1044. /*
  1045. // .
  1046. // .
  1047. // .
  1048. //(oldLeft) 0 a 1 (oldRight)
  1049. // | |
  1050. // | |
  1051. // | |
  1052. // | |
  1053. // | |
  1054. // 2---b---3
  1055. */
  1056. // Construct the arrow vertices
  1057. vertices[0] = oldLeft;
  1058. vertices[1] = oldRight;
  1059. vertices[2] = b + AZ::Vector3(-particleRight * trailWidthHalf) * scale;
  1060. vertices[3] = b + AZ::Vector3(particleRight * trailWidthHalf) * scale;
  1061. // Make sure we perfectly align with the arrow head
  1062. if (i == numTraceParticles - 1)
  1063. {
  1064. normalizedDistance = 1.0f;
  1065. vertices[0] = arrowOldLeft;
  1066. vertices[1] = arrowOldRight;
  1067. }
  1068. // Render the solid arrow
  1069. color.SetA(normalizedDistance);
  1070. debugDisplay->SetColor(color);
  1071. debugDisplay->DrawTri(vertices[0] + liftFromGround, vertices[2] + liftFromGround, vertices[1] + liftFromGround);
  1072. debugDisplay->DrawTri(vertices[1] + liftFromGround, vertices[2] + liftFromGround, vertices[3] + liftFromGround);
  1073. // Overwrite the old left and right values so that they can be used for the next trace particle
  1074. oldLeft = vertices[2];
  1075. oldRight = vertices[3];
  1076. }
  1077. }
  1078. void AtomActorDebugDraw::RenderRootMotion(AzFramework::DebugDisplayRequests* debugDisplay,
  1079. const EMotionFX::ActorInstance* actorInstance,
  1080. const AZ::Color& rootColor)
  1081. {
  1082. const AZ::Transform actorTransform = actorInstance->GetWorldSpaceTransform().ToAZTransform();
  1083. // Render two circle around the character position.
  1084. debugDisplay->SetColor(rootColor);
  1085. debugDisplay->DrawCircle(actorTransform.GetTranslation(), 1.0f);
  1086. debugDisplay->DrawCircle(actorTransform.GetTranslation(), 0.05f);
  1087. // Render the character facing direction.
  1088. const AZ::Vector3 forward = actorTransform.GetBasisY();
  1089. debugDisplay->DrawArrow(actorTransform.GetTranslation(), actorTransform.GetTranslation() + forward);
  1090. }
  1091. } // namespace AZ::Render