mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-02-06 07:14:55 +08:00
Added skinning, compatible with 1.8.19.
This commit is contained in:
parent
02da6a42ad
commit
c757da504d
@ -28,7 +28,7 @@
|
||||
|
||||
package com.esotericsoftware.spine;
|
||||
|
||||
import com.esotericsoftware.spine.attachments.MeshAttachment;
|
||||
import com.esotericsoftware.spine.attachments.Attachment;
|
||||
|
||||
import com.badlogic.gdx.graphics.Color;
|
||||
import com.badlogic.gdx.math.MathUtils;
|
||||
@ -427,7 +427,10 @@ public class Animation {
|
||||
float g = frames[i - 2];
|
||||
float b = frames[i - 1];
|
||||
float a = frames[i];
|
||||
color.set(r, g, b, a);
|
||||
if (alpha < 1)
|
||||
color.add((r - color.r) * alpha, (g - color.g) * alpha, (b - color.b) * alpha, (a - color.a) * alpha);
|
||||
else
|
||||
color.set(r, g, b, a);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -614,7 +617,7 @@ public class Animation {
|
||||
private final float[] frames; // time, ...
|
||||
private final float[][] frameVertices;
|
||||
int slotIndex;
|
||||
MeshAttachment meshAttachment;
|
||||
Attachment attachment;
|
||||
|
||||
public FfdTimeline (int frameCount) {
|
||||
super(frameCount);
|
||||
@ -630,12 +633,12 @@ public class Animation {
|
||||
return slotIndex;
|
||||
}
|
||||
|
||||
public void setMeshAttachment (MeshAttachment attachment) {
|
||||
this.meshAttachment = attachment;
|
||||
public void setAttachment (Attachment attachment) {
|
||||
this.attachment = attachment;
|
||||
}
|
||||
|
||||
public MeshAttachment getMeshAttachment () {
|
||||
return meshAttachment;
|
||||
public Attachment getAttachment () {
|
||||
return attachment;
|
||||
}
|
||||
|
||||
public float[] getFrames () {
|
||||
@ -654,7 +657,7 @@ public class Animation {
|
||||
|
||||
public void apply (Skeleton skeleton, float lastTime, float time, Array<Event> firedEvents, float alpha) {
|
||||
Slot slot = skeleton.slots.get(slotIndex);
|
||||
if (slot.getAttachment() != meshAttachment) return;
|
||||
if (slot.getAttachment() != attachment) return;
|
||||
|
||||
FloatArray verticesArray = slot.getAttachmentVertices();
|
||||
verticesArray.size = 0;
|
||||
@ -669,7 +672,12 @@ public class Animation {
|
||||
float[] vertices = verticesArray.items;
|
||||
|
||||
if (time >= frames[frames.length - 1]) { // Time is after last frame.
|
||||
System.arraycopy(frameVertices[frames.length - 1], 0, vertices, 0, vertexCount);
|
||||
float[] lastVertices = frameVertices[frames.length - 1];
|
||||
if (alpha < 1) {
|
||||
for (int i = 0; i < vertexCount; i++)
|
||||
vertices[i] += (lastVertices[i] - vertices[i]) * alpha;
|
||||
} else
|
||||
System.arraycopy(lastVertices, 0, vertices, 0, vertexCount);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -682,10 +690,16 @@ public class Animation {
|
||||
float[] prevVertices = frameVertices[frameIndex - 1];
|
||||
float[] nextVertices = frameVertices[frameIndex];
|
||||
|
||||
// BOZO - FFD, use alpha for mixing?
|
||||
for (int i = 0; i < vertexCount; i++) {
|
||||
float prev = prevVertices[i];
|
||||
vertices[i] = prev + (nextVertices[i] - prev) * percent;
|
||||
if (alpha < 1) {
|
||||
for (int i = 0; i < vertexCount; i++) {
|
||||
float prev = prevVertices[i];
|
||||
vertices[i] += (prev + (nextVertices[i] - prev) * percent - vertices[i]) * alpha;
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < vertexCount; i++) {
|
||||
float prev = prevVertices[i];
|
||||
vertices[i] = prev + (nextVertices[i] - prev) * percent;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -40,7 +40,7 @@ public class BoneData {
|
||||
boolean inheritScale = true, inheritRotation = true;
|
||||
|
||||
// Nonessential.
|
||||
final Color color = new Color(1, 1, 1, 1);
|
||||
final Color color = new Color(0.61f, 0.61f, 0.61f, 1);
|
||||
|
||||
/** @param parent May be null. */
|
||||
public BoneData (String name, BoneData parent) {
|
||||
|
||||
@ -45,12 +45,15 @@ 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.esotericsoftware.spine.attachments.SkinnedMeshAttachment;
|
||||
|
||||
import com.badlogic.gdx.files.FileHandle;
|
||||
import com.badlogic.gdx.graphics.Color;
|
||||
import com.badlogic.gdx.graphics.g2d.TextureAtlas;
|
||||
import com.badlogic.gdx.utils.Array;
|
||||
import com.badlogic.gdx.utils.DataInput;
|
||||
import com.badlogic.gdx.utils.FloatArray;
|
||||
import com.badlogic.gdx.utils.IntArray;
|
||||
import com.badlogic.gdx.utils.SerializationException;
|
||||
|
||||
import java.io.IOException;
|
||||
@ -218,17 +221,48 @@ public class SkeletonBinary {
|
||||
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);
|
||||
short[] triangles = readShortArray(input);
|
||||
float[] vertices = readFloatArray(input, scale);
|
||||
mesh.setMesh(vertices, triangles, uvs);
|
||||
Color.rgba8888ToColor(mesh.getColor(), input.readInt());
|
||||
if (nonessential) {
|
||||
mesh.setEdges(readIntArray(input));
|
||||
mesh.setHullLength(input.readInt(true));
|
||||
mesh.setHullLength(input.readInt(true) * 2);
|
||||
mesh.setWidth(input.readFloat() * scale);
|
||||
mesh.setHeight(input.readFloat() * scale);
|
||||
}
|
||||
return mesh;
|
||||
}
|
||||
case skinnedmesh: {
|
||||
String path = input.readString();
|
||||
if (path == null) path = name;
|
||||
SkinnedMeshAttachment mesh = attachmentLoader.newSkinnedMeshAttachment(skin, name, path);
|
||||
float[] uvs = readFloatArray(input, 1);
|
||||
short[] triangles = readShortArray(input);
|
||||
|
||||
int vertexCount = input.readInt(true);
|
||||
FloatArray weights = new FloatArray(uvs.length * 3 * 3);
|
||||
IntArray bones = new IntArray(uvs.length * 3);
|
||||
for (int i = 0; i < vertexCount; i++) {
|
||||
int boneCount = (int)input.readFloat();
|
||||
bones.add(boneCount);
|
||||
for (int nn = i + boneCount * 4; i < nn; i += 4) {
|
||||
bones.add((int)input.readFloat());
|
||||
weights.add(input.readFloat() * scale);
|
||||
weights.add(input.readFloat() * scale);
|
||||
weights.add(input.readFloat());
|
||||
}
|
||||
}
|
||||
mesh.setMesh(bones.toArray(), weights.toArray(), uvs, triangles);
|
||||
|
||||
Color.rgba8888ToColor(mesh.getColor(), input.readInt());
|
||||
if (nonessential) {
|
||||
mesh.setEdges(readIntArray(input));
|
||||
mesh.setHullLength(input.readInt(true) * 2);
|
||||
mesh.setWidth(input.readFloat() * scale);
|
||||
mesh.setHeight(input.readFloat() * scale);
|
||||
}
|
||||
mesh.setMesh(vertices, triangles, uvs);
|
||||
return mesh;
|
||||
}
|
||||
}
|
||||
@ -238,8 +272,13 @@ public class SkeletonBinary {
|
||||
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;
|
||||
if (scale == 1) {
|
||||
for (int i = 0; i < n; i++)
|
||||
array[i] = input.readFloat();
|
||||
} else {
|
||||
for (int i = 0; i < n; i++)
|
||||
array[i] = input.readFloat() * scale;
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
@ -344,22 +383,45 @@ public class SkeletonBinary {
|
||||
for (int ii = 0, nn = input.readInt(true); ii < nn; ii++) {
|
||||
int slotIndex = input.readInt(true);
|
||||
for (int iii = 0, nnn = input.readInt(true); iii < nnn; iii++) {
|
||||
MeshAttachment mesh = (MeshAttachment)skin.getAttachment(slotIndex, input.readString());
|
||||
Attachment attachment = skin.getAttachment(slotIndex, input.readString());
|
||||
int frameCount = input.readInt(true);
|
||||
FfdTimeline timeline = new FfdTimeline(frameCount);
|
||||
timeline.slotIndex = slotIndex;
|
||||
timeline.meshAttachment = mesh;
|
||||
timeline.attachment = attachment;
|
||||
for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) {
|
||||
float time = input.readFloat();
|
||||
|
||||
float[] vertices;
|
||||
int vertexCount = input.readInt(true);
|
||||
if (vertexCount == 0)
|
||||
vertices = mesh.getVertices();
|
||||
else {
|
||||
int vertexCount;
|
||||
if (attachment instanceof MeshAttachment)
|
||||
vertexCount = ((MeshAttachment)attachment).getVertices().length;
|
||||
else
|
||||
vertexCount = ((SkinnedMeshAttachment)attachment).getWeights().length / 3 * 2;
|
||||
|
||||
int end = input.readInt(true);
|
||||
if (end == 0) {
|
||||
if (attachment instanceof MeshAttachment)
|
||||
vertices = ((MeshAttachment)attachment).getVertices();
|
||||
else
|
||||
vertices = new float[vertexCount];
|
||||
} else {
|
||||
vertices = new float[vertexCount];
|
||||
for (int vertex = 0; vertex < vertexCount; vertex++)
|
||||
vertices[vertex] = input.readFloat() * scale;
|
||||
int start = input.readInt(true);
|
||||
end += start;
|
||||
if (scale == 1) {
|
||||
for (int v = start; v < end; v++)
|
||||
vertices[v] = input.readFloat();
|
||||
} else {
|
||||
for (int v = start; v < end; v++)
|
||||
vertices[v] = input.readFloat() * scale;
|
||||
}
|
||||
if (attachment instanceof MeshAttachment) {
|
||||
float[] meshVertices = ((MeshAttachment)attachment).getVertices();
|
||||
for (int v = 0, vn = vertices.length; v < vn; v++)
|
||||
vertices[v] += meshVertices[v];
|
||||
}
|
||||
}
|
||||
|
||||
timeline.setFrame(frameIndex, time, vertices);
|
||||
if (frameIndex < frameCount - 1) readCurve(input, frameIndex, timeline);
|
||||
}
|
||||
|
||||
@ -45,11 +45,14 @@ 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.esotericsoftware.spine.attachments.SkinnedMeshAttachment;
|
||||
|
||||
import com.badlogic.gdx.files.FileHandle;
|
||||
import com.badlogic.gdx.graphics.Color;
|
||||
import com.badlogic.gdx.graphics.g2d.TextureAtlas;
|
||||
import com.badlogic.gdx.utils.Array;
|
||||
import com.badlogic.gdx.utils.FloatArray;
|
||||
import com.badlogic.gdx.utils.IntArray;
|
||||
import com.badlogic.gdx.utils.JsonReader;
|
||||
import com.badlogic.gdx.utils.JsonValue;
|
||||
import com.badlogic.gdx.utils.SerializationException;
|
||||
@ -194,15 +197,44 @@ public class SkeletonJson {
|
||||
}
|
||||
case mesh: {
|
||||
MeshAttachment mesh = attachmentLoader.newMeshAttachment(skin, name, map.getString("path", name));
|
||||
float[] uvs = map.require("uvs").asFloatArray();
|
||||
short[] triangles = map.require("triangles").asShortArray();
|
||||
|
||||
float[] vertices = map.require("vertices").asFloatArray();
|
||||
if (scale != 1) {
|
||||
for (int i = 0, n = vertices.length; i < n; i++)
|
||||
vertices[i] *= scale;
|
||||
}
|
||||
short[] triangles = map.require("triangles").asShortArray();
|
||||
float[] uvs = map.require("uvs").asFloatArray();
|
||||
mesh.setMesh(vertices, triangles, uvs);
|
||||
if (map.has("hull")) mesh.setHullLength(map.require("hull").asInt());
|
||||
|
||||
if (map.has("hull")) mesh.setHullLength(map.require("hull").asInt() * 2);
|
||||
if (map.has("edges")) mesh.setEdges(map.require("edges").asIntArray());
|
||||
mesh.setWidth(map.getFloat("width", 0) * scale);
|
||||
mesh.setHeight(map.getFloat("height", 0) * scale);
|
||||
return mesh;
|
||||
}
|
||||
case skinnedmesh: {
|
||||
SkinnedMeshAttachment mesh = attachmentLoader.newSkinnedMeshAttachment(skin, name, map.getString("path", name));
|
||||
float[] uvs = map.require("uvs").asFloatArray();
|
||||
short[] triangles = map.require("triangles").asShortArray();
|
||||
|
||||
float[] vertices = map.require("vertices").asFloatArray();
|
||||
FloatArray weights = new FloatArray(uvs.length * 3 * 3);
|
||||
IntArray bones = new IntArray(uvs.length * 3);
|
||||
for (int i = 0, n = vertices.length; i < n;) {
|
||||
int boneCount = (int)vertices[i++];
|
||||
bones.add(boneCount);
|
||||
for (int nn = i + boneCount * 4; i < nn;) {
|
||||
bones.add((int)vertices[i]);
|
||||
weights.add(vertices[i + 1] * scale);
|
||||
weights.add(vertices[i + 2] * scale);
|
||||
weights.add(vertices[i + 3]);
|
||||
i += 4;
|
||||
}
|
||||
}
|
||||
mesh.setMesh(bones.toArray(), weights.toArray(), uvs, triangles);
|
||||
|
||||
if (map.has("hull")) mesh.setHullLength(map.require("hull").asInt() * 2);
|
||||
if (map.has("edges")) mesh.setEdges(map.require("edges").asIntArray());
|
||||
mesh.setWidth(map.getFloat("width", 0) * scale);
|
||||
mesh.setHeight(map.getFloat("height", 0) * scale);
|
||||
@ -222,6 +254,7 @@ public class SkeletonJson {
|
||||
}
|
||||
|
||||
private void readAnimation (String name, JsonValue map, SkeletonData skeletonData) {
|
||||
float scale = this.scale;
|
||||
Array<Timeline> timelines = new Array();
|
||||
float duration = 0;
|
||||
|
||||
@ -315,24 +348,41 @@ public class SkeletonJson {
|
||||
if (slotIndex == -1) throw new SerializationException("Slot not found: " + slotMap.name);
|
||||
for (JsonValue meshMap = slotMap.child; meshMap != null; meshMap = meshMap.next) {
|
||||
FfdTimeline timeline = new FfdTimeline(meshMap.size);
|
||||
MeshAttachment mesh = (MeshAttachment)skin.getAttachment(slotIndex, meshMap.name);
|
||||
if (mesh == null) throw new SerializationException("Mesh attachment not found: " + meshMap.name);
|
||||
Attachment attachment = skin.getAttachment(slotIndex, meshMap.name);
|
||||
if (attachment == null) throw new SerializationException("FFD attachment not found: " + meshMap.name);
|
||||
timeline.slotIndex = slotIndex;
|
||||
timeline.meshAttachment = mesh;
|
||||
timeline.attachment = attachment;
|
||||
|
||||
int frameIndex = 0;
|
||||
for (JsonValue valueMap = meshMap.child; valueMap != null; valueMap = valueMap.next) {
|
||||
float[] vertices;
|
||||
int vertexCount;
|
||||
if (attachment instanceof MeshAttachment)
|
||||
vertexCount = ((MeshAttachment)attachment).getVertices().length;
|
||||
else
|
||||
vertexCount = ((SkinnedMeshAttachment)attachment).getWeights().length / 3 * 2;
|
||||
|
||||
JsonValue verticesValue = valueMap.get("vertices");
|
||||
if (verticesValue == null)
|
||||
vertices = mesh.getVertices();
|
||||
else {
|
||||
vertices = verticesValue.asFloatArray();
|
||||
if (verticesValue == null) {
|
||||
if (attachment instanceof MeshAttachment)
|
||||
vertices = ((MeshAttachment)attachment).getVertices();
|
||||
else
|
||||
vertices = new float[vertexCount];
|
||||
} else {
|
||||
vertices = new float[vertexCount];
|
||||
int start = valueMap.getInt("offset", 0);
|
||||
System.arraycopy(verticesValue.asFloatArray(), 0, vertices, start, verticesValue.size);
|
||||
if (scale != 1) {
|
||||
for (int i = 0, n = vertices.length; i < n; i++)
|
||||
for (int i = start, n = i + verticesValue.size; i < n; i++)
|
||||
vertices[i] *= scale;
|
||||
}
|
||||
if (attachment instanceof MeshAttachment) {
|
||||
float[] meshVertices = ((MeshAttachment)attachment).getVertices();
|
||||
for (int i = 0, n = vertices.length; i < n; i++)
|
||||
vertices[i] += meshVertices[i];
|
||||
}
|
||||
}
|
||||
|
||||
timeline.setFrame(frameIndex, valueMap.getFloat("time"), vertices);
|
||||
readCurve(timeline, frameIndex, valueMap);
|
||||
frameIndex++;
|
||||
|
||||
@ -32,6 +32,7 @@ import com.esotericsoftware.spine.attachments.Attachment;
|
||||
import com.esotericsoftware.spine.attachments.MeshAttachment;
|
||||
import com.esotericsoftware.spine.attachments.RegionAttachment;
|
||||
import com.esotericsoftware.spine.attachments.SkeletonAttachment;
|
||||
import com.esotericsoftware.spine.attachments.SkinnedMeshAttachment;
|
||||
|
||||
import com.badlogic.gdx.graphics.GL20;
|
||||
import com.badlogic.gdx.graphics.Texture;
|
||||
@ -83,6 +84,14 @@ public class SkeletonRenderer {
|
||||
texture = mesh.getRegion().getTexture();
|
||||
batch.draw(texture, vertices, 0, vertices.length, triangles, 0, triangles.length);
|
||||
|
||||
} else if (attachment instanceof SkinnedMeshAttachment) {
|
||||
SkinnedMeshAttachment mesh = (SkinnedMeshAttachment)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;
|
||||
|
||||
@ -29,7 +29,9 @@
|
||||
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.SkinnedMeshAttachment;
|
||||
|
||||
import static com.badlogic.gdx.graphics.g2d.Batch.*;
|
||||
|
||||
@ -44,12 +46,14 @@ import com.badlogic.gdx.utils.FloatArray;
|
||||
public class SkeletonRendererDebug {
|
||||
static private final Color boneLineColor = Color.RED;
|
||||
static private final Color boneOriginColor = Color.GREEN;
|
||||
static private final Color regionAttachmentLineColor = new Color(0, 0, 1, 0.5f);
|
||||
static private final Color attachmentLineColor = new Color(0, 0, 1, 0.5f);
|
||||
static private final Color triangleLineColor = new Color(1, 0.64f, 0, 0.5f);
|
||||
static private final Color boundingBoxColor = new Color(0, 1, 0, 0.8f);
|
||||
static private final Color aabbColor = new Color(0, 1, 0, 0.5f);
|
||||
|
||||
private final ShapeRenderer renderer;
|
||||
private boolean drawBones = true, drawRegionAttachments = true, drawBoundingBoxes = true;
|
||||
private boolean drawMeshHull = true, drawMeshTriangles = true;
|
||||
private final SkeletonBounds bounds = new SkeletonBounds();
|
||||
private float scale = 1;
|
||||
|
||||
@ -78,7 +82,7 @@ public class SkeletonRendererDebug {
|
||||
}
|
||||
|
||||
if (drawRegionAttachments) {
|
||||
renderer.setColor(regionAttachmentLineColor);
|
||||
renderer.setColor(attachmentLineColor);
|
||||
Array<Slot> slots = skeleton.getSlots();
|
||||
for (int i = 0, n = slots.size; i < n; i++) {
|
||||
Slot slot = slots.get(i);
|
||||
@ -95,6 +99,48 @@ public class SkeletonRendererDebug {
|
||||
}
|
||||
}
|
||||
|
||||
if (drawMeshHull || drawMeshTriangles) {
|
||||
Array<Slot> slots = skeleton.getSlots();
|
||||
for (int i = 0, n = slots.size; i < n; i++) {
|
||||
Slot slot = slots.get(i);
|
||||
Attachment attachment = slot.attachment;
|
||||
float[] vertices = null;
|
||||
short[] triangles = null;
|
||||
if (attachment instanceof MeshAttachment) {
|
||||
MeshAttachment mesh = (MeshAttachment)attachment;
|
||||
mesh.updateWorldVertices(slot, false);
|
||||
vertices = mesh.getWorldVertices();
|
||||
triangles = mesh.getTriangles();
|
||||
} else if (attachment instanceof SkinnedMeshAttachment) {
|
||||
SkinnedMeshAttachment mesh = (SkinnedMeshAttachment)attachment;
|
||||
mesh.updateWorldVertices(slot, false);
|
||||
vertices = mesh.getWorldVertices();
|
||||
triangles = mesh.getTriangles();
|
||||
}
|
||||
if (vertices == null || triangles == null) continue;
|
||||
if (drawMeshTriangles) {
|
||||
renderer.setColor(triangleLineColor);
|
||||
for (int ii = 0, nn = triangles.length; ii < nn; ii += 3) {
|
||||
int v1 = triangles[ii] * 5, v2 = triangles[ii + 1] * 5, v3 = triangles[ii + 2] * 5;
|
||||
renderer.triangle(vertices[v1], vertices[v1 + 1], //
|
||||
vertices[v2], vertices[v2 + 1], //
|
||||
vertices[v3], vertices[v3 + 1] //
|
||||
);
|
||||
}
|
||||
}
|
||||
if (drawMeshHull) {
|
||||
renderer.setColor(attachmentLineColor);
|
||||
float lastX = vertices[vertices.length - 5], lastY = vertices[vertices.length - 4];
|
||||
for (int ii = 0, nn = vertices.length; ii < nn; ii += 5) {
|
||||
float x = vertices[ii], y = vertices[ii + 1];
|
||||
renderer.line(x, y, lastX, lastY);
|
||||
lastX = x;
|
||||
lastY = y;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (drawBoundingBoxes) {
|
||||
SkeletonBounds bounds = this.bounds;
|
||||
bounds.update(skeleton, true);
|
||||
@ -142,4 +188,12 @@ public class SkeletonRendererDebug {
|
||||
public void setBoundingBoxes (boolean boundingBoxes) {
|
||||
this.drawBoundingBoxes = boundingBoxes;
|
||||
}
|
||||
|
||||
public void setMeshHull (boolean meshHull) {
|
||||
this.drawMeshHull = meshHull;
|
||||
}
|
||||
|
||||
public void setMeshTriangles (boolean meshTriangles) {
|
||||
this.drawMeshTriangles = meshTriangles;
|
||||
}
|
||||
}
|
||||
|
||||
@ -100,7 +100,7 @@ public class Slot {
|
||||
if (this.attachment == attachment) return;
|
||||
this.attachment = attachment;
|
||||
attachmentTime = skeleton.time;
|
||||
attachmentVertices.clear();
|
||||
//attachmentVertices.clear();
|
||||
}
|
||||
|
||||
public void setAttachmentTime (float time) {
|
||||
@ -120,7 +120,7 @@ public class Slot {
|
||||
color.set(data.color);
|
||||
setAttachment(data.attachmentName == null ? null : skeleton.getAttachment(slotIndex, data.attachmentName));
|
||||
// BOZO - Set mesh to setup pose.
|
||||
// attachmentVertices.clear();
|
||||
attachmentVertices.clear();
|
||||
}
|
||||
|
||||
public void setToSetupPose () {
|
||||
|
||||
@ -56,7 +56,17 @@ public class AtlasAttachmentLoader implements AttachmentLoader {
|
||||
attachment.setPath(path);
|
||||
AtlasRegion region = atlas.findRegion(path);
|
||||
if (region == null)
|
||||
throw new RuntimeException("Region not found in atlas: " + attachment + " (region attachment: " + name + ")");
|
||||
throw new RuntimeException("Region not found in atlas: " + attachment + " (mesh attachment: " + name + ")");
|
||||
attachment.setRegion(region);
|
||||
return attachment;
|
||||
}
|
||||
|
||||
public SkinnedMeshAttachment newSkinnedMeshAttachment (Skin skin, String name, String path) {
|
||||
SkinnedMeshAttachment attachment = new SkinnedMeshAttachment(name);
|
||||
attachment.setPath(path);
|
||||
AtlasRegion region = atlas.findRegion(path);
|
||||
if (region == null)
|
||||
throw new RuntimeException("Region not found in atlas: " + attachment + " (skinned mesh attachment: " + name + ")");
|
||||
attachment.setRegion(region);
|
||||
return attachment;
|
||||
}
|
||||
|
||||
@ -36,6 +36,9 @@ public interface AttachmentLoader {
|
||||
|
||||
/** @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 SkinnedMeshAttachment newSkinnedMeshAttachment (Skin skin, String name, String path);
|
||||
|
||||
/** @return May be null to not load any attachment. */
|
||||
public BoundingBoxAttachment newBoundingBoxAttachment (Skin skin, String name);
|
||||
|
||||
@ -29,5 +29,5 @@
|
||||
package com.esotericsoftware.spine.attachments;
|
||||
|
||||
public enum AttachmentType {
|
||||
region, boundingbox, mesh
|
||||
region, boundingbox, mesh, skinnedmesh
|
||||
}
|
||||
|
||||
@ -0,0 +1,211 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes Software License
|
||||
* Version 2
|
||||
*
|
||||
* Copyright (c) 2013, Esoteric Software
|
||||
* All rights reserved.
|
||||
*
|
||||
* You are granted a perpetual, non-exclusive, non-sublicensable and
|
||||
* non-transferable license to install, execute and perform the Spine Runtimes
|
||||
* Software (the "Software") solely for internal use. Without the written
|
||||
* permission of Esoteric Software, you may not (a) modify, translate, adapt or
|
||||
* otherwise create derivative works, improvements of the Software or develop
|
||||
* new applications using the Software or (b) remove, delete, alter or obscure
|
||||
* any trademarks or any copyright, trademark, patent or other intellectual
|
||||
* property or proprietary rights notices on or in the Software, including
|
||||
* any copy thereof. Redistributions in binary or source form must include
|
||||
* this license and terms. THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE
|
||||
* "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 ESOTERIC SOFTARE 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.FloatArray;
|
||||
import com.badlogic.gdx.utils.NumberUtils;
|
||||
|
||||
/** Attachment that displays a texture region. */
|
||||
public class SkinnedMeshAttachment extends Attachment {
|
||||
private TextureRegion region;
|
||||
private String path;
|
||||
private int[] bones;
|
||||
private float[] weights;
|
||||
private short[] triangles;
|
||||
private float[] worldVertices;
|
||||
private final Color color = new Color(1, 1, 1, 1);
|
||||
|
||||
// Nonessential.
|
||||
private int[] edges;
|
||||
private float width, height;
|
||||
private int hullLength;
|
||||
|
||||
public SkinnedMeshAttachment (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 meshColor = slot.getColor();
|
||||
Color regionColor = color;
|
||||
float a = skeletonColor.a * meshColor.a * regionColor.a * 255;
|
||||
float multiplier = premultipliedAlpha ? a : 255;
|
||||
float color = NumberUtils.intToFloatColor( //
|
||||
((int)a << 24) //
|
||||
| ((int)(skeletonColor.b * meshColor.b * regionColor.b * multiplier) << 16) //
|
||||
| ((int)(skeletonColor.g * meshColor.g * regionColor.g * multiplier) << 8) //
|
||||
| (int)(skeletonColor.r * meshColor.r * regionColor.r * multiplier));
|
||||
|
||||
float[] worldVertices = this.worldVertices;
|
||||
float x = skeleton.getX(), y = skeleton.getY();
|
||||
Object[] skeletonBones = skeleton.getBones().items;
|
||||
float[] weights = this.weights;
|
||||
int[] bones = this.bones;
|
||||
|
||||
FloatArray ffdArray = slot.getAttachmentVertices();
|
||||
if (ffdArray.size == 0) {
|
||||
for (int w = 0, v = 0, b = 0, n = bones.length; v < n; w += 5) {
|
||||
float wx = 0, wy = 0;
|
||||
int nn = bones[v++] + v;
|
||||
for (; v < nn; v++, b += 3) {
|
||||
Bone bone = (Bone)skeletonBones[bones[v]];
|
||||
float vx = weights[b], vy = weights[b + 1], weight = weights[b + 2];
|
||||
wx += (vx * bone.getM00() + vy * bone.getM01() + bone.getWorldX()) * weight;
|
||||
wy += (vx * bone.getM10() + vy * bone.getM11() + bone.getWorldY()) * weight;
|
||||
}
|
||||
worldVertices[w] = wx + x;
|
||||
worldVertices[w + 1] = wy + y;
|
||||
worldVertices[w + 2] = color;
|
||||
}
|
||||
} else {
|
||||
float[] ffd = ffdArray.items;
|
||||
for (int w = 0, v = 0, b = 0, f = 0, n = bones.length; v < n; w += 5) {
|
||||
float wx = 0, wy = 0;
|
||||
int nn = bones[v++] + v;
|
||||
for (; v < nn; v++, b += 3, f += 2) {
|
||||
Bone bone = (Bone)skeletonBones[bones[v]];
|
||||
float vx = weights[b] + ffd[f], vy = weights[b + 1] + ffd[f + 1], weight = weights[b + 2];
|
||||
wx += (vx * bone.getM00() + vy * bone.getM01() + bone.getWorldX()) * weight;
|
||||
wy += (vx * bone.getM10() + vy * bone.getM11() + bone.getWorldY()) * weight;
|
||||
}
|
||||
worldVertices[w] = wx + x;
|
||||
worldVertices[w + 1] = wy + y;
|
||||
worldVertices[w + 2] = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public float[] getWorldVertices () {
|
||||
return worldVertices;
|
||||
}
|
||||
|
||||
public short[] getTriangles () {
|
||||
return triangles;
|
||||
}
|
||||
|
||||
public int[] getBones () {
|
||||
return bones;
|
||||
}
|
||||
|
||||
public float[] getWeights () {
|
||||
return weights;
|
||||
}
|
||||
|
||||
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 void setEdges (int[] edges) {
|
||||
this.edges = edges;
|
||||
}
|
||||
|
||||
public int[] getEdges () {
|
||||
return 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;
|
||||
}
|
||||
|
||||
/** @param bones For each vertex, the number of bones affecting the vertex followed by that many bone indices. Ie: count,
|
||||
* boneIndex, ...
|
||||
* @param weights For each bone affecting the vertex, the vertex position in the bone's coordinate system and the weight for
|
||||
* the bone's influence. Ie: x, y, weight, ...
|
||||
* @param uvs For each vertex, a texure coordinate pair. Ie: u, v, ...
|
||||
* @param triangles Vertex number triplets which describe the mesh's triangulation. */
|
||||
public void setMesh (int[] bones, float[] weights, float[] uvs, short[] triangles) {
|
||||
this.bones = bones;
|
||||
this.weights = weights;
|
||||
this.triangles = triangles;
|
||||
|
||||
int uvsLength = uvs.length;
|
||||
int worldVerticesLength = uvsLength / 2 * 5;
|
||||
if (worldVertices == null || worldVertices.length != worldVerticesLength) worldVertices = new float[worldVerticesLength];
|
||||
|
||||
float u, v, w, h;
|
||||
if (region == null) {
|
||||
u = v = 0;
|
||||
w = h = 1;
|
||||
} else {
|
||||
u = region.getU();
|
||||
v = region.getV();
|
||||
w = region.getU2() - u;
|
||||
h = region.getV2() - v;
|
||||
}
|
||||
for (int i = 0, ii = 3; i < uvsLength; i += 2, ii += 5) {
|
||||
worldVertices[ii] = u + uvs[i] * w;
|
||||
worldVertices[ii + 1] = v + uvs[i + 1] * h;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -168,7 +168,7 @@ public class NormalMapTest extends ApplicationAdapter {
|
||||
|
||||
public void resize (int width, int height) {
|
||||
batch.getProjectionMatrix().setToOrtho2D(0, 0, width, height);
|
||||
ui.stage.setViewport(width, height);
|
||||
ui.stage.getViewport().update(width, height, true);
|
||||
resolution.set(width, height);
|
||||
}
|
||||
|
||||
|
||||
@ -63,6 +63,7 @@ import com.badlogic.gdx.scenes.scene2d.ui.Window;
|
||||
import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener;
|
||||
import com.badlogic.gdx.scenes.scene2d.utils.ClickListener;
|
||||
import com.badlogic.gdx.utils.Array;
|
||||
import com.badlogic.gdx.utils.viewport.ScreenViewport;
|
||||
|
||||
import java.awt.FileDialog;
|
||||
import java.awt.Frame;
|
||||
@ -195,6 +196,8 @@ public class SkeletonTest extends ApplicationAdapter {
|
||||
debugRenderer.setBones(ui.debugBonesCheckbox.isChecked());
|
||||
debugRenderer.setRegionAttachments(ui.debugRegionsCheckbox.isChecked());
|
||||
debugRenderer.setBoundingBoxes(ui.debugBoundingBoxesCheckbox.isChecked());
|
||||
debugRenderer.setMeshHull(ui.debugMeshHullCheckbox.isChecked());
|
||||
debugRenderer.setMeshTriangles(ui.debugMeshTrianglesCheckbox.isChecked());
|
||||
debugRenderer.draw(skeleton);
|
||||
}
|
||||
|
||||
@ -205,12 +208,12 @@ public class SkeletonTest extends ApplicationAdapter {
|
||||
public void resize (int width, int height) {
|
||||
batch.getProjectionMatrix().setToOrtho2D(0, 0, width, height);
|
||||
debugRenderer.getShapeRenderer().setProjectionMatrix(batch.getProjectionMatrix());
|
||||
ui.stage.setViewport(width, height);
|
||||
ui.stage.getViewport().update(width, height, true);
|
||||
if (!ui.minimizeButton.isChecked()) ui.window.setHeight(height);
|
||||
}
|
||||
|
||||
class UI {
|
||||
Stage stage = new Stage();
|
||||
Stage stage = new Stage(new ScreenViewport());
|
||||
com.badlogic.gdx.scenes.scene2d.ui.Skin skin = new com.badlogic.gdx.scenes.scene2d.ui.Skin(
|
||||
Gdx.files.internal("skin/skin.json"));
|
||||
|
||||
@ -231,6 +234,8 @@ public class SkeletonTest extends ApplicationAdapter {
|
||||
CheckBox debugBonesCheckbox = new CheckBox(" Bones", skin);
|
||||
CheckBox debugRegionsCheckbox = new CheckBox(" Regions", skin);
|
||||
CheckBox debugBoundingBoxesCheckbox = new CheckBox(" Bounds", skin);
|
||||
CheckBox debugMeshHullCheckbox = new CheckBox(" Mesh Hull", skin);
|
||||
CheckBox debugMeshTrianglesCheckbox = new CheckBox(" Mesh Triangles", skin);
|
||||
Slider scaleSlider = new Slider(0.1f, 3, 0.01f, false, skin);
|
||||
Label scaleLabel = new Label("1.0", skin);
|
||||
TextButton pauseButton = new TextButton("Pause", skin, "toggle");
|
||||
@ -286,6 +291,8 @@ public class SkeletonTest extends ApplicationAdapter {
|
||||
root.add(table(flipXCheckbox, flipYCheckbox)).row();
|
||||
root.add("Debug:");
|
||||
root.add(table(debugBonesCheckbox, debugRegionsCheckbox, debugBoundingBoxesCheckbox)).row();
|
||||
root.add();
|
||||
root.add(table(debugMeshHullCheckbox, debugMeshTrianglesCheckbox)).row();
|
||||
root.add("Alpha:");
|
||||
root.add(premultipliedCheckbox).row();
|
||||
root.add("Skin:");
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user