Could someone help me with this Im just new to learning threejs

Hello everyone! Im having problem with raycasting to an gltf scene’s child. My goal is to higlight a child object of my gltf file when hovered and can be clicked to have a pop up message. I already found out the pop ups but the console is giving me inaccurate object and not the one I click. and I have read some posts here but have not really found an answer to my problem. My gltf file is an Isometric bedroom. So when I click the table for example, its giving me another object which is the wall or the bed. Someone help me plss. here is what I have so far

import * as THREE from ‘three’;
import { GLTFLoader } from ‘three/addons/loaders/GLTFLoader.js’;
import { OrbitControls } from ‘three/addons/controls/OrbitControls.js’;

let renderer, scene, camera, controls, spotLight, loader, isoroom;
const raycaster = new THREE.Raycaster();
const pointer = new THREE.Vector2();
init();
animate();
//render

function init(){
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.outputColorSpace = THREE.SRGBColorSpace;
renderer.setSize(1800,700);
renderer.setClearColor(0x000000);
renderer.setPixelRatio(window.devicePixelRatio);
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
document.body.appendChild(renderer.domElement);

//scene
scene = new THREE.Scene();

camera = new THREE.PerspectiveCamera(10, window.innerWidth / window.innerHeight, .1, 500);
camera.position.set(7, 15, 11);

controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.enablePan = false;
controls.minDistance = 60;
controls.maxDistance = 100;
controls.minPolarAngle = 1;
controls.maxPolarAngle = 5;
controls.autoRotate = false;
controls.target = new THREE.Vector3(0,0, 0);
controls.update();

const groundGeometry = new THREE.PlaneGeometry(250, 250, 105, 105);
groundGeometry.rotateX(-Math.PI / 2);
const groundMaterial = new THREE.MeshStandardMaterial({
color: 0x555555,
side: THREE.DoubleSide
});
const groundMesh = new THREE.Mesh(groundGeometry, groundMaterial);
groundMesh.castShadow = true;
groundMesh.receiveShadow = true;
scene.add(groundMesh);

spotLight = new THREE.SpotLight(0xffffff, 5, 60, .00001, 100);
spotLight.position.set(5, 30, 1);
spotLight.castShadow = true;
spotLight.shadow.bias = -0.000001;
scene.add(spotLight);

loader = new GLTFLoader().setPath(‘public/final_bed/’);
loader.load(‘scene.gltf’, (gltf) => {
isoroom = gltf.scene;
isoroom.traverse((object) => {
if (object.isMesh === true) {
object.material.userData.color = object.material.color.clone();
}
});

isoroom.name = 'dice';

isoroom.position.set(5, 1, 7);
scene.add(isoroom);

document.getElementById('progress-container').style.display = 'none';

}, ( xhr ) => {
document.getElementById(‘progress’).innerHTML = LOADING ${Math.max(xhr.loaded / xhr.total, 1) * 100}/100;
},);

window.addEventListener( ‘resize’, onWindowResize );
renderer.domElement.addEventListener(‘click’, onClick);
}

//resize
function onWindowResize() {

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

}

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

function onClick(event) {

pointer.x = (event.clientX / window.innerWidth) * 2 - 1;

pointer.y = -(event.clientY / window.innerHeight) * 2 + 1;
raycaster.setFromCamera(pointer, camera);
const intersects = raycaster.intersectObjects(isoroom.children);

if ( intersects.length > 0 ) {
var object = intersects[0].object;
alert(object);
console.log(object);
}
}

is it possible to use (child.isObject3d)? like this?

gltf.traverse((child) => {
// Check if the child is an object3D (mesh, group, etc.)
if (child.isObject3D) {
// Do something with the object, for example, change its material or position
child.material = new THREE.MeshBasicMaterial({ color: 0xff0000 });
child.position.set(0, 0, 0);
}
});

  1. Please format the code - 6th button in the formatting tab - it’s not really readable in copy-paste text form :smiling_face_with_tear:
  2. Three.Object3D is an abstraction that isn’t renderable - Three.Mesh is though. You can check whether intersected object is a renderable thing even simpler though, just by checking the material:
