it’s a little bit confused. object.children could be deeply nested.
you can do something like this
const hovered = new THREE.MeshStandardMaterial({ color: "hotpink" })
const unhovered = new THREE.MeshStandardMaterial({ color: "orange" })
const Model = (props) => {
const object = useLoader(Rhino3dmLoader, "./rhino_logo.3dm", (loader) => {
loader.setLibraryPath("https://cdn.jsdelivr.net/npm/rhino3dm@0.15.0-beta/")
})
useLayoutEffect(() => {
object.traverse(o => o.material = unhovered)
}, [])
return (
<primitive
onPointerOver={(e) => {
e.stopPropagation()
e.object.material = hovered
}}
onPointerOut={(e) => {
e.stopPropagation()
e.object.material = unhovered
}}
object={object}
{...props}
/>
)
}
<Canvas>
<Model scale={0.15} rotation={[-Math.PI / 2, 0, 0]} />
but keep in mind that if that model were declarative everything would be so easy. this is the difference between imperative and declarative in a nutshell, in vanilla threejs you get served a blob, a black box, and it’s hard to control that with traverse and it gets way too complex. all this evaporates with the declarative approach: save it as gltf and type npx gltfjsx yourmodel.gltf --transform
now the full graph would be under your control.