@gltf-transform/extensions
Version:
Adds extension support to @gltf-transform/core
150 lines (149 loc) • 6 kB
TypeScript
import { Extension, PropertyType, type ReaderContext, type WriterContext } from '@gltf-transform/core';
import { KHR_DRACO_MESH_COMPRESSION } from '../constants.js';
import { EncoderMethod, type EncoderOptions } from './encoder.js';
/**
* [`KHR_draco_mesh_compression`](https://github.com/KhronosGroup/gltf/blob/main/extensions/2.0/Khronos/KHR_draco_mesh_compression/)
* provides advanced compression for mesh geometry.
*
* For models where geometry is a significant factor (>1 MB), Draco can reduce filesize by ~95%
* in many cases. When animation or textures are large, other complementary compression methods
* should be used as well. For geometry <1MB, the size of the WASM decoder library may outweigh
* size savings.
*
* Be aware that decompression happens before uploading to the GPU — this will add some latency to
* the parsing process, and means that compressing geometry with Draco does _not_ affect runtime
* performance. To improve framerate, you'll need to simplify the geometry by reducing vertex count
* or draw calls — not just compress it. Finally, be aware that Draco compression is lossy:
* repeatedly compressing and decompressing a model in a pipeline will lose precision, so
* compression should generally be the last stage of an art workflow, and uncompressed original
* files should be kept.
*
* A decoder or encoder from the `draco3dgltf` npm module for Node.js (or
* [elsewhere for web](https://stackoverflow.com/a/66978236/1314762)) is required for reading and writing,
* and must be provided by the application.
*
* ### Encoding options
*
* Two compression methods are available: 'edgebreaker' and 'sequential'. The
* edgebreaker method will give higher compression in general, but changes the
* order of the model's vertices. To preserve index order, use sequential
* compression. When a mesh uses morph targets, or a high decoding speed is
* selected, sequential compression will automatically be chosen.
*
* Both speed options affect the encoder's choice of algorithms. For example, a
* requirement for fast decoding may prevent the encoder from using the best
* compression methods even if the encoding speed is set to 0. In general, the
* faster of the two options limits the choice of features that can be used by the
* encoder. Setting --decodeSpeed to be faster than the --encodeSpeed may allow
* the encoder to choose the optimal method out of the available features for the
* given --decodeSpeed.
*
* ### Example — Read
*
* To read glTF files using Draco compression, ensure that the extension
* and a decoder are registered. Geometry is decompressed while reading.
*
* ```typescript
* import { NodeIO } from '@gltf-transform/core';
* import { KHRDracoMeshCompression } from '@gltf-transform/extensions';
* import draco3d from 'draco3dgltf';
*
* const io = new NodeIO()
* .registerExtensions([KHRDracoMeshCompression])
* .registerDependencies({
* 'draco3d.decoder': await draco3d.createDecoderModule()
* });
*
* // Read and decode.
* const document = await io.read('compressed.glb');
* ```
*
* ### Example — Write
*
* The simplest way to apply Draco compression is with the {@link draco}
* transform. The extension and an encoder must be registered.
*
* ```typescript
* import { NodeIO } from '@gltf-transform/core';
* import { KHRDracoMeshCompression } from '@gltf-transform/extensions';
* import { draco } from '@gltf-transform/functions';
* import draco3d from 'draco3dgltf';
*
* const io = new NodeIO()
* .registerExtensions([KHRDracoMeshCompression])
* .registerDependencies({
* 'draco3d.encoder': await draco3d.createEncoderModule()
* });
*
* await document.transform(
* draco({method: 'edgebreaker'})
* );
*
* await io.write('compressed.glb', document);
* ```
*
* ### Example
*
* Equivalently, the KHRDracoMeshCompression extension can be added manually to a document.
*
* ```typescript
* import { KHRDracoMeshCompression } from '@gltf-transform/extensions';
*
* document.createExtension(KHRDracoMeshCompression)
* .setRequired(true)
* .setEncoderOptions({
* method: KHRDracoMeshCompression.EncoderMethod.EDGEBREAKER,
* encodeSpeed: 5,
* decodeSpeed: 5,
* });
*
* await io.write('compressed.glb', document);
* ```
*
* In either case, Compression is deferred until generating output with an
* I/O class.
*/
export declare class KHRDracoMeshCompression extends Extension {
readonly extensionName: typeof KHR_DRACO_MESH_COMPRESSION;
/** @hidden */
readonly prereadTypes: PropertyType[];
/** @hidden */
readonly prewriteTypes: PropertyType[];
/** @hidden */
readonly readDependencies: string[];
/** @hidden */
readonly writeDependencies: string[];
static readonly EXTENSION_NAME: typeof KHR_DRACO_MESH_COMPRESSION;
/**
* Compression method. `EncoderMethod.EDGEBREAKER` usually provides a higher compression ratio,
* while `EncoderMethod.SEQUENTIAL` better preserves original vertex order.
*/
static readonly EncoderMethod: typeof EncoderMethod;
private _decoderModule;
private _encoderModule;
private _encoderOptions;
/** @hidden */
install(key: string, dependency: unknown): this;
/**
* Sets Draco compression options. Compression does not take effect until the Document is
* written with an I/O class.
*
* Defaults:
* ```
* decodeSpeed?: number = 5;
* encodeSpeed?: number = 5;
* method?: EncoderMethod = EncoderMethod.EDGEBREAKER;
* quantizationBits?: {[ATTRIBUTE_NAME]: bits};
* quantizationVolume?: 'mesh' | 'scene' | bbox = 'mesh';
* ```
*/
setEncoderOptions(options: EncoderOptions): this;
/** @hidden */
preread(context: ReaderContext): this;
/** @hidden */
read(_context: ReaderContext): this;
/** @hidden */
prewrite(context: WriterContext, _propertyType: PropertyType): this;
/** @hidden */
write(context: WriterContext): this;
}