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