Refactor Physics to separate PhysicsBackend

#673
This commit is contained in:
Wojtek Figat
2022-01-20 23:28:43 +01:00
parent cf1af53ab8
commit 427846f73b
66 changed files with 4803 additions and 4327 deletions

View File

@@ -5,6 +5,7 @@
#if COMPILE_WITH_ASSETS_IMPORTER
#include "AssetsImportingManager.h"
#include "Engine/Physics/CollisionData.h"
CreateAssetResult CreateCollisionData::Create(CreateAssetContext& context)
{

View File

@@ -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::CollectResultsAll();
Physics::CollectResults();
}
// Call on exit event
@@ -242,7 +242,7 @@ void Engine::OnFixedUpdate()
{
PROFILE_CPU_NAMED("Fixed Update");
Physics::FlushRequestsAll();
Physics::FlushRequests();
// Call event
FixedUpdate();
@@ -253,7 +253,7 @@ void Engine::OnFixedUpdate()
if (!Time::GetGamePaused())
{
const float dt = Time::Physics.DeltaTime.GetTotalSeconds();
Physics::SimulateAll(dt);
Physics::Simulate(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::CollectResultsAll();
Physics::CollectResults();
// Before
Application::BeforeExit();

View File

@@ -1814,9 +1814,9 @@ void Actor::FromJson(const StringAnsiView& json)
void Actor::SetPhysicsScene(PhysicsScene* scene)
{
ASSERT(scene);
CHECK(scene);
auto previous = GetPhysicsScene();
const auto previous = GetPhysicsScene();
_physicsScene = scene;
if (previous != _physicsScene)
@@ -1829,10 +1829,7 @@ void Actor::SetPhysicsScene(PhysicsScene* scene)
}
}
PhysicsScene* Actor::GetPhysicsScene()
PhysicsScene* Actor::GetPhysicsScene() const
{
if (_physicsScene == nullptr)
_physicsScene = Physics::DefaultScene;
return _physicsScene;
return _physicsScene ? _physicsScene : Physics::DefaultScene;
}

View File

@@ -970,10 +970,10 @@ public:
/// <summary>
/// Get the physics world the controller is part of.
/// </summary>
API_PROPERTY(Attributes="HideInEditor") PhysicsScene* GetPhysicsScene();
API_PROPERTY(Attributes="HideInEditor") PhysicsScene* GetPhysicsScene() const;
protected:
virtual void OnPhysicsSceneChanged(PhysicsScene* previous) {};
virtual void OnPhysicsSceneChanged(PhysicsScene* previous) {}
private:

View File

@@ -2,36 +2,27 @@
#pragma once
namespace physx
{
class PxRigidActor;
class PxTransform;
}
/// <summary>
/// A base interface for all physical actors types/owners that can responds on transformation changed event.
/// </summary>
class FLAXENGINE_API IPhysicsActor
{
public:
/// <summary>
/// Finalizes an instance of the <see cref="IPhysicsActor"/> class.
/// </summary>
virtual ~IPhysicsActor() = default;
/// <summary>
/// Gets the rigid actor (PhysX object) may be null.
/// Gets the native physics backend object.
/// </summary>
/// <returns>PhysX rigid actor or null if not using</returns>
virtual physx::PxRigidActor* GetRigidActor() = 0;
virtual void* GetPhysicsActor() const = 0;
/// <summary>
/// Called when actor's active transformation gets changed after the physics simulation step.
/// Called when actor's active transformation gets changed after the physics simulation step during.
/// </summary>
/// <remarks>
/// This event is called internally by the Physics service and should not be used by the others.
/// </remarks>
/// <param name="transform">The current transformation.</param>
virtual void OnActiveTransformChanged(const physx::PxTransform& transform) = 0;
virtual void OnActiveTransformChanged() = 0;
};

View File

@@ -1,10 +1,7 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
#include "PhysicsActor.h"
#include "Engine/Physics/Utilities.h"
#include "Engine/Physics/Physics.h"
#include <ThirdParty/PhysX/PxActor.h>
#include <ThirdParty/PhysX/PxRigidActor.h>
#include "../PhysicsBackend.h"
PhysicsActor::PhysicsActor(const SpawnParams& params)
: Actor(params)
@@ -13,28 +10,23 @@ PhysicsActor::PhysicsActor(const SpawnParams& params)
{
}
void PhysicsActor::OnActiveTransformChanged(const PxTransform& transform)
void PhysicsActor::OnActiveTransformChanged()
{
// Change actor transform (but with locking)
ASSERT(!_isUpdatingTransform);
_isUpdatingTransform = true;
//SetTransform(Transform(P2C(transform.p), P2C(transform.q), GetScale()));
Transform transform;
PhysicsBackend::GetRigidActorPose(GetPhysicsActor(), transform.Translation, transform.Orientation);
transform.Scale = _transform.Scale;
if (_parent)
{
Transform v;
v.Translation = P2C(transform.p);
v.Orientation = P2C(transform.q);
v.Scale = _transform.Scale;
if (_parent)
{
_parent->GetTransform().WorldToLocal(v, _localTransform);
}
else
{
_localTransform = v;
}
OnTransformChanged();
_parent->GetTransform().WorldToLocal(transform, _localTransform);
}
else
{
_localTransform = transform;
}
OnTransformChanged();
_isUpdatingTransform = false;
}
@@ -48,31 +40,11 @@ void PhysicsActor::OnTransformChanged()
void PhysicsActor::UpdateBounds()
{
const auto actor = GetPhysXActor();
const float boundsScale = 1.02f;
if (actor && actor->getScene() != nullptr)
{
if (actor->is<PxRigidActor>())
{
const auto rigidActor = (PxRigidActor*)actor;
if (rigidActor->getNbShapes() != 0)
{
_box = P2C(actor->getWorldBounds(boundsScale));
}
else
{
_box = BoundingBox(_transform.Translation);
}
}
else
{
_box = P2C(actor->getWorldBounds(boundsScale));
}
}
void* actor = GetPhysicsActor();
if (actor)
PhysicsBackend::GetActorBounds(actor, _box);
else
{
_box = BoundingBox(_transform.Translation);
}
BoundingSphere::FromBox(_box, _sphere);
}

View File

@@ -6,8 +6,6 @@
#include "Engine/Physics/Types.h"
#include "IPhysicsActor.h"
class PhysicsScene;
/// <summary>
/// A base class for all physical actors.
/// </summary>
@@ -22,12 +20,6 @@ protected:
public:
/// <summary>
/// Gets the native PhysX actor object.
/// </summary>
/// <returns>The PhysX actor.</returns>
virtual PxActor* GetPhysXActor() = 0;
/// <summary>
/// Updates the bounding box.
/// </summary>
@@ -39,7 +31,7 @@ public:
bool IntersectsItself(const Ray& ray, float& distance, Vector3& normal) override;
// [IPhysicsActor]
void OnActiveTransformChanged(const PxTransform& transform) override;
void OnActiveTransformChanged() override;
protected:

View File

@@ -1,19 +1,11 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
#include "RigidBody.h"
#include "PxMaterial.h"
#include "Engine/Core/Log.h"
#include "Engine/Physics/Utilities.h"
#include "Engine/Physics/Colliders/Collider.h"
#include "Engine/Physics/Physics.h"
#include "Engine/Physics/PhysicalMaterial.h"
#include "Engine/Physics/PhysicsBackend.h"
#include "Engine/Physics/PhysicsScene.h"
#include "Engine/Serialization/Serialization.h"
#include <ThirdParty/PhysX/extensions/PxRigidBodyExt.h>
#include <ThirdParty/PhysX/PxRigidActor.h>
#include <ThirdParty/PhysX/PxRigidDynamic.h>
#include <ThirdParty/PhysX/PxPhysics.h>
RigidBody::RigidBody(const SpawnParams& params)
: PhysicsActor(params)
@@ -39,52 +31,38 @@ void RigidBody::SetIsKinematic(const bool value)
{
if (value == GetIsKinematic())
return;
_isKinematic = value;
if (_actor)
_actor->setRigidBodyFlag(PxRigidBodyFlag::eKINEMATIC, value);
PhysicsBackend::SetRigidDynamicActorFlag(_actor, PhysicsBackend::RigidDynamicFlags::Kinematic, value);
}
void RigidBody::SetLinearDamping(float value)
{
if (Math::NearEqual(value, _linearDamping))
return;
_linearDamping = value;
if (_actor)
{
_actor->setLinearDamping(value);
}
PhysicsBackend::SetRigidDynamicActorLinearDamping(_actor, _linearDamping);
}
void RigidBody::SetAngularDamping(float value)
{
if (Math::NearEqual(value, _angularDamping))
return;
_angularDamping = value;
if (_actor)
{
_actor->setAngularDamping(value);
}
PhysicsBackend::SetRigidDynamicActorAngularDamping(_actor, _angularDamping);
}
void RigidBody::SetEnableSimulation(bool value)
{
if (value == GetEnableSimulation())
return;
_enableSimulation = value;
if (_actor)
{
const bool isActive = _enableSimulation && IsActiveInHierarchy();
_actor->setActorFlag(PxActorFlag::eDISABLE_SIMULATION, !isActive);
// Auto wake up
PhysicsBackend::SetActorFlag(_actor, PhysicsBackend::ActorFlags::NoSimulation, !isActive);
if (isActive && GetStartAwake())
WakeUp();
}
@@ -94,24 +72,19 @@ void RigidBody::SetUseCCD(bool value)
{
if (value == GetUseCCD())
return;
_useCCD = value;
if (_actor)
_actor->setRigidBodyFlag(PxRigidBodyFlag::eENABLE_CCD, value);
PhysicsBackend::SetRigidDynamicActorFlag(_actor, PhysicsBackend::RigidDynamicFlags::CCD, value);
}
void RigidBody::SetEnableGravity(bool value)
{
if (value == GetEnableGravity())
return;
_enableGravity = value;
if (_actor)
{
_actor->setActorFlag(PxActorFlag::eDISABLE_GRAVITY, !value);
PhysicsBackend::SetActorFlag(_actor, PhysicsBackend::ActorFlags::NoGravity, !value);
if (value)
WakeUp();
}
@@ -131,11 +104,9 @@ void RigidBody::SetMaxAngularVelocity(float value)
{
if (Math::NearEqual(value, _maxAngularVelocity))
return;
_maxAngularVelocity = value;
if (_actor)
_actor->setMaxAngularVelocity(value);
PhysicsBackend::SetRigidDynamicActorMaxAngularVelocity(_actor, _maxAngularVelocity);
}
bool RigidBody::GetOverrideMass() const
@@ -147,9 +118,7 @@ void RigidBody::SetOverrideMass(bool value)
{
if (value == GetOverrideMass())
return;
_overrideMass = value;
UpdateMass();
}
@@ -162,12 +131,8 @@ void RigidBody::SetMass(float value)
{
if (Math::NearEqual(value, _mass))
return;
_mass = value;
// Auto enable override
_overrideMass = true;
UpdateMass();
}
@@ -180,7 +145,6 @@ void RigidBody::SetMassScale(float value)
{
if (Math::NearEqual(value, _massScale))
return;
_massScale = value;
UpdateMass();
}
@@ -189,163 +153,102 @@ void RigidBody::SetCenterOfMassOffset(const Vector3& value)
{
if (Vector3::NearEqual(value, _centerOfMassOffset))
return;
_centerOfMassOffset = value;
if (_actor)
{
PxTransform pose = _actor->getCMassLocalPose();
pose.p += C2P(_centerOfMassOffset);
_actor->setCMassLocalPose(pose);
}
PhysicsBackend::SetRigidDynamicActorCenterOfMassOffset(_actor, _centerOfMassOffset);
}
void RigidBody::SetConstraints(const RigidbodyConstraints value)
{
if (value == _constraints)
return;
_constraints = value;
if (_actor)
{
_actor->setRigidDynamicLockFlags(static_cast<PxRigidDynamicLockFlag::Enum>(value));
}
PhysicsBackend::SetRigidDynamicActorConstraints(_actor, value);
}
Vector3 RigidBody::GetLinearVelocity() const
{
return _actor ? P2C(_actor->getLinearVelocity()) : Vector3::Zero;
return _actor ? PhysicsBackend::GetRigidDynamicActorLinearVelocity(_actor) : Vector3::Zero;
}
void RigidBody::SetLinearVelocity(const Vector3& value) const
{
if (_actor)
_actor->setLinearVelocity(C2P(value), GetStartAwake());
PhysicsBackend::SetRigidDynamicActorLinearVelocity(_actor, value, GetStartAwake());
}
Vector3 RigidBody::GetAngularVelocity() const
{
return _actor ? P2C(_actor->getAngularVelocity()) : Vector3::Zero;
return _actor ? PhysicsBackend::GetRigidDynamicActorAngularVelocity(_actor) : Vector3::Zero;
}
void RigidBody::SetAngularVelocity(const Vector3& value) const
{
if (_actor)
_actor->setAngularVelocity(C2P(value), GetStartAwake());
PhysicsBackend::SetRigidDynamicActorAngularVelocity(_actor, value, GetStartAwake());
}
float RigidBody::GetMaxDepenetrationVelocity() const
{
return _actor ? _actor->getMaxDepenetrationVelocity() : 0;
return _actor ? PhysicsBackend::GetRigidDynamicActorMaxDepenetrationVelocity(_actor) : 0;
}
void RigidBody::SetMaxDepenetrationVelocity(const float value) const
{
if (_actor)
_actor->setMaxDepenetrationVelocity(value);
PhysicsBackend::SetRigidDynamicActorMaxDepenetrationVelocity(_actor, value);
}
float RigidBody::GetSleepThreshold() const
{
return _actor ? _actor->getSleepThreshold() : 0;
return _actor ? PhysicsBackend::GetRigidDynamicActorSleepThreshold(_actor) : 0;
}
void RigidBody::SetSleepThreshold(const float value) const
{
if (_actor)
_actor->setSleepThreshold(value);
PhysicsBackend::SetRigidDynamicActorSleepThreshold(_actor, value);
}
Vector3 RigidBody::GetCenterOfMass() const
{
return _actor ? P2C(_actor->getCMassLocalPose().p) : Vector3::Zero;
return _actor ? PhysicsBackend::GetRigidDynamicActorCenterOfMass(_actor) : Vector3::Zero;
}
bool RigidBody::IsSleeping() const
{
return _actor ? _actor->isSleeping() : false;
return _actor ? PhysicsBackend::GetRigidDynamicActorIsSleeping(_actor) : false;
}
void RigidBody::Sleep() const
{
if (_actor && GetEnableSimulation() && !GetIsKinematic() && IsActiveInHierarchy() && _actor->getScene())
{
_actor->putToSleep();
}
if (_actor && GetEnableSimulation() && !GetIsKinematic() && IsActiveInHierarchy())
PhysicsBackend::GetRigidActorSleep(_actor);
}
void RigidBody::WakeUp() const
{
if (_actor && GetEnableSimulation() && !GetIsKinematic() && IsActiveInHierarchy() && _actor->getScene())
{
_actor->wakeUp();
}
if (_actor && GetEnableSimulation() && !GetIsKinematic() && IsActiveInHierarchy())
PhysicsBackend::GetRigidDynamicActorWakeUp(_actor);
}
void RigidBody::UpdateMass()
{
if (_actor == nullptr)
return;
if (_overrideMass)
{
// Use fixed mass
PxRigidBodyExt::setMassAndUpdateInertia(*_actor, Math::Max(_mass * _massScale, 0.001f));
}
else
{
// Calculate per-shape densities (convert kg/m^3 into engine units)
const float minDensity = 0.08375f; // Hydrogen density
const float defaultDensity = 1000.0f; // Water density
Array<float, InlinedAllocation<32>> densities;
for (uint32 i = 0; i < _actor->getNbShapes(); i++)
{
PxShape* shape;
_actor->getShapes(&shape, 1, i);
if (shape->getFlags() & PxShapeFlag::eSIMULATION_SHAPE)
{
float density = defaultDensity;
PxMaterial* material;
if (shape->getMaterials(&material, 1, 0) == 1)
{
if (const auto mat = (PhysicalMaterial*)material->userData)
{
density = Math::Max(mat->Density, minDensity);
}
}
densities.Add(KgPerM3ToKgPerCm3(density));
}
}
if (densities.IsEmpty())
densities.Add(KgPerM3ToKgPerCm3(defaultDensity));
// Auto calculated mass
PxRigidBodyExt::updateMassAndInertia(*_actor, densities.Get(), densities.Count());
_mass = _actor->getMass();
const float massScale = Math::Max(_massScale, 0.001f);
if (!Math::IsOne(massScale))
{
_mass *= massScale;
_actor->setMass(_mass);
}
}
if (_actor)
PhysicsBackend::UpdateRigidDynamicActorMass(_actor, _mass, _massScale, _overrideMass == 0);
}
void RigidBody::AddForce(const Vector3& force, ForceMode mode) const
{
if (_actor && GetEnableSimulation())
{
_actor->addForce(C2P(force), static_cast<PxForceMode::Enum>(mode));
}
PhysicsBackend::AddRigidDynamicActorForce(_actor, force, mode);
}
void RigidBody::AddForceAtPosition(const Vector3& force, const Vector3& position, ForceMode mode) const
{
if (_actor && GetEnableSimulation())
{
PxRigidBodyExt::addForceAtPos(*_actor, C2P(force), C2P(position), static_cast<PxForceMode::Enum>(mode));
}
PhysicsBackend::AddRigidDynamicActorForceAtPosition(_actor, force, position, mode);
}
void RigidBody::AddRelativeForce(const Vector3& force, ForceMode mode) const
@@ -356,9 +259,7 @@ void RigidBody::AddRelativeForce(const Vector3& force, ForceMode mode) const
void RigidBody::AddTorque(const Vector3& torque, ForceMode mode) const
{
if (_actor && GetEnableSimulation())
{
_actor->addTorque(C2P(torque), static_cast<PxForceMode::Enum>(mode));
}
PhysicsBackend::AddRigidDynamicActorTorque(_actor, torque, mode);
}
void RigidBody::AddRelativeTorque(const Vector3& torque, ForceMode mode) const
@@ -369,9 +270,7 @@ void RigidBody::AddRelativeTorque(const Vector3& torque, ForceMode mode) const
void RigidBody::SetSolverIterationCounts(int32 minPositionIters, int32 minVelocityIters) const
{
if (_actor)
{
_actor->setSolverIterationCounts(Math::Clamp(minPositionIters, 1, 255), Math::Clamp(minVelocityIters, 1, 255));
}
PhysicsBackend::SetRigidDynamicActorSolverIterationCounts(_actor, minPositionIters, minVelocityIters);
}
void RigidBody::ClosestPoint(const Vector3& position, Vector3& result) const
@@ -424,67 +323,6 @@ void RigidBody::OnColliderChanged(Collider* c)
// WakeUp();
}
void RigidBody::CreateActor()
{
ASSERT(_actor == nullptr);
// Create rigid body
const PxTransform trans(C2P(_transform.Translation), C2P(_transform.Orientation));
_actor = CPhysX->createRigidDynamic(trans);
_actor->userData = this;
// Setup flags
#if WITH_PVD
PxActorFlags actorFlags = PxActorFlag::eVISUALIZATION;
#else
PxActorFlags actorFlags = static_cast<PxActorFlags>(0);
#endif
const bool isActive = _enableSimulation && IsActiveInHierarchy();
if (!isActive)
actorFlags |= PxActorFlag::eDISABLE_SIMULATION;
if (!_enableGravity)
actorFlags |= PxActorFlag::eDISABLE_GRAVITY;
_actor->setActorFlags(actorFlags);
if (_useCCD)
_actor->setRigidBodyFlag(PxRigidBodyFlag::eENABLE_CCD, true);
if (_isKinematic)
_actor->setRigidBodyFlag(PxRigidBodyFlag::eKINEMATIC, true);
// Apply properties
_actor->setLinearDamping(_linearDamping);
_actor->setAngularDamping(_angularDamping);
_actor->setMaxAngularVelocity(_maxAngularVelocity);
_actor->setRigidDynamicLockFlags(static_cast<PxRigidDynamicLockFlag::Enum>(_constraints));
// Find colliders to attach
for (int32 i = 0; i < Children.Count(); i++)
{
auto collider = dynamic_cast<Collider*>(Children[i]);
if (collider && collider->CanAttach(this))
{
collider->Attach(this);
}
}
// Setup mass (calculate or use overriden value)
UpdateMass();
// Apply the Center Of Mass offset
if (!_centerOfMassOffset.IsZero())
{
PxTransform pose = _actor->getCMassLocalPose();
pose.p += C2P(_centerOfMassOffset);
_actor->setCMassLocalPose(pose);
}
// Register actor
const bool putToSleep = !_startAwake && GetEnableSimulation() && !GetIsKinematic() && IsActiveInHierarchy();
GetPhysicsScene()->AddActor(_actor, putToSleep);
// Update cached data
UpdateBounds();
}
void RigidBody::UpdateScale()
{
const Vector3 scale = GetScale();
@@ -546,19 +384,61 @@ void RigidBody::Deserialize(DeserializeStream& stream, ISerializeModifier* modif
DESERIALIZE_BIT_MEMBER(UpdateMassWhenScaleChanges, _updateMassWhenScaleChanges);
}
PxActor* RigidBody::GetPhysXActor()
void* RigidBody::GetPhysicsActor() const
{
return (PxActor*)_actor;
}
PxRigidActor* RigidBody::GetRigidActor()
{
return (PxRigidActor*)_actor;
return _actor;
}
void RigidBody::BeginPlay(SceneBeginData* data)
{
CreateActor();
// Create rigid body
ASSERT(_actor == nullptr);
_actor = PhysicsBackend::CreateRigidDynamicActor(this, _transform.Translation, _transform.Orientation);
// Apply properties
auto actorFlags = PhysicsBackend::ActorFlags::None;
if (!_enableSimulation || !IsActiveInHierarchy())
actorFlags |= PhysicsBackend::ActorFlags::NoSimulation;
if (!_enableGravity)
actorFlags |= PhysicsBackend::ActorFlags::NoGravity;
PhysicsBackend::SetActorFlags(_actor, actorFlags);
auto rigidBodyFlags = PhysicsBackend::RigidDynamicFlags::None;
if (_isKinematic)
rigidBodyFlags |= PhysicsBackend::RigidDynamicFlags::Kinematic;
if (_useCCD)
rigidBodyFlags |= PhysicsBackend::RigidDynamicFlags::CCD;
PhysicsBackend::SetRigidDynamicActorFlags(_actor, rigidBodyFlags);
PhysicsBackend::SetRigidDynamicActorLinearDamping(_actor, _linearDamping);
PhysicsBackend::SetRigidDynamicActorAngularDamping(_actor, _angularDamping);
PhysicsBackend::SetRigidDynamicActorMaxAngularVelocity(_actor, _maxAngularVelocity);
PhysicsBackend::SetRigidDynamicActorConstraints(_actor, _constraints);
// Find colliders to attach
for (int32 i = 0; i < Children.Count(); i++)
{
auto collider = dynamic_cast<Collider*>(Children[i]);
if (collider && collider->CanAttach(this))
{
collider->Attach(this);
}
}
// Setup mass (calculate or use overriden value)
UpdateMass();
// Apply the Center Of Mass offset
if (!_centerOfMassOffset.IsZero())
PhysicsBackend::SetRigidDynamicActorCenterOfMassOffset(_actor, _centerOfMassOffset);
// Register actor
void* scene = GetPhysicsScene()->GetPhysicsScene();
PhysicsBackend::AddSceneActor(scene, _actor);
const bool putToSleep = !_startAwake && GetEnableSimulation() && !GetIsKinematic() && IsActiveInHierarchy();
if (putToSleep)
PhysicsBackend::AddSceneActorAction(scene, _actor, PhysicsBackend::ActionType::Sleep);
// Update cached data
UpdateBounds();
// Base
PhysicsActor::BeginPlay(data);
@@ -566,24 +446,15 @@ void RigidBody::BeginPlay(SceneBeginData* data)
void RigidBody::EndPlay()
{
// Detach all the shapes
PxShape* shapes[8];
while (_actor && _actor->getNbShapes() > 0)
{
const uint32 count = _actor->getShapes(shapes, 8, 0);
for (uint32 i = 0; i < count; i++)
{
_actor->detachShape(*shapes[i], false);
}
}
// Base
PhysicsActor::EndPlay();
if (_actor)
{
// Remove actor
GetPhysicsScene()->RemoveActor(_actor);
void* scene = GetPhysicsScene()->GetPhysicsScene();
PhysicsBackend::RemoveSceneActor(scene, _actor);
PhysicsBackend::DestroyActor(_actor);
_actor = nullptr;
}
}
@@ -596,7 +467,7 @@ void RigidBody::OnActiveInTreeChanged()
if (_actor)
{
const bool isActive = _enableSimulation && IsActiveInHierarchy();
_actor->setActorFlag(PxActorFlag::eDISABLE_SIMULATION, !isActive);
PhysicsBackend::SetActorFlag(_actor, PhysicsBackend::ActorFlags::NoSimulation, !isActive);
// Auto wake up
if (isActive && GetStartAwake())
@@ -615,11 +486,8 @@ void RigidBody::OnTransformChanged()
// Base (skip PhysicsActor call to optimize)
Actor::OnTransformChanged();
const PxTransform trans(C2P(_transform.Translation), C2P(_transform.Orientation));
if (GetIsKinematic() && GetEnableSimulation())
_actor->setKinematicTarget(trans);
else
_actor->setGlobalPose(trans, true);
const bool kinematic = GetIsKinematic() && GetEnableSimulation();
PhysicsBackend::SetRigidActorPose(_actor, _transform.Translation, _transform.Orientation, kinematic, true);
UpdateScale();
UpdateBounds();
@@ -633,12 +501,10 @@ void RigidBody::OnTransformChanged()
void RigidBody::OnPhysicsSceneChanged(PhysicsScene* previous)
{
ASSERT(previous);
PhysicsActor::OnPhysicsSceneChanged(previous);
previous->UnlinkActor(_actor);
PhysicsBackend::RemoveSceneActor(previous->GetPhysicsScene(), _actor);
void* scene = GetPhysicsScene()->GetPhysicsScene();
PhysicsBackend::AddSceneActor(scene, _actor);
const bool putToSleep = !_startAwake && GetEnableSimulation() && !GetIsKinematic() && IsActiveInHierarchy();
GetPhysicsScene()->AddActor(_actor, putToSleep);
if (putToSleep)
PhysicsBackend::AddSceneActorAction(scene, _actor, PhysicsBackend::ActionType::Sleep);
}

View File

@@ -6,6 +6,7 @@
#include "Engine/Physics/Collisions.h"
class PhysicsColliderActor;
class Collider;
/// <summary>
/// Physics simulation driven object.
@@ -16,7 +17,7 @@ API_CLASS() class FLAXENGINE_API RigidBody : public PhysicsActor
DECLARE_SCENE_OBJECT(RigidBody);
protected:
PxRigidDynamic* _actor;
void* _actor;
Vector3 _cachedScale;
float _mass;
@@ -386,15 +387,6 @@ public:
/// </summary>
API_FUNCTION() void UpdateMass();
/// <summary>
/// Gets the native PhysX rigid actor object.
/// </summary>
/// <returns>The PhysX dynamic rigid actor.</returns>
FORCE_INLINE PxRigidDynamic* GetPhysXRigidActor() const
{
return _actor;
}
/// <summary>
/// Applies a force (or impulse) defined in the world space to the rigidbody at its center of mass.
/// </summary>
@@ -547,11 +539,6 @@ public:
protected:
/// <summary>
/// Creates the physics actor.
/// </summary>
void CreateActor();
/// <summary>
/// Updates the rigidbody scale dependent properties like mass (may be modified when actor transformation changes).
/// </summary>
@@ -562,8 +549,7 @@ public:
// [PhysicsActor]
void Serialize(SerializeStream& stream, const void* otherObj) override;
void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override;
PxActor* GetPhysXActor() override;
PxRigidActor* GetRigidActor() override;
void* GetPhysicsActor() const override;
protected:

View File

@@ -1,47 +1,15 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
#include "WheeledVehicle.h"
#include "Engine/Core/Log.h"
#include "Engine/Physics/Physics.h"
#include "Engine/Physics/Utilities.h"
#include "Engine/Level/Scene/Scene.h"
#include "Engine/Physics/PhysicsBackend.h"
#include "Engine/Physics/PhysicsScene.h"
#include "Engine/Serialization/Serialization.h"
#if USE_EDITOR
#include "Engine/Level/Scene/SceneRendering.h"
#include "Engine/Debug/DebugDraw.h"
#endif
#if WITH_VEHICLE
#include <ThirdParty/PhysX/vehicle/PxVehicleSDK.h>
#include <ThirdParty/PhysX/vehicle/PxVehicleNoDrive.h>
#include <ThirdParty/PhysX/vehicle/PxVehicleDrive4W.h>
#include <ThirdParty/PhysX/vehicle/PxVehicleDriveNW.h>
#include <ThirdParty/PhysX/vehicle/PxVehicleUtilSetup.h>
#include <ThirdParty/PhysX/PxFiltering.h>
#endif
#if WITH_VEHICLE
extern void InitVehicleSDK();
#endif
namespace
{
void FreeDrive(WheeledVehicle::DriveTypes driveType, PxVehicleWheels* drive)
{
switch (driveType)
{
case WheeledVehicle::DriveTypes::Drive4W:
((PxVehicleDrive4W*)drive)->free();
break;
case WheeledVehicle::DriveTypes::DriveNW:
((PxVehicleDriveNW*)drive)->free();
break;
case WheeledVehicle::DriveTypes::NoDrive:
((PxVehicleNoDrive*)drive)->free();
break;
}
}
}
WheeledVehicle::WheeledVehicle(const SpawnParams& params)
: RigidBody(params)
@@ -101,12 +69,8 @@ WheeledVehicle::GearboxSettings WheeledVehicle::GetGearbox() const
void WheeledVehicle::SetGearbox(const GearboxSettings& value)
{
#if WITH_VEHICLE
auto& drive = (PxVehicleDrive*&)_drive;
if (drive)
{
drive->mDriveDynData.setUseAutoGears(value.AutoGear);
drive->mDriveDynData.setAutoBoxSwitchTime(Math::Max(value.SwitchTime, 0.0f));
}
if (_vehicle)
PhysicsBackend::SetVehicleGearbox(_vehicle, &value);
#endif
_gearbox = value;
}
@@ -142,8 +106,7 @@ void WheeledVehicle::ClearInput()
float WheeledVehicle::GetForwardSpeed() const
{
#if WITH_VEHICLE
auto& drive = (const PxVehicleWheels*&)_drive;
return drive ? drive->computeForwardSpeed() : 0.0f;
return _vehicle ? PhysicsBackend::GetVehicleForwardSpeed(_vehicle) : 0.0f;
#else
return 0.0f;
#endif
@@ -152,8 +115,7 @@ float WheeledVehicle::GetForwardSpeed() const
float WheeledVehicle::GetSidewaysSpeed() const
{
#if WITH_VEHICLE
auto& drive = (const PxVehicleWheels*&)_drive;
return drive ? drive->computeSidewaysSpeed() : 0.0f;
return _vehicle ? PhysicsBackend::GetVehicleSidewaysSpeed(_vehicle) : 0.0f;
#else
return 0.0f;
#endif
@@ -162,8 +124,7 @@ float WheeledVehicle::GetSidewaysSpeed() const
float WheeledVehicle::GetEngineRotationSpeed() const
{
#if WITH_VEHICLE
auto& drive = (const PxVehicleDrive*&)_drive;
return drive && _driveType != DriveTypes::NoDrive ? RadPerSToRpm(drive->mDriveDynData.getEngineRotationSpeed()) : 0.0f;
return _vehicle && _driveType != DriveTypes::NoDrive ? PhysicsBackend::GetVehicleEngineRotationSpeed(_vehicle) : 0.0f;
#else
return 0.0f;
#endif
@@ -172,8 +133,7 @@ float WheeledVehicle::GetEngineRotationSpeed() const
int32 WheeledVehicle::GetCurrentGear() const
{
#if WITH_VEHICLE
auto& drive = (const PxVehicleDrive*&)_drive;
return drive && _driveType != DriveTypes::NoDrive ? (int32)drive->mDriveDynData.getCurrentGear() - 1 : 0;
return _vehicle && _driveType != DriveTypes::NoDrive ? PhysicsBackend::GetVehicleCurrentGear(_vehicle) : 0;
#else
return 0;
#endif
@@ -182,19 +142,15 @@ int32 WheeledVehicle::GetCurrentGear() const
void WheeledVehicle::SetCurrentGear(int32 value)
{
#if WITH_VEHICLE
auto& drive = (PxVehicleDrive*&)_drive;
if (drive && _driveType != DriveTypes::NoDrive)
{
drive->mDriveDynData.forceGearChange((PxU32)(value + 1));
}
if (_vehicle && _driveType != DriveTypes::NoDrive)
PhysicsBackend::SetVehicleCurrentGear(_vehicle, value);
#endif
}
int32 WheeledVehicle::GetTargetGear() const
{
#if WITH_VEHICLE
auto& drive = (const PxVehicleDrive*&)_drive;
return drive && _driveType != DriveTypes::NoDrive ? (int32)drive->mDriveDynData.getTargetGear() - 1 : 0;
return _vehicle && _driveType != DriveTypes::NoDrive ? PhysicsBackend::GetVehicleTargetGear(_vehicle) : 0;
#else
return 0;
#endif
@@ -203,11 +159,8 @@ int32 WheeledVehicle::GetTargetGear() const
void WheeledVehicle::SetTargetGear(int32 value)
{
#if WITH_VEHICLE
auto& drive = (PxVehicleDrive*&)_drive;
if (drive && _driveType != DriveTypes::NoDrive)
{
drive->mDriveDynData.startGearChange((PxU32)(value + 1));
}
if (_vehicle && _driveType != DriveTypes::NoDrive)
PhysicsBackend::SetVehicleTargetGear(_vehicle, value);
#endif
}
@@ -232,281 +185,25 @@ void WheeledVehicle::Setup()
#if WITH_VEHICLE
if (!_actor || !IsDuringPlay())
return;
auto& drive = (PxVehicleWheels*&)_drive;
// Release previous
if (drive)
if (_vehicle)
{
GetPhysicsScene()->RemoveWheeledVehicle(this);
FreeDrive(_driveTypeCurrent, drive);
drive = nullptr;
PhysicsBackend::RemoveVehicle(GetPhysicsScene()->GetPhysicsScene(), this);
PhysicsBackend::DestroyVehicle(_vehicle, (int32)_driveTypeCurrent);
_vehicle = nullptr;
}
// Get wheels
Array<Wheel*, FixedAllocation<PX_MAX_NB_WHEELS>> wheels;
// Create a new one
_wheelsData.Clear();
for (auto& wheel : _wheels)
{
if (!wheel.Collider)
{
LOG(Warning, "Missing wheel collider in vehicle {0}", ToString());
continue;
}
if (wheel.Collider->GetParent() != this)
{
LOG(Warning, "Invalid wheel collider {1} in vehicle {0} attached to {2} (wheels needs to be added as children to vehicle)", ToString(), wheel.Collider->ToString(), wheel.Collider->GetParent() ? wheel.Collider->GetParent()->ToString() : String::Empty);
continue;
}
if (wheel.Collider->GetIsTrigger())
{
LOG(Warning, "Invalid wheel collider {1} in vehicle {0} cannot be a trigger", ToString(), wheel.Collider->ToString());
continue;
}
if (wheel.Collider->IsDuringPlay())
{
wheels.Add(&wheel);
}
}
if (wheels.IsEmpty())
{
// No wheel, no car
// No woman, no cry
_vehicle = PhysicsBackend::CreateVehicle(this);
if (!_vehicle)
return;
}
_wheelsData.Resize(wheels.Count());
InitVehicleSDK();
// Get linked shapes for the wheels mapping
Array<PxShape*, InlinedAllocation<8>> shapes;
shapes.Resize(_actor->getNbShapes());
_actor->getShapes(shapes.Get(), shapes.Count(), 0);
const PxTransform centerOfMassOffset = _actor->getCMassLocalPose();
// Initialize wheels simulation data
PxVec3 offsets[PX_MAX_NB_WHEELS];
for (int32 i = 0; i < wheels.Count(); i++)
{
Wheel& wheel = *wheels[i];
offsets[i] = C2P(wheel.Collider->GetLocalPosition());
}
PxF32 sprungMasses[PX_MAX_NB_WHEELS];
const float mass = _actor->getMass();
PxVehicleComputeSprungMasses(wheels.Count(), offsets, centerOfMassOffset.p, mass, 1, sprungMasses);
PxVehicleWheelsSimData* wheelsSimData = PxVehicleWheelsSimData::allocate(wheels.Count());
for (int32 i = 0; i < wheels.Count(); i++)
{
Wheel& wheel = *wheels[i];
auto& data = _wheelsData[i];
data.Collider = wheel.Collider;
data.LocalOrientation = wheel.Collider->GetLocalOrientation();
PxVehicleSuspensionData suspensionData;
const float suspensionFrequency = 7.0f;
suspensionData.mMaxCompression = wheel.SuspensionMaxRaise;
suspensionData.mMaxDroop = wheel.SuspensionMaxDrop;
suspensionData.mSprungMass = sprungMasses[i];
suspensionData.mSpringStrength = Math::Square(suspensionFrequency) * suspensionData.mSprungMass;
suspensionData.mSpringDamperRate = wheel.SuspensionDampingRate * 2.0f * Math::Sqrt(suspensionData.mSpringStrength * suspensionData.mSprungMass);
PxVehicleTireData tire;
tire.mType = 0;
tire.mLatStiffX = wheel.TireLateralMax;
tire.mLatStiffY = wheel.TireLateralStiffness;
tire.mLongitudinalStiffnessPerUnitGravity = wheel.TireLongitudinalStiffness;
PxVehicleWheelData wheelData;
wheelData.mMass = wheel.Mass;
wheelData.mRadius = wheel.Radius;
wheelData.mWidth = wheel.Width;
wheelData.mMOI = 0.5f * wheelData.mMass * Math::Square(wheelData.mRadius);
wheelData.mDampingRate = M2ToCm2(wheel.DampingRate);
wheelData.mMaxSteer = wheel.MaxSteerAngle * DegreesToRadians;
wheelData.mMaxBrakeTorque = M2ToCm2(wheel.MaxBrakeTorque);
wheelData.mMaxHandBrakeTorque = M2ToCm2(wheel.MaxHandBrakeTorque);
PxVec3 centreOffset = centerOfMassOffset.transformInv(offsets[i]);
PxVec3 forceAppPointOffset(centreOffset.x, wheel.SuspensionForceOffset, centreOffset.z);
wheelsSimData->setTireData(i, tire);
wheelsSimData->setWheelData(i, wheelData);
wheelsSimData->setSuspensionData(i, suspensionData);
wheelsSimData->setSuspTravelDirection(i, centerOfMassOffset.rotate(PxVec3(0.0f, -1.0f, 0.0f)));
wheelsSimData->setWheelCentreOffset(i, centreOffset);
wheelsSimData->setSuspForceAppPointOffset(i, forceAppPointOffset);
wheelsSimData->setTireForceAppPointOffset(i, forceAppPointOffset);
wheelsSimData->setSubStepCount(4.0f * 100.0f, 3, 1);
wheelsSimData->setMinLongSlipDenominator(4.0f * 100.0f);
PxShape* wheelShape = wheel.Collider->GetPxShape();
if (wheel.Collider->IsActiveInHierarchy())
{
wheelsSimData->setWheelShapeMapping(i, shapes.Find(wheelShape));
// Setup Vehicle ID inside word3 for suspension raycasts to ignore self
PxFilterData filter = wheelShape->getQueryFilterData();
filter.word3 = _id.D + 1;
wheelShape->setQueryFilterData(filter);
wheelShape->setSimulationFilterData(filter);
wheelsSimData->setSceneQueryFilterData(i, filter);
// Remove wheels from the simulation (suspension force hold the vehicle)
wheelShape->setFlag(PxShapeFlag::eSIMULATION_SHAPE, false);
}
else
{
wheelsSimData->setWheelShapeMapping(i, -1);
wheelsSimData->disableWheel(i);
}
}
for (auto child : Children)
{
auto collider = Cast<Collider>(child);
if (collider && collider->GetAttachedRigidBody() == this)
{
bool isWheel = false;
for (auto& w : wheels)
{
if (w->Collider == collider)
{
isWheel = true;
break;
}
}
if (!isWheel)
{
// Setup Vehicle ID inside word3 for suspension raycasts to ignore self
PxShape* shape = collider->GetPxShape();
PxFilterData filter = shape->getQueryFilterData();
filter.word3 = _id.D + 1;
shape->setQueryFilterData(filter);
shape->setSimulationFilterData(filter);
}
}
}
// Initialize vehicle drive
_driveTypeCurrent = _driveType;
switch (_driveType)
{
case DriveTypes::Drive4W:
{
PxVehicleDriveSimData4W driveSimData;
// Differential
PxVehicleDifferential4WData diff;
diff.mType = (PxVehicleDifferential4WData::Enum)_differential.Type;
diff.mFrontRearSplit = _differential.FrontRearSplit;
diff.mFrontLeftRightSplit = _differential.FrontLeftRightSplit;
diff.mRearLeftRightSplit = _differential.RearLeftRightSplit;
diff.mCentreBias = _differential.CentreBias;
diff.mFrontBias = _differential.FrontBias;
diff.mRearBias = _differential.RearBias;
driveSimData.setDiffData(diff);
// Engine
PxVehicleEngineData engine;
engine.mMOI = M2ToCm2(_engine.MOI);
engine.mPeakTorque = M2ToCm2(_engine.MaxTorque);
engine.mMaxOmega = RpmToRadPerS(_engine.MaxRotationSpeed);
engine.mDampingRateFullThrottle = M2ToCm2(0.15f);
engine.mDampingRateZeroThrottleClutchEngaged = M2ToCm2(2.0f);
engine.mDampingRateZeroThrottleClutchDisengaged = M2ToCm2(0.35f);
driveSimData.setEngineData(engine);
// Gears
PxVehicleGearsData gears;
gears.mSwitchTime = Math::Max(_gearbox.SwitchTime, 0.0f);
driveSimData.setGearsData(gears);
// Auto Box
PxVehicleAutoBoxData autoBox;
driveSimData.setAutoBoxData(autoBox);
// Clutch
PxVehicleClutchData clutch;
clutch.mStrength = M2ToCm2(_gearbox.ClutchStrength);
driveSimData.setClutchData(clutch);
// Ackermann steer accuracy
PxVehicleAckermannGeometryData ackermann;
ackermann.mAxleSeparation = Math::Abs(wheelsSimData->getWheelCentreOffset(PxVehicleDrive4WWheelOrder::eFRONT_LEFT).x - wheelsSimData->getWheelCentreOffset(PxVehicleDrive4WWheelOrder::eREAR_LEFT).x);
ackermann.mFrontWidth = Math::Abs(wheelsSimData->getWheelCentreOffset(PxVehicleDrive4WWheelOrder::eFRONT_RIGHT).z - wheelsSimData->getWheelCentreOffset(PxVehicleDrive4WWheelOrder::eFRONT_LEFT).z);
ackermann.mRearWidth = Math::Abs(wheelsSimData->getWheelCentreOffset(PxVehicleDrive4WWheelOrder::eREAR_RIGHT).z - wheelsSimData->getWheelCentreOffset(PxVehicleDrive4WWheelOrder::eREAR_LEFT).z);
driveSimData.setAckermannGeometryData(ackermann);
// Create vehicle drive
auto drive4W = PxVehicleDrive4W::allocate(wheels.Count());
drive4W->setup(CPhysX, _actor, *wheelsSimData, driveSimData, Math::Max(wheels.Count() - 4, 0));
drive4W->setToRestState();
drive4W->mDriveDynData.forceGearChange(PxVehicleGearsData::eFIRST);
drive4W->mDriveDynData.setUseAutoGears(_gearbox.AutoGear);
drive = drive4W;
break;
}
case DriveTypes::DriveNW:
{
PxVehicleDriveSimDataNW driveSimData;
// Differential
PxVehicleDifferentialNWData diff;
for (int32 i = 0; i < wheels.Count(); i++)
diff.setDrivenWheel(i, true);
driveSimData.setDiffData(diff);
// Engine
PxVehicleEngineData engine;
engine.mMOI = M2ToCm2(_engine.MOI);
engine.mPeakTorque = M2ToCm2(_engine.MaxTorque);
engine.mMaxOmega = RpmToRadPerS(_engine.MaxRotationSpeed);
engine.mDampingRateFullThrottle = M2ToCm2(0.15f);
engine.mDampingRateZeroThrottleClutchEngaged = M2ToCm2(2.0f);
engine.mDampingRateZeroThrottleClutchDisengaged = M2ToCm2(0.35f);
driveSimData.setEngineData(engine);
// Gears
PxVehicleGearsData gears;
gears.mSwitchTime = Math::Max(_gearbox.SwitchTime, 0.0f);
driveSimData.setGearsData(gears);
// Auto Box
PxVehicleAutoBoxData autoBox;
driveSimData.setAutoBoxData(autoBox);
// Clutch
PxVehicleClutchData clutch;
clutch.mStrength = M2ToCm2(_gearbox.ClutchStrength);
driveSimData.setClutchData(clutch);
// Create vehicle drive
auto driveNW = PxVehicleDriveNW::allocate(wheels.Count());
driveNW->setup(CPhysX, _actor, *wheelsSimData, driveSimData, wheels.Count());
driveNW->setToRestState();
driveNW->mDriveDynData.forceGearChange(PxVehicleGearsData::eFIRST);
driveNW->mDriveDynData.setUseAutoGears(_gearbox.AutoGear);
drive = driveNW;
break;
}
case DriveTypes::NoDrive:
{
// Create vehicle drive
auto driveNo = PxVehicleNoDrive::allocate(wheels.Count());
driveNo->setup(CPhysX, _actor, *wheelsSimData);
driveNo->setToRestState();
drive = driveNo;
break;
}
default:
CRASH;
}
GetPhysicsScene()->AddWheeledVehicle(this);
wheelsSimData->free();
_actor->setSolverIterationCounts(12, 4);
PhysicsBackend::AddVehicle(GetPhysicsScene()->GetPhysicsScene(), this);
PhysicsBackend::SetRigidDynamicActorSolverIterationCounts(_actor, 12, 4);
#else
LOG(Fatal, "PhysX Vehicle SDK is not supported.");
LOG(Fatal, "Vehicles are not supported.");
#endif
}
@@ -560,9 +257,12 @@ void WheeledVehicle::OnDebugDrawSelected()
{
const Vector3 currentPos = wheel.Collider->GetPosition();
const Vector3 basePos = currentPos - Vector3(0, data.State.SuspensionOffset, 0);
Transform actorPose = Transform::Identity, shapePose = Transform::Identity;
PhysicsBackend::GetRigidActorPose(_actor, actorPose.Translation, actorPose.Orientation);
PhysicsBackend::GetShapeLocalPose(wheel.Collider->GetPhysicsShape(), shapePose.Translation, shapePose.Orientation);
DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(basePos, wheel.Radius * 0.07f), Color::Blue * 0.3f, 0, false);
DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(currentPos, wheel.Radius * 0.08f), Color::Blue * 0.8f, 0, false);
DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(P2C(_actor->getGlobalPose().transform(wheel.Collider->GetPxShape()->getLocalPose()).p), wheel.Radius * 0.11f), Color::OrangeRed * 0.8f, 0, false);
DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(actorPose.LocalToWorld(shapePose.Translation), wheel.Radius * 0.11f), Color::OrangeRed * 0.8f, 0, false);
DEBUG_DRAW_LINE(basePos, currentPos, Color::Blue, 0, false);
DEBUG_DRAW_WIRE_CYLINDER(currentPos, wheel.Collider->GetOrientation(), wheel.Radius, wheel.Width, Color::Red * 0.4f, 0, false);
if (!data.State.SuspensionTraceStart.IsZero())
@@ -625,8 +325,10 @@ void WheeledVehicle::OnPhysicsSceneChanged(PhysicsScene* previous)
{
RigidBody::OnPhysicsSceneChanged(previous);
previous->RemoveWheeledVehicle(this);
GetPhysicsScene()->AddWheeledVehicle(this);
#if WITH_VEHICLE
PhysicsBackend::RemoveVehicle(previous->GetPhysicsScene(), this);
PhysicsBackend::AddVehicle(GetPhysicsScene()->GetPhysicsScene(), this);
#endif
}
void WheeledVehicle::BeginPlay(SceneBeginData* data)
@@ -649,13 +351,12 @@ void WheeledVehicle::EndPlay()
#endif
#if WITH_VEHICLE
auto& drive = (PxVehicleWheels*&)_drive;
if (drive)
if (_vehicle)
{
// Parkway Drive
GetPhysicsScene()->RemoveWheeledVehicle(this);
FreeDrive(_driveTypeCurrent, drive);
drive = nullptr;
PhysicsBackend::RemoveVehicle(GetPhysicsScene()->GetPhysicsScene(), this);
PhysicsBackend::DestroyVehicle(_vehicle, (int32)_driveTypeCurrent);
_vehicle = nullptr;
}
#endif

View File

@@ -6,17 +6,14 @@
#include "Engine/Physics/Colliders/Collider.h"
#include "Engine/Scripting/ScriptingObjectReference.h"
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.
/// </summary>
/// <seealso cref="RigidBody" />
API_CLASS() class FLAXENGINE_API WheeledVehicle : public RigidBody
{
friend PhysicsScene;
friend class PhysicsBackend;
DECLARE_SCENE_OBJECT(WheeledVehicle);
public:
/// <summary>
/// Vehicle driving mode types.
@@ -319,7 +316,7 @@ private:
WheelState State;
};
void* _drive = nullptr;
void* _vehicle = nullptr;
DriveTypes _driveType = DriveTypes::Drive4W, _driveTypeCurrent;
Array<WheelData, FixedAllocation<20>> _wheelsData;
float _throttle = 0.0f, _steering = 0.0f, _brake = 0.0f, _handBrake = 0.0f;

View File

@@ -2,8 +2,7 @@
#include "BoxCollider.h"
#include "Engine/Serialization/Serialization.h"
#include "Engine/Physics/Utilities.h"
#include <ThirdParty/PhysX/PxShape.h>
#include "Engine/Physics/PhysicsBackend.h"
BoxCollider::BoxCollider(const SpawnParams& params)
: Collider(params)
@@ -134,11 +133,11 @@ void BoxCollider::UpdateBounds()
BoundingSphere::FromBox(_box, _sphere);
}
void BoxCollider::GetGeometry(PxGeometryHolder& geometry)
void BoxCollider::GetGeometry(CollisionShape& collision)
{
Vector3 size = _size * _cachedScale;
size.Absolute();
const float minSize = 0.001f;
const PxBoxGeometry box(Math::Max(size.X * 0.5f, minSize), Math::Max(size.Y * 0.5f, minSize), Math::Max(size.Z * 0.5f, minSize));
geometry.storeAny(box);
size = Vector3::Max(size * 0.5f, Vector3(minSize));
collision.SetBox(size.Raw);
}

View File

@@ -62,7 +62,7 @@ protected:
// [Collider]
void UpdateBounds() override;
void GetGeometry(PxGeometryHolder& geometry) override;
void GetGeometry(CollisionShape& collision) override;
#if USE_EDITOR
void DrawPhysicsDebug(RenderView& view) override;
#endif

View File

@@ -2,8 +2,6 @@
#include "CapsuleCollider.h"
#include "Engine/Serialization/Serialization.h"
#include "Engine/Physics/Utilities.h"
#include <ThirdParty/PhysX/PxShape.h>
CapsuleCollider::CapsuleCollider(const SpawnParams& params)
: Collider(params)
@@ -106,12 +104,11 @@ void CapsuleCollider::UpdateBounds()
BoundingSphere::FromBox(_box, _sphere);
}
void CapsuleCollider::GetGeometry(PxGeometryHolder& geometry)
void CapsuleCollider::GetGeometry(CollisionShape& collision)
{
const float scaling = _cachedScale.GetAbsolute().MaxValue();
const float minSize = 0.001f;
const float radius = Math::Max(Math::Abs(_radius) * scaling, minSize);
const float height = Math::Max(Math::Abs(_height) * scaling, minSize);
const PxCapsuleGeometry capsule(radius, height * 0.5f);
geometry.storeAny(capsule);
collision.SetCapsule(radius, height * 0.5f);
}

View File

@@ -77,7 +77,7 @@ protected:
// [Collider]
void UpdateBounds() override;
void GetGeometry(PxGeometryHolder& geometry) override;
void GetGeometry(CollisionShape& collision) override;
#if USE_EDITOR
void DrawPhysicsDebug(RenderView& view) override;
#endif

View File

@@ -1,18 +1,12 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
#include "CharacterController.h"
#include "Engine/Physics/Utilities.h"
#include "Engine/Physics/Colliders/Collider.h"
#include "Engine/Physics/Physics.h"
#include "Engine/Physics/PhysicsBackend.h"
#include "Engine/Physics/PhysicsScene.h"
#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/characterkinematic/PxController.h>
#include <ThirdParty/PhysX/characterkinematic/PxControllerManager.h>
#include <ThirdParty/PhysX/characterkinematic//PxCapsuleController.h>
CharacterController::CharacterController(const SpawnParams& params)
: Collider(params)
@@ -27,7 +21,6 @@ CharacterController::CharacterController(const SpawnParams& params)
, _nonWalkableMode(NonWalkableModes::PreventClimbing)
, _lastFlags(CollisionFlags::None)
{
static_assert(sizeof(_filterData) == sizeof(PxFilterData), "Invalid filter data size.");
}
float CharacterController::GetRadius() const
@@ -72,11 +65,9 @@ void CharacterController::SetSlopeLimit(float value)
value = Math::Clamp(value, 0.0f, 89.0f);
if (Math::NearEqual(value, _slopeLimit))
return;
_slopeLimit = value;
if (_controller)
_controller->setSlopeLimit(Math::Cos(value * DegreesToRadians));
PhysicsBackend::SetControllerSlopeLimit(_controller, value);
}
CharacterController::NonWalkableModes CharacterController::GetNonWalkableMode() const
@@ -86,10 +77,11 @@ CharacterController::NonWalkableModes CharacterController::GetNonWalkableMode()
void CharacterController::SetNonWalkableMode(NonWalkableModes value)
{
if (_nonWalkableMode == value)
return;
_nonWalkableMode = value;
if (_controller)
_controller->setNonWalkableMode(static_cast<PxControllerNonWalkableMode::Enum>(value));
PhysicsBackend::SetControllerNonWalkableMode(_controller, (int32)value);
}
float CharacterController::GetStepOffset() const
@@ -111,15 +103,15 @@ void CharacterController::SetStepOffset(float value)
const float minSize = 0.001f;
const float height = Math::Max(Math::Abs(_height) * scaling, minSize);
const float radius = Math::Max(Math::Abs(_radius) * scaling - contactOffset, minSize);
_controller->setStepOffset(Math::Min(value, height + radius * 2.0f - minSize));
PhysicsBackend::SetControllerStepOffset(_controller, Math::Min(value, height + radius * 2.0f - minSize));
}
}
void CharacterController::SetUpDirection(const Vector3& up)
{
if (_controller)
_controller->setUpDirection(C2P(up));
_upDirection = up;
if (_controller)
PhysicsBackend::SetControllerUpDirection(_controller, up);
}
float CharacterController::GetMinMoveDistance() const
@@ -129,7 +121,7 @@ float CharacterController::GetMinMoveDistance() const
Vector3 CharacterController::GetUpDirection() const
{
return _controller ? P2C(_controller->getUpDirection()) : _upDirection;
return _controller ? PhysicsBackend::GetControllerUpDirection(_controller) : _upDirection;
}
void CharacterController::SetMinMoveDistance(float value)
@@ -139,7 +131,7 @@ void CharacterController::SetMinMoveDistance(float value)
Vector3 CharacterController::GetVelocity() const
{
return _controller ? P2C(_controller->getActor()->getLinearVelocity()) : Vector3::Zero;
return _controller ? PhysicsBackend::GetRigidDynamicActorLinearVelocity(PhysicsBackend::GetControllerRigidDynamicActor(_controller)) : Vector3::Zero;
}
bool CharacterController::IsGrounded() const
@@ -165,30 +157,16 @@ CharacterController::CollisionFlags CharacterController::SimpleMove(const Vector
CharacterController::CollisionFlags CharacterController::Move(const Vector3& displacement)
{
CollisionFlags result = CollisionFlags::None;
if (_controller)
{
const float deltaTime = Time::GetCurrentSafe()->DeltaTime.GetTotalSeconds();
PxControllerFilters filters;
filters.mFilterData = (PxFilterData*)&_filterData;
filters.mFilterCallback = GetPhysicsScene()->GetCharacterQueryFilterCallback();
filters.mFilterFlags = PxQueryFlag::eDYNAMIC | PxQueryFlag::eSTATIC | PxQueryFlag::ePREFILTER;
filters.mCCTFilterCallback = GetPhysicsScene()->GetCharacterControllerFilterCallback();
result = (CollisionFlags)(byte)_controller->move(C2P(displacement), _minMoveDistance, deltaTime, filters);
result = (CollisionFlags)PhysicsBackend::MoveController(_controller, _shape, displacement, _minMoveDistance, deltaTime);
_lastFlags = result;
SetPosition(P2C(_controller->getPosition()));
SetPosition(PhysicsBackend::GetControllerPosition(_controller));
}
return result;
}
PxRigidDynamic* CharacterController::GetPhysXRigidActor() const
{
return _controller ? _controller->getActor() : nullptr;
}
#if USE_EDITOR
#include "Engine/Debug/DebugDraw.h"
@@ -222,41 +200,16 @@ void CharacterController::OnDebugDrawSelected()
void CharacterController::CreateController()
{
// Create controller
ASSERT(_controller == nullptr && _shape == nullptr);
PxCapsuleControllerDesc desc;
desc.userData = this;
desc.contactOffset = Math::Max(_contactOffset, ZeroTolerance);
desc.position = PxExtendedVec3(_transform.Translation.X, _transform.Translation.Y, _transform.Translation.Z);
desc.slopeLimit = Math::Cos(_slopeLimit * DegreesToRadians);
desc.nonWalkableMode = static_cast<PxControllerNonWalkableMode::Enum>(_nonWalkableMode);
desc.climbingMode = PxCapsuleClimbingMode::eEASY;
if (Material && !Material->WaitForLoaded())
desc.material = ((PhysicalMaterial*)Material->Instance)->GetPhysXMaterial();
else
desc.material = Physics::GetDefaultMaterial();
_cachedScale = GetScale();
const float scaling = _cachedScale.GetAbsolute().MaxValue();
const float minSize = 0.001f;
desc.height = Math::Max(Math::Abs(_height) * scaling, minSize);
desc.radius = Math::Max(Math::Abs(_radius) * scaling - desc.contactOffset, minSize);
desc.stepOffset = Math::Min(_stepOffset, desc.height + desc.radius * 2.0f - minSize);
_controller = PhysicsBackend::CreateController(GetPhysicsScene()->GetPhysicsScene(), this, this, _contactOffset, _transform.Translation, _slopeLimit, (int32)_nonWalkableMode, Material.Get(), Math::Abs(_radius) * scaling, Math::Abs(_height) * scaling, _stepOffset, _shape);
// Create controller
_controller = (PxCapsuleController*)GetPhysicsScene()->GetControllerManager()->createController(desc);
ASSERT(_controller);
_controller->setUpDirection(C2P(_upDirection));
const auto actor = _controller->getActor();
ASSERT(actor && actor->getNbShapes() == 1);
actor->getShapes(&_shape, 1);
actor->userData = this;
_shape->userData = this;
// Apply additional shape properties
_shape->setLocalPose(PxTransform(C2P(_center)));
// Setup
PhysicsBackend::SetControllerUpDirection(_shape, _upDirection);
PhysicsBackend::SetShapeLocalPose(_shape, _center, Quaternion::Identity);
UpdateLayerBits();
// Update cached data
UpdateBounds();
}
@@ -264,12 +217,10 @@ void CharacterController::DeleteController()
{
if (_controller)
{
_shape->userData = nullptr;
_controller->getActor()->userData = nullptr;
_controller->release();
PhysicsBackend::DestroyController(_controller);
_controller = nullptr;
_shape = nullptr;
}
_shape = nullptr;
}
void CharacterController::UpdateSize() const
@@ -280,9 +231,7 @@ void CharacterController::UpdateSize() const
const float minSize = 0.001f;
const float radius = Math::Max(Math::Abs(_radius) * scaling - Math::Max(_contactOffset, ZeroTolerance), minSize);
const float height = Math::Max(Math::Abs(_height) * scaling, minSize);
_controller->setRadius(radius);
_controller->resize(height);
PhysicsBackend::SetControllerSize(_controller, radius, height);
}
}
@@ -293,10 +242,9 @@ void CharacterController::CreateShape()
void CharacterController::UpdateBounds()
{
const auto actor = GetPhysXRigidActor();
const float boundsScale = 1.03f;
void* actor = PhysicsBackend::GetShapeActor(_shape);
if (actor)
_box = P2C(actor->getWorldBounds(boundsScale));
PhysicsBackend::GetActorBounds(actor, _box);
else
_box = BoundingBox(_transform.Translation);
BoundingSphere::FromBox(_box, _sphere);
@@ -322,22 +270,24 @@ RigidBody* CharacterController::GetAttachedRigidBody() const
return nullptr;
}
void CharacterController::OnActiveTransformChanged(const PxTransform& transform)
void CharacterController::OnActiveTransformChanged()
{
// Change actor transform (but with locking)
ASSERT(!_isUpdatingTransform);
_isUpdatingTransform = true;
Transform t = _transform;
t.Translation = P2C(transform.p);
SetTransform(t);
Transform transform;
PhysicsBackend::GetRigidActorPose(PhysicsBackend::GetShapeActor(_shape), transform.Translation, transform.Orientation);
transform.Orientation = _transform.Orientation;
transform.Scale = _transform.Scale;
SetTransform(transform);
_isUpdatingTransform = false;
UpdateBounds();
}
PxRigidActor* CharacterController::GetRigidActor()
void* CharacterController::GetPhysicsActor() const
{
return _shape ? _shape->getActor() : nullptr;
return _shape ? PhysicsBackend::GetShapeActor(_shape) : nullptr;
}
void CharacterController::UpdateGeometry()
@@ -351,20 +301,11 @@ void CharacterController::UpdateGeometry()
UpdateSize();
}
void CharacterController::GetGeometry(PxGeometryHolder& geometry)
void CharacterController::GetGeometry(CollisionShape& collision)
{
// Unused
}
void CharacterController::UpdateLayerBits()
{
// Base
Collider::UpdateLayerBits();
// Cache filter data
*(PxFilterData*)&_filterData = _shape->getSimulationFilterData();
}
void CharacterController::BeginPlay(SceneBeginData* data)
{
if (IsActiveInHierarchy())
@@ -418,9 +359,7 @@ void CharacterController::OnTransformChanged()
// Update physics
if (!_isUpdatingTransform && _controller)
{
const PxExtendedVec3 pos(_transform.Translation.X, _transform.Translation.Y, _transform.Translation.Z);
_controller->setPosition(pos);
PhysicsBackend::SetControllerPosition(_controller, _transform.Translation);
const Vector3 scale = GetScale();
if (!Vector3::NearEqual(_cachedScale, scale))
UpdateGeometry();

View File

@@ -58,7 +58,7 @@ public:
private:
PxCapsuleController* _controller;
void* _controller;
float _stepOffset;
float _slopeLimit;
float _radius;
@@ -68,7 +68,6 @@ private:
Vector3 _upDirection;
NonWalkableModes _nonWalkableMode;
CollisionFlags _lastFlags;
uint32 _filterData[4];
public:
/// <summary>
@@ -181,11 +180,6 @@ public:
/// <returns>The collision flags. It can be used to trigger various character animations.</returns>
API_FUNCTION() CollisionFlags Move(const Vector3& displacement);
/// <summary>
/// Gets the native PhysX rigid actor object.
/// </summary>
PxRigidDynamic* GetPhysXRigidActor() const;
protected:
/// <summary>
@@ -218,15 +212,14 @@ public:
RigidBody* GetAttachedRigidBody() const override;
// [IPhysicsActor]
void OnActiveTransformChanged(const PxTransform& transform) override;
PxRigidActor* GetRigidActor() override;
void OnActiveTransformChanged() override;
void* GetPhysicsActor() const override;
protected:
// [PhysicsActor]
void UpdateGeometry() override;
void GetGeometry(PxGeometryHolder& geometry) override;
void UpdateLayerBits() override;
void GetGeometry(CollisionShape& collision) override;
void BeginPlay(SceneBeginData* data) override;
void EndPlay() override;
#if USE_EDITOR

View File

@@ -6,18 +6,11 @@
#include "Engine/Level/Scene/SceneRendering.h"
#endif
#include "Engine/Serialization/Serialization.h"
#include "Engine/Physics/Utilities.h"
#include "Engine/Physics/PhysicsSettings.h"
#include "Engine/Physics/Physics.h"
#include "Engine/Physics/PhysicalMaterial.h"
#include "Engine/Physics/PhysicsBackend.h"
#include "Engine/Physics/PhysicsScene.h"
#include "Engine/Physics/Actors/RigidBody.h"
#include <ThirdParty/PhysX/geometry/PxGeometryQuery.h>
#include <ThirdParty/PhysX/PxShape.h>
#include <ThirdParty/PhysX/PxPhysics.h>
#include <ThirdParty/PhysX/PxFiltering.h>
#include <ThirdParty/PhysX/PxRigidDynamic.h>
#include <ThirdParty/PhysX/PxRigidStatic.h>
Collider::Collider(const SpawnParams& params)
: PhysicsColliderActor(params)
@@ -31,7 +24,7 @@ Collider::Collider(const SpawnParams& params)
Material.Changed.Bind<Collider, &Collider::OnMaterialChanged>(this);
}
PxShape* Collider::GetPxShape() const
void* Collider::GetPhysicsShape() const
{
return _shape;
}
@@ -40,33 +33,24 @@ void Collider::SetIsTrigger(bool value)
{
if (value == _isTrigger || !CanBeTrigger())
return;
_isTrigger = value;
if (_shape)
{
const bool isTrigger = _isTrigger && CanBeTrigger();
const PxShapeFlags shapeFlags = GetShapeFlags(isTrigger, IsActiveInHierarchy());
_shape->setFlags(shapeFlags);
}
PhysicsBackend::SetShapeState(_shape, IsActiveInHierarchy(), _isTrigger && CanBeTrigger());
}
void Collider::SetCenter(const Vector3& value)
{
if (Vector3::NearEqual(value, _center))
return;
_center = value;
if (_staticActor)
{
_shape->setLocalPose(PxTransform(C2P(_center)));
PhysicsBackend::SetShapeLocalPose(_shape, _center, Quaternion::Identity);
}
else if (const RigidBody* rigidBody = GetAttachedRigidBody())
{
_shape->setLocalPose(PxTransform(C2P((_localTransform.Translation + _localTransform.Orientation * _center) * rigidBody->GetScale()), C2P(_localTransform.Orientation)));
PhysicsBackend::SetShapeLocalPose(_shape, (_localTransform.Translation + _localTransform.Orientation * _center) * rigidBody->GetScale(), _localTransform.Orientation);
}
UpdateBounds();
}
@@ -75,13 +59,9 @@ void Collider::SetContactOffset(float value)
value = Math::Clamp(value, 0.0f, 100.0f);
if (Math::NearEqual(value, _contactOffset))
return;
_contactOffset = value;
if (_shape)
{
_shape->setContactOffset(Math::Max(_shape->getRestOffset() + ZeroTolerance, _contactOffset));
}
PhysicsBackend::SetShapeContactOffset(_shape, _contactOffset);
}
bool Collider::RayCast(const Vector3& origin, const Vector3& direction, float& resultHitDistance, float maxDistance) const
@@ -89,73 +69,36 @@ bool Collider::RayCast(const Vector3& origin, const Vector3& direction, float& r
resultHitDistance = MAX_float;
if (_shape == nullptr)
return false;
// Prepare data
const PxTransform trans(C2P(_transform.Translation), C2P(_transform.Orientation));
const PxHitFlags hitFlags = PxHitFlag::ePOSITION | PxHitFlag::eNORMAL | PxHitFlag::eFACE_INDEX | PxHitFlag::eUV;
// Perform raycast test
PxRaycastHit hit;
if (PxGeometryQuery::raycast(C2P(origin), C2P(direction), _shape->getGeometry().any(), trans, maxDistance, hitFlags, 1, &hit) != 0)
{
resultHitDistance = hit.distance;
return true;
}
return false;
return PhysicsBackend::RayCastShape(_shape, _transform.Translation, _transform.Orientation, origin, direction, resultHitDistance, maxDistance);
}
bool Collider::RayCast(const Vector3& origin, const Vector3& direction, RayCastHit& hitInfo, float maxDistance) const
{
if (_shape == nullptr)
return false;
// Prepare data
const PxTransform trans(C2P(_transform.Translation), C2P(_transform.Orientation));
const PxHitFlags hitFlags = PxHitFlag::ePOSITION | PxHitFlag::eNORMAL | PxHitFlag::eUV;
PxRaycastHit hit;
// Perform raycast test
if (PxGeometryQuery::raycast(C2P(origin), C2P(direction), _shape->getGeometry().any(), trans, maxDistance, hitFlags, 1, &hit) == 0)
return false;
// Gather results
hitInfo.Gather(hit);
return true;
return PhysicsBackend::RayCastShape(_shape, _transform.Translation, _transform.Orientation, origin, direction, hitInfo, maxDistance);
}
void Collider::ClosestPoint(const Vector3& position, Vector3& result) const
void Collider::ClosestPoint(const Vector3& point, Vector3& result) const
{
if (_shape == nullptr)
{
result = Vector3::Maximum;
return;
}
// Prepare data
const PxTransform trans(C2P(_transform.Translation), C2P(_transform.Orientation));
PxVec3 closestPoint;
// Compute distance between a point and a geometry object
const float distanceSqr = PxGeometryQuery::pointDistance(C2P(position), _shape->getGeometry().any(), trans, &closestPoint);
Vector3 closestPoint;
const float distanceSqr = PhysicsBackend::ComputeShapeSqrDistanceToPoint(_shape, _transform.Translation, _transform.Orientation, point, &closestPoint);
if (distanceSqr > 0.0f)
{
// Use calculated point
result = P2C(closestPoint);
}
result = closestPoint;
else
{
// Fallback to the input location
result = position;
}
result = point;
}
bool Collider::ContainsPoint(const Vector3& point) const
{
if (_shape)
{
const PxTransform trans(C2P(_transform.Translation), C2P(_transform.Orientation));
const float distanceSqr = PxGeometryQuery::pointDistance(C2P(point), _shape->getGeometry().any(), trans);
const float distanceSqr = PhysicsBackend::ComputeShapeSqrDistanceToPoint(_shape, _transform.Translation, _transform.Orientation, point);
return distanceSqr <= 0.0f;
}
return false;
@@ -165,17 +108,12 @@ bool Collider::ComputePenetration(const Collider* colliderA, const Collider* col
{
direction = Vector3::Zero;
distance = 0.0f;
CHECK_RETURN(colliderA && colliderB, false);
const PxShape* shapeA = colliderA->GetPxShape();
const PxShape* shapeB = colliderB->GetPxShape();
void* shapeA = colliderA->GetPhysicsShape();
void* shapeB = colliderB->GetPhysicsShape();
if (!shapeA || !shapeB)
return false;
const PxTransform poseA(C2P(colliderA->GetPosition()), C2P(colliderA->GetOrientation()));
const PxTransform poseB(C2P(colliderB->GetPosition()), C2P(colliderB->GetOrientation()));
return PxGeometryQuery::computePenetration(C2P(direction), distance, shapeA->getGeometry().any(), poseA, shapeB->getGeometry().any(), poseB);
return PhysicsBackend::ComputeShapesPenetration(shapeA, shapeB, colliderA->GetPosition(), colliderA->GetOrientation(), colliderB->GetPosition(), colliderB->GetOrientation(), direction, distance);
}
bool Collider::CanAttach(RigidBody* rigidBody) const
@@ -192,9 +130,7 @@ RigidBody* Collider::GetAttachedRigidBody() const
{
if (_shape && _staticActor == nullptr)
{
auto actor = _shape->getActor();
if (actor && actor->is<PxRigidDynamic>())
return static_cast<RigidBody*>(actor->userData);
return dynamic_cast<RigidBody*>(GetParent());
}
return nullptr;
}
@@ -232,10 +168,10 @@ void Collider::Attach(RigidBody* rigidBody)
CreateShape();
// Attach
rigidBody->GetPhysXRigidActor()->attachShape(*_shape);
PhysicsBackend::AttachShape(_shape, rigidBody->GetPhysicsActor());
_cachedLocalPosePos = (_localTransform.Translation + _localTransform.Orientation * _center) * rigidBody->GetScale();
_cachedLocalPoseRot = _localTransform.Orientation;
_shape->setLocalPose(PxTransform(C2P(_cachedLocalPosePos), C2P(_cachedLocalPoseRot)));
PhysicsBackend::SetShapeLocalPose(_shape, _cachedLocalPosePos, _cachedLocalPoseRot);
if (rigidBody->IsDuringPlay())
{
rigidBody->UpdateBounds();
@@ -245,40 +181,29 @@ void Collider::Attach(RigidBody* rigidBody)
void Collider::UpdateLayerBits()
{
ASSERT(_shape);
PxFilterData filterData;
// Own layer ID
filterData.word0 = GetLayerMask();
const uint32 mask0 = GetLayerMask();
// Own layer mask
filterData.word1 = Physics::LayerMasks[GetLayer()];
const uint32 mask1 = Physics::LayerMasks[GetLayer()];
_shape->setSimulationFilterData(filterData);
_shape->setQueryFilterData(filterData);
ASSERT(_shape);
PhysicsBackend::SetShapeFilterMask(_shape, mask0, mask1);
}
void Collider::CreateShape()
{
ASSERT(_shape == nullptr);
// Setup shape geometry
_cachedScale = GetScale();
PxGeometryHolder geometry;
GetGeometry(geometry);
CollisionShape shape;
GetGeometry(shape);
// Create shape
const bool isTrigger = _isTrigger && CanBeTrigger();
const PxShapeFlags shapeFlags = GetShapeFlags(isTrigger, IsActiveInHierarchy());
PxMaterial* material = Physics::GetDefaultMaterial();
if (Material && !Material->WaitForLoaded() && Material->Instance)
{
material = ((PhysicalMaterial*)Material->Instance)->GetPhysXMaterial();
}
ASSERT(_shape == nullptr);
_shape = CPhysX->createShape(geometry.any(), *material, true, shapeFlags);
ASSERT(_shape);
_shape->userData = this;
_shape->setContactOffset(Math::Max(_shape->getRestOffset() + ZeroTolerance, _contactOffset));
_shape = PhysicsBackend::CreateShape(this, shape, Material.Get(), IsActiveInHierarchy(), isTrigger);
PhysicsBackend::SetShapeContactOffset(_shape, _contactOffset);
UpdateLayerBits();
}
@@ -289,20 +214,20 @@ void Collider::UpdateGeometry()
// Setup shape geometry
_cachedScale = GetScale();
PxGeometryHolder geometry;
GetGeometry(geometry);
CollisionShape shape;
GetGeometry(shape);
// Recreate shape if geometry has different type
if (_shape->getGeometryType() != geometry.getType())
if (PhysicsBackend::GetShapeType(_shape) != shape.Type)
{
// Detach from the actor
auto actor = _shape->getActor();
void* actor = PhysicsBackend::GetShapeActor(_shape);
if (actor)
actor->detachShape(*_shape);
PhysicsBackend::DetachShape(_shape, actor);
// Release shape
GetPhysicsScene()->RemoveCollider(this);
_shape->release();
PhysicsBackend::RemoveCollider(this);
PhysicsBackend::DestroyShape(_shape);
_shape = nullptr;
// Recreate shape
@@ -314,7 +239,7 @@ void Collider::UpdateGeometry()
const auto rigidBody = dynamic_cast<RigidBody*>(GetParent());
if (_staticActor != nullptr || (rigidBody && CanAttach(rigidBody)))
{
actor->attachShape(*_shape);
PhysicsBackend::AttachShape(_shape, actor);
}
else
{
@@ -327,33 +252,28 @@ void Collider::UpdateGeometry()
}
// Update shape
_shape->setGeometry(geometry.any());
PhysicsBackend::SetShapeGeometry(_shape, shape);
}
void Collider::CreateStaticActor()
{
ASSERT(_staticActor == nullptr);
const PxTransform trans(C2P(_transform.Translation), C2P(_transform.Orientation));
_staticActor = CPhysX->createRigidStatic(trans);
ASSERT(_staticActor);
_staticActor->userData = this;
#if WITH_PVD
_staticActor->setActorFlag(PxActorFlag::eVISUALIZATION, true);
#endif
_staticActor = PhysicsBackend::CreateRigidStaticActor(nullptr, _transform.Translation, _transform.Orientation);
// Reset local pos of the shape and link it to the actor
_shape->setLocalPose(PxTransform(C2P(_center)));
_staticActor->attachShape(*_shape);
PhysicsBackend::SetShapeLocalPose(_shape, _center, Quaternion::Identity);
PhysicsBackend::AttachShape(_shape, _staticActor);
GetPhysicsScene()->AddActor(_staticActor);
void* scene = GetPhysicsScene()->GetPhysicsScene();
PhysicsBackend::AddSceneActor(scene, _staticActor);
}
void Collider::RemoveStaticActor()
{
ASSERT(_staticActor != nullptr);
GetPhysicsScene()->RemoveActor(_staticActor);
void* scene = GetPhysicsScene()->GetPhysicsScene();
PhysicsBackend::RemoveSceneActor(scene, _staticActor);
PhysicsBackend::DestroyActor(_staticActor);
_staticActor = nullptr;
}
@@ -369,14 +289,7 @@ void Collider::OnMaterialChanged()
{
// Update the shape material
if (_shape)
{
PxMaterial* material = Physics::GetDefaultMaterial();
if (Material && !Material->WaitForLoaded() && Material->Instance)
{
material = ((PhysicalMaterial*)Material->Instance)->GetPhysXMaterial();
}
_shape->setMaterials(&material, 1);
}
PhysicsBackend::SetShapeMaterial(_shape, Material.Get());
}
void Collider::Serialize(SerializeStream& stream, const void* otherObj)
@@ -436,19 +349,22 @@ void Collider::EndPlay()
if (_shape)
{
// Detach from the actor
auto actor = _shape->getActor();
void* actor = PhysicsBackend::GetShapeActor(_shape);
RigidBody* rigidBody = GetAttachedRigidBody();
if (actor)
actor->detachShape(*_shape);
if (actor && actor->is<PxRigidDynamic>())
static_cast<RigidBody*>(actor->userData)->OnColliderChanged(this);
PhysicsBackend::DetachShape(_shape, actor);
if (rigidBody)
{
rigidBody->OnColliderChanged(this);
}
else if (_staticActor)
{
RemoveStaticActor();
}
// Release shape
GetPhysicsScene()->RemoveCollider(this);
_shape->release();
PhysicsBackend::RemoveCollider(this);
PhysicsBackend::DestroyShape(_shape);
_shape = nullptr;
}
}
@@ -460,9 +376,7 @@ void Collider::OnActiveInTreeChanged()
if (_shape)
{
const bool isTrigger = _isTrigger && CanBeTrigger();
const PxShapeFlags shapeFlags = GetShapeFlags(isTrigger, IsActiveInHierarchy());
_shape->setFlags(shapeFlags);
PhysicsBackend::SetShapeState(_shape, IsActiveInHierarchy(), _isTrigger && CanBeTrigger());
auto rigidBody = GetAttachedRigidBody();
if (rigidBody)
@@ -481,17 +395,20 @@ void Collider::OnParentChanged()
if (_shape)
{
// Detach from the actor
auto actor = _shape->getActor();
void* actor = PhysicsBackend::GetShapeActor(_shape);
RigidBody* rigidBody = GetAttachedRigidBody();
if (actor)
actor->detachShape(*_shape);
if (actor && actor->is<PxRigidDynamic>())
static_cast<RigidBody*>(actor->userData)->OnColliderChanged(this);
PhysicsBackend::DetachShape(_shape, actor);
if (rigidBody)
{
rigidBody->OnColliderChanged(this);
}
// Check if the new parent is a rigidbody
const auto rigidBody = dynamic_cast<RigidBody*>(GetParent());
rigidBody = dynamic_cast<RigidBody*>(GetParent());
if (rigidBody && CanAttach(rigidBody))
{
// Attach to the rigidbody (will remove static actor if it's n use)
// Attach to the rigidbody
Attach(rigidBody);
}
else
@@ -499,6 +416,8 @@ void Collider::OnParentChanged()
// Use static actor (if not created yet)
if (_staticActor == nullptr)
CreateStaticActor();
else
PhysicsBackend::AttachShape(_shape, _staticActor);
}
}
}
@@ -510,7 +429,7 @@ void Collider::OnTransformChanged()
if (_staticActor)
{
_staticActor->setGlobalPose(PxTransform(C2P(_transform.Translation), C2P(_transform.Orientation)));
PhysicsBackend::SetRigidActorPose(_staticActor, _transform.Translation, _transform.Orientation);
}
else if (const RigidBody* rigidBody = GetAttachedRigidBody())
{
@@ -519,7 +438,7 @@ void Collider::OnTransformChanged()
{
_cachedLocalPosePos = localPosePos;
_cachedLocalPoseRot = _localTransform.Orientation;
_shape->setLocalPose(PxTransform(C2P(localPosePos), C2P(_cachedLocalPoseRot)));
PhysicsBackend::SetShapeLocalPose(_shape, localPosePos, _cachedLocalPoseRot);
}
}
@@ -544,7 +463,8 @@ void Collider::OnPhysicsSceneChanged(PhysicsScene* previous)
if (_staticActor != nullptr)
{
previous->UnlinkActor(_staticActor);
GetPhysicsScene()->AddActor(_staticActor);
PhysicsBackend::RemoveSceneActor(previous->GetPhysicsScene(), _staticActor);
void* scene = GetPhysicsScene()->GetPhysicsScene();
PhysicsBackend::AddSceneActor(scene, _staticActor);
}
}

