Rigid Body Simulation¶
Overview¶
NovaPhy's rigid body pipeline handles free-floating bodies with collision detection and constraint resolution.
Creating Bodies¶
import numpy as np
import novaphy
builder = novaphy.ModelBuilder()
# Ground plane
builder.add_ground_plane(y=0.0)
# Box body (mass, half-extents)
body = novaphy.RigidBody.from_box(1.0, np.array([0.5, 0.5, 0.5]))
idx = builder.add_body(body, novaphy.Transform.from_translation(np.array([0, 5, 0])))
builder.add_shape(novaphy.CollisionShape.make_box(np.array([0.5, 0.5, 0.5]), idx))
Collision Detection¶
Broadphase¶
Filters candidate collision pairs using axis-aligned bounding boxes:
- Explicit — uses shape pairs precomputed by the builder
- Sweep and Prune (SAP) — sort-based broadphase for larger scenes
- NXN / all-pairs — simple AABB checks for small scenes and tests
Narrowphase¶
Generates exact contact points for each candidate pair:
| Pair | Algorithm |
|---|---|
| Sphere-Sphere | Analytic distance check |
| Sphere-Plane | Signed distance to plane |
| Box-Sphere | Closest point on box |
| Box-Plane | Vertex projection |
| Box-Box | Separating Axis Theorem (analytic contacts) |
| Convex-Convex | contact core |
Run collision explicitly before stepping a solver:
pipeline = novaphy.CollisionPipeline(model, broad_phase="sap")
contacts = pipeline.contacts()
pipeline.collide(state, contacts)
solver.step(state, state, control, contacts, dt)
Solvers¶
SolverSemiImplicit (Default)¶
Sequential Impulse solver with:
- Warm starting from previous frame
- Accumulated impulse clamping
- Coulomb friction (tangential impulse <= mu * normal impulse)
- Baumgarte stabilization for penetration correction
SolverXPBD¶
Extended Position Based Dynamics with compliance-based constraints. Operates in maximal coordinates.
Modifying State¶
State is owned by the caller. After state = model.state(), write into the
state buffer directly through the per-body setters or by editing the
state.transforms / state.body_qd arrays.
Python binding limitation
Some Python bindings return copies of std::vector. Use the dedicated
setter methods on SimState to update body velocities so the change is
reflected on the C++ side:
state = model.state()
# Set linear / angular velocity for body index 0.
state.set_linear_velocity(0, np.array([1.0, 0.0, 0.0], dtype=np.float32))
state.set_angular_velocity(0, np.array([0.0, 0.0, 1.0], dtype=np.float32))
# Read back via state.transforms / state.body_qd.
pos = state.transforms[0].position
Demos¶
| Demo | Description |
|---|---|
demo_pyramid_ball.py |
Pyramid stack with sphere projectile |
demo_friction_ramp.py |
Boxes on a 30-degree ramp |
demo_wall_break.py |
Wall hit by a sphere |
demo_dominoes.py |
Chain reaction dominos |
demo_unified_collision.py |
Gallery of all collision pairs |
demo_pyramids_numerous.py |
Stress test with many pyramids |