From 4a4098ed45e58f18bc72d8138470ba2876dfb83d Mon Sep 17 00:00:00 2001 From: NathanSweet Date: Tue, 23 Apr 2013 21:54:52 +0200 Subject: [PATCH] Committed spine-c into spine-unity. Screw it, it's easier for Unity people. --- .../Plugins/Spine/spine-csharp/Animation.cs | 415 ++++++++++++++ .../Spine/spine-csharp/AnimationState.cs | 101 ++++ .../Spine/spine-csharp/AnimationStateData.cs | 61 ++ .../Plugins/Spine/spine-csharp/Atlas.cs | 229 ++++++++ .../Attachments/AtlasAttachmentLoader.cs | 49 ++ .../spine-csharp/Attachments/Attachment.cs | 41 ++ .../Attachments/AttachmentLoader.cs | 33 ++ .../Attachments/AttachmentType.cs | 30 + .../Attachments/RegionAttachment.cs | 130 +++++ .../Assets/Plugins/Spine/spine-csharp/Bone.cs | 108 ++++ .../Plugins/Spine/spine-csharp/BoneData.cs | 53 ++ .../Assets/Plugins/Spine/spine-csharp/Json.cs | 542 ++++++++++++++++++ .../spine-csharp/Place spine-csharp here.txt | 0 .../Plugins/Spine/spine-csharp/Skeleton.cs | 196 +++++++ .../Spine/spine-csharp/SkeletonData.cs | 135 +++++ .../Spine/spine-csharp/SkeletonJson.cs | 302 ++++++++++ .../Assets/Plugins/Spine/spine-csharp/Skin.cs | 80 +++ .../Assets/Plugins/Spine/spine-csharp/Slot.cs | 86 +++ .../Plugins/Spine/spine-csharp/SlotData.cs | 54 ++ 19 files changed, 2645 insertions(+) create mode 100644 spine-unity/Assets/Plugins/Spine/spine-csharp/Animation.cs create mode 100644 spine-unity/Assets/Plugins/Spine/spine-csharp/AnimationState.cs create mode 100644 spine-unity/Assets/Plugins/Spine/spine-csharp/AnimationStateData.cs create mode 100644 spine-unity/Assets/Plugins/Spine/spine-csharp/Atlas.cs create mode 100644 spine-unity/Assets/Plugins/Spine/spine-csharp/Attachments/AtlasAttachmentLoader.cs create mode 100644 spine-unity/Assets/Plugins/Spine/spine-csharp/Attachments/Attachment.cs create mode 100644 spine-unity/Assets/Plugins/Spine/spine-csharp/Attachments/AttachmentLoader.cs create mode 100644 spine-unity/Assets/Plugins/Spine/spine-csharp/Attachments/AttachmentType.cs create mode 100644 spine-unity/Assets/Plugins/Spine/spine-csharp/Attachments/RegionAttachment.cs create mode 100644 spine-unity/Assets/Plugins/Spine/spine-csharp/Bone.cs create mode 100644 spine-unity/Assets/Plugins/Spine/spine-csharp/BoneData.cs create mode 100644 spine-unity/Assets/Plugins/Spine/spine-csharp/Json.cs delete mode 100644 spine-unity/Assets/Plugins/Spine/spine-csharp/Place spine-csharp here.txt create mode 100644 spine-unity/Assets/Plugins/Spine/spine-csharp/Skeleton.cs create mode 100644 spine-unity/Assets/Plugins/Spine/spine-csharp/SkeletonData.cs create mode 100644 spine-unity/Assets/Plugins/Spine/spine-csharp/SkeletonJson.cs create mode 100644 spine-unity/Assets/Plugins/Spine/spine-csharp/Skin.cs create mode 100644 spine-unity/Assets/Plugins/Spine/spine-csharp/Slot.cs create mode 100644 spine-unity/Assets/Plugins/Spine/spine-csharp/SlotData.cs diff --git a/spine-unity/Assets/Plugins/Spine/spine-csharp/Animation.cs b/spine-unity/Assets/Plugins/Spine/spine-csharp/Animation.cs new file mode 100644 index 000000000..cb89c732d --- /dev/null +++ b/spine-unity/Assets/Plugins/Spine/spine-csharp/Animation.cs @@ -0,0 +1,415 @@ +/******************************************************************************* + * Copyright (c) 2013, Esoteric Software + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 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; +using System.Collections.Generic; + +namespace Spine { + public class Animation { + public String Name { get; private set; } + public List Timelines { get; set; } + public float Duration { get; set; } + + public Animation (String name, List timelines, float duration) { + if (name == null) throw new ArgumentNullException("name cannot be null."); + if (timelines == null) throw new ArgumentNullException("timelines cannot be null."); + Name = name; + Timelines = timelines; + Duration = duration; + } + + /** Poses the skeleton at the specified time for this animation. */ + public void Apply (Skeleton skeleton, float time, bool loop) { + if (skeleton == null) throw new ArgumentNullException("skeleton cannot be null."); + + if (loop && Duration != 0) time %= Duration; + + List timelines = Timelines; + for (int i = 0, n = timelines.Count; i < n; i++) + timelines[i].Apply(skeleton, time, 1); + } + + /** Poses the skeleton at the specified time for this animation mixed with the current pose. + * @param alpha The amount of this animation that affects the current pose. */ + public void Mix (Skeleton skeleton, float time, bool loop, float alpha) { + if (skeleton == null) throw new ArgumentNullException("skeleton cannot be null."); + + if (loop && Duration != 0) time %= Duration; + + List timelines = Timelines; + for (int i = 0, n = timelines.Count; i < n; i++) + timelines[i].Apply(skeleton, time, alpha); + } + + /** @param target After the first and before the last entry. */ + internal static int binarySearch (float[] values, float target, int step) { + int low = 0; + int high = values.Length / step - 2; + if (high == 0) return step; + int current = (int)((uint)high >> 1); + while (true) { + if (values[(current + 1) * step] <= target) + low = current + 1; + else + high = current; + if (low == high) return (low + 1) * step; + current = (int)((uint)(low + high) >> 1); + } + } + + internal static int linearSearch (float[] values, float target, int step) { + for (int i = 0, last = values.Length - step; i <= last; i += step) + if (values[i] > target) return i; + return -1; + } + } + + public interface Timeline { + /** Sets the value(s) for the specified time. */ + void Apply (Skeleton skeleton, float time, float alpha); + } + + /** Base class for frames that use an interpolation bezier curve. */ + abstract public class CurveTimeline : Timeline { + static protected float LINEAR = 0; + static protected float STEPPED = -1; + static protected int BEZIER_SEGMENTS = 10; + + private float[] curves; // dfx, dfy, ddfx, ddfy, dddfx, dddfy, ... + public int FrameCount { + get { + return curves.Length / 6 + 1; + } + } + + public CurveTimeline (int frameCount) { + curves = new float[(frameCount - 1) * 6]; + } + + abstract public void Apply (Skeleton skeleton, float time, float alpha); + + public void SetLinear (int frameIndex) { + curves[frameIndex * 6] = LINEAR; + } + + public void SetStepped (int frameIndex) { + curves[frameIndex * 6] = STEPPED; + } + + /** Sets the control handle positions for an interpolation bezier curve used to transition from this keyframe to the next. + * cx1 and cx2 are from 0 to 1, representing the percent of time between the two keyframes. cy1 and cy2 are the percent of + * the difference between the keyframe's values. */ + public void SetCurve (int frameIndex, float cx1, float cy1, float cx2, float cy2) { + float subdiv_step = 1f / BEZIER_SEGMENTS; + float subdiv_step2 = subdiv_step * subdiv_step; + float subdiv_step3 = subdiv_step2 * subdiv_step; + float pre1 = 3 * subdiv_step; + float pre2 = 3 * subdiv_step2; + float pre4 = 6 * subdiv_step2; + float pre5 = 6 * subdiv_step3; + float tmp1x = -cx1 * 2 + cx2; + float tmp1y = -cy1 * 2 + cy2; + float tmp2x = (cx1 - cx2) * 3 + 1; + float tmp2y = (cy1 - cy2) * 3 + 1; + int i = frameIndex * 6; + float[] curves = this.curves; + curves[i] = cx1 * pre1 + tmp1x * pre2 + tmp2x * subdiv_step3; + curves[i + 1] = cy1 * pre1 + tmp1y * pre2 + tmp2y * subdiv_step3; + curves[i + 2] = tmp1x * pre4 + tmp2x * pre5; + curves[i + 3] = tmp1y * pre4 + tmp2y * pre5; + curves[i + 4] = tmp2x * pre5; + curves[i + 5] = tmp2y * pre5; + } + + public float GetCurvePercent (int frameIndex, float percent) { + int curveIndex = frameIndex * 6; + float[] curves = this.curves; + float dfx = curves[curveIndex]; + if (dfx == LINEAR) return percent; + if (dfx == STEPPED) return 0; + float dfy = curves[curveIndex + 1]; + float ddfx = curves[curveIndex + 2]; + float ddfy = curves[curveIndex + 3]; + float dddfx = curves[curveIndex + 4]; + float dddfy = curves[curveIndex + 5]; + float x = dfx, y = dfy; + int i = BEZIER_SEGMENTS - 2; + while (true) { + if (x >= percent) { + float lastX = x - dfx; + float lastY = y - dfy; + return lastY + (y - lastY) * (percent - lastX) / (x - lastX); + } + if (i == 0) break; + i--; + dfx += ddfx; + dfy += ddfy; + ddfx += dddfx; + ddfy += dddfy; + x += dfx; + y += dfy; + } + return y + (1 - y) * (percent - x) / (1 - x); // Last point is 1,1. + } + } + + public class RotateTimeline : CurveTimeline { + static protected int LAST_FRAME_TIME = -2; + static protected int FRAME_VALUE = 1; + + public int BoneIndex { get; set; } + public float[] Frames { get; private set; } // time, value, ... + + public RotateTimeline (int frameCount) + : base(frameCount) { + Frames = new float[frameCount * 2]; + } + + /** Sets the time and value of the specified keyframe. */ + public void SetFrame (int frameIndex, float time, float angle) { + frameIndex *= 2; + Frames[frameIndex] = time; + Frames[frameIndex + 1] = angle; + } + + override public void Apply (Skeleton skeleton, float time, float alpha) { + float[] frames = Frames; + if (time < frames[0]) return; // Time is before first frame. + + Bone bone = skeleton.Bones[BoneIndex]; + + float amount; + + if (time >= frames[frames.Length - 2]) { // Time is after last frame. + amount = bone.Data.Rotation + frames[frames.Length - 1] - bone.Rotation; + while (amount > 180) + amount -= 360; + while (amount < -180) + amount += 360; + bone.Rotation += amount * alpha; + return; + } + + // Interpolate between the last frame and the current frame. + int frameIndex = Animation.binarySearch(frames, time, 2); + float lastFrameValue = frames[frameIndex - 1]; + float frameTime = frames[frameIndex]; + float percent = 1 - (time - frameTime) / (frames[frameIndex + LAST_FRAME_TIME] - frameTime); + percent = GetCurvePercent(frameIndex / 2 - 1, percent < 0 ? 0 : (percent > 1 ? 1 : percent)); + + amount = frames[frameIndex + FRAME_VALUE] - lastFrameValue; + while (amount > 180) + amount -= 360; + while (amount < -180) + amount += 360; + amount = bone.Data.Rotation + (lastFrameValue + amount * percent) - bone.Rotation; + while (amount > 180) + amount -= 360; + while (amount < -180) + amount += 360; + bone.Rotation += amount * alpha; + } + } + + public class TranslateTimeline : CurveTimeline { + static protected int LAST_FRAME_TIME = -3; + static protected int FRAME_X = 1; + static protected int FRAME_Y = 2; + + public int BoneIndex { get; set; } + public float[] Frames { get; private set; } // time, value, value, ... + + public TranslateTimeline (int frameCount) + : base(frameCount) { + Frames = new float[frameCount * 3]; + } + + /** Sets the time and value of the specified keyframe. */ + public void SetFrame (int frameIndex, float time, float x, float y) { + frameIndex *= 3; + Frames[frameIndex] = time; + Frames[frameIndex + 1] = x; + Frames[frameIndex + 2] = y; + } + + override public void Apply (Skeleton skeleton, float time, float alpha) { + float[] frames = Frames; + if (time < frames[0]) return; // Time is before first frame. + + Bone bone = skeleton.Bones[BoneIndex]; + + if (time >= frames[frames.Length - 3]) { // Time is after last frame. + bone.X += (bone.Data.X + frames[frames.Length - 2] - bone.X) * alpha; + bone.Y += (bone.Data.Y + frames[frames.Length - 1] - bone.Y) * alpha; + return; + } + + // Interpolate between the last frame and the current frame. + int frameIndex = Animation.binarySearch(frames, time, 3); + float lastFrameX = frames[frameIndex - 2]; + float lastFrameY = frames[frameIndex - 1]; + float frameTime = frames[frameIndex]; + float percent = 1 - (time - frameTime) / (frames[frameIndex + LAST_FRAME_TIME] - frameTime); + percent = GetCurvePercent(frameIndex / 3 - 1, percent < 0 ? 0 : (percent > 1 ? 1 : percent)); + + bone.X += (bone.Data.X + lastFrameX + (frames[frameIndex + FRAME_X] - lastFrameX) * percent - bone.X) * alpha; + bone.Y += (bone.Data.Y + lastFrameY + (frames[frameIndex + FRAME_Y] - lastFrameY) * percent - bone.Y) * alpha; + } + } + + public class ScaleTimeline : TranslateTimeline { + public ScaleTimeline (int frameCount) + : base(frameCount) { + } + + override public void Apply (Skeleton skeleton, float time, float alpha) { + float[] frames = Frames; + if (time < frames[0]) return; // Time is before first frame. + + Bone bone = skeleton.Bones[BoneIndex]; + if (time >= frames[frames.Length - 3]) { // Time is after last frame. + bone.ScaleX += (bone.Data.ScaleX - 1 + frames[frames.Length - 2] - bone.ScaleX) * alpha; + bone.ScaleY += (bone.Data.ScaleY - 1 + frames[frames.Length - 1] - bone.ScaleY) * alpha; + return; + } + + // Interpolate between the last frame and the current frame. + int frameIndex = Animation.binarySearch(frames, time, 3); + float lastFrameX = frames[frameIndex - 2]; + float lastFrameY = frames[frameIndex - 1]; + float frameTime = frames[frameIndex]; + float percent = 1 - (time - frameTime) / (frames[frameIndex + LAST_FRAME_TIME] - frameTime); + percent = GetCurvePercent(frameIndex / 3 - 1, percent < 0 ? 0 : (percent > 1 ? 1 : percent)); + + bone.ScaleX += (bone.Data.ScaleX - 1 + lastFrameX + (frames[frameIndex + FRAME_X] - lastFrameX) * percent - bone.ScaleX) * alpha; + bone.ScaleY += (bone.Data.ScaleY - 1 + lastFrameY + (frames[frameIndex + FRAME_Y] - lastFrameY) * percent - bone.ScaleY) * alpha; + } + } + + public class ColorTimeline : CurveTimeline { + static protected int LAST_FRAME_TIME = -5; + static protected int FRAME_R = 1; + static protected int FRAME_G = 2; + static protected int FRAME_B = 3; + static protected int FRAME_A = 4; + + public int SlotIndex { get; set; } + public float[] Frames { get; private set; } // time, r, g, b, a, ... + + public ColorTimeline (int frameCount) + : base(frameCount) { + Frames = new float[frameCount * 5]; + } + + /** Sets the time and value of the specified keyframe. */ + public void setFrame (int frameIndex, float time, float r, float g, float b, float a) { + frameIndex *= 5; + Frames[frameIndex] = time; + Frames[frameIndex + 1] = r; + Frames[frameIndex + 2] = g; + Frames[frameIndex + 3] = b; + Frames[frameIndex + 4] = a; + } + + override public void Apply (Skeleton skeleton, float time, float alpha) { + float[] frames = Frames; + if (time < frames[0]) return; // Time is before first frame. + + Slot slot = skeleton.Slots[SlotIndex]; + + if (time >= frames[frames.Length - 5]) { // Time is after last frame. + int i = frames.Length - 1; + slot.R = frames[i - 3]; + slot.G = frames[i - 2]; + slot.B = frames[i - 1]; + slot.A = frames[i]; + return; + } + + // Interpolate between the last frame and the current frame. + int frameIndex = Animation.binarySearch(frames, time, 5); + float lastFrameR = frames[frameIndex - 4]; + float lastFrameG = frames[frameIndex - 3]; + float lastFrameB = frames[frameIndex - 2]; + float lastFrameA = frames[frameIndex - 1]; + float frameTime = frames[frameIndex]; + float percent = 1 - (time - frameTime) / (frames[frameIndex + LAST_FRAME_TIME] - frameTime); + percent = GetCurvePercent(frameIndex / 5 - 1, percent < 0 ? 0 : (percent > 1 ? 1 : percent)); + + float r = lastFrameR + (frames[frameIndex + FRAME_R] - lastFrameR) * percent; + float g = lastFrameG + (frames[frameIndex + FRAME_G] - lastFrameG) * percent; + float b = lastFrameB + (frames[frameIndex + FRAME_B] - lastFrameB) * percent; + float a = lastFrameA + (frames[frameIndex + FRAME_A] - lastFrameA) * percent; + if (alpha < 1) { + slot.R += (r - slot.R) * alpha; + slot.G += (g - slot.G) * alpha; + slot.B += (b - slot.B) * alpha; + slot.A += (a - slot.A) * alpha; + } else { + slot.R = r; + slot.G = g; + slot.B = b; + slot.A = a; + } + } + } + + public class AttachmentTimeline : Timeline { + public int SlotIndex { get; set; } + public float[] Frames { get; private set; } // time, ... + public String[] AttachmentNames { get; private set; } + public int FrameCount { + get { + return Frames.Length; + } + } + + public AttachmentTimeline (int frameCount) { + Frames = new float[frameCount]; + AttachmentNames = new String[frameCount]; + } + + /** Sets the time and value of the specified keyframe. */ + public void setFrame (int frameIndex, float time, String attachmentName) { + Frames[frameIndex] = time; + AttachmentNames[frameIndex] = attachmentName; + } + + public void Apply (Skeleton skeleton, float time, float alpha) { + float[] frames = Frames; + if (time < frames[0]) return; // Time is before first frame. + + int frameIndex; + if (time >= frames[frames.Length - 1]) // Time is after last frame. + frameIndex = frames.Length - 1; + else + frameIndex = Animation.binarySearch(frames, time, 1) - 1; + + String attachmentName = AttachmentNames[frameIndex]; + skeleton.Slots[SlotIndex].Attachment = + attachmentName == null ? null : skeleton.GetAttachment(SlotIndex, attachmentName); + } + } +} diff --git a/spine-unity/Assets/Plugins/Spine/spine-csharp/AnimationState.cs b/spine-unity/Assets/Plugins/Spine/spine-csharp/AnimationState.cs new file mode 100644 index 000000000..8b6ef6b8e --- /dev/null +++ b/spine-unity/Assets/Plugins/Spine/spine-csharp/AnimationState.cs @@ -0,0 +1,101 @@ +/******************************************************************************* + * Copyright (c) 2013, Esoteric Software + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 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; +using System.Collections.Generic; + +namespace Spine { + public class AnimationState { + public AnimationStateData Data { get; private set; } + public Animation Animation { get; private set; } + public float Time { get; set; } + public bool Loop { get; set; } + private Animation previous; + float previousTime; + bool previousLoop; + float mixTime, mixDuration; + + public AnimationState (AnimationStateData data) { + if (data == null) throw new ArgumentNullException("data cannot be null."); + Data = data; + } + + public void Update (float delta) { + Time += delta; + previousTime += delta; + mixTime += delta; + } + + public void Apply (Skeleton skeleton) { + if (Animation == null) return; + if (previous != null) { + previous.Apply(skeleton, previousTime, previousLoop); + float alpha = mixTime / mixDuration; + if (alpha >= 1) { + alpha = 1; + previous = null; + } + Animation.Mix(skeleton, Time, Loop, alpha); + } else + Animation.Apply(skeleton, Time, Loop); + } + + public void SetAnimation (String animationName, bool loop) { + Animation animation = Data.SkeletonData.FindAnimation(animationName); + if (animation == null) throw new ArgumentException("Animation not found: " + animationName); + SetAnimation(animation, loop); + } + + public void SetAnimation (Animation animation, bool loop) { + previous = null; + if (animation != null && Animation != null) { + mixDuration = Data.GetMix(Animation, animation); + if (mixDuration > 0) { + mixTime = 0; + previous = Animation; + previousTime = Time; + previousLoop = Loop; + } + } + Animation = animation; + Loop = loop; + Time = 0; + } + + public void ClearAnimation () { + previous = null; + Animation = null; + } + + /** Returns true if no animation is set or if the current time is greater than the animation duration, regardless of looping. */ + public bool isComplete () { + return Animation == null || Time >= Animation.Duration; + } + + override public String ToString () { + return (Animation != null && Animation.Name != null) ? Animation.Name : base.ToString(); + } + } +} diff --git a/spine-unity/Assets/Plugins/Spine/spine-csharp/AnimationStateData.cs b/spine-unity/Assets/Plugins/Spine/spine-csharp/AnimationStateData.cs new file mode 100644 index 000000000..571e9fc08 --- /dev/null +++ b/spine-unity/Assets/Plugins/Spine/spine-csharp/AnimationStateData.cs @@ -0,0 +1,61 @@ +/******************************************************************************* + * Copyright (c) 2013, Esoteric Software + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 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; +using System.Collections.Generic; + +namespace Spine { + public class AnimationStateData { + public SkeletonData SkeletonData { get; private set; } + private Dictionary, float> animationToMixTime = new Dictionary, float>(); + + public AnimationStateData (SkeletonData skeletonData) { + SkeletonData = skeletonData; + } + + 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); + if (to == null) throw new ArgumentException("Animation not found: " + toName); + SetMix(from, to, duration); + } + + public void SetMix (Animation from, Animation to, float duration) { + if (from == null) throw new ArgumentNullException("from cannot be null."); + if (to == null) throw new ArgumentNullException("to cannot be null."); + KeyValuePair key = new KeyValuePair(from, to); + animationToMixTime.Remove(key); + animationToMixTime.Add(key, duration); + } + + public float GetMix (Animation from, Animation to) { + KeyValuePair key = new KeyValuePair(from, to); + float duration; + animationToMixTime.TryGetValue(key, out duration); + return duration; + } + } +} diff --git a/spine-unity/Assets/Plugins/Spine/spine-csharp/Atlas.cs b/spine-unity/Assets/Plugins/Spine/spine-csharp/Atlas.cs new file mode 100644 index 000000000..421668136 --- /dev/null +++ b/spine-unity/Assets/Plugins/Spine/spine-csharp/Atlas.cs @@ -0,0 +1,229 @@ +/******************************************************************************* + * Copyright (c) 2013, Esoteric Software + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 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; +using System.Collections.Generic; +using System.IO; + +namespace Spine { + public class Atlas { + List pages = new List(); + List regions = new List(); + TextureLoader textureLoader; + + public Atlas (String path, TextureLoader textureLoader) { + using (StreamReader reader = new StreamReader(path)) { + try { + Load(reader, Path.GetDirectoryName(path), textureLoader); + } catch (Exception ex) { + throw new Exception("Error reading atlas file: " + path, ex); + } + } + } + + public Atlas (TextReader reader, String dir, TextureLoader textureLoader) { + Load(reader, dir, 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]; + AtlasPage page = null; + while (true) { + String line = reader.ReadLine(); + if (line == null) break; + if (line.Trim().Length == 0) + page = null; + else if (page == null) { + page = new AtlasPage(); + page.name = line; + + page.format = (Format)Enum.Parse(typeof(Format), readValue(reader), false); + + readTuple(reader, tuple); + page.minFilter = (TextureFilter)Enum.Parse(typeof(TextureFilter), tuple[0]); + page.magFilter = (TextureFilter)Enum.Parse(typeof(TextureFilter), tuple[1]); + + String direction = readValue(reader); + page.uWrap = TextureWrap.ClampToEdge; + page.vWrap = TextureWrap.ClampToEdge; + if (direction == "x") + page.uWrap = TextureWrap.Repeat; + else if (direction == "y") + page.vWrap = TextureWrap.Repeat; + else if (direction == "xy") + page.uWrap = page.vWrap = TextureWrap.Repeat; + + textureLoader.Load(page, Path.Combine(imagesDir, line)); + + pages.Add(page); + + } else { + AtlasRegion region = new AtlasRegion(); + region.name = line; + region.page = page; + + region.rotate = Boolean.Parse(readValue(reader)); + + readTuple(reader, tuple); + int x = int.Parse(tuple[0]); + int y = int.Parse(tuple[1]); + + readTuple(reader, tuple); + int width = int.Parse(tuple[0]); + int height = int.Parse(tuple[1]); + + region.u = x / (float)page.width; + region.v = y / (float)page.height; + region.u2 = (x + width) / (float)page.width; + region.v2 = (y + height) / (float)page.height; + region.x = x; + region.y = y; + region.width = Math.Abs(width); + region.height = Math.Abs(height); + + if (readTuple(reader, tuple) == 4) { // split is optional + region.splits = new int[] {int.Parse(tuple[0]), int.Parse(tuple[1]), + int.Parse(tuple[2]), int.Parse(tuple[3])}; + + if (readTuple(reader, tuple) == 4) { // pad is optional, but only present with splits + region.pads = new int[] {int.Parse(tuple[0]), int.Parse(tuple[1]), + int.Parse(tuple[2]), int.Parse(tuple[3])}; + + readTuple(reader, tuple); + } + } + + region.originalWidth = int.Parse(tuple[0]); + region.originalHeight = int.Parse(tuple[1]); + + readTuple(reader, tuple); + region.offsetX = int.Parse(tuple[0]); + region.offsetY = int.Parse(tuple[1]); + + region.index = int.Parse(readValue(reader)); + + regions.Add(region); + } + } + } + + 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 (2 or 4). */ + 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; + for (; i < 3; i++) { + int comma = line.IndexOf(',', lastMatch); + if (comma == -1) { + if (i == 0) throw new Exception("Invalid line: " + line); + break; + } + tuple[i] = line.Substring(lastMatch, comma - lastMatch).Trim(); + lastMatch = comma + 1; + } + tuple[i] = line.Substring(lastMatch).Trim(); + return i + 1; + } + + /** 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. + * @return The region, or null. */ + 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; + } + + public void Dispose () { + for (int i = 0, n = pages.Count; i < n; i++) + textureLoader.Unload(pages[i].texture); + } + } + + public enum Format { + Alpha, + Intensity, + LuminanceAlpha, + RGB565, + RGBA4444, + RGB888, + RGBA8888 + } + + public enum TextureFilter { + Nearest, + Linear, + MipMap, + MipMapNearestNearest, + MipMapLinearNearest, + MipMapNearestLinear, + MipMapLinearLinear + } + + public enum TextureWrap { + MirroredRepeat, + ClampToEdge, + Repeat + } + + public class AtlasPage { + public String name; + public Format format; + public TextureFilter minFilter; + public TextureFilter magFilter; + public TextureWrap uWrap; + public TextureWrap vWrap; + public Object texture; + public int width, height; + } + + public class AtlasRegion { + public AtlasPage page; + public String name; + public int x, y, width, height; + public float u, v, u2, v2; + public float offsetX, offsetY; + public int originalWidth, originalHeight; + public int index; + public bool rotate; + public int[] splits; + public int[] pads; + } + + public interface TextureLoader { + void Load (AtlasPage page, String path); + void Unload (Object texture); + } +} diff --git a/spine-unity/Assets/Plugins/Spine/spine-csharp/Attachments/AtlasAttachmentLoader.cs b/spine-unity/Assets/Plugins/Spine/spine-csharp/Attachments/AtlasAttachmentLoader.cs new file mode 100644 index 000000000..28f44658f --- /dev/null +++ b/spine-unity/Assets/Plugins/Spine/spine-csharp/Attachments/AtlasAttachmentLoader.cs @@ -0,0 +1,49 @@ +/******************************************************************************* + * Copyright (c) 2013, Esoteric Software + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 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 Spine { + public class AtlasAttachmentLoader : AttachmentLoader { + private Atlas atlas; + + public AtlasAttachmentLoader (Atlas atlas) { + if (atlas == null) throw new ArgumentNullException("atlas cannot be null."); + this.atlas = atlas; + } + + public Attachment NewAttachment (Skin skin, AttachmentType type, String name) { + switch (type) { + case AttachmentType.region: + AtlasRegion region = atlas.FindRegion(name); + if (region == null) throw new Exception("Region not found in atlas: " + name + " (" + type + ")"); + RegionAttachment attachment = new RegionAttachment(name); + attachment.Region = region; + return attachment; + } + throw new Exception("Unknown attachment type: " + type); + } + } +} diff --git a/spine-unity/Assets/Plugins/Spine/spine-csharp/Attachments/Attachment.cs b/spine-unity/Assets/Plugins/Spine/spine-csharp/Attachments/Attachment.cs new file mode 100644 index 000000000..2d9ea9f2e --- /dev/null +++ b/spine-unity/Assets/Plugins/Spine/spine-csharp/Attachments/Attachment.cs @@ -0,0 +1,41 @@ +/******************************************************************************* + * Copyright (c) 2013, Esoteric Software + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 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 Spine { + abstract public class Attachment { + public String Name { get; private set; } + + public Attachment (String name) { + if (name == null) throw new ArgumentNullException("name cannot be null."); + Name = name; + } + + override public String ToString () { + return Name; + } + } +} diff --git a/spine-unity/Assets/Plugins/Spine/spine-csharp/Attachments/AttachmentLoader.cs b/spine-unity/Assets/Plugins/Spine/spine-csharp/Attachments/AttachmentLoader.cs new file mode 100644 index 000000000..d926dfc80 --- /dev/null +++ b/spine-unity/Assets/Plugins/Spine/spine-csharp/Attachments/AttachmentLoader.cs @@ -0,0 +1,33 @@ +/******************************************************************************* + * Copyright (c) 2013, Esoteric Software + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 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 Spine { + public interface AttachmentLoader { + /** @return May be null to not load any attachment. */ + Attachment NewAttachment (Skin skin, AttachmentType type, String name); + } +} diff --git a/spine-unity/Assets/Plugins/Spine/spine-csharp/Attachments/AttachmentType.cs b/spine-unity/Assets/Plugins/Spine/spine-csharp/Attachments/AttachmentType.cs new file mode 100644 index 000000000..1d768a9aa --- /dev/null +++ b/spine-unity/Assets/Plugins/Spine/spine-csharp/Attachments/AttachmentType.cs @@ -0,0 +1,30 @@ +/******************************************************************************* + * Copyright (c) 2013, Esoteric Software + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 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 Spine { + public enum AttachmentType { + region, regionSequence + } +} diff --git a/spine-unity/Assets/Plugins/Spine/spine-csharp/Attachments/RegionAttachment.cs b/spine-unity/Assets/Plugins/Spine/spine-csharp/Attachments/RegionAttachment.cs new file mode 100644 index 000000000..11a7cf95e --- /dev/null +++ b/spine-unity/Assets/Plugins/Spine/spine-csharp/Attachments/RegionAttachment.cs @@ -0,0 +1,130 @@ +/******************************************************************************* + * Copyright (c) 2013, Esoteric Software + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 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 Spine { + /** Attachment that displays a texture region. */ + public class RegionAttachment : Attachment { + 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; + + public float X { get; set; } + public float Y { get; set; } + public float ScaleX { get; set; } + public float ScaleY { get; set; } + public float Rotation { get; set; } + public float Width { get; set; } + public float Height { get; set; } + + public float[] Offset { get; private set; } + public float[] Vertices { get; private set; } + public AtlasRegion Region { get; set; } + + public RegionAttachment (string name) + : base(name) { + Offset = new float[8]; + Vertices = new float[8]; + ScaleX = 1; + ScaleY = 1; + } + + public void UpdateOffset () { + float width = Width; + float height = Height; + float localX2 = width / 2; + float localY2 = height / 2; + float localX = -localX2; + float localY = -localY2; + AtlasRegion region = Region; + if (region != null) { + if (region.rotate) { + localX += region.offsetX / region.originalWidth * height; + localY += region.offsetY / region.originalHeight * width; + localX2 -= (region.originalWidth - region.offsetX - region.height) / region.originalWidth * width; + localY2 -= (region.originalHeight - region.offsetY - region.width) / region.originalHeight * height; + } else { + localX += region.offsetX / region.originalWidth * width; + localY += region.offsetY / region.originalHeight * height; + localX2 -= (region.originalWidth - region.offsetX - region.width) / region.originalWidth * width; + localY2 -= (region.originalHeight - region.offsetY - region.height) / region.originalHeight * height; + } + } + float scaleX = ScaleX; + float scaleY = ScaleY; + localX *= scaleX; + localY *= scaleY; + localX2 *= scaleX; + localY2 *= scaleY; + float radians = Rotation * (float)Math.PI / 180; + float cos = (float)Math.Cos(radians); + float sin = (float)Math.Sin(radians); + float x = X; + float y = Y; + float localXCos = localX * cos + x; + float localXSin = localX * sin; + float localYCos = localY * cos + y; + float localYSin = localY * sin; + float localX2Cos = localX2 * cos + x; + float localX2Sin = localX2 * sin; + float localY2Cos = localY2 * cos + y; + float localY2Sin = localY2 * sin; + float[] offset = Offset; + 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 UpdateVertices (Bone bone) { + float x = bone.WorldX; + float y = bone.WorldY; + float m00 = bone.M00; + float m01 = bone.M01; + float m10 = bone.M10; + float m11 = bone.M11; + float[] vertices = Vertices; + float[] offset = Offset; + vertices[X1] = offset[X1] * m00 + offset[Y1] * m01 + x; + vertices[Y1] = offset[X1] * m10 + offset[Y1] * m11 + y; + vertices[X2] = offset[X2] * m00 + offset[Y2] * m01 + x; + vertices[Y2] = offset[X2] * m10 + offset[Y2] * m11 + y; + vertices[X3] = offset[X3] * m00 + offset[Y3] * m01 + x; + vertices[Y3] = offset[X3] * m10 + offset[Y3] * m11 + y; + vertices[X4] = offset[X4] * m00 + offset[Y4] * m01 + x; + vertices[Y4] = offset[X4] * m10 + offset[Y4] * m11 + y; + } + } +} diff --git a/spine-unity/Assets/Plugins/Spine/spine-csharp/Bone.cs b/spine-unity/Assets/Plugins/Spine/spine-csharp/Bone.cs new file mode 100644 index 000000000..98ef3342b --- /dev/null +++ b/spine-unity/Assets/Plugins/Spine/spine-csharp/Bone.cs @@ -0,0 +1,108 @@ +/******************************************************************************* + * Copyright (c) 2013, Esoteric Software + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 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 Spine { + public class Bone { + static public bool yDown; + + public BoneData Data { get; private set; } + public Bone Parent { get; private set; } + public float X { get; set; } + public float Y { get; set; } + public float Rotation { get; set; } + public float ScaleX { get; set; } + public float ScaleY { get; set; } + + public float M00 { get; private set; } + public float M01 { get; private set; } + public float M10 { get; private set; } + public float M11 { get; private set; } + public float WorldX { get; private set; } + public float WorldY { get; private set; } + public float WorldRotation { get; private set; } + public float WorldScaleX { get; private set; } + public float WorldScaleY { get; private set; } + + /** @param parent May be null. */ + public Bone (BoneData data, Bone parent) { + if (data == null) throw new ArgumentNullException("data cannot be null."); + Data = data; + Parent = parent; + SetToBindPose(); + } + + /** Computes the world SRT using the parent bone and the local SRT. */ + public void UpdateWorldTransform (bool flipX, bool flipY) { + Bone parent = Parent; + if (parent != null) { + WorldX = X * parent.M00 + Y * parent.M01 + parent.WorldX; + WorldY = X * parent.M10 + Y * parent.M11 + parent.WorldY; + WorldScaleX = parent.WorldScaleX * ScaleX; + WorldScaleY = parent.WorldScaleY * ScaleY; + WorldRotation = parent.WorldRotation + Rotation; + } else { + WorldX = X; + WorldY = Y; + WorldScaleX = ScaleX; + WorldScaleY = ScaleY; + WorldRotation = Rotation; + } + float radians = WorldRotation * (float)Math.PI / 180; + float cos = (float)Math.Cos(radians); + float sin = (float)Math.Sin(radians); + M00 = cos * WorldScaleX; + M10 = sin * WorldScaleX; + M01 = -sin * WorldScaleY; + M11 = cos * WorldScaleY; + if (flipX) { + M00 = -M00; + M01 = -M01; + } + if (flipY) { + M10 = -M10; + M11 = -M11; + } + if (yDown) { + M10 = -M10; + M11 = -M11; + } + } + + public void SetToBindPose () { + BoneData data = Data; + X = data.X; + Y = data.Y; + Rotation = data.Rotation; + ScaleX = data.ScaleX; + ScaleY = data.ScaleY; + } + + override public String ToString () { + return Data.Name; + } + } +} diff --git a/spine-unity/Assets/Plugins/Spine/spine-csharp/BoneData.cs b/spine-unity/Assets/Plugins/Spine/spine-csharp/BoneData.cs new file mode 100644 index 000000000..784f5087c --- /dev/null +++ b/spine-unity/Assets/Plugins/Spine/spine-csharp/BoneData.cs @@ -0,0 +1,53 @@ +/******************************************************************************* + * Copyright (c) 2013, Esoteric Software + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 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 Spine { + public class BoneData { + /** May be null. */ + public BoneData Parent { get; private set; } + public String Name { get; private set; } + public float Length { get; set; } + public float X { get; set; } + public float Y { get; set; } + public float Rotation { get; set; } + public float ScaleX { get; set; } + public float ScaleY { get; set; } + + /** @param parent May be null. */ + public BoneData (String name, BoneData parent) { + if (name == null) throw new ArgumentNullException("name cannot be null."); + Name = name; + Parent = parent; + ScaleX = 1; + ScaleY = 1; + } + + override public String ToString () { + return Name; + } + } +} diff --git a/spine-unity/Assets/Plugins/Spine/spine-csharp/Json.cs b/spine-unity/Assets/Plugins/Spine/spine-csharp/Json.cs new file mode 100644 index 000000000..376dd7ef7 --- /dev/null +++ b/spine-unity/Assets/Plugins/Spine/spine-csharp/Json.cs @@ -0,0 +1,542 @@ +/* + * Copyright (c) 2012 Calvin Rien + * + * Based on the JSON parser by Patrick van Bergen + * http://techblog.procurios.nl/k/618/news/view/14605/14863/How-do-I-write-my-own-parser-for-JSON.html + * + * Simplified it so that it doesn't throw exceptions + * and can be used in Unity iPhone with maximum code stripping. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Globalization; + +namespace Spine +{ + // Example usage: + // + // using UnityEngine; + // using System.Collections; + // using System.Collections.Generic; + // using MiniJSON; + // + // public class MiniJSONTest : MonoBehaviour { + // void Start () { + // var jsonString = "{ \"array\": [1.44,2,3], " + + // "\"object\": {\"key1\":\"value1\", \"key2\":256}, " + + // "\"string\": \"The quick brown fox \\\"jumps\\\" over the lazy dog \", " + + // "\"unicode\": \"\\u3041 Men\u00fa sesi\u00f3n\", " + + // "\"int\": 65536, " + + // "\"float\": 3.1415926, " + + // "\"bool\": true, " + + // "\"null\": null }"; + // + // var dict = Json.Deserialize(jsonString) as Dictionary; + // + // Debug.Log("deserialized: " + dict.GetType()); + // Debug.Log("dict['array'][0]: " + ((List) dict["array"])[0]); + // Debug.Log("dict['string']: " + (string) dict["string"]); + // Debug.Log("dict['float']: " + (float) dict["float"]); + // Debug.Log("dict['int']: " + (long) dict["int"]); // ints come out as longs + // Debug.Log("dict['unicode']: " + (string) dict["unicode"]); + // + // var str = Json.Serialize(dict); + // + // Debug.Log("serialized: " + str); + // } + // } + + /// + /// This class encodes and decodes JSON strings. + /// Spec. details, see http://www.json.org/ + /// + /// JSON uses Arrays and Objects. These correspond here to the datatypes IList and IDictionary. + /// All numbers are parsed to floats. + /// + public static class Json { + /// + /// Parses the string json into a value + /// + /// A JSON string. + /// An List<object>, a Dictionary<string, object>, a float, an integer,a string, null, true, or false + public static object Deserialize (TextReader json) { + if (json == null) { + return null; + } + return Parser.Parse(json); + } + + sealed class Parser : IDisposable { + const string WHITE_SPACE = " \t\n\r"; + const string WORD_BREAK = " \t\n\r{}[],:\""; + + enum TOKEN { + NONE, + CURLY_OPEN, + CURLY_CLOSE, + SQUARED_OPEN, + SQUARED_CLOSE, + COLON, + COMMA, + STRING, + NUMBER, + TRUE, + FALSE, + NULL + }; + + TextReader json; + + Parser(TextReader reader) { + json = reader; + } + + public static object Parse (TextReader reader) { + using (var instance = new Parser(reader)) { + return instance.ParseValue(); + } + } + + public void Dispose() { + json.Dispose(); + json = null; + } + + Dictionary ParseObject() { + Dictionary table = new Dictionary(); + + // ditch opening brace + json.Read(); + + // { + while (true) { + switch (NextToken) { + case TOKEN.NONE: + return null; + case TOKEN.COMMA: + continue; + case TOKEN.CURLY_CLOSE: + return table; + default: + // name + string name = ParseString(); + if (name == null) { + return null; + } + + // : + if (NextToken != TOKEN.COLON) { + return null; + } + // ditch the colon + json.Read(); + + // value + table[name] = ParseValue(); + break; + } + } + } + + List ParseArray() { + List array = new List(); + + // ditch opening bracket + json.Read(); + + // [ + var parsing = true; + while (parsing) { + TOKEN nextToken = NextToken; + + switch (nextToken) { + case TOKEN.NONE: + return null; + case TOKEN.COMMA: + continue; + case TOKEN.SQUARED_CLOSE: + parsing = false; + break; + default: + object value = ParseByToken(nextToken); + + array.Add(value); + break; + } + } + + return array; + } + + object ParseValue() { + TOKEN nextToken = NextToken; + return ParseByToken(nextToken); + } + + object ParseByToken(TOKEN token) { + switch (token) { + case TOKEN.STRING: + return ParseString(); + case TOKEN.NUMBER: + return ParseNumber(); + case TOKEN.CURLY_OPEN: + return ParseObject(); + case TOKEN.SQUARED_OPEN: + return ParseArray(); + case TOKEN.TRUE: + return true; + case TOKEN.FALSE: + return false; + case TOKEN.NULL: + return null; + default: + return null; + } + } + + string ParseString() { + StringBuilder s = new StringBuilder(); + char c; + + // ditch opening quote + json.Read(); + + bool parsing = true; + while (parsing) { + + if (json.Peek() == -1) { + parsing = false; + break; + } + + c = NextChar; + switch (c) { + case '"': + parsing = false; + break; + case '\\': + if (json.Peek() == -1) { + parsing = false; + break; + } + + c = NextChar; + switch (c) { + case '"': + case '\\': + case '/': + s.Append(c); + break; + case 'b': + s.Append('\b'); + break; + case 'f': + s.Append('\f'); + break; + case 'n': + s.Append('\n'); + break; + case 'r': + s.Append('\r'); + break; + case 't': + s.Append('\t'); + break; + case 'u': + var hex = new StringBuilder(); + + for (int i=0; i< 4; i++) { + hex.Append(NextChar); + } + + s.Append((char) Convert.ToInt32(hex.ToString(), 16)); + break; + } + break; + default: + s.Append(c); + break; + } + } + + return s.ToString(); + } + + object ParseNumber() { + string number = NextWord; + float parsedFloat; + float.TryParse(number, NumberStyles.Float, CultureInfo.InvariantCulture, out parsedFloat); + return parsedFloat; + } + + void EatWhitespace() { + while (WHITE_SPACE.IndexOf(PeekChar) != -1) { + json.Read(); + + if (json.Peek() == -1) { + break; + } + } + } + + char PeekChar { + get { + return Convert.ToChar(json.Peek()); + } + } + + char NextChar { + get { + return Convert.ToChar(json.Read()); + } + } + + string NextWord { + get { + StringBuilder word = new StringBuilder(); + + while (WORD_BREAK.IndexOf(PeekChar) == -1) { + word.Append(NextChar); + + if (json.Peek() == -1) { + break; + } + } + + return word.ToString(); + } + } + + TOKEN NextToken { + get { + EatWhitespace(); + + if (json.Peek() == -1) { + return TOKEN.NONE; + } + + char c = PeekChar; + switch (c) { + case '{': + return TOKEN.CURLY_OPEN; + case '}': + json.Read(); + return TOKEN.CURLY_CLOSE; + case '[': + return TOKEN.SQUARED_OPEN; + case ']': + json.Read(); + return TOKEN.SQUARED_CLOSE; + case ',': + json.Read(); + return TOKEN.COMMA; + case '"': + return TOKEN.STRING; + case ':': + return TOKEN.COLON; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '-': + return TOKEN.NUMBER; + } + + string word = NextWord; + + switch (word) { + case "false": + return TOKEN.FALSE; + case "true": + return TOKEN.TRUE; + case "null": + return TOKEN.NULL; + } + + return TOKEN.NONE; + } + } + } + + /// + /// Converts a IDictionary / IList object or a simple type (string, int, etc.) into a JSON string + /// + /// A Dictionary<string, object> / List<object> + /// A JSON encoded string, or null if object 'json' is not serializable + public static string Serialize(object obj) { + return Serializer.Serialize(obj); + } + + sealed class Serializer { + StringBuilder builder; + + Serializer() { + builder = new StringBuilder(); + } + + public static string Serialize(object obj) { + var instance = new Serializer(); + + instance.SerializeValue(obj); + + return instance.builder.ToString(); + } + + void SerializeValue(object value) { + IList asList; + IDictionary asDict; + string asStr; + + if (value == null) { + builder.Append("null"); + } + else if ((asStr = value as string) != null) { + SerializeString(asStr); + } + else if (value is bool) { + builder.Append(value.ToString().ToLower()); + } + else if ((asList = value as IList) != null) { + SerializeArray(asList); + } + else if ((asDict = value as IDictionary) != null) { + SerializeObject(asDict); + } + else if (value is char) { + SerializeString(value.ToString()); + } + else { + SerializeOther(value); + } + } + + void SerializeObject(IDictionary obj) { + bool first = true; + + builder.Append('{'); + + foreach (object e in obj.Keys) { + if (!first) { + builder.Append(','); + } + + SerializeString(e.ToString()); + builder.Append(':'); + + SerializeValue(obj[e]); + + first = false; + } + + builder.Append('}'); + } + + void SerializeArray(IList anArray) { + builder.Append('['); + + bool first = true; + + foreach (object obj in anArray) { + if (!first) { + builder.Append(','); + } + + SerializeValue(obj); + + first = false; + } + + builder.Append(']'); + } + + void SerializeString(string str) { + builder.Append('\"'); + + char[] charArray = str.ToCharArray(); + foreach (var c in charArray) { + switch (c) { + case '"': + builder.Append("\\\""); + break; + case '\\': + builder.Append("\\\\"); + break; + case '\b': + builder.Append("\\b"); + break; + case '\f': + builder.Append("\\f"); + break; + case '\n': + builder.Append("\\n"); + break; + case '\r': + builder.Append("\\r"); + break; + case '\t': + builder.Append("\\t"); + break; + default: + int codepoint = Convert.ToInt32(c); + if ((codepoint >= 32) && (codepoint <= 126)) { + builder.Append(c); + } + else { + builder.Append("\\u" + Convert.ToString(codepoint, 16).PadLeft(4, '0')); + } + break; + } + } + + builder.Append('\"'); + } + + void SerializeOther(object value) { + if (value is float + || value is int + || value is uint + || value is long + || value is float + || value is sbyte + || value is byte + || value is short + || value is ushort + || value is ulong + || value is decimal) { + builder.Append(value.ToString()); + } + else { + SerializeString(value.ToString()); + } + } + } + } +} \ No newline at end of file diff --git a/spine-unity/Assets/Plugins/Spine/spine-csharp/Place spine-csharp here.txt b/spine-unity/Assets/Plugins/Spine/spine-csharp/Place spine-csharp here.txt deleted file mode 100644 index e69de29bb..000000000 diff --git a/spine-unity/Assets/Plugins/Spine/spine-csharp/Skeleton.cs b/spine-unity/Assets/Plugins/Spine/spine-csharp/Skeleton.cs new file mode 100644 index 000000000..0c033073c --- /dev/null +++ b/spine-unity/Assets/Plugins/Spine/spine-csharp/Skeleton.cs @@ -0,0 +1,196 @@ +/******************************************************************************* + * Copyright (c) 2013, Esoteric Software + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 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; +using System.Collections.Generic; + +namespace Spine { + public class Skeleton { + public SkeletonData Data { get; private set; } + public List Bones { get; private set; } + public List Slots { get; private set; } + public List DrawOrder { get; private set; } + public Skin Skin { get; set; } + public float R { get; set; } + public float G { get; set; } + public float B { get; set; } + public float A { get; set; } + public float Time { get; set; } + public bool FlipX { get; set; } + public bool FlipY { get; set; } + public Bone RootBone { + get { + return Bones.Count == 0 ? null : Bones[0]; + } + } + + public Skeleton (SkeletonData data) { + if (data == null) throw new ArgumentNullException("data cannot be null."); + Data = data; + + Bones = new List(Data.Bones.Count); + foreach (BoneData boneData in Data.Bones) { + Bone parent = boneData.Parent == null ? null : Bones[Data.Bones.IndexOf(boneData.Parent)]; + Bones.Add(new Bone(boneData, parent)); + } + + Slots = new List(Data.Slots.Count); + DrawOrder = new List(Data.Slots.Count); + foreach (SlotData slotData in Data.Slots) { + Bone bone = Bones[Data.Bones.IndexOf(slotData.BoneData)]; + Slot slot = new Slot(slotData, this, bone); + Slots.Add(slot); + DrawOrder.Add(slot); + } + + R = 1; + G = 1; + B = 1; + A = 1; + } + + /** Updates the world transform for each bone. */ + public void UpdateWorldTransform () { + bool flipX = FlipX; + bool flipY = FlipY; + List bones = Bones; + for (int i = 0, n = bones.Count; i < n; i++) + bones[i].UpdateWorldTransform(flipX, flipY); + } + + /** Sets the bones and slots to their bind pose values. */ + public void SetToBindPose () { + SetBonesToBindPose(); + SetSlotsToBindPose(); + } + + public void SetBonesToBindPose () { + List bones = this.Bones; + for (int i = 0, n = bones.Count; i < n; i++) + bones[i].SetToBindPose(); + } + + public void SetSlotsToBindPose () { + List slots = this.Slots; + for (int i = 0, n = slots.Count; i < n; i++) + slots[i].SetToBindPose(i); + } + + /** @return May be null. */ + public Bone FindBone (String boneName) { + if (boneName == null) throw new ArgumentNullException("boneName cannot be null."); + List bones = this.Bones; + for (int i = 0, n = bones.Count; i < n; i++) { + Bone bone = bones[i]; + if (bone.Data.Name == boneName) return bone; + } + return null; + } + + /** @return -1 if the bone was not found. */ + public int FindBoneIndex (String boneName) { + if (boneName == null) throw new ArgumentNullException("boneName cannot be null."); + List bones = this.Bones; + for (int i = 0, n = bones.Count; i < n; i++) + if (bones[i].Data.Name == boneName) return i; + return -1; + } + + /** @return May be null. */ + public Slot FindSlot (String slotName) { + if (slotName == null) throw new ArgumentNullException("slotName cannot be null."); + List slots = this.Slots; + for (int i = 0, n = slots.Count; i < n; i++) { + Slot slot = slots[i]; + if (slot.Data.Name == slotName) return slot; + } + return null; + } + + /** @return -1 if the bone was not found. */ + public int FindSlotIndex (String slotName) { + if (slotName == null) throw new ArgumentNullException("slotName cannot be null."); + List slots = this.Slots; + for (int i = 0, n = slots.Count; i < n; i++) + if (slots[i].Data.Name.Equals(slotName)) return i; + return -1; + } + + /** Sets a skin by name. + * @see #setSkin(Skin) */ + public void SetSkin (String skinName) { + Skin skin = Data.FindSkin(skinName); + if (skin == null) throw new ArgumentException("Skin not found: " + skinName); + SetSkin(skin); + } + + /** Sets the skin used to look up attachments not found in the {@link SkeletonData#getDefaultSkin() default skin}. Attachments + * from the new skin are attached if the corresponding attachment from the old skin was attached. + * @param newSkin May be null. */ + public void SetSkin (Skin newSkin) { + if (Skin != null && newSkin != null) newSkin.AttachAll(this, Skin); + Skin = newSkin; + } + + /** @return May be null. */ + public Attachment GetAttachment (String slotName, String attachmentName) { + return GetAttachment(Data.FindSlotIndex(slotName), attachmentName); + } + + /** @return May be null. */ + public Attachment GetAttachment (int slotIndex, String attachmentName) { + if (attachmentName == null) throw new ArgumentNullException("attachmentName cannot be null."); + if (Skin != null) { + Attachment attachment = Skin.GetAttachment(slotIndex, attachmentName); + if (attachment != null) return attachment; + } + if (Data.DefaultSkin != null) return Data.DefaultSkin.GetAttachment(slotIndex, attachmentName); + return null; + } + + /** @param attachmentName May be null. */ + public void SetAttachment (String slotName, String attachmentName) { + if (slotName == null) throw new ArgumentNullException("slotName cannot be null."); + List slots = Slots; + for (int i = 0, n = slots.Count; i < n; i++) { + Slot slot = slots[i]; + if (slot.Data.Name == slotName) { + Attachment attachment = null; + if (attachmentName != null) { + attachment = GetAttachment(i, attachmentName); + if (attachment == null) throw new ArgumentNullException("Attachment not found: " + attachmentName + ", for slot: " + slotName); + } + slot.Attachment = attachment; + return; + } + } + throw new Exception("Slot not found: " + slotName); + } + + public void Update (float delta) { + Time += delta; + } + } +} diff --git a/spine-unity/Assets/Plugins/Spine/spine-csharp/SkeletonData.cs b/spine-unity/Assets/Plugins/Spine/spine-csharp/SkeletonData.cs new file mode 100644 index 000000000..1b4b3ee8f --- /dev/null +++ b/spine-unity/Assets/Plugins/Spine/spine-csharp/SkeletonData.cs @@ -0,0 +1,135 @@ +/******************************************************************************* + * Copyright (c) 2013, Esoteric Software + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 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; +using System.Collections.Generic; + +namespace Spine { + public class SkeletonData { + public String Name { get; set; } + public List Bones { get; private set; } // Ordered parents first. + public List Slots { get; private set; } // Bind pose draw order. + public List Skins { get; private set; } + /** May be null. */ + public Skin DefaultSkin; + public List Animations { get; private set; } + + public SkeletonData () { + Bones = new List(); + Slots = new List(); + Skins = new List(); + Animations = new List(); + } + + // --- Bones. + + public void AddBone (BoneData bone) { + if (bone == null) throw new ArgumentNullException("bone cannot be null."); + Bones.Add(bone); + } + + + /** @return May be null. */ + public BoneData FindBone (String boneName) { + if (boneName == null) throw new ArgumentNullException("boneName cannot be null."); + for (int i = 0, n = Bones.Count; i < n; i++) { + BoneData bone = Bones[i]; + if (bone.Name == boneName) return bone; + } + return null; + } + + /** @return -1 if the bone was not found. */ + public int FindBoneIndex (String boneName) { + if (boneName == null) throw new ArgumentNullException("boneName cannot be null."); + for (int i = 0, n = Bones.Count; i < n; i++) + if (Bones[i].Name == boneName) return i; + return -1; + } + + // --- Slots. + + public void AddSlot (SlotData slot) { + if (slot == null) throw new ArgumentNullException("slot cannot be null."); + Slots.Add(slot); + } + + /** @return May be null. */ + public SlotData FindSlot (String slotName) { + if (slotName == null) throw new ArgumentNullException("slotName cannot be null."); + for (int i = 0, n = Slots.Count; i < n; i++) { + SlotData slot = Slots[i]; + if (slot.Name == slotName) return slot; + } + return null; + } + + /** @return -1 if the bone was not found. */ + public int FindSlotIndex (String slotName) { + if (slotName == null) throw new ArgumentNullException("slotName cannot be null."); + for (int i = 0, n = Slots.Count; i < n; i++) + if (Slots[i].Name == slotName) return i; + return -1; + } + + // --- Skins. + + public void AddSkin (Skin skin) { + if (skin == null) throw new ArgumentNullException("skin cannot be null."); + Skins.Add(skin); + } + + /** @return May be null. */ + public Skin FindSkin (String skinName) { + if (skinName == null) throw new ArgumentNullException("skinName cannot be null."); + foreach (Skin skin in Skins) + if (skin.Name == skinName) return skin; + return null; + } + + // --- Animations. + + public void AddAnimation (Animation animation) { + if (animation == null) throw new ArgumentNullException("animation cannot be null."); + Animations.Add(animation); + } + + /** @return May be null. */ + public Animation FindAnimation (String animationName) { + if (animationName == null) throw new ArgumentNullException("animationName cannot be null."); + for (int i = 0, n = Animations.Count; i < n; i++) { + Animation animation = Animations[i]; + if (animation.Name == animationName) return animation; + } + return null; + } + + // --- + + override public String ToString () { + return Name != null ? Name : base.ToString(); + } + } +} diff --git a/spine-unity/Assets/Plugins/Spine/spine-csharp/SkeletonJson.cs b/spine-unity/Assets/Plugins/Spine/spine-csharp/SkeletonJson.cs new file mode 100644 index 000000000..af7b31594 --- /dev/null +++ b/spine-unity/Assets/Plugins/Spine/spine-csharp/SkeletonJson.cs @@ -0,0 +1,302 @@ +/******************************************************************************* + * Copyright (c) 2013, Esoteric Software + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 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; +using System.IO; +using System.Collections.Generic; + +namespace Spine { + public class SkeletonJson { + static public String TIMELINE_SCALE = "scale"; + static public String TIMELINE_ROTATE = "rotate"; + static public String TIMELINE_TRANSLATE = "translate"; + static public String TIMELINE_ATTACHMENT = "attachment"; + static public String TIMELINE_COLOR = "color"; + + static public String ATTACHMENT_REGION = "region"; + static public String ATTACHMENT_REGION_SEQUENCE = "regionSequence"; + + private AttachmentLoader attachmentLoader; + public float Scale { get; set; } + + public SkeletonJson (Atlas atlas) { + this.attachmentLoader = new AtlasAttachmentLoader(atlas); + Scale = 1; + } + + public SkeletonJson (AttachmentLoader attachmentLoader) { + if (attachmentLoader == null) throw new ArgumentNullException("attachmentLoader cannot be null."); + this.attachmentLoader = attachmentLoader; + Scale = 1; + } + + public SkeletonData ReadSkeletonData (String path) { + using (StreamReader reader = new StreamReader(path)) { + SkeletonData skeletonData = ReadSkeletonData(reader); + skeletonData.Name = Path.GetFileNameWithoutExtension(path); + return skeletonData; + } + } + + public SkeletonData ReadSkeletonData (TextReader reader) { + if (reader == null) throw new ArgumentNullException("reader cannot be null."); + + SkeletonData skeletonData = new SkeletonData(); + + var root = Json.Deserialize(reader) as Dictionary; + if (root == null) throw new Exception("Invalid JSON."); + + // Bones. + foreach (Dictionary boneMap in (List)root["bones"]) { + BoneData parent = null; + if (boneMap.ContainsKey("parent")) { + parent = skeletonData.FindBone((String)boneMap["parent"]); + if (parent == null) + throw new Exception("Parent bone not found: " + boneMap["parent"]); + } + BoneData boneData = new BoneData((String)boneMap["name"], parent); + boneData.Length = GetFloat(boneMap, "length", 0) * Scale; + boneData.X = GetFloat(boneMap, "x", 0) * Scale; + boneData.Y = GetFloat(boneMap, "y", 0) * Scale; + boneData.Rotation = GetFloat(boneMap, "rotation", 0); + boneData.ScaleX = GetFloat(boneMap, "scaleX", 1); + boneData.ScaleY = GetFloat(boneMap, "scaleY", 1); + skeletonData.AddBone(boneData); + } + + // Slots. + if (root.ContainsKey("slots")) { + var slots = (List)root["slots"]; + foreach (Dictionary slotMap in (List)slots) { + String slotName = (String)slotMap["name"]; + String boneName = (String)slotMap["bone"]; + BoneData boneData = skeletonData.FindBone(boneName); + if (boneData == null) + throw new Exception("Slot bone not found: " + boneName); + SlotData slotData = new SlotData(slotName, boneData); + + if (slotMap.ContainsKey("color")) { + String color = (String)slotMap["color"]; + slotData.R = ToColor(color, 0); + slotData.G = ToColor(color, 1); + slotData.B = ToColor(color, 2); + slotData.A = ToColor(color, 3); + } + + if (slotMap.ContainsKey("attachment")) + slotData.AttachmentName = (String)slotMap["attachment"]; + + skeletonData.AddSlot(slotData); + } + } + + // Skins. + if (root.ContainsKey("skins")) { + var skinMap = (Dictionary)root["skins"]; + foreach (KeyValuePair entry in skinMap) { + Skin skin = new Skin(entry.Key); + foreach (KeyValuePair slotEntry in (Dictionary)entry.Value) { + int slotIndex = skeletonData.FindSlotIndex(slotEntry.Key); + foreach (KeyValuePair attachmentEntry in ((Dictionary)slotEntry.Value)) { + Attachment attachment = ReadAttachment(skin, attachmentEntry.Key, (Dictionary)attachmentEntry.Value); + skin.AddAttachment(slotIndex, attachmentEntry.Key, attachment); + } + } + skeletonData.AddSkin(skin); + if (skin.Name == "default") + skeletonData.DefaultSkin = skin; + } + } + + + // Animations. + if (root.ContainsKey("animations")) { + var animationMap = (Dictionary)root["animations"]; + foreach (KeyValuePair entry in animationMap) + ReadAnimation(entry.Key, (Dictionary)entry.Value, skeletonData); + } + + skeletonData.Bones.TrimExcess(); + skeletonData.Slots.TrimExcess(); + skeletonData.Skins.TrimExcess(); + skeletonData.Animations.TrimExcess(); + return skeletonData; + } + + private Attachment ReadAttachment (Skin skin, String name, Dictionary map) { + if (map.ContainsKey("name")) + name = (String)map["name"]; + + AttachmentType type = AttachmentType.region; + if (map.ContainsKey("type")) + type = (AttachmentType)Enum.Parse(typeof(AttachmentType), (String)map["type"], false); + Attachment attachment = attachmentLoader.NewAttachment(skin, type, name); + + if (attachment is RegionAttachment) { + RegionAttachment regionAttachment = (RegionAttachment)attachment; + regionAttachment.X = GetFloat(map, "x", 0) * Scale; + regionAttachment.Y = GetFloat(map, "y", 0) * Scale; + regionAttachment.ScaleX = GetFloat(map, "scaleX", 1); + regionAttachment.ScaleY = GetFloat(map, "scaleY", 1); + regionAttachment.Rotation = GetFloat(map, "rotation", 0); + regionAttachment.Width = GetFloat(map, "width", 32) * Scale; + regionAttachment.Height = GetFloat(map, "height", 32) * Scale; + regionAttachment.UpdateOffset(); + } + + return attachment; + } + + private float GetFloat (Dictionary map, String name, float defaultValue) { + if (!map.ContainsKey(name)) + return (float)defaultValue; + return (float)map[name]; + } + + public static float ToColor (String hexString, int colorIndex) { + if (hexString.Length != 8) + throw new ArgumentException("Color hexidecimal length must be 8, recieved: " + hexString); + return Convert.ToInt32(hexString.Substring(colorIndex * 2, 2), 16) / (float)255; + } + + private void ReadAnimation (String name, Dictionary map, SkeletonData skeletonData) { + var timelines = new List(); + float duration = 0; + + if (map.ContainsKey("bones")) { + var bonesMap = (Dictionary)map["bones"]; + foreach (KeyValuePair entry in bonesMap) { + String boneName = entry.Key; + int boneIndex = skeletonData.FindBoneIndex(boneName); + if (boneIndex == -1) + throw new Exception("Bone not found: " + boneName); + + var timelineMap = (Dictionary)entry.Value; + foreach (KeyValuePair timelineEntry in timelineMap) { + var values = (List)timelineEntry.Value; + String timelineName = (String)timelineEntry.Key; + if (timelineName.Equals(TIMELINE_ROTATE)) { + RotateTimeline timeline = new RotateTimeline(values.Count); + timeline.BoneIndex = boneIndex; + + int frameIndex = 0; + foreach (Dictionary valueMap in values) { + float time = (float)valueMap["time"]; + timeline.SetFrame(frameIndex, time, (float)valueMap["angle"]); + ReadCurve(timeline, frameIndex, valueMap); + frameIndex++; + } + timelines.Add(timeline); + duration = Math.Max(duration, timeline.Frames[timeline.FrameCount * 2 - 2]); + + } else if (timelineName.Equals(TIMELINE_TRANSLATE) || timelineName.Equals(TIMELINE_SCALE)) { + TranslateTimeline timeline; + float timelineScale = 1; + if (timelineName.Equals(TIMELINE_SCALE)) + timeline = new ScaleTimeline(values.Count); + else { + timeline = new TranslateTimeline(values.Count); + timelineScale = Scale; + } + timeline.BoneIndex = boneIndex; + + int frameIndex = 0; + foreach (Dictionary valueMap in values) { + float time = (float)valueMap["time"]; + float x = valueMap.ContainsKey("x") ? (float)valueMap["x"] : 0; + float y = valueMap.ContainsKey("y") ? (float)valueMap["y"] : 0; + timeline.SetFrame(frameIndex, time, (float)x * timelineScale, (float)y * timelineScale); + ReadCurve(timeline, frameIndex, valueMap); + frameIndex++; + } + timelines.Add(timeline); + duration = Math.Max(duration, timeline.Frames[timeline.FrameCount * 3 - 3]); + + } else + throw new Exception("Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"); + } + } + } + + if (map.ContainsKey("slots")) { + var slotsMap = (Dictionary)map["slots"]; + foreach (KeyValuePair entry in slotsMap) { + String slotName = entry.Key; + int slotIndex = skeletonData.FindSlotIndex(slotName); + var timelineMap = (Dictionary)entry.Value; + + foreach (KeyValuePair timelineEntry in timelineMap) { + var values = (List)timelineEntry.Value; + String timelineName = (String)timelineEntry.Key; + if (timelineName.Equals(TIMELINE_COLOR)) { + ColorTimeline timeline = new ColorTimeline(values.Count); + timeline.SlotIndex = slotIndex; + + int frameIndex = 0; + foreach (Dictionary valueMap in values) { + float time = (float)valueMap["time"]; + String c = (String)valueMap["color"]; + timeline.setFrame(frameIndex, time, ToColor(c, 0), ToColor(c, 1), ToColor(c, 2), ToColor(c, 3)); + ReadCurve(timeline, frameIndex, valueMap); + frameIndex++; + } + timelines.Add(timeline); + duration = Math.Max(duration, timeline.Frames[timeline.FrameCount * 5 - 5]); + + } else if (timelineName.Equals(TIMELINE_ATTACHMENT)) { + AttachmentTimeline 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 + throw new Exception("Invalid timeline type for a slot: " + timelineName + " (" + slotName + ")"); + } + } + } + + timelines.TrimExcess(); + skeletonData.AddAnimation(new Animation(name, timelines, duration)); + } + + private void ReadCurve (CurveTimeline timeline, int frameIndex, Dictionary valueMap) { + if (!valueMap.ContainsKey("curve")) + return; + Object curveObject = valueMap["curve"]; + if (curveObject.Equals("stepped")) + timeline.SetStepped(frameIndex); + else if (curveObject is List) { + List curve = (List)curveObject; + timeline.SetCurve(frameIndex, (float)curve[0], (float)curve[1], (float)curve[2], (float)curve[3]); + } + } + } +} diff --git a/spine-unity/Assets/Plugins/Spine/spine-csharp/Skin.cs b/spine-unity/Assets/Plugins/Spine/spine-csharp/Skin.cs new file mode 100644 index 000000000..5a65855e3 --- /dev/null +++ b/spine-unity/Assets/Plugins/Spine/spine-csharp/Skin.cs @@ -0,0 +1,80 @@ +/******************************************************************************* + * Copyright (c) 2013, Esoteric Software + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 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; +using System.Collections.Generic; + +namespace Spine { + /** Stores attachments by slot index and attachment name. */ + public class Skin { + public String Name { get; private set; } + private Dictionary, Attachment> attachments = new Dictionary, Attachment>(); + + public Skin (String name) { + if (name == null) throw new ArgumentNullException("name cannot be null."); + Name = name; + } + + public void AddAttachment (int slotIndex, String name, Attachment attachment) { + if (attachment == null) throw new ArgumentNullException("attachment cannot be null."); + attachments.Add(new KeyValuePair(slotIndex, name), attachment); + } + + /** @return May be null. */ + public Attachment GetAttachment (int slotIndex, String name) { + Attachment attachment; + attachments.TryGetValue(new KeyValuePair(slotIndex, name), out attachment); + return attachment; + } + + public void FindNamesForSlot (int slotIndex, List names) { + if (names == null) throw new ArgumentNullException("names cannot be null."); + foreach (KeyValuePair key in attachments.Keys) + if (key.Key == slotIndex) names.Add(key.Value); + } + + public void FindAttachmentsForSlot (int slotIndex, List attachments) { + if (attachments == null) throw new ArgumentNullException("attachments cannot be null."); + foreach (KeyValuePair, Attachment> entry in this.attachments) + if (entry.Key.Key == slotIndex) attachments.Add(entry.Value); + } + + override public String ToString () { + return Name; + } + + /** Attach all attachments from this skin if the corresponding attachment from the old skin is currently attached. */ + internal void AttachAll (Skeleton skeleton, Skin oldSkin) { + foreach (KeyValuePair, Attachment> entry in oldSkin.attachments) { + int slotIndex = entry.Key.Key; + Slot slot = skeleton.Slots[slotIndex]; + if (slot.Attachment == entry.Value) { + Attachment attachment = GetAttachment(slotIndex, entry.Key.Value); + if (attachment != null) slot.Attachment = attachment; + } + } + } + } +} diff --git a/spine-unity/Assets/Plugins/Spine/spine-csharp/Slot.cs b/spine-unity/Assets/Plugins/Spine/spine-csharp/Slot.cs new file mode 100644 index 000000000..7a1322624 --- /dev/null +++ b/spine-unity/Assets/Plugins/Spine/spine-csharp/Slot.cs @@ -0,0 +1,86 @@ +/******************************************************************************* + * Copyright (c) 2013, Esoteric Software + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 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 Spine { + public class Slot { + public SlotData Data { get; private set; } + public Bone Bone { get; private set; } + public Skeleton Skeleton { get; private set; } + public float R { get; set; } + public float G { get; set; } + public float B { get; set; } + public float A { get; set; } + + /** May be null. */ + private Attachment attachment; + public Attachment Attachment { + get { + return attachment; + } + set { + attachment = value; + attachmentTime = Skeleton.Time; + } + } + + private float attachmentTime; + public float AttachmentTime { + get { + return Skeleton.Time - attachmentTime; + } + set { + attachmentTime = Skeleton.Time - value; + } + } + + public Slot (SlotData data, Skeleton skeleton, Bone bone) { + if (data == null) throw new ArgumentNullException("data cannot be null."); + if (skeleton == null) throw new ArgumentNullException("skeleton cannot be null."); + if (bone == null) throw new ArgumentNullException("bone cannot be null."); + Data = data; + Skeleton = skeleton; + Bone = bone; + SetToBindPose(); + } + + internal void SetToBindPose (int slotIndex) { + R = Data.R; + G = Data.G; + B = Data.B; + A = Data.A; + Attachment = Data.AttachmentName == null ? null : Skeleton.GetAttachment(slotIndex, Data.AttachmentName); + } + + public void SetToBindPose () { + SetToBindPose(Skeleton.Data.Slots.IndexOf(Data)); + } + + override public String ToString () { + return Data.Name; + } + } +} diff --git a/spine-unity/Assets/Plugins/Spine/spine-csharp/SlotData.cs b/spine-unity/Assets/Plugins/Spine/spine-csharp/SlotData.cs new file mode 100644 index 000000000..2e84f6946 --- /dev/null +++ b/spine-unity/Assets/Plugins/Spine/spine-csharp/SlotData.cs @@ -0,0 +1,54 @@ +/******************************************************************************* + * Copyright (c) 2013, Esoteric Software + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 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 Spine { + public class SlotData { + public String Name { get; private set; } + public BoneData BoneData { get; private set; } + public float R { get; set; } + public float G { get; set; } + public float B { get; set; } + public float A { get; set; } + /** @param attachmentName May be null. */ + public String AttachmentName { get; set; } + + public SlotData (String name, BoneData boneData) { + if (name == null) throw new ArgumentNullException("name cannot be null."); + if (boneData == null) throw new ArgumentNullException("boneData cannot be null."); + Name = name; + BoneData = boneData; + R = 1; + G = 1; + B = 1; + A = 1; + } + + override public String ToString () { + return Name; + } + } +}