View File

@@ -22,8 +22,8 @@ protected:
Vector3 _center;
bool _isTrigger;
PxShape* _shape;
PxRigidStatic* _staticActor;
void* _shape;
void* _staticActor;
Vector3 _cachedScale;
float _contactOffset;
Vector3 _cachedLocalPosePos;
@@ -32,9 +32,9 @@ protected:
public:
/// <summary>
/// Gets the collider shape PhysX object.
/// Gets the native physics backend object.
/// </summary>
PxShape* GetPxShape() const;
void* GetPhysicsShape() const;
/// <summary>
/// Gets the 'IsTrigger' flag.
@@ -121,9 +121,9 @@ public:
/// <summary>
/// Gets a point on the collider that is closest to a given location. Can be used to find a hit location or position to apply explosion force or any other special effects.
/// </summary>
/// <param name="position">The position to find the closest point to it.</param>
/// <param name="point">The position to find the closest point to it.</param>
/// <param name="result">The result point on the collider that is closest to the specified location.</param>
API_FUNCTION() void ClosestPoint(const Vector3& position, API_PARAM(Out) Vector3& result) const;
API_FUNCTION() void ClosestPoint(const Vector3& point, API_PARAM(Out) Vector3& result) const;
/// <summary>
/// Checks if a point is inside the collider.
@@ -171,7 +171,7 @@ protected:
/// <summary>
/// Updates the shape actor collisions/queries layer mask bits.
/// </summary>
virtual void UpdateLayerBits();
void UpdateLayerBits();
/// <summary>
/// Updates the bounding box of the shape.
@@ -181,8 +181,8 @@ protected:
/// <summary>
/// Gets the collider shape geometry.
/// </summary>
/// <param name="geometry">The output geometry.</param>
virtual void GetGeometry(PxGeometryHolder& geometry) = 0;
/// <param name="collision">The output collision shape.</param>
virtual void GetGeometry(CollisionShape& collision) = 0;
/// <summary>
/// Creates the collider shape.

