How to loop through an array of .glb models, using React map, in React Three Rapier?

I have a glb model in my project, and want to make it appear at least four times. Here’s the code:

const models = [model, model, model, model]

And, to loop through the array, to display models using React Three Rapier, I wrote this piece of code:

models.map((model, index) => (
  <RigidBody
    key={ index }
    type="dynamic"
    colliders="trimesh"
    scale={ 10 }
    position={ [ 0, index * 8.1, 0 ] } >
    <primitive
      castShadow
      object={ model.scene } />
  </RigidBody>
))

And here’s ther result:

But as you can see, there’s only one model appearing… what should I do?

  1. For the sake of quick debugging - what happens if you use just a plain <mesh> instead of models in your rapier component? Does everything behave correctly?

  2. How are you loading the model?

If you’re using useGLTF to load the model and then pass it down the props - you’re only really using a single model at all times. useGLTF has a bit confusing behaviour of loading and caching just a single instance of the model - so if you use or modify the model in more than one component - you’ll be modifying it everywhere (it has it’s pros and cons - but as above, can be a bit confusing.

If, regardless of how you load the model, you pass it via props to the rapier component - you’d still need to ensure that component is using a unique instance of the model, ex.:

const uniqueModel = useMemo(() => model.clone(), [model]); // NOTE Be sure to dispose cloned models
1 Like

Thanks for your quick response!

For the first question; I injected this code, instead of what I’ve posted:

models.map((model, index) => (
    <primitive
      position={ [ 0, index * 8.1, 0 ] }
      castShadow
      scale={ 10 }
      object={ model.scene } />
))

But, the problem still exists…

And regarding the second question you asked; I used useGLTF for sure!

However… Could you please exaplin the piece of code your wrote in the reply?

Thanks, Sina!

I meant without the models, not without the physics tho:

models.map((model, index) => (
  <RigidBody
    key={ index }
    type="dynamic"
    colliders="trimesh"
    scale={ 10 }
    position={ [ 0, index * 8.1, 0 ] }
  >
    <mesh>
      <sphereGeometry args={[ 0.1, 32, 32 ]} />
      <meshNormalMaterial />
    </mesh>  
  </RigidBody>
))
1 Like

Sorry for misunderstanding!

Sure, your code works!

Hey @mjurczyk!

It works now, you were right! All I had to do was to clone the scene, not the 3D model:

const modelOne = useMemo(() => model.scene.clone(), [model])
const modelTwo = useMemo(() => model.scene.clone(), [model])
const models = [modelOne, modelTwo]

Problem solved!

you can also use gltf from drei, it auto clones

import { Gltf } from '@react-three/drei'

<Gltf src="url.glb" />

also check out GitHub - pmndrs/gltfjsx: 🎮 Turns GLTFs into JSX components with this you don’t need to clone any longer. this is the cleanest solution to the gltf problem.

2 Likes

ps, in threejs you can only have one model in one place.

const mesh = new THREE.Mesh(geo, mat)
sceneA.add(mesh)
sceneB.add(mesh)

three will first mount mesh into sceneA, then remove it, then mount it into sceneB. this is what happened to your primitive, which is just a parent.add(object).

1 Like