在THREE.js中取消将对象加载到场景中

canceling loading an object into a scene in THREE.js

本文关键字:加载 对象 THREE js 取消      更新时间:2023-09-26

所以我有一个THREE.js场景,我必须取消上传文件到场景。在我的页面中,我可以上传多个可能的文件,但我正在尝试设置它,以便您每次只能上传一个文件以在场景中查看。我使用angular来实现我的三个js场景和画布本质上是一个工厂,因为我一次只需要一个画布的实例。这里是该工厂的基本代码(针对这个特定的问题),顺便说一下,我使用的是Angular,这就是为什么在开头使用app.factory和注入PartLibrary的原因,但这无关紧要。您只需要知道PartLibrary.load_part_stl()只是一个函数,它通过ajax调用来获取要加载的stl文件的url::

app.factory('ModelViewer', ['PartLibrary', function(QuoteLibrary){
    var modelViewer = {
        model_material: {},
        part_model: {},
        part: {}, 
        scene: {},
        camera: {},
        renderer: {},
        loader: {},
        manager: {},
        id: {}
    };
    modelViewer.destroy_old = function(canvas_selector){
        cancelAnimationFrame(this.id);
        this.scene = null;
        this.renderer = null;
        this.camera = null;
        this.part_edges = null;
        this.part_model = null;
        this.loader = null;
        this.manager = null;
        $('canvas').remove();
    }
    modelViewer.init = function(part, canvas_selector){
        var self = this;

        // distroy any old three js canvases
        self.destroy_old(canvas_selector);
        self.part = part;
        self.initiated = true;
        var container = $(canvas_selector)[0];
        self.scene = new THREE.Scene();
        self.camera = new THREE.PerspectiveCamera( 45, $(canvas_selector).width()/$(canvas_selector).height(), 1, 20000 );
        self.scene.add( new THREE.AmbientLight( 0x4F4F4F ) );

        self.renderer = new THREE.WebGLRenderer( { antialias: true } );
        container.appendChild( self.renderer.domElement );
        PartLibrary.load_part_stl(part.id)
            .success(function(url){
                loadModel(url, self);
            })
            .error(function(){
                console.log("Error loading STL file for 3D view");
            });
        function loadModel(url){
            self.manager = new THREE.LoadingManager();
            self.loader = new THREE.STLLoader(self.manager);
            self.manager.onLoad = function(item, loaded, total){
                $('.model-loading').hide();
            }
            self.loader.load(url, function(geometry){
                self.model_material = new THREE.MeshPhongMaterial();
                self.part_model = new THREE.Mesh( geometry, self.model_material );
                self.scene.add( self.part_model );
            });
        }
        function animate() {
            self.id = requestAnimationFrame( animate );
            render();
        }
        function render() {
            // var timer = Date.now() * 0.0005;
            self.renderer.render( self.scene, self.camera );
        }
        animate();
    }
    return modelViewer;
}]);

所以基本上它的工作原理是有多个可能的画布(取决于canvas_selector),但只有一个活动画布,如旧画布/场景/渲染器等…在每次启动新模型时销毁(因此在init函数的开头使用self.destroy_old)。

如果我在创建新场景之前等待对象加载,它会按预期工作,所以当新场景启动时,只有一个部分出现在场景中。然而,如果一个场景被启动,然后另一个场景被启动,而旧的场景仍在加载该部分,新场景将同时加载新部分和旧部分。尽管在创建新loader、loadermanager和part之前将它们设置为null,但仍然会发生这种情况。

所以有人知道如何阻止加载器加载文件到一个场景,而它正在加载?

我在文档中找不到任何加载器或STLLoader(继承了loader)的本地函数。

现在只是更新任何人,拉请求已经合并,你实际上可以从onprogress回调中取消。

var loader = new THREE.STLLoader();
loader.load('test.stl', function ( geometry ) {
    //stuff here
}, function(url) {
    //logic here
    url.currentTarget.abort()
}) ;   

Ok,所以我必须对三个。js和STLLoader文件进行一些(非常简单的)更改,我从这里得到了取消下载jsonLoader &ImageUtils.loadTexture。总之,这是我自己的解决方案:

在THREE. js中,我必须对THREE进行以下更改。XHRLoader:

THREE.XHRLoader.prototype = {
    constructor: THREE.XHRLoader,
    load: function ( url, onLoad, onProgress, onError ) {
        ...
        var request = new XMLHttpRequest();
        ...
        scope.manager.itemStart( url );
        return request; // I ADDED THIS
    },
    ...
};

然后在STLLoader.js中我添加了:

THREE.STLLoader.prototype = {
constructor: THREE.STLLoader,
load: function ( url, onLoad, onProgress, onError ) {
    var scope = this;
    var loader = new THREE.XHRLoader( scope.manager );
    loader.setCrossOrigin( this.crossOrigin );
    loader.setResponseType('arraybuffer');
    // Here I added request to equal loader since i made load return the original xmlhttprequest
    var request = loader.load( url, function ( text ) {
        onLoad( scope.parse( text ) );
    }, onProgress, onError );
    // here I return the request further back to my own application
    return request;
},

最后在我自己的代码中:

var modelViewer = {
    request: {},
    ...
}
modelViewer.destroy_old = function(canvas_selector){
    if (!$.isEmptyObject(this.request)){
        this.request.abort();
    }
    ...
}
modelViewer.init = function(part, canvas_selector){
    ...
    function loadModel(url){
        ...
        self.request = self.loader.load(url, function(geometry){
        ...
        }
    }
}