@holoscript/engine 6.0.3 → 6.0.4

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 (192) hide show
  1. package/dist/AutoMesher-CK47F6AV.js +17 -0
  2. package/dist/GPUBuffers-2LHBCD7X.js +9 -0
  3. package/dist/WebGPUContext-TNEUYU2Y.js +11 -0
  4. package/dist/animation/index.cjs +38 -38
  5. package/dist/animation/index.d.cts +1 -1
  6. package/dist/animation/index.d.ts +1 -1
  7. package/dist/animation/index.js +1 -1
  8. package/dist/audio/index.cjs +16 -6
  9. package/dist/audio/index.d.cts +1 -1
  10. package/dist/audio/index.d.ts +1 -1
  11. package/dist/audio/index.js +1 -1
  12. package/dist/camera/index.cjs +23 -23
  13. package/dist/camera/index.d.cts +1 -1
  14. package/dist/camera/index.d.ts +1 -1
  15. package/dist/camera/index.js +1 -1
  16. package/dist/character/index.cjs +6 -4
  17. package/dist/character/index.js +1 -1
  18. package/dist/choreography/index.cjs +1194 -0
  19. package/dist/choreography/index.d.cts +687 -0
  20. package/dist/choreography/index.d.ts +687 -0
  21. package/dist/choreography/index.js +1156 -0
  22. package/dist/chunk-2CSNRI2N.js +217 -0
  23. package/dist/chunk-33T2WINR.js +266 -0
  24. package/dist/chunk-35R73OFM.js +1257 -0
  25. package/dist/chunk-4MMDSUNP.js +1256 -0
  26. package/dist/chunk-5V6HOU72.js +319 -0
  27. package/dist/chunk-6QOP6PYF.js +1038 -0
  28. package/dist/chunk-7KMJVHIL.js +8944 -0
  29. package/dist/chunk-7VPUC62U.js +1106 -0
  30. package/dist/chunk-A2Y6RCAT.js +1878 -0
  31. package/dist/chunk-AHM42MK6.js +8944 -0
  32. package/dist/chunk-BL7IDTHE.js +218 -0
  33. package/dist/chunk-CITOMSWL.js +10462 -0
  34. package/dist/chunk-CXDPKW2K.js +8944 -0
  35. package/dist/chunk-CXZPLD4S.js +223 -0
  36. package/dist/chunk-CZYJE7IH.js +5169 -0
  37. package/dist/chunk-D2OP7YC7.js +6325 -0
  38. package/dist/chunk-EDRVQHUU.js +1544 -0
  39. package/dist/chunk-EJSLOOW2.js +3589 -0
  40. package/dist/chunk-F53SFGW5.js +1878 -0
  41. package/dist/chunk-HCFPELPY.js +919 -0
  42. package/dist/chunk-HNEE36PY.js +93 -0
  43. package/dist/chunk-HYXNV36F.js +1256 -0
  44. package/dist/chunk-IB7KHVFY.js +821 -0
  45. package/dist/chunk-IBBO7YYG.js +690 -0
  46. package/dist/chunk-ILIBGINU.js +5470 -0
  47. package/dist/chunk-IS4MHLKN.js +5479 -0
  48. package/dist/chunk-JT2PFKWD.js +5479 -0
  49. package/dist/chunk-K4CUB4NY.js +1038 -0
  50. package/dist/chunk-KATDQXRJ.js +10462 -0
  51. package/dist/chunk-KBQE6ZFJ.js +8944 -0
  52. package/dist/chunk-KBVD5K7E.js +560 -0
  53. package/dist/chunk-KCDPVQRY.js +4088 -0
  54. package/dist/chunk-KN4QJPKN.js +8944 -0
  55. package/dist/chunk-KWJ3ROSI.js +8944 -0
  56. package/dist/chunk-L45VF6DD.js +919 -0
  57. package/dist/chunk-LY4T37YK.js +307 -0
  58. package/dist/chunk-MDN5WZXA.js +1544 -0
  59. package/dist/chunk-MGCDP6VU.js +928 -0
  60. package/dist/chunk-NCX7X6G2.js +8681 -0
  61. package/dist/chunk-OF54BPVD.js +913 -0
  62. package/dist/chunk-OWSN2Q3Q.js +690 -0
  63. package/dist/chunk-PRRB5TTA.js +406 -0
  64. package/dist/chunk-PXWVQF76.js +4086 -0
  65. package/dist/chunk-PYCOIDT2.js +812 -0
  66. package/dist/chunk-PZCSADOV.js +928 -0
  67. package/dist/chunk-Q2XBVS2K.js +1038 -0
  68. package/dist/chunk-QDZRXWN5.js +1776 -0
  69. package/dist/chunk-RNWOZ6WQ.js +913 -0
  70. package/dist/chunk-ROLFT4CJ.js +1693 -0
  71. package/dist/chunk-SLTJRZ2N.js +266 -0
  72. package/dist/chunk-SRUS5XSU.js +4088 -0
  73. package/dist/chunk-TKCA3WZ5.js +5409 -0
  74. package/dist/chunk-TNRMXYI2.js +1650 -0
  75. package/dist/chunk-TQB3GJGM.js +9763 -0
  76. package/dist/chunk-TUFGXG6K.js +510 -0
  77. package/dist/chunk-U6KMTGQJ.js +632 -0
  78. package/dist/chunk-VMGJQST6.js +8681 -0
  79. package/dist/chunk-X4F4TCG4.js +5470 -0
  80. package/dist/chunk-ZIFROE75.js +1544 -0
  81. package/dist/chunk-ZIJQYHSQ.js +1204 -0
  82. package/dist/combat/index.cjs +4 -4
  83. package/dist/combat/index.d.cts +1 -1
  84. package/dist/combat/index.d.ts +1 -1
  85. package/dist/combat/index.js +1 -1
  86. package/dist/ecs/index.cjs +1 -1
  87. package/dist/ecs/index.js +1 -1
  88. package/dist/environment/index.cjs +14 -14
  89. package/dist/environment/index.d.cts +1 -1
  90. package/dist/environment/index.d.ts +1 -1
  91. package/dist/environment/index.js +1 -1
  92. package/dist/gpu/index.cjs +4810 -0
  93. package/dist/gpu/index.js +3714 -0
  94. package/dist/hologram/index.cjs +27 -1
  95. package/dist/hologram/index.js +1 -1
  96. package/dist/index-B2PIsAmR.d.cts +2180 -0
  97. package/dist/index-B2PIsAmR.d.ts +2180 -0
  98. package/dist/index-BHySEPX7.d.cts +2921 -0
  99. package/dist/index-BJV21zuy.d.cts +341 -0
  100. package/dist/index-BJV21zuy.d.ts +341 -0
  101. package/dist/index-BQutTphC.d.cts +790 -0
  102. package/dist/index-ByIq2XrS.d.cts +3910 -0
  103. package/dist/index-BysHjDSO.d.cts +224 -0
  104. package/dist/index-BysHjDSO.d.ts +224 -0
  105. package/dist/index-CKwAJGck.d.ts +455 -0
  106. package/dist/index-CUl3QstQ.d.cts +3006 -0
  107. package/dist/index-CUl3QstQ.d.ts +3006 -0
  108. package/dist/index-CmYtNiI-.d.cts +953 -0
  109. package/dist/index-CmYtNiI-.d.ts +953 -0
  110. package/dist/index-CnRzWxi_.d.cts +522 -0
  111. package/dist/index-CnRzWxi_.d.ts +522 -0
  112. package/dist/index-CwRWbSC7.d.ts +2921 -0
  113. package/dist/index-CxKIBstO.d.ts +790 -0
  114. package/dist/index-DJ6-R8vh.d.cts +455 -0
  115. package/dist/index-DQKisbcI.d.cts +4968 -0
  116. package/dist/index-DQKisbcI.d.ts +4968 -0
  117. package/dist/index-DRT2zJez.d.ts +3910 -0
  118. package/dist/index-DfNLiAka.d.cts +192 -0
  119. package/dist/index-DfNLiAka.d.ts +192 -0
  120. package/dist/index-nMvkoRm8.d.cts +405 -0
  121. package/dist/index-nMvkoRm8.d.ts +405 -0
  122. package/dist/index-s9yOFU37.d.cts +604 -0
  123. package/dist/index-s9yOFU37.d.ts +604 -0
  124. package/dist/index.cjs +22966 -6960
  125. package/dist/index.d.cts +864 -20
  126. package/dist/index.d.ts +864 -20
  127. package/dist/index.js +3062 -48
  128. package/dist/input/index.cjs +1 -1
  129. package/dist/input/index.js +1 -1
  130. package/dist/orbital/index.cjs +3 -3
  131. package/dist/orbital/index.d.cts +1 -1
  132. package/dist/orbital/index.d.ts +1 -1
  133. package/dist/orbital/index.js +1 -1
  134. package/dist/particles/index.cjs +16 -16
  135. package/dist/particles/index.d.cts +1 -1
  136. package/dist/particles/index.d.ts +1 -1
  137. package/dist/particles/index.js +1 -1
  138. package/dist/physics/index.cjs +2377 -21
  139. package/dist/physics/index.d.cts +1 -1
  140. package/dist/physics/index.d.ts +1 -1
  141. package/dist/physics/index.js +35 -1
  142. package/dist/postfx/index.cjs +3491 -0
  143. package/dist/postfx/index.js +93 -0
  144. package/dist/procedural/index.cjs +1 -1
  145. package/dist/procedural/index.js +1 -1
  146. package/dist/puppeteer-5VF6KDVO.js +52197 -0
  147. package/dist/puppeteer-IZVZ3SG4.js +52197 -0
  148. package/dist/rendering/index.cjs +33 -32
  149. package/dist/rendering/index.d.cts +1 -1
  150. package/dist/rendering/index.d.ts +1 -1
  151. package/dist/rendering/index.js +8 -6
  152. package/dist/runtime/index.cjs +23 -13
  153. package/dist/runtime/index.d.cts +1 -1
  154. package/dist/runtime/index.d.ts +1 -1
  155. package/dist/runtime/index.js +8 -6
  156. package/dist/runtime/protocols/index.cjs +349 -0
  157. package/dist/runtime/protocols/index.js +15 -0
  158. package/dist/scene/index.cjs +8 -8
  159. package/dist/scene/index.d.cts +1 -1
  160. package/dist/scene/index.d.ts +1 -1
  161. package/dist/scene/index.js +1 -1
  162. package/dist/shader/index.cjs +3087 -0
  163. package/dist/shader/index.js +3044 -0
  164. package/dist/simulation/index.cjs +10680 -0
  165. package/dist/simulation/index.d.cts +3 -0
  166. package/dist/simulation/index.d.ts +3 -0
  167. package/dist/simulation/index.js +307 -0
  168. package/dist/spatial/index.cjs +2443 -0
  169. package/dist/spatial/index.d.cts +1545 -0
  170. package/dist/spatial/index.d.ts +1545 -0
  171. package/dist/spatial/index.js +2400 -0
  172. package/dist/terrain/index.cjs +1 -1
  173. package/dist/terrain/index.d.cts +1 -1
  174. package/dist/terrain/index.d.ts +1 -1
  175. package/dist/terrain/index.js +1 -1
  176. package/dist/transformers.node-4NKAPD5U.js +45620 -0
  177. package/dist/vm/index.cjs +7 -8
  178. package/dist/vm/index.d.cts +1 -1
  179. package/dist/vm/index.d.ts +1 -1
  180. package/dist/vm/index.js +1 -1
  181. package/dist/vm-bridge/index.cjs +2 -2
  182. package/dist/vm-bridge/index.d.cts +2 -2
  183. package/dist/vm-bridge/index.d.ts +2 -2
  184. package/dist/vm-bridge/index.js +1 -1
  185. package/dist/vr/index.cjs +6 -6
  186. package/dist/vr/index.js +1 -1
  187. package/dist/world/index.cjs +3 -3
  188. package/dist/world/index.d.cts +1 -1
  189. package/dist/world/index.d.ts +1 -1
  190. package/dist/world/index.js +1 -1
  191. package/package.json +53 -21
  192. package/LICENSE +0 -21
