Skip to main content

Getting started

Nexus is a physics engine for simulating rigid-bodies and deformable materials with compute shaders. It is available in 2D and 3D, through the nexus2d and nexus3d crates. This page shows how to set up a simple simulation with a ball falling on a fixed ground. The guide describes the APIs in the 0.7 source tree; an older installed release may expose different methods. The following pages separate rigid-body dynamics from MPM particles and materials, with simulation state and viewer covering the common workflow.

Setting up Nexus with Cargo​

Nexus relies on the official Rust package manager Cargo for dependency resolution and compilation. The rbd feature enables rigid-body dynamics, and the mpm feature enables the Material Point Method. Keep in mind that these two features are not enabled by default.

Nexus uses Rust-GPU to compile its compute shaders. Before building your project, install the shader compiler and the toolchain it needs:

cargo install cargo-gpu --version 0.10.0
cargo gpu install --auto-install-rust-toolchain

Then create a new project and add the dependencies corresponding to the dimension you want to simulate. NexusViewer is optional for Nexus itself, but is used by the example below.

cargo new example-using-nexus
cd example-using-nexus
cargo add nexus2d@0.7 --features rbd,mpm
cargo add nexus_viewer2d@0.7 pollster@1 anyhow@1
info

The nexus2d::rapier and nexus3d::rapier modules re-export the version of Rapier used by Nexus. Using this re-export avoids mixing different versions of RigidBodyBuilder and ColliderBuilder. Their concepts and builder settings are documented in the Rapier user-guide. Nexus uses its own NexusPipeline, so not every Rapier setting is implemented by Nexus.

Basic simulation example​

Here is a complete src/main.rs file. The RigidBodyBuilder and ColliderBuilder describe the scene. The NexusState contains that scene, and the NexusPipeline advances it.

use nexus_viewer2d::NexusViewer;
use nexus2d::prelude::{NexusPipeline, NexusState, RbdCoupling};
use nexus2d::rapier::prelude::*;

fn main() -> anyhow::Result<()> {
pollster::block_on(async {
let mut viewer = NexusViewer::new(Vec::new()).await.with_running();
viewer.init_backend();
let mut state = NexusState::default();
let mut pipeline = NexusPipeline::default();

/* Create the ground. */
let ground = RigidBodyBuilder::fixed().build();
let collider = ColliderBuilder::cuboid(5.0, 0.1).build();
let shape = collider.shared_shape().clone();
let handle = state.insert_rigid_body(ground, collider, RbdCoupling::None);
viewer.insert_shape(handle, &shape, Pose::IDENTITY);

/* Create the falling ball. */
let ball = RigidBodyBuilder::dynamic()
.translation(Vec2::new(0.0, 3.0))
.build();
let collider = ColliderBuilder::ball(0.5).build();
let shape = collider.shared_shape().clone();
let handle = state.insert_rigid_body(ball, collider, RbdCoupling::None);
viewer.insert_shape(handle, &shape, Pose::IDENTITY);

/* Upload the scene before rendering it. */
state.finalize(viewer.backend()).await?;

while viewer.render_frame().await {
if viewer.simulating() {
pipeline
.simulate(viewer.backend(), &mut state, None)
.await?;
}
viewer.sync(&mut state, None).await?;
}
Ok(())
})
}

Run the example with cargo run --release. The NexusViewer opens a window, and viewer.with_running() starts the simulation immediately. The window's controls change the value returned by viewer.simulating(), which determines whether the loop calls pipeline.simulate.

info

Call viewer.init_backend() before state.finalize or pipeline.simulate. The three operations initialize the backend, upload the scene, and advance physics respectively. See simulation state and viewer.

The example uses pollster::block_on to drive asynchronous work in a native executable. A browser application needs an asynchronous browser entry point instead. The repository's WebAssembly demos show this setup.