Determine the object that was clicked in three.js - three.js

I wish to detect when an object has been clicked. I am generating a page from the three.js editor. It is a brilliant environment.
Following the advice from this post (https://mandemeskel.wordpress.com/2013/08/19/mouse-events-raycasting-with-three-js/) I ended up with the following code. I have placed a comment where I am having a problem.
function mousedown( event ) {
var scene = this.parent;
var projector = new THREE.Projector();
var mouse_vector = new THREE.Vector3(),
mouse = { x: 0, y: 0, z: 1 },
ray = new THREE.Raycaster( new THREE.Vector3(0,0,0), new THREE.Vector3(0,0,0) ),
intersects = [];
mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
mouse_vector.set( mouse.x, mouse.y, mouse.z );
// How do I get the camera?
projector.unprojectVector( mouse_vector, camera );
var direction = mouse_vector.sub( camera.position ).normalize();
ray.set( camera.position, direction );
var object = scene.getObjectByProperty( 'uuid', this.uuid, true );
intersects = ray.intersectObject( object );
if( intersects.length ) {
alert( "hit" );
event.preventDefault();
}
}
I have temporarily fixed the problem by modifying app.js in the published page. By moving the camera variable to be global I got around it, but it is obviously a fudge. Is there any way to access the camera generated by the editor without modifying app.js?
var camera;
var APP = {
Player: function () {
....

The awesome mrdoob has modified the editor to include a getCamera() call
https://github.com/mrdoob/three.js/issues/7510#issuecomment-153059247
The default camera can now be accessed by
var camera = player.getCamera();

Related

Avoid collision with walls. first-person

I've created navigation mesh on my Scene with walkable Property in Blender. The walkable area is floor without objects (You can see in the attached image).I could load it as glb data in my Three js project, connect it with addevent click action and calculate between mouse Position and my navmesh by Raycasting. The problem is when i click on the floor ,returned array via intersectObject, has length of one but as soon as i click on the walls it returns null array, even though I'm still on the the navMesh. btw I'm using first-person Camera. I've searched everywhere and the only solution that I've found was Three-pathfinding library-> function clampStep but there is no example for that. Any suggestion how can i restrict movement of camera between the walls?
Could Verge3d from Blender help or it has nothing to do with my Problem?
I'm totally beginner in both threejs and blender, would really appreciate any help or suggestion.
loading glb and mesh:
init: function(){
this.scene = new THREE.Scene();
this.scene.background = new THREE.Color(0xdddddd);
this.camera.rotation.y = 10 / 180 * Math.PI;
this.camera.position.x = 10;
this.camera.position.y = 3;
this.camera.position.z = 20;
this.renderer = new THREE.WebGLRenderer({ antialias: true });
this.renderer.setSize(window.innerWidth, window.innerHeight);
this.renderer.outputEncoding = THREE.sRGBEncoding;
this.renderer.physicallyCorrectLights = true;
document.body.appendChild(this.renderer.domElement);
this.controls = new FirstPersonControls(this.camera, this.renderer.domElement);
this.controls.movementSpeed = 5;
this.controls.lookSpeed = 0.01;
this.controls.noFly = false;
this.controls.lookVertical = false;
this.hlight = new THREE.AmbientLight(0xffffff, 3);;
this.scene.add(this.hlight);
let loader = new GLTFLoader();
loader.load("/ShowRoom121122.glb", (gltf) => {
const self = this;
gltf.scene.traverse(function (child) {
if (child.isMesh){
if (child.name=="navMesh_1"){
self.navmesh=child;
child.material.transparent = true;
child.material.opacity = 0.5;
const mesh = new THREE.Mesh(child.geometry, new THREE.MeshBasicMaterial({ wireframe: true, color: 0x111111}));
mesh.position.copy(child.position);
gltf.scene.add(mesh);
}else{
child.castShadow = false;
child.receiveShadow = true;
}
self.scene.add( gltf.scene );
}
})});
this.renderer.setAnimationLoop(this.animate)
},
raycasting:
raycast:function(e){
debugger
this.mouse.x = ( e.clientX / window.innerWidth ) * 2 - 1;
this.mouse.y = - ( e.clientY / window.innerHeight ) * 2 + 1;
//2. set the picking ray from the camera position and mouse coordinates
this.raycaster.setFromCamera( this.mouse, this.camera );
//3. compute intersections
const intersects = this.raycaster.intersectObject( this.navmesh );
if (intersects.length>0){
const pt = intersects[0].point;
console.log(pt);
} },
animate:function(){
this.controls.update(0.01);
this.renderer.render(this.scene, this.camera);
this.renderer.domElement.addEventListener( 'click', this.raycast,false );
}
},

How to add event for a mesh in threejs?

I am new to three.js, I am try to create a click event for a mesh I have created :
const geometry = new THREE.BoxBufferGeometry(size,size*0.5,size);
const material = new THREE.MeshStandardMaterial({color:0x00aa00 });
var option1 = new THREE.Mesh(geometry, material);
option1.position.set( 6, 5, 1)
// option1.callback = objectClickHandler;
// option1.on('click', function(ev) {
// console.log(ev)
// });
console.log(option1)
scene.add(option1)
But this is not working. onclick I will hide and show the object I have imported. But not able to do so as click event is not triggering.Help would be much appreciated.
everything happens in three.js is only in this one DOM that is the canvas.
to detect a click on a mesh, what you do is:
add a click event listener on the canvas.
check if mouse is on the mesh when clicked.
and to check if mouse is on mesh, you use RayCaster.
it should be something like this:
var raycaster = new THREE.Raycaster();
var mouse = new THREE.Vector2();
var targetMesh
function onMouseClick( event ) {
raycaster.setFromCamera( mouse, camera );
var isIntersected = raycaster.intersectObject( targetMesh );
if (isIntersected) {
console.log('Mesh clicked!')
}
}
function onMouseMove( event ) {
mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
}
window.addEventListener( 'mouseclick', onMouseClick, false );
window.addEventListener( 'mousemove', onMouseMove, false );
documents: https://threejs.org/docs/#api/en/core/Raycaster

Three.js mouse picking, 3D model with OBJMTLLoader

i started programing with Three.js. Now i have a problem, when i load an Object with the OBJMTLLoader i dont get any access to change the position of the object with mouseMove. I have pushed the obects into the objects array but it doesnt work. If anybody could help or knowes a good tutorial would be really fine. I hope the structure how i wrote the question is clear....its the first time i´m using stacko.
These are my variables and the function i try to change the objects:
var mouse = new THREE.Vector2();
var raycaster = new THREE.Raycaster();
var objects = [];
function onDocumentMouseMove( event ) {
event.preventDefault();
mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
raycaster.setFromCamera(mouse,camera);
intersects = raycaster.intersectObjects(objects);
if (intersects.length> 0) {
intersects[0].object.position.set(5, 5 ,5);
}
}
Here i load the object with the OBJMTLLoader....this works:)
var loader = new THREE.OBJMTLLoader();
loader.load('models/sofa4.obj', 'models/sofa4.mtl', function(object) {
// console.log(object);
object.traverse(function(child) {
if (child instanceof THREE.Mesh) {
child.material.shininess = 0;
// console.log(child.material);
}
});
object.position.z = 2;
object.position.y = -5.95;
object.scale.set(0.05,0.05,0.05);
scene.add(object);
objects.push(object);
});

three.js orthographic camera object picking

i am trying to pick objects in a scene where i use an orthographic camera.
my code fragment already works, but it is not precise.
i already found some answers on stackoverflow, but those are deprecated or won't work anymore at all.
here is my code onMouseDown
function onDocumentMouseUp( event ) {
event.preventDefault();
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);
var pos = camera.position;
var ray = new THREE.Raycaster(pos, vector.unproject(camera).sub(camera.position).normalize());
var intersects = ray.intersectObjects(objects);
if (intersects.length > 0) {
console.log("touched:" + intersects[0]);
}
else {
console.log("not touched");
}
}
please see http://jsfiddle.net/ujzpe07t/1/
if you click some pixels away left/right/above/below the cube, it still tells me that the object was touched.
i am using three.js r69.
any hints would be very much appreciated.
thanks, cheers!
Here is the pattern to use when raycasting (picking) with either an orthographic camera or a perspective camera:
var raycaster = new THREE.Raycaster(); // create once
var mouse = new THREE.Vector2(); // create once
...
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.intersectObjects( objects, recursiveFlag );
three.js r.84

