reze-engine 0.22.1 → 0.23.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.
package/dist/engine.d.ts CHANGED
@@ -309,6 +309,20 @@ export declare class Engine {
309
309
  private createRenderPipeline;
310
310
  private createPipelines;
311
311
  private setupResize;
312
+ /** When set, render resolution is pinned to this size instead of tracking the
313
+ * canvas's CSS size × devicePixelRatio (see setRenderSize). */
314
+ private fixedRenderSize;
315
+ /**
316
+ * Pin the render resolution (canvas backing store + every render target) to an
317
+ * explicit size, decoupled from the canvas's CSS layout size — for offline
318
+ * rendering at arbitrary resolution (video export). On screen the browser scales
319
+ * the buffer to the layout box, so the canvas may display letterboxed/stretched
320
+ * while pinned; hosts typically cover it with an export overlay. Pass null to
321
+ * return to CSS-size × devicePixelRatio tracking. Applies immediately (targets
322
+ * rebuild before this returns), so the next render() is at the new size.
323
+ */
324
+ setRenderSize(width: number, height: number): void;
325
+ setRenderSize(size: null): void;
312
326
  private handleResize;
313
327
  private setupGizmo;
314
328
  private setupCamera;
@@ -444,6 +458,16 @@ export declare class Engine {
444
458
  private renderPickPass;
445
459
  private resolvePickResult;
446
460
  render(): void;
461
+ /**
462
+ * Render one frame advancing every clock — animation, physics, tweens, and the
463
+ * camera VMD — by exactly `deltaSeconds`, independent of wall time. This is the
464
+ * offline-rendering primitive (video export): call it N times with 1/fps and the
465
+ * result is deterministic whether the machine renders faster or slower than
466
+ * realtime. Also resets the realtime clock so a later render() (returning to the
467
+ * live loop) doesn't see the export's wall-clock gap as one giant delta.
468
+ */
469
+ renderFrame(deltaSeconds: number): void;
470
+ private renderWithDelta;
447
471
  private drawInstanceShadow;
448
472
  /** Read a model's current style groups (including auto-created defaults) for editor
449
473
  * round-trip. The host owns group state; this is bootstrap/read, not a second store. */
@@ -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":"AACA,OAAO,EAAQ,IAAI,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAA;AACzC,OAAO,EAAE,KAAK,EAAiB,MAAM,SAAS,CAAA;AAO9C,OAAO,EAQL,KAAK,WAAW,EACjB,MAAM,gBAAgB,CAAA;AAgBvB,OAAO,EAAgB,KAAK,cAAc,EAAkB,MAAM,iBAAiB,CAAA;AAGnF,OAAO,KAAK,EACV,qBAAqB,EACrB,sBAAsB,EAEtB,UAAU,EACX,MAAM,qBAAqB,CAAA;AAa5B,MAAM,MAAM,cAAc,GACtB,SAAS,GACT,MAAM,GACN,MAAM,GACN,MAAM,GACN,KAAK,GACL,WAAW,GACX,OAAO,GACP,cAAc,GACd,aAAa,CAAA;AAEjB,MAAM,MAAM,iBAAiB,GAAG,OAAO,CAAC,MAAM,CAAC,cAAc,EAAE,MAAM,EAAE,CAAC,CAAC,CAAA;AAqHzE,MAAM,MAAM,eAAe,GAAG,CAC5B,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,GAAG,IAAI,EACvB,IAAI,EAAE,MAAM,GAAG,IAAI,EACnB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,KACZ,IAAI,CAAA;AAET,kHAAkH;AAClH,MAAM,MAAM,yBAAyB,GAAG;IACtC,KAAK,EAAE,QAAQ,GAAG,IAAI,EAAE,CAAA;IACxB,OAAO,CAAC,EAAE,IAAI,CAAA;CACf,CAAA;AAID,MAAM,MAAM,YAAY,GAAG;IACzB,8FAA8F;IAC9F,KAAK,CAAC,EAAE,IAAI,CAAA;IACZ,uEAAuE;IACvE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB,CAAA;AAED;qDACqD;AACrD,MAAM,MAAM,cAAc,GAAG;IAC3B,QAAQ,EAAE,IAAI,CAAA;IACd,QAAQ,EAAE,IAAI,CAAA;IACd,iCAAiC;IACjC,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,OAAO,CAAA;CACjB,CAAA;AAED,MAAM,MAAM,UAAU,GAAG;IACvB,6DAA6D;IAC7D,KAAK,CAAC,EAAE,IAAI,CAAA;IACZ,+DAA+D;IAC/D,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,yFAAyF;IACzF,SAAS,CAAC,EAAE,IAAI,CAAA;CACjB,CAAA;AAED,MAAM,MAAM,aAAa,GAAG;IAC1B,kCAAkC;IAClC,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,gCAAgC;IAChC,MAAM,CAAC,EAAE,IAAI,CAAA;IACb,yCAAyC;IACzC,GAAG,CAAC,EAAE,MAAM,CAAA;CACb,CAAA;AAED,wFAAwF;AACxF,MAAM,MAAM,YAAY,GAAG;IACzB,OAAO,EAAE,OAAO,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,IAAI,CAAA;IACX,SAAS,EAAE,MAAM,CAAA;IACjB,KAAK,EAAE,MAAM,CAAA;CACd,CAAA;AAED,eAAO,MAAM,qBAAqB,EAAE,YAQnC,CAAA;AAED,4HAA4H;AAC5H,MAAM,MAAM,oBAAoB,GAAG;IACjC,2DAA2D;IAC3D,QAAQ,EAAE,MAAM,CAAA;IAChB,yDAAyD;IACzD,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,SAAS,GAAG,sBAAsB,CAAA;CACzC,CAAA;AAID,eAAO,MAAM,sBAAsB,EAAE,oBAIpC,CAAA;AAED,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG,WAAW,CAAA;AAElD,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,IAAI,EAAE,aAAa,CAAA;IACnB,kGAAkG;IAClG,aAAa,EAAE,IAAI,CAAA;IACnB,gBAAgB,EAAE,IAAI,CAAA;IACtB,4EAA4E;IAC5E,KAAK,CAAC,EAAE,OAAO,GAAG,KAAK,CAAA;CACxB;AAED;;;;;;;;;;GAUG;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAAA;AAE/D,MAAM,MAAM,aAAa,GAAG;IAC1B,KAAK,CAAC,EAAE,YAAY,CAAA;IACpB,GAAG,CAAC,EAAE,UAAU,CAAA;IAChB,MAAM,CAAC,EAAE,aAAa,CAAA;IACtB,yEAAyE;IACzE,KAAK,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,CAAA;IAC7B,gFAAgF;IAChF,IAAI,CAAC,EAAE,OAAO,CAAC,oBAAoB,CAAC,CAAA;IACpC,SAAS,CAAC,EAAE,eAAe,CAAA;IAC3B,qCAAqC;IACrC,WAAW,CAAC,EAAE,iBAAiB,CAAA;CAChC,CAAA;AAED,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;CAKlC,CAAA;AAED,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAA;IACX,SAAS,EAAE,MAAM,CAAA;IACjB,YAAY,EAAE,MAAM,CAAA;IACpB,cAAc,EAAE,MAAM,CAAA;IACtB,MAAM,EAAE,MAAM,CAAA;CACf;AAkED,qBAAa,MAAM;IACjB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAsB;IAE7C,MAAM,CAAC,WAAW,IAAI,MAAM;IAO5B,OAAO,CAAC,MAAM,CAAmB;IACjC,OAAO,CAAC,MAAM,CAAY;IAC1B,OAAO,CAAC,OAAO,CAAmB;IAClC,OAAO,CAAC,kBAAkB,CAAmB;IAC7C,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,mBAAmB,CAAY;IACvC,OAAO,CAAC,gBAAgB,CAAuB;IAE/C,OAAO,CAAC,KAAK,CAAoC;IACjD,OAAO,CAAC,GAAG,CAAqD;IAChE,OAAO,CAAC,YAAY,CAAkD;IACtE,OAAO,CAAC,kBAAkB,CAAY;IACtC,OAAO,CAAC,SAAS,CAAuB;IACxC,OAAO,CAAC,UAAU,CAAI;IACtB,OAAO,CAAC,cAAc,CAA8B;IACpD,OAAO,CAAC,aAAa,CAAQ;IAC7B,OAAO,CAAC,YAAY,CAAa;IAGjC,OAAO,CAAC,eAAe,CAAoB;IAK3C,OAAO,CAAC,eAAe,CAAY;IAEnC,OAAO,CAAC,kBAAkB,CAAoB;IAC9C,OAAO,CAAC,YAAY,CAAwB;IAC5C,OAAO,CAAC,uBAAuB,CAA0B;IAGzD,OAAO,CAAC,sBAAsB,CAAa;IAC3C,OAAO,CAAC,oBAAoB,CAAoB;IAChD,OAAO,CAAC,2BAA2B,CAAqB;IACxD,OAAO,CAAC,eAAe,CAAoB;IAC3C,OAAO,CAAC,gBAAgB,CAA2D;IACnF,OAAO,CAAC,oBAAoB,CAAC,CAAY;IACzC,OAAO,CAAC,iBAAiB,CAAC,CAAgB;IAC1C,OAAO,CAAC,qBAAqB,CAAoB;IACjD,OAAO,CAAC,2BAA2B,CAA0B;IAC7D,OAAO,CAAC,qBAAqB,CAAoB;IACjD,OAAO,CAAC,4BAA4B,CAAqB;IACzD,OAAO,CAAC,sBAAsB,CAAC,CAAc;IAC7C,OAAO,CAAC,0BAA0B,CAAY;IAC9C,OAAO,CAAC,2BAA2B,CAA0B;IAC7D,OAAO,CAAC,gBAAgB,CAAa;IAGrC,OAAO,CAAC,YAAY,CAA0E;IAC9F,OAAO,CAAC,iBAAiB,CAAY;IACrC,OAAO,CAAC,oBAAoB,CAAY;IACxC,OAAO,CAAC,aAAa,CAAoB;IACzC,OAAO,CAAC,eAAe,CAAe;IACtC,OAAO,CAAC,oBAAoB,CAAqB;IACjD,OAAO,CAAC,mBAAmB,CAA0B;IACrD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,mBAAmB,CAAK;IAChD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,iBAAiB,CAAM;IAG/C,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,iBAAiB,CAAO;IAIhD,OAAO,CAAC,UAAU,CAAoD;IACtE,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,gBAAgB,CAAM;IAC9C,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,kBAAkB,CAAO;IACjD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,uBAAuB,CAAO;IAOtD,OAAO,CAAC,SAAS,CAcF;IACf,OAAO,CAAC,2BAA2B,CAAqB;IACxD,OAAO,CAAC,8BAA8B,CAAqB;IAC3D,OAAO,CAAC,8BAA8B,CAAqB;IAC3D,OAAO,CAAC,8BAA8B,CAAqB;IAC3D,OAAO,CAAC,iCAAiC,CAAqB;IAC9D,OAAO,CAAC,iBAAiB,CAAe;IACxC,OAAO,CAAC,wBAAwB,CAAe;IAC/C,OAAO,CAAC,kBAAkB,CAAa;IACvC,OAAO,CAAC,iBAAiB,CAAa;IACtC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,iBAAiB,CAAI;IAiB7C,OAAO,CAAC,SAAS,CAAkC;IACnD;0FACsF;IACtF,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,iBAAiB,CAAI;IAC7C;;;;;;;gGAO4F;IAC5F,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,iBAAiB,CAA+B;IACxE,OAAO,CAAC,sBAAsB,CAAa;IAC3C,OAAO,CAAC,kBAAkB,CAAa;IACvC,OAAO,CAAC,eAAe,CAAiB;IACxC,OAAO,CAAC,oBAAoB,CAA0B;IACtD,OAAO,CAAC,uBAAuB,CAA0B;IAIzD,OAAO,CAAC,yBAAyB,CAAoB;IACrD,OAAO,CAAC,sBAAsB,CAAoB;IAClD,OAAO,CAAC,oBAAoB,CAAqB;IACjD,OAAO,CAAC,2BAA2B,CAAqB;IACxD,OAAO,CAAC,wBAAwB,CAAqB;IACrD,OAAO,CAAC,kBAAkB,CAAe;IACzC,OAAO,CAAC,sBAAsB,CAAY;IAE1C,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAAsB;IAQ3D,OAAO,CAAC,YAAY,CAAa;IACjC,OAAO,CAAC,sBAAsB,CAAY;IAC1C,OAAO,CAAC,0BAA0B,CAAY;IAC9C,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAAsB;IAC3D,OAAO,CAAC,QAAQ,CAAC,wBAAwB,CAAsB;IAC/D,OAAO,CAAC,iBAAiB,CAAoB;IAC7C,OAAO,CAAC,uBAAuB,CAAoB;IACnD,OAAO,CAAC,qBAAqB,CAAoB;IACjD,OAAO,CAAC,wBAAwB,CAAqB;IACrD,OAAO,CAAC,8BAA8B,CAAqB;IAC3D,OAAO,CAAC,4BAA4B,CAAqB;IACzD,OAAO,CAAC,gBAAgB,CAAa;IACrC,OAAO,CAAC,cAAc,CAAa;IACnC,OAAO,CAAC,aAAa,CAAI;IACzB,OAAO,CAAC,iBAAiB,CAAuB;IAChD,OAAO,CAAC,eAAe,CAAuB;IAC9C,OAAO,CAAC,kBAAkB,CAAe;IACzC,OAAO,CAAC,yBAAyB,CAAqB;IACtD,OAAO,CAAC,uBAAuB,CAAqB;IACpD,2EAA2E;IAC3E,OAAO,CAAC,mBAAmB,CAA0B;IACrD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,gBAAgB,CAAI;IAG5C,OAAO,CAAC,kBAAkB,CAAC,CAAW;IACtC,OAAO,CAAC,iBAAiB,CAAC,CAAW;IACrC,OAAO,CAAC,SAAS,CAAQ;IACzB,OAAO,CAAC,gBAAgB,CAAa;IACrC,OAAO,CAAC,kBAAkB,CAAiB;IAC3C,OAAO,CAAC,cAAc,CAAa;IACnC,OAAO,CAAC,WAAW,CAAiB;IACpC,OAAO,CAAC,gBAAgB,CAAa;IACrC,OAAO,CAAC,aAAa,CAAiB;IAEtC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,gBAAgB,CAAM;IAC9C,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,eAAe,CAAO;IAC9C,OAAO,CAAC,mBAAmB,CAAoB;IAC/C,OAAO,CAAC,mBAAmB,CAAY;IACvC,OAAO,CAAC,mBAAmB,CAAuB;IAClD,OAAO,CAAC,qBAAqB,CAAC,CAAc;IAC5C,OAAO,CAAC,uBAAuB,CAAa;IAC5C,OAAO,CAAC,0BAA0B,CAAC,CAAW;IAC9C,OAAO,CAAC,cAAc,CAAwB;IAE9C,OAAO,CAAC,SAAS,CAAC,CAAiB;IACnC,OAAO,CAAC,WAAW,CAAC,CAAmB;IACvC,OAAO,CAAC,aAAa,CAAI;IACzB,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAM;IAEvC,OAAO,CAAC,YAAY,CAAoB;IACxC,OAAO,CAAC,2BAA2B,CAAqB;IACxD,OAAO,CAAC,8BAA8B,CAAqB;IAC3D,OAAO,CAAC,8BAA8B,CAAqB;IAC3D,OAAO,CAAC,qBAAqB,CAAe;IAC5C,OAAO,CAAC,WAAW,CAAa;IAChC,OAAO,CAAC,gBAAgB,CAAa;IACrC,OAAO,CAAC,kBAAkB,CAAY;IACtC,OAAO,CAAC,WAAW,CAAwC;IAE3D,OAAO,CAAC,cAAc,CAAmC;IACzD,OAAO,CAAC,eAAe,CAAa;IACpC,OAAO,CAAC,uBAAuB,CAAa;IAC5C,OAAO,CAAC,YAAY,CAAgC;IACpD,OAAO,CAAC,eAAe,CAAiC;IACxD,OAAO,CAAC,cAAc,CAA0B;IAChD,OAAO,CAAC,mBAAmB,CAAI;IAG/B,OAAO,CAAC,SAAS,CAAO;IACxB,OAAO,CAAC,cAAc,CAAO;IAE7B,OAAO,CAAC,YAAY,CAAO;IAI3B,OAAO,CAAC,eAAe,CAA+B;IAGtD,OAAO,CAAC,iBAAiB,CAAqB;IAC9C,OAAO,CAAC,oBAAoB,CAAS;IACrC,OAAO,CAAC,kBAAkB,CAA0B;IAEpD,OAAO,CAAC,aAAa,CAAoB;IAGzC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,YAAY,CAAM;IAC1C,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,gBAAgB,CAAM;IAC9C,OAAO,CAAC,cAAc,CAAwC;IAC9D,OAAO,CAAC,kBAAkB,CAAI;IAC9B,OAAO,CAAC,mBAAmB,CAAI;IAC/B,OAAO,CAAC,gBAAgB,CAAoB;IAC5C,OAAO,CAAC,KAAK,CAMZ;IACD,OAAO,CAAC,gBAAgB,CAAsB;IAC9C,OAAO,CAAC,kBAAkB,CAA4B;IACtD,OAAO,CAAC,aAAa,CAAe;IACpC,OAAO,CAAC,aAAa,CAAuB;gBAEhC,MAAM,EAAE,iBAAiB,EAAE,OAAO,CAAC,EAAE,aAAa;IAuB9D,qEAAqE;IACrE,MAAM,CAAC,kBAAkB,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,GAAG,YAAY;IAcxE,MAAM,CAAC,0BAA0B,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC,oBAAoB,CAAC,GAAG,oBAAoB;IAShG,uEAAuE;IACvE,eAAe,IAAI,YAAY;IAa/B,uBAAuB,IAAI,oBAAoB;IAK/C,uBAAuB,CAAC,KAAK,EAAE,OAAO,CAAC,oBAAoB,CAAC,GAAG,IAAI;IAUnE,OAAO,CAAC,0BAA0B;IAmBlC,wEAAwE;IACxE,eAAe,CAAC,KAAK,EAAE,OAAO,CAAC,YAAY,CAAC,GAAG,IAAI;IAoBnD,OAAO,CAAC,kBAAkB;IAsBpB,IAAI;IAyCV,OAAO,CAAC,WAAW;IAiFnB,OAAO,CAAC,aAAa;IA+ErB,OAAO,CAAC,oBAAoB;IAiC5B,OAAO,CAAC,eAAe;IAqpBvB,OAAO,CAAC,WAAW;IAsBnB;oEACgE;IAChE,OAAO,CAAC,eAAe,CAAiD;IAExE;;;;;;;;OAQG;IACH,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IAClD,aAAa,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI;IAU/B,OAAO,CAAC,YAAY;IAiQpB,OAAO,CAAC,UAAU;IAgKlB,OAAO,CAAC,WAAW;IAmBnB,iFAAiF;IACjF,eAAe,CAAC,CAAC,EAAE,IAAI,GAAG,IAAI;IAC9B,gGAAgG;IAChG,eAAe,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,IAAI,GAAG,IAAI;IAoB3E,mIAAmI;IACnI,eAAe,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,EAAE,QAAQ,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,IAAI,GAAG,IAAI;IAgB5E;6FACyF;IACnF,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAM/C,uFAAuF;IACvF,uBAAuB,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI;IAMlD,8FAA8F;IAC9F,mBAAmB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;IAI3C,qEAAqE;IACrE,kBAAkB,IAAI,OAAO;IAI7B,+EAA+E;IAC/E,YAAY,IAAI,OAAO;IAIvB,8DAA8D;IAC9D,cAAc,IAAI,IAAI;IAQtB,OAAO,CAAC,eAAe;IAUvB,iBAAiB,IAAI,MAAM;IAG3B,iBAAiB,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI;IAGlC,cAAc,IAAI,MAAM;IAGxB,cAAc,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI;IAG/B,aAAa,IAAI,MAAM;IAGvB,aAAa,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI;IAK9B,OAAO,CAAC,aAAa;IAYrB;;;;OAIG;IACH,OAAO,CAAC,UAAU;IASlB,qFAAqF;IACrF,OAAO,CAAC,QAAQ;IAgBhB,gGAAgG;IAChG,QAAQ,CAAC,OAAO,EAAE,YAAY,GAAG,IAAI;IAMrC,0FAA0F;IAC1F,MAAM,CAAC,OAAO,EAAE,UAAU,GAAG,IAAI;IAUjC,QAAQ,IAAI,QAAQ,CAAC;QAAE,KAAK,EAAE,IAAI,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;IAGvD,MAAM,IAAI,QAAQ,CAAC;QAAE,KAAK,EAAE,IAAI,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,IAAI,CAAA;KAAE,CAAC;IAItE,SAAS,CAAC,OAAO,CAAC,EAAE;QAClB,KAAK,CAAC,EAAE,MAAM,CAAA;QACd,MAAM,CAAC,EAAE,MAAM,CAAA;QACf,YAAY,CAAC,EAAE,IAAI,CAAA;QACnB,SAAS,CAAC,EAAE,MAAM,CAAA;QAClB,OAAO,CAAC,EAAE,MAAM,CAAA;QAChB,cAAc,CAAC,EAAE,MAAM,CAAA;QACvB,WAAW,CAAC,EAAE,MAAM,CAAA;QACpB,aAAa,CAAC,EAAE,MAAM,CAAA;QACtB,eAAe,CAAC,EAAE,MAAM,CAAA;QACxB,aAAa,CAAC,EAAE,IAAI,CAAA;QACpB,aAAa,CAAC,EAAE,MAAM,CAAA;KACvB,GAAG,IAAI;IA4BR,OAAO,CAAC,iBAAiB;IAIzB,QAAQ,IAAI,WAAW;IAIvB,aAAa,CAAC,QAAQ,CAAC,EAAE,MAAM,IAAI;IAgBnC,cAAc;IAQd,OAAO;IAgCD,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC;IACvC,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC;IACrD,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,yBAAyB,GAAG,OAAO,CAAC,KAAK,CAAC;IAyB3E,QAAQ,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC;IAcxG,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAmB/B,aAAa,IAAI,MAAM,EAAE;IAIzB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,KAAK,GAAG,IAAI;IAIpC;;;;;;OAMG;IACH,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,CAAC,cAAc,CAAC,GAAG,IAAI;IASzE,kFAAkF;IAClF,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI;IAYtD,qBAAqB,CAAC,gBAAgB,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,IAAI;IAe9D,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,EAAE,YAAY,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IAIhF,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IAgBxE,OAAO,CAAC,uBAAuB;IAQ/B,kBAAkB,CAAC,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI;IAOnF,qBAAqB,CAAC,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,IAAI;IAOpE,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO;IAKnE,YAAY,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;IAKpC,mBAAmB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;IAI3C,YAAY,IAAI,OAAO;IAIvB,iBAAiB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;IAIzC,iBAAiB,IAAI,OAAO;IAI5B,YAAY,IAAI,IAAI;IAWpB,OAAO,CAAC,eAAe;IAIvB,OAAO,CAAC,eAAe;IA2BvB,OAAO,CAAC,kBAAkB;IA+B1B,OAAO,CAAC,oBAAoB;YAgBd,kBAAkB;IA0HhC,OAAO,CAAC,cAAc;IAoEtB,OAAO,CAAC,oBAAoB;IAwE5B,OAAO,CAAC,2BAA2B;IA4DnC,OAAO,CAAC,kBAAkB,CAAO;IACjC,OAAO,CAAC,mBAAmB;YAeb,yBAAyB;IAuIvC,OAAO,CAAC,2BAA2B;IAyBnC,OAAO,CAAC,mBAAmB;IAU3B,OAAO,CAAC,oBAAoB;YAId,4BAA4B;IA+E1C,OAAO,CAAC,eAAe;IA6CvB,OAAO,CAAC,YAAY;IASpB,OAAO,CAAC,uBAAuB,CAI9B;IAED,OAAO,CAAC,iBAAiB,CA0BxB;IAED,OAAO,CAAC,cAAc;IAStB,OAAO,CAAC,qBAAqB;IAsC7B,OAAO,CAAC,eAAe;IA8DvB,OAAO,CAAC,gBAAgB;IAYxB,OAAO,CAAC,SAAS;IAWjB,OAAO,CAAC,aAAa;IAkBrB,OAAO,CAAC,YAAY;IAqDpB,OAAO,CAAC,cAAc;IA0BtB,OAAO,CAAC,sBAAsB;IAW9B,OAAO,CAAC,QAAQ;IAWhB,OAAO,CAAC,gBAAgB;IAKxB,OAAO,CAAC,oBAAoB,CAoG3B;IAED,OAAO,CAAC,oBAAoB,CA+C3B;IAED,OAAO,CAAC,kBAAkB,CAsBzB;IAED,OAAO,CAAC,cAAc;YAgDR,iBAAiB;IAkD/B,MAAM;IASN;;;;;;;OAOG;IACH,WAAW,CAAC,YAAY,EAAE,MAAM;IAMhC,OAAO,CAAC,eAAe;IAsIvB,OAAO,CAAC,kBAAkB;IAiB1B;6FACyF;IACzF,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,UAAU,EAAE;IAM/C;;;;;;;OAOG;IACG,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,sBAAsB,CAAC;IA8BxG;;;;;OAKG;IACG,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,GAAG,OAAO,CAAC,sBAAsB,CAAC;IA0ChG;iDAC6C;IACvC,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,IAAI,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,qBAAqB,CAAC;IASnH,oFAAoF;IACpF,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI;IAU1D,0FAA0F;IAC1F,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAWzC,oFAAoF;IACpF,aAAa,CACX,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,GACvC,OAAO;IAiBV,OAAO,CAAC,aAAa;YASP,sBAAsB;IA2EpC,OAAO,CAAC,oBAAoB;IAmB5B,OAAO,CAAC,kBAAkB;IAiB1B,OAAO,CAAC,YAAY;IAKpB,OAAO,CAAC,aAAa;IAcrB;;;;OAIG;IACH,OAAO,CAAC,yBAAyB;IA2DjC,OAAO,CAAC,mBAAmB;IAQ3B;;;;;OAKG;IACH,OAAO,CAAC,aAAa;IAoBrB;;;;;OAKG;IACH,OAAO,CAAC,YAAY;IAepB;;;;;OAKG;IACH,OAAO,CAAC,cAAc;IAiBtB;;;;;OAKG;IACH,OAAO,CAAC,gBAAgB;IAuBxB,OAAO,CAAC,mBAAmB;IAM3B,OAAO,CAAC,oBAAoB;IAY5B,OAAO,CAAC,kBAAkB;IAgB1B,OAAO,CAAC,WAAW;CA0CpB"}
package/dist/engine.js CHANGED
@@ -233,6 +233,9 @@ export class Engine {
233
233
  };
234
234
  this.animationFrameId = null;
235
235
  this.renderLoopCallback = null;
236
+ /** When set, render resolution is pinned to this size instead of tracking the
237
+ * canvas's CSS size × devicePixelRatio (see setRenderSize). */
238
+ this.fixedRenderSize = null;
236
239
  // Shadow is cast from the visible sun direction — same vector the shader lights with.
237
240
  this.shadowLightVPDirty = true;
238
241
  this.handleCanvasDoubleClick = (event) => {
@@ -1405,12 +1408,19 @@ export class Engine {
1405
1408
  window.addEventListener("mousemove", this.handleGizmoMouseMove);
1406
1409
  window.addEventListener("mouseup", this.handleGizmoMouseUp);
1407
1410
  }
1411
+ setRenderSize(widthOrNull, height) {
1412
+ this.fixedRenderSize =
1413
+ widthOrNull === null
1414
+ ? null
1415
+ : { width: Math.max(1, Math.floor(widthOrNull)), height: Math.max(1, Math.floor(height ?? 1)) };
1416
+ this.resizePending = false;
1417
+ this.handleResize();
1418
+ }
1408
1419
  handleResize() {
1409
- const displayWidth = this.canvas.clientWidth;
1410
- const displayHeight = this.canvas.clientHeight;
1420
+ // Fixed override (offline/video rendering) wins; otherwise track CSS size × dpr.
1411
1421
  const dpr = window.devicePixelRatio || 1;
1412
- const width = Math.floor(displayWidth * dpr);
1413
- const height = Math.floor(displayHeight * dpr);
1422
+ const width = this.fixedRenderSize ? this.fixedRenderSize.width : Math.floor(this.canvas.clientWidth * dpr);
1423
+ const height = this.fixedRenderSize ? this.fixedRenderSize.height : Math.floor(this.canvas.clientHeight * dpr);
1414
1424
  if (!this.multisampleTexture || this.canvas.width !== width || this.canvas.height !== height) {
1415
1425
  this.canvas.width = width;
1416
1426
  this.canvas.height = height;
@@ -3173,13 +3183,30 @@ export class Engine {
3173
3183
  render() {
3174
3184
  if (!this.multisampleTexture || !this.camera || !this.device)
3175
3185
  return;
3186
+ const currentTime = performance.now();
3187
+ const deltaTime = this.lastFrameTime > 0 ? (currentTime - this.lastFrameTime) / 1000 : 0.016;
3188
+ this.lastFrameTime = currentTime;
3189
+ this.renderWithDelta(deltaTime);
3190
+ }
3191
+ /**
3192
+ * Render one frame advancing every clock — animation, physics, tweens, and the
3193
+ * camera VMD — by exactly `deltaSeconds`, independent of wall time. This is the
3194
+ * offline-rendering primitive (video export): call it N times with 1/fps and the
3195
+ * result is deterministic whether the machine renders faster or slower than
3196
+ * realtime. Also resets the realtime clock so a later render() (returning to the
3197
+ * live loop) doesn't see the export's wall-clock gap as one giant delta.
3198
+ */
3199
+ renderFrame(deltaSeconds) {
3200
+ if (!this.multisampleTexture || !this.camera || !this.device)
3201
+ return;
3202
+ this.lastFrameTime = performance.now();
3203
+ this.renderWithDelta(deltaSeconds);
3204
+ }
3205
+ renderWithDelta(deltaTime) {
3176
3206
  if (this.resizePending) {
3177
3207
  this.resizePending = false;
3178
3208
  this.handleResize();
3179
3209
  }
3180
- const currentTime = performance.now();
3181
- const deltaTime = this.lastFrameTime > 0 ? (currentTime - this.lastFrameTime) / 1000 : 0.016;
3182
- this.lastFrameTime = currentTime;
3183
3210
  const hasModels = this.modelInstances.size > 0;
3184
3211
  if (hasModels) {
3185
3212
  this.updateInstances(deltaTime);
@@ -0,0 +1,30 @@
1
+ import type { RezePhysics } from "./physics";
2
+ export declare class PhysicsDebugRenderer {
3
+ private device;
4
+ private bindGroup;
5
+ private wirePipelineSphere;
6
+ private wirePipelineBox;
7
+ private wirePipelineCapsule;
8
+ private wireSphereBuffer;
9
+ private wireBoxBuffer;
10
+ private wireCapsuleBuffer;
11
+ private wireSphereCount;
12
+ private wireBoxCount;
13
+ private wireCapsuleCount;
14
+ private solidPipelineSphere;
15
+ private solidPipelineBox;
16
+ private solidPipelineCapsule;
17
+ private solidSphereBuffer;
18
+ private solidBoxBuffer;
19
+ private solidCapsuleBuffer;
20
+ private solidSphereCount;
21
+ private solidBoxCount;
22
+ private solidCapsuleCount;
23
+ private instanceBuffer;
24
+ private instanceData;
25
+ private instanceCapacity;
26
+ constructor(device: GPUDevice, cameraUniformBuffer: GPUBuffer, presentationFormat: GPUTextureFormat);
27
+ render(pass: GPURenderPassEncoder, physics: RezePhysics): void;
28
+ destroy(): void;
29
+ }
30
+ //# sourceMappingURL=physics-debug.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"physics-debug.d.ts","sourceRoot":"","sources":["../src/physics-debug.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA;AAmB5C,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,MAAM,CAAW;IACzB,OAAO,CAAC,SAAS,CAAc;IAG/B,OAAO,CAAC,kBAAkB,CAAmB;IAC7C,OAAO,CAAC,eAAe,CAAmB;IAC1C,OAAO,CAAC,mBAAmB,CAAmB;IAC9C,OAAO,CAAC,gBAAgB,CAAW;IACnC,OAAO,CAAC,aAAa,CAAW;IAChC,OAAO,CAAC,iBAAiB,CAAW;IACpC,OAAO,CAAC,eAAe,CAAQ;IAC/B,OAAO,CAAC,YAAY,CAAQ;IAC5B,OAAO,CAAC,gBAAgB,CAAQ;IAIhC,OAAO,CAAC,mBAAmB,CAAmB;IAC9C,OAAO,CAAC,gBAAgB,CAAmB;IAC3C,OAAO,CAAC,oBAAoB,CAAmB;IAC/C,OAAO,CAAC,iBAAiB,CAAW;IACpC,OAAO,CAAC,cAAc,CAAW;IACjC,OAAO,CAAC,kBAAkB,CAAW;IACrC,OAAO,CAAC,gBAAgB,CAAQ;IAChC,OAAO,CAAC,aAAa,CAAQ;IAC7B,OAAO,CAAC,iBAAiB,CAAQ;IAEjC,OAAO,CAAC,cAAc,CAAW;IACjC,OAAO,CAAC,YAAY,CAAc;IAClC,OAAO,CAAC,gBAAgB,CAAQ;gBAG9B,MAAM,EAAE,SAAS,EACjB,mBAAmB,EAAE,SAAS,EAC9B,kBAAkB,EAAE,gBAAgB;IA2HtC,MAAM,CAAC,IAAI,EAAE,oBAAoB,EAAE,OAAO,EAAE,WAAW,GAAG,IAAI;IAmI9D,OAAO,IAAI,IAAI;CAShB"}
@@ -0,0 +1,526 @@
1
+ // Debug overlay drawing every rigidbody as a wireframe + semitransparent solid
2
+ // primitive (sphere / box / capsule), color-coded by type:
3
+ // yellow = static (FollowBone)
4
+ // cyan = kinematic
5
+ // red = dynamic
6
+ //
7
+ // Rendered in its own swapchain pass AFTER composite (see Engine.renderPhysicsDebug),
8
+ // so it sits cleanly on top of the final tonemapped image. No depth, no MSAA,
9
+ // no MRT, no interaction with the HDR alpha gate — bodies are always fully
10
+ // visible regardless of camera angle, model occlusion, or stacking. Reads body
11
+ // transforms straight from RezePhysics' SoA store — zero per-frame allocations.
12
+ import { Mat4 } from "./math";
13
+ import { RigidbodyShape, RigidbodyType } from "./physics";
14
+ import { PHYSICS_DEBUG_SHADER_WGSL } from "./shaders/passes/physics-debug";
15
+ const STRIDE_BYTES = 96; // 4 mat4 cols + size+pad + color = 6 vec4
16
+ const STRIDE_FLOATS = STRIDE_BYTES / 4;
17
+ // Per-instance color.alpha is the WIREFRAME alpha (1.0 for crisp edges). Solid
18
+ // pipelines override the SOLID_ALPHA shader constant to ~0.20 so the fill stays
19
+ // gentle where many bodies overlap.
20
+ // Hues chosen so wire+solid both read against the typical pink/grey reze studio
21
+ // scene background. Red [1, 0.3, 0.3] sat too close to the pink bg's hue —
22
+ // solid fill (α≈0.2) blended into the bg and the wire didn't separate from it.
23
+ // Orange-red shifts ~30° on the hue wheel, giving real chroma against pink.
24
+ const COLOR_STATIC = [1.0, 0.85, 0.15, 1.0]; // yellow
25
+ const COLOR_KINEMATIC = [0.25, 0.7, 1.0, 1.0]; // cyan-blue
26
+ const COLOR_DYNAMIC = [1.0, 0.35, 0.0, 1.0]; // orange-red
27
+ const SOLID_ALPHA = 0.2;
28
+ export class PhysicsDebugRenderer {
29
+ constructor(device, cameraUniformBuffer, presentationFormat) {
30
+ this.device = device;
31
+ const wireSphere = buildSphereWireGeometry();
32
+ const wireBox = buildBoxWireGeometry();
33
+ const wireCapsule = buildCapsuleWireGeometry();
34
+ this.wireSphereCount = wireSphere.length / 4;
35
+ this.wireBoxCount = wireBox.length / 4;
36
+ this.wireCapsuleCount = wireCapsule.length / 4;
37
+ const solidSphere = buildSphereSolidGeometry();
38
+ const solidBox = buildBoxSolidGeometry();
39
+ const solidCapsule = buildCapsuleSolidGeometry();
40
+ this.solidSphereCount = solidSphere.length / 4;
41
+ this.solidBoxCount = solidBox.length / 4;
42
+ this.solidCapsuleCount = solidCapsule.length / 4;
43
+ const upload = (label, data) => {
44
+ const buf = device.createBuffer({
45
+ label,
46
+ size: data.byteLength,
47
+ usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
48
+ });
49
+ device.queue.writeBuffer(buf, 0, data.buffer, data.byteOffset, data.byteLength);
50
+ return buf;
51
+ };
52
+ this.wireSphereBuffer = upload("physics-debug wire sphere", wireSphere);
53
+ this.wireBoxBuffer = upload("physics-debug wire box", wireBox);
54
+ this.wireCapsuleBuffer = upload("physics-debug wire capsule", wireCapsule);
55
+ this.solidSphereBuffer = upload("physics-debug solid sphere", solidSphere);
56
+ this.solidBoxBuffer = upload("physics-debug solid box", solidBox);
57
+ this.solidCapsuleBuffer = upload("physics-debug solid capsule", solidCapsule);
58
+ this.instanceCapacity = 512;
59
+ this.instanceData = new Float32Array(this.instanceCapacity * STRIDE_FLOATS);
60
+ this.instanceBuffer = device.createBuffer({
61
+ label: "physics-debug instances",
62
+ size: this.instanceCapacity * STRIDE_BYTES,
63
+ usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
64
+ });
65
+ const bgl = device.createBindGroupLayout({
66
+ label: "physics-debug bgl",
67
+ entries: [{ binding: 0, visibility: GPUShaderStage.VERTEX, buffer: { type: "uniform" } }],
68
+ });
69
+ this.bindGroup = device.createBindGroup({
70
+ label: "physics-debug bg",
71
+ layout: bgl,
72
+ entries: [{ binding: 0, resource: { buffer: cameraUniformBuffer } }],
73
+ });
74
+ const pipelineLayout = device.createPipelineLayout({ bindGroupLayouts: [bgl] });
75
+ const shader = device.createShaderModule({
76
+ label: "physics-debug shader",
77
+ code: PHYSICS_DEBUG_SHADER_WGSL,
78
+ });
79
+ const vertexBuffers = [
80
+ {
81
+ arrayStride: 16,
82
+ attributes: [{ shaderLocation: 0, offset: 0, format: "float32x4" }],
83
+ },
84
+ {
85
+ arrayStride: STRIDE_BYTES,
86
+ stepMode: "instance",
87
+ attributes: [
88
+ { shaderLocation: 1, offset: 0, format: "float32x4" },
89
+ { shaderLocation: 2, offset: 16, format: "float32x4" },
90
+ { shaderLocation: 3, offset: 32, format: "float32x4" },
91
+ { shaderLocation: 4, offset: 48, format: "float32x4" },
92
+ { shaderLocation: 5, offset: 64, format: "float32x4" },
93
+ { shaderLocation: 6, offset: 80, format: "float32x4" },
94
+ ],
95
+ },
96
+ ];
97
+ const targets = [
98
+ {
99
+ format: presentationFormat,
100
+ blend: {
101
+ color: { srcFactor: "src-alpha", dstFactor: "one-minus-src-alpha", operation: "add" },
102
+ alpha: { srcFactor: "one", dstFactor: "one-minus-src-alpha", operation: "add" },
103
+ },
104
+ },
105
+ ];
106
+ const buildPipeline = (label, kind, topology, solidAlpha) => device.createRenderPipeline({
107
+ label,
108
+ layout: pipelineLayout,
109
+ vertex: {
110
+ module: shader,
111
+ entryPoint: "vsMain",
112
+ buffers: vertexBuffers,
113
+ constants: { SHAPE_KIND: kind },
114
+ },
115
+ fragment: {
116
+ module: shader,
117
+ entryPoint: "fsMain",
118
+ targets,
119
+ constants: { SOLID_ALPHA: solidAlpha },
120
+ },
121
+ primitive: { topology, cullMode: "none" },
122
+ // No depth attachment, single multisample — pass renders straight to
123
+ // the swapchain after composite, so the overlay sits on top of the
124
+ // tonemapped scene without interacting with depth/stencil/MSAA.
125
+ multisample: { count: 1 },
126
+ });
127
+ this.wirePipelineSphere = buildPipeline("physics-debug wire sphere", 0, "line-list", 1.0);
128
+ this.wirePipelineBox = buildPipeline("physics-debug wire box", 1, "line-list", 1.0);
129
+ this.wirePipelineCapsule = buildPipeline("physics-debug wire capsule", 2, "line-list", 1.0);
130
+ this.solidPipelineSphere = buildPipeline("physics-debug solid sphere", 0, "triangle-list", SOLID_ALPHA);
131
+ this.solidPipelineBox = buildPipeline("physics-debug solid box", 1, "triangle-list", SOLID_ALPHA);
132
+ this.solidPipelineCapsule = buildPipeline("physics-debug solid capsule", 2, "triangle-list", SOLID_ALPHA);
133
+ }
134
+ render(pass, physics) {
135
+ const rigidbodies = physics.getRigidbodies();
136
+ const N = rigidbodies.length;
137
+ if (N === 0)
138
+ return;
139
+ const store = physics.getStore();
140
+ const positions = store.positions;
141
+ const orientations = store.orientations;
142
+ if (N > this.instanceCapacity) {
143
+ this.instanceCapacity = Math.max(N, this.instanceCapacity * 2);
144
+ this.instanceData = new Float32Array(this.instanceCapacity * STRIDE_FLOATS);
145
+ this.instanceBuffer.destroy();
146
+ this.instanceBuffer = this.device.createBuffer({
147
+ label: "physics-debug instances",
148
+ size: this.instanceCapacity * STRIDE_BYTES,
149
+ usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
150
+ });
151
+ }
152
+ // Two passes: count per-shape, then fill packed by shape so we can issue
153
+ // 3 instanced draws with vertex-buffer offsets.
154
+ let sphereCount = 0;
155
+ let boxCount = 0;
156
+ let capsuleCount = 0;
157
+ for (let i = 0; i < N; i++) {
158
+ const s = rigidbodies[i].shape;
159
+ if (s === RigidbodyShape.Sphere)
160
+ sphereCount++;
161
+ else if (s === RigidbodyShape.Box)
162
+ boxCount++;
163
+ else
164
+ capsuleCount++;
165
+ }
166
+ const sphereOffset = 0;
167
+ const boxOffset = sphereCount;
168
+ const capsuleOffset = sphereCount + boxCount;
169
+ const data = this.instanceData;
170
+ let sIdx = sphereOffset;
171
+ let bIdx = boxOffset;
172
+ let cIdx = capsuleOffset;
173
+ for (let i = 0; i < N; i++) {
174
+ const rb = rigidbodies[i];
175
+ const i3 = i * 3;
176
+ const i4 = i * 4;
177
+ let slot;
178
+ if (rb.shape === RigidbodyShape.Sphere)
179
+ slot = sIdx++;
180
+ else if (rb.shape === RigidbodyShape.Box)
181
+ slot = bIdx++;
182
+ else
183
+ slot = cIdx++;
184
+ const dst = slot * STRIDE_FLOATS;
185
+ // Model matrix: rotation from quat into [dst..dst+15], then translation.
186
+ Mat4.fromQuatInto(orientations[i4 + 0], orientations[i4 + 1], orientations[i4 + 2], orientations[i4 + 3], data, dst);
187
+ data[dst + 12] = positions[i3 + 0];
188
+ data[dst + 13] = positions[i3 + 1];
189
+ data[dst + 14] = positions[i3 + 2];
190
+ // size + pad
191
+ data[dst + 16] = rb.size.x;
192
+ data[dst + 17] = rb.size.y;
193
+ data[dst + 18] = rb.size.z;
194
+ data[dst + 19] = 0;
195
+ const c = rb.type === RigidbodyType.Static
196
+ ? COLOR_STATIC
197
+ : rb.type === RigidbodyType.Kinematic
198
+ ? COLOR_KINEMATIC
199
+ : COLOR_DYNAMIC;
200
+ data[dst + 20] = c[0];
201
+ data[dst + 21] = c[1];
202
+ data[dst + 22] = c[2];
203
+ data[dst + 23] = c[3];
204
+ }
205
+ this.device.queue.writeBuffer(this.instanceBuffer, 0, data.buffer, data.byteOffset, N * STRIDE_BYTES);
206
+ pass.setBindGroup(0, this.bindGroup);
207
+ // Solid fills first (gentle ~20% alpha for shape ID), wireframe edges on top
208
+ // (95% alpha for crisp silhouette).
209
+ if (sphereCount > 0) {
210
+ const off = sphereOffset * STRIDE_BYTES;
211
+ const size = sphereCount * STRIDE_BYTES;
212
+ pass.setPipeline(this.solidPipelineSphere);
213
+ pass.setVertexBuffer(0, this.solidSphereBuffer);
214
+ pass.setVertexBuffer(1, this.instanceBuffer, off, size);
215
+ pass.draw(this.solidSphereCount, sphereCount);
216
+ pass.setPipeline(this.wirePipelineSphere);
217
+ pass.setVertexBuffer(0, this.wireSphereBuffer);
218
+ pass.setVertexBuffer(1, this.instanceBuffer, off, size);
219
+ pass.draw(this.wireSphereCount, sphereCount);
220
+ }
221
+ if (boxCount > 0) {
222
+ const off = boxOffset * STRIDE_BYTES;
223
+ const size = boxCount * STRIDE_BYTES;
224
+ pass.setPipeline(this.solidPipelineBox);
225
+ pass.setVertexBuffer(0, this.solidBoxBuffer);
226
+ pass.setVertexBuffer(1, this.instanceBuffer, off, size);
227
+ pass.draw(this.solidBoxCount, boxCount);
228
+ pass.setPipeline(this.wirePipelineBox);
229
+ pass.setVertexBuffer(0, this.wireBoxBuffer);
230
+ pass.setVertexBuffer(1, this.instanceBuffer, off, size);
231
+ pass.draw(this.wireBoxCount, boxCount);
232
+ }
233
+ if (capsuleCount > 0) {
234
+ const off = capsuleOffset * STRIDE_BYTES;
235
+ const size = capsuleCount * STRIDE_BYTES;
236
+ pass.setPipeline(this.solidPipelineCapsule);
237
+ pass.setVertexBuffer(0, this.solidCapsuleBuffer);
238
+ pass.setVertexBuffer(1, this.instanceBuffer, off, size);
239
+ pass.draw(this.solidCapsuleCount, capsuleCount);
240
+ pass.setPipeline(this.wirePipelineCapsule);
241
+ pass.setVertexBuffer(0, this.wireCapsuleBuffer);
242
+ pass.setVertexBuffer(1, this.instanceBuffer, off, size);
243
+ pass.draw(this.wireCapsuleCount, capsuleCount);
244
+ }
245
+ }
246
+ destroy() {
247
+ this.wireSphereBuffer.destroy();
248
+ this.wireBoxBuffer.destroy();
249
+ this.wireCapsuleBuffer.destroy();
250
+ this.solidSphereBuffer.destroy();
251
+ this.solidBoxBuffer.destroy();
252
+ this.solidCapsuleBuffer.destroy();
253
+ this.instanceBuffer.destroy();
254
+ }
255
+ }
256
+ // ── Geometry builders ─────────────────────────────────────────────────────────
257
+ // Each vertex is vec4(unitPos.xyz, axialAnchor) packed as 4 floats.
258
+ function buildSphereWireGeometry() {
259
+ const segs = 32;
260
+ const out = new Float32Array(3 * segs * 2 * 4); // 3 great circles
261
+ let p = 0;
262
+ for (let plane = 0; plane < 3; plane++) {
263
+ for (let i = 0; i < segs; i++) {
264
+ const a0 = (i / segs) * Math.PI * 2;
265
+ const a1 = ((i + 1) / segs) * Math.PI * 2;
266
+ const c0 = Math.cos(a0), s0 = Math.sin(a0);
267
+ const c1 = Math.cos(a1), s1 = Math.sin(a1);
268
+ let p0x = 0, p0y = 0, p0z = 0;
269
+ let p1x = 0, p1y = 0, p1z = 0;
270
+ if (plane === 0) {
271
+ p0x = c0;
272
+ p0y = s0;
273
+ p1x = c1;
274
+ p1y = s1;
275
+ }
276
+ else if (plane === 1) {
277
+ p0x = c0;
278
+ p0z = s0;
279
+ p1x = c1;
280
+ p1z = s1;
281
+ }
282
+ else {
283
+ p0y = c0;
284
+ p0z = s0;
285
+ p1y = c1;
286
+ p1z = s1;
287
+ }
288
+ out[p++] = p0x;
289
+ out[p++] = p0y;
290
+ out[p++] = p0z;
291
+ out[p++] = 0;
292
+ out[p++] = p1x;
293
+ out[p++] = p1y;
294
+ out[p++] = p1z;
295
+ out[p++] = 0;
296
+ }
297
+ }
298
+ return out;
299
+ }
300
+ function buildBoxWireGeometry() {
301
+ const edges = [
302
+ // 4 along X
303
+ [-1, -1, -1, +1, -1, -1], [-1, -1, +1, +1, -1, +1],
304
+ [-1, +1, -1, +1, +1, -1], [-1, +1, +1, +1, +1, +1],
305
+ // 4 along Y
306
+ [-1, -1, -1, -1, +1, -1], [+1, -1, -1, +1, +1, -1],
307
+ [-1, -1, +1, -1, +1, +1], [+1, -1, +1, +1, +1, +1],
308
+ // 4 along Z
309
+ [-1, -1, -1, -1, -1, +1], [+1, -1, -1, +1, -1, +1],
310
+ [-1, +1, -1, -1, +1, +1], [+1, +1, -1, +1, +1, +1],
311
+ ];
312
+ const out = new Float32Array(edges.length * 2 * 4);
313
+ let p = 0;
314
+ for (const e of edges) {
315
+ out[p++] = e[0];
316
+ out[p++] = e[1];
317
+ out[p++] = e[2];
318
+ out[p++] = 0;
319
+ out[p++] = e[3];
320
+ out[p++] = e[4];
321
+ out[p++] = e[5];
322
+ out[p++] = 0;
323
+ }
324
+ return out;
325
+ }
326
+ function buildCapsuleWireGeometry() {
327
+ const ringSegs = 32;
328
+ const arcSegs = 16;
329
+ // 2 cap rings + 4 hemisphere arcs + 4 cylinder verticals
330
+ const lineCount = ringSegs * 2 + arcSegs * 4 + 4;
331
+ const out = new Float32Array(lineCount * 2 * 4);
332
+ let p = 0;
333
+ const push = (x, y, z, axial) => {
334
+ out[p++] = x;
335
+ out[p++] = y;
336
+ out[p++] = z;
337
+ out[p++] = axial;
338
+ };
339
+ // Top ring (axial=+1) in XZ plane
340
+ for (let i = 0; i < ringSegs; i++) {
341
+ const a0 = (i / ringSegs) * Math.PI * 2;
342
+ const a1 = ((i + 1) / ringSegs) * Math.PI * 2;
343
+ push(Math.cos(a0), 0, Math.sin(a0), +1);
344
+ push(Math.cos(a1), 0, Math.sin(a1), +1);
345
+ }
346
+ // Bottom ring (axial=-1)
347
+ for (let i = 0; i < ringSegs; i++) {
348
+ const a0 = (i / ringSegs) * Math.PI * 2;
349
+ const a1 = ((i + 1) / ringSegs) * Math.PI * 2;
350
+ push(Math.cos(a0), 0, Math.sin(a0), -1);
351
+ push(Math.cos(a1), 0, Math.sin(a1), -1);
352
+ }
353
+ // Top cap arcs in XY and YZ planes (θ ∈ [0, π], pos = (cosθ, sinθ, 0) etc.)
354
+ for (let i = 0; i < arcSegs; i++) {
355
+ const t0 = (i / arcSegs) * Math.PI;
356
+ const t1 = ((i + 1) / arcSegs) * Math.PI;
357
+ push(Math.cos(t0), Math.sin(t0), 0, +1);
358
+ push(Math.cos(t1), Math.sin(t1), 0, +1);
359
+ }
360
+ for (let i = 0; i < arcSegs; i++) {
361
+ const t0 = (i / arcSegs) * Math.PI;
362
+ const t1 = ((i + 1) / arcSegs) * Math.PI;
363
+ push(0, Math.sin(t0), Math.cos(t0), +1);
364
+ push(0, Math.sin(t1), Math.cos(t1), +1);
365
+ }
366
+ // Bottom cap arcs
367
+ for (let i = 0; i < arcSegs; i++) {
368
+ const t0 = (i / arcSegs) * Math.PI;
369
+ const t1 = ((i + 1) / arcSegs) * Math.PI;
370
+ push(Math.cos(t0), -Math.sin(t0), 0, -1);
371
+ push(Math.cos(t1), -Math.sin(t1), 0, -1);
372
+ }
373
+ for (let i = 0; i < arcSegs; i++) {
374
+ const t0 = (i / arcSegs) * Math.PI;
375
+ const t1 = ((i + 1) / arcSegs) * Math.PI;
376
+ push(0, -Math.sin(t0), Math.cos(t0), -1);
377
+ push(0, -Math.sin(t1), Math.cos(t1), -1);
378
+ }
379
+ // 4 cylinder verticals: bottom rim (axial=-1) → top rim (+1) at fixed θ
380
+ push(+1, 0, 0, -1);
381
+ push(+1, 0, 0, +1);
382
+ push(-1, 0, 0, -1);
383
+ push(-1, 0, 0, +1);
384
+ push(0, 0, +1, -1);
385
+ push(0, 0, +1, +1);
386
+ push(0, 0, -1, -1);
387
+ push(0, 0, -1, +1);
388
+ return out;
389
+ }
390
+ // ── Solid (triangle-list) geometry builders ─────────────────────────────────
391
+ // Each vertex is vec4(unitPos.xyz, axialAnchor) — same layout as wireframe.
392
+ // UV sphere — stacks × slices quads, each split into 2 triangles. Polar quads
393
+ // degenerate to triangles, which the GPU silently drops.
394
+ function buildSphereSolidGeometry() {
395
+ const stacks = 12;
396
+ const slices = 18;
397
+ const out = new Float32Array(stacks * slices * 6 * 4);
398
+ let p = 0;
399
+ const vert = (phi, theta) => {
400
+ out[p++] = Math.cos(phi) * Math.cos(theta);
401
+ out[p++] = Math.sin(phi);
402
+ out[p++] = Math.cos(phi) * Math.sin(theta);
403
+ out[p++] = 0;
404
+ };
405
+ for (let s = 0; s < stacks; s++) {
406
+ const phi0 = -Math.PI / 2 + (s / stacks) * Math.PI;
407
+ const phi1 = -Math.PI / 2 + ((s + 1) / stacks) * Math.PI;
408
+ for (let l = 0; l < slices; l++) {
409
+ const th0 = (l / slices) * Math.PI * 2;
410
+ const th1 = ((l + 1) / slices) * Math.PI * 2;
411
+ vert(phi0, th0);
412
+ vert(phi1, th0);
413
+ vert(phi1, th1);
414
+ vert(phi0, th0);
415
+ vert(phi1, th1);
416
+ vert(phi0, th1);
417
+ }
418
+ }
419
+ return out;
420
+ }
421
+ // 6 cube faces × 2 triangles × 3 verts = 36 verts.
422
+ function buildBoxSolidGeometry() {
423
+ const faces = [
424
+ // +X face
425
+ [+1, -1, -1, +1, +1, -1, +1, +1, +1, +1, -1, +1],
426
+ // -X face
427
+ [-1, -1, -1, -1, -1, +1, -1, +1, +1, -1, +1, -1],
428
+ // +Y face
429
+ [-1, +1, -1, -1, +1, +1, +1, +1, +1, +1, +1, -1],
430
+ // -Y face
431
+ [-1, -1, -1, +1, -1, -1, +1, -1, +1, -1, -1, +1],
432
+ // +Z face
433
+ [-1, -1, +1, +1, -1, +1, +1, +1, +1, -1, +1, +1],
434
+ // -Z face
435
+ [-1, -1, -1, -1, +1, -1, +1, +1, -1, +1, -1, -1],
436
+ ];
437
+ const out = new Float32Array(faces.length * 6 * 4);
438
+ let p = 0;
439
+ const v = (x, y, z) => {
440
+ out[p++] = x;
441
+ out[p++] = y;
442
+ out[p++] = z;
443
+ out[p++] = 0;
444
+ };
445
+ for (const f of faces) {
446
+ // f = [a, b, c, d] as 4 corners; emit tris (a,b,c) (a,c,d)
447
+ v(f[0], f[1], f[2]);
448
+ v(f[3], f[4], f[5]);
449
+ v(f[6], f[7], f[8]);
450
+ v(f[0], f[1], f[2]);
451
+ v(f[6], f[7], f[8]);
452
+ v(f[9], f[10], f[11]);
453
+ }
454
+ return out;
455
+ }
456
+ // Capsule: two hemispheres at axial=±1 + cylinder side connecting their equators.
457
+ // Vertex shader does: world.y = unit.y * radius + halfHeight * axial.
458
+ function buildCapsuleSolidGeometry() {
459
+ const slices = 18;
460
+ const capStacks = 6;
461
+ const cylTris = slices * 2; // 1 stack of quads → 2 tris/slice
462
+ const capTris = capStacks * slices * 2;
463
+ const totalVerts = (cylTris + capTris * 2) * 3;
464
+ const out = new Float32Array(totalVerts * 4);
465
+ let p = 0;
466
+ const v = (x, y, z, axial) => {
467
+ out[p++] = x;
468
+ out[p++] = y;
469
+ out[p++] = z;
470
+ out[p++] = axial;
471
+ };
472
+ // Cylinder side: two rings at axial=±1, both at unit y=0.
473
+ for (let l = 0; l < slices; l++) {
474
+ const th0 = (l / slices) * Math.PI * 2;
475
+ const th1 = ((l + 1) / slices) * Math.PI * 2;
476
+ const c0 = Math.cos(th0), s0 = Math.sin(th0);
477
+ const c1 = Math.cos(th1), s1 = Math.sin(th1);
478
+ // bottom-left, top-left, top-right
479
+ v(c0, 0, s0, -1);
480
+ v(c0, 0, s0, +1);
481
+ v(c1, 0, s1, +1);
482
+ // bottom-left, top-right, bottom-right
483
+ v(c0, 0, s0, -1);
484
+ v(c1, 0, s1, +1);
485
+ v(c1, 0, s1, -1);
486
+ }
487
+ // Top hemisphere (axial=+1): φ ∈ [0, π/2], pos = (cosφ cosθ, sinφ, cosφ sinθ).
488
+ for (let s = 0; s < capStacks; s++) {
489
+ const ph0 = (s / capStacks) * (Math.PI / 2);
490
+ const ph1 = ((s + 1) / capStacks) * (Math.PI / 2);
491
+ for (let l = 0; l < slices; l++) {
492
+ const th0 = (l / slices) * Math.PI * 2;
493
+ const th1 = ((l + 1) / slices) * Math.PI * 2;
494
+ const a = (ph, th) => [
495
+ Math.cos(ph) * Math.cos(th), Math.sin(ph), Math.cos(ph) * Math.sin(th),
496
+ ];
497
+ const p00 = a(ph0, th0), p10 = a(ph1, th0), p11 = a(ph1, th1), p01 = a(ph0, th1);
498
+ v(p00[0], p00[1], p00[2], +1);
499
+ v(p10[0], p10[1], p10[2], +1);
500
+ v(p11[0], p11[1], p11[2], +1);
501
+ v(p00[0], p00[1], p00[2], +1);
502
+ v(p11[0], p11[1], p11[2], +1);
503
+ v(p01[0], p01[1], p01[2], +1);
504
+ }
505
+ }
506
+ // Bottom hemisphere (axial=-1): same but y = -sinφ.
507
+ for (let s = 0; s < capStacks; s++) {
508
+ const ph0 = (s / capStacks) * (Math.PI / 2);
509
+ const ph1 = ((s + 1) / capStacks) * (Math.PI / 2);
510
+ for (let l = 0; l < slices; l++) {
511
+ const th0 = (l / slices) * Math.PI * 2;
512
+ const th1 = ((l + 1) / slices) * Math.PI * 2;
513
+ const a = (ph, th) => [
514
+ Math.cos(ph) * Math.cos(th), -Math.sin(ph), Math.cos(ph) * Math.sin(th),
515
+ ];
516
+ const p00 = a(ph0, th0), p10 = a(ph1, th0), p11 = a(ph1, th1), p01 = a(ph0, th1);
517
+ v(p00[0], p00[1], p00[2], -1);
518
+ v(p11[0], p11[1], p11[2], -1);
519
+ v(p10[0], p10[1], p10[2], -1);
520
+ v(p00[0], p00[1], p00[2], -1);
521
+ v(p01[0], p01[1], p01[2], -1);
522
+ v(p11[0], p11[1], p11[2], -1);
523
+ }
524
+ }
525
+ return out;
526
+ }
@@ -0,0 +1,2 @@
1
+ export declare const PHYSICS_DEBUG_SHADER_WGSL = "\nstruct CameraUniforms {\n view: mat4x4f,\n projection: mat4x4f,\n viewPos: vec3f,\n _pad: f32,\n};\n\n@group(0) @binding(0) var<uniform> camera: CameraUniforms;\n\noverride SHAPE_KIND: u32 = 0u;\noverride SOLID_ALPHA: f32 = 1.0;\n\nstruct VsIn {\n @location(0) unit: vec4f, // xyz = unit pos; w = capsule axial anchor\n @location(1) modelCol0: vec4f,\n @location(2) modelCol1: vec4f,\n @location(3) modelCol2: vec4f,\n @location(4) modelCol3: vec4f,\n @location(5) size: vec4f, // xyz = size; w unused\n @location(6) color: vec4f,\n};\n\nstruct VsOut {\n @builtin(position) pos: vec4f,\n @location(0) color: vec4f,\n};\n\n@vertex\nfn vsMain(in: VsIn) -> VsOut {\n let model = mat4x4f(in.modelCol0, in.modelCol1, in.modelCol2, in.modelCol3);\n var local: vec3f;\n if (SHAPE_KIND == 1u) {\n local = in.unit.xyz * in.size.xyz;\n } else if (SHAPE_KIND == 2u) {\n // PMX capsule: size.x = radius, size.y = full cylinder height. Bullet 2.x's\n // btCapsuleShape(radius, height) stores height/2 internally, so the actual\n // collision cylinder spans \u00B1size.y/2 \u2014 halve here to match.\n let r = in.size.x;\n let halfH = in.size.y * 0.5;\n local = vec3f(in.unit.x * r, in.unit.y * r + halfH * in.unit.w, in.unit.z * r);\n } else {\n local = in.unit.xyz * in.size.x;\n }\n let world = model * vec4f(local, 1.0);\n var out: VsOut;\n out.pos = camera.projection * camera.view * world;\n out.color = in.color;\n return out;\n}\n\n@fragment\nfn fsMain(in: VsOut) -> @location(0) vec4f {\n return vec4f(in.color.rgb, in.color.a * SOLID_ALPHA);\n}\n";
2
+ //# sourceMappingURL=physics-debug.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"physics-debug.d.ts","sourceRoot":"","sources":["../../../src/shaders/passes/physics-debug.ts"],"names":[],"mappings":"AAcA,eAAO,MAAM,yBAAyB,2kDAuDrC,CAAA"}
@@ -0,0 +1,69 @@
1
+ // Wireframe + solid overlay for physics rigid bodies (sphere / box / capsule).
2
+ // Rendered in its own pass after composite, straight to the swapchain — no
3
+ // MSAA, no depth, no MRT — so it sits cleanly on top of the tonemapped scene
4
+ // regardless of camera angle, model occlusion, or the composite alpha gate.
5
+ //
6
+ // SHAPE_KIND 0 = sphere → unit-sphere line/tri primitive scaled by size.x (radius)
7
+ // 1 = box → unit cube ±1 scaled by size.xyz (half-extents)
8
+ // 2 = capsule → unit cap+ring with per-vertex `axial` (-1/0/+1)
9
+ // that lifts cap rings/arcs ±halfHeight along Y;
10
+ // XZ scales by size.x (radius), Y by size.y/2.
11
+ // SOLID_ALPHA fragment alpha multiplier. Wireframe pipelines leave this at
12
+ // 1.0 for crisp edges; solid pipelines set it to ~0.2 so the fill
13
+ // stays gentle under stacked bodies.
14
+ export const PHYSICS_DEBUG_SHADER_WGSL = /* wgsl */ `
15
+ struct CameraUniforms {
16
+ view: mat4x4f,
17
+ projection: mat4x4f,
18
+ viewPos: vec3f,
19
+ _pad: f32,
20
+ };
21
+
22
+ @group(0) @binding(0) var<uniform> camera: CameraUniforms;
23
+
24
+ override SHAPE_KIND: u32 = 0u;
25
+ override SOLID_ALPHA: f32 = 1.0;
26
+
27
+ struct VsIn {
28
+ @location(0) unit: vec4f, // xyz = unit pos; w = capsule axial anchor
29
+ @location(1) modelCol0: vec4f,
30
+ @location(2) modelCol1: vec4f,
31
+ @location(3) modelCol2: vec4f,
32
+ @location(4) modelCol3: vec4f,
33
+ @location(5) size: vec4f, // xyz = size; w unused
34
+ @location(6) color: vec4f,
35
+ };
36
+
37
+ struct VsOut {
38
+ @builtin(position) pos: vec4f,
39
+ @location(0) color: vec4f,
40
+ };
41
+
42
+ @vertex
43
+ fn vsMain(in: VsIn) -> VsOut {
44
+ let model = mat4x4f(in.modelCol0, in.modelCol1, in.modelCol2, in.modelCol3);
45
+ var local: vec3f;
46
+ if (SHAPE_KIND == 1u) {
47
+ local = in.unit.xyz * in.size.xyz;
48
+ } else if (SHAPE_KIND == 2u) {
49
+ // PMX capsule: size.x = radius, size.y = full cylinder height. Bullet 2.x's
50
+ // btCapsuleShape(radius, height) stores height/2 internally, so the actual
51
+ // collision cylinder spans ±size.y/2 — halve here to match.
52
+ let r = in.size.x;
53
+ let halfH = in.size.y * 0.5;
54
+ local = vec3f(in.unit.x * r, in.unit.y * r + halfH * in.unit.w, in.unit.z * r);
55
+ } else {
56
+ local = in.unit.xyz * in.size.x;
57
+ }
58
+ let world = model * vec4f(local, 1.0);
59
+ var out: VsOut;
60
+ out.pos = camera.projection * camera.view * world;
61
+ out.color = in.color;
62
+ return out;
63
+ }
64
+
65
+ @fragment
66
+ fn fsMain(in: VsOut) -> @location(0) vec4f {
67
+ return vec4f(in.color.rgb, in.color.a * SOLID_ALPHA);
68
+ }
69
+ `;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "reze-engine",
3
- "version": "0.22.1",
3
+ "version": "0.23.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",
@@ -23,7 +23,7 @@
23
23
  "build": "tsc",
24
24
  "dev": "tsc --watch",
25
25
  "test": "node --import ./tests/register.mjs --test tests/*.test.mjs",
26
- "prepublishOnly": "cp ../README.md ./README.md && npm run build"
26
+ "prepublishOnly": "node -e \"require('fs').copyFileSync('../README.md','README.md')\" && npm run build"
27
27
  },
28
28
  "keywords": [
29
29
  "webgpu",
package/src/engine.ts CHANGED
@@ -1708,13 +1708,35 @@ export class Engine {
1708
1708
  window.addEventListener("mouseup", this.handleGizmoMouseUp)
1709
1709
  }
1710
1710
 
1711
- private handleResize() {
1712
- const displayWidth = this.canvas.clientWidth
1713
- const displayHeight = this.canvas.clientHeight
1711
+ /** When set, render resolution is pinned to this size instead of tracking the
1712
+ * canvas's CSS size × devicePixelRatio (see setRenderSize). */
1713
+ private fixedRenderSize: { width: number; height: number } | null = null
1714
+
1715
+ /**
1716
+ * Pin the render resolution (canvas backing store + every render target) to an
1717
+ * explicit size, decoupled from the canvas's CSS layout size — for offline
1718
+ * rendering at arbitrary resolution (video export). On screen the browser scales
1719
+ * the buffer to the layout box, so the canvas may display letterboxed/stretched
1720
+ * while pinned; hosts typically cover it with an export overlay. Pass null to
1721
+ * return to CSS-size × devicePixelRatio tracking. Applies immediately (targets
1722
+ * rebuild before this returns), so the next render() is at the new size.
1723
+ */
1724
+ setRenderSize(width: number, height: number): void
1725
+ setRenderSize(size: null): void
1726
+ setRenderSize(widthOrNull: number | null, height?: number): void {
1727
+ this.fixedRenderSize =
1728
+ widthOrNull === null
1729
+ ? null
1730
+ : { width: Math.max(1, Math.floor(widthOrNull)), height: Math.max(1, Math.floor(height ?? 1)) }
1731
+ this.resizePending = false
1732
+ this.handleResize()
1733
+ }
1714
1734
 
1735
+ private handleResize() {
1736
+ // Fixed override (offline/video rendering) wins; otherwise track CSS size × dpr.
1715
1737
  const dpr = window.devicePixelRatio || 1
1716
- const width = Math.floor(displayWidth * dpr)
1717
- const height = Math.floor(displayHeight * dpr)
1738
+ const width = this.fixedRenderSize ? this.fixedRenderSize.width : Math.floor(this.canvas.clientWidth * dpr)
1739
+ const height = this.fixedRenderSize ? this.fixedRenderSize.height : Math.floor(this.canvas.clientHeight * dpr)
1718
1740
 
1719
1741
  if (!this.multisampleTexture || this.canvas.width !== width || this.canvas.height !== height) {
1720
1742
  this.canvas.width = width
@@ -3907,15 +3929,32 @@ export class Engine {
3907
3929
  render() {
3908
3930
  if (!this.multisampleTexture || !this.camera || !this.device) return
3909
3931
 
3932
+ const currentTime = performance.now()
3933
+ const deltaTime = this.lastFrameTime > 0 ? (currentTime - this.lastFrameTime) / 1000 : 0.016
3934
+ this.lastFrameTime = currentTime
3935
+ this.renderWithDelta(deltaTime)
3936
+ }
3937
+
3938
+ /**
3939
+ * Render one frame advancing every clock — animation, physics, tweens, and the
3940
+ * camera VMD — by exactly `deltaSeconds`, independent of wall time. This is the
3941
+ * offline-rendering primitive (video export): call it N times with 1/fps and the
3942
+ * result is deterministic whether the machine renders faster or slower than
3943
+ * realtime. Also resets the realtime clock so a later render() (returning to the
3944
+ * live loop) doesn't see the export's wall-clock gap as one giant delta.
3945
+ */
3946
+ renderFrame(deltaSeconds: number) {
3947
+ if (!this.multisampleTexture || !this.camera || !this.device) return
3948
+ this.lastFrameTime = performance.now()
3949
+ this.renderWithDelta(deltaSeconds)
3950
+ }
3951
+
3952
+ private renderWithDelta(deltaTime: number) {
3910
3953
  if (this.resizePending) {
3911
3954
  this.resizePending = false
3912
3955
  this.handleResize()
3913
3956
  }
3914
3957
 
3915
- const currentTime = performance.now()
3916
- const deltaTime = this.lastFrameTime > 0 ? (currentTime - this.lastFrameTime) / 1000 : 0.016
3917
- this.lastFrameTime = currentTime
3918
-
3919
3958
  const hasModels = this.modelInstances.size > 0
3920
3959
  if (hasModels) {
3921
3960
  this.updateInstances(deltaTime)
@@ -1,19 +0,0 @@
1
- export declare class GpuTimestamp {
2
- enabled: boolean;
3
- readonly supported: boolean;
4
- private device;
5
- private querySet;
6
- private resolveBuffer;
7
- private readbackBuffer;
8
- private nextSlot;
9
- private frameLabels;
10
- private inFlight;
11
- private pendingLabels;
12
- constructor(device: GPUDevice);
13
- private ensureCreated;
14
- beginFrame(): void;
15
- wrap(desc: GPURenderPassDescriptor, label: string): GPURenderPassDescriptor;
16
- endFrame(encoder: GPUCommandEncoder): void;
17
- afterSubmit(): void;
18
- }
19
- //# sourceMappingURL=gpu-profile.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"gpu-profile.d.ts","sourceRoot":"","sources":["../src/gpu-profile.ts"],"names":[],"mappings":"AAoBA,qBAAa,YAAY;IACvB,OAAO,UAAQ;IACf,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAA;IAC3B,OAAO,CAAC,MAAM,CAAW;IACzB,OAAO,CAAC,QAAQ,CAA2B;IAC3C,OAAO,CAAC,aAAa,CAAyB;IAC9C,OAAO,CAAC,cAAc,CAAyB;IAC/C,OAAO,CAAC,QAAQ,CAAI;IACpB,OAAO,CAAC,WAAW,CAAe;IAClC,OAAO,CAAC,QAAQ,CAAQ;IAKxB,OAAO,CAAC,aAAa,CAAwB;gBAEjC,MAAM,EAAE,SAAS;IAK7B,OAAO,CAAC,aAAa;IAiBrB,UAAU,IAAI,IAAI;IAWlB,IAAI,CAAC,IAAI,EAAE,uBAAuB,EAAE,KAAK,EAAE,MAAM,GAAG,uBAAuB;IAqB3E,QAAQ,CAAC,OAAO,EAAE,iBAAiB,GAAG,IAAI;IAW1C,WAAW,IAAI,IAAI;CAwBpB"}
@@ -1,120 +0,0 @@
1
- // WebGPU timestamp-query profiling. Records pass durations into the same
2
- // profiler module as CPU phases (so the snapshot table mixes both with a
3
- // "gpu." prefix). Read-only — no effect on rendering output.
4
- //
5
- // Pattern (per spec):
6
- // 1. Each render pass declares timestampWrites { begin, end } slot indices.
7
- // 2. After all passes, encoder.resolveQuerySet writes uint64 ns timestamps
8
- // into a QUERY_RESOLVE buffer.
9
- // 3. We copy the resolve buffer into a MAP_READ readback buffer.
10
- // 4. Submit. After the GPU finishes, mapAsync resolves; we read durations
11
- // and record them.
12
- //
13
- // One readback buffer with an in-flight gate: if mapAsync hasn't resolved
14
- // from a previous frame, we skip the copy this frame (no contention, just
15
- // one frame of dropped sampling).
16
- import { profiler } from "./physics/profile";
17
- const MAX_PASSES_PER_FRAME = 64;
18
- export class GpuTimestamp {
19
- constructor(device) {
20
- this.enabled = false;
21
- this.querySet = null;
22
- this.resolveBuffer = null;
23
- this.readbackBuffer = null;
24
- this.nextSlot = 0;
25
- this.frameLabels = [];
26
- this.inFlight = false;
27
- // Set inside endFrame() when a copy was queued this frame; consumed by
28
- // afterSubmit() to kick mapAsync. mapAsync must run AFTER queue.submit —
29
- // calling it before submit puts the buffer into "mapping pending" state,
30
- // which makes the copy in the encoder illegal.
31
- this.pendingLabels = null;
32
- this.device = device;
33
- this.supported = device.features.has("timestamp-query");
34
- }
35
- ensureCreated() {
36
- if (this.querySet || !this.supported)
37
- return;
38
- const slots = MAX_PASSES_PER_FRAME * 2;
39
- this.querySet = this.device.createQuerySet({ type: "timestamp", count: slots });
40
- this.resolveBuffer = this.device.createBuffer({
41
- label: "gpu-timestamp-resolve",
42
- size: slots * 8,
43
- usage: GPUBufferUsage.QUERY_RESOLVE | GPUBufferUsage.COPY_SRC,
44
- });
45
- this.readbackBuffer = this.device.createBuffer({
46
- label: "gpu-timestamp-readback",
47
- size: slots * 8,
48
- usage: GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST,
49
- });
50
- }
51
- // Called once at the top of each render() before any pass wrap.
52
- beginFrame() {
53
- if (!this.enabled || !this.supported)
54
- return;
55
- this.ensureCreated();
56
- this.nextSlot = 0;
57
- this.frameLabels = [];
58
- }
59
- // Wrap a render-pass descriptor with timestampWrites for `label`. If
60
- // profiling is off / unsupported / out of slots, returns desc unchanged.
61
- // The same label called multiple times (e.g. each bloom mip) is fine —
62
- // the profiler accumulates by label.
63
- wrap(desc, label) {
64
- if (!this.enabled || !this.querySet)
65
- return desc;
66
- if (this.nextSlot + 2 > MAX_PASSES_PER_FRAME * 2)
67
- return desc;
68
- const begin = this.nextSlot;
69
- const end = this.nextSlot + 1;
70
- this.nextSlot += 2;
71
- this.frameLabels.push(label);
72
- return {
73
- ...desc,
74
- timestampWrites: {
75
- querySet: this.querySet,
76
- beginningOfPassWriteIndex: begin,
77
- endOfPassWriteIndex: end,
78
- },
79
- };
80
- }
81
- // Call on the encoder after all passes, BEFORE encoder.finish(). Adds
82
- // resolveQuerySet + copyBufferToBuffer commands. mapAsync is deferred to
83
- // afterSubmit() — calling it before submit transitions the readback buffer
84
- // into "mapping pending" state, which makes the queued copy illegal.
85
- endFrame(encoder) {
86
- if (!this.enabled || !this.querySet || this.frameLabels.length === 0)
87
- return;
88
- const count = this.frameLabels.length * 2;
89
- encoder.resolveQuerySet(this.querySet, 0, count, this.resolveBuffer, 0);
90
- if (this.inFlight)
91
- return;
92
- encoder.copyBufferToBuffer(this.resolveBuffer, 0, this.readbackBuffer, 0, count * 8);
93
- this.pendingLabels = this.frameLabels.slice();
94
- }
95
- // Call AFTER device.queue.submit(). Kicks mapAsync if a copy was queued
96
- // this frame; the .then() reads durations and unmaps.
97
- afterSubmit() {
98
- const labels = this.pendingLabels;
99
- if (!labels)
100
- return;
101
- this.pendingLabels = null;
102
- this.inFlight = true;
103
- const readback = this.readbackBuffer;
104
- void readback.mapAsync(GPUMapMode.READ).then(() => {
105
- const arr = new BigInt64Array(readback.getMappedRange());
106
- for (let i = 0; i < labels.length; i++) {
107
- const start = arr[i * 2];
108
- const end = arr[i * 2 + 1];
109
- // Per spec, timestamps are nanoseconds.
110
- const ms = Number(end - start) / 1e6;
111
- if (ms >= 0 && ms < 1000)
112
- profiler.record("gpu." + labels[i], ms);
113
- }
114
- readback.unmap();
115
- this.inFlight = false;
116
- }, () => {
117
- this.inFlight = false;
118
- });
119
- }
120
- }
@@ -1,18 +0,0 @@
1
- declare class PhysicsProfiler {
2
- enabled: boolean;
3
- private sums;
4
- private counts;
5
- begin(): number;
6
- end(label: string, t0: number): void;
7
- record(label: string, ms: number): void;
8
- snapshot(): Record<string, {
9
- avgMs: number;
10
- calls: number;
11
- totalMs: number;
12
- }>;
13
- reset(): void;
14
- }
15
- export declare const profiler: PhysicsProfiler;
16
- export type ProfileSnapshot = ReturnType<PhysicsProfiler["snapshot"]>;
17
- export {};
18
- //# sourceMappingURL=profile.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"profile.d.ts","sourceRoot":"","sources":["../../src/physics/profile.ts"],"names":[],"mappings":"AAIA,cAAM,eAAe;IACnB,OAAO,UAAQ;IACf,OAAO,CAAC,IAAI,CAA8C;IAC1D,OAAO,CAAC,MAAM,CAA8C;IAE5D,KAAK,IAAI,MAAM;IAIf,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI;IASpC,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI;IAQvC,QAAQ,IAAI,MAAM,CAAC,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAU7E,KAAK,IAAI,IAAI;CAId;AAED,eAAO,MAAM,QAAQ,iBAAwB,CAAA;AAC7C,MAAM,MAAM,eAAe,GAAG,UAAU,CAAC,eAAe,CAAC,UAAU,CAAC,CAAC,CAAA"}
@@ -1,44 +0,0 @@
1
- // Per-phase wall-clock accumulator. Off by default — when `enabled` is false,
2
- // begin()/end() short-circuit so production has zero overhead. Toggle via
3
- // `RezePhysics.setProfileEnabled(true)` and read with `getProfile()`.
4
- class PhysicsProfiler {
5
- constructor() {
6
- this.enabled = false;
7
- this.sums = Object.create(null);
8
- this.counts = Object.create(null);
9
- }
10
- begin() {
11
- return this.enabled ? performance.now() : 0;
12
- }
13
- end(label, t0) {
14
- if (!this.enabled)
15
- return;
16
- const dt = performance.now() - t0;
17
- this.sums[label] = (this.sums[label] ?? 0) + dt;
18
- this.counts[label] = (this.counts[label] ?? 0) + 1;
19
- }
20
- // Direct accumulator for durations not measured via begin()/end() — used by
21
- // GPU timestamp readback, which arrives async after the frame has ended.
22
- record(label, ms) {
23
- if (!this.enabled)
24
- return;
25
- this.sums[label] = (this.sums[label] ?? 0) + ms;
26
- this.counts[label] = (this.counts[label] ?? 0) + 1;
27
- }
28
- // Snapshot since last reset(). avgMs is per call; totalMs is the cumulative
29
- // window cost — divide by elapsed wall time to get a "% of frame budget".
30
- snapshot() {
31
- const out = {};
32
- for (const k in this.sums) {
33
- const total = this.sums[k];
34
- const calls = this.counts[k];
35
- out[k] = { avgMs: calls > 0 ? total / calls : 0, calls, totalMs: total };
36
- }
37
- return out;
38
- }
39
- reset() {
40
- this.sums = Object.create(null);
41
- this.counts = Object.create(null);
42
- }
43
- }
44
- export const profiler = new PhysicsProfiler();