@rig-c/play 1.18.0 → 1.20.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,5 +1,5 @@
1
1
  import type { LiveFrame } from './liveFrame.js';
2
- import { type RegionImage } from './DomTexture.js';
2
+ import { type AtlasRegion, type RegionImage } from './DomTexture.js';
3
3
  import type { PosedFrame } from './posedFrame.js';
4
4
  /** Rasterizer used for the mesh (deform) tier. */
5
5
  export type MeshBackend = 'canvas2d' | 'webgl';
@@ -40,6 +40,7 @@ export declare class DomRenderer {
40
40
  private readonly root;
41
41
  private readonly regionImages;
42
42
  private readonly pages;
43
+ private readonly atlasRegions;
43
44
  /**
44
45
  * Apply the clips the frame carries (default). Each element drawn inside a
45
46
  * clip gets a CSS `clip-path` in its own local frame — an element-level
@@ -63,6 +64,8 @@ export declare class DomRenderer {
63
64
  clipping: boolean;
64
65
  /** Clips applied last frame — distinct clip polygons over the drawn slots. */
65
66
  clipCount: number;
67
+ /** Atlas rigid canvases rasterized in the last frame; never includes meshes. */
68
+ rigidRasterCount: number;
66
69
  /**
67
70
  * Clips in the frame NOT applied last frame: `clipping` is false, or the
68
71
  * polygon has fewer than three points. A clip the runtime itself ignores (a
@@ -171,11 +174,13 @@ export declare class DomRenderer {
171
174
  * and scaling of the root belong to the caller.
172
175
  * @param regionImages Unpacked per-region bitmaps (for the rigid tier),
173
176
  * keyed by region name — `PosedRegion.region`.
177
+ * @param atlasRegions Optional page-backed rigid sources, from createAtlasRegions
178
+ * or ModelAssets.atlasRegions. A matching entry takes precedence over regionImages.
174
179
  * @param pages Page images (for the mesh tier), keyed by page name —
175
180
  * `PosedMesh.page`. Read on every mesh draw, so a caller may fill it after
176
181
  * construction.
177
182
  */
178
- constructor(root: HTMLElement, regionImages: ReadonlyMap<string, RegionImage>, pages?: ReadonlyMap<string, HTMLImageElement>);
183
+ constructor(root: HTMLElement, regionImages: ReadonlyMap<string, RegionImage>, pages?: ReadonlyMap<string, HTMLImageElement>, atlasRegions?: ReadonlyMap<string, AtlasRegion>);
179
184
  /**
180
185
  * Backing-store pixels currently allocated across the mesh canvases
181
186
  * (Σ width × height). Computed on demand from the views — nothing is
@@ -1 +1 @@
1
- {"version":3,"file":"DomRenderer.d.ts","sourceRoot":"","sources":["../src/DomRenderer.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"DomRenderer.d.ts","sourceRoot":"","sources":["../src/DomRenderer.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAEhD,OAAO,EAAE,KAAK,WAAW,EAAoC,KAAK,WAAW,EAAuB,MAAM,iBAAiB,CAAC;AAE5H,OAAO,KAAK,EAAwB,UAAU,EAAqC,MAAM,iBAAiB,CAAC;AAE3G,kDAAkD;AAClD,MAAM,MAAM,WAAW,GAAG,UAAU,GAAG,OAAO,CAAC;AAE/C;;;;GAIG;AACH,MAAM,MAAM,cAAc,GAAG,UAAU,GAAG,MAAM,CAAC;AAyMjD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,qBAAa,WAAW;IA+IpB,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,YAAY;IAC7B,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,YAAY;IAjJ/B;;;;;;;;;;;;;;;;;;;OAmBG;IACH,QAAQ,UAAQ;IAChB,8EAA8E;IAC9E,SAAS,SAAK;IACd,gFAAgF;IAChF,gBAAgB,SAAK;IACrB;;;;;OAKG;IACH,aAAa,SAAK;IAClB;;;;OAIG;IACH,cAAc,SAAK;IACnB,2CAA2C;IAC3C,SAAS,SAAK;IACd,kEAAkE;IAClE,cAAc,SAAK;IACnB;;;;OAIG;IACH,kBAAkB,SAAK;IACvB,uCAAuC;IACvC,aAAa,SAAK;IAClB;;;;;OAKG;IACH,cAAc,SAAO;IACrB;;;;OAIG;IACH,cAAc,EAAE,cAAc,CAA2B;IACzD;;;;;;OAMG;IACH,UAAU,SAA+D;IACzE;;;;;;;;;;;;;;OAcG;IACH,WAAW,EAAE,WAAW,CAAW;IACnC,+EAA+E;IAC/E,iBAAiB,EAAE,WAAW,CAAW;IAEzC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA+B;IACrD,6FAA6F;IAC7F,OAAO,CAAC,WAAW,CAAK;IACxB,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAqB;IACjD,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAkB;IAC/C;;;;OAIG;IACH,OAAO,CAAC,OAAO,CAAsC;IACrD;;;;;;OAMG;IACH,OAAO,CAAC,eAAe,CAAyB;IAChD,OAAO,CAAC,QAAQ,CAA8B;IAC9C,iEAAiE;IACjE,OAAO,CAAC,UAAU,CAA0B;IAC5C,gFAAgF;IAChF,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAwB;IAClD,OAAO,CAAC,WAAW,CAAS;IAC5B,yEAAyE;IACzE,OAAO,CAAC,UAAU,CAAS;IAC3B,0EAA0E;IAC1E,OAAO,CAAC,WAAW,CAAuB;IAC1C,OAAO,CAAC,iBAAiB,CAAK;IAC9B;;;;OAIG;IACH,OAAO,CAAC,aAAa,CAAuB;IAC5C,6EAA6E;IAC7E,OAAO,CAAC,eAAe,CAAM;IAE7B;;;;;;;;;;;OAWG;gBAEgB,IAAI,EAAE,WAAW,EACjB,YAAY,EAAE,WAAW,CAAC,MAAM,EAAE,WAAW,CAAC,EAC9C,KAAK,GAAE,WAAW,CAAC,MAAM,EAAE,gBAAgB,CAAa,EACxD,YAAY,GAAE,WAAW,CAAC,MAAM,EAAE,WAAW,CAAa;IAG7E;;;;;;;;;;;OAWG;IACH,IAAI,iBAAiB,IAAI,MAAM,CAM9B;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAmCG;IACH,cAAc,IAAI,MAAM;IAKxB;;;;OAIG;IACH,MAAM,CAAC,KAAK,EAAE,UAAU,GAAG,SAAS,GAAG,IAAI;IAwE3C;;;;;;;;;;;;;;;;;OAiBG;IACH,OAAO,IAAI,IAAI;IAiBf,OAAO,CAAC,YAAY;IAuDpB,OAAO,CAAC,UAAU;IA8JlB;;;;;;OAMG;IACH,OAAO,CAAC,UAAU;IAWlB,sEAAsE;IACtE,OAAO,CAAC,eAAe;IAwCvB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA2DG;IACH,OAAO,CAAC,YAAY;IAWpB;;;;;;;;;;;;;OAaG;IACH,OAAO,CAAC,OAAO;IAmCf;;;;;;;;;;OAUG;IACH,OAAO,CAAC,SAAS;IAoDjB,OAAO,CAAC,WAAW;IAOnB,yEAAyE;IACzE,OAAO,CAAC,YAAY;IAUpB,OAAO,CAAC,aAAa;IAUrB,OAAO,CAAC,eAAe;IAUvB,OAAO,CAAC,YAAY;IAOpB,OAAO,CAAC,WAAW;IA4CnB,OAAO,CAAC,aAAa;IAyBrB,OAAO,CAAC,IAAI;IAuDZ,OAAO,CAAC,IAAI;CAWb"}
@@ -46,6 +46,14 @@ export interface RegionImage {
46
46
  /** Unpacked height in atlas units — see `width`. */
47
47
  height: number;
48
48
  }
