* Apply the current frame's texture region to the quad — the geometry hook * called by the shared FrameAnimation engine (the 3D counterpart of * Sprite's sub-texture / size / anchor swap). Rewrites both the quad's * UVs (the atlas sub-rect, with a 90° corner permutation for pac
(region)
| 532 | * @ignore |
| 533 | */ |
| 534 | _applyFrame(region) { |
| 535 | // remember the region so a later flipX/flipY can re-map immediately |
| 536 | this._region = region; |
| 537 | // the texture backing this frame (full sheet for a spritesheet, the atlas |
| 538 | // page for a packed atlas) |
| 539 | const image = this.source.getTexture(region); |
| 540 | const iw = image.width; |
| 541 | const ih = image.height; |
| 542 | |
| 543 | // `region.width`/`height` are the art's UNROTATED dimensions; a |
| 544 | // packer-rotated region occupies a height×width box in the atlas. |
| 545 | const rotated = region.angle !== 0; |
| 546 | const aw = region.width; |
| 547 | const ah = region.height; |
| 548 | const atlasW = rotated ? ah : aw; |
| 549 | const atlasH = rotated ? aw : ah; |
| 550 | |
| 551 | // ── UVs: the atlas AABB, corners permuted 90° when the region is rotated |
| 552 | const ox = region.offset.x; |
| 553 | const oy = region.offset.y; |
| 554 | const uL = ox / iw; |
| 555 | const uR = (ox + atlasW) / iw; |
| 556 | // texture-space top is the smaller pixel y; the baked UVs put v=0 at the |
| 557 | // top corners (local +y) so the frame lands upright under Y-down. |
| 558 | const vT = oy / ih; |
| 559 | const vB = (oy + atlasH) / ih; |
| 560 | const uv = this.uvs; |
| 561 | // corners: 0=(left,bottom) 1=(right,bottom) 2=(right,top) 3=(left,top) |
| 562 | if (rotated) { |
| 563 | // TexturePacker stores rotated art 90° clockwise, so undo it by |
| 564 | // rotating the corner→UV assignment: the quad's left edge samples the |
| 565 | // atlas top row, its top edge samples the atlas right column. |
| 566 | uv[0] = uL; |
| 567 | uv[1] = vT; // BL → atlas top-left |
| 568 | uv[2] = uL; |
| 569 | uv[3] = vB; // BR → atlas bottom-left |
| 570 | uv[4] = uR; |
| 571 | uv[5] = vB; // TR → atlas bottom-right |
| 572 | uv[6] = uR; |
| 573 | uv[7] = vT; // TL → atlas top-right |
| 574 | } else { |
| 575 | uv[0] = uL; |
| 576 | uv[1] = vB; |
| 577 | uv[2] = uR; |
| 578 | uv[3] = vB; |
| 579 | uv[4] = uR; |
| 580 | uv[5] = vT; |
| 581 | uv[6] = uL; |
| 582 | uv[7] = vT; |
| 583 | } |
| 584 | |
| 585 | // ── geometry: place the (possibly trimmed) art rect inside the logical |
| 586 | // frame. Logical size is the untrimmed sourceSize; the art occupies a |
| 587 | // sub-rect offset by `trim`. The world quad (±_halfW/±_halfH) maps the |
| 588 | // logical frame captured from the first applied frame, so trimmed frames |
| 589 | // keep their position/size relative to the full sprite footprint. |
| 590 | const trimmed = region.trimmed === true && region.sourceSize != null; |
| 591 | const logicalW = trimmed ? region.sourceSize.w : aw; |
no test coverage detected