detect mouse click / hover event on sides of cube in three js - three.js

I have a cube created using three js.(WebGL renderer)
I have applied texture on all six sides of cube.
I want to detect on which side the user has clicked on cube ?

look intersects[0].faceIndex
var geometry = new THREE.BoxGeometry( 200, 200, 200 );
var raycaster = new THREE.Raycaster();
Cube = new THREE.Mesh( geometry,
new THREE.MeshFaceMaterial( materials ) );
scene.add( Cube );
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.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 );
switch (index) {
case 0:
case 1:
case 2:
case 3:
case 4:
case 5:
}
}
}

Related

Raycasting vertices of a mesh with three.js

with threejs i can raycast some meshes that i created like that
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 500 );
var renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
var raycaster = new THREE.Raycaster();
var mouse = new THREE.Vector2();
//create a triangular geometry
var material = new THREE.MeshBasicMaterial( { color : 0x00cc00 } );
var geometry = new THREE.Geometry();
geometry.vertices.push( new THREE.Vector3( -0.5, -0.5, 0 ) );
geometry.vertices.push( new THREE.Vector3( 0.5, -0.5, 0 ) );
geometry.vertices.push( new THREE.Vector3( 0.5, 0.5, 0 ) );
//create a new face using vertices 0, 1, 2
var face = new THREE.Face3( 0, 1, 2 );
//add the face to the geometry's faces array
geometry.faces.push( face );
var object1 = new THREE.Mesh( geometry, material );
scene.add( object1 );
camera.position.z = 10;
//get mouse position
function onDocumentMouseMove( event ) {
event.preventDefault();
mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
}
//render funciton
function animate() {
raycaster.setFromCamera( mouse, camera );
var intersects = raycaster.intersectObject( object1 );
if ( intersects.length > 0 ) console.log( intersects );
renderer.render( scene, camera );
}
//run
window.addEventListener( 'mousemove', onDocumentMouseMove, false);
window.addEventListener( 'mousemove', animate, false);
animate();
this line of codes work very well for me. but i want to raycast of indices of this triangular shape instead of all mesh. What should i do?
i tried tihs but it does not work? why?
raycaster.insertObject( object1.geometry.vertices );
Raycaster.intersectObjects documentation
intersects should provide the intersected face, as well as the point of intersection. You can check the distance from the point to a face vertex against a minimum threshold (or simply the closest vertex to point), and return the vertex if it passes.
var iFace = intersects[0].face;
var iPoint = intersects[0].point;
var ab = iFace.a.distanceTo(iFace.b);
var ac = iFace.a.distanceTo(iFace.c);
var bc = iFace.b.distanceTo(iFace.c);
var lambda = Math.min(ab, ac, bc) - 0.1;
if(iFace.a.distanceTo(iPoint) <= lambda){
return iFace.a;
}
if(iFace.b.distanceTo(iPoint) <= lambda){
return iFace.b;
}
if(iFace.c.distanceTo(iPoint) <= lambda){
return iFace.c;
}

How can I make three.js update a scene after adding an object or group of objects?

I am fairly new to three.js and have a problem I can't readily find an answer for.
Here is a codepen that should sum up the situation: http://codepen.io/anon/pen/PPYPzO
var container, stats;
var camera, controls, scene, renderer, raycaster, mouse;
init();
animate();
add_world();
var indie_render = true;
for(var j = 0; j < 20; j++){
add_objects(20);
indie_render = !indie_render;
console.log("adding more objects...");
if(!indie_render){render();}
}
function add_world(){
var geometry = new THREE.BoxGeometry( 1000, 1000, 1000);
var mesh = new THREE.MeshBasicMaterial( {color: 0xf5f5dc, wireframe: false, opacity: 0.2, transparent:true } );
var world = new THREE.Mesh( geometry, mesh );
scene.add( world );
render();
}
function add_objects(num, indiv){
var geometry = new THREE.SphereGeometry( 5, 32,32 );
var material = new THREE.MeshBasicMaterial( { shading: THREE.FlatShading } );
material.color.setRGB( Math.random(), Math.random(), Math.random() );
for ( var i = 0; i < num; i ++ ) {
var mesh = new THREE.Mesh( geometry, material );
mesh.position.x = ( Math.random() - 0.5 ) * 1000;
mesh.position.y = ( Math.random() - 0.5 ) * 1000;
mesh.position.z = ( Math.random() - 0.5 ) * 1000;
mesh.updateMatrix();
mesh.matrixAutoUpdate = false;
scene.add( mesh );
if(indie_render){
console.log("individual render");
render();
}
}
}
function init() {
camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 1, 2000 );
camera.position.set(500, 500, -1000);
camera.up.set( 0, 1, 0 );
camera.lookAt(500,500,500);
controls = new THREE.OrbitControls( camera );
controls.addEventListener( 'change', render );
//world
scene = new THREE.Scene();
// lights
light = new THREE.DirectionalLight( 0xffffff );
light.position.set( 1, 1, 1 );
scene.add( light );
light = new THREE.DirectionalLight( 0x002288 );
light.position.set( -1, -1, -1 );
scene.add( light );
light = new THREE.AmbientLight( 0x222222 );
scene.add( light );
renderer = new THREE.WebGLRenderer( { antialias: false } );
renderer.setClearColor( 0x000000, 1 );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.sortObjects = false;
container = document.getElementById( 'container' );
container.appendChild( renderer.domElement );
mouse = new THREE.Vector2();
raycaster = new THREE.Raycaster();
container.addEventListener( 'mousemove', onMouseMove, false );
container.addEventListener( 'mousedown', onMouseDown, false );
window.addEventListener( 'resize', onWindowResize, false );
}
function animate() {
requestAnimationFrame( animate );
controls.update();
}
function render() {
renderer.render( scene, camera );
}
function onMouseMove( e ) {
mouse.x = ( e.clientX / renderer.domElement.width ) * 2 - 1;
mouse.y = - ( e.clientY / renderer.domElement.height ) * 2 + 1;
}
function onMouseDown( e ) {
mouse.x = ( e.clientX / renderer.domElement.width ) * 2 - 1;
mouse.y = - ( e.clientY / renderer.domElement.height ) * 2 + 1;
if(e.button == 2){ //right button
raycaster.setFromCamera( mouse, camera );
var intersects = raycaster.intersectObjects( scene.children, true );
if ( intersects.length > 0 ) {
var geometry = new THREE.SphereGeometry( 5, 32,32 );
var material = new THREE.MeshBasicMaterial( { color:0xff0000, shading: THREE.FlatShading } );
var mesh = new THREE.Mesh( geometry, material );
mesh.position.set(intersects[0].point.x, intersects[0].point.y, intersects[0].point.z);
scene.add(mesh);
render();
}
}
}
In this demo, I init() and animate() a blank scene, and then add a translucent cube, following what seems to be convention. Then I add groups of spheres to the scene in a nested for loop, randomly placing the spheres inside the cube and making a render() call after every scene.add() call.
Currently, the scene adds all the spheres and only then is visible to the user, even though I can add individual objects after the for-loop objects are added (by right-clicking on the cube). I need for the user to be able to watch as spheres are added, rather than waiting for the whole thing to be done.
I realize this may not be the most efficient way to render the scene, but it would be quite helpful if, for example, the info on the objects to be animated is arriving asynchronously from a server. Does anyone have a suggestion?
Thanks
1) First: move call render() to animate:
function animate() {
requestAnimationFrame( animate );
render();
controls.update();
}
2) Call add_objects asynchronously: setTimeout( add_objects, 0, 20 );
http://codepen.io/anon/pen/bVbEEP

