UNPKG

@gltf-transform/functions

Version:

Functions for common glTF modifications, written using the core API

86 lines (75 loc) 2.7 kB
import { Accessor, AnimationChannel, AnimationSampler, Document, Transform } from '@gltf-transform/core'; import { assignDefaults, createTransform } from './utils.js'; const NAME = 'sequence'; export interface SequenceOptions { /** Frames per second, where one node is shown each frame. Default 10. */ fps?: number; /** Pattern (regex) used to filter nodes for the sequence. Required. */ pattern: RegExp; /** Name of the new animation. */ name?: string; /** Whether to sort the nodes by name, or use original order. Default true. */ sort?: boolean; } const SEQUENCE_DEFAULTS: Required<SequenceOptions> = { name: '', fps: 10, pattern: /.*/, sort: true, }; /** * Creates an {@link Animation} displaying each of the specified {@link Node}s sequentially. * * @category Transforms */ export function sequence(_options: SequenceOptions = SEQUENCE_DEFAULTS): Transform { const options = assignDefaults(SEQUENCE_DEFAULTS, _options); return createTransform(NAME, (doc: Document): void => { const logger = doc.getLogger(); const root = doc.getRoot(); const fps = options.fps; // Collect sequence nodes. const sequenceNodes = root.listNodes().filter((node) => node.getName().match(options.pattern)); // Sort by node name. if (options.sort) { sequenceNodes.sort((a, b) => (a.getName() > b.getName() ? 1 : -1)); } // Create animation cycling visibility of each node. const anim = doc.createAnimation(options.name); const animBuffer = root.listBuffers()[0]; sequenceNodes.forEach((node, i) => { // Create keyframe tracks that show each node for a single frame. let inputArray; let outputArray; if (i === 0) { inputArray = [i / fps, (i + 1) / fps]; outputArray = [1, 1, 1, 0, 0, 0]; } else if (i === sequenceNodes.length - 1) { inputArray = [(i - 1) / fps, i / fps]; outputArray = [0, 0, 0, 1, 1, 1]; } else { inputArray = [(i - 1) / fps, i / fps, (i + 1) / fps]; outputArray = [0, 0, 0, 1, 1, 1, 0, 0, 0]; } // Append channel to animation sequence. const input = doc.createAccessor().setArray(new Float32Array(inputArray)).setBuffer(animBuffer); const output = doc .createAccessor() .setArray(new Float32Array(outputArray)) .setBuffer(animBuffer) .setType(Accessor.Type.VEC3); const sampler = doc .createAnimationSampler() .setInterpolation(AnimationSampler.Interpolation.STEP) .setInput(input) .setOutput(output); const channel = doc .createAnimationChannel() .setTargetNode(node) .setTargetPath(AnimationChannel.TargetPath.SCALE) .setSampler(sampler); anim.addSampler(sampler).addChannel(channel); }); logger.debug(`${NAME}: Complete.`); }); }