|
group2 0.1.0
CSE 125 Group 2
|
Pure physics math — no ECS types, no registry. More...
Namespaces | |
| namespace | detail |
| Active world (runtime-switchable). | |
Classes | |
| struct | MapCollisionData |
| Collision data. More... | |
| struct | MapLoadOptions |
| Load options. More... | |
| struct | HitscanHit |
| Result of a hitscan raycast. More... | |
| struct | HitboxHit |
| Skeleton-driven hitbox raycast (capsule-based). More... | |
| struct | Plane |
| An infinite plane dividing free space from solid geometry. More... | |
| struct | WorldAABB |
| An axis-aligned box in world space, used as static collision geometry. More... | |
| struct | WorldBrush |
| A convex volume defined by bounding planes (for ramps, angled walls, etc.). More... | |
| struct | WorldCylinder |
| A vertical (Y-axis) cylinder in world space. More... | |
| struct | WorldSphere |
| A sphere in world space. More... | |
| struct | BVHNode |
| A single BVH node for spatial acceleration of triangle meshes. More... | |
| struct | WorldTriMesh |
| A triangle mesh with BVH acceleration for collision queries. More... | |
| struct | WorldGeometry |
| All world collision geometry for one tick. More... | |
| struct | HitResult |
| Result of a swept AABB collision query. More... | |
| struct | SphereHitResult |
| Result of a sphere-cast query (includes world-space hit point). More... | |
| struct | WallDetectionResult |
| Results of wall detection sphere casts. More... | |
Functions | |
| bool | loadMapCollision (const std::string &path, MapCollisionData &out, const MapLoadOptions &opts={}) |
| API. | |
| bool | loadPropCollision (const std::string &path, MapCollisionData &out, glm::vec3 position, float scale, bool decomposeNonConvex=false) |
| Load collision for a standalone prop GLB and append to existing collision data. | |
| glm::vec3 | applyGravity (glm::vec3 vel, float dt) |
| Apply gravity for one tick: subtracts k_gravity * dt from the Y component. | |
| glm::vec3 | applyGroundFriction (glm::vec3 vel, float dt) |
| Apply Quake-style ground friction to horizontal (XZ) velocity. | |
| glm::vec3 | accelerate (glm::vec3 vel, glm::vec3 wishDir, float wishSpeed, float accel, float dt) |
| Quake PM_Accelerate: accelerate toward wishDir up to wishSpeed. | |
| glm::vec3 | clipVelocity (glm::vec3 vel, glm::vec3 normal, float overbounce) |
| Project velocity onto a collision surface to slide along it. | |
| glm::vec3 | computeWishDir (float yaw, bool forward, bool back, bool left, bool right) |
| Compute the horizontal wish direction from yaw angle and WASD key state. | |
| 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). | |
| bool | raycastCylinder (glm::vec3 origin, glm::vec3 direction, const WorldCylinder &cyl, float maxDistance, float &outDistance, glm::vec3 &outNormal) |
| Ray vs vertical cylinder intersection. | |
| bool | raycastSphere (glm::vec3 origin, glm::vec3 direction, const WorldSphere &sph, float maxDistance, float &outDistance, glm::vec3 &outNormal) |
| Ray vs sphere intersection. | |
| bool | raycastTriMesh (glm::vec3 origin, glm::vec3 direction, const WorldTriMesh &mesh, float maxDistance, float &outDistance, glm::vec3 &outNormal) |
| Raycast against a triangle mesh using BVH-accelerated Möller-Trumbore. | |
| 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). | |
| HitscanHit | raycastWorld (glm::vec3 origin, glm::vec3 direction, const WorldGeometry &world) |
| Raycast against all static world geometry (planes + boxes + cylinders + spheres). | |
| HitscanHit | raycastPlayers (Registry ®istry, entt::entity shooter, glm::vec3 origin, glm::vec3 direction, float maxDistance) |
| Raycast against all player hitboxes (axis-aligned capsule approximation). | |
| HitscanHit | resolveHitscan (Registry ®istry, entt::entity shooter, glm::vec3 origin, glm::vec3 direction) |
| Full hitscan resolution: world geometry first, then players (closest wins). | |
| 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. | |
| HitboxHit | raycastPlayerHitboxes (Registry ®istry, entt::entity shooter, glm::vec3 origin, glm::vec3 direction, float maxDistance) |
| Raycast against all player hitbox capsules (skeleton-driven). | |
| HitboxHit | resolveHitscanHitbox (Registry ®istry, entt::entity shooter, glm::vec3 origin, glm::vec3 direction) |
| Full hitscan with skeleton-driven hitboxes. | |
| HitResult | sweepAABB (glm::vec3 halfExtents, glm::vec3 start, glm::vec3 end, std::span< const Plane > planes) |
| Sweep an AABB along the path [start, end] against a list of infinite planes. | |
| HitResult | sweepAABBvsBox (glm::vec3 halfExtents, glm::vec3 start, glm::vec3 end, const WorldAABB &box) |
| Sweep an AABB against a static axis-aligned box. | |
| HitResult | sweepAABBvsBrush (glm::vec3 halfExtents, glm::vec3 start, glm::vec3 end, const WorldBrush &brush) |
| Sweep an AABB against a convex brush (set of bounding planes). | |
| HitResult | sweepAABBvsCylinder (glm::vec3 halfExtents, glm::vec3 start, glm::vec3 end, const WorldCylinder &cyl) |
| Sweep an AABB against a vertical cylinder. | |
| HitResult | sweepAABBvsSphere (glm::vec3 halfExtents, glm::vec3 start, glm::vec3 end, const WorldSphere &sph) |
| Sweep an AABB against a sphere. | |
| HitResult | sweepAll (glm::vec3 halfExtents, glm::vec3 start, glm::vec3 end, const WorldGeometry &world) |
| Sweep an AABB against all world geometry, returning the earliest hit. | |
| SphereHitResult | sphereCast (float radius, glm::vec3 start, glm::vec3 end, const WorldGeometry &world) |
| Cast a sphere along the path [start, end] against all world geometry. | |
| void | buildTriMeshBVH (WorldTriMesh &mesh) |
| Build the BVH for a WorldTriMesh. | |
| HitResult | sweepAABBvsTriMesh (glm::vec3 halfExtents, glm::vec3 start, glm::vec3 end, const WorldTriMesh &mesh) |
| Sweep an AABB against a triangle mesh using BVH-accelerated SAT tests. | |
| void | depenetrateAABBvsTriMesh (glm::vec3 &pos, glm::vec3 &vel, glm::vec3 halfExtents, const WorldTriMesh &mesh, float pushback=0.03125f) |
| Push an AABB out of a triangle mesh using per-triangle SAT MTV. | |
| WallDetectionResult | detectWalls (glm::vec3 pos, float yaw, glm::vec3 halfExtents, const WorldGeometry &world, float checkDist, float sphereRadius, glm::vec3 prevWallNormal=glm::vec3(0.0f)) |
| Detect walls to the left, right, and front of the player via sphere casts. | |
| bool | isWallNormal (glm::vec3 normal) |
| Check if a surface normal represents a wall (not floor/ceiling). | |
| void | setActiveWorld (const WorldGeometry &geo) |
| Set the world geometry that activeWorld() returns. | |
| const WorldGeometry & | activeWorld () |
| Return the world geometry most recently set via setActiveWorld(), or fall back to testWorld() if none has been set. | |
| WorldBrush | makeRamp (float xMin, float xMax, float zMin, float zMax, float height) |
| Create a ramp brush that rises along +Z. | |
| WorldBrush | makeDiagonalWall (glm::vec3 center, float halfLen, float halfThick, float height, glm::vec3 dir) |
| Create a diagonal wall brush from a centre, direction, and dimensions. | |
| const WorldGeometry & | testWorld () |
| The physics test playground. | |
Variables | |
| constexpr float | k_gravity = 1000.0f |
| Downward acceleration (units/s^2). Faster than real-world for snappy arcs. | |
| constexpr float | k_jumpSpeed = 380.0f |
| Initial upward velocity on jump (units/s). Gives apex ~ 72 units (~6 ft). | |
| constexpr float | k_groundAccel = 15.0f |
| Ground acceleration constant. Higher = reaches max speed faster. | |
| constexpr float | k_airAccel |
| Air acceleration constant. Higher than Quake (0.7) for Titanfall-style air control. | |
| constexpr float | k_airMaxSpeed |
| Wish-speed cap in air (units/s). Does NOT cap total speed — existing momentum is preserved. | |
| constexpr float | k_friction = 4.0f |
| Ground friction coefficient (Quake default). | |
| constexpr float | k_stopSpeed = 150.0f |
| Friction is amplified below this speed for a crisp stop. | |
| constexpr float | k_overbounceWall = 1.001f |
| Separation impulse for walls/ceilings; prevents corner-sticking. | |
| constexpr float | k_overbounceFloor = 1.0f |
| Floor overbounce — exactly 1.0 means no bounce. | |
| constexpr float | k_stepHeight = 18.0f |
| Maximum obstacle height auto-stepped over without jumping (units). | |
| constexpr float | k_hitscanRange = 5000.0f |
| Maximum hitscan distance in world units. | |
| constexpr float | k_parallelEpsilon = 1e-6f |
| Epsilon for parallel-ray checks. | |
Pure physics math — no ECS types, no registry.
Wall / climb / ledge detection via sphere casts.
Pure swept-collision math — no ECS types, no registry.
All physics tuning values in one place.
All functions take values in and return values out (no mutation via pointer). Constants come from PhysicsConstants.hpp.
Units: Quake units (1 unit ≈ 1 inch), Y-up coordinate system.
Starting values target a Titanfall-to-Quake movement feel. Tune iteratively — k_gravity and k_jumpSpeed must always be tuned together: jump height = k_jumpSpeed^2 / (2 × k_gravity).
Plane convention: dot(normal, p) > distance is free space; dot(normal, p) < distance is solid. The normal always points into free space.
Example planes (Y-up coordinate system):
Used by the movement system each tick to detect nearby surfaces for wallrunning, climbing, and ledge grabbing.
| glm::vec3 physics::accelerate | ( | glm::vec3 | vel, |
| glm::vec3 | wishDir, | ||
| float | wishSpeed, | ||
| float | accel, | ||
| float | dt ) |
Quake PM_Accelerate: accelerate toward wishDir up to wishSpeed.
Does not cap total speed — only the projection of velocity onto wishDir is capped at wishSpeed. Existing momentum in any other direction is untouched. This property is what makes strafe jumping possible.
| vel | Current velocity. |
| wishDir | Normalised desired movement direction (from InputSnapshot + yaw). |
| wishSpeed | Target speed (systems::currentWishSpeed on ground, k_airMaxSpeed in air). |
| accel | Acceleration constant (k_groundAccel or k_airAccel). |
| dt | Delta time in seconds. |
|
inline |
Return the world geometry most recently set via setActiveWorld(), or fall back to testWorld() if none has been set.
This is the single entry point that all physics systems should use.
| glm::vec3 physics::applyGravity | ( | glm::vec3 | vel, |
| float | dt ) |
Apply gravity for one tick: subtracts k_gravity * dt from the Y component.
| vel | Current velocity. |
| dt | Delta time in seconds. |
| glm::vec3 physics::applyGroundFriction | ( | glm::vec3 | vel, |
| float | dt ) |
Apply Quake-style ground friction to horizontal (XZ) velocity.
Uses k_stopSpeed as a minimum control speed so entities stop crisply rather than asymptotically approaching zero.
| vel | Current velocity. |
| dt | Delta time in seconds. |
| void physics::buildTriMeshBVH | ( | WorldTriMesh & | mesh | ) |
Build the BVH for a WorldTriMesh.
Must be called after vertices and indices are populated. Fills in bvhNodes, triIndices, boundsMin, and boundsMax.
| glm::vec3 physics::clipVelocity | ( | glm::vec3 | vel, |
| glm::vec3 | normal, | ||
| float | overbounce ) |
Project velocity onto a collision surface to slide along it.
| vel | Current velocity. |
| normal | Surface normal at the contact point. |
| overbounce | Separation scalar: use k_overbounceFloor for floors, k_overbounceWall for walls/ceilings. |
| glm::vec3 physics::computeWishDir | ( | float | yaw, |
| bool | forward, | ||
| bool | back, | ||
| bool | left, | ||
| bool | right ) |
Compute the horizontal wish direction from yaw angle and WASD key state.
| yaw | Player's current yaw in radians. |
| forward | True when W is held. |
| back | True when S is held. |
| left | True when A is held. |
| right | True when D is held. |
| void physics::depenetrateAABBvsTriMesh | ( | glm::vec3 & | pos, |
| glm::vec3 & | vel, | ||
| glm::vec3 | halfExtents, | ||
| const WorldTriMesh & | mesh, | ||
| float | pushback = 0.03125f ) |
Push an AABB out of a triangle mesh using per-triangle SAT MTV.
For each triangle the AABB overlaps (BVH-accelerated leaf search), computes the minimum-translation-vector that separates the AABB from the triangle and applies it. More accurate than depenetrating against leaf-AABB proxies: curved surfaces (cylinders, spheres) feel curved instead of cubical.
Velocity is updated to cancel the component flowing into the surface, so the entity slides along the contact rather than re-penetrating next frame.
Trade-off: at sharp triangle edges where adjacent face normals fight, the per-triangle pushes can briefly disagree. In practice this is rare and the pushback bias keeps the entity off the surface.
| pos | AABB centre — modified in place to push out of overlaps. |
| vel | Velocity — modified in place to cancel inward motion. |
| halfExtents | AABB half-extents. |
| mesh | Triangle mesh to depenetrate from. |
| pushback | Tiny extra distance added to each push to avoid hairline contact (matches Quake's DIST_EPSILON of 1/32 unit). |
| WallDetectionResult physics::detectWalls | ( | glm::vec3 | pos, |
| float | yaw, | ||
| glm::vec3 | halfExtents, | ||
| const WorldGeometry & | world, | ||
| float | checkDist, | ||
| float | sphereRadius, | ||
| glm::vec3 | prevWallNormal = glm::vec3(0.0f) ) |
Detect walls to the left, right, and front of the player via sphere casts.
Also probes downward to measure ground distance (used for wallrun/climb min height).
| pos | Player AABB centre position. |
| yaw | Player facing direction (radians). |
| halfExtents | Player AABB half-extents (for offset calculations). |
| world | World collision geometry. |
| checkDist | How far sideways/forward to trace (u). |
| sphereRadius | Radius of the trace sphere (u). |
| prevWallNormal | Previous tick's wall normal (zero if not wallrunning). When non-zero, an additional trace is cast toward -prevWallNormal to track curved surfaces (cylinders, concave walls) whose normal rotates as the player moves. |
|
inline |
Check if a surface normal represents a wall (not floor/ceiling).
Walls have normals that are roughly horizontal (|normal.y| < 0.3).
| bool physics::loadMapCollision | ( | const std::string & | path, |
| MapCollisionData & | out, | ||
| const MapLoadOptions & | opts = {} ) |
API.
Extract collision geometry from a map .glb file.
Walks the Assimp scene graph. For each mesh node, determines whether it belongs to the collision collection (by checking ancestor node names) or, in prototype mode, always. Collision meshes are converted to per-object axis-aligned bounding boxes.
This function does not produce visual / renderable data — use the existing Renderer::loadSceneModel() path for that.
| path | Absolute or relative path to the .glb file. |
| out | Filled with extracted collision primitives on success. |
| opts | Loading options (scale, collection name, prototype mode). |
| bool physics::loadPropCollision | ( | const std::string & | path, |
| MapCollisionData & | out, | ||
| glm::vec3 | position, | ||
| float | scale, | ||
| bool | decomposeNonConvex = false ) |
Load collision for a standalone prop GLB and append to existing collision data.
Loads the GLB, applies the given transform (position + uniform scale), runs auto-detection on each mesh, and appends the resulting primitives to out. Call setActiveWorld(out.geometry()) after all props are loaded to update the physics world.
| path | Absolute path to the .glb file. |
| out | Existing collision data to append to. |
| position | World-space position of the prop. |
| scale | Uniform scale factor. |
| decomposeNonConvex | When true, non-convex meshes inside the prop are run through V-HACD convex decomposition (each becomes a small set of WorldBrushes) instead of falling back to WorldTriMesh. Smoother runtime collision on irregular shapes (a bottle, a bent metal pallet) at the cost of seconds-per-mesh load time. Default false because a prop GLB can hold dozens of sub- meshes and decomposing every one of them blows up startup. |
|
inline |
Create a diagonal wall brush from a centre, direction, and dimensions.
| center | Centre of the wall base (y=0). |
| halfLen | Half-length along dir. |
| halfThick | Half-thickness perpendicular to dir in XZ. |
| height | Wall height (from y=0 to y=height). |
| dir | Normalised direction along the wall in XZ. |
|
inline |
Create a ramp brush that rises along +Z.
The wedge shape goes from (xMin, 0, zMin) at ground level to (xMax, height, zMax) at the back-top edge.
|
inline |
Ray vs axis-aligned box intersection (slab method).
|
inline |
Ray vs convex brush intersection (generalised slab method).
A WorldBrush is the intersection of half-spaces defined by planes with outward normals; the solid interior satisfies dot(n, p) <= distance for every plane. A ray hits the brush at the latest plane it enters from outside, provided that t doesn't exceed the earliest plane it exits. Mirrors the logic in sweepAABBvsBrush but for a point-ray instead of a swept AABB.
|
inline |
Ray vs capsule intersection.
A capsule is the Minkowski sum of the line segment AB and a sphere of radius r. Decomposed into: (1) ray vs infinite cylinder along AB, clamped to the segment, then (2) ray vs hemisphere endcaps.
maxDist.
|
inline |
Ray vs vertical cylinder intersection.
|
inline |
Raycast against all player hitbox capsules (skeleton-driven).
Uses a broad-phase AABB check (CollisionShape) before testing individual capsules, so only nearby players pay the narrow-phase cost.
|
inline |
Raycast against all player hitboxes (axis-aligned capsule approximation).
|
inline |
Ray vs sphere intersection.
|
inline |
Raycast against a triangle mesh using BVH-accelerated Möller-Trumbore.
|
inline |
Raycast against all static world geometry (planes + boxes + cylinders + spheres).
|
inline |
Full hitscan resolution: world geometry first, then players (closest wins).
|
inline |
Full hitscan with skeleton-driven hitboxes.
World geometry first, then player hitbox capsules (closest wins). Falls back to the old AABB path for players without HitboxInstance.
|
inline |
Set the world geometry that activeWorld() returns.
The caller retains ownership of the memory behind the spans — the pointed-to data must outlive every call to activeWorld(). Typically backed by a MapCollisionData whose vectors live for the duration of the game.
Both client and server must call this with identical data before the first physics tick to maintain prediction parity.
| SphereHitResult physics::sphereCast | ( | float | radius, |
| glm::vec3 | start, | ||
| glm::vec3 | end, | ||
| const WorldGeometry & | world ) |
Cast a sphere along the path [start, end] against all world geometry.
Uses the Minkowski-sum approach: geometry is expanded by the sphere radius, then the sweep becomes a point (ray) test against the expanded geometry.
| radius | Sphere radius (u). |
| start | World-space start of sweep (sphere centre). |
| end | World-space end of sweep (sphere centre). |
| world | World collision geometry to test against. |
| HitResult physics::sweepAABB | ( | glm::vec3 | halfExtents, |
| glm::vec3 | start, | ||
| glm::vec3 | end, | ||
| std::span< const Plane > | planes ) |
Sweep an AABB along the path [start, end] against a list of infinite planes.
Uses the Minkowski-sum approach: each plane is expanded outward by the AABB half-extents, reducing the problem to a ray-vs-expanded-plane intersection.
| halfExtents | Half-dimensions of the AABB. |
| start | World-space start position (AABB centre). |
| end | World-space end position (AABB centre). |
| planes | World collision planes to test against. |
| HitResult physics::sweepAABBvsBox | ( | glm::vec3 | halfExtents, |
| glm::vec3 | start, | ||
| glm::vec3 | end, | ||
| const WorldAABB & | box ) |
Sweep an AABB against a static axis-aligned box.
Expands the static box by the moving AABB's half-extents (Minkowski sum) and performs a ray-slab intersection test on the swept centre point. Entities starting inside the box are skipped (depenetration handles that).
| HitResult physics::sweepAABBvsBrush | ( | glm::vec3 | halfExtents, |
| glm::vec3 | start, | ||
| glm::vec3 | end, | ||
| const WorldBrush & | brush ) |
Sweep an AABB against a convex brush (set of bounding planes).
Finds the time at which the AABB enters all half-spaces simultaneously. Entities starting inside the brush are skipped (depenetration handles that).
| HitResult physics::sweepAABBvsCylinder | ( | glm::vec3 | halfExtents, |
| glm::vec3 | start, | ||
| glm::vec3 | end, | ||
| const WorldCylinder & | cyl ) |
Sweep an AABB against a vertical cylinder.
Minkowski-expands the cylinder by the AABB half-extents: the radius grows by the XZ extent and the height caps grow by the Y extent. The sweep then reduces to a 2D ray-vs-circle test (XZ) clamped by the expanded Y slab.
| HitResult physics::sweepAABBvsSphere | ( | glm::vec3 | halfExtents, |
| glm::vec3 | start, | ||
| glm::vec3 | end, | ||
| const WorldSphere & | sph ) |
Sweep an AABB against a sphere.
Minkowski-expands the sphere radius by the AABB half-extents (approximation: uses the max half-extent component, making it slightly conservative at corners). Then performs a ray-vs-sphere test on the swept centre.
| HitResult physics::sweepAABBvsTriMesh | ( | glm::vec3 | halfExtents, |
| glm::vec3 | start, | ||
| glm::vec3 | end, | ||
| const WorldTriMesh & | mesh ) |
Sweep an AABB against a triangle mesh using BVH-accelerated SAT tests.
| HitResult physics::sweepAll | ( | glm::vec3 | halfExtents, |
| glm::vec3 | start, | ||
| glm::vec3 | end, | ||
| const WorldGeometry & | world ) |
Sweep an AABB against all world geometry, returning the earliest hit.
|
inline |
The physics test playground.
Layout along the +Z axis (forward from spawn at origin):
z ~ 400 : Reference cube (64^3) z ~ 800 : Small steppable box (64x16x64) and large jumpable box (80x64x80) z ~ 1000 : Gentle ramp (15deg, left) and steep ramp (40deg, right) z ~ 1500 : Stairs (5 steps rising along +Z) z ~ 1900 : Axis-aligned wall, diagonal wall, pole z ~ 2100 : Elevated thin walkway
|
constexpr |
Air acceleration constant. Higher than Quake (0.7) for Titanfall-style air control.
|
constexpr |
Wish-speed cap in air (units/s). Does NOT cap total speed — existing momentum is preserved.
|
constexpr |
Ground friction coefficient (Quake default).
|
constexpr |
Downward acceleration (units/s^2). Faster than real-world for snappy arcs.
|
constexpr |
Ground acceleration constant. Higher = reaches max speed faster.
|
inlineconstexpr |
Maximum hitscan distance in world units.
|
constexpr |
Initial upward velocity on jump (units/s). Gives apex ~ 72 units (~6 ft).
|
constexpr |
Floor overbounce — exactly 1.0 means no bounce.
|
constexpr |
Separation impulse for walls/ceilings; prevents corner-sticking.
|
inlineconstexpr |
Epsilon for parallel-ray checks.
|
constexpr |
Maximum obstacle height auto-stepped over without jumping (units).
|
constexpr |
Friction is amplified below this speed for a crisp stop.