123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267 |
- // Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
- // SPDX-FileCopyrightText: 2025 Jorrit Rouwe
- // SPDX-License-Identifier: MIT
- #pragma once
- // Jolt includes
- #include <Jolt/Physics/Collision/Shape/BoxShape.h>
- #include <Jolt/Physics/Collision/Shape/MeshShape.h>
- #include <Jolt/Physics/Collision/Shape/CapsuleShape.h>
- #include <Jolt/Physics/Collision/Shape/RotatedTranslatedShape.h>
- #include <Jolt/Physics/Character/CharacterVirtual.h>
- #include <Jolt/Physics/Body/BodyCreationSettings.h>
- // Local includes
- #include "PerformanceTestScene.h"
- #include "Layers.h"
- // A scene that drops a number of virtual characters on a scene and simulates them
- class CharacterVirtualScene : public PerformanceTestScene, public CharacterContactListener
- {
- public:
- virtual const char * GetName() const override
- {
- return "CharacterVirtual";
- }
- virtual bool Load(const String &inAssetPath) override
- {
- const int n = 100;
- const float cell_size = 0.5f;
- const float max_height = 2.0f;
- float center = n * cell_size / 2;
- // Create vertices
- const int num_vertices = (n + 1) * (n + 1);
- VertexList vertices;
- vertices.resize(num_vertices);
- for (int x = 0; x <= n; ++x)
- for (int z = 0; z <= n; ++z)
- {
- float height = Sin(float(x) * 20.0f / n) * Cos(float(z) * 20.0f / n);
- vertices[z * (n + 1) + x] = Float3(cell_size * x, max_height * height, cell_size * z);
- }
- // Create regular grid of triangles
- const int num_triangles = n * n * 2;
- IndexedTriangleList indices;
- indices.resize(num_triangles);
- IndexedTriangle *next = indices.data();
- for (int x = 0; x < n; ++x)
- for (int z = 0; z < n; ++z)
- {
- int start = (n + 1) * z + x;
- next->mIdx[0] = start;
- next->mIdx[1] = start + n + 1;
- next->mIdx[2] = start + 1;
- next++;
- next->mIdx[0] = start + 1;
- next->mIdx[1] = start + n + 1;
- next->mIdx[2] = start + n + 2;
- next++;
- }
- // Create mesh
- BodyCreationSettings mesh(new MeshShapeSettings(vertices, indices), RVec3(Real(-center), 0, Real(-center)), Quat::sIdentity(), EMotionType::Static, Layers::NON_MOVING);
- mWorld.push_back(mesh);
- // Create pyramid stairs
- for (int i = 0; i < 10; ++i)
- {
- float width = 4.0f - 0.4f * i;
- 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);
- mWorld.push_back(step);
- }
- // Create wall consisting of vertical pillars
- Ref<Shape> wall = new BoxShape(Vec3(0.1f, 2.5f, 0.1f), 0.0f);
- for (int z = 0; z < 10; ++z)
- {
- BodyCreationSettings bcs(wall, RVec3(2.0_r, 1.0_r, 2.0_r + 0.2_r * z), Quat::sIdentity(), EMotionType::Static, Layers::NON_MOVING);
- mWorld.push_back(bcs);
- }
- // Create some dynamic boxes
- Ref<Shape> box = new BoxShape(Vec3::sReplicate(0.25f));
- for (int x = 0; x < 10; ++x)
- for (int z = 0; z < 10; ++z)
- {
- 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);
- bcs.mOverrideMassProperties = EOverrideMassProperties::CalculateInertia;
- bcs.mMassPropertiesOverride.mMass = 1.0f;
- mWorld.push_back(bcs);
- }
- return true;
- }
- virtual void StartTest(PhysicsSystem &inPhysicsSystem, EMotionQuality inMotionQuality) override
- {
- // Construct bodies
- BodyInterface &bi = inPhysicsSystem.GetBodyInterface();
- for (BodyCreationSettings &bcs : mWorld)
- if (bcs.mMotionType == EMotionType::Dynamic)
- {
- bcs.mMotionQuality = inMotionQuality;
- bi.CreateAndAddBody(bcs, EActivation::Activate);
- }
- else
- bi.CreateAndAddBody(bcs, EActivation::DontActivate);
- // Construct characters
- CharacterID::sSetNextCharacterID();
- RefConst<Shape> standing_shape = RotatedTranslatedShapeSettings(Vec3(0, 0.5f * cCharacterHeightStanding + cCharacterRadiusStanding, 0), Quat::sIdentity(), new CapsuleShape(0.5f * cCharacterHeightStanding, cCharacterRadiusStanding)).Create().Get();
- 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();
- for (int y = 0; y < cNumCharactersY; ++y)
- for (int x = 0; x < cNumCharactersX; ++x)
- {
- Ref<CharacterVirtualSettings> settings = new CharacterVirtualSettings();
- settings->mShape = standing_shape;
- settings->mSupportingVolume = Plane(Vec3::sAxisY(), -cCharacterRadiusStanding); // Accept contacts that touch the lower sphere of the capsule
- settings->mInnerBodyShape = inner_standing_shape;
- settings->mInnerBodyLayer = Layers::MOVING;
- 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);
- character->SetCharacterVsCharacterCollision(&mCharacterVsCharacterCollision);
- character->SetListener(this);
- mCharacters.push_back(character);
- mCharacterVsCharacterCollision.Add(character);
- }
- // Start at time 0
- mTime = 0.0f;
- mHash = HashBytes(nullptr, 0);
- }
- virtual void UpdateTest(PhysicsSystem &inPhysicsSystem, TempAllocator &ioTempAllocator, float inDeltaTime) override
- {
- // Change direction every 2 seconds
- mTime += inDeltaTime;
- uint64 count = uint64(mTime / 2.0f) * cNumCharactersX * cNumCharactersY;
- for (CharacterVirtual *ch : mCharacters)
- {
- // Calculate new vertical velocity
- Vec3 new_velocity;
- if (ch->GetGroundState() == CharacterVirtual::EGroundState::OnGround // If on ground
- && ch->GetLinearVelocity().GetY() < 0.1f) // And not moving away from ground
- new_velocity = Vec3::sZero();
- else
- new_velocity = ch->GetLinearVelocity() * Vec3(0, 1, 0);
- new_velocity += inPhysicsSystem.GetGravity() * inDeltaTime;
- // Deterministic random input
- uint64 hash = Hash<uint64> {} (count);
- int x = int(hash % 10);
- int y = int((hash / 10) % 10);
- int speed = int((hash / 100) % 10);
- // Determine target position
- RVec3 target = RVec3(4.0_r * x - 20.0_r, 5.0_r, 4.0_r * y - 20.0_r);
- // Determine new character velocity
- Vec3 direction = Vec3(target - ch->GetPosition()).NormalizedOr(Vec3::sZero());
- direction.SetY(0);
- new_velocity += (5.0f + 0.5f * speed) * direction;
- ch->SetLinearVelocity(new_velocity);
- // Update the character position
- CharacterVirtual::ExtendedUpdateSettings update_settings;
- ch->ExtendedUpdate(inDeltaTime,
- inPhysicsSystem.GetGravity(),
- update_settings,
- inPhysicsSystem.GetDefaultBroadPhaseLayerFilter(Layers::MOVING),
- inPhysicsSystem.GetDefaultLayerFilter(Layers::MOVING),
- { },
- { },
- ioTempAllocator);
- ++count;
- }
- }
- virtual void UpdateHash(uint64 &ioHash) const override
- {
- // Hash the contact callback hash
- HashCombine(ioHash, mHash);
- // Hash the state of all characters
- for (const CharacterVirtual *ch : mCharacters)
- HashCombine(ioHash, ch->GetPosition());
- }
- virtual void StopTest(PhysicsSystem &inPhysicsSystem) override
- {
- for (const CharacterVirtual *ch : mCharacters)
- mCharacterVsCharacterCollision.Remove(ch);
- mCharacters.clear();
- }
- // See: CharacterContactListener
- virtual void OnContactAdded(const CharacterVirtual *inCharacter, const BodyID &inBodyID2, const SubShapeID &inSubShapeID2, RVec3Arg inContactPosition, Vec3Arg inContactNormal, CharacterContactSettings &ioSettings) override
- {
- HashCombine(mHash, 1);
- HashCombine(mHash, inCharacter->GetID());
- HashCombine(mHash, inBodyID2);
- HashCombine(mHash, inSubShapeID2.GetValue());
- HashCombine(mHash, inContactPosition);
- HashCombine(mHash, inContactNormal);
- }
- virtual void OnContactPersisted(const CharacterVirtual *inCharacter, const BodyID &inBodyID2, const SubShapeID &inSubShapeID2, RVec3Arg inContactPosition, Vec3Arg inContactNormal, CharacterContactSettings &ioSettings) override
- {
- HashCombine(mHash, 2);
- HashCombine(mHash, inCharacter->GetID());
- HashCombine(mHash, inBodyID2);
- HashCombine(mHash, inSubShapeID2.GetValue());
- HashCombine(mHash, inContactPosition);
- HashCombine(mHash, inContactNormal);
- }
- virtual void OnContactRemoved(const CharacterVirtual *inCharacter, const BodyID &inBodyID2, const SubShapeID &inSubShapeID2) override
- {
- HashCombine(mHash, 3);
- HashCombine(mHash, inCharacter->GetID());
- HashCombine(mHash, inBodyID2);
- HashCombine(mHash, inSubShapeID2.GetValue());
- }
- virtual void OnCharacterContactAdded(const CharacterVirtual *inCharacter, const CharacterVirtual *inOtherCharacter, const SubShapeID &inSubShapeID2, RVec3Arg inContactPosition, Vec3Arg inContactNormal, CharacterContactSettings &ioSettings) override
- {
- HashCombine(mHash, 4);
- HashCombine(mHash, inCharacter->GetID());
- HashCombine(mHash, inOtherCharacter->GetID());
- HashCombine(mHash, inSubShapeID2.GetValue());
- HashCombine(mHash, inContactPosition);
- HashCombine(mHash, inContactNormal);
- }
- virtual void OnCharacterContactPersisted(const CharacterVirtual *inCharacter, const CharacterVirtual *inOtherCharacter, const SubShapeID &inSubShapeID2, RVec3Arg inContactPosition, Vec3Arg inContactNormal, CharacterContactSettings &ioSettings) override
- {
- HashCombine(mHash, 5);
- HashCombine(mHash, inCharacter->GetID());
- HashCombine(mHash, inOtherCharacter->GetID());
- HashCombine(mHash, inSubShapeID2.GetValue());
- HashCombine(mHash, inContactPosition);
- HashCombine(mHash, inContactNormal);
- }
- virtual void OnCharacterContactRemoved(const CharacterVirtual *inCharacter, const CharacterID &inOtherCharacterID, const SubShapeID &inSubShapeID2) override
- {
- HashCombine(mHash, 6);
- HashCombine(mHash, inCharacter->GetID());
- HashCombine(mHash, inOtherCharacterID);
- HashCombine(mHash, inSubShapeID2.GetValue());
- }
- private:
- static constexpr int cNumCharactersX = 10;
- static constexpr int cNumCharactersY = 10;
- static constexpr float cCharacterHeightStanding = 1.35f;
- static constexpr float cCharacterRadiusStanding = 0.3f;
- static constexpr float cInnerShapeFraction = 0.9f;
- static constexpr float cStairsStepHeight = 0.3f;
- float mTime = 0.0f;
- uint64 mHash = 0;
- Array<BodyCreationSettings> mWorld;
- Array<Ref<CharacterVirtual>> mCharacters;
- CharacterVsCharacterCollisionSimple mCharacterVsCharacterCollision;
- };
|