@woosh/meep-engine 2.140.0 → 2.142.0

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 (74) hide show
  1. package/package.json +1 -1
  2. package/src/core/geom/3d/quaternion/quat3_multiply.d.ts +21 -0
  3. package/src/core/geom/3d/quaternion/quat3_multiply.d.ts.map +1 -0
  4. package/src/core/geom/3d/quaternion/quat3_multiply.js +25 -0
  5. package/src/engine/control/first-person/prototype_first_person_controller.js +5 -0
  6. package/src/engine/graphics/render/buffer/simple-fx/ao/AmbientOcclusionPostProcessEffect.d.ts.map +1 -1
  7. package/src/engine/graphics/render/buffer/simple-fx/ao/AmbientOcclusionPostProcessEffect.js +67 -42
  8. package/src/engine/graphics/render/buffer/simple-fx/ao/SAOShader.d.ts +12 -22
  9. package/src/engine/graphics/render/buffer/simple-fx/ao/SAOShader.d.ts.map +1 -1
  10. package/src/engine/graphics/render/buffer/simple-fx/ao/SAOShader.js +340 -186
  11. package/src/engine/graphics/render/buffer/simple-fx/ao/SAOUpscaleShader.d.ts +44 -0
  12. package/src/engine/graphics/render/buffer/simple-fx/ao/SAOUpscaleShader.d.ts.map +1 -0
  13. package/src/engine/graphics/render/buffer/simple-fx/ao/SAOUpscaleShader.js +151 -0
  14. package/src/engine/graphics/render/buffer/simple-fx/ao/generateHilbertNoiseTexture.d.ts +14 -0
  15. package/src/engine/graphics/render/buffer/simple-fx/ao/generateHilbertNoiseTexture.d.ts.map +1 -0
  16. package/src/engine/graphics/render/buffer/simple-fx/ao/generateHilbertNoiseTexture.js +78 -0
  17. package/src/engine/physics/PLAN.md +705 -461
  18. package/src/engine/physics/REVIEW_002.md +151 -0
  19. package/src/engine/physics/REVIEW_003.md +166 -0
  20. package/src/engine/physics/constraint/DofMode.d.ts +28 -0
  21. package/src/engine/physics/constraint/DofMode.d.ts.map +1 -0
  22. package/src/engine/physics/constraint/DofMode.js +35 -0
  23. package/src/engine/physics/constraint/solve_constraints.d.ts +38 -0
  24. package/src/engine/physics/constraint/solve_constraints.d.ts.map +1 -0
  25. package/src/engine/physics/constraint/solve_constraints.js +673 -0
  26. package/src/engine/physics/ecs/Joint.d.ts +294 -0
  27. package/src/engine/physics/ecs/Joint.d.ts.map +1 -0
  28. package/src/engine/physics/ecs/Joint.js +402 -0
  29. package/src/engine/physics/ecs/PhysicsSystem.d.ts +52 -0
  30. package/src/engine/physics/ecs/PhysicsSystem.d.ts.map +1 -1
  31. package/src/engine/physics/ecs/PhysicsSystem.js +126 -4
  32. package/src/engine/physics/fluid/FluidField.d.ts +14 -10
  33. package/src/engine/physics/fluid/FluidField.d.ts.map +1 -1
  34. package/src/engine/physics/fluid/FluidField.js +14 -10
  35. package/src/engine/physics/fluid/FluidSimulator.d.ts.map +1 -1
  36. package/src/engine/physics/fluid/FluidSimulator.js +0 -1
  37. package/src/engine/physics/fluid/solver/v3_grid_compute_solid_neighbour_mask.d.ts +17 -10
  38. package/src/engine/physics/fluid/solver/v3_grid_compute_solid_neighbour_mask.d.ts.map +1 -1
  39. package/src/engine/physics/fluid/solver/v3_grid_compute_solid_neighbour_mask.js +18 -11
  40. package/src/engine/physics/fluid/solver/v3_grid_solve_pressure.d.ts +13 -10
  41. package/src/engine/physics/fluid/solver/v3_grid_solve_pressure.d.ts.map +1 -1
  42. package/src/engine/physics/fluid/solver/v3_grid_solve_pressure.js +18 -13
  43. package/src/engine/physics/fluid/solver/v3_grid_solve_pressure_pcg.d.ts +4 -3
  44. package/src/engine/physics/fluid/solver/v3_grid_solve_pressure_pcg.d.ts.map +1 -1
  45. package/src/engine/physics/fluid/solver/v3_grid_solve_pressure_pcg.js +15 -11
  46. package/src/engine/physics/fluid/solver/v3_grid_subtract_pressure_gradient.d.ts +24 -22
  47. package/src/engine/physics/fluid/solver/v3_grid_subtract_pressure_gradient.d.ts.map +1 -1
  48. package/src/engine/physics/fluid/solver/v3_grid_subtract_pressure_gradient.js +26 -22
  49. package/src/engine/physics/island/IslandBuilder.d.ts +4 -1
  50. package/src/engine/physics/island/IslandBuilder.d.ts.map +1 -1
  51. package/src/engine/physics/island/IslandBuilder.js +33 -16
  52. package/src/engine/physics/narrowphase/box_box_manifold.d.ts.map +1 -1
  53. package/src/engine/physics/narrowphase/box_box_manifold.js +27 -1
  54. package/src/engine/physics/narrowphase/narrowphase_step.d.ts +33 -0
  55. package/src/engine/physics/narrowphase/narrowphase_step.d.ts.map +1 -1
  56. package/src/engine/physics/narrowphase/narrowphase_step.js +75 -0
  57. package/src/engine/physics/narrowphase/ray_shapes.d.ts +66 -0
  58. package/src/engine/physics/narrowphase/ray_shapes.d.ts.map +1 -0
  59. package/src/engine/physics/narrowphase/ray_shapes.js +187 -0
  60. package/src/engine/physics/narrowphase/refine_ray_concave.d.ts +16 -0
  61. package/src/engine/physics/narrowphase/refine_ray_concave.d.ts.map +1 -0
  62. package/src/engine/physics/narrowphase/refine_ray_concave.js +145 -0
  63. package/src/engine/physics/narrowphase/refine_ray_hit.d.ts +39 -0
  64. package/src/engine/physics/narrowphase/refine_ray_hit.d.ts.map +1 -0
  65. package/src/engine/physics/narrowphase/refine_ray_hit.js +78 -0
  66. package/src/engine/physics/queries/raycast.d.ts +11 -9
  67. package/src/engine/physics/queries/raycast.d.ts.map +1 -1
  68. package/src/engine/physics/queries/raycast.js +108 -159
  69. package/src/engine/physics/solver/solve_contacts.d.ts +28 -0
  70. package/src/engine/physics/solver/solve_contacts.d.ts.map +1 -1
  71. package/src/engine/physics/solver/solve_contacts.js +169 -1
  72. package/src/engine/physics/vehicle/RaycastVehicle.d.ts +114 -0
  73. package/src/engine/physics/vehicle/RaycastVehicle.d.ts.map +1 -0
  74. package/src/engine/physics/vehicle/RaycastVehicle.js +333 -0
@@ -157,6 +157,27 @@ export class PhysicsSystem extends System<any, any, any, any, any> {
157
157
  entity: number;
158
158
  bvhNode: number;
159
159
  }>>;
