@zephyr3d/scene 0.9.24 → 0.9.26

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 (264) hide show
  1. package/dist/animation/hair/gpu_hair_simulation.js +1090 -0
  2. package/dist/animation/hair/gpu_hair_simulation.js.map +1 -0
  3. package/dist/animation/joint_dynamics/controller.js +7 -5
  4. package/dist/animation/joint_dynamics/controller.js.map +1 -1
  5. package/dist/animation/joint_dynamics/joint_dynamics_system.js +81 -5
  6. package/dist/animation/joint_dynamics/joint_dynamics_system.js.map +1 -1
  7. package/dist/animation/morphtrack.js +3 -4
  8. package/dist/animation/morphtrack.js.map +1 -1
  9. package/dist/animation/skeleton.js +58 -24
  10. package/dist/animation/skeleton.js.map +1 -1
  11. package/dist/animation/spring/multi_chain_spring_system.js +577 -179
  12. package/dist/animation/spring/multi_chain_spring_system.js.map +1 -1
  13. package/dist/animation/spring/spring_collider.js +4 -3
  14. package/dist/animation/spring/spring_collider.js.map +1 -1
  15. package/dist/animation/spring/spring_node_pose_tracker.js +40 -0
  16. package/dist/animation/spring/spring_node_pose_tracker.js.map +1 -0
  17. package/dist/animation/spring/spring_particle.js +2 -0
  18. package/dist/animation/spring/spring_particle.js.map +1 -1
  19. package/dist/animation/spring/spring_system.js +11 -3
  20. package/dist/animation/spring/spring_system.js.map +1 -1
  21. package/dist/animation/spring_modifier.js +10 -0
  22. package/dist/animation/spring_modifier.js.map +1 -1
  23. package/dist/app/engine.js +53 -43
  24. package/dist/app/engine.js.map +1 -1
  25. package/dist/app/scriptingsystem.js +119 -6
  26. package/dist/app/scriptingsystem.js.map +1 -1
  27. package/dist/app/scriptregistry.js +3 -2
  28. package/dist/app/scriptregistry.js.map +1 -1
  29. package/dist/asset/assetmanager.js +284 -67
  30. package/dist/asset/assetmanager.js.map +1 -1
  31. package/dist/asset/loaders/zhair/zhair_format.js +288 -0
  32. package/dist/asset/loaders/zhair/zhair_format.js.map +1 -0
  33. package/dist/asset/loaders/zhair/zhair_loader.js +221 -0
  34. package/dist/asset/loaders/zhair/zhair_loader.js.map +1 -0
  35. package/dist/asset/model.js +178 -67
  36. package/dist/asset/model.js.map +1 -1
  37. package/dist/camera/camera.js +193 -21
  38. package/dist/camera/camera.js.map +1 -1
  39. package/dist/camera/perspectivecamera.js +3 -1
  40. package/dist/camera/perspectivecamera.js.map +1 -1
  41. package/dist/index.d.ts +4040 -1290
  42. package/dist/index.js +16 -6
  43. package/dist/index.js.map +1 -1
  44. package/dist/material/eye.js +932 -0
  45. package/dist/material/eye.js.map +1 -0
  46. package/dist/material/hair.js +917 -0
  47. package/dist/material/hair.js.map +1 -0
  48. package/dist/material/hairstrand.js +842 -0
  49. package/dist/material/hairstrand.js.map +1 -0
  50. package/dist/material/hairstrand_data.js +162 -0
  51. package/dist/material/hairstrand_data.js.map +1 -0
  52. package/dist/material/lambert.js +5 -2
  53. package/dist/material/lambert.js.map +1 -1
  54. package/dist/material/material.js +338 -300
  55. package/dist/material/material.js.map +1 -1
  56. package/dist/material/meshmaterial.js +120 -19
  57. package/dist/material/meshmaterial.js.map +1 -1
  58. package/dist/material/mixins/albedocolor.js +1 -1
  59. package/dist/material/mixins/albedocolor.js.map +1 -1
  60. package/dist/material/mixins/lightmodel/blinnphong.js +5 -3
  61. package/dist/material/mixins/lightmodel/blinnphong.js.map +1 -1
  62. package/dist/material/mixins/lightmodel/lambert.js +5 -4
  63. package/dist/material/mixins/lightmodel/lambert.js.map +1 -1
  64. package/dist/material/mixins/lightmodel/pbrblueprintmixin.js +3 -3
  65. package/dist/material/mixins/lightmodel/pbrblueprintmixin.js.map +1 -1
  66. package/dist/material/mixins/lightmodel/pbrmetallicroughness.js +5 -5
  67. package/dist/material/mixins/lightmodel/pbrmetallicroughness.js.map +1 -1
  68. package/dist/material/mixins/lightmodel/pbrspecularglossness.js +3 -3
  69. package/dist/material/mixins/lightmodel/pbrspecularglossness.js.map +1 -1
  70. package/dist/material/mixins/lit.js +73 -18
  71. package/dist/material/mixins/lit.js.map +1 -1
  72. package/dist/material/mixins/pbr/common.js +4 -4
  73. package/dist/material/mixins/pbr/common.js.map +1 -1
  74. package/dist/material/mixins/texture.js +48 -11
  75. package/dist/material/mixins/texture.js.map +1 -1
  76. package/dist/material/mtoon.js +6 -5
  77. package/dist/material/mtoon.js.map +1 -1
  78. package/dist/material/pbrblueprint.js +146 -28
  79. package/dist/material/pbrblueprint.js.map +1 -1
  80. package/dist/material/pbrblueprint_instance.js +501 -0
  81. package/dist/material/pbrblueprint_instance.js.map +1 -0
  82. package/dist/material/pbrmr.js +15 -16
  83. package/dist/material/pbrmr.js.map +1 -1
  84. package/dist/material/shader/helper.js +766 -456
  85. package/dist/material/shader/helper.js.map +1 -1
  86. package/dist/material/shader/terrain_blend.js +55 -0
  87. package/dist/material/shader/terrain_blend.js.map +1 -0
  88. package/dist/material/skin.js +192 -32
  89. package/dist/material/skin.js.map +1 -1
  90. package/dist/material/spriteblueprint.js +16 -1
  91. package/dist/material/spriteblueprint.js.map +1 -1
  92. package/dist/material/subsurfaceprofile.js +86 -23
  93. package/dist/material/subsurfaceprofile.js.map +1 -1
  94. package/dist/material/water.js +3 -1
  95. package/dist/material/water.js.map +1 -1
  96. package/dist/posteffect/bloom.js +167 -7
  97. package/dist/posteffect/bloom.js.map +1 -1
  98. package/dist/posteffect/coloradjust.js +2 -1
  99. package/dist/posteffect/coloradjust.js.map +1 -1
  100. package/dist/posteffect/compositor.js.map +1 -1
  101. package/dist/posteffect/fxaa.js +2 -1
  102. package/dist/posteffect/fxaa.js.map +1 -1
  103. package/dist/posteffect/grayscale.js +3 -1
  104. package/dist/posteffect/grayscale.js.map +1 -1
  105. package/dist/posteffect/motionblur.js +4 -1
  106. package/dist/posteffect/motionblur.js.map +1 -1
  107. package/dist/posteffect/posteffect.js.map +1 -1
  108. package/dist/posteffect/skinsss.js +368 -69
  109. package/dist/posteffect/skinsss.js.map +1 -1
  110. package/dist/posteffect/ssgi.js +16 -9
  111. package/dist/posteffect/ssgi.js.map +1 -1
  112. package/dist/posteffect/ssr.js +3 -0
  113. package/dist/posteffect/ssr.js.map +1 -1
  114. package/dist/posteffect/sss.js +91 -34
  115. package/dist/posteffect/sss.js.map +1 -1
  116. package/dist/posteffect/taa.js.map +1 -1
  117. package/dist/posteffect/tonemap.js +44 -1
  118. package/dist/posteffect/tonemap.js.map +1 -1
  119. package/dist/render/abuffer_oit.js +45 -39
  120. package/dist/render/abuffer_oit.js.map +1 -1
  121. package/dist/render/cluster_light.js +4 -4
  122. package/dist/render/cluster_light.js.map +1 -1
  123. package/dist/render/cull_visitor.js +19 -0
  124. package/dist/render/cull_visitor.js.map +1 -1
  125. package/dist/render/depthpass.js +36 -2
  126. package/dist/render/depthpass.js.map +1 -1
  127. package/dist/render/drawable_mixin.js +26 -1
  128. package/dist/render/drawable_mixin.js.map +1 -1
  129. package/dist/render/dualdepthpeeling_oit.js +52 -51
  130. package/dist/render/dualdepthpeeling_oit.js.map +1 -1
  131. package/dist/render/envlight.js +1 -1
  132. package/dist/render/envlight.js.map +1 -1
  133. package/dist/render/fbm_wavegenerator.js +3 -0
  134. package/dist/render/fbm_wavegenerator.js.map +1 -1
  135. package/dist/render/gerstner_wavegenerator.js +3 -0
  136. package/dist/render/gerstner_wavegenerator.js.map +1 -1
  137. package/dist/render/globalbindgroup_allocator.js +3 -0
  138. package/dist/render/globalbindgroup_allocator.js.map +1 -1
  139. package/dist/render/oit.js +29 -0
  140. package/dist/render/oit.js.map +1 -0
  141. package/dist/render/render_queue.js +3 -0
  142. package/dist/render/render_queue.js.map +1 -1
  143. package/dist/render/rendergraph/device_pool_allocator.js.map +1 -1
  144. package/dist/render/rendergraph/executor.js +2 -0
  145. package/dist/render/rendergraph/executor.js.map +1 -1
  146. package/dist/render/rendergraph/forward_plus_builder.js +61 -1
  147. package/dist/render/rendergraph/forward_plus_builder.js.map +1 -1
  148. package/dist/render/rendergraph/rendergraph.js.map +1 -1
  149. package/dist/render/rendergraph/texture_affinity_cache.js +1 -1
  150. package/dist/render/rendergraph/texture_affinity_cache.js.map +1 -1
  151. package/dist/render/rendergraph/types.js.map +1 -1
  152. package/dist/render/shadow_mask_pass.js +73 -8
  153. package/dist/render/shadow_mask_pass.js.map +1 -1
  154. package/dist/render/sky.js +166 -126
  155. package/dist/render/sky.js.map +1 -1
  156. package/dist/render/weightedblended_oit.js +36 -35
  157. package/dist/render/weightedblended_oit.js.map +1 -1
  158. package/dist/scene/basesprite.js +8 -0
  159. package/dist/scene/basesprite.js.map +1 -1
  160. package/dist/scene/batchgroup.js +3 -0
  161. package/dist/scene/batchgroup.js.map +1 -1
  162. package/dist/scene/environment.js +4 -1
  163. package/dist/scene/environment.js.map +1 -1
  164. package/dist/scene/hair.js +963 -0
  165. package/dist/scene/hair.js.map +1 -0
  166. package/dist/scene/light.js +5 -1
  167. package/dist/scene/light.js.map +1 -1
  168. package/dist/scene/mesh.js +481 -47
  169. package/dist/scene/mesh.js.map +1 -1
  170. package/dist/scene/meshdrawable.js +6 -0
  171. package/dist/scene/meshdrawable.js.map +1 -1
  172. package/dist/scene/msdftext.js +3 -0
  173. package/dist/scene/msdftext.js.map +1 -1
  174. package/dist/scene/msdftextsprite.js +3 -0
  175. package/dist/scene/msdftextsprite.js.map +1 -1
  176. package/dist/scene/particlesys.js +6 -0
  177. package/dist/scene/particlesys.js.map +1 -1
  178. package/dist/scene/scene.js +3 -0
  179. package/dist/scene/scene.js.map +1 -1
  180. package/dist/scene/scene_node.js +6 -1
  181. package/dist/scene/scene_node.js.map +1 -1
  182. package/dist/scene/terrain-cm/grass.js +273 -249
  183. package/dist/scene/terrain-cm/grass.js.map +1 -1
  184. package/dist/scene/terrain-cm/grassmaterial.js +3 -0
  185. package/dist/scene/terrain-cm/grassmaterial.js.map +1 -1
  186. package/dist/scene/terrain-cm/terrain-cm.js +6 -0
  187. package/dist/scene/terrain-cm/terrain-cm.js.map +1 -1
  188. package/dist/scene/terrain-cm/texture_cache.js +309 -0
  189. package/dist/scene/terrain-cm/texture_cache.js.map +1 -0
  190. package/dist/scene/water.js +6 -0
  191. package/dist/scene/water.js.map +1 -1
  192. package/dist/shaders/atmosphere.js.map +1 -1
  193. package/dist/shaders/diffusion.js +54 -0
  194. package/dist/shaders/diffusion.js.map +1 -0
  195. package/dist/shaders/shadow.js +33 -21
  196. package/dist/shaders/shadow.js.map +1 -1
  197. package/dist/shaders/temporal.js +71 -33
  198. package/dist/shaders/temporal.js.map +1 -1
  199. package/dist/shadow/dom.js +546 -0
  200. package/dist/shadow/dom.js.map +1 -0
  201. package/dist/shadow/pcf_opt.js +3 -5
  202. package/dist/shadow/pcf_opt.js.map +1 -1
  203. package/dist/shadow/pcf_pd.js +1 -1
  204. package/dist/shadow/pcf_pd.js.map +1 -1
  205. package/dist/shadow/receiver_bias.js +41 -0
  206. package/dist/shadow/receiver_bias.js.map +1 -0
  207. package/dist/shadow/shader.js +20 -60
  208. package/dist/shadow/shader.js.map +1 -1
  209. package/dist/shadow/shadow_impl.js +89 -0
  210. package/dist/shadow/shadow_impl.js.map +1 -1
  211. package/dist/shadow/shadow_region.js +50 -50
  212. package/dist/shadow/shadow_region.js.map +1 -1
  213. package/dist/shadow/shadowmapper.js +150 -37
  214. package/dist/shadow/shadowmapper.js.map +1 -1
  215. package/dist/shapes/shape.js +11 -2
  216. package/dist/shapes/shape.js.map +1 -1
  217. package/dist/shapes/sphere.js +50 -9
  218. package/dist/shapes/sphere.js.map +1 -1
  219. package/dist/utility/blueprint/common/constants.js +112 -2
  220. package/dist/utility/blueprint/common/constants.js.map +1 -1
  221. package/dist/utility/blueprint/material/func.js +142 -49
  222. package/dist/utility/blueprint/material/func.js.map +1 -1
  223. package/dist/utility/blueprint/material/ir.js +490 -25
  224. package/dist/utility/blueprint/material/ir.js.map +1 -1
  225. package/dist/utility/blueprint/material/texture.js +898 -4
  226. package/dist/utility/blueprint/material/texture.js.map +1 -1
  227. package/dist/utility/serialization/manager.js +431 -100
  228. package/dist/utility/serialization/manager.js.map +1 -1
  229. package/dist/utility/serialization/scene/animation.js +370 -3
  230. package/dist/utility/serialization/scene/animation.js.map +1 -1
  231. package/dist/utility/serialization/scene/batch.js +4 -0
  232. package/dist/utility/serialization/scene/batch.js.map +1 -1
  233. package/dist/utility/serialization/scene/camera.js +133 -1
  234. package/dist/utility/serialization/scene/camera.js.map +1 -1
  235. package/dist/utility/serialization/scene/common.js +26 -5
  236. package/dist/utility/serialization/scene/common.js.map +1 -1
  237. package/dist/utility/serialization/scene/hair.js +1141 -0
  238. package/dist/utility/serialization/scene/hair.js.map +1 -0
  239. package/dist/utility/serialization/scene/light.js +84 -4
  240. package/dist/utility/serialization/scene/light.js.map +1 -1
  241. package/dist/utility/serialization/scene/material.js +1867 -166
  242. package/dist/utility/serialization/scene/material.js.map +1 -1
  243. package/dist/utility/serialization/scene/mesh.js +231 -14
  244. package/dist/utility/serialization/scene/mesh.js.map +1 -1
  245. package/dist/utility/serialization/scene/node.js +141 -7
  246. package/dist/utility/serialization/scene/node.js.map +1 -1
  247. package/dist/utility/serialization/scene/particle.js +4 -0
  248. package/dist/utility/serialization/scene/particle.js.map +1 -1
  249. package/dist/utility/serialization/scene/primitive.js +18 -0
  250. package/dist/utility/serialization/scene/primitive.js.map +1 -1
  251. package/dist/utility/serialization/scene/sprite.js +4 -0
  252. package/dist/utility/serialization/scene/sprite.js.map +1 -1
  253. package/dist/utility/serialization/scene/terrain.js +92 -69
  254. package/dist/utility/serialization/scene/terrain.js.map +1 -1
  255. package/dist/utility/serialization/scene/text.js +4 -0
  256. package/dist/utility/serialization/scene/text.js.map +1 -1
  257. package/dist/utility/serialization/scene/water.js +4 -0
  258. package/dist/utility/serialization/scene/water.js.map +1 -1
  259. package/dist/utility/serialization/types.js.map +1 -1
  260. package/dist/utility/shprojector.js +96 -95
  261. package/dist/utility/shprojector.js.map +1 -1
  262. package/dist/values.js +105 -58
  263. package/dist/values.js.map +1 -1
  264. package/package.json +3 -3
