brianzinn / react-babylonjs

React for Babylon 3D engine
https://brianzinn.github.io/react-babylonjs/
812 stars 102 forks source link

How to render multiple meshes from the same mesh asset? #210

Closed LucasEThomas closed 2 years ago

LucasEThomas commented 2 years ago

I am trying to load in a mesh, and then use that mesh multiple times.

export const FourChairs = () => {

  const meshAssetResult = useAssetManager([{
    taskType: TaskType.Mesh,
    name: 'chair',
    rootUrl: 'http://localhost:3000/assets/',
    sceneFilename: 'chair.stl',
  }])

  const taskData = meshAssetResult.taskNameMap['chair'] as unknown as { loadedMeshes: Mesh[] }
  const meshData = taskData.loadedMeshes[0]

  return meshData && <>

    <mesh fromInstance={meshData} position={new Vector3(0,0,1)} name='chairMesh0'/>
    <mesh fromInstance={meshData} position={new Vector3(1,0,1)} name='chairMesh1'/>
    <mesh fromInstance={meshData} position={new Vector3(1,0,0)} name='chairMesh2'/>
    <mesh fromInstance={meshData} position={new Vector3(0,1,0)} name='chairMesh3'/>
  </> || null

}

However, the above code only renders one chair mesh (the last one).

How can I accomplish this?

LucasEThomas commented 2 years ago

Oh wait, hold up! I just figured out a solution by using .clone() like this

export const FourChairs = () => {

  const meshAssetResult = useAssetManager([{
    taskType: TaskType.Mesh,
    name: 'chair',
    rootUrl: 'http://localhost:3000/assets/',
    sceneFilename: 'chair.stl',
  }])

  const taskData = meshAssetResult.taskNameMap['chair'] as unknown as { loadedMeshes: Mesh[] }
  const meshData = taskData.loadedMeshes[0]

  return meshData && <>

    <mesh fromInstance={meshData.clone()} position={new Vector3(0,0,1)} name='chairMesh0'/>
    <mesh fromInstance={meshData.clone()} position={new Vector3(1,0,1)} name='chairMesh1'/>
    <mesh fromInstance={meshData.clone()} position={new Vector3(1,0,0)} name='chairMesh2'/>
    <mesh fromInstance={meshData.clone()} position={new Vector3(0,1,0)} name='chairMesh3'/>
  </> || null
}

It renders four chairs as expected (yay!). But, is it good practice? The documentation is a little lacking here. Are there any pitfalls I should be aware of doing this?

brianzinn commented 2 years ago

That's going to clone() the mesh. You may end up with better performance with using instanced meshes as well. That will look like this:

<instancedMesh
    source={meshData}
    name={`hex-${i}`}
    position={)
/>

I have a story up here: https://github.com/brianzinn/react-babylonjs/blob/master/packages/storybook/stories/babylonjs/Basic/instances.stories.js#L80

You can see the live demo here: https://brianzinn.github.io/react-babylonjs/?path=/story/babylon-basic--instances

brianzinn commented 2 years ago

Guess that means you got it working! You can cast to MeshAssetTask if you know it's meshes and then you don't need to cast as unknown. I could always add a generic on the hook for convenience to help out there if that sounds useful. ie:

const meshAssetResult = useAssetManager<MeshAssetTask>([{...}]);
const meshData = meshAssetResult.taskMap['chair'].loadedMeshes[0];