Threejs raycaster only works super close - three.js

I was following mrdoobs examples of how to use raycasting to handle clickable objects. I have also looked at all of the many many similar questions and have tried countless things.
The raycasting works... If I am at a distance of less than 1.
The Raycaster is set to near 0 and far infinity though (defaults).
I haven't seen any code examples where distance was set.
I am hoping for another pair of eyes.
// snippet
glow.clickables = [];
var cubeGeo = new THREE.CubeGeometry(2, 2, 2);
cubeGeo.computeFaceNormals();
var cube = new THREE.Mesh(cubeGeo, redmat);
cube.position.y = 10;
cube.position.x = 0;
cube.position.z = -12;
cube.overdraw = true;
glow.Vis.scene.add(cube);
glow.clickables.push(cube);
onclick_();
var onclick_ = function() {
$('#world').on('mousedown', function(e){
var mouseX = (event.offsetX / $('#world').width()) * 2 - 1;
var mouseY = - ( event.offsetY / $('#world').height()) * 2 + 1;
var vector = new THREE.Vector3(mouseX, mouseY, 0.1); //what should z be set to?
//console.info(vector); // A vector between -1,1 for both x and y. Z is whatever is set on the line above
projector.unprojectVector(vector, glow.Vis.camera);
var conts = glow.Vis.controls.getObject().position; // control 3dObject which the camera is added to.
var clickRay = new THREE.Raycaster(conts, vector.sub(conts).normalize());
var intersects = clickRay.intersectObjects(glow.clickables);
console.info(intersects.length);
if(intersects.length > 0) {
alert("Click detected!");
}
});
}

Setting the mouse position this way is more accurate.
var rect = renderer.domElement.getBoundingClientRect();
mouse.x = ( ( event.clientX - rect.left ) / rect.width ) * 2 - 1;
mouse.y = - ( ( event.clientY - rect.top ) / rect.height ) * 2 + 1;

For mouse detecting (far or near! no matter!), do this:
put this in your global:
var pointerDetectRay, projector, mouse2D;
put this in your init() function:
pointerDetectRay = new THREE.Raycaster();
pointerDetectRay.ray.direction.set(0, -1, 0);
projector = new THREE.Projector();
mouse2D = new THREE.Vector3(0, 0, 0);
put this in your render() loop function:
pointerDetectRay = projector.pickingRay(mouse2D.clone(), camera);
and it's your mouse event:
function onDocumentMouseMove(event) {
event.preventDefault();
mouse2D.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse2D.y = -(event.clientY / window.innerHeight) * 2 + 1;
}
Now, every where that you want to detect the objects under your mouse pointer, use this:
var intersects = pointerDetectRay.intersectObjects(scene.children);
if (intersects.length > 0) {
var intersect = intersects[0];
// intersect is the object under your mouse!
// do what ever you want!
}

I use the following which, for me, works for larger distances:
var projector = new THREE.Projector();
function onDocumentMouseDown( event ) {
event.preventDefault();
var vector = new THREE.Vector3( ( event.clientX / window.innerWidth ) * 2 - 1, - ( event.clientY / window.innerHeight ) * 2 + 1, 0.5 );
projector.unprojectVector(vector,camera);
var raycaster = new THREE.Raycaster(camera.position,vector.sub(camera.position).normalize() );
var intersects = raycaster.intersectObjects( [sphere,cylinder,cube] );
if ( intersects.length > 0 ) {
intersects[ 0 ].object.material.transparent=true;
intersects[ 0 ].object.material.opacity=0.1;
console.log(intersects[0]);
}
}
This just sets the first selected object to semitransparent. Complete example at: https://github.com/josdirksen/learning-threejs/blob/master/chapter-09/02-selecting-objects.html

Related

Tracking Elements on ctx, ctx placed on boxgeometry threejs

