@loaders.gl/gltf
Version:
Framework-independent loader for the glTF format
1,454 lines (1,418 loc) • 248 kB
JavaScript
(function webpackUniversalModuleDefinition(root, factory) {
if (typeof exports === 'object' && typeof module === 'object')
module.exports = factory();
else if (typeof define === 'function' && define.amd) define([], factory);
else if (typeof exports === 'object') exports['loaders'] = factory();
else root['loaders'] = factory();})(globalThis, function () {
"use strict";
var __exports__ = (() => {
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __commonJS = (cb, mod) => function __require() {
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
};
var __export = (target, all) => {
for (var name12 in all)
__defProp(target, name12, { get: all[name12], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// external-global-plugin:@loaders.gl/core
var require_core = __commonJS({
"external-global-plugin:@loaders.gl/core"(exports, module) {
module.exports = globalThis.loaders;
}
});
// bundle.ts
var bundle_exports = {};
__export(bundle_exports, {
EXT_FEATURE_METADATA: () => name3,
EXT_MESH_FEATURES: () => name,
EXT_STRUCTURAL_METADATA: () => name2,
GLBLoader: () => GLBLoader,
GLBWriter: () => GLBWriter,
GLTFLoader: () => GLTFLoader,
GLTFScenegraph: () => GLTFScenegraph,
GLTFWriter: () => GLTFWriter,
_getMemoryUsageGLTF: () => getMemoryUsageGLTF,
createExtMeshFeatures: () => createExtMeshFeatures,
createExtStructuralMetadata: () => createExtStructuralMetadata,
postProcessGLTF: () => postProcessGLTF
});
__reExport(bundle_exports, __toESM(require_core(), 1));
// src/lib/extensions/EXT_mesh_features.ts
var EXT_mesh_features_exports = {};
__export(EXT_mesh_features_exports, {
createExtMeshFeatures: () => createExtMeshFeatures,
decode: () => decode,
encode: () => encode,
name: () => name
});
// ../images/src/lib/utils/version.ts
var VERSION = typeof __VERSION__ !== "undefined" ? __VERSION__ : "latest";
// ../loader-utils/src/loader-types.ts
async function parseFromContext(data, loaders, options, context) {
return context._parse(data, loaders, options, context);
}
// ../loader-utils/src/lib/env-utils/assert.ts
function assert(condition, message) {
if (!condition) {
throw new Error(message || "loader assertion failed.");
}
}
// ../loader-utils/src/lib/env-utils/globals.ts
var globals = {
self: typeof self !== "undefined" && self,
window: typeof window !== "undefined" && window,
global: typeof global !== "undefined" && global,
document: typeof document !== "undefined" && document
};
var self_ = globals.self || globals.window || globals.global || {};
var window_ = globals.window || globals.self || globals.global || {};
var global_ = globals.global || globals.self || globals.window || {};
var document_ = globals.document || {};
var isBrowser = (
// @ts-ignore process does not exist on browser
Boolean(typeof process !== "object" || String(process) !== "[object process]" || process.browser)
);
var matches = typeof process !== "undefined" && process.version && /v([0-9]*)/.exec(process.version);
var nodeVersion = matches && parseFloat(matches[1]) || 0;
// ../loader-utils/src/lib/javascript-utils/is-type.ts
var isSharedArrayBuffer = (value) => typeof SharedArrayBuffer !== "undefined" && value instanceof SharedArrayBuffer;
// ../loader-utils/src/lib/module-utils/js-module-utils.ts
function registerJSModules(modules) {
globalThis.loaders ||= {};
globalThis.loaders.modules ||= {};
Object.assign(globalThis.loaders.modules, modules);
}
function getJSModuleOrNull(name12) {
const module = globalThis.loaders?.modules?.[name12];
return module || null;
}
// ../worker-utils/src/lib/npm-tag.ts
var NPM_TAG = "latest";
// ../worker-utils/src/lib/env-utils/version.ts
var warningIssued = false;
function getVersion() {
if (!globalThis._loadersgl_?.version) {
globalThis._loadersgl_ = globalThis._loadersgl_ || {};
if (typeof __VERSION__ === "undefined" && !warningIssued) {
console.warn(
"loaders.gl: The __VERSION__ variable is not injected using babel plugin. Latest unstable workers would be fetched from the CDN."
);
globalThis._loadersgl_.version = NPM_TAG;
warningIssued = true;
} else {
globalThis._loadersgl_.version = __VERSION__;
}
}
return globalThis._loadersgl_.version;
}
var VERSION2 = getVersion();
// ../worker-utils/src/lib/env-utils/assert.ts
function assert2(condition, message) {
if (!condition) {
throw new Error(message || "loaders.gl assertion failed.");
}
}
// ../worker-utils/src/lib/env-utils/globals.ts
var globals2 = {
self: typeof self !== "undefined" && self,
window: typeof window !== "undefined" && window,
global: typeof global !== "undefined" && global,
document: typeof document !== "undefined" && document
};
var self_2 = globals2.self || globals2.window || globals2.global || {};
var window_2 = globals2.window || globals2.self || globals2.global || {};
var global_2 = globals2.global || globals2.self || globals2.window || {};
var document_2 = globals2.document || {};
var isBrowser2 = (
// @ts-ignore process.browser
typeof process !== "object" || String(process) !== "[object process]" || process.browser
);
var isWorker = typeof importScripts === "function";
var isMobile = typeof window !== "undefined" && typeof window.orientation !== "undefined";
var matches2 = typeof process !== "undefined" && process.version && /v([0-9]*)/.exec(process.version);
var nodeVersion2 = matches2 && parseFloat(matches2[1]) || 0;
// ../worker-utils/src/lib/library-utils/library-utils.ts
var loadLibraryPromises = {};
function extractLoadLibraryOptions(options = {}) {
const useLocalLibraries = options.useLocalLibraries ?? options.core?.useLocalLibraries;
const CDN = options.CDN ?? options.core?.CDN;
const modules = options.modules;
return {
...useLocalLibraries !== void 0 ? { useLocalLibraries } : {},
...CDN !== void 0 ? { CDN } : {},
...modules !== void 0 ? { modules } : {}
};
}
async function loadLibrary(libraryUrl, moduleName = null, options = {}, libraryName = null) {
if (moduleName) {
libraryUrl = getLibraryUrl(libraryUrl, moduleName, options, libraryName);
}
loadLibraryPromises[libraryUrl] = // eslint-disable-next-line @typescript-eslint/no-misused-promises
loadLibraryPromises[libraryUrl] || loadLibraryFromFile(libraryUrl);
return await loadLibraryPromises[libraryUrl];
}
function getLibraryUrl(library, moduleName, options = {}, libraryName = null) {
if (options?.core) {
throw new Error("loadLibrary: options.core must be pre-normalized");
}
if (!options.useLocalLibraries && library.startsWith("http")) {
return library;
}
libraryName = libraryName || library;
const modules = options.modules || {};
if (modules[libraryName]) {
return modules[libraryName];
}
if (!isBrowser2) {
return `modules/${moduleName}/dist/libs/${libraryName}`;
}
if (options.CDN) {
assert2(options.CDN.startsWith("http"));
return `${options.CDN}/${moduleName}@${VERSION2}/dist/libs/${libraryName}`;
}
if (isWorker) {
return `../src/libs/${libraryName}`;
}
return `modules/${moduleName}/src/libs/${libraryName}`;
}
async function loadLibraryFromFile(libraryUrl) {
if (libraryUrl.endsWith("wasm")) {
return await loadAsArrayBuffer(libraryUrl);
}
if (!isBrowser2) {
const { requireFromFile } = globalThis.loaders || {};
try {
const result = await requireFromFile?.(libraryUrl);
if (result || !libraryUrl.includes("/dist/libs/")) {
return result;
}
return await requireFromFile?.(libraryUrl.replace("/dist/libs/", "/src/libs/"));
} catch (error) {
if (libraryUrl.includes("/dist/libs/")) {
try {
return await requireFromFile?.(libraryUrl.replace("/dist/libs/", "/src/libs/"));
} catch {
}
}
console.error(error);
return null;
}
}
if (isWorker) {
return importScripts(libraryUrl);
}
const scriptSource = await loadAsText(libraryUrl);
return loadLibraryFromString(scriptSource, libraryUrl);
}
function loadLibraryFromString(scriptSource, id) {
if (!isBrowser2) {
const { requireFromString } = globalThis.loaders || {};
return requireFromString?.(scriptSource, id);
}
if (isWorker) {
eval.call(globalThis, scriptSource);
return null;
}
const script = document.createElement("script");
script.id = id;
try {
script.appendChild(document.createTextNode(scriptSource));
} catch (e) {
script.text = scriptSource;
}
document.body.appendChild(script);
return null;
}
async function loadAsArrayBuffer(url) {
const { readFileAsArrayBuffer } = globalThis.loaders || {};
if (isBrowser2 || !readFileAsArrayBuffer || url.startsWith("http")) {
const response = await fetch(url);
return await response.arrayBuffer();
}
try {
return await readFileAsArrayBuffer(url);
} catch {
if (url.includes("/dist/libs/")) {
return await readFileAsArrayBuffer(url.replace("/dist/libs/", "/src/libs/"));
}
throw new Error(`Failed to load ArrayBuffer from ${url}`);
}
}
async function loadAsText(url) {
const { readFileAsText } = globalThis.loaders || {};
if (isBrowser2 || !readFileAsText || url.startsWith("http")) {
const response = await fetch(url);
return await response.text();
}
try {
return await readFileAsText(url);
} catch {
if (url.includes("/dist/libs/")) {
return await readFileAsText(url.replace("/dist/libs/", "/src/libs/"));
}
throw new Error(`Failed to load text from ${url}`);
}
}
// ../loader-utils/src/lib/binary-utils/get-first-characters.ts
function getFirstCharacters(data, length = 5) {
if (typeof data === "string") {
return data.slice(0, length);
} else if (ArrayBuffer.isView(data)) {
return getMagicString(data.buffer, data.byteOffset, length);
} else if (data instanceof ArrayBuffer) {
const byteOffset = 0;
return getMagicString(data, byteOffset, length);
}
return "";
}
function getMagicString(arrayBuffer, byteOffset, length) {
if (arrayBuffer.byteLength <= byteOffset + length) {
return "";
}
const dataView = new DataView(arrayBuffer);
let magic = "";
for (let i = 0; i < length; i++) {
magic += String.fromCharCode(dataView.getUint8(byteOffset + i));
}
return magic;
}
// ../loader-utils/src/lib/parser-utils/parse-json.ts
function parseJSON(string) {
try {
return JSON.parse(string);
} catch (_) {
throw new Error(`Failed to parse JSON from data starting with "${getFirstCharacters(string)}"`);
}
}
// ../loader-utils/src/lib/binary-utils/array-buffer-utils.ts
function sliceArrayBuffer(arrayBuffer, byteOffset, byteLength) {
const subArray = byteLength !== void 0 ? new Uint8Array(arrayBuffer).subarray(byteOffset, byteOffset + byteLength) : new Uint8Array(arrayBuffer).subarray(byteOffset);
const arrayCopy = new Uint8Array(subArray);
return arrayCopy.buffer;
}
// ../loader-utils/src/lib/binary-utils/memory-copy-utils.ts
function padToNBytes(byteLength, padding) {
assert(byteLength >= 0);
assert(padding > 0);
return byteLength + (padding - 1) & ~(padding - 1);
}
function copyToArray(source, target, targetOffset) {
let sourceArray;
if (source instanceof ArrayBuffer) {
sourceArray = new Uint8Array(source);
} else {
const srcByteOffset = source.byteOffset;
const srcByteLength = source.byteLength;
sourceArray = new Uint8Array(source.buffer || source.arrayBuffer, srcByteOffset, srcByteLength);
}
target.set(sourceArray, targetOffset);
return targetOffset + padToNBytes(sourceArray.byteLength, 4);
}
// ../loader-utils/src/lib/binary-utils/dataview-copy-utils.ts
function copyPaddedArrayBufferToDataView(dataView, byteOffset, sourceBuffer, padding) {
const paddedLength = padToNBytes(sourceBuffer.byteLength, padding);
const padLength = paddedLength - sourceBuffer.byteLength;
if (dataView) {
const targetArray = new Uint8Array(
dataView.buffer,
dataView.byteOffset + byteOffset,
sourceBuffer.byteLength
);
const sourceArray = new Uint8Array(sourceBuffer);
targetArray.set(sourceArray);
for (let i = 0; i < padLength; ++i) {
dataView.setUint8(byteOffset + sourceBuffer.byteLength + i, 32);
}
}
byteOffset += paddedLength;
return byteOffset;
}
function copyPaddedStringToDataView(dataView, byteOffset, string, padding) {
const textEncoder = new TextEncoder();
const stringBuffer = textEncoder.encode(string);
byteOffset = copyPaddedArrayBufferToDataView(dataView, byteOffset, stringBuffer, padding);
return byteOffset;
}
// ../loader-utils/src/lib/binary-utils/memory-conversion-utils.ts
function ensureArrayBuffer(bufferSource) {
if (bufferSource instanceof ArrayBuffer) {
return bufferSource;
}
if (isSharedArrayBuffer(bufferSource)) {
return copyToArrayBuffer(bufferSource);
}
const { buffer, byteOffset, byteLength } = bufferSource;
if (buffer instanceof ArrayBuffer && byteOffset === 0 && byteLength === buffer.byteLength) {
return buffer;
}
return copyToArrayBuffer(buffer, byteOffset, byteLength);
}
function copyToArrayBuffer(buffer, byteOffset = 0, byteLength = buffer.byteLength - byteOffset) {
const view = new Uint8Array(buffer, byteOffset, byteLength);
const copy = new Uint8Array(view.length);
copy.set(view);
return copy.buffer;
}
// ../images/src/lib/category-api/image-type.ts
var parseImageNode = globalThis.loaders?.parseImageNode;
var IMAGE_SUPPORTED = typeof Image !== "undefined";
var IMAGE_BITMAP_SUPPORTED = typeof ImageBitmap !== "undefined";
var NODE_IMAGE_SUPPORTED = Boolean(parseImageNode);
var DATA_SUPPORTED = isBrowser ? true : NODE_IMAGE_SUPPORTED;
function isImageTypeSupported(type) {
switch (type) {
case "auto":
return IMAGE_BITMAP_SUPPORTED || IMAGE_SUPPORTED || DATA_SUPPORTED;
case "imagebitmap":
return IMAGE_BITMAP_SUPPORTED;
case "image":
return IMAGE_SUPPORTED;
case "data":
return DATA_SUPPORTED;
default:
throw new Error(`@loaders.gl/images: image ${type} not supported in this environment`);
}
}
function getDefaultImageType() {
if (IMAGE_BITMAP_SUPPORTED) {
return "imagebitmap";
}
if (IMAGE_SUPPORTED) {
return "image";
}
if (DATA_SUPPORTED) {
return "data";
}
throw new Error("Install '@loaders.gl/polyfills' to parse images under Node.js");
}
// ../images/src/lib/category-api/parsed-image-api.ts
function getImageType(image) {
const format = getImageTypeOrNull(image);
if (!format) {
throw new Error("Not an image");
}
return format;
}
function getImageData(image) {
switch (getImageType(image)) {
case "data":
return image;
case "image":
case "imagebitmap":
const canvas = document.createElement("canvas");
const context = canvas.getContext("2d");
if (!context) {
throw new Error("getImageData");
}
canvas.width = image.width;
canvas.height = image.height;
context.drawImage(image, 0, 0);
return context.getImageData(0, 0, image.width, image.height);
default:
throw new Error("getImageData");
}
}
function getImageTypeOrNull(image) {
if (typeof ImageBitmap !== "undefined" && image instanceof ImageBitmap) {
return "imagebitmap";
}
if (typeof Image !== "undefined" && image instanceof Image) {
return "image";
}
if (image && typeof image === "object" && image.data && image.width && image.height) {
return "data";
}
return null;
}
// ../images/src/lib/parsers/svg-utils.ts
var SVG_DATA_URL_PATTERN = /^data:image\/svg\+xml/;
var SVG_URL_PATTERN = /\.svg((\?|#).*)?$/;
function isSVG(url) {
return url && (SVG_DATA_URL_PATTERN.test(url) || SVG_URL_PATTERN.test(url));
}
function getBlobOrSVGDataUrl(arrayBuffer, url) {
if (isSVG(url)) {
const textDecoder = new TextDecoder();
let xmlText = textDecoder.decode(arrayBuffer);
try {
if (typeof unescape === "function" && typeof encodeURIComponent === "function") {
xmlText = unescape(encodeURIComponent(xmlText));
}
} catch (error) {
throw new Error(error.message);
}
const src = `data:image/svg+xml;base64,${btoa(xmlText)}`;
return src;
}
return getBlob(arrayBuffer, url);
}
function getBlob(arrayBuffer, url) {
if (isSVG(url)) {
throw new Error("SVG cannot be parsed directly to imagebitmap");
}
return new Blob([new Uint8Array(arrayBuffer)]);
}
// ../images/src/lib/parsers/parse-to-image.ts
async function parseToImage(arrayBuffer, options, url) {
const blobOrDataUrl = getBlobOrSVGDataUrl(arrayBuffer, url);
const URL = self.URL || self.webkitURL;
const objectUrl = typeof blobOrDataUrl !== "string" && URL.createObjectURL(blobOrDataUrl);
try {
return await loadToImage(objectUrl || blobOrDataUrl, options);
} finally {
if (objectUrl) {
URL.revokeObjectURL(objectUrl);
}
}
}
async function loadToImage(url, options) {
const image = new Image();
image.src = url;
if (options.image && options.image.decode && image.decode) {
await image.decode();
return image;
}
return await new Promise((resolve, reject) => {
try {
image.onload = () => resolve(image);
image.onerror = (error) => {
const message = error instanceof Error ? error.message : "error";
reject(new Error(message));
};
} catch (error) {
reject(error);
}
});
}
// ../images/src/lib/parsers/parse-to-image-bitmap.ts
var imagebitmapOptionsSupported = true;
async function parseToImageBitmap(arrayBuffer, options, url) {
let blob;
if (isSVG(url)) {
const image = await parseToImage(arrayBuffer, options, url);
blob = image;
} else {
blob = getBlob(arrayBuffer, url);
}
const imagebitmapOptions = options && options.imagebitmap;
return await safeCreateImageBitmap(blob, imagebitmapOptions);
}
async function safeCreateImageBitmap(blob, imagebitmapOptions = null) {
if (isEmptyObject(imagebitmapOptions) || !imagebitmapOptionsSupported) {
imagebitmapOptions = null;
}
if (imagebitmapOptions) {
try {
return await createImageBitmap(blob, imagebitmapOptions);
} catch (error) {
console.warn(error);
imagebitmapOptionsSupported = false;
}
}
return await createImageBitmap(blob);
}
function isEmptyObject(object) {
if (!object) {
return true;
}
for (const key in object) {
if (Object.prototype.hasOwnProperty.call(object, key)) {
return false;
}
}
return true;
}
// ../images/src/lib/category-api/parse-isobmff-binary.ts
function getISOBMFFMediaType(buffer) {
if (!checkString(buffer, "ftyp", 4)) {
return null;
}
if ((buffer[8] & 96) === 0) {
return null;
}
return decodeMajorBrand(buffer);
}
function decodeMajorBrand(buffer) {
const brandMajor = getUTF8String(buffer, 8, 12).replace("\0", " ").trim();
switch (brandMajor) {
case "avif":
case "avis":
return { extension: "avif", mimeType: "image/avif" };
default:
return null;
}
}
function getUTF8String(array, start, end) {
return String.fromCharCode(...array.slice(start, end));
}
function stringToBytes(string) {
return [...string].map((character) => character.charCodeAt(0));
}
function checkString(buffer, header, offset = 0) {
const headerBytes = stringToBytes(header);
for (let i = 0; i < headerBytes.length; ++i) {
if (headerBytes[i] !== buffer[i + offset]) {
return false;
}
}
return true;
}
// ../images/src/lib/category-api/binary-image-api.ts
var BIG_ENDIAN = false;
var LITTLE_ENDIAN = true;
function getBinaryImageMetadata(binaryData) {
const dataView = toDataView(binaryData);
return getPngMetadata(dataView) || getJpegMetadata(dataView) || getGifMetadata(dataView) || getBmpMetadata(dataView) || getISOBMFFMetadata(dataView);
}
function getISOBMFFMetadata(binaryData) {
const buffer = new Uint8Array(binaryData instanceof DataView ? binaryData.buffer : binaryData);
const mediaType = getISOBMFFMediaType(buffer);
if (!mediaType) {
return null;
}
return {
mimeType: mediaType.mimeType,
// TODO - decode width and height
width: 0,
height: 0
};
}
function getPngMetadata(binaryData) {
const dataView = toDataView(binaryData);
const isPng = dataView.byteLength >= 24 && dataView.getUint32(0, BIG_ENDIAN) === 2303741511;
if (!isPng) {
return null;
}
return {
mimeType: "image/png",
width: dataView.getUint32(16, BIG_ENDIAN),
height: dataView.getUint32(20, BIG_ENDIAN)
};
}
function getGifMetadata(binaryData) {
const dataView = toDataView(binaryData);
const isGif = dataView.byteLength >= 10 && dataView.getUint32(0, BIG_ENDIAN) === 1195984440;
if (!isGif) {
return null;
}
return {
mimeType: "image/gif",
width: dataView.getUint16(6, LITTLE_ENDIAN),
height: dataView.getUint16(8, LITTLE_ENDIAN)
};
}
function getBmpMetadata(binaryData) {
const dataView = toDataView(binaryData);
const isBmp = dataView.byteLength >= 14 && dataView.getUint16(0, BIG_ENDIAN) === 16973 && dataView.getUint32(2, LITTLE_ENDIAN) === dataView.byteLength;
if (!isBmp) {
return null;
}
return {
mimeType: "image/bmp",
width: dataView.getUint32(18, LITTLE_ENDIAN),
height: dataView.getUint32(22, LITTLE_ENDIAN)
};
}
function getJpegMetadata(binaryData) {
const dataView = toDataView(binaryData);
const isJpeg = dataView.byteLength >= 3 && dataView.getUint16(0, BIG_ENDIAN) === 65496 && dataView.getUint8(2) === 255;
if (!isJpeg) {
return null;
}
const { tableMarkers, sofMarkers } = getJpegMarkers();
let i = 2;
while (i + 9 < dataView.byteLength) {
const marker = dataView.getUint16(i, BIG_ENDIAN);
if (sofMarkers.has(marker)) {
return {
mimeType: "image/jpeg",
height: dataView.getUint16(i + 5, BIG_ENDIAN),
// Number of lines
width: dataView.getUint16(i + 7, BIG_ENDIAN)
// Number of pixels per line
};
}
if (!tableMarkers.has(marker)) {
return null;
}
i += 2;
i += dataView.getUint16(i, BIG_ENDIAN);
}
return null;
}
function getJpegMarkers() {
const tableMarkers = /* @__PURE__ */ new Set([65499, 65476, 65484, 65501, 65534]);
for (let i = 65504; i < 65520; ++i) {
tableMarkers.add(i);
}
const sofMarkers = /* @__PURE__ */ new Set([
65472,
65473,
65474,
65475,
65477,
65478,
65479,
65481,
65482,
65483,
65485,
65486,
65487,
65502
]);
return { tableMarkers, sofMarkers };
}
function toDataView(data) {
if (data instanceof DataView) {
return data;
}
if (ArrayBuffer.isView(data)) {
return new DataView(data.buffer);
}
if (data instanceof ArrayBuffer) {
return new DataView(data);
}
throw new Error("toDataView");
}
// ../images/src/lib/parsers/parse-to-node-image.ts
async function parseToNodeImage(arrayBuffer, options) {
const { mimeType } = getBinaryImageMetadata(arrayBuffer) || {};
const parseImageNode2 = globalThis.loaders?.parseImageNode;
assert(parseImageNode2);
return await parseImageNode2(arrayBuffer, mimeType);
}
// ../images/src/lib/parsers/parse-image.ts
async function parseImage(arrayBuffer, options, context) {
options = options || {};
const imageOptions = options.image || {};
const imageType = imageOptions.type || "auto";
const { url } = context || {};
const loadType = getLoadableImageType(imageType);
let image;
switch (loadType) {
case "imagebitmap":
image = await parseToImageBitmap(arrayBuffer, options, url);
break;
case "image":
image = await parseToImage(arrayBuffer, options, url);
break;
case "data":
image = await parseToNodeImage(arrayBuffer, options);
break;
default:
assert(false);
}
if (imageType === "data") {
image = getImageData(image);
}
return image;
}
function getLoadableImageType(type) {
switch (type) {
case "auto":
case "data":
return getDefaultImageType();
default:
isImageTypeSupported(type);
return type;
}
}
// ../images/src/image-loader.ts
var EXTENSIONS = ["png", "jpg", "jpeg", "gif", "webp", "bmp", "ico", "svg", "avif"];
var MIME_TYPES = [
"image/png",
"image/jpeg",
"image/gif",
"image/webp",
"image/avif",
"image/bmp",
"image/vnd.microsoft.icon",
"image/svg+xml"
];
var DEFAULT_IMAGE_LOADER_OPTIONS = {
image: {
type: "auto",
decode: true
// if format is HTML
}
// imagebitmap: {} - passes (platform dependent) parameters to ImageBitmap constructor
};
var ImageLoader = {
dataType: null,
batchType: null,
id: "image",
module: "images",
name: "Images",
version: VERSION,
mimeTypes: MIME_TYPES,
extensions: EXTENSIONS,
parse: parseImage,
// TODO: byteOffset, byteLength;
tests: [(arrayBuffer) => Boolean(getBinaryImageMetadata(new DataView(arrayBuffer)))],
options: DEFAULT_IMAGE_LOADER_OPTIONS
};
// ../images/src/lib/category-api/image-format.ts
var mimeTypeSupportedSync = {};
function isImageFormatSupported(mimeType) {
if (mimeTypeSupportedSync[mimeType] === void 0) {
const supported = isBrowser ? checkBrowserImageFormatSupport(mimeType) : checkNodeImageFormatSupport(mimeType);
mimeTypeSupportedSync[mimeType] = supported;
}
return mimeTypeSupportedSync[mimeType];
}
function checkNodeImageFormatSupport(mimeType) {
const NODE_FORMAT_SUPPORT = ["image/png", "image/jpeg", "image/gif"];
const imageFormatsNode = globalThis.loaders?.imageFormatsNode || NODE_FORMAT_SUPPORT;
const parseImageNode2 = globalThis.loaders?.parseImageNode;
return Boolean(parseImageNode2) && imageFormatsNode.includes(mimeType);
}
function checkBrowserImageFormatSupport(mimeType) {
switch (mimeType) {
case "image/avif":
case "image/webp":
return testBrowserImageFormatSupport(mimeType);
default:
return true;
}
}
function testBrowserImageFormatSupport(mimeType) {
try {
const element = document.createElement("canvas");
const dataURL = element.toDataURL(mimeType);
return dataURL.indexOf(`data:${mimeType}`) === 0;
} catch {
return false;
}
}
// src/lib/utils/assert.ts
function assert3(condition, message) {
if (!condition) {
throw new Error(message || "assert failed: gltf");
}
}
// src/lib/gltf-utils/gltf-constants.ts
var COMPONENTS = {
SCALAR: 1,
VEC2: 2,
VEC3: 3,
VEC4: 4,
MAT2: 4,
MAT3: 9,
MAT4: 16
};
var BYTES = {
5120: 1,
// BYTE
5121: 1,
// UNSIGNED_BYTE
5122: 2,
// SHORT
5123: 2,
// UNSIGNED_SHORT
5125: 4,
// UNSIGNED_INT
5126: 4
// FLOAT
};
// src/lib/gltf-utils/gltf-utils.ts
var MIPMAP_FACTOR = 1.33;
var TYPES = ["SCALAR", "VEC2", "VEC3", "VEC4"];
var ARRAY_CONSTRUCTOR_TO_WEBGL_CONSTANT = [
[Int8Array, 5120],
[Uint8Array, 5121],
[Int16Array, 5122],
[Uint16Array, 5123],
[Uint32Array, 5125],
[Float32Array, 5126],
[Float64Array, 5130]
];
var ARRAY_TO_COMPONENT_TYPE = new Map(
ARRAY_CONSTRUCTOR_TO_WEBGL_CONSTANT
);
var ATTRIBUTE_TYPE_TO_COMPONENTS = {
SCALAR: 1,
VEC2: 2,
VEC3: 3,
VEC4: 4,
MAT2: 4,
MAT3: 9,
MAT4: 16
};
var ATTRIBUTE_COMPONENT_TYPE_TO_BYTE_SIZE = {
5120: 1,
5121: 1,
5122: 2,
5123: 2,
5125: 4,
5126: 4
};
var ATTRIBUTE_COMPONENT_TYPE_TO_ARRAY = {
5120: Int8Array,
5121: Uint8Array,
5122: Int16Array,
5123: Uint16Array,
5125: Uint32Array,
5126: Float32Array
};
function getAccessorTypeFromSize(size) {
const type = TYPES[size - 1];
return type || TYPES[0];
}
function getComponentTypeFromArray(typedArray) {
const componentType = ARRAY_TO_COMPONENT_TYPE.get(typedArray.constructor);
if (!componentType) {
throw new Error("Illegal typed array");
}
return componentType;
}
function getAccessorArrayTypeAndLength(accessor, bufferView) {
const ArrayType = ATTRIBUTE_COMPONENT_TYPE_TO_ARRAY[accessor.componentType];
const components = ATTRIBUTE_TYPE_TO_COMPONENTS[accessor.type];
const bytesPerComponent = ATTRIBUTE_COMPONENT_TYPE_TO_BYTE_SIZE[accessor.componentType];
const length = accessor.count * components;
const byteLength = accessor.count * components * bytesPerComponent;
assert3(byteLength >= 0 && byteLength <= bufferView.byteLength);
const componentByteSize = BYTES[accessor.componentType];
const numberOfComponentsInElement = COMPONENTS[accessor.type];
return { ArrayType, length, byteLength, componentByteSize, numberOfComponentsInElement };
}
function getMemoryUsageGLTF(gltf) {
let { images, bufferViews } = gltf;
images = images || [];
bufferViews = bufferViews || [];
const imageBufferViews = images.map((i) => i.bufferView);
bufferViews = bufferViews.filter((view) => !imageBufferViews.includes(view));
const bufferMemory = bufferViews.reduce((acc, view) => acc + view.byteLength, 0);
const pixelCount = images.reduce((acc, image) => {
const { width, height } = image.image;
return acc + width * height;
}, 0);
return bufferMemory + Math.ceil(4 * pixelCount * MIPMAP_FACTOR);
}
// src/lib/gltf-utils/get-typed-array.ts
function getTypedArrayForBufferView(json, buffers, bufferViewIndex) {
const bufferView = json.bufferViews[bufferViewIndex];
assert3(bufferView);
const bufferIndex = bufferView.buffer;
const binChunk = buffers[bufferIndex];
assert3(binChunk);
const byteOffset = (bufferView.byteOffset || 0) + binChunk.byteOffset;
return new Uint8Array(binChunk.arrayBuffer, byteOffset, bufferView.byteLength);
}
function getTypedArrayForAccessor(json, buffers, accessor) {
const gltfAccessor = typeof accessor === "number" ? json.accessors?.[accessor] : accessor;
if (!gltfAccessor) {
throw new Error(`No gltf accessor ${JSON.stringify(accessor)}`);
}
const bufferView = json.bufferViews?.[gltfAccessor.bufferView || 0];
if (!bufferView) {
throw new Error(`No gltf buffer view for accessor ${bufferView}`);
}
const { arrayBuffer, byteOffset: bufferByteOffset } = buffers[bufferView.buffer];
const byteOffset = (bufferByteOffset || 0) + (gltfAccessor.byteOffset || 0) + (bufferView.byteOffset || 0);
const { ArrayType, length, componentByteSize, numberOfComponentsInElement } = getAccessorArrayTypeAndLength(gltfAccessor, bufferView);
const elementByteSize = componentByteSize * numberOfComponentsInElement;
const elementAddressScale = bufferView.byteStride || elementByteSize;
if (typeof bufferView.byteStride === "undefined" || bufferView.byteStride === elementByteSize) {
const result2 = new ArrayType(arrayBuffer, byteOffset, length);
return result2;
}
const result = new ArrayType(length);
for (let i = 0; i < gltfAccessor.count; i++) {
const values = new ArrayType(
arrayBuffer,
byteOffset + i * elementAddressScale,
numberOfComponentsInElement
);
result.set(values, i * numberOfComponentsInElement);
}
return result;
}
// src/lib/api/gltf-scenegraph.ts
function makeDefaultGLTFJson() {
return {
asset: {
version: "2.0",
generator: "loaders.gl"
},
buffers: [],
extensions: {},
extensionsRequired: [],
extensionsUsed: []
};
}
var GLTFScenegraph = class {
// internal
gltf;
sourceBuffers;
byteLength;
// TODO - why is this not GLTFWithBuffers - what happens to images?
constructor(gltf) {
this.gltf = {
json: gltf?.json || makeDefaultGLTFJson(),
buffers: gltf?.buffers || [],
images: gltf?.images || []
};
this.sourceBuffers = [];
this.byteLength = 0;
if (this.gltf.buffers && this.gltf.buffers[0]) {
this.byteLength = this.gltf.buffers[0].byteLength;
this.sourceBuffers = [this.gltf.buffers[0]];
}
}
// Accessors
get json() {
return this.gltf.json;
}
getApplicationData(key) {
const data = this.json[key];
return data;
}
getExtraData(key) {
const extras = this.json.extras || {};
return extras[key];
}
hasExtension(extensionName) {
const isUsedExtension = this.getUsedExtensions().find((name12) => name12 === extensionName);
const isRequiredExtension = this.getRequiredExtensions().find((name12) => name12 === extensionName);
return typeof isUsedExtension === "string" || typeof isRequiredExtension === "string";
}
getExtension(extensionName) {
const isExtension = this.getUsedExtensions().find((name12) => name12 === extensionName);
const extensions = this.json.extensions || {};
return isExtension ? extensions[extensionName] : null;
}
getRequiredExtension(extensionName) {
const isRequired = this.getRequiredExtensions().find((name12) => name12 === extensionName);
return isRequired ? this.getExtension(extensionName) : null;
}
getRequiredExtensions() {
return this.json.extensionsRequired || [];
}
getUsedExtensions() {
return this.json.extensionsUsed || [];
}
getRemovedExtensions() {
return this.json.extensionsRemoved || [];
}
getObjectExtension(object, extensionName) {
const extensions = object.extensions || {};
return extensions[extensionName];
}
getScene(index) {
return this.getObject("scenes", index);
}
getNode(index) {
return this.getObject("nodes", index);
}
getSkin(index) {
return this.getObject("skins", index);
}
getMesh(index) {
return this.getObject("meshes", index);
}
getMaterial(index) {
return this.getObject("materials", index);
}
getAccessor(index) {
return this.getObject("accessors", index);
}
// getCamera(index: number): object | null {
// return null; // TODO: fix thi: object as null;
// }
getTexture(index) {
return this.getObject("textures", index);
}
getSampler(index) {
return this.getObject("samplers", index);
}
getImage(index) {
return this.getObject("images", index);
}
getBufferView(index) {
return this.getObject("bufferViews", index);
}
getBuffer(index) {
return this.getObject("buffers", index);
}
getObject(array, index) {
if (typeof index === "object") {
return index;
}
const object = this.json[array] && this.json[array][index];
if (!object) {
throw new Error(`glTF file error: Could not find ${array}[${index}]`);
}
return object;
}
/**
* Accepts buffer view index or buffer view object
* @returns a `Uint8Array`
*/
getTypedArrayForBufferView(bufferView) {
bufferView = this.getBufferView(bufferView);
const bufferIndex = bufferView.buffer;
const binChunk = this.gltf.buffers[bufferIndex];
assert3(binChunk);
const byteOffset = (bufferView.byteOffset || 0) + binChunk.byteOffset;
return new Uint8Array(binChunk.arrayBuffer, byteOffset, bufferView.byteLength);
}
/** Accepts accessor index or accessor object
* @returns a typed array with type that matches the types
*/
getTypedArrayForAccessor(accessor) {
const gltfAccessor = this.getAccessor(accessor);
return getTypedArrayForAccessor(this.gltf.json, this.gltf.buffers, gltfAccessor);
}
/** accepts accessor index or accessor object
* returns a `Uint8Array`
*/
getTypedArrayForImageData(image) {
image = this.getAccessor(image);
const bufferView = this.getBufferView(image.bufferView);
const buffer = this.getBuffer(bufferView.buffer);
const arrayBuffer = buffer.data;
const byteOffset = bufferView.byteOffset || 0;
return new Uint8Array(arrayBuffer, byteOffset, bufferView.byteLength);
}
// MODIFERS
/**
* Add an extra application-defined key to the top-level data structure
*/
addApplicationData(key, data) {
this.json[key] = data;
return this;
}
/**
* `extras` - Standard GLTF field for storing application specific data
*/
addExtraData(key, data) {
this.json.extras = this.json.extras || {};
this.json.extras[key] = data;
return this;
}
addObjectExtension(object, extensionName, data) {
object.extensions = object.extensions || {};
object.extensions[extensionName] = data;
this.registerUsedExtension(extensionName);
return this;
}
setObjectExtension(object, extensionName, data) {
const extensions = object.extensions || {};
extensions[extensionName] = data;
}
removeObjectExtension(object, extensionName) {
const extensions = object?.extensions || {};
if (extensions[extensionName]) {
this.json.extensionsRemoved = this.json.extensionsRemoved || [];
const extensionsRemoved = this.json.extensionsRemoved;
if (!extensionsRemoved.includes(extensionName)) {
extensionsRemoved.push(extensionName);
}
}
delete extensions[extensionName];
}
/**
* Add to standard GLTF top level extension object, mark as used
*/
addExtension(extensionName, extensionData = {}) {
assert3(extensionData);
this.json.extensions = this.json.extensions || {};
this.json.extensions[extensionName] = extensionData;
this.registerUsedExtension(extensionName);
return extensionData;
}
/**
* Standard GLTF top level extension object, mark as used and required
*/
addRequiredExtension(extensionName, extensionData = {}) {
assert3(extensionData);
this.addExtension(extensionName, extensionData);
this.registerRequiredExtension(extensionName);
return extensionData;
}
/**
* Add extensionName to list of used extensions
*/
registerUsedExtension(extensionName) {
this.json.extensionsUsed = this.json.extensionsUsed || [];
if (!this.json.extensionsUsed.find((ext) => ext === extensionName)) {
this.json.extensionsUsed.push(extensionName);
}
}
/**
* Add extensionName to list of required extensions
*/
registerRequiredExtension(extensionName) {
this.registerUsedExtension(extensionName);
this.json.extensionsRequired = this.json.extensionsRequired || [];
if (!this.json.extensionsRequired.find((ext) => ext === extensionName)) {
this.json.extensionsRequired.push(extensionName);
}
}
/**
* Removes an extension from the top-level list
*/
removeExtension(extensionName) {
if (this.json.extensions?.[extensionName]) {
this.json.extensionsRemoved = this.json.extensionsRemoved || [];
const extensionsRemoved = this.json.extensionsRemoved;
if (!extensionsRemoved.includes(extensionName)) {
extensionsRemoved.push(extensionName);
}
}
if (this.json.extensions) {
delete this.json.extensions[extensionName];
}
if (this.json.extensionsRequired) {
this._removeStringFromArray(this.json.extensionsRequired, extensionName);
}
if (this.json.extensionsUsed) {
this._removeStringFromArray(this.json.extensionsUsed, extensionName);
}
}
/**
* Set default scene which is to be displayed at load time
*/
setDefaultScene(sceneIndex) {
this.json.scene = sceneIndex;
}
/**
* @todo: add more properties for scene initialization:
* name`, `extensions`, `extras`
* https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#reference-scene
*/
addScene(scene) {
const { nodeIndices } = scene;
this.json.scenes = this.json.scenes || [];
this.json.scenes.push({ nodes: nodeIndices });
return this.json.scenes.length - 1;
}
/**
* @todo: add more properties for node initialization:
* `name`, `extensions`, `extras`, `camera`, `children`, `skin`, `rotation`, `scale`, `translation`, `weights`
* https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#node
*/
addNode(node) {
const { meshIndex, matrix } = node;
this.json.nodes = this.json.nodes || [];
const nodeData = { mesh: meshIndex };
if (matrix) {
nodeData.matrix = matrix;
}
this.json.nodes.push(nodeData);
return this.json.nodes.length - 1;
}
/** Adds a mesh to the json part */
addMesh(mesh) {
const { attributes, indices, material, mode = 4 } = mesh;
const accessors = this._addAttributes(attributes);
const glTFMesh = {
primitives: [
{
attributes: accessors,
mode
}
]
};
if (indices) {
const indicesAccessor = this._addIndices(indices);
glTFMesh.primitives[0].indices = indicesAccessor;
}
if (Number.isFinite(material)) {
glTFMesh.primitives[0].material = material;
}
this.json.meshes = this.json.meshes || [];
this.json.meshes.push(glTFMesh);
return this.json.meshes.length - 1;
}
addPointCloud(attributes) {
const accessorIndices = this._addAttributes(attributes);
const glTFMesh = {
primitives: [
{
attributes: accessorIndices,
mode: 0
// GL.POINTS
}
]
};
this.json.meshes = this.json.meshes || [];
this.json.meshes.push(glTFMesh);
return this.json.meshes.length - 1;
}
/**
* Adds a binary image. Builds glTF "JSON metadata" and saves buffer reference
* Buffer will be copied into BIN chunk during "pack"
* Currently encodes as glTF image
* @param imageData
* @param mimeType
*/
addImage(imageData, mimeTypeOpt) {
const metadata = getBinaryImageMetadata(imageData);
const mimeType = mimeTypeOpt || metadata?.mimeType;
const bufferViewIndex = this.addBufferView(imageData);
const glTFImage = {
bufferView: bufferViewIndex,
mimeType
};
this.json.images = this.json.images || [];
this.json.images.push(glTFImage);
return this.json.images.length - 1;
}
/**
* Add one untyped source buffer, create a matching glTF `bufferView`, and return its index
* @param buffer
*/
addBufferView(buffer, bufferIndex = 0, byteOffset = this.byteLength) {
const byteLength = buffer.byteLength;
assert3(Number.isFinite(byteLength));
this.sourceBuffers = this.sourceBuffers || [];
this.sourceBuffers.push(buffer);
const glTFBufferView = {
buffer: bufferIndex,
// Write offset from the start of the binary body
byteOffset,
byteLength
};
this.byteLength += padToNBytes(byteLength, 4);
this.json.bufferViews = this.json.bufferViews || [];
this.json.bufferViews.push(glTFBufferView);
return this.json.bufferViews.length - 1;
}
/**
* Adds an accessor to a bufferView
* @param bufferViewIndex
* @param accessor
*/
addAccessor(bufferViewIndex, accessor) {
const glTFAccessor = {
bufferView: bufferViewIndex,
// @ts-ignore
type: getAccessorTypeFromSize(accessor.size),
// @ts-ignore
componentType: accessor.componentType,
// @ts-ignore
count: accessor.count,
// @ts-ignore
max: accessor.max,
// @ts-ignore
min: accessor.min
};
this.json.accessors = this.json.accessors || [];
this.json.accessors.push(glTFAccessor);
return this.json.accessors.length - 1;
}
/**
* Add a binary buffer. Builds glTF "JSON metadata" and saves buffer reference
* Buffer will be copied into BIN chunk during "pack"
* Currently encodes buffers as glTF accessors, but this could be optimized
* @param sourceBuffer
* @param accessor
*/
addBinaryBuffer(sourceBuffer, accessor = { size: 3 }) {
const bufferViewIndex = this.addBufferView(sourceBuffer);
let minMax = { min: accessor.min, max: accessor.max };
if (!minMax.min || !minMax.max) {
minMax = this._getAccessorMinMax(sourceBuffer, accessor.size);
}
const accessorDefaults = {
// @ts-ignore
size: accessor.size,
componentType: getComponentTypeFromArray(sourceBuffer),
// @ts-ignore
count: Math.round(sourceBuffer.length / accessor.size),
min: minMax.min,
max: minMax.max
};
return this.addAccessor(bufferViewIndex, Object.assign(accessorDefaults, accessor));
}
/**
* Adds a texture to the json part
* @todo: add more properties for texture initialization
* `sampler`, `name`, `extensions`, `extras`
* https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#texture
*/
addTexture(texture) {
const { imageIndex } = texture;
const glTFTexture = {
source: imageIndex
};
this.json.textures = this.json.textures || [];
this.json.textures.push(glTFTexture);
return this.json.textures.length - 1;
}
/** Adds a material to the json part */
addMaterial(pbrMaterialInfo) {
this.json.materials = this.json.materials || [];
this.json.materials.push(pbrMaterialInfo);
return this.json.materials.length - 1;
}
/** Pack the binary chunk */
createBinaryChunk() {
const totalByteLength = this.byteLength;
const arrayBuffer = new ArrayBuffer(totalByteLength);
const targetArray = new Uint8Array(arrayBuffer);
let dstByteOffset = 0;
for (const sourceBuffer of this.sourceBuffers || []) {
dstByteOffset = copyToArray(sourceBuffer, targetArray, dstByteOffset);
}
if (this.json?.buffers?.[0]) {
this.json.buffers[0].byteLength = totalByteLength;
} else {
this.json.buffers = [{ byteLength: totalByteLength }];
}
this.gltf.binary = arrayBuffer;
this.sourceBuffers = [arrayBuffer];
this.gltf.buffers = [{ arrayBuffer, byteOffset: 0, byteLength: arrayBuffer.byteLength }];
}
// PRIVATE
_removeStringFromArray(array, string) {
let found = true;
while (found) {
const index = array.indexOf(string);
if (index > -1) {
array.splice(index, 1);
} else {
found = false;
}
}
}
/**
* Add attributes to buffers and create `attributes` object which is part of `mesh`
*/
_addAttributes(attributes = {}) {
const result = {};
for (const attributeKey in attributes) {
const attributeData = attributes[attributeKey];
const attrName = this._getGltfAttributeName(attributeKey);
const accessor = this.addBinaryBuffer(attributeData.value, attributeData);
result[attrName] = accessor;
}
return result;
}
/*