How to use async/await/loadAsync when loading a material texture image

I have a .obj, and .mtl with texture image that I want to render.
The scene is static so I want to render by explicitly calling the renderer.render() command once (and not use animate()), after the assets are loaded, and see the texture.
But the texture is not seen.

To wait until the texture is loaded, I:

  • call loadAsync() instead of load(), when loading the .mtl and the .obj files
  • add await before some of the calls (mtlLoader.loadAsync, materials.preload, objLoader.loadAsync)
  • change the file MTLLoader, OBJLoader slightly, compared to the reference (from threejs/releases/three.js-r120/examples/jsm/loaders/MTLLoader.js)
    (the changes are to accommodate the async/await changes)

The changes work, but don’t fully understand why, and would like to know.

Specifically, I have a question regarding placing async/await in one of the functions (MTLLoader.MaterialCreator::create)

In example13_MTLLoader.js, this is the program flow:

MTLLoader.MaterialCreator::preload
  MTLLoader.MaterialCreator::create
    MTLLoader.MaterialCreator::createMaterial_

in example13_OBJLoader.js, this is the program flow:

OBJLoader::parse
  MTLLoader.MaterialCreator::create

I placed async/await in the MTLLoader.MaterialCreator functions: preload, create, createMaterial_.

The rational was to wait for the texture to be loaded before continuing the program.


UseCase1
In this case:

  • The texture is loaded before the program continues. This can be seen in the log where:
    • the material is defined
      objLoader.materials.materials {test2: MeshPhongMaterial}
    • the map image is defined
      objLoader.materials.materials.test2.map.image <img crossorigin=​"anonymous" src=​"http:​/​/​127.0.0.1:​8080/​./​test2.jpg">​
  • But the texture is not displayed

    The code for this case can be seen here

UseCase2
If I take-out the async/await from MTLLoader.MaterialCreator::create

  • The texture is NOT loaded before the program continues. This can be seen in the log where:
    • the materials list is empty
      objLoader.materials.materials {}
    • the material test2 is undefined
      objLoader.materials.materials.test2 undefined
  • But the texture is displayed…

    The code for this case can be seen here
    (for some reason I need to click Run in jsfiddle multiple times, to see the texture, otherwise only a white patch is seen)

The only change between the 2 cases is in the value of MTLLoader.MaterialCreator::doUseCreateWithAsync = false/true
(the behaviour for the 2 cases is the same for single render or animate())

Why when placing async/await in MTLLoader.MaterialCreator::create, the texture is not seen?

Thanks

1 Like

The onLoad() callback of MTLLoader.load() does not wait until the texture is fully loaded. This is done on purpose since there are use cases where a texture definition is sufficient for start rendering. Meaning you don’t want to wait until all resources are fully loaded.

If you want to do so, use LoadingManager instead. Create a custom object of this class and pass it to MTLLoader and OBJLoader's ctor. You can then assign a callback to LoadingManager.onLoad().

1 Like

In my example I’m using
await mtlLoader.loadAsync( mtlFilename ).then( onLoad_mtlLoader )
instead of MTLLoader.load()
Within onLoad_mtlLoader I call:
let objInstance = await objLoader.loadAsync(objFilename);
and then I call onLoad_FileObj_objLoader (which is not async, so no need to await for it)
onLoad_FileObj_objLoader(objInstance);

This is all done before calling renderer.render( scene, camera );

So I expect that by the time that the rendering is called, the texture is fully loaded. This can be seen in here.
Can you show where the code is not waiting?

Regarding LoadingManager, I’m using it but for a different purpose (for adjusting the url)
Can you give an example how to pass in the LoadingManager and how to use the LoadingManager.onLoad(), with async/await?

I could have continued call the rendering and the continue the rest of the program from within the callback onLoad_FileObj_objLoader, but I thought I can use async/await/loadAsync (for better clarity).

(btw: I learned about loadAsync() instead of load() in one of your comments from another question, and I thought async/awit/loadAsync() can work together to replace the callback-hell)

Thank you!