CharacterVirtual.cpp 56 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418
  1. // SPDX-FileCopyrightText: 2021 Jorrit Rouwe
  2. // SPDX-License-Identifier: MIT
  3. #include <Jolt/Jolt.h>
  4. #include <Jolt/Physics/Character/CharacterVirtual.h>
  5. #include <Jolt/Physics/Body/Body.h>
  6. #include <Jolt/Physics/PhysicsSystem.h>
  7. #include <Jolt/Physics/Collision/ShapeCast.h>
  8. #include <Jolt/Physics/Collision/CollideShape.h>
  9. #include <Jolt/Physics/Collision/Shape/RotatedTranslatedShape.h>
  10. #include <Jolt/Core/QuickSort.h>
  11. #include <Jolt/Geometry/ConvexSupport.h>
  12. #include <Jolt/Geometry/GJKClosestPoint.h>
  13. #ifdef JPH_DEBUG_RENDERER
  14. #include <Jolt/Renderer/DebugRenderer.h>
  15. #endif // JPH_DEBUG_RENDERER
  16. JPH_NAMESPACE_BEGIN
  17. CharacterVirtual::CharacterVirtual(const CharacterVirtualSettings *inSettings, RVec3Arg inPosition, QuatArg inRotation, PhysicsSystem *inSystem) :
  18. CharacterBase(inSettings, inSystem),
  19. mPredictiveContactDistance(inSettings->mPredictiveContactDistance),
  20. mMaxCollisionIterations(inSettings->mMaxCollisionIterations),
  21. mMaxConstraintIterations(inSettings->mMaxConstraintIterations),
  22. mMinTimeRemaining(inSettings->mMinTimeRemaining),
  23. mCollisionTolerance(inSettings->mCollisionTolerance),
  24. mCharacterPadding(inSettings->mCharacterPadding),
  25. mMaxNumHits(inSettings->mMaxNumHits),
  26. mHitReductionCosMaxAngle(inSettings->mHitReductionCosMaxAngle),
  27. mPenetrationRecoverySpeed(inSettings->mPenetrationRecoverySpeed),
  28. mShapeOffset(inSettings->mShapeOffset),
  29. mPosition(inPosition),
  30. mRotation(inRotation)
  31. {
  32. // Copy settings
  33. SetMaxStrength(inSettings->mMaxStrength);
  34. SetMass(inSettings->mMass);
  35. }
  36. void CharacterVirtual::GetAdjustedBodyVelocity(const Body& inBody, Vec3 &outLinearVelocity, Vec3 &outAngularVelocity) const
  37. {
  38. // Get real velocity of body
  39. if (!inBody.IsStatic())
  40. {
  41. const MotionProperties *mp = inBody.GetMotionPropertiesUnchecked();
  42. outLinearVelocity = mp->GetLinearVelocity();
  43. outAngularVelocity = mp->GetAngularVelocity();
  44. }
  45. else
  46. {
  47. outLinearVelocity = outAngularVelocity = Vec3::sZero();
  48. }
  49. // Allow application to override
  50. if (mListener != nullptr)
  51. mListener->OnAdjustBodyVelocity(this, inBody, outLinearVelocity, outAngularVelocity);
  52. }
  53. template <class taCollector>
  54. void CharacterVirtual::sFillContactProperties(const CharacterVirtual *inCharacter, Contact &outContact, const Body &inBody, Vec3Arg inUp, RVec3Arg inBaseOffset, const taCollector &inCollector, const CollideShapeResult &inResult)
  55. {
  56. // Get adjusted body velocity
  57. Vec3 linear_velocity, angular_velocity;
  58. inCharacter->GetAdjustedBodyVelocity(inBody, linear_velocity, angular_velocity);
  59. outContact.mPosition = inBaseOffset + inResult.mContactPointOn2;
  60. outContact.mLinearVelocity = linear_velocity + angular_velocity.Cross(Vec3(outContact.mPosition - inBody.GetCenterOfMassPosition())); // Calculate point velocity
  61. outContact.mContactNormal = -inResult.mPenetrationAxis.NormalizedOr(Vec3::sZero());
  62. outContact.mSurfaceNormal = inCollector.GetContext()->GetWorldSpaceSurfaceNormal(inResult.mSubShapeID2, outContact.mPosition);
  63. if (outContact.mContactNormal.Dot(outContact.mSurfaceNormal) < 0.0f)
  64. outContact.mSurfaceNormal = -outContact.mSurfaceNormal; // Flip surface normal if we're hitting a back face
  65. if (outContact.mContactNormal.Dot(inUp) > outContact.mSurfaceNormal.Dot(inUp))
  66. outContact.mSurfaceNormal = outContact.mContactNormal; // Replace surface normal with contact normal if the contact normal is pointing more upwards
  67. outContact.mDistance = -inResult.mPenetrationDepth;
  68. outContact.mBodyB = inResult.mBodyID2;
  69. outContact.mSubShapeIDB = inResult.mSubShapeID2;
  70. outContact.mMotionTypeB = inBody.GetMotionType();
  71. outContact.mUserData = inBody.GetUserData();
  72. outContact.mMaterial = inCollector.GetContext()->GetMaterial(inResult.mSubShapeID2);
  73. }
  74. void CharacterVirtual::ContactCollector::AddHit(const CollideShapeResult &inResult)
  75. {
  76. // If we exceed our contact limit, try to clean up near-duplicate contacts
  77. if (mContacts.size() == mMaxHits)
  78. {
  79. // Flag that we hit this code path
  80. mMaxHitsExceeded = true;
  81. // Check if we can do reduction
  82. if (mHitReductionCosMaxAngle > -1.0f)
  83. {
  84. // Loop all contacts and find similar contacts
  85. for (int i = (int)mContacts.size() - 1; i >= 0; --i)
  86. {
  87. Contact &contact_i = mContacts[i];
  88. for (int j = i - 1; j >= 0; --j)
  89. {
  90. Contact &contact_j = mContacts[j];
  91. if (contact_i.mBodyB == contact_j.mBodyB // Same body
  92. && contact_i.mContactNormal.Dot(contact_j.mContactNormal) > mHitReductionCosMaxAngle) // Very similar contact normals
  93. {
  94. // Remove the contact with the biggest distance
  95. bool i_is_last = i == (int)mContacts.size() - 1;
  96. if (contact_i.mDistance > contact_j.mDistance)
  97. {
  98. // Remove i
  99. if (!i_is_last)
  100. contact_i = mContacts.back();
  101. mContacts.pop_back();
  102. // Break out of the loop, i is now an element that we already processed
  103. break;
  104. }
  105. else
  106. {
  107. // Remove j
  108. contact_j = mContacts.back();
  109. mContacts.pop_back();
  110. // If i was the last element, we just moved it into position j. Break out of the loop, we'll see it again later.
  111. if (i_is_last)
  112. break;
  113. }
  114. }
  115. }
  116. }
  117. }
  118. if (mContacts.size() == mMaxHits)
  119. {
  120. // There are still too many hits, give up!
  121. ForceEarlyOut();
  122. return;
  123. }
  124. }
  125. BodyLockRead lock(mSystem->GetBodyLockInterface(), inResult.mBodyID2);
  126. if (lock.SucceededAndIsInBroadPhase())
  127. {
  128. const Body &body = lock.GetBody();
  129. mContacts.emplace_back();
  130. Contact &contact = mContacts.back();
  131. sFillContactProperties(mCharacter, contact, body, mUp, mBaseOffset, *this, inResult);
  132. contact.mFraction = 0.0f;
  133. }
  134. }
  135. void CharacterVirtual::ContactCastCollector::AddHit(const ShapeCastResult &inResult)
  136. {
  137. // Should not have gotten here without a lower fraction
  138. JPH_ASSERT(inResult.mFraction < mContact.mFraction);
  139. if (inResult.mFraction > 0.0f // Ignore collisions at fraction = 0
  140. && inResult.mPenetrationAxis.Dot(mDisplacement) > 0.0f) // Ignore penetrations that we're moving away from
  141. {
  142. // Test if this contact should be ignored
  143. for (const IgnoredContact &c : mIgnoredContacts)
  144. if (c.mBodyID == inResult.mBodyID2 && c.mSubShapeID == inResult.mSubShapeID2)
  145. return;
  146. Contact contact;
  147. // Lock body only while we fetch contact properties
  148. {
  149. BodyLockRead lock(mSystem->GetBodyLockInterface(), inResult.mBodyID2);
  150. if (!lock.SucceededAndIsInBroadPhase())
  151. return;
  152. // Convert the hit result into a contact
  153. sFillContactProperties(mCharacter, contact, lock.GetBody(), mUp, mBaseOffset, *this, inResult);
  154. }
  155. contact.mFraction = inResult.mFraction;
  156. // Check if the contact that will make us penetrate more than the allowed tolerance
  157. if (contact.mDistance + contact.mContactNormal.Dot(mDisplacement) < -mCharacter->mCollisionTolerance
  158. && mCharacter->ValidateContact(contact))
  159. {
  160. mContact = contact;
  161. UpdateEarlyOutFraction(contact.mFraction);
  162. }
  163. }
  164. }
  165. void CharacterVirtual::CheckCollision(RVec3Arg inPosition, QuatArg inRotation, Vec3Arg inMovementDirection, float inMaxSeparationDistance, const Shape *inShape, RVec3Arg inBaseOffset, CollideShapeCollector &ioCollector, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter) const
  166. {
  167. // Query shape transform
  168. RMat44 transform = GetCenterOfMassTransform(inPosition, inRotation, inShape);
  169. // Settings for collide shape
  170. CollideShapeSettings settings;
  171. settings.mActiveEdgeMode = EActiveEdgeMode::CollideOnlyWithActive;
  172. settings.mBackFaceMode = EBackFaceMode::CollideWithBackFaces;
  173. settings.mActiveEdgeMovementDirection = inMovementDirection;
  174. settings.mMaxSeparationDistance = mCharacterPadding + inMaxSeparationDistance;
  175. // Collide shape
  176. mSystem->GetNarrowPhaseQuery().CollideShape(inShape, Vec3::sReplicate(1.0f), transform, settings, inBaseOffset, ioCollector, inBroadPhaseLayerFilter, inObjectLayerFilter, inBodyFilter, inShapeFilter);
  177. }
  178. void CharacterVirtual::GetContactsAtPosition(RVec3Arg inPosition, Vec3Arg inMovementDirection, const Shape *inShape, TempContactList &outContacts, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter) const
  179. {
  180. // Remove previous results
  181. outContacts.clear();
  182. // Collide shape
  183. ContactCollector collector(mSystem, this, mMaxNumHits, mHitReductionCosMaxAngle, mUp, mPosition, outContacts);
  184. CheckCollision(inPosition, mRotation, inMovementDirection, mPredictiveContactDistance, inShape, mPosition, collector, inBroadPhaseLayerFilter, inObjectLayerFilter, inBodyFilter, inShapeFilter);
  185. // Flag if we exceeded the max number of hits
  186. mMaxHitsExceeded = collector.mMaxHitsExceeded;
  187. // Reduce distance to contact by padding to ensure we stay away from the object by a little margin
  188. // (this will make collision detection cheaper - especially for sweep tests as they won't hit the surface if we're properly sliding)
  189. for (Contact &c : outContacts)
  190. c.mDistance -= mCharacterPadding;
  191. }
  192. void CharacterVirtual::RemoveConflictingContacts(TempContactList &ioContacts, IgnoredContactList &outIgnoredContacts) const
  193. {
  194. // Only use this algorithm if we're penetrating further than this (due to numerical precision issues we can always penetrate a little bit and we don't want to discard contacts if they just have a tiny penetration)
  195. // We do need to account for padding (see GetContactsAtPosition) that is removed from the contact distances, to compensate we add it to the cMinRequiredPenetration
  196. const float cMinRequiredPenetration = 1.25f * mCharacterPadding;
  197. // Discard conflicting penetrating contacts
  198. for (size_t c1 = 0; c1 < ioContacts.size(); c1++)
  199. {
  200. Contact &contact1 = ioContacts[c1];
  201. if (contact1.mDistance <= -cMinRequiredPenetration) // Only for penetrations
  202. for (size_t c2 = c1 + 1; c2 < ioContacts.size(); c2++)
  203. {
  204. Contact &contact2 = ioContacts[c2];
  205. if (contact1.mBodyB == contact2.mBodyB // Only same body
  206. && contact2.mDistance <= -cMinRequiredPenetration // Only for penetrations
  207. && contact1.mContactNormal.Dot(contact2.mContactNormal) < 0.0f) // Only opposing normals
  208. {
  209. // Discard contacts with the least amount of penetration
  210. if (contact1.mDistance < contact2.mDistance)
  211. {
  212. // Discard the 2nd contact
  213. outIgnoredContacts.emplace_back(contact2.mBodyB, contact2.mSubShapeIDB);
  214. ioContacts.erase(ioContacts.begin() + c2);
  215. c2--;
  216. }
  217. else
  218. {
  219. // Discard the first contact
  220. outIgnoredContacts.emplace_back(contact1.mBodyB, contact1.mSubShapeIDB);
  221. ioContacts.erase(ioContacts.begin() + c1);
  222. c1--;
  223. break;
  224. }
  225. }
  226. }
  227. }
  228. }
  229. bool CharacterVirtual::ValidateContact(const Contact &inContact) const
  230. {
  231. if (mListener == nullptr)
  232. return true;
  233. return mListener->OnContactValidate(this, inContact.mBodyB, inContact.mSubShapeIDB);
  234. }
  235. template <class T>
  236. inline static bool sCorrectFractionForCharacterPadding(const Shape *inShape, Mat44Arg inStart, Vec3Arg inDisplacement, const T &inPolygon, float &ioFraction)
  237. {
  238. if (inShape->GetType() == EShapeType::Convex)
  239. {
  240. // Get the support function for the shape we're casting
  241. const ConvexShape *convex_shape = static_cast<const ConvexShape *>(inShape);
  242. ConvexShape::SupportBuffer buffer;
  243. const ConvexShape::Support *support = convex_shape->GetSupportFunction(ConvexShape::ESupportMode::IncludeConvexRadius, buffer, Vec3::sReplicate(1.0f));
  244. // Cast the shape against the polygon
  245. GJKClosestPoint gjk;
  246. return gjk.CastShape(inStart, inDisplacement, cDefaultCollisionTolerance, *support, inPolygon, ioFraction);
  247. }
  248. else if (inShape->GetSubType() == EShapeSubType::RotatedTranslated)
  249. {
  250. const RotatedTranslatedShape *rt_shape = static_cast<const RotatedTranslatedShape *>(inShape);
  251. return sCorrectFractionForCharacterPadding(rt_shape->GetInnerShape(), inStart * Mat44::sRotation(rt_shape->GetRotation()), inDisplacement, inPolygon, ioFraction);
  252. }
  253. else
  254. {
  255. JPH_ASSERT(false, "Not supported yet!");
  256. return false;
  257. }
  258. }
  259. bool CharacterVirtual::GetFirstContactForSweep(RVec3Arg inPosition, Vec3Arg inDisplacement, Contact &outContact, const IgnoredContactList &inIgnoredContacts, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter) const
  260. {
  261. // Too small distance -> skip checking
  262. float displacement_len_sq = inDisplacement.LengthSq();
  263. if (displacement_len_sq < 1.0e-8f)
  264. return false;
  265. // Calculate start transform
  266. RMat44 start = GetCenterOfMassTransform(inPosition, mRotation, mShape);
  267. // Settings for the cast
  268. ShapeCastSettings settings;
  269. settings.mBackFaceModeTriangles = EBackFaceMode::CollideWithBackFaces;
  270. settings.mBackFaceModeConvex = EBackFaceMode::IgnoreBackFaces;
  271. settings.mActiveEdgeMode = EActiveEdgeMode::CollideOnlyWithActive;
  272. settings.mUseShrunkenShapeAndConvexRadius = true;
  273. settings.mReturnDeepestPoint = false;
  274. // Cast shape
  275. Contact contact;
  276. contact.mFraction = 1.0f + FLT_EPSILON;
  277. ContactCastCollector collector(mSystem, this, inDisplacement, mUp, inIgnoredContacts, start.GetTranslation(), contact);
  278. RShapeCast shape_cast(mShape, Vec3::sReplicate(1.0f), start, inDisplacement);
  279. mSystem->GetNarrowPhaseQuery().CastShape(shape_cast, settings, start.GetTranslation(), collector, inBroadPhaseLayerFilter, inObjectLayerFilter, inBodyFilter, inShapeFilter);
  280. if (contact.mBodyB.IsInvalid())
  281. return false;
  282. // Store contact
  283. outContact = contact;
  284. // Fetch the face we're colliding with
  285. TransformedShape ts = mSystem->GetBodyInterface().GetTransformedShape(outContact.mBodyB);
  286. Shape::SupportingFace face;
  287. ts.GetSupportingFace(outContact.mSubShapeIDB, -outContact.mContactNormal, start.GetTranslation(), face);
  288. bool corrected = false;
  289. if (face.size() >= 2)
  290. {
  291. // Inflate the colliding face by the character padding
  292. PolygonConvexSupport polygon(face);
  293. AddConvexRadius add_cvx(polygon, mCharacterPadding);
  294. // Correct fraction to hit this inflated face instead of the inner shape
  295. corrected = sCorrectFractionForCharacterPadding(mShape, start.GetRotation(), inDisplacement, add_cvx, outContact.mFraction);
  296. }
  297. if (!corrected)
  298. {
  299. // When there's only a single contact point or when we were unable to correct the fraction,
  300. // we can just move the fraction back so that the character and its padding don't hit the contact point anymore
  301. outContact.mFraction = max(0.0f, outContact.mFraction - mCharacterPadding / sqrt(displacement_len_sq));
  302. }
  303. return true;
  304. }
  305. void CharacterVirtual::DetermineConstraints(TempContactList &inContacts, ConstraintList &outConstraints) const
  306. {
  307. for (Contact &c : inContacts)
  308. {
  309. Vec3 contact_velocity = c.mLinearVelocity;
  310. // Penetrating contact: Add a contact velocity that pushes the character out at the desired speed
  311. if (c.mDistance < 0.0f)
  312. contact_velocity -= c.mContactNormal * c.mDistance * mPenetrationRecoverySpeed;
  313. // Convert to a constraint
  314. outConstraints.emplace_back();
  315. Constraint &constraint = outConstraints.back();
  316. constraint.mContact = &c;
  317. constraint.mLinearVelocity = contact_velocity;
  318. constraint.mPlane = Plane(c.mContactNormal, c.mDistance);
  319. // Next check if the angle is too steep and if it is add an additional constraint that holds the character back
  320. if (IsSlopeTooSteep(c.mSurfaceNormal))
  321. {
  322. // Only take planes that point up.
  323. // Note that we use the contact normal to allow for better sliding as the surface normal may be in the opposite direction of movement.
  324. float dot = c.mContactNormal.Dot(mUp);
  325. if (dot > 1.0e-3f) // Add a little slack, if the normal is perfectly horizontal we already have our vertical plane.
  326. {
  327. // Make horizontal normal
  328. Vec3 normal = (c.mContactNormal - dot * mUp).Normalized();
  329. // Create a secondary constraint that blocks horizontal movement
  330. outConstraints.emplace_back();
  331. Constraint &vertical_constraint = outConstraints.back();
  332. vertical_constraint.mContact = &c;
  333. vertical_constraint.mLinearVelocity = contact_velocity.Dot(normal) * normal; // Project the contact velocity on the new normal so that both planes push at an equal rate
  334. vertical_constraint.mPlane = Plane(normal, c.mDistance / normal.Dot(c.mContactNormal)); // Calculate the distance we have to travel horizontally to hit the contact plane
  335. }
  336. }
  337. }
  338. }
  339. bool CharacterVirtual::HandleContact(Vec3Arg inVelocity, Constraint &ioConstraint, float inDeltaTime) const
  340. {
  341. Contact &contact = *ioConstraint.mContact;
  342. // Validate the contact point
  343. if (!ValidateContact(contact))
  344. return false;
  345. // Send contact added event
  346. CharacterContactSettings settings;
  347. if (mListener != nullptr)
  348. mListener->OnContactAdded(this, contact.mBodyB, contact.mSubShapeIDB, contact.mPosition, -contact.mContactNormal, settings);
  349. contact.mCanPushCharacter = settings.mCanPushCharacter;
  350. // If body B cannot receive an impulse, we're done
  351. if (!settings.mCanReceiveImpulses || contact.mMotionTypeB != EMotionType::Dynamic)
  352. return true;
  353. // Lock the body we're colliding with
  354. BodyLockWrite lock(mSystem->GetBodyLockInterface(), contact.mBodyB);
  355. if (!lock.SucceededAndIsInBroadPhase())
  356. return false; // Body has been removed, we should not collide with it anymore
  357. const Body &body = lock.GetBody();
  358. // Calculate the velocity that we want to apply at B so that it will start moving at the character's speed at the contact point
  359. constexpr float cDamping = 0.9f;
  360. constexpr float cPenetrationResolution = 0.4f;
  361. Vec3 relative_velocity = inVelocity - contact.mLinearVelocity;
  362. float projected_velocity = relative_velocity.Dot(contact.mContactNormal);
  363. float delta_velocity = -projected_velocity * cDamping - min(contact.mDistance, 0.0f) * cPenetrationResolution / inDeltaTime;
  364. // Don't apply impulses if we're separating
  365. if (delta_velocity < 0.0f)
  366. return true;
  367. // Determine mass properties of the body we're colliding with
  368. const MotionProperties *motion_properties = body.GetMotionProperties();
  369. RVec3 center_of_mass = body.GetCenterOfMassPosition();
  370. Mat44 inverse_inertia = body.GetInverseInertia();
  371. float inverse_mass = motion_properties->GetInverseMass();
  372. // Calculate the inverse of the mass of body B as seen at the contact point in the direction of the contact normal
  373. Vec3 jacobian = Vec3(contact.mPosition - center_of_mass).Cross(contact.mContactNormal);
  374. float inv_effective_mass = inverse_inertia.Multiply3x3(jacobian).Dot(jacobian) + inverse_mass;
  375. // Impulse P = M dv
  376. float impulse = delta_velocity / inv_effective_mass;
  377. // Clamp the impulse according to the character strength, character strength is a force in newtons, P = F dt
  378. float max_impulse = mMaxStrength * inDeltaTime;
  379. impulse = min(impulse, max_impulse);
  380. // Calculate the world space impulse to apply
  381. Vec3 world_impulse = -impulse * contact.mContactNormal;
  382. // Cancel impulse in down direction (we apply gravity later)
  383. float impulse_dot_up = world_impulse.Dot(mUp);
  384. if (impulse_dot_up < 0.0f)
  385. world_impulse -= impulse_dot_up * mUp;
  386. // Now apply the impulse (body is already locked so we use the no-lock interface)
  387. mSystem->GetBodyInterfaceNoLock().AddImpulse(contact.mBodyB, world_impulse, contact.mPosition);
  388. return true;
  389. }
  390. void CharacterVirtual::SolveConstraints(Vec3Arg inVelocity, float inDeltaTime, float inTimeRemaining, ConstraintList &ioConstraints, IgnoredContactList &ioIgnoredContacts, float &outTimeSimulated, Vec3 &outDisplacement, TempAllocator &inAllocator
  391. #ifdef JPH_DEBUG_RENDERER
  392. , bool inDrawConstraints
  393. #endif // JPH_DEBUG_RENDERER
  394. ) const
  395. {
  396. // If there are no constraints we can immediately move to our target
  397. if (ioConstraints.empty())
  398. {
  399. outDisplacement = inVelocity * inTimeRemaining;
  400. outTimeSimulated = inTimeRemaining;
  401. return;
  402. }
  403. // Create array that holds the constraints in order of time of impact (sort will happen later)
  404. std::vector<Constraint *, STLTempAllocator<Constraint *>> sorted_constraints(inAllocator);
  405. sorted_constraints.resize(ioConstraints.size());
  406. for (size_t index = 0; index < sorted_constraints.size(); index++)
  407. sorted_constraints[index] = &ioConstraints[index];
  408. // This is the velocity we use for the displacement, if we hit something it will be shortened
  409. Vec3 velocity = inVelocity;
  410. // Keep track of the last velocity that was applied to the character so that we can detect when the velocity reverses
  411. Vec3 last_velocity = inVelocity;
  412. // Start with no displacement
  413. outDisplacement = Vec3::sZero();
  414. outTimeSimulated = 0.0f;
  415. // These are the contacts that we hit previously without moving a significant distance
  416. std::vector<Constraint *, STLTempAllocator<Constraint *>> previous_contacts(inAllocator);
  417. previous_contacts.resize(mMaxConstraintIterations);
  418. int num_previous_contacts = 0;
  419. // Loop for a max amount of iterations
  420. for (uint iteration = 0; iteration < mMaxConstraintIterations; iteration++)
  421. {
  422. // Calculate time of impact for all constraints
  423. for (Constraint &c : ioConstraints)
  424. {
  425. // Project velocity on plane direction
  426. c.mProjectedVelocity = c.mPlane.GetNormal().Dot(c.mLinearVelocity - velocity);
  427. if (c.mProjectedVelocity < 1.0e-6f)
  428. {
  429. c.mTOI = FLT_MAX;
  430. }
  431. else
  432. {
  433. // Distance to plane
  434. float dist = c.mPlane.SignedDistance(outDisplacement);
  435. if (dist - c.mProjectedVelocity * inTimeRemaining > -1.0e-4f)
  436. {
  437. // Too little penetration, accept the movement
  438. c.mTOI = FLT_MAX;
  439. }
  440. else
  441. {
  442. // Calculate time of impact
  443. c.mTOI = max(0.0f, dist / c.mProjectedVelocity);
  444. }
  445. }
  446. }
  447. // Sort constraints on proximity
  448. QuickSort(sorted_constraints.begin(), sorted_constraints.end(), [](const Constraint *inLHS, const Constraint *inRHS) {
  449. // If both constraints hit at t = 0 then order the one that will push the character furthest first
  450. // Note that because we add velocity to penetrating contacts, this will also resolve contacts that penetrate the most
  451. if (inLHS->mTOI <= 0.0f && inRHS->mTOI <= 0.0f)
  452. return inLHS->mProjectedVelocity > inRHS->mProjectedVelocity;
  453. // Then sort on time of impact
  454. if (inLHS->mTOI != inRHS->mTOI)
  455. return inLHS->mTOI < inRHS->mTOI;
  456. // As a tie breaker sort static first so it has the most influence
  457. return inLHS->mContact->mMotionTypeB > inRHS->mContact->mMotionTypeB;
  458. });
  459. // Find the first valid constraint
  460. Constraint *constraint = nullptr;
  461. for (Constraint *c : sorted_constraints)
  462. {
  463. // Take the first contact and see if we can reach it
  464. if (c->mTOI >= inTimeRemaining)
  465. {
  466. // We can reach our goal!
  467. outDisplacement += velocity * inTimeRemaining;
  468. outTimeSimulated += inTimeRemaining;
  469. return;
  470. }
  471. // Test if this contact was discarded by the contact callback before
  472. if (c->mContact->mWasDiscarded)
  473. continue;
  474. // Check if we made contact with this before
  475. if (!c->mContact->mHadCollision)
  476. {
  477. // Handle the contact
  478. if (!HandleContact(velocity, *c, inDeltaTime))
  479. {
  480. // Constraint should be ignored, remove it from the list
  481. c->mContact->mWasDiscarded = true;
  482. // Mark it as ignored for GetFirstContactForSweep
  483. ioIgnoredContacts.emplace_back(c->mContact->mBodyB, c->mContact->mSubShapeIDB);
  484. continue;
  485. }
  486. c->mContact->mHadCollision = true;
  487. }
  488. // Cancel velocity of constraint if it cannot push the character
  489. if (!c->mContact->mCanPushCharacter)
  490. c->mLinearVelocity = Vec3::sZero();
  491. // We found the first constraint that we want to collide with
  492. constraint = c;
  493. break;
  494. }
  495. if (constraint == nullptr)
  496. {
  497. // All constraints were discarded, we can reach our goal!
  498. outDisplacement += velocity * inTimeRemaining;
  499. outTimeSimulated += inTimeRemaining;
  500. return;
  501. }
  502. // Move to the contact
  503. outDisplacement += velocity * constraint->mTOI;
  504. inTimeRemaining -= constraint->mTOI;
  505. outTimeSimulated += constraint->mTOI;
  506. // If there's not enough time left to be simulated, bail
  507. if (inTimeRemaining < mMinTimeRemaining)
  508. return;
  509. // If we've moved significantly, clear all previous contacts
  510. if (constraint->mTOI > 1.0e-4f)
  511. num_previous_contacts = 0;
  512. // Get the normal of the plane we're hitting
  513. Vec3 plane_normal = constraint->mPlane.GetNormal();
  514. // Get the relative velocity between the character and the constraint
  515. Vec3 relative_velocity = velocity - constraint->mLinearVelocity;
  516. // Calculate new velocity if we cancel the relative velocity in the normal direction
  517. Vec3 new_velocity = velocity - relative_velocity.Dot(plane_normal) * plane_normal;
  518. // Find the normal of the previous contact that we will violate the most if we move in this new direction
  519. float highest_penetration = 0.0f;
  520. Constraint *other_constraint = nullptr;
  521. for (Constraint **c = previous_contacts.data(); c < previous_contacts.data() + num_previous_contacts; ++c)
  522. if (*c != constraint)
  523. {
  524. // Calculate how much we will penetrate if we move in this direction
  525. Vec3 other_normal = (*c)->mPlane.GetNormal();
  526. float penetration = ((*c)->mLinearVelocity - new_velocity).Dot(other_normal);
  527. if (penetration > highest_penetration)
  528. {
  529. // We don't want parallel or anti-parallel normals as that will cause our cross product below to become zero. Slack is approx 10 degrees.
  530. float dot = other_normal.Dot(plane_normal);
  531. if (dot < 0.984f && dot > -0.984f)
  532. {
  533. highest_penetration = penetration;
  534. other_constraint = *c;
  535. }
  536. }
  537. }
  538. // Check if we found a 2nd constraint
  539. if (other_constraint != nullptr)
  540. {
  541. // Calculate the sliding direction and project the new velocity onto that sliding direction
  542. Vec3 other_normal = other_constraint->mPlane.GetNormal();
  543. Vec3 slide_dir = plane_normal.Cross(other_normal).Normalized();
  544. Vec3 velocity_in_slide_dir = new_velocity.Dot(slide_dir) * slide_dir;
  545. // Cancel the constraint velocity in the other constraint plane's direction so that we won't try to apply it again and keep ping ponging between planes
  546. constraint->mLinearVelocity -= min(0.0f, constraint->mLinearVelocity.Dot(other_normal)) * other_normal;
  547. // Cancel the other constraints velocity in this constraint plane's direction so that we won't try to apply it again and keep ping ponging between planes
  548. other_constraint->mLinearVelocity -= min(0.0f, other_constraint->mLinearVelocity.Dot(plane_normal)) * plane_normal;
  549. // Calculate the velocity of this constraint perpendicular to the slide direction
  550. Vec3 perpendicular_velocity = constraint->mLinearVelocity - constraint->mLinearVelocity.Dot(slide_dir) * slide_dir;
  551. // Calculate the velocity of the other constraint perpendicular to the slide direction
  552. Vec3 other_perpendicular_velocity = other_constraint->mLinearVelocity - other_constraint->mLinearVelocity.Dot(slide_dir) * slide_dir;
  553. // Add all components together
  554. new_velocity = velocity_in_slide_dir + perpendicular_velocity + other_perpendicular_velocity;
  555. }
  556. // Allow application to modify calculated velocity
  557. if (mListener != nullptr)
  558. mListener->OnContactSolve(this, constraint->mContact->mBodyB, constraint->mContact->mSubShapeIDB, constraint->mContact->mPosition, constraint->mContact->mContactNormal, constraint->mContact->mLinearVelocity, constraint->mContact->mMaterial, velocity, new_velocity);
  559. #ifdef JPH_DEBUG_RENDERER
  560. if (inDrawConstraints)
  561. {
  562. // Calculate where to draw
  563. RVec3 offset = mPosition + Vec3(0, 0, 2.5f * (iteration + 1));
  564. // Draw constraint plane
  565. DebugRenderer::sInstance->DrawPlane(offset, constraint->mPlane.GetNormal(), Color::sCyan, 1.0f);
  566. // Draw 2nd constraint plane
  567. if (other_constraint != nullptr)
  568. DebugRenderer::sInstance->DrawPlane(offset, other_constraint->mPlane.GetNormal(), Color::sBlue, 1.0f);
  569. // Draw starting velocity
  570. DebugRenderer::sInstance->DrawArrow(offset, offset + velocity, Color::sGreen, 0.05f);
  571. // Draw resulting velocity
  572. DebugRenderer::sInstance->DrawArrow(offset, offset + new_velocity, Color::sRed, 0.05f);
  573. }
  574. #endif // JPH_DEBUG_RENDERER
  575. // Update the velocity
  576. velocity = new_velocity;
  577. // Add the contact to the list so that next iteration we can avoid violating it again
  578. previous_contacts[num_previous_contacts] = constraint;
  579. num_previous_contacts++;
  580. // Check early out
  581. if (constraint->mProjectedVelocity < 1.0e-8f // Constraint should not be pushing, otherwise there may be other constraints that are pushing us
  582. && velocity.LengthSq() < 1.0e-8f) // There's not enough velocity left
  583. return;
  584. // If the constraint has velocity we accept the new velocity, otherwise check that we didn't reverse velocity
  585. if (!constraint->mLinearVelocity.IsNearZero(1.0e-8f))
  586. last_velocity = constraint->mLinearVelocity;
  587. else if (velocity.Dot(last_velocity) < 0.0f)
  588. return;
  589. }
  590. }
  591. void CharacterVirtual::UpdateSupportingContact(bool inSkipContactVelocityCheck, TempAllocator &inAllocator)
  592. {
  593. // Flag contacts as having a collision if they're close enough but ignore contacts we're moving away from.
  594. // Note that if we did MoveShape before we want to preserve any contacts that it marked as colliding
  595. for (Contact &c : mActiveContacts)
  596. if (!c.mWasDiscarded
  597. && !c.mHadCollision
  598. && c.mDistance < mCollisionTolerance
  599. && (inSkipContactVelocityCheck || c.mSurfaceNormal.Dot(mLinearVelocity - c.mLinearVelocity) <= 1.0e-4f))
  600. {
  601. if (ValidateContact(c))
  602. c.mHadCollision = true;
  603. else
  604. c.mWasDiscarded = true;
  605. }
  606. // Calculate transform that takes us to character local space
  607. RMat44 inv_transform = RMat44::sInverseRotationTranslation(mRotation, mPosition);
  608. // Determine if we're supported or not
  609. int num_supported = 0;
  610. int num_sliding = 0;
  611. int num_avg_normal = 0;
  612. Vec3 avg_normal = Vec3::sZero();
  613. Vec3 avg_velocity = Vec3::sZero();
  614. const Contact *supporting_contact = nullptr;
  615. float max_cos_angle = -FLT_MAX;
  616. const Contact *deepest_contact = nullptr;
  617. float smallest_distance = FLT_MAX;
  618. for (const Contact &c : mActiveContacts)
  619. if (c.mHadCollision)
  620. {
  621. // Calculate the angle between the plane normal and the up direction
  622. float cos_angle = c.mSurfaceNormal.Dot(mUp);
  623. // Find the deepest contact
  624. if (c.mDistance < smallest_distance)
  625. {
  626. deepest_contact = &c;
  627. smallest_distance = c.mDistance;
  628. }
  629. // If this contact is in front of our plane, we cannot be supported by it
  630. if (mSupportingVolume.SignedDistance(Vec3(inv_transform * c.mPosition)) > 0.0f)
  631. continue;
  632. // Find the contact with the normal that is pointing most upwards and store it
  633. if (max_cos_angle < cos_angle)
  634. {
  635. supporting_contact = &c;
  636. max_cos_angle = cos_angle;
  637. }
  638. // Check if this is a sliding or supported contact
  639. bool is_supported = mCosMaxSlopeAngle > cNoMaxSlopeAngle || cos_angle >= mCosMaxSlopeAngle;
  640. if (is_supported)
  641. num_supported++;
  642. else
  643. num_sliding++;
  644. // If the angle between the two is less than 85 degrees we also use it to calculate the average normal
  645. if (cos_angle >= 0.08f)
  646. {
  647. avg_normal += c.mSurfaceNormal;
  648. num_avg_normal++;
  649. // For static or dynamic objects or for contacts that don't support us just take the contact velocity
  650. if (c.mMotionTypeB != EMotionType::Kinematic || !is_supported)
  651. avg_velocity += c.mLinearVelocity;
  652. else
  653. {
  654. // For keyframed objects that support us calculate the velocity at our position rather than at the contact position so that we properly follow the object
  655. // Note that we don't just take the point velocity because a point on an object with angular velocity traces an arc,
  656. // so if you just take point velocity * delta time you get an error that accumulates over time
  657. // Determine center of mass and angular velocity
  658. Vec3 angular_velocity;
  659. RVec3 com;
  660. {
  661. BodyLockRead lock(mSystem->GetBodyLockInterface(), c.mBodyB);
  662. if (lock.SucceededAndIsInBroadPhase())
  663. {
  664. const Body &body = lock.GetBody();
  665. // Get adjusted body velocity
  666. Vec3 linear_velocity;
  667. GetAdjustedBodyVelocity(body, linear_velocity, angular_velocity);
  668. // Add the linear velocity to the average velocity
  669. avg_velocity += linear_velocity;
  670. com = body.GetCenterOfMassPosition();
  671. }
  672. else
  673. {
  674. angular_velocity = Vec3::sZero();
  675. com = RVec3::sZero();
  676. }
  677. }
  678. // Get angular velocity
  679. float angular_velocity_len_sq = angular_velocity.LengthSq();
  680. if (angular_velocity_len_sq > 1.0e-12f)
  681. {
  682. float angular_velocity_len = sqrt(angular_velocity_len_sq);
  683. // Calculate the rotation that the object will make in the time step
  684. Quat rotation = Quat::sRotation(angular_velocity / angular_velocity_len, angular_velocity_len * mLastDeltaTime);
  685. // Calculate where the new contact position will be
  686. RVec3 new_position = com + rotation * Vec3(mPosition - com);
  687. // Calculate the velocity
  688. avg_velocity += Vec3(new_position - mPosition) / mLastDeltaTime;
  689. }
  690. }
  691. }
  692. }
  693. // Take either the most supporting contact or the deepest contact
  694. const Contact *best_contact = supporting_contact != nullptr? supporting_contact : deepest_contact;
  695. // Calculate average normal and velocity
  696. if (num_avg_normal >= 1)
  697. {
  698. mGroundNormal = avg_normal.Normalized();
  699. mGroundVelocity = avg_velocity / float(num_avg_normal);
  700. }
  701. else if (best_contact != nullptr)
  702. {
  703. mGroundNormal = best_contact->mSurfaceNormal;
  704. mGroundVelocity = best_contact->mLinearVelocity;
  705. }
  706. else
  707. {
  708. mGroundNormal = Vec3::sZero();
  709. mGroundVelocity = Vec3::sZero();
  710. }
  711. // Copy contact properties
  712. if (best_contact != nullptr)
  713. {
  714. mGroundBodyID = best_contact->mBodyB;
  715. mGroundBodySubShapeID = best_contact->mSubShapeIDB;
  716. mGroundPosition = best_contact->mPosition;
  717. mGroundMaterial = best_contact->mMaterial;
  718. mGroundUserData = best_contact->mUserData;
  719. }
  720. else
  721. {
  722. mGroundBodyID = BodyID();
  723. mGroundBodySubShapeID = SubShapeID();
  724. mGroundPosition = RVec3::sZero();
  725. mGroundMaterial = PhysicsMaterial::sDefault;
  726. mGroundUserData = 0;
  727. }
  728. // Determine ground state
  729. if (num_supported > 0)
  730. {
  731. // We made contact with something that supports us
  732. mGroundState = EGroundState::OnGround;
  733. }
  734. else if (num_sliding > 0)
  735. {
  736. // If we're sliding we may actually be standing on multiple sliding contacts in such a way that we can't slide off, in this case we're also supported
  737. // Convert the contacts into constraints
  738. TempContactList contacts(mActiveContacts.begin(), mActiveContacts.end(), inAllocator);
  739. ConstraintList constraints(inAllocator);
  740. constraints.reserve(contacts.size() * 2);
  741. DetermineConstraints(contacts, constraints);
  742. // Solve the displacement using these constraints, this is used to check if we didn't move at all because we are supported
  743. Vec3 displacement;
  744. float time_simulated;
  745. IgnoredContactList ignored_contacts(inAllocator);
  746. ignored_contacts.reserve(contacts.size());
  747. SolveConstraints(-mUp, 1.0f, 1.0f, constraints, ignored_contacts, time_simulated, displacement, inAllocator);
  748. // If we're blocked then we're supported, otherwise we're sliding
  749. float min_required_displacement_sq = Square(0.6f * mLastDeltaTime);
  750. if (time_simulated < 0.001f || displacement.LengthSq() < min_required_displacement_sq)
  751. mGroundState = EGroundState::OnGround;
  752. else
  753. mGroundState = EGroundState::OnSteepGround;
  754. }
  755. else
  756. {
  757. // Not supported by anything
  758. mGroundState = best_contact != nullptr? EGroundState::NotSupported : EGroundState::InAir;
  759. }
  760. }
  761. void CharacterVirtual::StoreActiveContacts(const TempContactList &inContacts, TempAllocator &inAllocator)
  762. {
  763. mActiveContacts.assign(inContacts.begin(), inContacts.end());
  764. UpdateSupportingContact(true, inAllocator);
  765. }
  766. void CharacterVirtual::MoveShape(RVec3 &ioPosition, Vec3Arg inVelocity, float inDeltaTime, ContactList *outActiveContacts, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter, TempAllocator &inAllocator
  767. #ifdef JPH_DEBUG_RENDERER
  768. , bool inDrawConstraints
  769. #endif // JPH_DEBUG_RENDERER
  770. ) const
  771. {
  772. Vec3 movement_direction = inVelocity.NormalizedOr(Vec3::sZero());
  773. float time_remaining = inDeltaTime;
  774. for (uint iteration = 0; iteration < mMaxCollisionIterations && time_remaining >= mMinTimeRemaining; iteration++)
  775. {
  776. // Determine contacts in the neighborhood
  777. TempContactList contacts(inAllocator);
  778. contacts.reserve(mMaxNumHits);
  779. GetContactsAtPosition(ioPosition, movement_direction, mShape, contacts, inBroadPhaseLayerFilter, inObjectLayerFilter, inBodyFilter, inShapeFilter);
  780. // Remove contacts with the same body that have conflicting normals
  781. IgnoredContactList ignored_contacts(inAllocator);
  782. ignored_contacts.reserve(contacts.size());
  783. RemoveConflictingContacts(contacts, ignored_contacts);
  784. // Convert contacts into constraints
  785. ConstraintList constraints(inAllocator);
  786. constraints.reserve(contacts.size() * 2);
  787. DetermineConstraints(contacts, constraints);
  788. #ifdef JPH_DEBUG_RENDERER
  789. bool draw_constraints = inDrawConstraints && iteration == 0;
  790. if (draw_constraints)
  791. {
  792. for (const Constraint &c : constraints)
  793. {
  794. // Draw contact point
  795. DebugRenderer::sInstance->DrawMarker(c.mContact->mPosition, Color::sYellow, 0.05f);
  796. Vec3 dist_to_plane = -c.mPlane.GetConstant() * c.mPlane.GetNormal();
  797. // Draw arrow towards surface that we're hitting
  798. DebugRenderer::sInstance->DrawArrow(c.mContact->mPosition, c.mContact->mPosition - dist_to_plane, Color::sYellow, 0.05f);
  799. // Draw plane around the player position indicating the space that we can move
  800. DebugRenderer::sInstance->DrawPlane(mPosition + dist_to_plane, c.mPlane.GetNormal(), Color::sCyan, 1.0f);
  801. DebugRenderer::sInstance->DrawArrow(mPosition + dist_to_plane, mPosition + dist_to_plane + c.mContact->mSurfaceNormal, Color::sRed, 0.05f);
  802. }
  803. }
  804. #endif // JPH_DEBUG_RENDERER
  805. // Solve the displacement using these constraints
  806. Vec3 displacement;
  807. float time_simulated;
  808. SolveConstraints(inVelocity, inDeltaTime, time_remaining, constraints, ignored_contacts, time_simulated, displacement, inAllocator
  809. #ifdef JPH_DEBUG_RENDERER
  810. , draw_constraints
  811. #endif // JPH_DEBUG_RENDERER
  812. );
  813. // Store the contacts now that the colliding ones have been marked
  814. if (outActiveContacts != nullptr)
  815. outActiveContacts->assign(contacts.begin(), contacts.end());
  816. // Do a sweep to test if the path is really unobstructed
  817. Contact cast_contact;
  818. if (GetFirstContactForSweep(ioPosition, displacement, cast_contact, ignored_contacts, inBroadPhaseLayerFilter, inObjectLayerFilter, inBodyFilter, inShapeFilter))
  819. {
  820. displacement *= cast_contact.mFraction;
  821. time_simulated *= cast_contact.mFraction;
  822. }
  823. // Update the position
  824. ioPosition += displacement;
  825. time_remaining -= time_simulated;
  826. // If the displacement during this iteration was too small we assume we cannot further progress this update
  827. if (displacement.LengthSq() < 1.0e-8f)
  828. break;
  829. }
  830. }
  831. Vec3 CharacterVirtual::CancelVelocityTowardsSteepSlopes(Vec3Arg inDesiredVelocity) const
  832. {
  833. // If we're not pushing against a steep slope, return the desired velocity
  834. // Note: This is important as WalkStairs overrides the ground state to OnGround when its first check fails but the second succeeds
  835. if (mGroundState == CharacterVirtual::EGroundState::OnGround
  836. || mGroundState == CharacterVirtual::EGroundState::InAir)
  837. return inDesiredVelocity;
  838. Vec3 desired_velocity = inDesiredVelocity;
  839. for (const Contact &c : mActiveContacts)
  840. if (c.mHadCollision
  841. && IsSlopeTooSteep(c.mSurfaceNormal))
  842. {
  843. Vec3 normal = c.mSurfaceNormal;
  844. // Remove normal vertical component
  845. normal -= normal.Dot(mUp) * mUp;
  846. // Cancel horizontal movement in opposite direction
  847. float dot = normal.Dot(desired_velocity);
  848. if (dot < 0.0f)
  849. desired_velocity -= (dot * normal) / normal.LengthSq();
  850. }
  851. return desired_velocity;
  852. }
  853. void CharacterVirtual::Update(float inDeltaTime, Vec3Arg inGravity, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter, TempAllocator &inAllocator)
  854. {
  855. // If there's no delta time, we don't need to do anything
  856. if (inDeltaTime <= 0.0f)
  857. return;
  858. // Remember delta time for checking if we're supported by the ground
  859. mLastDeltaTime = inDeltaTime;
  860. // Slide the shape through the world
  861. MoveShape(mPosition, mLinearVelocity, inDeltaTime, &mActiveContacts, inBroadPhaseLayerFilter, inObjectLayerFilter, inBodyFilter, inShapeFilter, inAllocator
  862. #ifdef JPH_DEBUG_RENDERER
  863. , sDrawConstraints
  864. #endif // JPH_DEBUG_RENDERER
  865. );
  866. // Determine the object that we're standing on
  867. UpdateSupportingContact(false, inAllocator);
  868. // If we're on the ground
  869. if (!mGroundBodyID.IsInvalid() && mMass > 0.0f)
  870. {
  871. // Add the impulse to the ground due to gravity: P = F dt = M g dt
  872. float normal_dot_gravity = mGroundNormal.Dot(inGravity);
  873. if (normal_dot_gravity < 0.0f)
  874. {
  875. Vec3 world_impulse = -(mMass * normal_dot_gravity / inGravity.Length() * inDeltaTime) * inGravity;
  876. mSystem->GetBodyInterface().AddImpulse(mGroundBodyID, world_impulse, mGroundPosition);
  877. }
  878. }
  879. }
  880. void CharacterVirtual::RefreshContacts(const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter, TempAllocator &inAllocator)
  881. {
  882. // Determine the contacts
  883. TempContactList contacts(inAllocator);
  884. contacts.reserve(mMaxNumHits);
  885. GetContactsAtPosition(mPosition, mLinearVelocity.NormalizedOr(Vec3::sZero()), mShape, contacts, inBroadPhaseLayerFilter, inObjectLayerFilter, inBodyFilter, inShapeFilter);
  886. StoreActiveContacts(contacts, inAllocator);
  887. }
  888. void CharacterVirtual::MoveToContact(RVec3Arg inPosition, const Contact &inContact, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter, TempAllocator &inAllocator)
  889. {
  890. // Set the new position
  891. SetPosition(inPosition);
  892. // Determine the contacts
  893. TempContactList contacts(inAllocator);
  894. contacts.reserve(mMaxNumHits + 1); // +1 because we can add one extra below
  895. GetContactsAtPosition(mPosition, mLinearVelocity.NormalizedOr(Vec3::sZero()), mShape, contacts, inBroadPhaseLayerFilter, inObjectLayerFilter, inBodyFilter, inShapeFilter);
  896. // Ensure that we mark inContact as colliding
  897. bool found_contact = false;
  898. for (Contact &c : contacts)
  899. if (c.mBodyB == inContact.mBodyB
  900. && c.mSubShapeIDB == inContact.mSubShapeIDB)
  901. {
  902. c.mHadCollision = true;
  903. found_contact = true;
  904. }
  905. if (!found_contact)
  906. {
  907. contacts.push_back(inContact);
  908. Contact &copy = contacts.back();
  909. copy.mHadCollision = true;
  910. }
  911. StoreActiveContacts(contacts, inAllocator);
  912. JPH_ASSERT(mGroundState != EGroundState::InAir);
  913. }
  914. bool CharacterVirtual::SetShape(const Shape *inShape, float inMaxPenetrationDepth, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter, TempAllocator &inAllocator)
  915. {
  916. if (mShape == nullptr || mSystem == nullptr)
  917. {
  918. // It hasn't been initialized yet
  919. mShape = inShape;
  920. return true;
  921. }
  922. if (inShape != mShape && inShape != nullptr)
  923. {
  924. if (inMaxPenetrationDepth < FLT_MAX)
  925. {
  926. // Check collision around the new shape
  927. TempContactList contacts(inAllocator);
  928. contacts.reserve(mMaxNumHits);
  929. GetContactsAtPosition(mPosition, mLinearVelocity.NormalizedOr(Vec3::sZero()), inShape, contacts, inBroadPhaseLayerFilter, inObjectLayerFilter, inBodyFilter, inShapeFilter);
  930. // Test if this results in penetration, if so cancel the transition
  931. for (const Contact &c : contacts)
  932. if (c.mDistance < -inMaxPenetrationDepth)
  933. return false;
  934. StoreActiveContacts(contacts, inAllocator);
  935. }
  936. // Set new shape
  937. mShape = inShape;
  938. }
  939. return mShape == inShape;
  940. }
  941. bool CharacterVirtual::CanWalkStairs(Vec3Arg inLinearVelocity) const
  942. {
  943. // We can only walk stairs if we're supported
  944. if (!IsSupported())
  945. return false;
  946. // Check if there's enough horizontal velocity to trigger a stair walk
  947. Vec3 horizontal_velocity = inLinearVelocity - inLinearVelocity.Dot(mUp) * mUp;
  948. if (horizontal_velocity.IsNearZero(1.0e-6f))
  949. return false;
  950. // Check contacts for steep slopes
  951. for (const Contact &c : mActiveContacts)
  952. if (c.mHadCollision
  953. && c.mSurfaceNormal.Dot(horizontal_velocity - c.mLinearVelocity) < 0.0f // Pushing into the contact
  954. && IsSlopeTooSteep(c.mSurfaceNormal)) // Slope too steep
  955. return true;
  956. return false;
  957. }
  958. bool CharacterVirtual::WalkStairs(float inDeltaTime, Vec3Arg inStepUp, Vec3Arg inStepForward, Vec3Arg inStepForwardTest, Vec3Arg inStepDownExtra, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter, TempAllocator &inAllocator)
  959. {
  960. // Move up
  961. Vec3 up = inStepUp;
  962. Contact contact;
  963. IgnoredContactList dummy_ignored_contacts(inAllocator);
  964. if (GetFirstContactForSweep(mPosition, up, contact, dummy_ignored_contacts, inBroadPhaseLayerFilter, inObjectLayerFilter, inBodyFilter, inShapeFilter))
  965. {
  966. if (contact.mFraction < 1.0e-6f)
  967. return false; // No movement, cancel
  968. // Limit up movement to the first contact point
  969. up *= contact.mFraction;
  970. }
  971. RVec3 up_position = mPosition + up;
  972. #ifdef JPH_DEBUG_RENDERER
  973. // Draw sweep up
  974. if (sDrawWalkStairs)
  975. DebugRenderer::sInstance->DrawArrow(mPosition, up_position, Color::sWhite, 0.01f);
  976. #endif // JPH_DEBUG_RENDERER
  977. // Horizontal movement
  978. RVec3 new_position = up_position;
  979. MoveShape(new_position, inStepForward / inDeltaTime, inDeltaTime, nullptr, inBroadPhaseLayerFilter, inObjectLayerFilter, inBodyFilter, inShapeFilter, inAllocator);
  980. float horizontal_movement_sq = Vec3(new_position - up_position).LengthSq();
  981. if (horizontal_movement_sq < 1.0e-8f)
  982. return false; // No movement, cancel
  983. #ifdef JPH_DEBUG_RENDERER
  984. // Draw horizontal sweep
  985. if (sDrawWalkStairs)
  986. DebugRenderer::sInstance->DrawArrow(up_position, new_position, Color::sWhite, 0.01f);
  987. #endif // JPH_DEBUG_RENDERER
  988. // Move down towards the floor.
  989. // Note that we travel the same amount down as we travelled up with the character padding and the specified extra
  990. // If we don't add the character padding, we may miss the floor (note that GetFirstContactForSweep will subtract the padding when it finds a hit)
  991. Vec3 down = -up - mCharacterPadding * mUp + inStepDownExtra;
  992. if (!GetFirstContactForSweep(new_position, down, contact, dummy_ignored_contacts, inBroadPhaseLayerFilter, inObjectLayerFilter, inBodyFilter, inShapeFilter))
  993. return false; // No floor found, we're in mid air, cancel stair walk
  994. #ifdef JPH_DEBUG_RENDERER
  995. // Draw sweep down
  996. if (sDrawWalkStairs)
  997. {
  998. RVec3 debug_pos = new_position + contact.mFraction * down;
  999. DebugRenderer::sInstance->DrawArrow(new_position, debug_pos, Color::sWhite, 0.01f);
  1000. DebugRenderer::sInstance->DrawArrow(contact.mPosition, contact.mPosition + contact.mSurfaceNormal, Color::sWhite, 0.01f);
  1001. mShape->Draw(DebugRenderer::sInstance, GetCenterOfMassTransform(debug_pos, mRotation, mShape), Vec3::sReplicate(1.0f), Color::sWhite, false, true);
  1002. }
  1003. #endif // JPH_DEBUG_RENDERER
  1004. // Test for floor that will support the character
  1005. if (IsSlopeTooSteep(contact.mSurfaceNormal))
  1006. {
  1007. // If no test position was provided, we cancel the stair walk
  1008. if (inStepForwardTest.IsNearZero())
  1009. return false;
  1010. // Delta time may be very small, so it may be that we hit the edge of a step and the normal is too horizontal.
  1011. // In order to judge if the floor is flat further along the sweep, we test again for a floor at inStepForwardTest
  1012. // and check if the normal is valid there.
  1013. RVec3 test_position = up_position;
  1014. MoveShape(test_position, inStepForwardTest / inDeltaTime, inDeltaTime, nullptr, inBroadPhaseLayerFilter, inObjectLayerFilter, inBodyFilter, inShapeFilter, inAllocator);
  1015. float test_horizontal_movement_sq = Vec3(test_position - up_position).LengthSq();
  1016. if (test_horizontal_movement_sq <= horizontal_movement_sq + 1.0e-8f)
  1017. return false; // We didn't move any further than in the previous test
  1018. #ifdef JPH_DEBUG_RENDERER
  1019. // Draw 2nd sweep horizontal
  1020. if (sDrawWalkStairs)
  1021. DebugRenderer::sInstance->DrawArrow(up_position, test_position, Color::sCyan, 0.01f);
  1022. #endif // JPH_DEBUG_RENDERER
  1023. // Then sweep down
  1024. Contact test_contact;
  1025. if (!GetFirstContactForSweep(test_position, down, test_contact, dummy_ignored_contacts, inBroadPhaseLayerFilter, inObjectLayerFilter, inBodyFilter, inShapeFilter))
  1026. return false;
  1027. #ifdef JPH_DEBUG_RENDERER
  1028. // Draw 2nd sweep down
  1029. if (sDrawWalkStairs)
  1030. {
  1031. RVec3 debug_pos = test_position + test_contact.mFraction * down;
  1032. DebugRenderer::sInstance->DrawArrow(test_position, debug_pos, Color::sCyan, 0.01f);
  1033. DebugRenderer::sInstance->DrawArrow(test_contact.mPosition, test_contact.mPosition + test_contact.mSurfaceNormal, Color::sCyan, 0.01f);
  1034. mShape->Draw(DebugRenderer::sInstance, GetCenterOfMassTransform(debug_pos, mRotation, mShape), Vec3::sReplicate(1.0f), Color::sCyan, false, true);
  1035. }
  1036. #endif // JPH_DEBUG_RENDERER
  1037. if (IsSlopeTooSteep(test_contact.mSurfaceNormal))
  1038. return false;
  1039. }
  1040. // Calculate new down position
  1041. down *= contact.mFraction;
  1042. new_position += down;
  1043. // Move the character to the new location
  1044. MoveToContact(new_position, contact, inBroadPhaseLayerFilter, inObjectLayerFilter, inBodyFilter, inShapeFilter, inAllocator);
  1045. // Override ground state to 'on ground', it is possible that the contact normal is too steep, but in this case the inStepForwardTest has found a contact normal that is not too steep
  1046. mGroundState = EGroundState::OnGround;
  1047. return true;
  1048. }
  1049. bool CharacterVirtual::StickToFloor(Vec3Arg inStepDown, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter, TempAllocator &inAllocator)
  1050. {
  1051. // Try to find the floor
  1052. Contact contact;
  1053. IgnoredContactList dummy_ignored_contacts(inAllocator);
  1054. if (!GetFirstContactForSweep(mPosition, inStepDown, contact, dummy_ignored_contacts, inBroadPhaseLayerFilter, inObjectLayerFilter, inBodyFilter, inShapeFilter))
  1055. return false; // If no floor found, don't update our position
  1056. // Calculate new position
  1057. RVec3 new_position = mPosition + contact.mFraction * inStepDown;
  1058. #ifdef JPH_DEBUG_RENDERER
  1059. // Draw sweep down
  1060. if (sDrawStickToFloor)
  1061. {
  1062. DebugRenderer::sInstance->DrawArrow(mPosition, new_position, Color::sOrange, 0.01f);
  1063. mShape->Draw(DebugRenderer::sInstance, GetCenterOfMassTransform(new_position, mRotation, mShape), Vec3::sReplicate(1.0f), Color::sOrange, false, true);
  1064. }
  1065. #endif // JPH_DEBUG_RENDERER
  1066. // Move the character to the new location
  1067. MoveToContact(new_position, contact, inBroadPhaseLayerFilter, inObjectLayerFilter, inBodyFilter, inShapeFilter, inAllocator);
  1068. return true;
  1069. }
  1070. void CharacterVirtual::ExtendedUpdate(float inDeltaTime, Vec3Arg inGravity, const ExtendedUpdateSettings &inSettings, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter, TempAllocator &inAllocator)
  1071. {
  1072. // Update the velocity
  1073. Vec3 desired_velocity = mLinearVelocity;
  1074. mLinearVelocity = CancelVelocityTowardsSteepSlopes(desired_velocity);
  1075. // Remember old position
  1076. RVec3 old_position = mPosition;
  1077. // Track if on ground before the update
  1078. bool ground_to_air = IsSupported();
  1079. // Update the character position (instant, do not have to wait for physics update)
  1080. Update(inDeltaTime, inGravity, inBroadPhaseLayerFilter, inObjectLayerFilter, inBodyFilter, inShapeFilter, inAllocator);
  1081. // ... and that we got into air after
  1082. if (IsSupported())
  1083. ground_to_air = false;
  1084. // If stick to floor enabled and we're going from supported to not supported
  1085. if (ground_to_air && !inSettings.mStickToFloorStepDown.IsNearZero())
  1086. {
  1087. // If we're not moving up, stick to the floor
  1088. float velocity = Vec3(mPosition - old_position).Dot(mUp) / inDeltaTime;
  1089. if (velocity <= 1.0e-6f)
  1090. StickToFloor(inSettings.mStickToFloorStepDown, inBroadPhaseLayerFilter, inObjectLayerFilter, inBodyFilter, inShapeFilter, inAllocator);
  1091. }
  1092. // If walk stairs enabled
  1093. if (!inSettings.mWalkStairsStepUp.IsNearZero())
  1094. {
  1095. // Calculate how much we wanted to move horizontally
  1096. Vec3 desired_horizontal_step = desired_velocity * inDeltaTime;
  1097. desired_horizontal_step -= desired_horizontal_step.Dot(mUp) * mUp;
  1098. float desired_horizontal_step_len = desired_horizontal_step.Length();
  1099. if (desired_horizontal_step_len > 0.0f)
  1100. {
  1101. // Calculate how much we moved horizontally
  1102. Vec3 achieved_horizontal_step = Vec3(mPosition - old_position);
  1103. achieved_horizontal_step -= achieved_horizontal_step.Dot(mUp) * mUp;
  1104. // Only count movement in the direction of the desired movement
  1105. // (otherwise we find it ok if we're sliding downhill while we're trying to climb uphill)
  1106. Vec3 step_forward_normalized = desired_horizontal_step / desired_horizontal_step_len;
  1107. achieved_horizontal_step = max(0.0f, achieved_horizontal_step.Dot(step_forward_normalized)) * step_forward_normalized;
  1108. float achieved_horizontal_step_len = achieved_horizontal_step.Length();
  1109. // If we didn't move as far as we wanted and we're against a slope that's too steep
  1110. if (achieved_horizontal_step_len + 1.0e-4f < desired_horizontal_step_len
  1111. && CanWalkStairs(desired_velocity))
  1112. {
  1113. // Calculate how much we should step forward
  1114. // Note that we clamp the step forward to a minimum distance. This is done because at very high frame rates the delta time
  1115. // may be very small, causing a very small step forward. If the step becomes small enough, we may not move far enough
  1116. // horizontally to actually end up at the top of the step.
  1117. Vec3 step_forward = step_forward_normalized * max(inSettings.mWalkStairsMinStepForward, desired_horizontal_step_len - achieved_horizontal_step_len);
  1118. // Calculate how far to scan ahead for a floor. This is only used in case the floor normal at step_forward is too steep.
  1119. // In that case an additional check will be performed at this distance to check if that normal is not too steep.
  1120. // Start with the ground normal in the horizontal plane and normalizing it
  1121. Vec3 step_forward_test = -mGroundNormal;
  1122. step_forward_test -= step_forward_test.Dot(mUp) * mUp;
  1123. step_forward_test = step_forward_test.NormalizedOr(step_forward_normalized);
  1124. // If this normalized vector and the character forward vector is bigger than a preset angle, we use the character forward vector instead of the ground normal
  1125. // to do our forward test
  1126. if (step_forward_test.Dot(step_forward_normalized) < inSettings.mWalkStairsCosAngleForwardContact)
  1127. step_forward_test = step_forward_normalized;
  1128. // Calculate the correct magnitude for the test vector
  1129. step_forward_test *= inSettings.mWalkStairsStepForwardTest;
  1130. WalkStairs(inDeltaTime, inSettings.mWalkStairsStepUp, step_forward, step_forward_test, inSettings.mWalkStairsStepDownExtra, inBroadPhaseLayerFilter, inObjectLayerFilter, inBodyFilter, inShapeFilter, inAllocator);
  1131. }
  1132. }
  1133. }
  1134. }
  1135. void CharacterVirtual::Contact::SaveState(StateRecorder &inStream) const
  1136. {
  1137. inStream.Write(mPosition);
  1138. inStream.Write(mLinearVelocity);
  1139. inStream.Write(mContactNormal);
  1140. inStream.Write(mSurfaceNormal);
  1141. inStream.Write(mDistance);
  1142. inStream.Write(mFraction);
  1143. inStream.Write(mBodyB);
  1144. inStream.Write(mSubShapeIDB);
  1145. inStream.Write(mMotionTypeB);
  1146. inStream.Write(mHadCollision);
  1147. inStream.Write(mWasDiscarded);
  1148. inStream.Write(mCanPushCharacter);
  1149. // Cannot store user data (may be a pointer) and material
  1150. }
  1151. void CharacterVirtual::Contact::RestoreState(StateRecorder &inStream)
  1152. {
  1153. inStream.Read(mPosition);
  1154. inStream.Read(mLinearVelocity);
  1155. inStream.Read(mContactNormal);
  1156. inStream.Read(mSurfaceNormal);
  1157. inStream.Read(mDistance);
  1158. inStream.Read(mFraction);
  1159. inStream.Read(mBodyB);
  1160. inStream.Read(mSubShapeIDB);
  1161. inStream.Read(mMotionTypeB);
  1162. inStream.Read(mHadCollision);
  1163. inStream.Read(mWasDiscarded);
  1164. inStream.Read(mCanPushCharacter);
  1165. mUserData = 0; // Cannot restore user data
  1166. mMaterial = PhysicsMaterial::sDefault; // Cannot restore material
  1167. }
  1168. void CharacterVirtual::SaveState(StateRecorder &inStream) const
  1169. {
  1170. CharacterBase::SaveState(inStream);
  1171. inStream.Write(mPosition);
  1172. inStream.Write(mRotation);
  1173. inStream.Write(mLinearVelocity);
  1174. inStream.Write(mLastDeltaTime);
  1175. inStream.Write(mMaxHitsExceeded);
  1176. // Store contacts that had collision, we're using it at the beginning of the step in CancelVelocityTowardsSteepSlopes
  1177. uint32 num_contacts = 0;
  1178. for (const Contact &c : mActiveContacts)
  1179. if (c.mHadCollision)
  1180. ++num_contacts;
  1181. inStream.Write(num_contacts);
  1182. for (const Contact &c : mActiveContacts)
  1183. if (c.mHadCollision)
  1184. c.SaveState(inStream);
  1185. }
  1186. void CharacterVirtual::RestoreState(StateRecorder &inStream)
  1187. {
  1188. CharacterBase::RestoreState(inStream);
  1189. inStream.Read(mPosition);
  1190. inStream.Read(mRotation);
  1191. inStream.Read(mLinearVelocity);
  1192. inStream.Read(mLastDeltaTime);
  1193. inStream.Read(mMaxHitsExceeded);
  1194. // When validating remove contacts that don't have collision since we didn't save them
  1195. if (inStream.IsValidating())
  1196. for (int i = (int)mActiveContacts.size() - 1; i >= 0; --i)
  1197. if (!mActiveContacts[i].mHadCollision)
  1198. mActiveContacts.erase(mActiveContacts.begin() + i);
  1199. uint32 num_contacts = (uint32)mActiveContacts.size();
  1200. inStream.Read(num_contacts);
  1201. mActiveContacts.resize(num_contacts);
  1202. for (Contact &c : mActiveContacts)
  1203. c.RestoreState(inStream);
  1204. }
  1205. JPH_NAMESPACE_END