49
+ /** One region's page-backed source, without an encoded per-region URL. */
50
+ export interface AtlasRegion {
51
+ readonly page: PageLayout;
52
+ readonly region: PageRegion;
53
+ readonly image: HTMLImageElement;
54
+ }
55
+ /** Maps atlas entries in layout order; duplicate names keep the last entry. */
56
+ export declare function createAtlasRegions(pages: readonly PageLayout[], images: ReadonlyMap<string, HTMLImageElement>): Map<string, AtlasRegion>;
49
57
  /**
50
58
  * Page pixels in the alpha convention a DOM/canvas2d consumer needs: the page
51
59
  * image itself, or the derived canvas below when the page is premultiplied.
@@ -1 +1 @@
1
- {"version":3,"file":"DomTexture.d.ts","sourceRoot":"","sources":["../src/DomTexture.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;GAIG;AACH,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,kFAAkF;IAClF,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,SAAS,UAAU,EAAE,CAAC;CAChC;AAQD,MAAM,WAAW,WAAW;IAC1B;;;;OAIG;IACH,GAAG,EAAE,MAAM,CAAC;IACZ;;;;;;;;;OASG;IACH,KAAK,EAAE,MAAM,CAAC;IACd,oDAAoD;IACpD,MAAM,EAAE,MAAM,CAAC;CAChB;AAkBD;;;GAGG;AACH,MAAM,MAAM,UAAU,GAAG,gBAAgB,GAAG,iBAAiB,CAAC;AAqC9D;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,IAAI,MAAM,CAEjD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,gBAAgB,EAAE,GAAG,EAAE,OAAO,GAAG,UAAU,CAcpF;AAuSD;;;;GAIG;AACH,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,MAAM,CAAC;AAE7C,MAAM,WAAW,oBAAoB;IACnC,2BAA2B;IAC3B,QAAQ,CAAC,EAAE,cAAc,CAAC;CAC3B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4DG;AACH,wBAAsB,aAAa,CACjC,KAAK,EAAE,SAAS,UAAU,EAAE,EAC5B,UAAU,EAAE,WAAW,CAAC,MAAM,EAAE,gBAAgB,CAAC,EACjD,OAAO,GAAE,oBAAyB,GACjC,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CA4EnC;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,GAAG,IAAI,CAEpE"}
1
+ {"version":3,"file":"DomTexture.d.ts","sourceRoot":"","sources":["../src/DomTexture.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;GAIG;AACH,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,kFAAkF;IAClF,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,SAAS,UAAU,EAAE,CAAC;CAChC;AAQD,MAAM,WAAW,WAAW;IAC1B;;;;OAIG;IACH,GAAG,EAAE,MAAM,CAAC;IACZ;;;;;;;;;OASG;IACH,KAAK,EAAE,MAAM,CAAC;IACd,oDAAoD;IACpD,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,0EAA0E;AAC1E,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,QAAQ,CAAC,KAAK,EAAE,gBAAgB,CAAC;CAClC;AAED,+EAA+E;AAC/E,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,SAAS,UAAU,EAAE,EAC5B,MAAM,EAAE,WAAW,CAAC,MAAM,EAAE,gBAAgB,CAAC,GAC5C,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CAQ1B;AAwBD;;;GAGG;AACH,MAAM,MAAM,UAAU,GAAG,gBAAgB,GAAG,iBAAiB,CAAC;AAqC9D;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,IAAI,MAAM,CAEjD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,gBAAgB,EAAE,GAAG,EAAE,OAAO,GAAG,UAAU,CAcpF;AAiTD;;;;GAIG;AACH,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,MAAM,CAAC;AAE7C,MAAM,WAAW,oBAAoB;IACnC,2BAA2B;IAC3B,QAAQ,CAAC,EAAE,cAAc,CAAC;CAC3B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4DG;AACH,wBAAsB,aAAa,CACjC,KAAK,EAAE,SAAS,UAAU,EAAE,EAC5B,UAAU,EAAE,WAAW,CAAC,MAAM,EAAE,gBAAgB,CAAC,EACjD,OAAO,GAAE,oBAAyB,GACjC,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CA4EnC;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,GAAG,IAAI,CAEpE"}
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  export { type CanvasBackend, CanvasRenderer, type CanvasViewport } from './CanvasRenderer.js';
2
2
  export { DomRenderer, type MeshBackend, type MeshSourceRect } from './DomRenderer.js';
3
- export { type PageLayout, type PageRegion, type RegionEncoding, type RegionImage, revokeRegions, type UnpackRegionsOptions, unpackRegions } from './DomTexture.js';
3
+ export { type AtlasRegion, createAtlasRegions, type PageLayout, type PageRegion, type RegionEncoding, type RegionImage, revokeRegions, type UnpackRegionsOptions, unpackRegions } from './DomTexture.js';
4
4
  export { type LoadModelOptions, loadModel, type ModelAssets, type ModelDocument, readSetupMesh, type RegionLoading, type RigSet, type SetupMesh, type SetupMeshTarget } from './loadModel.js';
5
5
  export { type AdjustableBones, type BoneAdjust, type BoneAdjustContext, type BoneLocals, type BoneWorld, type PhysicsParameters, type PhysicsParameterPatch, type PhysicsParameterRead, type PlayOptions, type PoseBounds, type QueueOptions, type RenderMode, type RigComplete, type RigEnvironment, type RigEvent, RigPlayer, type RigPlayerOptions, type SetActivationOptions, type SlotFilter, type SlotIdentity, type SlotIdentityReason, type SlotIdentitySnapshot, type TrackEntry } from './player.js';
6
6
  export type { BlendMode, PosedClip, PosedFrame, PosedMesh, PosedRegion, PosedSlot, } from './posedFrame.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,aAAa,EAAE,cAAc,EAAE,KAAK,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC9F,OAAO,EAAE,WAAW,EAAE,KAAK,WAAW,EAAE,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACtF,OAAO,EAAE,KAAK,UAAU,EAAE,KAAK,UAAU,EAAE,KAAK,cAAc,EAAE,KAAK,WAAW,EAAE,aAAa,EAAE,KAAK,oBAAoB,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACnK,OAAO,EAAE,KAAK,gBAAgB,EAAE,SAAS,EAAE,KAAK,WAAW,EAAE,KAAK,aAAa,EAAE,aAAa,EAAE,KAAK,aAAa,EAAE,KAAK,MAAM,EAAE,KAAK,SAAS,EAAE,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAC9L,OAAO,EAAE,KAAK,eAAe,EAAE,KAAK,UAAU,EAAE,KAAK,iBAAiB,EAAE,KAAK,UAAU,EAAE,KAAK,SAAS,EAAE,KAAK,iBAAiB,EAAE,KAAK,qBAAqB,EAAE,KAAK,oBAAoB,EAAE,KAAK,WAAW,EAAE,KAAK,UAAU,EAAE,KAAK,YAAY,EAAE,KAAK,UAAU,EAAE,KAAK,WAAW,EAAE,KAAK,cAAc,EAAE,KAAK,QAAQ,EAAE,SAAS,EAAE,KAAK,gBAAgB,EAAE,KAAK,oBAAoB,EAAE,KAAK,UAAU,EAAE,KAAK,YAAY,EAAE,KAAK,kBAAkB,EAAE,KAAK,oBAAoB,EAAE,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC;AAC/e,YAAY,EACV,SAAS,EACT,SAAS,EACT,UAAU,EACV,SAAS,EACT,WAAW,EACX,SAAS,GACV,MAAM,iBAAiB,CAAC;AAEzB,YAAY,EAAE,SAAS,EAAE,WAAW,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,aAAa,EAAE,cAAc,EAAE,KAAK,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC9F,OAAO,EAAE,WAAW,EAAE,KAAK,WAAW,EAAE,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACtF,OAAO,EAAE,KAAK,WAAW,EAAE,kBAAkB,EAAE,KAAK,UAAU,EAAE,KAAK,UAAU,EAAE,KAAK,cAAc,EAAE,KAAK,WAAW,EAAE,aAAa,EAAE,KAAK,oBAAoB,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACzM,OAAO,EAAE,KAAK,gBAAgB,EAAE,SAAS,EAAE,KAAK,WAAW,EAAE,KAAK,aAAa,EAAE,aAAa,EAAE,KAAK,aAAa,EAAE,KAAK,MAAM,EAAE,KAAK,SAAS,EAAE,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAC9L,OAAO,EAAE,KAAK,eAAe,EAAE,KAAK,UAAU,EAAE,KAAK,iBAAiB,EAAE,KAAK,UAAU,EAAE,KAAK,SAAS,EAAE,KAAK,iBAAiB,EAAE,KAAK,qBAAqB,EAAE,KAAK,oBAAoB,EAAE,KAAK,WAAW,EAAE,KAAK,UAAU,EAAE,KAAK,YAAY,EAAE,KAAK,UAAU,EAAE,KAAK,WAAW,EAAE,KAAK,cAAc,EAAE,KAAK,QAAQ,EAAE,SAAS,EAAE,KAAK,gBAAgB,EAAE,KAAK,oBAAoB,EAAE,KAAK,UAAU,EAAE,KAAK,YAAY,EAAE,KAAK,kBAAkB,EAAE,KAAK,oBAAoB,EAAE,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC;AAC/e,YAAY,EACV,SAAS,EACT,SAAS,EACT,UAAU,EACV,SAAS,EACT,WAAW,EACX,SAAS,GACV,MAAM,iBAAiB,CAAC;AAEzB,YAAY,EAAE,SAAS,EAAE,WAAW,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC"}
package/dist/index.js CHANGED
@@ -964,7 +964,8 @@ function clipHalf(input, p, q) {
964
964
  return { points: out, cut };
965
965
  }
966
966
  function clipThrough(shape, vertices, triangles, uvs, clip = clipTriangles, reading = {}) {
967
- const plan = clipShapeOf(shape);
967
+ let plan = clipShapeOf(shape);
968
+ if (plan.kind === "pieces") plan = clipShapeOf({ ...shape, polygon: shape.polygon.map(Math.fround) });
968
969
  if (plan.kind === "refused") return null;
969
970
  if (plan.kind === "convex") return clip(plan.polygon, vertices, triangles, uvs);
970
971
  const f = Math.fround;
@@ -6703,6 +6704,19 @@ function drawnRegions(doc, shown, order, source) {
6703
6704
  if (unmeasured.length > 0) return { drawn: null, why: `${unmeasured.join("; ")} — which of the two counts the runtime steps it over was not measured` };
6704
6705
  return { drawn, why: null };
6705
6706
  }
6707
+ function createAtlasRegions(pages, images) {
6708
+ const result = /* @__PURE__ */ new Map();
6709
+ for (const page of pages) {
6710
+ const image = images.get(page.name);
6711
+ if (!image) throw new Error(`Missing page image: ${page.name}`);
6712
+ for (const region of page.regions) result.set(region.name, { page, region, image });
6713
+ }
6714
+ return result;
6715
+ }
6716
+ function drawAtlasRegion(canvas, entry, ratio) {
6717
+ const plan = planCut(entry, entry.image);
6718
+ paintRegion(canvas, entry, entry.image, plan, ratio);
6719
+ }
6706
6720
  const ownedUrls = /* @__PURE__ */ new Set();
