@stowkit/three-loader 0.1.29 → 0.1.31
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/AssetCache.d.ts +54 -0
- package/dist/AssetCache.d.ts.map +1 -0
- package/dist/MeshParser.d.ts +2 -2
- package/dist/MeshParser.d.ts.map +1 -1
- package/dist/StowKitLoader.d.ts +4 -1
- package/dist/StowKitLoader.d.ts.map +1 -1
- package/dist/StowKitPack.d.ts +3 -1
- package/dist/StowKitPack.d.ts.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/stowkit-three-loader.esm.js +245 -38
- package/dist/stowkit-three-loader.esm.js.map +1 -1
- package/dist/stowkit-three-loader.js +245 -37
- package/dist/stowkit-three-loader.js.map +1 -1
- package/package.json +1 -1
|
@@ -24,6 +24,154 @@ function _interopNamespaceDefault(e) {
|
|
|
24
24
|
|
|
25
25
|
var THREE__namespace = /*#__PURE__*/_interopNamespaceDefault(THREE);
|
|
26
26
|
|
|
27
|
+
/**
|
|
28
|
+
* Persistent asset cache using IndexedDB
|
|
29
|
+
* Caches decoded Draco geometries and transcoded Basis textures
|
|
30
|
+
* Invalidates when source .stow file changes
|
|
31
|
+
*/
|
|
32
|
+
class AssetMemoryCache {
|
|
33
|
+
static async init() {
|
|
34
|
+
if (this.db)
|
|
35
|
+
return;
|
|
36
|
+
if (this.initPromise)
|
|
37
|
+
return this.initPromise;
|
|
38
|
+
this.initPromise = new Promise((resolve, reject) => {
|
|
39
|
+
const request = indexedDB.open(this.DB_NAME, this.DB_VERSION);
|
|
40
|
+
request.onerror = () => reject(request.error);
|
|
41
|
+
request.onsuccess = () => {
|
|
42
|
+
this.db = request.result;
|
|
43
|
+
resolve();
|
|
44
|
+
};
|
|
45
|
+
request.onupgradeneeded = (event) => {
|
|
46
|
+
const db = event.target.result;
|
|
47
|
+
if (!db.objectStoreNames.contains(this.GEOMETRY_STORE)) {
|
|
48
|
+
db.createObjectStore(this.GEOMETRY_STORE);
|
|
49
|
+
}
|
|
50
|
+
if (!db.objectStoreNames.contains(this.TEXTURE_STORE)) {
|
|
51
|
+
db.createObjectStore(this.TEXTURE_STORE);
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
});
|
|
55
|
+
return this.initPromise;
|
|
56
|
+
}
|
|
57
|
+
static async getGeometry(key, version) {
|
|
58
|
+
try {
|
|
59
|
+
await this.init();
|
|
60
|
+
if (!this.db)
|
|
61
|
+
return null;
|
|
62
|
+
return new Promise((resolve) => {
|
|
63
|
+
const transaction = this.db.transaction([this.GEOMETRY_STORE], 'readonly');
|
|
64
|
+
const store = transaction.objectStore(this.GEOMETRY_STORE);
|
|
65
|
+
const request = store.get(key);
|
|
66
|
+
request.onsuccess = () => {
|
|
67
|
+
const cached = request.result;
|
|
68
|
+
if (cached && cached.version === version) {
|
|
69
|
+
resolve(cached);
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
resolve(null);
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
request.onerror = () => resolve(null);
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
static async setGeometry(key, version, geometry) {
|
|
83
|
+
try {
|
|
84
|
+
await this.init();
|
|
85
|
+
if (!this.db)
|
|
86
|
+
return;
|
|
87
|
+
const cached = { ...geometry, version };
|
|
88
|
+
return new Promise((resolve) => {
|
|
89
|
+
const transaction = this.db.transaction([this.GEOMETRY_STORE], 'readwrite');
|
|
90
|
+
const store = transaction.objectStore(this.GEOMETRY_STORE);
|
|
91
|
+
store.put(cached, key);
|
|
92
|
+
transaction.oncomplete = () => resolve();
|
|
93
|
+
transaction.onerror = () => resolve();
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
// Silently fail if IndexedDB unavailable
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
static async getTexture(key, version) {
|
|
101
|
+
try {
|
|
102
|
+
await this.init();
|
|
103
|
+
if (!this.db)
|
|
104
|
+
return null;
|
|
105
|
+
return new Promise((resolve) => {
|
|
106
|
+
const transaction = this.db.transaction([this.TEXTURE_STORE], 'readonly');
|
|
107
|
+
const store = transaction.objectStore(this.TEXTURE_STORE);
|
|
108
|
+
const request = store.get(key);
|
|
109
|
+
request.onsuccess = () => {
|
|
110
|
+
const cached = request.result;
|
|
111
|
+
if (cached && cached.version === version) {
|
|
112
|
+
resolve(cached);
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
resolve(null);
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
request.onerror = () => resolve(null);
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
catch {
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
static async setTexture(key, version, textureData) {
|
|
126
|
+
try {
|
|
127
|
+
await this.init();
|
|
128
|
+
if (!this.db)
|
|
129
|
+
return;
|
|
130
|
+
const cached = { ...textureData, version };
|
|
131
|
+
return new Promise((resolve) => {
|
|
132
|
+
const transaction = this.db.transaction([this.TEXTURE_STORE], 'readwrite');
|
|
133
|
+
const store = transaction.objectStore(this.TEXTURE_STORE);
|
|
134
|
+
store.put(cached, key);
|
|
135
|
+
transaction.oncomplete = () => resolve();
|
|
136
|
+
transaction.onerror = () => resolve();
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
catch {
|
|
140
|
+
// Silently fail if IndexedDB unavailable
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
static async clear() {
|
|
144
|
+
try {
|
|
145
|
+
await this.init();
|
|
146
|
+
if (!this.db)
|
|
147
|
+
return;
|
|
148
|
+
return new Promise((resolve) => {
|
|
149
|
+
const transaction = this.db.transaction([this.GEOMETRY_STORE, this.TEXTURE_STORE], 'readwrite');
|
|
150
|
+
transaction.objectStore(this.GEOMETRY_STORE).clear();
|
|
151
|
+
transaction.objectStore(this.TEXTURE_STORE).clear();
|
|
152
|
+
transaction.oncomplete = () => resolve();
|
|
153
|
+
transaction.onerror = () => resolve();
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
catch {
|
|
157
|
+
// Silently fail
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Generate a version hash for cache invalidation
|
|
162
|
+
* Based on file size only
|
|
163
|
+
*/
|
|
164
|
+
static generateVersion(fileSize) {
|
|
165
|
+
return `${fileSize}`;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
AssetMemoryCache.db = null;
|
|
169
|
+
AssetMemoryCache.initPromise = null;
|
|
170
|
+
AssetMemoryCache.DB_NAME = 'StowKitAssetCache';
|
|
171
|
+
AssetMemoryCache.DB_VERSION = 1;
|
|
172
|
+
AssetMemoryCache.GEOMETRY_STORE = 'geometries';
|
|
173
|
+
AssetMemoryCache.TEXTURE_STORE = 'textures';
|
|
174
|
+
|
|
27
175
|
class MeshParser {
|
|
28
176
|
/**
|
|
29
177
|
* Parse mesh metadata from binary data
|
|
@@ -211,49 +359,75 @@ class MeshParser {
|
|
|
211
359
|
/**
|
|
212
360
|
* Create Three.js BufferGeometry from Draco compressed mesh data
|
|
213
361
|
*/
|
|
214
|
-
static async createGeometry(geoInfo, dataBlob, dracoLoader) {
|
|
215
|
-
|
|
362
|
+
static async createGeometry(geoInfo, dataBlob, dracoLoader, cacheKey, version) {
|
|
363
|
+
// Try loading from IndexedDB cache first
|
|
364
|
+
if (cacheKey && version) {
|
|
365
|
+
const cached = await AssetMemoryCache.getGeometry(cacheKey, version);
|
|
366
|
+
if (cached) {
|
|
367
|
+
reader.PerfLogger.log(`[Perf] Geometry cache hit: ${cacheKey}`);
|
|
368
|
+
const geometry = new THREE__namespace.BufferGeometry();
|
|
369
|
+
geometry.setAttribute('position', new THREE__namespace.BufferAttribute(cached.positions, 3));
|
|
370
|
+
if (cached.normals) {
|
|
371
|
+
geometry.setAttribute('normal', new THREE__namespace.BufferAttribute(cached.normals, 3));
|
|
372
|
+
}
|
|
373
|
+
if (cached.uvs) {
|
|
374
|
+
geometry.setAttribute('uv', new THREE__namespace.BufferAttribute(cached.uvs, 2));
|
|
375
|
+
}
|
|
376
|
+
geometry.setIndex(new THREE__namespace.BufferAttribute(cached.indices, 1));
|
|
377
|
+
return geometry;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
216
380
|
// Extract the Draco compressed buffer
|
|
217
381
|
if (geoInfo.compressedBufferOffset + geoInfo.compressedBufferSize > dataBlob.length) {
|
|
218
382
|
throw new Error(`Compressed buffer out of bounds: offset=${geoInfo.compressedBufferOffset}, size=${geoInfo.compressedBufferSize}, dataLength=${dataBlob.length}`);
|
|
219
383
|
}
|
|
220
384
|
const compressedData = dataBlob.slice(geoInfo.compressedBufferOffset, geoInfo.compressedBufferOffset + geoInfo.compressedBufferSize);
|
|
221
|
-
const extractTime = performance.now();
|
|
222
|
-
reader.PerfLogger.log(`[Perf] Draco extract: ${(extractTime - startTime).toFixed(2)}ms (${geoInfo.compressedBufferSize} bytes)`);
|
|
223
385
|
// Decode using Draco
|
|
224
|
-
// Create a blob URL for the Draco data (DRACOLoader expects a URL)
|
|
225
386
|
const arrayBuffer = compressedData.buffer.slice(compressedData.byteOffset, compressedData.byteOffset + compressedData.byteLength);
|
|
226
387
|
const blob = new Blob([arrayBuffer]);
|
|
227
388
|
const url = URL.createObjectURL(blob);
|
|
228
|
-
const decodeStart = performance.now();
|
|
229
389
|
const geometry = await new Promise((resolve, reject) => {
|
|
230
390
|
dracoLoader.load(url, (decoded) => {
|
|
231
391
|
URL.revokeObjectURL(url);
|
|
232
|
-
const decodeEnd = performance.now();
|
|
233
|
-
reader.PerfLogger.log(`[Perf] Draco decode: ${(decodeEnd - decodeStart).toFixed(2)}ms (vertices: ${geoInfo.vertexCount}, indices: ${geoInfo.indexCount})`);
|
|
234
392
|
resolve(decoded);
|
|
235
393
|
}, undefined, (error) => {
|
|
236
394
|
URL.revokeObjectURL(url);
|
|
237
395
|
reject(new Error(`Failed to decode Draco geometry: ${error}`));
|
|
238
396
|
});
|
|
239
397
|
});
|
|
240
|
-
//
|
|
241
|
-
|
|
242
|
-
|
|
398
|
+
// Cache the decoded geometry
|
|
399
|
+
if (cacheKey && version) {
|
|
400
|
+
const posAttr = geometry.getAttribute('position');
|
|
401
|
+
const normAttr = geometry.getAttribute('normal');
|
|
402
|
+
const uvAttr = geometry.getAttribute('uv');
|
|
403
|
+
const indexAttr = geometry.index;
|
|
404
|
+
if (posAttr && 'array' in posAttr && indexAttr && 'array' in indexAttr) {
|
|
405
|
+
AssetMemoryCache.setGeometry(cacheKey, version, {
|
|
406
|
+
positions: posAttr.array,
|
|
407
|
+
normals: (normAttr && 'array' in normAttr) ? normAttr.array : undefined,
|
|
408
|
+
uvs: (uvAttr && 'array' in uvAttr) ? uvAttr.array : undefined,
|
|
409
|
+
indices: indexAttr.array
|
|
410
|
+
}).catch(() => {
|
|
411
|
+
// Silent fail - caching is optional
|
|
412
|
+
});
|
|
413
|
+
}
|
|
414
|
+
}
|
|
243
415
|
return geometry;
|
|
244
416
|
}
|
|
245
417
|
/**
|
|
246
418
|
* Build Three.js scene from parsed mesh data
|
|
247
419
|
*/
|
|
248
|
-
static async buildScene(parsedData, dataBlob, dracoLoader) {
|
|
420
|
+
static async buildScene(parsedData, dataBlob, dracoLoader, cacheKeyPrefix, version) {
|
|
249
421
|
const root = new THREE__namespace.Group();
|
|
250
422
|
root.name = 'StowKitMesh';
|
|
251
423
|
const { geometries, materials, nodes, meshIndices } = parsedData;
|
|
252
424
|
// Pre-load ALL geometries in parallel for maximum speed
|
|
253
|
-
|
|
254
|
-
const geometryPromises = geometries.map(geoInfo => this.createGeometry(geoInfo, dataBlob, dracoLoader));
|
|
425
|
+
const dracoStart = performance.now();
|
|
426
|
+
const geometryPromises = geometries.map((geoInfo, index) => this.createGeometry(geoInfo, dataBlob, dracoLoader, cacheKeyPrefix ? `${cacheKeyPrefix}_geo${index}` : undefined, version));
|
|
255
427
|
const loadedGeometries = await Promise.all(geometryPromises);
|
|
256
|
-
|
|
428
|
+
const dracoTime = performance.now() - dracoStart;
|
|
429
|
+
const totalVerts = geometries.reduce((sum, g) => sum + g.vertexCount, 0);
|
|
430
|
+
reader.PerfLogger.log(`[Perf] Draco: ${dracoTime.toFixed(2)}ms (${geometries.length} meshes, ${totalVerts} verts)`);
|
|
257
431
|
// Create all Three.js objects for nodes
|
|
258
432
|
const nodeObjects = [];
|
|
259
433
|
for (const node of nodes) {
|
|
@@ -340,11 +514,13 @@ class MeshParser {
|
|
|
340
514
|
* Represents an opened StowKit pack with methods to load assets by name
|
|
341
515
|
*/
|
|
342
516
|
class StowKitPack {
|
|
343
|
-
constructor(reader, ktx2Loader, dracoLoader) {
|
|
517
|
+
constructor(reader, ktx2Loader, dracoLoader, packUrl = '', packVersion = '') {
|
|
344
518
|
this.textureCache = new Map();
|
|
345
519
|
this.reader = reader;
|
|
346
520
|
this.ktx2Loader = ktx2Loader;
|
|
347
521
|
this.dracoLoader = dracoLoader;
|
|
522
|
+
this.packUrl = packUrl;
|
|
523
|
+
this.packVersion = packVersion || Date.now().toString();
|
|
348
524
|
}
|
|
349
525
|
/**
|
|
350
526
|
* Load a skinned mesh by its string ID
|
|
@@ -673,17 +849,13 @@ class StowKitPack {
|
|
|
673
849
|
async loadMesh(assetPath) {
|
|
674
850
|
const totalStart = performance.now();
|
|
675
851
|
// Find asset by path
|
|
676
|
-
const findStart = performance.now();
|
|
677
852
|
const assetIndex = this.reader.findAssetByPath(assetPath);
|
|
678
853
|
if (assetIndex < 0) {
|
|
679
854
|
throw new Error(`Mesh not found: ${assetPath}`);
|
|
680
855
|
}
|
|
681
|
-
reader.PerfLogger.log(`[Perf] Find asset: ${(performance.now() - findStart).toFixed(2)}ms`);
|
|
682
856
|
// Read mesh data and metadata
|
|
683
|
-
const readStart = performance.now();
|
|
684
857
|
const data = this.reader.readAssetData(assetIndex);
|
|
685
858
|
const metadata = this.reader.readAssetMetadata(assetIndex);
|
|
686
|
-
reader.PerfLogger.log(`[Perf] Read mesh data: ${(performance.now() - readStart).toFixed(2)}ms (data: ${data?.byteLength || 0} bytes, metadata: ${metadata?.byteLength || 0} bytes)`);
|
|
687
859
|
if (!data) {
|
|
688
860
|
throw new Error(`Failed to read mesh data: ${assetPath}`);
|
|
689
861
|
}
|
|
@@ -691,18 +863,19 @@ class StowKitPack {
|
|
|
691
863
|
throw new Error(`No metadata available: ${assetPath}`);
|
|
692
864
|
}
|
|
693
865
|
// Parse mesh data
|
|
694
|
-
const parseStart = performance.now();
|
|
695
866
|
const parsedData = MeshParser.parseMeshData(metadata, data);
|
|
696
|
-
reader.PerfLogger.log(`[Perf] Parse mesh metadata: ${(performance.now() - parseStart).toFixed(2)}ms`);
|
|
697
867
|
// Load textures for materials
|
|
698
868
|
const textureStart = performance.now();
|
|
699
869
|
const textureNames = await this.loadMaterialTextures(parsedData.materialData, parsedData.materials);
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
870
|
+
const textureTime = performance.now() - textureStart;
|
|
871
|
+
if (textureNames.length > 0) {
|
|
872
|
+
reader.PerfLogger.log(`[Perf] Textures: ${textureTime.toFixed(2)}ms (${textureNames.join(', ')})`);
|
|
873
|
+
}
|
|
874
|
+
// Build Three.js scene with Draco decoder (with caching)
|
|
875
|
+
const cacheKey = `${this.packUrl}::${assetPath}`;
|
|
876
|
+
const scene = await MeshParser.buildScene(parsedData, data, this.dracoLoader, cacheKey, this.packVersion);
|
|
877
|
+
const totalTime = performance.now() - totalStart;
|
|
878
|
+
reader.PerfLogger.log(`[Perf] === Mesh "${assetPath}": ${totalTime.toFixed(2)}ms total ===`);
|
|
706
879
|
return scene;
|
|
707
880
|
}
|
|
708
881
|
/**
|
|
@@ -711,7 +884,6 @@ class StowKitPack {
|
|
|
711
884
|
async loadTexture(assetPath) {
|
|
712
885
|
// Check cache first
|
|
713
886
|
if (this.textureCache.has(assetPath)) {
|
|
714
|
-
reader.PerfLogger.log(`[Perf] Texture cache hit: ${assetPath}`);
|
|
715
887
|
return this.textureCache.get(assetPath);
|
|
716
888
|
}
|
|
717
889
|
// Cache the promise to avoid duplicate loads
|
|
@@ -1049,18 +1221,29 @@ class StowKitPack {
|
|
|
1049
1221
|
return Array.from(uniqueTextures);
|
|
1050
1222
|
}
|
|
1051
1223
|
async loadKTX2Texture(data, textureName) {
|
|
1052
|
-
|
|
1224
|
+
// Try IndexedDB cache first
|
|
1225
|
+
const cacheKey = textureName ? `${this.packUrl}::${textureName}` : undefined;
|
|
1226
|
+
if (cacheKey) {
|
|
1227
|
+
const startCache = performance.now();
|
|
1228
|
+
const cached = await AssetMemoryCache.getTexture(cacheKey, this.packVersion);
|
|
1229
|
+
if (cached) {
|
|
1230
|
+
reader.PerfLogger.log(`[Perf] Texture cache hit (IndexedDB): ${textureName} (${(performance.now() - startCache).toFixed(2)}ms)`);
|
|
1231
|
+
const texture = new THREE__namespace.CompressedTexture([{ data: cached.data, width: cached.width, height: cached.height }], cached.width, cached.height, cached.format, THREE__namespace.UnsignedByteType);
|
|
1232
|
+
texture.needsUpdate = true;
|
|
1233
|
+
return texture;
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1053
1236
|
// Create blob URL - KTX2Loader requires a URL, can't use ArrayBuffer directly
|
|
1054
1237
|
const arrayBuffer = data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength);
|
|
1055
1238
|
const blob = new Blob([arrayBuffer]);
|
|
1056
1239
|
const url = URL.createObjectURL(blob);
|
|
1057
1240
|
try {
|
|
1058
|
-
|
|
1059
|
-
|
|
1241
|
+
const decodeStart = performance.now();
|
|
1242
|
+
const texture = await new Promise((resolve, reject) => {
|
|
1060
1243
|
this.ktx2Loader.load(url, (texture) => {
|
|
1061
1244
|
URL.revokeObjectURL(url);
|
|
1062
|
-
const
|
|
1063
|
-
reader.PerfLogger.log(`[Perf] Basis
|
|
1245
|
+
const decodeTime = performance.now() - decodeStart;
|
|
1246
|
+
reader.PerfLogger.log(`[Perf] Basis decode: ${decodeTime.toFixed(2)}ms (${textureName || 'unknown'}, ${texture.image?.width}x${texture.image?.height})`);
|
|
1064
1247
|
texture.needsUpdate = true;
|
|
1065
1248
|
resolve(texture);
|
|
1066
1249
|
}, undefined, (error) => {
|
|
@@ -1068,6 +1251,22 @@ class StowKitPack {
|
|
|
1068
1251
|
reject(error);
|
|
1069
1252
|
});
|
|
1070
1253
|
});
|
|
1254
|
+
// Cache the transcoded texture data to IndexedDB
|
|
1255
|
+
if (cacheKey && texture.mipmaps && texture.mipmaps.length > 0) {
|
|
1256
|
+
const mipmap = texture.mipmaps[0];
|
|
1257
|
+
const dataArray = new Uint8Array(mipmap.data.buffer, mipmap.data.byteOffset, mipmap.data.byteLength);
|
|
1258
|
+
AssetMemoryCache.setTexture(cacheKey, this.packVersion, {
|
|
1259
|
+
data: dataArray,
|
|
1260
|
+
width: mipmap.width,
|
|
1261
|
+
height: mipmap.height,
|
|
1262
|
+
format: texture.format,
|
|
1263
|
+
internalFormat: texture.format,
|
|
1264
|
+
compressed: true
|
|
1265
|
+
}).catch(() => {
|
|
1266
|
+
// Silent fail - caching is optional
|
|
1267
|
+
});
|
|
1268
|
+
}
|
|
1269
|
+
return texture;
|
|
1071
1270
|
}
|
|
1072
1271
|
catch (error) {
|
|
1073
1272
|
URL.revokeObjectURL(url);
|
|
@@ -1128,17 +1327,22 @@ class StowKitLoader {
|
|
|
1128
1327
|
throw new Error(`Failed to fetch ${url}: ${response.statusText}`);
|
|
1129
1328
|
}
|
|
1130
1329
|
const arrayBuffer = await response.arrayBuffer();
|
|
1330
|
+
// Generate version for cache invalidation (based on file size)
|
|
1331
|
+
const version = `${arrayBuffer.byteLength}`;
|
|
1131
1332
|
// Create a new reader instance for this pack
|
|
1132
1333
|
const reader$1 = new reader.StowKitReader(this.wasmPath);
|
|
1133
1334
|
await reader$1.init();
|
|
1134
1335
|
await reader$1.open(arrayBuffer);
|
|
1135
1336
|
// Return pack wrapper with its own dedicated reader
|
|
1136
|
-
return new StowKitPack(reader$1, this.ktx2Loader, this.dracoLoader);
|
|
1337
|
+
return new StowKitPack(reader$1, this.ktx2Loader, this.dracoLoader, url, version);
|
|
1137
1338
|
}
|
|
1138
1339
|
/**
|
|
1139
1340
|
* Load a .stow pack from memory (ArrayBuffer, Blob, or File)
|
|
1341
|
+
* @param data - The .stow file data
|
|
1342
|
+
* @param options - Loader options
|
|
1343
|
+
* @param cacheKey - Optional unique identifier for caching (e.g., CDN URL). Recommended for files loaded from CDN.
|
|
1140
1344
|
*/
|
|
1141
|
-
static async loadFromMemory(data, options) {
|
|
1345
|
+
static async loadFromMemory(data, options, cacheKey) {
|
|
1142
1346
|
// Initialize loaders if needed
|
|
1143
1347
|
if (!this.initialized) {
|
|
1144
1348
|
await this.initialize(options);
|
|
@@ -1157,12 +1361,15 @@ class StowKitLoader {
|
|
|
1157
1361
|
else {
|
|
1158
1362
|
throw new Error('Data must be ArrayBuffer, Blob, or File');
|
|
1159
1363
|
}
|
|
1364
|
+
// Generate version for cache invalidation (based on file size)
|
|
1365
|
+
const version = `${arrayBuffer.byteLength}`;
|
|
1366
|
+
const packUrl = cacheKey || 'memory';
|
|
1160
1367
|
// Create a new reader instance for this pack
|
|
1161
1368
|
const reader$1 = new reader.StowKitReader(this.wasmPath);
|
|
1162
1369
|
await reader$1.init();
|
|
1163
1370
|
await reader$1.open(arrayBuffer);
|
|
1164
1371
|
// Return pack wrapper with its own dedicated reader
|
|
1165
|
-
return new StowKitPack(reader$1, this.ktx2Loader, this.dracoLoader);
|
|
1372
|
+
return new StowKitPack(reader$1, this.ktx2Loader, this.dracoLoader, packUrl, version);
|
|
1166
1373
|
}
|
|
1167
1374
|
/**
|
|
1168
1375
|
* Initialize the loader (called automatically on first load)
|
|
@@ -1197,6 +1404,7 @@ Object.defineProperty(exports, 'PerfLogger', {
|
|
|
1197
1404
|
enumerable: true,
|
|
1198
1405
|
get: function () { return reader.PerfLogger; }
|
|
1199
1406
|
});
|
|
1407
|
+
exports.AssetMemoryCache = AssetMemoryCache;
|
|
1200
1408
|
exports.StowKitLoader = StowKitLoader;
|
|
1201
1409
|
exports.StowKitPack = StowKitPack;
|
|
1202
1410
|
//# sourceMappingURL=stowkit-three-loader.js.map
|