From 500fcaffd3e23e2120692ba9bf91d727e2005e7b Mon Sep 17 00:00:00 2001 From: DualFroz Date: Sun, 6 Sep 2026 21:55:17 +0200 Subject: [PATCH] feat: add component and setComponent --- core/src/Collide.cpp | 156 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 156 insertions(+) create mode 100644 core/src/Collide.cpp diff --git a/core/src/Collide.cpp b/core/src/Collide.cpp new file mode 100644 index 0000000..075a026 --- /dev/null +++ b/core/src/Collide.cpp @@ -0,0 +1,156 @@ +#include "vf/Collide.h" + +#include +#include + +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(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(cell.x); + float y = static_cast(cell.y); + float z = static_cast(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(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; +