@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,7 +1,7 @@
1
1
  import { EPSILON } from './types.js';
2
2
  import { buildConstraints, buildSurfaceFaces } from './constraints.js';
3
3
  import { simulate, applyAngleLimits, applyResult } from './solver.js';
4
- import { Vector3, Quaternion, Matrix4x4, clamp01 } from '@zephyr3d/base';
4
+ import { Vector3, Quaternion, Matrix4x4, clamp01, InterpolatorScalar } from '@zephyr3d/base';
5
5
 
6
6
  // High-level orchestrator - SPCRJointDynamicsController API
7
7
  /**
@@ -33,6 +33,10 @@ import { Vector3, Quaternion, Matrix4x4, clamp01 } from '@zephyr3d/base';
33
33
  _previousRootPosition = Vector3.zero();
34
34
  _previousRootRotation = Quaternion.identity();
35
35
  _rootTransform = null;
36
+ _rootPoints = [];
37
+ _allPoints = [];
38
+ _parentMap = new Map();
39
+ _maxPointDepth = 0;
36
40
  _pointTransforms = [];
37
41
  _colliderTransforms = [];
38
42
  _grabberTransforms = [];
@@ -42,6 +46,10 @@ import { Vector3, Quaternion, Matrix4x4, clamp01 } from '@zephyr3d/base';
42
46
  _colliderHandleToIndex = new Map();
43
47
  _flatPlaneHandleToIndex = new Map();
44
48
  _grabberHandleToIndex = new Map();
49
+ _baseSystemScale = 1;
50
+ _currentSystemScale = 1;
51
+ _basePointParentLengths = [];
52
+ _baseConstraintLengths = [];
45
53
  _nextColliderHandleId = 1;
46
54
  _nextFlatPlaneHandleId = 1;
47
55
  _nextGrabberHandleId = 1;
@@ -51,7 +59,7 @@ import { Vector3, Quaternion, Matrix4x4, clamp01 } from '@zephyr3d/base';
51
59
  _fadeTimer = 0;
52
60
  _fadeDuration = 0;
53
61
  constructor(config){
54
- this._config = config;
62
+ this._config = this._sanitizeControllerConfig(this._cloneControllerConfig(config));
55
63
  }
56
64
  /**
57
65
  * Initializes the physics system.
@@ -63,32 +71,34 @@ import { Vector3, Quaternion, Matrix4x4, clamp01 } from '@zephyr3d/base';
63
71
  * @param flatPlanes - Plane limiters such as the floor to prevent penetration.
64
72
  */ initialize(rootTransform, rootPoints, pointTransforms, colliders, grabbers, flatPlanes) {
65
73
  this._rootTransform = rootTransform;
74
+ this._baseSystemScale = this._getSystemUniformScale();
75
+ this._currentSystemScale = this._baseSystemScale;
76
+ this._rootPoints = [
77
+ ...rootPoints
78
+ ];
66
79
  this._pointTransforms = pointTransforms;
67
80
  this._colliderTransforms = colliders.map((c)=>c.transform);
68
81
  this._grabberTransforms = grabbers.map((g)=>g.transform);
69
82
  // Build constraints
70
- this._constraints = buildConstraints(rootPoints, this._config.constraintOptions);
71
- this._surfaceConstraints = buildSurfaceFaces(rootPoints, this._config.constraintOptions.isLoop);
83
+ this._rebuildConstraints();
84
+ this._baseConstraintLengths = this._constraints.map((constraint)=>constraint.length);
72
85
  // Flatten bone hierarchy to point list, build parent map
73
86
  const allPoints = [];
74
- const parentMap = new Map(); // child index -> parent index
75
- function walk(node) {
87
+ this._parentMap = new Map(); // child index -> parent index
88
+ const walk = (node)=>{
76
89
  allPoints.push(node);
77
90
  for (const c of node.children){
78
- parentMap.set(c.index, node.index);
91
+ this._parentMap.set(c.index, node.index);
79
92
  walk(c);
80
93
  }
81
- }
94
+ };
82
95
  for (const r of rootPoints){
83
96
  walk(r);
84
97
  }
85
- const maxDepth = Math.max(...allPoints.map((p)=>p.depth));
98
+ this._allPoints = allPoints;
99
+ this._maxPointDepth = allPoints.length > 0 ? Math.max(...allPoints.map((p)=>p.depth)) : 0;
86
100
  // Compute boneAxis from transforms: local-space direction to first child
87
101
  // This is critical for correct bone rotation in skinned meshes
88
- const indexToNode = new Map();
89
- for (const p of allPoints){
90
- indexToNode.set(p.index, p);
91
- }
92
102
  for (const p of allPoints){
93
103
  if (!p.boneAxis && p.children.length > 0) {
94
104
  const childPos = pointTransforms[p.children[0].index].getWorldPosition();
@@ -105,7 +115,8 @@ import { Vector3, Quaternion, Matrix4x4, clamp01 } from '@zephyr3d/base';
105
115
  }
106
116
  }
107
117
  // Build PointR/RW arrays
108
- this._pointsR = allPoints.map((p)=>this._createPointR(p, maxDepth, parentMap, pointTransforms));
118
+ this._pointsR = allPoints.map((p)=>this._createPointR(p));
119
+ this._basePointParentLengths = this._pointsR.map((point)=>point.parentLength);
109
120
  this._pointsRW = allPoints.map(()=>this._createPointRW());
110
121
  this._positionsToTransform = new Array(allPoints.length).fill(Vector3.zero());
111
122
  // Initialize colliders
@@ -168,6 +179,7 @@ import { Vector3, Quaternion, Matrix4x4, clamp01 } from '@zephyr3d/base';
168
179
  }
