reze-engine 0.9.1 → 0.9.2

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.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Reze Engine
2
2
 
3
- A lightweight WebGPU engine for real-time 3D MMD/PMX model rendering, built with TypeScript.
3
+ A minimal-dependency WebGPU engine for real-time MMD/PMX rendering. Only external dependency is Ammo.js for physics.
4
4
 
5
5
  ## Install
6
6
 
@@ -10,13 +10,12 @@ npm install reze-engine
10
10
 
11
11
  ## Features
12
12
 
13
- - Blinn-Phong lighting, alpha blending, rim lighting, outlines, MSAA 4x
14
- - VMD animation (multiple named, non-interruptible), IK solver, Ammo/Bullet physics
15
- - GPU picking (double-click/tap returns model + material name)
16
- - Souls-style follow cam (orbit center bound to model bone)
17
- - Optimized bind groups (per-frame / per-instance / per-material)
18
- - Ground shadow mapping with PCF
19
- - Multi-model (per-model materials, IK, physics)
13
+ - Blinn-Phong shading, alpha blending, rim lighting, outlines, MSAA 4x
14
+ - VMD animation with IK solver and Bullet physics
15
+ - Orbit camera with bone-follow mode
16
+ - GPU picking (double-click/tap)
17
+ - Ground plane with PCF shadow mapping
18
+ - Multi-model support
20
19
 
21
20
  ## Quick Start
22
21
 
@@ -25,7 +24,7 @@ import { Engine, Vec3 } from "reze-engine"
25
24
 
26
25
  const engine = new Engine(canvas, {
27
26
  ambientColor: new Vec3(0.88, 0.92, 0.99),
28
- cameraDistance: 31.5,
27
+ cameraDistance: 31.5, // MMD units (1 unit = 8 cm)
29
28
  })
30
29
  await engine.init()
31
30
 
@@ -56,8 +55,7 @@ engine.runRenderLoop()
56
55
  | `engine.toggleMaterialVisible(model, mat)` | Toggle material visibility |
57
56
  | `engine.setIKEnabled(enabled)` | Enable/disable IK globally |
58
57
  | `engine.setPhysicsEnabled(enabled)` | Enable/disable physics globally |
59
- | `engine.resetPhysics()` | Reset physics to current pose |
60
- | `engine.setCameraFollow(model, bone?, offset?)` | Follow cam bound to bone |
58
+ | `engine.setCameraFollow(model, bone?, offset?)` | Orbit center tracks a bone |
61
59
  | `engine.setCameraTarget(vec3)` | Static camera target |
62
60
  | `engine.setCameraDistance(d)` | Set orbit radius |
63
61
  | `engine.setCameraAlpha(a)` | Set horizontal orbit angle |
@@ -99,15 +97,20 @@ engine.runRenderLoop()
99
97
  cameraTarget: Vec3,
100
98
  cameraFov: number,
101
99
  onRaycast: (modelName, material, screenX, screenY) => void,
100
+ physicsOptions: {
101
+ constraintSolverKeywords: string[],
102
+ },
102
103
  }
