Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to wait for THREE.TextureLoader.load() to finish?

I'm working with release R73. My current task is to fill an array with materials. The content of this array is supposed to be used later. That usage depends on all materials to me completely loaded.

By now I loop through an array of JSON information and call this code for every element:

TLoader.load(
        BASE_ODB_URL + jsonMat.pic,
        function (texture) {
            texture.wrapS = THREE.RepeatWrapping;
            texture.wrapT = THREE.RepeatWrapping;
            texture.repeat.set(jsonMat.scaleu, jsonMat.scalev);
            Mat = new THREE.MeshLambertMaterial({
                    map : texture,
                    side : THREE.DoubleSide,
                    name : jsonMat.mname
                });
            THREEMatList.push(Mat);
        },
        function (xhr) {
        }, //onProgress
            function (xhr) {
            Mat = new THREE.MeshLambertMaterial({
                    color : 0xff0000,
                    side : THREE.DoubleSide,
                    name : jsonMat.mname
                });
            THREEMatList.push(Mat);
        } 
    )

TLoader is initialized earlier: var TLoader = new THREE.TextureLoader();

If a material isn't there, when it is needed, I get a fallback material. This was only intended as an error option. Is there a way to wait until .load() finishes?

like image 336
S. Langhammer Avatar asked Feb 08 '23 18:02

S. Langhammer


2 Answers

Threejs already provides a callback for all elements loaded - via the use of a LoadingManager. By default, TextureLoader uses the DefaultLoadingManager:

import {TextureLoader, DefaultLoadingManager} from './three.module.js';

const getTextures = ()=> new Promise((resolve, reject)=>{
  const loader = new TextureLoader();
  DefaultLoadingManager.onLoad = ()=>resolve(textures);
  const textures = [
    "image1.jpg",
    "image2.jpg",
    "image3.jpg"
  ].map(filename=>loader.load(filename));
});

getTextures().then(result=>console.log("We received,", result,"!"));

That's going to wait for all assets to load, though. If you want to be listening for a specific subset of assets loaded, you can do that by constructing a custom LoadingManager and passing it into your TextureLoader to manage different asset bundles separately:

import {TextureLoader, LoadingManager} from './three.module.js';

const getTextures = ()=> new Promise((resolve, reject)=>{
  const manager = new LoadingManager(()=>resolve(textures));
  const loader = new TextureLoader(manager);
  const textures = [
    "image1.jpg",
    "image2.jpg",
    "image3.jpg"
  ].map(filename=>loader.load(filename));
});

getTextures().then(result=>console.log("We received,", result,"!"));
like image 81
Brandel Zachernuk Avatar answered Feb 11 '23 07:02

Brandel Zachernuk


You can also use this simple helper if you need to load multiple textures before rendering your scene :

    /**
     *
     * @param {Array} texturesSources - List of Strings that represent texture sources
     * @returns {Array} Array containing a Promise for each source 
     */
    function getTextures (texturesSources) {
        const loader = new THREE.TextureLoader()
        return texturesSources.map(textureSource => {
            return new Promise((resolve, reject) => {
                loader.load(
                    textureSource,
                    texture => resolve(texture),
                    undefined, // onProgress callback not supported from r84
                    err => reject(err)
                )
            })
        })
    }

Then wrap your code using Promise.all, allowing to fetch the sources in parallel, and fail properly by catching the error.

Example :

const texturesBasePath = '../assets/textures/'
const texturesSRC = [
    'image1.jpg',
    'image2.jpg',
    'image3.jpg',
].map(texture => texturesBasePath + texture)

Promise.all(getTextures(texturesSRC))
    .then(textures => {
        // create your materials, meshs...
        // render the scene
    })
    .catch(err => console.error(err))
like image 43
Lodz Avatar answered Feb 11 '23 08:02

Lodz