@zephyr3d/scene 0.9.25 → 0.9.27

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 (299) hide show
  1. package/dist/animation/animationset.js +92 -30
  2. package/dist/animation/animationset.js.map +1 -1
  3. package/dist/animation/hair/gpu_hair_simulation.js +1090 -0
  4. package/dist/animation/hair/gpu_hair_simulation.js.map +1 -0
  5. package/dist/animation/joint_dynamics/controller.js +7 -5
  6. package/dist/animation/joint_dynamics/controller.js.map +1 -1
  7. package/dist/animation/joint_dynamics/convex_collider.js +320 -0
  8. package/dist/animation/joint_dynamics/convex_collider.js.map +1 -0
  9. package/dist/animation/joint_dynamics/joint_dynamics_system.js +81 -5
  10. package/dist/animation/joint_dynamics/joint_dynamics_system.js.map +1 -1
  11. package/dist/animation/morphtrack.js +3 -4
  12. package/dist/animation/morphtrack.js.map +1 -1
  13. package/dist/animation/skeleton.js +79 -25
  14. package/dist/animation/skeleton.js.map +1 -1
  15. package/dist/animation/spring/multi_chain_spring_system.js +577 -179
  16. package/dist/animation/spring/multi_chain_spring_system.js.map +1 -1
  17. package/dist/animation/spring/spring_collider.js +4 -3
  18. package/dist/animation/spring/spring_collider.js.map +1 -1
  19. package/dist/animation/spring/spring_node_pose_tracker.js +40 -0
  20. package/dist/animation/spring/spring_node_pose_tracker.js.map +1 -0
  21. package/dist/animation/spring/spring_particle.js +2 -0
  22. package/dist/animation/spring/spring_particle.js.map +1 -1
  23. package/dist/animation/spring/spring_system.js +11 -3
  24. package/dist/animation/spring/spring_system.js.map +1 -1
  25. package/dist/animation/spring_modifier.js +10 -0
  26. package/dist/animation/spring_modifier.js.map +1 -1
  27. package/dist/app/engine.js +53 -43
  28. package/dist/app/engine.js.map +1 -1
  29. package/dist/app/scriptingsystem.js +119 -6
  30. package/dist/app/scriptingsystem.js.map +1 -1
  31. package/dist/app/scriptregistry.js +3 -2
  32. package/dist/app/scriptregistry.js.map +1 -1
  33. package/dist/asset/assetmanager.js +284 -67
  34. package/dist/asset/assetmanager.js.map +1 -1
  35. package/dist/asset/loaders/zhair/zhair_format.js +288 -0
  36. package/dist/asset/loaders/zhair/zhair_format.js.map +1 -0
  37. package/dist/asset/loaders/zhair/zhair_loader.js +221 -0
  38. package/dist/asset/loaders/zhair/zhair_loader.js.map +1 -0
  39. package/dist/asset/model.js +184 -69
  40. package/dist/asset/model.js.map +1 -1
  41. package/dist/camera/camera.js +268 -44
  42. package/dist/camera/camera.js.map +1 -1
  43. package/dist/camera/perspectivecamera.js +3 -1
  44. package/dist/camera/perspectivecamera.js.map +1 -1
  45. package/dist/index.d.ts +9044 -5139
  46. package/dist/index.js +17 -6
  47. package/dist/index.js.map +1 -1
  48. package/dist/material/eye.js +932 -0
  49. package/dist/material/eye.js.map +1 -0
  50. package/dist/material/hair.js +917 -0
  51. package/dist/material/hair.js.map +1 -0
  52. package/dist/material/hairstrand.js +842 -0
  53. package/dist/material/hairstrand.js.map +1 -0
  54. package/dist/material/hairstrand_data.js +162 -0
  55. package/dist/material/hairstrand_data.js.map +1 -0
  56. package/dist/material/lambert.js +5 -2
  57. package/dist/material/lambert.js.map +1 -1
  58. package/dist/material/material.js +48 -10
  59. package/dist/material/material.js.map +1 -1
  60. package/dist/material/meshmaterial.js +120 -19
  61. package/dist/material/meshmaterial.js.map +1 -1
  62. package/dist/material/mixins/albedocolor.js +1 -1
  63. package/dist/material/mixins/albedocolor.js.map +1 -1
  64. package/dist/material/mixins/lightmodel/blinnphong.js +5 -3
  65. package/dist/material/mixins/lightmodel/blinnphong.js.map +1 -1
  66. package/dist/material/mixins/lightmodel/lambert.js +5 -4
  67. package/dist/material/mixins/lightmodel/lambert.js.map +1 -1
  68. package/dist/material/mixins/lightmodel/pbrblueprintmixin.js +3 -3
  69. package/dist/material/mixins/lightmodel/pbrblueprintmixin.js.map +1 -1
  70. package/dist/material/mixins/lightmodel/pbrmetallicroughness.js +5 -5
  71. package/dist/material/mixins/lightmodel/pbrmetallicroughness.js.map +1 -1
  72. package/dist/material/mixins/lightmodel/pbrspecularglossness.js +3 -3
  73. package/dist/material/mixins/lightmodel/pbrspecularglossness.js.map +1 -1
  74. package/dist/material/mixins/lit.js +106 -19
  75. package/dist/material/mixins/lit.js.map +1 -1
  76. package/dist/material/mixins/pbr/common.js +4 -4
  77. package/dist/material/mixins/pbr/common.js.map +1 -1
  78. package/dist/material/mixins/texture.js +48 -11
  79. package/dist/material/mixins/texture.js.map +1 -1
  80. package/dist/material/mtoon.js +6 -5
  81. package/dist/material/mtoon.js.map +1 -1
  82. package/dist/material/pbrblueprint.js +146 -28
  83. package/dist/material/pbrblueprint.js.map +1 -1
  84. package/dist/material/pbrblueprint_instance.js +501 -0
  85. package/dist/material/pbrblueprint_instance.js.map +1 -0
  86. package/dist/material/pbrmr.js +15 -16
  87. package/dist/material/pbrmr.js.map +1 -1
  88. package/dist/material/shader/helper.js +734 -23
  89. package/dist/material/shader/helper.js.map +1 -1
  90. package/dist/material/shader/terrain_blend.js +55 -0
  91. package/dist/material/shader/terrain_blend.js.map +1 -0
  92. package/dist/material/skin.js +192 -32
  93. package/dist/material/skin.js.map +1 -1
  94. package/dist/material/spriteblueprint.js +16 -1
  95. package/dist/material/spriteblueprint.js.map +1 -1
  96. package/dist/material/subsurfaceprofile.js +86 -23
  97. package/dist/material/subsurfaceprofile.js.map +1 -1
  98. package/dist/material/water.js +2059 -72
  99. package/dist/material/water.js.map +1 -1
  100. package/dist/posteffect/bloom.js +167 -7
  101. package/dist/posteffect/bloom.js.map +1 -1
  102. package/dist/posteffect/coloradjust.js +2 -1
  103. package/dist/posteffect/coloradjust.js.map +1 -1
  104. package/dist/posteffect/compositor.js.map +1 -1
  105. package/dist/posteffect/fxaa.js +2 -1
  106. package/dist/posteffect/fxaa.js.map +1 -1
  107. package/dist/posteffect/grayscale.js +3 -1
  108. package/dist/posteffect/grayscale.js.map +1 -1
  109. package/dist/posteffect/motionblur.js +664 -56
  110. package/dist/posteffect/motionblur.js.map +1 -1
  111. package/dist/posteffect/posteffect.js +12 -1
  112. package/dist/posteffect/posteffect.js.map +1 -1
  113. package/dist/posteffect/sao.js +46 -44
  114. package/dist/posteffect/sao.js.map +1 -1
  115. package/dist/posteffect/skinsss.js +368 -69
  116. package/dist/posteffect/skinsss.js.map +1 -1
  117. package/dist/posteffect/ssgi.js +16 -9
  118. package/dist/posteffect/ssgi.js.map +1 -1
  119. package/dist/posteffect/ssr.js +3 -0
  120. package/dist/posteffect/ssr.js.map +1 -1
  121. package/dist/posteffect/sss.js +121 -36
  122. package/dist/posteffect/sss.js.map +1 -1
  123. package/dist/posteffect/taa.js.map +1 -1
  124. package/dist/posteffect/tonemap.js +44 -1
  125. package/dist/posteffect/tonemap.js.map +1 -1
  126. package/dist/render/abuffer_oit.js +8 -2
  127. package/dist/render/abuffer_oit.js.map +1 -1
  128. package/dist/render/clipmap.js +156 -5
  129. package/dist/render/clipmap.js.map +1 -1
  130. package/dist/render/cull_visitor.js +20 -0
  131. package/dist/render/cull_visitor.js.map +1 -1
  132. package/dist/render/depthpass.js +36 -2
  133. package/dist/render/depthpass.js.map +1 -1
  134. package/dist/render/drawable_mixin.js +26 -1
  135. package/dist/render/drawable_mixin.js.map +1 -1
  136. package/dist/render/dualdepthpeeling_oit.js +3 -2
  137. package/dist/render/dualdepthpeeling_oit.js.map +1 -1
  138. package/dist/render/envlight.js +1 -1
  139. package/dist/render/envlight.js.map +1 -1
  140. package/dist/render/fbm_wavegenerator.js +6 -3
  141. package/dist/render/fbm_wavegenerator.js.map +1 -1
  142. package/dist/render/fft_wavegenerator.js +57 -15
  143. package/dist/render/fft_wavegenerator.js.map +1 -1
  144. package/dist/render/gerstner_wavegenerator.js +130 -20
  145. package/dist/render/gerstner_wavegenerator.js.map +1 -1
  146. package/dist/render/globalbindgroup_allocator.js +3 -0
  147. package/dist/render/globalbindgroup_allocator.js.map +1 -1
  148. package/dist/render/hzb.js +80 -22
  149. package/dist/render/hzb.js.map +1 -1
  150. package/dist/render/lightpass.js +6 -3
  151. package/dist/render/lightpass.js.map +1 -1
  152. package/dist/render/oit.js +29 -0
  153. package/dist/render/oit.js.map +1 -0
  154. package/dist/render/render_queue.js +39 -0
  155. package/dist/render/render_queue.js.map +1 -1
  156. package/dist/render/renderer.js +4 -0
  157. package/dist/render/renderer.js.map +1 -1
  158. package/dist/render/rendergraph/blackboard.js +1 -0
  159. package/dist/render/rendergraph/blackboard.js.map +1 -1
  160. package/dist/render/rendergraph/device_pool_allocator.js.map +1 -1
  161. package/dist/render/rendergraph/executor.js +2 -0
  162. package/dist/render/rendergraph/executor.js.map +1 -1
  163. package/dist/render/rendergraph/forward_plus_builder.js +379 -10
  164. package/dist/render/rendergraph/forward_plus_builder.js.map +1 -1
  165. package/dist/render/rendergraph/frame_resource_requirements.js +1 -0
  166. package/dist/render/rendergraph/frame_resource_requirements.js.map +1 -1
  167. package/dist/render/rendergraph/history_resources.js +8 -1
  168. package/dist/render/rendergraph/history_resources.js.map +1 -1
  169. package/dist/render/rendergraph/rendergraph.js.map +1 -1
  170. package/dist/render/rendergraph/texture_affinity_cache.js +1 -1
  171. package/dist/render/rendergraph/texture_affinity_cache.js.map +1 -1
  172. package/dist/render/rendergraph/types.js.map +1 -1
  173. package/dist/render/shadow_mask_pass.js +73 -8
  174. package/dist/render/shadow_mask_pass.js.map +1 -1
  175. package/dist/render/sky.js +166 -126
  176. package/dist/render/sky.js.map +1 -1
  177. package/dist/render/underwater.js +624 -0
  178. package/dist/render/underwater.js.map +1 -0
  179. package/dist/render/water_caustics.js +1380 -0
  180. package/dist/render/water_caustics.js.map +1 -0
  181. package/dist/render/weightedblended_oit.js +3 -2
  182. package/dist/render/weightedblended_oit.js.map +1 -1
  183. package/dist/scene/basesprite.js +8 -0
  184. package/dist/scene/basesprite.js.map +1 -1
  185. package/dist/scene/batchgroup.js +3 -0
  186. package/dist/scene/batchgroup.js.map +1 -1
  187. package/dist/scene/environment.js +4 -1
  188. package/dist/scene/environment.js.map +1 -1
  189. package/dist/scene/hair.js +963 -0
  190. package/dist/scene/hair.js.map +1 -0
  191. package/dist/scene/light.js +5 -1
  192. package/dist/scene/light.js.map +1 -1
  193. package/dist/scene/mesh.js +481 -47
  194. package/dist/scene/mesh.js.map +1 -1
  195. package/dist/scene/meshdrawable.js +6 -0
  196. package/dist/scene/meshdrawable.js.map +1 -1
  197. package/dist/scene/msdftext.js +3 -0
  198. package/dist/scene/msdftext.js.map +1 -1
  199. package/dist/scene/msdftextsprite.js +3 -0
  200. package/dist/scene/msdftextsprite.js.map +1 -1
  201. package/dist/scene/particlesys.js +6 -0
  202. package/dist/scene/particlesys.js.map +1 -1
  203. package/dist/scene/scene.js +3 -0
  204. package/dist/scene/scene.js.map +1 -1
  205. package/dist/scene/scene_node.js +6 -1
  206. package/dist/scene/scene_node.js.map +1 -1
  207. package/dist/scene/terrain-cm/grass.js +3 -0
  208. package/dist/scene/terrain-cm/grass.js.map +1 -1
  209. package/dist/scene/terrain-cm/grassmaterial.js +3 -0
  210. package/dist/scene/terrain-cm/grassmaterial.js.map +1 -1
  211. package/dist/scene/terrain-cm/terrain-cm.js +6 -0
  212. package/dist/scene/terrain-cm/terrain-cm.js.map +1 -1
  213. package/dist/scene/terrain-cm/texture_cache.js +309 -0
  214. package/dist/scene/terrain-cm/texture_cache.js.map +1 -0
  215. package/dist/scene/water.js +546 -4
  216. package/dist/scene/water.js.map +1 -1
  217. package/dist/shaders/atmosphere.js.map +1 -1
  218. package/dist/shaders/diffusion.js +54 -0
  219. package/dist/shaders/diffusion.js.map +1 -0
  220. package/dist/shaders/shadow.js +33 -21
  221. package/dist/shaders/shadow.js.map +1 -1
  222. package/dist/shaders/temporal.js +71 -33
  223. package/dist/shaders/temporal.js.map +1 -1
  224. package/dist/shaders/water.js +22 -19
  225. package/dist/shaders/water.js.map +1 -1
  226. package/dist/shaders/water_medium.js +56 -0
  227. package/dist/shaders/water_medium.js.map +1 -0
  228. package/dist/shadow/dom.js +546 -0
  229. package/dist/shadow/dom.js.map +1 -0
  230. package/dist/shadow/pcf_opt.js +3 -5
  231. package/dist/shadow/pcf_opt.js.map +1 -1
  232. package/dist/shadow/pcf_pd.js +1 -1
  233. package/dist/shadow/pcf_pd.js.map +1 -1
  234. package/dist/shadow/receiver_bias.js +41 -0
  235. package/dist/shadow/receiver_bias.js.map +1 -0
  236. package/dist/shadow/shader.js +20 -60
  237. package/dist/shadow/shader.js.map +1 -1
  238. package/dist/shadow/shadow_impl.js +89 -0
  239. package/dist/shadow/shadow_impl.js.map +1 -1
  240. package/dist/shadow/shadow_region.js +1 -1
  241. package/dist/shadow/shadow_region.js.map +1 -1
  242. package/dist/shadow/shadowmapper.js +150 -37
  243. package/dist/shadow/shadowmapper.js.map +1 -1
  244. package/dist/shapes/shape.js +11 -2
  245. package/dist/shapes/shape.js.map +1 -1
  246. package/dist/shapes/sphere.js +50 -9
  247. package/dist/shapes/sphere.js.map +1 -1
  248. package/dist/utility/blueprint/common/constants.js +112 -2
  249. package/dist/utility/blueprint/common/constants.js.map +1 -1
  250. package/dist/utility/blueprint/material/func.js +142 -49
  251. package/dist/utility/blueprint/material/func.js.map +1 -1
  252. package/dist/utility/blueprint/material/ir.js +490 -25
  253. package/dist/utility/blueprint/material/ir.js.map +1 -1
  254. package/dist/utility/blueprint/material/texture.js +898 -4
  255. package/dist/utility/blueprint/material/texture.js.map +1 -1
  256. package/dist/utility/serialization/manager.js +434 -101
  257. package/dist/utility/serialization/manager.js.map +1 -1
  258. package/dist/utility/serialization/scene/animation.js +401 -5
  259. package/dist/utility/serialization/scene/animation.js.map +1 -1
  260. package/dist/utility/serialization/scene/batch.js +4 -0
  261. package/dist/utility/serialization/scene/batch.js.map +1 -1
  262. package/dist/utility/serialization/scene/camera.js +186 -26
  263. package/dist/utility/serialization/scene/camera.js.map +1 -1
  264. package/dist/utility/serialization/scene/common.js +26 -5
  265. package/dist/utility/serialization/scene/common.js.map +1 -1
  266. package/dist/utility/serialization/scene/hair.js +1141 -0
  267. package/dist/utility/serialization/scene/hair.js.map +1 -0
  268. package/dist/utility/serialization/scene/light.js +84 -4
  269. package/dist/utility/serialization/scene/light.js.map +1 -1
  270. package/dist/utility/serialization/scene/material.js +1867 -166
  271. package/dist/utility/serialization/scene/material.js.map +1 -1
  272. package/dist/utility/serialization/scene/mesh.js +231 -14
  273. package/dist/utility/serialization/scene/mesh.js.map +1 -1
  274. package/dist/utility/serialization/scene/node.js +141 -7
  275. package/dist/utility/serialization/scene/node.js.map +1 -1
  276. package/dist/utility/serialization/scene/particle.js +4 -0
  277. package/dist/utility/serialization/scene/particle.js.map +1 -1
  278. package/dist/utility/serialization/scene/primitive.js +18 -0
  279. package/dist/utility/serialization/scene/primitive.js.map +1 -1
  280. package/dist/utility/serialization/scene/sprite.js +4 -0
  281. package/dist/utility/serialization/scene/sprite.js.map +1 -1
  282. package/dist/utility/serialization/scene/terrain.js +4 -0
  283. package/dist/utility/serialization/scene/terrain.js.map +1 -1
  284. package/dist/utility/serialization/scene/text.js +4 -0
  285. package/dist/utility/serialization/scene/text.js.map +1 -1
  286. package/dist/utility/serialization/scene/water.js +1105 -21
  287. package/dist/utility/serialization/scene/water.js.map +1 -1
  288. package/dist/utility/serialization/types.js.map +1 -1
  289. package/dist/utility/shprojector.js +9 -8
  290. package/dist/utility/shprojector.js.map +1 -1
  291. package/dist/values.js +49 -2
  292. package/dist/values.js.map +1 -1
  293. package/package.json +3 -3
  294. package/dist/render/rendergraph/frame_graph_context.js +0 -41
  295. package/dist/render/rendergraph/frame_graph_context.js.map +0 -1
  296. package/dist/shaders/ssgi.js +0 -63
  297. package/dist/shaders/ssgi.js.map +0 -1
  298. package/dist/shaders/ssgi_reprojection.js +0 -118
  299. package/dist/shaders/ssgi_reprojection.js.map +0 -1
