CharacterVirtual.cpp 61 KB

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