0

I'm making a Three.js application and I want to catch clicks on objects. When I create cube or sphere everything is ok, but I fail with polyhedron - Raycaster.intersectObjects() returns empty result.

My code is below (see click events in console.log()).

What can I do to make it work? Are there other ways to create polyhedrons?

<html> 
<head> 
 <title>Моё 3</title> 
 <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
 <style>
  body { margin: 0; }
  canvas { width: 100%; height: 100% }
 </style> 
</head> 
<body>
<div id="ThreeJS" style="position: absolute; left:0px; top:0px"></div>
 <script src="http://stemkoski.github.io/Three.js/js/Three.js"></script>
<script src="http://stemkoski.github.io/Three.js/js/Detector.js"></script>
<script src="http://stemkoski.github.io/Three.js/js/Stats.js"></script>
<script src="http://stemkoski.github.io/Three.js/js/OrbitControls.js"></script>
<script src="http://stemkoski.github.io/Three.js/js/THREEx.KeyboardState.js"></script>
<script src="http://stemkoski.github.io/Three.js/js/THREEx.FullScreen.js"></script>
<script src="http://stemkoski.github.io/Three.js/js/THREEx.WindowResize.js"></script>
<script>
/*
 Three.js "tutorials by example"
 Author: Lee Stemkoski
 Date: July 2013 (three.js v59dev)
*/

// MAIN
var polyhedronShape, polyhedronPts = [], cube, mesh;
// standard global variables
var container, scene, camera, renderer, controls, stats;
var keyboard = new THREEx.KeyboardState();
var clock = new THREE.Clock();

// custom global variables
var targetList = [];
var projector, mouse = { x: 0, y: 0 };

init();
animate();

// FUNCTIONS   
function init() 
{
 // SCENE
 scene = new THREE.Scene();
 // CAMERA
 var SCREEN_WIDTH = window.innerWidth, SCREEN_HEIGHT = window.innerHeight;
 var VIEW_ANGLE = 45, ASPECT = SCREEN_WIDTH / SCREEN_HEIGHT, NEAR = 0.1, FAR = 20000;
 camera = new THREE.PerspectiveCamera( VIEW_ANGLE, ASPECT, NEAR, FAR);
 scene.add(camera);
 camera.position.set(0,150,400);
 camera.lookAt(scene.position); 
 // RENDERER
 if ( Detector.webgl )
  renderer = new THREE.WebGLRenderer( {antialias:true} );
 else
  renderer = new THREE.CanvasRenderer(); 
 renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT);
 container = document.getElementById( 'ThreeJS' );
 container.appendChild( renderer.domElement );
 // EVENTS
 THREEx.WindowResize(renderer, camera);
 THREEx.FullScreen.bindKey({ charCode : 'm'.charCodeAt(0) });
 // CONTROLS
 controls = new THREE.OrbitControls( camera, renderer.domElement );
 // STATS
 stats = new Stats();
 stats.domElement.style.position = 'absolute';
 stats.domElement.style.bottom = '0px';
 stats.domElement.style.zIndex = 100;
 container.appendChild( stats.domElement );
 // LIGHT
 var light = new THREE.PointLight(0xffffff);
 light.position.set(0,250,0);
 scene.add(light);
 // FLOOR
 var floorTexture = new THREE.ImageUtils.loadTexture( 'images/checkerboard.jpg' );
 floorTexture.wrapS = floorTexture.wrapT = THREE.RepeatWrapping; 
 floorTexture.repeat.set( 10, 10 );
 var floorMaterial = new THREE.MeshBasicMaterial( { map: floorTexture, side: THREE.DoubleSide } );
 var floorGeometry = new THREE.PlaneGeometry(1000, 1000, 10, 10);
 var floor = new THREE.Mesh(floorGeometry, floorMaterial);
 floor.position.y = -0.5;
 floor.rotation.x = Math.PI / 2;
 scene.add(floor);
 // SKYBOX/FOG
 var skyBoxGeometry = new THREE.CubeGeometry( 10000, 10000, 10000 );
 var skyBoxMaterial = new THREE.MeshBasicMaterial( { color: 0x9999ff, side: THREE.BackSide } );
 var skyBox = new THREE.Mesh( skyBoxGeometry, skyBoxMaterial );
 scene.add(skyBox);
 
 ////////////
 // CUSTOM //
 ////////////

 
 //////////////////////////////////////////////////////////////////////

 // this material causes a mesh to use colors assigned to faces
 var faceColorMaterial = new THREE.MeshBasicMaterial( 
 { color: 0xffffff, vertexColors: THREE.FaceColors } );
 
 var sphereGeometry = new THREE.SphereGeometry( 80, 32, 16 );
 for ( var i = 0; i < sphereGeometry.faces.length; i++ ) 
 {
  face = sphereGeometry.faces[ i ]; 
  face.color.setRGB( 0, 0, 0.8 * Math.random() + 0.2 );  
 }
 var sphere = new THREE.Mesh( sphereGeometry, faceColorMaterial );
 sphere.position.set(0, 50, 0);
 scene.add(sphere);
 
 targetList.push(sphere);


 
 // Create an array of materials to be used in a cube, one for each side
 var cubeMaterialArray = [];
 // order to add materials: x+,x-,y+,y-,z+,z-
 cubeMaterialArray.push( new THREE.MeshBasicMaterial( { color: 0xff3333 } ) );
 cubeMaterialArray.push( new THREE.MeshBasicMaterial( { color: 0xff8800 } ) );
 cubeMaterialArray.push( new THREE.MeshBasicMaterial( { color: 0xffff33 } ) );
 cubeMaterialArray.push( new THREE.MeshBasicMaterial( { color: 0x33ff33 } ) );
 cubeMaterialArray.push( new THREE.MeshBasicMaterial( { color: 0x3333ff } ) );
 cubeMaterialArray.push( new THREE.MeshBasicMaterial( { color: 0x8833ff } ) );
 var cubeMaterials = new THREE.MeshFaceMaterial( cubeMaterialArray );
 // Cube parameters: width (x), height (y), depth (z), 
 //        (optional) segments along x, segments along y, segments along z
 var cubeGeometry = new THREE.CubeGeometry( 100, 100, 100, 1, 1, 1 );
 // using THREE.MeshFaceMaterial() in the constructor below
 //   causes the mesh to use the materials stored in the geometry
 cube = new THREE.Mesh( cubeGeometry, cubeMaterials );
 cube.position.set(-100, 50, -50);
 scene.add( cube );  
 targetList.push(cube);

 
 
 // polyhedron

 polyhedronPts.push( new THREE.Vector2 ( -100, 600 ) );
 polyhedronPts.push( new THREE.Vector2 ( 300, 600 ) );
 polyhedronPts.push( new THREE.Vector2 ( 600, -100 ) );
 
 polyhedronShape = new THREE.Shape( polyhedronPts );

 var extrudeSettings = {amount: 20}; // bevelSegments: 2, steps: 2 , bevelSegments: 5, bevelSize: 8, bevelThickness:5
 
 var geometry = new THREE.ExtrudeGeometry( polyhedronShape, extrudeSettings );

 mesh = THREE.SceneUtils.createMultiMaterialObject( geometry, [ new THREE.MeshBasicMaterial( { color: 0x00cc00 } ), new THREE.MeshBasicMaterial( { color: 0xff3333, wireframe: true, transparent: true } ) ] );
 mesh.position.set( -50, 50, 300 );
 mesh.rotation.set( 300, 0, 0 );
 //mesh.scale.set( 1, 1, 1 );
 scene.add( mesh );
 targetList.push(mesh);


 
 //////////////////////////////////////////////////////////////////////
 
 // initialize object to perform world/screen calculations
 projector = new THREE.Projector();
 
 // when the mouse moves, call the given function
 document.addEventListener( 'mousedown', onDocumentMouseDown, false );
 
}