@@ -16,19 +16,24 @@ import '../material/unlit.js';
16
16
  import '../material/particle.js';
17
17
  import '../material/subsurfaceprofile.js';
18
18
  import '../material/skin.js';
19
+ import '../material/eye.js';
19
20
  import { Material } from '../material/material.js';
20
21
  import '../material/meshmaterial.js';
22
+ import '../material/hair.js';
23
+ import '../material/hairstrand.js';
21
24
  import '../material/grassmaterial.js';
22
25
  import '../material/terrain-cm.js';
23
26
  import '../material/pbrmr.js';
24
27
  import '../material/pbrsg.js';
25
28
  import '../material/mtoon.js';
26
29
  import { PBRBluePrintMaterial } from '../material/pbrblueprint.js';
30
+ import { PBRBluePrintMaterialInstance } from '../material/pbrblueprint_instance.js';
27
31
  import '../material/sprite.js';
28
32
  import '../material/sprite_std.js';
29
33
  import { SpriteBlueprintMaterial } from '../material/spriteblueprint.js';
30
34
  import { BoundingBox } from '../utility/bounding_volume.js';
31
35
  import { MaterialBlueprintIR } from '../utility/blueprint/material/ir.js';
36
+ import { getDefaultTexture2D } from '../utility/blueprint/material/texture.js';
32
37
  import '../render/render_queue.js';
