reactjsthree.jsgltfreact-three-fiber

Use the same GLTF model twice in react-three-fiber/drei/Three.js


In this minimal react-three-fiber App I am trying to load and include the same GLTF model twice:

import React, { Suspense } from "react";
import { Canvas } from "@react-three/fiber";
import { useGLTF } from "@react-three/drei";

function MyContent() {
  const firstGltf = useGLTF("/eye/scene.gltf");
  const secondGltf = useGLTF("/eye/scene.gltf");

  return (
    <>
      <primitive object={firstGltf.scene} position={[-200, 0, -400]} />
      <primitive object={secondGltf.scene} position={[200, 0, -400]} />
    </>
  );
}

export default function App() {
  return (
    <Canvas>
      <ambientLight color="white" intensity={0.5} />

      <Suspense fallback={null}>
        <MyContent />
      </Suspense>
    </Canvas>
  );
}

See this codesandbox

However only the second <primitive> is visible. If i remove the second <primitive>, then the first one is visible. I'm struggling to understand why this happens and how to do it better.

(Is it because the second call to useGLTF remembers that "/eye/scene.gltf" has already been loaded and returns the same object? And is this somehow messing up the usage with <primitive>, maybe because materials/geometries haven't been re-created a second time and exist only once?)

In particular, this is what I want to achieve:

On top of that, maybe you can help me to clarify these questions as well so I get a better understanding what's actually going on here:

Thank you!


Solution

  • I am not an expert on three.js, just based on what I find and try to answer your questions.


    1. Only one eye is shown even there is 2 primitives defined

    If you import the same model by using useGLTF(), it will refer to the same object. Therefore, the 2 primitives are pointing to the same gltf and only last/one config is applied.

    const firstGltf = useGLTF("/eye/scene.gltf");
    const secondGltf = useGLTF("/eye/scene.gltf");
    const glassesGltf = useGLTF("/glasses/scene.gltf");
    
    // for demonstrating first eye is same as second eye
    // Output: false, true
    console.log(firstGltf === glassesGltf, firstGltf === secondGltf);
    

    2. Is <primitive> actually the correct approach to show the 3D model?

    Yes, it is. but if you want to display the same gltf to the screen more than once, you need to create meshes and apply the model's geometries and materials so you can have a new object.

    function Model(props) {
      const { nodes, materials } = useGLTF("/eye/scene.gltf");
      return (
        <group
          {...props}
          dispose={null}
          rotation={[Math.PI, 0, -Math.PI / 2]}
          scale={[1, 1, 1]}
        >
          <mesh
            geometry={nodes.Sphere001_Eye_0.geometry}
            material={materials.material}
          />
        </group>
      );
    }
    ...
    <Model position={[-1, 0, 1]} />
    <Model position={[1, 0, 1]} />
    

    Here is the codesandbox for demo


    FYR:

    You can use this library https://github.com/pmndrs/gltfjsx to generate jsx from the model.