Sign in to follow this  
dbawel

[SOLVED] How to add OBJ files dynamically in scene

Recommended Posts

Hello,

I hope someone knows how to do this - I need to have a scene running, and also a scanner beside it. As scanned heads are added to the scene as OBJ files, I need them loaded and rendered in the scene without disposing of the scene. The paths to the new files must be added to an array.

I currently have a test scene online, and am loading 2 heads at the start. Then I have a left mouse event add a new file path and folder to the array - but don't know how to tell the loader to load again from the new path. I can delete any path in the array which has already had an OBJ loaded from it, as the path will only be used once. The online scene is below - it might need to be loaded twice the first time.

http://qedsoft.com/SBSW/10_heads/index.html

The main JS code is as follows:

Quote

 

    var canvas = document.getElementById("renderCanvas");
    var engine = new BABYLON.Engine(canvas);
    var scene = new BABYLON.Scene(engine);
    
    scene.clearColor = new BABYLON.Color3(1, 1, 1);
    
    var camera = new BABYLON.FreeCamera("camera", new BABYLON.Vector3(-2.15, 0, -3.2), scene);
    camera.MIN_VALUE = 0.01
    
    var light = new BABYLON.PointLight("light", new BABYLON.Vector3(0, 5, -5), scene);

var pathsToLoadFrom = ["./assets/obj_files_10/", "./assets/obj_files_11/"];
    
document.addEventListener("mousedown", push_path, false);

function push_path(evt) {
    pathsToLoadFrom.push("./assets/obj_files_12/");
}


    var mesh_pos = -5;

    
    if (pathsToLoadFrom.length > 0) {
        
        
    for (var p = 0; p < pathsToLoadFrom.length; p++) {
        

    
    
       let loader = new BABYLON.AssetsManager(scene);
        loader.useDefaultLoadingScreen = true;  // Set to false to remove the default loading
        let mesh_loaded_task = loader.addMeshTask("mesh" + p, "", pathsToLoadFrom[p], "mesh.obj");
        
    
        
        mesh_loaded_task.onSuccess = function (task) {
             task.loadedMeshes.forEach(function(m) {
                console.log("Loaded!");
                m.position.x = mesh_pos += 0.5;
                m.rotation = new BABYLON.Vector3(0, 0, 179.05);
                console.log(m);
             });
        };


        
        loader.onFinish = function() {
            engine.runRenderLoop(function () {
                scene.render();
            });
        };

        loader.load();
        
    }
    }

 

I appreciate it if you can assist.

Thanks,

DB

Share this post


Link to post
Share on other sites

you can dynamically load OBJ files on the fly - I don't think the MTL will be loaded here yet:

BABYLON.SceneLoader.ImportMesh(
  '',
  'folder/',
  'file.obj',
  scene,
  loadedMeshes => {
    loadedMeshes.forEach(loadedMesh => {
      console.log(`loaded '${loadedMesh.name}'`, loadedMesh)
      this._loadedMeshes.push(<BABYLON.Mesh> loadedMesh);
    });
  }
);

 

Share this post


Link to post
Share on other sites

Hi @brianzinn -

I've tried to implement several ways, and have not been successful yet. I can't find this in the documentation, but the logic makes sense. So as long as I push a loaded mesh into the loadedMeshes array, it should display while a scene is already rendering? And any thoughts on loading the MTL? Thanks for helping, I really need it right now. I'm stuck at a Starbucks off the freeway which is now closed due to mudslides.

If there's any additional info, I'd be grateful - especially how I might load the MTL file if it doesn't load automatically. However, I still need to get the OBJ files loaded,and my problem is that the path will come from an array.

Thanks much - I'll keep trying.

DB

Share this post


Link to post
Share on other sites

Hi @brianzinn

Everytime I push the new mesh, I receive a syntax error on the following line:

this._loadedMeshes.push(<BABYLON.Mesh> loadedMesh);

No matter how I set the syntax. What am I doing wrong?

DB

 

Share this post


Link to post
Share on other sites
this.loadedMeshs = [];
//If you want to keep object scope:
var self = this;

BABYLON.SceneLoader.ImportMesh(
  '',
  'folder/',
  'file.obj',
  scene,
  (meshes)=> { 
    for(var i=0; i<meshs.length; i++){
      var mesh = meshes[i];
       console.log(mesh);
       self.loadedMeshs.push(mesh);
     }
  }
);