33
38
  import '../render/rendergraph/forward_plus_builder.js';
34
39
  import '../render/lightpass.js';
@@ -76,6 +81,8 @@ import { FontAsset } from '../text/font/font_asset.js';
76
81
  /** @internal */ _primitives;
77
82
  /** @internal */ _skeletons;
78
83
  /** @internal */ _jsonDatas;
84
+ /** @internal */ _cacheKeysByPath;
85
+ /** @internal */ _cachePathByKey;
79
86
  /** @internal */ _resourceManager;
80
87
  /**
81
88
  * Creates an instance of AssetManager
@@ -91,6 +98,8 @@ import { FontAsset } from '../text/font/font_asset.js';
91
98
  this._fontAssets = {};
92
99
  this._textDatas = {};
93
100
  this._jsonDatas = {};
101
+ this._cacheKeysByPath = new Map();
102
+ this._cachePathByKey = new Map();
94
103
  }
95
104
  /**
96
105
  * VFS used to read resources (files, URLs, virtual mounts).
@@ -105,45 +114,149 @@ import { FontAsset } from '../text/font/font_asset.js';
105
114
  * - Does not forcibly dispose GPU resources; it only clears references so they can be GC'd
106
115
  * if no other owners are holding them.
107
116
  */ clearCache() {
108
- for(const k in Object.keys(this._textures)){
109
- const v = this._textures[k];
110
- if (v instanceof DWeakRef) {
111
- v.dispose();
117
+ this.clearCacheEntries(this._textures);
118
+ this._textures = {};
119
+ this.clearCacheEntries(this._models);
120
+ this._models = {};
121
+ this.clearCacheEntries(this._materials);
122
+ this._materials = {};
123
+ this.clearCacheEntries(this._primitives);
124
+ this._primitives = {};
125
+ this.clearCacheEntries(this._skeletons);
126
+ this._skeletons = {};
127
+ this._bluePrints = {};
128
+ this._fontAssets = {};
129
+ this._binaryDatas = {};
130
+ this._textDatas = {};
131
+ this._jsonDatas = {};
132
+ this._cacheKeysByPath.clear();
133
+ this._cachePathByKey.clear();
134
+ }
135
+ /**
136
+ * Evicts cached data loaded from a VFS path.
137
+ *
138
+ * Existing scene objects keep their current resources. Subsequent loads read the asset again.
139
+ *
140
+ * @param path - Changed VFS file or directory path.
141
+ * @param recursive - Whether entries below a directory path should also be evicted.
142
+ * @returns Number of cache entries removed.
143
+ */ invalidateAsset(path, recursive = false) {
144
+ const normalizedPath = this.vfs.normalizePath(path);
145
+ let removed = 0;
146
+ const indexedPaths = recursive ? [
147
+ ...this._cacheKeysByPath.keys()
148
+ ].filter((sourcePath)=>this.cachePathMatches(sourcePath, normalizedPath, true)) : [
149
+ normalizedPath
150
+ ];
151
+ for (const sourcePath of indexedPaths){
152
+ const tokens = this._cacheKeysByPath.get(sourcePath);
153
+ if (!tokens) {
154
+ continue;
155
+ }
156
+ for (const token of [
157
+ ...tokens
158
+ ]){
159
+ const separator = token.indexOf('\0');
160
+ const kind = token.slice(0, separator);
161
+ const key = token.slice(separator + 1);
162
+ if (this.removeCacheEntry(kind, key)) {
163
+ removed++;
164
+ }
112
165
  }
113
166
  }
114
- this._textures = {};
115
- for(const k in Object.keys(this._models)){
116
- const v = this._models[k];
117
- if (v instanceof DWeakRef) {
118
- v.dispose();
167
+ return removed;
168
+ }
169
+ clearCacheEntries(cache, matches = ()=>true) {
170
+ let removed = 0;
171
+ for (const key of Object.keys(cache)){
172
+ if (!matches(key)) {
173
+ continue;
119
174
  }
175
+ const cached = cache[key];
176
+ if (cached instanceof DWeakRef) {
177
+ cached.dispose();
178
+ }
179
+ delete cache[key];
180
+ removed++;
120
181
  }
121
- this._models = {};
122
- for(const k in Object.keys(this._materials)){
123
- const v = this._materials[k];
124
- if (v instanceof DWeakRef) {
125
- v.dispose();
182
+ return removed;
183
+ }
184
+ cachePathMatches(cachePath, normalizedPath, recursive) {
185
+ return cachePath === normalizedPath || recursive && cachePath.startsWith(normalizedPath.endsWith('/') ? normalizedPath : `${normalizedPath}/`);
186
+ }
187
+ normalizeAssetSourcePath(path) {
188
+ return /^(?:[a-z]+:)?\/\//i.test(path) || path.startsWith('data:') || path.startsWith('blob:') ? path : this.vfs.normalizePath(path);
189
+ }
190
+ getCache(kind) {
191
+ switch(kind){
192
+ case 'texture':
193
+ return this._textures;
194
+ case 'model':
195
+ return this._models;
196
+ case 'binary':
197
+ return this._binaryDatas;
198
+ case 'font':
199
+ return this._fontAssets;
200
+ case 'text':
201
+ return this._textDatas;
202
+ case 'blueprint':
203
+ return this._bluePrints;
204
+ case 'material':
205
+ return this._materials;
206
+ case 'primitive':
207
+ return this._primitives;
208
+ case 'skeleton':
209
+ return this._skeletons;
210
+ case 'json':
211
+ return this._jsonDatas;
212
+ }
213
+ }
214
+ getCacheToken(kind, key) {
215
+ return `${kind}\0${key}`;
216
+ }
217
+ trackCacheEntry(kind, key, sourcePath) {
218
+ const token = this.getCacheToken(kind, key);
219
+ const normalizedSourcePath = this.normalizeAssetSourcePath(sourcePath);
220
+ const previousPath = this._cachePathByKey.get(token);
221
+ if (previousPath === normalizedSourcePath) {
222
+ return;
223
+ }
224
+ if (previousPath) {
225
+ const previousKeys = this._cacheKeysByPath.get(previousPath);
226
+ previousKeys?.delete(token);
227
+ if (previousKeys?.size === 0) {
228
+ this._cacheKeysByPath.delete(previousPath);
126
229
  }
127
230
  }
128
- this._materials = {};
129
- for(const k in Object.keys(this._primitives)){
130
- const v = this._primitives[k];
131
- if (v instanceof DWeakRef) {
132
- v.dispose();
231
+ let keys = this._cacheKeysByPath.get(normalizedSourcePath);
232
+ if (!keys) {
233
+ keys = new Set();
234
+ this._cacheKeysByPath.set(normalizedSourcePath, keys);
235
+ }
236
+ keys.add(token);
237
+ this._cachePathByKey.set(token, normalizedSourcePath);
238
+ }
239
+ removeCacheEntry(kind, key) {
240
+ const cache = this.getCache(kind);
241
+ const existed = key in cache;
242
+ if (existed) {
243
+ const cached = cache[key];
244
+ if (cached instanceof DWeakRef) {
245
+ cached.dispose();
133
246
  }
247
+ delete cache[key];
134
248
  }
135
- this._primitives = {};
136
- for(const k in Object.keys(this._skeletons)){
137
- const v = this._skeletons[k];
138
- if (v instanceof DWeakRef) {
139
- v.dispose();
249
+ const token = this.getCacheToken(kind, key);
250
+ const sourcePath = this._cachePathByKey.get(token);
251
+ if (sourcePath) {
252
+ const keys = this._cacheKeysByPath.get(sourcePath);
253
+ keys?.delete(token);
254
+ if (keys?.size === 0) {
255
+ this._cacheKeysByPath.delete(sourcePath);
140
256
  }
257
+ this._cachePathByKey.delete(token);
141
258
  }
142
- this._skeletons = {};
143
- this._fontAssets = {};
144
- this._binaryDatas = {};
145
- this._textDatas = {};
146
- this._jsonDatas = {};
259
+ return existed;
147
260
  }
148
261
  /**
149
262
  * Removes a cached font asset entry by URL.
@@ -153,11 +266,15 @@ import { FontAsset } from '../text/font/font_asset.js';
153
266
  * @param url - Resource URL or VFS path.
154
267
  * @returns `true` if a cache entry existed and was removed.
155
268
  */ releaseFontAsset(url) {
156
- if (url in this._fontAssets) {
157
- delete this._fontAssets[url];
158
- return true;
159
- }
160
- return false;
269
+ return this.removeCacheEntry('font', url);
270
+ }
271
+ /**
272
+ * Remove one material entry from cache.
273
+ *
274
+ * @param url - Material asset path.
275
+ * @returns `true` if an entry existed and was removed.
276
+ */ invalidateMaterial(url) {
277
+ return this.removeCacheEntry('material', url);
161
278
  }
162
279
  /**
163
280
  * Register a texture loader (highest priority first).
@@ -194,6 +311,7 @@ import { FontAsset } from '../text/font/font_asset.js';
194
311
  if (!P) {
195
312
  P = this.loadTextData(url, postProcess, options?.overrideVFS);
196
313
  this._textDatas[hash] = P;
314
+ this.trackCacheEntry('text', hash, url);
197
315
  }
198
316
  return P;
199
317
  }
@@ -213,6 +331,7 @@ import { FontAsset } from '../text/font/font_asset.js';
213
331
  if (!P) {
214
332
  P = this.loadJsonData(url, postProcess, options?.overrideVFS);
215
333
  this._jsonDatas[hash] = P;
334
+ this.trackCacheEntry('json', hash, url);
216
335
  }
217
336
  return P;
218
337
  }
@@ -224,6 +343,7 @@ import { FontAsset } from '../text/font/font_asset.js';
224
343
  * @param url - Resource URL or VFS path.
225
344
  * @param postProcess - Optional transformation applied to the loaded ArrayBuffer.
226
345
  * @param httpRequest - Optional HttpRequest for custom URL resolution/headers.
346
+ * @param options - Optional fetch options
227
347
  * @returns A promise that resolves to the loaded (and optionally processed) ArrayBuffer.
228
348
  */ async fetchBinaryData(url, postProcess, httpRequest, options) {
229
349
  const hash = httpRequest?.urlResolver?.(url) ?? url;
@@ -231,6 +351,7 @@ import { FontAsset } from '../text/font/font_asset.js';
231
351
  if (!P) {
232
352
  P = this.loadBinaryData(url, postProcess, options?.overrideVFS);
233
353
  this._binaryDatas[hash] = P;
354
+ this.trackCacheEntry('binary', hash, url);
234
355
  }
235
356
  return P;
236
357
  }
@@ -256,6 +377,7 @@ import { FontAsset } from '../text/font/font_asset.js';
256
377
  });
257
378
  });
258
379
  this._fontAssets[hash] = P;
380
+ this.trackCacheEntry('font', hash, url);
259
381
  }
260
382
  return P;
261
383
  }
@@ -273,6 +395,7 @@ import { FontAsset } from '../text/font/font_asset.js';
273
395
  if (!P) {
274
396
  P = this.loadBluePrint(url, options?.overrideVFS);
275
397
  this._bluePrints[hash] = P;
398
+ this.trackCacheEntry('blueprint', hash, url);
276
399
  }
277
400
  return P;
278
401
  }
@@ -290,9 +413,10 @@ import { FontAsset } from '../text/font/font_asset.js';
290
413
  } else if (!P || P instanceof DWeakRef) {
291
414
  P = this.loadMaterial(url, false, options?.overrideVFS);
292
415
  this._materials[hash] = P;
416
+ this.trackCacheEntry('material', hash, url);
293
417
  }
294
418
  const material = await P;
295
- if (this._materials[hash] instanceof Promise) {
419
+ if (this._materials[hash] === P) {
296
420
  this._materials[hash] = new DWeakRef(material);
297
421
  }
298
422
  return material;
@@ -311,9 +435,10 @@ import { FontAsset } from '../text/font/font_asset.js';
311
435
  } else if (!P || P instanceof DWeakRef) {
312
436
  P = this.loadPrimitive(url, options?.overrideVFS);
313
437
  this._primitives[hash] = P;
438
+ this.trackCacheEntry('primitive', hash, url);
314
439
  }
315
440
  const primitive = await P;
316
- if (this._primitives[hash] instanceof Promise) {
441
+ if (this._primitives[hash] === P) {
317
442
  this._primitives[hash] = new DWeakRef(primitive);
318
443
  }
319
444
  return primitive;
@@ -342,9 +467,10 @@ import { FontAsset } from '../text/font/font_asset.js';
342
467
  } else if (!P || P instanceof DWeakRef) {
343
468
  P = this.loadTexture(url, options?.mimeType ?? null, !options?.linearColorSpace, options?.samplerOptions, null, options?.overrideVFS);
344
469
  this._textures[hash] = P;
470
+ this.trackCacheEntry('texture', hash, url);
345
471
  }
346
472
  const tex = await P;
347
- if (this._textures[hash] instanceof Promise) {
473
+ if (this._textures[hash] === P) {
348
474
  this._textures[hash] = new DWeakRef(tex);
349
475
  }
350
476
  return tex;
@@ -368,9 +494,10 @@ import { FontAsset } from '../text/font/font_asset.js';
368
494
  } else if (!P || P instanceof DWeakRef) {
369
495
  P = this.loadModel(url, options, options?.overrideVFS);
370
496
  this._models[hash] = P;
497
+ this.trackCacheEntry('model', hash, url);
371
498
  }
372
499
  const sharedModel = await P;
373
- if (this._models[hash] instanceof Promise) {
500
+ if (this._models[hash] === P) {
374
501
  this._models[hash] = new DWeakRef(sharedModel);
375
502
  }
376
503
  return sharedModel;
@@ -526,18 +653,68 @@ import { FontAsset } from '../text/font/font_asset.js';
526
653
  }
527
654
  }
528
655
  const materials = await Promise.all(promises);
656
+ const knownPaths = new Set(paths);
657
+ for (const assetId of this._resourceManager.getTrackedMaterialAssetIds()){
658
+ if (knownPaths.has(assetId)) {
659
+ continue;
660
+ }
661
+ const refs = this._resourceManager.getMaterialRefsByAssetId(assetId);
662
+ for (const ref of refs ?? []){
663
+ const material = ref.get();
664
+ if (material instanceof PBRBluePrintMaterial && !material.disposed) {
665
+ materials.push(material);
666
+ paths.push(assetId);
667
+ knownPaths.add(assetId);
668
+ break;
669
+ }
670
+ }
671
+ }
672
+ // Reload blueprint parents first so dependent instances always resync against the latest parent state.
529
673
  for(let i = 0; i < materials.length; i++){
530
674
  const m = materials[i];
531
- if (m instanceof PBRBluePrintMaterial && (!filter || filter(m))) {
675
+ if (m instanceof PBRBluePrintMaterial && !(m instanceof PBRBluePrintMaterialInstance) && (!filter || filter(m))) {
676
+ const content = JSON.parse(await this.readFileFromVFS(paths[i], {
677
+ encoding: 'utf8'
678
+ }));
532
679
  const data = await this.loadBluePrintMaterialData(paths[i], true);
533
680
  if (data) {
534
681
  m.fragmentIR = data.irFragment;
535
682
  m.vertexIR = data.irVertex;
536
683
  m.uniformValues = data.uniformValues;
537
684
  m.uniformTextures = data.uniformTextures;
685
+ if (content.type === 'PBRBluePrintMaterial' && content.props) {
686
+ await this._resourceManager.deserializeObjectProps(m, content.props);
687
+ }
688
+ this._resourceManager.syncMaterialReferences(m);
538
689
  }
539
690
  }
540
691
  }
692
+ // Then reload instances from disk so override maps stay in sync with the refreshed parent materials.
693
+ for(let i = 0; i < materials.length; i++){
694
+ const m = materials[i];
695
+ if (m instanceof PBRBluePrintMaterialInstance && (!filter || filter(m))) {
696
+ const content = JSON.parse(await this.readFileFromVFS(paths[i], {
697
+ encoding: 'utf8'
698
+ }));
699
+ if (content.type !== 'PBRBluePrintMaterialInstance' || !content.data?.parent) {
700
+ continue;
701
+ }
702
+ const parent = await this.fetchMaterial(content.data.parent);
703
+ if (!(parent instanceof PBRBluePrintMaterial)) {
704
+ continue;
705
+ }
706
+ m.setParentMaterial(parent, content.data.parent);
707
+ m.setOverrides(content.data.uniformValues ?? [], await this.hydrateBluePrintUniformTextures(content.data.uniformTextures ?? []));
708
+ const instanceContent = JSON.parse(await this.readFileFromVFS(paths[i], {
709
+ encoding: 'utf8'
710
+ }));
711
+ if (instanceContent.type === 'PBRBluePrintMaterialInstance' && instanceContent.props) {
712
+ await this._resourceManager.deserializeObjectProps(m, instanceContent.props);
713
+ }
714
+ m.setMaterialPropertyOverrides(Object.keys(instanceContent.props ?? {}));
715
+ this._resourceManager.syncMaterialReferences(m);
716
+ }
717
+ }
541
718
  }
542
719
  async loadBluePrintMaterialData(url, reload, vfs) {
543
720
  try {
@@ -547,8 +724,23 @@ import { FontAsset } from '../text/font/font_asset.js';
547
724
  encoding: 'utf8'
548
725
  }, vfs);
549
726
  const content = JSON.parse(data);
550
- ASSERT(content.type === 'PBRBluePrintMaterial' || content.type === 'SpriteBluePrintMaterial', `Unsupported material type: ${content.type}`);
551
- irData = content.data;
727
+ ASSERT(content.type === 'PBRBluePrintMaterial' || content.type === 'PBRBluePrintMaterialInstance' || content.type === 'SpriteBluePrintMaterial', `Unsupported material type: ${content.type}`);
728
+ if (content.type === 'PBRBluePrintMaterialInstance') {
729
+ const parentPath = content.data.parent;
730
+ ASSERT(typeof parentPath === 'string' && !!parentPath, 'Blueprint material instance requires parent');
731
+ const parentContent = JSON.parse(await this.readFileFromVFS(parentPath, {
732
+ encoding: 'utf8'
733
+ }, vfs));
734
+ ASSERT(parentContent.type === 'PBRBluePrintMaterial', `Invalid parent blueprint material: ${parentPath}`);
735
+ irData = {
736
+ IR: parentContent.data?.IR ?? content.data.IR ?? '',
737
+ uniformValues: content.data.uniformValues ?? parentContent.data?.uniformValues ?? [],
738
+ uniformTextures: content.data.uniformTextures ?? parentContent.data?.uniformTextures ?? []
739
+ };
740
+ ASSERT(!!irData.IR, `Parent blueprint material missing IR path: ${parentPath}`);
741
+ } else {
742
+ irData = content.data;
743
+ }
552
744
  } else {
553
745
  irData = url;
554
746
  }
