UNPKG

@gltf-transform/extensions

Version:

Adds extension support to @gltf-transform/core

162 lines (161 loc) 7.02 kB
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 {};