Is LoadingManager in Three.js onProgress only triggered after completion?

Currently, I am attempting to create a loading screen with an HTML progress bar within Three.js. To achieve this, I am utilizing a THREE.LoadingManager() that I pass into my GLTFLoader. The issue arises when I attempt to utilize the onProgress method to track the loading progress of each model. Unfortunately, it only seems to update at the end when all assets are loaded, resulting in a sudden jump from 0% to 98%. I suspect this behavior may be related to my use of a wrapper function.

Below is the section of code in question:

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]);

At this point, I believe the root of the problem lies in my utilization of the load() function instead of manually loading each asset individually. Any insights or assistance on resolving this would be greatly appreciated.

Thank you!

Answer №1

Read the LoadingManager documentation for detailed information:

onProgress — (optional) this function will be triggered once an item is fully loaded.

Consider adding a secondary callback function to the loader for tracking progress.

Implement the following code snippet to achieve the desired functionality:

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
        // ...
    }
)

Your code structure will resemble the example below using the aforementioned approach:

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));

Answer №2

Make sure to pass the onProgress function as the second parameter to the loader.

For more information, refer to

function onProgress(url, loaded, total){
    document.getElementById('loading-progress').value = Math.round((loaded / total) * 100);
    document.getElementById('loading-progress-label').innerHTML = `${Math.round((loaded / total) * 100)}%`;
}

function load(asset, pos=[0, 0, 0], rot=[-Math.PI / 2, Math.PI, Math.PI], scale=[5, 5, 5], appendTo){
    const assetUrl = `Assets/${asset}.glb`;
    loader.load(assetUrl, 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);
        }
    }, function(xhr){
       onProgress(assetUrl, xhr.loaded, xhr.total);
    }, function(error){
       // handle errors here
    });
};

I hope this explanation is helpful for you!

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

The functionality for selecting text in multiple dropdown menus using the .on method is currently limited to the first dropdown

Having trouble selecting an li element from a Bootstrap dropdown and displaying it in the dropdown box? I'm facing an issue where only the text from the first dropdown changes and the event for the second dropdown doesn't work. <div class="dr ...

The input field will be in a read-only state if it contains a value from the database. However, it will be editable if the value

Hello everyone, I am a newcomer to this community and would greatly appreciate your help. I am encountering an issue with the following lines of code: <input type="text" class="form-control" id="fines" name="fines&quo ...

AngularJS toaster doesn't seem to be appearing

I have integrated the angularjs toaster library into my project. Here are the necessary references: https://github.com/jirikavi/AngularJS-Toaster Added the following references for the library: <link rel="stylesheet" href="bower_components/Angu ...

Create an interactive list with the ability to be edited using HTML and

I'm currently working on a UI scenario that involves a text box field with two buttons beneath it. When the user clicks the first button, a popup will appear prompting them to input an IP address. Upon submitting the IP address in the popup, it will b ...

Convert the 'value' attribute in HTML into a clickable link

Is there a way to make the output value of an HTML input field into a clickable link? Right now, it appears as follows: <input type="email" class="form-control" name="contactEmail" value="<?php echo $row_rsContactD ...

Generate a new subprocess and terminate it once the operation has been initiated

Using child processes in the following way: var exec = require('child_process').exec; var cmd = 'npm install async --save'; exec(cmd, function(error, stdout, stderr) { console.log('stdout: ' + stdout); ...

Retrieving a Promise's value for an HTML Element

Hello, I'm new to working with React and JavaScript and could use some assistance. I have a function that returns a Promise that includes an interface. My goal is to access a variable in the interface and use it within <dl><dt>{//string va ...

Angular's parent div height can be adjusted using CSS transitions

I have implemented a CSS transition to create a sliding effect in my pagination. However, I am facing an issue where the height of the containing div changes even when I set the position of the transitioned elements. Here is a snippet of my CSS: .slide { ...

What is the process for redirecting clicked links to a specific page prior to visiting the linked URL?

Question: How do I set up a page so that when a user clicks on any link, they are directed to a page titled query_data.cfm where a database query is executed. Once the query is finished, the user is then redirected to the original link's URL? Current ...

Leverage ng2-charts along with a loading component during the AfterViewInit lifecycle hook

Currently, I am working on a web page that contains various charts. My focus right now is on developing a simple loader as shown below: <div *ngIf="loading === true; else elseBlock" class="container"> <div class="grid-pulse la-3x"> </di ...

The Best Way to Refresh a ReactJS Component in Plain HTML/JavaScript

So here's the situation. I'm diving into creating a React Modal component that can seamlessly integrate with any vanilla HTML / JS file. By generating a bundle.js file using Webpack from my React component, it becomes easier to inject it into a d ...

How to retrieve TypeScript object within a Bootstrap modal in Angular

Unable to make my modal access a JavaScript object in the controller to dynamically populate fields. Progress Made: Created a component displaying a list of "person" objects. Implemented a functionality to open a modal upon clicking a row in the list. ...

Looking for a fully customizable event and booking calendar?

Currently, I am searching for a unique event and booking calendar that provides the ability to customize each cell or day with our desired content. While most calendars only allow for inputting events as text, we require a solution that enables us to add ...

Is it typically required to install or declare anything in your frontend in order for CORS to be permitted?

I am currently facing a cross-origin error in my react/express project. The error message states: Error: A cross-origin error was thrown. React cannot access the actual error object during development. More information can be found at https://reactjs.org/l ...

Utilizing various AngularJS filters with multiple input sources

Looking to enhance my user array filtering process with two input boxes. Here's how the array is structured: $scope.users = [{ id: 1, fname: 'Sophia', lname: 'Smith', email: '<a href="/cdn-cgi/l/email ...

What's the best way to have a Json model automatically rotate within the scene?

Using mouse events, I am able to rotate the ballmesh. First attempt var jsonLoader = new THREE.JSONLoader(); jsonLoader.load('models/ball.json', addJsonToScn); function addJsonToScn(geometry) { var ball = new THREE.BufferGeometry().fro ...

The presence of a Bootstrap addon is resulting in horizontal scrolling on mobile devices within the webpage

I am encountering a peculiar issue with an input-group in my cshtml file which features a Bootstrap addon. The problem arises on mobile devices, where upon focusing on the input field, the page scrolls horizontally to the right, revealing the right margin ...

Exploring the intersection of objects and shader materials in Three.js

My current scene features objects intersected using Lambert material, as showcased in this jsFiddle. Now I'm exploring the idea of switching the material of one particular plane to a Shader material, which would transform the plane into a background ...

Having trouble with the Tap to copy discount code function not working in the Shopify cart drawer?

Our goal is to implement tap to copy functionality for code snippets on our Shopify website. It works seamlessly on the product detail page, but in the cart drawer, it only functions properly after the second page load. https://i.sstatic.net/xzMVY.png ...

Steps to exit browser in WebDriver Sampler in JMeter and halt execution

I have been attempting to close the browser in my Selenium Jmeter last sampler thread, but I keep encountering the following error: INFO c.g.j.p.w.s.WebDriverSampler: WebDriver has been quit. 2024-02-01 22:53:24,989 ERROR c.g.j.p.w.s.WebDriverSampler: Sess ...