@@ -22,10 +22,16 @@ var physics_exports = {};
22
22
  __export(physics_exports, {
23
23
  ActivationTriggerType: () => ActivationTriggerType,
24
24
  COLLISION_GROUPS: () => COLLISION_GROUPS,
25
+ ClothSim: () => ClothSim,
26
+ ConstraintSolver: () => ConstraintSolver,
25
27
  DEFAULT_ACTIVATION_CONFIG: () => DEFAULT_ACTIVATION_CONFIG,
26
28
  DEFAULT_INTENSITY_CURVE: () => DEFAULT_INTENSITY_CURVE,
27
29
  DEFAULT_LOCOMOTION_CONFIG: () => DEFAULT_LOCOMOTION_CONFIG,
30
+ DeformableMesh: () => DeformableMesh,
31
+ FluidSim: () => FluidSim,
32
+ HUMANOID_PRESET: () => HUMANOID_PRESET,
28
33
  IslandDetector: () => IslandDetector,
34
+ JointSystem: () => JointSystem,
29
35
  MLSMPMFluid: () => MLSMPMFluid,
30
36
  PBDSolverCPU: () => PBDSolverCPU,
31
37
  PBD_ATTACHMENT_SHADER: () => PBD_ATTACHMENT_SHADER,
@@ -48,14 +54,23 @@ __export(physics_exports, {
48
54
  PhysicsSyncReceiver: () => PhysicsSyncReceiver,
49
55
  PhysicsSyncSender: () => PhysicsSyncSender,
50
56
  PhysicsWorldImpl: () => PhysicsWorldImpl,
57
+ QUADRUPED_PRESET: () => QUADRUPED_PRESET,
58
+ RagdollController: () => RagdollController,
59
+ RagdollSystem: () => RagdollSystem,
60
+ RaycastSystem: () => RaycastSystem,
51
61
  RigidBody: () => RigidBody,
62
+ RopeSystem: () => RopeSystem,
52
63
  SOFT_BODY_PRESETS: () => SOFT_BODY_PRESETS,
53
64
  ScalarArithmetic: () => ScalarArithmetic,
54
65
  SoftBodyAdapter: () => SoftBodyAdapter,
55
66
  SoftBodyGrabController: () => SoftBodyGrabController,
56
67
  SoftBodySolver: () => SoftBodySolver,
68
+ SpatialHash: () => SpatialHash,
69
+ TriggerZoneSystem: () => TriggerZoneSystem,
57
70
  UnifiedParticleBuffer: () => UnifiedParticleBuffer,
71
+ VRPhysicsBridge: () => VRPhysicsBridge,
58
72
  Vector3Arithmetic: () => Vector3Arithmetic,
73
+ VehicleSystem: () => VehicleSystem,
59
74
  VelocityRingBuffer: () => VelocityRingBuffer,
60
75
  VelocitySmoother: () => VelocitySmoother,
61
76
  WindZoneManager: () => WindZoneManager,
@@ -65,10 +80,12 @@ __export(physics_exports, {
65
80
  colorConstraints: () => colorConstraints,
66
81
  computeRestLengths: () => computeRestLengths,
67
82
  computeSelfWind: () => computeSelfWind,
83
+ createDefaultCar: () => createDefaultCar,
68
84
  createPBDSolver: () => createPBDSolver,
69
85
  createPIDControllerTrait: () => createPIDControllerTrait,
70
86
  createPhysicsWorld: () => createPhysicsWorld,
71
87
  createScalarPIDController: () => createScalarPIDController,
88
+ createTruck: () => createTruck,
72
89
  createVector3PIDController: () => createVector3PIDController,
73
90
  defaultMaterial: () => defaultMaterial,
74
91
  defaultPIDConfig: () => defaultPIDConfig,
@@ -2662,11 +2679,11 @@ function extractBendingPairs(indices, positions) {
2662
2679
  const v0 = parseInt(minStr);
2663
2680
  const v1 = parseInt(maxStr);
2664
2681
  const v2 = tris[0].oppositeVertex;
2665
- const v3 = tris[1].oppositeVertex;
2682
+ const v32 = tris[1].oppositeVertex;
2666
2683
  const p0x = positions[v0 * 3], p0y = positions[v0 * 3 + 1], p0z = positions[v0 * 3 + 2];
2667
2684
  const p1x = positions[v1 * 3], p1y = positions[v1 * 3 + 1], p1z = positions[v1 * 3 + 2];
2668
2685
  const p2x = positions[v2 * 3], p2y = positions[v2 * 3 + 1], p2z = positions[v2 * 3 + 2];
2669
- const p3x = positions[v3 * 3], p3y = positions[v3 * 3 + 1], p3z = positions[v3 * 3 + 2];
2686
+ const p3x = positions[v32 * 3], p3y = positions[v32 * 3 + 1], p3z = positions[v32 * 3 + 2];
2670
2687
  const ex = p1x - p0x, ey = p1y - p0y, ez = p1z - p0z;
2671
2688
  const eLen = Math.sqrt(ex * ex + ey * ey + ez * ez);
2672
2689
  if (eLen < 1e-7) continue;
@@ -2689,7 +2706,7 @@ function extractBendingPairs(indices, positions) {
2689
2706
  const cy = n1nz * n2nx - n1nx * n2nz;
2690
2707
  const cz = n1nx * n2ny - n1ny * n2nx;
2691
2708
  const sinTheta = cx * enx + cy * eny + cz * enz;
2692
- bendingList.push([v0, v1, v2, v3]);
2709
+ bendingList.push([v0, v1, v2, v32]);
2693
2710
  angleList.push(Math.atan2(sinTheta, cosTheta));
2694
2711
  }
2695
2712
  const constraints = new Uint32Array(bendingList.length * 4);
@@ -2872,11 +2889,11 @@ var PBDSolverCPU = class {
2872
2889
  const v0 = tetIndices[t * 4];
2873
2890
  const v1 = tetIndices[t * 4 + 1];
2874
2891
  const v2 = tetIndices[t * 4 + 2];
2875
- const v3 = tetIndices[t * 4 + 3];
2876
- const vol = this.computeTetVolume(v0, v1, v2, v3, positions);
2892
+ const v32 = tetIndices[t * 4 + 3];
2893
+ const vol = this.computeTetVolume(v0, v1, v2, v32, positions);
2877
2894
  this.volumeConstraints.push({
2878
2895
  type: "volume",
2879
- vertices: [v0, v1, v2, v3],
2896
+ vertices: [v0, v1, v2, v32],
2880
2897
  restVolume: vol,
2881
2898
  compliance: compliance * 0.1
2882
2899
  });
@@ -2902,11 +2919,11 @@ var PBDSolverCPU = class {
2902
2919
  });
2903
2920
  }
2904
2921
  }
2905
- computeTetVolume(v0, v1, v2, v3, pos) {
2922
+ computeTetVolume(v0, v1, v2, v32, pos) {
2906
2923
  const p0x = pos[v0 * 3], p0y = pos[v0 * 3 + 1], p0z = pos[v0 * 3 + 2];
2907
2924
  const d1x = pos[v1 * 3] - p0x, d1y = pos[v1 * 3 + 1] - p0y, d1z = pos[v1 * 3 + 2] - p0z;
2908
2925
  const d2x = pos[v2 * 3] - p0x, d2y = pos[v2 * 3 + 1] - p0y, d2z = pos[v2 * 3 + 2] - p0z;
2909
- const d3x = pos[v3 * 3] - p0x, d3y = pos[v3 * 3 + 1] - p0y, d3z = pos[v3 * 3 + 2] - p0z;
2926
+ const d3x = pos[v32 * 3] - p0x, d3y = pos[v32 * 3 + 1] - p0y, d3z = pos[v32 * 3 + 2] - p0z;
2910
2927
  const crx = d2y * d3z - d2z * d3y;
2911
2928
  const cry = d2z * d3x - d2x * d3z;
2912
2929
  const crz = d2x * d3y - d2y * d3x;
@@ -3089,11 +3106,11 @@ var PBDSolverCPU = class {
3089
3106
  solveVolumeConstraint(c, dt) {
3090
3107
  const pred = this.state.predicted;
3091
3108
  const masses = this.config.masses;
3092
- const [v0, v1, v2, v3] = c.vertices;
3109
+ const [v0, v1, v2, v32] = c.vertices;
3093
3110
  const p0x = pred[v0 * 3], p0y = pred[v0 * 3 + 1], p0z = pred[v0 * 3 + 2];
3094
3111
  const p1x = pred[v1 * 3], p1y = pred[v1 * 3 + 1], p1z = pred[v1 * 3 + 2];
3095
3112
  const p2x = pred[v2 * 3], p2y = pred[v2 * 3 + 1], p2z = pred[v2 * 3 + 2];
3096
- const p3x = pred[v3 * 3], p3y = pred[v3 * 3 + 1], p3z = pred[v3 * 3 + 2];
3113
+ const p3x = pred[v32 * 3], p3y = pred[v32 * 3 + 1], p3z = pred[v32 * 3 + 2];
3097
3114
  const d1x = p1x - p0x, d1y = p1y - p0y, d1z = p1z - p0z;
3098
3115
  const d2x = p2x - p0x, d2y = p2y - p0y, d2z = p2z - p0z;
3099
3116
  const d3x = p3x - p0x, d3y = p3y - p0y, d3z = p3z - p0z;
@@ -3112,7 +3129,7 @@ var PBDSolverCPU = class {
3112
3129
  const w0 = masses[v0] > 0 ? 1 / masses[v0] : 0;
3113
3130
  const w1 = masses[v1] > 0 ? 1 / masses[v1] : 0;
3114
3131
  const w2 = masses[v2] > 0 ? 1 / masses[v2] : 0;
3115
- const w3 = masses[v3] > 0 ? 1 / masses[v3] : 0;
3132
+ const w3 = masses[v32] > 0 ? 1 / masses[v32] : 0;
3116
3133
  const denom = w0 * (g0x * g0x + g0y * g0y + g0z * g0z) + w1 * (g1x * g1x + g1y * g1y + g1z * g1z) + w2 * (g2x * g2x + g2y * g2y + g2z * g2z) + w3 * (g3x * g3x + g3y * g3y + g3z * g3z);
3117
3134
  if (denom < 1e-7) return;
3118
3135
  const alpha = c.compliance / (dt * dt);
@@ -3133,19 +3150,19 @@ var PBDSolverCPU = class {
3133
3150
  pred[v2 * 3 + 2] += g2z * lambda * w2;
3134
3151
  }
3135
3152
  if (w3 > 0) {
3136
- pred[v3 * 3] += g3x * lambda * w3;
3137
- pred[v3 * 3 + 1] += g3y * lambda * w3;
3138
- pred[v3 * 3 + 2] += g3z * lambda * w3;
3153
+ pred[v32 * 3] += g3x * lambda * w3;
3154
+ pred[v32 * 3 + 1] += g3y * lambda * w3;
3155
+ pred[v32 * 3 + 2] += g3z * lambda * w3;
3139
3156
  }
3140
3157
  }
3141
3158
  solveBendingConstraint(c, dt) {
3142
3159
  const pred = this.state.predicted;
3143
3160
  const masses = this.config.masses;
3144
- const [v0, v1, v2, v3] = c.vertices;
3161
+ const [v0, v1, v2, v32] = c.vertices;
3145
3162
  const p0x = pred[v0 * 3], p0y = pred[v0 * 3 + 1], p0z = pred[v0 * 3 + 2];
3146
3163
  const p1x = pred[v1 * 3], p1y = pred[v1 * 3 + 1], p1z = pred[v1 * 3 + 2];
3147
3164
  const p2x = pred[v2 * 3], p2y = pred[v2 * 3 + 1], p2z = pred[v2 * 3 + 2];
3148
- const p3x = pred[v3 * 3], p3y = pred[v3 * 3 + 1], p3z = pred[v3 * 3 + 2];
3165
+ const p3x = pred[v32 * 3], p3y = pred[v32 * 3 + 1], p3z = pred[v32 * 3 + 2];
3149
3166
  const ex = p1x - p0x, ey = p1y - p0y, ez = p1z - p0z;
3150
3167
  const eLen = Math.sqrt(ex * ex + ey * ey + ez * ez);
3151
3168
  if (eLen < 1e-7) return;
@@ -3184,7 +3201,7 @@ var PBDSolverCPU = class {
3184
3201
  const w0 = masses[v0] > 0 ? 1 / masses[v0] : 0;
3185
3202
  const w1 = masses[v1] > 0 ? 1 / masses[v1] : 0;
3186
3203
  const w2 = masses[v2] > 0 ? 1 / masses[v2] : 0;
3187
- const w3 = masses[v3] > 0 ? 1 / masses[v3] : 0;
3204
+ const w3 = masses[v32] > 0 ? 1 / masses[v32] : 0;
3188
3205
  const denom = w0 * (g0x * g0x + g0y * g0y + g0z * g0z) + w1 * (g1x2 * g1x2 + g1y2 * g1y2 + g1z2 * g1z2) + w2 * (g2x * g2x + g2y * g2y + g2z * g2z) + w3 * (g3x * g3x + g3y * g3y + g3z * g3z);
3189
3206
  const alpha = c.compliance / (dt * dt);
3190
3207
  if (denom + alpha < 1e-10) return;
@@ -3205,9 +3222,9 @@ var PBDSolverCPU = class {
3205
3222
  pred[v2 * 3 + 2] += g2z * lambda * w2;
3206
3223
  }
3207
3224
  if (w3 > 0) {
3208
- pred[v3 * 3] += g3x * lambda * w3;
3209
- pred[v3 * 3 + 1] += g3y * lambda * w3;
3210
- pred[v3 * 3 + 2] += g3z * lambda * w3;
3225
+ pred[v32 * 3] += g3x * lambda * w3;
3226
+ pred[v32 * 3 + 1] += g3y * lambda * w3;
3227
+ pred[v32 * 3 + 2] += g3z * lambda * w3;
3211
3228
  }
3212
3229
  }
3213
3230
  solveAttachmentConstraint(c, dt) {
@@ -5066,6 +5083,13 @@ var PIDLoop = class {
5066
5083
  this.filteredDerivative = math.zero();
5067
5084
  this.prevOutput = math.zero();
5068
5085
  }
5086
+ gains;
5087
+ outputLimit;
5088
+ integralLimit;
5089
+ filterAlpha;
5090
+ derivativeOnMeasurement;
5091
+ backCalcAntiWindup;
5092
+ backCalcGain;
5069
5093
  math;
5070
5094
  integral;
5071
5095
  prevError;
@@ -6244,7 +6268,7 @@ var SoftBodyAdapter = class {
6244
6268
  });
6245
6269
  this.vertexMapping.push(i);
6246
6270
  }
6247
- const width = Math.sqrt(particles.length);
6271
+ const _width = Math.sqrt(particles.length);
6248
6272
  for (let i = 0; i < particles.length - 1; i++) {
6249
6273
  constraints.push({
6250
6274
  p1: i,
@@ -6269,14 +6293,2335 @@ var SoftBodyAdapter = class {
6269
6293
  this.node.geometry.needsUpdate = true;
6270
6294
  }
6271
6295
  };
6296
+
6297
+ // src/physics/VRPhysicsBridge.ts
6298
+ var VRPhysicsBridge = class {
6299
+ world;
6300
+ handBodies = /* @__PURE__ */ new Map();
6301
+ // handId -> physicsBodyId
6302
+ lastPositions = /* @__PURE__ */ new Map();
6303
+ lastRotations = /* @__PURE__ */ new Map();
6304
+ onHaptic;
6305
+ constructor(world, onHaptic) {
6306
+ this.world = world;
6307
+ this.onHaptic = onHaptic || (() => {
6308
+ });
6309
+ }
6310
+ update(vrContext, delta) {
6311
+ this.updateHand(vrContext.hands.left, "left", delta);
6312
+ this.updateHand(vrContext.hands.right, "right", delta);
6313
+ this.checkCollisions();
6314
+ }
6315
+ checkCollisions() {
6316
+ const contacts = this.world.getContacts();
6317
+ for (const contact of contacts) {
6318
+ if (contact.type === "begin") {
6319
+ if (contact.bodyA === "hand_left" || contact.bodyB === "hand_left") {
6320
+ this.onHaptic("left", 0.5, 50);
6321
+ }
6322
+ if (contact.bodyA === "hand_right" || contact.bodyB === "hand_right") {
6323
+ this.onHaptic("right", 0.5, 50);
6324
+ }
6325
+ }
6326
+ }
6327
+ }
6328
+ // Make public for testing
6329
+ updateHand(hand, side, delta) {
6330
+ const bodyId = `hand_${side}`;
6331
+ if (!hand) {
6332
+ return;
6333
+ }
6334
+ let body = this.world.getBody(bodyId);
6335
+ if (!body) {
6336
+ const config = {
6337
+ id: bodyId,
6338
+ type: "kinematic",
6339
+ mass: 1,
6340
+ // Infinite mass for kinematic
6341
+ transform: {
6342
+ position: { x: hand.position?.x ?? 0, y: hand.position?.y ?? 0, z: hand.position?.z ?? 0 },
6343
+ rotation: {
6344
+ x: hand.rotation?.x ?? 0,
6345
+ y: hand.rotation?.y ?? 0,
6346
+ z: hand.rotation?.z ?? 0,
6347
+ w: 1
6348
+ }
6349
+ },
6350
+ shape: {
6351
+ type: "sphere",
6352
+ radius: 0.05
6353
+ // 5cm palm radius
6354
+ },
6355
+ material: {
6356
+ friction: 0.5,
6357
+ restitution: 0
6358
+ }
6359
+ };
6360
+ this.world.createBody(config);
6361
+ body = this.world.getBody(bodyId);
6362
+ }
6363
+ if (body) {
6364
+ const safePosX = hand.position?.x ?? 0;
6365
+ const safePosY = hand.position?.y ?? 0;
6366
+ const safePosZ = hand.position?.z ?? 0;
6367
+ const prevPos = this.lastPositions.get(bodyId) || {
6368
+ x: safePosX,
6369
+ y: safePosY,
6370
+ z: safePosZ
6371
+ };
6372
+ const safeDelta = delta > 1e-3 ? delta : 0.016;
6373
+ const rawVelocity = {
6374
+ x: (safePosX - prevPos.x) / safeDelta,
6375
+ y: (safePosY - prevPos.y) / safeDelta,
6376
+ z: (safePosZ - prevPos.z) / safeDelta
6377
+ };
6378
+ const prevVel = body.linearVelocity || { x: 0, y: 0, z: 0 };
6379
+ const smoothingFactor = 0.5;
6380
+ const smoothedVelocity = {
6381
+ x: prevVel.x * (1 - smoothingFactor) + rawVelocity.x * smoothingFactor,
6382
+ y: prevVel.y * (1 - smoothingFactor) + rawVelocity.y * smoothingFactor,
6383
+ z: prevVel.z * (1 - smoothingFactor) + rawVelocity.z * smoothingFactor
6384
+ };
6385
+ this.world.setPosition(bodyId, { x: safePosX, y: safePosY, z: safePosZ });
6386
+ this.world.setLinearVelocity(bodyId, smoothedVelocity);
6387
+ this.lastPositions.set(bodyId, { x: safePosX, y: safePosY, z: safePosZ });
6388
+ }
6389
+ }
6390
+ getHandBodyId(side) {
6391
+ const id = `hand_${side}`;
6392
+ return this.world.getBody(id) ? id : null;
6393
+ }
6394
+ };
6395
+
6396
+ // src/physics/ClothSim.ts
6397
+ var ClothSim = class {
6398
+ particles = [];
6399
+ constraints = [];
6400
+ config;
6401
+ width = 0;
6402
+ height = 0;
6403
+ constructor(config) {
6404
+ this.config = {
6405
+ gravity: -9.81,
6406
+ damping: 0.99,
6407
+ iterations: 5,
6408
+ wind: { x: 0, y: 0, z: 0 },
6409
+ ...config
6410
+ };
6411
+ }
6412
+ // ---------------------------------------------------------------------------
6413
+ // Grid
6414
+ // ---------------------------------------------------------------------------
6415
+ createGrid(width, height, spacing) {
6416
+ this.width = width;
6417
+ this.height = height;
6418
+ this.particles = [];
6419
+ this.constraints = [];
6420
+ for (let row = 0; row < height; row++) {
6421
+ for (let col = 0; col < width; col++) {
6422
+ this.particles.push({
6423
+ x: col * spacing,
6424
+ y: 0,
6425
+ z: row * spacing,
6426
+ prevX: col * spacing,
6427
+ prevY: 0,
6428
+ prevZ: row * spacing,
6429
+ mass: 1,
6430
+ pinned: false
6431
+ });
6432
+ }
6433
+ }
6434
+ for (let row = 0; row < height; row++) {
6435
+ for (let col = 0; col < width; col++) {
6436
+ const idx = row * width + col;
6437
+ if (col < width - 1) {
6438
+ this.addConstraint(idx, idx + 1, spacing);
6439
+ }
6440
+ if (row < height - 1) {
6441
+ this.addConstraint(idx, idx + width, spacing);
6442
+ }
6443
+ if (col < width - 1 && row < height - 1) {
6444
+ const diag = spacing * Math.SQRT2;
6445
+ this.addConstraint(idx, idx + width + 1, diag);
6446
+ this.addConstraint(idx + 1, idx + width, diag);
6447
+ }
6448
+ }
6449
+ }
6450
+ }
6451
+ addConstraint(a, b, restLength, stiffness = 1) {
6452
+ this.constraints.push({ particleA: a, particleB: b, restLength, stiffness });
6453
+ }
6454
+ // ---------------------------------------------------------------------------
6455
+ // Pinning
6456
+ // ---------------------------------------------------------------------------
6457
+ pin(index) {
6458
+ if (this.particles[index]) this.particles[index].pinned = true;
6459
+ }
6460
+ unpin(index) {
6461
+ if (this.particles[index]) this.particles[index].pinned = false;
6462
+ }
6463
+ pinTopRow() {
6464
+ for (let col = 0; col < this.width; col++) this.pin(col);
6465
+ }
6466
+ // ---------------------------------------------------------------------------
6467
+ // Update
6468
+ // ---------------------------------------------------------------------------
6469
+ update(dt) {
6470
+ for (const p of this.particles) {
6471
+ if (p.pinned) continue;
6472
+ const vx = (p.x - p.prevX) * this.config.damping;
6473
+ const vy = (p.y - p.prevY) * this.config.damping;
6474
+ const vz = (p.z - p.prevZ) * this.config.damping;
6475
+ p.prevX = p.x;
6476
+ p.prevY = p.y;
6477
+ p.prevZ = p.z;
6478
+ p.x += vx + this.config.wind.x * dt * dt / p.mass;
6479
+ p.y += vy + this.config.gravity * dt * dt;
6480
+ p.z += vz + this.config.wind.z * dt * dt / p.mass;
6481
+ }
6482
+ for (let iter = 0; iter < this.config.iterations; iter++) {
6483
+ for (const c of this.constraints) {
6484
+ const a = this.particles[c.particleA];
6485
+ const b = this.particles[c.particleB];
6486
+ const dx = b.x - a.x;
6487
+ const dy = b.y - a.y;
6488
+ const dz = b.z - a.z;
6489
+ const dist = Math.sqrt(dx * dx + dy * dy + dz * dz);
6490
+ if (dist === 0) continue;
6491
+ const diff = (c.restLength - dist) / dist * c.stiffness * 0.5;
6492
+ const ox = dx * diff;
6493
+ const oy = dy * diff;
6494
+ const oz = dz * diff;
6495
+ if (!a.pinned) {
6496
+ a.x -= ox;
6497
+ a.y -= oy;
6498
+ a.z -= oz;
6499
+ }
6500
+ if (!b.pinned) {
6501
+ b.x += ox;
6502
+ b.y += oy;
6503
+ b.z += oz;
6504
+ }
6505
+ }
6506
+ }
6507
+ }
6508
+ // ---------------------------------------------------------------------------
6509
+ // Wind
6510
+ // ---------------------------------------------------------------------------
6511
+ setWind(x, y, z) {
6512
+ this.config.wind = { x, y, z };
6513
+ }
6514
+ // ---------------------------------------------------------------------------
6515
+ // Queries
6516
+ // ---------------------------------------------------------------------------
6517
+ getParticle(index) {
6518
+ return this.particles[index];
6519
+ }
6520
+ getParticleCount() {
6521
+ return this.particles.length;
6522
+ }
6523
+ getConstraintCount() {
6524
+ return this.constraints.length;
6525
+ }
6526
+ getGridSize() {
6527
+ return { width: this.width, height: this.height };
6528
+ }
6529
+ getAABB() {
6530
+ let minX = Infinity, minY = Infinity, minZ = Infinity;
6531
+ let maxX = -Infinity, maxY = -Infinity, maxZ = -Infinity;
6532
+ for (const p of this.particles) {
6533
+ minX = Math.min(minX, p.x);
6534
+ minY = Math.min(minY, p.y);
6535
+ minZ = Math.min(minZ, p.z);
6536
+ maxX = Math.max(maxX, p.x);
6537
+ maxY = Math.max(maxY, p.y);
6538
+ maxZ = Math.max(maxZ, p.z);
6539
+ }
6540
+ return { min: { x: minX, y: minY, z: minZ }, max: { x: maxX, y: maxY, z: maxZ } };
6541
+ }
6542
+ };
6543
+
6544
+ // src/physics/ConstraintSolver.ts
6545
+ function v3(x, y, z) {
6546
+ return { x, y, z };
6547
+ }
6548
+ function v3Add(a, b) {
6549
+ return { x: a.x + b.x, y: a.y + b.y, z: a.z + b.z };
6550
+ }
6551
+ function v3Sub(a, b) {
6552
+ return { x: a.x - b.x, y: a.y - b.y, z: a.z - b.z };
6553
+ }
6554
+ function v3Scale(v, s) {
6555
+ return { x: v.x * s, y: v.y * s, z: v.z * s };
6556
+ }
6557
+ function v3Dot(a, b) {
6558
+ return a.x * b.x + a.y * b.y + a.z * b.z;
6559
+ }
6560
+ function v3Length(v) {
6561
+ return Math.sqrt(v.x * v.x + v.y * v.y + v.z * v.z);
6562
+ }
6563
+ function v3Normalize(v) {
6564
+ const len = v3Length(v);
6565
+ return len > 1e-10 ? v3Scale(v, 1 / len) : v3(0, 0, 0);
6566
+ }
6567
+ function v3Zero() {
6568
+ return { x: 0, y: 0, z: 0 };
6569
+ }
6570
+ var DEFAULT_CONFIG2 = {
6571
+ iterations: 10,
6572
+ baumgarte: 0.2,
6573
+ warmStarting: true,
6574
+ slop: 5e-3
6575
+ };
6576
+ var ConstraintSolver = class {
6577
+ config;
6578
+ solved = [];
6579
+ constructor(config = {}) {
6580
+ this.config = { ...DEFAULT_CONFIG2, ...config };
6581
+ }
6582
+ // ---------------------------------------------------------------------------
6583
+ // Public API
6584
+ // ---------------------------------------------------------------------------
6585
+ /**
6586
+ * Add a constraint to be solved.
6587
+ */
6588
+ addConstraint(constraint, bodyA, bodyB = null) {
6589
+ this.solved.push({
6590
+ constraint,
6591
+ bodyA,
6592
+ bodyB,
6593
+ accumulatedImpulse: v3Zero(),
6594
+ accumulatedAngularImpulse: v3Zero(),
6595
+ broken: false
6596
+ });
6597
+ }
6598
+ /**
6599
+ * Remove a constraint by ID.
6600
+ */
6601
+ removeConstraint(constraintId) {
6602
+ const idx = this.solved.findIndex((s) => s.constraint.id === constraintId);
6603
+ if (idx < 0) return false;
6604
+ this.solved.splice(idx, 1);
6605
+ return true;
6606
+ }
6607
+ /**
6608
+ * Get all active constraints.
6609
+ */
6610
+ getConstraints() {
6611
+ return this.solved.filter((s) => !s.broken).map((s) => s.constraint);
6612
+ }
6613
+ /**
6614
+ * Solve all constraints for one timestep.
6615
+ * Returns velocity corrections to apply to rigid bodies.
6616
+ */
6617
+ solve(dt) {
6618
+ const corrections = /* @__PURE__ */ new Map();
6619
+ if (this.config.warmStarting) {
6620
+ for (const sc of this.solved) {
6621
+ if (sc.broken) continue;
6622
+ this.applyWarmStart(sc, corrections);
6623
+ }
6624
+ }
6625
+ for (let iter = 0; iter < this.config.iterations; iter++) {
6626
+ for (const sc of this.solved) {
6627
+ if (sc.broken) continue;
6628
+ this.solveConstraint(sc, dt, corrections);
6629
+ }
6630
+ }
6631
+ for (const sc of this.solved) {
6632
+ if (sc.broken) continue;
6633
+ const breakForce = sc.constraint.breakForce;
6634
+ if (breakForce !== void 0 && breakForce > 0) {
6635
+ const impulseLen = v3Length(sc.accumulatedImpulse) / Math.max(dt, 1e-6);
6636
+ if (impulseLen > breakForce) {
6637
+ sc.broken = true;
6638
+ }
6639
+ }
6640
+ }
6641
+ return corrections;
6642
+ }
6643
+ /**
6644
+ * Get broken constraint IDs.
6645
+ */
6646
+ getBrokenConstraints() {
6647
+ return this.solved.filter((s) => s.broken).map((s) => s.constraint.id);
6648
+ }
6649
+ /**
6650
+ * Clear all constraints.
6651
+ */
6652
+ clear() {
6653
+ this.solved = [];
6654
+ }
6655
+ // ---------------------------------------------------------------------------
6656
+ // Internal: Dispatch
6657
+ // ---------------------------------------------------------------------------
6658
+ solveConstraint(sc, dt, corrections) {
6659
+ switch (sc.constraint.type) {
6660
+ case "fixed":
6661
+ this.solveFixed(sc, dt, corrections);
6662
+ break;
6663
+ case "distance":
6664
+ this.solveDistance(
6665
+ sc,
6666
+ dt,
6667
+ corrections
6668
+ );
6669
+ break;
6670
+ case "spring":
6671
+ this.solveSpring(
6672
+ sc,
6673
+ dt,
6674
+ corrections
6675
+ );
6676
+ break;
6677
+ case "hinge":
6678
+ this.solveHinge(sc, dt, corrections);
6679
+ break;
6680
+ case "ball":
6681
+ this.solveBall(sc, dt, corrections);
6682
+ break;
6683
+ case "slider":
6684
+ this.solveSlider(
6685
+ sc,
6686
+ dt,
6687
+ corrections
6688
+ );
6689
+ break;
6690
+ case "cone":
6691
+ this.solveCone(sc, dt, corrections);
6692
+ break;
6693
+ case "generic6dof":
6694
+ this.solveGeneric6DOF(
6695
+ sc,
6696
+ dt,
6697
+ corrections
6698
+ );
6699
+ break;
6700
+ }
6701
+ }
6702
+ // ---------------------------------------------------------------------------
6703
+ // Constraint Solvers
6704
+ // ---------------------------------------------------------------------------
6705
+ solveFixed(sc, dt, corrections) {
6706
+ const c = sc.constraint;
6707
+ const pivotWorld = v3Add(sc.bodyA.position, c.pivotA);
6708
+ const targetWorld = sc.bodyB ? v3Add(sc.bodyB.position, c.pivotB || v3Zero()) : pivotWorld;
6709
+ const error = v3Sub(targetWorld, pivotWorld);
6710
+ const correction = v3Scale(error, this.config.baumgarte / Math.max(dt, 1e-6));
6711
+ this.accumulateCorrection(corrections, sc.bodyA.id, correction, v3Zero());
6712
+ if (sc.bodyB) {
6713
+ this.accumulateCorrection(corrections, sc.bodyB.id, v3Scale(correction, -1), v3Zero());
6714
+ }
6715
+ sc.accumulatedImpulse = v3Add(sc.accumulatedImpulse, correction);
6716
+ }
6717
+ solveDistance(sc, dt, corrections) {
6718
+ const c = sc.constraint;
6719
+ const anchorA = v3Add(sc.bodyA.position, c.pivotA);
6720
+ const anchorB = sc.bodyB ? v3Add(sc.bodyB.position, c.pivotB || v3Zero()) : v3Add(sc.bodyA.position, v3(c.distance, 0, 0));
6721
+ const delta = v3Sub(anchorB, anchorA);
6722
+ const currentDist = v3Length(delta);
6723
+ const dir = currentDist > 1e-10 ? v3Scale(delta, 1 / currentDist) : v3(1, 0, 0);
6724
+ const penetration = currentDist - c.distance;
6725
+ if (Math.abs(penetration) < this.config.slop) return;
6726
+ const stiffness = c.stiffness ?? 1;
6727
+ const baumgarte = this.config.baumgarte * stiffness;
6728
+ const impulse = v3Scale(dir, penetration * baumgarte / Math.max(dt, 1e-6));
6729
+ this.accumulateCorrection(corrections, sc.bodyA.id, impulse, v3Zero());
6730
+ if (sc.bodyB) {
6731
+ this.accumulateCorrection(corrections, sc.bodyB.id, v3Scale(impulse, -1), v3Zero());
6732
+ }
6733
+ sc.accumulatedImpulse = v3Add(sc.accumulatedImpulse, impulse);
6734
+ }
6735
+ solveSpring(sc, dt, corrections) {
6736
+ const c = sc.constraint;
6737
+ const anchorA = v3Add(sc.bodyA.position, c.pivotA);
6738
+ const anchorB = sc.bodyB ? v3Add(sc.bodyB.position, c.pivotB || v3Zero()) : v3Add(sc.bodyA.position, v3(c.restLength, 0, 0));
6739
+ const delta = v3Sub(anchorB, anchorA);
6740
+ const currentLen = v3Length(delta);
6741
+ const dir = currentLen > 1e-10 ? v3Scale(delta, 1 / currentLen) : v3(1, 0, 0);
6742
+ const displacement = currentLen - c.restLength;
6743
+ const springForce = -c.stiffness * displacement;
6744
+ const relVel = sc.bodyB ? v3Sub(sc.bodyB.linearVelocity, sc.bodyA.linearVelocity) : v3Scale(sc.bodyA.linearVelocity, -1);
6745
+ const dampingForce = -c.damping * v3Dot(relVel, dir);
6746
+ const totalForce = springForce + dampingForce;
6747
+ const impulse = v3Scale(dir, totalForce * dt);
6748
+ this.accumulateCorrection(corrections, sc.bodyA.id, v3Scale(impulse, -1), v3Zero());
6749
+ if (sc.bodyB) {
6750
+ this.accumulateCorrection(corrections, sc.bodyB.id, impulse, v3Zero());
6751
+ }
6752
+ sc.accumulatedImpulse = v3Add(sc.accumulatedImpulse, impulse);
6753
+ }
6754
+ solveHinge(sc, dt, corrections) {
6755
+ const c = sc.constraint;
6756
+ const pivotWorld = v3Add(sc.bodyA.position, c.pivotA);
6757
+ const targetWorld = sc.bodyB ? v3Add(sc.bodyB.position, c.pivotB || v3Zero()) : pivotWorld;
6758
+ const posError = v3Sub(targetWorld, pivotWorld);
6759
+ const posCorrection = v3Scale(posError, this.config.baumgarte / Math.max(dt, 1e-6));
6760
+ this.accumulateCorrection(corrections, sc.bodyA.id, posCorrection, v3Zero());
6761
+ if (sc.bodyB) {
6762
+ this.accumulateCorrection(corrections, sc.bodyB.id, v3Scale(posCorrection, -1), v3Zero());
6763
+ }
6764
+ if (c.motor) {
6765
+ const axis = v3Normalize(c.axisA);
6766
+ const currentAngVel = v3Dot(sc.bodyA.angularVelocity, axis);
6767
+ const velError = c.motor.targetVelocity - currentAngVel;
6768
+ const motorImpulse = Math.min(Math.abs(velError * dt), c.motor.maxForce * dt) * Math.sign(velError);
6769
+ const angImpulse = v3Scale(axis, motorImpulse);
6770
+ this.accumulateCorrection(corrections, sc.bodyA.id, v3Zero(), angImpulse);
6771
+ }
6772
+ sc.accumulatedImpulse = v3Add(sc.accumulatedImpulse, posCorrection);
6773
+ }
6774
+ solveBall(sc, dt, corrections) {
6775
+ const c = sc.constraint;
6776
+ const pivotWorld = v3Add(sc.bodyA.position, c.pivotA);
6777
+ const targetWorld = sc.bodyB ? v3Add(sc.bodyB.position, c.pivotB || v3Zero()) : pivotWorld;
6778
+ const error = v3Sub(targetWorld, pivotWorld);
6779
+ const correction = v3Scale(error, this.config.baumgarte / Math.max(dt, 1e-6));
6780
+ this.accumulateCorrection(corrections, sc.bodyA.id, correction, v3Zero());
6781
+ if (sc.bodyB) {
6782
+ this.accumulateCorrection(corrections, sc.bodyB.id, v3Scale(correction, -1), v3Zero());
6783
+ }
6784
+ sc.accumulatedImpulse = v3Add(sc.accumulatedImpulse, correction);
6785
+ }
6786
+ solveSlider(sc, dt, corrections) {
6787
+ const c = sc.constraint;
6788
+ const axis = v3Normalize(c.axisA);
6789
+ const pivotWorld = v3Add(sc.bodyA.position, c.pivotA);
6790
+ const targetWorld = sc.bodyB ? v3Add(sc.bodyB.position, c.pivotB || v3Zero()) : pivotWorld;
6791
+ const delta = v3Sub(targetWorld, pivotWorld);
6792
+ const onAxis = v3Dot(delta, axis);
6793
+ const perp = v3Sub(delta, v3Scale(axis, onAxis));
6794
+ const perpCorrection = v3Scale(perp, this.config.baumgarte / Math.max(dt, 1e-6));
6795
+ this.accumulateCorrection(corrections, sc.bodyA.id, perpCorrection, v3Zero());
6796
+ if (sc.bodyB) {
6797
+ this.accumulateCorrection(corrections, sc.bodyB.id, v3Scale(perpCorrection, -1), v3Zero());
6798
+ }
6799
+ if (c.limits) {
6800
+ if (onAxis < c.limits.low) {
6801
+ const limitImpulse = v3Scale(
6802
+ axis,
6803
+ (c.limits.low - onAxis) * this.config.baumgarte / Math.max(dt, 1e-6)
6804
+ );
6805
+ this.accumulateCorrection(corrections, sc.bodyA.id, limitImpulse, v3Zero());
6806
+ } else if (onAxis > c.limits.high) {
6807
+ const limitImpulse = v3Scale(
6808
+ axis,
6809
+ (c.limits.high - onAxis) * this.config.baumgarte / Math.max(dt, 1e-6)
6810
+ );
6811
+ this.accumulateCorrection(corrections, sc.bodyA.id, limitImpulse, v3Zero());
6812
+ }
6813
+ }
6814
+ sc.accumulatedImpulse = v3Add(sc.accumulatedImpulse, perpCorrection);
6815
+ }
6816
+ solveCone(sc, dt, corrections) {
6817
+ const c = sc.constraint;
6818
+ const pivotWorld = v3Add(sc.bodyA.position, c.pivotA);
6819
+ const targetWorld = sc.bodyB ? v3Add(sc.bodyB.position, c.pivotB || v3Zero()) : pivotWorld;
6820
+ const posError = v3Sub(targetWorld, pivotWorld);
6821
+ const posCorrection = v3Scale(posError, this.config.baumgarte / Math.max(dt, 1e-6));
6822
+ this.accumulateCorrection(corrections, sc.bodyA.id, posCorrection, v3Zero());
6823
+ if (sc.bodyB) {
6824
+ this.accumulateCorrection(corrections, sc.bodyB.id, v3Scale(posCorrection, -1), v3Zero());
6825
+ }
6826
+ const twistAxis = v3Normalize(c.axisA);
6827
+ const twistVel = v3Dot(sc.bodyA.angularVelocity, twistAxis);
6828
+ if (Math.abs(twistVel) > c.twistSpan) {
6829
+ const twistCorrection = v3Scale(twistAxis, -twistVel * 0.5);
6830
+ this.accumulateCorrection(corrections, sc.bodyA.id, v3Zero(), twistCorrection);
6831
+ }
6832
+ sc.accumulatedImpulse = v3Add(sc.accumulatedImpulse, posCorrection);
6833
+ }
6834
+ solveGeneric6DOF(sc, dt, corrections) {
6835
+ const c = sc.constraint;
6836
+ const pos = sc.bodyA.position;
6837
+ const linCorrection = v3Zero();
6838
+ if (pos.x < c.linearLowerLimit.x)
6839
+ linCorrection.x = (c.linearLowerLimit.x - pos.x) * this.config.baumgarte / Math.max(dt, 1e-6);
6840
+ if (pos.x > c.linearUpperLimit.x)
6841
+ linCorrection.x = (c.linearUpperLimit.x - pos.x) * this.config.baumgarte / Math.max(dt, 1e-6);
6842
+ if (pos.y < c.linearLowerLimit.y)
6843
+ linCorrection.y = (c.linearLowerLimit.y - pos.y) * this.config.baumgarte / Math.max(dt, 1e-6);
6844
+ if (pos.y > c.linearUpperLimit.y)
6845
+ linCorrection.y = (c.linearUpperLimit.y - pos.y) * this.config.baumgarte / Math.max(dt, 1e-6);
6846
+ if (pos.z < c.linearLowerLimit.z)
6847
+ linCorrection.z = (c.linearLowerLimit.z - pos.z) * this.config.baumgarte / Math.max(dt, 1e-6);
6848
+ if (pos.z > c.linearUpperLimit.z)
6849
+ linCorrection.z = (c.linearUpperLimit.z - pos.z) * this.config.baumgarte / Math.max(dt, 1e-6);
6850
+ this.accumulateCorrection(corrections, sc.bodyA.id, linCorrection, v3Zero());
6851
+ sc.accumulatedImpulse = v3Add(sc.accumulatedImpulse, linCorrection);
6852
+ }
6853
+ // ---------------------------------------------------------------------------
6854
+ // Helpers
6855
+ // ---------------------------------------------------------------------------
6856
+ applyWarmStart(sc, corrections) {
6857
+ const scaled = v3Scale(sc.accumulatedImpulse, 0.8);
6858
+ this.accumulateCorrection(corrections, sc.bodyA.id, scaled, v3Zero());
6859
+ if (sc.bodyB) {
6860
+ this.accumulateCorrection(corrections, sc.bodyB.id, v3Scale(scaled, -1), v3Zero());
6861
+ }
6862
+ }
6863
+ accumulateCorrection(corrections, bodyId, linear, angular) {
6864
+ const existing = corrections.get(bodyId) || {
6865
+ linearVelocity: v3Zero(),
6866
+ angularVelocity: v3Zero()
6867
+ };
6868
+ corrections.set(bodyId, {
6869
+ linearVelocity: v3Add(existing.linearVelocity, linear),
6870
+ angularVelocity: v3Add(existing.angularVelocity, angular)
6871
+ });
6872
+ }
6873
+ };
6874
+
6875
+ // src/physics/DeformableMesh.ts
6876
+ var DeformableMesh = class {
6877
+ vertices = [];
6878
+ springs = [];
6879
+ config;
6880
+ restCentroid = { x: 0, y: 0, z: 0 };
6881
+ constructor(config) {
6882
+ this.config = {
6883
+ stiffness: 100,
6884
+ damping: 0.95,
6885
+ shapeMatchingStrength: 0.5,
6886
+ maxDisplacement: 5,
6887
+ plasticity: 0,
6888
+ ...config
6889
+ };
6890
+ }
6891
+ // ---------------------------------------------------------------------------
6892
+ // Mesh Setup
6893
+ // ---------------------------------------------------------------------------
6894
+ setVertices(positions) {
6895
+ this.vertices = positions.map((p) => ({
6896
+ rest: { ...p },
6897
+ current: { ...p },
6898
+ velocity: { x: 0, y: 0, z: 0 },
6899
+ mass: 1,
6900
+ locked: false
6901
+ }));
6902
+ this.computeRestCentroid();
6903
+ }
6904
+ addSpring(a, b, stiffness, damping) {
6905
+ const pa = this.vertices[a].rest, pb = this.vertices[b].rest;
6906
+ const dx = pb.x - pa.x, dy = pb.y - pa.y, dz = pb.z - pa.z;
6907
+ this.springs.push({
6908
+ a,
6909
+ b,
6910
+ restLength: Math.sqrt(dx * dx + dy * dy + dz * dz),
6911
+ stiffness: stiffness ?? this.config.stiffness,
6912
+ damping: damping ?? 5
6913
+ });
6914
+ }
6915
+ autoConnectRadius(radius) {
6916
+ for (let i = 0; i < this.vertices.length; i++) {
6917
+ for (let j = i + 1; j < this.vertices.length; j++) {
6918
+ const a = this.vertices[i].rest, b = this.vertices[j].rest;
6919
+ const dx = b.x - a.x, dy = b.y - a.y, dz = b.z - a.z;
6920
+ const dist = Math.sqrt(dx * dx + dy * dy + dz * dz);
6921
+ if (dist <= radius) this.addSpring(i, j);
6922
+ }
6923
+ }
6924
+ }
6925
+ computeRestCentroid() {
6926
+ let cx = 0, cy = 0, cz = 0;
6927
+ for (const v of this.vertices) {
6928
+ cx += v.rest.x;
6929
+ cy += v.rest.y;
6930
+ cz += v.rest.z;
6931
+ }
6932
+ const n = this.vertices.length || 1;
6933
+ this.restCentroid = { x: cx / n, y: cy / n, z: cz / n };
6934
+ }
6935
+ // ---------------------------------------------------------------------------
6936
+ // Deformation
6937
+ // ---------------------------------------------------------------------------
6938
+ applyImpact(center, radius, force) {
6939
+ for (const v of this.vertices) {
6940
+ if (v.locked) continue;
6941
+ const dx = v.current.x - center.x;
6942
+ const dy = v.current.y - center.y;
6943
+ const dz = v.current.z - center.z;
6944
+ const dist = Math.sqrt(dx * dx + dy * dy + dz * dz);
6945
+ if (dist > radius || dist === 0) continue;
6946
+ const falloff = 1 - dist / radius;
6947
+ const strength = force * falloff / v.mass;
6948
+ const n = dist;
6949
+ v.velocity.x += dx / n * strength;
6950
+ v.velocity.y += dy / n * strength;
6951
+ v.velocity.z += dz / n * strength;
6952
+ }
6953
+ }
6954
+ // ---------------------------------------------------------------------------
6955
+ // Simulation
6956
+ // ---------------------------------------------------------------------------
6957
+ update(dt) {
6958
+ for (const s of this.springs) {
6959
+ const a = this.vertices[s.a], b = this.vertices[s.b];
6960
+ const dx = b.current.x - a.current.x;
6961
+ const dy = b.current.y - a.current.y;
6962
+ const dz = b.current.z - a.current.z;
6963
+ const dist = Math.sqrt(dx * dx + dy * dy + dz * dz) || 1e-4;
6964
+ const stretch = dist - s.restLength;
6965
+ const fx = dx / dist * stretch * s.stiffness;
6966
+ const fy = dy / dist * stretch * s.stiffness;
6967
+ const fz = dz / dist * stretch * s.stiffness;
6968
+ const dvx = b.velocity.x - a.velocity.x;
6969
+ const dvy = b.velocity.y - a.velocity.y;
6970
+ const dvz = b.velocity.z - a.velocity.z;
6971
+ if (!a.locked) {
6972
+ a.velocity.x += (fx + dvx * s.damping) * dt / a.mass;
6973
+ a.velocity.y += (fy + dvy * s.damping) * dt / a.mass;
6974
+ a.velocity.z += (fz + dvz * s.damping) * dt / a.mass;
6975
+ }
6976
+ if (!b.locked) {
6977
+ b.velocity.x -= (fx + dvx * s.damping) * dt / b.mass;
6978
+ b.velocity.y -= (fy + dvy * s.damping) * dt / b.mass;
6979
+ b.velocity.z -= (fz + dvz * s.damping) * dt / b.mass;
6980
+ }
6981
+ }
6982
+ if (this.config.shapeMatchingStrength > 0) {
6983
+ let cx = 0, cy = 0, cz = 0;
6984
+ for (const v of this.vertices) {
6985
+ cx += v.current.x;
6986
+ cy += v.current.y;
6987
+ cz += v.current.z;
6988
+ }
6989
+ const n = this.vertices.length || 1;
6990
+ cx /= n;
6991
+ cy /= n;
6992
+ cz /= n;
6993
+ for (const v of this.vertices) {
6994
+ if (v.locked) continue;
6995
+ const goalX = v.rest.x - this.restCentroid.x + cx;
6996
+ const goalY = v.rest.y - this.restCentroid.y + cy;
6997
+ const goalZ = v.rest.z - this.restCentroid.z + cz;
6998
+ v.velocity.x += (goalX - v.current.x) * this.config.shapeMatchingStrength * dt * 10;
6999
+ v.velocity.y += (goalY - v.current.y) * this.config.shapeMatchingStrength * dt * 10;
7000
+ v.velocity.z += (goalZ - v.current.z) * this.config.shapeMatchingStrength * dt * 10;
7001
+ }
7002
+ }
7003
+ for (const v of this.vertices) {
7004
+ if (v.locked) continue;
7005
+ v.velocity.x *= this.config.damping;
7006
+ v.velocity.y *= this.config.damping;
7007
+ v.velocity.z *= this.config.damping;
7008
+ v.current.x += v.velocity.x * dt;
7009
+ v.current.y += v.velocity.y * dt;
7010
+ v.current.z += v.velocity.z * dt;
7011
+ const dx = v.current.x - v.rest.x;
7012
+ const dy = v.current.y - v.rest.y;
7013
+ const dz = v.current.z - v.rest.z;
7014
+ const disp = Math.sqrt(dx * dx + dy * dy + dz * dz);
7015
+ if (disp > this.config.maxDisplacement) {
7016
+ const scale = this.config.maxDisplacement / disp;
7017
+ v.current.x = v.rest.x + dx * scale;
7018
+ v.current.y = v.rest.y + dy * scale;
7019
+ v.current.z = v.rest.z + dz * scale;
7020
+ }
7021
+ if (this.config.plasticity > 0 && disp > 0.01) {
7022
+ v.rest.x += dx * this.config.plasticity * dt;
7023
+ v.rest.y += dy * this.config.plasticity * dt;
7024
+ v.rest.z += dz * this.config.plasticity * dt;
7025
+ }
7026
+ }
7027
+ }
7028
+ // ---------------------------------------------------------------------------
7029
+ // Queries
7030
+ // ---------------------------------------------------------------------------
7031
+ getVertices() {
7032
+ return this.vertices;
7033
+ }
7034
+ getVertex(index) {
7035
+ return this.vertices[index];
7036
+ }
7037
+ getVertexCount() {
7038
+ return this.vertices.length;
7039
+ }
7040
+ getSpringCount() {
7041
+ return this.springs.length;
7042
+ }
7043
+ getDisplacement(index) {
7044
+ const v = this.vertices[index];
7045
+ if (!v) return 0;
7046
+ const dx = v.current.x - v.rest.x, dy = v.current.y - v.rest.y, dz = v.current.z - v.rest.z;
7047
+ return Math.sqrt(dx * dx + dy * dy + dz * dz);
7048
+ }
7049
+ getMaxDisplacement() {
7050
+ let max = 0;
7051
+ for (let i = 0; i < this.vertices.length; i++) max = Math.max(max, this.getDisplacement(i));
7052
+ return max;
7053
+ }
7054
+ };
7055
+
7056
+ // src/physics/FluidSim.ts
7057
+ var FluidSim = class {
7058
+ particles = [];
7059
+ config;
7060
+ constructor(config) {
7061
+ this.config = {
7062
+ restDensity: 1e3,
7063
+ gasConstant: 2e3,
7064
+ viscosity: 200,
7065
+ surfaceTension: 0.5,
7066
+ gravity: { x: 0, y: -9.81, z: 0 },
7067
+ smoothingRadius: 1,
7068
+ timeStep: 0.016,
7069
+ boundaryMin: { x: -10, y: -10, z: -10 },
7070
+ boundaryMax: { x: 10, y: 10, z: 10 },
7071
+ boundaryDamping: 0.3,
7072
+ ...config
7073
+ };
7074
+ }
7075
+ // ---------------------------------------------------------------------------
7076
+ // Particle Management
7077
+ // ---------------------------------------------------------------------------
7078
+ addParticle(position, velocity) {
7079
+ this.particles.push({
7080
+ position: { ...position },
7081
+ velocity: velocity ? { ...velocity } : { x: 0, y: 0, z: 0 },
7082
+ density: this.config.restDensity,
7083
+ pressure: 0,
7084
+ mass: 1
7085
+ });
7086
+ }
7087
+ addBlock(min, max, spacing) {
7088
+ let count = 0;
7089
+ for (let x = min.x; x <= max.x; x += spacing) {
7090
+ for (let y = min.y; y <= max.y; y += spacing) {
7091
+ for (let z = min.z; z <= max.z; z += spacing) {
7092
+ this.addParticle([x, y, z]);
7093
+ count++;
7094
+ }
7095
+ }
7096
+ }
7097
+ return count;
7098
+ }
7099
+ // ---------------------------------------------------------------------------
7100
+ // SPH Kernels
7101
+ // ---------------------------------------------------------------------------
7102
+ poly6(r2, h) {
7103
+ if (r2 >= h * h) return 0;
7104
+ const h2 = h * h;
7105
+ const diff = h2 - r2;
7106
+ return 315 / (64 * Math.PI * Math.pow(h, 9)) * diff * diff * diff;
7107
+ }
7108
+ spikyGrad(r, h) {
7109
+ if (r >= h || r === 0) return 0;
7110
+ const diff = h - r;
7111
+ return -(45 / (Math.PI * Math.pow(h, 6))) * diff * diff;
7112
+ }
7113
+ viscosityLaplacian(r, h) {
7114
+ if (r >= h) return 0;
7115
+ return 45 / (Math.PI * Math.pow(h, 6)) * (h - r);
7116
+ }
7117
+ // ---------------------------------------------------------------------------
7118
+ // Simulation Step
7119
+ // ---------------------------------------------------------------------------
7120
+ update() {
7121
+ const h = this.config.smoothingRadius;
7122
+ const dt = this.config.timeStep;
7123
+ for (const pi of this.particles) {
7124
+ pi.density = 0;
7125
+ for (const pj of this.particles) {
7126
+ const dx = pj.position[0] - pi.position[0];
7127
+ const dy = pj.position[1] - pi.position[1];
7128
+ const dz = pj.position[2] - pi.position[2];
7129
+ const r2 = dx * dx + dy * dy + dz * dz;
7130
+ pi.density += pj.mass * this.poly6(r2, h);
7131
+ }
7132
+ pi.pressure = this.config.gasConstant * (pi.density - this.config.restDensity);
7133
+ }
7134
+ for (const pi of this.particles) {
7135
+ let fx = 0, fy = 0, fz = 0;
7136
+ for (const pj of this.particles) {
7137
+ if (pi === pj) continue;
7138
+ const dx = pj.position[0] - pi.position[0];
7139
+ const dy = pj.position[1] - pi.position[1];
7140
+ const dz = pj.position[2] - pi.position[2];
7141
+ const r = Math.sqrt(dx * dx + dy * dy + dz * dz);
7142
+ if (r < h && r > 0) {
7143
+ const pressGrad = this.spikyGrad(r, h);
7144
+ const pressScale = -pj.mass * (pi.pressure + pj.pressure) / (2 * pj.density || 1) * pressGrad;
7145
+ fx += dx / r * pressScale;
7146
+ fy += dy / r * pressScale;
7147
+ fz += dz / r * pressScale;
7148
+ const viscLap = this.viscosityLaplacian(r, h);
7149
+ const viscScale = this.config.viscosity * pj.mass / (pj.density || 1) * viscLap;
7150
+ fx += (pj.velocity.x - pi.velocity.x) * viscScale;
7151
+ fy += (pj.velocity.y - pi.velocity.y) * viscScale;
7152
+ fz += (pj.velocity.z - pi.velocity.z) * viscScale;
7153
+ }
7154
+ }
7155
+ fx += this.config.gravity.x * pi.density;
7156
+ fy += this.config.gravity.y * pi.density;
7157
+ fz += this.config.gravity.z * pi.density;
7158
+ const invDensity = 1 / (pi.density || 1);
7159
+ pi.velocity.x += fx * invDensity * dt;
7160
+ pi.velocity.y += fy * invDensity * dt;
7161
+ pi.velocity.z += fz * invDensity * dt;
7162
+ pi.position[0] += pi.velocity[0] * dt;
7163
+ pi.position[1] += pi.velocity[1] * dt;
7164
+ pi.position[2] += pi.velocity[2] * dt;
7165
+ }
7166
+ this.enforceBoundaries();
7167
+ }
7168
+ enforceBoundaries() {
7169
+ const { boundaryMin: mn, boundaryMax: mx, boundaryDamping: d } = this.config;
7170
+ for (const p of this.particles) {
7171
+ if (p.position[0] < mn[0]) {
7172
+ p.position[0] = mn[0];
7173
+ p.velocity.x *= -d;
7174
+ }
7175
+ if (p.position[0] > mx[0]) {
7176
+ p.position[0] = mx[0];
7177
+ p.velocity.x *= -d;
7178
+ }
7179
+ if (p.position[1] < mn[1]) {
7180
+ p.position[1] = mn[1];
7181
+ p.velocity.y *= -d;
7182
+ }
7183
+ if (p.position[1] > mx[1]) {
7184
+ p.position[1] = mx[1];
7185
+ p.velocity.y *= -d;
7186
+ }
7187
+ if (p.position[2] < mn[2]) {
7188
+ p.position[2] = mn[2];
7189
+ p.velocity.z *= -d;
7190
+ }
7191
+ if (p.position[2] > mx[2]) {
7192
+ p.position[2] = mx[2];
7193
+ p.velocity.z *= -d;
7194
+ }
7195
+ }
7196
+ }
7197
+ // ---------------------------------------------------------------------------
7198
+ // Queries
7199
+ // ---------------------------------------------------------------------------
7200
+ getParticles() {
7201
+ return this.particles;
7202
+ }
7203
+ getParticleCount() {
7204
+ return this.particles.length;
7205
+ }
7206
+ getAverageDensity() {
7207
+ if (this.particles.length === 0) return 0;
7208
+ return this.particles.reduce((s, p) => s + p.density, 0) / this.particles.length;
7209
+ }
7210
+ getKineticEnergy() {
7211
+ return this.particles.reduce((s, p) => {
7212
+ return s + 0.5 * p.mass * (p.velocity.x ** 2 + p.velocity.y ** 2 + p.velocity.z ** 2);
7213
+ }, 0);
7214
+ }
7215
+ clear() {
7216
+ this.particles = [];
7217
+ }
7218
+ setConfig(config) {
7219
+ Object.assign(this.config, config);
7220
+ }
7221
+ };
7222
+
7223
+ // src/physics/JointSystem.ts
7224
+ var _jointId = 0;
7225
+ var JointSystem = class {
7226
+ joints = /* @__PURE__ */ new Map();
7227
+ states = /* @__PURE__ */ new Map();
7228
+ bodyJoints = /* @__PURE__ */ new Map();
7229
+ // body → joint ids
7230
+ // ---------------------------------------------------------------------------
7231
+ // Joint Creation
7232
+ // ---------------------------------------------------------------------------
7233
+ createJoint(type, bodyA, bodyB, config) {
7234
+ const id = config?.id ?? `joint_${_jointId++}`;
7235
+ const joint = {
7236
+ id,
7237
+ type,
7238
+ bodyA,
7239
+ bodyB,
7240
+ anchorA: config?.anchorA ?? { x: 0, y: 0, z: 0 },
7241
+ anchorB: config?.anchorB ?? { x: 0, y: 0, z: 0 },
7242
+ axis: config?.axis ?? { x: 0, y: 1, z: 0 },
7243
+ limits: config?.limits,
7244
+ breakForce: config?.breakForce ?? Infinity,
7245
+ stiffness: config?.stiffness ?? 1,
7246
+ damping: config?.damping ?? 0.1,
7247
+ motorSpeed: config?.motorSpeed ?? 0,
7248
+ motorForce: config?.motorForce ?? 0,
7249
+ broken: false,
7250
+ enabled: true
7251
+ };
7252
+ this.joints.set(id, joint);
7253
+ this.states.set(id, { currentForce: 0, currentAngle: 0, currentDistance: 0 });
7254
+ for (const body of [bodyA, bodyB]) {
7255
+ if (!this.bodyJoints.has(body)) this.bodyJoints.set(body, /* @__PURE__ */ new Set());
7256
+ this.bodyJoints.get(body).add(id);
7257
+ }
7258
+ return joint;
7259
+ }
7260
+ removeJoint(id) {
7261
+ const joint = this.joints.get(id);
7262
+ if (!joint) return false;
7263
+ this.bodyJoints.get(joint.bodyA)?.delete(id);
7264
+ this.bodyJoints.get(joint.bodyB)?.delete(id);
7265
+ this.states.delete(id);
7266
+ return this.joints.delete(id);
7267
+ }
7268
+ // ---------------------------------------------------------------------------
7269
+ // Constraint Solving
7270
+ // ---------------------------------------------------------------------------
7271
+ solve(dt) {
7272
+ for (const [id, joint] of this.joints) {
7273
+ if (!joint.enabled || joint.broken) continue;
7274
+ const state = this.states.get(id);
7275
+ switch (joint.type) {
7276
+ case "spring": {
7277
+ const restLength = this.distance3D(joint.anchorA, joint.anchorB);
7278
+ const force = joint.stiffness * (state.currentDistance - restLength) + joint.damping * state.currentForce;
7279
+ state.currentForce = force;
7280
+ break;
7281
+ }
7282
+ case "distance": {
7283
+ const target = this.distance3D(joint.anchorA, joint.anchorB);
7284
+ state.currentDistance = target;
7285
+ state.currentForce = joint.stiffness * Math.abs(state.currentDistance - target);
7286
+ break;
7287
+ }
7288
+ case "hinge": {
7289
+ if (joint.limits) {
7290
+ state.currentAngle = Math.max(
7291
+ joint.limits.min,
7292
+ Math.min(joint.limits.max, state.currentAngle)
7293
+ );
7294
+ }
7295
+ if (joint.motorForce > 0) {
7296
+ state.currentAngle += joint.motorSpeed * dt;
7297
+ }
7298
+ state.currentForce = Math.abs(joint.stiffness * state.currentAngle);
7299
+ break;
7300
+ }
7301
+ case "slider": {
7302
+ if (joint.limits) {
7303
+ state.currentDistance = Math.max(
7304
+ joint.limits.min,
7305
+ Math.min(joint.limits.max, state.currentDistance)
7306
+ );
7307
+ }
7308
+ state.currentForce = joint.stiffness * Math.abs(state.currentDistance);
7309
+ break;
7310
+ }
7311
+ default:
7312
+ state.currentForce = 0;
7313
+ break;
7314
+ }
7315
+ if (state.currentForce > joint.breakForce) {
7316
+ joint.broken = true;
7317
+ }
7318
+ }
7319
+ }
7320
+ // ---------------------------------------------------------------------------
7321
+ // Controls
7322
+ // ---------------------------------------------------------------------------
7323
+ setMotor(id, speed, force) {
7324
+ const joint = this.joints.get(id);
7325
+ if (joint) {
7326
+ joint.motorSpeed = speed;
7327
+ joint.motorForce = force;
7328
+ }
7329
+ }
7330
+ setEnabled(id, enabled) {
7331
+ const joint = this.joints.get(id);
7332
+ if (joint) joint.enabled = enabled;
7333
+ }
7334
+ setAngle(id, angle) {
7335
+ const state = this.states.get(id);
7336
+ if (state) state.currentAngle = angle;
7337
+ }
7338
+ setDistance(id, dist) {
7339
+ const state = this.states.get(id);
7340
+ if (state) state.currentDistance = dist;
7341
+ }
7342
+ // ---------------------------------------------------------------------------
7343
+ // Queries
7344
+ // ---------------------------------------------------------------------------
7345
+ getJoint(id) {
7346
+ return this.joints.get(id);
7347
+ }
7348
+ getState(id) {
7349
+ return this.states.get(id);
7350
+ }
7351
+ getJointCount() {
7352
+ return this.joints.size;
7353
+ }
7354
+ getBrokenJoints() {
7355
+ return [...this.joints.values()].filter((j) => j.broken);
7356
+ }
7357
+ getJointsForBody(bodyId) {
7358
+ const ids = this.bodyJoints.get(bodyId);
7359
+ if (!ids) return [];
7360
+ return [...ids].map((id) => this.joints.get(id)).filter(Boolean);
7361
+ }
7362
+ // ---------------------------------------------------------------------------
7363
+ // Helpers
7364
+ // ---------------------------------------------------------------------------
7365
+ distance3D(a, b) {
7366
+ const dx = b.x - a.x, dy = b.y - a.y, dz = b.z - a.z;
7367
+ return Math.sqrt(dx * dx + dy * dy + dz * dz);
7368
+ }
7369
+ };
7370
+
7371
+ // src/physics/RagdollController.ts
7372
+ var RagdollController = class {
7373
+ bones = /* @__PURE__ */ new Map();
7374
+ rootBone = null;
7375
+ state = "active";
7376
+ blendFactor = 0;
7377
+ // 0 = animated, 1 = ragdoll
7378
+ blendSpeed = 2;
7379
+ config;
7380
+ constructor(config) {
7381
+ this.config = { gravity: -9.81, damping: 0.98, iterations: 4, ...config };
7382
+ }
7383
+ // ---------------------------------------------------------------------------
7384
+ // Bone Chain Setup
7385
+ // ---------------------------------------------------------------------------
7386
+ addBone(name, parentId, mass, length, limits) {
7387
+ const bone = {
7388
+ id: name,
7389
+ name,
7390
+ parentId,
7391
+ position: [0, 0, 0],
7392
+ rotation: { x: 0, y: 0, z: 0 },
7393
+ velocity: { x: 0, y: 0, z: 0 },
7394
+ angularVelocity: { x: 0, y: 0, z: 0 },
7395
+ mass,
7396
+ length,
7397
+ jointLimits: limits ?? { min: { x: -1, y: -1, z: -1 }, max: { x: 1, y: 1, z: 1 } }
7398
+ };
7399
+ this.bones.set(name, bone);
7400
+ if (!parentId) this.rootBone = name;
7401
+ return bone;
7402
+ }
7403
+ removeBone(name) {
7404
+ return this.bones.delete(name);
7405
+ }
7406
+ // ---------------------------------------------------------------------------
7407
+ // State Control
7408
+ // ---------------------------------------------------------------------------
7409
+ activate() {
7410
+ this.state = "active";
7411
+ this.blendFactor = 0;
7412
+ }
7413
+ goRagdoll() {
7414
+ this.state = "ragdoll";
7415
+ this.blendFactor = 1;
7416
+ }
7417
+ startBlend(toRagdoll = true) {
7418
+ this.state = "blending";
7419
+ this.blendFactor = toRagdoll ? 0 : 1;
7420
+ }
7421
+ getState() {
7422
+ return this.state;
7423
+ }
7424
+ getBlendFactor() {
7425
+ return this.blendFactor;
7426
+ }
7427
+ // ---------------------------------------------------------------------------
7428
+ // Physics Update
7429
+ // ---------------------------------------------------------------------------
7430
+ update(dt) {
7431
+ if (this.state === "blending") {
7432
+ this.blendFactor = Math.min(1, this.blendFactor + this.blendSpeed * dt);
7433
+ if (this.blendFactor >= 1) this.state = "ragdoll";
7434
+ }
7435
+ if (this.state === "active") return;
7436
+ for (const bone of this.bones.values()) {
7437
+ bone.velocity.y += this.config.gravity * dt * this.blendFactor;
7438
+ bone.velocity.x *= this.config.damping;
7439
+ bone.velocity.y *= this.config.damping;
7440
+ bone.velocity.z *= this.config.damping;
7441
+ bone.position[0] += bone.velocity[0] * dt;
7442
+ bone.position[1] += bone.velocity[1] * dt;
7443
+ bone.position[2] += bone.velocity[2] * dt;
7444
+ }
7445
+ for (let iter = 0; iter < this.config.iterations; iter++) {
7446
+ for (const bone of this.bones.values()) {
7447
+ if (!bone.parentId) continue;
7448
+ const parent = this.bones.get(bone.parentId);
7449
+ if (!parent) continue;
7450
+ const dx = bone.position[0] - parent.position[0];
7451
+ const dy = bone.position[1] - parent.position[1];
7452
+ const dz = bone.position[2] - parent.position[2];
7453
+ const dist = Math.sqrt(dx * dx + dy * dy + dz * dz);
7454
+ if (dist > 0 && dist !== bone.length) {
7455
+ const diff = (dist - bone.length) / dist;
7456
+ const mx = dx * diff * 0.5;
7457
+ const my = dy * diff * 0.5;
7458
+ const mz = dz * diff * 0.5;
7459
+ bone.position[0] -= mx;
7460
+ bone.position[1] -= my;
7461
+ bone.position[2] -= mz;
7462
+ parent.position[0] += mx;
7463
+ parent.position[1] += my;
7464
+ parent.position[2] += mz;
7465
+ }
7466
+ bone.rotation.x = Math.max(
7467
+ bone.jointLimits.min.x,
7468
+ Math.min(bone.jointLimits.max.x, bone.rotation.x)
7469
+ );
7470
+ bone.rotation.y = Math.max(
7471
+ bone.jointLimits.min.y,
7472
+ Math.min(bone.jointLimits.max.y, bone.rotation.y)
7473
+ );
7474
+ bone.rotation.z = Math.max(
7475
+ bone.jointLimits.min.z,
7476
+ Math.min(bone.jointLimits.max.z, bone.rotation.z)
7477
+ );
7478
+ }
7479
+ }
7480
+ }
7481
+ // ---------------------------------------------------------------------------
7482
+ // Impulse
7483
+ // ---------------------------------------------------------------------------
7484
+ applyImpulse(boneId, impulse) {
7485
+ const bone = this.bones.get(boneId);
7486
+ if (!bone) return;
7487
+ bone.velocity.x += impulse.x / bone.mass;
7488
+ bone.velocity.y += impulse.y / bone.mass;
7489
+ bone.velocity.z += impulse.z / bone.mass;
7490
+ }
7491
+ // ---------------------------------------------------------------------------
7492
+ // Queries
7493
+ // ---------------------------------------------------------------------------
7494
+ getBone(id) {
7495
+ return this.bones.get(id);
7496
+ }
7497
+ getBoneCount() {
7498
+ return this.bones.size;
7499
+ }
7500
+ getRootBone() {
7501
+ return this.rootBone ? this.bones.get(this.rootBone) : void 0;
7502
+ }
7503
+ getChildren(boneId) {
7504
+ return [...this.bones.values()].filter((b) => b.parentId === boneId);
7505
+ }
7506
+ };
7507
+
7508
+ // src/physics/RagdollSystem.ts
7509
+ var HUMANOID_PRESET = [
7510
+ // Torso
7511
+ {
7512
+ id: "pelvis",
7513
+ length: 0.25,
7514
+ radius: 0.12,
7515
+ mass: 15,
7516
+ localOffset: { x: 0, y: 0, z: 0 },
7517
+ jointType: "cone"
7518
+ },
7519
+ {
7520
+ id: "spine",
7521
+ parentBone: "pelvis",
7522
+ length: 0.3,
7523
+ radius: 0.1,
7524
+ mass: 12,
7525
+ localOffset: { x: 0, y: 0.25, z: 0 },
7526
+ jointType: "cone",
7527
+ jointLimits: { swingSpan1: 0.3, swingSpan2: 0.3, twistSpan: 0.2 }
7528
+ },
7529
+ {
7530
+ id: "chest",
7531
+ parentBone: "spine",
7532
+ length: 0.25,
7533
+ radius: 0.12,
7534
+ mass: 10,
7535
+ localOffset: { x: 0, y: 0.3, z: 0 },
7536
+ jointType: "cone",
7537
+ jointLimits: { swingSpan1: 0.2, swingSpan2: 0.2, twistSpan: 0.15 }
7538
+ },
7539
+ {
7540
+ id: "head",
7541
+ parentBone: "chest",
7542
+ length: 0.2,
7543
+ radius: 0.1,
7544
+ mass: 5,
7545
+ localOffset: { x: 0, y: 0.25, z: 0 },
7546
+ jointType: "cone",
7547
+ jointLimits: { swingSpan1: 0.5, swingSpan2: 0.3, twistSpan: 0.4 }
7548
+ },
7549
+ // Left arm
7550
+ {
7551
+ id: "l_upper_arm",
7552
+ parentBone: "chest",
7553
+ length: 0.28,
7554
+ radius: 0.04,
7555
+ mass: 3,
7556
+ localOffset: { x: -0.18, y: 0.15, z: 0 },
7557
+ jointType: "cone",
7558
+ jointLimits: { swingSpan1: 1.5, swingSpan2: 1, twistSpan: 0.8 }
7559
+ },
7560
+ {
7561
+ id: "l_forearm",
7562
+ parentBone: "l_upper_arm",
7563
+ length: 0.25,
7564
+ radius: 0.035,
7565
+ mass: 2,
7566
+ localOffset: { x: 0, y: -0.28, z: 0 },
7567
+ jointType: "hinge",
7568
+ jointLimits: { low: 0, high: 2.5 }
7569
+ },
7570
+ {
7571
+ id: "l_hand",
7572
+ parentBone: "l_forearm",
7573
+ length: 0.1,
7574
+ radius: 0.03,
7575
+ mass: 0.5,
7576
+ localOffset: { x: 0, y: -0.25, z: 0 },
7577
+ jointType: "cone",
7578
+ jointLimits: { swingSpan1: 0.5, swingSpan2: 0.3, twistSpan: 0.3 }
7579
+ },
7580
+ // Right arm
7581
+ {
7582
+ id: "r_upper_arm",
7583
+ parentBone: "chest",
7584
+ length: 0.28,
7585
+ radius: 0.04,
7586
+ mass: 3,
7587
+ localOffset: { x: 0.18, y: 0.15, z: 0 },
7588
+ jointType: "cone",
7589
+ jointLimits: { swingSpan1: 1.5, swingSpan2: 1, twistSpan: 0.8 }
7590
+ },
7591
+ {
7592
+ id: "r_forearm",
7593
+ parentBone: "r_upper_arm",
7594
+ length: 0.25,
7595
+ radius: 0.035,
7596
+ mass: 2,
7597
+ localOffset: { x: 0, y: -0.28, z: 0 },
7598
+ jointType: "hinge",
7599
+ jointLimits: { low: 0, high: 2.5 }
7600
+ },
7601
+ {
7602
+ id: "r_hand",
7603
+ parentBone: "r_forearm",
7604
+ length: 0.1,
7605
+ radius: 0.03,
7606
+ mass: 0.5,
7607
+ localOffset: { x: 0, y: -0.25, z: 0 },
7608
+ jointType: "cone",
7609
+ jointLimits: { swingSpan1: 0.5, swingSpan2: 0.3, twistSpan: 0.3 }
7610
+ },
7611
+ // Left leg
7612
+ {
7613
+ id: "l_thigh",
7614
+ parentBone: "pelvis",
7615
+ length: 0.4,
7616
+ radius: 0.06,
7617
+ mass: 8,
7618
+ localOffset: { x: -0.1, y: -0.15, z: 0 },
7619
+ jointType: "cone",
7620
+ jointLimits: { swingSpan1: 1.2, swingSpan2: 0.5, twistSpan: 0.3 }
7621
+ },
7622
+ {
7623
+ id: "l_shin",
7624
+ parentBone: "l_thigh",
7625
+ length: 0.38,
7626
+ radius: 0.05,
7627
+ mass: 5,
7628
+ localOffset: { x: 0, y: -0.4, z: 0 },
7629
+ jointType: "hinge",
7630
+ jointLimits: { low: -2.5, high: 0 }
7631
+ },
7632
+ {
7633
+ id: "l_foot",
7634
+ parentBone: "l_shin",
7635
+ length: 0.15,
7636
+ radius: 0.04,
7637
+ mass: 1,
7638
+ localOffset: { x: 0, y: -0.38, z: 0.05 },
7639
+ jointType: "hinge",
7640
+ jointLimits: { low: -0.5, high: 0.5 }
7641
+ },
7642
+ // Right leg
7643
+ {
7644
+ id: "r_thigh",
7645
+ parentBone: "pelvis",
7646
+ length: 0.4,
7647
+ radius: 0.06,
7648
+ mass: 8,
7649
+ localOffset: { x: 0.1, y: -0.15, z: 0 },
7650
+ jointType: "cone",
7651
+ jointLimits: { swingSpan1: 1.2, swingSpan2: 0.5, twistSpan: 0.3 }
7652
+ },
7653
+ {
7654
+ id: "r_shin",
7655
+ parentBone: "r_thigh",
7656
+ length: 0.38,
7657
+ radius: 0.05,
7658
+ mass: 5,
7659
+ localOffset: { x: 0, y: -0.4, z: 0 },
7660
+ jointType: "hinge",
7661
+ jointLimits: { low: -2.5, high: 0 }
7662
+ },
7663
+ {
7664
+ id: "r_foot",
7665
+ parentBone: "r_shin",
7666
+ length: 0.15,
7667
+ radius: 0.04,
7668
+ mass: 1,
7669
+ localOffset: { x: 0, y: -0.38, z: 0.05 },
7670
+ jointType: "hinge",
7671
+ jointLimits: { low: -0.5, high: 0.5 }
7672
+ }
7673
+ ];
7674
+ var QUADRUPED_PRESET = [
7675
+ // Body
7676
+ {
7677
+ id: "body",
7678
+ length: 0.6,
7679
+ radius: 0.15,
7680
+ mass: 20,
7681
+ localOffset: { x: 0, y: 0, z: 0 },
7682
+ jointType: "cone"
7683
+ },
7684
+ {
7685
+ id: "neck",
7686
+ parentBone: "body",
7687
+ length: 0.2,
7688
+ radius: 0.06,
7689
+ mass: 3,
7690
+ localOffset: { x: 0, y: 0.1, z: 0.3 },
7691
+ jointType: "cone",
7692
+ jointLimits: { swingSpan1: 0.8, swingSpan2: 0.4, twistSpan: 0.3 }
7693
+ },
7694
+ {
7695
+ id: "head",
7696
+ parentBone: "neck",
7697
+ length: 0.15,
7698
+ radius: 0.08,
7699
+ mass: 2,
7700
+ localOffset: { x: 0, y: 0.05, z: 0.2 },
7701
+ jointType: "cone",
7702
+ jointLimits: { swingSpan1: 0.6, swingSpan2: 0.4, twistSpan: 0.3 }
7703
+ },
7704
+ {
7705
+ id: "tail",
7706
+ parentBone: "body",
7707
+ length: 0.3,
7708
+ radius: 0.02,
7709
+ mass: 0.5,
7710
+ localOffset: { x: 0, y: 0.05, z: -0.35 },
7711
+ jointType: "cone",
7712
+ jointLimits: { swingSpan1: 1, swingSpan2: 0.5, twistSpan: 0.5 }
7713
+ },
7714
+ // Front legs
7715
+ {
7716
+ id: "fl_upper",
7717
+ parentBone: "body",
7718
+ length: 0.25,
7719
+ radius: 0.03,
7720
+ mass: 2,
7721
+ localOffset: { x: -0.12, y: -0.15, z: 0.2 },
7722
+ jointType: "cone",
7723
+ jointLimits: { swingSpan1: 0.8, swingSpan2: 0.4, twistSpan: 0.2 }
7724
+ },
7725
+ {
7726
+ id: "fl_lower",
7727
+ parentBone: "fl_upper",
7728
+ length: 0.2,
7729
+ radius: 0.025,
7730
+ mass: 1.5,
7731
+ localOffset: { x: 0, y: -0.25, z: 0 },
7732
+ jointType: "hinge",
7733
+ jointLimits: { low: -2, high: 0 }
7734
+ },
7735
+ {
7736
+ id: "fr_upper",
7737
+ parentBone: "body",
7738
+ length: 0.25,
7739
+ radius: 0.03,
7740
+ mass: 2,
7741
+ localOffset: { x: 0.12, y: -0.15, z: 0.2 },
7742
+ jointType: "cone",
7743
+ jointLimits: { swingSpan1: 0.8, swingSpan2: 0.4, twistSpan: 0.2 }
7744
+ },
7745
+ {
7746
+ id: "fr_lower",
7747
+ parentBone: "fr_upper",
7748
+ length: 0.2,
7749
+ radius: 0.025,
7750
+ mass: 1.5,
7751
+ localOffset: { x: 0, y: -0.25, z: 0 },
7752
+ jointType: "hinge",
7753
+ jointLimits: { low: -2, high: 0 }
7754
+ },
7755
+ // Hind legs
7756
+ {
7757
+ id: "hl_upper",
7758
+ parentBone: "body",
7759
+ length: 0.28,
7760
+ radius: 0.04,
7761
+ mass: 3,
7762
+ localOffset: { x: -0.12, y: -0.15, z: -0.2 },
7763
+ jointType: "cone",
7764
+ jointLimits: { swingSpan1: 1, swingSpan2: 0.5, twistSpan: 0.2 }
7765
+ },
7766
+ {
7767
+ id: "hl_lower",
7768
+ parentBone: "hl_upper",
7769
+ length: 0.22,
7770
+ radius: 0.03,
7771
+ mass: 2,
7772
+ localOffset: { x: 0, y: -0.28, z: 0 },
7773
+ jointType: "hinge",
7774
+ jointLimits: { low: 0, high: 2 }
7775
+ },
7776
+ {
7777
+ id: "hr_upper",
7778
+ parentBone: "body",
7779
+ length: 0.28,
7780
+ radius: 0.04,
7781
+ mass: 3,
7782
+ localOffset: { x: 0.12, y: -0.15, z: -0.2 },
7783
+ jointType: "cone",
7784
+ jointLimits: { swingSpan1: 1, swingSpan2: 0.5, twistSpan: 0.2 }
7785
+ },
7786
+ {
7787
+ id: "hr_lower",
7788
+ parentBone: "hr_upper",
7789
+ length: 0.22,
7790
+ radius: 0.03,
7791
+ mass: 2,
7792
+ localOffset: { x: 0, y: -0.28, z: 0 },
7793
+ jointType: "hinge",
7794
+ jointLimits: { low: 0, high: 2 }
7795
+ }
7796
+ ];
7797
+ var RagdollSystem = class {
7798
+ ragdolls = /* @__PURE__ */ new Map();
7799
+ /**
7800
+ * Create a ragdoll from a definition at a given world position.
7801
+ */
7802
+ createRagdoll(definition, rootPosition) {
7803
+ const bodies = [];
7804
+ const constraints = [];
7805
+ const bonePositions = /* @__PURE__ */ new Map();
7806
+ for (const bone of definition.bones) {
7807
+ const parentPos = bone.parentBone ? bonePositions.get(bone.parentBone) || rootPosition : rootPosition;
7808
+ const worldPos = {
7809
+ x: parentPos.x + bone.localOffset.x,
7810
+ y: parentPos.y + bone.localOffset.y,
7811
+ z: parentPos.z + bone.localOffset.z
7812
+ };
7813
+ bonePositions.set(bone.id, worldPos);
7814
+ const bodyConfig = {
7815
+ id: `${definition.id}_${bone.id}`,
7816
+ type: "dynamic",
7817
+ transform: {
7818
+ position: worldPos,
7819
+ rotation: { x: 0, y: 0, z: 0, w: 1 },
7820
+ scale: { x: 1, y: 1, z: 1 }
7821
+ },
7822
+ shape: {
7823
+ type: "capsule",
7824
+ radius: bone.radius,
7825
+ height: bone.length
7826
+ },
7827
+ mass: bone.mass,
7828
+ material: {
7829
+ friction: 0.6,
7830
+ restitution: 0.1
7831
+ },
7832
+ linearDamping: 0.05,
7833
+ angularDamping: 0.85
7834
+ };
7835
+ bodies.push(bodyConfig);
7836
+ }
7837
+ for (const bone of definition.bones) {
7838
+ if (!bone.parentBone) continue;
7839
+ const bodyAId = `${definition.id}_${bone.parentBone}`;
7840
+ const bodyBId = `${definition.id}_${bone.id}`;
7841
+ const constraintId = `${definition.id}_joint_${bone.parentBone}_${bone.id}`;
7842
+ if (bone.jointType === "cone") {
7843
+ const coneConstraint = {
7844
+ type: "cone",
7845
+ id: constraintId,
7846
+ bodyA: bodyAId,
7847
+ bodyB: bodyBId,
7848
+ pivotA: bone.localOffset,
7849
+ pivotB: { x: 0, y: 0, z: 0 },
7850
+ axisA: { x: 0, y: 1, z: 0 },
7851
+ axisB: { x: 0, y: 1, z: 0 },
7852
+ swingSpan1: bone.jointLimits?.swingSpan1 ?? 0.5,
7853
+ swingSpan2: bone.jointLimits?.swingSpan2 ?? 0.3,
7854
+ twistSpan: bone.jointLimits?.twistSpan ?? 0.2
7855
+ };
7856
+ constraints.push(coneConstraint);
7857
+ } else {
7858
+ const hingeConstraint = {
7859
+ type: "hinge",
7860
+ id: constraintId,
7861
+ bodyA: bodyAId,
7862
+ bodyB: bodyBId,
7863
+ pivotA: bone.localOffset,
7864
+ pivotB: { x: 0, y: 0, z: 0 },
7865
+ axisA: { x: 1, y: 0, z: 0 },
7866
+ axisB: { x: 1, y: 0, z: 0 },
7867
+ limits: {
7868
+ low: bone.jointLimits?.low ?? -1,
7869
+ high: bone.jointLimits?.high ?? 1
7870
+ }
7871
+ };
7872
+ constraints.push(hingeConstraint);
7873
+ }
7874
+ }
7875
+ const instance = {
7876
+ id: definition.id,
7877
+ definition,
7878
+ bodies,
7879
+ constraints,
7880
+ rootPosition
7881
+ };
7882
+ this.ragdolls.set(definition.id, instance);
7883
+ return instance;
7884
+ }
7885
+ /**
7886
+ * Create humanoid ragdoll from preset.
7887
+ */
7888
+ createHumanoid(id, rootPosition) {
7889
+ return this.createRagdoll({ id, bones: HUMANOID_PRESET }, rootPosition);
7890
+ }
7891
+ /**
7892
+ * Create quadruped ragdoll from preset.
7893
+ */
7894
+ createQuadruped(id, rootPosition) {
7895
+ return this.createRagdoll({ id, bones: QUADRUPED_PRESET }, rootPosition);
7896
+ }
7897
+ /**
7898
+ * Get a ragdoll instance by ID.
7899
+ */
7900
+ getRagdoll(id) {
7901
+ return this.ragdolls.get(id);
7902
+ }
7903
+ /**
7904
+ * Remove a ragdoll.
7905
+ */
7906
+ removeRagdoll(id) {
7907
+ return this.ragdolls.delete(id);
7908
+ }
7909
+ /**
7910
+ * Get total mass of a ragdoll.
7911
+ */
7912
+ getTotalMass(id) {
7913
+ const ragdoll = this.ragdolls.get(id);
7914
+ if (!ragdoll) return 0;
7915
+ return ragdoll.definition.bones.reduce((sum, bone) => sum + bone.mass, 0);
7916
+ }
7917
+ };
7918
+
7919
+ // src/physics/RaycastSystem.ts
7920
+ var RaycastSystem = class {
7921
+ colliders = /* @__PURE__ */ new Map();
7922
+ // ---------------------------------------------------------------------------
7923
+ // Registration
7924
+ // ---------------------------------------------------------------------------
7925
+ addCollider(collider) {
7926
+ this.colliders.set(collider.entityId, collider);
7927
+ }
7928
+ removeCollider(entityId) {
7929
+ this.colliders.delete(entityId);
7930
+ }
7931
+ getColliderCount() {
7932
+ return this.colliders.size;
7933
+ }
7934
+ // ---------------------------------------------------------------------------
7935
+ // Raycasting
7936
+ // ---------------------------------------------------------------------------
7937
+ raycast(ray, maxDistance = Infinity, layerMask = 4294967295) {
7938
+ const hits = this.raycastAll(ray, maxDistance, layerMask);
7939
+ return hits.length > 0 ? hits[0] : null;
7940
+ }
7941
+ raycastAll(ray, maxDistance = Infinity, layerMask = 4294967295) {
7942
+ const hits = [];
7943
+ const dir = this.normalize(ray.direction);
7944
+ for (const collider of this.colliders.values()) {
7945
+ if ((collider.layer & layerMask) === 0) continue;
7946
+ let hit = null;
7947
+ switch (collider.type) {
7948
+ case "aabb":
7949
+ hit = this.rayAABB(ray.origin, dir, collider.shape, collider.entityId);
7950
+ break;
7951
+ case "sphere":
7952
+ hit = this.raySphere(ray.origin, dir, collider.shape, collider.entityId);
7953
+ break;
7954
+ case "plane":
7955
+ hit = this.rayPlane(ray.origin, dir, collider.shape, collider.entityId);
7956
+ break;
7957
+ }
7958
+ if (hit && hit.distance <= maxDistance) hits.push(hit);
7959
+ }
7960
+ return hits.sort((a, b) => a.distance - b.distance);
7961
+ }
7962
+ // ---------------------------------------------------------------------------
7963
+ // Intersection Tests
7964
+ // ---------------------------------------------------------------------------
7965
+ rayAABB(origin, dir, aabb, entityId) {
7966
+ let tmin = -Infinity, tmax = Infinity;
7967
+ const axes = ["x", "y", "z"];
7968
+ let hitNormal = { x: 0, y: 0, z: 0 };
7969
+ for (const axis of axes) {
7970
+ if (Math.abs(dir[axis]) < 1e-10) {
7971
+ if (origin[axis] < aabb.min[axis] || origin[axis] > aabb.max[axis]) return null;
7972
+ continue;
7973
+ }
7974
+ const t1 = (aabb.min[axis] - origin[axis]) / dir[axis];
7975
+ const t2 = (aabb.max[axis] - origin[axis]) / dir[axis];
7976
+ const tNear = Math.min(t1, t2);
7977
+ const tFar = Math.max(t1, t2);
7978
+ if (tNear > tmin) {
7979
+ tmin = tNear;
7980
+ hitNormal = { x: 0, y: 0, z: 0 };
7981
+ hitNormal[axis] = dir[axis] > 0 ? -1 : 1;
7982
+ }
7983
+ tmax = Math.min(tmax, tFar);
7984
+ if (tmin > tmax || tmax < 0) return null;
7985
+ }
7986
+ const t = tmin >= 0 ? tmin : tmax;
7987
+ if (t < 0) return null;
7988
+ return {
7989
+ entityId,
7990
+ distance: t,
7991
+ point: { x: origin.x + dir.x * t, y: origin.y + dir.y * t, z: origin.z + dir.z * t },
7992
+ normal: hitNormal
7993
+ };
7994
+ }
7995
+ raySphere(origin, dir, sphere, entityId) {
7996
+ const ox = origin.x - sphere.center.x, oy = origin.y - sphere.center.y, oz = origin.z - sphere.center.z;
7997
+ const a = dir.x * dir.x + dir.y * dir.y + dir.z * dir.z;
7998
+ const b = 2 * (ox * dir.x + oy * dir.y + oz * dir.z);
7999
+ const c = ox * ox + oy * oy + oz * oz - sphere.radius * sphere.radius;
8000
+ const disc = b * b - 4 * a * c;
8001
+ if (disc < 0) return null;
8002
+ const sqrtDisc = Math.sqrt(disc);
8003
+ let t = (-b - sqrtDisc) / (2 * a);
8004
+ if (t < 0) t = (-b + sqrtDisc) / (2 * a);
8005
+ if (t < 0) return null;
8006
+ const point = { x: origin.x + dir.x * t, y: origin.y + dir.y * t, z: origin.z + dir.z * t };
8007
+ const nx = point.x - sphere.center.x, ny = point.y - sphere.center.y, nz = point.z - sphere.center.z;
8008
+ const nLen = Math.sqrt(nx * nx + ny * ny + nz * nz) || 1;
8009
+ return {
8010
+ entityId,
8011
+ distance: t,
8012
+ point,
8013
+ normal: { x: nx / nLen, y: ny / nLen, z: nz / nLen }
8014
+ };
8015
+ }
8016
+ rayPlane(origin, dir, plane, entityId) {
8017
+ const denom = plane.normal.x * dir.x + plane.normal.y * dir.y + plane.normal.z * dir.z;
8018
+ if (Math.abs(denom) < 1e-10) return null;
8019
+ const t = -(plane.normal.x * origin.x + plane.normal.y * origin.y + plane.normal.z * origin.z + plane.distance) / denom;
8020
+ if (t < 0) return null;
8021
+ return {
8022
+ entityId,
8023
+ distance: t,
8024
+ point: { x: origin.x + dir.x * t, y: origin.y + dir.y * t, z: origin.z + dir.z * t },
8025
+ normal: { ...plane.normal }
8026
+ };
8027
+ }
8028
+ // ---------------------------------------------------------------------------
8029
+ // Helpers
8030
+ // ---------------------------------------------------------------------------
8031
+ normalize(v) {
8032
+ const len = Math.sqrt(v.x * v.x + v.y * v.y + v.z * v.z) || 1;
8033
+ return { x: v.x / len, y: v.y / len, z: v.z / len };
8034
+ }
8035
+ };
8036
+
8037
+ // src/physics/RopeSystem.ts
8038
+ var RopeSystem = class {
8039
+ ropes = /* @__PURE__ */ new Map();
8040
+ // ---------------------------------------------------------------------------
8041
+ // Creation
8042
+ // ---------------------------------------------------------------------------
8043
+ createRope(id, start, end, config) {
8044
+ const segCount = config?.segmentCount ?? 10;
8045
+ const dx = end.x - start.x, dy = end.y - start.y, dz = end.z - start.z;
8046
+ const totalLength = Math.sqrt(dx * dx + dy * dy + dz * dz);
8047
+ const autoSegmentLength = totalLength / segCount;
8048
+ const cfg = {
8049
+ segmentCount: segCount,
8050
+ segmentLength: autoSegmentLength,
8051
+ gravity: { x: 0, y: -9.81, z: 0 },
8052
+ damping: 0.98,
8053
+ iterations: 8,
8054
+ elasticity: 1,
8055
+ ...config
8056
+ };
8057
+ const nodes = [];
8058
+ for (let i = 0; i <= cfg.segmentCount; i++) {
8059
+ const t = i / cfg.segmentCount;
8060
+ nodes.push({
8061
+ position: [start.x + (end.x - start.x) * t, start.y + (end.y - start.y) * t, start.z + (end.z - start.z) * t],
8062
+ previous: {
8063
+ x: start.x + (end.x - start.x) * t,
8064
+ y: start.y + (end.y - start.y) * t,
8065
+ z: start.z + (end.z - start.z) * t
8066
+ },
8067
+ mass: 1,
8068
+ pinned: false
8069
+ });
8070
+ }
8071
+ this.ropes.set(id, { nodes, config: cfg, attachments: [] });
8072
+ }
8073
+ // ---------------------------------------------------------------------------
8074
+ // Pin / Attach
8075
+ // ---------------------------------------------------------------------------
8076
+ pinNode(ropeId, nodeIndex) {
8077
+ const rope = this.ropes.get(ropeId);
8078
+ if (rope?.nodes[nodeIndex]) rope.nodes[nodeIndex].pinned = true;
8079
+ }
8080
+ unpinNode(ropeId, nodeIndex) {
8081
+ const rope = this.ropes.get(ropeId);
8082
+ if (rope?.nodes[nodeIndex]) rope.nodes[nodeIndex].pinned = false;
8083
+ }
8084
+ attach(ropeId, attachment) {
8085
+ const rope = this.ropes.get(ropeId);
8086
+ if (rope) rope.attachments.push(attachment);
8087
+ }
8088
+ // ---------------------------------------------------------------------------
8089
+ // Simulation
8090
+ // ---------------------------------------------------------------------------
8091
+ update(dt) {
8092
+ const dt2 = dt * dt;
8093
+ for (const rope of this.ropes.values()) {
8094
+ const { nodes, config } = rope;
8095
+ for (const n of nodes) {
8096
+ if (n.pinned) continue;
8097
+ const vx = (n.position[0] - n.previous[0]) * config.damping;
8098
+ const vy = (n.position[1] - n.previous[1]) * config.damping;
8099
+ const vz = (n.position[2] - n.previous[2]) * config.damping;
8100
+ n.previous = { ...n.position };
8101
+ n.position[0] += vx + config.gravity[0] * dt2;
8102
+ n.position[1] += vy + config.gravity[1] * dt2;
8103
+ n.position[2] += vz + config.gravity[2] * dt2;
8104
+ }
8105
+ for (let iter = 0; iter < config.iterations; iter++) {
8106
+ for (let i = 0; i < nodes.length - 1; i++) {
8107
+ const a = nodes[i], b = nodes[i + 1];
8108
+ const dx = b.position[0] - a.position[0];
8109
+ const dy = b.position[1] - a.position[1];
8110
+ const dz = b.position[2] - a.position[2];
8111
+ const dist = Math.sqrt(dx * dx + dy * dy + dz * dz) || 1e-4;
8112
+ const diff = (config.segmentLength - dist) / dist * config.elasticity * 0.5;
8113
+ const ox = dx * diff, oy = dy * diff, oz = dz * diff;
8114
+ if (!a.pinned) {
8115
+ a.position[0] -= ox;
8116
+ a.position[1] -= oy;
8117
+ a.position[2] -= oz;
8118
+ }
8119
+ if (!b.pinned) {
8120
+ b.position[0] += ox;
8121
+ b.position[1] += oy;
8122
+ b.position[2] += oz;
8123
+ }
8124
+ }
8125
+ }
8126
+ }
8127
+ }
8128
+ // ---------------------------------------------------------------------------
8129
+ // Queries
8130
+ // ---------------------------------------------------------------------------
8131
+ getRopeNodes(ropeId) {
8132
+ return this.ropes.get(ropeId)?.nodes ?? [];
8133
+ }
8134
+ getRopeLength(ropeId) {
8135
+ const nodes = this.ropes.get(ropeId)?.nodes;
8136
+ if (!nodes || nodes.length < 2) return 0;
8137
+ let len = 0;
8138
+ for (let i = 1; i < nodes.length; i++) {
8139
+ const a = nodes[i - 1].position, b = nodes[i].position;
8140
+ const dx = b[0] - a[0], dy = b[1] - a[1], dz = b[2] - a[2];
8141
+ len += Math.sqrt(dx * dx + dy * dy + dz * dz);
8142
+ }
8143
+ return len;
8144
+ }
8145
+ getTension(ropeId, nodeIndex) {
8146
+ const rope = this.ropes.get(ropeId);
8147
+ if (!rope || nodeIndex < 0 || nodeIndex >= rope.nodes.length - 1) return 0;
8148
+ const a = rope.nodes[nodeIndex].position, b = rope.nodes[nodeIndex + 1].position;
8149
+ const dx = b[0] - a[0], dy = b[1] - a[1], dz = b[2] - a[2];
8150
+ const dist = Math.sqrt(dx * dx + dy * dy + dz * dz);
8151
+ return Math.abs(dist - rope.config.segmentLength) / rope.config.segmentLength;
8152
+ }
8153
+ getRopeCount() {
8154
+ return this.ropes.size;
8155
+ }
8156
+ removeRope(id) {
8157
+ this.ropes.delete(id);
8158
+ }
8159
+ };
8160
+
8161
+ // src/physics/TriggerZone.ts
8162
+ var TriggerZoneSystem = class {
8163
+ zones = /* @__PURE__ */ new Map();
8164
+ callbacks = /* @__PURE__ */ new Map();
8165
+ // Per zone: set of entity IDs currently inside
8166
+ occupants = /* @__PURE__ */ new Map();
8167
+ // ---------------------------------------------------------------------------
8168
+ // Zone Management
8169
+ // ---------------------------------------------------------------------------
8170
+ addZone(config) {
8171
+ this.zones.set(config.id, config);
8172
+ this.occupants.set(config.id, /* @__PURE__ */ new Set());
8173
+ }
8174
+ removeZone(id) {
8175
+ this.zones.delete(id);
8176
+ this.occupants.delete(id);
8177
+ this.callbacks.delete(id);
8178
+ }
8179
+ enableZone(id, enabled) {
8180
+ const z = this.zones.get(id);
8181
+ if (z) z.enabled = enabled;
8182
+ }
8183
+ // ---------------------------------------------------------------------------
8184
+ // Callbacks
8185
+ // ---------------------------------------------------------------------------
8186
+ onTrigger(zoneId, callback) {
8187
+ if (!this.callbacks.has(zoneId)) this.callbacks.set(zoneId, []);
8188
+ this.callbacks.get(zoneId).push(callback);
8189
+ }
8190
+ fire(zoneId, entityId, event) {
8191
+ const cbs = this.callbacks.get(zoneId);
8192
+ if (cbs) for (const cb of cbs) cb(entityId, zoneId, event);
8193
+ }
8194
+ // ---------------------------------------------------------------------------
8195
+ // Update (test entities against zones)
8196
+ // ---------------------------------------------------------------------------
8197
+ update(entities) {
8198
+ for (const [zoneId, zone] of this.zones) {
8199
+ if (!zone.enabled) continue;
8200
+ const current = this.occupants.get(zoneId);
8201
+ const nowInside = /* @__PURE__ */ new Set();
8202
+ for (const entity of entities) {
8203
+ if (this.overlaps(zone.shape, entity.position, entity.radius ?? 0)) {
8204
+ nowInside.add(entity.id);
8205
+ if (current.has(entity.id)) {
8206
+ this.fire(zoneId, entity.id, "stay");
8207
+ } else {
8208
+ this.fire(zoneId, entity.id, "enter");
8209
+ }
8210
+ }
8211
+ }
8212
+ for (const prevId of current) {
8213
+ if (!nowInside.has(prevId)) {
8214
+ this.fire(zoneId, prevId, "exit");
8215
+ }
8216
+ }
8217
+ this.occupants.set(zoneId, nowInside);
8218
+ }
8219
+ }
8220
+ // ---------------------------------------------------------------------------
8221
+ // Overlap Tests
8222
+ // ---------------------------------------------------------------------------
8223
+ overlaps(shape, pos, entityRadius) {
8224
+ if (shape.type === "sphere" && shape.radius !== void 0) {
8225
+ const dx = pos[0] - shape.position[0], dy = pos[1] - shape.position[1], dz = pos[2] - shape.position[2];
8226
+ const dist = Math.sqrt(dx * dx + dy * dy + dz * dz);
8227
+ return dist <= shape.radius + entityRadius;
8228
+ }
8229
+ if (shape.type === "box" && shape.halfExtents) {
8230
+ const he = shape.halfExtents;
8231
+ const dx = Math.abs(pos[0] - shape.position[0]), dy = Math.abs(pos[1] - shape.position[1]), dz = Math.abs(pos[2] - shape.position[2]);
8232
+ return dx <= he.x + entityRadius && dy <= he.y + entityRadius && dz <= he.z + entityRadius;
8233
+ }
8234
+ return false;
8235
+ }
8236
+ // ---------------------------------------------------------------------------
8237
+ // Queries
8238
+ // ---------------------------------------------------------------------------
8239
+ isInside(entityId, zoneId) {
8240
+ return this.occupants.get(zoneId)?.has(entityId) ?? false;
8241
+ }
8242
+ getOccupants(zoneId) {
8243
+ const occ = this.occupants.get(zoneId);
8244
+ return occ ? [...occ] : [];
8245
+ }
8246
+ getZonesForEntity(entityId) {
8247
+ const zones = [];
8248
+ for (const [zoneId, occ] of this.occupants) {
8249
+ if (occ.has(entityId)) zones.push(zoneId);
8250
+ }
8251
+ return zones;
8252
+ }
8253
+ getZoneCount() {
8254
+ return this.zones.size;
8255
+ }
8256
+ };
8257
+
8258
+ // src/physics/VehicleSystem.ts
8259
+ function createDefaultCar(id) {
8260
+ const wheelConfig = (x, z, isSteering, isDriving) => ({
8261
+ id: `${id}_wheel_${x > 0 ? "r" : "l"}_${z > 0 ? "f" : "r"}`,
8262
+ connectionPoint: { x, y: -0.3, z },
8263
+ direction: { x: 0, y: -1, z: 0 },
8264
+ axle: { x: 1, y: 0, z: 0 },
8265
+ suspensionRestLength: 0.3,
8266
+ suspensionStiffness: 25,
8267
+ suspensionDamping: 4.4,
8268
+ maxSuspensionTravel: 0.2,
8269
+ wheelRadius: 0.35,
8270
+ frictionSlip: 1.2,
8271
+ isSteering,
8272
+ isDriving,
8273
+ rollInfluence: 0.1
8274
+ });
8275
+ return {
8276
+ id,
8277
+ chassisMass: 1500,
8278
+ chassisSize: { x: 1.8, y: 0.6, z: 4.2 },
8279
+ wheels: [
8280
+ wheelConfig(-0.8, 1.4, true, false),
8281
+ // Front Left
8282
+ wheelConfig(0.8, 1.4, true, false),
8283
+ // Front Right
8284
+ wheelConfig(-0.8, -1.4, false, true),
8285
+ // Rear Left
8286
+ wheelConfig(0.8, -1.4, false, true)
8287
+ // Rear Right
8288
+ ],
8289
+ maxSteerAngle: 0.5,
8290
+ maxEngineForce: 3e3,
8291
+ maxBrakeForce: 100
8292
+ };
8293
+ }
8294
+ function createTruck(id) {
8295
+ const wheelConfig = (x, z, isSteering, isDriving) => ({
8296
+ id: `${id}_wheel_${x > 0 ? "r" : "l"}_${z.toFixed(0)}`,
8297
+ connectionPoint: { x, y: -0.5, z },
8298
+ direction: { x: 0, y: -1, z: 0 },
8299
+ axle: { x: 1, y: 0, z: 0 },
8300
+ suspensionRestLength: 0.4,
8301
+ suspensionStiffness: 40,
8302
+ suspensionDamping: 6,
8303
+ maxSuspensionTravel: 0.3,
8304
+ wheelRadius: 0.5,
8305
+ frictionSlip: 1.5,
8306
+ isSteering,
8307
+ isDriving,
8308
+ rollInfluence: 0.05
8309
+ });
8310
+ return {
8311
+ id,
8312
+ chassisMass: 5e3,
8313
+ chassisSize: { x: 2.4, y: 1, z: 7 },
8314
+ wheels: [
8315
+ wheelConfig(-1, 2.8, true, false),
8316
+ wheelConfig(1, 2.8, true, false),
8317
+ wheelConfig(-1, -1.5, false, true),
8318
+ wheelConfig(1, -1.5, false, true),
8319
+ wheelConfig(-1, -2.8, false, true),
8320
+ wheelConfig(1, -2.8, false, true)
8321
+ ],
8322
+ maxSteerAngle: 0.4,
8323
+ maxEngineForce: 6e3,
8324
+ maxBrakeForce: 200
8325
+ };
8326
+ }
8327
+ var VehicleSystem = class {
8328
+ vehicles = /* @__PURE__ */ new Map();
8329
+ /**
8330
+ * Create a vehicle from a definition at a given position.
8331
+ */
8332
+ createVehicle(definition, position) {
8333
+ const wheels = definition.wheels.map((wc) => ({
8334
+ config: wc,
8335
+ suspensionLength: wc.suspensionRestLength,
8336
+ suspensionForce: 0,
8337
+ contactPoint: null,
8338
+ isGrounded: false,
8339
+ rotation: 0,
8340
+ steerAngle: 0,
8341
+ skidFactor: 0
8342
+ }));
8343
+ const state = {
8344
+ id: definition.id,
8345
+ definition,
8346
+ position: { ...position },
8347
+ rotation: { x: 0, y: 0, z: 0, w: 1 },
8348
+ linearVelocity: { x: 0, y: 0, z: 0 },
8349
+ angularVelocity: { x: 0, y: 0, z: 0 },
8350
+ wheels,
8351
+ speed: 0,
8352
+ engineForce: 0,
8353
+ brakeForce: 0,
8354
+ steerAngle: 0
8355
+ };
8356
+ this.vehicles.set(definition.id, state);
8357
+ return state;
8358
+ }
8359
+ /**
8360
+ * Update vehicle physics for one timestep.
8361
+ */
8362
+ update(vehicleId, dt) {
8363
+ const vehicle = this.vehicles.get(vehicleId);
8364
+ if (!vehicle) return null;
8365
+ const def = vehicle.definition;
8366
+ for (const wheel of vehicle.wheels) {
8367
+ if (wheel.config.isSteering) {
8368
+ wheel.steerAngle = vehicle.steerAngle;
8369
+ }
8370
+ }
8371
+ let totalSuspensionForce = 0;
8372
+ const forwardDir = this.getForwardVector(vehicle);
8373
+ for (const wheel of vehicle.wheels) {
8374
+ const wheelWorldY = vehicle.position.y + wheel.config.connectionPoint.y;
8375
+ const rayEnd = wheelWorldY - wheel.config.suspensionRestLength - wheel.config.wheelRadius;
8376
+ if (rayEnd <= 0) {
8377
+ wheel.isGrounded = true;
8378
+ const compression = -rayEnd;
8379
+ wheel.suspensionLength = Math.max(
8380
+ wheel.config.suspensionRestLength - compression,
8381
+ wheel.config.suspensionRestLength - wheel.config.maxSuspensionTravel
8382
+ );
8383
+ const springDelta = wheel.config.suspensionRestLength - wheel.suspensionLength;
8384
+ const springForce = springDelta * wheel.config.suspensionStiffness;
8385
+ const relVel = vehicle.linearVelocity.y;
8386
+ const dampForce = -relVel * wheel.config.suspensionDamping;
8387
+ wheel.suspensionForce = Math.max(0, springForce + dampForce);
8388
+ totalSuspensionForce += wheel.suspensionForce;
8389
+ wheel.contactPoint = {
8390
+ x: vehicle.position.x + wheel.config.connectionPoint.x,
8391
+ y: 0,
8392
+ z: vehicle.position.z + wheel.config.connectionPoint.z
8393
+ };
8394
+ } else {
8395
+ wheel.isGrounded = false;
8396
+ wheel.suspensionForce = 0;
8397
+ wheel.contactPoint = null;
8398
+ wheel.suspensionLength = wheel.config.suspensionRestLength;
8399
+ }
8400
+ }
8401
+ for (const wheel of vehicle.wheels) {
8402
+ if (!wheel.isGrounded) continue;
8403
+ if (wheel.config.isDriving && vehicle.engineForce !== 0) {
8404
+ const driveForce = vehicle.engineForce / vehicle.wheels.filter((w) => w.config.isDriving).length;
8405
+ vehicle.linearVelocity.x += forwardDir.x * driveForce / def.chassisMass * dt;
8406
+ vehicle.linearVelocity.z += forwardDir.z * driveForce / def.chassisMass * dt;
8407
+ }
8408
+ if (vehicle.brakeForce > 0) {
8409
+ const speed = Math.sqrt(vehicle.linearVelocity.x ** 2 + vehicle.linearVelocity.z ** 2);
8410
+ if (speed > 0.01) {
8411
+ const brakeDec = Math.min(vehicle.brakeForce / def.chassisMass * dt, speed);
8412
+ const factor = 1 - brakeDec / speed;
8413
+ vehicle.linearVelocity.x *= factor;
8414
+ vehicle.linearVelocity.z *= factor;
8415
+ }
8416
+ }
8417
+ const wheelSpeed = Math.sqrt(vehicle.linearVelocity.x ** 2 + vehicle.linearVelocity.z ** 2);
8418
+ wheel.rotation += wheelSpeed / wheel.config.wheelRadius * dt;
8419
+ const lateralSpeed = Math.abs(vehicle.angularVelocity.y * 0.5);
8420
+ wheel.skidFactor = Math.min(lateralSpeed / (wheel.config.frictionSlip + 1e-3), 1);
8421
+ }
8422
+ const isGrounded = vehicle.wheels.some((w) => w.isGrounded);
8423
+ if (!isGrounded) {
8424
+ vehicle.linearVelocity.y -= 9.81 * dt;
8425
+ } else {
8426
+ const suspensionAccel = totalSuspensionForce / def.chassisMass;
8427
+ vehicle.linearVelocity.y += (suspensionAccel - 9.81) * dt;
8428
+ const refWheel = vehicle.wheels[0].config;
8429
+ if (vehicle.position.y <= refWheel.wheelRadius + refWheel.suspensionRestLength) {
8430
+ vehicle.linearVelocity.y = Math.max(vehicle.linearVelocity.y, 0);
8431
+ }
8432
+ }
8433
+ if (vehicle.steerAngle !== 0 && isGrounded) {
8434
+ const speed = Math.sqrt(vehicle.linearVelocity.x ** 2 + vehicle.linearVelocity.z ** 2);
8435
+ const turnRate = vehicle.steerAngle * speed * 0.5;
8436
+ vehicle.angularVelocity.y = turnRate;
8437
+ } else {
8438
+ vehicle.angularVelocity.y *= 0.95;
8439
+ }
8440
+ vehicle.position.x += vehicle.linearVelocity.x * dt;
8441
+ vehicle.position.y += vehicle.linearVelocity.y * dt;
8442
+ vehicle.position.z += vehicle.linearVelocity.z * dt;
8443
+ vehicle.speed = Math.sqrt(vehicle.linearVelocity.x ** 2 + vehicle.linearVelocity.z ** 2) * 3.6;
8444
+ return vehicle;
8445
+ }
8446
+ // ---------------------------------------------------------------------------
8447
+ // Controls
8448
+ // ---------------------------------------------------------------------------
8449
+ setThrottle(vehicleId, throttle) {
8450
+ const v = this.vehicles.get(vehicleId);
8451
+ if (!v) return;
8452
+ v.engineForce = throttle * v.definition.maxEngineForce;
8453
+ }
8454
+ setBrake(vehicleId, brake) {
8455
+ const v = this.vehicles.get(vehicleId);
8456
+ if (!v) return;
8457
+ v.brakeForce = brake * v.definition.maxBrakeForce;
8458
+ }
8459
+ setSteering(vehicleId, steering) {
8460
+ const v = this.vehicles.get(vehicleId);
8461
+ if (!v) return;
8462
+ v.steerAngle = Math.max(-1, Math.min(1, steering)) * v.definition.maxSteerAngle;
8463
+ }
8464
+ getVehicle(vehicleId) {
8465
+ return this.vehicles.get(vehicleId);
8466
+ }
8467
+ removeVehicle(vehicleId) {
8468
+ return this.vehicles.delete(vehicleId);
8469
+ }
8470
+ // ---------------------------------------------------------------------------
8471
+ // Helpers
8472
+ // ---------------------------------------------------------------------------
8473
+ getForwardVector(vehicle) {
8474
+ const yaw = vehicle.angularVelocity.y;
8475
+ return { x: Math.sin(yaw), y: 0, z: Math.cos(yaw) };
8476
+ }
8477
+ };
8478
+
8479
+ // src/physics/SpatialHash.ts
8480
+ var SpatialHash = class {
8481
+ cellSize;
8482
+ cells = /* @__PURE__ */ new Map();
8483
+ entries = /* @__PURE__ */ new Map();
8484
+ constructor(cellSize) {
8485
+ this.cellSize = cellSize;
8486
+ }
8487
+ // ---------------------------------------------------------------------------
8488
+ // Insert / Remove
8489
+ // ---------------------------------------------------------------------------
8490
+ insert(entry) {
8491
+ this.entries.set(entry.id, entry);
8492
+ const cells = this.getCellsForEntry(entry);
8493
+ for (const key of cells) {
8494
+ if (!this.cells.has(key)) this.cells.set(key, /* @__PURE__ */ new Set());
8495
+ this.cells.get(key).add(entry.id);
8496
+ }
8497
+ }
8498
+ remove(id) {
8499
+ const entry = this.entries.get(id);
8500
+ if (!entry) return;
8501
+ const cells = this.getCellsForEntry(entry);
8502
+ for (const key of cells) {
8503
+ const cell = this.cells.get(key);
8504
+ if (cell) {
8505
+ cell.delete(id);
8506
+ if (cell.size === 0) this.cells.delete(key);
8507
+ }
8508
+ }
8509
+ this.entries.delete(id);
8510
+ }
8511
+ update(id, x, y, z) {
8512
+ this.remove(id);
8513
+ const entry = this.entries.get(id) ?? { id, x, y, z, radius: 0 };
8514
+ entry.x = x;
8515
+ entry.y = y;
8516
+ entry.z = z;
8517
+ this.insert(entry);
8518
+ }
8519
+ // ---------------------------------------------------------------------------
8520
+ // Queries
8521
+ // ---------------------------------------------------------------------------
8522
+ queryPoint(x, y, z) {
8523
+ const key = this.cellKey(
8524
+ Math.floor(x / this.cellSize),
8525
+ Math.floor(y / this.cellSize),
8526
+ Math.floor(z / this.cellSize)
8527
+ );
8528
+ const cell = this.cells.get(key);
8529
+ return cell ? [...cell] : [];
8530
+ }
8531
+ queryRadius(x, y, z, radius) {
8532
+ const results = /* @__PURE__ */ new Set();
8533
+ const minCx = Math.floor((x - radius) / this.cellSize);
8534
+ const maxCx = Math.floor((x + radius) / this.cellSize);
8535
+ const minCy = Math.floor((y - radius) / this.cellSize);
8536
+ const maxCy = Math.floor((y + radius) / this.cellSize);
8537
+ const minCz = Math.floor((z - radius) / this.cellSize);
8538
+ const maxCz = Math.floor((z + radius) / this.cellSize);
8539
+ for (let cx = minCx; cx <= maxCx; cx++) {
8540
+ for (let cy = minCy; cy <= maxCy; cy++) {
8541
+ for (let cz = minCz; cz <= maxCz; cz++) {
8542
+ const cell = this.cells.get(this.cellKey(cx, cy, cz));
8543
+ if (!cell) continue;
8544
+ for (const id of cell) {
8545
+ const entry = this.entries.get(id);
8546
+ if (entry) {
8547
+ const dx = entry.x - x, dy = entry.y - y, dz = entry.z - z;
8548
+ const dist = Math.sqrt(dx * dx + dy * dy + dz * dz);
8549
+ if (dist <= radius + entry.radius) results.add(id);
8550
+ }
8551
+ }
8552
+ }
8553
+ }
8554
+ }
8555
+ return [...results];
8556
+ }
8557
+ getNearbyPairs() {
8558
+ const pairs = [];
8559
+ const seen = /* @__PURE__ */ new Set();
8560
+ for (const cell of this.cells.values()) {
8561
+ const ids = [...cell];
8562
+ for (let i = 0; i < ids.length; i++) {
8563
+ for (let j = i + 1; j < ids.length; j++) {
8564
+ const key = ids[i] < ids[j] ? `${ids[i]}:${ids[j]}` : `${ids[j]}:${ids[i]}`;
8565
+ if (!seen.has(key)) {
8566
+ seen.add(key);
8567
+ pairs.push([ids[i], ids[j]]);
8568
+ }
8569
+ }
8570
+ }
8571
+ }
8572
+ return pairs;
8573
+ }
8574
+ // ---------------------------------------------------------------------------
8575
+ // Internals
8576
+ // ---------------------------------------------------------------------------
8577
+ getCellsForEntry(entry) {
8578
+ const keys = [];
8579
+ const minCx = Math.floor((entry.x - entry.radius) / this.cellSize);
8580
+ const maxCx = Math.floor((entry.x + entry.radius) / this.cellSize);
8581
+ const minCy = Math.floor((entry.y - entry.radius) / this.cellSize);
8582
+ const maxCy = Math.floor((entry.y + entry.radius) / this.cellSize);
8583
+ const minCz = Math.floor((entry.z - entry.radius) / this.cellSize);
8584
+ const maxCz = Math.floor((entry.z + entry.radius) / this.cellSize);
8585
+ for (let cx = minCx; cx <= maxCx; cx++) {
8586
+ for (let cy = minCy; cy <= maxCy; cy++) {
8587
+ for (let cz = minCz; cz <= maxCz; cz++) {
8588
+ keys.push(this.cellKey(cx, cy, cz));
8589
+ }
8590
+ }
8591
+ }
8592
+ return keys;
8593
+ }
8594
+ cellKey(cx, cy, cz) {
8595
+ return `${cx}:${cy}:${cz}`;
8596
+ }
8597
+ // ---------------------------------------------------------------------------
8598
+ // Stats
8599
+ // ---------------------------------------------------------------------------
8600
+ getEntryCount() {
8601
+ return this.entries.size;
8602
+ }
8603
+ getCellCount() {
8604
+ return this.cells.size;
8605
+ }
8606
+ clear() {
8607
+ this.cells.clear();
8608
+ this.entries.clear();
8609
+ }
8610
+ };
6272
8611
  // Annotate the CommonJS export names for ESM import in node:
6273
8612
  0 && (module.exports = {
6274
8613
  ActivationTriggerType,
6275
8614
  COLLISION_GROUPS,
8615
+ ClothSim,
8616
+ ConstraintSolver,
6276
8617
  DEFAULT_ACTIVATION_CONFIG,
6277
8618
  DEFAULT_INTENSITY_CURVE,
6278
8619
  DEFAULT_LOCOMOTION_CONFIG,
8620
+ DeformableMesh,
8621
+ FluidSim,
8622
+ HUMANOID_PRESET,
6279
8623
  IslandDetector,
8624
+ JointSystem,
6280
8625
  MLSMPMFluid,
6281
8626
  PBDSolverCPU,
6282
8627
  PBD_ATTACHMENT_SHADER,
@@ -6299,14 +8644,23 @@ var SoftBodyAdapter = class {
6299
8644
  PhysicsSyncReceiver,
6300
8645
  PhysicsSyncSender,
6301
8646
  PhysicsWorldImpl,
8647
+ QUADRUPED_PRESET,
8648
+ RagdollController,
8649
+ RagdollSystem,
8650
+ RaycastSystem,
6302
8651
  RigidBody,
8652
+ RopeSystem,
6303
8653
  SOFT_BODY_PRESETS,
6304
8654
  ScalarArithmetic,
6305
8655
  SoftBodyAdapter,
6306
8656
  SoftBodyGrabController,
6307
8657
  SoftBodySolver,
8658
+ SpatialHash,
8659
+ TriggerZoneSystem,
6308
8660
  UnifiedParticleBuffer,
8661
+ VRPhysicsBridge,
6309
8662
  Vector3Arithmetic,
8663
+ VehicleSystem,
6310
8664
  VelocityRingBuffer,
6311
8665
  VelocitySmoother,
6312
8666
  WindZoneManager,
@@ -6316,10 +8670,12 @@ var SoftBodyAdapter = class {
6316
8670
  colorConstraints,
6317
8671
  computeRestLengths,
6318
8672
  computeSelfWind,
8673
+ createDefaultCar,
6319
8674
  createPBDSolver,
6320
8675
  createPIDControllerTrait,
6321
8676
  createPhysicsWorld,
6322
8677
  createScalarPIDController,
8678
+ createTruck,
6323
8679
  createVector3PIDController,
6324
8680
  defaultMaterial,
6325
8681
  defaultPIDConfig,