CharacterVirtual.cpp 77 KB

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