@aura3d/assets 3.0.0 → 4.0.0
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/dist/AdvancedAssetCorpus.d.ts.map +1 -1
- package/dist/AdvancedAssetCorpus.js +7 -12
- package/dist/AdvancedAssetCorpus.js.map +1 -1
- package/dist/AssetDecoderRegistry.d.ts +56 -0
- package/dist/AssetDecoderRegistry.d.ts.map +1 -0
- package/dist/AssetDecoderRegistry.js +225 -0
- package/dist/AssetDecoderRegistry.js.map +1 -0
- package/dist/GLTFAnimationRuntime.d.ts +300 -6
- package/dist/GLTFAnimationRuntime.d.ts.map +1 -1
- package/dist/GLTFAnimationRuntime.js +981 -60
- package/dist/GLTFAnimationRuntime.js.map +1 -1
- package/dist/GLTFAssetParseCache.d.ts +24 -0
- package/dist/GLTFAssetParseCache.d.ts.map +1 -0
- package/dist/GLTFAssetParseCache.js +100 -0
- package/dist/GLTFAssetParseCache.js.map +1 -0
- package/dist/GLTFCompressionDecoders.d.ts +1 -0
- package/dist/GLTFCompressionDecoders.d.ts.map +1 -1
- package/dist/GLTFCompressionDecoders.js +20 -5
- package/dist/GLTFCompressionDecoders.js.map +1 -1
- package/dist/GLTFExtensionSupport.js +3 -3
- package/dist/GLTFExtensionSupport.js.map +1 -1
- package/dist/GLTFLoader.d.ts +28 -1
- package/dist/GLTFLoader.d.ts.map +1 -1
- package/dist/GLTFLoader.js +197 -30
- package/dist/GLTFLoader.js.map +1 -1
- package/dist/GLTFRenderResources.d.ts +77 -2
- package/dist/GLTFRenderResources.d.ts.map +1 -1
- package/dist/GLTFRenderResources.js +310 -153
- package/dist/GLTFRenderResources.js.map +1 -1
- package/dist/KTX2BasisTextureTranscoder.d.ts +70 -12
- package/dist/KTX2BasisTextureTranscoder.d.ts.map +1 -1
- package/dist/KTX2BasisTextureTranscoder.js +225 -184
- package/dist/KTX2BasisTextureTranscoder.js.map +1 -1
- package/dist/KTX2TargetSelection.d.ts +25 -0
- package/dist/KTX2TargetSelection.d.ts.map +1 -0
- package/dist/KTX2TargetSelection.js +36 -0
- package/dist/KTX2TargetSelection.js.map +1 -0
- package/dist/KTX2TranscodeDriver.d.ts +70 -0
- package/dist/KTX2TranscodeDriver.d.ts.map +1 -0
- package/dist/KTX2TranscodeDriver.js +104 -0
- package/dist/KTX2TranscodeDriver.js.map +1 -0
- package/dist/KTX2TranscodeWorker.d.ts +30 -0
- package/dist/KTX2TranscodeWorker.d.ts.map +1 -0
- package/dist/KTX2TranscodeWorker.js +230 -0
- package/dist/KTX2TranscodeWorker.js.map +1 -0
- package/dist/MikkTSpaceTangents.d.ts +43 -0
- package/dist/MikkTSpaceTangents.d.ts.map +1 -0
- package/dist/MikkTSpaceTangents.js +149 -0
- package/dist/MikkTSpaceTangents.js.map +1 -0
- package/dist/asset-corpus/ProductionAssetCorpus.d.ts.map +1 -1
- package/dist/asset-corpus/ProductionAssetCorpus.js +7 -13
- package/dist/asset-corpus/ProductionAssetCorpus.js.map +1 -1
- package/dist/asset-corpus/ProductionGLTFRenderPipeline.d.ts +43 -0
- package/dist/asset-corpus/ProductionGLTFRenderPipeline.d.ts.map +1 -1
- package/dist/asset-corpus/ProductionGLTFRenderPipeline.js +146 -22
- package/dist/asset-corpus/ProductionGLTFRenderPipeline.js.map +1 -1
- package/dist/assetPhaseMarks.d.ts +14 -0
- package/dist/assetPhaseMarks.d.ts.map +1 -0
- package/dist/assetPhaseMarks.js +23 -0
- package/dist/assetPhaseMarks.js.map +1 -0
- package/dist/browser-index.d.ts +5 -1
- package/dist/browser-index.d.ts.map +1 -1
- package/dist/browser-index.js +3 -1
- package/dist/browser-index.js.map +1 -1
- package/dist/contracts/decoders.d.ts +15 -0
- package/dist/contracts/decoders.d.ts.map +1 -0
- package/dist/contracts/decoders.js +8 -0
- package/dist/contracts/decoders.js.map +1 -0
- package/dist/decoderLoad.d.ts +28 -0
- package/dist/decoderLoad.d.ts.map +1 -0
- package/dist/decoderLoad.js +49 -0
- package/dist/decoderLoad.js.map +1 -0
- package/dist/gltf/ImageDecode.d.ts +18 -0
- package/dist/gltf/ImageDecode.d.ts.map +1 -0
- package/dist/gltf/ImageDecode.js +140 -0
- package/dist/gltf/ImageDecode.js.map +1 -0
- package/dist/gltf-runtime.d.ts +1 -1
- package/dist/gltf-runtime.d.ts.map +1 -1
- package/dist/gltf-runtime.js.map +1 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/lanes/index.d.ts +17 -0
- package/dist/lanes/index.d.ts.map +1 -0
- package/dist/lanes/index.js +17 -0
- package/dist/lanes/index.js.map +1 -0
- package/dist/lanes/prd01.d.ts +6 -0
- package/dist/lanes/prd01.d.ts.map +1 -0
- package/dist/lanes/prd01.js +6 -0
- package/dist/lanes/prd01.js.map +1 -0
- package/dist/lanes/prd02.d.ts +6 -0
- package/dist/lanes/prd02.d.ts.map +1 -0
- package/dist/lanes/prd02.js +6 -0
- package/dist/lanes/prd02.js.map +1 -0
- package/dist/lanes/prd03.d.ts +6 -0
- package/dist/lanes/prd03.d.ts.map +1 -0
- package/dist/lanes/prd03.js +6 -0
- package/dist/lanes/prd03.js.map +1 -0
- package/dist/lanes/prd04.d.ts +6 -0
- package/dist/lanes/prd04.d.ts.map +1 -0
- package/dist/lanes/prd04.js +6 -0
- package/dist/lanes/prd04.js.map +1 -0
- package/dist/lanes/prd05.d.ts +14 -0
- package/dist/lanes/prd05.d.ts.map +1 -0
- package/dist/lanes/prd05.js +14 -0
- package/dist/lanes/prd05.js.map +1 -0
- package/dist/lanes/prd06.d.ts +6 -0
- package/dist/lanes/prd06.d.ts.map +1 -0
- package/dist/lanes/prd06.js +6 -0
- package/dist/lanes/prd06.js.map +1 -0
- package/dist/lanes/prd07.d.ts +6 -0
- package/dist/lanes/prd07.d.ts.map +1 -0
- package/dist/lanes/prd07.js +6 -0
- package/dist/lanes/prd07.js.map +1 -0
- package/dist/lanes/prd08.d.ts +6 -0
- package/dist/lanes/prd08.d.ts.map +1 -0
- package/dist/lanes/prd08.js +6 -0
- package/dist/lanes/prd08.js.map +1 -0
- package/dist/lanes/prd09.d.ts +6 -0
- package/dist/lanes/prd09.d.ts.map +1 -0
- package/dist/lanes/prd09.js +6 -0
- package/dist/lanes/prd09.js.map +1 -0
- package/dist/lanes/prd10.d.ts +6 -0
- package/dist/lanes/prd10.d.ts.map +1 -0
- package/dist/lanes/prd10.js +6 -0
- package/dist/lanes/prd10.js.map +1 -0
- package/dist/lanes/prd11.d.ts +6 -0
- package/dist/lanes/prd11.d.ts.map +1 -0
- package/dist/lanes/prd11.js +6 -0
- package/dist/lanes/prd11.js.map +1 -0
- package/dist/lanes/prd12.d.ts +6 -0
- package/dist/lanes/prd12.d.ts.map +1 -0
- package/dist/lanes/prd12.js +6 -0
- package/dist/lanes/prd12.js.map +1 -0
- package/dist/lanes/prd13.d.ts +6 -0
- package/dist/lanes/prd13.d.ts.map +1 -0
- package/dist/lanes/prd13.js +6 -0
- package/dist/lanes/prd13.js.map +1 -0
- package/dist/lanes/prd14.d.ts +6 -0
- package/dist/lanes/prd14.d.ts.map +1 -0
- package/dist/lanes/prd14.js +6 -0
- package/dist/lanes/prd14.js.map +1 -0
- package/dist/lanes/prd15.d.ts +6 -0
- package/dist/lanes/prd15.d.ts.map +1 -0
- package/dist/lanes/prd15.js +6 -0
- package/dist/lanes/prd15.js.map +1 -0
- package/dist/vite/auraDecoders.d.ts +31 -0
- package/dist/vite/auraDecoders.d.ts.map +1 -0
- package/dist/vite/auraDecoders.js +89 -0
- package/dist/vite/auraDecoders.js.map +1 -0
- package/package.json +13 -5
- package/vendor/basis/README.md +25 -0
- package/vendor/basis/basis_transcoder.js +19 -0
- package/vendor/basis/basis_transcoder.wasm +0 -0
- package/vendor/draco/README.md +23 -0
- package/vendor/draco/draco_decoder.js +33 -0
- package/vendor/draco/draco_decoder.wasm +0 -0
- package/vendor/meshopt/README.md +21 -0
- package/vendor/meshopt/meshopt_decoder.cjs +199 -0
- package/vendor/meshopt/meshopt_decoder.mjs +196 -0
- package/vendor/meshopt_decoder.mjs +196 -0
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared KTX2/Basis transcode driver (PRD-05 §7.4). Consumes an initialised
|
|
3
|
+
* Basis Universal `BASIS` module — the same instance shape in a worker
|
|
4
|
+
* (`importScripts` + `wasmBinary`) and on the main thread (node, vendored
|
|
5
|
+
* UMD evaluated as CJS). Never touches the network itself: the wasm binary
|
|
6
|
+
* and module are provided by the caller.
|
|
7
|
+
*/
|
|
8
|
+
/** TranscoderFormat enum from the Basis Universal transcoder (three r185 table). */
|
|
9
|
+
export declare const BASIS_TRANSCODER_FORMAT: {
|
|
10
|
+
readonly ETC1: 0;
|
|
11
|
+
readonly ETC2: 1;
|
|
12
|
+
readonly BC1: 2;
|
|
13
|
+
readonly BC3: 3;
|
|
14
|
+
readonly BC7_M6_OPAQUE_ONLY: 6;
|
|
15
|
+
readonly BC7_M5: 7;
|
|
16
|
+
readonly ASTC_4x4: 10;
|
|
17
|
+
readonly RGBA32: 13;
|
|
18
|
+
};
|
|
19
|
+
import type { KTX2BasisTargetFormat } from "./KTX2TargetSelection.js";
|
|
20
|
+
export declare function basisTranscoderFormat(target: KTX2BasisTargetFormat): number;
|
|
21
|
+
export interface BasisKTX2FileLike {
|
|
22
|
+
isValid(): boolean;
|
|
23
|
+
isUASTC(): boolean;
|
|
24
|
+
isETC1S(): boolean;
|
|
25
|
+
isHDR?(): boolean;
|
|
26
|
+
getWidth(): number;
|
|
27
|
+
getHeight(): number;
|
|
28
|
+
getLayers(): number;
|
|
29
|
+
getLevels(): number;
|
|
30
|
+
getFaces(): number;
|
|
31
|
+
getHasAlpha(): boolean;
|
|
32
|
+
getDFDFlags?(): number;
|
|
33
|
+
getImageLevelInfo(level: number, layer: number, face: number): {
|
|
34
|
+
readonly width: number;
|
|
35
|
+
readonly height: number;
|
|
36
|
+
readonly origWidth: number;
|
|
37
|
+
readonly origHeight: number;
|
|
38
|
+
readonly numBlocksX?: number;
|
|
39
|
+
readonly numBlocksY?: number;
|
|
40
|
+
};
|
|
41
|
+
getImageTranscodedSizeInBytes(level: number, layer: number, face: number, format: number): number;
|
|
42
|
+
startTranscoding(): boolean;
|
|
43
|
+
transcodeImage(dst: Uint8Array, level: number, layer: number, face: number, format: number, unused0: number, unused1: number, unused2: number): boolean;
|
|
44
|
+
close(): void;
|
|
45
|
+
delete(): void;
|
|
46
|
+
}
|
|
47
|
+
export interface BasisModuleLike {
|
|
48
|
+
initializeBasis(): void;
|
|
49
|
+
readonly KTX2File: new (data: Uint8Array) => BasisKTX2FileLike;
|
|
50
|
+
}
|
|
51
|
+
export interface KTX2TranscodedLevel {
|
|
52
|
+
readonly width: number;
|
|
53
|
+
readonly height: number;
|
|
54
|
+
readonly data: Uint8Array;
|
|
55
|
+
}
|
|
56
|
+
export interface KTX2TranscodeResult {
|
|
57
|
+
readonly width: number;
|
|
58
|
+
readonly height: number;
|
|
59
|
+
readonly isUASTC: boolean;
|
|
60
|
+
readonly hasAlpha: boolean;
|
|
61
|
+
readonly levels: readonly KTX2TranscodedLevel[];
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Transcodes every mip level (largest first) of a KTX2 container to
|
|
65
|
+
* `transcoderFormat` (a `BASIS_TRANSCODER_FORMAT` value). Levels whose true
|
|
66
|
+
* dimensions exceed `maxDimension` are skipped — `startTranscoding` avoids
|
|
67
|
+
* touching their payloads entirely.
|
|
68
|
+
*/
|
|
69
|
+
export declare function transcodeKTX2Levels(basis: BasisModuleLike, bytes: Uint8Array, transcoderFormat: number, maxDimension?: number): KTX2TranscodeResult;
|
|
70
|
+
//# sourceMappingURL=KTX2TranscodeDriver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"KTX2TranscodeDriver.d.ts","sourceRoot":"","sources":["../../../../packages/assets/src/KTX2TranscodeDriver.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,oFAAoF;AACpF,eAAO,MAAM,uBAAuB;;;;;;;;;CAS1B,CAAC;AAEX,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AAEtE,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,qBAAqB,GAAG,MAAM,CAW3E;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,IAAI,OAAO,CAAC;IACnB,OAAO,IAAI,OAAO,CAAC;IACnB,OAAO,IAAI,OAAO,CAAC;IACnB,KAAK,CAAC,IAAI,OAAO,CAAC;IAClB,QAAQ,IAAI,MAAM,CAAC;IACnB,SAAS,IAAI,MAAM,CAAC;IACpB,SAAS,IAAI,MAAM,CAAC;IACpB,SAAS,IAAI,MAAM,CAAC;IACpB,QAAQ,IAAI,MAAM,CAAC;IACnB,WAAW,IAAI,OAAO,CAAC;IACvB,WAAW,CAAC,IAAI,MAAM,CAAC;IACvB,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG;QAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACxO,6BAA6B,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC;IAClG,gBAAgB,IAAI,OAAO,CAAC;IAC5B,cAAc,CAAC,GAAG,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC;IACxJ,KAAK,IAAI,IAAI,CAAC;IACd,MAAM,IAAI,IAAI,CAAC;CAChB;AAED,MAAM,WAAW,eAAe;IAC9B,eAAe,IAAI,IAAI,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,KAAK,IAAI,EAAE,UAAU,KAAK,iBAAiB,CAAC;CAChE;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;CAC3B;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,SAAS,mBAAmB,EAAE,CAAC;CACjD;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,eAAe,EACtB,KAAK,EAAE,UAAU,EACjB,gBAAgB,EAAE,MAAM,EACxB,YAAY,CAAC,EAAE,MAAM,GACpB,mBAAmB,CAuDrB"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared KTX2/Basis transcode driver (PRD-05 §7.4). Consumes an initialised
|
|
3
|
+
* Basis Universal `BASIS` module — the same instance shape in a worker
|
|
4
|
+
* (`importScripts` + `wasmBinary`) and on the main thread (node, vendored
|
|
5
|
+
* UMD evaluated as CJS). Never touches the network itself: the wasm binary
|
|
6
|
+
* and module are provided by the caller.
|
|
7
|
+
*/
|
|
8
|
+
/** TranscoderFormat enum from the Basis Universal transcoder (three r185 table). */
|
|
9
|
+
export const BASIS_TRANSCODER_FORMAT = {
|
|
10
|
+
ETC1: 0,
|
|
11
|
+
ETC2: 1,
|
|
12
|
+
BC1: 2,
|
|
13
|
+
BC3: 3,
|
|
14
|
+
BC7_M6_OPAQUE_ONLY: 6,
|
|
15
|
+
BC7_M5: 7,
|
|
16
|
+
ASTC_4x4: 10,
|
|
17
|
+
RGBA32: 13
|
|
18
|
+
};
|
|
19
|
+
export function basisTranscoderFormat(target) {
|
|
20
|
+
switch (target) {
|
|
21
|
+
case "astc-4x4-rgba-unorm": return BASIS_TRANSCODER_FORMAT.ASTC_4x4;
|
|
22
|
+
case "bc7-rgba-unorm": return BASIS_TRANSCODER_FORMAT.BC7_M5;
|
|
23
|
+
case "etc2-rgba8unorm": return BASIS_TRANSCODER_FORMAT.ETC2;
|
|
24
|
+
case "etc2-rgb8unorm": return BASIS_TRANSCODER_FORMAT.ETC2;
|
|
25
|
+
case "bc3-rgba-unorm": return BASIS_TRANSCODER_FORMAT.BC3;
|
|
26
|
+
case "bc1-rgb-unorm": return BASIS_TRANSCODER_FORMAT.BC1;
|
|
27
|
+
case "rgba8": return BASIS_TRANSCODER_FORMAT.RGBA32;
|
|
28
|
+
default: throw new Error(`KTX2/Basis target format ${String(target)} is unsupported`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Transcodes every mip level (largest first) of a KTX2 container to
|
|
33
|
+
* `transcoderFormat` (a `BASIS_TRANSCODER_FORMAT` value). Levels whose true
|
|
34
|
+
* dimensions exceed `maxDimension` are skipped — `startTranscoding` avoids
|
|
35
|
+
* touching their payloads entirely.
|
|
36
|
+
*/
|
|
37
|
+
export function transcodeKTX2Levels(basis, bytes, transcoderFormat, maxDimension) {
|
|
38
|
+
const file = new basis.KTX2File(bytes);
|
|
39
|
+
const cleanup = () => { file.close(); file.delete(); };
|
|
40
|
+
try {
|
|
41
|
+
if (!file.isValid()) {
|
|
42
|
+
throw new Error("Invalid or unsupported .ktx2 file");
|
|
43
|
+
}
|
|
44
|
+
if (!file.isUASTC() && !file.isETC1S() && !(file.isHDR?.() ?? false)) {
|
|
45
|
+
throw new Error("Unknown Basis encoding (expected UASTC or ETC1S)");
|
|
46
|
+
}
|
|
47
|
+
const width = file.getWidth();
|
|
48
|
+
const height = file.getHeight();
|
|
49
|
+
const layerCount = file.getLayers() || 1;
|
|
50
|
+
const levelCount = file.getLevels();
|
|
51
|
+
const faceCount = file.getFaces();
|
|
52
|
+
const hasAlpha = file.getHasAlpha();
|
|
53
|
+
if (!width || !height || !levelCount) {
|
|
54
|
+
throw new Error("Invalid texture");
|
|
55
|
+
}
|
|
56
|
+
if (!file.startTranscoding()) {
|
|
57
|
+
throw new Error(".startTranscoding failed");
|
|
58
|
+
}
|
|
59
|
+
const levels = [];
|
|
60
|
+
let skipped = 0;
|
|
61
|
+
for (let face = 0; face < faceCount; face += 1) {
|
|
62
|
+
for (let level = 0; level < levelCount; level += 1) {
|
|
63
|
+
const levelInfo = file.getImageLevelInfo(level, 0, face);
|
|
64
|
+
if (maxDimension !== undefined && Math.max(levelInfo.origWidth, levelInfo.origHeight) > maxDimension) {
|
|
65
|
+
skipped += 1;
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
const layerData = [];
|
|
69
|
+
for (let layer = 0; layer < layerCount; layer += 1) {
|
|
70
|
+
const dst = new Uint8Array(file.getImageTranscodedSizeInBytes(level, layer, face, transcoderFormat));
|
|
71
|
+
if (!file.transcodeImage(dst, level, layer, face, transcoderFormat, 0, -1, -1)) {
|
|
72
|
+
throw new Error(".transcodeImage failed");
|
|
73
|
+
}
|
|
74
|
+
layerData.push(dst);
|
|
75
|
+
}
|
|
76
|
+
const data = layerData.length === 1 ? layerData[0] : concatBytes(layerData);
|
|
77
|
+
// Non-multiple-of-four textures without mipmaps report padded
|
|
78
|
+
// levelInfo.width/height; use origWidth/origHeight when real mips exist
|
|
79
|
+
// (three r185 KTX2Loader parity).
|
|
80
|
+
const mipWidth = levelCount - skipped > 1 ? levelInfo.origWidth : levelInfo.width;
|
|
81
|
+
const mipHeight = levelCount - skipped > 1 ? levelInfo.origHeight : levelInfo.height;
|
|
82
|
+
levels.push({ width: mipWidth, height: mipHeight, data });
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
if (levels.length === 0) {
|
|
86
|
+
throw new Error(`maxDimension ${maxDimension} skipped every mip level`);
|
|
87
|
+
}
|
|
88
|
+
return { width: levels[0].width, height: levels[0].height, isUASTC: file.isUASTC(), hasAlpha, levels };
|
|
89
|
+
}
|
|
90
|
+
finally {
|
|
91
|
+
cleanup();
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
function concatBytes(parts) {
|
|
95
|
+
const total = parts.reduce((sum, part) => sum + part.byteLength, 0);
|
|
96
|
+
const out = new Uint8Array(total);
|
|
97
|
+
let offset = 0;
|
|
98
|
+
for (const part of parts) {
|
|
99
|
+
out.set(part, offset);
|
|
100
|
+
offset += part.byteLength;
|
|
101
|
+
}
|
|
102
|
+
return out;
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=KTX2TranscodeDriver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"KTX2TranscodeDriver.js","sourceRoot":"","sources":["../../../../packages/assets/src/KTX2TranscodeDriver.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* KTX2 transcode worker pool (PRD-05 §7.4). Workers are built from the
|
|
3
|
+
* vendored `basis_transcoder.js` + `.wasm` fetched once from the same-origin
|
|
4
|
+
* `transcoderUrl` — three r185's blob-worker recipe, so no bundler worker
|
|
5
|
+
* handling is required and module/wasm URLs stay same-origin.
|
|
6
|
+
*/
|
|
7
|
+
import type { KTX2TranscodedLevel } from "./KTX2TranscodeDriver.js";
|
|
8
|
+
export interface KTX2WorkerTranscodeRequest {
|
|
9
|
+
readonly bytes: Uint8Array;
|
|
10
|
+
readonly transcoderFormat: number;
|
|
11
|
+
readonly maxDimension?: number;
|
|
12
|
+
}
|
|
13
|
+
export interface KTX2WorkerTranscodeResult {
|
|
14
|
+
readonly width: number;
|
|
15
|
+
readonly height: number;
|
|
16
|
+
readonly isUASTC: boolean;
|
|
17
|
+
readonly hasAlpha: boolean;
|
|
18
|
+
readonly levels: readonly KTX2TranscodedLevel[];
|
|
19
|
+
}
|
|
20
|
+
export interface KTX2TranscodeWorkerPool {
|
|
21
|
+
transcode(request: KTX2WorkerTranscodeRequest): Promise<KTX2WorkerTranscodeResult>;
|
|
22
|
+
dispose(): void;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Creates a worker pool of `workerCount` transcoding workers. The pool lazily
|
|
26
|
+
* materialises its blob URL on first `transcode` call — construction is cheap
|
|
27
|
+
* and synchronous; `dispose()` terminates every worker.
|
|
28
|
+
*/
|
|
29
|
+
export declare function createKTX2TranscodeWorkerPool(transcoderUrl: string, workerCount: number): KTX2TranscodeWorkerPool;
|
|
30
|
+
//# sourceMappingURL=KTX2TranscodeWorker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"KTX2TranscodeWorker.d.ts","sourceRoot":"","sources":["../../../../packages/assets/src/KTX2TranscodeWorker.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAwFpE,MAAM,WAAW,0BAA0B;IACzC,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;IAC3B,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;CAChC;AAED,MAAM,WAAW,yBAAyB;IACxC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,SAAS,mBAAmB,EAAE,CAAC;CACjD;AAiBD,MAAM,WAAW,uBAAuB;IACtC,SAAS,CAAC,OAAO,EAAE,0BAA0B,GAAG,OAAO,CAAC,yBAAyB,CAAC,CAAC;IACnF,OAAO,IAAI,IAAI,CAAC;CACjB;AAED;;;;GAIG;AACH,wBAAgB,6BAA6B,CAAC,aAAa,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,uBAAuB,CAqHjH"}
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* KTX2 transcode worker pool (PRD-05 §7.4). Workers are built from the
|
|
3
|
+
* vendored `basis_transcoder.js` + `.wasm` fetched once from the same-origin
|
|
4
|
+
* `transcoderUrl` — three r185's blob-worker recipe, so no bundler worker
|
|
5
|
+
* handling is required and module/wasm URLs stay same-origin.
|
|
6
|
+
*/
|
|
7
|
+
import { AssetDecoderUnavailable, looksLikeWasmBinary, withDecoderTimeout } from "./decoderLoad.js";
|
|
8
|
+
const WORKER_SOURCE = `
|
|
9
|
+
let basisModule = null;
|
|
10
|
+
let readyPromise = null;
|
|
11
|
+
self.addEventListener("message", function (e) {
|
|
12
|
+
const message = e.data;
|
|
13
|
+
if (message.type === "init") {
|
|
14
|
+
const mod = { wasmBinary: message.wasmBinary, onRuntimeInitialized: null, onAbort: null };
|
|
15
|
+
readyPromise = new Promise(function (resolve, reject) {
|
|
16
|
+
mod.onRuntimeInitialized = resolve;
|
|
17
|
+
// A corrupt/short wasmBinary aborts emscripten through onAbort — without
|
|
18
|
+
// this the worker never answered and every queued transcode hung.
|
|
19
|
+
mod.onAbort = function (reason) { reject(new Error("basis_transcoder aborted: " + reason)); };
|
|
20
|
+
BASIS(mod);
|
|
21
|
+
}).then(function () {
|
|
22
|
+
basisModule = mod;
|
|
23
|
+
basisModule.initializeBasis();
|
|
24
|
+
});
|
|
25
|
+
readyPromise.then(function () {
|
|
26
|
+
self.postMessage({ type: "ready", id: message.id });
|
|
27
|
+
}, function (error) {
|
|
28
|
+
self.postMessage({ type: "error", id: message.id, error: error && error.message ? error.message : String(error) });
|
|
29
|
+
});
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
if (message.type === "transcode") {
|
|
33
|
+
readyPromise.then(function () {
|
|
34
|
+
try {
|
|
35
|
+
const result = __transcode(basisModule, new Uint8Array(message.buffer), message.transcoderFormat, message.maxDimension);
|
|
36
|
+
self.postMessage({ type: "transcode", id: message.id, result: result }, result.levels.map(function (l) { return l.data.buffer; }));
|
|
37
|
+
} catch (error) {
|
|
38
|
+
self.postMessage({ type: "error", id: message.id, error: error && error.message ? error.message : String(error) });
|
|
39
|
+
}
|
|
40
|
+
}, function (error) {
|
|
41
|
+
self.postMessage({ type: "error", id: message.id, error: error && error.message ? error.message : String(error) });
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
`;
|
|
46
|
+
const DRIVER_SOURCE = `
|
|
47
|
+
function __transcode(basis, bytes, transcoderFormat, maxDimension) {
|
|
48
|
+
const file = new basis.KTX2File(bytes);
|
|
49
|
+
function cleanup() { file.close(); file.delete(); }
|
|
50
|
+
try {
|
|
51
|
+
if (!file.isValid()) throw new Error("Invalid or unsupported .ktx2 file");
|
|
52
|
+
if (!file.isUASTC() && !file.isETC1S() && !(file.isHDR && file.isHDR())) throw new Error("Unknown Basis encoding");
|
|
53
|
+
const width = file.getWidth();
|
|
54
|
+
const height = file.getHeight();
|
|
55
|
+
const layerCount = file.getLayers() || 1;
|
|
56
|
+
const levelCount = file.getLevels();
|
|
57
|
+
const faceCount = file.getFaces();
|
|
58
|
+
const hasAlpha = file.getHasAlpha();
|
|
59
|
+
if (!width || !height || !levelCount) throw new Error("Invalid texture");
|
|
60
|
+
if (!file.startTranscoding()) throw new Error(".startTranscoding failed");
|
|
61
|
+
const levels = [];
|
|
62
|
+
let remaining = levelCount;
|
|
63
|
+
for (let face = 0; face < faceCount; face += 1) {
|
|
64
|
+
for (let level = 0; level < levelCount; level += 1) {
|
|
65
|
+
const levelInfo = file.getImageLevelInfo(level, 0, face);
|
|
66
|
+
if (maxDimension !== undefined && Math.max(levelInfo.origWidth, levelInfo.origHeight) > maxDimension) { remaining -= 1; continue; }
|
|
67
|
+
const layerData = [];
|
|
68
|
+
for (let layer = 0; layer < layerCount; layer += 1) {
|
|
69
|
+
const dst = new Uint8Array(file.getImageTranscodedSizeInBytes(level, layer, face, transcoderFormat));
|
|
70
|
+
if (!file.transcodeImage(dst, level, layer, face, transcoderFormat, 0, -1, -1)) throw new Error(".transcodeImage failed");
|
|
71
|
+
layerData.push(dst);
|
|
72
|
+
}
|
|
73
|
+
let data = layerData[0];
|
|
74
|
+
if (layerData.length > 1) {
|
|
75
|
+
let total = 0; for (const p of layerData) total += p.byteLength;
|
|
76
|
+
data = new Uint8Array(total);
|
|
77
|
+
let off = 0; for (const p of layerData) { data.set(p, off); off += p.byteLength; }
|
|
78
|
+
}
|
|
79
|
+
const mipWidth = remaining > 1 ? levelInfo.origWidth : levelInfo.width;
|
|
80
|
+
const mipHeight = remaining > 1 ? levelInfo.origHeight : levelInfo.height;
|
|
81
|
+
levels.push({ width: mipWidth, height: mipHeight, data: data });
|
|
82
|
+
remaining -= 1;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
if (levels.length === 0) throw new Error("maxDimension skipped every mip level");
|
|
86
|
+
return { width: levels[0].width, height: levels[0].height, isUASTC: file.isUASTC(), hasAlpha: hasAlpha, levels: levels };
|
|
87
|
+
} finally {
|
|
88
|
+
cleanup();
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
`;
|
|
92
|
+
/**
|
|
93
|
+
* Creates a worker pool of `workerCount` transcoding workers. The pool lazily
|
|
94
|
+
* materialises its blob URL on first `transcode` call — construction is cheap
|
|
95
|
+
* and synchronous; `dispose()` terminates every worker.
|
|
96
|
+
*/
|
|
97
|
+
export function createKTX2TranscodeWorkerPool(transcoderUrl, workerCount) {
|
|
98
|
+
const base = transcoderUrl.endsWith("/") ? transcoderUrl : `${transcoderUrl}/`;
|
|
99
|
+
const size = Math.max(1, workerCount | 0);
|
|
100
|
+
let initPromise;
|
|
101
|
+
const queue = [];
|
|
102
|
+
const jobIds = new Map();
|
|
103
|
+
let nextJobId = 1;
|
|
104
|
+
let disposed = false;
|
|
105
|
+
const init = async () => {
|
|
106
|
+
initPromise ??= withDecoderTimeout((async () => {
|
|
107
|
+
const jsUrl = `${base}basis_transcoder.js`;
|
|
108
|
+
const wasmUrl = `${base}basis_transcoder.wasm`;
|
|
109
|
+
const [jsText, wasmBinary] = await Promise.all([
|
|
110
|
+
fetch(jsUrl).then((response) => {
|
|
111
|
+
if (!response.ok)
|
|
112
|
+
throw new AssetDecoderUnavailable("ktx2", jsUrl);
|
|
113
|
+
return response.text();
|
|
114
|
+
}),
|
|
115
|
+
fetch(wasmUrl).then(async (response) => {
|
|
116
|
+
if (!response.ok)
|
|
117
|
+
throw new AssetDecoderUnavailable("ktx2", wasmUrl);
|
|
118
|
+
const buffer = await response.arrayBuffer();
|
|
119
|
+
// An SPA index.html fallback answers 200 with HTML — reject here,
|
|
120
|
+
// feeding it to the worker aborts emscripten without a listener.
|
|
121
|
+
if (!looksLikeWasmBinary(buffer))
|
|
122
|
+
throw new AssetDecoderUnavailable("ktx2", wasmUrl);
|
|
123
|
+
return buffer;
|
|
124
|
+
})
|
|
125
|
+
]);
|
|
126
|
+
if (jsText.trimStart().startsWith("<"))
|
|
127
|
+
throw new AssetDecoderUnavailable("ktx2", jsUrl);
|
|
128
|
+
const blob = new Blob([WORKER_SOURCE, "\n", jsText, "\n", DRIVER_SOURCE], { type: "text/javascript" });
|
|
129
|
+
const workerUrl = URL.createObjectURL(blob);
|
|
130
|
+
const workers = [];
|
|
131
|
+
for (let index = 0; index < size; index += 1) {
|
|
132
|
+
const worker = new Worker(workerUrl);
|
|
133
|
+
worker.onmessage = (event) => onWorkerMessage(workers[index], event);
|
|
134
|
+
worker.onerror = () => killWorker(workers[index], "ktx2 transcode worker error");
|
|
135
|
+
worker.onmessageerror = () => killWorker(workers[index], "ktx2 transcode worker message error");
|
|
136
|
+
workers.push({ worker, busy: false, dead: false });
|
|
137
|
+
worker.postMessage({ type: "init", id: 0, wasmBinary: wasmBinary.slice(0) }, [wasmBinary.slice(0)]);
|
|
138
|
+
}
|
|
139
|
+
return workers;
|
|
140
|
+
})(), "ktx2 worker init");
|
|
141
|
+
return initPromise;
|
|
142
|
+
};
|
|
143
|
+
const killWorker = (slot, reason) => {
|
|
144
|
+
if (slot.dead)
|
|
145
|
+
return;
|
|
146
|
+
slot.dead = true;
|
|
147
|
+
if (slot.currentJobId !== undefined) {
|
|
148
|
+
const job = jobIds.get(slot.currentJobId);
|
|
149
|
+
if (job) {
|
|
150
|
+
jobIds.delete(slot.currentJobId);
|
|
151
|
+
job.reject(new Error(reason));
|
|
152
|
+
}
|
|
153
|
+
slot.currentJobId = undefined;
|
|
154
|
+
}
|
|
155
|
+
// With every worker dead the queue can never drain — reject it now.
|
|
156
|
+
void initPromise?.then((workers) => {
|
|
157
|
+
if (workers.every((w) => w.dead)) {
|
|
158
|
+
for (const job of queue.splice(0))
|
|
159
|
+
job.reject(new Error(reason));
|
|
160
|
+
}
|
|
161
|
+
}).catch(() => { });
|
|
162
|
+
};
|
|
163
|
+
const onWorkerMessage = (slot, event) => {
|
|
164
|
+
const message = event.data;
|
|
165
|
+
if (message.type === "ready") {
|
|
166
|
+
slot.busy = false;
|
|
167
|
+
slot.currentJobId = undefined;
|
|
168
|
+
pump(slot);
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
if (message.type === "error" && message.id === 0) {
|
|
172
|
+
// init abort — the worker can never transcode; fail its jobs fast.
|
|
173
|
+
killWorker(slot, message.error ?? "ktx2 transcode worker init failed");
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
const job = message.id !== undefined ? jobIds.get(message.id) : undefined;
|
|
177
|
+
if (job) {
|
|
178
|
+
jobIds.delete(message.id);
|
|
179
|
+
slot.busy = false;
|
|
180
|
+
slot.currentJobId = undefined;
|
|
181
|
+
if (message.type === "transcode")
|
|
182
|
+
job.resolve(message.result);
|
|
183
|
+
else
|
|
184
|
+
job.reject(new Error(message.error ?? "KTX2 transcode failed"));
|
|
185
|
+
}
|
|
186
|
+
pump(slot);
|
|
187
|
+
};
|
|
188
|
+
const pump = (slot) => {
|
|
189
|
+
if (slot.busy || slot.dead || disposed)
|
|
190
|
+
return;
|
|
191
|
+
const job = queue.shift();
|
|
192
|
+
if (!job)
|
|
193
|
+
return;
|
|
194
|
+
slot.busy = true;
|
|
195
|
+
const id = nextJobId++;
|
|
196
|
+
slot.currentJobId = id;
|
|
197
|
+
jobIds.set(id, job);
|
|
198
|
+
const payload = { type: "transcode", id, buffer: job.request.bytes.buffer.slice(0), transcoderFormat: job.request.transcoderFormat, maxDimension: job.request.maxDimension };
|
|
199
|
+
slot.worker.postMessage(payload, [payload.buffer]);
|
|
200
|
+
};
|
|
201
|
+
return {
|
|
202
|
+
async transcode(request) {
|
|
203
|
+
if (disposed)
|
|
204
|
+
throw new Error("KTX2 worker pool is disposed");
|
|
205
|
+
const workers = await init();
|
|
206
|
+
if (workers.every((slot) => slot.dead))
|
|
207
|
+
throw new AssetDecoderUnavailable("ktx2", `${base}basis_transcoder.wasm`);
|
|
208
|
+
return new Promise((resolve, reject) => {
|
|
209
|
+
const job = { request, resolve, reject };
|
|
210
|
+
const free = workers.find((slot) => !slot.busy && !slot.dead);
|
|
211
|
+
if (free) {
|
|
212
|
+
queue.push(job);
|
|
213
|
+
pump(free);
|
|
214
|
+
}
|
|
215
|
+
else
|
|
216
|
+
queue.push(job);
|
|
217
|
+
});
|
|
218
|
+
},
|
|
219
|
+
dispose() {
|
|
220
|
+
disposed = true;
|
|
221
|
+
for (const job of queue.splice(0))
|
|
222
|
+
job.reject(new Error("KTX2 worker pool disposed"));
|
|
223
|
+
for (const job of [...jobIds.values()])
|
|
224
|
+
job.reject(new Error("KTX2 worker pool disposed"));
|
|
225
|
+
jobIds.clear();
|
|
226
|
+
initPromise?.then((workers) => workers.forEach((slot) => slot.worker.terminate())).catch(() => { });
|
|
227
|
+
}
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
//# sourceMappingURL=KTX2TranscodeWorker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"KTX2TranscodeWorker.js","sourceRoot":"","sources":["../../../../packages/assets/src/KTX2TranscodeWorker.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MikkTSpaceTangents.ts — MikkTSpace tangent generation seam (PRD-04 §7.3,
|
|
3
|
+
* P1-6; R8). The wasm module is injected (r185's
|
|
4
|
+
* `examples/jsm/libs/mikktspace.module.js`, vendored by PRD 05 under
|
|
5
|
+
* `packages/assets/vendor/mikktspace/` — request Q-05-1).
|
|
6
|
+
*
|
|
7
|
+
* - `setMikkTSpaceModule` stores the module; `mikkTSpaceAvailable` reports it.
|
|
8
|
+
* - `generateMikkTSpaceTangents` unwelds indexed input (MikkTSpace requires
|
|
9
|
+
* non-indexed streams), runs the module, then flips `.w` — the glTF UV
|
|
10
|
+
* convention, matching `computeMikkTSpaceTangents(geometry, mikk, true)`.
|
|
11
|
+
* - Runs in a module Worker when `Worker` is defined and a worker factory was
|
|
12
|
+
* supplied via `setMikkTSpaceWorkerFactory` (PRD-05 vendored wrapper wires
|
|
13
|
+
* `new Worker(new URL("./mikktspace.worker.js", import.meta.url))`); falls
|
|
14
|
+
* back to the inline call otherwise.
|
|
15
|
+
*/
|
|
16
|
+
export interface MikkTSpaceModule {
|
|
17
|
+
generateTangents(positions: Float32Array, normals: Float32Array, uvs: Float32Array): Float32Array;
|
|
18
|
+
/** r185's module exposes a thenable; `await` covers both. */
|
|
19
|
+
ready?: PromiseLike<void>;
|
|
20
|
+
}
|
|
21
|
+
export interface MikkTSpaceTangentInput {
|
|
22
|
+
readonly positions: Float32Array;
|
|
23
|
+
readonly normals: Float32Array;
|
|
24
|
+
readonly uvs: Float32Array;
|
|
25
|
+
readonly indices?: Uint32Array | Uint16Array;
|
|
26
|
+
}
|
|
27
|
+
export declare function setMikkTSpaceModule(mod: MikkTSpaceModule): void;
|
|
28
|
+
/**
|
|
29
|
+
* Optional: supply a factory that returns the MikkTSpace worker
|
|
30
|
+
* (`new Worker(new URL("...mikktspace.worker.js", import.meta.url), {type:"module"})`).
|
|
31
|
+
* The worker implements the `{id, positions, normals, uvs}` → `{id, tangents}`
|
|
32
|
+
* message protocol defined below.
|
|
33
|
+
*/
|
|
34
|
+
export declare function setMikkTSpaceWorkerFactory(factory: (() => Worker) | undefined): void;
|
|
35
|
+
export declare function mikkTSpaceAvailable(): boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Generate per-vertex tangents (`xyzw`; `w` = handedness sign, glTF
|
|
38
|
+
* convention). When `indices` are present the stream is unwelded first, so
|
|
39
|
+
* the returned array is `indices.length` × 4 — matching three's
|
|
40
|
+
* `computeMikkTSpaceTangents` non-indexed output exactly.
|
|
41
|
+
*/
|
|
42
|
+
export declare function generateMikkTSpaceTangents(input: MikkTSpaceTangentInput): Promise<Float32Array>;
|
|
43
|
+
//# sourceMappingURL=MikkTSpaceTangents.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MikkTSpaceTangents.d.ts","sourceRoot":"","sources":["../../../../packages/assets/src/MikkTSpaceTangents.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,MAAM,WAAW,gBAAgB;IAC/B,gBAAgB,CAAC,SAAS,EAAE,YAAY,EAAE,OAAO,EAAE,YAAY,EAAE,GAAG,EAAE,YAAY,GAAG,YAAY,CAAC;IAClG,6DAA6D;IAC7D,KAAK,CAAC,EAAE,WAAW,CAAC,IAAI,CAAC,CAAC;CAC3B;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,SAAS,EAAE,YAAY,CAAC;IACjC,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAC;IAC/B,QAAQ,CAAC,GAAG,EAAE,YAAY,CAAC;IAC3B,QAAQ,CAAC,OAAO,CAAC,EAAE,WAAW,GAAG,WAAW,CAAC;CAC9C;AAcD,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,gBAAgB,GAAG,IAAI,CAE/D;AAED;;;;;GAKG;AACH,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,CAAC,MAAM,MAAM,CAAC,GAAG,SAAS,GAAG,IAAI,CAMpF;AAED,wBAAgB,mBAAmB,IAAI,OAAO,CAE7C;AAiGD;;;;;GAKG;AACH,wBAAsB,0BAA0B,CAAC,KAAK,EAAE,sBAAsB,GAAG,OAAO,CAAC,YAAY,CAAC,CAarG"}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MikkTSpaceTangents.ts — MikkTSpace tangent generation seam (PRD-04 §7.3,
|
|
3
|
+
* P1-6; R8). The wasm module is injected (r185's
|
|
4
|
+
* `examples/jsm/libs/mikktspace.module.js`, vendored by PRD 05 under
|
|
5
|
+
* `packages/assets/vendor/mikktspace/` — request Q-05-1).
|
|
6
|
+
*
|
|
7
|
+
* - `setMikkTSpaceModule` stores the module; `mikkTSpaceAvailable` reports it.
|
|
8
|
+
* - `generateMikkTSpaceTangents` unwelds indexed input (MikkTSpace requires
|
|
9
|
+
* non-indexed streams), runs the module, then flips `.w` — the glTF UV
|
|
10
|
+
* convention, matching `computeMikkTSpaceTangents(geometry, mikk, true)`.
|
|
11
|
+
* - Runs in a module Worker when `Worker` is defined and a worker factory was
|
|
12
|
+
* supplied via `setMikkTSpaceWorkerFactory` (PRD-05 vendored wrapper wires
|
|
13
|
+
* `new Worker(new URL("./mikktspace.worker.js", import.meta.url))`); falls
|
|
14
|
+
* back to the inline call otherwise.
|
|
15
|
+
*/
|
|
16
|
+
let module_;
|
|
17
|
+
let workerFactory;
|
|
18
|
+
let worker;
|
|
19
|
+
let requestSeq = 0;
|
|
20
|
+
const pending = new Map();
|
|
21
|
+
export function setMikkTSpaceModule(mod) {
|
|
22
|
+
module_ = mod;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Optional: supply a factory that returns the MikkTSpace worker
|
|
26
|
+
* (`new Worker(new URL("...mikktspace.worker.js", import.meta.url), {type:"module"})`).
|
|
27
|
+
* The worker implements the `{id, positions, normals, uvs}` → `{id, tangents}`
|
|
28
|
+
* message protocol defined below.
|
|
29
|
+
*/
|
|
30
|
+
export function setMikkTSpaceWorkerFactory(factory) {
|
|
31
|
+
workerFactory = factory;
|
|
32
|
+
if (!factory && worker) {
|
|
33
|
+
worker.terminate();
|
|
34
|
+
worker = undefined;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
export function mikkTSpaceAvailable() {
|
|
38
|
+
return module_ !== undefined;
|
|
39
|
+
}
|
|
40
|
+
function unweld(input) {
|
|
41
|
+
const indices = input.indices;
|
|
42
|
+
if (!indices) {
|
|
43
|
+
return { positions: input.positions, normals: input.normals, uvs: input.uvs };
|
|
44
|
+
}
|
|
45
|
+
const count = indices.length;
|
|
46
|
+
const positions = new Float32Array(count * 3);
|
|
47
|
+
const normals = new Float32Array(count * 3);
|
|
48
|
+
const uvs = new Float32Array(count * 2);
|
|
49
|
+
for (let i = 0; i < count; i++) {
|
|
50
|
+
const v = indices[i];
|
|
51
|
+
positions[i * 3] = input.positions[v * 3];
|
|
52
|
+
positions[i * 3 + 1] = input.positions[v * 3 + 1];
|
|
53
|
+
positions[i * 3 + 2] = input.positions[v * 3 + 2];
|
|
54
|
+
normals[i * 3] = input.normals[v * 3];
|
|
55
|
+
normals[i * 3 + 1] = input.normals[v * 3 + 1];
|
|
56
|
+
normals[i * 3 + 2] = input.normals[v * 3 + 2];
|
|
57
|
+
uvs[i * 2] = input.uvs[v * 2];
|
|
58
|
+
uvs[i * 2 + 1] = input.uvs[v * 2 + 1];
|
|
59
|
+
}
|
|
60
|
+
return { positions, normals, uvs };
|
|
61
|
+
}
|
|
62
|
+
/** glTF UV convention: flip the handedness sign (three's `negateSign = true`). */
|
|
63
|
+
function applyGltfSign(tangents) {
|
|
64
|
+
for (let i = 3; i < tangents.length; i += 4) {
|
|
65
|
+
tangents[i] *= -1;
|
|
66
|
+
}
|
|
67
|
+
return tangents;
|
|
68
|
+
}
|
|
69
|
+
function acquireWorker() {
|
|
70
|
+
if (typeof Worker === "undefined" || !workerFactory)
|
|
71
|
+
return undefined;
|
|
72
|
+
if (worker)
|
|
73
|
+
return worker;
|
|
74
|
+
const instance = workerFactory();
|
|
75
|
+
instance.onmessage = (event) => {
|
|
76
|
+
const { id, tangents, error } = event.data;
|
|
77
|
+
const entry = pending.get(id);
|
|
78
|
+
if (!entry)
|
|
79
|
+
return;
|
|
80
|
+
pending.delete(id);
|
|
81
|
+
if (error !== undefined)
|
|
82
|
+
entry.reject(new Error(error));
|
|
83
|
+
else
|
|
84
|
+
entry.resolve(new Float32Array(tangents));
|
|
85
|
+
};
|
|
86
|
+
instance.onerror = (event) => {
|
|
87
|
+
for (const entry of pending.values())
|
|
88
|
+
entry.reject(event);
|
|
89
|
+
pending.clear();
|
|
90
|
+
};
|
|
91
|
+
worker = instance;
|
|
92
|
+
return worker;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* T0-18(f): bounded waits — a stuck wasm compile or a wedged worker must not
|
|
96
|
+
* hang the GLB load forever (the caller falls back to the legacy generator on
|
|
97
|
+
* rejection). 30 s matches the largest corpus fixture's worst-case compile.
|
|
98
|
+
*/
|
|
99
|
+
const MIKK_TSPACE_READY_TIMEOUT_MS = 30_000;
|
|
100
|
+
const MIKK_TSPACE_WORKER_TIMEOUT_MS = 30_000;
|
|
101
|
+
function withTimeout(promise, ms, label, onTimeout) {
|
|
102
|
+
return new Promise((resolve, reject) => {
|
|
103
|
+
const timer = setTimeout(() => {
|
|
104
|
+
onTimeout?.();
|
|
105
|
+
reject(new Error(`${label} timed out after ${ms} ms`));
|
|
106
|
+
}, ms);
|
|
107
|
+
promise.then((value) => { clearTimeout(timer); resolve(value); }, (error) => { clearTimeout(timer); reject(error); });
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
function runInWorker(w, input) {
|
|
111
|
+
const id = requestSeq++;
|
|
112
|
+
// Copy into tight buffers: caller arrays may be views into larger buffers.
|
|
113
|
+
const positions = new Float32Array(input.positions);
|
|
114
|
+
const normals = new Float32Array(input.normals);
|
|
115
|
+
const uvs = new Float32Array(input.uvs);
|
|
116
|
+
const work = new Promise((resolve, reject) => {
|
|
117
|
+
pending.set(id, { resolve, reject });
|
|
118
|
+
w.postMessage({ id, positions: positions.buffer, normals: normals.buffer, uvs: uvs.buffer }, [positions.buffer, normals.buffer, uvs.buffer]);
|
|
119
|
+
});
|
|
120
|
+
return withTimeout(work, MIKK_TSPACE_WORKER_TIMEOUT_MS, "MikkTSpace worker tangent generation", () => {
|
|
121
|
+
pending.delete(id);
|
|
122
|
+
// The worker is wedged; drop it so the next call retries on a fresh one.
|
|
123
|
+
if (worker === w) {
|
|
124
|
+
worker = undefined;
|
|
125
|
+
}
|
|
126
|
+
w.terminate();
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Generate per-vertex tangents (`xyzw`; `w` = handedness sign, glTF
|
|
131
|
+
* convention). When `indices` are present the stream is unwelded first, so
|
|
132
|
+
* the returned array is `indices.length` × 4 — matching three's
|
|
133
|
+
* `computeMikkTSpaceTangents` non-indexed output exactly.
|
|
134
|
+
*/
|
|
135
|
+
export async function generateMikkTSpaceTangents(input) {
|
|
136
|
+
if (!module_) {
|
|
137
|
+
throw new Error("MikkTSpace module not installed: call setMikkTSpaceModule() first");
|
|
138
|
+
}
|
|
139
|
+
await withTimeout(Promise.resolve(module_.ready), MIKK_TSPACE_READY_TIMEOUT_MS, "MikkTSpace module ready");
|
|
140
|
+
const streams = unweld(input);
|
|
141
|
+
const w = acquireWorker();
|
|
142
|
+
if (w) {
|
|
143
|
+
const tangents = await runInWorker(w, streams);
|
|
144
|
+
return applyGltfSign(tangents);
|
|
145
|
+
}
|
|
146
|
+
const tangents = module_.generateTangents(streams.positions, streams.normals, streams.uvs);
|
|
147
|
+
return applyGltfSign(tangents);
|
|
148
|
+
}
|
|
149
|
+
//# sourceMappingURL=MikkTSpaceTangents.js.map
|