浏览代码

Merge pull request #375 from LostPolygon/optimizations

A bunch of optimizations
Fenrisul 10 年之前
父节点
当前提交
054e303d2e

+ 34 - 33
spine-csharp/src/Animation.cs

@@ -34,15 +34,15 @@ using System.Collections.Generic;
 
 namespace Spine {
 	public class Animation {
-		internal List<Timeline> timelines;
+		internal ExposedList<Timeline> timelines;
 		internal float duration;
 		internal String name;
 
 		public String Name { get { return name; } }
-		public List<Timeline> Timelines { get { return timelines; } set { timelines = value; } }
+		public ExposedList<Timeline> Timelines { get { return timelines; } set { timelines = value; } }
 		public float Duration { get { return duration; } set { duration = value; } }
 
-		public Animation (String name, List<Timeline> timelines, float duration) {
+		public Animation (String name, ExposedList<Timeline> 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 {
 		/// <summary>Poses the skeleton at the specified time for this animation.</summary>
 		/// <param name="lastTime">The last time the animation was applied.</param>
 		/// <param name="events">Any triggered events are added.</param>
-		public void Apply (Skeleton skeleton, float lastTime, float time, bool loop, List<Event> events) {
+		public void Apply (Skeleton skeleton, float lastTime, float time, bool loop, ExposedList<Event> events) {
 			if (skeleton == null) throw new ArgumentNullException("skeleton cannot be null.");
 
 			if (loop && duration != 0) {
@@ -61,16 +61,16 @@ namespace Spine {
 				lastTime %= duration;
 			}
 
-			List<Timeline> timelines = this.timelines;
+			ExposedList<Timeline> 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);
 		}
 
 		/// <summary>Poses the skeleton at the specified time for this animation mixed with the current pose.</summary>
 		/// <param name="lastTime">The last time the animation was applied.</param>
 		/// <param name="events">Any triggered events are added.</param>
 		/// <param name="alpha">The amount of this animation that affects the current pose.</param>
-		public void Mix (Skeleton skeleton, float lastTime, float time, bool loop, List<Event> events, float alpha) {
+		public void Mix (Skeleton skeleton, float lastTime, float time, bool loop, ExposedList<Event> 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<Timeline> timelines = this.timelines;
+			ExposedList<Timeline> 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);
 		}
 
 		/// <param name="target">After the first and before the last entry.</param>
@@ -125,7 +125,7 @@ namespace Spine {
 	public interface Timeline {
 		/// <summary>Sets the value(s) for the specified time.</summary>
 		/// <param name="events">May be null to not collect fired events.</param>
-		void Apply (Skeleton skeleton, float lastTime, float time, List<Event> events, float alpha);
+		void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> events, float alpha);
 	}
 
 	/// <summary>Base class for frames that use an interpolation bezier curve.</summary>
@@ -140,7 +140,7 @@ namespace Spine {
 			curves = new float[(frameCount - 1) * BEZIER_SIZE];
 		}
 
-		abstract public void Apply (Skeleton skeleton, float lastTime, float time, List<Event> firedEvents, float alpha);
+		abstract public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> 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<Event> firedEvents, float alpha) {
+		override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> 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<Event> firedEvents, float alpha) {
+		override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> 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<Event> firedEvents, float alpha) {
+		override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> 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<Event> firedEvents, float alpha) {
+		override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> 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<Event> firedEvents, float alpha) {
+		public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> 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 {
 		}
 
 		/// <summary>Fires events for frames > lastTime and <= time.</summary>
-		public void Apply (Skeleton skeleton, float lastTime, float time, List<Event> firedEvents, float alpha) {
+		public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> 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<Event> firedEvents, float alpha) {
+		public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> 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<Slot> drawOrder = skeleton.drawOrder;
-			List<Slot> slots = skeleton.slots;
+			ExposedList<Slot> drawOrder = skeleton.drawOrder;
+			ExposedList<Slot> 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<Event> firedEvents, float alpha) {
-			Slot slot = skeleton.slots[slotIndex];
+		override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> 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<Event> firedEvents, float alpha) {
+		override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> 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<Event> firedEvents, float alpha) {
+		public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> 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) {

+ 13 - 13
spine-csharp/src/AnimationState.cs

@@ -36,8 +36,8 @@ using System.Text;
 namespace Spine {
 	public class AnimationState {
 		private AnimationStateData data;
-		private List<TrackEntry> tracks = new List<TrackEntry>();
-		private List<Event> events = new List<Event>();
+		private ExposedList<TrackEntry> tracks = new ExposedList<TrackEntry>();
+		private ExposedList<Event> events = new ExposedList<Event>();
 		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<Event> events = this.events;
+			ExposedList<Event> 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 {
 		/// <returns>May be null.</returns>
 		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());

+ 3 - 3
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<Bone> skeletonBones = skeleton.bones;
+			ExposedList<Bone> 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;

+ 2 - 2
spine-csharp/src/Bone.cs

@@ -39,7 +39,7 @@ namespace Spine {
 		internal BoneData data;
 		internal Skeleton skeleton;
 		internal Bone parent;
-		internal List<Bone> children = new List<Bone>();
+		internal ExposedList<Bone> children = new ExposedList<Bone>();
 		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<Bone> Children { get { return children; } }
+		public ExposedList<Bone> Children { get { return children; } }
 		public float X { get { return x; } set { x = value; } }
 		public float Y { get { return y; } set { y = value; } }
 		/// <summary>The forward kinetics rotation.</summary>

+ 585 - 0
spine-csharp/src/ExposedList.cs

@@ -0,0 +1,585 @@
+//
+// System.Collections.Generic.List
+//
+// Authors:
+//    Ben Maurer ([email protected])
+//    Martin Baulig ([email protected])
+//    Carlos Alberto Cortez ([email protected])
+//    David Waite ([email protected])
+//
+// 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<T> : IEnumerable<T> {
+		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<T> collection) {
+			CheckCollection(collection);
+
+			// initialize to needed size (if determinable)
+			ICollection<T> c = collection as ICollection<T>;
+			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<T> collection) {
+			int collectionCount = collection.Count;
+			if (collectionCount == 0)
+				return;
+
+			GrowIfNeeded(collectionCount);
+			collection.CopyTo(Items, Count);
+			Count += collectionCount;
+		}
+
+		private void AddEnumerable(IEnumerable<T> enumerable) {
+			foreach (T t in enumerable) {
+				Add(t);
+			}
+		}
+
+		public void AddRange(IEnumerable<T> collection) {
+			CheckCollection(collection);
+
+			ICollection<T> c = collection as ICollection<T>;
+			if (c != null)
+				AddCollection(c);
+			else
+				AddEnumerable(collection);
+			version++;
+		}
+
+		public int BinarySearch(T item) {
+			return Array.BinarySearch<T>(Items, 0, Count, item);
+		}
+
+		public int BinarySearch(T item, IComparer<T> comparer) {
+			return Array.BinarySearch<T>(Items, 0, Count, item, comparer);
+		}
+
+		public int BinarySearch(int index, int count, T item, IComparer<T> comparer) {
+			CheckRange(index, count);
+			return Array.BinarySearch<T>(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<T>(Items, item, 0, Count) != -1;
+		}
+
+		public ExposedList<TOutput> ConvertAll<TOutput>(Converter<T, TOutput> converter) {
+			if (converter == null)
+				throw new ArgumentNullException("converter");
+			ExposedList<TOutput> u = new ExposedList<TOutput>(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<T> match) {
+			CheckMatch(match);
+			return GetIndex(0, Count, match) != -1;
+		}
+
+		public T Find(Predicate<T> match) {
+			CheckMatch(match);
+			int i = GetIndex(0, Count, match);
+			return (i != -1) ? Items[i] : default (T);
+		}
+
+		private static void CheckMatch(Predicate<T> match) {
+			if (match == null)
+				throw new ArgumentNullException("match");
+		}
+
+		public ExposedList<T> FindAll(Predicate<T> match) {
+			CheckMatch(match);
+			return FindAllList(match);
+		}
+
+		private ExposedList<T> FindAllList(Predicate<T> match) {
+			ExposedList<T> results = new ExposedList<T>();
+			for (int i = 0; i < Count; i++)
+				if (match(Items[i]))
+					results.Add(Items[i]);
+
+			return results;
+		}
+
+		public int FindIndex(Predicate<T> match) {
+			CheckMatch(match);
+			return GetIndex(0, Count, match);
+		}
+
+		public int FindIndex(int startIndex, Predicate<T> match) {
+			CheckMatch(match);
+			CheckIndex(startIndex);
+			return GetIndex(startIndex, Count - startIndex, match);
+		}
+
+		public int FindIndex(int startIndex, int count, Predicate<T> match) {
+			CheckMatch(match);
+			CheckRange(startIndex, count);
+			return GetIndex(startIndex, count, match);
+		}
+
+		private int GetIndex(int startIndex, int count, Predicate<T> match) {
+			int end = startIndex + count;
+			for (int i = startIndex; i < end; i ++)
+				if (match(Items[i]))
+					return i;
+
+			return -1;
+		}
+
+		public T FindLast(Predicate<T> match) {
+			CheckMatch(match);
+			int i = GetLastIndex(0, Count, match);
+			return i == -1 ? default (T) : Items[i];
+		}
+
+		public int FindLastIndex(Predicate<T> match) {
+			CheckMatch(match);
+			return GetLastIndex(0, Count, match);
+		}
+
+		public int FindLastIndex(int startIndex, Predicate<T> match) {
+			CheckMatch(match);
+			CheckIndex(startIndex);
+			return GetLastIndex(0, startIndex + 1, match);
+		}
+
+		public int FindLastIndex(int startIndex, int count, Predicate<T> match) {
+			CheckMatch(match);
+			int start = startIndex - count + 1;
+			CheckRange(start, count);
+			return GetLastIndex(start, count, match);
+		}
+
+		private int GetLastIndex(int startIndex, int count, Predicate<T> 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<T> 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<T> GetRange(int index, int count) {
+			CheckRange(index, count);
+			T[] tmpArray = new T[count];
+			Array.Copy(Items, index, tmpArray, 0, count);
+			return new ExposedList<T>(tmpArray, count);
+		}
+
+		public int IndexOf(T item) {
+			return Array.IndexOf<T>(Items, item, 0, Count);
+		}
+
+		public int IndexOf(T item, int index) {
+			CheckIndex(index);
+			return Array.IndexOf<T>(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<T>(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<T> collection) {
+			if (collection == null)
+				throw new ArgumentNullException("collection");
+		}
+
+		public void InsertRange(int index, IEnumerable<T> 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<T> c = collection as ICollection<T>;
+				if (c != null)
+					InsertCollection(index, c);
+				else
+					InsertEnumeration(index, collection);
+			}
+			version++;
+		}
+
+		private void InsertCollection(int index, ICollection<T> collection) {
+			int collectionCount = collection.Count;
+			GrowIfNeeded(collectionCount);
+
+			Shift(index, collectionCount);
+			collection.CopyTo(Items, index);
+		}
+
+		private void InsertEnumeration(int index, IEnumerable<T> enumerable) {
+			foreach (T t in enumerable)
+				Insert(index++, t);
+		}
+
+		public int LastIndexOf(T item) {
+			return Array.LastIndexOf<T>(Items, item, Count - 1, Count);
+		}
+
+		public int LastIndexOf(T item, int index) {
+			CheckIndex(index);
+			return Array.LastIndexOf<T>(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<T>(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<T> 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<T>(Items, 0, Count, Comparer<T>.Default);
+			version++;
+		}
+
+		public void Sort(IComparer<T> comparer) {
+			Array.Sort<T>(Items, 0, Count, comparer);
+			version++;
+		}
+
+		public void Sort(Comparison<T> comparison) {
+			Array.Sort<T>(Items, comparison);
+			version++;
+		}
+
+		public void Sort(int index, int count, IComparer<T> comparer) {
+			CheckRange(index, count);
+			Array.Sort<T>(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<T> 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<T> IEnumerable<T>.GetEnumerator() {
+			return GetEnumerator();
+		}
+
+		IEnumerator IEnumerable.GetEnumerator() {
+			return GetEnumerator();
+		}
+
+		#endregion
+
+		[Serializable]
+		public struct Enumerator : IEnumerator<T>, IDisposable {
+			private ExposedList<T> l;
+			private int next;
+			private int ver;
+
+			private T current;
+
+			internal Enumerator(ExposedList<T> 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;
+				}
+			}
+		}
+	}
+}

+ 6 - 6
spine-csharp/src/IkConstraint.cs

@@ -37,13 +37,13 @@ namespace Spine {
 		private const float radDeg = 180 / (float)Math.PI;
 
 		internal IkConstraintData data;
-		internal List<Bone> bones = new List<Bone>();
+		internal ExposedList<Bone> bones = new ExposedList<Bone>();
 		internal Bone target;
 		internal int bendDirection;
 		internal float mix;
 
 		public IkConstraintData Data { get { return data; } }
-		public List<Bone> Bones { get { return bones; } }
+		public ExposedList<Bone> 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<Bone>(data.bones.Count);
+			bones = new ExposedList<Bone>(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<Bone> bones = this.bones;
+			ExposedList<Bone> 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;
 			}
 		}

+ 57 - 55
spine-csharp/src/Skeleton.cs

@@ -35,11 +35,11 @@ using System.Collections.Generic;
 namespace Spine {
 	public class Skeleton {
 		internal SkeletonData data;
-		internal List<Bone> bones;
-		internal List<Slot> slots;
-		internal List<Slot> drawOrder;
-		internal List<IkConstraint> ikConstraints;
-		private List<List<Bone>> boneCache = new List<List<Bone>>();
+		internal ExposedList<Bone> bones;
+		internal ExposedList<Slot> slots;
+		internal ExposedList<Slot> drawOrder;
+		internal ExposedList<IkConstraint> ikConstraints;
+		private ExposedList<ExposedList<Bone>> boneCache = new ExposedList<ExposedList<Bone>>();
 		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<Bone> Bones { get { return bones; } }
-		public List<Slot> Slots { get { return slots; } }
-		public List<Slot> DrawOrder { get { return drawOrder; } }
-		public List<IkConstraint> IkConstraints { get { return ikConstraints; } set { ikConstraints = value; } }
+		public ExposedList<Bone> Bones { get { return bones; } }
+		public ExposedList<Slot> Slots { get { return slots; } }
+		public ExposedList<Slot> DrawOrder { get { return drawOrder; } }
+		public ExposedList<IkConstraint> 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<Bone>(data.bones.Count);
+			bones = new ExposedList<Bone>(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<Slot>(data.slots.Count);
-			drawOrder = new List<Slot>(data.slots.Count);
+			slots = new ExposedList<Slot>(data.slots.Count);
+			drawOrder = new ExposedList<Slot>(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<IkConstraint>(data.ikConstraints.Count);
+			ikConstraints = new ExposedList<IkConstraint>(data.ikConstraints.Count);
 			foreach (IkConstraintData ikConstraintData in data.ikConstraints)
 				ikConstraints.Add(new IkConstraint(ikConstraintData, this));
 
@@ -99,31 +99,31 @@ namespace Spine {
 		/// <summary>Caches information about bones and IK constraints. Must be called if bones or IK constraints are added or
 		/// removed.</summary>
 		public void UpdateCache () {
-			List<List<Bone>> boneCache = this.boneCache;
-			List<IkConstraint> ikConstraints = this.ikConstraints;
+			ExposedList<ExposedList<Bone>> boneCache = this.boneCache;
+			ExposedList<IkConstraint> 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<Bone>());
+				boneCache.Add(new ExposedList<Bone>());
 
-			List<Bone> nonIkBones = boneCache[0];
+			ExposedList<Bone> 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 {
 
 		/// <summary>Updates the world transform for each bone and applies IK constraints.</summary>
 		public void UpdateWorldTransform () {
-			List<Bone> bones = this.bones;
+			ExposedList<Bone> 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<List<Bone>> boneCache = this.boneCache;
-			List<IkConstraint> ikConstraints = this.ikConstraints;
+			ExposedList<ExposedList<Bone>> boneCache = this.boneCache;
+			ExposedList<IkConstraint> ikConstraints = this.ikConstraints;
 			int i = 0, last = boneCache.Count - 1;
 			while (true) {
-				List<Bone> updateBones = boneCache[i];
+				ExposedList<Bone> 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<Bone> bones = this.bones;
+			ExposedList<Bone> bones = this.bones;
 			for (int i = 0, n = bones.Count; i < n; i++)
-				bones[i].SetToSetupPose();
+				bones.Items[i].SetToSetupPose();
 
-			List<IkConstraint> ikConstraints = this.ikConstraints;
+			ExposedList<IkConstraint> 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<Slot> slots = this.slots;
+			ExposedList<Slot> 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);
 		}
 
 		/// <returns>May be null.</returns>
 		public Bone FindBone (String boneName) {
 			if (boneName == null) throw new ArgumentNullException("boneName cannot be null.");
-			List<Bone> bones = this.bones;
+			ExposedList<Bone> 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 {
 		/// <returns>-1 if the bone was not found.</returns>
 		public int FindBoneIndex (String boneName) {
 			if (boneName == null) throw new ArgumentNullException("boneName cannot be null.");
-			List<Bone> bones = this.bones;
+			ExposedList<Bone> 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;
 		}
 
 		/// <returns>May be null.</returns>
 		public Slot FindSlot (String slotName) {
 			if (slotName == null) throw new ArgumentNullException("slotName cannot be null.");
-			List<Slot> slots = this.slots;
+			ExposedList<Slot> 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 {
 		/// <returns>-1 if the bone was not found.</returns>
 		public int FindSlotIndex (String slotName) {
 			if (slotName == null) throw new ArgumentNullException("slotName cannot be null.");
-			List<Slot> slots = this.slots;
+			ExposedList<Slot> 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<Slot> slots = this.slots;
+					ExposedList<Slot> 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 {
 		/// <param name="attachmentName">May be null.</param>
 		public void SetAttachment (String slotName, String attachmentName) {
 			if (slotName == null) throw new ArgumentNullException("slotName cannot be null.");
-			List<Slot> slots = this.slots;
+			ExposedList<Slot> 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<IkConstraint> ikConstraints = this.ikConstraints;
+			ExposedList<IkConstraint> 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;

+ 8 - 8
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<Timeline>();
+			var timelines = new ExposedList<Timeline>();
 			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);

+ 19 - 19
spine-csharp/src/SkeletonBounds.cs

@@ -34,11 +34,11 @@ using System.Collections.Generic;
 
 namespace Spine {
 	public class SkeletonBounds {
-		private List<Polygon> polygonPool = new List<Polygon>();
+		private ExposedList<Polygon> polygonPool = new ExposedList<Polygon>();
 		private float minX, minY, maxX, maxY;
 
-		public List<BoundingBoxAttachment> BoundingBoxes { get; private set; }
-		public List<Polygon> Polygons { get; private set; }
+		public ExposedList<BoundingBoxAttachment> BoundingBoxes { get; private set; }
+		public ExposedList<Polygon> 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<BoundingBoxAttachment>();
-			Polygons = new List<Polygon>();
+			BoundingBoxes = new ExposedList<BoundingBoxAttachment>();
+			Polygons = new ExposedList<Polygon>();
 		}
 
 		public void Update (Skeleton skeleton, bool updateAabb) {
-			List<BoundingBoxAttachment> boundingBoxes = BoundingBoxes;
-			List<Polygon> polygons = Polygons;
-			List<Slot> slots = skeleton.slots;
+			ExposedList<BoundingBoxAttachment> boundingBoxes = BoundingBoxes;
+			ExposedList<Polygon> polygons = Polygons;
+			ExposedList<Slot> 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<Polygon> polygons = Polygons;
+			ExposedList<Polygon> 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 {
 		/// <summary>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.</summary>
 		public BoundingBoxAttachment ContainsPoint (float x, float y) {
-			List<Polygon> polygons = Polygons;
+			ExposedList<Polygon> 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;
 		}
 
 		/// <summary>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.</summary>
 		public BoundingBoxAttachment IntersectsSegment (float x1, float y1, float x2, float y2) {
-			List<Polygon> polygons = Polygons;
+			ExposedList<Polygon> 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];
 		}
 	}
 

+ 24 - 24
spine-csharp/src/SkeletonData.cs

@@ -35,25 +35,25 @@ using System.Collections.Generic;
 namespace Spine {
 	public class SkeletonData {
 		internal String name;
-		internal List<BoneData> bones = new List<BoneData>();
-		internal List<SlotData> slots = new List<SlotData>();
-		internal List<Skin> skins = new List<Skin>();
+		internal ExposedList<BoneData> bones = new ExposedList<BoneData>();
+		internal ExposedList<SlotData> slots = new ExposedList<SlotData>();
+		internal ExposedList<Skin> skins = new ExposedList<Skin>();
 		internal Skin defaultSkin;
-		internal List<EventData> events = new List<EventData>();
-		internal List<Animation> animations = new List<Animation>();
-		internal List<IkConstraintData> ikConstraints = new List<IkConstraintData>();
+		internal ExposedList<EventData> events = new ExposedList<EventData>();
+		internal ExposedList<Animation> animations = new ExposedList<Animation>();
+		internal ExposedList<IkConstraintData> ikConstraints = new ExposedList<IkConstraintData>();
 		internal float width, height;
 		internal String version, hash, imagesPath;
 
 		public String Name { get { return name; } set { name = value; } }
-		public List<BoneData> Bones { get { return bones; } } // Ordered parents first.
-		public List<SlotData> Slots { get { return slots; } } // Setup pose draw order.
-		public List<Skin> Skins { get { return skins; } set { skins = value; } }
+		public ExposedList<BoneData> Bones { get { return bones; } } // Ordered parents first.
+		public ExposedList<SlotData> Slots { get { return slots; } } // Setup pose draw order.
+		public ExposedList<Skin> Skins { get { return skins; } set { skins = value; } }
 		/// <summary>May be null.</summary>
 		public Skin DefaultSkin { get { return defaultSkin; } set { defaultSkin = value; } }
-		public List<EventData> Events { get { return events; } set { events = value; } }
-		public List<Animation> Animations { get { return animations; } set { animations = value; } }
-		public List<IkConstraintData> IkConstraints { get { return ikConstraints; } set { ikConstraints = value; } }
+		public ExposedList<EventData> Events { get { return events; } set { events = value; } }
+		public ExposedList<Animation> Animations { get { return animations; } set { animations = value; } }
+		public ExposedList<IkConstraintData> 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; } }
 		/// <summary>The Spine version used to export this data.</summary>
@@ -65,9 +65,9 @@ namespace Spine {
 		/// <returns>May be null.</returns>
 		public BoneData FindBone (String boneName) {
 			if (boneName == null) throw new ArgumentNullException("boneName cannot be null.");
-			List<BoneData> bones = this.bones;
+			ExposedList<BoneData> 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 {
 		/// <returns>-1 if the bone was not found.</returns>
 		public int FindBoneIndex (String boneName) {
 			if (boneName == null) throw new ArgumentNullException("boneName cannot be null.");
-			List<BoneData> bones = this.bones;
+			ExposedList<BoneData> 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 {
 		/// <returns>May be null.</returns>
 		public SlotData FindSlot (String slotName) {
 			if (slotName == null) throw new ArgumentNullException("slotName cannot be null.");
-			List<SlotData> slots = this.slots;
+			ExposedList<SlotData> 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 {
 		/// <returns>-1 if the bone was not found.</returns>
 		public int FindSlotIndex (String slotName) {
 			if (slotName == null) throw new ArgumentNullException("slotName cannot be null.");
-			List<SlotData> slots = this.slots;
+			ExposedList<SlotData> 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 {
 		/// <returns>May be null.</returns>
 		public Animation FindAnimation (String animationName) {
 			if (animationName == null) throw new ArgumentNullException("animationName cannot be null.");
-			List<Animation> animations = this.animations;
+			ExposedList<Animation> 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 {
 		/// <returns>May be null.</returns>
 		public IkConstraintData FindIkConstraint (String ikConstraintName) {
 			if (ikConstraintName == null) throw new ArgumentNullException("ikConstraintName cannot be null.");
-			List<IkConstraintData> ikConstraints = this.ikConstraints;
+			ExposedList<IkConstraintData> 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;

+ 1 - 1
spine-csharp/src/SkeletonJson.cs

@@ -380,7 +380,7 @@ namespace Spine {
 		}
 
 		private void ReadAnimation (String name, Dictionary<String, Object> map, SkeletonData skeletonData) {
-			var timelines = new List<Timeline>();
+			var timelines = new ExposedList<Timeline>();
 			float duration = 0;
 			float scale = Scale;
 

+ 30 - 18
spine-csharp/src/Skin.cs

@@ -36,8 +36,8 @@ namespace Spine {
 	/// <summary>Stores attachments by slot index and attachment name.</summary>
 	public class Skin {
 		internal String name;
-		private Dictionary<KeyValuePair<int, String>, Attachment> attachments =
-			new Dictionary<KeyValuePair<int, String>, Attachment>(AttachmentComparer.Instance);
+		private Dictionary<AttachmentKeyTuple, Attachment> attachments =
+			new Dictionary<AttachmentKeyTuple, Attachment>(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<int, String>(slotIndex, name)] = attachment;
+			attachments[new AttachmentKeyTuple(slotIndex, name)] = attachment;
 		}
 
 		/// <returns>May be null.</returns>
 		public Attachment GetAttachment (int slotIndex, String name) {
 			Attachment attachment;
-			attachments.TryGetValue(new KeyValuePair<int, String>(slotIndex, name), out attachment);
+			attachments.TryGetValue(new AttachmentKeyTuple(slotIndex, name), out attachment);
 			return attachment;
 		}
 
 		public void FindNamesForSlot (int slotIndex, List<String> names) {
 			if (names == null) throw new ArgumentNullException("names cannot be null.");
-			foreach (KeyValuePair<int, String> 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<Attachment> attachments) {
 			if (attachments == null) throw new ArgumentNullException("attachments cannot be null.");
-			foreach (KeyValuePair<KeyValuePair<int, String>, Attachment> entry in this.attachments)
-				if (entry.Key.Key == slotIndex) attachments.Add(entry.Value);
+			foreach (KeyValuePair<AttachmentKeyTuple, Attachment> entry in this.attachments)
+				if (entry.Key.SlotIndex == slotIndex) attachments.Add(entry.Value);
 		}
 
 		override public String ToString () {
@@ -76,27 +76,39 @@ namespace Spine {
 
 		/// <summary>Attach all attachments from this skin if the corresponding attachment from the old skin is currently attached.</summary>
 		internal void AttachAll (Skeleton skeleton, Skin oldSkin) {
-			foreach (KeyValuePair<KeyValuePair<int, String>, Attachment> entry in oldSkin.attachments) {
-				int slotIndex = entry.Key.Key;
-				Slot slot = skeleton.slots[slotIndex];
+			foreach (KeyValuePair<AttachmentKeyTuple, Attachment> 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<KeyValuePair<int, String>> {
-			internal static readonly AttachmentComparer Instance = new AttachmentComparer();
+		private class AttachmentKeyTupleComparer : IEqualityComparer<AttachmentKeyTuple> {
+			internal static readonly AttachmentKeyTupleComparer Instance = new AttachmentKeyTupleComparer();
 
-			bool IEqualityComparer<KeyValuePair<int, string>>.Equals (KeyValuePair<int, string> o1, KeyValuePair<int, string> o2) {
-				return o1.Key == o2.Key && o1.Value == o2.Value;
+			bool IEqualityComparer<AttachmentKeyTuple>.Equals (AttachmentKeyTuple o1, AttachmentKeyTuple o2) {
+			    return o1.SlotIndex == o2.SlotIndex && o1.NameHashCode == o2.NameHashCode && o1.Name == o2.Name;
 			}
 
-			int IEqualityComparer<KeyValuePair<int, string>>.GetHashCode (KeyValuePair<int, string> o) {
-				return o.Key;
+			int IEqualityComparer<AttachmentKeyTuple>.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();
+            }
+        }
 	}
 }

+ 1 - 1
spine-unity/Assets/spine-unity/Editor/SkeletonAnimationInspector.cs

@@ -77,7 +77,7 @@ public class SkeletonAnimationInspector : SkeletonRendererInspector {
 			animations[0] = "<None>";
 			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;

+ 15 - 15
spine-unity/Assets/spine-unity/Editor/SkeletonBaker.cs

@@ -49,7 +49,7 @@ public static class SkeletonBaker {
 	/// </summary>
 	const float bakeIncrement = 1 / 60f;
 
-	public static void BakeToPrefab (SkeletonDataAsset skeletonDataAsset, List<Skin> skins, string outputPath = "", bool bakeAnimations = true, bool bakeIK = true, SendMessageOptions eventOptions = SendMessageOptions.DontRequireReceiver) {
+	public static void BakeToPrefab (SkeletonDataAsset skeletonDataAsset, ExposedList<Skin> 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<string> attachmentNames = new List<string>();
 				for (int i = 0; i < skinCount; i++) {
-					var skin = skins[i];
+					var skin = skins.Items[i];
 					List<string> temp = new List<string>();
 					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<int, List<string>> 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<string, AnimationCurve> curveTable = new Dictionary<string, AnimationCurve>();
 
@@ -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();
 

+ 5 - 5
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<Skin>(new Skin[] { bakeSkin }), "", bakeAnimations, bakeIK, bakeEventOptions);
+							SkeletonBaker.BakeToPrefab(m_skeletonDataAsset, new ExposedList<Skin>(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<Attachment> slotAttachments = new List<Attachment>();
 		List<string> slotAttachmentNames = new List<string>();
 		List<string> defaultSkinAttachmentNames = new List<string>();
-		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) {
 

+ 1 - 1
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;

+ 9 - 9
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));
 		}

+ 2 - 2
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();
 

+ 1 - 1
spine-unity/Assets/spine-unity/Ragdoll/SkeletonRagdoll2D.cs

@@ -280,7 +280,7 @@ public class SkeletonRagdoll2D : MonoBehaviour {
 			if (colliders.Count == 0) {
 				var box = go.AddComponent<BoxCollider2D>();
 				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);

+ 20 - 8
spine-unity/Assets/spine-unity/SkeletonAnimator.cs

@@ -44,7 +44,8 @@ public class SkeletonAnimator : SkeletonRenderer, ISkeletonAnimation {
 		}
 	}
 
-	Dictionary<string, Spine.Animation> animationTable = new Dictionary<string, Spine.Animation>();
+	Dictionary<int, Spine.Animation> animationTable = new Dictionary<int, Spine.Animation>();
+	Dictionary<AnimationClip, int> clipNameHashCodeTable = new Dictionary<AnimationClip, int>();
 	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<Animator>();
@@ -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;
+	}
 }

+ 368 - 105
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<Material> submeshMaterials = new List<Material>();
-	private readonly List<Submesh> submeshes = new List<Submesh>();
+	private readonly ExposedList<Material> submeshMaterials = new ExposedList<Material>();
+	private readonly ExposedList<Submesh> submeshes = new ExposedList<Submesh>();
 	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<MeshFilter>();
+		meshRenderer = GetComponent<MeshRenderer>();
 		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<Slot> drawOrder = skeleton.DrawOrder;
+		ExposedList<Slot> drawOrder = skeleton.drawOrder;
 		int drawOrderCount = drawOrder.Count;
+		int submeshSeparatorSlotsCount = submeshSeparatorSlots.Count;
 		bool renderMeshes = this.renderMeshes;
+
+		// Clear last state of attachments and submeshes
+		ExposedList<int> attachmentsTriangleCountTemp = lastState.attachmentsTriangleCountTemp;
+		attachmentsTriangleCountTemp.GrowIfNeeded(drawOrderCount);
+		attachmentsTriangleCountTemp.Count = drawOrderCount;
+		ExposedList<bool> attachmentsFlipStateTemp = lastState.attachmentsFlipStateTemp;
+		attachmentsFlipStateTemp.GrowIfNeeded(drawOrderCount);
+		attachmentsFlipStateTemp.Count = drawOrderCount;
+
+		ExposedList<LastState.AddSubmeshArguments> 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;
-
-			if (attachment is RegionAttachment) {
-				rendererObject = ((RegionAttachment)attachment).RendererObject;
+			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;
+
+			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;
+
+			attachmentsTriangleCountTemp.Items[i] = attachmentTriangleCount;
 		}
-		AddSubmesh(lastMaterial, submeshStartSlotIndex, drawOrderCount, submeshTriangleCount, submeshFirstVertex, true);
+		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();
 
-		// Set materials.
-		if (submeshMaterials.Count == sharedMaterials.Length)
-			submeshMaterials.CopyTo(sharedMaterials);
-		else
-			sharedMaterials = submeshMaterials.ToArray();
-		meshRenderer.sharedMaterials = sharedMaterials;
+			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);
-
-					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;
-
-					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]);
+				} 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 * 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 = 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<int> attachmentsTriangleCountCurrentMesh;
+		ExposedList<bool> attachmentsFlipStateCurrentMesh;
+		ExposedList<LastState.AddSubmeshArguments> 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<Renderer>().enabled = false;
+				}
 			}
 		}
 
 		useMesh1 = !useMesh1;
 	}
 
+	private bool CheckIfMustUpdateMeshStructure(ExposedList<int> attachmentsTriangleCountTemp, ExposedList<bool> attachmentsFlipStateTemp, ExposedList<LastState.AddSubmeshArguments> 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<int> attachmentsTriangleCountCurrentMesh;
+		ExposedList<bool> attachmentsFlipStateCurrentMesh;
+		ExposedList<LastState.AddSubmeshArguments> 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<bool> 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<Slot> drawOrder = skeleton.DrawOrder;
+		ExposedList<Slot> 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<MeshFilter>();
+		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<bool> attachmentsFlipStateTemp = new ExposedList<bool>();
+		public readonly ExposedList<bool> attachmentsFlipStateMesh1 = new ExposedList<bool>();
+		public readonly ExposedList<bool> attachmentsFlipStateMesh2 = new ExposedList<bool>();
+		public readonly ExposedList<int> attachmentsTriangleCountTemp = new ExposedList<int>();
+		public readonly ExposedList<int> attachmentsTriangleCountMesh1 = new ExposedList<int>();
+		public readonly ExposedList<int> attachmentsTriangleCountMesh2 = new ExposedList<int>();
+		public readonly ExposedList<AddSubmeshArguments> addSubmeshArgumentsTemp = new ExposedList<AddSubmeshArguments>();
+		public readonly ExposedList<AddSubmeshArguments> addSubmeshArgumentsMesh1 = new ExposedList<AddSubmeshArguments>();
+		public readonly ExposedList<AddSubmeshArguments> addSubmeshArgumentsMesh2 = new ExposedList<AddSubmeshArguments>();
+
+		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 {

+ 4 - 4
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<Attachment> attachments = new List<Attachment>();
 				
@@ -233,14 +233,14 @@ public class SkeletonUtilityBoneInspector : Editor {
 		}
 	}
 
-	void BoneSelectorContextMenu (string current, List<Bone> bones, string topValue, GenericMenu.MenuFunction2 callback) {
+	void BoneSelectorContextMenu (string current, ExposedList<Bone> 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<Rigidbody>();
 	}
-}
+}

+ 2 - 2
spine-unity/Assets/spine-unity/SkeletonUtility/Editor/SkeletonUtilityInspector.cs

@@ -132,7 +132,7 @@ public class SkeletonUtilityInspector : Editor {
 			List<Attachment> attachments = new List<Attachment>();
 			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);
 	}
-}
+}

+ 7 - 5
spine-unity/Assets/spine-unity/SkeletonUtility/SkeletonUtility.cs

@@ -224,9 +224,9 @@ public class SkeletonUtility : MonoBehaviour {
 		if (boneRoot != null) {
 			List<string> constraintTargetNames = new List<string>();
 
-			foreach (IkConstraint c in skeletonRenderer.skeleton.IkConstraints) {
-				constraintTargetNames.Add(c.Target.Data.Name);
-			}
+			ExposedList<IkConstraint> 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<Bone> 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<Renderer>().enabled = false;
 	}
-}
+}