@anhldh/gltf-animation-pointer-extensions
v0.1.1
Published
KHR_animation_pointer support for three.js (GLTFLoader) and gltf-transform.
Maintainers
Readme
@anhldh/gltf-animation-pointer-extensions
Support for the glTF KHR_animation_pointer extension on both sides of the pipeline:
- Runtime (three.js) — load models that use
KHR_animation_pointerwithGLTFLoader(and drei'suseGLTF). three.js does not support it by default, so those animations simply don't play. - Build-time (gltf-transform) — preserve
KHR_animation_pointerchannels while processing/optimizing a model. gltf-transform drops them by default.
What is KHR_animation_pointer?
The extension lets an animation target almost any property of a model via a JSON pointer — not just node transforms, but material factors / texture transforms, light parameters (KHR_lights_punctual), cameras, morph weights, visibility, and more. Because it lives outside "classic" animation, both three.js and gltf-transform ignore it unless a dedicated extension handles it.
Installation
pnpm add @anhldh/gltf-animation-pointer-extensionsPeer dependencies are optional — install only the side you use:
# runtime (three.js)
pnpm add three
# build-time (gltf-transform)
pnpm add @gltf-transform/core @gltf-transform/extensionsUsage — three.js
Plain GLTFLoader
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
import { GLTFAnimationPointerExtension } from "@anhldh/gltf-animation-pointer-extensions";
const loader = new GLTFLoader();
loader.register((p) => new GLTFAnimationPointerExtension(p));
const gltf = await loader.loadAsync("model.glb");
// gltf.animations now contains tracks generated from KHR_animation_pointerReact Three Fiber (drei useGLTF)
The 4th argument of useGLTF is extendLoader — where you register the extension:
import { useGLTF } from "@react-three/drei";
import type {
GLTFLoader,
GLTFParser,
} from "three/examples/jsm/loaders/GLTFLoader.js";
import { GLTFAnimationPointerExtension } from "@anhldh/gltf-animation-pointer-extensions";
const gltf = useGLTF(url, true, true, (loader) => {
// drei uses the three-stdlib GLTFLoader -> cast to three/examples.
const l = loader as unknown as GLTFLoader;
l.register((p: GLTFParser) => new GLTFAnimationPointerExtension(p));
});
useGLTFcaches by URL, soextendLoaderonly runs on the first load of each URL.
Usage — gltf-transform
When you run a gltf-transform pipeline (weld, dedup, draco, resample, prune, ...), KHR_animation_pointer channels are dropped by default. Register KHRAnimationPointer to read and write them back correctly — it automatically remaps pointers to the new node/material indices after transforms.
import { WebIO } from "@gltf-transform/core";
import { ALL_EXTENSIONS } from "@gltf-transform/extensions";
import { KHRAnimationPointer } from "@anhldh/gltf-animation-pointer-extensions/transform";
const io = new WebIO().registerExtensions([
...ALL_EXTENSIONS,
KHRAnimationPointer,
]);
const doc = await io.read(url); // preread: maps pointer -> Node/Material
// ...doc.transform(...)... // pointers follow Node/Material, stay correct
const glb = await io.writeBinary(doc); // write: emits pointer with new indicesOn Node, use NodeIO instead of WebIO:
import { NodeIO } from "@gltf-transform/core";
import { ALL_EXTENSIONS } from "@gltf-transform/extensions";
import { KHRAnimationPointer } from "@anhldh/gltf-animation-pointer-extensions/transform";
const io = new NodeIO().registerExtensions([
...ALL_EXTENSIONS,
KHRAnimationPointer,
]);
const doc = await io.read("in.glb");
await io.write("out.glb", doc);API
three.js side — from @anhldh/gltf-animation-pointer-extensions
GLTFAnimationPointerExtension— plugin registered viaGLTFLoader.register(...)..setAnimationPointerResolver(resolver)— customize how a pointer path maps to a three.js property.invalidateMaterialCache(root)— clear the material cache when materials are swapped/cloned at runtime.- type
AnimationPointerResolver.
gltf-transform side — from @anhldh/gltf-animation-pointer-extensions/transform
KHRAnimationPointer—Extensionregistered on the IO (WebIO/NodeIO).AnimationPointer—ExtensionPropertyattached to anAnimationChannel.KHR_ANIMATION_POINTER— the extension-name constant ("KHR_animation_pointer").
Notes
- The three.js side patches
PropertyBinding.findNode(global, once, guarded) so pointers can bind to material / texture / light / camera. Textures shared across materials are cloned before binding to avoid animating the wrong one. - If an animation fails, the extension returns an empty clip instead of breaking the whole model — the model still loads, only that animation is lost.
- Published builds are minified; read the source on GitHub.
- The two sides are separate entry points —
.(three.js) and/transform(gltf-transform). Importing one never loads the other's peer dependency, so you only need the peer dep of the side you actually import.
