Lite: Confusion about createNodeTransform and negative scaling

Repro (set newRoot to false for the default behavior)

I’m loading gltf models which set the root x-scale to -1. I believe that’s to correct the handedness of the model vs the engine. If I parent that root to a new transform node, the matrix propagation ends up clearing out the -1, resulting in inverted mesh textures.

I’m not sure if it’s actually bug, or I’m just using it wrong. Thanks!

This is a Babylon Lite setParent() bug/limitation.

The problem occurs because setParent() tries to preserve the world transform by calculating a new local matrix and decomposing it back into position, rotation, and scale.

However, mat4Decompose() explicitly does not preserve mirrored matrices:

Negative determinant matrices are not specially handled; the returned scale is always non-negative.

It calculates each scale component with Math.hypot(), so -1 becomes 1.

That explains why newRoot = false works and why using setParent() causes the model to become mirrored.

Workaround: When the new parent has an identity transform, establish the relationship directly instead of using setParent()

gltfRoot.parent = newRoot;
newRoot.children.push(gltfRoot);

Set up the relationship before adding the hierarchy to the scene.

When reparenting from an existing parent, also keep the children arrays synchronized:

const previousParent = gltfRoot.parent;

if (previousParent && "children" in previousParent) {
    const children = previousParent.children;
    const index = children.indexOf(gltfRoot);

    if (index !== -1) {
        children.splice(index, 1);
    }
}

gltfRoot.parent = newRoot;

if (!newRoot.children.includes(gltfRoot)) {
    newRoot.children.push(gltfRoot);
}

The engine-side fix should preserve the sign of the matrix determinant during decomposition – either inside mat4Decompose() or through a signed decomposition used specifically by setParent(). Since setParent() promises to preserve the complete world transform, losing the reflection violates that contract.
cc @Deltakosh

Yes good point I;ll fix that!