CharacterVirtualScene.h 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. // Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
  2. // SPDX-FileCopyrightText: 2025 Jorrit Rouwe
  3. // SPDX-License-Identifier: MIT
  4. #pragma once
  5. // Jolt includes
  6. #include <Jolt/Physics/Collision/Shape/BoxShape.h>
  7. #include <Jolt/Physics/Collision/Shape/MeshShape.h>
  8. #include <Jolt/Physics/Collision/Shape/CapsuleShape.h>
  9. #include <Jolt/Physics/Collision/Shape/RotatedTranslatedShape.h>
  10. #include <Jolt/Physics/Character/CharacterVirtual.h>
  11. #include <Jolt/Physics/Body/BodyCreationSettings.h>
  12. // Local includes
  13. #include "PerformanceTestScene.h"
  14. #include "Layers.h"
  15. // A scene that drops a number of virtual characters on a scene and simulates them
  16. class CharacterVirtualScene : public PerformanceTestScene, public CharacterContactListener
  17. {
  18. public:
  19. virtual const char * GetName() const override
  20. {
  21. return "CharacterVirtual";
  22. }
  23. virtual bool Load(const String &inAssetPath) override
  24. {
  25. const int n = 100;
  26. const float cell_size = 0.5f;
  27. const float max_height = 2.0f;
  28. float center = n * cell_size / 2;
  29. // Create vertices
  30. const int num_vertices = (n + 1) * (n + 1);
  31. VertexList vertices;
  32. vertices.resize(num_vertices);
  33. for (int x = 0; x <= n; ++x)
  34. for (int z = 0; z <= n; ++z)
  35. {
  36. float height = Sin(float(x) * 20.0f / n) * Cos(float(z) * 20.0f / n);
  37. vertices[z * (n + 1) + x] = Float3(cell_size * x, max_height * height, cell_size * z);
  38. }
  39. // Create regular grid of triangles
  40. const int num_triangles = n * n * 2;
  41. IndexedTriangleList indices;
  42. indices.resize(num_triangles);
  43. IndexedTriangle *next = indices.data();
  44. for (int x = 0; x < n; ++x)
  45. for (int z = 0; z < n; ++z)
  46. {
  47. int start = (n + 1) * z + x;
  48. next->mIdx[0] = start;
  49. next->mIdx[1] = start + n + 1;
  50. next->mIdx[2] = start + 1;
  51. next++;
  52. next->mIdx[0] = start + 1;
  53. next->mIdx[1] = start + n + 1;
  54. next->mIdx[2] = start + n + 2;
  55. next++;
  56. }
  57. // Create mesh
  58. BodyCreationSettings mesh(new MeshShapeSettings(vertices, indices), RVec3(Real(-center), 0, Real(-center)), Quat::sIdentity(), EMotionType::Static, Layers::NON_MOVING);
  59. mWorld.push_back(mesh);
  60. // Create pyramid stairs
  61. for (int i = 0; i < 10; ++i)
  62. {
  63. float width = 4.0f - 0.4f * i;
  64. BodyCreationSettings step(new BoxShape(Vec3(width, 0.5f * cStairsStepHeight, width)), RVec3(-4.0_r, -1.0_r + Real(i * cStairsStepHeight), 0), Quat::sIdentity(), EMotionType::Static, Layers::NON_MOVING);
  65. mWorld.push_back(step);
  66. }
  67. // Create wall consisting of vertical pillars
  68. Ref<Shape> wall = new BoxShape(Vec3(0.1f, 2.5f, 0.1f), 0.0f);
  69. for (int z = 0; z < 10; ++z)
  70. {
  71. BodyCreationSettings bcs(wall, RVec3(2.0_r, 1.0_r, 2.0_r + 0.2_r * z), Quat::sIdentity(), EMotionType::Static, Layers::NON_MOVING);
  72. mWorld.push_back(bcs);
  73. }
  74. // Create some dynamic boxes
  75. Ref<Shape> box = new BoxShape(Vec3::sReplicate(0.25f));
  76. for (int x = 0; x < 10; ++x)
  77. for (int z = 0; z < 10; ++z)
  78. {
  79. BodyCreationSettings bcs(box, RVec3(4.0_r * x - 20.0_r, 5.0_r, 4.0_r * z - 20.0_r), Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING);
  80. bcs.mOverrideMassProperties = EOverrideMassProperties::CalculateInertia;
  81. bcs.mMassPropertiesOverride.mMass = 1.0f;
  82. mWorld.push_back(bcs);
  83. }
  84. return true;
  85. }
  86. virtual void StartTest(PhysicsSystem &inPhysicsSystem, EMotionQuality inMotionQuality) override
  87. {
  88. // Construct bodies
  89. BodyInterface &bi = inPhysicsSystem.GetBodyInterface();
  90. for (BodyCreationSettings &bcs : mWorld)
  91. if (bcs.mMotionType == EMotionType::Dynamic)
  92. {
  93. bcs.mMotionQuality = inMotionQuality;
  94. bi.CreateAndAddBody(bcs, EActivation::Activate);
  95. }
  96. else
  97. bi.CreateAndAddBody(bcs, EActivation::DontActivate);
  98. // Construct characters
  99. CharacterID::sSetNextCharacterID();
  100. RefConst<Shape> standing_shape = RotatedTranslatedShapeSettings(Vec3(0, 0.5f * cCharacterHeightStanding + cCharacterRadiusStanding, 0), Quat::sIdentity(), new CapsuleShape(0.5f * cCharacterHeightStanding, cCharacterRadiusStanding)).Create().Get();
  101. RefConst<Shape> inner_standing_shape = RotatedTranslatedShapeSettings(Vec3(0, 0.5f * cCharacterHeightStanding + cCharacterRadiusStanding, 0), Quat::sIdentity(), new CapsuleShape(0.5f * cInnerShapeFraction * cCharacterHeightStanding, cInnerShapeFraction * cCharacterRadiusStanding)).Create().Get();
  102. for (int y = 0; y < cNumCharactersY; ++y)
  103. for (int x = 0; x < cNumCharactersX; ++x)
  104. {
  105. Ref<CharacterVirtualSettings> settings = new CharacterVirtualSettings();
  106. settings->mShape = standing_shape;
  107. settings->mSupportingVolume = Plane(Vec3::sAxisY(), -cCharacterRadiusStanding); // Accept contacts that touch the lower sphere of the capsule
  108. settings->mInnerBodyShape = inner_standing_shape;
  109. settings->mInnerBodyLayer = Layers::MOVING;
  110. Ref<CharacterVirtual> character = new CharacterVirtual(settings, RVec3(4.0_r * x - 20.0_r, 2.0_r, 4.0_r * y - 20.0_r), Quat::sIdentity(), 0, &inPhysicsSystem);
  111. character->SetCharacterVsCharacterCollision(&mCharacterVsCharacterCollision);
  112. character->SetListener(this);
  113. mCharacters.push_back(character);
  114. mCharacterVsCharacterCollision.Add(character);
  115. }
  116. // Start at time 0
  117. mTime = 0.0f;
  118. mHash = HashBytes(nullptr, 0);
  119. }
  120. virtual void UpdateTest(PhysicsSystem &inPhysicsSystem, TempAllocator &ioTempAllocator, float inDeltaTime) override
  121. {
  122. // Change direction every 2 seconds
  123. mTime += inDeltaTime;
  124. uint64 count = uint64(mTime / 2.0f) * cNumCharactersX * cNumCharactersY;
  125. for (CharacterVirtual *ch : mCharacters)
  126. {
  127. // Calculate new vertical velocity
  128. Vec3 new_velocity;
  129. if (ch->GetGroundState() == CharacterVirtual::EGroundState::OnGround // If on ground
  130. && ch->GetLinearVelocity().GetY() < 0.1f) // And not moving away from ground
  131. new_velocity = Vec3::sZero();
  132. else
  133. new_velocity = ch->GetLinearVelocity() * Vec3(0, 1, 0);
  134. new_velocity += inPhysicsSystem.GetGravity() * inDeltaTime;
  135. // Deterministic random input
  136. uint64 hash = Hash<uint64> {} (count);
  137. int x = int(hash % 10);
  138. int y = int((hash / 10) % 10);
  139. int speed = int((hash / 100) % 10);
  140. // Determine target position
  141. RVec3 target = RVec3(4.0_r * x - 20.0_r, 5.0_r, 4.0_r * y - 20.0_r);
  142. // Determine new character velocity
  143. Vec3 direction = Vec3(target - ch->GetPosition()).NormalizedOr(Vec3::sZero());
  144. direction.SetY(0);
  145. new_velocity += (5.0f + 0.5f * speed) * direction;
  146. ch->SetLinearVelocity(new_velocity);
  147. // Update the character position
  148. CharacterVirtual::ExtendedUpdateSettings update_settings;
  149. ch->ExtendedUpdate(inDeltaTime,
  150. inPhysicsSystem.GetGravity(),
  151. update_settings,
  152. inPhysicsSystem.GetDefaultBroadPhaseLayerFilter(Layers::MOVING),
  153. inPhysicsSystem.GetDefaultLayerFilter(Layers::MOVING),
  154. { },
  155. { },
  156. ioTempAllocator);
  157. ++count;
  158. }
  159. }
  160. virtual void UpdateHash(uint64 &ioHash) const override
  161. {
  162. // Hash the contact callback hash
  163. HashCombine(ioHash, mHash);
  164. // Hash the state of all characters
  165. for (const CharacterVirtual *ch : mCharacters)
  166. HashCombine(ioHash, ch->GetPosition());
  167. }
  168. virtual void StopTest(PhysicsSystem &inPhysicsSystem) override
  169. {
  170. for (const CharacterVirtual *ch : mCharacters)
  171. mCharacterVsCharacterCollision.Remove(ch);
  172. mCharacters.clear();
  173. }
  174. // See: CharacterContactListener
  175. virtual void OnContactAdded(const CharacterVirtual *inCharacter, const BodyID &inBodyID2, const SubShapeID &inSubShapeID2, RVec3Arg inContactPosition, Vec3Arg inContactNormal, CharacterContactSettings &ioSettings) override
  176. {
  177. HashCombine(mHash, 1);
  178. HashCombine(mHash, inCharacter->GetID());
  179. HashCombine(mHash, inBodyID2);
  180. HashCombine(mHash, inSubShapeID2.GetValue());
  181. HashCombine(mHash, inContactPosition);
  182. HashCombine(mHash, inContactNormal);
  183. }
  184. virtual void OnContactPersisted(const CharacterVirtual *inCharacter, const BodyID &inBodyID2, const SubShapeID &inSubShapeID2, RVec3Arg inContactPosition, Vec3Arg inContactNormal, CharacterContactSettings &ioSettings) override
  185. {
  186. HashCombine(mHash, 2);
  187. HashCombine(mHash, inCharacter->GetID());
  188. HashCombine(mHash, inBodyID2);
  189. HashCombine(mHash, inSubShapeID2.GetValue());
  190. HashCombine(mHash, inContactPosition);
  191. HashCombine(mHash, inContactNormal);
  192. }
  193. virtual void OnContactRemoved(const CharacterVirtual *inCharacter, const BodyID &inBodyID2, const SubShapeID &inSubShapeID2) override
  194. {
  195. HashCombine(mHash, 3);
  196. HashCombine(mHash, inCharacter->GetID());
  197. HashCombine(mHash, inBodyID2);
  198. HashCombine(mHash, inSubShapeID2.GetValue());
  199. }
  200. virtual void OnCharacterContactAdded(const CharacterVirtual *inCharacter, const CharacterVirtual *inOtherCharacter, const SubShapeID &inSubShapeID2, RVec3Arg inContactPosition, Vec3Arg inContactNormal, CharacterContactSettings &ioSettings) override
  201. {
  202. HashCombine(mHash, 4);
  203. HashCombine(mHash, inCharacter->GetID());
  204. HashCombine(mHash, inOtherCharacter->GetID());
  205. HashCombine(mHash, inSubShapeID2.GetValue());
  206. HashCombine(mHash, inContactPosition);
  207. HashCombine(mHash, inContactNormal);
  208. }
  209. virtual void OnCharacterContactPersisted(const CharacterVirtual *inCharacter, const CharacterVirtual *inOtherCharacter, const SubShapeID &inSubShapeID2, RVec3Arg inContactPosition, Vec3Arg inContactNormal, CharacterContactSettings &ioSettings) override
  210. {
  211. HashCombine(mHash, 5);
  212. HashCombine(mHash, inCharacter->GetID());
  213. HashCombine(mHash, inOtherCharacter->GetID());
  214. HashCombine(mHash, inSubShapeID2.GetValue());
  215. HashCombine(mHash, inContactPosition);
  216. HashCombine(mHash, inContactNormal);
  217. }
  218. virtual void OnCharacterContactRemoved(const CharacterVirtual *inCharacter, const CharacterID &inOtherCharacterID, const SubShapeID &inSubShapeID2) override
  219. {
  220. HashCombine(mHash, 6);
  221. HashCombine(mHash, inCharacter->GetID());
  222. HashCombine(mHash, inOtherCharacterID);
  223. HashCombine(mHash, inSubShapeID2.GetValue());
  224. }
  225. private:
  226. static constexpr int cNumCharactersX = 10;
  227. static constexpr int cNumCharactersY = 10;
  228. static constexpr float cCharacterHeightStanding = 1.35f;
  229. static constexpr float cCharacterRadiusStanding = 0.3f;
  230. static constexpr float cInnerShapeFraction = 0.9f;
  231. static constexpr float cStairsStepHeight = 0.3f;
  232. float mTime = 0.0f;
  233. uint64 mHash = 0;
  234. Array<BodyCreationSettings> mWorld;
  235. Array<Ref<CharacterVirtual>> mCharacters;
  236. CharacterVsCharacterCollisionSimple mCharacterVsCharacterCollision;
  237. };