Preloading Obj+Mtl Objects in Three.js - javascript

I need to preload some obj+mtl files with Three.js (It's not the same file) and I need call another function when all the objects have been loaded.
I tried putting a boolean variable that changes when every obj has been loaded and doing a function that confirms if all the objects have been loaded but it didn't work, for some reason the page crash.
I have all the obj and mtl paths in an array.
This is an example of what I'm doing. http://pastie.org/10297027
I tried to put the load function in a for statement but it didn't work well
Can you help me?

new THREE.MTLLoader()
.setPath( 'models/obj/male02/' )
.load( 'male02_dds.mtl', function ( materials ) {
materials.preload();
new THREE.OBJLoader()
.setMaterials( materials )
.setPath( 'models/obj/male02/' )
.load( 'male02.obj', function ( object ) {
object.position.y = - 95;
scene.add( object );
}, onProgress, onError );
} );
OBJMTLLoader is depricated. You should use OBJLoader and MTLLoader in combination.
Here is an example

Use a three.js loading manager for this task, here is how to do it.
Create a manager:
var manager = new THREE.LoadingManager();
manager.onProgress = function ( item, loaded, total ) {
// this gets called after an object has been loaded
};
manager.onLoad = function () {
// everything is loaded
// call your other function
};
Create a loader using the manager and load items:
var OBJMTLLoader = new THREE.OBJMTLLoader( manager );
OBJMTLLoader.load(urlsOBJ[0], urlsMTL[0], function(object) {
// stuff you do in your callback
var newObject = object.clone();
newObject.position.set(140, 10, 10);
newObject.rotation.x = -99;
scene.add(newObject);
objectArray.push(newObject);
});
Tested in three.js r71.

Related

Removing an object loaded with OBJLoader Three.js

This may seem like a very trivial problem but I can't find a solution to this. I've added an object to the scene using OBJLoader as seen below. How can I remove it from the scene? I've tried using code to clear scene.children, but this doesn't remove my "flower.obj"
const mloader = new THREE.OBJLoader();
mloader.load
(
'models/flower.obj', function(object)
{
object.scale.x=1
object.translateZ(2);
scene.add(object);
}
);
You need to save a reference to the loaded object so you can subsequently call scene.remove() when you're ready to get rid of it.
var flower;
const mloader = new THREE.OBJLoader();
mloader.load('models/flower.obj',
function(object) {
flower = object;
flower.scale.x=1
flower.translateZ(2);
scene.add(flower);
}
);
function removeFlower() {
scene.remove(flower);
}

Three.js objloader + texture

I have been using Three.js for a few weeks now, I managed to apply a texture to a cube created directly via the code, but once I tried to load an OBJ file with OBJLoaderI noticed that I couldn't use the same method to load simple png or jpg textures with TextureLoader.
Now I'm not entirely sure what is going wrong, if either this is not the right way to do it, if I just can't load a simple image fine or if I'm not applying it right.
I've seen that applying a texture seems quite easy to do with an MTL file, but unfortunately I'm not sure how to make one.
My current code looks like something like this:
var loader = new THREE.OBJLoader( manager );
loader.load( 'models/tshirt.obj', function ( object ) {
object.position.x = 0;
object.position.y = -200;
object.scale.x = 10;
object.scale.y = 10;
object.scale.z = 10;
obj = object;
texture = THREE.TextureLoader('img/crate.png'),
material = new THREE.MeshLambertMaterial( { map: texture } );
mesh = new THREE.Mesh( object, material );
scene.add( mesh );
} );
But it just doesn't seem to work. The model doesn't load and gives me random errors from Three.js. If instead of the code above I change scene.add( obj); the model actually loads.
What should I be doing here? Should I just go ahead and try to make an MTL file?
My full working code can bee seen at http://creativiii.com/3Dproject/old-index.html
EDIT: The error I get when adding mesh instead of obj is
three.min.js:436 Uncaught TypeError: Cannot read property 'center' of undefined
Try this code:
var OBJFile = 'my/mesh.obj';
var MTLFile = 'my/mesh.mtl';
var JPGFile = 'my/texture.jpg';
new THREE.MTLLoader()
.load(MTLFile, function (materials) {
materials.preload();
new THREE.OBJLoader()
.setMaterials(materials)
.load(OBJFile, function (object) {
object.position.y = - 95;
var texture = new THREE.TextureLoader().load(JPGFile);
object.traverse(function (child) { // aka setTexture
if (child instanceof THREE.Mesh) {
child.material.map = texture;
}
});
scene.add(object);
});
});
The texture loading changes was taken from: How to apply texture on an object loaded by OBJLoader? .

How do I swap an OBJ object for another in Three.JS?

I am using ThreeJS to load OBJs into a webpage, which I have done succesfully, but now I want to add buttons to my page that will swap out the displayed OBJ file for a different one. I have attempted to name the object when loading it:
object.name = "selectedObject";
so that I can remove it from the scene when the new button is clicked
scene.remove( selectedObject );
and attach the new object:
scene.add(newobject);
But I am getting lost in how to implement this into the general code/what the correct syntax would be.
Here's the code for loading the model:
var objectloading = 'obj/male02/new.obj';
var loader = new THREE.OBJLoader( manager );
loader.load( objectloading, function ( object ) {
object.traverse( function ( child ) {
if ( child instanceof THREE.Mesh ) {
child.material.map = texture;
}
} );
object.position.y = -30;
scene.add( object );
}, onProgress, onError );
Any help is apreciated, thanks!
Well there's many ways to go about it. What it comes down to is that your code needs to account for what has been loaded. You can traverse a scene, but capturing and listing your obj files into specific lists on your own is much cleaner, especially later when implementing things like raycasting.
I would write some functions, perhaps even a class if I planned on supporting other mesh types in the future and wanted a single interface. Importantly, you do not pass the mesh name as the parameter to scene.add and scene.remove, you pass a reference to the actual object. Three.js does this so that it can nullify the parent and call the object "removed" dispatch event in the Three.js library.
So for example purposes, one way is to store your objects in a hash, and use the url to the mesh as the parameter for adding and removal.
var meshes = {};
addObj = function(url){
var objectloading = url;
var loader = new THREE.OBJLoader( manager );
loader.load( objectloading, function ( object ) {
object.traverse( function ( child ) {
if ( child instanceof THREE.Mesh ) {
child.material.map = texture;
}
} );
object.position.y = -30;
meshes[url] = object;
scene.add(object);
}, onProgress, onError );
}
removeObj = function(url){
scene.remove(meshes[url]);
delete meshes[url];
}
for example, toggle between two models,
in a button click:
removeObj('obj/male02/old.obj');
addObj('obj/male02/new.obj');
and then in another button click:
removeObj('obj/male02/new.obj');
addObj('obj/male02/old.obj');
Although any String can be used for member names in "meshes," using urls could become problematic if the application grows and the url is actually a service uri utilizing a POST request, then you'll need another layer of references, possibly and usually using the UUID give to each object added to Three.js.

Mesh with multiple animations

I'm trying to understand this example in Three.js: http://threejs.org/examples/#webgl_animation_skinning_blending. I have some problems with this part of code (the BlendCharacter.js file).
this.load = function ( url, onLoad ) {
var scope = this;
var loader = new THREE.JSONLoader();
loader.load( url, function( geometry, materials ) {
var originalMaterial = materials[ 0 ];
originalMaterial.skinning = true;
THREE.SkinnedMesh.call( scope, geometry, originalMaterial ); // QUESTION (2)
// Create the animations
for ( var i = 0; i < geometry.animations.length; ++i ) {
var animName = geometry.animations[ i ].name; // QUESTION (1)
scope.animations[ animName ] = new THREE.Animation( scope, geometry.animations[ i ] );
}
(...)
} );
};
I have two questions:
(Main) How does the 3D object (in Three.js format) already has animations with names? In the for loop, "geometry.animation[i].name" is "walk", "idle" and "run". I made animations with maya and blender (beginner level), but I do not see how to export multiple animations on the same mesh, and how to name them.
(Less) This is a matter of JavaScript syntax. Why "var scope = this;" ? I tried to replace "scope" by "this" in "THREE.SkinnedMesh.call(scope, geometry, originalMaterial);", but this make it no longer works.
Thanks for reading my questions !
PS : sorry for my english...
(Main) question: When you are using Blender and if you create an animation, it automatically creates a name for the animation and you can change the name on action editor. Now it is possible to export multiple animations for a mesh. Inside the javascript code, you can call each animation by id (geometry.animations[id]).

ThreeJS - OBJLoader "Cannot read property 'uniforms' of undefined

I'm trying to use three.js to import and render an object I made. Everything was find, I even succeded to apply a wireframe texture.
But i wanted to apply a Point cloud texture (here) but either it just display a color on my object either i get an error like writte non the tittle
my code is like that
var loader = new THREE.OBJLoader( manager );
loader.load( 'obj/moimeme2.obj', function ( object ) {
object.traverse( function ( child ) {
if ( child instanceof THREE.Mesh ) {
child.material = new THREE.PointCloudMaterial( {color: 0x6FF060 } );
}
} );
/*object.scale.y = 5;
object.scale.x = 5;
object.scale.z = 5;*/
scene.add( object );
}, onProgress, onError );
I'm kinda a beginner in JS and i don't really get what is the problem this
Thank you for reading !
Point cloud material is for point cloud geometry. Not for regular geometry.

Categories