diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/PathConstraint.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/PathConstraint.java index c5d5daf49..baffcc0f6 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/PathConstraint.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/PathConstraint.java @@ -90,12 +90,14 @@ public class PathConstraint implements Updatable { float[] positions = computeWorldPositions((PathAttachment)attachment, spacesCount, tangents, 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; boolean tip = rotateMode == RotateMode.chain && offsetRotation == 0; for (int i = 0, p = 3; i < boneCount; i++, p += 3) { Bone bone = (Bone)bones[i]; - bone.worldX += (boneX - bone.worldX) * translateMix; - bone.worldY += (boneY - bone.worldY) * translateMix; + bone.worldX += (boneX - bone.worldX) * translateMix - skeletonX; + bone.worldY += (boneY - bone.worldY) * translateMix - skeletonY; float x = positions[p], y = positions[p + 1], dx = x - boneX, dy = y - boneY; if (scale) { float length = lengths[i]; @@ -147,7 +149,7 @@ public class PathConstraint implements Updatable { boolean closed = path.getClosed(); if (!path.getConstantSpeed()) { - float pathLength = path.getTotalLength(); + float pathLength = path.getLength(); if (percentPosition) position *= pathLength; if (percentSpacing) { 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); } 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; } @@ -351,8 +353,7 @@ public class PathConstraint implements Updatable { } break; } - - addCurvePosition(p * 0.1f, x1, y1, cx1, cy1, cx2, cy2, x2, y2, out, o, tangents || (space == 0 && i > 0)); + addCurvePosition(p * 0.1f, x1, y1, cx1, cy1, cx2, cy2, x2, y2, out, o, tangents || (i > 0 && space == 0)); } return out; } diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Skeleton.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Skeleton.java index 859ab69d2..8a46cf1d2 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Skeleton.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Skeleton.java @@ -140,7 +140,8 @@ public class Skeleton { 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 () { Array updateCache = this.updateCache; updateCache.clear(); @@ -191,6 +192,7 @@ public class Skeleton { for (int i = 0, n = pathConstraints.size; i < n; 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; sortBone(target); @@ -215,7 +217,7 @@ public class Skeleton { Bone target = constraint.target; sortBone(target); - // BOZO - Update transform constraints to support multiple constrained bones. + // BOZO! - Update transform constraints to support multiple constrained bones. // Array constrained = constraint.bones; // int boneCount = constrained.size; // for (int ii = 0; ii < boneCount; ii++) @@ -235,6 +237,8 @@ public class Skeleton { for (int i = 0, n = bones.size; i < n; i++) sortBone(bones.get(i)); + + System.out.println(updateCache); } private void sortBone (Bone bone) { diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonBinary.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonBinary.java index bc7b1de8c..1336df015 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonBinary.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonBinary.java @@ -57,6 +57,7 @@ import com.esotericsoftware.spine.Animation.ShearTimeline; import com.esotericsoftware.spine.Animation.Timeline; import com.esotericsoftware.spine.Animation.TransformConstraintTimeline; import com.esotericsoftware.spine.Animation.TranslateTimeline; +import com.esotericsoftware.spine.PathConstraintData.PositionMode; import com.esotericsoftware.spine.PathConstraintData.RotateMode; import com.esotericsoftware.spine.PathConstraintData.SpacingMode; import com.esotericsoftware.spine.SkeletonJson.LinkedMesh; @@ -188,6 +189,17 @@ public class SkeletonBinary { 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. for (int i = 0, n = input.readInt(true); i < n; i++) { IkConstraintData data = new IkConstraintData(input.readString()); @@ -223,27 +235,17 @@ public class SkeletonBinary { for (int ii = 0, nn = input.readInt(true); ii < nn; ii++) data.bones.add(skeletonData.bones.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.position = 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.translateMix = input.readFloat(); 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. Skin defaultSkin = readSkin(input, "default", nonessential); if (defaultSkin != null) { @@ -422,15 +424,25 @@ public class SkeletonBinary { return mesh; } case path: { + boolean closed = input.readBoolean(); + boolean constantSpeed = input.readBoolean(); int vertexCount = input.readInt(true); 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; PathAttachment path = attachmentLoader.newPathAttachment(skin, name); if (path == null) return null; + path.setClosed(closed); + path.setConstantSpeed(constantSpeed); + path.setLength(length); path.setWorldVerticesLength(vertexCount << 1); path.setVertices(vertices.vertices); path.setBones(vertices.bones); + path.getCurveLengths().addAll(curveLengths); if (nonessential) Color.rgba8888ToColor(path.getColor(), color); return path; } diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonJson.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonJson.java index bb5c9ec2e..5510f473d 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonJson.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonJson.java @@ -56,6 +56,7 @@ import com.esotericsoftware.spine.Animation.ShearTimeline; import com.esotericsoftware.spine.Animation.Timeline; import com.esotericsoftware.spine.Animation.TransformConstraintTimeline; import com.esotericsoftware.spine.Animation.TranslateTimeline; +import com.esotericsoftware.spine.PathConstraintData.PositionMode; import com.esotericsoftware.spine.PathConstraintData.RotateMode; import com.esotericsoftware.spine.PathConstraintData.SpacingMode; import com.esotericsoftware.spine.attachments.AtlasAttachmentLoader; @@ -137,6 +138,22 @@ public class SkeletonJson { 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. for (JsonValue constraintMap = root.getChild("ik"); constraintMap != null; constraintMap = constraintMap.next) { IkConstraintData data = new IkConstraintData(constraintMap.getString("name")); @@ -200,33 +217,18 @@ public class SkeletonJson { data.target = skeletonData.findSlot(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.position = constraintMap.getFloat("position", 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.translateMix = constraintMap.getFloat("translateMix", 1); 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. for (JsonValue skinMap = root.getChild("skins"); skinMap != null; skinMap = skinMap.next) { Skin skin = new Skin(skinMap.name); @@ -349,7 +351,17 @@ public class SkeletonJson { case path: { PathAttachment path = attachmentLoader.newPathAttachment(skin, name); 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); if (color != null) path.getColor().set(Color.valueOf(color)); @@ -519,8 +531,8 @@ public class SkeletonJson { timeline = new PathConstraintPositionTimeline(timelineMap.size); timeline.pathConstraintIndex = index; int frameIndex = 0; - for (JsonValue valueMap = constraintMap.child; valueMap != null; valueMap = valueMap.next) { - timeline.setFrame(frameIndex, valueMap.getFloat("time"), valueMap.getFloat(timelineName, 1)); + for (JsonValue valueMap = timelineMap.child; valueMap != null; valueMap = valueMap.next) { + timeline.setFrame(frameIndex, valueMap.getFloat("time"), valueMap.getFloat(timelineName, 0)); readCurve(valueMap, timeline, frameIndex); frameIndex++; } @@ -531,7 +543,7 @@ public class SkeletonJson { PathConstraintMixTimeline timeline = new PathConstraintMixTimeline(timelineMap.size); timeline.pathConstraintIndex = index; 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), valueMap.getFloat("translateMix", 1)); readCurve(valueMap, timeline, frameIndex); diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/PathAttachment.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/PathAttachment.java index 843deb58f..83c481d97 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/PathAttachment.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/PathAttachment.java @@ -36,7 +36,7 @@ import com.badlogic.gdx.utils.FloatArray; import com.esotericsoftware.spine.Slot; public class PathAttachment extends VertexAttachment { - float totalLength; + float length; final FloatArray lengths = new FloatArray(); boolean closed, constantSpeed; @@ -72,15 +72,15 @@ public class PathAttachment extends VertexAttachment { } /** Returns the length of the path in the setup pose. */ - public float getTotalLength () { - return totalLength; + public float getLength () { + return length; } - public void setTotalLength (float totalLength) { - this.totalLength = totalLength; + public void setLength (float 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 () { return lengths; }