CapsuleShape.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  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 "CapsuleShapeComponent.h"
  9. #include <AzCore/Math/IntersectPoint.h>
  10. #include <AzCore/Math/IntersectSegment.h>
  11. #include <AzCore/Math/Transform.h>
  12. #include <AzCore/Serialization/EditContext.h>
  13. #include <AzCore/Serialization/SerializeContext.h>
  14. #include <MathConversion.h>
  15. namespace LmbrCentral
  16. {
  17. const AZ::u32 g_capsuleDebugShapeSides = 16;
  18. const AZ::u32 g_capsuleDebugShapeCapSegments = 8;
  19. void CapsuleShape::Reflect(AZ::ReflectContext* context)
  20. {
  21. CapsuleShapeConfig::Reflect(context);
  22. if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
  23. {
  24. serializeContext->Class<CapsuleShape>()
  25. ->Version(1)
  26. ->Field("Configuration", &CapsuleShape::m_capsuleShapeConfig)
  27. ;
  28. if (AZ::EditContext* editContext = serializeContext->GetEditContext())
  29. {
  30. editContext->Class<CapsuleShape>("Capsule Shape", "Capsule shape configuration parameters")
  31. ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
  32. ->DataElement(AZ::Edit::UIHandlers::Default, &CapsuleShape::m_capsuleShapeConfig, "Capsule Configuration", "Capsule shape configuration")
  33. ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
  34. ->Attribute(AZ::Edit::Attributes::AutoExpand, true)
  35. ;
  36. }
  37. }
  38. }
  39. void CapsuleShape::Activate(AZ::EntityId entityId)
  40. {
  41. m_entityId = entityId;
  42. m_currentTransform = AZ::Transform::CreateIdentity();
  43. AZ::TransformBus::EventResult(m_currentTransform, m_entityId, &AZ::TransformBus::Events::GetWorldTM);
  44. m_intersectionDataCache.InvalidateCache(InvalidateShapeCacheReason::ShapeChange);
  45. AZ::TransformNotificationBus::Handler::BusConnect(m_entityId);
  46. ShapeComponentRequestsBus::Handler::BusConnect(m_entityId);
  47. CapsuleShapeComponentRequestsBus::Handler::BusConnect(m_entityId);
  48. }
  49. void CapsuleShape::Deactivate()
  50. {
  51. CapsuleShapeComponentRequestsBus::Handler::BusDisconnect();
  52. ShapeComponentRequestsBus::Handler::BusDisconnect();
  53. AZ::TransformNotificationBus::Handler::BusDisconnect();
  54. }
  55. void CapsuleShape::InvalidateCache(InvalidateShapeCacheReason reason)
  56. {
  57. AZStd::unique_lock lock(m_mutex);
  58. m_intersectionDataCache.InvalidateCache(reason);
  59. }
  60. void CapsuleShape::OnTransformChanged(const AZ::Transform& /*local*/, const AZ::Transform& world)
  61. {
  62. {
  63. AZStd::unique_lock lock(m_mutex);
  64. m_currentTransform = world;
  65. m_intersectionDataCache.InvalidateCache(InvalidateShapeCacheReason::TransformChange);
  66. }
  67. ShapeComponentNotificationsBus::Event(
  68. m_entityId, &ShapeComponentNotificationsBus::Events::OnShapeChanged,
  69. ShapeComponentNotifications::ShapeChangeReasons::TransformChanged);
  70. }
  71. void CapsuleShape::SetHeight(float height)
  72. {
  73. {
  74. AZStd::unique_lock lock(m_mutex);
  75. m_capsuleShapeConfig.m_height = height;
  76. m_intersectionDataCache.InvalidateCache(InvalidateShapeCacheReason::ShapeChange);
  77. }
  78. ShapeComponentNotificationsBus::Event(
  79. m_entityId, &ShapeComponentNotificationsBus::Events::OnShapeChanged,
  80. ShapeComponentNotifications::ShapeChangeReasons::ShapeChanged);
  81. }
  82. void CapsuleShape::SetRadius(float radius)
  83. {
  84. {
  85. AZStd::unique_lock lock(m_mutex);
  86. m_capsuleShapeConfig.m_radius = radius;
  87. m_intersectionDataCache.InvalidateCache(InvalidateShapeCacheReason::ShapeChange);
  88. }
  89. ShapeComponentNotificationsBus::Event(
  90. m_entityId, &ShapeComponentNotificationsBus::Events::OnShapeChanged,
  91. ShapeComponentNotifications::ShapeChangeReasons::ShapeChanged);
  92. }
  93. float CapsuleShape::GetHeight() const
  94. {
  95. AZStd::shared_lock lock(m_mutex);
  96. return m_capsuleShapeConfig.m_height;
  97. }
  98. float CapsuleShape::GetRadius() const
  99. {
  100. AZStd::shared_lock lock(m_mutex);
  101. return m_capsuleShapeConfig.m_radius;
  102. }
  103. CapsuleInternalEndPoints CapsuleShape::GetCapsulePoints() const
  104. {
  105. AZStd::shared_lock lock(m_mutex);
  106. m_intersectionDataCache.UpdateIntersectionParams(m_currentTransform, m_capsuleShapeConfig, &m_mutex);
  107. return { m_intersectionDataCache.m_basePlaneCenterPoint, m_intersectionDataCache.m_topPlaneCenterPoint };
  108. }
  109. AZ::Aabb CapsuleShape::GetEncompassingAabb() const
  110. {
  111. AZStd::shared_lock lock(m_mutex);
  112. m_intersectionDataCache.UpdateIntersectionParams(m_currentTransform, m_capsuleShapeConfig, &m_mutex);
  113. const AZ::Aabb topAabb(AZ::Aabb::CreateCenterRadius(
  114. m_intersectionDataCache.m_topPlaneCenterPoint, m_intersectionDataCache.m_radius));
  115. AZ::Aabb baseAabb(AZ::Aabb::CreateCenterRadius(
  116. m_intersectionDataCache.m_basePlaneCenterPoint, m_intersectionDataCache.m_radius));
  117. baseAabb.AddAabb(topAabb);
  118. return baseAabb;
  119. }
  120. void CapsuleShape::GetTransformAndLocalBounds(AZ::Transform& transform, AZ::Aabb& bounds) const
  121. {
  122. float halfHeight = AZ::GetMax(m_capsuleShapeConfig.m_height * 0.5f, m_capsuleShapeConfig.m_radius);
  123. const AZ::Vector3 extent(m_capsuleShapeConfig.m_radius, m_capsuleShapeConfig.m_radius, halfHeight);
  124. bounds = AZ::Aabb::CreateFromMinMax(
  125. m_capsuleShapeConfig.m_translationOffset - extent, m_capsuleShapeConfig.m_translationOffset + extent);
  126. transform = m_currentTransform;
  127. }
  128. bool CapsuleShape::IsPointInside(const AZ::Vector3& point) const
  129. {
  130. AZStd::shared_lock lock(m_mutex);
  131. m_intersectionDataCache.UpdateIntersectionParams(m_currentTransform, m_capsuleShapeConfig, &m_mutex);
  132. const float radiusSquared = m_intersectionDataCache.m_radius * m_intersectionDataCache.m_radius;
  133. // Check Bottom sphere
  134. if (AZ::Intersect::PointSphere(m_intersectionDataCache.m_basePlaneCenterPoint, radiusSquared, point))
  135. {
  136. return true;
  137. }
  138. // If the capsule is infact just a sphere then just stop (because the height of the cylinder <= 2 * radius of the cylinder)
  139. if (m_intersectionDataCache.m_isSphere)
  140. {
  141. return false;
  142. }
  143. // Check Top sphere
  144. if (AZ::Intersect::PointSphere(m_intersectionDataCache.m_topPlaneCenterPoint, radiusSquared, point))
  145. {
  146. return true;
  147. }
  148. // If its not in either sphere check the cylinder
  149. return AZ::Intersect::PointCylinder(
  150. m_intersectionDataCache.m_basePlaneCenterPoint, m_intersectionDataCache.m_axisVector,
  151. m_intersectionDataCache.m_internalHeight * m_intersectionDataCache.m_internalHeight, radiusSquared, point);
  152. }
  153. float CapsuleShape::DistanceSquaredFromPoint(const AZ::Vector3& point) const
  154. {
  155. AZStd::shared_lock lock(m_mutex);
  156. m_intersectionDataCache.UpdateIntersectionParams(m_currentTransform, m_capsuleShapeConfig, &m_mutex);
  157. float distanceSq = AZ::Intersect::PointSegmentDistanceSq(point, m_intersectionDataCache.m_basePlaneCenterPoint, m_intersectionDataCache.m_topPlaneCenterPoint);
  158. float distance = AZ::Sqrt(distanceSq);
  159. distance -= m_intersectionDataCache.m_radius;
  160. const float clampedDistance = AZStd::max(distance, 0.0f);
  161. return clampedDistance * clampedDistance;
  162. }
  163. bool CapsuleShape::IntersectRay(const AZ::Vector3& src, const AZ::Vector3& dir, float& distance) const
  164. {
  165. AZStd::shared_lock lock(m_mutex);
  166. m_intersectionDataCache.UpdateIntersectionParams(m_currentTransform, m_capsuleShapeConfig, &m_mutex);
  167. if (m_intersectionDataCache.m_isSphere)
  168. {
  169. return AZ::Intersect::IntersectRaySphere(
  170. src, dir, m_intersectionDataCache.m_basePlaneCenterPoint,
  171. m_intersectionDataCache.m_radius, distance) > 0;
  172. }
  173. float t;
  174. const float rayLength = 1000.0f;
  175. const bool intersection = AZ::Intersect::IntersectSegmentCapsule(
  176. src, dir * rayLength, m_intersectionDataCache.m_basePlaneCenterPoint,
  177. m_intersectionDataCache.m_topPlaneCenterPoint, m_intersectionDataCache.m_radius, t) > 0;
  178. distance = rayLength * t;
  179. return intersection;
  180. }
  181. AZ::Vector3 CapsuleShape::GetTranslationOffset() const
  182. {
  183. return m_capsuleShapeConfig.m_translationOffset;
  184. }
  185. void CapsuleShape::SetTranslationOffset(const AZ::Vector3& translationOffset)
  186. {
  187. bool shapeChanged = false;
  188. {
  189. AZStd::unique_lock lock(m_mutex);
  190. if (!m_capsuleShapeConfig.m_translationOffset.IsClose(translationOffset))
  191. {
  192. m_capsuleShapeConfig.m_translationOffset = translationOffset;
  193. m_intersectionDataCache.InvalidateCache(InvalidateShapeCacheReason::ShapeChange);
  194. shapeChanged = true;
  195. }
  196. }
  197. if (shapeChanged)
  198. {
  199. ShapeComponentNotificationsBus::Event(
  200. m_entityId, &ShapeComponentNotificationsBus::Events::OnShapeChanged,
  201. ShapeComponentNotifications::ShapeChangeReasons::ShapeChanged);
  202. }
  203. }
  204. void CapsuleShape::CapsuleIntersectionDataCache::UpdateIntersectionParamsImpl(
  205. const AZ::Transform& currentTransform, const CapsuleShapeConfig& configuration,
  206. [[maybe_unused]] const AZ::Vector3& currentNonUniformScale)
  207. {
  208. const float entityScale = currentTransform.GetUniformScale();
  209. m_axisVector = currentTransform.GetBasisZ().GetNormalizedSafe() * entityScale;
  210. const AZ::Vector3 offsetPosition = currentTransform.TransformPoint(configuration.m_translationOffset);
  211. const float internalCylinderHeight = configuration.m_height - configuration.m_radius * 2.0f;
  212. if (internalCylinderHeight > std::numeric_limits<float>::epsilon())
  213. {
  214. const AZ::Vector3 currentPositionToPlanesVector = m_axisVector * (internalCylinderHeight * 0.5f);
  215. m_topPlaneCenterPoint = offsetPosition + currentPositionToPlanesVector;
  216. m_basePlaneCenterPoint = offsetPosition - currentPositionToPlanesVector;
  217. m_axisVector = m_axisVector * internalCylinderHeight;
  218. m_isSphere = false;
  219. }
  220. else
  221. {
  222. m_basePlaneCenterPoint = offsetPosition;
  223. m_topPlaneCenterPoint = offsetPosition;
  224. m_isSphere = true;
  225. }
  226. // scale intersection data cache radius by entity transform for internal calculations
  227. m_radius = configuration.m_radius * entityScale;
  228. m_internalHeight = entityScale * internalCylinderHeight;
  229. }
  230. } // namespace LmbrCentral