CharacterVirtual.cpp 64 KB

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