GLTFLoader: Flatter node hierarchy

The THREE.Object3D hierarchy that results from GLTF2Loader can be quite deep. There may be quite a few places where a node can be merged with its parent or even eliminiated. An obvious example is the creation of the THREE.Group node to represent the glTF mesh entity, as in e.g.

  • THREE.Scene (glTF scene)
    • THREE.Object3D (root node)
      • THREE.Group (mesh)
        • THREE.Object3D (primitive)

In many (most?) use cases, mesh exists only to hold an array of primitives (and optionally one of associated morph target weights). The THREE.Group will always have the identity transform, so has no influence on its descendants — but because it can have a unique name as well as optional extra and extension fields, it is not a given that it can always be eliminated.

@donmccurdy outlined his own, even deeper hierarchy resulting from a trivial 1-node model, in KhronosGroup/glTF#1065

For the project I’m working on, our team must cope with complex scenes on mobile browsers, with weak CPUs. Any gratutious transform multiplications that can be eliminated from render loops is very valuable to us. Then again, it’s not clear to which degree the user should be able to expect the glTF hierarchy to be mirrored in the resulting scene graph. It seems particularly unpleasant to perform node-collapsing optimization only in some cases (when no optional fields are present) in a loaded model, so that the user has no way of knowing what topology to expect.

I had mentioned the possibility of an optimizing loader option, where the user opts into a mode where they guarantee their mesh structs will never have unique names, extras or extensions, and thus THREE.Group can always be eliminated / merged into its parent. But I don’t know if that’s the three.js way.

Author: Fantashit

1 thought on “GLTFLoader: Flatter node hierarchy

  1. Reopening this – I’m thinking we should actually be a bit less aggressive about flattening while parsing the glTF model. We’re pretty good today about flattening a node that only has one mesh, or merging primitives that can be a single geometry, but that’s getting complex (e.g. to handle userData) and doesn’t help us in cases where the node hierarchy in the original file is already silly, like this recent case:

    screen shot 2019-02-08 at 6 50 16 pm

    We should just naively output something as close as we can to the original file, without merging nodes, meshes, or primitives, hopefully simplifying GLTFLoader a bit. And then at the end (either in the loader, or in a utility method users can call) we have a flatten() method that operates on the result. For example:

    loader.load( 'foo.glb', function ( gltf ) {
      SceneUtils.flatten( gltf.scene );
      scene.add( gltf.scene );
    } );

    @takahirox thoughts?

Comments are closed.