@threlte/extras
useGltf
A Hook to load glTF files and use separate object nodes and materials of it.
Use the component <GLTF>
if you want to use a model in its entirety.
<script lang="ts">
import Scene from './Scene.svelte'
import { Button, Folder, Pane } from 'svelte-tweakpane-ui'
import { Canvas } from '@threlte/core'
let scene = $state()
let animating = $state(false)
const actions = $derived(scene?.actions)
const action = $derived($actions?.['Take 001'])
// start animating as soon as the action is ready
$effect(() => {
action?.play()
animating = true
})
</script>
<Pane
position="fixed"
title="littlest tokyo"
>
<Folder title="animation">
<Button
disabled={animating}
on:click={() => {
action?.play()
animating = true
}}
title="play"
/>
<Button
disabled={!animating}
on:click={() => {
action?.stop()
animating = false
}}
title="stop"
/>
<Button
disabled={!animating}
on:click={() => {
action?.reset()
}}
title="reset"
/>
</Folder>
</Pane>
<div>
<Canvas>
<Scene bind:this={scene} />
</Canvas>
</div>
<style>
div {
height: 100%;
}
</style>
<script lang="ts">
import { Environment, OrbitControls, useDraco, useGltf, useGltfAnimations } from '@threlte/extras'
import { T } from '@threlte/core'
const dracoLoader = useDraco()
const gltf = useGltf('/models/LittlestTokyo.glb', { dracoLoader })
export const { actions, mixer } = useGltfAnimations<'Take 001'>(gltf)
</script>
<T.PerspectiveCamera
makeDefault
position={[600, 200, -600]}
near={10}
far={10_000}
>
<OrbitControls
autoRotate
autoRotateSpeed={0.2}
enableDamping
enableZoom={false}
target={[-60, -75, 0]}
/>
</T.PerspectiveCamera>
<Environment
url="/textures/equirectangular/hdr/industrial_sunset_puresky_1k.hdr"
isBackground
/>
{#await gltf then { scene }}
<T is={scene} />
{/await}
<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, useTask } from '@threlte/core'
import { Environment, useGltf } from '@threlte/extras'
import type { Material, Object3D } from 'three'
let rotation = 0
useTask((delta) => {
const f = 1 / 60 / delta // ~1 at 60fps
rotation += 0.005 * f
})
const gltf = useGltf<{
nodes: {
'node_damagedHelmet_-6514': Object3D
}
materials: {
Material_MR: Material
}
}>('/models/helmet/DamagedHelmet.gltf')
</script>
<Environment url="/textures/equirectangular/hdr/shanghai_riverside_1k.hdr" />
<T.PerspectiveCamera
makeDefault
position.z={10}
fov={20}
/>
<T.DirectionalLight
position.y={10}
position.z={10}
/>
<T.Group rotation.y={rotation}>
{#await gltf}
<!-- Place loading placeholder here -->
{:then value}
<T is={value.nodes['node_damagedHelmet_-6514']} />
{/await}
</T.Group>
Model: Battle Damaged Sci-fi Helmet by theblueturtle_
Examples
Basic Example
gltf
is a store which gets populated as soon as the model loaded.
<script lang="ts">
import { T } from '@threlte/core'
import { useGltf } from '@threlte/extras'
import { MeshBasicMaterial } from 'three'
const gltf = useGltf('/path/to/model.glb')
</script>
<!-- Use an object node entirely -->
{#if $gltf}
<T is={$gltf.nodes['node-name']} />
{/if}
<!-- or only the geometry -->
{#if $gltf}
<T.Mesh
geometry={$gltf.nodes['node-name'].geometry}
material={new MeshBasicMaterial()}
/>
{/if}
DRACO decoding
Use the useDraco
hook for compressed glTF files, defaults to CDN loaded DRACO binaries.
import { useGltf, useDraco } from '@threlte/extras'
const dracoLoader = useDraco()
const gltf = useGltf('/path/to/model.glb', {
dracoLoader
})
You can set a custom path to DRACO decoder binaries.
import { useGltf, useDraco } from '@threlte/extras'
const dracoLoader = useDraco('/custom/draco/decoders/path')
const gltf = useGltf('/path/to/model.glb', {
dracoLoader
})
You can also provide your own instance of DRACOLoader
.
This is especially useful when you can confidently dispose of the loader, as the default loader is indefinitely cached.
import { useGltf } from '@threlte/extras'
const dracoLoader = new DRACOLoader().setDecoderPath(path)
const gltf = useGltf('/path/to/model.glb', {
dracoLoader
})
Meshopt decoding
Use the useMeshopt
hook for compressed glTF files, defaults to Three’s included decoder.
You can also provide your own instance of MeshoptDecoder.
import { useGltf, useMeshopt } from '@threlte/extras'
const meshoptDecoder = useMeshopt()
const gltf = useGltf('/path/to/model.glb', {
meshoptDecoder
})
KTX2Loader
Use the useKtx2
hook for KTX 2 texture support.
This hook requires a transcoder
path.
import { useGltf, useKtx2 } from '@threlte/extras'
const ktx2Loader = useKtx2('path/to/transcoder/')
const gltf = useGltf('/path/to/model.glb', {
ktx2Loader
})
You can also provide your own instance of KTX2Loader
.
This is especially useful when you can confidently dispose of the loader, as the default loader is indefinitely cached.
import { useThrelte } from '@threlte/core'
import { useGltf } from '@threlte/extras'
const { renderer } = useThrelte()
const ktx2Loader = new KTX2Loader()
ktx2Loader.setTranscoderPath('path/to/transcoder/')
ktx2Loader.detectSupport(renderer)
const gltf = useGltf('/path/to/model.glb', {
ktx2Loader
})
Nodes and Materials
The hook provides a map of all objects and materials in the loaded glTF.
<script lang="ts">
import { useGltf } from '@threlte/extras'
const gltf = useGltf('/path/to/model.glb')
let nodes = $derived($gltf?.nodes)
let materials = $derived($gltf?.materials)
</script>
Provide types and you will gain autocompletion for these objects and materials.
<script lang="ts">
import { useGltf } from '@threlte/extras'
const gltf = useGltf<{
nodes: {
MeshA: THREE.Mesh
MeshB: THREE.Mesh
Object3DA: THREE.Object3D
}
materials: {
MaterialA: THREE.MeshStandardMaterial
MaterialB: THREE.MeshBasicMaterial
}
}>('/path/to/model.glb')
$effect(() => {
if ($gltf) {
const objectA = $gltf.nodes['MeshA'] // -> THREE.Mesh
const materialA = $gltf.materials['MaterialA'] // -> THREE.MeshStandardMaterial
}
})
</script>
How to get the types?
On the loading-assets page, Threlte provides the
@threlte/gltf
CLI tool that can be used to generate a reusable Svelte component for your gltf as
well as its types.
Types can be separated into a typescript file and imported like so if you feel the need.
export type SomeGltf = {
nodes: {
Suzanne: THREE.Mesh
}
materials: {}
}
<script lang="ts">
import { useGltf } from '@threlte/extras'
import type { SomeGltf } from './SomeGltf.ts'
useGltf<SomeGltf>('model.glb')
</script>