Files
CosmicEngine/lib/All/JoltPhysics/PerformanceTest/CharacterVirtualScene.h

268 lines
10 KiB
C
Raw Normal View History

// 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;
};