intersectObjects not returning obj from OBJMTLLoader - javascript

I have a object that I'm adding into my scene. I also have various other cubes in the scene. I'm using the following code to fire a Ray and see if I can detect a collision:
var ray = new THREE.Raycaster(camera.position, vec);
var intersects = ray.intersectObjects( scene.children );
For some reason, the regular shapes (cubes) are detected, but the objects that loaded from the obj files are not.
var loader = new THREE.OBJMTLLoader();
loader.load( 'models/technicalTable1.obj', 'models/technicalTable1.mtl', function ( obj ) {
obj.scale.set(0.4, 0.4, 0.4);
obj.position.x = - roomWidth/2 + 100;
obj.position.y = 36;
obj.position.z = - roomLength/2 + 25;
scene.add( obj );
}, onProgress, onError );
Thanks for any help!

You need to pass the recursive flag to Raycaster.intersectObjects().
var intersects = ray.intersectObjects( scene.children, true );
three.js r.70

It doesnt work because the OBJMTLLoader creates a Three.Group
The Group does not have a raycaster.intersectObjects
(I just finished tracing it down and it went to a non implemented function
This REALLY limits the the use of OBJ objects.
I have tried JSON but since you can not load materials and have to create UVmap I find that I need a person that can 3D model or not be able to use the intersectObjects.
Not sure if this is a bug

Related

Three.js - How to replace the model of an Object3D?

I'm trying to replace an object3D with a character model, with another one, while keeping all it's properties, such as the position
var object = new THREE.Object3D( );
object = models.character[0].clone( );
object.position.set( 100, 230, 15 );
scene.add( object.scene );
const clip = THREE.AnimationClip.findByName( object.animations, 'idle' );
var action = mixer.clipAction( clip );
action.play();
//...later on, replace the model, while keeping it's position
object = models.character[1].clone( );
How do I replace the model of an THREE.Object3D( )?
An Object3D doesn't have a model because it doesn't have geometry or materials. This line of code doesn't do anything because you're immediately replacing the Object3D with something else:
var object = new THREE.Object3D( );
object = models.character[0].clone( );
I think you should be using a Mesh, which is a subclass of Object3D. Now, if you want to change the "model" of a Mesh, you can access its .geometry property, and assign it a new one:
// This is the original geometry
const mesh = new THREE.Mesh(geometry, material);
mesh.position.set(100, 230, 15);
// Here we assign a new geometry to the Mesh
mesh.geometry = someOtherGeometry;

Three.js with instancing - can't get it to work without FrustumCulling = false

I'm using three.js and instancing (as in this example), but I'm having the same problem others have reported: the objects are randomly clipped and keep disappearing from the camera
Mesh suddenly disappears in three.js. Clipping?
Three.js buffergeometry disappears after moving camera to close
The proposed workarounds are to set
my_instanced_object.frustumCulled = false;
but this means rendering every single object per each frame, and with a lot of objects this is killing my framerate.
What are my alternatives to this? How can I have proper frustum culling if I'm using instancing?
I'm adding the code I'm using in case someone wanted to take a look
var geometry = new THREE.InstancedBufferGeometry();
geometry.maxInstancedCount = all_meshes_data.length;
geometry.addAttribute( 'position', mesh.geometry.attributes.position );
geometry.addAttribute( 'normal', mesh.geometry.attributes.normal );
geometry.addAttribute( 'uv', mesh.geometry.attributes.uv );
var offsets = new THREE.InstancedBufferAttribute( new Float32Array( all_meshes_data.length * 3 ), 3, 1 );
for ( var i = 0, ul = all_meshes_data.length; i < ul; i++ ) { // Populate all instancing positions (where to spawn instances)
offsets.setXYZ( i, all_meshes_data[i].x, all_meshes_data[i].y, all_meshes_data[i].z );
}
geometry.addAttribute( 'offset', offsets );
var instanceMaterial = new THREE.RawShaderMaterial( {
vertexShader: document.getElementById( 'vertexShader' ).textContent,
fragmentShader: document.getElementById( 'fragmentShader' ).textContent,
transparent: true
} );
geometry.computeVertexNormals();
geometry.boundingSphere = new THREE.Sphere( new THREE.Vector3(), 50 ); // Not working, it works just for a 0;0;0 world positioned mesh that is the 'base' of all of the instanced ones
var instanced_mesh = new THREE.Mesh( geometry, instanceMaterial );
//instanced_mesh.frustumCulled = false; // Works, but the scene becomes very slow (rendering everything even if not in sight)
scene.add( instanced_mesh );
If you are using instancing, there are two ways to handle frustum culling.
One is to turn frustum culling off for the object:
object.frustumCulled = false;
The other option is to set the bounding sphere of the geometry manually -- if you know it, or can estimate it:
geometry.boundingSphere = new THREE.Sphere( new THREE.Vector3(), radius );
three.js r.86

ThreeJS assign array materials

If I assign framemat to createScene(ID,geometry,1,framemat) it works OK, if I do createScene( ID, geometry, 1, materials[ID] ) it doesn't work.
var jsonLoader = new THREE.JSONLoader(),
paths = [
"obj/jgd/GALD-JGD-frame.json",
"obj/jgd/GALD-JGD-logo.json",
"obj/jgd/GALD-JGD-light.json"
],
materials = [
"framemat",
"logomat",
"lightmat"
],
objNum = paths.length; // Cache obects.length
for ( var id = 0; id < paths.length; id++ ) {
(function(){ // IIFE start
jsonLoader.load( paths[id], function( geometry, materials ) {
createScene( id, geometry, 1, materials[id] )
}
);
})(); // IIFE end
}
CreateScene fun here:
function createScene( id, geometry, scale, material ) {
geometry.computeTangents();
objects[id]= new THREE.Mesh( geometry, material );
objects[id].scale.x = objects[id].scale.y = objects[id].scale.z = scale;
scene.add( objects[id] );
}
Your code is ambiguous : first you define an array called materials then you use the second argument of the JSONLoader and also name it materials to catch the textures in the json file, finally you use materials[id] to pass the material at the mesh creation. Which one is it ? Whereas the first array is still filled with strings, the json can have an array of materials instead of only one, worse of all the rank [id] may not exist in this one.
So you have two solutions :
If you want to create the materials independently, only keep the first array and add the missing function that transforms your strings to materials. Just remove the second argument of the JSONLoader.
If you want to use the material provided in the JSON File, the array materials is useless and can lead to other mistakes. There are two options then :
if you have one material for the whole object, write it this way : createScene(...,..., materials);
if you have several materials for the object, this way : createScene(....,....,new THREE.MeshFaceMaterial(materials);

Three.js: How to create new 'morphing' geometry if I have all necessary buffers?

I'm using a web-worker to load a .json file of an animated 3D model. For each of the big arrays (vertices, normals, etc.), I'm transferring an Float32Array buffer back to the UI thread. Since such buffers are transferable objects, this will take (almost) zero time.
Now, it turns out that WebGL (and therefore, Three.js) use Float32Array buffers internally, too. This means I could probably load this 3D animation without copying anything, spending almost zero time in the main thread. Isn't that nice?
But it's not clear how to do that part: In the main thread, we have array buffers available for the vertices, normals (the main ones, and the 'morph' ones) and faces. How do I create a working Geometry (or BufferGeometry) from these, without translating or copying the data?
var scene,
vertices, normals, faces,
morphVertices, morphNormals; // <-- we have all these as typed arrays
var geometry = ...; // <-- insert code here
var material = new THREE.MeshLambertMaterial({ morphTargets: true });
var object3D = new THREE.MorphAnimMesh(geometry, material);
scene.add(object3D);
This answer gives a hint, but only point 7 seems relevant, it assumes there is already some Geometry instance, and it doesn't handle morph-targets.
Here's an example based on the mesh loading portion of THREE.GLTF2Loader.
// Create BufferGeometry and assign vertices and normals.
var geometry = new THREE.BufferGeometry();
geometry.addAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) );
geometry.addAttribute( 'normal', new THREE.BufferAttribute( normals, 3 ) );
geometry.setIndex( new THREE.BufferAttribute( faces, 3 ) );
// Create material.
var material = new THREE.MeshStandardMaterial({
morphTargets: true,
morphNormals: true
});
// Set up morph target attributes.
var posAttr = new THREE.BufferAttribute( morphVertices, 3 );
var normAttr = new THREE.BufferAttribute( morphNormals, 3 );
for (var i = 0; i < posAttr.array.length; i++) {
posAttr.array[i] += geometry.attributes.position.array[i];
}
for (var j = 0; j < normAttr.array.length; j++) {
normAttr.array[j] += geometry.attributes.normal.array[j];
}
// Assign morph target attributes.
geometry.morphAttributes.position = [ posAttr ];
geometry.morphAttributes.normal = [ normAttr ];
// Create Mesh.
var mesh = new THREE.Mesh(geometry, material);
mesh.updateMorphTargets();
// Apply 50/50 blend of morph targets and default position/normals.
mesh.morphTargetInfluences[0] = 0.5;
three.js r86-dev.