103
104
  ```
104
105
 
106
+ `constraintSolverKeywords` — joints whose name contains any keyword use the Bullet 2.75 constraint solver; all others keep the stable Ammo 2.82+ default. See [babylon-mmd: Fix Constraint Behavior](https://noname0310.github.io/babylon-mmd/docs/reference/runtime/apply-physics-to-mmd-models/#fix-constraint-behavior) for details.
107
+
105
108
  ## Projects Using This Engine
106
109
 
107
- - **[MiKaPo](https://mikapo.vercel.app)** - Real-time motion capture for MMD
108
- - **[Popo](https://popo.love)** - LLM-generated MMD poses
109
- - **[MPL](https://mmd-mpl.vercel.app)** - Motion programming language for MMD
110
- - **[Mixamo-MMD](https://mixamo-mmd.vercel.app)** - Retarget Mixamo FBX to VMD
110
+ - **[MiKaPo](https://mikapo.vercel.app)** Real-time motion capture for MMD
111
+ - **[Popo](https://popo.love)** LLM-generated MMD poses
112
+ - **[MPL](https://mmd-mpl.vercel.app)** Motion programming language for MMD
113
+ - **[Mixamo-MMD](https://mixamo-mmd.vercel.app)** Retarget Mixamo FBX to VMD
111
114
 
112
115
  ## Tutorial
113
116
 
package/dist/engine.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { Vec3 } from "./math";
2
2
  import { Model } from "./model";
3
+ import { type PhysicsOptions } from "./physics";
3
4
  export type RaycastCallback = (modelName: string, material: string | null, screenX: number, screenY: number) => void;
4
5
  export type EngineOptions = {
5
6
  ambientColor?: Vec3;
@@ -10,9 +11,21 @@ export type EngineOptions = {
10
11
  cameraTarget?: Vec3;
11
12
  cameraFov?: number;
12
13
  onRaycast?: RaycastCallback;
14
+ physicsOptions?: PhysicsOptions;
15
+ };
16
+ export declare const DEFAULT_ENGINE_OPTIONS: {
17
+ ambientColor: Vec3;
18
+ directionalLightIntensity: number;
19
+ minSpecularIntensity: number;
20
+ rimLightIntensity: number;
21
+ cameraDistance: number;
22
+ cameraTarget: Vec3;
23
+ cameraFov: number;
24
+ onRaycast: undefined;
25
+ physicsOptions: {
26
+ constraintSolverKeywords: string[];
27
+ };
13
28
  };
14
- export type RequiredEngineOptions = Required<Omit<EngineOptions, "onRaycast">> & Pick<EngineOptions, "onRaycast">;
15
- export declare const DEFAULT_ENGINE_OPTIONS: RequiredEngineOptions;
16
29
  export interface EngineStats {
17
30
  fps: number;
18
31
  frameTime: number;
@@ -69,6 +82,7 @@ export declare class Engine {
69
82
  private shadowVPLightY;
70
83
  private shadowVPLightZ;
71
84
  private onRaycast?;
85
+ private physicsOptions;
72
86
  private lastTouchTime;
73
87
  private readonly DOUBLE_TAP_DELAY;
74
88
  private pickPipeline;
@@ -148,7 +162,6 @@ export declare class Engine {
148
162
  getIKEnabled(): boolean;
149
163
  setPhysicsEnabled(enabled: boolean): void;
150
164
  getPhysicsEnabled(): boolean;
151
- resetPhysics(): void;
152
165
  private forEachInstance;
153
166
  private updateInstances;
154
167
  private updateVertexBuffer;
@@ -1 +1 @@
1
- {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"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"}
package/dist/engine.js CHANGED
@@ -11,6 +11,7 @@ export const DEFAULT_ENGINE_OPTIONS = {
11
11
  cameraTarget: new Vec3(0, 12.5, 0),
12
12
  cameraFov: Math.PI / 4,
13
13
  onRaycast: undefined,
14
+ physicsOptions: { constraintSolverKeywords: ["胸"] },
14
15
  };
15
16
  export class Engine {
16
17
  static getInstance() {
@@ -30,6 +31,7 @@ export class Engine {
30
31
  this.shadowVPLightX = Number.NaN;
31
32
  this.shadowVPLightY = Number.NaN;
32
33
  this.shadowVPLightZ = Number.NaN;
34
+ this.physicsOptions = DEFAULT_ENGINE_OPTIONS.physicsOptions;
33
35
  this.lastTouchTime = 0;
34
36
  this.DOUBLE_TAP_DELAY = 300;
35
37
  this.pendingPick = null;
@@ -96,6 +98,7 @@ export class Engine {
96
98
  this.cameraTarget = options.cameraTarget ?? DEFAULT_ENGINE_OPTIONS.cameraTarget;
97
99
  this.cameraFov = options.cameraFov ?? DEFAULT_ENGINE_OPTIONS.cameraFov;
98
100
  this.onRaycast = options.onRaycast;
101
+ this.physicsOptions = options.physicsOptions ?? DEFAULT_ENGINE_OPTIONS.physicsOptions;
99
102
  }
100
103
  }
101
104
  // Step 1: Get WebGPU device and context
@@ -999,14 +1002,6 @@ export class Engine {
999
1002
  getPhysicsEnabled() {
1000
1003
  return this.physicsEnabled;
1001
1004
  }
1002
- resetPhysics() {
1003
- this.forEachInstance((inst) => {
1004
- if (!inst.physics)
1005
- return;
1006
- inst.model.computeWorldMatrices();
1007
- inst.physics.reset(inst.model.getWorldMatrices(), inst.model.getBoneInverseBindMatrices());
1008
- });
1009
- }
1010
1005
  forEachInstance(fn) {
1011
1006
  for (const inst of this.modelInstances.values())
1012
1007
  fn(inst);
@@ -1069,7 +1064,7 @@ export class Engine {
1069
1064
  });
1070
1065
  this.device.queue.writeBuffer(indexBuffer, 0, indices);
1071
1066
  const rbs = model.getRigidbodies();
1072
- const physics = rbs.length > 0 ? new Physics(rbs, model.getJoints()) : null;
1067
+ const physics = rbs.length > 0 ? new Physics(rbs, model.getJoints(), this.physicsOptions) : null;
1073
1068
  const shadowBindGroup = this.device.createBindGroup({
1074
1069
  label: `${name}: shadow bind`,
1075
1070
  layout: this.shadowDepthPipeline.getBindGroupLayout(0),
package/dist/index.d.ts CHANGED
@@ -2,4 +2,5 @@ export { Engine, type EngineStats } from "./engine";
2
2
  export { Model } from "./model";
3
3
  export { Vec3, Quat, Mat4 } from "./math";
4
4
  export { AnimationState, type AnimationClip, type BoneKeyframe, type MorphKeyframe, } from "./animation";
5
+ export { Physics, type PhysicsOptions } from "./physics";
5
6
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,KAAK,WAAW,EAAE,MAAM,UAAU,CAAA;AACnD,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAA;AAC/B,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAA;AACzC,OAAO,EACL,cAAc,EACd,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,aAAa,GACnB,MAAM,aAAa,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,KAAK,WAAW,EAAE,MAAM,UAAU,CAAA;AACnD,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAA;AAC/B,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAA;AACzC,OAAO,EACL,cAAc,EACd,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,aAAa,GACnB,MAAM,aAAa,CAAA;AACpB,OAAO,EAAE,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,WAAW,CAAA"}
package/dist/index.js CHANGED
@@ -2,3 +2,4 @@ export { Engine } from "./engine";
2
2
  export { Model } from "./model";
3
3
  export { Vec3, Quat, Mat4 } from "./math";
4
4
  export { AnimationState, } from "./animation";
5
+ export { Physics } from "./physics";
package/dist/physics.d.ts CHANGED
@@ -43,10 +43,14 @@ export interface Joint {
43
43
  springPosition: Vec3;
44
44
  springRotation: Vec3;
45
45
  }
46
+ export interface PhysicsOptions {
47
+ constraintSolverKeywords?: string[];
48
+ }
46
49
  export declare class Physics {
47
50
  private rigidbodies;
48
51
  private joints;
49
52
  private gravity;
53
+ private constraintSolverPattern;
50
54
  private ammoInitialized;
51
55
  private ammoPromise;
52
56
  private ammo;
@@ -56,9 +60,8 @@ export declare class Physics {
56
60
  private rigidbodiesInitialized;
57
61
  private jointsCreated;
58
62
  private firstFrame;
59
- private forceDisableOffsetForConstraintFrame;
60
63
  private zeroVector;
61
- constructor(rigidbodies: Rigidbody[], joints?: Joint[]);
64
+ constructor(rigidbodies: Rigidbody[], joints?: Joint[], options?: PhysicsOptions);
62
65
  private initAmmo;
63
66
  setGravity(gravity: Vec3): void;
64
67
  getGravity(): Vec3;
@@ -72,7 +75,6 @@ export declare class Physics {
72
75
  private createAmmoRigidbodies;
73
76
  private createAmmoJoints;
74
77
  private normalizeAngle;
75
- reset(boneWorldMatrices: Mat4[], boneInverseBindMatrices: Float32Array): void;
76
78
  step(dt: number, boneWorldMatrices: Mat4[], boneInverseBindMatrices: Float32Array): void;
77
79
  private computeBodyOffsets;
78
80
  private positionBodiesFromBones;
@@ -1 +1 @@
1
- {"version":3,"file":"physics.d.ts","sourceRoot":"","sources":["../src/physics.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"physics.d.ts","sourceRoot":"","sources":["../src/physics.ts"],"names":[],"mappings":"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"}
package/dist/physics.js CHANGED
@@ -13,8 +13,9 @@ export var RigidbodyType;
13
13
  RigidbodyType[RigidbodyType["Kinematic"] = 2] = "Kinematic";
14
14
  })(RigidbodyType || (RigidbodyType = {}));
15
15
  export class Physics {
16
- constructor(rigidbodies, joints = []) {
16
+ constructor(rigidbodies, joints = [], options) {
17
17
  this.gravity = new Vec3(0, -98, 0); // Gravity acceleration (cm/s²), MMD-style default
18
+ this.constraintSolverPattern = null;
18
19
  this.ammoInitialized = false;
19
20
  this.ammoPromise = null;
20
21
  this.ammo = null;
@@ -27,11 +28,14 @@ export class Physics {
27
28
  this.rigidbodiesInitialized = false; // bodyOffsetMatrixInverse computed and bodies positioned
28
29
  this.jointsCreated = false; // Joints delayed until after rigidbodies are positioned
29
30
  this.firstFrame = true; // Needed to reposition bodies before creating joints
30
- this.forceDisableOffsetForConstraintFrame = true; // MMD compatibility (Bullet 2.75 behavior)
31
31
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
32
32
  this.zeroVector = null; // Cached zero vector for velocity clearing
33
33
  this.rigidbodies = rigidbodies;
34
34
  this.joints = joints;
35
+ const keywords = options?.constraintSolverKeywords ?? [];
36
+ if (keywords.length > 0) {
37
+ this.constraintSolverPattern = new RegExp(keywords.map(k => k.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|"), "i");
38
+ }
35
39
  this.initAmmo();
36
40
  }
37
41
  async initAmmo() {
@@ -246,8 +250,9 @@ export class Physics {
246
250
  frameInB.setRotation(quatB);
247
251
  const useLinearReferenceFrameA = true;
248
252
  const constraint = new Ammo.btGeneric6DofSpringConstraint(bodyA, bodyB, frameInA, frameInB, useLinearReferenceFrameA);
249
- // Disable offset for constraint frame for MMD compatibility (Bullet 2.75 behavior)
250
- if (this.forceDisableOffsetForConstraintFrame) {
253
+ // Per-joint Bullet 2.75 constraint solver: disable m_useOffsetForConstraintFrame for
254
+ // joints whose name matches constraintSolverKeywords.
255
+ if (this.constraintSolverPattern && this.constraintSolverPattern.test(joint.name)) {
251
256
  let jointPtr;
252
257
  if (typeof Ammo.getPointer === "function") {
253
258
  jointPtr = Ammo.getPointer(constraint);
@@ -258,7 +263,6 @@ export class Physics {
258
263
  }
259
264
  if (jointPtr !== undefined && Ammo.HEAP8) {
260
265
  const heap8 = Ammo.HEAP8;
261
- // jointPtr + 1300 = m_useLinearReferenceFrameA, jointPtr + 1301 = m_useOffsetForConstraintFrame
262
266
  if (heap8[jointPtr + 1300] === (useLinearReferenceFrameA ? 1 : 0) && heap8[jointPtr + 1301] === 1) {
263
267
  heap8[jointPtr + 1301] = 0;
264
268
  }
@@ -329,63 +333,6 @@ export class Physics {
329
333
  }
330
334
  return angle;
331
335
  }
332
- // Reset physics state (reposition bodies, clear velocities)
333
- // Following babylon-mmd pattern: initialize all rigid body positions from current bone poses
334
- // Call this when starting a new animation to prevent physics instability from sudden pose changes
335
- reset(boneWorldMatrices, boneInverseBindMatrices) {
336
- if (!this.ammoInitialized || !this.ammo || !this.dynamicsWorld) {
337
- return;
338
- }
339
- const boneCount = boneWorldMatrices.length;
340
- const Ammo = this.ammo;
341
- // Ensure body offsets are computed
342
- if (!this.rigidbodiesInitialized) {
343
- this.computeBodyOffsets(boneInverseBindMatrices, boneCount);
344
- this.rigidbodiesInitialized = true;
345
- }
346
- // Reposition ALL rigid bodies from current bone poses (like babylon-mmd initialize)
347
- // This ensures all bodies are correctly positioned before physics starts
348
- for (let i = 0; i < this.rigidbodies.length; i++) {
349
- const rb = this.rigidbodies[i];
350
- const ammoBody = this.ammoRigidbodies[i];
351
- if (!ammoBody || rb.boneIndex < 0 || rb.boneIndex >= boneCount)
352
- continue;
353
- const boneIdx = rb.boneIndex;
354
- // Get bone world matrix
355
- const boneWorldMat = boneWorldMatrices[boneIdx];
356
- // Compute body world matrix: bodyWorld = boneWorld × bodyOffsetMatrix
357
- // (like babylon-mmd: bodyWorldMatrix = bodyOffsetMatrix.multiplyToRef(bodyWorldMatrix))
358
- const bodyOffsetMatrix = rb.bodyOffsetMatrix || rb.bodyOffsetMatrixInverse.inverse();
359
- const bodyWorldMatrix = boneWorldMat.multiply(bodyOffsetMatrix);
360
- const worldPos = bodyWorldMatrix.getPosition();
361
- const worldRot = bodyWorldMatrix.toQuat();
362
- // Set transform matrix
363
- const transform = new Ammo.btTransform();
364
- const pos = new Ammo.btVector3(worldPos.x, worldPos.y, worldPos.z);
365
- const quat = new Ammo.btQuaternion(worldRot.x, worldRot.y, worldRot.z, worldRot.w);
366
- transform.setOrigin(pos);
367
- transform.setRotation(quat);
368
- ammoBody.setWorldTransform(transform);
369
- ammoBody.getMotionState().setWorldTransform(transform);
370
- // Clear velocities for all rigidbodies
371
- if (!this.zeroVector) {
372
- this.zeroVector = new Ammo.btVector3(0, 0, 0);
373
- }
374
- ammoBody.setLinearVelocity(this.zeroVector);
375
- ammoBody.setAngularVelocity(this.zeroVector);
376
- // Explicitly activate dynamic rigidbodies after reset (wake them up)
377
- // This is critical for dress pieces and other dynamic bodies to prevent teleporting
378
- if (rb.type === RigidbodyType.Dynamic) {
379
- ammoBody.activate(true); // Wake up the body
380
- }
381
- Ammo.destroy(pos);
382
- Ammo.destroy(quat);
383
- }
384
- // Step simulation once to stabilize (like babylon-mmd)
385
- if (this.dynamicsWorld.stepSimulation) {
386
- this.dynamicsWorld.stepSimulation(0, 0, 0);
387
- }
388
- }
389
336
  // Syncs bones to rigidbodies, simulates dynamics, solves constraints
390
337
  // Modifies boneWorldMatrices in-place for dynamic rigidbodies that drive bones
391
338
  step(dt, boneWorldMatrices, boneInverseBindMatrices) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "reze-engine",
3
- "version": "0.9.1",
3
+ "version": "0.9.2",
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",
package/src/engine.ts CHANGED
@@ -2,7 +2,7 @@ import { Camera } from "./camera"
2
2
  import { Mat4, Vec3 } from "./math"
3
3
  import { Model } from "./model"
4
4
  import { PmxLoader } from "./pmx-loader"
5
- import { Physics } from "./physics"
5
+ import { Physics, type PhysicsOptions } from "./physics"
6
6
 
7
7
  export type RaycastCallback = (modelName: string, material: string | null, screenX: number, screenY: number) => void
8
8
 
@@ -15,11 +15,10 @@ export type EngineOptions = {
15
15
  cameraTarget?: Vec3
16
16
  cameraFov?: number
17
17
  onRaycast?: RaycastCallback
18
+ physicsOptions?: PhysicsOptions
18
19
  }
19
20
 
20
- export type RequiredEngineOptions = Required<Omit<EngineOptions, "onRaycast">> & Pick<EngineOptions, "onRaycast">
21
-
22
- export const DEFAULT_ENGINE_OPTIONS: RequiredEngineOptions = {
21
+ export const DEFAULT_ENGINE_OPTIONS = {
23
22
  ambientColor: new Vec3(0.88, 0.88, 0.88),
24
23
  directionalLightIntensity: 0.24,
25
24
  minSpecularIntensity: 0.3,
@@ -28,6 +27,7 @@ export const DEFAULT_ENGINE_OPTIONS: RequiredEngineOptions = {
28
27
  cameraTarget: new Vec3(0, 12.5, 0),
29
28
  cameraFov: Math.PI / 4,
30
29
  onRaycast: undefined,
30
+ physicsOptions: { constraintSolverKeywords: ["胸"] },
31
31
  }
32
32
 
33
33
  export interface EngineStats {
@@ -141,6 +141,7 @@ export class Engine {
141
141
  private shadowVPLightZ = Number.NaN
142
142
 
143
143
  private onRaycast?: RaycastCallback
144
+ private physicsOptions: PhysicsOptions = DEFAULT_ENGINE_OPTIONS.physicsOptions
144
145
  private lastTouchTime = 0
145
146
  private readonly DOUBLE_TAP_DELAY = 300
146
147
  // GPU picking
@@ -183,7 +184,7 @@ export class Engine {
183
184
  constructor(canvas: HTMLCanvasElement, options?: EngineOptions) {
184
185
  this.canvas = canvas
185
186
  if (options) {
186
- this.ambientColor = options.ambientColor ?? DEFAULT_ENGINE_OPTIONS.ambientColor!
187
+ this.ambientColor = options.ambientColor ?? DEFAULT_ENGINE_OPTIONS.ambientColor
187
188
  this.directionalLightIntensity =
188
189
  options.directionalLightIntensity ?? DEFAULT_ENGINE_OPTIONS.directionalLightIntensity
189
190
  this.minSpecularIntensity = options.minSpecularIntensity ?? DEFAULT_ENGINE_OPTIONS.minSpecularIntensity
@@ -192,6 +193,7 @@ export class Engine {
192
193
  this.cameraTarget = options.cameraTarget ?? DEFAULT_ENGINE_OPTIONS.cameraTarget
193
194
  this.cameraFov = options.cameraFov ?? DEFAULT_ENGINE_OPTIONS.cameraFov
194
195
  this.onRaycast = options.onRaycast
196
+ this.physicsOptions = options.physicsOptions ?? DEFAULT_ENGINE_OPTIONS.physicsOptions
195
197
  }
196
198
  }
197
199
 
@@ -1188,14 +1190,6 @@ export class Engine {
1188
1190
  return this.physicsEnabled
1189
1191
  }
1190
1192
 
1191
- public resetPhysics(): void {
1192
- this.forEachInstance((inst) => {
1193
- if (!inst.physics) return
1194
- inst.model.computeWorldMatrices()
1195
- inst.physics.reset(inst.model.getWorldMatrices(), inst.model.getBoneInverseBindMatrices())
1196
- })
1197
- }
1198
-
1199
1193
  private forEachInstance(fn: (inst: ModelInstance) => void): void {
1200
1194
  for (const inst of this.modelInstances.values()) fn(inst)
1201
1195
  }
@@ -1278,7 +1272,7 @@ export class Engine {
1278
1272
  this.device.queue.writeBuffer(indexBuffer, 0, indices)
1279
1273
 
1280
1274
  const rbs = model.getRigidbodies()
1281
- const physics = rbs.length > 0 ? new Physics(rbs, model.getJoints()) : null
1275
+ const physics = rbs.length > 0 ? new Physics(rbs, model.getJoints(), this.physicsOptions) : null
1282
1276
 
1283
1277
  const shadowBindGroup = this.device.createBindGroup({
1284
1278
  label: `${name}: shadow bind`,
package/src/index.ts CHANGED
@@ -7,3 +7,4 @@ export {
7
7
  type BoneKeyframe,
8
8
  type MorphKeyframe,
9
9
  } from "./animation"
10
+ export { Physics, type PhysicsOptions } from "./physics"
package/src/physics.ts CHANGED
@@ -50,10 +50,18 @@ export interface Joint {
50
50
  springRotation: Vec3 // Spring stiffness values
51
51
  }
52
52
 
53
+ export interface PhysicsOptions {
54
+ // Joint name keywords for per-joint Bullet 2.75 constraint solver behavior.
55
+ // Joints whose name contains any keyword get m_useOffsetForConstraintFrame
56
+ // disabled (matching Bullet 2.75). All others keep the stable Ammo 2.82+ default.
57
+ constraintSolverKeywords?: string[]
58
+ }
59
+
53
60
  export class Physics {
54
61
  private rigidbodies: Rigidbody[]
55
62
  private joints: Joint[]
56
63
  private gravity: Vec3 = new Vec3(0, -98, 0) // Gravity acceleration (cm/s²), MMD-style default
64
+ private constraintSolverPattern: RegExp | null = null
57
65
  private ammoInitialized = false
58
66
  private ammoPromise: Promise<AmmoInstance> | null = null
59
67
  private ammo: AmmoInstance | null = null
@@ -66,13 +74,16 @@ export class Physics {
66
74
  private rigidbodiesInitialized = false // bodyOffsetMatrixInverse computed and bodies positioned
67
75
  private jointsCreated = false // Joints delayed until after rigidbodies are positioned
68
76
  private firstFrame = true // Needed to reposition bodies before creating joints
69
- private forceDisableOffsetForConstraintFrame = true // MMD compatibility (Bullet 2.75 behavior)
70
77
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
71
78
  private zeroVector: any = null // Cached zero vector for velocity clearing
72
79
 
73
- constructor(rigidbodies: Rigidbody[], joints: Joint[] = []) {
80
+ constructor(rigidbodies: Rigidbody[], joints: Joint[] = [], options?: PhysicsOptions) {
74
81
  this.rigidbodies = rigidbodies
75
82
  this.joints = joints
83
+ const keywords = options?.constraintSolverKeywords ?? []
84
+ if (keywords.length > 0) {
85
+ this.constraintSolverPattern = new RegExp(keywords.map(k => k.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|"), "i")
86
+ }
76
87
  this.initAmmo()
77
88
  }
78
89
 
@@ -361,8 +372,9 @@ export class Physics {
361
372
  useLinearReferenceFrameA
362
373
  )
363
374
 
364
- // Disable offset for constraint frame for MMD compatibility (Bullet 2.75 behavior)
365
- if (this.forceDisableOffsetForConstraintFrame) {
375
+ // Per-joint Bullet 2.75 constraint solver: disable m_useOffsetForConstraintFrame for
376
+ // joints whose name matches constraintSolverKeywords.
377
+ if (this.constraintSolverPattern && this.constraintSolverPattern.test(joint.name)) {
366
378
  let jointPtr: number | undefined
367
379
  if (typeof Ammo.getPointer === "function") {
368
380
  jointPtr = Ammo.getPointer(constraint)
@@ -373,7 +385,6 @@ export class Physics {
373
385
 
374
386
  if (jointPtr !== undefined && Ammo.HEAP8) {
375
387
  const heap8 = Ammo.HEAP8 as Uint8Array
376
- // jointPtr + 1300 = m_useLinearReferenceFrameA, jointPtr + 1301 = m_useOffsetForConstraintFrame
377
388
  if (heap8[jointPtr + 1300] === (useLinearReferenceFrameA ? 1 : 0) && heap8[jointPtr + 1301] === 1) {
378
389
  heap8[jointPtr + 1301] = 0
379
390
  }
@@ -457,77 +468,6 @@ export class Physics {
457
468
  return angle
458
469
  }
459
470
 
460
- // Reset physics state (reposition bodies, clear velocities)
461
- // Following babylon-mmd pattern: initialize all rigid body positions from current bone poses
462
- // Call this when starting a new animation to prevent physics instability from sudden pose changes
463
- reset(boneWorldMatrices: Mat4[], boneInverseBindMatrices: Float32Array): void {
464
- if (!this.ammoInitialized || !this.ammo || !this.dynamicsWorld) {
465
- return
466
- }
467
-
468
- const boneCount = boneWorldMatrices.length
469
- const Ammo = this.ammo
470
-
471
- // Ensure body offsets are computed
472
- if (!this.rigidbodiesInitialized) {
473
- this.computeBodyOffsets(boneInverseBindMatrices, boneCount)
474
- this.rigidbodiesInitialized = true
475
- }
476
-
477
- // Reposition ALL rigid bodies from current bone poses (like babylon-mmd initialize)
478
- // This ensures all bodies are correctly positioned before physics starts
479
- for (let i = 0; i < this.rigidbodies.length; i++) {
480
- const rb = this.rigidbodies[i]
481
- const ammoBody = this.ammoRigidbodies[i]
482
- if (!ammoBody || rb.boneIndex < 0 || rb.boneIndex >= boneCount) continue
483
-
484
- const boneIdx = rb.boneIndex
485
-
486
- // Get bone world matrix
487
- const boneWorldMat = boneWorldMatrices[boneIdx]
488
-
489
- // Compute body world matrix: bodyWorld = boneWorld × bodyOffsetMatrix
490
- // (like babylon-mmd: bodyWorldMatrix = bodyOffsetMatrix.multiplyToRef(bodyWorldMatrix))
491
- const bodyOffsetMatrix = rb.bodyOffsetMatrix || rb.bodyOffsetMatrixInverse.inverse()
492
- const bodyWorldMatrix = boneWorldMat.multiply(bodyOffsetMatrix)
493
-
494
- const worldPos = bodyWorldMatrix.getPosition()
495
- const worldRot = bodyWorldMatrix.toQuat()
496
-
497
- // Set transform matrix
498
- const transform = new Ammo.btTransform()
499
- const pos = new Ammo.btVector3(worldPos.x, worldPos.y, worldPos.z)
500
- const quat = new Ammo.btQuaternion(worldRot.x, worldRot.y, worldRot.z, worldRot.w)
501
-
502
- transform.setOrigin(pos)
503
- transform.setRotation(quat)
504
-
505
- ammoBody.setWorldTransform(transform)
506
- ammoBody.getMotionState().setWorldTransform(transform)
507
-
508
- // Clear velocities for all rigidbodies
509
- if (!this.zeroVector) {
510
- this.zeroVector = new Ammo.btVector3(0, 0, 0)
511
- }
512
- ammoBody.setLinearVelocity(this.zeroVector)
513
- ammoBody.setAngularVelocity(this.zeroVector)
514
-
515
- // Explicitly activate dynamic rigidbodies after reset (wake them up)
516
- // This is critical for dress pieces and other dynamic bodies to prevent teleporting
517
- if (rb.type === RigidbodyType.Dynamic) {
518
- ammoBody.activate(true) // Wake up the body
519
- }
520
-
521
- Ammo.destroy(pos)
522
- Ammo.destroy(quat)
523
- }
524
-
525
- // Step simulation once to stabilize (like babylon-mmd)
526
- if (this.dynamicsWorld.stepSimulation) {
527
- this.dynamicsWorld.stepSimulation(0, 0, 0)
528
- }
529
- }
530
-
531
471
  // Syncs bones to rigidbodies, simulates dynamics, solves constraints
532
472
  // Modifies boneWorldMatrices in-place for dynamic rigidbodies that drive bones
533
473
  step(dt: number, boneWorldMatrices: Mat4[], boneInverseBindMatrices: Float32Array): void {