160
+ /**
161
+ * Live {@link Joint} (6-DOF constraint) instances, in a sparse array
162
+ * indexed by joint id. Solved alongside contacts inside the TGS
163
+ * substep loop. Holes (unlinked joints) are `undefined`.
164
+ * @type {Joint[]}
165
+ */
166
+ __joints: Joint[];
167
+ /**
168
+ * Lowest free index in {@link __joints} for slot reuse on link.
169
+ * @private
170
+ * @type {number}
171
+ */
172
+ private __joint_free;
173
+ /**
174
+ * Velocity iterations per substep for the joint solver. Joints (and
175
+ * especially joint chains) want a few more iterations than contacts to
176
+ * propagate impulses along the chain; cheap because joints are far
177
+ * fewer than contacts.
178
+ * @type {number}
179
+ */
180
+ jointIterations: number;
160
181
  /**
161
182
  * Per-body pseudo-velocity for the Catto split-impulse position
162
183
  * pass (TGS Phase 1). Flat layout, 6 doubles per body slot index:
@@ -305,6 +326,23 @@ export class PhysicsSystem extends System<any, any, any, any, any> {
305
326
  * @returns {number} body index or -1
306
327
  */
307
328
  private __find_body_index_by_entity;
329
+ /**
330
+ * Register a {@link Joint} (6-DOF constraint). Resolves the joint's
331
+ * entities to packed body ids and adds it to the active set, where it is
332
+ * solved alongside contacts in the TGS substep loop. Body A must be a
333
+ * linked body; body B is either a linked body or {@link JOINT_WORLD}
334
+ * (anchoring A to a fixed world point — `localAnchorB` is then a world
335
+ * coordinate).
336
+ *
337
+ * @param {Joint} joint
338
+ */
339
+ link_joint(joint: Joint): void;
340
+ /**
341
+ * Remove a previously {@link link_joint}'d joint from the active set.
342
+ * Idempotent.
343
+ * @param {Joint} joint
344
+ */
345
+ unlink_joint(joint: Joint): void;
308
346
  /**
309
347
  * Resolve a packed body id to its entity, or `-1` if the id is stale.
310
348
  * @param {number} packed_body_id
@@ -567,6 +605,20 @@ export class PhysicsSystem extends System<any, any, any, any, any> {
567
605
  * @private
568
606
  */
569
607
  private __wake_pairs;
