CharacterVirtual.cpp 50 KB

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