@@ -7,11 +7,14 @@ import '../material/unlit.js';
7
7
  import '../material/particle.js';
8
8
  import '../material/subsurfaceprofile.js';
9
9
  import '../material/skin.js';
10
+ import '../material/eye.js';
10
11
  import { PBStructTypeInfo, PBArrayTypeInfo, PBPrimitiveTypeInfo, PBPrimitiveType } from '@zephyr3d/device';
11
- import { MORPH_WEIGHTS_VECTOR_COUNT, MORPH_ATTRIBUTE_VECTOR_COUNT, QUEUE_OPAQUE } from '../values.js';
12
+ import { QUEUE_OPAQUE, MORPH_WEIGHTS_VECTOR_COUNT, MORPH_ATTRIBUTE_VECTOR_COUNT, getMorphTargetLimit, getActiveMorphTargetLimit } from '../values.js';
12
13
  import { RenderBundleWrapper } from '../render/renderbundle_wrapper.js';
13
14
  import { getDevice } from '../app/api.js';
14
15
  import '../material/meshmaterial.js';
16
+ import '../material/hair.js';
17
+ import '../material/hairstrand.js';
15
18
  import '../material/grassmaterial.js';
16
19
  import '../material/terrain-cm.js';
17
20
  import '../material/pbrmr.js';