@@ -559,26 +751,7 @@ import { FontAsset } from '../text/font/font_asset.js';
559
751
  ...v,
560
752
  finalValue: v.value.length === 1 ? v.value[0] : new Float32Array(v.value)
561
753
  }));
562
- const uniformTextures = [];
563
- const textures = irData.uniformTextures;
564
- for (const v of textures){
565
- const tex = await this.fetchTexture(v.texture, {
566
- linearColorSpace: !v.sRGB,
567
- overrideVFS: vfs
568
- });
569
- uniformTextures.push({
570
- ...v,
571
- finalTexture: new DRef(tex),
572
- finalSampler: getDevice().createSampler({
573
- addressU: v.wrapS,
574
- addressV: v.wrapT,
575
- minFilter: v.minFilter,
576
- magFilter: v.magFilter,
577
- mipFilter: v.mipFilter
578
- }),
579
- params: tex ? new Vector4(tex.width, tex.height, tex.depth, tex.mipLevelCount) : Vector4.zero()
580
- });
581
- }
754
+ const uniformTextures = await this.hydrateBluePrintUniformTextures(irData.uniformTextures, vfs);
582
755
  return {
583
756
  irFragment: ir?.['fragment'] ?? null,
584
757
  irVertex: ir?.['vertex'] ?? null,
@@ -604,11 +777,19 @@ import { FontAsset } from '../text/font/font_asset.js';
604
777
  encoding: 'utf8'
605
778
  }, vfs);