View File

@@ -4,7 +4,6 @@
#include "Engine/Core/Math/Matrix.h"
#include "Engine/Core/Math/Ray.h"
#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
@@ -147,7 +146,7 @@ void MeshCollider::UpdateBounds()
BoundingSphere::FromBox(_box, _sphere);
}
void MeshCollider::GetGeometry(PxGeometryHolder& geometry)
void MeshCollider::GetGeometry(CollisionShape& collision)
{
// Prepare scale
Vector3 scale = _cachedScale;
@@ -160,25 +159,9 @@ void MeshCollider::GetGeometry(PxGeometryHolder& geometry)
if (CollisionData && CollisionData->IsLoaded())
type = CollisionData->GetOptions().Type;
if (type == CollisionDataType::ConvexMesh)
{
// Convex mesh
PxConvexMeshGeometry convexMesh;
convexMesh.scale.scale = C2P(scale);
convexMesh.convexMesh = CollisionData->GetConvex();
geometry.storeAny(convexMesh);
}
collision.SetConvexMesh(CollisionData->GetConvex(), scale.Raw);
else if (type == CollisionDataType::TriangleMesh)
{
// Triangle mesh
PxTriangleMeshGeometry triangleMesh;
triangleMesh.scale.scale = C2P(scale);
triangleMesh.triangleMesh = CollisionData->GetTriangle();
geometry.storeAny(triangleMesh);
}
collision.SetTriangleMesh(CollisionData->GetTriangle(), scale.Raw);
else
{
// Dummy geometry
const PxSphereGeometry sphere(minSize);
geometry.storeAny(sphere);
}
collision.SetSphere(minSize);
}

View File

@@ -45,5 +45,5 @@ protected:
void DrawPhysicsDebug(RenderView& view) override;
#endif
void UpdateBounds() override;
void GetGeometry(PxGeometryHolder& geometry) override;
void GetGeometry(CollisionShape& collision) override;
};

View File

@@ -2,8 +2,6 @@
#include "SphereCollider.h"
#include "Engine/Serialization/Serialization.h"
#include "Engine/Physics/Utilities.h"
#include <PxShape.h>
SphereCollider::SphereCollider(const SpawnParams& params)
: Collider(params)
@@ -78,11 +76,10 @@ void SphereCollider::UpdateBounds()
_sphere.GetBoundingBox(_box);
}
void SphereCollider::GetGeometry(PxGeometryHolder& geometry)
void SphereCollider::GetGeometry(CollisionShape& collision)
{
const float scaling = _cachedScale.GetAbsolute().MaxValue();
const float radius = Math::Abs(_radius) * scaling;
const float minSize = 0.001f;
const PxSphereGeometry sphere(Math::Max(radius, minSize));
geometry.storeAny(sphere);
collision.SetSphere(Math::Max(radius, minSize));
}

View File

@@ -54,5 +54,5 @@ protected:
void DrawPhysicsDebug(RenderView& view) override;
#endif
void UpdateBounds() override;
void GetGeometry(PxGeometryHolder& geometry) override;
void GetGeometry(CollisionShape& collision) override;
};

View File

@@ -6,16 +6,13 @@
#include "Engine/Core/Math/Ray.h"
#include "Engine/Level/Actors/Spline.h"
#include "Engine/Serialization/Serialization.h"
#include "Engine/Physics/Utilities.h"
#include "Engine/Physics/Physics.h"
#include "Engine/Physics/PhysicsBackend.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)
: Collider(params)
@@ -173,7 +170,7 @@ void SplineCollider::EndPlay()
// Cleanup
if (_triangleMesh)
{
GetPhysicsScene()->RemoveObject(_triangleMesh);
PhysicsBackend::DestroyObject(_triangleMesh);
_triangleMesh = nullptr;
}
}
@@ -183,18 +180,17 @@ void SplineCollider::UpdateBounds()
// Unused as bounds are updated during collision building
}
void SplineCollider::GetGeometry(PxGeometryHolder& geometry)
void SplineCollider::GetGeometry(CollisionShape& collision)
{
// Reset bounds
_box = BoundingBox(_transform.Translation);
BoundingSphere::FromBox(_box, _sphere);
const float minSize = 0.001f;
collision.SetSphere(minSize);
// Skip if sth is missing
if (!_spline || !IsActiveInHierarchy() || _spline->GetSplinePointsCount() < 2 || !CollisionData || !CollisionData->IsLoaded())
{
geometry.storeAny(PxSphereGeometry(0.001f));
return;
}
PROFILE_CPU();
// Extract collision geometry
@@ -203,10 +199,7 @@ void SplineCollider::GetGeometry(PxGeometryHolder& geometry)
Array<int32> collisionIndices;
CollisionData->ExtractGeometry(collisionVertices, collisionIndices);
if (collisionIndices.IsEmpty())
{
geometry.storeAny(PxSphereGeometry(0.001f));
return;
}
// Apply local mesh transformation
if (!_preTransform.IsIdentity())
@@ -288,7 +281,6 @@ void SplineCollider::GetGeometry(PxGeometryHolder& geometry)
// Prepare scale
Vector3 scale = _cachedScale;
scale.Absolute();
const float minSize = 0.001f;
scale = Vector3::Max(scale, minSize);
// TODO: add support for cooking collision for static splines in editor and reusing it in game
@@ -307,16 +299,15 @@ void SplineCollider::GetGeometry(PxGeometryHolder& geometry)
// Create triangle mesh
if (_triangleMesh)
{
GetPhysicsScene()->RemoveObject(_triangleMesh);
PhysicsBackend::DestroyObject(_triangleMesh);
_triangleMesh = nullptr;
}
PxDefaultMemoryInputData input(collisionData.Get(), collisionData.Length());
// TODO: try using getVerticesForModification for dynamic triangle mesh vertices updating when changing curve in the editor
_triangleMesh = Physics::GetPhysics()->createTriangleMesh(input);
BoundingBox localBounds;
_triangleMesh = PhysicsBackend::CreateTriangleMesh(collisionData.Get(), collisionData.Length(), localBounds);
if (!_triangleMesh)
{
LOG(Error, "Failed to create triangle mesh from collision data of {0}.", ToString());
geometry.storeAny(PxSphereGeometry(0.001f));
return;
}
@@ -325,17 +316,13 @@ void SplineCollider::GetGeometry(PxGeometryHolder& geometry)
_vertexBuffer[i] = colliderTransform.LocalToWorld(_vertexBuffer[i]);
// Update bounds
_box = P2C(_triangleMesh->getLocalBounds());
Matrix splineWorld;
colliderTransform.GetWorld(splineWorld);
BoundingBox::Transform(_box, splineWorld, _box);
BoundingBox::Transform(localBounds, splineWorld, _box);
BoundingSphere::FromBox(_box, _sphere);
// Setup geometry
PxTriangleMeshGeometry triangleMesh;
triangleMesh.scale.scale = C2P(scale);
triangleMesh.triangleMesh = _triangleMesh;
geometry.storeAny(triangleMesh);
collision.SetTriangleMesh(_triangleMesh, scale.Raw);
// TODO: find a way of releasing _vertexBuffer and _indexBuffer for static colliders (note: ExtractGeometry usage for navmesh generation at runtime)
@@ -344,5 +331,4 @@ void SplineCollider::GetGeometry(PxGeometryHolder& geometry)
#endif
LOG(Error, "Cannot build collision data for {0} due to runtime collision cooking diabled.", ToString());
geometry.storeAny(PxSphereGeometry(0.001f));
}

View File

@@ -18,7 +18,7 @@ API_CLASS() class FLAXENGINE_API SplineCollider : public Collider
DECLARE_SCENE_OBJECT(SplineCollider);
private:
Spline* _spline = nullptr;
PxTriangleMesh* _triangleMesh = nullptr;
void* _triangleMesh = nullptr;
Array<Vector3> _vertexBuffer;
Array<int32> _indexBuffer;
Transform _preTransform = Transform::Identity;
@@ -76,5 +76,5 @@ protected:
void DrawPhysicsDebug(RenderView& view) override;
#endif
void UpdateBounds() override;
void GetGeometry(PxGeometryHolder& geometry) override;
void GetGeometry(CollisionShape& collision) override;
};

View File

