From 03325ed889e12cf07d7e27a7a7b64e4a1ce4037d Mon Sep 17 00:00:00 2001 From: Don McCurdy Date: Fri, 17 May 2024 23:19:42 -0400 Subject: [PATCH] feat(extensions): Add KHR_accessor_float64 --- packages/cli/src/cli.ts | 40 ++++++++++++++++++- packages/core/src/constants.ts | 4 +- packages/core/src/io/reader.ts | 4 ++ packages/core/src/io/writer.ts | 4 ++ packages/core/src/properties/accessor.ts | 15 +++++-- packages/core/src/types/gltf.ts | 2 +- packages/extensions/src/constants.ts | 1 + packages/extensions/src/index.ts | 3 ++ .../src/khr-draco-mesh-compression/encoder.ts | 12 +++--- 9 files changed, 73 insertions(+), 12 deletions(-) diff --git a/packages/cli/src/cli.ts b/packages/cli/src/cli.ts index cdda5117d..9305f850d 100755 --- a/packages/cli/src/cli.ts +++ b/packages/cli/src/cli.ts @@ -6,7 +6,7 @@ import fetch from 'node-fetch'; import mikktspace from 'mikktspace'; import { MeshoptEncoder, MeshoptSimplifier } from 'meshoptimizer'; import { ready as resampleReady, resample as resampleWASM } from 'keyframe-resample'; -import { Logger, NodeIO, PropertyType, VertexLayout, vec2, Transform } from '@gltf-transform/core'; +import { Logger, NodeIO, PropertyType, VertexLayout, vec2, Transform, Accessor } from '@gltf-transform/core'; import { CenterOptions, InstanceOptions, @@ -967,6 +967,44 @@ Removes KHR_mesh_quantization, if present.`.trim(), return Session.create(io, logger, args.input, args.output).transform(dequantize({ ...options, pattern })); }); +// F64 +program + .command('f64', 'Convert all f32 accessors to f64 (EXPERIMENTAL)') + .help(`Experimental testing feature for KHR_accessor_float64`.trim()) + .argument('', 'Path to read glTF 2.0 (.glb, .gltf) input') + .argument('', 'Path to write output') + .action(({ args, options, logger }) => { + return Session.create(io, logger, args.input, args.output).transform( + dequantize({ ...options }), + (document) => { + for (const accessor of document.getRoot().listAccessors()) { + if (accessor.getComponentType() === Accessor.ComponentType.FLOAT) { + accessor.setArray(new Float64Array(accessor.getArray()!)); + } + } + } + ); + }); + +// F32 +program + .command('f32', 'Convert all f64 accessors to f32 (EXPERIMENTAL)') + .help(`Experimental testing feature for KHR_accessor_float64`.trim()) + .argument('', 'Path to read glTF 2.0 (.glb, .gltf) input') + .argument('', 'Path to write output') + .action(({ args, options, logger }) => { + return Session.create(io, logger, args.input, args.output).transform( + dequantize({ ...options }), + (document) => { + for (const accessor of document.getRoot().listAccessors()) { + if (accessor.getComponentType() === Accessor.ComponentType.FLOAT64) { + accessor.setArray(new Float32Array(accessor.getArray()!)); + } + } + } + ); + }); + // WELD program .command('weld', 'Merge equivalent vertices to optimize geometry') diff --git a/packages/core/src/constants.ts b/packages/core/src/constants.ts index aff1f8e78..809d02fb6 100644 --- a/packages/core/src/constants.ts +++ b/packages/core/src/constants.ts @@ -81,13 +81,14 @@ export const GLB_BUFFER = '@glb.bin'; * Abstraction representing any one of the typed array classes supported by glTF and JavaScript. * @hidden */ -export type TypedArray = Float32Array | Uint32Array | Uint16Array | Uint8Array | Int16Array | Int8Array; +export type TypedArray = Float64Array | Float32Array | Uint32Array | Uint16Array | Uint8Array | Int16Array | Int8Array; /** * Abstraction representing the typed array constructors supported by glTF and JavaScript. * @hidden */ export type TypedArrayConstructor = + | Float64ArrayConstructor | Float32ArrayConstructor | Uint32ArrayConstructor | Uint16ArrayConstructor @@ -159,4 +160,5 @@ export const ComponentTypeToTypedArray = { '5123': Uint16Array, '5125': Uint32Array, '5126': Float32Array, + '5130': Float64Array, }; diff --git a/packages/core/src/io/reader.ts b/packages/core/src/io/reader.ts index e9ea02d7f..7580a2f59 100644 --- a/packages/core/src/io/reader.ts +++ b/packages/core/src/io/reader.ts @@ -588,6 +588,10 @@ function getInterleavedArray(accessorDef: GLTF.IAccessor, context: ReaderContext case Accessor.ComponentType.BYTE: value = view.getInt8(byteOffset); break; + // KHR_accessor_float64 + case Accessor.ComponentType.FLOAT64: + value = view.getFloat64(byteOffset, true); + break; default: throw new Error(`Unexpected componentType "${accessorDef.componentType}".`); } diff --git a/packages/core/src/io/writer.ts b/packages/core/src/io/writer.ts index f875406e8..975d6da68 100644 --- a/packages/core/src/io/writer.ts +++ b/packages/core/src/io/writer.ts @@ -192,6 +192,10 @@ export class GLTFWriter { case Accessor.ComponentType.UNSIGNED_INT: view.setUint32(viewByteOffset, value, true); break; + // KHR_accessor_float64 + case Accessor.ComponentType.FLOAT64: + view.setFloat64(viewByteOffset, value, true); + break; default: throw new Error('Unexpected component type: ' + componentType); } diff --git a/packages/core/src/properties/accessor.ts b/packages/core/src/properties/accessor.ts index 2d4e5a878..ee8e5902f 100644 --- a/packages/core/src/properties/accessor.ts +++ b/packages/core/src/properties/accessor.ts @@ -124,6 +124,12 @@ export class Accessor extends ExtensibleProperty { * {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Float32Array Float32Array}. */ FLOAT: 5126, + /** + * 8-byte floating point number, stored as + * {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Float64Array Float64Array}. + * KHR_accessor_float64 + */ + FLOAT64: 5130, }; /********************************************************************************************** @@ -175,17 +181,17 @@ export class Accessor extends ExtensibleProperty { public static getComponentSize(componentType: GLTF.AccessorComponentType): number { switch (componentType) { case Accessor.ComponentType.BYTE: - return 1; case Accessor.ComponentType.UNSIGNED_BYTE: return 1; case Accessor.ComponentType.SHORT: - return 2; case Accessor.ComponentType.UNSIGNED_SHORT: return 2; case Accessor.ComponentType.UNSIGNED_INT: - return 4; case Accessor.ComponentType.FLOAT: return 4; + // KHR_accessor_float64 + case Accessor.ComponentType.FLOAT64: + return 8; default: throw new Error('Unexpected component type: ' + componentType); } @@ -548,6 +554,9 @@ function arrayToComponentType(array: TypedArray): GLTF.AccessorComponentType { return Accessor.ComponentType.SHORT; case Int8Array: return Accessor.ComponentType.BYTE; + // KHR_accessor_float64 + case Float64Array: + return Accessor.ComponentType.FLOAT64; default: throw new Error('Unknown accessor componentType.'); } diff --git a/packages/core/src/types/gltf.ts b/packages/core/src/types/gltf.ts index 378727db8..8b2a4b84f 100644 --- a/packages/core/src/types/gltf.ts +++ b/packages/core/src/types/gltf.ts @@ -4,7 +4,7 @@ */ export declare module GLTF { /** Data type of the values composing each element in the accessor. */ - type AccessorComponentType = 5120 | 5121 | 5122 | 5123 | 5125 | 5126; + type AccessorComponentType = 5120 | 5121 | 5122 | 5123 | 5125 | 5126 | 5130; /** Element type contained by the accessor (SCALAR, VEC2, ...). */ type AccessorType = 'SCALAR' | 'VEC2' | 'VEC3' | 'VEC4' | 'MAT2' | 'MAT3' | 'MAT4'; /** Name of the property to be modified by an animation channel. */ diff --git a/packages/extensions/src/constants.ts b/packages/extensions/src/constants.ts index 1f519869e..3b983c12b 100644 --- a/packages/extensions/src/constants.ts +++ b/packages/extensions/src/constants.ts @@ -2,6 +2,7 @@ export const EXT_MESH_GPU_INSTANCING = 'EXT_mesh_gpu_instancing'; export const EXT_MESHOPT_COMPRESSION = 'EXT_meshopt_compression'; export const EXT_TEXTURE_WEBP = 'EXT_texture_webp'; export const EXT_TEXTURE_AVIF = 'EXT_texture_avif'; +export const KHR_ACCESSOR_FLOAT64 = 'KHR_accessor_float64'; export const KHR_DRACO_MESH_COMPRESSION = 'KHR_draco_mesh_compression'; export const KHR_LIGHTS_PUNCTUAL = 'KHR_lights_punctual'; export const KHR_MATERIALS_ANISOTROPY = 'KHR_materials_anisotropy'; diff --git a/packages/extensions/src/index.ts b/packages/extensions/src/index.ts index 620005614..85effa3fd 100644 --- a/packages/extensions/src/index.ts +++ b/packages/extensions/src/index.ts @@ -2,6 +2,7 @@ import { EXTMeshGPUInstancing } from './ext-mesh-gpu-instancing/index.js'; import { EXTMeshoptCompression } from './ext-meshopt-compression/index.js'; import { EXTTextureAVIF } from './ext-texture-avif/index.js'; import { EXTTextureWebP } from './ext-texture-webp/index.js'; +import { KHRAccessorFloat64 } from './khr-accessor-float64/index.js'; import { KHRDracoMeshCompression } from './khr-draco-mesh-compression/index.js'; import { KHRLightsPunctual } from './khr-lights-punctual/index.js'; import { KHRMaterialsAnisotropy } from './khr-materials-anisotropy/index.js'; @@ -24,6 +25,7 @@ import { KHRTextureTransform } from './khr-texture-transform/index.js'; import { KHRXMP } from './khr-xmp-json-ld/index.js'; export const KHRONOS_EXTENSIONS = [ + KHRAccessorFloat64, KHRDracoMeshCompression, KHRLightsPunctual, KHRMaterialsAnisotropy, @@ -58,6 +60,7 @@ export * from './ext-mesh-gpu-instancing/index.js'; export * from './ext-meshopt-compression/index.js'; export * from './ext-texture-avif/index.js'; export * from './ext-texture-webp/index.js'; +export * from './khr-accessor-float64/index.js'; export * from './khr-draco-mesh-compression/index.js'; export * from './khr-lights-punctual/index.js'; export * from './khr-materials-anisotropy/index.js'; diff --git a/packages/extensions/src/khr-draco-mesh-compression/encoder.ts b/packages/extensions/src/khr-draco-mesh-compression/encoder.ts index 96c93d8f6..2a8da72f9 100644 --- a/packages/extensions/src/khr-draco-mesh-compression/encoder.ts +++ b/packages/extensions/src/khr-draco-mesh-compression/encoder.ts @@ -174,17 +174,17 @@ function addAttribute( ): number { switch (componentType) { case Accessor.ComponentType.UNSIGNED_BYTE: - return builder.AddUInt8Attribute(mesh, attribute, count, itemSize, array); + return builder.AddUInt8Attribute(mesh, attribute, count, itemSize, array as Uint8Array); case Accessor.ComponentType.BYTE: - return builder.AddInt8Attribute(mesh, attribute, count, itemSize, array); + return builder.AddInt8Attribute(mesh, attribute, count, itemSize, array as Int8Array); case Accessor.ComponentType.UNSIGNED_SHORT: - return builder.AddUInt16Attribute(mesh, attribute, count, itemSize, array); + return builder.AddUInt16Attribute(mesh, attribute, count, itemSize, array as Uint16Array); case Accessor.ComponentType.SHORT: - return builder.AddInt16Attribute(mesh, attribute, count, itemSize, array); + return builder.AddInt16Attribute(mesh, attribute, count, itemSize, array as Int16Array); case Accessor.ComponentType.UNSIGNED_INT: - return builder.AddUInt32Attribute(mesh, attribute, count, itemSize, array); + return builder.AddUInt32Attribute(mesh, attribute, count, itemSize, array as Uint32Array); case Accessor.ComponentType.FLOAT: - return builder.AddFloatAttribute(mesh, attribute, count, itemSize, array); + return builder.AddFloatAttribute(mesh, attribute, count, itemSize, array as Float32Array); default: throw new Error(`Unexpected component type, "${componentType}".`); }