@zephyr3d/scene 0.9.3 → 0.9.5

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 (188) hide show
  1. package/dist/animation/animationmask.js +224 -0
  2. package/dist/animation/animationmask.js.map +1 -0
  3. package/dist/animation/animationset.js +268 -67
  4. package/dist/animation/animationset.js.map +1 -1
  5. package/dist/animation/animationtrack.js.map +1 -1
  6. package/dist/animation/eulerrotationtrack.js +3 -0
  7. package/dist/animation/eulerrotationtrack.js.map +1 -1
  8. package/dist/animation/fixed_geometry_cache_track.js +7 -0
  9. package/dist/animation/fixed_geometry_cache_track.js.map +1 -1
  10. package/dist/animation/ik_modifier.js.map +1 -1
  11. package/dist/animation/joint_dynamics/collision.js +6 -4
  12. package/dist/animation/joint_dynamics/collision.js.map +1 -1
  13. package/dist/animation/joint_dynamics/controller.js +390 -43
  14. package/dist/animation/joint_dynamics/controller.js.map +1 -1
  15. package/dist/animation/joint_dynamics/joint_dynamics_system.js +62 -3
  16. package/dist/animation/joint_dynamics/joint_dynamics_system.js.map +1 -1
  17. package/dist/animation/joint_dynamics/solver.js +4 -3
  18. package/dist/animation/joint_dynamics/solver.js.map +1 -1
  19. package/dist/animation/joint_dynamics/types.js.map +1 -1
  20. package/dist/animation/joint_dynamics_modifier.js +2 -0
  21. package/dist/animation/joint_dynamics_modifier.js.map +1 -1
  22. package/dist/animation/morphtarget.js +3 -72
  23. package/dist/animation/morphtarget.js.map +1 -1
  24. package/dist/animation/morphtrack.js +3 -0
  25. package/dist/animation/morphtrack.js.map +1 -1
  26. package/dist/animation/pca_geometry_cache_track.js +3 -0
  27. package/dist/animation/pca_geometry_cache_track.js.map +1 -1
  28. package/dist/animation/proptrack.js +3 -0
  29. package/dist/animation/proptrack.js.map +1 -1
  30. package/dist/animation/rotationtrack.js +3 -0
  31. package/dist/animation/rotationtrack.js.map +1 -1
  32. package/dist/animation/scaletrack.js +3 -0
  33. package/dist/animation/scaletrack.js.map +1 -1
  34. package/dist/animation/skeleton.js +254 -79
  35. package/dist/animation/skeleton.js.map +1 -1
  36. package/dist/animation/skeleton_modifier.js.map +1 -1
  37. package/dist/animation/spring/spring_particle.js +4 -4
  38. package/dist/animation/spring/spring_particle.js.map +1 -1
  39. package/dist/animation/spring_modifier.js.map +1 -1
  40. package/dist/animation/translationtrack.js +3 -0
  41. package/dist/animation/translationtrack.js.map +1 -1
  42. package/dist/app/engine.js +129 -17
  43. package/dist/app/engine.js.map +1 -1
  44. package/dist/app/scriptregistry.js +251 -80
  45. package/dist/app/scriptregistry.js.map +1 -1
  46. package/dist/asset/assetmanager.js +133 -102
  47. package/dist/asset/assetmanager.js.map +1 -1
  48. package/dist/asset/loaders/gltf/gltf_loader.js +18 -14
  49. package/dist/asset/loaders/gltf/gltf_loader.js.map +1 -1
  50. package/dist/asset/loaders/loader.js +1 -6
  51. package/dist/asset/loaders/loader.js.map +1 -1
  52. package/dist/asset/loaders/zabc/zabc_loader.js.map +1 -1
  53. package/dist/asset/model.js +1052 -183
  54. package/dist/asset/model.js.map +1 -1
  55. package/dist/avatar/wardrobe.js +474 -0
  56. package/dist/avatar/wardrobe.js.map +1 -0
  57. package/dist/index.d.ts +1810 -233
  58. package/dist/index.js +13 -3
  59. package/dist/index.js.map +1 -1
  60. package/dist/material/material.js +1 -1
  61. package/dist/material/material.js.map +1 -1
  62. package/dist/material/mixins/lightmodel/pbrmetallicroughness.js +6 -3
  63. package/dist/material/mixins/lightmodel/pbrmetallicroughness.js.map +1 -1
  64. package/dist/material/mixins/pbr/common.js +72 -15
  65. package/dist/material/mixins/pbr/common.js.map +1 -1
  66. package/dist/material/msdf_text.js +180 -0
  67. package/dist/material/msdf_text.js.map +1 -0
  68. package/dist/material/msdf_text_sprite.js +64 -0
  69. package/dist/material/msdf_text_sprite.js.map +1 -0
  70. package/dist/material/pbrblueprint.js +21 -6
  71. package/dist/material/pbrblueprint.js.map +1 -1
  72. package/dist/material/shader/helper.js +277 -277
  73. package/dist/material/shader/helper.js.map +1 -1
  74. package/dist/material/sprite_sdf.js +106 -0
  75. package/dist/material/sprite_sdf.js.map +1 -0
  76. package/dist/posteffect/ssr.js +1 -0
  77. package/dist/posteffect/ssr.js.map +1 -1
  78. package/dist/render/abuffer_oit.js +1 -0
  79. package/dist/render/abuffer_oit.js.map +1 -1
  80. package/dist/render/cluster_light.js.map +1 -1
  81. package/dist/render/cull_visitor.js +28 -0
  82. package/dist/render/cull_visitor.js.map +1 -1
  83. package/dist/render/drawable.js.map +1 -1
  84. package/dist/render/drawable_mixin.js +1 -0
  85. package/dist/render/drawable_mixin.js.map +1 -1
  86. package/dist/render/envlight.js +54 -1
  87. package/dist/render/envlight.js.map +1 -1
  88. package/dist/render/fbm_wavegenerator.js +1 -0
  89. package/dist/render/fbm_wavegenerator.js.map +1 -1
  90. package/dist/render/gerstner_wavegenerator.js +1 -0
  91. package/dist/render/gerstner_wavegenerator.js.map +1 -1
  92. package/dist/render/globalbindgroup_allocator.js +1 -0
  93. package/dist/render/globalbindgroup_allocator.js.map +1 -1
  94. package/dist/render/render_queue.js +1 -0
  95. package/dist/render/render_queue.js.map +1 -1
  96. package/dist/render/sky.js +9 -0
  97. package/dist/render/sky.js.map +1 -1
  98. package/dist/scene/basesprite.js +1 -5
  99. package/dist/scene/basesprite.js.map +1 -1
  100. package/dist/scene/batchgroup.js +18 -8
  101. package/dist/scene/batchgroup.js.map +1 -1
  102. package/dist/scene/environment.js +16 -2
  103. package/dist/scene/environment.js.map +1 -1
  104. package/dist/scene/mesh.js +164 -102
  105. package/dist/scene/mesh.js.map +1 -1
  106. package/dist/scene/meshdrawable.js +96 -0
  107. package/dist/scene/meshdrawable.js.map +1 -0
  108. package/dist/scene/msdftext.js +341 -0
  109. package/dist/scene/msdftext.js.map +1 -0
  110. package/dist/scene/msdftextsprite.js +349 -0
  111. package/dist/scene/msdftextsprite.js.map +1 -0
  112. package/dist/scene/particlesys.js +1 -0
  113. package/dist/scene/particlesys.js.map +1 -1
  114. package/dist/scene/scene.js +2 -0
  115. package/dist/scene/scene.js.map +1 -1
  116. package/dist/scene/scene_node.js +437 -230
  117. package/dist/scene/scene_node.js.map +1 -1
  118. package/dist/scene/sdftextsprite.js +322 -0
  119. package/dist/scene/sdftextsprite.js.map +1 -0
  120. package/dist/scene/terrain-cm/grass.js +1 -0
  121. package/dist/scene/terrain-cm/grass.js.map +1 -1
  122. package/dist/scene/terrain-cm/grassmaterial.js +1 -0
  123. package/dist/scene/terrain-cm/grassmaterial.js.map +1 -1
  124. package/dist/scene/terrain-cm/terrain-cm.js +1 -0
  125. package/dist/scene/terrain-cm/terrain-cm.js.map +1 -1
  126. package/dist/scene/textsprite.js +120 -0
  127. package/dist/scene/textsprite.js.map +1 -0
  128. package/dist/scene/water.js +1 -0
  129. package/dist/scene/water.js.map +1 -1
  130. package/dist/shadow/shader.js +1 -0
  131. package/dist/shadow/shader.js.map +1 -1
  132. package/dist/text/font/font_asset.js +884 -0
  133. package/dist/text/font/font_asset.js.map +1 -0
  134. package/dist/text/font/sfnt_reader.js +43 -0
  135. package/dist/text/font/sfnt_reader.js.map +1 -0
  136. package/dist/text/msdf/generator.js +417 -0
  137. package/dist/text/msdf/generator.js.map +1 -0
  138. package/dist/text/msdf/shape.js +146 -0
  139. package/dist/text/msdf/shape.js.map +1 -0
  140. package/dist/text/runtime/msdf_glyph_atlas.js +146 -0
  141. package/dist/text/runtime/msdf_glyph_atlas.js.map +1 -0
  142. package/dist/text/runtime/msdf_text_atlas_manager.js +50 -0
  143. package/dist/text/runtime/msdf_text_atlas_manager.js.map +1 -0
  144. package/dist/text/runtime/text_layout.js +96 -0
  145. package/dist/text/runtime/text_layout.js.map +1 -0
  146. package/dist/utility/blueprint/material/inputs.js +188 -1
  147. package/dist/utility/blueprint/material/inputs.js.map +1 -1
  148. package/dist/utility/blueprint/material/ir.js +16 -1
  149. package/dist/utility/blueprint/material/ir.js.map +1 -1
  150. package/dist/utility/blueprint/material/pbr.js +32 -0
  151. package/dist/utility/blueprint/material/pbr.js.map +1 -1
  152. package/dist/utility/bounding_volume.js.map +1 -1
  153. package/dist/utility/pmrem.js +34 -8
  154. package/dist/utility/pmrem.js.map +1 -1
  155. package/dist/utility/serialization/manager.js +340 -283
  156. package/dist/utility/serialization/manager.js.map +1 -1
  157. package/dist/utility/serialization/scene/animation.js +702 -7
  158. package/dist/utility/serialization/scene/animation.js.map +1 -1
  159. package/dist/utility/serialization/scene/batch.js +2 -0
  160. package/dist/utility/serialization/scene/batch.js.map +1 -1
  161. package/dist/utility/serialization/scene/camera.js +2 -0
  162. package/dist/utility/serialization/scene/camera.js.map +1 -1
  163. package/dist/utility/serialization/scene/light.js +2 -0
  164. package/dist/utility/serialization/scene/light.js.map +1 -1
  165. package/dist/utility/serialization/scene/material.js +1 -0
  166. package/dist/utility/serialization/scene/material.js.map +1 -1
  167. package/dist/utility/serialization/scene/mesh.js +66 -4
  168. package/dist/utility/serialization/scene/mesh.js.map +1 -1
  169. package/dist/utility/serialization/scene/node.js +189 -8
  170. package/dist/utility/serialization/scene/node.js.map +1 -1
  171. package/dist/utility/serialization/scene/particle.js +2 -0
  172. package/dist/utility/serialization/scene/particle.js.map +1 -1
  173. package/dist/utility/serialization/scene/primitive.js +1 -0
  174. package/dist/utility/serialization/scene/primitive.js.map +1 -1
  175. package/dist/utility/serialization/scene/sprite.js +2 -0
  176. package/dist/utility/serialization/scene/sprite.js.map +1 -1
  177. package/dist/utility/serialization/scene/terrain.js +2 -0
  178. package/dist/utility/serialization/scene/terrain.js.map +1 -1
  179. package/dist/utility/serialization/scene/text.js +516 -0
  180. package/dist/utility/serialization/scene/text.js.map +1 -0
  181. package/dist/utility/serialization/scene/water.js +2 -0
  182. package/dist/utility/serialization/scene/water.js.map +1 -1
  183. package/dist/utility/serialization/types.js.map +1 -1
  184. package/dist/utility/textures/sheenlut.js +138 -0
  185. package/dist/utility/textures/sheenlut.js.map +1 -0
  186. package/dist/values.js +48 -48
  187. package/dist/values.js.map +1 -1
  188. package/package.json +3 -3
