@stowkit/three-loader 0.1.2 → 0.1.3

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.
Files changed (2) hide show
  1. package/README.md +540 -80
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @stowkit/three-loader
2
2
 
3
- Three.js loader for StowKit asset packs. Provides an easy-to-use API for loading meshes, textures, and audio from `.stow` files into Three.js scenes.
3
+ Three.js loader for StowKit asset packs. Provides a simple, high-level API for loading meshes, textures, and audio from `.stow` files into Three.js applications.
4
4
 
5
5
  ## Installation
6
6
 
@@ -8,156 +8,616 @@ Three.js loader for StowKit asset packs. Provides an easy-to-use API for loading
8
8
  npm install @stowkit/three-loader three
9
9
  ```
10
10
 
11
- ## Usage
12
-
13
- ### Basic Mesh Loading
11
+ ## Quick Start
14
12
 
15
13
  ```typescript
16
14
  import * as THREE from 'three';
17
15
  import { StowKitLoader } from '@stowkit/three-loader';
18
16
 
17
+ // Create loader (auto-configured with defaults)
19
18
  const loader = new StowKitLoader();
20
- loader.setTranscoderPath('/basis/'); // Path to Basis Universal transcoder
21
19
 
22
- // Load a mesh
23
- const scene = await loader.loadMesh('assets.stow', 'models/character.mesh');
20
+ // Open a .stow pack file
21
+ await loader.openPack('assets.stow');
22
+
23
+ // Load a mesh with automatic textures and materials
24
+ const scene = await loader.loadMesh('', 'models/character.mesh');
24
25
  threeScene.add(scene);
25
26
  ```
26
27
 
27
- ### Loading Multiple Assets from Same Pack
28
+ ## Features
29
+
30
+ - ✅ **Zero configuration** - Works out of the box with sensible defaults
31
+ - ✅ **Automatic texture loading** - Materials reference textures by ID, loader handles everything
32
+ - ✅ **Material system** - Full support for material schemas, properties, and assignments
33
+ - ✅ **KTX2/Basis Universal** - GPU-compressed textures with automatic transcoding
34
+ - ✅ **Scene hierarchy** - Preserves transforms, parent-child relationships, and node structure
35
+ - ✅ **Multi-material meshes** - Supports meshes with multiple materials/submeshes
36
+ - ✅ **Type-safe** - Full TypeScript definitions included
37
+
38
+ ## API Reference
39
+
40
+ ### Constructor
41
+
42
+ ```typescript
43
+ new StowKitLoader(manager?: THREE.LoadingManager, parameters?: StowKitLoaderParameters)
44
+ ```
45
+
46
+ **Parameters (all optional):**
47
+ ```typescript
48
+ interface StowKitLoaderParameters {
49
+ transcoderPath?: string; // Path to Basis Universal transcoder (default: '/basis/')
50
+ wasmPath?: string; // Path to WASM reader module (default: '/stowkit_reader.wasm')
51
+ reader?: StowKitReader; // Custom reader instance (advanced)
52
+ }
53
+ ```
28
54
 
55
+ **Example:**
29
56
  ```typescript
57
+ // Default configuration
30
58
  const loader = new StowKitLoader();
31
- loader.setTranscoderPath('/basis/');
32
59
 
33
- // Open pack once
60
+ // Custom paths
61
+ const loader = new StowKitLoader(undefined, {
62
+ transcoderPath: '/my-basis-path/',
63
+ wasmPath: '/custom/stowkit_reader.wasm'
64
+ });
65
+ ```
66
+
67
+ ### Opening Packs
68
+
69
+ #### `openPack(url: string): Promise<void>`
70
+
71
+ Open a .stow pack file from a URL. Call this once, then load multiple assets.
72
+
73
+ ```typescript
34
74
  await loader.openPack('assets.stow');