@@ -25,6 +28,40 @@ import { BoundingBox } from '../utility/bounding_volume.js';
25
28
  import { mixinDrawable } from '../render/drawable_mixin.js';
26
29
  import { calculateMorphBoundingBox } from '../animation/morphtarget.js';
27
30
 
31
+ const MORPH_WEIGHT_CAPACITY = MORPH_WEIGHTS_VECTOR_COUNT * 4;
32
+ const MORPH_ATTRIBUTE_CAPACITY = MORPH_ATTRIBUTE_VECTOR_COUNT * 4;
33
+ const MORPH_INFO_DATA_LENGTH = 4 + MORPH_WEIGHT_CAPACITY + MORPH_ATTRIBUTE_CAPACITY;
34
+ const MORPH_RENDER_INDEX_OFFSET = 4 + MORPH_WEIGHT_CAPACITY;
35
+ const MORPH_RENDER_ATTRIBUTE_OFFSET = MORPH_RENDER_INDEX_OFFSET + MORPH_WEIGHT_CAPACITY;
36
+ const MORPH_RENDER_INFO_DATA_LENGTH = MORPH_RENDER_ATTRIBUTE_OFFSET + MORPH_ATTRIBUTE_CAPACITY;
37
+ const ACTIVE_MORPH_WEIGHT_EPSILON = 1e-5;
38
+ function normalizeMorphInfoData(data) {
39
+ const normalized = new Float32Array(MORPH_INFO_DATA_LENGTH);
40
+ for(let i = 0; i < MORPH_ATTRIBUTE_CAPACITY; i++){
41
+ normalized[4 + MORPH_WEIGHT_CAPACITY + i] = -1;
42
+ }
43
+ normalized.set(data.subarray(0, Math.min(data.length, normalized.length)));
44
+ const declaredCount = Math.max(0, Math.floor(Number(normalized[3]) || 0));
45
+ const supportedCount = Math.min(declaredCount, getMorphTargetLimit(), MORPH_WEIGHT_CAPACITY, Math.max(0, data.length - 4));
46
+ normalized[3] = supportedCount;
47
+ return {
48
+ data: normalized,
49
+ declaredCount,
50
+ supportedCount
51
+ };
52
+ }
53
+ function createMorphInfoBuffer(data) {
54
+ const bufferData = new Float32Array(data);
55
+ const bufferType = new PBStructTypeInfo('dummy', 'std140', [
56
+ {
57
+ name: ShaderHelper.getMorphInfoUniformName(),
58
+ type: new PBArrayTypeInfo(new PBPrimitiveTypeInfo(PBPrimitiveType.F32VEC4), 1 + MORPH_WEIGHTS_VECTOR_COUNT * 2 + MORPH_ATTRIBUTE_VECTOR_COUNT)
59
+ }
60
+ ]);
61
+ return getDevice().createStructuredBuffer(bufferType, {
62
+ usage: 'uniform'
63
+ }, bufferData);
64
+ }
28
65
  const MeshBase = castObservable(applyMixins(GraphNode, mixinDrawable))();