@@ -1,5 +1,4 @@
1
- import { DWeakRef, ASSERT, base64ToUint8Array, Vector3, DRef, Vector4, VFSError, isPowerOf2, nextPowerOf2 } from '@zephyr3d/base';
2
- import { GLTFLoader } from './loaders/gltf/gltf_loader.js';
1
+ import { DWeakRef, DRef, ASSERT, base64ToUint8Array, Vector3, Vector4, guessMimeType, isPowerOf2, nextPowerOf2 } from '@zephyr3d/base';
3
2
  import { WebImageLoader } from './loaders/image/webimage_loader.js';
4
3
  import { DDSLoader } from './loaders/dds/dds_loader.js';
5
4
  import { HDRLoader } from './loaders/hdr/hdr.js';
@@ -24,6 +23,7 @@ import '../material/pbrsg.js';
24
23
  import { PBRBluePrintMaterial } from '../material/pbrblueprint.js';
25
24
  import '../material/sprite.js';
26
25
  import '../material/sprite_std.js';
26
+ import '../material/msdf_text.js';
27
27
  import { SpriteBlueprintMaterial } from '../material/spriteblueprint.js';
28
28
  import { BoundingBox } from '../utility/bounding_volume.js';
29
29
  import { MaterialBlueprintIR } from '../utility/blueprint/material/ir.js';