75
+ ```
76
+
77
+ #### `open(fileOrBuffer: ArrayBuffer | File): Promise<boolean>`
35
78
 
36
- // Load multiple assets (no need to pass URL again)
37
- const character = await loader.loadMesh('', 'models/character.mesh');
38
- const weapon = await loader.loadMesh('', 'models/weapon.mesh');
39
- const texture = await loader.loadTexture('', 'textures/diffuse.ktx2');
79
+ Open a pack from a File or ArrayBuffer.
40
80
 
41
- threeScene.add(character);
42
- threeScene.add(weapon);
81
+ ```typescript
82
+ // From file input
83
+ const file = input.files[0];
84
+ await loader.open(file);
85
+
86
+ // From fetch
87
+ const response = await fetch('assets.stow');
88
+ const buffer = await response.arrayBuffer();
89
+ await loader.open(buffer);
43
90
  ```
44
91
 
45
- ### Loading Textures
92
+ ### Loading Meshes
93
+
94
+ #### `loadMesh(url: string, assetPath: string, onLoad?, onProgress?, onError?): Promise<THREE.Group>`
95
+
96
+ Load a mesh asset with automatic texture and material loading.
97
+
98
+ **Parameters:**
99
+ - `url` - URL to .stow file (empty string if already opened)
100
+ - `assetPath` - Canonical path to mesh within pack
101
+ - `onLoad` - Optional callback when loading completes
102
+ - `onProgress` - Optional progress callback
103
+ - `onError` - Optional error callback
46
104
 
105
+ **Returns:** `THREE.Group` containing the mesh hierarchy
106
+
107
+ **Example:**
47
108
  ```typescript
48
- const loader = new StowKitLoader();
49
- loader.setTranscoderPath('/basis/');
109
+ // Simple async/await
110
+ const mesh = await loader.loadMesh('', 'models/character.mesh');
111
+ scene.add(mesh);
112
+
113
+ // With callbacks (Three.js style)
114
+ loader.loadMesh('assets.stow', 'models/tree.mesh',
115
+ (scene) => {
116
+ threeScene.add(scene);
117
+ },
118
+ (progress) => console.log('Loading...', progress),
119
+ (error) => console.error('Failed:', error)
120
+ );
121
+ ```
122
+
123
+ **What you get:**
124
+ - Complete scene hierarchy with nodes
125
+ - All geometries (vertices, normals, UVs, indices)
126
+ - Materials with properties applied (colors, etc.)
127
+ - Textures automatically loaded and applied to materials
128
+ - Transforms (position, rotation, scale) preserved
129
+
130
+ ### Loading Textures
131
+
132
+ #### `loadTexture(url: string, assetPath: string, onLoad?, onProgress?, onError?): Promise<THREE.CompressedTexture>`
133
+
134
+ Load a texture asset (KTX2 format).
50
135
 
51
- const texture = await loader.loadTexture('assets.stow', 'textures/wood.ktx2');
136
+ ```typescript
137
+ const texture = await loader.loadTexture('', 'textures/wood.ktx2');
52
138
  material.map = texture;
53
139
  material.needsUpdate = true;
54
140
  ```
55
141
 
142
+ #### `loadTextureByIndex(index: number, onLoad?, onProgress?, onError?): Promise<THREE.CompressedTexture>`
143
+
144
+ Load a texture by its asset index (when you don't have the path).
145
+
146
+ ```typescript
147
+ const texture = await loader.loadTextureByIndex(5);
148
+ ```
149
+
56
150
  ### Loading Audio
57
151
 
152
+ #### `loadAudio(url: string, assetPath: string, listener: THREE.AudioListener, onLoad?, onProgress?, onError?): Promise<THREE.Audio>`
153
+
154
+ Load an audio asset as a Three.js Audio object.
155
+
58
156
  ```typescript
59
157
  const listener = new THREE.AudioListener();
60
158
  camera.add(listener);
61
159
 
62
- const loader = new StowKitLoader();
63
- const audio = await loader.loadAudio('assets.stow', 'sounds/bgm.ogg', listener);
160
+ const audio = await loader.loadAudio('', 'sounds/bgm.ogg', listener);
161
+ audio.setLoop(true);
64
162
  audio.play();
