@stowkit/three-loader 0.1.2 → 0.1.4
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 +548 -80
- package/dist/MeshParser.d.ts +9 -11
- package/dist/MeshParser.d.ts.map +1 -1
- package/dist/StowKitLoader.d.ts +13 -0
- package/dist/StowKitLoader.d.ts.map +1 -1
- package/dist/stowkit-three-loader.esm.js +107 -97
- package/dist/stowkit-three-loader.esm.js.map +1 -1
- package/dist/stowkit-three-loader.js +106 -96
- package/dist/stowkit-three-loader.js.map +1 -1
- package/package.json +5 -3
- package/public/draco/draco_decoder.js +33 -0
- package/public/draco/draco_decoder.wasm +0 -0
- package/public/draco/draco_wasm_wrapper.js +116 -0
package/dist/MeshParser.d.ts
CHANGED
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
import * as THREE from 'three';
|
|
2
|
+
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js';
|
|
2
3
|
export interface MeshGeometryInfo {
|
|
3
4
|
vertexCount: number;
|
|
4
5
|
indexCount: number;
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
vertexBufferSize: number;
|
|
10
|
-
indexBufferSize: number;
|
|
11
|
-
normalBufferSize: number;
|
|
12
|
-
uvBufferSize: number;
|
|
6
|
+
hasNormals: number;
|
|
7
|
+
hasUVs: number;
|
|
8
|
+
compressedBufferOffset: number;
|
|
9
|
+
compressedBufferSize: number;
|
|
13
10
|
materialIndex: number;
|
|
11
|
+
padding: number;
|
|
14
12
|
}
|
|
15
13
|
export interface Node {
|
|
16
14
|
name: string;
|
|
@@ -55,13 +53,13 @@ export declare class MeshParser {
|
|
|
55
53
|
meshIndices: Uint32Array;
|
|
56
54
|
};
|
|
57
55
|
/**
|
|
58
|
-
* Create Three.js BufferGeometry from mesh data
|
|
56
|
+
* Create Three.js BufferGeometry from Draco compressed mesh data
|
|
59
57
|
*/
|
|
60
|
-
static createGeometry(geoInfo: MeshGeometryInfo, dataBlob: Uint8Array): THREE.BufferGeometry
|
|
58
|
+
static createGeometry(geoInfo: MeshGeometryInfo, dataBlob: Uint8Array, dracoLoader: DRACOLoader): Promise<THREE.BufferGeometry>;
|
|
61
59
|
/**
|
|
62
60
|
* Build Three.js scene from parsed mesh data
|
|
63
61
|
*/
|
|
64
|
-
static buildScene(parsedData: ReturnType<typeof MeshParser.parseMeshData>, dataBlob: Uint8Array): THREE.Group
|
|
62
|
+
static buildScene(parsedData: ReturnType<typeof MeshParser.parseMeshData>, dataBlob: Uint8Array, dracoLoader: DRACOLoader): Promise<THREE.Group>;
|
|
65
63
|
/**
|
|
66
64
|
* Read null-terminated string from buffer
|
|
67
65
|
*/
|
package/dist/MeshParser.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MeshParser.d.ts","sourceRoot":"","sources":["../src/MeshParser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"MeshParser.d.ts","sourceRoot":"","sources":["../src/MeshParser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,EAAE,WAAW,EAAE,MAAM,2CAA2C,CAAC;AAExE,MAAM,WAAW,gBAAgB;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,sBAAsB,EAAE,MAAM,CAAC;IAC/B,oBAAoB,EAAE,MAAM,CAAC;IAC7B,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,IAAI;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IAC3C,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,qBAAqB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IACxC,SAAS,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,YAAY;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,qBAAqB,EAAE,CAAC;CACvC;AAED,MAAM,WAAW,YAAY;IACzB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;CACpB;AAED,qBAAa,UAAU;IAEnB;;OAEG;IACH,MAAM,CAAC,iBAAiB,CAAC,IAAI,EAAE,UAAU,GAAG,YAAY;IAcxD;;OAEG;IACH,MAAM,CAAC,aAAa,CAAC,YAAY,EAAE,UAAU,EAAE,QAAQ,EAAE,UAAU,GAAG;QAClE,QAAQ,EAAE,YAAY,CAAC;QACvB,UAAU,EAAE,gBAAgB,EAAE,CAAC;QAC/B,SAAS,EAAE,KAAK,CAAC,QAAQ,EAAE,CAAC;QAC5B,YAAY,EAAE,YAAY,EAAE,CAAC;QAC7B,KAAK,EAAE,IAAI,EAAE,CAAC;QACd,WAAW,EAAE,WAAW,CAAC;KAC5B;IAiMD;;OAEG;WACU,cAAc,CAAC,OAAO,EAAE,gBAAgB,EAAE,QAAQ,EAAE,UAAU,EAAE,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,KAAK,CAAC,cAAc,CAAC;IA4DrI;;OAEG;WACU,UAAU,CAAC,UAAU,EAAE,UAAU,CAAC,OAAO,UAAU,CAAC,aAAa,CAAC,EAAE,QAAQ,EAAE,UAAU,EAAE,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC;IAuItJ;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,UAAU;CAK5B"}
|
package/dist/StowKitLoader.d.ts
CHANGED
|
@@ -33,8 +33,13 @@ export interface StowKitLoaderParameters {
|
|
|
33
33
|
export declare class StowKitLoader extends THREE.Loader {
|
|
34
34
|
private reader;
|
|
35
35
|
private ktx2Loader;
|
|
36
|
+
private dracoLoader;
|
|
36
37
|
private ownedReader;
|
|
37
38
|
constructor(manager?: THREE.LoadingManager, parameters?: StowKitLoaderParameters);
|
|
39
|
+
/**
|
|
40
|
+
* Set the path to the Draco decoder
|
|
41
|
+
*/
|
|
42
|
+
setDracoDecoderPath(path: string): this;
|
|
38
43
|
/**
|
|
39
44
|
* Set the path to the Basis Universal transcoder
|
|
40
45
|
*/
|
|
@@ -48,6 +53,14 @@ export declare class StowKitLoader extends THREE.Loader {
|
|
|
48
53
|
* @param url - URL to the .stow file
|
|
49
54
|
*/
|
|
50
55
|
openPack(url: string): Promise<void>;
|
|
56
|
+
/**
|
|
57
|
+
* Load and parse a mesh asset by its index
|
|
58
|
+
* @param index - Asset index
|
|
59
|
+
* @param onLoad - Callback when loading completes
|
|
60
|
+
* @param onProgress - Progress callback
|
|
61
|
+
* @param onError - Error callback
|
|
62
|
+
*/
|
|
63
|
+
loadMeshByIndex(index: number, onLoad?: (scene: THREE.Group) => void, onProgress?: (event: ProgressEvent) => void, onError?: (error: Error) => void): Promise<THREE.Group>;
|
|
51
64
|
/**
|
|
52
65
|
* Load and parse a mesh asset from a StowKit pack
|
|
53
66
|
* @param url - URL to the .stow file (or omit if already opened with openPack)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StowKitLoader.d.ts","sourceRoot":"","sources":["../src/StowKitLoader.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"StowKitLoader.d.ts","sourceRoot":"","sources":["../src/StowKitLoader.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as THREE from 'three';
|
|
2
2
|
import { KTX2Loader } from 'three/examples/jsm/loaders/KTX2Loader.js';
|
|
3
|
+
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js';
|
|
3
4
|
import { StowKitReader } from '@stowkit/reader';
|
|
4
5
|
|
|
5
6
|
class MeshParser {
|
|
@@ -30,8 +31,8 @@ class MeshParser {
|
|
|
30
31
|
console.log(`Mesh counts: ${metadata.meshGeometryCount} geometries, ${metadata.materialCount} materials, ${metadata.nodeCount} nodes`);
|
|
31
32
|
// Check if the metadata blob contains just the header or all the mesh info
|
|
32
33
|
const expectedMetadataSize = 140 +
|
|
33
|
-
(metadata.meshGeometryCount *
|
|
34
|
-
(metadata.materialCount *
|
|
34
|
+
(metadata.meshGeometryCount * 40) + // Draco compressed (40 bytes each)
|
|
35
|
+
(metadata.materialCount * 196) + // MaterialData header
|
|
35
36
|
(metadata.nodeCount * 116);
|
|
36
37
|
let hasExtendedMetadata = metadataBlob.length >= expectedMetadataSize;
|
|
37
38
|
console.log(`Expected metadata size: ${expectedMetadataSize}, actual: ${metadataBlob.length}, has extended: ${hasExtendedMetadata}`);
|
|
@@ -39,28 +40,25 @@ class MeshParser {
|
|
|
39
40
|
const sourceBlob = hasExtendedMetadata ? metadataBlob : dataBlob;
|
|
40
41
|
const metaView = new DataView(sourceBlob.buffer, sourceBlob.byteOffset, sourceBlob.byteLength);
|
|
41
42
|
let offset = hasExtendedMetadata ? 140 : 0; // Start after MeshMetadata if in metadata blob, or at beginning of data blob
|
|
42
|
-
// Parse geometry infos
|
|
43
|
+
// Parse geometry infos (now Draco compressed!)
|
|
43
44
|
const geometries = [];
|
|
44
45
|
for (let i = 0; i < metadata.meshGeometryCount; i++) {
|
|
45
|
-
if (offset +
|
|
46
|
+
if (offset + 40 > sourceBlob.length) {
|
|
46
47
|
console.warn(`Data truncated at geometry ${i}/${metadata.meshGeometryCount}`);
|
|
47
48
|
break;
|
|
48
49
|
}
|
|
49
50
|
const geo = {
|
|
50
51
|
vertexCount: metaView.getUint32(offset, true),
|
|
51
52
|
indexCount: metaView.getUint32(offset + 4, true),
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
normalBufferSize: Number(metaView.getBigUint64(offset + 56, true)),
|
|
59
|
-
uvBufferSize: Number(metaView.getBigUint64(offset + 64, true)),
|
|
60
|
-
materialIndex: metaView.getUint32(offset + 72, true)
|
|
53
|
+
hasNormals: metaView.getUint32(offset + 8, true),
|
|
54
|
+
hasUVs: metaView.getUint32(offset + 12, true),
|
|
55
|
+
compressedBufferOffset: Number(metaView.getBigUint64(offset + 16, true)),
|
|
56
|
+
compressedBufferSize: Number(metaView.getBigUint64(offset + 24, true)),
|
|
57
|
+
materialIndex: metaView.getUint32(offset + 32, true),
|
|
58
|
+
padding: metaView.getUint32(offset + 36, true)
|
|
61
59
|
};
|
|
62
60
|
geometries.push(geo);
|
|
63
|
-
offset +=
|
|
61
|
+
offset += 40; // Size of MeshGeometryInfo (Draco compressed)
|
|
64
62
|
}
|
|
65
63
|
// Parse materials - MaterialData structure: name[64] + schema_id[128] + property_count(4) = 196 bytes
|
|
66
64
|
const materialData = [];
|
|
@@ -187,76 +185,40 @@ class MeshParser {
|
|
|
187
185
|
return { metadata, geometries, materials, materialData, nodes, meshIndices };
|
|
188
186
|
}
|
|
189
187
|
/**
|
|
190
|
-
* Create Three.js BufferGeometry from mesh data
|
|
188
|
+
* Create Three.js BufferGeometry from Draco compressed mesh data
|
|
191
189
|
*/
|
|
192
|
-
static createGeometry(geoInfo, dataBlob) {
|
|
193
|
-
console.log('
|
|
190
|
+
static async createGeometry(geoInfo, dataBlob, dracoLoader) {
|
|
191
|
+
console.log('Decoding Draco geometry:', {
|
|
194
192
|
vertexCount: geoInfo.vertexCount,
|
|
195
193
|
indexCount: geoInfo.indexCount,
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
194
|
+
hasNormals: geoInfo.hasNormals,
|
|
195
|
+
hasUVs: geoInfo.hasUVs,
|
|
196
|
+
compressedOffset: geoInfo.compressedBufferOffset,
|
|
197
|
+
compressedSize: geoInfo.compressedBufferSize
|
|
199
198
|
});
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
// Compute normals if not provided
|
|
224
|
-
geometry.computeVertexNormals();
|
|
225
|
-
}
|
|
226
|
-
// Extract UVs
|
|
227
|
-
if (geoInfo.uvBufferSize > 0 && geoInfo.uvBufferOffset + geoInfo.uvBufferSize <= dataBlob.length) {
|
|
228
|
-
const uvs = new Float32Array(geoInfo.vertexCount * 2);
|
|
229
|
-
for (let i = 0; i < geoInfo.vertexCount * 2; i += 2) {
|
|
230
|
-
uvs[i] = dataView.getFloat32(geoInfo.uvBufferOffset + i * 4, true); // U
|
|
231
|
-
uvs[i + 1] = 1.0 - dataView.getFloat32(geoInfo.uvBufferOffset + (i + 1) * 4, true); // V (flipped)
|
|
232
|
-
}
|
|
233
|
-
geometry.setAttribute('uv', new THREE.BufferAttribute(uvs, 2));
|
|
234
|
-
}
|
|
235
|
-
// Extract indices
|
|
236
|
-
if (geoInfo.indexCount > 0 && geoInfo.indexBufferOffset + geoInfo.indexBufferSize <= dataBlob.length) {
|
|
237
|
-
const indices = new Uint32Array(geoInfo.indexCount);
|
|
238
|
-
// Read the first index to determine the base offset
|
|
239
|
-
let minIndex = Number.MAX_SAFE_INTEGER;
|
|
240
|
-
for (let i = 0; i < geoInfo.indexCount; i++) {
|
|
241
|
-
const index = dataView.getUint32(geoInfo.indexBufferOffset + i * 4, true);
|
|
242
|
-
if (index < minIndex)
|
|
243
|
-
minIndex = index;
|
|
244
|
-
}
|
|
245
|
-
// Read indices again and offset them to start from 0
|
|
246
|
-
for (let i = 0; i < geoInfo.indexCount; i++) {
|
|
247
|
-
const absoluteIndex = dataView.getUint32(geoInfo.indexBufferOffset + i * 4, true);
|
|
248
|
-
indices[i] = absoluteIndex - minIndex;
|
|
249
|
-
}
|
|
250
|
-
geometry.setIndex(new THREE.BufferAttribute(indices, 1));
|
|
251
|
-
console.log(`Added indices (offset by ${minIndex}):`, indices.slice(0, 12)); // First 4 triangles
|
|
252
|
-
}
|
|
253
|
-
else if (geoInfo.indexCount > 0) {
|
|
254
|
-
console.warn('Index data out of bounds');
|
|
255
|
-
}
|
|
256
|
-
// Ensure we have normals (compute if missing)
|
|
257
|
-
if (!geometry.attributes.normal) {
|
|
258
|
-
geometry.computeVertexNormals();
|
|
259
|
-
}
|
|
199
|
+
// Extract the Draco compressed buffer
|
|
200
|
+
if (geoInfo.compressedBufferOffset + geoInfo.compressedBufferSize > dataBlob.length) {
|
|
201
|
+
throw new Error(`Compressed buffer out of bounds: offset=${geoInfo.compressedBufferOffset}, size=${geoInfo.compressedBufferSize}, dataLength=${dataBlob.length}`);
|
|
202
|
+
}
|
|
203
|
+
const compressedData = dataBlob.slice(geoInfo.compressedBufferOffset, geoInfo.compressedBufferOffset + geoInfo.compressedBufferSize);
|
|
204
|
+
// Decode using Draco
|
|
205
|
+
// Create a blob URL for the Draco data (DRACOLoader expects a URL)
|
|
206
|
+
const arrayBuffer = compressedData.buffer.slice(compressedData.byteOffset, compressedData.byteOffset + compressedData.byteLength);
|
|
207
|
+
const blob = new Blob([arrayBuffer]);
|
|
208
|
+
const url = URL.createObjectURL(blob);
|
|
209
|
+
const geometry = await new Promise((resolve, reject) => {
|
|
210
|
+
dracoLoader.load(url, (decoded) => {
|
|
211
|
+
URL.revokeObjectURL(url);
|
|
212
|
+
console.log('Draco decoded successfully:', decoded);
|
|
213
|
+
// UVs are pre-flipped in the packer now, no need to flip here
|
|
214
|
+
resolve(decoded);
|
|
215
|
+
}, undefined, (error) => {
|
|
216
|
+
URL.revokeObjectURL(url);
|
|
217
|
+
console.error('Draco decode failed:', error);
|
|
218
|
+
reject(new Error(`Failed to decode Draco geometry: ${error}`));
|
|
219
|
+
});
|
|
220
|
+
});
|
|
221
|
+
// Compute bounding volumes
|
|
260
222
|
geometry.computeBoundingSphere();
|
|
261
223
|
geometry.computeBoundingBox();
|
|
262
224
|
console.log('Final geometry:', {
|
|
@@ -271,7 +233,7 @@ class MeshParser {
|
|
|
271
233
|
/**
|
|
272
234
|
* Build Three.js scene from parsed mesh data
|
|
273
235
|
*/
|
|
274
|
-
static buildScene(parsedData, dataBlob) {
|
|
236
|
+
static async buildScene(parsedData, dataBlob, dracoLoader) {
|
|
275
237
|
const root = new THREE.Group();
|
|
276
238
|
root.name = 'StowKitMesh';
|
|
277
239
|
const { geometries, materials, nodes, meshIndices } = parsedData;
|
|
@@ -299,7 +261,7 @@ class MeshParser {
|
|
|
299
261
|
if (meshIndex < geometries.length) {
|
|
300
262
|
const geoInfo = geometries[meshIndex];
|
|
301
263
|
console.log(` Creating geometry from geoInfo: vertices=${geoInfo.vertexCount}, indices=${geoInfo.indexCount}, material=${geoInfo.materialIndex}`);
|
|
302
|
-
const geometry = this.createGeometry(geoInfo, dataBlob);
|
|
264
|
+
const geometry = await this.createGeometry(geoInfo, dataBlob, dracoLoader);
|
|
303
265
|
// Use assigned material if valid, otherwise create default
|
|
304
266
|
let material;
|
|
305
267
|
if (geoInfo.materialIndex < materials.length) {
|
|
@@ -342,9 +304,10 @@ class MeshParser {
|
|
|
342
304
|
// If no nodes, create a single mesh from all geometries
|
|
343
305
|
if (nodes.length === 0 && geometries.length > 0) {
|
|
344
306
|
console.log('No nodes found, creating meshes directly from geometries');
|
|
345
|
-
geometries.
|
|
307
|
+
for (let index = 0; index < geometries.length; index++) {
|
|
308
|
+
const geoInfo = geometries[index];
|
|
346
309
|
console.log(`Creating direct mesh ${index}: materialIndex=${geoInfo.materialIndex}, vertices=${geoInfo.vertexCount}`);
|
|
347
|
-
const geometry = this.createGeometry(geoInfo, dataBlob);
|
|
310
|
+
const geometry = await this.createGeometry(geoInfo, dataBlob, dracoLoader);
|
|
348
311
|
// Use assigned material if valid, otherwise create default
|
|
349
312
|
let material;
|
|
350
313
|
if (geoInfo.materialIndex < materials.length) {
|
|
@@ -363,7 +326,7 @@ class MeshParser {
|
|
|
363
326
|
mesh.name = `Mesh_${index}`;
|
|
364
327
|
root.add(mesh);
|
|
365
328
|
console.log(`Added mesh ${index} to root`);
|
|
366
|
-
}
|
|
329
|
+
}
|
|
367
330
|
}
|
|
368
331
|
console.log(`Final scene has ${root.children.length} direct children`);
|
|
369
332
|
// Debug: traverse and count all meshes, showing full hierarchy
|
|
@@ -430,16 +393,26 @@ class StowKitLoader extends THREE.Loader {
|
|
|
430
393
|
this.reader = new StowKitReader(wasmPath);
|
|
431
394
|
this.ownedReader = true;
|
|
432
395
|
}
|
|
433
|
-
// Setup KTX2 loader for textures with default path
|
|
434
|
-
const transcoderPath = parameters?.transcoderPath || '/basis/';
|
|
396
|
+
// Setup KTX2 loader for textures with default path (user must provide basis files)
|
|
397
|
+
const transcoderPath = parameters?.transcoderPath || '/basis/'; // Users still need to provide this
|
|
435
398
|
this.ktx2Loader = new KTX2Loader(manager);
|
|
436
399
|
this.ktx2Loader.setTranscoderPath(transcoderPath);
|
|
400
|
+
// Setup Draco loader for mesh decompression
|
|
401
|
+
this.dracoLoader = new DRACOLoader(manager);
|
|
402
|
+
this.dracoLoader.setDecoderPath('/stowkit/draco/'); // Draco files copied by postinstall
|
|
437
403
|
// Detect support with a temporary renderer
|
|
438
404
|
// This is required for KTX2Loader to work
|
|
439
405
|
const tempRenderer = new THREE.WebGLRenderer();
|
|
440
406
|
this.ktx2Loader.detectSupport(tempRenderer);
|
|
441
407
|
tempRenderer.dispose();
|
|
442
408
|
}
|
|
409
|
+
/**
|
|
410
|
+
* Set the path to the Draco decoder
|
|
411
|
+
*/
|
|
412
|
+
setDracoDecoderPath(path) {
|
|
413
|
+
this.dracoLoader.setDecoderPath(path);
|
|
414
|
+
return this;
|
|
415
|
+
}
|
|
443
416
|
/**
|
|
444
417
|
* Set the path to the Basis Universal transcoder
|
|
445
418
|
*/
|
|
@@ -469,6 +442,42 @@ class StowKitLoader extends THREE.Loader {
|
|
|
469
442
|
const arrayBuffer = await response.arrayBuffer();
|
|
470
443
|
await this.reader.open(arrayBuffer);
|
|
471
444
|
}
|
|
445
|
+
/**
|
|
446
|
+
* Load and parse a mesh asset by its index
|
|
447
|
+
* @param index - Asset index
|
|
448
|
+
* @param onLoad - Callback when loading completes
|
|
449
|
+
* @param onProgress - Progress callback
|
|
450
|
+
* @param onError - Error callback
|
|
451
|
+
*/
|
|
452
|
+
async loadMeshByIndex(index, onLoad, onProgress, onError) {
|
|
453
|
+
try {
|
|
454
|
+
// Read mesh data and metadata
|
|
455
|
+
const data = this.reader.readAssetData(index);
|
|
456
|
+
const metadata = this.reader.readAssetMetadata(index);
|
|
457
|
+
if (!data) {
|
|
458
|
+
throw new Error(`Failed to read mesh data for index ${index}`);
|
|
459
|
+
}
|
|
460
|
+
if (!metadata) {
|
|
461
|
+
throw new Error(`No metadata available for index ${index}`);
|
|
462
|
+
}
|
|
463
|
+
// Parse mesh data
|
|
464
|
+
const parsedData = MeshParser.parseMeshData(metadata, data);
|
|
465
|
+
// Load textures for materials
|
|
466
|
+
await this.loadMaterialTextures(parsedData.materialData, parsedData.materials);
|
|
467
|
+
// Build Three.js scene with Draco decoder
|
|
468
|
+
const scene = await MeshParser.buildScene(parsedData, data, this.dracoLoader);
|
|
469
|
+
if (onLoad) {
|
|
470
|
+
onLoad(scene);
|
|
471
|
+
}
|
|
472
|
+
return scene;
|
|
473
|
+
}
|
|
474
|
+
catch (error) {
|
|
475
|
+
if (onError) {
|
|
476
|
+
onError(error);
|
|
477
|
+
}
|
|
478
|
+
throw error;
|
|
479
|
+
}
|
|
480
|
+
}
|
|
472
481
|
/**
|
|
473
482
|
* Load and parse a mesh asset from a StowKit pack
|
|
474
483
|
* @param url - URL to the .stow file (or omit if already opened with openPack)
|
|
@@ -501,8 +510,8 @@ class StowKitLoader extends THREE.Loader {
|
|
|
501
510
|
const parsedData = MeshParser.parseMeshData(metadata, data);
|
|
502
511
|
// Load textures for materials
|
|
503
512
|
await this.loadMaterialTextures(parsedData.materialData, parsedData.materials);
|
|
504
|
-
// Build Three.js scene
|
|
505
|
-
const scene = MeshParser.buildScene(parsedData, data);
|
|
513
|
+
// Build Three.js scene with Draco decoder
|
|
514
|
+
const scene = await MeshParser.buildScene(parsedData, data, this.dracoLoader);
|
|
506
515
|
if (onLoad) {
|
|
507
516
|
onLoad(scene);
|
|
508
517
|
}
|
|
@@ -738,20 +747,20 @@ class StowKitLoader extends THREE.Loader {
|
|
|
738
747
|
if (assetIndex < 0)
|
|
739
748
|
return null;
|
|
740
749
|
const metadata = this.reader.readAssetMetadata(assetIndex);
|
|
741
|
-
if (!metadata || metadata.length <
|
|
742
|
-
return null;
|
|
750
|
+
if (!metadata || metadata.length < 144)
|
|
751
|
+
return null; // TextureMetadata is now 144 bytes
|
|
743
752
|
const view = new DataView(metadata.buffer, metadata.byteOffset, metadata.byteLength);
|
|
744
753
|
const decoder = new TextDecoder();
|
|
745
|
-
// Parse TextureMetadata structure
|
|
746
|
-
const stringIdBytes = metadata.slice(
|
|
754
|
+
// Parse TextureMetadata structure (width, height, channels, channel_format, string_id[128])
|
|
755
|
+
const stringIdBytes = metadata.slice(16, 144); // After 4 uint32s (16 bytes)
|
|
747
756
|
const nullIdx = stringIdBytes.indexOf(0);
|
|
748
757
|
const stringId = decoder.decode(stringIdBytes.slice(0, nullIdx >= 0 ? nullIdx : 128));
|
|
749
758
|
return {
|
|
750
759
|
width: view.getUint32(0, true),
|
|
751
760
|
height: view.getUint32(4, true),
|
|
752
761
|
channels: view.getUint32(8, true),
|
|
753
|
-
isKtx2:
|
|
754
|
-
channelFormat: view.getUint32(
|
|
762
|
+
isKtx2: true, // All textures are KTX2 now
|
|
763
|
+
channelFormat: view.getUint32(12, true),
|
|
755
764
|
stringId
|
|
756
765
|
};
|
|
757
766
|
}
|
|
@@ -880,8 +889,8 @@ class StowKitLoader extends THREE.Loader {
|
|
|
880
889
|
const materialCount = view.getUint32(4, true);
|
|
881
890
|
const nodeCount = view.getUint32(8, true);
|
|
882
891
|
let offset = 140;
|
|
883
|
-
// Skip geometries (
|
|
884
|
-
offset += meshGeometryCount *
|
|
892
|
+
// Skip geometries (40 bytes each - Draco compressed)
|
|
893
|
+
offset += meshGeometryCount * 40;
|
|
885
894
|
// Parse materials
|
|
886
895
|
const materials = [];
|
|
887
896
|
for (let i = 0; i < materialCount; i++) {
|
|
@@ -1002,6 +1011,7 @@ class StowKitLoader extends THREE.Loader {
|
|
|
1002
1011
|
*/
|
|
1003
1012
|
dispose() {
|
|
1004
1013
|
this.ktx2Loader.dispose();
|
|
1014
|
+
this.dracoLoader.dispose();
|
|
1005
1015
|
if (this.ownedReader) {
|
|
1006
1016
|
this.reader.close();
|
|
1007
1017
|
}
|