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/__init__.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_mujoco_hfield_attached_scene(...) in
src/unilab/base/backend/mujoco/xml.py. 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.
Go2 Rough Terrain Evidence¶
The current procedural terrain user-facing path is Go2 rough terrain:
Env owner:
src/unilab/envs/locomotion/go2/rough.pyTerrain generator:
src/unilab/terrains/terrain_generator.pyMuJoCo materializer:
src/unilab/base/backend/mujoco/xml.pyMotrix materializer:
src/unilab/base/backend/motrix/scene.pyOwner YAMLs:
conf/ppo/task/go2_joystick_rough/mujoco.yaml,conf/ppo/task/go2_joystick_rough/motrix.yaml
User instructions are in Procedural Terrain.