feat: add component and setComponent

This commit is contained in:
dualfroz committed 2026-09-06 21:55:17 +02:00
1 parent e722d2249b
commit 500fcaffd3
1 file changed
+156
+156
View File
@@ -0,0 +1,156 @@
#include "vf/Collide.h"
#include <cmath>
#include <limits>
namespace vf {
namespace {
constexpr float gatherPadding = 1.0e-3f;
constexpr float parallelMotion = 1.0e-12f;
float component(const Vec3& v, int a) { return (&v.x)[a]; }
void setComponent(Vec3& v, int a, float value) { (&v.x)[a] = value; }
int axisIndex(ContactAxis axis) {
switch (axis) {
case ContactAxis::X: return 0;
case ContactAxis::Y: return 1;
case ContactAxis::Z: return 2;
default: return -1;
}
}
ContactAxis axisFromIndex(int a) {
switch (a) {
case 0: return ContactAxis::X;
case 1: return ContactAxis::Y;
default: return ContactAxis::Z;
}
}
int floorToCell(float value) { return static_cast<int>(std::floor(value)); }
bool motionIsIdle(const Vec3& motion) {
return std::fabs(motion.x) < parallelMotion && std::fabs(motion.y) < parallelMotion &&
std::fabs(motion.z) < parallelMotion;
}
SweptBox unitCellBox(const Vec3i& cell, float loY, float hiY, float inset) {
float x = static_cast<float>(cell.x);
float y = static_cast<float>(cell.y);
float z = static_cast<float>(cell.z);
return SweptBox{{x + inset, y + loY, z + inset}, {x + 1.0f - inset, y + hiY, z + 1.0f - inset}};
}
} // namespace
SweptBox makeBoxAtFeet(const Vec3& feetCentre, float width, float height) {
float half = width * 0.5f;
return SweptBox{{feetCentre.x - half, feetCentre.y, feetCentre.z - half},
{feetCentre.x + half, feetCentre.y + height, feetCentre.z + half}};
}
SweptBox translatedBox(const SweptBox& box, const Vec3& delta) {
return SweptBox{box.min + delta, box.max + delta};
}
SweptBox inflatedBox(const SweptBox& box, float amount) {
Vec3 pad{amount, amount, amount};
return SweptBox{box.min - pad, box.max + pad};
}
SweptBox motionEnvelope(const SweptBox& box, const Vec3& motion) {
SweptBox moved = translatedBox(box, motion);
SweptBox out;
for (int a = 0; a < 3; ++a) {
setComponent(out.min, a, std::fmin(component(box.min, a), component(moved.min, a)));
setComponent(out.max, a, std::fmax(component(box.max, a), component(moved.max, a)));
}
return out;
}
bool boxesOverlap(const SweptBox& a, const SweptBox& b) {
return a.min.x < b.max.x && a.max.x > b.min.x && a.min.y < b.max.y && a.max.y > b.min.y &&
a.min.z < b.max.z && a.max.z > b.min.z;
}
bool boxesTouchOrOverlap(const SweptBox& a, const SweptBox& b, float tolerance) {
return boxesOverlap(inflatedBox(a, tolerance), b);
}
SweptBox aabbToSweptBox(const Aabb& box) { return SweptBox{box.min, box.max}; }
Aabb sweptBoxToAabb(const SweptBox& box) { return Aabb{box.min, box.max}; }
CollisionShape collisionShapeOf(Block block) {
switch (block) {
case Block::Slab: return CollisionShape::SlabBottom;
case Block::Stairs: return CollisionShape::Stairs;
case Block::Fence: return CollisionShape::Fence;
case Block::Ladder: return CollisionShape::Ladder;
case Block::Vine: return CollisionShape::Vine;
case Block::SnowLayer: return CollisionShape::Carpet;
default: return isSolid(block) ? CollisionShape::Full : CollisionShape::None;
}
}
bool isClimbableShape(CollisionShape shape) {
return shape == CollisionShape::Ladder || shape == CollisionShape::Vine;
}
bool shapeBlocksMovement(CollisionShape shape) {
switch (shape) {
case CollisionShape::None:
case CollisionShape::Ladder:
case CollisionShape::Vine:
return false;
default:
return true;
}
}
ShapeBoxes shapeBoxesAt(CollisionShape shape, const Vec3i& cell) {
ShapeBoxes out;
switch (shape) {
case CollisionShape::Full:
out.boxes[out.count++] = unitCellBox(cell, 0.0f, 1.0f, 0.0f);
break;
case CollisionShape::SlabBottom:
out.boxes[out.count++] = unitCellBox(cell, 0.0f, 0.5f, 0.0f);
break;
case CollisionShape::SlabTop:
out.boxes[out.count++] = unitCellBox(cell, 0.5f, 1.0f, 0.0f);
break;
case CollisionShape::Stairs:
// Lower slab plus the upper step over the +X half of the cell.
out.boxes[out.count++] = unitCellBox(cell, 0.0f, 0.5f, 0.0f);
{
SweptBox upper = unitCellBox(cell, 0.5f, 1.0f, 0.0f);
upper.min.x = static_cast<float>(cell.x) + 0.5f;
out.boxes[out.count++] = upper;
}
break;
case CollisionShape::Fence:
// The post reaches above its own cell, which is why a body cannot
// hop a fence the way it hops a full block.
out.boxes[out.count++] = unitCellBox(cell, 0.0f, fencePostHeight, 0.375f);
break;
case CollisionShape::Carpet:
out.boxes[out.count++] = unitCellBox(cell, 0.0f, carpetHeight, 0.0f);
break;
case CollisionShape::None:
case CollisionShape::Ladder:
case CollisionShape::Vine:
break;
}
return out;
}
SweepHit sweepBoxAgainstBox(const SweptBox& mover, const Vec3& motion, const SweptBox& obstacle) {
SweepHit hit;
Vec3 moverSize = mover.size();
SweptBox expanded{obstacle.min - moverSize, obstacle.max};
Vec3 point = mover.min;