diff --git a/spine-csharp/src/Animation.cs b/spine-csharp/src/Animation.cs index 9989f4861..a0589396f 100644 --- a/spine-csharp/src/Animation.cs +++ b/spine-csharp/src/Animation.cs @@ -52,7 +52,7 @@ namespace Spine { /// The duration of the animation in seconds, which is the highest time of all keys in the timeline. public float Duration { get { return duration; } set { duration = value; } } - /// The animation's name, which is unique within the skeleton. + /// The animation's name, which is unique across all animations in the skeleton. public string Name { get { return name; } } /// Applies all the animation's timelines to the specified skeleton. diff --git a/spine-csharp/src/Attachments/RegionAttachment.cs b/spine-csharp/src/Attachments/RegionAttachment.cs index f591de110..021069298 100644 --- a/spine-csharp/src/Attachments/RegionAttachment.cs +++ b/spine-csharp/src/Attachments/RegionAttachment.cs @@ -195,8 +195,8 @@ namespace Spine { copy.rotation = rotation; copy.width = width; copy.height = height; - Array.Copy(uvs, 0, copy.uvs, 0, uvs.Length); - Array.Copy(offset, 0, copy.offset, 0, offset.Length); + Array.Copy(uvs, 0, copy.uvs, 0, 8); + Array.Copy(offset, 0, copy.offset, 0, 8); return copy; } } diff --git a/spine-csharp/src/Attachments/VertexAttachment.cs b/spine-csharp/src/Attachments/VertexAttachment.cs index d951248b8..71bb42ea0 100644 --- a/spine-csharp/src/Attachments/VertexAttachment.cs +++ b/spine-csharp/src/Attachments/VertexAttachment.cs @@ -133,8 +133,7 @@ namespace Spine { return this == sourceAttachment; } - ///Internal method used by VertexAttachment subclasses to copy basic data. Does not copy id (generated) and name (set on - /// construction). + ///Does not copy id (generated) or name (set on construction). internal void CopyTo (VertexAttachment attachment) { if (bones != null) { attachment.bones = new int[bones.Length]; diff --git a/spine-csharp/src/Bone.cs b/spine-csharp/src/Bone.cs index 79dcdf5b3..a91d3978e 100644 --- a/spine-csharp/src/Bone.cs +++ b/spine-csharp/src/Bone.cs @@ -52,7 +52,7 @@ namespace Spine { internal float a, b, worldX; internal float c, d, worldY; - internal bool sorted; + internal bool sorted, update; public BoneData Data { get { return data; } } public Skeleton Skeleton { get { return skeleton; } } diff --git a/spine-csharp/src/BoneData.cs b/spine-csharp/src/BoneData.cs index 40eef4ac1..9c80fbe74 100644 --- a/spine-csharp/src/BoneData.cs +++ b/spine-csharp/src/BoneData.cs @@ -37,11 +37,12 @@ namespace Spine { internal float length; internal float x, y, rotation, scaleX = 1, scaleY = 1, shearX, shearY; internal TransformMode transformMode = TransformMode.Normal; + internal bool skinRequired; /// The index of the bone in Skeleton.Bones public int Index { get { return index; } } - /// The name of the bone, which is unique within the skeleton. + /// The name of the bone, which is unique across all bones in the skeleton. public string Name { get { return name; } } /// May be null. @@ -73,6 +74,11 @@ namespace Spine { /// The transform mode for how parent world transforms affect this bone. public TransformMode TransformMode { get { return transformMode; } set { transformMode = value; } } + ///When true, only updates this bone if the contains this + /// bone. + /// + public bool SkinRequired { get { return skinRequired; } set { skinRequired = value; } } + /// May be null. public BoneData (int index, string name, BoneData parent) { if (index < 0) throw new ArgumentException("index must be >= 0", "index"); diff --git a/spine-csharp/src/IConstraint.cs b/spine-csharp/src/ConstraintData.cs similarity index 59% rename from spine-csharp/src/IConstraint.cs rename to spine-csharp/src/ConstraintData.cs index 8f17c0439..7f50cc5fa 100644 --- a/spine-csharp/src/IConstraint.cs +++ b/spine-csharp/src/ConstraintData.cs @@ -27,13 +27,36 @@ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ -namespace Spine { - - /// The interface for all constraints. - public interface IConstraint : IUpdatable { - /// The ordinal for the order a skeleton's constraints will be applied. - int Order { get; } +using System; +using System.Collections.Generic; +namespace Spine +{ + /// The base class for all constraint datas. + public abstract class ConstraintData { + internal readonly string name; + internal int order; + internal bool skinRequired; + + public ConstraintData (string name) { + if (name == null) throw new ArgumentNullException("name", "name cannot be null."); + this.name = name; + } + + /// The constraint's name, which is unique across all constraints in the skeleton of the same type. + public string Name { get { return name; } } + + ///The ordinal of this constraint for the order a skeleton's constraints will be applied by + /// . + public int Order { get { return order; } set { order = value; } } + + ///When true, only updates this constraint if the contains + /// this constraint. + /// + public bool SkinRequired { get { return skinRequired; } set { skinRequired = value; } } + + override public string ToString () { + return name; + } } - } diff --git a/spine-csharp/src/EventData.cs b/spine-csharp/src/EventData.cs index 26bca7efc..2f1928d77 100644 --- a/spine-csharp/src/EventData.cs +++ b/spine-csharp/src/EventData.cs @@ -34,7 +34,7 @@ namespace Spine { public class EventData { internal string name; - /// The name of the event, which is unique within the skeleton. + /// The name of the event, which is unique across all events in the skeleton. public string Name { get { return name; } } public int Int { get; set; } public float Float { get; set; } diff --git a/spine-csharp/src/IkConstraint.cs b/spine-csharp/src/IkConstraint.cs index 8b5ba1a36..92b51f2a7 100644 --- a/spine-csharp/src/IkConstraint.cs +++ b/spine-csharp/src/IkConstraint.cs @@ -37,7 +37,7 @@ namespace Spine { /// /// See IK constraints in the Spine User Guide. /// - public class IkConstraint : IConstraint { + public class IkConstraint : IUpdatable { internal IkConstraintData data; internal ExposedList bones = new ExposedList(); internal Bone target; @@ -93,11 +93,6 @@ namespace Spine { } } - - public int Order { - get { return data.order; } - } - /// The bones that will be modified by this IK constraint. public ExposedList Bones { get { return bones; } diff --git a/spine-csharp/src/IkConstraintData.cs b/spine-csharp/src/IkConstraintData.cs index 5a4f7fca7..de231a39e 100644 --- a/spine-csharp/src/IkConstraintData.cs +++ b/spine-csharp/src/IkConstraintData.cs @@ -32,23 +32,14 @@ using System.Collections.Generic; namespace Spine { /// Stores the setup pose for an IkConstraint. - public class IkConstraintData { - internal string name; - internal int order; + public class IkConstraintData : ConstraintData { internal List bones = new List(); internal BoneData target; internal int bendDirection = 1; internal bool compress, stretch, uniform; internal float mix = 1; - /// The IK constraint's name, which is unique within the skeleton. - public string Name { - get { return name; } - } - - public int Order { - get { return order; } - set { order = value; } + public IkConstraintData (string name) : base(name) { } /// The bones that are constrained by this IK Constraint. @@ -98,14 +89,5 @@ namespace Spine { get { return uniform; } set { uniform = value; } } - - public IkConstraintData (string name) { - if (name == null) throw new ArgumentNullException("name", "name cannot be null."); - this.name = name; - } - - override public string ToString () { - return name; - } } } diff --git a/spine-csharp/src/PathConstraint.cs b/spine-csharp/src/PathConstraint.cs index 74bd08a2c..efaa29a7d 100644 --- a/spine-csharp/src/PathConstraint.cs +++ b/spine-csharp/src/PathConstraint.cs @@ -38,7 +38,7 @@ namespace Spine { /// /// See Path constraints in the Spine User Guide. /// - public class PathConstraint : IConstraint { + public class PathConstraint : IUpdatable { const int NONE = -1, BEFORE = -2, AFTER = -3; const float Epsilon = 0.00001f; @@ -446,7 +446,6 @@ namespace Spine { } } - public int Order { get { return data.order; } } /// The position along the path. public float Position { get { return position; } set { position = value; } } /// The spacing between bones. @@ -461,9 +460,5 @@ namespace Spine { public Slot Target { get { return target; } set { target = value; } } /// The path constraint's setup pose data. public PathConstraintData Data { get { return data; } } - - override public string ToString () { - return data.name; - } } } diff --git a/spine-csharp/src/PathConstraintData.cs b/spine-csharp/src/PathConstraintData.cs index 924c99a06..a8f5a4e44 100644 --- a/spine-csharp/src/PathConstraintData.cs +++ b/spine-csharp/src/PathConstraintData.cs @@ -30,9 +30,7 @@ using System; namespace Spine { - public class PathConstraintData { - internal string name; - internal int order; + public class PathConstraintData : ConstraintData { internal ExposedList bones = new ExposedList(); internal SlotData target; internal PositionMode positionMode; @@ -41,8 +39,9 @@ namespace Spine { internal float offsetRotation; internal float position, spacing, rotateMix, translateMix; - public string Name { get { return name; } } - public int Order { get { return order; } set { order = value; } } + public PathConstraintData (string name) : base(name) { + } + public ExposedList Bones { get { return bones; } } public SlotData Target { get { return target; } set { target = value; } } public PositionMode PositionMode { get { return positionMode; } set { positionMode = value; } } @@ -53,15 +52,6 @@ namespace Spine { public float Spacing { get { return spacing; } set { spacing = value; } } public float RotateMix { get { return rotateMix; } set { rotateMix = value; } } public float TranslateMix { get { return translateMix; } set { translateMix = value; } } - - public PathConstraintData (String name) { - if (name == null) throw new ArgumentNullException("name", "name cannot be null."); - this.name = name; - } - - public override string ToString () { - return name; - } } public enum PositionMode { diff --git a/spine-csharp/src/Skeleton.cs b/spine-csharp/src/Skeleton.cs index 6385fd64b..af8ddc642 100644 --- a/spine-csharp/src/Skeleton.cs +++ b/spine-csharp/src/Skeleton.cs @@ -55,7 +55,7 @@ namespace Spine { public ExposedList IkConstraints { get { return ikConstraints; } } public ExposedList PathConstraints { get { return pathConstraints; } } public ExposedList TransformConstraints { get { return transformConstraints; } } - public Skin Skin { get { return skin; } set { skin = value; } } + public Skin Skin { get { return skin; } set { SetSkin(value); } } public float R { get { return r; } set { r = value; } } public float G { get { return g; } set { g = value; } } public float B { get { return b; } set { b = value; } } @@ -118,21 +118,25 @@ namespace Spine { UpdateWorldTransform(); } - /// Caches information about bones and constraints. Must be called if bones, constraints or weighted path attachments are added - /// or removed. + /// Caches information about bones and constraints. Must be called if the skin is modified or if bones, constraints, or + /// weighted path attachments are added or removed. public void UpdateCache () { var updateCache = this.updateCache; updateCache.Clear(); this.updateCacheReset.Clear(); + int boneCount = this.bones.Items.Length; var bones = this.bones; - for (int i = 0, n = bones.Count; i < n; i++) - bones.Items[i].sorted = false; + for (int i = 0; i < boneCount; i++) { + Bone bone = bones.Items[i]; + bone.update = !bone.data.skinRequired || (skin != null && skin.bones.Contains(bone.data)); + bone.sorted = !bone.update; + } + int ikCount = this.ikConstraints.Count, transformCount = this.transformConstraints.Count, pathCount = this.pathConstraints.Count; var ikConstraints = this.ikConstraints; var transformConstraints = this.transformConstraints; var pathConstraints = this.pathConstraints; - int ikCount = ikConstraints.Count, transformCount = transformConstraints.Count, pathCount = pathConstraints.Count; int constraintCount = ikCount + transformCount + pathCount; //outer: for (int i = 0; i < constraintCount; i++) { @@ -160,11 +164,13 @@ namespace Spine { continue_outer: {} } - for (int i = 0, n = bones.Count; i < n; i++) + for (int i = 0; i < boneCount; i++) SortBone(bones.Items[i]); } private void SortIkConstraint (IkConstraint constraint) { + if (constraint.data.skinRequired && (skin == null || !skin.constraints.Contains(constraint.data))) return; + Bone target = constraint.target; SortBone(target); @@ -185,15 +191,15 @@ namespace Spine { } private void SortPathConstraint (PathConstraint constraint) { + if (constraint.data.skinRequired && (skin == null || !skin.constraints.Contains(constraint.data))) return; + Slot slot = constraint.target; int slotIndex = slot.data.index; Bone slotBone = slot.bone; if (skin != null) SortPathConstraintAttachment(skin, slotIndex, slotBone); if (data.defaultSkin != null && data.defaultSkin != skin) SortPathConstraintAttachment(data.defaultSkin, slotIndex, slotBone); - for (int ii = 0, nn = data.skins.Count; ii < nn; ii++) - SortPathConstraintAttachment(data.skins.Items[ii], slotIndex, slotBone); - + Attachment attachment = slot.attachment; if (attachment is PathAttachment) SortPathConstraintAttachment(attachment, slotBone); @@ -211,6 +217,8 @@ namespace Spine { } private void SortTransformConstraint (TransformConstraint constraint) { + if (constraint.data.skinRequired && (skin == null || !skin.constraints.Contains(constraint.data))) return; + SortBone(constraint.target); var constrained = constraint.bones; @@ -269,6 +277,7 @@ namespace Spine { var bonesItems = bones.Items; for (int i = 0, n = bones.Count; i < n; i++) { Bone bone = bonesItems[i]; + if (!bone.update) continue; if (bone.sorted) SortReset(bone.children); bone.sorted = false; } @@ -295,7 +304,8 @@ namespace Spine { } /// - /// Updates the world transform for each bone and applies all constraints. The root bone will be temporarily parented to the specified bone. + /// Temporarily sets the root bone as a child of the specified bone, then updates the world transform for each bone and applies + /// all constraints. /// public void UpdateWorldTransform (Bone parent) { // This partial update avoids computing the world transform for constrained bones when 1) the bone is not updated @@ -315,8 +325,7 @@ namespace Spine { bone.appliedValid = true; } - // Apply the parent bone transform to the root bone. The root bone - // always inherits scale, rotation and reflection. + // Apply the parent bone transform to the root bone. The root bone always inherits scale, rotation and reflection. Bone rootBone = this.RootBone; float pa = parent.a, pb = parent.b, pc = parent.c, pd = parent.d; rootBone.worldX = pa * x + pb * y + parent.worldX; @@ -447,8 +456,12 @@ namespace Spine { } /// - /// Attachments from the new skin are attached if the corresponding attachment from the old skin was attached. - /// If there was no old skin, each slot's setup mode attachment is attached from the new skin. + /// Sets the skin used to look up attachments before looking in the . If the + /// skin is changed, is called. + /// + /// Attachments from the new skin are attached if the corresponding attachment from the old skin was attached. + /// If there was no old skin, each slot's setup mode attachment is attached from the new skin. + /// /// After changing the skin, the visible attachments can be reset to those attached in the setup pose by calling /// . /// Also, often is called before the next time the @@ -456,6 +469,7 @@ namespace Spine { /// /// May be null. public void SetSkin (Skin newSkin) { + if (newSkin == skin) return; if (newSkin != null) { if (skin != null) newSkin.AttachAll(this, skin); @@ -472,6 +486,7 @@ namespace Spine { } } skin = newSkin; + UpdateCache(); } /// Finds an attachment by looking in the {@link #skin} and {@link SkeletonData#defaultSkin} using the slot name and attachment name. @@ -528,7 +543,7 @@ namespace Spine { ExposedList transformConstraints = this.transformConstraints; for (int i = 0, n = transformConstraints.Count; i < n; i++) { TransformConstraint transformConstraint = transformConstraints.Items[i]; - if (transformConstraint.data.name == constraintName) return transformConstraint; + if (transformConstraint.data.Name == constraintName) return transformConstraint; } return null; } @@ -539,7 +554,7 @@ namespace Spine { ExposedList pathConstraints = this.pathConstraints; for (int i = 0, n = pathConstraints.Count; i < n; i++) { PathConstraint constraint = pathConstraints.Items[i]; - if (constraint.data.name.Equals(constraintName)) return constraint; + if (constraint.data.Name.Equals(constraintName)) return constraint; } return null; } diff --git a/spine-csharp/src/SkeletonBinary.cs b/spine-csharp/src/SkeletonBinary.cs index 126149461..a660ddda4 100644 --- a/spine-csharp/src/SkeletonBinary.cs +++ b/spine-csharp/src/SkeletonBinary.cs @@ -174,6 +174,7 @@ namespace Spine { data.shearY = ReadFloat(input); data.length = ReadFloat(input) * scale; data.transformMode = TransformModeValues[ReadVarint(input, true)]; + data.skinRequired = ReadBoolean(input); if (nonessential) ReadInt(input); // Skip bone color. skeletonData.bones.Add(data); } @@ -206,6 +207,7 @@ namespace Spine { for (int i = 0, n = ReadVarint(input, true); i < n; i++) { IkConstraintData data = new IkConstraintData(ReadString(input)); data.order = ReadVarint(input, true); + data.skinRequired = ReadBoolean(input); for (int ii = 0, nn = ReadVarint(input, true); ii < nn; ii++) data.bones.Add(skeletonData.bones.Items[ReadVarint(input, true)]); data.target = skeletonData.bones.Items[ReadVarint(input, true)]; @@ -221,6 +223,7 @@ namespace Spine { for (int i = 0, n = ReadVarint(input, true); i < n; i++) { TransformConstraintData data = new TransformConstraintData(ReadString(input)); data.order = ReadVarint(input, true); + data.skinRequired = ReadBoolean(input); for (int ii = 0, nn = ReadVarint(input, true); ii < nn; ii++) data.bones.Add(skeletonData.bones.Items[ReadVarint(input, true)]); data.target = skeletonData.bones.Items[ReadVarint(input, true)]; @@ -243,6 +246,7 @@ namespace Spine { for (int i = 0, n = ReadVarint(input, true); i < n; i++) { PathConstraintData data = new PathConstraintData(ReadString(input)); data.order = ReadVarint(input, true); + data.skinRequired = ReadBoolean(input); for (int ii = 0, nn = ReadVarint(input, true); ii < nn; ii++) data.bones.Add(skeletonData.bones.Items[ReadVarint(input, true)]); data.target = skeletonData.slots.Items[ReadVarint(input, true)]; @@ -260,7 +264,7 @@ namespace Spine { } // Default skin. - Skin defaultSkin = ReadSkin(input, skeletonData, "default", nonessential); + Skin defaultSkin = ReadSkin(input, skeletonData, true, nonessential); if (defaultSkin != null) { skeletonData.defaultSkin = defaultSkin; skeletonData.skins.Add(defaultSkin); @@ -268,7 +272,7 @@ namespace Spine { // Skins. for (int i = 0, n = ReadVarint(input, true); i < n; i++) - skeletonData.skins.Add(ReadSkin(input, skeletonData, ReadString(input), nonessential)); + skeletonData.skins.Add(ReadSkin(input, skeletonData, false, nonessential)); // Linked meshes. for (int i = 0, n = linkedMeshes.Count; i < n; i++) { @@ -312,11 +316,19 @@ namespace Spine { /// May be null. - private Skin ReadSkin (Stream input, SkeletonData skeletonData, String skinName, bool nonessential) { - int slotCount = ReadVarint(input, true); - if (slotCount == 0) return null; - Skin skin = new Skin(skinName); - for (int i = 0; i < slotCount; i++) { + private Skin ReadSkin (Stream input, SkeletonData skeletonData, bool defaultSkin, bool nonessential) { + Skin skin = new Skin(defaultSkin ? "default" : ReadString(input)); + if (!defaultSkin) { + for (int i = 0, n = ReadVarint(input, true); i < n; i++) + skin.bones.Add(skeletonData.bones.Items[ReadVarint(input, true)]); + for (int i = 0, n = ReadVarint(input, true); i < n; i++) + skin.constraints.Add(skeletonData.ikConstraints.Items[ReadVarint(input, true)]); + for (int i = 0, n = ReadVarint(input, true); i < n; i++) + skin.constraints.Add(skeletonData.transformConstraints.Items[ReadVarint(input, true)]); + for (int i = 0, n = ReadVarint(input, true); i < n; i++) + skin.constraints.Add(skeletonData.pathConstraints.Items[ReadVarint(input, true)]); + } + for (int i = 0, n = ReadVarint(input, true); i < n; i++) { int slotIndex = ReadVarint(input, true); for (int ii = 0, nn = ReadVarint(input, true); ii < nn; ii++) { String name = ReadString(input); diff --git a/spine-csharp/src/SkeletonJson.cs b/spine-csharp/src/SkeletonJson.cs index 01a132995..b42d35276 100644 --- a/spine-csharp/src/SkeletonJson.cs +++ b/spine-csharp/src/SkeletonJson.cs @@ -109,27 +109,30 @@ namespace Spine { } // Bones. - foreach (Dictionary boneMap in (List)root["bones"]) { - BoneData parent = null; - if (boneMap.ContainsKey("parent")) { - parent = skeletonData.FindBone((string)boneMap["parent"]); - if (parent == null) - throw new Exception("Parent bone not found: " + boneMap["parent"]); + if (root.ContainsKey("bones")) { + foreach (Dictionary boneMap in (List)root["bones"]) { + BoneData parent = null; + if (boneMap.ContainsKey("parent")) { + parent = skeletonData.FindBone((string)boneMap["parent"]); + if (parent == null) + throw new Exception("Parent bone not found: " + boneMap["parent"]); + } + var data = new BoneData(skeletonData.Bones.Count, (string)boneMap["name"], parent); + data.length = GetFloat(boneMap, "length", 0) * scale; + data.x = GetFloat(boneMap, "x", 0) * scale; + data.y = GetFloat(boneMap, "y", 0) * scale; + data.rotation = GetFloat(boneMap, "rotation", 0); + data.scaleX = GetFloat(boneMap, "scaleX", 1); + data.scaleY = GetFloat(boneMap, "scaleY", 1); + data.shearX = GetFloat(boneMap, "shearX", 0); + data.shearY = GetFloat(boneMap, "shearY", 0); + + string tm = GetString(boneMap, "transform", TransformMode.Normal.ToString()); + data.transformMode = (TransformMode)Enum.Parse(typeof(TransformMode), tm, true); + data.skinRequired = GetBoolean(boneMap, "skin", false); + + skeletonData.bones.Add(data); } - var data = new BoneData(skeletonData.Bones.Count, (string)boneMap["name"], parent); - data.length = GetFloat(boneMap, "length", 0) * scale; - data.x = GetFloat(boneMap, "x", 0) * scale; - data.y = GetFloat(boneMap, "y", 0) * scale; - data.rotation = GetFloat(boneMap, "rotation", 0); - data.scaleX = GetFloat(boneMap, "scaleX", 1); - data.scaleY = GetFloat(boneMap, "scaleY", 1); - data.shearX = GetFloat(boneMap, "shearX", 0); - data.shearY = GetFloat(boneMap, "shearY", 0); - - string tm = GetString(boneMap, "transform", TransformMode.Normal.ToString()); - data.transformMode = (TransformMode)Enum.Parse(typeof(TransformMode), tm, true); - - skeletonData.bones.Add(data); } // Slots. @@ -171,16 +174,19 @@ namespace Spine { foreach (Dictionary constraintMap in (List)root["ik"]) { IkConstraintData data = new IkConstraintData((string)constraintMap["name"]); data.order = GetInt(constraintMap, "order", 0); + data.skinRequired = GetBoolean(constraintMap,"skin", false); - foreach (string boneName in (List)constraintMap["bones"]) { - BoneData bone = skeletonData.FindBone(boneName); - if (bone == null) throw new Exception("IK constraint bone not found: " + boneName); - data.bones.Add(bone); + if (constraintMap.ContainsKey("bones")) { + foreach (string boneName in (List)constraintMap["bones"]) { + BoneData bone = skeletonData.FindBone(boneName); + if (bone == null) throw new Exception("IK bone not found: " + boneName); + data.bones.Add(bone); + } } string targetName = (string)constraintMap["target"]; data.target = skeletonData.FindBone(targetName); - if (data.target == null) throw new Exception("Target bone not found: " + targetName); + if (data.target == null) throw new Exception("IK target bone not found: " + targetName); data.mix = GetFloat(constraintMap, "mix", 1); data.bendDirection = GetBoolean(constraintMap, "bendPositive", true) ? 1 : -1; data.compress = GetBoolean(constraintMap, "compress", false); @@ -196,16 +202,19 @@ namespace Spine { foreach (Dictionary constraintMap in (List)root["transform"]) { TransformConstraintData data = new TransformConstraintData((string)constraintMap["name"]); data.order = GetInt(constraintMap, "order", 0); + data.skinRequired = GetBoolean(constraintMap,"skin", false); - foreach (string boneName in (List)constraintMap["bones"]) { - BoneData bone = skeletonData.FindBone(boneName); - if (bone == null) throw new Exception("Transform constraint bone not found: " + boneName); - data.bones.Add(bone); + if (constraintMap.ContainsKey("bones")) { + foreach (string boneName in (List)constraintMap["bones"]) { + BoneData bone = skeletonData.FindBone(boneName); + if (bone == null) throw new Exception("Transform constraint bone not found: " + boneName); + data.bones.Add(bone); + } } string targetName = (string)constraintMap["target"]; data.target = skeletonData.FindBone(targetName); - if (data.target == null) throw new Exception("Target bone not found: " + targetName); + if (data.target == null) throw new Exception("Transform constraint target bone not found: " + targetName); data.local = GetBoolean(constraintMap, "local", false); data.relative = GetBoolean(constraintMap, "relative", false); @@ -231,16 +240,19 @@ namespace Spine { foreach (Dictionary constraintMap in (List)root["path"]) { PathConstraintData data = new PathConstraintData((string)constraintMap["name"]); data.order = GetInt(constraintMap, "order", 0); + data.skinRequired = GetBoolean(constraintMap,"skin", false); - foreach (string boneName in (List)constraintMap["bones"]) { - BoneData bone = skeletonData.FindBone(boneName); - if (bone == null) throw new Exception("Path bone not found: " + boneName); - data.bones.Add(bone); + if (constraintMap.ContainsKey("bones")) { + foreach (string boneName in (List)constraintMap["bones"]) { + BoneData bone = skeletonData.FindBone(boneName); + if (bone == null) throw new Exception("Path bone not found: " + boneName); + data.bones.Add(bone); + } } string targetName = (string)constraintMap["target"]; data.target = skeletonData.FindSlot(targetName); - if (data.target == null) throw new Exception("Target slot not found: " + targetName); + if (data.target == null) throw new Exception("Path target slot not found: " + targetName); data.positionMode = (PositionMode)Enum.Parse(typeof(PositionMode), GetString(constraintMap, "positionMode", "percent"), true); data.spacingMode = (SpacingMode)Enum.Parse(typeof(SpacingMode), GetString(constraintMap, "spacingMode", "length"), true); @@ -259,18 +271,48 @@ namespace Spine { // Skins. if (root.ContainsKey("skins")) { - foreach (KeyValuePair skinMap in (Dictionary)root["skins"]) { - var skin = new Skin(skinMap.Key); - foreach (KeyValuePair slotEntry in (Dictionary)skinMap.Value) { - int slotIndex = skeletonData.FindSlotIndex(slotEntry.Key); - foreach (KeyValuePair entry in ((Dictionary)slotEntry.Value)) { - try { - Attachment attachment = ReadAttachment((Dictionary)entry.Value, skin, slotIndex, entry.Key, skeletonData); - if (attachment != null) skin.SetAttachment(slotIndex, entry.Key, attachment); - } catch (Exception e) { - throw new Exception("Error reading attachment: " + entry.Key + ", skin: " + skin, e); - } - } + foreach (Dictionary skinMap in (List)root["skins"]) { + Skin skin = new Skin((string)skinMap["name"]); + if (skinMap.ContainsKey("bones")) { + foreach (string entryName in (List)skinMap["bones"]) { + BoneData bone = skeletonData.FindBone(entryName); + if (bone == null) throw new Exception("Skin bone not found: " + entryName); + skin.bones.Add(bone); + } + } + if (skinMap.ContainsKey("ik")) { + foreach (string entryName in (List)skinMap["ik"]) { + IkConstraintData constraint = skeletonData.FindIkConstraint(entryName); + if (constraint == null) throw new Exception("Skin IK constraint not found: " + entryName); + skin.constraints.Add(constraint); + } + } + if (skinMap.ContainsKey("transform")) { + foreach (string entryName in (List)skinMap["transform"]) { + TransformConstraintData constraint = skeletonData.FindTransformConstraint(entryName); + if (constraint == null) throw new Exception("Skin transform constraint not found: " + entryName); + skin.constraints.Add(constraint); + } + } + if (skinMap.ContainsKey("path")) { + foreach (string entryName in (List)skinMap["path"]) { + PathConstraintData constraint = skeletonData.FindPathConstraint(entryName); + if (constraint == null) throw new Exception("Skin path constraint not found: " + entryName); + skin.constraints.Add(constraint); + } + } + if (skinMap.ContainsKey("attachments")) { + foreach (KeyValuePair slotEntry in (Dictionary)skinMap["attachments"]) { + int slotIndex = skeletonData.FindSlotIndex(slotEntry.Key); + foreach (KeyValuePair entry in ((Dictionary)slotEntry.Value)) { + try { + Attachment attachment = ReadAttachment((Dictionary)entry.Value, skin, slotIndex, entry.Key, skeletonData); + if (attachment != null) skin.SetAttachment(slotIndex, entry.Key, attachment); + } catch (Exception e) { + throw new Exception("Error reading attachment: " + entry.Key + ", skin: " + skin, e); + } + } + } } skeletonData.skins.Add(skin); if (skin.name == "default") skeletonData.defaultSkin = skin; @@ -494,7 +536,7 @@ namespace Spine { int frameIndex = 0; foreach (Dictionary valueMap in values) { - float time = (float)valueMap["time"]; + float time = GetFloat(valueMap, "time", 0); timeline.SetFrame(frameIndex++, time, (string)valueMap["name"]); } timelines.Add(timeline); @@ -506,7 +548,7 @@ namespace Spine { int frameIndex = 0; foreach (Dictionary valueMap in values) { - float time = (float)valueMap["time"]; + float time = GetFloat(valueMap, "time", 0); string c = (string)valueMap["color"]; timeline.SetFrame(frameIndex, time, ToColor(c, 0), ToColor(c, 1), ToColor(c, 2), ToColor(c, 3)); ReadCurve(valueMap, timeline, frameIndex); @@ -521,7 +563,7 @@ namespace Spine { int frameIndex = 0; foreach (Dictionary valueMap in values) { - float time = (float)valueMap["time"]; + float time = GetFloat(valueMap, "time", 0); string light = (string)valueMap["light"]; string dark = (string)valueMap["dark"]; timeline.SetFrame(frameIndex, time, ToColor(light, 0), ToColor(light, 1), ToColor(light, 2), ToColor(light, 3), @@ -554,7 +596,7 @@ namespace Spine { int frameIndex = 0; foreach (Dictionary valueMap in values) { - timeline.SetFrame(frameIndex, (float)valueMap["time"], (float)valueMap["angle"]); + timeline.SetFrame(frameIndex, GetFloat(valueMap, "time", 0), GetFloat(valueMap, "angle", 0)); ReadCurve(valueMap, timeline, frameIndex); frameIndex++; } @@ -563,9 +605,11 @@ namespace Spine { } else if (timelineName == "translate" || timelineName == "scale" || timelineName == "shear") { TranslateTimeline timeline; - float timelineScale = 1; - if (timelineName == "scale") + float timelineScale = 1, defaultValue = 0; + if (timelineName == "scale") { timeline = new ScaleTimeline(values.Count); + defaultValue = 1; + } else if (timelineName == "shear") timeline = new ShearTimeline(values.Count); else { @@ -576,9 +620,9 @@ namespace Spine { int frameIndex = 0; foreach (Dictionary valueMap in values) { - float time = (float)valueMap["time"]; - float x = GetFloat(valueMap, "x", 0); - float y = GetFloat(valueMap, "y", 0); + float time = GetFloat(valueMap, "time", 0); + float x = GetFloat(valueMap, "x", defaultValue); + float y = GetFloat(valueMap, "y", defaultValue); timeline.SetFrame(frameIndex, time, x * timelineScale, y * timelineScale); ReadCurve(valueMap, timeline, frameIndex); frameIndex++; @@ -603,7 +647,7 @@ namespace Spine { foreach (Dictionary valueMap in values) { timeline.SetFrame( frameIndex, - (float)valueMap["time"], + GetFloat(valueMap, "time", 0), GetFloat(valueMap, "mix", 1), GetBoolean(valueMap, "bendPositive", true) ? 1 : -1, GetBoolean(valueMap, "compress", true), @@ -626,7 +670,7 @@ namespace Spine { timeline.transformConstraintIndex = skeletonData.transformConstraints.IndexOf(constraint); int frameIndex = 0; foreach (Dictionary valueMap in values) { - float time = (float)valueMap["time"]; + float time = GetFloat(valueMap, "time", 0); float rotateMix = GetFloat(valueMap, "rotateMix", 1); float translateMix = GetFloat(valueMap, "translateMix", 1); float scaleMix = GetFloat(valueMap, "scaleMix", 1); @@ -641,8 +685,8 @@ namespace Spine { } // Path constraint timelines. - if (map.ContainsKey("paths")) { - foreach (KeyValuePair constraintMap in (Dictionary)map["paths"]) { + if (map.ContainsKey("path")) { + foreach (KeyValuePair constraintMap in (Dictionary)map["path"]) { int index = skeletonData.FindPathConstraintIndex(constraintMap.Key); if (index == -1) throw new Exception("Path constraint not found: " + constraintMap.Key); PathConstraintData data = skeletonData.pathConstraints.Items[index]; @@ -664,7 +708,7 @@ namespace Spine { timeline.pathConstraintIndex = index; int frameIndex = 0; foreach (Dictionary valueMap in values) { - timeline.SetFrame(frameIndex, (float)valueMap["time"], GetFloat(valueMap, timelineName, 0) * timelineScale); + timeline.SetFrame(frameIndex, GetFloat(valueMap, "time", 0), GetFloat(valueMap, timelineName, 0) * timelineScale); ReadCurve(valueMap, timeline, frameIndex); frameIndex++; } @@ -676,7 +720,7 @@ namespace Spine { timeline.pathConstraintIndex = index; int frameIndex = 0; foreach (Dictionary valueMap in values) { - timeline.SetFrame(frameIndex, (float)valueMap["time"], GetFloat(valueMap, "rotateMix", 1), GetFloat(valueMap, "translateMix", 1)); + timeline.SetFrame(frameIndex, GetFloat(valueMap, "time", 0), GetFloat(valueMap, "rotateMix", 1), GetFloat(valueMap, "translateMix", 1)); ReadCurve(valueMap, timeline, frameIndex); frameIndex++; } @@ -727,7 +771,7 @@ namespace Spine { } } - timeline.SetFrame(frameIndex, (float)valueMap["time"], deform); + timeline.SetFrame(frameIndex, GetFloat(valueMap, "time", 0), deform); ReadCurve(valueMap, timeline, frameIndex); frameIndex++; } @@ -770,7 +814,7 @@ namespace Spine { for (int i = slotCount - 1; i >= 0; i--) if (drawOrder[i] == -1) drawOrder[i] = unchanged[--unchangedIndex]; } - timeline.SetFrame(frameIndex++, (float)drawOrderMap["time"], drawOrder); + timeline.SetFrame(frameIndex++, GetFloat(drawOrderMap, "time", 0), drawOrder); } timelines.Add(timeline); duration = Math.Max(duration, timeline.frames[timeline.FrameCount - 1]); @@ -784,7 +828,7 @@ namespace Spine { foreach (Dictionary eventMap in eventsMap) { EventData eventData = skeletonData.FindEvent((string)eventMap["name"]); if (eventData == null) throw new Exception("Event not found: " + eventMap["name"]); - var e = new Event((float)eventMap["time"], eventData) { + var e = new Event(GetFloat(eventMap, "time", 0), eventData) { intValue = GetInt(eventMap, "int", eventData.Int), floatValue = GetFloat(eventMap, "float", eventData.Float), stringValue = GetString(eventMap, "string", eventData.String) @@ -807,12 +851,12 @@ namespace Spine { if (!valueMap.ContainsKey("curve")) return; Object curveObject = valueMap["curve"]; - if (curveObject.Equals("stepped")) + if (curveObject is string) timeline.SetStepped(frameIndex); else { var curve = curveObject as List; if (curve != null) - timeline.SetCurve(frameIndex, (float)curve[0], (float)curve[1], (float)curve[2], (float)curve[3]); + timeline.SetCurve(frameIndex, (float)curveObject, GetFloat(valueMap, "c2", 0), GetFloat(valueMap, "c3", 1), GetFloat(valueMap, "c4", 1)); } } diff --git a/spine-csharp/src/Skin.cs b/spine-csharp/src/Skin.cs index 4771061c1..e57b3d33d 100644 --- a/spine-csharp/src/Skin.cs +++ b/spine-csharp/src/Skin.cs @@ -40,9 +40,13 @@ namespace Spine { public class Skin { internal string name; private OrderedDictionary attachments = new OrderedDictionary(SkinEntryComparer.Instance); // contains - + internal readonly ExposedList bones = new ExposedList(); + internal readonly ExposedList constraints = new ExposedList(); + public string Name { get { return name; } } public OrderedDictionary Attachments { get { return attachments; } } + public ExposedList Bones { get { return bones; } } + public ExposedList Constraints { get { return constraints; } } public Skin (string name) { if (name == null) throw new ArgumentNullException("name", "name cannot be null."); @@ -59,13 +63,23 @@ namespace Spine { ///Adds all attachments, bones, and constraints from the specified skin to this skin. public void AddSkin (Skin skin) { + foreach (BoneData data in skin.bones) + if (!bones.Contains(data)) bones.Add(data); + + foreach (ConstraintData data in skin.constraints) + if (!constraints.Contains(data)) constraints.Add(data); + foreach (SkinEntry entry in skin.attachments.Keys) SetAttachment(entry.SlotIndex, entry.Name, entry.Attachment); } ///Adds all attachments from the specified skin to this skin. Attachments are deep copied. public void CopySkin (Skin skin) { - // note: bones and constraints are added in a separate commit. + foreach (BoneData data in skin.bones) + if (!bones.Contains(data)) bones.Add(data); + + foreach (ConstraintData data in skin.constraints) + if (!constraints.Contains(data)) constraints.Add(data); foreach (SkinEntry entry in skin.attachments.Keys) { Attachment attachment = entry.Attachment.Copy(); @@ -98,13 +112,20 @@ namespace Spine { return entries; } - /// Returns all attachments for the given slot in this skin. + /// Returns all attachments in this skin for the specified slot index. /// The target slotIndex. To find the slot index, use or public void GetAttachments (int slotIndex, List attachments) { foreach (SkinEntry entry in this.attachments.Keys) if (entry.SlotIndex == slotIndex) attachments.Add(entry); } + ///Clears all attachments, bones, and constraints. + public void Clear () { + attachments.Clear(); + bones.Clear(); + constraints.Clear(); + } + override public string ToString () { return name; } @@ -141,6 +162,7 @@ namespace Spine { } } + /// The name the attachment is associated with, equivalent to the skin placeholder name in the Spine editor. public String Name { get { return name; diff --git a/spine-csharp/src/SlotData.cs b/spine-csharp/src/SlotData.cs index 4819fefd2..489b9b9b8 100644 --- a/spine-csharp/src/SlotData.cs +++ b/spine-csharp/src/SlotData.cs @@ -40,8 +40,11 @@ namespace Spine { internal string attachmentName; internal BlendMode blendMode; + /// The index of the slot in . public int Index { get { return index; } } + /// The name of the slot, which is unique across all slots in the skeleton. public string Name { get { return name; } } + /// The bone this slot belongs to. public BoneData BoneData { get { return boneData; } } public float R { get { return r; } set { r = value; } } public float G { get { return g; } set { g = value; } } @@ -53,8 +56,9 @@ namespace Spine { public float B2 { get { return b2; } set { b2 = value; } } public bool HasSecondColor { get { return hasSecondColor; } set { hasSecondColor = value; } } - /// May be null. + /// The name of the attachment that is visible for this slot in the setup pose, or null if no attachment is visible. public String AttachmentName { get { return attachmentName; } set { attachmentName = value; } } + /// The blend mode for drawing the slot's attachment. public BlendMode BlendMode { get { return blendMode; } set { blendMode = value; } } public SlotData (int index, String name, BoneData boneData) { diff --git a/spine-csharp/src/TransformConstraint.cs b/spine-csharp/src/TransformConstraint.cs index a2d7d184f..9ab64cdf0 100644 --- a/spine-csharp/src/TransformConstraint.cs +++ b/spine-csharp/src/TransformConstraint.cs @@ -37,7 +37,7 @@ namespace Spine { /// /// See Transform constraints in the Spine User Guide. /// - public class TransformConstraint : IConstraint { + public class TransformConstraint : IUpdatable { internal TransformConstraintData data; internal ExposedList bones; internal Bone target; @@ -288,7 +288,6 @@ namespace Spine { } } - public int Order { get { return data.order; } } /// The bones that will be modified by this transform constraint. public ExposedList Bones { get { return bones; } } /// The target bone whose world transform will be copied to the constrained bones. diff --git a/spine-csharp/src/TransformConstraintData.cs b/spine-csharp/src/TransformConstraintData.cs index 1adc43dc4..0793c6355 100644 --- a/spine-csharp/src/TransformConstraintData.cs +++ b/spine-csharp/src/TransformConstraintData.cs @@ -30,17 +30,13 @@ using System; namespace Spine { - public class TransformConstraintData { - internal string name; - internal int order; + public class TransformConstraintData : ConstraintData { internal ExposedList bones = new ExposedList(); internal BoneData target; internal float rotateMix, translateMix, scaleMix, shearMix; internal float offsetRotation, offsetX, offsetY, offsetScaleX, offsetScaleY, offsetShearY; internal bool relative, local; - public string Name { get { return name; } } - public int Order { get { return order; } set { order = value; } } public ExposedList Bones { get { return bones; } } public BoneData Target { get { return target; } set { target = value; } } public float RotateMix { get { return rotateMix; } set { rotateMix = value; } } @@ -58,13 +54,7 @@ namespace Spine { public bool Relative { get { return relative; } set { relative = value; } } public bool Local { get { return local; } set { local = value; } } - public TransformConstraintData (string name) { - if (name == null) throw new ArgumentNullException("name", "name cannot be null."); - this.name = name; - } - - override public string ToString () { - return name; + public TransformConstraintData (string name) : base(name) { } } } diff --git a/spine-unity/Assets/Spine/Editor/spine-unity/Editor/SkeletonRendererInspector.cs b/spine-unity/Assets/Spine/Editor/spine-unity/Editor/SkeletonRendererInspector.cs index 567d44611..025e4aaa4 100644 --- a/spine-unity/Assets/Spine/Editor/spine-unity/Editor/SkeletonRendererInspector.cs +++ b/spine-unity/Assets/Spine/Editor/spine-unity/Editor/SkeletonRendererInspector.cs @@ -504,8 +504,17 @@ namespace Spine.Unity.Editor { if (!fieldMatchesSkin) { Skin skinToSet = string.IsNullOrEmpty(componentSkinName) ? null : skeletonRenderer.Skeleton.Data.FindSkin(componentSkinName); - skeletonRenderer.Skeleton.Skin = skinToSet; + skeletonRenderer.Skeleton.SetSkin(skinToSet); skeletonRenderer.Skeleton.SetSlotsToSetupPose(); + + // Note: the UpdateIfSkinMismatch concept shall be replaced with e.g. an OnValidate based + // solution or in a separate commit. The current solution does not repaint the Game view because + // it is first applying values and in the next editor pass is calling this skin-changing method. + if (skeletonRenderer is SkeletonAnimation) + ((SkeletonAnimation) skeletonRenderer).Update(0f); + else if (skeletonRenderer is SkeletonMecanim) + ((SkeletonMecanim) skeletonRenderer).Update(); + skeletonRenderer.LateUpdate(); return true; } diff --git a/spine-unity/Assets/Spine/Editor/spine-unity/Editor/SpineEditorUtilities.cs b/spine-unity/Assets/Spine/Editor/spine-unity/Editor/SpineEditorUtilities.cs index 46e161cbf..29e69aa7c 100644 --- a/spine-unity/Assets/Spine/Editor/spine-unity/Editor/SpineEditorUtilities.cs +++ b/spine-unity/Assets/Spine/Editor/spine-unity/Editor/SpineEditorUtilities.cs @@ -707,9 +707,10 @@ namespace Spine.Unity.Editor { if (root == null || !root.ContainsKey("skins")) return requiredPaths; - foreach (var skin in (Dictionary)root["skins"]) { - foreach (var slot in (Dictionary)skin.Value) { - + foreach (Dictionary skinMap in (List)root["skins"]) { + if (!skinMap.ContainsKey("attachments")) + continue; + foreach (var slot in (Dictionary)skinMap["attachments"]) { foreach (var attachment in ((Dictionary)slot.Value)) { var data = ((Dictionary)attachment.Value);