UNPKG

@playcanvas/splat-transform

Version:

CLI tool for 3D Gaussian splat format conversion and transformation

2,047 lines (2,022 loc) 5.98 MB
import { open, readFile as readFile$1, writeFile as writeFile$2, unlink, mkdir, rename, lstat } from 'node:fs/promises'; import { join, dirname, resolve as resolve$2, basename } from 'node:path'; import { hrtime, exit } from 'node:process'; import { parseArgs } from 'node:util'; import { globals, create } from 'webgpu'; import { Buffer as Buffer$1 } from 'node:buffer'; import { randomBytes } from 'crypto'; import os from 'node:os'; const TRACEID_GPU_TIMINGS = 'GpuTimings'; const version$1 = '2.14.3'; const revision = '26ac50c'; function extend(target, ex) { for(const prop in ex){ const copy = ex[prop]; if (Array.isArray(copy)) { target[prop] = extend([], copy); } else if (copy && typeof copy === 'object') { target[prop] = extend({}, copy); } else { target[prop] = copy; } } return target; } const guid = { create () { return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c)=>{ const r = Math.random() * 16 | 0; const v = c === 'x' ? r : r & 0x3 | 0x8; return v.toString(16); }); } }; const path = { delimiter: '/', join (...sections) { let result = sections[0]; for(let i = 0; i < sections.length - 1; i++){ const one = sections[i]; const two = sections[i + 1]; if (two[0] === path.delimiter) { result = two; continue; } if (one && two && one[one.length - 1] !== path.delimiter && two[0] !== path.delimiter) { result += path.delimiter + two; } else { result += two; } } return result; }, normalize (pathname) { const lead = pathname.startsWith(path.delimiter); const trail = pathname.endsWith(path.delimiter); const parts = pathname.split('/'); let result = ''; let cleaned = []; for(let i = 0; i < parts.length; i++){ if (parts[i] === '') continue; if (parts[i] === '.') continue; if (parts[i] === '..' && cleaned.length > 0) { cleaned = cleaned.slice(0, cleaned.length - 2); continue; } if (i > 0) cleaned.push(path.delimiter); cleaned.push(parts[i]); } result = cleaned.join(''); if (!lead && result[0] === path.delimiter) { result = result.slice(1); } if (trail && result[result.length - 1] !== path.delimiter) { result += path.delimiter; } return result; }, split (pathname) { const lastDelimiterIndex = pathname.lastIndexOf(path.delimiter); if (lastDelimiterIndex !== -1) { return [ pathname.substring(0, lastDelimiterIndex), pathname.substring(lastDelimiterIndex + 1) ]; } return [ '', pathname ]; }, getBasename (pathname) { return path.split(pathname)[1]; }, getDirectory (pathname) { return path.split(pathname)[0]; }, getExtension (pathname) { const ext = pathname.split('?')[0].split('.').pop(); if (ext !== pathname) { return `.${ext}`; } return ''; }, isRelativePath (pathname) { return pathname.charAt(0) !== '/' && pathname.match(/:\/\//) === null; }, extractPath (pathname) { let result = ''; const parts = pathname.split('/'); let i = 0; if (parts.length > 1) { if (path.isRelativePath(pathname)) { if (parts[0] === '.') { for(i = 0; i < parts.length - 1; ++i){ result += i === 0 ? parts[i] : `/${parts[i]}`; } } else if (parts[0] === '..') { for(i = 0; i < parts.length - 1; ++i){ result += i === 0 ? parts[i] : `/${parts[i]}`; } } else { result = '.'; for(i = 0; i < parts.length - 1; ++i){ result += `/${parts[i]}`; } } } else { for(i = 0; i < parts.length - 1; ++i){ result += i === 0 ? parts[i] : `/${parts[i]}`; } } } return result; } }; const detectPassiveEvents = ()=>{ let result = false; try { const opts = Object.defineProperty({}, 'passive', { get: function() { result = true; return false; } }); window.addEventListener('testpassive', null, opts); window.removeEventListener('testpassive', null, opts); } catch (e) {} return result; }; const ua = typeof navigator !== 'undefined' ? navigator.userAgent : ''; const environment = typeof window !== 'undefined' ? 'browser' : typeof global !== 'undefined' ? 'node' : 'worker'; const platformName = /android/i.test(ua) ? 'android' : /ip(?:[ao]d|hone)/i.test(ua) ? 'ios' : /windows/i.test(ua) ? 'windows' : /mac os/i.test(ua) ? 'osx' : /linux/i.test(ua) ? 'linux' : /cros/i.test(ua) ? 'cros' : null; detectPassiveEvents(); const platform = { name: platformName, environment: environment, browser: environment === 'browser', worker: environment === 'worker', desktop: [ 'windows', 'osx', 'linux', 'cros' ].includes(platformName), mobile: [ 'android', 'ios' ].includes(platformName), ios: platformName === 'ios', android: platformName === 'android'}; class EventHandle { constructor(handler, name, callback, scope, once = false){ this._removed = false; this.handler = handler; this.name = name; this.callback = callback; this.scope = scope; this._once = once; } off() { if (this._removed) return; this.handler.offByHandle(this); } on(name, callback, scope = this) { return this.handler._addCallback(name, callback, scope, false); } once(name, callback, scope = this) { return this.handler._addCallback(name, callback, scope, true); } set removed(value) { if (!value) return; this._removed = true; } get removed() { return this._removed; } toJSON(key) { return undefined; } } class EventHandler { initEventHandler() { this._callbacks = new Map(); this._callbackActive = new Map(); } _addCallback(name, callback, scope, once) { if (!this._callbacks.has(name)) { this._callbacks.set(name, []); } if (this._callbackActive.has(name)) { const callbackActive = this._callbackActive.get(name); if (callbackActive && callbackActive === this._callbacks.get(name)) { this._callbackActive.set(name, callbackActive.slice()); } } const evt = new EventHandle(this, name, callback, scope, once); this._callbacks.get(name).push(evt); return evt; } on(name, callback, scope = this) { return this._addCallback(name, callback, scope, false); } once(name, callback, scope = this) { return this._addCallback(name, callback, scope, true); } off(name, callback, scope) { if (name) { if (this._callbackActive.has(name) && this._callbackActive.get(name) === this._callbacks.get(name)) { this._callbackActive.set(name, this._callbackActive.get(name).slice()); } } else { for (const [key, callbacks] of this._callbackActive){ if (!this._callbacks.has(key)) { continue; } if (this._callbacks.get(key) !== callbacks) { continue; } this._callbackActive.set(key, callbacks.slice()); } } if (!name) { for (const callbacks of this._callbacks.values()){ for(let i = 0; i < callbacks.length; i++){ callbacks[i].removed = true; } } this._callbacks.clear(); } else if (!callback) { const callbacks = this._callbacks.get(name); if (callbacks) { for(let i = 0; i < callbacks.length; i++){ callbacks[i].removed = true; } this._callbacks.delete(name); } } else { const callbacks = this._callbacks.get(name); if (!callbacks) { return this; } for(let i = 0; i < callbacks.length; i++){ if (callbacks[i].callback !== callback) { continue; } if (scope && callbacks[i].scope !== scope) { continue; } callbacks[i].removed = true; callbacks.splice(i, 1); i--; } if (callbacks.length === 0) { this._callbacks.delete(name); } } return this; } offByHandle(handle) { const name = handle.name; handle.removed = true; if (this._callbackActive.has(name) && this._callbackActive.get(name) === this._callbacks.get(name)) { this._callbackActive.set(name, this._callbackActive.get(name).slice()); } const callbacks = this._callbacks.get(name); if (!callbacks) { return this; } const ind = callbacks.indexOf(handle); if (ind !== -1) { callbacks.splice(ind, 1); if (callbacks.length === 0) { this._callbacks.delete(name); } } return this; } fire(name, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) { if (!name) { return this; } const callbacksInitial = this._callbacks.get(name); if (!callbacksInitial) { return this; } let callbacks; if (!this._callbackActive.has(name)) { this._callbackActive.set(name, callbacksInitial); } else if (this._callbackActive.get(name) !== callbacksInitial) { callbacks = callbacksInitial.slice(); } for(let i = 0; (callbacks || this._callbackActive.get(name)) && i < (callbacks || this._callbackActive.get(name)).length; i++){ const evt = (callbacks || this._callbackActive.get(name))[i]; if (!evt.callback) continue; evt.callback.call(evt.scope, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); if (evt._once) { const existingCallback = this._callbacks.get(name); const ind = existingCallback ? existingCallback.indexOf(evt) : -1; if (ind !== -1) { if (this._callbackActive.get(name) === existingCallback) { this._callbackActive.set(name, this._callbackActive.get(name).slice()); } const callbacks = this._callbacks.get(name); if (!callbacks) continue; callbacks[ind].removed = true; callbacks.splice(ind, 1); if (callbacks.length === 0) { this._callbacks.delete(name); } } } } if (!callbacks) { this._callbackActive.delete(name); } return this; } hasEvent(name) { return !!this._callbacks.get(name)?.length; } constructor(){ this._callbacks = new Map(); this._callbackActive = new Map(); } } const cachedResult = (func)=>{ const uninitToken = {}; let result = uninitToken; return ()=>{ if (result === uninitToken) { result = func(); } return result; }; }; class Impl { static{ this.modules = {}; } static{ this.wasmSupported = cachedResult(()=>{ try { if (typeof WebAssembly === 'object' && typeof WebAssembly.instantiate === 'function') { const module = new WebAssembly.Module(Uint8Array.of(0x0, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00)); if (module instanceof WebAssembly.Module) { return new WebAssembly.Instance(module) instanceof WebAssembly.Instance; } } } catch (e) {} return false; }); } static loadScript(url, callback) { const s = document.createElement("script"); s.setAttribute('src', url); s.onload = ()=>{ callback(null); }; s.onerror = ()=>{ callback(`Failed to load script='${url}'`); }; document.body.appendChild(s); } static loadWasm(moduleName, config, callback) { const loadUrl = Impl.wasmSupported() && config.glueUrl && config.wasmUrl ? config.glueUrl : config.fallbackUrl; if (loadUrl) { Impl.loadScript(loadUrl, (err)=>{ if (err) { callback(err, null); } else { const module = window[moduleName]; window[moduleName] = undefined; module({ locateFile: ()=>config.wasmUrl, onAbort: ()=>{ callback('wasm module aborted.'); } }).then((instance)=>{ callback(null, instance); }); } }); } else { callback('No supported wasm modules found.', null); } } static getModule(name) { if (!Impl.modules.hasOwnProperty(name)) { Impl.modules[name] = { config: null, initializing: false, instance: null, callbacks: [] }; } return Impl.modules[name]; } static initialize(moduleName, module) { if (module.initializing) { return; } const config = module.config; if (config.glueUrl || config.wasmUrl || config.fallbackUrl) { module.initializing = true; Impl.loadWasm(moduleName, config, (err, instance)=>{ if (err) { if (config.errorHandler) { config.errorHandler(err); } else { console.error(`failed to initialize module=${moduleName} error=${err}`); } } else { module.instance = instance; module.callbacks.forEach((callback)=>{ callback(instance); }); } }); } } } class WasmModule { static setConfig(moduleName, config) { const module = Impl.getModule(moduleName); module.config = config; if (module.callbacks.length > 0) { Impl.initialize(moduleName, module); } } static getConfig(moduleName) { return Impl.modules?.[moduleName]?.config; } static getInstance(moduleName, callback) { const module = Impl.getModule(moduleName); if (module.instance) { callback(module.instance); } else { module.callbacks.push(callback); if (module.config) { Impl.initialize(moduleName, module); } } } } class ReadStream { constructor(arraybuffer){ this.offset = 0; this.arraybuffer = arraybuffer; this.dataView = new DataView(arraybuffer); } get remainingBytes() { return this.dataView.byteLength - this.offset; } reset(offset = 0) { this.offset = offset; } skip(bytes) { this.offset += bytes; } align(bytes) { this.offset = this.offset + bytes - 1 & ~(bytes - 1); } _inc(amount) { this.offset += amount; return this.offset - amount; } readChar() { return String.fromCharCode(this.dataView.getUint8(this.offset++)); } readChars(numChars) { let result = ''; for(let i = 0; i < numChars; ++i){ result += this.readChar(); } return result; } readU8() { return this.dataView.getUint8(this.offset++); } readU16() { return this.dataView.getUint16(this._inc(2), true); } readU32() { return this.dataView.getUint32(this._inc(4), true); } readU64() { return this.readU32() + 2 ** 32 * this.readU32(); } readU32be() { return this.dataView.getUint32(this._inc(4), false); } readArray(result) { for(let i = 0; i < result.length; ++i){ result[i] = this.readU8(); } } readLine() { const view = this.dataView; let result = ''; while(true){ if (this.offset >= view.byteLength) { break; } const c = String.fromCharCode(this.readU8()); if (c === '\n') { break; } result += c; } return result; } } class SortedLoopArray { constructor(args){ this.items = []; this.length = 0; this.loopIndex = -1; this._sortBy = args.sortBy; this._sortHandler = this._doSort.bind(this); } _binarySearch(item) { let left = 0; let right = this.items.length - 1; const search = item[this._sortBy]; let middle; let current; while(left <= right){ middle = Math.floor((left + right) / 2); current = this.items[middle][this._sortBy]; if (current <= search) { left = middle + 1; } else if (current > search) { right = middle - 1; } } return left; } _doSort(a, b) { const sortBy = this._sortBy; return a[sortBy] - b[sortBy]; } insert(item) { const index = this._binarySearch(item); this.items.splice(index, 0, item); this.length++; if (this.loopIndex >= index) { this.loopIndex++; } } append(item) { this.items.push(item); this.length++; } remove(item) { const idx = this.items.indexOf(item); if (idx < 0) return; this.items.splice(idx, 1); this.length--; if (this.loopIndex >= idx) { this.loopIndex--; } } sort() { const current = this.loopIndex >= 0 ? this.items[this.loopIndex] : null; this.items.sort(this._sortHandler); if (current !== null) { this.loopIndex = this.items.indexOf(current); } } } class Tags extends EventHandler { static{ this.EVENT_ADD = 'add'; } static{ this.EVENT_REMOVE = 'remove'; } static{ this.EVENT_CHANGE = 'change'; } constructor(parent){ super(), this._index = {}, this._list = []; this._parent = parent; } add(...args) { let changed = false; const tags = this._processArguments(args, true); if (!tags.length) { return changed; } for(let i = 0; i < tags.length; i++){ if (this._index[tags[i]]) { continue; } changed = true; this._index[tags[i]] = true; this._list.push(tags[i]); this.fire('add', tags[i], this._parent); } if (changed) { this.fire('change', this._parent); } return changed; } remove(...args) { let changed = false; if (!this._list.length) { return changed; } const tags = this._processArguments(args, true); if (!tags.length) { return changed; } for(let i = 0; i < tags.length; i++){ if (!this._index[tags[i]]) { continue; } changed = true; delete this._index[tags[i]]; this._list.splice(this._list.indexOf(tags[i]), 1); this.fire('remove', tags[i], this._parent); } if (changed) { this.fire('change', this._parent); } return changed; } clear() { if (!this._list.length) { return; } const tags = this._list.slice(0); this._list = []; this._index = {}; for(let i = 0; i < tags.length; i++){ this.fire('remove', tags[i], this._parent); } this.fire('change', this._parent); } has(...query) { if (!this._list.length) { return false; } return this._has(this._processArguments(query)); } _has(tags) { if (!this._list.length || !tags.length) { return false; } for(let i = 0; i < tags.length; i++){ if (tags[i].length === 1) { if (this._index[tags[i][0]]) { return true; } } else { let multiple = true; for(let t = 0; t < tags[i].length; t++){ if (this._index[tags[i][t]]) { continue; } multiple = false; break; } if (multiple) { return true; } } } return false; } list() { return this._list.slice(0); } _processArguments(args, flat) { const tags = []; let tmp = []; if (!args || !args.length) { return tags; } for(let i = 0; i < args.length; i++){ if (args[i] instanceof Array) { if (!flat) { tmp = []; } for(let t = 0; t < args[i].length; t++){ if (typeof args[i][t] !== 'string') { continue; } if (flat) { tags.push(args[i][t]); } else { tmp.push(args[i][t]); } } if (!flat && tmp.length) { tags.push(tmp); } } else if (typeof args[i] === 'string') { if (flat) { tags.push(args[i]); } else { tags.push([ args[i] ]); } } } return tags; } get size() { return this._list.length; } } const now = typeof window !== 'undefined' && window.performance && window.performance.now ? performance.now.bind(performance) : Date.now; const re = /^(([^:/?#]+):)?(\/\/([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?/; class URI { constructor(uri){ const result = uri.match(re); this.scheme = result[2]; this.authority = result[4]; this.path = result[5]; this.query = result[7]; this.fragment = result[9]; } toString() { let s = ''; if (this.scheme) { s += `${this.scheme}:`; } if (this.authority) { s += `//${this.authority}`; } s += this.path; if (this.query) { s += `?${this.query}`; } if (this.fragment) { s += `#${this.fragment}`; } return s; } getQuery() { const result = {}; if (this.query) { const queryParams = decodeURIComponent(this.query).split('&'); for (const queryParam of queryParams){ const pair = queryParam.split('='); result[pair[0]] = pair[1]; } } return result; } setQuery(params) { let q = ''; for(const key in params){ if (params.hasOwnProperty(key)) { if (q !== '') { q += '&'; } q += `${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`; } } this.query = q; } } class Tracing { static{ this._traceChannels = new Set(); } static{ this.stack = false; } static set(channel, enabled = true) {} static get(channel) { return Tracing._traceChannels.has(channel); } } const CURVE_LINEAR = 0; const CURVE_SMOOTHSTEP = 1; const CURVE_SPLINE = 4; const CURVE_STEP = 5; const math = { DEG_TO_RAD: Math.PI / 180, RAD_TO_DEG: 180 / Math.PI, clamp (value, min, max) { if (value >= max) return max; if (value <= min) return min; return value; }, intToBytes24 (i) { const r = i >> 16 & 0xff; const g = i >> 8 & 0xff; const b = i & 0xff; return [ r, g, b ]; }, intToBytes32 (i) { const r = i >> 24 & 0xff; const g = i >> 16 & 0xff; const b = i >> 8 & 0xff; const a = i & 0xff; return [ r, g, b, a ]; }, bytesToInt24 (r, g, b) { if (r.length) { b = r[2]; g = r[1]; r = r[0]; } return r << 16 | g << 8 | b; }, bytesToInt32 (r, g, b, a) { if (r.length) { a = r[3]; b = r[2]; g = r[1]; r = r[0]; } return (r << 24 | g << 16 | b << 8 | a) >>> 0; }, lerp (a, b, alpha) { return a + (b - a) * math.clamp(alpha, 0, 1); }, lerpAngle (a, b, alpha) { if (b - a > 180) { b -= 360; } if (b - a < -180) { b += 360; } return math.lerp(a, b, math.clamp(alpha, 0, 1)); }, powerOfTwo (x) { return x !== 0 && !(x & x - 1); }, nextPowerOfTwo (val) { val--; val |= val >> 1; val |= val >> 2; val |= val >> 4; val |= val >> 8; val |= val >> 16; val++; return val; }, nearestPowerOfTwo (val) { return Math.pow(2, Math.round(Math.log2(val))); }, random (min, max) { const diff = max - min; return Math.random() * diff + min; }, smoothstep (min, max, x) { if (x <= min) return 0; if (x >= max) return 1; x = (x - min) / (max - min); return x * x * (3 - 2 * x); }, smootherstep (min, max, x) { if (x <= min) return 0; if (x >= max) return 1; x = (x - min) / (max - min); return x * x * x * (x * (x * 6 - 15) + 10); }, roundUp (numToRound, multiple) { if (multiple === 0) { return numToRound; } return Math.ceil(numToRound / multiple) * multiple; }, between (num, a, b, inclusive) { const min = Math.min(a, b); const max = Math.max(a, b); return inclusive ? num >= min && num <= max : num > min && num < max; } }; class Color { constructor(r = 0, g = 0, b = 0, a = 1){ const length = r.length; if (length === 3 || length === 4) { this.r = r[0]; this.g = r[1]; this.b = r[2]; this.a = r[3] ?? 1; } else { this.r = r; this.g = g; this.b = b; this.a = a; } } clone() { const cstr = this.constructor; return new cstr(this.r, this.g, this.b, this.a); } copy(rhs) { this.r = rhs.r; this.g = rhs.g; this.b = rhs.b; this.a = rhs.a; return this; } equals(rhs) { return this.r === rhs.r && this.g === rhs.g && this.b === rhs.b && this.a === rhs.a; } set(r, g, b, a = 1) { this.r = r; this.g = g; this.b = b; this.a = a; return this; } lerp(lhs, rhs, alpha) { this.r = lhs.r + alpha * (rhs.r - lhs.r); this.g = lhs.g + alpha * (rhs.g - lhs.g); this.b = lhs.b + alpha * (rhs.b - lhs.b); this.a = lhs.a + alpha * (rhs.a - lhs.a); return this; } linear(src = this) { this.r = Math.pow(src.r, 2.2); this.g = Math.pow(src.g, 2.2); this.b = Math.pow(src.b, 2.2); this.a = src.a; return this; } gamma(src = this) { this.r = Math.pow(src.r, 1 / 2.2); this.g = Math.pow(src.g, 1 / 2.2); this.b = Math.pow(src.b, 1 / 2.2); this.a = src.a; return this; } mulScalar(scalar) { this.r *= scalar; this.g *= scalar; this.b *= scalar; return this; } fromString(hex) { const i = parseInt(hex.replace('#', '0x'), 16); let bytes; if (hex.length > 7) { bytes = math.intToBytes32(i); } else { bytes = math.intToBytes24(i); bytes[3] = 255; } this.set(bytes[0] / 255, bytes[1] / 255, bytes[2] / 255, bytes[3] / 255); return this; } fromArray(arr, offset = 0) { this.r = arr[offset] ?? this.r; this.g = arr[offset + 1] ?? this.g; this.b = arr[offset + 2] ?? this.b; this.a = arr[offset + 3] ?? this.a; return this; } toString(alpha, asArray) { const { r, g, b, a } = this; if (asArray || r > 1 || g > 1 || b > 1) { return `${r.toFixed(3)}, ${g.toFixed(3)}, ${b.toFixed(3)}, ${a.toFixed(3)}`; } let s = `#${((1 << 24) + (Math.round(r * 255) << 16) + (Math.round(g * 255) << 8) + Math.round(b * 255)).toString(16).slice(1)}`; if (alpha === true) { const aa = Math.round(a * 255).toString(16); if (this.a < 16 / 255) { s += `0${aa}`; } else { s += aa; } } return s; } toArray(arr = [], offset = 0, alpha = true) { arr[offset] = this.r; arr[offset + 1] = this.g; arr[offset + 2] = this.b; if (alpha) { arr[offset + 3] = this.a; } return arr; } static{ this.BLACK = Object.freeze(new Color(0, 0, 0, 1)); } static{ this.BLUE = Object.freeze(new Color(0, 0, 1, 1)); } static{ this.CYAN = Object.freeze(new Color(0, 1, 1, 1)); } static{ this.GRAY = Object.freeze(new Color(0.5, 0.5, 0.5, 1)); } static{ this.GREEN = Object.freeze(new Color(0, 1, 0, 1)); } static{ this.MAGENTA = Object.freeze(new Color(1, 0, 1, 1)); } static{ this.RED = Object.freeze(new Color(1, 0, 0, 1)); } static{ this.WHITE = Object.freeze(new Color(1, 1, 1, 1)); } static{ this.YELLOW = Object.freeze(new Color(1, 1, 0, 1)); } } class CurveEvaluator { constructor(curve, time = 0){ this._left = -Infinity; this._right = Infinity; this._recip = 0; this._p0 = 0; this._p1 = 0; this._m0 = 0; this._m1 = 0; this._curve = curve; this._reset(time); } evaluate(time, forceReset = false) { if (forceReset || time < this._left || time >= this._right) { this._reset(time); } let result; const type = this._curve.type; if (type === CURVE_STEP) { result = this._p0; } else { const t = this._recip === 0 ? 0 : (time - this._left) * this._recip; if (type === CURVE_LINEAR) { result = math.lerp(this._p0, this._p1, t); } else if (type === CURVE_SMOOTHSTEP) { result = math.lerp(this._p0, this._p1, t * t * (3 - 2 * t)); } else { result = this._evaluateHermite(this._p0, this._p1, this._m0, this._m1, t); } } return result; } _reset(time) { const keys = this._curve.keys; const len = keys.length; if (!len) { this._left = -Infinity; this._right = Infinity; this._recip = 0; this._p0 = this._p1 = this._m0 = this._m1 = 0; } else { if (time < keys[0][0]) { this._left = -Infinity; this._right = keys[0][0]; this._recip = 0; this._p0 = this._p1 = keys[0][1]; this._m0 = this._m1 = 0; } else if (time >= keys[len - 1][0]) { this._left = keys[len - 1][0]; this._right = Infinity; this._recip = 0; this._p0 = this._p1 = keys[len - 1][1]; this._m0 = this._m1 = 0; } else { let index = 0; while(time >= keys[index + 1][0]){ index++; } this._left = keys[index][0]; this._right = keys[index + 1][0]; const diff = 1.0 / (this._right - this._left); this._recip = isFinite(diff) ? diff : 0; this._p0 = keys[index][1]; this._p1 = keys[index + 1][1]; if (this._curve.type === CURVE_SPLINE) { this._calcTangents(keys, index); } } } } _calcTangents(keys, index) { let a; const b = keys[index]; const c = keys[index + 1]; let d; if (index === 0) { a = [ keys[0][0] + (keys[0][0] - keys[1][0]), keys[0][1] + (keys[0][1] - keys[1][1]) ]; } else { a = keys[index - 1]; } if (index === keys.length - 2) { d = [ keys[index + 1][0] + (keys[index + 1][0] - keys[index][0]), keys[index + 1][1] + (keys[index + 1][1] - keys[index][1]) ]; } else { d = keys[index + 2]; } if (this._curve.type === CURVE_SPLINE) { const s1_ = 2 * (c[0] - b[0]) / (c[0] - a[0]); const s2_ = 2 * (c[0] - b[0]) / (d[0] - b[0]); this._m0 = this._curve.tension * (isFinite(s1_) ? s1_ : 0) * (c[1] - a[1]); this._m1 = this._curve.tension * (isFinite(s2_) ? s2_ : 0) * (d[1] - b[1]); } else { const s1 = (c[0] - b[0]) / (b[0] - a[0]); const s2 = (c[0] - b[0]) / (d[0] - c[0]); const a_ = b[1] + (a[1] - b[1]) * (isFinite(s1) ? s1 : 0); const d_ = c[1] + (d[1] - c[1]) * (isFinite(s2) ? s2 : 0); const tension = this._curve.tension; this._m0 = tension * (c[1] - a_); this._m1 = tension * (d_ - b[1]); } } _evaluateHermite(p0, p1, m0, m1, t) { const t2 = t * t; const twot = t + t; const omt = 1 - t; const omt2 = omt * omt; return p0 * ((1 + twot) * omt2) + m0 * (t * omt2) + p1 * (t2 * (3 - twot)) + m1 * (t2 * (t - 1)); } } class Curve { constructor(data){ this.keys = []; this.type = CURVE_SMOOTHSTEP; this.tension = 0.5; this._eval = new CurveEvaluator(this); if (data) { for(let i = 0; i < data.length - 1; i += 2){ this.keys.push([ data[i], data[i + 1] ]); } } this.sort(); } get length() { return this.keys.length; } add(time, value) { const keys = this.keys; const len = keys.length; let i = 0; for(; i < len; i++){ if (keys[i][0] > time) { break; } } const key = [ time, value ]; this.keys.splice(i, 0, key); return key; } get(index) { return this.keys[index]; } sort() { this.keys.sort((a, b)=>a[0] - b[0]); } value(time) { return this._eval.evaluate(time, true); } closest(time) { const keys = this.keys; const length = keys.length; let min = 2; let result = null; for(let i = 0; i < length; i++){ const diff = Math.abs(time - keys[i][0]); if (min >= diff) { min = diff; result = keys[i]; } else { break; } } return result; } clone() { const result = new this.constructor(); result.keys = this.keys.map((key)=>[ ...key ]); result.type = this.type; result.tension = this.tension; return result; } quantize(precision) { precision = Math.max(precision, 2); const values = new Float32Array(precision); const step = 1.0 / (precision - 1); values[0] = this._eval.evaluate(0, true); for(let i = 1; i < precision; i++){ values[i] = this._eval.evaluate(step * i); } return values; } quantizeClamped(precision, min, max) { const result = this.quantize(precision); for(let i = 0; i < result.length; ++i){ result[i] = Math.min(max, Math.max(min, result[i])); } return result; } } class CurveSet { constructor(...args){ this.curves = []; this._type = CURVE_SMOOTHSTEP; if (args.length > 1) { for(let i = 0; i < args.length; i++){ this.curves.push(new Curve(args[i])); } } else if (args.length === 0) { this.curves.push(new Curve()); } else { const arg = args[0]; if (typeof arg === 'number') { for(let i = 0; i < arg; i++){ this.curves.push(new Curve()); } } else { for(let i = 0; i < arg.length; i++){ this.curves.push(new Curve(arg[i])); } } } } get length() { return this.curves.length; } set type(value) { this._type = value; for(let i = 0; i < this.curves.length; i++){ this.curves[i].type = value; } } get type() { return this._type; } get(index) { return this.curves[index]; } value(time, result = []) { const length = this.curves.length; result.length = length; for(let i = 0; i < length; i++){ result[i] = this.curves[i].value(time); } return result; } clone() { const result = new this.constructor(); result.curves = []; for(let i = 0; i < this.curves.length; i++){ result.curves.push(this.curves[i].clone()); } result._type = this._type; return result; } quantize(precision) { precision = Math.max(precision, 2); const numCurves = this.curves.length; const values = new Float32Array(precision * numCurves); const step = 1.0 / (precision - 1); for(let c = 0; c < numCurves; c++){ const ev = new CurveEvaluator(this.curves[c]); for(let i = 0; i < precision; i++){ values[i * numCurves + c] = ev.evaluate(step * i); } } return values; } quantizeClamped(precision, min, max) { const result = this.quantize(precision); for(let i = 0; i < result.length; ++i){ result[i] = Math.min(max, Math.max(min, result[i])); } return result; } } const floatView = new Float32Array(1); const int32View = new Int32Array(floatView.buffer); class FloatPacking { static float2Half(value) { floatView[0] = value; const x = int32View[0]; let bits = x >> 16 & 0x8000; let m = x >> 12 & 0x07ff; const e = x >> 23 & 0xff; if (e < 103) { return bits; } if (e > 142) { bits |= 0x7c00; bits |= (e === 255 ? 0 : 1) && x & 0x007fffff; return bits; } if (e < 113) { m |= 0x0800; bits |= (m >> 114 - e) + (m >> 113 - e & 1); return bits; } bits |= e - 112 << 10 | m >> 1; bits += m & 1; return bits; } static float2RGBA8(value, data) { floatView[0] = value; const intBits = int32View[0]; data.r = (intBits >> 24 & 0xFF) / 255.0; data.g = (intBits >> 16 & 0xFF) / 255.0; data.b = (intBits >> 8 & 0xFF) / 255.0; data.a = (intBits & 0xFF) / 255.0; } } class Vec3 { constructor(x = 0, y = 0, z = 0){ if (x.length === 3) { this.x = x[0]; this.y = x[1]; this.z = x[2]; } else { this.x = x; this.y = y; this.z = z; } } add(rhs) { this.x += rhs.x; this.y += rhs.y; this.z += rhs.z; return this; } add2(lhs, rhs) { this.x = lhs.x + rhs.x; this.y = lhs.y + rhs.y; this.z = lhs.z + rhs.z; return this; } addScalar(scalar) { this.x += scalar; this.y += scalar; this.z += scalar; return this; } addScaled(rhs, scalar) { this.x += rhs.x * scalar; this.y += rhs.y * scalar; this.z += rhs.z * scalar; return this; } clone() { const cstr = this.constructor; return new cstr(this.x, this.y, this.z); } copy(rhs) { this.x = rhs.x; this.y = rhs.y; this.z = rhs.z; return this; } cross(lhs, rhs) { const lx = lhs.x; const ly = lhs.y; const lz = lhs.z; const rx = rhs.x; const ry = rhs.y; const rz = rhs.z; this.x = ly * rz - ry * lz; this.y = lz * rx - rz * lx; this.z = lx * ry - rx * ly; return this; } distance(rhs) { const x = this.x - rhs.x; const y = this.y - rhs.y; const z = this.z - rhs.z; return Math.sqrt(x * x + y * y + z * z); } div(rhs) { this.x /= rhs.x; this.y /= rhs.y; this.z /= rhs.z; return this; } div2(lhs, rhs) { this.x = lhs.x / rhs.x; this.y = lhs.y / rhs.y; this.z = lhs.z / rhs.z; return this; } divScalar(scalar) { this.x /= scalar; this.y /= scalar; this.z /= scalar; return this; } dot(rhs) { return this.x * rhs.x + this.y * rhs.y + this.z * rhs.z; } equals(rhs) { return this.x === rhs.x && this.y === rhs.y && this.z === rhs.z; } equalsApprox(rhs, epsilon = 1e-6) { return Math.abs(this.x - rhs.x) < epsilon && Math.abs(this.y - rhs.y) < epsilon && Math.abs(this.z - rhs.z) < epsilon; } length() { return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z); } lengthSq() { return this.x * this.x + this.y * this.y + this.z * this.z; } lerp(lhs, rhs, alpha) { this.x = lhs.x + alpha * (rhs.x - lhs.x); this.y = lhs.y + alpha * (rhs.y - lhs.y); this.z = lhs.z + alpha * (rhs.z - lhs.z); return this; } mul(rhs) { this.x *= rhs.x; this.y *= rhs.y; this.z *= rhs.z; return this; } mul2(lhs, rhs) { this.x = lhs.x * rhs.x; this.y = lhs.y * rhs.y; this.z = lhs.z * rhs.z; return this; } mulScalar(scalar) { this.x *= scalar; this.y *= scalar; this.z *= scalar; return this; } normalize(src = this) { const lengthSq = src.x * src.x + src.y * src.y + src.z * src.z; if (lengthSq > 0) { const invLength = 1 / Math.sqrt(lengthSq); this.x = src.x * invLength; this.y = src.y * invLength; this.z = src.z * invLength; } return this; } floor(src = this) { this.x = Math.floor(src.x); this.y = Math.floor(src.y); this.z = Math.floor(src.z); return this; } ceil(src = this) { this.x = Math.ceil(src.x); this.y = Math.ceil(src.y); this.z = Math.ceil(src.z); return this; } round(src = this) { this.x = Math.round(src.x); this.y = Math.round(src.y); this.z = Math.round(src.z); return this; } min(rhs) { if (rhs.x < this.x) this.x = rhs.x; if (rhs.y < this.y) this.y = rhs.y; if (rhs.z < this.z) this.z = rhs.z; return this; } max(rhs) { if (rhs.x > this.x) this.x = rhs.x; if (rhs.y > this.y) this.y = rhs.y; if (rhs.z > this.z) this.z = rhs.z; return this; } project(rhs) { const a_dot_b = this.x * rhs.x + this.y * rhs.y + this.z * rhs.z; const b_dot_b = rhs.x * rhs.x + rhs.y * rhs.y + rhs.z * rhs.z; const s = a_dot_b / b_dot_b; this.x = rhs.x * s; this.y = rhs.y * s; this.z = rhs.z * s; return this; } set(x, y, z) { this.x = x; this.y = y; this.z = z; return this; } sub(rhs) { this.x -= rhs.x; this.y -= rhs.y; this.z -= rhs.z; return this; } sub2(lhs, rhs) { this.x = lhs.x - rhs.x; this.y = lhs.y - rhs.y; this.z = lhs.z - rhs.z; return this; } subScalar(scalar) { this.x -= scalar; this.y -= scalar; this.z -= scalar; return this; } fromArray(arr, offset = 0) { this.x = arr[offset] ?? this.x; this.y = arr[offset + 1] ?? this.y; this.z = arr[offset + 2] ?? this.z; return this; } toString() { return `[${this.x}, ${this.y}, ${this.z}]`; } toArray(arr = [], offset = 0) { arr[offset] = this.x; arr[offset + 1] = this.y; arr[offset + 2] = this.z; return arr; } static{ this.ZERO = Object.freeze(new Vec3(0, 0, 0)); } static{ this.HALF = Object.freeze(new Vec3(0.5, 0.5, 0.5)); } static{ this.ONE = Object.freeze(new Vec3(1, 1, 1)); } static{ this.UP = Object.freeze(new Vec3(0, 1, 0)); } static{ this.DOWN = Object.freeze(new Vec3(0, -1, 0)); } static{ this.RIGHT = Object.freeze(new Vec3(1, 0, 0)); } static{ this.LEFT = Object.freeze(new Vec3(-1, 0, 0)); } static{ this.FORWARD = Object.freeze(new Vec3(0, 0, -1)); } static{ this.BACK = Object.freeze(new Vec3(0, 0, 1)); } } class Mat3 { constructor(){ this.data = new Float32Array(9); this.data[0] = this.data[4] = this.data[8] = 1; } clone() { const cstr = this.constructor; return new cstr().copy(this); } copy(rhs) { const src = rhs.data; const dst = this.data; dst[0] = src[0]; dst[1] = src[1]; dst[2] = src[2]; dst[3] = src[3]; dst[4] = src[4]; dst[5] = src[5]; dst[6] = src[6]; dst[7] = src[7]; dst[8] = src[8]; return this; } set(src) { const dst = this.data; dst[0] = src[0]; dst[1] = src[1]; dst[2] = src[2]; dst[3] = src[3]; dst[4] = src[4]; dst[5] = src[5]; dst[6] = src[6]; dst[7] = src[7]; dst[8] = src[8]; return this; } getX(x = new Vec3()) { return x.set(this.data[0], this.data[1], this.data[2]); } getY(y = new Vec3()) { return y.set(this.data[3], this.data[4], this.data[5]); } getZ(z = new Vec3()) { return z.set(this.data[6], this.data[7], this.data[8]); } equals(rhs) { const l = this.data; const r = rhs.data; return l[0] === r[0] && l[1] === r[1] && l[2] === r[2] && l[3] === r[3] && l[4] === r[4] && l[5] === r[5] && l[6] === r[6] && l[7] === r[7] && l[8] === r[8]; } isIdentity() { const m = this.data; return m[0] === 1 && m[1] === 0 && m[2] === 0 && m[3] === 0 && m[4] === 1 && m[5] === 0 && m[6] === 0 && m[7] === 0 && m[8] === 1; } setIdentity() { const m = this.data; m[0] = 1; m[1] = 0; m[2] = 0; m[3] = 0; m[4] = 1; m[5] = 0; m[6] = 0; m[7] = 0; m[8] = 1; return this; } toString() { return `[${this.data.join(', ')}]`; } transpose(src = this) { const s = src.data; const t = this.data; if (s === t) { let tmp; tmp = s[1]; t[1] = s[3]; t[3] = tmp; tmp = s[2]; t[2] = s[6]; t[6] = tmp; tmp = s[5]; t[5] = s[7]; t[7] = tmp; } else { t[0] = s[0]; t[1] = s[3]; t[2] = s[6]; t[3] = s[1]; t[4] = s[4]; t[5] = s[7]; t[6] = s[2]; t[7] = s[5]; t[8] = s[8]; } return this; } setFromMat4(m) { const src = m.data; const dst = this.data; dst[0] = src[0]; dst[1] = src[1]; dst[2] = src[2]; dst[3] = src[4]; dst[4] = src[5]; dst[5] = src[6]; dst[6] = src[8]; dst[7] = src[9]; dst[8] = src[10]; return this; } setFromQuat(r) { const qx = r.x; const qy = r.y; const qz = r.z; const qw = r.w; const x2 = qx + qx; const y2 = qy + qy; const z2 = qz + qz; const xx = qx * x2; const xy = qx * y2; const xz = qx * z2; const yy = qy * y2; const yz = qy * z2; const zz = qz * z2; const wx = qw * x2; const wy = qw * y2; const wz = qw * z2; const m = this.data; m[0] = 1 - (yy + zz); m[1] = xy + wz; m[2] = xz - wy; m[3] = xy - wz; m[4] = 1 - (xx + zz); m[5] = yz + wx; m[6] = xz + wy; m[7] = yz - wx; m[8] = 1 - (xx + yy); return this; } invertMat4(src) { const s = src.data; const a0 = s[0]; const a1 = s[1]; const a2 = s[2]; const a4 = s[4]; const a5 = s[5]; const a6 = s[6]; const a8 = s[8]; const a9 = s[9]; const a10 = s[10]; const b11 = a10 * a5 - a6 * a9; const b21 = -a10 * a1 + a2 * a9; const b31 = a6 * a1 - a2 * a5; const b12 = -a10 * a4 + a6 * a8; const b22 = a10 * a0 - a2 * a8; const b32 = -a6 * a0 + a2 * a4; const b13 = a9 * a4 - a5 * a8; const b23 = -a9 * a0 + a1 * a8; const b33 = a5 * a0 - a1 * a4; const det = a0 * b11 + a1 * b12 + a2 * b13; if (det === 0) { this.setIdentity(); } else { const invDet = 1 / det; const t = this.data; t[0] = b11 * invDet; t[1] = b21 * invDet; t[2] = b31 * invDet; t[3] = b12 * invDet; t[4] = b22 * invDet; t[5] = b32 * invDet; t[6] = b13 * invDet; t[7] = b23 * invDet; t[8] = b33 * invDet; } return this; } transformVector(vec, res = new Vec3()) { const m = this.data; const { x, y, z } = vec; res.x = x * m[0] + y * m[3] + z * m[6]; res.y = x * m[1] + y * m[4] + z * m[7]; res.z = x * m[2] + y * m[5] + z * m[8]; return res; } static{ this.IDENTITY = Object.freeze(new Mat3()); } static{ this.ZERO = Object.freeze(new Mat3().set([ 0, 0, 0, 0, 0, 0, 0, 0, 0 ])); } } class Vec2 { constructor(x = 0, y = 0){ if (x.length === 2) { this.x = x[0]; this.y = x[1]; } else { this.x = x; this.y = y; } } add(rhs) { this.x += rhs.x; this.y += rhs.y; return this; } add2(lhs, rhs) { this.x = lhs.x + rhs.x; this.y = lhs.y + rhs.y; return this; } addScalar(scalar) { this.x += scalar; this.y += scalar; return this; } addScaled(rhs, scalar) { this.x += rhs.x * scalar; this.y += rhs.y * scalar; return this; } clone() { const cstr = this.constructor; return new cstr(this.x, this.y); } copy(rhs) { this.x = rhs.x; this.y = rhs.y; return this; } cross(rhs) { return this.x * rhs.y - this.y * rhs.x; } distance(rhs) { const x = this.x - rhs.x; const y = this.y - rhs.y; return Math.sqrt(x * x + y * y); } div(rhs) { this.x /= rhs.x; this.y /= rhs.y; return this; } div2(lhs, rhs) { this.x = lhs.x / rhs.x; this.y = lhs.y / rhs.y; return this; } divScalar(scalar) { this.x /= scalar; this.y /= scalar; return this; } dot(rhs) { return this.x * rhs.x + this.y * rhs.y; } equals(rhs) { return this.x === rhs.x && this.y === rhs.y; } equalsApprox(rhs, epsilon = 1e-6) { return Math.abs(this.x - rhs.x) < epsilon && Math.abs(this.y - rhs.y) < epsilon; } length() { return Math.sqrt(this.x * this.x + this.y * this.y); } lengthSq() { return this.x * this.x + this.y * this.y; } lerp(lhs, rhs, alpha) { this.x = lhs.x + alpha * (rhs.x - lhs.x); this.y = lhs.y + alpha * (rhs.y - lhs.y); return this; } mul(rhs) { this.x *= rhs.x; this.y *= rhs.y; return this; } mul2(lhs, rhs) { this.x = lhs.x * rhs.x; this.y = lhs.y * rhs.y; return this; } mulScalar(scalar) { this.x *= scalar; this.y *= scalar; return this; } normalize(src = this) { const lengthSq = src.x * src.x + src.y * src.y; if (lengthSq > 0) { const invLength = 1 / Math.sqrt(lengthSq); this.x = src.x * invLength; this.y = src.y * invLength; } return this; } rotate(degrees) { const angle = Math.atan2(this.x, this.y) + degrees * math.DEG_TO_RAD; const len = Math.sqrt(this.x * this.x + this.y * this.y); this.x = Math.sin(angle) * len; this.y = Math.cos(angle) * len; return this; } angle() { return Math.atan2(this.x, this.y) * math.RAD_TO_DEG; } angleTo(rhs) { return Math.atan2(this.x * rhs.y + this.y * rhs.x, this.x * rhs.x + this.y * rhs.y) * math.RAD_TO_DEG; } floor(src = this) { this.x = Math.floor(src.x); this.y = Math.floor(src.y); return this; } ceil(src = this) { this.x = Math.ceil(src.x); this.y = Math.ceil(src.y); return this; } round(src = this) { this.x = Math.round(src.x); this.y = Math.round(src.y); return this; } min(rhs) { if (rhs.x < this.x) this.x = rhs.x; if (rhs.y < this.y) this.y = rhs.y; return this; } max(rhs) { if (rhs.x > this.x) this.x = rhs.x; if (rhs.y > this.y) this.y = rhs.y; return this; } set(x, y) { this.x = x; this.y = y; return this; } sub(rhs) { this.x -= rhs.x; this.y -= rhs.y; return this; } sub2(lhs, rhs) { this.x = lhs.x - rhs.x; this.y = lhs.y - rhs.y; return this; } subScalar(scalar) { this.x -= scalar; this.y -= scalar; return this; } fromArray(arr, offset = 0) { this.x = arr[offset] ?? this.x; this.y = arr[offset + 1] ?? this.y; return this; } toString() { return `[${this.x}, ${this.y}]`; } toArray(arr = [], offset = 0) { arr[offset] = this.x; arr[offset + 1] = this.y; return arr; } static angleRad(lhs, rhs) { return Math.atan2(lhs.x * rhs.y - lhs.y * rhs.x, lhs.x * rhs.x + lhs.y * rhs.y); } static{ this.ZERO = Object.freeze(new Vec2(0, 0)); } static{ this.HALF = Object.freeze(new Vec2(0.5, 0.5)); } static{ this.ONE = Object.freeze(new Vec2(1, 1)); } static{ this.UP = Object.freeze(new Vec2(0, 1)); } static{ this.DOWN = Object.freeze(new Vec2(0, -1)); } static{ this.RIGHT = Object.freeze(new Vec2(1, 0)); } static{ this.LEFT = Object.freeze(new Vec2(-1, 0)); } } class Vec4 { constructor(x = 0, y = 0, z = 0, w = 0){ if (x.length === 4) { this.x = x[0]; this.y = x[1]; this.z = x[2]; this.w = x[3]; } else { this.x = x; this.y = y; this.z = z; this.w = w; } } add(rhs) { this.x += rhs.x; this.y += rhs.y; this.z += rhs.z; this.w += rhs.w; return this; } add2(lhs, rhs) { this.x = lhs.x + rhs.x; this.y = lhs.y + rhs.y; this.z = lhs.z + rhs.z; this.w = lhs.w + rhs.w; return this; } addScalar(scala