Эх сурвалжийг харах

Bounding boxes for spine-as3.

NathanSweet 12 жил өмнө
parent
commit
4c487f38c6

+ 82 - 0
spine-as3/spine-as3/src/spine/Polygon.as

@@ -0,0 +1,82 @@
+/******************************************************************************
+ * Spine Runtime Software License - Version 1.1
+ * 
+ * Copyright (c) 2013, Esoteric Software
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms in whole or in part, with
+ * or without modification, are permitted provided that the following conditions
+ * are met:
+ * 
+ * 1. A Spine Essential, Professional, Enterprise, or Education License must
+ *    be purchased from Esoteric Software and the license must remain valid:
+ *    http://esotericsoftware.com/
+ * 2. Redistributions of source code must retain this license, which is the
+ *    above copyright notice, this declaration of conditions and the following
+ *    disclaimer.
+ * 3. Redistributions in binary form must reproduce this license, which is the
+ *    above copyright notice, this declaration of conditions and the following
+ *    disclaimer, in the documentation and/or other materials provided with the
+ *    distribution.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+package spine {
+
+public class Polygon {
+	public var vertices:Vector.<Number> = new Vector.<Number>();
+	
+	/// <summary>Returns true if the polygon contains the line segment.</summary>
+	public function intersectsSegment (x1:Number, y1:Number, x2:Number, y2:Number) : Boolean {
+		var nn:int = vertices.length;
+		
+		var width12:Number = x1 - x2, height12:Number = y1 - y2;
+		var det1:Number = x1 * y2 - y1 * x2;
+		var x3:Number = vertices[nn - 2], y3:Number = vertices[nn - 1];
+		for (var ii:int = 0; ii < nn; ii += 2) {
+			var x4:Number = vertices[ii], y4:Number = vertices[ii + 1];
+			var det2:Number = x3 * y4 - y3 * x4;
+			var width34:Number = x3 - x4, height34:Number = y3 - y4;
+			var det3:Number = width12 * height34 - height12 * width34;
+			var x:Number = (det1 * width34 - width12 * det2) / det3;
+			if (((x >= x3 && x <= x4) || (x >= x4 && x <= x3)) && ((x >= x1 && x <= x2) || (x >= x2 && x <= x1))) {
+				var y:Number = (det1 * height34 - height12 * det2) / det3;
+				if (((y >= y3 && y <= y4) || (y >= y4 && y <= y3)) && ((y >= y1 && y <= y2) || (y >= y2 && y <= y1))) return true;
+			}
+			x3 = x4;
+			y3 = y4;
+		}
+		return false;
+	}
+	
+	/// <summary>Returns true if the polygon contains the point.</summary>
+	public function containsPoint (x:Number, y:Number) : Boolean {
+		var nn:int = vertices.length;
+		
+		var prevIndex:int = nn - 2;
+		var inside:Boolean = false;
+		for (var ii:int = 0; ii < nn; ii += 2) {
+			var vertexY:Number = vertices[ii + 1];
+			var prevY:Number = vertices[prevIndex + 1];
+			if ((vertexY < y && prevY >= y) || (prevY < y && vertexY >= y)) {
+				var vertexX:Number = vertices[ii];
+				if (vertexX + (y - vertexY) / (prevY - vertexY) * (vertices[prevIndex] - vertexX) < x) inside = !inside;
+			}
+			prevIndex = ii;
+		}
+		
+		return inside;
+	}
+}
+
+}

+ 153 - 0
spine-as3/spine-as3/src/spine/SkeletonBounds.as

@@ -0,0 +1,153 @@
+/******************************************************************************
+ * Spine Runtime Software License - Version 1.1
+ * 
+ * Copyright (c) 2013, Esoteric Software
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms in whole or in part, with
+ * or without modification, are permitted provided that the following conditions
+ * are met:
+ * 
+ * 1. A Spine Essential, Professional, Enterprise, or Education License must
+ *    be purchased from Esoteric Software and the license must remain valid:
+ *    http://esotericsoftware.com/
+ * 2. Redistributions of source code must retain this license, which is the
+ *    above copyright notice, this declaration of conditions and the following
+ *    disclaimer.
+ * 3. Redistributions in binary form must reproduce this license, which is the
+ *    above copyright notice, this declaration of conditions and the following
+ *    disclaimer, in the documentation and/or other materials provided with the
+ *    distribution.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+package spine {
+import spine.attachments.Attachment;
+import spine.attachments.BoundingBoxAttachment;
+
+public class SkeletonBounds {
+	private var polygonPool:Vector.<Polygon> = new Vector.<Polygon>();
+
+	public var boundingBoxes:Vector.<BoundingBoxAttachment> = new Vector.<BoundingBoxAttachment>();
+	public var polygons:Vector.<Polygon> = new Vector.<Polygon>();
+	public var minX:Number, minY:Number, maxX:Number, maxY:Number;
+
+	public function update (skeleton:Skeleton, updateAabb:Boolean) : void {
+		var slots:Vector.<Slot> = skeleton.slots;
+		var slotCount:int = slots.length;
+		var x:Number = skeleton.x, y:Number = skeleton.y;
+		
+		boundingBoxes.length = 0;
+		for each (var polygon:Polygon in polygons)
+			polygonPool.push(polygon);
+		polygons.length = 0;
+
+		for (var i:int = 0; i < slotCount; i++) {
+			var slot:Slot = slots[i];
+			var boundingBox:BoundingBoxAttachment = slot.attachment as BoundingBoxAttachment;
+			if (boundingBox == null) continue;
+			boundingBoxes.push(boundingBox);
+
+			var poolCount:int = polygonPool.length;
+			if (poolCount > 0) {
+				polygon = polygonPool[poolCount - 1];
+				polygonPool.splice(poolCount - 1, 1);
+			} else
+				polygon = new Polygon();
+			polygons.push(polygon);
+
+			polygon.vertices.length = boundingBox.vertices.length;
+			boundingBox.computeWorldVertices(x, y, slot.bone, polygon.vertices);
+		}
+
+		if (updateAabb) aabbCompute();
+	}
+
+	private function aabbCompute () : void {
+		var minX:Number = int.MAX_VALUE, minY:Number = int.MAX_VALUE, maxX:Number = int.MIN_VALUE, maxY:Number = int.MIN_VALUE;
+		for (var i:int = 0, n:int = polygons.length; i < n; i++) {
+			var polygon:Polygon = polygons[i];
+			var vertices:Vector.<Number> = polygon.vertices;
+			for (var ii:int = 0, nn:int = vertices.length; ii < nn; ii += 2) {
+				var x:Number = vertices[ii];
+				var y:Number = vertices[ii + 1];
+				minX = Math.min(minX, x);
+				minY = Math.min(minY, y);
+				maxX = Math.max(maxX, x);
+				maxY = Math.max(maxY, y);
+			}
+		}
+		this.minX = minX;
+		this.minY = minY;
+		this.maxX = maxX;
+		this.maxY = maxY;
+	}
+	
+	
+	/// <summary>Returns true if the axis aligned bounding box contains the point.</summary>
+	public function aabbContainsPoint (x:Number, y:Number) : Boolean {
+		return x >= minX && x <= maxX && y >= minY && y <= maxY;
+	}
+	
+	/// <summary>Returns true if the axis aligned bounding box intersects the line segment.</summary>
+	public function aabbIntersectsSegment (x1:Number, y1:Number, x2:Number, y2:Number) : Boolean {
+		if ((x1 <= minX && x2 <= minX) || (y1 <= minY && y2 <= minY) || (x1 >= maxX && x2 >= maxX) || (y1 >= maxY && y2 >= maxY))
+			return false;
+		var m:Number = (y2 - y1) / (x2 - x1);
+		var y:Number = m * (minX - x1) + y1;
+		if (y > minY && y < maxY) return true;
+		y = m * (maxX - x1) + y1;
+		if (y > minY && y < maxY) return true;
+		var x:Number = (minY - y1) / m + x1;
+		if (x > minX && x < maxX) return true;
+		x = (maxY - y1) / m + x1;
+		if (x > minX && x < maxX) return true;
+		return false;
+	}
+	
+	/// <summary>Returns true if the axis aligned bounding box intersects the axis aligned bounding box of the specified bounds.</summary>
+	public function aabbIntersectsSkeleton (bounds:SkeletonBounds) : Boolean {
+		return minX < bounds.maxX && maxX > bounds.minX && minY < bounds.maxY && maxY > bounds.minY;
+	}
+	
+	/// <summary>Returns the first bounding box attachment that contains the point, or null. When doing many checks, it is usually more
+	/// efficient to only call this method if {@link #aabbContainsPoint(float, float)} returns true.</summary>
+	public function containsPoint (x:Number, y:Number) : BoundingBoxAttachment {
+		for (var i:int = 0, n:int = polygons.length; i < n; i++)
+			if (polygons[i].containsPoint(x, y)) return boundingBoxes[i];
+		return null;
+	}
+	
+	/// <summary>Returns the first bounding box attachment that contains the line segment, or null. When doing many checks, it is usually
+	/// more efficient to only call this method if {@link #aabbIntersectsSegment(float, float, float, float)} returns true.</summary>
+	public function intersectsSegment (x1:Number, y1:Number, x2:Number, y2:Number) : BoundingBoxAttachment {
+		for (var i:int = 0, n:int = polygons.length; i < n; i++)
+			if (polygons[i].intersectsSegment(x1, y1, x2, y2)) return boundingBoxes[i];
+		return null;
+	}
+
+	public function getPolygon (attachment:BoundingBoxAttachment) : Polygon {
+		var index:int = boundingBoxes.indexOf(attachment);
+		return index == -1 ? null : polygons[index];
+	}
+
+	public function get width () : Number {
+		return maxX - minX;
+	}
+	
+	public function get height () : Number {
+		return maxY - minY;
+	}
+}
+
+}

+ 5 - 0
spine-as3/spine-as3/src/spine/SkeletonJson.as

@@ -47,6 +47,7 @@ import spine.animation.TranslateTimeline;
 import spine.attachments.Attachment;
 import spine.attachments.AttachmentLoader;
 import spine.attachments.AttachmentType;
+import spine.attachments.BoundingBoxAttachment;
 import spine.attachments.RegionAttachment;
 
 public class SkeletonJson {
@@ -181,6 +182,10 @@ public class SkeletonJson {
 			regionAttachment.width = (map["width"] || 32) * scale;
 			regionAttachment.height = (map["height"] || 32) * scale;
 			regionAttachment.updateOffset();
+		} else if (attachment is BoundingBoxAttachment) {
+			var box:BoundingBoxAttachment = attachment as BoundingBoxAttachment;
+			for each (var point:Number in map["vertices"])
+				box.vertices.push(point * scale);
 		}
 
 		return attachment;