Common glTF modifications, written using the core API.

Most of these functions are Transforms, applying a modification to the Document, used with Document.transform. This project includes many common transforms already, and others can be quickly implemented using the same APIs. Other functions, like bounds, provide non-mutating functionality on top of the existing glTF-Transform property types.

Installation

Install:

npm install --save @gltf-transform/functions

Import:

import { NodeIO } from '@gltf-transform/core';
import { dedup, quantize, weld } from '@gltf-transform/functions';

const io = new NodeIO();
const document = await io.read('input.glb');

await document.transform(
    weld(),
    quantize(),
    dedup()
);

await io.write('output.glb', document);

Functions

  • bounds(node: Node | Scene): bbox
  • Computes bounding box (AABB) in world space for the given Node or Scene.

    Example:

    const {min, max} = bounds(scene);
    
  • Centers the Scene at the origin, or above/below it. Transformations from animation, skinning, and morph targets are not taken into account.

    Example:

    await document.transform(center({pivot: 'below'}));
    
  • Vertex color colorspace correction. The glTF format requires vertex colors to be stored as linear values, and this function provides a way to correct vertex colors that are (incorrectly) sRGB.

  • createLayoutPlan(document: Document): LayoutPlan
  • Constructs a plan for processing vertex streams, based on unique index:attribute[] groups. Where different indices are used with the same attributes, we'll end up splitting the primitives to not share attributes, which appears to be consistent with the Meshopt implementation.

  • dedup(_options?: DedupOptions): Transform
  • Removes duplicate Accessor, Mesh, Texture, and Material properties. Partially based on a gist by mattdesl. Only accessors in mesh primitives, morph targets, and animation samplers are processed.

    Example:

    document.getRoot().listMeshes(); // → [Mesh, Mesh, Mesh]
    
    await document.transform(dedup({propertyTypes: [PropertyType.MESH]}));
    
    document.getRoot().listMeshes(); // → [Mesh]
    
  • draco(_options: DracoOptions): Transform
  • getTextureChannelMask(document: Document, texture: Texture): number
  • Returns bitmask of all TextureChannels used by the given texture. Determination is based only on the role of the textures, e.g. a texture used for the occlusionTexture will have (at least) a red channel. See listTextureChannels for an array alternative.

    Example:

    const mask = getTextureChannelMask(document, texture);
    if (mask & TextureChannel.R) {
      console.log('texture red channel used');
    }
    
  • inspect(doc: Document): InspectReport
  • instance(_options?: InstanceOptions): Transform
  • listTextureChannels(document: Document, texture: Texture): TextureChannel[]
  • Returns a list of TextureChannels used by the given texture. Determination is based only on the role of the textures, e.g. a texture used for the occlusionTexture will have (at least) a red channel in use. See getTextureChannelMask for bitmask alternative.

    Example:

    const channels = listTextureChannels(document, texture);
    if (channels.includes(TextureChannel.R)) {
      console.log('texture red channel used');
    }
    
  • listTextureInfo(document: Document, texture: Texture): TextureInfo[]
  • Lists all TextureInfo definitions associated with a given Texture.

    Example:

    // Find TextureInfo instances associated with the texture.
    const results = listTextureInfo(document, texture);
    
    // Find which UV sets (TEXCOORD_0, TEXCOORD_1, ...) are required.
    const texCoords = results.map((info) => info.getTexCoord());
    // → [0, 0, 1]
    
  • listTextureSlots(doc: Document, texture: Texture): string[]
  • Returns names of all texture slots using the given texture.

    Example:

    const slots = listTextureSlots(document, texture);
    // → ['occlusionTexture', 'metallicRoughnesTexture']
    
  • meshopt(_options: MeshoptOptions): Transform
  • Applies Meshopt compression using EXT_meshopt_compression. This type of compression can reduce the size of point, line, and triangle geometry, morph targets, and animation data.

    This function is a thin wrapper around reorder, quantize, and MeshoptCompression, and exposes relatively few configuration options. To access more options (like quantization bits) direct use of the underlying functions is recommended.

    Example:

    import { MeshoptEncoder } from 'meshoptimizer';
    import { reorder } from '@gltf-transform/functions';
    
    await MeshoptEncoder.ready;
    
    await document.transform(
      reorder({encoder: MeshoptEncoder, level: 'medium'})
    );
    
  • metalRough(_options?: MetalRoughOptions): Transform
  • Convert Materials from spec/gloss PBR workflow to metal/rough PBR workflow, removing KHR_materials_pbrSpecularGlossiness and adding KHR_materials_ior and KHR_materials_specular. The metal/rough PBR workflow is preferred for most use cases, and is a prerequisite for other advanced PBR extensions provided by glTF.

    No options are currently implemented for this function.

  • mozjpeg(options: SquooshOptions): Transform
  • Optimizes JPEG images by default, optionally converting PNG textures to JPEG.

    Requires @squoosh/lib, and currently works only in Node.js environments. Support for encoding in web browsers may be available pending GoogleChromeLabs/squoosh#1084.

    Example:

    import { cpus } from 'os';
    import * as squoosh from '@squoosh/lib';
    import { mozjpeg } from '@gltf-transform/functions';
    
    await document.transform(
        mozjpeg({ squoosh, jobs: cpus().length })
    );
    
  • Generates flat vertex normals for mesh primitives.

    Example:

    import { normals } from '@gltf-transform/functions';
    
    await document.transform(normals({overwrite: true}));
    
  • oxipng(options: SquooshOptions): Transform
  • Optimizes PNG images by default, optionally converting JPEG textures to PNG.

    Requires @squoosh/lib, and currently works only in Node.js environments. Support for encoding in web browsers may be available pending GoogleChromeLabs/squoosh#1084.

    Example:

    import { cpus } from 'os';
    import * as squoosh from '@squoosh/lib';
    import { oxipng } from '@gltf-transform/functions';
    
    await document.transform(
        oxipng({ squoosh, jobs: cpus().length })
    );
    
  • partition(_options?: PartitionOptions): Transform
  • Partitions the binary payload of a glTF file so separate mesh or animation data is in separate .bin Buffers. This technique may be useful for engines that support lazy-loading specific binary resources as needed over the application lifecycle.

    Example:

    document.getRoot().listBuffers(); // → [Buffer]
    
    await document.transform(partition({meshes: true}));
    
    document.getRoot().listBuffers(); // → [Buffer, Buffer, ...]
    
  • prune(_options?: PruneOptions): Transform
  • Removes properties from the file if they are not referenced by a Scene. Commonly helpful for cleaning up after other operations, e.g. allowing a node to be detached and any unused meshes, materials, or other resources to be removed automatically.

    Example:

    document.getRoot().listMaterials(); // → [Material, Material]
    
    await document.transform(prune());
    
    document.getRoot().listMaterials(); // → [Material]
    

    No options are currently implemented for this function.

  • Optimizes Mesh Primitives for locality of reference. Choose whether the order should be optimal for transmission size (recommended for Web) or for GPU rendering performance. Requires a MeshoptEncoder instance from the Meshoptimizer library.

    Example:

    import { MeshoptEncoder } from 'meshoptimizer';
    import { reorder } from '@gltf-transform/functions';
    
    await MeshoptEncoder.ready;
    
    await document.transform(
        reorder({encoder: MeshoptEncoder})
    );
    
  • resample(_options?: ResampleOptions): Transform
  • Resample Animations, losslessly deduplicating keyframes to reduce file size. Duplicate keyframes are commonly present in animation 'baked' by the authoring software to apply IK constraints or other software-specific features. Based on THREE.KeyframeTrack.optimize().

    Example: (0,0,0,0,1,1,1,0,0,0,0,0,0,0) --> (0,0,1,1,0,0)

  • sequence(_options?: SequenceOptions): Transform
  • Simplification algorithm, based on meshoptimizer, producing meshes with fewer triangles and vertices. Simplification is lossy, but the algorithm aims to preserve visual quality as much as possible for given parameters.

    The algorithm aims to reach the target 'ratio', while minimizing error. If error exceeds the specified 'error' threshold, the algorithm will quit before reaching the target ratio. Examples:

    • ratio=0.5, error=0.001: Aims for 50% simplification, constrained to 0.1% error.
    • ratio=0.5, error=1: Aims for 50% simplification, unconstrained by error.
    • ratio=0.0, error=0.01: Aims for maximum simplification, constrained to 1% error.

    Topology, particularly split vertices, will also limit the simplifier. For best results, apply a weld operation before simplification.

    Example:

    import { simplify, weld } from '@gltf-transform/functions';
    import { MeshoptSimplifier } from 'meshoptimizer';
    
    await document.transform(
      weld({ tolerance: 0.0001 }),
      simplify({ simplifier: MeshoptSimplifier, ratio: 0.75, error: 0.001 })
    );
    

    References:

  • sortPrimitiveWeights(prim: Primitive | PrimitiveTarget, limit?: number): void
  • Sorts skinning weights from high to low, for each vertex of the input Primitive or PrimitiveTarget, and normalizes the weights. Optionally, uses the given 'limit' to remove least-significant joint influences such that no vertex has more than 'limit' influences.

    Most realtime engines support a limited number of joint influences per vertex, often 4 or 8. Sorting and removing the additional influences can reduce file size and improve compatibility.

    Example:

    import { sortPrimitiveWeights } from '@gltf-transform/functions';
    
    const limit = 4;
    for (const mesh of document.getRoot().listMeshes()) {
        for (const prim of mesh.listPrimitives()) {
            sortPrimitiveWeights(prim, limit);
        }
    }
    
  • Generates MikkTSpace vertex tangents for mesh primitives, which may fix rendering issues occuring with some baked normal maps. Requires access to the mikktspace WASM package, or equivalent.

    Example:

    import { generateTangents } from 'mikktspace';
    import { tangents } from '@gltf-transform/functions';
    
    await document.transform(
        tangents({generateTangents})
    );
    
  • transformMesh(mesh: Mesh, matrix: mat4, overwrite?: boolean, skipIndices?: Set<number>): void
  • Applies a transform matrix to every Primitive in the given Mesh.

    Method:

    • If any primitives are shared by other meshes, they will be detached.
    • If any vertex streams are shared by primitives of other meshes, vertex data will be overwritten unless overwrite=false or the indices are masked. If overwrite=false, a detached copy of the vertex stream is made before applying the transform.
    • Primitives within the mesh sharing vertex streams will continue to share those streams.
    • For indexed primitives, only indexed vertices are modified.

    Example:

    import { fromTranslation } from 'gl-matrix/mat4';
    import { transformMesh } from '@gltf-transform/functions';
    
    // offset vertices, y += 10.
    transformMesh(mesh, fromTranslation([], [0, 10, 0]));
    
  • transformPrimitive(prim: Primitive, matrix: mat4, skipIndices?: Set<number>): void
  • Applies a transform matrix to a Primitive.

    When calling transformPrimitive, any un-masked vertices are overwritten directly in the underlying vertex streams. If streams should be detached instead, see transformMesh.

    Example:

    import { fromTranslation } from 'gl-matrix/mat4';
    import { transformPrimitive } from '@gltf-transform/functions';
    
    // offset vertices, y += 10.
    transformPrimitive(prim, fromTranslation([], [0, 10, 0]));
    
  • unpartition(_options?: UnpartitionOptions): Transform
  • Removes partitions from the binary payload of a glTF file, so that the asset contains at most one (1) .bin Buffer. This process reverses the changes from a partition transform.

    Example:

    document.getRoot().listBuffers(); // → [Buffer, Buffer, ...]
    
    await document.transform(unpartition());
    
    document.getRoot().listBuffers(); // → [Buffer]
    
  • De-index Primitives, disconnecting any shared vertices. This operation will generally increase the number of vertices in a mesh, but may be helpful for some geometry operations or for creating hard edges.

    No options are currently implemented for this function.

  • webp(options: SquooshOptions): Transform
  • Converts images to WebP, using the TextureWebP extension.

    Requires @squoosh/lib, and currently works only in Node.js environments. Support for encoding in web browsers may be available pending GoogleChromeLabs/squoosh#1084.

    Example:

    import { cpus } from 'os';
    import * as squoosh from '@squoosh/lib';
    import { webp } from '@gltf-transform/functions';
    
    await document.transform(
        webp({ squoosh, jobs: cpus().length })
    );
    
  • Index Primitives and (optionally) merge similar vertices. When merged and indexed, data is shared more efficiently between vertices. File size can be reduced, and the GPU can sometimes use the vertex cache more efficiently.

    When welding, the 'tolerance' threshold determines which vertices qualify for welding based on distance between the vertices as a fraction of the primitive's bounding box (AABB). For example, tolerance=0.01 welds vertices within +/-1% of the AABB's longest dimension. Other vertex attributes are also compared during welding, with attribute-specific thresholds. For --tolerance=0, geometry is indexed in place, without merging.

    Example:

    import { weld } from '@gltf-transform/functions';
    
    await document.transform(
        weld({ tolerance: 0.001 })
    );
    
  • weldPrimitive(doc: Document, prim: Primitive, options: Required<WeldOptions>): void
  • Index a Primitive and (optionally) weld similar vertices. When merged and indexed, data is shared more efficiently between vertices. File size can be reduced, and the GPU can sometimes use the vertex cache more efficiently.

    When welding, the 'tolerance' threshold determines which vertices qualify for welding based on distance between the vertices as a fraction of the primitive's bounding box (AABB). For example, tolerance=0.01 welds vertices within +/-1% of the AABB's longest dimension. Other vertex attributes are also compared during welding, with attribute-specific thresholds. For --tolerance=0, geometry is indexed in place, without merging.

    Example:

    import { weldPrimitive } from '@gltf-transform/functions';
    
    const mesh = document.getRoot().listMeshes()
        .find((mesh) => mesh.getName() === 'Gizmo');
    
    for (const prim of mesh.listPrimitives()) {
      weldPrimitive(document, prim, {tolerance: 0.0001});
    }
    
Function symbol, f(📦) → 📦, where the argument and output are a box labeled 'glTF'.

Made by Don McCurdy TypeDoc documentation Copyright 2021 under MIT license