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 = contact_velocity.Dot(normal) * normal; // Project the contact velocity on the new normal so that both planes push at an equal rate
  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. 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. }
  810. // Check if we found a 2nd constraint
  811. if (other_constraint != nullptr)
  812. {
  813. // Calculate the sliding direction and project the new velocity onto that sliding direction
  814. Vec3 other_normal = other_constraint->mPlane.GetNormal();
  815. Vec3 slide_dir = plane_normal.Cross(other_normal).Normalized();
  816. Vec3 velocity_in_slide_dir = new_velocity.Dot(slide_dir) * slide_dir;
  817. // 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
  818. constraint->mLinearVelocity -= min(0.0f, constraint->mLinearVelocity.Dot(other_normal)) * other_normal;
  819. // 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
  820. other_constraint->mLinearVelocity -= min(0.0f, other_constraint->mLinearVelocity.Dot(plane_normal)) * plane_normal;
  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