128 lines
4.2 KiB
Markdown
128 lines
4.2 KiB
Markdown
# 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.
|