Added skinning, compatible with 1.8.19.

This commit is contained in:
NathanSweet 2014-04-02 16:22:23 +02:00
parent 02da6a42ad
commit c757da504d
13 changed files with 468 additions and 48 deletions

View File

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

View File

@ -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) {

View File

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

View File

@ -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++;

View File

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

View File

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

View File

@ -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 () {

View File

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

View File

@ -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);

View File

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

View File

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

View File

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

View File

@ -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:");