Environment
SUMMARY
The Environment holds everything a scene can contain: the models, the material and background pools, the lights, and the camera. You fill it with add(), the randomizer decides what each scene does contain, and the generator renders it.
Import
from telekinesis.illusion import config
from telekinesis.illusion.core.environment import EnvironmentConstructor
environment = Environment() # or Environment(asset_dir="path/to/assets")| Parameter | Type | Default | Description |
|---|---|---|---|
asset_dir | str | Path | None | None | Base directory for relative asset paths, such as surface-imperfection maps. None resolves it with resolve_asset_dir() (see Asset directory). Nothing is registered from it automatically. |
Constructing an environment resets the process-wide Blender scene, so keep one environment per process. It starts empty: no camera, no lights, no models.
Register Everything with add()
Every kind of resource is described by a config from telekinesis.illusion.config and registered the same way:
from telekinesis.illusion.utils.assets import resolve_asset_dir
assets = resolve_asset_dir()
environment.add(config.CameraConfig(image_width=640, image_height=480))
environment.add(config.LightConfig("key", "AREA", location=(0.0, 0.0, 1.2), properties={"power": 80.0, "size": 0.4}))
environment.add(config.ModelConfig.from_path(assets / "models/mechanical_parts/gearwheel_1.glb", model_name="gear", category_id=1))
environment.add(config.MaterialConfig.discover(assets / "materials")) # a list: every material set, grouped by folder
environment.add(config.BackgroundConfig.discover(assets / "hdris")) # a list: every .hdr/.exr, grouped by folderadd() accepts a single config or any iterable of them, and returns the normalized config (or a list of them). What happens on registration depends on the type:
| Config | On add() |
|---|---|
CameraConfig | Creates the camera. Exactly one is allowed; a second raises ValueError. |
LightConfig | Creates the light immediately. Get it back with get_light(name). |
ModelConfig | Registers the model and its annotation category. The mesh is imported later, when the generator prepares the instance pools. |
MaterialConfig | Adds the material set to the pool. Textures load only when a MaterialRandomizer first picks it. |
BackgroundConfig | Adds the HDRI to the pool. It is activated only when a BackgroundRandomizer picks it. |
Add a camera
There is no default camera. get_camera(), and therefore rendering, raises RuntimeError until a CameraConfig has been added. CameraConfig() with no arguments gives a 720 × 720 camera.
Models
gear = config.ModelConfig.from_path(
assets / "models/mechanical_parts/gearwheel_1.glb",
model_name="gear", # name used by randomizers; defaults to the file stem
category_name="gear", # COCO category; defaults to model_name
category_id=1, # defaults to the next free ID
groups="parts", # optional selection groups
)| Field | Default | Description |
|---|---|---|
model_path | required | .glb, .gltf, .fbx, .stl, .usd/.usda/.usdc, .obj, .ply or .dae. |
model_name | file stem | Unique name. Randomizers refer to the model by it. |
category_name | model_name | COCO category. "distractor" renders the model but leaves it unannotated. |
category_id | next free ID | COCO category ID, >= 0. Reusing a category_name reuses its ID. |
active_in_simulation | False | Whether physics moves the model. Containers stay False. |
collision_shape | "CONVEX_HULL" | BOX, SPHERE, CAPSULE, CYLINDER, CONE, CONVEX_HULL, MESH or COMPOUND. Use MESH for containers so parts can fall inside. |
scale | 1.0 | Uniform scale or (x, y, z), all > 0. |
preprocess_model | True | Clean up the imported mesh. |
shading | "FLAT" | FLAT, SMOOTH or AUTO_SMOOTH. |
material_preprocessing | "replace" | replace the imported materials or preserve them. |
uv_mapping | "smart" | smart, cube, cylinder or sphere. Use cube for models without UVs that should take texture-based materials. |
groups | () | Selection groups, e.g. "parts" or ["part", "metal_parts"]. A MaterialRandomizer can target models by group. |
ModelConfig(...) takes the same fields directly; from_path() just fills in the name from the file.
Materials and backgrounds: discovery and groups
discover(root) walks a folder and returns one config per asset it finds. Each asset's groups are its parent folders, relative to root, including every ancestor:
assets/hdris/
├── indoor/
│ ├── industrial/boiler_room_1k.exr → groups ("indoor", "indoor/industrial")
│ └── studio/... → groups ("indoor", "indoor/studio")
└── outdoor/day/... → groups ("outdoor", "outdoor/day")A randomizer can then select by group (groups=["indoor"] picks from every indoor HDRI) or by exact name. A material set is a folder containing a <folder name>_Color.png/.jpg texture, plus any other PBR maps in the ambientCG naming scheme.
To register a single asset, or assets that sit directly in the root folder, give the groups yourself:
environment.add(config.MaterialConfig.from_path(assets / "materials/metal/Metal019_1K-PNG", groups="metal"))
environment.add(config.BackgroundConfig.discover("my_hdris", group_prefix="factory"))Names must be unique per type
Two materials, or two backgrounds, with the same name raise ValueError, even when they live in different folders. Pass name= to from_path() to rename one.
Lights
environment.add(config.LightConfig("key", "AREA", properties={"power": 80.0, "size": 0.4, "shape": "DISK"}))
environment.add(config.LightConfig("sun", "SUN", rotation=(0.6, 0.0, 0.8), properties={"power": 2.0, "angle": 0.05}))light_type is POINT, SUN, SPOT or AREA. location is in meters and rotation in radians (lights emit along their local −Z). Which properties are valid depends on the type:
| Property | Light types | Meaning |
|---|---|---|
power | all | Watts (W/m² for SUN) |
color | all | Linear RGB, each in [0, 1] |
radius | POINT, SPOT | Emitter radius in meters |
size, size_y | AREA | Emitter size in meters (size_y for RECTANGLE/ELLIPSE) |
shape | AREA | SQUARE, RECTANGLE, DISK or ELLIPSE |
angle | SUN | Angular diameter in radians, [0, π] |
spot_size, spot_blend | SPOT | Cone angle in radians, and edge softness in [0, 1] |
Fixed values here are the starting point; a LightRandomizer and LightPoseRandomizer vary them per scene.
Camera
environment.add(config.CameraConfig(image_width=820, image_height=640, field_of_view=0.69))| Field | Default | Description |
|---|---|---|
image_width, image_height | 720 | Output resolution in pixels |
focal_length | None | Lens in mm. Mutually exclusive with field_of_view |
field_of_view | None | Horizontal field of view in radians |
clip_start, clip_end | 0.1, 1000.0 | Clipping distances in meters |
pixel_aspect_x, pixel_aspect_y, shift_x, shift_y | 1.0, 1.0, 0.0, 0.0 | Sensor geometry |
location, rotation | None | Initial pose, meters and degrees. Usually left to a CameraPoseRandomizer |
Asset Directory
from telekinesis.illusion.utils.assets import resolve_asset_dir
assets = resolve_asset_dir() # or resolve_asset_dir("path/to/my_assets")resolve_asset_dir() returns the first of:
- the path you pass;
./assetsin the current working directory;assets/of atelekinesis-illusionsource checkout;- the public example assets, downloaded once (about 180 MB) to
~/.cache/telekinesis/telekinesis-illusion/and reused afterwards.
The example assets are organized as models/, materials/, hdris/ and surface_imperfections/. Organize your own assets the same way and the snippets on these pages work unchanged.
Read the Environment
| Method | Returns |
|---|---|
get_camera() | The Camera. Raises RuntimeError before a camera is added |
get_light(name) / get_lights() | One Light, or all of them by name |
get_categories() | The registered annotation categories |
get_assets() | The registered ModelConfigs by model name |
get_models() | Every prepared model instance, keyed <model_name>_INSTANCE_<n> |
get_model_group(model_name) | All instances of one model |
get_models_by_name(name) | Instances whose name contains name: one Model, or a list |
get_visible_model_names() | Instance names visible in the current scene |
get_background() | The live world background |
get_asset_dir() | The resolved asset directory |
The instance accessors return results only after the generator has prepared the environment. Instance pools are sized from the randomizer's maximum counts.
The resource managers are available as environment.model_manager, material_manager, background_manager, light_manager and camera_manager. For example, environment.material_manager.get_names(groups=["metal"]) lists the metal material sets that a randomizer could pick.
Lifecycle
You rarely call these directly; SyntheticDataGenerator does:
prepare(required_instances)imports the model meshes into fixed-size instance pools. Adding a model after this raisesRuntimeError.begin_sample()deactivates every visible instance before the next scene is randomized. Poses and materials are not reset; the randomizer nodes set them again.clean_up()releases the Blender resources. It is safe to call more than once, but the environment cannot be prepared again afterwards.