diff --git a/spine-csharp/src/Animation.cs b/spine-csharp/src/Animation.cs index f9e5298e9..eb0b695c9 100644 --- a/spine-csharp/src/Animation.cs +++ b/spine-csharp/src/Animation.cs @@ -34,15 +34,15 @@ using System.Collections.Generic; namespace Spine { public class Animation { - internal List timelines; + internal ExposedList timelines; internal float duration; internal String name; public String Name { get { return name; } } - public List Timelines { get { return timelines; } set { timelines = value; } } + public ExposedList Timelines { get { return timelines; } set { timelines = value; } } public float Duration { get { return duration; } set { duration = value; } } - public Animation (String name, List timelines, float duration) { + public Animation (String name, ExposedList timelines, float duration) { if (name == null) throw new ArgumentNullException("name cannot be null."); if (timelines == null) throw new ArgumentNullException("timelines cannot be null."); this.name = name; @@ -53,7 +53,7 @@ namespace Spine { /// Poses the skeleton at the specified time for this animation. /// The last time the animation was applied. /// Any triggered events are added. - public void Apply (Skeleton skeleton, float lastTime, float time, bool loop, List events) { + public void Apply (Skeleton skeleton, float lastTime, float time, bool loop, ExposedList events) { if (skeleton == null) throw new ArgumentNullException("skeleton cannot be null."); if (loop && duration != 0) { @@ -61,16 +61,16 @@ namespace Spine { lastTime %= duration; } - List timelines = this.timelines; + ExposedList timelines = this.timelines; for (int i = 0, n = timelines.Count; i < n; i++) - timelines[i].Apply(skeleton, lastTime, time, events, 1); + timelines.Items[i].Apply(skeleton, lastTime, time, events, 1); } /// Poses the skeleton at the specified time for this animation mixed with the current pose. /// The last time the animation was applied. /// Any triggered events are added. /// The amount of this animation that affects the current pose. - public void Mix (Skeleton skeleton, float lastTime, float time, bool loop, List events, float alpha) { + public void Mix (Skeleton skeleton, float lastTime, float time, bool loop, ExposedList events, float alpha) { if (skeleton == null) throw new ArgumentNullException("skeleton cannot be null."); if (loop && duration != 0) { @@ -78,9 +78,9 @@ namespace Spine { lastTime %= duration; } - List timelines = this.timelines; + ExposedList timelines = this.timelines; for (int i = 0, n = timelines.Count; i < n; i++) - timelines[i].Apply(skeleton, lastTime, time, events, alpha); + timelines.Items[i].Apply(skeleton, lastTime, time, events, alpha); } /// After the first and before the last entry. @@ -125,7 +125,7 @@ namespace Spine { public interface Timeline { /// Sets the value(s) for the specified time. /// May be null to not collect fired events. - void Apply (Skeleton skeleton, float lastTime, float time, List events, float alpha); + void Apply (Skeleton skeleton, float lastTime, float time, ExposedList events, float alpha); } /// Base class for frames that use an interpolation bezier curve. @@ -140,7 +140,7 @@ namespace Spine { curves = new float[(frameCount - 1) * BEZIER_SIZE]; } - abstract public void Apply (Skeleton skeleton, float lastTime, float time, List firedEvents, float alpha); + abstract public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList firedEvents, float alpha); public void SetLinear (int frameIndex) { curves[frameIndex * BEZIER_SIZE] = LINEAR; @@ -230,11 +230,11 @@ namespace Spine { frames[frameIndex + 1] = angle; } - override public void Apply (Skeleton skeleton, float lastTime, float time, List firedEvents, float alpha) { + override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList firedEvents, float alpha) { float[] frames = this.frames; if (time < frames[0]) return; // Time is before first frame. - Bone bone = skeleton.bones[boneIndex]; + Bone bone = skeleton.bones.Items[boneIndex]; float amount; @@ -293,11 +293,11 @@ namespace Spine { frames[frameIndex + 2] = y; } - override public void Apply (Skeleton skeleton, float lastTime, float time, List firedEvents, float alpha) { + override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList firedEvents, float alpha) { float[] frames = this.frames; if (time < frames[0]) return; // Time is before first frame. - Bone bone = skeleton.bones[boneIndex]; + Bone bone = skeleton.bones.Items[boneIndex]; if (time >= frames[frames.Length - 3]) { // Time is after last frame. bone.x += (bone.data.x + frames[frames.Length - 2] - bone.x) * alpha; @@ -323,11 +323,11 @@ namespace Spine { : base(frameCount) { } - override public void Apply (Skeleton skeleton, float lastTime, float time, List firedEvents, float alpha) { + override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList firedEvents, float alpha) { float[] frames = this.frames; if (time < frames[0]) return; // Time is before first frame. - Bone bone = skeleton.bones[boneIndex]; + Bone bone = skeleton.bones.Items[boneIndex]; if (time >= frames[frames.Length - 3]) { // Time is after last frame. bone.scaleX += (bone.data.scaleX * frames[frames.Length - 2] - bone.scaleX) * alpha; bone.scaleY += (bone.data.scaleY * frames[frames.Length - 1] - bone.scaleY) * alpha; @@ -375,7 +375,7 @@ namespace Spine { frames[frameIndex + 4] = a; } - override public void Apply (Skeleton skeleton, float lastTime, float time, List firedEvents, float alpha) { + override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList firedEvents, float alpha) { float[] frames = this.frames; if (time < frames[0]) return; // Time is before first frame. @@ -403,7 +403,7 @@ namespace Spine { b = prevFrameB + (frames[frameIndex + FRAME_B] - prevFrameB) * percent; a = prevFrameA + (frames[frameIndex + FRAME_A] - prevFrameA) * percent; } - Slot slot = skeleton.slots[slotIndex]; + Slot slot = skeleton.slots.Items[slotIndex]; if (alpha < 1) { slot.r += (r - slot.r) * alpha; slot.g += (g - slot.g) * alpha; @@ -439,7 +439,7 @@ namespace Spine { attachmentNames[frameIndex] = attachmentName; } - public void Apply (Skeleton skeleton, float lastTime, float time, List firedEvents, float alpha) { + public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList firedEvents, float alpha) { float[] frames = this.frames; if (time < frames[0]) { if (lastTime > time) Apply(skeleton, lastTime, int.MaxValue, null, 0); @@ -451,7 +451,7 @@ namespace Spine { if (frames[frameIndex] < lastTime) return; String attachmentName = attachmentNames[frameIndex]; - skeleton.slots[slotIndex].Attachment = + skeleton.slots.Items[slotIndex].Attachment = attachmentName == null ? null : skeleton.GetAttachment(slotIndex, attachmentName); } } @@ -476,7 +476,7 @@ namespace Spine { } /// Fires events for frames > lastTime and <= time. - public void Apply (Skeleton skeleton, float lastTime, float time, List firedEvents, float alpha) { + public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList firedEvents, float alpha) { if (firedEvents == null) return; float[] frames = this.frames; int frameCount = frames.Length; @@ -524,7 +524,7 @@ namespace Spine { drawOrders[frameIndex] = drawOrder; } - public void Apply (Skeleton skeleton, float lastTime, float time, List firedEvents, float alpha) { + public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList firedEvents, float alpha) { float[] frames = this.frames; if (time < frames[0]) return; // Time is before first frame. @@ -534,15 +534,16 @@ namespace Spine { else frameIndex = Animation.binarySearch(frames, time) - 1; - List drawOrder = skeleton.drawOrder; - List slots = skeleton.slots; + ExposedList drawOrder = skeleton.drawOrder; + ExposedList slots = skeleton.slots; int[] drawOrderToSetupIndex = drawOrders[frameIndex]; if (drawOrderToSetupIndex == null) { drawOrder.Clear(); - drawOrder.AddRange(slots); + for (int i = 0, n = slots.Count; i < n; i++) + drawOrder.Add(slots.Items[i]); } else { for (int i = 0, n = drawOrderToSetupIndex.Length; i < n; i++) - drawOrder[i] = slots[drawOrderToSetupIndex[i]]; + drawOrder.Items[i] = slots.Items[drawOrderToSetupIndex[i]]; } } } @@ -570,8 +571,8 @@ namespace Spine { frameVertices[frameIndex] = vertices; } - override public void Apply (Skeleton skeleton, float lastTime, float time, List firedEvents, float alpha) { - Slot slot = skeleton.slots[slotIndex]; + override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList firedEvents, float alpha) { + Slot slot = skeleton.slots.Items[slotIndex]; if (slot.attachment != attachment) return; float[] frames = this.frames; @@ -649,11 +650,11 @@ namespace Spine { frames[frameIndex + 2] = bendDirection; } - override public void Apply (Skeleton skeleton, float lastTime, float time, List firedEvents, float alpha) { + override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList firedEvents, float alpha) { float[] frames = this.frames; if (time < frames[0]) return; // Time is before first frame. - IkConstraint ikConstraint = skeleton.ikConstraints[ikConstraintIndex]; + IkConstraint ikConstraint = skeleton.ikConstraints.Items[ikConstraintIndex]; if (time >= frames[frames.Length - 3]) { // Time is after last frame. ikConstraint.mix += (frames[frames.Length - 2] - ikConstraint.mix) * alpha; @@ -693,7 +694,7 @@ namespace Spine { frames[frameIndex + 1] = flip ? 1 : 0; } - public void Apply (Skeleton skeleton, float lastTime, float time, List firedEvents, float alpha) { + public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList firedEvents, float alpha) { float[] frames = this.frames; if (time < frames[0]) { if (lastTime > time) Apply(skeleton, lastTime, int.MaxValue, null, 0); @@ -704,7 +705,7 @@ namespace Spine { int frameIndex = (time >= frames[frames.Length - 2] ? frames.Length : Animation.binarySearch(frames, time, 2)) - 2; if (frames[frameIndex] < lastTime) return; - SetFlip(skeleton.bones[boneIndex], frames[frameIndex + 1] != 0); + SetFlip(skeleton.bones.Items[boneIndex], frames[frameIndex + 1] != 0); } virtual protected void SetFlip (Bone bone, bool flip) { diff --git a/spine-csharp/src/AnimationState.cs b/spine-csharp/src/AnimationState.cs index 4a34df6ca..d616cb886 100644 --- a/spine-csharp/src/AnimationState.cs +++ b/spine-csharp/src/AnimationState.cs @@ -36,8 +36,8 @@ using System.Text; namespace Spine { public class AnimationState { private AnimationStateData data; - private List tracks = new List(); - private List events = new List(); + private ExposedList tracks = new ExposedList(); + private ExposedList events = new ExposedList(); private float timeScale = 1; public AnimationStateData Data { get { return data; } } @@ -61,7 +61,7 @@ namespace Spine { public void Update (float delta) { delta *= timeScale; for (int i = 0; i < tracks.Count; i++) { - TrackEntry current = tracks[i]; + TrackEntry current = tracks.Items[i]; if (current == null) continue; float trackDelta = delta * current.timeScale; @@ -93,10 +93,10 @@ namespace Spine { } public void Apply (Skeleton skeleton) { - List events = this.events; + ExposedList events = this.events; for (int i = 0; i < tracks.Count; i++) { - TrackEntry current = tracks[i]; + TrackEntry current = tracks.Items[i]; if (current == null) continue; events.Clear(); @@ -125,7 +125,7 @@ namespace Spine { } for (int ii = 0, nn = events.Count; ii < nn; ii++) { - Event e = events[ii]; + Event e = events.Items[ii]; current.OnEvent(this, i, e); if (Event != null) Event(this, i, e); } @@ -142,17 +142,17 @@ namespace Spine { public void ClearTrack (int trackIndex) { if (trackIndex >= tracks.Count) return; - TrackEntry current = tracks[trackIndex]; + TrackEntry current = tracks.Items[trackIndex]; if (current == null) return; current.OnEnd(this, trackIndex); if (End != null) End(this, trackIndex); - tracks[trackIndex] = null; + tracks.Items[trackIndex] = null; } private TrackEntry ExpandToIndex (int index) { - if (index < tracks.Count) return tracks[index]; + if (index < tracks.Count) return tracks.Items[index]; while (index >= tracks.Count) tracks.Add(null); return null; @@ -178,7 +178,7 @@ namespace Spine { } } - tracks[index] = entry; + tracks.Items[index] = entry; entry.OnStart(this, index); if (Start != null) Start(this, index); @@ -224,7 +224,7 @@ namespace Spine { last = last.next; last.next = entry; } else - tracks[trackIndex] = entry; + tracks.Items[trackIndex] = entry; if (delay <= 0) { if (last != null) @@ -240,13 +240,13 @@ namespace Spine { /// May be null. public TrackEntry GetCurrent (int trackIndex) { if (trackIndex >= tracks.Count) return null; - return tracks[trackIndex]; + return tracks.Items[trackIndex]; } override public String ToString () { StringBuilder buffer = new StringBuilder(); for (int i = 0, n = tracks.Count; i < n; i++) { - TrackEntry entry = tracks[i]; + TrackEntry entry = tracks.Items[i]; if (entry == null) continue; if (buffer.Length > 0) buffer.Append(", "); buffer.Append(entry.ToString()); diff --git a/spine-csharp/src/Attachments/SkinnedMeshAttachment.cs b/spine-csharp/src/Attachments/SkinnedMeshAttachment.cs index 86874fec1..1e99fab38 100644 --- a/spine-csharp/src/Attachments/SkinnedMeshAttachment.cs +++ b/spine-csharp/src/Attachments/SkinnedMeshAttachment.cs @@ -96,7 +96,7 @@ namespace Spine { public void ComputeWorldVertices (Slot slot, float[] worldVertices) { Skeleton skeleton = slot.bone.skeleton; - List skeletonBones = skeleton.bones; + ExposedList skeletonBones = skeleton.bones; float x = skeleton.x, y = skeleton.y; float[] weights = this.weights; int[] bones = this.bones; @@ -105,7 +105,7 @@ namespace Spine { float wx = 0, wy = 0; int nn = bones[v++] + v; for (; v < nn; v++, b += 3) { - Bone bone = skeletonBones[bones[v]]; + Bone bone = skeletonBones.Items[bones[v]]; float vx = weights[b], vy = weights[b + 1], weight = weights[b + 2]; wx += (vx * bone.m00 + vy * bone.m01 + bone.worldX) * weight; wy += (vx * bone.m10 + vy * bone.m11 + bone.worldY) * weight; @@ -119,7 +119,7 @@ namespace Spine { float wx = 0, wy = 0; int nn = bones[v++] + v; for (; v < nn; v++, b += 3, f += 2) { - Bone bone = skeletonBones[bones[v]]; + Bone bone = skeletonBones.Items[bones[v]]; float vx = weights[b] + ffd[f], vy = weights[b + 1] + ffd[f + 1], weight = weights[b + 2]; wx += (vx * bone.m00 + vy * bone.m01 + bone.worldX) * weight; wy += (vx * bone.m10 + vy * bone.m11 + bone.worldY) * weight; diff --git a/spine-csharp/src/Bone.cs b/spine-csharp/src/Bone.cs index 32a952827..748e35dc6 100644 --- a/spine-csharp/src/Bone.cs +++ b/spine-csharp/src/Bone.cs @@ -39,7 +39,7 @@ namespace Spine { internal BoneData data; internal Skeleton skeleton; internal Bone parent; - internal List children = new List(); + internal ExposedList children = new ExposedList(); internal float x, y, rotation, rotationIK, scaleX, scaleY; internal bool flipX, flipY; internal float m00, m01, m10, m11; @@ -49,7 +49,7 @@ namespace Spine { public BoneData Data { get { return data; } } public Skeleton Skeleton { get { return skeleton; } } public Bone Parent { get { return parent; } } - public List Children { get { return children; } } + public ExposedList Children { get { return children; } } public float X { get { return x; } set { x = value; } } public float Y { get { return y; } set { y = value; } } /// The forward kinetics rotation. diff --git a/spine-csharp/src/ExposedList.cs b/spine-csharp/src/ExposedList.cs new file mode 100644 index 000000000..efac67ac2 --- /dev/null +++ b/spine-csharp/src/ExposedList.cs @@ -0,0 +1,585 @@ +// +// System.Collections.Generic.List +// +// Authors: +// Ben Maurer (bmaurer@ximian.com) +// Martin Baulig (martin@ximian.com) +// Carlos Alberto Cortez (calberto.cortez@gmail.com) +// David Waite (mass@akuma.org) +// +// Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com) +// Copyright (C) 2005 David Waite +// +// 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.Diagnostics; + +namespace Spine { + [Serializable] + [DebuggerDisplay("Count={Count}")] + public class ExposedList : IEnumerable { + public T[] Items; + public int Count; + + private const int DefaultCapacity = 4; + private static readonly T[] EmptyArray = new T[0]; + private int version; + + public ExposedList() { + Items = EmptyArray; + } + + public ExposedList(IEnumerable collection) { + CheckCollection(collection); + + // initialize to needed size (if determinable) + ICollection c = collection as ICollection; + if (c == null) { + Items = EmptyArray; + AddEnumerable(collection); + } else { + Items = new T[c.Count]; + AddCollection(c); + } + } + + public ExposedList(int capacity) { + if (capacity < 0) + throw new ArgumentOutOfRangeException("capacity"); + Items = new T[capacity]; + } + + internal ExposedList(T[] data, int size) { + Items = data; + Count = size; + } + + public void Add(T item) { + // If we check to see if we need to grow before trying to grow + // we can speed things up by 25% + if (Count == Items.Length) + GrowIfNeeded(1); + Items[Count ++] = item; + version++; + } + + public void GrowIfNeeded(int newCount) { + int minimumSize = Count + newCount; + if (minimumSize > Items.Length) + Capacity = Math.Max(Math.Max(Capacity * 2, DefaultCapacity), minimumSize); + } + + private void CheckRange(int idx, int count) { + if (idx < 0) + throw new ArgumentOutOfRangeException("index"); + + if (count < 0) + throw new ArgumentOutOfRangeException("count"); + + if ((uint) idx + (uint) count > (uint) Count) + throw new ArgumentException("index and count exceed length of list"); + } + + private void AddCollection(ICollection collection) { + int collectionCount = collection.Count; + if (collectionCount == 0) + return; + + GrowIfNeeded(collectionCount); + collection.CopyTo(Items, Count); + Count += collectionCount; + } + + private void AddEnumerable(IEnumerable enumerable) { + foreach (T t in enumerable) { + Add(t); + } + } + + public void AddRange(IEnumerable collection) { + CheckCollection(collection); + + ICollection c = collection as ICollection; + if (c != null) + AddCollection(c); + else + AddEnumerable(collection); + version++; + } + + public int BinarySearch(T item) { + return Array.BinarySearch(Items, 0, Count, item); + } + + public int BinarySearch(T item, IComparer comparer) { + return Array.BinarySearch(Items, 0, Count, item, comparer); + } + + public int BinarySearch(int index, int count, T item, IComparer comparer) { + CheckRange(index, count); + return Array.BinarySearch(Items, index, count, item, comparer); + } + + public void Clear(bool clearArray = true) { + if (clearArray) + Array.Clear(Items, 0, Items.Length); + + Count = 0; + version++; + } + + public bool Contains(T item) { + return Array.IndexOf(Items, item, 0, Count) != -1; + } + + public ExposedList ConvertAll(Converter converter) { + if (converter == null) + throw new ArgumentNullException("converter"); + ExposedList u = new ExposedList(Count); + for (int i = 0; i < Count; i++) + u.Items[i] = converter(Items[i]); + + u.Count = Count; + return u; + } + + public void CopyTo(T[] array) { + Array.Copy(Items, 0, array, 0, Count); + } + + public void CopyTo(T[] array, int arrayIndex) { + Array.Copy(Items, 0, array, arrayIndex, Count); + } + + public void CopyTo(int index, T[] array, int arrayIndex, int count) { + CheckRange(index, count); + Array.Copy(Items, index, array, arrayIndex, count); + } + + public bool Exists(Predicate match) { + CheckMatch(match); + return GetIndex(0, Count, match) != -1; + } + + public T Find(Predicate match) { + CheckMatch(match); + int i = GetIndex(0, Count, match); + return (i != -1) ? Items[i] : default (T); + } + + private static void CheckMatch(Predicate match) { + if (match == null) + throw new ArgumentNullException("match"); + } + + public ExposedList FindAll(Predicate match) { + CheckMatch(match); + return FindAllList(match); + } + + private ExposedList FindAllList(Predicate match) { + ExposedList results = new ExposedList(); + for (int i = 0; i < Count; i++) + if (match(Items[i])) + results.Add(Items[i]); + + return results; + } + + public int FindIndex(Predicate match) { + CheckMatch(match); + return GetIndex(0, Count, match); + } + + public int FindIndex(int startIndex, Predicate match) { + CheckMatch(match); + CheckIndex(startIndex); + return GetIndex(startIndex, Count - startIndex, match); + } + + public int FindIndex(int startIndex, int count, Predicate match) { + CheckMatch(match); + CheckRange(startIndex, count); + return GetIndex(startIndex, count, match); + } + + private int GetIndex(int startIndex, int count, Predicate match) { + int end = startIndex + count; + for (int i = startIndex; i < end; i ++) + if (match(Items[i])) + return i; + + return -1; + } + + public T FindLast(Predicate match) { + CheckMatch(match); + int i = GetLastIndex(0, Count, match); + return i == -1 ? default (T) : Items[i]; + } + + public int FindLastIndex(Predicate match) { + CheckMatch(match); + return GetLastIndex(0, Count, match); + } + + public int FindLastIndex(int startIndex, Predicate match) { + CheckMatch(match); + CheckIndex(startIndex); + return GetLastIndex(0, startIndex + 1, match); + } + + public int FindLastIndex(int startIndex, int count, Predicate match) { + CheckMatch(match); + int start = startIndex - count + 1; + CheckRange(start, count); + return GetLastIndex(start, count, match); + } + + private int GetLastIndex(int startIndex, int count, Predicate match) { + // unlike FindLastIndex, takes regular params for search range + for (int i = startIndex + count; i != startIndex;) + if (match(Items[--i])) + return i; + return -1; + } + + public void ForEach(Action action) { + if (action == null) + throw new ArgumentNullException("action"); + for (int i = 0; i < Count; i++) + action(Items[i]); + } + + public Enumerator GetEnumerator() { + return new Enumerator(this); + } + + public ExposedList GetRange(int index, int count) { + CheckRange(index, count); + T[] tmpArray = new T[count]; + Array.Copy(Items, index, tmpArray, 0, count); + return new ExposedList(tmpArray, count); + } + + public int IndexOf(T item) { + return Array.IndexOf(Items, item, 0, Count); + } + + public int IndexOf(T item, int index) { + CheckIndex(index); + return Array.IndexOf(Items, item, index, Count - index); + } + + public int IndexOf(T item, int index, int count) { + if (index < 0) + throw new ArgumentOutOfRangeException("index"); + + if (count < 0) + throw new ArgumentOutOfRangeException("count"); + + if ((uint) index + (uint) count > (uint) Count) + throw new ArgumentOutOfRangeException("index and count exceed length of list"); + + return Array.IndexOf(Items, item, index, count); + } + + private void Shift(int start, int delta) { + if (delta < 0) + start -= delta; + + if (start < Count) + Array.Copy(Items, start, Items, start + delta, Count - start); + + Count += delta; + + if (delta < 0) + Array.Clear(Items, Count, -delta); + } + + private void CheckIndex(int index) { + if (index < 0 || (uint) index > (uint) Count) + throw new ArgumentOutOfRangeException("index"); + } + + public void Insert(int index, T item) { + CheckIndex(index); + if (Count == Items.Length) + GrowIfNeeded(1); + Shift(index, 1); + Items[index] = item; + version++; + } + + private void CheckCollection(IEnumerable collection) { + if (collection == null) + throw new ArgumentNullException("collection"); + } + + public void InsertRange(int index, IEnumerable collection) { + CheckCollection(collection); + CheckIndex(index); + if (collection == this) { + T[] buffer = new T[Count]; + CopyTo(buffer, 0); + GrowIfNeeded(Count); + Shift(index, buffer.Length); + Array.Copy(buffer, 0, Items, index, buffer.Length); + } else { + ICollection c = collection as ICollection; + if (c != null) + InsertCollection(index, c); + else + InsertEnumeration(index, collection); + } + version++; + } + + private void InsertCollection(int index, ICollection collection) { + int collectionCount = collection.Count; + GrowIfNeeded(collectionCount); + + Shift(index, collectionCount); + collection.CopyTo(Items, index); + } + + private void InsertEnumeration(int index, IEnumerable enumerable) { + foreach (T t in enumerable) + Insert(index++, t); + } + + public int LastIndexOf(T item) { + return Array.LastIndexOf(Items, item, Count - 1, Count); + } + + public int LastIndexOf(T item, int index) { + CheckIndex(index); + return Array.LastIndexOf(Items, item, index, index + 1); + } + + public int LastIndexOf(T item, int index, int count) { + if (index < 0) + throw new ArgumentOutOfRangeException("index", index, "index is negative"); + + if (count < 0) + throw new ArgumentOutOfRangeException("count", count, "count is negative"); + + if (index - count + 1 < 0) + throw new ArgumentOutOfRangeException("count", count, "count is too large"); + + return Array.LastIndexOf(Items, item, index, count); + } + + public bool Remove(T item) { + int loc = IndexOf(item); + if (loc != -1) + RemoveAt(loc); + + return loc != -1; + } + + public int RemoveAll(Predicate match) { + CheckMatch(match); + int i = 0; + int j = 0; + + // Find the first item to remove + for (i = 0; i < Count; i++) + if (match(Items[i])) + break; + + if (i == Count) + return 0; + + version++; + + // Remove any additional items + for (j = i + 1; j < Count; j++) { + if (!match(Items[j])) + Items[i++] = Items[j]; + } + if (j - i > 0) + Array.Clear(Items, i, j - i); + + Count = i; + return (j - i); + } + + public void RemoveAt(int index) { + if (index < 0 || (uint) index >= (uint) Count) + throw new ArgumentOutOfRangeException("index"); + Shift(index, -1); + Array.Clear(Items, Count, 1); + version++; + } + + public void RemoveRange(int index, int count) { + CheckRange(index, count); + if (count > 0) { + Shift(index, -count); + Array.Clear(Items, Count, count); + version++; + } + } + + public void Reverse() { + Array.Reverse(Items, 0, Count); + version++; + } + + public void Reverse(int index, int count) { + CheckRange(index, count); + Array.Reverse(Items, index, count); + version++; + } + + public void Sort() { + Array.Sort(Items, 0, Count, Comparer.Default); + version++; + } + + public void Sort(IComparer comparer) { + Array.Sort(Items, 0, Count, comparer); + version++; + } + + public void Sort(Comparison comparison) { + Array.Sort(Items, comparison); + version++; + } + + public void Sort(int index, int count, IComparer comparer) { + CheckRange(index, count); + Array.Sort(Items, index, count, comparer); + version++; + } + + public T[] ToArray() { + T[] t = new T[Count]; + Array.Copy(Items, t, Count); + + return t; + } + + public void TrimExcess() { + Capacity = Count; + } + + public bool TrueForAll(Predicate match) { + CheckMatch(match); + + for (int i = 0; i < Count; i++) + if (!match(Items[i])) + return false; + + return true; + } + + public int Capacity { + get { + return Items.Length; + } + set { + if ((uint) value < (uint) Count) + throw new ArgumentOutOfRangeException(); + + Array.Resize(ref Items, value); + } + } + + #region Interface implementations. + + IEnumerator IEnumerable.GetEnumerator() { + return GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() { + return GetEnumerator(); + } + + #endregion + + [Serializable] + public struct Enumerator : IEnumerator, IDisposable { + private ExposedList l; + private int next; + private int ver; + + private T current; + + internal Enumerator(ExposedList l) + : this() { + this.l = l; + ver = l.version; + } + + public void Dispose() { + l = null; + } + + private void VerifyState() { + if (l == null) + throw new ObjectDisposedException(GetType().FullName); + if (ver != l.version) + throw new InvalidOperationException( + "Collection was modified; enumeration operation may not execute."); + } + + public bool MoveNext() { + VerifyState(); + + if (next < 0) + return false; + + if (next < l.Count) { + current = l.Items[next++]; + return true; + } + + next = -1; + return false; + } + + public T Current { + get { + return current; + } + } + + void IEnumerator.Reset() { + VerifyState(); + next = 0; + } + + object IEnumerator.Current { + get { + VerifyState(); + if (next <= 0) + throw new InvalidOperationException(); + return current; + } + } + } + } +} \ No newline at end of file diff --git a/spine-csharp/src/IkConstraint.cs b/spine-csharp/src/IkConstraint.cs index e81197af4..e5b7bc9d4 100644 --- a/spine-csharp/src/IkConstraint.cs +++ b/spine-csharp/src/IkConstraint.cs @@ -37,13 +37,13 @@ namespace Spine { private const float radDeg = 180 / (float)Math.PI; internal IkConstraintData data; - internal List bones = new List(); + internal ExposedList bones = new ExposedList(); internal Bone target; internal int bendDirection; internal float mix; public IkConstraintData Data { get { return data; } } - public List Bones { get { return bones; } } + public ExposedList Bones { get { return bones; } } public Bone Target { get { return target; } set { target = value; } } public int BendDirection { get { return bendDirection; } set { bendDirection = value; } } public float Mix { get { return mix; } set { mix = value; } } @@ -55,7 +55,7 @@ namespace Spine { mix = data.mix; bendDirection = data.bendDirection; - bones = new List(data.bones.Count); + bones = new ExposedList(data.bones.Count); foreach (BoneData boneData in data.bones) bones.Add(skeleton.FindBone(boneData.name)); target = skeleton.FindBone(data.target.name); @@ -63,13 +63,13 @@ namespace Spine { public void apply () { Bone target = this.target; - List bones = this.bones; + ExposedList bones = this.bones; switch (bones.Count) { case 1: - apply(bones[0], target.worldX, target.worldY, mix); + apply(bones.Items[0], target.worldX, target.worldY, mix); break; case 2: - apply(bones[0], bones[1], target.worldX, target.worldY, bendDirection, mix); + apply(bones.Items[0], bones.Items[1], target.worldX, target.worldY, bendDirection, mix); break; } } diff --git a/spine-csharp/src/Skeleton.cs b/spine-csharp/src/Skeleton.cs index 7565acf90..8cd9b91f1 100644 --- a/spine-csharp/src/Skeleton.cs +++ b/spine-csharp/src/Skeleton.cs @@ -35,11 +35,11 @@ using System.Collections.Generic; namespace Spine { public class Skeleton { internal SkeletonData data; - internal List bones; - internal List slots; - internal List drawOrder; - internal List ikConstraints; - private List> boneCache = new List>(); + internal ExposedList bones; + internal ExposedList slots; + internal ExposedList drawOrder; + internal ExposedList ikConstraints; + private ExposedList> boneCache = new ExposedList>(); internal Skin skin; internal float r = 1, g = 1, b = 1, a = 1; internal float time; @@ -47,10 +47,10 @@ namespace Spine { internal float x, y; public SkeletonData Data { get { return data; } } - public List Bones { get { return bones; } } - public List Slots { get { return slots; } } - public List DrawOrder { get { return drawOrder; } } - public List IkConstraints { get { return ikConstraints; } set { ikConstraints = value; } } + public ExposedList Bones { get { return bones; } } + public ExposedList Slots { get { return slots; } } + public ExposedList DrawOrder { get { return drawOrder; } } + public ExposedList IkConstraints { get { return ikConstraints; } set { ikConstraints = value; } } public Skin Skin { get { return skin; } set { skin = value; } } public float R { get { return r; } set { r = value; } } public float G { get { return g; } set { g = value; } } @@ -64,7 +64,7 @@ namespace Spine { public Bone RootBone { get { - return bones.Count == 0 ? null : bones[0]; + return bones.Count == 0 ? null : bones.Items[0]; } } @@ -72,24 +72,24 @@ namespace Spine { if (data == null) throw new ArgumentNullException("data cannot be null."); this.data = data; - bones = new List(data.bones.Count); + bones = new ExposedList(data.bones.Count); foreach (BoneData boneData in data.bones) { - Bone parent = boneData.parent == null ? null : bones[data.bones.IndexOf(boneData.parent)]; + Bone parent = boneData.parent == null ? null : bones.Items[data.bones.IndexOf(boneData.parent)]; Bone bone = new Bone(boneData, this, parent); if (parent != null) parent.children.Add(bone); bones.Add(bone); } - slots = new List(data.slots.Count); - drawOrder = new List(data.slots.Count); + slots = new ExposedList(data.slots.Count); + drawOrder = new ExposedList(data.slots.Count); foreach (SlotData slotData in data.slots) { - Bone bone = bones[data.bones.IndexOf(slotData.boneData)]; + Bone bone = bones.Items[data.bones.IndexOf(slotData.boneData)]; Slot slot = new Slot(slotData, bone); slots.Add(slot); drawOrder.Add(slot); } - ikConstraints = new List(data.ikConstraints.Count); + ikConstraints = new ExposedList(data.ikConstraints.Count); foreach (IkConstraintData ikConstraintData in data.ikConstraints) ikConstraints.Add(new IkConstraint(ikConstraintData, this)); @@ -99,31 +99,31 @@ namespace Spine { /// Caches information about bones and IK constraints. Must be called if bones or IK constraints are added or /// removed. public void UpdateCache () { - List> boneCache = this.boneCache; - List ikConstraints = this.ikConstraints; + ExposedList> boneCache = this.boneCache; + ExposedList ikConstraints = this.ikConstraints; int ikConstraintsCount = ikConstraints.Count; int arrayCount = ikConstraintsCount + 1; if (boneCache.Count > arrayCount) boneCache.RemoveRange(arrayCount, boneCache.Count - arrayCount); for (int i = 0, n = boneCache.Count; i < n; i++) - boneCache[i].Clear(); + boneCache.Items[i].Clear(); while (boneCache.Count < arrayCount) - boneCache.Add(new List()); + boneCache.Add(new ExposedList()); - List nonIkBones = boneCache[0]; + ExposedList nonIkBones = boneCache.Items[0]; for (int i = 0, n = bones.Count; i < n; i++) { - Bone bone = bones[i]; + Bone bone = bones.Items[i]; Bone current = bone; do { for (int ii = 0; ii < ikConstraintsCount; ii++) { - IkConstraint ikConstraint = ikConstraints[ii]; - Bone parent = ikConstraint.bones[0]; - Bone child = ikConstraint.bones[ikConstraint.bones.Count - 1]; + IkConstraint ikConstraint = ikConstraints.Items[ii]; + Bone parent = ikConstraint.bones.Items[0]; + Bone child = ikConstraint.bones.Items[ikConstraint.bones.Count - 1]; while (true) { if (current == child) { - boneCache[ii].Add(bone); - boneCache[ii + 1].Add(bone); + boneCache.Items[ii].Add(bone); + boneCache.Items[ii + 1].Add(bone); goto outer; } if (child == parent) break; @@ -139,20 +139,20 @@ namespace Spine { /// Updates the world transform for each bone and applies IK constraints. public void UpdateWorldTransform () { - List bones = this.bones; + ExposedList bones = this.bones; for (int ii = 0, nn = bones.Count; ii < nn; ii++) { - Bone bone = bones[ii]; + Bone bone = bones.Items[ii]; bone.rotationIK = bone.rotation; } - List> boneCache = this.boneCache; - List ikConstraints = this.ikConstraints; + ExposedList> boneCache = this.boneCache; + ExposedList ikConstraints = this.ikConstraints; int i = 0, last = boneCache.Count - 1; while (true) { - List updateBones = boneCache[i]; + ExposedList updateBones = boneCache.Items[i]; for (int ii = 0, nn = updateBones.Count; ii < nn; ii++) - updateBones[ii].UpdateWorldTransform(); + updateBones.Items[ii].UpdateWorldTransform(); if (i == last) break; - ikConstraints[i].apply(); + ikConstraints.Items[i].apply(); i++; } } @@ -164,32 +164,34 @@ namespace Spine { } public void SetBonesToSetupPose () { - List bones = this.bones; + ExposedList bones = this.bones; for (int i = 0, n = bones.Count; i < n; i++) - bones[i].SetToSetupPose(); + bones.Items[i].SetToSetupPose(); - List ikConstraints = this.ikConstraints; + ExposedList ikConstraints = this.ikConstraints; for (int i = 0, n = ikConstraints.Count; i < n; i++) { - IkConstraint ikConstraint = ikConstraints[i]; + IkConstraint ikConstraint = ikConstraints.Items[i]; ikConstraint.bendDirection = ikConstraint.data.bendDirection; ikConstraint.mix = ikConstraint.data.mix; } } public void SetSlotsToSetupPose () { - List slots = this.slots; + ExposedList slots = this.slots; drawOrder.Clear(); - drawOrder.AddRange(slots); for (int i = 0, n = slots.Count; i < n; i++) - slots[i].SetToSetupPose(i); + drawOrder.Add(slots.Items[i]); + + for (int i = 0, n = slots.Count; i < n; i++) + slots.Items[i].SetToSetupPose(i); } /// May be null. public Bone FindBone (String boneName) { if (boneName == null) throw new ArgumentNullException("boneName cannot be null."); - List bones = this.bones; + ExposedList bones = this.bones; for (int i = 0, n = bones.Count; i < n; i++) { - Bone bone = bones[i]; + Bone bone = bones.Items[i]; if (bone.data.name == boneName) return bone; } return null; @@ -198,18 +200,18 @@ namespace Spine { /// -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; + ExposedList bones = this.bones; for (int i = 0, n = bones.Count; i < n; i++) - if (bones[i].data.name == boneName) return i; + if (bones.Items[i].data.name == boneName) return i; return -1; } /// May be null. public Slot FindSlot (String slotName) { if (slotName == null) throw new ArgumentNullException("slotName cannot be null."); - List slots = this.slots; + ExposedList slots = this.slots; for (int i = 0, n = slots.Count; i < n; i++) { - Slot slot = slots[i]; + Slot slot = slots.Items[i]; if (slot.data.name == slotName) return slot; } return null; @@ -218,9 +220,9 @@ namespace Spine { /// -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; + ExposedList slots = this.slots; for (int i = 0, n = slots.Count; i < n; i++) - if (slots[i].data.name.Equals(slotName)) return i; + if (slots.Items[i].data.name.Equals(slotName)) return i; return -1; } @@ -240,9 +242,9 @@ namespace Spine { if (skin != null) newSkin.AttachAll(this, skin); else { - List slots = this.slots; + ExposedList slots = this.slots; for (int i = 0, n = slots.Count; i < n; i++) { - Slot slot = slots[i]; + Slot slot = slots.Items[i]; String name = slot.data.attachmentName; if (name != null) { Attachment attachment = newSkin.GetAttachment(i, name); @@ -273,9 +275,9 @@ namespace Spine { /// May be null. public void SetAttachment (String slotName, String attachmentName) { if (slotName == null) throw new ArgumentNullException("slotName cannot be null."); - List slots = this.slots; + ExposedList slots = this.slots; for (int i = 0, n = slots.Count; i < n; i++) { - Slot slot = slots[i]; + Slot slot = slots.Items[i]; if (slot.data.name == slotName) { Attachment attachment = null; if (attachmentName != null) { @@ -292,9 +294,9 @@ namespace Spine { /** @return May be null. */ public IkConstraint FindIkConstraint (String ikConstraintName) { if (ikConstraintName == null) throw new ArgumentNullException("ikConstraintName cannot be null."); - List ikConstraints = this.ikConstraints; + ExposedList ikConstraints = this.ikConstraints; for (int i = 0, n = ikConstraints.Count; i < n; i++) { - IkConstraint ikConstraint = ikConstraints[i]; + IkConstraint ikConstraint = ikConstraints.Items[i]; if (ikConstraint.data.name == ikConstraintName) return ikConstraint; } return null; diff --git a/spine-csharp/src/SkeletonBinary.cs b/spine-csharp/src/SkeletonBinary.cs index ab12c506f..4cbd34f1c 100644 --- a/spine-csharp/src/SkeletonBinary.cs +++ b/spine-csharp/src/SkeletonBinary.cs @@ -119,7 +119,7 @@ namespace Spine { String name = ReadString(input); BoneData parent = null; int parentIndex = ReadInt(input, true) - 1; - if (parentIndex != -1) parent = skeletonData.bones[parentIndex]; + if (parentIndex != -1) parent = skeletonData.bones.Items[parentIndex]; BoneData boneData = new BoneData(name, parent); boneData.x = ReadFloat(input) * scale; boneData.y = ReadFloat(input) * scale; @@ -139,8 +139,8 @@ namespace Spine { for (int i = 0, n = ReadInt(input, true); i < n; i++) { IkConstraintData ikConstraintData = new IkConstraintData(ReadString(input)); for (int ii = 0, nn = ReadInt(input, true); ii < nn; ii++) - ikConstraintData.bones.Add(skeletonData.bones[ReadInt(input, true)]); - ikConstraintData.target = skeletonData.bones[ReadInt(input, true)]; + ikConstraintData.bones.Add(skeletonData.bones.Items[ReadInt(input, true)]); + ikConstraintData.target = skeletonData.bones.Items[ReadInt(input, true)]; ikConstraintData.mix = ReadFloat(input); ikConstraintData.bendDirection = ReadSByte(input); skeletonData.ikConstraints.Add(ikConstraintData); @@ -149,7 +149,7 @@ namespace Spine { // Slots. for (int i = 0, n = ReadInt(input, true); i < n; i++) { String slotName = ReadString(input); - BoneData boneData = skeletonData.bones[ReadInt(input, true)]; + BoneData boneData = skeletonData.bones.Items[ReadInt(input, true)]; SlotData slotData = new SlotData(slotName, boneData); int color = ReadInt(input); slotData.r = ((color & 0xff000000) >> 24) / 255f; @@ -340,7 +340,7 @@ namespace Spine { } private void ReadAnimation (String name, Stream input, SkeletonData skeletonData) { - var timelines = new List(); + var timelines = new ExposedList(); float scale = Scale; float duration = 0; @@ -436,7 +436,7 @@ namespace Spine { // IK timelines. for (int i = 0, n = ReadInt(input, true); i < n; i++) { - IkConstraintData ikConstraint = skeletonData.ikConstraints[ReadInt(input, true)]; + IkConstraintData ikConstraint = skeletonData.ikConstraints.Items[ReadInt(input, true)]; int frameCount = ReadInt(input, true); IkConstraintTimeline timeline = new IkConstraintTimeline(frameCount); timeline.ikConstraintIndex = skeletonData.ikConstraints.IndexOf(ikConstraint); @@ -450,7 +450,7 @@ namespace Spine { // FFD timelines. for (int i = 0, n = ReadInt(input, true); i < n; i++) { - Skin skin = skeletonData.skins[ReadInt(input, true)]; + Skin skin = skeletonData.skins.Items[ReadInt(input, true)]; for (int ii = 0, nn = ReadInt(input, true); ii < nn; ii++) { int slotIndex = ReadInt(input, true); for (int iii = 0, nnn = ReadInt(input, true); iii < nnn; iii++) { @@ -540,7 +540,7 @@ namespace Spine { EventTimeline timeline = new EventTimeline(eventCount); for (int i = 0; i < eventCount; i++) { float time = ReadFloat(input); - EventData eventData = skeletonData.events[ReadInt(input, true)]; + EventData eventData = skeletonData.events.Items[ReadInt(input, true)]; Event e = new Event(eventData); e.Int = ReadInt(input, false); e.Float = ReadFloat(input); diff --git a/spine-csharp/src/SkeletonBounds.cs b/spine-csharp/src/SkeletonBounds.cs index 9e93ee90e..11d875a68 100644 --- a/spine-csharp/src/SkeletonBounds.cs +++ b/spine-csharp/src/SkeletonBounds.cs @@ -34,11 +34,11 @@ using System.Collections.Generic; namespace Spine { public class SkeletonBounds { - private List polygonPool = new List(); + private ExposedList polygonPool = new ExposedList(); private float minX, minY, maxX, maxY; - public List BoundingBoxes { get; private set; } - public List Polygons { get; private set; } + public ExposedList BoundingBoxes { get; private set; } + public ExposedList Polygons { get; private set; } public float MinX { get { return minX; } set { minX = value; } } public float MinY { get { return minY; } set { minY = value; } } public float MaxX { get { return maxX; } set { maxX = value; } } @@ -47,23 +47,23 @@ namespace Spine { public float Height { get { return maxY - minY; } } public SkeletonBounds () { - BoundingBoxes = new List(); - Polygons = new List(); + BoundingBoxes = new ExposedList(); + Polygons = new ExposedList(); } public void Update (Skeleton skeleton, bool updateAabb) { - List boundingBoxes = BoundingBoxes; - List polygons = Polygons; - List slots = skeleton.slots; + ExposedList boundingBoxes = BoundingBoxes; + ExposedList polygons = Polygons; + ExposedList slots = skeleton.slots; int slotCount = slots.Count; boundingBoxes.Clear(); - foreach (Polygon polygon in polygons) - polygonPool.Add(polygon); + for (int i = 0, n = polygons.Count; i < n; i++) + polygonPool.Add(polygons.Items[i]); polygons.Clear(); for (int i = 0; i < slotCount; i++) { - Slot slot = slots[i]; + Slot slot = slots.Items[i]; BoundingBoxAttachment boundingBox = slot.attachment as BoundingBoxAttachment; if (boundingBox == null) continue; boundingBoxes.Add(boundingBox); @@ -71,7 +71,7 @@ namespace Spine { Polygon polygon = null; int poolCount = polygonPool.Count; if (poolCount > 0) { - polygon = polygonPool[poolCount - 1]; + polygon = polygonPool.Items[poolCount - 1]; polygonPool.RemoveAt(poolCount - 1); } else polygon = new Polygon(); @@ -88,9 +88,9 @@ namespace Spine { private void aabbCompute () { float minX = int.MaxValue, minY = int.MaxValue, maxX = int.MinValue, maxY = int.MinValue; - List polygons = Polygons; + ExposedList polygons = Polygons; for (int i = 0, n = polygons.Count; i < n; i++) { - Polygon polygon = polygons[i]; + Polygon polygon = polygons.Items[i]; float[] vertices = polygon.Vertices; for (int ii = 0, nn = polygon.Count; ii < nn; ii += 2) { float x = vertices[ii]; @@ -160,18 +160,18 @@ namespace Spine { /// Returns the first bounding box attachment that contains the point, or null. When doing many checks, it is usually more /// efficient to only call this method if {@link #aabbContainsPoint(float, float)} returns true. public BoundingBoxAttachment ContainsPoint (float x, float y) { - List polygons = Polygons; + ExposedList polygons = Polygons; for (int i = 0, n = polygons.Count; i < n; i++) - if (ContainsPoint(polygons[i], x, y)) return BoundingBoxes[i]; + if (ContainsPoint(polygons.Items[i], x, y)) return BoundingBoxes.Items[i]; return null; } /// Returns the first bounding box attachment that contains the line segment, or null. When doing many checks, it is usually /// more efficient to only call this method if {@link #aabbIntersectsSegment(float, float, float, float)} returns true. public BoundingBoxAttachment IntersectsSegment (float x1, float y1, float x2, float y2) { - List polygons = Polygons; + ExposedList polygons = Polygons; for (int i = 0, n = polygons.Count; i < n; i++) - if (IntersectsSegment(polygons[i], x1, y1, x2, y2)) return BoundingBoxes[i]; + if (IntersectsSegment(polygons.Items[i], x1, y1, x2, y2)) return BoundingBoxes.Items[i]; return null; } @@ -201,7 +201,7 @@ namespace Spine { public Polygon getPolygon (BoundingBoxAttachment attachment) { int index = BoundingBoxes.IndexOf(attachment); - return index == -1 ? null : Polygons[index]; + return index == -1 ? null : Polygons.Items[index]; } } diff --git a/spine-csharp/src/SkeletonData.cs b/spine-csharp/src/SkeletonData.cs index 03b47051e..5976a24ad 100644 --- a/spine-csharp/src/SkeletonData.cs +++ b/spine-csharp/src/SkeletonData.cs @@ -35,25 +35,25 @@ using System.Collections.Generic; namespace Spine { public class SkeletonData { internal String name; - internal List bones = new List(); - internal List slots = new List(); - internal List skins = new List(); + internal ExposedList bones = new ExposedList(); + internal ExposedList slots = new ExposedList(); + internal ExposedList skins = new ExposedList(); internal Skin defaultSkin; - internal List events = new List(); - internal List animations = new List(); - internal List ikConstraints = new List(); + internal ExposedList events = new ExposedList(); + internal ExposedList animations = new ExposedList(); + internal ExposedList ikConstraints = new ExposedList(); internal float width, height; internal String version, hash, imagesPath; public String Name { get { return name; } set { name = value; } } - public List Bones { get { return bones; } } // Ordered parents first. - public List Slots { get { return slots; } } // Setup pose draw order. - public List Skins { get { return skins; } set { skins = value; } } + public ExposedList Bones { get { return bones; } } // Ordered parents first. + public ExposedList Slots { get { return slots; } } // Setup pose draw order. + public ExposedList Skins { get { return skins; } set { skins = value; } } /// May be null. public Skin DefaultSkin { get { return defaultSkin; } set { defaultSkin = value; } } - public List Events { get { return events; } set { events = value; } } - public List Animations { get { return animations; } set { animations = value; } } - public List IkConstraints { get { return ikConstraints; } set { ikConstraints = value; } } + public ExposedList Events { get { return events; } set { events = value; } } + public ExposedList Animations { get { return animations; } set { animations = value; } } + public ExposedList IkConstraints { get { return ikConstraints; } set { ikConstraints = value; } } public float Width { get { return width; } set { width = value; } } public float Height { get { return height; } set { height = value; } } /// The Spine version used to export this data. @@ -65,9 +65,9 @@ namespace Spine { /// May be null. public BoneData FindBone (String boneName) { if (boneName == null) throw new ArgumentNullException("boneName cannot be null."); - List bones = this.bones; + ExposedList bones = this.bones; for (int i = 0, n = bones.Count; i < n; i++) { - BoneData bone = bones[i]; + BoneData bone = bones.Items[i]; if (bone.name == boneName) return bone; } return null; @@ -76,9 +76,9 @@ namespace Spine { /// -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; + ExposedList bones = this.bones; for (int i = 0, n = bones.Count; i < n; i++) - if (bones[i].name == boneName) return i; + if (bones.Items[i].name == boneName) return i; return -1; } @@ -87,9 +87,9 @@ namespace Spine { /// May be null. public SlotData FindSlot (String slotName) { if (slotName == null) throw new ArgumentNullException("slotName cannot be null."); - List slots = this.slots; + ExposedList slots = this.slots; for (int i = 0, n = slots.Count; i < n; i++) { - SlotData slot = slots[i]; + SlotData slot = slots.Items[i]; if (slot.name == slotName) return slot; } return null; @@ -98,9 +98,9 @@ namespace Spine { /// -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; + ExposedList slots = this.slots; for (int i = 0, n = slots.Count; i < n; i++) - if (slots[i].name == slotName) return i; + if (slots.Items[i].name == slotName) return i; return -1; } @@ -129,9 +129,9 @@ namespace Spine { /// May be null. public Animation FindAnimation (String animationName) { if (animationName == null) throw new ArgumentNullException("animationName cannot be null."); - List animations = this.animations; + ExposedList animations = this.animations; for (int i = 0, n = animations.Count; i < n; i++) { - Animation animation = animations[i]; + Animation animation = animations.Items[i]; if (animation.name == animationName) return animation; } return null; @@ -142,9 +142,9 @@ namespace Spine { /// May be null. public IkConstraintData FindIkConstraint (String ikConstraintName) { if (ikConstraintName == null) throw new ArgumentNullException("ikConstraintName cannot be null."); - List ikConstraints = this.ikConstraints; + ExposedList ikConstraints = this.ikConstraints; for (int i = 0, n = ikConstraints.Count; i < n; i++) { - IkConstraintData ikConstraint = ikConstraints[i]; + IkConstraintData ikConstraint = ikConstraints.Items[i]; if (ikConstraint.name == ikConstraintName) return ikConstraint; } return null; diff --git a/spine-csharp/src/SkeletonJson.cs b/spine-csharp/src/SkeletonJson.cs index 2210a8d76..bf31c0441 100644 --- a/spine-csharp/src/SkeletonJson.cs +++ b/spine-csharp/src/SkeletonJson.cs @@ -380,7 +380,7 @@ namespace Spine { } private void ReadAnimation (String name, Dictionary map, SkeletonData skeletonData) { - var timelines = new List(); + var timelines = new ExposedList(); float duration = 0; float scale = Scale; diff --git a/spine-csharp/src/Skin.cs b/spine-csharp/src/Skin.cs index d3feee416..4b8408334 100644 --- a/spine-csharp/src/Skin.cs +++ b/spine-csharp/src/Skin.cs @@ -36,8 +36,8 @@ namespace Spine { /// Stores attachments by slot index and attachment name. public class Skin { internal String name; - private Dictionary, Attachment> attachments = - new Dictionary, Attachment>(AttachmentComparer.Instance); + private Dictionary attachments = + new Dictionary(AttachmentKeyTupleComparer.Instance); public String Name { get { return name; } } @@ -48,26 +48,26 @@ namespace Spine { public void AddAttachment (int slotIndex, String name, Attachment attachment) { if (attachment == null) throw new ArgumentNullException("attachment cannot be null."); - attachments[new KeyValuePair(slotIndex, name)] = attachment; + attachments[new AttachmentKeyTuple(slotIndex, name)] = attachment; } /// May be null. public Attachment GetAttachment (int slotIndex, String name) { Attachment attachment; - attachments.TryGetValue(new KeyValuePair(slotIndex, name), out attachment); + attachments.TryGetValue(new AttachmentKeyTuple(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); + foreach (AttachmentKeyTuple key in attachments.Keys) + if (key.SlotIndex == slotIndex) names.Add(key.Name); } 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); + foreach (KeyValuePair entry in this.attachments) + if (entry.Key.SlotIndex == slotIndex) attachments.Add(entry.Value); } override public String ToString () { @@ -76,27 +76,39 @@ namespace Spine { /// 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]; + foreach (KeyValuePair entry in oldSkin.attachments) { + int slotIndex = entry.Key.SlotIndex; + Slot slot = skeleton.slots.Items[slotIndex]; if (slot.attachment == entry.Value) { - Attachment attachment = GetAttachment(slotIndex, entry.Key.Value); + Attachment attachment = GetAttachment(slotIndex, entry.Key.Name); if (attachment != null) slot.Attachment = attachment; } } } // Avoids boxing in the dictionary. - private class AttachmentComparer : IEqualityComparer> { - internal static readonly AttachmentComparer Instance = new AttachmentComparer(); + private class AttachmentKeyTupleComparer : IEqualityComparer { + internal static readonly AttachmentKeyTupleComparer Instance = new AttachmentKeyTupleComparer(); - bool IEqualityComparer>.Equals (KeyValuePair o1, KeyValuePair o2) { - return o1.Key == o2.Key && o1.Value == o2.Value; + bool IEqualityComparer.Equals (AttachmentKeyTuple o1, AttachmentKeyTuple o2) { + return o1.SlotIndex == o2.SlotIndex && o1.NameHashCode == o2.NameHashCode && o1.Name == o2.Name; } - int IEqualityComparer>.GetHashCode (KeyValuePair o) { - return o.Key; + int IEqualityComparer.GetHashCode (AttachmentKeyTuple o) { + return o.SlotIndex; } } + + private class AttachmentKeyTuple { + public readonly int SlotIndex; + public readonly string Name; + public readonly int NameHashCode; + + public AttachmentKeyTuple(int slotIndex, string name) { + SlotIndex = slotIndex; + Name = name; + NameHashCode = Name.GetHashCode(); + } + } } } diff --git a/spine-unity/Assets/spine-unity/Editor/SkeletonAnimationInspector.cs b/spine-unity/Assets/spine-unity/Editor/SkeletonAnimationInspector.cs index f91a5d510..bd7bdecb1 100644 --- a/spine-unity/Assets/spine-unity/Editor/SkeletonAnimationInspector.cs +++ b/spine-unity/Assets/spine-unity/Editor/SkeletonAnimationInspector.cs @@ -77,7 +77,7 @@ public class SkeletonAnimationInspector : SkeletonRendererInspector { animations[0] = ""; int animationIndex = 0; for (int i = 0; i < animations.Length - 1; i++) { - String name = component.skeleton.Data.Animations[i].Name; + String name = component.skeleton.Data.Animations.Items[i].Name; animations[i + 1] = name; if (name == animationName.stringValue) animationIndex = i + 1; diff --git a/spine-unity/Assets/spine-unity/Editor/SkeletonBaker.cs b/spine-unity/Assets/spine-unity/Editor/SkeletonBaker.cs index bc656700b..4373a1a22 100644 --- a/spine-unity/Assets/spine-unity/Editor/SkeletonBaker.cs +++ b/spine-unity/Assets/spine-unity/Editor/SkeletonBaker.cs @@ -49,7 +49,7 @@ public static class SkeletonBaker { /// const float bakeIncrement = 1 / 60f; - public static void BakeToPrefab (SkeletonDataAsset skeletonDataAsset, List skins, string outputPath = "", bool bakeAnimations = true, bool bakeIK = true, SendMessageOptions eventOptions = SendMessageOptions.DontRequireReceiver) { + public static void BakeToPrefab (SkeletonDataAsset skeletonDataAsset, ExposedList skins, string outputPath = "", bool bakeAnimations = true, bool bakeIK = true, SendMessageOptions eventOptions = SendMessageOptions.DontRequireReceiver) { if (skeletonDataAsset == null || skeletonDataAsset.GetSkeletonData(true) == null) { Debug.LogError("Could not export Spine Skeleton because SkeletonDataAsset is null or invalid!"); return; @@ -108,7 +108,7 @@ public static class SkeletonBaker { for (int s = 0; s < skeletonData.Slots.Count; s++) { List attachmentNames = new List(); for (int i = 0; i < skinCount; i++) { - var skin = skins[i]; + var skin = skins.Items[i]; List temp = new List(); skin.FindNamesForSlot(s, temp); foreach (string str in temp) { @@ -189,7 +189,7 @@ public static class SkeletonBaker { //create bones for (int i = 0; i < skeletonData.Bones.Count; i++) { - var boneData = skeletonData.Bones[i]; + var boneData = skeletonData.Bones.Items[i]; Transform boneTransform = new GameObject(boneData.Name).transform; boneTransform.parent = prefabRoot.transform; boneTable.Add(boneTransform.name, boneTransform); @@ -198,7 +198,7 @@ public static class SkeletonBaker { for (int i = 0; i < skeletonData.Bones.Count; i++) { - var boneData = skeletonData.Bones[i]; + var boneData = skeletonData.Bones.Items[i]; Transform boneTransform = boneTable[boneData.Name]; Transform parentTransform = null; if (i > 0) @@ -219,7 +219,7 @@ public static class SkeletonBaker { //create slots and attachments for (int i = 0; i < skeletonData.Slots.Count; i++) { - var slotData = skeletonData.Slots[i]; + var slotData = skeletonData.Slots.Items[i]; Transform slotTransform = new GameObject(slotData.Name).transform; slotTransform.parent = prefabRoot.transform; slotTable.Add(slotData.Name, slotTransform); @@ -615,7 +615,7 @@ public static class SkeletonBaker { skeleton.UpdateWorldTransform(); float[] floatVerts = new float[attachment.UVs.Length]; - attachment.ComputeWorldVertices(skeleton.Slots[slotIndex], floatVerts); + attachment.ComputeWorldVertices(skeleton.Slots.Items[slotIndex], floatVerts); Vector2[] uvs = ExtractUV(attachment.UVs); Vector3[] verts = ExtractVerts(floatVerts); @@ -887,8 +887,8 @@ public static class SkeletonBaker { static void ParseAttachmentTimeline (Skeleton skeleton, AttachmentTimeline timeline, Dictionary> slotLookup, AnimationClip clip) { var attachmentNames = slotLookup[timeline.SlotIndex]; - string bonePath = GetPath(skeleton.Slots[timeline.SlotIndex].Bone.Data); - string slotPath = bonePath + "/" + skeleton.Slots[timeline.SlotIndex].Data.Name; + string bonePath = GetPath(skeleton.Slots.Items[timeline.SlotIndex].Bone.Data); + string slotPath = bonePath + "/" + skeleton.Slots.Items[timeline.SlotIndex].Data.Name; Dictionary curveTable = new Dictionary(); @@ -899,7 +899,7 @@ public static class SkeletonBaker { float[] frames = timeline.Frames; if (frames[0] != 0) { - string startingName = skeleton.Slots[timeline.SlotIndex].Data.AttachmentName; + string startingName = skeleton.Slots.Items[timeline.SlotIndex].Data.AttachmentName; foreach (var pair in curveTable) { if (startingName == "" || startingName == null) { pair.Value.AddKey(new Keyframe(0, 0, float.PositiveInfinity, float.PositiveInfinity)); @@ -1037,8 +1037,8 @@ public static class SkeletonBaker { } static void ParseTranslateTimeline (Skeleton skeleton, TranslateTimeline timeline, AnimationClip clip) { - var boneData = skeleton.Data.Bones[timeline.BoneIndex]; - var bone = skeleton.Bones[timeline.BoneIndex]; + var boneData = skeleton.Data.Bones.Items[timeline.BoneIndex]; + var bone = skeleton.Bones.Items[timeline.BoneIndex]; AnimationCurve xCurve = new AnimationCurve(); AnimationCurve yCurve = new AnimationCurve(); @@ -1183,8 +1183,8 @@ public static class SkeletonBaker { } static void ParseScaleTimeline (Skeleton skeleton, ScaleTimeline timeline, AnimationClip clip) { - var boneData = skeleton.Data.Bones[timeline.BoneIndex]; - var bone = skeleton.Bones[timeline.BoneIndex]; + var boneData = skeleton.Data.Bones.Items[timeline.BoneIndex]; + var bone = skeleton.Bones.Items[timeline.BoneIndex]; AnimationCurve xCurve = new AnimationCurve(); AnimationCurve yCurve = new AnimationCurve(); @@ -1316,8 +1316,8 @@ public static class SkeletonBaker { } static void ParseRotateTimeline (Skeleton skeleton, RotateTimeline timeline, AnimationClip clip) { - var boneData = skeleton.Data.Bones[timeline.BoneIndex]; - var bone = skeleton.Bones[timeline.BoneIndex]; + var boneData = skeleton.Data.Bones.Items[timeline.BoneIndex]; + var bone = skeleton.Bones.Items[timeline.BoneIndex]; AnimationCurve curve = new AnimationCurve(); diff --git a/spine-unity/Assets/spine-unity/Editor/SkeletonDataAssetInspector.cs b/spine-unity/Assets/spine-unity/Editor/SkeletonDataAssetInspector.cs index aa204943e..5fc35fe02 100644 --- a/spine-unity/Assets/spine-unity/Editor/SkeletonDataAssetInspector.cs +++ b/spine-unity/Assets/spine-unity/Editor/SkeletonDataAssetInspector.cs @@ -186,7 +186,7 @@ public class SkeletonDataAssetInspector : Editor { Skin bakeSkin = m_skeletonAnimation.skeleton.Skin; if (bakeSkin == null) { skinName = "Default"; - bakeSkin = m_skeletonData.Skins[0]; + bakeSkin = m_skeletonData.Skins.Items[0]; } else skinName = m_skeletonAnimation.skeleton.Skin.Name; @@ -195,7 +195,7 @@ public class SkeletonDataAssetInspector : Editor { try { GUILayout.BeginVertical(); if (GUILayout.Button(new GUIContent("Bake " + skinName, SpineEditorUtilities.Icons.unityIcon), GUILayout.Height(32), GUILayout.Width(250))) - SkeletonBaker.BakeToPrefab(m_skeletonDataAsset, new List(new Skin[] { bakeSkin }), "", bakeAnimations, bakeIK, bakeEventOptions); + SkeletonBaker.BakeToPrefab(m_skeletonDataAsset, new ExposedList(new Skin[] { bakeSkin }), "", bakeAnimations, bakeIK, bakeEventOptions); GUILayout.BeginHorizontal(); GUILayout.Label(new GUIContent("Skins", SpineEditorUtilities.Icons.skinsRoot), GUILayout.Width(50)); @@ -259,7 +259,7 @@ public class SkeletonDataAssetInspector : Editor { // Animation names String[] animations = new String[m_skeletonData.Animations.Count]; for (int i = 0; i < animations.Length; i++) - animations[i] = m_skeletonData.Animations[i].Name; + animations[i] = m_skeletonData.Animations.Items[i].Name; for (int i = 0; i < fromAnimation.arraySize; i++) { SerializedProperty from = fromAnimation.GetArrayElementAtIndex(i); @@ -350,14 +350,14 @@ public class SkeletonDataAssetInspector : Editor { List slotAttachments = new List(); List slotAttachmentNames = new List(); List defaultSkinAttachmentNames = new List(); - var defaultSkin = m_skeletonData.Skins[0]; + var defaultSkin = m_skeletonData.Skins.Items[0]; Skin skin = m_skeletonAnimation.skeleton.Skin; if (skin == null) { skin = defaultSkin; } for (int i = m_skeletonAnimation.skeleton.Slots.Count - 1; i >= 0; i--) { - Slot slot = m_skeletonAnimation.skeleton.Slots[i]; + Slot slot = m_skeletonAnimation.skeleton.Slots.Items[i]; EditorGUILayout.LabelField(new GUIContent(slot.Data.Name, SpineEditorUtilities.Icons.slot)); if (showAttachments) { diff --git a/spine-unity/Assets/spine-unity/Editor/SkeletonRendererInspector.cs b/spine-unity/Assets/spine-unity/Editor/SkeletonRendererInspector.cs index 117c8ec14..267a4e4d0 100644 --- a/spine-unity/Assets/spine-unity/Editor/SkeletonRendererInspector.cs +++ b/spine-unity/Assets/spine-unity/Editor/SkeletonRendererInspector.cs @@ -92,7 +92,7 @@ public class SkeletonRendererInspector : Editor { String[] skins = new String[component.skeleton.Data.Skins.Count]; int skinIndex = 0; for (int i = 0; i < skins.Length; i++) { - String name = component.skeleton.Data.Skins[i].Name; + String name = component.skeleton.Data.Skins.Items[i].Name; skins[i] = name; if (name == initialSkinName.stringValue) skinIndex = i; diff --git a/spine-unity/Assets/spine-unity/Editor/SpineAttributeDrawers.cs b/spine-unity/Assets/spine-unity/Editor/SpineAttributeDrawers.cs index fbf51dcf8..17810e6e0 100644 --- a/spine-unity/Assets/spine-unity/Editor/SpineAttributeDrawers.cs +++ b/spine-unity/Assets/spine-unity/Editor/SpineAttributeDrawers.cs @@ -85,7 +85,7 @@ public class SpineSlotDrawer : PropertyDrawer { menu.AddSeparator(""); for (int i = 0; i < data.Slots.Count; i++) { - string name = data.Slots[i].Name; + string name = data.Slots.Items[i].Name; if (name.StartsWith(attrib.startsWith)) { if (attrib.containsBoundingBoxes) { @@ -190,7 +190,7 @@ public class SpineSkinDrawer : PropertyDrawer { menu.AddSeparator(""); for (int i = 0; i < data.Skins.Count; i++) { - string name = data.Skins[i].Name; + string name = data.Skins.Items[i].Name; if (name.StartsWith(attrib.startsWith)) menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property)); } @@ -330,7 +330,7 @@ public class SpineAnimationDrawer : PropertyDrawer { var animations = skeletonDataAsset.GetAnimationStateData().SkeletonData.Animations; for (int i = 0; i < animations.Count; i++) { - string name = animations[i].Name; + string name = animations.Items[i].Name; if (name.StartsWith(attrib.startsWith)) menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property)); } @@ -416,7 +416,7 @@ public class SpineAttachmentDrawer : PropertyDrawer { if (skeletonRenderer.skeleton.Skin != null) { validSkins.Add(skeletonRenderer.skeleton.Skin); } else { - validSkins.Add(data.Skins[0]); + validSkins.Add(data.Skins.Items[0]); } } else { foreach (Skin skin in data.Skins) { @@ -440,7 +440,7 @@ public class SpineAttachmentDrawer : PropertyDrawer { menu.AddItem(new GUIContent("Null"), property.stringValue == "", HandleSelect, new SpineDrawerValuePair("", property)); menu.AddSeparator(""); - Skin defaultSkin = data.Skins[0]; + Skin defaultSkin = data.Skins.Items[0]; SerializedProperty slotProperty = property.serializedObject.FindProperty(attrib.slotField); string slotMatch = ""; @@ -457,7 +457,7 @@ public class SpineAttachmentDrawer : PropertyDrawer { prefix = skinPrefix; for (int i = 0; i < data.Slots.Count; i++) { - if (slotMatch.Length > 0 && data.Slots[i].Name.ToLower().Contains(slotMatch) == false) + if (slotMatch.Length > 0 && data.Slots.Items[i].Name.ToLower().Contains(slotMatch) == false) continue; attachmentNames.Clear(); @@ -473,11 +473,11 @@ public class SpineAttachmentDrawer : PropertyDrawer { for (int a = 0; a < attachmentNames.Count; a++) { string attachmentPath = attachmentNames[a]; - string menuPath = prefix + data.Slots[i].Name + "/" + attachmentPath; + string menuPath = prefix + data.Slots.Items[i].Name + "/" + attachmentPath; string name = attachmentNames[a]; if (attrib.returnAttachmentPath) - name = skin.Name + "/" + data.Slots[i].Name + "/" + attachmentPath; + name = skin.Name + "/" + data.Slots.Items[i].Name + "/" + attachmentPath; if (attrib.placeholdersOnly && placeholderNames.Contains(attachmentPath) == false) { menu.AddDisabledItem(new GUIContent(menuPath)); @@ -564,7 +564,7 @@ public class SpineBoneDrawer : PropertyDrawer { menu.AddSeparator(""); for (int i = 0; i < data.Bones.Count; i++) { - string name = data.Bones[i].Name; + string name = data.Bones.Items[i].Name; if (name.StartsWith(attrib.startsWith)) menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property)); } diff --git a/spine-unity/Assets/spine-unity/Editor/SpineEditorUtilities.cs b/spine-unity/Assets/spine-unity/Editor/SpineEditorUtilities.cs index c70c30d83..19991c208 100644 --- a/spine-unity/Assets/spine-unity/Editor/SpineEditorUtilities.cs +++ b/spine-unity/Assets/spine-unity/Editor/SpineEditorUtilities.cs @@ -949,7 +949,7 @@ public class SpineEditorUtilities : AssetPostprocessor { skin = data.DefaultSkin; if (skin == null) - skin = data.Skins[0]; + skin = data.Skins.Items[0]; anim.Reset(); @@ -1035,7 +1035,7 @@ public class SpineEditorUtilities : AssetPostprocessor { skin = data.DefaultSkin; if (skin == null) - skin = data.Skins[0]; + skin = data.Skins.Items[0]; anim.Reset(); diff --git a/spine-unity/Assets/spine-unity/Ragdoll/SkeletonRagdoll2D.cs b/spine-unity/Assets/spine-unity/Ragdoll/SkeletonRagdoll2D.cs index 690e10dd2..962acf543 100644 --- a/spine-unity/Assets/spine-unity/Ragdoll/SkeletonRagdoll2D.cs +++ b/spine-unity/Assets/spine-unity/Ragdoll/SkeletonRagdoll2D.cs @@ -280,7 +280,7 @@ public class SkeletonRagdoll2D : MonoBehaviour { if (colliders.Count == 0) { var box = go.AddComponent(); box.size = new Vector2(length, thickness); -#if UNITY_5_0 +#if UNITY_5 box.offset = new Vector2((b.WorldFlipX ? -length : length) / 2, 0); #else box.center = new Vector2((b.WorldFlipX ? -length : length) / 2, 0); diff --git a/spine-unity/Assets/spine-unity/SkeletonAnimator.cs b/spine-unity/Assets/spine-unity/SkeletonAnimator.cs index e9abf894a..7994a661f 100644 --- a/spine-unity/Assets/spine-unity/SkeletonAnimator.cs +++ b/spine-unity/Assets/spine-unity/SkeletonAnimator.cs @@ -44,7 +44,8 @@ public class SkeletonAnimator : SkeletonRenderer, ISkeletonAnimation { } } - Dictionary animationTable = new Dictionary(); + Dictionary animationTable = new Dictionary(); + Dictionary clipNameHashCodeTable = new Dictionary(); Animator animator; float lastTime; @@ -54,11 +55,12 @@ public class SkeletonAnimator : SkeletonRenderer, ISkeletonAnimation { return; animationTable.Clear(); + clipNameHashCodeTable.Clear(); var data = skeletonDataAsset.GetSkeletonData(true); foreach (var a in data.Animations) { - animationTable.Add(a.Name, a); + animationTable.Add(a.Name.GetHashCode(), a); } animator = GetComponent(); @@ -106,7 +108,7 @@ public class SkeletonAnimator : SkeletonRenderer, ISkeletonAnimation { continue; float time = stateInfo.normalizedTime * info.clip.length; - animationTable[info.clip.name].Mix(skeleton, Mathf.Max(0, time - deltaTime), time, stateInfo.loop, null, weight); + animationTable[GetAnimationClipNameHashCode(info.clip)].Mix(skeleton, Mathf.Max(0, time - deltaTime), time, stateInfo.loop, null, weight); } foreach (var info in nextClipInfo) { @@ -115,7 +117,7 @@ public class SkeletonAnimator : SkeletonRenderer, ISkeletonAnimation { continue; float time = nextStateInfo.normalizedTime * info.clip.length; - animationTable[info.clip.name].Mix(skeleton, Mathf.Max(0, time - deltaTime), time, nextStateInfo.loop, null, weight); + animationTable[GetAnimationClipNameHashCode(info.clip)].Mix(skeleton, Mathf.Max(0, time - deltaTime), time, nextStateInfo.loop, null, weight); } } else if (mode >= MixMode.MixNext) { //apply first non-zero weighted clip @@ -128,7 +130,7 @@ public class SkeletonAnimator : SkeletonRenderer, ISkeletonAnimation { continue; float time = stateInfo.normalizedTime * info.clip.length; - animationTable[info.clip.name].Apply(skeleton, Mathf.Max(0, time - deltaTime), time, stateInfo.loop, null); + animationTable[GetAnimationClipNameHashCode(info.clip)].Apply(skeleton, Mathf.Max(0, time - deltaTime), time, stateInfo.loop, null); break; } @@ -140,7 +142,7 @@ public class SkeletonAnimator : SkeletonRenderer, ISkeletonAnimation { continue; float time = stateInfo.normalizedTime * info.clip.length; - animationTable[info.clip.name].Mix(skeleton, Mathf.Max(0, time - deltaTime), time, stateInfo.loop, null, weight); + animationTable[GetAnimationClipNameHashCode(info.clip)].Mix(skeleton, Mathf.Max(0, time - deltaTime), time, stateInfo.loop, null, weight); } c = 0; @@ -154,7 +156,7 @@ public class SkeletonAnimator : SkeletonRenderer, ISkeletonAnimation { continue; float time = nextStateInfo.normalizedTime * info.clip.length; - animationTable[info.clip.name].Apply(skeleton, Mathf.Max(0, time - deltaTime), time, nextStateInfo.loop, null); + animationTable[GetAnimationClipNameHashCode(info.clip)].Apply(skeleton, Mathf.Max(0, time - deltaTime), time, nextStateInfo.loop, null); break; } } @@ -167,7 +169,7 @@ public class SkeletonAnimator : SkeletonRenderer, ISkeletonAnimation { continue; float time = nextStateInfo.normalizedTime * info.clip.length; - animationTable[info.clip.name].Mix(skeleton, Mathf.Max(0, time - deltaTime), time, nextStateInfo.loop, null, weight); + animationTable[GetAnimationClipNameHashCode(info.clip)].Mix(skeleton, Mathf.Max(0, time - deltaTime), time, nextStateInfo.loop, null, weight); } } } @@ -188,4 +190,14 @@ public class SkeletonAnimator : SkeletonRenderer, ISkeletonAnimation { lastTime = Time.time; } + + private int GetAnimationClipNameHashCode(AnimationClip clip) { + int clipNameHashCode; + if (!clipNameHashCodeTable.TryGetValue(clip, out clipNameHashCode)) { + clipNameHashCode = clip.name.GetHashCode(); + clipNameHashCodeTable.Add(clip, clipNameHashCode); + } + + return clipNameHashCode; + } } \ No newline at end of file diff --git a/spine-unity/Assets/spine-unity/SkeletonRenderer.cs b/spine-unity/Assets/spine-unity/SkeletonRenderer.cs index bd3dcef23..c544a9891 100644 --- a/spine-unity/Assets/spine-unity/SkeletonRenderer.cs +++ b/spine-unity/Assets/spine-unity/SkeletonRenderer.cs @@ -30,7 +30,6 @@ *****************************************************************************/ using System; -using System.IO; using System.Collections.Generic; using UnityEngine; using Spine; @@ -65,14 +64,14 @@ public class SkeletonRenderer : MonoBehaviour { private Mesh mesh1, mesh2; private bool useMesh1; private float[] tempVertices = new float[8]; - private int lastVertexCount; private Vector3[] vertices; private Color32[] colors; private Vector2[] uvs; private Material[] sharedMaterials = new Material[0]; - private readonly List submeshMaterials = new List(); - private readonly List submeshes = new List(); + private readonly ExposedList submeshMaterials = new ExposedList(); + private readonly ExposedList submeshes = new ExposedList(); private SkeletonUtilitySubmeshRenderer[] submeshRenderers; + private LastState lastState = new LastState(); public virtual void Reset () { if (meshFilter != null) @@ -95,9 +94,9 @@ public class SkeletonRenderer : MonoBehaviour { DestroyImmediate(mesh2); } + lastState = new LastState(); mesh1 = null; mesh2 = null; - lastVertexCount = 0; vertices = null; colors = null; uvs = null; @@ -119,6 +118,7 @@ public class SkeletonRenderer : MonoBehaviour { valid = true; meshFilter = GetComponent(); + meshRenderer = GetComponent(); mesh1 = newMesh(); mesh2 = newMesh(); vertices = new Vector3[0]; @@ -178,40 +178,67 @@ public class SkeletonRenderer : MonoBehaviour { public virtual void LateUpdate () { if (!valid) return; + + // Exit early if there is nothing to render + if (!meshRenderer.enabled && submeshRenderers.Length == 0) + return; + // Count vertices and submesh triangles. int vertexCount = 0; int submeshTriangleCount = 0, submeshFirstVertex = 0, submeshStartSlotIndex = 0; Material lastMaterial = null; - submeshMaterials.Clear(); - List drawOrder = skeleton.DrawOrder; + ExposedList drawOrder = skeleton.drawOrder; int drawOrderCount = drawOrder.Count; + int submeshSeparatorSlotsCount = submeshSeparatorSlots.Count; bool renderMeshes = this.renderMeshes; + + // Clear last state of attachments and submeshes + ExposedList attachmentsTriangleCountTemp = lastState.attachmentsTriangleCountTemp; + attachmentsTriangleCountTemp.GrowIfNeeded(drawOrderCount); + attachmentsTriangleCountTemp.Count = drawOrderCount; + ExposedList attachmentsFlipStateTemp = lastState.attachmentsFlipStateTemp; + attachmentsFlipStateTemp.GrowIfNeeded(drawOrderCount); + attachmentsFlipStateTemp.Count = drawOrderCount; + + ExposedList addSubmeshArgumentsTemp = lastState.addSubmeshArgumentsTemp; + addSubmeshArgumentsTemp.Clear(false); for (int i = 0; i < drawOrderCount; i++) { - Slot slot = drawOrder[i]; + Slot slot = drawOrder.Items[i]; + Bone bone = slot.bone; Attachment attachment = slot.attachment; object rendererObject; int attachmentVertexCount, attachmentTriangleCount; + bool worldScaleXIsPositive = bone.worldScaleX >= 0f; + bool worldScaleYIsPositive = bone.worldScaleY >= 0f; + bool worldScaleIsSameSigns = (worldScaleXIsPositive && worldScaleYIsPositive) || + (!worldScaleXIsPositive && !worldScaleYIsPositive); + bool flip = frontFacing && ((bone.worldFlipX != bone.worldFlipY) == worldScaleIsSameSigns); + attachmentsFlipStateTemp.Items[i] = flip; - if (attachment is RegionAttachment) { - rendererObject = ((RegionAttachment)attachment).RendererObject; + attachmentsTriangleCountTemp.Items[i] = -1; + RegionAttachment regionAttachment = attachment as RegionAttachment; + if (regionAttachment != null) { + rendererObject = regionAttachment.RendererObject; attachmentVertexCount = 4; attachmentTriangleCount = 6; } else { if (!renderMeshes) continue; - if (attachment is MeshAttachment) { - MeshAttachment meshAttachment = (MeshAttachment)attachment; + MeshAttachment meshAttachment = attachment as MeshAttachment; + if (meshAttachment != null) { rendererObject = meshAttachment.RendererObject; attachmentVertexCount = meshAttachment.vertices.Length >> 1; attachmentTriangleCount = meshAttachment.triangles.Length; - } else if (attachment is SkinnedMeshAttachment) { - SkinnedMeshAttachment meshAttachment = (SkinnedMeshAttachment)attachment; - rendererObject = meshAttachment.RendererObject; - attachmentVertexCount = meshAttachment.uvs.Length >> 1; - attachmentTriangleCount = meshAttachment.triangles.Length; - } else - continue; + } else { + SkinnedMeshAttachment skinnedMeshAttachment = attachment as SkinnedMeshAttachment; + if (skinnedMeshAttachment != null) { + rendererObject = skinnedMeshAttachment.RendererObject; + attachmentVertexCount = skinnedMeshAttachment.uvs.Length >> 1; + attachmentTriangleCount = skinnedMeshAttachment.triangles.Length; + } else + continue; + } } // Populate submesh when material changes. @@ -220,9 +247,11 @@ public class SkeletonRenderer : MonoBehaviour { #else Material material = (rendererObject.GetType() == typeof(Material)) ? (Material)rendererObject : (Material)((AtlasRegion)rendererObject).page.rendererObject; #endif - - if ((lastMaterial != material && lastMaterial != null) || submeshSeparatorSlots.Contains(slot)) { - AddSubmesh(lastMaterial, submeshStartSlotIndex, i, submeshTriangleCount, submeshFirstVertex, false); + if ((lastMaterial != null && lastMaterial.GetInstanceID() != material.GetInstanceID()) || + (submeshSeparatorSlotsCount > 0 && submeshSeparatorSlots.Contains(slot))) { + addSubmeshArgumentsTemp.Add( + new LastState.AddSubmeshArguments(lastMaterial, submeshStartSlotIndex, i, submeshTriangleCount, submeshFirstVertex, false) + ); submeshTriangleCount = 0; submeshFirstVertex = vertexCount; submeshStartSlotIndex = i; @@ -231,15 +260,37 @@ public class SkeletonRenderer : MonoBehaviour { submeshTriangleCount += attachmentTriangleCount; vertexCount += attachmentVertexCount; - } - AddSubmesh(lastMaterial, submeshStartSlotIndex, drawOrderCount, submeshTriangleCount, submeshFirstVertex, true); - // Set materials. - if (submeshMaterials.Count == sharedMaterials.Length) - submeshMaterials.CopyTo(sharedMaterials); - else - sharedMaterials = submeshMaterials.ToArray(); - meshRenderer.sharedMaterials = sharedMaterials; + attachmentsTriangleCountTemp.Items[i] = attachmentTriangleCount; + } + addSubmeshArgumentsTemp.Add( + new LastState.AddSubmeshArguments(lastMaterial, submeshStartSlotIndex, drawOrderCount, submeshTriangleCount, submeshFirstVertex, true) + ); + + bool mustUpdateMeshStructure = CheckIfMustUpdateMeshStructure(attachmentsTriangleCountTemp, attachmentsFlipStateTemp, addSubmeshArgumentsTemp); + if (mustUpdateMeshStructure) { + submeshMaterials.Clear(); + for (int i = 0, n = addSubmeshArgumentsTemp.Count; i < n; i++) { + LastState.AddSubmeshArguments arguments = addSubmeshArgumentsTemp.Items[i]; + AddSubmesh( + arguments.material, + arguments.startSlot, + arguments.endSlot, + arguments.triangleCount, + arguments.firstVertex, + arguments.lastSubmesh, + attachmentsFlipStateTemp + ); + } + + // Set materials. + if (submeshMaterials.Count == sharedMaterials.Length) + submeshMaterials.CopyTo(sharedMaterials); + else + sharedMaterials = submeshMaterials.ToArray(); + + meshRenderer.sharedMaterials = sharedMaterials; + } // Ensure mesh data is the right size. Vector3[] vertices = this.vertices; @@ -254,31 +305,48 @@ public class SkeletonRenderer : MonoBehaviour { } else { // Too many vertices, zero the extra. Vector3 zero = Vector3.zero; - for (int i = vertexCount, n = lastVertexCount; i < n; i++) + for (int i = vertexCount, n = lastState.vertexCount ; i < n; i++) vertices[i] = zero; } - lastVertexCount = vertexCount; + lastState.vertexCount = vertexCount; // Setup mesh. + float zSpacing = this.zSpacing; float[] tempVertices = this.tempVertices; Vector2[] uvs = this.uvs; Color32[] colors = this.colors; int vertexIndex = 0; - Color32 color = new Color32(); - float zSpacing = this.zSpacing; + Color32 color; float a = skeleton.a * 255, r = skeleton.r, g = skeleton.g, b = skeleton.b; + + Vector3 meshBoundsMin; + meshBoundsMin.x = float.MaxValue; + meshBoundsMin.y = float.MaxValue; + meshBoundsMin.z = zSpacing > 0f ? 0f : zSpacing * (drawOrderCount - 1); + Vector3 meshBoundsMax; + meshBoundsMax.x = float.MinValue; + meshBoundsMax.y = float.MinValue; + meshBoundsMax.z = zSpacing < 0f ? 0f : zSpacing * (drawOrderCount - 1); for (int i = 0; i < drawOrderCount; i++) { - Slot slot = drawOrder[i]; + Slot slot = drawOrder.Items[i]; Attachment attachment = slot.attachment; - if (attachment is RegionAttachment) { - RegionAttachment regionAttachment = (RegionAttachment)attachment; + RegionAttachment regionAttachment = attachment as RegionAttachment; + if (regionAttachment != null) { regionAttachment.ComputeWorldVertices(slot.bone, tempVertices); float z = i * zSpacing; - vertices[vertexIndex] = new Vector3(tempVertices[RegionAttachment.X1], tempVertices[RegionAttachment.Y1], z); - vertices[vertexIndex + 1] = new Vector3(tempVertices[RegionAttachment.X4], tempVertices[RegionAttachment.Y4], z); - vertices[vertexIndex + 2] = new Vector3(tempVertices[RegionAttachment.X2], tempVertices[RegionAttachment.Y2], z); - vertices[vertexIndex + 3] = new Vector3(tempVertices[RegionAttachment.X3], tempVertices[RegionAttachment.Y3], z); + vertices[vertexIndex].x = tempVertices[RegionAttachment.X1]; + vertices[vertexIndex].y = tempVertices[RegionAttachment.Y1]; + vertices[vertexIndex].z = z; + vertices[vertexIndex + 1].x = tempVertices[RegionAttachment.X4]; + vertices[vertexIndex + 1].y = tempVertices[RegionAttachment.Y4]; + vertices[vertexIndex + 1].z = z; + vertices[vertexIndex + 2].x = tempVertices[RegionAttachment.X2]; + vertices[vertexIndex + 2].y = tempVertices[RegionAttachment.Y2]; + vertices[vertexIndex + 2].z = z; + vertices[vertexIndex + 3].x = tempVertices[RegionAttachment.X3]; + vertices[vertexIndex + 3].y = tempVertices[RegionAttachment.Y3]; + vertices[vertexIndex + 3].z = z; color.a = (byte)(a * slot.a * regionAttachment.a); color.r = (byte)(r * slot.r * regionAttachment.r * color.a); @@ -291,17 +359,57 @@ public class SkeletonRenderer : MonoBehaviour { colors[vertexIndex + 3] = color; float[] regionUVs = regionAttachment.uvs; - uvs[vertexIndex] = new Vector2(regionUVs[RegionAttachment.X1], regionUVs[RegionAttachment.Y1]); - uvs[vertexIndex + 1] = new Vector2(regionUVs[RegionAttachment.X4], regionUVs[RegionAttachment.Y4]); - uvs[vertexIndex + 2] = new Vector2(regionUVs[RegionAttachment.X2], regionUVs[RegionAttachment.Y2]); - uvs[vertexIndex + 3] = new Vector2(regionUVs[RegionAttachment.X3], regionUVs[RegionAttachment.Y3]); + uvs[vertexIndex].x = regionUVs[RegionAttachment.X1]; + uvs[vertexIndex].y = regionUVs[RegionAttachment.Y1]; + uvs[vertexIndex + 1].x = regionUVs[RegionAttachment.X4]; + uvs[vertexIndex + 1].y = regionUVs[RegionAttachment.Y4]; + uvs[vertexIndex + 2].x = regionUVs[RegionAttachment.X2]; + uvs[vertexIndex + 2].y = regionUVs[RegionAttachment.Y2]; + uvs[vertexIndex + 3].x = regionUVs[RegionAttachment.X3]; + uvs[vertexIndex + 3].y = regionUVs[RegionAttachment.Y3]; + + // Calculate min/max X + if (tempVertices[RegionAttachment.X1] < meshBoundsMin.x) + meshBoundsMin.x = tempVertices[RegionAttachment.X1]; + else if (tempVertices[RegionAttachment.X1] > meshBoundsMax.x) + meshBoundsMax.x = tempVertices[RegionAttachment.X1]; + if (tempVertices[RegionAttachment.X2] < meshBoundsMin.x) + meshBoundsMin.x = tempVertices[RegionAttachment.X2]; + else if (tempVertices[RegionAttachment.X2] > meshBoundsMax.x) + meshBoundsMax.x = tempVertices[RegionAttachment.X2]; + if (tempVertices[RegionAttachment.X3] < meshBoundsMin.x) + meshBoundsMin.x = tempVertices[RegionAttachment.X3]; + else if (tempVertices[RegionAttachment.X3] > meshBoundsMax.x) + meshBoundsMax.x = tempVertices[RegionAttachment.X3]; + if (tempVertices[RegionAttachment.X4] < meshBoundsMin.x) + meshBoundsMin.x = tempVertices[RegionAttachment.X4]; + else if (tempVertices[RegionAttachment.X4] > meshBoundsMax.x) + meshBoundsMax.x = tempVertices[RegionAttachment.X4]; + + // Calculate min/max Y + if (tempVertices[RegionAttachment.Y1] < meshBoundsMin.y) + meshBoundsMin.y = tempVertices[RegionAttachment.Y1]; + else if (tempVertices[RegionAttachment.Y1] > meshBoundsMax.y) + meshBoundsMax.y = tempVertices[RegionAttachment.Y1]; + if (tempVertices[RegionAttachment.Y2] < meshBoundsMin.y) + meshBoundsMin.y = tempVertices[RegionAttachment.Y2]; + else if (tempVertices[RegionAttachment.Y2] > meshBoundsMax.y) + meshBoundsMax.y = tempVertices[RegionAttachment.Y2]; + if (tempVertices[RegionAttachment.Y3] < meshBoundsMin.y) + meshBoundsMin.y = tempVertices[RegionAttachment.Y3]; + else if (tempVertices[RegionAttachment.Y3] > meshBoundsMax.y) + meshBoundsMax.y = tempVertices[RegionAttachment.Y3]; + if (tempVertices[RegionAttachment.Y4] < meshBoundsMin.y) + meshBoundsMin.y = tempVertices[RegionAttachment.Y4]; + else if (tempVertices[RegionAttachment.Y4] > meshBoundsMax.y) + meshBoundsMax.y = tempVertices[RegionAttachment.Y4]; vertexIndex += 4; } else { if (!renderMeshes) continue; - if (attachment is MeshAttachment) { - MeshAttachment meshAttachment = (MeshAttachment)attachment; + MeshAttachment meshAttachment = attachment as MeshAttachment; + if (meshAttachment != null) { int meshVertexCount = meshAttachment.vertices.Length; if (tempVertices.Length < meshVertexCount) this.tempVertices = tempVertices = new float[meshVertexCount]; @@ -316,29 +424,55 @@ public class SkeletonRenderer : MonoBehaviour { float[] meshUVs = meshAttachment.uvs; float z = i * zSpacing; for (int ii = 0; ii < meshVertexCount; ii += 2, vertexIndex++) { - vertices[vertexIndex] = new Vector3(tempVertices[ii], tempVertices[ii + 1], z); + vertices[vertexIndex].x = tempVertices[ii]; + vertices[vertexIndex].y = tempVertices[ii + 1]; + vertices[vertexIndex].z = z; colors[vertexIndex] = color; - uvs[vertexIndex] = new Vector2(meshUVs[ii], meshUVs[ii + 1]); + uvs[vertexIndex].x = meshUVs[ii]; + uvs[vertexIndex].y = meshUVs[ii + 1]; + + if (tempVertices[ii] < meshBoundsMin.x) + meshBoundsMin.x = tempVertices[ii]; + else if (tempVertices[ii] > meshBoundsMax.x) + meshBoundsMax.x = tempVertices[ii]; + if (tempVertices[ii + 1]< meshBoundsMin.y) + meshBoundsMin.y = tempVertices[ii + 1]; + else if (tempVertices[ii + 1] > meshBoundsMax.y) + meshBoundsMax.y = tempVertices[ii + 1]; } - } else if (attachment is SkinnedMeshAttachment) { - SkinnedMeshAttachment meshAttachment = (SkinnedMeshAttachment)attachment; - int meshVertexCount = meshAttachment.uvs.Length; - if (tempVertices.Length < meshVertexCount) - this.tempVertices = tempVertices = new float[meshVertexCount]; - meshAttachment.ComputeWorldVertices(slot, tempVertices); + } else { + SkinnedMeshAttachment skinnedMeshAttachment = attachment as SkinnedMeshAttachment; + if (skinnedMeshAttachment != null) { + int meshVertexCount = skinnedMeshAttachment.uvs.Length; + if (tempVertices.Length < meshVertexCount) + this.tempVertices = tempVertices = new float[meshVertexCount]; + skinnedMeshAttachment.ComputeWorldVertices(slot, tempVertices); - color.a = (byte)(a * slot.a * meshAttachment.a); - color.r = (byte)(r * slot.r * meshAttachment.r * color.a); - color.g = (byte)(g * slot.g * meshAttachment.g * color.a); - color.b = (byte)(b * slot.b * meshAttachment.b * color.a); - if (slot.data.blendMode == BlendMode.additive) color.a = 0; + color.a = (byte)(a * slot.a * skinnedMeshAttachment.a); + color.r = (byte)(r * slot.r * skinnedMeshAttachment.r * color.a); + color.g = (byte)(g * slot.g * skinnedMeshAttachment.g * color.a); + color.b = (byte)(b * slot.b * skinnedMeshAttachment.b * color.a); + if (slot.data.blendMode == BlendMode.additive) color.a = 0; - float[] meshUVs = meshAttachment.uvs; - float z = i * zSpacing; - for (int ii = 0; ii < meshVertexCount; ii += 2, vertexIndex++) { - vertices[vertexIndex] = new Vector3(tempVertices[ii], tempVertices[ii + 1], z); - colors[vertexIndex] = color; - uvs[vertexIndex] = new Vector2(meshUVs[ii], meshUVs[ii + 1]); + float[] meshUVs = skinnedMeshAttachment.uvs; + float z = i * zSpacing; + for (int ii = 0; ii < meshVertexCount; ii += 2, vertexIndex++) { + vertices[vertexIndex].x = tempVertices[ii]; + vertices[vertexIndex].y = tempVertices[ii + 1]; + vertices[vertexIndex].z = z; + colors[vertexIndex] = color; + uvs[vertexIndex].x = meshUVs[ii]; + uvs[vertexIndex].y = meshUVs[ii + 1]; + + if (tempVertices[ii] < meshBoundsMin.x) + meshBoundsMin.x = tempVertices[ii]; + else if (tempVertices[ii] > meshBoundsMax.x) + meshBoundsMax.x = tempVertices[ii]; + if (tempVertices[ii + 1]< meshBoundsMin.y) + meshBoundsMin.y = tempVertices[ii + 1]; + else if (tempVertices[ii + 1] > meshBoundsMax.y) + meshBoundsMax.y = tempVertices[ii + 1]; + } } } } @@ -352,11 +486,16 @@ public class SkeletonRenderer : MonoBehaviour { mesh.colors32 = colors; mesh.uv = uvs; - int submeshCount = submeshMaterials.Count; - mesh.subMeshCount = submeshCount; - for (int i = 0; i < submeshCount; ++i) - mesh.SetTriangles(submeshes[i].triangles, i); - mesh.RecalculateBounds(); + if (mustUpdateMeshStructure) { + int submeshCount = submeshMaterials.Count; + mesh.subMeshCount = submeshCount; + for (int i = 0; i < submeshCount; ++i) + mesh.SetTriangles(submeshes.Items[i].triangles, i); + } + + Vector3 meshBoundsExtents = meshBoundsMax - meshBoundsMin; + Vector3 meshBoundsCenter = meshBoundsMin + meshBoundsExtents * 0.5f; + mesh.bounds = new Bounds(meshBoundsCenter, meshBoundsExtents); if (newTriangles && calculateNormals) { Vector3[] normals = new Vector3[vertexCount]; @@ -377,21 +516,105 @@ public class SkeletonRenderer : MonoBehaviour { } } + // Update previous state + ExposedList attachmentsTriangleCountCurrentMesh; + ExposedList attachmentsFlipStateCurrentMesh; + ExposedList addSubmeshArgumentsCurrentMesh; + if (useMesh1) { + attachmentsTriangleCountCurrentMesh = lastState.attachmentsTriangleCountMesh1; + addSubmeshArgumentsCurrentMesh = lastState.addSubmeshArgumentsMesh1; + attachmentsFlipStateCurrentMesh = lastState.attachmentsFlipStateMesh1; + lastState.immutableTrianglesMesh1 = immutableTriangles; + } else { + attachmentsTriangleCountCurrentMesh = lastState.attachmentsTriangleCountMesh2; + addSubmeshArgumentsCurrentMesh = lastState.addSubmeshArgumentsMesh2; + attachmentsFlipStateCurrentMesh = lastState.attachmentsFlipStateMesh2; + lastState.immutableTrianglesMesh2 = immutableTriangles; + } + + attachmentsTriangleCountCurrentMesh.GrowIfNeeded(attachmentsTriangleCountTemp.Capacity); + attachmentsTriangleCountCurrentMesh.Count = attachmentsTriangleCountTemp.Count; + attachmentsTriangleCountTemp.CopyTo(attachmentsTriangleCountCurrentMesh.Items, 0); + + attachmentsFlipStateCurrentMesh.GrowIfNeeded(attachmentsFlipStateTemp.Capacity); + attachmentsFlipStateCurrentMesh.Count = attachmentsFlipStateTemp.Count; + attachmentsFlipStateTemp.CopyTo(attachmentsFlipStateCurrentMesh.Items, 0); + + addSubmeshArgumentsCurrentMesh.GrowIfNeeded(addSubmeshArgumentsTemp.Count); + addSubmeshArgumentsCurrentMesh.Count = addSubmeshArgumentsTemp.Count; + addSubmeshArgumentsTemp.CopyTo(addSubmeshArgumentsCurrentMesh.Items); + if (submeshRenderers.Length > 0) { - foreach (var submeshRenderer in submeshRenderers) { - if (submeshRenderer.submeshIndex < sharedMaterials.Length) + for (int i = 0; i < submeshRenderers.Length; i++) { + SkeletonUtilitySubmeshRenderer submeshRenderer = submeshRenderers[i]; + if (submeshRenderer.submeshIndex < sharedMaterials.Length) { submeshRenderer.SetMesh(meshRenderer, useMesh1 ? mesh1 : mesh2, sharedMaterials[submeshRenderer.submeshIndex]); - else + } else { submeshRenderer.GetComponent().enabled = false; + } } } useMesh1 = !useMesh1; } + private bool CheckIfMustUpdateMeshStructure(ExposedList attachmentsTriangleCountTemp, ExposedList attachmentsFlipStateTemp, ExposedList addSubmeshArgumentsTemp) { + // Check if any mesh settings were changed + bool mustUpdateMeshStructure = + immutableTriangles != (useMesh1 ? lastState.immutableTrianglesMesh1 : lastState.immutableTrianglesMesh2); +#if UNITY_EDITOR + mustUpdateMeshStructure |= !Application.isPlaying; +#endif + + if (mustUpdateMeshStructure) + return true; + + // Check if any attachments were enabled/disabled + // or submesh structures has changed + ExposedList attachmentsTriangleCountCurrentMesh; + ExposedList attachmentsFlipStateCurrentMesh; + ExposedList addSubmeshArgumentsCurrentMesh; + if (useMesh1) { + attachmentsTriangleCountCurrentMesh = lastState.attachmentsTriangleCountMesh1; + addSubmeshArgumentsCurrentMesh = lastState.addSubmeshArgumentsMesh1; + attachmentsFlipStateCurrentMesh = lastState.attachmentsFlipStateMesh1; + } else { + attachmentsTriangleCountCurrentMesh = lastState.attachmentsTriangleCountMesh2; + addSubmeshArgumentsCurrentMesh = lastState.addSubmeshArgumentsMesh2; + attachmentsFlipStateCurrentMesh = lastState.attachmentsFlipStateMesh2; + } + + // Check attachments + int attachmentCount = attachmentsTriangleCountTemp.Count; + if (attachmentsTriangleCountCurrentMesh.Count != attachmentCount) + return true; + + for (int i = 0; i < attachmentCount; i++) { + if (attachmentsTriangleCountCurrentMesh.Items[i] != attachmentsTriangleCountTemp.Items[i]) + return true; + } + + // Check flip state + for (int i = 0; i < attachmentCount; i++) { + if (attachmentsFlipStateCurrentMesh.Items[i] != attachmentsFlipStateTemp.Items[i]) + return true; + } + + // Check submeshes + int submeshCount = addSubmeshArgumentsTemp.Count; + if (addSubmeshArgumentsCurrentMesh.Count != submeshCount) + return true; + + for (int i = 0; i < submeshCount; i++) { + if (!addSubmeshArgumentsCurrentMesh.Items[i].Equals(ref addSubmeshArgumentsTemp.Items[i])) + return true; + } + + return false; + } + /** Stores vertices and triangles for a single material. */ - private void AddSubmesh (Material material, int startSlot, int endSlot, int triangleCount, int firstVertex, bool lastSubmesh) { - + private void AddSubmesh (Material material, int startSlot, int endSlot, int triangleCount, int firstVertex, bool lastSubmesh, ExposedList flipStates) { int submeshIndex = submeshMaterials.Count; submeshMaterials.Add(material); @@ -400,7 +623,7 @@ public class SkeletonRenderer : MonoBehaviour { else if (immutableTriangles) return; - Submesh submesh = submeshes[submeshIndex]; + Submesh submesh = submeshes.Items[submeshIndex]; int[] triangles = submesh.triangles; int trianglesCapacity = triangles.Length; @@ -434,12 +657,12 @@ public class SkeletonRenderer : MonoBehaviour { } // Store triangles. - List drawOrder = skeleton.DrawOrder; + ExposedList drawOrder = skeleton.DrawOrder; for (int i = startSlot, triangleIndex = 0; i < endSlot; i++) { - Slot slot = drawOrder[i]; + Slot slot = drawOrder.Items[i]; Attachment attachment = slot.attachment; - Bone bone = slot.bone; - bool flip = frontFacing && ((bone.WorldFlipX != bone.WorldFlipY) != (Mathf.Sign(bone.WorldScaleX) != Mathf.Sign(bone.WorldScaleY))); + + bool flip = flipStates.Items[i]; if (attachment is RegionAttachment) { if (!flip) { @@ -464,16 +687,18 @@ public class SkeletonRenderer : MonoBehaviour { } int[] attachmentTriangles; int attachmentVertexCount; - if (attachment is MeshAttachment) { - MeshAttachment meshAttachment = (MeshAttachment)attachment; + MeshAttachment meshAttachment = attachment as MeshAttachment; + if (meshAttachment != null) { attachmentVertexCount = meshAttachment.vertices.Length >> 1; attachmentTriangles = meshAttachment.triangles; - } else if (attachment is SkinnedMeshAttachment) { - SkinnedMeshAttachment meshAttachment = (SkinnedMeshAttachment)attachment; - attachmentVertexCount = meshAttachment.uvs.Length >> 1; - attachmentTriangles = meshAttachment.triangles; - } else - continue; + } else { + SkinnedMeshAttachment skinnedMeshAttachment = attachment as SkinnedMeshAttachment; + if (skinnedMeshAttachment != null) { + attachmentVertexCount = skinnedMeshAttachment.uvs.Length >> 1; + attachmentTriangles = skinnedMeshAttachment.triangles; + } else + continue; + } if (flip) { for (int ii = 0, nn = attachmentTriangles.Length; ii < nn; ii += 3, triangleIndex += 3) { @@ -494,24 +719,62 @@ public class SkeletonRenderer : MonoBehaviour { #if UNITY_EDITOR void OnDrawGizmos () { // Make selection easier by drawing a clear gizmo over the skeleton. - if (vertices == null) return; - Vector3 gizmosCenter = new Vector3(); - Vector3 gizmosSize = new Vector3(); - Vector3 min = new Vector3(float.MaxValue, float.MaxValue, 0f); - Vector3 max = new Vector3(float.MinValue, float.MinValue, 0f); - foreach (Vector3 vert in vertices) { - min = Vector3.Min(min, vert); - max = Vector3.Max(max, vert); - } - float width = max.x - min.x; - float height = max.y - min.y; - gizmosCenter = new Vector3(min.x + (width / 2f), min.y + (height / 2f), 0f); - gizmosSize = new Vector3(width, height, 1f); + meshFilter = GetComponent(); + if (meshFilter == null) return; + + Mesh mesh = meshFilter.sharedMesh; + if (mesh == null) return; + + Bounds meshBounds = mesh.bounds; Gizmos.color = Color.clear; Gizmos.matrix = transform.localToWorldMatrix; - Gizmos.DrawCube(gizmosCenter, gizmosSize); + Gizmos.DrawCube(meshBounds.center, meshBounds.size); } #endif + + private class LastState { + public bool immutableTrianglesMesh1; + public bool immutableTrianglesMesh2; + public int vertexCount; + public readonly ExposedList attachmentsFlipStateTemp = new ExposedList(); + public readonly ExposedList attachmentsFlipStateMesh1 = new ExposedList(); + public readonly ExposedList attachmentsFlipStateMesh2 = new ExposedList(); + public readonly ExposedList attachmentsTriangleCountTemp = new ExposedList(); + public readonly ExposedList attachmentsTriangleCountMesh1 = new ExposedList(); + public readonly ExposedList attachmentsTriangleCountMesh2 = new ExposedList(); + public readonly ExposedList addSubmeshArgumentsTemp = new ExposedList(); + public readonly ExposedList addSubmeshArgumentsMesh1 = new ExposedList(); + public readonly ExposedList addSubmeshArgumentsMesh2 = new ExposedList(); + + public struct AddSubmeshArguments { + public Material material; + public int startSlot; + public int endSlot; + public int triangleCount; + public int firstVertex; + public bool lastSubmesh; + + public AddSubmeshArguments(Material material, int startSlot, int endSlot, int triangleCount, int firstVertex, bool lastSubmesh) { + this.material = material; + this.startSlot = startSlot; + this.endSlot = endSlot; + this.triangleCount = triangleCount; + this.firstVertex = firstVertex; + this.lastSubmesh = lastSubmesh; + } + + public bool Equals(ref AddSubmeshArguments other) { + return + !ReferenceEquals(material, null) && + !ReferenceEquals(other.material, null) && + material.GetInstanceID() == other.material.GetInstanceID() && + startSlot == other.startSlot && + endSlot == other.endSlot && + triangleCount == other.triangleCount && + firstVertex == other.firstVertex; + } + } + } } class Submesh { diff --git a/spine-unity/Assets/spine-unity/SkeletonUtility/Editor/SkeletonUtilityBoneInspector.cs b/spine-unity/Assets/spine-unity/SkeletonUtility/Editor/SkeletonUtilityBoneInspector.cs index 4d4ca9154..6fc3ae8d1 100644 --- a/spine-unity/Assets/spine-unity/SkeletonUtility/Editor/SkeletonUtilityBoneInspector.cs +++ b/spine-unity/Assets/spine-unity/SkeletonUtility/Editor/SkeletonUtilityBoneInspector.cs @@ -61,7 +61,7 @@ public class SkeletonUtilityBoneInspector : Editor { currentSkinName = skin.Name; for(int i = 0; i < slotCount; i++){ - Slot slot = skeletonUtility.skeletonRenderer.skeleton.Slots[i]; + Slot slot = skeletonUtility.skeletonRenderer.skeleton.Slots.Items[i]; if (slot.Bone == utilityBone.bone) { List attachments = new List(); @@ -233,14 +233,14 @@ public class SkeletonUtilityBoneInspector : Editor { } } - void BoneSelectorContextMenu (string current, List bones, string topValue, GenericMenu.MenuFunction2 callback) { + void BoneSelectorContextMenu (string current, ExposedList bones, string topValue, GenericMenu.MenuFunction2 callback) { GenericMenu menu = new GenericMenu(); if (topValue != "") menu.AddItem(new GUIContent(topValue), current == topValue, callback, null); for (int i = 0; i < bones.Count; i++) { - menu.AddItem(new GUIContent(bones[i].Data.Name), bones[i].Data.Name == current, callback, bones[i]); + menu.AddItem(new GUIContent(bones.Items[i].Data.Name), bones.Items[i].Data.Name == current, callback, bones.Items[i]); } menu.ShowAsContext(); @@ -344,4 +344,4 @@ public class SkeletonUtilityBoneInspector : Editor { utilBone.gameObject.AddComponent(); } -} \ No newline at end of file +} diff --git a/spine-unity/Assets/spine-unity/SkeletonUtility/Editor/SkeletonUtilityInspector.cs b/spine-unity/Assets/spine-unity/SkeletonUtility/Editor/SkeletonUtilityInspector.cs index 34807f67d..b2a3c657e 100644 --- a/spine-unity/Assets/spine-unity/SkeletonUtility/Editor/SkeletonUtilityInspector.cs +++ b/spine-unity/Assets/spine-unity/SkeletonUtility/Editor/SkeletonUtilityInspector.cs @@ -132,7 +132,7 @@ public class SkeletonUtilityInspector : Editor { List attachments = new List(); skin.FindAttachmentsForSlot(i, attachments); - attachmentTable.Add(skeleton.Slots[i], attachments); + attachmentTable.Add(skeleton.Slots.Items[i], attachments); } } @@ -280,4 +280,4 @@ public class SkeletonUtilityInspector : Editor { Selection.activeGameObject = skeletonUtility.SpawnRoot(SkeletonUtilityBone.Mode.Override, true, true, true); AttachIconsToChildren(skeletonUtility.boneRoot); } -} \ No newline at end of file +} diff --git a/spine-unity/Assets/spine-unity/SkeletonUtility/SkeletonUtility.cs b/spine-unity/Assets/spine-unity/SkeletonUtility/SkeletonUtility.cs index a236c2c78..60eb7bab5 100644 --- a/spine-unity/Assets/spine-unity/SkeletonUtility/SkeletonUtility.cs +++ b/spine-unity/Assets/spine-unity/SkeletonUtility/SkeletonUtility.cs @@ -224,9 +224,9 @@ public class SkeletonUtility : MonoBehaviour { if (boneRoot != null) { List constraintTargetNames = new List(); - foreach (IkConstraint c in skeletonRenderer.skeleton.IkConstraints) { - constraintTargetNames.Add(c.Target.Data.Name); - } + ExposedList ikConstraints = skeletonRenderer.skeleton.IkConstraints; + for (int i = 0, n = ikConstraints.Count; i < n; i++) + constraintTargetNames.Add(ikConstraints.Items[i].Target.Data.Name); foreach (var b in utilityBones) { if (b.bone == null) { @@ -343,7 +343,9 @@ public class SkeletonUtility : MonoBehaviour { public GameObject SpawnBoneRecursively (Bone bone, Transform parent, SkeletonUtilityBone.Mode mode, bool pos, bool rot, bool sca) { GameObject go = SpawnBone(bone, parent, mode, pos, rot, sca); - foreach (Bone child in bone.Children) { + ExposedList childrenBones = bone.Children; + for (int i = 0, n = childrenBones.Count; i < n; i++) { + Bone child = childrenBones.Items[i]; SpawnBoneRecursively(child, go.transform, mode, pos, rot, sca); } @@ -399,4 +401,4 @@ public class SkeletonUtility : MonoBehaviour { if (disablePrimaryRenderer) GetComponent().enabled = false; } -} \ No newline at end of file +}