Scene Composition¶
Scene composition is a cold-path contract. Env configs describe the scene with
SceneCfg; backend materializers convert that declaration into the backend’s
native model during initialization.
Contract¶
SceneCfg and TerrainSceneCfg live in src/unilab/base/scene.py:
@dataclass
class TerrainSceneCfg:
generator: TerrainGeneratorCfg | None = None
hfield_name: str = "terrain_hfield"
geom_name: str | None = None
@dataclass
class SceneCfg:
model_file: str
fragment_files: list[str] = field(default_factory=list)
terrain: TerrainSceneCfg | None = None
EnvCfg.scene is the single scene source. Static scenes use
SceneCfg(model_file=...). Procedural terrain scenes combine a robot model,
task fragments, and terrain configuration:
env:
scene:
model_file: src/unilab/assets/robots/go2/go2.xml
fragment_files:
- src/unilab/assets/robots/go2/locomotion_task.xml
terrain:
hfield_name: terrain_hfield
geom_name: floor
The env hands the scene to create_backend(...); it does not call MuJoCo or
Motrix materializers directly.
Backend Dispatch¶
create_backend(...) in src/unilab/base/backend_factory.py routes on backend
type and applies these rules to the SceneCfg:
Static
SceneCfg(model_file=...)with no terrain: load the full static scene (mergingfragment_fileson a cold path when present).SceneCfg(model_file=..., terrain=...): treatmodel_fileas the robot model, assemble the materialized scene on a cold path, and mergefragment_files.scene is None: fail loudly (create_backendraisesValueError).A backend that does not support a
SceneCfgfeature must raise an explicit error. For example, Motrix rough-terrain must fail loudly rather than silently fall back to a flat scene.
MuJoCo Materializer Pipeline¶
For procedural terrain, the MuJoCo backend calls
materialize_hfield_attached_scene(...) in
unisim.backend.mujoco.xml. The cold-path steps are:
TerrainGenerator(terrain_cfg).write_png(...)generates a backend-agnostic heightfield PNG.MjSpec.add_hfield(...)plusworldbody.add_geom(type=mjGEOM_HFIELD, ...)place the terrain.MjSpec.from_file(robot_path)loads the robot spec andspec.attach(...)attaches it under a frame.Each
fragment_filesentry (task sensors, keyframes) is merged into the scene XML.MjSpec.from_string(...).compile()returns a compiledMjModeltogether withterrain_origins(and an optional surface sampler).
The procedural path returns a precompiled MjModel; it does not expose a final
scene.xml path as a contract. Scene context such as terrain_origins is
handed back to the env through backend attributes.
Layer Ownership¶
Layer |
Owns |
|---|---|
Config / registry |
|
Terrain |
Backend-agnostic height matrices, terrain origins, and terrain presets |
Backend materializer |
XML/world assembly, native model compilation, scene artifact cleanup |
Env |
MDP semantics, reset, reward, observation, and cached scene context use |
Cold-Path Boundary¶
Allowed on cold paths:
Reading XML and asset files.
Generating terrain heightfields.
Compiling MuJoCo
MjModelor Motrix scene models.Resolving scene IDs, terrain origins, and scanner handles.
Disallowed on hot paths:
Parsing XML or reading assets during
step,reset, or interval DR.Branching reward or observation logic on raw asset metadata.
Probing backend-private scene methods instead of using explicit contracts.
Regenerating terrain after env construction.
Terrain Contract Evidence¶
The core repository keeps the terrain contract and its materialization tests, but it does not own a production rough-terrain task:
Terrain generator:
unisim.terrain.generatorMuJoCo materializer:
unisim.backend.mujoco.xmlMotrix materializer:
unisim.backend.motrix.sceneContract coverage:
tests/utils/test_xml_utils.py
User instructions are in Procedural Terrain.