@gltf-transform/extensions
Version:
Adds extension support to @gltf-transform/core
162 lines (161 loc) • 7.02 kB
TypeScript
import { Extension, PropertyType, type ReaderContext, WriterContext } from '@gltf-transform/core';
import { EXT_MESHOPT_COMPRESSION } from '../constants.js';
import { EncoderMethod } from './constants.js';
interface EncoderOptions {
method?: EncoderMethod;
}
/**
* [`EXT_meshopt_compression`](https://github.com/KhronosGroup/gltf/blob/main/extensions/2.0/Vendor/EXT_meshopt_compression/)
* provides compression and fast decoding for geometry, morph targets, and animations.
*
* Meshopt compression (based on the [meshoptimizer](https://github.com/zeux/meshoptimizer)
* library) offers a lightweight decoder with very fast runtime decompression, and is
* appropriate for models of any size. Meshopt can reduce the transmission sizes of geometry,
* morph targets, animation, and other numeric data stored in buffer views. When textures are
* large, other complementary compression methods should be used as well.
*
* For the full benefits of meshopt compression, **apply gzip, brotli, or another lossless
* compression method** to the resulting .glb, .gltf, or .bin files. Meshopt specifically
* pre-optimizes assets for this purpose — without this secondary compression, the size
* reduction is considerably less.
*
* Be aware that decompression happens before uploading to the GPU. While Meshopt decoding is
* considerably faster than Draco decoding, neither compression method will improve runtime
* performance directly. 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 Meshopt 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.
*
* The meshoptimizer library ([github](https://github.com/zeux/meshoptimizer/tree/master/js),
* [npm](https://www.npmjs.com/package/meshoptimizer)) is a required dependency for reading or
* writing files, and must be provided by the application. Compression may alternatively be applied
* with the [gltfpack](https://github.com/zeux/meshoptimizer/tree/master/gltf) tool.
*
* ### Example — Read
*
* To read glTF files using Meshopt compression, ensure that the extension
* and a decoder are registered. Geometry and other data are decompressed
* while reading the file.
*
* ```typescript
* import { NodeIO } from '@gltf-transform/core';
* import { EXTMeshoptCompression } from '@gltf-transform/extensions';
* import { MeshoptDecoder } from 'meshoptimizer';
*
* await MeshoptDecoder.ready;
*
* const io = new NodeIO()
* .registerExtensions([EXTMeshoptCompression])
* .registerDependencies({ 'meshopt.decoder': MeshoptDecoder });
*
* // Read and decode.
* const document = await io.read('compressed.glb');
* ```
*
* ### Example — Write
*
* The simplest way to apply Meshopt compression is with the {@link meshopt}
* transform. The extension and an encoder must be registered.
*
* ```typescript
* import { NodeIO } from '@gltf-transform/core';
* import { EXTMeshoptCompression } from '@gltf-transform/extensions';
* import { meshopt } from '@gltf-transform/functions';
* import { MeshoptEncoder } from 'meshoptimizer';
*
* await MeshoptEncoder.ready;
*
* const io = new NodeIO()
* .registerExtensions([EXTMeshoptCompression])
* .registerDependencies({ 'meshopt.encoder': MeshoptEncoder });
*
* await document.transform(
* meshopt({encoder: MeshoptEncoder, level: 'medium'})
* );
*
* await io.write('compressed-medium.glb', document);
* ```
*
* ### Example — Advanced
*
* Internally, the {@link meshopt} transform reorders and quantizes vertex data
* to preparate for compression. If you prefer different pre-processing, the
* EXTMeshoptCompression extension can be added to the document manually:
*
* ```typescript
* import { reorder, quantize } from '@gltf-transform/functions';
* import { EXTMeshoptCompression } from '@gltf-transform/extensions';
* import { MeshoptEncoder } from 'meshoptimizer';
*
* await document.transform(
* reorder({encoder: MeshoptEncoder}),
* quantize()
* );
*
* document.createExtension(EXTMeshoptCompression)
* .setRequired(true)
* .setEncoderOptions({ method: EXTMeshoptCompression.EncoderMethod.QUANTIZE });
* ```
*
* In either case, compression is deferred until generating output with an I/O
* class.
*/
export declare class EXTMeshoptCompression extends Extension {
readonly extensionName: typeof EXT_MESHOPT_COMPRESSION;
/** @hidden */
readonly prereadTypes: PropertyType[];
/** @hidden */
readonly prewriteTypes: PropertyType[];
/** @hidden */
readonly readDependencies: string[];
/** @hidden */
readonly writeDependencies: string[];
static readonly EXTENSION_NAME: typeof EXT_MESHOPT_COMPRESSION;
static readonly EncoderMethod: typeof EncoderMethod;
private _decoder;
private _decoderFallbackBufferMap;
private _encoder;
private _encoderOptions;
private _encoderFallbackBuffer;
private _encoderBufferViews;
private _encoderBufferViewData;
private _encoderBufferViewAccessors;
/** @hidden */
install(key: string, dependency: unknown): this;
/**
* Configures Meshopt options for quality/compression tuning. The two methods rely on different
* pre-processing before compression, and should be compared on the basis of (a) quality/loss
* and (b) final asset size after _also_ applying a lossless compression such as gzip or brotli.
*
* - QUANTIZE: Default. Pre-process with {@link quantize quantize()} (lossy to specified
* precision) before applying lossless Meshopt compression. Offers a considerable compression
* ratio with or without further supercompression. Equivalent to `gltfpack -c`.
* - FILTER: Pre-process with lossy filters to improve compression, before applying lossless
* Meshopt compression. While output may initially be larger than with the QUANTIZE method,
* this method will benefit more from supercompression (e.g. gzip or brotli). Equivalent to
* `gltfpack -cc`.
*
* Output with the FILTER method will generally be smaller after supercompression (e.g. gzip or
* brotli) is applied, but may be larger than QUANTIZE output without it. Decoding is very fast
* with both methods.
*
* Example:
*
* ```ts
* import { EXTMeshoptCompression } from '@gltf-transform/extensions';
*
* doc.createExtension(EXTMeshoptCompression)
* .setRequired(true)
* .setEncoderOptions({
* method: EXTMeshoptCompression.EncoderMethod.QUANTIZE
* });
* ```
*/
setEncoderOptions(options: EncoderOptions): this;
/** @hidden Removes Fallback buffers, if extension is required. */
read(_context: ReaderContext): this;
/** @hidden Puts encoded data into glTF output. */
write(context: WriterContext): this;
}
export {};