142 lines
6.6 KiB
C++
142 lines
6.6 KiB
C++
// jph_queries.inl - what is there: a ray, the top a body could step onto, overlaps, a push out
|
|
|
|
// a ray from o along d (its length the reach) against the layers in mask; the body id or -1, and
|
|
// out[7] the fraction, the point and the normal
|
|
JPH_SHIM int jph_ray(void *w, float ox, float oy, float oz, float dx, float dy, float dz, int mask, float *out) {
|
|
World *ww = static_cast<World *>(w);
|
|
RRayCast ray{ RVec3(ox, oy, oz), Vec3(dx, dy, dz) };
|
|
RayCastResult hit;
|
|
MaskFilter f(mask);
|
|
if (!ww->sys.GetNarrowPhaseQuery().CastRay(ray, hit, {}, f)) return -1;
|
|
RVec3 p = ray.GetPointOnRay(hit.mFraction);
|
|
Vec3 n = Vec3::sAxisY();
|
|
BodyLockRead lock(ww->sys.GetBodyLockInterface(), hit.mBodyID);
|
|
if (lock.Succeeded()) n = lock.GetBody().GetWorldSpaceSurfaceNormal(hit.mSubShapeID2, p);
|
|
float vals[7] = { hit.mFraction, float(p.GetX()), float(p.GetY()), float(p.GetZ()), n.GetX(), n.GetY(), n.GetZ() };
|
|
for (int i = 0; i < 7; i++) out[i] = vals[i];
|
|
return int(hit.mBodyID.GetIndexAndSequenceNumber());
|
|
}
|
|
|
|
// a ball of radius r let down from (x, from_y, z) at most `depth`: the height of what it lands
|
|
// on, or -FLT_MAX when it lands on nothing in mask
|
|
JPH_SHIM float jph_top_at(void *w, float x, float z, float r, float from_y, float depth, int mask) {
|
|
World *ww = static_cast<World *>(w);
|
|
SphereShape ball(r);
|
|
ball.SetEmbedded();
|
|
RShapeCast cast = RShapeCast::sFromWorldTransform(&ball, Vec3::sOne(), RMat44::sTranslation(RVec3(x, from_y, z)),
|
|
Vec3(0.0f, -depth, 0.0f));
|
|
ClosestHitCollisionCollector<CastShapeCollector> hit;
|
|
MaskFilter f(mask);
|
|
ww->sys.GetNarrowPhaseQuery().CastShape(cast, ShapeCastSettings(), RVec3::sZero(), hit, {}, f);
|
|
if (!hit.HadHit()) return -FLT_MAX;
|
|
return float(hit.mHit.mContactPointOn2.GetY());
|
|
}
|
|
|
|
// the bodies (in mask) a ball at (x, y, z) touches, up to cap into ids; returns how many
|
|
JPH_SHIM int jph_overlap(void *w, float x, float y, float z, float r, int mask, int *ids, int cap) {
|
|
World *ww = static_cast<World *>(w);
|
|
SphereShape ball(r);
|
|
ball.SetEmbedded();
|
|
AllHitCollisionCollector<CollideShapeCollector> hits;
|
|
MaskFilter f(mask);
|
|
ww->sys.GetNarrowPhaseQuery().CollideShape(&ball, Vec3::sOne(), RMat44::sTranslation(RVec3(x, y, z)),
|
|
CollideShapeSettings(), RVec3::sZero(), hits, {}, f);
|
|
hits.Sort();
|
|
int n = 0;
|
|
for (const CollideShapeResult &h : hits.mHits) {
|
|
int id = int(h.mBodyID2.GetIndexAndSequenceNumber());
|
|
bool seen = false;
|
|
for (int i = 0; i < n; i++) seen = seen || ids[i] == id;
|
|
if (!seen && n < cap) ids[n++] = id;
|
|
}
|
|
return n;
|
|
}
|
|
|
|
// an upright body r wide, from y0 to y1, at (x, z): how far to move it in x and z to stand clear
|
|
// of what it overlaps in mask, into out[2]; returns how many things were in the way
|
|
JPH_SHIM int jph_push(void *w, float x, float z, float r, float y0, float y1, int mask, float *out) {
|
|
World *ww = static_cast<World *>(w);
|
|
float half = (y1 - y0) * 0.5f;
|
|
CylinderShape body(half, r, 0.0f);
|
|
body.SetEmbedded();
|
|
AllHitCollisionCollector<CollideShapeCollector> hits;
|
|
MaskFilter f(mask);
|
|
CollideShapeSettings cs;
|
|
cs.mBackFaceMode = EBackFaceMode::CollideWithBackFaces;
|
|
ww->sys.GetNarrowPhaseQuery().CollideShape(&body, Vec3::sOne(), RMat44::sTranslation(RVec3(x, y0 + half, z)),
|
|
cs, RVec3::sZero(), hits, {}, f);
|
|
hits.Sort();
|
|
// a thing wholly below the feet (stood on) or above the head is not in the way; anything else
|
|
// pushes out sideways - along Jolt's own axis when the contact is side-on, else straight away
|
|
// from the thing's middle, by as much as the two overlap across (exact for an upright post)
|
|
float px = 0.0f, pz = 0.0f;
|
|
int n = 0;
|
|
for (const CollideShapeResult &h : hits.mHits) {
|
|
BodyLockRead lock(ww->sys.GetBodyLockInterface(), h.mBodyID2);
|
|
if (!lock.Succeeded()) continue;
|
|
AABox bb = lock.GetBody().GetWorldSpaceBounds();
|
|
if (float(bb.mMax.GetY()) <= y0 + 1.0e-3f || float(bb.mMin.GetY()) >= y1 - 1.0e-3f) continue;
|
|
Vec3 a = h.mPenetrationAxis.NormalizedOr(Vec3::sAxisY());
|
|
Vec3 flat(a.GetX(), 0.0f, a.GetZ());
|
|
float dx, dz;
|
|
if (flat.Length() >= 0.5f && h.mPenetrationDepth > 1.0e-4f) {
|
|
flat = flat.Normalized() * h.mPenetrationDepth;
|
|
dx = -flat.GetX();
|
|
dz = -flat.GetZ();
|
|
} else {
|
|
Vec3 c = bb.GetCenter();
|
|
float ex = x - float(c.GetX()), ez = z - float(c.GetZ());
|
|
float d = std::sqrt(ex * ex + ez * ez);
|
|
float half = std::max(float(bb.GetExtent().GetX()), float(bb.GetExtent().GetZ()));
|
|
float push = half + r - d;
|
|
if (push <= 1.0e-4f) continue;
|
|
float ux = 1.0f, uz = 0.0f;
|
|
if (d > 1.0e-6f) { ux = ex / d; uz = ez / d; }
|
|
dx = ux * push;
|
|
dz = uz * push;
|
|
}
|
|
px += dx;
|
|
pz += dz;
|
|
n++;
|
|
}
|
|
out[0] = px;
|
|
out[1] = pz;
|
|
return n;
|
|
}
|
|
|
|
// the highest top under an upright body r wide at (x, z) that a foot at `feet` could stand on or
|
|
// step up to: a thing whose top is above feet + reach is a wall and is left out, whatever else is
|
|
// there. Each candidate's surface is found by letting a ball r across down onto it alone.
|
|
// -FLT_MAX when there is nothing.
|
|
JPH_SHIM float jph_step_top(void *w, float x, float z, float r, float feet, float reach, int mask) {
|
|
World *ww = static_cast<World *>(w);
|
|
float limit = feet + reach;
|
|
float y0 = feet - 1.0f, y1 = limit + 0.05f;
|
|
float half = (y1 - y0) * 0.5f;
|
|
CylinderShape column(half, r, 0.0f);
|
|
column.SetEmbedded();
|
|
AllHitCollisionCollector<CollideShapeCollector> hits;
|
|
MaskFilter f(mask);
|
|
ww->sys.GetNarrowPhaseQuery().CollideShape(&column, Vec3::sOne(), RMat44::sTranslation(RVec3(x, y0 + half, z)),
|
|
CollideShapeSettings(), RVec3::sZero(), hits, {}, f);
|
|
hits.Sort();
|
|
float best = -FLT_MAX;
|
|
SphereShape ball(r);
|
|
ball.SetEmbedded();
|
|
float from = limit + r + 0.01f;
|
|
RShapeCast cast = RShapeCast::sFromWorldTransform(&ball, Vec3::sOne(), RMat44::sTranslation(RVec3(x, from, z)),
|
|
Vec3(0.0f, y0 - from, 0.0f));
|
|
for (const CollideShapeResult &h : hits.mHits) {
|
|
BodyLockRead lock(ww->sys.GetBodyLockInterface(), h.mBodyID2);
|
|
if (!lock.Succeeded()) continue;
|
|
const Body &b = lock.GetBody();
|
|
float top = float(b.GetWorldSpaceBounds().mMax.GetY());
|
|
if (top > limit + 1.0e-4f) continue;
|
|
ClosestHitCollisionCollector<CastShapeCollector> c;
|
|
b.GetTransformedShape().CastShape(cast, ShapeCastSettings(), RVec3::sZero(), c);
|
|
float y = c.HadHit() ? float(c.mHit.mContactPointOn2.GetY()) : top;
|
|
if (y > top) y = top;
|
|
if (y > best) best = y;
|
|
}
|
|
return best;
|
|
}
|