@@ -8,96 +8,6 @@
#include "Engine/Graphics/Models/MeshBase.h"
#include "Engine/Threading/Threading.h"
#include "Engine/Core/Log.h"
#include "Physics.h"
#include <ThirdParty/PhysX/cooking/PxCooking.h>
#include <ThirdParty/PhysX/extensions/PxDefaultStreams.h>
#define CONVEX_VERTEX_MIN 8
#define CONVEX_VERTEX_MAX 255
#define ENSURE_CAN_COOK \
auto cooking = Physics::GetCooking(); \
if (cooking == nullptr) \
{ \
LOG(Warning, "Physics collisions cooking is disabled at runtime. Enable Physics Settings option SupportCookingAtRuntime to use collision generation at runtime."); \
return true; \
}
bool CollisionCooking::CookConvexMesh(CookingInput& input, BytesContainer& output)
{
ENSURE_CAN_COOK;
if (input.VertexCount == 0)
LOG(Warning, "Empty mesh data for collision cooking.");
// Init options
PxConvexMeshDesc desc;
desc.points.count = input.VertexCount;
desc.points.stride = sizeof(Vector3);
desc.points.data = input.VertexData;
desc.flags = PxConvexFlag::eCOMPUTE_CONVEX;
if (input.ConvexVertexLimit == 0)
desc.vertexLimit = CONVEX_VERTEX_MAX;
else
desc.vertexLimit = (PxU16)Math::Clamp(input.ConvexVertexLimit, CONVEX_VERTEX_MIN, CONVEX_VERTEX_MAX);
if (input.ConvexFlags & ConvexMeshGenerationFlags::SkipValidation)
desc.flags |= PxConvexFlag::Enum::eDISABLE_MESH_VALIDATION;
if (input.ConvexFlags & ConvexMeshGenerationFlags::UsePlaneShifting)
desc.flags |= PxConvexFlag::Enum::ePLANE_SHIFTING;
if (input.ConvexFlags & ConvexMeshGenerationFlags::UseFastInteriaComputation)
desc.flags |= PxConvexFlag::Enum::eFAST_INERTIA_COMPUTATION;
if (input.ConvexFlags & ConvexMeshGenerationFlags::ShiftVertices)
desc.flags |= PxConvexFlag::Enum::eSHIFT_VERTICES;
PxCookingParams cookingParams = cooking->getParams();
cookingParams.suppressTriangleMeshRemapTable = input.ConvexFlags & ConvexMeshGenerationFlags::SuppressFaceRemapTable;
cooking->setParams(cookingParams);
// Perform cooking
PxDefaultMemoryOutputStream outputStream;
PxConvexMeshCookingResult::Enum result;
if (!cooking->cookConvexMesh(desc, outputStream, &result))
{
LOG(Warning, "Convex Mesh cooking failed. Error code: {0}, Input vertices count: {1}", result, input.VertexCount);
return true;
}
// Copy result
output.Copy(outputStream.getData(), outputStream.getSize());
return false;
}
bool CollisionCooking::CookTriangleMesh(CookingInput& input, BytesContainer& output)
{
ENSURE_CAN_COOK;
if (input.VertexCount == 0 || input.IndexCount == 0)
LOG(Warning, "Empty mesh data for collision cooking.");
// Init options
PxTriangleMeshDesc desc;
desc.points.count = input.VertexCount;
desc.points.stride = sizeof(Vector3);
desc.points.data = input.VertexData;
desc.triangles.count = input.IndexCount / 3;
desc.triangles.stride = 3 * (input.Is16bitIndexData ? sizeof(uint16) : sizeof(uint32));
desc.triangles.data = input.IndexData;
desc.flags = input.Is16bitIndexData ? PxMeshFlag::e16_BIT_INDICES : (PxMeshFlag::Enum)0;
PxCookingParams cookingParams = cooking->getParams();
cookingParams.suppressTriangleMeshRemapTable = input.ConvexFlags & ConvexMeshGenerationFlags::SuppressFaceRemapTable;
cooking->setParams(cookingParams);
// Perform cooking
PxDefaultMemoryOutputStream outputStream;
PxTriangleMeshCookingResult::Enum result;
if (!cooking->cookTriangleMesh(desc, outputStream, &result))
{
LOG(Warning, "Triangle Mesh cooking failed. Error code: {0}, Input vertices count: {1}, indices count: {2}", result, input.VertexCount, input.IndexCount);
return true;
}
// Copy result
output.Copy(outputStream.getData(), outputStream.getSize());
return false;
}
bool CollisionCooking::CookCollision(const Argument& arg, CollisionData::SerializedOptions& outputOptions, BytesContainer& outputData)
{
@@ -371,17 +281,4 @@ bool CollisionCooking::CookCollision(const Argument& arg, CollisionData::Seriali
return false;
}
bool CollisionCooking::CookHeightField(const physx::PxHeightFieldDesc& desc, physx::PxOutputStream& stream)
{
ENSURE_CAN_COOK;
if (!Physics::GetCooking()->cookHeightField(desc, stream))
{
LOG(Warning, "Height Field collision cooking failed.");
return true;
}
return false;
}
#endif

View File

@@ -8,12 +8,10 @@
#include "Engine/Physics/CollisionData.h"
#include "Engine/Graphics/Models/ModelData.h"
#include "Engine/Content/Assets/ModelBase.h"
#include "Engine/Physics/PhysicsBackend.h"
namespace physx
{
class PxHeightFieldDesc;
class PxOutputStream;
}
#define CONVEX_VERTEX_MIN 8
#define CONVEX_VERTEX_MAX 255
/// <summary>
/// Physical collision data cooking tools. Allows to bake heightfield, convex and triangle mesh colliders data.
@@ -21,7 +19,6 @@ namespace physx
class CollisionCooking
{
public:
struct CookingInput
{
int32 VertexCount = 0;
@@ -65,6 +62,16 @@ public:
/// <returns>True if failed, otherwise false.</returns>
static bool CookTriangleMesh(CookingInput& input, BytesContainer& output);
/// <summary>
/// Cooks a heightfield. The results are written to the stream. To create a heightfield object there is an option to precompute some of calculations done while loading the heightfield data.
/// </summary>
/// <param name="cols">The heightfield columns count.</param>
/// <param name="rows">The heightfield rows count.</param>
/// <param name="data">The heightfield data.</param>
/// <param name="stream">The user stream to output the cooked data.</param>
/// <returns>True if failed, otherwise false.</returns>
static bool CookHeightField(int32 cols, int32 rows, const PhysicsBackend::HeightFieldSample* data, WriteStream& stream);
/// <summary>
/// Cooks the collision from the model and prepares the data for the <see cref="CollisionData"/> format.
/// </summary>
@@ -73,14 +80,6 @@ public:
/// <param name="outputData">The output data container.</param>
/// <returns>True if failed, otherwise false.</returns>
static bool CookCollision(const Argument& arg, CollisionData::SerializedOptions& outputOptions, BytesContainer& outputData);
/// <summary>
/// Cooks a heightfield. The results are written to the stream. To create a heightfield object there is an option to precompute some of calculations done while loading the heightfield data.
/// </summary>
/// <param name="desc">The heightfield descriptor to read the HF from.</param>
/// <param name="stream">The user stream to output the cooked data.</param>
/// <returns>True if failed, otherwise false.</returns>
static bool CookHeightField(const physx::PxHeightFieldDesc& desc, physx::PxOutputStream& stream);
};
#endif

View File

@@ -1,19 +1,14 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
#include "CollisionData.h"
#include "Engine/Core/Log.h"
#include "Engine/Content/Content.h"
#include "Engine/Content/Assets/Model.h"
#include "Engine/Content/Factories/BinaryAssetFactory.h"
#include "Engine/Physics/Physics.h"
#include "Engine/Physics/PhysicsScene.h"
#include "Engine/Physics/Utilities.h"
#include "Engine/Physics/CollisionData.h"
#include "Engine/Physics/PhysicsBackend.h"
#include "Engine/Physics/CollisionCooking.h"
#include "Engine/Threading/Threading.h"
#include <ThirdParty/PhysX/extensions/PxDefaultStreams.h>
#include <ThirdParty/PhysX/geometry/PxTriangleMesh.h>
#include <ThirdParty/PhysX/geometry/PxConvexMesh.h>
#include <ThirdParty/PhysX/PxPhysics.h>
REGISTER_BINARY_ASSET(CollisionData, "FlaxEngine.CollisionData", true);
@@ -141,9 +136,11 @@ bool CollisionData::GetModelTriangle(uint32 faceIndex, MeshBase*& mesh, uint32&
if (!IsLoaded())
return false;
ScopeLock lock(Locker);
if (_triangleMesh && faceIndex < _triangleMesh->getNbTriangles())
if (_triangleMesh)
{
if (const PxU32* remap = _triangleMesh->getTrianglesRemap())
uint32 trianglesCount;
const uint32* remap = PhysicsBackend::GetTriangleMeshRemap(_triangleMesh, trianglesCount);
if (remap && faceIndex < trianglesCount)
{
// Get source triangle index from the triangle mesh
meshTriangleIndex = remap[faceIndex];
@@ -187,98 +184,10 @@ void CollisionData::ExtractGeometry(Array<Vector3>& vertexBuffer, Array<int32>&
indexBuffer.Clear();
ScopeLock lock(Locker);
uint32 numVertices = 0;
uint32 numIndices = 0;
// Convex Mesh
if (_convexMesh)
{
numVertices = _convexMesh->getNbVertices();
const uint32 numPolygons = _convexMesh->getNbPolygons();
for (uint32 i = 0; i < numPolygons; i++)
{
PxHullPolygon face;
const bool status = _convexMesh->getPolygonData(i, face);
ASSERT(status);
numIndices += (face.mNbVerts - 2) * 3;
}
}
// Triangle Mesh
PhysicsBackend::GetConvexMeshTriangles(_convexMesh, vertexBuffer, indexBuffer);
else if (_triangleMesh)
{
numVertices = _triangleMesh->getNbVertices();
numIndices = _triangleMesh->getNbTriangles() * 3;
}
// No collision data
else
{
return;
}
// Prepare vertex and index buffers
vertexBuffer.Resize(numVertices);
indexBuffer.Resize(numIndices);
auto outVertices = vertexBuffer.Get();
auto outIndices = indexBuffer.Get();
if (_convexMesh)
{
const PxVec3* convexVertices = _convexMesh->getVertices();
const byte* convexIndices = _convexMesh->getIndexBuffer();
for (uint32 i = 0; i < numVertices; i++)
*outVertices++ = P2C(convexVertices[i]);
uint32 numPolygons = _convexMesh->getNbPolygons();
for (uint32 i = 0; i < numPolygons; i++)
{
PxHullPolygon face;
bool status = _convexMesh->getPolygonData(i, face);
ASSERT(status);
const PxU8* faceIndices = convexIndices + face.mIndexBase;
for (uint32 j = 2; j < face.mNbVerts; j++)
{
*outIndices++ = faceIndices[0];
*outIndices++ = faceIndices[j];
*outIndices++ = faceIndices[j - 1];
}
}
}
else
{
const PxVec3* vertices = _triangleMesh->getVertices();
for (uint32 i = 0; i < numVertices; i++)
*outVertices++ = P2C(vertices[i]);
if (_triangleMesh->getTriangleMeshFlags() & PxTriangleMeshFlag::e16_BIT_INDICES)
{
const uint16* indices = (const uint16*)_triangleMesh->getTriangles();
uint32 numTriangles = numIndices / 3;
for (uint32 i = 0; i < numTriangles; i++)
{
outIndices[i * 3 + 0] = (uint32)indices[i * 3 + 0];
outIndices[i * 3 + 1] = (uint32)indices[i * 3 + 1];
outIndices[i * 3 + 2] = (uint32)indices[i * 3 + 2];
}
}
else
{
const uint32* indices = (const uint32*)_triangleMesh->getTriangles();
uint32 numTriangles = numIndices / 3;
for (uint32 i = 0; i < numTriangles; i++)
{
outIndices[i * 3 + 0] = indices[i * 3 + 0];
outIndices[i * 3 + 1] = indices[i * 3 + 1];
outIndices[i * 3 + 2] = indices[i * 3 + 2];
}
}
}
PhysicsBackend::GetTriangleMeshTriangles(_triangleMesh, vertexBuffer, indexBuffer);
}
#if USE_EDITOR
@@ -360,16 +269,13 @@ CollisionData::LoadResult CollisionData::load(const SerializedOptions* options,
return LoadResult::InvalidData;
// Create PhysX object
PxDefaultMemoryInputData input(dataPtr, dataSize);
if (_options.Type == CollisionDataType::ConvexMesh)
{
_convexMesh = Physics::GetPhysics()->createConvexMesh(input);
_options.Box = P2C(_convexMesh->getLocalBounds());
_convexMesh = PhysicsBackend::CreateConvexMesh(dataPtr, dataSize, _options.Box);
}
else if (_options.Type == CollisionDataType::TriangleMesh)
{
_triangleMesh = Physics::GetPhysics()->createTriangleMesh(input);
_options.Box = P2C(_triangleMesh->getLocalBounds());
_triangleMesh = PhysicsBackend::CreateTriangleMesh(dataPtr, dataSize, _options.Box);
}
else
{
@@ -385,16 +291,12 @@ void CollisionData::unload(bool isReloading)
{
if (_convexMesh)
{
for (auto scene : Physics::Scenes)
scene->RemoveObject(_convexMesh);
PhysicsBackend::DestroyObject(_convexMesh);
_convexMesh = nullptr;
}
if (_triangleMesh)
{
for (auto scene : Physics::Scenes)
scene->RemoveObject(_triangleMesh);
PhysicsBackend::DestroyObject(_triangleMesh);
_triangleMesh = nullptr;
}
_options = CollisionDataOptions();

View File

@@ -10,12 +10,6 @@ class ModelBase;
class ModelData;
class MeshBase;
namespace physx
{
class PxConvexMesh;
class PxTriangleMesh;
}
/// <summary>
/// A <see cref="CollisionData"/> storage data type.
/// </summary>
@@ -165,8 +159,8 @@ public:
private:
CollisionDataOptions _options;
physx::PxConvexMesh* _convexMesh;
physx::PxTriangleMesh* _triangleMesh;
void* _convexMesh;
void* _triangleMesh;
public:
@@ -181,7 +175,7 @@ public:
/// <summary>
/// Gets the convex mesh object (valid only if asset is loaded and has cooked convex data).
/// </summary>
FORCE_INLINE physx::PxConvexMesh* GetConvex() const
FORCE_INLINE void* GetConvex() const
{
return _convexMesh;
}
@@ -189,7 +183,7 @@ public:
/// <summary>
/// Gets the triangle mesh object (valid only if asset is loaded and has cooked triangle data).
/// </summary>
FORCE_INLINE physx::PxTriangleMesh* GetTriangle() const
FORCE_INLINE void* GetTriangle() const
{
return _triangleMesh;
}

View File

@@ -2,13 +2,9 @@
#pragma once
#include "Engine/Core/Delegate.h"
#include "Engine/Core/Math/Vector3.h"
class PhysicsColliderActor;
class Joint;
class Rigidbody;
class Collider;
/// <summary>
/// Contains a contact point data for the collision location.

View File

@@ -3,9 +3,7 @@
#include "D6Joint.h"
#include "Engine/Serialization/JsonTools.h"
#include "Engine/Serialization/Serialization.h"
#include "Engine/Physics/Utilities.h"
#include "Engine/Physics/Physics.h"
#include <ThirdParty/PhysX/extensions/PxD6Joint.h>
#include "Engine/Physics/PhysicsBackend.h"
D6Joint::D6Joint(const SpawnParams& params)
: Joint(params)
@@ -19,185 +17,118 @@ void D6Joint::SetMotion(const D6JointAxis axis, const D6JointMotion value)
{
if (value == GetMotion(axis))
return;
_motion[static_cast<int32>(axis)] = value;
if (_joint)
{
auto joint = static_cast<PxD6Joint*>(_joint);
joint->setMotion(static_cast<PxD6Axis::Enum>(axis), static_cast<PxD6Motion::Enum>(value));
}
PhysicsBackend::SetD6JointMotion(_joint, axis, value);
}
void D6Joint::SetDrive(const D6JointDriveType index, const D6JointDrive& value)
{
if (value == GetDrive(index))
return;
_drive[static_cast<int32>(index)] = value;
if (_joint)
{
auto joint = static_cast<PxD6Joint*>(_joint);
PxD6JointDrive drive;
if (value.Acceleration)
drive.flags = PxD6JointDriveFlag::eACCELERATION;
drive.stiffness = value.Stiffness;
drive.damping = value.Damping;
drive.forceLimit = value.ForceLimit;
ASSERT_LOW_LAYER(drive.isValid());
joint->setDrive(static_cast<PxD6Drive::Enum>(index), drive);
}
PhysicsBackend::SetD6JointDrive(_joint, index, value);
}
void D6Joint::SetLimitLinear(const LimitLinear& value)
{
if (value == _limitLinear)
return;
_limitLinear = value;
if (_joint)
{
auto joint = static_cast<PxD6Joint*>(_joint);
PxJointLinearLimit limit(*Physics::GetTolerancesScale(), Math::Max(value.Extent, ZeroTolerance), value.ContactDist);
limit.stiffness = value.Spring.Stiffness;
limit.damping = value.Spring.Damping;
limit.restitution = value.Restitution;
ASSERT_LOW_LAYER(limit.isValid());
joint->setLinearLimit(limit);
}
PhysicsBackend::SetD6JointLimitLinear(_joint, value);
}
void D6Joint::SetLimitTwist(const LimitAngularRange& value)
{
if (value == _limitTwist)
return;
_limitTwist = value;
if (_joint)
{
auto joint = static_cast<PxD6Joint*>(_joint);
PxJointAngularLimitPair limit(value.Lower * DegreesToRadians, Math::Max(value.Upper, value.Lower) * DegreesToRadians, value.ContactDist);
limit.stiffness = value.Spring.Stiffness;
limit.damping = value.Spring.Damping;
limit.restitution = value.Restitution;
ASSERT_LOW_LAYER(limit.isValid());
joint->setTwistLimit(limit);
}
PhysicsBackend::SetD6JointLimitTwist(_joint, value);
}
void D6Joint::SetLimitSwing(const LimitConeRange& value)
{
if (value == _limitSwing)
return;
_limitSwing = value;
if (_joint)
{
auto joint = static_cast<PxD6Joint*>(_joint);
PxJointLimitCone limit(Math::Clamp(value.YLimitAngle * DegreesToRadians, ZeroTolerance, PI - ZeroTolerance), Math::Clamp(value.ZLimitAngle * DegreesToRadians, ZeroTolerance, PI - ZeroTolerance), value.ContactDist);
limit.stiffness = value.Spring.Stiffness;
limit.damping = value.Spring.Damping;
limit.restitution = value.Restitution;
ASSERT_LOW_LAYER(limit.isValid());
joint->setSwingLimit(limit);
}
PhysicsBackend::SetD6JointLimitSwing(_joint, value);
}
Vector3 D6Joint::GetDrivePosition() const
{
return _joint ? P2C(static_cast<PxD6Joint*>(_joint)->getDrivePosition().p) : Vector3::Zero;
return _joint ? PhysicsBackend::GetD6JointDrivePosition(_joint) : Vector3::Zero;
}
void D6Joint::SetDrivePosition(const Vector3& value)
{
if (_joint)
{
auto joint = static_cast<PxD6Joint*>(_joint);
PxTransform t = joint->getDrivePosition();
t.p = C2P(value);
joint->setDrivePosition(t);
}
PhysicsBackend::SetD6JointDrivePosition(_joint, value);
}
Quaternion D6Joint::GetDriveRotation() const
{
return _joint ? P2C(static_cast<PxD6Joint*>(_joint)->getDrivePosition().q) : Quaternion::Identity;
return _joint ? PhysicsBackend::GetD6JointDriveRotation(_joint) : Quaternion::Identity;
}
void D6Joint::SetDriveRotation(const Quaternion& value)
{
if (_joint)
{
auto joint = static_cast<PxD6Joint*>(_joint);
PxTransform t = joint->getDrivePosition();
t.q = C2P(value);
joint->setDrivePosition(t);
}
PhysicsBackend::SetD6JointDriveRotation(_joint, value);
}
Vector3 D6Joint::GetDriveLinearVelocity() const
{
Vector3 linear = Vector3::Zero, angular;
if (_joint)
{
PxVec3 linear, angular;
static_cast<PxD6Joint*>(_joint)->getDriveVelocity(linear, angular);
return P2C(linear);
}
return Vector3::Zero;
PhysicsBackend::GetD6JointDriveVelocity(_joint, linear, angular);
return linear;
}
void D6Joint::SetDriveLinearVelocity(const Vector3& value)
{
if (_joint)
{
PxVec3 linear, angular;
auto joint = static_cast<PxD6Joint*>(_joint);
joint->getDriveVelocity(linear, angular);
linear = C2P(value);
joint->setDriveVelocity(linear, angular);
Vector3 linear, angular;
PhysicsBackend::GetD6JointDriveVelocity(_joint, linear, angular);
PhysicsBackend::SetD6JointDriveVelocity(_joint, value, angular);
}
}
Vector3 D6Joint::GetDriveAngularVelocity() const
{
Vector3 linear, angular = Vector3::Zero;
if (_joint)
{
PxVec3 linear, angular;
static_cast<PxD6Joint*>(_joint)->getDriveVelocity(linear, angular);
return P2C(angular);
}
return Vector3::Zero;
PhysicsBackend::GetD6JointDriveVelocity(_joint, linear, angular);
return angular;
}
void D6Joint::SetDriveAngularVelocity(const Vector3& value)
{
if (_joint)
{
PxVec3 linear, angular;
auto joint = static_cast<PxD6Joint*>(_joint);
joint->getDriveVelocity(linear, angular);
angular = C2P(value);
joint->setDriveVelocity(linear, angular);
Vector3 linear, angular;
PhysicsBackend::GetD6JointDriveVelocity(_joint, linear, angular);
PhysicsBackend::SetD6JointDriveVelocity(_joint, linear, value);
}
}
float D6Joint::GetCurrentTwist() const
{
return _joint ? static_cast<PxD6Joint*>(_joint)->getTwistAngle() : 0.0f;
return _joint ? PhysicsBackend::GetD6JointTwist(_joint) : 0.0f;
}
float D6Joint::GetCurrentSwingYAngle() const
float D6Joint::GetCurrentSwingY() const
{
return _joint ? static_cast<PxD6Joint*>(_joint)->getSwingYAngle() : 0.0f;
return _joint ? PhysicsBackend::GetD6JointSwingY(_joint) : 0.0f;
}
float D6Joint::GetCurrentSwingZAngle() const
float D6Joint::GetCurrentSwingZ() const
{
return _joint ? static_cast<PxD6Joint*>(_joint)->getSwingZAngle() : 0.0f;
return _joint ? PhysicsBackend::GetD6JointSwingZ(_joint) : 0.0f;
}
#if USE_EDITOR
@@ -412,59 +343,15 @@ void D6Joint::Deserialize(DeserializeStream& stream, ISerializeModifier* modifie
JsonTools::GetFloat(_limitSwing.Spring.Damping, stream, "LimitSwing.Damping");
}
PxJoint* D6Joint::CreateJoint(JointData& data)
void* D6Joint::CreateJoint(const PhysicsJointDesc& desc)
{
const PxTransform trans0(C2P(data.Pos0), C2P(data.Rot0));
const PxTransform trans1(C2P(data.Pos1), C2P(data.Rot1));
auto joint = PxD6JointCreate(*data.Physics, data.Actor0, trans0, data.Actor1, trans1);
void* joint = PhysicsBackend::CreateD6Joint(desc);
for (int32 i = 0; i < static_cast<int32>(D6JointAxis::MAX); i++)
{
joint->setMotion(static_cast<PxD6Axis::Enum>(i), static_cast<PxD6Motion::Enum>(_motion[i]));
}
PhysicsBackend::SetD6JointMotion(joint, (D6JointAxis)i, _motion[i]);
for (int32 i = 0; i < static_cast<int32>(D6JointAxis::MAX); i++)
{
const auto& value = _drive[i];
PxD6JointDrive drive;
if (value.Acceleration)
drive.flags = PxD6JointDriveFlag::eACCELERATION;
drive.stiffness = value.Stiffness;
drive.damping = value.Damping;
drive.forceLimit = value.ForceLimit;
ASSERT_LOW_LAYER(drive.isValid());
joint->setDrive(static_cast<PxD6Drive::Enum>(i), drive);
}
{
const auto& value = _limitLinear;
PxJointLinearLimit limit(*Physics::GetTolerancesScale(), Math::Max(value.Extent, ZeroTolerance), value.ContactDist);
limit.stiffness = value.Spring.Stiffness;
limit.damping = value.Spring.Damping;
limit.restitution = value.Restitution;
ASSERT_LOW_LAYER(limit.isValid());
joint->setDistanceLimit(limit);
}
{
const auto& value = _limitTwist;
PxJointAngularLimitPair limit(value.Lower * DegreesToRadians, Math::Max(value.Upper, value.Lower) * DegreesToRadians, value.ContactDist);
limit.stiffness = value.Spring.Stiffness;
limit.damping = value.Spring.Damping;
limit.restitution = value.Restitution;
ASSERT_LOW_LAYER(limit.isValid());
joint->setTwistLimit(limit);
}
{
const auto& value = _limitSwing;
PxJointLimitCone limit(Math::Clamp(value.YLimitAngle * DegreesToRadians, ZeroTolerance, PI - ZeroTolerance), Math::Clamp(value.ZLimitAngle * DegreesToRadians, ZeroTolerance, PI - ZeroTolerance), value.ContactDist);
limit.stiffness = value.Spring.Stiffness;
limit.damping = value.Spring.Damping;
limit.restitution = value.Restitution;
ASSERT_LOW_LAYER(limit.isValid());
joint->setSwingLimit(limit);
}
PhysicsBackend::SetD6JointDrive(joint, (D6JointDriveType)i, _drive[i]);
PhysicsBackend::SetD6JointLimitLinear(joint, _limitLinear);
PhysicsBackend::SetD6JointLimitTwist(joint, _limitTwist);
PhysicsBackend::SetD6JointLimitSwing(joint, _limitSwing);
return joint;
}

View File

@@ -303,19 +303,19 @@ public:
public:
/// <summary>
/// Gets the twist angle of the joint.
/// Gets the twist angle of the joint (in the range (-2*Pi, 2*Pi]).
/// </summary>
API_PROPERTY() float GetCurrentTwist() const;
/// <summary>
/// Gets the current swing angle of the joint from the Y axis.
/// </summary>
API_PROPERTY() float GetCurrentSwingYAngle() const;
API_PROPERTY() float GetCurrentSwingY() const;
/// <summary>
/// Gets the current swing angle of the joint from the Z axis.
/// </summary>
API_PROPERTY() float GetCurrentSwingZAngle() const;
API_PROPERTY() float GetCurrentSwingZ() const;
public:
@@ -329,5 +329,5 @@ public:
protected:
// [Joint]
PxJoint* CreateJoint(JointData& data) override;
void* CreateJoint(const PhysicsJointDesc& desc) override;
};

View File

@@ -1,9 +1,8 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
#include "DistanceJoint.h"
#include "Engine/Physics/PhysicsBackend.h"
#include "Engine/Serialization/Serialization.h"
#include "Engine/Physics/Utilities.h"
#include <ThirdParty/PhysX/extensions/PxDistanceJoint.h>
DistanceJoint::DistanceJoint(const SpawnParams& params)
: Joint(params)
@@ -18,13 +17,9 @@ void DistanceJoint::SetFlags(DistanceJointFlag value)
{
if (_flags == value)
return;
_flags = value;
if (_joint)
{
static_cast<PxDistanceJoint*>(_joint)->setDistanceJointFlags(static_cast<PxDistanceJointFlag::Enum>(value));
}
PhysicsBackend::SetDistanceJointFlags(_joint, value);
}
void DistanceJoint::SetMinDistance(float value)
@@ -32,13 +27,9 @@ void DistanceJoint::SetMinDistance(float value)
value = Math::Clamp(value, 0.0f, _maxDistance);
if (Math::NearEqual(value, _minDistance))
return;
_minDistance = value;
if (_joint)
{
static_cast<PxDistanceJoint*>(_joint)->setMinDistance(value);
}
PhysicsBackend::SetDistanceJointMinDistance(_joint, value);
}
void DistanceJoint::SetMaxDistance(float value)
@@ -46,13 +37,9 @@ void DistanceJoint::SetMaxDistance(float value)
value = Math::Max(_minDistance, value);
if (Math::NearEqual(value, _maxDistance))
return;
_maxDistance = value;
if (_joint)
{
static_cast<PxDistanceJoint*>(_joint)->setMaxDistance(value);
}
PhysicsBackend::SetDistanceJointMaxDistance(_joint, value);
}
void DistanceJoint::SetTolerance(float value)
@@ -60,32 +47,23 @@ void DistanceJoint::SetTolerance(float value)
value = Math::Max(0.1f, value);
if (Math::NearEqual(value, _tolerance))
return;
_tolerance = value;
if (_joint)
{
static_cast<PxDistanceJoint*>(_joint)->setTolerance(value);
}
PhysicsBackend::SetDistanceJointTolerance(_joint, value);
}
void DistanceJoint::SetSpringParameters(const SpringParameters& value)
{
if (value == _spring)
return;
_spring = value;
if (_joint)
{
static_cast<PxDistanceJoint*>(_joint)->setStiffness(value.Stiffness);
static_cast<PxDistanceJoint*>(_joint)->setDamping(value.Damping);
}
PhysicsBackend::SetDistanceJointSpring(_joint, value);
}
float DistanceJoint::GetCurrentDistance() const
{
return _joint ? static_cast<PxDistanceJoint*>(_joint)->getDistance() : 0.0f;
return _joint ? PhysicsBackend::GetDistanceJointDistance(_joint) : 0.0f;
}
#if USE_EDITOR
@@ -149,18 +127,13 @@ void DistanceJoint::Deserialize(DeserializeStream& stream, ISerializeModifier* m
DESERIALIZE_MEMBER(Damping, _spring.Damping);
}
PxJoint* DistanceJoint::CreateJoint(JointData& data)
void* DistanceJoint::CreateJoint(const PhysicsJointDesc& desc)
{
const PxTransform trans0(C2P(data.Pos0), C2P(data.Rot0));
const PxTransform trans1(C2P(data.Pos1), C2P(data.Rot1));
auto joint = PxDistanceJointCreate(*data.Physics, data.Actor0, trans0, data.Actor1, trans1);
joint->setMinDistance(_minDistance);
joint->setMaxDistance(_maxDistance);
joint->setTolerance(_tolerance);
joint->setDistanceJointFlags(static_cast<PxDistanceJointFlag::Enum>(_flags));
joint->setStiffness(_spring.Stiffness);
joint->setDamping(_spring.Damping);
void* joint = PhysicsBackend::CreateDistanceJoint(desc);
PhysicsBackend::SetDistanceJointFlags(joint, _flags);
PhysicsBackend::SetDistanceJointMinDistance(joint, _minDistance);
PhysicsBackend::SetDistanceJointMaxDistance(joint, _maxDistance);
PhysicsBackend::SetDistanceJointTolerance(joint, _tolerance);
PhysicsBackend::SetDistanceJointSpring(joint, _spring);
return joint;
}

View File

@@ -157,5 +157,5 @@ public:
protected:
// [Joint]
PxJoint* CreateJoint(JointData& data) override;
void* CreateJoint(const PhysicsJointDesc& desc) override;
};

View File

@@ -1,8 +1,7 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
#include "FixedJoint.h"
#include "Engine/Physics/Utilities.h"
#include <ThirdParty/PhysX/extensions/PxFixedJoint.h>
#include "Engine/Physics/PhysicsBackend.h"
FixedJoint::FixedJoint(const SpawnParams& params)
: Joint(params)
@@ -23,9 +22,7 @@ void FixedJoint::OnDebugDrawSelected()
#endif
PxJoint* FixedJoint::CreateJoint(JointData& data)
void* FixedJoint::CreateJoint(const PhysicsJointDesc& desc)
{
const PxTransform trans0(C2P(data.Pos0), C2P(data.Rot0));
const PxTransform trans1(C2P(data.Pos1), C2P(data.Rot1));
return PxFixedJointCreate(*data.Physics, data.Actor0, trans0, data.Actor1, trans1);
return PhysicsBackend::CreateFixedJoint(desc);
}

View File

@@ -21,5 +21,5 @@ public:
protected:
// [Joint]
PxJoint* CreateJoint(JointData& data) override;
void* CreateJoint(const PhysicsJointDesc& desc) override;
};

View File

@@ -2,8 +2,7 @@
#include "HingeJoint.h"
#include "Engine/Serialization/Serialization.h"
#include "Engine/Physics/Utilities.h"
#include <ThirdParty/PhysX/extensions/PxRevoluteJoint.h>
#include "Engine/Physics/PhysicsBackend.h"
HingeJoint::HingeJoint(const SpawnParams& params)
: Joint(params)
@@ -17,61 +16,37 @@ void HingeJoint::SetFlags(const HingeJointFlag value)
{
if (_flags == value)
return;
_flags = value;
if (_joint)
{
auto joint = static_cast<PxRevoluteJoint*>(_joint);
joint->setRevoluteJointFlag(PxRevoluteJointFlag::eLIMIT_ENABLED, (_flags & HingeJointFlag::Limit) != 0);
joint->setRevoluteJointFlag(PxRevoluteJointFlag::eDRIVE_ENABLED, (_flags & HingeJointFlag::Drive) != 0);
}
PhysicsBackend::SetHingeJointFlags(_joint, value, _drive.FreeSpin);
}
void HingeJoint::SetLimit(const LimitAngularRange& value)
{
if (_limit == value)
return;
_limit = value;
if (_joint)
{
auto joint = static_cast<PxRevoluteJoint*>(_joint);
PxJointAngularLimitPair limit(value.Lower * DegreesToRadians, Math::Max(value.Upper, value.Lower) * DegreesToRadians, value.ContactDist);
limit.stiffness = value.Spring.Stiffness;
limit.damping = value.Spring.Damping;
limit.restitution = value.Restitution;
ASSERT_LOW_LAYER(limit.isValid());
joint->setLimit(limit);
}
PhysicsBackend::SetHingeJointLimit(_joint, value);
}
void HingeJoint::SetDrive(const HingeJointDrive& value)
{
if (_drive == value)
return;
_drive = value;
if (_joint)
{
auto joint = static_cast<PxRevoluteJoint*>(_joint);
joint->setDriveVelocity(Math::Max(value.Velocity, 0.0f));
joint->setDriveForceLimit(Math::Max(value.ForceLimit, 0.0f));
joint->setDriveGearRatio(Math::Max(value.GearRatio, 0.0f));
joint->setRevoluteJointFlag(PxRevoluteJointFlag::eDRIVE_FREESPIN, value.FreeSpin);
}
PhysicsBackend::SetHingeJointDrive(_joint, value);
}
float HingeJoint::GetCurrentAngle() const
{
return _joint ? static_cast<PxRevoluteJoint*>(_joint)->getAngle() : 0.0f;
return _joint ? PhysicsBackend::GetHingeJointAngle(_joint) : 0.0f;
}
float HingeJoint::GetCurrentVelocity() const
{
return _joint ? static_cast<PxRevoluteJoint*>(_joint)->getVelocity() : 0.0f;
return _joint ? PhysicsBackend::GetHingeJointVelocity(_joint) : 0.0f;
}
#if USE_EDITOR
@@ -146,29 +121,11 @@ void HingeJoint::Deserialize(DeserializeStream& stream, ISerializeModifier* modi
DESERIALIZE_MEMBER(FreeSpin, _drive.FreeSpin);
}
PxJoint* HingeJoint::CreateJoint(JointData& data)
void* HingeJoint::CreateJoint(const PhysicsJointDesc& desc)
{
const PxTransform trans0(C2P(data.Pos0), C2P(data.Rot0));
const PxTransform trans1(C2P(data.Pos1), C2P(data.Rot1));
auto joint = PxRevoluteJointCreate(*data.Physics, data.Actor0, trans0, data.Actor1, trans1);
int32 flags = 0;
if (_flags & HingeJointFlag::Limit)
flags |= PxRevoluteJointFlag::eLIMIT_ENABLED;
if (_flags & HingeJointFlag::Drive)
flags |= PxRevoluteJointFlag::eDRIVE_ENABLED;
if (_drive.FreeSpin)
flags |= PxRevoluteJointFlag::eDRIVE_FREESPIN;
joint->setRevoluteJointFlags(static_cast<PxRevoluteJointFlag::Enum>(flags));
joint->setDriveVelocity(_drive.Velocity);
joint->setDriveForceLimit(_drive.ForceLimit);
joint->setDriveGearRatio(_drive.GearRatio);
PxJointAngularLimitPair limit(_limit.Lower * DegreesToRadians, Math::Max(_limit.Upper, _limit.Lower) * DegreesToRadians, _limit.ContactDist);
limit.stiffness = _limit.Spring.Stiffness;
limit.damping = _limit.Spring.Damping;
limit.restitution = _limit.Restitution;
ASSERT_LOW_LAYER(limit.isValid());
joint->setLimit(limit);
void* joint = PhysicsBackend::CreateHingeJoint(desc);
PhysicsBackend::SetHingeJointFlags(joint, _flags, _drive.FreeSpin);
PhysicsBackend::SetHingeJointLimit(joint, _limit);
PhysicsBackend::SetHingeJointDrive(joint, _drive);
return joint;
}

View File

@@ -165,5 +165,5 @@ public:
protected:
// [Joint]
PxJoint* CreateJoint(JointData& data) override;
void* CreateJoint(const PhysicsJointDesc& desc) override;
};

View File

@@ -3,14 +3,13 @@
#include "Joint.h"
#include "Engine/Serialization/Serialization.h"
#include "Engine/Core/Log.h"
#include "Engine/Physics/Utilities.h"
#include "Engine/Physics/Physics.h"
#include "Engine/Physics/PhysicsBackend.h"
#include "Engine/Physics/PhysicsScene.h"
#include "Engine/Physics/Actors/IPhysicsActor.h"
#if USE_EDITOR
#include "Engine/Level/Scene/SceneRendering.h"
#endif
#include "Engine/Scripting/Script.h"
#include <ThirdParty/PhysX/extensions/PxJoint.h>
Joint::Joint(const SpawnParams& params)
: Actor(params)
@@ -27,36 +26,27 @@ void Joint::SetBreakForce(float value)
{
if (Math::NearEqual(value, _breakForce))
return;
_breakForce = value;
if (_joint)
{
_joint->setBreakForce(_breakForce, _breakTorque);
}
PhysicsBackend::SetJointBreakForce(_joint, _breakForce, _breakTorque);
}
void Joint::SetBreakTorque(float value)
{
if (Math::NearEqual(value, _breakTorque))
return;
_breakTorque = value;
if (_joint)
{
_joint->setBreakForce(_breakForce, _breakTorque);
}
PhysicsBackend::SetJointBreakForce(_joint, _breakForce, _breakTorque);
}
void Joint::SetEnableCollision(bool value)
{
if (value == GetEnableCollision())
return;
_enableCollision = value;
if (_joint)
{
_joint->setConstraintFlag(PxConstraintFlag::eCOLLISION_ENABLED, value);
}
PhysicsBackend::SetJointFlags(_joint, _enableCollision ? PhysicsBackend::JointFlags::Collision : PhysicsBackend::JointFlags::None);
}
bool Joint::GetEnableAutoAnchor() const
@@ -73,24 +63,23 @@ void Joint::SetTargetAnchor(const Vector3& value)
{
if (Vector3::NearEqual(value, _targetAnchor))
return;
_targetAnchor = value;
if (_joint && !_enableAutoAnchor)
{
_joint->setLocalPose(PxJointActorIndex::eACTOR1, PxTransform(C2P(_targetAnchor), C2P(_targetAnchorRotation)));
}
PhysicsBackend::SetJointActorPose(_joint, _targetAnchor, _targetAnchorRotation, 1);
}
void Joint::SetTargetAnchorRotation(const Quaternion& value)
{
if (Quaternion::NearEqual(value, _targetAnchorRotation))
return;
_targetAnchorRotation = value;
if (_joint && !_enableAutoAnchor)
{
_joint->setLocalPose(PxJointActorIndex::eACTOR1, PxTransform(C2P(_targetAnchor), C2P(_targetAnchorRotation)));
}
PhysicsBackend::SetJointActorPose(_joint, _targetAnchor, _targetAnchorRotation, 1);
}
void* Joint::GetPhysicsImpl() const
{
return _joint;
}
void Joint::SetJointLocation(const Vector3& location)
@@ -128,14 +117,9 @@ void Joint::SetJointOrientation(const Quaternion& orientation)
void Joint::GetCurrentForce(Vector3& linear, Vector3& angular) const
{
if (_joint && _joint->getConstraint())
{
_joint->getConstraint()->getForce(*(PxVec3*)&linear, *(PxVec3*)&angular);
}
else
{
linear = angular = Vector3::Zero;
}
linear = angular = Vector3::Zero;
if (_joint)
PhysicsBackend::GetJointForce(_joint, linear, angular);
}
void Joint::Create()
@@ -151,26 +135,25 @@ void Joint::Create()
}
// Create joint object
JointData data;
data.Physics = Physics::GetPhysics();
data.Actor0 = parent->GetRigidActor();
data.Actor1 = target ? target->GetRigidActor() : nullptr;
data.Pos0 = _localTransform.Translation;
data.Rot0 = _localTransform.Orientation;
data.Pos1 = _targetAnchor;
data.Rot1 = _targetAnchorRotation;
PhysicsJointDesc desc;
desc.Joint = this;
desc.Actor0 = parent->GetPhysicsActor();
desc.Actor1 = target ? target->GetPhysicsActor() : nullptr;
desc.Pos0 = _localTransform.Translation;
desc.Rot0 = _localTransform.Orientation;
desc.Pos1 = _targetAnchor;
desc.Rot1 = _targetAnchorRotation;
if (_enableAutoAnchor && target)
{
// Place target anchor at the joint location
data.Pos1 = Target->GetTransform().WorldToLocal(GetPosition());
data.Rot1 = WorldToLocal(Target->GetOrientation(), GetOrientation());
desc.Pos1 = Target->GetTransform().WorldToLocal(GetPosition());
desc.Rot1 = WorldToLocal(Target->GetOrientation(), GetOrientation());
}
_joint = CreateJoint(data);
_joint->userData = this;
_joint = CreateJoint(desc);
// Setup joint properties
_joint->setBreakForce(_breakForce, _breakTorque);
_joint->setConstraintFlag(PxConstraintFlag::eCOLLISION_ENABLED, _enableCollision);
PhysicsBackend::SetJointBreakForce(_joint, _breakForce, _breakTorque);
PhysicsBackend::SetJointFlags(_joint, _enableCollision ? PhysicsBackend::JointFlags::Collision : PhysicsBackend::JointFlags::None);
}
void Joint::OnJointBreak()
@@ -180,10 +163,8 @@ void Joint::OnJointBreak()
void Joint::Delete()
{
// Remove the joint
Physics::RemoveJoint(this);
_joint->userData = nullptr;
_joint->release();
PhysicsBackend::RemoveJoint(this);
PhysicsBackend::DestroyJoint(_joint);
_joint = nullptr;
}
@@ -192,8 +173,7 @@ void Joint::SetActors()
auto parent = dynamic_cast<IPhysicsActor*>(GetParent());
auto target = dynamic_cast<IPhysicsActor*>(Target.Get());
ASSERT(parent != nullptr);
_joint->setActors(parent ? parent->GetRigidActor() : nullptr, target ? target->GetRigidActor() : nullptr);
PhysicsBackend::SetJointActors(_joint, parent->GetPhysicsActor(), target ? target->GetPhysicsActor() : nullptr);
}
void Joint::OnTargetChanged()
@@ -347,7 +327,7 @@ void Joint::OnActiveInTreeChanged()
else
Delete();
}
// Joint object may not be created if actor is disabled on play mode begin (late init feature)
// Joint object may not be created if actor is disabled on play mode begin (late init feature)
else if (IsDuringPlay())
{
Create();
@@ -393,9 +373,6 @@ void Joint::OnTransformChanged()
_box = BoundingBox(_transform.Translation);
_sphere = BoundingSphere(_transform.Translation, 0.0f);
if (_joint)
{
_joint->setLocalPose(PxJointActorIndex::eACTOR0, PxTransform(C2P(_localTransform.Translation), C2P(_localTransform.Orientation)));
}
PhysicsBackend::SetJointActorPose(_joint, _localTransform.Translation, _localTransform.Orientation, 0);
}

View File

@@ -21,7 +21,7 @@ API_CLASS(Abstract) class FLAXENGINE_API Joint : public Actor
DECLARE_SCENE_OBJECT_ABSTRACT(Joint);
protected:
PxJoint* _joint;
void* _joint;
float _breakForce;
float _breakTorque;
Vector3 _targetAnchor;
@@ -133,12 +133,9 @@ public:
public:
/// <summary>
/// Gets the native PhysX joint object.
/// Gets the native physics backend object.
/// </summary>
FORCE_INLINE PxJoint* GetPhysXJoint() const
{
return _joint;
}
void* GetPhysicsImpl() const;
/// <summary>
/// Sets the location of the joint by automatically computing local position and target anchor to place a joint at the given location (world-space).
@@ -178,20 +175,9 @@ public:
protected:
struct JointData
{
PxPhysics* Physics;
PxRigidActor* Actor0;
PxRigidActor* Actor1;
Quaternion Rot0;
Quaternion Rot1;
Vector3 Pos0;
Vector3 Pos1;
};
Vector3 GetTargetPosition() const;
Quaternion GetTargetOrientation() const;
virtual PxJoint* CreateJoint(JointData& data) = 0;
virtual void* CreateJoint(const struct PhysicsJointDesc& desc) = 0;
#if USE_EDITOR
virtual void DrawPhysicsDebug(RenderView& view);
#endif

View File

@@ -2,9 +2,7 @@
#include "SliderJoint.h"
#include "Engine/Serialization/Serialization.h"
#include "Engine/Physics/Utilities.h"
#include "Engine/Physics/Physics.h"
#include <ThirdParty/PhysX/extensions/PxPrismaticJoint.h>
#include "Engine/Physics/PhysicsBackend.h"
SliderJoint::SliderJoint(const SpawnParams& params)
: Joint(params)
@@ -18,43 +16,28 @@ void SliderJoint::SetFlags(const SliderJointFlag value)
{
if (_flags == value)
return;
_flags = value;
if (_joint)
{
auto joint = static_cast<PxPrismaticJoint*>(_joint);
joint->setPrismaticJointFlag(PxPrismaticJointFlag::eLIMIT_ENABLED, (_flags & SliderJointFlag::Limit) != 0);
}
PhysicsBackend::SetSliderJointFlags(_joint, value);
}
void SliderJoint::SetLimit(const LimitLinearRange& value)
{
if (_limit == value)
return;
_limit = value;
if (_joint)
{
auto joint = static_cast<PxPrismaticJoint*>(_joint);
PxJointLinearLimitPair limit(*Physics::GetTolerancesScale(), value.Lower, value.Upper, value.ContactDist);
limit.stiffness = value.Spring.Stiffness;
limit.damping = value.Spring.Damping;
limit.restitution = value.Restitution;
ASSERT_LOW_LAYER(limit.isValid());
joint->setLimit(limit);
}
PhysicsBackend::SetSliderJointLimit(_joint, value);
}
float SliderJoint::GetCurrentPosition() const
{
return _joint ? static_cast<PxPrismaticJoint*>(_joint)->getPosition() : 0.0f;
return _joint ? PhysicsBackend::GetSliderJointPosition(_joint) : 0.0f;
}
float SliderJoint::GetCurrentVelocity() const
{
return _joint ? static_cast<PxPrismaticJoint*>(_joint)->getVelocity() : 0.0f;
return _joint ? PhysicsBackend::GetSliderJointVelocity(_joint) : 0.0f;
}
#if USE_EDITOR
@@ -111,21 +94,10 @@ void SliderJoint::Deserialize(DeserializeStream& stream, ISerializeModifier* mod
DESERIALIZE_MEMBER(UpperLimit, _limit.Upper);
}
PxJoint* SliderJoint::CreateJoint(JointData& data)
void* SliderJoint::CreateJoint(const PhysicsJointDesc& desc)
{
const PxTransform trans0(C2P(data.Pos0), C2P(data.Rot0));
const PxTransform trans1(C2P(data.Pos1), C2P(data.Rot1));
auto joint = PxPrismaticJointCreate(*data.Physics, data.Actor0, trans0, data.Actor1, trans1);
int32 flags = 0;
if (_flags & SliderJointFlag::Limit)
flags |= PxPrismaticJointFlag::eLIMIT_ENABLED;
joint->setPrismaticJointFlags(static_cast<PxPrismaticJointFlag::Enum>(flags));
PxJointLinearLimitPair limit(*Physics::GetTolerancesScale(), _limit.Lower, _limit.Upper, _limit.ContactDist);
limit.stiffness = _limit.Spring.Stiffness;
limit.damping = _limit.Spring.Damping;
limit.restitution = _limit.Restitution;
joint->setLimit(limit);
void* joint = PhysicsBackend::CreateSliderJoint(desc);
PhysicsBackend::SetSliderJointFlags(joint, _flags);
PhysicsBackend::SetSliderJointLimit(joint, _limit);
return joint;
}

View File

@@ -95,5 +95,5 @@ public:
protected:
// [Joint]
PxJoint* CreateJoint(JointData& data) override;
void* CreateJoint(const PhysicsJointDesc& desc) override;
};

View File

