@forgeax/engine-fbx 0.1.4 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -17
- package/dist/.tsbuildinfo +1 -1
- package/dist/fbx-importer.d.ts +1 -2
- package/dist/fbx-importer.d.ts.map +1 -1
- package/dist/index.mjs +129 -891
- package/dist/index.mjs.map +1 -1
- package/dist/parse-material.d.ts +1 -7
- package/dist/parse-material.d.ts.map +1 -1
- package/dist/parse-texture.d.ts +1 -5
- package/dist/parse-texture.d.ts.map +1 -1
- package/dist/to-asset-pack.d.ts +1 -8
- package/dist/to-asset-pack.d.ts.map +1 -1
- package/package.json +11 -12
- package/pkg/fbx-wasm.wasm +0 -0
- package/scripts/content-key.mjs +6 -5
- package/scripts/fetch-ufbx.mjs +15 -3
- package/scripts/ufbx-source.lock.json +15 -0
- package/src/__tests__/blendshape-import.integration.test.ts +12 -18
- package/src/__tests__/fbx-importer.test.ts +1 -165
- package/src/__tests__/index.test.ts +1 -0
- package/src/fbx-importer.ts +10 -236
- package/src/native/bridge.c +0 -118
- package/src/parse-material.ts +1 -49
- package/src/parse-texture.ts +3 -17
- package/src/to-asset-pack.ts +19 -90
- package/dist/__tests__/asset-runtime-fixture.d.ts +0 -9
- package/dist/__tests__/asset-runtime-fixture.d.ts.map +0 -1
- package/src/__tests__/asset-runtime-fixture.ts +0 -251
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
import { ImportError, IMPORT_ERROR_HINTS,
|
|
1
|
+
import { reconcileMeshMaterialSlotTopology, ImportError, IMPORT_ERROR_HINTS, resolveMeshMaterialSlotDefaultGuid, err, ok } from '@forgeax/engine-types';
|
|
2
2
|
import { deriveAnimationTargetId } from '@forgeax/engine-animation/target-id';
|
|
3
|
+
import '@forgeax/engine-pack';
|
|
3
4
|
import { box3 } from '@forgeax/engine-math';
|
|
5
|
+
import { AssetGuid } from '@forgeax/engine-pack/guid';
|
|
6
|
+
import { MESH_BIN_HEADER_V4_BYTES, writeMeshBinHeader } from '@forgeax/engine-pack/mesh-bin-contract';
|
|
4
7
|
|
|
5
8
|
// src/errors.ts
|
|
6
9
|
var fbxErrorPolicy = {
|
|
@@ -242,20 +245,6 @@ function phongRoughness(shininess, maxGloss = 100) {
|
|
|
242
245
|
return clamp(1 - Math.sqrt(shininess / maxGloss), 0, 1);
|
|
243
246
|
}
|
|
244
247
|
function parseMaterial(raw, sourceIndex) {
|
|
245
|
-
const textureBindings = raw.textureBindings?.map((binding) => ({
|
|
246
|
-
slot: binding.slot,
|
|
247
|
-
textureIndex: binding.textureIndex,
|
|
248
|
-
...binding.texCoord === void 0 ? {} : { texCoord: binding.texCoord }
|
|
249
|
-
}));
|
|
250
|
-
const textureFields = textureBindings === void 0 || textureBindings.length === 0 ? {} : {
|
|
251
|
-
textureBindings,
|
|
252
|
-
...Object.fromEntries(
|
|
253
|
-
textureBindings.map((binding) => [
|
|
254
|
-
binding.slot === "baseColorTexture" ? "baseColorTextureIndex" : binding.slot === "metallicRoughnessTexture" ? "metallicRoughnessTextureIndex" : binding.slot === "normalTexture" ? "normalTextureIndex" : binding.slot === "occlusionTexture" ? "occlusionTextureIndex" : binding.slot === "emissiveTexture" ? "emissiveTextureIndex" : "specularTintTextureIndex",
|
|
255
|
-
binding.textureIndex
|
|
256
|
-
])
|
|
257
|
-
)
|
|
258
|
-
};
|
|
259
248
|
switch (raw.kind) {
|
|
260
249
|
case "stingray-pbs": {
|
|
261
250
|
const sp = raw.stingrayProps ?? {};
|
|
@@ -268,8 +257,7 @@ function parseMaterial(raw, sourceIndex) {
|
|
|
268
257
|
1
|
|
269
258
|
],
|
|
270
259
|
metallicFactor: sp.metallic ?? 0,
|
|
271
|
-
roughnessFactor: sp.roughness ?? 0.5
|
|
272
|
-
...textureFields
|
|
260
|
+
roughnessFactor: sp.roughness ?? 0.5
|
|
273
261
|
};
|
|
274
262
|
}
|
|
275
263
|
case "phong": {
|
|
@@ -279,8 +267,7 @@ function parseMaterial(raw, sourceIndex) {
|
|
|
279
267
|
...raw.name !== void 0 && { name: raw.name },
|
|
280
268
|
baseColorFactor: [d[0] ?? 0.5, d[1] ?? 0.5, d[2] ?? 0.5, 1],
|
|
281
269
|
metallicFactor: 0,
|
|
282
|
-
roughnessFactor: phongRoughness(gloss)
|
|
283
|
-
...textureFields
|
|
270
|
+
roughnessFactor: phongRoughness(gloss)
|
|
284
271
|
};
|
|
285
272
|
}
|
|
286
273
|
case "lambert": {
|
|
@@ -289,17 +276,15 @@ function parseMaterial(raw, sourceIndex) {
|
|
|
289
276
|
...raw.name !== void 0 && { name: raw.name },
|
|
290
277
|
baseColorFactor: [d[0] ?? 0.5, d[1] ?? 0.5, d[2] ?? 0.5, 1],
|
|
291
278
|
metallicFactor: 0,
|
|
292
|
-
roughnessFactor: 0.5
|
|
279
|
+
roughnessFactor: 0.5
|
|
293
280
|
// lambert has no specular → default roughness
|
|
294
|
-
...textureFields
|
|
295
281
|
};
|
|
296
282
|
}
|
|
297
283
|
default: {
|
|
298
284
|
return {
|
|
299
285
|
baseColorFactor: [0.5, 0.5, 0.5, 1],
|
|
300
286
|
metallicFactor: 0,
|
|
301
|
-
roughnessFactor: 0.5
|
|
302
|
-
...textureFields
|
|
287
|
+
roughnessFactor: 0.5
|
|
303
288
|
};
|
|
304
289
|
}
|
|
305
290
|
}
|
|
@@ -510,511 +495,14 @@ function parseSkin(doc) {
|
|
|
510
495
|
}
|
|
511
496
|
|
|
512
497
|
// src/parse-texture.ts
|
|
513
|
-
function normalizePath(path) {
|
|
514
|
-
return path.replaceAll("\\", "/");
|
|
515
|
-
}
|
|
516
498
|
function parseTextures(raw) {
|
|
517
499
|
const textures = raw.textures ?? [];
|
|
518
500
|
return textures.map((t) => ({
|
|
519
|
-
name: t.name ?? t.filePath
|
|
520
|
-
filePath:
|
|
521
|
-
...t.relativeFilePath === void 0 ? {} : { relativeFilePath: normalizePath(t.relativeFilePath) },
|
|
522
|
-
...t.absoluteFilePath === void 0 ? {} : { absoluteFilePath: t.absoluteFilePath },
|
|
523
|
-
...t.embeddedBytes === void 0 ? {} : { embeddedBytes: Uint8Array.from(t.embeddedBytes) },
|
|
524
|
-
...t.type === void 0 ? {} : { type: t.type },
|
|
501
|
+
name: t.name ?? t.filePath,
|
|
502
|
+
filePath: t.filePath,
|
|
525
503
|
sourceIndex: t.sourceIndex
|
|
526
504
|
}));
|
|
527
505
|
}
|
|
528
|
-
|
|
529
|
-
// src/resolve-texture-path.ts
|
|
530
|
-
function normalizeSourceRelativePath(raw) {
|
|
531
|
-
const value = raw.replaceAll("\\", "/");
|
|
532
|
-
if (value.startsWith("/") || /^[A-Za-z]:\//.test(value) || value.startsWith("//"))
|
|
533
|
-
return void 0;
|
|
534
|
-
const parts = [];
|
|
535
|
-
for (const part of value.split("/")) {
|
|
536
|
-
if (part === "" || part === ".") continue;
|
|
537
|
-
if (part === "..") {
|
|
538
|
-
if (parts.at(-1) !== void 0 && parts.at(-1) !== "..") parts.pop();
|
|
539
|
-
else parts.push("..");
|
|
540
|
-
} else {
|
|
541
|
-
parts.push(part);
|
|
542
|
-
}
|
|
543
|
-
}
|
|
544
|
-
return parts.join("/");
|
|
545
|
-
}
|
|
546
|
-
function requestSegments(raw) {
|
|
547
|
-
const value = raw.replaceAll("\\", "/").replace(/^[A-Za-z]:\//, "").replace(/^\/+/, "");
|
|
548
|
-
return value.split("/").filter((part) => part !== "" && part !== ".");
|
|
549
|
-
}
|
|
550
|
-
function uniqueMatches(paths, predicate) {
|
|
551
|
-
return [...new Set(paths.filter(predicate))];
|
|
552
|
-
}
|
|
553
|
-
function longestSuffixLength(request, candidate) {
|
|
554
|
-
let count = 0;
|
|
555
|
-
while (count < request.length && count < candidate.length && request[request.length - 1 - count]?.toLowerCase() === candidate[candidate.length - 1 - count]?.toLowerCase()) {
|
|
556
|
-
count++;
|
|
557
|
-
}
|
|
558
|
-
return count;
|
|
559
|
-
}
|
|
560
|
-
function resolveFbxTexturePath(sourcePath, request, candidates) {
|
|
561
|
-
const candidatePaths = candidates.flatMap((candidate) => {
|
|
562
|
-
const normalized = normalizeSourceRelativePath(candidate.relativePath);
|
|
563
|
-
return normalized === void 0 || normalized.length === 0 ? [] : [normalized];
|
|
564
|
-
});
|
|
565
|
-
const requestedPath = request.declaredRelativePath ?? request.declaredFilename ?? request.declaredAbsolutePath ?? "";
|
|
566
|
-
const choose = (matches, strategy) => {
|
|
567
|
-
const unique = [...new Set(matches)];
|
|
568
|
-
if (unique.length === 1) {
|
|
569
|
-
const relativePath = unique[0];
|
|
570
|
-
if (relativePath === void 0) return void 0;
|
|
571
|
-
return {
|
|
572
|
-
ok: true,
|
|
573
|
-
relativePath,
|
|
574
|
-
readUri: relativePath,
|
|
575
|
-
strategy
|
|
576
|
-
};
|
|
577
|
-
}
|
|
578
|
-
if (unique.length > 1) {
|
|
579
|
-
return {
|
|
580
|
-
ok: false,
|
|
581
|
-
code: "fbx-external-texture-ambiguous",
|
|
582
|
-
requestedPath,
|
|
583
|
-
candidates: unique
|
|
584
|
-
};
|
|
585
|
-
}
|
|
586
|
-
return void 0;
|
|
587
|
-
};
|
|
588
|
-
const relativeRequest = request.declaredRelativePath ?? request.declaredFilename;
|
|
589
|
-
if (relativeRequest !== void 0 && !/^(?:[A-Za-z]:[\\/]|[\\/])/.test(relativeRequest)) {
|
|
590
|
-
const normalizedRequest = normalizeSourceRelativePath(relativeRequest);
|
|
591
|
-
if (normalizedRequest !== void 0) {
|
|
592
|
-
const scopeExact = choose(
|
|
593
|
-
uniqueMatches(candidatePaths, (path) => path === normalizedRequest),
|
|
594
|
-
"exact"
|
|
595
|
-
);
|
|
596
|
-
if (scopeExact !== void 0) return scopeExact;
|
|
597
|
-
const scopeFolded = choose(
|
|
598
|
-
uniqueMatches(
|
|
599
|
-
candidatePaths,
|
|
600
|
-
(path) => path.toLowerCase() === normalizedRequest.toLowerCase()
|
|
601
|
-
),
|
|
602
|
-
"case-folded"
|
|
603
|
-
);
|
|
604
|
-
if (scopeFolded !== void 0) return scopeFolded;
|
|
605
|
-
}
|
|
606
|
-
}
|
|
607
|
-
const requestedSegments = requestSegments(requestedPath);
|
|
608
|
-
if (requestedSegments.length > 0) {
|
|
609
|
-
const scored = candidatePaths.map((path) => ({
|
|
610
|
-
path,
|
|
611
|
-
score: longestSuffixLength(requestedSegments, path.split("/"))
|
|
612
|
-
}));
|
|
613
|
-
const bestScore = Math.max(0, ...scored.map((entry) => entry.score));
|
|
614
|
-
if (bestScore > 0) {
|
|
615
|
-
const best = scored.filter((entry) => entry.score === bestScore).map((entry) => entry.path);
|
|
616
|
-
const strategy = bestScore === 1 ? "basename" : "suffix";
|
|
617
|
-
const suffix = choose(best, strategy);
|
|
618
|
-
if (suffix !== void 0) return suffix;
|
|
619
|
-
}
|
|
620
|
-
}
|
|
621
|
-
return {
|
|
622
|
-
ok: false,
|
|
623
|
-
code: "fbx-external-texture-missing",
|
|
624
|
-
requestedPath,
|
|
625
|
-
candidates: candidatePaths
|
|
626
|
-
};
|
|
627
|
-
}
|
|
628
|
-
|
|
629
|
-
// ../../node_modules/.pnpm/uuidv7@1.2.1/node_modules/uuidv7/dist/index.js
|
|
630
|
-
var DIGITS = "0123456789abcdef";
|
|
631
|
-
var UUID = class _UUID {
|
|
632
|
-
/** @param bytes - The 16-byte byte array representation. */
|
|
633
|
-
constructor(bytes) {
|
|
634
|
-
this.bytes = bytes;
|
|
635
|
-
}
|
|
636
|
-
/**
|
|
637
|
-
* Creates an object from the internal representation, a 16-byte byte array
|
|
638
|
-
* containing the binary UUID representation in the big-endian byte order.
|
|
639
|
-
*
|
|
640
|
-
* This method does NOT shallow-copy the argument, and thus the created object
|
|
641
|
-
* holds the reference to the underlying buffer.
|
|
642
|
-
*
|
|
643
|
-
* @throws TypeError if the length of the argument is not 16.
|
|
644
|
-
*/
|
|
645
|
-
static ofInner(bytes) {
|
|
646
|
-
if (bytes.length !== 16) {
|
|
647
|
-
throw new TypeError("not 128-bit length");
|
|
648
|
-
} else {
|
|
649
|
-
return new _UUID(bytes);
|
|
650
|
-
}
|
|
651
|
-
}
|
|
652
|
-
/**
|
|
653
|
-
* Builds a byte array from UUIDv7 field values.
|
|
654
|
-
*
|
|
655
|
-
* @param unixTsMs - A 48-bit `unix_ts_ms` field value.
|
|
656
|
-
* @param randA - A 12-bit `rand_a` field value.
|
|
657
|
-
* @param randBHi - The higher 30 bits of 62-bit `rand_b` field value.
|
|
658
|
-
* @param randBLo - The lower 32 bits of 62-bit `rand_b` field value.
|
|
659
|
-
* @throws RangeError if any field value is out of the specified range.
|
|
660
|
-
*/
|
|
661
|
-
static fromFieldsV7(unixTsMs, randA, randBHi, randBLo) {
|
|
662
|
-
if (!Number.isInteger(unixTsMs) || !Number.isInteger(randA) || !Number.isInteger(randBHi) || !Number.isInteger(randBLo) || unixTsMs < 0 || randA < 0 || randBHi < 0 || randBLo < 0 || unixTsMs > 281474976710655 || randA > 4095 || randBHi > 1073741823 || randBLo > 4294967295) {
|
|
663
|
-
throw new RangeError("invalid field value");
|
|
664
|
-
}
|
|
665
|
-
const bytes = new Uint8Array(16);
|
|
666
|
-
bytes[0] = unixTsMs / 2 ** 40;
|
|
667
|
-
bytes[1] = unixTsMs / 2 ** 32;
|
|
668
|
-
bytes[2] = unixTsMs / 2 ** 24;
|
|
669
|
-
bytes[3] = unixTsMs / 2 ** 16;
|
|
670
|
-
bytes[4] = unixTsMs / 2 ** 8;
|
|
671
|
-
bytes[5] = unixTsMs;
|
|
672
|
-
bytes[6] = 112 | randA >>> 8;
|
|
673
|
-
bytes[7] = randA;
|
|
674
|
-
bytes[8] = 128 | randBHi >>> 24;
|
|
675
|
-
bytes[9] = randBHi >>> 16;
|
|
676
|
-
bytes[10] = randBHi >>> 8;
|
|
677
|
-
bytes[11] = randBHi;
|
|
678
|
-
bytes[12] = randBLo >>> 24;
|
|
679
|
-
bytes[13] = randBLo >>> 16;
|
|
680
|
-
bytes[14] = randBLo >>> 8;
|
|
681
|
-
bytes[15] = randBLo;
|
|
682
|
-
return new _UUID(bytes);
|
|
683
|
-
}
|
|
684
|
-
/**
|
|
685
|
-
* Builds a byte array from a string representation.
|
|
686
|
-
*
|
|
687
|
-
* This method accepts the following formats:
|
|
688
|
-
*
|
|
689
|
-
* - 32-digit hexadecimal format without hyphens: `0189dcd553117d408db09496a2eef37b`
|
|
690
|
-
* - 8-4-4-4-12 hyphenated format: `0189dcd5-5311-7d40-8db0-9496a2eef37b`
|
|
691
|
-
* - Hyphenated format with surrounding braces: `{0189dcd5-5311-7d40-8db0-9496a2eef37b}`
|
|
692
|
-
* - RFC 9562 URN format: `urn:uuid:0189dcd5-5311-7d40-8db0-9496a2eef37b`
|
|
693
|
-
*
|
|
694
|
-
* Leading and trailing whitespaces represents an error.
|
|
695
|
-
*
|
|
696
|
-
* @throws SyntaxError if the argument could not parse as a valid UUID string.
|
|
697
|
-
*/
|
|
698
|
-
static parse(uuid) {
|
|
699
|
-
var _a, _b, _c, _d;
|
|
700
|
-
let hex = void 0;
|
|
701
|
-
switch (uuid.length) {
|
|
702
|
-
case 32:
|
|
703
|
-
hex = (_a = /^[0-9a-f]{32}$/i.exec(uuid)) === null || _a === void 0 ? void 0 : _a[0];
|
|
704
|
-
break;
|
|
705
|
-
case 36:
|
|
706
|
-
hex = (_b = /^([0-9a-f]{8})-([0-9a-f]{4})-([0-9a-f]{4})-([0-9a-f]{4})-([0-9a-f]{12})$/i.exec(uuid)) === null || _b === void 0 ? void 0 : _b.slice(1, 6).join("");
|
|
707
|
-
break;
|
|
708
|
-
case 38:
|
|
709
|
-
hex = (_c = /^\{([0-9a-f]{8})-([0-9a-f]{4})-([0-9a-f]{4})-([0-9a-f]{4})-([0-9a-f]{12})\}$/i.exec(uuid)) === null || _c === void 0 ? void 0 : _c.slice(1, 6).join("");
|
|
710
|
-
break;
|
|
711
|
-
case 45:
|
|
712
|
-
hex = (_d = /^urn:uuid:([0-9a-f]{8})-([0-9a-f]{4})-([0-9a-f]{4})-([0-9a-f]{4})-([0-9a-f]{12})$/i.exec(uuid)) === null || _d === void 0 ? void 0 : _d.slice(1, 6).join("");
|
|
713
|
-
break;
|
|
714
|
-
}
|
|
715
|
-
if (hex) {
|
|
716
|
-
const inner = new Uint8Array(16);
|
|
717
|
-
for (let i = 0; i < 16; i += 4) {
|
|
718
|
-
const n = parseInt(hex.substring(2 * i, 2 * i + 8), 16);
|
|
719
|
-
inner[i + 0] = n >>> 24;
|
|
720
|
-
inner[i + 1] = n >>> 16;
|
|
721
|
-
inner[i + 2] = n >>> 8;
|
|
722
|
-
inner[i + 3] = n;
|
|
723
|
-
}
|
|
724
|
-
return new _UUID(inner);
|
|
725
|
-
} else {
|
|
726
|
-
throw new SyntaxError("could not parse UUID string");
|
|
727
|
-
}
|
|
728
|
-
}
|
|
729
|
-
/**
|
|
730
|
-
* @returns The 8-4-4-4-12 canonical hexadecimal string representation
|
|
731
|
-
* (`0189dcd5-5311-7d40-8db0-9496a2eef37b`).
|
|
732
|
-
*/
|
|
733
|
-
toString() {
|
|
734
|
-
let text = "";
|
|
735
|
-
for (let i = 0; i < this.bytes.length; i++) {
|
|
736
|
-
text += DIGITS.charAt(this.bytes[i] >>> 4);
|
|
737
|
-
text += DIGITS.charAt(this.bytes[i] & 15);
|
|
738
|
-
if (i === 3 || i === 5 || i === 7 || i === 9) {
|
|
739
|
-
text += "-";
|
|
740
|
-
}
|
|
741
|
-
}
|
|
742
|
-
return text;
|
|
743
|
-
}
|
|
744
|
-
/**
|
|
745
|
-
* @returns The 32-digit hexadecimal representation without hyphens
|
|
746
|
-
* (`0189dcd553117d408db09496a2eef37b`).
|
|
747
|
-
*/
|
|
748
|
-
toHex() {
|
|
749
|
-
let text = "";
|
|
750
|
-
for (let i = 0; i < this.bytes.length; i++) {
|
|
751
|
-
text += DIGITS.charAt(this.bytes[i] >>> 4);
|
|
752
|
-
text += DIGITS.charAt(this.bytes[i] & 15);
|
|
753
|
-
}
|
|
754
|
-
return text;
|
|
755
|
-
}
|
|
756
|
-
/** @returns The 8-4-4-4-12 canonical hexadecimal string representation. */
|
|
757
|
-
toJSON() {
|
|
758
|
-
return this.toString();
|
|
759
|
-
}
|
|
760
|
-
/**
|
|
761
|
-
* Reports the variant field value of the UUID or, if appropriate, "NIL" or
|
|
762
|
-
* "MAX".
|
|
763
|
-
*
|
|
764
|
-
* For convenience, this method reports "NIL" or "MAX" if `this` represents
|
|
765
|
-
* the Nil or Max UUID, although the Nil and Max UUIDs are technically
|
|
766
|
-
* subsumed under the variants `0b0` and `0b111`, respectively.
|
|
767
|
-
*/
|
|
768
|
-
getVariant() {
|
|
769
|
-
const n = this.bytes[8] >>> 4;
|
|
770
|
-
if (n < 0) {
|
|
771
|
-
throw new Error("unreachable");
|
|
772
|
-
} else if (n <= 7) {
|
|
773
|
-
return this.isNil() ? "NIL" : "VAR_0";
|
|
774
|
-
} else if (n <= 11) {
|
|
775
|
-
return "VAR_10";
|
|
776
|
-
} else if (n <= 13) {
|
|
777
|
-
return "VAR_110";
|
|
778
|
-
} else if (n <= 15) {
|
|
779
|
-
return this.isMax() ? "MAX" : "VAR_RESERVED";
|
|
780
|
-
} else {
|
|
781
|
-
throw new Error("unreachable");
|
|
782
|
-
}
|
|
783
|
-
}
|
|
784
|
-
/**
|
|
785
|
-
* Returns the version field value of the UUID or `undefined` if the UUID does
|
|
786
|
-
* not have the variant field value of `0b10`.
|
|
787
|
-
*/
|
|
788
|
-
getVersion() {
|
|
789
|
-
return this.getVariant() === "VAR_10" ? this.bytes[6] >>> 4 : void 0;
|
|
790
|
-
}
|
|
791
|
-
/** Returns `true` if `this` is the Nil UUID. */
|
|
792
|
-
isNil() {
|
|
793
|
-
return this.bytes.every((e) => e === 0);
|
|
794
|
-
}
|
|
795
|
-
/** Returns `true` if `this` is the Max UUID. */
|
|
796
|
-
isMax() {
|
|
797
|
-
return this.bytes.every((e) => e === 255);
|
|
798
|
-
}
|
|
799
|
-
/** Creates an object from `this`. */
|
|
800
|
-
clone() {
|
|
801
|
-
return new _UUID(this.bytes.slice(0));
|
|
802
|
-
}
|
|
803
|
-
/** Returns true if `this` is equivalent to `other`. */
|
|
804
|
-
equals(other) {
|
|
805
|
-
return this.compareTo(other) === 0;
|
|
806
|
-
}
|
|
807
|
-
/**
|
|
808
|
-
* Returns a negative integer, zero, or positive integer if `this` is less
|
|
809
|
-
* than, equal to, or greater than `other`, respectively.
|
|
810
|
-
*/
|
|
811
|
-
compareTo(other) {
|
|
812
|
-
for (let i = 0; i < 16; i++) {
|
|
813
|
-
const diff = this.bytes[i] - other.bytes[i];
|
|
814
|
-
if (diff !== 0) {
|
|
815
|
-
return Math.sign(diff);
|
|
816
|
-
}
|
|
817
|
-
}
|
|
818
|
-
return 0;
|
|
819
|
-
}
|
|
820
|
-
};
|
|
821
|
-
var V7Generator = class {
|
|
822
|
-
/**
|
|
823
|
-
* Creates a generator object with the default random number generator, or
|
|
824
|
-
* with the specified one if passed as an argument. The specified random
|
|
825
|
-
* number generator should be cryptographically strong and securely seeded.
|
|
826
|
-
*/
|
|
827
|
-
constructor(randomNumberGenerator) {
|
|
828
|
-
this.timestampBiased = 0;
|
|
829
|
-
this.counter = 0;
|
|
830
|
-
this.rollbackAllowance = 1e4;
|
|
831
|
-
this.random = randomNumberGenerator !== null && randomNumberGenerator !== void 0 ? randomNumberGenerator : getDefaultRandom();
|
|
832
|
-
}
|
|
833
|
-
/**
|
|
834
|
-
* Sets the `rollbackAllowance` parameter of the generator.
|
|
835
|
-
*
|
|
836
|
-
* The `rollbackAllowance` parameter specifies the amount of `unixTsMs`
|
|
837
|
-
* rollback that is considered significant. The default value is `10_000`
|
|
838
|
-
* (milliseconds). See the {@link generate} or {@link generateOrAbort}
|
|
839
|
-
* documentation for the treatment of the significant rollback.
|
|
840
|
-
*
|
|
841
|
-
*/
|
|
842
|
-
setRollbackAllowance(rollbackAllowance) {
|
|
843
|
-
if (rollbackAllowance < 0 || rollbackAllowance > 281474976710655) {
|
|
844
|
-
throw new RangeError("`rollbackAllowance` out of reasonable range");
|
|
845
|
-
}
|
|
846
|
-
this.rollbackAllowance = rollbackAllowance;
|
|
847
|
-
}
|
|
848
|
-
/**
|
|
849
|
-
* Generates a new UUIDv7 object from the current timestamp, or resets the
|
|
850
|
-
* generator upon significant timestamp rollback.
|
|
851
|
-
*
|
|
852
|
-
* This method returns a monotonically increasing UUID by reusing the previous
|
|
853
|
-
* timestamp even if the up-to-date timestamp is smaller than the immediately
|
|
854
|
-
* preceding UUID's. However, when such a clock rollback is considered
|
|
855
|
-
* significant (by default, more than ten seconds), this method resets the
|
|
856
|
-
* generator and returns a new UUID based on the given timestamp, breaking the
|
|
857
|
-
* increasing order of UUIDs.
|
|
858
|
-
*
|
|
859
|
-
* See {@link generateOrAbort} for the other mode of generation and
|
|
860
|
-
* {@link generateOrResetWithTs} for the variant accepting a custom timestamp.
|
|
861
|
-
*/
|
|
862
|
-
generate() {
|
|
863
|
-
return this.generateOrResetWithTs(Date.now());
|
|
864
|
-
}
|
|
865
|
-
/**
|
|
866
|
-
* Generates a new UUIDv7 object from the current timestamp, or returns
|
|
867
|
-
* `undefined` upon significant timestamp rollback.
|
|
868
|
-
*
|
|
869
|
-
* This method returns a monotonically increasing UUID by reusing the previous
|
|
870
|
-
* timestamp even if the up-to-date timestamp is smaller than the immediately
|
|
871
|
-
* preceding UUID's. However, when such a clock rollback is considered
|
|
872
|
-
* significant (by default, more than ten seconds), this method aborts and
|
|
873
|
-
* returns `undefined` immediately.
|
|
874
|
-
*
|
|
875
|
-
* See {@link generate} for the other mode of generation and
|
|
876
|
-
* {@link generateOrAbortWithTs} for the variant accepting a custom timestamp.
|
|
877
|
-
*/
|
|
878
|
-
generateOrAbort() {
|
|
879
|
-
return this.generateOrAbortWithTs(Date.now());
|
|
880
|
-
}
|
|
881
|
-
/**
|
|
882
|
-
* Generates a new UUIDv7 object from the `unixTsMs` passed, or resets the
|
|
883
|
-
* generator upon significant timestamp rollback.
|
|
884
|
-
*
|
|
885
|
-
* This method is equivalent to {@link generate} except that it takes a custom
|
|
886
|
-
* timestamp.
|
|
887
|
-
*
|
|
888
|
-
* @throws RangeError if `unixTsMs` is not a 48-bit unsigned integer.
|
|
889
|
-
*/
|
|
890
|
-
generateOrResetWithTs(unixTsMs) {
|
|
891
|
-
let value = this.generateOrAbortWithTs(unixTsMs);
|
|
892
|
-
if (value === void 0) {
|
|
893
|
-
this.timestampBiased = 0;
|
|
894
|
-
value = this.generateOrAbortWithTs(unixTsMs);
|
|
895
|
-
}
|
|
896
|
-
return value;
|
|
897
|
-
}
|
|
898
|
-
/**
|
|
899
|
-
* Generates a new UUIDv7 object from the `unixTsMs` passed, or returns
|
|
900
|
-
* `undefined` upon significant timestamp rollback.
|
|
901
|
-
*
|
|
902
|
-
* This method is equivalent to {@link generateOrAbort} except that it takes a
|
|
903
|
-
* custom timestamp.
|
|
904
|
-
*
|
|
905
|
-
* @throws RangeError if `unixTsMs` is not a 48-bit unsigned integer.
|
|
906
|
-
*/
|
|
907
|
-
generateOrAbortWithTs(unixTsMs) {
|
|
908
|
-
const MAX_COUNTER = 4398046511103;
|
|
909
|
-
if (!Number.isInteger(unixTsMs) || unixTsMs < 0 || unixTsMs > 281474976710655) {
|
|
910
|
-
throw new RangeError("`unixTsMs` must be a 48-bit unsigned integer");
|
|
911
|
-
}
|
|
912
|
-
unixTsMs++;
|
|
913
|
-
if (unixTsMs > this.timestampBiased) {
|
|
914
|
-
this.timestampBiased = unixTsMs;
|
|
915
|
-
this.resetCounter();
|
|
916
|
-
} else if (unixTsMs + this.rollbackAllowance >= this.timestampBiased) {
|
|
917
|
-
this.counter++;
|
|
918
|
-
if (this.counter > MAX_COUNTER) {
|
|
919
|
-
this.timestampBiased++;
|
|
920
|
-
this.resetCounter();
|
|
921
|
-
}
|
|
922
|
-
} else {
|
|
923
|
-
return void 0;
|
|
924
|
-
}
|
|
925
|
-
return UUID.fromFieldsV7(this.timestampBiased - 1, Math.trunc(this.counter / 2 ** 30), this.counter & 2 ** 30 - 1, this.random.nextUint32());
|
|
926
|
-
}
|
|
927
|
-
/**
|
|
928
|
-
* Generates a new UUIDv7 object from the `unixTsMs` passed, or resets the
|
|
929
|
-
* generator upon significant timestamp rollback.
|
|
930
|
-
*
|
|
931
|
-
* This method is a deprecated version of {@link generateOrResetWithTs} that
|
|
932
|
-
* accepts the `rollbackAllowance` parameter as an argument, rather than using
|
|
933
|
-
* the generator-level parameter.
|
|
934
|
-
*
|
|
935
|
-
* @param rollbackAllowance - The amount of `unixTsMs` rollback that is
|
|
936
|
-
* considered significant. A suggested value is `10_000` (milliseconds).
|
|
937
|
-
* @throws RangeError if `unixTsMs` is not a 48-bit unsigned integer.
|
|
938
|
-
* @deprecated Since v1.2.0. Use {@link generateOrResetWithTs} instead.
|
|
939
|
-
*/
|
|
940
|
-
generateOrResetCore(unixTsMs, rollbackAllowance) {
|
|
941
|
-
const origRollbackAllowance = this.rollbackAllowance;
|
|
942
|
-
try {
|
|
943
|
-
this.setRollbackAllowance(rollbackAllowance);
|
|
944
|
-
return this.generateOrResetWithTs(unixTsMs);
|
|
945
|
-
} catch (e) {
|
|
946
|
-
throw e;
|
|
947
|
-
} finally {
|
|
948
|
-
this.rollbackAllowance = origRollbackAllowance;
|
|
949
|
-
}
|
|
950
|
-
}
|
|
951
|
-
/**
|
|
952
|
-
* Generates a new UUIDv7 object from the `unixTsMs` passed, or returns
|
|
953
|
-
* `undefined` upon significant timestamp rollback.
|
|
954
|
-
*
|
|
955
|
-
* This method is a deprecated version of {@link generateOrAbortWithTs} that
|
|
956
|
-
* accepts the `rollbackAllowance` parameter as an argument, rather than using
|
|
957
|
-
* the generator-level parameter.
|
|
958
|
-
*
|
|
959
|
-
* @param rollbackAllowance - The amount of `unixTsMs` rollback that is
|
|
960
|
-
* considered significant. A suggested value is `10_000` (milliseconds).
|
|
961
|
-
* @throws RangeError if `unixTsMs` is not a 48-bit unsigned integer.
|
|
962
|
-
* @deprecated Since v1.2.0. Use {@link generateOrAbortWithTs} instead.
|
|
963
|
-
*/
|
|
964
|
-
generateOrAbortCore(unixTsMs, rollbackAllowance) {
|
|
965
|
-
const origRollbackAllowance = this.rollbackAllowance;
|
|
966
|
-
try {
|
|
967
|
-
this.setRollbackAllowance(rollbackAllowance);
|
|
968
|
-
return this.generateOrAbortWithTs(unixTsMs);
|
|
969
|
-
} catch (e) {
|
|
970
|
-
throw e;
|
|
971
|
-
} finally {
|
|
972
|
-
this.rollbackAllowance = origRollbackAllowance;
|
|
973
|
-
}
|
|
974
|
-
}
|
|
975
|
-
/** Initializes the counter at a 42-bit random integer. */
|
|
976
|
-
resetCounter() {
|
|
977
|
-
this.counter = this.random.nextUint32() * 1024 + (this.random.nextUint32() & 1023);
|
|
978
|
-
}
|
|
979
|
-
/**
|
|
980
|
-
* Generates a new UUIDv4 object utilizing the random number generator inside.
|
|
981
|
-
*
|
|
982
|
-
* @internal
|
|
983
|
-
*/
|
|
984
|
-
generateV4() {
|
|
985
|
-
const bytes = new Uint8Array(Uint32Array.of(this.random.nextUint32(), this.random.nextUint32(), this.random.nextUint32(), this.random.nextUint32()).buffer);
|
|
986
|
-
bytes[6] = 64 | bytes[6] >>> 4;
|
|
987
|
-
bytes[8] = 128 | bytes[8] >>> 2;
|
|
988
|
-
return UUID.ofInner(bytes);
|
|
989
|
-
}
|
|
990
|
-
};
|
|
991
|
-
var getDefaultRandom = () => {
|
|
992
|
-
if (typeof crypto !== "undefined" && typeof crypto.getRandomValues !== "undefined") {
|
|
993
|
-
return new BufferedCryptoRandom();
|
|
994
|
-
} else {
|
|
995
|
-
if (typeof UUIDV7_DENY_WEAK_RNG !== "undefined" && UUIDV7_DENY_WEAK_RNG) {
|
|
996
|
-
throw new Error("no cryptographically strong RNG available");
|
|
997
|
-
}
|
|
998
|
-
return {
|
|
999
|
-
nextUint32: () => Math.trunc(Math.random() * 65536) * 65536 + Math.trunc(Math.random() * 65536)
|
|
1000
|
-
};
|
|
1001
|
-
}
|
|
1002
|
-
};
|
|
1003
|
-
var BufferedCryptoRandom = class {
|
|
1004
|
-
constructor() {
|
|
1005
|
-
this.buffer = new Uint32Array(8);
|
|
1006
|
-
this.cursor = 65535;
|
|
1007
|
-
}
|
|
1008
|
-
nextUint32() {
|
|
1009
|
-
if (this.cursor >= this.buffer.length) {
|
|
1010
|
-
crypto.getRandomValues(this.buffer);
|
|
1011
|
-
this.cursor = 0;
|
|
1012
|
-
}
|
|
1013
|
-
return this.buffer[this.cursor++];
|
|
1014
|
-
}
|
|
1015
|
-
};
|
|
1016
|
-
var defaultGenerator;
|
|
1017
|
-
var uuidv7obj = () => (defaultGenerator || (defaultGenerator = new V7Generator())).generate();
|
|
1018
506
|
var ATTRIBUTE_FORMAT_MAP = {
|
|
1019
507
|
position: "float32x3",
|
|
1020
508
|
normal: "float32x3",
|
|
@@ -1084,117 +572,6 @@ function deriveVertexLayoutProjection(map) {
|
|
|
1084
572
|
digest: fnv1a(digestInput)
|
|
1085
573
|
});
|
|
1086
574
|
}
|
|
1087
|
-
|
|
1088
|
-
// ../pack/dist/guid.mjs
|
|
1089
|
-
var PackError = class extends Error {
|
|
1090
|
-
code;
|
|
1091
|
-
expected;
|
|
1092
|
-
hint;
|
|
1093
|
-
detail;
|
|
1094
|
-
constructor(args) {
|
|
1095
|
-
super(`[PackError ${args.code}] expected: ${args.expected}; hint: ${args.hint}`);
|
|
1096
|
-
this.name = "PackError";
|
|
1097
|
-
this.code = args.code;
|
|
1098
|
-
this.expected = args.expected;
|
|
1099
|
-
this.hint = args.hint;
|
|
1100
|
-
this.detail = args.detail;
|
|
1101
|
-
}
|
|
1102
|
-
};
|
|
1103
|
-
function brand(bytes) {
|
|
1104
|
-
return bytes;
|
|
1105
|
-
}
|
|
1106
|
-
var HEX_BYTE = Array.from({ length: 256 }, (_, i) => i.toString(16).padStart(2, "0"));
|
|
1107
|
-
function bytesToDashForm(bytes) {
|
|
1108
|
-
const h = HEX_BYTE;
|
|
1109
|
-
return `${h[bytes[0]]}${h[bytes[1]]}${h[bytes[2]]}${h[bytes[3]]}-${h[bytes[4]]}${h[bytes[5]]}-${h[bytes[6]]}${h[bytes[7]]}-${h[bytes[8]]}${h[bytes[9]]}-${h[bytes[10]]}${h[bytes[11]]}${h[bytes[12]]}${h[bytes[13]]}${h[bytes[14]]}${h[bytes[15]]}`;
|
|
1110
|
-
}
|
|
1111
|
-
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
1112
|
-
function isValidAssetGuidString(value) {
|
|
1113
|
-
return typeof value === "string" && UUID_RE.test(value);
|
|
1114
|
-
}
|
|
1115
|
-
function dashFormToBytes(dashForm) {
|
|
1116
|
-
const hex = dashForm.replace(/-/g, "");
|
|
1117
|
-
const bytes = new Uint8Array(16);
|
|
1118
|
-
for (let i = 0; i < 16; i++) {
|
|
1119
|
-
bytes[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
|
|
1120
|
-
}
|
|
1121
|
-
return bytes;
|
|
1122
|
-
}
|
|
1123
|
-
var AssetGuid = {
|
|
1124
|
-
/**
|
|
1125
|
-
* Parse a 36-char RFC 4122 dash-form UUID string into an AssetGuid.
|
|
1126
|
-
* Returns Ok(AssetGuid) on success or Err(PackError) with code 'pack-guid-malformed' on failure.
|
|
1127
|
-
* Never throws for expected failures (requirements §4.2 / §14 / charter proposition 4).
|
|
1128
|
-
*/
|
|
1129
|
-
parse(dashForm) {
|
|
1130
|
-
if (!isValidAssetGuidString(dashForm)) {
|
|
1131
|
-
return {
|
|
1132
|
-
ok: false,
|
|
1133
|
-
error: new PackError({
|
|
1134
|
-
code: "pack-guid-malformed",
|
|
1135
|
-
expected: "36-char RFC 4122 dash-form UUID",
|
|
1136
|
-
hint: "use AssetGuid.random() or a UUIDv7 generator; all GUID fields must be 36-char RFC 4122 dash-form",
|
|
1137
|
-
detail: {
|
|
1138
|
-
raw: dashForm,
|
|
1139
|
-
reason: "expected 36-char RFC 4122 dash-form UUID"
|
|
1140
|
-
}
|
|
1141
|
-
})
|
|
1142
|
-
};
|
|
1143
|
-
}
|
|
1144
|
-
return { ok: true, value: brand(dashFormToBytes(dashForm)) };
|
|
1145
|
-
},
|
|
1146
|
-
/**
|
|
1147
|
-
* Format an AssetGuid as a 36-char RFC 4122 lowercase dash-form string.
|
|
1148
|
-
*/
|
|
1149
|
-
format(guid) {
|
|
1150
|
-
return bytesToDashForm(guid);
|
|
1151
|
-
},
|
|
1152
|
-
/**
|
|
1153
|
-
* Test byte-by-byte equality between two AssetGuids.
|
|
1154
|
-
*/
|
|
1155
|
-
equals(a, b) {
|
|
1156
|
-
for (let i = 0; i < 16; i++) {
|
|
1157
|
-
if (a[i] !== b[i]) return false;
|
|
1158
|
-
}
|
|
1159
|
-
return true;
|
|
1160
|
-
},
|
|
1161
|
-
/**
|
|
1162
|
-
* Mint a new time-ordered UUIDv7 as an AssetGuid.
|
|
1163
|
-
* Works in both Node.js and browser environments.
|
|
1164
|
-
*/
|
|
1165
|
-
random() {
|
|
1166
|
-
const uuid = uuidv7obj();
|
|
1167
|
-
const bytes = new Uint8Array(16);
|
|
1168
|
-
bytes.set(uuid.bytes);
|
|
1169
|
-
return brand(bytes);
|
|
1170
|
-
}
|
|
1171
|
-
};
|
|
1172
|
-
|
|
1173
|
-
// ../pack/dist/mesh-bin-contract.mjs
|
|
1174
|
-
var MESH_BIN_HEADER_V4_BYTES = 80;
|
|
1175
|
-
var MESH_BIN_DIGEST_BYTES = 32;
|
|
1176
|
-
function digestBytes(digest) {
|
|
1177
|
-
const bytes = new Uint8Array(MESH_BIN_DIGEST_BYTES);
|
|
1178
|
-
bytes.set(new TextEncoder().encode(digest).subarray(0, MESH_BIN_DIGEST_BYTES));
|
|
1179
|
-
return bytes;
|
|
1180
|
-
}
|
|
1181
|
-
function writeMeshBinHeader(header, out) {
|
|
1182
|
-
if (out.byteLength < MESH_BIN_HEADER_V4_BYTES) {
|
|
1183
|
-
throw new RangeError("mesh-bin v4 header output is truncated");
|
|
1184
|
-
}
|
|
1185
|
-
const view = new DataView(out.buffer, out.byteOffset, out.byteLength);
|
|
1186
|
-
view.setUint32(0, header.version, true);
|
|
1187
|
-
view.setUint32(4, header.projectionVersion, true);
|
|
1188
|
-
view.setUint32(8, header.mask, true);
|
|
1189
|
-
view.setUint32(12, header.stride, true);
|
|
1190
|
-
view.setUint32(16, header.vertexCount, true);
|
|
1191
|
-
view.setUint32(20, header.vertexBytes, true);
|
|
1192
|
-
view.setUint32(24, header.indexCount, true);
|
|
1193
|
-
view.setUint32(28, header.indexWidth, true);
|
|
1194
|
-
view.setUint32(32, header.indexBytes, true);
|
|
1195
|
-
view.setUint32(36, header.jsonBytes, true);
|
|
1196
|
-
out.set(digestBytes(header.digest), 48);
|
|
1197
|
-
}
|
|
1198
575
|
function failure(sourceKey, expected, actual) {
|
|
1199
576
|
return {
|
|
1200
577
|
code: "mesh-bin-payload-invalid",
|
|
@@ -1596,44 +973,7 @@ function buildMeshAsset(pod, guid, influences, materialContext = {}) {
|
|
|
1596
973
|
}
|
|
1597
974
|
};
|
|
1598
975
|
}
|
|
1599
|
-
function
|
|
1600
|
-
if (pod.textureBindings !== void 0) return pod.textureBindings;
|
|
1601
|
-
const bindings = [];
|
|
1602
|
-
const legacy = [
|
|
1603
|
-
["baseColorTexture", pod.baseColorTextureIndex],
|
|
1604
|
-
["metallicRoughnessTexture", pod.metallicRoughnessTextureIndex],
|
|
1605
|
-
["normalTexture", pod.normalTextureIndex],
|
|
1606
|
-
["specularTintTexture", pod.specularTintTextureIndex],
|
|
1607
|
-
["emissiveTexture", pod.emissiveTextureIndex],
|
|
1608
|
-
["occlusionTexture", pod.occlusionTextureIndex]
|
|
1609
|
-
];
|
|
1610
|
-
for (const [slot, textureIndex] of legacy) {
|
|
1611
|
-
if (textureIndex !== void 0) bindings.push({ slot, textureIndex });
|
|
1612
|
-
}
|
|
1613
|
-
return bindings;
|
|
1614
|
-
}
|
|
1615
|
-
function buildMaterialAsset(pod, guid, skinned = false, textureGuidByIndex = /* @__PURE__ */ new Map()) {
|
|
1616
|
-
const values = {
|
|
1617
|
-
baseColor: pod.baseColorFactor,
|
|
1618
|
-
metallic: pod.metallicFactor,
|
|
1619
|
-
roughness: pod.roughnessFactor
|
|
1620
|
-
};
|
|
1621
|
-
const refs = [];
|
|
1622
|
-
for (const binding of materialBindings(pod)) {
|
|
1623
|
-
const textureGuid = textureGuidByIndex.get(binding.textureIndex);
|
|
1624
|
-
if (textureGuid === void 0) continue;
|
|
1625
|
-
const textureValue = {
|
|
1626
|
-
// Runtime material loading interprets texture handles as indexes into
|
|
1627
|
-
// this asset's refs[]; the GUID is carried by the corresponding edge.
|
|
1628
|
-
texture: refs.length,
|
|
1629
|
-
...binding.texCoord === void 0 ? {} : { coordinates: { set: binding.texCoord } }
|
|
1630
|
-
};
|
|
1631
|
-
values[binding.slot] = textureValue;
|
|
1632
|
-
refs.push({
|
|
1633
|
-
guid: textureGuid,
|
|
1634
|
-
sourceField: { componentName: "<material>", fieldName: binding.slot }
|
|
1635
|
-
});
|
|
1636
|
-
}
|
|
976
|
+
function buildMaterialAsset(pod, guid, skinned = false) {
|
|
1637
977
|
const mat = {
|
|
1638
978
|
kind: "material",
|
|
1639
979
|
colorSpace: "linear",
|
|
@@ -1644,14 +984,18 @@ function buildMaterialAsset(pod, guid, skinned = false, textureGuidByIndex = /*
|
|
|
1644
984
|
renderState: { tags: { LightMode: "Forward" }, queue: 2e3 }
|
|
1645
985
|
}
|
|
1646
986
|
],
|
|
1647
|
-
values
|
|
987
|
+
values: {
|
|
988
|
+
baseColor: pod.baseColorFactor,
|
|
989
|
+
metallic: pod.metallicFactor,
|
|
990
|
+
roughness: pod.roughnessFactor
|
|
991
|
+
}
|
|
1648
992
|
};
|
|
1649
993
|
return {
|
|
1650
994
|
guid,
|
|
1651
995
|
kind: "material",
|
|
1652
996
|
...pod.name !== void 0 ? { name: pod.name } : {},
|
|
1653
997
|
payload: mat,
|
|
1654
|
-
refs,
|
|
998
|
+
refs: [],
|
|
1655
999
|
artifacts: {}
|
|
1656
1000
|
};
|
|
1657
1001
|
}
|
|
@@ -1721,29 +1065,19 @@ function buildSceneAsset(pod, guid, ctx) {
|
|
|
1721
1065
|
artifacts: {}
|
|
1722
1066
|
};
|
|
1723
1067
|
}
|
|
1724
|
-
function
|
|
1068
|
+
function buildTextureNote(_pod, _guid) {
|
|
1725
1069
|
return {
|
|
1726
|
-
guid,
|
|
1070
|
+
guid: _guid,
|
|
1727
1071
|
kind: "texture",
|
|
1728
|
-
...
|
|
1729
|
-
payload:
|
|
1072
|
+
..._pod.name !== void 0 ? { name: _pod.name } : {},
|
|
1073
|
+
payload: {},
|
|
1730
1074
|
refs: [],
|
|
1731
|
-
artifacts: {
|
|
1732
|
-
body: {
|
|
1733
|
-
mediaType: decoded.mediaType?.startsWith("image/ktx2") ? decoded.mediaType : "application/x-forgeax-rgba8",
|
|
1734
|
-
...decoded.assetCodec === void 0 ? {} : { assetCodec: decoded.assetCodec },
|
|
1735
|
-
bytes: decoded.bytes
|
|
1736
|
-
}
|
|
1737
|
-
}
|
|
1075
|
+
artifacts: {}
|
|
1738
1076
|
};
|
|
1739
1077
|
}
|
|
1740
1078
|
function toAssetPack(params) {
|
|
1741
1079
|
const assets = [];
|
|
1742
1080
|
const guidOf = makeGuidResolver(params.subAssets);
|
|
1743
|
-
const textureGuidByIndex = /* @__PURE__ */ new Map();
|
|
1744
|
-
for (const texture of params.subAssets) {
|
|
1745
|
-
if (texture.kind === "texture") textureGuidByIndex.set(texture.sourceIndex, texture.guid);
|
|
1746
|
-
}
|
|
1747
1081
|
const materialGuidByIndex = /* @__PURE__ */ new Map();
|
|
1748
1082
|
const materialNameByIndex = /* @__PURE__ */ new Map();
|
|
1749
1083
|
const materialSourceKeyByIndex = /* @__PURE__ */ new Map();
|
|
@@ -1793,19 +1127,11 @@ function toAssetPack(params) {
|
|
|
1793
1127
|
const mat = params.materials[i];
|
|
1794
1128
|
if (!mat) continue;
|
|
1795
1129
|
const guid = guidOf("material", i);
|
|
1796
|
-
if (guid !== void 0)
|
|
1797
|
-
assets.push(buildMaterialAsset(mat, guid, hasSkin, textureGuidByIndex));
|
|
1798
|
-
}
|
|
1130
|
+
if (guid !== void 0) assets.push(buildMaterialAsset(mat, guid, hasSkin));
|
|
1799
1131
|
}
|
|
1800
1132
|
for (const tex of params.textures) {
|
|
1801
1133
|
const guid = guidOf("texture", tex.sourceIndex);
|
|
1802
|
-
if (guid !== void 0)
|
|
1803
|
-
const decoded = params.decodedTextures?.get(tex.sourceIndex);
|
|
1804
|
-
if (decoded === void 0) {
|
|
1805
|
-
throw new Error(`fbx texture ${tex.sourceIndex} was declared but not decoded`);
|
|
1806
|
-
}
|
|
1807
|
-
assets.push(buildTextureAsset(tex, guid, decoded));
|
|
1808
|
-
}
|
|
1134
|
+
if (guid !== void 0) assets.push(buildTextureNote(tex, guid));
|
|
1809
1135
|
}
|
|
1810
1136
|
const declaredByKind = (kind) => params.subAssets.filter((s) => s.kind === kind).slice().sort((a, b) => a.sourceIndex - b.sourceIndex).map((s) => s.guid);
|
|
1811
1137
|
const meshGuids = declaredByKind("mesh");
|
|
@@ -1915,8 +1241,6 @@ function toAssetPack(params) {
|
|
|
1915
1241
|
function sourceKeyForFbxOutput(output) {
|
|
1916
1242
|
const kind = output.kind.trim();
|
|
1917
1243
|
if (kind.length === 0) return void 0;
|
|
1918
|
-
const explicit = output.sourceKey?.trim();
|
|
1919
|
-
if (explicit !== void 0 && explicit.length > 0) return explicit;
|
|
1920
1244
|
const name = output.name?.trim();
|
|
1921
1245
|
return name === void 0 || name.length === 0 ? `fbx:${kind}` : `fbx:${kind}:${name}`;
|
|
1922
1246
|
}
|
|
@@ -1936,96 +1260,18 @@ function deriveFbxSourceKeys(outputs) {
|
|
|
1936
1260
|
}
|
|
1937
1261
|
return { ok: true, keys };
|
|
1938
1262
|
}
|
|
1939
|
-
function validationError(ctx, textureIndex, code, actual, hint) {
|
|
1940
|
-
const diagnostic = {
|
|
1941
|
-
code,
|
|
1942
|
-
severity: "error",
|
|
1943
|
-
sourcePath: `${ctx.source}#textures[${textureIndex}]`,
|
|
1944
|
-
sourceRange: { start: 0, end: 0, line: 1, column: 1 },
|
|
1945
|
-
rule: "fbx-external-texture-dependency-closure",
|
|
1946
|
-
expected: "every bound FBX texture resolves to one readable supported image in the authorized scope",
|
|
1947
|
-
actual,
|
|
1948
|
-
hint
|
|
1949
|
-
};
|
|
1950
|
-
return new ImportError({
|
|
1951
|
-
code: "source-validation-failed",
|
|
1952
|
-
expected: diagnostic.expected,
|
|
1953
|
-
hint: IMPORT_ERROR_HINTS["source-validation-failed"],
|
|
1954
|
-
detail: { diagnostics: [diagnostic] }
|
|
1955
|
-
});
|
|
1956
|
-
}
|
|
1957
|
-
var FBX_PARSE_DIAGNOSTIC_MAX = 256;
|
|
1958
|
-
var FBX_PARSE_EXPECTED = "a readable FBX source that ufbx can parse";
|
|
1959
|
-
function boundedErrorMessage(error) {
|
|
1960
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
1961
|
-
return message.length <= FBX_PARSE_DIAGNOSTIC_MAX ? message : `${message.slice(0, FBX_PARSE_DIAGNOSTIC_MAX - 3)}...`;
|
|
1962
|
-
}
|
|
1963
|
-
function parseValidationError(ctx, error) {
|
|
1964
|
-
const actual = boundedErrorMessage(error);
|
|
1965
|
-
const diagnostic = {
|
|
1966
|
-
code: "fbx-source-parse-failed",
|
|
1967
|
-
severity: "error",
|
|
1968
|
-
sourcePath: ctx.source,
|
|
1969
|
-
sourceRange: { start: 0, end: 0, line: 1, column: 1 },
|
|
1970
|
-
rule: "fbx-source-parse",
|
|
1971
|
-
expected: FBX_PARSE_EXPECTED,
|
|
1972
|
-
actual,
|
|
1973
|
-
hint: "repair or re-export the FBX source, then retry the same importer and source path"
|
|
1974
|
-
};
|
|
1975
|
-
return new ImportError({
|
|
1976
|
-
code: "source-validation-failed",
|
|
1977
|
-
expected: FBX_PARSE_EXPECTED,
|
|
1978
|
-
actual,
|
|
1979
|
-
hint: IMPORT_ERROR_HINTS["source-validation-failed"],
|
|
1980
|
-
detail: { diagnostics: [diagnostic] }
|
|
1981
|
-
});
|
|
1982
|
-
}
|
|
1983
|
-
function importCandidates(ctx) {
|
|
1984
|
-
const raw = ctx.importSettings.fbxCandidatePaths;
|
|
1985
|
-
if (!Array.isArray(raw)) return [];
|
|
1986
|
-
return raw.flatMap(
|
|
1987
|
-
(path) => typeof path === "string" ? [{ relativePath: path }] : []
|
|
1988
|
-
);
|
|
1989
|
-
}
|
|
1990
|
-
function mimeForTexture(path, bytes) {
|
|
1991
|
-
const lower = path.toLowerCase();
|
|
1992
|
-
if (lower.endsWith(".png") || bytes[0] === 137 && bytes[1] === 80 && bytes[2] === 78) {
|
|
1993
|
-
return "image/png";
|
|
1994
|
-
}
|
|
1995
|
-
if (lower.endsWith(".jpg") || lower.endsWith(".jpeg") || bytes[0] === 255 && bytes[1] === 216) {
|
|
1996
|
-
return "image/jpeg";
|
|
1997
|
-
}
|
|
1998
|
-
return "image/x-tga";
|
|
1999
|
-
}
|
|
2000
|
-
function textureColorSpace(slot) {
|
|
2001
|
-
return slot === "normalTexture" || slot === "metallicRoughnessTexture" || slot === "occlusionTexture" ? "linear" : "srgb";
|
|
2002
|
-
}
|
|
2003
1263
|
var fbxImporter = {
|
|
2004
1264
|
key: "fbx",
|
|
2005
1265
|
async import(ctx) {
|
|
2006
1266
|
const read = await ctx.readSource();
|
|
2007
1267
|
if (!read.ok) {
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
code: "source-read-failed",
|
|
2012
|
-
expected: `readable source file at meta.source "${ctx.source}"`,
|
|
2013
|
-
hint: IMPORT_ERROR_HINTS["source-read-failed"],
|
|
2014
|
-
detail: {
|
|
2015
|
-
source: ctx.source,
|
|
2016
|
-
reason: read.error instanceof Error ? read.error.message : String(read.error)
|
|
2017
|
-
}
|
|
2018
|
-
})
|
|
2019
|
-
};
|
|
1268
|
+
const wrapper = new Error(`fbx-source-unreadable: ${ctx.source}`);
|
|
1269
|
+
wrapper.cause = read.error;
|
|
1270
|
+
throw wrapper;
|
|
2020
1271
|
}
|
|
2021
1272
|
await initFbxWasm();
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
const jsonStr = parseFbx(read.value);
|
|
2025
|
-
doc = JSON.parse(jsonStr);
|
|
2026
|
-
} catch (error) {
|
|
2027
|
-
return { ok: false, error: parseValidationError(ctx, error) };
|
|
2028
|
-
}
|
|
1273
|
+
const jsonStr = parseFbx(read.value);
|
|
1274
|
+
const doc = JSON.parse(jsonStr);
|
|
2029
1275
|
const maybeError = doc;
|
|
2030
1276
|
if (maybeError.error?.code === "fbx-mesh-type-unsupported") {
|
|
2031
1277
|
const e = fbxErr("fbx-mesh-type-unsupported", {
|
|
@@ -2057,101 +1303,6 @@ var fbxImporter = {
|
|
|
2057
1303
|
throw wrapper;
|
|
2058
1304
|
}
|
|
2059
1305
|
const animationClips = parseAnimationClips(doc);
|
|
2060
|
-
const textureSlots = /* @__PURE__ */ new Map();
|
|
2061
|
-
for (const material of materials) {
|
|
2062
|
-
for (const binding of material.textureBindings ?? []) {
|
|
2063
|
-
const prior = textureSlots.get(binding.textureIndex);
|
|
2064
|
-
const colorSpace = textureColorSpace(binding.slot);
|
|
2065
|
-
if (prior !== void 0 && prior !== colorSpace) {
|
|
2066
|
-
return {
|
|
2067
|
-
ok: false,
|
|
2068
|
-
error: validationError(
|
|
2069
|
-
ctx,
|
|
2070
|
-
binding.textureIndex,
|
|
2071
|
-
"fbx-texture-color-space-conflict",
|
|
2072
|
-
`texture ${binding.textureIndex} is used as both ${prior} and ${colorSpace}`,
|
|
2073
|
-
"split the source texture or author one color-space semantic per FBX texture"
|
|
2074
|
-
)
|
|
2075
|
-
};
|
|
2076
|
-
}
|
|
2077
|
-
textureSlots.set(binding.textureIndex, colorSpace);
|
|
2078
|
-
}
|
|
2079
|
-
}
|
|
2080
|
-
const decodedTextures = /* @__PURE__ */ new Map();
|
|
2081
|
-
for (const texture of textures) {
|
|
2082
|
-
const guidDeclared = ctx.subAssets.some(
|
|
2083
|
-
(subAsset) => subAsset.kind === "texture" && subAsset.sourceIndex === texture.sourceIndex
|
|
2084
|
-
);
|
|
2085
|
-
if (!guidDeclared) continue;
|
|
2086
|
-
let bytes;
|
|
2087
|
-
let sourcePath = texture.filePath;
|
|
2088
|
-
if (texture.embeddedBytes !== void 0 && texture.embeddedBytes.byteLength > 0) {
|
|
2089
|
-
bytes = texture.embeddedBytes;
|
|
2090
|
-
sourcePath = texture.relativeFilePath ?? texture.filePath;
|
|
2091
|
-
} else {
|
|
2092
|
-
if (texture.type !== void 0 && texture.type !== "file") {
|
|
2093
|
-
return {
|
|
2094
|
-
ok: false,
|
|
2095
|
-
error: validationError(
|
|
2096
|
-
ctx,
|
|
2097
|
-
texture.sourceIndex,
|
|
2098
|
-
"fbx-material-texture-slot-unsupported",
|
|
2099
|
-
`texture type ${texture.type} is not a single file texture`,
|
|
2100
|
-
"flatten layered/procedural FBX textures to a file texture before importing"
|
|
2101
|
-
)
|
|
2102
|
-
};
|
|
2103
|
-
}
|
|
2104
|
-
const resolution = resolveFbxTexturePath(
|
|
2105
|
-
ctx.source,
|
|
2106
|
-
{
|
|
2107
|
-
...texture.relativeFilePath === void 0 ? {} : { declaredRelativePath: texture.relativeFilePath },
|
|
2108
|
-
...texture.filePath.length === 0 ? {} : { declaredFilename: texture.filePath },
|
|
2109
|
-
...texture.absoluteFilePath === void 0 ? {} : { declaredAbsolutePath: texture.absoluteFilePath }
|
|
2110
|
-
},
|
|
2111
|
-
importCandidates(ctx)
|
|
2112
|
-
);
|
|
2113
|
-
if (!resolution.ok) {
|
|
2114
|
-
return {
|
|
2115
|
-
ok: false,
|
|
2116
|
-
error: validationError(
|
|
2117
|
-
ctx,
|
|
2118
|
-
texture.sourceIndex,
|
|
2119
|
-
resolution.code,
|
|
2120
|
-
`${resolution.requestedPath}; candidates=${resolution.candidates.join(", ") || "<none>"}`,
|
|
2121
|
-
resolution.code === "fbx-external-texture-ambiguous" ? "select a folder whose dependency path is unique; first-match guessing is disabled" : "select the containing folder so the FBX and its referenced texture are in the authorized candidate scope"
|
|
2122
|
-
)
|
|
2123
|
-
};
|
|
2124
|
-
}
|
|
2125
|
-
const read2 = await ctx.readSibling(resolution.readUri);
|
|
2126
|
-
if (!read2.ok) return read2;
|
|
2127
|
-
bytes = read2.value;
|
|
2128
|
-
sourcePath = resolution.relativePath;
|
|
2129
|
-
}
|
|
2130
|
-
const slot = [...textureSlots.entries()].find(([index]) => index === texture.sourceIndex)?.[1] ?? "srgb";
|
|
2131
|
-
const decoded = await ctx.decodeImage(bytes, mimeForTexture(sourcePath, bytes), {
|
|
2132
|
-
...ctx.importSettings,
|
|
2133
|
-
colorSpace: slot
|
|
2134
|
-
});
|
|
2135
|
-
if (!decoded.ok) {
|
|
2136
|
-
const reason = decoded.error.code === "image-decode-failed" ? decoded.error.detail.reason : `${decoded.error.code}: ${JSON.stringify(decoded.error.detail)}`;
|
|
2137
|
-
return {
|
|
2138
|
-
ok: false,
|
|
2139
|
-
error: validationError(
|
|
2140
|
-
ctx,
|
|
2141
|
-
texture.sourceIndex,
|
|
2142
|
-
"fbx-external-texture-decode-failed",
|
|
2143
|
-
`${sourcePath}: ${reason}`,
|
|
2144
|
-
"repair or re-export the referenced image, then retry the FBX import"
|
|
2145
|
-
)
|
|
2146
|
-
};
|
|
2147
|
-
}
|
|
2148
|
-
decodedTextures.set(texture.sourceIndex, {
|
|
2149
|
-
texture: decoded.value.texture,
|
|
2150
|
-
bytes: decoded.value.bytes,
|
|
2151
|
-
...decoded.value.mediaType === void 0 ? {} : { mediaType: decoded.value.mediaType },
|
|
2152
|
-
...decoded.value.assetCodec === void 0 ? {} : { assetCodec: decoded.value.assetCodec }
|
|
2153
|
-
});
|
|
2154
|
-
}
|
|
2155
1306
|
return {
|
|
2156
1307
|
ok: true,
|
|
2157
1308
|
value: {
|
|
@@ -2164,7 +1315,6 @@ var fbxImporter = {
|
|
|
2164
1315
|
skin,
|
|
2165
1316
|
animationClips,
|
|
2166
1317
|
subAssets: ctx.subAssets,
|
|
2167
|
-
decodedTextures,
|
|
2168
1318
|
...ctx.sourceOverrides === void 0 ? {} : { sourceOverrides: ctx.sourceOverrides }
|
|
2169
1319
|
}),
|
|
2170
1320
|
sourceDependencies: []
|
|
@@ -2173,6 +1323,106 @@ var fbxImporter = {
|
|
|
2173
1323
|
}
|
|
2174
1324
|
};
|
|
2175
1325
|
|
|
1326
|
+
// src/resolve-texture-path.ts
|
|
1327
|
+
function normalizeSourceRelativePath(raw) {
|
|
1328
|
+
const value = raw.replaceAll("\\", "/");
|
|
1329
|
+
if (value.startsWith("/") || /^[A-Za-z]:\//.test(value) || value.startsWith("//"))
|
|
1330
|
+
return void 0;
|
|
1331
|
+
const parts = [];
|
|
1332
|
+
for (const part of value.split("/")) {
|
|
1333
|
+
if (part === "" || part === ".") continue;
|
|
1334
|
+
if (part === "..") {
|
|
1335
|
+
if (parts.at(-1) !== void 0 && parts.at(-1) !== "..") parts.pop();
|
|
1336
|
+
else parts.push("..");
|
|
1337
|
+
} else {
|
|
1338
|
+
parts.push(part);
|
|
1339
|
+
}
|
|
1340
|
+
}
|
|
1341
|
+
return parts.join("/");
|
|
1342
|
+
}
|
|
1343
|
+
function requestSegments(raw) {
|
|
1344
|
+
const value = raw.replaceAll("\\", "/").replace(/^[A-Za-z]:\//, "").replace(/^\/+/, "");
|
|
1345
|
+
return value.split("/").filter((part) => part !== "" && part !== ".");
|
|
1346
|
+
}
|
|
1347
|
+
function uniqueMatches(paths, predicate) {
|
|
1348
|
+
return [...new Set(paths.filter(predicate))];
|
|
1349
|
+
}
|
|
1350
|
+
function longestSuffixLength(request, candidate) {
|
|
1351
|
+
let count = 0;
|
|
1352
|
+
while (count < request.length && count < candidate.length && request[request.length - 1 - count]?.toLowerCase() === candidate[candidate.length - 1 - count]?.toLowerCase()) {
|
|
1353
|
+
count++;
|
|
1354
|
+
}
|
|
1355
|
+
return count;
|
|
1356
|
+
}
|
|
1357
|
+
function resolveFbxTexturePath(sourcePath, request, candidates) {
|
|
1358
|
+
const candidatePaths = candidates.flatMap((candidate) => {
|
|
1359
|
+
const normalized = normalizeSourceRelativePath(candidate.relativePath);
|
|
1360
|
+
return normalized === void 0 || normalized.length === 0 ? [] : [normalized];
|
|
1361
|
+
});
|
|
1362
|
+
const requestedPath = request.declaredRelativePath ?? request.declaredFilename ?? request.declaredAbsolutePath ?? "";
|
|
1363
|
+
const choose = (matches, strategy) => {
|
|
1364
|
+
const unique = [...new Set(matches)];
|
|
1365
|
+
if (unique.length === 1) {
|
|
1366
|
+
const relativePath = unique[0];
|
|
1367
|
+
if (relativePath === void 0) return void 0;
|
|
1368
|
+
return {
|
|
1369
|
+
ok: true,
|
|
1370
|
+
relativePath,
|
|
1371
|
+
readUri: relativePath,
|
|
1372
|
+
strategy
|
|
1373
|
+
};
|
|
1374
|
+
}
|
|
1375
|
+
if (unique.length > 1) {
|
|
1376
|
+
return {
|
|
1377
|
+
ok: false,
|
|
1378
|
+
code: "fbx-external-texture-ambiguous",
|
|
1379
|
+
requestedPath,
|
|
1380
|
+
candidates: unique
|
|
1381
|
+
};
|
|
1382
|
+
}
|
|
1383
|
+
return void 0;
|
|
1384
|
+
};
|
|
1385
|
+
const relativeRequest = request.declaredRelativePath ?? request.declaredFilename;
|
|
1386
|
+
if (relativeRequest !== void 0 && !/^(?:[A-Za-z]:[\\/]|[\\/])/.test(relativeRequest)) {
|
|
1387
|
+
const normalizedRequest = normalizeSourceRelativePath(relativeRequest);
|
|
1388
|
+
if (normalizedRequest !== void 0) {
|
|
1389
|
+
const scopeExact = choose(
|
|
1390
|
+
uniqueMatches(candidatePaths, (path) => path === normalizedRequest),
|
|
1391
|
+
"exact"
|
|
1392
|
+
);
|
|
1393
|
+
if (scopeExact !== void 0) return scopeExact;
|
|
1394
|
+
const scopeFolded = choose(
|
|
1395
|
+
uniqueMatches(
|
|
1396
|
+
candidatePaths,
|
|
1397
|
+
(path) => path.toLowerCase() === normalizedRequest.toLowerCase()
|
|
1398
|
+
),
|
|
1399
|
+
"case-folded"
|
|
1400
|
+
);
|
|
1401
|
+
if (scopeFolded !== void 0) return scopeFolded;
|
|
1402
|
+
}
|
|
1403
|
+
}
|
|
1404
|
+
const requestedSegments = requestSegments(requestedPath);
|
|
1405
|
+
if (requestedSegments.length > 0) {
|
|
1406
|
+
const scored = candidatePaths.map((path) => ({
|
|
1407
|
+
path,
|
|
1408
|
+
score: longestSuffixLength(requestedSegments, path.split("/"))
|
|
1409
|
+
}));
|
|
1410
|
+
const bestScore = Math.max(0, ...scored.map((entry) => entry.score));
|
|
1411
|
+
if (bestScore > 0) {
|
|
1412
|
+
const best = scored.filter((entry) => entry.score === bestScore).map((entry) => entry.path);
|
|
1413
|
+
const strategy = bestScore === 1 ? "basename" : "suffix";
|
|
1414
|
+
const suffix = choose(best, strategy);
|
|
1415
|
+
if (suffix !== void 0) return suffix;
|
|
1416
|
+
}
|
|
1417
|
+
}
|
|
1418
|
+
return {
|
|
1419
|
+
ok: false,
|
|
1420
|
+
code: "fbx-external-texture-missing",
|
|
1421
|
+
requestedPath,
|
|
1422
|
+
candidates: candidatePaths
|
|
1423
|
+
};
|
|
1424
|
+
}
|
|
1425
|
+
|
|
2176
1426
|
// src/index.ts
|
|
2177
1427
|
var wasmModule = null;
|
|
2178
1428
|
var initPromise = null;
|
|
@@ -2243,18 +1493,6 @@ function parseFbxToObject(fbxBytes) {
|
|
|
2243
1493
|
function isFbxWasmReady() {
|
|
2244
1494
|
return wasmModule !== null;
|
|
2245
1495
|
}
|
|
2246
|
-
/*! Bundled license information:
|
|
2247
|
-
|
|
2248
|
-
uuidv7/dist/index.js:
|
|
2249
|
-
(**
|
|
2250
|
-
* uuidv7: A JavaScript implementation of UUID version 7
|
|
2251
|
-
*
|
|
2252
|
-
* Copyright 2021-2026 LiosK
|
|
2253
|
-
*
|
|
2254
|
-
* @license Apache-2.0
|
|
2255
|
-
* @packageDocumentation
|
|
2256
|
-
*)
|
|
2257
|
-
*/
|
|
2258
1496
|
|
|
2259
1497
|
export { FBX_ERROR_HINTS, deriveFbxSourceKeys, fbxErr, fbxImporter, initFbxWasm, isFbxWasmReady, parseAnimationClips, parseFbx, parseFbxToObject, parseMaterial, parseMesh, parseScene, parseSkeleton, parseSkin, parseTextures, resolveFbxTexturePath, sourceKeyForFbxOutput, toAssetPack };
|
|
2260
1498
|
//# sourceMappingURL=index.mjs.map
|