That syntax would never of worked.

Share this post


Link to post
Share on other sites

My example was copied from working code.  Both ways should work, do you have an array declared to push to - this first line in @Pryme8's example?

You don't need "self" with fat arrow :) So, I don't believe that was why it was not working...
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions
"An arrow function does not have its own this; the this value of the enclosing execution context is used."

Share this post


Link to post
Share on other sites

Yup, which would have scope of the window then... this syntax is wrong.

this._loadedMeshes.push(<BABYLON.Mesh> loadedMesh);

is all sorts of wrong logic... first why would you push to the array your iterating through the same mesh that you are looping through?  Sounds like infinite recursion to me.
 

Share this post


Link to post
Share on other sites

I think it's unfair to say the syntax is wrong and there is "all sorts of wrong logic" without trying it yourself.  Perhaps you are not familiar with how classes work to encapsulate logic.    Here is the same code unchanged and working:
https://www.babylonjs-playground.com/#C02KIC

25 minutes ago, Pryme8 said:

Yup, which would have scope of the window then...

I think you are confusing window scope and enclosing scope.  I would just refer you to same MDN link :) 

@dbawel - the callback is a good place to rotate/position/etc if you can do it all there, but if you want to change it later just have a variable with external scope and it will be available afterwards or you can use a class like I did in the PG, but note that the load is asynchronous, so not available right after ImportMesh.

Cheers.

Share this post


Link to post
Share on other sites

dude, I can prove it to you... first off
BABYLON.SceneLoader.ImportMesh
Is in window scope...


and your first code was wrong then because:
 

class BotLoader {
constructor(folder, file) {
this.meshes = []
console.log(`loading" ${folder}${file}...`);
BABYLON.SceneLoader.ImportMesh("", folder, file, scene, meshes => {
meshes.forEach(mesh => {
console.log(`mesh: ${mesh.name}`, mesh)
this.meshes.push(mesh);
})
});
}
}

IS WAY DIFFERENT!

and yes now you have
encapsulate  scope... with this set up... which points the this to BotLoader instance...

your original post had its scope pointing to the window though...  

https://www.babylonjs-playground.com/#C02KIC#1

Argue with me about scope some more, its fun...

Share this post


Link to post
Share on other sites
2 minutes ago, dbawel said:

Hey @Pryme8

I can set transforms there, but can't define how to incrementally position each head after I add each one (at a different time) so the models don't overlap.

Thanks,

DB 

have a running value variable outside of the scope of the function so it does not get overwritten.

Share this post


Link to post
Share on other sites
8 minutes ago, brianzinn said:

The code snippet to answer the question is 100% identical! :) But I have to concede that the full context does change things.  Glad the problem is solved. Cheers.

not its not, the scope is different... the second you added an encapsulating object it changed the way the syntax reacted.

That's like saying a submarine performs the same in as out of the water...

I can also show proof of breaking the fat arrow's scope depending on when you define the function but that's a whole other discussion.

Share this post


Link to post
Share on other sites

Hey @Pryme8

I can't make a playground scene because I can't call the OBJ files or more specifically the paths to them. But the scene and code are below:

http://qedsoft.com/SBSW/10_heads/index.html

The code is rough test code, and I have it calling a new path and mesh on a mousedown event.

Quote

 

    var canvas = document.getElementById("renderCanvas");
    var engine = new BABYLON.Engine(canvas);
    var scene = new BABYLON.Scene(engine);
    
    scene.clearColor = new BABYLON.Color3(1, 1, 1);
    
    var camera = new BABYLON.FreeCamera("camera", new BABYLON.Vector3(-2.15, 0, -3.2), scene);
    camera.MIN_VALUE = 0.01
    
    var light = new BABYLON.PointLight("light", new BABYLON.Vector3(0, 5, -5), scene);

    var mesh_pos = -5;

var pathsToLoadFrom = ["./assets/obj_files_10/", "./assets/obj_files_11/"];
    
document.addEventListener("mousedown", push_path, false);

