Skip to content

Coordinate Systems

SAGE uses BabylonJS as its rendering engine, which operates in a left-handed coordinate system. Most 3D content is authored in right-handed tools (Blender, glTF standard). This page explains how SAGE handles the conversion transparently.

How BabylonJS Handles glTF Imports

When BabylonJS imports a .glb or .gltf file into a left-handed scene, it creates a hidden __root__ transform node at the top of the imported hierarchy. This node has scaling = (-1, 1, 1), which mirrors the X axis to convert from right-handed to left-handed coordinates.

You generally don't need to think about __root__. It's an implementation detail of BabylonJS's loader. All nodes in the imported scene become children of __root__, so their local coordinates are in the mirrored (right-handed) space, while their world-space positions are correct left-handed values.

How SAGE Handles Spawn Points

Spawn points are nodes in your scene file tagged with a spawn custom property. They live inside the scene's __root__ hierarchy. When SAGE reads spawn points, it extracts world-space (absolute) position and rotation. Scaling uses the spawn point's own local value since it represents artist intent, not the coordinate conversion.

Entity nodes are created at the scene root — outside __root__. Since they have no parent, local equals world, so the absolute values apply directly. This ensures entities appear exactly where the spawn point is, facing the correct direction.

How Entity Meshes Work

When an entity has a GLB mesh, SAGE imports it and the loader creates its own __root__ node for that import. This is parented under the entity node at the scene root:

scene root
├── __root__ (scene GLB — handedness conversion)
│   ├── level geometry
│   ├── lights
│   └── spawn points

├── entity-1 (world-space position from spawn point)
│   └── __root__ (entity GLB — handedness conversion)
│       └── meshes

├── entity-2 (primitive — no GLB, no __root__)
│   └── box mesh

Each GLB import gets exactly one __root__ handedness conversion. Primitive meshes (box, sphere, capsule, cylinder) don't need conversion since they're created natively in BabylonJS's left-handed space.

The rightHanded Option

If your scene file was exported for a right-handed coordinate system, you can set rightHanded: true in your level config:

yaml
scene:
  path: models/level.glb
  rightHanded: true

This sets scene.useRightHandedSystem = true, which tells BabylonJS to interpret all coordinates as right-handed. When enabled, the glTF loader does not create __root__ — no conversion is needed since both the scene and the format are right-handed.

Troubleshooting

Entity appears at mirrored X position: The spawn point's position is being read in local space instead of world space. This is a SAGE bug — spawn points should always use getAbsolutePosition(). Check that you're using an up-to-date version.

Entity mesh appears mirrored or inside-out: The entity's GLB __root__ may be missing or stripped. Each GLB import needs its own __root__ for correct handedness conversion.

Entity mesh appears correct but at the wrong rotation: Same root cause as the position issue. Rotations under a mirrored parent behave differently — SAGE extracts absoluteRotationQuaternion to handle this correctly.

Released under the MIT License.