diff --git a/Source/Editor/States/PlayingState.cs b/Source/Editor/States/PlayingState.cs
index c9100dcf9..be2c838c6 100644
--- a/Source/Editor/States/PlayingState.cs
+++ b/Source/Editor/States/PlayingState.cs
@@ -156,7 +156,7 @@ namespace FlaxEditor.States
private void SetupEditorEnvOptions()
{
Time.TimeScale = 1.0f;
- Physics.AutoSimulation = true;
+ Physics.DefaultScene.AutoSimulation = true;
Screen.CursorVisible = true;
Screen.CursorLock = CursorLockMode.None;
}
diff --git a/Source/Engine/Engine/Engine.cpp b/Source/Engine/Engine/Engine.cpp
index 08465b349..4e411243d 100644
--- a/Source/Engine/Engine/Engine.cpp
+++ b/Source/Engine/Engine/Engine.cpp
@@ -197,7 +197,7 @@ int32 Engine::Main(const Char* cmdLine)
}
// Collect physics simulation results (does nothing if Simulate hasn't been called in the previous loop step)
- Physics::CollectResults();
+ Physics::CollectResultsAll();
}
// Call on exit event
@@ -242,7 +242,7 @@ void Engine::OnFixedUpdate()
{
PROFILE_CPU_NAMED("Fixed Update");
- Physics::FlushRequests();
+ Physics::FlushRequestsAll();
// Call event
FixedUpdate();
@@ -250,10 +250,10 @@ void Engine::OnFixedUpdate()
// Update services
EngineService::OnFixedUpdate();
- if (!Time::GetGamePaused() && Physics::AutoSimulation)
+ if (!Time::GetGamePaused())
{
const float dt = Time::Physics.DeltaTime.GetTotalSeconds();
- Physics::Simulate(dt);
+ Physics::SimulateAll(dt);
// After this point we should not modify physic objects state (rendering operations is mostly readonly)
// That's because auto-simulation mode is performing rendering during physics simulation
@@ -429,7 +429,7 @@ void Engine::OnExit()
EngineImpl::IsReady = false;
// Collect physics simulation results because we cannot exit with physics running
- Physics::CollectResults();
+ Physics::CollectResultsAll();
// Before
Application::BeforeExit();
diff --git a/Source/Engine/Level/Actor.cpp b/Source/Engine/Level/Actor.cpp
index 54c2a81f4..f2a0a3a62 100644
--- a/Source/Engine/Level/Actor.cpp
+++ b/Source/Engine/Level/Actor.cpp
@@ -17,6 +17,7 @@
#include "Engine/Debug/Exceptions/JsonParseException.h"
#include "Engine/Graphics/RenderTask.h"
#include "Engine/Graphics/RenderView.h"
+#include "Engine/Physics/Physics.h"
#include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Scripting/Scripting.h"
#include "Engine/Serialization/ISerializeModifier.h"
@@ -77,6 +78,7 @@ Actor::Actor(const SpawnParams& params)
, _transform(Transform::Identity)
, _sphere(BoundingSphere::Empty)
, _box(BoundingBox::Zero)
+ , _physicsScene(nullptr)
, HideFlags(HideFlags::None)
{
}
@@ -1809,3 +1811,28 @@ void Actor::FromJson(const StringAnsiView& json)
Scripting::ObjectsLookupIdMapping.Set(nullptr);
OnTransformChanged();
}
+
+void Actor::SetPhysicsScene(PhysicsScene* scene)
+{
+ ASSERT(scene);
+
+ auto previous = GetPhysicsScene();
+ _physicsScene = scene;
+
+ if (previous != _physicsScene)
+ {
+ OnPhysicsSceneChanged(previous);
+
+ // cascade
+ for (auto child : Children)
+ child->SetPhysicsScene(scene);
+ }
+}
+
+PhysicsScene* Actor::GetPhysicsScene()
+{
+ if (_physicsScene == nullptr)
+ _physicsScene = Physics::DefaultScene;
+
+ return _physicsScene;
+}
diff --git a/Source/Engine/Level/Actor.h b/Source/Engine/Level/Actor.h
index e3fcbc371..a54155e5f 100644
--- a/Source/Engine/Level/Actor.h
+++ b/Source/Engine/Level/Actor.h
@@ -14,6 +14,7 @@ struct RenderView;
struct RenderContext;
class GPUContext;
class MemoryWriteStream;
+class PhysicsScene;
class SceneRendering;
class SceneRenderTask;
@@ -47,6 +48,7 @@ protected:
BoundingSphere _sphere;
BoundingBox _box;
String _name;
+ PhysicsScene* _physicsScene;
private:
@@ -959,6 +961,20 @@ public:
/// The scene rendering interface.
SceneRendering* GetSceneRendering() const;
+public:
+ ///
+ /// Set the physics world the controller is part of.
+ ///
+ API_PROPERTY(Attributes="HideInEditor") void SetPhysicsScene(PhysicsScene* scene);
+
+ ///
+ /// Get the physics world the controller is part of.
+ ///
+ API_PROPERTY(Attributes="HideInEditor") PhysicsScene* GetPhysicsScene();
+
+protected:
+ virtual void OnPhysicsSceneChanged(PhysicsScene* previous) {};
+
private:
void SetSceneInHierarchy(Scene* scene);
diff --git a/Source/Engine/Level/Level.cpp b/Source/Engine/Level/Level.cpp
index 496dc2519..3bbf91363 100644
--- a/Source/Engine/Level/Level.cpp
+++ b/Source/Engine/Level/Level.cpp
@@ -181,6 +181,8 @@ bool LevelImpl::spawnActor(Actor* actor, Actor* parent)
}
if (parent == nullptr)
parent = Level::Scenes[0];
+
+ actor->SetPhysicsScene(parent->GetPhysicsScene());
actor->SetParent(parent, true, true);
}
diff --git a/Source/Engine/Physics/Actors/PhysicsActor.h b/Source/Engine/Physics/Actors/PhysicsActor.h
index 69d239f95..05e015074 100644
--- a/Source/Engine/Physics/Actors/PhysicsActor.h
+++ b/Source/Engine/Physics/Actors/PhysicsActor.h
@@ -6,6 +6,8 @@
#include "Engine/Physics/Types.h"
#include "IPhysicsActor.h"
+class PhysicsScene;
+
///
/// A base class for all physical actors.
///
diff --git a/Source/Engine/Physics/Actors/RigidBody.cpp b/Source/Engine/Physics/Actors/RigidBody.cpp
index 9eb8dee28..a3047889d 100644
--- a/Source/Engine/Physics/Actors/RigidBody.cpp
+++ b/Source/Engine/Physics/Actors/RigidBody.cpp
@@ -2,10 +2,13 @@
#include "RigidBody.h"
#include "PxMaterial.h"
+
+#include "Engine/Core/Log.h"
#include "Engine/Physics/Utilities.h"
#include "Engine/Physics/Colliders/Collider.h"
#include "Engine/Physics/Physics.h"
#include "Engine/Physics/PhysicalMaterial.h"
+#include "Engine/Physics/PhysicsScene.h"
#include "Engine/Serialization/Serialization.h"
#include
#include
@@ -476,7 +479,7 @@ void RigidBody::CreateActor()
// Register actor
const bool putToSleep = !_startAwake && GetEnableSimulation() && !GetIsKinematic() && IsActiveInHierarchy();
- Physics::AddActor(_actor, putToSleep);
+ GetPhysicsScene()->AddActor(_actor, putToSleep);
// Update cached data
UpdateBounds();
@@ -580,7 +583,7 @@ void RigidBody::EndPlay()
if (_actor)
{
// Remove actor
- Physics::RemoveActor(_actor);
+ GetPhysicsScene()->RemoveActor(_actor);
_actor = nullptr;
}
}
@@ -627,3 +630,15 @@ void RigidBody::OnTransformChanged()
PhysicsActor::OnTransformChanged();
}
}
+
+void RigidBody::OnPhysicsSceneChanged(PhysicsScene* previous)
+{
+ ASSERT(previous);
+
+ PhysicsActor::OnPhysicsSceneChanged(previous);
+
+ previous->UnlinkActor(_actor);
+
+ const bool putToSleep = !_startAwake && GetEnableSimulation() && !GetIsKinematic() && IsActiveInHierarchy();
+ GetPhysicsScene()->AddActor(_actor, putToSleep);
+}
diff --git a/Source/Engine/Physics/Actors/RigidBody.h b/Source/Engine/Physics/Actors/RigidBody.h
index 8d487b869..8726161da 100644
--- a/Source/Engine/Physics/Actors/RigidBody.h
+++ b/Source/Engine/Physics/Actors/RigidBody.h
@@ -572,4 +572,5 @@ protected:
void EndPlay() override;
void OnActiveInTreeChanged() override;
void OnTransformChanged() override;
+ void OnPhysicsSceneChanged(PhysicsScene* previous) override;
};
diff --git a/Source/Engine/Physics/Actors/SplineRopeBody.cpp b/Source/Engine/Physics/Actors/SplineRopeBody.cpp
index b341f7008..a0c1c6b1e 100644
--- a/Source/Engine/Physics/Actors/SplineRopeBody.cpp
+++ b/Source/Engine/Physics/Actors/SplineRopeBody.cpp
@@ -4,6 +4,7 @@
#include "Engine/Level/Actors/Spline.h"
#include "Engine/Level/Scene/Scene.h"
#include "Engine/Physics/Physics.h"
+#include "Engine/Physics/PhysicsScene.h"
#include "Engine/Engine/Time.h"
#include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Serialization/Serialization.h"
@@ -20,7 +21,7 @@ void SplineRopeBody::Tick()
PROFILE_CPU();
// Cache data
- const Vector3 gravity = Physics::GetGravity() * GravityScale;
+ const Vector3 gravity = GetPhysicsScene()->GetGravity() * GravityScale;
auto& keyframes = _spline->Curve.GetKeyframes();
const Transform splineTransform = _spline->GetTransform();
const int32 keyframesCount = keyframes.Count();
diff --git a/Source/Engine/Physics/Actors/WheeledVehicle.cpp b/Source/Engine/Physics/Actors/WheeledVehicle.cpp
index 7c6106158..5becca07e 100644
--- a/Source/Engine/Physics/Actors/WheeledVehicle.cpp
+++ b/Source/Engine/Physics/Actors/WheeledVehicle.cpp
@@ -5,6 +5,7 @@
#include "Engine/Physics/Physics.h"
#include "Engine/Physics/Utilities.h"
#include "Engine/Level/Scene/Scene.h"
+#include "Engine/Physics/PhysicsScene.h"
#include "Engine/Serialization/Serialization.h"
#if USE_EDITOR
#include "Engine/Level/Scene/SceneRendering.h"
@@ -21,7 +22,6 @@
#if WITH_VEHICLE
extern void InitVehicleSDK();
-extern Array WheelVehicles;
#endif
namespace
@@ -237,7 +237,7 @@ void WheeledVehicle::Setup()
// Release previous
if (drive)
{
- WheelVehicles.Remove(this);
+ GetPhysicsScene()->RemoveWheeledVehicle(this);
FreeDrive(_driveTypeCurrent, drive);
drive = nullptr;
}
@@ -501,7 +501,7 @@ void WheeledVehicle::Setup()
CRASH;
}
- WheelVehicles.Add(this);
+ GetPhysicsScene()->AddWheeledVehicle(this);
wheelsSimData->free();
_actor->setSolverIterationCounts(12, 4);
@@ -621,6 +621,14 @@ void WheeledVehicle::OnColliderChanged(Collider* c)
Setup();
}
+void WheeledVehicle::OnPhysicsSceneChanged(PhysicsScene* previous)
+{
+ RigidBody::OnPhysicsSceneChanged(previous);
+
+ previous->RemoveWheeledVehicle(this);
+ GetPhysicsScene()->AddWheeledVehicle(this);
+}
+
void WheeledVehicle::BeginPlay(SceneBeginData* data)
{
RigidBody::BeginPlay(data);
@@ -645,7 +653,7 @@ void WheeledVehicle::EndPlay()
if (drive)
{
// Parkway Drive
- WheelVehicles.Remove(this);
+ GetPhysicsScene()->RemoveWheeledVehicle(this);
FreeDrive(_driveTypeCurrent, drive);
drive = nullptr;
}
diff --git a/Source/Engine/Physics/Actors/WheeledVehicle.h b/Source/Engine/Physics/Actors/WheeledVehicle.h
index bbc52af5b..cd3ef88f4 100644
--- a/Source/Engine/Physics/Actors/WheeledVehicle.h
+++ b/Source/Engine/Physics/Actors/WheeledVehicle.h
@@ -6,7 +6,7 @@
#include "Engine/Physics/Colliders/Collider.h"
#include "Engine/Scripting/ScriptingObjectReference.h"
-class Physics;
+class PhysicsScene;
///
/// Representation of the car vehicle that uses wheels. Built on top of the RigidBody with collider representing its chassis shape and wheels.
@@ -14,7 +14,7 @@ class Physics;
///
API_CLASS() class FLAXENGINE_API WheeledVehicle : public RigidBody
{
- friend Physics;
+ friend PhysicsScene;
DECLARE_SCENE_OBJECT(WheeledVehicle);
public:
@@ -489,6 +489,7 @@ public:
void OnColliderChanged(Collider* c) override;
protected:
+ void OnPhysicsSceneChanged(PhysicsScene* previous) override;
// [Vehicle]
void BeginPlay(SceneBeginData* data) override;
diff --git a/Source/Engine/Physics/Colliders/CharacterController.cpp b/Source/Engine/Physics/Colliders/CharacterController.cpp
index c57c0f395..52324c98b 100644
--- a/Source/Engine/Physics/Colliders/CharacterController.cpp
+++ b/Source/Engine/Physics/Colliders/CharacterController.cpp
@@ -7,9 +7,9 @@
#include "Engine/Serialization/Serialization.h"
#include "Engine/Engine/Time.h"
#include "Engine/Physics/PhysicalMaterial.h"
+#include "Engine/Physics/PhysicsScene.h"
#include
#include
-#include
#include
#include
#include
@@ -156,7 +156,7 @@ CharacterController::CollisionFlags CharacterController::SimpleMove(const Vector
{
const float deltaTime = Time::GetCurrentSafe()->DeltaTime.GetTotalSeconds();
Vector3 displacement = speed;
- displacement += Physics::GetGravity() * deltaTime;
+ displacement += GetPhysicsScene()->GetGravity() * deltaTime;
displacement *= deltaTime;
return Move(displacement);
@@ -171,9 +171,9 @@ CharacterController::CollisionFlags CharacterController::Move(const Vector3& dis
const float deltaTime = Time::GetCurrentSafe()->DeltaTime.GetTotalSeconds();
PxControllerFilters filters;
filters.mFilterData = (PxFilterData*)&_filterData;
- filters.mFilterCallback = Physics::GetCharacterQueryFilterCallback();
+ filters.mFilterCallback = GetPhysicsScene()->GetCharacterQueryFilterCallback();
filters.mFilterFlags = PxQueryFlag::eDYNAMIC | PxQueryFlag::eSTATIC | PxQueryFlag::ePREFILTER;
- filters.mCCTFilterCallback = Physics::GetCharacterControllerFilterCallback();
+ filters.mCCTFilterCallback = GetPhysicsScene()->GetCharacterControllerFilterCallback();
result = (CollisionFlags)(byte)_controller->move(C2P(displacement), _minMoveDistance, deltaTime, filters);
_lastFlags = result;
@@ -243,7 +243,7 @@ void CharacterController::CreateController()
desc.stepOffset = Math::Min(_stepOffset, desc.height + desc.radius * 2.0f - minSize);
// Create controller
- _controller = (PxCapsuleController*)Physics::GetControllerManager()->createController(desc);
+ _controller = (PxCapsuleController*)GetPhysicsScene()->GetControllerManager()->createController(desc);
ASSERT(_controller);
_controller->setUpDirection(C2P(_upDirection));
const auto actor = _controller->getActor();
@@ -433,6 +433,14 @@ void CharacterController::OnTransformChanged()
}
}
+void CharacterController::OnPhysicsSceneChanged(PhysicsScene* previous)
+{
+ Collider::OnPhysicsSceneChanged(previous);
+
+ DeleteController();
+ CreateController();
+}
+
void CharacterController::Serialize(SerializeStream& stream, const void* otherObj)
{
// Base
diff --git a/Source/Engine/Physics/Colliders/CharacterController.h b/Source/Engine/Physics/Colliders/CharacterController.h
index 5fece3195..8a0a79223 100644
--- a/Source/Engine/Physics/Colliders/CharacterController.h
+++ b/Source/Engine/Physics/Colliders/CharacterController.h
@@ -237,4 +237,5 @@ protected:
void OnDisable() override;
void OnParentChanged() override;
void OnTransformChanged() override;
+ void OnPhysicsSceneChanged(PhysicsScene* previous) override;
};
diff --git a/Source/Engine/Physics/Colliders/Collider.cpp b/Source/Engine/Physics/Colliders/Collider.cpp
index 35a4daf7e..73440c5b3 100644
--- a/Source/Engine/Physics/Colliders/Collider.cpp
+++ b/Source/Engine/Physics/Colliders/Collider.cpp
@@ -10,6 +10,7 @@
#include "Engine/Physics/PhysicsSettings.h"
#include "Engine/Physics/Physics.h"
#include "Engine/Physics/PhysicalMaterial.h"
+#include "Engine/Physics/PhysicsScene.h"
#include "Engine/Physics/Actors/RigidBody.h"
#include
#include
@@ -17,7 +18,6 @@
#include
#include
#include
-#include
Collider::Collider(const SpawnParams& params)
: PhysicsColliderActor(params)
@@ -301,7 +301,7 @@ void Collider::UpdateGeometry()
actor->detachShape(*_shape);
// Release shape
- Physics::RemoveCollider(this);
+ GetPhysicsScene()->RemoveCollider(this);
_shape->release();
_shape = nullptr;
@@ -346,14 +346,14 @@ void Collider::CreateStaticActor()
_shape->setLocalPose(PxTransform(C2P(_center)));
_staticActor->attachShape(*_shape);
- Physics::AddActor(_staticActor);
+ GetPhysicsScene()->AddActor(_staticActor);
}
void Collider::RemoveStaticActor()
{
ASSERT(_staticActor != nullptr);
- Physics::RemoveActor(_staticActor);
+ GetPhysicsScene()->RemoveActor(_staticActor);
_staticActor = nullptr;
}
@@ -447,7 +447,7 @@ void Collider::EndPlay()
}
// Release shape
- Physics::RemoveCollider(this);
+ GetPhysicsScene()->RemoveCollider(this);
_shape->release();
_shape = nullptr;
}
@@ -537,3 +537,14 @@ void Collider::OnLayerChanged()
if (_shape)
UpdateLayerBits();
}
+
+void Collider::OnPhysicsSceneChanged(PhysicsScene* previous)
+{
+ PhysicsColliderActor::OnPhysicsSceneChanged(previous);
+
+ if (_staticActor != nullptr)
+ {
+ previous->UnlinkActor(_staticActor);
+ GetPhysicsScene()->AddActor(_staticActor);
+ }
+}
diff --git a/Source/Engine/Physics/Colliders/Collider.h b/Source/Engine/Physics/Colliders/Collider.h
index 1c78f57ce..479b7c288 100644
--- a/Source/Engine/Physics/Colliders/Collider.h
+++ b/Source/Engine/Physics/Colliders/Collider.h
@@ -232,4 +232,5 @@ protected:
void OnParentChanged() override;
void OnTransformChanged() override;
void OnLayerChanged() override;
+ void OnPhysicsSceneChanged(PhysicsScene* previous) override;
};
diff --git a/Source/Engine/Physics/Colliders/MeshCollider.cpp b/Source/Engine/Physics/Colliders/MeshCollider.cpp
index 113fd7355..5b720860c 100644
--- a/Source/Engine/Physics/Colliders/MeshCollider.cpp
+++ b/Source/Engine/Physics/Colliders/MeshCollider.cpp
@@ -6,6 +6,7 @@
#include "Engine/Serialization/Serialization.h"
#include "Engine/Physics/Utilities.h"
#include "Engine/Physics/Physics.h"
+#include "Engine/Physics/PhysicsScene.h"
#if USE_EDITOR || !BUILD_RELEASE
#include "Engine/Debug/DebugLog.h"
#endif
@@ -20,7 +21,7 @@ MeshCollider::MeshCollider(const SpawnParams& params)
void MeshCollider::OnCollisionDataChanged()
{
// This should not be called during physics simulation, if it happened use write lock on physx scene
- ASSERT(!GetScene() || !Physics::IsDuringSimulation());
+ ASSERT(!GetScene() || !GetPhysicsScene()->IsDuringSimulation());
if (CollisionData)
{
diff --git a/Source/Engine/Physics/Colliders/SplineCollider.cpp b/Source/Engine/Physics/Colliders/SplineCollider.cpp
index 79e49e16a..62516a954 100644
--- a/Source/Engine/Physics/Colliders/SplineCollider.cpp
+++ b/Source/Engine/Physics/Colliders/SplineCollider.cpp
@@ -8,6 +8,7 @@
#include "Engine/Serialization/Serialization.h"
#include "Engine/Physics/Utilities.h"
#include "Engine/Physics/Physics.h"
+#include "Engine/Physics/PhysicsScene.h"
#include "Engine/Profiler/ProfilerCPU.h"
#if COMPILE_WITH_PHYSICS_COOKING
#include "Engine/Physics/CollisionCooking.h"
@@ -45,7 +46,7 @@ void SplineCollider::ExtractGeometry(Array& vertexBuffer, Array&
void SplineCollider::OnCollisionDataChanged()
{
// This should not be called during physics simulation, if it happened use write lock on physx scene
- ASSERT(!GetScene() || !Physics::IsDuringSimulation());
+ ASSERT(!GetScene() || !GetPhysicsScene()->IsDuringSimulation());
if (CollisionData)
{
@@ -172,7 +173,7 @@ void SplineCollider::EndPlay()
// Cleanup
if (_triangleMesh)
{
- Physics::RemoveObject(_triangleMesh);
+ GetPhysicsScene()->RemoveObject(_triangleMesh);
_triangleMesh = nullptr;
}
}
@@ -306,7 +307,7 @@ void SplineCollider::GetGeometry(PxGeometryHolder& geometry)
// Create triangle mesh
if (_triangleMesh)
{
- Physics::RemoveObject(_triangleMesh);
+ GetPhysicsScene()->RemoveObject(_triangleMesh);
_triangleMesh = nullptr;
}
PxDefaultMemoryInputData input(collisionData.Get(), collisionData.Length());
diff --git a/Source/Engine/Physics/CollisionData.cpp b/Source/Engine/Physics/CollisionData.cpp
index 9de47a0f0..95e5a67ca 100644
--- a/Source/Engine/Physics/CollisionData.cpp
+++ b/Source/Engine/Physics/CollisionData.cpp
@@ -1,12 +1,13 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
-#include "CollisionData.h"
#include "Engine/Core/Log.h"
#include "Engine/Content/Content.h"
#include "Engine/Content/Assets/Model.h"
#include "Engine/Content/Factories/BinaryAssetFactory.h"
#include "Engine/Physics/Physics.h"
+#include "Engine/Physics/PhysicsScene.h"
#include "Engine/Physics/Utilities.h"
+#include "Engine/Physics/CollisionData.h"
#include "Engine/Physics/CollisionCooking.h"
#include "Engine/Threading/Threading.h"
#include
@@ -384,12 +385,16 @@ void CollisionData::unload(bool isReloading)
{
if (_convexMesh)
{
- Physics::RemoveObject(_convexMesh);
+ for (auto scene : Physics::Scenes)
+ scene->RemoveObject(_convexMesh);
+
_convexMesh = nullptr;
}
if (_triangleMesh)
{
- Physics::RemoveObject(_triangleMesh);
+ for (auto scene : Physics::Scenes)
+ scene->RemoveObject(_triangleMesh);
+
_triangleMesh = nullptr;
}
_options = CollisionDataOptions();
diff --git a/Source/Engine/Physics/Physics.Queries.cpp b/Source/Engine/Physics/Physics.Queries.cpp
index f7986397e..b8c691c21 100644
--- a/Source/Engine/Physics/Physics.Queries.cpp
+++ b/Source/Engine/Physics/Physics.Queries.cpp
@@ -3,696 +3,140 @@
#include "Physics.h"
#include "Utilities.h"
#include "CollisionData.h"
+#include "PhysicsScene.h"
#include "Actors/PhysicsColliderActor.h"
-#include
-#include
-#include
-#include
-
-// Temporary result buffer size
-#define HIT_BUFFER_SIZE 128
-
-template
-class DynamicHitBuffer : public PxHitCallback
-{
-private:
-
- uint32 _count;
- HitType _buffer[HIT_BUFFER_SIZE];
-
-public:
-
- DynamicHitBuffer()
- : PxHitCallback(_buffer, HIT_BUFFER_SIZE)
- , _count(0)
- {
- }
-
-public:
-
- // Computes the number of any hits in this result, blocking or touching.
- PX_INLINE PxU32 getNbAnyHits() const
- {
- return getNbTouches();
- }
-
- // Convenience iterator used to access any hits in this result, blocking or touching.
- PX_INLINE const HitType& getAnyHit(const PxU32 index) const
- {
- PX_ASSERT(index < getNbTouches() + PxU32(this->hasBlock));
- return index < getNbTouches() ? getTouches()[index] : this->block;
- }
-
- PX_INLINE PxU32 getNbTouches() const
- {
- return _count;
- }
-
- PX_INLINE const HitType* getTouches() const
- {
- return _buffer;
- }
-
- PX_INLINE const HitType& getTouch(const PxU32 index) const
- {
- PX_ASSERT(index < getNbTouches());
- return _buffer[index];
- }
-
- PX_INLINE PxU32 getMaxNbTouches() const
- {
- return HIT_BUFFER_SIZE;
- }
-
-protected:
-
- PxAgain processTouches(const HitType* buffer, PxU32 nbHits) override
- {
- nbHits = Math::Min(nbHits, HIT_BUFFER_SIZE - _count);
- for (PxU32 i = 0; i < nbHits; i++)
- {
- _buffer[_count + i] = buffer[i];
- }
- _count += nbHits;
- return true;
- }
-
- void finalizeQuery() override
- {
- if (this->hasBlock)
- {
- // Blocking hits go to hits
- processTouches(&this->block, 1);
- }
- }
-};
-
-#define SCENE_QUERY_SETUP(blockSingle) if (GetScene() == nullptr) return false; \
- const PxHitFlags hitFlags = PxHitFlag::ePOSITION | PxHitFlag::eNORMAL | PxHitFlag::eUV; \
- PxQueryFilterData filterData = PxQueryFilterData(); \
- filterData.flags |= PxQueryFlag::ePREFILTER; \
- filterData.data.word0 = layerMask; \
- filterData.data.word1 = blockSingle ? 1 : 0; \
- filterData.data.word2 = hitTriggers ? 1 : 0
-
-#define SCENE_QUERY_SETUP_SWEEP_1() SCENE_QUERY_SETUP(true); \
- PxSweepBufferN<1> buffer
-
-#define SCENE_QUERY_SETUP_SWEEP() SCENE_QUERY_SETUP(false); \
- DynamicHitBuffer buffer
-
-#define SCENE_QUERY_SETUP_OVERLAP_1() SCENE_QUERY_SETUP(false); \
- PxOverlapBufferN<1> buffer
-
-#define SCENE_QUERY_SETUP_OVERLAP() SCENE_QUERY_SETUP(false); \
- DynamicHitBuffer buffer
-
-#define SCENE_QUERY_COLLECT_SINGLE() const auto& hit = buffer.getAnyHit(0); \
- hitInfo.Gather(hit)
-
-#define SCENE_QUERY_COLLECT_ALL() results.Clear(); \
- results.Resize(buffer.getNbAnyHits(), false); \
- for (int32 i = 0; i < results.Count(); i++) \
- { \
- const auto& hit = buffer.getAnyHit(i); \
- results[i].Gather(hit); \
- }
-
-#define SCENE_QUERY_COLLECT_OVERLAP() results.Clear(); \
- results.Resize(buffer.getNbTouches(), false); \
- for (int32 i = 0; i < results.Count(); i++) \
- { \
- auto& hitInfo = results[i]; \
- const auto& hit = buffer.getTouch(i); \
- hitInfo = hit.shape ? static_cast<::PhysicsColliderActor*>(hit.shape->userData) : nullptr; \
- }
-
-#define SCENE_QUERY_COLLECT_OVERLAP_COLLIDER() results.Clear(); \
- results.Resize(buffer.getNbTouches(), false); \
- for (int32 i = 0; i < results.Count(); i++) \
- { \
- auto& hitInfo = results[i]; \
- const auto& hit = buffer.getTouch(i); \
- hitInfo = hit.shape ? static_cast<::Collider*>(hit.shape->userData) : nullptr; \
- }
-
-void RayCastHit::Gather(const PxRaycastHit& hit)
-{
- Point = P2C(hit.position);
- Normal = P2C(hit.normal);
- Distance = hit.distance;
- Collider = hit.shape ? static_cast(hit.shape->userData) : nullptr;
- FaceIndex = hit.faceIndex;
- UV.X = hit.u;
- UV.Y = hit.v;
-}
-
-void RayCastHit::Gather(const PxSweepHit& hit)
-{
- Point = P2C(hit.position);
- Normal = P2C(hit.normal);
- Distance = hit.distance;
- Collider = hit.shape ? static_cast(hit.shape->userData) : nullptr;
- FaceIndex = hit.faceIndex;
- UV = Vector2::Zero;
-}
-
-class QueryFilter : public PxQueryFilterCallback
-{
-public:
-
- PxQueryHitType::Enum preFilter(const PxFilterData& filterData, const PxShape* shape, const PxRigidActor* actor, PxHitFlags& queryFlags) override
- {
- // Early out to avoid crashing
- if (!shape)
- return PxQueryHitType::eNONE;
-
- // Check mask
- const PxFilterData shapeFilter = shape->getQueryFilterData();
- if ((filterData.word0 & shapeFilter.word0) == 0)
- {
- return PxQueryHitType::eNONE;
- }
-
- // Check if skip triggers
- const bool hitTriggers = filterData.word2 != 0;
- if (!hitTriggers && shape->getFlags() & PxShapeFlag::eTRIGGER_SHAPE)
- return PxQueryHitType::eNONE;
-
- const bool blockSingle = filterData.word1 != 0;
- return blockSingle ? PxQueryHitType::eBLOCK : PxQueryHitType::eTOUCH;
- }
-
- PxQueryHitType::Enum postFilter(const PxFilterData& filterData, const PxQueryHit& hit) override
- {
- // Not used
- return PxQueryHitType::eNONE;
- }
-};
-
-class CharacterQueryFilter : public PxQueryFilterCallback
-{
-public:
-
- PxQueryHitType::Enum preFilter(const PxFilterData& filterData, const PxShape* shape, const PxRigidActor* actor, PxHitFlags& queryFlags) override
- {
- // Early out to avoid crashing
- if (!shape)
- return PxQueryHitType::eNONE;
-
- // Let triggers through
- if (PxFilterObjectIsTrigger(shape->getFlags()))
- return PxQueryHitType::eNONE;
-
- // Trigger the contact callback for pairs (A,B) where the filtermask of A contains the ID of B and vice versa
- const PxFilterData shapeFilter = shape->getQueryFilterData();
- if (filterData.word0 & shapeFilter.word1)
- return PxQueryHitType::eBLOCK;
-
- return PxQueryHitType::eNONE;
- }
-
- PxQueryHitType::Enum postFilter(const PxFilterData& filterData, const PxQueryHit& hit) override
- {
- // Not used
- return PxQueryHitType::eNONE;
- }
-};
-
-class CharacterControllerFilter : public PxControllerFilterCallback
-{
-private:
-
- PxShape* getShape(const PxController& controller)
- {
- PxRigidDynamic* actor = controller.getActor();
-
- // Early out if no actor or no shapes
- if (!actor || actor->getNbShapes() < 1)
- return nullptr;
-
- // Get first shape only.
- PxShape* shape = nullptr;
- actor->getShapes(&shape, 1);
-
- return shape;
- }
-
-public:
-
- bool filter(const PxController& a, const PxController& b) override
- {
- // Early out to avoid crashing
- PxShape* shapeA = getShape(a);
- if (!shapeA)
- return false;
-
- PxShape* shapeB = getShape(b);
- if (!shapeB)
- return false;
-
- // Let triggers through
- if (PxFilterObjectIsTrigger(shapeB->getFlags()))
- return false;
-
- // Trigger the contact callback for pairs (A,B) where the filtermask of A contains the ID of B and vice versa
- const PxFilterData shapeFilterA = shapeA->getQueryFilterData();
- const PxFilterData shapeFilterB = shapeB->getQueryFilterData();
- if (shapeFilterA.word0 & shapeFilterB.word1)
- return true;
-
- return false;
- }
-};
-
-PxQueryFilterCallback* Physics::GetQueryFilterCallback()
-{
- static QueryFilter Filter;
- return &Filter;
-}
-
-PxQueryFilterCallback* Physics::GetCharacterQueryFilterCallback()
-{
- static CharacterQueryFilter Filter;
- return &Filter;
-}
-
-PxControllerFilterCallback* Physics::GetCharacterControllerFilterCallback()
-{
- static CharacterControllerFilter Filter;
- return &Filter;
-}
bool Physics::RayCast(const Vector3& origin, const Vector3& direction, const float maxDistance, uint32 layerMask, bool hitTriggers)
{
- // Prepare data
- SCENE_QUERY_SETUP(true);
- PxRaycastBuffer buffer;
-
- // Perform raycast test
- return GetScene()->raycast(C2P(origin), C2P(direction), maxDistance, buffer, hitFlags, filterData, GetQueryFilterCallback());
+ return DefaultScene->RayCast(origin, direction, maxDistance, layerMask, hitTriggers);
}
bool Physics::RayCast(const Vector3& origin, const Vector3& direction, RayCastHit& hitInfo, const float maxDistance, uint32 layerMask, bool hitTriggers)
{
- // Prepare data
- SCENE_QUERY_SETUP(true);
- PxRaycastBuffer buffer;
-
- // Perform raycast test
- if (!GetScene()->raycast(C2P(origin), C2P(direction), maxDistance, buffer, hitFlags, filterData, GetQueryFilterCallback()))
- return false;
-
- // Collect results
- SCENE_QUERY_COLLECT_SINGLE();
-
- return true;
+ return DefaultScene->RayCast(origin, direction, hitInfo, maxDistance, layerMask, hitTriggers);
}
bool Physics::RayCastAll(const Vector3& origin, const Vector3& direction, Array& results, const float maxDistance, uint32 layerMask, bool hitTriggers)
{
- // Prepare data
- SCENE_QUERY_SETUP(false);
- DynamicHitBuffer buffer;
-
- // Perform raycast test
- if (!GetScene()->raycast(C2P(origin), C2P(direction), maxDistance, buffer, hitFlags, filterData, GetQueryFilterCallback()))
- return false;
-
- // Collect results
- SCENE_QUERY_COLLECT_ALL();
-
- return true;
+ return DefaultScene->RayCastAll(origin, direction, results, maxDistance, layerMask, hitTriggers);
}
bool Physics::BoxCast(const Vector3& center, const Vector3& halfExtents, const Vector3& direction, const Quaternion& rotation, const float maxDistance, uint32 layerMask, bool hitTriggers)
{
- // Prepare data
- SCENE_QUERY_SETUP_SWEEP_1();
- const PxTransform pose(C2P(center), C2P(rotation));
- const PxBoxGeometry geometry(C2P(halfExtents));
-
- // Perform sweep test
- return GetScene()->sweep(geometry, pose, C2P(direction), maxDistance, buffer, hitFlags, filterData, GetQueryFilterCallback());
+ return DefaultScene->BoxCast(center, halfExtents, direction, rotation, maxDistance, layerMask, hitTriggers);
}
bool Physics::BoxCast(const Vector3& center, const Vector3& halfExtents, const Vector3& direction, RayCastHit& hitInfo, const Quaternion& rotation, const float maxDistance, uint32 layerMask, bool hitTriggers)
{
- // Prepare data
- SCENE_QUERY_SETUP_SWEEP_1();
- const PxTransform pose(C2P(center), C2P(rotation));
- const PxBoxGeometry geometry(C2P(halfExtents));
-
- // Perform sweep test
- if (!GetScene()->sweep(geometry, pose, C2P(direction), maxDistance, buffer, hitFlags, filterData, GetQueryFilterCallback()))
- return false;
-
- // Collect results
- SCENE_QUERY_COLLECT_SINGLE();
-
- return true;
+ return DefaultScene->BoxCast(center, halfExtents, direction, hitInfo, rotation, maxDistance, layerMask, hitTriggers);
}
bool Physics::BoxCastAll(const Vector3& center, const Vector3& halfExtents, const Vector3& direction, Array& results, const Quaternion& rotation, const float maxDistance, uint32 layerMask, bool hitTriggers)
{
- // Prepare data
- SCENE_QUERY_SETUP_SWEEP();
- const PxTransform pose(C2P(center), C2P(rotation));
- const PxBoxGeometry geometry(C2P(halfExtents));
-
- // Perform sweep test
- if (!GetScene()->sweep(geometry, pose, C2P(direction), maxDistance, buffer, hitFlags, filterData, GetQueryFilterCallback()))
- return false;
-
- // Collect results
- SCENE_QUERY_COLLECT_ALL();
-
- return true;
+ return DefaultScene->BoxCastAll(center, halfExtents, direction, results, rotation, maxDistance, layerMask, hitTriggers);
}
bool Physics::SphereCast(const Vector3& center, const float radius, const Vector3& direction, const float maxDistance, uint32 layerMask, bool hitTriggers)
{
- // Prepare data
- SCENE_QUERY_SETUP_SWEEP_1();
- const PxTransform pose(C2P(center));
- const PxSphereGeometry geometry(radius);
-
- // Perform sweep test
- return GetScene()->sweep(geometry, pose, C2P(direction), maxDistance, buffer, hitFlags, filterData, GetQueryFilterCallback());
+ return DefaultScene->SphereCast(center, radius, direction, maxDistance, layerMask, hitTriggers);
}
bool Physics::SphereCast(const Vector3& center, const float radius, const Vector3& direction, RayCastHit& hitInfo, const float maxDistance, uint32 layerMask, bool hitTriggers)
{
- // Prepare data
- SCENE_QUERY_SETUP_SWEEP_1();
- const PxTransform pose(C2P(center));
- const PxSphereGeometry geometry(radius);
-
- // Perform sweep test
- if (!GetScene()->sweep(geometry, pose, C2P(direction), maxDistance, buffer, hitFlags, filterData, GetQueryFilterCallback()))
- return false;
-
- // Collect results
- SCENE_QUERY_COLLECT_SINGLE();
-
- return true;
+ return DefaultScene->SphereCast(center, radius, direction, hitInfo, maxDistance, layerMask, hitTriggers);
}
bool Physics::SphereCastAll(const Vector3& center, const float radius, const Vector3& direction, Array& results, const float maxDistance, uint32 layerMask, bool hitTriggers)
{
- // Prepare data
- SCENE_QUERY_SETUP_SWEEP();
- const PxTransform pose(C2P(center));
- const PxSphereGeometry geometry(radius);
-
- // Perform sweep test
- if (!GetScene()->sweep(geometry, pose, C2P(direction), maxDistance, buffer, hitFlags, filterData, GetQueryFilterCallback()))
- return false;
-
- // Collect results
- SCENE_QUERY_COLLECT_ALL();
-
- return true;
+ return DefaultScene->SphereCastAll(center, radius, direction, results, maxDistance, layerMask, hitTriggers);
}
bool Physics::CapsuleCast(const Vector3& center, const float radius, const float height, const Vector3& direction, const Quaternion& rotation, const float maxDistance, uint32 layerMask, bool hitTriggers)
{
- // Prepare data
- SCENE_QUERY_SETUP_SWEEP_1();
- const PxTransform pose(C2P(center), C2P(rotation));
- const PxCapsuleGeometry geometry(radius, height * 0.5f);
-
- // Perform sweep test
- return GetScene()->sweep(geometry, pose, C2P(direction), maxDistance, buffer, hitFlags, filterData, GetQueryFilterCallback());
+ return DefaultScene->CapsuleCast(center, radius, height, direction, rotation, maxDistance, layerMask, hitTriggers);
}
bool Physics::CapsuleCast(const Vector3& center, const float radius, const float height, const Vector3& direction, RayCastHit& hitInfo, const Quaternion& rotation, const float maxDistance, uint32 layerMask, bool hitTriggers)
{
- // Prepare data
- SCENE_QUERY_SETUP_SWEEP_1();
- const PxTransform pose(C2P(center), C2P(rotation));
- const PxCapsuleGeometry geometry(radius, height * 0.5f);
-
- // Perform sweep test
- if (!GetScene()->sweep(geometry, pose, C2P(direction), maxDistance, buffer, hitFlags, filterData, GetQueryFilterCallback()))
- return false;
-
- // Collect results
- SCENE_QUERY_COLLECT_SINGLE();
-
- return true;
+ return DefaultScene->CapsuleCast(center, radius, height, direction, hitInfo, rotation, maxDistance, layerMask, hitTriggers);
}
bool Physics::CapsuleCastAll(const Vector3& center, const float radius, const float height, const Vector3& direction, Array& results, const Quaternion& rotation, const float maxDistance, uint32 layerMask, bool hitTriggers)
{
- // Prepare data
- SCENE_QUERY_SETUP_SWEEP();
- const PxTransform pose(C2P(center), C2P(rotation));
- const PxCapsuleGeometry geometry(radius, height * 0.5f);
-
- // Perform sweep test
- if (!GetScene()->sweep(geometry, pose, C2P(direction), maxDistance, buffer, hitFlags, filterData, GetQueryFilterCallback()))
- return false;
-
- // Collect results
- SCENE_QUERY_COLLECT_ALL();
-
- return true;
+ return DefaultScene->CapsuleCastAll(center, radius, height, direction, results, rotation, maxDistance, layerMask, hitTriggers);
}
bool Physics::ConvexCast(const Vector3& center, const CollisionData* convexMesh, const Vector3& scale, const Vector3& direction, const Quaternion& rotation, const float maxDistance, uint32 layerMask, bool hitTriggers)
{
- CHECK_RETURN(convexMesh && convexMesh->GetOptions().Type == CollisionDataType::ConvexMesh, false)
-
- // Prepare data
- SCENE_QUERY_SETUP_SWEEP_1();
- const PxTransform pose(C2P(center), C2P(rotation));
- const PxConvexMeshGeometry geometry(convexMesh->GetConvex(), PxMeshScale(C2P(scale)));
-
- // Perform sweep test
- return GetScene()->sweep(geometry, pose, C2P(direction), maxDistance, buffer, hitFlags, filterData, GetQueryFilterCallback());
+ return DefaultScene->ConvexCast(center, convexMesh, scale, direction, rotation, maxDistance, layerMask, hitTriggers);
}
bool Physics::ConvexCast(const Vector3& center, const CollisionData* convexMesh, const Vector3& scale, const Vector3& direction, RayCastHit& hitInfo, const Quaternion& rotation, const float maxDistance, uint32 layerMask, bool hitTriggers)
{
- CHECK_RETURN(convexMesh && convexMesh->GetOptions().Type == CollisionDataType::ConvexMesh, false)
-
- // Prepare data
- SCENE_QUERY_SETUP_SWEEP_1();
- const PxTransform pose(C2P(center), C2P(rotation));
- const PxConvexMeshGeometry geometry(convexMesh->GetConvex(), PxMeshScale(C2P(scale)));
-
- // Perform sweep test
- if (!GetScene()->sweep(geometry, pose, C2P(direction), maxDistance, buffer, hitFlags, filterData, GetQueryFilterCallback()))
- return false;
-
- // Collect results
- SCENE_QUERY_COLLECT_SINGLE();
-
- return true;
+ return DefaultScene->ConvexCast(center, convexMesh, scale, direction, hitInfo, rotation, maxDistance, layerMask, hitTriggers);
}
bool Physics::ConvexCastAll(const Vector3& center, const CollisionData* convexMesh, const Vector3& scale, const Vector3& direction, Array& results, const Quaternion& rotation, const float maxDistance, uint32 layerMask, bool hitTriggers)
{
- CHECK_RETURN(convexMesh && convexMesh->GetOptions().Type == CollisionDataType::ConvexMesh, false)
-
- // Prepare data
- SCENE_QUERY_SETUP_SWEEP();
- const PxTransform pose(C2P(center), C2P(rotation));
- const PxConvexMeshGeometry geometry(convexMesh->GetConvex(), PxMeshScale(C2P(scale)));
-
- // Perform sweep test
- if (!GetScene()->sweep(geometry, pose, C2P(direction), maxDistance, buffer, hitFlags, filterData, GetQueryFilterCallback()))
- return false;
-
- // Collect results
- SCENE_QUERY_COLLECT_ALL();
-
- return true;
+ return DefaultScene->ConvexCastAll(center, convexMesh, scale, direction, results, rotation, maxDistance, layerMask, hitTriggers);
}
bool Physics::CheckBox(const Vector3& center, const Vector3& halfExtents, const Quaternion& rotation, uint32 layerMask, bool hitTriggers)
{
- // Prepare data
- SCENE_QUERY_SETUP_OVERLAP_1();
- const PxTransform pose(C2P(center), C2P(rotation));
- const PxBoxGeometry geometry(C2P(halfExtents));
-
- // Perform overlap test
- return GetScene()->overlap(geometry, pose, buffer, filterData, GetQueryFilterCallback());
+ return DefaultScene->CheckBox(center, halfExtents, rotation, layerMask, hitTriggers);
}
bool Physics::CheckSphere(const Vector3& center, const float radius, uint32 layerMask, bool hitTriggers)
{
- // Prepare data
- SCENE_QUERY_SETUP_OVERLAP_1();
- const PxTransform pose(C2P(center));
- const PxSphereGeometry geometry(radius);
-
- // Perform overlap test
- return GetScene()->overlap(geometry, pose, buffer, filterData, GetQueryFilterCallback());
+ return DefaultScene->CheckSphere(center, radius, layerMask, hitTriggers);
}
bool Physics::CheckCapsule(const Vector3& center, const float radius, const float height, const Quaternion& rotation, uint32 layerMask, bool hitTriggers)
{
- // Prepare data
- SCENE_QUERY_SETUP_OVERLAP_1();
- const PxTransform pose(C2P(center), C2P(rotation));
- const PxCapsuleGeometry geometry(radius, height * 0.5f);
-
- // Perform overlap test
- return GetScene()->overlap(geometry, pose, buffer, filterData, GetQueryFilterCallback());
+ return DefaultScene->CheckCapsule(center, radius, height, rotation, layerMask, hitTriggers);
}
bool Physics::CheckConvex(const Vector3& center, const CollisionData* convexMesh, const Vector3& scale, const Quaternion& rotation, uint32 layerMask, bool hitTriggers)
{
- CHECK_RETURN(convexMesh && convexMesh->GetOptions().Type == CollisionDataType::ConvexMesh, false)
-
- // Prepare data
- SCENE_QUERY_SETUP_OVERLAP_1();
- const PxTransform pose(C2P(center), C2P(rotation));
- const PxConvexMeshGeometry geometry(convexMesh->GetConvex(), PxMeshScale(C2P(scale)));
-
- // Perform overlap test
- return GetScene()->overlap(geometry, pose, buffer, filterData, GetQueryFilterCallback());
+ return DefaultScene->CheckConvex(center, convexMesh, scale, rotation, layerMask, hitTriggers);
}
bool Physics::OverlapBox(const Vector3& center, const Vector3& halfExtents, Array& results, const Quaternion& rotation, uint32 layerMask, bool hitTriggers)
{
- // Prepare data
- SCENE_QUERY_SETUP_OVERLAP();
- const PxTransform pose(C2P(center), C2P(rotation));
- const PxBoxGeometry geometry(C2P(halfExtents));
-
- // Perform overlap test
- if (!GetScene()->overlap(geometry, pose, buffer, filterData, GetQueryFilterCallback()))
- return false;
-
- // Collect results
- SCENE_QUERY_COLLECT_OVERLAP_COLLIDER();
-
- return true;
+ return DefaultScene->OverlapBox(center, halfExtents, results, rotation, layerMask, hitTriggers);
}
bool Physics::OverlapSphere(const Vector3& center, const float radius, Array& results, uint32 layerMask, bool hitTriggers)
{
- // Prepare data
- SCENE_QUERY_SETUP_OVERLAP();
- const PxTransform pose(C2P(center));
- const PxSphereGeometry geometry(radius);
-
- // Perform overlap test
- if (!GetScene()->overlap(geometry, pose, buffer, filterData, GetQueryFilterCallback()))
- return false;
-
- // Collect results
- SCENE_QUERY_COLLECT_OVERLAP_COLLIDER();
-
- return true;
+ return DefaultScene->OverlapSphere(center, radius, results, layerMask, hitTriggers);
}
bool Physics::OverlapCapsule(const Vector3& center, const float radius, const float height, Array& results, const Quaternion& rotation, uint32 layerMask, bool hitTriggers)
{
- // Prepare data
- SCENE_QUERY_SETUP_OVERLAP();
- const PxTransform pose(C2P(center), C2P(rotation));
- const PxCapsuleGeometry geometry(radius, height * 0.5f);
-
- // Perform overlap test
- if (!GetScene()->overlap(geometry, pose, buffer, filterData, GetQueryFilterCallback()))
- return false;
-
- // Collect results
- SCENE_QUERY_COLLECT_OVERLAP_COLLIDER();
-
- return true;
+ return DefaultScene->OverlapCapsule(center, radius, height, results, rotation, layerMask, hitTriggers);
}
bool Physics::OverlapConvex(const Vector3& center, const CollisionData* convexMesh, const Vector3& scale, Array& results, const Quaternion& rotation, uint32 layerMask, bool hitTriggers)
{
- CHECK_RETURN(convexMesh && convexMesh->GetOptions().Type == CollisionDataType::ConvexMesh, false)
-
- // Prepare data
- SCENE_QUERY_SETUP_OVERLAP();
- const PxTransform pose(C2P(center), C2P(rotation));
- const PxConvexMeshGeometry geometry(convexMesh->GetConvex(), PxMeshScale(C2P(scale)));
-
- // Perform overlap test
- if (!GetScene()->overlap(geometry, pose, buffer, filterData, GetQueryFilterCallback()))
- return false;
-
- // Collect results
- SCENE_QUERY_COLLECT_OVERLAP_COLLIDER();
-
- return true;
+ return DefaultScene->OverlapConvex(center, convexMesh, scale, results, rotation, layerMask, hitTriggers);
}
bool Physics::OverlapBox(const Vector3& center, const Vector3& halfExtents, Array& results, const Quaternion& rotation, uint32 layerMask, bool hitTriggers)
{
- // Prepare data
- SCENE_QUERY_SETUP_OVERLAP();
- const PxTransform pose(C2P(center), C2P(rotation));
- const PxBoxGeometry geometry(C2P(halfExtents));
-
- // Perform overlap test
- if (!GetScene()->overlap(geometry, pose, buffer, filterData, GetQueryFilterCallback()))
- return false;
-
- // Collect results
- SCENE_QUERY_COLLECT_OVERLAP();
-
- return true;
+ return DefaultScene->OverlapBox(center, halfExtents, results, rotation, layerMask, hitTriggers);
}
bool Physics::OverlapSphere(const Vector3& center, const float radius, Array& results, uint32 layerMask, bool hitTriggers)
{
- // Prepare data
- SCENE_QUERY_SETUP_OVERLAP();
- const PxTransform pose(C2P(center));
- const PxSphereGeometry geometry(radius);
-
- // Perform overlap test
- if (!GetScene()->overlap(geometry, pose, buffer, filterData, GetQueryFilterCallback()))
- return false;
-
- // Collect results
- SCENE_QUERY_COLLECT_OVERLAP();
-
- return true;
+ return DefaultScene->OverlapSphere(center, radius, results, layerMask, hitTriggers);
}
bool Physics::OverlapCapsule(const Vector3& center, const float radius, const float height, Array& results, const Quaternion& rotation, uint32 layerMask, bool hitTriggers)
{
- // Prepare data
- SCENE_QUERY_SETUP_OVERLAP();
- const PxTransform pose(C2P(center), C2P(rotation));
- const PxCapsuleGeometry geometry(radius, height * 0.5f);
-
- // Perform overlap test
- if (!GetScene()->overlap(geometry, pose, buffer, filterData, GetQueryFilterCallback()))
- return false;
-
- // Collect results
- SCENE_QUERY_COLLECT_OVERLAP();
-
- return true;
+ return DefaultScene->OverlapCapsule(center, radius, height, results, rotation, layerMask, hitTriggers);
}
bool Physics::OverlapConvex(const Vector3& center, const CollisionData* convexMesh, const Vector3& scale, Array& results, const Quaternion& rotation, uint32 layerMask, bool hitTriggers)
{
- CHECK_RETURN(convexMesh && convexMesh->GetOptions().Type == CollisionDataType::ConvexMesh, false)
-
- // Prepare data
- SCENE_QUERY_SETUP_OVERLAP();
- const PxTransform pose(C2P(center), C2P(rotation));
- const PxConvexMeshGeometry geometry(convexMesh->GetConvex(), PxMeshScale(C2P(scale)));
-
- // Perform overlap test
- if (!GetScene()->overlap(geometry, pose, buffer, filterData, GetQueryFilterCallback()))
- return false;
-
- // Collect results
- SCENE_QUERY_COLLECT_OVERLAP();
-
- return true;
+ return DefaultScene->OverlapConvex(center, convexMesh, scale, results, rotation, layerMask, hitTriggers);
}
diff --git a/Source/Engine/Physics/Physics.cpp b/Source/Engine/Physics/Physics.cpp
index 72e1e66ba..6198accfc 100644
--- a/Source/Engine/Physics/Physics.cpp
+++ b/Source/Engine/Physics/Physics.cpp
@@ -1,36 +1,26 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
#include "Physics.h"
+#include "PhysicsScene.h"
#include "PhysicalMaterial.h"
+
#include "Engine/Core/Log.h"
-#include "Engine/Threading/Threading.h"
#include "Engine/Platform/CPUInfo.h"
#include "PhysicsSettings.h"
#include "Utilities.h"
#include "PhysicsStepper.h"
-#include "SimulationEventCallback.h"
-#if WITH_VEHICLE
-#include "Actors/WheeledVehicle.h"
-#endif
+
#include "Engine/Level/Level.h"
-#include "Actors/PhysicsActor.h"
#include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Core/Memory/Memory.h"
#include "Engine/Engine/EngineService.h"
#include "Engine/Serialization/Serialization.h"
#include "Engine/Engine/Time.h"
#include
-#include
-#if WITH_VEHICLE
-#include
-#endif
#if WITH_PVD
#include
#endif
-// Temporary memory size used by the PhysX during the simulation. Must be multiply of 4kB and 16bit aligned.
-#define SCRATCH_BLOCK_SIZE (1024 * 128)
-
#define PHYSX_VEHICLE_DEBUG_TELEMETRY 0
#if PHYSX_VEHICLE_DEBUG_TELEMETRY
@@ -73,57 +63,6 @@ public:
}
};
-PxFilterFlags FilterShader(
- PxFilterObjectAttributes attributes0, PxFilterData filterData0,
- PxFilterObjectAttributes attributes1, PxFilterData filterData1,
- PxPairFlags& pairFlags, const void* constantBlock, PxU32 constantBlockSize)
-{
- // Let triggers through
- if (PxFilterObjectIsTrigger(attributes0) || PxFilterObjectIsTrigger(attributes1))
- {
- pairFlags |= PxPairFlag::eNOTIFY_TOUCH_FOUND;
- pairFlags |= PxPairFlag::eNOTIFY_TOUCH_LOST;
- pairFlags |= PxPairFlag::eDETECT_DISCRETE_CONTACT;
- return PxFilterFlag::eDEFAULT;
- }
-
- // Send events for the kinematic actors but don't solve the contact
- if (PxFilterObjectIsKinematic(attributes0) && PxFilterObjectIsKinematic(attributes1))
- {
- pairFlags |= PxPairFlag::eNOTIFY_TOUCH_FOUND;
- pairFlags |= PxPairFlag::eNOTIFY_TOUCH_PERSISTS;
- pairFlags |= PxPairFlag::eNOTIFY_TOUCH_LOST;
- pairFlags |= PxPairFlag::eDETECT_DISCRETE_CONTACT;
- return PxFilterFlag::eSUPPRESS;
- }
-
- // Trigger the contact callback for pairs (A,B) where the filtermask of A contains the ID of B and vice versa
- if ((filterData0.word0 & filterData1.word1) && (filterData1.word0 & filterData0.word1))
- {
- pairFlags |= PxPairFlag::eSOLVE_CONTACT;
- pairFlags |= PxPairFlag::eDETECT_DISCRETE_CONTACT;
- pairFlags |= PxPairFlag::eNOTIFY_TOUCH_FOUND;
- pairFlags |= PxPairFlag::eNOTIFY_TOUCH_PERSISTS;
- pairFlags |= PxPairFlag::ePOST_SOLVER_VELOCITY;
- pairFlags |= PxPairFlag::eNOTIFY_CONTACT_POINTS;
- return PxFilterFlag::eDEFAULT;
- }
-
- // Ignore pair (no collisions nor events)
- return PxFilterFlag::eKILL;
-}
-
-enum class ActionType
-{
- Sleep,
-};
-
-struct ActionData
-{
- ActionType Type;
- PxActor* Actor;
-};
-
PxPhysics* CPhysX = nullptr;
#if WITH_PVD
PxPvd* CPVD = nullptr;
@@ -133,48 +72,27 @@ namespace
{
PhysXAllocator PhysXAllocatorCallback;
PhysXError PhysXErrorCallback;
- PxSimulationFilterShader PhysXDefaultFilterShader = PxDefaultSimulationFilterShader;
PxTolerancesScale ToleranceScale;
- SimulationEventCallback EventsCallback;
- void* ScratchMemory = nullptr;
- FixedStepper* Stepper = nullptr;
- CriticalSection FlushLocker;
- Array NewActors;
- Array Actions;
- Array DeadActors;
- Array DeadMaterials;
- Array _deadObjects;
- Array DeadColliders;
- Array DeadJoints;
bool _queriesHitTriggers = true;
- bool _isDuringSimulation = false;
PhysicsCombineMode _frictionCombineMode = PhysicsCombineMode::Average;
PhysicsCombineMode _restitutionCombineMode = PhysicsCombineMode::Average;
PxFoundation* _foundation = nullptr;
#if COMPILE_WITH_PHYSICS_COOKING
PxCooking* Cooking = nullptr;
#endif
- PxScene* PhysicsScene = nullptr;
PxMaterial* DefaultMaterial = nullptr;
- PxControllerManager* ControllerManager = nullptr;
- PxCpuDispatcher* CpuDispatcher = nullptr;
- float LastDeltaTime = 0.0f;
#if WITH_VEHICLE
bool VehicleSDKInitialized = false;
- Array WheelVehiclesCache;
- Array WheelQueryResults;
- Array WheelHitResults;
- Array WheelVehiclesResultsPerWheel;
- Array WheelVehiclesResultsPerVehicle;
- PxBatchQuery* WheelRaycastBatchQuery = nullptr;
- PxVehicleDrivableSurfaceToTireFrictionPairs* WheelTireFrictions = nullptr;
#endif
+
+ void reset()
+ {
+ Physics::Scenes.Resize(1);
+ }
}
-#if WITH_VEHICLE
-Array WheelVehicles;
-#endif
-bool Physics::AutoSimulation = true;
+PhysicsScene* Physics::DefaultScene = nullptr;
+Array Physics::Scenes;
uint32 Physics::LayerMasks[32];
class PhysicsService : public EngineService
@@ -233,23 +151,6 @@ void InitVehicleSDK()
}
}
-static PxQueryHitType::Enum WheelRaycastPreFilter(PxFilterData filterData0, PxFilterData filterData1, const void* constantBlock, PxU32 constantBlockSize, PxHitFlags& queryFlags)
-{
- // Hardcoded id for vehicle shapes masking
- if (filterData0.word3 == filterData1.word3)
- {
- return PxQueryHitType::eNONE;
- }
-
- // Collide for pairs (A,B) where the filtermask of A contains the ID of B and vice versa
- if ((filterData0.word0 & filterData1.word1) && (filterData1.word0 & filterData0.word1))
- {
- return PxQueryHitType::eBLOCK;
- }
-
- return PxQueryHitType::eNONE;
-}
-
#endif
void PhysicsSettings::Apply()
@@ -325,7 +226,8 @@ PhysicalMaterial::~PhysicalMaterial()
{
if (_material)
{
- Physics::RemoveMaterial(_material);
+ for (auto scene : Physics::Scenes)
+ scene->RemoveMaterial(_material);
}
}
@@ -366,7 +268,6 @@ bool PhysicsService::Init()
{
#define CHECK_INIT(value, msg) if(!value) { LOG(Error, msg); return true; }
- auto cpuInfo = Platform::GetCPUInfo();
auto& settings = *PhysicsSettings::Get();
// Send info
@@ -422,45 +323,7 @@ bool PhysicsService::Init()
}
#endif
- // Create scene description
- PxSceneDesc sceneDesc(CPhysX->getTolerancesScale());
- sceneDesc.gravity = C2P(settings.DefaultGravity);
- sceneDesc.flags |= PxSceneFlag::eENABLE_ACTIVE_ACTORS;
- if (!settings.DisableCCD)
- sceneDesc.flags |= PxSceneFlag::eENABLE_CCD;
- if (settings.EnableAdaptiveForce)
- sceneDesc.flags |= PxSceneFlag::eADAPTIVE_FORCE;
- sceneDesc.simulationEventCallback = &EventsCallback;
- sceneDesc.filterShader = FilterShader;
- sceneDesc.bounceThresholdVelocity = settings.BounceThresholdVelocity;
- if (sceneDesc.cpuDispatcher == nullptr)
- {
- CpuDispatcher = PxDefaultCpuDispatcherCreate(Math::Clamp(cpuInfo.ProcessorCoreCount - 1, 1, 4));
- CHECK_INIT(CpuDispatcher, "PxDefaultCpuDispatcherCreate failed!");
- sceneDesc.cpuDispatcher = CpuDispatcher;
- }
- if (sceneDesc.filterShader == nullptr)
- {
- sceneDesc.filterShader = PhysXDefaultFilterShader;
- }
-
- // Create scene
- PhysicsScene = CPhysX->createScene(sceneDesc);
- CHECK_INIT(PhysicsScene, "createScene failed!");
-#if WITH_PVD
- auto pvdClient = PhysicsScene->getScenePvdClient();
- if (pvdClient)
- {
- pvdClient->setScenePvdFlags(PxPvdSceneFlag::eTRANSMIT_CONSTRAINTS | PxPvdSceneFlag::eTRANSMIT_SCENEQUERIES | PxPvdSceneFlag::eTRANSMIT_CONTACTS);
- }
- else
- {
- LOG(Info, "Missing PVD client scene.");
- }
-#endif
-
- // Init characters controller
- ControllerManager = PxCreateControllerManager(*PhysicsScene);
+ Physics::DefaultScene = Physics::FindOrCreateScene(TEXT("Default"));
// Create default resources
DefaultMaterial = CPhysX->createMaterial(0.7f, 0.7f, 0.3f);
@@ -472,15 +335,21 @@ bool PhysicsService::Init()
void PhysicsService::LateUpdate()
{
- Physics::FlushRequests();
+ for (auto scene : Physics::Scenes)
+ scene->FlushRequests();
}
void PhysicsService::Dispose()
{
// Ensure to finish (wait for simulation end)
- Physics::CollectResults();
- if (CPhysX)
- Physics::FlushRequests();
+
+ for (auto scene : Physics::Scenes)
+ {
+ scene->CollectResults();
+
+ if (CPhysX)
+ scene->FlushRequests();
+ }
#if WITH_VEHICLE
if (VehicleSDKInitialized)
@@ -488,21 +357,13 @@ void PhysicsService::Dispose()
VehicleSDKInitialized = false;
PxCloseVehicleSDK();
}
- RELEASE_PHYSX(WheelRaycastBatchQuery);
- RELEASE_PHYSX(WheelTireFrictions);
- WheelQueryResults.Resize(0);
- WheelHitResults.Resize(0);
- WheelVehiclesResultsPerWheel.Resize(0);
- WheelVehiclesResultsPerVehicle.Resize(0);
#endif
// Cleanup
RELEASE_PHYSX(DefaultMaterial);
- SAFE_DELETE(Stepper);
- Allocator::Free(ScratchMemory);
- ScratchMemory = nullptr;
- RELEASE_PHYSX(ControllerManager);
- SAFE_DELETE(CpuDispatcher);
+
+ Physics::Scenes.Resize(0);
+ Physics::DefaultScene = nullptr;
// Remove all scenes still registered
const int32 numScenes = CPhysX ? CPhysX->getNbScenes() : 0;
@@ -548,16 +409,6 @@ PxCooking* Physics::GetCooking()
return Cooking;
}
-PxScene* Physics::GetScene()
-{
- return PhysicsScene;
-}
-
-PxControllerManager* Physics::GetControllerManager()
-{
- return ControllerManager;
-}
-
PxTolerancesScale* Physics::GetTolerancesScale()
{
return &ToleranceScale;
@@ -565,415 +416,70 @@ PxTolerancesScale* Physics::GetTolerancesScale()
Vector3 Physics::GetGravity()
{
- return PhysicsScene ? P2C(PhysicsScene->getGravity()) : Vector3::Zero;
+ return DefaultScene ? DefaultScene->GetGravity() : Vector3::Zero;
}
void Physics::SetGravity(const Vector3& value)
{
- if (PhysicsScene)
- PhysicsScene->setGravity(C2P(value));
+ if (DefaultScene)
+ DefaultScene->SetGravity(value);
}
bool Physics::GetEnableCCD()
{
- return PhysicsScene ? (PhysicsScene->getFlags() & PxSceneFlag::eENABLE_CCD) == PxSceneFlag::eENABLE_CCD : !PhysicsSettings::Get()->DisableCCD;
+ return DefaultScene ? DefaultScene->GetEnableCCD() : !PhysicsSettings::Get()->DisableCCD;
}
void Physics::SetEnableCCD(const bool value)
{
- if (PhysicsScene)
- PhysicsScene->setFlag(PxSceneFlag::eENABLE_CCD, value);
+ if (DefaultScene)
+ DefaultScene->SetEnableCCD(value);
}
float Physics::GetBounceThresholdVelocity()
{
- return PhysicsScene ? PhysicsScene->getBounceThresholdVelocity() : PhysicsSettings::Get()->BounceThresholdVelocity;
+ return DefaultScene ? DefaultScene->GetBounceThresholdVelocity() : PhysicsSettings::Get()->BounceThresholdVelocity;
}
void Physics::SetBounceThresholdVelocity(const float value)
{
- if (PhysicsScene)
- PhysicsScene->setBounceThresholdVelocity(value);
+ if (DefaultScene)
+ DefaultScene->SetBounceThresholdVelocity(value);
}
void Physics::Simulate(float dt)
{
- ASSERT(IsInMainThread() && !_isDuringSimulation);
- ASSERT(CPhysX);
- const auto& settings = *PhysicsSettings::Get();
+ if (DefaultScene)
+ DefaultScene->Simulate(dt);
+}
- // Flush the old/new objects and the other requests before the simulation
- FlushRequests();
-
- // Clamp delta
- dt = Math::Clamp(dt, 0.0f, settings.MaxDeltaTime);
-
- // Prepare util objects
- if (ScratchMemory == nullptr)
+void Physics::SimulateAll(float dt)
+{
+ for (auto scene : Scenes)
{
- ScratchMemory = Allocator::Allocate(SCRATCH_BLOCK_SIZE, 16);
+ if (scene->GetAutoSimulation())
+ scene->Simulate(dt);
}
- if (Stepper == nullptr)
- {
- Stepper = New();
- }
- if (settings.EnableSubstepping)
- {
- // Use substeps
- Stepper->Setup(settings.SubstepDeltaTime, settings.MaxSubsteps);
- }
- else
- {
- // Use single step
- Stepper->Setup(dt);
- }
-
- // Start simulation (may not be fired due to too small delta time)
- _isDuringSimulation = true;
- if (Stepper->advance(PhysicsScene, dt, ScratchMemory, SCRATCH_BLOCK_SIZE) == false)
- return;
- EventsCallback.Clear();
- LastDeltaTime = dt;
-
- // TODO: move this call after rendering done
- Stepper->renderDone();
}
void Physics::CollectResults()
{
- if (!_isDuringSimulation)
- return;
- ASSERT(IsInMainThread());
- ASSERT(CPhysX && Stepper);
+ if (DefaultScene)
+ DefaultScene->CollectResults();
+}
- {
- PROFILE_CPU_NAMED("Physics.Fetch");
-
- // Gather results (with waiting for the end)
- Stepper->wait(PhysicsScene);
- }
-
-#if WITH_VEHICLE
- if (WheelVehicles.HasItems())
- {
- PROFILE_CPU_NAMED("Physics.Vehicles");
-
- // Update vehicles steering
- WheelVehiclesCache.Clear();
- WheelVehiclesCache.EnsureCapacity(WheelVehicles.Count());
- int32 wheelsCount = 0;
- for (auto wheelVehicle : WheelVehicles)
- {
- if (!wheelVehicle->IsActiveInHierarchy())
- continue;
- auto drive = (PxVehicleWheels*)wheelVehicle->_drive;
- ASSERT(drive);
- WheelVehiclesCache.Add(drive);
- wheelsCount += drive->mWheelsSimData.getNbWheels();
-
- float throttle = wheelVehicle->_throttle;
- float brake = wheelVehicle->_brake;
- if (wheelVehicle->UseReverseAsBrake)
- {
- const float invalidDirectionThreshold = 80.0f;
- const float breakThreshold = 8.0f;
- const float forwardSpeed = wheelVehicle->GetForwardSpeed();
-
- // Automatic gear change when changing driving direction
- if (Math::Abs(forwardSpeed) < invalidDirectionThreshold)
- {
- if (throttle < -ZeroTolerance && wheelVehicle->GetCurrentGear() >= 0 && wheelVehicle->GetTargetGear() >= 0)
- {
- wheelVehicle->SetCurrentGear(-1);
- }
- else if (throttle > ZeroTolerance && wheelVehicle->GetCurrentGear() <= 0 && wheelVehicle->GetTargetGear() <= 0)
- {
- wheelVehicle->SetCurrentGear(1);
- }
- }
-
- // Automatic break when changing driving direction
- if (throttle > 0.0f)
- {
- if (forwardSpeed < -invalidDirectionThreshold)
- {
- brake = 1.0f;
- }
- }
- else if (throttle < 0.0f)
- {
- if (forwardSpeed > invalidDirectionThreshold)
- {
- brake = 1.0f;
- }
- }
- else
- {
- if (forwardSpeed < breakThreshold && forwardSpeed > -breakThreshold)
- {
- brake = 1.0f;
- }
- }
-
- // Block throttle if user is changing driving direction
- if ((throttle > 0.0f && wheelVehicle->GetTargetGear() < 0) || (throttle < 0.0f && wheelVehicle->GetTargetGear() > 0))
- {
- throttle = 0.0f;
- }
-
- throttle = Math::Abs(throttle);
- }
- else
- {
- throttle = Math::Max(throttle, 0.0f);
- }
- // @formatter:off
- // Reference: PhysX SDK docs
- // TODO: expose input control smoothing data
- static constexpr PxVehiclePadSmoothingData padSmoothing =
- {
- {
- 6.0f, // rise rate eANALOG_INPUT_ACCEL
- 6.0f, // rise rate eANALOG_INPUT_BRAKE
- 12.0f, // rise rate eANALOG_INPUT_HANDBRAKE
- 2.5f, // rise rate eANALOG_INPUT_STEER_LEFT
- 2.5f, // rise rate eANALOG_INPUT_STEER_RIGHT
- },
- {
- 10.0f, // fall rate eANALOG_INPUT_ACCEL
- 10.0f, // fall rate eANALOG_INPUT_BRAKE
- 12.0f, // fall rate eANALOG_INPUT_HANDBRAKE
- 5.0f, // fall rate eANALOG_INPUT_STEER_LEFT
- 5.0f, // fall rate eANALOG_INPUT_STEER_RIGHT
- }
- };
- PxVehicleKeySmoothingData keySmoothing =
- {
- {
- 3.0f, // rise rate eANALOG_INPUT_ACCEL
- 3.0f, // rise rate eANALOG_INPUT_BRAKE
- 10.0f, // rise rate eANALOG_INPUT_HANDBRAKE
- 2.5f, // rise rate eANALOG_INPUT_STEER_LEFT
- 2.5f, // rise rate eANALOG_INPUT_STEER_RIGHT
- },
- {
- 5.0f, // fall rate eANALOG_INPUT__ACCEL
- 5.0f, // fall rate eANALOG_INPUT__BRAKE
- 10.0f, // fall rate eANALOG_INPUT__HANDBRAKE
- 5.0f, // fall rate eANALOG_INPUT_STEER_LEFT
- 5.0f // fall rate eANALOG_INPUT_STEER_RIGHT
- }
- };
- // Reference: PhysX SDK docs
- // TODO: expose steer vs forward curve into per-vehicle (up to 8 points, values clamped into 0/1 range)
- static constexpr PxF32 steerVsForwardSpeedData[] =
- {
- 0.0f, 1.0f,
- 20.0f, 0.9f,
- 65.0f, 0.8f,
- 120.0f, 0.7f,
- PX_MAX_F32, PX_MAX_F32,
- PX_MAX_F32, PX_MAX_F32,
- PX_MAX_F32, PX_MAX_F32,
- PX_MAX_F32, PX_MAX_F32,
- };
- const PxFixedSizeLookupTable<8> steerVsForwardSpeed(steerVsForwardSpeedData, 4);
- // @formatter:on
- if (wheelVehicle->UseAnalogSteering)
- {
- switch (wheelVehicle->_driveTypeCurrent)
- {
- case WheeledVehicle::DriveTypes::Drive4W:
- {
- PxVehicleDrive4WRawInputData rawInputData;
- rawInputData.setAnalogAccel(throttle);
- rawInputData.setAnalogBrake(brake);
- rawInputData.setAnalogSteer(wheelVehicle->_steering);
- rawInputData.setAnalogHandbrake(wheelVehicle->_handBrake);
- PxVehicleDrive4WSmoothAnalogRawInputsAndSetAnalogInputs(padSmoothing, steerVsForwardSpeed, rawInputData, LastDeltaTime, false, *(PxVehicleDrive4W*)drive);
- break;
- }
- case WheeledVehicle::DriveTypes::DriveNW:
- {
- PxVehicleDriveNWRawInputData rawInputData;
- rawInputData.setAnalogAccel(throttle);
- rawInputData.setAnalogBrake(brake);
- rawInputData.setAnalogSteer(wheelVehicle->_steering);
- rawInputData.setAnalogHandbrake(wheelVehicle->_handBrake);
- PxVehicleDriveNWSmoothAnalogRawInputsAndSetAnalogInputs(padSmoothing, steerVsForwardSpeed, rawInputData, LastDeltaTime, false, *(PxVehicleDriveNW*)drive);
- break;
- }
- }
- }
- else
- {
- const float deadZone = 0.1f;
- switch (wheelVehicle->_driveTypeCurrent)
- {
- case WheeledVehicle::DriveTypes::Drive4W:
- {
- PxVehicleDrive4WRawInputData rawInputData;
- rawInputData.setDigitalAccel(throttle > deadZone);
- rawInputData.setDigitalBrake(brake > deadZone);
- rawInputData.setDigitalSteerLeft(wheelVehicle->_steering < -deadZone);
- rawInputData.setDigitalSteerRight(wheelVehicle->_steering > deadZone);
- rawInputData.setDigitalHandbrake(wheelVehicle->_handBrake > deadZone);
- PxVehicleDrive4WSmoothDigitalRawInputsAndSetAnalogInputs(keySmoothing, steerVsForwardSpeed, rawInputData, LastDeltaTime, false, *(PxVehicleDrive4W*)drive);
- break;
- }
- case WheeledVehicle::DriveTypes::DriveNW:
- {
- PxVehicleDriveNWRawInputData rawInputData;
- rawInputData.setDigitalAccel(throttle > deadZone);
- rawInputData.setDigitalBrake(brake > deadZone);
- rawInputData.setDigitalSteerLeft(wheelVehicle->_steering < -deadZone);
- rawInputData.setDigitalSteerRight(wheelVehicle->_steering > deadZone);
- rawInputData.setDigitalHandbrake(wheelVehicle->_handBrake > deadZone);
- PxVehicleDriveNWSmoothDigitalRawInputsAndSetAnalogInputs(keySmoothing, steerVsForwardSpeed, rawInputData, LastDeltaTime, false, *(PxVehicleDriveNW*)drive);
- break;
- }
- }
- }
- }
-
- // Update batches queries cache
- if (wheelsCount > WheelQueryResults.Count())
- {
- if (WheelRaycastBatchQuery)
- WheelRaycastBatchQuery->release();
- WheelQueryResults.Resize(wheelsCount, false);
- WheelHitResults.Resize(wheelsCount, false);
- PxBatchQueryDesc desc(wheelsCount, 0, 0);
- desc.queryMemory.userRaycastResultBuffer = WheelQueryResults.Get();
- desc.queryMemory.userRaycastTouchBuffer = WheelHitResults.Get();
- desc.queryMemory.raycastTouchBufferSize = wheelsCount;
- desc.preFilterShader = WheelRaycastPreFilter;
- WheelRaycastBatchQuery = PhysicsScene->createBatchQuery(desc);
- }
-
- // TODO: expose vehicle tires configuration
- if (!WheelTireFrictions)
- {
- PxVehicleDrivableSurfaceType surfaceTypes[1];
- surfaceTypes[0].mType = 0;
- const PxMaterial* surfaceMaterials[1];
- surfaceMaterials[0] = DefaultMaterial;
- WheelTireFrictions = PxVehicleDrivableSurfaceToTireFrictionPairs::allocate(1, 1);
- WheelTireFrictions->setup(1, 1, surfaceMaterials, surfaceTypes);
- WheelTireFrictions->setTypePairFriction(0, 0, 5.0f);
- }
-
- // Setup cache for wheel states
- WheelVehiclesResultsPerVehicle.Resize(WheelVehiclesCache.Count(), false);
- WheelVehiclesResultsPerWheel.Resize(wheelsCount, false);
- wheelsCount = 0;
- for (int32 i = 0, ii = 0; i < WheelVehicles.Count(); i++)
- {
- auto wheelVehicle = WheelVehicles[i];
- if (!wheelVehicle->IsActiveInHierarchy())
- continue;
- auto drive = (PxVehicleWheels*)WheelVehicles[ii]->_drive;
- auto& perVehicle = WheelVehiclesResultsPerVehicle[ii];
- ii++;
- perVehicle.nbWheelQueryResults = drive->mWheelsSimData.getNbWheels();
- perVehicle.wheelQueryResults = WheelVehiclesResultsPerWheel.Get() + wheelsCount;
- wheelsCount += perVehicle.nbWheelQueryResults;
- }
-
- // Update vehicles
- if (WheelVehiclesCache.Count() != 0)
- {
- PxVehicleSuspensionRaycasts(WheelRaycastBatchQuery, WheelVehiclesCache.Count(), WheelVehiclesCache.Get(), WheelQueryResults.Count(), WheelQueryResults.Get());
- PxVehicleUpdates(LastDeltaTime, PhysicsScene->getGravity(), *WheelTireFrictions, WheelVehiclesCache.Count(), WheelVehiclesCache.Get(), WheelVehiclesResultsPerVehicle.Get());
- }
-
- // Synchronize state
- for (int32 i = 0, ii = 0; i < WheelVehicles.Count(); i++)
- {
- auto wheelVehicle = WheelVehicles[i];
- if (!wheelVehicle->IsActiveInHierarchy())
- continue;
- auto drive = WheelVehiclesCache[ii];
- auto& perVehicle = WheelVehiclesResultsPerVehicle[ii];
- ii++;
-#if PHYSX_VEHICLE_DEBUG_TELEMETRY
- LOG(Info, "Vehicle[{}] Gear={}, RPM={}", ii, wheelVehicle->GetCurrentGear(), (int32)wheelVehicle->GetEngineRotationSpeed());
-#endif
-
- // Update wheels
- for (int32 j = 0; j < wheelVehicle->_wheelsData.Count(); j++)
- {
- auto& wheelData = wheelVehicle->_wheelsData[j];
- auto& perWheel = perVehicle.wheelQueryResults[j];
-#if PHYSX_VEHICLE_DEBUG_TELEMETRY
- LOG(Info, "Vehicle[{}] Wheel[{}] longitudinalSlip={}, lateralSlip={}, suspSpringForce={}", ii, j, Utilities::RoundTo2DecimalPlaces(perWheel.longitudinalSlip), Utilities::RoundTo2DecimalPlaces(perWheel.lateralSlip), (int32)perWheel.suspSpringForce);
-#endif
-
- auto& state = wheelData.State;
- state.IsInAir = perWheel.isInAir;
- state.TireContactCollider = perWheel.tireContactShape ? static_cast(perWheel.tireContactShape->userData) : nullptr;
- state.TireContactPoint = P2C(perWheel.tireContactPoint);
- state.TireContactNormal = P2C(perWheel.tireContactNormal);
- state.TireFriction = perWheel.tireFriction;
- state.SteerAngle = RadiansToDegrees * perWheel.steerAngle;
- state.RotationAngle = -RadiansToDegrees * drive->mWheelsDynData.getWheelRotationAngle(j);
- state.SuspensionOffset = perWheel.suspJounce;
-#if USE_EDITOR
- state.SuspensionTraceStart = P2C(perWheel.suspLineStart);
- state.SuspensionTraceEnd = P2C(perWheel.suspLineStart + perWheel.suspLineDir * perWheel.suspLineLength);
-#endif
-
- if (!wheelData.Collider)
- continue;
- auto shape = wheelData.Collider->GetPxShape();
-
- // Update wheel collider transformation
- auto localPose = shape->getLocalPose();
- Transform t = wheelData.Collider->GetLocalTransform();
- t.Orientation = Quaternion::Euler(0, state.SteerAngle, state.RotationAngle) * wheelData.LocalOrientation;
- t.Translation = P2C(localPose.p) / wheelVehicle->GetScale() - t.Orientation * wheelData.Collider->GetCenter();
- wheelData.Collider->SetLocalTransform(t);
- }
- }
- }
-#endif
-
- {
- PROFILE_CPU_NAMED("Physics.FlushActiveTransforms");
-
- // Gather change info
- PxU32 activeActorsCount;
- PxActor** activeActors = PhysicsScene->getActiveActors(activeActorsCount);
- if (activeActorsCount > 0)
- {
- // Update changed transformations
- // TODO: use jobs system if amount if huge
- for (uint32 i = 0; i < activeActorsCount; i++)
- {
- const auto pxActor = (PxRigidActor*)*activeActors++;
- auto actor = dynamic_cast((Actor*)pxActor->userData);
- ASSERT(actor);
- actor->OnActiveTransformChanged(pxActor->getGlobalPose());
- }
- }
- }
-
- {
- PROFILE_CPU_NAMED("Physics.SendEvents");
-
- EventsCallback.CollectResults();
- EventsCallback.SendTriggerEvents();
- EventsCallback.SendCollisionEvents();
- EventsCallback.SendJointEvents();
- }
-
- // End
- _isDuringSimulation = false;
+void Physics::CollectResultsAll()
+{
+ for (auto scene : Scenes)
+ scene->CollectResults();
}
bool Physics::IsDuringSimulation()
{
- return _isDuringSimulation;
+ if (DefaultScene)
+ return DefaultScene->IsDuringSimulation();
+
+ return false;
}
PxMaterial* Physics::GetDefaultMaterial()
@@ -981,163 +487,48 @@ PxMaterial* Physics::GetDefaultMaterial()
return DefaultMaterial;
}
-void Physics::FlushRequests()
+bool Physics::GetAutoSimulation()
{
- ASSERT(!IsDuringSimulation());
- ASSERT(CPhysX);
+ if (DefaultScene)
+ return DefaultScene->GetAutoSimulation();
- PROFILE_CPU();
-
- FlushLocker.Lock();
-
- // Note: this does not handle case when actor is removed and added to the scene at the same time
-
- if (NewActors.HasItems())
- {
- GetScene()->addActors(NewActors.Get(), NewActors.Count());
- NewActors.Clear();
- }
-
- for (int32 i = 0; i < Actions.Count(); i++)
- {
- const auto action = Actions[i];
- switch (action.Type)
- {
- case ActionType::Sleep:
- static_cast(action.Actor)->putToSleep();
- break;
- }
- }
- Actions.Clear();
-
- if (DeadActors.HasItems())
- {
- GetScene()->removeActors(DeadActors.Get(), DeadActors.Count(), true);
- for (int32 i = 0; i < DeadActors.Count(); i++)
- {
- DeadActors[i]->release();
- }
- DeadActors.Clear();
- }
-
- if (DeadColliders.HasItems())
- {
- for (int32 i = 0; i < DeadColliders.Count(); i++)
- {
- EventsCallback.OnColliderRemoved(DeadColliders[i]);
- }
- DeadColliders.Clear();
- }
-
- if (DeadJoints.HasItems())
- {
- for (int32 i = 0; i < DeadJoints.Count(); i++)
- {
- EventsCallback.OnJointRemoved(DeadJoints[i]);
- }
- DeadJoints.Clear();
- }
-
- for (int32 i = 0; i < DeadMaterials.Count(); i++)
- {
- auto material = DeadMaterials[i];
-
- // Unlink ref to flax object
- material->userData = nullptr;
-
- material->release();
- }
- DeadMaterials.Clear();
-
- for (int32 i = 0; i < _deadObjects.Count(); i++)
- {
- _deadObjects[i]->release();
- }
- _deadObjects.Clear();
-
- FlushLocker.Unlock();
+ return false;
}
-void Physics::RemoveMaterial(PxMaterial* material)
+void Physics::FlushRequestsAll()
{
- ASSERT(material);
-
- FlushLocker.Lock();
- DeadMaterials.Add(material);
- FlushLocker.Unlock();
-}
-
-void Physics::RemoveObject(PxBase* obj)
-{
- ASSERT(obj);
-
- FlushLocker.Lock();
- _deadObjects.Add(obj);
- FlushLocker.Unlock();
-}
-
-void Physics::AddActor(PxActor* actor)
-{
- ASSERT(actor);
-
- FlushLocker.Lock();
- if (IsInMainThread())
- {
- GetScene()->addActor(*actor);
- }
- else
- {
- NewActors.Add(actor);
- }
- FlushLocker.Unlock();
-}
-
-void Physics::AddActor(PxRigidDynamic* actor, bool putToSleep)
-{
- ASSERT(actor);
-
- FlushLocker.Lock();
- if (IsInMainThread())
- {
- GetScene()->addActor(*actor);
- if (putToSleep)
- actor->putToSleep();
- }
- else
- {
- NewActors.Add(actor);
- if (putToSleep)
- Actions.Add({ ActionType::Sleep, actor });
- }
- FlushLocker.Unlock();
-}
-
-void Physics::RemoveActor(PxActor* actor)
-{
- ASSERT(actor);
-
- // Unlink ref to flax object
- actor->userData = nullptr;
-
- FlushLocker.Lock();
- DeadActors.Add(actor);
- FlushLocker.Unlock();
-}
-
-void Physics::RemoveCollider(PhysicsColliderActor* collider)
-{
- ASSERT(collider);
-
- FlushLocker.Lock();
- DeadColliders.Add(collider);
- FlushLocker.Unlock();
+ for (auto scene : Scenes)
+ scene->FlushRequests();
}
void Physics::RemoveJoint(Joint* joint)
{
- ASSERT(joint);
-
- FlushLocker.Lock();
- DeadJoints.Add(joint);
- FlushLocker.Unlock();
+ for (auto scene : Scenes)
+ scene->RemoveJoint(joint);
+}
+
+PhysicsScene* Physics::FindOrCreateScene(const String& name)
+{
+ auto scene = FindScene(name);
+
+ if (scene == nullptr)
+ {
+ auto& settings = *PhysicsSettings::Get();
+
+ scene = New(name, settings);
+ Scenes.Add(scene);
+ }
+
+ return scene;
+}
+
+PhysicsScene* Physics::FindScene(const String& name)
+{
+ for (auto scene : Scenes)
+ {
+ if (scene->GetName() == name)
+ return scene;
+ }
+
+ return nullptr;
}
diff --git a/Source/Engine/Physics/Physics.h b/Source/Engine/Physics/Physics.h
index 6eb38dcd6..f2317288a 100644
--- a/Source/Engine/Physics/Physics.h
+++ b/Source/Engine/Physics/Physics.h
@@ -9,6 +9,7 @@
#include "Types.h"
class PhysicsColliderActor;
+class PhysicsScene;
class Joint;
class Collider;
class CollisionData;
@@ -87,47 +88,45 @@ DECLARE_SCRIPTING_TYPE_NO_SPAWN(Physics);
#endif
- ///
- /// Gets PhysX scene object
- ///
- static PxScene* GetScene();
-
- ///
- /// Gets PhysX characters controller manager object
- ///
- static PxControllerManager* GetControllerManager();
-
///
/// Gets the physics tolerances scale.
///
static PxTolerancesScale* GetTolerancesScale();
- ///
- /// Gets the default query filter callback used for the scene queries.
- ///
- static PxQueryFilterCallback* GetQueryFilterCallback();
-
- ///
- /// Gets the default query filter callback used for the character controller collisions detection.
- ///
- static PxQueryFilterCallback* GetCharacterQueryFilterCallback();
-
- ///
- /// Gets the default controller filter callback used for the character controller collisions detection.
- ///
- static PxControllerFilterCallback* GetCharacterControllerFilterCallback();
-
///
/// Gets the default physical material.
///
static PxMaterial* GetDefaultMaterial();
public:
+
+ ///
+ /// The default physics scene.
+ ///
+ API_FIELD(ReadOnly)
+ static PhysicsScene* DefaultScene;
+ ///
+ /// List with all physics scenes (readonly).
+ ///
+ API_FIELD(ReadOnly)
+ static Array Scenes;
+
+ ///
+ /// Finds an existing or creates it if it does not exist.
+ ///
+ API_FUNCTION() static PhysicsScene* FindOrCreateScene(const String& name);
+
+ ///
+ ///Finds an existing scene.
+ ///
+ API_FUNCTION() static PhysicsScene* FindScene(const String& name);
+
+public:
///
/// The automatic simulation feature. True if perform physics simulation after on fixed update by auto, otherwise user should do it.
///
- API_FIELD() static bool AutoSimulation;
+ API_PROPERTY() static bool GetAutoSimulation();
///
/// Gets the current gravity force.
@@ -519,11 +518,22 @@ public:
/// The delta time (in seconds).
API_FUNCTION() static void Simulate(float dt);
+ ///
+ /// Called during main engine loop to start physic simulation on all registered scenes.
+ ///
+ /// The delta time (in seconds).
+ static void SimulateAll(float dt);
+
///
/// Called during main engine loop to collect physic simulation results and apply them as well as fire collision events.
///
API_FUNCTION() static void CollectResults();
+ ///
+ /// Called during main engine loop to collect physic simulation results on all registered scenes and apply them as well as fire collision events.
+ ///
+ static void CollectResultsAll();
+
///
/// Checks if physical simulation is running
///
@@ -535,7 +545,7 @@ public:
///
/// Flushes the async requests to add/remove actors, remove materials, etc..
///
- static void FlushRequests();
+ static void FlushRequestsAll();
///
/// Removes the material (using safe async request).
diff --git a/Source/Engine/Physics/PhysicsScene.Queries.cpp b/Source/Engine/Physics/PhysicsScene.Queries.cpp
new file mode 100644
index 000000000..87fa78529
--- /dev/null
+++ b/Source/Engine/Physics/PhysicsScene.Queries.cpp
@@ -0,0 +1,697 @@
+// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
+
+#include "Physics.h"
+#include "PhysicsScene.h"
+#include "Utilities.h"
+#include "CollisionData.h"
+#include "Actors/PhysicsColliderActor.h"
+#include
+#include
+
+// Temporary result buffer size
+#define HIT_BUFFER_SIZE 128
+
+template
+class DynamicHitBuffer : public PxHitCallback
+{
+private:
+
+ uint32 _count;
+ HitType _buffer[HIT_BUFFER_SIZE];
+
+public:
+
+ DynamicHitBuffer()
+ : PxHitCallback(_buffer, HIT_BUFFER_SIZE)
+ , _count(0)
+ {
+ }
+
+public:
+
+ // Computes the number of any hits in this result, blocking or touching.
+ PX_INLINE PxU32 getNbAnyHits() const
+ {
+ return getNbTouches();
+ }
+
+ // Convenience iterator used to access any hits in this result, blocking or touching.
+ PX_INLINE const HitType& getAnyHit(const PxU32 index) const
+ {
+ PX_ASSERT(index < getNbTouches() + PxU32(this->hasBlock));
+ return index < getNbTouches() ? getTouches()[index] : this->block;
+ }
+
+ PX_INLINE PxU32 getNbTouches() const
+ {
+ return _count;
+ }
+
+ PX_INLINE const HitType* getTouches() const
+ {
+ return _buffer;
+ }
+
+ PX_INLINE const HitType& getTouch(const PxU32 index) const
+ {
+ PX_ASSERT(index < getNbTouches());
+ return _buffer[index];
+ }
+
+ PX_INLINE PxU32 getMaxNbTouches() const
+ {
+ return HIT_BUFFER_SIZE;
+ }
+
+protected:
+
+ PxAgain processTouches(const HitType* buffer, PxU32 nbHits) override
+ {
+ nbHits = Math::Min(nbHits, HIT_BUFFER_SIZE - _count);
+ for (PxU32 i = 0; i < nbHits; i++)
+ {
+ _buffer[_count + i] = buffer[i];
+ }
+ _count += nbHits;
+ return true;
+ }
+
+ void finalizeQuery() override
+ {
+ if (this->hasBlock)
+ {
+ // Blocking hits go to hits
+ processTouches(&this->block, 1);
+ }
+ }
+};
+
+#define SCENE_QUERY_SETUP(blockSingle) if (GetScene() == nullptr) return false; \
+ const PxHitFlags hitFlags = PxHitFlag::ePOSITION | PxHitFlag::eNORMAL | PxHitFlag::eUV; \
+ PxQueryFilterData filterData = PxQueryFilterData(); \
+ filterData.flags |= PxQueryFlag::ePREFILTER; \
+ filterData.data.word0 = layerMask; \
+ filterData.data.word1 = blockSingle ? 1 : 0; \
+ filterData.data.word2 = hitTriggers ? 1 : 0
+
+#define SCENE_QUERY_SETUP_SWEEP_1() SCENE_QUERY_SETUP(true); \
+ PxSweepBufferN<1> buffer
+
+#define SCENE_QUERY_SETUP_SWEEP() SCENE_QUERY_SETUP(false); \
+ DynamicHitBuffer buffer
+
+#define SCENE_QUERY_SETUP_OVERLAP_1() SCENE_QUERY_SETUP(false); \
+ PxOverlapBufferN<1> buffer
+
+#define SCENE_QUERY_SETUP_OVERLAP() SCENE_QUERY_SETUP(false); \
+ DynamicHitBuffer buffer
+
+#define SCENE_QUERY_COLLECT_SINGLE() const auto& hit = buffer.getAnyHit(0); \
+ hitInfo.Gather(hit)
+
+#define SCENE_QUERY_COLLECT_ALL() results.Clear(); \
+ results.Resize(buffer.getNbAnyHits(), false); \
+ for (int32 i = 0; i < results.Count(); i++) \
+ { \
+ const auto& hit = buffer.getAnyHit(i); \
+ results[i].Gather(hit); \
+ }
+
+#define SCENE_QUERY_COLLECT_OVERLAP() results.Clear(); \
+ results.Resize(buffer.getNbTouches(), false); \
+ for (int32 i = 0; i < results.Count(); i++) \
+ { \
+ auto& hitInfo = results[i]; \
+ const auto& hit = buffer.getTouch(i); \
+ hitInfo = hit.shape ? static_cast<::PhysicsColliderActor*>(hit.shape->userData) : nullptr; \
+ }
+
+#define SCENE_QUERY_COLLECT_OVERLAP_COLLIDER() results.Clear(); \
+ results.Resize(buffer.getNbTouches(), false); \
+ for (int32 i = 0; i < results.Count(); i++) \
+ { \
+ auto& hitInfo = results[i]; \
+ const auto& hit = buffer.getTouch(i); \
+ hitInfo = hit.shape ? static_cast<::Collider*>(hit.shape->userData) : nullptr; \
+ }
+
+void RayCastHit::Gather(const PxRaycastHit& hit)
+{
+ Point = P2C(hit.position);
+ Normal = P2C(hit.normal);
+ Distance = hit.distance;
+ Collider = hit.shape ? static_cast(hit.shape->userData) : nullptr;
+ FaceIndex = hit.faceIndex;
+ UV.X = hit.u;
+ UV.Y = hit.v;
+}
+
+void RayCastHit::Gather(const PxSweepHit& hit)
+{
+ Point = P2C(hit.position);
+ Normal = P2C(hit.normal);
+ Distance = hit.distance;
+ Collider = hit.shape ? static_cast(hit.shape->userData) : nullptr;
+ FaceIndex = hit.faceIndex;
+ UV = Vector2::Zero;
+}
+
+class QueryFilter : public PxQueryFilterCallback
+{
+public:
+
+ PxQueryHitType::Enum preFilter(const PxFilterData& filterData, const PxShape* shape, const PxRigidActor* actor, PxHitFlags& queryFlags) override
+ {
+ // Early out to avoid crashing
+ if (!shape)
+ return PxQueryHitType::eNONE;
+
+ // Check mask
+ const PxFilterData shapeFilter = shape->getQueryFilterData();
+ if ((filterData.word0 & shapeFilter.word0) == 0)
+ {
+ return PxQueryHitType::eNONE;
+ }
+
+ // Check if skip triggers
+ const bool hitTriggers = filterData.word2 != 0;
+ if (!hitTriggers && shape->getFlags() & PxShapeFlag::eTRIGGER_SHAPE)
+ return PxQueryHitType::eNONE;
+
+ const bool blockSingle = filterData.word1 != 0;
+ return blockSingle ? PxQueryHitType::eBLOCK : PxQueryHitType::eTOUCH;
+ }
+
+ PxQueryHitType::Enum postFilter(const PxFilterData& filterData, const PxQueryHit& hit) override
+ {
+ // Not used
+ return PxQueryHitType::eNONE;
+ }
+};
+
+class CharacterQueryFilter : public PxQueryFilterCallback
+{
+public:
+
+ PxQueryHitType::Enum preFilter(const PxFilterData& filterData, const PxShape* shape, const PxRigidActor* actor, PxHitFlags& queryFlags) override
+ {
+ // Early out to avoid crashing
+ if (!shape)
+ return PxQueryHitType::eNONE;
+
+ // Let triggers through
+ if (PxFilterObjectIsTrigger(shape->getFlags()))
+ return PxQueryHitType::eNONE;
+
+ // Trigger the contact callback for pairs (A,B) where the filtermask of A contains the ID of B and vice versa
+ const PxFilterData shapeFilter = shape->getQueryFilterData();
+ if (filterData.word0 & shapeFilter.word1)
+ return PxQueryHitType::eBLOCK;
+
+ return PxQueryHitType::eNONE;
+ }
+
+ PxQueryHitType::Enum postFilter(const PxFilterData& filterData, const PxQueryHit& hit) override
+ {
+ // Not used
+ return PxQueryHitType::eNONE;
+ }
+};
+
+class CharacterControllerFilter : public PxControllerFilterCallback
+{
+private:
+
+ PxShape* getShape(const PxController& controller)
+ {
+ PxRigidDynamic* actor = controller.getActor();
+
+ // Early out if no actor or no shapes
+ if (!actor || actor->getNbShapes() < 1)
+ return nullptr;
+
+ // Get first shape only.
+ PxShape* shape = nullptr;
+ actor->getShapes(&shape, 1);
+
+ return shape;
+ }
+
+public:
+
+ bool filter(const PxController& a, const PxController& b) override
+ {
+ // Early out to avoid crashing
+ PxShape* shapeA = getShape(a);
+ if (!shapeA)
+ return false;
+
+ PxShape* shapeB = getShape(b);
+ if (!shapeB)
+ return false;
+
+ // Let triggers through
+ if (PxFilterObjectIsTrigger(shapeB->getFlags()))
+ return false;
+
+ // Trigger the contact callback for pairs (A,B) where the filtermask of A contains the ID of B and vice versa
+ const PxFilterData shapeFilterA = shapeA->getQueryFilterData();
+ const PxFilterData shapeFilterB = shapeB->getQueryFilterData();
+ if (shapeFilterA.word0 & shapeFilterB.word1)
+ return true;
+
+ return false;
+ }
+};
+
+PxQueryFilterCallback* PhysicsScene::GetQueryFilterCallback()
+{
+ static QueryFilter Filter;
+ return &Filter;
+}
+
+PxQueryFilterCallback* PhysicsScene::GetCharacterQueryFilterCallback()
+{
+ static CharacterQueryFilter Filter;
+ return &Filter;
+}
+
+PxControllerFilterCallback* PhysicsScene::GetCharacterControllerFilterCallback()
+{
+ static CharacterControllerFilter Filter;
+ return &Filter;
+}
+
+bool PhysicsScene::RayCast(const Vector3& origin, const Vector3& direction, const float maxDistance, uint32 layerMask, bool hitTriggers)
+{
+ // Prepare data
+ SCENE_QUERY_SETUP(true);
+ PxRaycastBuffer buffer;
+
+ // Perform raycast test
+ return GetScene()->raycast(C2P(origin), C2P(direction), maxDistance, buffer, hitFlags, filterData, GetQueryFilterCallback());
+}
+
+bool PhysicsScene::RayCast(const Vector3& origin, const Vector3& direction, RayCastHit& hitInfo, const float maxDistance, uint32 layerMask, bool hitTriggers)
+{
+ // Prepare data
+ SCENE_QUERY_SETUP(true);
+ PxRaycastBuffer buffer;
+
+ // Perform raycast test
+ if (!GetScene()->raycast(C2P(origin), C2P(direction), maxDistance, buffer, hitFlags, filterData, GetQueryFilterCallback()))
+ return false;
+
+ // Collect results
+ SCENE_QUERY_COLLECT_SINGLE();
+
+ return true;
+}
+
+bool PhysicsScene::RayCastAll(const Vector3& origin, const Vector3& direction, Array& results, const float maxDistance, uint32 layerMask, bool hitTriggers)
+{
+ // Prepare data
+ SCENE_QUERY_SETUP(false);
+ DynamicHitBuffer buffer;
+
+ // Perform raycast test
+ if (!GetScene()->raycast(C2P(origin), C2P(direction), maxDistance, buffer, hitFlags, filterData, GetQueryFilterCallback()))
+ return false;
+
+ // Collect results
+ SCENE_QUERY_COLLECT_ALL();
+
+ return true;
+}
+
+bool PhysicsScene::BoxCast(const Vector3& center, const Vector3& halfExtents, const Vector3& direction, const Quaternion& rotation, const float maxDistance, uint32 layerMask, bool hitTriggers)
+{
+ // Prepare data
+ SCENE_QUERY_SETUP_SWEEP_1();
+ const PxTransform pose(C2P(center), C2P(rotation));
+ const PxBoxGeometry geometry(C2P(halfExtents));
+
+ // Perform sweep test
+ return GetScene()->sweep(geometry, pose, C2P(direction), maxDistance, buffer, hitFlags, filterData, GetQueryFilterCallback());
+}
+
+bool PhysicsScene::BoxCast(const Vector3& center, const Vector3& halfExtents, const Vector3& direction, RayCastHit& hitInfo, const Quaternion& rotation, const float maxDistance, uint32 layerMask, bool hitTriggers)
+{
+ // Prepare data
+ SCENE_QUERY_SETUP_SWEEP_1();
+ const PxTransform pose(C2P(center), C2P(rotation));
+ const PxBoxGeometry geometry(C2P(halfExtents));
+
+ // Perform sweep test
+ if (!GetScene()->sweep(geometry, pose, C2P(direction), maxDistance, buffer, hitFlags, filterData, GetQueryFilterCallback()))
+ return false;
+
+ // Collect results
+ SCENE_QUERY_COLLECT_SINGLE();
+
+ return true;
+}
+
+bool PhysicsScene::BoxCastAll(const Vector3& center, const Vector3& halfExtents, const Vector3& direction, Array& results, const Quaternion& rotation, const float maxDistance, uint32 layerMask, bool hitTriggers)
+{
+ // Prepare data
+ SCENE_QUERY_SETUP_SWEEP();
+ const PxTransform pose(C2P(center), C2P(rotation));
+ const PxBoxGeometry geometry(C2P(halfExtents));
+
+ // Perform sweep test
+ if (!GetScene()->sweep(geometry, pose, C2P(direction), maxDistance, buffer, hitFlags, filterData, GetQueryFilterCallback()))
+ return false;
+
+ // Collect results
+ SCENE_QUERY_COLLECT_ALL();
+
+ return true;
+}
+
+bool PhysicsScene::SphereCast(const Vector3& center, const float radius, const Vector3& direction, const float maxDistance, uint32 layerMask, bool hitTriggers)
+{
+ // Prepare data
+ SCENE_QUERY_SETUP_SWEEP_1();
+ const PxTransform pose(C2P(center));
+ const PxSphereGeometry geometry(radius);
+
+ // Perform sweep test
+ return GetScene()->sweep(geometry, pose, C2P(direction), maxDistance, buffer, hitFlags, filterData, GetQueryFilterCallback());
+}
+
+bool PhysicsScene::SphereCast(const Vector3& center, const float radius, const Vector3& direction, RayCastHit& hitInfo, const float maxDistance, uint32 layerMask, bool hitTriggers)
+{
+ // Prepare data
+ SCENE_QUERY_SETUP_SWEEP_1();
+ const PxTransform pose(C2P(center));
+ const PxSphereGeometry geometry(radius);
+
+ // Perform sweep test
+ if (!GetScene()->sweep(geometry, pose, C2P(direction), maxDistance, buffer, hitFlags, filterData, GetQueryFilterCallback()))
+ return false;
+
+ // Collect results
+ SCENE_QUERY_COLLECT_SINGLE();
+
+ return true;
+}
+
+bool PhysicsScene::SphereCastAll(const Vector3& center, const float radius, const Vector3& direction, Array& results, const float maxDistance, uint32 layerMask, bool hitTriggers)
+{
+ // Prepare data
+ SCENE_QUERY_SETUP_SWEEP();
+ const PxTransform pose(C2P(center));
+ const PxSphereGeometry geometry(radius);
+
+ // Perform sweep test
+ if (!GetScene()->sweep(geometry, pose, C2P(direction), maxDistance, buffer, hitFlags, filterData, GetQueryFilterCallback()))
+ return false;
+
+ // Collect results
+ SCENE_QUERY_COLLECT_ALL();
+
+ return true;
+}
+
+bool PhysicsScene::CapsuleCast(const Vector3& center, const float radius, const float height, const Vector3& direction, const Quaternion& rotation, const float maxDistance, uint32 layerMask, bool hitTriggers)
+{
+ // Prepare data
+ SCENE_QUERY_SETUP_SWEEP_1();
+ const PxTransform pose(C2P(center), C2P(rotation));
+ const PxCapsuleGeometry geometry(radius, height * 0.5f);
+
+ // Perform sweep test
+ return GetScene()->sweep(geometry, pose, C2P(direction), maxDistance, buffer, hitFlags, filterData, GetQueryFilterCallback());
+}
+
+bool PhysicsScene::CapsuleCast(const Vector3& center, const float radius, const float height, const Vector3& direction, RayCastHit& hitInfo, const Quaternion& rotation, const float maxDistance, uint32 layerMask, bool hitTriggers)
+{
+ // Prepare data
+ SCENE_QUERY_SETUP_SWEEP_1();
+ const PxTransform pose(C2P(center), C2P(rotation));
+ const PxCapsuleGeometry geometry(radius, height * 0.5f);
+
+ // Perform sweep test
+ if (!GetScene()->sweep(geometry, pose, C2P(direction), maxDistance, buffer, hitFlags, filterData, GetQueryFilterCallback()))
+ return false;
+
+ // Collect results
+ SCENE_QUERY_COLLECT_SINGLE();
+
+ return true;
+}
+
+bool PhysicsScene::CapsuleCastAll(const Vector3& center, const float radius, const float height, const Vector3& direction, Array& results, const Quaternion& rotation, const float maxDistance, uint32 layerMask, bool hitTriggers)
+{
+ // Prepare data
+ SCENE_QUERY_SETUP_SWEEP();
+ const PxTransform pose(C2P(center), C2P(rotation));
+ const PxCapsuleGeometry geometry(radius, height * 0.5f);
+
+ // Perform sweep test
+ if (!GetScene()->sweep(geometry, pose, C2P(direction), maxDistance, buffer, hitFlags, filterData, GetQueryFilterCallback()))
+ return false;
+
+ // Collect results
+ SCENE_QUERY_COLLECT_ALL();
+
+ return true;
+}
+
+bool PhysicsScene::ConvexCast(const Vector3& center, const CollisionData* convexMesh, const Vector3& scale, const Vector3& direction, const Quaternion& rotation, const float maxDistance, uint32 layerMask, bool hitTriggers)
+{
+ CHECK_RETURN(convexMesh && convexMesh->GetOptions().Type == CollisionDataType::ConvexMesh, false)
+
+ // Prepare data
+ SCENE_QUERY_SETUP_SWEEP_1();
+ const PxTransform pose(C2P(center), C2P(rotation));
+ const PxConvexMeshGeometry geometry(convexMesh->GetConvex(), PxMeshScale(C2P(scale)));
+
+ // Perform sweep test
+ return GetScene()->sweep(geometry, pose, C2P(direction), maxDistance, buffer, hitFlags, filterData, GetQueryFilterCallback());
+}
+
+bool PhysicsScene::ConvexCast(const Vector3& center, const CollisionData* convexMesh, const Vector3& scale, const Vector3& direction, RayCastHit& hitInfo, const Quaternion& rotation, const float maxDistance, uint32 layerMask, bool hitTriggers)
+{
+ CHECK_RETURN(convexMesh && convexMesh->GetOptions().Type == CollisionDataType::ConvexMesh, false)
+
+ // Prepare data
+ SCENE_QUERY_SETUP_SWEEP_1();
+ const PxTransform pose(C2P(center), C2P(rotation));
+ const PxConvexMeshGeometry geometry(convexMesh->GetConvex(), PxMeshScale(C2P(scale)));
+
+ // Perform sweep test
+ if (!GetScene()->sweep(geometry, pose, C2P(direction), maxDistance, buffer, hitFlags, filterData, GetQueryFilterCallback()))
+ return false;
+
+ // Collect results
+ SCENE_QUERY_COLLECT_SINGLE();
+
+ return true;
+}
+
+bool PhysicsScene::ConvexCastAll(const Vector3& center, const CollisionData* convexMesh, const Vector3& scale, const Vector3& direction, Array& results, const Quaternion& rotation, const float maxDistance, uint32 layerMask, bool hitTriggers)
+{
+ CHECK_RETURN(convexMesh && convexMesh->GetOptions().Type == CollisionDataType::ConvexMesh, false)
+
+ // Prepare data
+ SCENE_QUERY_SETUP_SWEEP();
+ const PxTransform pose(C2P(center), C2P(rotation));
+ const PxConvexMeshGeometry geometry(convexMesh->GetConvex(), PxMeshScale(C2P(scale)));
+
+ // Perform sweep test
+ if (!GetScene()->sweep(geometry, pose, C2P(direction), maxDistance, buffer, hitFlags, filterData, GetQueryFilterCallback()))
+ return false;
+
+ // Collect results
+ SCENE_QUERY_COLLECT_ALL();
+
+ return true;
+}
+
+bool PhysicsScene::CheckBox(const Vector3& center, const Vector3& halfExtents, const Quaternion& rotation, uint32 layerMask, bool hitTriggers)
+{
+ // Prepare data
+ SCENE_QUERY_SETUP_OVERLAP_1();
+ const PxTransform pose(C2P(center), C2P(rotation));
+ const PxBoxGeometry geometry(C2P(halfExtents));
+
+ // Perform overlap test
+ return GetScene()->overlap(geometry, pose, buffer, filterData, GetQueryFilterCallback());
+}
+
+bool PhysicsScene::CheckSphere(const Vector3& center, const float radius, uint32 layerMask, bool hitTriggers)
+{
+ // Prepare data
+ SCENE_QUERY_SETUP_OVERLAP_1();
+ const PxTransform pose(C2P(center));
+ const PxSphereGeometry geometry(radius);
+
+ // Perform overlap test
+ return GetScene()->overlap(geometry, pose, buffer, filterData, GetQueryFilterCallback());
+}
+
+bool PhysicsScene::CheckCapsule(const Vector3& center, const float radius, const float height, const Quaternion& rotation, uint32 layerMask, bool hitTriggers)
+{
+ // Prepare data
+ SCENE_QUERY_SETUP_OVERLAP_1();
+ const PxTransform pose(C2P(center), C2P(rotation));
+ const PxCapsuleGeometry geometry(radius, height * 0.5f);
+
+ // Perform overlap test
+ return GetScene()->overlap(geometry, pose, buffer, filterData, GetQueryFilterCallback());
+}
+
+bool PhysicsScene::CheckConvex(const Vector3& center, const CollisionData* convexMesh, const Vector3& scale, const Quaternion& rotation, uint32 layerMask, bool hitTriggers)
+{
+ CHECK_RETURN(convexMesh && convexMesh->GetOptions().Type == CollisionDataType::ConvexMesh, false)
+
+ // Prepare data
+ SCENE_QUERY_SETUP_OVERLAP_1();
+ const PxTransform pose(C2P(center), C2P(rotation));
+ const PxConvexMeshGeometry geometry(convexMesh->GetConvex(), PxMeshScale(C2P(scale)));
+
+ // Perform overlap test
+ return GetScene()->overlap(geometry, pose, buffer, filterData, GetQueryFilterCallback());
+}
+
+bool PhysicsScene::OverlapBox(const Vector3& center, const Vector3& halfExtents, Array& results, const Quaternion& rotation, uint32 layerMask, bool hitTriggers)
+{
+ // Prepare data
+ SCENE_QUERY_SETUP_OVERLAP();
+ const PxTransform pose(C2P(center), C2P(rotation));
+ const PxBoxGeometry geometry(C2P(halfExtents));
+
+ // Perform overlap test
+ if (!GetScene()->overlap(geometry, pose, buffer, filterData, GetQueryFilterCallback()))
+ return false;
+
+ // Collect results
+ SCENE_QUERY_COLLECT_OVERLAP_COLLIDER();
+
+ return true;
+}
+
+bool PhysicsScene::OverlapSphere(const Vector3& center, const float radius, Array& results, uint32 layerMask, bool hitTriggers)
+{
+ // Prepare data
+ SCENE_QUERY_SETUP_OVERLAP();
+ const PxTransform pose(C2P(center));
+ const PxSphereGeometry geometry(radius);
+
+ // Perform overlap test
+ if (!GetScene()->overlap(geometry, pose, buffer, filterData, GetQueryFilterCallback()))
+ return false;
+
+ // Collect results
+ SCENE_QUERY_COLLECT_OVERLAP_COLLIDER();
+
+ return true;
+}
+
+bool PhysicsScene::OverlapCapsule(const Vector3& center, const float radius, const float height, Array& results, const Quaternion& rotation, uint32 layerMask, bool hitTriggers)
+{
+ // Prepare data
+ SCENE_QUERY_SETUP_OVERLAP();
+ const PxTransform pose(C2P(center), C2P(rotation));
+ const PxCapsuleGeometry geometry(radius, height * 0.5f);
+
+ // Perform overlap test
+ if (!GetScene()->overlap(geometry, pose, buffer, filterData, GetQueryFilterCallback()))
+ return false;
+
+ // Collect results
+ SCENE_QUERY_COLLECT_OVERLAP_COLLIDER();
+
+ return true;
+}
+
+bool PhysicsScene::OverlapConvex(const Vector3& center, const CollisionData* convexMesh, const Vector3& scale, Array& results, const Quaternion& rotation, uint32 layerMask, bool hitTriggers)
+{
+ CHECK_RETURN(convexMesh && convexMesh->GetOptions().Type == CollisionDataType::ConvexMesh, false)
+
+ // Prepare data
+ SCENE_QUERY_SETUP_OVERLAP();
+ const PxTransform pose(C2P(center), C2P(rotation));
+ const PxConvexMeshGeometry geometry(convexMesh->GetConvex(), PxMeshScale(C2P(scale)));
+
+ // Perform overlap test
+ if (!GetScene()->overlap(geometry, pose, buffer, filterData, GetQueryFilterCallback()))
+ return false;
+
+ // Collect results
+ SCENE_QUERY_COLLECT_OVERLAP_COLLIDER();
+
+ return true;
+}
+
+bool PhysicsScene::OverlapBox(const Vector3& center, const Vector3& halfExtents, Array& results, const Quaternion& rotation, uint32 layerMask, bool hitTriggers)
+{
+ // Prepare data
+ SCENE_QUERY_SETUP_OVERLAP();
+ const PxTransform pose(C2P(center), C2P(rotation));
+ const PxBoxGeometry geometry(C2P(halfExtents));
+
+ // Perform overlap test
+ if (!GetScene()->overlap(geometry, pose, buffer, filterData, GetQueryFilterCallback()))
+ return false;
+
+ // Collect results
+ SCENE_QUERY_COLLECT_OVERLAP();
+
+ return true;
+}
+
+bool PhysicsScene::OverlapSphere(const Vector3& center, const float radius, Array& results, uint32 layerMask, bool hitTriggers)
+{
+ // Prepare data
+ SCENE_QUERY_SETUP_OVERLAP();
+ const PxTransform pose(C2P(center));
+ const PxSphereGeometry geometry(radius);
+
+ // Perform overlap test
+ if (!GetScene()->overlap(geometry, pose, buffer, filterData, GetQueryFilterCallback()))
+ return false;
+
+ // Collect results
+ SCENE_QUERY_COLLECT_OVERLAP();
+
+ return true;
+}
+
+bool PhysicsScene::OverlapCapsule(const Vector3& center, const float radius, const float height, Array& results, const Quaternion& rotation, uint32 layerMask, bool hitTriggers)
+{
+ // Prepare data
+ SCENE_QUERY_SETUP_OVERLAP();
+ const PxTransform pose(C2P(center), C2P(rotation));
+ const PxCapsuleGeometry geometry(radius, height * 0.5f);
+
+ // Perform overlap test
+ if (!GetScene()->overlap(geometry, pose, buffer, filterData, GetQueryFilterCallback()))
+ return false;
+
+ // Collect results
+ SCENE_QUERY_COLLECT_OVERLAP();
+
+ return true;
+}
+
+bool PhysicsScene::OverlapConvex(const Vector3& center, const CollisionData* convexMesh, const Vector3& scale, Array& results, const Quaternion& rotation, uint32 layerMask, bool hitTriggers)
+{
+ CHECK_RETURN(convexMesh && convexMesh->GetOptions().Type == CollisionDataType::ConvexMesh, false)
+
+ // Prepare data
+ SCENE_QUERY_SETUP_OVERLAP();
+ const PxTransform pose(C2P(center), C2P(rotation));
+ const PxConvexMeshGeometry geometry(convexMesh->GetConvex(), PxMeshScale(C2P(scale)));
+
+ // Perform overlap test
+ if (!GetScene()->overlap(geometry, pose, buffer, filterData, GetQueryFilterCallback()))
+ return false;
+
+ // Collect results
+ SCENE_QUERY_COLLECT_OVERLAP();
+
+ return true;
+}
diff --git a/Source/Engine/Physics/PhysicsScene.cpp b/Source/Engine/Physics/PhysicsScene.cpp
new file mode 100644
index 000000000..7b5520c0a
--- /dev/null
+++ b/Source/Engine/Physics/PhysicsScene.cpp
@@ -0,0 +1,818 @@
+#include "Physics.h"
+#include "PhysicsScene.h"
+#include "PhysicsSettings.h"
+#include "PhysicsStepper.h"
+#include "SimulationEventCallback.h"
+#include "Utilities.h"
+
+#include "Actors/IPhysicsActor.h"
+
+#include "Engine/Core/Log.h"
+#include "Engine/Platform/CPUInfo.h"
+#include "Engine/Profiler/ProfilerCPU.h"
+#include "Engine/Threading/Threading.h"
+#include
+
+#if WITH_VEHICLE
+#include "Actors/WheeledVehicle.h"
+#include
+#endif
+
+// Temporary memory size used by the PhysX during the simulation. Must be multiply of 4kB and 16bit aligned.
+#define SCRATCH_BLOCK_SIZE (1024 * 128)
+
+PxFilterFlags FilterShader(
+ PxFilterObjectAttributes attributes0, PxFilterData filterData0,
+ PxFilterObjectAttributes attributes1, PxFilterData filterData1,
+ PxPairFlags& pairFlags, const void* constantBlock, PxU32 constantBlockSize)
+{
+ // Let triggers through
+ if (PxFilterObjectIsTrigger(attributes0) || PxFilterObjectIsTrigger(attributes1))
+ {
+ pairFlags |= PxPairFlag::eNOTIFY_TOUCH_FOUND;
+ pairFlags |= PxPairFlag::eNOTIFY_TOUCH_LOST;
+ pairFlags |= PxPairFlag::eDETECT_DISCRETE_CONTACT;
+ return PxFilterFlag::eDEFAULT;
+ }
+
+ // Send events for the kinematic actors but don't solve the contact
+ if (PxFilterObjectIsKinematic(attributes0) && PxFilterObjectIsKinematic(attributes1))
+ {
+ pairFlags |= PxPairFlag::eNOTIFY_TOUCH_FOUND;
+ pairFlags |= PxPairFlag::eNOTIFY_TOUCH_PERSISTS;
+ pairFlags |= PxPairFlag::eNOTIFY_TOUCH_LOST;
+ pairFlags |= PxPairFlag::eDETECT_DISCRETE_CONTACT;
+ return PxFilterFlag::eSUPPRESS;
+ }
+
+ // Trigger the contact callback for pairs (A,B) where the filtermask of A contains the ID of B and vice versa
+ if ((filterData0.word0 & filterData1.word1) && (filterData1.word0 & filterData0.word1))
+ {
+ pairFlags |= PxPairFlag::eSOLVE_CONTACT;
+ pairFlags |= PxPairFlag::eDETECT_DISCRETE_CONTACT;
+ pairFlags |= PxPairFlag::eNOTIFY_TOUCH_FOUND;
+ pairFlags |= PxPairFlag::eNOTIFY_TOUCH_PERSISTS;
+ pairFlags |= PxPairFlag::ePOST_SOLVER_VELOCITY;
+ pairFlags |= PxPairFlag::eNOTIFY_CONTACT_POINTS;
+ return PxFilterFlag::eDEFAULT;
+ }
+
+ // Ignore pair (no collisions nor events)
+ return PxFilterFlag::eKILL;
+}
+
+enum class ActionType
+{
+ Sleep,
+};
+
+struct ActionData
+{
+ ActionType Type;
+ PxActor* Actor;
+};
+
+#if WITH_VEHICLE
+
+static PxQueryHitType::Enum WheelRaycastPreFilter(PxFilterData filterData0, PxFilterData filterData1, const void* constantBlock, PxU32 constantBlockSize, PxHitFlags& queryFlags)
+{
+ // Hardcoded id for vehicle shapes masking
+ if (filterData0.word3 == filterData1.word3)
+ {
+ return PxQueryHitType::eNONE;
+ }
+
+ // Collide for pairs (A,B) where the filtermask of A contains the ID of B and vice versa
+ if ((filterData0.word0 & filterData1.word1) && (filterData1.word0 & filterData0.word1))
+ {
+ return PxQueryHitType::eBLOCK;
+ }
+
+ return PxQueryHitType::eNONE;
+}
+
+#endif
+
+class PhysicsScenePhysX
+{
+ friend PhysicsScene;
+
+private:
+ PxScene* Scene;
+ PxCpuDispatcher* CpuDispatcher;
+ PxControllerManager* ControllerManager;
+ PxSimulationFilterShader PhysXDefaultFilterShader = PxDefaultSimulationFilterShader;
+ SimulationEventCallback EventsCallback;
+ CriticalSection FlushLocker;
+
+ Array NewActors;
+ Array DeadActors;
+ Array DeadMaterials;
+ Array DeadColliders;
+ Array DeadJoints;
+ Array Actions;
+ Array DeadObjects;
+
+#if WITH_VEHICLE
+ Array WheelVehiclesCache;
+ Array WheelQueryResults;
+ Array WheelHitResults;
+ Array WheelVehiclesResultsPerWheel;
+ Array WheelVehiclesResultsPerVehicle;
+ PxBatchQuery* WheelRaycastBatchQuery = nullptr;
+ PxVehicleDrivableSurfaceToTireFrictionPairs* WheelTireFrictions = nullptr;
+ Array WheelVehicles;
+#endif
+};
+
+PhysicsScene::PhysicsScene(const String& name, const PhysicsSettings& settings)
+ : PersistentScriptingObject(SpawnParams(Guid::New(), TypeInitializer))
+{
+#define CHECK_INIT(value, msg) if(!value) { LOG(Error, msg); return; }
+
+ mName = name;
+ mPhysxImpl = New();
+
+ // Create scene description
+ PxSceneDesc sceneDesc(CPhysX->getTolerancesScale());
+ sceneDesc.gravity = C2P(settings.DefaultGravity);
+ sceneDesc.flags |= PxSceneFlag::eENABLE_ACTIVE_ACTORS;
+ if (!settings.DisableCCD)
+ sceneDesc.flags |= PxSceneFlag::eENABLE_CCD;
+ if (settings.EnableAdaptiveForce)
+ sceneDesc.flags |= PxSceneFlag::eADAPTIVE_FORCE;
+ sceneDesc.simulationEventCallback = &mPhysxImpl->EventsCallback;
+ sceneDesc.filterShader = FilterShader;
+ sceneDesc.bounceThresholdVelocity = settings.BounceThresholdVelocity;
+ if (sceneDesc.cpuDispatcher == nullptr)
+ {
+ mPhysxImpl->CpuDispatcher = PxDefaultCpuDispatcherCreate(Math::Clamp(Platform::GetCPUInfo().ProcessorCoreCount - 1, 1, 4));
+ CHECK_INIT(mPhysxImpl->CpuDispatcher, "PxDefaultCpuDispatcherCreate failed!");
+ sceneDesc.cpuDispatcher = mPhysxImpl->CpuDispatcher;
+ }
+ if (sceneDesc.filterShader == nullptr)
+ {
+ sceneDesc.filterShader = mPhysxImpl->PhysXDefaultFilterShader;
+ }
+
+ // Create scene
+ mPhysxImpl->Scene = CPhysX->createScene(sceneDesc);
+ CHECK_INIT(mPhysxImpl->Scene, "createScene failed!");
+#if WITH_PVD
+ auto pvdClient = PhysicsScene->getScenePvdClient();
+ if (pvdClient)
+ {
+ pvdClient->setScenePvdFlags(PxPvdSceneFlag::eTRANSMIT_CONSTRAINTS | PxPvdSceneFlag::eTRANSMIT_SCENEQUERIES | PxPvdSceneFlag::eTRANSMIT_CONTACTS);
+ }
+ else
+ {
+ LOG(Info, "Missing PVD client scene.");
+ }
+#endif
+
+ // Init characters controller
+ mPhysxImpl->ControllerManager = PxCreateControllerManager(*mPhysxImpl->Scene);
+}
+
+PhysicsScene::~PhysicsScene()
+{
+#if WITH_VEHICLE
+ RELEASE_PHYSX(mPhysxImpl->WheelRaycastBatchQuery);
+ RELEASE_PHYSX(mPhysxImpl->WheelTireFrictions);
+ mPhysxImpl->WheelQueryResults.Resize(0);
+ mPhysxImpl->WheelHitResults.Resize(0);
+ mPhysxImpl->WheelVehiclesResultsPerWheel.Resize(0);
+ mPhysxImpl->WheelVehiclesResultsPerVehicle.Resize(0);
+#endif
+
+ RELEASE_PHYSX(mPhysxImpl->ControllerManager);
+ SAFE_DELETE(mPhysxImpl->CpuDispatcher);
+ SAFE_DELETE(mStepper);
+ Allocator::Free(mScratchMemory);
+
+ mScratchMemory = nullptr;
+ mPhysxImpl->Scene->release();
+
+ SAFE_DELETE(mPhysxImpl);
+}
+
+String PhysicsScene::GetName() const
+{
+ return mName;
+}
+
+PxScene* PhysicsScene::GetScene()
+{
+ return mPhysxImpl->Scene;
+}
+
+bool PhysicsScene::GetAutoSimulation()
+{
+ return mAutoSimulation;
+}
+
+void PhysicsScene::SetAutoSimulation(bool value)
+{
+ mAutoSimulation = value;
+}
+
+void PhysicsScene::SetGravity(const Vector3& value)
+{
+ if(mPhysxImpl->Scene)
+ {
+ mPhysxImpl->Scene->setGravity(C2P(value));
+ }
+}
+
+Vector3 PhysicsScene::GetGravity()
+{
+ return mPhysxImpl->Scene ? P2C(mPhysxImpl->Scene->getGravity()) : Vector3::Zero;
+}
+
+bool PhysicsScene::GetEnableCCD()
+{
+ return mPhysxImpl->Scene ? (mPhysxImpl->Scene->getFlags() & PxSceneFlag::eENABLE_CCD) == PxSceneFlag::eENABLE_CCD : !PhysicsSettings::Get()->DisableCCD;
+}
+
+void PhysicsScene::SetEnableCCD(const bool value)
+{
+ if (mPhysxImpl->Scene)
+ mPhysxImpl->Scene->setFlag(PxSceneFlag::eENABLE_CCD, value);
+}
+
+float PhysicsScene::GetBounceThresholdVelocity()
+{
+ return mPhysxImpl->Scene ? mPhysxImpl->Scene->getBounceThresholdVelocity() : PhysicsSettings::Get()->BounceThresholdVelocity;
+}
+
+void PhysicsScene::SetBounceThresholdVelocity(const float value)
+{
+ if (mPhysxImpl->Scene)
+ mPhysxImpl->Scene->setBounceThresholdVelocity(value);
+}
+
+void PhysicsScene::Simulate(float dt)
+{
+ ASSERT(IsInMainThread() && !mIsDuringSimulation);
+ ASSERT(CPhysX);
+ const auto& settings = *PhysicsSettings::Get();
+
+ // Flush the old/new objects and the other requests before the simulation
+ FlushRequests();
+
+ // Clamp delta
+ dt = Math::Clamp(dt, 0.0f, settings.MaxDeltaTime);
+
+ // Prepare util objects
+ if (mScratchMemory == nullptr)
+ {
+ mScratchMemory = Allocator::Allocate(SCRATCH_BLOCK_SIZE, 16);
+ }
+ if (mStepper == nullptr)
+ {
+ mStepper = New();
+ }
+ if (settings.EnableSubstepping)
+ {
+ // Use substeps
+ mStepper->Setup(settings.SubstepDeltaTime, settings.MaxSubsteps);
+ }
+ else
+ {
+ // Use single step
+ mStepper->Setup(dt);
+ }
+
+ // Start simulation (may not be fired due to too small delta time)
+ mIsDuringSimulation = true;
+ if (mStepper->advance(mPhysxImpl->Scene, dt, mScratchMemory, SCRATCH_BLOCK_SIZE) == false)
+ return;
+ mPhysxImpl->EventsCallback.Clear();
+ mLastDeltaTime = dt;
+
+ // TODO: move this call after rendering done
+ mStepper->renderDone();
+}
+
+bool PhysicsScene::IsDuringSimulation()
+{
+ return mIsDuringSimulation;
+}
+
+void PhysicsScene::CollectResults()
+{
+ if (!mIsDuringSimulation)
+ return;
+ ASSERT(IsInMainThread());
+ ASSERT(CPhysX && mStepper);
+
+ {
+ PROFILE_CPU_NAMED("Physics.Fetch");
+
+ // Gather results (with waiting for the end)
+ mStepper->wait(mPhysxImpl->Scene);
+ }
+
+#if WITH_VEHICLE
+ if (mPhysxImpl->WheelVehicles.HasItems())
+ {
+ PROFILE_CPU_NAMED("Physics.Vehicles");
+
+ // Update vehicles steering
+ mPhysxImpl->WheelVehiclesCache.Clear();
+ mPhysxImpl->WheelVehiclesCache.EnsureCapacity(mPhysxImpl->WheelVehicles.Count());
+ int32 wheelsCount = 0;
+ for (auto wheelVehicle : mPhysxImpl->WheelVehicles)
+ {
+ if (!wheelVehicle->IsActiveInHierarchy())
+ continue;
+ auto drive = (PxVehicleWheels*)wheelVehicle->_drive;
+ ASSERT(drive);
+ mPhysxImpl->WheelVehiclesCache.Add(drive);
+ wheelsCount += drive->mWheelsSimData.getNbWheels();
+
+ float throttle = wheelVehicle->_throttle;
+ float brake = wheelVehicle->_brake;
+ if (wheelVehicle->UseReverseAsBrake)
+ {
+ const float invalidDirectionThreshold = 80.0f;
+ const float breakThreshold = 8.0f;
+ const float forwardSpeed = wheelVehicle->GetForwardSpeed();
+
+ // Automatic gear change when changing driving direction
+ if (Math::Abs(forwardSpeed) < invalidDirectionThreshold)
+ {
+ if (throttle < -ZeroTolerance && wheelVehicle->GetCurrentGear() >= 0 && wheelVehicle->GetTargetGear() >= 0)
+ {
+ wheelVehicle->SetCurrentGear(-1);
+ }
+ else if (throttle > ZeroTolerance && wheelVehicle->GetCurrentGear() <= 0 && wheelVehicle->GetTargetGear() <= 0)
+ {
+ wheelVehicle->SetCurrentGear(1);
+ }
+ }
+
+ // Automatic break when changing driving direction
+ if (throttle > 0.0f)
+ {
+ if (forwardSpeed < -invalidDirectionThreshold)
+ {
+ brake = 1.0f;
+ }
+ }
+ else if (throttle < 0.0f)
+ {
+ if (forwardSpeed > invalidDirectionThreshold)
+ {
+ brake = 1.0f;
+ }
+ }
+ else
+ {
+ if (forwardSpeed < breakThreshold && forwardSpeed > -breakThreshold)
+ {
+ brake = 1.0f;
+ }
+ }
+
+ // Block throttle if user is changing driving direction
+ if ((throttle > 0.0f && wheelVehicle->GetTargetGear() < 0) || (throttle < 0.0f && wheelVehicle->GetTargetGear() > 0))
+ {
+ throttle = 0.0f;
+ }
+
+ throttle = Math::Abs(throttle);
+ }
+ else
+ {
+ throttle = Math::Max(throttle, 0.0f);
+ }
+ // @formatter:off
+ // Reference: PhysX SDK docs
+ // TODO: expose input control smoothing data
+ static constexpr PxVehiclePadSmoothingData padSmoothing =
+ {
+ {
+ 6.0f, // rise rate eANALOG_INPUT_ACCEL
+ 6.0f, // rise rate eANALOG_INPUT_BRAKE
+ 12.0f, // rise rate eANALOG_INPUT_HANDBRAKE
+ 2.5f, // rise rate eANALOG_INPUT_STEER_LEFT
+ 2.5f, // rise rate eANALOG_INPUT_STEER_RIGHT
+ },
+ {
+ 10.0f, // fall rate eANALOG_INPUT_ACCEL
+ 10.0f, // fall rate eANALOG_INPUT_BRAKE
+ 12.0f, // fall rate eANALOG_INPUT_HANDBRAKE
+ 5.0f, // fall rate eANALOG_INPUT_STEER_LEFT
+ 5.0f, // fall rate eANALOG_INPUT_STEER_RIGHT
+ }
+ };
+ PxVehicleKeySmoothingData keySmoothing =
+ {
+ {
+ 3.0f, // rise rate eANALOG_INPUT_ACCEL
+ 3.0f, // rise rate eANALOG_INPUT_BRAKE
+ 10.0f, // rise rate eANALOG_INPUT_HANDBRAKE
+ 2.5f, // rise rate eANALOG_INPUT_STEER_LEFT
+ 2.5f, // rise rate eANALOG_INPUT_STEER_RIGHT
+ },
+ {
+ 5.0f, // fall rate eANALOG_INPUT__ACCEL
+ 5.0f, // fall rate eANALOG_INPUT__BRAKE
+ 10.0f, // fall rate eANALOG_INPUT__HANDBRAKE
+ 5.0f, // fall rate eANALOG_INPUT_STEER_LEFT
+ 5.0f // fall rate eANALOG_INPUT_STEER_RIGHT
+ }
+ };
+ // Reference: PhysX SDK docs
+ // TODO: expose steer vs forward curve into per-vehicle (up to 8 points, values clamped into 0/1 range)
+ static constexpr PxF32 steerVsForwardSpeedData[] =
+ {
+ 0.0f, 1.0f,
+ 20.0f, 0.9f,
+ 65.0f, 0.8f,
+ 120.0f, 0.7f,
+ PX_MAX_F32, PX_MAX_F32,
+ PX_MAX_F32, PX_MAX_F32,
+ PX_MAX_F32, PX_MAX_F32,
+ PX_MAX_F32, PX_MAX_F32,
+ };
+ const PxFixedSizeLookupTable<8> steerVsForwardSpeed(steerVsForwardSpeedData, 4);
+ // @formatter:on
+ if (wheelVehicle->UseAnalogSteering)
+ {
+ switch (wheelVehicle->_driveTypeCurrent)
+ {
+ case WheeledVehicle::DriveTypes::Drive4W:
+ {
+ PxVehicleDrive4WRawInputData rawInputData;
+ rawInputData.setAnalogAccel(throttle);
+ rawInputData.setAnalogBrake(brake);
+ rawInputData.setAnalogSteer(wheelVehicle->_steering);
+ rawInputData.setAnalogHandbrake(wheelVehicle->_handBrake);
+ PxVehicleDrive4WSmoothAnalogRawInputsAndSetAnalogInputs(padSmoothing, steerVsForwardSpeed, rawInputData, mLastDeltaTime, false, *(PxVehicleDrive4W*)drive);
+ break;
+ }
+ case WheeledVehicle::DriveTypes::DriveNW:
+ {
+ PxVehicleDriveNWRawInputData rawInputData;
+ rawInputData.setAnalogAccel(throttle);
+ rawInputData.setAnalogBrake(brake);
+ rawInputData.setAnalogSteer(wheelVehicle->_steering);
+ rawInputData.setAnalogHandbrake(wheelVehicle->_handBrake);
+ PxVehicleDriveNWSmoothAnalogRawInputsAndSetAnalogInputs(padSmoothing, steerVsForwardSpeed, rawInputData, mLastDeltaTime, false, *(PxVehicleDriveNW*)drive);
+ break;
+ }
+ }
+ }
+ else
+ {
+ const float deadZone = 0.1f;
+ switch (wheelVehicle->_driveTypeCurrent)
+ {
+ case WheeledVehicle::DriveTypes::Drive4W:
+ {
+ PxVehicleDrive4WRawInputData rawInputData;
+ rawInputData.setDigitalAccel(throttle > deadZone);
+ rawInputData.setDigitalBrake(brake > deadZone);
+ rawInputData.setDigitalSteerLeft(wheelVehicle->_steering < -deadZone);
+ rawInputData.setDigitalSteerRight(wheelVehicle->_steering > deadZone);
+ rawInputData.setDigitalHandbrake(wheelVehicle->_handBrake > deadZone);
+ PxVehicleDrive4WSmoothDigitalRawInputsAndSetAnalogInputs(keySmoothing, steerVsForwardSpeed, rawInputData, mLastDeltaTime, false, *(PxVehicleDrive4W*)drive);
+ break;
+ }
+ case WheeledVehicle::DriveTypes::DriveNW:
+ {
+ PxVehicleDriveNWRawInputData rawInputData;
+ rawInputData.setDigitalAccel(throttle > deadZone);
+ rawInputData.setDigitalBrake(brake > deadZone);
+ rawInputData.setDigitalSteerLeft(wheelVehicle->_steering < -deadZone);
+ rawInputData.setDigitalSteerRight(wheelVehicle->_steering > deadZone);
+ rawInputData.setDigitalHandbrake(wheelVehicle->_handBrake > deadZone);
+ PxVehicleDriveNWSmoothDigitalRawInputsAndSetAnalogInputs(keySmoothing, steerVsForwardSpeed, rawInputData, mLastDeltaTime, false, *(PxVehicleDriveNW*)drive);
+ break;
+ }
+ }
+ }
+ }
+
+ // Update batches queries cache
+ if (wheelsCount > mPhysxImpl->WheelQueryResults.Count())
+ {
+ if (mPhysxImpl->WheelRaycastBatchQuery)
+ mPhysxImpl->WheelRaycastBatchQuery->release();
+ mPhysxImpl->WheelQueryResults.Resize(wheelsCount, false);
+ mPhysxImpl->WheelHitResults.Resize(wheelsCount, false);
+ PxBatchQueryDesc desc(wheelsCount, 0, 0);
+ desc.queryMemory.userRaycastResultBuffer = mPhysxImpl->WheelQueryResults.Get();
+ desc.queryMemory.userRaycastTouchBuffer = mPhysxImpl->WheelHitResults.Get();
+ desc.queryMemory.raycastTouchBufferSize = wheelsCount;
+ desc.preFilterShader = WheelRaycastPreFilter;
+ mPhysxImpl->WheelRaycastBatchQuery = mPhysxImpl->Scene->createBatchQuery(desc);
+ }
+
+ // TODO: expose vehicle tires configuration
+ if (!mPhysxImpl->WheelTireFrictions)
+ {
+ PxVehicleDrivableSurfaceType surfaceTypes[1];
+ surfaceTypes[0].mType = 0;
+ const PxMaterial* surfaceMaterials[1];
+ surfaceMaterials[0] = Physics::GetDefaultMaterial();
+ mPhysxImpl->WheelTireFrictions = PxVehicleDrivableSurfaceToTireFrictionPairs::allocate(1, 1);
+ mPhysxImpl->WheelTireFrictions->setup(1, 1, surfaceMaterials, surfaceTypes);
+ mPhysxImpl->WheelTireFrictions->setTypePairFriction(0, 0, 5.0f);
+ }
+
+ // Setup cache for wheel states
+ mPhysxImpl->WheelVehiclesResultsPerVehicle.Resize(mPhysxImpl->WheelVehiclesCache.Count(), false);
+ mPhysxImpl->WheelVehiclesResultsPerWheel.Resize(wheelsCount, false);
+ wheelsCount = 0;
+ for (int32 i = 0, ii = 0; i < mPhysxImpl->WheelVehicles.Count(); i++)
+ {
+ auto wheelVehicle = mPhysxImpl->WheelVehicles[i];
+ if (!wheelVehicle->IsActiveInHierarchy())
+ continue;
+ auto drive = (PxVehicleWheels*)mPhysxImpl->WheelVehicles[ii]->_drive;
+ auto& perVehicle = mPhysxImpl->WheelVehiclesResultsPerVehicle[ii];
+ ii++;
+ perVehicle.nbWheelQueryResults = drive->mWheelsSimData.getNbWheels();
+ perVehicle.wheelQueryResults = mPhysxImpl->WheelVehiclesResultsPerWheel.Get() + wheelsCount;
+ wheelsCount += perVehicle.nbWheelQueryResults;
+ }
+
+ // Update vehicles
+ if (mPhysxImpl->WheelVehiclesCache.Count() != 0)
+ {
+ PxVehicleSuspensionRaycasts(mPhysxImpl->WheelRaycastBatchQuery, mPhysxImpl->WheelVehiclesCache.Count(), mPhysxImpl->WheelVehiclesCache.Get(), mPhysxImpl->WheelQueryResults.Count(), mPhysxImpl->WheelQueryResults.Get());
+ PxVehicleUpdates(mLastDeltaTime, mPhysxImpl->Scene->getGravity(), *mPhysxImpl->WheelTireFrictions, mPhysxImpl->WheelVehiclesCache.Count(), mPhysxImpl->WheelVehiclesCache.Get(), mPhysxImpl->WheelVehiclesResultsPerVehicle.Get());
+ }
+
+ // Synchronize state
+ for (int32 i = 0, ii = 0; i < mPhysxImpl->WheelVehicles.Count(); i++)
+ {
+ auto wheelVehicle = mPhysxImpl->WheelVehicles[i];
+ if (!wheelVehicle->IsActiveInHierarchy())
+ continue;
+ auto drive = mPhysxImpl->WheelVehiclesCache[ii];
+ auto& perVehicle = mPhysxImpl->WheelVehiclesResultsPerVehicle[ii];
+ ii++;
+#if PHYSX_VEHICLE_DEBUG_TELEMETRY
+ LOG(Info, "Vehicle[{}] Gear={}, RPM={}", ii, wheelVehicle->GetCurrentGear(), (int32)wheelVehicle->GetEngineRotationSpeed());
+#endif
+
+ // Update wheels
+ for (int32 j = 0; j < wheelVehicle->_wheelsData.Count(); j++)
+ {
+ auto& wheelData = wheelVehicle->_wheelsData[j];
+ auto& perWheel = perVehicle.wheelQueryResults[j];
+#if PHYSX_VEHICLE_DEBUG_TELEMETRY
+ LOG(Info, "Vehicle[{}] Wheel[{}] longitudinalSlip={}, lateralSlip={}, suspSpringForce={}", ii, j, Utilities::RoundTo2DecimalPlaces(perWheel.longitudinalSlip), Utilities::RoundTo2DecimalPlaces(perWheel.lateralSlip), (int32)perWheel.suspSpringForce);
+#endif
+
+ auto& state = wheelData.State;
+ state.IsInAir = perWheel.isInAir;
+ state.TireContactCollider = perWheel.tireContactShape ? static_cast(perWheel.tireContactShape->userData) : nullptr;
+ state.TireContactPoint = P2C(perWheel.tireContactPoint);
+ state.TireContactNormal = P2C(perWheel.tireContactNormal);
+ state.TireFriction = perWheel.tireFriction;
+ state.SteerAngle = RadiansToDegrees * perWheel.steerAngle;
+ state.RotationAngle = -RadiansToDegrees * drive->mWheelsDynData.getWheelRotationAngle(j);
+ state.SuspensionOffset = perWheel.suspJounce;
+#if USE_EDITOR
+ state.SuspensionTraceStart = P2C(perWheel.suspLineStart);
+ state.SuspensionTraceEnd = P2C(perWheel.suspLineStart + perWheel.suspLineDir * perWheel.suspLineLength);
+#endif
+
+ if (!wheelData.Collider)
+ continue;
+ auto shape = wheelData.Collider->GetPxShape();
+
+ // Update wheel collider transformation
+ auto localPose = shape->getLocalPose();
+ Transform t = wheelData.Collider->GetLocalTransform();
+ t.Orientation = Quaternion::Euler(0, state.SteerAngle, state.RotationAngle) * wheelData.LocalOrientation;
+ t.Translation = P2C(localPose.p) / wheelVehicle->GetScale() - t.Orientation * wheelData.Collider->GetCenter();
+ wheelData.Collider->SetLocalTransform(t);
+ }
+ }
+ }
+#endif
+
+ {
+ PROFILE_CPU_NAMED("Physics.FlushActiveTransforms");
+
+ // Gather change info
+ PxU32 activeActorsCount;
+ PxActor** activeActors = mPhysxImpl->Scene->getActiveActors(activeActorsCount);
+ if (activeActorsCount > 0)
+ {
+ // Update changed transformations
+ // TODO: use jobs system if amount if huge
+ for (uint32 i = 0; i < activeActorsCount; i++)
+ {
+ const auto pxActor = (PxRigidActor*)*activeActors++;
+ auto actor = dynamic_cast((Actor*)pxActor->userData);
+ ASSERT(actor);
+ actor->OnActiveTransformChanged(pxActor->getGlobalPose());
+ }
+ }
+ }
+
+ {
+ PROFILE_CPU_NAMED("Physics.SendEvents");
+
+ mPhysxImpl->EventsCallback.CollectResults();
+ mPhysxImpl->EventsCallback.SendTriggerEvents();
+ mPhysxImpl->EventsCallback.SendCollisionEvents();
+ mPhysxImpl->EventsCallback.SendJointEvents();
+ }
+
+ // End
+ mIsDuringSimulation = false;
+}
+
+void PhysicsScene::FlushRequests()
+{
+ ASSERT(!IsDuringSimulation());
+ ASSERT(CPhysX);
+
+ PROFILE_CPU();
+
+ mPhysxImpl->FlushLocker.Lock();
+
+ // Note: this does not handle case when actor is removed and added to the scene at the same time
+
+ if (mPhysxImpl->NewActors.HasItems())
+ {
+ GetScene()->addActors(mPhysxImpl->NewActors.Get(), mPhysxImpl->NewActors.Count());
+ mPhysxImpl->NewActors.Clear();
+ }
+
+ for (int32 i = 0; i < mPhysxImpl->Actions.Count(); i++)
+ {
+ const auto action = mPhysxImpl->Actions[i];
+ switch (action.Type)
+ {
+ case ActionType::Sleep:
+ static_cast(action.Actor)->putToSleep();
+ break;
+ }
+ }
+ mPhysxImpl->Actions.Clear();
+
+ if (mPhysxImpl->DeadActors.HasItems())
+ {
+ GetScene()->removeActors(mPhysxImpl->DeadActors.Get(), mPhysxImpl->DeadActors.Count(), true);
+ for (int32 i = 0; i < mPhysxImpl->DeadActors.Count(); i++)
+ {
+ mPhysxImpl->DeadActors[i]->release();
+ }
+ mPhysxImpl->DeadActors.Clear();
+ }
+
+ if (mPhysxImpl->DeadColliders.HasItems())
+ {
+ for (int32 i = 0; i < mPhysxImpl->DeadColliders.Count(); i++)
+ {
+ mPhysxImpl->EventsCallback.OnColliderRemoved(mPhysxImpl->DeadColliders[i]);
+ }
+ mPhysxImpl->DeadColliders.Clear();
+ }
+
+ if (mPhysxImpl->DeadJoints.HasItems())
+ {
+ for (int32 i = 0; i < mPhysxImpl->DeadJoints.Count(); i++)
+ {
+ mPhysxImpl->EventsCallback.OnJointRemoved(mPhysxImpl->DeadJoints[i]);
+ }
+ mPhysxImpl->DeadJoints.Clear();
+ }
+
+ for (int32 i = 0; i < mPhysxImpl->DeadMaterials.Count(); i++)
+ {
+ auto material = mPhysxImpl->DeadMaterials[i];
+
+ // Unlink ref to flax object
+ material->userData = nullptr;
+
+ material->release();
+ }
+ mPhysxImpl->DeadMaterials.Clear();
+
+ for (int32 i = 0; i < mPhysxImpl->DeadObjects.Count(); i++)
+ {
+ mPhysxImpl->DeadObjects[i]->release();
+ }
+ mPhysxImpl->DeadObjects.Clear();
+
+ mPhysxImpl->FlushLocker.Unlock();
+}
+
+void PhysicsScene::RemoveMaterial(PxMaterial* material)
+{
+ ASSERT(material);
+
+ mPhysxImpl->FlushLocker.Lock();
+ mPhysxImpl->DeadMaterials.Add(material);
+ mPhysxImpl->FlushLocker.Unlock();
+}
+
+void PhysicsScene::RemoveObject(PxBase* obj)
+{
+ ASSERT(obj);
+
+ mPhysxImpl->FlushLocker.Lock();
+ mPhysxImpl->DeadObjects.Add(obj);
+ mPhysxImpl->FlushLocker.Unlock();
+}
+
+void PhysicsScene::AddActor(PxActor* actor)
+{
+ ASSERT(actor);
+
+ mPhysxImpl->FlushLocker.Lock();
+ if (IsInMainThread())
+ {
+ GetScene()->addActor(*actor);
+ }
+ else
+ {
+ mPhysxImpl->NewActors.Add(actor);
+ }
+ mPhysxImpl->FlushLocker.Unlock();
+}
+
+void PhysicsScene::AddActor(PxRigidDynamic* actor, bool putToSleep)
+{
+ ASSERT(actor);
+
+ mPhysxImpl->FlushLocker.Lock();
+ if (IsInMainThread())
+ {
+ GetScene()->addActor(*actor);
+ if (putToSleep)
+ actor->putToSleep();
+ }
+ else
+ {
+ mPhysxImpl->NewActors.Add(actor);
+ if (putToSleep)
+ mPhysxImpl->Actions.Add({ ActionType::Sleep, actor });
+ }
+ mPhysxImpl->FlushLocker.Unlock();
+}
+
+void PhysicsScene::UnlinkActor(PxActor* actor)
+{
+ ASSERT(IsInMainThread())
+ ASSERT(actor);
+
+ GetScene()->removeActor(*actor);
+}
+
+void PhysicsScene::RemoveActor(PxActor* actor)
+{
+ ASSERT(actor);
+
+ // Unlink ref to flax object
+ actor->userData = nullptr;
+
+ mPhysxImpl->FlushLocker.Lock();
+ mPhysxImpl->DeadActors.Add(actor);
+ mPhysxImpl->FlushLocker.Unlock();
+}
+
+void PhysicsScene::RemoveCollider(PhysicsColliderActor* collider)
+{
+ ASSERT(collider);
+
+ mPhysxImpl->FlushLocker.Lock();
+ mPhysxImpl->DeadColliders.Add(collider);
+ mPhysxImpl->FlushLocker.Unlock();
+}
+
+void PhysicsScene::RemoveJoint(Joint* joint)
+{
+ ASSERT(joint);
+
+ mPhysxImpl->FlushLocker.Lock();
+ mPhysxImpl->DeadJoints.Add(joint);
+ mPhysxImpl->FlushLocker.Unlock();
+}
+
+PxControllerManager* PhysicsScene::GetControllerManager()
+{
+ return mPhysxImpl->ControllerManager;
+}
+
+#if WITH_VEHICLE
+void PhysicsScene::AddWheeledVehicle(WheeledVehicle* vehicle)
+{
+ mPhysxImpl->WheelVehicles.Add(vehicle);
+}
+
+void PhysicsScene::RemoveWheeledVehicle(WheeledVehicle* vehicle)
+{
+ mPhysxImpl->WheelVehicles.Remove(vehicle);
+}
+#endif
diff --git a/Source/Engine/Physics/PhysicsScene.h b/Source/Engine/Physics/PhysicsScene.h
new file mode 100644
index 000000000..1c95e7ece
--- /dev/null
+++ b/Source/Engine/Physics/PhysicsScene.h
@@ -0,0 +1,533 @@
+#pragma once
+
+#include "Engine/Scripting/ScriptingObject.h"
+#include "Engine/Scripting/ScriptingType.h"
+#include "Engine/Core/Math/Vector3.h"
+#include "Engine/Core/Math/Quaternion.h"
+#include "Types.h"
+
+#if WITH_VEHICLE
+class WheeledVehicle;
+#endif
+
+struct ActionData;
+struct RayCastHit;
+class FixedStepper;
+class PhysicsSettings;
+class PhysicsColliderActor;
+class PhysicsScenePhysX;
+class Joint;
+class Collider;
+class CollisionData;
+
+///
+/// Isolated physics scene.
+///
+API_CLASS(NoSpawn) class FLAXENGINE_API PhysicsScene : public PersistentScriptingObject
+{
+ DECLARE_SCRIPTING_TYPE_NO_SPAWN(PhysicsScene);
+
+ explicit PhysicsScene(const String& name, const PhysicsSettings& settings);
+ ~PhysicsScene();
+
+ ///
+ /// Gets the name of the scene.
+ ///
+ API_PROPERTY() String GetName() const;
+
+public:
+ String ToString() const override
+ {
+ return GetName();
+ }
+
+ PxScene* GetScene();
+
+ ///
+ /// The automatic simulation feature. True if perform physics simulation after on fixed update by auto, otherwise user should do it.
+ ///
+ API_PROPERTY() bool GetAutoSimulation();
+ API_PROPERTY() void SetAutoSimulation(bool value);
+
+ ///
+ /// Sets the current gravity force.
+ ///
+ API_PROPERTY() void SetGravity(const Vector3& value);
+
+ ///
+ /// Gets the current gravity force.
+ ///
+ API_PROPERTY() Vector3 GetGravity();
+
+ ///
+ /// Gets the CCD feature enable flag.
+ ///
+ API_PROPERTY() bool GetEnableCCD();
+
+ ///
+ /// Sets the CCD feature enable flag.
+ ///
+ API_PROPERTY() void SetEnableCCD(const bool value);
+
+ ///
+ /// Gets the minimum relative velocity required for an object to bounce.
+ ///
+ API_PROPERTY() float GetBounceThresholdVelocity();
+
+ ///
+ /// Sets the minimum relative velocity required for an object to bounce.
+ ///
+ API_PROPERTY() void SetBounceThresholdVelocity(const float value);
+
+ ///
+ /// Called during main engine loop to start physic simulation. Use CollectResults after.
+ ///
+ /// The delta time (in seconds).
+ API_FUNCTION() void Simulate(float dt);
+
+ ///
+ /// Checks if physical simulation is running
+ ///
+ /// True if simulation is active, otherwise false
+ API_PROPERTY() bool IsDuringSimulation();
+
+ ///
+ /// Called to collect physic simulation results and apply them as well as fire collision events.
+ ///
+ API_FUNCTION() void CollectResults();
+
+ ///
+ /// Flushes the async requests to add/remove actors, remove materials, etc..
+ ///
+ void FlushRequests();
+
+ ///
+ /// Removes the material (using safe async request).
+ ///
+ /// The material.
+ void RemoveMaterial(PxMaterial* material);
+
+ ///
+ /// Removes the physX object via calling release() on it (using safe async request).
+ ///
+ /// The obj.
+ void RemoveObject(PxBase* obj);
+
+ ///
+ /// Adds the actor (using safe async request).
+ ///
+ /// The actor.
+ void AddActor(PxActor* actor);
+
+ ///
+ /// Adds the actor (using safe async request).
+ ///
+ /// The actor.
+ /// If set to true will put actor to sleep after spawning.
+ void AddActor(PxRigidDynamic* actor, bool putToSleep = false);
+
+ ///
+ /// Removes the actor (using safe async request).
+ ///
+ /// The actor.
+ void RemoveActor(PxActor* actor);
+
+ ///
+ /// Removes the actor from the underlying physics scene without destroying it.
+ ///
+ void UnlinkActor(PxActor* actor);
+
+ ///
+ /// Marks that collider has been removed (all collision events should be cleared to prevent leaks of using removed object).
+ ///
+ /// The collider.
+ void RemoveCollider(PhysicsColliderActor* collider);
+
+ ///
+ /// Marks that joint has been removed (all collision events should be cleared to prevent leaks of using removed object).
+ ///
+ /// The joint.
+ void RemoveJoint(Joint* joint);
+
+ ///
+ /// Gets PhysX characters controller manager object
+ ///
+ PxControllerManager* GetControllerManager();
+
+public:
+ ///
+ /// Gets the default query filter callback used for the scene queries.
+ ///
+ PxQueryFilterCallback* GetQueryFilterCallback();
+
+ ///
+ /// Gets the default query filter callback used for the character controller collisions detection.
+ ///
+ PxQueryFilterCallback* GetCharacterQueryFilterCallback();
+
+ ///
+ /// Gets the default controller filter callback used for the character controller collisions detection.
+ ///
+ static PxControllerFilterCallback* GetCharacterControllerFilterCallback();
+
+ ///
+ /// Performs a raycast against objects in the scene.
+ ///
+ /// The origin of the ray.
+ /// The normalized direction of the ray.
+ /// The maximum distance the ray should check for collisions.
+ /// The layer mask used to filter the results.
+ /// If set to true triggers will be hit, otherwise will skip them.
+ /// True if ray hits an matching object, otherwise false.
+ API_FUNCTION() bool RayCast(const Vector3& origin, const Vector3& direction, float maxDistance = MAX_float, uint32 layerMask = MAX_uint32, bool hitTriggers = true);
+
+ ///
+ /// Performs a raycast against objects in the scene, returns results in a RayCastHit structure.
+ ///
+ /// The origin of the ray.
+ /// The normalized direction of the ray.
+ /// The result hit information. Valid only when method returns true.
+ /// The maximum distance the ray should check for collisions.
+ /// The layer mask used to filter the results.
+ /// If set to true triggers will be hit, otherwise will skip them.
+ /// True if ray hits an matching object, otherwise false.
+ API_FUNCTION() bool RayCast(const Vector3& origin, const Vector3& direction, API_PARAM(Out) RayCastHit& hitInfo, float maxDistance = MAX_float, uint32 layerMask = MAX_uint32, bool hitTriggers = true);
+
+ ///
+ /// Performs a raycast against objects in the scene, returns results in a RayCastHit structure.
+ ///
+ /// The origin of the ray.
+ /// The normalized direction of the ray.
+ /// The result hits. Valid only when method returns true.
+ /// The maximum distance the ray should check for collisions.
+ /// The layer mask used to filter the results.
+ /// If set to true triggers will be hit, otherwise will skip them.
+ /// True if ray hits an matching object, otherwise false.
+ API_FUNCTION() bool RayCastAll(const Vector3& origin, const Vector3& direction, API_PARAM(Out) Array& results, float maxDistance = MAX_float, uint32 layerMask = MAX_uint32, bool hitTriggers = true);
+
+ ///
+ /// Performs a sweep test against objects in the scene using a box geometry.
+ ///
+ /// The box center.
+ /// The half size of the box in each direction.
+ /// The normalized direction in which cast a box.
+ /// The box rotation.
+ /// The maximum distance the ray should check for collisions.
+ /// The layer mask used to filter the results.
+ /// If set to true triggers will be hit, otherwise will skip them.
+ /// True if box hits an matching object, otherwise false.
+ API_FUNCTION() bool BoxCast(const Vector3& center, const Vector3& halfExtents, const Vector3& direction, const Quaternion& rotation = Quaternion::Identity, float maxDistance = MAX_float, uint32 layerMask = MAX_uint32, bool hitTriggers = true);
+
+ ///
+ /// Performs a sweep test against objects in the scene using a box geometry.
+ ///
+ /// The box center.
+ /// The half size of the box in each direction.
+ /// The normalized direction in which cast a box.
+ /// The result hit information. Valid only when method returns true.
+ /// The box rotation.
+ /// The maximum distance the ray should check for collisions.
+ /// The layer mask used to filter the results.
+ /// If set to true triggers will be hit, otherwise will skip them.
+ /// True if box hits an matching object, otherwise false.
+ API_FUNCTION() bool BoxCast(const Vector3& center, const Vector3& halfExtents, const Vector3& direction, API_PARAM(Out) RayCastHit& hitInfo, const Quaternion& rotation = Quaternion::Identity, float maxDistance = MAX_float, uint32 layerMask = MAX_uint32, bool hitTriggers = true);
+
+ ///
+ /// Performs a sweep test against objects in the scene using a box geometry.
+ ///
+ /// The box center.
+ /// The half size of the box in each direction.
+ /// The normalized direction in which cast a box.
+ /// The result hits. Valid only when method returns true.
+ /// The box rotation.
+ /// The maximum distance the ray should check for collisions.
+ /// The layer mask used to filter the results.
+ /// If set to true triggers will be hit, otherwise will skip them.
+ /// True if box hits an matching object, otherwise false.
+ API_FUNCTION() bool BoxCastAll(const Vector3& center, const Vector3& halfExtents, const Vector3& direction, API_PARAM(Out) Array& results, const Quaternion& rotation = Quaternion::Identity, float maxDistance = MAX_float, uint32 layerMask = MAX_uint32, bool hitTriggers = true);
+
+ ///
+ /// Performs a sweep test against objects in the scene using a sphere geometry.
+ ///
+ /// The sphere center.
+ /// The radius of the sphere.
+ /// The normalized direction in which cast a sphere.
+ /// The maximum distance the ray should check for collisions.
+ /// The layer mask used to filter the results.
+ /// If set to true triggers will be hit, otherwise will skip them.
+ /// True if sphere hits an matching object, otherwise false.
+ API_FUNCTION() bool SphereCast(const Vector3& center, float radius, const Vector3& direction, float maxDistance = MAX_float, uint32 layerMask = MAX_uint32, bool hitTriggers = true);
+
+ ///
+ /// Performs a sweep test against objects in the scene using a sphere geometry.
+ ///
+ /// The sphere center.
+ /// The radius of the sphere.
+ /// The normalized direction in which cast a sphere.
+ /// The result hit information. Valid only when method returns true.
+ /// The maximum distance the ray should check for collisions.
+ /// The layer mask used to filter the results.
+ /// If set to true triggers will be hit, otherwise will skip them.
+ /// True if sphere hits an matching object, otherwise false.
+ API_FUNCTION() bool SphereCast(const Vector3& center, float radius, const Vector3& direction, API_PARAM(Out) RayCastHit& hitInfo, float maxDistance = MAX_float, uint32 layerMask = MAX_uint32, bool hitTriggers = true);
+
+ ///
+ /// Performs a sweep test against objects in the scene using a sphere geometry.
+ ///
+ /// The sphere center.
+ /// The radius of the sphere.
+ /// The normalized direction in which cast a sphere.
+ /// The result hits. Valid only when method returns true.
+ /// The maximum distance the ray should check for collisions.
+ /// The layer mask used to filter the results.
+ /// If set to true triggers will be hit, otherwise will skip them.
+ /// True if sphere hits an matching object, otherwise false.
+ API_FUNCTION() bool SphereCastAll(const Vector3& center, float radius, const Vector3& direction, API_PARAM(Out) Array& results, float maxDistance = MAX_float, uint32 layerMask = MAX_uint32, bool hitTriggers = true);
+
+ ///
+ /// Performs a sweep test against objects in the scene using a capsule geometry.
+ ///
+ /// The capsule center.
+ /// The radius of the capsule.
+ /// The height of the capsule, excluding the top and bottom spheres.
+ /// The normalized direction in which cast a capsule.
+ /// The capsule rotation.
+ /// The maximum distance the ray should check for collisions.
+ /// The layer mask used to filter the results.
+ /// If set to true triggers will be hit, otherwise will skip them.
+ /// True if capsule hits an matching object, otherwise false.
+ API_FUNCTION() bool CapsuleCast(const Vector3& center, float radius, float height, const Vector3& direction, const Quaternion& rotation = Quaternion::Identity, float maxDistance = MAX_float, uint32 layerMask = MAX_uint32, bool hitTriggers = true);
+
+ ///
+ /// Performs a sweep test against objects in the scene using a capsule geometry.
+ ///
+ /// The capsule center.
+ /// The radius of the capsule.
+ /// The height of the capsule, excluding the top and bottom spheres.
+ /// The normalized direction in which cast a capsule.
+ /// The result hit information. Valid only when method returns true.
+ /// The capsule rotation.
+ /// The maximum distance the ray should check for collisions.
+ /// The layer mask used to filter the results.
+ /// If set to true triggers will be hit, otherwise will skip them.
+ /// True if capsule hits an matching object, otherwise false.
+ API_FUNCTION() bool CapsuleCast(const Vector3& center, float radius, float height, const Vector3& direction, API_PARAM(Out) RayCastHit& hitInfo, const Quaternion& rotation = Quaternion::Identity, float maxDistance = MAX_float, uint32 layerMask = MAX_uint32, bool hitTriggers = true);
+
+ ///
+ /// Performs a sweep test against objects in the scene using a capsule geometry.
+ ///
+ /// The capsule center.
+ /// The radius of the capsule.
+ /// The height of the capsule, excluding the top and bottom spheres.
+ /// The normalized direction in which cast a capsule.
+ /// The result hits. Valid only when method returns true.
+ /// The capsule rotation.
+ /// The maximum distance the ray should check for collisions.
+ /// The layer mask used to filter the results.
+ /// If set to true triggers will be hit, otherwise will skip them.
+ /// True if capsule hits an matching object, otherwise false.
+ API_FUNCTION() bool CapsuleCastAll(const Vector3& center, float radius, float height, const Vector3& direction, API_PARAM(Out) Array& results, const Quaternion& rotation = Quaternion::Identity, float maxDistance = MAX_float, uint32 layerMask = MAX_uint32, bool hitTriggers = true);
+
+ ///
+ /// Performs a sweep test against objects in the scene using a convex mesh.
+ ///
+ /// The convex mesh center.
+ /// Collision data of the convex mesh.
+ /// The scale of the convex mesh.
+ /// The normalized direction in which cast a convex mesh.
+ /// The convex mesh rotation.
+ /// The maximum distance the ray should check for collisions.
+ /// The layer mask used to filter the results.
+ /// If set to true triggers will be hit, otherwise will skip them.
+ /// True if convex mesh hits an matching object, otherwise false.
+ API_FUNCTION() bool ConvexCast(const Vector3& center, const CollisionData* convexMesh, const Vector3& scale, const Vector3& direction, const Quaternion& rotation = Quaternion::Identity, float maxDistance = MAX_float, uint32 layerMask = MAX_uint32, bool hitTriggers = true);
+
+ ///
+ /// Performs a sweep test against objects in the scene using a convex mesh.
+ ///
+ /// The convex mesh center.
+ /// Collision data of the convex mesh.
+ /// The scale of the convex mesh.
+ /// The normalized direction in which cast a convex mesh.
+ /// The result hit information. Valid only when method returns true.
+ /// The convex mesh rotation.
+ /// The maximum distance the ray should check for collisions.
+ /// The layer mask used to filter the results.
+ /// If set to true triggers will be hit, otherwise will skip them.
+ /// True if convex mesh hits an matching object, otherwise false.
+ API_FUNCTION() bool ConvexCast(const Vector3& center, const CollisionData* convexMesh, const Vector3& scale, const Vector3& direction, API_PARAM(Out) RayCastHit& hitInfo, const Quaternion& rotation = Quaternion::Identity, float maxDistance = MAX_float, uint32 layerMask = MAX_uint32, bool hitTriggers = true);
+
+ ///
+ /// Performs a sweep test against objects in the scene using a convex mesh.
+ ///
+ /// The convex mesh center.
+ /// Collision data of the convex mesh.
+ /// The scale of the convex mesh.
+ /// The normalized direction in which cast a convex mesh.
+ /// The result hits. Valid only when method returns true.
+ /// The convex mesh rotation.
+ /// The maximum distance the ray should check for collisions.
+ /// The layer mask used to filter the results.
+ /// If set to true triggers will be hit, otherwise will skip them.
+ /// True if convex mesh hits an matching object, otherwise false.
+ API_FUNCTION() bool ConvexCastAll(const Vector3& center, const CollisionData* convexMesh, const Vector3& scale, const Vector3& direction, API_PARAM(Out) Array& results, const Quaternion& rotation = Quaternion::Identity, float maxDistance = MAX_float, uint32 layerMask = MAX_uint32, bool hitTriggers = true);
+
+ ///
+ /// Checks whether the given box overlaps with other colliders or not.
+ ///
+ /// The box center.
+ /// The half size of the box in each direction.
+ /// The box rotation.
+ /// The layer mask used to filter the results.
+ /// If set to true triggers will be hit, otherwise will skip them.
+ /// True if box overlaps any matching object, otherwise false.
+ API_FUNCTION() bool CheckBox(const Vector3& center, const Vector3& halfExtents, const Quaternion& rotation = Quaternion::Identity, uint32 layerMask = MAX_uint32, bool hitTriggers = true);
+
+ ///
+ /// Checks whether the given sphere overlaps with other colliders or not.
+ ///
+ /// The sphere center.
+ /// The radius of the sphere.
+ /// The layer mask used to filter the results.
+ /// If set to true triggers will be hit, otherwise will skip them.
+ /// True if sphere overlaps any matching object, otherwise false.
+ API_FUNCTION() bool CheckSphere(const Vector3& center, float radius, uint32 layerMask = MAX_uint32, bool hitTriggers = true);
+
+ ///
+ /// Checks whether the given capsule overlaps with other colliders or not.
+ ///
+ /// The capsule center.
+ /// The radius of the capsule.
+ /// The height of the capsule, excluding the top and bottom spheres.
+ /// The capsule rotation.
+ /// The layer mask used to filter the results.
+ /// If set to true triggers will be hit, otherwise will skip them.
+ /// True if capsule overlaps any matching object, otherwise false.
+ API_FUNCTION() bool CheckCapsule(const Vector3& center, float radius, float height, const Quaternion& rotation = Quaternion::Identity, uint32 layerMask = MAX_uint32, bool hitTriggers = true);
+
+ ///
+ /// Checks whether the given convex mesh overlaps with other colliders or not.
+ ///
+ /// The convex mesh center.
+ /// Collision data of the convex mesh.
+ /// The scale of the convex mesh.
+ /// The convex mesh rotation.
+ /// The layer mask used to filter the results.
+ /// If set to true triggers will be hit, otherwise will skip them.
+ /// True if convex mesh overlaps any matching object, otherwise false.
+ API_FUNCTION() bool CheckConvex(const Vector3& center, const CollisionData* convexMesh, const Vector3& scale, const Quaternion& rotation = Quaternion::Identity, uint32 layerMask = MAX_uint32, bool hitTriggers = true);
+
+ ///
+ /// Finds all colliders touching or inside of the given box.
+ ///
+ /// The box center.
+ /// The half size of the box in each direction.
+ /// The box rotation.
+ /// The result colliders that overlap with the given box. Valid only when method returns true.
+ /// The layer mask used to filter the results.
+ /// If set to true triggers will be hit, otherwise will skip them.
+ /// True if box overlaps any matching object, otherwise false.
+ API_FUNCTION() bool OverlapBox(const Vector3& center, const Vector3& halfExtents, API_PARAM(Out) Array& results, const Quaternion& rotation = Quaternion::Identity, uint32 layerMask = MAX_uint32, bool hitTriggers = true);
+
+ ///
+ /// Finds all colliders touching or inside of the given sphere.
+ ///
+ /// The sphere center.
+ /// The radius of the sphere.
+ /// The result colliders that overlap with the given sphere. Valid only when method returns true.
+ /// The layer mask used to filter the results.
+ /// If set to true triggers will be hit, otherwise will skip them.
+ /// True if sphere overlaps any matching object, otherwise false.
+ API_FUNCTION() bool OverlapSphere(const Vector3& center, float radius, API_PARAM(Out) Array& results, uint32 layerMask = MAX_uint32, bool hitTriggers = true);
+
+ ///
+ /// Finds all colliders touching or inside of the given capsule.
+ ///
+ /// The capsule center.
+ /// The radius of the capsule.
+ /// The height of the capsule, excluding the top and bottom spheres.
+ /// The result colliders that overlap with the given capsule. Valid only when method returns true.
+ /// The capsule rotation.
+ /// The layer mask used to filter the results.
+ /// If set to true triggers will be hit, otherwise will skip them.
+ /// True if capsule overlaps any matching object, otherwise false.
+ API_FUNCTION() bool OverlapCapsule(const Vector3& center, float radius, float height, API_PARAM(Out) Array& results, const Quaternion& rotation = Quaternion::Identity, uint32 layerMask = MAX_uint32, bool hitTriggers = true);
+
+ ///
+ /// Finds all colliders touching or inside of the given convex mesh.
+ ///
+ /// The convex mesh center.
+ /// Collision data of the convex mesh.
+ /// The scale of the convex mesh.
+ /// The result colliders that overlap with the given convex mesh. Valid only when method returns true.
+ /// The convex mesh rotation.
+ /// The layer mask used to filter the results.
+ /// If set to true triggers will be hit, otherwise will skip them.
+ /// True if convex mesh overlaps any matching object, otherwise false.
+ API_FUNCTION() bool OverlapConvex(const Vector3& center, const CollisionData* convexMesh, const Vector3& scale, API_PARAM(Out) Array& results, const Quaternion& rotation = Quaternion::Identity, uint32 layerMask = MAX_uint32, bool hitTriggers = true);
+
+ ///
+ /// Finds all colliders touching or inside of the given box.
+ ///
+ /// The box center.
+ /// The half size of the box in each direction.
+ /// The box rotation.
+ /// The result colliders that overlap with the given box. Valid only when method returns true.
+ /// The layer mask used to filter the results.
+ /// If set to true triggers will be hit, otherwise will skip them.
+ /// True if box overlaps any matching object, otherwise false.
+ API_FUNCTION() bool OverlapBox(const Vector3& center, const Vector3& halfExtents, API_PARAM(Out) Array& results, const Quaternion& rotation = Quaternion::Identity, uint32 layerMask = MAX_uint32, bool hitTriggers = true);
+
+ ///
+ /// Finds all colliders touching or inside of the given sphere.
+ ///
+ /// The sphere center.
+ /// The radius of the sphere.
+ /// The result colliders that overlap with the given sphere. Valid only when method returns true.
+ /// The layer mask used to filter the results.
+ /// If set to true triggers will be hit, otherwise will skip them.
+ /// True if sphere overlaps any matching object, otherwise false.
+ API_FUNCTION() bool OverlapSphere(const Vector3& center, float radius, API_PARAM(Out) Array& results, uint32 layerMask = MAX_uint32, bool hitTriggers = true);
+
+ ///
+ /// Finds all colliders touching or inside of the given capsule.
+ ///
+ /// The capsule center.
+ /// The radius of the capsule.
+ /// The height of the capsule, excluding the top and bottom spheres.
+ /// The result colliders that overlap with the given capsule. Valid only when method returns true.
+ /// The capsule rotation.
+ /// The layer mask used to filter the results.
+ /// If set to true triggers will be hit, otherwise will skip them.
+ /// True if capsule overlaps any matching object, otherwise false.
+ API_FUNCTION() bool OverlapCapsule(const Vector3& center, float radius, float height, API_PARAM(Out) Array& results, const Quaternion& rotation = Quaternion::Identity, uint32 layerMask = MAX_uint32, bool hitTriggers = true);
+
+ ///
+ /// Finds all colliders touching or inside of the given convex mesh.
+ ///
+ /// The convex mesh center.
+ /// Collision data of the convex mesh.
+ /// The scale of the convex mesh.
+ /// The result colliders that overlap with the given convex mesh. Valid only when method returns true.
+ /// The convex mesh rotation.
+ /// The layer mask used to filter the results.
+ /// If set to true triggers will be hit, otherwise will skip them.
+ /// True if convex mesh overlaps any matching object, otherwise false.
+ API_FUNCTION() bool OverlapConvex(const Vector3& center, const CollisionData* convexMesh, const Vector3& scale, API_PARAM(Out) Array& results, const Quaternion& rotation = Quaternion::Identity, uint32 layerMask = MAX_uint32, bool hitTriggers = true);
+
+public:
+#if WITH_VEHICLE
+ void AddWheeledVehicle(WheeledVehicle* vehicle);
+ void RemoveWheeledVehicle(WheeledVehicle* vehicle);
+#endif
+
+private:
+ String mName;
+ bool mAutoSimulation = true;
+ void* mScratchMemory = nullptr;
+ FixedStepper* mStepper = nullptr;
+ float mLastDeltaTime = 0.0f;
+ bool mIsDuringSimulation = false;
+
+ PhysicsScenePhysX* mPhysxImpl;
+};
diff --git a/Source/Engine/Physics/PhysicsStepper.h b/Source/Engine/Physics/PhysicsStepper.h
index 4bc1ed536..7e394fdcf 100644
--- a/Source/Engine/Physics/PhysicsStepper.h
+++ b/Source/Engine/Physics/PhysicsStepper.h
@@ -3,7 +3,6 @@
#pragma once
#include "Types.h"
-#include "Engine/Core/Core.h"
#include
#include
#include
diff --git a/Source/Engine/Terrain/Terrain.cpp b/Source/Engine/Terrain/Terrain.cpp
index 5e1f4da77..c09e20609 100644
--- a/Source/Engine/Terrain/Terrain.cpp
+++ b/Source/Engine/Terrain/Terrain.cpp
@@ -830,6 +830,14 @@ void Terrain::OnActiveInTreeChanged()
}
}
+void Terrain::OnPhysicsSceneChanged(PhysicsScene* previous)
+{
+ PhysicsColliderActor::OnPhysicsSceneChanged(previous);
+
+ for (auto patch : _patches)
+ patch->OnPhysicsSceneChanged(previous);
+}
+
void Terrain::BeginPlay(SceneBeginData* data)
{
CacheNeighbors();
diff --git a/Source/Engine/Terrain/Terrain.h b/Source/Engine/Terrain/Terrain.h
index 329fcf4ab..a305feebe 100644
--- a/Source/Engine/Terrain/Terrain.h
+++ b/Source/Engine/Terrain/Terrain.h
@@ -458,6 +458,7 @@ protected:
void OnDisable() override;
void OnTransformChanged() override;
void OnActiveInTreeChanged() override;
+ void OnPhysicsSceneChanged(PhysicsScene* previous) override;
void BeginPlay(SceneBeginData* data) override;
void EndPlay() override;
};
diff --git a/Source/Engine/Terrain/TerrainPatch.cpp b/Source/Engine/Terrain/TerrainPatch.cpp
index 89bcb9123..82b379d39 100644
--- a/Source/Engine/Terrain/TerrainPatch.cpp
+++ b/Source/Engine/Terrain/TerrainPatch.cpp
@@ -7,6 +7,7 @@
#include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Physics/Utilities.h"
#include "Engine/Physics/Physics.h"
+#include "Engine/Physics/PhysicsScene.h"
#include "Engine/Physics/PhysicalMaterial.h"
#include "Engine/Level/Scene/Scene.h"
#include "Engine/Graphics/Async/GPUTask.h"
@@ -1957,7 +1958,7 @@ bool TerrainPatch::UpdateCollision()
_collisionVertices.Resize(0);
// Recreate height field
- Physics::RemoveObject(_physicsHeightField);
+ _terrain->GetPhysicsScene()->RemoveObject(_physicsHeightField);
_physicsHeightField = nullptr;
if (CreateHeightField())
{
@@ -2129,7 +2130,7 @@ void TerrainPatch::UpdatePostManualDeserialization()
_collisionVertices.Resize(0);
// Recreate height field
- Physics::RemoveObject(_physicsHeightField);
+ _terrain->GetPhysicsScene()->RemoveObject(_physicsHeightField);
_physicsHeightField = nullptr;
if (CreateHeightField())
{
@@ -2190,8 +2191,6 @@ void TerrainPatch::CreateCollision()
_physicsActor->setActorFlag(PxActorFlag::eVISUALIZATION, true);
#endif
_physicsActor->attachShape(*_physicsShape);
-
- Physics::AddActor(_physicsActor);
}
bool TerrainPatch::CreateHeightField()
@@ -2243,10 +2242,11 @@ void TerrainPatch::DestroyCollision()
ScopeLock lock(_collisionLocker);
ASSERT(HasCollision());
- Physics::RemoveCollider(_terrain);
- Physics::RemoveActor(_physicsActor);
- Physics::RemoveObject(_physicsShape);
- Physics::RemoveObject(_physicsHeightField);
+ _terrain->GetPhysicsScene()->RemoveCollider(_terrain);
+ _terrain->GetPhysicsScene()->RemoveActor(_physicsActor);
+ _terrain->GetPhysicsScene()->RemoveObject(_physicsShape);
+ _terrain->GetPhysicsScene()->RemoveObject(_physicsHeightField);
+
_physicsActor = nullptr;
_physicsShape = nullptr;
_physicsHeightField = nullptr;
@@ -2609,3 +2609,9 @@ void TerrainPatch::Deserialize(DeserializeStream& stream, ISerializeModifier* mo
}
}
}
+
+void TerrainPatch::OnPhysicsSceneChanged(PhysicsScene* previous)
+{
+ previous->UnlinkActor(_physicsActor);
+ _terrain->GetPhysicsScene()->AddActor(_physicsActor);
+}
diff --git a/Source/Engine/Terrain/TerrainPatch.h b/Source/Engine/Terrain/TerrainPatch.h
index c98436c5f..7e27220ca 100644
--- a/Source/Engine/Terrain/TerrainPatch.h
+++ b/Source/Engine/Terrain/TerrainPatch.h
@@ -411,6 +411,7 @@ private:
/// True if failed, otherwise false.
bool UpdateCollision();
+ void OnPhysicsSceneChanged(PhysicsScene* previous);
public:
// [ISerializable]