6707
6721
  function revokeOwned(url) {
6708
6722
  if (!ownedUrls.delete(url)) return;
@@ -6794,16 +6808,7 @@ async function cutRegion(placed, pageImages, encoding) {
6794
6808
  const plan = planCut(placed, image);
6795
6809
  if (plan.passThrough) return { url: image.src, width: w, height: h };
6796
6810
  const canvas = document.createElement("canvas");
6797
- canvas.width = plan.cw;
6798
- canvas.height = plan.ch;
6799
- const ctx = canvas.getContext("2d");
6800
- if (!ctx) throw new Error("2d context unavailable");
6801
- if (region.degrees === 90) {
6802
- ctx.translate(plan.cw, 0);
6803
- ctx.rotate(Math.PI / 2);
6804
- }
6805
- const source = straightAlphaSource(image, page.pma === true);
6806
- ctx.drawImage(source, plan.sx, plan.sy, plan.sw, plan.sh, 0, 0, plan.sw, plan.sh);
6811
+ paintRegion(canvas, placed, image, plan);
6807
6812
  if (encoding === "data") return { url: canvas.toDataURL("image/png"), width: w, height: h };
6808
6813
  const blob = await new Promise((resolve, reject) => {
6809
6814
  canvas.toBlob((b) => b ? resolve(b) : reject(new Error("toBlob failed")), "image/png");
@@ -6812,6 +6817,22 @@ async function cutRegion(placed, pageImages, encoding) {
6812
6817
  ownedUrls.add(url);
6813
6818
  return { url, width: w, height: h };
6814
6819
  }
6820
+ function paintRegion(canvas, placed, image, plan, ratio) {
6821
+ canvas.width = ratio === void 0 ? plan.cw : Math.min(plan.cw, Math.max(1, Math.ceil(placed.region.width * ratio)));
6822
+ canvas.height = ratio === void 0 ? plan.ch : Math.min(plan.ch, Math.max(1, Math.ceil(placed.region.height * ratio)));
6823
+ const ctx = canvas.getContext("2d");
6824
+ if (!ctx) throw new Error("2d context unavailable");
6825
+ if (ratio !== void 0) {
6826
+ ctx.imageSmoothingQuality = "high";
6827
+ ctx.scale(canvas.width / plan.cw, canvas.height / plan.ch);
6828
+ }
6829
+ if (placed.region.degrees === 90) {
6830
+ ctx.translate(plan.cw, 0);
6831
+ ctx.rotate(Math.PI / 2);
6832
+ }
6833
+ const source = straightAlphaSource(image, placed.page.pma === true);
6834
+ ctx.drawImage(source, plan.sx, plan.sy, plan.sw, plan.sh, 0, 0, plan.sw, plan.sh);
6835
+ }
6815
6836
  async function unpackRegions(pages, pageImages, options = {}) {
6816
6837
  const encoding = options.encoding ?? "blob";
6817
6838
  if (encoding !== "blob" && encoding !== "data") throw new Error(`encoding must be 'blob' or 'data', got ${String(options.encoding)}`);
@@ -7403,14 +7424,17 @@ class DomRenderer {
7403
7424
  * and scaling of the root belong to the caller.
7404
7425
  * @param regionImages Unpacked per-region bitmaps (for the rigid tier),
7405
7426
  * keyed by region name — `PosedRegion.region`.
7427
+ * @param atlasRegions Optional page-backed rigid sources, from createAtlasRegions
7428
+ * or ModelAssets.atlasRegions. A matching entry takes precedence over regionImages.
7406
7429
  * @param pages Page images (for the mesh tier), keyed by page name —
7407
7430
  * `PosedMesh.page`. Read on every mesh draw, so a caller may fill it after
7408
7431
  * construction.
7409
7432
  */
7410
- constructor(root, regionImages, pages = /* @__PURE__ */ new Map()) {
7433
+ constructor(root, regionImages, pages = /* @__PURE__ */ new Map(), atlasRegions = /* @__PURE__ */ new Map()) {
7411
7434
  this.root = root;
7412
7435
  this.regionImages = regionImages;
7413
7436
  this.pages = pages;
7437
+ this.atlasRegions = atlasRegions;
7414
7438
  }
7415
7439
  /**
7416
7440
  * Apply the clips the frame carries (default). Each element drawn inside a
@@ -7435,6 +7459,8 @@ class DomRenderer {
7435
7459
  clipping = true;
7436
7460
  /** Clips applied last frame — distinct clip polygons over the drawn slots. */
7437
7461
  clipCount = 0;
7462
+ /** Atlas rigid canvases rasterized in the last frame; never includes meshes. */
7463
+ rigidRasterCount = 0;
7438
7464
  /**
7439
7465
  * Clips in the frame NOT applied last frame: `clipping` is false, or the
7440
7466
  * polygon has fewer than three points. A clip the runtime itself ignores (a
@@ -7604,6 +7630,7 @@ class DomRenderer {
7604
7630
  render(input) {
7605
7631
  const frame2 = renderFrameOf(input);
7606
7632
  this.clipCount = 0;
7633
+ this.rigidRasterCount = 0;
7607
7634
  this.clipSkipCount = 0;
7608
7635
  this.clipWriteCount = 0;
7609
7636
  this.meshCount = 0;
@@ -7690,16 +7717,10 @@ class DomRenderer {
7690
7717
  }
7691
7718
  // --- rigid tier -----------------------------------------------------------
7692
7719
  renderRegion(slot) {
7693
- const regionImage = this.regionImages.get(slot.region);
7720
+ const atlas = this.atlasRegions.get(slot.region);
7721
+ const regionImage = atlas ? atlas.region : this.regionImages.get(slot.region);
7694
7722
  if (!regionImage) return null;
7695
- const view = this.view(slot.slot, "image");
7696
- const img = view.el;
7697
- if (view.regionName !== slot.region) {
7698
- view.regionName = slot.region;
7699
- img.src = regionImage.url;
7700
- img.width = regionImage.width;
7701
- img.height = regionImage.height;
7702
- }
7723
+ const view = this.view(slot.slot, atlas ? "region-canvas" : "image");
7703
7724
  const corners = slot.corners;
7704
7725
  for (let v = 0; v < 8; v++) regionVertices[v] = corners[v];
7705
7726
  const blx = regionVertices[0], bly = -regionVertices[1];
@@ -7711,6 +7732,23 @@ class DomRenderer {
7711
7732
  const b = (ury - uly) / w;
7712
7733
  const c = (blx - ulx) / h;
7713
7734
  const d = (bly - uly) / h;
7735
+ const effective = this.pixelRatio * Math.max(Math.hypot(a, b), Math.hypot(c, d));
7736
+ const band = effective > 0 && effective < 1 ? 1.25 ** Math.ceil(Math.log(effective) / Math.log(1.25)) : 1;
7737
+ if (view.regionName !== slot.region || atlas && view.regionRatio !== band) {
7738
+ view.regionName = slot.region;
7739
+ view.regionRatio = band;
7740
+ if (atlas) {
7741
+ drawAtlasRegion(view.el, atlas, band < 1 ? band : void 0);
7742
+ view.el.style.width = `${w}px`;
7743
+ view.el.style.height = `${h}px`;
7744
+ this.rigidRasterCount++;
7745
+ } else {
7746
+ const img = view.el;
7747
+ img.src = this.regionImages.get(slot.region).url;
7748
+ img.width = w;
7749
+ img.height = h;
7750
+ }
7751
+ }
7714
7752
  this.setTransform(view, `matrix(${a},${b},${c},${d},${ulx},${uly})`);
7715
7753
  this.applyClip(view, a, b, c, d, ulx, uly, w, h);
7716
7754
  this.applyCommon(view, slot);
@@ -8135,6 +8173,7 @@ class DomRenderer {
8135
8173
  kind,
8136
8174
  el,
8137
8175
  regionName: "",
8176
+ regionRatio: -1,
8138
8177
  visible: false,
8139
8178
  zIndex: -1,
8140
8179
  opacity: 1,
@@ -8944,8 +8983,8 @@ async function loadModel(options) {
8944
8983
  );
8945
8984
  }
8946
8985
  const regions = options.regions ?? "blob";
8947
- if (regions !== "blob" && regions !== "data" && regions !== "none") {
8948
- throw new Error(`regions must be 'blob', 'data' or 'none', got ${String(options.regions)}`);
8986
+ if (regions !== "atlas" && regions !== "blob" && regions !== "data" && regions !== "none") {
8987
+ throw new Error(`regions must be 'atlas', 'blob', 'data' or 'none', got ${String(options.regions)}`);
8949
8988
  }
8950
8989
  const fetchImpl = options.fetch ?? globalThis.fetch.bind(globalThis);
8951
8990
  const resolvePage = options.resolvePage ?? resolveAgainstModel;
@@ -8958,7 +8997,7 @@ async function loadModel(options) {
8958
8997
  })
8959
8998
  );
8960
8999
  const sets = await loadSets(fetchImpl, modelUrl, options.setsUrl, read.doc);
8961
- const regionImages = regions === "none" ? /* @__PURE__ */ new Map() : await unpackRegions(pages, pageImages, { encoding: regions });
9000
+ const regionImages = regions === "none" || regions === "atlas" ? /* @__PURE__ */ new Map() : await unpackRegions(pages, pageImages, { encoding: regions });
8962
9001
  let disposed = false;
8963
9002
  return {
8964
9003
  spec: read.spec,
@@ -8968,6 +9007,7 @@ async function loadModel(options) {
8968
9007
  stage: stageOf(read.doc),
8969
9008
  regions,
8970
9009
  regionImages,
9010
+ atlasRegions: regions === "atlas" ? createAtlasRegions(pages, pageImages) : /* @__PURE__ */ new Map(),
8971
9011
  pageImages,
8972
9012
  document: read,
8973
9013
  dispose() {
@@ -9034,7 +9074,37 @@ function channelValue(curve, i, c, t) {
9034
9074
  const y0 = p[o + q - 1];
9035
9075
  return y0 + (t - x0) / (p[o + q] - x0) * (p[o + q + 1] - y0);
9036
9076
  }
9037
- const RUNTIME_STAGE = 1;
9077
+ function compileDeformCurve(keys) {
9078
+ const curve = compileCurve(keys.map((k) => ({ time: k.time, values: [0], curve: "linear", stated: { time: k.stated, values: [0] } })), 1);
9079
+ for (let i = 0; i < keys.length; i++) {
9080
+ const a = keys[i];
9081
+ curve.kinds[i] = a.curve === "linear" ? CURVE_LINEAR : a.curve === "stepped" ? CURVE_STEPPED : CURVE_BEZIER;
9082
+ if (typeof a.curve === "string" || i + 1 === keys.length) continue;
9083
+ const b = keys[i + 1];
9084
+ const c = a.curve;
9085
+ const inner = bezierPolyline(a.stated, 0, c[0], c[1], c[2], c[3], b.stated, DEFORM_CURVE_END);
9086
+ const o = i * POLYLINE;
9087
+ curve.polylines[o] = a.time;
9088
+ curve.polylines[o + 1] = 0;
9089
+ curve.polylines.set(inner, o + 2);
9090
+ curve.polylines[o + POLYLINE - 2] = b.time;
9091
+ curve.polylines[o + POLYLINE - 1] = 1;
9092
+ }
9093
+ return curve;
9094
+ }
9095
+ function deformFraction(curve, i, t) {
9096
+ if (curve.kinds[i] === CURVE_STEPPED) return 0;
9097
+ if (curve.kinds[i] === CURVE_LINEAR) return (t - curve.times[i]) / (curve.times[i + 1] - curve.times[i]);
9098
+ const p = curve.polylines;
9099
+ const o = i * POLYLINE;
9100
+ let q = 2;
9101
+ while (q < POLYLINE - 2 && p[o + q] < t) q += 2;
9102
+ const x0 = p[o + q - 2], y0 = p[o + q - 1], x1 = p[o + q], y1 = p[o + q + 1];
9103
+ if (q === 2) return y1 * (t - x0) / (x1 - x0);
9104
+ if (q === POLYLINE - 2) return y0 + (1 - y0) * (t - x0) / (x1 - x0);
9105
+ return y0 + (t - x0) / (x1 - x0) * (y1 - y0);
9106
+ }
9107
+ const RUNTIME_STAGE = 2;
9038
9108
  function missingText(m) {
9039
9109
  return `${m.feature}: ${m.detail}${m.animation === null ? "" : ` (animation "${m.animation}")`}`;
9040
9110
  }
@@ -9178,6 +9248,7 @@ function compileRuntime(doc, options = {}, plant = {}) {
9178
9248
  }
9179
9249
  const attachments = [];
9180
9250
  let vertexCount = 0;
9251
+ let deformCount = 0;
9181
9252
  const sourceOf = sourceOfDoc(doc);
9182
9253
  const attachmentView = plant.attachmentSkin === void 0 ? doc : underSkin(doc, plant.attachmentSkin);
9183
9254
  const resolvedBySlot = doc.slots.map(() => /* @__PURE__ */ new Map());
@@ -9198,7 +9269,7 @@ function compileRuntime(doc, options = {}, plant = {}) {
9198
9269
  if ("conflict" in shown) return { index: -1, missing: ["skin-conflict", `slot "${slot.name}" placeholder "${placeholder}" is filled by skins that disagree`] };
9199
9270
  const row = shownRow(shown);
9200
9271
  const g = shown.record.geometry;
9201
- const at = { slot: si, name: row.name, skin: shown.skin, placeholder: shown.placeholder, path: CORE_REGION_KINDS.has(shown.record.kind) ? row.path : null, bone: indexOf(slot.bone), offset: vertexCount };
9272
+ const at = { timeline: timelineIdentity(slot.name, shown), deformOffset: deformCount, slot: si, name: row.name, skin: shown.skin, placeholder: shown.placeholder, path: CORE_REGION_KINDS.has(shown.record.kind) ? row.path : null, bone: indexOf(slot.bone), offset: vertexCount };
9202
9273
  let made = null;
9203
9274
  let missing = null;
9204
9275
  if (g?.kind === "clipping") missing = ["clipping", `slot "${slot.name}" shows clipping attachment "${row.name}"`];
@@ -9240,6 +9311,7 @@ function compileRuntime(doc, options = {}, plant = {}) {
9240
9311
  if (made === null) made = vertexAttachment(at, DRAW_NONE, new Float64Array(0), 0);
9241
9312
  attachments.push(made);
9242
9313
  vertexCount += made.count;
9314
+ deformCount += made.deformCount;
9243
9315
  return { index: attachments.length - 1, missing };
9244
9316
  };
9245
9317
  const slots = doc.slots.map((slot, si) => {
@@ -9261,7 +9333,7 @@ function compileRuntime(doc, options = {}, plant = {}) {
9261
9333
  const refused = [];
9262
9334
  let cursorMax = 0;
9263
9335
  for (const anim of doc.animations) {
9264
- const compiled = compileAnimation(anim, refusals, ignoreEvents, boneIndex2, slotIndex, slots, constraints, (si, ph) => resolve(si, ph, anim.name));
9336
+ const compiled = compileAnimation(anim, refusals, ignoreEvents, boneIndex2, slotIndex, slots, constraints, doc, (si, ph) => resolve(si, ph, anim.name));
9265
9337
  const missing = refusals.byAnimation.get(anim.name);
9266
9338
  if (missing !== void 0 && missing.length > 0) refused.push({ animation: anim.name, missing });
9267
9339
  else {
@@ -9272,6 +9344,9 @@ function compileRuntime(doc, options = {}, plant = {}) {
9272
9344
  if (refusals.model.length > 0) throw new RuntimeRefusal(refusals.model);
9273
9345
  const names = doc.animations.map((a) => a.name);
9274
9346
  return {
9347
+ omitDeform: plant.omitDeform === true,
9348
+ deformAfterWeights: plant.deformAfterWeights === true,
9349
+ deformCount,
9275
9350
  eventWrapOffByOne: plant.eventWrapOffByOne === true,
9276
9351
  restoreEventsFromStart: plant.restoreEventsFromStart === true,
9277
9352
  doc,
@@ -9303,7 +9378,7 @@ function compileRuntime(doc, options = {}, plant = {}) {
9303
9378
  const NO_INTS = new Int32Array(0);
9304
9379
  const NO_NUMBERS = new Float64Array(0);
9305
9380
  function vertexAttachment(at, draw, local, count) {
9306
- return { ...at, draw, local, count, bindingCounts: NO_INTS, bindingBones: NO_INTS, bindingX: NO_NUMBERS, bindingY: NO_NUMBERS, bindingWeights: NO_NUMBERS };
9381
+ return { ...at, draw, local, count, deformCount: draw === DRAW_MESH ? count : 0, bindingCounts: NO_INTS, bindingBones: NO_INTS, bindingX: NO_NUMBERS, bindingY: NO_NUMBERS, bindingWeights: NO_NUMBERS };
9307
9382
  }
9308
9383
  function meshAttachment(at, vertices, indexOf, rewrite, bones) {
9309
9384
  if (!vertices.weighted) {
@@ -9327,7 +9402,7 @@ function meshAttachment(at, vertices, indexOf, rewrite, bones) {
9327
9402
  ws.push(Math.fround(b.weight));
9328
9403
  }
9329
9404
  }
9330
- return { ...at, draw: DRAW_WEIGHTED, local: NO_NUMBERS, count: counts.length * 2, bindingCounts: Int32Array.from(counts), bindingBones: Int32Array.from(ids), bindingX: Float64Array.from(xs), bindingY: Float64Array.from(ys), bindingWeights: Float64Array.from(ws) };
9405
+ return { ...at, deformCount: ids.length * 2, draw: DRAW_WEIGHTED, local: NO_NUMBERS, count: counts.length * 2, bindingCounts: Int32Array.from(counts), bindingBones: Int32Array.from(ids), bindingX: Float64Array.from(xs), bindingY: Float64Array.from(ys), bindingWeights: Float64Array.from(ws) };
9331
9406
  }
9332
9407
  function compileIk(r, index, applied, bones, indexOf) {
9333
9408
  return {
@@ -9387,13 +9462,12 @@ function compileTransform(r, index, applied, bones, indexOf) {
9387
9462
  clamp: r.clamp
9388
9463
  };
9389
9464
  }
9390
- function compileAnimation(anim, refusals, ignoreEvents, boneIndex2, slotIndex, slots, constraints, resolve) {
9465
+ function compileAnimation(anim, refusals, ignoreEvents, boneIndex2, slotIndex, slots, constraints, doc, resolve) {
9391
9466
  const tl = anim.timelines;
9392
9467
  const name = anim.name;
9393
9468
  let cursors = 0;
9394
9469
  const deform = tl.attachments.filter((a) => a.deform !== null);
9395
9470
  const sequence = tl.attachments.filter((a) => a.sequence !== null);
9396
- if (deform.length > 0) refusals.note("deform-timeline", `${deform.length} deform timeline(s): ${deform.slice(0, 3).map((a) => `${a.skin}/${a.slot}/${a.attachment}`).join(", ")}${deform.length > 3 ? ", …" : ""}`, name);
9397
9471
  if (sequence.length > 0) refusals.note("sequence-timeline", `${sequence.length} sequence timeline(s): ${sequence.slice(0, 3).map((a) => `${a.skin}/${a.slot}/${a.attachment}`).join(", ")}${sequence.length > 3 ? ", …" : ""}`, name);
9398
9472
  if (tl.drawOrder.length > 0) refusals.note("draw-order-timeline", `${tl.drawOrder.length} draw-order key(s)`, name);
9399
9473
  const lastBy = (targets) => new Map(targets.map((t) => [t.name, t]));
@@ -9441,7 +9515,14 @@ function compileAnimation(anim, refusals, ignoreEvents, boneIndex2, slotIndex, s
9441
9515
  constraintTimelines[c.index] = { constraint: c.index, curve: compileCurve(keyed.keys, TRANSFORM_PROPERTIES.length), cursor: cursors++, flags: null };
9442
9516
  }
9443
9517
  }
9444
- return { name, duration: tl.duration, bones, slots: slotTimelines, constraints: constraintTimelines, cursors, events: tl.events.length, eventKeys: Object.freeze(ignoreEvents ? [] : tl.events.map((key) => Object.freeze({ name: key.name, time: key.time, int: key.int, float: key.float, string: key.string, volume: key.volume ?? 0, balance: key.balance ?? 0 }))) };
9518
+ const deforms = deform.map((a) => {
9519
+ const g = doc.skins.find((s) => s.name === a.skin)?.attachments[a.slot]?.[a.attachment]?.geometry;
9520
+ if (g === void 0 || g.kind === "region" || g.kind === "linkedmesh") throw new RuntimeInputError(`deform "${a.skin}/${a.slot}/${a.attachment}" has no vertex geometry (readModel refuses it first)`);
9521
+ const keys = a.deform;
9522
+ const arrays = keys.map((k) => heldArray(g.vertices, k));
9523
+ return { identity: `${a.skin}/${a.slot}/${a.attachment}`, cursor: cursors++, curve: compileDeformCurve(keys), count: arrays[0].length, held: Float64Array.from(arrays.flat()) };
9524
+ });
9525
+ return { name, duration: tl.duration, deforms, bones, slots: slotTimelines, constraints: constraintTimelines, cursors, events: tl.events.length, eventKeys: Object.freeze(ignoreEvents ? [] : tl.events.map((key) => Object.freeze({ name: key.name, time: key.time, int: key.int, float: key.float, string: key.string, volume: key.volume ?? 0, balance: key.balance ?? 0 }))) };
9445
9526
  }
9446
9527
  const DEG = RUNTIME_DEG;
9447
9528
  const TWO_PI = 2 * RUNTIME_PI;
@@ -9474,6 +9555,8 @@ class RuntimePose {
9474
9555
  this.light = new Float64Array(ns * 4);
9475
9556
  this.shown = new Int32Array(ns);
9476
9557
  this.vertices = new Float64Array(model.vertexCount);
9558
+ this.deform = new Float64Array(model.deformCount);
9559
+ this.deformed = new Uint8Array(model.attachments.length);
9477
9560
  this.cursors = new Int32Array(Math.max(1, model.cursors)).fill(-1);
9478
9561
  this.ikMix = new Float64Array(nc);
9479
9562
  this.ikSoftness = new Float64Array(nc);
@@ -9523,6 +9606,7 @@ class RuntimePose {
9523
9606
  this.constraintValues(null, 0);
9524
9607
  this.solveConstraints();
9525
9608
  this.slotValues(null, 0);
9609
+ this.deformValues(null, 0);
9526
9610
  }
9527
9611
  /** The animation posed at its time `t`, from the setup pose (the header). */
9528
9612
  apply(t) {
@@ -9536,6 +9620,7 @@ class RuntimePose {
9536
9620
  this.constraintValues(anim, t);
9537
9621
  this.solveConstraints();
9538
9622
  this.slotValues(anim, t);
9623
+ this.deformValues(anim, t);
9539
9624
  }
9540
9625
  /** One step of a looping track: the track time moved by `dt`, the animation posed at it wrapped by the duration (`loopedTime` in src/core/raw.ts). */
9541
9626
  step(dt) {
@@ -10245,6 +10330,31 @@ class RuntimePose {
10245
10330
  }
10246
10331
  }
10247
10332
  }
10333
+ /** Reset on every pose, then match source identity after switches; no old frame's offsets survive. */
10334
+ deformValues(anim, t) {
10335
+ this.deformed.fill(0);
10336
+ this.deform.fill(0);
10337
+ if (anim === null || this.model.omitDeform) return;
10338
+ for (let k = 0; k < anim.deforms.length; k++) {
10339
+ const tl = anim.deforms[k];
10340
+ const i = keyAt(tl.curve, this.cursors, tl.cursor, t);
10341
+ if (i < 0) continue;
10342
+ const held = i + 1 === tl.curve.keys || tl.curve.kinds[i] === CURVE_STEPPED;
10343
+ const p = held ? 0 : deformFraction(tl.curve, i, t);
10344
+ const start = i * tl.count;
10345
+ for (let s = 0; s < this.shown.length; s++) {
10346
+ const index = this.shown[s];
10347
+ if (index < 0 || !this.model.slots[s].live) continue;
10348
+ const at = this.model.attachments[index];
10349
+ if (at.timeline !== tl.identity || at.deformCount === 0) continue;
10350
+ this.deformed[index] = 1;
10351
+ for (let q = 0; q < tl.count; q++) {
10352
+ const v = tl.held[start + q];
10353
+ this.deform[at.deformOffset + q] = held ? v : v + (tl.held[start + tl.count + q] - v) * p;
10354
+ }
10355
+ }
10356
+ }
10357
+ }
10248
10358
  /** Every shown attachment's world vertices into `vertices` at its offset (`regionCorners`, `worldVertices`). */
10249
10359
  computeVertices() {
10250
10360
  const m = this.model;
@@ -10266,7 +10376,8 @@ class RuntimePose {
10266
10376
  const d = W[b + W_D];
10267
10377
  const wx = W[b + W_X];
10268
10378
  const wy = W[b + W_Y];
10269
- const local = at.local;
10379
+ const local = this.deformed[index] && at.draw === DRAW_MESH ? this.deform : at.local;
10380
+ const lo = this.deformed[index] && at.draw === DRAW_MESH ? at.deformOffset : 0;
10270
10381
  if (at.draw === DRAW_REGION) {
10271
10382
  for (let q = 0; q < 8; q += 2) {
10272
10383
  const lx = local[q];
@@ -10276,8 +10387,8 @@ class RuntimePose {
10276
10387
  }
10277
10388
  } else {
10278
10389
  for (let q = 0; q < at.count; q += 2) {
10279
- const x = local[q];
10280
- const y = local[q + 1];
10390
+ const x = local[lo + q];
10391
+ const y = local[lo + q + 1];
10281
10392
  out[o++] = x * a + y * bb + wx;
10282
10393
  out[o++] = x * c + y * d + wy;
10283
10394
  }
@@ -10296,9 +10407,15 @@ class RuntimePose {
10296
10407
  const end = n + counts[v];
10297
10408
  for (; n < end; n++) {
10298
10409
  const b = bones[n] * WORLDS;
10299
- const x = xs[n];
10300
- const y = ys[n];
10301
10410
  const weight = weights[n];
10411
+ const deformed = this.deformed[index] !== 0;
10412
+ const x = deformed ? xs[n] + this.deform[at.deformOffset + n * 2] : xs[n];
10413
+ const y = deformed ? ys[n] + this.deform[at.deformOffset + n * 2 + 1] : ys[n];
10414
+ if (deformed && m.deformAfterWeights) {
10415
+ wx += (xs[n] * W[b + W_A] + ys[n] * W[b + W_B] + W[b + W_X]) * weight + this.deform[at.deformOffset + n * 2];
10416
+ wy += (xs[n] * W[b + W_C] + ys[n] * W[b + W_D] + W[b + W_Y]) * weight + this.deform[at.deformOffset + n * 2 + 1];
10417
+ continue;
10418
+ }
10302
10419
  wx += (x * W[b + W_A] + y * W[b + W_B] + W[b + W_X]) * weight;
10303
10420
  wy += (x * W[b + W_C] + y * W[b + W_D] + W[b + W_Y]) * weight;
10304
10421
  }
@@ -11195,10 +11312,10 @@ class RigPlayer {
11195
11312
  if (mode !== "dom") throw new Error(`mode must be 'dom' or 'canvas', got ${String(mode)}`);
11196
11313
  if (this.assets.regions === "none") {
11197
11314
  throw new Error(
11198
- "these assets were loaded with regions: 'none' (no region bitmaps, for the canvas mode only); the DOM mode needs a load with regions 'blob' or 'data'"
11315
+ "these assets were loaded with regions: 'none' (no region bitmaps, for the canvas mode only); the DOM mode needs a load with regions 'atlas', 'blob' or 'data'"
11199
11316
  );
11200
11317
  }
11201
- const renderer = new DomRenderer(this.root, this.assets.regionImages, this.assets.pageImages);
11318
+ const renderer = new DomRenderer(this.root, this.assets.regionImages, this.assets.pageImages, this.assets.atlasRegions);
11202
11319
  if (this.meshBackend) renderer.meshBackend = this.meshBackend;
11203
11320
  return renderer;
11204
11321
  }
@@ -12663,6 +12780,7 @@ export {
12663
12780
  CanvasRenderer,
12664
12781
  DomRenderer,
12665
12782
  RigPlayer,
12783
+ createAtlasRegions,
12666
12784
  loadModel,
12667
12785
  readSetupMesh,
12668
12786
  revokeRegions,