21#include <glm/common.hpp>
22#include <glm/geometric.hpp>
53 float tMax = maxDistance;
54 glm::vec3 hitNormal{0.0f};
56 for (
int axis = 0; axis < 3; ++axis) {
58 if (origin[axis] < box.
min[axis] || origin[axis] > box.
max[axis]) {
64 const float invDir = 1.0f / direction[axis];
65 float t1 = (box.
min[axis] - origin[axis]) * invDir;
66 float t2 = (box.
max[axis] - origin[axis]) * invDir;
67 glm::vec3 axisNormal{0.0f};
68 axisNormal[axis] = (invDir >= 0.0f) ? -1.0f : 1.0f;
72 axisNormal = -axisNormal;
77 hitNormal = axisNormal;
80 tMax = std::min(tMax, t2);
86 if (tMin < 0.0f || tMin > maxDistance) {
91 outNormal = hitNormal;
101 glm::vec3& outNormal)
104 const float ox = origin.x - cyl.
base.x;
105 const float oz = origin.z - cyl.
base.z;
106 const float dx = direction.x;
107 const float dz = direction.z;
109 const float a = dx * dx + dz * dz;
110 const float b = 2.0f * (ox * dx + oz * dz);
111 const float c = ox * ox + oz * oz - cyl.
radius * cyl.
radius;
113 float tSide = maxDistance + 1.0f;
114 glm::vec3 sideNormal{0.0f};
117 const float disc = b * b - 4.0f * a * c;
119 const float t = (-b - std::sqrt(disc)) / (2.0f * a);
120 if (t >= 0.0f && t < maxDistance) {
122 const float hitY = origin.y + direction.y * t;
125 sideNormal = glm::vec3(
126 origin.x + direction.x * t - cyl.
base.x, 0.0f, origin.z + direction.z * t - cyl.
base.z);
127 const float len = glm::length(sideNormal);
131 sideNormal = glm::vec3(1.0f, 0.0f, 0.0f);
138 float tCap = maxDistance + 1.0f;
139 glm::vec3 capNormal{0.0f};
143 float t = (cyl.
base.y - origin.y) / direction.y;
144 if (t >= 0.0f && t < maxDistance) {
145 float hx = origin.x + direction.x * t - cyl.
base.x;
146 float hz = origin.z + direction.z * t - cyl.
base.z;
147 if (hx * hx + hz * hz <= cyl.
radius * cyl.
radius && t < tCap) {
149 capNormal = glm::vec3(0.0f, -1.0f, 0.0f);
153 t = (cyl.
base.y + cyl.
height - origin.y) / direction.y;
154 if (t >= 0.0f && t < maxDistance) {
155 float hx = origin.x + direction.x * t - cyl.
base.x;
156 float hz = origin.z + direction.z * t - cyl.
base.z;
157 if (hx * hx + hz * hz <= cyl.
radius * cyl.
radius && t < tCap) {
159 capNormal = glm::vec3(0.0f, 1.0f, 0.0f);
164 float best = maxDistance + 1.0f;
165 glm::vec3 bestN{0.0f};
175 if (best > maxDistance)
189 glm::vec3& outNormal)
191 const glm::vec3 oc = origin - sph.
center;
192 const float a = glm::dot(direction, direction);
195 const float b = 2.0f * glm::dot(oc, direction);
196 const float c = glm::dot(oc, oc) - sph.
radius * sph.
radius;
201 const float disc = b * b - 4.0f * a * c;
205 const float t = (-b - std::sqrt(disc)) / (2.0f * a);
206 if (t < 0.0f || t > maxDistance)
210 const glm::vec3 hitPos = origin + direction * t;
211 outNormal = glm::normalize(hitPos - sph.
center);
223 glm::vec3& outNormal,
227 float dummyDist = maxDistance;
228 glm::vec3 dummyN{0.0f};
230 if (!
raycastAABB(origin, direction, meshBounds, maxDistance, dummyDist, dummyN))
234 float bestDist = maxDistance;
235 glm::vec3 bestNormal{0.0f};
236 uint32_t bestTri = 0;
242 while (stackPtr >= 0) {
243 const int nodeIdx = stack[stackPtr--];
244 const auto& node = mesh.
bvhNodes[
static_cast<size_t>(nodeIdx)];
247 const WorldAABB nodeBox{node.boundsMin, node.boundsMax};
248 float nodeDist = bestDist;
249 glm::vec3 nodeN{0.0f};
250 if (!
raycastAABB(origin, direction, nodeBox, bestDist, nodeDist, nodeN))
253 if (node.count > 0) {
255 for (
int i = node.leftFirst; i < node.leftFirst + node.count; ++i) {
256 const uint32_t ti = mesh.
triIndices[
static_cast<size_t>(i)];
261 const glm::vec3 e1 = v1 - v0;
262 const glm::vec3 e2 = v2 - v0;
263 const glm::vec3 h = glm::cross(direction, e2);
264 const float a = glm::dot(e1, h);
265 if (std::abs(a) < 1e-8f)
268 const float f = 1.0f / a;
269 const glm::vec3 s = origin - v0;
270 const float u = f * glm::dot(s, h);
271 if (u < 0.0f || u > 1.0f)
274 const glm::vec3 q = glm::cross(s, e1);
275 const float v = f * glm::dot(direction, q);
276 if (v < 0.0f || u + v > 1.0f)
279 const float t = f * glm::dot(e2, q);
280 if (t > 0.0f && t < bestDist) {
282 bestNormal = glm::normalize(glm::cross(e1, e2));
283 if (glm::dot(bestNormal, direction) > 0.0f)
284 bestNormal = -bestNormal;
290 stack[++stackPtr] = node.leftFirst;
291 stack[++stackPtr] = node.leftFirst + 1;
296 outDistance = bestDist;
297 outNormal = bestNormal;
309 glm::vec3& outNormal)
311 uint32_t ignored = 0;
328 glm::vec3& outNormal)
331 float tExit = maxDistance;
332 glm::vec3 entryNormal{0.0f, 1.0f, 0.0f};
333 bool startsOutside =
false;
341 const float dist = glm::dot(plane.
normal, origin) - plane.
distance;
342 const float denom = glm::dot(plane.
normal, direction);
345 startsOutside =
true;
358 const float t = -dist / denom;
366 entryNormal = plane.
normal;
380 if (tEntry >= tExit || tEntry < 0.0f || tEntry >= maxDistance)
383 outDistance = tEntry;
384 outNormal = entryNormal;
394 const float denom = glm::dot(plane.
normal, direction);
399 const float distance = (plane.
distance - glm::dot(plane.
normal, origin)) / denom;
400 if (distance < 0.0f || distance >= bestHit.
distance) {
406 bestHit.
point = origin + direction * distance;
413 glm::vec3 normal{0.0f};
420 bestHit.
point = origin + direction * distance;
427 glm::vec3 normal{0.0f};
432 bestHit.
point = origin + direction * distance;
439 glm::vec3 normal{0.0f};
444 bestHit.
point = origin + direction * distance;
451 glm::vec3 normal{0.0f};
456 bestHit.
point = origin + direction * distance;
463 glm::vec3 normal{0.0f};
464 uint32_t hitTriIdx = 0;
469 bestHit.
point = origin + direction * distance;
472 bestHit.
surface = (hitTriIdx < tm.triangleMaterials.size())
473 ?
static_cast<SurfaceType>(tm.triangleMaterials[hitTriIdx])
477 const glm::vec3 triQueryEnd = origin + direction * bestHit.
distance;
478 const WorldAABB triQuery{.min = glm::min(origin, triQueryEnd), .max = glm::max(origin, triQueryEnd)};
481 if (meshIndex < world.triMeshes.size())
482 testTriMesh(world.triMeshes[meshIndex]);
502 if (entity == shooter) {
512 glm::vec3 normal{0.0f};
519 bestHit.
point = origin + direction * distance;
573 glm::vec3& outNormal)
575 const glm::vec3 AB = B - A;
576 const float segLenSq = glm::dot(AB, AB);
581 return raycastSphere(origin, dir, sph, maxDist, outDist, outNormal);
584 const float segLen = std::sqrt(segLenSq);
585 const glm::vec3 segDir = AB / segLen;
589 const glm::vec3 oa = origin - A;
594 const float dirDotSeg = glm::dot(dir, segDir);
595 const float oaDotSeg = glm::dot(oa, segDir);
597 const glm::vec3 dPerp = dir - dirDotSeg * segDir;
598 const glm::vec3 oaPerp = oa - oaDotSeg * segDir;
600 const float a = glm::dot(dPerp, dPerp);
601 const float b = 2.0f * glm::dot(dPerp, oaPerp);
602 const float c = glm::dot(oaPerp, oaPerp) - r * r;
604 float bestT = maxDist + 1.0f;
605 glm::vec3 bestN{0.0f};
608 const float disc = b * b - 4.0f * a * c;
610 const float sqrtDisc = std::sqrt(disc);
611 const float inv2a = 1.0f / (2.0f * a);
614 for (
int side = 0; side < 2; ++side) {
615 const float t = (side == 0) ? (-b - sqrtDisc) * inv2a : (-b + sqrtDisc) * inv2a;
616 if (t < 0.0f || t > maxDist || t >= bestT)
620 const float h = oaDotSeg + t * dirDotSeg;
621 if (h >= 0.0f && h <= segLen) {
624 const glm::vec3 hitPos = origin + dir * t;
625 const glm::vec3 closest = A + segDir * h;
626 bestN = glm::normalize(hitPos - closest);
633 auto testEndcap = [&](glm::vec3 center) {
636 const WorldSphere sph{.center = center, .radius = r};
637 if (
raycastSphere(origin, dir, sph, std::min(maxDist, bestT), t, n)) {
673 Registry& registry, entt::entity shooter, glm::vec3 origin, glm::vec3 direction,
float maxDistance)
686 if (entity == shooter)
706 glm::vec3 boundsMin{std::numeric_limits<float>::max()};
707 glm::vec3 boundsMax{std::numeric_limits<float>::lowest()};
711 boundsMin = glm::min(boundsMin, capMin);
712 boundsMax = glm::max(boundsMax, capMax);
714 const WorldAABB bounds{.min = boundsMin, .max = boundsMax};
716 glm::vec3 aabbNormal{0.0f};
723 glm::vec3 normal{0.0f};
730 bestHit.
point = origin + direction * dist;
Collision shape component — AABB or vertical capsule.
Skeleton-driven hitbox types: body regions, capsule definitions, damage profiles, and per-entity runt...
BodyRegion
Body regions.
Definition Hitbox.hpp:23
@ UpperTorso
Definition Hitbox.hpp:26
World-space position component for ECS entities.
Projectile component and weapon/surface type enumerations.
Shared ECS registry type alias for the game engine.
entt::registry Registry
Shared ECS registry type alias.
Definition Registry.hpp:11
SurfaceType
Surface material hit by a projectile / hitscan / contact.
Definition SurfaceType.hpp:28
@ Flesh
Definition SurfaceType.hpp:31
@ Concrete
Definition SurfaceType.hpp:30
Swept AABB and sphere collision queries against world geometry.
Triangle-mesh collision with BVH acceleration and seam welding.
Shared world geometry for collision / movement / raycast systems.
Pure physics math — no ECS types, no registry.
Definition BroadphaseTree.cpp:11
HitscanHit raycastPlayers(Registry ®istry, entt::entity shooter, glm::vec3 origin, glm::vec3 direction, float maxDistance)
Raycast against all player hitboxes (axis-aligned capsule approximation).
Definition Raycast.hpp:495
constexpr float k_hitscanRange
Maximum hitscan distance in world units.
Definition Raycast.hpp:28
bool raycastTriMeshIndexed(glm::vec3 origin, glm::vec3 direction, const WorldTriMesh &mesh, float maxDistance, float &outDistance, glm::vec3 &outNormal, uint32_t &outTriIdx)
Raycast against a triangle mesh using BVH-accelerated Möller-Trumbore.
Definition Raycast.hpp:218
bool raycastCapsule(glm::vec3 origin, glm::vec3 dir, glm::vec3 A, glm::vec3 B, float r, float maxDist, float &outDist, glm::vec3 &outNormal)
Ray vs capsule intersection.
Definition Raycast.hpp:566
bool raycastSphere(glm::vec3 origin, glm::vec3 direction, const WorldSphere &sph, float maxDistance, float &outDistance, glm::vec3 &outNormal)
Ray vs sphere intersection.
Definition Raycast.hpp:184
const WorldGeometry & activeWorld()
Return the world geometry most recently set via setActiveWorld(), or fall back to testWorld() if none...
Definition WorldData.hpp:236
bool raycastAABB(glm::vec3 origin, glm::vec3 direction, const WorldAABB &box, float maxDistance, float &outDistance, glm::vec3 &outNormal)
Ray vs axis-aligned box intersection (slab method).
Definition Raycast.hpp:45
HitboxHit resolveHitscanHitbox(Registry ®istry, entt::entity shooter, glm::vec3 origin, glm::vec3 direction)
Full hitscan with skeleton-driven hitboxes.
Definition Raycast.hpp:744
bool raycastCylinder(glm::vec3 origin, glm::vec3 direction, const WorldCylinder &cyl, float maxDistance, float &outDistance, glm::vec3 &outNormal)
Ray vs vertical cylinder intersection.
Definition Raycast.hpp:96
HitscanHit resolveHitscan(Registry ®istry, entt::entity shooter, glm::vec3 origin, glm::vec3 direction)
Full hitscan resolution: world geometry first, then players (closest wins).
Definition Raycast.hpp:529
HitscanHit raycastWorld(glm::vec3 origin, glm::vec3 direction, const WorldGeometry &world)
Raycast against all static world geometry (planes + boxes + cylinders + spheres).
Definition Raycast.hpp:389
constexpr float k_parallelEpsilon
Epsilon for parallel-ray checks.
Definition Raycast.hpp:31
bool raycastBrush(glm::vec3 origin, glm::vec3 direction, const WorldBrush &brush, float maxDistance, float &outDistance, glm::vec3 &outNormal)
Ray vs convex brush intersection (generalised slab method).
Definition Raycast.hpp:323
HitboxHit raycastPlayerHitboxes(Registry ®istry, entt::entity shooter, glm::vec3 origin, glm::vec3 direction, float maxDistance)
Raycast against all player hitbox capsules (skeleton-driven).
Definition Raycast.hpp:672
void queryStaticWorldBroadphase(const StaticWorldBroadphase &broadphase, const WorldAABB &query, const std::function< bool(uint32_t meshIndex)> &visit)
Visit triangle-mesh indices whose mesh bounds overlap query.
Definition SweptCollision.cpp:217
bool raycastTriMesh(glm::vec3 origin, glm::vec3 direction, const WorldTriMesh &mesh, float maxDistance, float &outDistance, glm::vec3 &outNormal)
Convenience wrapper for callers that don't care which triangle was hit.
Definition Raycast.hpp:304
Collision shape attached to an entity.
Definition CollisionShape.hpp:34
glm::vec3 halfExtents
AABB half-dimensions.
Definition CollisionShape.hpp:41
ECS components.
Definition Hitbox.hpp:108
std::vector< WorldCapsule > capsules
~12 capsules per character.
Definition Hitbox.hpp:109
Result of a hitscan raycast.
Definition Raycast.hpp:35
ECS tag component: marks an entity as a player (vs. projectile, spawner, etc.).
Definition Player.hpp:8
World-space position of an entity, in game units.
Definition Position.hpp:10
glm::vec3 value
XYZ position (Y-up, Quake units).
Definition Position.hpp:11
Runtime capsule (world-space, per-entity, per-frame).
Definition Hitbox.hpp:95
glm::vec3 pointA
One endpoint of the capsule centerline.
Definition Hitbox.hpp:96
float radius
Capsule radius (world units).
Definition Hitbox.hpp:98
glm::vec3 pointB
Other endpoint of the capsule centerline.
Definition Hitbox.hpp:97
BodyRegion region
For damage multiplier lookup.
Definition Hitbox.hpp:99
Skeleton-driven hitbox raycast (capsule-based).
Definition Raycast.hpp:550
BodyRegion region
Definition Raycast.hpp:555
entt::entity entity
Definition Raycast.hpp:556
glm::vec3 point
Definition Raycast.hpp:553
float distance
Definition Raycast.hpp:552
glm::vec3 normal
Definition Raycast.hpp:554
bool hit
Definition Raycast.hpp:551
Result of a hitscan raycast.
Definition Raycast.hpp:35
SurfaceType surface
Definition Raycast.hpp:40
bool hit
Definition Raycast.hpp:36
float distance
Definition Raycast.hpp:37
glm::vec3 normal
Definition Raycast.hpp:39
glm::vec3 point
Definition Raycast.hpp:38
entt::entity entity
Definition Raycast.hpp:41
An infinite plane dividing free space from solid geometry.
Definition SweptCollision.hpp:31
SurfaceType surfaceType
Material tag for impact VFX / SFX (Phase 3).
Definition SweptCollision.hpp:34
glm::vec3 normal
Unit vector pointing into free (non-solid) space.
Definition SweptCollision.hpp:32
float distance
Signed offset: dot(normal, p) == distance for points on the plane.
Definition SweptCollision.hpp:33
std::vector< BVHNode > nodes
Definition SweptCollision.hpp:150
An axis-aligned box in world space, used as static collision geometry.
Definition SweptCollision.hpp:39
SurfaceType surfaceType
Material tag (Phase 3).
Definition SweptCollision.hpp:42
glm::vec3 max
Maximum corner (highest x, y, z).
Definition SweptCollision.hpp:41
glm::vec3 min
Minimum corner (lowest x, y, z).
Definition SweptCollision.hpp:40
A convex volume defined by bounding planes (for ramps, angled walls, etc.).
Definition SweptCollision.hpp:58
Plane planes[k_maxPlanes]
Definition SweptCollision.hpp:60
SurfaceType surfaceType
Material tag (Phase 3).
Definition SweptCollision.hpp:62
int planeCount
Definition SweptCollision.hpp:61
A vertical (Y-axis) cylinder in world space.
Definition SweptCollision.hpp:70
float radius
Horizontal radius.
Definition SweptCollision.hpp:72
float height
Extent along +Y from base.
Definition SweptCollision.hpp:73
glm::vec3 base
Centre of the bottom cap.
Definition SweptCollision.hpp:71
SurfaceType surfaceType
Material tag (Phase 3).
Definition SweptCollision.hpp:74
All world collision geometry for one tick.
Definition SweptCollision.hpp:225
std::span< const WorldCylinder > cylinders
Definition SweptCollision.hpp:229
std::span< const WorldTriMesh > triMeshes
Definition SweptCollision.hpp:231
const StaticWorldBroadphase * staticBroadphase
Definition SweptCollision.hpp:232
std::span< const Plane > planes
Definition SweptCollision.hpp:226
std::span< const WorldAABB > boxes
Definition SweptCollision.hpp:227
std::span< const WorldBrush > brushes
Definition SweptCollision.hpp:228
std::span< const WorldSphere > spheres
Definition SweptCollision.hpp:230
A sphere in world space.
Definition SweptCollision.hpp:79
SurfaceType surfaceType
Material tag (Phase 3).
Definition SweptCollision.hpp:82
glm::vec3 center
Centre point.
Definition SweptCollision.hpp:80
float radius
Radius.
Definition SweptCollision.hpp:81
A triangle mesh with BVH acceleration for collision queries.
Definition SweptCollision.hpp:108
std::vector< uint32_t > indices
Triangle indices (3 per triangle).
Definition SweptCollision.hpp:110
std::vector< BVHNode > bvhNodes
Flat BVH node array.
Definition SweptCollision.hpp:111
std::vector< glm::vec3 > vertices
All vertex positions (world space, scaled).
Definition SweptCollision.hpp:109
glm::vec3 boundsMin
Whole-mesh AABB min.
Definition SweptCollision.hpp:113
glm::vec3 boundsMax
Whole-mesh AABB max.
Definition SweptCollision.hpp:114
std::vector< uint32_t > triIndices
Permutation: BVH leaf ranges → triangle indices.
Definition SweptCollision.hpp:112