Adds feature for creating multiple physics scenes
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
/// <returns>The scene rendering interface.</returns>
|
||||
SceneRendering* GetSceneRendering() const;
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Set the physics world the controller is part of.
|
||||
/// </summary>
|
||||
API_PROPERTY(Attributes="HideInEditor") void SetPhysicsScene(PhysicsScene* scene);
|
||||
|
||||
/// <summary>
|
||||
/// Get the physics world the controller is part of.
|
||||
/// </summary>
|
||||
API_PROPERTY(Attributes="HideInEditor") PhysicsScene* GetPhysicsScene();
|
||||
|
||||
protected:
|
||||
virtual void OnPhysicsSceneChanged(PhysicsScene* previous) {};
|
||||
|
||||
private:
|
||||
|
||||
void SetSceneInHierarchy(Scene* scene);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
#include "Engine/Physics/Types.h"
|
||||
#include "IPhysicsActor.h"
|
||||
|
||||
class PhysicsScene;
|
||||
|
||||
/// <summary>
|
||||
/// A base class for all physical actors.
|
||||
/// </summary>
|
||||
|
||||
@@ -2,10 +2,14 @@
|
||||
|
||||
#include "RigidBody.h"
|
||||
#include "PxMaterial.h"
|
||||
#include <iterator>
|
||||
|
||||
#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 <ThirdParty/PhysX/extensions/PxRigidBodyExt.h>
|
||||
#include <ThirdParty/PhysX/PxRigidActor.h>
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -572,4 +572,5 @@ protected:
|
||||
void EndPlay() override;
|
||||
void OnActiveInTreeChanged() override;
|
||||
void OnTransformChanged() override;
|
||||
void OnPhysicsSceneChanged(PhysicsScene* previous) override;
|
||||
};
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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<WheeledVehicle*> 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;
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#include "Engine/Physics/Colliders/Collider.h"
|
||||
#include "Engine/Scripting/ScriptingObjectReference.h"
|
||||
|
||||
class Physics;
|
||||
class PhysicsScene;
|
||||
|
||||
/// <summary>
|
||||
/// 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;
|
||||
/// <seealso cref="RigidBody" />
|
||||
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;
|
||||
|
||||
@@ -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 <ThirdParty/PhysX/PxRigidActor.h>
|
||||
#include <ThirdParty/PhysX/PxRigidDynamic.h>
|
||||
#include <ThirdParty/PhysX/PxPhysics.h>
|
||||
#include <ThirdParty/PhysX/characterkinematic/PxController.h>
|
||||
#include <ThirdParty/PhysX/characterkinematic/PxControllerManager.h>
|
||||
#include <ThirdParty/PhysX/characterkinematic//PxCapsuleController.h>
|
||||
@@ -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
|
||||
|
||||
@@ -237,4 +237,5 @@ protected:
|
||||
void OnDisable() override;
|
||||
void OnParentChanged() override;
|
||||
void OnTransformChanged() override;
|
||||
void OnPhysicsSceneChanged(PhysicsScene* previous) override;
|
||||
};
|
||||
|
||||
@@ -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 <ThirdParty/PhysX/geometry/PxGeometryQuery.h>
|
||||
#include <ThirdParty/PhysX/PxShape.h>
|
||||
@@ -17,7 +18,6 @@
|
||||
#include <ThirdParty/PhysX/PxFiltering.h>
|
||||
#include <ThirdParty/PhysX/PxRigidDynamic.h>
|
||||
#include <ThirdParty/PhysX/PxRigidStatic.h>
|
||||
#include <ThirdParty/PhysX/PxScene.h>
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -232,4 +232,6 @@ protected:
|
||||
void OnParentChanged() override;
|
||||
void OnTransformChanged() override;
|
||||
void OnLayerChanged() override;
|
||||
|
||||
void OnPhysicsSceneChanged(PhysicsScene* previous) override;
|
||||
};
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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 <ThirdParty/PhysX/PxPhysics.h>
|
||||
#include <ThirdParty/PhysX/extensions/PxDefaultStreams.h>
|
||||
#endif
|
||||
|
||||
#include <ThirdParty/PhysX/geometry/PxTriangleMesh.h>
|
||||
|
||||
SplineCollider::SplineCollider(const SpawnParams& params)
|
||||
@@ -45,7 +47,7 @@ void SplineCollider::ExtractGeometry(Array<Vector3>& vertexBuffer, Array<int32>&
|
||||
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());
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -3,696 +3,140 @@
|
||||
#include "Physics.h"
|
||||
#include "Utilities.h"
|
||||
#include "CollisionData.h"
|
||||
#include "PhysicsScene.h"
|
||||
#include "Actors/PhysicsColliderActor.h"
|
||||
#include <ThirdParty/PhysX/PxScene.h>
|
||||
#include <ThirdParty/PhysX/PxQueryFiltering.h>
|
||||
#include <ThirdParty/PhysX/PxRigidDynamic.h>
|
||||
#include <ThirdParty/PhysX/characterkinematic/PxController.h>
|
||||
|
||||
// Temporary result buffer size
|
||||
#define HIT_BUFFER_SIZE 128
|
||||
|
||||
template<typename HitType>
|
||||
class DynamicHitBuffer : public PxHitCallback<HitType>
|
||||
{
|
||||
private:
|
||||
|
||||
uint32 _count;
|
||||
HitType _buffer[HIT_BUFFER_SIZE];
|
||||
|
||||
public:
|
||||
|
||||
DynamicHitBuffer()
|
||||
: PxHitCallback<HitType>(_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<PxSweepHit> buffer
|
||||
|
||||
#define SCENE_QUERY_SETUP_OVERLAP_1() SCENE_QUERY_SETUP(false); \
|
||||
PxOverlapBufferN<1> buffer
|
||||
|
||||
#define SCENE_QUERY_SETUP_OVERLAP() SCENE_QUERY_SETUP(false); \
|
||||
DynamicHitBuffer<PxOverlapHit> 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<PhysicsColliderActor*>(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<PhysicsColliderActor*>(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<RayCastHit>& results, const float maxDistance, uint32 layerMask, bool hitTriggers)
|
||||
{
|
||||
// Prepare data
|
||||
SCENE_QUERY_SETUP(false);
|
||||
DynamicHitBuffer<PxRaycastHit> 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<RayCastHit>& 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<RayCastHit>& 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<RayCastHit>& 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<RayCastHit>& 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<Collider*>& 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<Collider*>& 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<Collider*>& 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<Collider*>& 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<PhysicsColliderActor*>& 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<PhysicsColliderActor*>& 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<PhysicsColliderActor*>& 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<PhysicsColliderActor*>& 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);
|
||||
}
|
||||
|
||||
@@ -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 <ThirdParty/PhysX/PxPhysicsAPI.h>
|
||||
#include <ThirdParty/PhysX/PxActor.h>
|
||||
#if WITH_VEHICLE
|
||||
#include <ThirdParty/PhysX/vehicle/PxVehicleUpdate.h>
|
||||
#endif
|
||||
#if WITH_PVD
|
||||
#include <ThirdParty/PhysX/pvd/PxPvd.h>
|
||||
#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<PxActor*> NewActors;
|
||||
Array<ActionData> Actions;
|
||||
Array<PxActor*> DeadActors;
|
||||
Array<PxMaterial*> DeadMaterials;
|
||||
Array<PxBase*> _deadObjects;
|
||||
Array<PhysicsColliderActor*> DeadColliders;
|
||||
Array<Joint*> 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<PxVehicleWheels*> WheelVehiclesCache;
|
||||
Array<PxRaycastQueryResult> WheelQueryResults;
|
||||
Array<PxRaycastHit> WheelHitResults;
|
||||
Array<PxWheelQueryResult> WheelVehiclesResultsPerWheel;
|
||||
Array<PxVehicleWheelQueryResult> WheelVehiclesResultsPerVehicle;
|
||||
PxBatchQuery* WheelRaycastBatchQuery = nullptr;
|
||||
PxVehicleDrivableSurfaceToTireFrictionPairs* WheelTireFrictions = nullptr;
|
||||
#endif
|
||||
|
||||
void reset()
|
||||
{
|
||||
Physics::Scenes.Resize(1);
|
||||
}
|
||||
}
|
||||
|
||||
#if WITH_VEHICLE
|
||||
Array<WheeledVehicle*> WheelVehicles;
|
||||
#endif
|
||||
bool Physics::AutoSimulation = true;
|
||||
PhysicsScene* Physics::DefaultScene = nullptr;
|
||||
Array<PhysicsScene*> 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<uint32>(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<FixedStepper>();
|
||||
}
|
||||
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<PhysicsColliderActor*>(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<IPhysicsActor*>((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<PxRigidDynamic*>(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();
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
/// <summary>
|
||||
/// Gets PhysX scene object
|
||||
/// </summary>
|
||||
static PxScene* GetScene();
|
||||
|
||||
/// <summary>
|
||||
/// Gets PhysX characters controller manager object
|
||||
/// </summary>
|
||||
static PxControllerManager* GetControllerManager();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the physics tolerances scale.
|
||||
/// </summary>
|
||||
static PxTolerancesScale* GetTolerancesScale();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the default query filter callback used for the scene queries.
|
||||
/// </summary>
|
||||
static PxQueryFilterCallback* GetQueryFilterCallback();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the default query filter callback used for the character controller collisions detection.
|
||||
/// </summary>
|
||||
static PxQueryFilterCallback* GetCharacterQueryFilterCallback();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the default controller filter callback used for the character controller collisions detection.
|
||||
/// </summary>
|
||||
static PxControllerFilterCallback* GetCharacterControllerFilterCallback();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the default physical material.
|
||||
/// </summary>
|
||||
static PxMaterial* GetDefaultMaterial();
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// The default physics scene.
|
||||
/// </summary>
|
||||
API_FIELD(ReadOnly)
|
||||
static PhysicsScene* DefaultScene;
|
||||
|
||||
/// <summary>
|
||||
/// List with all physics scenes (readonly).
|
||||
/// </summary>
|
||||
API_FIELD(ReadOnly)
|
||||
static Array<PhysicsScene*> Scenes;
|
||||
|
||||
/// <summary>
|
||||
/// Finds an existing <see cref="PhysicsScene"/> or creates it if it does not exist.
|
||||
/// </summary>
|
||||
API_FUNCTION() static PhysicsScene* FindOrCreateScene(String name);
|
||||
|
||||
/// <summary>
|
||||
///Finds an existing scene.
|
||||
/// </summary>
|
||||
API_FUNCTION() static PhysicsScene* FindScene(String name);
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// The automatic simulation feature. True if perform physics simulation after on fixed update by auto, otherwise user should do it.
|
||||
/// </summary>
|
||||
API_FIELD() static bool AutoSimulation;
|
||||
API_PROPERTY() static bool GetAutoSimulation();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current gravity force.
|
||||
@@ -519,11 +519,22 @@ public:
|
||||
/// <param name="dt">The delta time (in seconds).</param>
|
||||
API_FUNCTION() static void Simulate(float dt);
|
||||
|
||||
/// <summary>
|
||||
/// Called during main engine loop to start physic simulation on all registered scenes.
|
||||
/// </summary>
|
||||
/// <param name="dt">The delta time (in seconds).</param>
|
||||
static void SimulateAll(float dt);
|
||||
|
||||
/// <summary>
|
||||
/// Called during main engine loop to collect physic simulation results and apply them as well as fire collision events.
|
||||
/// </summary>
|
||||
API_FUNCTION() static void CollectResults();
|
||||
|
||||
/// <summary>
|
||||
/// Called during main engine loop to collect physic simulation results on all registered scenes and apply them as well as fire collision events.
|
||||
/// </summary>
|
||||
static void CollectResultsAll();
|
||||
|
||||
/// <summary>
|
||||
/// Checks if physical simulation is running
|
||||
/// </summary>
|
||||
@@ -535,7 +546,7 @@ public:
|
||||
/// <summary>
|
||||
/// Flushes the async requests to add/remove actors, remove materials, etc..
|
||||
/// </summary>
|
||||
static void FlushRequests();
|
||||
static void FlushRequestsAll();
|
||||
|
||||
/// <summary>
|
||||
/// 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).
|
||||
/// </summary>
|
||||
/// <param name="joint">The joint.</param>
|
||||
static void RemoveJoint(Joint* joint);
|
||||
static void RemoveJointAll(Joint* joint);
|
||||
|
||||
public:
|
||||
static void BeginPlay();
|
||||
static void EndPlay();
|
||||
};
|
||||
|
||||
657
Source/Engine/Physics/PhysicsScene.Queries.cpp
Normal file
657
Source/Engine/Physics/PhysicsScene.Queries.cpp
Normal file
@@ -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 <ThirdParty/PhysX/PxPhysicsAPI.h>
|
||||
#include <ThirdParty/PhysX/PxQueryFiltering.h>
|
||||
|
||||
// Temporary result buffer size
|
||||
#define HIT_BUFFER_SIZE 128
|
||||
|
||||
template<typename HitType>
|
||||
class DynamicHitBuffer : public PxHitCallback<HitType>
|
||||
{
|
||||
private:
|
||||
|
||||
uint32 _count;
|
||||
HitType _buffer[HIT_BUFFER_SIZE];
|
||||
|
||||
public:
|
||||
|
||||
DynamicHitBuffer()
|
||||
: PxHitCallback<HitType>(_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<PxSweepHit> buffer
|
||||
|
||||
#define SCENE_QUERY_SETUP_OVERLAP_1() SCENE_QUERY_SETUP(false); \
|
||||
PxOverlapBufferN<1> buffer
|
||||
|
||||
#define SCENE_QUERY_SETUP_OVERLAP() SCENE_QUERY_SETUP(false); \
|
||||
DynamicHitBuffer<PxOverlapHit> 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<PhysicsColliderActor*>(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<PhysicsColliderActor*>(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<RayCastHit>& results, const float maxDistance, uint32 layerMask, bool hitTriggers)
|
||||
{
|
||||
// Prepare data
|
||||
SCENE_QUERY_SETUP(false);
|
||||
DynamicHitBuffer<PxRaycastHit> 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<RayCastHit>& 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<RayCastHit>& 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<RayCastHit>& 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<RayCastHit>& 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<Collider*>& 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<Collider*>& 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<Collider*>& 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<Collider*>& 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<PhysicsColliderActor*>& 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<PhysicsColliderActor*>& 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<PhysicsColliderActor*>& 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<PhysicsColliderActor*>& 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
|
||||
770
Source/Engine/Physics/PhysicsScene.cpp
Normal file
770
Source/Engine/Physics/PhysicsScene.cpp
Normal file
@@ -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 <ThirdParty/PhysX/PxPhysicsAPI.h>
|
||||
|
||||
#if WITH_VEHICLE
|
||||
#include "Actors/WheeledVehicle.h"
|
||||
#include <ThirdParty/PhysX/vehicle/PxVehicleUpdate.h>
|
||||
#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<uint32>(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<FixedStepper>();
|
||||
}
|
||||
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<PhysicsColliderActor*>(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<IPhysicsActor*>((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<PxRigidDynamic*>(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;
|
||||
}
|
||||
560
Source/Engine/Physics/PhysicsScene.h
Normal file
560
Source/Engine/Physics/PhysicsScene.h
Normal file
@@ -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 <ThirdParty/PhysX/PxPhysicsAPI.h>
|
||||
|
||||
#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;
|
||||
|
||||
/// <summary>
|
||||
/// Isolated physics scene.
|
||||
/// </summary>
|
||||
API_CLASS(NoSpawn) class FLAXENGINE_API PhysicsScene : public PersistentScriptingObject
|
||||
{
|
||||
DECLARE_SCRIPTING_TYPE_NO_SPAWN(PhysicsScene);
|
||||
|
||||
explicit PhysicsScene(String name, PhysicsSettings settings, CPUInfo cpuInfo);
|
||||
~PhysicsScene();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the scene.
|
||||
/// </summary>
|
||||
API_PROPERTY() String GetName() const;
|
||||
|
||||
public:
|
||||
String ToString() const override
|
||||
{
|
||||
return GetName();
|
||||
}
|
||||
|
||||
PxScene* GetScene();
|
||||
|
||||
/// <summary>
|
||||
/// The automatic simulation feature. True if perform physics simulation after on fixed update by auto, otherwise user should do it.
|
||||
/// </summary>
|
||||
API_PROPERTY() bool GetAutoSimulation();
|
||||
API_PROPERTY() void SetAutoSimulation(bool value);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the current gravity force.
|
||||
/// </summary>
|
||||
API_PROPERTY() void SetGravity(const Vector3& value);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current gravity force.
|
||||
/// </summary>
|
||||
API_PROPERTY() Vector3 GetGravity();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the CCD feature enable flag.
|
||||
/// </summary>
|
||||
API_PROPERTY() bool GetEnableCCD();
|
||||
|
||||
/// <summary>
|
||||
/// Sets the CCD feature enable flag.
|
||||
/// </summary>
|
||||
API_PROPERTY() void SetEnableCCD(const bool value);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the minimum relative velocity required for an object to bounce.
|
||||
/// </summary>
|
||||
API_PROPERTY() float GetBounceThresholdVelocity();
|
||||
|
||||
/// <summary>
|
||||
/// Sets the minimum relative velocity required for an object to bounce.
|
||||
/// </summary>
|
||||
API_PROPERTY() void SetBounceThresholdVelocity(const float value);
|
||||
|
||||
/// <summary>
|
||||
/// Called during main engine loop to start physic simulation. Use CollectResults after.
|
||||
/// </summary>
|
||||
/// <param name="dt">The delta time (in seconds).</param>
|
||||
API_FUNCTION() void Simulate(float dt);
|
||||
|
||||
/// <summary>
|
||||
/// Checks if physical simulation is running
|
||||
/// </summary>
|
||||
/// <returns>True if simulation is active, otherwise false</returns>
|
||||
API_PROPERTY() bool IsDuringSimulation();
|
||||
|
||||
/// <summary>
|
||||
/// Called to collect physic simulation results and apply them as well as fire collision events.
|
||||
/// </summary>
|
||||
API_FUNCTION() void CollectResults();
|
||||
|
||||
/// <summary>
|
||||
/// Flushes the async requests to add/remove actors, remove materials, etc..
|
||||
/// </summary>
|
||||
void FlushRequests();
|
||||
|
||||
/// <summary>
|
||||
/// Removes the material (using safe async request).
|
||||
/// </summary>
|
||||
/// <param name="material">The material.</param>
|
||||
void RemoveMaterial(PxMaterial* material);
|
||||
|
||||
/// <summary>
|
||||
/// Removes the physX object via calling release() on it (using safe async request).
|
||||
/// </summary>
|
||||
/// <param name="obj">The obj.</param>
|
||||
void RemoveObject(PxBase* obj);
|
||||
|
||||
/// <summary>
|
||||
/// Adds the actor (using safe async request).
|
||||
/// </summary>
|
||||
/// <param name="actor">The actor.</param>
|
||||
void AddActor(PxActor* actor);
|
||||
|
||||
/// <summary>
|
||||
/// Adds the actor (using safe async request).
|
||||
/// </summary>
|
||||
/// <param name="actor">The actor.</param>
|
||||
/// <param name="putToSleep">If set to <c>true</c> will put actor to sleep after spawning.</param>
|
||||
void AddActor(PxRigidDynamic* actor, bool putToSleep = false);
|
||||
|
||||
/// <summary>
|
||||
/// Removes the actor (using safe async request).
|
||||
/// </summary>
|
||||
/// <param name="actor">The actor.</param>
|
||||
void RemoveActor(PxActor* actor);
|
||||
|
||||
/// <summary>
|
||||
/// Removes the actor from the underlying physics scene without destroying it.
|
||||
/// </summary>
|
||||
void UnlinkActor(PxActor* actor);
|
||||
|
||||
/// <summary>
|
||||
/// Marks that collider has been removed (all collision events should be cleared to prevent leaks of using removed object).
|
||||
/// </summary>
|
||||
/// <param name="collider">The collider.</param>
|
||||
void RemoveCollider(PhysicsColliderActor* collider);
|
||||
|
||||
/// <summary>
|
||||
/// Marks that joint has been removed (all collision events should be cleared to prevent leaks of using removed object).
|
||||
/// </summary>
|
||||
/// <param name="joint">The joint.</param>
|
||||
void RemoveJoint(Joint* joint);
|
||||
|
||||
/// <summary>
|
||||
/// Gets PhysX characters controller manager object
|
||||
/// </summary>
|
||||
PxControllerManager* GetControllerManager();
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Gets the default query filter callback used for the scene queries.
|
||||
/// </summary>
|
||||
PxQueryFilterCallback* GetQueryFilterCallback();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the default query filter callback used for the character controller collisions detection.
|
||||
/// </summary>
|
||||
PxQueryFilterCallback* GetCharacterQueryFilterCallback();
|
||||
|
||||
/// <summary>
|
||||
/// Performs a raycast against objects in the scene.
|
||||
/// </summary>
|
||||
/// <param name="origin">The origin of the ray.</param>
|
||||
/// <param name="direction">The normalized direction of the ray.</param>
|
||||
/// <param name="maxDistance">The maximum distance the ray should check for collisions.</param>
|
||||
/// <param name="layerMask">The layer mask used to filter the results.</param>
|
||||
/// <param name="hitTriggers">If set to <c>true</c> triggers will be hit, otherwise will skip them.</param>
|
||||
/// <returns>True if ray hits an matching object, otherwise false.</returns>
|
||||
API_FUNCTION() bool RayCast(const Vector3& origin, const Vector3& direction, float maxDistance = MAX_float, uint32 layerMask = MAX_uint32, bool hitTriggers = true);
|
||||
|
||||
/// <summary>
|
||||
/// Performs a raycast against objects in the scene, returns results in a RayCastHit structure.
|
||||
/// </summary>
|
||||
/// <param name="origin">The origin of the ray.</param>
|
||||
/// <param name="direction">The normalized direction of the ray.</param>
|
||||
/// <param name="hitInfo">The result hit information. Valid only when method returns true.</param>
|
||||
/// <param name="maxDistance">The maximum distance the ray should check for collisions.</param>
|
||||
/// <param name="layerMask">The layer mask used to filter the results.</param>
|
||||
/// <param name="hitTriggers">If set to <c>true</c> triggers will be hit, otherwise will skip them.</param>
|
||||
/// <returns>True if ray hits an matching object, otherwise false.</returns>
|
||||
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);
|
||||
|
||||
/// <summary>
|
||||
/// Performs a raycast against objects in the scene, returns results in a RayCastHit structure.
|
||||
/// </summary>
|
||||
/// <param name="origin">The origin of the ray.</param>
|
||||
/// <param name="direction">The normalized direction of the ray.</param>
|
||||
/// <param name="results">The result hits. Valid only when method returns true.</param>
|
||||
/// <param name="maxDistance">The maximum distance the ray should check for collisions.</param>
|
||||
/// <param name="layerMask">The layer mask used to filter the results.</param>
|
||||
/// <param name="hitTriggers">If set to <c>true</c> triggers will be hit, otherwise will skip them.</param>
|
||||
/// <returns>True if ray hits an matching object, otherwise false.</returns>
|
||||
API_FUNCTION() bool RayCastAll(const Vector3& origin, const Vector3& direction, API_PARAM(Out) Array<RayCastHit, HeapAllocation>& results, float maxDistance = MAX_float, uint32 layerMask = MAX_uint32, bool hitTriggers = true);
|
||||
|
||||
/// <summary>
|
||||
/// Performs a sweep test against objects in the scene using a box geometry.
|
||||
/// </summary>
|
||||
/// <param name="center">The box center.</param>
|
||||
/// <param name="halfExtents">The half size of the box in each direction.</param>
|
||||
/// <param name="direction">The normalized direction in which cast a box.</param>
|
||||
/// <param name="rotation">The box rotation.</param>
|
||||
/// <param name="maxDistance">The maximum distance the ray should check for collisions.</param>
|
||||
/// <param name="layerMask">The layer mask used to filter the results.</param>
|
||||
/// <param name="hitTriggers">If set to <c>true</c> triggers will be hit, otherwise will skip them.</param>
|
||||
/// <returns>True if box hits an matching object, otherwise false.</returns>
|
||||
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);
|
||||
|
||||
/// <summary>
|
||||
/// Performs a sweep test against objects in the scene using a box geometry.
|
||||
/// </summary>
|
||||
/// <param name="center">The box center.</param>
|
||||
/// <param name="halfExtents">The half size of the box in each direction.</param>
|
||||
/// <param name="direction">The normalized direction in which cast a box.</param>
|
||||
/// <param name="hitInfo">The result hit information. Valid only when method returns true.</param>
|
||||
/// <param name="rotation">The box rotation.</param>
|
||||
/// <param name="maxDistance">The maximum distance the ray should check for collisions.</param>
|
||||
/// <param name="layerMask">The layer mask used to filter the results.</param>
|
||||
/// <param name="hitTriggers">If set to <c>true</c> triggers will be hit, otherwise will skip them.</param>
|
||||
/// <returns>True if box hits an matching object, otherwise false.</returns>
|
||||
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);
|
||||
|
||||
/// <summary>
|
||||
/// Performs a sweep test against objects in the scene using a box geometry.
|
||||
/// </summary>
|
||||
/// <param name="center">The box center.</param>
|
||||
/// <param name="halfExtents">The half size of the box in each direction.</param>
|
||||
/// <param name="direction">The normalized direction in which cast a box.</param>
|
||||
/// <param name="results">The result hits. Valid only when method returns true.</param>
|
||||
/// <param name="rotation">The box rotation.</param>
|
||||
/// <param name="maxDistance">The maximum distance the ray should check for collisions.</param>
|
||||
/// <param name="layerMask">The layer mask used to filter the results.</param>
|
||||
/// <param name="hitTriggers">If set to <c>true</c> triggers will be hit, otherwise will skip them.</param>
|
||||
/// <returns>True if box hits an matching object, otherwise false.</returns>
|
||||
API_FUNCTION() bool BoxCastAll(const Vector3& center, const Vector3& halfExtents, const Vector3& direction, API_PARAM(Out) Array<RayCastHit, HeapAllocation>& results, const Quaternion& rotation = Quaternion::Identity, float maxDistance = MAX_float, uint32 layerMask = MAX_uint32, bool hitTriggers = true);
|
||||
|
||||
/// <summary>
|
||||
/// Performs a sweep test against objects in the scene using a sphere geometry.
|
||||
/// </summary>
|
||||
/// <param name="center">The sphere center.</param>
|
||||
/// <param name="radius">The radius of the sphere.</param>
|
||||
/// <param name="direction">The normalized direction in which cast a sphere.</param>
|
||||
/// <param name="maxDistance">The maximum distance the ray should check for collisions.</param>
|
||||
/// <param name="layerMask">The layer mask used to filter the results.</param>
|
||||
/// <param name="hitTriggers">If set to <c>true</c> triggers will be hit, otherwise will skip them.</param>
|
||||
/// <returns>True if sphere hits an matching object, otherwise false.</returns>
|
||||
API_FUNCTION() bool SphereCast(const Vector3& center, float radius, const Vector3& direction, float maxDistance = MAX_float, uint32 layerMask = MAX_uint32, bool hitTriggers = true);
|
||||
|
||||
/// <summary>
|
||||
/// Performs a sweep test against objects in the scene using a sphere geometry.
|
||||
/// </summary>
|
||||
/// <param name="center">The sphere center.</param>
|
||||
/// <param name="radius">The radius of the sphere.</param>
|
||||
/// <param name="direction">The normalized direction in which cast a sphere.</param>
|
||||
/// <param name="hitInfo">The result hit information. Valid only when method returns true.</param>
|
||||
/// <param name="maxDistance">The maximum distance the ray should check for collisions.</param>
|
||||
/// <param name="layerMask">The layer mask used to filter the results.</param>
|
||||
/// <param name="hitTriggers">If set to <c>true</c> triggers will be hit, otherwise will skip them.</param>
|
||||
/// <returns>True if sphere hits an matching object, otherwise false.</returns>
|
||||
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);
|
||||
|
||||
/// <summary>
|
||||
/// Performs a sweep test against objects in the scene using a sphere geometry.
|
||||
/// </summary>
|
||||
/// <param name="center">The sphere center.</param>
|
||||
/// <param name="radius">The radius of the sphere.</param>
|
||||
/// <param name="direction">The normalized direction in which cast a sphere.</param>
|
||||
/// <param name="results">The result hits. Valid only when method returns true.</param>
|
||||
/// <param name="maxDistance">The maximum distance the ray should check for collisions.</param>
|
||||
/// <param name="layerMask">The layer mask used to filter the results.</param>
|
||||
/// <param name="hitTriggers">If set to <c>true</c> triggers will be hit, otherwise will skip them.</param>
|
||||
/// <returns>True if sphere hits an matching object, otherwise false.</returns>
|
||||
API_FUNCTION() bool SphereCastAll(const Vector3& center, float radius, const Vector3& direction, API_PARAM(Out) Array<RayCastHit, HeapAllocation>& results, float maxDistance = MAX_float, uint32 layerMask = MAX_uint32, bool hitTriggers = true);
|
||||
|
||||
/// <summary>
|
||||
/// Performs a sweep test against objects in the scene using a capsule geometry.
|
||||
/// </summary>
|
||||
/// <param name="center">The capsule center.</param>
|
||||
/// <param name="radius">The radius of the capsule.</param>
|
||||
/// <param name="height">The height of the capsule, excluding the top and bottom spheres.</param>
|
||||
/// <param name="direction">The normalized direction in which cast a capsule.</param>
|
||||
/// <param name="rotation">The capsule rotation.</param>
|
||||
/// <param name="maxDistance">The maximum distance the ray should check for collisions.</param>
|
||||
/// <param name="layerMask">The layer mask used to filter the results.</param>
|
||||
/// <param name="hitTriggers">If set to <c>true</c> triggers will be hit, otherwise will skip them.</param>
|
||||
/// <returns>True if capsule hits an matching object, otherwise false.</returns>
|
||||
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);
|
||||
|
||||
/// <summary>
|
||||
/// Performs a sweep test against objects in the scene using a capsule geometry.
|
||||
/// </summary>
|
||||
/// <param name="center">The capsule center.</param>
|
||||
/// <param name="radius">The radius of the capsule.</param>
|
||||
/// <param name="height">The height of the capsule, excluding the top and bottom spheres.</param>
|
||||
/// <param name="direction">The normalized direction in which cast a capsule.</param>
|
||||
/// <param name="hitInfo">The result hit information. Valid only when method returns true.</param>
|
||||
/// <param name="rotation">The capsule rotation.</param>
|
||||
/// <param name="maxDistance">The maximum distance the ray should check for collisions.</param>
|
||||
/// <param name="layerMask">The layer mask used to filter the results.</param>
|
||||
/// <param name="hitTriggers">If set to <c>true</c> triggers will be hit, otherwise will skip them.</param>
|
||||
/// <returns>True if capsule hits an matching object, otherwise false.</returns>
|
||||
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);
|
||||
|
||||
/// <summary>
|
||||
/// Performs a sweep test against objects in the scene using a capsule geometry.
|
||||
/// </summary>
|
||||
/// <param name="center">The capsule center.</param>
|
||||
/// <param name="radius">The radius of the capsule.</param>
|
||||
/// <param name="height">The height of the capsule, excluding the top and bottom spheres.</param>
|
||||
/// <param name="direction">The normalized direction in which cast a capsule.</param>
|
||||
/// <param name="results">The result hits. Valid only when method returns true.</param>
|
||||
/// <param name="rotation">The capsule rotation.</param>
|
||||
/// <param name="maxDistance">The maximum distance the ray should check for collisions.</param>
|
||||
/// <param name="layerMask">The layer mask used to filter the results.</param>
|
||||
/// <param name="hitTriggers">If set to <c>true</c> triggers will be hit, otherwise will skip them.</param>
|
||||
/// <returns>True if capsule hits an matching object, otherwise false.</returns>
|
||||
API_FUNCTION() bool CapsuleCastAll(const Vector3& center, float radius, float height, const Vector3& direction, API_PARAM(Out) Array<RayCastHit, HeapAllocation>& results, const Quaternion& rotation = Quaternion::Identity, float maxDistance = MAX_float, uint32 layerMask = MAX_uint32, bool hitTriggers = true);
|
||||
|
||||
/// <summary>
|
||||
/// Performs a sweep test against objects in the scene using a convex mesh.
|
||||
/// </summary>
|
||||
/// <param name="center">The convex mesh center.</param>
|
||||
/// <param name="convexMesh">Collision data of the convex mesh.</param>
|
||||
/// <param name="scale">The scale of the convex mesh.</param>
|
||||
/// <param name="direction">The normalized direction in which cast a convex mesh.</param>
|
||||
/// <param name="rotation">The convex mesh rotation.</param>
|
||||
/// <param name="maxDistance">The maximum distance the ray should check for collisions.</param>
|
||||
/// <param name="layerMask">The layer mask used to filter the results.</param>
|
||||
/// <param name="hitTriggers">If set to <c>true</c> triggers will be hit, otherwise will skip them.</param>
|
||||
/// <returns>True if convex mesh hits an matching object, otherwise false.</returns>
|
||||
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);
|
||||
|
||||
/// <summary>
|
||||
/// Performs a sweep test against objects in the scene using a convex mesh.
|
||||
/// </summary>
|
||||
/// <param name="center">The convex mesh center.</param>
|
||||
/// <param name="convexMesh">Collision data of the convex mesh.</param>
|
||||
/// <param name="scale">The scale of the convex mesh.</param>
|
||||
/// <param name="direction">The normalized direction in which cast a convex mesh.</param>
|
||||
/// <param name="hitInfo">The result hit information. Valid only when method returns true.</param>
|
||||
/// <param name="rotation">The convex mesh rotation.</param>
|
||||
/// <param name="maxDistance">The maximum distance the ray should check for collisions.</param>
|
||||
/// <param name="layerMask">The layer mask used to filter the results.</param>
|
||||
/// <param name="hitTriggers">If set to <c>true</c> triggers will be hit, otherwise will skip them.</param>
|
||||
/// <returns>True if convex mesh hits an matching object, otherwise false.</returns>
|
||||
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);
|
||||
|
||||
/// <summary>
|
||||
/// Performs a sweep test against objects in the scene using a convex mesh.
|
||||
/// </summary>
|
||||
/// <param name="center">The convex mesh center.</param>
|
||||
/// <param name="convexMesh">Collision data of the convex mesh.</param>
|
||||
/// <param name="scale">The scale of the convex mesh.</param>
|
||||
/// <param name="direction">The normalized direction in which cast a convex mesh.</param>
|
||||
/// <param name="results">The result hits. Valid only when method returns true.</param>
|
||||
/// <param name="rotation">The convex mesh rotation.</param>
|
||||
/// <param name="maxDistance">The maximum distance the ray should check for collisions.</param>
|
||||
/// <param name="layerMask">The layer mask used to filter the results.</param>
|
||||
/// <param name="hitTriggers">If set to <c>true</c> triggers will be hit, otherwise will skip them.</param>
|
||||
/// <returns>True if convex mesh hits an matching object, otherwise false.</returns>
|
||||
API_FUNCTION() bool ConvexCastAll(const Vector3& center, const CollisionData* convexMesh, const Vector3& scale, const Vector3& direction, API_PARAM(Out) Array<RayCastHit, HeapAllocation>& results, const Quaternion& rotation = Quaternion::Identity, float maxDistance = MAX_float, uint32 layerMask = MAX_uint32, bool hitTriggers = true);
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the given box overlaps with other colliders or not.
|
||||
/// </summary>
|
||||
/// <param name="center">The box center.</param>
|
||||
/// <param name="halfExtents">The half size of the box in each direction.</param>
|
||||
/// <param name="rotation">The box rotation.</param>
|
||||
/// <param name="layerMask">The layer mask used to filter the results.</param>
|
||||
/// <param name="hitTriggers">If set to <c>true</c> triggers will be hit, otherwise will skip them.</param>
|
||||
/// <returns>True if box overlaps any matching object, otherwise false.</returns>
|
||||
API_FUNCTION() bool CheckBox(const Vector3& center, const Vector3& halfExtents, const Quaternion& rotation = Quaternion::Identity, uint32 layerMask = MAX_uint32, bool hitTriggers = true);
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the given sphere overlaps with other colliders or not.
|
||||
/// </summary>
|
||||
/// <param name="center">The sphere center.</param>
|
||||
/// <param name="radius">The radius of the sphere.</param>
|
||||
/// <param name="layerMask">The layer mask used to filter the results.</param>
|
||||
/// <param name="hitTriggers">If set to <c>true</c> triggers will be hit, otherwise will skip them.</param>
|
||||
/// <returns>True if sphere overlaps any matching object, otherwise false.</returns>
|
||||
API_FUNCTION() bool CheckSphere(const Vector3& center, float radius, uint32 layerMask = MAX_uint32, bool hitTriggers = true);
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the given capsule overlaps with other colliders or not.
|
||||
/// </summary>
|
||||
/// <param name="center">The capsule center.</param>
|
||||
/// <param name="radius">The radius of the capsule.</param>
|
||||
/// <param name="height">The height of the capsule, excluding the top and bottom spheres.</param>
|
||||
/// <param name="rotation">The capsule rotation.</param>
|
||||
/// <param name="layerMask">The layer mask used to filter the results.</param>
|
||||
/// <param name="hitTriggers">If set to <c>true</c> triggers will be hit, otherwise will skip them.</param>
|
||||
/// <returns>True if capsule overlaps any matching object, otherwise false.</returns>
|
||||
API_FUNCTION() bool CheckCapsule(const Vector3& center, float radius, float height, const Quaternion& rotation = Quaternion::Identity, uint32 layerMask = MAX_uint32, bool hitTriggers = true);
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the given convex mesh overlaps with other colliders or not.
|
||||
/// </summary>
|
||||
/// <param name="center">The convex mesh center.</param>
|
||||
/// <param name="convexMesh">Collision data of the convex mesh.</param>
|
||||
/// <param name="scale">The scale of the convex mesh.</param>
|
||||
/// <param name="rotation">The convex mesh rotation.</param>
|
||||
/// <param name="layerMask">The layer mask used to filter the results.</param>
|
||||
/// <param name="hitTriggers">If set to <c>true</c> triggers will be hit, otherwise will skip them.</param>
|
||||
/// <returns>True if convex mesh overlaps any matching object, otherwise false.</returns>
|
||||
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);
|
||||
|
||||
/// <summary>
|
||||
/// Finds all colliders touching or inside of the given box.
|
||||
/// </summary>
|
||||
/// <param name="center">The box center.</param>
|
||||
/// <param name="halfExtents">The half size of the box in each direction.</param>
|
||||
/// <param name="rotation">The box rotation.</param>
|
||||
/// <param name="results">The result colliders that overlap with the given box. Valid only when method returns true.</param>
|
||||
/// <param name="layerMask">The layer mask used to filter the results.</param>
|
||||
/// <param name="hitTriggers">If set to <c>true</c> triggers will be hit, otherwise will skip them.</param>
|
||||
/// <returns>True if box overlaps any matching object, otherwise false.</returns>
|
||||
API_FUNCTION() bool OverlapBox(const Vector3& center, const Vector3& halfExtents, API_PARAM(Out) Array<Collider*, HeapAllocation>& results, const Quaternion& rotation = Quaternion::Identity, uint32 layerMask = MAX_uint32, bool hitTriggers = true);
|
||||
|
||||
/// <summary>
|
||||
/// Finds all colliders touching or inside of the given sphere.
|
||||
/// </summary>
|
||||
/// <param name="center">The sphere center.</param>
|
||||
/// <param name="radius">The radius of the sphere.</param>
|
||||
/// <param name="results">The result colliders that overlap with the given sphere. Valid only when method returns true.</param>
|
||||
/// <param name="layerMask">The layer mask used to filter the results.</param>
|
||||
/// <param name="hitTriggers">If set to <c>true</c> triggers will be hit, otherwise will skip them.</param>
|
||||
/// <returns>True if sphere overlaps any matching object, otherwise false.</returns>
|
||||
API_FUNCTION() bool OverlapSphere(const Vector3& center, float radius, API_PARAM(Out) Array<Collider*, HeapAllocation>& results, uint32 layerMask = MAX_uint32, bool hitTriggers = true);
|
||||
|
||||
/// <summary>
|
||||
/// Finds all colliders touching or inside of the given capsule.
|
||||
/// </summary>
|
||||
/// <param name="center">The capsule center.</param>
|
||||
/// <param name="radius">The radius of the capsule.</param>
|
||||
/// <param name="height">The height of the capsule, excluding the top and bottom spheres.</param>
|
||||
/// <param name="results">The result colliders that overlap with the given capsule. Valid only when method returns true.</param>
|
||||
/// <param name="rotation">The capsule rotation.</param>
|
||||
/// <param name="layerMask">The layer mask used to filter the results.</param>
|
||||
/// <param name="hitTriggers">If set to <c>true</c> triggers will be hit, otherwise will skip them.</param>
|
||||
/// <returns>True if capsule overlaps any matching object, otherwise false.</returns>
|
||||
API_FUNCTION() bool OverlapCapsule(const Vector3& center, float radius, float height, API_PARAM(Out) Array<Collider*, HeapAllocation>& results, const Quaternion& rotation = Quaternion::Identity, uint32 layerMask = MAX_uint32, bool hitTriggers = true);
|
||||
|
||||
/// <summary>
|
||||
/// Finds all colliders touching or inside of the given convex mesh.
|
||||
/// </summary>
|
||||
/// <param name="center">The convex mesh center.</param>
|
||||
/// <param name="convexMesh">Collision data of the convex mesh.</param>
|
||||
/// <param name="scale">The scale of the convex mesh.</param>
|
||||
/// <param name="results">The result colliders that overlap with the given convex mesh. Valid only when method returns true.</param>
|
||||
/// <param name="rotation">The convex mesh rotation.</param>
|
||||
/// <param name="layerMask">The layer mask used to filter the results.</param>
|
||||
/// <param name="hitTriggers">If set to <c>true</c> triggers will be hit, otherwise will skip them.</param>
|
||||
/// <returns>True if convex mesh overlaps any matching object, otherwise false.</returns>
|
||||
API_FUNCTION() bool OverlapConvex(const Vector3& center, const CollisionData* convexMesh, const Vector3& scale, API_PARAM(Out) Array<Collider*, HeapAllocation>& results, const Quaternion& rotation = Quaternion::Identity, uint32 layerMask = MAX_uint32, bool hitTriggers = true);
|
||||
|
||||
/// <summary>
|
||||
/// Finds all colliders touching or inside of the given box.
|
||||
/// </summary>
|
||||
/// <param name="center">The box center.</param>
|
||||
/// <param name="halfExtents">The half size of the box in each direction.</param>
|
||||
/// <param name="rotation">The box rotation.</param>
|
||||
/// <param name="results">The result colliders that overlap with the given box. Valid only when method returns true.</param>
|
||||
/// <param name="layerMask">The layer mask used to filter the results.</param>
|
||||
/// <param name="hitTriggers">If set to <c>true</c> triggers will be hit, otherwise will skip them.</param>
|
||||
/// <returns>True if box overlaps any matching object, otherwise false.</returns>
|
||||
API_FUNCTION() bool OverlapBox(const Vector3& center, const Vector3& halfExtents, API_PARAM(Out) Array<PhysicsColliderActor*, HeapAllocation>& results, const Quaternion& rotation = Quaternion::Identity, uint32 layerMask = MAX_uint32, bool hitTriggers = true);
|
||||
|
||||
/// <summary>
|
||||
/// Finds all colliders touching or inside of the given sphere.
|
||||
/// </summary>
|
||||
/// <param name="center">The sphere center.</param>
|
||||
/// <param name="radius">The radius of the sphere.</param>
|
||||
/// <param name="results">The result colliders that overlap with the given sphere. Valid only when method returns true.</param>
|
||||
/// <param name="layerMask">The layer mask used to filter the results.</param>
|
||||
/// <param name="hitTriggers">If set to <c>true</c> triggers will be hit, otherwise will skip them.</param>
|
||||
/// <returns>True if sphere overlaps any matching object, otherwise false.</returns>
|
||||
API_FUNCTION() bool OverlapSphere(const Vector3& center, float radius, API_PARAM(Out) Array<PhysicsColliderActor*, HeapAllocation>& results, uint32 layerMask = MAX_uint32, bool hitTriggers = true);
|
||||
|
||||
/// <summary>
|
||||
/// Finds all colliders touching or inside of the given capsule.
|
||||
/// </summary>
|
||||
/// <param name="center">The capsule center.</param>
|
||||
/// <param name="radius">The radius of the capsule.</param>
|
||||
/// <param name="height">The height of the capsule, excluding the top and bottom spheres.</param>
|
||||
/// <param name="results">The result colliders that overlap with the given capsule. Valid only when method returns true.</param>
|
||||
/// <param name="rotation">The capsule rotation.</param>
|
||||
/// <param name="layerMask">The layer mask used to filter the results.</param>
|
||||
/// <param name="hitTriggers">If set to <c>true</c> triggers will be hit, otherwise will skip them.</param>
|
||||
/// <returns>True if capsule overlaps any matching object, otherwise false.</returns>
|
||||
API_FUNCTION() bool OverlapCapsule(const Vector3& center, float radius, float height, API_PARAM(Out) Array<PhysicsColliderActor*, HeapAllocation>& results, const Quaternion& rotation = Quaternion::Identity, uint32 layerMask = MAX_uint32, bool hitTriggers = true);
|
||||
|
||||
/// <summary>
|
||||
/// Finds all colliders touching or inside of the given convex mesh.
|
||||
/// </summary>
|
||||
/// <param name="center">The convex mesh center.</param>
|
||||
/// <param name="convexMesh">Collision data of the convex mesh.</param>
|
||||
/// <param name="scale">The scale of the convex mesh.</param>
|
||||
/// <param name="results">The result colliders that overlap with the given convex mesh. Valid only when method returns true.</param>
|
||||
/// <param name="rotation">The convex mesh rotation.</param>
|
||||
/// <param name="layerMask">The layer mask used to filter the results.</param>
|
||||
/// <param name="hitTriggers">If set to <c>true</c> triggers will be hit, otherwise will skip them.</param>
|
||||
/// <returns>True if convex mesh overlaps any matching object, otherwise false.</returns>
|
||||
API_FUNCTION() bool OverlapConvex(const Vector3& center, const CollisionData* convexMesh, const Vector3& scale, API_PARAM(Out) Array<PhysicsColliderActor*, HeapAllocation>& 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<PxActor*> mNewActors;
|
||||
Array<PxActor*> mDeadActors;
|
||||
Array<PxMaterial*> mDeadMaterials;
|
||||
Array<PhysicsColliderActor*> mDeadColliders;
|
||||
Array<Joint*> mDeadJoints;
|
||||
Array<ActionData> mActions;
|
||||
Array<PxBase*> mDeadObjects;
|
||||
|
||||
#if WITH_VEHICLE
|
||||
Array<PxVehicleWheels*> mWheelVehiclesCache;
|
||||
Array<PxRaycastQueryResult> mWheelQueryResults;
|
||||
Array<PxRaycastHit> mWheelHitResults;
|
||||
Array<PxWheelQueryResult> mWheelVehiclesResultsPerWheel;
|
||||
Array<PxVehicleWheelQueryResult> mWheelVehiclesResultsPerVehicle;
|
||||
PxBatchQuery* mWheelRaycastBatchQuery = nullptr;
|
||||
PxVehicleDrivableSurfaceToTireFrictionPairs* mWheelTireFrictions = nullptr;
|
||||
|
||||
Array<WheeledVehicle*> mWheelVehicles;
|
||||
#endif
|
||||
};
|
||||
@@ -3,7 +3,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "Types.h"
|
||||
#include "Engine/Core/Core.h"
|
||||
#include <ThirdParty/PhysX/task/PxTask.h>
|
||||
#include <ThirdParty/PhysX/foundation/PxSimpleTypes.h>
|
||||
#include <ThirdParty/PhysX/foundation/PsSync.h>
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -411,6 +411,7 @@ private:
|
||||
/// <returns>True if failed, otherwise false.</returns>
|
||||
bool UpdateCollision();
|
||||
|
||||
void OnPhysicsSceneChanged(PhysicsScene* previous);
|
||||
public:
|
||||
|
||||
// [ISerializable]
|
||||
|
||||
Reference in New Issue
Block a user