threlte logo
@threlte/extras

<InstancedMeshes>

The component <InstancedMeshes> takes existing THREE.Mesh instances and creates a THREE.InstancedMesh per THREE.Mesh. This is especially useful if you want to instantiate a lot of meshes that have been loaded with hooks like useGltf.

It takes the same arguments as <InstancedMesh>.

<script lang="ts">
  import { Canvas } from '@threlte/core'
  import Scene from './Scene.svelte'
</script>

<div>
  <Canvas>
    <Scene />
  </Canvas>
</div>

<style>
  div {
    height: 100%;
  }
</style>
<script lang="ts">
  import { T } from '@threlte/core'
</script>

<T.Group {...$$restProps}>
  <!-- Correct rotation -->
  <T.Group rotation.x={(90 * Math.PI) / 180}>
    <slot />
  </T.Group>
</T.Group>
<script lang="ts">
  import { T } from '@threlte/core'
  import { InstancedMeshes, OrbitControls, useGltf } from '@threlte/extras'
  import { DoubleSide, Mesh } from 'three'
  import { DEG2RAD } from 'three/src/math/MathUtils.js'
  import Flower from './Flower.svelte'

  const gltf = useGltf<{
    nodes: {
      Blossom: Mesh
      Stem: Mesh
    }
    materials: {}
  }>('/models/Flower.glb')

  // make an array of random x-z coordinates in the range of -20 to 20 with 200 elements
  const items = Array.from({ length: 1000 }, () => ({
    x: Math.random() * 5 - 2.5,
    z: Math.random() * 5 - 2.5,
    scale: Math.random() * 0.5 + 0.5,
    rotation: {
      x: Math.random() * 8,
      y: Math.random() * 360,
      z: Math.random() * 8
    }
  }))
</script>

{#if $gltf}
  <InstancedMeshes
    castShadow
    meshes={$gltf.nodes}
    let:components={{ Blossom, Stem }}
  >
    {#each items as item}
      <Flower
        position.x={item.x}
        position.z={item.z}
        scale={item.scale}
        rotation.y={(item.rotation.y * Math.PI) / 180}
        rotation.x={(item.rotation.x * Math.PI) / 180}
        rotation.z={(item.rotation.z * Math.PI) / 180}
      >
        <Blossom />
        <Stem />
      </Flower>
    {/each}
  </InstancedMeshes>
{/if}

<T.DirectionalLight
  position.y={10}
  position.z={5}
  castShadow
  shadow.camera.left={-2.5}
  shadow.camera.right={2.5}
  shadow.camera.top={2.5}
  shadow.camera.bottom={-2.5}
  shadow.mapSize.width={1024}
  shadow.mapSize.height={1024}
/>

<T.Mesh
  receiveShadow
  rotation.x={-90 * DEG2RAD}
>
  <T.PlaneGeometry args={[5, 5]} />
  <T.MeshStandardMaterial
    color="#288278"
    side={DoubleSide}
  />
</T.Mesh>

<T.AmbientLight intensity={0.1} />

<T.PerspectiveCamera
  position={[3, 0.5, 3]}
  makeDefault
  fov={20}
>
  <OrbitControls
    autoRotate
    enableZoom={false}
    enableDamping
    autoRotateSpeed={0.1}
    enablePan={false}
  />
</T.PerspectiveCamera>

Usage

Passing a map

Load a gltf file with the useGltf hook and pass the result to the <InstancedMeshes> component. The slot prop components can be used to instantiate a mesh multiple times.

<script lang="ts">
  import { useGltf, InstancedMeshes } from '@threlte/extras'

  // Let's say the file contains a mesh named "Cube".
  // The hook `useGltf`  will automatically provide a map with
  // all nodes of the gltf file at the key `nodes`. When
  // passing that map to the `<InstancedMeshes>` component, it will
  // automatically filter out all nodes that are not
  // `THREE.Mesh` instances.
  const gltf = useGltf('path/to/file.gltf')
</script>

{#if gltf}
  <!--
    You can use object destructuring
    to access the component <Cube>
  -->
  <InstancedMeshes meshes={$gltf.nodes} let:components={{ Cube }}>
    <Cube position.y={2} position.x={-1}>
  </InstancedMeshes>
{/if}

When using <InstancedMeshes> with a large gltf file, be aware that <InstancedMeshes> will create a new <InstancedMesh> for each <Mesh> in the gltf file. This can lead to a lot of <InstancedMesh> components, which can have a negative impact on performance. You might want to filter out the meshes you want to instantiate beforehand.

Passing an array

If you don’t want to use the useGltf hook, you can also pass an array of THREE.Mesh instances to the <InstancedMeshes> component.

<script lang="ts">
  import { InstancedMeshes } from '@threlte/extras'
  import { Mesh, BoxGeometry, MeshStandardMaterial } from 'three'

  const meshes = [
    new Mesh(new BoxGeometry(), new MeshStandardMaterial()), // MeshA
    new Mesh(new SphereGeometry(), new MeshStandardMaterial()), // MeshB
    new Mesh(new PlaneGeometry(), new MeshStandardMaterial())  // MeshC
  ]
</script>

<!--
    You can use array destructuring
    to access the components <MeshA>,
    <MeshB> and <MeshC>
  -->
<InstancedMeshes meshes={meshes} let:components={[MeshA, MeshB, MeshC]}>
  <MeshA position.y={2} position.x={-1}>
  <MeshB position.y={-2}>
  <MeshC position.y={0} position.x={1}>
</InstancedMeshes>

Component Signature

<InstancedMeshes> extends <T.InstancedMesh> and supports all its props, slot props, bindings and events.

Props

name
type
required
default
description

meshes
THREE.Mesh[] | Record<string, THREE.Mesh>
yes

limit
number
no
1000
Limits the amount of possible <Instance> components.

range
number
no
1000
Limits the amount of drawn <Instance> components.

update
boolean
no
true
Whether the THREE.InstancedMesh should be updated.