@stowkit/three-loader 0.1.10 → 0.1.12
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 +47 -91
- package/dist/StowKitLoader.d.ts +23 -198
- package/dist/StowKitLoader.d.ts.map +1 -1
- package/dist/StowKitPack.d.ts +132 -0
- package/dist/StowKitPack.d.ts.map +1 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/stowkit-three-loader.esm.js +308 -550
- package/dist/stowkit-three-loader.esm.js.map +1 -1
- package/dist/stowkit-three-loader.js +308 -550
- package/dist/stowkit-three-loader.js.map +1 -1
- package/package.json +1 -1
|
@@ -366,167 +366,214 @@ class MeshParser {
|
|
|
366
366
|
}
|
|
367
367
|
|
|
368
368
|
/**
|
|
369
|
-
*
|
|
370
|
-
*
|
|
371
|
-
* Usage:
|
|
372
|
-
* ```typescript
|
|
373
|
-
* const loader = new StowKitLoader();
|
|
374
|
-
* loader.setTranscoderPath('/basis/');
|
|
375
|
-
*
|
|
376
|
-
* // Load a mesh asset
|
|
377
|
-
* loader.loadMesh('assets.stow', 'path/to/mesh', (scene) => {
|
|
378
|
-
* threeScene.add(scene);
|
|
379
|
-
* });
|
|
380
|
-
* ```
|
|
369
|
+
* Represents an opened StowKit pack with methods to load assets by name
|
|
381
370
|
*/
|
|
382
|
-
class
|
|
383
|
-
constructor(
|
|
384
|
-
|
|
385
|
-
this.
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
371
|
+
class StowKitPack {
|
|
372
|
+
constructor(reader, ktx2Loader, dracoLoader) {
|
|
373
|
+
this.reader = reader;
|
|
374
|
+
this.ktx2Loader = ktx2Loader;
|
|
375
|
+
this.dracoLoader = dracoLoader;
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* Load a mesh by its canonical path/name
|
|
379
|
+
*/
|
|
380
|
+
async loadMesh(assetPath) {
|
|
381
|
+
// Find asset by path
|
|
382
|
+
const assetIndex = this.reader.findAssetByPath(assetPath);
|
|
383
|
+
if (assetIndex < 0) {
|
|
384
|
+
throw new Error(`Mesh not found: ${assetPath}`);
|
|
385
|
+
}
|
|
386
|
+
// Read mesh data and metadata
|
|
387
|
+
const data = this.reader.readAssetData(assetIndex);
|
|
388
|
+
const metadata = this.reader.readAssetMetadata(assetIndex);
|
|
389
|
+
if (!data) {
|
|
390
|
+
throw new Error(`Failed to read mesh data: ${assetPath}`);
|
|
390
391
|
}
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
this.reader = new StowKitReader(wasmPath);
|
|
394
|
-
this.ownedReader = true;
|
|
392
|
+
if (!metadata) {
|
|
393
|
+
throw new Error(`No metadata available: ${assetPath}`);
|
|
395
394
|
}
|
|
396
|
-
//
|
|
397
|
-
const
|
|
398
|
-
|
|
399
|
-
this.
|
|
400
|
-
//
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
// Detect support with a temporary renderer
|
|
404
|
-
// This is required for KTX2Loader to work
|
|
405
|
-
const tempRenderer = new THREE.WebGLRenderer();
|
|
406
|
-
this.ktx2Loader.detectSupport(tempRenderer);
|
|
407
|
-
tempRenderer.dispose();
|
|
395
|
+
// Parse mesh data
|
|
396
|
+
const parsedData = MeshParser.parseMeshData(metadata, data);
|
|
397
|
+
// Load textures for materials
|
|
398
|
+
await this.loadMaterialTextures(parsedData.materialData, parsedData.materials);
|
|
399
|
+
// Build Three.js scene with Draco decoder
|
|
400
|
+
const scene = await MeshParser.buildScene(parsedData, data, this.dracoLoader);
|
|
401
|
+
return scene;
|
|
408
402
|
}
|
|
409
403
|
/**
|
|
410
|
-
*
|
|
404
|
+
* Load a texture by its canonical path/name
|
|
411
405
|
*/
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
406
|
+
async loadTexture(assetPath) {
|
|
407
|
+
// Find asset by path
|
|
408
|
+
const assetIndex = this.reader.findAssetByPath(assetPath);
|
|
409
|
+
if (assetIndex < 0) {
|
|
410
|
+
throw new Error(`Texture not found: ${assetPath}`);
|
|
411
|
+
}
|
|
412
|
+
// Read texture data
|
|
413
|
+
const data = this.reader.readAssetData(assetIndex);
|
|
414
|
+
if (!data) {
|
|
415
|
+
throw new Error(`Failed to read texture data: ${assetPath}`);
|
|
416
|
+
}
|
|
417
|
+
// Load as KTX2
|
|
418
|
+
return await this.loadKTX2Texture(data);
|
|
415
419
|
}
|
|
416
420
|
/**
|
|
417
|
-
*
|
|
421
|
+
* Load audio by its canonical path/name
|
|
418
422
|
*/
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
423
|
+
async loadAudio(assetPath, listener) {
|
|
424
|
+
// Find asset by path
|
|
425
|
+
const assetIndex = this.reader.findAssetByPath(assetPath);
|
|
426
|
+
if (assetIndex < 0) {
|
|
427
|
+
throw new Error(`Audio not found: ${assetPath}`);
|
|
428
|
+
}
|
|
429
|
+
// Read audio data
|
|
430
|
+
const data = this.reader.readAssetData(assetIndex);
|
|
431
|
+
if (!data) {
|
|
432
|
+
throw new Error(`Failed to read audio data: ${assetPath}`);
|
|
433
|
+
}
|
|
434
|
+
// Create audio object
|
|
435
|
+
const audio = new THREE.Audio(listener);
|
|
436
|
+
// Decode audio data
|
|
437
|
+
const arrayBuffer = data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength);
|
|
438
|
+
const audioContext = listener.context;
|
|
439
|
+
const audioBuffer = await audioContext.decodeAudioData(arrayBuffer);
|
|
440
|
+
audio.setBuffer(audioBuffer);
|
|
441
|
+
return audio;
|
|
422
442
|
}
|
|
423
443
|
/**
|
|
424
|
-
*
|
|
444
|
+
* Get list of all assets in pack
|
|
425
445
|
*/
|
|
426
|
-
|
|
427
|
-
this.
|
|
428
|
-
return this;
|
|
446
|
+
listAssets() {
|
|
447
|
+
return this.reader.listAssets();
|
|
429
448
|
}
|
|
430
449
|
/**
|
|
431
|
-
*
|
|
432
|
-
* @param url - URL to the .stow file
|
|
450
|
+
* Get asset count
|
|
433
451
|
*/
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
await this.reader.init();
|
|
437
|
-
}
|
|
438
|
-
const response = await fetch(url);
|
|
439
|
-
if (!response.ok) {
|
|
440
|
-
throw new Error(`Failed to fetch ${url}: ${response.statusText}`);
|
|
441
|
-
}
|
|
442
|
-
const arrayBuffer = await response.arrayBuffer();
|
|
443
|
-
await this.reader.open(arrayBuffer);
|
|
452
|
+
getAssetCount() {
|
|
453
|
+
return this.reader.getAssetCount();
|
|
444
454
|
}
|
|
445
455
|
/**
|
|
446
|
-
*
|
|
447
|
-
* @param index - Asset index
|
|
448
|
-
* @param onLoad - Callback when loading completes
|
|
449
|
-
* @param onProgress - Progress callback
|
|
450
|
-
* @param onError - Error callback
|
|
456
|
+
* Get asset info by index
|
|
451
457
|
*/
|
|
452
|
-
|
|
453
|
-
|
|
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
|
-
}
|
|
458
|
+
getAssetInfo(index) {
|
|
459
|
+
return this.reader.getAssetInfo(index);
|
|
480
460
|
}
|
|
481
461
|
/**
|
|
482
|
-
*
|
|
483
|
-
* @param url - URL to the .stow file (or omit if already opened with openPack)
|
|
484
|
-
* @param assetPath - Path to the mesh asset within the pack
|
|
485
|
-
* @param onLoad - Callback when loading completes
|
|
486
|
-
* @param onProgress - Progress callback
|
|
487
|
-
* @param onError - Error callback
|
|
462
|
+
* Read asset data by index
|
|
488
463
|
*/
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
// Open pack if URL provided
|
|
492
|
-
if (url) {
|
|
493
|
-
await this.openPack(url);
|
|
494
|
-
}
|
|
495
|
-
// Find the mesh asset
|
|
496
|
-
const assetIndex = this.reader.findAssetByPath(assetPath);
|
|
497
|
-
if (assetIndex < 0) {
|
|
498
|
-
throw new Error(`Asset not found: ${assetPath}`);
|
|
499
|
-
}
|
|
500
|
-
// Read mesh data and metadata
|
|
501
|
-
const data = this.reader.readAssetData(assetIndex);
|
|
502
|
-
const metadata = this.reader.readAssetMetadata(assetIndex);
|
|
503
|
-
if (!data) {
|
|
504
|
-
throw new Error(`Failed to read mesh data for ${assetPath}`);
|
|
505
|
-
}
|
|
506
|
-
if (!metadata) {
|
|
507
|
-
throw new Error(`No metadata available for ${assetPath}`);
|
|
508
|
-
}
|
|
509
|
-
// Parse mesh data
|
|
510
|
-
const parsedData = MeshParser.parseMeshData(metadata, data);
|
|
511
|
-
// Load textures for materials
|
|
512
|
-
await this.loadMaterialTextures(parsedData.materialData, parsedData.materials);
|
|
513
|
-
// Build Three.js scene with Draco decoder
|
|
514
|
-
const scene = await MeshParser.buildScene(parsedData, data, this.dracoLoader);
|
|
515
|
-
if (onLoad) {
|
|
516
|
-
onLoad(scene);
|
|
517
|
-
}
|
|
518
|
-
return scene;
|
|
519
|
-
}
|
|
520
|
-
catch (error) {
|
|
521
|
-
if (onError) {
|
|
522
|
-
onError(error);
|
|
523
|
-
}
|
|
524
|
-
throw error;
|
|
525
|
-
}
|
|
464
|
+
readAssetData(index) {
|
|
465
|
+
return this.reader.readAssetData(index);
|
|
526
466
|
}
|
|
527
467
|
/**
|
|
528
|
-
*
|
|
468
|
+
* Read asset metadata by index
|
|
529
469
|
*/
|
|
470
|
+
readAssetMetadata(index) {
|
|
471
|
+
return this.reader.readAssetMetadata(index);
|
|
472
|
+
}
|
|
473
|
+
/**
|
|
474
|
+
* Parse texture metadata
|
|
475
|
+
*/
|
|
476
|
+
parseTextureMetadata(metadataBytes) {
|
|
477
|
+
return this.reader.parseTextureMetadata(metadataBytes);
|
|
478
|
+
}
|
|
479
|
+
/**
|
|
480
|
+
* Load mesh by index
|
|
481
|
+
*/
|
|
482
|
+
async loadMeshByIndex(index) {
|
|
483
|
+
const data = this.reader.readAssetData(index);
|
|
484
|
+
const metadata = this.reader.readAssetMetadata(index);
|
|
485
|
+
if (!data)
|
|
486
|
+
throw new Error(`Failed to read mesh data for index ${index}`);
|
|
487
|
+
if (!metadata)
|
|
488
|
+
throw new Error(`No metadata for index ${index}`);
|
|
489
|
+
const parsedData = MeshParser.parseMeshData(metadata, data);
|
|
490
|
+
await this.loadMaterialTextures(parsedData.materialData, parsedData.materials);
|
|
491
|
+
return await MeshParser.buildScene(parsedData, data, this.dracoLoader);
|
|
492
|
+
}
|
|
493
|
+
/**
|
|
494
|
+
* Load texture by index
|
|
495
|
+
*/
|
|
496
|
+
async loadTextureByIndex(index) {
|
|
497
|
+
const data = this.reader.readAssetData(index);
|
|
498
|
+
if (!data)
|
|
499
|
+
throw new Error(`Failed to read texture for index ${index}`);
|
|
500
|
+
return await this.loadKTX2Texture(data);
|
|
501
|
+
}
|
|
502
|
+
/**
|
|
503
|
+
* Create HTML audio preview element
|
|
504
|
+
*/
|
|
505
|
+
async createAudioPreview(index) {
|
|
506
|
+
const data = this.reader.readAssetData(index);
|
|
507
|
+
if (!data)
|
|
508
|
+
throw new Error(`Failed to read audio for index ${index}`);
|
|
509
|
+
const blob = new Blob([data], { type: 'audio/ogg' });
|
|
510
|
+
const url = URL.createObjectURL(blob);
|
|
511
|
+
const audio = document.createElement('audio');
|
|
512
|
+
audio.controls = true;
|
|
513
|
+
audio.src = url;
|
|
514
|
+
audio.addEventListener('ended', () => URL.revokeObjectURL(url));
|
|
515
|
+
audio.addEventListener('error', () => URL.revokeObjectURL(url));
|
|
516
|
+
return audio;
|
|
517
|
+
}
|
|
518
|
+
/**
|
|
519
|
+
* Get audio metadata by path
|
|
520
|
+
*/
|
|
521
|
+
getAudioMetadata(assetPath) {
|
|
522
|
+
const assetIndex = this.reader.findAssetByPath(assetPath);
|
|
523
|
+
if (assetIndex < 0)
|
|
524
|
+
return null;
|
|
525
|
+
const metadata = this.reader.readAssetMetadata(assetIndex);
|
|
526
|
+
if (!metadata || metadata.length < 140)
|
|
527
|
+
return null;
|
|
528
|
+
const view = new DataView(metadata.buffer, metadata.byteOffset, metadata.byteLength);
|
|
529
|
+
const decoder = new TextDecoder();
|
|
530
|
+
const stringIdBytes = metadata.slice(0, 128);
|
|
531
|
+
const nullIdx = stringIdBytes.indexOf(0);
|
|
532
|
+
const stringId = decoder.decode(stringIdBytes.slice(0, nullIdx >= 0 ? nullIdx : 128));
|
|
533
|
+
return {
|
|
534
|
+
stringId,
|
|
535
|
+
sampleRate: view.getUint32(128, true),
|
|
536
|
+
channels: view.getUint32(132, true),
|
|
537
|
+
durationMs: view.getUint32(136, true)
|
|
538
|
+
};
|
|
539
|
+
}
|
|
540
|
+
/**
|
|
541
|
+
* Load material schema by index
|
|
542
|
+
*/
|
|
543
|
+
loadMaterialSchemaByIndex(index) {
|
|
544
|
+
return this.loadMaterialSchemaByIndex_internal(index);
|
|
545
|
+
}
|
|
546
|
+
/**
|
|
547
|
+
* Get mesh materials by index
|
|
548
|
+
*/
|
|
549
|
+
getMeshMaterialsByIndex(index) {
|
|
550
|
+
return this.getMeshMaterialsByIndex_internal(index);
|
|
551
|
+
}
|
|
552
|
+
/**
|
|
553
|
+
* Get material schema information
|
|
554
|
+
*/
|
|
555
|
+
getMaterialSchema(assetPath) {
|
|
556
|
+
const assetIndex = this.reader.findAssetByPath(assetPath);
|
|
557
|
+
if (assetIndex < 0)
|
|
558
|
+
return null;
|
|
559
|
+
return this.loadMaterialSchemaByIndex_internal(assetIndex);
|
|
560
|
+
}
|
|
561
|
+
/**
|
|
562
|
+
* Get mesh materials information
|
|
563
|
+
*/
|
|
564
|
+
getMeshMaterials(assetPath) {
|
|
565
|
+
const assetIndex = this.reader.findAssetByPath(assetPath);
|
|
566
|
+
if (assetIndex < 0)
|
|
567
|
+
return null;
|
|
568
|
+
return this.getMeshMaterialsByIndex_internal(assetIndex);
|
|
569
|
+
}
|
|
570
|
+
/**
|
|
571
|
+
* Close the pack and free resources
|
|
572
|
+
*/
|
|
573
|
+
dispose() {
|
|
574
|
+
this.reader.close();
|
|
575
|
+
}
|
|
576
|
+
// Private methods moved from StowKitLoader
|
|
530
577
|
async loadMaterialTextures(materialData, materials) {
|
|
531
578
|
for (let i = 0; i < materialData.length; i++) {
|
|
532
579
|
const matData = materialData[i];
|
|
@@ -534,21 +581,7 @@ class StowKitLoader extends THREE.Loader {
|
|
|
534
581
|
for (const prop of matData.properties) {
|
|
535
582
|
if (prop.textureId && prop.textureId.length > 0) {
|
|
536
583
|
try {
|
|
537
|
-
|
|
538
|
-
const textureIndex = this.reader.findAssetByPath(prop.textureId);
|
|
539
|
-
if (textureIndex < 0) {
|
|
540
|
-
console.warn(`Texture not found: ${prop.textureId}`);
|
|
541
|
-
continue;
|
|
542
|
-
}
|
|
543
|
-
// Read texture data
|
|
544
|
-
const textureData = this.reader.readAssetData(textureIndex);
|
|
545
|
-
if (!textureData) {
|
|
546
|
-
console.warn(`Failed to read texture: ${prop.textureId}`);
|
|
547
|
-
continue;
|
|
548
|
-
}
|
|
549
|
-
// Load as KTX2
|
|
550
|
-
const texture = await this.loadKTX2Texture(textureData);
|
|
551
|
-
// Apply to material based on property name
|
|
584
|
+
const texture = await this.loadTexture(prop.textureId);
|
|
552
585
|
this.applyTextureToMaterial(material, prop.fieldName, texture);
|
|
553
586
|
}
|
|
554
587
|
catch (error) {
|
|
@@ -558,9 +591,6 @@ class StowKitLoader extends THREE.Loader {
|
|
|
558
591
|
}
|
|
559
592
|
}
|
|
560
593
|
}
|
|
561
|
-
/**
|
|
562
|
-
* Load a KTX2 texture from binary data
|
|
563
|
-
*/
|
|
564
594
|
async loadKTX2Texture(data) {
|
|
565
595
|
const arrayBuffer = data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength);
|
|
566
596
|
const blob = new Blob([arrayBuffer]);
|
|
@@ -569,7 +599,6 @@ class StowKitLoader extends THREE.Loader {
|
|
|
569
599
|
return await new Promise((resolve, reject) => {
|
|
570
600
|
this.ktx2Loader.load(url, (texture) => {
|
|
571
601
|
URL.revokeObjectURL(url);
|
|
572
|
-
// Compressed textures don't support flipY, we flip UVs in MeshParser instead
|
|
573
602
|
texture.needsUpdate = true;
|
|
574
603
|
resolve(texture);
|
|
575
604
|
}, undefined, (error) => {
|
|
@@ -583,9 +612,6 @@ class StowKitLoader extends THREE.Loader {
|
|
|
583
612
|
throw error;
|
|
584
613
|
}
|
|
585
614
|
}
|
|
586
|
-
/**
|
|
587
|
-
* Apply texture to appropriate material property
|
|
588
|
-
*/
|
|
589
615
|
applyTextureToMaterial(material, propertyName, texture) {
|
|
590
616
|
const propNameLower = propertyName.toLowerCase();
|
|
591
617
|
if (propNameLower === 'maintex' || propNameLower.includes('diffuse') ||
|
|
@@ -602,421 +628,153 @@ class StowKitLoader extends THREE.Loader {
|
|
|
602
628
|
material.roughnessMap = texture;
|
|
603
629
|
}
|
|
604
630
|
else {
|
|
605
|
-
// Default to main texture
|
|
606
631
|
material.map = texture;
|
|
607
632
|
}
|
|
608
633
|
material.needsUpdate = true;
|
|
609
634
|
}
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
* @param assetPath - Path to the texture asset within the pack
|
|
614
|
-
* @param onLoad - Callback when loading completes
|
|
615
|
-
* @param onProgress - Progress callback
|
|
616
|
-
* @param onError - Error callback
|
|
617
|
-
*/
|
|
618
|
-
async loadTexture(url, assetPath, onLoad, onProgress, onError) {
|
|
619
|
-
try {
|
|
620
|
-
// Open pack if URL provided
|
|
621
|
-
if (url) {
|
|
622
|
-
await this.openPack(url);
|
|
623
|
-
}
|
|
624
|
-
// Find the texture asset
|
|
625
|
-
const assetIndex = this.reader.findAssetByPath(assetPath);
|
|
626
|
-
if (assetIndex < 0) {
|
|
627
|
-
throw new Error(`Asset not found: ${assetPath}`);
|
|
628
|
-
}
|
|
629
|
-
return await this.loadTextureByIndex(assetIndex, onLoad, onProgress, onError);
|
|
630
|
-
}
|
|
631
|
-
catch (error) {
|
|
632
|
-
if (onError) {
|
|
633
|
-
onError(error);
|
|
634
|
-
}
|
|
635
|
-
throw error;
|
|
636
|
-
}
|
|
637
|
-
}
|
|
638
|
-
/**
|
|
639
|
-
* Load a texture asset by its index in the pack
|
|
640
|
-
* @param index - Asset index
|
|
641
|
-
* @param onLoad - Callback when loading completes
|
|
642
|
-
* @param onProgress - Progress callback
|
|
643
|
-
* @param onError - Error callback
|
|
644
|
-
*/
|
|
645
|
-
async loadTextureByIndex(index, onLoad, onProgress, onError) {
|
|
646
|
-
try {
|
|
647
|
-
// Read texture data
|
|
648
|
-
const data = this.reader.readAssetData(index);
|
|
649
|
-
if (!data) {
|
|
650
|
-
throw new Error(`Failed to read texture data for index ${index}`);
|
|
651
|
-
}
|
|
652
|
-
// Load as KTX2
|
|
653
|
-
const texture = await this.loadKTX2Texture(data);
|
|
654
|
-
if (onLoad) {
|
|
655
|
-
onLoad(texture);
|
|
656
|
-
}
|
|
657
|
-
return texture;
|
|
658
|
-
}
|
|
659
|
-
catch (error) {
|
|
660
|
-
if (onError) {
|
|
661
|
-
onError(error);
|
|
662
|
-
}
|
|
663
|
-
throw error;
|
|
664
|
-
}
|
|
665
|
-
}
|
|
666
|
-
/**
|
|
667
|
-
* Load an audio asset from a StowKit pack
|
|
668
|
-
* @param url - URL to the .stow file (or omit if already opened with openPack)
|
|
669
|
-
* @param assetPath - Path to the audio asset within the pack
|
|
670
|
-
* @param listener - THREE.AudioListener to attach to
|
|
671
|
-
* @param onLoad - Callback when loading completes
|
|
672
|
-
* @param onProgress - Progress callback
|
|
673
|
-
* @param onError - Error callback
|
|
674
|
-
*/
|
|
675
|
-
async loadAudio(url, assetPath, listener, onLoad, onProgress, onError) {
|
|
676
|
-
try {
|
|
677
|
-
// Open pack if URL provided
|
|
678
|
-
if (url) {
|
|
679
|
-
await this.openPack(url);
|
|
680
|
-
}
|
|
681
|
-
// Find the audio asset
|
|
682
|
-
const assetIndex = this.reader.findAssetByPath(assetPath);
|
|
683
|
-
if (assetIndex < 0) {
|
|
684
|
-
throw new Error(`Asset not found: ${assetPath}`);
|
|
685
|
-
}
|
|
686
|
-
// Read audio data
|
|
687
|
-
const data = this.reader.readAssetData(assetIndex);
|
|
688
|
-
if (!data) {
|
|
689
|
-
throw new Error(`Failed to read audio data for ${assetPath}`);
|
|
690
|
-
}
|
|
691
|
-
// Create audio object
|
|
692
|
-
const audio = new THREE.Audio(listener);
|
|
693
|
-
// Decode audio data
|
|
694
|
-
const arrayBuffer = data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength);
|
|
695
|
-
const audioContext = listener.context;
|
|
696
|
-
const audioBuffer = await audioContext.decodeAudioData(arrayBuffer);
|
|
697
|
-
audio.setBuffer(audioBuffer);
|
|
698
|
-
if (onLoad) {
|
|
699
|
-
onLoad(audio);
|
|
700
|
-
}
|
|
701
|
-
return audio;
|
|
702
|
-
}
|
|
703
|
-
catch (error) {
|
|
704
|
-
if (onError) {
|
|
705
|
-
onError(error);
|
|
706
|
-
}
|
|
707
|
-
throw error;
|
|
708
|
-
}
|
|
709
|
-
}
|
|
710
|
-
/**
|
|
711
|
-
* Get metadata for an audio asset
|
|
712
|
-
* @param assetPath - Path to the audio asset within the pack
|
|
713
|
-
*/
|
|
714
|
-
getAudioMetadata(assetPath) {
|
|
715
|
-
try {
|
|
716
|
-
const assetIndex = this.reader.findAssetByPath(assetPath);
|
|
717
|
-
if (assetIndex < 0)
|
|
718
|
-
return null;
|
|
719
|
-
const metadata = this.reader.readAssetMetadata(assetIndex);
|
|
720
|
-
if (!metadata || metadata.length < 140)
|
|
721
|
-
return null;
|
|
722
|
-
const view = new DataView(metadata.buffer, metadata.byteOffset, metadata.byteLength);
|
|
723
|
-
const decoder = new TextDecoder();
|
|
724
|
-
// Parse AudioMetadata structure
|
|
725
|
-
const stringIdBytes = metadata.slice(0, 128);
|
|
726
|
-
const nullIdx = stringIdBytes.indexOf(0);
|
|
727
|
-
const stringId = decoder.decode(stringIdBytes.slice(0, nullIdx >= 0 ? nullIdx : 128));
|
|
728
|
-
return {
|
|
729
|
-
stringId,
|
|
730
|
-
sampleRate: view.getUint32(128, true),
|
|
731
|
-
channels: view.getUint32(132, true),
|
|
732
|
-
durationMs: view.getUint32(136, true)
|
|
733
|
-
};
|
|
734
|
-
}
|
|
735
|
-
catch (error) {
|
|
736
|
-
console.error('Failed to read audio metadata:', error);
|
|
737
|
-
return null;
|
|
738
|
-
}
|
|
739
|
-
}
|
|
740
|
-
/**
|
|
741
|
-
* Get metadata for a texture asset
|
|
742
|
-
* @param assetPath - Path to the texture asset within the pack
|
|
743
|
-
*/
|
|
744
|
-
getTextureMetadata(assetPath) {
|
|
745
|
-
try {
|
|
746
|
-
const assetIndex = this.reader.findAssetByPath(assetPath);
|
|
747
|
-
if (assetIndex < 0)
|
|
748
|
-
return null;
|
|
749
|
-
const metadata = this.reader.readAssetMetadata(assetIndex);
|
|
750
|
-
if (!metadata || metadata.length < 144)
|
|
751
|
-
return null; // TextureMetadata is now 144 bytes
|
|
752
|
-
const view = new DataView(metadata.buffer, metadata.byteOffset, metadata.byteLength);
|
|
753
|
-
const decoder = new TextDecoder();
|
|
754
|
-
// Parse TextureMetadata structure (width, height, channels, channel_format, string_id[128])
|
|
755
|
-
const stringIdBytes = metadata.slice(16, 144); // After 4 uint32s (16 bytes)
|
|
756
|
-
const nullIdx = stringIdBytes.indexOf(0);
|
|
757
|
-
const stringId = decoder.decode(stringIdBytes.slice(0, nullIdx >= 0 ? nullIdx : 128));
|
|
758
|
-
return {
|
|
759
|
-
width: view.getUint32(0, true),
|
|
760
|
-
height: view.getUint32(4, true),
|
|
761
|
-
channels: view.getUint32(8, true),
|
|
762
|
-
isKtx2: true, // All textures are KTX2 now
|
|
763
|
-
channelFormat: view.getUint32(12, true),
|
|
764
|
-
stringId
|
|
765
|
-
};
|
|
766
|
-
}
|
|
767
|
-
catch (error) {
|
|
768
|
-
console.error('Failed to read texture metadata:', error);
|
|
635
|
+
loadMaterialSchemaByIndex_internal(index) {
|
|
636
|
+
const metadata = this.reader.readAssetMetadata(index);
|
|
637
|
+
if (!metadata || metadata.length < 196)
|
|
769
638
|
return null;
|
|
639
|
+
const view = new DataView(metadata.buffer, metadata.byteOffset, metadata.byteLength);
|
|
640
|
+
const decoder = new TextDecoder();
|
|
641
|
+
const stringIdBytes = metadata.slice(0, 128);
|
|
642
|
+
const schemaNameBytes = metadata.slice(128, 192);
|
|
643
|
+
const stringId = decoder.decode(stringIdBytes.slice(0, stringIdBytes.indexOf(0) || 128));
|
|
644
|
+
const schemaName = decoder.decode(schemaNameBytes.slice(0, schemaNameBytes.indexOf(0) || 64));
|
|
645
|
+
const fieldCount = view.getUint32(192, true);
|
|
646
|
+
const fields = [];
|
|
647
|
+
let offset = 196;
|
|
648
|
+
for (let i = 0; i < fieldCount; i++) {
|
|
649
|
+
if (offset + 88 > metadata.length)
|
|
650
|
+
break;
|
|
651
|
+
const fieldNameBytes = metadata.slice(offset, offset + 64);
|
|
652
|
+
const fieldName = decoder.decode(fieldNameBytes.slice(0, fieldNameBytes.indexOf(0) || 64));
|
|
653
|
+
const fieldType = view.getUint32(offset + 64, true);
|
|
654
|
+
const previewFlags = view.getUint32(offset + 68, true);
|
|
655
|
+
const defaultValue = [
|
|
656
|
+
view.getFloat32(offset + 72, true),
|
|
657
|
+
view.getFloat32(offset + 76, true),
|
|
658
|
+
view.getFloat32(offset + 80, true),
|
|
659
|
+
view.getFloat32(offset + 84, true)
|
|
660
|
+
];
|
|
661
|
+
const typeNames = ['Texture', 'Color', 'Float', 'Vec2', 'Vec3', 'Vec4', 'Int'];
|
|
662
|
+
const previewFlagNames = ['None', 'MainTex', 'Tint'];
|
|
663
|
+
fields.push({
|
|
664
|
+
name: fieldName,
|
|
665
|
+
type: typeNames[fieldType] || 'Float',
|
|
666
|
+
previewFlag: previewFlagNames[previewFlags] || 'None',
|
|
667
|
+
defaultValue
|
|
668
|
+
});
|
|
669
|
+
offset += 88;
|
|
770
670
|
}
|
|
671
|
+
return { stringId, schemaName, fields };
|
|
771
672
|
}
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
*/
|
|
776
|
-
loadMaterialSchemaByIndex(index) {
|
|
777
|
-
try {
|
|
778
|
-
const metadata = this.reader.readAssetMetadata(index);
|
|
779
|
-
if (!metadata || metadata.length < 196)
|
|
780
|
-
return null;
|
|
781
|
-
const view = new DataView(metadata.buffer, metadata.byteOffset, metadata.byteLength);
|
|
782
|
-
const decoder = new TextDecoder();
|
|
783
|
-
const stringIdBytes = metadata.slice(0, 128);
|
|
784
|
-
const schemaNameBytes = metadata.slice(128, 192);
|
|
785
|
-
const stringId = decoder.decode(stringIdBytes.slice(0, stringIdBytes.indexOf(0) || 128));
|
|
786
|
-
const schemaName = decoder.decode(schemaNameBytes.slice(0, schemaNameBytes.indexOf(0) || 64));
|
|
787
|
-
const fieldCount = view.getUint32(192, true);
|
|
788
|
-
const fields = [];
|
|
789
|
-
let offset = 196;
|
|
790
|
-
for (let i = 0; i < fieldCount; i++) {
|
|
791
|
-
if (offset + 88 > metadata.length)
|
|
792
|
-
break;
|
|
793
|
-
const fieldNameBytes = metadata.slice(offset, offset + 64);
|
|
794
|
-
const fieldName = decoder.decode(fieldNameBytes.slice(0, fieldNameBytes.indexOf(0) || 64));
|
|
795
|
-
const fieldType = view.getUint32(offset + 64, true);
|
|
796
|
-
const previewFlags = view.getUint32(offset + 68, true);
|
|
797
|
-
const defaultValue = [
|
|
798
|
-
view.getFloat32(offset + 72, true),
|
|
799
|
-
view.getFloat32(offset + 76, true),
|
|
800
|
-
view.getFloat32(offset + 80, true),
|
|
801
|
-
view.getFloat32(offset + 84, true)
|
|
802
|
-
];
|
|
803
|
-
const typeNames = ['Texture', 'Color', 'Float', 'Vec2', 'Vec3', 'Vec4', 'Int'];
|
|
804
|
-
const previewFlagNames = ['None', 'MainTex', 'Tint'];
|
|
805
|
-
fields.push({
|
|
806
|
-
name: fieldName,
|
|
807
|
-
type: typeNames[fieldType] || 'Float',
|
|
808
|
-
previewFlag: previewFlagNames[previewFlags] || 'None',
|
|
809
|
-
defaultValue
|
|
810
|
-
});
|
|
811
|
-
offset += 88;
|
|
812
|
-
}
|
|
813
|
-
return { stringId, schemaName, fields };
|
|
814
|
-
}
|
|
815
|
-
catch (error) {
|
|
816
|
-
console.error('Failed to read material schema:', error);
|
|
673
|
+
getMeshMaterialsByIndex_internal(index) {
|
|
674
|
+
const metadata = this.reader.readAssetMetadata(index);
|
|
675
|
+
if (!metadata || metadata.length < 140)
|
|
817
676
|
return null;
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
if (
|
|
828
|
-
|
|
829
|
-
const
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
const
|
|
833
|
-
const
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
// Parse fields: name[64] + field_type(4) + preview_flags(4) + default_value[4*4=16] = 88 bytes
|
|
843
|
-
for (let i = 0; i < fieldCount; i++) {
|
|
844
|
-
if (offset + 88 > metadata.length)
|
|
677
|
+
const view = new DataView(metadata.buffer, metadata.byteOffset, metadata.byteLength);
|
|
678
|
+
const decoder = new TextDecoder();
|
|
679
|
+
const meshGeometryCount = view.getUint32(0, true);
|
|
680
|
+
const materialCount = view.getUint32(4, true);
|
|
681
|
+
const nodeCount = view.getUint32(8, true);
|
|
682
|
+
let offset = 140;
|
|
683
|
+
offset += meshGeometryCount * 40;
|
|
684
|
+
const materials = [];
|
|
685
|
+
for (let i = 0; i < materialCount; i++) {
|
|
686
|
+
if (offset + 196 > metadata.length)
|
|
687
|
+
break;
|
|
688
|
+
const nameBytes = metadata.slice(offset, offset + 64);
|
|
689
|
+
const schemaIdBytes = metadata.slice(offset + 64, offset + 192);
|
|
690
|
+
const name = decoder.decode(nameBytes.slice(0, nameBytes.indexOf(0) || 64));
|
|
691
|
+
const schemaId = decoder.decode(schemaIdBytes.slice(0, schemaIdBytes.indexOf(0) || 128));
|
|
692
|
+
const propertyCount = view.getUint32(offset + 192, true);
|
|
693
|
+
materials.push({ name, schemaId, propertyCount, properties: [] });
|
|
694
|
+
offset += 196;
|
|
695
|
+
}
|
|
696
|
+
offset += nodeCount * 116;
|
|
697
|
+
offset += nodeCount * 4;
|
|
698
|
+
for (const mat of materials) {
|
|
699
|
+
for (let j = 0; j < mat.propertyCount; j++) {
|
|
700
|
+
if (offset + 144 > metadata.length)
|
|
845
701
|
break;
|
|
846
702
|
const fieldNameBytes = metadata.slice(offset, offset + 64);
|
|
847
703
|
const fieldName = decoder.decode(fieldNameBytes.slice(0, fieldNameBytes.indexOf(0) || 64));
|
|
848
|
-
const
|
|
849
|
-
|
|
850
|
-
|
|
704
|
+
const value = [
|
|
705
|
+
view.getFloat32(offset + 64, true),
|
|
706
|
+
view.getFloat32(offset + 68, true),
|
|
851
707
|
view.getFloat32(offset + 72, true),
|
|
852
|
-
view.getFloat32(offset + 76, true)
|
|
853
|
-
view.getFloat32(offset + 80, true),
|
|
854
|
-
view.getFloat32(offset + 84, true)
|
|
708
|
+
view.getFloat32(offset + 76, true)
|
|
855
709
|
];
|
|
856
|
-
const
|
|
857
|
-
const
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
type: typeNames[fieldType] || 'Float',
|
|
861
|
-
previewFlag: previewFlagNames[previewFlags] || 'None',
|
|
862
|
-
defaultValue
|
|
863
|
-
});
|
|
864
|
-
offset += 88; // MaterialSchemaField is 88 bytes total
|
|
865
|
-
}
|
|
866
|
-
return { stringId, schemaName, fields };
|
|
867
|
-
}
|
|
868
|
-
catch (error) {
|
|
869
|
-
console.error('Failed to read material schema:', error);
|
|
870
|
-
return null;
|
|
871
|
-
}
|
|
872
|
-
}
|
|
873
|
-
/**
|
|
874
|
-
* Get material information from a loaded mesh
|
|
875
|
-
* Returns material data including properties and texture references
|
|
876
|
-
*/
|
|
877
|
-
getMeshMaterials(assetPath) {
|
|
878
|
-
try {
|
|
879
|
-
const assetIndex = this.reader.findAssetByPath(assetPath);
|
|
880
|
-
if (assetIndex < 0)
|
|
881
|
-
return null;
|
|
882
|
-
const metadata = this.reader.readAssetMetadata(assetIndex);
|
|
883
|
-
if (!metadata || metadata.length < 140)
|
|
884
|
-
return null;
|
|
885
|
-
const view = new DataView(metadata.buffer, metadata.byteOffset, metadata.byteLength);
|
|
886
|
-
const decoder = new TextDecoder();
|
|
887
|
-
// Parse MeshMetadata
|
|
888
|
-
const meshGeometryCount = view.getUint32(0, true);
|
|
889
|
-
const materialCount = view.getUint32(4, true);
|
|
890
|
-
const nodeCount = view.getUint32(8, true);
|
|
891
|
-
let offset = 140;
|
|
892
|
-
// Skip geometries (40 bytes each - Draco compressed)
|
|
893
|
-
offset += meshGeometryCount * 40;
|
|
894
|
-
// Parse materials
|
|
895
|
-
const materials = [];
|
|
896
|
-
for (let i = 0; i < materialCount; i++) {
|
|
897
|
-
if (offset + 196 > metadata.length)
|
|
898
|
-
break;
|
|
899
|
-
const nameBytes = metadata.slice(offset, offset + 64);
|
|
900
|
-
const schemaIdBytes = metadata.slice(offset + 64, offset + 192);
|
|
901
|
-
const name = decoder.decode(nameBytes.slice(0, nameBytes.indexOf(0) || 64));
|
|
902
|
-
const schemaId = decoder.decode(schemaIdBytes.slice(0, schemaIdBytes.indexOf(0) || 128));
|
|
903
|
-
const propertyCount = view.getUint32(offset + 192, true);
|
|
904
|
-
materials.push({ name, schemaId, propertyCount, properties: [] });
|
|
905
|
-
offset += 196;
|
|
906
|
-
}
|
|
907
|
-
// Skip nodes (116 bytes each)
|
|
908
|
-
offset += nodeCount * 116;
|
|
909
|
-
// Skip mesh indices (4 bytes each)
|
|
910
|
-
const totalMeshRefs = materials.reduce((sum, mat) => {
|
|
911
|
-
// This is a simplification - we'd need to parse nodes to get exact count
|
|
912
|
-
return sum;
|
|
913
|
-
}, 0);
|
|
914
|
-
// For now, skip based on nodeCount assumption
|
|
915
|
-
offset += nodeCount * 4;
|
|
916
|
-
// Parse material properties
|
|
917
|
-
for (const mat of materials) {
|
|
918
|
-
for (let j = 0; j < mat.propertyCount; j++) {
|
|
919
|
-
if (offset + 144 > metadata.length)
|
|
920
|
-
break;
|
|
921
|
-
const fieldNameBytes = metadata.slice(offset, offset + 64);
|
|
922
|
-
const fieldName = decoder.decode(fieldNameBytes.slice(0, fieldNameBytes.indexOf(0) || 64));
|
|
923
|
-
const value = [
|
|
924
|
-
view.getFloat32(offset + 64, true),
|
|
925
|
-
view.getFloat32(offset + 68, true),
|
|
926
|
-
view.getFloat32(offset + 72, true),
|
|
927
|
-
view.getFloat32(offset + 76, true)
|
|
928
|
-
];
|
|
929
|
-
const textureIdBytes = metadata.slice(offset + 80, offset + 144);
|
|
930
|
-
const textureId = decoder.decode(textureIdBytes.slice(0, textureIdBytes.indexOf(0) || 64));
|
|
931
|
-
mat.properties.push({ fieldName, value, textureId });
|
|
932
|
-
offset += 144;
|
|
933
|
-
}
|
|
710
|
+
const textureIdBytes = metadata.slice(offset + 80, offset + 144);
|
|
711
|
+
const textureId = decoder.decode(textureIdBytes.slice(0, textureIdBytes.indexOf(0) || 64));
|
|
712
|
+
mat.properties.push({ fieldName, value, textureId });
|
|
713
|
+
offset += 144;
|
|
934
714
|
}
|
|
935
|
-
return materials;
|
|
936
|
-
}
|
|
937
|
-
catch (error) {
|
|
938
|
-
console.error('Failed to read mesh materials:', error);
|
|
939
|
-
return null;
|
|
940
715
|
}
|
|
716
|
+
return materials;
|
|
941
717
|
}
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
/**
|
|
721
|
+
* Three.js loader for StowKit asset packs
|
|
722
|
+
*
|
|
723
|
+
* Usage:
|
|
724
|
+
* ```typescript
|
|
725
|
+
* const pack = await StowKitLoader.load('assets.stow');
|
|
726
|
+
* const mesh = await pack.loadMesh('models/character');
|
|
727
|
+
* const texture = await pack.loadTexture('textures/diffuse');
|
|
728
|
+
* const audio = await pack.loadAudio('sounds/bgm', listener);
|
|
729
|
+
* ```
|
|
730
|
+
*/
|
|
731
|
+
class StowKitLoader {
|
|
942
732
|
/**
|
|
943
|
-
*
|
|
733
|
+
* Load a .stow pack file
|
|
944
734
|
*/
|
|
945
|
-
async
|
|
946
|
-
// Initialize
|
|
947
|
-
if (this.
|
|
948
|
-
await this.
|
|
735
|
+
static async load(url, options) {
|
|
736
|
+
// Initialize loaders if needed
|
|
737
|
+
if (!this.initialized) {
|
|
738
|
+
await this.initialize(options);
|
|
949
739
|
}
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
*/
|
|
955
|
-
getAssetCount() {
|
|
956
|
-
return this.reader.getAssetCount();
|
|
957
|
-
}
|
|
958
|
-
/**
|
|
959
|
-
* Get info about a specific asset
|
|
960
|
-
*/
|
|
961
|
-
getAssetInfo(index) {
|
|
962
|
-
return this.reader.getAssetInfo(index);
|
|
963
|
-
}
|
|
964
|
-
/**
|
|
965
|
-
* Read asset data by index
|
|
966
|
-
*/
|
|
967
|
-
readAssetData(index) {
|
|
968
|
-
return this.reader.readAssetData(index);
|
|
969
|
-
}
|
|
970
|
-
/**
|
|
971
|
-
* Read asset metadata by index
|
|
972
|
-
*/
|
|
973
|
-
readAssetMetadata(index) {
|
|
974
|
-
return this.reader.readAssetMetadata(index);
|
|
975
|
-
}
|
|
976
|
-
/**
|
|
977
|
-
* Parse texture metadata
|
|
978
|
-
*/
|
|
979
|
-
parseTextureMetadata(metadataBytes) {
|
|
980
|
-
return this.reader.parseTextureMetadata(metadataBytes);
|
|
981
|
-
}
|
|
982
|
-
/**
|
|
983
|
-
* Create an HTML audio element for preview
|
|
984
|
-
* @param index - Asset index
|
|
985
|
-
*/
|
|
986
|
-
async createAudioPreview(index) {
|
|
987
|
-
const data = this.reader.readAssetData(index);
|
|
988
|
-
if (!data) {
|
|
989
|
-
throw new Error(`Failed to read audio data for index ${index}`);
|
|
740
|
+
// Fetch the pack file
|
|
741
|
+
const response = await fetch(url);
|
|
742
|
+
if (!response.ok) {
|
|
743
|
+
throw new Error(`Failed to fetch ${url}: ${response.statusText}`);
|
|
990
744
|
}
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
//
|
|
995
|
-
|
|
996
|
-
audio.controls = true;
|
|
997
|
-
audio.src = url;
|
|
998
|
-
// Clean up URL when audio ends or on error
|
|
999
|
-
audio.addEventListener('ended', () => URL.revokeObjectURL(url));
|
|
1000
|
-
audio.addEventListener('error', () => URL.revokeObjectURL(url));
|
|
1001
|
-
return audio;
|
|
1002
|
-
}
|
|
1003
|
-
/**
|
|
1004
|
-
* List all assets in the opened pack
|
|
1005
|
-
*/
|
|
1006
|
-
listAssets() {
|
|
1007
|
-
return this.reader.listAssets();
|
|
745
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
746
|
+
// Open pack
|
|
747
|
+
await this.reader.open(arrayBuffer);
|
|
748
|
+
// Return pack wrapper
|
|
749
|
+
return new StowKitPack(this.reader, this.ktx2Loader, this.dracoLoader);
|
|
1008
750
|
}
|
|
1009
751
|
/**
|
|
1010
|
-
*
|
|
752
|
+
* Initialize the loader (called automatically on first load)
|
|
1011
753
|
*/
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
754
|
+
static async initialize(options) {
|
|
755
|
+
const wasmPath = options?.wasmPath || '/stowkit_reader.wasm';
|
|
756
|
+
const basisPath = options?.basisPath || '/stowkit/basis/';
|
|
757
|
+
const dracoPath = options?.dracoPath || '/stowkit/draco/';
|
|
758
|
+
// Initialize reader
|
|
759
|
+
this.reader = new StowKitReader(wasmPath);
|
|
760
|
+
await this.reader.init();
|
|
761
|
+
// Setup KTX2 loader
|
|
762
|
+
this.ktx2Loader = new KTX2Loader();
|
|
763
|
+
this.ktx2Loader.setTranscoderPath(basisPath);
|
|
764
|
+
// Setup Draco loader
|
|
765
|
+
this.dracoLoader = new DRACOLoader();
|
|
766
|
+
this.dracoLoader.setDecoderPath(dracoPath);
|
|
767
|
+
// Detect support
|
|
768
|
+
const tempRenderer = new THREE.WebGLRenderer();
|
|
769
|
+
this.ktx2Loader.detectSupport(tempRenderer);
|
|
770
|
+
tempRenderer.dispose();
|
|
771
|
+
this.initialized = true;
|
|
1018
772
|
}
|
|
1019
773
|
}
|
|
774
|
+
StowKitLoader.reader = null;
|
|
775
|
+
StowKitLoader.ktx2Loader = null;
|
|
776
|
+
StowKitLoader.dracoLoader = null;
|
|
777
|
+
StowKitLoader.initialized = false;
|
|
1020
778
|
|
|
1021
|
-
export {
|
|
779
|
+
export { StowKitLoader, StowKitPack };
|
|
1022
780
|
//# sourceMappingURL=stowkit-three-loader.esm.js.map
|