mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-02-04 22:34:53 +08:00
[libgdx] Separate pose and applied for bones, slots, and constraints, sliders WIP.
This commit is contained in:
parent
9424018102
commit
71ef2d5f98
@ -76,14 +76,14 @@ public class BonePlotting {
|
||||
|
||||
SkeletonData skeletonData = json.readSkeletonData(new FileHandle("assets/spineboy/spineboy-ess.json"));
|
||||
Skeleton skeleton = new Skeleton(skeletonData);
|
||||
Bone bone = skeleton.findBone("gun-tip");
|
||||
BoneApplied bone = skeleton.findBone("gun-tip").getApplied();
|
||||
|
||||
// Pose the skeleton at regular intervals throughout each animation.
|
||||
float fps = 1 / 15f;
|
||||
for (Animation animation : skeletonData.getAnimations()) {
|
||||
float time = 0;
|
||||
while (time < animation.getDuration()) {
|
||||
animation.apply(skeleton, time, time, false, null, 1, MixBlend.first, MixDirection.in);
|
||||
animation.apply(skeleton, time, time, false, null, 1, MixBlend.first, MixDirection.in, false);
|
||||
skeleton.update(fps);
|
||||
skeleton.updateWorldTransform(Physics.update);
|
||||
|
||||
|
||||
@ -149,7 +149,7 @@ public class Box2DExample extends ApplicationAdapter {
|
||||
batch.setTransformMatrix(camera.view);
|
||||
batch.begin();
|
||||
|
||||
animation.apply(skeleton, time, time, true, events, 1, MixBlend.first, MixDirection.in);
|
||||
animation.apply(skeleton, time, time, true, events, 1, MixBlend.first, MixDirection.in, false);
|
||||
skeleton.x += 8 * delta;
|
||||
skeleton.update(delta);
|
||||
skeleton.updateWorldTransform(Physics.update);
|
||||
@ -162,9 +162,10 @@ public class Box2DExample extends ApplicationAdapter {
|
||||
if (!(slot.getAttachment() instanceof Box2dAttachment)) continue;
|
||||
Box2dAttachment attachment = (Box2dAttachment)slot.getAttachment();
|
||||
if (attachment.body == null) continue;
|
||||
float x = slot.getBone().getWorldX();
|
||||
float y = slot.getBone().getWorldY();
|
||||
float rotation = slot.getBone().getWorldRotationX();
|
||||
BoneApplied bone = slot.getBone().getApplied();
|
||||
float x = bone.getWorldX();
|
||||
float y = bone.getWorldY();
|
||||
float rotation = bone.getWorldRotationX();
|
||||
attachment.body.setTransform(x, y, rotation * MathUtils.degRad);
|
||||
}
|
||||
|
||||
|
||||
@ -177,7 +177,7 @@ public class EventTimelineTests {
|
||||
|
||||
int beforeCount = firedEvents.size;
|
||||
Array<Event> original = new Array(firedEvents);
|
||||
timeline.apply(skeleton, lastTimeLooped, timeLooped, firedEvents, 1, MixBlend.first, MixDirection.in);
|
||||
timeline.apply(skeleton, lastTimeLooped, timeLooped, firedEvents, 1, MixBlend.first, MixDirection.in, false);
|
||||
|
||||
while (beforeCount < firedEvents.size) {
|
||||
char fired = firedEvents.get(beforeCount).getData().getName().charAt(0);
|
||||
@ -186,7 +186,7 @@ public class EventTimelineTests {
|
||||
} else {
|
||||
if (firedEvents.size > eventsCount) {
|
||||
if (print) System.out.println(lastTimeLooped + "->" + timeLooped + ": " + fired + " == ?");
|
||||
timeline.apply(skeleton, lastTimeLooped, timeLooped, original, 1, MixBlend.first, MixDirection.in);
|
||||
timeline.apply(skeleton, lastTimeLooped, timeLooped, original, 1, MixBlend.first, MixDirection.in, false);
|
||||
fail("Too many events fired.");
|
||||
}
|
||||
}
|
||||
@ -194,7 +194,7 @@ public class EventTimelineTests {
|
||||
System.out.println(lastTimeLooped + "->" + timeLooped + ": " + fired + " == " + events[eventIndex]);
|
||||
}
|
||||
if (fired != events[eventIndex]) {
|
||||
timeline.apply(skeleton, lastTimeLooped, timeLooped, original, 1, MixBlend.first, MixDirection.in);
|
||||
timeline.apply(skeleton, lastTimeLooped, timeLooped, original, 1, MixBlend.first, MixDirection.in, false);
|
||||
fail("Wrong event fired.");
|
||||
}
|
||||
eventIndex++;
|
||||
@ -206,7 +206,7 @@ public class EventTimelineTests {
|
||||
i++;
|
||||
}
|
||||
if (firedEvents.size < eventsCount) {
|
||||
timeline.apply(skeleton, lastTimeLooped, timeLooped, firedEvents, 1, MixBlend.first, MixDirection.in);
|
||||
timeline.apply(skeleton, lastTimeLooped, timeLooped, firedEvents, 1, MixBlend.first, MixDirection.in, false);
|
||||
if (print) System.out.println(firedEvents);
|
||||
fail("Event not fired: " + events[eventIndex] + ", " + frames[eventIndex]);
|
||||
}
|
||||
|
||||
@ -107,7 +107,7 @@ public class IKTest extends ApplicationAdapter {
|
||||
|
||||
Bone crosshair = skeleton.findBone("crosshair"); // Should be cached.
|
||||
boneCoords.set(cameraCoords.x, cameraCoords.y);
|
||||
crosshair.getParent().worldToLocal(boneCoords); // camera space to local bone space
|
||||
crosshair.getParent().getApplied().worldToLocal(boneCoords); // camera space to local bone space
|
||||
crosshair.setPosition(boneCoords.x, boneCoords.y); // override the crosshair position
|
||||
|
||||
// Calculate final world transform with the crosshair bone set to the mouse cursor position. Update physics this time.
|
||||
|
||||
@ -137,7 +137,7 @@ public class NormalMapTest extends ApplicationAdapter {
|
||||
float lastTime = time;
|
||||
float delta = Gdx.graphics.getDeltaTime();
|
||||
time += delta;
|
||||
if (animation != null) animation.apply(skeleton, lastTime, time, true, null, 1, MixBlend.first, MixDirection.in);
|
||||
if (animation != null) animation.apply(skeleton, lastTime, time, true, null, 1, MixBlend.first, MixDirection.in, false);
|
||||
skeleton.update(delta);
|
||||
skeleton.updateWorldTransform(Physics.update);
|
||||
|
||||
|
||||
@ -104,7 +104,7 @@ public class PngExportTest extends ApplicationAdapter {
|
||||
float fps = 1 / 15f, time = 0;
|
||||
int frame = 1;
|
||||
while (time < animation.getDuration()) {
|
||||
animation.apply(skeleton, time, time, false, null, 1, MixBlend.first, MixDirection.in);
|
||||
animation.apply(skeleton, time, time, false, null, 1, MixBlend.first, MixDirection.in, false);
|
||||
skeleton.update(fps);
|
||||
skeleton.updateWorldTransform(Physics.update);
|
||||
|
||||
|
||||
@ -48,7 +48,7 @@ public class SkeletonAttachmentTest extends ApplicationAdapter {
|
||||
|
||||
Skeleton spineboy, goblin;
|
||||
AnimationState spineboyState, goblinState;
|
||||
Bone attachmentBone;
|
||||
BoneApplied attachmentBone;
|
||||
|
||||
public void create () {
|
||||
camera = new OrthographicCamera();
|
||||
@ -87,7 +87,7 @@ public class SkeletonAttachmentTest extends ApplicationAdapter {
|
||||
skeletonAttachment.setSkeleton(goblin);
|
||||
Slot slot = spineboy.findSlot("front-upper-arm");
|
||||
slot.setAttachment(skeletonAttachment);
|
||||
attachmentBone = slot.getBone();
|
||||
attachmentBone = slot.getBone().getApplied();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -109,23 +109,24 @@ public class TimelineApiTest extends ApplicationAdapter {
|
||||
skeleton.setX(-50);
|
||||
} else if (time > beforeJump + jump) {
|
||||
// just walk after jump
|
||||
walkAnimation.apply(skeleton, time, time, true, events, 1, MixBlend.first, MixDirection.in);
|
||||
walkAnimation.apply(skeleton, time, time, true, events, 1, MixBlend.first, MixDirection.in, false);
|
||||
} else if (time > blendOutStart) {
|
||||
// blend out jump
|
||||
walkAnimation.apply(skeleton, time, time, true, events, 1, MixBlend.first, MixDirection.in);
|
||||
walkAnimation.apply(skeleton, time, time, true, events, 1, MixBlend.first, MixDirection.in, false);
|
||||
jumpAnimation.apply(skeleton, time - beforeJump, time - beforeJump, false, events, 1 - (time - blendOutStart) / blendOut,
|
||||
MixBlend.first, MixDirection.in);
|
||||
MixBlend.first, MixDirection.in, false);
|
||||
} else if (time > beforeJump + blendIn) {
|
||||
// just jump
|
||||
jumpAnimation.apply(skeleton, time - beforeJump, time - beforeJump, false, events, 1, MixBlend.first, MixDirection.in);
|
||||
jumpAnimation.apply(skeleton, time - beforeJump, time - beforeJump, false, events, 1, MixBlend.first, MixDirection.in,
|
||||
false);
|
||||
} else if (time > beforeJump) {
|
||||
// blend in jump
|
||||
walkAnimation.apply(skeleton, time, time, true, events, 1, MixBlend.first, MixDirection.in);
|
||||
walkAnimation.apply(skeleton, time, time, true, events, 1, MixBlend.first, MixDirection.in, false);
|
||||
jumpAnimation.apply(skeleton, time - beforeJump, time - beforeJump, false, events, (time - beforeJump) / blendIn,
|
||||
MixBlend.first, MixDirection.in);
|
||||
MixBlend.first, MixDirection.in, false);
|
||||
} else {
|
||||
// just walk before jump
|
||||
walkAnimation.apply(skeleton, time, time, true, events, 1, MixBlend.first, MixDirection.in);
|
||||
walkAnimation.apply(skeleton, time, time, true, events, 1, MixBlend.first, MixDirection.in, false);
|
||||
}
|
||||
|
||||
skeleton.update(delta);
|
||||
|
||||
@ -96,7 +96,7 @@ public class Animation {
|
||||
|
||||
/** Applies the animation's timelines to the specified skeleton.
|
||||
* <p>
|
||||
* See Timeline {@link Timeline#apply(Skeleton, float, float, Array, float, MixBlend, MixDirection)}.
|
||||
* See Timeline {@link Timeline#apply(Skeleton, float, float, Array, float, MixBlend, MixDirection, boolean)}.
|
||||
* @param skeleton The skeleton the animation is being applied to. This provides access to the bones, slots, and other skeleton
|
||||
* components the timelines may change.
|
||||
* @param lastTime The last time in seconds this animation was applied. Some timelines trigger only at specific times rather
|
||||
@ -115,7 +115,7 @@ public class Animation {
|
||||
* @param direction Indicates whether the timelines are mixing in or out. Used by timelines which perform instant transitions,
|
||||
* such as {@link DrawOrderTimeline} or {@link AttachmentTimeline}. */
|
||||
public void apply (Skeleton skeleton, float lastTime, float time, boolean loop, @Null Array<Event> events, float alpha,
|
||||
MixBlend blend, MixDirection direction) {
|
||||
MixBlend blend, MixDirection direction, boolean appliedPose) {
|
||||
if (skeleton == null) throw new IllegalArgumentException("skeleton cannot be null.");
|
||||
|
||||
if (loop && duration != 0) {
|
||||
@ -125,7 +125,7 @@ public class Animation {
|
||||
|
||||
Object[] timelines = this.timelines.items;
|
||||
for (int i = 0, n = this.timelines.size; i < n; i++)
|
||||
((Timeline)timelines[i]).apply(skeleton, lastTime, time, events, alpha, blend, direction);
|
||||
((Timeline)timelines[i]).apply(skeleton, lastTime, time, events, alpha, blend, direction, appliedPose);
|
||||
}
|
||||
|
||||
/** The animation's name, which is unique across all animations in the skeleton. */
|
||||
@ -140,7 +140,7 @@ public class Animation {
|
||||
/** Controls how timeline values are mixed with setup pose values or current pose values when a timeline is applied with
|
||||
* <code>alpha</code> < 1.
|
||||
* <p>
|
||||
* See Timeline {@link Timeline#apply(Skeleton, float, float, Array, float, MixBlend, MixDirection)}. */
|
||||
* See Timeline {@link Timeline#apply(Skeleton, float, float, Array, float, MixBlend, MixDirection, boolean)}. */
|
||||
static public enum MixBlend {
|
||||
/** Transitions from the setup value to the timeline value (the current value is not used). Before the first frame, the
|
||||
* setup value is set. */
|
||||
@ -168,7 +168,7 @@ public class Animation {
|
||||
/** Indicates whether a timeline's <code>alpha</code> is mixing out over time toward 0 (the setup or current pose value) or
|
||||
* mixing in toward 1 (the timeline's value). Some timelines use this to decide how values are applied.
|
||||
* <p>
|
||||
* See Timeline {@link Timeline#apply(Skeleton, float, float, Array, float, MixBlend, MixDirection)}. */
|
||||
* See Timeline {@link Timeline#apply(Skeleton, float, float, Array, float, MixBlend, MixDirection, boolean)}. */
|
||||
static public enum MixDirection {
|
||||
in, out
|
||||
}
|
||||
@ -239,9 +239,10 @@ public class Animation {
|
||||
* apply animations on top of each other (layering).
|
||||
* @param blend Controls how mixing is applied when <code>alpha</code> < 1.
|
||||
* @param direction Indicates whether the timeline is mixing in or out. Used by timelines which perform instant transitions,
|
||||
* such as {@link DrawOrderTimeline} or {@link AttachmentTimeline}, and others such as {@link ScaleTimeline}. */
|
||||
* such as {@link DrawOrderTimeline} or {@link AttachmentTimeline}, and others such as {@link ScaleTimeline}.
|
||||
* @param appliedPose True to to modify the applied pose. */
|
||||
abstract public void apply (Skeleton skeleton, float lastTime, float time, @Null Array<Event> events, float alpha,
|
||||
MixBlend blend, MixDirection direction);
|
||||
MixBlend blend, MixDirection direction, boolean appliedPose);
|
||||
|
||||
/** Linear search using a stride of 1.
|
||||
* @param time Must be >= the first value in <code>frames</code>.
|
||||
@ -557,10 +558,13 @@ public class Animation {
|
||||
}
|
||||
|
||||
public void apply (Skeleton skeleton, float lastTime, float time, @Null Array<Event> events, float alpha, MixBlend blend,
|
||||
MixDirection direction) {
|
||||
MixDirection direction, boolean appliedPose) {
|
||||
|
||||
Bone bone = skeleton.bones.get(boneIndex);
|
||||
if (bone.active) bone.rotation = getRelativeValue(time, alpha, blend, bone.rotation, bone.data.rotation);
|
||||
if (bone.active) {
|
||||
if (appliedPose) bone = bone.applied;
|
||||
bone.rotation = getRelativeValue(time, alpha, blend, bone.rotation, bone.data.rotation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -580,10 +584,11 @@ public class Animation {
|
||||
}
|
||||
|
||||
public void apply (Skeleton skeleton, float lastTime, float time, @Null Array<Event> events, float alpha, MixBlend blend,
|
||||
MixDirection direction) {
|
||||
MixDirection direction, boolean appliedPose) {
|
||||
|
||||
Bone bone = skeleton.bones.get(boneIndex);
|
||||
if (!bone.active) return;
|
||||
if (appliedPose) bone = bone.applied;
|
||||
|
||||
float[] frames = this.frames;
|
||||
if (time < frames[0]) {
|
||||
@ -650,10 +655,13 @@ public class Animation {
|
||||
}
|
||||
|
||||
public void apply (Skeleton skeleton, float lastTime, float time, @Null Array<Event> events, float alpha, MixBlend blend,
|
||||
MixDirection direction) {
|
||||
MixDirection direction, boolean appliedPose) {
|
||||
|
||||
Bone bone = skeleton.bones.get(boneIndex);
|
||||
if (bone.active) bone.x = getRelativeValue(time, alpha, blend, bone.x, bone.data.x);
|
||||
if (bone.active) {
|
||||
if (appliedPose) bone = bone.applied;
|
||||
bone.x = getRelativeValue(time, alpha, blend, bone.x, bone.data.x);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -671,10 +679,13 @@ public class Animation {
|
||||
}
|
||||
|
||||
public void apply (Skeleton skeleton, float lastTime, float time, @Null Array<Event> events, float alpha, MixBlend blend,
|
||||
MixDirection direction) {
|
||||
MixDirection direction, boolean appliedPose) {
|
||||
|
||||
Bone bone = skeleton.bones.get(boneIndex);
|
||||
if (bone.active) bone.y = getRelativeValue(time, alpha, blend, bone.y, bone.data.y);
|
||||
if (bone.active) {
|
||||
if (appliedPose) bone = bone.applied;
|
||||
bone.y = getRelativeValue(time, alpha, blend, bone.y, bone.data.y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -694,10 +705,11 @@ public class Animation {
|
||||
}
|
||||
|
||||
public void apply (Skeleton skeleton, float lastTime, float time, @Null Array<Event> events, float alpha, MixBlend blend,
|
||||
MixDirection direction) {
|
||||
MixDirection direction, boolean appliedPose) {
|
||||
|
||||
Bone bone = skeleton.bones.get(boneIndex);
|
||||
if (!bone.active) return;
|
||||
if (appliedPose) bone = bone.applied;
|
||||
|
||||
float[] frames = this.frames;
|
||||
if (time < frames[0]) {
|
||||
@ -803,10 +815,13 @@ public class Animation {
|
||||
}
|
||||
|
||||
public void apply (Skeleton skeleton, float lastTime, float time, @Null Array<Event> events, float alpha, MixBlend blend,
|
||||
MixDirection direction) {
|
||||
MixDirection direction, boolean appliedPose) {
|
||||
|
||||
Bone bone = skeleton.bones.get(boneIndex);
|
||||
if (bone.active) bone.scaleX = getScaleValue(time, alpha, blend, direction, bone.scaleX, bone.data.scaleX);
|
||||
if (bone.active) {
|
||||
if (appliedPose) bone = bone.applied;
|
||||
bone.scaleX = getScaleValue(time, alpha, blend, direction, bone.scaleX, bone.data.scaleX);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -824,10 +839,13 @@ public class Animation {
|
||||
}
|
||||
|
||||
public void apply (Skeleton skeleton, float lastTime, float time, @Null Array<Event> events, float alpha, MixBlend blend,
|
||||
MixDirection direction) {
|
||||
MixDirection direction, boolean appliedPose) {
|
||||
|
||||
Bone bone = skeleton.bones.get(boneIndex);
|
||||
if (bone.active) bone.scaleY = getScaleValue(time, alpha, blend, direction, bone.scaleY, bone.data.scaleY);
|
||||
if (bone.active) {
|
||||
if (appliedPose) bone = bone.applied;
|
||||
bone.scaleY = getScaleValue(time, alpha, blend, direction, bone.scaleY, bone.data.scaleY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -847,10 +865,11 @@ public class Animation {
|
||||
}
|
||||
|
||||
public void apply (Skeleton skeleton, float lastTime, float time, @Null Array<Event> events, float alpha, MixBlend blend,
|
||||
MixDirection direction) {
|
||||
MixDirection direction, boolean appliedPose) {
|
||||
|
||||
Bone bone = skeleton.bones.get(boneIndex);
|
||||
if (!bone.active) return;
|
||||
if (appliedPose) bone = bone.applied;
|
||||
|
||||
float[] frames = this.frames;
|
||||
if (time < frames[0]) {
|
||||
@ -917,10 +936,13 @@ public class Animation {
|
||||
}
|
||||
|
||||
public void apply (Skeleton skeleton, float lastTime, float time, @Null Array<Event> events, float alpha, MixBlend blend,
|
||||
MixDirection direction) {
|
||||
MixDirection direction, boolean appliedPose) {
|
||||
|
||||
Bone bone = skeleton.bones.get(boneIndex);
|
||||
if (bone.active) bone.shearX = getRelativeValue(time, alpha, blend, bone.shearX, bone.data.shearX);
|
||||
if (bone.active) {
|
||||
if (appliedPose) bone = bone.applied;
|
||||
bone.shearX = getRelativeValue(time, alpha, blend, bone.shearX, bone.data.shearX);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -938,10 +960,13 @@ public class Animation {
|
||||
}
|
||||
|
||||
public void apply (Skeleton skeleton, float lastTime, float time, @Null Array<Event> events, float alpha, MixBlend blend,
|
||||
MixDirection direction) {
|
||||
MixDirection direction, boolean appliedPose) {
|
||||
|
||||
Bone bone = skeleton.bones.get(boneIndex);
|
||||
if (bone.active) bone.shearY = getRelativeValue(time, alpha, blend, bone.shearY, bone.data.shearY);
|
||||
if (bone.active) {
|
||||
if (appliedPose) bone = bone.applied;
|
||||
bone.shearY = getRelativeValue(time, alpha, blend, bone.shearY, bone.data.shearY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -975,10 +1000,11 @@ public class Animation {
|
||||
}
|
||||
|
||||
public void apply (Skeleton skeleton, float lastTime, float time, @Null Array<Event> events, float alpha, MixBlend blend,
|
||||
MixDirection direction) {
|
||||
MixDirection direction, boolean appliedPose) {
|
||||
|
||||
Bone bone = skeleton.bones.get(boneIndex);
|
||||
if (!bone.active) return;
|
||||
if (appliedPose) bone = bone.applied;
|
||||
|
||||
if (direction == out) {
|
||||
if (blend == setup) bone.inherit = bone.data.inherit;
|
||||
@ -1029,10 +1055,11 @@ public class Animation {
|
||||
}
|
||||
|
||||
public void apply (Skeleton skeleton, float lastTime, float time, @Null Array<Event> events, float alpha, MixBlend blend,
|
||||
MixDirection direction) {
|
||||
MixDirection direction, boolean appliedPose) {
|
||||
|
||||
Slot slot = skeleton.slots.get(slotIndex);
|
||||
if (!slot.bone.active) return;
|
||||
if (appliedPose) slot = slot.applied;
|
||||
|
||||
float[] frames = this.frames;
|
||||
Color color = slot.color;
|
||||
@ -1118,10 +1145,11 @@ public class Animation {
|
||||
}
|
||||
|
||||
public void apply (Skeleton skeleton, float lastTime, float time, @Null Array<Event> events, float alpha, MixBlend blend,
|
||||
MixDirection direction) {
|
||||
MixDirection direction, boolean appliedPose) {
|
||||
|
||||
Slot slot = skeleton.slots.get(slotIndex);
|
||||
if (!slot.bone.active) return;
|
||||
if (appliedPose) slot = slot.applied;
|
||||
|
||||
float[] frames = this.frames;
|
||||
Color color = slot.color;
|
||||
@ -1197,10 +1225,11 @@ public class Animation {
|
||||
}
|
||||
|
||||
public void apply (Skeleton skeleton, float lastTime, float time, @Null Array<Event> events, float alpha, MixBlend blend,
|
||||
MixDirection direction) {
|
||||
MixDirection direction, boolean appliedPose) {
|
||||
|
||||
Slot slot = skeleton.slots.get(slotIndex);
|
||||
if (!slot.bone.active) return;
|
||||
if (appliedPose) slot = slot.applied;
|
||||
|
||||
float[] frames = this.frames;
|
||||
Color color = slot.color;
|
||||
@ -1267,10 +1296,11 @@ public class Animation {
|
||||
}
|
||||
|
||||
public void apply (Skeleton skeleton, float lastTime, float time, @Null Array<Event> events, float alpha, MixBlend blend,
|
||||
MixDirection direction) {
|
||||
MixDirection direction, boolean appliedPose) {
|
||||
|
||||
Slot slot = skeleton.slots.get(slotIndex);
|
||||
if (!slot.bone.active) return;
|
||||
if (appliedPose) slot = slot.applied;
|
||||
|
||||
float[] frames = this.frames;
|
||||
Color light = slot.color, dark = slot.darkColor;
|
||||
@ -1393,10 +1423,11 @@ public class Animation {
|
||||
}
|
||||
|
||||
public void apply (Skeleton skeleton, float lastTime, float time, @Null Array<Event> events, float alpha, MixBlend blend,
|
||||
MixDirection direction) {
|
||||
MixDirection direction, boolean appliedPose) {
|
||||
|
||||
Slot slot = skeleton.slots.get(slotIndex);
|
||||
if (!slot.bone.active) return;
|
||||
if (appliedPose) slot = slot.applied;
|
||||
|
||||
float[] frames = this.frames;
|
||||
Color light = slot.color, dark = slot.darkColor;
|
||||
@ -1518,10 +1549,11 @@ public class Animation {
|
||||
}
|
||||
|
||||
public void apply (Skeleton skeleton, float lastTime, float time, @Null Array<Event> events, float alpha, MixBlend blend,
|
||||
MixDirection direction) {
|
||||
MixDirection direction, boolean appliedPose) {
|
||||
|
||||
Slot slot = skeleton.slots.get(slotIndex);
|
||||
if (!slot.bone.active) return;
|
||||
if (appliedPose) slot = slot.applied;
|
||||
|
||||
if (direction == out) {
|
||||
if (blend == setup) setAttachment(skeleton, slot, slot.data.attachmentName);
|
||||
@ -1636,11 +1668,12 @@ public class Animation {
|
||||
}
|
||||
|
||||
public void apply (Skeleton skeleton, float lastTime, float time, @Null Array<Event> events, float alpha, MixBlend blend,
|
||||
MixDirection direction) {
|
||||
MixDirection direction, boolean appliedPose) {
|
||||
|
||||
Slot slot = skeleton.slots.get(slotIndex);
|
||||
if (!slot.bone.active || !(slot.attachment instanceof VertexAttachment vertexAttachment)
|
||||
|| vertexAttachment.getTimelineAttachment() != attachment) return;
|
||||
if (appliedPose) slot = slot.applied;
|
||||
|
||||
FloatArray deformArray = slot.deform;
|
||||
if (deformArray.size == 0) blend = setup;
|
||||
@ -1838,7 +1871,7 @@ public class Animation {
|
||||
|
||||
/** Fires events for frames > <code>lastTime</code> and <= <code>time</code>. */
|
||||
public void apply (Skeleton skeleton, float lastTime, float time, @Null Array<Event> firedEvents, float alpha,
|
||||
MixBlend blend, MixDirection direction) {
|
||||
MixBlend blend, MixDirection direction, boolean appliedPose) {
|
||||
|
||||
if (firedEvents == null) return;
|
||||
|
||||
@ -1846,7 +1879,7 @@ public class Animation {
|
||||
int frameCount = frames.length;
|
||||
|
||||
if (lastTime > time) { // Apply after lastTime for looped animations.
|
||||
apply(skeleton, lastTime, Integer.MAX_VALUE, firedEvents, alpha, blend, direction);
|
||||
apply(skeleton, lastTime, Integer.MAX_VALUE, firedEvents, alpha, blend, direction, appliedPose);
|
||||
lastTime = -1f;
|
||||
} else if (lastTime >= frames[frameCount - 1]) // Last time is after last frame.
|
||||
return;
|
||||
@ -1899,7 +1932,7 @@ public class Animation {
|
||||
}
|
||||
|
||||
public void apply (Skeleton skeleton, float lastTime, float time, @Null Array<Event> events, float alpha, MixBlend blend,
|
||||
MixDirection direction) {
|
||||
MixDirection direction, boolean appliedPose) {
|
||||
|
||||
if (direction == out) {
|
||||
if (blend == setup) arraycopy(skeleton.slots.items, 0, skeleton.drawOrder.items, 0, skeleton.slots.size);
|
||||
@ -1963,10 +1996,11 @@ public class Animation {
|
||||
}
|
||||
|
||||
public void apply (Skeleton skeleton, float lastTime, float time, @Null Array<Event> events, float alpha, MixBlend blend,
|
||||
MixDirection direction) {
|
||||
MixDirection direction, boolean appliedPose) {
|
||||
|
||||
IkConstraint constraint = skeleton.ikConstraints.get(constraintIndex);
|
||||
if (!constraint.active) return;
|
||||
if (appliedPose) constraint = constraint.applied;
|
||||
|
||||
float[] frames = this.frames;
|
||||
if (time < frames[0]) {
|
||||
@ -2072,10 +2106,11 @@ public class Animation {
|
||||
}
|
||||
|
||||
public void apply (Skeleton skeleton, float lastTime, float time, @Null Array<Event> events, float alpha, MixBlend blend,
|
||||
MixDirection direction) {
|
||||
MixDirection direction, boolean appliedPose) {
|
||||
|
||||
TransformConstraint constraint = skeleton.transformConstraints.get(constraintIndex);
|
||||
if (!constraint.active) return;
|
||||
if (appliedPose) constraint = constraint.applied;
|
||||
|
||||
float[] frames = this.frames;
|
||||
if (time < frames[0]) {
|
||||
@ -2171,11 +2206,13 @@ public class Animation {
|
||||
}
|
||||
|
||||
public void apply (Skeleton skeleton, float lastTime, float time, @Null Array<Event> events, float alpha, MixBlend blend,
|
||||
MixDirection direction) {
|
||||
MixDirection direction, boolean appliedPose) {
|
||||
|
||||
PathConstraint constraint = skeleton.pathConstraints.get(constraintIndex);
|
||||
if (constraint.active)
|
||||
if (constraint.active) {
|
||||
if (appliedPose) constraint = constraint.applied;
|
||||
constraint.position = getAbsoluteValue(time, alpha, blend, constraint.position, constraint.data.position);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2195,11 +2232,13 @@ public class Animation {
|
||||
}
|
||||
|
||||
public void apply (Skeleton skeleton, float lastTime, float time, @Null Array<Event> events, float alpha, MixBlend blend,
|
||||
MixDirection direction) {
|
||||
MixDirection direction, boolean appliedPose) {
|
||||
|
||||
PathConstraint constraint = skeleton.pathConstraints.get(constraintIndex);
|
||||
if (constraint.active)
|
||||
if (constraint.active) {
|
||||
if (appliedPose) constraint = constraint.applied;
|
||||
constraint.spacing = getAbsoluteValue(time, alpha, blend, constraint.spacing, constraint.data.spacing);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2238,10 +2277,11 @@ public class Animation {
|
||||
}
|
||||
|
||||
public void apply (Skeleton skeleton, float lastTime, float time, @Null Array<Event> events, float alpha, MixBlend blend,
|
||||
MixDirection direction) {
|
||||
MixDirection direction, boolean appliedPose) {
|
||||
|
||||
PathConstraint constraint = skeleton.pathConstraints.get(constraintIndex);
|
||||
if (!constraint.active) return;
|
||||
if (appliedPose) constraint = constraint.applied;
|
||||
|
||||
float[] frames = this.frames;
|
||||
if (time < frames[0]) {
|
||||
@ -2314,7 +2354,7 @@ public class Animation {
|
||||
}
|
||||
|
||||
public void apply (Skeleton skeleton, float lastTime, float time, @Null Array<Event> events, float alpha, MixBlend blend,
|
||||
MixDirection direction) {
|
||||
MixDirection direction, boolean appliedPose) {
|
||||
|
||||
PhysicsConstraint constraint;
|
||||
if (constraintIndex == -1) {
|
||||
@ -2323,12 +2363,17 @@ public class Animation {
|
||||
Object[] constraints = skeleton.physicsConstraints.items;
|
||||
for (int i = 0, n = skeleton.physicsConstraints.size; i < n; i++) {
|
||||
constraint = (PhysicsConstraint)constraints[i];
|
||||
if (constraint.active && global(constraint.data))
|
||||
if (constraint.active && global(constraint.data)) {
|
||||
if (appliedPose) constraint = constraint.applied;
|
||||
set(constraint, getAbsoluteValue(time, alpha, blend, get(constraint), setup(constraint), value));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
constraint = skeleton.physicsConstraints.get(constraintIndex);
|
||||
if (constraint.active) set(constraint, getAbsoluteValue(time, alpha, blend, get(constraint), setup(constraint)));
|
||||
if (constraint.active) {
|
||||
if (appliedPose) constraint = constraint.applied;
|
||||
set(constraint, getAbsoluteValue(time, alpha, blend, get(constraint), setup(constraint)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2532,7 +2577,7 @@ public class Animation {
|
||||
|
||||
/** Resets the physics constraint when frames > <code>lastTime</code> and <= <code>time</code>. */
|
||||
public void apply (Skeleton skeleton, float lastTime, float time, @Null Array<Event> firedEvents, float alpha,
|
||||
MixBlend blend, MixDirection direction) {
|
||||
MixBlend blend, MixDirection direction, boolean appliedPose) {
|
||||
|
||||
PhysicsConstraint constraint = null;
|
||||
if (constraintIndex != -1) {
|
||||
@ -2543,20 +2588,24 @@ public class Animation {
|
||||
float[] frames = this.frames;
|
||||
|
||||
if (lastTime > time) { // Apply after lastTime for looped animations.
|
||||
apply(skeleton, lastTime, Integer.MAX_VALUE, null, alpha, blend, direction);
|
||||
apply(skeleton, lastTime, Integer.MAX_VALUE, null, alpha, blend, direction, appliedPose);
|
||||
lastTime = -1f;
|
||||
} else if (lastTime >= frames[frames.length - 1]) // Last time is after last frame.
|
||||
return;
|
||||
if (time < frames[0]) return;
|
||||
|
||||
if (lastTime < frames[0] || time >= frames[search(frames, lastTime) + 1]) {
|
||||
if (constraint != null)
|
||||
if (constraint != null) {
|
||||
if (appliedPose) constraint = constraint.applied;
|
||||
constraint.reset();
|
||||
else {
|
||||
} else {
|
||||
Object[] constraints = skeleton.physicsConstraints.items;
|
||||
for (int i = 0, n = skeleton.physicsConstraints.size; i < n; i++) {
|
||||
constraint = (PhysicsConstraint)constraints[i];
|
||||
if (constraint.active) constraint.reset();
|
||||
if (constraint.active) {
|
||||
if (appliedPose) constraint = constraint.applied;
|
||||
constraint.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2601,10 +2650,12 @@ public class Animation {
|
||||
}
|
||||
|
||||
public void apply (Skeleton skeleton, float lastTime, float time, @Null Array<Event> events, float alpha, MixBlend blend,
|
||||
MixDirection direction) {
|
||||
MixDirection direction, boolean appliedPose) {
|
||||
|
||||
Slot slot = skeleton.slots.get(slotIndex);
|
||||
if (!slot.bone.active) return;
|
||||
if (appliedPose) slot = slot.applied;
|
||||
|
||||
Attachment slotAttachment = slot.attachment;
|
||||
if (slotAttachment != attachment) {
|
||||
if (!(slotAttachment instanceof VertexAttachment vertexAttachment)
|
||||
|
||||
@ -238,8 +238,10 @@ public class AnimationState {
|
||||
Object timeline = timelines[ii];
|
||||
if (timeline instanceof AttachmentTimeline attachmentTimeline)
|
||||
applyAttachmentTimeline(attachmentTimeline, skeleton, applyTime, blend, attachments);
|
||||
else
|
||||
((Timeline)timeline).apply(skeleton, animationLast, applyTime, applyEvents, alpha, blend, MixDirection.in);
|
||||
else {
|
||||
((Timeline)timeline).apply(skeleton, animationLast, applyTime, applyEvents, alpha, blend, MixDirection.in,
|
||||
false);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
int[] timelineMode = current.timelineMode.items;
|
||||
@ -258,7 +260,7 @@ public class AnimationState {
|
||||
} else if (timeline instanceof AttachmentTimeline attachmentTimeline)
|
||||
applyAttachmentTimeline(attachmentTimeline, skeleton, applyTime, blend, attachments);
|
||||
else
|
||||
timeline.apply(skeleton, animationLast, applyTime, applyEvents, alpha, timelineBlend, MixDirection.in);
|
||||
timeline.apply(skeleton, animationLast, applyTime, applyEvents, alpha, timelineBlend, MixDirection.in, false);
|
||||
}
|
||||
}
|
||||
queueEvents(current, animationTime);
|
||||
@ -313,7 +315,7 @@ public class AnimationState {
|
||||
|
||||
if (blend == MixBlend.add) {
|
||||
for (int i = 0; i < timelineCount; i++)
|
||||
((Timeline)timelines[i]).apply(skeleton, animationLast, applyTime, events, alphaMix, blend, MixDirection.out);
|
||||
((Timeline)timelines[i]).apply(skeleton, animationLast, applyTime, events, alphaMix, blend, MixDirection.out, false);
|
||||
} else {
|
||||
int[] timelineMode = from.timelineMode.items;
|
||||
Object[] timelineHoldMix = from.timelineHoldMix.items;
|
||||
@ -363,7 +365,7 @@ public class AnimationState {
|
||||
else {
|
||||
if (drawOrder && timeline instanceof DrawOrderTimeline && timelineBlend == MixBlend.setup)
|
||||
direction = MixDirection.in;
|
||||
timeline.apply(skeleton, animationLast, applyTime, events, alpha, timelineBlend, direction);
|
||||
timeline.apply(skeleton, animationLast, applyTime, events, alpha, timelineBlend, direction, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -409,7 +411,7 @@ public class AnimationState {
|
||||
if (firstFrame) timelinesRotation[i] = 0;
|
||||
|
||||
if (alpha == 1) {
|
||||
timeline.apply(skeleton, 0, time, null, 1, blend, MixDirection.in);
|
||||
timeline.apply(skeleton, 0, time, null, 1, blend, MixDirection.in, false);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@ -29,41 +29,45 @@
|
||||
|
||||
package com.esotericsoftware.spine;
|
||||
|
||||
import static com.badlogic.gdx.math.Matrix3.*;
|
||||
import static com.esotericsoftware.spine.utils.SpineUtils.*;
|
||||
|
||||
import com.badlogic.gdx.math.Matrix3;
|
||||
import com.badlogic.gdx.math.Vector2;
|
||||
import com.badlogic.gdx.utils.Array;
|
||||
import com.badlogic.gdx.utils.Null;
|
||||
|
||||
import com.esotericsoftware.spine.BoneData.Inherit;
|
||||
import com.esotericsoftware.spine.Skeleton.Physics;
|
||||
|
||||
/** Stores a bone's current pose.
|
||||
* <p>
|
||||
* A bone has a local transform which is used to compute its world transform. A bone also has an applied transform, which is a
|
||||
* local transform that can be applied to compute the world transform. The local transform and applied transform may differ if a
|
||||
* constraint or application code modifies the world transform after it was computed from the local transform. */
|
||||
public class Bone implements Updatable {
|
||||
public class Bone {
|
||||
final BoneData data;
|
||||
final Skeleton skeleton;
|
||||
@Null final Bone parent;
|
||||
final Array<Bone> children = new Array();
|
||||
final Array<Bone> children;
|
||||
BoneApplied applied;
|
||||
|
||||
float x, y, rotation, scaleX, scaleY, shearX, shearY;
|
||||
float ax, ay, arotation, ascaleX, ascaleY, ashearX, ashearY;
|
||||
float a, b, worldX;
|
||||
float c, d, worldY;
|
||||
Inherit inherit;
|
||||
|
||||
boolean sorted, active;
|
||||
|
||||
Bone (Bone bone) {
|
||||
this.data = bone.data;
|
||||
this.skeleton = bone.skeleton;
|
||||
this.parent = bone.parent;
|
||||
this.children = bone.children;
|
||||
}
|
||||
|
||||
public Bone (BoneData data, Skeleton skeleton, @Null Bone parent) {
|
||||
if (data == null) throw new IllegalArgumentException("data cannot be null.");
|
||||
if (skeleton == null) throw new IllegalArgumentException("skeleton cannot be null.");
|
||||
this.data = data;
|
||||
this.skeleton = skeleton;
|
||||
this.parent = parent;
|
||||
children = new Array();
|
||||
|
||||
applied = new BoneApplied(this);
|
||||
|
||||
setToSetupPose();
|
||||
}
|
||||
|
||||
@ -73,6 +77,7 @@ public class Bone implements Updatable {
|
||||
if (skeleton == null) throw new IllegalArgumentException("skeleton cannot be null.");
|
||||
this.skeleton = skeleton;
|
||||
this.parent = parent;
|
||||
children = new Array();
|
||||
data = bone.data;
|
||||
x = bone.x;
|
||||
y = bone.y;
|
||||
@ -84,131 +89,6 @@ public class Bone implements Updatable {
|
||||
inherit = bone.inherit;
|
||||
}
|
||||
|
||||
/** Computes the world transform using the parent bone and this bone's local applied transform. */
|
||||
public void update (Physics physics) {
|
||||
updateWorldTransform(ax, ay, arotation, ascaleX, ascaleY, ashearX, ashearY);
|
||||
}
|
||||
|
||||
/** Computes the world transform using the parent bone and this bone's local transform.
|
||||
* <p>
|
||||
* See {@link #updateWorldTransform(float, float, float, float, float, float, float)}. */
|
||||
public void updateWorldTransform () {
|
||||
updateWorldTransform(x, y, rotation, scaleX, scaleY, shearX, shearY);
|
||||
}
|
||||
|
||||
/** Computes the world transform using the parent bone and the specified local transform. The applied transform is set to the
|
||||
* specified local transform. Child bones are not updated.
|
||||
* <p>
|
||||
* See <a href="https://esotericsoftware.com/spine-runtime-skeletons#World-transforms">World transforms</a> in the Spine
|
||||
* Runtimes Guide. */
|
||||
public void updateWorldTransform (float x, float y, float rotation, float scaleX, float scaleY, float shearX, float shearY) {
|
||||
ax = x;
|
||||
ay = y;
|
||||
arotation = rotation;
|
||||
ascaleX = scaleX;
|
||||
ascaleY = scaleY;
|
||||
ashearX = shearX;
|
||||
ashearY = shearY;
|
||||
|
||||
Bone parent = this.parent;
|
||||
if (parent == null) { // Root bone.
|
||||
Skeleton skeleton = this.skeleton;
|
||||
float sx = skeleton.scaleX, sy = skeleton.scaleY;
|
||||
float rx = (rotation + shearX) * degRad;
|
||||
float ry = (rotation + 90 + shearY) * degRad;
|
||||
a = cos(rx) * scaleX * sx;
|
||||
b = cos(ry) * scaleY * sx;
|
||||
c = sin(rx) * scaleX * sy;
|
||||
d = sin(ry) * scaleY * sy;
|
||||
worldX = x * sx + skeleton.x;
|
||||
worldY = y * sy + skeleton.y;
|
||||
return;
|
||||
}
|
||||
|
||||
float pa = parent.a, pb = parent.b, pc = parent.c, pd = parent.d;
|
||||
worldX = pa * x + pb * y + parent.worldX;
|
||||
worldY = pc * x + pd * y + parent.worldY;
|
||||
|
||||
switch (inherit) {
|
||||
case normal -> {
|
||||
float rx = (rotation + shearX) * degRad;
|
||||
float ry = (rotation + 90 + shearY) * degRad;
|
||||
float la = cos(rx) * scaleX;
|
||||
float lb = cos(ry) * scaleY;
|
||||
float lc = sin(rx) * scaleX;
|
||||
float ld = sin(ry) * scaleY;
|
||||
a = pa * la + pb * lc;
|
||||
b = pa * lb + pb * ld;
|
||||
c = pc * la + pd * lc;
|
||||
d = pc * lb + pd * ld;
|
||||
return;
|
||||
}
|
||||
case onlyTranslation -> {
|
||||
float rx = (rotation + shearX) * degRad;
|
||||
float ry = (rotation + 90 + shearY) * degRad;
|
||||
a = cos(rx) * scaleX;
|
||||
b = cos(ry) * scaleY;
|
||||
c = sin(rx) * scaleX;
|
||||
d = sin(ry) * scaleY;
|
||||
}
|
||||
case noRotationOrReflection -> {
|
||||
float sx = 1 / skeleton.scaleX, sy = 1 / skeleton.scaleY;
|
||||
pa *= sx;
|
||||
pc *= sy;
|
||||
float s = pa * pa + pc * pc, prx;
|
||||
if (s > 0.0001f) {
|
||||
s = Math.abs(pa * pd * sy - pb * sx * pc) / s;
|
||||
pb = pc * s;
|
||||
pd = pa * s;
|
||||
prx = atan2Deg(pc, pa);
|
||||
} else {
|
||||
pa = 0;
|
||||
pc = 0;
|
||||
prx = 90 - atan2Deg(pd, pb);
|
||||
}
|
||||
float rx = (rotation + shearX - prx) * degRad;
|
||||
float ry = (rotation + shearY - prx + 90) * degRad;
|
||||
float la = cos(rx) * scaleX;
|
||||
float lb = cos(ry) * scaleY;
|
||||
float lc = sin(rx) * scaleX;
|
||||
float ld = sin(ry) * scaleY;
|
||||
a = pa * la - pb * lc;
|
||||
b = pa * lb - pb * ld;
|
||||
c = pc * la + pd * lc;
|
||||
d = pc * lb + pd * ld;
|
||||
}
|
||||
case noScale, noScaleOrReflection -> {
|
||||
rotation *= degRad;
|
||||
float cos = cos(rotation), sin = sin(rotation);
|
||||
float za = (pa * cos + pb * sin) / skeleton.scaleX;
|
||||
float zc = (pc * cos + pd * sin) / skeleton.scaleY;
|
||||
float s = (float)Math.sqrt(za * za + zc * zc);
|
||||
if (s > 0.00001f) s = 1 / s;
|
||||
za *= s;
|
||||
zc *= s;
|
||||
s = (float)Math.sqrt(za * za + zc * zc);
|
||||
if (inherit == Inherit.noScale && (pa * pd - pb * pc < 0) != (skeleton.scaleX < 0 != skeleton.scaleY < 0)) s = -s;
|
||||
rotation = PI / 2 + atan2(zc, za);
|
||||
float zb = cos(rotation) * s;
|
||||
float zd = sin(rotation) * s;
|
||||
shearX *= degRad;
|
||||
shearY = (90 + shearY) * degRad;
|
||||
float la = cos(shearX) * scaleX;
|
||||
float lb = cos(shearY) * scaleY;
|
||||
float lc = sin(shearX) * scaleX;
|
||||
float ld = sin(shearY) * scaleY;
|
||||
a = za * la + zb * lc;
|
||||
b = za * lb + zb * ld;
|
||||
c = zc * la + zd * lc;
|
||||
d = zc * lb + zd * ld;
|
||||
}
|
||||
}
|
||||
a *= skeleton.scaleX;
|
||||
b *= skeleton.scaleX;
|
||||
c *= skeleton.scaleY;
|
||||
d *= skeleton.scaleY;
|
||||
}
|
||||
|
||||
/** Sets this bone's local transform to the setup pose. */
|
||||
public void setToSetupPose () {
|
||||
BoneData data = this.data;
|
||||
@ -246,8 +126,6 @@ public class Bone implements Updatable {
|
||||
return active;
|
||||
}
|
||||
|
||||
// -- Local transform
|
||||
|
||||
/** The local x translation. */
|
||||
public float getX () {
|
||||
return x;
|
||||
@ -336,309 +214,11 @@ public class Bone implements Updatable {
|
||||
this.inherit = inherit;
|
||||
}
|
||||
|
||||
// -- Applied transform
|
||||
|
||||
/** The applied local x translation. */
|
||||
public float getAX () {
|
||||
return ax;
|
||||
/** Returns the bone for applied pose. */
|
||||
public BoneApplied getApplied () {
|
||||
return applied;
|
||||
}
|
||||
|
||||
public void setAX (float ax) {
|
||||
this.ax = ax;
|
||||
}
|
||||
|
||||
/** The applied local y translation. */
|
||||
public float getAY () {
|
||||
return ay;
|
||||
}
|
||||
|
||||
public void setAY (float ay) {
|
||||
this.ay = ay;
|
||||
}
|
||||
|
||||
/** The applied local rotation in degrees, counter clockwise. */
|
||||
public float getARotation () {
|
||||
return arotation;
|
||||
}
|
||||
|
||||
public void setARotation (float arotation) {
|
||||
this.arotation = arotation;
|
||||
}
|
||||
|
||||
/** The applied local scaleX. */
|
||||
public float getAScaleX () {
|
||||
return ascaleX;
|
||||
}
|
||||
|
||||
public void setAScaleX (float ascaleX) {
|
||||
this.ascaleX = ascaleX;
|
||||
}
|
||||
|
||||
/** The applied local scaleY. */
|
||||
public float getAScaleY () {
|
||||
return ascaleY;
|
||||
}
|
||||
|
||||
public void setAScaleY (float ascaleY) {
|
||||
this.ascaleY = ascaleY;
|
||||
}
|
||||
|
||||
/** The applied local shearX. */
|
||||
public float getAShearX () {
|
||||
return ashearX;
|
||||
}
|
||||
|
||||
public void setAShearX (float ashearX) {
|
||||
this.ashearX = ashearX;
|
||||
}
|
||||
|
||||
/** The applied local shearY. */
|
||||
public float getAShearY () {
|
||||
return ashearY;
|
||||
}
|
||||
|
||||
public void setAShearY (float ashearY) {
|
||||
this.ashearY = ashearY;
|
||||
}
|
||||
|
||||
/** Computes the applied transform values from the world transform.
|
||||
* <p>
|
||||
* If the world transform is modified (by a constraint, {@link #rotateWorld(float)}, etc) then this method should be called so
|
||||
* the applied transform matches the world transform. The applied transform may be needed by other code (eg to apply another
|
||||
* constraint).
|
||||
* <p>
|
||||
* Some information is ambiguous in the world transform, such as -1,-1 scale versus 180 rotation. The applied transform after
|
||||
* calling this method is equivalent to the local transform used to compute the world transform, but may not be identical. */
|
||||
public void updateAppliedTransform () {
|
||||
Bone parent = this.parent;
|
||||
if (parent == null) {
|
||||
ax = worldX - skeleton.x;
|
||||
ay = worldY - skeleton.y;
|
||||
float a = this.a, b = this.b, c = this.c, d = this.d;
|
||||
arotation = atan2Deg(c, a);
|
||||
ascaleX = (float)Math.sqrt(a * a + c * c);
|
||||
ascaleY = (float)Math.sqrt(b * b + d * d);
|
||||
ashearX = 0;
|
||||
ashearY = atan2Deg(a * b + c * d, a * d - b * c);
|
||||
return;
|
||||
}
|
||||
|
||||
float pa = parent.a, pb = parent.b, pc = parent.c, pd = parent.d;
|
||||
float pid = 1 / (pa * pd - pb * pc);
|
||||
float ia = pd * pid, ib = pb * pid, ic = pc * pid, id = pa * pid;
|
||||
float dx = worldX - parent.worldX, dy = worldY - parent.worldY;
|
||||
ax = (dx * ia - dy * ib);
|
||||
ay = (dy * id - dx * ic);
|
||||
|
||||
float ra, rb, rc, rd;
|
||||
if (inherit == Inherit.onlyTranslation) {
|
||||
ra = a;
|
||||
rb = b;
|
||||
rc = c;
|
||||
rd = d;
|
||||
} else {
|
||||
switch (inherit) {
|
||||
case noRotationOrReflection -> {
|
||||
float s = Math.abs(pa * pd - pb * pc) / (pa * pa + pc * pc);
|
||||
pb = -pc * skeleton.scaleX * s / skeleton.scaleY;
|
||||
pd = pa * skeleton.scaleY * s / skeleton.scaleX;
|
||||
pid = 1 / (pa * pd - pb * pc);
|
||||
ia = pd * pid;
|
||||
ib = pb * pid;
|
||||
}
|
||||
case noScale, noScaleOrReflection -> {
|
||||
float r = rotation * degRad, cos = cos(r), sin = sin(r);
|
||||
pa = (pa * cos + pb * sin) / skeleton.scaleX;
|
||||
pc = (pc * cos + pd * sin) / skeleton.scaleY;
|
||||
float s = (float)Math.sqrt(pa * pa + pc * pc);
|
||||
if (s > 0.00001f) s = 1 / s;
|
||||
pa *= s;
|
||||
pc *= s;
|
||||
s = (float)Math.sqrt(pa * pa + pc * pc);
|
||||
if (inherit == Inherit.noScale && pid < 0 != (skeleton.scaleX < 0 != skeleton.scaleY < 0)) s = -s;
|
||||
r = PI / 2 + atan2(pc, pa);
|
||||
pb = cos(r) * s;
|
||||
pd = sin(r) * s;
|
||||
pid = 1 / (pa * pd - pb * pc);
|
||||
ia = pd * pid;
|
||||
ib = pb * pid;
|
||||
ic = pc * pid;
|
||||
id = pa * pid;
|
||||
}
|
||||
}
|
||||
ra = ia * a - ib * c;
|
||||
rb = ia * b - ib * d;
|
||||
rc = id * c - ic * a;
|
||||
rd = id * d - ic * b;
|
||||
}
|
||||
|
||||
ashearX = 0;
|
||||
ascaleX = (float)Math.sqrt(ra * ra + rc * rc);
|
||||
if (ascaleX > 0.0001f) {
|
||||
float det = ra * rd - rb * rc;
|
||||
ascaleY = det / ascaleX;
|
||||
ashearY = -atan2Deg(ra * rb + rc * rd, det);
|
||||
arotation = atan2Deg(rc, ra);
|
||||
} else {
|
||||
ascaleX = 0;
|
||||
ascaleY = (float)Math.sqrt(rb * rb + rd * rd);
|
||||
ashearY = 0;
|
||||
arotation = 90 - atan2Deg(rd, rb);
|
||||
}
|
||||
}
|
||||
|
||||
// -- World transform
|
||||
|
||||
/** Part of the world transform matrix for the X axis. If changed, {@link #updateAppliedTransform()} should be called. */
|
||||
public float getA () {
|
||||
return a;
|
||||
}
|
||||
|
||||
public void setA (float a) {
|
||||
this.a = a;
|
||||
}
|
||||
|
||||
/** Part of the world transform matrix for the Y axis. If changed, {@link #updateAppliedTransform()} should be called. */
|
||||
public float getB () {
|
||||
return b;
|
||||
}
|
||||
|
||||
public void setB (float b) {
|
||||
this.b = b;
|
||||
}
|
||||
|
||||
/** Part of the world transform matrix for the X axis. If changed, {@link #updateAppliedTransform()} should be called. */
|
||||
public float getC () {
|
||||
return c;
|
||||
}
|
||||
|
||||
public void setC (float c) {
|
||||
this.c = c;
|
||||
}
|
||||
|
||||
/** Part of the world transform matrix for the Y axis. If changed, {@link #updateAppliedTransform()} should be called. */
|
||||
public float getD () {
|
||||
return d;
|
||||
}
|
||||
|
||||
public void setD (float d) {
|
||||
this.d = d;
|
||||
}
|
||||
|
||||
/** The world X position. If changed, {@link #updateAppliedTransform()} should be called. */
|
||||
public float getWorldX () {
|
||||
return worldX;
|
||||
}
|
||||
|
||||
public void setWorldX (float worldX) {
|
||||
this.worldX = worldX;
|
||||
}
|
||||
|
||||
/** The world Y position. If changed, {@link #updateAppliedTransform()} should be called. */
|
||||
public float getWorldY () {
|
||||
return worldY;
|
||||
}
|
||||
|
||||
public void setWorldY (float worldY) {
|
||||
this.worldY = worldY;
|
||||
}
|
||||
|
||||
/** The world rotation for the X axis, calculated using {@link #a} and {@link #c}. */
|
||||
public float getWorldRotationX () {
|
||||
return atan2Deg(c, a);
|
||||
}
|
||||
|
||||
/** The world rotation for the Y axis, calculated using {@link #b} and {@link #d}. */
|
||||
public float getWorldRotationY () {
|
||||
return atan2Deg(d, b);
|
||||
}
|
||||
|
||||
/** The magnitude (always positive) of the world scale X, calculated using {@link #a} and {@link #c}. */
|
||||
public float getWorldScaleX () {
|
||||
return (float)Math.sqrt(a * a + c * c);
|
||||
}
|
||||
|
||||
/** The magnitude (always positive) of the world scale Y, calculated using {@link #b} and {@link #d}. */
|
||||
public float getWorldScaleY () {
|
||||
return (float)Math.sqrt(b * b + d * d);
|
||||
}
|
||||
|
||||
public Matrix3 getWorldTransform (Matrix3 worldTransform) {
|
||||
if (worldTransform == null) throw new IllegalArgumentException("worldTransform cannot be null.");
|
||||
float[] val = worldTransform.val;
|
||||
val[M00] = a;
|
||||
val[M01] = b;
|
||||
val[M10] = c;
|
||||
val[M11] = d;
|
||||
val[M02] = worldX;
|
||||
val[M12] = worldY;
|
||||
val[M20] = 0;
|
||||
val[M21] = 0;
|
||||
val[M22] = 1;
|
||||
return worldTransform;
|
||||
}
|
||||
|
||||
/** Transforms a point from world coordinates to the bone's local coordinates. */
|
||||
public Vector2 worldToLocal (Vector2 world) {
|
||||
if (world == null) throw new IllegalArgumentException("world cannot be null.");
|
||||
float det = a * d - b * c;
|
||||
float x = world.x - worldX, y = world.y - worldY;
|
||||
world.x = (x * d - y * b) / det;
|
||||
world.y = (y * a - x * c) / det;
|
||||
return world;
|
||||
}
|
||||
|
||||
/** Transforms a point from the bone's local coordinates to world coordinates. */
|
||||
public Vector2 localToWorld (Vector2 local) {
|
||||
if (local == null) throw new IllegalArgumentException("local cannot be null.");
|
||||
float x = local.x, y = local.y;
|
||||
local.x = x * a + y * b + worldX;
|
||||
local.y = x * c + y * d + worldY;
|
||||
return local;
|
||||
}
|
||||
|
||||
/** Transforms a point from world coordinates to the parent bone's local coordinates. */
|
||||
public Vector2 worldToParent (Vector2 world) {
|
||||
if (world == null) throw new IllegalArgumentException("world cannot be null.");
|
||||
return parent == null ? world : parent.worldToLocal(world);
|
||||
}
|
||||
|
||||
/** Transforms a point from the parent bone's coordinates to world coordinates. */
|
||||
public Vector2 parentToWorld (Vector2 world) {
|
||||
if (world == null) throw new IllegalArgumentException("world cannot be null.");
|
||||
return parent == null ? world : parent.localToWorld(world);
|
||||
}
|
||||
|
||||
/** Transforms a world rotation to a local rotation. */
|
||||
public float worldToLocalRotation (float worldRotation) {
|
||||
worldRotation *= degRad;
|
||||
float sin = sin(worldRotation), cos = cos(worldRotation);
|
||||
return atan2Deg(a * sin - c * cos, d * cos - b * sin) + rotation - shearX;
|
||||
}
|
||||
|
||||
/** Transforms a local rotation to a world rotation. */
|
||||
public float localToWorldRotation (float localRotation) {
|
||||
localRotation = (localRotation - rotation - shearX) * degRad;
|
||||
float sin = sin(localRotation), cos = cos(localRotation);
|
||||
return atan2Deg(cos * c + sin * d, cos * a + sin * b);
|
||||
}
|
||||
|
||||
/** Rotates the world transform the specified amount.
|
||||
* <p>
|
||||
* After changes are made to the world transform, {@link #updateAppliedTransform()} should be called and
|
||||
* {@link #update(Physics)} will need to be called on any child bones, recursively. */
|
||||
public void rotateWorld (float degrees) {
|
||||
degrees *= degRad;
|
||||
float sin = sin(degrees), cos = cos(degrees);
|
||||
float ra = a, rb = b;
|
||||
a = cos * ra - sin * c;
|
||||
b = cos * rb - sin * d;
|
||||
c = sin * ra + cos * c;
|
||||
d = sin * rb + cos * d;
|
||||
}
|
||||
|
||||
// ---
|
||||
|
||||
public String toString () {
|
||||
return data.name;
|
||||
}
|
||||
|
||||
@ -0,0 +1,372 @@
|
||||
|
||||
package com.esotericsoftware.spine;
|
||||
|
||||
import static com.badlogic.gdx.math.Matrix3.*;
|
||||
import static com.esotericsoftware.spine.utils.SpineUtils.*;
|
||||
|
||||
import com.badlogic.gdx.math.Matrix3;
|
||||
import com.badlogic.gdx.math.Vector2;
|
||||
import com.badlogic.gdx.utils.Null;
|
||||
|
||||
import com.esotericsoftware.spine.BoneData.Inherit;
|
||||
import com.esotericsoftware.spine.Skeleton.Physics;
|
||||
|
||||
public class BoneApplied extends Bone implements Updatable {
|
||||
final Bone pose;
|
||||
@Null final BoneApplied parentApplied;
|
||||
float a, b, worldX;
|
||||
float c, d, worldY;
|
||||
|
||||
BoneApplied (Bone bone) {
|
||||
super(bone);
|
||||
pose = bone;
|
||||
parentApplied = parent == null ? null : parent.applied;
|
||||
}
|
||||
|
||||
/** Computes the world transform using the parent bone and this bone's local applied transform. */
|
||||
public void updateWorldTransform () {
|
||||
updateWorldTransform();
|
||||
}
|
||||
|
||||
/** Computes the world transform using the parent bone and this bone's local transform.
|
||||
* <p>
|
||||
* See {@link #updateWorldTransform(float, float, float, float, float, float, float)}. */
|
||||
/** Computes the world transform using the parent bone and the specified local transform. The applied transform is set to the
|
||||
* specified local transform. Child bones are not updated.
|
||||
* <p>
|
||||
* See <a href="https://esotericsoftware.com/spine-runtime-skeletons#World-transforms">World transforms</a> in the Spine
|
||||
* Runtimes Guide. */
|
||||
public void update (Physics physics) {
|
||||
BoneApplied parent = parentApplied;
|
||||
if (parent == null) { // Root bone.
|
||||
Skeleton skeleton = this.skeleton;
|
||||
float sx = skeleton.scaleX, sy = skeleton.scaleY;
|
||||
float rx = (rotation + shearX) * degRad;
|
||||
float ry = (rotation + 90 + shearY) * degRad;
|
||||
a = cos(rx) * scaleX * sx;
|
||||
b = cos(ry) * scaleY * sx;
|
||||
c = sin(rx) * scaleX * sy;
|
||||
d = sin(ry) * scaleY * sy;
|
||||
worldX = x * sx + skeleton.x;
|
||||
worldY = y * sy + skeleton.y;
|
||||
return;
|
||||
}
|
||||
|
||||
float pa = parent.a, pb = parent.b, pc = parent.c, pd = parent.d;
|
||||
worldX = pa * x + pb * y + parent.worldX;
|
||||
worldY = pc * x + pd * y + parent.worldY;
|
||||
|
||||
switch (inherit) {
|
||||
case normal -> {
|
||||
float rx = (rotation + shearX) * degRad;
|
||||
float ry = (rotation + 90 + shearY) * degRad;
|
||||
float la = cos(rx) * scaleX;
|
||||
float lb = cos(ry) * scaleY;
|
||||
float lc = sin(rx) * scaleX;
|
||||
float ld = sin(ry) * scaleY;
|
||||
a = pa * la + pb * lc;
|
||||
b = pa * lb + pb * ld;
|
||||
c = pc * la + pd * lc;
|
||||
d = pc * lb + pd * ld;
|
||||
return;
|
||||
}
|
||||
case onlyTranslation -> {
|
||||
float rx = (rotation + shearX) * degRad;
|
||||
float ry = (rotation + 90 + shearY) * degRad;
|
||||
a = cos(rx) * scaleX;
|
||||
b = cos(ry) * scaleY;
|
||||
c = sin(rx) * scaleX;
|
||||
d = sin(ry) * scaleY;
|
||||
}
|
||||
case noRotationOrReflection -> {
|
||||
float sx = 1 / skeleton.scaleX, sy = 1 / skeleton.scaleY;
|
||||
pa *= sx;
|
||||
pc *= sy;
|
||||
float s = pa * pa + pc * pc, prx;
|
||||
if (s > 0.0001f) {
|
||||
s = Math.abs(pa * pd * sy - pb * sx * pc) / s;
|
||||
pb = pc * s;
|
||||
pd = pa * s;
|
||||
prx = atan2Deg(pc, pa);
|
||||
} else {
|
||||
pa = 0;
|
||||
pc = 0;
|
||||
prx = 90 - atan2Deg(pd, pb);
|
||||
}
|
||||
float rx = (rotation + shearX - prx) * degRad;
|
||||
float ry = (rotation + shearY - prx + 90) * degRad;
|
||||
float la = cos(rx) * scaleX;
|
||||
float lb = cos(ry) * scaleY;
|
||||
float lc = sin(rx) * scaleX;
|
||||
float ld = sin(ry) * scaleY;
|
||||
a = pa * la - pb * lc;
|
||||
b = pa * lb - pb * ld;
|
||||
c = pc * la + pd * lc;
|
||||
d = pc * lb + pd * ld;
|
||||
}
|
||||
case noScale, noScaleOrReflection -> {
|
||||
rotation *= degRad;
|
||||
float cos = cos(rotation), sin = sin(rotation);
|
||||
float za = (pa * cos + pb * sin) / skeleton.scaleX;
|
||||
float zc = (pc * cos + pd * sin) / skeleton.scaleY;
|
||||
float s = (float)Math.sqrt(za * za + zc * zc);
|
||||
if (s > 0.00001f) s = 1 / s;
|
||||
za *= s;
|
||||
zc *= s;
|
||||
s = (float)Math.sqrt(za * za + zc * zc);
|
||||
if (inherit == Inherit.noScale && (pa * pd - pb * pc < 0) != (skeleton.scaleX < 0 != skeleton.scaleY < 0)) s = -s;
|
||||
rotation = PI / 2 + atan2(zc, za);
|
||||
float zb = cos(rotation) * s;
|
||||
float zd = sin(rotation) * s;
|
||||
shearX *= degRad;
|
||||
shearY = (90 + shearY) * degRad;
|
||||
float la = cos(shearX) * scaleX;
|
||||
float lb = cos(shearY) * scaleY;
|
||||
float lc = sin(shearX) * scaleX;
|
||||
float ld = sin(shearY) * scaleY;
|
||||
a = za * la + zb * lc;
|
||||
b = za * lb + zb * ld;
|
||||
c = zc * la + zd * lc;
|
||||
d = zc * lb + zd * ld;
|
||||
}
|
||||
}
|
||||
a *= skeleton.scaleX;
|
||||
b *= skeleton.scaleX;
|
||||
c *= skeleton.scaleY;
|
||||
d *= skeleton.scaleY;
|
||||
}
|
||||
|
||||
/** Computes the applied transform values from the world transform.
|
||||
* <p>
|
||||
* If the world transform is modified (by a constraint, {@link #rotateWorld(float)}, etc) then this method should be called so
|
||||
* the applied transform matches the world transform. The applied transform may be needed by other code (eg to apply another
|
||||
* constraint).
|
||||
* <p>
|
||||
* Some information is ambiguous in the world transform, such as -1,-1 scale versus 180 rotation. The applied transform after
|
||||
* calling this method is equivalent to the local transform used to compute the world transform, but may not be identical. */
|
||||
public void updateAppliedTransform () {
|
||||
BoneApplied parent = parentApplied;
|
||||
if (parent == null) {
|
||||
x = worldX - skeleton.x;
|
||||
y = worldY - skeleton.y;
|
||||
float a = this.a, b = this.b, c = this.c, d = this.d;
|
||||
rotation = atan2Deg(c, a);
|
||||
scaleX = (float)Math.sqrt(a * a + c * c);
|
||||
scaleY = (float)Math.sqrt(b * b + d * d);
|
||||
shearX = 0;
|
||||
shearY = atan2Deg(a * b + c * d, a * d - b * c);
|
||||
return;
|
||||
}
|
||||
|
||||
float pa = parent.a, pb = parent.b, pc = parent.c, pd = parent.d;
|
||||
float pid = 1 / (pa * pd - pb * pc);
|
||||
float ia = pd * pid, ib = pb * pid, ic = pc * pid, id = pa * pid;
|
||||
float dx = worldX - parent.worldX, dy = worldY - parent.worldY;
|
||||
x = (dx * ia - dy * ib);
|
||||
y = (dy * id - dx * ic);
|
||||
|
||||
float ra, rb, rc, rd;
|
||||
if (inherit == Inherit.onlyTranslation) {
|
||||
ra = a;
|
||||
rb = b;
|
||||
rc = c;
|
||||
rd = d;
|
||||
} else {
|
||||
switch (inherit) {
|
||||
case noRotationOrReflection -> {
|
||||
float s = Math.abs(pa * pd - pb * pc) / (pa * pa + pc * pc);
|
||||
pb = -pc * skeleton.scaleX * s / skeleton.scaleY;
|
||||
pd = pa * skeleton.scaleY * s / skeleton.scaleX;
|
||||
pid = 1 / (pa * pd - pb * pc);
|
||||
ia = pd * pid;
|
||||
ib = pb * pid;
|
||||
}
|
||||
case noScale, noScaleOrReflection -> {
|
||||
float r = rotation * degRad, cos = cos(r), sin = sin(r);
|
||||
pa = (pa * cos + pb * sin) / skeleton.scaleX;
|
||||
pc = (pc * cos + pd * sin) / skeleton.scaleY;
|
||||
float s = (float)Math.sqrt(pa * pa + pc * pc);
|
||||
if (s > 0.00001f) s = 1 / s;
|
||||
pa *= s;
|
||||
pc *= s;
|
||||
s = (float)Math.sqrt(pa * pa + pc * pc);
|
||||
if (inherit == Inherit.noScale && pid < 0 != (skeleton.scaleX < 0 != skeleton.scaleY < 0)) s = -s;
|
||||
r = PI / 2 + atan2(pc, pa);
|
||||
pb = cos(r) * s;
|
||||
pd = sin(r) * s;
|
||||
pid = 1 / (pa * pd - pb * pc);
|
||||
ia = pd * pid;
|
||||
ib = pb * pid;
|
||||
ic = pc * pid;
|
||||
id = pa * pid;
|
||||
}
|
||||
}
|
||||
ra = ia * a - ib * c;
|
||||
rb = ia * b - ib * d;
|
||||
rc = id * c - ic * a;
|
||||
rd = id * d - ic * b;
|
||||
}
|
||||
|
||||
shearX = 0;
|
||||
scaleX = (float)Math.sqrt(ra * ra + rc * rc);
|
||||
if (scaleX > 0.0001f) {
|
||||
float det = ra * rd - rb * rc;
|
||||
scaleY = det / scaleX;
|
||||
shearY = -atan2Deg(ra * rb + rc * rd, det);
|
||||
rotation = atan2Deg(rc, ra);
|
||||
} else {
|
||||
scaleX = 0;
|
||||
scaleY = (float)Math.sqrt(rb * rb + rd * rd);
|
||||
shearY = 0;
|
||||
rotation = 90 - atan2Deg(rd, rb);
|
||||
}
|
||||
}
|
||||
|
||||
/** Part of the world transform matrix for the X axis. If changed, {@link #updateAppliedTransform()} should be called. */
|
||||
public float getA () {
|
||||
return a;
|
||||
}
|
||||
|
||||
public void setA (float a) {
|
||||
this.a = a;
|
||||
}
|
||||
|
||||
/** Part of the world transform matrix for the Y axis. If changed, {@link #updateAppliedTransform()} should be called. */
|
||||
public float getB () {
|
||||
return b;
|
||||
}
|
||||
|
||||
public void setB (float b) {
|
||||
this.b = b;
|
||||
}
|
||||
|
||||
/** Part of the world transform matrix for the X axis. If changed, {@link #updateAppliedTransform()} should be called. */
|
||||
public float getC () {
|
||||
return c;
|
||||
}
|
||||
|
||||
public void setC (float c) {
|
||||
this.c = c;
|
||||
}
|
||||
|
||||
/** Part of the world transform matrix for the Y axis. If changed, {@link #updateAppliedTransform()} should be called. */
|
||||
public float getD () {
|
||||
return d;
|
||||
}
|
||||
|
||||
public void setD (float d) {
|
||||
this.d = d;
|
||||
}
|
||||
|
||||
/** The world X position. If changed, {@link #updateAppliedTransform()} should be called. */
|
||||
public float getWorldX () {
|
||||
return worldX;
|
||||
}
|
||||
|
||||
public void setWorldX (float worldX) {
|
||||
this.worldX = worldX;
|
||||
}
|
||||
|
||||
/** The world Y position. If changed, {@link #updateAppliedTransform()} should be called. */
|
||||
public float getWorldY () {
|
||||
return worldY;
|
||||
}
|
||||
|
||||
public void setWorldY (float worldY) {
|
||||
this.worldY = worldY;
|
||||
}
|
||||
|
||||
/** The world rotation for the X axis, calculated using {@link #a} and {@link #c}. */
|
||||
public float getWorldRotationX () {
|
||||
return atan2Deg(c, a);
|
||||
}
|
||||
|
||||
/** The world rotation for the Y axis, calculated using {@link #b} and {@link #d}. */
|
||||
public float getWorldRotationY () {
|
||||
return atan2Deg(d, b);
|
||||
}
|
||||
|
||||
/** The magnitude (always positive) of the world scale X, calculated using {@link #a} and {@link #c}. */
|
||||
public float getWorldScaleX () {
|
||||
return (float)Math.sqrt(a * a + c * c);
|
||||
}
|
||||
|
||||
/** The magnitude (always positive) of the world scale Y, calculated using {@link #b} and {@link #d}. */
|
||||
public float getWorldScaleY () {
|
||||
return (float)Math.sqrt(b * b + d * d);
|
||||
}
|
||||
|
||||
public Matrix3 getWorldTransform (Matrix3 worldTransform) {
|
||||
if (worldTransform == null) throw new IllegalArgumentException("worldTransform cannot be null.");
|
||||
float[] val = worldTransform.val;
|
||||
val[M00] = a;
|
||||
val[M01] = b;
|
||||
val[M10] = c;
|
||||
val[M11] = d;
|
||||
val[M02] = worldX;
|
||||
val[M12] = worldY;
|
||||
val[M20] = 0;
|
||||
val[M21] = 0;
|
||||
val[M22] = 1;
|
||||
return worldTransform;
|
||||
}
|
||||
|
||||
/** Transforms a point from world coordinates to the bone's local coordinates. */
|
||||
public Vector2 worldToLocal (Vector2 world) {
|
||||
if (world == null) throw new IllegalArgumentException("world cannot be null.");
|
||||
float det = a * d - b * c;
|
||||
float x = world.x - worldX, y = world.y - worldY;
|
||||
world.x = (x * d - y * b) / det;
|
||||
world.y = (y * a - x * c) / det;
|
||||
return world;
|
||||
}
|
||||
|
||||
/** Transforms a point from the bone's local coordinates to world coordinates. */
|
||||
public Vector2 localToWorld (Vector2 local) {
|
||||
if (local == null) throw new IllegalArgumentException("local cannot be null.");
|
||||
float x = local.x, y = local.y;
|
||||
local.x = x * a + y * b + worldX;
|
||||
local.y = x * c + y * d + worldY;
|
||||
return local;
|
||||
}
|
||||
|
||||
/** Transforms a point from world coordinates to the parent bone's local coordinates. */
|
||||
public Vector2 worldToParent (Vector2 world) {
|
||||
if (world == null) throw new IllegalArgumentException("world cannot be null.");
|
||||
return parent == null ? world : parentApplied.worldToLocal(world);
|
||||
}
|
||||
|
||||
/** Transforms a point from the parent bone's coordinates to world coordinates. */
|
||||
public Vector2 parentToWorld (Vector2 world) {
|
||||
if (world == null) throw new IllegalArgumentException("world cannot be null.");
|
||||
return parent == null ? world : parentApplied.localToWorld(world);
|
||||
}
|
||||
|
||||
/** Transforms a world rotation to a local rotation. */
|
||||
public float worldToLocalRotation (float worldRotation) {
|
||||
worldRotation *= degRad;
|
||||
float sin = sin(worldRotation), cos = cos(worldRotation);
|
||||
return atan2Deg(a * sin - c * cos, d * cos - b * sin) + rotation - shearX;
|
||||
}
|
||||
|
||||
/** Transforms a local rotation to a world rotation. */
|
||||
public float localToWorldRotation (float localRotation) {
|
||||
localRotation = (localRotation - rotation - shearX) * degRad;
|
||||
float sin = sin(localRotation), cos = cos(localRotation);
|
||||
return atan2Deg(cos * c + sin * d, cos * a + sin * b);
|
||||
}
|
||||
|
||||
/** Rotates the world transform the specified amount.
|
||||
* <p>
|
||||
* After changes are made to the world transform, {@link #updateAppliedTransform()} should be called and
|
||||
* {@link #update(Physics)} will need to be called on any child bones, recursively. */
|
||||
public void rotateWorld (float degrees) {
|
||||
degrees *= degRad;
|
||||
float sin = sin(degrees), cos = cos(degrees);
|
||||
float ra = a, rb = b;
|
||||
a = cos * ra - sin * c;
|
||||
b = cos * rb - sin * d;
|
||||
c = sin * ra + cos * c;
|
||||
d = sin * rb + cos * d;
|
||||
}
|
||||
}
|
||||
@ -35,7 +35,7 @@ import com.esotericsoftware.spine.AnimationState.AnimationStateListener;
|
||||
/** Stores the current pose values for an {@link Event}.
|
||||
* <p>
|
||||
* See Timeline
|
||||
* {@link Timeline#apply(Skeleton, float, float, com.badlogic.gdx.utils.Array, float, com.esotericsoftware.spine.Animation.MixBlend, com.esotericsoftware.spine.Animation.MixDirection)},
|
||||
* {@link Timeline#apply(Skeleton, float, float, com.badlogic.gdx.utils.Array, float, com.esotericsoftware.spine.Animation.MixBlend, com.esotericsoftware.spine.Animation.MixDirection, boolean)},
|
||||
* AnimationStateListener {@link AnimationStateListener#event(com.esotericsoftware.spine.AnimationState.TrackEntry, Event)}, and
|
||||
* <a href="https://esotericsoftware.com/spine-events">Events</a> in the Spine User Guide. */
|
||||
public class Event {
|
||||
|
||||
@ -42,13 +42,20 @@ import com.esotericsoftware.spine.Skeleton.Physics;
|
||||
* See <a href="https://esotericsoftware.com/spine-ik-constraints">IK constraints</a> in the Spine User Guide. */
|
||||
public class IkConstraint implements Updatable {
|
||||
final IkConstraintData data;
|
||||
final Array<Bone> bones;
|
||||
Bone target;
|
||||
final Array<BoneApplied> bones;
|
||||
BoneApplied target;
|
||||
IkConstraint applied;
|
||||
boolean active;
|
||||
|
||||
int bendDirection;
|
||||
boolean compress, stretch;
|
||||
float mix = 1, softness;
|
||||
|
||||
boolean active;
|
||||
private IkConstraint (IkConstraintData data, Array<BoneApplied> bones, BoneApplied target) {
|
||||
this.data = data;
|
||||
this.bones = bones;
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
public IkConstraint (IkConstraintData data, Skeleton skeleton) {
|
||||
if (data == null) throw new IllegalArgumentException("data cannot be null.");
|
||||
@ -57,9 +64,11 @@ public class IkConstraint implements Updatable {
|
||||
|
||||
bones = new Array(data.bones.size);
|
||||
for (BoneData boneData : data.bones)
|
||||
bones.add(skeleton.bones.get(boneData.index));
|
||||
bones.add(skeleton.bones.get(boneData.index).applied);
|
||||
|
||||
target = skeleton.bones.get(data.target.index);
|
||||
target = skeleton.bones.get(data.target.index).applied;
|
||||
|
||||
applied = new IkConstraint(data, bones, target);
|
||||
|
||||
setToSetupPose();
|
||||
}
|
||||
@ -82,17 +91,18 @@ public class IkConstraint implements Updatable {
|
||||
/** Applies the constraint to the constrained bones. */
|
||||
public void update (Physics physics) {
|
||||
if (mix == 0) return;
|
||||
Bone target = this.target;
|
||||
BoneApplied target = this.target;
|
||||
Object[] bones = this.bones.items;
|
||||
switch (this.bones.size) {
|
||||
case 1 -> apply((Bone)bones[0], target.worldX, target.worldY, compress, stretch, data.uniform, mix);
|
||||
case 1 -> apply((BoneApplied)bones[0], target.worldX, target.worldY, compress, stretch, data.uniform, mix);
|
||||
case 2 -> //
|
||||
apply((Bone)bones[0], (Bone)bones[1], target.worldX, target.worldY, bendDirection, stretch, data.uniform, softness, mix);
|
||||
apply((BoneApplied)bones[0], (BoneApplied)bones[1], target.worldX, target.worldY, bendDirection, stretch, data.uniform,
|
||||
softness, mix);
|
||||
}
|
||||
}
|
||||
|
||||
/** The bones that will be modified by this IK constraint. */
|
||||
public Array<Bone> getBones () {
|
||||
/** The 1 or 2 bones that will be modified by this IK constraint. */
|
||||
public Array<BoneApplied> getBones () {
|
||||
return bones;
|
||||
}
|
||||
|
||||
@ -101,7 +111,7 @@ public class IkConstraint implements Updatable {
|
||||
return target;
|
||||
}
|
||||
|
||||
public void setTarget (Bone target) {
|
||||
public void setTarget (BoneApplied target) {
|
||||
if (target == null) throw new IllegalArgumentException("target cannot be null.");
|
||||
this.target = target;
|
||||
}
|
||||
@ -171,12 +181,12 @@ public class IkConstraint implements Updatable {
|
||||
}
|
||||
|
||||
/** Applies 1 bone IK. The target is specified in the world coordinate system. */
|
||||
static public void apply (Bone bone, float targetX, float targetY, boolean compress, boolean stretch, boolean uniform,
|
||||
static public void apply (BoneApplied bone, float targetX, float targetY, boolean compress, boolean stretch, boolean uniform,
|
||||
float alpha) {
|
||||
if (bone == null) throw new IllegalArgumentException("bone cannot be null.");
|
||||
Bone p = bone.parent;
|
||||
BoneApplied p = bone.parentApplied;
|
||||
float pa = p.a, pb = p.b, pc = p.c, pd = p.d;
|
||||
float rotationIK = -bone.ashearX - bone.arotation, tx, ty;
|
||||
float rotationIK = -bone.shearX - bone.rotation, tx, ty;
|
||||
switch (bone.inherit) {
|
||||
case onlyTranslation:
|
||||
tx = (targetX - bone.worldX) * Math.signum(bone.skeleton.scaleX);
|
||||
@ -197,17 +207,17 @@ public class IkConstraint implements Updatable {
|
||||
tx = 0;
|
||||
ty = 0;
|
||||
} else {
|
||||
tx = (x * pd - y * pb) / d - bone.ax;
|
||||
ty = (y * pa - x * pc) / d - bone.ay;
|
||||
tx = (x * pd - y * pb) / d - bone.x;
|
||||
ty = (y * pa - x * pc) / d - bone.y;
|
||||
}
|
||||
}
|
||||
rotationIK += atan2Deg(ty, tx);
|
||||
if (bone.ascaleX < 0) rotationIK += 180;
|
||||
if (bone.scaleX < 0) rotationIK += 180;
|
||||
if (rotationIK > 180)
|
||||
rotationIK -= 360;
|
||||
else if (rotationIK < -180) //
|
||||
rotationIK += 360;
|
||||
float sx = bone.ascaleX, sy = bone.ascaleY;
|
||||
bone.rotation += rotationIK * alpha;
|
||||
if (compress || stretch) {
|
||||
switch (bone.inherit) {
|
||||
case noScale, noScaleOrReflection -> {
|
||||
@ -215,27 +225,27 @@ public class IkConstraint implements Updatable {
|
||||
ty = targetY - bone.worldY;
|
||||
}
|
||||
}
|
||||
float b = bone.data.length * sx;
|
||||
float b = bone.data.length * bone.scaleX;
|
||||
if (b > 0.0001f) {
|
||||
float dd = tx * tx + ty * ty;
|
||||
if ((compress && dd < b * b) || (stretch && dd > b * b)) {
|
||||
float s = ((float)Math.sqrt(dd) / b - 1) * alpha + 1;
|
||||
sx *= s;
|
||||
if (uniform) sy *= s;
|
||||
bone.scaleX *= s;
|
||||
if (uniform) bone.scaleY *= s;
|
||||
}
|
||||
}
|
||||
}
|
||||
bone.updateWorldTransform(bone.ax, bone.ay, bone.arotation + rotationIK * alpha, sx, sy, bone.ashearX, bone.ashearY);
|
||||
bone.updateWorldTransform();
|
||||
}
|
||||
|
||||
/** Applies 2 bone IK. The target is specified in the world coordinate system.
|
||||
* @param child A direct descendant of the parent bone. */
|
||||
static public void apply (Bone parent, Bone child, float targetX, float targetY, int bendDir, boolean stretch, boolean uniform,
|
||||
float softness, float alpha) {
|
||||
static public void apply (BoneApplied parent, BoneApplied child, float targetX, float targetY, int bendDir, boolean stretch,
|
||||
boolean uniform, float softness, float alpha) {
|
||||
if (parent == null) throw new IllegalArgumentException("parent cannot be null.");
|
||||
if (child == null) throw new IllegalArgumentException("child cannot be null.");
|
||||
if (parent.inherit != Inherit.normal || child.inherit != Inherit.normal) return;
|
||||
float px = parent.ax, py = parent.ay, psx = parent.ascaleX, psy = parent.ascaleY, sx = psx, sy = psy, csx = child.ascaleX;
|
||||
float px = parent.x, py = parent.y, psx = parent.scaleX, psy = parent.scaleY, csx = child.scaleX;
|
||||
int os1, os2, s2;
|
||||
if (psx < 0) {
|
||||
psx = -psx;
|
||||
@ -254,18 +264,17 @@ public class IkConstraint implements Updatable {
|
||||
os2 = 180;
|
||||
} else
|
||||
os2 = 0;
|
||||
float cx = child.ax, cy, cwx, cwy, a = parent.a, b = parent.b, c = parent.c, d = parent.d;
|
||||
float cwx, cwy, a = parent.a, b = parent.b, c = parent.c, d = parent.d;
|
||||
boolean u = Math.abs(psx - psy) <= 0.0001f;
|
||||
if (!u || stretch) {
|
||||
cy = 0;
|
||||
cwx = a * cx + parent.worldX;
|
||||
cwy = c * cx + parent.worldY;
|
||||
child.y = 0;
|
||||
cwx = a * child.x + parent.worldX;
|
||||
cwy = c * child.x + parent.worldY;
|
||||
} else {
|
||||
cy = child.ay;
|
||||
cwx = a * cx + b * cy + parent.worldX;
|
||||
cwy = c * cx + d * cy + parent.worldY;
|
||||
cwx = a * child.x + b * child.y + parent.worldX;
|
||||
cwy = c * child.x + d * child.y + parent.worldY;
|
||||
}
|
||||
Bone pp = parent.parent;
|
||||
BoneApplied pp = parent.parentApplied;
|
||||
a = pp.a;
|
||||
b = pp.b;
|
||||
c = pp.c;
|
||||
@ -276,7 +285,8 @@ public class IkConstraint implements Updatable {
|
||||
float l1 = (float)Math.sqrt(dx * dx + dy * dy), l2 = child.data.length * csx, a1, a2;
|
||||
if (l1 < 0.0001f) {
|
||||
apply(parent, targetX, targetY, false, stretch, false, alpha);
|
||||
child.updateWorldTransform(cx, cy, 0, child.ascaleX, child.ascaleY, child.ashearX, child.ashearY);
|
||||
child.rotation = 0;
|
||||
child.updateWorldTransform();
|
||||
return;
|
||||
}
|
||||
x = targetX - pp.worldX;
|
||||
@ -306,8 +316,8 @@ public class IkConstraint implements Updatable {
|
||||
a2 = 0;
|
||||
if (stretch) {
|
||||
a = ((float)Math.sqrt(dd) / (l1 + l2) - 1) * alpha + 1;
|
||||
sx *= a;
|
||||
if (uniform) sy *= a;
|
||||
parent.scaleX *= a;
|
||||
if (uniform) parent.scaleY *= a;
|
||||
}
|
||||
} else
|
||||
a2 = (float)Math.acos(cos) * bendDir;
|
||||
@ -364,20 +374,20 @@ public class IkConstraint implements Updatable {
|
||||
a2 = maxAngle * bendDir;
|
||||
}
|
||||
}
|
||||
float os = atan2(cy, cx) * s2;
|
||||
float rotation = parent.arotation;
|
||||
a1 = (a1 - os) * radDeg + os1 - rotation;
|
||||
float os = atan2(child.y, child.x) * s2;
|
||||
a1 = (a1 - os) * radDeg + os1 - parent.rotation;
|
||||
if (a1 > 180)
|
||||
a1 -= 360;
|
||||
else if (a1 < -180) //
|
||||
a1 += 360;
|
||||
parent.updateWorldTransform(px, py, rotation + a1 * alpha, sx, sy, 0, 0);
|
||||
rotation = child.arotation;
|
||||
a2 = ((a2 + os) * radDeg - child.ashearX) * s2 + os2 - rotation;
|
||||
parent.rotation += a1 * alpha;
|
||||
parent.updateWorldTransform();
|
||||
a2 = ((a2 + os) * radDeg - child.shearX) * s2 + os2 - child.rotation;
|
||||
if (a2 > 180)
|
||||
a2 -= 360;
|
||||
else if (a2 < -180) //
|
||||
a2 += 360;
|
||||
child.updateWorldTransform(cx, cy, rotation + a2 * alpha, child.ascaleX, child.ascaleY, child.ashearX, child.ashearY);
|
||||
child.rotation += a2 * alpha;
|
||||
child.updateWorldTransform();
|
||||
}
|
||||
}
|
||||
|
||||
@ -35,6 +35,7 @@ import java.util.Arrays;
|
||||
|
||||
import com.badlogic.gdx.utils.Array;
|
||||
import com.badlogic.gdx.utils.FloatArray;
|
||||
import com.badlogic.gdx.utils.Null;
|
||||
|
||||
import com.esotericsoftware.spine.PathConstraintData.PositionMode;
|
||||
import com.esotericsoftware.spine.PathConstraintData.RotateMode;
|
||||
@ -51,16 +52,23 @@ public class PathConstraint implements Updatable {
|
||||
static final float epsilon = 0.00001f;
|
||||
|
||||
final PathConstraintData data;
|
||||
final Array<Bone> bones;
|
||||
final Array<BoneApplied> bones;
|
||||
Slot slot;
|
||||
float position, spacing, mixRotate, mixX, mixY;
|
||||
|
||||
PathConstraint applied;
|
||||
boolean active;
|
||||
|
||||
float position, spacing, mixRotate, mixX, mixY;
|
||||
|
||||
private final FloatArray spaces = new FloatArray(), positions = new FloatArray();
|
||||
private final FloatArray world = new FloatArray(), curves = new FloatArray(), lengths = new FloatArray();
|
||||
private final float[] segments = new float[10];
|
||||
|
||||
public PathConstraint (PathConstraintData data, Array<BoneApplied> bones, Slot slot) {
|
||||
this.data = data;
|
||||
this.bones = bones;
|
||||
this.slot = slot;
|
||||
}
|
||||
|
||||
public PathConstraint (PathConstraintData data, Skeleton skeleton) {
|
||||
if (data == null) throw new IllegalArgumentException("data cannot be null.");
|
||||
if (skeleton == null) throw new IllegalArgumentException("skeleton cannot be null.");
|
||||
@ -68,10 +76,12 @@ public class PathConstraint implements Updatable {
|
||||
|
||||
bones = new Array(data.bones.size);
|
||||
for (BoneData boneData : data.bones)
|
||||
bones.add(skeleton.bones.get(boneData.index));
|
||||
bones.add(skeleton.bones.get(boneData.index).applied);
|
||||
|
||||
slot = skeleton.slots.get(data.slot.index);
|
||||
|
||||
applied = new PathConstraint(data, bones, slot);
|
||||
|
||||
setToSetupPose();
|
||||
}
|
||||
|
||||
@ -108,7 +118,7 @@ public class PathConstraint implements Updatable {
|
||||
case percent -> {
|
||||
if (scale) {
|
||||
for (int i = 0, n = spacesCount - 1; i < n; i++) {
|
||||
var bone = (Bone)bones[i];
|
||||
var bone = (BoneApplied)bones[i];
|
||||
float setupLength = bone.data.length;
|
||||
float x = setupLength * bone.a, y = setupLength * bone.c;
|
||||
lengths[i] = (float)Math.sqrt(x * x + y * y);
|
||||
@ -119,7 +129,7 @@ public class PathConstraint implements Updatable {
|
||||
case proportional -> {
|
||||
float sum = 0;
|
||||
for (int i = 0, n = spacesCount - 1; i < n;) {
|
||||
var bone = (Bone)bones[i];
|
||||
var bone = (BoneApplied)bones[i];
|
||||
float setupLength = bone.data.length;
|
||||
if (setupLength < epsilon) {
|
||||
if (scale) lengths[i] = 0;
|
||||
@ -141,7 +151,7 @@ public class PathConstraint implements Updatable {
|
||||
default -> {
|
||||
boolean lengthSpacing = data.spacingMode == SpacingMode.length;
|
||||
for (int i = 0, n = spacesCount - 1; i < n;) {
|
||||
var bone = (Bone)bones[i];
|
||||
var bone = (BoneApplied)bones[i];
|
||||
float setupLength = bone.data.length;
|
||||
if (setupLength < epsilon) {
|
||||
if (scale) lengths[i] = 0;
|
||||
@ -163,11 +173,11 @@ public class PathConstraint implements Updatable {
|
||||
tip = data.rotateMode == RotateMode.chain;
|
||||
else {
|
||||
tip = false;
|
||||
Bone p = slot.bone;
|
||||
BoneApplied p = slot.bone.applied;
|
||||
offsetRotation *= p.a * p.d - p.b * p.c > 0 ? degRad : -degRad;
|
||||
}
|
||||
for (int i = 0, p = 3; i < boneCount; i++, p += 3) {
|
||||
var bone = (Bone)bones[i];
|
||||
var bone = (BoneApplied)bones[i];
|
||||
bone.worldX += (boneX - bone.worldX) * mixX;
|
||||
bone.worldY += (boneY - bone.worldY) * mixY;
|
||||
float x = positions[p], y = positions[p + 1], dx = x - boneX, dy = y - boneY;
|
||||
@ -518,7 +528,7 @@ public class PathConstraint implements Updatable {
|
||||
}
|
||||
|
||||
/** The bones that will be modified by this path constraint. */
|
||||
public Array<Bone> getBones () {
|
||||
public Array<BoneApplied> getBones () {
|
||||
return bones;
|
||||
}
|
||||
|
||||
|
||||
@ -38,7 +38,11 @@ import com.esotericsoftware.spine.Skeleton.Physics;
|
||||
* See <a href="https://esotericsoftware.com/spine-physics-constraints">Physics constraints</a> in the Spine User Guide. */
|
||||
public class PhysicsConstraint implements Updatable {
|
||||
final PhysicsConstraintData data;
|
||||
Bone bone;
|
||||
final Skeleton skeleton;
|
||||
BoneApplied bone;
|
||||
PhysicsConstraint applied;
|
||||
boolean active;
|
||||
|
||||
float inertia, strength, damping, massInverse, wind, gravity, mix;
|
||||
|
||||
boolean reset = true;
|
||||
@ -47,19 +51,23 @@ public class PhysicsConstraint implements Updatable {
|
||||
float yOffset, yVelocity;
|
||||
float rotateOffset, rotateVelocity;
|
||||
float scaleOffset, scaleVelocity;
|
||||
|
||||
boolean active;
|
||||
|
||||
final Skeleton skeleton;
|
||||
float remaining, lastTime;
|
||||
|
||||
private PhysicsConstraint (PhysicsConstraintData data, Skeleton skeleton, BoneApplied bone) {
|
||||
this.data = data;
|
||||
this.skeleton = skeleton;
|
||||
this.bone = bone;
|
||||
}
|
||||
|
||||
public PhysicsConstraint (PhysicsConstraintData data, Skeleton skeleton) {
|
||||
if (data == null) throw new IllegalArgumentException("data cannot be null.");
|
||||
if (skeleton == null) throw new IllegalArgumentException("skeleton cannot be null.");
|
||||
this.data = data;
|
||||
this.skeleton = skeleton;
|
||||
|
||||
bone = skeleton.bones.get(data.bone.index);
|
||||
bone = skeleton.bones.get(data.bone.index).applied;
|
||||
|
||||
applied = new PhysicsConstraint(data, skeleton, bone);
|
||||
|
||||
setToSetupPose();
|
||||
}
|
||||
@ -118,7 +126,7 @@ public class PhysicsConstraint implements Updatable {
|
||||
if (mix == 0) return;
|
||||
|
||||
boolean x = data.x > 0, y = data.y > 0, rotateOrShearX = data.rotate > 0 || data.shearX > 0, scaleX = data.scaleX > 0;
|
||||
Bone bone = this.bone;
|
||||
BoneApplied bone = this.bone;
|
||||
float l = bone.data.length;
|
||||
|
||||
switch (physics) {
|
||||
@ -278,11 +286,11 @@ public class PhysicsConstraint implements Updatable {
|
||||
}
|
||||
|
||||
/** The bone constrained by this physics constraint. */
|
||||
public Bone getBone () {
|
||||
public BoneApplied getBone () {
|
||||
return bone;
|
||||
}
|
||||
|
||||
public void setBone (Bone bone) {
|
||||
public void setBone (BoneApplied bone) {
|
||||
this.bone = bone;
|
||||
}
|
||||
|
||||
|
||||
@ -37,6 +37,8 @@ import com.badlogic.gdx.utils.Array;
|
||||
import com.badlogic.gdx.utils.FloatArray;
|
||||
import com.badlogic.gdx.utils.Null;
|
||||
|
||||
import com.esotericsoftware.spine.Animation.BoneTimeline;
|
||||
import com.esotericsoftware.spine.Animation.Timeline;
|
||||
import com.esotericsoftware.spine.Skin.SkinEntry;
|
||||
import com.esotericsoftware.spine.attachments.Attachment;
|
||||
import com.esotericsoftware.spine.attachments.ClippingAttachment;
|
||||
@ -55,6 +57,7 @@ public class Skeleton {
|
||||
final Array<Bone> bones;
|
||||
final Array<Slot> slots;
|
||||
Array<Slot> drawOrder;
|
||||
final Array<Slider> sliders;
|
||||
final Array<IkConstraint> ikConstraints;
|
||||
final Array<TransformConstraint> transformConstraints;
|
||||
final Array<PathConstraint> pathConstraints;
|
||||
@ -85,27 +88,30 @@ public class Skeleton {
|
||||
slots = new Array(data.slots.size);
|
||||
drawOrder = new Array(data.slots.size);
|
||||
for (SlotData slotData : data.slots) {
|
||||
var bone = (Bone)bones[slotData.boneData.index];
|
||||
var slot = new Slot(slotData, bone);
|
||||
var slot = new Slot(slotData, this);
|
||||
slots.add(slot);
|
||||
drawOrder.add(slot);
|
||||
}
|
||||
|
||||
sliders = new Array(data.sliders.size);
|
||||
for (SliderData constraint : data.sliders)
|
||||
sliders.add(new Slider(constraint, this));
|
||||
|
||||
ikConstraints = new Array(data.ikConstraints.size);
|
||||
for (IkConstraintData ikConstraintData : data.ikConstraints)
|
||||
ikConstraints.add(new IkConstraint(ikConstraintData, this));
|
||||
for (IkConstraintData constraint : data.ikConstraints)
|
||||
ikConstraints.add(new IkConstraint(constraint, this));
|
||||
|
||||
transformConstraints = new Array(data.transformConstraints.size);
|
||||
for (TransformConstraintData transformConstraintData : data.transformConstraints)
|
||||
transformConstraints.add(new TransformConstraint(transformConstraintData, this));
|
||||
for (TransformConstraintData constraint : data.transformConstraints)
|
||||
transformConstraints.add(new TransformConstraint(constraint, this));
|
||||
|
||||
pathConstraints = new Array(data.pathConstraints.size);
|
||||
for (PathConstraintData pathConstraintData : data.pathConstraints)
|
||||
pathConstraints.add(new PathConstraint(pathConstraintData, this));
|
||||
for (PathConstraintData constraint : data.pathConstraints)
|
||||
pathConstraints.add(new PathConstraint(constraint, this));
|
||||
|
||||
physicsConstraints = new Array(data.physicsConstraints.size);
|
||||
for (PhysicsConstraintData physicsConstraintData : data.physicsConstraints)
|
||||
physicsConstraints.add(new PhysicsConstraint(physicsConstraintData, this));
|
||||
for (PhysicsConstraintData constraint : data.physicsConstraints)
|
||||
physicsConstraints.add(new PhysicsConstraint(constraint, this));
|
||||
|
||||
color = new Color(1, 1, 1, 1);
|
||||
|
||||
@ -140,21 +146,25 @@ public class Skeleton {
|
||||
for (Slot slot : skeleton.drawOrder)
|
||||
drawOrder.add(slots.get(slot.data.index));
|
||||
|
||||
sliders = new Array(skeleton.sliders.size);
|
||||
for (Slider constraint : skeleton.sliders)
|
||||
sliders.add(new Slider(constraint, skeleton));
|
||||
|
||||
ikConstraints = new Array(skeleton.ikConstraints.size);
|
||||
for (IkConstraint ikConstraint : skeleton.ikConstraints)
|
||||
ikConstraints.add(new IkConstraint(ikConstraint, skeleton));
|
||||
for (IkConstraint constraint : skeleton.ikConstraints)
|
||||
ikConstraints.add(new IkConstraint(constraint, skeleton));
|
||||
|
||||
transformConstraints = new Array(skeleton.transformConstraints.size);
|
||||
for (TransformConstraint transformConstraint : skeleton.transformConstraints)
|
||||
transformConstraints.add(new TransformConstraint(transformConstraint, skeleton));
|
||||
for (TransformConstraint constraint : skeleton.transformConstraints)
|
||||
transformConstraints.add(new TransformConstraint(constraint, skeleton));
|
||||
|
||||
pathConstraints = new Array(skeleton.pathConstraints.size);
|
||||
for (PathConstraint pathConstraint : skeleton.pathConstraints)
|
||||
pathConstraints.add(new PathConstraint(pathConstraint, skeleton));
|
||||
for (PathConstraint constraint : skeleton.pathConstraints)
|
||||
pathConstraints.add(new PathConstraint(constraint, skeleton));
|
||||
|
||||
physicsConstraints = new Array(skeleton.physicsConstraints.size);
|
||||
for (PhysicsConstraint physicsConstraint : skeleton.physicsConstraints)
|
||||
physicsConstraints.add(new PhysicsConstraint(physicsConstraint, skeleton));
|
||||
for (PhysicsConstraint constraint : skeleton.physicsConstraints)
|
||||
physicsConstraints.add(new PhysicsConstraint(constraint, skeleton));
|
||||
|
||||
skin = skeleton.skin;
|
||||
color = new Color(skeleton.color);
|
||||
@ -192,13 +202,21 @@ public class Skeleton {
|
||||
}
|
||||
}
|
||||
|
||||
int ikCount = ikConstraints.size, transformCount = transformConstraints.size, pathCount = pathConstraints.size,
|
||||
physicsCount = physicsConstraints.size;
|
||||
Object[] ikConstraints = this.ikConstraints.items, transformConstraints = this.transformConstraints.items,
|
||||
pathConstraints = this.pathConstraints.items, physicsConstraints = this.physicsConstraints.items;
|
||||
int sliderCount = sliders.size, ikCount = ikConstraints.size, transformCount = transformConstraints.size,
|
||||
pathCount = pathConstraints.size, physicsCount = physicsConstraints.size;
|
||||
Object[] sliders = this.sliders.items, ikConstraints = this.ikConstraints.items,
|
||||
transformConstraints = this.transformConstraints.items, pathConstraints = this.pathConstraints.items,
|
||||
physicsConstraints = this.physicsConstraints.items;
|
||||
int constraintCount = ikCount + transformCount + pathCount + physicsCount;
|
||||
outer:
|
||||
for (int i = 0; i < constraintCount; i++) {
|
||||
for (int ii = 0; ii < sliderCount; ii++) {
|
||||
var constraint = (Slider)sliders[ii];
|
||||
if (constraint.data.order == i) {
|
||||
sortSlider(constraint);
|
||||
continue outer;
|
||||
}
|
||||
}
|
||||
for (int ii = 0; ii < ikCount; ii++) {
|
||||
var constraint = (IkConstraint)ikConstraints[ii];
|
||||
if (constraint.data.order == i) {
|
||||
@ -233,21 +251,42 @@ public class Skeleton {
|
||||
sortBone((Bone)bones[i]);
|
||||
}
|
||||
|
||||
private void sortSlider (Slider constraint) {
|
||||
constraint.active = !constraint.data.skinRequired || (skin != null && skin.constraints.contains(constraint.data, true));
|
||||
if (!constraint.active) return;
|
||||
|
||||
Object[] bones = this.bones.items;
|
||||
for (Timeline timeline : constraint.animation.timelines)
|
||||
if (timeline instanceof BoneTimeline boneTimeline) sortBone((Bone)bones[boneTimeline.getBoneIndex()]);
|
||||
|
||||
updateCache.add(constraint);
|
||||
|
||||
for (Timeline timeline : constraint.animation.timelines) {
|
||||
if (timeline instanceof BoneTimeline boneTimeline) {
|
||||
var bone = (Bone)bones[boneTimeline.getBoneIndex()];
|
||||
sortReset(bone.children);
|
||||
bone.sorted = false;
|
||||
}
|
||||
}
|
||||
for (Timeline timeline : constraint.animation.timelines)
|
||||
if (timeline instanceof BoneTimeline boneTimeline) sortBone((Bone)bones[boneTimeline.getBoneIndex()]);
|
||||
}
|
||||
|
||||
private void sortIkConstraint (IkConstraint constraint) {
|
||||
constraint.active = constraint.target.active
|
||||
&& (!constraint.data.skinRequired || (skin != null && skin.constraints.contains(constraint.data, true)));
|
||||
if (!constraint.active) return;
|
||||
|
||||
sortBone(constraint.target);
|
||||
sortBone(constraint.target.pose);
|
||||
|
||||
Array<Bone> constrained = constraint.bones;
|
||||
Bone parent = constrained.first();
|
||||
Array<BoneApplied> constrained = constraint.bones;
|
||||
Bone parent = constrained.first().pose;
|
||||
sortBone(parent);
|
||||
if (constrained.size == 1) {
|
||||
updateCache.add(constraint);
|
||||
sortReset(parent.children);
|
||||
} else {
|
||||
Bone child = constrained.peek();
|
||||
Bone child = constrained.peek().pose;
|
||||
sortBone(child);
|
||||
|
||||
updateCache.add(constraint);
|
||||
@ -262,27 +301,27 @@ public class Skeleton {
|
||||
&& (!constraint.data.skinRequired || (skin != null && skin.constraints.contains(constraint.data, true)));
|
||||
if (!constraint.active) return;
|
||||
|
||||
sortBone(constraint.source);
|
||||
sortBone(constraint.source.pose);
|
||||
|
||||
Object[] constrained = constraint.bones.items;
|
||||
int boneCount = constraint.bones.size;
|
||||
if (constraint.data.localSource) {
|
||||
for (int i = 0; i < boneCount; i++) {
|
||||
var child = (Bone)constrained[i];
|
||||
Bone child = ((BoneApplied)constrained[i]).pose;
|
||||
sortBone(child.parent);
|
||||
sortBone(child);
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < boneCount; i++)
|
||||
sortBone((Bone)constrained[i]);
|
||||
sortBone(((BoneApplied)constrained[i]).pose);
|
||||
}
|
||||
|
||||
updateCache.add(constraint);
|
||||
|
||||
for (int i = 0; i < boneCount; i++)
|
||||
sortReset(((Bone)constrained[i]).children);
|
||||
sortReset(((BoneApplied)constrained[i]).children);
|
||||
for (int i = 0; i < boneCount; i++)
|
||||
((Bone)constrained[i]).sorted = true;
|
||||
((BoneApplied)constrained[i]).pose.sorted = true;
|
||||
}
|
||||
|
||||
private void sortPathConstraint (PathConstraint constraint) {
|
||||
@ -302,7 +341,7 @@ public class Skeleton {
|
||||
Object[] constrained = constraint.bones.items;
|
||||
int boneCount = constraint.bones.size;
|
||||
for (int i = 0; i < boneCount; i++)
|
||||
sortBone((Bone)constrained[i]);
|
||||
sortBone(((BoneApplied)constrained[i]).pose);
|
||||
|
||||
updateCache.add(constraint);
|
||||
|
||||
@ -331,13 +370,13 @@ public class Skeleton {
|
||||
int nn = pathBones[i++];
|
||||
nn += i;
|
||||
while (i < nn)
|
||||
sortBone((Bone)bones[pathBones[i++]]);
|
||||
sortBone(((BoneApplied)bones[pathBones[i++]]).pose);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void sortPhysicsConstraint (PhysicsConstraint constraint) {
|
||||
Bone bone = constraint.bone;
|
||||
Bone bone = constraint.bone.pose;
|
||||
constraint.active = bone.active
|
||||
&& (!constraint.data.skinRequired || (skin != null && skin.constraints.contains(constraint.data, true)));
|
||||
if (!constraint.active) return;
|
||||
@ -355,7 +394,7 @@ public class Skeleton {
|
||||
Bone parent = bone.parent;
|
||||
if (parent != null) sortBone(parent);
|
||||
bone.sorted = true;
|
||||
updateCache.add(bone);
|
||||
updateCache.add(bone.applied);
|
||||
}
|
||||
|
||||
private void sortReset (Array<Bone> bones) {
|
||||
@ -376,14 +415,29 @@ public class Skeleton {
|
||||
Object[] bones = this.bones.items;
|
||||
for (int i = 0, n = this.bones.size; i < n; i++) {
|
||||
var bone = (Bone)bones[i];
|
||||
bone.ax = bone.x;
|
||||
bone.ay = bone.y;
|
||||
bone.arotation = bone.rotation;
|
||||
bone.ascaleX = bone.scaleX;
|
||||
bone.ascaleY = bone.scaleY;
|
||||
bone.ashearX = bone.shearX;
|
||||
bone.ashearY = bone.shearY;
|
||||
if (!bone.active) continue;
|
||||
BoneApplied applied = bone.applied;
|
||||
applied.x = bone.x;
|
||||
applied.y = bone.y;
|
||||
applied.rotation = bone.rotation;
|
||||
applied.scaleX = bone.scaleX;
|
||||
applied.scaleY = bone.scaleY;
|
||||
applied.shearX = bone.shearX;
|
||||
applied.shearY = bone.shearY;
|
||||
applied.inherit = bone.inherit;
|
||||
}
|
||||
Object[] slots = this.slots.items;
|
||||
for (int i = 0, n = this.slots.size; i < n; i++) {
|
||||
var slot = (Slot)slots[i];
|
||||
if (!slot.bone.active) continue;
|
||||
Slot applied = slot.applied;
|
||||
applied.color.set(slot.color);
|
||||
if (applied.darkColor != null) applied.darkColor.set(slot.darkColor);
|
||||
applied.attachment = slot.attachment;
|
||||
applied.sequenceIndex = slot.sequenceIndex;
|
||||
applied.deform = slot.deform;
|
||||
}
|
||||
// BOZO! - Reset the rest.
|
||||
|
||||
Object[] updateCache = this.updateCache.items;
|
||||
for (int i = 0, n = this.updateCache.size; i < n; i++)
|
||||
@ -395,23 +449,26 @@ public class Skeleton {
|
||||
* <p>
|
||||
* See <a href="https://esotericsoftware.com/spine-runtime-skeletons#World-transforms">World transforms</a> in the Spine
|
||||
* Runtimes Guide. */
|
||||
public void updateWorldTransform (Physics physics, Bone parent) {
|
||||
public void updateWorldTransform (Physics physics, BoneApplied parent) {
|
||||
if (parent == null) throw new IllegalArgumentException("parent cannot be null.");
|
||||
|
||||
Object[] bones = this.bones.items;
|
||||
for (int i = 1, n = this.bones.size; i < n; i++) { // Skip root bone.
|
||||
var bone = (Bone)bones[i];
|
||||
bone.ax = bone.x;
|
||||
bone.ay = bone.y;
|
||||
bone.arotation = bone.rotation;
|
||||
bone.ascaleX = bone.scaleX;
|
||||
bone.ascaleY = bone.scaleY;
|
||||
bone.ashearX = bone.shearX;
|
||||
bone.ashearY = bone.shearY;
|
||||
BoneApplied applied = bone.applied;
|
||||
applied.x = bone.x;
|
||||
applied.y = bone.y;
|
||||
applied.rotation = bone.rotation;
|
||||
applied.scaleX = bone.scaleX;
|
||||
applied.scaleY = bone.scaleY;
|
||||
applied.shearX = bone.shearX;
|
||||
applied.shearY = bone.shearY;
|
||||
applied.inherit = bone.inherit;
|
||||
}
|
||||
// BOZO! - Reset the rest.
|
||||
|
||||
// Apply the parent bone transform to the root bone. The root bone always inherits scale, rotation and reflection.
|
||||
Bone rootBone = getRootBone();
|
||||
BoneApplied rootBone = getRootBone().applied;
|
||||
float pa = parent.a, pb = parent.b, pc = parent.c, pd = parent.d;
|
||||
rootBone.worldX = pa * x + pb * y + parent.worldX;
|
||||
rootBone.worldY = pc * x + pd * y + parent.worldY;
|
||||
@ -447,21 +504,25 @@ public class Skeleton {
|
||||
for (int i = 0, n = this.bones.size; i < n; i++)
|
||||
((Bone)bones[i]).setToSetupPose();
|
||||
|
||||
Object[] ikConstraints = this.ikConstraints.items;
|
||||
for (int i = 0, n = this.ikConstraints.size; i < n; i++)
|
||||
((IkConstraint)ikConstraints[i]).setToSetupPose();
|
||||
Object[] constraints = sliders.items;
|
||||
for (int i = 0, n = sliders.size; i < n; i++)
|
||||
((Slider)constraints[i]).setToSetupPose();
|
||||
|
||||
Object[] transformConstraints = this.transformConstraints.items;
|
||||
for (int i = 0, n = this.transformConstraints.size; i < n; i++)
|
||||
((TransformConstraint)transformConstraints[i]).setToSetupPose();
|
||||
constraints = ikConstraints.items;
|
||||
for (int i = 0, n = ikConstraints.size; i < n; i++)
|
||||
((IkConstraint)constraints[i]).setToSetupPose();
|
||||
|
||||
Object[] pathConstraints = this.pathConstraints.items;
|
||||
for (int i = 0, n = this.pathConstraints.size; i < n; i++)
|
||||
((PathConstraint)pathConstraints[i]).setToSetupPose();
|
||||
constraints = transformConstraints.items;
|
||||
for (int i = 0, n = transformConstraints.size; i < n; i++)
|
||||
((TransformConstraint)constraints[i]).setToSetupPose();
|
||||
|
||||
Object[] physicsConstraints = this.physicsConstraints.items;
|
||||
for (int i = 0, n = this.physicsConstraints.size; i < n; i++)
|
||||
((PhysicsConstraint)physicsConstraints[i]).setToSetupPose();
|
||||
constraints = pathConstraints.items;
|
||||
for (int i = 0, n = pathConstraints.size; i < n; i++)
|
||||
((PathConstraint)constraints[i]).setToSetupPose();
|
||||
|
||||
constraints = physicsConstraints.items;
|
||||
for (int i = 0, n = physicsConstraints.size; i < n; i++)
|
||||
((PhysicsConstraint)constraints[i]).setToSetupPose();
|
||||
}
|
||||
|
||||
/** Sets the slots and draw order to their setup pose values. */
|
||||
@ -617,6 +678,23 @@ public class Skeleton {
|
||||
slot.setAttachment(attachment);
|
||||
}
|
||||
|
||||
/** The skeleton's sliders. */
|
||||
public Array<Slider> getSliders () {
|
||||
return sliders;
|
||||
}
|
||||
|
||||
/** Finds a slider by comparing each slider's name. It is more efficient to cache the results of this method than to call it
|
||||
* repeatedly. */
|
||||
public @Null Slider findSlider (String constraintName) {
|
||||
if (constraintName == null) throw new IllegalArgumentException("constraintName cannot be null.");
|
||||
Object[] sliders = this.sliders.items;
|
||||
for (int i = 0, n = this.sliders.size; i < n; i++) {
|
||||
var slider = (Slider)sliders[i];
|
||||
if (slider.data.name.equals(constraintName)) return slider;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/** The skeleton's IK constraints. */
|
||||
public Array<IkConstraint> getIkConstraints () {
|
||||
return ikConstraints;
|
||||
|
||||
@ -44,6 +44,7 @@ public class SkeletonData {
|
||||
@Null Skin defaultSkin;
|
||||
final Array<EventData> events = new Array();
|
||||
final Array<Animation> animations = new Array();
|
||||
final Array<SliderData> sliders = new Array();
|
||||
final Array<IkConstraintData> ikConstraints = new Array();
|
||||
final Array<TransformConstraintData> transformConstraints = new Array();
|
||||
final Array<PathConstraintData> pathConstraints = new Array();
|
||||
@ -159,6 +160,25 @@ public class SkeletonData {
|
||||
return null;
|
||||
}
|
||||
|
||||
// --- Sliders
|
||||
|
||||
/** The skeleton's sliders. */
|
||||
public Array<SliderData> getSliders () {
|
||||
return sliders;
|
||||
}
|
||||
|
||||
/** Finds a slider by comparing each IK constraint's name. It is more efficient to cache the results of this method than to
|
||||
* call it multiple times. */
|
||||
public @Null SliderData findSlider (String constraintName) {
|
||||
if (constraintName == null) throw new IllegalArgumentException("constraintName cannot be null.");
|
||||
Object[] sliders = this.sliders.items;
|
||||
for (int i = 0, n = this.sliders.size; i < n; i++) {
|
||||
var constraint = (SliderData)sliders[i];
|
||||
if (constraint.name.equals(constraintName)) return constraint;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// --- IK constraints
|
||||
|
||||
/** The skeleton's IK constraints. */
|
||||
|
||||
@ -80,8 +80,8 @@ public class SkeletonRendererDebug {
|
||||
Gdx.gl.glBlendFunc(srcFunc, GL20.GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
ShapeRenderer shapes = this.shapes;
|
||||
Array<Bone> bones = skeleton.getBones();
|
||||
Array<Slot> slots = skeleton.getSlots();
|
||||
Array<Bone> bones = skeleton.bones;
|
||||
Array<Slot> slots = skeleton.slots;
|
||||
|
||||
shapes.begin(ShapeType.Filled);
|
||||
|
||||
@ -96,11 +96,12 @@ public class SkeletonRendererDebug {
|
||||
shapes.setColor(boneOriginColor);
|
||||
} else
|
||||
shapes.setColor(boneLineColor);
|
||||
float x = length * bone.a + bone.worldX;
|
||||
float y = length * bone.c + bone.worldY;
|
||||
shapes.rectLine(bone.worldX, bone.worldY, x, y, width * scale);
|
||||
BoneApplied applied = bone.applied;
|
||||
float x = length * applied.a + applied.worldX;
|
||||
float y = length * applied.c + applied.worldY;
|
||||
shapes.rectLine(applied.worldX, applied.worldY, x, y, width * scale);
|
||||
}
|
||||
shapes.x(skeleton.getX(), skeleton.getY(), 4 * scale);
|
||||
shapes.x(skeleton.x, skeleton.y, 4 * scale);
|
||||
}
|
||||
|
||||
if (drawPoints) {
|
||||
@ -110,8 +111,8 @@ public class SkeletonRendererDebug {
|
||||
if (!slot.bone.active) continue;
|
||||
Attachment attachment = slot.attachment;
|
||||
if (!(attachment instanceof PointAttachment point)) continue;
|
||||
point.computeWorldPosition(slot.getBone(), temp1);
|
||||
temp2.set(8, 0).rotate(point.computeWorldRotation(slot.getBone()));
|
||||
point.computeWorldPosition(slot.bone.applied, temp1);
|
||||
temp2.set(8, 0).rotate(point.computeWorldRotation(slot.bone.applied));
|
||||
shapes.rectLine(temp1, temp2, boneWidth / 2 * scale);
|
||||
}
|
||||
}
|
||||
@ -244,7 +245,7 @@ public class SkeletonRendererDebug {
|
||||
for (int i = 0, n = bones.size; i < n; i++) {
|
||||
Bone bone = bones.get(i);
|
||||
if (!bone.active) continue;
|
||||
shapes.circle(bone.worldX, bone.worldY, 3 * scale, 8);
|
||||
shapes.circle(bone.applied.worldX, bone.applied.worldY, 3 * scale, 8);
|
||||
}
|
||||
}
|
||||
|
||||
@ -255,7 +256,7 @@ public class SkeletonRendererDebug {
|
||||
if (!slot.bone.active) continue;
|
||||
Attachment attachment = slot.attachment;
|
||||
if (!(attachment instanceof PointAttachment point)) continue;
|
||||
point.computeWorldPosition(slot.getBone(), temp1);
|
||||
point.computeWorldPosition(slot.bone.applied, temp1);
|
||||
shapes.circle(temp1.x, temp1.y, 3 * scale, 8);
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,100 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated April 5, 2025. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2025, Esoteric Software LLC
|
||||
*
|
||||
* Integration of the Spine Runtimes into software or otherwise creating
|
||||
* derivative works of the Spine Runtimes is permitted under the terms and
|
||||
* conditions of Section 2 of the Spine Editor License Agreement:
|
||||
* http://esotericsoftware.com/spine-editor-license
|
||||
*
|
||||
* Otherwise, it is permitted to integrate the Spine Runtimes into software
|
||||
* or otherwise create derivative works of the Spine Runtimes (collectively,
|
||||
* "Products"), provided that each user of the Products must obtain their own
|
||||
* Spine Editor license and redistribution of the Products in any form must
|
||||
* include this license and copyright notice.
|
||||
*
|
||||
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "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 LLC BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
|
||||
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) 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
|
||||
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
package com.esotericsoftware.spine;
|
||||
|
||||
import com.esotericsoftware.spine.Animation.MixBlend;
|
||||
import com.esotericsoftware.spine.Animation.MixDirection;
|
||||
import com.esotericsoftware.spine.Skeleton.Physics;
|
||||
|
||||
/** Stores the setup pose for a {@link PhysicsConstraint}.
|
||||
* <p>
|
||||
* See <a href="https://esotericsoftware.com/spine-physics-constraints">Physics constraints</a> in the Spine User Guide. */
|
||||
public class Slider implements Updatable {
|
||||
final SliderData data;
|
||||
final Skeleton skeleton;
|
||||
Animation animation;
|
||||
float time, mix;
|
||||
|
||||
boolean active;
|
||||
|
||||
public Slider (SliderData data, Skeleton skeleton) {
|
||||
if (data == null) throw new IllegalArgumentException("data cannot be null.");
|
||||
if (skeleton == null) throw new IllegalArgumentException("skeleton cannot be null.");
|
||||
this.data = data;
|
||||
this.skeleton = skeleton;
|
||||
|
||||
setToSetupPose();
|
||||
}
|
||||
|
||||
/** Copy constructor. */
|
||||
public Slider (Slider slider, Skeleton skeleton) {
|
||||
this(slider.data, skeleton);
|
||||
setToSetupPose();
|
||||
}
|
||||
|
||||
public void update (Physics physics) {
|
||||
animation.apply(skeleton, time, time, false, null, mix, MixBlend.replace, MixDirection.in, true);
|
||||
}
|
||||
|
||||
public void setToSetupPose () {
|
||||
SliderData data = this.data;
|
||||
animation = data.animation;
|
||||
time = data.time;
|
||||
mix = data.mix;
|
||||
}
|
||||
|
||||
public boolean isActive () {
|
||||
return true;
|
||||
}
|
||||
|
||||
public Animation getAnimation () {
|
||||
return animation;
|
||||
}
|
||||
|
||||
public void setAnimation (Animation animation) {
|
||||
this.animation = animation;
|
||||
}
|
||||
|
||||
public float getTime () {
|
||||
return time;
|
||||
}
|
||||
|
||||
public void setTime (float time) {
|
||||
this.time = time;
|
||||
}
|
||||
|
||||
public float getMix () {
|
||||
return mix;
|
||||
}
|
||||
|
||||
public void setMix (float mix) {
|
||||
this.mix = mix;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,66 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated April 5, 2025. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2025, Esoteric Software LLC
|
||||
*
|
||||
* Integration of the Spine Runtimes into software or otherwise creating
|
||||
* derivative works of the Spine Runtimes is permitted under the terms and
|
||||
* conditions of Section 2 of the Spine Editor License Agreement:
|
||||
* http://esotericsoftware.com/spine-editor-license
|
||||
*
|
||||
* Otherwise, it is permitted to integrate the Spine Runtimes into software
|
||||
* or otherwise create derivative works of the Spine Runtimes (collectively,
|
||||
* "Products"), provided that each user of the Products must obtain their own
|
||||
* Spine Editor license and redistribution of the Products in any form must
|
||||
* include this license and copyright notice.
|
||||
*
|
||||
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "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 LLC BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
|
||||
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) 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
|
||||
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
package com.esotericsoftware.spine;
|
||||
|
||||
/** Stores the setup pose for a {@link PhysicsConstraint}.
|
||||
* <p>
|
||||
* See <a href="https://esotericsoftware.com/spine-physics-constraints">Physics constraints</a> in the Spine User Guide. */
|
||||
public class SliderData extends ConstraintData {
|
||||
Animation animation;
|
||||
float time, mix;
|
||||
|
||||
public SliderData (String name) {
|
||||
super(name);
|
||||
}
|
||||
|
||||
public Animation getAnimation () {
|
||||
return animation;
|
||||
}
|
||||
|
||||
public void setAnimation (Animation animation) {
|
||||
this.animation = animation;
|
||||
}
|
||||
|
||||
public float getTime () {
|
||||
return time;
|
||||
}
|
||||
|
||||
public void setTime (float time) {
|
||||
this.time = time;
|
||||
}
|
||||
|
||||
public float getMix () {
|
||||
return mix;
|
||||
}
|
||||
|
||||
public void setMix (float mix) {
|
||||
this.mix = mix;
|
||||
}
|
||||
}
|
||||
@ -44,20 +44,34 @@ import com.esotericsoftware.spine.attachments.VertexAttachment;
|
||||
public class Slot {
|
||||
final SlotData data;
|
||||
final Bone bone;
|
||||
Color color = new Color();
|
||||
Slot applied;
|
||||
|
||||
final Color color = new Color();
|
||||
@Null final Color darkColor;
|
||||
@Null Attachment attachment;
|
||||
int sequenceIndex;
|
||||
FloatArray deform = new FloatArray();
|
||||
FloatArray deform;
|
||||
|
||||
int attachmentState;
|
||||
|
||||
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.");
|
||||
private Slot (SlotData data, Bone bone) {
|
||||
this.data = data;
|
||||
this.bone = bone;
|
||||
|
||||
darkColor = data.darkColor == null ? null : new Color();
|
||||
}
|
||||
|
||||
public Slot (SlotData data, Skeleton skeleton) {
|
||||
if (data == null) throw new IllegalArgumentException("data cannot be null.");
|
||||
if (skeleton == null) throw new IllegalArgumentException("skeleton cannot be null.");
|
||||
this.data = data;
|
||||
this.bone = skeleton.bones.get(data.boneData.index);
|
||||
|
||||
darkColor = data.darkColor == null ? null : new Color();
|
||||
deform = new FloatArray();
|
||||
|
||||
applied = new Slot(data, bone);
|
||||
|
||||
setToSetupPose();
|
||||
}
|
||||
|
||||
@ -71,7 +85,7 @@ public class Slot {
|
||||
darkColor = slot.darkColor == null ? null : new Color(slot.darkColor);
|
||||
attachment = slot.attachment;
|
||||
sequenceIndex = slot.sequenceIndex;
|
||||
deform.addAll(slot.deform);
|
||||
deform = new FloatArray(slot.deform);
|
||||
}
|
||||
|
||||
/** The slot's setup pose data. */
|
||||
@ -95,10 +109,6 @@ public class Slot {
|
||||
return color;
|
||||
}
|
||||
|
||||
public void setColor (Color color) {
|
||||
this.color = color;
|
||||
}
|
||||
|
||||
/** The dark color used to tint the slot's attachment for two color tinting, or null if two color tinting is not used. The dark
|
||||
* color's alpha is not used. */
|
||||
public @Null Color getDarkColor () {
|
||||
@ -158,6 +168,11 @@ public class Slot {
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns the bone for applied pose. */
|
||||
public Slot getApplied () {
|
||||
return applied;
|
||||
}
|
||||
|
||||
public String toString () {
|
||||
return data.name;
|
||||
}
|
||||
|
||||
@ -31,8 +31,8 @@ package com.esotericsoftware.spine;
|
||||
|
||||
import static com.badlogic.gdx.math.MathUtils.*;
|
||||
|
||||
import com.badlogic.gdx.math.Vector2;
|
||||
import com.badlogic.gdx.utils.Array;
|
||||
import com.badlogic.gdx.utils.Null;
|
||||
|
||||
import com.esotericsoftware.spine.Skeleton.Physics;
|
||||
import com.esotericsoftware.spine.TransformConstraintData.FromProperty;
|
||||
@ -44,12 +44,18 @@ import com.esotericsoftware.spine.TransformConstraintData.ToProperty;
|
||||
* See <a href="https://esotericsoftware.com/spine-transform-constraints">Transform constraints</a> in the Spine User Guide. */
|
||||
public class TransformConstraint implements Updatable {
|
||||
final TransformConstraintData data;
|
||||
final Array<Bone> bones;
|
||||
Bone source;
|
||||
final Array<BoneApplied> bones;
|
||||
BoneApplied source;
|
||||
TransformConstraint applied;
|
||||
boolean active;
|
||||
|
||||
float mixRotate, mixX, mixY, mixScaleX, mixScaleY, mixShearY;
|
||||
|
||||
boolean active;
|
||||
final Vector2 temp = new Vector2();
|
||||
public TransformConstraint (TransformConstraintData data, Array<BoneApplied> bones, BoneApplied source) {
|
||||
this.data = data;
|
||||
this.bones = bones;
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
public TransformConstraint (TransformConstraintData data, Skeleton skeleton) {
|
||||
if (data == null) throw new IllegalArgumentException("data cannot be null.");
|
||||
@ -58,9 +64,11 @@ public class TransformConstraint implements Updatable {
|
||||
|
||||
bones = new Array(data.bones.size);
|
||||
for (BoneData boneData : data.bones)
|
||||
bones.add(skeleton.bones.get(boneData.index));
|
||||
bones.add(skeleton.bones.get(boneData.index).applied);
|
||||
|
||||
source = skeleton.bones.get(data.source.index);
|
||||
source = skeleton.bones.get(data.source.index).applied;
|
||||
|
||||
applied = new TransformConstraint(data, bones, source);
|
||||
|
||||
setToSetupPose();
|
||||
}
|
||||
@ -87,12 +95,12 @@ public class TransformConstraint implements Updatable {
|
||||
|
||||
TransformConstraintData data = this.data;
|
||||
boolean localFrom = data.localSource, localTarget = data.localTarget, additive = data.additive, clamp = data.clamp;
|
||||
Bone source = this.source;
|
||||
BoneApplied source = this.source;
|
||||
Object[] fromItems = data.properties.items;
|
||||
int fn = data.properties.size;
|
||||
Object[] bones = this.bones.items;
|
||||
for (int i = 0, n = this.bones.size; i < n; i++) {
|
||||
var bone = (Bone)bones[i];
|
||||
var bone = (BoneApplied)bones[i];
|
||||
for (int f = 0; f < fn; f++) {
|
||||
var from = (FromProperty)fromItems[f];
|
||||
float value = from.value(data, source, localFrom) - from.offset;
|
||||
@ -119,16 +127,16 @@ public class TransformConstraint implements Updatable {
|
||||
}
|
||||
|
||||
/** The bones that will be modified by this transform constraint. */
|
||||
public Array<Bone> getBones () {
|
||||
public Array<BoneApplied> getBones () {
|
||||
return bones;
|
||||
}
|
||||
|
||||
/** The bone whose world transform will be copied to the constrained bones. */
|
||||
public Bone getSource () {
|
||||
public BoneApplied getSource () {
|
||||
return source;
|
||||
}
|
||||
|
||||
public void setSource (Bone source) {
|
||||
public void setSource (BoneApplied source) {
|
||||
if (source == null) throw new IllegalArgumentException("source cannot be null.");
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
@ -221,7 +221,7 @@ public class TransformConstraintData extends ConstraintData {
|
||||
public final Array<ToProperty> to = new Array();
|
||||
|
||||
/** Reads this property from the specified bone. */
|
||||
abstract public float value (TransformConstraintData data, Bone source, boolean local);
|
||||
abstract public float value (TransformConstraintData data, BoneApplied source, boolean local);
|
||||
}
|
||||
|
||||
/** Constrained property for a {@link TransformConstraint}. */
|
||||
@ -239,12 +239,12 @@ public class TransformConstraintData extends ConstraintData {
|
||||
abstract public float mix (TransformConstraint constraint);
|
||||
|
||||
/** Applies the value to this property. */
|
||||
abstract public void apply (TransformConstraint constraint, Bone bone, float value, boolean local, boolean additive);
|
||||
abstract public void apply (TransformConstraint constraint, BoneApplied bone, float value, boolean local, boolean additive);
|
||||
}
|
||||
|
||||
static public class FromRotate extends FromProperty {
|
||||
public float value (TransformConstraintData data, Bone source, boolean local) {
|
||||
if (local) return source.arotation + data.offsetRotation;
|
||||
public float value (TransformConstraintData data, BoneApplied source, boolean local) {
|
||||
if (local) return source.rotation + data.offsetRotation;
|
||||
float value = atan2(source.c, source.a) * radDeg
|
||||
+ (source.a * source.d - source.b * source.c > 0 ? data.offsetRotation : -data.offsetRotation);
|
||||
if (value < 0) value += 360;
|
||||
@ -257,10 +257,10 @@ public class TransformConstraintData extends ConstraintData {
|
||||
return constraint.mixRotate;
|
||||
}
|
||||
|
||||
public void apply (TransformConstraint constraint, Bone bone, float value, boolean local, boolean additive) {
|
||||
public void apply (TransformConstraint constraint, BoneApplied bone, float value, boolean local, boolean additive) {
|
||||
if (local) {
|
||||
if (!additive) value -= bone.arotation;
|
||||
bone.arotation += value * constraint.mixRotate;
|
||||
if (!additive) value -= bone.rotation;
|
||||
bone.rotation += value * constraint.mixRotate;
|
||||
} else {
|
||||
float a = bone.a, b = bone.b, c = bone.c, d = bone.d;
|
||||
value *= degRad;
|
||||
@ -280,8 +280,8 @@ public class TransformConstraintData extends ConstraintData {
|
||||
}
|
||||
|
||||
static public class FromX extends FromProperty {
|
||||
public float value (TransformConstraintData data, Bone source, boolean local) {
|
||||
return local ? source.ax + data.offsetX : data.offsetX * source.a + data.offsetY * source.b + source.worldX;
|
||||
public float value (TransformConstraintData data, BoneApplied source, boolean local) {
|
||||
return local ? source.x + data.offsetX : data.offsetX * source.a + data.offsetY * source.b + source.worldX;
|
||||
}
|
||||
}
|
||||
|
||||
@ -290,10 +290,10 @@ public class TransformConstraintData extends ConstraintData {
|
||||
return constraint.mixX;
|
||||
}
|
||||
|
||||
public void apply (TransformConstraint constraint, Bone bone, float value, boolean local, boolean additive) {
|
||||
public void apply (TransformConstraint constraint, BoneApplied bone, float value, boolean local, boolean additive) {
|
||||
if (local) {
|
||||
if (!additive) value -= bone.ax;
|
||||
bone.ax += value * constraint.mixX;
|
||||
if (!additive) value -= bone.x;
|
||||
bone.x += value * constraint.mixX;
|
||||
} else {
|
||||
if (!additive) value -= bone.worldX;
|
||||
bone.worldX += value * constraint.mixX;
|
||||
@ -302,8 +302,8 @@ public class TransformConstraintData extends ConstraintData {
|
||||
}
|
||||
|
||||
static public class FromY extends FromProperty {
|
||||
public float value (TransformConstraintData data, Bone source, boolean local) {
|
||||
return local ? source.ay + data.offsetY : data.offsetX * source.c + data.offsetY * source.d + source.worldY;
|
||||
public float value (TransformConstraintData data, BoneApplied source, boolean local) {
|
||||
return local ? source.y + data.offsetY : data.offsetX * source.c + data.offsetY * source.d + source.worldY;
|
||||
}
|
||||
}
|
||||
|
||||
@ -312,10 +312,10 @@ public class TransformConstraintData extends ConstraintData {
|
||||
return constraint.mixY;
|
||||
}
|
||||
|
||||
public void apply (TransformConstraint constraint, Bone bone, float value, boolean local, boolean additive) {
|
||||
public void apply (TransformConstraint constraint, BoneApplied bone, float value, boolean local, boolean additive) {
|
||||
if (local) {
|
||||
if (!additive) value -= bone.ay;
|
||||
bone.ay += value * constraint.mixY;
|
||||
if (!additive) value -= bone.y;
|
||||
bone.y += value * constraint.mixY;
|
||||
} else {
|
||||
if (!additive) value -= bone.worldY;
|
||||
bone.worldY += value * constraint.mixY;
|
||||
@ -324,8 +324,8 @@ public class TransformConstraintData extends ConstraintData {
|
||||
}
|
||||
|
||||
static public class FromScaleX extends FromProperty {
|
||||
public float value (TransformConstraintData data, Bone source, boolean local) {
|
||||
return (local ? source.ascaleX : (float)Math.sqrt(source.a * source.a + source.c * source.c)) + data.offsetScaleX;
|
||||
public float value (TransformConstraintData data, BoneApplied source, boolean local) {
|
||||
return (local ? source.scaleX : (float)Math.sqrt(source.a * source.a + source.c * source.c)) + data.offsetScaleX;
|
||||
}
|
||||
}
|
||||
|
||||
@ -334,12 +334,12 @@ public class TransformConstraintData extends ConstraintData {
|
||||
return constraint.mixScaleX;
|
||||
}
|
||||
|
||||
public void apply (TransformConstraint constraint, Bone bone, float value, boolean local, boolean additive) {
|
||||
public void apply (TransformConstraint constraint, BoneApplied bone, float value, boolean local, boolean additive) {
|
||||
if (local) {
|
||||
if (additive)
|
||||
bone.ascaleX *= 1 + ((value - 1) * constraint.mixScaleX);
|
||||
else if (bone.ascaleX != 0) //
|
||||
bone.ascaleX = 1 + (value / bone.ascaleX - 1) * constraint.mixScaleX;
|
||||
bone.scaleX *= 1 + ((value - 1) * constraint.mixScaleX);
|
||||
else if (bone.scaleX != 0) //
|
||||
bone.scaleX = 1 + (value / bone.scaleX - 1) * constraint.mixScaleX;
|
||||
} else {
|
||||
float s;
|
||||
if (additive)
|
||||
@ -355,8 +355,8 @@ public class TransformConstraintData extends ConstraintData {
|
||||
}
|
||||
|
||||
static public class FromScaleY extends FromProperty {
|
||||
public float value (TransformConstraintData data, Bone source, boolean local) {
|
||||
return (local ? source.ascaleY : (float)Math.sqrt(source.b * source.b + source.d * source.d)) + data.offsetScaleY;
|
||||
public float value (TransformConstraintData data, BoneApplied source, boolean local) {
|
||||
return (local ? source.scaleY : (float)Math.sqrt(source.b * source.b + source.d * source.d)) + data.offsetScaleY;
|
||||
}
|
||||
}
|
||||
|
||||
@ -365,12 +365,12 @@ public class TransformConstraintData extends ConstraintData {
|
||||
return constraint.mixScaleY;
|
||||
}
|
||||
|
||||
public void apply (TransformConstraint constraint, Bone bone, float value, boolean local, boolean additive) {
|
||||
public void apply (TransformConstraint constraint, BoneApplied bone, float value, boolean local, boolean additive) {
|
||||
if (local) {
|
||||
if (additive)
|
||||
bone.ascaleY *= 1 + ((value - 1) * constraint.mixScaleY);
|
||||
else if (bone.ascaleY != 0) //
|
||||
bone.ascaleY = 1 + (value / bone.ascaleY - 1) * constraint.mixScaleY;
|
||||
bone.scaleY *= 1 + ((value - 1) * constraint.mixScaleY);
|
||||
else if (bone.scaleY != 0) //
|
||||
bone.scaleY = 1 + (value / bone.scaleY - 1) * constraint.mixScaleY;
|
||||
} else {
|
||||
float s;
|
||||
if (additive)
|
||||
@ -386,8 +386,8 @@ public class TransformConstraintData extends ConstraintData {
|
||||
}
|
||||
|
||||
static public class FromShearY extends FromProperty {
|
||||
public float value (TransformConstraintData data, Bone source, boolean local) {
|
||||
return (local ? source.ashearY : (atan2(source.d, source.b) - atan2(source.c, source.a)) * radDeg - 90)
|
||||
public float value (TransformConstraintData data, BoneApplied source, boolean local) {
|
||||
return (local ? source.shearY : (atan2(source.d, source.b) - atan2(source.c, source.a)) * radDeg - 90)
|
||||
+ data.offsetShearY;
|
||||
}
|
||||
}
|
||||
@ -397,10 +397,10 @@ public class TransformConstraintData extends ConstraintData {
|
||||
return constraint.mixShearY;
|
||||
}
|
||||
|
||||
public void apply (TransformConstraint constraint, Bone bone, float value, boolean local, boolean additive) {
|
||||
public void apply (TransformConstraint constraint, BoneApplied bone, float value, boolean local, boolean additive) {
|
||||
if (local) {
|
||||
if (!additive) value -= bone.ashearY;
|
||||
bone.ashearY += value * constraint.mixShearY;
|
||||
if (!additive) value -= bone.shearY;
|
||||
bone.shearY += value * constraint.mixShearY;
|
||||
} else {
|
||||
float b = bone.b, d = bone.d, by = atan2(d, b);
|
||||
value = (value + 90) * degRad;
|
||||
|
||||
@ -34,7 +34,7 @@ import static com.esotericsoftware.spine.utils.SpineUtils.*;
|
||||
import com.badlogic.gdx.graphics.Color;
|
||||
import com.badlogic.gdx.math.Vector2;
|
||||
|
||||
import com.esotericsoftware.spine.Bone;
|
||||
import com.esotericsoftware.spine.BoneApplied;
|
||||
|
||||
/** An attachment which is a single point and a rotation. This can be used to spawn projectiles, particles, etc. A bone can be
|
||||
* used in similar ways, but a PointAttachment is slightly less expensive to compute and can be hidden, shown, and placed in a
|
||||
@ -90,13 +90,13 @@ public class PointAttachment extends Attachment {
|
||||
return color;
|
||||
}
|
||||
|
||||
public Vector2 computeWorldPosition (Bone bone, Vector2 point) {
|
||||
public Vector2 computeWorldPosition (BoneApplied bone, Vector2 point) {
|
||||
point.x = x * bone.getA() + y * bone.getB() + bone.getWorldX();
|
||||
point.y = x * bone.getC() + y * bone.getD() + bone.getWorldY();
|
||||
return point;
|
||||
}
|
||||
|
||||
public float computeWorldRotation (Bone bone) {
|
||||
public float computeWorldRotation (BoneApplied bone) {
|
||||
float r = rotation * degRad, cos = cos(r), sin = sin(r);
|
||||
float x = cos * bone.getA() + sin * bone.getB();
|
||||
float y = cos * bone.getC() + sin * bone.getD();
|
||||
|
||||
@ -37,6 +37,7 @@ import com.badlogic.gdx.graphics.g2d.TextureRegion;
|
||||
import com.badlogic.gdx.utils.Null;
|
||||
|
||||
import com.esotericsoftware.spine.Bone;
|
||||
import com.esotericsoftware.spine.BoneApplied;
|
||||
import com.esotericsoftware.spine.Slot;
|
||||
|
||||
/** An attachment that displays a textured quadrilateral.
|
||||
@ -179,7 +180,7 @@ public class RegionAttachment extends Attachment implements HasTextureRegion {
|
||||
if (sequence != null) sequence.apply(slot, this);
|
||||
|
||||
float[] vertexOffset = this.offset;
|
||||
Bone bone = slot.getBone();
|
||||
BoneApplied bone = slot.getBone().getApplied();
|
||||
float x = bone.getWorldX(), y = bone.getWorldY();
|
||||
float a = bone.getA(), b = bone.getB(), c = bone.getC(), d = bone.getD();
|
||||
float offsetX, offsetY;
|
||||
|
||||
@ -35,6 +35,7 @@ import com.badlogic.gdx.utils.FloatArray;
|
||||
import com.badlogic.gdx.utils.Null;
|
||||
|
||||
import com.esotericsoftware.spine.Bone;
|
||||
import com.esotericsoftware.spine.BoneApplied;
|
||||
import com.esotericsoftware.spine.Skeleton;
|
||||
import com.esotericsoftware.spine.Slot;
|
||||
|
||||
@ -91,7 +92,7 @@ abstract public class VertexAttachment extends Attachment {
|
||||
int[] bones = this.bones;
|
||||
if (bones == null) {
|
||||
if (deformArray.size > 0) vertices = deformArray.items;
|
||||
Bone bone = slot.getBone();
|
||||
BoneApplied bone = slot.getBone().getApplied();
|
||||
float x = bone.getWorldX(), y = bone.getWorldY();
|
||||
float a = bone.getA(), b = bone.getB(), c = bone.getC(), d = bone.getD();
|
||||
for (int v = start, w = offset; w < count; v += 2, w += stride) {
|
||||
@ -114,7 +115,7 @@ abstract public class VertexAttachment extends Attachment {
|
||||
int n = bones[v++];
|
||||
n += v;
|
||||
for (; v < n; v++, b += 3) {
|
||||
var bone = (Bone)skeletonBones[bones[v]];
|
||||
BoneApplied bone = ((Bone)skeletonBones[bones[v]]).getApplied();
|
||||
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;
|
||||
@ -129,7 +130,7 @@ abstract public class VertexAttachment extends Attachment {
|
||||
int n = bones[v++];
|
||||
n += v;
|
||||
for (; v < n; v++, b += 3, f += 2) {
|
||||
var bone = (Bone)skeletonBones[bones[v]];
|
||||
BoneApplied bone = ((Bone)skeletonBones[bones[v]]).getApplied();
|
||||
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;
|
||||
|
||||
@ -41,7 +41,7 @@ public class SkeletonClipping {
|
||||
private final FloatArray clippingPolygon = new FloatArray();
|
||||
private final FloatArray clipOutput = new FloatArray(128);
|
||||
private final FloatArray clippedVertices = new FloatArray(128);
|
||||
private final FloatArray clippedUvs = new FloatArray(128);
|
||||
private final FloatArray clippedUvs = new FloatArray(0);
|
||||
private final ShortArray clippedTriangles = new ShortArray(128);
|
||||
private final FloatArray scratch = new FloatArray();
|
||||
|
||||
@ -459,7 +459,7 @@ public class SkeletonClipping {
|
||||
return clippedVertices;
|
||||
}
|
||||
|
||||
/** Only returns a non-empty array if clipTrianglesUnpacked() was used **/
|
||||
/** Returns an empty array unless {@link #clipTrianglesUnpacked(float[], int, short[], int, float[])} was used. **/
|
||||
public FloatArray getClippedUvs () {
|
||||
return clippedUvs;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user