MeshAttachment support.

This commit is contained in:
NathanSweet 2013-12-21 18:49:23 +01:00
parent f087d56da6
commit 1c9973ef1d
7 changed files with 297 additions and 15 deletions

View File

@ -47,6 +47,7 @@ import com.esotericsoftware.spine.attachments.Attachment;
import com.esotericsoftware.spine.attachments.AttachmentLoader; import com.esotericsoftware.spine.attachments.AttachmentLoader;
import com.esotericsoftware.spine.attachments.AttachmentType; import com.esotericsoftware.spine.attachments.AttachmentType;
import com.esotericsoftware.spine.attachments.BoundingBoxAttachment; import com.esotericsoftware.spine.attachments.BoundingBoxAttachment;
import com.esotericsoftware.spine.attachments.MeshAttachment;
import com.esotericsoftware.spine.attachments.RegionAttachment; import com.esotericsoftware.spine.attachments.RegionAttachment;
import com.badlogic.gdx.files.FileHandle; import com.badlogic.gdx.files.FileHandle;
@ -188,7 +189,7 @@ public class SkeletonBinary {
if (name == null) name = attachmentName; if (name == null) name = attachmentName;
switch (AttachmentType.values()[input.readByte()]) { switch (AttachmentType.values()[input.readByte()]) {
case region: case region: {
String path = input.readString(); String path = input.readString();
if (path == null) path = name; if (path == null) path = name;
RegionAttachment region = attachmentLoader.newRegionAttachment(skin, name, path); RegionAttachment region = attachmentLoader.newRegionAttachment(skin, name, path);
@ -203,19 +204,58 @@ public class SkeletonBinary {
Color.rgba8888ToColor(region.getColor(), input.readInt()); Color.rgba8888ToColor(region.getColor(), input.readInt());
region.updateOffset(); region.updateOffset();
return region; return region;
case boundingbox: }
case boundingbox: {
BoundingBoxAttachment box = attachmentLoader.newBoundingBoxAttachment(skin, name); BoundingBoxAttachment box = attachmentLoader.newBoundingBoxAttachment(skin, name);
if (box == null) return null; if (box == null) return null;
int n = input.readInt(true); box.setVertices(readFloatArray(input, scale));
float[] points = new float[n];
for (int i = 0; i < n; i++)
points[i] = input.readFloat();
box.setVertices(points);
return box; 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; 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) { private void readAnimation (String name, DataInput input, SkeletonData skeletonData) {
Array<Timeline> timelines = new Array(); Array<Timeline> timelines = new Array();
float duration = 0; float duration = 0;

View File

@ -47,6 +47,7 @@ import com.esotericsoftware.spine.attachments.Attachment;
import com.esotericsoftware.spine.attachments.AttachmentLoader; import com.esotericsoftware.spine.attachments.AttachmentLoader;
import com.esotericsoftware.spine.attachments.AttachmentType; import com.esotericsoftware.spine.attachments.AttachmentType;
import com.esotericsoftware.spine.attachments.BoundingBoxAttachment; import com.esotericsoftware.spine.attachments.BoundingBoxAttachment;
import com.esotericsoftware.spine.attachments.MeshAttachment;
import com.esotericsoftware.spine.attachments.RegionAttachment; import com.esotericsoftware.spine.attachments.RegionAttachment;
import com.badlogic.gdx.files.FileHandle; import com.badlogic.gdx.files.FileHandle;
@ -184,16 +185,24 @@ public class SkeletonJson {
region.updateOffset(); region.updateOffset();
return region; return region;
case boundingbox: case boundingbox: {
BoundingBoxAttachment box = attachmentLoader.newBoundingBoxAttachment(skin, name); BoundingBoxAttachment box = attachmentLoader.newBoundingBoxAttachment(skin, name);
JsonValue verticesArray = map.require("vertices"); box.setVertices(readFloatArray(map.require("vertices"), scale));
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);
return box; 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; // RegionSequenceAttachment regionSequenceAttachment = (RegionSequenceAttachment)attachment;
// //
@ -206,6 +215,30 @@ public class SkeletonJson {
return null; 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) { private void readAnimation (String name, JsonValue map, SkeletonData skeletonData) {
Array<Timeline> timelines = new Array(); Array<Timeline> timelines = new Array();
float duration = 0; float duration = 0;

View File

@ -34,6 +34,7 @@
package com.esotericsoftware.spine; package com.esotericsoftware.spine;
import com.esotericsoftware.spine.attachments.Attachment; import com.esotericsoftware.spine.attachments.Attachment;
import com.esotericsoftware.spine.attachments.MeshAttachment;
import com.esotericsoftware.spine.attachments.RegionAttachment; import com.esotericsoftware.spine.attachments.RegionAttachment;
import com.esotericsoftware.spine.attachments.SkeletonAttachment; import com.esotericsoftware.spine.attachments.SkeletonAttachment;
@ -78,6 +79,15 @@ public class SkeletonRenderer {
} }
batch.draw(texture, vertices, 0, vertices.length, triangles, 0, triangles.length); 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) { } else if (attachment instanceof SkeletonAttachment) {
Skeleton attachmentSkeleton = ((SkeletonAttachment)attachment).getSkeleton(); Skeleton attachmentSkeleton = ((SkeletonAttachment)attachment).getSkeleton();
if (attachmentSkeleton == null) continue; if (attachmentSkeleton == null) continue;

View File

@ -56,6 +56,16 @@ public class AtlasAttachmentLoader implements AttachmentLoader {
return attachment; 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) { public BoundingBoxAttachment newBoundingBoxAttachment (Skin skin, String name) {
return new BoundingBoxAttachment(name); return new BoundingBoxAttachment(name);
} }

View File

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

View File

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

View File

@ -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];
}
}
}