Three.js Raycasting with Camera as Origin

I'm trying to determine whether a point in space is visible to the camera or hidden behind other objects in the scene. I'm doing this by casting a ray from the position of the camera to that point in space and testing if that ray is intersected by an set of intersectable objects.
My problem is no intersections occur until the camera position itself intersects one of the objects in the set of intersectable objects.
I've created a jsfiddle in which, if an intersection is detected, a line is drawn from the camera position to the position in space i'm testing for visibility. Currently I believe, the line is only draw at specific points where the camera position intersects the set of intersectable objects.
How do I get the intersections to be registered as they should be, without having to have the camera position intersect objects in the set of intersectable objects?
the code:
var container;
var camera, controls, scene, renderer;
init();
animate();
function init() {
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 10000);
camera.position.z = 1000;
controls = new THREE.OrbitControls(camera);
controls.rotateSpeed = 1.0;
controls.zoomSpeed = 1.2;
controls.panSpeed = 0.8;
controls.noZoom = false;
controls.noPan = false;
controls.staticMoving = true;
controls.dynamicDampingFactor = 0.3;
controls.keys = [65, 83, 68];
controls.addEventListener('change', render);
// world
scene = new THREE.Scene();
var testObject_G = new THREE.CubeGeometry(100, 100, 5);
var testObject_M = new THREE.MeshBasicMaterial({
color: 0xBBBBBB
});
var testObject_Mesh = new THREE.Mesh(testObject_G, testObject_M);
testObject_Mesh.position.x = -150;
scene.add(testObject_Mesh);
var testObject_Mesh2 = new THREE.Mesh(testObject_G, testObject_M);
testObject_Mesh2.position.x = 0;
scene.add(testObject_Mesh2);
var testObject_Mesh3 = new THREE.Mesh(testObject_G, testObject_M);
testObject_Mesh3.position.x = 150;
scene.add(testObject_Mesh3);
scene2 = new THREE.Object3D();
// renderer
renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(0xffffff, 1);
container = document.getElementById('container');
container.appendChild(renderer.domElement);
//
window.addEventListener('resize', onWindowResize, false);
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
controls.handleResize();
render();
}
function animate() {
requestAnimationFrame(animate);
controls.update();
}
function render() {
renderer.render(scene, camera);
castRays();
}
function castRays() {
// rays
var direction = new THREE.Vector3(0, 200, -200);
var startPoint = camera.position.clone();
var ray = new THREE.Raycaster(startPoint, direction);
scene.updateMatrixWorld(); // required, since you haven't rendered yet
var rayIntersects = ray.intersectObjects(scene.children, true);
if (rayIntersects[0]) {
console.log(rayIntersects[0]);
var material = new THREE.LineBasicMaterial({
color: 0x0000ff
});
var geometry = new THREE.Geometry();
geometry.vertices.push(new THREE.Vector3(ray.ray.origin.x, ray.ray.origin.y, ray.ray.origin.z));
geometry.vertices.push(new THREE.Vector3(ray.ray.direction.x, ray.ray.direction.y, ray.ray.direction.z));
var line = new THREE.Line(geometry, material);
scene2.add( line );
}
scene.add(scene2);
}
Thank you
For anyone currently seeing this thread, THREE.Projector has been replaced.
Three.js THREE.Projector has been moved to
The code below handles a 3D vector. If you go to the link above, the first commenter provided the code for a 2D vector.
var vector = new THREE.Vector3();
var raycaster = new THREE.Raycaster();
var dir = new THREE.Vector3();
...
if ( camera instanceof THREE.OrthographicCamera ) {
vector.set( ( event.clientX / window.innerWidth ) * 2 - 1, - ( event.clientY / window.innerHeight ) * 2 + 1, - 1 ); // z = - 1 important!
vector.unproject( camera );
dir.set( 0, 0, - 1 ).transformDirection( camera.matrixWorld );
raycaster.set( vector, dir );
} else if ( camera instanceof THREE.PerspectiveCamera ) {
vector.set( ( event.clientX / window.innerWidth ) * 2 - 1, - ( event.clientY / window.innerHeight ) * 2 + 1, 0.5 ); // z = 0.5 important!
vector.unproject( camera );
raycaster.set( camera.position, vector.sub( camera.position ).normalize());
}
var intersects = raycaster.intersectObjects( objects, recursiveFlag );`
Your idea of casting a ray is good, however raycasting in three.js already does this :
mouse.x = ( ( event.clientX - renderer.domElement.offsetLeft ) / renderer.domElement.width ) * 2 - 1;
mouse.y = - ( ( event.clientY - renderer.domElement.offsetTop ) / renderer.domElement.height ) * 2 + 1;
this formula maps a pixel coordinate from Screen Space to a point in Normalized Device Coordinate ( NDC ) Space.
projector.unprojectVector( vector, camera );
maps a point from NDC Space to a point in World space
Raycaster then creates a ray from the camera position through that world point.
Here is your working Fiddle in which I changed the way of raycasting in your scene and this works, all you have to do now is creating a ray with the right coordinates that I provided you.
r.68

Resources