Different 3D models as hyperlinks

I shall begin with mentioning that I’m a total beginner, so I assume that there is a minor issue I keep missing over and over again while trying to make it work.

In short: I loaded a GLTF scene with 3 different objects and added the scene separately. I want to make each of the objects open a new page using a URL. Raycaster seems to work just fine, but when I add multiple URLs all the objects seem to open the same (first mentioned) page.

The console shows me that each object reacts on its own, but when adding URLs they all work together. I figured maybe I should group my objects once they GTFL is loaded, but I can’t seem to figure it out.
What am I missing?

Here is my GLTFLoader and Raycaster

const loader = new GLTFLoader().setPath('public/foldery/');
loader.load('folder 3d.gltf', (gltf) => {
  const mesh = gltf.scene;

  mesh.traverse((child) => {
    if (child.isMesh) {
      child.castShadow = true;
      child.receiveShadow = true;
      
    }
  });


  mesh.position.set(0, 1.35, 0);

  scene.add(mesh);

  const raycaster = new THREE.Raycaster();
  const pointer = new THREE.Vector2();
  
   let selectedObject = undefined;    
  
  document.addEventListener('mousedown', onMouseDown);
  
  
  function onMouseDown(event) {
    const coords = new THREE.Vector2(
      (event.clientX / renderer.domElement.clientWidth) * 2 - 1,
      -((event.clientY / renderer.domElement.clientHeight) * 2 - 1),
    );
  
    raycaster.setFromCamera(coords, camera);
  
  
  
    var intersections = raycaster.intersectObjects(scene.children, true);
  
  
  if (intersections.length > 0) {          // it seems to work up to this point
  
    
    
    window.open('https://www.amazon.com') (intersectObjects(cube1));

    window.open('https://www.google.com') (intersectObjects(cube2));

    window.open('https://www.facebook.com') (intersectObjects(cube3));    //all 3 cubes open amazon!

    }
  };

});

Try with the following:

    if (intersections.length > 0) {
  
      const object = intersections[0].object;

      if(object === cube1) window.open('https://www.amazon.com');
      else if(object === cube2) window.open('https://www.google.com');
      else window.open('facebook');

    }

Thank you for such fast reply.

I get: Uncaught ReferenceError: cube1 is not defined

:frowning:

cube1, 2 and 3, are supposedly parts/objects/children of the loaded model. You’ll have to define them first, then check the intersection.

You can find them by name scene.getObjectByName or by property scene.getObjectByProperty

I’ve put together a quick example on how to handle the raycaster, and dispatch the objects links by checking their respective names.

@Fennec that fiddle needs slight correction where name should actually be objectName, maybe try to paste the following code:

import * as THREE from "three"
const scene = new THREE.Scene()
const camera = new THREE.PerspectiveCamera(
  75,
  window.innerWidth / window.innerHeight,
  0.1,
  1000,
)
const renderer = new THREE.WebGLRenderer()
renderer.setSize(window.innerWidth, window.innerHeight)
document.body.appendChild(renderer.domElement)

// Raycaster and mouse
const raycaster = new THREE.Raycaster()
const mouse = new THREE.Vector2()

// Add sphere
const sphereGeometry = new THREE.SphereGeometry(0.75, 32, 32)
const sphereMaterial = new THREE.MeshBasicMaterial({ color: 0xff0000, transparent: true })
const sphere = new THREE.Mesh(sphereGeometry, sphereMaterial)
sphere.name = "sphere"
sphere.position.x = -2.5
scene.add(sphere)

// Add cube
const cubeGeometry = new THREE.BoxGeometry()
const cubeMaterial = new THREE.MeshBasicMaterial({ color: 0x00ff00, transparent: true })
const cube = new THREE.Mesh(cubeGeometry, cubeMaterial)
cube.name = "cube"
scene.add(cube)

// Add capsule
const capsuleGeometry = new THREE.CapsuleGeometry(0.5, 1, 4, 8)
const capsuleMaterial = new THREE.MeshBasicMaterial({ color: 0x0000ff, transparent: true })
const capsule = new THREE.Mesh(capsuleGeometry, capsuleMaterial)
capsule.name = "capsule"
capsule.position.x = 2.5
scene.add(capsule)

// Set camera position
camera.position.z = 5

// Event listener for mouse movement
window.addEventListener('mousemove', onMouseMove, false);

let hoveredObject = null;
function onMouseMove(event) {
  mouse.x = (event.clientX / window.innerWidth) * 2 - 1
  mouse.y = -(event.clientY / window.innerHeight) * 2 + 1

  raycaster.setFromCamera(mouse, camera)

  const intersects = raycaster.intersectObjects(scene.children)

  if (intersects.length > 0) {
  	document.body.style.cursor = "pointer";
    const object = intersects[0].object;

    if (hoveredObject !== object) {
			if(hoveredObject) hoveredObject.material.opacity = 1;
      console.log("test")
			object.material.opacity = 0.5;
      hoveredObject = object;
    }
  } else {
		document.body.style.cursor = "";
		if (hoveredObject) {
      hoveredObject.material.opacity = 1
      hoveredObject = null
    }
	}	
}

// Event listener for mouse click
window.addEventListener("click", onClick, false)

function onClick(event) {
  if (hoveredObject) {
    const clickedObject = hoveredObject
    const objectName = clickedObject.name
    

    if (objectName === "sphere") window.open(`https://example.com/sphere`)
    else if (objectName === "cube") window.open(`https://threejs.org`)
    else window.open(`https://example.com/capsule`)
  }
}

// Animation loop
function animate() {
  requestAnimationFrame(animate)
  renderer.render(scene, camera)
}
animate()

window.onresize = () => {
  camera.aspect = window.innerWidth / window.innerHeight
  camera.updateProjectionMatrix()
  renderer.setSize(window.innerWidth, window.innerHeight)
}
1 Like

Is it possible to add a custom data to objects in Blender?
If yes, then it’s enough to have, for example, object.userData.link property, that stores a URL.
And all you need to do is to obtain that URL. :thinking:
Demo: https://codepen.io/prisoner849/full/vEBBVVE

1 Like

Yep! My bad :sweat_smile:, also all the URLs were pointing to the same content, fixed with new URLs pointing to the documentation for each shape.

It is :blush:, and it’s discussed in this thread:

1 Like