// 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(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(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 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(w); SphereShape ball(r); ball.SetEmbedded(); AllHitCollisionCollector 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(w); float half = (y1 - y0) * 0.5f; CylinderShape body(half, r, 0.0f); body.SetEmbedded(); AllHitCollisionCollector 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(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 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 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; }