From 3ec8830c5c0d08bc02e8c962c3ee1b82cf8d72d2 Mon Sep 17 00:00:00 2001 From: ww-rm Date: Thu, 22 Jan 2026 18:06:19 +0800 Subject: [PATCH 1/4] =?UTF-8?q?=E6=9B=BF=E6=8D=A2=E4=B8=BA3.5.51tag?= =?UTF-8?q?=E4=B8=8B=E5=AE=98=E6=96=B9=E6=BA=90=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../V35/Attachments/ClippingAttachment35.cs | 33 -- .../V35/Attachments/PointAttachment35.cs | 32 -- .../V35/Attachments/RegionAttachment35.cs | 2 +- .../Implementations/V35/SkeletonClipping35.cs | 44 +- Spine/Implementations/V35/Slot35.cs | 8 +- .../Implementations/V35/SpineObjectData35.cs | 2 - SpineRuntimes/SpineRuntime35/Animation.cs | 399 ++++-------------- .../SpineRuntime35/AnimationState.cs | 282 +++++-------- .../SpineRuntime35/AnimationStateData.cs | 16 +- SpineRuntimes/SpineRuntime35/Atlas.cs | 32 +- .../Attachments/AtlasAttachmentLoader.cs | 27 +- .../SpineRuntime35/Attachments/Attachment.cs | 4 +- .../Attachments/AttachmentLoader.cs | 14 +- .../Attachments/AttachmentType.cs | 2 +- .../Attachments/ClippingAttachment.cs | 42 -- .../Attachments/MeshAttachment.cs | 7 +- .../Attachments/PointAttachment.cs | 61 --- .../Attachments/RegionAttachment.cs | 128 +++--- .../Attachments/VertexAttachment.cs | 28 +- SpineRuntimes/SpineRuntime35/BlendMode.cs | 2 +- SpineRuntimes/SpineRuntime35/Bone.cs | 174 ++++---- SpineRuntimes/SpineRuntime35/BoneData.cs | 37 +- SpineRuntimes/SpineRuntime35/Event.cs | 5 +- SpineRuntimes/SpineRuntime35/EventData.cs | 12 +- SpineRuntimes/SpineRuntime35/IConstraint.cs | 37 +- SpineRuntimes/SpineRuntime35/IUpdatable.cs | 2 + SpineRuntimes/SpineRuntime35/IkConstraint.cs | 64 +-- .../SpineRuntime35/IkConstraintData.cs | 39 +- .../SpineRuntime35/PathConstraint.cs | 32 +- SpineRuntimes/SpineRuntime35/Skeleton.cs | 149 ++----- .../SpineRuntime35/SkeletonBinary.cs | 86 +--- .../SpineRuntime35/SkeletonBounds.cs | 15 +- .../SpineRuntime35/SkeletonClipping.cs | 285 ------------- SpineRuntimes/SpineRuntime35/SkeletonData.cs | 65 +-- SpineRuntimes/SpineRuntime35/SkeletonJson.cs | 79 +--- SpineRuntimes/SpineRuntime35/Skin.cs | 6 +- SpineRuntimes/SpineRuntime35/Slot.cs | 9 +- SpineRuntimes/SpineRuntime35/SlotData.cs | 16 +- .../SpineRuntime35/TransformConstraint.cs | 168 +------- .../SpineRuntime35/TransformConstraintData.cs | 12 +- SpineRuntimes/SpineRuntime35/Triangulator.cs | 278 ------------ 41 files changed, 580 insertions(+), 2155 deletions(-) delete mode 100644 Spine/Implementations/V35/Attachments/ClippingAttachment35.cs delete mode 100644 Spine/Implementations/V35/Attachments/PointAttachment35.cs delete mode 100644 SpineRuntimes/SpineRuntime35/Attachments/ClippingAttachment.cs delete mode 100644 SpineRuntimes/SpineRuntime35/Attachments/PointAttachment.cs delete mode 100644 SpineRuntimes/SpineRuntime35/SkeletonClipping.cs delete mode 100644 SpineRuntimes/SpineRuntime35/Triangulator.cs diff --git a/Spine/Implementations/V35/Attachments/ClippingAttachment35.cs b/Spine/Implementations/V35/Attachments/ClippingAttachment35.cs deleted file mode 100644 index d37dae3f..00000000 --- a/Spine/Implementations/V35/Attachments/ClippingAttachment35.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Spine.Implementations.V35; -using Spine.Interfaces; -using Spine.Interfaces.Attachments; -using SpineRuntime35; - -namespace Spine.Implementations.V35.Attachments -{ - internal sealed class ClippingAttachment35(ClippingAttachment innerObject) : - Attachment35(innerObject), - IClippingAttachment - { - private readonly ClippingAttachment _o = innerObject; - - public override ClippingAttachment InnerObject => _o; - - public override int ComputeWorldVertices(ISlot slot, ref float[] worldVertices) - { - if (slot is Slot35 st) - { - var length = _o.WorldVerticesLength; - if (worldVertices.Length < length) worldVertices = new float[length]; - _o.ComputeWorldVertices(st.InnerObject, worldVertices); - return length; - } - throw new ArgumentException($"Invalid slot type. Expected {nameof(Slot35)}, but received {slot.GetType().Name}", nameof(slot)); - } - } -} diff --git a/Spine/Implementations/V35/Attachments/PointAttachment35.cs b/Spine/Implementations/V35/Attachments/PointAttachment35.cs deleted file mode 100644 index 387c4014..00000000 --- a/Spine/Implementations/V35/Attachments/PointAttachment35.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Spine.Implementations.V35; -using Spine.Interfaces; -using Spine.Interfaces.Attachments; -using SpineRuntime35; - -namespace Spine.Implementations.V35.Attachments -{ - internal sealed class PointAttachment35(PointAttachment innerObject) : - Attachment35(innerObject), - IPointAttachment - { - private readonly PointAttachment _o = innerObject; - - public override PointAttachment InnerObject => _o; - - public override int ComputeWorldVertices(ISlot slot, ref float[] worldVertices) - { - if (slot is Slot35 st) - { - if (worldVertices.Length < 2) worldVertices = new float[2]; - _o.ComputeWorldPosition(st.InnerObject.Bone, out worldVertices[0], out worldVertices[1]); - return 2; - } - throw new ArgumentException($"Invalid slot type. Expected {nameof(Slot35)}, but received {slot.GetType().Name}", nameof(slot)); - } - } -} \ No newline at end of file diff --git a/Spine/Implementations/V35/Attachments/RegionAttachment35.cs b/Spine/Implementations/V35/Attachments/RegionAttachment35.cs index 29ebb352..3c946ce2 100644 --- a/Spine/Implementations/V35/Attachments/RegionAttachment35.cs +++ b/Spine/Implementations/V35/Attachments/RegionAttachment35.cs @@ -23,7 +23,7 @@ public override int ComputeWorldVertices(ISlot slot, ref float[] worldVertices) if (slot is Slot35 st) { if (worldVertices.Length < 8) worldVertices = new float[8]; - _o.ComputeWorldVertices(st.InnerObject.Bone, worldVertices, 0); + _o.ComputeWorldVertices(st.InnerObject.Bone, worldVertices); return 8; } throw new ArgumentException($"Invalid slot type. Expected {nameof(Slot35)}, but received {slot.GetType().Name}", nameof(slot)); diff --git a/Spine/Implementations/V35/SkeletonClipping35.cs b/Spine/Implementations/V35/SkeletonClipping35.cs index 17555b62..b73559b6 100644 --- a/Spine/Implementations/V35/SkeletonClipping35.cs +++ b/Spine/Implementations/V35/SkeletonClipping35.cs @@ -12,45 +12,31 @@ namespace Spine.Implementations.V35 { internal sealed class SkeletonClipping35 : ISkeletonClipping { - private readonly SkeletonClipping _o = new(); + public bool IsClipping => false; - public bool IsClipping => _o.IsClipping(); + public float[] ClippedVertices { get; private set; } = []; - public float[] ClippedVertices => _o.ClippedVertices.Items; + public int ClippedVerticesLength { get; private set; } = 0; - public int ClippedVerticesLength => _o.ClippedVertices.Count; + public int[] ClippedTriangles { get; private set; } = []; - public int[] ClippedTriangles => _o.ClippedTriangles.Items; + public int ClippedTrianglesLength { get; private set; } = 0; - public int ClippedTrianglesLength => _o.ClippedTriangles.Count; + public float[] ClippedUVs { get; private set; } = []; - public float[] ClippedUVs => _o.ClippedUVs.Items; + public void ClipEnd(ISlot slot) { } - public void ClipTriangles(float[] vertices, int verticesLength, int[] triangles, int trianglesLength, float[] uvs) - => _o.ClipTriangles(vertices, verticesLength, triangles, trianglesLength, uvs); + public void ClipEnd() { } - public void ClipStart(ISlot slot, IClippingAttachment clippingAttachment) - { - if (slot is Slot35 st && clippingAttachment is Attachments.ClippingAttachment35 att) - { - _o.ClipStart(st.InnerObject, att.InnerObject); - return; - } - throw new ArgumentException($"Received {slot.GetType().Name} {clippingAttachment.GetType().Name}"); - } + public void ClipStart(ISlot slot, IClippingAttachment clippingAttachment) { } - public void ClipEnd(ISlot slot) + public void ClipTriangles(float[] vertices, int verticesLength, int[] triangles, int trianglesLength, float[] uvs) { - if (slot is Slot35 st) - { - _o.ClipEnd(st.InnerObject); - return; - } - throw new ArgumentException($"Received {slot.GetType().Name}", nameof(slot)); + ClippedVertices = vertices.ToArray(); + ClippedVerticesLength = verticesLength; + ClippedTriangles = triangles.ToArray(); + ClippedTrianglesLength = trianglesLength; + ClippedUVs = uvs.ToArray(); } - - public void ClipEnd() => _o.ClipEnd(); - - public override string ToString() => _o.ToString(); } } diff --git a/Spine/Implementations/V35/Slot35.cs b/Spine/Implementations/V35/Slot35.cs index ae6706a5..becd799c 100644 --- a/Spine/Implementations/V35/Slot35.cs +++ b/Spine/Implementations/V35/Slot35.cs @@ -27,10 +27,10 @@ public Slot35(Slot innerObject, SpineObjectData35 data, Bone35 bone) _bone = bone; _blendMode = _o.Data.BlendMode switch { - BlendMode.Normal => SFMLBlendMode.NormalPma, - BlendMode.Additive => SFMLBlendMode.AdditivePma, - BlendMode.Multiply => SFMLBlendMode.MultiplyPma, - BlendMode.Screen => SFMLBlendMode.ScreenPma, + BlendMode.normal => SFMLBlendMode.NormalPma, + BlendMode.additive => SFMLBlendMode.AdditivePma, + BlendMode.multiply => SFMLBlendMode.MultiplyPma, + BlendMode.screen => SFMLBlendMode.ScreenPma, _ => throw new NotImplementedException($"{_o.Data.BlendMode}"), }; } diff --git a/Spine/Implementations/V35/SpineObjectData35.cs b/Spine/Implementations/V35/SpineObjectData35.cs index 23a9a7e1..a4c8b7ec 100644 --- a/Spine/Implementations/V35/SpineObjectData35.cs +++ b/Spine/Implementations/V35/SpineObjectData35.cs @@ -96,10 +96,8 @@ public SpineObjectData35(string skelPath, string atlasPath, TextureLoader textur { RegionAttachment regionAtt => new RegionAttachment35(regionAtt), MeshAttachment meshAtt => new MeshAttachment35(meshAtt), - ClippingAttachment clipAtt => new ClippingAttachment35(clipAtt), BoundingBoxAttachment bbAtt => new BoundingBoxAttachment35(bbAtt), PathAttachment pathAtt => new PathAttachment35(pathAtt), - PointAttachment ptAtt => new PointAttachment35(ptAtt), _ => throw new InvalidOperationException($"Unrecognized attachment type {att.GetType().FullName}") }; } diff --git a/SpineRuntimes/SpineRuntime35/Animation.cs b/SpineRuntimes/SpineRuntime35/Animation.cs index a004cfb0..1c694f71 100644 --- a/SpineRuntimes/SpineRuntime35/Animation.cs +++ b/SpineRuntimes/SpineRuntime35/Animation.cs @@ -37,11 +37,11 @@ public class Animation { internal float duration; internal String name; - public string Name { get { return name; } } + public String Name { get { return name; } } public ExposedList Timelines { get { return timelines; } set { timelines = value; } } public float Duration { get { return duration; } set { duration = value; } } - public Animation (string name, ExposedList timelines, float duration) { + public Animation (String name, ExposedList timelines, float duration) { if (name == null) throw new ArgumentNullException("name", "name cannot be null."); if (timelines == null) throw new ArgumentNullException("timelines", "timelines cannot be null."); this.name = name; @@ -50,8 +50,16 @@ public Animation (string name, ExposedList timelines, float duration) } /// Applies all the animation's timelines to the specified skeleton. - /// - public void Apply (Skeleton skeleton, float lastTime, float time, bool loop, ExposedList events, float alpha, MixPose pose, MixDirection direction) { + /// The skeleton to be posed. + /// The last time the animation was applied. + /// The point in time in the animation to apply to the skeleton. + /// If true, time wraps within the animation duration. + /// Any triggered events are added. May be null. + /// The percentage between this animation's pose and the current pose. + /// If true, the animation is mixed with the setup pose, else it is mixed with the current pose. Passing true when alpha is 1 is slightly more efficient. + /// True when mixing over time toward the setup or current pose, false when mixing toward the keyed pose. Irrelevant when alpha is 1. + /// + public void Apply (Skeleton skeleton, float lastTime, float time, bool loop, ExposedList events, float alpha, bool setupPose, bool mixingOut) { if (skeleton == null) throw new ArgumentNullException("skeleton", "skeleton cannot be null."); if (loop && duration != 0) { @@ -61,7 +69,7 @@ public void Apply (Skeleton skeleton, float lastTime, float time, bool loop, Exp ExposedList timelines = this.timelines; for (int i = 0, n = timelines.Count; i < n; i++) - timelines.Items[i].Apply(skeleton, lastTime, time, events, alpha, pose, direction); + timelines.Items[i].Apply(skeleton, lastTime, time, events, alpha, setupPose, mixingOut); } /// After the first and before the last entry. @@ -105,48 +113,20 @@ internal static int LinearSearch (float[] values, float target, int step) { public interface Timeline { /// Sets the value(s) for the specified time. - /// The skeleton the timeline is being applied to. This provides access to the bones, slots, and other skeleton components the timeline may change. - /// lastTime The time this timeline was last applied. Timelines such as EventTimeline trigger only at specific times rather than every frame. In that case, the timeline triggers everything between lastTime (exclusive) and time (inclusive). - /// The time within the animation. Most timelines find the key before and the key after this time so they can interpolate between the keys. - /// If any events are fired, they are added to this list. Can be null to ignore firing events or if the timeline does not fire events. May be null. - /// alpha 0 applies the current or setup pose value (depending on pose parameter). 1 applies the timeline - /// value. Between 0 and 1 applies a value between the current or setup pose and the timeline value. By adjusting - /// alpha over time, an animation can be mixed in or out. alpha can also be useful to - /// apply animations on top of each other (layered). - /// Controls how mixing is applied when alpha is than 1. - /// Indicates whether the timeline is mixing in or out. Used by timelines which perform instant transitions such as DrawOrderTimeline and AttachmentTimeline. - void Apply (Skeleton skeleton, float lastTime, float time, ExposedList events, float alpha, MixPose pose, MixDirection direction); + /// Any triggered events are added. May be null. + /// True when the timeline is mixed with the setup pose, false when it is mixed with the current pose. Passing true when alpha is 1 is slightly more efficient. + /// True when mixing over time toward the setup or current pose, false when mixing toward the keyed pose. + /// Used for timelines with instant transitions, eg draw order, attachment visibility, scale sign. + void Apply (Skeleton skeleton, float lastTime, float time, ExposedList events, float alpha, bool setupPose, bool mixingOut); int PropertyId { get; } } - /// - /// Controls how a timeline is mixed with the setup or current pose. - /// - public enum MixPose { - /// The timeline value is mixed with the setup pose (the current pose is not used). - Setup, - /// The timeline value is mixed with the current pose. The setup pose is used as the timeline value before the first key, - /// except for timelines which perform instant transitions, such as DrawOrderTimeline or AttachmentTimeline. - Current, - /// The timeline value is mixed with the current pose. No change is made before the first key (the current pose is kept until the first key). - CurrentLayered - } - - /// - /// Indicates whether a timeline's alpha is mixing out over time toward 0 (the setup or current pose) or mixing in toward 1 (the timeline's pose). - /// - public enum MixDirection { - In, - Out - } - internal enum TimelineType { Rotate = 0, Translate, Scale, Shear, // Attachment, Color, Deform, // Event, DrawOrder, // IkConstraint, TransformConstraint, // - PathConstraintPosition, PathConstraintSpacing, PathConstraintMix, // - TwoColor + PathConstraintPosition, PathConstraintSpacing, PathConstraintMix } /// Base class for frames that use an interpolation bezier curve. @@ -162,7 +142,7 @@ public CurveTimeline (int frameCount) { curves = new float[(frameCount - 1) * BEZIER_SIZE]; } - abstract public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList firedEvents, float alpha, MixPose pose, MixDirection direction); + abstract public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList firedEvents, float alpha, bool setupPose, bool mixingOut); abstract public int PropertyId { get; } @@ -258,26 +238,17 @@ public void SetFrame (int frameIndex, float time, float degrees) { frames[frameIndex + ROTATION] = degrees; } - override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList firedEvents, float alpha, MixPose pose, MixDirection direction) { + override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList firedEvents, float alpha, bool setupPose, bool mixingOut) { Bone bone = skeleton.bones.Items[boneIndex]; float[] frames = this.frames; if (time < frames[0]) { - switch (pose) { - case MixPose.Setup: - bone.rotation = bone.data.rotation; - return; - case MixPose.Current: - float rr = bone.data.rotation - bone.rotation; - rr -= (16384 - (int)(16384.499999999996 - rr / 360)) * 360; - bone.rotation += rr * alpha; - return; - } + if (setupPose) bone.rotation = bone.data.rotation; return; } if (time >= frames[frames.Length - ENTRIES]) { // Time is after last frame. - if (pose == MixPose.Setup) { + if (setupPose) { bone.rotation = bone.data.rotation + frames[frames.Length + PREV_ROTATION] * alpha; } else { float rr = bone.data.rotation + frames[frames.Length + PREV_ROTATION] - bone.rotation; @@ -296,7 +267,7 @@ override public void Apply (Skeleton skeleton, float lastTime, float time, Expos float r = frames[frame + ROTATION] - prevRotation; r -= (16384 - (int)(16384.499999999996 - r / 360)) * 360; r = prevRotation + r * percent; - if (pose == MixPose.Setup) { + if (setupPose) { r -= (16384 - (int)(16384.499999999996 - r / 360)) * 360; bone.rotation = bone.data.rotation + r * alpha; } else { @@ -335,20 +306,14 @@ public void SetFrame (int frameIndex, float time, float x, float y) { frames[frameIndex + Y] = y; } - override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList firedEvents, float alpha, MixPose pose, MixDirection direction) { + override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList firedEvents, float alpha, bool setupPose, bool mixingOut) { Bone bone = skeleton.bones.Items[boneIndex]; float[] frames = this.frames; if (time < frames[0]) { - switch (pose) { - case MixPose.Setup: + if (setupPose) { bone.x = bone.data.x; bone.y = bone.data.y; - return; - case MixPose.Current: - bone.x += (bone.data.x - bone.x) * alpha; - bone.y += (bone.data.y - bone.y) * alpha; - return; } return; } @@ -369,7 +334,7 @@ override public void Apply (Skeleton skeleton, float lastTime, float time, Expos x += (frames[frame + X] - x) * percent; y += (frames[frame + Y] - y) * percent; } - if (pose == MixPose.Setup) { + if (setupPose) { bone.x = bone.data.x + x * alpha; bone.y = bone.data.y + y * alpha; } else { @@ -388,20 +353,14 @@ public ScaleTimeline (int frameCount) : base(frameCount) { } - override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList firedEvents, float alpha, MixPose pose, MixDirection direction) { + override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList firedEvents, float alpha, bool setupPose, bool mixingOut) { Bone bone = skeleton.bones.Items[boneIndex]; float[] frames = this.frames; if (time < frames[0]) { - switch (pose) { - case MixPose.Setup: + if (setupPose) { bone.scaleX = bone.data.scaleX; bone.scaleY = bone.data.scaleY; - return; - case MixPose.Current: - bone.scaleX += (bone.data.scaleX - bone.scaleX) * alpha; - bone.scaleY += (bone.data.scaleY - bone.scaleY) * alpha; - return; } return; } @@ -427,7 +386,7 @@ override public void Apply (Skeleton skeleton, float lastTime, float time, Expos bone.scaleY = y; } else { float bx, by; - if (pose == MixPose.Setup) { + if (setupPose) { bx = bone.data.scaleX; by = bone.data.scaleY; } else { @@ -435,7 +394,7 @@ override public void Apply (Skeleton skeleton, float lastTime, float time, Expos by = bone.scaleY; } // Mixing out uses sign of setup or current pose, else use sign of key. - if (direction == MixDirection.Out) { + if (mixingOut) { x = Math.Abs(x) * Math.Sign(bx); y = Math.Abs(y) * Math.Sign(by); } else { @@ -457,19 +416,13 @@ public ShearTimeline (int frameCount) : base(frameCount) { } - override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList firedEvents, float alpha, MixPose pose, MixDirection direction) { + override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList firedEvents, float alpha, bool setupPose, bool mixingOut) { Bone bone = skeleton.bones.Items[boneIndex]; float[] frames = this.frames; if (time < frames[0]) { - switch (pose) { - case MixPose.Setup: + if (setupPose) { bone.shearX = bone.data.shearX; bone.shearY = bone.data.shearY; - return; - case MixPose.Current: - bone.shearX += (bone.data.shearX - bone.shearX) * alpha; - bone.shearY += (bone.data.shearY - bone.shearY) * alpha; - return; } return; } @@ -490,7 +443,7 @@ override public void Apply (Skeleton skeleton, float lastTime, float time, Expos x = x + (frames[frame + X] - x) * percent; y = y + (frames[frame + Y] - y) * percent; } - if (pose == MixPose.Setup) { + if (setupPose) { bone.shearX = bone.data.shearX + x * alpha; bone.shearY = bone.data.shearY + y * alpha; } else { @@ -530,24 +483,16 @@ public void SetFrame (int frameIndex, float time, float r, float g, float b, flo frames[frameIndex + A] = a; } - override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList firedEvents, float alpha, MixPose pose, MixDirection direction) { + override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList firedEvents, float alpha, bool setupPose, bool mixingOut) { Slot slot = skeleton.slots.Items[slotIndex]; float[] frames = this.frames; if (time < frames[0]) { - var slotData = slot.data; - switch (pose) { - case MixPose.Setup: + if (setupPose) { + var slotData = slot.data; slot.r = slotData.r; slot.g = slotData.g; slot.b = slotData.b; slot.a = slotData.a; - return; - case MixPose.Current: - slot.r += (slot.r - slotData.r) * alpha; - slot.g += (slot.g - slotData.g) * alpha; - slot.b += (slot.b - slotData.b) * alpha; - slot.a += (slot.a - slotData.a) * alpha; - return; } return; } @@ -582,164 +527,23 @@ override public void Apply (Skeleton skeleton, float lastTime, float time, Expos slot.a = a; } else { float br, bg, bb, ba; - if (pose == MixPose.Setup) { - br = slot.data.r; - bg = slot.data.g; - bb = slot.data.b; - ba = slot.data.a; - } else { - br = slot.r; - bg = slot.g; - bb = slot.b; - ba = slot.a; - } - slot.r = br + ((r - br) * alpha); - slot.g = bg + ((g - bg) * alpha); - slot.b = bb + ((b - bb) * alpha); - slot.a = ba + ((a - ba) * alpha); - } - } - } - - public class TwoColorTimeline : CurveTimeline { - public const int ENTRIES = 8; - protected const int PREV_TIME = -8, PREV_R = -7, PREV_G = -6, PREV_B = -5, PREV_A = -4; - protected const int PREV_R2 = -3, PREV_G2 = -2, PREV_B2 = -1; - protected const int R = 1, G = 2, B = 3, A = 4, R2 = 5, G2 = 6, B2 = 7; - - internal float[] frames; // time, r, g, b, a, r2, g2, b2, ... - public float[] Frames { get { return frames; } } - - internal int slotIndex; - public int SlotIndex { - get { return slotIndex; } - set { - if (value < 0) throw new ArgumentOutOfRangeException("index must be >= 0."); - slotIndex = value; - } - } - - override public int PropertyId { - get { return ((int)TimelineType.TwoColor << 24) + slotIndex; } - } - - public TwoColorTimeline (int frameCount) : - base(frameCount) { - frames = new float[frameCount * ENTRIES]; - } - - /// Sets the time and value of the specified keyframe. - public void SetFrame (int frameIndex, float time, float r, float g, float b, float a, float r2, float g2, float b2) { - frameIndex *= ENTRIES; - frames[frameIndex] = time; - frames[frameIndex + R] = r; - frames[frameIndex + G] = g; - frames[frameIndex + B] = b; - frames[frameIndex + A] = a; - frames[frameIndex + R2] = r2; - frames[frameIndex + G2] = g2; - frames[frameIndex + B2] = b2; - } - - override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList firedEvents, float alpha, MixPose pose, MixDirection direction) { - Slot slot = skeleton.slots.Items[slotIndex]; - float[] frames = this.frames; - if (time < frames[0]) { // Time is before first frame. - var slotData = slot.data; - switch (pose) { - case MixPose.Setup: - // slot.color.set(slot.data.color); - // slot.darkColor.set(slot.data.darkColor); - slot.r = slotData.r; - slot.g = slotData.g; - slot.b = slotData.b; - slot.a = slotData.a; - slot.r2 = slotData.r2; - slot.g2 = slotData.g2; - slot.b2 = slotData.b2; - return; - case MixPose.Current: - slot.r += (slot.r - slotData.r) * alpha; - slot.g += (slot.g - slotData.g) * alpha; - slot.b += (slot.b - slotData.b) * alpha; - slot.a += (slot.a - slotData.a) * alpha; - slot.r2 += (slot.r2 - slotData.r2) * alpha; - slot.g2 += (slot.g2 - slotData.g2) * alpha; - slot.b2 += (slot.b2 - slotData.b2) * alpha; - return; - } - return; - } - - float r, g, b, a, r2, g2, b2; - if (time >= frames[frames.Length - ENTRIES]) { // Time is after last frame. - int i = frames.Length; - r = frames[i + PREV_R]; - g = frames[i + PREV_G]; - b = frames[i + PREV_B]; - a = frames[i + PREV_A]; - r2 = frames[i + PREV_R2]; - g2 = frames[i + PREV_G2]; - b2 = frames[i + PREV_B2]; - } else { - // Interpolate between the previous frame and the current frame. - int frame = Animation.BinarySearch(frames, time, ENTRIES); - r = frames[frame + PREV_R]; - g = frames[frame + PREV_G]; - b = frames[frame + PREV_B]; - a = frames[frame + PREV_A]; - r2 = frames[frame + PREV_R2]; - g2 = frames[frame + PREV_G2]; - b2 = frames[frame + PREV_B2]; - float frameTime = frames[frame]; - float percent = GetCurvePercent(frame / ENTRIES - 1, - 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime)); - - r += (frames[frame + R] - r) * percent; - g += (frames[frame + G] - g) * percent; - b += (frames[frame + B] - b) * percent; - a += (frames[frame + A] - a) * percent; - r2 += (frames[frame + R2] - r2) * percent; - g2 += (frames[frame + G2] - g2) * percent; - b2 += (frames[frame + B2] - b2) * percent; - } - if (alpha == 1) { - slot.r = r; - slot.g = g; - slot.b = b; - slot.a = a; - slot.r2 = r2; - slot.g2 = g2; - slot.b2 = b2; - } else { - float br, bg, bb, ba, br2, bg2, bb2; - if (pose == MixPose.Setup) { + if (setupPose) { br = slot.data.r; bg = slot.data.g; bb = slot.data.b; ba = slot.data.a; - br2 = slot.data.r2; - bg2 = slot.data.g2; - bb2 = slot.data.b2; } else { br = slot.r; bg = slot.g; bb = slot.b; ba = slot.a; - br2 = slot.r2; - bg2 = slot.g2; - bb2 = slot.b2; } slot.r = br + ((r - br) * alpha); slot.g = bg + ((g - bg) * alpha); slot.b = bb + ((b - bb) * alpha); slot.a = ba + ((a - ba) * alpha); - slot.r2 = br2 + ((r2 - br2) * alpha); - slot.g2 = bg2 + ((g2 - bg2) * alpha); - slot.b2 = bb2 + ((b2 - bb2) * alpha); } } - } public class AttachmentTimeline : Timeline { @@ -767,10 +571,10 @@ public void SetFrame (int frameIndex, float time, String attachmentName) { attachmentNames[frameIndex] = attachmentName; } - public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList firedEvents, float alpha, MixPose pose, MixDirection direction) { + public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList firedEvents, float alpha, bool setupPose, bool mixingOut) { string attachmentName; Slot slot = skeleton.slots.Items[slotIndex]; - if (direction == MixDirection.Out && pose == MixPose.Setup) { + if (mixingOut && setupPose) { attachmentName = slot.data.attachmentName; slot.Attachment = attachmentName == null ? null : skeleton.GetAttachment(slotIndex, attachmentName); return; @@ -778,7 +582,7 @@ public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList firedEvents, float alpha, MixPose pose, MixDirection direction) { + override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList firedEvents, float alpha, bool setupPose, bool mixingOut) { Slot slot = skeleton.slots.Items[slotIndex]; VertexAttachment slotAttachment = slot.attachment as VertexAttachment; if (slotAttachment == null || !slotAttachment.ApplyDeform(attachment)) return; var verticesArray = slot.attachmentVertices; - float[][] frameVertices = this.frameVertices; - int vertexCount = frameVertices[0].Length; - if (verticesArray.Count != vertexCount && pose != MixPose.Setup) alpha = 1; // Don't mix from uninitialized slot vertices. - // verticesArray.SetSize(vertexCount) // Ensure size and preemptively set count. - if (verticesArray.Capacity < vertexCount) verticesArray.Capacity = vertexCount; - verticesArray.Count = vertexCount; - float[] vertices = verticesArray.Items; - float[] frames = this.frames; if (time < frames[0]) { - switch (pose) { - case MixPose.Setup: - verticesArray.Clear(); - return; - case MixPose.Current: - alpha = 1 - alpha; - for (int i = 0; i < vertexCount; i++) - vertices[i] *= alpha; - - return; - } + if (setupPose) verticesArray.Clear(); return; } + float[][] frameVertices = this.frameVertices; + int vertexCount = frameVertices[0].Length; + if (verticesArray.Count != vertexCount) alpha = 1; // Don't mix from uninitialized slot vertices. + // verticesArray.SetSize(vertexCount) // Ensure size and preemptively set count. + if (verticesArray.Capacity < vertexCount) verticesArray.Capacity = vertexCount; + verticesArray.Count = vertexCount; + float[] vertices = verticesArray.Items; if (time >= frames[frames.Length - 1]) { // Time is after last frame. float[] lastVertices = frameVertices[frames.Length - 1]; if (alpha == 1) { // Vertex positions or deform offsets, no alpha. Array.Copy(lastVertices, 0, vertices, 0, vertexCount); - } else if (pose == MixPose.Setup) { + } else if (setupPose) { VertexAttachment vertexAttachment = slotAttachment; if (vertexAttachment.bones == null) { // Unweighted vertex positions, with alpha. @@ -895,7 +688,7 @@ override public void Apply (Skeleton skeleton, float lastTime, float time, Expos float prev = prevVertices[i]; vertices[i] = prev + (nextVertices[i] - prev) * percent; } - } else if (pose == MixPose.Setup) { + } else if (setupPose) { VertexAttachment vertexAttachment = (VertexAttachment)slotAttachment; if (vertexAttachment.bones == null) { // Unweighted vertex positions, with alpha. @@ -945,13 +738,13 @@ public void SetFrame (int frameIndex, Event e) { } /// Fires events for frames > lastTime and <= time. - public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList firedEvents, float alpha, MixPose pose, MixDirection direction) { + public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList firedEvents, float alpha, bool setupPose, bool mixingOut) { if (firedEvents == null) return; float[] frames = this.frames; int frameCount = frames.Length; if (lastTime > time) { // Fire events after last time for looped animations. - Apply(skeleton, lastTime, int.MaxValue, firedEvents, alpha, pose, direction); + Apply(skeleton, lastTime, int.MaxValue, firedEvents, alpha, setupPose, mixingOut); lastTime = -1f; } else if (lastTime >= frames[frameCount - 1]) // Last time is after last frame. return; @@ -997,17 +790,17 @@ public void SetFrame (int frameIndex, float time, int[] drawOrder) { drawOrders[frameIndex] = drawOrder; } - public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList firedEvents, float alpha, MixPose pose, MixDirection direction) { + public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList firedEvents, float alpha, bool setupPose, bool mixingOut) { ExposedList drawOrder = skeleton.drawOrder; ExposedList slots = skeleton.slots; - if (direction == MixDirection.Out && pose == MixPose.Setup) { + if (mixingOut && setupPose) { Array.Copy(slots.Items, 0, drawOrder.Items, 0, slots.Count); return; } float[] frames = this.frames; if (time < frames[0]) { - if (pose == MixPose.Setup) Array.Copy(slots.Items, 0, drawOrder.Items, 0, slots.Count); + if (setupPose) Array.Copy(slots.Items, 0, drawOrder.Items, 0, slots.Count); return; } @@ -1059,31 +852,25 @@ public void SetFrame (int frameIndex, float time, float mix, int bendDirection) frames[frameIndex + BEND_DIRECTION] = bendDirection; } - override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList firedEvents, float alpha, MixPose pose, MixDirection direction) { + override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList firedEvents, float alpha, bool setupPose, bool mixingOut) { IkConstraint constraint = skeleton.ikConstraints.Items[ikConstraintIndex]; float[] frames = this.frames; if (time < frames[0]) { - switch (pose) { - case MixPose.Setup: + if (setupPose) { constraint.mix = constraint.data.mix; constraint.bendDirection = constraint.data.bendDirection; - return; - case MixPose.Current: - constraint.mix += (constraint.data.mix - constraint.mix) * alpha; - constraint.bendDirection = constraint.data.bendDirection; - return; } return; } if (time >= frames[frames.Length - ENTRIES]) { // Time is after last frame. - if (pose == MixPose.Setup) { + if (setupPose) { constraint.mix = constraint.data.mix + (frames[frames.Length + PREV_MIX] - constraint.data.mix) * alpha; - constraint.bendDirection = direction == MixDirection.Out ? constraint.data.bendDirection + constraint.bendDirection = mixingOut ? constraint.data.bendDirection : (int)frames[frames.Length + PREV_BEND_DIRECTION]; } else { constraint.mix += (frames[frames.Length + PREV_MIX] - constraint.mix) * alpha; - if (direction == MixDirection.In) constraint.bendDirection = (int)frames[frames.Length + PREV_BEND_DIRECTION]; + if (!mixingOut) constraint.bendDirection = (int)frames[frames.Length + PREV_BEND_DIRECTION]; } return; } @@ -1094,12 +881,12 @@ override public void Apply (Skeleton skeleton, float lastTime, float time, Expos float frameTime = frames[frame]; float percent = GetCurvePercent(frame / ENTRIES - 1, 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime)); - if (pose == MixPose.Setup) { + if (setupPose) { constraint.mix = constraint.data.mix + (mix + (frames[frame + MIX] - mix) * percent - constraint.data.mix) * alpha; - constraint.bendDirection = direction == MixDirection.Out ? constraint.data.bendDirection : (int)frames[frame + PREV_BEND_DIRECTION]; + constraint.bendDirection = mixingOut ? constraint.data.bendDirection : (int)frames[frame + PREV_BEND_DIRECTION]; } else { constraint.mix += (mix + (frames[frame + MIX] - mix) * percent - constraint.mix) * alpha; - if (direction == MixDirection.In) constraint.bendDirection = (int)frames[frame + PREV_BEND_DIRECTION]; + if (!mixingOut) constraint.bendDirection = (int)frames[frame + PREV_BEND_DIRECTION]; } } } @@ -1133,24 +920,16 @@ public void SetFrame (int frameIndex, float time, float rotateMix, float transla frames[frameIndex + SHEAR] = shearMix; } - override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList firedEvents, float alpha, MixPose pose, MixDirection direction) { + override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList firedEvents, float alpha, bool setupPose, bool mixingOut) { TransformConstraint constraint = skeleton.transformConstraints.Items[transformConstraintIndex]; float[] frames = this.frames; if (time < frames[0]) { - var data = constraint.data; - switch (pose) { - case MixPose.Setup: + if (setupPose) { + var data = constraint.data; constraint.rotateMix = data.rotateMix; constraint.translateMix = data.translateMix; constraint.scaleMix = data.scaleMix; constraint.shearMix = data.shearMix; - return; - case MixPose.Current: - constraint.rotateMix += (data.rotateMix - constraint.rotateMix) * alpha; - constraint.translateMix += (data.translateMix - constraint.translateMix) * alpha; - constraint.scaleMix += (data.scaleMix - constraint.scaleMix) * alpha; - constraint.shearMix += (data.shearMix - constraint.shearMix) * alpha; - return; } return; } @@ -1178,7 +957,7 @@ override public void Apply (Skeleton skeleton, float lastTime, float time, Expos scale += (frames[frame + SCALE] - scale) * percent; shear += (frames[frame + SHEAR] - shear) * percent; } - if (pose == MixPose.Setup) { + if (setupPose) { TransformConstraintData data = constraint.data; constraint.rotateMix = data.rotateMix + (rotate - data.rotateMix) * alpha; constraint.translateMix = data.translateMix + (translate - data.translateMix) * alpha; @@ -1220,18 +999,11 @@ public void SetFrame (int frameIndex, float time, float value) { frames[frameIndex + VALUE] = value; } - override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList firedEvents, float alpha, MixPose pose, MixDirection direction) { + override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList firedEvents, float alpha, bool setupPose, bool mixingOut) { PathConstraint constraint = skeleton.pathConstraints.Items[pathConstraintIndex]; float[] frames = this.frames; if (time < frames[0]) { - switch (pose) { - case MixPose.Setup: - constraint.position = constraint.data.position; - return; - case MixPose.Current: - constraint.position += (constraint.data.position - constraint.position) * alpha; - return; - } + if (setupPose) constraint.position = constraint.data.position; return; } @@ -1248,7 +1020,7 @@ override public void Apply (Skeleton skeleton, float lastTime, float time, Expos position += (frames[frame + VALUE] - position) * percent; } - if (pose == MixPose.Setup) + if (setupPose) constraint.position = constraint.data.position + (position - constraint.data.position) * alpha; else constraint.position += (position - constraint.position) * alpha; @@ -1264,18 +1036,11 @@ public PathConstraintSpacingTimeline (int frameCount) : base(frameCount) { } - override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList firedEvents, float alpha, MixPose pose, MixDirection direction) { + override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList firedEvents, float alpha, bool setupPose, bool mixingOut) { PathConstraint constraint = skeleton.pathConstraints.Items[pathConstraintIndex]; float[] frames = this.frames; if (time < frames[0]) { - switch (pose) { - case MixPose.Setup: - constraint.spacing = constraint.data.spacing; - return; - case MixPose.Current: - constraint.spacing += (constraint.data.spacing - constraint.spacing) * alpha; - return; - } + if (setupPose) constraint.spacing = constraint.data.spacing; return; } @@ -1293,7 +1058,7 @@ override public void Apply (Skeleton skeleton, float lastTime, float time, Expos spacing += (frames[frame + VALUE] - spacing) * percent; } - if (pose == MixPose.Setup) + if (setupPose) constraint.spacing = constraint.data.spacing + (spacing - constraint.data.spacing) * alpha; else constraint.spacing += (spacing - constraint.spacing) * alpha; @@ -1328,19 +1093,13 @@ public void SetFrame (int frameIndex, float time, float rotateMix, float transla frames[frameIndex + TRANSLATE] = translateMix; } - override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList firedEvents, float alpha, MixPose pose, MixDirection direction) { + override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList firedEvents, float alpha, bool setupPose, bool mixingOut) { PathConstraint constraint = skeleton.pathConstraints.Items[pathConstraintIndex]; float[] frames = this.frames; if (time < frames[0]) { - switch (pose) { - case MixPose.Setup: + if (setupPose) { constraint.rotateMix = constraint.data.rotateMix; constraint.translateMix = constraint.data.translateMix; - return; - case MixPose.Current: - constraint.rotateMix += (constraint.data.rotateMix - constraint.rotateMix) * alpha; - constraint.translateMix += (constraint.data.translateMix - constraint.translateMix) * alpha; - return; } return; } @@ -1362,7 +1121,7 @@ override public void Apply (Skeleton skeleton, float lastTime, float time, Expos translate += (frames[frame + TRANSLATE] - translate) * percent; } - if (pose == MixPose.Setup) { + if (setupPose) { constraint.rotateMix = constraint.data.rotateMix + (rotate - constraint.data.rotateMix) * alpha; constraint.translateMix = constraint.data.translateMix + (translate - constraint.data.translateMix) * alpha; } else { diff --git a/SpineRuntimes/SpineRuntime35/AnimationState.cs b/SpineRuntimes/SpineRuntime35/AnimationState.cs index 03ab6c7b..2d3067a1 100644 --- a/SpineRuntimes/SpineRuntime35/AnimationState.cs +++ b/SpineRuntimes/SpineRuntime35/AnimationState.cs @@ -34,22 +34,18 @@ namespace SpineRuntime35 { public class AnimationState { static readonly Animation EmptyAnimation = new Animation("", new ExposedList(), 0); - internal const int SUBSEQUENT = 0, FIRST = 1, DIP = 2, DIP_MIX = 3; private AnimationStateData data; private readonly ExposedList tracks = new ExposedList(); private readonly HashSet propertyIDs = new HashSet(); private readonly ExposedList events = new ExposedList(); private readonly EventQueue queue; - - private readonly ExposedList mixingTo = new ExposedList(); private bool animationsChanged; - private float timeScale = 1; - private readonly Pool trackEntryPool = new Pool(); + Pool trackEntryPool = new Pool(); - public AnimationStateData Data { get { return data; } } + public AnimationStateData Data { get { return data; } } /// A list of tracks that have animations, which may contain nulls. public ExposedList Tracks { get { return tracks; } } public float TimeScale { get { return timeScale; } set { timeScale = value; } } @@ -107,23 +103,16 @@ public void Update (float delta) { } continue; } - } else if (current.trackLast >= current.trackEnd && current.mixingFrom == null) { + } else { // Clear the track when there is no next entry, the track end time is reached, and there is no mixingFrom. - tracksItems[i] = null; - - queue.End(current); - DisposeNext(current); - continue; - } - if (current.mixingFrom != null && UpdateMixingFrom(current, delta)) { - // End mixing from entries once all have completed. - var from = current.mixingFrom; - current.mixingFrom = null; - while (from != null) { - queue.End(from); - from = from.mixingFrom; + if (current.trackLast >= current.trackEnd && current.mixingFrom == null) { + tracksItems[i] = null; + queue.End(current); + DisposeNext(current); + continue; } } + UpdateMixingFrom(current, delta); current.trackTime += currentDelta; } @@ -131,53 +120,44 @@ public void Update (float delta) { queue.Drain(); } - /// Returns true when all mixing from entries are complete. - private bool UpdateMixingFrom (TrackEntry to, float delta) { - TrackEntry from = to.mixingFrom; - if (from == null) return true; + private void UpdateMixingFrom (TrackEntry entry, float delta) { + TrackEntry from = entry.mixingFrom; + if (from == null) return; - bool finished = UpdateMixingFrom(from, delta); + UpdateMixingFrom(from, delta); - // Require mixTime > 0 to ensure the mixing from entry was applied at least once. - if (to.mixTime > 0 && (to.mixTime >= to.mixDuration || to.timeScale == 0)) { - if (from.totalAlpha == 0) { - to.mixingFrom = from.mixingFrom; - to.interruptAlpha = from.interruptAlpha; - queue.End(from); - } - return finished; + if (entry.mixTime >= entry.mixDuration && from.mixingFrom == null && entry.mixTime > 0) { + entry.mixingFrom = null; + queue.End(from); + return; } from.animationLast = from.nextAnimationLast; from.trackLast = from.nextTrackLast; from.trackTime += delta * from.timeScale; - to.mixTime += delta * to.timeScale; - return false; + entry.mixTime += delta * entry.timeScale; } /// /// Poses the skeleton using the track entry animations. There are no side effects other than invoking listeners, so the /// animation state can be applied to multiple skeletons to pose them identically. - public bool Apply (Skeleton skeleton) { + public void Apply (Skeleton skeleton) { if (skeleton == null) throw new ArgumentNullException("skeleton", "skeleton cannot be null."); if (animationsChanged) AnimationsChanged(); var events = this.events; - bool applied = false; var tracksItems = tracks.Items; for (int i = 0, m = tracks.Count; i < m; i++) { TrackEntry current = tracksItems[i]; if (current == null || current.delay > 0) continue; - applied = true; - MixPose currentPose = i == 0 ? MixPose.Current : MixPose.CurrentLayered; // Apply mixing from entries first. float mix = current.alpha; if (current.mixingFrom != null) - mix *= ApplyMixingFrom(current, skeleton, currentPose); - else if (current.trackTime >= current.trackEnd && current.next == null) // + mix *= ApplyMixingFrom(current, skeleton); + else if (current.trackTime >= current.trackEnd) // mix = 0; // Set to setup pose the last time the entry will be applied. // Apply current entry. @@ -187,22 +167,22 @@ public bool Apply (Skeleton skeleton) { var timelinesItems = timelines.Items; if (mix == 1) { for (int ii = 0; ii < timelineCount; ii++) - timelinesItems[ii].Apply(skeleton, animationLast, animationTime, events, 1, MixPose.Setup, MixDirection.In); + timelinesItems[ii].Apply(skeleton, animationLast, animationTime, events, 1, true, false); } else { - var timelineData = current.timelineData.Items; - bool firstFrame = current.timelinesRotation.Count == 0; if (firstFrame) current.timelinesRotation.EnsureCapacity(timelines.Count << 1); var timelinesRotation = current.timelinesRotation.Items; + var timelinesFirstItems = current.timelinesFirst.Items; for (int ii = 0; ii < timelineCount; ii++) { Timeline timeline = timelinesItems[ii]; - MixPose pose = timelineData[ii] >= FIRST ? MixPose.Setup : currentPose; var rotateTimeline = timeline as RotateTimeline; - if (rotateTimeline != null) - ApplyRotateTimeline(rotateTimeline, skeleton, animationTime, mix, pose, timelinesRotation, ii << 1, firstFrame); - else - timeline.Apply(skeleton, animationLast, animationTime, events, mix, pose, MixDirection.In); + if (rotateTimeline != null) { + ApplyRotateTimeline(rotateTimeline, skeleton, animationTime, mix, timelinesFirstItems[ii], timelinesRotation, ii << 1, + firstFrame); + } else { + timeline.Apply(skeleton, animationLast, animationTime, events, mix, timelinesFirstItems[ii], false); + } } } QueueEvents(current, animationTime); @@ -212,18 +192,17 @@ public bool Apply (Skeleton skeleton) { } queue.Drain(); - return applied; } - private float ApplyMixingFrom (TrackEntry to, Skeleton skeleton, MixPose currentPose) { - TrackEntry from = to.mixingFrom; - if (from.mixingFrom != null) ApplyMixingFrom(from, skeleton, currentPose); + private float ApplyMixingFrom (TrackEntry entry, Skeleton skeleton) { + TrackEntry from = entry.mixingFrom; + if (from.mixingFrom != null) ApplyMixingFrom(from, skeleton); float mix; - if (to.mixDuration == 0) // Single frame mix to undo mixingFrom changes. + if (entry.mixDuration == 0) // Single frame mix to undo mixingFrom changes. mix = 1; else { - mix = to.mixTime / to.mixDuration; + mix = entry.mixTime / entry.mixDuration; if (mix > 1) mix = 1; } @@ -231,73 +210,53 @@ private float ApplyMixingFrom (TrackEntry to, Skeleton skeleton, MixPose current bool attachments = mix < from.attachmentThreshold, drawOrder = mix < from.drawOrderThreshold; float animationLast = from.animationLast, animationTime = from.AnimationTime; var timelines = from.animation.timelines; - int timelineCount = timelines.Count; var timelinesItems = timelines.Items; - var timelineData = from.timelineData.Items; - var timelineDipMix = from.timelineDipMix.Items; + int timelineCount = timelines.Count; + var timelinesFirst = from.timelinesFirst; + var timelinesFirstItems = timelinesFirst.Items; + float alpha = from.alpha * entry.mixAlpha * (1 - mix); - bool firstFrame = from.timelinesRotation.Count == 0; - if (firstFrame) from.timelinesRotation.Resize(timelines.Count << 1); // from.timelinesRotation.setSize - var timelinesRotation = from.timelinesRotation.Items; + bool firstFrame = entry.timelinesRotation.Count == 0; + if (firstFrame) entry.timelinesRotation.EnsureCapacity(timelines.Count << 1); + var timelinesRotation = entry.timelinesRotation.Items; - MixPose pose; - float alphaDip = from.alpha * to.interruptAlpha, alphaMix = alphaDip * (1 - mix), alpha; - from.totalAlpha = 0; for (int i = 0; i < timelineCount; i++) { Timeline timeline = timelinesItems[i]; - switch (timelineData[i]) { - case SUBSEQUENT: - if (!attachments && timeline is AttachmentTimeline) continue; - if (!drawOrder && timeline is DrawOrderTimeline) continue; - pose = currentPose; - alpha = alphaMix; - break; - case FIRST: - pose = MixPose.Setup; - alpha = alphaMix; - break; - case DIP: - pose = MixPose.Setup; - alpha = alphaDip; - break; - default: - pose = MixPose.Setup; - alpha = alphaDip; - var dipMix = timelineDipMix[i]; - alpha *= Math.Max(0, 1 - dipMix.mixTime / dipMix.mixDuration); - break; - } - from.totalAlpha += alpha; + bool setupPose = timelinesFirstItems[i]; var rotateTimeline = timeline as RotateTimeline; if (rotateTimeline != null) { - ApplyRotateTimeline(rotateTimeline, skeleton, animationTime, alpha, pose, timelinesRotation, i << 1, firstFrame); + ApplyRotateTimeline(rotateTimeline, skeleton, animationTime, alpha, setupPose, timelinesRotation, i << 1, firstFrame); } else { - timeline.Apply(skeleton, animationLast, animationTime, eventBuffer, alpha, pose, MixDirection.Out); + if (!setupPose) { + if (!attachments && timeline is AttachmentTimeline) continue; + if (!drawOrder && timeline is DrawOrderTimeline) continue; + } + timeline.Apply(skeleton, animationLast, animationTime, eventBuffer, alpha, setupPose, true); } } - if (to.mixDuration > 0) QueueEvents(from, animationTime); - this.events.Clear(false); + if (entry.mixDuration > 0 ) QueueEvents(from, animationTime); + events.Clear(false); from.nextAnimationLast = animationTime; from.nextTrackLast = from.trackTime; return mix; } - static private void ApplyRotateTimeline (RotateTimeline rotateTimeline, Skeleton skeleton, float time, float alpha, MixPose pose, + static private void ApplyRotateTimeline (RotateTimeline rotateTimeline, Skeleton skeleton, float time, float alpha, bool setupPose, float[] timelinesRotation, int i, bool firstFrame) { if (firstFrame) timelinesRotation[i] = 0; if (alpha == 1) { - rotateTimeline.Apply(skeleton, 0, time, null, 1, pose, MixDirection.In); + rotateTimeline.Apply(skeleton, 0, time, null, 1, setupPose, false); return; } Bone bone = skeleton.bones.Items[rotateTimeline.boneIndex]; float[] frames = rotateTimeline.frames; if (time < frames[0]) { - if (pose == MixPose.Setup) bone.rotation = bone.data.rotation; + if (setupPose) bone.rotation = bone.data.rotation; return; } @@ -319,7 +278,7 @@ static private void ApplyRotateTimeline (RotateTimeline rotateTimeline, Skeleton } // Mix between rotations using the direction of the shortest route on the first frame while detecting crosses. - float r1 = pose == MixPose.Setup ? bone.data.rotation : bone.rotation; + float r1 = setupPose ? bone.data.rotation : bone.rotation; float total, diff = r2 - r1; if (diff == 0) { total = timelinesRotation[i]; @@ -430,11 +389,10 @@ private void SetCurrent (int index, TrackEntry current, bool interrupt) { current.mixingFrom = from; current.mixTime = 0; - // Store interrupted mix percentage. - if (from.mixingFrom != null && from.mixDuration > 0) - current.interruptAlpha *= Math.Min(1, from.mixTime / from.mixDuration); + from.timelinesRotation.Clear(); - from.timelinesRotation.Clear(); // Reset rotation for mixing out, in case entry was mixed in. + // If not completely mixed in, set mixAlpha so mixing out happens from current mix to zero. + if (from.mixingFrom != null && from.mixDuration > 0) current.mixAlpha *= Math.Min(from.mixTime / from.mixDuration, 1); } queue.Start(current); @@ -594,7 +552,7 @@ private TrackEntry NewTrackEntry (int trackIndex, Animation animation, bool loop entry.timeScale = 1; entry.alpha = 1; - entry.interruptAlpha = 1; + entry.mixAlpha = 1; entry.mixTime = 0; entry.mixDuration = (last == null) ? 0 : data.GetMix(last.animation, animation); return entry; @@ -613,18 +571,58 @@ private void AnimationsChanged () { animationsChanged = false; var propertyIDs = this.propertyIDs; + + // Set timelinesFirst for all entries, from lowest track to highest. + int i = 0, n = tracks.Count; propertyIDs.Clear(); - var mixingTo = this.mixingTo; + for (; i < n; i++) { // Find first non-null entry. + TrackEntry entry = tracks.Items[i]; + if (entry == null) continue; + SetTimelinesFirst(entry); + i++; + break; + } + for (; i < n; i++) { // Rest of entries. + TrackEntry entry = tracks.Items[i]; + if (entry != null) CheckTimelinesFirst(entry); + } + } - TrackEntry lastEntry = null; - var tracksItems = tracks.Items; - for (int i = 0, n = tracks.Count; i < n; i++) { - var entry = tracksItems[i]; - if (entry != null) { - entry.SetTimelineData(lastEntry, mixingTo, propertyIDs); - lastEntry = entry; - } + /// From last to first mixingFrom entries, sets timelinesFirst to true on last, calls checkTimelineUsage on rest. + private void SetTimelinesFirst (TrackEntry entry) { + if (entry.mixingFrom != null) { + SetTimelinesFirst(entry.mixingFrom); + CheckTimelinesUsage(entry); + return; } + var propertyIDs = this.propertyIDs; + var timelines = entry.animation.timelines; + int n = timelines.Count; + entry.timelinesFirst.EnsureCapacity(n); // entry.timelinesFirst.setSize(n); + var usage = entry.timelinesFirst.Items; + var timelinesItems = timelines.Items; + for (int i = 0; i < n; i++) { + propertyIDs.Add(timelinesItems[i].PropertyId); + usage[i] = true; + } + } + + /// From last to first mixingFrom entries, calls checkTimelineUsage. + private void CheckTimelinesFirst (TrackEntry entry) { + if (entry.mixingFrom != null) CheckTimelinesFirst(entry.mixingFrom); + CheckTimelinesUsage(entry); + } + + private void CheckTimelinesUsage (TrackEntry entry) { + var propertyIDs = this.propertyIDs; + var timelines = entry.animation.timelines; + int n = timelines.Count; + var usageArray = entry.timelinesFirst; + usageArray.EnsureCapacity(n); + var usage = usageArray.Items; + var timelinesItems = timelines.Items; + for (int i = 0; i < n; i++) + usage[i] = propertyIDs.Add(timelinesItems[i].PropertyId); } /// The track entry for the animation currently playing on the track, or null if no animation is currently playing. @@ -662,9 +660,8 @@ public class TrackEntry : Pool.IPoolable { internal float eventThreshold, attachmentThreshold, drawOrderThreshold; internal float animationStart, animationEnd, animationLast, nextAnimationLast; internal float delay, trackTime, trackLast, nextTrackLast, trackEnd, timeScale = 1f; - internal float alpha, mixTime, mixDuration, interruptAlpha, totalAlpha; - internal readonly ExposedList timelineData = new ExposedList(); - internal readonly ExposedList timelineDipMix = new ExposedList(); + internal float alpha, mixTime, mixDuration, mixAlpha; + internal readonly ExposedList timelinesFirst = new ExposedList(); internal readonly ExposedList timelinesRotation = new ExposedList(); // IPoolable.Reset() @@ -672,8 +669,7 @@ public void Reset () { next = null; mixingFrom = null; animation = null; - timelineData.Clear(); - timelineDipMix.Clear(); + timelinesFirst.Clear(); timelinesRotation.Clear(); Start = null; @@ -684,52 +680,6 @@ public void Reset () { Event = null; } - /// May be null. - internal TrackEntry SetTimelineData (TrackEntry to, ExposedList mixingToArray, HashSet propertyIDs) { - if (to != null) mixingToArray.Add(to); - var lastEntry = mixingFrom != null ? mixingFrom.SetTimelineData(this, mixingToArray, propertyIDs) : this; - if (to != null) mixingToArray.RemoveAt(mixingToArray.Count - 1); // mixingToArray.pop(); - - var mixingTo = mixingToArray.Items; - int mixingToLast = mixingToArray.Count - 1; - var timelines = animation.timelines.Items; - int timelinesCount = animation.timelines.Count; - var timelineDataItems = timelineData.Resize(timelinesCount).Items; // timelineData.setSize(timelinesCount); - timelineDipMix.Clear(); - var timelineDipMixItems = timelineDipMix.Resize(timelinesCount).Items; //timelineDipMix.setSize(timelinesCount); - - // outer: - for (int i = 0; i < timelinesCount; i++) { - int id = timelines[i].PropertyId; - if (!propertyIDs.Add(id)) { - timelineDataItems[i] = AnimationState.SUBSEQUENT; - } else if (to == null || !to.HasTimeline(id)) { - timelineDataItems[i] = AnimationState.FIRST; - } else { - for (int ii = mixingToLast; ii >= 0; ii--) { - var entry = mixingTo[ii]; - if (!entry.HasTimeline(id)) { - if (entry.mixDuration > 0) { - timelineDataItems[i] = AnimationState.DIP_MIX; - timelineDipMixItems[i] = entry; - goto outer; // continue outer; - } - } - } - timelineDataItems[i] = AnimationState.DIP; - } - outer: {} - } - return lastEntry; - } - - bool HasTimeline (int id) { - var timelines = animation.timelines.Items; - for (int i = 0, n = animation.timelines.Count; i < n; i++) - if (timelines[i].PropertyId == id) return true; - return false; - } - /// The index of the track where this entry is either current or queued. public int TrackIndex { get { return trackIndex; } } @@ -847,26 +797,20 @@ public bool IsComplete { /// /// Seconds from 0 to the mix duration when mixing from the previous animation to this animation. May be slightly more than - /// when the mix is complete. + /// . public float MixTime { get { return mixTime; } set { mixTime = value; } } /// /// Seconds for mixing from the previous animation to this animation. Defaults to the value provided by /// based on the animation before this animation (if any). /// - /// The mix duration can be set manually rather than use the value from AnimationStateData.GetMix. - /// In that case, the mixDuration must be set before is next called. - /// - /// When using with a - /// delay less than or equal to 0, note the is set using the mix duration from the - /// - /// + /// The mix duration must be set before is next called. /// public float MixDuration { get { return mixDuration; } set { mixDuration = value; } } /// /// The track entry for the previous animation when mixing from the previous animation to this animation, or null if no - /// mixing is currently occuring. When mixing from multiple animations, MixingFrom makes up a linked list. + /// mixing is currently occuring. public TrackEntry MixingFrom { get { return mixingFrom; } } public event AnimationState.TrackEntryDelegate Start, Interrupt, End, Dispose, Complete; diff --git a/SpineRuntimes/SpineRuntime35/AnimationStateData.cs b/SpineRuntimes/SpineRuntime35/AnimationStateData.cs index e3371f30..96a99429 100644 --- a/SpineRuntimes/SpineRuntime35/AnimationStateData.cs +++ b/SpineRuntimes/SpineRuntime35/AnimationStateData.cs @@ -32,18 +32,13 @@ using System.Collections.Generic; namespace SpineRuntime35 { - - /// Stores mix (crossfade) durations to be applied when AnimationState animations are changed. public class AnimationStateData { internal SkeletonData skeletonData; + readonly Dictionary animationToMixTime = new Dictionary(AnimationPairComparer.Instance); internal float defaultMix; - /// The SkeletonData to look up animations when they are specified by name. public SkeletonData SkeletonData { get { return skeletonData; } } - - /// - /// The mix duration to use when no mix duration has been specifically defined between two animations. public float DefaultMix { get { return defaultMix; } set { defaultMix = value; } } public AnimationStateData (SkeletonData skeletonData) { @@ -51,8 +46,7 @@ public AnimationStateData (SkeletonData skeletonData) { this.skeletonData = skeletonData; } - /// Sets a mix duration by animation names. - public void SetMix (string fromName, string toName, float duration) { + public void SetMix (String fromName, String toName, float duration) { Animation from = skeletonData.FindAnimation(fromName); if (from == null) throw new ArgumentException("Animation not found: " + fromName); Animation to = skeletonData.FindAnimation(toName); @@ -60,8 +54,6 @@ public void SetMix (string fromName, string toName, float duration) { SetMix(from, to, duration); } - /// Sets a mix duration when changing from the specified animation to the other. - /// See TrackEntry.MixDuration. public void SetMix (Animation from, Animation to, float duration) { if (from == null) throw new ArgumentNullException("from", "from cannot be null."); if (to == null) throw new ArgumentNullException("to", "to cannot be null."); @@ -70,10 +62,6 @@ public void SetMix (Animation from, Animation to, float duration) { animationToMixTime.Add(key, duration); } - /// - /// The mix duration to use when changing from the specified animation to the other, - /// or the DefaultMix if no mix duration has been set. - /// public float GetMix (Animation from, Animation to) { if (from == null) throw new ArgumentNullException("from", "from cannot be null."); if (to == null) throw new ArgumentNullException("to", "to cannot be null."); diff --git a/SpineRuntimes/SpineRuntime35/Atlas.cs b/SpineRuntimes/SpineRuntime35/Atlas.cs index 9deef4b5..1c68842e 100644 --- a/SpineRuntimes/SpineRuntime35/Atlas.cs +++ b/SpineRuntimes/SpineRuntime35/Atlas.cs @@ -40,7 +40,7 @@ namespace SpineRuntime35 { public class Atlas { - readonly List pages = new List(); + List pages = new List(); List regions = new List(); TextureLoader textureLoader; @@ -58,12 +58,12 @@ private async Task ReadFile(string path, TextureLoader textureLoader) { } } - public Atlas(string path, TextureLoader textureLoader) { + public Atlas(String path, TextureLoader textureLoader) { this.ReadFile(path, textureLoader).Wait(); } #else - public Atlas (string path, TextureLoader textureLoader) { + public Atlas (String path, TextureLoader textureLoader) { #if WINDOWS_PHONE Stream stream = Microsoft.Xna.Framework.TitleContainer.OpenStream(path); @@ -84,7 +84,7 @@ public Atlas (string path, TextureLoader textureLoader) { #endif // !(UNITY) - public Atlas (TextReader reader, string dir, TextureLoader textureLoader) { + public Atlas (TextReader reader, String dir, TextureLoader textureLoader) { Load(reader, dir, textureLoader); } @@ -94,14 +94,14 @@ public Atlas (List pages, List regions) { this.textureLoader = null; } - private void Load (TextReader reader, string imagesDir, TextureLoader textureLoader) { + private void Load (TextReader reader, String imagesDir, TextureLoader textureLoader) { if (textureLoader == null) throw new ArgumentNullException("textureLoader cannot be null."); this.textureLoader = textureLoader; - string[] tuple = new string[4]; + String[] tuple = new String[4]; AtlasPage page = null; while (true) { - string line = reader.ReadLine(); + String line = reader.ReadLine(); if (line == null) break; if (line.Trim().Length == 0) page = null; @@ -120,7 +120,7 @@ private void Load (TextReader reader, string imagesDir, TextureLoader textureLoa page.minFilter = (TextureFilter)Enum.Parse(typeof(TextureFilter), tuple[0], false); page.magFilter = (TextureFilter)Enum.Parse(typeof(TextureFilter), tuple[1], false); - string direction = ReadValue(reader); + String direction = ReadValue(reader); page.uWrap = TextureWrap.ClampToEdge; page.vWrap = TextureWrap.ClampToEdge; if (direction == "x") @@ -189,16 +189,16 @@ private void Load (TextReader reader, string imagesDir, TextureLoader textureLoa } } - static string ReadValue (TextReader reader) { - string line = reader.ReadLine(); + static String ReadValue (TextReader reader) { + String line = reader.ReadLine(); int colon = line.IndexOf(':'); if (colon == -1) throw new Exception("Invalid line: " + line); return line.Substring(colon + 1).Trim(); } /// Returns the number of tuple values read (1, 2 or 4). - static int ReadTuple (TextReader reader, string[] tuple) { - string line = reader.ReadLine(); + static int ReadTuple (TextReader reader, String[] tuple) { + String line = reader.ReadLine(); int colon = line.IndexOf(':'); if (colon == -1) throw new Exception("Invalid line: " + line); int i = 0, lastMatch = colon + 1; @@ -223,7 +223,7 @@ public void FlipV () { /// Returns the first region found with the specified name. This method uses string comparison to find the region, so the result /// should be cached rather than calling this method multiple times. /// The region, or null. - public AtlasRegion FindRegion (string name) { + public AtlasRegion FindRegion (String name) { for (int i = 0, n = regions.Count; i < n; i++) if (regions[i].name == name) return regions[i]; return null; @@ -263,7 +263,7 @@ public enum TextureWrap { } public class AtlasPage { - public string name; + public String name; public Format format; public TextureFilter minFilter; public TextureFilter magFilter; @@ -275,7 +275,7 @@ public class AtlasPage { public class AtlasRegion { public AtlasPage page; - public string name; + public String name; public int x, y, width, height; public float u, v, u2, v2; public float offsetX, offsetY; @@ -287,7 +287,7 @@ public class AtlasRegion { } public interface TextureLoader { - void Load (AtlasPage page, string path); + void Load (AtlasPage page, String path); void Unload (Object texture); } } diff --git a/SpineRuntimes/SpineRuntime35/Attachments/AtlasAttachmentLoader.cs b/SpineRuntimes/SpineRuntime35/Attachments/AtlasAttachmentLoader.cs index 695ec8ce..9f391ab1 100644 --- a/SpineRuntimes/SpineRuntime35/Attachments/AtlasAttachmentLoader.cs +++ b/SpineRuntimes/SpineRuntime35/Attachments/AtlasAttachmentLoader.cs @@ -31,11 +31,6 @@ using System; namespace SpineRuntime35 { - - /// - /// An AttachmentLoader that configures attachments using texture regions from an Atlas. - /// See Loading Skeleton Data in the Spine Runtimes Guide. - /// public class AtlasAttachmentLoader : AttachmentLoader { private Atlas[] atlasArray; @@ -44,9 +39,9 @@ public AtlasAttachmentLoader (params Atlas[] atlasArray) { this.atlasArray = atlasArray; } - public RegionAttachment NewRegionAttachment (Skin skin, string name, string path) { + public RegionAttachment NewRegionAttachment (Skin skin, String name, String path) { AtlasRegion region = FindRegion(path); - if (region == null) throw new ArgumentException(string.Format("Region not found in atlas: {0} (region attachment: {1})", path, name)); + if (region == null) throw new Exception("Region not found in atlas: " + path + " (region attachment: " + name + ")"); RegionAttachment attachment = new RegionAttachment(name); attachment.RendererObject = region; attachment.SetUVs(region.u, region.v, region.u2, region.v2, region.rotate); @@ -59,9 +54,9 @@ public RegionAttachment NewRegionAttachment (Skin skin, string name, string path return attachment; } - public MeshAttachment NewMeshAttachment (Skin skin, string name, string path) { + public MeshAttachment NewMeshAttachment (Skin skin, String name, String path) { AtlasRegion region = FindRegion(path); - if (region == null) throw new ArgumentException(string.Format("Region not found in atlas: {0} (region attachment: {1})", path, name)); + if (region == null) throw new Exception("Region not found in atlas: " + path + " (mesh attachment: " + name + ")"); MeshAttachment attachment = new MeshAttachment(name); attachment.RendererObject = region; attachment.RegionU = region.u; @@ -78,20 +73,12 @@ public MeshAttachment NewMeshAttachment (Skin skin, string name, string path) { return attachment; } - public BoundingBoxAttachment NewBoundingBoxAttachment (Skin skin, string name) { + public BoundingBoxAttachment NewBoundingBoxAttachment (Skin skin, String name) { return new BoundingBoxAttachment(name); } - public PathAttachment NewPathAttachment (Skin skin, string name) { - return new PathAttachment(name); - } - - public PointAttachment NewPointAttachment (Skin skin, string name) { - return new PointAttachment(name); - } - - public ClippingAttachment NewClippingAttachment(Skin skin, string name) { - return new ClippingAttachment(name); + public PathAttachment NewPathAttachment (Skin skin, String name) { + return new PathAttachment (name); } public AtlasRegion FindRegion (string name) { diff --git a/SpineRuntimes/SpineRuntime35/Attachments/Attachment.cs b/SpineRuntimes/SpineRuntime35/Attachments/Attachment.cs index 45a7b8a7..7e832e1d 100644 --- a/SpineRuntimes/SpineRuntime35/Attachments/Attachment.cs +++ b/SpineRuntimes/SpineRuntime35/Attachments/Attachment.cs @@ -32,14 +32,14 @@ namespace SpineRuntime35 { abstract public class Attachment { - public string Name { get; private set; } + public String Name { get; private set; } public Attachment (String name) { if (name == null) throw new ArgumentNullException("name", "name cannot be null"); Name = name; } - override public string ToString () { + override public String ToString () { return Name; } } diff --git a/SpineRuntimes/SpineRuntime35/Attachments/AttachmentLoader.cs b/SpineRuntimes/SpineRuntime35/Attachments/AttachmentLoader.cs index f2e115e0..68f9d536 100644 --- a/SpineRuntimes/SpineRuntime35/Attachments/AttachmentLoader.cs +++ b/SpineRuntimes/SpineRuntime35/Attachments/AttachmentLoader.cs @@ -28,22 +28,20 @@ * POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ +using System; + namespace SpineRuntime35 { public interface AttachmentLoader { /// May be null to not load any attachment. - RegionAttachment NewRegionAttachment (Skin skin, string name, string path); + RegionAttachment NewRegionAttachment (Skin skin, String name, String path); /// May be null to not load any attachment. - MeshAttachment NewMeshAttachment (Skin skin, string name, string path); + MeshAttachment NewMeshAttachment (Skin skin, String name, String path); /// May be null to not load any attachment. - BoundingBoxAttachment NewBoundingBoxAttachment (Skin skin, string name); + BoundingBoxAttachment NewBoundingBoxAttachment (Skin skin, String name); /// May be null to not load any attachment - PathAttachment NewPathAttachment (Skin skin, string name); - - PointAttachment NewPointAttachment (Skin skin, string name); - - ClippingAttachment NewClippingAttachment (Skin skin, string name); + PathAttachment NewPathAttachment (Skin skin, String name); } } diff --git a/SpineRuntimes/SpineRuntime35/Attachments/AttachmentType.cs b/SpineRuntimes/SpineRuntime35/Attachments/AttachmentType.cs index c4bc1aec..7a7419c1 100644 --- a/SpineRuntimes/SpineRuntime35/Attachments/AttachmentType.cs +++ b/SpineRuntimes/SpineRuntime35/Attachments/AttachmentType.cs @@ -30,6 +30,6 @@ namespace SpineRuntime35 { public enum AttachmentType { - Region, Boundingbox, Mesh, Linkedmesh, Path, Point, Clipping + Region, Boundingbox, Mesh, Linkedmesh, Path } } diff --git a/SpineRuntimes/SpineRuntime35/Attachments/ClippingAttachment.cs b/SpineRuntimes/SpineRuntime35/Attachments/ClippingAttachment.cs deleted file mode 100644 index b4514959..00000000 --- a/SpineRuntimes/SpineRuntime35/Attachments/ClippingAttachment.cs +++ /dev/null @@ -1,42 +0,0 @@ -/****************************************************************************** - * Spine Runtimes Software License v2.5 - * - * Copyright (c) 2013-2016, Esoteric Software - * All rights reserved. - * - * You are granted a perpetual, non-exclusive, non-sublicensable, and - * non-transferable license to use, install, execute, and perform the Spine - * Runtimes software and derivative works solely for personal or internal - * use. Without the written permission of Esoteric Software (see Section 2 of - * the Spine Software License Agreement), you may not (a) modify, translate, - * adapt, or develop new applications using the Spine Runtimes or otherwise - * create derivative works or improvements of the Spine Runtimes or (b) remove, - * delete, alter, or obscure any trademarks or any copyright, trademark, patent, - * or other intellectual property or proprietary rights notices on or in the - * Software, including any copy thereof. Redistributions in binary or source - * form must include this license and terms. - * - * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, 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 THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - *****************************************************************************/ - -using System; - -namespace SpineRuntime35 { - public class ClippingAttachment : VertexAttachment { - internal SlotData endSlot; - - public SlotData EndSlot { get { return endSlot; } set { endSlot = value; } } - - public ClippingAttachment(string name) : base(name) { - } - } -} diff --git a/SpineRuntimes/SpineRuntime35/Attachments/MeshAttachment.cs b/SpineRuntimes/SpineRuntime35/Attachments/MeshAttachment.cs index 6a252d87..a3862998 100644 --- a/SpineRuntimes/SpineRuntime35/Attachments/MeshAttachment.cs +++ b/SpineRuntimes/SpineRuntime35/Attachments/MeshAttachment.cs @@ -34,16 +34,15 @@ namespace SpineRuntime35 { /// Attachment that displays a texture region using a mesh. public class MeshAttachment : VertexAttachment { internal float regionOffsetX, regionOffsetY, regionWidth, regionHeight, regionOriginalWidth, regionOriginalHeight; - private MeshAttachment parentMesh; internal float[] uvs, regionUVs; internal int[] triangles; internal float r = 1, g = 1, b = 1, a = 1; internal int hulllength; + internal MeshAttachment parentMesh; internal bool inheritDeform; public int HullLength { get { return hulllength; } set { hulllength = value; } } public float[] RegionUVs { get { return regionUVs; } set { regionUVs = value; } } - /// The UV pair for each vertex, normalized within the entire texture. public float[] UVs { get { return uvs; } set { uvs = value; } } public int[] Triangles { get { return triangles; } set { triangles = value; } } @@ -52,8 +51,8 @@ public class MeshAttachment : VertexAttachment { public float B { get { return b; } set { b = value; } } public float A { get { return a; } set { a = value; } } - public string Path { get; set; } - public object RendererObject; //public Object RendererObject { get; set; } + public String Path { get; set; } + public Object RendererObject { get; set; } public float RegionU { get; set; } public float RegionV { get; set; } public float RegionU2 { get; set; } diff --git a/SpineRuntimes/SpineRuntime35/Attachments/PointAttachment.cs b/SpineRuntimes/SpineRuntime35/Attachments/PointAttachment.cs deleted file mode 100644 index d119935b..00000000 --- a/SpineRuntimes/SpineRuntime35/Attachments/PointAttachment.cs +++ /dev/null @@ -1,61 +0,0 @@ -/****************************************************************************** - * Spine Runtimes Software License v2.5 - * - * Copyright (c) 2013-2016, Esoteric Software - * All rights reserved. - * - * You are granted a perpetual, non-exclusive, non-sublicensable, and - * non-transferable license to use, install, execute, and perform the Spine - * Runtimes software and derivative works solely for personal or internal - * use. Without the written permission of Esoteric Software (see Section 2 of - * the Spine Software License Agreement), you may not (a) modify, translate, - * adapt, or develop new applications using the Spine Runtimes or otherwise - * create derivative works or improvements of the Spine Runtimes or (b) remove, - * delete, alter, or obscure any trademarks or any copyright, trademark, patent, - * or other intellectual property or proprietary rights notices on or in the - * Software, including any copy thereof. Redistributions in binary or source - * form must include this license and terms. - * - * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, 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 THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - *****************************************************************************/ - -namespace SpineRuntime35 { - /// - /// 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 - /// skin. - ///

- /// See Point Attachments in the Spine User Guide. - ///

- public class PointAttachment : Attachment { - internal float x, y, rotation; - public float X { get { return x; } set { x = value; } } - public float Y { get { return y; } set { y = value; } } - public float Rotation { get { return rotation; } set { rotation = value; } } - - public PointAttachment (string name) - : base(name) { - } - - public void ComputeWorldPosition (Bone bone, out float ox, out float oy) { - bone.LocalToWorld(this.x, this.y, out ox, out oy); - } - - public float ComputeWorldRotation (Bone bone) { - float cos = MathUtils.CosDeg(rotation), sin = MathUtils.SinDeg(rotation); - float ix = cos * bone.a + sin * bone.b; - float iy = cos * bone.c + sin * bone.d; - return MathUtils.Atan2(iy, ix) * MathUtils.RadDeg; - } - } -} - diff --git a/SpineRuntimes/SpineRuntime35/Attachments/RegionAttachment.cs b/SpineRuntimes/SpineRuntime35/Attachments/RegionAttachment.cs index f718d87a..bca9f274 100644 --- a/SpineRuntimes/SpineRuntime35/Attachments/RegionAttachment.cs +++ b/SpineRuntimes/SpineRuntime35/Attachments/RegionAttachment.cs @@ -33,14 +33,14 @@ namespace SpineRuntime35 { /// Attachment that displays a texture region. public class RegionAttachment : Attachment { - public const int BLX = 0; - public const int BLY = 1; - public const int ULX = 2; - public const int ULY = 3; - public const int URX = 4; - public const int URY = 5; - public const int BRX = 6; - public const int BRY = 7; + public const int X1 = 0; + public const int Y1 = 1; + public const int X2 = 2; + public const int Y2 = 3; + public const int X3 = 4; + public const int Y3 = 5; + public const int X4 = 6; + public const int Y4 = 7; internal float x, y, rotation, scaleX = 1, scaleY = 1, width, height; internal float regionOffsetX, regionOffsetY, regionWidth, regionHeight, regionOriginalWidth, regionOriginalHeight; @@ -60,8 +60,8 @@ public class RegionAttachment : Attachment { public float B { get { return b; } set { b = value; } } public float A { get { return a; } set { a = value; } } - public string Path { get; set; } - public object RendererObject; //public object RendererObject { get; set; } + public String Path { get; set; } + public Object RendererObject { get; set; } public float RegionOffsetX { get { return regionOffsetX; } set { regionOffsetX = value; } } public float RegionOffsetY { get { return regionOffsetY; } set { regionOffsetY = value; } } // Pixels stripped from the bottom left, unrotated. public float RegionWidth { get { return regionWidth; } set { regionWidth = value; } } @@ -76,6 +76,29 @@ public RegionAttachment (string name) : base(name) { } + public void SetUVs (float u, float v, float u2, float v2, bool rotate) { + float[] uvs = this.uvs; + if (rotate) { + uvs[X2] = u; + uvs[Y2] = v2; + uvs[X3] = u; + uvs[Y3] = v; + uvs[X4] = u2; + uvs[Y4] = v; + uvs[X1] = u2; + uvs[Y1] = v2; + } else { + uvs[X1] = u; + uvs[Y1] = v2; + uvs[X2] = u; + uvs[Y2] = v; + uvs[X3] = u2; + uvs[Y3] = v; + uvs[X4] = u2; + uvs[Y4] = v2; + } + } + public void UpdateOffset () { float width = this.width; float height = this.height; @@ -101,75 +124,28 @@ public void UpdateOffset () { float localY2Cos = localY2 * cos + y; float localY2Sin = localY2 * sin; float[] offset = this.offset; - offset[BLX] = localXCos - localYSin; - offset[BLY] = localYCos + localXSin; - offset[ULX] = localXCos - localY2Sin; - offset[ULY] = localY2Cos + localXSin; - offset[URX] = localX2Cos - localY2Sin; - offset[URY] = localY2Cos + localX2Sin; - offset[BRX] = localX2Cos - localYSin; - offset[BRY] = localYCos + localX2Sin; + offset[X1] = localXCos - localYSin; + offset[Y1] = localYCos + localXSin; + offset[X2] = localXCos - localY2Sin; + offset[Y2] = localY2Cos + localXSin; + offset[X3] = localX2Cos - localY2Sin; + offset[Y3] = localY2Cos + localX2Sin; + offset[X4] = localX2Cos - localYSin; + offset[Y4] = localYCos + localX2Sin; } - public void SetUVs (float u, float v, float u2, float v2, bool rotate) { - float[] uvs = this.uvs; - // UV values differ from RegionAttachment.java - if (rotate) { - uvs[URX] = u; - uvs[URY] = v2; - uvs[BRX] = u; - uvs[BRY] = v; - uvs[BLX] = u2; - uvs[BLY] = v; - uvs[ULX] = u2; - uvs[ULY] = v2; - } else { - uvs[ULX] = u; - uvs[ULY] = v2; - uvs[URX] = u; - uvs[URY] = v; - uvs[BRX] = u2; - uvs[BRY] = v; - uvs[BLX] = u2; - uvs[BLY] = v2; - } - } - - /// Transforms the attachment's four vertices to world coordinates. - /// The parent bone. - /// The output world vertices. Must have a length greater than or equal to offset + 8. - /// The worldVertices index to begin writing values. - /// The number of worldVertices entries between the value pairs written. - public void ComputeWorldVertices (Bone bone, float[] worldVertices, int offset, int stride = 2) { - float[] vertexOffset = this.offset; - float bwx = bone.worldX, bwy = bone.worldY; + public void ComputeWorldVertices (Bone bone, float[] worldVertices) { + float x = bone.worldX, y = bone.worldY; float a = bone.a, b = bone.b, c = bone.c, d = bone.d; - float offsetX, offsetY; - - // Vertex order is different from RegionAttachment.java - offsetX = vertexOffset[BRX]; // 0 - offsetY = vertexOffset[BRY]; // 1 - worldVertices[offset] = offsetX * a + offsetY * b + bwx; // bl - worldVertices[offset + 1] = offsetX * c + offsetY * d + bwy; - offset += stride; - - offsetX = vertexOffset[BLX]; // 2 - offsetY = vertexOffset[BLY]; // 3 - worldVertices[offset] = offsetX * a + offsetY * b + bwx; // ul - worldVertices[offset + 1] = offsetX * c + offsetY * d + bwy; - offset += stride; - - offsetX = vertexOffset[ULX]; // 4 - offsetY = vertexOffset[ULY]; // 5 - worldVertices[offset] = offsetX * a + offsetY * b + bwx; // ur - worldVertices[offset + 1] = offsetX * c + offsetY * d + bwy; - offset += stride; - - offsetX = vertexOffset[URX]; // 6 - offsetY = vertexOffset[URY]; // 7 - worldVertices[offset] = offsetX * a + offsetY * b + bwx; // br - worldVertices[offset + 1] = offsetX * c + offsetY * d + bwy; - //offset += stride; + float[] offset = this.offset; + worldVertices[X1] = offset[X1] * a + offset[Y1] * b + x; + worldVertices[Y1] = offset[X1] * c + offset[Y1] * d + y; + worldVertices[X2] = offset[X2] * a + offset[Y2] * b + x; + worldVertices[Y2] = offset[X2] * c + offset[Y2] * d + y; + worldVertices[X3] = offset[X3] * a + offset[Y3] * b + x; + worldVertices[Y3] = offset[X3] * c + offset[Y3] * d + y; + worldVertices[X4] = offset[X4] * a + offset[Y4] * b + x; + worldVertices[Y4] = offset[X4] * c + offset[Y4] * d + y; } } } diff --git a/SpineRuntimes/SpineRuntime35/Attachments/VertexAttachment.cs b/SpineRuntimes/SpineRuntime35/Attachments/VertexAttachment.cs index c40b661b..4be9f9ff 100644 --- a/SpineRuntimes/SpineRuntime35/Attachments/VertexAttachment.cs +++ b/SpineRuntimes/SpineRuntime35/Attachments/VertexAttachment.cs @@ -29,30 +29,21 @@ *****************************************************************************/ using System; +using System.Collections.Generic; namespace SpineRuntime35 { /// >An attachment with vertices that are transformed by one or more bones and can be deformed by a slot's vertices. public class VertexAttachment : Attachment { - static int nextID = 0; - static readonly Object nextIdLock = new Object(); - - internal readonly int id; internal int[] bones; internal float[] vertices; internal int worldVerticesLength; - /// Gets a unique ID for this attachment. - public int Id { get { return id; } } public int[] Bones { get { return bones; } set { bones = value; } } public float[] Vertices { get { return vertices; } set { vertices = value; } } public int WorldVerticesLength { get { return worldVerticesLength; } set { worldVerticesLength = value; } } - public VertexAttachment (string name) + public VertexAttachment (String name) : base(name) { - - lock (VertexAttachment.nextIdLock) { - id = (VertexAttachment.nextID++ & 65535) << 11; - } } public void ComputeWorldVertices (Slot slot, float[] worldVertices) { @@ -64,10 +55,9 @@ public void ComputeWorldVertices (Slot slot, float[] worldVertices) { /// The number of world vertex values to output. Must be less than or equal to - start. /// The output world vertices. Must have a length greater than or equal to + . /// The index to begin writing values. - /// The number of entries between the value pairs written. - public void ComputeWorldVertices (Slot slot, int start, int count, float[] worldVertices, int offset, int stride = 2) { - count = offset + (count >> 1) * stride; - Skeleton skeleton = slot.bone.skeleton; + public void ComputeWorldVertices (Slot slot, int start, int count, float[] worldVertices, int offset) { + count += offset; + Skeleton skeleton = slot.Skeleton; var deformArray = slot.attachmentVertices; float[] vertices = this.vertices; int[] bones = this.bones; @@ -76,7 +66,7 @@ public void ComputeWorldVertices (Slot slot, int start, int count, float[] world Bone bone = slot.bone; float x = bone.worldX, y = bone.worldY; float a = bone.a, b = bone.b, c = bone.c, d = bone.d; - for (int vv = start, w = offset; w < count; vv += 2, w += stride) { + for (int vv = start, w = offset; w < count; vv += 2, w += 2) { float vx = vertices[vv], vy = vertices[vv + 1]; worldVertices[w] = vx * a + vy * b + x; worldVertices[w + 1] = vx * c + vy * d + y; @@ -89,9 +79,9 @@ public void ComputeWorldVertices (Slot slot, int start, int count, float[] world v += n + 1; skip += n; } - var skeletonBones = skeleton.bones.Items; + Bone[] skeletonBones = skeleton.Bones.Items; if (deformArray.Count == 0) { - for (int w = offset, b = skip * 3; w < count; w += stride) { + for (int w = offset, b = skip * 3; w < count; w += 2) { float wx = 0, wy = 0; int n = bones[v++]; n += v; @@ -106,7 +96,7 @@ public void ComputeWorldVertices (Slot slot, int start, int count, float[] world } } else { float[] deform = deformArray.Items; - for (int w = offset, b = skip * 3, f = skip << 1; w < count; w += stride) { + for (int w = offset, b = skip * 3, f = skip << 1; w < count; w += 2) { float wx = 0, wy = 0; int n = bones[v++]; n += v; diff --git a/SpineRuntimes/SpineRuntime35/BlendMode.cs b/SpineRuntimes/SpineRuntime35/BlendMode.cs index c6b2c83c..4c214e48 100644 --- a/SpineRuntimes/SpineRuntime35/BlendMode.cs +++ b/SpineRuntimes/SpineRuntime35/BlendMode.cs @@ -30,6 +30,6 @@ namespace SpineRuntime35 { public enum BlendMode { - Normal, Additive, Multiply, Screen + normal, additive, multiply, screen } } diff --git a/SpineRuntimes/SpineRuntime35/Bone.cs b/SpineRuntimes/SpineRuntime35/Bone.cs index 37e0f162..197ead88 100644 --- a/SpineRuntimes/SpineRuntime35/Bone.cs +++ b/SpineRuntimes/SpineRuntime35/Bone.cs @@ -31,14 +31,6 @@ using System; namespace SpineRuntime35 { - /// - /// Stores a bone's current pose. - /// - /// 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 : IUpdatable { static public bool yDown; @@ -63,51 +55,20 @@ public class Bone : IUpdatable { public Skeleton Skeleton { get { return skeleton; } } public Bone Parent { get { return parent; } } public ExposedList Children { get { return children; } } - /// The local X translation. public float X { get { return x; } set { x = value; } } - /// The local Y translation. public float Y { get { return y; } set { y = value; } } - /// The local rotation. public float Rotation { get { return rotation; } set { rotation = value; } } - - /// The local scaleX. + /// The rotation, as calculated by any constraints. + public float AppliedRotation { get { return arotation; } set { arotation = value; } } public float ScaleX { get { return scaleX; } set { scaleX = value; } } - - /// The local scaleY. public float ScaleY { get { return scaleY; } set { scaleY = value; } } - - /// The local shearX. public float ShearX { get { return shearX; } set { shearX = value; } } - - /// The local shearY. public float ShearY { get { return shearY; } set { shearY = value; } } - /// The rotation, as calculated by any constraints. - public float AppliedRotation { get { return arotation; } set { arotation = value; } } - - /// The applied local x translation. - public float AX { get { return ax; } set { ax = value; } } - - /// The applied local y translation. - public float AY { get { return ay; } set { ay = value; } } - - /// The applied local scaleX. - public float AScaleX { get { return ascaleX; } set { ascaleX = value; } } - - /// The applied local scaleY. - public float AScaleY { get { return ascaleY; } set { ascaleY = value; } } - - /// The applied local shearX. - public float AShearX { get { return ashearX; } set { ashearX = value; } } - - /// The applied local shearY. - public float AShearY { get { return ashearY; } set { ashearY = value; } } - public float A { get { return a; } } public float B { get { return b; } } public float C { get { return c; } } public float D { get { return d; } } - public float WorldX { get { return worldX; } } public float WorldY { get { return worldY; } } public float WorldRotationX { get { return MathUtils.Atan2(c, a) * MathUtils.RadDeg; } } @@ -152,19 +113,37 @@ public void UpdateWorldTransform (float x, float y, float rotation, float scaleX Bone parent = this.parent; if (parent == null) { // Root bone. - float rotationY = rotation + 90 + shearY, sx = skeleton.scaleX, sy = skeleton.scaleY; - a = MathUtils.CosDeg(rotation + shearX) * scaleX * sx; - b = MathUtils.CosDeg(rotationY) * scaleY * sx; - c = MathUtils.SinDeg(rotation + shearX) * scaleX * sy; - d = MathUtils.SinDeg(rotationY) * scaleY * sy; - worldX = x * sx + skeleton.x; - worldY = y * sy + skeleton.y; + float rotationY = rotation + 90 + shearY; + float la = MathUtils.CosDeg(rotation + shearX) * scaleX; + float lb = MathUtils.CosDeg(rotationY) * scaleY; + float lc = MathUtils.SinDeg(rotation + shearX) * scaleX; + float ld = MathUtils.SinDeg(rotationY) * scaleY; + if (skeleton.flipX) { + x = -x; + la = -la; + lb = -lb; + } + if (skeleton.flipY != yDown) { + y = -y; + lc = -lc; + ld = -ld; + } + a = la; + b = lb; + c = lc; + d = ld; + worldX = x + skeleton.x; + worldY = y + skeleton.y; +// worldSignX = Math.Sign(scaleX); +// worldSignY = Math.Sign(scaleY); 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; +// worldSignX = parent.worldSignX * Math.Sign(scaleX); +// worldSignY = parent.worldSignY * Math.Sign(scaleY); switch (data.transformMode) { case TransformMode.Normal: { @@ -214,16 +193,13 @@ public void UpdateWorldTransform (float x, float y, float rotation, float scaleX case TransformMode.NoScale: case TransformMode.NoScaleOrReflection: { float cos = MathUtils.CosDeg(rotation), sin = MathUtils.SinDeg(rotation); - float za = (pa * cos + pb * sin) / skeleton.scaleX; - float zc = (pc * cos + pd * sin) / skeleton.scaleY; + float za = pa * cos + pb * sin; + float zc = pc * cos + pd * sin; 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 (data.transformMode == TransformMode.NoScale - && (pa * pd - pb * pc < 0) != (skeleton.scaleX < 0 != skeleton.scaleY < 0)) s = -s; - float r = MathUtils.PI / 2 + MathUtils.Atan2(zc, za); float zb = MathUtils.Cos(r) * s; float zd = MathUtils.Sin(r) * s; @@ -235,15 +211,23 @@ public void UpdateWorldTransform (float x, float y, float rotation, float scaleX b = za * lb + zb * ld; c = zc * la + zd * lc; d = zc * lb + zd * ld; - break; + if (data.transformMode != TransformMode.NoScaleOrReflection ? pa * pd - pb * pc < 0 : skeleton.flipX != skeleton.flipY) { + b = -b; + d = -d; + } + return; } } - a *= skeleton.scaleX; - b *= skeleton.scaleX; - c *= skeleton.scaleY; - d *= skeleton.scaleY; - } + if (skeleton.flipX) { + a = -a; + b = -b; + } + if (skeleton.flipY != Bone.yDown) { + c = -c; + d = -d; + } + } public void SetToSetupPose () { BoneData data = this.data; @@ -256,6 +240,34 @@ public void SetToSetupPose () { shearY = data.shearY; } + public float WorldToLocalRotationX { + get { + Bone parent = this.parent; + if (parent == null) return arotation; + float pa = parent.a, pb = parent.b, pc = parent.c, pd = parent.d, a = this.a, c = this.c; + return MathUtils.Atan2(pa * c - pc * a, pd * a - pb * c) * MathUtils.RadDeg; + } + } + + public float WorldToLocalRotationY { + get { + Bone parent = this.parent; + if (parent == null) return arotation; + float pa = parent.a, pb = parent.b, pc = parent.c, pd = parent.d, b = this.b, d = this.d; + return MathUtils.Atan2(pa * d - pc * b, pd * b - pb * d) * MathUtils.RadDeg; + } + } + + public void RotateWorld (float degrees) { + float a = this.a, b = this.b, c = this.c, d = this.d; + float cos = MathUtils.CosDeg(degrees), sin = MathUtils.SinDeg(degrees); + this.a = cos * a - sin * c; + this.b = cos * b - sin * d; + this.c = sin * a + cos * c; + this.d = sin * b + cos * d; + appliedValid = false; + } + /// /// Computes the individual applied transform values from the world transform. This can be useful to perform processing using /// the applied transform after the world transform has been modified directly (eg, by a constraint).. @@ -316,49 +328,7 @@ public void LocalToWorld (float localX, float localY, out float worldX, out floa worldY = localX * c + localY * d + this.worldY; } - public float WorldToLocalRotationX { - get { - Bone parent = this.parent; - if (parent == null) return arotation; - float pa = parent.a, pb = parent.b, pc = parent.c, pd = parent.d, a = this.a, c = this.c; - return MathUtils.Atan2(pa * c - pc * a, pd * a - pb * c) * MathUtils.RadDeg; - } - } - - public float WorldToLocalRotationY { - get { - Bone parent = this.parent; - if (parent == null) return arotation; - float pa = parent.a, pb = parent.b, pc = parent.c, pd = parent.d, b = this.b, d = this.d; - return MathUtils.Atan2(pa * d - pc * b, pd * b - pb * d) * MathUtils.RadDeg; - } - } - - public float WorldToLocalRotation (float worldRotation) { - float sin = MathUtils.SinDeg(worldRotation), cos = MathUtils.CosDeg(worldRotation); - return MathUtils.Atan2(a * sin - c * cos, d * cos - b * sin) * MathUtils.RadDeg; - } - - public float LocalToWorldRotation (float localRotation) { - float sin = MathUtils.SinDeg(localRotation), cos = MathUtils.CosDeg(localRotation); - return MathUtils.Atan2(cos * c + sin * d, cos * a + sin * b) * MathUtils.RadDeg; - } - - /// - /// Rotates the world transform the specified amount and sets isAppliedValid to false. - /// - /// Degrees. - public void RotateWorld (float degrees) { - float a = this.a, b = this.b, c = this.c, d = this.d; - float cos = MathUtils.CosDeg(degrees), sin = MathUtils.SinDeg(degrees); - this.a = cos * a - sin * c; - this.b = cos * b - sin * d; - this.c = sin * a + cos * c; - this.d = sin * b + cos * d; - appliedValid = false; - } - - override public string ToString () { + override public String ToString () { return data.name; } } diff --git a/SpineRuntimes/SpineRuntime35/BoneData.cs b/SpineRuntimes/SpineRuntime35/BoneData.cs index b8be343c..890d26c9 100644 --- a/SpineRuntimes/SpineRuntime35/BoneData.cs +++ b/SpineRuntimes/SpineRuntime35/BoneData.cs @@ -33,49 +33,32 @@ namespace SpineRuntime35 { public class BoneData { internal int index; - internal string name; + internal String name; internal BoneData parent; internal float length; internal float x, y, rotation, scaleX = 1, scaleY = 1, shearX, shearY; internal TransformMode transformMode = TransformMode.Normal; - - /// The index of the bone in Skeleton.Bones - public int Index { get { return index; } } - - /// The name of the bone, which is unique within the skeleton. - public string Name { get { return name; } } + //internal bool inheritRotation = true, inheritScale = true; /// May be null. + public int Index { get { return index; } } + public String Name { get { return name; } } public BoneData Parent { get { return parent; } } - public float Length { get { return length; } set { length = value; } } - - /// Local X translation. public float X { get { return x; } set { x = value; } } - - /// Local Y translation. public float Y { get { return y; } set { y = value; } } - - /// Local rotation. public float Rotation { get { return rotation; } set { rotation = value; } } - - /// Local scaleX. public float ScaleX { get { return scaleX; } set { scaleX = value; } } - - /// Local scaleY. public float ScaleY { get { return scaleY; } set { scaleY = value; } } - - /// Local shearX. public float ShearX { get { return shearX; } set { shearX = value; } } - - /// Local shearY. public float ShearY { get { return shearY; } set { shearY = value; } } - - /// The transform mode for how parent world transforms affect this bone. public TransformMode TransformMode { get { return transformMode; } set { transformMode = value; } } +// public bool InheritRotation { get { return inheritRotation; } set { inheritRotation = value; } } +// public bool InheritScale { get { return inheritScale; } set { inheritScale = value; } } + /// May be null. - public BoneData (int index, string name, BoneData parent) { + public BoneData (int index, String name, BoneData parent) { if (index < 0) throw new ArgumentException("index must be >= 0", "index"); if (name == null) throw new ArgumentNullException("name", "name cannot be null."); this.index = index; @@ -83,14 +66,14 @@ public BoneData (int index, string name, BoneData parent) { this.parent = parent; } - override public string ToString () { + override public String ToString () { return name; } } [Flags] public enum TransformMode { - //0000 0 Flip Scale Rotation + //0000 0FSR Normal = 0, // 0000 OnlyTranslation = 7, // 0111 NoRotationOrReflection = 1, // 0001 diff --git a/SpineRuntimes/SpineRuntime35/Event.cs b/SpineRuntimes/SpineRuntime35/Event.cs index cbd3767a..6d3f428f 100644 --- a/SpineRuntimes/SpineRuntime35/Event.cs +++ b/SpineRuntimes/SpineRuntime35/Event.cs @@ -31,7 +31,6 @@ using System; namespace SpineRuntime35 { - /// Stores the current pose values for an Event. public class Event { internal readonly EventData data; internal readonly float time; @@ -40,12 +39,10 @@ public class Event { internal string stringValue; public EventData Data { get { return data; } } - /// The animation time this event was keyed. public float Time { get { return time; } } - public int Int { get { return intValue; } set { intValue = value; } } public float Float { get { return floatValue; } set { floatValue = value; } } - public string String { get { return stringValue; } set { stringValue = value; } } + public String String { get { return stringValue; } set { stringValue = value; } } public Event (float time, EventData data) { if (data == null) throw new ArgumentNullException("data", "data cannot be null."); diff --git a/SpineRuntimes/SpineRuntime35/EventData.cs b/SpineRuntimes/SpineRuntime35/EventData.cs index fa753376..e5a9f9c3 100644 --- a/SpineRuntimes/SpineRuntime35/EventData.cs +++ b/SpineRuntimes/SpineRuntime35/EventData.cs @@ -31,22 +31,20 @@ using System; namespace SpineRuntime35 { - /// Stores the setup pose values for an Event. public class EventData { - internal string name; + internal String name; - /// The name of the event, which is unique within the skeleton. - public string Name { get { return name; } } + public String Name { get { return name; } } public int Int { get; set; } public float Float { get; set; } - public string String { get; set; } + public String String { get; set; } - public EventData (string name) { + public EventData (String name) { if (name == null) throw new ArgumentNullException("name", "name cannot be null."); this.name = name; } - override public string ToString () { + override public String ToString () { return Name; } } diff --git a/SpineRuntimes/SpineRuntime35/IConstraint.cs b/SpineRuntimes/SpineRuntime35/IConstraint.cs index 61bcf94f..620690cb 100644 --- a/SpineRuntimes/SpineRuntime35/IConstraint.cs +++ b/SpineRuntimes/SpineRuntime35/IConstraint.cs @@ -1,40 +1,5 @@ -/****************************************************************************** - * Spine Runtimes Software License v2.5 - * - * Copyright (c) 2013-2016, Esoteric Software - * All rights reserved. - * - * You are granted a perpetual, non-exclusive, non-sublicensable, and - * non-transferable license to use, install, execute, and perform the Spine - * Runtimes software and derivative works solely for personal or internal - * use. Without the written permission of Esoteric Software (see Section 2 of - * the Spine Software License Agreement), you may not (a) modify, translate, - * adapt, or develop new applications using the Spine Runtimes or otherwise - * create derivative works or improvements of the Spine Runtimes or (b) remove, - * delete, alter, or obscure any trademarks or any copyright, trademark, patent, - * or other intellectual property or proprietary rights notices on or in the - * Software, including any copy thereof. Redistributions in binary or source - * form must include this license and terms. - * - * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, 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 THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - *****************************************************************************/ - -namespace SpineRuntime35 { - - /// The interface for all constraints. +namespace SpineRuntime35 { public interface IConstraint : IUpdatable { - /// The ordinal for the order a skeleton's constraints will be applied. int Order { get; } - } - } \ No newline at end of file diff --git a/SpineRuntimes/SpineRuntime35/IUpdatable.cs b/SpineRuntimes/SpineRuntime35/IUpdatable.cs index 87b6dad4..7b02eb73 100644 --- a/SpineRuntimes/SpineRuntime35/IUpdatable.cs +++ b/SpineRuntimes/SpineRuntime35/IUpdatable.cs @@ -28,6 +28,8 @@ * POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ +using System; + namespace SpineRuntime35 { public interface IUpdatable { void Update (); diff --git a/SpineRuntimes/SpineRuntime35/IkConstraint.cs b/SpineRuntimes/SpineRuntime35/IkConstraint.cs index 81dacd6d..55680f28 100644 --- a/SpineRuntimes/SpineRuntime35/IkConstraint.cs +++ b/SpineRuntimes/SpineRuntime35/IkConstraint.cs @@ -58,12 +58,11 @@ public IkConstraint (IkConstraintData data, Skeleton skeleton) { target = skeleton.FindBone(data.target.name); } - /// Applies the constraint to the constrained bones. - public void Apply () { - Update(); + public void Update () { + Apply(); } - public void Update () { + public void Apply () { Bone target = this.target; ExposedList bones = this.bones; switch (bones.Count) { @@ -76,7 +75,7 @@ public void Update () { } } - override public string ToString () { + override public String ToString () { return data.name; } @@ -93,7 +92,7 @@ static public void Apply (Bone bone, float targetX, float targetY, float alpha) if (rotationIK > 180) rotationIK -= 360; else if (rotationIK < -180) rotationIK += 360; - bone.UpdateWorldTransform(bone.ax, bone.ay, bone.arotation + rotationIK * alpha, bone.ascaleX, bone.ascaleY, bone.ashearX, + bone.UpdateWorldTransform(bone.ax, bone.ay, bone.arotation + rotationIK * alpha, bone.ascaleX, bone.ascaleY, bone.ashearX, bone.ashearY); } @@ -176,29 +175,40 @@ static public void Apply (Bone parent, Bone child, float targetX, float targetY, y = (float)Math.Sqrt(dd - r * r) * bendDir; a1 = ta - (float)Math.Atan2(y, r); a2 = (float)Math.Atan2(y / psy, (r - l1) / psx); - goto outer; // break outer; + goto outer; } } - float minAngle = MathUtils.PI, minX = l1 - a, minDist = minX * minX, minY = 0; - float maxAngle = 0, maxX = l1 + a, maxDist = maxX * maxX, maxY = 0; - c = -a * l1 / (aa - bb); - if (c >= -1 && c <= 1) { - c = (float)Math.Acos(c); - x = a * (float)Math.Cos(c) + l1; - y = b * (float)Math.Sin(c); - d = x * x + y * y; - if (d < minDist) { - minAngle = c; - minDist = d; - minX = x; - minY = y; - } - if (d > maxDist) { - maxAngle = c; - maxDist = d; - maxX = x; - maxY = y; - } + float minAngle = 0, minDist = float.MaxValue, minX = 0, minY = 0; + float maxAngle = 0, maxDist = 0, maxX = 0, maxY = 0; + x = l1 + a; + d = x * x; + if (d > maxDist) { + maxAngle = 0; + maxDist = d; + maxX = x; + } + x = l1 - a; + d = x * x; + if (d < minDist) { + minAngle = (float)Math.PI; + minDist = d; + minX = x; + } + float angle = (float)Math.Acos(-a * l1 / (aa - bb)); + x = a * (float)Math.Cos(angle) + l1; + y = b * (float)Math.Sin(angle); + d = x * x + y * y; + if (d < minDist) { + minAngle = angle; + minDist = d; + minX = x; + minY = y; + } + if (d > maxDist) { + maxAngle = angle; + maxDist = d; + maxX = x; + maxY = y; } if (dd <= (minDist + maxDist) / 2) { a1 = ta - (float)Math.Atan2(minY * bendDir, minX); diff --git a/SpineRuntimes/SpineRuntime35/IkConstraintData.cs b/SpineRuntimes/SpineRuntime35/IkConstraintData.cs index 70e3c1f8..f92d1987 100644 --- a/SpineRuntimes/SpineRuntime35/IkConstraintData.cs +++ b/SpineRuntimes/SpineRuntime35/IkConstraintData.cs @@ -32,50 +32,27 @@ using System.Collections.Generic; namespace SpineRuntime35 { - /// Stores the setup pose for an IkConstraint. public class IkConstraintData { - internal string name; + internal String name; internal int order; internal List bones = new List(); internal BoneData target; internal int bendDirection = 1; internal float mix = 1; - /// The IK constraint's name, which is unique within the skeleton. - public string Name { - get { return name; } - } - - public int Order { - get { return order; } - set { order = value; } - } - - /// The bones that are constrained by this IK Constraint. - public List Bones { - get { return bones; } - } - - /// The bone that is the IK target. - public BoneData Target { - get { return target; } - set { target = value; } - } - - /// Controls the bend direction of the IK bones, either 1 or -1. - public int BendDirection { - get { return bendDirection; } - set { bendDirection = value; } - } - + public String Name { get { return name; } } + public int Order { get { return order; } set { order = value; } } + public List Bones { get { return bones; } } + public BoneData Target { get { return target; } set { target = value; } } + public int BendDirection { get { return bendDirection; } set { bendDirection = value; } } public float Mix { get { return mix; } set { mix = value; } } - public IkConstraintData (string name) { + public IkConstraintData (String name) { if (name == null) throw new ArgumentNullException("name", "name cannot be null."); this.name = name; } - override public string ToString () { + override public String ToString () { return name; } } diff --git a/SpineRuntimes/SpineRuntime35/PathConstraint.cs b/SpineRuntimes/SpineRuntime35/PathConstraint.cs index 5c519fa1..e4c22cd7 100644 --- a/SpineRuntimes/SpineRuntime35/PathConstraint.cs +++ b/SpineRuntimes/SpineRuntime35/PathConstraint.cs @@ -32,7 +32,7 @@ namespace SpineRuntime35 { public class PathConstraint : IConstraint { - const int NONE = -1, BEFORE = -2, AFTER = -3; + private const int NONE = -1, BEFORE = -2, AFTER = -3; internal PathConstraintData data; internal ExposedList bones; @@ -84,17 +84,17 @@ public void Update () { RotateMode rotateMode = data.rotateMode; bool tangents = rotateMode == RotateMode.Tangent, scale = rotateMode == RotateMode.ChainScale; int boneCount = this.bones.Count, spacesCount = tangents ? boneCount : boneCount + 1; - Bone[] bonesItems = this.bones.Items; + Bone[] bones = this.bones.Items; ExposedList spaces = this.spaces.Resize(spacesCount), lengths = null; float spacing = this.spacing; if (scale || lengthSpacing) { if (scale) lengths = this.lengths.Resize(boneCount); for (int i = 0, n = spacesCount - 1; i < n;) { - Bone bone = bonesItems[i]; - float setupLength = bone.data.length, x = setupLength * bone.a, y = setupLength * bone.c; - float length = (float)Math.Sqrt(x * x + y * y); - if (scale) lengths.Items[i] = setupLength; - spaces.Items[++i] = (lengthSpacing ? Math.Max(0, setupLength + spacing) : spacing) * length / setupLength; + Bone bone = bones[i]; + float length = bone.data.length, x = length * bone.a, y = length * bone.c; + length = (float)Math.Sqrt(x * x + y * y); + if (scale) lengths.Items[i] = length; + spaces.Items[++i] = lengthSpacing ? Math.Max(0, length + spacing) : spacing; } } else { for (int i = 1; i < spacesCount; i++) @@ -113,7 +113,7 @@ public void Update () { offsetRotation *= p.a * p.d - p.b * p.c > 0 ? MathUtils.DegRad : -MathUtils.DegRad; } for (int i = 0, p = 3; i < boneCount; i++, p += 3) { - Bone bone = bonesItems[i]; + Bone bone = (Bone)bones[i]; bone.worldX += (boneX - bone.worldX) * translateMix; bone.worldY += (boneY - bone.worldY) * translateMix; float x = positions[p], y = positions[p + 1], dx = x - boneX, dy = y - boneY; @@ -166,7 +166,7 @@ float[] ComputeWorldPositions (PathAttachment path, int spacesCount, bool tangen Slot target = this.target; float position = this.position; - float[] spacesItems = this.spaces.Items, output = this.positions.Resize(spacesCount * 3 + 2).Items, world; + float[] spaces = this.spaces.Items, output = this.positions.Resize(spacesCount * 3 + 2).Items, world; bool closed = path.Closed; int verticesLength = path.WorldVerticesLength, curveCount = verticesLength / 6, prevCurve = NONE; @@ -178,11 +178,11 @@ float[] ComputeWorldPositions (PathAttachment path, int spacesCount, bool tangen if (percentPosition) position *= pathLength; if (percentSpacing) { for (int i = 0; i < spacesCount; i++) - spacesItems[i] *= pathLength; + spaces[i] *= pathLength; } world = this.world.Resize(8).Items; for (int i = 0, o = 0, curve = 0; i < spacesCount; i++, o += 3) { - float space = spacesItems[i]; + float space = spaces[i]; position += space; float p = position; @@ -286,13 +286,13 @@ float[] ComputeWorldPositions (PathAttachment path, int spacesCount, bool tangen if (percentPosition) position *= pathLength; if (percentSpacing) { for (int i = 0; i < spacesCount; i++) - spacesItems[i] *= pathLength; + spaces[i] *= pathLength; } float[] segments = this.segments; float curveLength = 0; for (int i = 0, o = 0, curve = 0, segment = 0; i < spacesCount; i++, o += 3) { - float space = spacesItems[i]; + float space = spaces[i]; position += space; float p = position; @@ -380,21 +380,21 @@ float[] ComputeWorldPositions (PathAttachment path, int spacesCount, bool tangen return output; } - static void AddBeforePosition (float p, float[] temp, int i, float[] output, int o) { + private void AddBeforePosition (float p, float[] temp, int i, float[] output, int o) { float x1 = temp[i], y1 = temp[i + 1], dx = temp[i + 2] - x1, dy = temp[i + 3] - y1, r = MathUtils.Atan2(dy, dx); output[o] = x1 + p * MathUtils.Cos(r); output[o + 1] = y1 + p * MathUtils.Sin(r); output[o + 2] = r; } - static void AddAfterPosition (float p, float[] temp, int i, float[] output, int o) { + private void AddAfterPosition (float p, float[] temp, int i, float[] output, int o) { float x1 = temp[i + 2], y1 = temp[i + 3], dx = x1 - temp[i], dy = y1 - temp[i + 1], r = MathUtils.Atan2(dy, dx); output[o] = x1 + p * MathUtils.Cos(r); output[o + 1] = y1 + p * MathUtils.Sin(r); output[o + 2] = r; } - static void AddCurvePosition (float p, float x1, float y1, float cx1, float cy1, float cx2, float cy2, float x2, float y2, + private void AddCurvePosition (float p, float x1, float y1, float cx1, float cy1, float cx2, float cy2, float x2, float y2, float[] output, int o, bool tangents) { if (p == 0 || float.IsNaN(p)) p = 0.0001f; float tt = p * p, ttt = tt * p, u = 1 - p, uu = u * u, uuu = uu * u; diff --git a/SpineRuntimes/SpineRuntime35/Skeleton.cs b/SpineRuntimes/SpineRuntime35/Skeleton.cs index d12e9b3b..c7a96a2d 100644 --- a/SpineRuntimes/SpineRuntime35/Skeleton.cs +++ b/SpineRuntimes/SpineRuntime35/Skeleton.cs @@ -45,8 +45,8 @@ public class Skeleton { internal Skin skin; internal float r = 1, g = 1, b = 1, a = 1; internal float time; - internal float scaleX = 1, scaleY = 1; - internal float x, y; + internal bool flipX, flipY; + internal float x, y; public SkeletonData Data { get { return data; } } public ExposedList Bones { get { return bones; } } @@ -64,14 +64,8 @@ public class Skeleton { public float Time { get { return time; } set { time = value; } } public float X { get { return x; } set { x = value; } } public float Y { get { return y; } set { y = value; } } - public float ScaleX { get { return scaleX; } set { scaleX = value; } } - public float ScaleY { get { return scaleY; } set { scaleY = value; } } - - [Obsolete("Use ScaleX instead. FlipX is when ScaleX is negative.")] - public bool FlipX { get { return scaleX < 0; } set { scaleX = value ? -1f : 1f; } } - - [Obsolete("Use ScaleY instead. FlipY is when ScaleY is negative.")] - public bool FlipY { get { return scaleY < 0; } set { scaleY = value ? -1f : 1f; } } + public bool FlipX { get { return flipX; } set { flipX = value; } } + public bool FlipY { get { return flipY; } set { flipY = value; } } public Bone RootBone { get { return bones.Count == 0 ? null : bones.Items[0]; } @@ -200,15 +194,15 @@ private void SortPathConstraint (PathConstraint constraint) { var constrained = constraint.bones; int boneCount = constrained.Count; - for (int i = 0; i < boneCount; i++) - SortBone(constrained.Items[i]); + for (int ii = 0; ii < boneCount; ii++) + SortBone(constrained.Items[ii]); updateCache.Add(constraint); - for (int i = 0; i < boneCount; i++) - SortReset(constrained.Items[i].children); - for (int i = 0; i < boneCount; i++) - constrained.Items[i].sorted = true; + for (int ii = 0; ii < boneCount; ii++) + SortReset(constrained.Items[ii].children); + for (int ii = 0; ii < boneCount; ii++) + constrained.Items[ii].sorted = true; } private void SortTransformConstraint (TransformConstraint constraint) { @@ -216,23 +210,15 @@ private void SortTransformConstraint (TransformConstraint constraint) { var constrained = constraint.bones; int boneCount = constrained.Count; - if (constraint.data.local) { - for (int i = 0; i < boneCount; i++) { - Bone child = constrained.Items[i]; - SortBone(child.parent); - if (!updateCache.Contains(child)) updateCacheReset.Add(child); - } - } else { - for (int i = 0; i < boneCount; i++) - SortBone(constrained.Items[i]); - } + for (int ii = 0; ii < boneCount; ii++) + SortBone(constrained.Items[ii]); updateCache.Add(constraint); - for (int i = 0; i < boneCount; i++) - SortReset(constrained.Items[i].children); - for (int i = 0; i < boneCount; i++) - constrained.Items[i].sorted = true; + for (int ii = 0; ii < boneCount; ii++) + SortReset(constrained.Items[ii].children); + for (int ii = 0; ii < boneCount; ii++) + constrained.Items[ii].sorted = true; } private void SortPathConstraintAttachment (Skin skin, int slotIndex, Bone slotBone) { @@ -264,7 +250,7 @@ private void SortBone (Bone bone) { updateCache.Add(bone); } - private static void SortReset (ExposedList bones) { + private void SortReset (ExposedList bones) { var bonesItems = bones.Items; for (int i = 0, n = bones.Count; i < n; i++) { Bone bone = bonesItems[i]; @@ -315,21 +301,21 @@ public void SetBonesToSetupPose () { var transformConstraintsItems = this.transformConstraints.Items; for (int i = 0, n = transformConstraints.Count; i < n; i++) { TransformConstraint constraint = transformConstraintsItems[i]; - TransformConstraintData constraintData = constraint.data; - constraint.rotateMix = constraintData.rotateMix; - constraint.translateMix = constraintData.translateMix; - constraint.scaleMix = constraintData.scaleMix; - constraint.shearMix = constraintData.shearMix; + TransformConstraintData data = constraint.data; + constraint.rotateMix = data.rotateMix; + constraint.translateMix = data.translateMix; + constraint.scaleMix = data.scaleMix; + constraint.shearMix = data.shearMix; } var pathConstraintItems = this.pathConstraints.Items; for (int i = 0, n = pathConstraints.Count; i < n; i++) { PathConstraint constraint = pathConstraintItems[i]; - PathConstraintData constraintData = constraint.data; - constraint.position = constraintData.position; - constraint.spacing = constraintData.spacing; - constraint.rotateMix = constraintData.rotateMix; - constraint.translateMix = constraintData.translateMix; + PathConstraintData data = constraint.data; + constraint.position = data.position; + constraint.spacing = data.spacing; + constraint.rotateMix = data.rotateMix; + constraint.translateMix = data.translateMix; } } @@ -345,7 +331,7 @@ public void SetSlotsToSetupPose () { } /// May be null. - public Bone FindBone (string boneName) { + public Bone FindBone (String boneName) { if (boneName == null) throw new ArgumentNullException("boneName", "boneName cannot be null."); var bones = this.bones; var bonesItems = bones.Items; @@ -357,7 +343,7 @@ public Bone FindBone (string boneName) { } /// -1 if the bone was not found. - public int FindBoneIndex (string boneName) { + public int FindBoneIndex (String boneName) { if (boneName == null) throw new ArgumentNullException("boneName", "boneName cannot be null."); var bones = this.bones; var bonesItems = bones.Items; @@ -367,7 +353,7 @@ public int FindBoneIndex (string boneName) { } /// May be null. - public Slot FindSlot (string slotName) { + public Slot FindSlot (String slotName) { if (slotName == null) throw new ArgumentNullException("slotName", "slotName cannot be null."); var slots = this.slots; var slotsItems = slots.Items; @@ -379,7 +365,7 @@ public Slot FindSlot (string slotName) { } /// -1 if the bone was not found. - public int FindSlotIndex (string slotName) { + public int FindSlotIndex (String slotName) { if (slotName == null) throw new ArgumentNullException("slotName", "slotName cannot be null."); var slots = this.slots; var slotsItems = slots.Items; @@ -389,10 +375,10 @@ public int FindSlotIndex (string slotName) { } /// Sets a skin by name (see SetSkin). - public void SetSkin (string skinName) { - Skin foundSkin = data.FindSkin(skinName); - if (foundSkin == null) throw new ArgumentException("Skin not found: " + skinName, "skinName"); - SetSkin(foundSkin); + public void SetSkin (String skinName) { + Skin skin = data.FindSkin(skinName); + if (skin == null) throw new ArgumentException("Skin not found: " + skinName, "skinName"); + SetSkin(skin); } /// Sets the skin used to look up attachments before looking in the {@link SkeletonData#getDefaultSkin() default @@ -407,7 +393,7 @@ public void SetSkin (Skin newSkin) { ExposedList slots = this.slots; for (int i = 0, n = slots.Count; i < n; i++) { Slot slot = slots.Items[i]; - string name = slot.data.attachmentName; + String name = slot.data.attachmentName; if (name != null) { Attachment attachment = newSkin.GetAttachment(i, name); if (attachment != null) slot.Attachment = attachment; @@ -419,22 +405,23 @@ public void SetSkin (Skin newSkin) { } /// May be null. - public Attachment GetAttachment (string slotName, string attachmentName) { + public Attachment GetAttachment (String slotName, String attachmentName) { return GetAttachment(data.FindSlotIndex(slotName), attachmentName); } /// May be null. - public Attachment GetAttachment (int slotIndex, string attachmentName) { + public Attachment GetAttachment (int slotIndex, String attachmentName) { if (attachmentName == null) throw new ArgumentNullException("attachmentName", "attachmentName cannot be null."); if (skin != null) { Attachment attachment = skin.GetAttachment(slotIndex, attachmentName); if (attachment != null) return attachment; } - return data.defaultSkin != null ? data.defaultSkin.GetAttachment(slotIndex, attachmentName) : null; + if (data.defaultSkin != null) return data.defaultSkin.GetAttachment(slotIndex, attachmentName); + return null; } /// May be null. - public void SetAttachment (string slotName, string attachmentName) { + public void SetAttachment (String slotName, String attachmentName) { if (slotName == null) throw new ArgumentNullException("slotName", "slotName cannot be null."); ExposedList slots = this.slots; for (int i = 0, n = slots.Count; i < n; i++) { @@ -453,7 +440,7 @@ public void SetAttachment (string slotName, string attachmentName) { } /// May be null. - public IkConstraint FindIkConstraint (string constraintName) { + public IkConstraint FindIkConstraint (String constraintName) { if (constraintName == null) throw new ArgumentNullException("constraintName", "constraintName cannot be null."); ExposedList ikConstraints = this.ikConstraints; for (int i = 0, n = ikConstraints.Count; i < n; i++) { @@ -464,7 +451,7 @@ public IkConstraint FindIkConstraint (string constraintName) { } /// May be null. - public TransformConstraint FindTransformConstraint (string constraintName) { + public TransformConstraint FindTransformConstraint (String constraintName) { if (constraintName == null) throw new ArgumentNullException("constraintName", "constraintName cannot be null."); ExposedList transformConstraints = this.transformConstraints; for (int i = 0, n = transformConstraints.Count; i < n; i++) { @@ -475,7 +462,7 @@ public TransformConstraint FindTransformConstraint (string constraintName) { } /// May be null. - public PathConstraint FindPathConstraint (string constraintName) { + public PathConstraint FindPathConstraint (String constraintName) { if (constraintName == null) throw new ArgumentNullException("constraintName", "constraintName cannot be null."); ExposedList pathConstraints = this.pathConstraints; for (int i = 0, n = pathConstraints.Count; i < n; i++) { @@ -488,53 +475,5 @@ public PathConstraint FindPathConstraint (string constraintName) { public void Update (float delta) { time += delta; } - - /// 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. - /// The width of the AABB - /// The height of the AABB. - /// Reference to hold a float[]. May be a null reference. This method will assign it a new float[] with the appropriate size as needed. - public void GetBounds (out float x, out float y, out float width, out float height, ref float[] vertexBuffer) { - float[] temp = vertexBuffer; - temp = temp ?? new float[8]; - var drawOrderItems = this.drawOrder.Items; - float minX = int.MaxValue, minY = int.MaxValue, maxX = int.MinValue, maxY = int.MinValue; - for (int i = 0, n = this.drawOrder.Count; i < n; i++) { - Slot slot = drawOrderItems[i]; - int verticesLength = 0; - float[] vertices = null; - Attachment attachment = slot.attachment; - var regionAttachment = attachment as RegionAttachment; - if (regionAttachment != null) { - verticesLength = 8; - if (temp.Length < 8) temp = new float[8]; - regionAttachment.ComputeWorldVertices(slot.bone, temp, 0); - } else { - var meshAttachment = attachment as MeshAttachment; - if (meshAttachment != null) { - MeshAttachment mesh = meshAttachment; - verticesLength = mesh.WorldVerticesLength; - if (temp.Length < verticesLength) temp = new float[verticesLength]; - mesh.ComputeWorldVertices(slot, 0, verticesLength, temp, 0); - } - } - - if (vertices != null) { - for (int ii = 0; ii + 1 < verticesLength; ii += 2) { - float vx = vertices[ii], vy = vertices[ii + 1]; - minX = Math.Min(minX, vx); - minY = Math.Min(minY, vy); - maxX = Math.Max(maxX, vx); - maxY = Math.Max(maxY, vy); - } - } - } - x = minX; - y = minY; - width = maxX - minX; - height = maxY - minY; - vertexBuffer = temp; - } } } diff --git a/SpineRuntimes/SpineRuntime35/SkeletonBinary.cs b/SpineRuntimes/SpineRuntime35/SkeletonBinary.cs index 7dbe349e..badf7b12 100644 --- a/SpineRuntimes/SpineRuntime35/SkeletonBinary.cs +++ b/SpineRuntimes/SpineRuntime35/SkeletonBinary.cs @@ -50,7 +50,6 @@ public class SkeletonBinary { public const int SLOT_ATTACHMENT = 0; public const int SLOT_COLOR = 1; - public const int SLOT_TWO_COLOR = 2; public const int PATH_POSITION = 0; public const int PATH_SPACING = 1; @@ -183,15 +182,6 @@ public SkeletonData ReadSkeletonData (Stream input) { slotData.g = ((color & 0x00ff0000) >> 16) / 255f; slotData.b = ((color & 0x0000ff00) >> 8) / 255f; slotData.a = ((color & 0x000000ff)) / 255f; - - int darkColor = ReadInt(input); // 0x00rrggbb - if (darkColor != -1) { - slotData.hasSecondColor = true; - slotData.r2 = ((darkColor & 0x00ff0000) >> 16) / 255f; - slotData.g2 = ((darkColor & 0x0000ff00) >> 8) / 255f; - slotData.b2 = ((darkColor & 0x000000ff)) / 255f; - } - slotData.attachmentName = ReadString(input); slotData.blendMode = (BlendMode)ReadVarint(input, true); skeletonData.slots.Add(slotData); @@ -216,8 +206,6 @@ public SkeletonData ReadSkeletonData (Stream input) { for (int ii = 0, nn = ReadVarint(input, true); ii < nn; ii++) data.bones.Add(skeletonData.bones.Items[ReadVarint(input, true)]); data.target = skeletonData.bones.Items[ReadVarint(input, true)]; - data.local = ReadBoolean(input); - data.relative = ReadBoolean(input); data.offsetRotation = ReadFloat(input); data.offsetX = ReadFloat(input) * scale; data.offsetY = ReadFloat(input) * scale; @@ -252,7 +240,7 @@ public SkeletonData ReadSkeletonData (Stream input) { } // Default skin. - Skin defaultSkin = ReadSkin(input, skeletonData, "default", nonessential); + Skin defaultSkin = ReadSkin(input, "default", nonessential); if (defaultSkin != null) { skeletonData.defaultSkin = defaultSkin; skeletonData.skins.Add(defaultSkin); @@ -260,7 +248,7 @@ public SkeletonData ReadSkeletonData (Stream input) { // Skins. for (int i = 0, n = ReadVarint(input, true); i < n; i++) - skeletonData.skins.Add(ReadSkin(input, skeletonData, ReadString(input), nonessential)); + skeletonData.skins.Add(ReadSkin(input, ReadString(input), nonessential)); // Linked meshes. for (int i = 0, n = linkedMeshes.Count; i < n; i++) { @@ -299,7 +287,7 @@ public SkeletonData ReadSkeletonData (Stream input) { /// May be null. - private Skin ReadSkin (Stream input, SkeletonData skeletonData, String skinName, bool nonessential) { + private Skin ReadSkin (Stream input, String skinName, bool nonessential) { int slotCount = ReadVarint(input, true); if (slotCount == 0) return null; Skin skin = new Skin(skinName); @@ -307,14 +295,14 @@ private Skin ReadSkin (Stream input, SkeletonData skeletonData, String skinName, int slotIndex = ReadVarint(input, true); for (int ii = 0, nn = ReadVarint(input, true); ii < nn; ii++) { String name = ReadString(input); - Attachment attachment = ReadAttachment(input, skeletonData, skin, slotIndex, name, nonessential); + Attachment attachment = ReadAttachment(input, skin, slotIndex, name, nonessential); if (attachment != null) skin.AddAttachment(slotIndex, name, attachment); } } return skin; } - private Attachment ReadAttachment (Stream input, SkeletonData skeletonData, Skin skin, int slotIndex, String attachmentName, bool nonessential) { + private Attachment ReadAttachment (Stream input, Skin skin, int slotIndex, String attachmentName, bool nonessential) { float scale = Scale; String name = ReadString(input); @@ -437,7 +425,7 @@ private Attachment ReadAttachment (Stream input, SkeletonData skeletonData, Skin float[] lengths = new float[vertexCount / 3]; for (int i = 0, n = lengths.Length; i < n; i++) lengths[i] = ReadFloat(input) * scale; - if (nonessential) ReadInt(input); //int color = nonessential ? ReadInt(input) : 0; + if (nonessential) ReadInt(input); //int color = nonessential ? ReadInt(input) : 0; // Avoid unused local warning. PathAttachment path = attachmentLoader.NewPathAttachment(skin, name); if (path == null) return null; @@ -448,35 +436,7 @@ private Attachment ReadAttachment (Stream input, SkeletonData skeletonData, Skin path.bones = vertices.bones; path.lengths = lengths; return path; - } - case AttachmentType.Point: { - float rotation = ReadFloat(input); - float x = ReadFloat(input); - float y = ReadFloat(input); - if (nonessential) ReadInt(input); //int color = nonessential ? ReadInt(input) : 0; - - PointAttachment point = attachmentLoader.NewPointAttachment(skin, name); - if (point == null) return null; - point.x = x * scale; - point.y = y * scale; - point.rotation = rotation; - //if (nonessential) point.color = color; - return point; - } - case AttachmentType.Clipping: { - int endSlotIndex = ReadVarint(input, true); - int vertexCount = ReadVarint(input, true); - Vertices vertices = ReadVertices(input, vertexCount); - if (nonessential) ReadInt(input); - - ClippingAttachment clip = attachmentLoader.NewClippingAttachment(skin, name); - if (clip == null) return null; - clip.EndSlot = skeletonData.slots.Items[endSlotIndex]; - clip.worldVerticesLength = vertexCount << 1; - clip.vertices = vertices.vertices; - clip.bones = vertices.bones; - return clip; - } + } } return null; } @@ -539,15 +499,6 @@ private void ReadAnimation (String name, Stream input, SkeletonData skeletonData int timelineType = input.ReadByte(); int frameCount = ReadVarint(input, true); switch (timelineType) { - case SLOT_ATTACHMENT: { - AttachmentTimeline timeline = new AttachmentTimeline(frameCount); - timeline.slotIndex = slotIndex; - for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) - timeline.SetFrame(frameIndex, ReadFloat(input), ReadString(input)); - timelines.Add(timeline); - duration = Math.Max(duration, timeline.frames[frameCount - 1]); - break; - } case SLOT_COLOR: { ColorTimeline timeline = new ColorTimeline(frameCount); timeline.slotIndex = slotIndex; @@ -565,26 +516,13 @@ private void ReadAnimation (String name, Stream input, SkeletonData skeletonData duration = Math.Max(duration, timeline.frames[(timeline.FrameCount - 1) * ColorTimeline.ENTRIES]); break; } - case SLOT_TWO_COLOR: { - TwoColorTimeline timeline = new TwoColorTimeline(frameCount); + case SLOT_ATTACHMENT: { + AttachmentTimeline timeline = new AttachmentTimeline(frameCount); timeline.slotIndex = slotIndex; - for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) { - float time = ReadFloat(input); - int color = ReadInt(input); - float r = ((color & 0xff000000) >> 24) / 255f; - float g = ((color & 0x00ff0000) >> 16) / 255f; - float b = ((color & 0x0000ff00) >> 8) / 255f; - float a = ((color & 0x000000ff)) / 255f; - int color2 = ReadInt(input); // 0x00rrggbb - float r2 = ((color2 & 0x00ff0000) >> 16) / 255f; - float g2 = ((color2 & 0x0000ff00) >> 8) / 255f; - float b2 = ((color2 & 0x000000ff)) / 255f; - - timeline.SetFrame(frameIndex, time, r, g, b, a, r2, g2, b2); - if (frameIndex < frameCount - 1) ReadCurve(input, frameIndex, timeline); - } + for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) + timeline.SetFrame(frameIndex, ReadFloat(input), ReadString(input)); timelines.Add(timeline); - duration = Math.Max(duration, timeline.frames[(timeline.FrameCount - 1) * TwoColorTimeline.ENTRIES]); + duration = Math.Max(duration, timeline.frames[frameCount - 1]); break; } } diff --git a/SpineRuntimes/SpineRuntime35/SkeletonBounds.cs b/SpineRuntimes/SpineRuntime35/SkeletonBounds.cs index 38c7002f..120dd050 100644 --- a/SpineRuntimes/SpineRuntime35/SkeletonBounds.cs +++ b/SpineRuntimes/SpineRuntime35/SkeletonBounds.cs @@ -31,11 +31,6 @@ using System; namespace SpineRuntime35 { - - /// - /// Collects each BoundingBoxAttachment that is visible and computes the world vertices for its polygon. - /// The polygon vertices are provided along with convenience methods for doing hit detection. - /// public class SkeletonBounds { private ExposedList polygonPool = new ExposedList(); private float minX, minY, maxX, maxY; @@ -54,14 +49,6 @@ public SkeletonBounds () { Polygons = new ExposedList(); } - /// - /// Clears any previous polygons, finds all visible bounding box attachments, - /// and computes the world vertices for each bounding box's polygon. - /// The skeleton. - /// - /// If true, the axis aligned bounding box containing all the polygons is computed. - /// If false, the SkeletonBounds AABB methods will always return true. - /// public void Update (Skeleton skeleton, bool updateAabb) { ExposedList boundingBoxes = BoundingBoxes; ExposedList polygons = Polygons; @@ -88,7 +75,7 @@ public void Update (Skeleton skeleton, bool updateAabb) { polygon = new Polygon(); polygons.Add(polygon); - int count = boundingBox.worldVerticesLength; + int count = boundingBox.Vertices.Length; polygon.Count = count; if (polygon.Vertices.Length < count) polygon.Vertices = new float[count]; boundingBox.ComputeWorldVertices(slot, polygon.Vertices); diff --git a/SpineRuntimes/SpineRuntime35/SkeletonClipping.cs b/SpineRuntimes/SpineRuntime35/SkeletonClipping.cs deleted file mode 100644 index 1ffc2591..00000000 --- a/SpineRuntimes/SpineRuntime35/SkeletonClipping.cs +++ /dev/null @@ -1,285 +0,0 @@ -/****************************************************************************** - * Spine Runtimes Software License v2.5 - * - * Copyright (c) 2013-2016, Esoteric Software - * All rights reserved. - * - * You are granted a perpetual, non-exclusive, non-sublicensable, and - * non-transferable license to use, install, execute, and perform the Spine - * Runtimes software and derivative works solely for personal or internal - * use. Without the written permission of Esoteric Software (see Section 2 of - * the Spine Software License Agreement), you may not (a) modify, translate, - * adapt, or develop new applications using the Spine Runtimes or otherwise - * create derivative works or improvements of the Spine Runtimes or (b) remove, - * delete, alter, or obscure any trademarks or any copyright, trademark, patent, - * or other intellectual property or proprietary rights notices on or in the - * Software, including any copy thereof. Redistributions in binary or source - * form must include this license and terms. - * - * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, 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 THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - *****************************************************************************/ - -using System; - -namespace SpineRuntime35 { - public class SkeletonClipping { - internal readonly Triangulator triangulator = new Triangulator(); - internal readonly ExposedList clippingPolygon = new ExposedList(); - internal readonly ExposedList clipOutput = new ExposedList(128); - internal readonly ExposedList clippedVertices = new ExposedList(128); - internal readonly ExposedList clippedTriangles = new ExposedList(128); - internal readonly ExposedList clippedUVs = new ExposedList(128); - internal readonly ExposedList scratch = new ExposedList(); - - internal ClippingAttachment clipAttachment; - internal ExposedList> clippingPolygons; - - public ExposedList ClippedVertices { get { return clippedVertices; } } - public ExposedList ClippedTriangles { get { return clippedTriangles; } } - public ExposedList ClippedUVs { get { return clippedUVs; } } - - public bool IsClipping () { return clipAttachment != null; } - - public int ClipStart (Slot slot, ClippingAttachment clip) { - if (clipAttachment != null) return 0; - clipAttachment = clip; - - int n = clip.worldVerticesLength; - float[] vertices = clippingPolygon.Resize(n).Items; - clip.ComputeWorldVertices(slot, 0, n, vertices, 0, 2); - MakeClockwise(clippingPolygon); - clippingPolygons = triangulator.Decompose(clippingPolygon, triangulator.Triangulate(clippingPolygon)); - foreach (var polygon in clippingPolygons) { - MakeClockwise(polygon); - polygon.Add(polygon.Items[0]); - polygon.Add(polygon.Items[1]); - } - return clippingPolygons.Count; - } - - public void ClipEnd (Slot slot) { - if (clipAttachment != null && clipAttachment.endSlot == slot.data) ClipEnd(); - } - - public void ClipEnd () { - if (clipAttachment == null) return; - clipAttachment = null; - clippingPolygons = null; - clippedVertices.Clear(); - clippedTriangles.Clear(); - clippingPolygon.Clear(); - } - - public void ClipTriangles (float[] vertices, int verticesLength, int[] triangles, int trianglesLength, float[] uvs) { - ExposedList clipOutput = this.clipOutput, clippedVertices = this.clippedVertices; - var clippedTriangles = this.clippedTriangles; - var polygons = clippingPolygons.Items; - int polygonsCount = clippingPolygons.Count; - - int index = 0; - clippedVertices.Clear(); - clippedUVs.Clear(); - clippedTriangles.Clear(); - //outer: // libgdx - for (int i = 0; i < trianglesLength; i += 3) { - int vertexOffset = triangles[i] << 1; - float x1 = vertices[vertexOffset], y1 = vertices[vertexOffset + 1]; - float u1 = uvs[vertexOffset], v1 = uvs[vertexOffset + 1]; - - vertexOffset = triangles[i + 1] << 1; - float x2 = vertices[vertexOffset], y2 = vertices[vertexOffset + 1]; - float u2 = uvs[vertexOffset], v2 = uvs[vertexOffset + 1]; - - vertexOffset = triangles[i + 2] << 1; - float x3 = vertices[vertexOffset], y3 = vertices[vertexOffset + 1]; - float u3 = uvs[vertexOffset], v3 = uvs[vertexOffset + 1]; - - for (int p = 0; p < polygonsCount; p++) { - int s = clippedVertices.Count; - if (Clip(x1, y1, x2, y2, x3, y3, polygons[p], clipOutput)) { - int clipOutputLength = clipOutput.Count; - if (clipOutputLength == 0) continue; - float d0 = y2 - y3, d1 = x3 - x2, d2 = x1 - x3, d4 = y3 - y1; - float d = 1 / (d0 * d2 + d1 * (y1 - y3)); - - int clipOutputCount = clipOutputLength >> 1; - float[] clipOutputItems = clipOutput.Items; - float[] clippedVerticesItems = clippedVertices.Resize(s + clipOutputCount * 2).Items; - float[] clippedUVsItems = clippedUVs.Resize(s + clipOutputCount * 2).Items; - for (int ii = 0; ii < clipOutputLength; ii += 2) { - float x = clipOutputItems[ii], y = clipOutputItems[ii + 1]; - clippedVerticesItems[s] = x; - clippedVerticesItems[s + 1] = y; - float c0 = x - x3, c1 = y - y3; - float a = (d0 * c0 + d1 * c1) * d; - float b = (d4 * c0 + d2 * c1) * d; - float c = 1 - a - b; - clippedUVsItems[s] = u1 * a + u2 * b + u3 * c; - clippedUVsItems[s + 1] = v1 * a + v2 * b + v3 * c; - s += 2; - } - - s = clippedTriangles.Count; - int[] clippedTrianglesItems = clippedTriangles.Resize(s + 3 * (clipOutputCount - 2)).Items; - clipOutputCount--; - for (int ii = 1; ii < clipOutputCount; ii++) { - clippedTrianglesItems[s] = index; - clippedTrianglesItems[s + 1] = index + ii; - clippedTrianglesItems[s + 2] = index + ii + 1; - s += 3; - } - index += clipOutputCount + 1; - } - else { - float[] clippedVerticesItems = clippedVertices.Resize(s + 3 * 2).Items; - float[] clippedUVsItems = clippedUVs.Resize(s + 3 * 2).Items; - clippedVerticesItems[s] = x1; - clippedVerticesItems[s + 1] = y1; - clippedVerticesItems[s + 2] = x2; - clippedVerticesItems[s + 3] = y2; - clippedVerticesItems[s + 4] = x3; - clippedVerticesItems[s + 5] = y3; - - clippedUVsItems[s] = u1; - clippedUVsItems[s + 1] = v1; - clippedUVsItems[s + 2] = u2; - clippedUVsItems[s + 3] = v2; - clippedUVsItems[s + 4] = u3; - clippedUVsItems[s + 5] = v3; - - s = clippedTriangles.Count; - int[] clippedTrianglesItems = clippedTriangles.Resize(s + 3).Items; - clippedTrianglesItems[s] = index; - clippedTrianglesItems[s + 1] = index + 1; - clippedTrianglesItems[s + 2] = index + 2; - index += 3; - break; //continue outer; - } - } - } - - } - - /** Clips the input triangle against the convex, clockwise clipping area. If the triangle lies entirely within the clipping - * area, false is returned. The clipping area must duplicate the first vertex at the end of the vertices list. */ - internal bool Clip (float x1, float y1, float x2, float y2, float x3, float y3, ExposedList clippingArea, ExposedList output) { - var originalOutput = output; - var clipped = false; - - // Avoid copy at the end. - ExposedList input = null; - if (clippingArea.Count % 4 >= 2) { - input = output; - output = scratch; - } - else - input = scratch; - - input.Clear(); - input.Add(x1); - input.Add(y1); - input.Add(x2); - input.Add(y2); - input.Add(x3); - input.Add(y3); - input.Add(x1); - input.Add(y1); - output.Clear(); - - float[] clippingVertices = clippingArea.Items; - int clippingVerticesLast = clippingArea.Count - 4; - for (int i = 0; ; i += 2) { - float edgeX = clippingVertices[i], edgeY = clippingVertices[i + 1]; - float edgeX2 = clippingVertices[i + 2], edgeY2 = clippingVertices[i + 3]; - float deltaX = edgeX - edgeX2, deltaY = edgeY - edgeY2; - - float[] inputVertices = input.Items; - int inputVerticesLength = input.Count - 2, outputStart = output.Count; - for (int ii = 0; ii < inputVerticesLength; ii += 2) { - float inputX = inputVertices[ii], inputY = inputVertices[ii + 1]; - float inputX2 = inputVertices[ii + 2], inputY2 = inputVertices[ii + 3]; - bool side2 = deltaX * (inputY2 - edgeY2) - deltaY * (inputX2 - edgeX2) > 0; - if (deltaX * (inputY - edgeY2) - deltaY * (inputX - edgeX2) > 0) { - if (side2) { // v1 inside, v2 inside - output.Add(inputX2); - output.Add(inputY2); - continue; - } - // v1 inside, v2 outside - float c0 = inputY2 - inputY, c2 = inputX2 - inputX; - float ua = (c2 * (edgeY - inputY) - c0 * (edgeX - inputX)) / (c0 * (edgeX2 - edgeX) - c2 * (edgeY2 - edgeY)); - output.Add(edgeX + (edgeX2 - edgeX) * ua); - output.Add(edgeY + (edgeY2 - edgeY) * ua); - } - else if (side2) { // v1 outside, v2 inside - float c0 = inputY2 - inputY, c2 = inputX2 - inputX; - float ua = (c2 * (edgeY - inputY) - c0 * (edgeX - inputX)) / (c0 * (edgeX2 - edgeX) - c2 * (edgeY2 - edgeY)); - output.Add(edgeX + (edgeX2 - edgeX) * ua); - output.Add(edgeY + (edgeY2 - edgeY) * ua); - output.Add(inputX2); - output.Add(inputY2); - } - clipped = true; - } - - if (outputStart == output.Count) { // All edges outside. - originalOutput.Clear(); - return true; - } - - output.Add(output.Items[0]); - output.Add(output.Items[1]); - - if (i == clippingVerticesLast) break; - var temp = output; - output = input; - output.Clear(); - input = temp; - } - - if (originalOutput != output) { - originalOutput.Clear(); - for (int i = 0, n = output.Count - 2; i < n; i++) { - originalOutput.Add(output.Items[i]); - } - } - else - originalOutput.Resize(originalOutput.Count - 2); - - return clipped; - } - - static void MakeClockwise (ExposedList polygon) { - float[] vertices = polygon.Items; - int verticeslength = polygon.Count; - - float area = vertices[verticeslength - 2] * vertices[1] - vertices[0] * vertices[verticeslength - 1], p1x, p1y, p2x, p2y; - for (int i = 0, n = verticeslength - 3; i < n; i += 2) { - p1x = vertices[i]; - p1y = vertices[i + 1]; - p2x = vertices[i + 2]; - p2y = vertices[i + 3]; - area += p1x * p2y - p2x * p1y; - } - if (area < 0) return; - - for (int i = 0, lastX = verticeslength - 2, n = verticeslength >> 1; i < n; i += 2) { - float x = vertices[i], y = vertices[i + 1]; - int other = lastX - i; - vertices[i] = vertices[other]; - vertices[i + 1] = vertices[other + 1]; - vertices[other] = x; - vertices[other + 1] = y; - } - } - } -} diff --git a/SpineRuntimes/SpineRuntime35/SkeletonData.cs b/SpineRuntimes/SpineRuntime35/SkeletonData.cs index 5cb6eead..2a1535a5 100644 --- a/SpineRuntimes/SpineRuntime35/SkeletonData.cs +++ b/SpineRuntimes/SpineRuntime35/SkeletonData.cs @@ -31,12 +31,10 @@ using System; namespace SpineRuntime35 { - - /// Stores the setup pose and all of the stateless data for a skeleton. public class SkeletonData { internal string name; - internal ExposedList bones = new ExposedList(); // Ordered parents first - internal ExposedList slots = new ExposedList(); // Setup pose draw order. + internal ExposedList bones = new ExposedList(); + internal ExposedList slots = new ExposedList(); internal ExposedList skins = new ExposedList(); internal Skin defaultSkin; internal ExposedList events = new ExposedList(); @@ -51,23 +49,12 @@ public class SkeletonData { internal float fps; internal string imagesPath; - public string Name { get { return name; } set { name = value; } } - - /// The skeleton's bones, sorted parent first. The root bone is always the first bone. - public ExposedList Bones { get { return bones; } } - - public ExposedList Slots { get { return slots; } } - - /// All skins, including the default skin. + public String Name { get { return name; } set { name = value; } } + public ExposedList Bones { get { return bones; } } // Ordered parents first. + public ExposedList Slots { get { return slots; } } // Setup pose draw order. public ExposedList Skins { get { return skins; } set { skins = value; } } - - /// - /// The skeleton's default skin. - /// By default this skin contains all attachments that were not in a skin in Spine. - /// - /// May be null. + /// May be null. public Skin DefaultSkin { get { return defaultSkin; } set { defaultSkin = value; } } - public ExposedList Events { get { return events; } set { events = value; } } public ExposedList Animations { get { return animations; } set { animations = value; } } public ExposedList IkConstraints { get { return ikConstraints; } set { ikConstraints = value; } } @@ -80,42 +67,34 @@ public class SkeletonData { public string Version { get { return version; } set { version = value; } } public string Hash { get { return hash; } set { hash = value; } } public string ImagesPath { get { return imagesPath; } set { imagesPath = value; } } - - /// - /// The dopesheet FPS in Spine. Available only when nonessential data was exported. public float Fps { get { return fps; } set { fps = value; } } // --- Bones. - /// - /// Finds a bone by comparing each bone's name. - /// It is more efficient to cache the results of this method than to call it multiple times. /// May be null. - public BoneData FindBone (string boneName) { + public BoneData FindBone (String boneName) { if (boneName == null) throw new ArgumentNullException("boneName", "boneName cannot be null."); - var bones = this.bones; - var bonesItems = bones.Items; + ExposedList bones = this.bones; for (int i = 0, n = bones.Count; i < n; i++) { - BoneData bone = bonesItems[i]; + BoneData bone = bones.Items[i]; if (bone.name == boneName) return bone; } return null; } /// -1 if the bone was not found. - public int FindBoneIndex (string boneName) { + public int FindBoneIndex (String boneName) { if (boneName == null) throw new ArgumentNullException("boneName", "boneName cannot be null."); - var bones = this.bones; - var bonesItems = bones.Items; + ExposedList bones = this.bones; for (int i = 0, n = bones.Count; i < n; i++) - if (bonesItems[i].name == boneName) return i; + if (bones.Items[i].name == boneName) return i; return -1; } // --- Slots. /// May be null. - public SlotData FindSlot (string slotName) { + public SlotData FindSlot (String slotName) { if (slotName == null) throw new ArgumentNullException("slotName", "slotName cannot be null."); ExposedList slots = this.slots; for (int i = 0, n = slots.Count; i < n; i++) { @@ -126,7 +105,7 @@ public SlotData FindSlot (string slotName) { } /// -1 if the slot was not found. - public int FindSlotIndex (string slotName) { + public int FindSlotIndex (String slotName) { if (slotName == null) throw new ArgumentNullException("slotName", "slotName cannot be null."); ExposedList slots = this.slots; for (int i = 0, n = slots.Count; i < n; i++) @@ -137,7 +116,7 @@ public int FindSlotIndex (string slotName) { // --- Skins. /// May be null. - public Skin FindSkin (string skinName) { + public Skin FindSkin (String skinName) { if (skinName == null) throw new ArgumentNullException("skinName", "skinName cannot be null."); foreach (Skin skin in skins) if (skin.name == skinName) return skin; @@ -147,7 +126,7 @@ public Skin FindSkin (string skinName) { // --- Events. /// May be null. - public EventData FindEvent (string eventDataName) { + public EventData FindEvent (String eventDataName) { if (eventDataName == null) throw new ArgumentNullException("eventDataName", "eventDataName cannot be null."); foreach (EventData eventData in events) if (eventData.name == eventDataName) return eventData; @@ -157,7 +136,7 @@ public EventData FindEvent (string eventDataName) { // --- Animations. /// May be null. - public Animation FindAnimation (string animationName) { + public Animation FindAnimation (String animationName) { if (animationName == null) throw new ArgumentNullException("animationName", "animationName cannot be null."); ExposedList animations = this.animations; for (int i = 0, n = animations.Count; i < n; i++) { @@ -170,7 +149,7 @@ public Animation FindAnimation (string animationName) { // --- IK constraints. /// May be null. - public IkConstraintData FindIkConstraint (string constraintName) { + public IkConstraintData FindIkConstraint (String constraintName) { if (constraintName == null) throw new ArgumentNullException("constraintName", "constraintName cannot be null."); ExposedList ikConstraints = this.ikConstraints; for (int i = 0, n = ikConstraints.Count; i < n; i++) { @@ -183,7 +162,7 @@ public IkConstraintData FindIkConstraint (string constraintName) { // --- Transform constraints. /// May be null. - public TransformConstraintData FindTransformConstraint (string constraintName) { + public TransformConstraintData FindTransformConstraint (String constraintName) { if (constraintName == null) throw new ArgumentNullException("constraintName", "constraintName cannot be null."); ExposedList transformConstraints = this.transformConstraints; for (int i = 0, n = transformConstraints.Count; i < n; i++) { @@ -196,7 +175,7 @@ public TransformConstraintData FindTransformConstraint (string constraintName) { // --- Path constraints. /// May be null. - public PathConstraintData FindPathConstraint (string constraintName) { + public PathConstraintData FindPathConstraint (String constraintName) { if (constraintName == null) throw new ArgumentNullException("constraintName", "constraintName cannot be null."); ExposedList pathConstraints = this.pathConstraints; for (int i = 0, n = pathConstraints.Count; i < n; i++) { @@ -207,7 +186,7 @@ public PathConstraintData FindPathConstraint (string constraintName) { } /// -1 if the path constraint was not found. - public int FindPathConstraintIndex (string pathConstraintName) { + public int FindPathConstraintIndex (String pathConstraintName) { if (pathConstraintName == null) throw new ArgumentNullException("pathConstraintName", "pathConstraintName cannot be null."); ExposedList pathConstraints = this.pathConstraints; for (int i = 0, n = pathConstraints.Count; i < n; i++) @@ -217,7 +196,7 @@ public int FindPathConstraintIndex (string pathConstraintName) { // --- - override public string ToString () { + override public String ToString () { return name ?? base.ToString(); } } diff --git a/SpineRuntimes/SpineRuntime35/SkeletonJson.cs b/SpineRuntimes/SpineRuntime35/SkeletonJson.cs index 7ef00ad9..bee50cb6 100644 --- a/SpineRuntimes/SpineRuntime35/SkeletonJson.cs +++ b/SpineRuntimes/SpineRuntime35/SkeletonJson.cs @@ -146,20 +146,12 @@ public SkeletonData ReadSkeletonData (TextReader reader) { data.b = ToColor(color, 2); data.a = ToColor(color, 3); } - - if (slotMap.ContainsKey("dark")) { - var color2 = (String)slotMap["dark"]; - data.r2 = ToColor(color2, 0, 6); // expectedLength = 6. ie. "RRGGBB" - data.g2 = ToColor(color2, 1, 6); - data.b2 = ToColor(color2, 2, 6); - data.hasSecondColor = true; - } data.attachmentName = GetString(slotMap, "attachment", null); if (slotMap.ContainsKey("blend")) - data.blendMode = (BlendMode)Enum.Parse(typeof(BlendMode), (String)slotMap["blend"], true); + data.blendMode = (BlendMode)Enum.Parse(typeof(BlendMode), (String)slotMap["blend"], false); else - data.blendMode = BlendMode.Normal; + data.blendMode = BlendMode.normal; skeletonData.slots.Add(data); } } @@ -203,9 +195,6 @@ public SkeletonData ReadSkeletonData (TextReader reader) { data.target = skeletonData.FindBone(targetName); if (data.target == null) throw new Exception("Target bone not found: " + targetName); - data.local = GetBoolean(constraintMap, "local", false); - data.relative = GetBoolean(constraintMap, "relative", false); - data.offsetRotation = GetFloat(constraintMap, "rotation", 0); data.offsetX = GetFloat(constraintMap, "x", 0) * scale; data.offsetY = GetFloat(constraintMap, "y", 0) * scale; @@ -261,7 +250,7 @@ public SkeletonData ReadSkeletonData (TextReader reader) { int slotIndex = skeletonData.FindSlotIndex(slotEntry.Key); foreach (KeyValuePair entry in ((Dictionary)slotEntry.Value)) { try { - Attachment attachment = ReadAttachment((Dictionary)entry.Value, skin, slotIndex, entry.Key, skeletonData); + Attachment attachment = ReadAttachment((Dictionary)entry.Value, skin, slotIndex, entry.Key); if (attachment != null) skin.AddAttachment(slotIndex, entry.Key, attachment); } catch (Exception e) { throw new Exception("Error reading attachment: " + entry.Key + ", skin: " + skin, e); @@ -317,7 +306,7 @@ public SkeletonData ReadSkeletonData (TextReader reader) { return skeletonData; } - private Attachment ReadAttachment (Dictionary map, Skin skin, int slotIndex, String name, SkeletonData skeletonData) { + private Attachment ReadAttachment (Dictionary map, Skin skin, int slotIndex, String name) { var scale = this.Scale; name = GetString(map, "name", name); @@ -405,27 +394,6 @@ private Attachment ReadAttachment (Dictionary map, Skin skin, in pathAttachment.lengths = GetFloatArray(map, "lengths", scale); return pathAttachment; } - case AttachmentType.Point: { - PointAttachment point = attachmentLoader.NewPointAttachment(skin, name); - if (point == null) return null; - point.x = GetFloat(map, "x", 0) * scale; - point.y = GetFloat(map, "y", 0) * scale; - point.rotation = GetFloat(map, "rotation", 0); - - //string color = GetString(map, "color", null); - //if (color != null) point.color = color; - return point; - } - case AttachmentType.Clipping: { - ClippingAttachment clip = attachmentLoader.NewClippingAttachment(skin, name); - if (clip == null) return null; - - SlotData slot = skeletonData.FindSlot(GetString(map, "end", null)); - if (slot == null) throw new Exception("Clipping end slot not found: " + GetString(map, "end", null)); - clip.endSlot = slot; - ReadVertices(map, clip, GetInt(map, "vertexCount", 0) << 1); - return clip; - } } return null; } @@ -473,26 +441,14 @@ private void ReadAnimation (Dictionary map, String name, Skeleto foreach (KeyValuePair timelineEntry in timelineMap) { var values = (List)timelineEntry.Value; var timelineName = (String)timelineEntry.Key; - if (timelineName == "attachment") { - var timeline = new AttachmentTimeline(values.Count); - timeline.slotIndex = slotIndex; - - int frameIndex = 0; - foreach (Dictionary valueMap in values) { - float time = (float)valueMap["time"]; - timeline.SetFrame(frameIndex++, time, (String)valueMap["name"]); - } - timelines.Add(timeline); - duration = Math.Max(duration, timeline.frames[timeline.FrameCount - 1]); - - } else if (timelineName == "color") { + if (timelineName == "color") { var timeline = new ColorTimeline(values.Count); timeline.slotIndex = slotIndex; int frameIndex = 0; - foreach (Dictionary valueMap in values) { + foreach (Dictionary valueMap in values) { float time = (float)valueMap["time"]; - string c = (string)valueMap["color"]; + String c = (String)valueMap["color"]; timeline.SetFrame(frameIndex, time, ToColor(c, 0), ToColor(c, 1), ToColor(c, 2), ToColor(c, 3)); ReadCurve(valueMap, timeline, frameIndex); frameIndex++; @@ -500,22 +456,17 @@ private void ReadAnimation (Dictionary map, String name, Skeleto timelines.Add(timeline); duration = Math.Max(duration, timeline.frames[(timeline.FrameCount - 1) * ColorTimeline.ENTRIES]); - } else if (timelineName == "twoColor") { - var timeline = new TwoColorTimeline(values.Count); + } else if (timelineName == "attachment") { + var timeline = new AttachmentTimeline(values.Count); timeline.slotIndex = slotIndex; int frameIndex = 0; - foreach (Dictionary valueMap in values) { + foreach (Dictionary valueMap in values) { float time = (float)valueMap["time"]; - string light = (string)valueMap["light"]; - string dark = (string)valueMap["dark"]; - timeline.SetFrame(frameIndex, time, ToColor(light, 0), ToColor(light, 1), ToColor(light, 2), ToColor(light, 3), - ToColor(dark, 0, 6), ToColor(dark, 1, 6), ToColor(dark, 2, 6)); - ReadCurve(valueMap, timeline, frameIndex); - frameIndex++; + timeline.SetFrame(frameIndex++, time, (String)valueMap["name"]); } timelines.Add(timeline); - duration = Math.Max(duration, timeline.frames[(timeline.FrameCount - 1) * TwoColorTimeline.ENTRIES]); + duration = Math.Max(duration, timeline.frames[timeline.FrameCount - 1]); } else throw new Exception("Invalid timeline type for a slot: " + timelineName + " (" + slotName + ")"); @@ -850,9 +801,9 @@ static String GetString(Dictionary map, String name, String defa return (String)map[name]; } - static float ToColor(String hexString, int colorIndex, int expectedLength = 8) { - if (hexString.Length != expectedLength) - throw new ArgumentException("Color hexidecimal length must be " + expectedLength + ", recieved: " + hexString, "hexString"); + static float ToColor(String hexString, int colorIndex) { + if (hexString.Length != 8) + throw new ArgumentException("Color hexidecimal length must be 8, received: " + hexString, "hexString"); return Convert.ToInt32(hexString.Substring(colorIndex * 2, 2), 16) / (float)255; } } diff --git a/SpineRuntimes/SpineRuntime35/Skin.cs b/SpineRuntimes/SpineRuntime35/Skin.cs index ed313446..f412dfc4 100644 --- a/SpineRuntimes/SpineRuntime35/Skin.cs +++ b/SpineRuntimes/SpineRuntime35/Skin.cs @@ -37,7 +37,7 @@ namespace SpineRuntime35 { /// Runtime skins in the Spine Runtimes Guide. /// public class Skin { - internal string name; + internal String name; private Dictionary attachments = new Dictionary(AttachmentKeyTupleComparer.Instance); @@ -49,13 +49,11 @@ public Skin (string name) { this.name = name; } - /// Adds an attachment to the skin for the specified slot index and name. If the name already exists for the slot, the previous value is replaced. public void AddAttachment (int slotIndex, string name, Attachment attachment) { if (attachment == null) throw new ArgumentNullException("attachment", "attachment cannot be null."); attachments[new AttachmentKeyTuple(slotIndex, name)] = attachment; } - /// Returns the attachment for the specified slot index and name, or null. /// May be null. public Attachment GetAttachment (int slotIndex, string name) { Attachment attachment; @@ -81,7 +79,7 @@ public void FindAttachmentsForSlot (int slotIndex, List attachments) if (entry.Key.slotIndex == slotIndex) attachments.Add(entry.Value); } - override public string ToString () { + override public String ToString () { return name; } diff --git a/SpineRuntimes/SpineRuntime35/Slot.cs b/SpineRuntimes/SpineRuntime35/Slot.cs index 44516d71..0d036022 100644 --- a/SpineRuntimes/SpineRuntime35/Slot.cs +++ b/SpineRuntimes/SpineRuntime35/Slot.cs @@ -35,8 +35,6 @@ public class Slot { internal SlotData data; internal Bone bone; internal float r, g, b, a; - internal float r2, g2, b2; - internal bool hasSecondColor = false; internal Attachment attachment; internal float attachmentTime; internal ExposedList attachmentVertices = new ExposedList(); @@ -49,11 +47,6 @@ public class Slot { public float B { get { return b; } set { b = value; } } public float A { get { return a; } set { a = value; } } - public float R2 { get { return r2; } set { r2 = value; } } - public float G2 { get { return g2; } set { g2 = value; } } - public float B2 { get { return b2; } set { b2 = value; } } - public bool HasSecondColor { get { return data.hasSecondColor; } set { data.hasSecondColor = value; } } - /// May be null. public Attachment Attachment { get { return attachment; } @@ -93,7 +86,7 @@ public void SetToSetupPose () { } } - override public string ToString () { + override public String ToString () { return data.name; } } diff --git a/SpineRuntimes/SpineRuntime35/SlotData.cs b/SpineRuntimes/SpineRuntime35/SlotData.cs index 4a3054b5..7fe19f4a 100644 --- a/SpineRuntimes/SpineRuntime35/SlotData.cs +++ b/SpineRuntimes/SpineRuntime35/SlotData.cs @@ -33,27 +33,19 @@ namespace SpineRuntime35 { public class SlotData { internal int index; - internal string name; + internal String name; internal BoneData boneData; internal float r = 1, g = 1, b = 1, a = 1; - internal float r2 = 0, g2 = 0, b2 = 0; - internal bool hasSecondColor = false; - internal string attachmentName; + internal String attachmentName; internal BlendMode blendMode; public int Index { get { return index; } } - public string Name { get { return name; } } + public String Name { get { return name; } } public BoneData BoneData { get { return boneData; } } public float R { get { return r; } set { r = value; } } public float G { get { return g; } set { g = value; } } public float B { get { return b; } set { b = value; } } public float A { get { return a; } set { a = value; } } - - public float R2 { get { return r2; } set { r2 = value; } } - public float G2 { get { return g2; } set { g2 = value; } } - public float B2 { get { return b2; } set { b2 = value; } } - public bool HasSecondColor { get { return hasSecondColor; } set { hasSecondColor = value; } } - /// May be null. public String AttachmentName { get { return attachmentName; } set { attachmentName = value; } } public BlendMode BlendMode { get { return blendMode; } set { blendMode = value; } } @@ -67,7 +59,7 @@ public SlotData (int index, String name, BoneData boneData) { this.boneData = boneData; } - override public string ToString () { + override public String ToString () { return name; } } diff --git a/SpineRuntimes/SpineRuntime35/TransformConstraint.cs b/SpineRuntimes/SpineRuntime35/TransformConstraint.cs index 0b003ab1..e208ef22 100644 --- a/SpineRuntimes/SpineRuntime35/TransformConstraint.cs +++ b/SpineRuntimes/SpineRuntime35/TransformConstraint.cs @@ -67,28 +67,15 @@ public void Apply () { } public void Update () { - if (data.local) { - if (data.relative) - ApplyRelativeLocal(); - else - ApplyAbsoluteLocal(); - } else { - if (data.relative) - ApplyRelativeWorld(); - else - ApplyAbsoluteWorld(); - } - } - - void ApplyAbsoluteWorld () { float rotateMix = this.rotateMix, translateMix = this.translateMix, scaleMix = this.scaleMix, shearMix = this.shearMix; Bone target = this.target; float ta = target.a, tb = target.b, tc = target.c, td = target.d; - float degRadReflect = ta * td - tb * tc > 0 ? MathUtils.DegRad : -MathUtils.DegRad; + float degRadReflect = (ta * td - tb * tc > 0) ? MathUtils.DegRad : -MathUtils.DegRad; float offsetRotation = data.offsetRotation * degRadReflect, offsetShearY = data.offsetShearY * degRadReflect; var bones = this.bones; + var bonesItems = bones.Items; for (int i = 0, n = bones.Count; i < n; i++) { - Bone bone = bones.Items[i]; + Bone bone = bonesItems[i]; bool modified = false; if (rotateMix != 0) { @@ -107,22 +94,22 @@ void ApplyAbsoluteWorld () { } if (translateMix != 0) { - float tx, ty; //Vector2 temp = this.temp; - target.LocalToWorld(data.offsetX, data.offsetY, out tx, out ty); //target.localToWorld(temp.set(data.offsetX, data.offsetY)); - bone.worldX += (tx - bone.worldX) * translateMix; - bone.worldY += (ty - bone.worldY) * translateMix; + float tempx, tempy; + target.LocalToWorld(data.offsetX, data.offsetY, out tempx, out tempy); + bone.worldX += (tempx - bone.worldX) * translateMix; + bone.worldY += (tempy - bone.worldY) * translateMix; modified = true; } if (scaleMix > 0) { float s = (float)Math.Sqrt(bone.a * bone.a + bone.c * bone.c); - //float ts = (float)Math.sqrt(ta * ta + tc * tc); - if (s > 0.00001f) s = (s + ((float)Math.Sqrt(ta * ta + tc * tc) - s + data.offsetScaleX) * scaleMix) / s; + float ts = (float)Math.Sqrt(ta * ta + tc * tc); + if (s > 0.00001f) s = (s + (ts - s + data.offsetScaleX) * scaleMix) / s; bone.a *= s; bone.c *= s; s = (float)Math.Sqrt(bone.b * bone.b + bone.d * bone.d); - //ts = (float)Math.Sqrt(tb * tb + td * td); - if (s > 0.00001f) s = (s + ((float)Math.Sqrt(tb * tb + td * td) - s + data.offsetScaleY) * scaleMix) / s; + ts = (float)Math.Sqrt(tb * tb + td * td); + if (s > 0.00001f) s = (s + (ts - s + data.offsetScaleY) * scaleMix) / s; bone.b *= s; bone.d *= s; modified = true; @@ -146,138 +133,7 @@ void ApplyAbsoluteWorld () { } } - void ApplyRelativeWorld () { - float rotateMix = this.rotateMix, translateMix = this.translateMix, scaleMix = this.scaleMix, shearMix = this.shearMix; - Bone target = this.target; - float ta = target.a, tb = target.b, tc = target.c, td = target.d; - float degRadReflect = ta * td - tb * tc > 0 ? MathUtils.DegRad : -MathUtils.DegRad; - float offsetRotation = data.offsetRotation * degRadReflect, offsetShearY = data.offsetShearY * degRadReflect; - var bones = this.bones; - for (int i = 0, n = bones.Count; i < n; i++) { - Bone bone = bones.Items[i]; - bool modified = false; - - if (rotateMix != 0) { - float a = bone.a, b = bone.b, c = bone.c, d = bone.d; - float r = MathUtils.Atan2(tc, ta) + offsetRotation; - if (r > MathUtils.PI) - r -= MathUtils.PI2; - else if (r < -MathUtils.PI) r += MathUtils.PI2; - r *= rotateMix; - float cos = MathUtils.Cos(r), sin = MathUtils.Sin(r); - bone.a = cos * a - sin * c; - bone.b = cos * b - sin * d; - bone.c = sin * a + cos * c; - bone.d = sin * b + cos * d; - modified = true; - } - - if (translateMix != 0) { - float tx, ty; //Vector2 temp = this.temp; - target.LocalToWorld(data.offsetX, data.offsetY, out tx, out ty); //target.localToWorld(temp.set(data.offsetX, data.offsetY)); - bone.worldX += tx * translateMix; - bone.worldY += ty * translateMix; - modified = true; - } - - if (scaleMix > 0) { - float s = ((float)Math.Sqrt(ta * ta + tc * tc) - 1 + data.offsetScaleX) * scaleMix + 1; - bone.a *= s; - bone.c *= s; - s = ((float)Math.Sqrt(tb * tb + td * td) - 1 + data.offsetScaleY) * scaleMix + 1; - bone.b *= s; - bone.d *= s; - modified = true; - } - - if (shearMix > 0) { - float r = MathUtils.Atan2(td, tb) - MathUtils.Atan2(tc, ta); - if (r > MathUtils.PI) - r -= MathUtils.PI2; - else if (r < -MathUtils.PI) r += MathUtils.PI2; - float b = bone.b, d = bone.d; - r = MathUtils.Atan2(d, b) + (r - MathUtils.PI / 2 + offsetShearY) * shearMix; - float s = (float)Math.Sqrt(b * b + d * d); - bone.b = MathUtils.Cos(r) * s; - bone.d = MathUtils.Sin(r) * s; - modified = true; - } - - if (modified) bone.appliedValid = false; - } - } - - void ApplyAbsoluteLocal () { - float rotateMix = this.rotateMix, translateMix = this.translateMix, scaleMix = this.scaleMix, shearMix = this.shearMix; - Bone target = this.target; - if (!target.appliedValid) target.UpdateAppliedTransform(); - var bonesItems = this.bones.Items; - for (int i = 0, n = this.bones.Count; i < n; i++) { - Bone bone = bonesItems[i]; - if (!bone.appliedValid) bone.UpdateAppliedTransform(); - - float rotation = bone.arotation; - if (rotateMix != 0) { - float r = target.arotation - rotation + data.offsetRotation; - r -= (16384 - (int)(16384.499999999996 - r / 360)) * 360; - rotation += r * rotateMix; - } - - float x = bone.ax, y = bone.ay; - if (translateMix != 0) { - x += (target.ax - x + data.offsetX) * translateMix; - y += (target.ay - y + data.offsetY) * translateMix; - } - - float scaleX = bone.ascaleX, scaleY = bone.ascaleY; - if (scaleMix > 0) { - if (scaleX > 0.00001f) scaleX = (scaleX + (target.ascaleX - scaleX + data.offsetScaleX) * scaleMix) / scaleX; - if (scaleY > 0.00001f) scaleY = (scaleY + (target.ascaleY - scaleY + data.offsetScaleY) * scaleMix) / scaleY; - } - - float shearY = bone.ashearY; - if (shearMix > 0) { - float r = target.ashearY - shearY + data.offsetShearY; - r -= (16384 - (int)(16384.499999999996 - r / 360)) * 360; - bone.shearY += r * shearMix; - } - - bone.UpdateWorldTransform(x, y, rotation, scaleX, scaleY, bone.ashearX, shearY); - } - } - - void ApplyRelativeLocal () { - float rotateMix = this.rotateMix, translateMix = this.translateMix, scaleMix = this.scaleMix, shearMix = this.shearMix; - Bone target = this.target; - if (!target.appliedValid) target.UpdateAppliedTransform(); - var bonesItems = this.bones.Items; - for (int i = 0, n = this.bones.Count; i < n; i++) { - Bone bone = bonesItems[i]; - if (!bone.appliedValid) bone.UpdateAppliedTransform(); - - float rotation = bone.arotation; - if (rotateMix != 0) rotation += (target.arotation + data.offsetRotation) * rotateMix; - - float x = bone.ax, y = bone.ay; - if (translateMix != 0) { - x += (target.ax + data.offsetX) * translateMix; - y += (target.ay + data.offsetY) * translateMix; - } - - float scaleX = bone.ascaleX, scaleY = bone.ascaleY; - if (scaleMix > 0) { - if (scaleX > 0.00001f) scaleX *= ((target.ascaleX - 1 + data.offsetScaleX) * scaleMix) + 1; - if (scaleY > 0.00001f) scaleY *= ((target.ascaleY - 1 + data.offsetScaleY) * scaleMix) + 1; - } - - float shearY = bone.ashearY; - if (shearMix > 0) shearY += (target.ashearY + data.offsetShearY) * shearMix; - - bone.UpdateWorldTransform(x, y, rotation, scaleX, scaleY, bone.ashearX, shearY); - } - } - - override public string ToString () { + override public String ToString () { return data.name; } } diff --git a/SpineRuntimes/SpineRuntime35/TransformConstraintData.cs b/SpineRuntimes/SpineRuntime35/TransformConstraintData.cs index 41196ae9..d9540375 100644 --- a/SpineRuntimes/SpineRuntime35/TransformConstraintData.cs +++ b/SpineRuntimes/SpineRuntime35/TransformConstraintData.cs @@ -32,15 +32,14 @@ namespace SpineRuntime35 { public class TransformConstraintData { - internal string name; + internal String name; internal int order; internal ExposedList bones = new ExposedList(); internal BoneData target; internal float rotateMix, translateMix, scaleMix, shearMix; internal float offsetRotation, offsetX, offsetY, offsetScaleX, offsetScaleY, offsetShearY; - internal bool relative, local; - public string Name { get { return name; } } + public String Name { get { return name; } } public int Order { get { return order; } set { order = value; } } public ExposedList Bones { get { return bones; } } public BoneData Target { get { return target; } set { target = value; } } @@ -56,15 +55,12 @@ public class TransformConstraintData { public float OffsetScaleY { get { return offsetScaleY; } set { offsetScaleY = value; } } public float OffsetShearY { get { return offsetShearY; } set { offsetShearY = value; } } - public bool Relative { get { return relative; } set { relative = value; } } - public bool Local { get { return local; } set { local = value; } } - - public TransformConstraintData (string name) { + public TransformConstraintData (String name) { if (name == null) throw new ArgumentNullException("name", "name cannot be null."); this.name = name; } - override public string ToString () { + override public String ToString () { return name; } } diff --git a/SpineRuntimes/SpineRuntime35/Triangulator.cs b/SpineRuntimes/SpineRuntime35/Triangulator.cs deleted file mode 100644 index d717dd2a..00000000 --- a/SpineRuntimes/SpineRuntime35/Triangulator.cs +++ /dev/null @@ -1,278 +0,0 @@ -/****************************************************************************** - * Spine Runtimes Software License v2.5 - * - * Copyright (c) 2013-2016, Esoteric Software - * All rights reserved. - * - * You are granted a perpetual, non-exclusive, non-sublicensable, and - * non-transferable license to use, install, execute, and perform the Spine - * Runtimes software and derivative works solely for personal or internal - * use. Without the written permission of Esoteric Software (see Section 2 of - * the Spine Software License Agreement), you may not (a) modify, translate, - * adapt, or develop new applications using the Spine Runtimes or otherwise - * create derivative works or improvements of the Spine Runtimes or (b) remove, - * delete, alter, or obscure any trademarks or any copyright, trademark, patent, - * or other intellectual property or proprietary rights notices on or in the - * Software, including any copy thereof. Redistributions in binary or source - * form must include this license and terms. - * - * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, 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 THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - *****************************************************************************/ - -using System; - -namespace SpineRuntime35 { - internal class Triangulator { - private readonly ExposedList> convexPolygons = new ExposedList>(); - private readonly ExposedList> convexPolygonsIndices = new ExposedList>(); - - private readonly ExposedList indicesArray = new ExposedList(); - private readonly ExposedList isConcaveArray = new ExposedList(); - private readonly ExposedList triangles = new ExposedList(); - - private readonly Pool> polygonPool = new Pool>(); - private readonly Pool> polygonIndicesPool = new Pool>(); - - public ExposedList Triangulate (ExposedList verticesArray) { - var vertices = verticesArray.Items; - int vertexCount = verticesArray.Count >> 1; - - var indicesArray = this.indicesArray; - indicesArray.Clear(); - int[] indices = indicesArray.Resize(vertexCount).Items; - for (int i = 0; i < vertexCount; i++) - indices[i] = i; - - var isConcaveArray = this.isConcaveArray; - bool[] isConcave = isConcaveArray.Resize(vertexCount).Items; - for (int i = 0, n = vertexCount; i < n; ++i) - isConcave[i] = IsConcave(i, vertexCount, vertices, indices); - - var triangles = this.triangles; - triangles.Clear(); - triangles.EnsureCapacity(Math.Max(0, vertexCount - 2) << 2); - - while (vertexCount > 3) { - // Find ear tip. - int previous = vertexCount - 1, i = 0, next = 1; - - // outer: - while (true) { - if (!isConcave[i]) { - int p1 = indices[previous] << 1, p2 = indices[i] << 1, p3 = indices[next] << 1; - float p1x = vertices[p1], p1y = vertices[p1 + 1]; - float p2x = vertices[p2], p2y = vertices[p2 + 1]; - float p3x = vertices[p3], p3y = vertices[p3 + 1]; - for (int ii = (next + 1) % vertexCount; ii != previous; ii = (ii + 1) % vertexCount) { - if (!isConcave[ii]) continue; - int v = indices[ii] << 1; - float vx = vertices[v], vy = vertices[v + 1]; - if (PositiveArea(p3x, p3y, p1x, p1y, vx, vy)) { - if (PositiveArea(p1x, p1y, p2x, p2y, vx, vy)) { - if (PositiveArea(p2x, p2y, p3x, p3y, vx, vy)) goto outer; // break outer; - } - } - } - break; - } - - if (next == 0) { - do { - if (!isConcave[i]) break; - i--; - } while (i > 0); - break; - } - - previous = i; - i = next; - next = (next + 1) % vertexCount; - } - outer: - - // Cut ear tip. - triangles.Add(indices[(vertexCount + i - 1) % vertexCount]); - triangles.Add(indices[i]); - triangles.Add(indices[(i + 1) % vertexCount]); - indicesArray.RemoveAt(i); - isConcaveArray.RemoveAt(i); - vertexCount--; - - int previousIndex = (vertexCount + i - 1) % vertexCount; - int nextIndex = i == vertexCount ? 0 : i; - isConcave[previousIndex] = IsConcave(previousIndex, vertexCount, vertices, indices); - isConcave[nextIndex] = IsConcave(nextIndex, vertexCount, vertices, indices); - } - - if (vertexCount == 3) { - triangles.Add(indices[2]); - triangles.Add(indices[0]); - triangles.Add(indices[1]); - } - - return triangles; - } - - public ExposedList> Decompose (ExposedList verticesArray, ExposedList triangles) { - var vertices = verticesArray.Items; - var convexPolygons = this.convexPolygons; - for (int i = 0, n = convexPolygons.Count; i < n; i++) { - polygonPool.Free(convexPolygons.Items[i]); - } - convexPolygons.Clear(); - - var convexPolygonsIndices = this.convexPolygonsIndices; - for (int i = 0, n = convexPolygonsIndices.Count; i < n; i++) { - polygonIndicesPool.Free(convexPolygonsIndices.Items[i]); - } - convexPolygonsIndices.Clear(); - - var polygonIndices = polygonIndicesPool.Obtain(); - polygonIndices.Clear(); - - var polygon = polygonPool.Obtain(); - polygon.Clear(); - - // Merge subsequent triangles if they form a triangle fan. - int fanBaseIndex = -1, lastWinding = 0; - int[] trianglesItems = triangles.Items; - for (int i = 0, n = triangles.Count; i < n; i += 3) { - int t1 = trianglesItems[i] << 1, t2 = trianglesItems[i + 1] << 1, t3 = trianglesItems[i + 2] << 1; - float x1 = vertices[t1], y1 = vertices[t1 + 1]; - float x2 = vertices[t2], y2 = vertices[t2 + 1]; - float x3 = vertices[t3], y3 = vertices[t3 + 1]; - - // If the base of the last triangle is the same as this triangle, check if they form a convex polygon (triangle fan). - var merged = false; - if (fanBaseIndex == t1) { - int o = polygon.Count - 4; - float[] p = polygon.Items; - int winding1 = Winding(p[o], p[o + 1], p[o + 2], p[o + 3], x3, y3); - int winding2 = Winding(x3, y3, p[0], p[1], p[2], p[3]); - if (winding1 == lastWinding && winding2 == lastWinding) { - polygon.Add(x3); - polygon.Add(y3); - polygonIndices.Add(t3); - merged = true; - } - } - - // Otherwise make this triangle the new base. - if (!merged) { - if (polygon.Count > 0) { - convexPolygons.Add(polygon); - convexPolygonsIndices.Add(polygonIndices); - } else { - polygonPool.Free(polygon); - polygonIndicesPool.Free(polygonIndices); - } - polygon = polygonPool.Obtain(); - polygon.Clear(); - polygon.Add(x1); - polygon.Add(y1); - polygon.Add(x2); - polygon.Add(y2); - polygon.Add(x3); - polygon.Add(y3); - polygonIndices = polygonIndicesPool.Obtain(); - polygonIndices.Clear(); - polygonIndices.Add(t1); - polygonIndices.Add(t2); - polygonIndices.Add(t3); - lastWinding = Winding(x1, y1, x2, y2, x3, y3); - fanBaseIndex = t1; - } - } - - if (polygon.Count > 0) { - convexPolygons.Add(polygon); - convexPolygonsIndices.Add(polygonIndices); - } - - // Go through the list of polygons and try to merge the remaining triangles with the found triangle fans. - for (int i = 0, n = convexPolygons.Count; i < n; i++) { - polygonIndices = convexPolygonsIndices.Items[i]; - if (polygonIndices.Count == 0) continue; - int firstIndex = polygonIndices.Items[0]; - int lastIndex = polygonIndices.Items[polygonIndices.Count - 1]; - - polygon = convexPolygons.Items[i]; - int o = polygon.Count - 4; - float[] p = polygon.Items; - float prevPrevX = p[o], prevPrevY = p[o + 1]; - float prevX = p[o + 2], prevY = p[o + 3]; - float firstX = p[0], firstY = p[1]; - float secondX = p[2], secondY = p[3]; - int winding = Winding(prevPrevX, prevPrevY, prevX, prevY, firstX, firstY); - - for (int ii = 0; ii < n; ii++) { - if (ii == i) continue; - var otherIndices = convexPolygonsIndices.Items[ii]; - if (otherIndices.Count != 3) continue; - int otherFirstIndex = otherIndices.Items[0]; - int otherSecondIndex = otherIndices.Items[1]; - int otherLastIndex = otherIndices.Items[2]; - - var otherPoly = convexPolygons.Items[ii]; - float x3 = otherPoly.Items[otherPoly.Count - 2], y3 = otherPoly.Items[otherPoly.Count - 1]; - - if (otherFirstIndex != firstIndex || otherSecondIndex != lastIndex) continue; - int winding1 = Winding(prevPrevX, prevPrevY, prevX, prevY, x3, y3); - int winding2 = Winding(x3, y3, firstX, firstY, secondX, secondY); - if (winding1 == winding && winding2 == winding) { - otherPoly.Clear(); - otherIndices.Clear(); - polygon.Add(x3); - polygon.Add(y3); - polygonIndices.Add(otherLastIndex); - prevPrevX = prevX; - prevPrevY = prevY; - prevX = x3; - prevY = y3; - ii = 0; - } - } - } - - // Remove empty polygons that resulted from the merge step above. - for (int i = convexPolygons.Count - 1; i >= 0; i--) { - polygon = convexPolygons.Items[i]; - if (polygon.Count == 0) { - convexPolygons.RemoveAt(i); - polygonPool.Free(polygon); - polygonIndices = convexPolygonsIndices.Items[i]; - convexPolygonsIndices.RemoveAt(i); - polygonIndicesPool.Free(polygonIndices); - } - } - - return convexPolygons; - } - - static private bool IsConcave (int index, int vertexCount, float[] vertices, int[] indices) { - int previous = indices[(vertexCount + index - 1) % vertexCount] << 1; - int current = indices[index] << 1; - int next = indices[(index + 1) % vertexCount] << 1; - return !PositiveArea(vertices[previous], vertices[previous + 1], vertices[current], vertices[current + 1], vertices[next], - vertices[next + 1]); - } - - static private bool PositiveArea (float p1x, float p1y, float p2x, float p2y, float p3x, float p3y) { - return p1x * (p3y - p2y) + p2x * (p1y - p3y) + p3x * (p2y - p1y) >= 0; - } - - static private int Winding (float p1x, float p1y, float p2x, float p2y, float p3x, float p3y) { - float px = p2x - p1x, py = p2y - p1y; - return p3x * py - p3y * px + px * p1y - p1x * py >= 0 ? 1 : -1; - } - } -} From e9a4894517bc165c1c1038a707eccabbcfdf1089 Mon Sep 17 00:00:00 2001 From: ww-rm Date: Thu, 22 Jan 2026 18:21:08 +0800 Subject: [PATCH 2/4] =?UTF-8?q?=E8=A1=A5=E5=85=85scalex=E5=92=8Cscaley?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SpineRuntime35/AnimationState.cs | 2 +- SpineRuntimes/SpineRuntime35/Bone.cs | 61 ++++++------------- SpineRuntimes/SpineRuntime35/Skeleton.cs | 16 +++-- 3 files changed, 31 insertions(+), 48 deletions(-) diff --git a/SpineRuntimes/SpineRuntime35/AnimationState.cs b/SpineRuntimes/SpineRuntime35/AnimationState.cs index 2d3067a1..fcd341f6 100644 --- a/SpineRuntimes/SpineRuntime35/AnimationState.cs +++ b/SpineRuntimes/SpineRuntime35/AnimationState.cs @@ -43,7 +43,7 @@ public class AnimationState { private bool animationsChanged; private float timeScale = 1; - Pool trackEntryPool = new Pool(); + private readonly Pool trackEntryPool = new Pool(); public AnimationStateData Data { get { return data; } } /// A list of tracks that have animations, which may contain nulls. diff --git a/SpineRuntimes/SpineRuntime35/Bone.cs b/SpineRuntimes/SpineRuntime35/Bone.cs index 197ead88..27d1addc 100644 --- a/SpineRuntimes/SpineRuntime35/Bone.cs +++ b/SpineRuntimes/SpineRuntime35/Bone.cs @@ -113,37 +113,19 @@ public void UpdateWorldTransform (float x, float y, float rotation, float scaleX Bone parent = this.parent; if (parent == null) { // Root bone. - float rotationY = rotation + 90 + shearY; - float la = MathUtils.CosDeg(rotation + shearX) * scaleX; - float lb = MathUtils.CosDeg(rotationY) * scaleY; - float lc = MathUtils.SinDeg(rotation + shearX) * scaleX; - float ld = MathUtils.SinDeg(rotationY) * scaleY; - if (skeleton.flipX) { - x = -x; - la = -la; - lb = -lb; - } - if (skeleton.flipY != yDown) { - y = -y; - lc = -lc; - ld = -ld; - } - a = la; - b = lb; - c = lc; - d = ld; - worldX = x + skeleton.x; - worldY = y + skeleton.y; -// worldSignX = Math.Sign(scaleX); -// worldSignY = Math.Sign(scaleY); - return; + float rotationY = rotation + 90 + shearY, sx = skeleton.scaleX, sy = skeleton.scaleY; + a = MathUtils.CosDeg(rotation + shearX) * scaleX * sx; + b = MathUtils.CosDeg(rotationY) * scaleY * sx; + c = MathUtils.SinDeg(rotation + shearX) * scaleX * sy; + d = MathUtils.SinDeg(rotationY) * 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; -// worldSignX = parent.worldSignX * Math.Sign(scaleX); -// worldSignY = parent.worldSignY * Math.Sign(scaleY); switch (data.transformMode) { case TransformMode.Normal: { @@ -193,13 +175,16 @@ public void UpdateWorldTransform (float x, float y, float rotation, float scaleX case TransformMode.NoScale: case TransformMode.NoScaleOrReflection: { float cos = MathUtils.CosDeg(rotation), sin = MathUtils.SinDeg(rotation); - float za = pa * cos + pb * sin; - float zc = pc * cos + pd * sin; + 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 (data.transformMode == TransformMode.NoScale + && (pa * pd - pb * pc < 0) != (skeleton.scaleX < 0 != skeleton.scaleY < 0)) s = -s; + float r = MathUtils.PI / 2 + MathUtils.Atan2(zc, za); float zb = MathUtils.Cos(r) * s; float zd = MathUtils.Sin(r) * s; @@ -211,23 +196,15 @@ public void UpdateWorldTransform (float x, float y, float rotation, float scaleX b = za * lb + zb * ld; c = zc * la + zd * lc; d = zc * lb + zd * ld; - if (data.transformMode != TransformMode.NoScaleOrReflection ? pa * pd - pb * pc < 0 : skeleton.flipX != skeleton.flipY) { - b = -b; - d = -d; - } - return; + break; } } - if (skeleton.flipX) { - a = -a; - b = -b; - } - if (skeleton.flipY != Bone.yDown) { - c = -c; - d = -d; - } - } + a *= skeleton.scaleX; + b *= skeleton.scaleX; + c *= skeleton.scaleY; + d *= skeleton.scaleY; + } public void SetToSetupPose () { BoneData data = this.data; diff --git a/SpineRuntimes/SpineRuntime35/Skeleton.cs b/SpineRuntimes/SpineRuntime35/Skeleton.cs index c7a96a2d..0087ad25 100644 --- a/SpineRuntimes/SpineRuntime35/Skeleton.cs +++ b/SpineRuntimes/SpineRuntime35/Skeleton.cs @@ -45,8 +45,8 @@ public class Skeleton { internal Skin skin; internal float r = 1, g = 1, b = 1, a = 1; internal float time; - internal bool flipX, flipY; - internal float x, y; + internal float scaleX = 1, scaleY = 1; + internal float x, y; public SkeletonData Data { get { return data; } } public ExposedList Bones { get { return bones; } } @@ -64,10 +64,16 @@ public class Skeleton { public float Time { get { return time; } set { time = value; } } public float X { get { return x; } set { x = value; } } public float Y { get { return y; } set { y = value; } } - public bool FlipX { get { return flipX; } set { flipX = value; } } - public bool FlipY { get { return flipY; } set { flipY = value; } } + public float ScaleX { get { return scaleX; } set { scaleX = value; } } + public float ScaleY { get { return scaleY; } set { scaleY = value; } } - public Bone RootBone { + [Obsolete("Use ScaleX instead. FlipX is when ScaleX is negative.")] + public bool FlipX { get { return scaleX < 0; } set { scaleX = value ? -1f : 1f; } } + + [Obsolete("Use ScaleY instead. FlipY is when ScaleY is negative.")] + public bool FlipY { get { return scaleY < 0; } set { scaleY = value ? -1f : 1f; } } + + public Bone RootBone { get { return bones.Count == 0 ? null : bones.Items[0]; } } From cdf132528d0bd4f117339121c9ffa76e8d1ca5fa Mon Sep 17 00:00:00 2001 From: ww-rm Date: Thu, 22 Jan 2026 18:21:48 +0800 Subject: [PATCH 3/4] update to v0.16.15 --- Spine/Spine.csproj | 2 +- SpineViewer/SpineViewer.csproj | 2 +- SpineViewerCLI/SpineViewerCLI.csproj | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Spine/Spine.csproj b/Spine/Spine.csproj index 34cf0d5e..aa310c58 100644 --- a/Spine/Spine.csproj +++ b/Spine/Spine.csproj @@ -11,7 +11,7 @@ $(SolutionDir)out $(BaseOutputPath)\$(Configuration)\$(PlatformTarget) false - 0.16.14 + 0.16.15 diff --git a/SpineViewer/SpineViewer.csproj b/SpineViewer/SpineViewer.csproj index 114cfdcb..769616ec 100644 --- a/SpineViewer/SpineViewer.csproj +++ b/SpineViewer/SpineViewer.csproj @@ -11,7 +11,7 @@ $(SolutionDir)out $(BaseOutputPath)\$(Configuration)\$(PlatformTarget) false - 0.16.14 + 0.16.15 WinExe true diff --git a/SpineViewerCLI/SpineViewerCLI.csproj b/SpineViewerCLI/SpineViewerCLI.csproj index e787d461..0be70e79 100644 --- a/SpineViewerCLI/SpineViewerCLI.csproj +++ b/SpineViewerCLI/SpineViewerCLI.csproj @@ -11,7 +11,7 @@ $(SolutionDir)out $(BaseOutputPath)\$(Configuration)\$(PlatformTarget) false - 0.16.14 + 0.16.15 Exe From 9f8bef43e1e5f5695334f60d9213579c477ab668 Mon Sep 17 00:00:00 2001 From: ww-rm Date: Thu, 22 Jan 2026 18:22:22 +0800 Subject: [PATCH 4/4] update changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f4b0269..ff5b4ff2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # CHANGELOG +## v0.16.15 + +- 修复 3.5 版本文件加载错误 + ## v0.16.14 - 优化 FFmpeg 导出速度