SceneLoader (and glTF) options

Hey folks - we recently made some fairly big changes to SceneLoader:

  1. Introduced module level loading functions, which improves tree shaking (bundle size)
  2. The new module level functions take an options object, which includes options for the load call, options for the specific loader (e.g. glTF), and in the case of glTF options for individual glTF extensions.

You can now make calls this:

const assetContainer = await loadAssetContainerAsync("path/to/model", scene, {
  rootUrl: "path/to", // load function option
  pluginOptions: { // options for loader plugins
    gltf: { // options specifically for the glTF loader
      skipMaterials: true, // a glTF loader option
      extensionOptions: { // options for glTF extensions
        MSFT_lod: { // options specifically for the MSFT_lod extension
          maxLODsToLoad: 1, // a MSFT_lod option
        },
      }
    },
  },
});

This is intended to make configuring model loading easier and more discoverable. Prior to this, you would have had to do something like:

const pluginActivatedObserver = SceneLoader.OnPluginActivatedObservable.addOnce((loader) => {
  if (loader.name === "gltf") {
    const glTFLoader = loader as GLTFFileLoader;
    glTFLoader.skipMaterials = true;
    glTFLoader.onExtensionLoadedObservable.addOnce((extension) => {
      if (extension.name === "MSFT_Lod") {
        const msftLodExtension = extension as MSFT_lod;
        msftLodExtension.maxLODsToLoad = 1;
      };
   });
  }
});

await SceneLoader.LoadAssetContainerAsync(...);

Demo: Babylon.js Playground (babylonjs-playground.com)

Docs: Loading Any File Type | Babylon.js Documentation (babylonjs.com)

If you find any issues or have feedback, feel free to respond to this thread!

14 Likes

Just to confirm then - does this change how general (GLTF) models should best be loaded? I currently use SceneLoader.ImportMeshAsync, would it now be better to use e.g. appendSceneAsync?

If so, then: When importing meshes I currently make use of the ImportMeshAsync progress callback to have a loading bar, as well as the result.meshes it returns (in a .then((result) => {})), can the same be achieved with the new functions? From a quick look at the appendSceneAsync documentation it doesn’t mention anything about that.

Thanks!

  1. if you’re using es6 imports and want to do treeshaking effectively, it’s better to use appendSceneAsync or loadAssetContainerAsync

    If the cost of making the change is not significant, it’s probably better to switch to the new API.

  2. you can pass the onProgress callback like this

    await appendSceneAsync("path/to/model.glb", scene, {
        onProgress: e => {
            console.log(`Loading: ${e.loaded}/${e.total}`);
        }
    });
    
2 Likes

Ah - thank you, I am indeed using es6 imports. I’d looked at the appendSceneAsync documentation but hadn’t noticed the onLoad in the LoadAssetContainerOptions. Hopefully it shouldn’t be too much work to switch it over as it seems to work in a very similar way - and for my use case any reduction in bundled size is important, so if it helps with that it’s worth it :person_shrugging:

appendSceneAsync doesn’t seem to return the appended meshes into a .then((result) => { result.meshes }) after the promise - do you know if there’s any other way to easily get a reference to the appended meshes?

As far as I know, this is the simplest way to get appended meshes from the new API.

const meshes = await loadAssetContainerAsync("your/model/url.glb", scene, {
    onProgress: e => { /*...*/ }
}).then(result => {
    result.addAllToScene();
    return result.meshes;
})
2 Likes

Yes, pretty much what @noname0310 said on all accounts (better for tree shaking / bundle size, prefer loadAssetContainerAsync, and use the options for getting progress).

There is a typo in the above example in that it is both using await and a then, but you probably get the idea.

If there is some reason that ImportMeshAsync works for your use case, but loadAssetContainerAsync does not, please share details. We had not planned to introduce a module level function equivalent of ImportMeshAsync since I believe all its use cases can be hanlded with loadAssetContainerAsync.

Thanks @noname0310 for helping with answers here!

2 Likes

Perfect, thank you (and thank you @noname0310!) for the confirmation on that. I’ll migrate my project over to the new API tomorrow and hopefully it’ll all work fine. As you say, as long as loadAssetContainerAsync is no less performant than ImportMeshAsync then any possible improvements to tree shaking etc. are of course worth it!
I’m all for improvements even if they do require some refactoring as long as they’re announced etc. as this one has been, progress is great!

3 Likes

Hi there, @ryantrem:

I’d like to join this interesting conversation.

What about the “animated start mode (autoplay)” on GLTF/GLB, is that disabled out of the box with the new LoadAssetContainerAsync, or is still needed some “tweaking” the way it was done via plugins in the case of SceneLoader?

//deshabilito autoplay tras carga de ficheros GLTF/GLB
BABYLON.SceneLoader.OnPluginActivatedObservable.add(function (plugin) {
  if (plugin.name === "gltf" && plugin instanceof BABYLON.GLTFFileLoader) {
	plugin.animationStartMode = BABYLON.GLTFLoaderAnimationStartMode.NONE;
  }
});

Thanks for your time.

Animation start mode is one of the top level glTF loader options, so you can just do this:

const assetContainer = await BABYLON.LoadAssetContainerAsync(..., scene, {
  pluginOptions: {
    gltf: {
      animationStartMode: BABYLON.GLTFLoaderAnimationStartMode.NONE,
    },
  },
});

Here is a PG example: Babylon.js Playground

4 Likes

Hi, @ryantrem:

Let me ask you a related doubt.

I’ve read (e.g. here) that the AssetManager indeed makes use of SceneLoader internally.

So, how is it possible that the last has been deprecated but not the former?

Thanks for your time in advance.

For backward compatibility AssetManager is handled a little differently internally, but addMeshTask does accept the same options and behave the same way.

1 Like

Hi @ryantrem:

So, nowadays, going with AssetManager functionality is perfectly appropriate in therms of performance and, mainly, not deprecated.

On the other hand, old code using SceneLoader would have to be ported to another assets importing API. Isn’t it?

Thanks for your time.

SceneLoader is deprecated, but we’re not going to remove it. The deprecation is saying “hey, there is a newer better API for this now that makes it much easier to specify options and is much more tree shakeable, so we recommend you use these newer APIs.” We may port AssetManager to the newer APIs in the future, but really that should be considered an implementation detail of AssetManager.

1 Like

Hi there, @ryantrem:

I perfectly understand your point here and, at the same time, hope that the aforementioned enhancements for the AssetManager by means of the new APIs, are going to be a reality in a near future.

The case is that, as I’m gaining experience with BJS, I’m definitively finding the several “managers” in it more and more useful, in order to not “reinvent the wheel” in the case of generic tasks.

Have a nice day.