Skip to content

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.

python python/demos/demo_pyramid_ball.py --solver xpbd
python python/demos/xpbd/demo_xpbd_stack.py

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