608
+ /**
609
+ * Wake propagation across joints: if a joint connects an awake body to a
610
+ * sleeping one, wake the sleeper so the constraint stays coupled (a joint
611
+ * with one body asleep and one awake would otherwise be skipped by the
612
+ * solver, letting the awake side drift). A common trigger is a
613
+ * kinematic/motor-driven body pulling on a sleeping chain.
614
+ *
615
+ * Bodies that slept together as one island share a sleep group, so the
616
+ * usual atomic wake already revives the whole chain when any member is
617
+ * hit; this catches the cases that atomic wake doesn't (joint spanning
618
+ * separate groups, kinematic driver).
619
+ * @private
620
+ */
621
+ private __wake_joints;
570
622
  /**
571
623
  * Per-island atomic sleep test. Walks each island once and applies the
572
624
  * decision uniformly across all members:
@@ -1 +1 @@
1
- {"version":3,"file":"PhysicsSystem.d.ts","sourceRoot":"","sources":["../../../../../src/engine/physics/ecs/PhysicsSystem.js"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"PhysicsSystem.d.ts","sourceRoot":"","sources":["../../../../../src/engine/physics/ecs/PhysicsSystem.js"],"names":[],"mappings":"AA+DA;;;;;;;;;;;;;;;;GAgBG;AACH;IAEI,cA6NC;IA1NG,sDAA0C;IAE1C,kKAIC;IAED;;OAEG;IACH,SAFU,WAAW,CAEW;IAEhC;;OAEG;IACH,WAFU,GAAG,CAEa;IAE1B;;OAEG;IACH,YAFU,GAAG,CAEc;IAE3B;;;OAGG;IACH,WAFU,aAAa,CAEa;IAEpC;;;;OAIG;IACH,OAFU,QAAQ,CAES;IAE3B;;;;OAIG;IACH,eAFU,kBAAkB,CAEiB;IAE7C;;;;;;;OAOG;IACH,SAFU,aAAa,CAEW;IAElC;;;;;;OAMG;IACH,2BAFU,MAAM,CAEqB;IAErC;;;;OAIG;IACH,oBAFU,MAAM,CAEa;IAE7B;;;;;;;;;;;OAWG;IACH,UAFU,MAAM,CAEC;IAEjB;;;;;OAKG;IACH,oBAFU,MAAM,CAEW;IAE3B;;;OAGG;IACH,oBAFU,MAAM,CAEW;IAE3B;;;;OAIG;IACH,0BAOC;IAED;;;;OAIG;IACH,kBAFU,OAAO,CAEsB;IAEvC;;;;OAIG;IACH,yBAFU,MAAM,CAEkB;IAElC;;;;OAIG;IACH,wBAFU,MAAM,CAEiB;IAEjC;;;;OAIG;IACH,uBAFU,MAAM,CAEgB;IAEhC;;;;;OAKG;IACH,yBAA4B;IAE5B;;;;;OAKG;IACH,UAFU,SAAS,EAAE,CAEH;IAClB,0BAA0B;IAC1B,cADW,SAAS,EAAE,CACA;IAEtB;;;;;;;;OAQG;IACH,uBAFU,MAAM,MAAM;QAAC,QAAQ,EAAE,QAAQ,CAAC;QAAC,SAAS,EAAE,SAAS,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAC,CAAC,CAAC,CAEpE;IAE/B;;;;;OAKG;IACH,UAFU,OAAO,CAEC;IAElB;;;;OAIG;IACH,qBAAsB;IAEtB;;;;;;OAMG;IACH,iBAFU,MAAM,CAEQ;IAExB;;;;;;;;;;;;;;;;OAgBG;IACH,mBAFU,YAAY,CAEsB;IAE5C;;;;;OAKG;IACH,4BAAqE;IAGzE;;;;;;;;;;;;;OAaG;IACH,sBAqBC;IAED;;;;;;;;;OASG;IACH,2BAGC;IAED;;;;;;;;;;OAUG;IACH,gCAOC;IAED;;;OAGG;IACH,cAFW,OAAO,GAAC;QAAC,CAAC,EAAC,MAAM,CAAC;QAAA,CAAC,EAAC,MAAM,CAAC;QAAA,CAAC,EAAC,MAAM,CAAA;KAAC,QAI9C;IAED;;;OAGG;IACH,+BAFqB,MAAM,WAAU,MAAM,aAAY,QAAQ,aAAY,QAAQ,KAAK,OAAO,QAI9F;IAED;;OAEG;IACH,8BAFuB,MAAM,WAAU,MAAM,aAAY,QAAQ,aAAY,QAAQ,KAAK,OAAO,CAIhG;IAED;;;;;;OAMG;IACH,iCA8BC;IAED;;;;OAIG;IACH,iCAIC;IAED;;;;;;;;;;OAUG;IACH,gBAJW,SAAS,aACT,SAAS,UACT,MAAM,QAmBhB;IAED;;;;;;;OAOG;IACH,kBAJW,SAAS,aACT,SAAS,UACT,MAAM,QAkChB;IAED;;;;;;;;;;;;;OAaG;IACH,6BALW,MAAM,YACN,QAAQ,aACR,SAAS,oBACT,MAAM,QAmBhB;IAED;;;;;OAKG;IACH,6BAHW,MAAM,YACN,QAAQ,QAqBlB;IAED;;;;;;;;OAQG;IACH,oCAMC;IAED;;;;;;;;;OASG;IACH,+BAsBC;IAED;;;;OAIG;IACH,iCAMC;IAED;;;;OAIG;IACH,yBAHW,MAAM,GACJ,MAAM,CAKlB;IAED;;;OAGG;IACH,wBAEC;IAED;;;;;;;;;OASG;IACH,wBAHW,SAAS,WACT,OAAO,GAAC;QAAC,CAAC,EAAC,MAAM,CAAC;QAAA,CAAC,EAAC,MAAM,CAAC;QAAA,CAAC,EAAC,MAAM,CAAA;KAAC,QAU9C;IAED;;;;;;;;;;;OAWG;IACH,0BALW,SAAS,aACT,SAAS,WACT,OAAO,GAAC;QAAC,CAAC,EAAC,MAAM,CAAC;QAAA,CAAC,EAAC,MAAM,CAAC;QAAA,CAAC,EAAC,MAAM,CAAA;KAAC,cACpC,OAAO,GAAC;QAAC,CAAC,EAAC,MAAM,CAAC;QAAA,CAAC,EAAC,MAAM,CAAC;QAAA,CAAC,EAAC,MAAM,CAAA;KAAC,QAgC9C;IAED;;;;;;;;OAQG;IACH,uBAHW,SAAS,UACT,OAAO,GAAC;QAAC,CAAC,EAAC,MAAM,CAAC;QAAA,CAAC,EAAC,MAAM,CAAC;QAAA,CAAC,EAAC,MAAM,CAAA;KAAC,QAY9C;IAED;;;;;;;;;;;OAWG;IACH,wBALW,SAAS,aACT,SAAS,SACT,OAAO,GAAC;QAAC,CAAC,EAAC,MAAM,CAAC;QAAA,CAAC,EAAC,MAAM,CAAC;QAAA,CAAC,EAAC,MAAM,CAAA;KAAC,cACpC,OAAO,GAAC;QAAC,CAAC,EAAC,MAAM,CAAC;QAAA,CAAC,EAAC,MAAM,CAAC;QAAA,CAAC,EAAC,MAAM,CAAA;KAAC,QAuB9C;IAED;;;;;;OAMG;IACH,sBAHW,SAAS,SACT,OAAO,GAAC;QAAC,CAAC,EAAC,MAAM,CAAC;QAAA,CAAC,EAAC,MAAM,CAAC;QAAA,CAAC,EAAC,MAAM,CAAA;KAAC,QAQ9C;IAED;;;;;OAKG;IACH,6BAHW,SAAS,KACT,OAAO,GAAC;QAAC,CAAC,EAAC,MAAM,CAAC;QAAA,CAAC,EAAC,MAAM,CAAC;QAAA,CAAC,EAAC,MAAM,CAAA;KAAC,QAO9C;IAED;;;OAGG;IACH,gBAFW,SAAS,QAInB;IAED;;;;OAIG;IACH,iBAFW,SAAS,QAYnB;IAED;;;;;;;;;;;;;;OAcG;IACH,oBAgCC;IAED;;;;;;;;;;;;;;OAcG;IACH,oCAgCC;IAED;;;;;OAKG;IACH,2BAHW,MAAM,GACJ,MAAM,CAIlB;IAED;;;;;;;;;;;;;;OAcG;IACH,kEAJmB,MAAM,YAAW,QAAQ,KAAG,OAAO,GAEzC,OAAO,CAInB;IAED;;;;;;;;;;;;;;;;;;;OAmBG;IACH,uDALW;QAAC,CAAC,EAAC,MAAM,CAAC;QAAA,CAAC,EAAC,MAAM,CAAC;QAAA,CAAC,EAAC,MAAM,CAAC;QAAA,CAAC,EAAC,MAAM,CAAA;KAAC,iDAE7B,MAAM,YAAW,QAAQ,KAAG,OAAO,GACzC,OAAO,CAInB;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAuCG;IACH,0CAVW;QAAC,CAAC,EAAC,MAAM,CAAC;QAAA,CAAC,EAAC,MAAM,CAAC;QAAA,CAAC,EAAC,MAAM,CAAA;KAAC,YAE5B;QAAC,CAAC,EAAC,MAAM,CAAC;QAAA,CAAC,EAAC,MAAM,CAAC;QAAA,CAAC,EAAC,MAAM,CAAC;QAAA,CAAC,EAAC,MAAM,CAAA;KAAC,UAErC,WAAW,GAAC,MAAM,EAAE,iBACpB,MAAM,oBACE,MAAM,YAAW,QAAQ,KAAG,OAAO,GAEzC,MAAM,CAIlB;IAED;;;;;;OAMG;IACH;;;;;;OAMG;IACH,qBAkBC;IAED;;;;;;;;;;;;OAYG;IACH,sBAmBC;IAED;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACH,qBAgEC;IAED;;;;;;;;;OASG;IACH,kCAuDC;IAED,2BA4JC;IAGL;;;OAGG;IACH,0BAFU,OAAO,CAEsB;CANtC;uBA/3CsB,qBAAqB;0BAClB,kCAAkC;0BAsCV,gBAAgB;4CAxCtB,oDAAoD;yBAuCrD,eAAe;4BApCf,wBAAwB;oBAP/C,gCAAgC;8BAYN,6BAA6B;yBADlD,2BAA2B;mCAEC,iCAAiC;8BAIxD,4BAA4B;oBAftC,+BAA+B;mBADhC,uCAAuC;yBAyCjC,eAAe;+BAGT,qBAAqB"}
@@ -25,11 +25,14 @@ import { returnTrue } from "../../../core/function/returnTrue.js";
25
25
  import {
26
26
  prepare_contacts,
27
27
  refresh_contacts,
28
+ redetect_concave_contacts,
28
29
  warm_start_contacts,
29
30
  solve_velocity,
30
31
  apply_restitution,
31
32
  solve_position,
32
33
  } from "../solver/solve_contacts.js";
34
+ import { solve_joints } from "../constraint/solve_constraints.js";
35
+ import { JOINT_WORLD, JOINT_UNALLOCATED } from "./Joint.js";
33
36
  import { world_inverse_inertia_apply } from "../inertia/world_inverse_inertia.js";
34
37
  import { PhysicsEvents } from "./PhysicsEvents.js";
35
38
 
@@ -248,6 +251,30 @@ export class PhysicsSystem extends System {
248
251
  */
249
252
  this.__body_collider_lists = [];
250
253
 
