# Physics System Source: `src/dusk/physics/`, entity component at `src/dusk/entity/component/physics/entityphysics.h/.c` ## Overview Dusk uses a lightweight, custom 3D physics simulation with no external library dependency. It is integrated with the ECS: only entities that have both a `COMPONENT_TYPE_PHYSICS` and a `COMPONENT_TYPE_POSITION` component participate in the simulation. ## Shapes ```c typedef enum { PHYSICS_SHAPE_CUBE, // Axis-aligned bounding box (AABB) PHYSICS_SHAPE_SPHERE, PHYSICS_SHAPE_CAPSULE, // Y-axis aligned; radius + halfHeight PHYSICS_SHAPE_PLANE, // Infinite plane; normal + distance } physicshapetype_t; ``` All shape pairs are supported by the collision dispatch (`physicsTestShapeVsShape`). See `physicstest.h` for the individual test functions. ## Body types ```c typedef enum { PHYSICS_BODY_STATIC, // Never moves; immovable collision surface PHYSICS_BODY_DYNAMIC, // Driven by gravity, velocity, collisions PHYSICS_BODY_KINEMATIC, // Moved programmatically; collides but not // driven by the simulation (e.g. player) } physicsbodytype_t; ``` ## World and gravity ```c extern physicsworld_t PHYSICS_WORLD; // PHYSICS_WORLD.gravity -- default {0, -9.81, 0} ``` The simulation step is driven by `physicsManagerUpdate()`, which is called each fixed-timestep game loop tick. It skips dynamic-timestep sub-steps (`DUSK_TIME_DYNAMIC`). ## Simulation phases (each step) 1. **Integrate dynamics** -- apply gravity scaled by `gravityScale`, advance velocity, update position. 2. **Dynamic vs static/kinematic** -- resolve penetration and cancel the normal velocity component. 3. **Dynamic vs dynamic** -- split penetration 50/50; exchange relative normal velocity. 4. **Rebuild transforms** -- call `entityPositionRebuild()` for all affected dynamic bodies. `PHYSICS_GROUND_THRESHOLD = 0.707f` -- a collision normal with a Y component above this value sets `onGround = true` on the dynamic body. ## Entity component (`entityphysics_t`) ```c typedef struct { physicsbodytype_t type; physicsshape_t shape; vec3 velocity; float_t gravityScale; // default 1.0 bool_t onGround; // set by the solver each step } entityphysics_t; ``` Default on init: DYNAMIC body, 0.5m half-extents AABB cube, `gravityScale = 1.0f`. ### Component API ```c entityphysics_t *entityPhysicsGet(entityid_t, componentid_t); void entityPhysicsSetShape(entityid_t, componentid_t, physicsshape_t); physicsshape_t entityPhysicsGetShape(entityid_t, componentid_t); void entityPhysicsSetVelocity(entityid_t, componentid_t, vec3); void entityPhysicsGetVelocity(entityid_t, componentid_t, vec3 dest); void entityPhysicsApplyImpulse(entityid_t, componentid_t, vec3); // No-op on STATIC bodies. bool_t entityPhysicsIsOnGround(entityid_t, componentid_t); void entityPhysicsSetBodyType(entityid_t, componentid_t, physicsbodytype_t); physicsbodytype_t entityPhysicsGetBodyType(entityid_t, componentid_t); ``` ## Collision detection primitives (`physicstest.h`) Each function returns `true` if overlapping and writes the push-out normal (pointing from B toward A) and penetration depth. | Function | Shapes | |----------|--------| | `physicsTestAabbVsAabb` | CUBE vs CUBE | | `physicsTestSphereVsSphere` | SPHERE vs SPHERE | | `physicsTestSphereVsAabb` | SPHERE vs CUBE | | `physicsTestSphereVsPlane` | SPHERE vs PLANE | | `physicsTestAabbVsPlane` | CUBE vs PLANE | | `physicsTestCapsuleVsSphere` | CAPSULE vs SPHERE | | `physicsTestCapsuleVsAabb` | CAPSULE vs CUBE | | `physicsTestCapsuleVsPlane` | CAPSULE vs PLANE | | `physicsTestCapsuleVsCapsule` | CAPSULE vs CAPSULE | | `physicsTestShapeVsShape` | Any pair via dispatch | Capsules are always Y-axis aligned. Planes are infinite (not half-spaces). ## Limitations and known gaps - No rotation simulation -- bodies do not rotate from collisions. - No friction or damping model yet. - No sleeping / deactivation for resting bodies. - No broad-phase culling: the solver is O(n^2) per phase. This is acceptable up to the ECS entity limit (64 entities) but must be revisited if the entity count grows. - Capsule vs plane uses the bottom/top hemisphere centers as a degenerate approximation -- accurate for large planes but not for thin surfaces.