three-vat is a Three.js runtime library for importing and playing Houdini-style Vertex Animation Textures in the browser, with an included Blender exporter and demo harness.
The goal is to provide a robust, practical VAT pipeline for Three.js:
- loading sidecar metadata plus mesh/texture assets from disk
- patching standard Three.js materials with VAT deformation via
onBeforeCompile - supporting instancing with per-instance time offsets and speed multipliers
- covering multiple VAT variants: soft body, dynamic mesh/fluid, rigid body, particles, and an OpenVAT sample
- authoring new compatible VAT datasets from Blender with the bundled exporter
Demo: https://manthrax.github.io/three-vat/
The core idea is to make VAT playback feel like a normal part of a Three.js pipeline, not a sidecar experiment that requires replacing everything with custom shaders.
In practice, that means:
- preserving standard Three.js material workflows as much as possible
- supporting multiple VAT flavors behind one runtime model
- aiming for compatibility with Houdini-style VAT3 asset conventions
- leaving room for Blender-authored exports that target the same runtime contract
This repo includes the runtime, a Blender exporter, sample assets, and a browser demo/testbed because the goal is an end-to-end workflow, not just an isolated viewer.
- index.html is the standalone demo app and scene setup
- VATLoader.js loads metadata, mesh assets, and VAT textures
- VATEffect.js injects shader logic into Three.js materials
- vat_export.py is a Blender addon/exporter for generating compatible VAT assets
- blender_vat.blend is the sample authoring scene that includes the exporter script
- public/examples contains sample datasets for each supported variant
- the demo doubles as a visual regression harness for the included example VATs
npm install
npm run devBuild for the GitHub Pages-style deployment target:
npm run buildPreview the production build locally:
npm run previewThe demo uses the loader/effect pair like this:
import { VATLoader } from './VATLoader.js';
import { VATEffect } from './VATEffect.js';
const assets = await VATLoader.load('examples/', 'vat3_softBody');
const vat = new VATEffect(assets, { camera });
scene.add(vat.mesh);
vat.speed = 1.0;
vat.time = 0.0;
vat.setEnabled(true);For instanced playback, the runtime supports per-instance offsets and rate variation:
vat.setInstanceTimeOffset(index, offsetInSeconds);
vat.setInstanceSpeedScale(index, speedMultiplier);The loader expects a folder per asset under public/examples/<assetName>/.
Expected files typically look like:
<assetName>_data.json<assetName>_mesh.glbor legacy*_mesh.fbxfor the older demo assets<assetName>_pos.exror.png<assetName>_rot.exror.pngwhen the variant needs rotation/normal data- optional
_col,_lookup,_pos2,_col2
The repo also contains an OpenVAT-style sample (Cube_vat) with a slightly different naming/layout convention:
- metadata is read from
<baseName>-remap_info.json - the mesh is read from
<baseName>.glb - the primary position texture is read from
<assetName>.exr
- legacy samples may use a lookup texture indirection path
- current Blender-packed exports use a generated triangle-soup carrier mesh plus packed position/rotation atlases
- positions come from
_pos - rotations/normals come from
_rot - dynamic topology may use
_lookupor the newer packed-atlas path depending on metadata
See docs/dynamic-mesh-export.md for the current packed dynamic-mesh export/runtime contract.
- standard indexed or unindexed deforming mesh
- texture width corresponds to vertex count
- texture height corresponds to frame count
- positions come from
_pos - orientation/normal data comes from
_rot
- texture width corresponds to maximum particle count
- positions come from
_pos - this sample also uses
_colas the particle/albedo texture - the runtime can billboard particles and vary playback per instance
- chunk transforms are sampled per frame
- pivots are reconstructed from mesh UV channels when present
- positions come from
_pos - rotations come from
_rot
- supports changing topology over time
- the render mesh is a generated triangle-soup carrier rather than a stable authored bind mesh
- positions come from
_pos - rotations / normals come from
_rot - some assets use a lookup indirection texture, while current Blender exports can also use the packed-atlas path described in docs/dynamic-mesh-export.md
vat_export.py registers a Blender sidebar panel named VAT Exporter and can export:
- dynamic / fluid mesh
- soft body
- rigid body / chunk animation
- particles
The exporter writes Houdini-style sidecar JSON plus EXR/PNG textures and supports:
- auto-inferring asset name and VAT mode from the current selection
- exporting from a selected effect root for fluid, soft body, rigid body, and particles
- optional PNG or EXR texture output
- adjustable texture width for atlas packing
- scene-FPS sampling by default, with optional custom export FPS resampling
- a panel summary showing estimated output sizes and the last generated files
The sample Blender scene blender_vat.blend is included as the starting point for authoring new VATs with the bundled exporter.
For the current dynamic mesh / fluid export details, see docs/dynamic-mesh-export.md.
The runtime is already useful as a practical reference implementation and the exporter is far enough along to generate working soft body, dynamic mesh/fluid, particle, and rigid body samples from Blender. The project is still evolving, so expect the API, exporter details, and asset conventions to keep getting refined as the public surface gets cleaned up.
- a Three.js VAT runtime that supports multiple Houdini-style VAT3 variants
- a Blender exporter for generating compatible VAT datasets
- a sample scene plus exporter script for authoring and iterating on new effects
- a browser demo that also acts as a regression suite for the included examples
- tighten the loader/runtime API into a more library-shaped public surface
- document the asset contract more formally per VAT variant
- improve Blender exporter compatibility with Houdini-style VAT3 conventions
- expand sample coverage and validation across soft body, rigid body, particles, and dynamic mesh/fluid workflows
- reduce special-case handling between legacy samples, current samples, and OpenVAT-style inputs
This implementation was informed by:
- mikelyndon/r3f-webgl-vertex-animation-textures
- floating-world-lda/vat3-wgsl-ts (Three.js)
- floating-world-lda/vat3-wgsl-ts (Babylon.js)
- Babylon playground reference
by Thrax
