Clicking on gltf models

I was wandering if there is any method in three.js that allows the user to click on a model (for example an imported gltf file from blender) and as a result of the click something else to happen (for example a pop up box or an image is displayed).

I was trying to look it up in the three.js website but all found was the raycaster class. But from my understanding this will only work with objects that have a mesh and thus only the ones that are made in three.js itself with the geometry properties.

I also found tutorials with the use of threex.domevents. But once again I don’t see how this can be used with the models imported from blender. I tried using it but it only caused my models to disappear from the screen.

So is there another solution? or is it impossible?

I really appreciate the help,thank you very much.

I recommend you use raycaster.

Raycaster will not only work on objects made in THREE.js itself. It will also work on GLTF, GLB, OBJ, FBX, … models.

1 Like

/cc

Here is the official glTF example enhanced with basic raycasting:

https://jsfiddle.net/ct4dboym/

Instead of logging something to the console, you can of course perform an arbitrary action. Most important part is the onClick() handler which triggers the actual intersection:

function onClick( event ) {

	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 ] );

	}

}
7 Likes

Hello @Mugen87, I have a proplem when I created Mouse Click Raycaster event on model GLTF loader import from Blender. I want click on a model and it will zoom in and other model will hide but it not work. How do I catch the click event on a GLTF model ?.

function loadModel(url) {
  return new Promise((resolve) => {
    gltfLoader.load(url, resolve);
  });
}
let model1, model2;

let p1 = loadModel("models/human_teeth.gltf").then((result) => {
  model1 = result.scene;
});
let p2 = loadModel("models/human_teeth1.gltf").then((result) => {
  model2 = result.scene.children[0];
});
Promise.all([p1, p2]).then(() => {
  //do something to the model

  //add model to the scene
  scene.add(model1, model2);
  //continue the process
});
const raycaster = new THREE.Raycaster();
let currentIntersect = null;
const rayOrigin = new THREE.Vector3(-3, 0, 0);
const rayDirection = new THREE.Vector3(10, 0, 0);
rayDirection.normalize();
/**
 * Sizes
 */
const sizes = {
  width: window.innerWidth,
  height: window.innerHeight,
};

window.addEventListener("resize", () => {
  // Update sizes
  sizes.width = window.innerWidth;
  sizes.height = window.innerHeight;

  // Update camera
  camera.aspect = sizes.width / sizes.height;
  camera.updateProjectionMatrix();

  // Update renderer
  renderer.setSize(sizes.width, sizes.height);
  renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
});

/**
 * Camera
 */
// Base camera
const camera = new THREE.PerspectiveCamera(
  55,
  sizes.width / sizes.height,
  0.1,
  100
);
camera.position.set(-12, 6, 8);
scene.add(camera);

const mouse = new THREE.Vector2();
window.addEventListener("mousemove", (event) => {
  mouse.x = (event.clientX / sizes.width) * 2 - 1;
  mouse.y = -(event.clientY / sizes.height) * 2 + 1;
});
window.addEventListener("click", () => {
  if (currentIntersect) {
    switch (currentIntersect.object) {
      case model1:
        console.log("click on object 1");
        break;

      case model2:
        console.log("click on object 2");
        break;
    }
  }
});
// Controls
const controls = new OrbitControls(camera, canvas);
controls.target.set(0, 4, 0);

/**
 * Renderer
 */
const renderer = new THREE.WebGLRenderer({
  canvas: canvas,
});
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
renderer.setSize(sizes.width, sizes.height);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));

/**
 * Animate
 */

const tick = () => {
  raycaster.setFromCamera(mouse, camera);
  const objectsToTest = [model1, model2];
  const intersects = raycaster.intersectObjects(objectsToTest);

  if (intersects.length) {
    if (!currentIntersect) {
      console.log("mouse enter");
    }

    currentIntersect = intersects[0];
  } else {
    if (currentIntersect) {
      console.log("mouse leave");
    }

    currentIntersect = null;
  }
controls.update();

  // Render
  renderer.render(scene, camera);
  // scene.background = new THREE.Color(0x555252);
  // Call tick again on the next frame
  window.requestAnimationFrame(tick);
};

tick();

And I have an error:
Uncaught TypeError: Cannot read properties of undefined (reading ‘layers’)
at intersectObject (three.module.js:46609:1)
at Raycaster.intersectObjects (three.module.js:46589:1)
at tick (script.js:153:32)
at script.js:191:1
at bundle.afdd5463a1049c8d.js:60375:3
at bundle.afdd5463a1049c8d.js:60377:12
So is there another solution? or is it impossible?

I really appreciate the help,thank you very much.

1 Like

any solution for this?