@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.