javascriptthree.jsobjloader

Three.js Manipulate an object from ObjLoader


I am trying to manipulate (e.g. change position, scale, rotation) an object loaded using OBJLoader in Three.js. While this is easy to do so once, I cannot work out how to do this when I want e.g. during the animate loop, or anywhere outside of the initial load callback.

Here is my code:

function loadObj( path, name )
{
    var obj;
    var mtlLoader = new THREE.MTLLoader();
    mtlLoader.setPath( path );
    mtlLoader.load( name+".mtl", function( materials ) {
        materials.preload();

        var objLoader = new THREE.OBJLoader();
        objLoader.setMaterials( materials );
        objLoader.setPath( path );
        objLoader.load( name+".obj", function( object ) {
            obj = object;
            obj.position.x = 20;
            scene.add( obj );
        });
    });
    return obj;
}

var myObj = loadObj( "assets/", "test" );

myObj.position.y = 20;

The key points to note here are:

I've tried similar code with the JSON loader, to the same results (I am able to manipulate it within the load, but not afterwards).


Solution

  • Loaders in THREE.js are asynchronous, so the best way around this is to simply use a Promise, or use the new await. Heres the promise implementation. I simply wrapped all the THREE.js loader code inside the promise and call resolve at the end. Then just use .then to execute whenever the asynchronous request is done.

    function loadObj( path, name ){
      
      var progress = console.log;
    
      return new Promise(function( resolve, reject ){
      
        var obj;
        var mtlLoader = new THREE.MTLLoader();
        
        mtlLoader.setPath( path );
        mtlLoader.load( name + ".mtl", function( materials ){
        
            materials.preload();
            
            var objLoader = new THREE.OBJLoader();
            
            objLoader.setMaterials( materials );
            objLoader.setPath( path );
            objLoader.load( name + ".obj", resolve, progress, reject );
            
        }, progress, reject );
       
      });
      
    }
    
    // This way you can use as many .then as you want
    
    var myObjPromise = loadObj( "assets/", "test" );
    
    myObjPromise.then(myObj => {
      
      scene.add( myObj );
      
      myObj.position.y = 20;
      
    });

    Update Corrected a little mistake where it would reject while onProgress. My Bad, read the THREE.js docs again and noticed that the order of load is url, onSuccess, onProgress, onError, so the final should be url, resolve, () => {}, reject.