mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-03-25 22:23:42 +08:00
Spine v3.3 WIP
This commit is contained in:
parent
d7633706e7
commit
ee29123dfa
@ -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;
|
||||
}
|
||||
});
|
||||
|
||||
@ -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") {};
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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<Event> 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<Event> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<Bone> 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<Bone> getChildren () {
|
||||
return children;
|
||||
}
|
||||
|
||||
public float getX () {
|
||||
return x;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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<Slot> drawOrder;
|
||||
final Array<IkConstraint> ikConstraints;
|
||||
final Array<TransformConstraint> transformConstraints;
|
||||
final Array<PathConstraint> pathConstraints;
|
||||
private final Array<Updatable> 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<Updatable> updateCache = this.updateCache;
|
||||
Array<IkConstraint> ikConstraints = this.ikConstraints;
|
||||
Array<TransformConstraint> transformConstraints = this.transformConstraints;
|
||||
Array<PathConstraint> 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<PathConstraint> 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<Slot> 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<PathConstraint> getPathConstraints () {
|
||||
return pathConstraints;
|
||||
}
|
||||
|
||||
/** @return May be null. */
|
||||
public PathConstraint findPathConstraint (String constraintName) {
|
||||
if (constraintName == null) throw new IllegalArgumentException("constraintName cannot be null.");
|
||||
Array<PathConstraint> 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) {
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -43,6 +43,7 @@ public class SkeletonData {
|
||||
final Array<Animation> animations = new Array();
|
||||
final Array<IkConstraintData> ikConstraints = new Array();
|
||||
final Array<TransformConstraintData> transformConstraints = new Array();
|
||||
final Array<PathConstraintData> pathConstraints = new Array();
|
||||
float width, height;
|
||||
String version, hash, imagesPath;
|
||||
|
||||
@ -188,6 +189,23 @@ public class SkeletonData {
|
||||
return null;
|
||||
}
|
||||
|
||||
// --- Path constraints
|
||||
|
||||
public Array<PathConstraintData> getPathConstraints () {
|
||||
return pathConstraints;
|
||||
}
|
||||
|
||||
/** @return May be null. */
|
||||
public PathConstraintData findPathConstraint (String constraintName) {
|
||||
if (constraintName == null) throw new IllegalArgumentException("constraintName cannot be null.");
|
||||
Array<PathConstraintData> 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. */
|
||||
|
||||
@ -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<Timeline> 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;
|
||||
|
||||
@ -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<PolygonSpriteBatch> {
|
||||
static private final short[] quadTriangles = {0, 1, 2, 2, 3, 0};
|
||||
@ -67,12 +66,6 @@ public class SkeletonMeshRenderer extends SkeletonRenderer<PolygonSpriteBatch> {
|
||||
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;
|
||||
|
||||
@ -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<T extends Batch> {
|
||||
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<T extends Batch> {
|
||||
}
|
||||
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) {
|
||||
|
||||
@ -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<FloatArray> polygons = bounds.getPolygons();
|
||||
Array<BoundingBoxAttachment> 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<Slot> 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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +0,0 @@
|
||||
|
||||
package com.esotericsoftware.spine.attachments;
|
||||
|
||||
public interface FfdAttachment {
|
||||
public boolean applyFFD (Attachment sourceAttachment);
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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];
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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<String> animationList = new List(skin);
|
||||
List<String> 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();
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user