Tom Vu
Tom Vu

Reputation: 42

How to load the same gltf model in threejs or react-three-fiber

i'm using react-three-fiber and i want to make a house. So i use a GLTF model for displaying the door.

Render one door is fine but when i render 2 doors from the same GLTF file then only the 2nd door is rendered. Looks like the 2nd door has replaced the 1st instead of being a new door.

How can i achieve having multiple doors, i've searched but there seems to be no one asking this question???

My code:

Door.tsx

import React from 'react';
import { useLoader } from "@react-three/fiber";
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';

interface DoorProps {
  rotation: any;
}

function Door(props: DoorProps) {
  const gltf = useLoader(GLTFLoader, '/simple_door.gltf');

  return (
    <>
      <primitive
        object={gltf.scene}
        position={[25, 1, -17]}
        scale={0.05}
        rotation={props.rotation}
      />
    </>
  );
}

export default Door;

Room.tsx

function Room() {
  return (
    <Canvas
      shadows
      dpr={[1, 2]}
      frameloop="demand"
      style={{ height: 800 }}
      camera={{ fov: 75, near: 0.1, far: 1000, position: [0, 10, 20] }}
    >
      <OrbitControls addEventListener={undefined} hasEventListener={undefined} removeEventListener={undefined} dispatchEvent={undefined} />
      <ambientLight intensity={0.8} />
      <color attach="background" args={['#d0d0d0']} />
      <fog attach="fog" args={['#d0d0d0', 100, 600]} />
      <Suspense fallback={null}>
        <Environment preset="city" />
        <Door />
        <Plane 
          width={50}
          height={10}
          depth={1}
          position={[0, 0, -20]}
        />
        <Door
          rotation={[0, 90*Math.PI/180, 0]}
        />

        <Plane 
          width={50}
          height={1}
          depth={50}
          position={[0, -4.5, 5]}
        />
      </Suspense>
    </Canvas>
  );
};

export default Room;

Upvotes: 3

Views: 6187

Answers (3)

hpalu
hpalu

Reputation: 1435

you can't add the same model in two places, threejs will unmount it automatically from the first place. the solution is gltfjsx. this is what allows you to re-use models. there is no counterpart for this in vanilla three, and they usually re-parse and/or clone. with gltfjsx the model is loaded and parsed only once, but since it is immutable you can readily re-use it ootb.

here's an example: https://codesandbox.io/s/re-using-gltfs-dix1y gltfjsx can even generate instances, so no matter how many times you render it, you'll have only one drawcall.

Upvotes: 1

codeanjero
codeanjero

Reputation: 694

Looks like you're trying to clone your 3D door model,

There are several ways to do so, what you're looking for is probably à simple use of the clone function :

import React from 'react';
import { useGraph } from '@react-three/fiber'
import { useGLTF } from '@react-three/drei'
import { SkeletonUtils } from "three/examples/jsm/utils/SkeletonUtils"

interface DoorProps {
  rotation: any;
}

function Door(props: DoorProps) {
  const { scene, materials, animations } = useGLTF('/simple_door.gltf');
  const clone = useMemo(() => SkeletonUtils.clone(scene), [scene])
  const { nodes } = useGraph(clone)

  return (
    <>
      <primitive
        object={nodes}
        position={[25, 1, -17]}
        scale={0.05}
        rotation={props.rotation}
      />
    </>
  );
}

export default Door;

I used the drei package, because it helps to load other informations than the model if needed

here is another example : https://codesandbox.io/s/react-three-fiber-wildlife-nrbnq?file=/src/Model.js

you could maybe also want to display a lot of doors, in this case i recommend you to use instancedMesh instead of primitive

also a tool that would help you to create your Door component would be gltfjsx, have a look at it : https://www.npmjs.com/package/gltfjsx

Upvotes: 2

Cva
Cva

Reputation: 124

You did not pass rotation prop for the first door,

If you don't want to pass rotation prop to First Door make the rotation an optional prop like this.

interface DoorProps {
   rotation?: any;
}

Upvotes: 0

Related Questions