function onDocumentMouseDown( event ) 
{
 // the following line would stop any other event handler from firing
 // (such as the mouse's TrackballControls)
 // event.preventDefault();
 
 //console.log("Click.");
 
 // update the mouse variable
 mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
 mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
 
 // find intersections

 // create a Ray with origin at the mouse position
 //   and direction into the scene (camera direction)
 var vector = new THREE.Vector3( mouse.x, mouse.y, 1 );
 projector.unprojectVector( vector, camera );
 var ray = new THREE.Raycaster( camera.position, vector.sub( camera.position ).normalize() );

 // create an array containing all objects in the scene with which the ray intersects
 var intersects = ray.intersectObjects( targetList );
 
 // if there is one (or more) intersections
 if ( intersects.length > 0 )
 {
  console.log("Hit @ " + toString( intersects[0].point ) );
  // change the color of the closest face.
  intersects[ 0 ].face.color.setRGB( 0.8 * Math.random() + 0.2, 0, 0 ); 
  intersects[ 0 ].object.geometry.colorsNeedUpdate = true;
 }

}

function toString(v) { return "[ " + v.x + ", " + v.y + ", " + v.z + " ]"; }

function animate() 
{
    requestAnimationFrame( animate );
 render();  
 update();
}

function update()
{
 if ( keyboard.pressed("z") ) 
 { 
  // do something
 }
 
 controls.update();
 stats.update();
}

function render() 
{
 renderer.render( scene, camera );
}

</script>

</body>
</html>
Somerussian
  • 381
  • 4
  • 17
  • 1
    Try `raycaster.intersectObjects( targetList, true )`. – WestLangley Jan 20 '15 at 06:55
  • Great, it works. Thanks, WestLangley – Somerussian Jan 20 '15 at 14:16
  • Polyhedron has rounded corners (rounded faces). Is it possible to set usual corners? – Somerussian Jan 20 '15 at 14:18
  • 1
    1. Do you know why "true" is required in your case? 2. You need to make a new post if you have another question. – WestLangley Jan 20 '15 at 16:09
  • @WestLangley Good question. I do not believe the second parameter set to "true" is required. The raycaster.intersectObjects() method takes 3 parameters, only the first one being required (the "targetList" or event.target.targetList) https://threejs.org/docs/#api/en/core/Raycaster.intersectObjects – Marty McGee Dec 13 '21 at 17:52

0 Answers0