mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2025-12-21 01:36:02 +08:00
MeshAttachment support.
This commit is contained in:
parent
f087d56da6
commit
1c9973ef1d
@ -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;
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user