I created a function for loading obj files in Three.js. Instead of adding the object directly to the scene in that function, I want to return it to the upper function.
Current code:
var loadObjFile = function(modelConfiguration) {
var mtlLoader = new THREE.MTLLoader();
mtlLoader.load(modelConfiguration.mtl, function (materials) {
materials.preload();
var objLoader = new THREE.OBJLoader();
objLoader.setMaterials(materials);
objLoader.load(modelConfiguration.obj, function (object3d) {
object3d.name = modelConfiguration.name;
scene.add(object3d);
});
});
};
Things I tried
Rewritting the function in difference ways. But couldn't get it to return the object3d. Example:
var loadObjFile = function(modelConfiguration) {
var mtlLoader = new THREE.MTLLoader();
var obj;
mtlLoader.load(modelConfiguration.mtl, function (materials) {
materials.preload();
var objLoader = new THREE.OBJLoader();
objLoader.setMaterials(materials);
objLoader.load(modelConfiguration.obj, function (object3d) {
object3d.name = modelConfiguration.name;
obj = object3d;
});
});
return obj;
};
Instead of adding the object3d to the scene, I added it to a dummy group and returned this group. It worked but later on I have a lot of unnecessary groups. I also tried to extract the object from the group with group.children[0] and group.getObjectByName(modelConfiguration.name) but it didn't work either. Example:
var loadObjFile = function(modelConfiguration) {
var mtlLoader = new THREE.MTLLoader();
var group;
mtlLoader.load(modelConfiguration.mtl, function (materials) {
materials.preload();
var objLoader = new THREE.OBJLoader();
objLoader.setMaterials(materials);
objLoader.load(modelConfiguration.obj, function (object3d) {
object3d.name = modelConfiguration.name;
group.add(object3d);
});
});
return group; // works, but unneccessary group
//return group.children[0]; // error: undefined object
//return group.getObjectByName(modelConfiguration.name); // error: undefined object
};
Thank you in advance!
use a lambda:
var loadObjFile = function(modelConfiguration, onObj) {
var mtlLoader = new THREE.MTLLoader();
var obj;
mtlLoader.load(modelConfiguration.mtl, function (materials) {
materials.preload();
var objLoader = new THREE.OBJLoader();
objLoader.setMaterials(materials);
objLoader.load(modelConfiguration.obj, function (object3d) {
object3d.name = modelConfiguration.name;
obj = object3d;
onObj(obj);
});
});
};
use:
loadObjFile(myModelConfig, function(obj){
console.log(obj);
})
Related
I am trying to render textures on planes loaded from different URL's. For some reason after 2nd or 3rd image I can see in browser that loading image is stuck and it is not being rendered.
Adding the code used:
function init() {
loadPicturesFromDirectUrl();
}
function loadPicturesFromDirectUrl(currentPictureIndex) {
if (currentPictureIndex === undefined) {
currentPictureIndex = 0;
}
var picture = data.pictures[currentPictureIndex];
var loader = new THREE.TextureLoader();
loader.load(picture.url, function (texture) {
renderPicture(picture, texture);
currentPictureIndex++;
if (currentPictureIndex > data.pictures.length - 1) {
return;
}
loadPicturesFromDirectUrl(currentPictureIndex);
}, null, function (e) {
console.log(e);
});
}
function renderPicture(picture, texture) {
var planeGeometry = new THREE.PlaneGeometry(picture.size.width, picture.size.height);
var planeMaterial = new THREE.MeshBasicMaterial({ map: texture });
var planeMesh = new THREE.Mesh(planeGeometry, planeMaterial);
planeMesh.position.x = picture.location.x;
planeMesh.position.y = picture.location.y;
planeMesh.rotateY(myzeum.toRadians(180));
scene.add(planeMesh);
}
You may want to try to preload all the pictures in one function and store them in an array and render them only after all of them have been loaded. Like so:
var all_textures = [];
function init() {
loadPicturesFromDirectUrl();
for int (i = 0; i < all_picture.length; i++) {
renderPicture(data.pictures[i], all_textures[i])
}
}
function loadPicturesFromDirectUrl(currentPictureIndex) {
if (currentPictureIndex === undefined) {
currentPictureIndex = 0;
}
var picture = data.pictures[currentPictureIndex];
var loader = new THREE.TextureLoader();
loader.load(picture.url, function (texture) {
all_textures[currentPictureIndex] = texture
currentPictureIndex++;
if (currentPictureIndex > data.pictures.length - 1) {
return;
}
loadPicturesFromDirectUrl(currentPictureIndex);
}, null, function (e) {
console.log(e);
});
}
function renderPicture(picture, texture) {
var planeGeometry = new THREE.PlaneGeometry(picture.size.width, picture.size.height);
var planeMaterial = new THREE.MeshBasicMaterial({ map: texture });
var planeMesh = new THREE.Mesh(planeGeometry, planeMaterial);
planeMesh.position.x = picture.location.x;
planeMesh.position.y = picture.location.y;
planeMesh.rotateY(myzeum.toRadians(180));
scene.add(planeMesh);
}
I tried to merge two geometries pre-loaded from two PLY files, the console of the browser told me should be use the BufferGeometryUtils.mergeBufferGeometries() for merge de two geometries into one, but I some problems for call this from the class.
Here is my code:
var scene = new THREE.Scene();
var singleGeometry, material, mesh;
var loader = new THREE.PLYLoader();
function loadPLY(path) {
return new Promise(resolve => {
loader.load(path, result => {
console.log('Success');
resolve(result);
});
});
}
init();
Promise.all([
loadPLY("ply/Paso_1_mandible.ply"),
loadPLY("ply/maxila.ply")
])
.then(geometries => {
geometries.forEach(geometry => geometry.computeVertexNormals());
singleGeometry = new THREE.BufferGeometry();
singleGeometry = THREE.BufferGeometryUtils.mergeBufferGeometries(geometries);
material = new THREE.MeshPhongMaterial({color: 0x0055ff });
mesh = new THREE.Mesh(singleGeometry, material);
mesh.position.y = 1;
mesh.position.z = 1;
mesh.position.x = 1;
mesh.scale.multiplyScalar( 0.0001 );
mesh.castShadow = true;
mesh.receiveShadow = true;
scene.add(mesh);
})
.then(() => {
animate();
})
.catch(err => {console.error(err);});
but I got this error:
TypeError: "THREE.BufferGeometryUtils is undefined"
here is the link to the documentation:
class BufferGeometryUtils
It's in examples/js/BufferGeometryUtils.js in the THREE master distro.
https://github.com/mrdoob/three.js/archive/master.zip
The error TypeError: "THREE.BufferGeometryUtils is undefined" is quite clear.
You need to include the BufferGeometryUtils class / file in your code.
As it is in the examples folder, it is not included in the main Three.js library.
I'm using the following code to load OBJ objects, but in order to load the resulted group on a global variable, I pre-define the variable as 'null' which raises Type-error messages in Firefox console. I tried defining it as THREE.Group, THREE.Mesh, etc to no avail -those are causing the code to not execute.
How can I have the same functionality without those annoying error messages?
//this is causing Type-error messages in FF dev tools
var femModel = null;
var onProgress = function ( xhr ) {
if ( xhr.lengthComputable ) {
var percentComplete = xhr.loaded / xhr.total * 100;
console.log( Math.round(percentComplete,2)+ '% downloaded');//<<<<
}
if (percentComplete = 100){
waitmodeltoload();//<<<<<<
}
};
var onError = function ( xhr ) { };
var mtlLoader = new THREE.MTLLoader();
mtlLoader.setPath( 'female02/' );
mtlLoader.setMaterialOptions({ side:THREE.DoubleSide });
// the code in question:
mtlLoader.load( 'female02.mtl', function( materials ) {
materials.preload();
var objLoader = new THREE.OBJLoader();
objLoader.setMaterials( materials );
objLoader.setPath( 'female02/' );
objLoader.load( 'female02.obj', function ( object ) {
femModel = object;
}, onProgress, onError );
});
var waitid;
function waitmodeltoload(){
if (!femModel){
clearTimeout(waitid);
waitid = setTimeout(setupmodel, 100);
}
setupmodel();//<<<<<
}
function setupmodel(){
var intersects=null;
femModel.traverse( function ( child ) { // <<<<<<<<
if ( child instanceof THREE.Mesh ) {
child.castShadow = true;
child.receiveShadow = true;
}
} );
femModel.position.set( 0, -90, 0 );
sceneR2S.add( femModel );
}
OK, that was simpler than I thought, it was a logical error of mine, I overlooked that the code was trying to access femModel before it was available:
So instead of:
var waitid;
function waitmodeltoload(){
if (!femModel){
clearTimeout(waitid);
waitid = setTimeout(setupmodel, 100);
}
setupmodel();
}
It should be:
var waitid;
function waitmodeltoload(){
if (!femModel){
clearTimeout(waitid);
waitid = setTimeout(setupmodel, 100);
}else{
setupmodel();
}
}
In the first case setupmodel() was called even if the model wasn't loaded.
An "else" was all that was needed -no more red-alert Type-error messages.
I have a obj model that I'm loading via THREE.LoadingManager and a few textures. When I'm opening the page with clear cache - I'm always getting model without textures. I can see them after pressing F5. My code looks like this:
var manager = new THREE.LoadingManager();
var loader = new THREE.TextureLoader(manager);
var textures_loaded = 0;
var id;
for (id in materials) {
loader.load('images/' + materials[id].unique_id + '.jpg', function(t) {
t.magFilter = THREE.NearestFilter;
t.minFilter = THREE.LinearMipMapLinearFilter;
var re = /images\/(\d+)\.jpg/g;
var result = re.exec(t.image.currentSrc);
material = materials.filter(function(obj) {
return obj.unique_id == result[1];
}).shift();
material.setTexture(t);
textures_loaded += 1;
if (textures_loaded == materials.length) {
mainLoop();
}
});
}
var loader = new THREE.OBJLoader(manager);
loader.load('obj/' + model.model_name, function (object) {
object.traverse(function(child) {
if (child instanceof THREE.Mesh) {
var mesh = model.meshes.filter(function(mesh) {
return mesh.name == child.name;
}).shift();
child.material.map = mesh.material.texture;
child.geometry.buffersNeedUpdate;
child.geometry.uvsNeedUpdate;
}
});
scene.add(object);
}
);
I'm running mainLoop only after all textures are loaded. But sometimes model loads faster. How can I control order of loading objects using THREE.LoadingManager ?. How can I show model only after everything is loaded ?
I would do something like this. Have two functions responsible for loading each set of content inline. In this case the meshes will not be loaded until the materials are done loading.
var manager = new THREE.LoadingManager();
var loader = new THREE.TextureLoader(manager);
function loadMaterials(materials, callback){
var textures_loaded = 0;
for (var id in materials) {
loader.load('images/' + materials[id].unique_id + '.jpg', function(t) {
t.magFilter = THREE.NearestFilter;
t.minFilter = THREE.LinearMipMapLinearFilter;
var re = /images\/(\d+)\.jpg/g;
var result = re.exec(t.image.currentSrc);
material = materials.filter(function(obj) {
return obj.unique_id == result[1];
}).shift();
material.setTexture(t);
textures_loaded += 1;
if (textures_loaded == materials.length) {
callback("materialsLoaded");
//mainLoop();
}
});
}
}
var loadedMeshes = [];
function loadMeshes(meshes, callback){
var meshes_loaded = 0;
for(var i in meshes){
var loader = new THREE.OBJLoader(manager);
loader.load('obj/' + model.model_name, function (object) {
loadedMeshes.push(object);
object.traverse(function(child) {
if (child instanceof THREE.Mesh) {
//you'll need to map to the right material.
child.material.map = mesh.material.texture;
child.geometry.buffersNeedUpdate;
child.geometry.uvsNeedUpdate;
}
});
if(loadedMeshes.length == meshes.length){
callback("meshesLoaded");
}
};
}
}
var materials = ["material1", "material2"];
var meshes = ["mesh1.obj", "mesh2.obj"];
function onLoad(){
for(var i in loadedMeshes){
scene.add(loadedMeshes[i]);
}
mainLoop();
}
function init(){
loadMaterials(materials, function(t){
console.log(t);
loadMeshes(meshes, function(t){
console.log(t);
onLoad(t);
})
})
}
Maybe you can try to execute your mainLoop only when the document is ready, using some jQuery:
$('document').ready(function mainLoop(){});
"this event does not get triggered until all assets such as images have been completely received"
from jQuery .ready event
am exporting my models using ObjectExporter, my code is follows
exporter = new THREE.ObjectExporter;
var obj = exporter.parse(globalObject);
var json = JSON.stringify(obj);
console.log(json);
i can get the json exported data successfully, but after load it using ObjectLoader the Geometry only loading materials are not loading, am loading my saved model by following code
var loader = new THREE.ObjectLoader();
loader.load("savedjson.json",function ( obj ) {
scene.add( obj );
console.log(obj);
});
any clue to get materials work with the ObjectExporter?
I had the same problem. I have a first attempt at a workaround(but it needs to be improved for sure). What I do is, after loading the object, I traverse the model
loader.load("savedjson.json", function (obj){
obj.traverse(function(child){ initChild(child); });
scene.add(obj);
}
In initChild(child) I do this:
initChild(child)
{
if(child.material != null)
{
var childMaterialName = child.material.name;
child.material = new THREE.MeshPhongMaterial();
child.material.name = childMaterialName ;
AssignMap(child.material);
}
}
In AssignMap(material) I first load the textures, then assign them based on the material name:
AssignMap(material)
{
var texture_metal = new THREE.ImageUtils.loadTexture("media/texture_metal.jpg");
var texture_glass = new THREE.ImageUtils.loadTexture("media/texture_glass.jpg");
if(material.name == "metal texture")
{
material.map = texture_metal;
}
if(material.name == "glass texture")
{
material.map = texture_glass;
}
}