Note
Work in progress. Should already work though. If you find any bugs please open an issue.
gltfjsx-preview.mp4
A small command-line tool that turns GLTF assets into declarative and re-usable Trejs Vue components.
- GLTF is thrown whole 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
- 🧑💻 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
$ node cli.js [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: 3)
--draco, -d Draco binary path
--root, -r Sets directory from which .gltf file is served
--instance, -i Instance re-occuring geometry
--instanceall, -I Instance every geometry (for cheaper re-use)
--transform, -T Transform the asset for the web (draco, prune, resize)
--resolution, -R Resolution for texture resizing (default: 1024)
--keepmeshes, -j Do not join compatible meshes
--keepmaterials, -M Do not palette join materials
--format, -f Texture format (default: "webp")
--simplify, -S Mesh simplification (default: false)
--weld Weld tolerance (default: 0.00005)
--ratio Simplifier ratio (default: 0)
--error Simplifier error threshold (default: 0.0001)
--debug, -D Debug output
First you run your model through gltfvue.
node cli.js model.gltf --transform
This will create a Model.vue
file that plots out all of the assets contents.
/*
auto-generated by: https://github.com/OmnomnomTee/gltfvue
author: abcdef (https://sketchfab.com/abcdef)
license: CC-BY-4.0 (http://creativecommons.org/licenses/by/4.0/)
source: https://sketchfab.com/models/...
title: Model
*/
<script setup>
import { useGLTF } from '@tresjs/cientos'
const { nodes, materials } = await useGLTF('/model-transformed.glb', { draco: true })
</script>
<template>
<TresGroup>
<TresGroup>
<TresMesh :geometry="nodes.model.geometry" :material="materials['default']" />
</TresGroup>
</TresGroup>
</template>
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.
/public
model-transformed.glb
The component can now be dropped into your scene.
import { TresCanvas } from '@tresjs/core'
import Model from '@/components/Model.vue'
<template>
<TresCanvas>
<Model />
You can re-use it, it will re-use geometries and materials out of the box:
<Model :position="[0, 0, 0]" />
<Model :position="[10, 0, -10]" />
Or make the model dynamic. Change its colors for example:
<TresMesh :geometry="nodes.robot.geometry" :material="materials.metal" material-color="green" />
Add events:
<TresMesh :geometry="nodes.robot.geometry" :material="materials.metal" @click="handleClick" />
You don't need to do anything if your models are draco compressed, since useGLTF
defaults to a draco CDN. By adding the --draco
flag you can refer to local binaries which must reside in your /public folder.
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. 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
.
Add the --types
flag and your GLTF will be typesafe.
type GLTFResult = GLTF & {
nodes: { robot: THREE.Mesh; rocket: THREE.Mesh }
materials: { metal: THREE.MeshStandardMaterial; wood: THREE.MeshStandardMaterial }
}
export default function Model(props: JSX.IntrinsicElements['group']) {
const { nodes, materials } = useGLTF<GLTFResult>('/model.gltf')
If your GLTF contains animations it will add Clientos useAnimations
hook, which extracts all clips and prepares them as actions:
const { scene:model, nodes, materials, animations } = await useGLTF('/model.gltf')
const { actions } = useAnimations(animations, model)
If you want to play an animation you can do so at any time:
<TresMesh @click="(e) => {actions.jump.play()}" />
import { parse } from 'gltfvue'
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 jsx = parse(gltf, optionalConfig)
})
const jsx = parse(scene, optionalConfig)
The GLTFStructureLoader can come in handy while testing gltf assets. It allows you to extract the structure without the actual binaries and textures making it possible to run in a testing environment.
import { GLTFStructureLoader } from 'gltfjsx'
import fs from 'fs/promises'
it('should have a scene with a blue mesh', async () => {
const loader = new GLTFStructureLoader()
const data = await fs.readFile('./model.glb')
const { scene } = await new Promise((res) => loader.parse(data, '', res))
expect(() => scene.children.length).toEqual(1)
expect(() => scene.children[0].type).toEqual('mesh')
expect(() => scene.children[0].material.color).toEqual('blue')
})
- Nodejs must be installed
- The GLTF file has to be present in your projects
/public
folder - three (>= 122.x)
- @react-three/fiber (>= 5.x)
- @react-three/drei (>= 2.x)