diff --git a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/AnimationStateTest.java b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/AnimationStateTest.java index 2b571bd61..67503c58a 100644 --- a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/AnimationStateTest.java +++ b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/AnimationStateTest.java @@ -37,13 +37,13 @@ import com.badlogic.gdx.utils.Array; import com.esotericsoftware.spine.AnimationState.AnimationStateListener; import com.esotericsoftware.spine.attachments.AttachmentLoader; import com.esotericsoftware.spine.attachments.BoundingBoxAttachment; -import com.esotericsoftware.spine.attachments.MeshAttachment; import com.esotericsoftware.spine.attachments.RegionAttachment; -import com.esotericsoftware.spine.attachments.WeightedMeshAttachment; +import com.esotericsoftware.spine.attachments.MeshAttachment; +import com.esotericsoftware.spine.attachments.PathAttachment; public class AnimationStateTest { final SkeletonJson json = new SkeletonJson(new AttachmentLoader() { - public WeightedMeshAttachment newWeightedMeshAttachment (Skin skin, String name, String path) { + public MeshAttachment newMeshAttachment (Skin skin, String name, String path) { return null; } @@ -51,11 +51,11 @@ public class AnimationStateTest { return null; } - public MeshAttachment newMeshAttachment (Skin skin, String name, String path) { + public BoundingBoxAttachment newBoundingBoxAttachment (Skin skin, String name) { return null; } - public BoundingBoxAttachment newBoundingBoxAttachment (Skin skin, String name) { + public PathAttachment newPathAttachment (Skin skin, String name) { return null; } }); diff --git a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/AttachmentTimelineTests.java b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/AttachmentTimelineTests.java index 2134db0e5..2b3cdaa7a 100644 --- a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/AttachmentTimelineTests.java +++ b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/AttachmentTimelineTests.java @@ -46,10 +46,10 @@ public class AttachmentTimelineTests { public AttachmentTimelineTests () { skeletonData = new SkeletonData(); - BoneData boneData = new BoneData("bone", null); + BoneData boneData = new BoneData(0, "bone", null); skeletonData.getBones().add(boneData); - skeletonData.getSlots().add(new SlotData("slot", boneData)); + skeletonData.getSlots().add(new SlotData(0, "slot", boneData)); Attachment attachment1 = new Attachment("attachment1") {}; Attachment attachment2 = new Attachment("attachment2") {}; diff --git a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/BonePlotting.java b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/BonePlotting.java index 2eb022270..b96161237 100644 --- a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/BonePlotting.java +++ b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/BonePlotting.java @@ -34,18 +34,14 @@ package com.esotericsoftware.spine; import com.badlogic.gdx.files.FileHandle; import com.esotericsoftware.spine.attachments.AttachmentLoader; import com.esotericsoftware.spine.attachments.BoundingBoxAttachment; -import com.esotericsoftware.spine.attachments.MeshAttachment; import com.esotericsoftware.spine.attachments.RegionAttachment; -import com.esotericsoftware.spine.attachments.WeightedMeshAttachment; +import com.esotericsoftware.spine.attachments.MeshAttachment; +import com.esotericsoftware.spine.attachments.PathAttachment; public class BonePlotting { static public void main (String[] args) throws Exception { // This example shows how to load skeleton data and plot a bone transform for each animation. SkeletonJson json = new SkeletonJson(new AttachmentLoader() { - public WeightedMeshAttachment newWeightedMeshAttachment (Skin skin, String name, String path) { - return null; - } - public RegionAttachment newRegionAttachment (Skin skin, String name, String path) { return null; } @@ -57,6 +53,10 @@ public class BonePlotting { public BoundingBoxAttachment newBoundingBoxAttachment (Skin skin, String name) { return null; } + + public PathAttachment newPathAttachment (Skin skin, String name) { + return null; + } }); SkeletonData skeletonData = json.readSkeletonData(new FileHandle("assets/spineboy/spineboy.json")); Skeleton skeleton = new Skeleton(skeletonData); diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Animation.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Animation.java index a05848644..9d56ae280 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Animation.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Animation.java @@ -36,7 +36,7 @@ import com.badlogic.gdx.math.MathUtils; import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.FloatArray; import com.esotericsoftware.spine.attachments.Attachment; -import com.esotericsoftware.spine.attachments.FfdAttachment; +import com.esotericsoftware.spine.attachments.VertexAttachment; public class Animation { final String name; @@ -654,13 +654,13 @@ public class Animation { } } - static public class FfdTimeline extends CurveTimeline { + static public class DeformTimeline extends CurveTimeline { private final float[] frames; // time, ... private final float[][] frameVertices; int slotIndex; - Attachment attachment; + VertexAttachment attachment; - public FfdTimeline (int frameCount) { + public DeformTimeline (int frameCount) { super(frameCount); frames = new float[frameCount]; frameVertices = new float[frameCount][]; @@ -674,7 +674,7 @@ public class Animation { return slotIndex; } - public void setAttachment (Attachment attachment) { + public void setAttachment (VertexAttachment attachment) { this.attachment = attachment; } @@ -699,7 +699,7 @@ public class Animation { public void apply (Skeleton skeleton, float lastTime, float time, Array firedEvents, float alpha) { Slot slot = skeleton.slots.get(slotIndex); Attachment slotAttachment = slot.getAttachment(); - if (!(slotAttachment instanceof FfdAttachment) || !((FfdAttachment)slotAttachment).applyFFD(attachment)) return; + if (!(slotAttachment instanceof VertexAttachment) || !((VertexAttachment)slotAttachment).applyDeform(attachment)) return; float[] frames = this.frames; if (time < frames[0]) return; // Time is before first frame. @@ -709,10 +709,7 @@ public class Animation { FloatArray verticesArray = slot.getAttachmentVertices(); if (verticesArray.size != vertexCount) alpha = 1; // Don't mix from uninitialized slot vertices. - verticesArray.size = 0; - verticesArray.ensureCapacity(vertexCount); - verticesArray.size = vertexCount; - float[] vertices = verticesArray.items; + float[] vertices = verticesArray.setSize(vertexCount); if (time >= frames[frames.length - 1]) { // Time is after last frame. float[] lastVertices = frameVertices[frames.length - 1]; @@ -760,8 +757,8 @@ public class Animation { frames = new float[frameCount * 3]; } - public void setIkConstraintIndex (int ikConstraint) { - this.ikConstraintIndex = ikConstraint; + public void setIkConstraintIndex (int index) { + this.ikConstraintIndex = index; } public int getIkConstraintIndex () { @@ -823,8 +820,8 @@ public class Animation { frames = new float[frameCount * 5]; } - public void setTransformConstraintIndex (int ikConstraint) { - this.transformConstraintIndex = ikConstraint; + public void setTransformConstraintIndex (int index) { + this.transformConstraintIndex = index; } public int getTransformConstraintIndex () { @@ -877,4 +874,72 @@ public class Animation { constraint.shearMix += (shear + (frames[frame + SHEAR_MIX] - shear) * percent - constraint.shearMix) * alpha; } } + + static public class PathConstraintTimeline extends CurveTimeline { + static private final int PREV_TIME = -4; + static private final int PREV_POSITION = -3; + static private final int PREV_ROTATE_MIX = -2; + static private final int PREV_TRANSLATE_MIX = -1; + static private final int POSITION = 1; + static private final int ROTATE_MIX = 2; + static private final int TRANSLATE_MIX = 3; + + int pathConstraintIndex; + private final float[] frames; // time, rotate mix, translate mix, scale mix, shear mix, ... + + public PathConstraintTimeline (int frameCount) { + super(frameCount); + frames = new float[frameCount * 4]; + } + + public void setPathConstraintIndex (int index) { + this.pathConstraintIndex = index; + } + + public int getPathConstraintIndex () { + return pathConstraintIndex; + } + + public float[] getFrames () { + return frames; + } + + /** Sets the time, position, and mixes of the specified keyframe. */ + public void setFrame (int frameIndex, float time, float position, float rotateMix, float translateMix) { + frameIndex *= 4; + frames[frameIndex] = time; + frames[frameIndex + 1] = position; + frames[frameIndex + 2] = rotateMix; + frames[frameIndex + 3] = translateMix; + } + + public void apply (Skeleton skeleton, float lastTime, float time, Array events, float alpha) { + float[] frames = this.frames; + if (time < frames[0]) return; // Time is before first frame. + + PathConstraint constraint = skeleton.pathConstraints.get(pathConstraintIndex); + + if (time >= frames[frames.length - 4]) { // Time is after last frame. + int i = frames.length - 1; + constraint.position += (frames[i - 2] - constraint.position) * alpha; + constraint.rotateMix += (frames[i - 1] - constraint.rotateMix) * alpha; + constraint.translateMix += (frames[i] - constraint.translateMix) * alpha; + return; + } + + // Interpolate between the previous frame and the current frame. + int frame = binarySearch(frames, time, 4); + float frameTime = frames[frame]; + float percent = MathUtils.clamp(1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime), 0, 1); + percent = getCurvePercent(frame / 4 - 1, percent); + + float position = frames[frame + PREV_POSITION]; + float rotate = frames[frame + PREV_ROTATE_MIX]; + float translate = frames[frame + PREV_TRANSLATE_MIX]; + constraint.position += (position + (frames[frame + POSITION] - position) * percent - constraint.position) * alpha; + constraint.rotateMix += (rotate + (frames[frame + ROTATE_MIX] - rotate) * percent - constraint.rotateMix) * alpha; + constraint.translateMix += (translate + (frames[frame + TRANSLATE_MIX] - translate) * percent - constraint.translateMix) + * alpha; + } + } } diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Bone.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Bone.java index ef52a0712..2b53a500e 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Bone.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Bone.java @@ -36,11 +36,13 @@ import static com.badlogic.gdx.math.Matrix3.*; import com.badlogic.gdx.math.Matrix3; import com.badlogic.gdx.math.Vector2; +import com.badlogic.gdx.utils.Array; public class Bone implements Updatable { final BoneData data; final Skeleton skeleton; final Bone parent; + final Array children = new Array(); float x, y, rotation, scaleX, scaleY, shearX, shearY; float appliedRotation, appliedScaleX, appliedScaleY; @@ -48,12 +50,6 @@ public class Bone implements Updatable { float c, d, worldY; float worldSignX, worldSignY; - Bone (BoneData data) { - this.data = data; - parent = null; - skeleton = null; - } - /** @param parent May be null. */ public Bone (BoneData data, Skeleton skeleton, Bone parent) { if (data == null) throw new IllegalArgumentException("data cannot be null."); @@ -230,6 +226,10 @@ public class Bone implements Updatable { return parent; } + public Array getChildren () { + return children; + } + public float getX () { return x; } diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/BoneData.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/BoneData.java index fdc5cff99..b1b6026a1 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/BoneData.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/BoneData.java @@ -34,8 +34,9 @@ package com.esotericsoftware.spine; import com.badlogic.gdx.graphics.Color; public class BoneData { - final BoneData parent; + final int index; final String name; + final BoneData parent; float length; float x, y, rotation, scaleX = 1, scaleY = 1, shearX, shearY; boolean inheritScale = true, inheritRotation = true; @@ -44,8 +45,9 @@ public class BoneData { final Color color = new Color(0.61f, 0.61f, 0.61f, 1); /** @param parent May be null. */ - public BoneData (String name, BoneData parent) { + public BoneData (int index, String name, BoneData parent) { if (name == null) throw new IllegalArgumentException("name cannot be null."); + this.index = index; this.name = name; this.parent = parent; } @@ -54,8 +56,9 @@ public class BoneData { * @param parent May be null. */ public BoneData (BoneData bone, BoneData parent) { if (bone == null) throw new IllegalArgumentException("bone cannot be null."); - this.parent = parent; + index = bone.index; name = bone.name; + this.parent = parent; length = bone.length; x = bone.x; y = bone.y; @@ -66,15 +69,19 @@ public class BoneData { shearY = bone.shearY; } - /** @return May be null. */ - public BoneData getParent () { - return parent; + public int getIndex () { + return index; } public String getName () { return name; } + /** @return May be null. */ + public BoneData getParent () { + return parent; + } + public float getLength () { return length; } diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/IkConstraint.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/IkConstraint.java index 9969688f1..8ed69047e 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/IkConstraint.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/IkConstraint.java @@ -60,8 +60,8 @@ public class IkConstraint implements Updatable { data = ikConstraint.data; bones = new Array(ikConstraint.bones.size); for (Bone bone : ikConstraint.bones) - bones.add(skeleton.bones.get(bone.skeleton.bones.indexOf(bone, true))); - target = skeleton.bones.get(ikConstraint.target.skeleton.bones.indexOf(ikConstraint.target, true)); + bones.add(skeleton.bones.get(bone.data.index)); + target = skeleton.bones.get(ikConstraint.target.data.index); mix = ikConstraint.mix; bendDirection = ikConstraint.bendDirection; } diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/PathConstraint.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/PathConstraint.java new file mode 100644 index 000000000..3501a8ce7 --- /dev/null +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/PathConstraint.java @@ -0,0 +1,97 @@ + +package com.esotericsoftware.spine; + +import com.badlogic.gdx.math.Vector2; +import com.esotericsoftware.spine.attachments.Attachment; +import com.esotericsoftware.spine.attachments.PathAttachment; + +public class PathConstraint implements Updatable { + final PathConstraintData data; + Bone bone; + Slot target; + float position, rotateMix, translateMix; + + public PathConstraint (PathConstraintData data, Skeleton skeleton) { + this.data = data; + position = data.position; + rotateMix = data.rotateMix; + translateMix = data.translateMix; + bone = skeleton.findBone(data.bone.name); + target = skeleton.findSlot(data.target.name); + } + + /** Copy constructor. */ + public PathConstraint (PathConstraint constraint, Skeleton skeleton) { + data = constraint.data; + bone = skeleton.bones.get(constraint.bone.data.index); + target = skeleton.slots.get(constraint.target.data.index); + position = constraint.position; + rotateMix = constraint.rotateMix; + translateMix = constraint.translateMix; + } + + public void apply () { + update(); + } + + public void update () { + Attachment attachment = target.getAttachment(); + if (!(attachment instanceof PathAttachment)) return; + PathAttachment path = (PathAttachment)attachment; + + float translateMix = this.translateMix; + if (translateMix > 0) { + Vector2 temp = path.computeWorldPosition(target, position); + bone.worldX += (temp.x - bone.worldX) * translateMix; + bone.worldY += (temp.y - bone.worldY) * translateMix; + } + } + + public float getPosition () { + return position; + } + + public void setPosition (float position) { + this.position = position; + } + + public float getRotateMix () { + return rotateMix; + } + + public void setRotateMix (float rotateMix) { + this.rotateMix = rotateMix; + } + + public float getTranslateMix () { + return translateMix; + } + + public void setTranslateMix (float translateMix) { + this.translateMix = translateMix; + } + + public Bone getBone () { + return bone; + } + + public void setBone (Bone bone) { + this.bone = bone; + } + + public Slot getTarget () { + return target; + } + + public void setTarget (Slot target) { + this.target = target; + } + + public PathConstraintData getData () { + return data; + } + + public String toString () { + return data.name; + } +} diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/PathConstraintData.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/PathConstraintData.java new file mode 100644 index 000000000..f3a276896 --- /dev/null +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/PathConstraintData.java @@ -0,0 +1,70 @@ + +package com.esotericsoftware.spine; + +public class PathConstraintData { + final String name; + BoneData bone; + SlotData target; + float position, rotateMix, translateMix; + float offsetRotation; + + public PathConstraintData (String name) { + this.name = name; + } + + public BoneData getBone () { + return bone; + } + + public void setBone (BoneData bone) { + this.bone = bone; + } + + public SlotData getTarget () { + return target; + } + + public void setTarget (SlotData target) { + this.target = target; + } + + public float getPosition () { + return position; + } + + public void setPosition (float position) { + this.position = position; + } + + public float getRotateMix () { + return rotateMix; + } + + public void setRotateMix (float rotateMix) { + this.rotateMix = rotateMix; + } + + public float getTranslateMix () { + return translateMix; + } + + public void setTranslateMix (float translateMix) { + this.translateMix = translateMix; + } + + public float getOffsetRotation () { + return offsetRotation; + } + + public void setOffsetRotation (float offsetRotation) { + this.offsetRotation = offsetRotation; + } + + public String getName () { + return name; + } + + public String toString () { + return name; + } +} 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 7f5390073..b22a47c2e 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Skeleton.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Skeleton.java @@ -37,7 +37,6 @@ import com.badlogic.gdx.utils.Array; import com.esotericsoftware.spine.attachments.Attachment; import com.esotericsoftware.spine.attachments.MeshAttachment; import com.esotericsoftware.spine.attachments.RegionAttachment; -import com.esotericsoftware.spine.attachments.WeightedMeshAttachment; public class Skeleton { final SkeletonData data; @@ -46,6 +45,7 @@ public class Skeleton { Array drawOrder; final Array ikConstraints; final Array transformConstraints; + final Array pathConstraints; private final Array updateCache = new Array(); Skin skin; final Color color; @@ -59,14 +59,21 @@ public class Skeleton { bones = new Array(data.bones.size); for (BoneData boneData : data.bones) { - Bone parent = boneData.parent == null ? null : bones.get(data.bones.indexOf(boneData.parent, true)); - bones.add(new Bone(boneData, this, parent)); + Bone bone; + if (boneData.parent == null) + bone = new Bone(boneData, this, null); + else { + Bone parent = bones.get(boneData.parent.index); + bone = new Bone(boneData, this, parent); + parent.children.add(bone); + } + bones.add(bone); } slots = new Array(data.slots.size); drawOrder = new Array(data.slots.size); for (SlotData slotData : data.slots) { - Bone bone = bones.get(data.bones.indexOf(slotData.boneData, true)); + Bone bone = bones.get(slotData.boneData.index); Slot slot = new Slot(slotData, bone); slots.add(slot); drawOrder.add(slot); @@ -80,6 +87,10 @@ public class Skeleton { for (TransformConstraintData transformConstraintData : data.transformConstraints) transformConstraints.add(new TransformConstraint(transformConstraintData, this)); + pathConstraints = new Array(data.pathConstraints.size); + for (PathConstraintData pathConstraintData : data.pathConstraints) + pathConstraints.add(new PathConstraint(pathConstraintData, this)); + color = new Color(1, 1, 1, 1); updateCache(); @@ -92,19 +103,19 @@ public class Skeleton { bones = new Array(skeleton.bones.size); for (Bone bone : skeleton.bones) { - Bone parent = bone.parent == null ? null : bones.get(skeleton.bones.indexOf(bone.parent, true)); + Bone parent = bone.parent == null ? null : bones.get(bone.parent.data.index); bones.add(new Bone(bone, this, parent)); } slots = new Array(skeleton.slots.size); for (Slot slot : skeleton.slots) { - Bone bone = bones.get(skeleton.bones.indexOf(slot.bone, true)); + Bone bone = bones.get(slot.bone.data.index); slots.add(new Slot(slot, bone)); } drawOrder = new Array(slots.size); for (Slot slot : skeleton.drawOrder) - drawOrder.add(slots.get(skeleton.slots.indexOf(slot, true))); + drawOrder.add(slots.get(slot.data.index)); ikConstraints = new Array(skeleton.ikConstraints.size); for (IkConstraint ikConstraint : skeleton.ikConstraints) @@ -114,6 +125,10 @@ public class Skeleton { for (TransformConstraint transformConstraint : skeleton.transformConstraints) transformConstraints.add(new TransformConstraint(transformConstraint, this)); + pathConstraints = new Array(skeleton.pathConstraints.size); + for (PathConstraint pathConstraint : skeleton.pathConstraints) + pathConstraints.add(new PathConstraint(pathConstraint, this)); + skin = skeleton.skin; color = new Color(skeleton.color); time = skeleton.time; @@ -129,8 +144,8 @@ public class Skeleton { Array updateCache = this.updateCache; Array ikConstraints = this.ikConstraints; Array transformConstraints = this.transformConstraints; + Array pathConstraints = this.pathConstraints; int ikConstraintsCount = ikConstraints.size; - int transformConstraintsCount = transformConstraints.size; updateCache.clear(); for (int i = 0, n = bones.size; i < n; i++) { @@ -145,10 +160,22 @@ public class Skeleton { } } - for (int i = 0; i < transformConstraintsCount; i++) { - TransformConstraint transformConstraint = transformConstraints.get(i); + for (int i = 0, n = pathConstraints.size; i < n; i++) { + PathConstraint pathConstraint = pathConstraints.get(i); + Bone bone = pathConstraint.bone; for (int ii = updateCache.size - 1; ii >= 0; ii--) { - if (updateCache.get(ii) == transformConstraint.bone) { + if (updateCache.get(ii) == bone) { + updateCache.insert(ii + 1, pathConstraint); + break; + } + } + } + + for (int i = 0, n = transformConstraints.size; i < n; i++) { + TransformConstraint transformConstraint = transformConstraints.get(i); + Bone bone = transformConstraint.bone; + for (int ii = updateCache.size - 1; ii >= 0; ii--) { + if (updateCache.get(ii) == bone) { updateCache.insert(ii + 1, transformConstraint); break; } @@ -191,13 +218,22 @@ public class Skeleton { constraint.scaleMix = data.scaleMix; constraint.shearMix = data.shearMix; } + + Array pathConstraints = this.pathConstraints; + for (int i = 0, n = pathConstraints.size; i < n; i++) { + PathConstraint constraint = pathConstraints.get(i); + PathConstraintData data = constraint.data; + constraint.position = data.position; + constraint.rotateMix = data.rotateMix; + constraint.translateMix = data.translateMix; + } } public void setSlotsToSetupPose () { Array slots = this.slots; System.arraycopy(slots.items, 0, drawOrder.items, 0, slots.size); for (int i = 0, n = slots.size; i < n; i++) - slots.get(i).setToSetupPose(i); + slots.get(i).setToSetupPose(); } public SkeletonData getData () { @@ -370,6 +406,21 @@ public class Skeleton { return null; } + public Array getPathConstraints () { + return pathConstraints; + } + + /** @return May be null. */ + public PathConstraint findPathConstraint (String constraintName) { + if (constraintName == null) throw new IllegalArgumentException("constraintName cannot be null."); + Array pathConstraints = this.pathConstraints; + for (int i = 0, n = pathConstraints.size; i < n; i++) { + PathConstraint constraint = pathConstraints.get(i); + if (constraint.data.name.equals(constraintName)) return constraint; + } + return null; + } + /** Returns the axis aligned bounding box (AABB) of the region, mesh, and skinned mesh attachments for the current pose. * @param offset The distance from the skeleton origin to the bottom left corner of the AABB. * @param size The width and height of the AABB. */ @@ -382,12 +433,8 @@ public class Skeleton { Attachment attachment = slot.attachment; if (attachment instanceof RegionAttachment) { vertices = ((RegionAttachment)attachment).updateWorldVertices(slot, false); - } else if (attachment instanceof MeshAttachment) { vertices = ((MeshAttachment)attachment).updateWorldVertices(slot, true); - - } else if (attachment instanceof WeightedMeshAttachment) { - vertices = ((WeightedMeshAttachment)attachment).updateWorldVertices(slot, true); } if (vertices != null) { for (int ii = 0, nn = vertices.length; ii < nn; ii += 5) { 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 4fc13a0dc..8c6b4c3d7 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonBinary.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonBinary.java @@ -44,10 +44,11 @@ import com.badlogic.gdx.utils.SerializationException; import com.esotericsoftware.spine.Animation.AttachmentTimeline; import com.esotericsoftware.spine.Animation.ColorTimeline; import com.esotericsoftware.spine.Animation.CurveTimeline; +import com.esotericsoftware.spine.Animation.DeformTimeline; import com.esotericsoftware.spine.Animation.DrawOrderTimeline; import com.esotericsoftware.spine.Animation.EventTimeline; -import com.esotericsoftware.spine.Animation.FfdTimeline; import com.esotericsoftware.spine.Animation.IkConstraintTimeline; +import com.esotericsoftware.spine.Animation.PathConstraintTimeline; import com.esotericsoftware.spine.Animation.RotateTimeline; import com.esotericsoftware.spine.Animation.ScaleTimeline; import com.esotericsoftware.spine.Animation.ShearTimeline; @@ -61,8 +62,9 @@ import com.esotericsoftware.spine.attachments.AttachmentLoader; import com.esotericsoftware.spine.attachments.AttachmentType; import com.esotericsoftware.spine.attachments.BoundingBoxAttachment; import com.esotericsoftware.spine.attachments.MeshAttachment; +import com.esotericsoftware.spine.attachments.PathAttachment; import com.esotericsoftware.spine.attachments.RegionAttachment; -import com.esotericsoftware.spine.attachments.WeightedMeshAttachment; +import com.esotericsoftware.spine.attachments.VertexAttachment; public class SkeletonBinary { static public final int TIMELINE_ROTATE = 0; @@ -161,59 +163,71 @@ public class SkeletonBinary { for (int i = 0, n = input.readInt(true); i < n; i++) { String name = input.readString(); BoneData parent = i == 0 ? null : skeletonData.bones.get(input.readInt(true)); - BoneData boneData = new BoneData(name, parent); - boneData.rotation = input.readFloat(); - boneData.x = input.readFloat() * scale; - boneData.y = input.readFloat() * scale; - boneData.scaleX = input.readFloat(); - boneData.scaleY = input.readFloat(); - boneData.shearX = input.readFloat(); - boneData.shearY = input.readFloat(); - boneData.length = input.readFloat() * scale; - boneData.inheritRotation = input.readBoolean(); - boneData.inheritScale = input.readBoolean(); - if (nonessential) Color.rgba8888ToColor(boneData.color, input.readInt()); - skeletonData.bones.add(boneData); + BoneData data = new BoneData(i, name, parent); + data.rotation = input.readFloat(); + data.x = input.readFloat() * scale; + data.y = input.readFloat() * scale; + data.scaleX = input.readFloat(); + data.scaleY = input.readFloat(); + data.shearX = input.readFloat(); + data.shearY = input.readFloat(); + data.length = input.readFloat() * scale; + data.inheritRotation = input.readBoolean(); + data.inheritScale = input.readBoolean(); + if (nonessential) Color.rgba8888ToColor(data.color, input.readInt()); + skeletonData.bones.add(data); } // IK constraints. for (int i = 0, n = input.readInt(true); i < n; i++) { - IkConstraintData ikConstraintData = new IkConstraintData(input.readString()); + IkConstraintData data = new IkConstraintData(input.readString()); for (int ii = 0, nn = input.readInt(true); ii < nn; ii++) - ikConstraintData.bones.add(skeletonData.bones.get(input.readInt(true))); - ikConstraintData.target = skeletonData.bones.get(input.readInt(true)); - ikConstraintData.mix = input.readFloat(); - ikConstraintData.bendDirection = input.readByte(); - skeletonData.ikConstraints.add(ikConstraintData); + data.bones.add(skeletonData.bones.get(input.readInt(true))); + data.target = skeletonData.bones.get(input.readInt(true)); + data.mix = input.readFloat(); + data.bendDirection = input.readByte(); + skeletonData.ikConstraints.add(data); } // Transform constraints. for (int i = 0, n = input.readInt(true); i < n; i++) { - TransformConstraintData transformConstraintData = new TransformConstraintData(input.readString()); - transformConstraintData.bone = skeletonData.bones.get(input.readInt(true)); - transformConstraintData.target = skeletonData.bones.get(input.readInt(true)); - transformConstraintData.offsetRotation = input.readFloat(); - transformConstraintData.offsetX = input.readFloat() * scale; - transformConstraintData.offsetY = input.readFloat() * scale; - transformConstraintData.offsetScaleX = input.readFloat(); - transformConstraintData.offsetScaleY = input.readFloat(); - transformConstraintData.offsetShearY = input.readFloat(); - transformConstraintData.rotateMix = input.readFloat(); - transformConstraintData.translateMix = input.readFloat(); - transformConstraintData.scaleMix = input.readFloat(); - transformConstraintData.shearMix = input.readFloat(); - skeletonData.transformConstraints.add(transformConstraintData); + TransformConstraintData data = new TransformConstraintData(input.readString()); + data.bone = skeletonData.bones.get(input.readInt(true)); + data.target = skeletonData.bones.get(input.readInt(true)); + data.offsetRotation = input.readFloat(); + data.offsetX = input.readFloat() * scale; + data.offsetY = input.readFloat() * scale; + data.offsetScaleX = input.readFloat(); + data.offsetScaleY = input.readFloat(); + data.offsetShearY = input.readFloat(); + data.rotateMix = input.readFloat(); + data.translateMix = input.readFloat(); + data.scaleMix = input.readFloat(); + data.shearMix = input.readFloat(); + skeletonData.transformConstraints.add(data); + } + + // Path constraints. + for (int i = 0, n = input.readInt(true); i < n; i++) { + PathConstraintData data = new PathConstraintData(input.readString()); + data.bone = skeletonData.bones.get(input.readInt(true)); + data.target = skeletonData.slots.get(input.readInt(true)); + data.offsetRotation = input.readFloat(); + data.position = input.readFloat(); + 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 slotData = new SlotData(slotName, boneData); - Color.rgba8888ToColor(slotData.color, input.readInt()); - slotData.attachmentName = input.readString(); - slotData.blendMode = BlendMode.values[input.readInt(true)]; - skeletonData.slots.add(slotData); + 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. @@ -234,25 +248,18 @@ public class SkeletonBinary { if (skin == null) throw new SerializationException("Skin not found: " + linkedMesh.skin); Attachment parent = skin.getAttachment(linkedMesh.slotIndex, linkedMesh.parent); if (parent == null) throw new SerializationException("Parent mesh not found: " + linkedMesh.parent); - if (linkedMesh.mesh instanceof MeshAttachment) { - MeshAttachment mesh = (MeshAttachment)linkedMesh.mesh; - mesh.setParentMesh((MeshAttachment)parent); - mesh.updateUVs(); - } else { - WeightedMeshAttachment mesh = (WeightedMeshAttachment)linkedMesh.mesh; - mesh.setParentMesh((WeightedMeshAttachment)parent); - mesh.updateUVs(); - } + linkedMesh.mesh.setParentMesh((MeshAttachment)parent); + linkedMesh.mesh.updateUVs(); } linkedMeshes.clear(); // Events. for (int i = 0, n = input.readInt(true); i < n; i++) { - EventData eventData = new EventData(input.readString()); - eventData.intValue = input.readInt(false); - eventData.floatValue = input.readFloat(); - eventData.stringValue = input.readString(); - skeletonData.events.add(eventData); + EventData data = new EventData(input.readString()); + data.intValue = input.readInt(false); + data.floatValue = input.readFloat(); + data.stringValue = input.readString(); + skeletonData.events.add(data); } // Animations. @@ -328,21 +335,26 @@ public class SkeletonBinary { return region; } case boundingbox: { - float[] vertices = readFloatArray(input, input.readInt(true) * 2, scale); + int vertexCount = input.readInt(true); + Vertices vertices = readVertices(input, vertexCount); + int color = nonessential ? input.readInt() : 0; + BoundingBoxAttachment box = attachmentLoader.newBoundingBoxAttachment(skin, name); if (box == null) return null; - box.setVertices(vertices); + box.setWorldVerticesLength(vertexCount << 1); + box.setVertices(vertices.vertices); + box.setBones(vertices.bones); + if (nonessential) Color.rgba8888ToColor(box.getColor(), color); return box; } case mesh: { String path = input.readString(); int color = input.readInt(); - int hullLength = 0; - int verticesLength = input.readInt(true) * 2; - float[] uvs = readFloatArray(input, verticesLength, 1); + int vertexCount = input.readInt(true); + float[] uvs = readFloatArray(input, vertexCount << 1, 1); short[] triangles = readShortArray(input); - float[] vertices = readFloatArray(input, verticesLength, scale); - hullLength = input.readInt(true); + Vertices vertices = readVertices(input, vertexCount); + int hullLength = input.readInt(true); short[] edges = null; float width = 0, height = 0; if (nonessential) { @@ -356,11 +368,13 @@ public class SkeletonBinary { if (mesh == null) return null; mesh.setPath(path); Color.rgba8888ToColor(mesh.getColor(), color); - mesh.setVertices(vertices); + mesh.setBones(vertices.bones); + mesh.setVertices(vertices.vertices); + mesh.setWorldVerticesLength(vertexCount << 1); mesh.setTriangles(triangles); mesh.setRegionUVs(uvs); mesh.updateUVs(); - mesh.setHullLength(hullLength * 2); + mesh.setHullLength(hullLength << 1); if (nonessential) { mesh.setEdges(edges); mesh.setWidth(width * scale); @@ -373,7 +387,7 @@ public class SkeletonBinary { int color = input.readInt(); String skinName = input.readString(); String parent = input.readString(); - boolean inheritFFD = input.readBoolean(); + boolean inheritDeform = input.readBoolean(); float width = 0, height = 0; if (nonessential) { width = input.readFloat(); @@ -385,7 +399,7 @@ public class SkeletonBinary { if (mesh == null) return null; mesh.setPath(path); Color.rgba8888ToColor(mesh.getColor(), color); - mesh.setInheritFFD(inheritFFD); + mesh.setInheritDeform(inheritDeform); if (nonessential) { mesh.setWidth(width * scale); mesh.setHeight(height * scale); @@ -393,80 +407,47 @@ public class SkeletonBinary { linkedMeshes.add(new LinkedMesh(mesh, skinName, slotIndex, parent)); return mesh; } - case weightedmesh: { - String path = input.readString(); - int color = input.readInt(); + case path: { int vertexCount = input.readInt(true); - float[] uvs = readFloatArray(input, vertexCount * 2, 1); - short[] triangles = readShortArray(input); - 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 ii = 0; ii < boneCount; ii++) { - bones.add((int)input.readFloat()); - weights.add(input.readFloat() * scale); - weights.add(input.readFloat() * scale); - weights.add(input.readFloat()); - } - } - int hullLength = input.readInt(true); - short[] edges = null; - float width = 0, height = 0; - if (nonessential) { - edges = readShortArray(input); - width = input.readFloat(); - height = input.readFloat(); - } + Vertices vertices = readVertices(input, vertexCount); + int color = nonessential ? input.readInt() : 0; - if (path == null) path = name; - WeightedMeshAttachment mesh = attachmentLoader.newWeightedMeshAttachment(skin, name, path); - if (mesh == null) return null; - mesh.setPath(path); - Color.rgba8888ToColor(mesh.getColor(), color); - mesh.setBones(bones.toArray()); - mesh.setWeights(weights.toArray()); - mesh.setTriangles(triangles); - mesh.setRegionUVs(uvs); - mesh.updateUVs(); - mesh.setHullLength(hullLength * 2); - if (nonessential) { - mesh.setEdges(edges); - mesh.setWidth(width * scale); - mesh.setHeight(height * scale); - } - return mesh; - } - case weightedlinkedmesh: { - String path = input.readString(); - int color = input.readInt(); - String skinName = input.readString(); - String parent = input.readString(); - boolean inheritFFD = input.readBoolean(); - float width = 0, height = 0; - if (nonessential) { - width = input.readFloat(); - height = input.readFloat(); - } - - if (path == null) path = name; - WeightedMeshAttachment mesh = attachmentLoader.newWeightedMeshAttachment(skin, name, path); - if (mesh == null) return null; - mesh.setPath(path); - Color.rgba8888ToColor(mesh.getColor(), color); - mesh.setInheritFFD(inheritFFD); - if (nonessential) { - mesh.setWidth(width * scale); - mesh.setHeight(height * scale); - } - linkedMeshes.add(new LinkedMesh(mesh, skinName, slotIndex, parent)); - return mesh; + PathAttachment path = attachmentLoader.newPathAttachment(skin, name); + if (path == null) return null; + path.setWorldVerticesLength(vertexCount << 1); + path.setVertices(vertices.vertices); + path.setBones(vertices.bones); + if (nonessential) Color.rgba8888ToColor(path.getColor(), color); + return path; } } return null; } + private Vertices readVertices (DataInput input, int vertexCount) throws IOException { + int verticesLength = vertexCount << 1; + Vertices vertices = new Vertices(); + if (!input.readBoolean()) { + vertices.vertices = readFloatArray(input, verticesLength, scale); + return vertices; + } + FloatArray weights = new FloatArray(verticesLength * 3 * 3); + IntArray bonesArray = new IntArray(verticesLength * 3); + for (int i = 0; i < vertexCount; i++) { + int boneCount = input.readInt(true); + bonesArray.add(boneCount); + for (int ii = 0; ii < boneCount; ii++) { + bonesArray.add(input.readInt(true)); + weights.add(input.readFloat() * scale); + weights.add(input.readFloat() * scale); + weights.add(input.readFloat()); + } + } + vertices.vertices = weights.toArray(); + vertices.bones = bonesArray.toArray(); + return vertices; + } + private float[] readFloatArray (DataInput input, int n, float scale) throws IOException { float[] array = new float[n]; if (scale == 1) { @@ -572,10 +553,10 @@ public class SkeletonBinary { // IK constraint timelines. for (int i = 0, n = input.readInt(true); i < n; i++) { - IkConstraintData constraint = skeletonData.ikConstraints.get(input.readInt(true)); + int index = input.readInt(true); int frameCount = input.readInt(true); IkConstraintTimeline timeline = new IkConstraintTimeline(frameCount); - timeline.ikConstraintIndex = skeletonData.getIkConstraints().indexOf(constraint, true); + timeline.ikConstraintIndex = index; for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) { timeline.setFrame(frameIndex, input.readFloat(), input.readFloat(), input.readByte()); if (frameIndex < frameCount - 1) readCurve(input, frameIndex, timeline); @@ -586,10 +567,10 @@ public class SkeletonBinary { // Transform constraint timelines. for (int i = 0, n = input.readInt(true); i < n; i++) { - TransformConstraintData constraint = skeletonData.transformConstraints.get(input.readInt(true)); + int index = input.readInt(true); int frameCount = input.readInt(true); TransformConstraintTimeline timeline = new TransformConstraintTimeline(frameCount); - timeline.transformConstraintIndex = skeletonData.getTransformConstraints().indexOf(constraint, true); + timeline.transformConstraintIndex = index; for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) { timeline.setFrame(frameIndex, input.readFloat(), input.readFloat(), input.readFloat(), input.readFloat(), input.readFloat()); @@ -599,52 +580,60 @@ public class SkeletonBinary { duration = Math.max(duration, timeline.getFrames()[frameCount * 5 - 5]); } - // FFD timelines. + // Path constraint timelines. + for (int i = 0, n = input.readInt(true); i < n; i++) { + int index = input.readInt(true); + int frameCount = input.readInt(true); + PathConstraintTimeline timeline = new PathConstraintTimeline(frameCount); + timeline.pathConstraintIndex = index; + for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) { + timeline.setFrame(frameIndex, input.readFloat(), input.readFloat(), input.readFloat(), input.readFloat()); + if (frameIndex < frameCount - 1) readCurve(input, frameIndex, timeline); + } + timelines.add(timeline); + duration = Math.max(duration, timeline.getFrames()[frameCount * 4 - 4]); + } + + // Deform timelines. for (int i = 0, n = input.readInt(true); i < n; i++) { Skin skin = skeletonData.skins.get(input.readInt(true)); 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++) { - Attachment attachment = skin.getAttachment(slotIndex, input.readString()); + VertexAttachment attachment = (VertexAttachment)skin.getAttachment(slotIndex, input.readString()); + boolean weighted = attachment.getBones() != null; + float[] vertices = attachment.getVertices(); + int deformLength = weighted ? vertices.length / 3 * 2 : vertices.length; + int frameCount = input.readInt(true); - FfdTimeline timeline = new FfdTimeline(frameCount); + DeformTimeline timeline = new DeformTimeline(frameCount); timeline.slotIndex = slotIndex; timeline.attachment = attachment; + for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) { float time = input.readFloat(); - - float[] vertices; - int vertexCount; - if (attachment instanceof MeshAttachment) - vertexCount = ((MeshAttachment)attachment).getVertices().length; - else - vertexCount = ((WeightedMeshAttachment)attachment).getWeights().length / 3 * 2; - + float[] deform; 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]; + if (end == 0) + deform = weighted ? new float[deformLength] : vertices; + else { + deform = new float[deformLength]; int start = input.readInt(true); end += start; if (scale == 1) { for (int v = start; v < end; v++) - vertices[v] = input.readFloat(); + deform[v] = input.readFloat(); } else { for (int v = start; v < end; v++) - vertices[v] = input.readFloat() * scale; + deform[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]; + if (!weighted) { + for (int v = 0, vn = deform.length; v < vn; v++) + deform[v] += vertices[v]; } } - timeline.setFrame(frameIndex, time, vertices); + timeline.setFrame(frameIndex, time, deform); if (frameIndex < frameCount - 1) readCurve(input, frameIndex, timeline); } timelines.add(timeline); @@ -702,12 +691,17 @@ public class SkeletonBinary { timelines.add(timeline); duration = Math.max(duration, timeline.getFrames()[eventCount - 1]); } - } catch (IOException ex) { + } catch ( + + IOException ex) + + { throw new SerializationException("Error reading skeleton file.", ex); } timelines.shrink(); skeletonData.animations.add(new Animation(name, timelines, duration)); + } private void readCurve (DataInput input, int frameIndex, CurveTimeline timeline) throws IOException { @@ -724,4 +718,9 @@ public class SkeletonBinary { void setCurve (CurveTimeline timeline, int frameIndex, float cx1, float cy1, float cx2, float cy2) { timeline.setCurve(frameIndex, cx1, cy1, cx2, cy2); } + + static class Vertices { + int[] bones; + float[] vertices; + } } diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonBounds.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonBounds.java index d345a29a1..07831f211 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonBounds.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonBounds.java @@ -67,11 +67,7 @@ public class SkeletonBounds { FloatArray polygon = polygonPool.obtain(); polygons.add(polygon); - int vertexCount = boundingBox.getVertices().length; - polygon.ensureCapacity(vertexCount); - polygon.size = vertexCount; - - boundingBox.computeWorldVertices(slot.bone, polygon.items); + boundingBox.computeWorldVertices(slot, polygon.setSize(boundingBox.getWorldVerticesLength())); } } diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonData.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonData.java index 4914ebeb0..c48c63b5c 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonData.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonData.java @@ -43,6 +43,7 @@ public class SkeletonData { final Array animations = new Array(); final Array ikConstraints = new Array(); final Array transformConstraints = new Array(); + final Array pathConstraints = new Array(); float width, height; String version, hash, imagesPath; @@ -188,6 +189,23 @@ public class SkeletonData { return null; } + // --- Path constraints + + public Array getPathConstraints () { + return pathConstraints; + } + + /** @return May be null. */ + public PathConstraintData findPathConstraint (String constraintName) { + if (constraintName == null) throw new IllegalArgumentException("constraintName cannot be null."); + Array pathConstraints = this.pathConstraints; + for (int i = 0, n = pathConstraints.size; i < n; i++) { + PathConstraintData constraint = pathConstraints.get(i); + if (constraint.name.equals(constraintName)) return constraint; + } + return null; + } + // --- /** @return May be null. */ 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 5e97caabc..0a42150da 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonJson.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonJson.java @@ -43,10 +43,11 @@ import com.badlogic.gdx.utils.SerializationException; import com.esotericsoftware.spine.Animation.AttachmentTimeline; import com.esotericsoftware.spine.Animation.ColorTimeline; import com.esotericsoftware.spine.Animation.CurveTimeline; +import com.esotericsoftware.spine.Animation.DeformTimeline; import com.esotericsoftware.spine.Animation.DrawOrderTimeline; import com.esotericsoftware.spine.Animation.EventTimeline; -import com.esotericsoftware.spine.Animation.FfdTimeline; import com.esotericsoftware.spine.Animation.IkConstraintTimeline; +import com.esotericsoftware.spine.Animation.PathConstraintTimeline; import com.esotericsoftware.spine.Animation.RotateTimeline; import com.esotericsoftware.spine.Animation.ScaleTimeline; import com.esotericsoftware.spine.Animation.ShearTimeline; @@ -59,8 +60,9 @@ import com.esotericsoftware.spine.attachments.AttachmentLoader; import com.esotericsoftware.spine.attachments.AttachmentType; import com.esotericsoftware.spine.attachments.BoundingBoxAttachment; import com.esotericsoftware.spine.attachments.MeshAttachment; +import com.esotericsoftware.spine.attachments.PathAttachment; import com.esotericsoftware.spine.attachments.RegionAttachment; -import com.esotericsoftware.spine.attachments.WeightedMeshAttachment; +import com.esotericsoftware.spine.attachments.VertexAttachment; public class SkeletonJson { private final AttachmentLoader attachmentLoader; @@ -112,70 +114,90 @@ public class SkeletonJson { parent = skeletonData.findBone(parentName); if (parent == null) throw new SerializationException("Parent bone not found: " + parentName); } - BoneData boneData = new BoneData(boneMap.getString("name"), parent); - boneData.length = boneMap.getFloat("length", 0) * scale; - boneData.x = boneMap.getFloat("x", 0) * scale; - boneData.y = boneMap.getFloat("y", 0) * scale; - boneData.rotation = boneMap.getFloat("rotation", 0); - boneData.scaleX = boneMap.getFloat("scaleX", 1); - boneData.scaleY = boneMap.getFloat("scaleY", 1); - boneData.shearX = boneMap.getFloat("shearX", 0); - boneData.shearY = boneMap.getFloat("shearY", 0); - boneData.inheritScale = boneMap.getBoolean("inheritScale", true); - boneData.inheritRotation = boneMap.getBoolean("inheritRotation", true); + BoneData data = new BoneData(skeletonData.bones.size, boneMap.getString("name"), parent); + data.length = boneMap.getFloat("length", 0) * scale; + data.x = boneMap.getFloat("x", 0) * scale; + data.y = boneMap.getFloat("y", 0) * scale; + data.rotation = boneMap.getFloat("rotation", 0); + data.scaleX = boneMap.getFloat("scaleX", 1); + data.scaleY = boneMap.getFloat("scaleY", 1); + data.shearX = boneMap.getFloat("shearX", 0); + data.shearY = boneMap.getFloat("shearY", 0); + data.inheritScale = boneMap.getBoolean("inheritScale", true); + data.inheritRotation = boneMap.getBoolean("inheritRotation", true); String color = boneMap.getString("color", null); - if (color != null) boneData.getColor().set(Color.valueOf(color)); + if (color != null) data.getColor().set(Color.valueOf(color)); - skeletonData.bones.add(boneData); + skeletonData.bones.add(data); } // IK constraints. - for (JsonValue ikMap = root.getChild("ik"); ikMap != null; ikMap = ikMap.next) { - IkConstraintData ikConstraintData = new IkConstraintData(ikMap.getString("name")); + for (JsonValue constraintMap = root.getChild("ik"); constraintMap != null; constraintMap = constraintMap.next) { + IkConstraintData data = new IkConstraintData(constraintMap.getString("name")); - for (JsonValue boneMap = ikMap.getChild("bones"); boneMap != null; boneMap = boneMap.next) { + for (JsonValue boneMap = constraintMap.getChild("bones"); boneMap != null; boneMap = boneMap.next) { String boneName = boneMap.asString(); BoneData bone = skeletonData.findBone(boneName); if (bone == null) throw new SerializationException("IK bone not found: " + boneName); - ikConstraintData.bones.add(bone); + data.bones.add(bone); } - String targetName = ikMap.getString("target"); - ikConstraintData.target = skeletonData.findBone(targetName); - if (ikConstraintData.target == null) throw new SerializationException("Target bone not found: " + targetName); + String targetName = constraintMap.getString("target"); + data.target = skeletonData.findBone(targetName); + if (data.target == null) throw new SerializationException("Target bone not found: " + targetName); - ikConstraintData.bendDirection = ikMap.getBoolean("bendPositive", true) ? 1 : -1; - ikConstraintData.mix = ikMap.getFloat("mix", 1); + data.bendDirection = constraintMap.getBoolean("bendPositive", true) ? 1 : -1; + data.mix = constraintMap.getFloat("mix", 1); - skeletonData.ikConstraints.add(ikConstraintData); + skeletonData.ikConstraints.add(data); } // Transform constraints. - for (JsonValue transformMap = root.getChild("transform"); transformMap != null; transformMap = transformMap.next) { - TransformConstraintData transformConstraintData = new TransformConstraintData(transformMap.getString("name")); + for (JsonValue constraintMap = root.getChild("transform"); constraintMap != null; constraintMap = constraintMap.next) { + TransformConstraintData data = new TransformConstraintData(constraintMap.getString("name")); - String boneName = transformMap.getString("bone"); - transformConstraintData.bone = skeletonData.findBone(boneName); - if (transformConstraintData.bone == null) throw new SerializationException("Bone not found: " + boneName); + String boneName = constraintMap.getString("bone"); + data.bone = skeletonData.findBone(boneName); + if (data.bone == null) throw new SerializationException("Bone not found: " + boneName); - String targetName = transformMap.getString("target"); - transformConstraintData.target = skeletonData.findBone(targetName); - if (transformConstraintData.target == null) throw new SerializationException("Target bone not found: " + targetName); + String targetName = constraintMap.getString("target"); + data.target = skeletonData.findBone(targetName); + if (data.target == null) throw new SerializationException("Target bone not found: " + targetName); - transformConstraintData.offsetRotation = transformMap.getFloat("rotation", 0); - transformConstraintData.offsetX = transformMap.getFloat("x", 0) * scale; - transformConstraintData.offsetY = transformMap.getFloat("y", 0) * scale; - transformConstraintData.offsetScaleX = transformMap.getFloat("scaleX", 0) * scale; - transformConstraintData.offsetScaleY = transformMap.getFloat("scaleY", 0) * scale; - transformConstraintData.offsetShearY = transformMap.getFloat("shearY", 0) * scale; + data.offsetRotation = constraintMap.getFloat("rotation", 0); + data.offsetX = constraintMap.getFloat("x", 0) * scale; + data.offsetY = constraintMap.getFloat("y", 0) * scale; + data.offsetScaleX = constraintMap.getFloat("scaleX", 0) * scale; + data.offsetScaleY = constraintMap.getFloat("scaleY", 0) * scale; + data.offsetShearY = constraintMap.getFloat("shearY", 0) * scale; - transformConstraintData.rotateMix = transformMap.getFloat("rotateMix", 1); - transformConstraintData.translateMix = transformMap.getFloat("translateMix", 1); - transformConstraintData.scaleMix = transformMap.getFloat("scaleMix", 1); - transformConstraintData.shearMix = transformMap.getFloat("shearMix", 1); + data.rotateMix = constraintMap.getFloat("rotateMix", 1); + data.translateMix = constraintMap.getFloat("translateMix", 1); + data.scaleMix = constraintMap.getFloat("scaleMix", 1); + data.shearMix = constraintMap.getFloat("shearMix", 1); - skeletonData.transformConstraints.add(transformConstraintData); + skeletonData.transformConstraints.add(data); + } + + // Path constraints. + for (JsonValue constraintMap = root.getChild("path"); constraintMap != null; constraintMap = constraintMap.next) { + PathConstraintData data = new PathConstraintData(constraintMap.getString("name")); + + String boneName = constraintMap.getString("bone"); + data.bone = skeletonData.findBone(boneName); + if (data.bone == null) throw new SerializationException("Bone not found: " + boneName); + + String targetName = constraintMap.getString("target"); + data.target = skeletonData.findSlot(targetName); + if (data.target == null) throw new SerializationException("Target slot not found: " + targetName); + + data.offsetRotation = constraintMap.getFloat("rotation", 0); + data.position = constraintMap.getFloat("position", 0); + data.rotateMix = constraintMap.getFloat("rotateMix", 1); + data.translateMix = constraintMap.getFloat("translateMix", 1); + + skeletonData.pathConstraints.add(data); } // Slots. @@ -184,14 +206,14 @@ public class SkeletonJson { String boneName = slotMap.getString("bone"); BoneData boneData = skeletonData.findBone(boneName); if (boneData == null) throw new SerializationException("Slot bone not found: " + boneName); - SlotData slotData = new SlotData(slotName, boneData); + SlotData data = new SlotData(skeletonData.slots.size, slotName, boneData); String color = slotMap.getString("color", null); - if (color != null) slotData.getColor().set(Color.valueOf(color)); + if (color != null) data.getColor().set(Color.valueOf(color)); - slotData.attachmentName = slotMap.getString("attachment", null); - slotData.blendMode = BlendMode.valueOf(slotMap.getString("blend", BlendMode.normal.name())); - skeletonData.slots.add(slotData); + data.attachmentName = slotMap.getString("attachment", null); + data.blendMode = BlendMode.valueOf(slotMap.getString("blend", BlendMode.normal.name())); + skeletonData.slots.add(data); } // Skins. @@ -201,7 +223,7 @@ public class SkeletonJson { int slotIndex = skeletonData.findSlotIndex(slotEntry.name); if (slotIndex == -1) throw new SerializationException("Slot not found: " + slotEntry.name); for (JsonValue entry = slotEntry.child; entry != null; entry = entry.next) { - Attachment attachment = readAttachment(skin, slotIndex, entry.name, entry); + Attachment attachment = readAttachment(entry, skin, slotIndex, entry.name); if (attachment != null) skin.addAttachment(slotIndex, entry.name, attachment); } } @@ -216,30 +238,23 @@ public class SkeletonJson { if (skin == null) throw new SerializationException("Skin not found: " + linkedMesh.skin); Attachment parent = skin.getAttachment(linkedMesh.slotIndex, linkedMesh.parent); if (parent == null) throw new SerializationException("Parent mesh not found: " + linkedMesh.parent); - if (linkedMesh.mesh instanceof MeshAttachment) { - MeshAttachment mesh = (MeshAttachment)linkedMesh.mesh; - mesh.setParentMesh((MeshAttachment)parent); - mesh.updateUVs(); - } else { - WeightedMeshAttachment mesh = (WeightedMeshAttachment)linkedMesh.mesh; - mesh.setParentMesh((WeightedMeshAttachment)parent); - mesh.updateUVs(); - } + linkedMesh.mesh.setParentMesh((MeshAttachment)parent); + linkedMesh.mesh.updateUVs(); } linkedMeshes.clear(); // Events. for (JsonValue eventMap = root.getChild("events"); eventMap != null; eventMap = eventMap.next) { - EventData eventData = new EventData(eventMap.name); - eventData.intValue = eventMap.getInt("int", 0); - eventData.floatValue = eventMap.getFloat("float", 0f); - eventData.stringValue = eventMap.getString("string", null); - skeletonData.events.add(eventData); + EventData data = new EventData(eventMap.name); + data.intValue = eventMap.getInt("int", 0); + data.floatValue = eventMap.getFloat("float", 0f); + data.stringValue = eventMap.getString("string", null); + skeletonData.events.add(data); } // Animations. for (JsonValue animationMap = root.getChild("animations"); animationMap != null; animationMap = animationMap.next) - readAnimation(animationMap.name, animationMap, skeletonData); + readAnimation(animationMap, animationMap.name, skeletonData); skeletonData.bones.shrink(); skeletonData.slots.shrink(); @@ -250,15 +265,20 @@ public class SkeletonJson { return skeletonData; } - private Attachment readAttachment (Skin skin, int slotIndex, String name, JsonValue map) { + private Attachment readAttachment (JsonValue map, Skin skin, int slotIndex, String name) { float scale = this.scale; name = map.getString("name", name); - String path = map.getString("path", name); String type = map.getString("type", AttachmentType.region.name()); + + // Warning: These types are deprecated and will be removed in the near future. if (type.equals("skinnedmesh")) type = "weightedmesh"; + if (type.equals("weightedmesh")) type = "mesh"; + if (type.equals("weightedlinkedmesh")) type = "linkedmesh"; + switch (AttachmentType.valueOf(type)) { case region: { + String path = map.getString("path", name); RegionAttachment region = attachmentLoader.newRegionAttachment(skin, name, path); if (region == null) return null; region.setPath(path); @@ -279,16 +299,15 @@ public class SkeletonJson { case boundingbox: { BoundingBoxAttachment box = attachmentLoader.newBoundingBoxAttachment(skin, name); if (box == null) return null; - float[] vertices = map.require("vertices").asFloatArray(); - if (scale != 1) { - for (int i = 0, n = vertices.length; i < n; i++) - vertices[i] *= scale; - } - box.setVertices(vertices); + readVertices(map, box, map.getInt("vertexCount") << 1); + + String color = map.getString("color", null); + if (color != null) box.getColor().set(Color.valueOf(color)); return box; } case mesh: case linkedmesh: { + String path = map.getString("path", name); MeshAttachment mesh = attachmentLoader.newMeshAttachment(skin, name, path); if (mesh == null) return null; mesh.setPath(path); @@ -300,81 +319,63 @@ public class SkeletonJson { mesh.setHeight(map.getFloat("height", 0) * scale); String parent = map.getString("parent", null); - if (parent == null) { - float[] vertices = map.require("vertices").asFloatArray(); - if (scale != 1) { - for (int i = 0, n = vertices.length; i < n; i++) - vertices[i] *= scale; - } - mesh.setVertices(vertices); - mesh.setTriangles(map.require("triangles").asShortArray()); - mesh.setRegionUVs(map.require("uvs").asFloatArray()); - mesh.updateUVs(); - - if (map.has("hull")) mesh.setHullLength(map.require("hull").asInt() * 2); - if (map.has("edges")) mesh.setEdges(map.require("edges").asShortArray()); - } else { - mesh.setInheritFFD(map.getBoolean("ffd", true)); + if (parent != null) { + mesh.setInheritDeform(map.getBoolean("deform", true)); linkedMeshes.add(new LinkedMesh(mesh, map.getString("skin", null), slotIndex, parent)); + return mesh; } + + float[] uvs = map.require("uvs").asFloatArray(); + readVertices(map, mesh, uvs.length); + mesh.setTriangles(map.require("triangles").asShortArray()); + mesh.setRegionUVs(uvs); + mesh.updateUVs(); + + if (map.has("hull")) mesh.setHullLength(map.require("hull").asInt() * 2); + if (map.has("edges")) mesh.setEdges(map.require("edges").asShortArray()); return mesh; } - case weightedmesh: - case weightedlinkedmesh: { - WeightedMeshAttachment mesh = attachmentLoader.newWeightedMeshAttachment(skin, name, path); - if (mesh == null) return null; - mesh.setPath(path); + case path: { + PathAttachment path = attachmentLoader.newPathAttachment(skin, name); + if (path == null) return null; + readVertices(map, path, map.getInt("vertexCount") << 1); String color = map.getString("color", null); - if (color != null) mesh.getColor().set(Color.valueOf(color)); - - mesh.setWidth(map.getFloat("width", 0) * scale); - mesh.setHeight(map.getFloat("height", 0) * scale); - - String parent = map.getString("parent", null); - if (parent == null) { - float[] uvs = map.require("uvs").asFloatArray(); - 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; i += 4) { - bones.add((int)vertices[i]); - weights.add(vertices[i + 1] * scale); - weights.add(vertices[i + 2] * scale); - weights.add(vertices[i + 3]); - } - } - mesh.setBones(bones.toArray()); - mesh.setWeights(weights.toArray()); - mesh.setTriangles(map.require("triangles").asShortArray()); - mesh.setRegionUVs(uvs); - mesh.updateUVs(); - - if (map.has("hull")) mesh.setHullLength(map.require("hull").asInt() * 2); - if (map.has("edges")) mesh.setEdges(map.require("edges").asShortArray()); - } else { - mesh.setInheritFFD(map.getBoolean("ffd", true)); - linkedMeshes.add(new LinkedMesh(mesh, map.getString("skin", null), slotIndex, parent)); - } - return mesh; + if (color != null) path.getColor().set(Color.valueOf(color)); + return path; } } - - // RegionSequenceAttachment regionSequenceAttachment = (RegionSequenceAttachment)attachment; - // - // float fps = map.getFloat("fps"); - // regionSequenceAttachment.setFrameTime(fps); - // - // String modeString = map.getString("mode"); - // regionSequenceAttachment.setMode(modeString == null ? Mode.forward : Mode.valueOf(modeString)); - return null; } - private void readAnimation (String name, JsonValue map, SkeletonData skeletonData) { + private void readVertices (JsonValue map, VertexAttachment attachment, int verticesLength) { + attachment.setWorldVerticesLength(verticesLength); + float[] vertices = map.require("vertices").asFloatArray(); + if (verticesLength == vertices.length) { + if (scale != 1) { + for (int i = 0, n = vertices.length; i < n; i++) + vertices[i] *= scale; + } + attachment.setVertices(vertices); + return; + } + FloatArray weights = new FloatArray(verticesLength * 3 * 3); + IntArray bones = new IntArray(verticesLength * 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; i += 4) { + bones.add((int)vertices[i]); + weights.add(vertices[i + 1] * scale); + weights.add(vertices[i + 2] * scale); + weights.add(vertices[i + 3]); + } + } + attachment.setBones(bones.toArray()); + attachment.setVertices(weights.toArray()); + } + + private void readAnimation (JsonValue map, String name, SkeletonData skeletonData) { float scale = this.scale; Array timelines = new Array(); float duration = 0; @@ -394,7 +395,7 @@ public class SkeletonJson { for (JsonValue valueMap = timelineMap.child; valueMap != null; valueMap = valueMap.next) { Color color = Color.valueOf(valueMap.getString("color")); timeline.setFrame(frameIndex, valueMap.getFloat("time"), color.r, color.g, color.b, color.a); - readCurve(timeline, frameIndex, valueMap); + readCurve(valueMap, timeline, frameIndex); frameIndex++; } timelines.add(timeline); @@ -428,7 +429,7 @@ public class SkeletonJson { int frameIndex = 0; for (JsonValue valueMap = timelineMap.child; valueMap != null; valueMap = valueMap.next) { timeline.setFrame(frameIndex, valueMap.getFloat("time"), valueMap.getFloat("angle")); - readCurve(timeline, frameIndex, valueMap); + readCurve(valueMap, timeline, frameIndex); frameIndex++; } timelines.add(timeline); @@ -451,7 +452,7 @@ public class SkeletonJson { for (JsonValue valueMap = timelineMap.child; valueMap != null; valueMap = valueMap.next) { float x = valueMap.getFloat("x", 0), y = valueMap.getFloat("y", 0); timeline.setFrame(frameIndex, valueMap.getFloat("time"), x * timelineScale, y * timelineScale); - readCurve(timeline, frameIndex, valueMap); + readCurve(valueMap, timeline, frameIndex); frameIndex++; } timelines.add(timeline); @@ -471,7 +472,7 @@ public class SkeletonJson { for (JsonValue valueMap = constraintMap.child; valueMap != null; valueMap = valueMap.next) { timeline.setFrame(frameIndex, valueMap.getFloat("time"), valueMap.getFloat("mix", 1), valueMap.getBoolean("bendPositive") ? 1 : -1); - readCurve(timeline, frameIndex, valueMap); + readCurve(valueMap, timeline, frameIndex); frameIndex++; } timelines.add(timeline); @@ -487,59 +488,69 @@ public class SkeletonJson { for (JsonValue valueMap = constraintMap.child; valueMap != null; valueMap = valueMap.next) { timeline.setFrame(frameIndex, valueMap.getFloat("time"), valueMap.getFloat("rotateMix", 1), valueMap.getFloat("translateMix", 1), valueMap.getFloat("scaleMix", 1), valueMap.getFloat("shearMix", 1)); - readCurve(timeline, frameIndex, valueMap); + readCurve(valueMap, timeline, frameIndex); frameIndex++; } timelines.add(timeline); duration = Math.max(duration, timeline.getFrames()[timeline.getFrameCount() * 5 - 5]); } - // FFD timelines. - for (JsonValue ffdMap = map.getChild("ffd"); ffdMap != null; ffdMap = ffdMap.next) { - Skin skin = skeletonData.findSkin(ffdMap.name); - if (skin == null) throw new SerializationException("Skin not found: " + ffdMap.name); - for (JsonValue slotMap = ffdMap.child; slotMap != null; slotMap = slotMap.next) { + // Path constraint timelines. + for (JsonValue constraintMap = map.getChild("path"); constraintMap != null; constraintMap = constraintMap.next) { + PathConstraintData constraint = skeletonData.findPathConstraint(constraintMap.name); + PathConstraintTimeline timeline = new PathConstraintTimeline(constraintMap.size); + timeline.pathConstraintIndex = skeletonData.getPathConstraints().indexOf(constraint, true); + int frameIndex = 0; + for (JsonValue valueMap = constraintMap.child; valueMap != null; valueMap = valueMap.next) { + timeline.setFrame(frameIndex, valueMap.getFloat("time"), valueMap.getFloat("scaleMix", 1), + valueMap.getFloat("rotateMix", 1), valueMap.getFloat("translateMix", 1)); + readCurve(valueMap, timeline, frameIndex); + frameIndex++; + } + timelines.add(timeline); + duration = Math.max(duration, timeline.getFrames()[timeline.getFrameCount() * 4 - 4]); + } + + // Deform timelines. + for (JsonValue deformMap = map.getChild("deform"); deformMap != null; deformMap = deformMap.next) { + Skin skin = skeletonData.findSkin(deformMap.name); + if (skin == null) throw new SerializationException("Skin not found: " + deformMap.name); + for (JsonValue slotMap = deformMap.child; slotMap != null; slotMap = slotMap.next) { int slotIndex = skeletonData.findSlotIndex(slotMap.name); 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); - Attachment attachment = skin.getAttachment(slotIndex, meshMap.name); - if (attachment == null) throw new SerializationException("FFD attachment not found: " + meshMap.name); + for (JsonValue timelineMap = slotMap.child; timelineMap != null; timelineMap = timelineMap.next) { + VertexAttachment attachment = (VertexAttachment)skin.getAttachment(slotIndex, timelineMap.name); + if (attachment == null) throw new SerializationException("Deform attachment not found: " + timelineMap.name); + boolean weighted = attachment.getBones() != null; + float[] vertices = attachment.getVertices(); + int deformLength = weighted ? vertices.length / 3 * 2 : vertices.length; + + DeformTimeline timeline = new DeformTimeline(timelineMap.size); timeline.slotIndex = slotIndex; timeline.attachment = attachment; - int vertexCount; - if (attachment instanceof MeshAttachment) - vertexCount = ((MeshAttachment)attachment).getVertices().length; - else - vertexCount = ((WeightedMeshAttachment)attachment).getWeights().length / 3 * 2; - int frameIndex = 0; - for (JsonValue valueMap = meshMap.child; valueMap != null; valueMap = valueMap.next) { - float[] vertices; + for (JsonValue valueMap = timelineMap.child; valueMap != null; valueMap = valueMap.next) { + float[] deform; JsonValue verticesValue = valueMap.get("vertices"); - if (verticesValue == null) { - if (attachment instanceof MeshAttachment) - vertices = ((MeshAttachment)attachment).getVertices(); - else - vertices = new float[vertexCount]; - } else { - vertices = new float[vertexCount]; + if (verticesValue == null) + deform = weighted ? new float[deformLength] : vertices; + else { + deform = new float[deformLength]; int start = valueMap.getInt("offset", 0); - System.arraycopy(verticesValue.asFloatArray(), 0, vertices, start, verticesValue.size); + System.arraycopy(verticesValue.asFloatArray(), 0, deform, start, verticesValue.size); if (scale != 1) { for (int i = start, n = i + verticesValue.size; i < n; i++) - vertices[i] *= scale; + deform[i] *= scale; } - if (attachment instanceof MeshAttachment) { - float[] meshVertices = ((MeshAttachment)attachment).getVertices(); - for (int i = 0; i < vertexCount; i++) - vertices[i] += meshVertices[i]; + if (!weighted) { + for (int i = 0; i < deformLength; i++) + deform[i] += vertices[i]; } } - timeline.setFrame(frameIndex, valueMap.getFloat("time"), vertices); - readCurve(timeline, frameIndex, valueMap); + timeline.setFrame(frameIndex, valueMap.getFloat("time"), deform); + readCurve(valueMap, timeline, frameIndex); frameIndex++; } timelines.add(timeline); @@ -608,8 +619,8 @@ public class SkeletonJson { skeletonData.animations.add(new Animation(name, timelines, duration)); } - void readCurve (CurveTimeline timeline, int frameIndex, JsonValue valueMap) { - JsonValue curve = valueMap.get("curve"); + void readCurve (JsonValue map, CurveTimeline timeline, int frameIndex) { + JsonValue curve = map.get("curve"); if (curve == null) return; if (curve.isString() && curve.asString().equals("stepped")) timeline.setStepped(frameIndex); @@ -621,9 +632,9 @@ public class SkeletonJson { static class LinkedMesh { String parent, skin; int slotIndex; - Attachment mesh; + MeshAttachment mesh; - public LinkedMesh (Attachment mesh, String skin, int slotIndex, String parent) { + public LinkedMesh (MeshAttachment mesh, String skin, int slotIndex, String parent) { this.mesh = mesh; this.skin = skin; this.slotIndex = slotIndex; diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonMeshRenderer.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonMeshRenderer.java index 9033b90c8..e6db7b114 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonMeshRenderer.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonMeshRenderer.java @@ -35,10 +35,9 @@ import com.badlogic.gdx.graphics.Texture; import com.badlogic.gdx.graphics.g2d.PolygonSpriteBatch; import com.badlogic.gdx.utils.Array; 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.WeightedMeshAttachment; +import com.esotericsoftware.spine.attachments.MeshAttachment; public class SkeletonMeshRenderer extends SkeletonRenderer { static private final short[] quadTriangles = {0, 1, 2, 2, 3, 0}; @@ -67,12 +66,6 @@ public class SkeletonMeshRenderer extends SkeletonRenderer { triangles = mesh.getTriangles(); texture = mesh.getRegion().getTexture(); - } else if (attachment instanceof WeightedMeshAttachment) { - WeightedMeshAttachment mesh = (WeightedMeshAttachment)attachment; - vertices = mesh.updateWorldVertices(slot, premultipliedAlpha); - triangles = mesh.getTriangles(); - texture = mesh.getRegion().getTexture(); - } else if (attachment instanceof SkeletonAttachment) { Skeleton attachmentSkeleton = ((SkeletonAttachment)attachment).getSkeleton(); if (attachmentSkeleton == null) continue; diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonRenderer.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonRenderer.java index eee9055b3..5a4321727 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonRenderer.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonRenderer.java @@ -34,18 +34,13 @@ package com.esotericsoftware.spine; import com.badlogic.gdx.graphics.g2d.Batch; import com.badlogic.gdx.utils.Array; 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.WeightedMeshAttachment; +import com.esotericsoftware.spine.attachments.MeshAttachment; public class SkeletonRenderer { boolean premultipliedAlpha; - public SkeletonRenderer () { - super(); - } - public void draw (T batch, Skeleton skeleton) { boolean premultipliedAlpha = this.premultipliedAlpha; BlendMode blendMode = null; @@ -64,7 +59,7 @@ public class SkeletonRenderer { } batch.draw(regionAttachment.getRegion().getTexture(), vertices, 0, 20); - } else if (attachment instanceof MeshAttachment || attachment instanceof WeightedMeshAttachment) { + } else if (attachment instanceof MeshAttachment) { throw new RuntimeException("SkeletonMeshRenderer is required to render meshes."); } else if (attachment instanceof SkeletonAttachment) { diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonRendererDebug.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonRendererDebug.java index 130a08efb..253ad77f2 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonRendererDebug.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonRendererDebug.java @@ -31,11 +31,6 @@ 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.WeightedMeshAttachment; - import static com.badlogic.gdx.graphics.g2d.Batch.*; import com.badlogic.gdx.Gdx; @@ -45,19 +40,24 @@ import com.badlogic.gdx.graphics.glutils.ShapeRenderer; import com.badlogic.gdx.graphics.glutils.ShapeRenderer.ShapeType; import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.FloatArray; +import com.esotericsoftware.spine.attachments.Attachment; +import com.esotericsoftware.spine.attachments.BoundingBoxAttachment; +import com.esotericsoftware.spine.attachments.MeshAttachment; +import com.esotericsoftware.spine.attachments.PathAttachment; +import com.esotericsoftware.spine.attachments.RegionAttachment; public class SkeletonRendererDebug { static private final Color boneLineColor = Color.RED; static private final Color boneOriginColor = Color.GREEN; 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 shapes; private boolean drawBones = true, drawRegionAttachments = true, drawBoundingBoxes = true; - private boolean drawMeshHull = true, drawMeshTriangles = true; + private boolean drawMeshHull = true, drawMeshTriangles = true, drawPaths = true; private final SkeletonBounds bounds = new SkeletonBounds(); + private final FloatArray temp = new FloatArray(); private float scale = 1; private float boneWidth = 2; private boolean premultipliedAlpha; @@ -119,23 +119,12 @@ public class SkeletonRendererDebug { 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; - int hullLength = 0; - if (attachment instanceof MeshAttachment) { - MeshAttachment mesh = (MeshAttachment)attachment; - mesh.updateWorldVertices(slot, false); - vertices = mesh.getWorldVertices(); - triangles = mesh.getTriangles(); - hullLength = mesh.getHullLength(); - } else if (attachment instanceof WeightedMeshAttachment) { - WeightedMeshAttachment mesh = (WeightedMeshAttachment)attachment; - mesh.updateWorldVertices(slot, false); - vertices = mesh.getWorldVertices(); - triangles = mesh.getTriangles(); - hullLength = mesh.getHullLength(); - } - if (vertices == null || triangles == null) continue; + if (!(attachment instanceof MeshAttachment)) continue; + MeshAttachment mesh = (MeshAttachment)attachment; + mesh.updateWorldVertices(slot, false); + float[] vertices = mesh.getWorldVertices(); + short[] triangles = mesh.getTriangles(); + int hullLength = mesh.getHullLength(); if (drawMeshTriangles) { shapes.setColor(triangleLineColor); for (int ii = 0, nn = triangles.length; ii < nn; ii += 3) { @@ -143,7 +132,7 @@ public class SkeletonRendererDebug { shapes.triangle(vertices[v1], vertices[v1 + 1], // vertices[v2], vertices[v2 + 1], // vertices[v3], vertices[v3 + 1] // - ); + ); } } if (drawMeshHull && hullLength > 0) { @@ -165,14 +154,39 @@ public class SkeletonRendererDebug { bounds.update(skeleton, true); shapes.setColor(aabbColor); shapes.rect(bounds.getMinX(), bounds.getMinY(), bounds.getWidth(), bounds.getHeight()); - shapes.setColor(boundingBoxColor); Array polygons = bounds.getPolygons(); + Array boxes = bounds.getBoundingBoxes(); for (int i = 0, n = polygons.size; i < n; i++) { FloatArray polygon = polygons.get(i); + shapes.setColor(boxes.get(i).getColor()); shapes.polygon(polygon.items, 0, polygon.size); } } + if (drawPaths) { + Array slots = skeleton.getSlots(); + for (int i = 0, n = slots.size; i < n; i++) { + Slot slot = slots.get(i); + Attachment attachment = slot.attachment; + if (!(attachment instanceof PathAttachment)) continue; + PathAttachment path = (PathAttachment)attachment; + int nn = path.getWorldVerticesLength(); + float[] worldVertices = temp.setSize(nn); + path.computeWorldVertices(slot, worldVertices); + shapes.setColor(path.getColor()); + float x1 = worldVertices[0], y1 = worldVertices[1]; + float cx1 = x1 + (x1 - worldVertices[2]), cy1 = y1 + (y1 - worldVertices[3]); + for (int ii = 4; ii < nn; ii += 4) { + float x2 = worldVertices[ii], y2 = worldVertices[ii + 1], cx2 = worldVertices[ii + 2], cy2 = worldVertices[ii + 3]; + shapes.curve(x1, y1, cx1, cy1, cx2, cy2, x2, y2, 32); + x1 = x2; + y1 = y2; + cx1 = x2 + (x2 - cx2); + cy1 = y2 + (y2 - cy2); + } + } + } + shapes.end(); shapes.begin(ShapeType.Filled); @@ -186,6 +200,7 @@ public class SkeletonRendererDebug { } shapes.end(); + } public ShapeRenderer getShapeRenderer () { @@ -216,6 +231,10 @@ public class SkeletonRendererDebug { this.drawMeshTriangles = meshTriangles; } + public void setPaths (boolean paths) { + this.drawPaths = paths; + } + public void setPremultipliedAlpha (boolean premultipliedAlpha) { this.premultipliedAlpha = premultipliedAlpha; } diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Slot.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Slot.java index c5f510051..cac1e1644 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Slot.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Slot.java @@ -44,12 +44,6 @@ public class Slot { private float attachmentTime; private FloatArray attachmentVertices = new FloatArray(); - Slot (SlotData data) { - this.data = data; - bone = null; - color = new Color(1, 1, 1, 1); - } - public Slot (SlotData data, Bone bone) { if (data == null) throw new IllegalArgumentException("data cannot be null."); if (bone == null) throw new IllegalArgumentException("bone cannot be null."); @@ -117,20 +111,16 @@ public class Slot { return attachmentVertices; } - void setToSetupPose (int slotIndex) { + public void setToSetupPose () { color.set(data.color); if (data.attachmentName == null) setAttachment(null); else { attachment = null; - setAttachment(bone.skeleton.getAttachment(slotIndex, data.attachmentName)); + setAttachment(bone.skeleton.getAttachment(data.index, data.attachmentName)); } } - public void setToSetupPose () { - setToSetupPose(bone.skeleton.data.slots.indexOf(data, true)); - } - public String toString () { return data.name; } diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SlotData.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SlotData.java index 951e42d23..1958251b3 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SlotData.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SlotData.java @@ -34,20 +34,17 @@ package com.esotericsoftware.spine; import com.badlogic.gdx.graphics.Color; public class SlotData { + final int index; final String name; final BoneData boneData; final Color color = new Color(1, 1, 1, 1); String attachmentName; BlendMode blendMode; - SlotData () { - name = null; - boneData = null; - } - - public SlotData (String name, BoneData boneData) { + public SlotData (int index, String name, BoneData boneData) { if (name == null) throw new IllegalArgumentException("name cannot be null."); if (boneData == null) throw new IllegalArgumentException("boneData cannot be null."); + this.index = index; this.name = name; this.boneData = boneData; } diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/TransformConstraint.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/TransformConstraint.java index d95bdf301..171227ade 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/TransformConstraint.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/TransformConstraint.java @@ -9,20 +9,14 @@ public class TransformConstraint implements Updatable { final TransformConstraintData data; Bone bone, target; float rotateMix, translateMix, scaleMix, shearMix; - float offsetRotation, offsetX, offsetY, offsetScaleX, offsetScaleY, offsetShearY; final Vector2 temp = new Vector2(); public TransformConstraint (TransformConstraintData data, Skeleton skeleton) { this.data = data; - translateMix = data.translateMix; rotateMix = data.rotateMix; + translateMix = data.translateMix; scaleMix = data.scaleMix; shearMix = data.shearMix; - offsetX = data.offsetX; - offsetY = data.offsetY; - offsetScaleX = data.offsetScaleX; - offsetScaleY = data.offsetScaleY; - offsetShearY = data.offsetShearY; if (skeleton != null) { bone = skeleton.findBone(data.bone.name); @@ -33,17 +27,12 @@ public class TransformConstraint implements Updatable { /** Copy constructor. */ public TransformConstraint (TransformConstraint constraint, Skeleton skeleton) { data = constraint.data; - bone = skeleton.bones.get(constraint.bone.skeleton.bones.indexOf(constraint.bone, true)); - target = skeleton.bones.get(constraint.target.skeleton.bones.indexOf(constraint.target, true)); - translateMix = constraint.translateMix; + bone = skeleton.bones.get(constraint.bone.data.index); + target = skeleton.bones.get(constraint.target.data.index); rotateMix = constraint.rotateMix; + translateMix = constraint.translateMix; scaleMix = constraint.scaleMix; shearMix = constraint.shearMix; - offsetX = constraint.offsetX; - offsetY = constraint.offsetY; - offsetScaleX = constraint.offsetScaleX; - offsetScaleY = constraint.offsetScaleY; - offsetShearY = constraint.offsetShearY; } public void apply () { @@ -56,7 +45,7 @@ public class TransformConstraint implements Updatable { if (rotateMix > 0) { float a = bone.a, b = bone.b, c = bone.c, d = bone.d; - float r = atan2(target.c, target.a) - atan2(c, a) + offsetRotation * degRad; + float r = atan2(target.c, target.a) - atan2(c, a) + data.offsetRotation * degRad; if (r > PI) r -= PI2; else if (r < -PI) r += PI2; @@ -71,12 +60,12 @@ public class TransformConstraint implements Updatable { if (scaleMix > 0) { float bs = (float)Math.sqrt(bone.a * bone.a + bone.c * bone.c); float ts = (float)Math.sqrt(target.a * target.a + target.c * target.c); - float s = bs > 0.00001f ? (bs + (ts - bs + offsetScaleX) * scaleMix) / bs : 0; + float s = bs > 0.00001f ? (bs + (ts - bs + data.offsetScaleX) * scaleMix) / bs : 0; bone.a *= s; bone.c *= s; bs = (float)Math.sqrt(bone.b * bone.b + bone.d * bone.d); ts = (float)Math.sqrt(target.b * target.b + target.d * target.d); - s = bs > 0.00001f ? (bs + (ts - bs + offsetScaleY) * scaleMix) / bs : 0; + s = bs > 0.00001f ? (bs + (ts - bs + data.offsetScaleY) * scaleMix) / bs : 0; bone.b *= s; bone.d *= s; } @@ -88,7 +77,7 @@ public class TransformConstraint implements Updatable { if (r > PI) r -= PI2; else if (r < -PI) r += PI2; - r = by + (r + offsetShearY * degRad) * shearMix; + r = by + (r + data.offsetShearY * degRad) * shearMix; float s = (float)Math.sqrt(b * b + d * d); bone.b = cos(r) * s; bone.d = sin(r) * s; @@ -97,7 +86,7 @@ public class TransformConstraint implements Updatable { float translateMix = this.translateMix; if (translateMix > 0) { Vector2 temp = this.temp; - target.localToWorld(temp.set(offsetX, offsetY)); + target.localToWorld(temp.set(data.offsetX, data.offsetY)); bone.worldX += (temp.x - bone.worldX) * translateMix; bone.worldY += (temp.y - bone.worldY) * translateMix; } @@ -151,54 +140,6 @@ public class TransformConstraint implements Updatable { this.shearMix = shearMix; } - public float getOffsetRotation () { - return offsetRotation; - } - - public void setOffsetRotation (float offsetRotation) { - this.offsetRotation = offsetRotation; - } - - public float getOffsetX () { - return offsetX; - } - - public void setOffsetX (float offsetX) { - this.offsetX = offsetX; - } - - public float getOffsetY () { - return offsetY; - } - - public void setOffsetY (float offsetY) { - this.offsetY = offsetY; - } - - public float getOffsetScaleX () { - return offsetScaleX; - } - - public void setOffsetScaleX (float offsetScaleX) { - this.offsetScaleX = offsetScaleX; - } - - public float getOffsetScaleY () { - return offsetScaleY; - } - - public void setOffsetScaleY (float offsetScaleY) { - this.offsetScaleY = offsetScaleY; - } - - public float getOffsetShearY () { - return offsetShearY; - } - - public void setOffsetShearY (float offsetShearY) { - this.offsetShearY = offsetShearY; - } - public TransformConstraintData getData () { return data; } diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/AtlasAttachmentLoader.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/AtlasAttachmentLoader.java index 496452311..a667c9b3d 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/AtlasAttachmentLoader.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/AtlasAttachmentLoader.java @@ -46,8 +46,7 @@ public class AtlasAttachmentLoader implements AttachmentLoader { public RegionAttachment newRegionAttachment (Skin skin, String name, String path) { AtlasRegion region = atlas.findRegion(path); - if (region == null) - throw new RuntimeException("Region not found in atlas: " + path + " (region attachment: " + name + ")"); + if (region == null) throw new RuntimeException("Region not found in atlas: " + path + " (region attachment: " + name + ")"); RegionAttachment attachment = new RegionAttachment(name); attachment.setRegion(region); return attachment; @@ -61,16 +60,11 @@ public class AtlasAttachmentLoader implements AttachmentLoader { return attachment; } - public WeightedMeshAttachment newWeightedMeshAttachment (Skin skin, String name, String path) { - AtlasRegion region = atlas.findRegion(path); - if (region == null) - throw new RuntimeException("Region not found in atlas: " + path + " (weighted mesh attachment: " + name + ")"); - WeightedMeshAttachment attachment = new WeightedMeshAttachment(name); - attachment.setRegion(region); - return attachment; - } - public BoundingBoxAttachment newBoundingBoxAttachment (Skin skin, String name) { return new BoundingBoxAttachment(name); } + + public PathAttachment newPathAttachment (Skin skin, String name) { + return new PathAttachment(name); + } } diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/AttachmentLoader.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/AttachmentLoader.java index 1b7c8f0b8..6081e7d37 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/AttachmentLoader.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/AttachmentLoader.java @@ -41,8 +41,8 @@ public interface AttachmentLoader { public MeshAttachment newMeshAttachment (Skin skin, String name, String path); /** @return May be null to not load any attachment. */ - public WeightedMeshAttachment newWeightedMeshAttachment (Skin skin, String name, String path); + public BoundingBoxAttachment newBoundingBoxAttachment (Skin skin, String name); /** @return May be null to not load any attachment. */ - public BoundingBoxAttachment newBoundingBoxAttachment (Skin skin, String name); + public PathAttachment newPathAttachment (Skin skin, String name); } diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/AttachmentType.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/AttachmentType.java index bf8f446e6..6e8e5f303 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/AttachmentType.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/AttachmentType.java @@ -32,7 +32,7 @@ package com.esotericsoftware.spine.attachments; public enum AttachmentType { - region, boundingbox, mesh, weightedmesh, linkedmesh, weightedlinkedmesh; + region, boundingbox, mesh, linkedmesh, path; static public AttachmentType[] values = values(); } diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/BoundingBoxAttachment.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/BoundingBoxAttachment.java index 4c11f1069..eb7397a48 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/BoundingBoxAttachment.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/BoundingBoxAttachment.java @@ -31,37 +31,22 @@ package com.esotericsoftware.spine.attachments; -import com.esotericsoftware.spine.Bone; -import com.esotericsoftware.spine.Skeleton; +import com.badlogic.gdx.graphics.Color; +import com.esotericsoftware.spine.Slot; -public class BoundingBoxAttachment extends Attachment { - private float[] vertices; +public class BoundingBoxAttachment extends VertexAttachment { + // Nonessential. + final Color color = new Color(0.38f, 0.94f, 0, 1); public BoundingBoxAttachment (String name) { super(name); } - public void computeWorldVertices (Bone bone, float[] worldVertices) { - Skeleton skeleton = bone.getSkeleton(); - float x = skeleton.getX() + bone.getWorldX(), y = skeleton.getY() + bone.getWorldY(); - float m00 = bone.getA(); - float m01 = bone.getB(); - float m10 = bone.getC(); - float m11 = bone.getD(); - float[] vertices = this.vertices; - for (int i = 0, n = vertices.length; i < n; i += 2) { - float px = vertices[i]; - float py = vertices[i + 1]; - worldVertices[i] = px * m00 + py * m01 + x; - worldVertices[i + 1] = px * m10 + py * m11 + y; - } + public void computeWorldVertices (Slot slot, float[] worldVertices) { + super.computeWorldVertices(slot, worldVertices); } - public float[] getVertices () { - return vertices; - } - - public void setVertices (float[] vertices) { - this.vertices = vertices; + public Color getColor () { + return color; } } diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/FfdAttachment.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/FfdAttachment.java deleted file mode 100644 index 88e045dbb..000000000 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/FfdAttachment.java +++ /dev/null @@ -1,6 +0,0 @@ - -package com.esotericsoftware.spine.attachments; - -public interface FfdAttachment { - public boolean applyFFD (Attachment sourceAttachment); -} diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/MeshAttachment.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/MeshAttachment.java index b145d407a..b9fdefa6b 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/MeshAttachment.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/MeshAttachment.java @@ -41,16 +41,15 @@ import com.esotericsoftware.spine.Skeleton; import com.esotericsoftware.spine.Slot; /** Attachment that displays a texture region. */ -public class MeshAttachment extends Attachment implements FfdAttachment { +public class MeshAttachment extends VertexAttachment { private TextureRegion region; private String path; - private float[] vertices, regionUVs; + private float[] regionUVs, worldVertices; private short[] triangles; - private float[] worldVertices; private final Color color = new Color(1, 1, 1, 1); private int hullLength; private MeshAttachment parentMesh; - private boolean inheritFFD; + private boolean inheritDeform; // Nonessential. private short[] edges; @@ -71,7 +70,8 @@ public class MeshAttachment extends Attachment implements FfdAttachment { } public void updateUVs () { - int verticesLength = vertices.length; + float[] regionUVs = this.regionUVs; + int verticesLength = regionUVs.length; int worldVerticesLength = verticesLength / 2 * 5; if (worldVertices == null || worldVertices.length != worldVerticesLength) worldVertices = new float[worldVerticesLength]; @@ -85,7 +85,6 @@ public class MeshAttachment extends Attachment implements FfdAttachment { width = region.getU2() - u; height = region.getV2() - v; } - float[] regionUVs = this.regionUVs; if (region instanceof AtlasRegion && ((AtlasRegion)region).rotate) { for (int i = 0, w = 3; i < verticesLength; i += 2, w += 5) { worldVertices[w] = u + regionUVs[i + 1] * width; @@ -102,54 +101,81 @@ public class MeshAttachment extends Attachment implements FfdAttachment { /** @return The updated world vertices. */ public float[] updateWorldVertices (Slot slot, boolean premultipliedAlpha) { Skeleton skeleton = slot.getSkeleton(); - Color skeletonColor = skeleton.getColor(); - Color slotColor = slot.getColor(); - Color meshColor = color; - float a = skeletonColor.a * slotColor.a * meshColor.a * 255; - float multiplier = premultipliedAlpha ? a : 255; + Color skeletonColor = skeleton.getColor(), slotColor = slot.getColor(), meshColor = color; + float alpha = skeletonColor.a * slotColor.a * meshColor.a * 255; + float multiplier = premultipliedAlpha ? alpha : 255; float color = NumberUtils.intToFloatColor( // - ((int)a << 24) // + ((int)alpha << 24) // | ((int)(skeletonColor.b * slotColor.b * meshColor.b * multiplier) << 16) // | ((int)(skeletonColor.g * slotColor.g * meshColor.g * multiplier) << 8) // | (int)(skeletonColor.r * slotColor.r * meshColor.r * multiplier)); - float[] worldVertices = this.worldVertices; - FloatArray slotVertices = slot.getAttachmentVertices(); - float[] vertices = this.vertices; - if (slotVertices.size == vertices.length) vertices = slotVertices.items; - Bone bone = slot.getBone(); - float x = skeleton.getX() + bone.getWorldX(), y = skeleton.getY() + bone.getWorldY(); - float m00 = bone.getA(), m01 = bone.getB(), m10 = bone.getC(), m11 = bone.getD(); - for (int v = 0, w = 0, n = worldVertices.length; w < n; v += 2, w += 5) { - float vx = vertices[v]; - float vy = vertices[v + 1]; - worldVertices[w] = vx * m00 + vy * m01 + x; - worldVertices[w + 1] = vx * m10 + vy * m11 + y; - worldVertices[w + 2] = color; + float x = skeleton.getX(), y = skeleton.getY(); + FloatArray deformArray = slot.getAttachmentVertices(); + float[] vertices = this.vertices, worldVertices = this.worldVertices; + int[] bones = this.bones; + if (bones == null) { + int verticesLength = vertices.length; + if (deformArray.size > 0) vertices = deformArray.items; + Bone bone = slot.getBone(); + x += bone.getWorldX(); + y += bone.getWorldY(); + float a = bone.getA(), b = bone.getB(), c = bone.getC(), d = bone.getD(); + for (int v = 0, w = 0; v < verticesLength; v += 2, w += 5) { + float vx = vertices[v], vy = vertices[v + 1]; + worldVertices[w] = vx * a + vy * b + x; + worldVertices[w + 1] = vx * c + vy * d + y; + worldVertices[w + 2] = color; + } + return worldVertices; + } + Object[] skeletonBones = skeleton.getBones().items; + if (deformArray.size == 0) { + for (int w = 0, v = 0, b = 0, n = bones.length; v < n; w += 5) { + float wx = x, wy = y; + int nn = bones[v++] + v; + for (; v < nn; v++, b += 3) { + Bone bone = (Bone)skeletonBones[bones[v]]; + float vx = vertices[b], vy = vertices[b + 1], weight = vertices[b + 2]; + wx += (vx * bone.getA() + vy * bone.getB() + bone.getWorldX()) * weight; + wy += (vx * bone.getC() + vy * bone.getD() + bone.getWorldY()) * weight; + } + worldVertices[w] = wx; + worldVertices[w + 1] = wy; + worldVertices[w + 2] = color; + } + } else { + float[] deform = deformArray.items; + for (int w = 0, v = 0, b = 0, f = 0, n = bones.length; v < n; w += 5) { + float wx = x, wy = y; + int nn = bones[v++] + v; + for (; v < nn; v++, b += 3, f += 2) { + Bone bone = (Bone)skeletonBones[bones[v]]; + float vx = vertices[b] + deform[f], vy = vertices[b + 1] + deform[f + 1], weight = vertices[b + 2]; + wx += (vx * bone.getA() + vy * bone.getB() + bone.getWorldX()) * weight; + wy += (vx * bone.getC() + vy * bone.getD() + bone.getWorldY()) * weight; + } + worldVertices[w] = wx; + worldVertices[w + 1] = wy; + worldVertices[w + 2] = color; + } } return worldVertices; } - public boolean applyFFD (Attachment sourceAttachment) { - return this == sourceAttachment || (inheritFFD && parentMesh == sourceAttachment); + public boolean applyDeform (VertexAttachment sourceAttachment) { + return this == sourceAttachment || (inheritDeform && parentMesh == sourceAttachment); } public float[] getWorldVertices () { return worldVertices; } - public float[] getVertices () { - return vertices; - } - - public void setVertices (float[] vertices) { - this.vertices = vertices; - } - public short[] getTriangles () { return triangles; } + /** Vertex number triplets which describe the mesh's triangulation. */ public void setTriangles (short[] triangles) { this.triangles = triangles; } @@ -158,6 +184,7 @@ public class MeshAttachment extends Attachment implements FfdAttachment { return regionUVs; } + /** Sets the texture coordinates for the region. The values are u,v pairs for each vertex. */ public void setRegionUVs (float[] regionUVs) { this.regionUVs = regionUVs; } @@ -182,14 +209,14 @@ public class MeshAttachment extends Attachment implements FfdAttachment { this.hullLength = hullLength; } - public short[] getEdges () { - return edges; - } - public void setEdges (short[] edges) { this.edges = edges; } + public short[] getEdges () { + return edges; + } + public float getWidth () { return width; } @@ -215,6 +242,7 @@ public class MeshAttachment extends Attachment implements FfdAttachment { public void setParentMesh (MeshAttachment parentMesh) { this.parentMesh = parentMesh; if (parentMesh != null) { + bones = parentMesh.bones; vertices = parentMesh.vertices; regionUVs = parentMesh.regionUVs; triangles = parentMesh.triangles; @@ -225,11 +253,11 @@ public class MeshAttachment extends Attachment implements FfdAttachment { } } - public boolean getInheritFFD () { - return inheritFFD; + public boolean getInheritDeform () { + return inheritDeform; } - public void setInheritFFD (boolean inheritFFD) { - this.inheritFFD = inheritFFD; + public void setInheritDeform (boolean inheritDeform) { + this.inheritDeform = inheritDeform; } } 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 new file mode 100644 index 000000000..3b6dba744 --- /dev/null +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/PathAttachment.java @@ -0,0 +1,165 @@ +/****************************************************************************** + * Spine Runtimes Software License + * Version 2.3 + * + * Copyright (c) 2013-2015, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable and + * non-transferable license to use, install, execute and perform the Spine + * Runtimes Software (the "Software") and derivative works solely for personal + * or internal use. Without the written permission of Esoteric Software (see + * Section 2 of the Spine Software License Agreement), 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 SOFTWARE 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.badlogic.gdx.graphics.Color; +import com.badlogic.gdx.math.Vector2; +import com.esotericsoftware.spine.Slot; + +public class PathAttachment extends VertexAttachment { + // Nonessential. + final Color color = new Color(1, 0.5f, 0, 1); + final Vector2 temp = new Vector2(); + float[] worldVertices, lengths; + int totalLength; + boolean closed; + + public PathAttachment (String name) { + super(name); + } + + public void computeWorldVertices (Slot slot, float[] worldVertices) { + super.computeWorldVertices(slot, worldVertices); + } + + public Vector2 computeWorldPosition (Slot slot, float position) { + float[] worldVertices = this.worldVertices; + super.computeWorldVertices(slot, worldVertices); + + int curve = 0; + float t = 0; + if (closed) { + // BOZO - closed boolean used to turn off fancy calculations for now. + int curves = (worldVerticesLength >> 2) - 1; + curve = position < 1 ? (int)(curves * position) : curves - 1; + t = (position - curve / (float)curves) * curves; + } else { + // Compute lengths of all curves. + totalLength = 0; + float[] lengths = this.lengths; + float x1 = worldVertices[0], y1 = worldVertices[1]; + float cx1 = x1 + (x1 - worldVertices[2]), cy1 = y1 + (y1 - worldVertices[3]); + for (int i = 0, w = 4, n = worldVerticesLength; w < n; i += 6, w += 4) { + float x2 = worldVertices[w], y2 = worldVertices[w + 1]; + float cx2 = worldVertices[w + 2], cy2 = worldVertices[w + 3]; + addLengths(i, x1, y1, cx1, cy1, cx2, cy2, x2, y2); + x1 = x2; + y1 = y2; + cx1 = x2 + (x2 - cx2); + cy1 = y2 + (y2 - cy2); + } + + // Determine curve containing position. + float target = totalLength * position, distance = 0; + for (int i = 5;; i += 6) { + float curveLength = lengths[i]; + if (distance + curveLength > target) { + curve = i / 6; + t = (target - distance) / curveLength; + break; + } + distance += curveLength; + } + + // Adjust t for constant speed using lengths of curves as weights. + for (int i = curve * 6, n = i + 5; i < n; i++) { + float bezierPercent = lengths[i]; + if (t > bezierPercent) { + float linearPercent = 0.75f - 0.25f * (i - curve * 6 - 1); + float bezierPercentNext = lengths[i - 1]; + t = linearPercent + 0.25f * ((t - bezierPercent) / (bezierPercentNext - bezierPercent)); + break; + } + } + } + + // Calculate bezier point. + int i = curve << 2; + float x1 = worldVertices[i], y1 = worldVertices[i + 1]; + float cx1 = x1 + (x1 - worldVertices[i + 2]), cy1 = y1 + (y1 - worldVertices[i + 3]); + float x2 = worldVertices[i + 4], y2 = worldVertices[i + 5]; + float cx2 = worldVertices[i + 6], cy2 = worldVertices[i + 7]; + float tt = t * t, ttt = tt * t, t3 = t * 3; + float x = (x1 + t * (-x1 * 3 + t * (3 * x1 - x1 * t))) + t * (3 * cx1 + t * (-6 * cx1 + cx1 * t3)) + + tt * (cx2 * 3 - cx2 * t3) + x2 * ttt; + float y = (y1 + t * (-y1 * 3 + t * (3 * y1 - y1 * t))) + t * (3 * cy1 + t * (-6 * cy1 + cy1 * t3)) + + tt * (cy2 * 3 - cy2 * t3) + y2 * ttt; + return temp.set(x, y); + } + + private void addLengths (int index, float x1, float y1, float cx1, float cy1, float cx2, float cy2, float x2, float y2) { + float tmp1x = x1 - cx1 * 2 + cx2, tmp1y = y1 - cy1 * 2 + cy2; + float tmp2x = (cx1 - cx2) * 3 - x1 + x2, tmp2y = (cy1 - cy2) * 3 - y1 + y2; + float dfx = (cx1 - x1) * 0.75f + tmp1x * 0.1875f + tmp2x * 0.015625f; + float dfy = (cy1 - y1) * 0.75f + tmp1y * 0.1875f + tmp2y * 0.015625f; + float ddfx = tmp1x * 0.375f + tmp2x * 0.09375f, ddfy = tmp1y * 0.375f + tmp2y * 0.09375f; + float dddfx = tmp2x * 0.09375f, dddfy = tmp2y * 0.09375f; + float length0 = (float)Math.sqrt(dfx * dfx + dfy * dfy); + dfx += ddfx; + dfy += ddfy; + ddfx += dddfx; + ddfy += dddfy; + float length1 = length0 + (float)Math.sqrt(dfx * dfx + dfy * dfy); + dfx += ddfx; + dfy += ddfy; + float length2 = length1 + (float)Math.sqrt(dfx * dfx + dfy * dfy); + dfx += ddfx + dddfx; + dfy += ddfy + dddfy; + float total = length2 + (float)Math.sqrt(dfx * dfx + dfy * dfy); + totalLength += total; + float[] lengths = this.lengths; + lengths[index] = 1; + lengths[index + 1] = length2 / total; + lengths[index + 2] = length1 / total; + lengths[index + 3] = length0 / total; + lengths[index + 4] = 0; + lengths[index + 5] = total; + } + + public Color getColor () { + return color; + } + + public boolean getClosed () { + return closed; + } + + public void setClosed (boolean closed) { + this.closed = closed; + } + + public void setWorldVerticesLength (int worldVerticesLength) { + super.setWorldVerticesLength(worldVerticesLength); + worldVertices = new float[worldVerticesLength]; + lengths = new float[(worldVerticesLength >> 2) * 6]; + } +} diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/RegionAttachment.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/RegionAttachment.java index c645f58ce..cc5a36242 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/RegionAttachment.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/RegionAttachment.java @@ -152,10 +152,10 @@ public class RegionAttachment extends Attachment { Color skeletonColor = skeleton.getColor(); Color slotColor = slot.getColor(); Color regionColor = color; - float a = skeletonColor.a * slotColor.a * regionColor.a * 255; - float multiplier = premultipliedAlpha ? a : 255; + float alpha = skeletonColor.a * slotColor.a * regionColor.a * 255; + float multiplier = premultipliedAlpha ? alpha : 255; float color = NumberUtils.intToFloatColor( // - ((int)a << 24) // + ((int)alpha << 24) // | ((int)(skeletonColor.b * slotColor.b * regionColor.b * multiplier) << 16) // | ((int)(skeletonColor.g * slotColor.g * regionColor.g * multiplier) << 8) // | (int)(skeletonColor.r * slotColor.r * regionColor.r * multiplier)); @@ -164,31 +164,31 @@ public class RegionAttachment extends Attachment { float[] offset = this.offset; Bone bone = slot.getBone(); float x = skeleton.getX() + bone.getWorldX(), y = skeleton.getY() + bone.getWorldY(); - float m00 = bone.getA(), m01 = bone.getB(), m10 = bone.getC(), m11 = bone.getD(); + float a = bone.getA(), b = bone.getB(), c = bone.getC(), d = bone.getD(); float offsetX, offsetY; offsetX = offset[BRX]; offsetY = offset[BRY]; - vertices[X1] = offsetX * m00 + offsetY * m01 + x; // br - vertices[Y1] = offsetX * m10 + offsetY * m11 + y; + vertices[X1] = offsetX * a + offsetY * b + x; // br + vertices[Y1] = offsetX * c + offsetY * d + y; vertices[C1] = color; offsetX = offset[BLX]; offsetY = offset[BLY]; - vertices[X2] = offsetX * m00 + offsetY * m01 + x; // bl - vertices[Y2] = offsetX * m10 + offsetY * m11 + y; + vertices[X2] = offsetX * a + offsetY * b + x; // bl + vertices[Y2] = offsetX * c + offsetY * d + y; vertices[C2] = color; offsetX = offset[ULX]; offsetY = offset[ULY]; - vertices[X3] = offsetX * m00 + offsetY * m01 + x; // ul - vertices[Y3] = offsetX * m10 + offsetY * m11 + y; + vertices[X3] = offsetX * a + offsetY * b + x; // ul + vertices[Y3] = offsetX * c + offsetY * d + y; vertices[C3] = color; offsetX = offset[URX]; offsetY = offset[URY]; - vertices[X4] = offsetX * m00 + offsetY * m01 + x; // ur - vertices[Y4] = offsetX * m10 + offsetY * m11 + y; + vertices[X4] = offsetX * a + offsetY * b + x; // ur + vertices[Y4] = offsetX * c + offsetY * d + y; vertices[C4] = color; return vertices; } diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/VertexAttachment.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/VertexAttachment.java new file mode 100644 index 000000000..913d6faf6 --- /dev/null +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/VertexAttachment.java @@ -0,0 +1,133 @@ +/****************************************************************************** + * Spine Runtimes Software License + * Version 2.3 + * + * Copyright (c) 2013-2015, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable and + * non-transferable license to use, install, execute and perform the Spine + * Runtimes Software (the "Software") and derivative works solely for personal + * or internal use. Without the written permission of Esoteric Software (see + * Section 2 of the Spine Software License Agreement), 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 SOFTWARE 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.badlogic.gdx.utils.FloatArray; +import com.esotericsoftware.spine.Bone; +import com.esotericsoftware.spine.Skeleton; +import com.esotericsoftware.spine.Slot; + +/** An attachment with vertices that are transformed by one or more bones and can be deformed by a slot's vertices. */ +public class VertexAttachment extends Attachment { + int[] bones; + float[] vertices; + int worldVerticesLength; + + public VertexAttachment (String name) { + super(name); + } + + protected void computeWorldVertices (Slot slot, float[] worldVertices) { + Skeleton skeleton = slot.getSkeleton(); + float x = skeleton.getX(), y = skeleton.getY(); + FloatArray deformArray = slot.getAttachmentVertices(); + float[] vertices = this.vertices; + int[] bones = this.bones; + if (bones == null) { + int verticesLength = vertices.length; + if (deformArray.size > 0) vertices = deformArray.items; + Bone bone = slot.getBone(); + x += bone.getWorldX(); + y += bone.getWorldY(); + float a = bone.getA(), b = bone.getB(), c = bone.getC(), d = bone.getD(); + for (int v = 0; v < verticesLength; v += 2) { + float vx = vertices[v], vy = vertices[v + 1]; + worldVertices[v] = vx * a + vy * b + x; + worldVertices[v + 1] = vx * c + vy * d + y; + } + return; + } + Object[] skeletonBones = skeleton.getBones().items; + if (deformArray.size == 0) { + for (int w = 0, v = 0, b = 0, n = bones.length; v < n; w += 2) { + float wx = x, wy = y; + int nn = bones[v++] + v; + for (; v < nn; v++, b += 3) { + Bone bone = (Bone)skeletonBones[bones[v]]; + float vx = vertices[b], vy = vertices[b + 1], weight = vertices[b + 2]; + wx += (vx * bone.getA() + vy * bone.getB() + bone.getWorldX()) * weight; + wy += (vx * bone.getC() + vy * bone.getD() + bone.getWorldY()) * weight; + } + worldVertices[w] = wx; + worldVertices[w + 1] = wy; + } + } else { + float[] deform = deformArray.items; + for (int w = 0, v = 0, b = 0, f = 0, n = bones.length; v < n; w += 2) { + float wx = x, wy = y; + int nn = bones[v++] + v; + for (; v < nn; v++, b += 3, f += 2) { + Bone bone = (Bone)skeletonBones[bones[v]]; + float vx = vertices[b] + deform[f], vy = vertices[b + 1] + deform[f + 1], weight = vertices[b + 2]; + wx += (vx * bone.getA() + vy * bone.getB() + bone.getWorldX()) * weight; + wy += (vx * bone.getC() + vy * bone.getD() + bone.getWorldY()) * weight; + } + worldVertices[w] = wx; + worldVertices[w + 1] = wy; + } + } + } + + /** Returns true if a deform originally applied to the specified attachment should be applied to this attachment. */ + public boolean applyDeform (VertexAttachment sourceAttachment) { + return true; + } + + /** @return May be null if this attachment has no weights. */ + public int[] getBones () { + return bones; + } + + /** For each vertex, the number of bones affecting the vertex followed by that many bone indices. Ie: count, boneIndex, ... + * @param bones May be null if this attachment has no weights. */ + public void setBones (int[] bones) { + this.bones = bones; + } + + public float[] getVertices () { + return vertices; + } + + /** Sets the vertex position in the bone's coordinate system. For a non-weighted attachment, the values are x,y entries for + * each vertex. For a weighted attachment, the values are x,y,weight entries for each bone affecting each vertex. */ + public void setVertices (float[] vertices) { + this.vertices = vertices; + } + + public int getWorldVerticesLength () { + return worldVerticesLength; + } + + public void setWorldVerticesLength (int worldVerticesLength) { + this.worldVerticesLength = worldVerticesLength; + } +} diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/WeightedMeshAttachment.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/WeightedMeshAttachment.java deleted file mode 100644 index 032f4789f..000000000 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/WeightedMeshAttachment.java +++ /dev/null @@ -1,274 +0,0 @@ -/****************************************************************************** - * Spine Runtimes Software License - * Version 2.3 - * - * Copyright (c) 2013-2015, Esoteric Software - * All rights reserved. - * - * You are granted a perpetual, non-exclusive, non-sublicensable and - * non-transferable license to use, install, execute and perform the Spine - * Runtimes Software (the "Software") and derivative works solely for personal - * or internal use. Without the written permission of Esoteric Software (see - * Section 2 of the Spine Software License Agreement), 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 SOFTWARE 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.TextureAtlas.AtlasRegion; -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 WeightedMeshAttachment extends Attachment implements FfdAttachment { - private TextureRegion region; - private String path; - private int[] bones; - private float[] weights, regionUVs; - private short[] triangles; - private float[] worldVertices; - private final Color color = new Color(1, 1, 1, 1); - private int hullLength; - private WeightedMeshAttachment parentMesh; - private boolean inheritFFD; - - // Nonessential. - private short[] edges; - private float width, height; - - public WeightedMeshAttachment (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 updateUVs () { - float[] regionUVs = this.regionUVs; - int verticesLength = regionUVs.length; - int worldVerticesLength = verticesLength / 2 * 5; - if (worldVertices == null || worldVertices.length != worldVerticesLength) worldVertices = new float[worldVerticesLength]; - - float u, v, width, height; - if (region == null) { - u = v = 0; - width = height = 1; - } else { - u = region.getU(); - v = region.getV(); - width = region.getU2() - u; - height = region.getV2() - v; - } - if (region instanceof AtlasRegion && ((AtlasRegion)region).rotate) { - for (int i = 0, w = 3; i < verticesLength; i += 2, w += 5) { - worldVertices[w] = u + regionUVs[i + 1] * width; - worldVertices[w + 1] = v + height - regionUVs[i] * height; - } - } else { - for (int i = 0, w = 3; i < verticesLength; i += 2, w += 5) { - worldVertices[w] = u + regionUVs[i] * width; - worldVertices[w + 1] = v + regionUVs[i + 1] * height; - } - } - } - - /** @return The updated world vertices. */ - public float[] 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.getA() + vy * bone.getB() + bone.getWorldX()) * weight; - wy += (vx * bone.getC() + vy * bone.getD() + 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.getA() + vy * bone.getB() + bone.getWorldX()) * weight; - wy += (vx * bone.getC() + vy * bone.getD() + bone.getWorldY()) * weight; - } - worldVertices[w] = wx + x; - worldVertices[w + 1] = wy + y; - worldVertices[w + 2] = color; - } - } - return worldVertices; - } - - public boolean applyFFD (Attachment sourceAttachment) { - return this == sourceAttachment || (inheritFFD && parentMesh == sourceAttachment); - } - - public float[] getWorldVertices () { - return worldVertices; - } - - public int[] getBones () { - return bones; - } - - /** For each vertex, the number of bones affecting the vertex followed by that many bone indices. Ie: count, boneIndex, ... */ - public void setBones (int[] bones) { - this.bones = bones; - } - - public float[] getWeights () { - return 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, ... */ - public void setWeights (float[] weights) { - this.weights = weights; - } - - public short[] getTriangles () { - return triangles; - } - - /** Vertex number triplets which describe the mesh's triangulation. */ - public void setTriangles (short[] triangles) { - this.triangles = triangles; - } - - public float[] getRegionUVs () { - return regionUVs; - } - - /** For each vertex, a texure coordinate pair. Ie: u, v, ... */ - public void setRegionUVs (float[] regionUVs) { - this.regionUVs = regionUVs; - } - - 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 (short[] edges) { - this.edges = edges; - } - - public short[] 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; - } - - /** Returns the source mesh if this is a linked mesh, else returns null. */ - public WeightedMeshAttachment getParentMesh () { - return parentMesh; - } - - /** @param parentMesh May be null. */ - public void setParentMesh (WeightedMeshAttachment parentMesh) { - this.parentMesh = parentMesh; - if (parentMesh != null) { - bones = parentMesh.bones; - weights = parentMesh.weights; - regionUVs = parentMesh.regionUVs; - triangles = parentMesh.triangles; - hullLength = parentMesh.hullLength; - edges = parentMesh.edges; - width = parentMesh.width; - height = parentMesh.height; - } - } - - public boolean getInheritFFD () { - return inheritFFD; - } - - public void setInheritFFD (boolean inheritFFD) { - this.inheritFFD = inheritFFD; - } -} diff --git a/spine-libgdx/spine-skeletonviewer/src/com/esotericsoftware/spine/SkeletonViewer.java b/spine-libgdx/spine-skeletonviewer/src/com/esotericsoftware/spine/SkeletonViewer.java index 96f4e40a3..bb3c62432 100644 --- a/spine-libgdx/spine-skeletonviewer/src/com/esotericsoftware/spine/SkeletonViewer.java +++ b/spine-libgdx/spine-skeletonviewer/src/com/esotericsoftware/spine/SkeletonViewer.java @@ -246,6 +246,7 @@ public class SkeletonViewer extends ApplicationAdapter { debugRenderer.setBoundingBoxes(ui.debugBoundingBoxesCheckbox.isChecked()); debugRenderer.setMeshHull(ui.debugMeshHullCheckbox.isChecked()); debugRenderer.setMeshTriangles(ui.debugMeshTrianglesCheckbox.isChecked()); + debugRenderer.setPaths(ui.debugPathsCheckbox.isChecked()); debugRenderer.draw(skeleton); } @@ -285,19 +286,20 @@ public class SkeletonViewer extends ApplicationAdapter { TextButton openButton = new TextButton("Open", skin); List animationList = new List(skin); List skinList = new List(skin); - CheckBox loopCheckbox = new CheckBox(" Loop", skin); - CheckBox premultipliedCheckbox = new CheckBox(" Premultiplied", skin); + CheckBox loopCheckbox = new CheckBox("Loop", skin); + CheckBox premultipliedCheckbox = new CheckBox("Premultiplied", skin); Slider mixSlider = new Slider(0f, 2, 0.01f, false, skin); Label mixLabel = new Label("0.3", skin); Slider speedSlider = new Slider(0.1f, 3, 0.01f, false, skin); Label speedLabel = new Label("1.0", skin); - CheckBox flipXCheckbox = new CheckBox(" X", skin); - CheckBox flipYCheckbox = new CheckBox(" Y", skin); - 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); + CheckBox flipXCheckbox = new CheckBox("X", skin); + CheckBox flipYCheckbox = new CheckBox("Y", skin); + 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("Triangles", skin); + CheckBox debugPathsCheckbox = new CheckBox("Paths", 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"); @@ -330,6 +332,7 @@ public class SkeletonViewer extends ApplicationAdapter { window.setX(-3); window.setY(-2); + window.getTitleLabel().setColor(new Color(0.76f, 1, 1, 1)); window.getTitleTable().add(openButton).space(3); window.getTitleTable().add(minimizeButton).width(20); @@ -356,12 +359,12 @@ public class SkeletonViewer extends ApplicationAdapter { root.add("Debug:"); root.add(table(debugBonesCheckbox, debugRegionsCheckbox, debugBoundingBoxesCheckbox)).row(); root.add(); - root.add(table(debugMeshHullCheckbox, debugMeshTrianglesCheckbox)).row(); + root.add(table(debugMeshHullCheckbox, debugMeshTrianglesCheckbox, debugPathsCheckbox)).row(); root.add("Alpha:"); root.add(premultipliedCheckbox).row(); root.add("Skin:"); root.add(skinScroll).expand().fill().minHeight(75).row(); - root.add("Setup Pose:"); + root.add("Setup pose:"); root.add(table(bonesSetupPoseButton, slotsSetupPoseButton, setupPoseButton)).row(); root.add("Animation:"); root.add(animationScroll).expand().fill().minHeight(75).row();