mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-07-16 09:04:38 +00:00
multiple fixes,
JoltPlayer was getting physics from triggers JoltWorld ray was colliding with triggers which made triggers collidable few other fixes around making joltCollision shapes
This commit is contained in:
parent
f3b9c65411
commit
51f5d507ed
5 changed files with 261 additions and 187 deletions
|
|
@ -1,6 +1,6 @@
|
||||||
#include "platform/platform.h"
|
#include "platform/platform.h"
|
||||||
#include "T3D/physics/jolt/joltBody.h"
|
#include "T3D/physics/jolt/joltBody.h"
|
||||||
|
|
||||||
#include "math/mBox.h"
|
#include "math/mBox.h"
|
||||||
#include "console/console.h"
|
#include "console/console.h"
|
||||||
#include "scene/sceneObject.h"
|
#include "scene/sceneObject.h"
|
||||||
|
|
@ -22,13 +22,21 @@ JoltBody::~JoltBody()
|
||||||
SAFE_DELETE(mCenterOfMass);
|
SAFE_DELETE(mCenterOfMass);
|
||||||
SAFE_DELETE(mInvCenterOfMass);
|
SAFE_DELETE(mInvCenterOfMass);
|
||||||
|
|
||||||
mColShape = NULL;
|
mColShape = nullptr;
|
||||||
setSimulationEnabled(false);
|
|
||||||
|
if (!isValid())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// User data must be cleared BEFORE the body is removed or destroyed.
|
||||||
|
// DestroyBody returns the body to Jolt's internal pool; touching it
|
||||||
|
// afterwards is use-after-free undefined behaviour.
|
||||||
|
mBody->SetUserData(0);
|
||||||
|
|
||||||
|
setSimulationEnabled(false); // removes body from the simulation
|
||||||
|
|
||||||
JPH::BodyInterface& bi = mWorld->getPhysicsSystem()->GetBodyInterface();
|
JPH::BodyInterface& bi = mWorld->getPhysicsSystem()->GetBodyInterface();
|
||||||
|
|
||||||
bi.DestroyBody(mBody->GetID());
|
bi.DestroyBody(mBody->GetID());
|
||||||
mBody->SetUserData(NULL);
|
mBody = nullptr;
|
||||||
mBody = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PhysicsWorld* JoltBody::getWorld()
|
PhysicsWorld* JoltBody::getWorld()
|
||||||
|
|
@ -93,6 +101,7 @@ bool JoltBody::init(PhysicsCollision* shape, F32 mass, U32 bodyFlags, SceneObjec
|
||||||
settings.mOverrideMassProperties = JPH::EOverrideMassProperties::CalculateInertia;
|
settings.mOverrideMassProperties = JPH::EOverrideMassProperties::CalculateInertia;
|
||||||
settings.mMassPropertiesOverride.mMass = mass;
|
settings.mMassPropertiesOverride.mMass = mass;
|
||||||
settings.mMaxLinearVelocity = 1000.0f; // stops an assert, this is torques upper limit, jolts is 500.0f by default.
|
settings.mMaxLinearVelocity = 1000.0f; // stops an assert, this is torques upper limit, jolts is 500.0f by default.
|
||||||
|
mMass = mass; // FIX: was never stored, getMass() always returned 0
|
||||||
mIsDynamic = true;
|
mIsDynamic = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -142,9 +151,15 @@ void JoltBody::setTransform(const MatrixF& xfm)
|
||||||
|
|
||||||
if (isDynamic())
|
if (isDynamic())
|
||||||
{
|
{
|
||||||
mBody->ResetMotion();// resets accumulated torque and forces, also velocity but do this explicitly
|
// ResetMotion (clears accumulated forces/torques) is a Body method only,
|
||||||
mBody->SetLinearVelocity(JPH::Vec3::sZero());
|
// so acquire a write lock. Velocity is cleared through the thread-safe interface.
|
||||||
mBody->SetAngularVelocity(JPH::Vec3::sZero());
|
{
|
||||||
|
JPH::BodyLockWrite lock(mWorld->getPhysicsSystem()->GetBodyLockInterface(), mBody->GetID());
|
||||||
|
if (lock.Succeeded())
|
||||||
|
lock.GetBody().ResetMotion();
|
||||||
|
}
|
||||||
|
bi.SetLinearVelocity(mBody->GetID(), JPH::Vec3::sZero());
|
||||||
|
bi.SetAngularVelocity(mBody->GetID(), JPH::Vec3::sZero());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -163,17 +178,26 @@ MatrixF& JoltBody::getTransform(MatrixF* outMatrix)
|
||||||
|
|
||||||
void JoltBody::applyImpulse(const Point3F& origin, const Point3F& force)
|
void JoltBody::applyImpulse(const Point3F& origin, const Point3F& force)
|
||||||
{
|
{
|
||||||
mBody->AddImpulse(joltCast(force), joltCast(origin));
|
if (!isValid())
|
||||||
|
return;
|
||||||
|
JPH::BodyInterface& bi = mWorld->getPhysicsSystem()->GetBodyInterface();
|
||||||
|
bi.AddImpulse(mBody->GetID(), joltCast(force), joltCast(origin));
|
||||||
}
|
}
|
||||||
|
|
||||||
void JoltBody::applyTorque(const Point3F& torque)
|
void JoltBody::applyTorque(const Point3F& torque)
|
||||||
{
|
{
|
||||||
mBody->AddTorque(joltCast(torque));
|
if (!isValid())
|
||||||
|
return;
|
||||||
|
JPH::BodyInterface& bi = mWorld->getPhysicsSystem()->GetBodyInterface();
|
||||||
|
bi.AddTorque(mBody->GetID(), joltCast(torque));
|
||||||
}
|
}
|
||||||
|
|
||||||
void JoltBody::applyForce(const Point3F& force)
|
void JoltBody::applyForce(const Point3F& force)
|
||||||
{
|
{
|
||||||
mBody->AddForce(joltCast(force));
|
if (!isValid())
|
||||||
|
return;
|
||||||
|
JPH::BodyInterface& bi = mWorld->getPhysicsSystem()->GetBodyInterface();
|
||||||
|
bi.AddForce(mBody->GetID(), joltCast(force));
|
||||||
}
|
}
|
||||||
|
|
||||||
void JoltBody::moveKinematicTo(const MatrixF& xfm)
|
void JoltBody::moveKinematicTo(const MatrixF& xfm)
|
||||||
|
|
@ -237,8 +261,8 @@ void JoltBody::setSleepThreshold(F32 linear, F32 angular)
|
||||||
|
|
||||||
JPH::MotionProperties* mp = mBody->GetMotionProperties();
|
JPH::MotionProperties* mp = mBody->GetMotionProperties();
|
||||||
|
|
||||||
/* mp->SetLinearSleepThreshold(linear);
|
/* mp->SetLinearSleepThreshold(linear);
|
||||||
mp->SetAngularSleepThreshold(angular);*/
|
mp->SetAngularSleepThreshold(angular);*/
|
||||||
}
|
}
|
||||||
|
|
||||||
void JoltBody::setDamping(F32 linear, F32 angular)
|
void JoltBody::setDamping(F32 linear, F32 angular)
|
||||||
|
|
@ -293,11 +317,7 @@ void JoltBody::getState(PhysicsState* outState)
|
||||||
// Linear momentum = m * v
|
// Linear momentum = m * v
|
||||||
F32 mass = mp->GetInverseMass() > 0.0f ? 1.0f / mp->GetInverseMass() : 0.0f;
|
F32 mass = mp->GetInverseMass() > 0.0f ? 1.0f / mp->GetInverseMass() : 0.0f;
|
||||||
outState->momentum = outState->linVelocity * mass;
|
outState->momentum = outState->linVelocity * mass;
|
||||||
|
outState->angularMomentum = outState->angVelocity;
|
||||||
// Angular momentum
|
|
||||||
JPH::Mat44 I_world = mBody->GetInverseInertia(); // 3x3 part
|
|
||||||
JPH::Vec3 angMomentum = I_world.Multiply3x3(mBody->GetAngularVelocity());
|
|
||||||
outState->angularMomentum = joltCast(angMomentum);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -335,7 +355,7 @@ void JoltBody::setLinVelocity(const Point3F& vel)
|
||||||
if (!bi.IsAdded(mBody->GetID()))
|
if (!bi.IsAdded(mBody->GetID()))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
mBody->SetLinearVelocity(joltCast(vel));
|
bi.SetLinearVelocity(mBody->GetID(), joltCast(vel));
|
||||||
}
|
}
|
||||||
|
|
||||||
void JoltBody::setAngVelocity(const Point3F& vel)
|
void JoltBody::setAngVelocity(const Point3F& vel)
|
||||||
|
|
@ -348,7 +368,7 @@ void JoltBody::setAngVelocity(const Point3F& vel)
|
||||||
if (!bi.IsAdded(mBody->GetID()))
|
if (!bi.IsAdded(mBody->GetID()))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
mBody->SetAngularVelocity(joltCast(vel));
|
bi.SetAngularVelocity(mBody->GetID(), joltCast(vel));
|
||||||
}
|
}
|
||||||
|
|
||||||
void JoltBody::findContact(SceneObject** contactObject,
|
void JoltBody::findContact(SceneObject** contactObject,
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,8 @@
|
||||||
|
|
||||||
// Save and undefine the macro if it exists
|
// Save and undefine the macro if it exists
|
||||||
#ifdef Offset
|
#ifdef Offset
|
||||||
#pragma push_macro("Offset")
|
#pragma push_macro("Offset")
|
||||||
#undef Offset
|
#undef Offset
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <Jolt/Physics/Collision/Shape/BoxShape.h>
|
#include <Jolt/Physics/Collision/Shape/BoxShape.h>
|
||||||
|
|
@ -21,7 +21,7 @@
|
||||||
|
|
||||||
#ifdef Offset
|
#ifdef Offset
|
||||||
// Restore the original macro after includes
|
// Restore the original macro after includes
|
||||||
#pragma pop_macro("Offset")
|
#pragma pop_macro("Offset")
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
JoltCollision::JoltCollision()
|
JoltCollision::JoltCollision()
|
||||||
|
|
@ -73,7 +73,7 @@ void JoltCollision::addBox(const Point3F& halfWidth, const MatrixF& localXfm)
|
||||||
toJolt(localXfm, localPos, localRot);
|
toJolt(localXfm, localPos, localRot);
|
||||||
|
|
||||||
// Wrap the base shape with RotatedTranslatedShape
|
// Wrap the base shape with RotatedTranslatedShape
|
||||||
auto rtsSettings = new JPH::RotatedTranslatedShapeSettings(localPos, localRot, baseShape);
|
JPH::Ref<JPH::RotatedTranslatedShapeSettings> rtsSettings = new JPH::RotatedTranslatedShapeSettings(localPos, localRot, baseShape);
|
||||||
auto rtsShape = rtsSettings->Create().Get();
|
auto rtsShape = rtsSettings->Create().Get();
|
||||||
|
|
||||||
// Store in child entry
|
// Store in child entry
|
||||||
|
|
@ -103,7 +103,7 @@ void JoltCollision::addSphere(F32 radius, const MatrixF& localXfm)
|
||||||
JPH::Quat localRot;
|
JPH::Quat localRot;
|
||||||
toJolt(localXfm, localPos, localRot);
|
toJolt(localXfm, localPos, localRot);
|
||||||
|
|
||||||
auto rtsSettings = new JPH::RotatedTranslatedShapeSettings(localPos, localRot, baseShape);
|
JPH::Ref<JPH::RotatedTranslatedShapeSettings> rtsSettings = new JPH::RotatedTranslatedShapeSettings(localPos, localRot, baseShape);
|
||||||
auto rtsShape = rtsSettings->Create().Get();
|
auto rtsShape = rtsSettings->Create().Get();
|
||||||
|
|
||||||
ChildShapeEntry entry;
|
ChildShapeEntry entry;
|
||||||
|
|
@ -132,7 +132,7 @@ void JoltCollision::addCapsule(F32 radius, F32 height, const MatrixF& localXfm)
|
||||||
JPH::Quat localRot;
|
JPH::Quat localRot;
|
||||||
toJolt(localXfm, localPos, localRot);
|
toJolt(localXfm, localPos, localRot);
|
||||||
|
|
||||||
auto rtsSettings = new JPH::RotatedTranslatedShapeSettings(localPos, localRot, baseShape);
|
JPH::Ref<JPH::RotatedTranslatedShapeSettings> rtsSettings = new JPH::RotatedTranslatedShapeSettings(localPos, localRot, baseShape);
|
||||||
auto rtsShape = rtsSettings->Create().Get();
|
auto rtsShape = rtsSettings->Create().Get();
|
||||||
|
|
||||||
ChildShapeEntry entry;
|
ChildShapeEntry entry;
|
||||||
|
|
@ -150,33 +150,34 @@ bool JoltCollision::addConvex(const Point3F* points, U32 count, const MatrixF& l
|
||||||
if (count == 0)
|
if (count == 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
// Pre-transform points into shape-local space for the same reason as
|
||||||
|
// addTriangleMesh: avoids the RTS wrapper and the double-transform in
|
||||||
|
// rebuildCompound when multiple shapes share a JoltCollision.
|
||||||
|
const bool isIdentity = localXfm.isIdentity();
|
||||||
|
|
||||||
std::vector<JPH::Vec3> verts;
|
std::vector<JPH::Vec3> verts;
|
||||||
verts.reserve(count);
|
verts.reserve(count);
|
||||||
for (U32 i = 0; i < count; ++i)
|
for (U32 i = 0; i < count; ++i)
|
||||||
verts.emplace_back(points[i].x, points[i].y, points[i].z);
|
{
|
||||||
|
Point3F p = points[i];
|
||||||
|
if (!isIdentity)
|
||||||
|
localXfm.mulP(points[i], &p);
|
||||||
|
verts.emplace_back(p.x, p.y, p.z);
|
||||||
|
}
|
||||||
|
|
||||||
JPH::ConvexHullShapeSettings settings(verts.data(), verts.size());
|
JPH::ConvexHullShapeSettings settings(verts.data(), (int)verts.size());
|
||||||
auto result = settings.Create();
|
auto result = settings.Create();
|
||||||
if (result.HasError())
|
if (result.HasError())
|
||||||
{
|
{
|
||||||
Con::errorf("Jolt Error: %s", result.GetError().c_str());
|
Con::errorf("Jolt addConvex Error: %s", result.GetError().c_str());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto baseShape = result.Get();
|
|
||||||
|
|
||||||
JPH::Vec3 localPos;
|
|
||||||
JPH::Quat localRot;
|
|
||||||
toJolt(localXfm, localPos, localRot);
|
|
||||||
|
|
||||||
auto rtsSettings = new JPH::RotatedTranslatedShapeSettings(localPos, localRot, baseShape);
|
|
||||||
auto rtsShape = rtsSettings->Create().Get();
|
|
||||||
|
|
||||||
ChildShapeEntry entry;
|
ChildShapeEntry entry;
|
||||||
entry.shape = rtsShape;
|
entry.shape = result.Get();
|
||||||
entry.localPos = localPos;
|
entry.localPos = JPH::Vec3::sZero();
|
||||||
entry.localRot = localRot;
|
entry.localRot = JPH::Quat::sIdentity();
|
||||||
entry.localXfm = joltCast(localXfm);
|
entry.localXfm = JPH::Mat44::sIdentity();
|
||||||
mChildren.push_back(entry);
|
mChildren.push_back(entry);
|
||||||
|
|
||||||
rebuildCompound();
|
rebuildCompound();
|
||||||
|
|
@ -188,50 +189,53 @@ bool JoltCollision::addTriangleMesh(const Point3F* vert, U32 vertCount, const U3
|
||||||
if (!vert || !index || vertCount == 0 || triCount == 0)
|
if (!vert || !index || vertCount == 0 || triCount == 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Build the TriangleList
|
// Bake localXfm directly into the vertex positions so the MeshShape sits in
|
||||||
|
// shape-local space with an identity transform. This avoids the need for an
|
||||||
|
// RTS wrapper and eliminates the double-transform bug that occurs when the
|
||||||
|
// wrapper's position is later re-applied by rebuildCompound's AddShape call.
|
||||||
|
const bool isIdentity = localXfm.isIdentity();
|
||||||
|
|
||||||
JPH::TriangleList triangles;
|
JPH::TriangleList triangles;
|
||||||
triangles.reserve(triCount);
|
triangles.reserve(triCount);
|
||||||
|
|
||||||
for (U32 i = 0; i < triCount; ++i)
|
for (U32 i = 0; i < triCount; ++i)
|
||||||
{
|
{
|
||||||
const Point3F& v0 = vert[index[i * 3 + 0]];
|
Point3F p0 = vert[index[i * 3 + 0]];
|
||||||
const Point3F& v1 = vert[index[i * 3 + 1]];
|
Point3F p1 = vert[index[i * 3 + 1]];
|
||||||
const Point3F& v2 = vert[index[i * 3 + 2]];
|
Point3F p2 = vert[index[i * 3 + 2]];
|
||||||
|
|
||||||
|
if (!isIdentity)
|
||||||
|
{
|
||||||
|
localXfm.mulP(vert[index[i * 3 + 0]], &p0);
|
||||||
|
localXfm.mulP(vert[index[i * 3 + 1]], &p1);
|
||||||
|
localXfm.mulP(vert[index[i * 3 + 2]], &p2);
|
||||||
|
}
|
||||||
|
|
||||||
triangles.push_back(
|
triangles.push_back(
|
||||||
JPH::Triangle(
|
JPH::Triangle(
|
||||||
JPH::Float3(v0.x, v0.y, v0.z),
|
JPH::Float3(p0.x, p0.y, p0.z),
|
||||||
JPH::Float3(v2.x, v2.y, v2.z),
|
JPH::Float3(p2.x, p2.y, p2.z), // winding order maintained
|
||||||
JPH::Float3(v1.x, v1.y, v1.z),
|
JPH::Float3(p1.x, p1.y, p1.z),
|
||||||
0, // material index
|
0, // material index
|
||||||
i // user data = original triangle index
|
i // user data = original triangle index
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the MeshShape
|
|
||||||
JPH::MeshShapeSettings settings(triangles);
|
JPH::MeshShapeSettings settings(triangles);
|
||||||
auto result = settings.Create();
|
auto result = settings.Create();
|
||||||
if (result.HasError())
|
if (result.HasError())
|
||||||
{
|
{
|
||||||
Con::errorf("Jolt Error: %s", result.GetError().c_str());
|
Con::errorf("Jolt addTriangleMesh Error: %s", result.GetError().c_str());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto baseShape = result.Get();
|
// Store at identity — vertices are already in shape-local space.
|
||||||
|
|
||||||
JPH::Vec3 localPos;
|
|
||||||
JPH::Quat localRot;
|
|
||||||
toJolt(localXfm, localPos, localRot);
|
|
||||||
|
|
||||||
auto rtsSettings = new JPH::RotatedTranslatedShapeSettings(localPos, localRot, baseShape);
|
|
||||||
auto rtsShape = rtsSettings->Create().Get();
|
|
||||||
|
|
||||||
ChildShapeEntry entry;
|
ChildShapeEntry entry;
|
||||||
entry.shape = rtsShape;
|
entry.shape = result.Get();
|
||||||
entry.localPos = localPos;
|
entry.localPos = JPH::Vec3::sZero();
|
||||||
entry.localRot = localRot;
|
entry.localRot = JPH::Quat::sIdentity();
|
||||||
entry.localXfm = joltCast(localXfm);
|
entry.localXfm = JPH::Mat44::sIdentity();
|
||||||
mChildren.push_back(entry);
|
mChildren.push_back(entry);
|
||||||
|
|
||||||
rebuildCompound();
|
rebuildCompound();
|
||||||
|
|
@ -248,49 +252,56 @@ bool JoltCollision::addHeightfield(
|
||||||
if (!heightData || blockSize == 0)
|
if (!heightData || blockSize == 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
const F32 heightScale = 0.03125f;
|
// Jolt's internal BVH page size: power-of-2 between 2 and 8, independent of
|
||||||
const F32 minHeight = 0;
|
// inSampleCount. Use 4 for larger terrains to produce a shallower BVH tree.
|
||||||
const F32 maxHeight = 65535 * heightScale;
|
const U32 joltBlockSize = (blockSize >= 512) ? 4 : 2;
|
||||||
|
|
||||||
const U32 sampleCount = blockSize * blockSize;
|
// inSampleCount must be a multiple of joltBlockSize. Round up so any blockSize
|
||||||
std::vector<float> samples(sampleCount);
|
// works — padding columns/rows are edge-clamped to stay physically correct.
|
||||||
|
const U32 paddedSize = ((blockSize + joltBlockSize - 1) / joltBlockSize) * joltBlockSize;
|
||||||
|
const U32 totalSamples = paddedSize * paddedSize;
|
||||||
|
|
||||||
for (U32 x = 0; x < blockSize; ++x)
|
// Pass 1: build a flat (un-flipped) padded grid with edge-clamping.
|
||||||
|
std::vector<float> unflipped(totalSamples);
|
||||||
|
for (U32 y = 0; y < paddedSize; ++y)
|
||||||
{
|
{
|
||||||
for (U32 y = 0; y < blockSize; ++y)
|
U32 srcY = (y < blockSize) ? y : blockSize - 1;
|
||||||
|
for (U32 x = 0; x < paddedSize; ++x)
|
||||||
{
|
{
|
||||||
// Terrain storage (row-major)
|
U32 srcX = (x < blockSize) ? x : blockSize - 1;
|
||||||
U32 srcIdx = y * blockSize + x;
|
U32 srcIdx = srcY * blockSize + srcX;
|
||||||
|
|
||||||
// Flip Z direction for Jolt
|
|
||||||
U32 dstIdx = (blockSize - 1 - y) * blockSize + x;
|
|
||||||
|
|
||||||
float h = fixedToFloat(heightData[srcIdx]);
|
float h = fixedToFloat(heightData[srcIdx]);
|
||||||
|
|
||||||
if (holes && holes[srcIdx])
|
if (holes && holes[srcIdx])
|
||||||
h = JPH::HeightFieldShapeConstants::cNoCollisionValue;
|
h = JPH::HeightFieldShapeConstants::cNoCollisionValue;
|
||||||
|
|
||||||
samples[dstIdx] = h;
|
unflipped[y * paddedSize + x] = h;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
float terrainSize = blockSize * metersPerSample;
|
// Pass 2: flip Y axis into the final sample array for Jolt's coordinate system.
|
||||||
float verticalAdjust = -heightScale * 0.5f;
|
std::vector<float> samples(totalSamples);
|
||||||
JPH::Vec3 joltOffset(
|
for (U32 y = 0; y < paddedSize; ++y)
|
||||||
0.0,
|
for (U32 x = 0; x < paddedSize; ++x)
|
||||||
verticalAdjust,
|
samples[(paddedSize - 1 - y) * paddedSize + x] = unflipped[y * paddedSize + x];
|
||||||
-terrainSize
|
|
||||||
);
|
|
||||||
|
|
||||||
JPH::Vec3 joltScale(metersPerSample,1.0f, metersPerSample);
|
// Offset uses actual terrain extent (blockSize) so the padded fringe never
|
||||||
|
// shifts the visible terrain. The vertical adjustment centres quantisation error.
|
||||||
|
const float heightScale = 0.03125f;
|
||||||
|
const float verticalAdjust = -heightScale * 0.5f;
|
||||||
|
const float terrainSize = blockSize * metersPerSample;
|
||||||
|
|
||||||
JPH::HeightFieldShapeSettings settings(samples.data(), joltOffset, joltScale, blockSize);
|
JPH::Vec3 joltOffset(0.0f, verticalAdjust, -terrainSize);
|
||||||
settings.mBlockSize = 2;
|
JPH::Vec3 joltScale(metersPerSample, 1.0f, metersPerSample);
|
||||||
|
|
||||||
|
JPH::HeightFieldShapeSettings settings(samples.data(), joltOffset, joltScale, paddedSize);
|
||||||
|
settings.mBlockSize = joltBlockSize;
|
||||||
|
|
||||||
auto result = settings.Create();
|
auto result = settings.Create();
|
||||||
if (result.HasError())
|
if (result.HasError())
|
||||||
{
|
{
|
||||||
Con::errorf("Jolt Error: %s", result.GetError().c_str());
|
Con::errorf("Jolt addHeightfield Error (blockSize=%u paddedSize=%u): %s",
|
||||||
|
blockSize, paddedSize, result.GetError().c_str());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -302,7 +313,7 @@ bool JoltCollision::addHeightfield(
|
||||||
JPH::Quat rotFix = JPH::Quat::sRotation(JPH::Vec3::sAxisX(), JPH::DegreesToRadians(90.0f));
|
JPH::Quat rotFix = JPH::Quat::sRotation(JPH::Vec3::sAxisX(), JPH::DegreesToRadians(90.0f));
|
||||||
localRot = rotFix * localRot;
|
localRot = rotFix * localRot;
|
||||||
|
|
||||||
auto rtsSettings = new JPH::RotatedTranslatedShapeSettings(localPos, localRot, baseShape);
|
JPH::Ref<JPH::RotatedTranslatedShapeSettings> rtsSettings = new JPH::RotatedTranslatedShapeSettings(localPos, localRot, baseShape);
|
||||||
auto rtsShape = rtsSettings->Create().Get();
|
auto rtsShape = rtsSettings->Create().Get();
|
||||||
|
|
||||||
ChildShapeEntry entry;
|
ChildShapeEntry entry;
|
||||||
|
|
@ -321,33 +332,35 @@ void JoltCollision::rebuildCompound()
|
||||||
if (mChildren.empty())
|
if (mChildren.empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
JPH::CompoundShapeSettings* compoundSettings = nullptr;
|
if (mChildren.size() == 1)
|
||||||
|
|
||||||
if (mChildren.size() > 1)
|
|
||||||
{
|
{
|
||||||
compoundSettings = new JPH::StaticCompoundShapeSettings();
|
// Single shape: use it directly. For primitive shapes this is the
|
||||||
|
// RotatedTranslatedShape with the local transform baked in. For
|
||||||
|
// triangle meshes and convex hulls it is the base shape at identity.
|
||||||
|
mCompundShape = mChildren[0].shape;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto& colShape : mChildren)
|
// Multiple shapes: build a static compound. Each child's ChildShapeEntry
|
||||||
|
// already has its local transform baked into entry.shape (either as an RTS
|
||||||
|
// wrapper for primitives, or pre-transformed vertices for meshes/convex).
|
||||||
|
// Using localPos/localRot here would apply the offset a SECOND time.
|
||||||
|
JPH::StaticCompoundShapeSettings compoundSettings;
|
||||||
|
for (const auto& child : mChildren)
|
||||||
{
|
{
|
||||||
if (compoundSettings)
|
compoundSettings.AddShape(
|
||||||
{
|
JPH::Vec3::sZero(),
|
||||||
compoundSettings->AddShape(
|
JPH::Quat::sIdentity(),
|
||||||
colShape.localPos,
|
child.shape
|
||||||
colShape.localRot,
|
);
|
||||||
colShape.shape
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
mCompundShape = colShape.shape;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (compoundSettings)
|
auto result = compoundSettings.Create();
|
||||||
|
if (result.HasError())
|
||||||
{
|
{
|
||||||
mCompundShape = compoundSettings->Create().Get();
|
Con::errorf("Jolt rebuildCompound Error: %s", result.GetError().c_str());
|
||||||
delete compoundSettings;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mCompundShape = result.Get();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -37,9 +37,18 @@ JoltPlayer::JoltPlayer()
|
||||||
|
|
||||||
JoltPlayer::~JoltPlayer()
|
JoltPlayer::~JoltPlayer()
|
||||||
{
|
{
|
||||||
mCharacter->SetUserData(NULL);
|
if (mCharacter)
|
||||||
mCharacter = NULL;
|
{
|
||||||
setSimulationEnabled(false);
|
// Remove the inner body from the physics world before the character is released.
|
||||||
|
// Setting mCharacter=nullptr first would make disableCollision() a no-op.
|
||||||
|
if (mCollisionEnabled)
|
||||||
|
disableCollision();
|
||||||
|
|
||||||
|
mCharacter->SetUserData(0);
|
||||||
|
mCharacter = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
mIsEnabled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void JoltPlayer::setTransform(const MatrixF& xfm)
|
void JoltPlayer::setTransform(const MatrixF& xfm)
|
||||||
|
|
@ -99,7 +108,6 @@ void JoltPlayer::init(const char* type, const Point3F& size, F32 runSurfaceCos,
|
||||||
JPH::Ref<JPH::Shape> shape = JPH::RotatedTranslatedShapeSettings(JPH::Vec3(0, 0, mOriginOffset), rotFix, new JPH::CapsuleShape(0.5f * height, radius)).Create().Get();
|
JPH::Ref<JPH::Shape> shape = JPH::RotatedTranslatedShapeSettings(JPH::Vec3(0, 0, mOriginOffset), rotFix, new JPH::CapsuleShape(0.5f * height, radius)).Create().Get();
|
||||||
JPH::Ref<JPH::Shape> inner_shape = JPH::RotatedTranslatedShapeSettings(JPH::Vec3(0, 0, mOriginOffset), rotFix, new JPH::CapsuleShape(0.5f * 0.9f * height, 0.9f * radius)).Create().Get();
|
JPH::Ref<JPH::Shape> inner_shape = JPH::RotatedTranslatedShapeSettings(JPH::Vec3(0, 0, mOriginOffset), rotFix, new JPH::CapsuleShape(0.5f * 0.9f * height, 0.9f * radius)).Create().Get();
|
||||||
|
|
||||||
|
|
||||||
JPH::Ref<JPH::CharacterVirtualSettings> settings = new JPH::CharacterVirtualSettings();
|
JPH::Ref<JPH::CharacterVirtualSettings> settings = new JPH::CharacterVirtualSettings();
|
||||||
Con::printf("JoltPlayer::init - maxSlopeAngle: %f degrees", mRadToDeg(mAcos(runSurfaceCos)));
|
Con::printf("JoltPlayer::init - maxSlopeAngle: %f degrees", mRadToDeg(mAcos(runSurfaceCos)));
|
||||||
settings->mMaxSlopeAngle = mAcos(runSurfaceCos);
|
settings->mMaxSlopeAngle = mAcos(runSurfaceCos);
|
||||||
|
|
@ -139,20 +147,12 @@ void JoltPlayer::init(const char* type, const Point3F& size, F32 runSurfaceCos,
|
||||||
setSimulationEnabled(true);
|
setSimulationEnabled(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
class CharacterBodyFilter : public JPH::BodyFilter
|
void JoltPlayer::preUpdate(F32 dt)
|
||||||
{
|
{
|
||||||
public:
|
// Intentionally empty. Ground velocity is refreshed inside move() on every
|
||||||
JPH::BodyID mCharacterBodyID;
|
// call. If the calling code ever starts calling preUpdate() before move(),
|
||||||
|
// the refresh in move() will simply be a no-op on the same-frame data.
|
||||||
CharacterBodyFilter(JPH::BodyID id)
|
}
|
||||||
: mCharacterBodyID(id) {
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual bool ShouldCollide(const JPH::BodyID& inBodyID) const override
|
|
||||||
{
|
|
||||||
return inBodyID != mCharacterBodyID;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Point3F JoltPlayer::move(const VectorF& displacement, CollisionList& outCol)
|
Point3F JoltPlayer::move(const VectorF& displacement, CollisionList& outCol)
|
||||||
{
|
{
|
||||||
|
|
@ -166,30 +166,31 @@ Point3F JoltPlayer::move(const VectorF& displacement, CollisionList& outCol)
|
||||||
JPH::Vec3 velocity = joltCast(displacement) / dt;
|
JPH::Vec3 velocity = joltCast(displacement) / dt;
|
||||||
|
|
||||||
const auto& system = mWorld->getPhysicsSystem();
|
const auto& system = mWorld->getPhysicsSystem();
|
||||||
const JPH::Vec3 up = mCharacter->GetUp();
|
|
||||||
|
|
||||||
mCharacter->UpdateGroundVelocity();
|
mCharacter->UpdateGroundVelocity();
|
||||||
|
|
||||||
const JPH::CharacterVirtual::EGroundState groundState = mCharacter->GetGroundState();
|
const JPH::CharacterVirtual::EGroundState groundState = mCharacter->GetGroundState();
|
||||||
const bool isOnGround = groundState == JPH::CharacterVirtual::EGroundState::OnGround;
|
const bool isOnGround = groundState == JPH::CharacterVirtual::EGroundState::OnGround;
|
||||||
|
|
||||||
// Temporary - log what displacement.z actually is each frame
|
|
||||||
/*Con::printf("groundState=%d disp.z=%.4f vel.z=%.4f",
|
|
||||||
(int)groundState,
|
|
||||||
displacement.z,
|
|
||||||
displacement.z / TickSec);*/
|
|
||||||
|
|
||||||
if (isOnGround && !mCharacter->IsSlopeTooSteep(mCharacter->GetGroundNormal()))
|
if (isOnGround && !mCharacter->IsSlopeTooSteep(mCharacter->GetGroundNormal()))
|
||||||
{
|
{
|
||||||
JPH::Vec3 horizontalVel(velocity.GetX(), velocity.GetY(), 0.0f);
|
JPH::Vec3 horizontalVel(velocity.GetX(), velocity.GetY(), 0.0f);
|
||||||
mAllowSliding = horizontalVel.Length() > 0.01f;
|
mAllowSliding = horizontalVel.Length() > 0.01f;
|
||||||
|
|
||||||
if (velocity.GetZ() < 0.05f)
|
velocity.SetZ(0.0f);
|
||||||
velocity.SetZ(0.00f);
|
|
||||||
|
|
||||||
JPH::Vec3 groundVel = mCharacter->GetGroundVelocity();
|
JPH::Vec3 groundVel = mCharacter->GetGroundVelocity();
|
||||||
if (groundVel.LengthSq() > 0.001f)
|
// Threshold raised to filter static-surface floating-point residuals.
|
||||||
velocity += groundVel;
|
if (groundVel.LengthSq() > 0.01f)
|
||||||
|
{
|
||||||
|
// Always carry horizontal platform motion (conveyor belts, sliding floors).
|
||||||
|
velocity.SetX(velocity.GetX() + groundVel.GetX());
|
||||||
|
velocity.SetY(velocity.GetY() + groundVel.GetY());
|
||||||
|
// Only carry Z from platforms that are meaningfully moving upward (elevators).
|
||||||
|
// Static surfaces can produce tiny Z residuals that fight gravity resolution.
|
||||||
|
if (groundVel.GetZ() > 0.05f)
|
||||||
|
velocity.SetZ(velocity.GetZ() + groundVel.GetZ());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -199,28 +200,25 @@ Point3F JoltPlayer::move(const VectorF& displacement, CollisionList& outCol)
|
||||||
mCharacter->SetLinearVelocity(velocity);
|
mCharacter->SetLinearVelocity(velocity);
|
||||||
|
|
||||||
//----------------------------------------
|
//----------------------------------------
|
||||||
// Substep (bullet was doing a max of 10 iterations)
|
// Substep
|
||||||
//----------------------------------------
|
//----------------------------------------
|
||||||
const U32 iterations = 3;
|
const U32 iterations = 3;
|
||||||
const F32 subDt = dt / iterations;
|
const F32 subDt = dt / iterations;
|
||||||
|
|
||||||
JPH::CharacterVirtual::ExtendedUpdateSettings settings;
|
// Build settings once outside the loop — they don't change per-substep.
|
||||||
|
JPH::CharacterVirtual::ExtendedUpdateSettings extSettings;
|
||||||
|
extSettings.mWalkStairsStepUp = JPH::Vec3(0, 0, mStepHeight);
|
||||||
|
extSettings.mStickToFloorStepDown = JPH::Vec3(0, 0, -mStepHeight);
|
||||||
|
extSettings.mWalkStairsCosAngleForwardContact = mMaxSlopeCos;
|
||||||
|
|
||||||
for (int i = 0; i < iterations; ++i)
|
for (U32 i = 0; i < iterations; ++i)
|
||||||
{
|
{
|
||||||
bool isLast = (i == iterations - 1);
|
|
||||||
|
|
||||||
settings.mWalkStairsStepUp = JPH::Vec3(0, 0, mStepHeight);
|
|
||||||
settings.mStickToFloorStepDown = JPH::Vec3(0, 0, -mStepHeight);
|
|
||||||
settings.mWalkStairsCosAngleForwardContact = mAcos(mMaxSlopeCos);
|
|
||||||
|
|
||||||
|
|
||||||
mCharacter->ExtendedUpdate(
|
mCharacter->ExtendedUpdate(
|
||||||
subDt,
|
subDt,
|
||||||
system->GetGravity(),
|
system->GetGravity(),
|
||||||
settings,
|
extSettings,
|
||||||
system->GetDefaultBroadPhaseLayerFilter(Layers::MOVING),
|
system->GetDefaultBroadPhaseLayerFilter(Layers::CHARACTER),
|
||||||
system->GetDefaultLayerFilter(Layers::MOVING),
|
system->GetDefaultLayerFilter(Layers::CHARACTER),
|
||||||
{},
|
{},
|
||||||
{},
|
{},
|
||||||
*mWorld->getTempAllocator()
|
*mWorld->getTempAllocator()
|
||||||
|
|
@ -411,8 +409,8 @@ void JoltPlayer::setSpacials(const Point3F& nPos, const Point3F& nSize)
|
||||||
if (mCharacter->SetShape(
|
if (mCharacter->SetShape(
|
||||||
shape,
|
shape,
|
||||||
maxPenetration,
|
maxPenetration,
|
||||||
system->GetDefaultBroadPhaseLayerFilter(Layers::MOVING),
|
system->GetDefaultBroadPhaseLayerFilter(Layers::CHARACTER),
|
||||||
system->GetDefaultLayerFilter(Layers::MOVING),
|
system->GetDefaultLayerFilter(Layers::CHARACTER),
|
||||||
body_filter,
|
body_filter,
|
||||||
shape_filter,
|
shape_filter,
|
||||||
*mWorld->getTempAllocator())
|
*mWorld->getTempAllocator())
|
||||||
|
|
@ -420,8 +418,6 @@ void JoltPlayer::setSpacials(const Point3F& nPos, const Point3F& nSize)
|
||||||
{
|
{
|
||||||
mCharacter->SetInnerBodyShape(inner_shape);
|
mCharacter->SetInnerBodyShape(inner_shape);
|
||||||
}
|
}
|
||||||
|
|
||||||
//mCharacter->SetPosition(joltCast(nPos));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void JoltPlayer::enableCollision()
|
void JoltPlayer::enableCollision()
|
||||||
|
|
@ -480,26 +476,51 @@ void JoltPlayer::OnAdjustBodyVelocity(const JPH::CharacterVirtual* inCharacter,
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool JoltPlayer::OnContactValidate(const JPH::CharacterVirtual* inCharacter, const JPH::BodyID& inBodyID2, const JPH::SubShapeID& inSubShapeID2)
|
||||||
|
{
|
||||||
|
if (inCharacter != mCharacter)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
JPH::BodyLockRead lock(mWorld->getPhysicsSystem()->GetBodyLockInterface(), inBodyID2);
|
||||||
|
if (lock.Succeeded() && lock.GetBody().IsSensor())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void JoltPlayer::OnContactAdded(const JPH::CharacterVirtual* inCharacter, const JPH::BodyID& inBodyID2, const JPH::SubShapeID& inSubShapeID2, JPH::RVec3Arg inContactPosition, JPH::Vec3Arg inContactNormal, JPH::CharacterContactSettings& ioSettings)
|
void JoltPlayer::OnContactAdded(const JPH::CharacterVirtual* inCharacter, const JPH::BodyID& inBodyID2, const JPH::SubShapeID& inSubShapeID2, JPH::RVec3Arg inContactPosition, JPH::Vec3Arg inContactNormal, JPH::CharacterContactSettings& ioSettings)
|
||||||
{
|
{
|
||||||
if (inCharacter == mCharacter)
|
if (inCharacter != mCharacter)
|
||||||
{
|
return;
|
||||||
JPH::CharacterVirtual::ContactKey c(inBodyID2, inSubShapeID2);
|
|
||||||
if (std::find(mActiveContacts.begin(), mActiveContacts.end(), c) != mActiveContacts.end())
|
|
||||||
AssertFatal(false, "JoltPlayer::OnContactAdded - Received a contact that should have been persisted");
|
|
||||||
|
|
||||||
mActiveContacts.push_back(c);
|
// Sensors are already rejected by OnContactValidate so this callback will
|
||||||
|
// never fire for them. No sensor check needed here.
|
||||||
|
JPH::CharacterVirtual::ContactKey c(inBodyID2, inSubShapeID2);
|
||||||
|
if (std::find(mActiveContacts.begin(), mActiveContacts.end(), c) != mActiveContacts.end())
|
||||||
|
{
|
||||||
|
Con::warnf("JoltPlayer::OnContactAdded - contact already present, skipping duplicate");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
mActiveContacts.push_back(c);
|
||||||
|
|
||||||
|
OnContactCommon(inCharacter, inBodyID2, inSubShapeID2, inContactPosition, inContactNormal, ioSettings);
|
||||||
}
|
}
|
||||||
|
|
||||||
void JoltPlayer::OnContactPersisted(const JPH::CharacterVirtual* inCharacter, const JPH::BodyID& inBodyID2, const JPH::SubShapeID& inSubShapeID2, JPH::RVec3Arg inContactPosition, JPH::Vec3Arg inContactNormal, JPH::CharacterContactSettings& ioSettings)
|
void JoltPlayer::OnContactPersisted(const JPH::CharacterVirtual* inCharacter, const JPH::BodyID& inBodyID2, const JPH::SubShapeID& inSubShapeID2, JPH::RVec3Arg inContactPosition, JPH::Vec3Arg inContactNormal, JPH::CharacterContactSettings& ioSettings)
|
||||||
{
|
{
|
||||||
if (inCharacter == mCharacter)
|
if (inCharacter != mCharacter)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Sensors are already rejected by OnContactValidate — this callback will
|
||||||
|
// not fire for them.
|
||||||
|
JPH::CharacterVirtual::ContactKey c(inBodyID2, inSubShapeID2);
|
||||||
|
if (std::find(mActiveContacts.begin(), mActiveContacts.end(), c) == mActiveContacts.end())
|
||||||
{
|
{
|
||||||
JPH::CharacterVirtual::ContactKey c(inBodyID2, inSubShapeID2);
|
Con::warnf("JoltPlayer::OnContactPersisted - contact not in set, inserting as new");
|
||||||
if (std::find(mActiveContacts.begin(), mActiveContacts.end(), c) == mActiveContacts.end())
|
mActiveContacts.push_back(c);
|
||||||
AssertFatal(false, "JoltPlayer::OnContactPersisted - Received a contact that should have been added instead");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
OnContactCommon(inCharacter, inBodyID2, inSubShapeID2, inContactPosition, inContactNormal, ioSettings);
|
||||||
}
|
}
|
||||||
|
|
||||||
void JoltPlayer::OnContactRemoved(const JPH::CharacterVirtual* inCharacter, const JPH::BodyID& inBodyID2, const JPH::SubShapeID& inSubShapeID2)
|
void JoltPlayer::OnContactRemoved(const JPH::CharacterVirtual* inCharacter, const JPH::BodyID& inBodyID2, const JPH::SubShapeID& inSubShapeID2)
|
||||||
|
|
@ -509,8 +530,11 @@ void JoltPlayer::OnContactRemoved(const JPH::CharacterVirtual* inCharacter, cons
|
||||||
JPH::CharacterVirtual::ContactKey c(inBodyID2, inSubShapeID2);
|
JPH::CharacterVirtual::ContactKey c(inBodyID2, inSubShapeID2);
|
||||||
ContactSet::iterator it = std::find(mActiveContacts.begin(), mActiveContacts.end(), c);
|
ContactSet::iterator it = std::find(mActiveContacts.begin(), mActiveContacts.end(), c);
|
||||||
if (it == mActiveContacts.end())
|
if (it == mActiveContacts.end())
|
||||||
AssertFatal(false, "JoltPlayer::OnContactRemoved - Attempted to remove a contact that was not added");
|
{
|
||||||
|
// Can occur when the contact was never added due to an earlier ordering anomaly.
|
||||||
|
Con::warnf("JoltPlayer::OnContactRemoved - contact not found, ignoring");
|
||||||
|
return;
|
||||||
|
}
|
||||||
mActiveContacts.erase(it);
|
mActiveContacts.erase(it);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -537,4 +561,3 @@ void JoltPlayer::OnContactSolve(const JPH::CharacterVirtual* inCharacter, const
|
||||||
if (!mAllowSliding && inContactVelocity.IsNearZero() && !inCharacter->IsSlopeTooSteep(inContactNormal))
|
if (!mAllowSliding && inContactVelocity.IsNearZero() && !inCharacter->IsSlopeTooSteep(inContactNormal))
|
||||||
ioNewCharacterVelocity = JPH::Vec3::sZero();
|
ioNewCharacterVelocity = JPH::Vec3::sZero();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -56,11 +56,11 @@ public:
|
||||||
|
|
||||||
// Physics Player.
|
// Physics Player.
|
||||||
void init(const char* type,
|
void init(const char* type,
|
||||||
const Point3F& size,
|
const Point3F& size,
|
||||||
F32 runSurfaceCos,
|
F32 runSurfaceCos,
|
||||||
F32 stepHeight,
|
F32 stepHeight,
|
||||||
SceneObject* obj,
|
SceneObject* obj,
|
||||||
PhysicsWorld* world) override;
|
PhysicsWorld* world) override;
|
||||||
|
|
||||||
void preUpdate(F32 dt);
|
void preUpdate(F32 dt);
|
||||||
|
|
||||||
|
|
@ -88,6 +88,10 @@ public: // contact listener
|
||||||
/// Callback to adjust the velocity of a body as seen by the character. Can be adjusted to e.g. implement a conveyor belt or an inertial dampener system of a sci-fi space ship.
|
/// Callback to adjust the velocity of a body as seen by the character. Can be adjusted to e.g. implement a conveyor belt or an inertial dampener system of a sci-fi space ship.
|
||||||
virtual void OnAdjustBodyVelocity(const JPH::CharacterVirtual* inCharacter, const JPH::Body& inBody2, JPH::Vec3& ioLinearVelocity, JPH::Vec3& ioAngularVelocity) override;
|
virtual void OnAdjustBodyVelocity(const JPH::CharacterVirtual* inCharacter, const JPH::Body& inBody2, JPH::Vec3& ioLinearVelocity, JPH::Vec3& ioAngularVelocity) override;
|
||||||
|
|
||||||
|
// Called to check if the character can collide with a body at all. Return false to discard the contact
|
||||||
|
// before it reaches the constraint solver, stair walking, or stick-to-floor algorithms.
|
||||||
|
virtual bool OnContactValidate(const JPH::CharacterVirtual* inCharacter, const JPH::BodyID& inBodyID2, const JPH::SubShapeID& inSubShapeID2) override;
|
||||||
|
|
||||||
// Called whenever the character collides with a body.
|
// Called whenever the character collides with a body.
|
||||||
virtual void OnContactAdded(const JPH::CharacterVirtual* inCharacter, const JPH::BodyID& inBodyID2, const JPH::SubShapeID& inSubShapeID2, JPH::RVec3Arg inContactPosition, JPH::Vec3Arg inContactNormal, JPH::CharacterContactSettings& ioSettings) override;
|
virtual void OnContactAdded(const JPH::CharacterVirtual* inCharacter, const JPH::BodyID& inBodyID2, const JPH::SubShapeID& inSubShapeID2, JPH::RVec3Arg inContactPosition, JPH::Vec3Arg inContactNormal, JPH::CharacterContactSettings& ioSettings) override;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -123,6 +123,15 @@ void JoltWorld::destroyWorld()
|
||||||
mDestroyPending.store(true, std::memory_order_release);
|
mDestroyPending.store(true, std::memory_order_release);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class SensorExcludeFilter final : public JPH::BodyFilter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
bool ShouldCollideLocked(const JPH::Body& inBody) const override
|
||||||
|
{
|
||||||
|
return !inBody.IsSensor();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
bool JoltWorld::castRay(const Point3F& startPnt, const Point3F& endPnt, RayInfo* ri, const Point3F& impulse)
|
bool JoltWorld::castRay(const Point3F& startPnt, const Point3F& endPnt, RayInfo* ri, const Point3F& impulse)
|
||||||
{
|
{
|
||||||
JPH::Vec3 startV(startPnt.x, startPnt.y, startPnt.z);
|
JPH::Vec3 startV(startPnt.x, startPnt.y, startPnt.z);
|
||||||
|
|
@ -134,10 +143,12 @@ bool JoltWorld::castRay(const Point3F& startPnt, const Point3F& endPnt, RayInfo*
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
JPH::RRayCast ray(startV, dir);
|
JPH::RRayCast ray(startV, dir);
|
||||||
|
|
||||||
JPH::RayCastResult result;
|
JPH::RayCastResult result;
|
||||||
|
|
||||||
if (!mPhysicsSystem.GetNarrowPhaseQuery().CastRay(ray, result))
|
// Exclude sensor bodies: they are non-solid trigger volumes and must not
|
||||||
|
// appear as solid hits to projectiles or other physics ray casts.
|
||||||
|
SensorExcludeFilter sensorFilter;
|
||||||
|
if (!mPhysicsSystem.GetNarrowPhaseQuery().CastRay(ray, result, {}, {}, sensorFilter))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
JPH::BodyLockRead lock(
|
JPH::BodyLockRead lock(
|
||||||
|
|
@ -154,21 +165,23 @@ bool JoltWorld::castRay(const Point3F& startPnt, const Point3F& endPnt, RayInfo*
|
||||||
|
|
||||||
JPH::RVec3 hitPos = ray.GetPointOnRay(result.mFraction);
|
JPH::RVec3 hitPos = ray.GetPointOnRay(result.mFraction);
|
||||||
|
|
||||||
JPH::Vec3 normal = body.GetWorldSpaceSurfaceNormal( result.mSubShapeID2, hitPos );
|
JPH::Vec3 normal = body.GetWorldSpaceSurfaceNormal(result.mSubShapeID2, hitPos);
|
||||||
|
|
||||||
if (!body.GetUserData())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (ri)
|
if (ri)
|
||||||
{
|
{
|
||||||
ri->point.set(joltCast(hitPos));
|
ri->point.set(joltCast(hitPos));
|
||||||
ri->normal.set(joltCast(normal));
|
ri->normal.set(joltCast(normal));
|
||||||
|
|
||||||
ri->distance = result.mFraction * rayLength;
|
ri->distance = result.mFraction * rayLength;
|
||||||
// SceneObject mapping
|
|
||||||
PhysicsUserData* userData = PhysicsUserData::cast((void*)body.GetUserData());
|
// A body with no UserData means no associated SceneObject (e.g. internal
|
||||||
ri->object = userData->getObject();
|
// collision geometry). Return the hit but leave ri->object as null rather
|
||||||
ri->material = NULL;
|
// than hiding the hit entirely.
|
||||||
|
PhysicsUserData* userData = body.GetUserData()
|
||||||
|
? PhysicsUserData::cast((void*)body.GetUserData())
|
||||||
|
: nullptr;
|
||||||
|
|
||||||
|
ri->object = userData ? userData->getObject() : nullptr;
|
||||||
|
ri->material = nullptr;
|
||||||
ri->face = 0;
|
ri->face = 0;
|
||||||
ri->faceDot = 0;
|
ri->faceDot = 0;
|
||||||
}
|
}
|
||||||
|
|
@ -236,8 +249,9 @@ void JoltWorld::explosion(const Point3F& pos, F32 radius, F32 forceMagnitude)
|
||||||
|
|
||||||
JPH::Body& body = lock.GetBody();
|
JPH::Body& body = lock.GetBody();
|
||||||
|
|
||||||
// Ignore static/kinematic bodies
|
// Skip sensors and non-dynamic bodies — sensors are trigger volumes and
|
||||||
if (!body.IsDynamic())
|
// static/kinematic bodies don't respond to impulses.
|
||||||
|
if (body.IsSensor() || !body.IsDynamic())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
JPH::Vec3 bodyPos = body.GetCenterOfMassPosition();
|
JPH::Vec3 bodyPos = body.GetCenterOfMassPosition();
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue