Skip to content

Commit f309722

Browse files
committed
[ts] Skeleton.getBounds() applies clipping, see #2515. Port of commits b043e5c, 637321a and 2049bed.
1 parent b343543 commit f309722

File tree

3 files changed

+110
-5
lines changed

3 files changed

+110
-5
lines changed

spine-ts/spine-core/src/Skeleton.ts

+21-4
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,15 @@
2828
*****************************************************************************/
2929

3030
import { Attachment } from "./attachments/Attachment.js";
31+
import { ClippingAttachment } from "./attachments/ClippingAttachment.js";
3132
import { MeshAttachment } from "./attachments/MeshAttachment.js";
3233
import { PathAttachment } from "./attachments/PathAttachment.js";
3334
import { RegionAttachment } from "./attachments/RegionAttachment.js";
3435
import { Bone } from "./Bone.js";
3536
import { IkConstraint } from "./IkConstraint.js";
3637
import { PathConstraint } from "./PathConstraint.js";
3738
import { PhysicsConstraint } from "./PhysicsConstraint.js";
39+
import { SkeletonClipping } from "./SkeletonClipping.js";
3840
import { SkeletonData } from "./SkeletonData.js";
3941
import { Skin } from "./Skin.js";
4042
import { Slot } from "./Slot.js";
@@ -46,6 +48,7 @@ import { Color, Utils, MathUtils, Vector2, NumberArrayLike } from "./Utils.js";
4648
*
4749
* See [Instance objects](http://esotericsoftware.com/spine-runtime-architecture#Instance-objects) in the Spine Runtimes Guide. */
4850
export class Skeleton {
51+
private static quadTriangles = [0, 1, 2, 2, 3, 0];
4952
static yDown = false;
5053

5154
/** The skeleton's setup pose data. */
@@ -606,8 +609,9 @@ export class Skeleton {
606609
/** Returns the axis aligned bounding box (AABB) of the region and mesh attachments for the current pose.
607610
* @param offset An output value, the distance from the skeleton origin to the bottom left corner of the AABB.
608611
* @param size An output value, the width and height of the AABB.
609-
* @param temp Working memory to temporarily store attachments' computed world vertices. */
610-
getBounds (offset: Vector2, size: Vector2, temp: Array<number> = new Array<number>(2)) {
612+
* @param temp Working memory to temporarily store attachments' computed world vertices.
613+
* @param clipper {@link SkeletonClipping} to use. If <code>null</code>, no clipping is applied. */
614+
getBounds (offset: Vector2, size: Vector2, temp: Array<number> = new Array<number>(2), clipper: SkeletonClipping | null = null) {
611615
if (!offset) throw new Error("offset cannot be null.");
612616
if (!size) throw new Error("size cannot be null.");
613617
let drawOrder = this.drawOrder;
@@ -617,18 +621,29 @@ export class Skeleton {
617621
if (!slot.bone.active) continue;
618622
let verticesLength = 0;
619623
let vertices: NumberArrayLike | null = null;
624+
let triangles: NumberArrayLike | null = null;
620625
let attachment = slot.getAttachment();
621626
if (attachment instanceof RegionAttachment) {
622627
verticesLength = 8;
623628
vertices = Utils.setArraySize(temp, verticesLength, 0);
624-
(<RegionAttachment>attachment).computeWorldVertices(slot, vertices, 0, 2);
629+
attachment.computeWorldVertices(slot, vertices, 0, 2);
630+
triangles = Skeleton.quadTriangles;
625631
} else if (attachment instanceof MeshAttachment) {
626632
let mesh = (<MeshAttachment>attachment);
627633
verticesLength = mesh.worldVerticesLength;
628634
vertices = Utils.setArraySize(temp, verticesLength, 0);
629635
mesh.computeWorldVertices(slot, 0, verticesLength, vertices, 0, 2);
636+
triangles = mesh.triangles;
637+
} else if (attachment instanceof ClippingAttachment && clipper != null) {
638+
clipper.clipStart(slot, attachment);
639+
continue;
630640
}
631-
if (vertices) {
641+
if (vertices && triangles) {
642+
if (clipper != null && clipper.isClipping()) {
643+
clipper.clipTriangles(vertices, verticesLength, triangles, triangles.length);
644+
vertices = clipper.clippedVertices;
645+
verticesLength = clipper.clippedVertices.length;
646+
}
632647
for (let ii = 0, nn = vertices.length; ii < nn; ii += 2) {
633648
let x = vertices[ii], y = vertices[ii + 1];
634649
minX = Math.min(minX, x);
@@ -637,7 +652,9 @@ export class Skeleton {
637652
maxY = Math.max(maxY, y);
638653
}
639654
}
655+
if (clipper != null) clipper.clipEndWithSlot(slot);
640656
}
657+
if (clipper != null) clipper.clipEnd();
641658
offset.set(minX, minY);
642659
size.set(maxX - minX, maxY - minY);
643660
}

spine-ts/spine-core/src/SkeletonClipping.ts

+84-1
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,90 @@ export class SkeletonClipping {
8080
return this.clipAttachment != null;
8181
}
8282

83-
clipTriangles (vertices: NumberArrayLike, verticesLength: number, triangles: NumberArrayLike, trianglesLength: number, uvs: NumberArrayLike,
83+
clipTriangles(vertices: NumberArrayLike, verticesLength: number, triangles: NumberArrayLike, trianglesLength: number): void;
84+
clipTriangles(vertices: NumberArrayLike, verticesLength: number, triangles: NumberArrayLike, trianglesLength: number, uvs: NumberArrayLike,
85+
light: Color, dark: Color, twoColor: boolean): void;
86+
clipTriangles(vertices: NumberArrayLike, verticesLength: number, triangles: NumberArrayLike, trianglesLength: number, uvs?: NumberArrayLike,
87+
light?: Color, dark?: Color, twoColor?: boolean): void {
88+
89+
if (uvs && light && dark && typeof twoColor === 'boolean')
90+
this.clipTrianglesRender(vertices, verticesLength, triangles, trianglesLength, uvs, light, dark, twoColor);
91+
else
92+
this.clipTrianglesNoRender(vertices, verticesLength, triangles, trianglesLength);
93+
}
94+
private clipTrianglesNoRender (vertices: NumberArrayLike, verticesLength: number, triangles: NumberArrayLike, trianglesLength: number) {
95+
96+
let clipOutput = this.clipOutput, clippedVertices = this.clippedVertices;
97+
let clippedTriangles = this.clippedTriangles;
98+
let polygons = this.clippingPolygons!;
99+
let polygonsCount = polygons.length;
100+
let vertexSize = 2;
101+
102+
let index = 0;
103+
clippedVertices.length = 0;
104+
clippedTriangles.length = 0;
105+
outer:
106+
for (let i = 0; i < trianglesLength; i += 3) {
107+
let vertexOffset = triangles[i] << 1;
108+
let x1 = vertices[vertexOffset], y1 = vertices[vertexOffset + 1];
109+
110+
vertexOffset = triangles[i + 1] << 1;
111+
let x2 = vertices[vertexOffset], y2 = vertices[vertexOffset + 1];
112+
113+
vertexOffset = triangles[i + 2] << 1;
114+
let x3 = vertices[vertexOffset], y3 = vertices[vertexOffset + 1];
115+
116+
for (let p = 0; p < polygonsCount; p++) {
117+
let s = clippedVertices.length;
118+
if (this.clip(x1, y1, x2, y2, x3, y3, polygons[p], clipOutput)) {
119+
let clipOutputLength = clipOutput.length;
120+
if (clipOutputLength == 0) continue;
121+
122+
let clipOutputCount = clipOutputLength >> 1;
123+
let clipOutputItems = this.clipOutput;
124+
let clippedVerticesItems = Utils.setArraySize(clippedVertices, s + clipOutputCount * vertexSize);
125+
for (let ii = 0; ii < clipOutputLength; ii += 2) {
126+
let x = clipOutputItems[ii], y = clipOutputItems[ii + 1];
127+
clippedVerticesItems[s] = x;
128+
clippedVerticesItems[s + 1] = y;
129+
s += 2;
130+
}
131+
132+
s = clippedTriangles.length;
133+
let clippedTrianglesItems = Utils.setArraySize(clippedTriangles, s + 3 * (clipOutputCount - 2));
134+
clipOutputCount--;
135+
for (let ii = 1; ii < clipOutputCount; ii++) {
136+
clippedTrianglesItems[s] = index;
137+
clippedTrianglesItems[s + 1] = (index + ii);
138+
clippedTrianglesItems[s + 2] = (index + ii + 1);
139+
s += 3;
140+
}
141+
index += clipOutputCount + 1;
142+
143+
} else {
144+
let clippedVerticesItems = Utils.setArraySize(clippedVertices, s + 3 * vertexSize);
145+
clippedVerticesItems[s] = x1;
146+
clippedVerticesItems[s + 1] = y1;
147+
148+
clippedVerticesItems[s + 2] = x2;
149+
clippedVerticesItems[s + 3] = y2;
150+
151+
clippedVerticesItems[s + 4] = x3;
152+
clippedVerticesItems[s + 5] = y3;
153+
154+
s = clippedTriangles.length;
155+
let clippedTrianglesItems = Utils.setArraySize(clippedTriangles, s + 3);
156+
clippedTrianglesItems[s] = index;
157+
clippedTrianglesItems[s + 1] = (index + 1);
158+
clippedTrianglesItems[s + 2] = (index + 2);
159+
index += 3;
160+
continue outer;
161+
}
162+
}
163+
}
164+
}
165+
166+
private clipTrianglesRender (vertices: NumberArrayLike, verticesLength: number, triangles: NumberArrayLike, trianglesLength: number, uvs: NumberArrayLike,
84167
light: Color, dark: Color, twoColor: boolean) {
85168

86169
let clipOutput = this.clipOutput, clippedVertices = this.clippedVertices;

spine-ts/spine-webgl/src/SkeletonRenderer.ts

+5
Original file line numberDiff line numberDiff line change
@@ -205,4 +205,9 @@ export class SkeletonRenderer {
205205
}
206206
clipper.clipEnd();
207207
}
208+
209+
/** Returns the {@link SkeletonClipping} used by this renderer for use with e.g. {@link Skeleton.getBounds} **/
210+
public getSkeletonClipping (): SkeletonClipping {
211+
return this.clipper;
212+
}
208213
}

0 commit comments

Comments
 (0)