@@ -32,6 +32,7 @@ import '../render/rendergraph/forward_plus_builder.js';
32
32
  import '../render/sky.js';
33
33
  import '../render/clipmap.js';
34
34
  import '../shaders/atmosphere.js';
35
+ import { FontAsset } from '../text/font/font_asset.js';
35
36
 
36
37
  /**
37
38
  * Centralized asset manager for loading and caching resources.
@@ -58,12 +59,11 @@ import '../shaders/atmosphere.js';
58
59
  new HDRLoader(),
59
60
  new TGALoader()
60
61
  ];
61
- /** @internal */ static _modelLoaders = [
62
- new GLTFLoader()
63
- ];
62
+ /** @internal */ _modelLoaders = {};
64
63
  /** @internal */ _textures;
65
64
  /** @internal */ _models;
66
65
  /** @internal */ _binaryDatas;
66
+ /** @internal */ _fontAssets;
67
67
  /** @internal */ _textDatas;
68
68
  /** @internal */ _bluePrints;
69
69
  /** @internal */ _materials;
@@ -82,6 +82,7 @@ import '../shaders/atmosphere.js';
82
82
  this._skeletons = {};
83
83
  this._bluePrints = {};
84
84
  this._binaryDatas = {};
85
+ this._fontAssets = {};
85
86
  this._textDatas = {};