if (child.material) {
  // NOTE Do something, you can assume this is a Mesh
}
  1. Raycaster returns intersected objects sorted by distance - so the closest object first. This can include objects that aren’t always visible in the scene (ie. these that have opacity as 0.0.) You’d need to debug why it returns the incorrect object.
import * as THREE from 'three';
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';

let renderer, scene, camera, controls, spotLight, loader, isoroom;
const raycaster = new THREE.Raycaster();
const pointer = new THREE.Vector2();
init();
animate();
//render

function init(){
  renderer = new THREE.WebGLRenderer({ antialias: true });
  renderer.outputColorSpace = THREE.SRGBColorSpace;
  renderer.setSize(1800,700);
  renderer.setClearColor(0x000000);
  renderer.setPixelRatio(window.devicePixelRatio);
  renderer.shadowMap.enabled = true;
  renderer.shadowMap.type = THREE.PCFSoftShadowMap;
  document.body.appendChild(renderer.domElement);

  //scene
  scene = new THREE.Scene();

  camera = new THREE.PerspectiveCamera(10, window.innerWidth / window.innerHeight, .1, 500);
  camera.position.set(7, 15, 11);

  controls = new OrbitControls(camera, renderer.domElement);
  controls.enableDamping = true;
  controls.enablePan = false;
  controls.minDistance = 60;
  controls.maxDistance = 100;
  controls.minPolarAngle = 1;
  controls.maxPolarAngle = 5;
  controls.autoRotate = false;
  controls.target = new THREE.Vector3(0,0, 0);
  controls.update();

  const groundGeometry = new THREE.PlaneGeometry(250, 250, 105, 105);
  groundGeometry.rotateX(-Math.PI / 2);
  const groundMaterial = new THREE.MeshStandardMaterial({
    color: 0x555555,
    side: THREE.DoubleSide
  });
  const groundMesh = new THREE.Mesh(groundGeometry, groundMaterial);
  groundMesh.castShadow = true;
  groundMesh.receiveShadow = true;
  scene.add(groundMesh);

  spotLight = new THREE.SpotLight(0xffffff,  5, 60, .00001, 100);
  spotLight.position.set(5, 30, 1);
  spotLight.castShadow = true;
  spotLight.shadow.bias = -0.000001;
  scene.add(spotLight);

  loader = new GLTFLoader().setPath('public/final_bed/');
  loader.load('FINALITY.glb', (gltf) => {
    isoroom = gltf.scene;
    isoroom.traverse((object) => {
      if (object.isMesh === true) {
        object.material.userData.color = object.material.color.clone();
      }
    });

    isoroom.name = 'dice';

    isoroom.position.set(5, 1, 7);
    scene.add(isoroom);

    document.getElementById('progress-container').style.display = 'none';
  }, ( xhr ) => {
    document.getElementById('progress').innerHTML = `LOADING ${Math.max(xhr.loaded / xhr.total, 1) * 100}/100`;
  },);


  window.addEventListener( 'resize', onWindowResize );
  renderer.domElement.addEventListener('click', onClick);
}

  //resize
  function onWindowResize() {

    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
    renderer.setSize( window.innerWidth, window.innerHeight );
  }


function animate() {
  requestAnimationFrame(animate);
  controls.update();
  renderer.render(scene, camera);
}

function onClick(event) {

	pointer.x = (event.clientX / window.innerWidth) * 2 - 1;
  pointer.y = -(event.clientY / window.innerHeight) * 2 + 1;
  raycaster.setFromCamera(pointer, camera);
  const intersects = raycaster.intersectObjects(isoroom.children);

  if ( intersects.length > 0 ) {
    var object = intersects[0].object;
    alert('clicked on object: ' + object.name);
    console.log(object);
  }
  
}






IT still returns the incorect object. Maybe I have to edit the gltf file in blender properly. Thanks!

Make sure you’re setting second parameter of intersectObjects to true.

1 Like