3

我正在尝试操作(例如更改位置、缩放、旋转)在 Three.js 中使用 OBJLoader 加载的对象。虽然这很容易做到一次,但我无法弄清楚如何在我想要的时候做到这一点,例如在动画循环期间,或者在初始加载回调之外的任何地方。

这是我的代码:

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;

这里需要注意的关键点是:

  • 我可以加载它并在循环内很好地操作它,并且不会引发错误;
  • 如果我执行上述操作,我会在最后一行收到错误消息,其中指出:Cannot read property 'position' of undefined.
  • 如果我obj在函数外部定义(作为全局),则此错误仍然存​​在,然后相应地引用它。

我已经用 JSON 加载器尝试了类似的代码,得到了相同的结果(我可以在加载中操作它,但之后不能)。

4

1 回答 1

5

加载THREE.js器是异步的,因此最好的解决方法是简单地使用 a Promise,或者使用新的 await。这是承诺的实现。我只是将所有THREE.js加载器代码包装在 Promise 中并resolve在最后调用。然后.then只要异步请求完成就使用来执行。

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

更新reject更正了while的一个小错误onProgress。我的坏,再次阅读 THREE.js 文档并注意到顺序loadurl, onSuccess, onProgress, onError,所以最终应该是url, resolve, () => {}, reject.

于 2018-05-04T13:40:39.507 回答