29
66
  /**
30
67
  * Mesh node
@@ -37,9 +74,16 @@ const MeshBase = castObservable(applyMixins(GraphNode, mixinDrawable))();
37
74
  /** @internal */ _animatedBoundingBox;
38
75
  /** @internal */ _skinBindingName;
39
76
  /** @internal */ _boneMatrices;
77
+ /** @internal */ _skinInfluenceData;
40
78
  /** @internal */ _morphData;
41
79
  /** @internal */ _morphInfo;
80
+ /** @internal */ _renderMorphInfo;
42
81
  /** @internal */ _morphBoundingInfo;
82
+ /** @internal */ _morphSource;
83
+ /** @internal */ _morphSourceData;
84
+ /** @internal */ _activeMorphTargetIndices;
85
+ /** @internal */ _compactMorphData;
86
+ /** @internal */ _activeMorphCandidates;
43
87
  /** @internal */ _morphDirty;
44
88
  /** @internal */ _instanceHash;
45
89
  /** @internal */ _batchable;
@@ -61,9 +105,16 @@ const MeshBase = castObservable(applyMixins(GraphNode, mixinDrawable))();
61
105
  this._skinnedBoundingInfo = null;
62
106
  this._animatedBoundingBox = null;
63
107
  this._boneMatrices = new DRef();
108
+ this._skinInfluenceData = null;
64
109
  this._morphData = null;
65
110
  this._morphInfo = null;
111
+ this._renderMorphInfo = null;
66
112
  this._morphBoundingInfo = null;
113
+ this._morphSource = null;
114
+ this._morphSourceData = null;
115
+ this._activeMorphTargetIndices = new Uint32Array(0);
116
+ this._compactMorphData = false;
117
+ this._activeMorphCandidates = [];
67
118
  this._morphDirty = false;
68
119
  this._instanceHash = null;
69
120
  this._pickTarget = {
@@ -154,6 +205,12 @@ const MeshBase = castObservable(applyMixins(GraphNode, mixinDrawable))();
154
205
  this.invalidateBoundingVolume();
155
206
  RenderBundleWrapper.drawableChanged(this);
156
207
  this._primitiveChangeTag = null;
208
+ if (this._morphData) {
209
+ this._ensureWebGLVertexIndexAttribute('morphing');
210
+ }
211
+ if (this._skinInfluenceData && this._skinInfluenceData.influenceCount > 4) {
212
+ this._ensureWebGLVertexIndexAttribute('skinning');
213
+ }
157
214
  this.dispatchEvent('primitive_changed', prim);
158
215
  }
159
216
  }
@@ -215,12 +272,61 @@ const MeshBase = castObservable(applyMixins(GraphNode, mixinDrawable))();
215
272
  }
216
273
  }