@@ -2,8 +2,7 @@
#include "SphericalJoint.h"
#include "Engine/Serialization/Serialization.h"
#include "Engine/Physics/Utilities.h"
#include <ThirdParty/PhysX/extensions/PxSphericalJoint.h>
#include "Engine/Physics/PhysicsBackend.h"
SphericalJoint::SphericalJoint(const SpawnParams& params)
: Joint(params)
@@ -15,33 +14,18 @@ void SphericalJoint::SetFlags(const SphericalJointFlag value)
{
if (_flags == value)
return;
_flags = value;
if (_joint)
{
auto joint = static_cast<PxSphericalJoint*>(_joint);
joint->setSphericalJointFlag(PxSphericalJointFlag::eLIMIT_ENABLED, (_flags & SphericalJointFlag::Limit) != 0);
}
PhysicsBackend::SetSphericalJointFlags(_joint, value);
}
void SphericalJoint::SetLimit(const LimitConeRange& value)
{
if (_limit == value)
return;
_limit = value;
if (_joint)
{
auto joint = static_cast<PxSphericalJoint*>(_joint);
PxJointLimitCone limit(Math::Clamp(value.YLimitAngle * DegreesToRadians, ZeroTolerance, PI - ZeroTolerance), Math::Clamp(value.ZLimitAngle * DegreesToRadians, ZeroTolerance, PI - ZeroTolerance), value.ContactDist);
limit.stiffness = value.Spring.Stiffness;
limit.damping = value.Spring.Damping;
limit.restitution = value.Restitution;
ASSERT_LOW_LAYER(limit.isValid());
joint->setLimitCone(limit);
}
PhysicsBackend::SetSphericalJointLimit(_joint, value);
}
#if USE_EDITOR
@@ -101,21 +85,10 @@ void SphericalJoint::Deserialize(DeserializeStream& stream, ISerializeModifier*
DESERIALIZE_MEMBER(ZLimitAngle, _limit.ZLimitAngle);
}
PxJoint* SphericalJoint::CreateJoint(JointData& data)
void* SphericalJoint::CreateJoint(const PhysicsJointDesc& desc)
{
const PxTransform trans0(C2P(data.Pos0), C2P(data.Rot0));
const PxTransform trans1(C2P(data.Pos1), C2P(data.Rot1));
auto joint = PxSphericalJointCreate(*data.Physics, data.Actor0, trans0, data.Actor1, trans1);
int32 flags = 0;
if (_flags & SphericalJointFlag::Limit)
flags |= PxSphericalJointFlag::eLIMIT_ENABLED;
joint->setSphericalJointFlags(static_cast<PxSphericalJointFlag::Enum>(flags));
PxJointLimitCone limit(Math::Clamp(_limit.YLimitAngle * DegreesToRadians, ZeroTolerance, PI - ZeroTolerance), Math::Clamp(_limit.ZLimitAngle * DegreesToRadians, ZeroTolerance, PI - ZeroTolerance), _limit.ContactDist);
limit.stiffness = _limit.Spring.Stiffness;
limit.damping = _limit.Spring.Damping;
limit.restitution = _limit.Restitution;
joint->setLimitCone(limit);
void* joint = PhysicsBackend::CreateSphericalJoint(desc);
PhysicsBackend::SetSphericalJointFlags(joint, _flags);
PhysicsBackend::SetSphericalJointLimit(joint, _limit);
return joint;
}

View File

@@ -85,5 +85,5 @@ public:
protected:
// [Joint]
PxJoint* CreateJoint(JointData& data) override;
void* CreateJoint(const PhysicsJointDesc& desc) override;
};

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,23 @@
// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
#pragma once
#if COMPILE_WITH_PHYSX
#include "Engine/Physics/PhysicsBackend.h"
#include "Types.h"
/// <summary>
/// Implementation of the physical simulation using PhysX.
/// </summary>
class PhysicsBackendPhysX
{
public:
static PxPhysics* GetPhysics();
#if COMPILE_WITH_PHYSICS_COOKING
static PxCooking* GetCooking();
#endif
static PxMaterial* GetDefaultMaterial();
};
#endif

View File

@@ -1,7 +1,9 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
#include "PhysicsStepper.h"
#include "Physics.h"
#if COMPILE_WITH_PHYSX
#include "PhysicsStepperPhysX.h"
#include "Engine/Physics/Physics.h"
#include "Engine/Profiler/ProfilerCPU.h"
#include <ThirdParty/PhysX/foundation/PxMath.h>
#include <ThirdParty/PhysX/PxSceneLock.h>
@@ -138,3 +140,5 @@ void FixedStepper::substepStrategy(const PxReal stepSize, PxU32& substepCount, P
mAccumulator -= PxReal(substepCount) * substepSize;
}
#endif

View File

@@ -1,7 +1,9 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
#pragma once
#if COMPILE_WITH_PHYSX
#include "Types.h"
#include <ThirdParty/PhysX/task/PxTask.h>
#include <ThirdParty/PhysX/foundation/PxSimpleTypes.h>
@@ -215,3 +217,5 @@ public:
mMaxSubSteps = maxSteps;
}
};
#endif

View File

@@ -1,10 +1,11 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
#include "SimulationEventCallback.h"
#include "Utilities.h"
#include "Colliders/Collider.h"
#include "Joints/Joint.h"
#include "Actors/RigidBody.h"
#if COMPILE_WITH_PHYSX
#include "SimulationEventCallbackPhysX.h"
#include "Engine/Physics/Colliders/Collider.h"
#include "Engine/Physics/Joints/Joint.h"
#include "Engine/Physics/Actors/RigidBody.h"
#include <ThirdParty/PhysX/extensions/PxJoint.h>
#include <ThirdParty/PhysX/PxShape.h>
@@ -136,7 +137,6 @@ void SimulationEventCallback::onConstraintBreak(PxConstraintInfo* constraints, P
for (uint32 i = 0; i < count; i++)
{
PxJoint* joint = reinterpret_cast<PxJoint*>(constraints[i].externalReference);
if (joint->userData)
BrokenJoints.Add(static_cast<Joint*>(joint->userData));
}
@@ -177,6 +177,7 @@ void SimulationEventCallback::onContact(const PxContactPairHeader& pairHeader, c
c.ThisActor = static_cast<PhysicsColliderActor*>(pair.shapes[0]->userData);
c.OtherActor = static_cast<PhysicsColliderActor*>(pair.shapes[1]->userData);
ASSERT_LOW_LAYER(c.ThisActor && c.OtherActor);
while (i.hasNextPatch())
{
@@ -219,6 +220,7 @@ void SimulationEventCallback::onContact(const PxContactPairHeader& pairHeader, c
const PxContactPair& pair = pairs[i.contactPairIndex];
c.ThisActor = static_cast<PhysicsColliderActor*>(pair.shapes[0]->userData);
c.OtherActor = static_cast<PhysicsColliderActor*>(pair.shapes[1]->userData);
ASSERT_LOW_LAYER(c.ThisActor && c.OtherActor);
Collision& collision = Collisions[CollidersPair(c.ThisActor, c.OtherActor)];
collision.ThisVelocity = P2C(linearVelocityActor0);
@@ -239,6 +241,7 @@ void SimulationEventCallback::onTrigger(PxTriggerPair* pairs, PxU32 count)
auto trigger = static_cast<PhysicsColliderActor*>(pair.triggerShape->userData);
auto otherCollider = static_cast<PhysicsColliderActor*>(pair.otherShape->userData);
ASSERT_LOW_LAYER(trigger && otherCollider);
CollidersPair collidersPair(trigger, otherCollider);
if (pair.status & PxPairFlag::eNOTIFY_TOUCH_LOST)
@@ -256,3 +259,5 @@ void SimulationEventCallback::onAdvance(const PxRigidBody* const* bodyBuffer, co
{
// Not used
}
#endif

View File

@@ -1,10 +1,12 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
#pragma once
#if COMPILE_WITH_PHYSX
#include "Types.h"
#include "Collisions.h"
#include "Colliders/Collider.h"
#include "Engine/Physics/Collisions.h"
#include "Engine/Physics/Colliders/Collider.h"
#include "Engine/Core/Types/Pair.h"
#include "Engine/Core/Collections/Dictionary.h"
#include <ThirdParty/PhysX/PxSimulationEventCallback.h>
@@ -12,7 +14,6 @@
/// <summary>
/// Default implementation of the PxSimulationEventCallback to send physics events to the other engine services.
/// </summary>
/// <seealso cref="PxSimulationEventCallback" />
class SimulationEventCallback : public PxSimulationEventCallback
{
public:
@@ -106,3 +107,5 @@ public:
void onTrigger(PxTriggerPair* pairs, PxU32 count) override;
void onAdvance(const PxRigidBody* const* bodyBuffer, const PxTransform* poseBuffer, const PxU32 count) override;
};
#endif

View File

@@ -0,0 +1,174 @@
// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
#pragma once
#if COMPILE_WITH_PHYSX
#include "Engine/Physics/Types.h"
#include "Engine/Core/Math/Vector2.h"
#include "Engine/Core/Math/Vector3.h"
#include "Engine/Core/Math/Vector4.h"
#include "Engine/Core/Math/Quaternion.h"
#include "Engine/Core/Math/BoundingBox.h"
#include <ThirdParty/PhysX/foundation/PxVec2.h>
#include <ThirdParty/PhysX/foundation/PxVec3.h>
#include <ThirdParty/PhysX/foundation/PxVec4.h>
#include <ThirdParty/PhysX/foundation/PxQuat.h>
#include <ThirdParty/PhysX/foundation/PxBounds3.h>
#include <ThirdParty/PhysX/characterkinematic/PxExtended.h>
#include <ThirdParty/PhysX/PxShape.h>
#include <ThirdParty/PhysX/PxQueryReport.h>
namespace physx
{
class PxScene;
class PxConvexMesh;
class PxTriangleMesh;
class PxCooking;
class PxPhysics;
class PxVec3;
class PxVec4;
class PxTransform;
class PxJoint;
class PxMat44;
class PxCpuDispatcher;
class PxGpuDispatcher;
class PxSimulationEventCallback;
struct PxActiveTransform;
class PxActor;
class PxRigidActor;
class PxRigidDynamic;
class PxRigidStatic;
class PxFoundation;
class PxShape;
class PxGeometry;
class PxGeometryHolder;
class PxProfileZoneManager;
class PxMaterial;
class PxPvd;
class PxBase;
class PxTolerancesScale;
class PxBaseTask;
class PxControllerManager;
class PxController;
class PxCapsuleController;
class PxQueryFilterCallback;
class PxControllerFilterCallback;
class PxHeightField;
struct PxFilterData;
struct PxRaycastHit;
struct PxSweepHit;
}
using namespace physx;
#define RELEASE_PHYSX(x) if(x) { (x)->release(); x = nullptr; }
inline PxVec2& C2P(const Vector2& v)
{
return *(PxVec2*)&v;
}
inline PxVec3& C2P(const Vector3& v)
{
return *(PxVec3*)&v;
}
inline PxVec4& C2P(const Vector4& v)
{
return *(PxVec4*)&v;
}
inline PxQuat& C2P(const Quaternion& v)
{
return *(PxQuat*)&v;
}
inline PxBounds3& C2P(const BoundingBox& v)
{
return *(PxBounds3*)&v;
}
inline Vector2& P2C(const PxVec2& v)
{
return *(Vector2*)&v;
}
inline Vector3& P2C(const PxVec3& v)
{
return *(Vector3*)&v;
}
inline Vector4& P2C(const PxVec4& v)
{
return *(Vector4*)&v;
}
inline Quaternion& P2C(const PxQuat& v)
{
return *(Quaternion*)&v;
}
inline BoundingBox& P2C(const PxBounds3& v)
{
return *(BoundingBox*)&v;
}
inline Vector3 P2C(const PxExtendedVec3& v)
{
#ifdef PX_BIG_WORLDS
return Vector3((float)v.x, (float)v.y, (float)v.z);
#else
return *(Vector3*)&v;
#endif
}
inline float M2ToCm2(float v)
{
return v * (100.0f * 100.0f);
}
inline float Cm2ToM2(float v)
{
return v / (100.0f * 100.0f);
}
inline float KgPerM3ToKgPerCm3(float v)
{
return v / (100.0f * 100.0f * 100.0f);
}
inline float RpmToRadPerS(float v)
{
return v * (PI / 30.0f);
}
inline float RadPerSToRpm(float v)
{
return v * (30.0f / PI);
}
inline void P2C(const PxRaycastHit& hit, RayCastHit& result)
{
result.Point = P2C(hit.position);
result.Normal = P2C(hit.normal);
result.Distance = hit.distance;
result.Collider = hit.shape ? static_cast<PhysicsColliderActor*>(hit.shape->userData) : nullptr;
result.FaceIndex = hit.faceIndex;
result.UV.X = hit.u;
result.UV.Y = hit.v;
}
inline void P2C(const PxSweepHit& hit, RayCastHit& result)
{
result.Point = P2C(hit.position);
result.Normal = P2C(hit.normal);
result.Distance = hit.distance;
result.Collider = hit.shape ? static_cast<PhysicsColliderActor*>(hit.shape->userData) : nullptr;
result.FaceIndex = hit.faceIndex;
result.UV = Vector2::Zero;
}
extern PxShapeFlags GetShapeFlags(bool isTrigger, bool isEnabled);
#endif

View File

@@ -14,7 +14,7 @@ API_AUTO_SERIALIZATION();
DECLARE_SCRIPTING_TYPE_MINIMAL(PhysicalMaterial);
private:
PxMaterial* _material;
void* _material;
public:
@@ -78,10 +78,10 @@ public:
/// Gets the PhysX material.
/// </summary>
/// <returns>The native material object.</returns>
PxMaterial* GetPhysXMaterial();
void* GetPhysicsMaterial();
/// <summary>
/// Updates the PhysX material (after any property change).
/// Updates the physics material (after any property change).
/// </summary>
void UpdatePhysXMaterial();
void UpdatePhysicsMaterial();
};

View File

@@ -1,5 +1,6 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
using System;
using Flax.Build;
using Flax.Build.NativeCpp;
@@ -13,16 +14,26 @@ public class Physics : EngineModule
/// </summary>
public static bool WithCooking = true;
/// <summary>
/// Physics system extension event to override for custom physics backend plugin.
/// </summary>
public static Action<Physics, BuildOptions> SetupPhysicsBackend = SetupPhysicsBackendPhysX;
/// <inheritdoc />
public override void Setup(BuildOptions options)
{
base.Setup(options);
options.PrivateDependencies.Add("PhysX");
SetupPhysicsBackend(this, options);
if (WithCooking)
{
options.PublicDefinitions.Add("COMPILE_WITH_PHYSICS_COOKING");
}
}
private static void SetupPhysicsBackendPhysX(Physics physics, BuildOptions options)
{
options.PrivateDependencies.Add("PhysX");
}
}

View File

@@ -1,142 +0,0 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
#include "Physics.h"
#include "Utilities.h"
#include "CollisionData.h"
#include "PhysicsScene.h"
#include "Actors/PhysicsColliderActor.h"
bool Physics::RayCast(const Vector3& origin, const Vector3& direction, const float maxDistance, uint32 layerMask, bool hitTriggers)
{
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)
{
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)
{
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)
{
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)
{
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)
{
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)
{
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)
{
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)
{
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)
{
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)
{
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)
{
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)
{
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)
{
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)
{
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)
{
return DefaultScene->CheckBox(center, halfExtents, rotation, layerMask, hitTriggers);
}
bool Physics::CheckSphere(const Vector3& center, const float radius, uint32 layerMask, bool hitTriggers)
{
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)
{
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)
{
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)
{
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)
{
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)
{
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)
{
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)
{
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)
{
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)
{
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)
{
return DefaultScene->OverlapConvex(center, convexMesh, scale, results, rotation, layerMask, hitTriggers);
}

View File

@@ -2,94 +2,14 @@
#include "Physics.h"
#include "PhysicsScene.h"
#include "PhysicsBackend.h"
#include "PhysicalMaterial.h"
#include "Engine/Core/Log.h"
#include "Engine/Platform/CPUInfo.h"
#include "PhysicsSettings.h"
#include "Utilities.h"
#include "PhysicsStepper.h"
#include "Engine/Level/Level.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>
#if WITH_PVD
#include <ThirdParty/PhysX/pvd/PxPvd.h>
#endif
#define PHYSX_VEHICLE_DEBUG_TELEMETRY 0
#if PHYSX_VEHICLE_DEBUG_TELEMETRY
#include "Engine/Core/Utilities.h"
#endif
class PhysXAllocator : public PxAllocatorCallback
{
public:
void* allocate(size_t size, const char* typeName, const char* filename, int line) override
{
return Allocator::Allocate(size, 16);
}
void deallocate(void* ptr) override
{
Allocator::Free(ptr);
}
};
class PhysXError : public PxErrorCallback
{
public:
PhysXError()
{
}
~PhysXError()
{
}
public:
// [PxErrorCallback]
void reportError(PxErrorCode::Enum code, const char* message, const char* file, int line) override
{
LOG(Error, "PhysX Error! Code: {0}.\n{1}\nSource: {2} : {3}.", static_cast<int32>(code), String(message), String(file), line);
}
};
PxPhysics* CPhysX = nullptr;
#if WITH_PVD
PxPvd* CPVD = nullptr;
#endif
namespace
{
PhysXAllocator PhysXAllocatorCallback;
PhysXError PhysXErrorCallback;
PxTolerancesScale ToleranceScale;
bool _queriesHitTriggers = true;
PhysicsCombineMode _frictionCombineMode = PhysicsCombineMode::Average;
PhysicsCombineMode _restitutionCombineMode = PhysicsCombineMode::Average;
PxFoundation* _foundation = nullptr;
#if COMPILE_WITH_PHYSICS_COOKING
PxCooking* Cooking = nullptr;
#endif
PxMaterial* DefaultMaterial = nullptr;
#if WITH_VEHICLE
bool VehicleSDKInitialized = false;
#endif
void reset()
{
Physics::Scenes.Resize(1);
}
}
#include "Engine/Engine/EngineService.h"
#include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Serialization/Serialization.h"
#include "Engine/Threading/Threading.h"
PhysicsScene* Physics::DefaultScene = nullptr;
Array<PhysicsScene*> Physics::Scenes;
@@ -98,7 +18,6 @@ uint32 Physics::LayerMasks[32];
class PhysicsService : public EngineService
{
public:
PhysicsService()
: EngineService(TEXT("Physics"), 0)
{
@@ -113,74 +32,14 @@ public:
PhysicsService PhysicsServiceInstance;
PxShapeFlags GetShapeFlags(bool isTrigger, bool isEnabled)
{
#if WITH_PVD
PxShapeFlags flags = PxShapeFlag::eVISUALIZATION;
#else
PxShapeFlags flags = static_cast<PxShapeFlags>(0);
#endif
if (isEnabled)
{
if (isTrigger)
{
flags |= PxShapeFlag::eTRIGGER_SHAPE;
if (_queriesHitTriggers)
flags |= PxShapeFlag::eSCENE_QUERY_SHAPE;
}
else
{
flags = PxShapeFlag::eSIMULATION_SHAPE | PxShapeFlag::eSCENE_QUERY_SHAPE;
}
}
return flags;
}
#if WITH_VEHICLE
void InitVehicleSDK()
{
if (!VehicleSDKInitialized)
{
VehicleSDKInitialized = true;
PxInitVehicleSDK(*CPhysX);
PxVehicleSetBasisVectors(PxVec3(0, 1, 0), PxVec3(1, 0, 0));
PxVehicleSetUpdateMode(PxVehicleUpdateMode::eVELOCITY_CHANGE);
}
}
#endif
void PhysicsSettings::Apply()
{
Time::_physicsMaxDeltaTime = MaxDeltaTime;
_queriesHitTriggers = QueriesHitTriggers;
_frictionCombineMode = FrictionCombineMode;
_restitutionCombineMode = RestitutionCombineMode;
Platform::MemoryCopy(Physics::LayerMasks, LayerMasks, sizeof(LayerMasks));
Physics::SetGravity(DefaultGravity);
Physics::SetBounceThresholdVelocity(BounceThresholdVelocity);
Physics::SetEnableCCD(!DisableCCD);
// TODO: setting eADAPTIVE_FORCE requires PxScene setup (physx docs: This flag is not mutable, and must be set in PxSceneDesc at scene creation.)
// TODO: update all shapes filter data
// TODO: update all shapes flags
/*
{
get all actors and then:
const PxU32 numShapes = actor->getNbShapes();
PxShape** shapes = (PxShape**)SAMPLE_ALLOC(sizeof(PxShape*)*numShapes);
actor->getShapes(shapes, numShapes);
for (PxU32 i = 0; i < numShapes; i++)
{
..
}
SAMPLE_FREE(shapes);
}*/
PhysicsBackend::ApplySettings(*this);
}
PhysicsSettings::PhysicsSettings()
@@ -225,193 +84,77 @@ PhysicalMaterial::PhysicalMaterial()
PhysicalMaterial::~PhysicalMaterial()
{
if (_material)
{
for (auto scene : Physics::Scenes)
scene->RemoveMaterial(_material);
}
}
PxMaterial* PhysicalMaterial::GetPhysXMaterial()
{
if (_material == nullptr && CPhysX)
{
_material = CPhysX->createMaterial(Friction, Friction, Restitution);
_material->userData = this;
const PhysicsCombineMode useFrictionCombineMode = OverrideFrictionCombineMode ? FrictionCombineMode : _frictionCombineMode;
_material->setFrictionCombineMode(static_cast<PxCombineMode::Enum>(useFrictionCombineMode));
const PhysicsCombineMode useRestitutionCombineMode = OverrideRestitutionCombineMode ? RestitutionCombineMode : _restitutionCombineMode;
_material->setRestitutionCombineMode(static_cast<PxCombineMode::Enum>(useRestitutionCombineMode));
}
return _material;
}
void PhysicalMaterial::UpdatePhysXMaterial()
{
if (_material != nullptr)
{
_material->setStaticFriction(Friction);
_material->setDynamicFriction(Friction);
const PhysicsCombineMode useFrictionCombineMode = OverrideFrictionCombineMode ? FrictionCombineMode : _frictionCombineMode;
_material->setFrictionCombineMode(static_cast<PxCombineMode::Enum>(useFrictionCombineMode));
_material->setRestitution(Restitution);
const PhysicsCombineMode useRestitutionCombineMode = OverrideRestitutionCombineMode ? RestitutionCombineMode : _restitutionCombineMode;
_material->setRestitutionCombineMode(static_cast<PxCombineMode::Enum>(useRestitutionCombineMode));
}
PhysicsBackend::DestroyObject(_material);
}
bool PhysicsService::Init()
{
#define CHECK_INIT(value, msg) if(!value) { LOG(Error, msg); return true; }
auto& settings = *PhysicsSettings::Get();
// Send info
LOG(Info, "Setup NVIDIA PhysX {0}.{1}.{2}", PX_PHYSICS_VERSION_MAJOR, PX_PHYSICS_VERSION_MINOR, PX_PHYSICS_VERSION_BUGFIX);
// Init PhysX foundation object
_foundation = PxCreateFoundation(PX_PHYSICS_VERSION, PhysXAllocatorCallback, PhysXErrorCallback);
CHECK_INIT(_foundation, "PxCreateFoundation failed!");
// Config
ToleranceScale.length = 100;
ToleranceScale.speed = 981;
PxPvd* pvd = nullptr;
#if WITH_PVD
{
// Init PVD
pvd = PxCreatePvd(*_foundation);
PxPvdTransport* transport = PxDefaultPvdSocketTransportCreate("127.0.0.1", 5425, 100);
//PxPvdTransport* transport = PxDefaultPvdFileTransportCreate("D:\\physx_sample.pxd2");
if (transport)
{
const bool isConnected = pvd->connect(*transport, PxPvdInstrumentationFlag::eALL);
if (isConnected)
{
LOG(Info, "Connected to PhysX Visual Debugger (PVD)");
}
}
CPVD = pvd;
}
#endif
// Init PhysX
CPhysX = PxCreatePhysics(PX_PHYSICS_VERSION, *_foundation, ToleranceScale, false, pvd);
CHECK_INIT(CPhysX, "PxCreatePhysics failed!");
// Init extensions
const bool extensionsInit = PxInitExtensions(*CPhysX, pvd);
CHECK_INIT(extensionsInit, "PxInitExtensions failed!");
// Init collision cooking
#if COMPILE_WITH_PHYSICS_COOKING
#if !USE_EDITOR
if (settings.SupportCookingAtRuntime)
#endif
{
PxCookingParams cookingParams(ToleranceScale);
cookingParams.meshWeldTolerance = 0.1f; // Weld to 1mm precision
cookingParams.meshPreprocessParams = PxMeshPreprocessingFlags(PxMeshPreprocessingFlag::eWELD_VERTICES);
Cooking = PxCreateCooking(PX_PHYSICS_VERSION, *_foundation, cookingParams);
CHECK_INIT(Cooking, "PxCreateCooking failed!");
}
#endif
// Initialize backend
if (PhysicsBackend::Init())
return true;
// Create default scene
Physics::DefaultScene = Physics::FindOrCreateScene(TEXT("Default"));
// Create default resources
DefaultMaterial = CPhysX->createMaterial(0.7f, 0.7f, 0.3f);
return false;
#undef CHECK_INIT
return Physics::DefaultScene == nullptr;
}
void PhysicsService::LateUpdate()
{
for (auto scene : Physics::Scenes)
scene->FlushRequests();
Physics::FlushRequests();
}
void PhysicsService::Dispose()
{
// Ensure to finish (wait for simulation end)
for (auto scene : Physics::Scenes)
for (PhysicsScene* scene : Physics::Scenes)
{
scene->CollectResults();
if (CPhysX)
scene->FlushRequests();
}
#if WITH_VEHICLE
if (VehicleSDKInitialized)
// Dispose scenes
for (PhysicsScene* scene : Physics::Scenes)
{
VehicleSDKInitialized = false;
PxCloseVehicleSDK();
Delete(scene);
}
#endif
// Cleanup
RELEASE_PHYSX(DefaultMaterial);
Physics::Scenes.Resize(0);
Physics::DefaultScene = nullptr;
// Remove all scenes still registered
const int32 numScenes = CPhysX ? CPhysX->getNbScenes() : 0;
if (numScenes)
{
Array<PxScene*> PScenes;
PScenes.Resize(numScenes);
PScenes.SetAll(nullptr);
CPhysX->getScenes(PScenes.Get(), sizeof(PxScene*) * numScenes);
// Dispose backend
PhysicsBackend::Shutdown();
}
for (int32 i = 0; i < numScenes; i++)
PhysicsScene* Physics::FindOrCreateScene(const StringView& name)
{
auto scene = FindScene(name);
if (scene == nullptr)
{
const auto& settings = *PhysicsSettings::Get();
scene = New<PhysicsScene>();
if (scene->Init(name, settings))
{
if (PScenes[i])
{
PScenes[i]->release();
}
Delete(scene);
scene = nullptr;
}
else
Scenes.Add(scene);
return scene;
}
return scene;
}
#if COMPILE_WITH_PHYSICS_COOKING
RELEASE_PHYSX(Cooking);
#endif
if (CPhysX)
PhysicsScene* Physics::FindScene(const StringView& name)
{
for (PhysicsScene* scene : Scenes)
{
PxCloseExtensions();
if (scene->GetName() == name)
return scene;
}
RELEASE_PHYSX(CPhysX);
#if WITH_PVD
RELEASE_PHYSX(CPVD);
#endif
RELEASE_PHYSX(_foundation);
return nullptr;
}
PxPhysics* Physics::GetPhysics()
bool Physics::GetAutoSimulation()
{
return CPhysX;
}
PxCooking* Physics::GetCooking()
{
return Cooking;
}
PxTolerancesScale* Physics::GetTolerancesScale()
{
return &ToleranceScale;
return !DefaultScene || DefaultScene->GetAutoSimulation();
}
Vector3 Physics::GetGravity()
@@ -449,13 +192,7 @@ void Physics::SetBounceThresholdVelocity(const float value)
void Physics::Simulate(float dt)
{
if (DefaultScene)
DefaultScene->Simulate(dt);
}
void Physics::SimulateAll(float dt)
{
for (auto scene : Scenes)
for (PhysicsScene* scene : Scenes)
{
if (scene->GetAutoSimulation())
scene->Simulate(dt);
@@ -468,67 +205,373 @@ void Physics::CollectResults()
DefaultScene->CollectResults();
}
void Physics::CollectResultsAll()
{
for (auto scene : Scenes)
scene->CollectResults();
}
bool Physics::IsDuringSimulation()
{
if (DefaultScene)
return DefaultScene->IsDuringSimulation();
return false;
return DefaultScene && DefaultScene->IsDuringSimulation();
}
PxMaterial* Physics::GetDefaultMaterial()
void Physics::FlushRequests()
{
return DefaultMaterial;
PROFILE_CPU_NAMED("Physics.FlushRequests");
for (PhysicsScene* scene : Scenes)
PhysicsBackend::FlushRequests(scene->GetPhysicsScene());
PhysicsBackend::FlushRequests();
}
bool Physics::GetAutoSimulation()
bool Physics::RayCast(const Vector3& origin, const Vector3& direction, const float maxDistance, uint32 layerMask, bool hitTriggers)
{
if (DefaultScene)
return DefaultScene->GetAutoSimulation();
return false;
return DefaultScene->RayCast(origin, direction, maxDistance, layerMask, hitTriggers);
}
void Physics::FlushRequestsAll()
bool Physics::RayCast(const Vector3& origin, const Vector3& direction, RayCastHit& hitInfo, const float maxDistance, uint32 layerMask, bool hitTriggers)
{
for (auto scene : Scenes)
scene->FlushRequests();
return DefaultScene->RayCast(origin, direction, hitInfo, maxDistance, layerMask, hitTriggers);
}
void Physics::RemoveJoint(Joint* joint)
bool Physics::RayCastAll(const Vector3& origin, const Vector3& direction, Array<RayCastHit>& results, const float maxDistance, uint32 layerMask, bool hitTriggers)
{
for (auto scene : Scenes)
scene->RemoveJoint(joint);
return DefaultScene->RayCastAll(origin, direction, results, maxDistance, layerMask, hitTriggers);
}
PhysicsScene* Physics::FindOrCreateScene(const String& name)
bool Physics::BoxCast(const Vector3& center, const Vector3& halfExtents, const Vector3& direction, const Quaternion& rotation, const float maxDistance, uint32 layerMask, bool hitTriggers)
{
auto scene = FindScene(name);
return DefaultScene->BoxCast(center, halfExtents, direction, rotation, maxDistance, layerMask, hitTriggers);
}
if (scene == nullptr)
bool Physics::BoxCast(const Vector3& center, const Vector3& halfExtents, const Vector3& direction, RayCastHit& hitInfo, const Quaternion& rotation, const float maxDistance, uint32 layerMask, bool hitTriggers)
{
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)
{
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)
{
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)
{
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)
{
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)
{
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)
{
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)
{
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)
{
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)
{
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)
{
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)
{
return DefaultScene->CheckBox(center, halfExtents, rotation, layerMask, hitTriggers);
}
bool Physics::CheckSphere(const Vector3& center, const float radius, uint32 layerMask, bool hitTriggers)
{
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)
{
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)
{
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)
{
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)
{
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)
{
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)
{
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)
{
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)
{
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)
{
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)
{
return DefaultScene->OverlapConvex(center, convexMesh, scale, results, rotation, layerMask, hitTriggers);
}
PhysicsScene::PhysicsScene(const SpawnParams& params)
: ScriptingObject(params)
{
}
PhysicsScene::~PhysicsScene()
{
if (_scene)
PhysicsBackend::DestroyScene(_scene);
}
String PhysicsScene::GetName() const
{
return _name;
}
bool PhysicsScene::GetAutoSimulation() const
{
return _autoSimulation;
}
void PhysicsScene::SetAutoSimulation(bool value)
{
_autoSimulation = value;
}
void PhysicsScene::SetGravity(const Vector3& value)
{
PhysicsBackend::SetSceneGravity(_scene, value);
}
Vector3 PhysicsScene::GetGravity()
{
return PhysicsBackend::GetSceneGravity(_scene);
}
bool PhysicsScene::GetEnableCCD()
{
return PhysicsBackend::GetSceneEnableCCD(_scene);
}
void PhysicsScene::SetEnableCCD(bool value)
{
PhysicsBackend::SetSceneEnableCCD(_scene, value);
}
float PhysicsScene::GetBounceThresholdVelocity()
{
return PhysicsBackend::GetSceneBounceThresholdVelocity(_scene);
}
void PhysicsScene::SetBounceThresholdVelocity(float value)
{
PhysicsBackend::SetSceneBounceThresholdVelocity(_scene, value);
}
bool PhysicsScene::Init(const StringView& name, const PhysicsSettings& settings)
{
if (_scene)
{
auto& settings = *PhysicsSettings::Get();
scene = New<PhysicsScene>(name, settings);
Scenes.Add(scene);
PhysicsBackend::DestroyScene(_scene);
}
return scene;
_name = name;
_scene = PhysicsBackend::CreateScene(settings);
return _scene == nullptr;
}
PhysicsScene* Physics::FindScene(const String& name)
void PhysicsScene::Simulate(float dt)
{
for (auto scene : Scenes)
{
if (scene->GetName() == name)
return scene;
}
return nullptr;
ASSERT(IsInMainThread() && !_isDuringSimulation);
_isDuringSimulation = true;
PhysicsBackend::StartSimulateScene(_scene, dt);
}
bool PhysicsScene::IsDuringSimulation() const
{
return _isDuringSimulation;
}
void PhysicsScene::CollectResults()
{
if (!_isDuringSimulation)
return;
ASSERT(IsInMainThread());
PhysicsBackend::EndSimulateScene(_scene);
_isDuringSimulation = false;
}
bool PhysicsScene::RayCast(const Vector3& origin, const Vector3& direction, const float maxDistance, uint32 layerMask, bool hitTriggers)
{
return PhysicsBackend::RayCast(_scene, origin, direction, maxDistance, layerMask, hitTriggers);
}
bool PhysicsScene::RayCast(const Vector3& origin, const Vector3& direction, RayCastHit& hitInfo, const float maxDistance, uint32 layerMask, bool hitTriggers)
{
return PhysicsBackend::RayCast(_scene, origin, direction, hitInfo, maxDistance, layerMask, hitTriggers);
}
bool PhysicsScene::RayCastAll(const Vector3& origin, const Vector3& direction, Array<RayCastHit>& results, const float maxDistance, uint32 layerMask, bool hitTriggers)
{
return PhysicsBackend::RayCastAll(_scene, origin, direction, results, maxDistance, layerMask, hitTriggers);
}
bool PhysicsScene::BoxCast(const Vector3& center, const Vector3& halfExtents, const Vector3& direction, const Quaternion& rotation, const float maxDistance, uint32 layerMask, bool hitTriggers)
{
return PhysicsBackend::BoxCast(_scene, center, halfExtents, direction, rotation, maxDistance, layerMask, hitTriggers);
}
bool PhysicsScene::BoxCast(const Vector3& center, const Vector3& halfExtents, const Vector3& direction, RayCastHit& hitInfo, const Quaternion& rotation, const float maxDistance, uint32 layerMask, bool hitTriggers)
{
return PhysicsBackend::BoxCast(_scene, center, halfExtents, direction, hitInfo, rotation, maxDistance, layerMask, hitTriggers);
}
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)
{
return PhysicsBackend::BoxCastAll(_scene, center, halfExtents, direction, results, rotation, maxDistance, layerMask, hitTriggers);
}
bool PhysicsScene::SphereCast(const Vector3& center, const float radius, const Vector3& direction, const float maxDistance, uint32 layerMask, bool hitTriggers)
{
return PhysicsBackend::SphereCast(_scene, center, radius, direction, maxDistance, layerMask, hitTriggers);
}
bool PhysicsScene::SphereCast(const Vector3& center, const float radius, const Vector3& direction, RayCastHit& hitInfo, const float maxDistance, uint32 layerMask, bool hitTriggers)
{
return PhysicsBackend::SphereCast(_scene, center, radius, direction, hitInfo, maxDistance, layerMask, hitTriggers);
}
bool PhysicsScene::SphereCastAll(const Vector3& center, const float radius, const Vector3& direction, Array<RayCastHit>& results, const float maxDistance, uint32 layerMask, bool hitTriggers)
{
return PhysicsBackend::SphereCastAll(_scene, center, radius, direction, results, maxDistance, layerMask, hitTriggers);
}
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)
{
return PhysicsBackend::CapsuleCast(_scene, center, radius, height, direction, rotation, maxDistance, layerMask, hitTriggers);
}
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)
{
return PhysicsBackend::CapsuleCast(_scene, center, radius, height, direction, hitInfo, rotation, maxDistance, layerMask, hitTriggers);
}
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)
{
return PhysicsBackend::CapsuleCastAll(_scene, center, radius, height, direction, results, rotation, maxDistance, layerMask, hitTriggers);
}
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)
{
return PhysicsBackend::ConvexCast(_scene, center, convexMesh, scale, direction, rotation, maxDistance, layerMask, hitTriggers);
}
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)
{
return PhysicsBackend::ConvexCast(_scene, center, convexMesh, scale, direction, hitInfo, rotation, maxDistance, layerMask, hitTriggers);
}
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)
{
return PhysicsBackend::ConvexCastAll(_scene, center, convexMesh, scale, direction, results, rotation, maxDistance, layerMask, hitTriggers);
}
bool PhysicsScene::CheckBox(const Vector3& center, const Vector3& halfExtents, const Quaternion& rotation, uint32 layerMask, bool hitTriggers)
{
return PhysicsBackend::CheckBox(_scene, center, halfExtents, rotation, layerMask, hitTriggers);
}
bool PhysicsScene::CheckSphere(const Vector3& center, const float radius, uint32 layerMask, bool hitTriggers)
{
return PhysicsBackend::CheckSphere(_scene, center, radius, layerMask, hitTriggers);
}
bool PhysicsScene::CheckCapsule(const Vector3& center, const float radius, const float height, const Quaternion& rotation, uint32 layerMask, bool hitTriggers)
{
return PhysicsBackend::CheckCapsule(_scene, center, radius, height, rotation, layerMask, hitTriggers);
}
bool PhysicsScene::CheckConvex(const Vector3& center, const CollisionData* convexMesh, const Vector3& scale, const Quaternion& rotation, uint32 layerMask, bool hitTriggers)
{
return PhysicsBackend::CheckConvex(_scene, center, convexMesh, scale, rotation, layerMask, hitTriggers);
}
bool PhysicsScene::OverlapBox(const Vector3& center, const Vector3& halfExtents, Array<Collider*>& results, const Quaternion& rotation, uint32 layerMask, bool hitTriggers)
{
return PhysicsBackend::OverlapBox(_scene, center, halfExtents, results, rotation, layerMask, hitTriggers);
}
bool PhysicsScene::OverlapSphere(const Vector3& center, const float radius, Array<Collider*>& results, uint32 layerMask, bool hitTriggers)
{
return PhysicsBackend::OverlapSphere(_scene, center, radius, results, layerMask, hitTriggers);
}
bool PhysicsScene::OverlapCapsule(const Vector3& center, const float radius, const float height, Array<Collider*>& results, const Quaternion& rotation, uint32 layerMask, bool hitTriggers)
{
return PhysicsBackend::OverlapCapsule(_scene, center, radius, height, results, rotation, layerMask, hitTriggers);
}
bool PhysicsScene::OverlapConvex(const Vector3& center, const CollisionData* convexMesh, const Vector3& scale, Array<Collider*>& results, const Quaternion& rotation, uint32 layerMask, bool hitTriggers)
{
return PhysicsBackend::OverlapConvex(_scene, center, convexMesh, scale, results, rotation, layerMask, hitTriggers);
}
bool PhysicsScene::OverlapBox(const Vector3& center, const Vector3& halfExtents, Array<PhysicsColliderActor*>& results, const Quaternion& rotation, uint32 layerMask, bool hitTriggers)
{
return PhysicsBackend::OverlapBox(_scene, center, halfExtents, results, rotation, layerMask, hitTriggers);
}
bool PhysicsScene::OverlapSphere(const Vector3& center, const float radius, Array<PhysicsColliderActor*>& results, uint32 layerMask, bool hitTriggers)
{
return PhysicsBackend::OverlapSphere(_scene, center, radius, results, layerMask, hitTriggers);
}
bool PhysicsScene::OverlapCapsule(const Vector3& center, const float radius, const float height, Array<PhysicsColliderActor*>& results, const Quaternion& rotation, uint32 layerMask, bool hitTriggers)
{
return PhysicsBackend::OverlapCapsule(_scene, center, radius, height, results, rotation, layerMask, hitTriggers);
}
bool PhysicsScene::OverlapConvex(const Vector3& center, const CollisionData* convexMesh, const Vector3& scale, Array<PhysicsColliderActor*>& results, const Quaternion& rotation, uint32 layerMask, bool hitTriggers)
{
return PhysicsBackend::OverlapConvex(_scene, center, convexMesh, scale, results, rotation, layerMask, hitTriggers);
}

