CharacterVirtual.cpp 73 KB

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