217
274
  /**
275
+ * Sets additional per-vertex skinning influences packed in a texture.
276
+ * The first 4 influences remain in the vertex attributes; this payload stores the remainder.
277
+ * @param data - Extra influence texture payload, or null to clear it
278
+ */ setSkinInfluenceData(data) {
279
+ if (!data) {
280
+ if (this._skinInfluenceData) {
281
+ this._skinInfluenceData.texture?.get()?.dispose();
282
+ this._skinInfluenceData = null;
283
+ this._renderBundle = {};
284
+ RenderBundleWrapper.drawableChanged(this);
285
+ }
286
+ return;
287
+ }
288
+ if (!this._skinInfluenceData) {
289
+ this._skinInfluenceData = {
290
+ texture: new DRef(),
291
+ width: 0,
292
+ height: 0,
293
+ influenceCount: 4,
294
+ data: new Float32Array(0)
295
+ };
296
+ }
297
+ this._skinInfluenceData.width = data.width;
298
+ this._skinInfluenceData.height = data.height;
299
+ this._skinInfluenceData.influenceCount = data.influenceCount;
300
+ this._skinInfluenceData.data = data.data.slice();
301
+ if (data.texture?.get()) {
302
+ this._skinInfluenceData.texture.set(data.texture.get());
303
+ } else {
304
+ let tex = this._skinInfluenceData.texture?.get() ?? null;
305
+ if (!tex || tex.width !== data.width || tex.height !== data.height) {
306
+ tex?.dispose();
307
+ tex = getDevice().createTexture2D('rgba32f', data.width, data.height, {
308
+ mipmapping: false,
309
+ samplerOptions: {
310
+ minFilter: 'nearest',
311
+ magFilter: 'nearest',
312
+ mipFilter: 'none'
313
+ }
314
+ });
315
+ this._skinInfluenceData.texture.set(tex);
316
+ }
317
+ tex.update(data.data, 0, 0, data.width, data.height);
318
+ }
319
+ this._ensureWebGLVertexIndexAttribute('skinning');
320
+ this._renderBundle = {};
321
+ RenderBundleWrapper.drawableChanged(this);
322
+ }
323
+ /**
218
324
  * Sets the texture that contains the morph target data
219
325
  * @param data - The texture that contains the morph target data
220
326
  */ setMorphData(data) {
221
327
  if (!data) {
222
328
  if (this._morphData) {
223
- this._morphData?.texture?.dispose();
329
+ this._morphData.texture?.get()?.dispose();
224
330
  this._morphData = null;
225
331
  this._renderBundle = {};
226
332
  RenderBundleWrapper.drawableChanged(this);
@@ -237,19 +343,27 @@ const MeshBase = castObservable(applyMixins(GraphNode, mixinDrawable))();
237
343
  if (data.texture?.get()) {
238
344
  this._morphData.texture.set(data.texture.get());
239
345
  } else {
240
- const tex = getDevice().createTexture2D('rgba32f', data.width, data.height, {
241
- mipmapping: false,
242
- samplerOptions: {
243
- minFilter: 'nearest',
244
- magFilter: 'nearest',
245
- mipFilter: 'none'
246
- }
247
- });
346
+ let tex = this._morphData.texture?.get() ?? null;
347
+ if (!tex || tex.width !== data.width || tex.height !== data.height) {
348
+ tex?.dispose();
349
+ tex = getDevice().createTexture2D('rgba32f', data.width, data.height, {
350
+ mipmapping: false,
351
+ samplerOptions: {
352
+ minFilter: 'nearest',
353
+ magFilter: 'nearest',
354
+ mipFilter: 'none'
355
+ }
356
+ });
357
+ this._morphData.texture.set(tex);
358
+ }
248
359
  tex.update(data.data, 0, 0, data.width, data.height);
249
- this._morphData.texture.set(tex);
250
360
  }
361
+ this._ensureWebGLVertexIndexAttribute('morphing');
251
362
  this._renderBundle = {};
252
363
  RenderBundleWrapper.drawableChanged(this);
364
+ if (this._morphInfo && !this._morphSourceData) {
365
+ this.updateRenderMorphInfo(this.collectActiveMorphTargetIndices(), false);
366
+ }
253
367
  }
254
368
  }
255
369
  /**
@@ -264,41 +378,86 @@ const MeshBase = castObservable(applyMixins(GraphNode, mixinDrawable))();
264
378
  return this._morphData;
265
379
  }
266
380
  /**
381
+ * {@inheritDoc Drawable.getSkinInfluenceData}
382
+ */ getSkinInfluenceData() {
383
+ return this._skinInfluenceData;
384
+ }
385
+ /**
386
+ * Gets the external morph source descriptor.
387
+ */ getMorphSource() {
388
+ return this._morphSource;
389
+ }
390
+ /**
391
+ * Sets the external morph source descriptor.
392
+ * @param source - The morph source descriptor
393
+ */ setMorphSource(source) {
394
+ this._morphSource = source ? {
395
+ ...source
396
+ } : null;
397
+ }
398
+ /**
399
+ * Gets the CPU-side morph source data.
400
+ */ getMorphSourceData() {
401
+ return this._morphSourceData;
402
+ }
403
+ /**
404
+ * Sets the CPU-side morph source data used to rebuild GPU morph textures on demand.
405
+ */ setMorphSourceData(data) {
406
+ this._morphSourceData = data ?? null;
407
+ this._activeMorphTargetIndices = new Uint32Array(0);
408
+ this._compactMorphData = false;
409
+ this._morphDirty = true;
410
+ if (!data) {
411
+ this.setMorphData(null);
412
+ this.setRenderMorphInfo(null);
413
+ } else if (this._morphInfo) {
414
+ this.setMorphData(null);
415
+ this.setRenderMorphInfo(null);
416
+ this.rebuildActiveMorphData();
417
+ this._morphDirty = false;
418
+ }
419
+ }
420
+ /**
267
421
  * Sets the buffer that contains the morph target information
268
422
  * @param info - The buffer that contains the morph target information
269
423
  */ setMorphInfo(info) {
270
424
  if (!info) {
271
425
  if (this._morphInfo) {
272
- this._morphInfo.buffer?.dispose();
273
426
  this._morphInfo = null;
274
- this._renderBundle = {};
275
- RenderBundleWrapper.drawableChanged(this);
276
427
  }
428
+ this.setMorphData(null);
429
+ this.setRenderMorphInfo(null);
430
+ this._activeMorphTargetIndices = new Uint32Array(0);
431
+ this._compactMorphData = false;
432
+ this._renderBundle = {};
433
+ RenderBundleWrapper.drawableChanged(this);
277
434
  } else {
278
435
  if (!this._morphInfo) {
279
- this._morphInfo = {
280
- buffer: new DRef()
281
- };
436
+ this._morphInfo = {};
282
437
  }
283
- this._morphInfo.data = info.data.slice();
284
- this._morphInfo.names = {
285
- ...info.names
286
- };
287
- if (info.buffer?.get()) {
288
- this._morphInfo.buffer.set(info.buffer.get());
289
- } else {
290
- const bufferType = new PBStructTypeInfo('dummy', 'std140', [
291
- {
292
- name: ShaderHelper.getMorphInfoUniformName(),
293
- type: new PBArrayTypeInfo(new PBPrimitiveTypeInfo(PBPrimitiveType.F32VEC4), 1 + MORPH_WEIGHTS_VECTOR_COUNT + MORPH_ATTRIBUTE_VECTOR_COUNT)
294
- }
295
- ]);
296
- const morphUniformBuffer = getDevice().createStructuredBuffer(bufferType, {
297
- usage: 'uniform'
298
- }, info.data);
299
- this._morphInfo.buffer.set(morphUniformBuffer);
438
+ const { data, declaredCount, supportedCount } = normalizeMorphInfoData(info.data);
439
+ if (declaredCount !== supportedCount) {
440
+ console.warn(`Morph target count truncated from ${declaredCount} to ${supportedCount} to fit the runtime buffer layout`);
441
+ }
442
+ const names = {};
443
+ for (const [name, index] of Object.entries(info.names ?? {})){
444
+ if (Number.isInteger(index) && index >= 0 && index < supportedCount) {
445
+ names[name] = index;
446
+ }
447
+ }
448
+ this._morphInfo.data = data;
449
+ this._morphInfo.names = names;
450
+ this._morphDirty = true;
451
+ if (this._morphSourceData) {
452
+ this.setMorphData(null);
453
+ this.setRenderMorphInfo(null);
454
+ this.rebuildActiveMorphData();
455
+ this._morphDirty = false;
456
+ } else if (this._morphData) {
457
+ this.setRenderMorphInfo(null);
458
+ this.updateRenderMorphInfo(this.collectActiveMorphTargetIndices(), false);
459
+ this._morphDirty = false;
300
460
  }
301
- this._morphDirty = false;
302
461
  this.refreshAnimatedBoundingBox();
303
462
  this._renderBundle = {};
304
463
  RenderBundleWrapper.drawableChanged(this);
@@ -309,10 +468,228 @@ const MeshBase = castObservable(applyMixins(GraphNode, mixinDrawable))();
309
468
  */ getMorphInfo() {
310
469
  return this._morphInfo;
311
470
  }
471
+ /** @internal */ getRenderMorphInfo() {
472
+ return this._renderMorphInfo;
473
+ }
474
+ /** @internal */ setRenderMorphInfo(info) {
475
+ if (!info) {
476
+ if (this._renderMorphInfo) {
477
+ this._renderMorphInfo.buffer?.dispose();
478
+ this._renderMorphInfo = null;
479
+ }
480
+ return;
481
+ }
482
+ if (!this._renderMorphInfo) {
483
+ this._renderMorphInfo = {
484
+ buffer: new DRef()
485
+ };
486
+ }
487
+ this._renderMorphInfo.data = info.data;
488
+ this._renderMorphInfo.names = info.names;
489
+ if (!this._renderMorphInfo.buffer?.get()) {
490
+ this._renderMorphInfo.buffer.set(createMorphInfoBuffer(info.data));
491
+ } else {
492
+ this._renderMorphInfo.buffer.get().bufferSubData(0, info.data);
493
+ }
494
+ }
495
+ /** @internal */ collectActiveMorphTargetIndices() {
496
+ if (!this._morphInfo) {
497
+ return new Uint32Array(0);
498
+ }
499
+ const count = this.getNumMorphTargets();
500
+ const activeLimit = Math.min(getActiveMorphTargetLimit(), count);
501
+ if (activeLimit <= 0) {
502
+ return new Uint32Array(0);
503
+ }
504
+ const weighted = this._activeMorphCandidates;
505
+ let weightedCount = 0;
506
+ for(let i = 0; i < count; i++){
507
+ const weight = this._morphInfo.data[4 + i];
508
+ if (Math.abs(weight) > ACTIVE_MORPH_WEIGHT_EPSILON) {
509
+ const candidate = weighted[weightedCount] ?? {
510
+ index: i,
511
+ weight: 0
512
+ };
513
+ candidate.index = i;
514
+ candidate.weight = Math.abs(weight);
515
+ weighted[weightedCount] = candidate;
516
+ weightedCount++;
517
+ }
518
+ }
519
+ weighted.length = weightedCount;
520
+ let selectedCount = weightedCount;
521
+ if (weightedCount > activeLimit) {
522
+ weighted.sort((a, b)=>b.weight - a.weight);
523
+ selectedCount = activeLimit;
524
+ }
525
+ for(let i = 1; i < selectedCount; i++){
526
+ const candidate = weighted[i];
527
+ let targetIndex = i;
528
+ while(targetIndex > 0 && weighted[targetIndex - 1].index > candidate.index){
529
+ weighted[targetIndex] = weighted[targetIndex - 1];
530
+ targetIndex--;
531
+ }
532
+ weighted[targetIndex] = candidate;
533
+ }
534
+ const activeIndices = new Uint32Array(selectedCount);
535
+ for(let i = 0; i < selectedCount; i++){
536
+ activeIndices[i] = weighted[i].index;
537
+ }
538
+ return activeIndices;
539
+ }
540
+ /** @internal */ rebuildActiveMorphData() {
541
+ if (!this._morphInfo || !this._morphSourceData) {
542
+ this.setMorphData(null);
543
+ this.setRenderMorphInfo(null);
544
+ this._activeMorphTargetIndices = new Uint32Array(0);
545
+ this._compactMorphData = false;
546
+ return;
547
+ }
548
+ const activeIndices = this.collectActiveMorphTargetIndices();
549
+ const sameActiveSet = this._activeMorphTargetIndices.length === activeIndices.length && this._activeMorphTargetIndices.every((value, index)=>value === activeIndices[index]);
550
+ if (this._morphData && (!this._compactMorphData || sameActiveSet)) {
551
+ this.updateRenderMorphInfo(activeIndices, this._compactMorphData);
552
+ this._activeMorphTargetIndices = activeIndices;
553
+ return;
554
+ }
555
+ const attributes = Object.keys(this._morphSourceData.targets).map((key)=>Number(key)).filter((value)=>Number.isInteger(value) && value >= 0 && value < MORPH_ATTRIBUTE_CAPACITY).sort((a, b)=>a - b);
556
+ const numVertices = this._morphSourceData.numVertices;
557
+ const textureTargetCount = Math.min(this.getNumMorphTargets(), this._morphSourceData.numTargets);
558
+ if (numVertices <= 0 || attributes.length === 0 || textureTargetCount <= 0) {
559
+ this.setMorphData(null);
560
+ this.setRenderMorphInfo(null);
561
+ this._activeMorphTargetIndices = new Uint32Array(0);
562
+ this._compactMorphData = false;
563
+ return;
564
+ }
565
+ const maxTextureSize = getDevice().getDeviceCaps().textureCaps.maxTextureSize;
566
+ const fullTextureSize = Math.ceil(Math.sqrt(numVertices * attributes.length * textureTargetCount));
567
+ let textureTargetIndices;
568
+ if (fullTextureSize <= maxTextureSize) {
569
+ textureTargetIndices = new Uint32Array(textureTargetCount);
570
+ for(let i = 0; i < textureTargetCount; i++){
571
+ textureTargetIndices[i] = i;
572
+ }
573
+ this._compactMorphData = false;
574
+ } else if (activeIndices.length > 0) {
575
+ textureTargetIndices = activeIndices;
576
+ this._compactMorphData = true;
577
+ } else {
578
+ this.setMorphData(null);
579
+ this.setRenderMorphInfo(null);
580
+ this._activeMorphTargetIndices = activeIndices;
581
+ this._compactMorphData = true;
582
+ return;
583
+ }
584
+ const textureSize = Math.ceil(Math.sqrt(numVertices * attributes.length * textureTargetIndices.length));
585
+ if (textureSize > maxTextureSize) {
586
+ console.warn(`Active morph target texture too large for mesh "${this.name ?? ''}": ${textureSize} exceeds device limit`);
587
+ this.setMorphData(null);
588
+ this.setRenderMorphInfo(null);
589
+ this._activeMorphTargetIndices = new Uint32Array(0);
590
+ return;
591
+ }
592
+ const textureData = new Float32Array(textureSize * textureSize * 4);
593
+ const attributeOffsets = new Float32Array(MORPH_ATTRIBUTE_CAPACITY);
594
+ for(let i = 0; i < MORPH_ATTRIBUTE_CAPACITY; i++){
595
+ attributeOffsets[i] = -1;
596
+ }
597
+ let offset = 0;
598
+ for (const attrib of attributes){
599
+ const source = this._morphSourceData.targets[attrib];
600
+ if (!source) {
601
+ continue;
602
+ }
603
+ attributeOffsets[attrib] = offset >> 2;
604
+ for(let slot = 0; slot < textureTargetIndices.length; slot++){
605
+ const targetIndex = textureTargetIndices[slot];
606
+ const targetData = source.data[targetIndex];
607
+ const sparseIndices = source.indices?.[targetIndex];
608
+ const baseOffset = offset + slot * numVertices * 4;
609
+ if (sparseIndices && targetData) {
610
+ for(let i = 0; i < sparseIndices.length; i++){
611
+ const vertexOffset = baseOffset + sparseIndices[i] * 4;
612
+ for(let j = 0; j < source.numComponents; j++){
613
+ textureData[vertexOffset + j] = targetData[i * source.numComponents + j];
614
+ }
615
+ }
616
+ } else if (targetData) {
617
+ for(let vertex = 0; vertex < numVertices; vertex++){
618
+ const vertexOffset = baseOffset + vertex * 4;
619
+ for(let j = 0; j < source.numComponents; j++){
620
+ textureData[vertexOffset + j] = targetData[vertex * source.numComponents + j];
621
+ }
622
+ }
623
+ }
624
+ }
625
+ offset += numVertices * 4 * textureTargetIndices.length;
626
+ }
627
+ this.setMorphData({
628
+ width: textureSize,
629
+ height: textureSize,
630
+ data: textureData
631
+ });
632
+ this.updateRenderMorphInfo(activeIndices, this._compactMorphData, attributeOffsets);
633
+ this._activeMorphTargetIndices = activeIndices;
634
+ }
635
+ /** @internal */ updateRenderMorphInfo(activeIndices, useCompactSlots, attributeOffsets) {
636
+ if (!this._morphInfo || !this._morphData) {
637
+ this.setRenderMorphInfo(null);
638
+ return;
639
+ }
640
+ const infoData = this._renderMorphInfo?.data instanceof Float32Array && this._renderMorphInfo.data.length === MORPH_RENDER_INFO_DATA_LENGTH ? this._renderMorphInfo.data : new Float32Array(MORPH_RENDER_INFO_DATA_LENGTH);
641
+ infoData[0] = this._morphData.width;
642
+ infoData[1] = this._morphData.height;
643
+ infoData[2] = this._morphSourceData?.numVertices ?? (Number(this._morphInfo.data[2]) || 0);
644
+ infoData[3] = activeIndices.length;
645
+ infoData.fill(0, 4, MORPH_RENDER_ATTRIBUTE_OFFSET);
646
+ for(let slot = 0; slot < activeIndices.length; slot++){
647
+ const targetIndex = activeIndices[slot];
648
+ infoData[4 + slot] = this._morphInfo.data[4 + targetIndex];
649
+ infoData[MORPH_RENDER_INDEX_OFFSET + slot] = useCompactSlots ? slot : targetIndex;
650
+ }
651
+ if (attributeOffsets) {
652
+ infoData.fill(-1, MORPH_RENDER_ATTRIBUTE_OFFSET);
653
+ infoData.set(attributeOffsets, MORPH_RENDER_ATTRIBUTE_OFFSET);
654
+ } else if (!this._renderMorphInfo) {
655
+ infoData.fill(-1, MORPH_RENDER_ATTRIBUTE_OFFSET);
656
+ for(let attrib = 0; attrib < MORPH_ATTRIBUTE_CAPACITY; attrib++){
657
+ infoData[MORPH_RENDER_ATTRIBUTE_OFFSET + attrib] = this._morphInfo.data[4 + MORPH_WEIGHT_CAPACITY + attrib] ?? -1;
658
+ }
659
+ }
660
+ this.setRenderMorphInfo({
661
+ data: infoData,
662
+ names: this._renderMorphInfo?.names ?? {}
663
+ });
664
+ }
312
665
  /** @internal */ resolveAnimatedBoundingBox(morphBoundingBox) {
313
666
  const skinnedBoundingBox = this._boneMatrices.get() && this._skinnedBoundingInfo?.boundingBox?.isValid() ? this._skinnedBoundingInfo.boundingBox : null;
314
667
  if (skinnedBoundingBox && morphBoundingBox) {
315
- return skinnedBoundingBox.clone().union(morphBoundingBox);
668
+ // Skinning and morphing both active. The two boxes live in different
669
+ // frames: the skinned box already reflects where the geometry actually
670
+ // ended up, while the morph box is still expressed around the rest pose.
671
+ // Unioning them therefore spans the gap between rest and posed positions
672
+ // and produces a wildly oversized box - which is why that union was
673
+ // removed. But dropping the morph side entirely loses the displacement,
674
+ // since the skinned box is built from rest-pose representative vertices
675
+ // (SkinBinding.computeBoundingBox) and never sees morph offsets - so a
676
+ // large facial expression could reach outside its own bounds and be
677
+ // culled.
678
+ //
679
+ // Transfer only what is frame-independent: the displacement. Clamped so
680
+ // it can only grow the box, because a morph that pushes geometry one way
681
+ // must not be allowed to shrink the opposite side.
682
+ const box = skinnedBoundingBox.clone();
683
+ const displacement = this.calculateMorphDisplacement();
684
+ if (displacement) {
685
+ const min = box.minPoint;
686
+ const max = box.maxPoint;
687
+ const dmin = displacement.minPoint;
688
+ const dmax = displacement.maxPoint;
689
+ min.setXYZ(min.x + Math.min(0, dmin.x), min.y + Math.min(0, dmin.y), min.z + Math.min(0, dmin.z));
690
+ max.setXYZ(max.x + Math.max(0, dmax.x), max.y + Math.max(0, dmax.y), max.z + Math.max(0, dmax.z));
691
+ }
692
+ return box;
316
693
  }
317
694
  return skinnedBoundingBox?.clone() ?? morphBoundingBox ?? null;
318
695
  }
@@ -321,7 +698,7 @@ const MeshBase = castObservable(applyMixins(GraphNode, mixinDrawable))();
321
698
  *
322
699
  * @returns The number of morph targets
323
700
  */ getNumMorphTargets() {
324
- return this._morphInfo?.data[3] ?? 0;
701
+ return this._morphInfo ? Math.min(this._morphInfo.data[3], MORPH_WEIGHT_CAPACITY) : 0;
325
702
  }
326
703
  /**
327
704
  * Get the name of the morph target by index
@@ -329,7 +706,7 @@ const MeshBase = castObservable(applyMixins(GraphNode, mixinDrawable))();
329
706
  * @param index - The index of the morph target
330
707
  * @returns The name of the morph target, or null if not found
331
708
  */ getMorphTargetName(index) {
332
- if (this._morphInfo && index >= 0 && index < this._morphInfo.data[3]) {
709
+ if (this._morphInfo && index >= 0 && index < this.getNumMorphTargets()) {
333
710
  const name = Object.keys(this._morphInfo.names).find((key)=>this._morphInfo.names[key] === index);
334
711
  return name ?? null;
335
712
  }
@@ -359,9 +736,10 @@ const MeshBase = castObservable(applyMixins(GraphNode, mixinDrawable))();
359
736
  * @param index - The index of the morph target
360
737
  * @param weight - The weight of the morph target
361
738
  */ setMorphWeightByIndex(index, weight) {
362
- if (index >= 0 && index < this._morphInfo.data[3]) {
363
- if (this._morphInfo.data[4 + index] !== weight) {
364
- this._morphInfo.data[4 + index] = weight;
739
+ if (index >= 0 && index < this.getNumMorphTargets()) {
740
+ const normalizedWeight = Math.fround(weight);
741
+ if (this._morphInfo.data[4 + index] !== normalizedWeight) {
742
+ this._morphInfo.data[4 + index] = normalizedWeight;
365
743
  this._morphDirty = true;
366
744
  this.refreshAnimatedBoundingBox();
367
745
  this.scene.queueUpdateNode(this);
@@ -377,7 +755,7 @@ const MeshBase = castObservable(applyMixins(GraphNode, mixinDrawable))();
377
755
  * @returns The weight of the morph target, or 0 if not found
378
756
  */ getMorphWeight(name) {
379
757
  const index = this._morphInfo?.names?.[name];
380
- if (index !== undefined && index >= 0 && index < this._morphInfo.data[3]) {
758
+ if (index !== undefined && index >= 0 && index < this.getNumMorphTargets()) {
381
759
  return this._morphInfo.data[4 + index];
382
760
  }
383
761
  return 0;
@@ -387,8 +765,18 @@ const MeshBase = castObservable(applyMixins(GraphNode, mixinDrawable))();
387
765
  *
388
766
  * @param weight - The morph target weights. The length must not exceed the mesh's morph target count.
389
767
  */ updateMorphWeights(weight) {
390
- if (this._morphInfo && weight && weight.length <= this._morphInfo.data[3]) {
391
- this._morphInfo.data.set(weight, 4);
768
+ if (this._morphInfo && weight && weight.length <= this.getNumMorphTargets()) {
769
+ let changed = false;
770
+ for(let i = 0; i < weight.length; i++){
771
+ const normalizedWeight = Math.fround(weight[i] ?? 0);
772
+ if (this._morphInfo.data[4 + i] !== normalizedWeight) {
773
+ this._morphInfo.data[4 + i] = normalizedWeight;
774
+ changed = true;
775
+ }
776
+ }
777
+ if (!changed) {
778
+ return;
779
+ }
392
780
  this._morphDirty = true;
393
781
  this.refreshAnimatedBoundingBox();
394
782
  this.scene.queueUpdateNode(this);
@@ -439,17 +827,35 @@ const MeshBase = castObservable(applyMixins(GraphNode, mixinDrawable))();
439
827
  */ needSceneDepth() {
440
828
  return this.material?.needSceneDepth() ?? false;
441
829
  }
442
- /** @internal */ calculateMorphBoundingBox() {
830
+ /** @internal */ /**
831
+ * Weighted morph displacement, relative to the rest pose - i.e. how far the
832
+ * active morph targets push geometry beyond `originBox`, not where that
833
+ * geometry ends up.
834
+ *
835
+ * Kept separate from {@link calculateMorphBoundingBox} because the two are
836
+ * useful in different frames of reference: the absolute box is meaningful for
837
+ * an unskinned mesh, while for a skinned one only the displacement transfers -
838
+ * the rest-space position is superseded by the skinning result.
839
+ *
840
+ * @internal
841
+ */ calculateMorphDisplacement() {
443
842
  if (!this._morphInfo || !this._morphBoundingInfo) {
444
843
  return null;
445
844
  }
446
- const numTargets = Math.min(this._morphInfo.data[3], this._morphBoundingInfo.targetBoxes.length);
845
+ const numTargets = Math.min(this.getNumMorphTargets(), this._morphBoundingInfo.targetBoxes.length);
447
846
  if (numTargets <= 0) {
448
847
  return null;
449
848
  }
450
849
  const weights = this._morphInfo.data instanceof Float32Array ? this._morphInfo.data.subarray(4, 4 + numTargets) : new Float32Array(Array.from(this._morphInfo.data.subarray(4, 4 + numTargets)));
451
850
  const bbox = new BoundingBox();
452
851
  calculateMorphBoundingBox(bbox, this._morphBoundingInfo.targetBoxes, weights, numTargets);
852
+ return bbox;
853
+ }
854
+ /** @internal */ calculateMorphBoundingBox() {
855
+ const bbox = this.calculateMorphDisplacement();
856
+ if (!bbox) {
857
+ return null;
858
+ }
453
859
  bbox.minPoint.addBy(this._morphBoundingInfo.originBox.minPoint);
454
860
  bbox.maxPoint.addBy(this._morphBoundingInfo.originBox.maxPoint);
455
861
  return bbox;
@@ -459,7 +865,13 @@ const MeshBase = castObservable(applyMixins(GraphNode, mixinDrawable))();
459
865
  }
460
866
  /** @internal */ updateMorphState() {
461
867
  if (this._morphInfo && this._morphDirty) {
462
- this._morphInfo.buffer.get().bufferSubData(4 * 4, this._morphInfo.data, 4, this._morphInfo.data[3]);
868
+ if (this._morphSourceData) {
869
+ this.rebuildActiveMorphData();
870
+ } else if (this._morphData) {
871
+ const activeIndices = this.collectActiveMorphTargetIndices();
872
+ this.updateRenderMorphInfo(activeIndices, false);
873
+ this._activeMorphTargetIndices = activeIndices;
874
+ }
463
875
  this.refreshAnimatedBoundingBox();
464
876
  this._morphDirty = false;
465
877
  }
@@ -546,8 +958,11 @@ const MeshBase = castObservable(applyMixins(GraphNode, mixinDrawable))();
546
958
  this._primitive.dispose();
547
959
  this._material.dispose();
548
960
  this._boneMatrices.dispose();
961
+ this.setSkinInfluenceData(null);
549
962
  this.setMorphData(null);
963
+ this.setRenderMorphInfo(null);
550
964
  this.setMorphInfo(null);
965
+ this.setMorphSourceData(null);
551
966
  this.setMorphBoundingInfo(null);
552
967
  this._renderBundle = null;
553
968
  RenderBundleWrapper.drawableChanged(this);
@@ -555,6 +970,25 @@ const MeshBase = castObservable(applyMixins(GraphNode, mixinDrawable))();
555
970
  /** @internal */ _onBoundingboxChange() {
556
971
  this.invalidateBoundingVolume();
557
972
  }
973
+ /** @internal */ _ensureWebGLVertexIndexAttribute(feature) {
974
+ const primitive = this._primitive.get();
975
+ if (!primitive || getDevice().type !== 'webgl') {
976
+ return;
977
+ }
978
+ if (primitive.getVertexBuffer('texCoord7')) {
979
+ return;
980
+ }
981
+ const numVertices = primitive.getNumVertices();
982
+ if (numVertices <= 0) {
983
+ return;
984
+ }
985
+ const vertexIndices = new Float32Array(numVertices);
986
+ for(let i = 0; i < numVertices; i++){
987
+ vertexIndices[i] = i;
988
+ }
989
+ primitive.createAndSetVertexBuffer('tex7_f32', vertexIndices);
990
+ console.info(`Injected texCoord7 vertex indices for WebGL ${feature} on mesh "${this.name ?? ''}"`);
991
+ }
558
992
  /** @internal */ static _defaultMaterial = null;
559
993
  /** @internal */ static _getDefaultMaterial() {
560
994
  if (!this._defaultMaterial) {