127 if (!std::isfinite(
x) || !std::isfinite(
y) || !std::isfinite(
z))
128 throw eve::Exception(
"DistanceField3D.sample: coordinates must be finite");
129 const float gx = (
x - originX_) / cellSize_;
130 const float gy = (
y - originY_) / cellSize_;
131 const float gz = (
z - originZ_) / cellSize_;
132 if (gx < 0.f || gy < 0.f || gz < 0.f || gx > width_ - 1.f || gy > height_ - 1.f ||
134 return outsideDistance_;
136 const int x0 = std::min(
static_cast<int>(std::floor(gx)), width_ - 2);
137 const int y0 = std::min(
static_cast<int>(std::floor(gy)), height_ - 2);
138 const int z0 = std::min(
static_cast<int>(std::floor(gz)), depth_ - 2);
139 const float tx = std::clamp(gx - x0, 0.f, 1.f);
140 const float ty = std::clamp(gy - y0, 0.f, 1.f);
141 const float tz = std::clamp(gz - z0, 0.f, 1.f);
142 auto mix = [](
float a,
float b,
float t) {
return a + (
b -
a) *
t; };
146 const float c11 = mix(
getDistance(x0, y0 + 1, z0 + 1),
148 return mix(mix(c00, c10, ty), mix(c01, c11, ty), tz);
314 float radius,
float deltaX,
float deltaY,
float deltaZ) {
315 if (!std::isfinite(
ax) || !std::isfinite(
ay) || !std::isfinite(
az) ||
316 !std::isfinite(
bx) || !std::isfinite(
by) || !std::isfinite(
bz) ||
317 !(
radius >= 0.f) || !std::isfinite(
radius) || !std::isfinite(deltaX) ||
318 !std::isfinite(deltaY) || !std::isfinite(deltaZ))
320 "DistanceField3D.castCapsule: coordinates, radius, and delta must be finite; radius >= 0");
321 const float travel = std::hypot(deltaX, deltaY, deltaZ);
322 float hitX, hitY, hitZ;
323 const float initial =
325 if (initial <= 0.f) {
326 setCastResult(0.f, travel, hitX, hitY, hitZ, initial,
radius,
true);
330 castDistance_ = travel;
331 castStartedInside_ =
false;
332 if (travel <= 1e-7f)
return false;
334 const int steps = queryIntervals(travel, cellSize_ * 0.25,
"DistanceField3D.castCapsule");
336 for (
int i = 1; i <=
steps; ++i) {
337 const float fraction =
static_cast<float>(i) /
static_cast<float>(
steps);
338 float closestX, closestY, closestZ;
339 const float clearance = capsuleClearance(
340 ax + deltaX * fraction,
ay + deltaY * fraction,
az + deltaZ * fraction,
341 bx + deltaX * fraction,
by + deltaY * fraction,
bz + deltaZ * fraction,
radius,
342 closestX, closestY, closestZ);
343 if (clearance > 0.f) {
347 float low =
previous, high = fraction;
348 for (
int iteration = 0; iteration < 14; ++iteration) {
349 const float middle = 0.5f * (low + high);
350 float ignoredX, ignoredY, ignoredZ;
351 if (capsuleClearance(
352 ax + deltaX * middle,
ay + deltaY * middle,
az + deltaZ * middle,
353 bx + deltaX * middle,
by + deltaY * middle,
bz + deltaZ * middle,
radius,
354 ignoredX, ignoredY, ignoredZ) <= 0.f)
359 const float finalClearance = capsuleClearance(
360 ax + deltaX * high,
ay + deltaY * high,
az + deltaZ * high,
361 bx + deltaX * high,
by + deltaY * high,
bz + deltaZ * high,
radius, hitX, hitY,
363 setCastResult(high, travel, hitX, hitY, hitZ, finalClearance,
radius,
false);
408 float radius,
float deltaX,
float deltaY,
float deltaZ) {
411 if (!std::isfinite(deltaX) || !std::isfinite(deltaY) || !std::isfinite(deltaZ))
412 throw eve::Exception(
"DistanceField3D.moveCapsule: delta must be finite");
417 auto dot = [](Vec3
a, Vec3
b) {
return a.x *
b.x +
a.y *
b.y +
a.z *
b.z; };
420 constexpr int maxIterations = 6;
421 constexpr int maxPlanes = 4;
424 const float skin = std::max(moverSkinWidth_, cellSize_ * 1e-6f);
426 Vec3 remaining{deltaX, deltaY, deltaZ};
429 bool constrained =
false;
430 moverIterations_ = 0;
434 moverGroundDot_ = -1.f;
435 moverGrounded_ =
false;
436 moverHitWall_ =
false;
437 auto recordNormal = [&](Vec3
normal) {
443 moverGroundDot_ = std::max(moverGroundDot_, upDot);
444 if (upDot < moverSlopeCos_) moverHitWall_ =
true;
447 for (
int iteration = 0; iteration < maxIterations; ++iteration) {
448 moverIterations_ = iteration + 1;
454 if (
checkCapsule(currentAx, currentAy, currentAz, currentBx, currentBy, currentBz,
456 const float recovery = -collisionDistance_ + skin;
457 Vec3
normal{normalX_, normalY_, normalZ_};
463 if (planeCount < maxPlanes)
planes[planeCount++] =
normal;
467 const float travel =
length(remaining);
468 if (travel <= 1e-6f)
break;
469 if (!
castCapsule(currentAx, currentAy, currentAz, currentBx, currentBy, currentBz,
radius,
470 remaining.x, remaining.y, remaining.z)) {
479 const Vec3
normal{normalX_, normalY_, normalZ_};
481 if (planeCount < maxPlanes)
planes[planeCount++] =
normal;
483 const float safeFraction = std::max(0.f, castFraction_ - skin / travel);
484 position.x += remaining.x * safeFraction;
485 position.y += remaining.y * safeFraction;
486 position.z += remaining.z * safeFraction;
487 remaining.x *= 1.f - safeFraction;
488 remaining.y *= 1.f - safeFraction;
489 remaining.z *= 1.f - safeFraction;
493 for (
int pass = 0; pass < planeCount; ++pass) {
494 for (
int plane = 0; plane < planeCount; ++plane) {
495 const float into = dot(remaining,
planes[plane]);
497 remaining.x -=
planes[plane].x * into;
498 remaining.y -=
planes[plane].y * into;
499 remaining.z -=
planes[plane].z * into;
505 moverGrounded_ = moverGroundDot_ >= moverSlopeCos_;
506 if (!moverGrounded_ && moverGroundSnap_ > 0.f) {
507 const Vec3 snap{-moverUpX_ * moverGroundSnap_, -moverUpY_ * moverGroundSnap_,
508 -moverUpZ_ * moverGroundSnap_};
511 !castStartedInside_) {
512 const Vec3 snapNormal{normalX_, normalY_, normalZ_};
513 const float groundDot = snapNormal.x * moverUpX_ + snapNormal.y * moverUpY_ +
514 snapNormal.z * moverUpZ_;
515 if (groundDot >= moverSlopeCos_) {
516 const float safeFraction =
517 std::max(0.f, castFraction_ - skin / moverGroundSnap_);
518 position.x += snap.x * safeFraction;
519 position.y += snap.y * safeFraction;
520 position.z += snap.z * safeFraction;
521 recordNormal(snapNormal);
522 moverGrounded_ =
true;
532 const Vec3 requested{deltaX, deltaY, deltaZ};
533 const float requestedUp =
534 requested.x * moverUpX_ + requested.y * moverUpY_ + requested.z * moverUpZ_;
535 const Vec3 lateral{requested.x - moverUpX_ * requestedUp,
536 requested.y - moverUpY_ * requestedUp,
537 requested.z - moverUpZ_ * requestedUp};
538 const float lateralLength =
length(lateral);
539 if (!moverStepping_ && moverStepHeight_ > 0.f && moverHitWall_ && lateralLength > 1e-6f) {
543 bool grounded, hitWall;
545 const MoverState direct{moverDeltaX_, moverDeltaY_, moverDeltaZ_,
546 moverNormalX_, moverNormalY_, moverNormalZ_,
547 moverGroundDot_, moverIterations_, moverGrounded_,
549 auto restore = [&](
const MoverState &
state) {
550 moverDeltaX_ =
state.dx;
551 moverDeltaY_ =
state.dy;
552 moverDeltaZ_ =
state.dz;
553 moverNormalX_ =
state.nx;
554 moverNormalY_ =
state.ny;
555 moverNormalZ_ =
state.nz;
556 moverGroundDot_ =
state.groundDot;
557 moverIterations_ =
state.iterations;
558 moverGrounded_ =
state.grounded;
559 moverHitWall_ =
state.hitWall;
561 const Vec3
up{moverUpX_ * moverStepHeight_, moverUpY_ * moverStepHeight_,
562 moverUpZ_ * moverStepHeight_};
566 moverStepping_ =
true;
571 moverStepping_ =
false;
574 moverStepping_ =
false;
576 Vec3 candidate{
up.x + moverDeltaX_,
up.y + moverDeltaY_,
up.z + moverDeltaZ_};
577 const int candidateIterations = moverIterations_;
578 const bool candidateHitWall = moverHitWall_;
579 const float downDistance = moverStepHeight_ + moverGroundSnap_ + skin;
580 const Vec3 down{-moverUpX_ * downDistance, -moverUpY_ * downDistance,
581 -moverUpZ_ * downDistance};
583 bx + candidate.x,
by + candidate.y,
bz + candidate.z,
radius, down.x,
585 !castStartedInside_) {
586 const Vec3 landingNormal{normalX_, normalY_, normalZ_};
587 const float landingDot = landingNormal.x * moverUpX_ +
588 landingNormal.y * moverUpY_ +
589 landingNormal.z * moverUpZ_;
590 if (landingDot >= moverSlopeCos_) {
591 const float safeFraction =
592 std::max(0.f, castFraction_ - skin / downDistance);
593 candidate.x += down.x * safeFraction;
594 candidate.y += down.y * safeFraction;
595 candidate.z += down.z * safeFraction;
596 const float directProgress =
597 (direct.dx * lateral.x + direct.dy * lateral.y + direct.dz * lateral.z) /
599 const float candidateProgress =
600 (candidate.x * lateral.x + candidate.y * lateral.y +
601 candidate.z * lateral.z) /
603 if (candidateProgress > directProgress + std::max(1e-4f, skin)) {
604 moverDeltaX_ = candidate.x;
605 moverDeltaY_ = candidate.y;
606 moverDeltaZ_ = candidate.z;
607 moverNormalX_ = landingNormal.x;
608 moverNormalY_ = landingNormal.y;
609 moverNormalZ_ = landingNormal.z;
610 moverGroundDot_ = landingDot;
611 moverGrounded_ =
true;
612 moverHitWall_ = candidateHitWall;
613 moverIterations_ = candidateIterations + 2;