169
180
  }
170
181
  const blendRatio = this._computeBlendRatio();
182
+ this._updateScaleDependentParameters();
171
183
  // Capture current transforms
172
184
  const rootPos = this._rootTransform.getWorldPosition();
173
185
  const rootRot = this._rootTransform.getWorldRotation();
@@ -243,6 +255,189 @@ import { Vector3, Quaternion, Matrix4x4, clamp01 } from '@zephyr3d/base';
243
255
  }));
244
256
  }
245
257
  /**
258
+ * Returns a detached snapshot of the current runtime configuration.
259
+ */ getConfig() {
260
+ return this._cloneControllerConfig(this._config);
261
+ }
262
+ /**
263
+ * Returns detached collider snapshots for persistence.
264
+ */ getColliderSnapshots() {
265
+ return this._collidersR.map((r, index)=>({
266
+ r: {
267
+ ...r
268
+ },
269
+ transform: this._colliderTransforms[index]?.node ?? null,
270
+ enabled: this._collidersRW[index]?.enabled !== 0
271
+ }));
272
+ }
273
+ /**
274
+ * Returns detached flat-plane snapshots for persistence.
275
+ */ getFlatPlaneSnapshots() {
276
+ return this._flatPlaneAll.map((plane, index)=>({
277
+ up: plane.normal.clone(),
278
+ position: Vector3.scale(plane.normal, -plane.distance),
279
+ enabled: this._flatPlaneEnabled[index] ?? true
280
+ }));
281
+ }
282
+ /**
283
+ * Returns detached grabber snapshots for persistence.
284
+ */ getGrabberSnapshots() {
285
+ return this._grabbersR.map((r, index)=>({
286
+ r: {
287
+ ...r
288
+ },
289
+ transform: this._grabberTransforms[index]?.node ?? null,
290
+ enabled: this._grabbersRW[index]?.enabled !== 0
291
+ }));
292
+ }
293
+ /**
294
+ * Replaces the runtime configuration.
295
+ *
296
+ * If the controller is already initialized, cached per-point parameters and generated
297
+ * constraints are refreshed immediately so editor-driven changes take effect in the next step.
298
+ *
299
+ * @param config - Complete controller configuration to apply.
300
+ */ setConfig(config) {
301
+ this._config = this._sanitizeControllerConfig(this._cloneControllerConfig(config));
302
+ this._refreshCachedSimulationConfig();
303
+ }
304
+ /**
305
+ * Applies a partial runtime configuration update.
306
+ *
307
+ * This is intended for editor workflows that tweak one or more simulation parameters live.
308
+ * The controller updates only the affected caches: global step parameters are applied directly,
309
+ * per-point curve/gravity changes rebuild cached point coefficients, and constraint option
310
+ * changes rebuild the generated constraints.
311
+ *
312
+ * @param config - Partial configuration update.
313
+ */ updateConfig(config) {
314
+ let refreshPoints = false;
315
+ let refreshConstraints = false;
316
+ if (config.gravity) {
317
+ this._config.gravity = config.gravity.clone();
318
+ refreshPoints = true;
319
+ }
320
+ if (config.windForce) {
321
+ this._config.windForce = config.windForce.clone();
322
+ }
323
+ if (typeof config.relaxation === 'number') {
324
+ this._config.relaxation = Math.max(0, Math.trunc(config.relaxation));
325
+ }
326
+ if (typeof config.subSteps === 'number') {
327
+ this._config.subSteps = Math.max(1, Math.trunc(config.subSteps));
328
+ }
329
+ if (typeof config.rootSlideLimit === 'number') {
330
+ this._config.rootSlideLimit = config.rootSlideLimit;
331
+ }
332
+ if (typeof config.rootRotateLimit === 'number') {
333
+ this._config.rootRotateLimit = config.rootRotateLimit;
334
+ }
335
+ if (typeof config.constraintShrinkLimit === 'number') {
336
+ this._config.constraintShrinkLimit = Math.max(0, config.constraintShrinkLimit);
337
+ }
338
+ if (typeof config.blendRatio === 'number') {
339
+ this._config.blendRatio = clamp01(config.blendRatio);
340
+ }
341
+ if (typeof config.stabilizationFrameRate === 'number') {
342
+ this._config.stabilizationFrameRate = config.stabilizationFrameRate;
343
+ }
344
+ if (typeof config.isFakeWave === 'boolean') {
345
+ this._config.isFakeWave = config.isFakeWave;
346
+ }
347
+ if (typeof config.fakeWaveSpeed === 'number') {
348
+ this._config.fakeWaveSpeed = config.fakeWaveSpeed;
349
+ }
350
+ if (typeof config.fakeWavePower === 'number') {
351
+ this._config.fakeWavePower = config.fakeWavePower;
352
+ }
353
+ if (typeof config.enableSurfaceCollision === 'boolean') {
354
+ this._config.enableSurfaceCollision = config.enableSurfaceCollision;
355
+ if (!config.constraintOptions || config.constraintOptions.enableSurfaceCollision === undefined) {
356
+ this._config.constraintOptions.enableSurfaceCollision = config.enableSurfaceCollision;
357
+ refreshConstraints = true;
358
+ }
359
+ }
360
+ if (typeof config.enableBroadPhase === 'boolean') {
361
+ this._config.enableBroadPhase = config.enableBroadPhase;
362
+ }
363
+ if (typeof config.preserveTwist === 'boolean') {
364
+ this._config.preserveTwist = config.preserveTwist;
365
+ }
366
+ if (config.angleLimitConfig) {
367
+ if (typeof config.angleLimitConfig.angleLimit === 'number') {
368
+ this._config.angleLimitConfig.angleLimit = config.angleLimitConfig.angleLimit;
369
+ }
370
+ if (typeof config.angleLimitConfig.limitFromRoot === 'boolean') {
371
+ this._config.angleLimitConfig.limitFromRoot = config.angleLimitConfig.limitFromRoot;
372
+ }
373
+ }
374
+ if (config.curves) {
375
+ const curveKeys = [
376
+ 'massScale',
377
+ 'gravityScale',
378
+ 'windForceScale',
379
+ 'resistance',
380
+ 'hardness',
381
+ 'friction',
382
+ 'pointRadius',
383
+ 'sliderJointLength',
384
+ 'allShrinkScale',
385
+ 'allStretchScale',
386
+ 'structuralShrinkVertical',
387
+ 'structuralStretchVertical',
388
+ 'structuralShrinkHorizontal',
389
+ 'structuralStretchHorizontal',
390
+ 'shearShrink',
391
+ 'shearStretch',
392
+ 'bendingShrinkVertical',
393
+ 'bendingStretchVertical',
394
+ 'bendingShrinkHorizontal',
395
+ 'bendingStretchHorizontal',
396
+ 'fakeWavePower',
397
+ 'fakeWaveFreq'
398
+ ];
399
+ for (const key of curveKeys){
400
+ const curve = config.curves[key];
401
+ if (curve !== undefined) {
402
+ this._config.curves[key] = this._cloneInterpolatorScalar(curve);
403
+ refreshPoints = true;
404
+ }
405
+ }
406
+ }
407
+ if (config.constraintOptions) {
408
+ const optionKeys = [
409
+ 'structuralVertical',
410
+ 'structuralHorizontal',
411
+ 'shear',
412
+ 'bendingVertical',
413
+ 'bendingHorizontal',
414
+ 'isLoop',
415
+ 'collideStructuralVertical',
416
+ 'collideStructuralHorizontal',
417
+ 'collideShear',
418
+ 'enableSurfaceCollision'
419
+ ];
420
+ for (const key of optionKeys){
421
+ const value = config.constraintOptions[key];
422
+ if (value !== undefined) {
423
+ this._config.constraintOptions[key] = value;
424
+ if (key === 'enableSurfaceCollision' && config.enableSurfaceCollision === undefined) {
425
+ this._config.enableSurfaceCollision = value;
426
+ }
427
+ refreshConstraints = true;
428
+ }
429
+ }
430
+ }
431
+ if (refreshConstraints) {
432
+ this._rebuildConstraints();
433
+ this._baseConstraintLengths = this._constraints.map((constraint)=>constraint.length);
434
+ this._updateScaleDependentParameters(true);
435
+ }
436
+ if (refreshPoints) {
437
+ this._refreshPointParameters();
438
+ }
439
+ }
440
+ /**
246
441
  * Compensates for teleportation by resetting the previous root transform to the current one.
247
442
  * Call this after a character warp or teleport to avoid a large root-motion impulse on the
248
443
  * next simulation step.
@@ -326,7 +521,7 @@ import { Vector3, Quaternion, Matrix4x4, clamp01 } from '@zephyr3d/base';
326
521
  * The final wind contribution is still scaled per point by `windForceScale` and mass.
327
522
  * @param wind - Wind vector in world space.
328
523
  */ setWindForce(wind) {
329
- this._config.windForce = wind;
524
+ this._config.windForce = wind.clone();
330
525
  }
