diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/PhysicsConstraint.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/PhysicsConstraint.java
index e69e6542d..ca56a1ea4 100644
--- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/PhysicsConstraint.java
+++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/PhysicsConstraint.java
@@ -1,16 +1,16 @@
/******************************************************************************
* Spine Runtimes License Agreement
- * Last updated September 24, 2021. Replaces all prior versions.
+ * Last updated July 28, 2023. Replaces all prior versions.
*
- * Copyright (c) 2013-2021, Esoteric Software LLC
+ * Copyright (c) 2013-2023, 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,
+ * 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.
@@ -23,31 +23,32 @@
* (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.
+ * (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 static com.badlogic.gdx.math.Interpolation.*;
import static com.esotericsoftware.spine.utils.SpineUtils.*;
-import com.badlogic.gdx.math.Vector2;
-import com.badlogic.gdx.utils.Array;
-
import com.esotericsoftware.spine.Skeleton.Physics;
-// BOZO - Physics steps/something in metrics view.
-
/** Stores the current pose for a physics constraint. A physics constraint applies physics to bones.
*
* See Physics constraints in the Spine User Guide. */
public class PhysicsConstraint implements Updatable {
final PhysicsConstraintData data;
- final Array bones;
- State[] states = {};
+ public Bone bone;
+
float mix;
+ boolean reset = true;
+ float beforeX, beforeY, afterX, afterY, tipX, tipY;
+ float xOffset, xVelocity;
+ float yOffset, yVelocity;
+ float rotateOffset, rotateVelocity;
+ float scaleOffset, scaleVelocity;
+
boolean active;
final Skeleton skeleton;
@@ -59,10 +60,7 @@ public class PhysicsConstraint implements Updatable {
this.data = data;
this.skeleton = skeleton;
- bones = new Array(data.bones.size);
- for (BoneData boneData : data.bones)
- bones.add(skeleton.bones.get(boneData.index));
-
+ bone = skeleton.bones.get(data.bone.index);
mix = data.mix;
}
@@ -71,36 +69,23 @@ public class PhysicsConstraint implements Updatable {
if (constraint == null) throw new IllegalArgumentException("constraint cannot be null.");
data = constraint.data;
skeleton = constraint.skeleton;
- bones = new Array(constraint.bones);
+ bone = constraint.bone;
mix = constraint.mix;
}
- /** Caches information about bones. Must be called if {@link PathConstraintData#getBones()} is modified. */
- public void updateBones () {
- int count = 0;
- if (data.x) count = 1;
- if (data.y) count++;
- if (data.rotate) count++;
- if (data.scaleX) count++;
- if (data.shearX) count++;
- count *= bones.size * 2;
- if (states.length != count) {
- states = new State[count];
- for (int i = 0; i < count; i++)
- states[i] = new State();
- }
- }
-
public void reset () {
remaining = 0;
lastTime = skeleton.time;
- for (int i = 0, n = states.length; i < n; i++) {
- State state = states[i];
- state.last = false;
- state.offset = 0;
- state.velocity = 0;
- }
+ reset = true;
+ xOffset = 0;
+ xVelocity = 0;
+ yOffset = 0;
+ yVelocity = 0;
+ rotateOffset = 0;
+ rotateVelocity = 0;
+ scaleOffset = 0;
+ scaleVelocity = 0;
}
public void setToSetupPose () {
@@ -112,112 +97,127 @@ public class PhysicsConstraint implements Updatable {
/** Applies the constraint to the constrained bones. */
public void update (Physics physics) {
+ float mix = this.mix;
if (mix == 0) return;
- data.rotate = true; // BOZO - Remove.
- updateBones(); // BOZO - Remove.
-
- Object[] bones = this.bones.items;
- int boneCount = this.bones.size;
+ boolean x = data.x, y = data.y, rotateOrShear = data.rotate || data.shearX, scaleX = data.scaleX;
+ Bone bone = this.bone;
switch (physics) {
case none:
return;
case reset:
reset();
- // Fall through.
+ break;
case update:
- for (int i = 0; i < boneCount; i++) {
- Bone bone = (Bone)bones[i];
- if (data.rotate) {
- Vector2 tip = bone.localToWorld(new Vector2(bone.data.length, 0));
- State state = states[i];
- if (!state.last)
- state.last = true;
- else if (state.x != bone.worldX || state.y != bone.worldY) {
- float angleToOldTip = new Vector2(state.tipx, state.tipy).sub(bone.worldX, bone.worldY).angleDeg()
- + state.offset - bone.getWorldRotationX();
- angleToOldTip -= (16384 - (int)(16384.499999999996 - angleToOldTip / 360)) * 360;
- state.offset = linear.apply(0, angleToOldTip, data.inertia);
-// if (angleToOldTip > 0.0001f || angleToOldTip < -0.0001f) //
-// System.out.println(angleToOldTip);
-// if (applyShear) {
-// if (rotationOffset > 0)
-// rotationOffset = Math.max(0, rotationOffset - shearOffset);
-// else
-// rotationOffset = Math.min(0, rotationOffset - shearOffset);
-// }
+ remaining += Math.max(skeleton.time - lastTime, 0);
+ lastTime = skeleton.time;
+
+ float length = bone.data.length, br = atan2(bone.c, bone.a);
+ float wind = data.wind, gravity = data.gravity, strength = data.strength, friction = data.friction, mass = data.mass;
+ float damping = data.damping, step = data.step;
+ boolean angle = rotateOrShear || scaleX;
+ float cos = 0, sin = 0;
+ while (remaining >= step) {
+ remaining -= step;
+ if (x) {
+ xVelocity += (wind - xOffset * strength - friction * xVelocity) * mass;
+ xOffset += xVelocity * step;
+ xVelocity *= damping;
+ }
+ if (y) {
+ yVelocity += (-gravity - yOffset * strength - friction * yVelocity) * mass;
+ yOffset += yVelocity * step;
+ yVelocity *= damping;
+ }
+ if (angle) {
+ float r = br + rotateOffset * degRad;
+ cos = cos(r);
+ sin = sin(r);
+ }
+ if (rotateOrShear) {
+ rotateVelocity += (length * (-wind * sin - gravity * cos) - rotateOffset * strength - friction * rotateVelocity)
+ * mass;
+ rotateOffset += rotateVelocity * step;
+ rotateVelocity *= damping;
+ }
+ if (scaleX) {
+ scaleVelocity += (wind * cos - gravity * sin - scaleOffset * strength - friction * scaleVelocity) * mass;
+ scaleOffset += scaleVelocity * step;
+ scaleVelocity *= damping;
+ }
+ }
+
+ float bx = bone.worldX, by = bone.worldY;
+ if (reset)
+ reset = false;
+ else {
+ float i = data.inertia;
+ if (x) {
+ xOffset += (beforeX - bx) * i;
+ bone.worldX += xOffset * mix;
+ }
+ if (y) {
+ yOffset += (beforeY - by) * i;
+ bone.worldY += yOffset * mix;
+ }
+ if (rotateOrShear) {
+ if (length == 0)
+ rotateOffset = 0;
+ else {
+ float r = (atan2(afterY - bone.worldY + tipY, afterX - bone.worldX + tipX) - br) * radDeg;
+ rotateOffset += (r - (16384 - (int)(16384.499999999996 - r / 360)) * 360) * i;
+ }
+ }
+ if (scaleX) {
+ if (length == 0)
+ scaleOffset = 0;
+ else {
+ float r = br + rotateOffset * degRad;
+ scaleOffset += ((afterX - bone.worldX) * cos(r) + (afterY - bone.worldY) * sin(r)) * i / length;
}
- tip = bone.localToWorld(new Vector2(bone.data.length, 0));
-// if (bone.worldX!=271.64316f)
-// System.out.println(bone.worldX);
- if (bone.worldY != 662.5888f) System.out.println(bone.worldY);
-// System.out.println(bone.worldY);
- state.x = bone.worldX;
- state.y = bone.worldY;
- state.tipx = tip.x;
- state.tipy = tip.y;
}
- // BOZO - Update physics x, y, scaleX, shearX.
}
+ beforeX = bx;
+ beforeY = by;
+ afterX = bone.worldX;
+ afterY = bone.worldY;
+ tipX = length * bone.a;
+ tipY = length * bone.c;
+ break;
+ case pose:
+ if (x) bone.worldX += xOffset * mix;
+ if (y) bone.worldY += yOffset * mix;
}
- boolean angle = data.rotate || data.scaleX || data.shearX;
-
- remaining += Math.max(skeleton.time - lastTime, 0);
- lastTime = skeleton.time;
-
- float step = 0.016f; // BOZO - Keep fixed step? Make it configurable?
- float cos = 0, sin = 0;
- while (remaining >= step) {
- remaining -= step;
-
- for (int i = 0; i < boneCount; i++) {
- Bone bone = (Bone)bones[i];
- if (angle) {
- float r = bone.getWorldRotationX() * degRad;
- cos = (float)Math.cos(r);
- sin = (float)Math.sin(r);
- }
+ if (rotateOrShear) {
+ float r = rotateOffset * mix;
+ if (data.shearX) {
if (data.rotate) {
- State state = states[i];
- // BOZO - Keep length affecting rotation? Calculate world length?
- float windForce = bone.data.length * 0.5f * (-data.wind * sin - data.gravity * cos);
- float springForce = state.offset * data.strength;
- float frictionForce = data.friction * state.velocity;
- state.velocity += (windForce - springForce - frictionForce) / data.mass;
- state.offset += state.velocity * step;
- state.velocity *= data.damping;
+ r *= 0.5f;
+ bone.rotateWorld(r);
}
- }
+ float t = (float)Math.tan(r * degRad);
+ bone.b += bone.a * t;
+ bone.d += bone.c * t;
+ } else
+ bone.rotateWorld(r);
}
-
- if (mix == 1) {
- for (int i = 0; i < boneCount; i++) {
- Bone bone = (Bone)bones[i];
- if (angle) {
- float r = bone.getWorldRotationX() * degRad;
- cos = (float)Math.cos(r);
- sin = (float)Math.sin(r);
- }
- if (data.rotate) {
- State state = states[i];
- bone.rotateWorld(state.offset);
- bone.updateAppliedTransform();
- }
- }
- } else {
- // BOZO - PhysicsConstraint mix.
+ if (scaleX) {
+ float s = 1 + scaleOffset * mix;
+ bone.a *= s;
+ bone.c *= s;
}
+ bone.updateAppliedTransform();
}
- public void step () {
- // BOZO - PhysicsConstraint#step.
+ /** The bone constrained by this physics constraint. */
+ public Bone getBone () {
+ return bone;
}
- /** The bones that will be modified by this physics constraint. */
- public Array getBones () {
- return bones;
+ public void setBone (Bone bone) {
+ this.bone = bone;
}
/** A percentage (0-1) that controls the mix between the constrained and unconstrained poses. */
@@ -241,9 +241,4 @@ public class PhysicsConstraint implements Updatable {
public String toString () {
return data.name;
}
-
- static class State {
- boolean last;
- float x, y, tipx, tipy, offset, velocity;
- }
}
diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/PhysicsConstraintData.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/PhysicsConstraintData.java
index 53ac05c7e..710e4f01b 100644
--- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/PhysicsConstraintData.java
+++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/PhysicsConstraintData.java
@@ -1,16 +1,16 @@
/******************************************************************************
* Spine Runtimes License Agreement
- * Last updated September 24, 2021. Replaces all prior versions.
+ * Last updated July 28, 2023. Replaces all prior versions.
*
- * Copyright (c) 2013-2021, Esoteric Software LLC
+ * Copyright (c) 2013-2023, 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,
+ * 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.
@@ -23,20 +23,18 @@
* (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.
+ * (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.badlogic.gdx.utils.Array;
-
/** Stores the setup pose for a {@link PhysicsConstraint}.
*
* See Physics constraints in the Spine User Guide. */
public class PhysicsConstraintData extends ConstraintData {
- final Array bones = new Array();
- float speed = 1, mass = 1; // BOZO - Keep speed?
+ BoneData bone;
+ float step = 1 / 60f, mass = 1;
float strength, friction, damping, inertia, wind, gravity, mix;
boolean x, y, rotate, scaleX, shearX;
@@ -44,17 +42,21 @@ public class PhysicsConstraintData extends ConstraintData {
super(name);
}
- /** The bones that are constrained by this physics constraint. */
- public Array getBones () {
- return bones;
+ /** The bone constrained by this physics constraint. */
+ public BoneData getBone () {
+ return bone;
}
- public float getSpeed () {
- return speed;
+ public void setBone (BoneData bone) {
+ this.bone = bone;
}
- public void setSpeed (float speed) {
- this.speed = speed;
+ public float getStep () {
+ return step;
+ }
+
+ public void setStep (float step) {
+ this.step = step;
}
public float getMass () {
diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Skeleton.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Skeleton.java
index cf57df9fe..1668d42e9 100644
--- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Skeleton.java
+++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Skeleton.java
@@ -341,25 +341,16 @@ public class Skeleton {
constraint.active = !constraint.data.skinRequired || (skin != null && skin.constraints.contains(constraint.data, true));
if (!constraint.active) return;
- Object[] constrained = constraint.bones.items;
- int boneCount = constraint.bones.size;
- for (int i = 0; i < boneCount; i++) {
- if (((Bone)constrained[i]).active) {
- constraint.active = true;
- break;
- }
- }
+ Bone bone = constraint.bone;
+ constraint.active = bone.active;
if (!constraint.active) return;
- for (int i = 0; i < boneCount; i++)
- sortBone((Bone)constrained[i]);
+ sortBone(bone);
updateCache.add(constraint);
- for (int i = 0; i < boneCount; i++)
- sortReset(((Bone)constrained[i]).children);
- for (int i = 0; i < boneCount; i++)
- ((Bone)constrained[i]).sorted = true;
+ sortReset(bone.children);
+ bone.sorted = true;
}
private void sortBone (Bone bone) {
@@ -839,13 +830,13 @@ public class Skeleton {
/** Physics are not updated or applied. */
none,
- /** Physics are not updated but the pose from physics is applied. */
- pose,
+ /** Physics are reset to the current pose. */
+ reset,
/** Physics are updated and the pose from physics is applied. */
update,
- /** Physics are reset to the current pose. */
- reset
+ /** Physics are not updated but the pose from physics is applied. */
+ pose
}
}