From a4e102672d561046d3bc2d57cd815ffc7cff306f Mon Sep 17 00:00:00 2001 From: Iain Mckay Date: Wed, 1 Dec 2021 15:30:31 +0100 Subject: [PATCH 1/7] Adds feature for creating multiple physics scenes --- Source/Editor/States/PlayingState.cs | 2 +- Source/Engine/Engine/Engine.cpp | 10 +- Source/Engine/Level/Actor.cpp | 25 + Source/Engine/Level/Actor.h | 16 + Source/Engine/Level/Level.cpp | 7 + Source/Engine/Physics/Actors/PhysicsActor.h | 2 + Source/Engine/Physics/Actors/RigidBody.cpp | 20 +- Source/Engine/Physics/Actors/RigidBody.h | 1 + .../Engine/Physics/Actors/SplineRopeBody.cpp | 3 +- .../Engine/Physics/Actors/WheeledVehicle.cpp | 16 +- Source/Engine/Physics/Actors/WheeledVehicle.h | 5 +- .../Physics/Colliders/CharacterController.cpp | 17 +- .../Physics/Colliders/CharacterController.h | 1 + Source/Engine/Physics/Colliders/Collider.cpp | 21 +- Source/Engine/Physics/Colliders/Collider.h | 2 + .../Engine/Physics/Colliders/MeshCollider.cpp | 3 +- .../Physics/Colliders/SplineCollider.cpp | 8 +- Source/Engine/Physics/CollisionData.cpp | 10 +- Source/Engine/Physics/Joints/Joint.cpp | 2 +- Source/Engine/Physics/Physics.Queries.cpp | 612 +------------- Source/Engine/Physics/Physics.cpp | 775 ++---------------- Source/Engine/Physics/Physics.h | 71 +- .../Engine/Physics/PhysicsScene.Queries.cpp | 657 +++++++++++++++ Source/Engine/Physics/PhysicsScene.cpp | 770 +++++++++++++++++ Source/Engine/Physics/PhysicsScene.h | 560 +++++++++++++ Source/Engine/Physics/PhysicsStepper.h | 1 - Source/Engine/Terrain/Terrain.cpp | 8 + Source/Engine/Terrain/Terrain.h | 1 + Source/Engine/Terrain/TerrainPatch.cpp | 22 +- Source/Engine/Terrain/TerrainPatch.h | 1 + 30 files changed, 2312 insertions(+), 1337 deletions(-) create mode 100644 Source/Engine/Physics/PhysicsScene.Queries.cpp create mode 100644 Source/Engine/Physics/PhysicsScene.cpp create mode 100644 Source/Engine/Physics/PhysicsScene.h 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 7b991bf8e..69f9b7911 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..8c040cd6c 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,26 @@ 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 1d4296285..7bec74965 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 5c13fc402..2073222fa 100644 --- a/Source/Engine/Level/Level.cpp +++ b/Source/Engine/Level/Level.cpp @@ -22,6 +22,7 @@ #include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Scripting/Script.h" #include "Engine/Engine/Time.h" +#include "Engine/Physics/Physics.h" #include "Engine/Scripting/ManagedCLR/MAssembly.h" #include "Engine/Scripting/ManagedCLR/MClass.h" #include "Engine/Scripting/ManagedCLR/MDomain.h" @@ -164,6 +165,8 @@ bool LevelImpl::spawnActor(Actor* actor, Actor* parent) } if (parent == nullptr) parent = Level::Scenes[0]; + + actor->SetPhysicsScene(parent->GetPhysicsScene()); actor->SetParent(parent, true, true); } @@ -776,6 +779,8 @@ bool LevelImpl::unloadScene(Scene* scene) // Force flush deleted objects so we actually delete unloaded scene objects (prevent from reloading their managed objects, etc.) ObjectsRemovalService::Flush(); + Physics::EndPlay(); + return false; } @@ -1026,6 +1031,8 @@ bool Level::loadScene(rapidjson_flax::Value& data, int32 engineBuild, Scene** ou { PROFILE_CPU_NAMED("BeginPlay"); + Physics::BeginPlay(); + ScopeLock lock(ScenesLock); Scenes.Add(scene); SceneBeginData beginData; 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..09a8f7986 100644 --- a/Source/Engine/Physics/Actors/RigidBody.cpp +++ b/Source/Engine/Physics/Actors/RigidBody.cpp @@ -2,10 +2,14 @@ #include "RigidBody.h" #include "PxMaterial.h" +#include + +#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 +480,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 +584,7 @@ void RigidBody::EndPlay() if (_actor) { // Remove actor - Physics::RemoveActor(_actor); + GetPhysicsScene()->RemoveActor(_actor); _actor = nullptr; } } @@ -627,3 +631,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..d15e4c718 100644 --- a/Source/Engine/Physics/Colliders/CharacterController.cpp +++ b/Source/Engine/Physics/Colliders/CharacterController.cpp @@ -7,9 +7,10 @@ #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 +157,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,7 +172,7 @@ 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(); @@ -243,7 +244,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 +434,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..d7ef43d21 100644 --- a/Source/Engine/Physics/Colliders/Collider.h +++ b/Source/Engine/Physics/Colliders/Collider.h @@ -232,4 +232,6 @@ 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..d924d0011 100644 --- a/Source/Engine/Physics/Colliders/SplineCollider.cpp +++ b/Source/Engine/Physics/Colliders/SplineCollider.cpp @@ -8,12 +8,14 @@ #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" #include #include #endif + #include SplineCollider::SplineCollider(const SpawnParams& params) @@ -45,7 +47,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 +174,7 @@ void SplineCollider::EndPlay() // Cleanup if (_triangleMesh) { - Physics::RemoveObject(_triangleMesh); + GetPhysicsScene()->RemoveObject(_triangleMesh); _triangleMesh = nullptr; } } @@ -306,7 +308,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..679694928 100644 --- a/Source/Engine/Physics/CollisionData.cpp +++ b/Source/Engine/Physics/CollisionData.cpp @@ -1,6 +1,8 @@ // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. #include "CollisionData.h" +#include "PhysicsScene.h" + #include "Engine/Core/Log.h" #include "Engine/Content/Content.h" #include "Engine/Content/Assets/Model.h" @@ -384,12 +386,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/Joints/Joint.cpp b/Source/Engine/Physics/Joints/Joint.cpp index 84a8ad249..b55cbcabb 100644 --- a/Source/Engine/Physics/Joints/Joint.cpp +++ b/Source/Engine/Physics/Joints/Joint.cpp @@ -181,7 +181,7 @@ void Joint::OnJointBreak() void Joint::Delete() { // Remove the joint - Physics::RemoveJoint(this); + Physics::RemoveJointAll(this); _joint->userData = nullptr; _joint->release(); _joint = nullptr; 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..0942cc150 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); } } @@ -422,45 +324,8 @@ 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 = new PhysicsScene(String("Default"), settings, cpuInfo); + Physics::Scenes.Add(Physics::DefaultScene); // Create default resources DefaultMaterial = CPhysX->createMaterial(0.7f, 0.7f, 0.3f); @@ -472,15 +337,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 +359,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 +411,6 @@ PxCooking* Physics::GetCooking() return Cooking; } -PxScene* Physics::GetScene() -{ - return PhysicsScene; -} - -PxControllerManager* Physics::GetControllerManager() -{ - return ControllerManager; -} - PxTolerancesScale* Physics::GetTolerancesScale() { return &ToleranceScale; @@ -565,415 +418,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 +489,60 @@ 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(); + for (auto scene : Scenes) + scene->FlushRequests(); } -void Physics::RemoveObject(PxBase* obj) +void Physics::RemoveJointAll(Joint* joint) { - ASSERT(obj); - - FlushLocker.Lock(); - _deadObjects.Add(obj); - FlushLocker.Unlock(); + for (auto scene : Scenes) + scene->RemoveJoint(joint); } -void Physics::AddActor(PxActor* actor) +PhysicsScene* Physics::FindOrCreateScene(String name) { - ASSERT(actor); + auto scene = FindScene(name); - FlushLocker.Lock(); - if (IsInMainThread()) + if (scene == nullptr) { - GetScene()->addActor(*actor); + auto cpuInfo = Platform::GetCPUInfo(); + auto& settings = *PhysicsSettings::Get(); + + scene = new PhysicsScene(name, settings, cpuInfo); + Scenes.Add(scene); } - else + + return scene; +} + +PhysicsScene* Physics::FindScene(String name) +{ + for (auto scene : Scenes) { - NewActors.Add(actor); - } - FlushLocker.Unlock(); + if (scene->GetName() == name) + return scene; + } + + return nullptr; } -void Physics::AddActor(PxRigidDynamic* actor, bool putToSleep) +void Physics::BeginPlay() { - 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(); + reset(); } -void Physics::RemoveActor(PxActor* actor) + +void Physics::EndPlay() { - 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(); -} - -void Physics::RemoveJoint(Joint* joint) -{ - ASSERT(joint); - - FlushLocker.Lock(); - DeadJoints.Add(joint); - FlushLocker.Unlock(); + reset(); } diff --git a/Source/Engine/Physics/Physics.h b/Source/Engine/Physics/Physics.h index 6eb38dcd6..e532ccc82 100644 --- a/Source/Engine/Physics/Physics.h +++ b/Source/Engine/Physics/Physics.h @@ -2,6 +2,7 @@ #pragma once +#include "Engine/Core/Collections/Array.h" #include "Engine/Core/Math/Vector2.h" #include "Engine/Core/Math/Vector3.h" #include "Engine/Core/Math/Quaternion.h" @@ -9,6 +10,7 @@ #include "Types.h" class PhysicsColliderActor; +class PhysicsScene; class Joint; class Collider; class CollisionData; @@ -87,47 +89,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(String name); + + /// + ///Finds an existing scene. + /// + API_FUNCTION() static PhysicsScene* FindScene(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 +519,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 +546,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). @@ -578,5 +589,9 @@ public: /// Marks that joint has been removed (all collision events should be cleared to prevent leaks of using removed object). /// /// The joint. - static void RemoveJoint(Joint* joint); + static void RemoveJointAll(Joint* joint); + +public: + static void BeginPlay(); + static void EndPlay(); }; diff --git a/Source/Engine/Physics/PhysicsScene.Queries.cpp b/Source/Engine/Physics/PhysicsScene.Queries.cpp new file mode 100644 index 000000000..d3101f681 --- /dev/null +++ b/Source/Engine/Physics/PhysicsScene.Queries.cpp @@ -0,0 +1,657 @@ +// 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; + } +}; + +PxQueryFilterCallback* PhysicsScene::GetQueryFilterCallback() +{ + static QueryFilter Filter; + return &Filter; +} + +PxQueryFilterCallback* PhysicsScene::GetCharacterQueryFilterCallback() +{ + static CharacterQueryFilter 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; +} + +#if WITH_VEHICLE +void PhysicsScene::AddWheeledVehicle(WheeledVehicle* vehicle) +{ + mWheelVehicles.Add(vehicle); +} + +void PhysicsScene::RemoveWheeledVehicle(WheeledVehicle* vehicle) +{ + mWheelVehicles.Remove(vehicle); +} +#endif diff --git a/Source/Engine/Physics/PhysicsScene.cpp b/Source/Engine/Physics/PhysicsScene.cpp new file mode 100644 index 000000000..0a69ffd2e --- /dev/null +++ b/Source/Engine/Physics/PhysicsScene.cpp @@ -0,0 +1,770 @@ +#include "Physics.h" +#include "PhysicsScene.h" +#include "PhysicsSettings.h" +#include "PhysicsStepper.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 + +PhysicsScene::PhysicsScene(String name, PhysicsSettings settings, CPUInfo cpuInfo) + : PersistentScriptingObject(SpawnParams(Guid::New(), TypeInitializer)) +{ +#define CHECK_INIT(value, msg) if(!value) { LOG(Error, msg); return; } + + mName = name; + + // 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 = &mEventsCallback; + sceneDesc.filterShader = FilterShader; + sceneDesc.bounceThresholdVelocity = settings.BounceThresholdVelocity; + if (sceneDesc.cpuDispatcher == nullptr) + { + mCpuDispatcher = PxDefaultCpuDispatcherCreate(Math::Clamp(cpuInfo.ProcessorCoreCount - 1, 1, 4)); + CHECK_INIT(mCpuDispatcher, "PxDefaultCpuDispatcherCreate failed!"); + sceneDesc.cpuDispatcher = mCpuDispatcher; + } + if (sceneDesc.filterShader == nullptr) + { + sceneDesc.filterShader = mPhysXDefaultFilterShader; + } + + // Create scene + mScene = CPhysX->createScene(sceneDesc); + CHECK_INIT(mScene, "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 + mControllerManager = PxCreateControllerManager(*mScene); +} + +PhysicsScene::~PhysicsScene() +{ +#if WITH_VEHICLE + RELEASE_PHYSX(mWheelRaycastBatchQuery); + RELEASE_PHYSX(mWheelTireFrictions); + mWheelQueryResults.Resize(0); + mWheelHitResults.Resize(0); + mWheelVehiclesResultsPerWheel.Resize(0); + mWheelVehiclesResultsPerVehicle.Resize(0); +#endif + + RELEASE_PHYSX(mControllerManager); + SAFE_DELETE(mCpuDispatcher); + SAFE_DELETE(mStepper); + Allocator::Free(mScratchMemory); + + mScratchMemory = nullptr; + mScene->release(); +} + +String PhysicsScene::GetName() const +{ + return mName; +} + +PxScene* PhysicsScene::GetScene() +{ + return mScene; +} + +bool PhysicsScene::GetAutoSimulation() +{ + return mAutoSimulation; +} + +void PhysicsScene::SetAutoSimulation(bool value) +{ + mAutoSimulation = value; +} + +void PhysicsScene::SetGravity(const Vector3& value) +{ + if(mScene) + { + mScene->setGravity(C2P(value)); + } +} + +Vector3 PhysicsScene::GetGravity() +{ + return mScene ? P2C(mScene->getGravity()) : Vector3::Zero; +} + +bool PhysicsScene::GetEnableCCD() +{ + return mScene ? (mScene->getFlags() & PxSceneFlag::eENABLE_CCD) == PxSceneFlag::eENABLE_CCD : !PhysicsSettings::Get()->DisableCCD; +} + +void PhysicsScene::SetEnableCCD(const bool value) +{ + if (mScene) + mScene->setFlag(PxSceneFlag::eENABLE_CCD, value); +} + +float PhysicsScene::GetBounceThresholdVelocity() +{ + return mScene ? mScene->getBounceThresholdVelocity() : PhysicsSettings::Get()->BounceThresholdVelocity; +} + +void PhysicsScene::SetBounceThresholdVelocity(const float value) +{ + if (mScene) + mScene->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(mScene, dt, mScratchMemory, SCRATCH_BLOCK_SIZE) == false) + return; + mEventsCallback.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(mScene); + } + +#if WITH_VEHICLE + if (mWheelVehicles.HasItems()) + { + PROFILE_CPU_NAMED("Physics.Vehicles"); + + // Update vehicles steering + mWheelVehiclesCache.Clear(); + mWheelVehiclesCache.EnsureCapacity(mWheelVehicles.Count()); + int32 wheelsCount = 0; + for (auto wheelVehicle : mWheelVehicles) + { + if (!wheelVehicle->IsActiveInHierarchy()) + continue; + auto drive = (PxVehicleWheels*)wheelVehicle->_drive; + ASSERT(drive); + mWheelVehiclesCache.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 > mWheelQueryResults.Count()) + { + if (mWheelRaycastBatchQuery) + mWheelRaycastBatchQuery->release(); + mWheelQueryResults.Resize(wheelsCount, false); + mWheelHitResults.Resize(wheelsCount, false); + PxBatchQueryDesc desc(wheelsCount, 0, 0); + desc.queryMemory.userRaycastResultBuffer = mWheelQueryResults.Get(); + desc.queryMemory.userRaycastTouchBuffer = mWheelHitResults.Get(); + desc.queryMemory.raycastTouchBufferSize = wheelsCount; + desc.preFilterShader = WheelRaycastPreFilter; + mWheelRaycastBatchQuery = mScene->createBatchQuery(desc); + } + + // TODO: expose vehicle tires configuration + if (!mWheelTireFrictions) + { + PxVehicleDrivableSurfaceType surfaceTypes[1]; + surfaceTypes[0].mType = 0; + const PxMaterial* surfaceMaterials[1]; + surfaceMaterials[0] = Physics::GetDefaultMaterial(); + mWheelTireFrictions = PxVehicleDrivableSurfaceToTireFrictionPairs::allocate(1, 1); + mWheelTireFrictions->setup(1, 1, surfaceMaterials, surfaceTypes); + mWheelTireFrictions->setTypePairFriction(0, 0, 5.0f); + } + + // Setup cache for wheel states + mWheelVehiclesResultsPerVehicle.Resize(mWheelVehiclesCache.Count(), false); + mWheelVehiclesResultsPerWheel.Resize(wheelsCount, false); + wheelsCount = 0; + for (int32 i = 0, ii = 0; i < mWheelVehicles.Count(); i++) + { + auto wheelVehicle = mWheelVehicles[i]; + if (!wheelVehicle->IsActiveInHierarchy()) + continue; + auto drive = (PxVehicleWheels*)mWheelVehicles[ii]->_drive; + auto& perVehicle = mWheelVehiclesResultsPerVehicle[ii]; + ii++; + perVehicle.nbWheelQueryResults = drive->mWheelsSimData.getNbWheels(); + perVehicle.wheelQueryResults = mWheelVehiclesResultsPerWheel.Get() + wheelsCount; + wheelsCount += perVehicle.nbWheelQueryResults; + } + + // Update vehicles + if (mWheelVehiclesCache.Count() != 0) + { + PxVehicleSuspensionRaycasts(mWheelRaycastBatchQuery, mWheelVehiclesCache.Count(), mWheelVehiclesCache.Get(), mWheelQueryResults.Count(), mWheelQueryResults.Get()); + PxVehicleUpdates(mLastDeltaTime, mScene->getGravity(), *mWheelTireFrictions, mWheelVehiclesCache.Count(), mWheelVehiclesCache.Get(), mWheelVehiclesResultsPerVehicle.Get()); + } + + // Synchronize state + for (int32 i = 0, ii = 0; i < mWheelVehicles.Count(); i++) + { + auto wheelVehicle = mWheelVehicles[i]; + if (!wheelVehicle->IsActiveInHierarchy()) + continue; + auto drive = mWheelVehiclesCache[ii]; + auto& perVehicle = mWheelVehiclesResultsPerVehicle[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 = mScene->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"); + + mEventsCallback.CollectResults(); + mEventsCallback.SendTriggerEvents(); + mEventsCallback.SendCollisionEvents(); + mEventsCallback.SendJointEvents(); + } + + // End + mIsDuringSimulation = false; +} + +void PhysicsScene::FlushRequests() +{ + ASSERT(!IsDuringSimulation()); + ASSERT(CPhysX); + + PROFILE_CPU(); + + mFlushLocker.Lock(); + + // Note: this does not handle case when actor is removed and added to the scene at the same time + + if (mNewActors.HasItems()) + { + GetScene()->addActors(mNewActors.Get(), mNewActors.Count()); + mNewActors.Clear(); + } + + for (int32 i = 0; i < mActions.Count(); i++) + { + const auto action = mActions[i]; + switch (action.Type) + { + case ActionType::Sleep: + static_cast(action.Actor)->putToSleep(); + break; + } + } + mActions.Clear(); + + if (mDeadActors.HasItems()) + { + GetScene()->removeActors(mDeadActors.Get(), mDeadActors.Count(), true); + for (int32 i = 0; i < mDeadActors.Count(); i++) + { + mDeadActors[i]->release(); + } + mDeadActors.Clear(); + } + + if (mDeadColliders.HasItems()) + { + for (int32 i = 0; i < mDeadColliders.Count(); i++) + { + mEventsCallback.OnColliderRemoved(mDeadColliders[i]); + } + mDeadColliders.Clear(); + } + + if (mDeadJoints.HasItems()) + { + for (int32 i = 0; i < mDeadJoints.Count(); i++) + { + mEventsCallback.OnJointRemoved(mDeadJoints[i]); + } + mDeadJoints.Clear(); + } + + for (int32 i = 0; i < mDeadMaterials.Count(); i++) + { + auto material = mDeadMaterials[i]; + + // Unlink ref to flax object + material->userData = nullptr; + + material->release(); + } + mDeadMaterials.Clear(); + + for (int32 i = 0; i < mDeadObjects.Count(); i++) + { + mDeadObjects[i]->release(); + } + mDeadObjects.Clear(); + + mFlushLocker.Unlock(); +} + +void PhysicsScene::RemoveMaterial(PxMaterial* material) +{ + ASSERT(material); + + mFlushLocker.Lock(); + mDeadMaterials.Add(material); + mFlushLocker.Unlock(); +} + +void PhysicsScene::RemoveObject(PxBase* obj) +{ + ASSERT(obj); + + mFlushLocker.Lock(); + mDeadObjects.Add(obj); + mFlushLocker.Unlock(); +} + +void PhysicsScene::AddActor(PxActor* actor) +{ + ASSERT(actor); + + mFlushLocker.Lock(); + if (IsInMainThread()) + { + GetScene()->addActor(*actor); + } + else + { + mNewActors.Add(actor); + } + mFlushLocker.Unlock(); +} + +void PhysicsScene::AddActor(PxRigidDynamic* actor, bool putToSleep) +{ + ASSERT(actor); + + mFlushLocker.Lock(); + if (IsInMainThread()) + { + GetScene()->addActor(*actor); + if (putToSleep) + actor->putToSleep(); + } + else + { + mNewActors.Add(actor); + if (putToSleep) + mActions.Add({ ActionType::Sleep, actor }); + } + mFlushLocker.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; + + mFlushLocker.Lock(); + mDeadActors.Add(actor); + mFlushLocker.Unlock(); +} + +void PhysicsScene::RemoveCollider(PhysicsColliderActor* collider) +{ + ASSERT(collider); + + mFlushLocker.Lock(); + mDeadColliders.Add(collider); + mFlushLocker.Unlock(); +} + +void PhysicsScene::RemoveJoint(Joint* joint) +{ + ASSERT(joint); + + mFlushLocker.Lock(); + mDeadJoints.Add(joint); + mFlushLocker.Unlock(); +} + +PxControllerManager* PhysicsScene::GetControllerManager() +{ + return mControllerManager; +} diff --git a/Source/Engine/Physics/PhysicsScene.h b/Source/Engine/Physics/PhysicsScene.h new file mode 100644 index 000000000..c940ad83b --- /dev/null +++ b/Source/Engine/Physics/PhysicsScene.h @@ -0,0 +1,560 @@ +#pragma once + +#include "SimulationEventCallback.h" + +#include "Engine/Scripting/ScriptingObject.h" +#include "Engine/Scripting/ScriptingType.h" +#include "Engine/Core/Collections/Array.h" +#include "Engine/Core/Math/Vector3.h" +#include "Engine/Core/Math/Quaternion.h" +#include "Types.h" +#include + +#if WITH_VEHICLE +#include "vehicle/PxVehicleUpdate.h" +#include "vehicle/PxVehicleWheels.h" + +class WheeledVehicle; +#endif + +struct ActionData; +class FixedStepper; +class PhysicsSettings; +class PhysicsColliderActor; +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(String name, PhysicsSettings settings, CPUInfo cpuInfo); + ~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(); + + /// + /// 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; + + PxScene* mScene; + PxCpuDispatcher* mCpuDispatcher; + PxControllerManager* mControllerManager; + PxSimulationFilterShader mPhysXDefaultFilterShader = PxDefaultSimulationFilterShader; + SimulationEventCallback mEventsCallback; + + void* mScratchMemory = nullptr; + FixedStepper* mStepper = nullptr; + float mLastDeltaTime = 0.0f; + bool mIsDuringSimulation = false; + + + CriticalSection mFlushLocker; + Array mNewActors; + Array mDeadActors; + Array mDeadMaterials; + Array mDeadColliders; + Array mDeadJoints; + Array mActions; + Array mDeadObjects; + +#if WITH_VEHICLE + Array mWheelVehiclesCache; + Array mWheelQueryResults; + Array mWheelHitResults; + Array mWheelVehiclesResultsPerWheel; + Array mWheelVehiclesResultsPerVehicle; + PxBatchQuery* mWheelRaycastBatchQuery = nullptr; + PxVehicleDrivableSurfaceToTireFrictionPairs* mWheelTireFrictions = nullptr; + + Array mWheelVehicles; +#endif +}; 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] From 87c2aefe03985703847dc2cf2c9e3143aab70061 Mon Sep 17 00:00:00 2001 From: Iain Mckay Date: Tue, 14 Dec 2021 17:02:33 +0100 Subject: [PATCH 2/7] Code review feedback --- Source/Engine/Level/Actor.cpp | 8 +++++--- Source/Engine/Physics/Actors/RigidBody.cpp | 1 - Source/Engine/Physics/Colliders/CharacterController.cpp | 1 - Source/Engine/Physics/Colliders/Collider.h | 1 - Source/Engine/Physics/CollisionData.cpp | 5 ++--- Source/Engine/Physics/Physics.cpp | 2 +- Source/Engine/Physics/Physics.h | 2 +- Source/Engine/Physics/PhysicsScene.cpp | 4 ++-- Source/Engine/Physics/PhysicsScene.h | 5 ++--- 9 files changed, 13 insertions(+), 16 deletions(-) diff --git a/Source/Engine/Level/Actor.cpp b/Source/Engine/Level/Actor.cpp index 8c040cd6c..f2a0a3a62 100644 --- a/Source/Engine/Level/Actor.cpp +++ b/Source/Engine/Level/Actor.cpp @@ -1820,11 +1820,13 @@ void Actor::SetPhysicsScene(PhysicsScene* scene) _physicsScene = scene; if (previous != _physicsScene) + { OnPhysicsSceneChanged(previous); - // cascade - for (auto child : Children) - child->SetPhysicsScene(scene); + // cascade + for (auto child : Children) + child->SetPhysicsScene(scene); + } } PhysicsScene* Actor::GetPhysicsScene() diff --git a/Source/Engine/Physics/Actors/RigidBody.cpp b/Source/Engine/Physics/Actors/RigidBody.cpp index 09a8f7986..a3047889d 100644 --- a/Source/Engine/Physics/Actors/RigidBody.cpp +++ b/Source/Engine/Physics/Actors/RigidBody.cpp @@ -2,7 +2,6 @@ #include "RigidBody.h" #include "PxMaterial.h" -#include #include "Engine/Core/Log.h" #include "Engine/Physics/Utilities.h" diff --git a/Source/Engine/Physics/Colliders/CharacterController.cpp b/Source/Engine/Physics/Colliders/CharacterController.cpp index d15e4c718..40d5c23c2 100644 --- a/Source/Engine/Physics/Colliders/CharacterController.cpp +++ b/Source/Engine/Physics/Colliders/CharacterController.cpp @@ -8,7 +8,6 @@ #include "Engine/Engine/Time.h" #include "Engine/Physics/PhysicalMaterial.h" #include "Engine/Physics/PhysicsScene.h" - #include #include #include diff --git a/Source/Engine/Physics/Colliders/Collider.h b/Source/Engine/Physics/Colliders/Collider.h index d7ef43d21..479b7c288 100644 --- a/Source/Engine/Physics/Colliders/Collider.h +++ b/Source/Engine/Physics/Colliders/Collider.h @@ -232,6 +232,5 @@ protected: void OnParentChanged() override; void OnTransformChanged() override; void OnLayerChanged() override; - void OnPhysicsSceneChanged(PhysicsScene* previous) override; }; diff --git a/Source/Engine/Physics/CollisionData.cpp b/Source/Engine/Physics/CollisionData.cpp index 679694928..95e5a67ca 100644 --- a/Source/Engine/Physics/CollisionData.cpp +++ b/Source/Engine/Physics/CollisionData.cpp @@ -1,14 +1,13 @@ // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. -#include "CollisionData.h" -#include "PhysicsScene.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 diff --git a/Source/Engine/Physics/Physics.cpp b/Source/Engine/Physics/Physics.cpp index 0942cc150..514a8963b 100644 --- a/Source/Engine/Physics/Physics.cpp +++ b/Source/Engine/Physics/Physics.cpp @@ -503,7 +503,7 @@ void Physics::FlushRequestsAll() scene->FlushRequests(); } -void Physics::RemoveJointAll(Joint* joint) +void Physics::RemoveJoint(Joint* joint) { for (auto scene : Scenes) scene->RemoveJoint(joint); diff --git a/Source/Engine/Physics/Physics.h b/Source/Engine/Physics/Physics.h index e532ccc82..2cbc0a4ec 100644 --- a/Source/Engine/Physics/Physics.h +++ b/Source/Engine/Physics/Physics.h @@ -589,7 +589,7 @@ public: /// Marks that joint has been removed (all collision events should be cleared to prevent leaks of using removed object). /// /// The joint. - static void RemoveJointAll(Joint* joint); + static void RemoveJoint(Joint* joint); public: static void BeginPlay(); diff --git a/Source/Engine/Physics/PhysicsScene.cpp b/Source/Engine/Physics/PhysicsScene.cpp index 0a69ffd2e..383623dd7 100644 --- a/Source/Engine/Physics/PhysicsScene.cpp +++ b/Source/Engine/Physics/PhysicsScene.cpp @@ -92,7 +92,7 @@ static PxQueryHitType::Enum WheelRaycastPreFilter(PxFilterData filterData0, PxFi #endif -PhysicsScene::PhysicsScene(String name, PhysicsSettings settings, CPUInfo cpuInfo) +PhysicsScene::PhysicsScene(const String& name, const PhysicsSettings& settings) : PersistentScriptingObject(SpawnParams(Guid::New(), TypeInitializer)) { #define CHECK_INIT(value, msg) if(!value) { LOG(Error, msg); return; } @@ -112,7 +112,7 @@ PhysicsScene::PhysicsScene(String name, PhysicsSettings settings, CPUInfo cpuInf sceneDesc.bounceThresholdVelocity = settings.BounceThresholdVelocity; if (sceneDesc.cpuDispatcher == nullptr) { - mCpuDispatcher = PxDefaultCpuDispatcherCreate(Math::Clamp(cpuInfo.ProcessorCoreCount - 1, 1, 4)); + mCpuDispatcher = PxDefaultCpuDispatcherCreate(Math::Clamp(Platform::GetCPUInfo().ProcessorCoreCount - 1, 1, 4)); CHECK_INIT(mCpuDispatcher, "PxDefaultCpuDispatcherCreate failed!"); sceneDesc.cpuDispatcher = mCpuDispatcher; } diff --git a/Source/Engine/Physics/PhysicsScene.h b/Source/Engine/Physics/PhysicsScene.h index c940ad83b..e470e7531 100644 --- a/Source/Engine/Physics/PhysicsScene.h +++ b/Source/Engine/Physics/PhysicsScene.h @@ -1,7 +1,6 @@ #pragma once -#include "SimulationEventCallback.h" - +#include "Engine/Physics/SimulationEventCallback.h" #include "Engine/Scripting/ScriptingObject.h" #include "Engine/Scripting/ScriptingType.h" #include "Engine/Core/Collections/Array.h" @@ -32,7 +31,7 @@ API_CLASS(NoSpawn) class FLAXENGINE_API PhysicsScene : public PersistentScriptin { DECLARE_SCRIPTING_TYPE_NO_SPAWN(PhysicsScene); - explicit PhysicsScene(String name, PhysicsSettings settings, CPUInfo cpuInfo); + explicit PhysicsScene(const String& name, const PhysicsSettings& settings); ~PhysicsScene(); /// From 77627f21f65713f9b443609b066fbe26eda8f705 Mon Sep 17 00:00:00 2001 From: Iain Mckay Date: Tue, 14 Dec 2021 19:33:25 +0100 Subject: [PATCH 3/7] Code review feedback --- Source/Engine/Physics/Joints/Joint.cpp | 2 +- Source/Engine/Physics/Physics.cpp | 6 +- .../Engine/Physics/PhysicsScene.Queries.cpp | 12 - Source/Engine/Physics/PhysicsScene.cpp | 233 +++++++++++------- Source/Engine/Physics/PhysicsScene.h | 32 +-- 5 files changed, 145 insertions(+), 140 deletions(-) diff --git a/Source/Engine/Physics/Joints/Joint.cpp b/Source/Engine/Physics/Joints/Joint.cpp index b55cbcabb..84a8ad249 100644 --- a/Source/Engine/Physics/Joints/Joint.cpp +++ b/Source/Engine/Physics/Joints/Joint.cpp @@ -181,7 +181,7 @@ void Joint::OnJointBreak() void Joint::Delete() { // Remove the joint - Physics::RemoveJointAll(this); + Physics::RemoveJoint(this); _joint->userData = nullptr; _joint->release(); _joint = nullptr; diff --git a/Source/Engine/Physics/Physics.cpp b/Source/Engine/Physics/Physics.cpp index 514a8963b..61845660a 100644 --- a/Source/Engine/Physics/Physics.cpp +++ b/Source/Engine/Physics/Physics.cpp @@ -268,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 @@ -324,7 +323,7 @@ bool PhysicsService::Init() } #endif - Physics::DefaultScene = new PhysicsScene(String("Default"), settings, cpuInfo); + Physics::DefaultScene = new PhysicsScene(String("Default"), settings); Physics::Scenes.Add(Physics::DefaultScene); // Create default resources @@ -515,10 +514,9 @@ PhysicsScene* Physics::FindOrCreateScene(String name) if (scene == nullptr) { - auto cpuInfo = Platform::GetCPUInfo(); auto& settings = *PhysicsSettings::Get(); - scene = new PhysicsScene(name, settings, cpuInfo); + scene = new PhysicsScene(name, settings); Scenes.Add(scene); } diff --git a/Source/Engine/Physics/PhysicsScene.Queries.cpp b/Source/Engine/Physics/PhysicsScene.Queries.cpp index d3101f681..790e98382 100644 --- a/Source/Engine/Physics/PhysicsScene.Queries.cpp +++ b/Source/Engine/Physics/PhysicsScene.Queries.cpp @@ -643,15 +643,3 @@ bool PhysicsScene::OverlapConvex(const Vector3& center, const CollisionData* con return true; } - -#if WITH_VEHICLE -void PhysicsScene::AddWheeledVehicle(WheeledVehicle* vehicle) -{ - mWheelVehicles.Add(vehicle); -} - -void PhysicsScene::RemoveWheeledVehicle(WheeledVehicle* vehicle) -{ - mWheelVehicles.Remove(vehicle); -} -#endif diff --git a/Source/Engine/Physics/PhysicsScene.cpp b/Source/Engine/Physics/PhysicsScene.cpp index 383623dd7..bcb4b6daa 100644 --- a/Source/Engine/Physics/PhysicsScene.cpp +++ b/Source/Engine/Physics/PhysicsScene.cpp @@ -92,12 +92,43 @@ static PxQueryHitType::Enum WheelRaycastPreFilter(PxFilterData filterData0, PxFi #endif +class PhysicsScenePhysX +{ + friend PhysicsScene; + +private: + PxScene* Scene; + PxCpuDispatcher* CpuDispatcher; + PxControllerManager* ControllerManager; + PxSimulationFilterShader PhysXDefaultFilterShader = PxDefaultSimulationFilterShader; + + 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 PhysicsScenePhysX(); // Create scene description PxSceneDesc sceneDesc(CPhysX->getTolerancesScale()); @@ -112,18 +143,18 @@ PhysicsScene::PhysicsScene(const String& name, const PhysicsSettings& settings) sceneDesc.bounceThresholdVelocity = settings.BounceThresholdVelocity; if (sceneDesc.cpuDispatcher == nullptr) { - mCpuDispatcher = PxDefaultCpuDispatcherCreate(Math::Clamp(Platform::GetCPUInfo().ProcessorCoreCount - 1, 1, 4)); - CHECK_INIT(mCpuDispatcher, "PxDefaultCpuDispatcherCreate failed!"); - sceneDesc.cpuDispatcher = mCpuDispatcher; + 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 = mPhysXDefaultFilterShader; + sceneDesc.filterShader = mPhysxImpl->PhysXDefaultFilterShader; } // Create scene - mScene = CPhysX->createScene(sceneDesc); - CHECK_INIT(mScene, "createScene failed!"); + mPhysxImpl->Scene = CPhysX->createScene(sceneDesc); + CHECK_INIT(mPhysxImpl->Scene, "createScene failed!"); #if WITH_PVD auto pvdClient = PhysicsScene->getScenePvdClient(); if (pvdClient) @@ -137,27 +168,29 @@ PhysicsScene::PhysicsScene(const String& name, const PhysicsSettings& settings) #endif // Init characters controller - mControllerManager = PxCreateControllerManager(*mScene); + mPhysxImpl->ControllerManager = PxCreateControllerManager(*mPhysxImpl->Scene); } PhysicsScene::~PhysicsScene() { #if WITH_VEHICLE - RELEASE_PHYSX(mWheelRaycastBatchQuery); - RELEASE_PHYSX(mWheelTireFrictions); - mWheelQueryResults.Resize(0); - mWheelHitResults.Resize(0); - mWheelVehiclesResultsPerWheel.Resize(0); - mWheelVehiclesResultsPerVehicle.Resize(0); + 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(mControllerManager); - SAFE_DELETE(mCpuDispatcher); + RELEASE_PHYSX(mPhysxImpl->ControllerManager); + SAFE_DELETE(mPhysxImpl->CpuDispatcher); SAFE_DELETE(mStepper); Allocator::Free(mScratchMemory); mScratchMemory = nullptr; - mScene->release(); + mPhysxImpl->Scene->release(); + + SAFE_DELETE(mPhysxImpl); } String PhysicsScene::GetName() const @@ -167,7 +200,7 @@ String PhysicsScene::GetName() const PxScene* PhysicsScene::GetScene() { - return mScene; + return mPhysxImpl->Scene; } bool PhysicsScene::GetAutoSimulation() @@ -182,37 +215,37 @@ void PhysicsScene::SetAutoSimulation(bool value) void PhysicsScene::SetGravity(const Vector3& value) { - if(mScene) + if(mPhysxImpl->Scene) { - mScene->setGravity(C2P(value)); + mPhysxImpl->Scene->setGravity(C2P(value)); } } Vector3 PhysicsScene::GetGravity() { - return mScene ? P2C(mScene->getGravity()) : Vector3::Zero; + return mPhysxImpl->Scene ? P2C(mPhysxImpl->Scene->getGravity()) : Vector3::Zero; } bool PhysicsScene::GetEnableCCD() { - return mScene ? (mScene->getFlags() & PxSceneFlag::eENABLE_CCD) == PxSceneFlag::eENABLE_CCD : !PhysicsSettings::Get()->DisableCCD; + return mPhysxImpl->Scene ? (mPhysxImpl->Scene->getFlags() & PxSceneFlag::eENABLE_CCD) == PxSceneFlag::eENABLE_CCD : !PhysicsSettings::Get()->DisableCCD; } void PhysicsScene::SetEnableCCD(const bool value) { - if (mScene) - mScene->setFlag(PxSceneFlag::eENABLE_CCD, value); + if (mPhysxImpl->Scene) + mPhysxImpl->Scene->setFlag(PxSceneFlag::eENABLE_CCD, value); } float PhysicsScene::GetBounceThresholdVelocity() { - return mScene ? mScene->getBounceThresholdVelocity() : PhysicsSettings::Get()->BounceThresholdVelocity; + return mPhysxImpl->Scene ? mPhysxImpl->Scene->getBounceThresholdVelocity() : PhysicsSettings::Get()->BounceThresholdVelocity; } void PhysicsScene::SetBounceThresholdVelocity(const float value) { - if (mScene) - mScene->setBounceThresholdVelocity(value); + if (mPhysxImpl->Scene) + mPhysxImpl->Scene->setBounceThresholdVelocity(value); } void PhysicsScene::Simulate(float dt) @@ -249,7 +282,7 @@ void PhysicsScene::Simulate(float dt) // Start simulation (may not be fired due to too small delta time) mIsDuringSimulation = true; - if (mStepper->advance(mScene, dt, mScratchMemory, SCRATCH_BLOCK_SIZE) == false) + if (mStepper->advance(mPhysxImpl->Scene, dt, mScratchMemory, SCRATCH_BLOCK_SIZE) == false) return; mEventsCallback.Clear(); mLastDeltaTime = dt; @@ -274,25 +307,25 @@ void PhysicsScene::CollectResults() PROFILE_CPU_NAMED("Physics.Fetch"); // Gather results (with waiting for the end) - mStepper->wait(mScene); + mStepper->wait(mPhysxImpl->Scene); } #if WITH_VEHICLE - if (mWheelVehicles.HasItems()) + if (mPhysxImpl->WheelVehicles.HasItems()) { PROFILE_CPU_NAMED("Physics.Vehicles"); // Update vehicles steering - mWheelVehiclesCache.Clear(); - mWheelVehiclesCache.EnsureCapacity(mWheelVehicles.Count()); + mPhysxImpl->WheelVehiclesCache.Clear(); + mPhysxImpl->WheelVehiclesCache.EnsureCapacity(mPhysxImpl->WheelVehicles.Count()); int32 wheelsCount = 0; - for (auto wheelVehicle : mWheelVehicles) + for (auto wheelVehicle : mPhysxImpl->WheelVehicles) { if (!wheelVehicle->IsActiveInHierarchy()) continue; auto drive = (PxVehicleWheels*)wheelVehicle->_drive; ASSERT(drive); - mWheelVehiclesCache.Add(drive); + mPhysxImpl->WheelVehiclesCache.Add(drive); wheelsCount += drive->mWheelsSimData.getNbWheels(); float throttle = wheelVehicle->_throttle; @@ -461,64 +494,64 @@ void PhysicsScene::CollectResults() } // Update batches queries cache - if (wheelsCount > mWheelQueryResults.Count()) + if (wheelsCount > mPhysxImpl->WheelQueryResults.Count()) { - if (mWheelRaycastBatchQuery) - mWheelRaycastBatchQuery->release(); - mWheelQueryResults.Resize(wheelsCount, false); - mWheelHitResults.Resize(wheelsCount, false); + if (mPhysxImpl->WheelRaycastBatchQuery) + mPhysxImpl->WheelRaycastBatchQuery->release(); + mPhysxImpl->WheelQueryResults.Resize(wheelsCount, false); + mPhysxImpl->WheelHitResults.Resize(wheelsCount, false); PxBatchQueryDesc desc(wheelsCount, 0, 0); - desc.queryMemory.userRaycastResultBuffer = mWheelQueryResults.Get(); - desc.queryMemory.userRaycastTouchBuffer = mWheelHitResults.Get(); + desc.queryMemory.userRaycastResultBuffer = mPhysxImpl->WheelQueryResults.Get(); + desc.queryMemory.userRaycastTouchBuffer = mPhysxImpl->WheelHitResults.Get(); desc.queryMemory.raycastTouchBufferSize = wheelsCount; desc.preFilterShader = WheelRaycastPreFilter; - mWheelRaycastBatchQuery = mScene->createBatchQuery(desc); + mPhysxImpl->WheelRaycastBatchQuery = mPhysxImpl->Scene->createBatchQuery(desc); } // TODO: expose vehicle tires configuration - if (!mWheelTireFrictions) + if (!mPhysxImpl->WheelTireFrictions) { PxVehicleDrivableSurfaceType surfaceTypes[1]; surfaceTypes[0].mType = 0; const PxMaterial* surfaceMaterials[1]; surfaceMaterials[0] = Physics::GetDefaultMaterial(); - mWheelTireFrictions = PxVehicleDrivableSurfaceToTireFrictionPairs::allocate(1, 1); - mWheelTireFrictions->setup(1, 1, surfaceMaterials, surfaceTypes); - mWheelTireFrictions->setTypePairFriction(0, 0, 5.0f); + 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 - mWheelVehiclesResultsPerVehicle.Resize(mWheelVehiclesCache.Count(), false); - mWheelVehiclesResultsPerWheel.Resize(wheelsCount, false); + mPhysxImpl->WheelVehiclesResultsPerVehicle.Resize(mPhysxImpl->WheelVehiclesCache.Count(), false); + mPhysxImpl->WheelVehiclesResultsPerWheel.Resize(wheelsCount, false); wheelsCount = 0; - for (int32 i = 0, ii = 0; i < mWheelVehicles.Count(); i++) + for (int32 i = 0, ii = 0; i < mPhysxImpl->WheelVehicles.Count(); i++) { - auto wheelVehicle = mWheelVehicles[i]; + auto wheelVehicle = mPhysxImpl->WheelVehicles[i]; if (!wheelVehicle->IsActiveInHierarchy()) continue; - auto drive = (PxVehicleWheels*)mWheelVehicles[ii]->_drive; - auto& perVehicle = mWheelVehiclesResultsPerVehicle[ii]; + auto drive = (PxVehicleWheels*)mPhysxImpl->WheelVehicles[ii]->_drive; + auto& perVehicle = mPhysxImpl->WheelVehiclesResultsPerVehicle[ii]; ii++; perVehicle.nbWheelQueryResults = drive->mWheelsSimData.getNbWheels(); - perVehicle.wheelQueryResults = mWheelVehiclesResultsPerWheel.Get() + wheelsCount; + perVehicle.wheelQueryResults = mPhysxImpl->WheelVehiclesResultsPerWheel.Get() + wheelsCount; wheelsCount += perVehicle.nbWheelQueryResults; } // Update vehicles - if (mWheelVehiclesCache.Count() != 0) + if (mPhysxImpl->WheelVehiclesCache.Count() != 0) { - PxVehicleSuspensionRaycasts(mWheelRaycastBatchQuery, mWheelVehiclesCache.Count(), mWheelVehiclesCache.Get(), mWheelQueryResults.Count(), mWheelQueryResults.Get()); - PxVehicleUpdates(mLastDeltaTime, mScene->getGravity(), *mWheelTireFrictions, mWheelVehiclesCache.Count(), mWheelVehiclesCache.Get(), mWheelVehiclesResultsPerVehicle.Get()); + 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 < mWheelVehicles.Count(); i++) + for (int32 i = 0, ii = 0; i < mPhysxImpl->WheelVehicles.Count(); i++) { - auto wheelVehicle = mWheelVehicles[i]; + auto wheelVehicle = mPhysxImpl->WheelVehicles[i]; if (!wheelVehicle->IsActiveInHierarchy()) continue; - auto drive = mWheelVehiclesCache[ii]; - auto& perVehicle = mWheelVehiclesResultsPerVehicle[ii]; + 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()); @@ -567,7 +600,7 @@ void PhysicsScene::CollectResults() // Gather change info PxU32 activeActorsCount; - PxActor** activeActors = mScene->getActiveActors(activeActorsCount); + PxActor** activeActors = mPhysxImpl->Scene->getActiveActors(activeActorsCount); if (activeActorsCount > 0) { // Update changed transformations @@ -606,15 +639,15 @@ void PhysicsScene::FlushRequests() // Note: this does not handle case when actor is removed and added to the scene at the same time - if (mNewActors.HasItems()) + if (mPhysxImpl->NewActors.HasItems()) { - GetScene()->addActors(mNewActors.Get(), mNewActors.Count()); - mNewActors.Clear(); + GetScene()->addActors(mPhysxImpl->NewActors.Get(), mPhysxImpl->NewActors.Count()); + mPhysxImpl->NewActors.Clear(); } - for (int32 i = 0; i < mActions.Count(); i++) + for (int32 i = 0; i < mPhysxImpl->Actions.Count(); i++) { - const auto action = mActions[i]; + const auto action = mPhysxImpl->Actions[i]; switch (action.Type) { case ActionType::Sleep: @@ -622,52 +655,52 @@ void PhysicsScene::FlushRequests() break; } } - mActions.Clear(); + mPhysxImpl->Actions.Clear(); - if (mDeadActors.HasItems()) + if (mPhysxImpl->DeadActors.HasItems()) { - GetScene()->removeActors(mDeadActors.Get(), mDeadActors.Count(), true); - for (int32 i = 0; i < mDeadActors.Count(); i++) + GetScene()->removeActors(mPhysxImpl->DeadActors.Get(), mPhysxImpl->DeadActors.Count(), true); + for (int32 i = 0; i < mPhysxImpl->DeadActors.Count(); i++) { - mDeadActors[i]->release(); + mPhysxImpl->DeadActors[i]->release(); } - mDeadActors.Clear(); + mPhysxImpl->DeadActors.Clear(); } - if (mDeadColliders.HasItems()) + if (mPhysxImpl->DeadColliders.HasItems()) { - for (int32 i = 0; i < mDeadColliders.Count(); i++) + for (int32 i = 0; i < mPhysxImpl->DeadColliders.Count(); i++) { - mEventsCallback.OnColliderRemoved(mDeadColliders[i]); + mEventsCallback.OnColliderRemoved(mPhysxImpl->DeadColliders[i]); } - mDeadColliders.Clear(); + mPhysxImpl->DeadColliders.Clear(); } - if (mDeadJoints.HasItems()) + if (mPhysxImpl->DeadJoints.HasItems()) { - for (int32 i = 0; i < mDeadJoints.Count(); i++) + for (int32 i = 0; i < mPhysxImpl->DeadJoints.Count(); i++) { - mEventsCallback.OnJointRemoved(mDeadJoints[i]); + mEventsCallback.OnJointRemoved(mPhysxImpl->DeadJoints[i]); } - mDeadJoints.Clear(); + mPhysxImpl->DeadJoints.Clear(); } - for (int32 i = 0; i < mDeadMaterials.Count(); i++) + for (int32 i = 0; i < mPhysxImpl->DeadMaterials.Count(); i++) { - auto material = mDeadMaterials[i]; + auto material = mPhysxImpl->DeadMaterials[i]; // Unlink ref to flax object material->userData = nullptr; material->release(); } - mDeadMaterials.Clear(); + mPhysxImpl->DeadMaterials.Clear(); - for (int32 i = 0; i < mDeadObjects.Count(); i++) + for (int32 i = 0; i < mPhysxImpl->DeadObjects.Count(); i++) { - mDeadObjects[i]->release(); + mPhysxImpl->DeadObjects[i]->release(); } - mDeadObjects.Clear(); + mPhysxImpl->DeadObjects.Clear(); mFlushLocker.Unlock(); } @@ -677,7 +710,7 @@ void PhysicsScene::RemoveMaterial(PxMaterial* material) ASSERT(material); mFlushLocker.Lock(); - mDeadMaterials.Add(material); + mPhysxImpl->DeadMaterials.Add(material); mFlushLocker.Unlock(); } @@ -686,7 +719,7 @@ void PhysicsScene::RemoveObject(PxBase* obj) ASSERT(obj); mFlushLocker.Lock(); - mDeadObjects.Add(obj); + mPhysxImpl->DeadObjects.Add(obj); mFlushLocker.Unlock(); } @@ -701,7 +734,7 @@ void PhysicsScene::AddActor(PxActor* actor) } else { - mNewActors.Add(actor); + mPhysxImpl->NewActors.Add(actor); } mFlushLocker.Unlock(); } @@ -719,9 +752,9 @@ void PhysicsScene::AddActor(PxRigidDynamic* actor, bool putToSleep) } else { - mNewActors.Add(actor); + mPhysxImpl->NewActors.Add(actor); if (putToSleep) - mActions.Add({ ActionType::Sleep, actor }); + mPhysxImpl->Actions.Add({ ActionType::Sleep, actor }); } mFlushLocker.Unlock(); } @@ -742,7 +775,7 @@ void PhysicsScene::RemoveActor(PxActor* actor) actor->userData = nullptr; mFlushLocker.Lock(); - mDeadActors.Add(actor); + mPhysxImpl->DeadActors.Add(actor); mFlushLocker.Unlock(); } @@ -751,7 +784,7 @@ void PhysicsScene::RemoveCollider(PhysicsColliderActor* collider) ASSERT(collider); mFlushLocker.Lock(); - mDeadColliders.Add(collider); + mPhysxImpl->DeadColliders.Add(collider); mFlushLocker.Unlock(); } @@ -760,11 +793,23 @@ void PhysicsScene::RemoveJoint(Joint* joint) ASSERT(joint); mFlushLocker.Lock(); - mDeadJoints.Add(joint); + mPhysxImpl->DeadJoints.Add(joint); mFlushLocker.Unlock(); } PxControllerManager* PhysicsScene::GetControllerManager() { - return mControllerManager; + 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 index e470e7531..4993f216e 100644 --- a/Source/Engine/Physics/PhysicsScene.h +++ b/Source/Engine/Physics/PhysicsScene.h @@ -3,23 +3,20 @@ #include "Engine/Physics/SimulationEventCallback.h" #include "Engine/Scripting/ScriptingObject.h" #include "Engine/Scripting/ScriptingType.h" -#include "Engine/Core/Collections/Array.h" #include "Engine/Core/Math/Vector3.h" #include "Engine/Core/Math/Quaternion.h" #include "Types.h" -#include #if WITH_VEHICLE -#include "vehicle/PxVehicleUpdate.h" -#include "vehicle/PxVehicleWheels.h" - class WheeledVehicle; #endif + struct ActionData; class FixedStepper; class PhysicsSettings; class PhysicsColliderActor; +class PhysicsScenePhysX; class Joint; class Collider; class CollisionData; @@ -523,11 +520,6 @@ public: private: String mName; bool mAutoSimulation = true; - - PxScene* mScene; - PxCpuDispatcher* mCpuDispatcher; - PxControllerManager* mControllerManager; - PxSimulationFilterShader mPhysXDefaultFilterShader = PxDefaultSimulationFilterShader; SimulationEventCallback mEventsCallback; void* mScratchMemory = nullptr; @@ -535,25 +527,7 @@ private: float mLastDeltaTime = 0.0f; bool mIsDuringSimulation = false; - CriticalSection mFlushLocker; - Array mNewActors; - Array mDeadActors; - Array mDeadMaterials; - Array mDeadColliders; - Array mDeadJoints; - Array mActions; - Array mDeadObjects; -#if WITH_VEHICLE - Array mWheelVehiclesCache; - Array mWheelQueryResults; - Array mWheelHitResults; - Array mWheelVehiclesResultsPerWheel; - Array mWheelVehiclesResultsPerVehicle; - PxBatchQuery* mWheelRaycastBatchQuery = nullptr; - PxVehicleDrivableSurfaceToTireFrictionPairs* mWheelTireFrictions = nullptr; - - Array mWheelVehicles; -#endif + PhysicsScenePhysX* mPhysxImpl; }; From 32f225c922c1b4fcd081caf60f2848d1bd13f42c Mon Sep 17 00:00:00 2001 From: Iain Mckay Date: Wed, 15 Dec 2021 16:34:42 +0100 Subject: [PATCH 4/7] Code review changes --- Source/Engine/Physics/Colliders/SplineCollider.cpp | 1 - Source/Engine/Physics/Physics.cpp | 8 ++++---- Source/Engine/Physics/Physics.h | 4 ++-- Source/Engine/Physics/PhysicsScene.cpp | 2 +- Source/Engine/Physics/PhysicsScene.h | 1 - 5 files changed, 7 insertions(+), 9 deletions(-) diff --git a/Source/Engine/Physics/Colliders/SplineCollider.cpp b/Source/Engine/Physics/Colliders/SplineCollider.cpp index d924d0011..62516a954 100644 --- a/Source/Engine/Physics/Colliders/SplineCollider.cpp +++ b/Source/Engine/Physics/Colliders/SplineCollider.cpp @@ -15,7 +15,6 @@ #include #include #endif - #include SplineCollider::SplineCollider(const SpawnParams& params) diff --git a/Source/Engine/Physics/Physics.cpp b/Source/Engine/Physics/Physics.cpp index 61845660a..e51d0c6d8 100644 --- a/Source/Engine/Physics/Physics.cpp +++ b/Source/Engine/Physics/Physics.cpp @@ -323,7 +323,7 @@ bool PhysicsService::Init() } #endif - Physics::DefaultScene = new PhysicsScene(String("Default"), settings); + Physics::DefaultScene = Physics::FindOrCreateScene(TEXT("Default")); Physics::Scenes.Add(Physics::DefaultScene); // Create default resources @@ -508,7 +508,7 @@ void Physics::RemoveJoint(Joint* joint) scene->RemoveJoint(joint); } -PhysicsScene* Physics::FindOrCreateScene(String name) +PhysicsScene* Physics::FindOrCreateScene(const String& name) { auto scene = FindScene(name); @@ -516,14 +516,14 @@ PhysicsScene* Physics::FindOrCreateScene(String name) { auto& settings = *PhysicsSettings::Get(); - scene = new PhysicsScene(name, settings); + scene = New(name, settings); Scenes.Add(scene); } return scene; } -PhysicsScene* Physics::FindScene(String name) +PhysicsScene* Physics::FindScene(const String& name) { for (auto scene : Scenes) { diff --git a/Source/Engine/Physics/Physics.h b/Source/Engine/Physics/Physics.h index 2cbc0a4ec..903737c08 100644 --- a/Source/Engine/Physics/Physics.h +++ b/Source/Engine/Physics/Physics.h @@ -116,12 +116,12 @@ public: /// /// Finds an existing or creates it if it does not exist. /// - API_FUNCTION() static PhysicsScene* FindOrCreateScene(String name); + API_FUNCTION() static PhysicsScene* FindOrCreateScene(const String& name); /// ///Finds an existing scene. /// - API_FUNCTION() static PhysicsScene* FindScene(String name); + API_FUNCTION() static PhysicsScene* FindScene(const String& name); public: /// diff --git a/Source/Engine/Physics/PhysicsScene.cpp b/Source/Engine/Physics/PhysicsScene.cpp index bcb4b6daa..fcc935b51 100644 --- a/Source/Engine/Physics/PhysicsScene.cpp +++ b/Source/Engine/Physics/PhysicsScene.cpp @@ -128,7 +128,7 @@ PhysicsScene::PhysicsScene(const String& name, const PhysicsSettings& settings) #define CHECK_INIT(value, msg) if(!value) { LOG(Error, msg); return; } mName = name; - mPhysxImpl = new PhysicsScenePhysX(); + mPhysxImpl = New(); // Create scene description PxSceneDesc sceneDesc(CPhysX->getTolerancesScale()); diff --git a/Source/Engine/Physics/PhysicsScene.h b/Source/Engine/Physics/PhysicsScene.h index 4993f216e..fa0e0303d 100644 --- a/Source/Engine/Physics/PhysicsScene.h +++ b/Source/Engine/Physics/PhysicsScene.h @@ -11,7 +11,6 @@ class WheeledVehicle; #endif - struct ActionData; class FixedStepper; class PhysicsSettings; From 2674086e0cfe3f10c4e4c1d56c5f5ede562306ca Mon Sep 17 00:00:00 2001 From: Iain Mckay Date: Wed, 15 Dec 2021 16:56:39 +0100 Subject: [PATCH 5/7] Code review changes --- Source/Engine/Level/Level.cpp | 5 ----- Source/Engine/Physics/Physics.cpp | 12 ------------ Source/Engine/Physics/Physics.h | 7 +------ 3 files changed, 1 insertion(+), 23 deletions(-) diff --git a/Source/Engine/Level/Level.cpp b/Source/Engine/Level/Level.cpp index 2073222fa..454939103 100644 --- a/Source/Engine/Level/Level.cpp +++ b/Source/Engine/Level/Level.cpp @@ -22,7 +22,6 @@ #include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Scripting/Script.h" #include "Engine/Engine/Time.h" -#include "Engine/Physics/Physics.h" #include "Engine/Scripting/ManagedCLR/MAssembly.h" #include "Engine/Scripting/ManagedCLR/MClass.h" #include "Engine/Scripting/ManagedCLR/MDomain.h" @@ -779,8 +778,6 @@ bool LevelImpl::unloadScene(Scene* scene) // Force flush deleted objects so we actually delete unloaded scene objects (prevent from reloading their managed objects, etc.) ObjectsRemovalService::Flush(); - Physics::EndPlay(); - return false; } @@ -1031,8 +1028,6 @@ bool Level::loadScene(rapidjson_flax::Value& data, int32 engineBuild, Scene** ou { PROFILE_CPU_NAMED("BeginPlay"); - Physics::BeginPlay(); - ScopeLock lock(ScenesLock); Scenes.Add(scene); SceneBeginData beginData; diff --git a/Source/Engine/Physics/Physics.cpp b/Source/Engine/Physics/Physics.cpp index e51d0c6d8..6198accfc 100644 --- a/Source/Engine/Physics/Physics.cpp +++ b/Source/Engine/Physics/Physics.cpp @@ -324,7 +324,6 @@ bool PhysicsService::Init() #endif Physics::DefaultScene = Physics::FindOrCreateScene(TEXT("Default")); - Physics::Scenes.Add(Physics::DefaultScene); // Create default resources DefaultMaterial = CPhysX->createMaterial(0.7f, 0.7f, 0.3f); @@ -533,14 +532,3 @@ PhysicsScene* Physics::FindScene(const String& name) return nullptr; } - -void Physics::BeginPlay() -{ - reset(); -} - - -void Physics::EndPlay() -{ - reset(); -} diff --git a/Source/Engine/Physics/Physics.h b/Source/Engine/Physics/Physics.h index 903737c08..f2317288a 100644 --- a/Source/Engine/Physics/Physics.h +++ b/Source/Engine/Physics/Physics.h @@ -2,7 +2,6 @@ #pragma once -#include "Engine/Core/Collections/Array.h" #include "Engine/Core/Math/Vector2.h" #include "Engine/Core/Math/Vector3.h" #include "Engine/Core/Math/Quaternion.h" @@ -111,7 +110,7 @@ public: /// List with all physics scenes (readonly). /// API_FIELD(ReadOnly) - static Array Scenes; + static Array Scenes; /// /// Finds an existing or creates it if it does not exist. @@ -590,8 +589,4 @@ public: /// /// The joint. static void RemoveJoint(Joint* joint); - -public: - static void BeginPlay(); - static void EndPlay(); }; From a5884a2e07b85d669d58f1862dce871192302ede Mon Sep 17 00:00:00 2001 From: Iain Mckay Date: Wed, 15 Dec 2021 20:52:58 +0100 Subject: [PATCH 6/7] Fixing Linux build --- Source/Engine/Physics/PhysicsScene.cpp | 51 ++++++++++++++------------ Source/Engine/Physics/PhysicsScene.h | 6 +-- 2 files changed, 28 insertions(+), 29 deletions(-) diff --git a/Source/Engine/Physics/PhysicsScene.cpp b/Source/Engine/Physics/PhysicsScene.cpp index fcc935b51..7b5520c0a 100644 --- a/Source/Engine/Physics/PhysicsScene.cpp +++ b/Source/Engine/Physics/PhysicsScene.cpp @@ -2,6 +2,7 @@ #include "PhysicsScene.h" #include "PhysicsSettings.h" #include "PhysicsStepper.h" +#include "SimulationEventCallback.h" #include "Utilities.h" #include "Actors/IPhysicsActor.h" @@ -101,6 +102,8 @@ private: PxCpuDispatcher* CpuDispatcher; PxControllerManager* ControllerManager; PxSimulationFilterShader PhysXDefaultFilterShader = PxDefaultSimulationFilterShader; + SimulationEventCallback EventsCallback; + CriticalSection FlushLocker; Array NewActors; Array DeadActors; @@ -138,7 +141,7 @@ PhysicsScene::PhysicsScene(const String& name, const PhysicsSettings& settings) sceneDesc.flags |= PxSceneFlag::eENABLE_CCD; if (settings.EnableAdaptiveForce) sceneDesc.flags |= PxSceneFlag::eADAPTIVE_FORCE; - sceneDesc.simulationEventCallback = &mEventsCallback; + sceneDesc.simulationEventCallback = &mPhysxImpl->EventsCallback; sceneDesc.filterShader = FilterShader; sceneDesc.bounceThresholdVelocity = settings.BounceThresholdVelocity; if (sceneDesc.cpuDispatcher == nullptr) @@ -284,7 +287,7 @@ void PhysicsScene::Simulate(float dt) mIsDuringSimulation = true; if (mStepper->advance(mPhysxImpl->Scene, dt, mScratchMemory, SCRATCH_BLOCK_SIZE) == false) return; - mEventsCallback.Clear(); + mPhysxImpl->EventsCallback.Clear(); mLastDeltaTime = dt; // TODO: move this call after rendering done @@ -618,10 +621,10 @@ void PhysicsScene::CollectResults() { PROFILE_CPU_NAMED("Physics.SendEvents"); - mEventsCallback.CollectResults(); - mEventsCallback.SendTriggerEvents(); - mEventsCallback.SendCollisionEvents(); - mEventsCallback.SendJointEvents(); + mPhysxImpl->EventsCallback.CollectResults(); + mPhysxImpl->EventsCallback.SendTriggerEvents(); + mPhysxImpl->EventsCallback.SendCollisionEvents(); + mPhysxImpl->EventsCallback.SendJointEvents(); } // End @@ -635,7 +638,7 @@ void PhysicsScene::FlushRequests() PROFILE_CPU(); - mFlushLocker.Lock(); + mPhysxImpl->FlushLocker.Lock(); // Note: this does not handle case when actor is removed and added to the scene at the same time @@ -671,7 +674,7 @@ void PhysicsScene::FlushRequests() { for (int32 i = 0; i < mPhysxImpl->DeadColliders.Count(); i++) { - mEventsCallback.OnColliderRemoved(mPhysxImpl->DeadColliders[i]); + mPhysxImpl->EventsCallback.OnColliderRemoved(mPhysxImpl->DeadColliders[i]); } mPhysxImpl->DeadColliders.Clear(); } @@ -680,7 +683,7 @@ void PhysicsScene::FlushRequests() { for (int32 i = 0; i < mPhysxImpl->DeadJoints.Count(); i++) { - mEventsCallback.OnJointRemoved(mPhysxImpl->DeadJoints[i]); + mPhysxImpl->EventsCallback.OnJointRemoved(mPhysxImpl->DeadJoints[i]); } mPhysxImpl->DeadJoints.Clear(); } @@ -702,32 +705,32 @@ void PhysicsScene::FlushRequests() } mPhysxImpl->DeadObjects.Clear(); - mFlushLocker.Unlock(); + mPhysxImpl->FlushLocker.Unlock(); } void PhysicsScene::RemoveMaterial(PxMaterial* material) { ASSERT(material); - mFlushLocker.Lock(); + mPhysxImpl->FlushLocker.Lock(); mPhysxImpl->DeadMaterials.Add(material); - mFlushLocker.Unlock(); + mPhysxImpl->FlushLocker.Unlock(); } void PhysicsScene::RemoveObject(PxBase* obj) { ASSERT(obj); - mFlushLocker.Lock(); + mPhysxImpl->FlushLocker.Lock(); mPhysxImpl->DeadObjects.Add(obj); - mFlushLocker.Unlock(); + mPhysxImpl->FlushLocker.Unlock(); } void PhysicsScene::AddActor(PxActor* actor) { ASSERT(actor); - mFlushLocker.Lock(); + mPhysxImpl->FlushLocker.Lock(); if (IsInMainThread()) { GetScene()->addActor(*actor); @@ -736,14 +739,14 @@ void PhysicsScene::AddActor(PxActor* actor) { mPhysxImpl->NewActors.Add(actor); } - mFlushLocker.Unlock(); + mPhysxImpl->FlushLocker.Unlock(); } void PhysicsScene::AddActor(PxRigidDynamic* actor, bool putToSleep) { ASSERT(actor); - mFlushLocker.Lock(); + mPhysxImpl->FlushLocker.Lock(); if (IsInMainThread()) { GetScene()->addActor(*actor); @@ -756,7 +759,7 @@ void PhysicsScene::AddActor(PxRigidDynamic* actor, bool putToSleep) if (putToSleep) mPhysxImpl->Actions.Add({ ActionType::Sleep, actor }); } - mFlushLocker.Unlock(); + mPhysxImpl->FlushLocker.Unlock(); } void PhysicsScene::UnlinkActor(PxActor* actor) @@ -774,27 +777,27 @@ void PhysicsScene::RemoveActor(PxActor* actor) // Unlink ref to flax object actor->userData = nullptr; - mFlushLocker.Lock(); + mPhysxImpl->FlushLocker.Lock(); mPhysxImpl->DeadActors.Add(actor); - mFlushLocker.Unlock(); + mPhysxImpl->FlushLocker.Unlock(); } void PhysicsScene::RemoveCollider(PhysicsColliderActor* collider) { ASSERT(collider); - mFlushLocker.Lock(); + mPhysxImpl->FlushLocker.Lock(); mPhysxImpl->DeadColliders.Add(collider); - mFlushLocker.Unlock(); + mPhysxImpl->FlushLocker.Unlock(); } void PhysicsScene::RemoveJoint(Joint* joint) { ASSERT(joint); - mFlushLocker.Lock(); + mPhysxImpl->FlushLocker.Lock(); mPhysxImpl->DeadJoints.Add(joint); - mFlushLocker.Unlock(); + mPhysxImpl->FlushLocker.Unlock(); } PxControllerManager* PhysicsScene::GetControllerManager() diff --git a/Source/Engine/Physics/PhysicsScene.h b/Source/Engine/Physics/PhysicsScene.h index fa0e0303d..b4a3c7d8d 100644 --- a/Source/Engine/Physics/PhysicsScene.h +++ b/Source/Engine/Physics/PhysicsScene.h @@ -1,6 +1,5 @@ #pragma once -#include "Engine/Physics/SimulationEventCallback.h" #include "Engine/Scripting/ScriptingObject.h" #include "Engine/Scripting/ScriptingType.h" #include "Engine/Core/Math/Vector3.h" @@ -12,6 +11,7 @@ class WheeledVehicle; #endif struct ActionData; +struct RayCastHit; class FixedStepper; class PhysicsSettings; class PhysicsColliderActor; @@ -519,14 +519,10 @@ public: private: String mName; bool mAutoSimulation = true; - SimulationEventCallback mEventsCallback; - void* mScratchMemory = nullptr; FixedStepper* mStepper = nullptr; float mLastDeltaTime = 0.0f; bool mIsDuringSimulation = false; - CriticalSection mFlushLocker; - PhysicsScenePhysX* mPhysxImpl; }; From 498040765b02f64d708cec7c904b177677ad64d1 Mon Sep 17 00:00:00 2001 From: Iain Mckay Date: Wed, 5 Jan 2022 14:33:25 +0100 Subject: [PATCH 7/7] Fixing conflicts --- .../Physics/Colliders/CharacterController.cpp | 2 +- .../Engine/Physics/PhysicsScene.Queries.cpp | 52 +++++++++++++++++++ Source/Engine/Physics/PhysicsScene.h | 5 ++ 3 files changed, 58 insertions(+), 1 deletion(-) diff --git a/Source/Engine/Physics/Colliders/CharacterController.cpp b/Source/Engine/Physics/Colliders/CharacterController.cpp index 40d5c23c2..52324c98b 100644 --- a/Source/Engine/Physics/Colliders/CharacterController.cpp +++ b/Source/Engine/Physics/Colliders/CharacterController.cpp @@ -173,7 +173,7 @@ CharacterController::CollisionFlags CharacterController::Move(const Vector3& dis filters.mFilterData = (PxFilterData*)&_filterData; 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; diff --git a/Source/Engine/Physics/PhysicsScene.Queries.cpp b/Source/Engine/Physics/PhysicsScene.Queries.cpp index 790e98382..87fa78529 100644 --- a/Source/Engine/Physics/PhysicsScene.Queries.cpp +++ b/Source/Engine/Physics/PhysicsScene.Queries.cpp @@ -218,6 +218,52 @@ public: } }; +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; @@ -230,6 +276,12 @@ PxQueryFilterCallback* PhysicsScene::GetCharacterQueryFilterCallback() 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 diff --git a/Source/Engine/Physics/PhysicsScene.h b/Source/Engine/Physics/PhysicsScene.h index b4a3c7d8d..1c95e7ece 100644 --- a/Source/Engine/Physics/PhysicsScene.h +++ b/Source/Engine/Physics/PhysicsScene.h @@ -165,6 +165,11 @@ public: /// 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. ///