reze-engine 0.35.0 → 0.36.1

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.
@@ -2,7 +2,8 @@ import { Vec3 } from "../math"
2
2
  import type { RigidBodyStore } from "./body"
3
3
  import { RigidbodyType } from "./types"
4
4
  import type { SixDofSpringConstraint } from "./constraint"
5
- import { solveConstraints, type SolverCache } from "./solver"
5
+ import { solveConstraints, saveContactImpulses, applySplitImpulsePush, type SolverCache } from "./solver"
6
+ import { ManifoldCache } from "./manifold"
6
7
  import { findContacts, type ContactPool } from "./contact"
7
8
 
8
9
  // World step: predict velocities → collide → solve → position correction →
@@ -38,6 +39,9 @@ export class World {
38
39
  readonly gravity: Vec3
39
40
  solverIterations = 10
40
41
 
42
+ /** Per-pair contact impulse history behind warm starting. */
43
+ private manifolds = new ManifoldCache()
44
+
41
45
  // Per-body damping factors pow(1−damping, dt), cached because damping and
42
46
  // the fixed dt never change — two Math.pow per body per substep otherwise.
43
47
  private dampCacheDt = -1
@@ -166,50 +170,20 @@ export class World {
166
170
 
167
171
  // 3. Solve joint + contact constraints (velocity-only).
168
172
  if (constraints.length > 0 || contacts.count > 0) {
169
- solveConstraints(store, constraints, cache, contacts, dt, this.solverIterations)
170
- }
171
-
172
- // 4. Position correction (split impulse). Direct translation along the
173
- // contact normal — joint constraints in the same SI loop can't undo
174
- // it because it doesn't go through the velocity channel. Inverse-mass
175
- // weighted so a kinematic body stays put and only the dynamic one
176
- // translates.
177
- // Softer and later than a rigid-body engine would use, deliberately. MMD
178
- // cloth is a dense chain of small bodies resting on the character, and
179
- // aggressive penetration recovery feeds those contacts energy that the
180
- // joint springs hand straight back — the tips never settle. Measured on a
181
- // 688-body model: resting peak velocity 1.79 → 1.10, and 12.9 → 6.6 under
182
- // an idle animation, for a slop of two hundredths of a unit (under 2 mm at
183
- // MMD scale) that no eye finds.
184
- const POS_CORRECTION_FACTOR = 0.15
185
- const POS_SLOP = 0.02
186
- for (let ci = 0; ci < contacts.count; ci++) {
187
- const c = contacts.get(ci)
188
- if (c.depth <= POS_SLOP) continue
189
- const imA = invMass[c.bodyA]
190
- const imB = invMass[c.bodyB]
191
- const total = imA + imB
192
- if (total <= 0) continue
193
- const correction = (c.depth - POS_SLOP) * POS_CORRECTION_FACTOR
194
- const dx = correction * c.nx
195
- const dy = correction * c.ny
196
- const dz = correction * c.nz
197
- const ai = c.bodyA * 3
198
- const bi = c.bodyB * 3
199
- if (imA > 0) {
200
- const fA = imA / total
201
- pos[ai + 0] -= dx * fA
202
- pos[ai + 1] -= dy * fA
203
- pos[ai + 2] -= dz * fA
204
- }
205
- if (imB > 0) {
206
- const fB = imB / total
207
- pos[bi + 0] += dx * fB
208
- pos[bi + 1] += dy * fB
209
- pos[bi + 2] += dz * fB
210
- }
173
+ solveConstraints(store, constraints, cache, contacts, dt, this.solverIterations, this.manifolds)
174
+ applySplitImpulsePush(store, dt)
175
+ saveContactImpulses(store, contacts, this.manifolds)
176
+ } else {
177
+ this.manifolds.clear()
211
178
  }
212
179
 
180
+ // 4. Penetration recovery happens in the contact velocity row (Baumgarte,
181
+ // CONTACT_ERP), not here. Bullet 2.75 — the build MMD's physics runs —
182
+ // defaults m_splitImpulse to false and folds positionalError straight
183
+ // into m_rhs, so a separate position-translation pass is a divergence
184
+ // from what PMX rigs are authored against. It also could not be made to
185
+ // behave: every contact translated the body by its own full share, so a
186
+ // panel carrying dozens of rows was shoved out dozens of times.
213
187
  // 5. Integrate. Cap angular velocity at π/2 per step — a high-impulse
214
188
  // contact spike on a low-inertia body would otherwise spin past π
215
189
  // in one step and trash the quaternion integration. Linear velocity