diff --git a/spine-csharp/src/Skeleton.cs b/spine-csharp/src/Skeleton.cs index 7ff11eb60..71b9dbcef 100644 --- a/spine-csharp/src/Skeleton.cs +++ b/spine-csharp/src/Skeleton.cs @@ -38,6 +38,7 @@ namespace Spine { internal ExposedList ikConstraints; internal ExposedList transformConstraints; internal ExposedList pathConstraints; + internal ExposedList springConstraints; internal ExposedList updateCache = new ExposedList(); internal Skin skin; internal float r = 1, g = 1, b = 1, a = 1; @@ -51,6 +52,7 @@ namespace Spine { public ExposedList DrawOrder { get { return drawOrder; } } public ExposedList IkConstraints { get { return ikConstraints; } } public ExposedList PathConstraints { get { return pathConstraints; } } + public ExposedList SpringConstraints { get { return springConstraints; } } public ExposedList TransformConstraints { get { return transformConstraints; } } public Skin Skin { @@ -118,6 +120,10 @@ namespace Spine { foreach (PathConstraintData pathConstraintData in data.pathConstraints) pathConstraints.Add(new PathConstraint(pathConstraintData, this)); + springConstraints = new ExposedList(data.springConstraints.Count); + foreach (SpringConstraintData springConstraintData in data.springConstraints) + springConstraints.Add(new SpringConstraint(springConstraintData, this)); + UpdateCache(); } @@ -163,6 +169,10 @@ namespace Spine { foreach (PathConstraint pathConstraint in skeleton.pathConstraints) pathConstraints.Add(new PathConstraint(pathConstraint, this)); + springConstraints = new ExposedList(skeleton.springConstraints.Count); + foreach (SpringConstraint springConstraint in skeleton.springConstraints) + springConstraints.Add(new SpringConstraint(springConstraint, this)); + skin = skeleton.skin; r = skeleton.r; g = skeleton.g; @@ -199,11 +209,13 @@ namespace Spine { } } - int ikCount = this.ikConstraints.Count, transformCount = this.transformConstraints.Count, pathCount = this.pathConstraints.Count; + int ikCount = this.ikConstraints.Count, transformCount = this.transformConstraints.Count, pathCount = this.pathConstraints.Count, + springCount = this.springConstraints.Count; IkConstraint[] ikConstraints = this.ikConstraints.Items; TransformConstraint[] transformConstraints = this.transformConstraints.Items; PathConstraint[] pathConstraints = this.pathConstraints.Items; - int constraintCount = ikCount + transformCount + pathCount; + SpringConstraint[] springConstraints = this.springConstraints.Items; + int constraintCount = ikCount + transformCount + pathCount + springCount; for (int i = 0; i < constraintCount; i++) { for (int ii = 0; ii < ikCount; ii++) { IkConstraint constraint = ikConstraints[ii]; @@ -226,6 +238,13 @@ namespace Spine { goto continue_outer; } } + for (int ii = 0; ii < springCount; ii++) { + SpringConstraint constraint = springConstraints[ii]; + if (constraint.data.order == i) { + SortSpringConstraint(constraint); + goto continue_outer; + } + } continue_outer: { } } @@ -336,6 +355,23 @@ namespace Spine { } } + private void SortSpringConstraint (SpringConstraint constraint) { + constraint.active = !constraint.data.skinRequired || (skin != null && skin.constraints.Contains(constraint.data)); + if (!constraint.active) return; + + Object[] constrained = constraint.bones.Items; + int boneCount = constraint.bones.Count; + for (int i = 0; i < boneCount; i++) + SortBone((Bone)constrained[i]); + + 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; + } + private void SortBone (Bone bone) { if (bone.sorted) return; Bone parent = bone.parent; @@ -454,6 +490,20 @@ namespace Spine { constraint.mixX = data.mixX; constraint.mixY = data.mixY; } + + SpringConstraint[] springConstraints = this.springConstraints.Items; + for (int i = 0, n = this.springConstraints.Count; i < n; i++) { + SpringConstraint constraint = springConstraints[i]; + SpringConstraintData data = constraint.data; + constraint.mix = data.mix; + constraint.friction = data.friction; + constraint.gravity = data.gravity; + constraint.wind = data.wind; + constraint.stiffness = data.stiffness; + constraint.damping = data.damping; + constraint.rope = data.rope; + constraint.stretch = data.stretch; + } } public void SetSlotsToSetupPose () { @@ -607,6 +657,19 @@ namespace Spine { return null; } + /// Finds a spring constraint by comparing each spring constraint's name. It is more efficient to cache the results of this + /// method than to call it repeatedly. + /// May be null. + public SpringConstraint FindSpringConstraint (String constraintName) { + if (constraintName == null) throw new ArgumentNullException("constraintName", "constraintName cannot be null."); + SpringConstraint[] springConstraints = this.springConstraints.Items; + for (int i = 0, n = this.springConstraints.Count; i < n; i++) { + SpringConstraint constraint = springConstraints[i]; + if (constraint.data.name.Equals(constraintName)) return constraint; + } + return null; + } + /// Returns the axis aligned bounding box (AABB) of the region and mesh attachments for the current pose. /// The horizontal distance between the skeleton origin and the left side of the AABB. /// The vertical distance between the skeleton origin and the bottom side of the AABB. diff --git a/spine-csharp/src/SkeletonData.cs b/spine-csharp/src/SkeletonData.cs index 9039073c4..c8d367be8 100644 --- a/spine-csharp/src/SkeletonData.cs +++ b/spine-csharp/src/SkeletonData.cs @@ -43,6 +43,7 @@ namespace Spine { internal ExposedList ikConstraints = new ExposedList(); internal ExposedList transformConstraints = new ExposedList(); internal ExposedList pathConstraints = new ExposedList(); + internal ExposedList springConstraints = new ExposedList(); internal float x, y, width, height; internal string version, hash; @@ -74,6 +75,7 @@ namespace Spine { public ExposedList IkConstraints { get { return ikConstraints; } set { ikConstraints = value; } } public ExposedList TransformConstraints { get { return transformConstraints; } set { transformConstraints = value; } } public ExposedList PathConstraints { get { return pathConstraints; } set { pathConstraints = value; } } + public ExposedList SpringConstraints { get { return springConstraints; } set { springConstraints = value; } } public float X { get { return x; } set { x = value; } } public float Y { get { return y; } set { y = value; } } @@ -200,6 +202,23 @@ namespace Spine { return null; } + // --- Spring constraints + + /// + /// Finds a spring constraint by comparing each spring constraint's name. It is more efficient to cache the results of this + /// method than to call it multiple times. + /// + /// May be null. + public SpringConstraintData FindSpringConstraint (String constraintName) { + if (constraintName == null) throw new ArgumentNullException("constraintName", "constraintName cannot be null."); + Object[] springConstraints = this.springConstraints.Items; + for (int i = 0, n = this.springConstraints.Count; i < n; i++) { + SpringConstraintData constraint = (SpringConstraintData)springConstraints[i]; + if (constraint.name.Equals(constraintName)) return constraint; + } + return null; + } + // --- override public string ToString () { diff --git a/spine-csharp/src/SpringConstraint.cs b/spine-csharp/src/SpringConstraint.cs new file mode 100644 index 000000000..a775af5c1 --- /dev/null +++ b/spine-csharp/src/SpringConstraint.cs @@ -0,0 +1,105 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated September 24, 2021. Replaces all prior versions. + * + * Copyright (c) 2013-2021, 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. + *****************************************************************************/ + +using System; + +namespace Spine { + /// + /// Stores the current pose for a spring constraint. A spring constraint applies physics to bones. + /// + /// See Spring constraints in the Spine User Guide. + /// + public class SpringConstraint : IUpdatable { + internal readonly SpringConstraintData data; + internal readonly ExposedList bones; + // BOZO! - stiffness -> strength. stiffness, damping, rope, stretch -> move to spring. + internal float mix, friction, gravity, wind, stiffness, damping; + internal bool rope, stretch; + + internal bool active; + + public SpringConstraint (SpringConstraintData data, Skeleton skeleton) { + if (data == null) throw new ArgumentNullException("data", "data cannot be null."); + if (skeleton == null) throw new ArgumentNullException("skeleton", "skeleton cannot be null."); + this.data = data; + mix = data.mix; + friction = data.friction; + gravity = data.gravity; + wind = data.wind; + stiffness = data.stiffness; + damping = data.damping; + rope = data.rope; + stretch = data.stretch; + + bones = new ExposedList(data.Bones.Count); + foreach (BoneData boneData in data.bones) + bones.Add(skeleton.bones.Items[boneData.index]); + } + + /// Copy constructor. + public SpringConstraint (SpringConstraint constraint, Skeleton skeleton) { + if (constraint == null) throw new ArgumentNullException("constraint", "constraint cannot be null."); + if (skeleton == null) throw new ArgumentNullException("skeleton", "skeleton cannot be null."); + data = constraint.data; + bones = new ExposedList(constraint.bones.Count); + foreach (Bone bone in constraint.bones) + bones.Add(skeleton.bones.Items[bone.data.index]); + mix = constraint.mix; + friction = constraint.friction; + gravity = constraint.gravity; + wind = constraint.wind; + stiffness = constraint.stiffness; + damping = constraint.damping; + rope = constraint.rope; + stretch = constraint.stretch; + } + + /// Applies the constraint to the constrained bones. + public void Update () { + + } + + /// A percentage (0-1) that controls the mix between the constrained and unconstrained poses. + public float Mix { get { return mix; } set { mix = value; } } + public float Friction { get { return friction; } set { friction = value; } } + public float Gravity { get { return gravity; } set { gravity = value; } } + public float Wind { get { return wind; } set { wind = value; } } + public float Stiffness { get { return stiffness; } set { stiffness = value; } } + public float Damping { get { return damping; } set { damping = value; } } + public bool Rope { get { return rope; } set { rope = value; } } + public bool Stretch { get { return stretch; } set { stretch = value; } } + public bool Active { get { return active; } } + /// The spring constraint's setup pose data. + public SpringConstraintData Data { get { return data; } } + + override public string ToString () { + return data.name; + } + } +} diff --git a/spine-csharp/src/SpringConstraint.cs.meta b/spine-csharp/src/SpringConstraint.cs.meta new file mode 100644 index 000000000..e06a49dfe --- /dev/null +++ b/spine-csharp/src/SpringConstraint.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e2816491d178b3b4986920107586ce55 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/spine-csharp/src/SpringConstraintData.cs b/spine-csharp/src/SpringConstraintData.cs new file mode 100644 index 000000000..787159aa1 --- /dev/null +++ b/spine-csharp/src/SpringConstraintData.cs @@ -0,0 +1,59 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated September 24, 2021. Replaces all prior versions. + * + * Copyright (c) 2013-2021, 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. + *****************************************************************************/ + +using System; + +namespace Spine { + /// + /// Stores the setup pose for a . + /// + /// See Spring constraints in the Spine User Guide. + /// + public class SpringConstraintData : ConstraintData { + internal ExposedList bones = new ExposedList(); + internal float mix, friction, gravity, wind, stiffness, damping; + internal bool rope, stretch; + + public SpringConstraintData (string name) : base(name) { + } + + /// The bones that are constrained by this spring constraint. + public ExposedList Bones { get { return bones; } } + + /// A percentage (0-1) that controls the mix between the constrained and unconstrained poses. + public float Mix { get { return mix; } set { mix = value; } } + public float Friction { get { return friction; } set { friction = value; } } + public float Gravity { get { return gravity; } set { gravity = value; } } + public float Wind { get { return wind; } set { wind = value; } } + public float Stiffness { get { return stiffness; } set { stiffness = value; } } + public float Damping { get { return damping; } set { damping = value; } } + public bool Rope { get { return rope; } set { rope = value; } } + public bool Stretch { get { return stretch; } set { stretch = value; } } + } +} diff --git a/spine-csharp/src/SpringConstraintData.cs.meta b/spine-csharp/src/SpringConstraintData.cs.meta new file mode 100644 index 000000000..d883a6d05 --- /dev/null +++ b/spine-csharp/src/SpringConstraintData.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 438688f6194e6dc40953a23d05d48e1a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: