Three.js make a group of meshes selectable/clickable

Hi everyone, I have a 3D model of a building and the task is to make each floor of the building clickable so that on a click the floor plan opens. I am using Raycaster in three js to detect meshes of the object, but how can I make a group of meshes selectable with three.js?
Please see the code bellow.

threejs - models
<canvas id="myCanvas"></canvas>

<script type="module">
		import * as THREE from '../build/three.module.js';

		import { GLTFLoader } from './jsm/loaders/GLTFLoader.js';
		import { DRACOLoader } from './jsm/loaders/DRACOLoader.js';
		import { OrbitControls } from './jsm/controls/OrbitControls.js';
		var camera, scene, renderer;

		// Instantiate a loader
		var loader = new GLTFLoader();
		var renderer;

		var scene = new THREE.Scene();
		var camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );

	

			renderer = new THREE.WebGLRenderer( { alpha: true } );
			renderer.setPixelRatio( window.devicePixelRatio );
			renderer.setSize( window.innerWidth, window.innerHeight );
			document.body.appendChild( renderer.domElement );

			var controls = new OrbitControls( camera, renderer.domElement );
			controls.addEventListener( 'change', render );
			controls.target.set( 0, 1.2, 2 );
			controls.minDistance = 1;
			controls.maxDistance = 300;
			controls.update();

			window.addEventListener( 'resize', onWindowResize, false );
			function render() {

				renderer.render( scene, camera );

				}

			function onWindowResize() {

						camera.aspect = window.innerWidth / window.innerHeight;
						camera.updateProjectionMatrix();

						renderer.setSize( window.innerWidth, window.innerHeight );
							render();
			}
			// Color for white
			renderer.setClearColor( 0xffffff, 1 );



						//LIGHTS
			var light = new THREE.AmbientLight(0xffff, 0.5);
			scene.add(light);
			var light2 = new THREE.PointLight(0xffffff, 2);
			scene.add(light2);

			var dracoLoader = new DRACOLoader();
			dracoLoader.setDecoderPath( '/examples/js/libs/draco/' );
			loader.setDRACOLoader( dracoLoader );

			var toIntersect = [];

			// Load a glTF resource
			loader.load(
				//'max.gltf',
				'./last.gltf',
				function ( gltf ) {
					console.log('gltf', gltf)
					camera.position.z = 40;
					scene.add( gltf.scene );
					var t = gltf.scenes[0].children;
					scene.traverse(function (t) {
						if (t instanceof THREE.Mesh) {
							toIntersect.push(t);
						}
					});
				
				},
				// called while loading is progressing
				function ( xhr ) {
					console.log('done')
					console.log( ( xhr.loaded / xhr.total * 100 ) + '% loaded' );

				},
				// called when loading has errors
				function ( error ) {
					console.log(error);
				}
			);

			var raycaster, mouse = { x : 0, y : 0 };
			raycaster = new THREE.Raycaster();
			var mouse = new THREE.Vector2();

			renderer.domElement.addEventListener( 'click', raycast, false );
		

			function raycast ( e ) {
			
				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, 1 );
				raycaster.setFromCamera( mouse, camera );
				var intersects = raycaster.intersectObjects( toIntersect );
				console.log(intersects)
				var INTERSECTED;

				if ( intersects.length > 0 )
				{
					var color = (Math.random() * 0xffffff);
					intersects[0].object.material.color.setHex(color);
				}
				

				}
</script>

three.js can’t do this by default since raycasting always works on primitive level (mesh, lines, points). The idea is to manage the group affiliation on application level. This can be done in various ways. If the meshes of one group share a single parent object (an instance of THREE.Group or THREE.Object3D), it’s easy to determine all siblings and perform the material modifications. Otherwise you need a custom data structure that groups logically associated meshes.

3 Likes

Thank you :slight_smile: