JSON and binary path attachment and constraint loading.

This commit is contained in:
NathanSweet 2016-06-09 03:33:48 +02:00
parent a6ccff5fde
commit 40950eded7
5 changed files with 78 additions and 49 deletions

View File

@ -90,12 +90,14 @@ public class PathConstraint implements Updatable {
float[] positions = computeWorldPositions((PathAttachment)attachment, spacesCount, tangents, float[] positions = computeWorldPositions((PathAttachment)attachment, spacesCount, tangents,
data.positionMode == PositionMode.percent, spacingMode == SpacingMode.percent); data.positionMode == PositionMode.percent, spacingMode == SpacingMode.percent);
Skeleton skeleton = target.getSkeleton();
float skeletonX = skeleton.x, skeletonY = skeleton.y;
float boneX = positions[0], boneY = positions[1], offsetRotation = data.offsetRotation; float boneX = positions[0], boneY = positions[1], offsetRotation = data.offsetRotation;
boolean tip = rotateMode == RotateMode.chain && offsetRotation == 0; boolean tip = rotateMode == RotateMode.chain && offsetRotation == 0;
for (int i = 0, p = 3; i < boneCount; i++, p += 3) { for (int i = 0, p = 3; i < boneCount; i++, p += 3) {
Bone bone = (Bone)bones[i]; Bone bone = (Bone)bones[i];
bone.worldX += (boneX - bone.worldX) * translateMix; bone.worldX += (boneX - bone.worldX) * translateMix - skeletonX;
bone.worldY += (boneY - bone.worldY) * translateMix; bone.worldY += (boneY - bone.worldY) * translateMix - skeletonY;
float x = positions[p], y = positions[p + 1], dx = x - boneX, dy = y - boneY; float x = positions[p], y = positions[p + 1], dx = x - boneX, dy = y - boneY;
if (scale) { if (scale) {
float length = lengths[i]; float length = lengths[i];
@ -147,7 +149,7 @@ public class PathConstraint implements Updatable {
boolean closed = path.getClosed(); boolean closed = path.getClosed();
if (!path.getConstantSpeed()) { if (!path.getConstantSpeed()) {
float pathLength = path.getTotalLength(); float pathLength = path.getLength();
if (percentPosition) position *= pathLength; if (percentPosition) position *= pathLength;
if (percentSpacing) { if (percentSpacing) {
for (int i = 0; i < spacesCount; i++) for (int i = 0; i < spacesCount; i++)
@ -203,7 +205,7 @@ public class PathConstraint implements Updatable {
path.computeWorldVertices(target, curve * 6 + 2, 8, world, 0); path.computeWorldVertices(target, curve * 6 + 2, 8, world, 0);
} }
addCurvePosition(p, world[0], world[1], world[2], world[3], world[4], world[5], world[6], world[7], out, o, addCurvePosition(p, world[0], world[1], world[2], world[3], world[4], world[5], world[6], world[7], out, o,
tangents || (space == 0 && i > 0)); tangents || (i > 0 && space == 0));
} }
return out; return out;
} }
@ -351,8 +353,7 @@ public class PathConstraint implements Updatable {
} }
break; break;
} }
addCurvePosition(p * 0.1f, x1, y1, cx1, cy1, cx2, cy2, x2, y2, out, o, tangents || (i > 0 && space == 0));
addCurvePosition(p * 0.1f, x1, y1, cx1, cy1, cx2, cy2, x2, y2, out, o, tangents || (space == 0 && i > 0));
} }
return out; return out;
} }

View File

