Picking.cpp 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  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 <AzCore/Math/IntersectSegment.h>
  9. #include <AzFramework/Physics/ShapeConfiguration.h>
  10. #include <AzToolsFramework/Viewport/ViewportTypes.h>
  11. #include <EMotionFX/Source/TransformData.h>
  12. #include <EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderOptions.h>
  13. #include <Editor/Picking.h>
  14. #include <Editor/Plugins/SkeletonOutliner/SkeletonOutlinerBus.h>
  15. #include <Editor/SkeletonModel.h>
  16. namespace EMotionFX
  17. {
  18. struct PickingIntersection
  19. {
  20. bool m_intersected = false;
  21. float m_distance = AZ::Constants::FloatMax;
  22. size_t m_jointIndex = 0;
  23. };
  24. void CompareIntersection(PickingIntersection& closestIntersection, float distance, size_t jointIndex)
  25. {
  26. if (distance < closestIntersection.m_distance)
  27. {
  28. closestIntersection.m_intersected = true;
  29. closestIntersection.m_distance = distance;
  30. closestIntersection.m_jointIndex = jointIndex;
  31. }
  32. }
  33. void IntersectLineSkeleton(
  34. PickingIntersection& closestIntersection,
  35. const AZ::Vector3& rayOrigin,
  36. const AZ::Vector3& rayDirection,
  37. const ActorInstance* actorInstance,
  38. const EMotionFX::Skeleton* skeleton)
  39. {
  40. const EMotionFX::TransformData* transformData = actorInstance->GetTransformData();
  41. const EMotionFX::Pose* pose = transformData->GetCurrentPose();
  42. const size_t lodLevel = actorInstance->GetLODLevel();
  43. const size_t numJoints = skeleton->GetNumNodes();
  44. for (size_t jointIndex = 0; jointIndex < numJoints; ++jointIndex)
  45. {
  46. const EMotionFX::Node* joint = skeleton->GetNode(jointIndex);
  47. if (!joint->GetSkeletalLODStatus(lodLevel))
  48. {
  49. continue;
  50. }
  51. const size_t parentIndex = joint->GetParentIndex();
  52. if (parentIndex == InvalidIndex)
  53. {
  54. continue;
  55. }
  56. const AZ::Vector3 parentPos = pose->GetWorldSpaceTransform(parentIndex).m_position;
  57. const AZ::Vector3 bonePos = pose->GetWorldSpaceTransform(jointIndex).m_position;
  58. const AZ::Vector3 boneDir = (parentPos - bonePos).GetNormalized();
  59. const float boneLength = (parentPos - bonePos).GetLength();
  60. float t1 = AZ::Constants::FloatMax;
  61. float t2 = AZ::Constants::FloatMax;
  62. int numIntersections =
  63. AZ::Intersect::IntersectRayCappedCylinder(rayOrigin, rayDirection, bonePos, boneDir, boneLength, PickingMargin, t1, t2);
  64. if (numIntersections > 0)
  65. {
  66. const float distance = (numIntersections == 1) ? t1 : AZ::GetMin(t1, t2);
  67. CompareIntersection(closestIntersection, distance, parentIndex);
  68. }
  69. }
  70. }
  71. void IntersectRagdollColliders(
  72. PickingIntersection& closestIntersection,
  73. const AZ::Vector3& rayOrigin,
  74. const AZ::Vector3& rayDirection,
  75. const ActorInstance* actorInstance
  76. )
  77. {
  78. Physics::CharacterColliderConfiguration* ragdollColliderConfiguration =
  79. actorInstance->GetActor()->GetPhysicsSetup()->GetColliderConfigByType(EMotionFX::PhysicsSetup::Ragdoll);
  80. for (const Physics::CharacterColliderNodeConfiguration& nodeConfig : ragdollColliderConfiguration->m_nodes)
  81. {
  82. const EMotionFX::Actor* actor = actorInstance->GetActor();
  83. const EMotionFX::Node* joint = actor->GetSkeleton()->FindNodeByName(nodeConfig.m_name.c_str());
  84. if (joint)
  85. {
  86. const size_t jointIndex = joint->GetNodeIndex();
  87. const EMotionFX::Transform& actorInstanceGlobalTransform = actorInstance->GetWorldSpaceTransform();
  88. const EMotionFX::Transform& emfxNodeGlobalTransform =
  89. actorInstance->GetTransformData()->GetCurrentPose()->GetModelSpaceTransform(jointIndex);
  90. const AZ::Transform worldTransform = (emfxNodeGlobalTransform * actorInstanceGlobalTransform).ToAZTransform();
  91. for (const AzPhysics::ShapeColliderPair& shapeColliderPair : nodeConfig.m_shapes)
  92. {
  93. const AZ::Transform colliderOffsetTransform = AZ::Transform::CreateFromQuaternionAndTranslation(
  94. shapeColliderPair.first->m_rotation, shapeColliderPair.first->m_position);
  95. const AZ::Transform colliderGlobalTransform = worldTransform * colliderOffsetTransform;
  96. const AZ::TypeId colliderType = shapeColliderPair.second->RTTI_GetType();
  97. if (colliderType == azrtti_typeid<Physics::SphereShapeConfiguration>())
  98. {
  99. auto* sphere = static_cast<Physics::SphereShapeConfiguration*>(shapeColliderPair.second.get());
  100. float distance = AZ::Constants::FloatMax;
  101. AZ::Intersect::SphereIsectTypes result = AZ::Intersect::IntersectRaySphere(
  102. rayOrigin, rayDirection, colliderGlobalTransform.GetTranslation(), sphere->m_radius, distance);
  103. if (result != AZ::Intersect::SphereIsectTypes::ISECT_RAY_SPHERE_NONE)
  104. {
  105. CompareIntersection(closestIntersection, distance, jointIndex);
  106. }
  107. }
  108. else if (colliderType == azrtti_typeid<Physics::CapsuleShapeConfiguration>())
  109. {
  110. auto* capsule = static_cast<Physics::CapsuleShapeConfiguration*>(shapeColliderPair.second.get());
  111. const AZ::Vector3 capsuleZ = colliderGlobalTransform.GetBasisZ();
  112. const float cylinderHeight = AZ::GetMax(AZ::Constants::Tolerance, capsule->m_height - 2.0f * capsule->m_radius);
  113. const AZ::Vector3 cylinderEnd1 = colliderGlobalTransform.GetTranslation() - 0.5f * cylinderHeight * capsuleZ;
  114. const AZ::Vector3 cylinderEnd2 = colliderGlobalTransform.GetTranslation() + 0.5f * cylinderHeight * capsuleZ;
  115. const float rayLength = 1000.0f;
  116. float t = AZ::Constants::FloatMax;
  117. const AZ::Intersect::CapsuleIsectTypes result = AZ::Intersect::IntersectSegmentCapsule(
  118. rayOrigin, rayDirection * rayLength, cylinderEnd1, cylinderEnd2, capsule->m_radius, t);
  119. if (result != AZ::Intersect::CapsuleIsectTypes::ISECT_RAY_CAPSULE_NONE)
  120. {
  121. const float distance = rayLength * t;
  122. CompareIntersection(closestIntersection, distance, jointIndex);
  123. }
  124. }
  125. else if (colliderType == azrtti_typeid<Physics::BoxShapeConfiguration>())
  126. {
  127. auto* box = static_cast<Physics::BoxShapeConfiguration*>(shapeColliderPair.second.get());
  128. float distance = AZ::Constants::FloatMax;
  129. if (AZ::Intersect::IntersectRayBox(
  130. rayOrigin,
  131. rayDirection,
  132. colliderGlobalTransform.GetTranslation(),
  133. colliderGlobalTransform.GetBasisX(),
  134. colliderGlobalTransform.GetBasisY(),
  135. colliderGlobalTransform.GetBasisZ(),
  136. 0.5f * box->m_dimensions.GetX(),
  137. 0.5f * box->m_dimensions.GetY(),
  138. 0.5f * box->m_dimensions.GetZ(),
  139. distance))
  140. {
  141. CompareIntersection(closestIntersection, distance, jointIndex);
  142. }
  143. }
  144. }
  145. }
  146. }
  147. }
  148. bool Picking::HandleMouseInteraction(
  149. const AzToolsFramework::ViewportInteraction::MouseInteractionEvent& mouseInteractionEvent)
  150. {
  151. const auto& mouseInteraction = mouseInteractionEvent.m_mouseInteraction;
  152. if (!mouseInteraction.m_mouseButtons.Left() ||
  153. mouseInteractionEvent.m_mouseEvent != AzToolsFramework::ViewportInteraction::MouseEvent::Down)
  154. {
  155. return false;
  156. }
  157. SkeletonModel* skeletonModel = nullptr;
  158. SkeletonOutlinerRequestBus::BroadcastResult(skeletonModel, &SkeletonOutlinerRequests::GetModel);
  159. if (!skeletonModel)
  160. {
  161. return false;
  162. }
  163. const ActorInstance* actorInstance = skeletonModel->GetActorInstance();
  164. if (!actorInstance)
  165. {
  166. return false;
  167. }
  168. PickingIntersection closestIntersection;
  169. const EMotionFX::Skeleton* skeleton = actorInstance->GetActor()->GetSkeleton();
  170. const AZ::Vector3& rayOrigin = mouseInteraction.m_mousePick.m_rayOrigin;
  171. const AZ::Vector3& rayDirection = mouseInteraction.m_mousePick.m_rayDirection;
  172. if (AZ::RHI::CheckBitsAny(m_renderFlags, EMotionFX::ActorRenderFlags::LineSkeleton))
  173. {
  174. IntersectLineSkeleton(closestIntersection, rayOrigin, rayDirection, actorInstance, skeleton);
  175. }
  176. if (AZ::RHI::CheckBitsAny(m_renderFlags, EMotionFX::ActorRenderFlags::RagdollColliders))
  177. {
  178. IntersectRagdollColliders(closestIntersection, rayOrigin, rayDirection, actorInstance);
  179. }
  180. if (closestIntersection.m_intersected)
  181. {
  182. QModelIndex modelIndex = skeletonModel->GetModelIndex(skeleton->GetNode(closestIntersection.m_jointIndex));
  183. skeletonModel->GetSelectionModel().select(modelIndex, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
  184. }
  185. return closestIntersection.m_intersected;
  186. }
  187. void Picking::SetRenderFlags(ActorRenderFlags renderFlags)
  188. {
  189. m_renderFlags = renderFlags;
  190. }
  191. } // namespace EMotionFX