254
+ /**
255
+ * Live {@link Joint} (6-DOF constraint) instances, in a sparse array
256
+ * indexed by joint id. Solved alongside contacts inside the TGS
257
+ * substep loop. Holes (unlinked joints) are `undefined`.
258
+ * @type {Joint[]}
259
+ */
260
+ this.__joints = [];
261
+
262
+ /**
263
+ * Lowest free index in {@link __joints} for slot reuse on link.
264
+ * @private
265
+ * @type {number}
266
+ */
267
+ this.__joint_free = [];
268
+
269
+ /**
270
+ * Velocity iterations per substep for the joint solver. Joints (and
271
+ * especially joint chains) want a few more iterations than contacts to
272
+ * propagate impulses along the chain; cheap because joints are far
273
+ * fewer than contacts.
274
+ * @type {number}
275
+ */
276
+ this.jointIterations = 8;
277
+
251
278
  /**
252
279
  * Per-body pseudo-velocity for the Catto split-impulse position
253
280
  * pass (TGS Phase 1). Flat layout, 6 doubles per body slot index:
@@ -570,6 +597,53 @@ export class PhysicsSystem extends System {
570
597
  return -1;
571
598
  }
572
599
 
600
+ /**
601
+ * Register a {@link Joint} (6-DOF constraint). Resolves the joint's
602
+ * entities to packed body ids and adds it to the active set, where it is
603
+ * solved alongside contacts in the TGS substep loop. Body A must be a
604
+ * linked body; body B is either a linked body or {@link JOINT_WORLD}
605
+ * (anchoring A to a fixed world point — `localAnchorB` is then a world
606
+ * coordinate).
607
+ *
608
+ * @param {Joint} joint
609
+ */
610
+ link_joint(joint) {
611
+ const idxA = this.__find_body_index_by_entity(joint.entityA);
612
+ assert.notEqual(idxA, -1, `link_joint: no body for entityA ${joint.entityA}`);
613
+ joint._bodyIdA = this.__bodies[idxA]._bodyId;
614
+
615
+ if (joint.entityB === JOINT_WORLD) {
616
+ joint._bodyIdB = JOINT_WORLD;
617
+ } else {
618
+ const idxB = this.__find_body_index_by_entity(joint.entityB);
619
+ assert.notEqual(idxB, -1, `link_joint: no body for entityB ${joint.entityB}`);
620
+ joint._bodyIdB = this.__bodies[idxB]._bodyId;
621
+ }
622
+
623
+ // Reuse a freed slot if available so joint ids stay dense-ish.
624
+ let id;
625
+ if (this.__joint_free.length > 0) {
626
+ id = this.__joint_free.pop();
627
+ } else {
628
+ id = this.__joints.length;
629
+ }
630
+ joint._jointId = id;
631
+ this.__joints[id] = joint;
632
+ }
633
+
634
+ /**
635
+ * Remove a previously {@link link_joint}'d joint from the active set.
636
+ * Idempotent.
637
+ * @param {Joint} joint
638
+ */
639
+ unlink_joint(joint) {
640
+ const id = joint._jointId;
641
+ if (id === JOINT_UNALLOCATED || this.__joints[id] !== joint) return;
642
+ this.__joints[id] = undefined;
643
+ this.__joint_free.push(id);
644
+ joint._jointId = JOINT_UNALLOCATED;
645
+ }
646
+
573
647
  /**
574
648
  * Resolve a packed body id to its entity, or `-1` if the id is stale.
575
649
  * @param {number} packed_body_id
@@ -990,6 +1064,40 @@ export class PhysicsSystem extends System {
990
1064
  }
991
1065
  }
992
1066
 
1067
+ /**
1068
+ * Wake propagation across joints: if a joint connects an awake body to a
1069
+ * sleeping one, wake the sleeper so the constraint stays coupled (a joint
1070
+ * with one body asleep and one awake would otherwise be skipped by the
1071
+ * solver, letting the awake side drift). A common trigger is a
1072
+ * kinematic/motor-driven body pulling on a sleeping chain.
1073
+ *
1074
+ * Bodies that slept together as one island share a sleep group, so the
1075
+ * usual atomic wake already revives the whole chain when any member is
1076
+ * hit; this catches the cases that atomic wake doesn't (joint spanning
1077
+ * separate groups, kinematic driver).
1078
+ * @private
1079
+ */
1080
+ __wake_joints() {
1081
+ const joints = this.__joints;
1082
+ const n = joints.length;
1083
+ if (n === 0) return;
1084
+ const bodies = this.__bodies;
1085
+ const storage = this.storage;
1086
+ for (let i = 0; i < n; i++) {
1087
+ const joint = joints[i];
1088
+ if (joint === undefined || joint === null) continue;
1089
+ if (joint._bodyIdB === JOINT_WORLD) continue;
1090
+ if (!storage.is_valid(joint._bodyIdA) || !storage.is_valid(joint._bodyIdB)) continue;
1091
+ const a = bodies[body_id_index(joint._bodyIdA)];
1092
+ const b = bodies[body_id_index(joint._bodyIdB)];
1093
+ if (a === undefined || b === undefined) continue;
1094
+ const aSleep = a.sleepState === SleepState.Sleeping;
1095
+ const bSleep = b.sleepState === SleepState.Sleeping;
1096
+ if (aSleep === bSleep) continue; // both awake or both asleep — leave as is
1097
+ if (aSleep) this.__wake_body(a); else this.__wake_body(b);
1098
+ }
1099
+ }
1100
+
993
1101
  /**
994
1102
  * Per-island atomic sleep test. Walks each island once and applies the
995
1103
  * decision uniformly across all members:
@@ -1203,8 +1311,10 @@ export class PhysicsSystem extends System {
1203
1311
  this.__pair_filter_bound,
1204
1312
  );
1205
1313
 
1206
- // Stage 4: wake propagation.
1314
+ // Stage 4: wake propagation — through broadphase pairs, then through
1315
+ // joints (a joint must not have one body awake and one asleep).
1207
1316
  this.__wake_pairs();
1317
+ this.__wake_joints();
1208
1318
 
1209
1319
  // Stage 5: narrowphase — once per outer step. The substep loop below
1210
1320
  // re-derives each contact's penetration analytically from the moved
@@ -1213,7 +1323,7 @@ export class PhysicsSystem extends System {
1213
1323
 
1214
1324
  // Stage 6: partition awake bodies + touched contacts into islands.
1215
1325
  // Consumed by the solver (flattened contact list) and the sleep test.
1216
- this.islands.build(this.storage, this.manifolds, this.__bodies, this.__body_collider_lists);
1326
+ this.islands.build(this.storage, this.manifolds, this.__bodies, this.__body_collider_lists, this.__joints);
1217
1327
 
1218
1328
  // Stage 7: TGS substep loop.
1219
1329
  //
@@ -1249,12 +1359,24 @@ export class PhysicsSystem extends System {
1249
1359
  integrate_velocity_gravity(this.__bodies[idx], this.__transforms[idx], gx, gy, gz, h);
1250
1360
  }
1251
1361
 
1252
- // Re-derive contact geometry at the current poses, replay the
1253
- // per-substep warm-start, then solve velocity.
1362
+ // Re-derive contact geometry at the current poses: concave pairs
1363
+ // re-run narrowphase (fresh feature/normal as the body rocks),
1364
+ // convex pairs rotate frozen anchors analytically. Then replay
1365
+ // the per-substep warm-start and solve velocity.
1366
+ redetect_concave_contacts(this.manifolds, this);
1254
1367
  refresh_contacts(this.manifolds, this);
1255
1368
  warm_start_contacts(this.manifolds, this);
1256
1369
  solve_velocity(this.manifolds, this, this.velocityIterations);
1257
1370
 
1371
+ // Joints share the substep: warm-start + velocity-solve the 6-DOF
1372
+ // constraints on real velocity, coupled with the contacts above
1373
+ // (a body touched by both sees one substep of Gauss-Seidel across
1374
+ // contacts then joints). Position correction for locked DOFs is a
1375
+ // SPOOK bias inside this solve, so no separate joint position pass.
1376
+ if (this.__joints.length > 0) {
1377
+ solve_joints(this.__joints, this, h, this.jointIterations);
1378
+ }
1379
+
1258
1380
  // Position correction writes pseudo-velocity (zeroed first so it
1259
1381
  // is a fresh per-substep correction), folded into the pose by the
1260
1382
  // position integrate and then discarded.
@@ -45,17 +45,21 @@ export class FluidField {
45
45
  * Per-cell pre-baked neighbourhood mask consumed by the pressure SOR loop.
46
46
  * One byte per cell encoding which of the 6 cardinal neighbours are fluid:
47
47
  *
48
- * bit 0 (= 1) : -x neighbour is fluid (in-bounds AND non-solid)
49
- * bit 1 (= 2) : +x neighbour is fluid
50
- * bit 2 (= 4) : -y neighbour is fluid
51
- * bit 3 (= 8) : +y neighbour is fluid
52
- * bit 4 (= 16) : -z neighbour is fluid
53
- * bit 5 (= 32) : +z neighbour is fluid
48
+ * bit 0 (= 1) : -x neighbour is fluid (in-bounds AND non-solid)
49
+ * bit 1 (= 2) : +x neighbour is fluid
50
+ * bit 2 (= 4) : -y neighbour is fluid
51
+ * bit 3 (= 8) : +y neighbour is fluid
52
+ * bit 4 (= 16) : -z neighbour is fluid
53
+ * bit 5 (= 32) : +z neighbour is fluid
54
+ * bit 7 (= 128) : this cell is itself solid
54
55
  *
55
- * Solid cells themselves are encoded as `mask = 0` so the SOR loop's
56
- * `if (mask === 0) continue` skip handles both "this cell is solid" and
57
- * "this fluid cell is isolated" with a single comparison instead of one
58
- * solid-self-check plus six solid-neighbour-checks plus six boundary checks.
56
+ * A solid cell encodes as `128` (bit 7 set, neighbour bits clear) and an
57
+ * isolated fluid cell as `0`, so the SOR / PCG loops' `if ((mask & 63) === 0)
58
+ * continue` skip handles both "this cell is solid" and "this fluid cell is
59
+ * isolated" with a single comparison instead of one solid-self-check plus six
60
+ * solid-neighbour-checks plus six boundary checks. Bit 7 additionally lets
61
+ * {@link v3_grid_subtract_pressure_gradient} zero solid-cell velocity without
62
+ * consulting the {@link solid} array.
59
63
  *
60
64
  * Computed once by {@link recomputeSolidNeighbourMask} after every solid-mask
61
65
  * mutation. The simulator runs that recompute at the top of every project()
@@ -1 +1 @@
1
- {"version":3,"file":"FluidField.d.ts","sourceRoot":"","sources":["../../../../../src/engine/physics/fluid/FluidField.js"],"names":[],"mappings":"AASA;;;;;;;;;;;;;;;;;;;GAmBG;AACH;IAQI;;;;OAIG;IACH,YAFU,YAAY,GAAC,IAAI,CAET;IAElB;;OAEG;IACH,YAFU,YAAY,GAAC,IAAI,CAET;IAElB;;OAEG;IACH,YAFU,YAAY,GAAC,IAAI,CAET;IAElB;;;;;;OAMG;IACH,OAFU,UAAU,GAAC,IAAI,CAEZ;IAEb;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACH,sBAFU,UAAU,GAAC,IAAI,CAEG;IAE5B;;;;;;;;;;;;;OAaG;IACH,UAFU,YAAY,kBAAc,IAAI,CAExB;IAEhB;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA4BG;IACH,yBAFU,OAAO,CAEe;IAShC;;;;;;OAMG;IACH,qBAJW,MAAM,SACN,MAAM,SACN,MAAM,QAWhB;IAED;;;OAGG;IACH,iBAHY,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAKnC;IAED;;;OAGG;IACH,aAHY,MAAM,CAKjB;IAED;;;;;;;;;OASG;IACH,gBAHW,MAAM,GACL,MAAM,CAejB;IAED;;;OAGG;IACH,kBAHW,MAAM,GACL,MAAM,CAIjB;IAED;;;;OAIG;IACH,oBAJW,MAAM,GACL,YAAY,GAAC,IAAI,CAM5B;IAED;;OAEG;IACH,uBAFY;QAAE,MAAM,MAAM,CAAC;QAAC,IAAI,EAAE,YAAY,CAAA;KAAE,EAAE,CAIjD;IAED;;;OAGG;IACH,cAmBC;IAED;;;;;;OAMG;IACH,aALW,MAAM,KACN,MAAM,KACN,MAAM,GACL,MAAM,CAajB;IAED;;;;;;;;OAQG;IACH,iBAPW,MAAM,KACN,MAAM,KACN,MAAM,MACN,MAAM,MACN,MAAM,MACN,MAAM,QAOhB;IAED;;;;;;;;OAQG;IACH,iBAPW,MAAM,KACN,MAAM,KACN,MAAM,OACN,MAAM,OACN,MAAM,OACN,MAAM,QAOhB;IAED;;;;;;OAMG;IACH,kCANW,MAAM,KACN,MAAM,KACN,MAAM,KACN,MAAM,SACN,MAAM,QAOhB;IAED;;;;;;OAMG;IACH,cALW,MAAM,KACN,MAAM,KACN,MAAM,YACN,OAAO,QAIjB;IAED;;;;;OAKG;IACH,aALW,MAAM,KACN,MAAM,KACN,MAAM,GACL,OAAO,CAIlB;IAED;;;;;;;;OAQG;IACH,oBANW,YAAY,GAAC,MAAM,EAAE,KACrB,MAAM,KACN,MAAM,KACN,MAAM,GACL,YAAY,GAAC,MAAM,EAAE,CAUhC;IAED;;;;;;;OAOG;IACH,mBANW,MAAM,KACN,MAAM,KACN,MAAM,KACN,MAAM,GACL,MAAM,CAMjB;IAED;;;;;;;;;;OAUG;IACH,cAHW,UAAU,GACT,OAAO,CAqDlB;IAED;;;;;;;;;;;OAWG;IACH,QAFY,MAAM,CAajB;IAED;;;;;;;;;OASG;IACH,oCASC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;OA0BG;IACH,eAJW,MAAM,WACN,MAAM,WACN,MAAM,QAyChB;IAED;;;;OAIG;IACH,cASC;;CACJ"}
1
+ {"version":3,"file":"FluidField.d.ts","sourceRoot":"","sources":["../../../../../src/engine/physics/fluid/FluidField.js"],"names":[],"mappings":"AASA;;;;;;;;;;;;;;;;;;;GAmBG;AACH;IAQI;;;;OAIG;IACH,YAFU,YAAY,GAAC,IAAI,CAET;IAElB;;OAEG;IACH,YAFU,YAAY,GAAC,IAAI,CAET;IAElB;;OAEG;IACH,YAFU,YAAY,GAAC,IAAI,CAET;IAElB;;;;;;OAMG;IACH,OAFU,UAAU,GAAC,IAAI,CAEZ;IAEb;;;;;;;;;;;;;;;;;;;;;;;;;;;OA2BG;IACH,sBAFU,UAAU,GAAC,IAAI,CAEG;IAE5B;;;;;;;;;;;;;OAaG;IACH,UAFU,YAAY,kBAAc,IAAI,CAExB;IAEhB;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA4BG;IACH,yBAFU,OAAO,CAEe;IAShC;;;;;;OAMG;IACH,qBAJW,MAAM,SACN,MAAM,SACN,MAAM,QAWhB;IAED;;;OAGG;IACH,iBAHY,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAKnC;IAED;;;OAGG;IACH,aAHY,MAAM,CAKjB;IAED;;;;;;;;;OASG;IACH,gBAHW,MAAM,GACL,MAAM,CAejB;IAED;;;OAGG;IACH,kBAHW,MAAM,GACL,MAAM,CAIjB;IAED;;;;OAIG;IACH,oBAJW,MAAM,GACL,YAAY,GAAC,IAAI,CAM5B;IAED;;OAEG;IACH,uBAFY;QAAE,MAAM,MAAM,CAAC;QAAC,IAAI,EAAE,YAAY,CAAA;KAAE,EAAE,CAIjD;IAED;;;OAGG;IACH,cAmBC;IAED;;;;;;OAMG;IACH,aALW,MAAM,KACN,MAAM,KACN,MAAM,GACL,MAAM,CAajB;IAED;;;;;;;;OAQG;IACH,iBAPW,MAAM,KACN,MAAM,KACN,MAAM,MACN,MAAM,MACN,MAAM,MACN,MAAM,QAOhB;IAED;;;;;;;;OAQG;IACH,iBAPW,MAAM,KACN,MAAM,KACN,MAAM,OACN,MAAM,OACN,MAAM,OACN,MAAM,QAOhB;IAED;;;;;;OAMG;IACH,kCANW,MAAM,KACN,MAAM,KACN,MAAM,KACN,MAAM,SACN,MAAM,QAOhB;IAED;;;;;;OAMG;IACH,cALW,MAAM,KACN,MAAM,KACN,MAAM,YACN,OAAO,QAIjB;IAED;;;;;OAKG;IACH,aALW,MAAM,KACN,MAAM,KACN,MAAM,GACL,OAAO,CAIlB;IAED;;;;;;;;OAQG;IACH,oBANW,YAAY,GAAC,MAAM,EAAE,KACrB,MAAM,KACN,MAAM,KACN,MAAM,GACL,YAAY,GAAC,MAAM,EAAE,CAUhC;IAED;;;;;;;OAOG;IACH,mBANW,MAAM,KACN,MAAM,KACN,MAAM,KACN,MAAM,GACL,MAAM,CAMjB;IAED;;;;;;;;;;OAUG;IACH,cAHW,UAAU,GACT,OAAO,CAqDlB;IAED;;;;;;;;;;;OAWG;IACH,QAFY,MAAM,CAajB;IAED;;;;;;;;;OASG;IACH,oCASC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;OA0BG;IACH,eAJW,MAAM,WACN,MAAM,WACN,MAAM,QAyChB;IAED;;;;OAIG;IACH,cASC;;CACJ"}
@@ -65,17 +65,21 @@ export class FluidField {
65
65
  * Per-cell pre-baked neighbourhood mask consumed by the pressure SOR loop.
66
66
  * One byte per cell encoding which of the 6 cardinal neighbours are fluid:
67
67
  *
68
- * bit 0 (= 1) : -x neighbour is fluid (in-bounds AND non-solid)
69
- * bit 1 (= 2) : +x neighbour is fluid
70
- * bit 2 (= 4) : -y neighbour is fluid
71
- * bit 3 (= 8) : +y neighbour is fluid
72
- * bit 4 (= 16) : -z neighbour is fluid
73
- * bit 5 (= 32) : +z neighbour is fluid
68
+ * bit 0 (= 1) : -x neighbour is fluid (in-bounds AND non-solid)
69
+ * bit 1 (= 2) : +x neighbour is fluid
70
+ * bit 2 (= 4) : -y neighbour is fluid
71
+ * bit 3 (= 8) : +y neighbour is fluid
72
+ * bit 4 (= 16) : -z neighbour is fluid
73
+ * bit 5 (= 32) : +z neighbour is fluid
74
+ * bit 7 (= 128) : this cell is itself solid
74
75
  *
75
- * Solid cells themselves are encoded as `mask = 0` so the SOR loop's
76
- * `if (mask === 0) continue` skip handles both "this cell is solid" and
77
- * "this fluid cell is isolated" with a single comparison instead of one
78
- * solid-self-check plus six solid-neighbour-checks plus six boundary checks.
76
+ * A solid cell encodes as `128` (bit 7 set, neighbour bits clear) and an
77
+ * isolated fluid cell as `0`, so the SOR / PCG loops' `if ((mask & 63) === 0)
78
+ * continue` skip handles both "this cell is solid" and "this fluid cell is
79
+ * isolated" with a single comparison instead of one solid-self-check plus six
80
+ * solid-neighbour-checks plus six boundary checks. Bit 7 additionally lets
81
+ * {@link v3_grid_subtract_pressure_gradient} zero solid-cell velocity without
82
+ * consulting the {@link solid} array.
79
83
  *
80
84
  * Computed once by {@link recomputeSolidNeighbourMask} after every solid-mask
81
85
  * mutation. The simulator runs that recompute at the top of every project()
@@ -1 +1 @@
1
- {"version":3,"file":"FluidSimulator.d.ts","sourceRoot":"","sources":["../../../../../src/engine/physics/fluid/FluidSimulator.js"],"names":[],"mappings":";;;;;;;;;;;;;;;6BAyBU,MAAM;;;;;AAOhB;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH;;;;;;;;;GASG;AACH,wCAFU,MAAM,CAE2B;AAE3C;IAEI;;;OAGG;IACH,yBAFU,MAAM,CAEY;IAE5B;;;;OAIG;IACH,+BAFU,MAAM,CAEkB;IAElC;;;;;OAKG;IACH,uBAFU,MAAM,CAEU;IAE1B;;OAEG;IACH,6BAFU,MAAM,CAEgB;IAEhC;;;;;;;;;;;;;;;OAeG;IACH,iBAFU,MAAM,CAEqB;IAErC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA+BG;IACH,0BAFU,OAAO,CAEe;IAEhC;;;;;;;;;;;;;;;;;;;;;;;;;OAyBG;IACH,0BAFU,MAAM,CAEgB;IA+EhC;;;;;;;;;;;;OAYG;IACH,iCALW,MAAM,SACN,MAAM,SACN,MAAM,GACL,MAAM,CAUjB;IAED;;;;;;OAMG;IACH,iCAmDC;IAqBD;;;;;;;;;;;;;OAaG;IACH,oCAVW,MAAM,aAEN,uBAAuB,iBACvB,YAAY,GAAC,MAAM,EAAE,QAyF/B;;CACJ"}
1
+ {"version":3,"file":"FluidSimulator.d.ts","sourceRoot":"","sources":["../../../../../src/engine/physics/fluid/FluidSimulator.js"],"names":[],"mappings":";;;;;;;;;;;;;;;6BAyBU,MAAM;;;;;AAOhB;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH;;;;;;;;;GASG;AACH,wCAFU,MAAM,CAE2B;AAE3C;IAEI;;;OAGG;IACH,yBAFU,MAAM,CAEY;IAE5B;;;;OAIG;IACH,+BAFU,MAAM,CAEkB;IAElC;;;;;OAKG;IACH,uBAFU,MAAM,CAEU;IAE1B;;OAEG;IACH,6BAFU,MAAM,CAEgB;IAEhC;;;;;;;;;;;;;;;OAeG;IACH,iBAFU,MAAM,CAEqB;IAErC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA+BG;IACH,0BAFU,OAAO,CAEe;IAEhC;;;;;;;;;;;;;;;;;;;;;;;;;OAyBG;IACH,0BAFU,MAAM,CAEgB;IA+EhC;;;;;;;;;;;;OAYG;IACH,iCALW,MAAM,SACN,MAAM,SACN,MAAM,GACL,MAAM,CAUjB;IAED;;;;;;OAMG;IACH,iCAkDC;IAqBD;;;;;;;;;;;;;OAaG;IACH,oCAVW,MAAM,aAEN,uBAAuB,iBACvB,YAAY,GAAC,MAAM,EAAE,QAyF/B;;CACJ"}
@@ -332,7 +332,6 @@ export class FluidSimulator {
332
332
  field.velocity_x, field.velocity_y, field.velocity_z,
333
333
  field.pressure,
334
334
  res[0], res[1], res[2],
335
- solid,
336
335
  field.solid_neighbour_mask
337
336
  );
338
337
  }
@@ -4,18 +4,25 @@
4
4
  * {@link v3_grid_solve_pressure}, which uses the mask to skip boundary checks
5
5
  * and solid-neighbour checks entirely.
6
6
  *
7
- * Encoding (one byte per cell, low 6 bits):
7
+ * Encoding (one byte per cell):
8
8
  *
9
- * bit 0 (= 1) : -x neighbour is fluid
10
- * bit 1 (= 2) : +x neighbour is fluid
11
- * bit 2 (= 4) : -y neighbour is fluid
12
- * bit 3 (= 8) : +y neighbour is fluid
13
- * bit 4 (= 16) : -z neighbour is fluid
14
- * bit 5 (= 32) : +z neighbour is fluid
9
+ * bit 0 (= 1) : -x neighbour is fluid
10
+ * bit 1 (= 2) : +x neighbour is fluid
11
+ * bit 2 (= 4) : -y neighbour is fluid
12
+ * bit 3 (= 8) : +y neighbour is fluid
13
+ * bit 4 (= 16) : -z neighbour is fluid
14
+ * bit 5 (= 32) : +z neighbour is fluid
15
+ * bit 7 (= 128) : this cell is itself solid
15
16
  *
16
- * Solid cells themselves are encoded as `mask = 0` (not "their own neighbours
17
- * are fluid") so the SOR loop's `mask === 0 skip` branch covers both
18
- * "self-solid" and "fluid but no fluid neighbours" with one comparison.
17
+ * The low 6 bits are the fluid-neighbour set; bit 7 flags the cell as solid.
18
+ * A solid cell encodes as exactly `128` bit 7 set, every neighbour bit clear
19
+ * and an isolated fluid cell (no fluid neighbours) as `0`. Both therefore
20
+ * satisfy `(mask & 63) === 0`, so the SOR / PCG loops' "no fluid neighbours →
21
+ * skip" branch is a single `(mask & 63) === 0` comparison that covers
22
+ * "self-solid" and "fluid but no fluid neighbours" alike. The dedicated bit 7
23
+ * additionally lets {@link v3_grid_subtract_pressure_gradient} tell solid cells
24
+ * (zero the velocity) apart from isolated fluid (leave it) without the original
25
+ * `solid` array.
19
26
  *
20
27
  * O(N) — one byte-write per cell, up to six byte-reads per cell. The output
21
28
  * fully replaces whatever was in `mask` before; no need to zero-init.
@@ -1 +1 @@
1
- {"version":3,"file":"v3_grid_compute_solid_neighbour_mask.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/physics/fluid/solver/v3_grid_compute_solid_neighbour_mask.js"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,2DANW,UAAU,SACV,UAAU,SACV,MAAM,SACN,MAAM,SACN,MAAM,QAqChB"}
1
+ {"version":3,"file":"v3_grid_compute_solid_neighbour_mask.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/physics/fluid/solver/v3_grid_compute_solid_neighbour_mask.js"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,2DANW,UAAU,SACV,UAAU,SACV,MAAM,SACN,MAAM,SACN,MAAM,QAqChB"}
@@ -6,18 +6,25 @@ import { assert } from "../../../../core/assert.js";
6
6
  * {@link v3_grid_solve_pressure}, which uses the mask to skip boundary checks
7
7
  * and solid-neighbour checks entirely.
8
8
  *
9
- * Encoding (one byte per cell, low 6 bits):
9
+ * Encoding (one byte per cell):
10
10
  *
11
- * bit 0 (= 1) : -x neighbour is fluid
12
- * bit 1 (= 2) : +x neighbour is fluid
13
- * bit 2 (= 4) : -y neighbour is fluid
14
- * bit 3 (= 8) : +y neighbour is fluid
15
- * bit 4 (= 16) : -z neighbour is fluid
16
- * bit 5 (= 32) : +z neighbour is fluid
11
+ * bit 0 (= 1) : -x neighbour is fluid
12
+ * bit 1 (= 2) : +x neighbour is fluid
13
+ * bit 2 (= 4) : -y neighbour is fluid
14
+ * bit 3 (= 8) : +y neighbour is fluid
15
+ * bit 4 (= 16) : -z neighbour is fluid
16
+ * bit 5 (= 32) : +z neighbour is fluid
17
+ * bit 7 (= 128) : this cell is itself solid
17
18
  *
18
- * Solid cells themselves are encoded as `mask = 0` (not "their own neighbours
19
- * are fluid") so the SOR loop's `mask === 0 skip` branch covers both
20
- * "self-solid" and "fluid but no fluid neighbours" with one comparison.
19
+ * The low 6 bits are the fluid-neighbour set; bit 7 flags the cell as solid.
20
+ * A solid cell encodes as exactly `128` bit 7 set, every neighbour bit clear
21
+ * and an isolated fluid cell (no fluid neighbours) as `0`. Both therefore
22
+ * satisfy `(mask & 63) === 0`, so the SOR / PCG loops' "no fluid neighbours →
23
+ * skip" branch is a single `(mask & 63) === 0` comparison that covers
24
+ * "self-solid" and "fluid but no fluid neighbours" alike. The dedicated bit 7
25
+ * additionally lets {@link v3_grid_subtract_pressure_gradient} tell solid cells
26
+ * (zero the velocity) apart from isolated fluid (leave it) without the original
27
+ * `solid` array.
21
28
  *
22
29
  * O(N) — one byte-write per cell, up to six byte-reads per cell. The output
23
30
  * fully replaces whatever was in `mask` before; no need to zero-init.
@@ -49,7 +56,7 @@ export function v3_grid_compute_solid_neighbour_mask(mask, solid, res_x, res_y,
49
56
  for (let x = 0; x < res_x; x++) {
50
57
  const c = y_off + x;
51
58
  if (solid[c] !== 0) {
52
- mask[c] = 0;
59
+ mask[c] = 128; // bit 7 = this cell is solid; neighbour bits stay clear
53
60
  continue;
54
61
  }
55
62
  let m = 0;
@@ -11,17 +11,20 @@
11
11
  * a Uint8Array allocated and populated by {@link FluidField} via
12
12
  * {@link FluidField.recomputeSolidNeighbourMask}. The encoding is:
13
13
  *
14
- * bit 0 (= 1) : -x neighbour is fluid
15
- * bit 1 (= 2) : +x neighbour is fluid
16
- * bit 2 (= 4) : -y neighbour is fluid
17
- * bit 3 (= 8) : +y neighbour is fluid
18
- * bit 4 (= 16) : -z neighbour is fluid
19
- * bit 5 (= 32) : +z neighbour is fluid
14
+ * bit 0 (= 1) : -x neighbour is fluid
15
+ * bit 1 (= 2) : +x neighbour is fluid
16
+ * bit 2 (= 4) : -y neighbour is fluid
17
+ * bit 3 (= 8) : +y neighbour is fluid
18
+ * bit 4 (= 16) : -z neighbour is fluid
19
+ * bit 5 (= 32) : +z neighbour is fluid
20
+ * bit 7 (= 128) : this cell is itself solid (consumed by
21
+ * {@link v3_grid_subtract_pressure_gradient}, ignored here)
20
22
  *
21
- * Solid cells encode as `mask = 0`, so the inner loop's `mask === 0 ? skip` covers
22
- * both self-solid AND fluid-with-no-fluid-neighbours in one comparison. This
23
- * eliminates six boundary checks plus six solid-neighbour checks per cell per
24
- * iteration, replacing them with a single mask read and six bit-tests on a
23
+ * The low 6 bits are the fluid-neighbour set. A solid cell encodes as `128` and
24
+ * an isolated fluid cell as `0`, so the inner loop's `(mask & 63) === 0 ? skip`
25
+ * covers both self-solid AND fluid-with-no-fluid-neighbours in one comparison.
26
+ * This eliminates six boundary checks plus six solid-neighbour checks per cell
27
+ * per iteration, replacing them with a single mask read and six bit-tests on a
25
28
  * register-resident value. Boundary handling (cells at x=0, x=last, etc.) is
26
29
  * implicit — the recompute step writes 0 into the boundary-facing bit, so the
27
30
  * SOR loop never reads out-of-range memory.
@@ -1 +1 @@
1
- {"version":3,"file":"v3_grid_solve_pressure.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/physics/fluid/solver/v3_grid_solve_pressure.js"],"names":[],"mappings":"AAmBA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AACH,iDAdW,YAAY,eAAa,cAEzB,YAAY,eAAa,SAEzB,MAAM,SACN,MAAM,SACN,MAAM,cACN,MAAM,SACN,MAAM,kBAEN,UAAU,QA+DpB"}
1
+ {"version":3,"file":"v3_grid_solve_pressure.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/physics/fluid/solver/v3_grid_solve_pressure.js"],"names":[],"mappings":"AAmBA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiDG;AACH,iDAdW,YAAY,eAAa,cAEzB,YAAY,eAAa,SAEzB,MAAM,SACN,MAAM,SACN,MAAM,cACN,MAAM,SACN,MAAM,kBAEN,UAAU,QAiEpB"}
@@ -30,17 +30,20 @@ const POPCOUNT_6 = (function () {
30
30
  * a Uint8Array allocated and populated by {@link FluidField} via
31
31
  * {@link FluidField.recomputeSolidNeighbourMask}. The encoding is:
32
32
  *
33
- * bit 0 (= 1) : -x neighbour is fluid
34
- * bit 1 (= 2) : +x neighbour is fluid
35
- * bit 2 (= 4) : -y neighbour is fluid
36
- * bit 3 (= 8) : +y neighbour is fluid
37
- * bit 4 (= 16) : -z neighbour is fluid
38
- * bit 5 (= 32) : +z neighbour is fluid
33
+ * bit 0 (= 1) : -x neighbour is fluid
34
+ * bit 1 (= 2) : +x neighbour is fluid
35
+ * bit 2 (= 4) : -y neighbour is fluid
36
+ * bit 3 (= 8) : +y neighbour is fluid
37
+ * bit 4 (= 16) : -z neighbour is fluid
38
+ * bit 5 (= 32) : +z neighbour is fluid
39
+ * bit 7 (= 128) : this cell is itself solid (consumed by
40
+ * {@link v3_grid_subtract_pressure_gradient}, ignored here)
39
41
  *
40
- * Solid cells encode as `mask = 0`, so the inner loop's `mask === 0 ? skip` covers
41
- * both self-solid AND fluid-with-no-fluid-neighbours in one comparison. This
42
- * eliminates six boundary checks plus six solid-neighbour checks per cell per
43
- * iteration, replacing them with a single mask read and six bit-tests on a
42
+ * The low 6 bits are the fluid-neighbour set. A solid cell encodes as `128` and
43
+ * an isolated fluid cell as `0`, so the inner loop's `(mask & 63) === 0 ? skip`
44
+ * covers both self-solid AND fluid-with-no-fluid-neighbours in one comparison.
45
+ * This eliminates six boundary checks plus six solid-neighbour checks per cell
46
+ * per iteration, replacing them with a single mask read and six bit-tests on a
44
47
  * register-resident value. Boundary handling (cells at x=0, x=last, etc.) is
45
48
  * implicit — the recompute step writes 0 into the boundary-facing bit, so the
46
49
  * SOR loop never reads out-of-range memory.
@@ -101,9 +104,11 @@ export function v3_grid_solve_pressure(pressure, divergence, res_x, res_y, res_z
101
104
  const c = z_off + y_off + x;
102
105
 
103
106
  const mask = neighbour_mask[c];
104
- if (mask === 0) {
105
- // Solid cell OR isolated fluid with no fluid neighbours.
106
- // Either way, no degrees of freedom — skip.
107
+ if ((mask & 63) === 0) {
108
+ // No fluid neighbours: solid cell (bit 7 set) OR isolated
109
+ // fluid. Either way, no degrees of freedom — skip. Cells
110
+ // that reach past here have bit 7 clear, so `mask` indexes
111
+ // POPCOUNT_6 (sized 64) in range.
107
112
  continue;
108
113
  }
109
114
 
@@ -69,9 +69,10 @@
69
69
  * the hot path that prevents MIC-PCG from parallelizing onto a GPU without
70
70
  * substantial reformulation. On single-thread JS that's not a concern.
71
71
  *
72
- * Solid cells (mask = 0) are excluded from the system: their pressure stays at
73
- * whatever it was (typically 0), their entries in r, z, s, As stay 0, and they
74
- * contribute nothing to dot products.
72
+ * Cells with no fluid neighbours solid cells (mask bit 7 set) and isolated
73
+ * fluid (mask = 0), both identified by `(mask & 63) === 0` are excluded from
74
+ * the system: their pressure stays at whatever it was (typically 0), their
75
+ * entries in r, z, s, As stay 0, and they contribute nothing to dot products.
75
76
  *
76
77
  * @param {Float32Array|Float16Array} pressure Mutated in place. Pre-fill with 0
77
78
  * or with the previous step's solution to warm-start.
@@ -1 +1 @@
1
- {"version":3,"file":"v3_grid_solve_pressure_pcg.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/physics/fluid/solver/v3_grid_solve_pressure_pcg.js"],"names":[],"mappings":"AAuCA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0FG;AACH,qDAhBW,YAAY,eAAa,cAEzB,YAAY,eAAa,SACzB,MAAM,SACN,MAAM,SACN,MAAM,kBACN,MAAM,kBAEN,UAAU,aACV,YAAY,aACZ,YAAY,aACZ,YAAY,cACZ,YAAY,kBACZ,YAAY,QAuNtB"}
1
+ {"version":3,"file":"v3_grid_solve_pressure_pcg.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/physics/fluid/solver/v3_grid_solve_pressure_pcg.js"],"names":[],"mappings":"AAuCA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2FG;AACH,qDAhBW,YAAY,eAAa,cAEzB,YAAY,eAAa,SACzB,MAAM,SACN,MAAM,SACN,MAAM,kBACN,MAAM,kBAEN,UAAU,aACV,YAAY,aACZ,YAAY,aACZ,YAAY,cACZ,YAAY,kBACZ,YAAY,QA0NtB"}