function push_path(evt) {
    pathsToLoadFrom.push("./assets/obj_files_12/");
    
this.loadedMeshs = [];
var self = this;
    
BABYLON.SceneLoader.ImportMesh(
  '',
  './assets/obj_files_12/',
  'mesh.obj',
  scene,
  (meshes)=> { 
    for(var i=0; i<meshes.length; i++){
      var mesh = meshes;
      mesh.rotation = new BABYLON.Vector3(0, 0, 179.05);
      mesh.position.x = messh_pos += 0.5;
       console.log(mesh);
       self.loadedMeshs.push(mesh);
     }
  }
);    
}

    
    if (pathsToLoadFrom.length > 0) {
        
        
    for (var p = 0; p < pathsToLoadFrom.length; p++) {
        

    
    
       let loader = new BABYLON.AssetsManager(scene);
        loader.useDefaultLoadingScreen = true;  // Set to false to remove the default loading
        let mesh_loaded_task = loader.addMeshTask("mesh" + p, "", pathsToLoadFrom[p], "mesh.obj");
        
    
        
        mesh_loaded_task.onSuccess = function (task) {
             task.loadedMeshes.forEach(function(m) {
                console.log("Loaded!");
                m.position.x = mesh_pos += 0.5;
                m.rotation = new BABYLON.Vector3(0, 0, 179.05);
                console.log(m);
             });
        };


        
        loader.onFinish = function() {
            engine.runRenderLoop(function () {
                scene.render();
            });
        };

        loader.load();
        
    }
    }

 

Thanks

DB

Share this post


Link to post
Share on other sites
var canvas = document.getElementById("renderCanvas");
var engine = new BABYLON.Engine(canvas);
var scene = new BABYLON.Scene(engine);

scene.clearColor = new BABYLON.Color3(1, 1, 1);
var camera = new BABYLON.FreeCamera("camera", new BABYLON.Vector3(-2.15, 0, -3.2), scene);
camera.minZ = 0.01;
var light = new BABYLON.PointLight("light", new BABYLON.Vector3(0, 5, -5), scene);

var pathsToLoadFrom = ["./assets/obj_files_10/", "./assets/obj_files_11/", "./assets/obj_files_12/"];
    
//document.addEventListener("mousedown", push_path, false);


loader = function(paths){
	this.mesh_pos = -5;
	this.meshs = [];
	for(var i=0; i<paths.length; i++){
		this._run(paths[i]);
		this.mesh_pos += 1;
	}
	
	return this;
};

loader.prototype = {
	_run = function(path){
		var self = this;
		BABYLON.SceneLoader.ImportMesh('', './', path, scene, (meshes)=> {
			for(var i=0; i<meshes.length; i++){
				var mesh = meshes[i];
				mesh.position.x = self.mesh_pos;
				self.meshs.push(mesh);
			}
		});
	}
};
        
  	
engine.runRenderLoop(function () {
   scene.render();
});

var _l = new loader(pathsToLoadFrom);
      
        
    }
}


This is off the top of my head and prolly will drop an error... I have no clue I have not tested it.
But you had a lot of redundant stuff and things that were not effectively doing anything.

Share this post


Link to post
Share on other sites
document.addEventListener("DOMContentLoaded", ()=>{

var canvas = document.getElementById("renderCanvas");
var engine = new BABYLON.Engine(canvas);
var scene = new BABYLON.Scene(engine);

scene.clearColor = new BABYLON.Color3(1, 1, 1);
var camera = new BABYLON.FreeCamera("camera", new BABYLON.Vector3(-2.15, 0, -3.2), scene);
camera.minZ = 0.01;
var light = new BABYLON.PointLight("light", new BABYLON.Vector3(0, 5, -5), scene);

var pathsToLoadFrom = ["./assets/obj_files_10/", "./assets/obj_files_11/", "./assets/obj_files_12/"];
    
//document.addEventListener("mousedown", push_path, false);


loader = function(paths){
	this.mesh_pos = -5;
	this.meshs = [];
	for(var i=0; i<paths.length; i++){
		this._run(paths[i]);
		this.mesh_pos += 1;
	}
	
	return this;
};

loader.prototype = {
	_run : function(path){
		var self = this;
		BABYLON.SceneLoader.ImportMesh('', './', path, scene, (meshes)=> {
			for(var i=0; i<meshes.length; i++){
				var mesh = meshes[i];
				mesh.position.x = self.mesh_pos;
				self.meshs.push(mesh);
			}
		});
	}
};
        
  	
engine.runRenderLoop(function () {
   scene.render();
});

var _l = new loader(pathsToLoadFrom);

 }, false);     

Give that a shot. Should work.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this  

  • Recently Browsing   0 members

    No registered users viewing this page.