Multiple .obj Models in THREE.js and detecting clicked objects - javascript

I have been following along this example to load .obj Model using three.js. Since I needed more than one model to load so I tried this
loader.addEventListener( 'load', function ( event ) {
var object = event.content;
object.position.y = - 80;
scene.add( object );
});
loader.load( 'obj/model1.obj' );
loader.load( 'obj/model2.obj' );
First: I don't know whether this is the right approach or not since I searched but didn't find any tutorial loading more than one .obj models.
Second: I want to be able to click different models on the screen. I tried this which doest not seem to work for me. Any suggestions on this?

This example shows you a pattern for loading multiple models:
http://mrdoob.github.com/three.js/examples/webgl_loader_json_objconverter.html
Regarding detecting clicked objects, depending on your model, you may need to set the recursive flag to true:
ray.intersectObjects( objects, true );

Well, since you haven't provide enough code to fully explain you question I will guess that for the second part you have to make sure you put the objects in an array of objects such as:
var objects = [];
after initializing the objects you do:
objects.push( object );
now you have an array where you can check for objects intersected after implementing the THREE.Ray.

Related

How to get all objects that are visible on the camera in Three.js

I need lazy load textures to my meshes, and load only those that are visible to the camera.
how i can do this without million vectors in Raycaster?
One option is to use a Frustum.
Configure the frustum from the camera by using Frustum.setFromProjectionMatrix:
const frustum = new THREE.Frustum().setFromProjectionMatrix( new THREE.Matrix4().multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse ) );
Once you have your frustum, loop through your scene objects to see if they are contained within:
const visibleObjects = []
yourScene.traverse( node => {
if( node.isMesh && ( frustum.containsPoint( node.position ) || frustum.intersectsObject( node ) ){
visibleObjects.push( node )
}
} )
Another (much harder) option is to use GPU picking to identify objects based on an ID color assigned when rendering them. This option is quite involved, so you should probably try the frustum approach first to see if it satisfies what you want to do.
scene.traverseVisible(callback)
More info:
https://threejs.org/docs/#api/en/core/Object3D.traverseVisible

resizing individual models in a single geometry

I have a 3D model of my home town. I would like to use real time data to change the height of the buildings. In my first try, I loaded the buildings as individual meshes and called scene.add(buildingMesh) during setup.
var threeObjects = []
var buildingMesh = new THREE.Mesh(geometry, material)
threeObjects.push(buildingMesh);
$.each(threeObjects,function(i, buildingMesh)
{
buildingMesh.rotation.x += -3.1415*0.5;
buildingMesh.castShadow = true;
buildingMesh.receiveShadow = true;
scene.add(buildingMesh);
});
Which is too slow as my dataset consists of roughly 10.000 building.
So I took the approach to add all the (geometries of the) meshes to a single geometry and wrap that in a mesh to be added to the scene
singleGeometry.merge(buildingMesh.geometry, buildingMesh.matrix); //in a loop
var faceColorMaterial = new THREE.MeshLambertMaterial( { color: 0xffffff, vertexColors: THREE.VertexColors } );
combinedMesh = new THREE.Mesh(singleGeometry, faceColorMaterial);
scene.add(combinedMesh);
Just to make a proof of concept, I'm trying to change the height of a building when I click it. Alas, this is not working.
By adding a new id field, I can get a reference to the faces and vertices and change the color of the building, but I can not for the life of me, get them to to change height.
In my first version, I would just use something like:
buildingMesh.scale.z=2;
But as I have no meshes anymore, I'm kinda lost.
Can anybody help?
disclaimer: I'm new to Three.js, so my question might be stupid...hope it's not :)
If you combine all of your buildings into a single geometry, you're obliterating everything that makes the buildings distinct from each other. Now you can't tell building A from building B because it's all one big geometry, and geometry at its basic level is literally just arrays of points and polygons with no way of telling any of it apart. So I think it's the wrong approach to merge it all together.
Instead, you should take advantage of three.js's efficient scene graph architecture. You had the right idea at first to just add all the buildings to a single root Object3D ("scene"). That way you get all the efficiencies of the scene graph but can still individually address the buildings.
To make it load more efficiently, instead of creating the scene graph in three.js every time you load the app, you should do it ahead of time in a 3D modeling program. Build the parent/child relationships there, and export it as a single model containing all of the buildings as child nodes. When you import it into three.js, it should retain its structure.
JCD: That was not quite the question I asked.
But anyhow, I found a solution to the problem.
What I did was to merge all the geometries, but in stead of using the standard clone function in geometry.merge() I used a shallow reference, which made it possible for me to use the reference in threeObjects to find the correct building and resize the part of the geometry using Mesh.scale, followed by a geometry.verticesNeedUpdate = true;
For further optimization, I split the model into 5 different geometries and only updated the geometry that contained the building