three js highlight box while using fly controls

Is it possible to have a object highlighted while using fly controls? Like this but the boxes highlighting when you click them. What I ultimately want it to do is to click on a object and then it go onto a website. But for the moment I have tried to just highlight a object when it its clicked using:
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( mesh );
if ( intersects.length > 0 ) {
intersects[ 0 ].object.material.color.setHex( Math.random() * 0xffffff );
}
/*
// Parse all the faces
for ( var i in intersects ) {
intersects[ i ].face.material[ 0 ].color.setHex( Math.random() * 0xffffff | 0x80000000 );
}
*/
But this does not seem to work when I'm using fly controls is it possible? If not could the object just be highlighted to show some text?
I just tested this with the fly controls, its working fine. Make sure to raycast against an array that contains your meshes or just scene.children in this case.
var raycaster = new THREE.Raycaster();
var mouse = new THREE.Vector2();
function onDocumentMouseDown( event ) {
event.preventDefault();
mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
raycaster.setFromCamera( mouse, camera );
// calculate objects intersecting the picking ray
var intersects = raycaster.intersectObjects( scene.children );
if ( intersects.length > 0 ) {
intersects[ 0 ].object.material.color.setHex( Math.random() * 0xffffff );
}
}
container.addEventListener( 'mousedown', onDocumentMouseDown, false );
Three.js r.71
Consider checking out https://github.com/jeromeetienne/threex.domevents to easily add DOM Events like click or mouseover to your meshes.

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

three.js - intersectObjects with GeometryUtils.merge

I'm would like to create multiple cubes, add a click-event on each one of them and combine them in a "parent" object with GeometryUtils.merge.
var cubes=[];
var mainGeom = new THREE.Geometry();
var cubegeometry = new THREE.CubeGeometry(50, 50, 50, 1, 1, 1 );
cubes[1] = new THREE.Mesh( cubegeometry );
cubes[1].position.y = -50;
THREE.GeometryUtils.merge( mainGeom, cubes[1] );
cubes[2] = new THREE.Mesh( cubegeometry );
cubes[2].position.y = 50;
THREE.GeometryUtils.merge( mainGeom, cubes[2] );
cubes[3] = new THREE.Mesh( cubegeometry );
cubes[3].position.z = -50;
THREE.GeometryUtils.merge( mainGeom, cubes[3] );
cube = new THREE.Mesh( mainGeom,new THREE.MeshBasicMaterial( { color: 0xff0000 } ));
Combining is working fine.
But how can i add a click event on each one of them? I am using this:
function onDocumentMouseDown( event ) {
event.preventDefault();
mouse = {x : ( event.clientX / window.innerWidth ) * 2 - 1, y :- ( event.clientY / window.innerHeight ) * 2 + 1 };
var vector = new THREE.Vector3( mouse.x, mouse.y, 0.5 );
projector.unprojectVector( vector, camera );
var raycaster = new THREE.Raycaster( camera.position, vector.sub( camera.position ).normalize() );
var intersects = raycaster.intersectObjects( cube );
if ( intersects.length > 0 ) {
console.log(intersects);
}
}
But the intersects.length is always 0!
What am I doing wrong?
Thanks a lot!
Regards - Kosha
GeorgeProfenza has given me a good hint. The solution was to add these Subobject to a parent object, then - this was the solution! Thanks

Resources