How to set clickable ares on .gltf 3d model

Hi everyone. I’m new in three.js and I’m trying to build 3d model using .gltf format. I have a building, that is 3d model and rotatable and I want to make each apartment to be clickable.
Reading docs I understand that I should use Raycaster, but I couldn’t find anything similar to my issue.

Can anyone give me a direction or advise how to achieve this.

Thanks a lot.

Possible duplicate of Clicking on gltf models

Thanks for your reply, but jsfiddle example does not work. Could you please specify any other example that actually works to figure out.

Yeah, that fiddle links to raw.githack which doesn’t exist anymore. However, the code is still fine. You just need this function:

const raycaster = new THREE.Raycaster();
const mouse = new THREE.Vector2()

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

function onClick() {
	event.preventDefault();

	mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
	mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;

	raycaster.setFromCamera( mouse, camera );

	var intersects = raycaster.intersectObjects( scene.children, true );

	if ( intersects.length > 0 ) {
	    console.log( 'Intersection:', intersects[ 0 ] );
	}
}

You can check the value of intersects[ 0 ], it should be the model under the mouse that’s closest to the camera. Then to highlight, it you can change the material, apply an outline effect or whatever else you like.

I’ve updated the live example to the latest version of three.js. I’ve also locked the used version of three.js so the demo should not break anymore (unless jsdelivr shuts down^^).

1 Like

If you want clickable areas on a model, you have to seperate your model in different objects before you export it to e.g. glTF format.
For example your building would have a structure as follows:
-building (combines all subobjects)
—floor4
—floor3
—floor2
—floor1
—ground-floor
—basement

then you can distinguish by name for example (also possible to add tags in blender)

var intersects = raycaster.intersectObjects( scene.children, true );

if ( intersects.length > 0 ) {
    
      if(intersects[ 0 ].object.name == 'basement' ) ...
}
3 Likes

Thanks for your reply, I came to this solution as well to have separate floors and load them.

Can you tell me how to add name(as you mentioned in your comment) on the object to get it on mouse click?

Note that you can keep all your floors in one object. Therefore the obj structure above. So you have your floors seperated, but can load them at once.

You have several optiones for the tagging. Every object3D, Mesh, Group, etc. have a ‘name’ property.

let obj = new THREE.Object3D();
obj.name = 'floor1';

If you load your model, you would probably want to name your models in your 3D Software already. In Blender for example, the name of the object in the inspector list, will be the name of the mesh/object3D loaded (as above).

All of them have a userData obj as well. You can just add new properties to the userData obj.

obj.userData.tag = 'floor1';

Its also possible to set tags directly in blender, then you also read them via the userData obj.

if( obj.userData.blenderPropertyName = 'your value' ) ...

2 Likes

Thanks a lot for your help.

1 Like