View File

@@ -2,125 +2,35 @@
#pragma once
#include "Engine/Core/Math/Vector2.h"
#include "Engine/Core/Math/Vector3.h"
#include "Engine/Core/Math/Quaternion.h"
#include "Engine/Scripting/ScriptingType.h"
#include "Types.h"
class PhysicsColliderActor;
class PhysicsScene;
class Joint;
class Collider;
class CollisionData;
/// <summary>
/// Raycast hit result data.
/// </summary>
API_STRUCT() struct RayCastHit
{
DECLARE_SCRIPTING_TYPE_NO_SPAWN(RayCastHit);
/// <summary>
/// The collider that was hit.
/// </summary>
API_FIELD() PhysicsColliderActor* Collider = nullptr;
/// <summary>
/// The normal of the surface the ray hit.
/// </summary>
API_FIELD() Vector3 Normal;
/// <summary>
/// The distance from the ray's origin to the hit location.
/// </summary>
API_FIELD() float Distance;
/// <summary>
/// The point in the world space where ray hit the collider.
/// </summary>
API_FIELD() Vector3 Point;
/// <summary>
/// The index of the face that was hit. Valid only for convex mesh (polygon index), triangle mesh (triangle index) and height field (triangle index).
/// </summary>
/// <seealso cref="CollisionData.GetModelTriangle" />
API_FIELD() uint32 FaceIndex;
/// <summary>
/// The barycentric coordinates of hit triangle. Valid only for triangle mesh and height field.
/// </summary>
API_FIELD() Vector2 UV;
public:
/// <summary>
/// Gathers the data from the specified hit (PhysX).
/// </summary>
/// <param name="hit">The hit.</param>
void Gather(const PxRaycastHit& hit);
/// <summary>
/// Gathers the data from the specified hit (PhysX).
/// </summary>
/// <param name="hit">The hit.</param>
void Gather(const PxSweepHit& hit);
};
/// <summary>
/// Physics simulation system.
/// </summary>
API_CLASS(Static) class FLAXENGINE_API Physics
{
DECLARE_SCRIPTING_TYPE_NO_SPAWN(Physics);
DECLARE_SCRIPTING_TYPE_NO_SPAWN(Physics);
/// <summary>
/// Gets the master physics object.
/// </summary>
static PxPhysics* GetPhysics();
#if COMPILE_WITH_PHYSICS_COOKING
/// <summary>
/// Gets physics cooking object
/// </summary>
static PxCooking* GetCooking();
#endif
/// <summary>
/// Gets the physics tolerances scale.
/// </summary>
static PxTolerancesScale* GetTolerancesScale();
/// <summary>
/// Gets the default physical material.
/// </summary>
static PxMaterial* GetDefaultMaterial();
public:
/// <summary>
/// The default physics scene.
/// </summary>
API_FIELD(ReadOnly)
static PhysicsScene* DefaultScene;
API_FIELD(ReadOnly) static PhysicsScene* DefaultScene;
/// <summary>
/// List with all physics scenes (readonly).
/// </summary>
API_FIELD(ReadOnly)
static Array<PhysicsScene*, HeapAllocation> Scenes;
API_FIELD(ReadOnly) static Array<PhysicsScene*, HeapAllocation> Scenes;
/// <summary>
/// Finds an existing <see cref="PhysicsScene"/> or creates it if it does not exist.
/// </summary>
API_FUNCTION() static PhysicsScene* FindOrCreateScene(const String& name);
API_FUNCTION() static PhysicsScene* FindOrCreateScene(const StringView& name);
/// <summary>
///Finds an existing scene.
/// Finds an existing scene.
/// </summary>
API_FUNCTION() static PhysicsScene* FindScene(const String& name);
API_FUNCTION() static PhysicsScene* FindScene(const StringView& name);
public:
/// <summary>
@@ -162,9 +72,29 @@ public:
/// The collision layers masks. Used to define layer-based collision detection.
/// </summary>
static uint32 LayerMasks[32];
public:
/// <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() static void Simulate(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>
/// Checks if physical simulation is running.
/// </summary>
API_PROPERTY() static bool IsDuringSimulation();
/// <summary>
/// Flushes any latent physics actions (eg. object destroy, actor add/remove to the scene, etc.).
/// </summary>
API_FUNCTION() static void FlushRequests();
public:
/// <summary>
/// Performs a raycast against objects in the scene.
/// </summary>
@@ -509,84 +439,4 @@ public:
/// <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() static 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:
/// <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() 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>
/// <returns>True if simulation is active, otherwise false</returns>
API_PROPERTY() static bool IsDuringSimulation();
public:
/// <summary>
/// Flushes the async requests to add/remove actors, remove materials, etc..
/// </summary>
static void FlushRequestsAll();
/// <summary>
/// Removes the material (using safe async request).
/// </summary>
/// <param name="material">The material.</param>
static 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>
static void RemoveObject(PxBase* obj);
/// <summary>
/// Adds the actor (using safe async request).
/// </summary>
/// <param name="actor">The actor.</param>
static 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>
static void AddActor(PxRigidDynamic* actor, bool putToSleep = false);
/// <summary>
/// Removes the actor (using safe async request).
/// </summary>
/// <param name="actor">The actor.</param>
static void RemoveActor(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>
static 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>
static void RemoveJoint(Joint* joint);
};

View File

@@ -0,0 +1,294 @@
// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
#pragma once
#include "Physics.h"
#include "PhysicsSettings.h"
struct HingeJointDrive;
struct SpringParameters;
struct LimitLinearRange;
struct LimitAngularRange;
struct LimitConeRange;
struct LimitLinear;
struct D6JointDrive;
enum class HingeJointFlag;
enum class DistanceJointFlag;
enum class SliderJointFlag;
enum class SphericalJointFlag;
enum class D6JointAxis;
enum class D6JointMotion;
enum class D6JointDriveType;
class IPhysicsActor;
class PhysicalMaterial;
class JsonAsset;
struct PhysicsJointDesc
{
Joint* Joint;
void* Actor0;
void* Actor1;
Quaternion Rot0;
Quaternion Rot1;
Vector3 Pos0;
Vector3 Pos1;
};
/// <summary>
/// Interface for the physical simulation backend implementation.
/// </summary>
class PhysicsBackend
{
public:
enum class ActorFlags
{
None = 0,
NoGravity = 1 << 0,
NoSimulation = 1 << 1,
};
enum class RigidDynamicFlags
{
None = 0,
Kinematic = 1 << 0,
CCD = 1 << 1,
};
enum class JointFlags
{
None = 0,
Collision = 1 << 0,
};
enum class ActionType
{
Sleep,
};
struct HeightFieldSample
{
int16 Height;
uint8 MaterialIndex0;
uint8 MaterialIndex1;
};
enum class HeightFieldMaterial
{
Hole = 127,
};
public:
// General
static bool Init();
static void Shutdown();
static void ApplySettings(const PhysicsSettings& settings);
// Scene
static void* CreateScene(const PhysicsSettings& settings);
static void DestroyScene(void* scene);
static void StartSimulateScene(void* scene, float dt);
static void EndSimulateScene(void* scene);
static Vector3 GetSceneGravity(void* scene);
static void SetSceneGravity(void* scene, const Vector3& value);
static bool GetSceneEnableCCD(void* scene);
static void SetSceneEnableCCD(void* scene, bool value);
static float GetSceneBounceThresholdVelocity(void* scene);
static void SetSceneBounceThresholdVelocity(void* scene, float value);
static void AddSceneActor(void* scene, void* actor);
static void RemoveSceneActor(void* scene, void* actor);
static void AddSceneActorAction(void* scene, void* actor, ActionType action);
// Scene Queries
static bool RayCast(void* scene, const Vector3& origin, const Vector3& direction, float maxDistance, uint32 layerMask, bool hitTriggers);
static bool RayCast(void* scene, const Vector3& origin, const Vector3& direction, RayCastHit& hitInfo, float maxDistance, uint32 layerMask, bool hitTriggers);
static bool RayCastAll(void* scene, const Vector3& origin, const Vector3& direction, Array<RayCastHit, HeapAllocation>& results, float maxDistance, uint32 layerMask, bool hitTriggers);
static bool BoxCast(void* scene, const Vector3& center, const Vector3& halfExtents, const Vector3& direction, const Quaternion& rotation, float maxDistance, uint32 layerMask, bool hitTriggers);
static bool BoxCast(void* scene, const Vector3& center, const Vector3& halfExtents, const Vector3& direction, RayCastHit& hitInfo, const Quaternion& rotation, float maxDistance, uint32 layerMask, bool hitTriggers);
static bool BoxCastAll(void* scene, const Vector3& center, const Vector3& halfExtents, const Vector3& direction, Array<RayCastHit, HeapAllocation>& results, const Quaternion& rotation, float maxDistance, uint32 layerMask, bool hitTriggers);
static bool SphereCast(void* scene, const Vector3& center, float radius, const Vector3& direction, float maxDistance, uint32 layerMask, bool hitTriggers);
static bool SphereCast(void* scene, const Vector3& center, float radius, const Vector3& direction, RayCastHit& hitInfo, float maxDistance, uint32 layerMask, bool hitTriggers);
static bool SphereCastAll(void* scene, const Vector3& center, float radius, const Vector3& direction, Array<RayCastHit, HeapAllocation>& results, float maxDistance, uint32 layerMask, bool hitTriggers);
static bool CapsuleCast(void* scene, const Vector3& center, float radius, float height, const Vector3& direction, const Quaternion& rotation, float maxDistance, uint32 layerMask, bool hitTriggers);
static bool CapsuleCast(void* scene, const Vector3& center, float radius, float height, const Vector3& direction, RayCastHit& hitInfo, const Quaternion& rotation, float maxDistance, uint32 layerMask, bool hitTriggers);
static bool CapsuleCastAll(void* scene, const Vector3& center, float radius, float height, const Vector3& direction, Array<RayCastHit, HeapAllocation>& results, const Quaternion& rotation, float maxDistance, uint32 layerMask, bool hitTriggers);
static bool ConvexCast(void* scene, const Vector3& center, const CollisionData* convexMesh, const Vector3& scale, const Vector3& direction, const Quaternion& rotation, float maxDistance, uint32 layerMask, bool hitTriggers);
static bool ConvexCast(void* scene, const Vector3& center, const CollisionData* convexMesh, const Vector3& scale, const Vector3& direction, RayCastHit& hitInfo, const Quaternion& rotation, float maxDistance, uint32 layerMask, bool hitTriggers);
static bool ConvexCastAll(void* scene, const Vector3& center, const CollisionData* convexMesh, const Vector3& scale, const Vector3& direction, Array<RayCastHit, HeapAllocation>& results, const Quaternion& rotation, float maxDistance, uint32 layerMask, bool hitTriggers);
static bool CheckBox(void* scene, const Vector3& center, const Vector3& halfExtents, const Quaternion& rotation, uint32 layerMask, bool hitTriggers);
static bool CheckSphere(void* scene, const Vector3& center, float radius, uint32 layerMask, bool hitTriggers);
static bool CheckCapsule(void* scene, const Vector3& center, float radius, float height, const Quaternion& rotation, uint32 layerMask, bool hitTriggers);
static bool CheckConvex(void* scene, const Vector3& center, const CollisionData* convexMesh, const Vector3& scale, const Quaternion& rotation, uint32 layerMask, bool hitTriggers);
static bool OverlapBox(void* scene, const Vector3& center, const Vector3& halfExtents, Array<Collider*, HeapAllocation>& results, const Quaternion& rotation, uint32 layerMask, bool hitTriggers);
static bool OverlapSphere(void* scene, const Vector3& center, float radius, Array<Collider*, HeapAllocation>& results, uint32 layerMask, bool hitTriggers);
static bool OverlapCapsule(void* scene, const Vector3& center, float radius, float height, Array<Collider*, HeapAllocation>& results, const Quaternion& rotation, uint32 layerMask, bool hitTriggers);
static bool OverlapConvex(void* scene, const Vector3& center, const CollisionData* convexMesh, const Vector3& scale, Array<Collider*, HeapAllocation>& results, const Quaternion& rotation, uint32 layerMask, bool hitTriggers);
static bool OverlapBox(void* scene, const Vector3& center, const Vector3& halfExtents, Array<PhysicsColliderActor*, HeapAllocation>& results, const Quaternion& rotation, uint32 layerMask, bool hitTriggers);
static bool OverlapSphere(void* scene, const Vector3& center, float radius, Array<PhysicsColliderActor*, HeapAllocation>& results, uint32 layerMask, bool hitTriggers);
static bool OverlapCapsule(void* scene, const Vector3& center, float radius, float height, Array<PhysicsColliderActor*, HeapAllocation>& results, const Quaternion& rotation, uint32 layerMask, bool hitTriggers);
static bool OverlapConvex(void* scene, const Vector3& center, const CollisionData* convexMesh, const Vector3& scale, Array<PhysicsColliderActor*, HeapAllocation>& results, const Quaternion& rotation, uint32 layerMask, bool hitTriggers);
// Actors
static ActorFlags GetActorFlags(void* actor);
static void SetActorFlags(void* actor, ActorFlags value);
static void GetActorBounds(void* actor, BoundingBox& bounds);
static void* CreateRigidDynamicActor(IPhysicsActor* actor, const Vector3& position, const Quaternion& orientation);
static void* CreateRigidStaticActor(IPhysicsActor* actor, const Vector3& position, const Quaternion& orientation);
static RigidDynamicFlags GetRigidDynamicActorFlags(void* actor);
static void SetRigidDynamicActorFlags(void* actor, RigidDynamicFlags value);
static void GetRigidActorPose(void* actor, Vector3& position, Quaternion& orientation);
static void SetRigidActorPose(void* actor, const Vector3& position, const Quaternion& orientation, bool kinematic = false, bool wakeUp = false);
static void SetRigidDynamicActorLinearDamping(void* actor, float value);
static void SetRigidDynamicActorAngularDamping(void* actor, float value);
static void SetRigidDynamicActorMaxAngularVelocity(void* actor, float value);
static void SetRigidDynamicActorConstraints(void* actor, RigidbodyConstraints value);
static Vector3 GetRigidDynamicActorLinearVelocity(void* actor);
static void SetRigidDynamicActorLinearVelocity(void* actor, const Vector3& value, bool wakeUp);
static Vector3 GetRigidDynamicActorAngularVelocity(void* actor);
static void SetRigidDynamicActorAngularVelocity(void* actor, const Vector3& value, bool wakeUp);
static Vector3 GetRigidDynamicActorCenterOfMass(void* actor);
static void SetRigidDynamicActorCenterOfMassOffset(void* actor, const Vector3& value);
static bool GetRigidDynamicActorIsSleeping(void* actor);
static void GetRigidActorSleep(void* actor);
static void GetRigidDynamicActorWakeUp(void* actor);
static float GetRigidDynamicActorSleepThreshold(void* actor);
static void SetRigidDynamicActorSleepThreshold(void* actor, float value);
static float GetRigidDynamicActorMaxDepenetrationVelocity(void* actor);
static void SetRigidDynamicActorMaxDepenetrationVelocity(void* actor, float value);
static void SetRigidDynamicActorSolverIterationCounts(void* actor, int32 minPositionIters, int32 minVelocityIters);
static void UpdateRigidDynamicActorMass(void* actor, float& mass, float massScale, bool autoCalculate);
static void AddRigidDynamicActorForce(void* actor, const Vector3& force, ForceMode mode);
static void AddRigidDynamicActorForceAtPosition(void* actor, const Vector3& force, const Vector3& position, ForceMode mode);
static void AddRigidDynamicActorTorque(void* actor, const Vector3& torque, ForceMode mode);
// Shapes
static void* CreateShape(PhysicsColliderActor* collider, const CollisionShape& geometry, JsonAsset* material, bool enabled, bool trigger);
static void SetShapeState(void* shape, bool enabled, bool trigger);
static void SetShapeFilterMask(void* shape, uint32 mask0, uint32 mask1);
static void* GetShapeActor(void* shape);
static void GetShapePose(void* shape, Vector3& position, Quaternion& orientation);
static CollisionShape::Types GetShapeType(void* shape);
static void GetShapeLocalPose(void* shape, Vector3& position, Quaternion& orientation);
static void SetShapeLocalPose(void* shape, const Vector3& position, const Quaternion& orientation);
static void SetShapeContactOffset(void* shape, float value);
static void SetShapeMaterial(void* shape, JsonAsset* material);
static void SetShapeGeometry(void* shape, const CollisionShape& geometry);
static void AttachShape(void* shape, void* actor);
static void DetachShape(void* shape, void* actor);
static bool ComputeShapesPenetration(void* shapeA, void* shapeB, const Vector3& positionA, const Quaternion& orientationA, const Vector3& positionB, const Quaternion& orientationB, Vector3& direction, float& distance);
static float ComputeShapeSqrDistanceToPoint(void* shape, const Vector3& position, const Quaternion& orientation, const Vector3& point, Vector3* closestPoint = nullptr);
static bool RayCastShape(void* shape, const Vector3& position, const Quaternion& orientation, const Vector3& origin, const Vector3& direction, float& resultHitDistance, float maxDistance);
static bool RayCastShape(void* shape, const Vector3& position, const Quaternion& orientation, const Vector3& origin, const Vector3& direction, RayCastHit& hitInfo, float maxDistance);
// Joints
static void SetJointFlags(void* joint, JointFlags value);
static void SetJointActors(void* joint, void* actors0, void* actor1);
static void SetJointActorPose(void* joint, const Vector3& position, const Quaternion& orientation, uint8 index);
static void SetJointBreakForce(void* joint, float force, float torque);
static void GetJointForce(void* joint, Vector3& linear, Vector3& angular);
static void* CreateFixedJoint(const PhysicsJointDesc& desc);
static void* CreateDistanceJoint(const PhysicsJointDesc& desc);
static void* CreateHingeJoint(const PhysicsJointDesc& desc);
static void* CreateSliderJoint(const PhysicsJointDesc& desc);
static void* CreateSphericalJoint(const PhysicsJointDesc& desc);
static void* CreateD6Joint(const PhysicsJointDesc& desc);
static void SetDistanceJointFlags(void* joint, DistanceJointFlag flags);
static void SetDistanceJointMinDistance(void* joint, float value);
static void SetDistanceJointMaxDistance(void* joint, float value);
static void SetDistanceJointTolerance(void* joint, float value);
static void SetDistanceJointSpring(void* joint, const SpringParameters& value);
static float GetDistanceJointDistance(void* joint);
static void SetHingeJointFlags(void* joint, HingeJointFlag value, bool driveFreeSpin);
static void SetHingeJointLimit(void* joint, const LimitAngularRange& value);
static void SetHingeJointDrive(void* joint, const HingeJointDrive& value);
static float GetHingeJointAngle(void* joint);
static float GetHingeJointVelocity(void* joint);
static void SetSliderJointFlags(void* joint, SliderJointFlag value);
static void SetSliderJointLimit(void* joint, const LimitLinearRange& value);
static float GetSliderJointPosition(void* joint);
static float GetSliderJointVelocity(void* joint);
static void SetSphericalJointFlags(void* joint, SphericalJointFlag value);
static void SetSphericalJointLimit(void* joint, const LimitConeRange& value);
static void SetD6JointMotion(void* joint, D6JointAxis axis, D6JointMotion value);
static void SetD6JointDrive(void* joint, const D6JointDriveType index, const D6JointDrive& value);
static void SetD6JointLimitLinear(void* joint, const LimitLinear& value);
static void SetD6JointLimitTwist(void* joint, const LimitAngularRange& value);
static void SetD6JointLimitSwing(void* joint, const LimitConeRange& value);
static Vector3 GetD6JointDrivePosition(void* joint);
static void SetD6JointDrivePosition(void* joint, const Vector3& value);
static Quaternion GetD6JointDriveRotation(void* joint);
static void SetD6JointDriveRotation(void* joint, const Quaternion& value);
static void GetD6JointDriveVelocity(void* joint, Vector3& linear, Vector3& angular);
static void SetD6JointDriveVelocity(void* joint, const Vector3& linear, const Vector3& angular);
static float GetD6JointTwist(void* joint);
static float GetD6JointSwingY(void* joint);
static float GetD6JointSwingZ(void* joint);
// Character Controllers
static void* CreateController(void* scene, IPhysicsActor* actor, PhysicsColliderActor* collider, float contactOffset, const Vector3& position, float slopeLimit, int32 nonWalkableMode, JsonAsset* material, float radius, float height, float stepOffset, void*& shape);
static void* GetControllerRigidDynamicActor(void* controller);
static void SetControllerSize(void* controller, float radius, float height);
static void SetControllerSlopeLimit(void* controller, float value);
static void SetControllerNonWalkableMode(void* controller, int32 value);
static void SetControllerStepOffset(void* controller, float value);
static Vector3 GetControllerUpDirection(void* controller);
static void SetControllerUpDirection(void* controller, const Vector3& value);
static Vector3 GetControllerPosition(void* controller);
static void SetControllerPosition(void* controller, const Vector3& value);
static int32 MoveController(void* controller, void* shape, const Vector3& displacement, float minMoveDistance, float deltaTime);
#if WITH_VEHICLE
// Vehicles
static void* CreateVehicle(class WheeledVehicle* actor);
static void DestroyVehicle(void* vehicle, int32 driveType);
static void SetVehicleGearbox(void* vehicle, const void* value);
static int32 GetVehicleTargetGear(void* vehicle);
static void SetVehicleTargetGear(void* vehicle, int32 value);
static int32 GetVehicleCurrentGear(void* vehicle);
static void SetVehicleCurrentGear(void* vehicle, int32 value);
static float GetVehicleForwardSpeed(void* vehicle);
static float GetVehicleSidewaysSpeed(void* vehicle);
static float GetVehicleEngineRotationSpeed(void* vehicle);
static void AddVehicle(void* scene, WheeledVehicle* actor);
static void RemoveVehicle(void* scene, WheeledVehicle* actor);
#endif
// Resources
static void* CreateConvexMesh(byte* data, int32 dataSize, BoundingBox& localBounds);
static void* CreateTriangleMesh(byte* data, int32 dataSize, BoundingBox& localBounds);
static void* CreateHeightField(byte* data, int32 dataSize);
static void GetConvexMeshTriangles(void* contextMesh, Array<Vector3, HeapAllocation>& vertexBuffer, Array<int32, HeapAllocation>& indexBuffer);
static void GetTriangleMeshTriangles(void* triangleMesh, Array<Vector3, HeapAllocation>& vertexBuffer, Array<int32, HeapAllocation>& indexBuffer);
static const uint32* GetTriangleMeshRemap(void* triangleMesh, uint32& count);
static void GetHeightFieldSize(void* heightField, int32& rows, int32& columns);
static float GetHeightFieldHeight(void* heightField, float x, float z);
static bool ModifyHeightField(void* heightField, int32 startCol, int32 startRow, int32 cols, int32 rows, const HeightFieldSample* data);
static void FlushRequests();
static void FlushRequests(void* scene);
static void DestroyActor(void* actor);
static void DestroyShape(void* shape);
static void DestroyJoint(void* joint);
static void DestroyController(void* controller);
static void DestroyObject(void* object);
static void RemoveCollider(PhysicsColliderActor* collider);
static void RemoveJoint(Joint* joint);
public:
// Utilities
FORCE_INLINE static void SetActorFlag(void* actor, ActorFlags flag, bool value)
{
auto flags = GetActorFlags(actor);
flags = (ActorFlags)(((uint32)flags & ~(uint32)flag) | (value ? (uint32)flag : 0));
SetActorFlags(actor, flags);
}
FORCE_INLINE static void SetRigidDynamicActorFlag(void* actor, RigidDynamicFlags flag, bool value)
{
auto flags = GetRigidDynamicActorFlags(actor);
flags = (RigidDynamicFlags)(((uint32)flags & ~(uint32)flag) | (value ? (uint32)flag : 0));
SetRigidDynamicActorFlags(actor, flags);
}
};
DECLARE_ENUM_OPERATORS(PhysicsBackend::ActorFlags);
DECLARE_ENUM_OPERATORS(PhysicsBackend::RigidDynamicFlags);
DECLARE_ENUM_OPERATORS(PhysicsBackend::JointFlags);

View File

@@ -1,697 +0,0 @@
// 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;
}
};
class CharacterControllerFilter : public PxControllerFilterCallback
{
private:
PxShape* getShape(const PxController& controller)
{
PxRigidDynamic* actor = controller.getActor();
// Early out if no actor or no shapes
if (!actor || actor->getNbShapes() < 1)
return nullptr;
// Get first shape only.
PxShape* shape = nullptr;
actor->getShapes(&shape, 1);
return shape;
}
public:
bool filter(const PxController& a, const PxController& b) override
{
// Early out to avoid crashing
PxShape* shapeA = getShape(a);
if (!shapeA)
return false;
PxShape* shapeB = getShape(b);
if (!shapeB)
return false;
// Let triggers through
if (PxFilterObjectIsTrigger(shapeB->getFlags()))
return false;
// Trigger the contact callback for pairs (A,B) where the filtermask of A contains the ID of B and vice versa
const PxFilterData shapeFilterA = shapeA->getQueryFilterData();
const PxFilterData shapeFilterB = shapeB->getQueryFilterData();
if (shapeFilterA.word0 & shapeFilterB.word1)
return true;
return false;
}
};
PxQueryFilterCallback* PhysicsScene::GetQueryFilterCallback()
{
static QueryFilter Filter;
return &Filter;
}
PxQueryFilterCallback* PhysicsScene::GetCharacterQueryFilterCallback()
{
static CharacterQueryFilter Filter;
return &Filter;
}
PxControllerFilterCallback* PhysicsScene::GetCharacterControllerFilterCallback()
{
static CharacterControllerFilter Filter;
return &Filter;
}
bool PhysicsScene::RayCast(const Vector3& origin, const Vector3& direction, const float maxDistance, uint32 layerMask, bool hitTriggers)
{
// Prepare data
SCENE_QUERY_SETUP(true);
PxRaycastBuffer buffer;
// Perform raycast test
return GetScene()->raycast(C2P(origin), C2P(direction), maxDistance, buffer, hitFlags, filterData, GetQueryFilterCallback());
}
bool PhysicsScene::RayCast(const Vector3& origin, const Vector3& direction, RayCastHit& hitInfo, const float maxDistance, uint32 layerMask, bool hitTriggers)
{
// Prepare data
SCENE_QUERY_SETUP(true);
PxRaycastBuffer buffer;
// Perform raycast test
if (!GetScene()->raycast(C2P(origin), C2P(direction), maxDistance, buffer, hitFlags, filterData, GetQueryFilterCallback()))
return false;
// Collect results
SCENE_QUERY_COLLECT_SINGLE();
return true;
}
bool PhysicsScene::RayCastAll(const Vector3& origin, const Vector3& direction, Array<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;
}

View File

