I'm trying to make a loading screen with an HTML
progress bar in Three.js. I use a THREE.LoadingManager()
to pass into my GLTFLoader
. I use the onProgress
method to get when every model is loaded, but it doesn't seem to work. It only works at the end, once everything is loaded (so basically it jumps from 0% to 98%). I think it has something to do with me using a wrapper function.
Here is my code:
let loadingManager = new THREE.LoadingManager();
loadingManager.onProgress = function(url, loaded, total){
document.getElementById('loading-progress').value = Math.round((loaded / total) * 100);
document.getElementById('loading-progress-label').innerHTML = `${Math.round((loaded / total) * 100)}%`;
}
let loader = new THREE.GLTFLoader(loadingManager);
function load(asset, pos=[0, 0, 0], rot=[-Math.PI / 2, Math.PI, Math.PI], scale=[5, 5, 5], appendTo){
loader.load(`Assets/${asset}.glb`, function(object){
object.scene.position.set(pos[0], pos[1], pos[2]);
object.scene.rotation.set(rot[0], rot[1], rot[2]);
object.scene.scale.set(scale[0], scale[1], scale[2]);
object.scene.name = asset;
scene.add(object.scene);
if(appendTo != undefined){
appendTo.push(object.scene);
}
});
};
//Decoration
load('platform_grass', [-5, 4, -0.5]);
load('platform_grass', [-2, 3, -0.5], undefined, [5, 10, 5]);
load('rock_largeA', [1, 3, -1], undefined, [2, 2, 2]);
load('plant_bushDetailed', [-5, 1, -1], undefined, [3, 3, 3]);
load('plant_bushDetailed', [-2, 6, -1], undefined, [3, 3, 3]);
I'm pretty sure the problem has to do with me using the load()
function, rather than manually loading each asset. Any help is appreciated.
Thanks!
The LoadingManager docs actually say:
onProgress — (optional) this function will be called when an item is complete.
Try passing a second callback function to the loader (which will act as your progress callback).
Here is what works for me:
const loadingManager = new THREE.LoadingManager()
const loader = new THREE.GLTFLoader(loadingManager)
loader.load(
gltfPath,
// on load handler
async (gltf) => {
// Do whatever you want with the glTF
},
// on progress handler
(xhr) => {
const progressXHR = xhr.loaded / xhr.total
// ...
}
)
Here is what your code would look like when using the approach above:
const loadingManager = new THREE.LoadingManager();
const loader = new THREE.GLTFLoader(loadingManager);
let loadedCount = 0;
const toLoad = [
{ asset: 'platform_grass', pos: [-5, 4, -0.5] },
{ asset: 'platform_grass', pos: [-2, 3, -0.5], rot: undefined, scale: [5, 10, 5] },
{ asset: 'rock_largeA', pos: [1, 3, -1], rot: undefined, scale: [2, 2, 2] },
{ asset: 'plant_bushDetailed', pos: [-5, 1, -1], rot: undefined, scale: [3, 3, 3] },
{ asset: 'plant_bushDetailed', pos: [-2, 6, -1], rot: undefined, scale: [3, 3, 3] },
];
function load(asset, pos=[0, 0, 0], rot=[-Math.PI / 2, Math.PI, Math.PI], scale=[5, 5, 5], appendTo) {
loader.load(`Assets/${asset}.glb`, (object) => {
loadedCount++;
object.scene.position.set(pos[0], pos[1], pos[2]);
object.scene.rotation.set(rot[0], rot[1], rot[2]);
object.scene.scale.set(scale[0], scale[1], scale[2]);
object.scene.name = asset;
scene.add(object.scene);
if (appendTo !== undefined) {
appendTo.push(object.scene);
}
}, (xhr) => {
const progress = Math.round(((xhr.loaded / xhr.total + loadedCount) / toLoad.length) * 100);
document.getElementById('loading-progress').value = progress;
document.getElementById('loading-progress-label').innerHTML = `Loaded ${progress}%`;
});
};
toLoad.forEach((item) => load(item.asset, item.pos, item.rot, item.scale));