I've applied ctx to all the side of BoxGeometry .furthermore, I'm using raycaster to get the data present on ctx so that onclick it gives x,y co-ordinates of ctx and index of that partcular side to understand which side the ctx is present.
this is my code
document.addEventListener ("mousedown",( event ) => {
var vector = new THREE.Vector3();
pointer.x = ( event.clientX / window.innerWidth ) * 2 - 1;
pointer.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
vector.unproject( camera );
raycaster.setFromCamera( pointer, camera);
var intersects = raycaster.intersectObject( this.cube );
if (intersects.length > 0) {
index = Math.floor(intersects[0].faceIndex / 2);
if (index == 0) {
let templabelarray = []
for (let i = 0; i < this.labels.length; i++) {
if (this.labels[i].typename == 'Left' ) {
templabelarray.push(this.labels[i]);
}
this.state.totalLabels.push(templabelarray);
let rect = this.ctxArr[0].getBoundingClientRect()
this.x = e.clientX - rect.left
this.y = e.clientY - rect.top
let i = 0, r;
this.labels will give me the array for ctx present on the particular side and further i"m pssing it into state variable
Using this I'm able to get the index but, unable to fetch x,y co-ordinates for ctx.
any help would be greatly appreciated

Three.js: not accurate to detect faceIndex when clicking the 2*2*2 geometry cube

I have created a 2*2*2 geometry cube via Three.js. Now I want to detect the click event when clicking the faces (24 faces in total).
Please check my current implementation at https://jsfiddle.net/agongdai/pdwg3myr/17/. When clicking on the faces, I want to console.log the current face index. But the index is not always accurate. For example, clicking on the top-left gray cell should show 0, but actually clicking the bottom part of it shows 2.
Please help me to check the mouse click event handler:
var raycaster = new THREE.Raycaster();
function onDocumentMouseDown( event ) {
var vector = new THREE.Vector3(
( event.clientX / window.innerWidth ) * 2 - 1,
- ( event.clientY / window.innerHeight ) * 2 + 1, 0.5 );
vector.unproject( camera );
raycaster.setFromCamera( vector, camera );
raycaster.set( camera.position, vector.sub( camera.position ).normalize() );
var intersects = raycaster.intersectObject( cube );
if ( intersects.length > 0 ) {
var index = Math.floor( intersects[0].faceIndex / 2 );
console.log(index);
}
}
Could anybody please help?
Em, after googling a lot, I found this page and applied the approach. It's working properly https://jsfiddle.net/agongdai/pdwg3myr/19/:
var raycaster = new THREE.Raycaster();
var mouse = new THREE.Vector2();
function onDocumentMouseDown(event) {
mouse.x = (event.clientX / renderer.domElement.clientWidth) * 2 - 1;
mouse.y = -(event.clientY / renderer.domElement.clientHeight) * 2 + 1;
raycaster.setFromCamera(mouse, camera);
var intersects = raycaster.intersectObject(cube);
if (intersects.length > 0) {
var index = Math.floor(intersects[0].faceIndex / 2);
console.log(index);
}
}
Update
To adjust to the left/top shift and scrolling, update it to https://jsfiddle.net/agongdai/pdwg3myr/24/:
function onDocumentMouseDown(event) {
const holder = renderer.domElement;
const rect = holder.getBoundingClientRect();
mouse.x = ((event.pageX - rect.left - window.scrollX) / holder.clientWidth) * 2 - 1;
mouse.y = -((event.pageY - rect.top - window.scrollY) / holder.clientHeight) * 2 + 1;
...
}

Uncaught TypeError: Cannot read property 'length' of null - Three.js

I've trying to draw the square wall by getting mouse clicks coordinates and extrude it.
I've picking up the mouse coordinates by clicking at the scene.
var onDocumentMouseDown = function ( event )
{
//update the mouse variable
mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
mouse.y = -( event.clientY / window.innerHeight ) * 2 + 1;
var vector = new THREE.Vector3(mouse.x, mouse.y, 0.5);
vector.unproject( camera );
var dir = vector.sub( camera.position ).normalize();
var distance = - camera.position.z / dir.z;
var pos = camera.position.clone().add( dir.multiplyScalar( distance));
console.log('mouse_x ' + pos.x + ' mouse_y ' + pos.y);
if (clickCount <= 3){
coord[clickCount] = {'x' : pos.x, 'y' : pos.y};
clickCount ++;
} else {
//make new wall and stop function
newshape = new THREE.Shape();
shape.moveTo(coord['0'].x ,coord['0'].y);
shape.lineTo(coord['0'].x, coord['1'].y);
shape.lineTo(coord['2'].x, +coord['2'].y);
shape.lineTo(coord['3'].x, coord['3'].y);
shape.lineTo(coord['0'].x, coord['0'].y);
var newextrudeSettings = {
//*******/
};
}
And when I've recived four coordinates, three.js throw the error:
Uncaught TypeError: Cannot read property 'length' of null
at Object.triangulateShape (three.js:26140)
at ExtrudeGeometry.addShape (three.js:26330)
at ExtrudeGeometry.addShapeList (three.js:26235)
at new ExtrudeGeometry (three.js:26211)
at HTMLDocument.onDocumentMouseDown (script.js:116)
To find points of intersection I prefer to use THREE.Raycaster() (though I've never used THREE.Projector() for this purpose).
This is the result of my code:
I hope I got your conception. Thus, all the stuff you need is here:
var raycaster = new THREE.Raycaster();
var mouse = new THREE.Vector2();
var intersects;
var controlPoints = [];
var clickCount = 0;
function onMouseDown(event) {
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
raycaster.setFromCamera(mouse, camera);
intersects = raycaster.intersectObjects(objects); // objects is an array which contains just the mesh of the plane
if (intersects.length > 0) {
if (clickCount <= 3) { // I took your idea of 4 clicks
controlPoints[clickCount] = intersects[0].point.clone(); // add a control point to the array
// visualization of a control point
var cp = new THREE.Mesh(new THREE.SphereGeometry(0.125, 16, 12), new THREE.MeshBasicMaterial({color: "red"}));
cp.position.copy(intersects[0].point);
scene.add(cp);
clickCount++;
} else { // on the fifth click we'll create our wall
shape = new THREE.Shape();
shape.moveTo(controlPoints[0].x, -controlPoints[0].z);
shape.lineTo(controlPoints[1].x, -controlPoints[1].z);
shape.lineTo(controlPoints[2].x, -controlPoints[2].z);
shape.lineTo(controlPoints[3].x, -controlPoints[3].z);
shape.lineTo(controlPoints[0].x, -controlPoints[0].z);
var extrudeSettings = {
steps: 1,
amount: 2,
bevelEnabled: false
};
var extrudeGeom = new THREE.ExtrudeGeometry(shape, extrudeSettings);
extrudeGeom.rotateX(-Math.PI / 2);
var wall = new THREE.Mesh(extrudeGeom, new THREE.MeshStandardMaterial({
color: "gray"
}));
scene.add(wall);
controlPoints = []; // we clear the array of control points
clickCount = 0; // and reset the counter of clicks
};
};
};
jsfiddle example. 4 clicks for setting control points, the fifth click creates a wall, and so on.

Unable to intersect Points

I unable to intersect Three.Points but i am able to intersect THREE.Mesh, below is my code: Can someone please help me to understand what mistake i am making. When i intersect the Points all i get is 0.
function init()
{
geometry = new THREE.Geometry();
vertex = new THREE.Vector3();
vertex.x=( [i + 3 ] * 140 ) - 1330;
vertex.y = - ( [i + 3 ] * 180 ) + 990;
vertex.z = 100;
geometry.vertices.push(vertex);
var particleTexture = THREE.ImageUtils.loadTexture(imgstr);
var materials = new THREE.PointsMaterial({
map:particleTexture,
size: 150
});
particles = new THREE.Points(geometry, materials);
scene.add(particles);
targetList.push(particles);
}
function onDocumentMouseMove( event ) {
event.preventDefault();
mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
}
function onDocumentMouseDown(event){
var projector = new THREE.Projector();
var vector = new THREE.Vector3(
(event.clientX / window.innerWidth) * 2 - 1,
-(event.clientY / window.innerHeight) * 2 + 1,
0.5);
projector.unprojectVector(vector, camera);
var raycaster = new THREE.Raycaster(camera.position,vector.sub(camera.position).normalize());
var intersects = raycaster.intersectObjects(targetList);
console.log(intersects.length);
}
function onDocumentMouseDown( event ) {
event.preventDefault();
if(event.which == 1) {
var raycaster = new THREE.Raycaster();
raycaster.setFromCamera( mouse, camera );
raycaster.params.Points.threshold = 100000; // bigger number = bigger sensitivity
var intersects = raycaster.intersectObjects( myobjectsarray,false); // myobject array is array of THREE.Points object
if ( intersects.length > 0 ) {console.log('tadaah',intersects);}
}
}

Picking Object3D loaded via OBJMTLLoader

When we load an Object3D with OBJMTLLoader, it is not possible to use raycaster to pick this object with mouse. Intersection array length is always 0. Any one knows the reason? Below is the code...
The loader routine
var loader2 = new THREE.OBJMTLLoader();
loader2.load('/assets/unwrap/masa/dogtasmasa.obj', '/assets/unwrap/masa/dogtasmasa.mtl', function (object) {
object.position.y = 1.5;
object.position.x = 0;
object.position.z = 2;
object.rotateX(-Math.PI / 2);
object.rotateZ(-Math.PI / 2);
object.scale.set(0.04, 0.04, 0.04);
object.castShadow = true;
scene.add(object);
});
and the picking
function onDocumentMouseDown(event) {
event.preventDefault();
SCREEN_WIDTH = window.innerWidth - 5;
SCREEN_HEIGHT = window.innerHeight - 5;
var vector = new THREE.Vector3((event.clientX / SCREEN_WIDTH) * 2 - 1, -(event.clientY / SCREEN_HEIGHT) * 2 + 1, 0.5);
projector.unprojectVector(vector, camera);
var raycaster = new THREE.Raycaster(camera.position, vector.sub(camera.position).normalize());
var intersects = raycaster.intersectObjects(scene.children);
if (intersects.length > 0) {
for (var i = 0; i < intersects.length; i++) {
var obj = intersects[i].object;
controls.enabled = false;
tControls.attach();
}
}
else {
controls.enabled = true;
tControls.detach();
}
}
The scene is the whole browser window. Any other mesh cerated via THREE types can be picked, but object3d not...
Thanks for all kinds of help
Add the recursive flag like so:
var intersects = raycaster.intersectObjects( objects, true );
three.js r.66

Resources