@ -140,7 +140,8 @@ public class Skeleton {
updateCache(); updateCache();
} }
/** Caches information about bones and constraints. Must be called if bones or constraints are added or removed. */ /** Caches information about bones and constraints. Must be called if bones, constraints, or weighted path attachments are
* added or removed. */
public void updateCache () { public void updateCache () {
Array<Updatable> updateCache = this.updateCache; Array<Updatable> updateCache = this.updateCache;
updateCache.clear(); updateCache.clear();
@ -191,6 +192,7 @@ public class Skeleton {
for (int i = 0, n = pathConstraints.size; i < n; i++) { for (int i = 0, n = pathConstraints.size; i < n; i++) {
PathConstraint constraint = pathConstraints.get(i); PathConstraint constraint = pathConstraints.get(i);
// BOZO! - All bones any paths in the target slot are weighted to must come before the path constraint.
Bone target = constraint.target.bone; Bone target = constraint.target.bone;
sortBone(target); sortBone(target);
@ -215,7 +217,7 @@ public class Skeleton {
Bone target = constraint.target; Bone target = constraint.target;
sortBone(target); sortBone(target);
// BOZO - Update transform constraints to support multiple constrained bones. // BOZO! - Update transform constraints to support multiple constrained bones.
// Array<Bone> constrained = constraint.bones; // Array<Bone> constrained = constraint.bones;
// int boneCount = constrained.size; // int boneCount = constrained.size;
// for (int ii = 0; ii < boneCount; ii++) // for (int ii = 0; ii < boneCount; ii++)
@ -235,6 +237,8 @@ public class Skeleton {
for (int i = 0, n = bones.size; i < n; i++) for (int i = 0, n = bones.size; i < n; i++)
sortBone(bones.get(i)); sortBone(bones.get(i));
System.out.println(updateCache);
} }
private void sortBone (Bone bone) { private void sortBone (Bone bone) {

View File

@ -57,6 +57,7 @@ import com.esotericsoftware.spine.Animation.ShearTimeline;
import com.esotericsoftware.spine.Animation.Timeline; import com.esotericsoftware.spine.Animation.Timeline;
import com.esotericsoftware.spine.Animation.TransformConstraintTimeline; import com.esotericsoftware.spine.Animation.TransformConstraintTimeline;
import com.esotericsoftware.spine.Animation.TranslateTimeline; import com.esotericsoftware.spine.Animation.TranslateTimeline;
import com.esotericsoftware.spine.PathConstraintData.PositionMode;
import com.esotericsoftware.spine.PathConstraintData.RotateMode; import com.esotericsoftware.spine.PathConstraintData.RotateMode;
import com.esotericsoftware.spine.PathConstraintData.SpacingMode; import com.esotericsoftware.spine.PathConstraintData.SpacingMode;
import com.esotericsoftware.spine.SkeletonJson.LinkedMesh; import com.esotericsoftware.spine.SkeletonJson.LinkedMesh;
@ -188,6 +189,17 @@ public class SkeletonBinary {
skeletonData.bones.add(data); skeletonData.bones.add(data);
} }
// Slots.
for (int i = 0, n = input.readInt(true); i < n; i++) {
String slotName = input.readString();
BoneData boneData = skeletonData.bones.get(input.readInt(true));
SlotData data = new SlotData(i, slotName, boneData);
Color.rgba8888ToColor(data.color, input.readInt());
data.attachmentName = input.readString();
data.blendMode = BlendMode.values[input.readInt(true)];
skeletonData.slots.add(data);
}
// IK constraints. // IK constraints.
for (int i = 0, n = input.readInt(true); i < n; i++) { for (int i = 0, n = input.readInt(true); i < n; i++) {
IkConstraintData data = new IkConstraintData(input.readString()); IkConstraintData data = new IkConstraintData(input.readString());
@ -223,27 +235,17 @@ public class SkeletonBinary {
for (int ii = 0, nn = input.readInt(true); ii < nn; ii++) for (int ii = 0, nn = input.readInt(true); ii < nn; ii++)
data.bones.add(skeletonData.bones.get(input.readInt(true))); data.bones.add(skeletonData.bones.get(input.readInt(true)));
data.target = skeletonData.slots.get(input.readInt(true)); data.target = skeletonData.slots.get(input.readInt(true));
data.positionMode = PositionMode.values[input.readInt(true)];
data.spacingMode = SpacingMode.values[input.readInt(true)];
data.rotateMode = RotateMode.values[input.readInt(true)];
data.offsetRotation = input.readFloat(); data.offsetRotation = input.readFloat();
data.position = input.readFloat(); data.position = input.readFloat();
data.spacing = input.readFloat(); data.spacing = input.readFloat();
data.spacingMode = SpacingMode.values[input.readInt(true)];
data.rotateMode = RotateMode.values[input.readInt(true)];
data.rotateMix = input.readFloat(); data.rotateMix = input.readFloat();
data.translateMix = input.readFloat(); data.translateMix = input.readFloat();
skeletonData.pathConstraints.add(data); skeletonData.pathConstraints.add(data);
} }
// Slots.
for (int i = 0, n = input.readInt(true); i < n; i++) {
String slotName = input.readString();
BoneData boneData = skeletonData.bones.get(input.readInt(true));
SlotData data = new SlotData(i, slotName, boneData);
Color.rgba8888ToColor(data.color, input.readInt());
data.attachmentName = input.readString();
data.blendMode = BlendMode.values[input.readInt(true)];
skeletonData.slots.add(data);
}
// Default skin. // Default skin.
Skin defaultSkin = readSkin(input, "default", nonessential); Skin defaultSkin = readSkin(input, "default", nonessential);
if (defaultSkin != null) { if (defaultSkin != null) {
@ -422,15 +424,25 @@ public class SkeletonBinary {
return mesh; return mesh;
} }
case path: { case path: {
boolean closed = input.readBoolean();
boolean constantSpeed = input.readBoolean();
int vertexCount = input.readInt(true); int vertexCount = input.readInt(true);
Vertices vertices = readVertices(input, vertexCount); Vertices vertices = readVertices(input, vertexCount);
float length = input.readFloat();
float[] curveLengths = new float[vertexCount / 3];
for (int i = 0, n = curveLengths.length; i < n; i++)
curveLengths[i] = input.readFloat();
int color = nonessential ? input.readInt() : 0; int color = nonessential ? input.readInt() : 0;
PathAttachment path = attachmentLoader.newPathAttachment(skin, name); PathAttachment path = attachmentLoader.newPathAttachment(skin, name);
if (path == null) return null; if (path == null) return null;
path.setClosed(closed);
path.setConstantSpeed(constantSpeed);
path.setLength(length);
path.setWorldVerticesLength(vertexCount << 1); path.setWorldVerticesLength(vertexCount << 1);
path.setVertices(vertices.vertices); path.setVertices(vertices.vertices);
path.setBones(vertices.bones); path.setBones(vertices.bones);
path.getCurveLengths().addAll(curveLengths);
if (nonessential) Color.rgba8888ToColor(path.getColor(), color); if (nonessential) Color.rgba8888ToColor(path.getColor(), color);
return path; return path;
} }

View File

@ -56,6 +56,7 @@ import com.esotericsoftware.spine.Animation.ShearTimeline;
import com.esotericsoftware.spine.Animation.Timeline; import com.esotericsoftware.spine.Animation.Timeline;
import com.esotericsoftware.spine.Animation.TransformConstraintTimeline; import com.esotericsoftware.spine.Animation.TransformConstraintTimeline;
import com.esotericsoftware.spine.Animation.TranslateTimeline; import com.esotericsoftware.spine.Animation.TranslateTimeline;
import com.esotericsoftware.spine.PathConstraintData.PositionMode;
import com.esotericsoftware.spine.PathConstraintData.RotateMode; import com.esotericsoftware.spine.PathConstraintData.RotateMode;
import com.esotericsoftware.spine.PathConstraintData.SpacingMode; import com.esotericsoftware.spine.PathConstraintData.SpacingMode;
import com.esotericsoftware.spine.attachments.AtlasAttachmentLoader; import com.esotericsoftware.spine.attachments.AtlasAttachmentLoader;
@ -137,6 +138,22 @@ public class SkeletonJson {
skeletonData.bones.add(data); skeletonData.bones.add(data);
} }
// Slots.
for (JsonValue slotMap = root.getChild("slots"); slotMap != null; slotMap = slotMap.next) {
String slotName = slotMap.getString("name");
String boneName = slotMap.getString("bone");
BoneData boneData = skeletonData.findBone(boneName);
if (boneData == null) throw new SerializationException("Slot bone not found: " + boneName);
SlotData data = new SlotData(skeletonData.slots.size, slotName, boneData);
String color = slotMap.getString("color", null);
if (color != null) data.getColor().set(Color.valueOf(color));
data.attachmentName = slotMap.getString("attachment", null);
data.blendMode = BlendMode.valueOf(slotMap.getString("blend", BlendMode.normal.name()));
skeletonData.slots.add(data);
}
// IK constraints. // IK constraints.
for (JsonValue constraintMap = root.getChild("ik"); constraintMap != null; constraintMap = constraintMap.next) { for (JsonValue constraintMap = root.getChild("ik"); constraintMap != null; constraintMap = constraintMap.next) {
IkConstraintData data = new IkConstraintData(constraintMap.getString("name")); IkConstraintData data = new IkConstraintData(constraintMap.getString("name"));
@ -200,33 +217,18 @@ public class SkeletonJson {
data.target = skeletonData.findSlot(targetName); data.target = skeletonData.findSlot(targetName);
if (data.target == null) throw new SerializationException("Target slot not found: " + targetName); if (data.target == null) throw new SerializationException("Target slot not found: " + targetName);
data.positionMode = PositionMode.valueOf(constraintMap.getString("positionMode", "percent"));
data.spacingMode = SpacingMode.valueOf(constraintMap.getString("spacingMode", "length"));
data.rotateMode = RotateMode.valueOf(constraintMap.getString("rotateMode", "tangent"));
data.offsetRotation = constraintMap.getFloat("rotation", 0); data.offsetRotation = constraintMap.getFloat("rotation", 0);
data.position = constraintMap.getFloat("position", 0); data.position = constraintMap.getFloat("position", 0);
data.spacing = constraintMap.getFloat("spacing", 0); data.spacing = constraintMap.getFloat("spacing", 0);
data.spacingMode = SpacingMode.valueOf(constraintMap.getString("spacingMode", "length"));
data.rotateMode = RotateMode.valueOf(constraintMap.getString("rotateMode", "tangent"));
data.rotateMix = constraintMap.getFloat("rotateMix", 1); data.rotateMix = constraintMap.getFloat("rotateMix", 1);
data.translateMix = constraintMap.getFloat("translateMix", 1); data.translateMix = constraintMap.getFloat("translateMix", 1);
skeletonData.pathConstraints.add(data); skeletonData.pathConstraints.add(data);
} }
// Slots.
for (JsonValue slotMap = root.getChild("slots"); slotMap != null; slotMap = slotMap.next) {
String slotName = slotMap.getString("name");
String boneName = slotMap.getString("bone");
BoneData boneData = skeletonData.findBone(boneName);
if (boneData == null) throw new SerializationException("Slot bone not found: " + boneName);
SlotData data = new SlotData(skeletonData.slots.size, slotName, boneData);
String color = slotMap.getString("color", null);
if (color != null) data.getColor().set(Color.valueOf(color));
data.attachmentName = slotMap.getString("attachment", null);
data.blendMode = BlendMode.valueOf(slotMap.getString("blend", BlendMode.normal.name()));
skeletonData.slots.add(data);
}
// Skins. // Skins.
for (JsonValue skinMap = root.getChild("skins"); skinMap != null; skinMap = skinMap.next) { for (JsonValue skinMap = root.getChild("skins"); skinMap != null; skinMap = skinMap.next) {
Skin skin = new Skin(skinMap.name); Skin skin = new Skin(skinMap.name);
@ -349,7 +351,17 @@ public class SkeletonJson {
case path: { case path: {
PathAttachment path = attachmentLoader.newPathAttachment(skin, name); PathAttachment path = attachmentLoader.newPathAttachment(skin, name);
if (path == null) return null; if (path == null) return null;
readVertices(map, path, map.getInt("vertexCount") << 1); path.setClosed(map.getBoolean("closed", false));
path.setConstantSpeed(map.getBoolean("constantSpeed", true));
path.setLength(map.getFloat("length"));
int vertexCount = map.getInt("vertexCount");
readVertices(map, path, vertexCount << 1);
float[] curveLengths = path.getCurveLengths().setSize(vertexCount / 3);
int i = 0;
for (JsonValue curves = map.get("curves").child; curves != null; curves = curves.next)
curveLengths[i++] = curves.asFloat();
String color = map.getString("color", null); String color = map.getString("color", null);
if (color != null) path.getColor().set(Color.valueOf(color)); if (color != null) path.getColor().set(Color.valueOf(color));
@ -519,8 +531,8 @@ public class SkeletonJson {
timeline = new PathConstraintPositionTimeline(timelineMap.size); timeline = new PathConstraintPositionTimeline(timelineMap.size);
timeline.pathConstraintIndex = index; timeline.pathConstraintIndex = index;
int frameIndex = 0; int frameIndex = 0;
for (JsonValue valueMap = constraintMap.child; valueMap != null; valueMap = valueMap.next) { for (JsonValue valueMap = timelineMap.child; valueMap != null; valueMap = valueMap.next) {
timeline.setFrame(frameIndex, valueMap.getFloat("time"), valueMap.getFloat(timelineName, 1)); timeline.setFrame(frameIndex, valueMap.getFloat("time"), valueMap.getFloat(timelineName, 0));
readCurve(valueMap, timeline, frameIndex); readCurve(valueMap, timeline, frameIndex);
frameIndex++; frameIndex++;
} }
@ -531,7 +543,7 @@ public class SkeletonJson {
PathConstraintMixTimeline timeline = new PathConstraintMixTimeline(timelineMap.size); PathConstraintMixTimeline timeline = new PathConstraintMixTimeline(timelineMap.size);
timeline.pathConstraintIndex = index; timeline.pathConstraintIndex = index;
int frameIndex = 0; int frameIndex = 0;
for (JsonValue valueMap = constraintMap.child; valueMap != null; valueMap = valueMap.next) { for (JsonValue valueMap = timelineMap.child; valueMap != null; valueMap = valueMap.next) {
timeline.setFrame(frameIndex, valueMap.getFloat("time"), valueMap.getFloat("rotateMix", 1), timeline.setFrame(frameIndex, valueMap.getFloat("time"), valueMap.getFloat("rotateMix", 1),
valueMap.getFloat("translateMix", 1)); valueMap.getFloat("translateMix", 1));
readCurve(valueMap, timeline, frameIndex); readCurve(valueMap, timeline, frameIndex);

View File

@ -36,7 +36,7 @@ import com.badlogic.gdx.utils.FloatArray;
import com.esotericsoftware.spine.Slot; import com.esotericsoftware.spine.Slot;
public class PathAttachment extends VertexAttachment { public class PathAttachment extends VertexAttachment {
float totalLength; float length;
final FloatArray lengths = new FloatArray(); final FloatArray lengths = new FloatArray();
boolean closed, constantSpeed; boolean closed, constantSpeed;
@ -72,15 +72,15 @@ public class PathAttachment extends VertexAttachment {
} }
/** Returns the length of the path in the setup pose. */ /** Returns the length of the path in the setup pose. */
public float getTotalLength () { public float getLength () {
return totalLength; return length;
} }
public void setTotalLength (float totalLength) { public void setLength (float totalLength) {
this.totalLength = totalLength; this.length = totalLength;
} }
/** Returns the length of each curve in the setup pose. */ /** Returns the distance in the setup pose from the start of the path to the end of each curve. */
public FloatArray getCurveLengths () { public FloatArray getCurveLengths () {
return lengths; return lengths;
} }