Call variables in load function

Hi. I’m building a scene with multiple similar GLTF models. To optimize my code I would like to set all my models in variables an only call a function name, the path of the model and a visibility boolean with a for loop. I cant turn my head around to make this to work. Could someone please tell me what I’m doing wrong?

let objectList = [
	'Head_Happy',
	'Tongue_Inside',
	'Theets_Regular',
];

let objectModels = [
	"Head_Happy.gltf",
	"Tongue_Inside.gltf",
	"Theets_Regular.gltf",
];

let objectVisibility = [
	false,
	true,
	true,
];

for (let i = 0; i < objectList.length; i++) {
	loader.load( 'objectModels[i]', function( objectList[i] ) {	
		scene.add( objectList[i].scene );
		objectList[i].scene.traverse( function( child ) {
			if ( child.isMesh ) { 
				child.castShadow = true; 
				child.receiveShadow = true;
			}
		} );
		objectList[i].scene.visible = objectVisibility[i];
		render();
	} );
}

it’s quite difficult to understand your question, you may need to rephrase the problem you’re experiencing and what is the result you’re getting… my first thought is that your for loop is excecuting the next iterated load function before the previous model is loaded, a better way to ensure the previous model is loaded before calling the next iteration would be an approach like this…

let i = 0
function loadNext(){
	loader.load( 'objectModels[i]', function( objectList[i] ) {	
		scene.add( objectList[i].scene );
		objectList[i].scene.traverse( function( child ) {
			if ( child.isMesh ) { 
				child.castShadow = true; 
				child.receiveShadow = true;
			}
		} );
		objectList[i].scene.visible = objectVisibility[i];
        i++
        if( i < objectList.length){
          loadNext()
        }
		render();
	} );
}
loadNext()

Sorry, I’ll try to explain in a better and simple way. I understand your approach but it didn’t solve my issue.

Basically I’m loading multiple models in my scene

const loader = new GLTFLoader().setPath( 'models/' );

loader.load( 'head.gltf', function( head ) {	
	scene.add( head.scene );
	render();
} );

loader.load( 'hairs.gltf', function( hairs ) {	
	scene.add( hairs.scene );
	render();
} );

loader.load( 'mouth.gltf', function( mouth ) {	
	scene.add( mouth.scene );
	render();
} );

Everything works, no problem there. But’s thats kinda repetitive (and I have a lot of models to load). So instead I’d like to use a loop

let object = [ 'head', 'hairs', 'mouth'];

let i = 0
function loadNext() {
	loader.load( object[i] + '.gltf', function( object[i] ) {	
		scene.add( object[i].scene );
		i++
		if( i < object.length){ 
			loadNext() 
		}
		render();
	} );
}
loadNext()

and that’s where I’m stuck. I think I’ve made a mistake with the syntax of this line loader.load( object[i] + '.gltf', function( object[i] ) {

(I’m not the brightest light with js)

The code above looks for GLTF file named “objectModels[i]” (this is actual name, with square brackets in it). Maybe you want to load the GLTF file with a name stored in objectModels[i]. In this case, try without the single quotes:

Also, the argument of the call-back function cannot be objectList[i] . It must be a single identifier.

Correct, I missed this…

'objectModels[i]'

It would be…

let object = [ 'head', 'hairs', 'mouth'];

let i = 0
function loadNext() {
	loader.load( object[i], function( o ) {	
		scene.add( o );
		i++
		if( i < object.length){ 
			loadNext() 
		}
		render();
	} );
}
loadNext()
1 Like

An alternative approach, without recursion:

for( let i = 0; i<objectNames.length; i++ )
{
    var filename = ...;
    loader.load( filename, gltf=>loaded(i,gltf.scene) );
}
	
function loaded( index, gltf )
{
  // do whatever you want with the model
  objects[index] = gltf;
  scene.add( gltf );
}

Example:

https://codepen.io/boytchev/full/LYJVJeO

image

1 Like

Thanks to both of you, it worked! The major mistake was calling my variable with objectList[i] in the function (). That wasn’t needed at all.

Have a good day, mine will surely be

1 Like

three is inherently async, all loaders are, how are you not using async/await? nested callbacks have been abolished, no good idea to go back to this pattern as it would complicate matters to no end and create race conditions.

the following will fetch all assets in parallel, no waterfalls, next line everything’s available and you can do with it what you want.

const [tex1, tex2, model1, model2] = await Promise.all([
  new Promise(res => textureLoader.load("foo.png", res)),
  new Promise(res => textureLoader.load("bar.png", res)),
  new Promise(res => gltfLoader.load("model1.glb", res)),
  new Promise(res => gltfLoader.load("model2.glb", res)),
])
...
model1.scene.traverse(o => (o.castShadow = o.receiveShadow = true))
foo.material.map = tex1
scene.add(model2.scene)
...
render()

ps, you can also map urls to make it shorter

const modelUrls = ["Head_Happy", "Tongue_Inside", "Theets_Regular"]
const [tex1, tex2, ...models] = await Promise.all([
  new Promise(res => textureLoader.load("foo.png", res)),
  new Promise(res => textureLoader.load("bar.png", res)),
  ...modelUrls.map(url => new Promise(res =>
    gltfLoader.load(`/assets/${url}.glb`, res)
  ))
])
1 Like