606
779
  const content = JSON.parse(data);
607
- ASSERT(content.type === 'PBRBluePrintMaterial' || content.type === 'SpriteBluePrintMaterial' || content.type === 'Default', `Unsupported material type: ${content.type}`);
780
+ ASSERT(content.type === 'PBRBluePrintMaterial' || content.type === 'PBRBluePrintMaterialInstance' || content.type === 'SpriteBluePrintMaterial' || content.type === 'Default', `Unsupported material type: ${content.type}`);
608
781
  let mat;
609
782
  if (content.type === 'PBRBluePrintMaterial') {
610
783
  const data = await this.loadBluePrintMaterialData(content.data, reload, vfs);
611
784
  mat = new PBRBluePrintMaterial(data.irFragment, data.irVertex, data.uniformValues, data.uniformTextures);
785
+ } else if (content.type === 'PBRBluePrintMaterialInstance') {
786
+ const parentMaterial = await this.fetchMaterial(content.data.parent, vfs ? {
787
+ overrideVFS: vfs
788
+ } : undefined);
789
+ ASSERT(parentMaterial instanceof PBRBluePrintMaterial, `Invalid parent blueprint material: ${content.data.parent}`);
790
+ const instance = new PBRBluePrintMaterialInstance(parentMaterial, content.data.parent);
791
+ instance.setOverrides(content.data.uniformValues ?? [], await this.hydrateBluePrintUniformTextures(content.data.uniformTextures ?? [], vfs));
792
+ mat = instance;
612
793
  } else if (content.type === 'SpriteBluePrintMaterial') {
613
794
  const data = await this.loadBluePrintMaterialData(content.data, reload, vfs);
614
795
  mat = new SpriteBlueprintMaterial(data.irFragment, data.uniformValues, data.uniformTextures);
@@ -625,12 +806,45 @@ import { FontAsset } from '../text/font/font_asset.js';
625
806
  if (mat && content.props) {
626
807
  await this._resourceManager.deserializeObjectProps(mat, content.props);
627
808
  }
809
+ if (mat instanceof PBRBluePrintMaterialInstance) {
810
+ mat.setMaterialPropertyOverrides(Object.keys(content.props ?? {}));
811
+ mat.syncInheritedUniforms();
812
+ }
628
813
  return mat;
629
814
  } catch (err) {
630
815
  console.error(`Load material failed: ${err}`);
631
816
  return null;
632
817
  }
