From 1e820253d1b8cdca2403339841155badd30cccab Mon Sep 17 00:00:00 2001 From: Nathan Sweet Date: Tue, 18 Oct 2022 12:20:14 -0400 Subject: [PATCH] [libgdx] Physics update. --- .../spine/PhysicsConstraint.java | 213 ++++++++++++++---- .../spine/PhysicsConstraintData.java | 210 ++++------------- .../com/esotericsoftware/spine/Skeleton.java | 2 +- .../spine/SkeletonRendererDebug.java | 4 +- .../spine/SkeletonViewerUI.java | 12 +- 5 files changed, 218 insertions(+), 223 deletions(-) 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 d36ec884a..cddd40793 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/PhysicsConstraint.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/PhysicsConstraint.java @@ -29,12 +29,13 @@ package com.esotericsoftware.spine; +import static com.esotericsoftware.spine.utils.SpineUtils.*; + import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.utils.Array; +import com.badlogic.gdx.utils.Null; -import com.esotericsoftware.spine.PhysicsConstraintData.Node; import com.esotericsoftware.spine.PhysicsConstraintData.NodeData; -import com.esotericsoftware.spine.PhysicsConstraintData.Spring; import com.esotericsoftware.spine.PhysicsConstraintData.SpringData; /** Stores the current pose for a physics constraint. A physics constraint applies physics to bones. @@ -44,7 +45,7 @@ public class PhysicsConstraint implements Updatable { final PhysicsConstraintData data; final Array nodes; final Array springs; - float mix, length, strength, damping, gravity, wind; + float friction, gravity, wind, length, stiffness, damping, mix; boolean active; @@ -66,12 +67,13 @@ public class PhysicsConstraint implements Updatable { for (SpringData springData : data.springs) springs.add(new Spring(springData, this, skeleton)); - mix = data.mix; - length = data.length; - strength = data.strength; - damping = data.damping; + friction = data.friction; gravity = data.gravity; wind = data.wind; + length = data.length; + stiffness = data.stiffness; + damping = data.damping; + mix = data.mix; } /** Copy constructor. */ @@ -88,12 +90,13 @@ public class PhysicsConstraint implements Updatable { for (Spring spring : constraint.springs) springs.add(new Spring(spring, this)); - mix = constraint.mix; - length = constraint.length; - strength = constraint.strength; - damping = constraint.damping; + friction = constraint.friction; gravity = constraint.gravity; wind = constraint.wind; + length = constraint.length; + stiffness = constraint.stiffness; + damping = constraint.damping; + mix = constraint.mix; } public void setToSetupPose () { @@ -109,12 +112,13 @@ public class PhysicsConstraint implements Updatable { ((Spring)springs[i]).setToSetupPose(); PhysicsConstraintData data = this.data; - mix = data.mix; - length = data.length; - strength = data.strength; - damping = data.damping; + friction = data.friction; gravity = data.gravity; wind = data.wind; + length = data.length; + stiffness = data.stiffness; + damping = data.damping; + mix = data.mix; } /** Applies the constraint to the constrained bones. */ @@ -177,37 +181,12 @@ public class PhysicsConstraint implements Updatable { return springs; } - /** A percentage (0-1) that controls the mix between the constrained and unconstrained poses. */ - public float getMix () { - return mix; + public float getFriction () { + return friction; } - public void setMix (float mix) { - this.mix = mix; - } - - public float getLength () { - return length; - } - - public void setLength (float length) { - this.length = length; - } - - public float getStrength () { - return strength; - } - - public void setStrength (float strength) { - this.strength = strength; - } - - public float getDamping () { - return damping; - } - - public void setDamping (float damping) { - this.damping = damping; + public void setFriction (float friction) { + this.friction = friction; } public float getGravity () { @@ -226,6 +205,39 @@ public class PhysicsConstraint implements Updatable { this.wind = wind; } + public float getLength () { + return length; + } + + public void setLength (float length) { + this.length = length; + } + + public float getStiffness () { + return stiffness; + } + + public void setStiffness (float stiffness) { + this.stiffness = stiffness; + } + + public float getDamping () { + return damping; + } + + public void setDamping (float damping) { + this.damping = damping; + } + + /** A percentage (0-1) that controls the mix between the constrained and unconstrained poses. */ + public float getMix () { + return mix; + } + + public void setMix (float mix) { + this.mix = mix; + } + public boolean isActive () { return active; } @@ -238,4 +250,117 @@ public class PhysicsConstraint implements Updatable { public String toString () { return data.name; } + + static public class Node { + public final NodeData data; + public @Null Bone parentBone; + public Bone[] bones; + + /** Position relative to the parent bone, or world position if there is no parent bone. */ + public float x, y; + + public float massInverse, vx, vy; + + Node (NodeData data) { // Editor. + this.data = data; + } + + public Node (NodeData data, Skeleton skeleton) { + this.data = data; + + parentBone = data.parentBone == -1 ? null : skeleton.bones.get(data.parentBone); + + bones = new Bone[data.bones.length]; + for (int i = 0, n = bones.length; i < n; i++) + bones[i] = skeleton.bones.get(data.bones[i]); + + setToSetupPose(); + } + + public Node (Node node) { + this.data = node.data; + parentBone = node.parentBone; + bones = new Bone[node.bones.length]; + arraycopy(node.bones, 0, bones, 0, bones.length); + x = node.x; + y = node.y; + vx = node.vx; + vy = node.vy; + } + + public void setToSetupPose () { + x = data.x; + y = data.y; + vx = 0; + vy = 0; + } + + public void update (PhysicsConstraint constraint) { + if (parentBone != null) { + vx = 0; + vy = 0; + return; + } + x += vx; + y += vy; + vx = vx * constraint.friction + constraint.wind; + vy = vy * constraint.friction - constraint.gravity; + } + } + + static public class Spring { + public final SpringData data; + public Node node1, node2; + public Bone[] bones; + public float length, stiffness, damping; + + Spring (SpringData data) { // Editor. + this.data = data; + } + + public Spring (SpringData data, PhysicsConstraint constraint, Skeleton skeleton) { + this.data = data; + + node1 = constraint.nodes.get(data.node1); + node2 = constraint.nodes.get(data.node2); + + bones = new Bone[data.bones.length]; + for (int i = 0, n = bones.length; i < n; i++) + bones[i] = skeleton.bones.get(data.bones[i]); + + setToSetupPose(); + } + + public Spring (Spring spring, PhysicsConstraint constraint) { + this.data = spring.data; + node1 = constraint.nodes.get(data.node1); + node2 = constraint.nodes.get(data.node2); + bones = new Bone[spring.bones.length]; + arraycopy(spring.bones, 0, bones, 0, bones.length); + length = spring.length; + stiffness = spring.stiffness; + damping = spring.damping; + } + + public void setToSetupPose () { + length = data.length; + stiffness = data.stiffness; + damping = data.damping; + } + + public void update () { + float x = node2.x - node1.x, y = node2.y - node1.y, d = (float)Math.sqrt(Math.max(x * x + y * y, 0.00001f)); + if (data.rope && d <= length) return; + x /= d; + y /= d; + float m1 = node1.massInverse, m2 = node2.massInverse; + float i = (damping * (x * (node2.vx - node1.vx) + y * (node2.vy - node1.vy)) + stiffness * (d - length)) / (m1 + m2); + x *= i; + y *= i; + node1.vx += x * m1; + node1.vy += y * m1; + node2.vx -= x * m2; + node2.vy -= y * m2; + } + } } 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 8d14f6d23..f2e55eb12 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/PhysicsConstraintData.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/PhysicsConstraintData.java @@ -29,11 +29,7 @@ package com.esotericsoftware.spine; -import static com.esotericsoftware.spine.utils.SpineUtils.*; - -import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.utils.Array; -import com.badlogic.gdx.utils.Null; /** Stores the setup pose for a {@link PhysicsConstraint}. *

@@ -41,7 +37,7 @@ import com.badlogic.gdx.utils.Null; public class PhysicsConstraintData extends ConstraintData { final Array nodes = new Array(); final Array springs = new Array(); - float mix, length, strength, damping, gravity, wind; + float friction, gravity, wind, length, stiffness, damping, mix; public PhysicsConstraintData (String name) { super(name); @@ -55,37 +51,12 @@ public class PhysicsConstraintData extends ConstraintData { return springs; } - /** A percentage (0-1) that controls the mix between the constrained and unconstrained poses. */ - public float getMix () { - return mix; + public float getFriction () { + return friction; } - public void setMix (float mix) { - this.mix = mix; - } - - public float getLength () { - return length; - } - - public void setLength (float length) { - this.length = length; - } - - public float getStrength () { - return strength; - } - - public void setStrength (float strength) { - this.strength = strength; - } - - public float getDamping () { - return damping; - } - - public void setDamping (float damping) { - this.damping = damping; + public void setFriction (float friction) { + this.friction = friction; } public float getGravity () { @@ -104,150 +75,49 @@ public class PhysicsConstraintData extends ConstraintData { this.wind = wind; } + public float getLength () { + return length; + } + + public void setLength (float length) { + this.length = length; + } + + public float getStiffness () { + return stiffness; + } + + public void setStiffness (float stiffness) { + this.stiffness = stiffness; + } + + public float getDamping () { + return damping; + } + + public void setDamping (float damping) { + this.damping = damping; + } + + /** A percentage (0-1) that controls the mix between the constrained and unconstrained poses. */ + public float getMix () { + return mix; + } + + public void setMix (float mix) { + this.mix = mix; + } + static public class NodeData { public int parentBone = -1; public int[] bones; public float x, y; } - static public class Node { - public final NodeData data; - public @Null Bone parentBone; - public Bone[] bones; - public float x, y, vx, vy, ax, ay; - - Node (NodeData data) { // Editor. - this.data = data; - } - - public Node (NodeData data, Skeleton skeleton) { - this.data = data; - - parentBone = data.parentBone == -1 ? null : skeleton.bones.get(data.parentBone); - - bones = new Bone[data.bones.length]; - for (int i = 0, n = bones.length; i < n; i++) - bones[i] = skeleton.bones.get(data.bones[i]); - - setToSetupPose(); - } - - public Node (Node node) { - this.data = node.data; - parentBone = node.parentBone; - bones = new Bone[node.bones.length]; - arraycopy(node.bones, 0, bones, 0, bones.length); - x = node.x; - y = node.y; - vx = node.vx; - vy = node.vy; - ax = node.ax; - ay = node.ay; - } - - public void setToSetupPose () { - x = data.x; - y = data.y; - vx = 0; - vy = 0; - ax = 0; - ay = 0; - } - - public void update (PhysicsConstraint constraint) { - if (parentBone != null) { - vx = 0; - vy = 0; - ax = 0; - ay = 0; - return; - } - float friction = 0.98f; - vx += ax - constraint.wind / friction; - vy += ay - constraint.gravity / friction; - ax = 0; - ay = 0; - x += vx; - y += vy; - vx *= friction; - vy *= friction; - } - } - static public class SpringData { public int node1, node2; public int[] bones; - public float length, strength, damping; - public boolean rope, stretch; - } - - static public class Spring { - public final SpringData data; - public Node node1, node2; - public Bone[] bones; - public float length, strength, damping; - - Spring (SpringData data) { // Editor. - this.data = data; - } - - public Spring (SpringData data, PhysicsConstraint constraint, Skeleton skeleton) { - this.data = data; - - node1 = constraint.nodes.get(data.node1); - node2 = constraint.nodes.get(data.node2); - - bones = new Bone[data.bones.length]; - for (int i = 0, n = bones.length; i < n; i++) - bones[i] = skeleton.bones.get(data.bones[i]); - - setToSetupPose(); - } - - public Spring (Spring spring, PhysicsConstraint constraint) { - this.data = spring.data; - node1 = constraint.nodes.get(data.node1); - node2 = constraint.nodes.get(data.node2); - bones = new Bone[spring.bones.length]; - arraycopy(spring.bones, 0, bones, 0, bones.length); - length = spring.length; - strength = spring.strength; - damping = spring.damping; - } - - public void setToSetupPose () { - length = data.length; - strength = data.strength; - damping = data.damping; - } - - public void update () { - float strength = 1f; - - float x = node2.x - node1.x, y = node2.y - node1.y; - float d = (float)Math.sqrt(Math.max(x * x + y * y, 0.00001f)); - if (data.rope && d <= length) return; - - Vector2 unit_vector = new Vector2(x / d, y / d); - float distance_error = unit_vector.dot(x, y) - length; - float distance_impulse = 0.33f * strength * distance_error; - - float vx = node2.vx - node1.vx, vy = node2.vy - node1.vy; - float velocity_error = unit_vector.dot(vx, vy); - float velocity_impulse = 0.2f * velocity_error; - - float impulse = -(distance_impulse + velocity_impulse); - Vector2 f = unit_vector.scl(impulse); - - node1.ax -= f.x - vx * damping; - node1.ay -= f.y - vy * damping; - node2.ax += f.x - vx * damping; - node2.ay += f.y - vy * damping; - - if (!data.stretch && node2.parentBone == null) { - node2.x = node1.x + length * x / d; - node2.y = node1.y + length * y / d; - } - } + public float length, stiffness, damping; + public boolean rope; } } 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 91bf4a147..6a16d416a 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Skeleton.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Skeleton.java @@ -37,7 +37,7 @@ import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.FloatArray; import com.badlogic.gdx.utils.Null; -import com.esotericsoftware.spine.PhysicsConstraintData.Node; +import com.esotericsoftware.spine.PhysicsConstraint.Node; import com.esotericsoftware.spine.Skin.SkinEntry; import com.esotericsoftware.spine.attachments.Attachment; import com.esotericsoftware.spine.attachments.MeshAttachment; diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonRendererDebug.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonRendererDebug.java index a29b29512..84d37b001 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonRendererDebug.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonRendererDebug.java @@ -38,8 +38,8 @@ import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.FloatArray; -import com.esotericsoftware.spine.PhysicsConstraintData.Node; -import com.esotericsoftware.spine.PhysicsConstraintData.Spring; +import com.esotericsoftware.spine.PhysicsConstraint.Node; +import com.esotericsoftware.spine.PhysicsConstraint.Spring; import com.esotericsoftware.spine.attachments.Attachment; import com.esotericsoftware.spine.attachments.BoundingBoxAttachment; import com.esotericsoftware.spine.attachments.ClippingAttachment; diff --git a/spine-libgdx/spine-skeletonviewer/src/com/esotericsoftware/spine/SkeletonViewerUI.java b/spine-libgdx/spine-skeletonviewer/src/com/esotericsoftware/spine/SkeletonViewerUI.java index 01e227b68..a3770b393 100644 --- a/spine-libgdx/spine-skeletonviewer/src/com/esotericsoftware/spine/SkeletonViewerUI.java +++ b/spine-libgdx/spine-skeletonviewer/src/com/esotericsoftware/spine/SkeletonViewerUI.java @@ -170,16 +170,16 @@ class SkeletonViewerUI { loopCheckbox.setChecked(true); loadScaleSlider.setValue(1); - loadScaleSlider.setSnapToValues(new float[] {0.5f, 1, 1.5f, 2, 2.5f}, 0.09f); + loadScaleSlider.setSnapToValues(0.09f, 0.5f, 1, 1.5f, 2, 2.5f); zoomSlider.setValue(1); - zoomSlider.setSnapToValues(new float[] {1, 2}, 0.30f); + zoomSlider.setSnapToValues(0.30f, 1, 2); xScaleSlider.setValue(1); - xScaleSlider.setSnapToValues(new float[] {-1.5f, -1, -0.5f, 0.5f, 1, 1.5f}, 0.12f); + xScaleSlider.setSnapToValues(0.12f, -1.5f, -1, -0.5f, 0.5f, 1, 1.5f); yScaleSlider.setValue(1); - yScaleSlider.setSnapToValues(new float[] {-1.5f, -1, -0.5f, 0.5f, 1, 1.5f}, 0.12f); + yScaleSlider.setSnapToValues(0.12f, -1.5f, -1, -0.5f, 0.5f, 1, 1.5f); skinList.getSelection().setRequired(false); skinList.getSelection().setToggle(true); @@ -188,10 +188,10 @@ class SkeletonViewerUI { animationList.getSelection().setToggle(true); mixSlider.setValue(0.3f); - mixSlider.setSnapToValues(new float[] {1, 1.5f, 2, 2.5f, 3, 3.5f}, 0.12f); + mixSlider.setSnapToValues(0.12f, 1, 1.5f, 2, 2.5f, 3, 3.5f); speedSlider.setValue(1); - speedSlider.setSnapToValues(new float[] {0.5f, 0.75f, 1, 1.25f, 1.5f, 2, 2.5f}, 0.09f); + speedSlider.setSnapToValues(0.09f, 0.5f, 0.75f, 1, 1.25f, 1.5f, 2, 2.5f); alphaSlider.setValue(1); alphaSlider.setDisabled(true);