GLTFLoader onProgress total is always 0

I don’t know if this is an issue or I’m doing something wrong.
When I load a model using the GLTFLoader, when I try to calculate the progress % in the onProgress function, the “total” value of the ProgressEvent is always 0. I tried with different models and I always get the same result.

Here my onProgress function:
function (xhr) { console.log((xhr.loaded / xhr.total * 100) + '% loaded'); }

xhr.total is always 0 so the result is always “Infinity% loaded”. I saw the same thing on many examples online.
Attached you can also find a screen of the ProgressEvent debugged.
48

/cc

I run into the same issue, and I think this is related to running three.js in a react app using npm. Any ideas?

As stated at GitHub, onProgress() does not properly work for glTF assets. This is a conceptual issue and can’t be easily fixed.

Hi @Mugen87. Thank you for your reply! How do I circumvent this? Do I just drop the onProgress part?

For the record, when I run a webserver with the three.js code alone, things run just fine. Using the same three.js code as part of a react website is what creates the problem. In the react case I run an npm server.

What do you mean with problem? Can’t you load your glTF asset at all?

Yes. It doesn’t load at all. But this only occurs when I use three.js as part of a react site, which I test using npm run start. If I isolate the three.js code and run it using VSCode for example, it runs fine.

Here is my JS code:

// the function to create a new 3D object
function createGLTF(i, model, folder, xPos, yPos, zPos, rotation) {

  let loader = new THREE.GLTFLoader();

  loader.load(
    // resource URL
    `model_lib/${folder}/${model}.gltf`,

    // called when resource is loaded
    function (object) {

      const aabb = new THREE.Box3().setFromObject(object.scene);
      const centerHere = aabb.getCenter(new THREE.Vector3());

      object.scene.position.x += (object.scene.position.x - centerHere.x);
      //object.position.y += ( object.position.y - centerHere.y );
      object.scene.position.z += (object.scene.position.z - centerHere.z);

      if (gridHelp) {
        var boxHelp = new THREE.BoxHelper(object.scene, 0x000000);
        object.scene.add(boxHelp);
      }

      // this little helper allows the park items to be centered in view
      const xPosCenter = xPos - (parkWidth / 2)
      const yPosCenter = yPos - (parkHeight / 2)

      // note zPos & yPos are switched in 2D to 3D exchange
      object.scene.position.set(xPosCenter, 0, yPosCenter);
      const rotationRadian = -(Number(rotation) * Math.PI / 180).toString();
      object.scene.rotation.set(0, rotationRadian, 0);

      // var box = new THREE.Box3().setFromObject(object);
      // var center = new THREE.Vector3();
      // box.getCenter( center );
      // object.position.sub( center )


      scene.add(object.scene);
    },

    // called when loading is in progresses
    function (xhr) {
      console.log(model + ' model is ' + (xhr.loaded / xhr.total * 100).toFixed(2) + '% loaded');

      document.querySelector('#jsFeedback').textContent = model + ' model is ' + (xhr.loaded / xhr.total * 100).toFixed(2) + '% loaded'

      // if an object is 100% loaded
      if ((xhr.loaded / xhr.total * 100) === 100) {
        modelsLoaded++
        document.querySelector('#jsTotalDone').textContent = modelsLoaded

        // if all models are loaded
        if (totalModels === modelsLoaded) {
          setTimeout(function () {
            document.querySelector('#jsPreloader').classList.add('load-done')
          }, 1500)
        }
      }

    }

    ,
    // called when loading has errors
    function (error) {
      console.log('An error happened');
    }
  );

}

And here is the relevant HTML part:

<div id="jsPreloader" class="preloader">
    <h2>Loading test model</h2>
    <p><span id="jsTotalDone">0</span> of <span id="jsTotalLoad">0</span> models loaded</p>
    <p id="jsFeedback" class="small-print"></p>
  </div>

Hello,
it does seems you try to manage in the same time all loaded object in function (xhr)

you should try to manage outside .

create a tab
var progressLoading =[]

progressLoading.push({ way : ‘glb/asse1.glb’,name’garden’,shadow:true }) ;

progressLoading.push({ way : ‘glb/asse6.glb’,name’house’,shadow:true }) ;

create a new event to manage outside .
var assetLoaded =new Event(‘assetLoaded’);

create importObjet(way,name,shadow ){


loader.load( way , function(object){} , function(xhr) , function(error){} )
}

in loader.load( way , function(object){} , function(xhr) , function(error){} )
at the end of function (object) {} put an window.dispatchEvent(assetLoaded);

in function(xhr ) {} use this function only to manage the display of loading progression of only one asset

and manage all the load outside like this
var numberAssetLoaded = 0 ;
window.addEventListener(‘assetLoaded’,(e) => {
numberAssetLoaded += 1;
if ( numberAssetLoaded === progressLoading.length ) {


init();
}
else{
importObjet(progressLoading[numberAssetLoaded].way,progressLoading[numberAssetLoaded].name,progressLoading[numberAssetLoaded].shadow ); }
});

EDIT: i have to explain my solution ( my English is not good ) . the main idea , is to see that in
loader.load( way , function(object){} , function(xhr) , function(error){} )
function(object){} is to manage once object have been loaded
function(xhr){} is to manage when object is loading but not again loaded
function(error){}

so if you try to manage all object loaded in function(xhr){} it could not work very well

Hi @elysium11 and thanks for your reply.

The actual problem is that the xhr.total returns 0 for gltf files. In fact, if you set xhr.total equal to xhr.loaded the model loads just fine as I found out.

I did some searching around and learned about XMLHttpRequest (xhr). Using that I found that for obj files one can use the following code to find out xhr.total (which is set equal to contentLength):

  var contentLength;

  if (xhr.lengthComputable) {
    contentLength = xhr.total;
  } else {
    contentLength = xhr.target.getResponseHeader('content-length');
  }

Unfortunately the header 'content-length' does not exist for gltf files as you can find out using xhr.target.getAllResponseHeaders() so I’m still stuck, even though I isolated the problem.

1 Like

A simple workaround that I found for this is to set the total size of the object first. If you run the loading event in the console, the final size will be output as the last xhr.load value. If you set that as a variable, you will be able to calculate the total load against the predetermined file size:

function loadHomeObjects() {

  // Load a glTF resource
  var objloader = new THREE.GLTFLoader();
  objloader.crossOrigin = true;
  THREE.ImageUtils.crossOrigin = "";

  // set object size
  var totalSize = 15073008;

  // load device object
  objloader.load(

    // 3D object file
    'docs/objects/myObject.glb',

    // Called when the resource is loaded
    function(object) {

      // set device object
      deviceObj = object.scene;

    },

    // called while loading
    function(xhr) {
      console.log('Ajax object data', xhr);
      console.log('Ajax object', (xhr.loaded / totalSize * 100) + '% loaded');
    },

    // report loading errors to console
    function(error) {
      console.log('An error happened loading object:', error);
    }

  );

},
1 Like

I ran into the same problem using gltFloader in my React project

React:^17.0.2
three:^0.133.1

Have you solved this problem ?

example :

@Mugen87

Thanks, it sovle my problem.
I forget response content-length on my serverside.