CapsuleShape.cpp 11 KB

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