UNPKG

@gltf-transform/core

Version:

glTF 2.0 SDK for JavaScript and TypeScript, on Web and Node.js.

1 lines 550 kB
{"version":3,"file":"index.cjs","sources":["../src/constants.ts","../src/utils/buffer-utils.ts","../src/utils/color-utils.ts","../src/utils/image-utils.ts","../src/utils/file-utils.ts","../../../node_modules/gl-matrix/esm/common.js","../../../node_modules/gl-matrix/esm/vec3.js","../src/utils/get-bounds.ts","../src/utils/http-utils.ts","../src/utils/is-plain-object.ts","../src/utils/logger.ts","../../../node_modules/gl-matrix/esm/mat4.js","../src/utils/math-utils.ts","../src/utils/property-utils.ts","../src/utils/uuid.ts","../src/properties/property.ts","../src/properties/extensible-property.ts","../src/properties/accessor.ts","../src/properties/animation.ts","../src/properties/animation-channel.ts","../src/properties/animation-sampler.ts","../src/properties/buffer.ts","../src/properties/camera.ts","../src/properties/extension-property.ts","../src/properties/texture-info.ts","../src/properties/material.ts","../src/properties/mesh.ts","../src/properties/node.ts","../src/properties/primitive.ts","../src/properties/primitive-target.ts","../src/properties/scene.ts","../src/properties/skin.ts","../src/properties/texture.ts","../src/properties/root.ts","../src/document.ts","../src/extension.ts","../src/io/reader-context.ts","../src/io/reader.ts","../src/io/writer-context.ts","../src/io/writer.ts","../src/io/platform-io.ts","../src/io/deno-io.ts","../src/io/node-io.ts","../src/io/web-io.ts"],"sourcesContent":["// Injected at compile time, from $npm_package_version.\ndeclare const PACKAGE_VERSION: string;\n\n/**\n * Current version of the package.\n * @hidden\n */\nexport const VERSION: string = `v${PACKAGE_VERSION}`;\n\n/** @internal */\nexport const NAME = '@gltf-transform/core';\n\n/**\n * Interface allowing Accessor setter/getter methods to be used interchangeably with gl-matrix\n * arrays or with three.js math objects' fromArray/toArray methods. For example, THREE.Vector2,\n * THREE.Vector3, THREE.Vector4, THREE.Quaternion, THREE.Matrix3, THREE.Matrix4, and THREE.Color.\n *\n * @internal\n */\nexport interface ArrayProxy {\n\t/** Sets the value of the object from an array of values. */\n\tfromArray(array: number[]): ArrayProxy;\n\t/** Writes the value of the object into the given array. */\n\ttoArray(array: number[]): number[];\n}\n\n/**\n * TypeScript utility for nullable types.\n * @hidden\n */\nexport type Nullable<T> = { [P in keyof T]: T[P] | null };\n\n/**\n * 2-dimensional vector.\n * @hidden\n */\nexport type vec2 = [number, number];\n\n/**\n * 3-dimensional vector.\n * @hidden\n */\nexport type vec3 = [number, number, number];\n\n/**\n * 4-dimensional vector, e.g. RGBA or a quaternion.\n * @hidden\n */\nexport type vec4 = [number, number, number, number];\n\n// biome-ignore format: Readability.\n/**\n * 3x3 matrix, e.g. an affine transform of a 2D vector.\n * @hidden\n */\nexport type mat3 = [\n\tnumber, number, number,\n\tnumber, number, number,\n\tnumber, number, number,\n];\n\n// biome-ignore format: Readability.\n/**\n * 4x4 matrix, e.g. an affine transform of a 3D vector.\n * @hidden\n */\nexport type mat4 = [\n\tnumber, number, number, number,\n\tnumber, number, number, number,\n\tnumber, number, number, number,\n\tnumber, number, number, number,\n];\n\n/** @hidden */\nexport type bbox = { min: vec3; max: vec3 };\n\n/** @hidden */\nexport const GLB_BUFFER = '@glb.bin';\n\n/**\n * Abstraction representing any one of the typed array classes supported by glTF and JavaScript.\n * @hidden\n */\nexport type TypedArray = Float32Array | Uint32Array | Uint16Array | Uint8Array | Int16Array | Int8Array;\n\n/**\n * Abstraction representing the typed array constructors supported by glTF and JavaScript.\n * @hidden\n */\nexport type TypedArrayConstructor =\n\t| Float32ArrayConstructor\n\t| Uint32ArrayConstructor\n\t| Uint16ArrayConstructor\n\t| Uint8ArrayConstructor\n\t| Int16ArrayConstructor\n\t| Int8ArrayConstructor;\n\n/** String IDs for core {@link Property} types. */\nexport enum PropertyType {\n\tACCESSOR = 'Accessor',\n\tANIMATION = 'Animation',\n\tANIMATION_CHANNEL = 'AnimationChannel',\n\tANIMATION_SAMPLER = 'AnimationSampler',\n\tBUFFER = 'Buffer',\n\tCAMERA = 'Camera',\n\tMATERIAL = 'Material',\n\tMESH = 'Mesh',\n\tPRIMITIVE = 'Primitive',\n\tPRIMITIVE_TARGET = 'PrimitiveTarget',\n\tNODE = 'Node',\n\tROOT = 'Root',\n\tSCENE = 'Scene',\n\tSKIN = 'Skin',\n\tTEXTURE = 'Texture',\n\tTEXTURE_INFO = 'TextureInfo',\n}\n\n/** Vertex layout method. */\nexport enum VertexLayout {\n\t/**\n\t * Stores vertex attributes in a single buffer view per mesh primitive. Interleaving vertex\n\t * data may improve performance by reducing page-thrashing in GPU memory.\n\t */\n\tINTERLEAVED = 'interleaved',\n\n\t/**\n\t * Stores each vertex attribute in a separate buffer view. May decrease performance by causing\n\t * page-thrashing in GPU memory. Some 3D engines may prefer this layout, e.g. for simplicity.\n\t */\n\tSEPARATE = 'separate',\n}\n\n/** Accessor usage. */\nexport enum BufferViewUsage {\n\tARRAY_BUFFER = 'ARRAY_BUFFER',\n\tELEMENT_ARRAY_BUFFER = 'ELEMENT_ARRAY_BUFFER',\n\tINVERSE_BIND_MATRICES = 'INVERSE_BIND_MATRICES',\n\tOTHER = 'OTHER',\n\tSPARSE = 'SPARSE',\n}\n\n/** Texture channels. */\nexport enum TextureChannel {\n\tR = 0x1000,\n\tG = 0x0100,\n\tB = 0x0010,\n\tA = 0x0001,\n}\n\nexport enum Format {\n\tGLTF = 'GLTF',\n\tGLB = 'GLB',\n}\n\nexport const ComponentTypeToTypedArray: Record<string, TypedArrayConstructor> = {\n\t'5120': Int8Array,\n\t'5121': Uint8Array,\n\t'5122': Int16Array,\n\t'5123': Uint16Array,\n\t'5125': Uint32Array,\n\t'5126': Float32Array,\n};\n","import type { TypedArray } from '../constants.js';\n\n/**\n * *Common utilities for working with Uint8Array and Buffer objects.*\n *\n * @category Utilities\n */\nexport class BufferUtils {\n\t/** Creates a byte array from a Data URI. */\n\tstatic createBufferFromDataURI(dataURI: string): Uint8Array {\n\t\tif (typeof Buffer === 'undefined') {\n\t\t\t// Browser.\n\t\t\tconst byteString = atob(dataURI.split(',')[1]);\n\t\t\tconst ia = new Uint8Array(byteString.length);\n\t\t\tfor (let i = 0; i < byteString.length; i++) {\n\t\t\t\tia[i] = byteString.charCodeAt(i);\n\t\t\t}\n\t\t\treturn ia;\n\t\t} else {\n\t\t\t// Node.js.\n\t\t\tconst data = dataURI.split(',')[1];\n\t\t\tconst isBase64 = dataURI.indexOf('base64') >= 0;\n\t\t\treturn Buffer.from(data, isBase64 ? 'base64' : 'utf8');\n\t\t}\n\t}\n\n\t/** Encodes text to a byte array. */\n\tstatic encodeText(text: string): Uint8Array {\n\t\treturn new TextEncoder().encode(text);\n\t}\n\n\t/** Decodes a byte array to text. */\n\tstatic decodeText(array: Uint8Array): string {\n\t\treturn new TextDecoder().decode(array);\n\t}\n\n\t/**\n\t * Concatenates N byte arrays.\n\t */\n\tstatic concat(arrays: Uint8Array[]): Uint8Array {\n\t\tlet totalByteLength = 0;\n\t\tfor (const array of arrays) {\n\t\t\ttotalByteLength += array.byteLength;\n\t\t}\n\n\t\tconst result = new Uint8Array(totalByteLength);\n\t\tlet byteOffset = 0;\n\n\t\tfor (const array of arrays) {\n\t\t\tresult.set(array, byteOffset);\n\t\t\tbyteOffset += array.byteLength;\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t/**\n\t * Pads a Uint8Array to the next 4-byte boundary.\n\t *\n\t * Reference: [glTF → Data Alignment](https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#data-alignment)\n\t */\n\tstatic pad(srcArray: Uint8Array, paddingByte = 0): Uint8Array {\n\t\tconst paddedLength = this.padNumber(srcArray.byteLength);\n\t\tif (paddedLength === srcArray.byteLength) return srcArray;\n\n\t\tconst dstArray = new Uint8Array(paddedLength);\n\t\tdstArray.set(srcArray);\n\n\t\tif (paddingByte !== 0) {\n\t\t\tfor (let i = srcArray.byteLength; i < paddedLength; i++) {\n\t\t\t\tdstArray[i] = paddingByte;\n\t\t\t}\n\t\t}\n\n\t\treturn dstArray;\n\t}\n\n\t/** Pads a number to 4-byte boundaries. */\n\tstatic padNumber(v: number): number {\n\t\treturn Math.ceil(v / 4) * 4;\n\t}\n\n\t/** Returns true if given byte array instances are equal. */\n\tstatic equals(a: Uint8Array, b: Uint8Array): boolean {\n\t\tif (a === b) return true;\n\n\t\tif (a.byteLength !== b.byteLength) return false;\n\n\t\tlet i = a.byteLength;\n\t\twhile (i--) {\n\t\t\tif (a[i] !== b[i]) return false;\n\t\t}\n\n\t\treturn true;\n\t}\n\n\t/**\n\t * Returns a Uint8Array view of a typed array, with the same underlying ArrayBuffer.\n\t *\n\t * A shorthand for:\n\t *\n\t * ```js\n\t * const buffer = new Uint8Array(\n\t * \tarray.buffer,\n\t * \tarray.byteOffset + byteOffset,\n\t * \tMath.min(array.byteLength, byteLength)\n\t * );\n\t * ```\n\t *\n\t */\n\tstatic toView(a: TypedArray, byteOffset = 0, byteLength: number = Infinity): Uint8Array {\n\t\treturn new Uint8Array(a.buffer, a.byteOffset + byteOffset, Math.min(a.byteLength, byteLength));\n\t}\n\n\t/** @internal */\n\tstatic assertView(view: null): null;\n\tstatic assertView(view: Uint8Array): Uint8Array;\n\tstatic assertView(view: Uint8Array | null): Uint8Array | null;\n\tstatic assertView(view: Uint8Array | null): Uint8Array | null {\n\t\tif (view && !ArrayBuffer.isView(view)) {\n\t\t\tthrow new Error(`Method requires Uint8Array parameter; received \"${typeof view}\".`);\n\t\t}\n\t\treturn view as Uint8Array;\n\t}\n}\n","import type { vec3, vec4 } from '../constants.js';\n\n/**\n * *Common utilities for working with colors in vec3, vec4, or hexadecimal form.*\n *\n * Provides methods to convert linear components (vec3, vec4) to sRGB hex values. All colors in\n * the glTF specification, excluding color textures, are linear. Hexadecimal values, in sRGB\n * colorspace, are accessible through helper functions in the API as a convenience.\n *\n * ```typescript\n * // Hex (sRGB) to factor (linear).\n * const factor = ColorUtils.hexToFactor(0xFFCCCC, []);\n *\n * // Factor (linear) to hex (sRGB).\n * const hex = ColorUtils.factorToHex([1, .25, .25])\n * ```\n *\n * @category Utilities\n */\nexport class ColorUtils {\n\t/**\n\t * Converts sRGB hexadecimal to linear components.\n\t * @typeParam T vec3 or vec4 linear components.\n\t */\n\tstatic hexToFactor<T = vec3 | vec4>(hex: number, target: T): T {\n\t\thex = Math.floor(hex);\n\t\tconst _target = target as unknown as vec3;\n\t\t_target[0] = ((hex >> 16) & 255) / 255;\n\t\t_target[1] = ((hex >> 8) & 255) / 255;\n\t\t_target[2] = (hex & 255) / 255;\n\t\treturn this.convertSRGBToLinear<T>(target, target);\n\t}\n\n\t/**\n\t * Converts linear components to sRGB hexadecimal.\n\t * @typeParam T vec3 or vec4 linear components.\n\t */\n\tstatic factorToHex<T = vec3 | vec4>(factor: T): number {\n\t\tconst target = [...(factor as unknown as number[])] as unknown as T;\n\t\tconst [r, g, b] = this.convertLinearToSRGB(factor, target) as unknown as number[];\n\t\treturn ((r * 255) << 16) ^ ((g * 255) << 8) ^ ((b * 255) << 0);\n\t}\n\n\t/**\n\t * Converts sRGB components to linear components.\n\t * @typeParam T vec3 or vec4 linear components.\n\t */\n\tstatic convertSRGBToLinear<T = vec3 | vec4>(source: T, target: T): T {\n\t\tconst _source = source as unknown as vec3;\n\t\tconst _target = target as unknown as vec3;\n\t\tfor (let i = 0; i < 3; i++) {\n\t\t\t_target[i] =\n\t\t\t\t_source[i] < 0.04045\n\t\t\t\t\t? _source[i] * 0.0773993808\n\t\t\t\t\t: Math.pow(_source[i] * 0.9478672986 + 0.0521327014, 2.4);\n\t\t}\n\t\treturn target;\n\t}\n\n\t/**\n\t * Converts linear components to sRGB components.\n\t * @typeParam T vec3 or vec4 linear components.\n\t */\n\tstatic convertLinearToSRGB<T = vec3 | vec4>(source: T, target: T): T {\n\t\tconst _source = source as unknown as vec3;\n\t\tconst _target = target as unknown as vec3;\n\t\tfor (let i = 0; i < 3; i++) {\n\t\t\t_target[i] = _source[i] < 0.0031308 ? _source[i] * 12.92 : 1.055 * Math.pow(_source[i], 0.41666) - 0.055;\n\t\t}\n\t\treturn target;\n\t}\n}\n","import type { vec2 } from '../constants.js';\nimport { BufferUtils } from './buffer-utils.js';\n\n/** Implements support for an image format in the {@link ImageUtils} class. */\nexport interface ImageUtilsFormat {\n\tmatch(buffer: Uint8Array): boolean;\n\tgetSize(buffer: Uint8Array): vec2 | null;\n\tgetChannels(buffer: Uint8Array): number | null;\n\tgetVRAMByteLength?(buffer: Uint8Array): number | null;\n}\n\n/** JPEG image support. */\nclass JPEGImageUtils implements ImageUtilsFormat {\n\tmatch(array: Uint8Array): boolean {\n\t\treturn array.length >= 3 && array[0] === 255 && array[1] === 216 && array[2] === 255;\n\t}\n\tgetSize(array: Uint8Array): vec2 {\n\t\t// Skip 4 chars, they are for signature\n\t\tlet view = new DataView(array.buffer, array.byteOffset + 4);\n\n\t\tlet i: number, next: number;\n\t\twhile (view.byteLength) {\n\t\t\t// read length of the next block\n\t\t\ti = view.getUint16(0, false);\n\t\t\t// i = buffer.readUInt16BE(0);\n\n\t\t\t// ensure correct format\n\t\t\tvalidateJPEGBuffer(view, i);\n\n\t\t\t// 0xFFC0 is baseline standard(SOF)\n\t\t\t// 0xFFC1 is baseline optimized(SOF)\n\t\t\t// 0xFFC2 is progressive(SOF2)\n\t\t\tnext = view.getUint8(i + 1);\n\t\t\tif (next === 0xc0 || next === 0xc1 || next === 0xc2) {\n\t\t\t\treturn [view.getUint16(i + 7, false), view.getUint16(i + 5, false)];\n\t\t\t}\n\n\t\t\t// move to the next block\n\t\t\tview = new DataView(array.buffer, view.byteOffset + i + 2);\n\t\t}\n\n\t\tthrow new TypeError('Invalid JPG, no size found');\n\t}\n\n\tgetChannels(_buffer: Uint8Array): number {\n\t\treturn 3;\n\t}\n}\n\n/**\n * PNG image support.\n *\n * PNG signature: 'PNG\\r\\n\\x1a\\n'\n * PNG image header chunk name: 'IHDR'\n */\nclass PNGImageUtils implements ImageUtilsFormat {\n\t// Used to detect \"fried\" png's: http://www.jongware.com/pngdefry.html\n\tstatic PNG_FRIED_CHUNK_NAME = 'CgBI';\n\tmatch(array: Uint8Array): boolean {\n\t\treturn (\n\t\t\tarray.length >= 8 &&\n\t\t\tarray[0] === 0x89 &&\n\t\t\tarray[1] === 0x50 &&\n\t\t\tarray[2] === 0x4e &&\n\t\t\tarray[3] === 0x47 &&\n\t\t\tarray[4] === 0x0d &&\n\t\t\tarray[5] === 0x0a &&\n\t\t\tarray[6] === 0x1a &&\n\t\t\tarray[7] === 0x0a\n\t\t);\n\t}\n\tgetSize(array: Uint8Array): vec2 {\n\t\tconst view = new DataView(array.buffer, array.byteOffset);\n\t\tconst magic = BufferUtils.decodeText(array.slice(12, 16));\n\t\tif (magic === PNGImageUtils.PNG_FRIED_CHUNK_NAME) {\n\t\t\treturn [view.getUint32(32, false), view.getUint32(36, false)];\n\t\t}\n\t\treturn [view.getUint32(16, false), view.getUint32(20, false)];\n\t}\n\tgetChannels(_buffer: Uint8Array): number {\n\t\treturn 4;\n\t}\n}\n\n/**\n * *Common utilities for working with image data.*\n *\n * @category Utilities\n */\nexport class ImageUtils {\n\tstatic impls: Record<string, ImageUtilsFormat> = {\n\t\t'image/jpeg': new JPEGImageUtils(),\n\t\t'image/png': new PNGImageUtils(),\n\t};\n\n\t/** Registers support for a new image format; useful for certain extensions. */\n\tpublic static registerFormat(mimeType: string, impl: ImageUtilsFormat): void {\n\t\tthis.impls[mimeType] = impl;\n\t}\n\n\t/**\n\t * Returns detected MIME type of the given image buffer. Note that for image\n\t * formats with support provided by extensions, the extension must be\n\t * registered with an I/O class before it can be detected by ImageUtils.\n\t */\n\tpublic static getMimeType(buffer: Uint8Array): string | null {\n\t\tfor (const mimeType in this.impls) {\n\t\t\tif (this.impls[mimeType].match(buffer)) {\n\t\t\t\treturn mimeType;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\t/** Returns the dimensions of the image. */\n\tpublic static getSize(buffer: Uint8Array, mimeType: string): vec2 | null {\n\t\tif (!this.impls[mimeType]) return null;\n\t\treturn this.impls[mimeType].getSize(buffer);\n\t}\n\n\t/**\n\t * Returns a conservative estimate of the number of channels in the image. For some image\n\t * formats, the method may return 4 indicating the possibility of an alpha channel, without\n\t * the ability to guarantee that an alpha channel is present.\n\t */\n\tpublic static getChannels(buffer: Uint8Array, mimeType: string): number | null {\n\t\tif (!this.impls[mimeType]) return null;\n\t\treturn this.impls[mimeType].getChannels(buffer);\n\t}\n\n\t/** Returns a conservative estimate of the GPU memory required by this image. */\n\tpublic static getVRAMByteLength(buffer: Uint8Array, mimeType: string): number | null {\n\t\tif (!this.impls[mimeType]) return null;\n\n\t\tif (this.impls[mimeType].getVRAMByteLength) {\n\t\t\treturn this.impls[mimeType].getVRAMByteLength!(buffer);\n\t\t}\n\n\t\tlet uncompressedBytes = 0;\n\t\tconst channels = 4; // See https://github.com/donmccurdy/glTF-Transform/issues/151.\n\t\tconst resolution = this.getSize(buffer, mimeType);\n\t\tif (!resolution) return null;\n\n\t\twhile (resolution[0] > 1 || resolution[1] > 1) {\n\t\t\tuncompressedBytes += resolution[0] * resolution[1] * channels;\n\t\t\tresolution[0] = Math.max(Math.floor(resolution[0] / 2), 1);\n\t\t\tresolution[1] = Math.max(Math.floor(resolution[1] / 2), 1);\n\t\t}\n\t\tuncompressedBytes += 1 * 1 * channels;\n\t\treturn uncompressedBytes;\n\t}\n\n\t/** Returns the preferred file extension for the given MIME type. */\n\tpublic static mimeTypeToExtension(mimeType: string): string {\n\t\tif (mimeType === 'image/jpeg') return 'jpg';\n\t\treturn mimeType.split('/').pop()!;\n\t}\n\n\t/** Returns the MIME type for the given file extension. */\n\tpublic static extensionToMimeType(extension: string): string {\n\t\tif (extension === 'jpg') return 'image/jpeg';\n\t\tif (!extension) return '';\n\t\treturn `image/${extension}`;\n\t}\n}\n\nfunction validateJPEGBuffer(view: DataView, i: number): DataView {\n\t// index should be within buffer limits\n\tif (i > view.byteLength) {\n\t\tthrow new TypeError('Corrupt JPG, exceeded buffer limits');\n\t}\n\t// Every JPEG block must begin with a 0xFF\n\tif (view.getUint8(i) !== 0xff) {\n\t\tthrow new TypeError('Invalid JPG, marker table corrupted');\n\t}\n\n\treturn view;\n}\n","import { ImageUtils } from './image-utils.js';\n\n/**\n * *Utility class for working with file systems and URI paths.*\n *\n * @category Utilities\n */\nexport class FileUtils {\n\t/**\n\t * Extracts the basename from a file path, e.g. \"folder/model.glb\" -> \"model\".\n\t * See: {@link HTTPUtils.basename}\n\t */\n\tstatic basename(uri: string): string {\n\t\tconst fileName = uri.split(/[\\\\/]/).pop()!;\n\t\treturn fileName.substring(0, fileName.lastIndexOf('.'));\n\t}\n\n\t/**\n\t * Extracts the extension from a file path, e.g. \"folder/model.glb\" -> \"glb\".\n\t * See: {@link HTTPUtils.extension}\n\t */\n\tstatic extension(uri: string): string {\n\t\tif (uri.startsWith('data:image/')) {\n\t\t\tconst mimeType = uri.match(/data:(image\\/\\w+)/)![1];\n\t\t\treturn ImageUtils.mimeTypeToExtension(mimeType);\n\t\t} else if (uri.startsWith('data:model/gltf+json')) {\n\t\t\treturn 'gltf';\n\t\t} else if (uri.startsWith('data:model/gltf-binary')) {\n\t\t\treturn 'glb';\n\t\t} else if (uri.startsWith('data:application/')) {\n\t\t\treturn 'bin';\n\t\t}\n\t\treturn uri.split(/[\\\\/]/).pop()!.split(/[.]/).pop()!;\n\t}\n}\n","/**\n * Common utilities\n * @module glMatrix\n */\n// Configuration Constants\nexport var EPSILON = 0.000001;\nexport var ARRAY_TYPE = typeof Float32Array !== 'undefined' ? Float32Array : Array;\nexport var RANDOM = Math.random;\n/**\n * Sets the type of array used when creating new vectors and matrices\n *\n * @param {Float32ArrayConstructor | ArrayConstructor} type Array type, such as Float32Array or Array\n */\n\nexport function setMatrixArrayType(type) {\n ARRAY_TYPE = type;\n}\nvar degree = Math.PI / 180;\n/**\n * Convert Degree To Radian\n *\n * @param {Number} a Angle in Degrees\n */\n\nexport function toRadian(a) {\n return a * degree;\n}\n/**\n * Tests whether or not the arguments have approximately the same value, within an absolute\n * or relative tolerance of glMatrix.EPSILON (an absolute tolerance is used for values less\n * than or equal to 1.0, and a relative tolerance is used for larger values)\n *\n * @param {Number} a The first number to test.\n * @param {Number} b The second number to test.\n * @returns {Boolean} True if the numbers are approximately equal, false otherwise.\n */\n\nexport function equals(a, b) {\n return Math.abs(a - b) <= EPSILON * Math.max(1.0, Math.abs(a), Math.abs(b));\n}\nif (!Math.hypot) Math.hypot = function () {\n var y = 0,\n i = arguments.length;\n\n while (i--) {\n y += arguments[i] * arguments[i];\n }\n\n return Math.sqrt(y);\n};","import * as glMatrix from \"./common.js\";\n/**\n * 3 Dimensional Vector\n * @module vec3\n */\n\n/**\n * Creates a new, empty vec3\n *\n * @returns {vec3} a new 3D vector\n */\n\nexport function create() {\n var out = new glMatrix.ARRAY_TYPE(3);\n\n if (glMatrix.ARRAY_TYPE != Float32Array) {\n out[0] = 0;\n out[1] = 0;\n out[2] = 0;\n }\n\n return out;\n}\n/**\n * Creates a new vec3 initialized with values from an existing vector\n *\n * @param {ReadonlyVec3} a vector to clone\n * @returns {vec3} a new 3D vector\n */\n\nexport function clone(a) {\n var out = new glMatrix.ARRAY_TYPE(3);\n out[0] = a[0];\n out[1] = a[1];\n out[2] = a[2];\n return out;\n}\n/**\n * Calculates the length of a vec3\n *\n * @param {ReadonlyVec3} a vector to calculate length of\n * @returns {Number} length of a\n */\n\nexport function length(a) {\n var x = a[0];\n var y = a[1];\n var z = a[2];\n return Math.hypot(x, y, z);\n}\n/**\n * Creates a new vec3 initialized with the given values\n *\n * @param {Number} x X component\n * @param {Number} y Y component\n * @param {Number} z Z component\n * @returns {vec3} a new 3D vector\n */\n\nexport function fromValues(x, y, z) {\n var out = new glMatrix.ARRAY_TYPE(3);\n out[0] = x;\n out[1] = y;\n out[2] = z;\n return out;\n}\n/**\n * Copy the values from one vec3 to another\n *\n * @param {vec3} out the receiving vector\n * @param {ReadonlyVec3} a the source vector\n * @returns {vec3} out\n */\n\nexport function copy(out, a) {\n out[0] = a[0];\n out[1] = a[1];\n out[2] = a[2];\n return out;\n}\n/**\n * Set the components of a vec3 to the given values\n *\n * @param {vec3} out the receiving vector\n * @param {Number} x X component\n * @param {Number} y Y component\n * @param {Number} z Z component\n * @returns {vec3} out\n */\n\nexport function set(out, x, y, z) {\n out[0] = x;\n out[1] = y;\n out[2] = z;\n return out;\n}\n/**\n * Adds two vec3's\n *\n * @param {vec3} out the receiving vector\n * @param {ReadonlyVec3} a the first operand\n * @param {ReadonlyVec3} b the second operand\n * @returns {vec3} out\n */\n\nexport function add(out, a, b) {\n out[0] = a[0] + b[0];\n out[1] = a[1] + b[1];\n out[2] = a[2] + b[2];\n return out;\n}\n/**\n * Subtracts vector b from vector a\n *\n * @param {vec3} out the receiving vector\n * @param {ReadonlyVec3} a the first operand\n * @param {ReadonlyVec3} b the second operand\n * @returns {vec3} out\n */\n\nexport function subtract(out, a, b) {\n out[0] = a[0] - b[0];\n out[1] = a[1] - b[1];\n out[2] = a[2] - b[2];\n return out;\n}\n/**\n * Multiplies two vec3's\n *\n * @param {vec3} out the receiving vector\n * @param {ReadonlyVec3} a the first operand\n * @param {ReadonlyVec3} b the second operand\n * @returns {vec3} out\n */\n\nexport function multiply(out, a, b) {\n out[0] = a[0] * b[0];\n out[1] = a[1] * b[1];\n out[2] = a[2] * b[2];\n return out;\n}\n/**\n * Divides two vec3's\n *\n * @param {vec3} out the receiving vector\n * @param {ReadonlyVec3} a the first operand\n * @param {ReadonlyVec3} b the second operand\n * @returns {vec3} out\n */\n\nexport function divide(out, a, b) {\n out[0] = a[0] / b[0];\n out[1] = a[1] / b[1];\n out[2] = a[2] / b[2];\n return out;\n}\n/**\n * Math.ceil the components of a vec3\n *\n * @param {vec3} out the receiving vector\n * @param {ReadonlyVec3} a vector to ceil\n * @returns {vec3} out\n */\n\nexport function ceil(out, a) {\n out[0] = Math.ceil(a[0]);\n out[1] = Math.ceil(a[1]);\n out[2] = Math.ceil(a[2]);\n return out;\n}\n/**\n * Math.floor the components of a vec3\n *\n * @param {vec3} out the receiving vector\n * @param {ReadonlyVec3} a vector to floor\n * @returns {vec3} out\n */\n\nexport function floor(out, a) {\n out[0] = Math.floor(a[0]);\n out[1] = Math.floor(a[1]);\n out[2] = Math.floor(a[2]);\n return out;\n}\n/**\n * Returns the minimum of two vec3's\n *\n * @param {vec3} out the receiving vector\n * @param {ReadonlyVec3} a the first operand\n * @param {ReadonlyVec3} b the second operand\n * @returns {vec3} out\n */\n\nexport function min(out, a, b) {\n out[0] = Math.min(a[0], b[0]);\n out[1] = Math.min(a[1], b[1]);\n out[2] = Math.min(a[2], b[2]);\n return out;\n}\n/**\n * Returns the maximum of two vec3's\n *\n * @param {vec3} out the receiving vector\n * @param {ReadonlyVec3} a the first operand\n * @param {ReadonlyVec3} b the second operand\n * @returns {vec3} out\n */\n\nexport function max(out, a, b) {\n out[0] = Math.max(a[0], b[0]);\n out[1] = Math.max(a[1], b[1]);\n out[2] = Math.max(a[2], b[2]);\n return out;\n}\n/**\n * Math.round the components of a vec3\n *\n * @param {vec3} out the receiving vector\n * @param {ReadonlyVec3} a vector to round\n * @returns {vec3} out\n */\n\nexport function round(out, a) {\n out[0] = Math.round(a[0]);\n out[1] = Math.round(a[1]);\n out[2] = Math.round(a[2]);\n return out;\n}\n/**\n * Scales a vec3 by a scalar number\n *\n * @param {vec3} out the receiving vector\n * @param {ReadonlyVec3} a the vector to scale\n * @param {Number} b amount to scale the vector by\n * @returns {vec3} out\n */\n\nexport function scale(out, a, b) {\n out[0] = a[0] * b;\n out[1] = a[1] * b;\n out[2] = a[2] * b;\n return out;\n}\n/**\n * Adds two vec3's after scaling the second operand by a scalar value\n *\n * @param {vec3} out the receiving vector\n * @param {ReadonlyVec3} a the first operand\n * @param {ReadonlyVec3} b the second operand\n * @param {Number} scale the amount to scale b by before adding\n * @returns {vec3} out\n */\n\nexport function scaleAndAdd(out, a, b, scale) {\n out[0] = a[0] + b[0] * scale;\n out[1] = a[1] + b[1] * scale;\n out[2] = a[2] + b[2] * scale;\n return out;\n}\n/**\n * Calculates the euclidian distance between two vec3's\n *\n * @param {ReadonlyVec3} a the first operand\n * @param {ReadonlyVec3} b the second operand\n * @returns {Number} distance between a and b\n */\n\nexport function distance(a, b) {\n var x = b[0] - a[0];\n var y = b[1] - a[1];\n var z = b[2] - a[2];\n return Math.hypot(x, y, z);\n}\n/**\n * Calculates the squared euclidian distance between two vec3's\n *\n * @param {ReadonlyVec3} a the first operand\n * @param {ReadonlyVec3} b the second operand\n * @returns {Number} squared distance between a and b\n */\n\nexport function squaredDistance(a, b) {\n var x = b[0] - a[0];\n var y = b[1] - a[1];\n var z = b[2] - a[2];\n return x * x + y * y + z * z;\n}\n/**\n * Calculates the squared length of a vec3\n *\n * @param {ReadonlyVec3} a vector to calculate squared length of\n * @returns {Number} squared length of a\n */\n\nexport function squaredLength(a) {\n var x = a[0];\n var y = a[1];\n var z = a[2];\n return x * x + y * y + z * z;\n}\n/**\n * Negates the components of a vec3\n *\n * @param {vec3} out the receiving vector\n * @param {ReadonlyVec3} a vector to negate\n * @returns {vec3} out\n */\n\nexport function negate(out, a) {\n out[0] = -a[0];\n out[1] = -a[1];\n out[2] = -a[2];\n return out;\n}\n/**\n * Returns the inverse of the components of a vec3\n *\n * @param {vec3} out the receiving vector\n * @param {ReadonlyVec3} a vector to invert\n * @returns {vec3} out\n */\n\nexport function inverse(out, a) {\n out[0] = 1.0 / a[0];\n out[1] = 1.0 / a[1];\n out[2] = 1.0 / a[2];\n return out;\n}\n/**\n * Normalize a vec3\n *\n * @param {vec3} out the receiving vector\n * @param {ReadonlyVec3} a vector to normalize\n * @returns {vec3} out\n */\n\nexport function normalize(out, a) {\n var x = a[0];\n var y = a[1];\n var z = a[2];\n var len = x * x + y * y + z * z;\n\n if (len > 0) {\n //TODO: evaluate use of glm_invsqrt here?\n len = 1 / Math.sqrt(len);\n }\n\n out[0] = a[0] * len;\n out[1] = a[1] * len;\n out[2] = a[2] * len;\n return out;\n}\n/**\n * Calculates the dot product of two vec3's\n *\n * @param {ReadonlyVec3} a the first operand\n * @param {ReadonlyVec3} b the second operand\n * @returns {Number} dot product of a and b\n */\n\nexport function dot(a, b) {\n return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];\n}\n/**\n * Computes the cross product of two vec3's\n *\n * @param {vec3} out the receiving vector\n * @param {ReadonlyVec3} a the first operand\n * @param {ReadonlyVec3} b the second operand\n * @returns {vec3} out\n */\n\nexport function cross(out, a, b) {\n var ax = a[0],\n ay = a[1],\n az = a[2];\n var bx = b[0],\n by = b[1],\n bz = b[2];\n out[0] = ay * bz - az * by;\n out[1] = az * bx - ax * bz;\n out[2] = ax * by - ay * bx;\n return out;\n}\n/**\n * Performs a linear interpolation between two vec3's\n *\n * @param {vec3} out the receiving vector\n * @param {ReadonlyVec3} a the first operand\n * @param {ReadonlyVec3} b the second operand\n * @param {Number} t interpolation amount, in the range [0-1], between the two inputs\n * @returns {vec3} out\n */\n\nexport function lerp(out, a, b, t) {\n var ax = a[0];\n var ay = a[1];\n var az = a[2];\n out[0] = ax + t * (b[0] - ax);\n out[1] = ay + t * (b[1] - ay);\n out[2] = az + t * (b[2] - az);\n return out;\n}\n/**\n * Performs a hermite interpolation with two control points\n *\n * @param {vec3} out the receiving vector\n * @param {ReadonlyVec3} a the first operand\n * @param {ReadonlyVec3} b the second operand\n * @param {ReadonlyVec3} c the third operand\n * @param {ReadonlyVec3} d the fourth operand\n * @param {Number} t interpolation amount, in the range [0-1], between the two inputs\n * @returns {vec3} out\n */\n\nexport function hermite(out, a, b, c, d, t) {\n var factorTimes2 = t * t;\n var factor1 = factorTimes2 * (2 * t - 3) + 1;\n var factor2 = factorTimes2 * (t - 2) + t;\n var factor3 = factorTimes2 * (t - 1);\n var factor4 = factorTimes2 * (3 - 2 * t);\n out[0] = a[0] * factor1 + b[0] * factor2 + c[0] * factor3 + d[0] * factor4;\n out[1] = a[1] * factor1 + b[1] * factor2 + c[1] * factor3 + d[1] * factor4;\n out[2] = a[2] * factor1 + b[2] * factor2 + c[2] * factor3 + d[2] * factor4;\n return out;\n}\n/**\n * Performs a bezier interpolation with two control points\n *\n * @param {vec3} out the receiving vector\n * @param {ReadonlyVec3} a the first operand\n * @param {ReadonlyVec3} b the second operand\n * @param {ReadonlyVec3} c the third operand\n * @param {ReadonlyVec3} d the fourth operand\n * @param {Number} t interpolation amount, in the range [0-1], between the two inputs\n * @returns {vec3} out\n */\n\nexport function bezier(out, a, b, c, d, t) {\n var inverseFactor = 1 - t;\n var inverseFactorTimesTwo = inverseFactor * inverseFactor;\n var factorTimes2 = t * t;\n var factor1 = inverseFactorTimesTwo * inverseFactor;\n var factor2 = 3 * t * inverseFactorTimesTwo;\n var factor3 = 3 * factorTimes2 * inverseFactor;\n var factor4 = factorTimes2 * t;\n out[0] = a[0] * factor1 + b[0] * factor2 + c[0] * factor3 + d[0] * factor4;\n out[1] = a[1] * factor1 + b[1] * factor2 + c[1] * factor3 + d[1] * factor4;\n out[2] = a[2] * factor1 + b[2] * factor2 + c[2] * factor3 + d[2] * factor4;\n return out;\n}\n/**\n * Generates a random vector with the given scale\n *\n * @param {vec3} out the receiving vector\n * @param {Number} [scale] Length of the resulting vector. If ommitted, a unit vector will be returned\n * @returns {vec3} out\n */\n\nexport function random(out, scale) {\n scale = scale || 1.0;\n var r = glMatrix.RANDOM() * 2.0 * Math.PI;\n var z = glMatrix.RANDOM() * 2.0 - 1.0;\n var zScale = Math.sqrt(1.0 - z * z) * scale;\n out[0] = Math.cos(r) * zScale;\n out[1] = Math.sin(r) * zScale;\n out[2] = z * scale;\n return out;\n}\n/**\n * Transforms the vec3 with a mat4.\n * 4th vector component is implicitly '1'\n *\n * @param {vec3} out the receiving vector\n * @param {ReadonlyVec3} a the vector to transform\n * @param {ReadonlyMat4} m matrix to transform with\n * @returns {vec3} out\n */\n\nexport function transformMat4(out, a, m) {\n var x = a[0],\n y = a[1],\n z = a[2];\n var w = m[3] * x + m[7] * y + m[11] * z + m[15];\n w = w || 1.0;\n out[0] = (m[0] * x + m[4] * y + m[8] * z + m[12]) / w;\n out[1] = (m[1] * x + m[5] * y + m[9] * z + m[13]) / w;\n out[2] = (m[2] * x + m[6] * y + m[10] * z + m[14]) / w;\n return out;\n}\n/**\n * Transforms the vec3 with a mat3.\n *\n * @param {vec3} out the receiving vector\n * @param {ReadonlyVec3} a the vector to transform\n * @param {ReadonlyMat3} m the 3x3 matrix to transform with\n * @returns {vec3} out\n */\n\nexport function transformMat3(out, a, m) {\n var x = a[0],\n y = a[1],\n z = a[2];\n out[0] = x * m[0] + y * m[3] + z * m[6];\n out[1] = x * m[1] + y * m[4] + z * m[7];\n out[2] = x * m[2] + y * m[5] + z * m[8];\n return out;\n}\n/**\n * Transforms the vec3 with a quat\n * Can also be used for dual quaternions. (Multiply it with the real part)\n *\n * @param {vec3} out the receiving vector\n * @param {ReadonlyVec3} a the vector to transform\n * @param {ReadonlyQuat} q quaternion to transform with\n * @returns {vec3} out\n */\n\nexport function transformQuat(out, a, q) {\n // benchmarks: https://jsperf.com/quaternion-transform-vec3-implementations-fixed\n var qx = q[0],\n qy = q[1],\n qz = q[2],\n qw = q[3];\n var x = a[0],\n y = a[1],\n z = a[2]; // var qvec = [qx, qy, qz];\n // var uv = vec3.cross([], qvec, a);\n\n var uvx = qy * z - qz * y,\n uvy = qz * x - qx * z,\n uvz = qx * y - qy * x; // var uuv = vec3.cross([], qvec, uv);\n\n var uuvx = qy * uvz - qz * uvy,\n uuvy = qz * uvx - qx * uvz,\n uuvz = qx * uvy - qy * uvx; // vec3.scale(uv, uv, 2 * w);\n\n var w2 = qw * 2;\n uvx *= w2;\n uvy *= w2;\n uvz *= w2; // vec3.scale(uuv, uuv, 2);\n\n uuvx *= 2;\n uuvy *= 2;\n uuvz *= 2; // return vec3.add(out, a, vec3.add(out, uv, uuv));\n\n out[0] = x + uvx + uuvx;\n out[1] = y + uvy + uuvy;\n out[2] = z + uvz + uuvz;\n return out;\n}\n/**\n * Rotate a 3D vector around the x-axis\n * @param {vec3} out The receiving vec3\n * @param {ReadonlyVec3} a The vec3 point to rotate\n * @param {ReadonlyVec3} b The origin of the rotation\n * @param {Number} rad The angle of rotation in radians\n * @returns {vec3} out\n */\n\nexport function rotateX(out, a, b, rad) {\n var p = [],\n r = []; //Translate point to the origin\n\n p[0] = a[0] - b[0];\n p[1] = a[1] - b[1];\n p[2] = a[2] - b[2]; //perform rotation\n\n r[0] = p[0];\n r[1] = p[1] * Math.cos(rad) - p[2] * Math.sin(rad);\n r[2] = p[1] * Math.sin(rad) + p[2] * Math.cos(rad); //translate to correct position\n\n out[0] = r[0] + b[0];\n out[1] = r[1] + b[1];\n out[2] = r[2] + b[2];\n return out;\n}\n/**\n * Rotate a 3D vector around the y-axis\n * @param {vec3} out The receiving vec3\n * @param {ReadonlyVec3} a The vec3 point to rotate\n * @param {ReadonlyVec3} b The origin of the rotation\n * @param {Number} rad The angle of rotation in radians\n * @returns {vec3} out\n */\n\nexport function rotateY(out, a, b, rad) {\n var p = [],\n r = []; //Translate point to the origin\n\n p[0] = a[0] - b[0];\n p[1] = a[1] - b[1];\n p[2] = a[2] - b[2]; //perform rotation\n\n r[0] = p[2] * Math.sin(rad) + p[0] * Math.cos(rad);\n r[1] = p[1];\n r[2] = p[2] * Math.cos(rad) - p[0] * Math.sin(rad); //translate to correct position\n\n out[0] = r[0] + b[0];\n out[1] = r[1] + b[1];\n out[2] = r[2] + b[2];\n return out;\n}\n/**\n * Rotate a 3D vector around the z-axis\n * @param {vec3} out The receiving vec3\n * @param {ReadonlyVec3} a The vec3 point to rotate\n * @param {ReadonlyVec3} b The origin of the rotation\n * @param {Number} rad The angle of rotation in radians\n * @returns {vec3} out\n */\n\nexport function rotateZ(out, a, b, rad) {\n var p = [],\n r = []; //Translate point to the origin\n\n p[0] = a[0] - b[0];\n p[1] = a[1] - b[1];\n p[2] = a[2] - b[2]; //perform rotation\n\n r[0] = p[0] * Math.cos(rad) - p[1] * Math.sin(rad);\n r[1] = p[0] * Math.sin(rad) + p[1] * Math.cos(rad);\n r[2] = p[2]; //translate to correct position\n\n out[0] = r[0] + b[0];\n out[1] = r[1] + b[1];\n out[2] = r[2] + b[2];\n return out;\n}\n/**\n * Get the angle between two 3D vectors\n * @param {ReadonlyVec3} a The first operand\n * @param {ReadonlyVec3} b The second operand\n * @returns {Number} The angle in radians\n */\n\nexport function angle(a, b) {\n var ax = a[0],\n ay = a[1],\n az = a[2],\n bx = b[0],\n by = b[1],\n bz = b[2],\n mag1 = Math.sqrt(ax * ax + ay * ay + az * az),\n mag2 = Math.sqrt(bx * bx + by * by + bz * bz),\n mag = mag1 * mag2,\n cosine = mag && dot(a, b) / mag;\n return Math.acos(Math.min(Math.max(cosine, -1), 1));\n}\n/**\n * Set the components of a vec3 to zero\n *\n * @param {vec3} out the receiving vector\n * @returns {vec3} out\n */\n\nexport function zero(out) {\n out[0] = 0.0;\n out[1] = 0.0;\n out[2] = 0.0;\n return out;\n}\n/**\n * Returns a string representation of a vector\n *\n * @param {ReadonlyVec3} a vector to represent as a string\n * @returns {String} string representation of the vector\n */\n\nexport function str(a) {\n return \"vec3(\" + a[0] + \", \" + a[1] + \", \" + a[2] + \")\";\n}\n/**\n * Returns whether or not the vectors have exactly the same elements in the same position (when compared with ===)\n *\n * @param {ReadonlyVec3} a The first vector.\n * @param {ReadonlyVec3} b The second vector.\n * @returns {Boolean} True if the vectors are equal, false otherwise.\n */\n\nexport function exactEquals(a, b) {\n return a[0] === b[0] && a[1] === b[1] && a[2] === b[2];\n}\n/**\n * Returns whether or not the vectors have approximately the same elements in the same position.\n *\n * @param {ReadonlyVec3} a The first vector.\n * @param {ReadonlyVec3} b The second vector.\n * @returns {Boolean} True if the vectors are equal, false otherwise.\n */\n\nexport function equals(a, b) {\n var a0 = a[0],\n a1 = a[1],\n a2 = a[2];\n var b0 = b[0],\n b1 = b[1],\n b2 = b[2];\n return Math.abs(a0 - b0) <= glMatrix.EPSILON * Math.max(1.0, Math.abs(a0), Math.abs(b0)) && Math.abs(a1 - b1) <= glMatrix.EPSILON * Math.max(1.0, Math.abs(a1), Math.abs(b1)) && Math.abs(a2 - b2) <= glMatrix.EPSILON * Math.max(1.0, Math.abs(a2), Math.abs(b2));\n}\n/**\n * Alias for {@link vec3.subtract}\n * @function\n */\n\nexport var sub = subtract;\n/**\n * Alias for {@link vec3.multiply}\n * @function\n */\n\nexport var mul = multiply;\n/**\n * Alias for {@link vec3.divide}\n * @function\n */\n\nexport var div = divide;\n/**\n * Alias for {@link vec3.distance}\n * @function\n */\n\nexport var dist = distance;\n/**\n * Alias for {@link vec3.squaredDistance}\n * @function\n */\n\nexport var sqrDist = squaredDistance;\n/**\n * Alias for {@link vec3.length}\n * @function\n */\n\nexport var len = length;\n/**\n * Alias for {@link vec3.squaredLength}\n * @function\n */\n\nexport var sqrLen = squaredLength;\n/**\n * Perform some operation over an array of vec3s.\n *\n * @param {Array} a the array of vectors to iterate over\n * @param {Number} stride Number of elements between the start of each vec3. If 0 assumes tightly packed\n * @param {Number} offset Number of elements to skip at the beginning of the array\n * @param {Number} count Number of vec3s to iterate over. If 0 iterates over entire array\n * @param {Function} fn Function to call for each vector in the array\n * @param {Object} [arg] additional argument to pass to fn\n * @returns {Array} a\n * @function\n */\n\nexport var forEach = function () {\n var vec = create();\n return function (a, stride, offset, count, fn, arg) {\n var i, l;\n\n if (!stride) {\n stride = 3;\n }\n\n if (!offset) {\n offset = 0;\n }\n\n if (count) {\n l = Math.min(count * stride + offset, a.length);\n } else {\n l = a.length;\n }\n\n for (i = offset; i < l; i += stride) {\n vec[0] = a[i];\n vec[1] = a[i + 1];\n vec[2] = a[i + 2];\n fn(vec, vec, arg);\n a[i] = vec[0];\n a[i + 1] = vec[1];\n a[i + 2] = vec[2];\n }\n\n return a;\n };\n}();","import { transformMat4 } from 'gl-matrix/vec3';\nimport { type bbox, type mat4, PropertyType, type vec3 } from '../constants.js';\nimport type { Mesh, Node, Scene } from '../properties/index.js';\n\n/** @hidden Implemented in /core for use by /extensions, publicly exported from /functions. */\nexport function getBounds(node: Node | Scene): bbox {\n\tconst resultBounds = createBounds();\n\tconst parents = node.propertyType === PropertyType.NODE ? [node] : node.listChildren();\n\n\tfor (const parent of parents) {\n\t\tparent.traverse((node) => {\n\t\t\tconst mesh = node.getMesh();\n\t\t\tif (!mesh) return;\n\n\t\t\t// Compute mesh bounds and update result.\n\t\t\tconst meshBounds = getMeshBounds(mesh, node.getWorldMatrix());\n\t\t\tif (meshBounds.min.every(isFinite) && meshBounds.max.every(isFinite)) {\n\t\t\t\texpandBounds(meshBounds.min, resultBounds);\n\t\t\t\texpandBounds(meshBounds.max, resultBounds);\n\t\t\t}\n\t\t});\n\t}\n\n\treturn resultBounds;\n}\n\n/** Computes mesh bounds in world space. */\nfunction getMeshBounds(mesh: Mesh, worldMatrix: mat4): bbox {\n\tconst meshBounds = createBounds();\n\n\t// We can't transform a local AABB into world space and still have a tight AABB in world space,\n\t// so we need to compute the world AABB vertex by vertex here.\n\tfor (const prim of mesh.listPrimitives()) {\n\t\tconst position = prim.getAttribute('POSITION');\n\t\tconst indices = prim.getIndices();\n\t\tif (!position) continue;\n\n\t\tlet localPos: vec3 = [0, 0, 0];\n\t\tlet worldPos: vec3 = [0, 0, 0];\n\t\tfor (let i = 0, il = indices ? indices.getCount() : position.getCount(); i < il; i++) {\n\t\t\tconst index = indices ? indices.getScalar(i) : i;\n\t\t\tlocalPos = position.getElement(index, localPos) as vec3;\n\t\t\tworldPos = transformMat4(worldPos, localPos, worldMatrix) as vec3;\n\t\t\texpandBounds(worldPos, meshBounds);\n\t\t}\n\t}\n\n\treturn meshBounds;\n}\n\n/** Expands bounds of target by given source. */\nfunction expandBounds(point: vec3, target: bbox): void {\n\tfor (let i = 0; i < 3; i++) {\n\t\ttarget.min[i] = Math.min(point[i], target.min[i]);\n\t\ttarget.max[i] = Math.max(point[i], target.max[i]);\n\t}\n}\n\n/** Creates new bounds with min=Infinity, max=-Infinity. */\nfunction createBounds(): bbox {\n\treturn {\n\t\tmin: [Infinity, Infinity, Infinity] as vec3,\n\t\tmax: [-Infinity, -Infinity, -Infinity] as vec3,\n\t};\n}\n","import { FileUtils } from './file-utils.js';\n\n// Need a placeholder domain to construct a URL from a relative path. We only\n// access `url.pathname`, so the domain doesn't matter.\nconst NULL_DOMAIN = 'https://null.example';\n\n/**\n * *Utility class for working with URLs.*\n *\n * @category Utilities\n */\nexport class HTTPUtils {\n\tstatic readonly DEFAULT_INIT: RequestInit = {};\n\tstatic readonly PROTOCOL_REGEXP: RegExp = /^[a-zA-Z]+:\\/\\//;\n\n\tstatic dirname(path: string): string {\n\t\tconst index = path.lastIndexOf('/');\n\t\tif (index === -1) return './';\n\t\treturn path.substring(0, index + 1);\n\t}\n\n\t/**\n\t * Extracts the basename from a URL, e.g. \"folder/model.glb\" -> \"model\".\n\t * See: {@link FileUtils.basename}\n\t */\n\tstatic basename(uri: string): string {\n\t\treturn FileUtils.basename(new URL(uri, NULL_DOMAIN).pathname);\n\t}\n\n\t/**\n\t * Extracts the extension from a URL, e.g. \"folder/model.glb\" -> \"glb\".\n\t * See: {@link FileUtils.extension}\n\t */\n\tstatic extension(uri: string): string {\n\t\treturn FileUtils.extension(new URL(uri, NULL_DOMAIN).pathname);\n\t}\n\n\tstatic resolve(base: string, path: string): string {\n\t\tif (!this.isRelativePath(path)) return path;\n\n\t\tconst stack = base.split('/');\n\t\tconst parts = path.split('/');\n\t\tstack.pop();\n\t\tfor (let i = 0; i < parts.length; i++) {\n\t\t\tif (parts[i] === '.') continue;\n\t\t\tif (parts[i] === '..') {\n\t\t\t\tstack.pop();\n\t\t\t} else {\n\t\t\t\tstack.push(parts[i]);\n\t\t\t}\n\t\t}\n\t\treturn stack.join('/');\n\t}\n\n\t/**\n\t * Returns true for URLs containing a protocol, and false for both\n\t * absolute and relative paths.\n\t */\n\tstatic isAbsoluteURL(path: string): boolean {\n\t\treturn this.PROTOCOL_REGEXP.test(path);\n\t}\n\n\t/**\n\t * Returns true for paths that are declared relative to some unknown base\n\t * path. For example, \"foo/bar/\" is relative both \"/foo/bar/\" is not.\n\t */\n\tstatic isRelativePath(path: string): boolean {\n\t\treturn !/^(?:[a-zA-Z]+:)?\\//.test(path);\n\t}\n}\n","// Reference: https://github.com/jonschlinkert/is-plain-object\n\nfunction isObject(o: unknown): o is object {\n\treturn Object.prototype.toString.call(o) === '[object Object]';\n}\n\nexport function isPlainObject(o: unknown): o is object {\n\tif (isObject(o) === false) return false;\n\n\t// If has modified constructor\n\tconst ctor = o.constructor;\n\tif (ctor === undefined) return true;\n\n\t// If has modified prototype\n\tconst prot = ctor.prototype;\n\tif (isObject(prot) === false) return false;\n\n\t// If constructor does not have an Object-specific method\n\tif (Object.hasOwn(prot, 'isPrototypeOf') === false) {\n\t\treturn false;\n\t}\n\n\t// Most likely a plain Object\n\treturn true;\n}\n","/** Logger verbosity thresholds. */\nexport enum Verbosity {\n\t/** No events are logged. */\n\tSILENT = 4,\n\n\t/** Only error events are logged. */\n\tERROR = 3,\n\n\t/** Only error and warn events are logged. */\n\tWARN = 2,\n\n\t/** Only error, warn, and info events are logged. (DEFAULT) */\n\tINFO = 1,\n\n\t/** All events are logged. */\n\tDEBUG = 0,\n}\n\nexport interface ILogger {\n\tdebug(text: string): void;\n\tinfo(text: string): void;\n\twarn(text: string): void;\n\terror(text: string): void;\n}\n\n/**\n * *Logger utility class.*\n *\n * @category Utilities\n */\nexport class Logger implements ILogger {\n\t/** Logger verbosity thresholds. */\n\tstatic Verbosity: typeof Verbosity = Verbosity;\n\n\t/** Default logger instance. */\n\tpublic static DEFAULT_INSTANCE: Logger = new Logger(Logger.Verbosity.INFO);\n\n\t/** Constructs a new Logger instance. */\n\tconstructor(private readonly verbosity: number) {}\n\n\t/** Logs an event at level {@link Logger.Verbosity.DEBUG}. */\n\tdebug(text: string): void {\n\t\tif (this.verbosity <= Logger.Verbosity.DEBUG) {\n\t\t\tconsole.debug(text);\n\t\t}\n\t}\n\n\t/** Logs an event at level {@link Logger.Verbosity.INFO}. */\n\tinfo(text: string): void {\n\t\tif (this.verbosity <= Logger.Verbosity.INFO) {\n\t\t\tconsole.info(text);\n\t\t}\n\t}\n\n\t/** Logs an event at level {@link Logger.Verbosity.WARN}. */\n\twarn(text: string): void {\n\t\tif (this.verbosity <= Logger.Verbosity.WARN) {\n\t\t\tconsole.warn(text);\n\t\t}\n\t}\n\n\t/** Logs an event at level {@link Logger.Verbosity.ERROR}. */\n\terror(text: string): void {\n\t\tif (this.verbosity <= Logger.Verbosity.ERROR) {\n\t\t\tconsole.error(text);\n\t\t}\n\t}\n}\n","import * as glMatrix from \"./common.js\";\n/**\n * 4x4 Matrix<br>Format: column-major, when typed out it looks like row-major<br>The matrices are being post multiplied.\n * @module mat4\n */\n\n/**\n * Creates a new identity mat4\n *\n * @returns {mat4} a new 4x4 matrix\n */\n\nexport function create() {\n var out = new glMatrix.ARRAY_TYPE(16);\n\n if (glMatrix.ARRAY_TYPE != Float32Array) {\n out[1] = 0;\n out[2] = 0;\n out[3] = 0;\n out[4] = 0;\n out[6] = 0;\n out[7] = 0;\n out[8] = 0;\n out[9] = 0;\n out[11] = 0;\n out[12] = 0;\n out[13] = 0;\n out[14] = 0;\n }\n\n out[0] = 1;\n out[5] = 1;\n out[10] = 1;\n out[15] = 1;\n return out;\n}\n/**\n * Creates a new mat4 initialized with values from an existing matrix\n *\n * @param {ReadonlyMat4} a matrix to clone\n * @returns {mat4} a new 4x4 matrix\n */\n\nexport function clone(a) {\n var out = new glMatrix.ARRAY_TYPE(16);\n out[0] = a[0];\n out[1] = a[1];\n out[2] = a[2];\n out[3] = a[3];\n out[4] = a[4];\n out[5] = a[5];\n out[6] = a[6];\n out[7] = a[7];\n out[8] = a[8];\n out[9] = a[9];\n out[10] = a[10];\n out[11] = a[11];\n out[12] = a[12];\n out[13] = a[13];\n out[14] = a[14];\n out[15] = a[15];\n return out;\n}\n/**\n * Copy the values from one mat4 to another\n *\n * @param {mat4} out the receiving matrix\n * @param {ReadonlyMat4} a the source matrix\n * @returns {mat4} out\n */\n\nexport function copy(out, a) {\n out[0] = a[0];\n out[1] = a[1];\n out[2] = a[2];\n out[3] = a[3];\n out[4] = a[4];\n out[5] = a[5];\n out[6] = a[6];\n out[7] = a[7];\n out[8] = a[8];\n out[9] = a[9];\n out[10] = a[10];\n out[11] = a[11];\n out[12] = a[12];\n out[13] = a[13];\n out[14] = a[14];\n out[15] = a[15];\n return out;\n}\n/**\n * Create a new mat4 with the given values\n *\n * @param {Number} m00 Component in column 0, row 0 position (index 0)\n * @param {Number} m01 Component in column 0, row 1 position (index 1)\n * @param {Number} m02 Component in column 0, row 2 position (index 2)\n * @param {Number} m03 Component in column 0, row 3 position (index 3)\n * @param {Number} m10 Component in column 1, row 0 position (index 4)\n * @param {Number} m11 Component in column 1, row 1 position (index 5)\n * @param {Number} m12 Component in column 1, row 2 position (index 6)\n * @param {Number} m13 Component in column 1, row 3 position (index 7)\n * @param {Number} m20 Component in column 2, row 0 position (index 8)\n * @param {Number} m21 Component in column 2, row 1 position (index 9)\n * @param {Number} m22 Component in column 2, row 2 position (index 10)\n * @param {Number} m23 Component in column 2, row 3 position (index 11)\n * @param {Number} m30 Component in column 3, row 0 position (index 12)\n * @param {Number} m31 Component in colum