Is there a way to dynamically add and/or remove a loaded gltf (model from blender, for example) into the scene later on (based on user/game events or anything like that)? I’ve tried the following already:
let pizzaMonster = new THREE.Object3D();
loader.load( 'assets/pizzaMonster.glb', function ( gltf ) {
pizzaMonster = gltf.scene;
//scene.add( pizzaMonster );
}, undefined, function ( error ) {
console.error( error );
} );
And then later in the code, maybe when the camera controls intersects some area, I can run:
scene.add(pizzaMonster);
This does not work - my scene remains blank, but it also does not throw an error in the browser. Any ideas?
Ideally, I can load all of my assets into different variables and then add/remove them at any point based on other events. How is this done?
This would work to simply hide it, but my ultimate goal is to update the map of my scene by adding a new room to the other side of an existing room once the user (camera controls) gets to a certain mark in the existing room. So this would require (I think) dynamic loading and positioning. Just making it not visible might not be enough.
what you have there is called a race condition. you load something and hope it’s there at a later point. threejs is inherently async and trying to counter that with hopes and good luck is futile — it will all come down and crash. javascript has async/await and promises, you should not rely on function callbacks.
this code for instance is an anti pattern, it was once was named “callback hell”, i would suggest you don’t do that unless you want everything to be accidental and random.
loader.load(url, (gltf) => {
foo.add(gltf)
...
instead load your assets first, then you can mount them whenever you want
async function app() {
const [font, hdri, level1, level2] = await Promise.all([
fontLoader.loadAsync("/font.json")),
rgbeLoader.loadAsync("/warehouse.hdr")),
gltfLoader.loadAsync("/level1.glb")),
gltfLoader.loadAsync("/level2.glb")),
// ...
])
scene.add(level1)
scene.background = hdri
const font = new TextGeometry('hello', { font })
// ...
you could in theory make sections of your app async and await them, for instance a game with 3 levels should only load level 1 initially and level 2 and 3 come after and await their own critical assets.
Three has a LoadingManager that you can pass into the gltf loader…
const manager = new THREE.LoadingManager()
manager.onLoad = () => {
init()
}
const loader = new GLTFLoader(manager)
let pizzaMonster
loader.load( 'assets/pizzaMonster.glb', function ( gltf ) {
pizzaMonster = gltf.scene;
}, undefined, function ( error ) {
console.error( error );
} );
this way the manager will wait till any and all models are loaded through gltf loader and call init once loading is complete meaning at this point you can add and remove models in the way you’re doing already because you’ve waited for them to load…
as for loadingmanager, imo that’s useful for loading indications but coding like that seems indirect, maybe even fragile. you don’t have to know threejs specifics to handle async, vanilla javascript async/await imo is more important to know when starting with threejs than how meshes and shaders work since three is fundamentally async and race conditions will will always come back to bite unless there’s a solid foundation.