@@ -1,818 +0,0 @@
#include "Physics.h"
#include "PhysicsScene.h"
#include "PhysicsSettings.h"
#include "PhysicsStepper.h"
#include "SimulationEventCallback.h"
#include "Utilities.h"
#include "Actors/IPhysicsActor.h"
#include "Engine/Core/Log.h"
#include "Engine/Platform/CPUInfo.h"
#include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Threading/Threading.h"
#include <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
class PhysicsScenePhysX
{
friend PhysicsScene;
private:
PxScene* Scene;
PxCpuDispatcher* CpuDispatcher;
PxControllerManager* ControllerManager;
PxSimulationFilterShader PhysXDefaultFilterShader = PxDefaultSimulationFilterShader;
SimulationEventCallback EventsCallback;
CriticalSection FlushLocker;
Array<PxActor*> NewActors;
Array<PxActor*> DeadActors;
Array<PxMaterial*> DeadMaterials;
Array<PhysicsColliderActor*> DeadColliders;
Array<Joint*> DeadJoints;
Array<ActionData> Actions;
Array<PxBase*> DeadObjects;
#if WITH_VEHICLE
Array<PxVehicleWheels*> WheelVehiclesCache;
Array<PxRaycastQueryResult> WheelQueryResults;
Array<PxRaycastHit> WheelHitResults;
Array<PxWheelQueryResult> WheelVehiclesResultsPerWheel;
Array<PxVehicleWheelQueryResult> WheelVehiclesResultsPerVehicle;
PxBatchQuery* WheelRaycastBatchQuery = nullptr;
PxVehicleDrivableSurfaceToTireFrictionPairs* WheelTireFrictions = nullptr;
Array<WheeledVehicle*> WheelVehicles;
#endif
};
PhysicsScene::PhysicsScene(const String& name, const PhysicsSettings& settings)
: PersistentScriptingObject(SpawnParams(Guid::New(), TypeInitializer))
{
#define CHECK_INIT(value, msg) if(!value) { LOG(Error, msg); return; }
mName = name;
mPhysxImpl = New<PhysicsScenePhysX>();
// Create scene description
PxSceneDesc sceneDesc(CPhysX->getTolerancesScale());
sceneDesc.gravity = C2P(settings.DefaultGravity);
sceneDesc.flags |= PxSceneFlag::eENABLE_ACTIVE_ACTORS;
if (!settings.DisableCCD)
sceneDesc.flags |= PxSceneFlag::eENABLE_CCD;
if (settings.EnableAdaptiveForce)
sceneDesc.flags |= PxSceneFlag::eADAPTIVE_FORCE;
sceneDesc.simulationEventCallback = &mPhysxImpl->EventsCallback;
sceneDesc.filterShader = FilterShader;
sceneDesc.bounceThresholdVelocity = settings.BounceThresholdVelocity;
if (sceneDesc.cpuDispatcher == nullptr)
{
mPhysxImpl->CpuDispatcher = PxDefaultCpuDispatcherCreate(Math::Clamp<uint32>(Platform::GetCPUInfo().ProcessorCoreCount - 1, 1, 4));
CHECK_INIT(mPhysxImpl->CpuDispatcher, "PxDefaultCpuDispatcherCreate failed!");
sceneDesc.cpuDispatcher = mPhysxImpl->CpuDispatcher;
}
if (sceneDesc.filterShader == nullptr)
{
sceneDesc.filterShader = mPhysxImpl->PhysXDefaultFilterShader;
}
// Create scene
mPhysxImpl->Scene = CPhysX->createScene(sceneDesc);
CHECK_INIT(mPhysxImpl->Scene, "createScene failed!");
#if WITH_PVD
auto pvdClient = PhysicsScene->getScenePvdClient();
if (pvdClient)
{
pvdClient->setScenePvdFlags(PxPvdSceneFlag::eTRANSMIT_CONSTRAINTS | PxPvdSceneFlag::eTRANSMIT_SCENEQUERIES | PxPvdSceneFlag::eTRANSMIT_CONTACTS);
}
else
{
LOG(Info, "Missing PVD client scene.");
}
#endif
// Init characters controller
mPhysxImpl->ControllerManager = PxCreateControllerManager(*mPhysxImpl->Scene);
}
PhysicsScene::~PhysicsScene()
{
#if WITH_VEHICLE
RELEASE_PHYSX(mPhysxImpl->WheelRaycastBatchQuery);
RELEASE_PHYSX(mPhysxImpl->WheelTireFrictions);
mPhysxImpl->WheelQueryResults.Resize(0);
mPhysxImpl->WheelHitResults.Resize(0);
mPhysxImpl->WheelVehiclesResultsPerWheel.Resize(0);
mPhysxImpl->WheelVehiclesResultsPerVehicle.Resize(0);
#endif
RELEASE_PHYSX(mPhysxImpl->ControllerManager);
SAFE_DELETE(mPhysxImpl->CpuDispatcher);
SAFE_DELETE(mStepper);
Allocator::Free(mScratchMemory);
mScratchMemory = nullptr;
mPhysxImpl->Scene->release();
SAFE_DELETE(mPhysxImpl);
}
String PhysicsScene::GetName() const
{
return mName;
}
PxScene* PhysicsScene::GetScene()
{
return mPhysxImpl->Scene;
}
bool PhysicsScene::GetAutoSimulation()
{
return mAutoSimulation;
}
void PhysicsScene::SetAutoSimulation(bool value)
{
mAutoSimulation = value;
}
void PhysicsScene::SetGravity(const Vector3& value)
{
if(mPhysxImpl->Scene)
{
mPhysxImpl->Scene->setGravity(C2P(value));
}
}
Vector3 PhysicsScene::GetGravity()
{
return mPhysxImpl->Scene ? P2C(mPhysxImpl->Scene->getGravity()) : Vector3::Zero;
}
bool PhysicsScene::GetEnableCCD()
{
return mPhysxImpl->Scene ? (mPhysxImpl->Scene->getFlags() & PxSceneFlag::eENABLE_CCD) == PxSceneFlag::eENABLE_CCD : !PhysicsSettings::Get()->DisableCCD;
}
void PhysicsScene::SetEnableCCD(const bool value)
{
if (mPhysxImpl->Scene)
mPhysxImpl->Scene->setFlag(PxSceneFlag::eENABLE_CCD, value);
}
float PhysicsScene::GetBounceThresholdVelocity()
{
return mPhysxImpl->Scene ? mPhysxImpl->Scene->getBounceThresholdVelocity() : PhysicsSettings::Get()->BounceThresholdVelocity;
}
void PhysicsScene::SetBounceThresholdVelocity(const float value)
{
if (mPhysxImpl->Scene)
mPhysxImpl->Scene->setBounceThresholdVelocity(value);
}
void PhysicsScene::Simulate(float dt)
{
ASSERT(IsInMainThread() && !mIsDuringSimulation);
ASSERT(CPhysX);
const auto& settings = *PhysicsSettings::Get();
// Flush the old/new objects and the other requests before the simulation
FlushRequests();
// Clamp delta
dt = Math::Clamp(dt, 0.0f, settings.MaxDeltaTime);
// Prepare util objects
if (mScratchMemory == nullptr)
{
mScratchMemory = Allocator::Allocate(SCRATCH_BLOCK_SIZE, 16);
}
if (mStepper == nullptr)
{
mStepper = New<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(mPhysxImpl->Scene, dt, mScratchMemory, SCRATCH_BLOCK_SIZE) == false)
return;
mPhysxImpl->EventsCallback.Clear();
mLastDeltaTime = dt;
// TODO: move this call after rendering done
mStepper->renderDone();
}
bool PhysicsScene::IsDuringSimulation()
{
return mIsDuringSimulation;
}
void PhysicsScene::CollectResults()
{
if (!mIsDuringSimulation)
return;
ASSERT(IsInMainThread());
ASSERT(CPhysX && mStepper);
{
PROFILE_CPU_NAMED("Physics.Fetch");
// Gather results (with waiting for the end)
mStepper->wait(mPhysxImpl->Scene);
}
#if WITH_VEHICLE
if (mPhysxImpl->WheelVehicles.HasItems())
{
PROFILE_CPU_NAMED("Physics.Vehicles");
// Update vehicles steering
mPhysxImpl->WheelVehiclesCache.Clear();
mPhysxImpl->WheelVehiclesCache.EnsureCapacity(mPhysxImpl->WheelVehicles.Count());
int32 wheelsCount = 0;
for (auto wheelVehicle : mPhysxImpl->WheelVehicles)
{
if (!wheelVehicle->IsActiveInHierarchy())
continue;
auto drive = (PxVehicleWheels*)wheelVehicle->_drive;
ASSERT(drive);
mPhysxImpl->WheelVehiclesCache.Add(drive);
wheelsCount += drive->mWheelsSimData.getNbWheels();
float throttle = wheelVehicle->_throttle;
float brake = wheelVehicle->_brake;
if (wheelVehicle->UseReverseAsBrake)
{
const float invalidDirectionThreshold = 80.0f;
const float breakThreshold = 8.0f;
const float forwardSpeed = wheelVehicle->GetForwardSpeed();
// Automatic gear change when changing driving direction
if (Math::Abs(forwardSpeed) < invalidDirectionThreshold)
{
if (throttle < -ZeroTolerance && wheelVehicle->GetCurrentGear() >= 0 && wheelVehicle->GetTargetGear() >= 0)
{
wheelVehicle->SetCurrentGear(-1);
}
else if (throttle > ZeroTolerance && wheelVehicle->GetCurrentGear() <= 0 && wheelVehicle->GetTargetGear() <= 0)
{
wheelVehicle->SetCurrentGear(1);
}
}
// Automatic break when changing driving direction
if (throttle > 0.0f)
{
if (forwardSpeed < -invalidDirectionThreshold)
{
brake = 1.0f;
}
}
else if (throttle < 0.0f)
{
if (forwardSpeed > invalidDirectionThreshold)
{
brake = 1.0f;
}
}
else
{
if (forwardSpeed < breakThreshold && forwardSpeed > -breakThreshold)
{
brake = 1.0f;
}
}
// Block throttle if user is changing driving direction
if ((throttle > 0.0f && wheelVehicle->GetTargetGear() < 0) || (throttle < 0.0f && wheelVehicle->GetTargetGear() > 0))
{
throttle = 0.0f;
}
throttle = Math::Abs(throttle);
}
else
{
throttle = Math::Max(throttle, 0.0f);
}
// @formatter:off
// Reference: PhysX SDK docs
// TODO: expose input control smoothing data
static constexpr PxVehiclePadSmoothingData padSmoothing =
{
{
6.0f, // rise rate eANALOG_INPUT_ACCEL
6.0f, // rise rate eANALOG_INPUT_BRAKE
12.0f, // rise rate eANALOG_INPUT_HANDBRAKE
2.5f, // rise rate eANALOG_INPUT_STEER_LEFT
2.5f, // rise rate eANALOG_INPUT_STEER_RIGHT
},
{
10.0f, // fall rate eANALOG_INPUT_ACCEL
10.0f, // fall rate eANALOG_INPUT_BRAKE
12.0f, // fall rate eANALOG_INPUT_HANDBRAKE
5.0f, // fall rate eANALOG_INPUT_STEER_LEFT
5.0f, // fall rate eANALOG_INPUT_STEER_RIGHT
}
};
PxVehicleKeySmoothingData keySmoothing =
{
{
3.0f, // rise rate eANALOG_INPUT_ACCEL
3.0f, // rise rate eANALOG_INPUT_BRAKE
10.0f, // rise rate eANALOG_INPUT_HANDBRAKE
2.5f, // rise rate eANALOG_INPUT_STEER_LEFT
2.5f, // rise rate eANALOG_INPUT_STEER_RIGHT
},
{
5.0f, // fall rate eANALOG_INPUT__ACCEL
5.0f, // fall rate eANALOG_INPUT__BRAKE
10.0f, // fall rate eANALOG_INPUT__HANDBRAKE
5.0f, // fall rate eANALOG_INPUT_STEER_LEFT
5.0f // fall rate eANALOG_INPUT_STEER_RIGHT
}
};
// Reference: PhysX SDK docs
// TODO: expose steer vs forward curve into per-vehicle (up to 8 points, values clamped into 0/1 range)
static constexpr PxF32 steerVsForwardSpeedData[] =
{
0.0f, 1.0f,
20.0f, 0.9f,
65.0f, 0.8f,
120.0f, 0.7f,
PX_MAX_F32, PX_MAX_F32,
PX_MAX_F32, PX_MAX_F32,
PX_MAX_F32, PX_MAX_F32,
PX_MAX_F32, PX_MAX_F32,
};
const PxFixedSizeLookupTable<8> steerVsForwardSpeed(steerVsForwardSpeedData, 4);
// @formatter:on
if (wheelVehicle->UseAnalogSteering)
{
switch (wheelVehicle->_driveTypeCurrent)
{
case WheeledVehicle::DriveTypes::Drive4W:
{
PxVehicleDrive4WRawInputData rawInputData;
rawInputData.setAnalogAccel(throttle);
rawInputData.setAnalogBrake(brake);
rawInputData.setAnalogSteer(wheelVehicle->_steering);
rawInputData.setAnalogHandbrake(wheelVehicle->_handBrake);
PxVehicleDrive4WSmoothAnalogRawInputsAndSetAnalogInputs(padSmoothing, steerVsForwardSpeed, rawInputData, mLastDeltaTime, false, *(PxVehicleDrive4W*)drive);
break;
}
case WheeledVehicle::DriveTypes::DriveNW:
{
PxVehicleDriveNWRawInputData rawInputData;
rawInputData.setAnalogAccel(throttle);
rawInputData.setAnalogBrake(brake);
rawInputData.setAnalogSteer(wheelVehicle->_steering);
rawInputData.setAnalogHandbrake(wheelVehicle->_handBrake);
PxVehicleDriveNWSmoothAnalogRawInputsAndSetAnalogInputs(padSmoothing, steerVsForwardSpeed, rawInputData, mLastDeltaTime, false, *(PxVehicleDriveNW*)drive);
break;
}
}
}
else
{
const float deadZone = 0.1f;
switch (wheelVehicle->_driveTypeCurrent)
{
case WheeledVehicle::DriveTypes::Drive4W:
{
PxVehicleDrive4WRawInputData rawInputData;
rawInputData.setDigitalAccel(throttle > deadZone);
rawInputData.setDigitalBrake(brake > deadZone);
rawInputData.setDigitalSteerLeft(wheelVehicle->_steering < -deadZone);
rawInputData.setDigitalSteerRight(wheelVehicle->_steering > deadZone);
rawInputData.setDigitalHandbrake(wheelVehicle->_handBrake > deadZone);
PxVehicleDrive4WSmoothDigitalRawInputsAndSetAnalogInputs(keySmoothing, steerVsForwardSpeed, rawInputData, mLastDeltaTime, false, *(PxVehicleDrive4W*)drive);
break;
}
case WheeledVehicle::DriveTypes::DriveNW:
{
PxVehicleDriveNWRawInputData rawInputData;
rawInputData.setDigitalAccel(throttle > deadZone);
rawInputData.setDigitalBrake(brake > deadZone);
rawInputData.setDigitalSteerLeft(wheelVehicle->_steering < -deadZone);
rawInputData.setDigitalSteerRight(wheelVehicle->_steering > deadZone);
rawInputData.setDigitalHandbrake(wheelVehicle->_handBrake > deadZone);
PxVehicleDriveNWSmoothDigitalRawInputsAndSetAnalogInputs(keySmoothing, steerVsForwardSpeed, rawInputData, mLastDeltaTime, false, *(PxVehicleDriveNW*)drive);
break;
}
}
}
}
// Update batches queries cache
if (wheelsCount > mPhysxImpl->WheelQueryResults.Count())
{
if (mPhysxImpl->WheelRaycastBatchQuery)
mPhysxImpl->WheelRaycastBatchQuery->release();
mPhysxImpl->WheelQueryResults.Resize(wheelsCount, false);
mPhysxImpl->WheelHitResults.Resize(wheelsCount, false);
PxBatchQueryDesc desc(wheelsCount, 0, 0);
desc.queryMemory.userRaycastResultBuffer = mPhysxImpl->WheelQueryResults.Get();
desc.queryMemory.userRaycastTouchBuffer = mPhysxImpl->WheelHitResults.Get();
desc.queryMemory.raycastTouchBufferSize = wheelsCount;
desc.preFilterShader = WheelRaycastPreFilter;
mPhysxImpl->WheelRaycastBatchQuery = mPhysxImpl->Scene->createBatchQuery(desc);
}
// TODO: expose vehicle tires configuration
if (!mPhysxImpl->WheelTireFrictions)
{
PxVehicleDrivableSurfaceType surfaceTypes[1];
surfaceTypes[0].mType = 0;
const PxMaterial* surfaceMaterials[1];
surfaceMaterials[0] = Physics::GetDefaultMaterial();
mPhysxImpl->WheelTireFrictions = PxVehicleDrivableSurfaceToTireFrictionPairs::allocate(1, 1);
mPhysxImpl->WheelTireFrictions->setup(1, 1, surfaceMaterials, surfaceTypes);
mPhysxImpl->WheelTireFrictions->setTypePairFriction(0, 0, 5.0f);
}
// Setup cache for wheel states
mPhysxImpl->WheelVehiclesResultsPerVehicle.Resize(mPhysxImpl->WheelVehiclesCache.Count(), false);
mPhysxImpl->WheelVehiclesResultsPerWheel.Resize(wheelsCount, false);
wheelsCount = 0;
for (int32 i = 0, ii = 0; i < mPhysxImpl->WheelVehicles.Count(); i++)
{
auto wheelVehicle = mPhysxImpl->WheelVehicles[i];
if (!wheelVehicle->IsActiveInHierarchy())
continue;
auto drive = (PxVehicleWheels*)mPhysxImpl->WheelVehicles[ii]->_drive;
auto& perVehicle = mPhysxImpl->WheelVehiclesResultsPerVehicle[ii];
ii++;
perVehicle.nbWheelQueryResults = drive->mWheelsSimData.getNbWheels();
perVehicle.wheelQueryResults = mPhysxImpl->WheelVehiclesResultsPerWheel.Get() + wheelsCount;
wheelsCount += perVehicle.nbWheelQueryResults;
}
// Update vehicles
if (mPhysxImpl->WheelVehiclesCache.Count() != 0)
{
PxVehicleSuspensionRaycasts(mPhysxImpl->WheelRaycastBatchQuery, mPhysxImpl->WheelVehiclesCache.Count(), mPhysxImpl->WheelVehiclesCache.Get(), mPhysxImpl->WheelQueryResults.Count(), mPhysxImpl->WheelQueryResults.Get());
PxVehicleUpdates(mLastDeltaTime, mPhysxImpl->Scene->getGravity(), *mPhysxImpl->WheelTireFrictions, mPhysxImpl->WheelVehiclesCache.Count(), mPhysxImpl->WheelVehiclesCache.Get(), mPhysxImpl->WheelVehiclesResultsPerVehicle.Get());
}
// Synchronize state
for (int32 i = 0, ii = 0; i < mPhysxImpl->WheelVehicles.Count(); i++)
{
auto wheelVehicle = mPhysxImpl->WheelVehicles[i];
if (!wheelVehicle->IsActiveInHierarchy())
continue;
auto drive = mPhysxImpl->WheelVehiclesCache[ii];
auto& perVehicle = mPhysxImpl->WheelVehiclesResultsPerVehicle[ii];
ii++;
#if PHYSX_VEHICLE_DEBUG_TELEMETRY
LOG(Info, "Vehicle[{}] Gear={}, RPM={}", ii, wheelVehicle->GetCurrentGear(), (int32)wheelVehicle->GetEngineRotationSpeed());
#endif
// Update wheels
for (int32 j = 0; j < wheelVehicle->_wheelsData.Count(); j++)
{
auto& wheelData = wheelVehicle->_wheelsData[j];
auto& perWheel = perVehicle.wheelQueryResults[j];
#if PHYSX_VEHICLE_DEBUG_TELEMETRY
LOG(Info, "Vehicle[{}] Wheel[{}] longitudinalSlip={}, lateralSlip={}, suspSpringForce={}", ii, j, Utilities::RoundTo2DecimalPlaces(perWheel.longitudinalSlip), Utilities::RoundTo2DecimalPlaces(perWheel.lateralSlip), (int32)perWheel.suspSpringForce);
#endif
auto& state = wheelData.State;
state.IsInAir = perWheel.isInAir;
state.TireContactCollider = perWheel.tireContactShape ? static_cast<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 = mPhysxImpl->Scene->getActiveActors(activeActorsCount);
if (activeActorsCount > 0)
{
// Update changed transformations
// TODO: use jobs system if amount if huge
for (uint32 i = 0; i < activeActorsCount; i++)
{
const auto pxActor = (PxRigidActor*)*activeActors++;
auto actor = dynamic_cast<IPhysicsActor*>((Actor*)pxActor->userData);
ASSERT(actor);
actor->OnActiveTransformChanged(pxActor->getGlobalPose());
}
}
}
{
PROFILE_CPU_NAMED("Physics.SendEvents");
mPhysxImpl->EventsCallback.CollectResults();
mPhysxImpl->EventsCallback.SendTriggerEvents();
mPhysxImpl->EventsCallback.SendCollisionEvents();
mPhysxImpl->EventsCallback.SendJointEvents();
}
// End
mIsDuringSimulation = false;
}
void PhysicsScene::FlushRequests()
{
ASSERT(!IsDuringSimulation());
ASSERT(CPhysX);
PROFILE_CPU();
mPhysxImpl->FlushLocker.Lock();
// Note: this does not handle case when actor is removed and added to the scene at the same time
if (mPhysxImpl->NewActors.HasItems())
{
GetScene()->addActors(mPhysxImpl->NewActors.Get(), mPhysxImpl->NewActors.Count());
mPhysxImpl->NewActors.Clear();
}
for (int32 i = 0; i < mPhysxImpl->Actions.Count(); i++)
{
const auto action = mPhysxImpl->Actions[i];
switch (action.Type)
{
case ActionType::Sleep:
static_cast<PxRigidDynamic*>(action.Actor)->putToSleep();
break;
}
}
mPhysxImpl->Actions.Clear();
if (mPhysxImpl->DeadActors.HasItems())
{
GetScene()->removeActors(mPhysxImpl->DeadActors.Get(), mPhysxImpl->DeadActors.Count(), true);
for (int32 i = 0; i < mPhysxImpl->DeadActors.Count(); i++)
{
mPhysxImpl->DeadActors[i]->release();
}
mPhysxImpl->DeadActors.Clear();
}
if (mPhysxImpl->DeadColliders.HasItems())
{
for (int32 i = 0; i < mPhysxImpl->DeadColliders.Count(); i++)
{
mPhysxImpl->EventsCallback.OnColliderRemoved(mPhysxImpl->DeadColliders[i]);
}
mPhysxImpl->DeadColliders.Clear();
}
if (mPhysxImpl->DeadJoints.HasItems())
{
for (int32 i = 0; i < mPhysxImpl->DeadJoints.Count(); i++)
{
mPhysxImpl->EventsCallback.OnJointRemoved(mPhysxImpl->DeadJoints[i]);
}
mPhysxImpl->DeadJoints.Clear();
}
for (int32 i = 0; i < mPhysxImpl->DeadMaterials.Count(); i++)
{
auto material = mPhysxImpl->DeadMaterials[i];
// Unlink ref to flax object
material->userData = nullptr;
material->release();
}
mPhysxImpl->DeadMaterials.Clear();
for (int32 i = 0; i < mPhysxImpl->DeadObjects.Count(); i++)
{
mPhysxImpl->DeadObjects[i]->release();
}
mPhysxImpl->DeadObjects.Clear();
mPhysxImpl->FlushLocker.Unlock();
}
void PhysicsScene::RemoveMaterial(PxMaterial* material)
{
ASSERT(material);
mPhysxImpl->FlushLocker.Lock();
mPhysxImpl->DeadMaterials.Add(material);
mPhysxImpl->FlushLocker.Unlock();
}
void PhysicsScene::RemoveObject(PxBase* obj)
{
ASSERT(obj);
mPhysxImpl->FlushLocker.Lock();
mPhysxImpl->DeadObjects.Add(obj);
mPhysxImpl->FlushLocker.Unlock();
}
void PhysicsScene::AddActor(PxActor* actor)
{
ASSERT(actor);
mPhysxImpl->FlushLocker.Lock();
if (IsInMainThread())
{
GetScene()->addActor(*actor);
}
else
{
mPhysxImpl->NewActors.Add(actor);
}
mPhysxImpl->FlushLocker.Unlock();
}
void PhysicsScene::AddActor(PxRigidDynamic* actor, bool putToSleep)
{
ASSERT(actor);
mPhysxImpl->FlushLocker.Lock();
if (IsInMainThread())
{
GetScene()->addActor(*actor);
if (putToSleep)
actor->putToSleep();
}
else
{
mPhysxImpl->NewActors.Add(actor);
if (putToSleep)
mPhysxImpl->Actions.Add({ ActionType::Sleep, actor });
}
mPhysxImpl->FlushLocker.Unlock();
}
void PhysicsScene::UnlinkActor(PxActor* actor)
{
ASSERT(IsInMainThread())
ASSERT(actor);
GetScene()->removeActor(*actor);
}
void PhysicsScene::RemoveActor(PxActor* actor)
{
ASSERT(actor);
// Unlink ref to flax object
actor->userData = nullptr;
mPhysxImpl->FlushLocker.Lock();
mPhysxImpl->DeadActors.Add(actor);
mPhysxImpl->FlushLocker.Unlock();
}
void PhysicsScene::RemoveCollider(PhysicsColliderActor* collider)
{
ASSERT(collider);
mPhysxImpl->FlushLocker.Lock();
mPhysxImpl->DeadColliders.Add(collider);
mPhysxImpl->FlushLocker.Unlock();
}
void PhysicsScene::RemoveJoint(Joint* joint)
{
ASSERT(joint);
mPhysxImpl->FlushLocker.Lock();
mPhysxImpl->DeadJoints.Add(joint);
mPhysxImpl->FlushLocker.Unlock();
}
PxControllerManager* PhysicsScene::GetControllerManager()
{
return mPhysxImpl->ControllerManager;
}
#if WITH_VEHICLE
void PhysicsScene::AddWheeledVehicle(WheeledVehicle* vehicle)
{
mPhysxImpl->WheelVehicles.Add(vehicle);
}
void PhysicsScene::RemoveWheeledVehicle(WheeledVehicle* vehicle)
{
mPhysxImpl->WheelVehicles.Remove(vehicle);
}
#endif

View File

@@ -1,33 +1,36 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
#pragma once
#include "Engine/Scripting/ScriptingObject.h"
#include "Engine/Scripting/ScriptingType.h"
#include "Engine/Core/Math/Vector3.h"
#include "Engine/Core/Math/Quaternion.h"
#include "Engine/Scripting/ScriptingObject.h"
#include "Types.h"
struct ActionData;
struct RayCastHit;
class PhysicsSettings;
class PhysicsColliderActor;
class Joint;
class Collider;
class CollisionData;
#if WITH_VEHICLE
class WheeledVehicle;
#endif
struct ActionData;
struct RayCastHit;
class FixedStepper;
class PhysicsSettings;
class PhysicsColliderActor;
class PhysicsScenePhysX;
class Joint;
class Collider;
class CollisionData;
/// <summary>
/// Isolated physics scene.
/// Physical simulation scene.
/// </summary>
API_CLASS(NoSpawn) class FLAXENGINE_API PhysicsScene : public PersistentScriptingObject
API_CLASS() class FLAXENGINE_API PhysicsScene : public ScriptingObject
{
DECLARE_SCRIPTING_TYPE_NO_SPAWN(PhysicsScene);
DECLARE_SCRIPTING_TYPE(PhysicsScene);
private:
String _name;
bool _autoSimulation = true;
bool _isDuringSimulation = false;
void* _scene = nullptr;
explicit PhysicsScene(const String& name, const PhysicsSettings& settings);
public:
~PhysicsScene();
/// <summary>
@@ -35,30 +38,34 @@ API_CLASS(NoSpawn) class FLAXENGINE_API PhysicsScene : public PersistentScriptin
/// </summary>
API_PROPERTY() String GetName() const;
public:
String ToString() const override
/// <summary>
/// Gets the native physics system scene object.
/// </summary>
FORCE_INLINE void* GetPhysicsScene() const
{
return GetName();
return _scene;
}
PxScene* GetScene();
/// <summary>
/// Gets the automatic simulation feature that perform physics simulation after on fixed update by auto, otherwise user should do it.
/// </summary>
API_PROPERTY() bool GetAutoSimulation() const;
/// <summary>
/// The automatic simulation feature. True if perform physics simulation after on fixed update by auto, otherwise user should do it.
/// Sets the automatic simulation feature that 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>
/// Sets the current gravity force.
/// </summary>
API_PROPERTY() void SetGravity(const Vector3& value);
/// <summary>
/// Gets the CCD feature enable flag.
/// </summary>
@@ -67,7 +74,7 @@ public:
/// <summary>
/// Sets the CCD feature enable flag.
/// </summary>
API_PROPERTY() void SetEnableCCD(const bool value);
API_PROPERTY() void SetEnableCCD(bool value);
/// <summary>
/// Gets the minimum relative velocity required for an object to bounce.
@@ -77,7 +84,16 @@ public:
/// <summary>
/// Sets the minimum relative velocity required for an object to bounce.
/// </summary>
API_PROPERTY() void SetBounceThresholdVelocity(const float value);
API_PROPERTY() void SetBounceThresholdVelocity(float value);
public:
/// <summary>
/// Initializes the scene.
/// </summary>
/// <param name="name">The name.</param>
/// <param name="settings">The physics settings.</param>
/// <returns>True if failed, otherwise false.</returns>
bool Init(const StringView& name, const PhysicsSettings& settings);
/// <summary>
/// Called during main engine loop to start physic simulation. Use CollectResults after.
@@ -86,90 +102,16 @@ public:
API_FUNCTION() void Simulate(float dt);
/// <summary>
/// Checks if physical simulation is running
/// Checks if physical simulation is running.
/// </summary>
/// <returns>True if simulation is active, otherwise false</returns>
API_PROPERTY() bool IsDuringSimulation();
API_PROPERTY() bool IsDuringSimulation() const;
/// <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>
/// Gets the default controller filter callback used for the character controller collisions detection.
/// </summary>
static PxControllerFilterCallback* GetCharacterControllerFilterCallback();
/// <summary>
/// Performs a raycast against objects in the scene.
/// </summary>
@@ -514,20 +456,4 @@ public:
/// <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;
void* mScratchMemory = nullptr;
FixedStepper* mStepper = nullptr;
float mLastDeltaTime = 0.0f;
bool mIsDuringSimulation = false;
PhysicsScenePhysX* mPhysxImpl;
};

View File

@@ -4,54 +4,15 @@
#include "Engine/Core/Enums.h"
#include "Engine/Core/Config.h"
#include "Engine/Core/Math/Vector2.h"
#include "Engine/Core/Math/Vector3.h"
#include "Engine/Scripting/ScriptingType.h"
namespace physx
{
class PxScene;
class PxConvexMesh;
class PxTriangleMesh;
class PxCooking;
class PxPhysics;
class PxVec3;
class PxVec4;
class PxTransform;
class PxJoint;
class PxMat44;
class PxCpuDispatcher;
class PxGpuDispatcher;
class PxSimulationEventCallback;
struct PxActiveTransform;
class PxActor;
class PxRigidActor;
class PxRigidDynamic;
class PxRigidStatic;
class PxFoundation;
class PxShape;
class PxGeometry;
class PxGeometryHolder;
class PxProfileZoneManager;
class PxMaterial;
class PxPvd;
class PxBase;
class PxTolerancesScale;
class PxBaseTask;
class PxControllerManager;
class PxController;
class PxCapsuleController;
class PxQueryFilterCallback;
class PxControllerFilterCallback;
class PxHeightField;
struct PxFilterData;
struct PxRaycastHit;
struct PxSweepHit;
}
using namespace physx;
#define RELEASE_PHYSX(x) if(x) { (x)->release(); x = nullptr; }
// Global pointer to PhysX SDK object
extern PxPhysics* CPhysX;
class PhysicsColliderActor;
class PhysicsScene;
class Joint;
class Collider;
class CollisionData;
/// <summary>
/// Enumeration that determines the way in which two material properties will be combined to yield a friction or restitution coefficient for a collision.
@@ -167,3 +128,147 @@ API_ENUM(Attributes="Flags") enum class RigidbodyConstraints
};
DECLARE_ENUM_OPERATORS(RigidbodyConstraints);
/// <summary>
/// Raycast hit result data.
/// </summary>
API_STRUCT() struct RayCastHit
{
DECLARE_SCRIPTING_TYPE_NO_SPAWN(RayCastHit);
/// <summary>
/// The collider that was hit.
/// </summary>
API_FIELD() PhysicsColliderActor* Collider = nullptr;
/// <summary>
/// The normal of the surface the ray hit.
/// </summary>
API_FIELD() Vector3 Normal;
/// <summary>
/// The distance from the ray's origin to the hit location.
/// </summary>
API_FIELD() float Distance;
/// <summary>
/// The point in the world space where ray hit the collider.
/// </summary>
API_FIELD() Vector3 Point;
/// <summary>
/// The index of the face that was hit. Valid only for convex mesh (polygon index), triangle mesh (triangle index) and height field (triangle index).
/// </summary>
/// <seealso cref="CollisionData.GetModelTriangle" />
API_FIELD() uint32 FaceIndex;
/// <summary>
/// The barycentric coordinates of hit triangle. Valid only for triangle mesh and height field.
/// </summary>
API_FIELD() Vector2 UV;
};
/// <summary>
/// Physics collision shape variant for different shapes such as box, sphere, capsule.
/// </summary>
struct FLAXENGINE_API CollisionShape
{
enum class Types : uint8
{
Sphere,
Box,
Capsule,
ConvexMesh,
TriangleMesh,
HeightField,
};
Types Type;
union
{
struct
{
float Radius;
} Sphere;
struct
{
float HalfExtents[3];
} Box;
struct
{
float Radius;
float HalfHeight;
} Capsule;
struct
{
void* ConvexMesh;
float Scale[3];
} ConvexMesh;
struct
{
void* TriangleMesh;
float Scale[3];
} TriangleMesh;
struct
{
void* HeightField;
float HeightScale;
float RowScale;
float ColumnScale;
} HeightField;
};
void SetSphere(float radius)
{
Type = Types::Sphere;
Sphere.Radius = radius;
}
void SetBox(float halfExtents[3])
{
Type = Types::Box;
Box.HalfExtents[0] = halfExtents[0];
Box.HalfExtents[1] = halfExtents[1];
Box.HalfExtents[2] = halfExtents[2];
}
void SetCapsule(float radius, float halfHeight)
{
Type = Types::Capsule;
Capsule.Radius = radius;
Capsule.HalfHeight = halfHeight;
}
void SetConvexMesh(void* contextMesh, float scale[3])
{
Type = Types::ConvexMesh;
ConvexMesh.ConvexMesh = contextMesh;
ConvexMesh.Scale[0] = scale[0];
ConvexMesh.Scale[1] = scale[1];
ConvexMesh.Scale[2] = scale[2];
}
void SetTriangleMesh(void* triangleMesh, float scale[3])
{
Type = Types::TriangleMesh;
TriangleMesh.TriangleMesh = triangleMesh;
TriangleMesh.Scale[0] = scale[0];
TriangleMesh.Scale[1] = scale[1];
TriangleMesh.Scale[2] = scale[2];
}
void SetHeightField(void* heightField, float heightScale, float rowScale, float columnScale)
{
Type = Types::HeightField;
HeightField.HeightField = heightField;
HeightField.HeightScale = heightScale;
HeightField.RowScale = rowScale;
HeightField.ColumnScale = columnScale;
}
};

View File

@@ -1,102 +0,0 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
#pragma once
#include "Engine/Core/Math/Vector2.h"
#include "Engine/Core/Math/Vector3.h"
#include "Engine/Core/Math/Vector4.h"
#include "Engine/Core/Math/Quaternion.h"
#include "Engine/Core/Math/BoundingBox.h"
#include <ThirdParty/PhysX/foundation/PxVec2.h>
#include <ThirdParty/PhysX/foundation/PxVec3.h>
#include <ThirdParty/PhysX/foundation/PxVec4.h>
#include <ThirdParty/PhysX/foundation/PxQuat.h>
#include <ThirdParty/PhysX/foundation/PxBounds3.h>
#include <ThirdParty/PhysX/characterkinematic/PxExtended.h>
#include <ThirdParty/PhysX/PxShape.h>
inline PxVec2& C2P(const Vector2& v)
{
return *(PxVec2*)&v;
}
inline PxVec3& C2P(const Vector3& v)
{
return *(PxVec3*)&v;
}
inline PxVec4& C2P(const Vector4& v)
{
return *(PxVec4*)&v;
}
inline PxQuat& C2P(const Quaternion& v)
{
return *(PxQuat*)&v;
}
inline PxBounds3& C2P(const BoundingBox& v)
{
return *(PxBounds3*)&v;
}
inline Vector2& P2C(const PxVec2& v)
{
return *(Vector2*)&v;
}
inline Vector3& P2C(const PxVec3& v)
{
return *(Vector3*)&v;
}
inline Vector4& P2C(const PxVec4& v)
{
return *(Vector4*)&v;
}
inline Quaternion& P2C(const PxQuat& v)
{
return *(Quaternion*)&v;
}
inline BoundingBox& P2C(const PxBounds3& v)
{
return *(BoundingBox*)&v;
}
inline Vector3 P2C(const PxExtendedVec3& v)
{
#ifdef PX_BIG_WORLDS
return Vector3((float)v.x, (float)v.y, (float)v.z);
#else
return *(Vector3*)&v;
#endif
}
inline float M2ToCm2(float v)
{
return v * (100.0f * 100.0f);
}
inline float Cm2ToM2(float v)
{
return v / (100.0f * 100.0f);
}
inline float KgPerM3ToKgPerCm3(float v)
{
return v / (100.0f * 100.0f * 100.0f);
}
inline float RpmToRadPerS(float v)
{
return v * (PI / 30.0f);
}
inline float RadPerSToRpm(float v)
{
return v * (30.0f / PI);
}
extern PxShapeFlags GetShapeFlags(bool isTrigger, bool isEnabled);

View File

@@ -7,13 +7,12 @@
#include "Engine/Level/Scene/SceneRendering.h"
#include "Engine/Serialization/Serialization.h"
#include "Engine/Physics/Physics.h"
#include "Engine/Physics/Utilities.h"
#include "Engine/Physics/PhysicalMaterial.h"
#include "Engine/Physics/PhysicsBackend.h"
#include "Engine/Graphics/RenderView.h"
#include "Engine/Graphics/RenderTask.h"
#include "Engine/Graphics/Textures/GPUTexture.h"
#include "Engine/Profiler/ProfilerCPU.h"
#include <ThirdParty/PhysX/PxFiltering.h>
Terrain::Terrain(const SpawnParams& params)
: PhysicsColliderActor(params)
@@ -69,13 +68,11 @@ void Terrain::UpdateLayerBits()
if (_patches.IsEmpty())
return;
PxFilterData filterData;
// Own layer ID
filterData.word0 = GetLayerMask();
const uint32 mask0 = GetLayerMask();
// Own layer mask
filterData.word1 = Physics::LayerMasks[GetLayer()];
const uint32 mask1 = Physics::LayerMasks[GetLayer()];
// Update the shapes layer bits
for (int32 pathIndex = 0; pathIndex < _patches.Count(); pathIndex++)
@@ -83,8 +80,7 @@ void Terrain::UpdateLayerBits()
const auto patch = _patches[pathIndex];
if (patch->HasCollision())
{
patch->_physicsShape->setSimulationFilterData(filterData);
patch->_physicsShape->setQueryFilterData(filterData);
PhysicsBackend::SetShapeFilterMask(patch->_physicsShape, mask0, mask1);
}
}
}
@@ -170,7 +166,6 @@ bool Terrain::RayCast(const Vector3& origin, const Vector3& direction, RayCastHi
bool result = false;
RayCastHit tmpHit;
const Ray ray(origin, direction);
for (int32 pathIndex = 0; pathIndex < _patches.Count(); pathIndex++)
{
const auto patch = _patches[pathIndex];
@@ -184,7 +179,6 @@ bool Terrain::RayCast(const Vector3& origin, const Vector3& direction, RayCastHi
result = true;
}
}
return result;
}
@@ -192,13 +186,12 @@ void Terrain::ClosestPoint(const Vector3& position, Vector3& result) const
{
float minDistance = MAX_float;
Vector3 tmp;
for (int32 pathIndex = 0; pathIndex < _patches.Count(); pathIndex++)
{
const auto patch = _patches[pathIndex];
if (patch->HasCollision())
{
patch->ClosestPoint(position, &tmp);
patch->ClosestPoint(position, tmp);
const auto distance = Vector3::DistanceSquared(position, tmp);
if (distance < minDistance)
{
@@ -237,22 +230,13 @@ void Terrain::OnPhysicalMaterialChanged()
if (_patches.IsEmpty())
return;
PxMaterial* material = Physics::GetDefaultMaterial();
if (PhysicalMaterial)
{
if (!PhysicalMaterial->WaitForLoaded())
{
material = ((::PhysicalMaterial*)PhysicalMaterial->Instance)->GetPhysXMaterial();
}
}
// Update the shapes material
for (int32 pathIndex = 0; pathIndex < _patches.Count(); pathIndex++)
{
const auto patch = _patches[pathIndex];
if (patch->HasCollision())
{
patch->_physicsShape->setMaterials(&material, 1);
PhysicsBackend::SetShapeMaterial(patch->_physicsShape, PhysicalMaterial.Get());
}
}
}
@@ -819,13 +803,12 @@ void Terrain::OnActiveInTreeChanged()
Actor::OnActiveInTreeChanged();
// Update physics
const PxShapeFlags shapeFlags = GetShapeFlags(false, IsActiveInHierarchy());
for (int32 pathIndex = 0; pathIndex < _patches.Count(); pathIndex++)
{
const auto patch = _patches[pathIndex];
if (patch->HasCollision())
{
patch->_physicsShape->setFlags(shapeFlags);
PhysicsBackend::SetShapeState(patch->_physicsShape, IsActiveInHierarchy(), false);
}
}
}

View File

@@ -5,11 +5,12 @@
#include "Engine/Serialization/Serialization.h"
#include "Engine/Core/Math/Color32.h"
#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/Physics/PhysicsBackend.h"
#include "Engine/Physics/CollisionCooking.h"
#include "Engine/Level/Scene/Scene.h"
#include "Engine/Level/Level.h"
#include "Engine/Graphics/Async/GPUTask.h"
#include "Engine/Threading/Threading.h"
#if TERRAIN_EDITING
@@ -19,6 +20,7 @@
#include "Engine/Graphics/RenderView.h"
#include "Engine/Graphics/Textures/GPUTexture.h"
#include "Engine/Graphics/Textures/TextureData.h"
#include "Engine/Serialization/MemoryWriteStream.h"
#if USE_EDITOR
#include "Editor/Editor.h"
#include "Engine/ContentImporters/AssetsImportingManager.h"
@@ -27,21 +29,10 @@
#if USE_EDITOR
#include "Engine/Debug/DebugDraw.h"
#endif
#include "Engine/Physics/CollisionCooking.h"
#include "Engine/Content/Content.h"
#include "Engine/Content/Assets/RawDataAsset.h"
#include "Engine/Level/Level.h"
#include <extensions/PxDefaultStreams.h>
#include <extensions/PxShapeExt.h>
#include <foundation/PxTransform.h>
#include <geometry/PxHeightField.h>
#include <geometry/PxHeightFieldDesc.h>
#include <geometry/PxHeightFieldSample.h>
#include <cooking/PxCooking.h>
#include <PxRigidStatic.h>
#include <PxPhysics.h>
#define TERRAIN_PATCH_COLLISION_QUANTIZATION ((PxReal)0x7fff)
#define TERRAIN_PATCH_COLLISION_QUANTIZATION ((float)0x7fff)
struct TerrainCollisionDataHeader
{
@@ -128,8 +119,7 @@ void TerrainPatch::UpdateTransform()
if (_physicsActor)
{
const Transform& terrainTransform = _terrain->_transform;
const PxTransform trans(C2P(terrainTransform.LocalToWorld(_offset)), C2P(terrainTransform.Orientation));
_physicsActor->setGlobalPose(trans);
PhysicsBackend::SetRigidActorPose(_physicsActor, terrainTransform.LocalToWorld(_offset), terrainTransform.Orientation);
}
// Update chunks cache
@@ -565,10 +555,10 @@ bool CookCollision(const TerrainDataUpdateInfo& info, TextureBase::InitData* ini
const int32 heightFieldChunkSize = ((info.ChunkSize + 1) >> collisionLOD) - 1;
const int32 heightFieldSize = heightFieldChunkSize * TerrainPatch::CHUNKS_COUNT_EDGE + 1;
const int32 heightFieldLength = heightFieldSize * heightFieldSize;
GET_TERRAIN_SCRATCH_BUFFER(heightFieldData, heightFieldLength, PxHeightFieldSample);
PxHeightFieldSample sample;
Platform::MemoryClear(&sample, sizeof(PxHeightFieldSample));
Platform::MemoryClear(heightFieldData, sizeof(PxHeightFieldSample) * heightFieldLength);
GET_TERRAIN_SCRATCH_BUFFER(heightFieldData, heightFieldLength, PhysicsBackend::HeightFieldSample);
PhysicsBackend::HeightFieldSample sample;
Platform::MemoryClear(&sample, sizeof(PhysicsBackend::HeightFieldSample));
Platform::MemoryClear(heightFieldData, sizeof(PhysicsBackend::HeightFieldSample) * heightFieldLength);
// Setup terrain collision information
auto& mip = initData->Mips[collisionLOD];
@@ -598,47 +588,38 @@ bool CookCollision(const TerrainDataUpdateInfo& info, TextureBase::InitData* ini
const int32 heightmapZ = chunkStartZ + z;
const int32 dstIndex = (heightmapX * heightFieldSize) + heightmapZ;
sample.height = PxI16(TERRAIN_PATCH_COLLISION_QUANTIZATION * normalizedHeight);
sample.materialIndex0 = sample.materialIndex1 = isHole ? PxHeightFieldMaterial::eHOLE : 0;
sample.Height = int16(TERRAIN_PATCH_COLLISION_QUANTIZATION * normalizedHeight);
sample.MaterialIndex0 = sample.MaterialIndex1 = isHole ? (uint8)PhysicsBackend::HeightFieldMaterial::Hole : 0;
heightFieldData[dstIndex] = sample;
}
}
}
}
PxHeightFieldDesc heightFieldDesc;
heightFieldDesc.format = PxHeightFieldFormat::eS16_TM;
heightFieldDesc.flags = PxHeightFieldFlag::eNO_BOUNDARY_EDGES;
heightFieldDesc.nbColumns = heightFieldSize;
heightFieldDesc.nbRows = heightFieldSize;
heightFieldDesc.samples.data = heightFieldData;
heightFieldDesc.samples.stride = sizeof(PxHeightFieldSample);
// Cook height field
PxDefaultMemoryOutputStream outputStream;
if (CollisionCooking::CookHeightField(heightFieldDesc, outputStream))
MemoryWriteStream outputStream;
if (CollisionCooking::CookHeightField(heightFieldSize, heightFieldSize, heightFieldData, outputStream))
{
return true;
}
// Write results
collisionData->Resize(sizeof(TerrainCollisionDataHeader) + outputStream.getSize(), false);
collisionData->Resize(sizeof(TerrainCollisionDataHeader) + outputStream.GetPosition(), false);
const auto header = (TerrainCollisionDataHeader*)collisionData->Get();
header->LOD = collisionLOD;
header->ScaleXZ = (float)info.HeightmapSize / heightFieldSize;
Platform::MemoryCopy(collisionData->Get() + sizeof(TerrainCollisionDataHeader), outputStream.getData(), outputStream.getSize());
Platform::MemoryCopy(collisionData->Get() + sizeof(TerrainCollisionDataHeader), outputStream.GetHandle(), outputStream.GetPosition());
return false;
#else
LOG(Warning, "Collision cooking is disabled.");
return true;
LOG(Warning, "Collision cooking is disabled.");
return true;
#endif
}
bool ModifyCollision(const TerrainDataUpdateInfo& info, TextureBase::InitData* initData, int32 collisionLod, const Int2& modifiedOffset, const Int2& modifiedSize, PxHeightField* heightField)
bool ModifyCollision(const TerrainDataUpdateInfo& info, TextureBase::InitData* initData, int32 collisionLod, const Int2& modifiedOffset, const Int2& modifiedSize, void* heightField)
{
PROFILE_CPU_NAMED("Terrain.ModifyCollision");
@@ -658,10 +639,10 @@ bool ModifyCollision(const TerrainDataUpdateInfo& info, TextureBase::InitData* i
// Allocate data
const int32 heightFieldDataLength = samplesSize.X * samplesSize.Y;
GET_TERRAIN_SCRATCH_BUFFER(heightFieldData, info.HeightmapLength, PxHeightFieldSample);
PxHeightFieldSample sample;
Platform::MemoryClear(&sample, sizeof(PxHeightFieldSample));
Platform::MemoryClear(heightFieldData, sizeof(PxHeightFieldSample) * heightFieldDataLength);
GET_TERRAIN_SCRATCH_BUFFER(heightFieldData, info.HeightmapLength, PhysicsBackend::HeightFieldSample);
PhysicsBackend::HeightFieldSample sample;
Platform::MemoryClear(&sample, sizeof(PhysicsBackend::HeightFieldSample));
Platform::MemoryClear(heightFieldData, sizeof(PhysicsBackend::HeightFieldSample) * heightFieldDataLength);
// Setup terrain collision information
auto& mip = initData->Mips[collisionLOD];
@@ -711,24 +692,17 @@ bool ModifyCollision(const TerrainDataUpdateInfo& info, TextureBase::InitData* i
const int32 dstIndex = (heightmapLocalX * samplesSize.Y) + heightmapLocalZ;
sample.height = PxI16(TERRAIN_PATCH_COLLISION_QUANTIZATION * normalizedHeight);
sample.materialIndex0 = sample.materialIndex1 = isHole ? PxHeightFieldMaterial::eHOLE : 0;
sample.Height = int16(TERRAIN_PATCH_COLLISION_QUANTIZATION * normalizedHeight);
sample.MaterialIndex0 = sample.MaterialIndex1 = isHole ? (uint8)PhysicsBackend::HeightFieldMaterial::Hole : 0;
heightFieldData[dstIndex] = sample;
}
}
}
}
PxHeightFieldDesc heightFieldDesc;
heightFieldDesc.format = PxHeightFieldFormat::eS16_TM;
heightFieldDesc.flags = PxHeightFieldFlag::eNO_BOUNDARY_EDGES;
heightFieldDesc.nbColumns = samplesSize.Y;
heightFieldDesc.nbRows = samplesSize.X;
heightFieldDesc.samples.data = heightFieldData;
heightFieldDesc.samples.stride = sizeof(PxHeightFieldSample);
// Update height field range
if (!heightField->modifySamples(samplesOffset.Y, samplesOffset.X, heightFieldDesc, true))
if (PhysicsBackend::ModifyHeightField(heightField, samplesOffset.Y, samplesOffset.X, samplesSize.Y, samplesSize.X, heightFieldData))
{
LOG(Warning, "Height Field collision modification failed.");
return true;
@@ -1958,7 +1932,7 @@ bool TerrainPatch::UpdateCollision()
_collisionVertices.Resize(0);
// Recreate height field
_terrain->GetPhysicsScene()->RemoveObject(_physicsHeightField);
PhysicsBackend::DestroyObject(_physicsHeightField);
_physicsHeightField = nullptr;
if (CreateHeightField())
{
@@ -1981,42 +1955,26 @@ bool TerrainPatch::RayCast(const Vector3& origin, const Vector3& direction, floa
{
if (_physicsShape == nullptr)
return false;
// Prepare data
PxTransform trans = _physicsActor->getGlobalPose();
trans.p = trans.transform(_physicsShape->getLocalPose().p);
const PxHitFlags hitFlags = (PxHitFlags)0;
// Perform raycast test
PxRaycastHit hit;
if (PxGeometryQuery::raycast(C2P(origin), C2P(direction), _physicsShape->getGeometry().any(), trans, maxDistance, hitFlags, 1, &hit) != 0)
{
resultHitDistance = hit.distance;
return true;
}
return false;
Vector3 shapePos;
Quaternion shapeRot;
PhysicsBackend::GetShapePose(_physicsShape, shapePos, shapeRot);
return PhysicsBackend::RayCastShape(_physicsShape, shapePos, shapeRot, origin, direction, resultHitDistance, maxDistance);
}
bool TerrainPatch::RayCast(const Vector3& origin, const Vector3& direction, float& resultHitDistance, Vector3& resultHitNormal, float maxDistance) const
{
if (_physicsShape == nullptr)
return false;
// Prepare data
PxTransform trans = _physicsActor->getGlobalPose();
trans.p = trans.transform(_physicsShape->getLocalPose().p);
const PxHitFlags hitFlags = PxHitFlag::eNORMAL;
// Perform raycast test
PxRaycastHit hit;
if (PxGeometryQuery::raycast(C2P(origin), C2P(direction), _physicsShape->getGeometry().any(), trans, maxDistance, hitFlags, 1, &hit) != 0)
Vector3 shapePos;
Quaternion shapeRot;
PhysicsBackend::GetShapePose(_physicsShape, shapePos, shapeRot);
RayCastHit hit;
if (PhysicsBackend::RayCastShape(_physicsShape, shapePos, shapeRot, origin, direction, hit, maxDistance))
{
resultHitDistance = hit.distance;
resultHitNormal = P2C(hit.normal);
resultHitDistance = hit.Distance;
resultHitNormal = hit.Normal;
return true;
}
return false;
}
@@ -2024,19 +1982,17 @@ bool TerrainPatch::RayCast(const Vector3& origin, const Vector3& direction, floa
{
if (_physicsShape == nullptr)
return false;
// Prepare data
PxTransform trans = _physicsActor->getGlobalPose();
trans.p = trans.transform(_physicsShape->getLocalPose().p);
const PxHitFlags hitFlags = (PxHitFlags)0;
Vector3 shapePos;
Quaternion shapeRot;
PhysicsBackend::GetShapePose(_physicsShape, shapePos, shapeRot);
// Perform raycast test
PxRaycastHit hit;
if (PxGeometryQuery::raycast(C2P(origin), C2P(direction), _physicsShape->getGeometry().any(), trans, maxDistance, hitFlags, 1, &hit) != 0)
float hitDistance;
if (PhysicsBackend::RayCastShape(_physicsShape, shapePos, shapeRot, origin, direction, hitDistance, maxDistance))
{
// Find hit chunk
resultChunk = nullptr;
const auto hitPoint = origin + direction * hit.distance;
const auto hitPoint = origin + direction * hitDistance;
for (int32 chunkIndex = 0; chunkIndex < CHUNKS_COUNT; chunkIndex++)
{
const auto box = Chunks[chunkIndex]._bounds;
@@ -2052,7 +2008,7 @@ bool TerrainPatch::RayCast(const Vector3& origin, const Vector3& direction, floa
if (resultChunk == nullptr)
return false;
resultHitDistance = hit.distance;
resultHitDistance = hitDistance;
return true;
}
@@ -2063,44 +2019,28 @@ bool TerrainPatch::RayCast(const Vector3& origin, const Vector3& direction, RayC
{
if (_physicsShape == nullptr)
return false;
// Prepare data
PxTransform trans = _physicsActor->getGlobalPose();
trans.p = trans.transform(_physicsShape->getLocalPose().p);
const PxHitFlags hitFlags = PxHitFlag::ePOSITION | PxHitFlag::eNORMAL | PxHitFlag::eUV;
PxRaycastHit hit;
// Perform raycast test
if (PxGeometryQuery::raycast(C2P(origin), C2P(direction), _physicsShape->getGeometry().any(), trans, maxDistance, hitFlags, 1, &hit) == 0)
return false;
// Gather results
hitInfo.Gather(hit);
return true;
Vector3 shapePos;
Quaternion shapeRot;
PhysicsBackend::GetShapePose(_physicsShape, shapePos, shapeRot);
return PhysicsBackend::RayCastShape(_physicsShape, shapePos, shapeRot, origin, direction, hitInfo, maxDistance);
}
void TerrainPatch::ClosestPoint(const Vector3& position, Vector3* result) const
void TerrainPatch::ClosestPoint(const Vector3& position, Vector3& result) const
{
if (_physicsShape == nullptr)
{
result = Vector3::Maximum;
return;
// Prepare data
PxTransform trans = _physicsActor->getGlobalPose();
trans.p = trans.transform(_physicsShape->getLocalPose().p);
PxVec3 closestPoint;
// Compute distance between a point and a geometry object
const float distanceSqr = PxGeometryQuery::pointDistance(C2P(position), _physicsShape->getGeometry().any(), trans, &closestPoint);
}
Vector3 shapePos;
Quaternion shapeRot;
PhysicsBackend::GetShapePose(_physicsShape, shapePos, shapeRot);
Vector3 closestPoint;
const float distanceSqr = PhysicsBackend::ComputeShapeSqrDistanceToPoint(_physicsShape, shapePos, shapeRot, position, &closestPoint);
if (distanceSqr > 0.0f)
{
// Use calculated point
*result = P2C(closestPoint);
}
result = closestPoint;
else
{
// Fallback to the input location
*result = position;
}
result = position;
}
#if USE_EDITOR
@@ -2130,7 +2070,7 @@ void TerrainPatch::UpdatePostManualDeserialization()
_collisionVertices.Resize(0);
// Recreate height field
_terrain->GetPhysicsScene()->RemoveObject(_physicsHeightField);
PhysicsBackend::DestroyObject(_physicsHeightField);
_physicsHeightField = nullptr;
if (CreateHeightField())
{
@@ -2152,45 +2092,27 @@ void TerrainPatch::UpdatePostManualDeserialization()
void TerrainPatch::CreateCollision()
{
ASSERT(!HasCollision());
if (CreateHeightField())
return;
ASSERT(_physicsHeightField);
// Create geometry
const Transform terrainTransform = _terrain->_transform;
PxHeightFieldGeometry geometry;
geometry.heightField = _physicsHeightField;
geometry.rowScale = Math::Max(Math::Abs(terrainTransform.Scale.X) * _collisionScaleXZ, PX_MIN_HEIGHTFIELD_XZ_SCALE);
geometry.heightScale = Math::Max(Math::Abs(terrainTransform.Scale.Y) * _yHeight / TERRAIN_PATCH_COLLISION_QUANTIZATION, PX_MIN_HEIGHTFIELD_Y_SCALE);
geometry.columnScale = Math::Max(Math::Abs(terrainTransform.Scale.Z) * _collisionScaleXZ, PX_MIN_HEIGHTFIELD_XZ_SCALE);
// Prepare
const PxShapeFlags shapeFlags = GetShapeFlags(false, _terrain->IsActiveInHierarchy());
PxMaterial* material = Physics::GetDefaultMaterial();
if (_terrain->PhysicalMaterial)
{
if (!_terrain->PhysicalMaterial->WaitForLoaded())
{
material = ((::PhysicalMaterial*)_terrain->PhysicalMaterial->Instance)->GetPhysXMaterial();
}
}
CollisionShape shape;
const float rowScale = Math::Abs(terrainTransform.Scale.X) * _collisionScaleXZ;
const float heightScale = Math::Abs(terrainTransform.Scale.Y) * _yHeight / TERRAIN_PATCH_COLLISION_QUANTIZATION;
const float columnScale = Math::Abs(terrainTransform.Scale.Z) * _collisionScaleXZ;
shape.SetHeightField(_physicsHeightField, heightScale, rowScale, columnScale);
// Create shape
_physicsShape = CPhysX->createShape(geometry, *material, true, shapeFlags);
ASSERT(_physicsShape);
_physicsShape->userData = _terrain;
_physicsShape->setLocalPose(PxTransform(0, _yOffset * terrainTransform.Scale.Y, 0));
_physicsShape = PhysicsBackend::CreateShape(_terrain, shape, _terrain->PhysicalMaterial.Get(), _terrain->IsActiveInHierarchy(), false);
PhysicsBackend::SetShapeLocalPose(_physicsShape, Vector3(0, _yOffset * terrainTransform.Scale.Y, 0), Quaternion::Identity);
// Create static actor
const PxTransform trans(C2P(terrainTransform.LocalToWorld(_offset)), C2P(terrainTransform.Orientation));
_physicsActor = CPhysX->createRigidStatic(trans);
ASSERT(_physicsActor);
_physicsActor->userData = _terrain;
#if WITH_PVD
_physicsActor->setActorFlag(PxActorFlag::eVISUALIZATION, true);
#endif
_physicsActor->attachShape(*_physicsShape);
_physicsActor = PhysicsBackend::CreateRigidStaticActor(nullptr, terrainTransform.LocalToWorld(_offset), terrainTransform.Orientation);
PhysicsBackend::AttachShape(_physicsShape, _physicsActor);
void* scene = _terrain->GetPhysicsScene()->GetPhysicsScene();
PhysicsBackend::AddSceneActor(scene, _physicsActor);
}
bool TerrainPatch::CreateHeightField()
@@ -2208,9 +2130,7 @@ bool TerrainPatch::CreateHeightField()
const auto collisionHeader = (TerrainCollisionDataHeader*)_heightfield->Data.Get();
_collisionScaleXZ = collisionHeader->ScaleXZ * TERRAIN_UNITS_PER_VERTEX;
PxDefaultMemoryInputData heightFieldData(_heightfield->Data.Get() + sizeof(TerrainCollisionDataHeader), _heightfield->Data.Count() - sizeof(TerrainCollisionDataHeader));
_physicsHeightField = CPhysX->createHeightField(heightFieldData);
_physicsHeightField = PhysicsBackend::CreateHeightField(_heightfield->Data.Get() + sizeof(TerrainCollisionDataHeader), _heightfield->Data.Count() - sizeof(TerrainCollisionDataHeader));
if (_physicsHeightField == nullptr)
{
LOG(Error, "Failed to create terrain collision height field.");
@@ -2226,15 +2146,15 @@ void TerrainPatch::UpdateCollisionScale() const
// Create geometry
const Transform terrainTransform = _terrain->_transform;
PxHeightFieldGeometry geometry;
geometry.heightField = _physicsHeightField;
geometry.rowScale = Math::Max(Math::Abs(terrainTransform.Scale.X) * _collisionScaleXZ, PX_MIN_HEIGHTFIELD_XZ_SCALE);
geometry.heightScale = Math::Max(Math::Abs(terrainTransform.Scale.Y) * _yHeight / TERRAIN_PATCH_COLLISION_QUANTIZATION, PX_MIN_HEIGHTFIELD_Y_SCALE);
geometry.columnScale = Math::Max(Math::Abs(terrainTransform.Scale.Z) * _collisionScaleXZ, PX_MIN_HEIGHTFIELD_XZ_SCALE);
CollisionShape geometry;
const float rowScale = Math::Abs(terrainTransform.Scale.X) * _collisionScaleXZ;
const float heightScale = Math::Abs(terrainTransform.Scale.Y) * _yHeight / TERRAIN_PATCH_COLLISION_QUANTIZATION;
const float columnScale = Math::Abs(terrainTransform.Scale.Z) * _collisionScaleXZ;
geometry.SetHeightField(_physicsHeightField, heightScale, rowScale, columnScale);
// Update shape
_physicsShape->setGeometry(geometry);
_physicsShape->setLocalPose(PxTransform(0, _yOffset * terrainTransform.Scale.Y, 0));
PhysicsBackend::SetShapeGeometry(_physicsShape, geometry);
PhysicsBackend::SetShapeLocalPose(_physicsShape, Vector3(0, _yOffset * terrainTransform.Scale.Y, 0), Quaternion::Identity);
}
void TerrainPatch::DestroyCollision()
@@ -2242,10 +2162,12 @@ void TerrainPatch::DestroyCollision()
ScopeLock lock(_collisionLocker);
ASSERT(HasCollision());
_terrain->GetPhysicsScene()->RemoveCollider(_terrain);
_terrain->GetPhysicsScene()->RemoveActor(_physicsActor);
_terrain->GetPhysicsScene()->RemoveObject(_physicsShape);
_terrain->GetPhysicsScene()->RemoveObject(_physicsHeightField);
void* scene = _terrain->GetPhysicsScene()->GetPhysicsScene();
PhysicsBackend::RemoveCollider(_terrain);
PhysicsBackend::RemoveSceneActor(scene, _physicsActor);
PhysicsBackend::DestroyActor(_physicsActor);
PhysicsBackend::DestroyShape(_physicsShape);
PhysicsBackend::DestroyObject(_physicsHeightField);
_physicsActor = nullptr;
_physicsShape = nullptr;
@@ -2265,17 +2187,17 @@ void TerrainPatch::CacheDebugLines()
{
ASSERT(_debugLines.IsEmpty() && _physicsHeightField);
const uint32 rows = _physicsHeightField->getNbRows();
const uint32 cols = _physicsHeightField->getNbColumns();
int32 rows, cols;
PhysicsBackend::GetHeightFieldSize(_physicsHeightField, rows, cols);
_debugLines.Resize((rows - 1) * (cols - 1) * 6 + (cols + rows - 2) * 2);
Vector3* data = _debugLines.Get();
#define GET_VERTEX(x, y) const Vector3 v##x##y((float)(row + (x)), _physicsHeightField->getHeight((PxReal)(row + (x)), (PxReal)(col + (y))) / TERRAIN_PATCH_COLLISION_QUANTIZATION, (float)(col + (y)))
#define GET_VERTEX(x, y) const Vector3 v##x##y((float)(row + (x)), PhysicsBackend::GetHeightFieldHeight(_physicsHeightField, (float)(row + (x)), (float)(col + (y))) / TERRAIN_PATCH_COLLISION_QUANTIZATION, (float)(col + (y)))
for (uint32 row = 0; row < rows - 1; row++)
for (int32 row = 0; row < rows - 1; row++)
{
for (uint32 col = 0; col < cols - 1; col++)
for (int32 col = 0; col < cols - 1; col++)
{
GET_VERTEX(0, 0);
GET_VERTEX(0, 1);
@@ -2293,9 +2215,9 @@ void TerrainPatch::CacheDebugLines()
}
}
for (uint32 row = 0; row < rows - 1; row++)
for (int32 row = 0; row < rows - 1; row++)
{
const uint32 col = cols - 1;
const int32 col = cols - 1;
GET_VERTEX(0, 0);
GET_VERTEX(1, 0);
@@ -2303,9 +2225,9 @@ void TerrainPatch::CacheDebugLines()
*data++ = v10;
}
for (uint32 col = 0; col < cols - 1; col++)
for (int32 col = 0; col < cols - 1; col++)
{
const uint32 row = rows - 1;
const int32 row = rows - 1;
GET_VERTEX(0, 0);
GET_VERTEX(0, 1);
@@ -2352,22 +2274,22 @@ const Array<Vector3>& TerrainPatch::GetCollisionTriangles()
if (!_physicsShape || _collisionTriangles.HasItems())
return _collisionTriangles;
const uint32 rows = _physicsHeightField->getNbRows();
const uint32 cols = _physicsHeightField->getNbColumns();
int32 rows, cols;
PhysicsBackend::GetHeightFieldSize(_physicsHeightField, rows, cols);
_collisionTriangles.Resize((rows - 1) * (cols - 1) * 6);
Vector3* data = _collisionTriangles.Get();
#define GET_VERTEX(x, y) Vector3 v##x##y((float)(row + (x)), _physicsHeightField->getHeight((PxReal)(row + (x)), (PxReal)(col + (y))) / TERRAIN_PATCH_COLLISION_QUANTIZATION, (float)(col + (y))); Vector3::Transform(v##x##y, world, v##x##y)
#define GET_VERTEX(x, y) Vector3 v##x##y((float)(row + (x)), PhysicsBackend::GetHeightFieldHeight(_physicsHeightField, (float)(row + (x)), (float)(col + (y))) / TERRAIN_PATCH_COLLISION_QUANTIZATION, (float)(col + (y))); Vector3::Transform(v##x##y, world, v##x##y)
const float size = _terrain->_chunkSize * TERRAIN_UNITS_PER_VERTEX * CHUNKS_COUNT_EDGE;
const Transform terrainTransform = _terrain->_transform;
Transform localTransform(Vector3(_x * size, _yOffset, _z * size), Quaternion::Identity, Vector3(_collisionScaleXZ, _yHeight, _collisionScaleXZ));
const Matrix world = localTransform.GetWorld() * terrainTransform.GetWorld();
for (uint32 row = 0; row < rows - 1; row++)
for (int32 row = 0; row < rows - 1; row++)
{
for (uint32 col = 0; col < cols - 1; col++)
for (int32 col = 0; col < cols - 1; col++)
{
GET_VERTEX(0, 0);
GET_VERTEX(0, 1);
@@ -2394,9 +2316,8 @@ void TerrainPatch::GetCollisionTriangles(const BoundingSphere& bounds, Array<Vec
result.Clear();
// Skip if no intersection with patch
if (!CollisionsHelper::BoxIntersectsSphere(GetBounds(), bounds))
if (!CollisionsHelper::BoxIntersectsSphere(GetBounds(), bounds) || !_physicsHeightField)
return;
CHECK(_physicsHeightField);
// Prepare
const auto& triangles = GetCollisionTriangles();
@@ -2424,8 +2345,8 @@ void TerrainPatch::GetCollisionTriangles(const BoundingSphere& bounds, Array<Vec
}
// Normalize bounds and map to actual triangles buffer
const int32 rows = _physicsHeightField->getNbRows();
const int32 cols = _physicsHeightField->getNbColumns();
int32 rows, cols;
PhysicsBackend::GetHeightFieldSize(_physicsHeightField, rows, cols);
int32 startRow = Math::FloorToInt(min.X / size * rows);
int32 startCol = Math::FloorToInt(min.Z / size * cols);
int32 endRow = Math::CeilToInt(max.X / size * rows);
@@ -2495,8 +2416,8 @@ void TerrainPatch::ExtractCollisionGeometry(Array<Vector3>& vertexBuffer, Array<
if (!_physicsShape)
return;
const uint32 rows = _physicsHeightField->getNbRows();
const uint32 cols = _physicsHeightField->getNbColumns();
int32 rows, cols;
PhysicsBackend::GetHeightFieldSize(_physicsHeightField, rows, cols);
// Cache pre-transformed collision heightfield vertices locations
if (_collisionVertices.IsEmpty())
@@ -2513,11 +2434,11 @@ void TerrainPatch::ExtractCollisionGeometry(Array<Vector3>& vertexBuffer, Array<
const int32 vertexCount = rows * cols;
_collisionVertices.Resize(vertexCount);
Vector3* vb = _collisionVertices.Get();
for (uint32 row = 0; row < rows; row++)
for (int32 row = 0; row < rows; row++)
{
for (uint32 col = 0; col < cols; col++)
for (int32 col = 0; col < cols; col++)
{
Vector3 v((float)row, _physicsHeightField->getHeight((PxReal)row, (PxReal)col) / TERRAIN_PATCH_COLLISION_QUANTIZATION, (float)col);
Vector3 v((float)row, PhysicsBackend::GetHeightFieldHeight(_physicsHeightField, (float)row, (float)col) / TERRAIN_PATCH_COLLISION_QUANTIZATION, (float)col);
Vector3::Transform(v, world, v);
*vb++ = v;
}
@@ -2532,9 +2453,9 @@ void TerrainPatch::ExtractCollisionGeometry(Array<Vector3>& vertexBuffer, Array<
const int32 indexCount = (rows - 1) * (cols - 1) * 6;
indexBuffer.Resize(indexCount);
int32* ib = indexBuffer.Get();
for (uint32 row = 0; row < rows - 1; row++)
for (int32 row = 0; row < rows - 1; row++)
{
for (uint32 col = 0; col < cols - 1; col++)
for (int32 col = 0; col < cols - 1; col++)
{
#define GET_INDEX(x, y) *ib++ = (col + (y)) + (row + (x)) * cols
@@ -2612,6 +2533,7 @@ void TerrainPatch::Deserialize(DeserializeStream& stream, ISerializeModifier* mo
void TerrainPatch::OnPhysicsSceneChanged(PhysicsScene* previous)
{
previous->UnlinkActor(_physicsActor);
_terrain->GetPhysicsScene()->AddActor(_physicsActor);
PhysicsBackend::RemoveSceneActor(previous->GetPhysicsScene(), _physicsActor);
void* scene = _terrain->GetPhysicsScene()->GetPhysicsScene();
PhysicsBackend::AddSceneActor(scene, _physicsActor);
}

View File

@@ -6,7 +6,6 @@
#include "TerrainChunk.h"
#include "Engine/Core/Math/Color32.h"
#include "Engine/Core/Math/Int2.h"
#include "Engine/Physics/Types.h"
#include "Engine/Level/Scene/Lightmap.h"
#include "Engine/Content/Assets/RawDataAsset.h"
@@ -38,9 +37,9 @@ private:
BoundingBox _bounds;
Vector3 _offset;
AssetReference<RawDataAsset> _heightfield;
PxShape* _physicsShape;
PxRigidStatic* _physicsActor;
PxHeightField* _physicsHeightField;
void* _physicsShape;
void* _physicsActor;
void* _physicsHeightField;
CriticalSection _collisionLocker;
float _collisionScaleXZ;
#if TERRAIN_UPDATING
@@ -331,7 +330,7 @@ public:
/// </summary>
/// <param name="position">The position to find the closest point to it.</param>
/// <param name="result">The result point on the collider that is closest to the specified location.</param>
void ClosestPoint(const Vector3& position, Vector3* result) const;
void ClosestPoint(const Vector3& position, Vector3& result) const;
#if USE_EDITOR
@@ -373,7 +372,6 @@ private:
/// <summary>
/// Determines whether this patch has created collision representation.
/// </summary>
/// <returns><c>true</c> if this patch has collider; otherwise, <c>false</c>.</returns>
FORCE_INLINE bool HasCollision() const
{
return _physicsShape != nullptr;

View File

@@ -48,6 +48,8 @@ public class PhysX : DepsModule
if (useVehicle)
options.PublicDefinitions.Add("WITH_VEHICLE");
options.PublicDefinitions.Add("COMPILE_WITH_PHYSX");
string archPostFix = string.Empty;
switch (options.Platform.Target)
{