Search code examples
javascriptthree.jshoverreact-three-fiber

Hover on Face with React Three Fiber


I'm still a noob to the whole Three.js ecosystem, and so I'm having difficulties even when I find something very close to a solution. For example, I've finally been able to find a solution to how to highlight the face of a geometry on hover through seanwasere's comment here on Three.js Discourse, on this CodeSandbox.

How do I translate this piece of code to React Three Fiber?

For reference, let me reproduce the code here:

import * as THREE from "/build/three.module.js";
import { OrbitControls } from "/jsm/controls/OrbitControls.js";
import Stats from "/jsm/libs/stats.module.js";

const scene = new THREE.Scene();

const camera = new THREE.PerspectiveCamera(
  75,
  window.innerWidth / window.innerHeight,
  0.1,
  100
);
camera.position.z = 2;

const light = new THREE.DirectionalLight();
light.position.set(2, 2, 10);
scene.add(light);

const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

const controls = new OrbitControls(camera, renderer.domElement);

const geometry = new THREE.BoxGeometry();
var materials = [
  new THREE.MeshPhongMaterial({
    color: 0x00ff00
  }),
  new THREE.MeshPhongMaterial({
    color: 0x00ff00
  }),
  new THREE.MeshPhongMaterial({
    color: 0x00ff00
  }),
  new THREE.MeshPhongMaterial({
    color: 0x00ff00
  }),
  new THREE.MeshPhongMaterial({
    color: 0x00ff00
  }),
  new THREE.MeshPhongMaterial({
    color: 0x00ff00
  })
];
const cube = new THREE.Mesh(geometry, materials);
scene.add(cube);

window.addEventListener(
  "resize",
  () => {
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
    renderer.setSize(window.innerWidth, window.innerHeight);
    render();
  },
  false
);

renderer.domElement.addEventListener("mousemove", onMouseMove, false);

const raycaster = new THREE.Raycaster();
let activeFaceIndex = 0;
function onMouseMove(event) {
  raycaster.setFromCamera(
    {
      x: (event.clientX / renderer.domElement.clientWidth) * 2 - 1,
      y: -(event.clientY / renderer.domElement.clientHeight) * 2 + 1
    },
    camera
  );

  const intersects = raycaster.intersectObject(cube, false);

  if (intersects.length > 0) {
    if (
      intersects[0].face.materialIndex !== activeFaceIndex &&
      activeFaceIndex !== -1
    ) {
      materials[activeFaceIndex].color.setHex(0x00ff00);
    }
    activeFaceIndex = intersects[0].face.materialIndex;
    materials[activeFaceIndex].color.setHex(0xff0000);
  } else {
    if (activeFaceIndex !== -1) {
      materials[activeFaceIndex].color.setHex(0x00ff00);
    }
    activeFaceIndex = -1;
  }
}
const stats = Stats();
document.body.appendChild(stats.dom);

var animate = function () {
  requestAnimationFrame(animate);
  cube.rotation.x += 0.01;
  cube.rotation.y += 0.01;
  controls.update();
  render();
  stats.update();
};

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

animate();

Another useful reference on how to highlight triangles on a face is here by Mugen87, or in this JSFiddle.


Solution

  • Here is a working R3F version based on the CodeSandbox version.