浏览代码

MeshAttachment support.

NathanSweet 11 年之前
父节点
当前提交
1c9973ef1d

+ 47 - 7
spine-libgdx/src/com/esotericsoftware/spine/SkeletonBinary.java

@@ -47,6 +47,7 @@ import com.esotericsoftware.spine.attachments.Attachment;
 import com.esotericsoftware.spine.attachments.AttachmentLoader;
 import com.esotericsoftware.spine.attachments.AttachmentType;
 import com.esotericsoftware.spine.attachments.BoundingBoxAttachment;
+import com.esotericsoftware.spine.attachments.MeshAttachment;
 import com.esotericsoftware.spine.attachments.RegionAttachment;
 
 import com.badlogic.gdx.files.FileHandle;
@@ -188,7 +189,7 @@ public class SkeletonBinary {
 		if (name == null) name = attachmentName;
 
 		switch (AttachmentType.values()[input.readByte()]) {
-		case region:
+		case region: {
 			String path = input.readString();
 			if (path == null) path = name;
 			RegionAttachment region = attachmentLoader.newRegionAttachment(skin, name, path);
@@ -203,19 +204,58 @@ public class SkeletonBinary {
 			Color.rgba8888ToColor(region.getColor(), input.readInt());
 			region.updateOffset();
 			return region;
-		case boundingbox:
+		}
+		case boundingbox: {
 			BoundingBoxAttachment box = attachmentLoader.newBoundingBoxAttachment(skin, name);
 			if (box == null) return null;
-			int n = input.readInt(true);
-			float[] points = new float[n];
-			for (int i = 0; i < n; i++)
-				points[i] = input.readFloat();
-			box.setVertices(points);
+			box.setVertices(readFloatArray(input, scale));
 			return box;
 		}
+		case mesh: {
+			String path = input.readString();
+			if (path == null) path = name;
+			MeshAttachment mesh = attachmentLoader.newMeshAttachment(skin, name, path);
+			float[] vertices = readFloatArray(input, scale);
+			short[] triangles = readShortArray(input);
+			float[] uvs = readFloatArray(input, 1);
+			Color.rgba8888ToColor(mesh.getColor(), input.readInt());
+			mesh.setEdges(readIntArray(input));
+			if (mesh.getEdges().length > 0) {
+				mesh.setHullLength(input.readInt(true));
+				mesh.setWidth(input.readFloat() * scale);
+				mesh.setHeight(input.readFloat() * scale);
+			}
+			mesh.setMesh(vertices, triangles, uvs);
+			return mesh;
+		}
+		}
 		return null;
 	}
 
+	private float[] readFloatArray (DataInput input, float scale) throws IOException {
+		int n = input.readInt(true);
+		float[] array = new float[n];
+		for (int i = 0; i < n; i++)
+			array[i] = input.readFloat() * scale;
+		return array;
+	}
+
+	private short[] readShortArray (DataInput input) throws IOException {
+		int n = input.readInt(true);
+		short[] array = new short[n];
+		for (int i = 0; i < n; i++)
+			array[i] = input.readShort();
+		return array;
+	}
+
+	private int[] readIntArray (DataInput input) throws IOException {
+		int n = input.readInt(true);
+		int[] array = new int[n];
+		for (int i = 0; i < n; i++)
+			array[i] = input.readInt(true);
+		return array;
+	}
+
 	private void readAnimation (String name, DataInput input, SkeletonData skeletonData) {
 		Array<Timeline> timelines = new Array();
 		float duration = 0;

+ 40 - 7
spine-libgdx/src/com/esotericsoftware/spine/SkeletonJson.java

@@ -47,6 +47,7 @@ import com.esotericsoftware.spine.attachments.Attachment;
 import com.esotericsoftware.spine.attachments.AttachmentLoader;
 import com.esotericsoftware.spine.attachments.AttachmentType;
 import com.esotericsoftware.spine.attachments.BoundingBoxAttachment;
+import com.esotericsoftware.spine.attachments.MeshAttachment;
 import com.esotericsoftware.spine.attachments.RegionAttachment;
 
 import com.badlogic.gdx.files.FileHandle;
@@ -184,16 +185,24 @@ public class SkeletonJson {
 
 			region.updateOffset();
 			return region;
-		case boundingbox:
+		case boundingbox: {
 			BoundingBoxAttachment box = attachmentLoader.newBoundingBoxAttachment(skin, name);
-			JsonValue verticesArray = map.require("vertices");
-			float[] vertices = new float[verticesArray.size];
-			int i = 0;
-			for (JsonValue point = verticesArray.child; point != null; point = point.next())
-				vertices[i++] = point.asFloat() * scale;
-			box.setVertices(vertices);
+			box.setVertices(readFloatArray(map.require("vertices"), scale));
 			return box;
 		}
+		case mesh: {
+			MeshAttachment mesh = attachmentLoader.newMeshAttachment(skin, name, map.getString("path", name));
+			float[] vertices = readFloatArray(map.require("vertices"), scale);
+			short[] triangles = readShortArray(map.require("triangles"));
+			float[] uvs = readFloatArray(map.require("uvs"), 1);
+			mesh.setMesh(vertices, triangles, uvs);
+			if (map.has("hull")) mesh.setHullLength(map.require("hull").asInt());
+			if (map.has("edges")) mesh.setEdges(readIntArray(map.require("edges")));
+			mesh.setWidth(map.getFloat("width", 0) * scale);
+			mesh.setHeight(map.getFloat("height", 0) * scale);
+			return mesh;
+		}
+		}
 
 		// RegionSequenceAttachment regionSequenceAttachment = (RegionSequenceAttachment)attachment;
 		//
@@ -206,6 +215,30 @@ public class SkeletonJson {
 		return null;
 	}
 
+	private float[] readFloatArray (JsonValue jsonArray, float scale) {
+		float[] array = new float[jsonArray.size];
+		int i = 0;
+		for (JsonValue point = jsonArray.child; point != null; point = point.next())
+			array[i++] = point.asFloat() * scale;
+		return array;
+	}
+
+	private short[] readShortArray (JsonValue jsonArray) {
+		short[] array = new short[jsonArray.size];
+		int i = 0;
+		for (JsonValue point = jsonArray.child; point != null; point = point.next())
+			array[i++] = (short)point.asInt();
+		return array;
+	}
+
+	private int[] readIntArray (JsonValue jsonArray) {
+		int[] array = new int[jsonArray.size];
+		int i = 0;
+		for (JsonValue point = jsonArray.child; point != null; point = point.next())
+			array[i++] = point.asInt();
+		return array;
+	}
+
 	private void readAnimation (String name, JsonValue map, SkeletonData skeletonData) {
 		Array<Timeline> timelines = new Array();
 		float duration = 0;

+ 10 - 0
spine-libgdx/src/com/esotericsoftware/spine/SkeletonRenderer.java

@@ -34,6 +34,7 @@
 package com.esotericsoftware.spine;
 
 import com.esotericsoftware.spine.attachments.Attachment;
+import com.esotericsoftware.spine.attachments.MeshAttachment;
 import com.esotericsoftware.spine.attachments.RegionAttachment;
 import com.esotericsoftware.spine.attachments.SkeletonAttachment;
 
@@ -78,6 +79,15 @@ public class SkeletonRenderer {
 				}
 
 				batch.draw(texture, vertices, 0, vertices.length, triangles, 0, triangles.length);
+
+			} else if (attachment instanceof MeshAttachment) {
+				MeshAttachment mesh = (MeshAttachment)attachment;
+				mesh.updateWorldVertices(slot, true);
+				vertices = mesh.getWorldVertices();
+				triangles = mesh.getTriangles();
+				texture = mesh.getRegion().getTexture();
+				batch.draw(texture, vertices, 0, vertices.length, triangles, 0, triangles.length);
+
 			} else if (attachment instanceof SkeletonAttachment) {
 				Skeleton attachmentSkeleton = ((SkeletonAttachment)attachment).getSkeleton();
 				if (attachmentSkeleton == null) continue;

+ 10 - 0
spine-libgdx/src/com/esotericsoftware/spine/attachments/AtlasAttachmentLoader.java

@@ -56,6 +56,16 @@ public class AtlasAttachmentLoader implements AttachmentLoader {
 		return attachment;
 	}
 
+	public MeshAttachment newMeshAttachment (Skin skin, String name, String path) {
+		MeshAttachment attachment = new MeshAttachment(name);
+		attachment.setPath(path);
+		AtlasRegion region = atlas.findRegion(path);
+		if (region == null)
+			throw new RuntimeException("Region not found in atlas: " + attachment + " (region attachment: " + name + ")");
+		attachment.setRegion(region);
+		return attachment;
+	}
+
 	public BoundingBoxAttachment newBoundingBoxAttachment (Skin skin, String name) {
 		return new BoundingBoxAttachment(name);
 	}

+ 3 - 0
spine-libgdx/src/com/esotericsoftware/spine/attachments/AttachmentLoader.java

@@ -39,6 +39,9 @@ public interface AttachmentLoader {
 	/** @return May be null to not load any attachment. */
 	public RegionAttachment newRegionAttachment (Skin skin, String name, String path);
 
+	/** @return May be null to not load any attachment. */
+	public MeshAttachment newMeshAttachment (Skin skin, String name, String path);
+
 	/** @return May be null to not load any attachment. */
 	public BoundingBoxAttachment newBoundingBoxAttachment (Skin skin, String name);
 }

+ 1 - 1
spine-libgdx/src/com/esotericsoftware/spine/attachments/AttachmentType.java

@@ -34,5 +34,5 @@
 package com.esotericsoftware.spine.attachments;
 
 public enum AttachmentType {
-	region, boundingbox
+	region, boundingbox, mesh
 }

+ 186 - 0
spine-libgdx/src/com/esotericsoftware/spine/attachments/MeshAttachment.java

@@ -0,0 +1,186 @@
+/******************************************************************************
+ * 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 com.esotericsoftware.spine.attachments;
+
+import com.esotericsoftware.spine.Bone;
+import com.esotericsoftware.spine.Skeleton;
+import com.esotericsoftware.spine.Slot;
+
+import com.badlogic.gdx.graphics.Color;
+import com.badlogic.gdx.graphics.g2d.TextureRegion;
+import com.badlogic.gdx.utils.NumberUtils;
+
+/** Attachment that displays a texture region. */
+public class MeshAttachment extends Attachment {
+	private TextureRegion region;
+	private String path;
+	private int hullLength;
+	private float[] vertices;
+	private short[] triangles;
+	private int[] edges;
+	private float[] worldVertices;
+	private final Color color = new Color(1, 1, 1, 1);
+	private float width, height;
+
+	public MeshAttachment (String name) {
+		super(name);
+	}
+
+	public void setRegion (TextureRegion region) {
+		if (region == null) throw new IllegalArgumentException("region cannot be null.");
+		this.region = region;
+	}
+
+	public TextureRegion getRegion () {
+		if (region == null) throw new IllegalStateException("Region has not been set: " + this);
+		return region;
+	}
+
+	public void updateWorldVertices (Slot slot, boolean premultipliedAlpha) {
+		Skeleton skeleton = slot.getSkeleton();
+		Color skeletonColor = skeleton.getColor();
+		Color slotColor = slot.getColor();
+		Color regionColor = color;
+		float r = skeletonColor.r * slotColor.r * regionColor.r;
+		float g = skeletonColor.g * slotColor.g * regionColor.g;
+		float b = skeletonColor.b * slotColor.b * regionColor.b;
+		float a = skeletonColor.a * slotColor.a * regionColor.a * 255;
+		float color;
+		if (premultipliedAlpha) {
+			r *= a;
+			g *= a;
+			b *= a;
+		} else {
+			r *= 255;
+			g *= 255;
+			b *= 255;
+		}
+		color = NumberUtils.intToFloatColor( //
+			((int)(a) << 24) //
+				| ((int)(b) << 16) //
+				| ((int)(g) << 8) //
+				| ((int)(r)));
+
+		float[] worldVertices = this.worldVertices;
+		float[] vertices = this.vertices;
+		Bone bone1 = slot.getBone();
+		float x = skeleton.getX();
+		float y = skeleton.getY();
+		float m00 = bone1.getM00();
+		float m01 = bone1.getM01();
+		float m10 = bone1.getM10();
+		float m11 = bone1.getM11();
+
+		float vx, vy;
+		for (int v = 0, w = 0, n = vertices.length; v < n; v += 2, w += 5) {
+			vx = vertices[v];
+			vy = vertices[v + 1];
+			float wx1 = vx * m00 + vy * m01 + x + bone1.getWorldX();
+			float wy1 = vx * m10 + vy * m11 + y + bone1.getWorldY();
+			worldVertices[w] = wx1;
+			worldVertices[w + 1] = wy1;
+			worldVertices[w + 2] = Color.WHITE.toFloatBits();
+			worldVertices[w + 2] = color;
+		}
+	}
+
+	public float[] getWorldVertices () {
+		return worldVertices;
+	}
+
+	public float[] getVertices () {
+		return vertices;
+	}
+
+	public short[] getTriangles () {
+		return triangles;
+	}
+
+	public Color getColor () {
+		return color;
+	}
+
+	public String getPath () {
+		return path;
+	}
+
+	public void setPath (String path) {
+		this.path = path;
+	}
+
+	public int getHullLength () {
+		return hullLength;
+	}
+
+	public void setHullLength (int hullLength) {
+		this.hullLength = hullLength;
+	}
+
+	public int[] getEdges () {
+		return edges;
+	}
+
+	public void setEdges (int[] edges) {
+		this.edges = edges;
+	}
+
+	public float getWidth () {
+		return width;
+	}
+
+	public void setWidth (float width) {
+		this.width = width;
+	}
+
+	public float getHeight () {
+		return height;
+	}
+
+	public void setHeight (float height) {
+		this.height = height;
+	}
+
+	public void setMesh (float[] vertices, short[] triangles, float[] uvs) {
+		this.vertices = vertices;
+		this.triangles = triangles;
+
+		int worldVerticesLength = vertices.length / 2 * 5;
+		if (worldVertices == null || worldVertices.length != worldVerticesLength) worldVertices = new float[worldVerticesLength];
+
+		for (int i = 0, w = 3, n = vertices.length; i < n; i += 2, w += 5) {
+			worldVertices[w] = uvs[i];
+			worldVertices[w + 1] = uvs[i + 1];
+		}
+	}
+}