Skip to main content

Scenes

A Scene is an independent simulation container owned by a Context. It owns its actors, constraints, callbacks, captured states, and evolving simulation state. Actors in different scenes do not interact.

Creating and Destroying Scenes

#include <superdex_physics.h>

int main() {
superdex::Context* context = superdex::CreateContext();
superdex::Scene* scene = context->CreateScene("training environment");

// Create actors and constraints, then step the scene.

context->DestroyScene(scene);
superdex::DestroyContext(context);
}

Destroying a Scene immediately destroys everything it owns. Do not use pointers or handles to the Scene, its actors, or its constraints afterward. Explicit destruction is useful when reclaiming one Scene while keeping the Context alive; otherwise Context shutdown automatically cleans up remaining scenes.

A Context can own multiple scenes. This is useful for independent environments or disconnected systems because actors in separate scenes cannot affect one another.

Concurrency

A Scene and its actors and constraints may be accessed by only one thread at a time. This is an exclusion rule, not permanent thread affinity: different threads may access the Scene at different times when the application orders and synchronizes those accesses. Ordinary Scene operations do not provide that synchronization.

Different scenes can be stepped concurrently, provided each scene is accessed by only one thread at a time. In C++, bind each non-creator calling thread as described in Context. For C++ applications that need a scene to advance independently, AsyncScene provides queued access to its wrapped Scene.

Scene Responsibilities

A Scene owns and coordinates:

  • Actor and Constraint lifetimes.
  • Gravity, Contact behavior, and Solver parameters.
  • Simulation state advancement and registered Query updates.
  • Aggregate solver and performance diagnostics.
  • Dynamic State capture and restoration.

Gravity

SuperDex Physics uses Y-up coordinates by default. New scenes default to world-space gravity (0, -9.8, 0) [m/s²], along -Y. Use SetGravity() in C++ or set_gravity() in Python to match your application's coordinate convention.

SuperDex Robotics and Studio

SuperDex Robotics uses Z-up coordinates. Its examples and SuperDex Studio's default configuration set gravity along -Z. Set gravity explicitly when creating SuperDex Physics scenes for use with Robotics.

Stepping

Call Step(timeStepSec) in C++ or scene.step(time_step_sec) in Python to run one synchronous step. The public sequence is:

  1. Pre-step callbacks run.
  2. Physics state advances and registered queries update.
  3. Post-step callbacks run.

A time step may vary between calls. Step(0) still runs both callback phases and refreshes registered queries, but it does not advance simulation state or total simulation time.

Use the following accessors after stepping:

MethodResult
GetLastTimeStep() / get_last_time_step()Size of the most recent positive step. A zero step leaves it unchanged.
GetTotalSimulationTime() / get_total_simulation_time()Accumulated simulation time, which may differ from elapsed wall time.
GetSolverStats() / get_solver_stats()Aggregate convergence data from the last step. A zero step clears it.
GetPerformanceStats() / get_performance_stats()Timing and profiling data from the last step.

GetLastTimeStep() is unspecified until a newly created Scene has completed its first positive step.

Callbacks

Pre-step and post-step callbacks run sequentially on the thread that calls Step. Lower numeric priorities run first; callbacks with equal priority have no specified relative order.

Callbacks may safely read or update Scene and actor state during their phase, but they must not change Scene structure by adding or removing actors or constraints. If a callback accesses state shared with other threads, the application must provide synchronization.

Simulation Islands

A simulation island is a dynamic set of actors whose states may be coupled during a step and therefore must be solved together. Constraints and possible two-way interactions can couple actors; as those interactions change, islands may merge or split.

Independent islands limit the size of each numerical system and may be solved concurrently. A large coupled island increases solve cost and leaves less independent work to run in parallel. The Solvers page explains why limiting system size matters for methods whose cost grows faster than the number of unknowns.

SetForceSingleIsland(true) / set_force_single_island(True) forces all actors into one island. This is a debugging aid for investigating partitioning behavior and normally hurts performance.

AsyncScene

AsyncScene is a C++-only wrapper that owns a synchronous Scene and advances it with a long-lived task on the shared SuperDex Physics worker pool. It is useful when physics should progress independently of rendering or other application work. Access the wrapped Scene only from queued commands and step callbacks.

Creating an AsyncScene is fallible and must occur outside the worker pool while at least one worker is available and single-threaded mode is disabled. Starting paused makes initial construction deterministic: queued commands continue to execute, but stepping does not begin until requested.

#include <superdex_physics.h>

int main() {
superdex::Context* context = superdex::CreateContext(2);

superdex::Error createError;
superdex::AsyncScene* asyncScene =
context->CreateAsyncScenePaused("interactive scene", createError);
if (asyncScene == nullptr) {
superdex::DestroyContext(context);
return 1;
}

asyncScene->QueueCommand([](superdex::Scene*) {
// Create or modify scene-owned objects here.
});
asyncScene->WaitForQueuedCommands();

asyncScene->Pause(false);
// The application runs while the scene steps on the worker pool.

// Destroy from outside this AsyncScene's commands and callbacks.
context->DestroyAsyncScene(asyncScene);
superdex::DestroyContext(context);
}

Starting paused lets the example queue initialization before any steps run. WaitForQueuedCommands() ensures that initialization finishes before the scene is unpaused. Applications should propagate or report createError when creation fails.

Safe Access

Use QueueCommand for work that needs a Scene*, and QueueActorCommand for work on an actor identified by ActorHandle. Outside commands and callbacks, retain handles rather than raw actor pointers. An actor command is skipped if its handle no longer resolves when the command is processed.

WaitForQueuedCommands() blocks an outside caller until commands queued before it have executed. It is illegal to call from the simulation task, and it is a command fence rather than a general step-completion primitive.

Async pre-step and post-step callbacks are the other safe places to access the wrapped Scene during stepping. They run sequentially on the simulation task and follow the same priority, shared-state synchronization, and no-structural-change rules as synchronous Scene callbacks.

Time Stepping and Pausing

AsyncStepParams selects a time step in this order:

  1. A custom time-step callback takes precedence when it returns a non-negative value.
  2. Otherwise, fixed mode uses fixedTimeStepSeconds.
  3. Otherwise, dynamic mode uses elapsed wall time clamped to its configured bounds.

Pause(true) requests that automatic stepping stop; queued commands still execute while paused. It is not a barrier proving that an already-running step has completed. RequestStepThenPause() requests one step followed by a pause. That step uses a non-negative custom callback result when available and otherwise uses fixedTimeStepSeconds, even if dynamic mode is configured.

For actor data that must be computed on demand, AsyncScene provides thread-safe query registration helpers. See Queries for query lifetimes and update behavior.