331
526
  /**
332
527
  * Enable/disable broad-phase pruning for runtime performance comparison
@@ -595,6 +790,10 @@ import { Vector3, Quaternion, Matrix4x4, clamp01 } from '@zephyr3d/base';
595
790
  }
596
791
  return this._config.blendRatio;
597
792
  }
793
+ _refreshCachedSimulationConfig() {
794
+ this._rebuildConstraints();
795
+ this._refreshPointParameters();
796
+ }
598
797
  _getColliderIndex(handle) {
599
798
  if (handle.type !== 'collider') {
600
799
  return -1;
@@ -647,13 +846,61 @@ import { Vector3, Quaternion, Matrix4x4, clamp01 } from '@zephyr3d/base';
647
846
  this._grabberHandleToIndex.set(this._grabberHandleIds[i], i);
648
847
  }
649
848
  }
650
- _createPointR(node, maxDepth, parentMap, transforms) {
651
- const rate = maxDepth > 0 ? node.depth / maxDepth : 0;
652
- const c = this._config.curves;
653
- const parentIdx = parentMap.get(node.index) ?? -1;
849
+ _rebuildConstraints() {
850
+ if (this._rootPoints.length === 0) {
851
+ this._constraints = [];
852
+ this._surfaceConstraints = [];
853
+ return;
854
+ }
855
+ this._constraints = buildConstraints(this._rootPoints, this._config.constraintOptions);
856
+ this._surfaceConstraints = buildSurfaceFaces(this._rootPoints, this._config.constraintOptions.isLoop);
857
+ }
858
+ _getSystemUniformScale() {
859
+ if (!this._rootTransform) {
860
+ return 1;
861
+ }
862
+ const scale = this._rootTransform.getWorldScale();
863
+ return Math.max((Math.abs(scale.x) + Math.abs(scale.y) + Math.abs(scale.z)) / 3, EPSILON);
864
+ }
865
+ _updateScaleDependentParameters(force = false) {
866
+ const systemScale = this._getSystemUniformScale();
867
+ const scaleChanged = Math.abs(systemScale - this._currentSystemScale) > EPSILON;
868
+ if (!force && !scaleChanged) {
869
+ return;
870
+ }
871
+ this._currentSystemScale = systemScale;
872
+ const ratio = this._baseSystemScale > EPSILON ? systemScale / this._baseSystemScale : 1;
873
+ for(let i = 0; i < this._pointsR.length; i++){
874
+ this._pointsR[i].parentLength = (this._basePointParentLengths[i] ?? this._pointsR[i].parentLength) * ratio;
875
+ }
876
+ for(let i = 0; i < this._constraints.length; i++){
877
+ this._constraints[i].length = (this._baseConstraintLengths[i] ?? this._constraints[i].length) * ratio;
878
+ }
879
+ this._refreshPointParameters();
880
+ if (scaleChanged) {
881
+ for(let i = 0; i < this._pointsRW.length; i++){
882
+ const pos = this._pointTransforms[i].getWorldPosition();
883
+ this._pointsRW[i].positionCurrent = pos.clone();
884
+ this._pointsRW[i].positionPrevious = pos.clone();
885
+ this._pointsRW[i].positionCurrentTransform = pos.clone();
886
+ this._pointsRW[i].positionPreviousTransform = pos.clone();
887
+ }
888
+ this.warp();
889
+ }
890
+ }
891
+ _refreshPointParameters() {
892
+ if (!this._initialized) {
893
+ return;
894
+ }
895
+ for(let i = 0; i < this._allPoints.length; i++){
896
+ this._applyConfigToPoint(this._pointsR[i], this._allPoints[i]);
897
+ }
898
+ }
899
+ _createPointR(node) {
900
+ const parentIdx = this._parentMap.get(node.index) ?? -1;
654
901
  const childIdx = node.children.length > 0 ? node.children[0].index : -1;
655
902
  // Capture initial local transform for rotation blending
656
- const t = transforms[node.index];
903
+ const t = this._pointTransforms[node.index];
657
904
  const initLocalPos = t.getLocalPosition();
658
905
  const initLocalRot = t.getLocalRotation();
659
906
  const initLocalScale = t.getLocalScale();
@@ -662,39 +909,39 @@ import { Vector3, Quaternion, Matrix4x4, clamp01 } from '@zephyr3d/base';
662
909
  // ParentLength: distance to parent
663
910
  let parentLength = 0;
664
911
  if (parentIdx !== -1) {
665
- const pPos = transforms[parentIdx].getWorldPosition();
666
- const cPos = transforms[node.index].getWorldPosition();
912
+ const pPos = this._pointTransforms[parentIdx].getWorldPosition();
913
+ const cPos = this._pointTransforms[node.index].getWorldPosition();
667
914
  parentLength = Vector3.distance(pPos, cPos);
668
915
  }
669
- return {
916
+ const point = {
670
917
  parent: parentIdx,
671
918
  child: childIdx,
672
919
  applyInvertCollision: 0,
673
920
  movableLimitIndex: -1,
674
921
  movableLimitRadius: 0,
675
922
  weight: node.isFixed ? 0 : 1,
676
- mass: c.massScale.evaluate(rate),
677
- resistance: clamp01(c.resistance.evaluate(rate)),
678
- hardness: clamp01(c.hardness.evaluate(rate)),
679
- frictionScale: c.friction.evaluate(rate),
680
- sliderJointLength: c.sliderJointLength.evaluate(rate),
923
+ mass: 0,
924
+ resistance: 0,
925
+ hardness: 0,
926
+ frictionScale: 0,
927
+ sliderJointLength: 0,
681
928
  parentLength,
682
- structuralShrinkVertical: c.structuralShrinkVertical.evaluate(rate) * 0.5,
683
- structuralStretchVertical: c.structuralStretchVertical.evaluate(rate) * 0.5,
684
- structuralShrinkHorizontal: c.structuralShrinkHorizontal.evaluate(rate) * 0.5,
685
- structuralStretchHorizontal: c.structuralStretchHorizontal.evaluate(rate) * 0.5,
686
- shearShrink: c.shearShrink.evaluate(rate) * 0.5,
687
- shearStretch: c.shearStretch.evaluate(rate) * 0.5,
688
- bendingShrinkVertical: c.bendingShrinkVertical.evaluate(rate) * 0.5,
689
- bendingStretchVertical: c.bendingStretchVertical.evaluate(rate) * 0.5,
690
- bendingShrinkHorizontal: c.bendingShrinkHorizontal.evaluate(rate) * 0.5,
691
- bendingStretchHorizontal: c.bendingStretchHorizontal.evaluate(rate) * 0.5,
692
- windForceScale: c.windForceScale.evaluate(rate) * rate,
693
- fakeWavePower: c.fakeWavePower.evaluate(rate),
694
- fakeWaveFreq: c.fakeWaveFreq.evaluate(rate),
929
+ structuralShrinkVertical: 0,
930
+ structuralStretchVertical: 0,
931
+ structuralShrinkHorizontal: 0,
932
+ structuralStretchHorizontal: 0,
933
+ shearShrink: 0,
934
+ shearStretch: 0,
935
+ bendingShrinkVertical: 0,
936
+ bendingStretchVertical: 0,
937
+ bendingShrinkHorizontal: 0,
938
+ bendingStretchHorizontal: 0,
939
+ windForceScale: 0,
940
+ fakeWavePower: 0,
941
+ fakeWaveFreq: 0,
695
942
  forceFadeRatio: 0,
696
- pointRadius: Math.max(0, c.pointRadius.evaluate(rate)),
697
- gravity: Vector3.scale(this._config.gravity, c.gravityScale.evaluate(rate)),
943
+ pointRadius: 0,
944
+ gravity: Vector3.zero(),
698
945
  boneAxis,
699
946
  initialLocalScale: initLocalScale,
700
947
  initialLocalRotation: initLocalRot,
@@ -705,6 +952,35 @@ import { Vector3, Quaternion, Matrix4x4, clamp01 } from '@zephyr3d/base';
705
952
  })(),
706
953
  initialLocalPosition: initLocalPos
707
954
  };
955
+ this._applyConfigToPoint(point, node);
956
+ return point;
957
+ }
958
+ _applyConfigToPoint(point, node) {
959
+ const rate = this._maxPointDepth > 0 ? node.depth / this._maxPointDepth : 0;
960
+ const c = this._config.curves;
961
+ const allShrinkScale = c.allShrinkScale.evaluate(rate);
962
+ const allStretchScale = c.allStretchScale.evaluate(rate);
963
+ point.mass = c.massScale.evaluate(rate);
964
+ point.resistance = clamp01(c.resistance.evaluate(rate));
965
+ point.hardness = clamp01(c.hardness.evaluate(rate));
966
+ point.frictionScale = c.friction.evaluate(rate);
967
+ point.sliderJointLength = c.sliderJointLength.evaluate(rate);
968
+ point.structuralShrinkVertical = c.structuralShrinkVertical.evaluate(rate) * allShrinkScale * 0.5;
969
+ point.structuralStretchVertical = c.structuralStretchVertical.evaluate(rate) * allStretchScale * 0.5;
970
+ point.structuralShrinkHorizontal = c.structuralShrinkHorizontal.evaluate(rate) * allShrinkScale * 0.5;
971
+ point.structuralStretchHorizontal = c.structuralStretchHorizontal.evaluate(rate) * allStretchScale * 0.5;
972
+ point.shearShrink = c.shearShrink.evaluate(rate) * allShrinkScale * 0.5;
973
+ point.shearStretch = c.shearStretch.evaluate(rate) * allStretchScale * 0.5;
974
+ point.bendingShrinkVertical = c.bendingShrinkVertical.evaluate(rate) * allShrinkScale * 0.5;
975
+ point.bendingStretchVertical = c.bendingStretchVertical.evaluate(rate) * allStretchScale * 0.5;
976
+ point.bendingShrinkHorizontal = c.bendingShrinkHorizontal.evaluate(rate) * allShrinkScale * 0.5;
977
+ point.bendingStretchHorizontal = c.bendingStretchHorizontal.evaluate(rate) * allStretchScale * 0.5;
978
+ point.windForceScale = c.windForceScale.evaluate(rate) * rate;
979
+ point.fakeWavePower = c.fakeWavePower.evaluate(rate);
980
+ point.fakeWaveFreq = c.fakeWaveFreq.evaluate(rate);
981
+ const systemScale = this._currentSystemScale;
982
+ point.pointRadius = Math.max(0, c.pointRadius.evaluate(rate) * systemScale);
983
+ point.gravity = Vector3.scale(this._config.gravity, c.gravityScale.evaluate(rate) * systemScale);
708
984
  }
709
985
  _createPointRW() {
710
986
  return {
@@ -738,9 +1014,80 @@ import { Vector3, Quaternion, Matrix4x4, clamp01 } from '@zephyr3d/base';
738
1014
  localBoundsMin: Vector3.zero(),
739
1015
  localBoundsMax: Vector3.zero(),
740
1016
  radius: 0,
1017
+ height: 0,
741
1018
  enabled: 1
742
1019
  };
743
1020
  }
1021
+ _cloneControllerConfig(config) {
1022
+ const curves = {
1023
+ massScale: this._cloneInterpolatorScalar(config.curves.massScale),
1024
+ gravityScale: this._cloneInterpolatorScalar(config.curves.gravityScale),
1025
+ windForceScale: this._cloneInterpolatorScalar(config.curves.windForceScale),
1026
+ resistance: this._cloneInterpolatorScalar(config.curves.resistance),
1027
+ hardness: this._cloneInterpolatorScalar(config.curves.hardness),
1028
+ friction: this._cloneInterpolatorScalar(config.curves.friction),
1029
+ pointRadius: this._cloneInterpolatorScalar(config.curves.pointRadius),
1030
+ sliderJointLength: this._cloneInterpolatorScalar(config.curves.sliderJointLength),
1031
+ allShrinkScale: this._cloneInterpolatorScalar(config.curves.allShrinkScale),
1032
+ allStretchScale: this._cloneInterpolatorScalar(config.curves.allStretchScale),
1033
+ structuralShrinkVertical: this._cloneInterpolatorScalar(config.curves.structuralShrinkVertical),
1034
+ structuralStretchVertical: this._cloneInterpolatorScalar(config.curves.structuralStretchVertical),
1035
+ structuralShrinkHorizontal: this._cloneInterpolatorScalar(config.curves.structuralShrinkHorizontal),
1036
+ structuralStretchHorizontal: this._cloneInterpolatorScalar(config.curves.structuralStretchHorizontal),
1037
+ shearShrink: this._cloneInterpolatorScalar(config.curves.shearShrink),
1038
+ shearStretch: this._cloneInterpolatorScalar(config.curves.shearStretch),
1039
+ bendingShrinkVertical: this._cloneInterpolatorScalar(config.curves.bendingShrinkVertical),
1040
+ bendingStretchVertical: this._cloneInterpolatorScalar(config.curves.bendingStretchVertical),
1041
+ bendingShrinkHorizontal: this._cloneInterpolatorScalar(config.curves.bendingShrinkHorizontal),
1042
+ bendingStretchHorizontal: this._cloneInterpolatorScalar(config.curves.bendingStretchHorizontal),
1043
+ fakeWavePower: this._cloneInterpolatorScalar(config.curves.fakeWavePower),
1044
+ fakeWaveFreq: this._cloneInterpolatorScalar(config.curves.fakeWaveFreq)
1045
+ };
1046
+ return {
1047
+ gravity: config.gravity.clone(),
1048
+ windForce: config.windForce.clone(),
1049
+ relaxation: config.relaxation,
1050
+ subSteps: config.subSteps,
1051
+ rootSlideLimit: config.rootSlideLimit,
1052
+ rootRotateLimit: config.rootRotateLimit,
1053
+ constraintShrinkLimit: config.constraintShrinkLimit,
1054
+ blendRatio: config.blendRatio,
1055
+ stabilizationFrameRate: config.stabilizationFrameRate,
1056
+ isFakeWave: config.isFakeWave,
1057
+ fakeWaveSpeed: config.fakeWaveSpeed,
1058
+ fakeWavePower: config.fakeWavePower,
1059
+ enableSurfaceCollision: config.enableSurfaceCollision,
1060
+ enableBroadPhase: config.enableBroadPhase,
1061
+ preserveTwist: config.preserveTwist,
1062
+ angleLimitConfig: {
1063
+ angleLimit: config.angleLimitConfig.angleLimit,
1064
+ limitFromRoot: config.angleLimitConfig.limitFromRoot
1065
+ },
1066
+ curves,
1067
+ constraintOptions: {
1068
+ structuralVertical: config.constraintOptions.structuralVertical,
1069
+ structuralHorizontal: config.constraintOptions.structuralHorizontal,
1070
+ shear: config.constraintOptions.shear,
1071
+ bendingVertical: config.constraintOptions.bendingVertical,
1072
+ bendingHorizontal: config.constraintOptions.bendingHorizontal,
1073
+ isLoop: config.constraintOptions.isLoop,
1074
+ collideStructuralVertical: config.constraintOptions.collideStructuralVertical,
1075
+ collideStructuralHorizontal: config.constraintOptions.collideStructuralHorizontal,
1076
+ collideShear: config.constraintOptions.collideShear,
1077
+ enableSurfaceCollision: config.constraintOptions.enableSurfaceCollision
1078
+ }
1079
+ };
1080
+ }
1081
+ _cloneInterpolatorScalar(curve) {
1082
+ return new InterpolatorScalar(curve.mode, new Float32Array(curve.inputs), new Float32Array(curve.outputs));
1083
+ }
1084
+ _sanitizeControllerConfig(config) {
1085
+ config.relaxation = Math.max(0, Math.trunc(config.relaxation));
1086
+ config.subSteps = Math.max(1, Math.trunc(config.subSteps));
1087
+ config.constraintShrinkLimit = Math.max(0, config.constraintShrinkLimit);
1088
+ config.blendRatio = clamp01(config.blendRatio);
1089
+ return config;
1090
+ }
744
1091
  }
745
1092
 
746
1093
  export { JointDynamicsSystemController };