86
87
  this._jsonDatas = {};
87
88
  }
@@ -133,11 +134,26 @@ import '../shaders/atmosphere.js';
133
134
  }
134
135
  }
135
136
  this._skeletons = {};
137
+ this._fontAssets = {};
136
138
  this._binaryDatas = {};
137
139
  this._textDatas = {};
138
140
  this._jsonDatas = {};
139
141
  }
140
142
  /**
143
+ * Removes a cached font asset entry by URL.
144
+ *
145
+ * This only evicts the cache entry. It does not dispose any external references to the FontAsset.
146
+ *
147
+ * @param url - Resource URL or VFS path.
148
+ * @returns `true` if a cache entry existed and was removed.
149
+ */ releaseFontAsset(url) {
150
+ if (url in this._fontAssets) {
151
+ delete this._fontAssets[url];
152
+ return true;
153
+ }
154
+ return false;
155
+ }
156
+ /**
141
157
  * Register a texture loader (highest priority first).
142
158
  *
143
159
  * Note: This is a static registry shared by all AssetManager instances.
@@ -150,13 +166,9 @@ import '../shaders/atmosphere.js';
150
166
  }
151
167
  /**
152
168
  * Register a model loader (highest priority first).
153
- *
154
- * Note: This is a static registry shared by all AssetManager instances.
155
- *
156
- * @param loader - A concrete model loader implementation.
157
- */ static addModelLoader(loader) {
169
+ */ setModelLoader(mimeType, loader) {
158
170
  if (loader) {
159
- this._modelLoaders.unshift(loader);
171
+ this._modelLoaders[mimeType] = loader;
160
172
  }
161
173
  }
162
174
  /**
@@ -170,11 +182,11 @@ import '../shaders/atmosphere.js';
170
182
  * @param postProcess - Optional transformation applied to the loaded text.
171
183
  * @param httpRequest - Optional HttpRequest for custom URL resolution/headers.
172
184
  * @returns A promise that resolves to the loaded (and optionally processed) text.
173
- */ async fetchTextData(url, postProcess, httpRequest, VFSs) {
185
+ */ async fetchTextData(url, postProcess, httpRequest, options) {
174
186
  const hash = httpRequest?.urlResolver?.(url) ?? url;
175
187
  let P = this._textDatas[hash];
176
188
  if (!P) {
177
- P = this.loadTextData(url, postProcess, VFSs);
189
+ P = this.loadTextData(url, postProcess, options?.overrideVFS);
178
190
  this._textDatas[hash] = P;
179
191
  }
180
192
  return P;
@@ -189,11 +201,11 @@ import '../shaders/atmosphere.js';
189
201
  * @param postProcess - Optional transformation applied to the parsed JSON object.
190
202
  * @param httpRequest - Optional HttpRequest for custom URL resolution/headers.
191
203
  * @returns A promise that resolves to the loaded (and optionally processed) JSON value.
192
- */ async fetchJsonData(url, postProcess, httpRequest, VFSs) {
204
+ */ async fetchJsonData(url, postProcess, httpRequest, options) {
193
205
  const hash = httpRequest?.urlResolver?.(url) ?? url;
194
206
  let P = this._jsonDatas[hash];
195
207
  if (!P) {
196
- P = this.loadJsonData(url, postProcess, VFSs);
208
+ P = this.loadJsonData(url, postProcess, options?.overrideVFS);
197
209
  this._jsonDatas[hash] = P;
198
210
  }
199
211
  return P;
@@ -207,20 +219,53 @@ import '../shaders/atmosphere.js';
207
219
  * @param postProcess - Optional transformation applied to the loaded ArrayBuffer.
208
220
  * @param httpRequest - Optional HttpRequest for custom URL resolution/headers.
209
221
  * @returns A promise that resolves to the loaded (and optionally processed) ArrayBuffer.
210
- */ async fetchBinaryData(url, postProcess, httpRequest, VFSs) {
222
+ */ async fetchBinaryData(url, postProcess, httpRequest, options) {
211
223
  const hash = httpRequest?.urlResolver?.(url) ?? url;
212
224
  let P = this._binaryDatas[hash];
213
225
  if (!P) {
214
- P = this.loadBinaryData(url, postProcess, VFSs);
226
+ P = this.loadBinaryData(url, postProcess, options?.overrideVFS);
215
227
  this._binaryDatas[hash] = P;
216
228
  }
217
229
  return P;
218
230
  }
219
- async fetchBluePrint(url, VFSs) {
231
+ /**
232
+ * Fetch a font asset via VFS.
233
+ *
234
+ * - Cached per URL only.
235
+ * - `options` are applied only when the font is first loaded for that URL.
236
+ * - If the URL is already cached, later calls ignore `options` and reuse the cached FontAsset.
237
+ *
238
+ * @param url - Resource URL or VFS path.
239
+ * @param options - Optional MSDF atlas settings bound to the loaded FontAsset.
240
+ * @returns A promise that resolves to the loaded (and optionally processed) font asset.
241
+ */ async fetchFontAsset(url, options) {
242
+ const hash = url;
243
+ let P = this._fontAssets[hash];
244
+ if (!P) {
245
+ P = new Promise((resolve, reject)=>{
246
+ this.loadBinaryData(url, null, options?.overrideVFS).then((data)=>{
247
+ resolve(data ? FontAsset.fromBuffer(data, options) : null);
248
+ }).catch((err)=>{
249
+ reject(new Error(`Failed to load font asset from ${url}: ${err instanceof Error ? err.message : err}`));
250
+ });
251
+ });
252
+ this._fontAssets[hash] = P;
253
+ }
254
+ return P;
255
+ }
256
+ /**
257
+ * Fetch a font asset via VFS if already cached.
258
+ * @param url - Resource URL or VFS path.
259
+ * @returns The cached FontAsset if it exists and is loaded, or null if not cached or still loading.
260
+ */ getFontAsset(url) {
261
+ const hash = url;
262
+ return this._fontAssets[hash] instanceof Promise ? null : this._fontAssets[hash] ?? null;
263
+ }
264
+ async fetchBluePrint(url, options) {
220
265
  const hash = url;
221
266
  let P = this._bluePrints[hash];
222
267
  if (!P) {
223
- P = this.loadBluePrint(url, VFSs);
268
+ P = this.loadBluePrint(url, options?.overrideVFS);
224
269
  this._bluePrints[hash] = P;
225
270
  }
226
271
  return P;
@@ -231,13 +276,13 @@ import '../shaders/atmosphere.js';
231
276
  * @typeParam T - Expected concrete material type.
232
277
  * @param url - Resource URL or VFS path.
233
278
  * @returns A promise that resolves to the loaded material.
234
- */ async fetchMaterial(url, VFSs) {
279
+ */ async fetchMaterial(url, options) {
235
280
  const hash = url;
236
281
  let P = this._materials[hash];
237
282
  if (P instanceof DWeakRef && P.get() && !P.get().disposed) {
238
283
  return P.get();
239
284
  } else if (!P || P instanceof DWeakRef) {
240
- P = this.loadMaterial(url, false, VFSs);
285
+ P = this.loadMaterial(url, false, options?.overrideVFS);
241
286
  this._materials[hash] = P;
242
287
  }
243
288
  const material = await P;
@@ -252,13 +297,13 @@ import '../shaders/atmosphere.js';
252
297
  * @typeParam T - Expected concrete primitive type.
253
298
  * @param url - Resource URL or VFS path.
254
299
  * @returns A promise that resolves to the loaded primitive.
255
- */ async fetchPrimitive(url, VFSs) {
300
+ */ async fetchPrimitive(url, options) {
256
301
  const hash = url;
257
302
  let P = this._primitives[hash];
258
303
  if (P instanceof DWeakRef && P.get() && !P.get().disposed) {
259
304
  return P.get();
260
305
  } else if (!P || P instanceof DWeakRef) {
261
- P = this.loadPrimitive(url, VFSs);
306
+ P = this.loadPrimitive(url, options?.overrideVFS);
262
307
  this._primitives[hash] = P;
263
308
  }
264
309
  const primitive = await P;
@@ -280,16 +325,16 @@ import '../shaders/atmosphere.js';
280
325
  * @param options - Texture fetching options (color space, sampler, target texture).
281
326
  * @param httpRequest - Optional HttpRequest (not used for binary read but may supply URL resolver for hashing).
282
327
  * @returns A promise that resolves to the loaded texture.
283
- */ async fetchTexture(url, options, VFSs) {
328
+ */ async fetchTexture(url, options) {
284
329
  if (options?.texture) {
285
- return this.loadTexture(url, options.mimeType ?? null, !options.linearColorSpace, options.samplerOptions, options.texture, VFSs);
330
+ return this.loadTexture(url, options.mimeType ?? null, !options.linearColorSpace, options.samplerOptions, options.texture, options.overrideVFS);
286
331
  } else {
287
332
  const hash = this.getHash('2d', url, options);
288
333
  let P = this._textures[hash];
289
334
  if (P instanceof DWeakRef && P.get() && !P.get().disposed) {
290
335
  return P.get();
291
336
  } else if (!P || P instanceof DWeakRef) {
292
- P = this.loadTexture(url, options?.mimeType ?? null, !options?.linearColorSpace, options?.samplerOptions, null, VFSs);
337
+ P = this.loadTexture(url, options?.mimeType ?? null, !options?.linearColorSpace, options?.samplerOptions, null, options?.overrideVFS);
293
338
  this._textures[hash] = P;
294
339
  }
295
340
  const tex = await P;
@@ -309,13 +354,13 @@ import '../shaders/atmosphere.js';
309
354
  * @param options - Model loader options (MIME override, Draco, instancing hint, post-process).
310
355
  * @returns A promise that resolves to the SharedModel.
311
356
  * @internal
312
- */ async fetchModelData(url, options, VFSs) {
357
+ */ async fetchModelData(url, options) {
313
358
  const hash = url;
314
359
  let P = this._models[hash];
315
360
  if (P instanceof DWeakRef && P.get() && !P.get().disposed) {
316
361
  return P.get();
317
362
  } else if (!P || P instanceof DWeakRef) {
318
- P = this.loadModel(url, options, VFSs);
363
+ P = this.loadModel(url, options, options?.overrideVFS);
319
364
  this._models[hash] = P;
320
365
  }
321
366
  const sharedModel = await P;
@@ -335,13 +380,21 @@ import '../shaders/atmosphere.js';
335
380
  * @param options - Model loader options and instancing hint.
336
381
  * @param httpRequest - Optional HttpRequest (unused for binary read; present for API symmetry).
337
382
  * @returns A promise with the created node group and animation set info.
338
- */ async fetchModel(scene, url, options, VFSs) {
339
- const sharedModel = await this.fetchModelData(url, options, VFSs);
340
- const node = sharedModel.createSceneNode(scene, !!options?.enableInstancing);
341
- return {
342
- group: node,
343
- animationSet: node.animationSet
344
- };
383
+ *
384
+ * @internal
385
+ */ async fetchModel(scene, url, options) {
386
+ const sharedModel = new DRef();
387
+ try {
388
+ sharedModel.set(await this.fetchModelData(url, options));
389
+ const node = await sharedModel.get().createSceneNode(getEngine().resourceManager, scene, options?.enableInstancing ?? false, options?.loadMeshes ?? true, options?.loadSkeletons ?? true, options?.loadAnimations ?? true, options?.loadJointDynamics ?? true, options?.overrideVFS ?? getEngine().resourceManager.VFS);
390
+ node.sharedModel = sharedModel.get();
391
+ node.animationSet.resetSkeletonModifiers();
392
+ return node;
393
+ } catch (err) {
394
+ console.error(`Load model failed: ${url}: ${err}`);
395
+ } finally{
396
+ sharedModel.dispose();
397
+ }
345
398
  }
346
399
  /**
347
400
  * Load a text resource via VFS and optionally post-process it.
@@ -352,10 +405,10 @@ import '../shaders/atmosphere.js';
352
405
  * @param postProcess - Optional transformation applied to the text.
353
406
  * @returns A promise that resolves to the loaded (and optionally processed) text.
354
407
  * @internal
355
- */ async loadTextData(url, postProcess, VFSs) {
356
- let text = await this.readFileFromVFSs(url, {
408
+ */ async loadTextData(url, postProcess, vfs) {
409
+ let text = await this.readFileFromVFS(url, {
357
410
  encoding: 'utf8'
358
- }, VFSs);
411
+ }, vfs);
359
412
  if (postProcess) {
360
413
  try {
361
414
  text = postProcess(text);
@@ -374,10 +427,10 @@ import '../shaders/atmosphere.js';
374
427
  * @param postProcess - Optional transformation applied to the parsed JSON.
375
428
  * @returns A promise that resolves to the loaded (and optionally processed) JSON.
376
429
  * @internal
377
- */ async loadJsonData(url, postProcess, VFSs) {
378
- let json = JSON.parse(await this.readFileFromVFSs(url, {
430
+ */ async loadJsonData(url, postProcess, vfs) {
431
+ let json = JSON.parse(await this.readFileFromVFS(url, {
379
432
  encoding: 'utf8'
380
- }, VFSs));
433
+ }, vfs));
381
434
  if (postProcess) {
382
435
  try {
383
436
  json = postProcess(json);
@@ -396,11 +449,11 @@ import '../shaders/atmosphere.js';
396
449
  * @param postProcess - Optional transformation applied to the ArrayBuffer.
397
450
  * @returns A promise that resolves to the loaded (and optionally processed) ArrayBuffer.
398
451
  * @internal
399
- */ async loadBinaryData(url, postProcess, VFSs) {
452
+ */ async loadBinaryData(url, postProcess, vfs) {
400
453
  try {
401
- let data = await this.readFileFromVFSs(url, {
454
+ let data = await this.readFileFromVFS(url, {
402
455
  encoding: 'binary'
403
- }, VFSs);
456
+ }, vfs);
404
457
  if (postProcess) {
405
458
  data = postProcess(data);
406
459
  }
@@ -410,11 +463,11 @@ import '../shaders/atmosphere.js';
410
463
  return null;
411
464
  }
412
465
  }
413
- async loadPrimitive(url, VFSs) {
466
+ async loadPrimitive(url, vfs) {
414
467
  try {
415
- const data = await this.readFileFromVFSs(url, {
468
+ const data = await this.readFileFromVFS(url, {
416
469
  encoding: 'utf8'
417
- }, VFSs);
470
+ }, vfs);
418
471
  const content = JSON.parse(data);
419
472
  ASSERT(content.type === 'Primitive' || content.type === 'Default', `Unsupported primitive type: ${content.type}`);
420
473
  if (content.type === 'Primitive') {
@@ -480,20 +533,22 @@ import '../shaders/atmosphere.js';
480
533
  }
481
534
  }
482
535
  }
483
- async loadBluePrintMaterialData(url, reload, VFSs) {
536
+ async loadBluePrintMaterialData(url, reload, vfs) {
484
537
  try {
485
538
  let irData;
486
539
  if (typeof url === 'string') {
487
- const data = await this.readFileFromVFSs(url, {
540
+ const data = await this.readFileFromVFS(url, {
488
541
  encoding: 'utf8'
489
- }, VFSs);
542
+ }, vfs);
490
543
  const content = JSON.parse(data);
491
544
  ASSERT(content.type === 'PBRBluePrintMaterial' || content.type === 'SpriteBluePrintMaterial', `Unsupported material type: ${content.type}`);
492
545
  irData = content.data;
493
546
  } else {
494
547
  irData = url;
495
548
  }
496
- const ir = reload ? await this.loadBluePrint(irData.IR, VFSs) : await this.fetchBluePrint(irData.IR, VFSs);
549
+ const ir = reload ? await this.loadBluePrint(irData.IR, vfs) : await this.fetchBluePrint(irData.IR, vfs ? {
550
+ overrideVFS: vfs
551
+ } : undefined);
497
552
  const uniformValues = irData.uniformValues.map((v)=>({
498
553
  ...v,
499
554
  finalValue: v.value.length === 1 ? v.value[0] : new Float32Array(v.value)
@@ -502,8 +557,9 @@ import '../shaders/atmosphere.js';
502
557
  const textures = irData.uniformTextures;
503
558
  for (const v of textures){
504
559
  const tex = await this.fetchTexture(v.texture, {
505
- linearColorSpace: !v.sRGB
506
- }, VFSs);
560
+ linearColorSpace: !v.sRGB,
561
+ overrideVFS: vfs
562
+ });
507
563
  uniformTextures.push({
508
564
  ...v,
509
565
  finalTexture: new DRef(tex),
@@ -536,19 +592,19 @@ import '../shaders/atmosphere.js';
536
592
  * @param url - Resource URL or VFS path.
537
593
  * @returns A promise that resolves to the loaded material.
538
594
  * @internal
539
- */ async loadMaterial(url, reload, VFSs) {
595
+ */ async loadMaterial(url, reload, vfs) {
540
596
  try {
541
- const data = await this.readFileFromVFSs(url, {
597
+ const data = await this.readFileFromVFS(url, {
542
598
  encoding: 'utf8'
543
- }, VFSs);
599
+ }, vfs);
544
600
  const content = JSON.parse(data);
545
601
  ASSERT(content.type === 'PBRBluePrintMaterial' || content.type === 'SpriteBluePrintMaterial' || content.type === 'Default', `Unsupported material type: ${content.type}`);
546
602
  let mat;
547
603
  if (content.type === 'PBRBluePrintMaterial') {
548
- const data = await this.loadBluePrintMaterialData(content.data, reload, VFSs);
604
+ const data = await this.loadBluePrintMaterialData(content.data, reload, vfs);
549
605
  mat = new PBRBluePrintMaterial(data.irFragment, data.irVertex, data.uniformValues, data.uniformTextures);
550
606
  } else if (content.type === 'SpriteBluePrintMaterial') {
551
- const data = await this.loadBluePrintMaterialData(content.data, reload, VFSs);
607
+ const data = await this.loadBluePrintMaterialData(content.data, reload, vfs);
552
608
  mat = new SpriteBlueprintMaterial(data.irFragment, data.uniformValues, data.uniformTextures);
553
609
  } else {
554
610
  const obj = await this._resourceManager.deserializeObject(null, content.data);
@@ -691,11 +747,11 @@ import '../shaders/atmosphere.js';
691
747
  invalidateBluePrint(path) {
692
748
  delete this._bluePrints[path];
693
749
  }
694
- async loadBluePrint(path, VFSs) {
750
+ async loadBluePrint(path, vfs) {
695
751
  try {
696
- const content = await this.readFileFromVFSs(path, {
752
+ const content = await this.readFileFromVFS(path, {
697
753
  encoding: 'utf8'
698
- }, VFSs);
754
+ }, vfs);
699
755
  const bp = JSON.parse(content);
700
756
  ASSERT(bp.type === 'PBRMaterial' || bp.type === 'SpriteMaterial' || bp.type === 'MaterialFunction', `Unsupported blueprint type: ${bp.type}`);
701
757
  const states = bp.state;
@@ -758,10 +814,10 @@ import '../shaders/atmosphere.js';
758
814
  * @param texture - Optional destination texture to populate.
759
815
  * @returns A promise that resolves to the created or populated texture.
760
816
  * @internal
761
- */ async loadTexture(url, mimeType, srgb, samplerOptions, texture, VFSs) {
762
- const data = await this.readFileFromVFSs(url, {
817
+ */ async loadTexture(url, mimeType, srgb, samplerOptions, texture, vfs) {
818
+ const data = await this.readFileFromVFS(url, {
763
819
  encoding: 'binary'
764
- }, VFSs);
820
+ }, vfs);
765
821
  mimeType = mimeType ?? this.vfs.guessMIMEType(url);
766
822
  for (const loader of AssetManager._textureLoaders){
767
823
  if (!loader.supportMIMEType(mimeType)) {
@@ -848,22 +904,11 @@ import '../shaders/atmosphere.js';
848
904
  * @param options - Model load options (MIME override, Draco module, post-process hook).
849
905
  * @returns A promise that resolves to the loaded SharedModel.
850
906
  * @internal
851
- */ async loadModel(url, options, VFSs) {
852
- const arrayBuffer = await this.readFileFromVFSs(url, {
853
- encoding: 'binary'
854
- }, VFSs);
855
- const mimeType = options?.mimeType || this.vfs.guessMIMEType(url);
856
- const data = new Blob([
857
- arrayBuffer
858
- ], {
859
- type: mimeType
860
- });
861
- const filename = this.vfs.basename(url);
862
- for (const loader of AssetManager._modelLoaders){
863
- if (!loader.supportMIMEType(mimeType)) {
864
- continue;
865
- }
866
- let model = await loader.load(this, url, options?.mimeType || data.type, data, options?.dracoDecoderModule, VFSs);
907
+ */ async loadModel(url, options, vfs) {
908
+ const mimeType = options?.mimeType || guessMimeType(url);
909
+ const importer = this._modelLoaders[mimeType];
910
+ if (importer) {
911
+ let model = await importer.loadModel(url, vfs);
867
912
  if (!model) {
868
913
  throw new Error(`Load asset failed: ${url}`);
869
914
  }
@@ -874,7 +919,6 @@ import '../shaders/atmosphere.js';
874
919
  throw new Error(`Model loader post process failed: ${err}`);
875
920
  }
876
921
  }
877
- model.name = filename;
878
922
  return model;
879
923
  }
880
924
  throw new Error(`Can not find loader for asset ${url}`);
@@ -939,39 +983,26 @@ import '../shaders/atmosphere.js';
939
983
  return `${type}:${url}:${!options?.linearColorSpace}`;
940
984
  }
941
985
  /**
942
- * Try reading from file from a list of VFSs
986
+ * Try reading from file from a VFS
943
987
  *
944
988
  * @param path - File path
945
989
  * @param options - Read options
946
- * @param vfsList - VFS list
990
+ * @param vfs - VFS to read from
947
991
  * @returns File content
948
992
  *
949
993
  * @internal
950
- */ async readFileFromVFSs(path, options, vfsList) {
951
- vfsList = vfsList ?? [
952
- this.vfs
953
- ];
954
- for (const vfs of vfsList){
955
- try {
956
- return await vfs.readFile(path, options);
957
- } catch {}
958
- }
959
- throw new VFSError(`File does not exist: ${path}`, 'ENOENT', path);
994
+ */ async readFileFromVFS(path, options, vfs) {
995
+ return await (vfs ?? this.vfs).readFile(path, options);
960
996
  }
961
997
  /**
962
- * Write file to a list of VFSs
998
+ * Write file to a VFS
963
999
  *
964
1000
  * @param path - File path
965
1001
  * @param data - Data to write
966
1002
  * @param options - Write options
967
- * @param vfsList - Which VFSs will be written to
968
- */ async writeFileToVFSs(path, data, options, vfsList) {
969
- vfsList = vfsList ?? [
970
- this.vfs
971
- ];
972
- for (const vfs of vfsList){
973
- await vfs.writeFile(path, data, options);
974
- }
1003
+ * @param vfs - VFS to write to
1004
+ */ async writeFileToVFSs(path, data, options, vfs) {
1005
+ await (vfs ?? this.vfs).writeFile(path, data, options);
975
1006
  }
976
1007
  }
977
1008