Hey guys, struggling to get my head around how to make objects loaded via GLTFLoader available for use in other functions, mainly for animation, timelines etc?
I can assign the model and add to scene no problem, but when trying to access the object in update or animation functions it returns undefined.
Seems like a function scope issue but I could really use a nudge in the right direction!
How do I assign the loaded glb object to a global variable for use later?
Left out render, camera, scene etc for brevity…
import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'
// let model = new THREE.Object3D()
let model
export default class gltfPractice {
constructor(scene) {
this.scene = scene
this.GLTFLoader = new GLTFLoader()
this.createGLTF()
this.update()
}
createGLTF() {
const modelPATH = 'models/model.glb';
this.GLTFLoader.load(modelPATH, (gltf) => {
model = gltf.scene
model.scale.multiplyScalar(50)
model.position.set(200, 840, 0)
model.rotation.y = -15
modelMaterial = new THREE.MeshPhongMaterial({
side: THREE.DoubleSide,
color: 0x000000,
specular: 0x333333,
shininess: 30,
wireframe: true
})
model.traverse(o => {
if (o.isMesh) {
o.castShadow = true
o.material = modelMaterial;
//console.log(o)
}
})
// works ---> this.scene.add(model)
})
// doesn't work ---> this.scene.add(model)
}
update() {
// doesn't work ---> model.position.x += 0.05
}
// how the hell do i assign the model object created by GLTFLoader to a global object for use elsewhere
}
Scope is alright, but keep in mind that loading models is asynchronous (ie. model will remain undefined
until it is fully loaded from a file.)
1 Like
Ah okay. So pop the model in a loading function then init the scene on complete?
Brutally…Could you try adding a boolean flag as
let loaded_model=false;
you can set true after your model.traverse
model.traverse(o => {
if (o.isMesh) {
o.castShadow = true
o.material = modelMaterial;
//console.log(o)
}
})
loaded_model=true;
// works —> this.scene.add(model)
and than check in your update
update() {
if (loaded_model) model.position.x += 0.05
// doesn't work ---> model.position.x += 0.05
}
That seems to have the same issue. If models load async as @mjurczyk has said then all the flag does is update the boolean when the loader runs?
I’m sorry if I misunderstood your problem
But if your update is called multiple times, eg in render () or animate (), as it seems from your position.x+=0.05 (to animate the position i suppose), sooner or later (after loading) will be true
No problem, I get what you mean. I think in this case I need the glb model object to be fully loaded and available before the update/animation script runs from the sound of things.
exactly
as mjurczyk said, as long as it is not loaded it will remain undefined, the condition in the update avoids you the exception until the end of the loading.
Mine is not a clean solution, I’m new to the community, but I had the same problem as you and I solved it as I told you … I hope it will be useful, surely a more experienced user will give you a more performing answer
Thanks for the advice man. This must be a common enough problem!
I’ve found another work around by using timeouts but it feels a bit hacky and will probably cause headaches as the project gets bigger. I’m going to have to do a deep dive on callbacks and best practice here I think.
this.createModel()
setTimeout( () => { this.animate() }, 1000)
setTimeout( () => { this.update() }, 1010)
I do not recommend this solution
maybe it can work with very small files, but the first internet slowdown will be undefined
2 Likes
Just a general note - model
is a variable by itself, and it can have a falsy / truthy value.
Doing this:
let model;
function loadModel() {
// Load the model here
}
function animate(){
if (model) {
// This will be executed only after model is loaded
model.rotation.y += 0.01;
}
}
should work just fine for a small app. Separate loaded
variable may not be necessary.
3 Likes
Have a look at loading manager, using this you can load all models and run animate/update when loading is finished, then to access a model at any point after this you can do var model = scene.getObjectByName(“objectName”)
1 Like
Loading Manager was definitely the solution here, thanks.
For anybody else looking, here’s how it worked for me -
const loadingManager = new THREE.LoadingManager()
loadingManager.onLoad = () => {
percentComplete = 0;
progress.style.width = 0
JSLoader.classList.add( 'fade-out' )
JSLoader.addEventListener( 'transitionend', this.onTransitionEnd )
this.modelTimeline()
this.update()
clearInterval( frameID )
}
// loaders
this.GLTFLoader = new GLTFLoader(loadingManager)
1 Like