<div align="right">
 <a href="https://www.npmjs.com/package/@threlte/gltf">
  <img alt="npm" src="https://img.shields.io/npm/v/@threlte/gltf?color=fe4100&labelColor=171d27&logo=npm&logoColor=white"/>
 </a>
 <a href="https://github.com/threlte/threlte/blob/main/LICENSE.md">
  <img alt="license" src="https://img.shields.io/npm/l/@threlte/core?color=fe4100&labelColor=171d27&logo=git&logoColor=white"/>
 </a>
 <a href="https://discord.com/channels/985983540804091964">
  <img alt="discord" src="https://img.shields.io/discord/985983540804091964?label=discord&color=fe4100&labelColor=171d27&logo=discord&logoColor=white"/>
 </a>
 <a href="https://threlte.xyz">
  <img alt="docs" src="https://img.shields.io/website?down_color=red&down_message=offline&label=docs&color=fe4100&labelColor=171d27&up_message=online&url=https%3A%2F%2Fthrelte.xyz&logo=svelte&logoColor=white"/>
 </a>
</div>

<a href="https://threlte.xyz">
 <img src="https://threlte.xyz/logo/threlte-banner.jpg"/>
</a>

## Rapidly Build Interactive 3D Apps for the Web

Threlte is a [Svelte](https://svelte.dev/) library that simplifies creating 3D apps for the web. It provides a **declarative**, **type-safe**, **reactive** and **interactive** API out-of-the-box.

Threlte's **3D rendering** is powered by [Three.js](https://threejs.org/), and it also provides a **physics engine** through [Rapier](https://rapier.rs/) and an **animation studio** via [Theatre.js](https://www.theatrejs.com/); see [packages](#packages) for details.

Check out our **[documentation](https://threlte.xyz)** and our **[Discord community](https://discord.gg/EqUBCfCaGm)**.

## @threlte/gltf

[@threlte/gltf](https://threlte.xyz/docs/reference/gltf/getting-started) gives you a CLI to that turn GLTF assets into declarative and reusable Threlte components.

This helps with performance optimization for asset-heavy Threlte apps. It also allows you to modify your GLTF assets as Svelte components, instead of working with 3D software like Blender.

### Motivation

#### The GLTF workflow on the web is not ideal ...

- GLTF is thrown wholesale into the scene which prevents re-use, in threejs objects can only be mounted once
- Contents can only be found by traversal which is cumbersome and slow
- Changes to queried nodes are made by mutation, which alters the source data and prevents re-use
- Re-structuring content, making nodes conditional or adding/removing is cumbersome
- Model compression is complex and not easily achieved
- Models often have unnecessary nodes that cause extra work and matrix updates

#### @threlte/gltf fixes that

- 🧑‍💻 It creates a virtual graph of all objects and materials. Now you can easily alter contents and re-use.
- 🏎️ The graph gets pruned (empty groups, unnecessary transforms, ...) and will perform better.
- ⚡️ It will optionally compress your model with up to 70%-90% size reduction.

## Usage

```bash
Usage
  $ npx @threlte/gltf@latest [Model.glb] [options]

Options
  --output, -o        Output file name/path
  --types, -t         Add Typescript definitions
  --keepnames, -k     Keep original names
  --keepgroups, -K    Keep (empty) groups, disable pruning
  --meta, -m          Include metadata (as userData)
  --shadows, -s       Let meshes cast and receive shadows
  --printwidth, -w    Prettier printWidth (default: 120)
  --precision, -p     Number of fractional digits (default: 2)
  --draco, -d         Draco binary path
  --preload -P        Add preload method to module script
  --suspense -u       Make the component suspense-ready
  --isolated, -i      Output as isolated module (No $$restProps usage)
  --root, -r          Sets directory from which .gltf file is served
  --transform, -T     Transform the asset for the web (draco, prune, resize)
    --resolution, -R  Transform resolution for texture resizing (default: 1024)
    --simplify, -S    Transform simplification (default: false) (experimental!)
      --weld          Weld tolerance (default: 0.0001)
      --ratio         Simplifier ratio (default: 0.75)
      --error         Simplifier error threshold (default: 0.001)
  --debug, -D         Debug output
```

### Requirements

- NodeJS must be installed
- The GLTF file has to be present in your projects `/public` folder
- [three](https://github.com/mrdoob/three.js/) (>= 122.x)
- @threlte/core > 5.0.0
- @threlte/core > 4.9.3

### Support

Have questions? Feel free to ask in our [Discord support forum](https://discord.com/channels/985983540804091964/1031843197963477002).

### A typical use-case

First you run your model through `@threlte/gltf`. `npx` allows you to use npm packages without installing them.

```bash
npx @threlte/gltf@latest model.gltf --transform
```

This will create a `Model.svelte` file that plots out all of the assets contents.

```svelte
<!--
Auto-generated by: https://github.com/pmndrs/gltfjsx
Command: npx gltfjsx@0.0.1 ./stacy.glb
-->
<script>
  import { Group } from 'three'
  import { T } from '@threlte/core'
  import { useGltf, useGltfAnimations } from '@threlte/extras'

  export const ref = new Group()

  const gltf = useGltf('/stacy.glb')
  export const { actions, mixer } = useGltfAnimations(gltf, ref)
</script>

{#if $gltf}
  <T
    is={ref}
    {...$$restProps}
  >
    <T.Group name="Scene">
      <T.Group
        name="Stacy"
        rotation={[Math.PI / 2, 0, 0]}
        scale={0.01}
      >
        <T is={$gltf.nodes.mixamorigHips} />
        <T.SkinnedMesh
          name="stacy"
          geometry={$gltf.nodes.stacy.geometry}
          material={$gltf.nodes.stacy.material}
          skeleton={$gltf.nodes.stacy.skeleton}
          rotation={[-Math.PI / 2, 0, 0]}
          scale={100}
        />
      </T.Group>
    </T.Group>

    <slot {ref} />
  </T>
{/if}
```

Add your model to your `/public` folder as you would normally do. With the `--transform` flag it has created a compressed copy of it (in the above case `model-transformed.glb`). Without the flag just copy the original model.

```text
/public
  model-transformed.glb
```

The component can now be dropped into your scene.

```svelte
<script>
  import { Canvas } from '@threlte/core'
  import Model from './Model.svelte'
</script>

<Canvas>
  <Model />
</Canvas>
```

You can re-use it, it will re-use geometries and materials out of the box:

```svelte
<Model position={[0, 0, 0]} />
<Model position={[10, 0, -10]} />
```

Or make the model dynamic. Change its colors for example:

```svelte
<T.Mesh
  geometry={$gltf.nodes.robot.geometry}
  material={$gltf.materials.metal}
  material.color="green"
/>
```

Or exchange materials:

```svelte
<T.Mesh geometry={$gltf.nodes.robot.geometry}>
  <T.MeshPhysicalMaterial color="hotpink" />
</T.Mesh>
```

Make contents conditional:

```svelte
{#if condition}
  <T.Mesh
    geometry={$gltf.nodes.robot.geometry}
    material={$gltf.materials.metal}
  />}
{/if}
```

## Features

#### ⚡️ Draco and meshopt compression ootb

You don't need to do anything if your models are draco compressed, since `useGltf` defaults to a [draco CDN](https://www.gstatic.com/draco/v1/decoders/). By adding the `--draco` flag you can refer to [local binaries](https://github.com/mrdoob/three.js/tree/dev/examples/js/libs/draco/gltf) which must reside in your /public folder.

#### ⚡️ Auto-transform (compression, resize)

With the `--transform` flag it creates a binary-packed, draco-compressed, texture-resized (1024x1024), webp compressed, deduped, instanced and pruned \*.glb ready to be consumed on a web site. It uses [glTF-Transform](https://github.com/donmccurdy/glTF-Transform). This can reduce the size of an asset by 70%-90%.

It will not alter the original but create a copy and append `[modelname]-transformed.glb`.

#### ⚡️ Type-safety

Add the `--types` flag and your component will be typesafe.

```svelte
<!--
Auto-generated by: https://github.com/pmndrs/gltfjsx
Command: npx gltfjsx@0.0.1 ./stacy.glb -t
-->
<script lang="ts">
  import type * as THREE from 'three'
  import { Group } from 'three'
  import { T, type Props, type Events, type Slots } from '@threlte/core'
  import { useGltf, useGltfAnimations } from '@threlte/extras'

  type $$Props = Props<THREE.Group>
  type $$Events = Events<THREE.Group>
  type $$Slots = Slots<THREE.Group>

  export const ref = new Group()

  type ActionName =
    | 'pockets'
    | 'rope'
    | 'swingdance'
    | 'jump'
    | 'react'
    | 'shrug'
    | 'wave'
    | 'golf'
    | 'idle'
  type GLTFResult = {
    nodes: {
      stacy: THREE.SkinnedMesh
      mixamorigHips: THREE.Bone
    }
    materials: {}
  }

  const gltf = useGltf<GLTFResult>('/stacy.glb')
  export const { actions, mixer } = useGltfAnimations<ActionName>(gltf, ref)
</script>

{#if $gltf}
  <T
    is={ref}
    {...$$restProps}
  >
    <T.Group name="Scene">
      <T.Group
        name="Stacy"
        rotation={[Math.PI / 2, 0, 0]}
        scale={0.01}
      >
        <T is={$gltf.nodes.mixamorigHips} />
        <T.SkinnedMesh
          name="stacy"
          geometry={$gltf.nodes.stacy.geometry}
          material={$gltf.nodes.stacy.material}
          skeleton={$gltf.nodes.stacy.skeleton}
          rotation={[-Math.PI / 2, 0, 0]}
          scale={100}
        />
      </T.Group>
    </T.Group>

    <slot {ref} />
  </T>
{/if}
```

#### ⚡️ Easier access to animations

If your GLTF contains animations it will add [@threlte/extras's `useGltfAnimations`](https://threlte.xyz/extras/use-gltf-animations) hook, which extracts all clips and prepares them as actions:

```ts
const gltf = useGltf('/stacy.glb')

export const {(actions, mixer)} = useGltfAnimations(gltf, ref)
```

If you want to play an animation you can do so at any time:

```ts
const onEvent = () => {
  $actions.jump.play()
}
```

## Using the parser stand-alone

```jsx
import { parse } from '@threlte/gltf'
import { GLTFLoader, DRACOLoader } from 'three-stdlib'

const gltfLoader = new GLTFLoader()
const dracoloader = new DRACOLoader()
dracoloader.setDecoderPath('https://www.gstatic.com/draco/v1/decoders/')
gltfLoader.setDRACOLoader(dracoloader)

gltfLoader.load(url, (gltf) => {
  const component = parse(filename, gltf, config)
})
```

## Contributing

Contributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are **greatly appreciated**.

- **Filing Issues** - if you have feature requestions or you think you spotted a bug, [submit an issue](https://github.com/threlte/threlte/issues/new).
- **Contributing Code** - if you would like to drop us a PR, read the [contribution guide](https://github.com/threlte/threlte/blob/main/CONTRIBUTING.md) first.

## Sponsors

[![Powered by Vercel](./assets/vercel/powered-by-vercel.svg)](https://vercel.com/?utm_source=threlte&utm_campaign=oss)

---

### License

The MIT License (MIT). Please see the [License File](LICENSE.md) for more information.
