No way to set onLoad callbacks for assets in ColladaLoader

Description of the problem

Currently there is no official way to specify an onLoad callback for assets associated with a Collada model. Many models cannot be rendered properly until assets (like textures) are loaded. It is not a problem for applications that do continuous rendering on a timer. But in my use case (a CAD application) it is, as I only render the scene when it changes.

I ended up with a hack temporarily globally overriding THREE.TextureLoader.prototype.load(). But it would make things a lot easier if TextureLoader (or ImageLoader) instances used internally by ColladaLoader were exposed as properties rather that being sealed inside lambdas, so callbacks could have been set up normally on them.

Or, ideally, the ColladaLoader onLoad callback was called when all assets are loaded and the model is ready to use. Here’s my workaround to illustrate the issue:

// Extend the Collada loader to wait for all textures to load before running
// the onLoad callback.

class ColladaLoader extends THREE.ColladaLoader {

  onTextureLoadOrError(url, onLoad, isOK) {
    // Do steps required on both success or failure of loading a texture.

    DEBUG(`Texture "${url}"`, isOK ? "loaded" : "failed to load");
    delete this.pendingTexUrls[url];
    this.tryCompletingModeLoad(onLoad);
  }

  tryCompletingModeLoad(onLoad) {
    // If there're no loads pending, complete model loading and call "onLoad".

    if (Object.keys(this.pendingTexUrls).length)
      return;
    THREE.TextureLoader.prototype.load = this.origTexLoad;
    onLoad(this.colladaMesh);
  }

  load(url, onLoad, onProgress, onError) {
    // Overriden.

    var loader = new THREE.FileLoader(this.manager);
    loader.setPath(this.path);
    var path = this.path ? this.path : THREE.LoaderUtils.extractUrlBase(url);

    this.pendingTexUrls = {};
    this.origTexLoad = THREE.TextureLoader.prototype.load;

    var that = this;
    loader.load(url, function (text) {

      THREE.TextureLoader.prototype.load = (texUrl, onTexLoad, onTexProgress,
                                            onTexError) => {
        let fullUrl = path + texUrl;
        that.pendingTexUrls[fullUrl] = true;

        function onLoadWrapper(texture) {
          let retVal = onTexLoad ? onTexLoad(texture) : undefined;
          that.onTextureLoadOrError(fullUrl, onLoad, true);
          return retVal;
        }

        function onErrorWrapper(texture) {
          let retVal = onTexError ? onTexError(texture) : undefined;
          that.onTextureLoadOrError(fullUrl, onError false);
          return retVal;
        };

        DEBUG(`Loading texture "${fullUrl}", path: "${path}"`);
        return that.origTexLoad.call(this, fullUrl, onLoadWrapper, onTexProgress,
                                     onErrorWrapper);
      };

      that.colladaMesh = that.parse(text, path);
      that.tryCompletingModeLoad(onLoad);
    }, onProgress, onError);
  }
}

Author: Fantashit

1 thought on “No way to set onLoad callbacks for assets in ColladaLoader

Comments are closed.