65
163
  ```
66
164
 
67
- ### With Callbacks (Three.js style)
165
+ #### `createAudioPreview(index: number): Promise<HTMLAudioElement>`
166
+
167
+ Create an HTML5 audio element for previewing audio (simpler than Three.js Audio).
68
168
 
69
169
  ```typescript
70
- loader.loadMesh(
71
- 'assets.stow',
72
- 'models/tree.mesh',
73
- (scene) => {
74
- // onLoad
75
- threeScene.add(scene);
76
- },
77
- (progress) => {
78
- // onProgress
79
- console.log('Loading...', progress);
80
- },
81
- (error) => {
82
- // onError
83
- console.error('Failed to load:', error);
170
+ const audioElement = await loader.createAudioPreview(index);
171
+ audioElement.autoplay = true;
172
+ container.appendChild(audioElement);
173
+ ```
174
+
175
+ ### Material Information
176
+
177
+ #### `loadMaterialSchema(assetPath: string): object | null`
178
+
179
+ Load material schema definition.
180
+
181
+ ```typescript
182
+ const schema = loader.loadMaterialSchema('shaders/DefaultShader.stowschema');
183
+
184
+ console.log(schema.schemaName); // "Default Shader"
185
+ console.log(schema.fields); // Array of field definitions
186
+
187
+ schema.fields.forEach(field => {
188
+ console.log(`${field.name}: ${field.type}`);
189
+ // mainTex: Texture
190
+ // tint: Color
191
+ // roughness: Float
192
+ });
193
+ ```
194
+
195
+ #### `loadMaterialSchemaByIndex(index: number): object | null`
196
+
197
+ Load material schema by asset index.
198
+
199
+ ```typescript
200
+ const schema = loader.loadMaterialSchemaByIndex(6);
201
+ ```
202
+
203
+ **Schema Structure:**
204
+ ```typescript
205
+ {
206
+ stringId: string; // Schema canonical path
207
+ schemaName: string; // Human-readable name
208
+ fields: Array<{
209
+ name: string; // Field name (e.g., "mainTex", "tint")
210
+ type: 'Texture' | 'Color' | 'Float' | 'Vec2' | 'Vec3' | 'Vec4' | 'Int';
211
+ previewFlag: 'None' | 'MainTex' | 'Tint';
212
+ defaultValue: [number, number, number, number];
213
+ }>;
214
+ }
215
+ ```
216
+
217
+ #### `getMeshMaterials(assetPath: string): array | null`
218
+
219
+ Get material information from a mesh (properties and texture references).
220
+
221
+ ```typescript
222
+ const materials = loader.getMeshMaterials('models/character.mesh');
223
+
224
+ materials.forEach(mat => {
225
+ console.log(`Material: ${mat.name}`);
226
+ console.log(`Schema: ${mat.schemaId}`);
227
+
228
+ mat.properties.forEach(prop => {
229
+ if (prop.textureId) {
230
+ console.log(`${prop.fieldName}: ${prop.textureId}`); // mainTex: textures/skin.ktx2
231
+ } else {
232
+ console.log(`${prop.fieldName}:`, prop.value); // tint: [1, 0.8, 0.6, 1]
84
233
  }
85
- );
234
+ });
235
+ });
236
+ ```
237
+
238
+ ### Metadata Helpers
239
+
240
+ #### `getTextureMetadata(assetPath: string): object | null`
241
+
242
+ Get texture metadata (dimensions, format, etc.).
243
+
244
+ ```typescript
245
+ const info = loader.getTextureMetadata('textures/wood.ktx2');
246
+
247
+ console.log(`${info.width}x${info.height}`); // 1024x1024
248
+ console.log(`Channels: ${info.channels}`); // 3 (RGB) or 4 (RGBA)
249
+ console.log(`KTX2: ${info.isKtx2}`); // true
86
250
  ```
87
251
 
88
- ### Getting Metadata
252
+ #### `getAudioMetadata(assetPath: string): object | null`
253
+
254
+ Get audio metadata (sample rate, duration, etc.).
89
255
 
90
256
  ```typescript
91
- // Get texture info
92
- const texInfo = loader.getTextureMetadata('textures/wood.ktx2');
93
- console.log(`Texture: ${texInfo.width}x${texInfo.height}`);
257
+ const info = loader.getAudioMetadata('sounds/bgm.ogg');
94
258
 
95
- // Get audio info
96
- const audioInfo = loader.getAudioMetadata('sounds/bgm.ogg');
97
- console.log(`Duration: ${audioInfo.durationMs}ms`);
259
+ console.log(`Sample Rate: ${info.sampleRate} Hz`);
260
+ console.log(`Channels: ${info.channels}`);
261
+ console.log(`Duration: ${info.durationMs / 1000}s`);
98
262
  ```
99
263
 
100
- ### Listing Assets
264
+ ### Asset Listing
265
+
266
+ #### `listAssets(): array`
267
+
268
+ List all assets in the opened pack.
101
269
 
102
270
  ```typescript
103
- await loader.openPack('assets.stow');
104
271
  const assets = loader.listAssets();
105
272
 
106
273
  assets.forEach(asset => {
107
- console.log(`${asset.name} (${asset.type}): ${asset.dataSize} bytes`);
274
+ console.log(`[${asset.index}] ${asset.name}`);
275
+ console.log(` Type: ${asset.type}, Size: ${asset.dataSize} bytes`);
108
276
  });
109
277
  ```
110
278
 
111
- ## Features
279
+ #### `getAssetCount(): number`
112
280
 
113
- - **Automatic texture loading** - Materials with `mainTex` properties automatically load and apply textures
114
- - ✅ **Material schema support** - Reads tint colors, texture references, and other material properties
115
- - ✅ **KTX2/Basis Universal** - Compressed texture support with GPU transcoding
116
- - ✅ **Scene hierarchy** - Preserves node transforms and parent-child relationships
117
- - ✅ **Multi-material meshes** - Supports meshes with multiple materials/submeshes
118
- - ✅ **Type-safe** - Full TypeScript definitions
281
+ Get total number of assets.
119
282
 
120
- ## API Reference
283
+ ```typescript
284
+ const count = loader.getAssetCount();
285
+ ```
286
+
287
+ #### `getAssetInfo(index: number): object`
121
288
 
122
- ### StowKitLoader
289
+ Get detailed info about a specific asset.
123
290
 
124
- #### Constructor
125
291
  ```typescript
126
- new StowKitLoader(manager?: THREE.LoadingManager, parameters?: StowKitLoaderParameters)
292
+ const info = loader.getAssetInfo(0);
293
+ console.log(info.type, info.data_size, info.metadata_size);
127
294
  ```
128
295
 
129
- #### Methods
296
+ ### Utility Methods
130
297
 
131
- ##### `setTranscoderPath(path: string): this`
132
- Set the path to the Basis Universal transcoder (required for KTX2 textures).
298
+ #### `readAssetData(index: number): Uint8Array | null`
133
299
 
134
- ##### `detectSupport(renderer: THREE.WebGLRenderer): this`
135
- Detect GPU compressed texture format support.
300
+ Read raw asset data by index.
136
301
 
137
- ##### `openPack(url: string): Promise<void>`
138
- Open a .stow pack file. Call this once when loading multiple assets from the same pack.
302
+ ```typescript
303
+ const data = loader.readAssetData(5);
304
+ ```
139
305
 
140
- ##### `loadMesh(url, assetPath, onLoad?, onProgress?, onError?): Promise<THREE.Group>`
141
- Load a mesh asset. Returns a Three.js Group containing the mesh hierarchy.
306
+ #### `readAssetMetadata(index: number): Uint8Array | null`
142
307
 
143
- ##### `loadTexture(url, assetPath, onLoad?, onProgress?, onError?): Promise<THREE.CompressedTexture>`
144
- Load a texture asset. Returns a Three.js CompressedTexture.
308
+ Read raw asset metadata by index.
145
309
 
146
- ##### `loadAudio(url, assetPath, listener, onLoad?, onProgress?, onError?): Promise<THREE.Audio>`
147
- Load an audio asset. Returns a Three.js Audio object.
310
+ ```typescript
311
+ const metadata = loader.readAssetMetadata(5);
312
+ ```
148
313
 
149
- ##### `getTextureMetadata(assetPath): object | null`
150
- Get texture metadata (width, height, format, etc.).
314
+ #### `dispose(): void`
151
315
 
152
- ##### `getAudioMetadata(assetPath): object | null`
153
- Get audio metadata (sample rate, channels, duration).
316
+ Clean up resources and free memory.
154
317
 
155
- ##### `listAssets(): array`
156
- List all assets in the opened pack.
318
+ ```typescript
319
+ loader.dispose();
320
+ ```
157
321
 
158
- ##### `dispose(): void`
159
- Clean up resources and free memory.
322
+ ## Complete Example
323
+
324
+ ```typescript
325
+ import * as THREE from 'three';
326
+ import { StowKitLoader } from '@stowkit/three-loader';
327
+
328
+ // Setup Three.js scene
329
+ const scene = new THREE.Scene();
330
+ const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
331
+ const renderer = new THREE.WebGLRenderer();
332
+ renderer.setSize(window.innerWidth, window.innerHeight);
333
+ document.body.appendChild(renderer.domElement);
334
+
335
+ // Setup audio
336
+ const listener = new THREE.AudioListener();
337
+ camera.add(listener);
338
+
339
+ // Initialize loader
340
+ const loader = new StowKitLoader();
341
+
342
+ // Load assets
343
+ async function loadAssets() {
344
+ await loader.openPack('game-assets.stow');
345
+
346
+ // Load main character
347
+ const character = await loader.loadMesh('', 'models/player.mesh');
348
+ character.position.set(0, 0, 0);
349
+ scene.add(character);
350
+
351
+ // Load environment
352
+ const environment = await loader.loadMesh('', 'models/level1.mesh');
353
+ scene.add(environment);
354
+
355
+ // Load background music
356
+ const bgm = await loader.loadAudio('', 'audio/theme.ogg', listener);
357
+ bgm.setLoop(true);
358
+ bgm.setVolume(0.5);
359
+ bgm.play();
360
+
361
+ // Log what was loaded
362
+ console.log(`Loaded ${loader.getAssetCount()} assets`);
363
+
364
+ // Start rendering
365
+ animate();
366
+ }
367
+
368
+ function animate() {
369
+ requestAnimationFrame(animate);
370
+ renderer.render(scene, camera);
371
+ }
372
+
373
+ loadAssets();
374
+ ```
375
+
376
+ ## Material System
377
+
378
+ The loader automatically handles material assignments:
379
+
380
+ ```typescript
381
+ // When you load a mesh, materials are created with:
382
+ // 1. Colors from 'tint' properties
383
+ // 2. Textures from 'mainTex', 'normalMap', etc.
384
+ // 3. Other properties (roughness, metallic, etc.)
385
+
386
+ const mesh = await loader.loadMesh('', 'models/character.mesh');
387
+
388
+ // Materials are automatically applied to geometries based on material indices
389
+ // Textures are automatically loaded by their string IDs and applied to materials
390
+ // No manual texture loading needed!
391
+ ```
392
+
393
+ **Supported material properties:**
394
+ - `mainTex` / `diffuse` / `albedo` → `material.map`
395
+ - `tint` / `color` → `material.color`
396
+ - `normalMap` → `material.normalMap`
397
+ - `metallicMap` → `material.metalnessMap`
398
+ - `roughnessMap` → `material.roughnessMap`
399
+
400
+ ## Advanced Usage
401
+
402
+ ### Multiple Assets from Same Pack
403
+
404
+ ```typescript
405
+ // Open once
406
+ await loader.openPack('assets.stow');
407
+
408
+ // Load many (no URL needed)
409
+ const mesh1 = await loader.loadMesh('', 'models/char1.mesh');
410
+ const mesh2 = await loader.loadMesh('', 'models/char2.mesh');
411
+ const tex1 = await loader.loadTexture('', 'textures/t1.ktx2');
412
+ const tex2 = await loader.loadTexture('', 'textures/t2.ktx2');
413
+ ```
414
+
415
+ ### Inspect Before Loading
416
+
417
+ ```typescript
418
+ await loader.openPack('assets.stow');
419
+
420
+ // List all assets
421
+ const assets = loader.listAssets();
422
+ const meshes = assets.filter(a => a.type === 1); // Type 1 = Static Mesh
423
+ const textures = assets.filter(a => a.type === 2); // Type 2 = Texture
424
+
425
+ // Get material info before loading
426
+ const materials = loader.getMeshMaterials('models/character.mesh');
427
+ materials.forEach(mat => {
428
+ console.log(`Material "${mat.name}" needs:`, mat.properties.map(p => p.textureId).filter(Boolean));
429
+ });
430
+
431
+ // Preload textures
432
+ for (const mat of materials) {
433
+ for (const prop of mat.properties) {
434
+ if (prop.textureId) {
435
+ await loader.loadTexture('', prop.textureId);
436
+ }
437
+ }
438
+ }
439
+
440
+ // Now load the mesh (textures already cached)
441
+ const mesh = await loader.loadMesh('', 'models/character.mesh');
442
+ ```
443
+
444
+ ### Error Handling
445
+
446
+ ```typescript
447
+ try {
448
+ const mesh = await loader.loadMesh('assets.stow', 'models/character.mesh');
449
+ scene.add(mesh);
450
+ } catch (error) {
451
+ console.error('Failed to load mesh:', error.message);
452
+ }
453
+
454
+ // Or with callbacks
455
+ loader.loadMesh('assets.stow', 'models/tree.mesh',
456
+ (scene) => threeScene.add(scene),
457
+ undefined,
458
+ (error) => console.error('Error:', error)
459
+ );
460
+ ```
461
+
462
+ ## Asset Types
463
+
464
+ | Type | Value | Description |
465
+ |------|-------|-------------|
466
+ | Static Mesh | 1 | 3D models with geometry, materials, and hierarchy |
467
+ | Texture 2D | 2 | KTX2 compressed textures |
468
+ | Audio | 3 | Audio files (OGG, MP3, etc.) |
469
+ | Material Schema | 4 | Material template definitions |
470
+
471
+ ## File Structure
472
+
473
+ `.stow` files contain:
474
+ - **File Header** - Magic number, version, asset count
475
+ - **Asset Data** - Binary geometry, texture, audio data
476
+ - **Asset Directory** - Index of all assets with offsets
477
+ - **Metadata** - Asset-specific information (dimensions, transforms, etc.)
478
+
479
+ ## Requirements
480
+
481
+ ### Public Folder Setup
482
+
483
+ Place these files in your public folder:
484
+
485
+ ```
486
+ public/
487
+ ├── stowkit_reader.wasm # WASM reader module
488
+ └── basis/ # Basis Universal transcoder
489
+ ├── basis_transcoder.js
490
+ └── basis_transcoder.wasm
491
+ ```
492
+
493
+ **Get the files:**
494
+ - `stowkit_reader.wasm` - Included with `@stowkit/reader` package
495
+ - Basis transcoder - From [three.js examples](https://github.com/mrdoob/three.js/tree/dev/examples/jsm/libs/basis)
496
+
497
+ ### Three.js Version
498
+
499
+ Requires Three.js r150 or later.
500
+
501
+ ```bash
502
+ npm install three@^0.150.0
503
+ ```
504
+
505
+ ## Examples
506
+
507
+ ### Loading a Character with Equipment
508
+
509
+ ```typescript
510
+ const loader = new StowKitLoader();
511
+ await loader.openPack('characters.stow');
512
+
513
+ // Load character base
514
+ const character = await loader.loadMesh('', 'characters/knight.mesh');
515
+
516
+ // Load and attach weapon
517
+ const weapon = await loader.loadMesh('', 'weapons/sword.mesh');
518
+ const rightHand = character.getObjectByName('RightHand');
519
+ if (rightHand) {
520
+ rightHand.add(weapon);
521
+ }
522
+
523
+ scene.add(character);
524
+ ```
525
+
526
+ ### Dynamic Material Updates
527
+
528
+ ```typescript
529
+ // Load mesh
530
+ const mesh = await loader.loadMesh('', 'models/car.mesh');
531
+
532
+ // Get material info
533
+ const materials = loader.getMeshMaterials('models/car.mesh');
534
+
535
+ // Find and modify specific material
536
+ mesh.traverse((child) => {
537
+ if (child instanceof THREE.Mesh) {
538
+ const mat = child.material as THREE.MeshStandardMaterial;
539
+ if (mat.name === 'Paint') {
540
+ // Change car color
541
+ mat.color.set(0xff0000); // Red
542
+ }
543
+ }
544
+ });
545
+ ```
546
+
547
+ ### Texture Swapping
548
+
549
+ ```typescript
550
+ // Load model
551
+ const character = await loader.loadMesh('', 'characters/player.mesh');
552
+
553
+ // Load alternate texture
554
+ const winterSkin = await loader.loadTexture('', 'textures/winter-skin.ktx2');
555
+
556
+ // Apply to character
557
+ character.traverse((child) => {
558
+ if (child instanceof THREE.Mesh) {
559
+ const mat = child.material as THREE.MeshStandardMaterial;
560
+ mat.map = winterSkin;
561
+ mat.needsUpdate = true;
562
+ }
563
+ });
564
+ ```
565
+
566
+ ### Audio with Spatial Sound
567
+
568
+ ```typescript
569
+ const listener = new THREE.AudioListener();
570
+ camera.add(listener);
571
+
572
+ // Load positional audio
573
+ const sound = await loader.loadAudio('', 'sounds/waterfall.ogg', listener);
574
+ const positionalAudio = new THREE.PositionalAudio(listener);
575
+ positionalAudio.setBuffer(sound.buffer);
576
+ positionalAudio.setRefDistance(20);
577
+ positionalAudio.setLoop(true);
578
+
579
+ // Attach to a mesh
580
+ waterfall.add(positionalAudio);
581
+ positionalAudio.play();
582
+ ```
583
+
584
+ ## Troubleshooting
585
+
586
+ ### Textures appear upside down
587
+
588
+ The loader automatically handles UV orientation for KTX2 textures. If textures still appear flipped, check your source files.
589
+
590
+ ### "Missing initialization with .detectSupport()"
591
+
592
+ The loader automatically calls `detectSupport()` on initialization. If you see this error, ensure you're creating the loader before loading textures.
593
+
594
+ ### "Asset not found"
595
+
596
+ Make sure:
597
+ - The pack file is opened before loading assets
598
+ - Asset paths match exactly (case-sensitive)
599
+ - The asset exists in the pack (use `listAssets()` to verify)
600
+
601
+ ### Textures not loading on meshes
602
+
603
+ Check console for texture loading messages. Common issues:
604
+ - Texture string ID in material doesn't match any asset
605
+ - Texture asset is missing from pack
606
+ - Basis transcoder files not in `/basis/` folder
607
+
608
+ ## Performance Tips
609
+
610
+ 1. **Reuse loader instance** - Create one loader, load many assets
611
+ 2. **Open pack once** - Use `openPack()` then load multiple assets
612
+ 3. **Dispose when done** - Call `loader.dispose()` to free memory
613
+ 4. **Use compressed textures** - KTX2 provides GPU-native compression
160
614
 
161
615
  ## License
162
616
 
163
617
  MIT
618
+
619
+ ## Links
620
+
621
+ - [npm package](https://www.npmjs.com/package/@stowkit/three-loader)
622
+ - [Three.js documentation](https://threejs.org/docs/)
623
+ - [Basis Universal](https://github.com/BinomialLLC/basis_universal)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stowkit/three-loader",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "Three.js loader for StowKit asset packs",
5
5
  "main": "dist/stowkit-three-loader.js",
6
6
  "module": "dist/stowkit-three-loader.esm.js",