Skip to content

[docs] Document safe glTF hierarchy reparenting in compat #731

Description

@ryantrem

Is your feature request related to a problem? Please describe.

Babylon Lite intentionally represents each glTF node as a transform node and attaches each primitive mesh beneath it. @babylonjs/lite-compat reconstructs that retained hierarchy as canonical wrappers, then exposes a flattened hierarchy-order list through SceneLoader.ImportMeshAsync(...).meshes / AssetContainer.meshes, including the synthetic __root__ at index 0.

That differs from a common Babylon.js case where a one-primitive glTF node is surfaced as one Mesh carrying the node transform. Babylon.js application code may therefore iterate every returned mesh and assign mesh.parent = applicationRoot. In compat, doing that detaches primitive geometry from the source transform node while preserving only its local transform, so inherited scale, rotation, or translation is lost.

NuminaPRO reported this with hardware-kit assets whose glTF nodes supplied a millimeter-to-meter scale of 0.001. Reparenting every flattened entry turned connector and leg meshes into room-sized geometry outside the camera range. Baking meshes in hierarchy order also failed because ancestor transforms were changed before their children captured them.

Current docs list glTF and SceneLoader support but do not describe this hierarchy difference or safe post-import handling.

Describe the solution you'd like

Add a prominent @babylonjs/lite-compat migration note for glTF import hierarchy and reparenting, preferably in the compat README's loader section and linked from the Lite porting guide.

The documentation should explain:

  • result.meshes is a flattened view of a retained hierarchy, not a list of independent renderable meshes.
  • The list includes the synthetic __root__, source glTF transform nodes, and primitive mesh children; transform-only entries can have getTotalVertices() === 0.
  • Assigning .parent preserves the node's local transform and is unsafe when flattening entries whose required scale/rotation comes from an ancestor.
  • To preserve the hierarchy, reparent only the imported root and use the world-preserving API:
const result = await SceneLoader.ImportMeshAsync("", rootUrl, "model.glb", scene);
result.meshes[0]!.setParent(applicationRoot);
  • To intentionally flatten renderable primitive meshes while preserving their current world transforms, filter to geometry-backed meshes and call setParent() on those leaves rather than changing every hierarchy entry:
const renderableMeshes = result.meshes.filter((mesh) => mesh.getTotalVertices() > 0);
for (const mesh of renderableMeshes) {
    mesh.setParent(applicationRoot);
}
  • If local transforms must also be baked into geometry, first use setParent(applicationRoot) so the full hierarchy-composed world transform is converted into the mesh's new local transform, then call bakeCurrentTransformIntoVertices():
for (const mesh of renderableMeshes) {
    mesh.setParent(applicationRoot);
    mesh.bakeCurrentTransformIntoVertices();
}
  • Calling bakeCurrentTransformIntoVertices() while the mesh is still under its source transform node bakes only the mesh's local transform and does not include ancestor scaling.
  • Lite's current setParent() preserves mirrored transforms, including the synthetic glTF handedness root; this was fixed by fix(scene): preserve mirrored transforms in setParent #456.

Keep this documentation task separate from any future decision to make compat mask the node-plus-child representation or change the native loader.

Acceptance criteria:

  • The published compat documentation contains a clearly discoverable glTF hierarchy/reparenting section.
  • It reproduces the reporter's failure mode: a one-primitive node with ancestor scale loses that scale when every flattened entry is assigned a new .parent.
  • It distinguishes .parent = value (preserve local transform) from .setParent(value) (preserve world transform).
  • It includes safe examples for preserving the hierarchy, flattening renderable leaves, and optionally baking the composed transform.
  • It warns that transform-only entries appear in result.meshes and should not be treated as geometry solely because compat wraps them as Mesh.
  • No loader or compatibility behavior changes are required by this issue.

Discussion

Original forum report:
https://forum.babylonjs.com/t/numinapro-on-babylon-lite-two-live-versions-numbers-and-a-list-of-fixes/64042/2

The detailed report's item 6 describes the exact node-plus-child split, the lost 0.001 parent scale, the failed hierarchy-order baking approach, and requests that the split be documented prominently if it remains.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    compatMissing or broken feature in the compatibility layer (@babylonjs/lite-compat)documentationImprovements or additions to documentation

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions