Is there any way to make glb/gltf viewer with interaction through react three fiber?

Basically, I want to show multiple models. When a user clicks on a particular model from react component the model should appear and show in the same position, with the same camera setup and cover the model whatever the size is. And I have added some interaction with a model and that interaction should work with another model. Is there any way to do that?. I’m using react three fiber

its all still just plain threejs meshes, materials and cameras. if you know how to deal with a camera then nothing changes. generally if you’re dealing with gltf, try gltfjsx. zoom to fit is available in drei as <Bounds>

But how I’m gonna add on click event in some specific mesh then without the jsx? Obviously I don’t want to generate jsx for different models again and again

I have models like this:

import { useLoader, useThree } from '@react-three/fiber'
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'
import { Suspense } from 'react'
import * as THREE from 'three'

export default function Scene() {
    const gltf = useLoader(GLTFLoader, '3D_Model/model.glb')
    const {nodes,materials}=gltf
    const {scene}=useThree()
    // materials["UnitBox"]
    return (
      <Suspense fallback={null}>
        <primitive object={gltf.scene}  />
      </Suspense>
    )
  }

I want to add onClick and onPointerDown event to some specific material/mesh like the commented out material "materials[“UnitBox”]. How can I do that?

you must know a mesh in order to put a click event on it. if you don’t care and anything is clickable you can do this:

function Model({ url }) {
  const { scene } = useGLTF(url)
  return <primitive object={scene} onClick={console.log} />
}

this will fire on every object hit within, you get the object in event.object.

btw, you’re handling suspense wrong. useLoader/useGLTF is what’s causing suspense, not the primitive. the suspense bound must wrap the component itself. i’d also suggest you use useGLTF from drei because it handles compression for you. the plain THREE.GLTFLoader won’t load draco or meshopt compressed files without some churn like providing wasm binaries and such things.

<Suspense fallback={null}>
  <Model url="/model.glb" />
</Suspense>

Thank you. I have used useGLTF from drei. It is working fine. But what I said before I want a specific mesh to be clickable not the whole.


This is what I’m getting when I console the material. What I want is only the M_UnitBox material should be clickable.

Here is what I got when I consoled the scene. You can see the mesh here. So how can I make those mesh clickable?

<primitive object={scene} onClick={e => {
  if (e.object.material === materials.M_UnitBox) {
    // ...
  }

if you need specific things inside to carry out specific actions i would not bypass gltfjsx. otherwise you’re dealing with a blackbox which is what the gltf format is, a nested soup of content. you may get away with broad actions like above, or you can write your own raycasting mechanism ofc. but keep in mind that if you go that way you go back to imperative programming, mutation and traversal.

to make it short, no such thing as mesh.addEventListener("click", ...) exists, neither in three nor in r3f. all events in r3f are declarative, but, as you can see above, you can group because events bubble. your mesh, if it is underneath the cursor, will fire if you click it.

1 Like

This really helped. Thanks a lot for such help.