How can I access different parts of an OBJ loaded with OBJMTLLoader?

I made a model of a building with 3DS Max where each room is a modified cube, I used OBJMTLLoader to load the OBJ file with his own MTL.
The problem is that I need to emphasize certain rooms according to the user requirements.
I use dat.gui to display a menu where the user can check/uncheck rooms to remark them (I think I will change the room size or marterial in order to emphasize it).
this is the code of my loader
var loader = new THREE.OBJMTLLoader();
loader.load( 'models/ed.obj', 'models/textures/ed.mtl',
//onLoad()
function ( object ){
contenido = object;
contenido.position.set(0,0,0);
contenido.position.y = -80;
contenido.name = 'edificio';
scene.add(contenido);
return contenido;
} //and other onLoad, and onError stuff
I can play with the whole model, I have a transparency function that adjust transparency to a dat.gui bar (the one called opciones.Opacidad)
contenido.traverse( function( object ) {
if( object.material ) {
object.material.opacity = opciones.Opacidad;
object.material.transparent = true;
}
And it works correctly, but the probblem come when I try to access internal cubes of the geometry to remark them in wireframe (for example) I use:
contenido.getObjectByName("RoomNameIn3DSMax").material.wireframe=true;
In order to find and show it in wireframe, because the OBJ file names every module with its 3DS Max name. But it does not work, It looks like the item is found because I don't get any error, but it does not appear in wireframe, also when I use another room names, sometimes the program show in wireframe lots of things that can include the requested one or not, another ones it finds "undefined" I think it has something to be with the Groups created by the OBJMTLLoader.
I've also tried
contenido.traverse( function ( child ) {
if ( child.name == "NameInOBJor3DSMax" ) {
child.material.wireframe = true;
}
})
But the result is the same.
So my question is, How can I access internal modules of my model loaded with OBJMTLLoader? As you can see I tried to use the "Object3D" methods to access the internal cubes of the whole model, am I doing it correctly? I can provide all the code and files if needed, you can see an approach of my problem here:
3DBuilding link
You can also see all code there ;)
Thank you for your attention, I hope someone can help me.
EDIT: When I do:
scene.getObjectByName("nameOfModule").material.color.set(0xff0000);
To remark it on red or just: .material.wireframe=true; it shows remarked a lot of things that are not realted. It does the same when I use scene.getObjectById(ObjectId, true); But it does not show the name of the object in the DOM Tree.
I am trying to use another exporting tools to see if the problem is just the OBJ Exporter. It is something really annoying.
Here are some screens of the problem.
What I try to remark:
as you can see, it's only a edited box
what is remarked:
If we go to the OBJ file we can see something like this:
#
# object SDHAE001
#
v 106.4733 84.7697 -94.9228
... (more vertices)
v 106.3747 76.8453 -96.3784
# 16 vertices
vn 0.0000 1.0000 -0.0000
...(more normals)
vn -0.9991 0.0046 0.0425
# 14 vertex normals
#and here the group of faces fot that object ( Using triangles )
g SDHAE001
usemtl wire_115115115
f 10663//5549 10664//5549 10665//5549
...(more triangle faces)
f 10676//5559 10663//5561 10678//5562
# 22 faces
As you can see, 3DS Max is creating Groups of polygons for my geometry, and as far as I know, OBJMTL ignores "g" groups, so when I try to remark something, it remarks A LOT of things... Trying with other exporters... Parsing manually the OBJ file would be so annoying...
EDITED AGAIN
I've been exploring the options, and OBJ is generated ok by 3DS MAX, BUT when I explore de DOM Tree, I can see the THREE.Mesh as an array where only some of the the pair indexes have the expected object with his own name, and odd indexes have some sort of geometry I can't control without any name assigned, and for some strange reason, there are some groups of "vertices" created in geometry instance with no sense, I upload some screens and the OBJ/MTL files to make my problem clearer:
Look at this, I will try to remark now a module called "Conserjeria". and this is the module located in the DOM Tree:
Bigger image
Here, on the first column we can see, first problem, my OBJ (all the geometry) file only has about 298 Objects, so why are there 1056 Mesh Objects? It is not very relevant because most of them are with empty names.
Second problem, on the second column you can see geometry property for "Conserjeria" room, I think here is where the problem is generated...
On the third column, I show you that strange "vertices" array for my "Conserjeria" object. I say strange, because it is composed by 6272 indexes... NOTHING in my OBJ file has 6272 vertices... You can see the OBJ here, and you can find Conserjeria if you want to see its structure (52v, 11vn, 80f). Might this be the reason why when I try to remark only certain objects it remarks a lot of them?
I've also tried with a simplified model of 3 boxes, 2 of them are correctly remarked, but the first one is not, have a look (use the checkboxes to remark them) I can't imagina why is this happening. (there are also 7 THREE.MESH index in the array when there are only 3 cubes in the scene...)
TestingCubes
Any help to remark only my rooms? I can share everything I have if needed(Code, OBJ, MTL, 3ds...)
Thank you to everyone who is trying to help me.
Looks like you got a problem with the mesh names.
When I execute the following script, it's working just fine:
scene.children[2].traverse(function(c) {
if(c.material && c.name.indexOf("INVESTIGACION_1_") >= 0) {
c.material.color.set(0xff0000)
}
})
That tints part of the model red, as expected.

Three.js hiding objects of loaded model

I have model (exported from Blender) which contains two meshes. One mesh is rigged and animated character, second mesh - hair model. I need to select one of the meshes and hide it or change it's texture.
I load model like this:
jsonLoader.load('./models/character.js', function(geometry, materials) {
for(var i = 0; i < geometry.animations.length; i++){
THREE.AnimationHandler.add( geometry.animations[i] );
}
var character = new THREE.SkinnedMesh( geometry );
});
Now both meshes works like one solid mesh.
You can make an object invisible by setting its visible property to false.
character.visible = false;
You need to convert this two meshes in two different objects to make them selectable (see this nice post to learn how to select an object).
Once the object is selected you can change its property to make it visible or hide it.

Three.js load multiple separated objects / JSONLoader

is it possible to load a scene (e.g. two different cubes) exported from blender to json and identify them?
I need to distinguish between them e.g. to make one rotating and the other moving.
Thank you in advance!
Denv
edit+++
Thank you for your answer!
So if I load two cubes in one JSON file:
loader.load("untitled1.js", function(geometry, materials) {
mesh = new THREE.Mesh( geometry, new THREE.MeshFaceMaterial(materials));
mesh.scale.set( 10, 10, 10 );
mesh.position.y = 0;
mesh.position.x = 0;
scene.add( mesh );
});
How can I move first cube?
mesh.getObjectById(0).position.x = 15;
Doesn't seems to work.
Thank you!
Yes, it is possible to load an entire scene from a json file exported from Blender!
I achieved that with the following process: (Using three.js r80)
First you have to name your different objects in Blender like in the following image Outliner.
Then you can export the file using Three.js json exporter add-on for Blender, but taking care of marking the Scene checkbox like in the following:
Using this option your json file now contains all the meshes on the Blender's Outliner, as you can verify using any text editor. It doesn't matter if the meshes were selected or not.
It is important to know that the root (or parent) object isn't a Geometry anymore. It is labeled with the Object type by now. To access the children objects (of Mesh type) you can use the getObjectByName method on the root object like in the following code:
jsonloader.load( "obj/Books.json", function ( loadedObj ) {
var surface = loadedObj.getObjectByName("Surface");
var outline = loadedObj.getObjectByName("Outline");
var mask = loadedObj.getObjectByName("Mask");
// Watch the objects properties on console:
console.log(loadedObj);
console.log(surface);
console.log(outline);
console.log(mask);
} );
If we check the browser's console, we can see the proper objects assigned. And from now, you can manipulate the children objects independently (move, rotate, change materials, etc.)
Each object loaded has an associated .id. So you can use the Object3D.getObjectById() to find it and apply transforms on it.

Categories

Resources