threejs - inaccurate mouse picking when loading using ObjLoader and doing raycasting - three.js

I want to pick a face in a mesh when clicking on it with the mouse.
The mesh is loaded using ObjLoader and MtlLoader (attached)
When clicking on the mesh, sometimes an intersection is detected, but sometimes not.
Attached here is a small self-contained example that is based on webgl_interactive_cubes.html and the .obj file.
If setting the variable loadCubes to true the original example runs as expected, i.e. intersections are detected.
If setting the variable loadCubes to false, the obj file is loaded but the intersections are not detected.
Thanks,
Avner
p.s.
Related links that I looked at:
similar problem description: here and here, code pattern, raycasting documentation

THREE.Raycaster() has .intersectObjects ( objects, recursive ) method.
The documentation says about parameters of this method (if you looked at it attentively):
objects — The objects to check for intersection with the ray.
recursive — If true, it also checks all descendants of the objects. Otherwise it only checks intersecton with the objects. Default is false.
THREE.OBJLoader(), returns THREE.Group() object, containing THREE.Mesh() object(s) in its .children property.
So your check for intersection should be like that:
var intersects = raycaster.intersectObjects( scene.children, true );

Related

Changing parents of a 3D model while keeping the same position

I'm using three.js library to work with 3d models (mostly .glb but it shouldn't matter)
The idea is to import a 3d model that contains groups and meshes. I want to be able to move meshes between already existing groups within a model without changing the visual representation of the model.
Piece of my code is below. movedInternal is
movedMesh.matrixWorldNeedsUpdate = true; // not sure if it's needed
let meshPosition = new THREE.Vector3();
movedMesh.getWorldPosition(meshPosition);
oldParent.remove(movedMesh);
newParent.add(movedMesh);
movedMesh.worldToLocal(meshPosition);
movedMesh.position.set(meshPosition.x, meshPosition.y, meshPosition.z);
And this is not working. Mesh changes its global position because new parent’s position is not the same as previous parent position, but I expect it to stay where it was but change its local position considering new parent’s position.
What do I do wrong?
I think you need to update the movedMesh's world matrix after you change its parents. When you .remove() then .add(), it doesn't know that its parents have been updated (Three.js usually does it when you're rendering, but this is happening before the next frame render).
let meshPosition = new THREE.Vector3();
movedMesh.getWorldPosition(meshPosition);
oldParent.remove(movedMesh);
newParent.add(movedMesh);
// Here it needs to re-learn its new coordinates
movedMesh.updateMatrixWorld(true);
movedMesh.worldToLocal(meshPosition);
movedMesh.position.set(meshPosition.x, meshPosition.y, meshPosition.z);
I've been searching for this solution since a week and haven't been able to tackle the exact problem. Though my question is diferent but has same scenario, I would like to give a solution for those, looking for it.
I have a 3D object with different child meshes, and i want to replace one of my child mesh with another 3D model.
Below code snippet:
replacementMesh is a new 3D object, meshToReplace is the child mesh that needs to be replaced.
// Copy the transformation matrix from obj1 to obj2
replacementMesh.matrix.copy(meshToReplace.matrix);
// Update obj2's position, rotation, and scale to match the transformation matrix
replacementMesh.position.setFromMatrixPosition(replacementMesh.matrix);
replacementMesh.rotation.setFromRotationMatrix(replacementMesh.matrix);
replacementMesh.scale.setFromMatrixScale(replacementMesh.matrix);
meshToReplace.parent.add(replacementMesh);
meshToReplace.parent.remove(meshToReplace);

How to raycasting hidden object or not add to scene in Threejs

I created 1000 hidden objects with BoxGeometry geometry using THREE.JS. I set object.visible = false to hide each object, however this causes the raycasting/interaction to not work.
I expect that hiding the objects will give me a performance boost.
I can hide the box objects by setting material.visible = false on each object, however the performance of my app is still terrible.
How can I achieve the required raycasting interaction with hidden objects in the performance friendly way?
One way to achieve what you require would be to not add your Box objects to your scene which would ensure that they are not rendered, and pass those directly to a THREE.Raycaster to determine if intersection between any of those boxes has occurred.
You could for instance crate a THREE.Raycaster object from your ray primitive, and then pass an array of your Box objects to the .intersectObjects() method to determine ray intersection.
In code, that would look something like this:
// ray is your intersection primitive
const raycaster = new THREE.Raycaster(ray.origin, ray.direction);
// boxObjects is an array of THREE.Object3D's representing your 1000 boxes
const intersectionResult = raycaster.intersectObjects(boxObjects)

OrbitControls.js and re-focusing orbit target on a new selected object

I am very bad at three.js - but I believe I have cobbled together a basic raycast on a set of objects, to select the object of interest.
http://designs.playgami.com/webgl_loader_fbx.html
I am now trying to have OrbitControls.js focus the target vector on that object, i.e., once selected, be able to orbit around that object. I have tried a few variations, where some lead to what appears to be an empty (or out of camera range) scene.
controls.target.set(obj.position)
where obj is the raycast object and controls is the OrbitControl instance.
The quick(?) question: how do you set the orbit center of three.js OrbitControl given an object from a raycast?
It turns out that the fbx model on raycast seemed to return one of its child objects in the child object's coordinate system (though the material coloring seemed to color the whole object). Setting it to the parent seems to have solved it.
Check to see if it's a child object or child segment that is returning the same local coordinate
for controls.target setting, the vector3 is an object that needs to be copied .copy (thanks #WestLangley)

Render single Object / Mesh immediatly into a renderTarget

I need to render a single specific mesh from a scene into a texture using a THREE.WebGLRenderTarget. I already achieved that during the rendering of the scene, all other meshes, except this one, are being ignored. So, I basically achieved my goal. The thing i hate is, that there is still a lot of unnecessary work going on for my whole scene graph, during the render process of the scene. I need to render this texture every frame, so with my current method i get extrem fps drop downs. (There are lots of meshes in the whole scene graph).
So what i found was the function "renderBufferImmediate" from the THREE.WebGLRenderer. (Link to the renderer source code here) My pseudo code to achieve my goal would look like this:
var mesh = some_Mesh;
var renderer = some_WebGLRenderer;
var renderTarget = some_WebGLRenderTarget;
renderer.setRenderTarget(renderTarget);
var materialProperties = renderer.properties.get(mesh.material);
var program = materialProperties.program;
renderer.renderBufferImmediate(mesh, program, mesh.material);
var texture = renderTarget.texture;
The renderBufferImmediate function takes an instance of an THREE.Object3D, a WebGLShaderProgram and a THREE.Material. Problem i see here: The implementation of this function tries to lookup properties of the Object3D which, afaik doesn't exist. (Like "hasPositions" or "hasNormals"). In short: my approach doesn't work.
I would be grateful if someone could tell me if i can use this function for my purpose (Meaning i am currently using it wrong) or if there is another solution for my problem.
Thanks in advance.

using renderOrder in three.js

I want to have two overlapping objects in a scene but I want to define which object should be drawn first. I have a sample of the code here: http://jsfiddle.net/sg02e5sm/1/
I'm using renderOrder = 1 for the second object to make it appear always on top of the first object (as long as they have the same Z value), but it's not working.
renderOrder does not make something appear "on top". It controls the order in which objects are rendered.
If you want your 2nd plane to be "on top", you can add
mesh.material.depthTest = false;
for the 2nd plane.
Alternatively, you can implement two render passes, as described in this SO answer.
three.js r.142

Resources