633
818
  }
819
+ async hydrateBluePrintUniformTextures(textures, vfs) {
820
+ return Promise.all((textures ?? []).map(async (v)=>{
821
+ let tex = null;
822
+ if (v.texture) {
823
+ try {
824
+ tex = await this.fetchTexture(v.texture, {
825
+ linearColorSpace: !v.sRGB,
826
+ overrideVFS: vfs
827
+ });
828
+ } catch (err) {
829
+ console.warn(`Load blueprint texture failed: ${v.texture}: ${String(err)}`);
830
+ }
831
+ }
832
+ tex = tex ?? getDefaultTexture2D();
833
+ return {
834
+ ...v,
835
+ exposed: v.exposed ?? true,
836
+ finalTexture: new DRef(tex),
837
+ finalSampler: getDevice().createSampler({
838
+ addressU: v.wrapS,
839
+ addressV: v.wrapT,
840
+ minFilter: v.minFilter,
841
+ magFilter: v.magFilter,
842
+ mipFilter: v.mipFilter
843
+ }),
844
+ params: tex ? new Vector4(tex.width, tex.height, tex.depth, tex.mipLevelCount) : Vector4.zero()
845
+ };
846
+ }));
847
+ }
634
848
  rebuildGraphStructure(nodes, links) {
635
849
  const gs = {
636
850
  outgoing: {},
@@ -751,7 +965,7 @@ import { FontAsset } from '../text/font/font_asset.js';
751
965
  };
752
966
  }
753
967
  invalidateBluePrint(path) {
754
- delete this._bluePrints[path];
968
+ this.removeCacheEntry('blueprint', path);
755
969
  }
756
970
  async loadBluePrint(path, vfs) {
757
971
  try {
@@ -766,11 +980,14 @@ import { FontAsset } from '../text/font/font_asset.js';
766
980
  const roots = [];
767
981
  const nodeMap = {};
768
982
  const state = states[k];
769
- for (const node of state.nodes){
770
- const impl = await this._resourceManager.deserializeObject(null, node.node);
771
- nodeMap[node.id] = impl;
983
+ const nodes = await Promise.all(state.nodes.map(async (node)=>({
984
+ id: node.id,
985
+ impl: await this._resourceManager.deserializeObject(null, node.node)
986
+ })));
987
+ for (const { id, impl } of nodes){
988
+ nodeMap[id] = impl;
772
989
  if (impl.outputs.length === 0) {
773
- roots.push(node.id);
990
+ roots.push(id);
774
991
  }
775
992
  }
776
993
  const dag = await this.createBluePrintDAG(nodeMap, roots, state.links);