Character.cpp 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  1. // SPDX-FileCopyrightText: 2021 Jorrit Rouwe
  2. // SPDX-License-Identifier: MIT
  3. #include <Jolt/Jolt.h>
  4. #include <Jolt/Physics/Body/BodyCreationSettings.h>
  5. #include <Jolt/Physics/Body/BodyLock.h>
  6. #include <Jolt/Physics/Collision/CollideShape.h>
  7. #include <Jolt/Physics/Character/Character.h>
  8. #include <Jolt/Physics/PhysicsSystem.h>
  9. #include <Jolt/ObjectStream/TypeDeclarations.h>
  10. JPH_NAMESPACE_BEGIN
  11. static inline const BodyLockInterface &sGetBodyLockInterface(const PhysicsSystem *inSystem, bool inLockBodies)
  12. {
  13. return inLockBodies? static_cast<const BodyLockInterface &>(inSystem->GetBodyLockInterface()) : static_cast<const BodyLockInterface &>(inSystem->GetBodyLockInterfaceNoLock());
  14. }
  15. static inline BodyInterface &sGetBodyInterface(PhysicsSystem *inSystem, bool inLockBodies)
  16. {
  17. return inLockBodies? inSystem->GetBodyInterface() : inSystem->GetBodyInterfaceNoLock();
  18. }
  19. static inline const NarrowPhaseQuery &sGetNarrowPhaseQuery(PhysicsSystem *inSystem, bool inLockBodies)
  20. {
  21. return inLockBodies? inSystem->GetNarrowPhaseQuery() : inSystem->GetNarrowPhaseQueryNoLock();
  22. }
  23. Character::Character(const CharacterSettings *inSettings, Vec3Arg inPosition, QuatArg inRotation, uint64 inUserData, PhysicsSystem *inSystem) :
  24. CharacterBase(inSettings, inSystem),
  25. mLayer(inSettings->mLayer)
  26. {
  27. // Construct rigid body
  28. BodyCreationSettings settings(mShape, inPosition, inRotation, EMotionType::Dynamic, mLayer);
  29. settings.mFriction = inSettings->mFriction;
  30. settings.mGravityFactor = inSettings->mGravityFactor;
  31. settings.mUserData = inUserData;
  32. Body *body = mSystem->GetBodyInterface().CreateBody(settings);
  33. if (body != nullptr)
  34. {
  35. // Update the mass properties of the shape so that we set the correct mass and don't allow any rotation
  36. body->GetMotionProperties()->SetInverseMass(1.0f / inSettings->mMass);
  37. body->GetMotionProperties()->SetInverseInertia(Vec3::sZero(), Quat::sIdentity());
  38. mBodyID = body->GetID();
  39. }
  40. }
  41. Character::~Character()
  42. {
  43. // Destroy the body
  44. mSystem->GetBodyInterface().DestroyBody(mBodyID);
  45. }
  46. void Character::AddToPhysicsSystem(EActivation inActivationMode, bool inLockBodies)
  47. {
  48. sGetBodyInterface(mSystem, inLockBodies).AddBody(mBodyID, inActivationMode);
  49. }
  50. void Character::RemoveFromPhysicsSystem(bool inLockBodies)
  51. {
  52. sGetBodyInterface(mSystem, inLockBodies).RemoveBody(mBodyID);
  53. }
  54. void Character::Activate(bool inLockBodies)
  55. {
  56. sGetBodyInterface(mSystem, inLockBodies).ActivateBody(mBodyID);
  57. }
  58. void Character::CheckCollision(const Shape *inShape, float inMaxSeparationDistance, CollideShapeCollector &ioCollector, bool inLockBodies) const
  59. {
  60. // Create query broadphase layer filter
  61. DefaultBroadPhaseLayerFilter broadphase_layer_filter = mSystem->GetDefaultBroadPhaseLayerFilter(mLayer);
  62. // Create query object layer filter
  63. DefaultObjectLayerFilter object_layer_filter = mSystem->GetDefaultLayerFilter(mLayer);
  64. // Ignore my own body
  65. IgnoreSingleBodyFilter body_filter(mBodyID);
  66. // Determine position and velocity of body
  67. Mat44 query_transform;
  68. Vec3 velocity;
  69. {
  70. BodyLockRead lock(sGetBodyLockInterface(mSystem, inLockBodies), mBodyID);
  71. if (!lock.Succeeded())
  72. return;
  73. const Body &body = lock.GetBody();
  74. // Correct the center of mass transform for the difference between the old and new center of mass shape
  75. query_transform = body.GetCenterOfMassTransform().PreTranslated(inShape->GetCenterOfMass() - mShape->GetCenterOfMass());
  76. velocity = body.GetLinearVelocity();
  77. }
  78. // Settings for collide shape
  79. CollideShapeSettings settings;
  80. settings.mMaxSeparationDistance = inMaxSeparationDistance;
  81. settings.mActiveEdgeMode = EActiveEdgeMode::CollideOnlyWithActive;
  82. settings.mActiveEdgeMovementDirection = velocity;
  83. settings.mBackFaceMode = EBackFaceMode::IgnoreBackFaces;
  84. sGetNarrowPhaseQuery(mSystem, inLockBodies).CollideShape(inShape, Vec3::sReplicate(1.0f), query_transform, settings, ioCollector, broadphase_layer_filter, object_layer_filter, body_filter);
  85. }
  86. void Character::PostSimulation(float inMaxSeparationDistance, bool inLockBodies)
  87. {
  88. // Collector that finds the hit with the normal that is the most 'up'
  89. class MyCollector : public CollideShapeCollector
  90. {
  91. public:
  92. // Constructor
  93. explicit MyCollector(Vec3Arg inGravity) : mGravity(inGravity) { }
  94. // See: CollectorType::AddHit
  95. virtual void AddHit(const CollideShapeResult &inResult) override
  96. {
  97. Vec3 normal = -inResult.mPenetrationAxis.Normalized();
  98. float dot = normal.Dot(mGravity);
  99. if (dot < mBestDot) // Find the hit that is most opposite to the gravity
  100. {
  101. mGroundBodyID = inResult.mBodyID2;
  102. mGroundBodySubShapeID = inResult.mSubShapeID2;
  103. mGroundPosition = inResult.mContactPointOn2;
  104. mGroundNormal = normal;
  105. mBestDot = dot;
  106. }
  107. }
  108. BodyID mGroundBodyID;
  109. SubShapeID mGroundBodySubShapeID;
  110. Vec3 mGroundPosition = Vec3::sZero();
  111. Vec3 mGroundNormal = Vec3::sZero();
  112. private:
  113. float mBestDot = FLT_MAX;
  114. Vec3 mGravity;
  115. };
  116. // Collide shape
  117. MyCollector collector(mSystem->GetGravity());
  118. CheckCollision(mShape, inMaxSeparationDistance, collector);
  119. // Copy results
  120. mGroundBodyID = collector.mGroundBodyID;
  121. mGroundBodySubShapeID = collector.mGroundBodySubShapeID;
  122. mGroundPosition = collector.mGroundPosition;
  123. mGroundNormal = collector.mGroundNormal;
  124. // Get additional data from body
  125. BodyLockRead lock(sGetBodyLockInterface(mSystem, inLockBodies), mGroundBodyID);
  126. if (lock.Succeeded())
  127. {
  128. const Body &body = lock.GetBody();
  129. // Update ground state
  130. Vec3 up = -mSystem->GetGravity().Normalized();
  131. if (mGroundNormal.Dot(up) > mCosMaxSlopeAngle)
  132. mGroundState = EGroundState::OnGround;
  133. else
  134. mGroundState = EGroundState::Sliding;
  135. // Copy other body properties
  136. mGroundMaterial = body.GetShape()->GetMaterial(mGroundBodySubShapeID);
  137. mGroundVelocity = body.GetPointVelocity(mGroundPosition);
  138. mGroundUserData = body.GetUserData();
  139. }
  140. else
  141. {
  142. mGroundState = EGroundState::InAir;
  143. mGroundMaterial = PhysicsMaterial::sDefault;
  144. mGroundVelocity = Vec3::sZero();
  145. mGroundUserData = 0;
  146. }
  147. }
  148. void Character::SetLinearAndAngularVelocity(Vec3Arg inLinearVelocity, Vec3Arg inAngularVelocity, bool inLockBodies)
  149. {
  150. sGetBodyInterface(mSystem, inLockBodies).SetLinearAndAngularVelocity(mBodyID, inLinearVelocity, inAngularVelocity);
  151. }
  152. Vec3 Character::GetLinearVelocity(bool inLockBodies) const
  153. {
  154. return sGetBodyInterface(mSystem, inLockBodies).GetLinearVelocity(mBodyID);
  155. }
  156. void Character::SetLinearVelocity(Vec3Arg inLinearVelocity, bool inLockBodies)
  157. {
  158. sGetBodyInterface(mSystem, inLockBodies).SetLinearVelocity(mBodyID, inLinearVelocity);
  159. }
  160. void Character::AddLinearVelocity(Vec3Arg inLinearVelocity, bool inLockBodies)
  161. {
  162. sGetBodyInterface(mSystem, inLockBodies).AddLinearVelocity(mBodyID, inLinearVelocity);
  163. }
  164. void Character::AddImpulse(Vec3Arg inImpulse, bool inLockBodies)
  165. {
  166. sGetBodyInterface(mSystem, inLockBodies).AddImpulse(mBodyID, inImpulse);
  167. }
  168. void Character::GetPositionAndRotation(Vec3 &outPosition, Quat &outRotation, bool inLockBodies) const
  169. {
  170. sGetBodyInterface(mSystem, inLockBodies).GetPositionAndRotation(mBodyID, outPosition, outRotation);
  171. }
  172. void Character::SetPositionAndRotation(Vec3Arg inPosition, QuatArg inRotation, EActivation inActivationMode, bool inLockBodies) const
  173. {
  174. sGetBodyInterface(mSystem, inLockBodies).SetPositionAndRotation(mBodyID, inPosition, inRotation, inActivationMode);
  175. }
  176. Vec3 Character::GetPosition(bool inLockBodies) const
  177. {
  178. return sGetBodyInterface(mSystem, inLockBodies).GetPosition(mBodyID);
  179. }
  180. void Character::SetPosition(Vec3Arg inPosition, EActivation inActivationMode, bool inLockBodies)
  181. {
  182. sGetBodyInterface(mSystem, inLockBodies).SetPosition(mBodyID, inPosition, inActivationMode);
  183. }
  184. Quat Character::GetRotation(bool inLockBodies) const
  185. {
  186. return sGetBodyInterface(mSystem, inLockBodies).GetRotation(mBodyID);
  187. }
  188. void Character::SetRotation(QuatArg inRotation, EActivation inActivationMode, bool inLockBodies)
  189. {
  190. sGetBodyInterface(mSystem, inLockBodies).SetRotation(mBodyID, inRotation, inActivationMode);
  191. }
  192. Vec3 Character::GetCenterOfMassPosition(bool inLockBodies) const
  193. {
  194. return sGetBodyInterface(mSystem, inLockBodies).GetCenterOfMassPosition(mBodyID);
  195. }
  196. Mat44 Character::GetWorldTransform(bool inLockBodies) const
  197. {
  198. return sGetBodyInterface(mSystem, inLockBodies).GetWorldTransform(mBodyID);
  199. }
  200. void Character::SetLayer(ObjectLayer inLayer, bool inLockBodies)
  201. {
  202. mLayer = inLayer;
  203. sGetBodyInterface(mSystem, inLockBodies).SetObjectLayer(mBodyID, inLayer);
  204. }
  205. bool Character::SetShape(const Shape *inShape, float inMaxPenetrationDepth, bool inLockBodies)
  206. {
  207. if (inMaxPenetrationDepth < FLT_MAX)
  208. {
  209. // Collector that checks if there is anything in the way while switching to inShape
  210. class MyCollector : public CollideShapeCollector
  211. {
  212. public:
  213. // Constructor
  214. explicit MyCollector(float inMaxPenetrationDepth) : mMaxPenetrationDepth(inMaxPenetrationDepth) { }
  215. // See: CollectorType::AddHit
  216. virtual void AddHit(const CollideShapeResult &inResult) override
  217. {
  218. if (inResult.mPenetrationDepth > mMaxPenetrationDepth)
  219. {
  220. mHadCollision = true;
  221. ForceEarlyOut();
  222. }
  223. }
  224. float mMaxPenetrationDepth;
  225. bool mHadCollision = false;
  226. };
  227. // Test if anything is in the way of switching
  228. MyCollector collector(inMaxPenetrationDepth);
  229. CheckCollision(inShape, 0.0f, collector);
  230. if (collector.mHadCollision)
  231. return false;
  232. }
  233. // Switch the shape
  234. mShape = inShape;
  235. sGetBodyInterface(mSystem, inLockBodies).SetShape(mBodyID, mShape, false, EActivation::Activate);
  236. return true;
  237. }
  238. JPH_NAMESPACE_END