Physics3D/PhysWorld3D: Add generic RaycastQuery

This commit is contained in:
SirLynix 2023-03-23 13:16:06 +01:00 committed by Jérôme Leclercq
parent 4d42c0cf9c
commit 63d75e8904
4 changed files with 68 additions and 7 deletions

View File

@ -14,6 +14,7 @@
#include <Nazara/Math/Vector3.hpp> #include <Nazara/Math/Vector3.hpp>
#include <NazaraUtils/FunctionRef.hpp> #include <NazaraUtils/FunctionRef.hpp>
#include <NazaraUtils/MovablePtr.hpp> #include <NazaraUtils/MovablePtr.hpp>
#include <optional>
class btDynamicsWorld; class btDynamicsWorld;
class btRigidBody; class btRigidBody;
@ -39,6 +40,7 @@ namespace Nz
std::size_t GetMaxStepCount() const; std::size_t GetMaxStepCount() const;
Time GetStepSize() const; Time GetStepSize() const;
bool RaycastQuery(const Vector3f& from, const Vector3f& to, const FunctionRef<std::optional<float>(const RaycastHit& hitInfo)>& callback);
bool RaycastQueryFirst(const Vector3f& from, const Vector3f& to, RaycastHit* hitInfo = nullptr); bool RaycastQueryFirst(const Vector3f& from, const Vector3f& to, RaycastHit* hitInfo = nullptr);
void SetGravity(const Vector3f& gravity); void SetGravity(const Vector3f& gravity);

View File

@ -38,6 +38,7 @@ namespace Nz
inline BulletPhysWorld3D& GetPhysWorld(); inline BulletPhysWorld3D& GetPhysWorld();
inline const BulletPhysWorld3D& GetPhysWorld() const; inline const BulletPhysWorld3D& GetPhysWorld() const;
bool RaycastQuery(const Vector3f& from, const Vector3f& to, const FunctionRef<std::optional<float>(const RaycastHit& hitInfo)>& callback);
bool RaycastQueryFirst(const Vector3f& from, const Vector3f& to, RaycastHit* hitInfo = nullptr); bool RaycastQueryFirst(const Vector3f& from, const Vector3f& to, RaycastHit* hitInfo = nullptr);
void Update(Time elapsedTime); void Update(Time elapsedTime);

View File

@ -16,6 +16,38 @@
namespace Nz namespace Nz
{ {
class CallbackHitResult : public btCollisionWorld::RayResultCallback
{
public:
CallbackHitResult(const Vector3f& from, const Vector3f& to, const FunctionRef<std::optional<float>(const BulletPhysWorld3D::RaycastHit& hitInfo)>& callback) :
m_callback(callback),
m_from(from),
m_to(to)
{
}
btScalar addSingleResult(btCollisionWorld::LocalRayResult& rayResult, bool normalInWorldSpace) override
{
m_collisionObject = rayResult.m_collisionObject;
BulletPhysWorld3D::RaycastHit hitInfo;
hitInfo.fraction = rayResult.m_hitFraction;
hitInfo.hitPosition = Lerp(m_from, m_to, rayResult.m_hitFraction);
hitInfo.hitNormal = FromBullet((normalInWorldSpace) ? rayResult.m_hitNormalLocal : m_collisionObject->getWorldTransform().getBasis() * rayResult.m_hitNormalLocal);
if (const btRigidBody* body = btRigidBody::upcast(m_collisionObject))
hitInfo.hitBody = static_cast<BulletRigidBody3D*>(body->getUserPointer());
m_closestHitFraction = std::max(m_callback(hitInfo).value_or(m_closestHitFraction), 0.f);
return m_closestHitFraction;
}
private:
const FunctionRef<std::optional<float>(const BulletPhysWorld3D::RaycastHit& hitInfo)>& m_callback;
Vector3f m_from;
Vector3f m_to;
};
struct BulletPhysWorld3D::BulletWorld struct BulletPhysWorld3D::BulletWorld
{ {
btDefaultCollisionConfiguration collisionConfiguration; btDefaultCollisionConfiguration collisionConfiguration;
@ -70,21 +102,29 @@ namespace Nz
return m_stepSize; return m_stepSize;
} }
bool BulletPhysWorld3D::RaycastQuery(const Vector3f& from, const Vector3f& to, const FunctionRef<std::optional<float>(const RaycastHit& hitInfo)>& callback)
{
CallbackHitResult resultHandler(from, to, callback);
m_world->dynamicWorld.rayTest(ToBullet(from), ToBullet(to), resultHandler);
return resultHandler.hasHit();
}
bool BulletPhysWorld3D::RaycastQueryFirst(const Vector3f& from, const Vector3f& to, RaycastHit* hitInfo) bool BulletPhysWorld3D::RaycastQueryFirst(const Vector3f& from, const Vector3f& to, RaycastHit* hitInfo)
{ {
btCollisionWorld::ClosestRayResultCallback callback(ToBullet(from), ToBullet(to)); btCollisionWorld::ClosestRayResultCallback resultHandler(ToBullet(from), ToBullet(to));
m_world->dynamicWorld.rayTest(ToBullet(from), ToBullet(to), callback); m_world->dynamicWorld.rayTest(ToBullet(from), ToBullet(to), resultHandler);
if (!callback.hasHit()) if (!resultHandler.hasHit())
return false; return false;
if (hitInfo) if (hitInfo)
{ {
hitInfo->fraction = callback.m_closestHitFraction; hitInfo->fraction = resultHandler.m_closestHitFraction;
hitInfo->hitNormal = FromBullet(callback.m_hitNormalWorld); hitInfo->hitNormal = FromBullet(resultHandler.m_hitNormalWorld);
hitInfo->hitPosition = FromBullet(callback.m_hitPointWorld); hitInfo->hitPosition = FromBullet(resultHandler.m_hitPointWorld);
if (const btRigidBody* body = btRigidBody::upcast(callback.m_collisionObject)) if (const btRigidBody* body = btRigidBody::upcast(resultHandler.m_collisionObject))
hitInfo->hitBody = static_cast<BulletRigidBody3D*>(body->getUserPointer()); hitInfo->hitBody = static_cast<BulletRigidBody3D*>(body->getUserPointer());
} }

View File

@ -46,6 +46,24 @@ namespace Nz
m_updateTime = Time::Zero(); m_updateTime = Time::Zero();
} }
bool BulletPhysics3DSystem::RaycastQuery(const Vector3f& from, const Vector3f& to, const FunctionRef<std::optional<float>(const RaycastHit& hitInfo)>& callback)
{
return m_physWorld.RaycastQuery(from, to, [&](const BulletPhysWorld3D::RaycastHit& hitInfo)
{
RaycastHit hitWithEntity;
static_cast<BulletPhysWorld3D::RaycastHit&>(hitWithEntity) = hitInfo;
if (hitWithEntity.hitBody)
{
std::size_t uniqueIndex = hitWithEntity.hitBody->GetUniqueIndex();
if (uniqueIndex < m_physicsEntities.size())
hitWithEntity.hitEntity = entt::handle(m_registry, m_physicsEntities[uniqueIndex]);
}
return callback(hitWithEntity);
});
}
bool BulletPhysics3DSystem::RaycastQueryFirst(const Vector3f& from, const Vector3f& to, RaycastHit* hitInfo) bool BulletPhysics3DSystem::RaycastQueryFirst(const Vector3f& from, const Vector3f& to, RaycastHit* hitInfo)
{ {
if (!m_physWorld.RaycastQueryFirst(from, to, hitInfo)) if (!m_physWorld.RaycastQueryFirst(from, to, hitInfo))