reze-engine 0.67.1 → 0.68.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.
@@ -1 +1 @@
1
- {"version":3,"file":"contact.d.ts","sourceRoot":"","sources":["../../src/physics/contact.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAA;AAa5C,eAAO,MAAM,cAAc,OAAO,CAAA;AAElC,MAAM,WAAW,OAAO;IACtB,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IAEb,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,EAAE,MAAM,CAAA;IAEX,EAAE,EAAE,MAAM,CAAA;IACV,EAAE,EAAE,MAAM,CAAA;IACV,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,MAAM,CAAA;IAChB,WAAW,EAAE,MAAM,CAAA;IAEnB,oBAAoB,EAAE,MAAM,CAAA;IAC5B,uBAAuB,EAAE,MAAM,CAAA;IAC/B,uBAAuB,EAAE,MAAM,CAAA;IAI/B,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;IACxC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;IACxC,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;IACjB;kFAC8E;IAC9E,GAAG,EAAE,MAAM,CAAA;IACX;6EACyE;IACzE,cAAc,EAAE,MAAM,CAAA;IACtB,gFAAgF;IAChF,kBAAkB,EAAE,MAAM,CAAA;IAC1B;;0DAEsD;IACtD,OAAO,EAAE,MAAM,CAAA;IAEf,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;IACrC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;IAC3C,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;IAC3C,QAAQ,EAAE,MAAM,CAAA;IAEhB,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;IACrC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;IAC3C,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;IAC3C,QAAQ,EAAE,MAAM,CAAA;CACjB;AAkCD,qBAAa,WAAW;IACtB,OAAO,CAAC,IAAI,CAAgB;IAC5B,KAAK,SAAI;IAET,OAAO,IAAI,OAAO;IAgBlB,KAAK,IAAI,IAAI;IAGb,GAAG,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO;CAGxB;AASD,wBAAgB,WAAW,CAAC,KAAK,EAAE,cAAc,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAahF;AAwoCD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,cAAc,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,GAAG,IAAI,CAgDrG;AAiCD,wBAAgB,YAAY,CAAC,KAAK,EAAE,cAAc,EAAE,IAAI,EAAE,WAAW,GAAG,IAAI,CAsB3E"}
1
+ {"version":3,"file":"contact.d.ts","sourceRoot":"","sources":["../../src/physics/contact.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAA;AAa5C,eAAO,MAAM,cAAc,OAAO,CAAA;AAElC,MAAM,WAAW,OAAO;IACtB,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IAEb,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,EAAE,MAAM,CAAA;IAEX,EAAE,EAAE,MAAM,CAAA;IACV,EAAE,EAAE,MAAM,CAAA;IACV,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,MAAM,CAAA;IAChB,WAAW,EAAE,MAAM,CAAA;IAEnB,oBAAoB,EAAE,MAAM,CAAA;IAC5B,uBAAuB,EAAE,MAAM,CAAA;IAC/B,uBAAuB,EAAE,MAAM,CAAA;IAI/B,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;IACxC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;IACxC,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;IACjB;kFAC8E;IAC9E,GAAG,EAAE,MAAM,CAAA;IACX;6EACyE;IACzE,cAAc,EAAE,MAAM,CAAA;IACtB,gFAAgF;IAChF,kBAAkB,EAAE,MAAM,CAAA;IAC1B;;0DAEsD;IACtD,OAAO,EAAE,MAAM,CAAA;IAEf,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;IACrC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;IAC3C,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;IAC3C,QAAQ,EAAE,MAAM,CAAA;IAEhB,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;IACrC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;IAC3C,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;IAC3C,QAAQ,EAAE,MAAM,CAAA;CACjB;AAkCD,qBAAa,WAAW;IACtB,OAAO,CAAC,IAAI,CAAgB;IAC5B,KAAK,SAAI;IAET,OAAO,IAAI,OAAO;IAgBlB,KAAK,IAAI,IAAI;IAGb,GAAG,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO;CAGxB;AASD,wBAAgB,WAAW,CAAC,KAAK,EAAE,cAAc,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAahF;AAwoCD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,cAAc,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,GAAG,IAAI,CAgDrG;AAiCD,wBAAgB,YAAY,CAAC,KAAK,EAAE,cAAc,EAAE,IAAI,EAAE,WAAW,GAAG,IAAI,CAyB3E"}
@@ -1091,7 +1091,10 @@ function flipOneNormal(c) {
1091
1091
  // see RigidBodyStore.getCollisionPairs. SAP / dynamic AABB tree pay off
1092
1092
  // above ~500 bodies; below that this flat sweep wins on cache locality.
1093
1093
  export function findContacts(store, pool) {
1094
- store.updateAabbs();
1094
+ // Inflated by the contact margin and no more: a narrowphase only ever makes
1095
+ // a contact within CONTACT_MARGIN, so a fatter box only bought SAT tests on
1096
+ // pairs that could never touch — at 0.5 that was most of the collision cost.
1097
+ store.updateAabbs(CONTACT_MARGIN + 0.01);
1095
1098
  const pairs = store.getCollisionPairs();
1096
1099
  for (let p = 0; p < pairs.length; p += 2) {
1097
1100
  const i = pairs[p];
@@ -14,6 +14,8 @@ export declare class RezePhysics {
14
14
  private timeAccum;
15
15
  private readonly fixedTimeStep;
16
16
  private readonly maxSubSteps;
17
+ private static readonly ITERATIONS;
18
+ private static readonly MIN_STEPS;
17
19
  /** EMA of one world.step's CPU cost; drives catch-up load shedding. */
18
20
  private stepCostEmaMs;
19
21
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"physics.d.ts","sourceRoot":"","sources":["../../src/physics/physics.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"physics.d.ts","sourceRoot":"","sources":["../../src/physics/physics.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,SAAS,CAAA;AAC1C,OAAO,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,SAAS,CAAA;AAE/C,OAAO,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAA;AACvC,OAAO,EAAS,KAAK,WAAW,EAAE,MAAM,SAAS,CAAA;AAYjD,qBAAa,WAAW;IACtB,OAAO,CAAC,WAAW,CAAa;IAChC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,KAAK,CAAgB;IAC7B,OAAO,CAAC,KAAK,CAAO;IACpB,OAAO,CAAC,WAAW,CAA0B;IAC7C,OAAO,CAAC,WAAW,CAAa;IAChC,OAAO,CAAC,QAAQ,CAAa;IAC7B,OAAO,CAAC,UAAU,CAAO;IACzB,OAAO,CAAC,SAAS,CAAI;IAOrB,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAU;IACxC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAK;IACjC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAI;IAGtC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAI;IACrC,uEAAuE;IACvE,OAAO,CAAC,aAAa,CAAM;IAC3B;;;;;;;;;;;;;;;;OAgBG;IACH,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAM;IACzC;gFAC4E;IAC5E,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAI;IACpC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAK;IAMrC,OAAO,CAAC,aAAa,CAAc;IACnC,OAAO,CAAC,gBAAgB,CAAc;IAQtC,OAAO,CAAC,YAAY,CAAc;IAClC,OAAO,CAAC,YAAY,CAAc;IAClC,OAAO,CAAC,YAAY,CAAc;IAClC,OAAO,CAAC,eAAe,CAAc;IAMrC,OAAO,CAAC,OAAO,CAAY;IAE3B,OAAO,CAAC,aAAa,CAAY;IAMjC,OAAO,CAAC,WAAW,CAAY;IAG/B,aAAa,SAAI;IAEjB;yCACqC;IACrC,OAAO,CAAC,UAAU,CAAK;gBAEX,WAAW,EAAE,SAAS,EAAE,EAAE,MAAM,GAAE,KAAK,EAAO;IAoE1D,OAAO,CAAC,mBAAmB;IA4B3B,OAAO,CAAC,aAAa;IAKrB;;;;;;;;OAQG;IACH,QAAQ,CAAC,EAAE,EAAE,OAAO,GAAG,IAAI;IAI3B,UAAU,CAAC,OAAO,EAAE,IAAI,GAAG,IAAI;IAG/B,UAAU,IAAI,IAAI;IAGlB,2EAA2E;IAC3E,OAAO,CAAC,IAAI,EAAE,WAAW,GAAG,IAAI,GAAG,IAAI;IAGvC,OAAO,IAAI,WAAW,GAAG,IAAI;IAG7B,cAAc,IAAI,SAAS,EAAE;IAG7B,SAAS,IAAI,KAAK,EAAE;IAGpB,QAAQ,IAAI,cAAc;IAI1B,sBAAsB,IAAI,KAAK,CAAC;QAAE,QAAQ,EAAE,IAAI,CAAC;QAAC,QAAQ,EAAE,IAAI,CAAA;KAAE,CAAC;IAiBnE,KAAK,CAAC,iBAAiB,EAAE,IAAI,EAAE,GAAG,IAAI;IActC,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,iBAAiB,EAAE,IAAI,EAAE,EAAE,uBAAuB,EAAE,YAAY,GAAG,IAAI;IAiFxF,OAAO,CAAC,iBAAiB;IAuCzB,OAAO,CAAC,uBAAuB;IAqF/B,OAAO,CAAC,yBAAyB;IAgEjC;;;;;;;;;;;;;;;;;;;OAmBG;IACH,OAAO,CAAC,sBAAsB;IA0C9B,OAAO,CAAC,2BAA2B;IA6EnC,OAAO,CAAC,wBAAwB;IA2BhC,OAAO,CAAC,sBAAsB;IAqC9B;;;;;;;;;;;;;;;;;;;OAmBG;IACH,gBAAgB,CAAC,WAAW,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAmB5D,6EAA6E;IAC7E,OAAO,CAAC,eAAe,CAA4B;IACnD,OAAO,CAAC,eAAe,CAA4B;IAEnD;;;;;;;;;OASG;IACH,qBAAqB,IAAI,MAAM,EAAE;IAUjC,OAAO,CAAC,oBAAoB;CA6D7B"}
@@ -15,8 +15,14 @@ export class RezePhysics {
15
15
  constructor(rigidbodies, joints = []) {
16
16
  this.firstFrame = true;
17
17
  this.timeAccum = 0;
18
- this.fixedTimeStep = 1 / 60;
19
- this.maxSubSteps = 6;
18
+ // Two 120 Hz steps of 5 iterations where MMD takes one 60 Hz step of 10.
19
+ // Contacts and joints converge against each other far better in two small
20
+ // steps: a thin skirt lattice pressed on the legs shook 58% less and sat
21
+ // inside them a third as often. Collision runs every step — reusing one
22
+ // contact set across steps missed contacts that began between detections,
23
+ // and the late push-out shook worse than the single step ever did.
24
+ this.fixedTimeStep = 1 / 120;
25
+ this.maxSubSteps = 12;
20
26
  /** EMA of one world.step's CPU cost; drives catch-up load shedding. */
21
27
  this.stepCostEmaMs = 0.5;
22
28
  /**
@@ -90,6 +96,7 @@ export class RezePhysics {
90
96
  this.groundBody = gi;
91
97
  this.store.groundIndex = gi;
92
98
  this.world = new World(new Vec3(0, -98, 0));
99
+ this.world.solverIterations = RezePhysics.ITERATIONS;
93
100
  this.constraints = buildConstraints(rigidbodies, joints);
94
101
  this.solverCache = new SolverCache(this.constraints);
95
102
  this.contacts = new ContactPool();
@@ -258,7 +265,7 @@ export class RezePhysics {
258
265
  // Catching up must never cost more than a frame-budget slice: shed the backlog
259
266
  // instead (physics runs fractionally slow-motion for a beat; framerate holds).
260
267
  const budgetMs = Math.min(this.stepBudgetMaxMs, Math.max(this.stepBudgetMinMs, dt * 1000 * this.stepBudgetFraction));
261
- const affordable = Math.max(1, Math.floor(budgetMs / Math.max(0.05, this.stepCostEmaMs)));
268
+ const affordable = Math.max(RezePhysics.MIN_STEPS, Math.floor(budgetMs / Math.max(0.05, this.stepCostEmaMs)));
262
269
  if (nSub > affordable)
263
270
  nSub = affordable;
264
271
  for (let k = 0; k < nSub; k++) {
@@ -817,3 +824,7 @@ export class RezePhysics {
817
824
  }
818
825
  }
819
826
  }
827
+ RezePhysics.ITERATIONS = 5;
828
+ // One 60 Hz frame of simulated time — what load shedding may never cut
829
+ // below, the guarantee the single 1/60 step always gave.
830
+ RezePhysics.MIN_STEPS = 2;
@@ -1 +1 @@
1
- {"version":3,"file":"solver.d.ts","sourceRoot":"","sources":["../../src/physics/solver.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAA;AAC5C,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAA;AAE1D,OAAO,KAAK,EAAW,WAAW,EAAE,MAAM,WAAW,CAAA;AACrD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAmJ/C,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,cAAc,EACrB,WAAW,EAAE,sBAAsB,EAAE,EACrC,KAAK,EAAE,WAAW,EAClB,QAAQ,EAAE,WAAW,EACrB,EAAE,EAAE,MAAM,EACV,UAAU,EAAE,MAAM,EAClB,SAAS,GAAE,aAAa,GAAG,IAAW,GACrC,IAAI,CA+FN;AAgDD;8EAC8E;AAC9E,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,cAAc,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI,CA8B7E;AAyCD,qBAAa,WAAW;IACtB,QAAQ,CAAC,CAAC,EAAE,YAAY,CAAA;IACxB,QAAQ,CAAC,CAAC,EAAE,UAAU,CAAA;IACtB;sEACkE;IAClE,QAAQ,CAAC,CAAC,EAAE,YAAY,CAAA;gBACZ,WAAW,EAAE,sBAAsB,EAAE;CAUlD;AAs6BD,0EAA0E;AAC1E,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,cAAc,EACrB,QAAQ,EAAE,WAAW,EACrB,SAAS,EAAE,aAAa,GACvB,IAAI,CAcN"}
1
+ {"version":3,"file":"solver.d.ts","sourceRoot":"","sources":["../../src/physics/solver.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAA;AAC5C,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAA;AAE1D,OAAO,KAAK,EAAW,WAAW,EAAE,MAAM,WAAW,CAAA;AACrD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AA6J/C,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,cAAc,EACrB,WAAW,EAAE,sBAAsB,EAAE,EACrC,KAAK,EAAE,WAAW,EAClB,QAAQ,EAAE,WAAW,EACrB,EAAE,EAAE,MAAM,EACV,UAAU,EAAE,MAAM,EAClB,SAAS,GAAE,aAAa,GAAG,IAAW,GACrC,IAAI,CA+FN;AAgDD;8EAC8E;AAC9E,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,cAAc,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI,CA8B7E;AAyCD,qBAAa,WAAW;IACtB,QAAQ,CAAC,CAAC,EAAE,YAAY,CAAA;IACxB,QAAQ,CAAC,CAAC,EAAE,UAAU,CAAA;IACtB;sEACkE;IAClE,QAAQ,CAAC,CAAC,EAAE,YAAY,CAAA;gBACZ,WAAW,EAAE,sBAAsB,EAAE;CAUlD;AAs6BD,0EAA0E;AAC1E,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,cAAc,EACrB,QAAQ,EAAE,WAAW,EACrB,SAAS,EAAE,aAAa,GACvB,IAAI,CAcN"}
@@ -30,7 +30,12 @@ const BOUNCE_THRESHOLD = 2.0;
30
30
  // pushes apart; separated (depth < 0, a speculative row) it ALLOWS approach at
31
31
  // the rate that closes the gap, which is what keeps those rows inert until the
32
32
  // body would really arrive.
33
- const CONTACT_ERP = 0.2;
33
+ //
34
+ // 0.1, half Bullet's 0.2: at 120 Hz each step closes a tenth of the overlap,
35
+ // the same correction per second as MMD's 0.2 at 60 Hz, and a light cloth body
36
+ // pressed between its joints and a collider stops being kicked out harder than
37
+ // the joints can take back (skirt shake −20% on its own).
38
+ const CONTACT_ERP = 0.1;
34
39
  // btContactSolverInfo's SOLVER_RANDMIZE_ORDER. Gauss-Seidel is order-biased —
35
40
  // rows solved first win — which on a cross-linked lattice shows up as a lean or
36
41
  // a period-2 chatter. Bullet ships the flag off; it is here to be measured.
@@ -104,6 +109,11 @@ const LIMIT_SOFTNESS_ANGULAR = 0.5;
104
109
  // ratio (ζ = 0.7); ported in isolation the motor form measured 0.0015 mean
105
110
  // reversal at jerk 0.5, the calmest joint-side result recorded.
106
111
  const SPRING_DAMPING = 1.0;
112
+ // The motor's velocity gain at the rate PMX rigs are authored against — MMD's
113
+ // Bullet at 60 Hz with 10 iterations. Bullet's form divides by the step's fps
114
+ // and iteration count; at any other substep rate that retunes every spring,
115
+ // so the gain is pinned here and the substep rate stays a solver choice.
116
+ const SPRING_VEL_FACTOR = (60 * SPRING_DAMPING) / 10;
107
117
  // btConstraintInfo2::erp, from infoGlobal.m_erp — used only by getMotorFactor.
108
118
  const INFO_ERP = 0.2;
109
119
  // btTypedConstraint::getMotorFactor. Scales a motor down as it approaches the
@@ -532,7 +542,7 @@ function setupConstraint(con, ci, cache, store, dt, invDt, iterations) {
532
542
  const k = con.springStiffness[i];
533
543
  const serr = curr - con.equilibriumPoint[i];
534
544
  const force = serr * k;
535
- const velFactor = (invDt * SPRING_DAMPING) / iterations;
545
+ const velFactor = SPRING_VEL_FACTOR;
536
546
  const target = -velFactor * force;
537
547
  // getMotorFactor's `vel` is Bullet's tag_vel: −targetVelocity for a
538
548
  // linear axis, which in our sign convention is the target itself.
@@ -635,7 +645,7 @@ function setupConstraint(con, ci, cache, store, dt, invDt, iterations) {
635
645
  const k = con.springStiffness[idx];
636
646
  const serr = _angDiffScratch[i] - con.equilibriumPoint[idx];
637
647
  const force = -serr * k;
638
- const velFactor = (invDt * SPRING_DAMPING) / iterations;
648
+ const velFactor = SPRING_VEL_FACTOR;
639
649
  const target = -velFactor * force;
640
650
  // tag_vel for a rotational axis is Bullet's targetVelocity, i.e. −ours.
641
651
  const motFact = getMotorFactor(_angDiffScratch[i], con.angularMin[i], con.angularMax[i], -target, invDt * INFO_ERP);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "reze-engine",
3
- "version": "0.67.1",
3
+ "version": "0.68.0",
4
4
  "description": "A lightweight WebGPU engine for real-time 3D MMD/PMX model rendering",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -1397,7 +1397,10 @@ function flipOneNormal(c: Contact): void {
1397
1397
  // see RigidBodyStore.getCollisionPairs. SAP / dynamic AABB tree pay off
1398
1398
  // above ~500 bodies; below that this flat sweep wins on cache locality.
1399
1399
  export function findContacts(store: RigidBodyStore, pool: ContactPool): void {
1400
- store.updateAabbs()
1400
+ // Inflated by the contact margin and no more: a narrowphase only ever makes
1401
+ // a contact within CONTACT_MARGIN, so a fatter box only bought SAT tests on
1402
+ // pairs that could never touch — at 0.5 that was most of the collision cost.
1403
+ store.updateAabbs(CONTACT_MARGIN + 0.01)
1401
1404
  const pairs = store.getCollisionPairs()
1402
1405
  for (let p = 0; p < pairs.length; p += 2) {
1403
1406
  const i = pairs[p]
@@ -24,8 +24,18 @@ export class RezePhysics {
24
24
  private contacts: ContactPool
25
25
  private firstFrame = true
26
26
  private timeAccum = 0
27
- private readonly fixedTimeStep = 1 / 60
28
- private readonly maxSubSteps = 6
27
+ // Two 120 Hz steps of 5 iterations where MMD takes one 60 Hz step of 10.
28
+ // Contacts and joints converge against each other far better in two small
29
+ // steps: a thin skirt lattice pressed on the legs shook 58% less and sat
30
+ // inside them a third as often. Collision runs every step — reusing one
31
+ // contact set across steps missed contacts that began between detections,
32
+ // and the late push-out shook worse than the single step ever did.
33
+ private readonly fixedTimeStep = 1 / 120
34
+ private readonly maxSubSteps = 12
35
+ private static readonly ITERATIONS = 5
36
+ // One 60 Hz frame of simulated time — what load shedding may never cut
37
+ // below, the guarantee the single 1/60 step always gave.
38
+ private static readonly MIN_STEPS = 2
29
39
  /** EMA of one world.step's CPU cost; drives catch-up load shedding. */
30
40
  private stepCostEmaMs = 0.5
31
41
  /**
@@ -131,6 +141,7 @@ export class RezePhysics {
131
141
  this.groundBody = gi
132
142
  this.store.groundIndex = gi
133
143
  this.world = new World(new Vec3(0, -98, 0))
144
+ this.world.solverIterations = RezePhysics.ITERATIONS
134
145
  this.constraints = buildConstraints(rigidbodies, joints)
135
146
  this.solverCache = new SolverCache(this.constraints)
136
147
  this.contacts = new ContactPool()
@@ -307,7 +318,7 @@ export class RezePhysics {
307
318
  this.stepBudgetMaxMs,
308
319
  Math.max(this.stepBudgetMinMs, dt * 1000 * this.stepBudgetFraction),
309
320
  )
310
- const affordable = Math.max(1, Math.floor(budgetMs / Math.max(0.05, this.stepCostEmaMs)))
321
+ const affordable = Math.max(RezePhysics.MIN_STEPS, Math.floor(budgetMs / Math.max(0.05, this.stepCostEmaMs)))
311
322
  if (nSub > affordable) nSub = affordable
312
323
  for (let k = 0; k < nSub; k++) {
313
324
  this.savePrevState()
@@ -37,7 +37,12 @@ const BOUNCE_THRESHOLD = 2.0
37
37
  // pushes apart; separated (depth < 0, a speculative row) it ALLOWS approach at
38
38
  // the rate that closes the gap, which is what keeps those rows inert until the
39
39
  // body would really arrive.
40
- const CONTACT_ERP = 0.2
40
+ //
41
+ // 0.1, half Bullet's 0.2: at 120 Hz each step closes a tenth of the overlap,
42
+ // the same correction per second as MMD's 0.2 at 60 Hz, and a light cloth body
43
+ // pressed between its joints and a collider stops being kicked out harder than
44
+ // the joints can take back (skirt shake −20% on its own).
45
+ const CONTACT_ERP = 0.1
41
46
 
42
47
  // btContactSolverInfo's SOLVER_RANDMIZE_ORDER. Gauss-Seidel is order-biased —
43
48
  // rows solved first win — which on a cross-linked lattice shows up as a lean or
@@ -119,6 +124,11 @@ const LIMIT_SOFTNESS_ANGULAR = 0.5
119
124
  // ratio (ζ = 0.7); ported in isolation the motor form measured 0.0015 mean
120
125
  // reversal at jerk 0.5, the calmest joint-side result recorded.
121
126
  const SPRING_DAMPING = 1.0
127
+ // The motor's velocity gain at the rate PMX rigs are authored against — MMD's
128
+ // Bullet at 60 Hz with 10 iterations. Bullet's form divides by the step's fps
129
+ // and iteration count; at any other substep rate that retunes every spring,
130
+ // so the gain is pinned here and the substep rate stays a solver choice.
131
+ const SPRING_VEL_FACTOR = (60 * SPRING_DAMPING) / 10
122
132
  // btConstraintInfo2::erp, from infoGlobal.m_erp — used only by getMotorFactor.
123
133
  const INFO_ERP = 0.2
124
134
 
@@ -543,7 +553,7 @@ function setupConstraint(
543
553
  const k = con.springStiffness[i]
544
554
  const serr = curr - con.equilibriumPoint[i]
545
555
  const force = serr * k
546
- const velFactor = (invDt * SPRING_DAMPING) / iterations
556
+ const velFactor = SPRING_VEL_FACTOR
547
557
  const target = -velFactor * force
548
558
  // getMotorFactor's `vel` is Bullet's tag_vel: −targetVelocity for a
549
559
  // linear axis, which in our sign convention is the target itself.
@@ -625,7 +635,7 @@ function setupConstraint(
625
635
  const k = con.springStiffness[idx]
626
636
  const serr = _angDiffScratch[i] - con.equilibriumPoint[idx]
627
637
  const force = -serr * k
628
- const velFactor = (invDt * SPRING_DAMPING) / iterations
638
+ const velFactor = SPRING_VEL_FACTOR
629
639
  const target = -velFactor * force
630
640
  // tag_vel for a rotational axis is Bullet's targetVelocity, i.e. −ours.
631
641
  const motFact = getMotorFactor(_angDiffScratch[i], con.angularMin[i], con.angularMax[i], -target, invDt * INFO_ERP)