0

Can anyone please advise as what's the best way to load multiple models (exported from blender to JSON format)?

In my example below, I'm using the static_objects array filled with some pre-defined objects data, and that's also place where all the information about model URLs are stored. These URLs are then passed to the JSONLoader within a for loop. But for some reason I get incorrect references whenever I'm accessing the static_objects array inside the JSONLoader.load method.

See the example below.

var renderer, camera, scene, controls;

/////// JSOn DATA ////
var static_objects = [  
      {  
         name:"ground",
         pos:{  
            x:-45.0, y:-1, z:14.0
         },
         size:20,
         model_url: "obj.moon_ground.json",
      },
      {  
         name:"cylinder",
         pos:{  
            x:-20.0, y:0.0, z:0.0
         },
         size:10,
         model_url:"obj.cylinder.json",
      }
];

var ObjectsToLoad =  static_objects.length || 0;
///////////////////////////


function initRenderer( width, height){
    console.log("  - renderer");
    if(Detector.webgl){
        renderer = new THREE.WebGLRenderer({antialias:true});
    }else{
        renderer = THREE.CanvasRenderer();
    }
    //// container ////
    container  = document.createElement( 'div' );
    document.body.appendChild( container );
    /////////////////////

    renderer.setPixelRatio( window.devicePixelRatio );
    renderer.setSize( width, height );
    renderer.shadowMap.enabled = true;
    renderer.shadowMapSoft = true;
    renderer.setClearColor( 0x142639, 1 );
    ///////////////////////
    container.appendChild( renderer.domElement );
}

function initCamera(width, height){
    console.log("  - camera");
    camera = new THREE.PerspectiveCamera(55, width/height, 1, 100 );
    camera.position.set(17.05217, 8.07079, 0.0);
    camera.lookAt( static_objects[1].pos );
    controls = new THREE.OrbitControls( camera );
}

function InitLights(){
    console.log("  - lights");
    var spot_light = new THREE.SpotLight( 0xC1C1C1, 0.4 );
    spot_light.castShadow = true;
    spot_light.shadowDarkness = 0.1;
    spot_light.shadowCameraNear = 0.1;
    spot_light.shadowCameraFar = 100;
    spot_light.shadowCameraFov = 45;
    spot_light.shadowMapWidth = 512; 
    spot_light.shadowMapHeight  = 512;
    spot_light.position.set( -1.8, 100, 2.5 );
    spot_light.target.position.set(  static_objects[1].pos.x, static_objects[1].pos.y, static_objects[1].pos.z );
    scene.add(spot_light);
    var c_helper = new THREE.CameraHelper( spot_light.shadow.camera );
    scene.add( c_helper );
    var ambient_light = new THREE.AmbientLight( 0xD0D0D0, 0.25);
    scene.add(ambient_light);
}

function initScene(){
    console.log("  - scene");
    scene = new THREE.Scene();
}


function initObjects(){
    console.log("  - StaticObjects");
    for(var o = 0; o < static_objects.length; o++ ){
        var loader = new THREE.JSONLoader();
        var o_data = static_objects[o];
        loader.load(o_data.model_url, function(){
            console.log("loading object "+ o_data.name ) <----- it always refers to same object /// 
            ObjectsToLoad--;
            //object.scale.set( self.properties.size, self.properties.size, self.properties.size ) ;
        })
    }
}

function initAll(){
    console.log(" initializing:");
    initRenderer(window.innerWidth / 2, window.innerHeight / 2);
    initScene();
    initCamera(window.innerWidth / 2, window.innerHeight / 2);
    InitLights();
    initObjects();
    animate();
}

function animate(){
    window.requestAnimationFrame( animate );
    if(ObjectsToLoad === 0){
        render_all();
    }
}

function render_all(){
        //var timer = Date.now() * 0.001;
        controls.update();
        renderer.render(scene, camera);
}

initAll();

2 Answers 2

1

Stripping the code not relevant to illustrating the problem, here's your original function:

function initObjects(){
    for(var o = 0; o < static_objects.length; o++ ){
        var loader = new THREE.JSONLoader();
        var o_data = static_objects[o];
        loader.load(o_data.model_url, function(){
            console.log("loading object "+ o_data.name ) <----- it always refers to same object /// 
        })
    }
}

That doesn't quite do in JavaScript what you would expect from other languages as var isn't lexically scoped. var is scoped to the function it is defined in and that affects its in-line initialisation, which happens only once.

You effectively wrote:

function initObjects(){
    var loader = new THREE.JSONLoader();
    var o_data = undefined;

    for(var o = 0; o < static_objects.length; o++ ){

        if ( o_data === undefined ) {
            o_data = static_objects[o];
        }

        loader.load(o_data.model_url, function(){
            console.log("loading object "+ o_data.name ) <----- it always refers to same object /// 
        })
    }
}

ES6 solves this with the let and const keywords but you do need to transpile your code in a build process to make that work in all browsers. That though is an entirely different topic, so while you're still using regular var you should always declare the variables at the start of your function to make it a lot clearer what is going on.

Sign up to request clarification or add additional context in comments.

Comments

0

I have solved this by passing an existing function to load method (rather than creating a new one).

function loadObjects(){
    console.log("  - StaticObjects");
    var loader = new THREE.JSONLoader();
    for(var o = 0; o < static_objects.length; o++ ){
        var o_data = static_objects[o];
        loader.load( o_data.model_url, initObject(o) );
    }
}

function initObject(o_id){
    console.log("loading object "+ o_id );
    return function(geometry, materials) {
        geometry.translate( 0.0, 0.0, -2.0 ); 
        mesh =  new THREE.Mesh( geometry, new THREE.MeshFaceMaterial( materials ) );
        mesh.scale.set( static_objects[o_id].size, static_objects[o_id].size, static_objects[o_id].size ) ;
        mesh.position.set(  static_objects[o_id].pos.x, static_objects[o_id].pos.y, static_objects[o_id].pos.z );
        mesh.traverse( function( node ) { if ( node instanceof THREE.Mesh ) { node.castShadow = true; node.receiveShadow = true;  } } );
        ObjectsToLoad--;
        scene.add(mesh);
    }
} 

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.