Parcourir la source

[ts] Skeleton.getBounds() applies clipping, see #2515. Port of commits b043e5c, 637321a and 2049bed.

Davide Tantillo il y a 1 an
Parent
commit
f3097222f9

+ 21 - 4
spine-ts/spine-core/src/Skeleton.ts

@@ -28,6 +28,7 @@
  *****************************************************************************/
 
 import { Attachment } from "./attachments/Attachment.js";
+import { ClippingAttachment } from "./attachments/ClippingAttachment.js";
 import { MeshAttachment } from "./attachments/MeshAttachment.js";
 import { PathAttachment } from "./attachments/PathAttachment.js";
 import { RegionAttachment } from "./attachments/RegionAttachment.js";
@@ -35,6 +36,7 @@ import { Bone } from "./Bone.js";
 import { IkConstraint } from "./IkConstraint.js";
 import { PathConstraint } from "./PathConstraint.js";
 import { PhysicsConstraint } from "./PhysicsConstraint.js";
+import { SkeletonClipping } from "./SkeletonClipping.js";
 import { SkeletonData } from "./SkeletonData.js";
 import { Skin } from "./Skin.js";
 import { Slot } from "./Slot.js";
@@ -46,6 +48,7 @@ import { Color, Utils, MathUtils, Vector2, NumberArrayLike } from "./Utils.js";
  *
  * See [Instance objects](http://esotericsoftware.com/spine-runtime-architecture#Instance-objects) in the Spine Runtimes Guide. */
 export class Skeleton {
+	private static quadTriangles = [0, 1, 2, 2, 3, 0];
 	static yDown = false;
 
 	/** The skeleton's setup pose data. */
@@ -606,8 +609,9 @@ export class Skeleton {
 	/** Returns the axis aligned bounding box (AABB) of the region and mesh attachments for the current pose.
 	 * @param offset An output value, the distance from the skeleton origin to the bottom left corner of the AABB.
 	 * @param size An output value, the width and height of the AABB.
-	 * @param temp Working memory to temporarily store attachments' computed world vertices. */
-	getBounds (offset: Vector2, size: Vector2, temp: Array<number> = new Array<number>(2)) {
+	 * @param temp Working memory to temporarily store attachments' computed world vertices.
+	 * @param clipper {@link SkeletonClipping} to use. If <code>null</code>, no clipping is applied. */
+	getBounds (offset: Vector2, size: Vector2, temp: Array<number> = new Array<number>(2), clipper: SkeletonClipping | null = null) {
 		if (!offset) throw new Error("offset cannot be null.");
 		if (!size) throw new Error("size cannot be null.");
 		let drawOrder = this.drawOrder;
@@ -617,18 +621,29 @@ export class Skeleton {
 			if (!slot.bone.active) continue;
 			let verticesLength = 0;
 			let vertices: NumberArrayLike | null = null;
+			let triangles: NumberArrayLike | null = null;
 			let attachment = slot.getAttachment();
 			if (attachment instanceof RegionAttachment) {
 				verticesLength = 8;
 				vertices = Utils.setArraySize(temp, verticesLength, 0);
-				(<RegionAttachment>attachment).computeWorldVertices(slot, vertices, 0, 2);
+				attachment.computeWorldVertices(slot, vertices, 0, 2);
+				triangles = Skeleton.quadTriangles;
 			} else if (attachment instanceof MeshAttachment) {
 				let mesh = (<MeshAttachment>attachment);
 				verticesLength = mesh.worldVerticesLength;
 				vertices = Utils.setArraySize(temp, verticesLength, 0);
 				mesh.computeWorldVertices(slot, 0, verticesLength, vertices, 0, 2);
+				triangles = mesh.triangles;
+			} else if (attachment instanceof ClippingAttachment && clipper != null) {
+				clipper.clipStart(slot, attachment);
+				continue;
 			}
-			if (vertices) {
+			if (vertices && triangles) {
+				if (clipper != null && clipper.isClipping()) {
+					clipper.clipTriangles(vertices, verticesLength, triangles, triangles.length);
+					vertices = clipper.clippedVertices;
+					verticesLength = clipper.clippedVertices.length;
+				}
 				for (let ii = 0, nn = vertices.length; ii < nn; ii += 2) {
 					let x = vertices[ii], y = vertices[ii + 1];
 					minX = Math.min(minX, x);
@@ -637,7 +652,9 @@ export class Skeleton {
 					maxY = Math.max(maxY, y);
 				}
 			}
+			if (clipper != null) clipper.clipEndWithSlot(slot);
 		}
+		if (clipper != null) clipper.clipEnd();
 		offset.set(minX, minY);
 		size.set(maxX - minX, maxY - minY);
 	}

+ 84 - 1
spine-ts/spine-core/src/SkeletonClipping.ts

@@ -80,7 +80,90 @@ export class SkeletonClipping {
 		return this.clipAttachment != null;
 	}
 
-	clipTriangles (vertices: NumberArrayLike, verticesLength: number, triangles: NumberArrayLike, trianglesLength: number, uvs: NumberArrayLike,
+	clipTriangles(vertices: NumberArrayLike, verticesLength: number, triangles: NumberArrayLike, trianglesLength: number): void;
+    clipTriangles(vertices: NumberArrayLike, verticesLength: number, triangles: NumberArrayLike, trianglesLength: number, uvs: NumberArrayLike,
+		light: Color, dark: Color, twoColor: boolean): void;
+    clipTriangles(vertices: NumberArrayLike, verticesLength: number, triangles: NumberArrayLike, trianglesLength: number, uvs?: NumberArrayLike,
+		light?: Color, dark?: Color, twoColor?: boolean): void {
+
+		if (uvs && light && dark && typeof twoColor === 'boolean')
+			this.clipTrianglesRender(vertices, verticesLength, triangles, trianglesLength, uvs, light, dark, twoColor);
+		else
+			this.clipTrianglesNoRender(vertices, verticesLength, triangles, trianglesLength);
+	}
+	private clipTrianglesNoRender (vertices: NumberArrayLike, verticesLength: number, triangles: NumberArrayLike, trianglesLength: number) {
+
+		let clipOutput = this.clipOutput, clippedVertices = this.clippedVertices;
+		let clippedTriangles = this.clippedTriangles;
+		let polygons = this.clippingPolygons!;
+		let polygonsCount = polygons.length;
+		let vertexSize = 2;
+
+		let index = 0;
+		clippedVertices.length = 0;
+		clippedTriangles.length = 0;
+		outer:
+		for (let i = 0; i < trianglesLength; i += 3) {
+			let vertexOffset = triangles[i] << 1;
+			let x1 = vertices[vertexOffset], y1 = vertices[vertexOffset + 1];
+
+			vertexOffset = triangles[i + 1] << 1;
+			let x2 = vertices[vertexOffset], y2 = vertices[vertexOffset + 1];
+
+			vertexOffset = triangles[i + 2] << 1;
+			let x3 = vertices[vertexOffset], y3 = vertices[vertexOffset + 1];
+
+			for (let p = 0; p < polygonsCount; p++) {
+				let s = clippedVertices.length;
+				if (this.clip(x1, y1, x2, y2, x3, y3, polygons[p], clipOutput)) {
+					let clipOutputLength = clipOutput.length;
+					if (clipOutputLength == 0) continue;
+
+					let clipOutputCount = clipOutputLength >> 1;
+					let clipOutputItems = this.clipOutput;
+					let clippedVerticesItems = Utils.setArraySize(clippedVertices, s + clipOutputCount * vertexSize);
+					for (let ii = 0; ii < clipOutputLength; ii += 2) {
+						let x = clipOutputItems[ii], y = clipOutputItems[ii + 1];
+						clippedVerticesItems[s] = x;
+						clippedVerticesItems[s + 1] = y;
+						s += 2;
+					}
+
+					s = clippedTriangles.length;
+					let clippedTrianglesItems = Utils.setArraySize(clippedTriangles, s + 3 * (clipOutputCount - 2));
+					clipOutputCount--;
+					for (let ii = 1; ii < clipOutputCount; ii++) {
+						clippedTrianglesItems[s] = index;
+						clippedTrianglesItems[s + 1] = (index + ii);
+						clippedTrianglesItems[s + 2] = (index + ii + 1);
+						s += 3;
+					}
+					index += clipOutputCount + 1;
+
+				} else {
+					let clippedVerticesItems = Utils.setArraySize(clippedVertices, s + 3 * vertexSize);
+					clippedVerticesItems[s] = x1;
+					clippedVerticesItems[s + 1] = y1;
+
+					clippedVerticesItems[s + 2] = x2;
+					clippedVerticesItems[s + 3] = y2;
+
+					clippedVerticesItems[s + 4] = x3;
+					clippedVerticesItems[s + 5] = y3;
+
+					s = clippedTriangles.length;
+					let clippedTrianglesItems = Utils.setArraySize(clippedTriangles, s + 3);
+					clippedTrianglesItems[s] = index;
+					clippedTrianglesItems[s + 1] = (index + 1);
+					clippedTrianglesItems[s + 2] = (index + 2);
+					index += 3;
+					continue outer;
+				}
+			}
+		}
+	}
+
+	private clipTrianglesRender (vertices: NumberArrayLike, verticesLength: number, triangles: NumberArrayLike, trianglesLength: number, uvs: NumberArrayLike,
 		light: Color, dark: Color, twoColor: boolean) {
 
 		let clipOutput = this.clipOutput, clippedVertices = this.clippedVertices;

+ 5 - 0
spine-ts/spine-webgl/src/SkeletonRenderer.ts

@@ -205,4 +205,9 @@ export class SkeletonRenderer {
 		}
 		clipper.clipEnd();
 	}
+
+	/** Returns the {@link SkeletonClipping} used by this renderer for use with e.g. {@link Skeleton.getBounds} **/
+	public getSkeletonClipping (): SkeletonClipping {
+		return this.clipper;
+	}
 }