Intersection within Object3D

I have some objects added to an Object3D (for grouping elements) and I'm trying to detect clicks on it.
My scene has a size of 600x400, my camera is within a three-object and my event handler code looks like below:
function onDocumentMouseDown( event ) {
event.preventDefault();
var mouse = {};
mouse.x = ( event.clientX / 600 ) * 2 - 1;
mouse.y = - ( event.clientY / 400 ) * 2 + 1;
var vector = new THREE.Vector3( mouse.x, mouse.y, 1 );
projector.unprojectVector( vector, three.camera );
var ray = new THREE.Ray( three.camera.position, vector.subSelf( three.camera.position ).normalize() );
var intersects = ray.intersectObjects( group.children );
alert(intersects.length);
[...]
}
Actually I'm alerting the count of intersected objects. But it stays zero. It couldn't find any intersected objects. I've already played a bit aroud with the x, y and z values of my projection vector - without success.
I've added a stripped down sample for demonstrating this issue on jsfiddle. Maybe someone has a short hint for me what goes wrong with it?
In your fiddle, because you are calling THREE.SceneUtils.createMultiMaterialObject( ), which creates a hierarchical structure, you need to add the recursive flag to ray.intersectObjects().
var intersects = ray.intersectObjects( group.children, true );
EDiT: ray is now an instance of THREE.Raycaster -- not THREE.Ray.
three.js r.58
I had the same problem and WestLangley's answer provides the answer. Great job! For anybody struggling with mouse selection of objects grouped in Object3D wrapper too, I am posting my own solution.
First, I created an array of objects that are selectable - I hope this also saves some performance, as RayCaster doesnt need to search all objects in the scene, but only those you wish to respond to selection. I also attached this array to scene object directly (solely for the fact that it is already accessible from most parts of my app)
scene.selectable = [];
Next step is to push all objects that you wish to make selectable into this array. You will insert only meshes/sprites/etc from your group, not the whole group. Only last line is important here:
var myWrapper = new THREE.Object3D();
var myObject = new THREE.Mesh( something );
myWrapper.add( myObject );
scene.add ( myWrapper );
scene.selectable.push( myObject );
And lastly in your mouse selection routine you will call raycaster like this:
var intersects = ray.intersectObjects( scene.selectable );

Categories

Resources