[csharp] See #1346: Port bone/constraint association with skins. Also contains second (and final) partial port of commit ff5b854. Adapted spine-unity for skin changes. Fixed a bug in Skin setter property.

This commit is contained in:
Harald Csaszar 2019-05-22 18:29:54 +02:00
parent 1eea0c24c9
commit c63bc7b88f
20 changed files with 260 additions and 174 deletions

View File

@ -52,7 +52,7 @@ namespace Spine {
/// <summary>The duration of the animation in seconds, which is the highest time of all keys in the timeline.</summary> /// <summary>The duration of the animation in seconds, which is the highest time of all keys in the timeline.</summary>
public float Duration { get { return duration; } set { duration = value; } } public float Duration { get { return duration; } set { duration = value; } }
/// <summary>The animation's name, which is unique within the skeleton.</summary> /// <summary>The animation's name, which is unique across all animations in the skeleton.</summary>
public string Name { get { return name; } } public string Name { get { return name; } }
/// <summary>Applies all the animation's timelines to the specified skeleton.</summary> /// <summary>Applies all the animation's timelines to the specified skeleton.</summary>

View File

@ -195,8 +195,8 @@ namespace Spine {
copy.rotation = rotation; copy.rotation = rotation;
copy.width = width; copy.width = width;
copy.height = height; copy.height = height;
Array.Copy(uvs, 0, copy.uvs, 0, uvs.Length); Array.Copy(uvs, 0, copy.uvs, 0, 8);
Array.Copy(offset, 0, copy.offset, 0, offset.Length); Array.Copy(offset, 0, copy.offset, 0, 8);
return copy; return copy;
} }
} }

View File

@ -133,8 +133,7 @@ namespace Spine {
return this == sourceAttachment; return this == sourceAttachment;
} }
///<summary>Internal method used by VertexAttachment subclasses to copy basic data. Does not copy id (generated) and name (set on ///<summary>Does not copy id (generated) or name (set on construction).</summary>
/// construction).</summary>
internal void CopyTo (VertexAttachment attachment) { internal void CopyTo (VertexAttachment attachment) {
if (bones != null) { if (bones != null) {
attachment.bones = new int[bones.Length]; attachment.bones = new int[bones.Length];

View File

@ -52,7 +52,7 @@ namespace Spine {
internal float a, b, worldX; internal float a, b, worldX;
internal float c, d, worldY; internal float c, d, worldY;
internal bool sorted; internal bool sorted, update;
public BoneData Data { get { return data; } } public BoneData Data { get { return data; } }
public Skeleton Skeleton { get { return skeleton; } } public Skeleton Skeleton { get { return skeleton; } }

View File

@ -37,11 +37,12 @@ namespace Spine {
internal float length; internal float length;
internal float x, y, rotation, scaleX = 1, scaleY = 1, shearX, shearY; internal float x, y, rotation, scaleX = 1, scaleY = 1, shearX, shearY;
internal TransformMode transformMode = TransformMode.Normal; internal TransformMode transformMode = TransformMode.Normal;
internal bool skinRequired;
/// <summary>The index of the bone in Skeleton.Bones</summary> /// <summary>The index of the bone in Skeleton.Bones</summary>
public int Index { get { return index; } } public int Index { get { return index; } }
/// <summary>The name of the bone, which is unique within the skeleton.</summary> /// <summary>The name of the bone, which is unique across all bones in the skeleton.</summary>
public string Name { get { return name; } } public string Name { get { return name; } }
/// <summary>May be null.</summary> /// <summary>May be null.</summary>
@ -73,6 +74,11 @@ namespace Spine {
/// <summary>The transform mode for how parent world transforms affect this bone.</summary> /// <summary>The transform mode for how parent world transforms affect this bone.</summary>
public TransformMode TransformMode { get { return transformMode; } set { transformMode = value; } } public TransformMode TransformMode { get { return transformMode; } set { transformMode = value; } }
///<summary>When true, <see cref="Skeleton.UpdateWorldTransform()"/> only updates this bone if the <see cref="Skeleton.Skin"/> contains this
/// bone.</summary>
/// <seealso cref="Skin.Bones"/>
public bool SkinRequired { get { return skinRequired; } set { skinRequired = value; } }
/// <param name="parent">May be null.</param> /// <param name="parent">May be null.</param>
public BoneData (int index, string name, BoneData parent) { public BoneData (int index, string name, BoneData parent) {
if (index < 0) throw new ArgumentException("index must be >= 0", "index"); if (index < 0) throw new ArgumentException("index must be >= 0", "index");

View File

@ -27,13 +27,36 @@
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/ *****************************************************************************/
namespace Spine { using System;
using System.Collections.Generic;
/// <summary>The interface for all constraints.</summary> namespace Spine
public interface IConstraint : IUpdatable { {
/// <summary>The ordinal for the order a skeleton's constraints will be applied.</summary> /// <summary>The base class for all constraint datas.</summary>
int Order { get; } 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;
} }
/// <summary> The constraint's name, which is unique across all constraints in the skeleton of the same type.</summary>
public string Name { get { return name; } }
///<summary>The ordinal of this constraint for the order a skeleton's constraints will be applied by
/// <see cref="Skeleton.UpdateWorldTransform()"/>.</summary>
public int Order { get { return order; } set { order = value; } }
///<summary>When true, <see cref="Skeleton.UpdateWorldTransform()"/> only updates this constraint if the <see cref="Skeleton.Skin"/> contains
/// this constraint.</summary>
///<seealso cref="Skin.Constraints"/>
public bool SkinRequired { get { return skinRequired; } set { skinRequired = value; } }
override public string ToString () {
return name;
}
}
} }

View File

@ -34,7 +34,7 @@ namespace Spine {
public class EventData { public class EventData {
internal string name; internal string name;
/// <summary>The name of the event, which is unique within the skeleton.</summary> /// <summary>The name of the event, which is unique across all events in the skeleton.</summary>
public string Name { get { return name; } } public string Name { get { return name; } }
public int Int { get; set; } public int Int { get; set; }
public float Float { get; set; } public float Float { get; set; }

View File

@ -37,7 +37,7 @@ namespace Spine {
/// <para> /// <para>
/// See <a href="http://esotericsoftware.com/spine-ik-constraints">IK constraints</a> in the Spine User Guide.</para> /// See <a href="http://esotericsoftware.com/spine-ik-constraints">IK constraints</a> in the Spine User Guide.</para>
/// </summary> /// </summary>
public class IkConstraint : IConstraint { public class IkConstraint : IUpdatable {
internal IkConstraintData data; internal IkConstraintData data;
internal ExposedList<Bone> bones = new ExposedList<Bone>(); internal ExposedList<Bone> bones = new ExposedList<Bone>();
internal Bone target; internal Bone target;
@ -93,11 +93,6 @@ namespace Spine {
} }
} }
public int Order {
get { return data.order; }
}
/// <summary>The bones that will be modified by this IK constraint.</summary> /// <summary>The bones that will be modified by this IK constraint.</summary>
public ExposedList<Bone> Bones { public ExposedList<Bone> Bones {
get { return bones; } get { return bones; }

View File

@ -32,23 +32,14 @@ using System.Collections.Generic;
namespace Spine { namespace Spine {
/// <summary>Stores the setup pose for an IkConstraint.</summary> /// <summary>Stores the setup pose for an IkConstraint.</summary>
public class IkConstraintData { public class IkConstraintData : ConstraintData {
internal string name;
internal int order;
internal List<BoneData> bones = new List<BoneData>(); internal List<BoneData> bones = new List<BoneData>();
internal BoneData target; internal BoneData target;
internal int bendDirection = 1; internal int bendDirection = 1;
internal bool compress, stretch, uniform; internal bool compress, stretch, uniform;
internal float mix = 1; internal float mix = 1;
/// <summary>The IK constraint's name, which is unique within the skeleton.</summary> public IkConstraintData (string name) : base(name) {
public string Name {
get { return name; }
}
public int Order {
get { return order; }
set { order = value; }
} }
/// <summary>The bones that are constrained by this IK Constraint.</summary> /// <summary>The bones that are constrained by this IK Constraint.</summary>
@ -98,14 +89,5 @@ namespace Spine {
get { return uniform; } get { return uniform; }
set { uniform = value; } 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;
}
} }
} }

View File

@ -38,7 +38,7 @@ namespace Spine {
/// <para> /// <para>
/// See <a href="http://esotericsoftware.com/spine-path-constraints">Path constraints</a> in the Spine User Guide.</para> /// See <a href="http://esotericsoftware.com/spine-path-constraints">Path constraints</a> in the Spine User Guide.</para>
/// </summary> /// </summary>
public class PathConstraint : IConstraint { public class PathConstraint : IUpdatable {
const int NONE = -1, BEFORE = -2, AFTER = -3; const int NONE = -1, BEFORE = -2, AFTER = -3;
const float Epsilon = 0.00001f; const float Epsilon = 0.00001f;
@ -446,7 +446,6 @@ namespace Spine {
} }
} }
public int Order { get { return data.order; } }
/// <summary>The position along the path.</summary> /// <summary>The position along the path.</summary>
public float Position { get { return position; } set { position = value; } } public float Position { get { return position; } set { position = value; } }
/// <summary>The spacing between bones.</summary> /// <summary>The spacing between bones.</summary>
@ -461,9 +460,5 @@ namespace Spine {
public Slot Target { get { return target; } set { target = value; } } public Slot Target { get { return target; } set { target = value; } }
/// <summary>The path constraint's setup pose data.</summary> /// <summary>The path constraint's setup pose data.</summary>
public PathConstraintData Data { get { return data; } } public PathConstraintData Data { get { return data; } }
override public string ToString () {
return data.name;
}
} }
} }

View File

@ -30,9 +30,7 @@
using System; using System;
namespace Spine { namespace Spine {
public class PathConstraintData { public class PathConstraintData : ConstraintData {
internal string name;
internal int order;
internal ExposedList<BoneData> bones = new ExposedList<BoneData>(); internal ExposedList<BoneData> bones = new ExposedList<BoneData>();
internal SlotData target; internal SlotData target;
internal PositionMode positionMode; internal PositionMode positionMode;
@ -41,8 +39,9 @@ namespace Spine {
internal float offsetRotation; internal float offsetRotation;
internal float position, spacing, rotateMix, translateMix; internal float position, spacing, rotateMix, translateMix;
public string Name { get { return name; } } public PathConstraintData (string name) : base(name) {
public int Order { get { return order; } set { order = value; } } }
public ExposedList<BoneData> Bones { get { return bones; } } public ExposedList<BoneData> Bones { get { return bones; } }
public SlotData Target { get { return target; } set { target = value; } } public SlotData Target { get { return target; } set { target = value; } }
public PositionMode PositionMode { get { return positionMode; } set { positionMode = 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 Spacing { get { return spacing; } set { spacing = value; } }
public float RotateMix { get { return rotateMix; } set { rotateMix = value; } } public float RotateMix { get { return rotateMix; } set { rotateMix = value; } }
public float TranslateMix { get { return translateMix; } set { translateMix = 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 { public enum PositionMode {

View File

@ -55,7 +55,7 @@ namespace Spine {
public ExposedList<IkConstraint> IkConstraints { get { return ikConstraints; } } public ExposedList<IkConstraint> IkConstraints { get { return ikConstraints; } }
public ExposedList<PathConstraint> PathConstraints { get { return pathConstraints; } } public ExposedList<PathConstraint> PathConstraints { get { return pathConstraints; } }
public ExposedList<TransformConstraint> TransformConstraints { get { return transformConstraints; } } public ExposedList<TransformConstraint> 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 R { get { return r; } set { r = value; } }
public float G { get { return g; } set { g = value; } } public float G { get { return g; } set { g = value; } }
public float B { get { return b; } set { b = value; } } public float B { get { return b; } set { b = value; } }
@ -118,21 +118,25 @@ namespace Spine {
UpdateWorldTransform(); UpdateWorldTransform();
} }
/// <summary>Caches information about bones and constraints. Must be called if bones, constraints or weighted path attachments are added /// <summary>Caches information about bones and constraints. Must be called if the skin is modified or if bones, constraints, or
/// or removed.</summary> /// weighted path attachments are added or removed.</summary>
public void UpdateCache () { public void UpdateCache () {
var updateCache = this.updateCache; var updateCache = this.updateCache;
updateCache.Clear(); updateCache.Clear();
this.updateCacheReset.Clear(); this.updateCacheReset.Clear();
int boneCount = this.bones.Items.Length;
var bones = this.bones; var bones = this.bones;
for (int i = 0, n = bones.Count; i < n; i++) for (int i = 0; i < boneCount; i++) {
bones.Items[i].sorted = false; 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 ikConstraints = this.ikConstraints;
var transformConstraints = this.transformConstraints; var transformConstraints = this.transformConstraints;
var pathConstraints = this.pathConstraints; var pathConstraints = this.pathConstraints;
int ikCount = ikConstraints.Count, transformCount = transformConstraints.Count, pathCount = pathConstraints.Count;
int constraintCount = ikCount + transformCount + pathCount; int constraintCount = ikCount + transformCount + pathCount;
//outer: //outer:
for (int i = 0; i < constraintCount; i++) { for (int i = 0; i < constraintCount; i++) {
@ -160,11 +164,13 @@ namespace Spine {
continue_outer: {} continue_outer: {}
} }
for (int i = 0, n = bones.Count; i < n; i++) for (int i = 0; i < boneCount; i++)
SortBone(bones.Items[i]); SortBone(bones.Items[i]);
} }
private void SortIkConstraint (IkConstraint constraint) { private void SortIkConstraint (IkConstraint constraint) {
if (constraint.data.skinRequired && (skin == null || !skin.constraints.Contains(constraint.data))) return;
Bone target = constraint.target; Bone target = constraint.target;
SortBone(target); SortBone(target);
@ -185,14 +191,14 @@ namespace Spine {
} }
private void SortPathConstraint (PathConstraint constraint) { private void SortPathConstraint (PathConstraint constraint) {
if (constraint.data.skinRequired && (skin == null || !skin.constraints.Contains(constraint.data))) return;
Slot slot = constraint.target; Slot slot = constraint.target;
int slotIndex = slot.data.index; int slotIndex = slot.data.index;
Bone slotBone = slot.bone; Bone slotBone = slot.bone;
if (skin != null) SortPathConstraintAttachment(skin, slotIndex, slotBone); if (skin != null) SortPathConstraintAttachment(skin, slotIndex, slotBone);
if (data.defaultSkin != null && data.defaultSkin != skin) if (data.defaultSkin != null && data.defaultSkin != skin)
SortPathConstraintAttachment(data.defaultSkin, slotIndex, slotBone); 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; Attachment attachment = slot.attachment;
if (attachment is PathAttachment) SortPathConstraintAttachment(attachment, slotBone); if (attachment is PathAttachment) SortPathConstraintAttachment(attachment, slotBone);
@ -211,6 +217,8 @@ namespace Spine {
} }
private void SortTransformConstraint (TransformConstraint constraint) { private void SortTransformConstraint (TransformConstraint constraint) {
if (constraint.data.skinRequired && (skin == null || !skin.constraints.Contains(constraint.data))) return;
SortBone(constraint.target); SortBone(constraint.target);
var constrained = constraint.bones; var constrained = constraint.bones;
@ -269,6 +277,7 @@ namespace Spine {
var bonesItems = bones.Items; var bonesItems = bones.Items;
for (int i = 0, n = bones.Count; i < n; i++) { for (int i = 0, n = bones.Count; i < n; i++) {
Bone bone = bonesItems[i]; Bone bone = bonesItems[i];
if (!bone.update) continue;
if (bone.sorted) SortReset(bone.children); if (bone.sorted) SortReset(bone.children);
bone.sorted = false; bone.sorted = false;
} }
@ -295,7 +304,8 @@ namespace Spine {
} }
/// <summary> /// <summary>
/// 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.
/// </summary> /// </summary>
public void UpdateWorldTransform (Bone parent) { public void UpdateWorldTransform (Bone parent) {
// This partial update avoids computing the world transform for constrained bones when 1) the bone is not updated // 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; bone.appliedValid = true;
} }
// Apply the parent bone transform to the root bone. The root bone // Apply the parent bone transform to the root bone. The root bone always inherits scale, rotation and reflection.
// always inherits scale, rotation and reflection.
Bone rootBone = this.RootBone; Bone rootBone = this.RootBone;
float pa = parent.a, pb = parent.b, pc = parent.c, pd = parent.d; float pa = parent.a, pb = parent.b, pc = parent.c, pd = parent.d;
rootBone.worldX = pa * x + pb * y + parent.worldX; rootBone.worldX = pa * x + pb * y + parent.worldX;
@ -447,8 +456,12 @@ namespace Spine {
} }
/// <summary> /// <summary>
/// <para>Sets the skin used to look up attachments before looking in the <see cref="SkeletonData.DefaultSkin"/>. If the
/// skin is changed, <see cref="UpdateCache()"/> is called.
/// </para>
/// <para>Attachments from the new skin are attached if the corresponding attachment from the old skin was attached. /// <para>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.</para> /// If there was no old skin, each slot's setup mode attachment is attached from the new skin.
/// </para>
/// <para>After changing the skin, the visible attachments can be reset to those attached in the setup pose by calling /// <para>After changing the skin, the visible attachments can be reset to those attached in the setup pose by calling
/// <see cref="Skeleton.SetSlotsToSetupPose()"/>. /// <see cref="Skeleton.SetSlotsToSetupPose()"/>.
/// Also, often <see cref="AnimationState.Apply(Skeleton)"/> is called before the next time the /// Also, often <see cref="AnimationState.Apply(Skeleton)"/> is called before the next time the
@ -456,6 +469,7 @@ namespace Spine {
/// </summary> /// </summary>
/// <param name="newSkin">May be null.</param> /// <param name="newSkin">May be null.</param>
public void SetSkin (Skin newSkin) { public void SetSkin (Skin newSkin) {
if (newSkin == skin) return;
if (newSkin != null) { if (newSkin != null) {
if (skin != null) if (skin != null)
newSkin.AttachAll(this, skin); newSkin.AttachAll(this, skin);
@ -472,6 +486,7 @@ namespace Spine {
} }
} }
skin = newSkin; skin = newSkin;
UpdateCache();
} }
/// <summary>Finds an attachment by looking in the {@link #skin} and {@link SkeletonData#defaultSkin} using the slot name and attachment name.</summary> /// <summary>Finds an attachment by looking in the {@link #skin} and {@link SkeletonData#defaultSkin} using the slot name and attachment name.</summary>
@ -528,7 +543,7 @@ namespace Spine {
ExposedList<TransformConstraint> transformConstraints = this.transformConstraints; ExposedList<TransformConstraint> transformConstraints = this.transformConstraints;
for (int i = 0, n = transformConstraints.Count; i < n; i++) { for (int i = 0, n = transformConstraints.Count; i < n; i++) {
TransformConstraint transformConstraint = transformConstraints.Items[i]; TransformConstraint transformConstraint = transformConstraints.Items[i];
if (transformConstraint.data.name == constraintName) return transformConstraint; if (transformConstraint.data.Name == constraintName) return transformConstraint;
} }
return null; return null;
} }
@ -539,7 +554,7 @@ namespace Spine {
ExposedList<PathConstraint> pathConstraints = this.pathConstraints; ExposedList<PathConstraint> pathConstraints = this.pathConstraints;
for (int i = 0, n = pathConstraints.Count; i < n; i++) { for (int i = 0, n = pathConstraints.Count; i < n; i++) {
PathConstraint constraint = pathConstraints.Items[i]; PathConstraint constraint = pathConstraints.Items[i];
if (constraint.data.name.Equals(constraintName)) return constraint; if (constraint.data.Name.Equals(constraintName)) return constraint;
} }
return null; return null;
} }

View File

@ -174,6 +174,7 @@ namespace Spine {
data.shearY = ReadFloat(input); data.shearY = ReadFloat(input);
data.length = ReadFloat(input) * scale; data.length = ReadFloat(input) * scale;
data.transformMode = TransformModeValues[ReadVarint(input, true)]; data.transformMode = TransformModeValues[ReadVarint(input, true)];
data.skinRequired = ReadBoolean(input);
if (nonessential) ReadInt(input); // Skip bone color. if (nonessential) ReadInt(input); // Skip bone color.
skeletonData.bones.Add(data); skeletonData.bones.Add(data);
} }
@ -206,6 +207,7 @@ namespace Spine {
for (int i = 0, n = ReadVarint(input, true); i < n; i++) { for (int i = 0, n = ReadVarint(input, true); i < n; i++) {
IkConstraintData data = new IkConstraintData(ReadString(input)); IkConstraintData data = new IkConstraintData(ReadString(input));
data.order = ReadVarint(input, true); data.order = ReadVarint(input, true);
data.skinRequired = ReadBoolean(input);
for (int ii = 0, nn = ReadVarint(input, true); ii < nn; ii++) for (int ii = 0, nn = ReadVarint(input, true); ii < nn; ii++)
data.bones.Add(skeletonData.bones.Items[ReadVarint(input, true)]); data.bones.Add(skeletonData.bones.Items[ReadVarint(input, true)]);
data.target = 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++) { for (int i = 0, n = ReadVarint(input, true); i < n; i++) {
TransformConstraintData data = new TransformConstraintData(ReadString(input)); TransformConstraintData data = new TransformConstraintData(ReadString(input));
data.order = ReadVarint(input, true); data.order = ReadVarint(input, true);
data.skinRequired = ReadBoolean(input);
for (int ii = 0, nn = ReadVarint(input, true); ii < nn; ii++) for (int ii = 0, nn = ReadVarint(input, true); ii < nn; ii++)
data.bones.Add(skeletonData.bones.Items[ReadVarint(input, true)]); data.bones.Add(skeletonData.bones.Items[ReadVarint(input, true)]);
data.target = 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++) { for (int i = 0, n = ReadVarint(input, true); i < n; i++) {
PathConstraintData data = new PathConstraintData(ReadString(input)); PathConstraintData data = new PathConstraintData(ReadString(input));
data.order = ReadVarint(input, true); data.order = ReadVarint(input, true);
data.skinRequired = ReadBoolean(input);
for (int ii = 0, nn = ReadVarint(input, true); ii < nn; ii++) for (int ii = 0, nn = ReadVarint(input, true); ii < nn; ii++)
data.bones.Add(skeletonData.bones.Items[ReadVarint(input, true)]); data.bones.Add(skeletonData.bones.Items[ReadVarint(input, true)]);
data.target = skeletonData.slots.Items[ReadVarint(input, true)]; data.target = skeletonData.slots.Items[ReadVarint(input, true)];
@ -260,7 +264,7 @@ namespace Spine {
} }
// Default skin. // Default skin.
Skin defaultSkin = ReadSkin(input, skeletonData, "default", nonessential); Skin defaultSkin = ReadSkin(input, skeletonData, true, nonessential);
if (defaultSkin != null) { if (defaultSkin != null) {
skeletonData.defaultSkin = defaultSkin; skeletonData.defaultSkin = defaultSkin;
skeletonData.skins.Add(defaultSkin); skeletonData.skins.Add(defaultSkin);
@ -268,7 +272,7 @@ namespace Spine {
// Skins. // Skins.
for (int i = 0, n = ReadVarint(input, true); i < n; i++) 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. // Linked meshes.
for (int i = 0, n = linkedMeshes.Count; i < n; i++) { for (int i = 0, n = linkedMeshes.Count; i < n; i++) {
@ -312,11 +316,19 @@ namespace Spine {
/// <returns>May be null.</returns> /// <returns>May be null.</returns>
private Skin ReadSkin (Stream input, SkeletonData skeletonData, String skinName, bool nonessential) { private Skin ReadSkin (Stream input, SkeletonData skeletonData, bool defaultSkin, bool nonessential) {
int slotCount = ReadVarint(input, true); Skin skin = new Skin(defaultSkin ? "default" : ReadString(input));
if (slotCount == 0) return null; if (!defaultSkin) {
Skin skin = new Skin(skinName); for (int i = 0, n = ReadVarint(input, true); i < n; i++)
for (int i = 0; i < slotCount; 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); int slotIndex = ReadVarint(input, true);
for (int ii = 0, nn = ReadVarint(input, true); ii < nn; ii++) { for (int ii = 0, nn = ReadVarint(input, true); ii < nn; ii++) {
String name = ReadString(input); String name = ReadString(input);

View File

@ -109,6 +109,7 @@ namespace Spine {
} }
// Bones. // Bones.
if (root.ContainsKey("bones")) {
foreach (Dictionary<string, Object> boneMap in (List<Object>)root["bones"]) { foreach (Dictionary<string, Object> boneMap in (List<Object>)root["bones"]) {
BoneData parent = null; BoneData parent = null;
if (boneMap.ContainsKey("parent")) { if (boneMap.ContainsKey("parent")) {
@ -128,9 +129,11 @@ namespace Spine {
string tm = GetString(boneMap, "transform", TransformMode.Normal.ToString()); string tm = GetString(boneMap, "transform", TransformMode.Normal.ToString());
data.transformMode = (TransformMode)Enum.Parse(typeof(TransformMode), tm, true); data.transformMode = (TransformMode)Enum.Parse(typeof(TransformMode), tm, true);
data.skinRequired = GetBoolean(boneMap, "skin", false);
skeletonData.bones.Add(data); skeletonData.bones.Add(data);
} }
}
// Slots. // Slots.
if (root.ContainsKey("slots")) { if (root.ContainsKey("slots")) {
@ -171,16 +174,19 @@ namespace Spine {
foreach (Dictionary<string, Object> constraintMap in (List<Object>)root["ik"]) { foreach (Dictionary<string, Object> constraintMap in (List<Object>)root["ik"]) {
IkConstraintData data = new IkConstraintData((string)constraintMap["name"]); IkConstraintData data = new IkConstraintData((string)constraintMap["name"]);
data.order = GetInt(constraintMap, "order", 0); data.order = GetInt(constraintMap, "order", 0);
data.skinRequired = GetBoolean(constraintMap,"skin", false);
if (constraintMap.ContainsKey("bones")) {
foreach (string boneName in (List<Object>)constraintMap["bones"]) { foreach (string boneName in (List<Object>)constraintMap["bones"]) {
BoneData bone = skeletonData.FindBone(boneName); BoneData bone = skeletonData.FindBone(boneName);
if (bone == null) throw new Exception("IK constraint bone not found: " + boneName); if (bone == null) throw new Exception("IK bone not found: " + boneName);
data.bones.Add(bone); data.bones.Add(bone);
} }
}
string targetName = (string)constraintMap["target"]; string targetName = (string)constraintMap["target"];
data.target = skeletonData.FindBone(targetName); 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.mix = GetFloat(constraintMap, "mix", 1);
data.bendDirection = GetBoolean(constraintMap, "bendPositive", true) ? 1 : -1; data.bendDirection = GetBoolean(constraintMap, "bendPositive", true) ? 1 : -1;
data.compress = GetBoolean(constraintMap, "compress", false); data.compress = GetBoolean(constraintMap, "compress", false);
@ -196,16 +202,19 @@ namespace Spine {
foreach (Dictionary<string, Object> constraintMap in (List<Object>)root["transform"]) { foreach (Dictionary<string, Object> constraintMap in (List<Object>)root["transform"]) {
TransformConstraintData data = new TransformConstraintData((string)constraintMap["name"]); TransformConstraintData data = new TransformConstraintData((string)constraintMap["name"]);
data.order = GetInt(constraintMap, "order", 0); data.order = GetInt(constraintMap, "order", 0);
data.skinRequired = GetBoolean(constraintMap,"skin", false);
if (constraintMap.ContainsKey("bones")) {
foreach (string boneName in (List<Object>)constraintMap["bones"]) { foreach (string boneName in (List<Object>)constraintMap["bones"]) {
BoneData bone = skeletonData.FindBone(boneName); BoneData bone = skeletonData.FindBone(boneName);
if (bone == null) throw new Exception("Transform constraint bone not found: " + boneName); if (bone == null) throw new Exception("Transform constraint bone not found: " + boneName);
data.bones.Add(bone); data.bones.Add(bone);
} }
}
string targetName = (string)constraintMap["target"]; string targetName = (string)constraintMap["target"];
data.target = skeletonData.FindBone(targetName); 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.local = GetBoolean(constraintMap, "local", false);
data.relative = GetBoolean(constraintMap, "relative", false); data.relative = GetBoolean(constraintMap, "relative", false);
@ -231,16 +240,19 @@ namespace Spine {
foreach (Dictionary<string, Object> constraintMap in (List<Object>)root["path"]) { foreach (Dictionary<string, Object> constraintMap in (List<Object>)root["path"]) {
PathConstraintData data = new PathConstraintData((string)constraintMap["name"]); PathConstraintData data = new PathConstraintData((string)constraintMap["name"]);
data.order = GetInt(constraintMap, "order", 0); data.order = GetInt(constraintMap, "order", 0);
data.skinRequired = GetBoolean(constraintMap,"skin", false);
if (constraintMap.ContainsKey("bones")) {
foreach (string boneName in (List<Object>)constraintMap["bones"]) { foreach (string boneName in (List<Object>)constraintMap["bones"]) {
BoneData bone = skeletonData.FindBone(boneName); BoneData bone = skeletonData.FindBone(boneName);
if (bone == null) throw new Exception("Path bone not found: " + boneName); if (bone == null) throw new Exception("Path bone not found: " + boneName);
data.bones.Add(bone); data.bones.Add(bone);
} }
}
string targetName = (string)constraintMap["target"]; string targetName = (string)constraintMap["target"];
data.target = skeletonData.FindSlot(targetName); 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.positionMode = (PositionMode)Enum.Parse(typeof(PositionMode), GetString(constraintMap, "positionMode", "percent"), true);
data.spacingMode = (SpacingMode)Enum.Parse(typeof(SpacingMode), GetString(constraintMap, "spacingMode", "length"), true); data.spacingMode = (SpacingMode)Enum.Parse(typeof(SpacingMode), GetString(constraintMap, "spacingMode", "length"), true);
@ -259,9 +271,38 @@ namespace Spine {
// Skins. // Skins.
if (root.ContainsKey("skins")) { if (root.ContainsKey("skins")) {
foreach (KeyValuePair<string, Object> skinMap in (Dictionary<string, Object>)root["skins"]) { foreach (Dictionary<string, object> skinMap in (List<object>)root["skins"]) {
var skin = new Skin(skinMap.Key); Skin skin = new Skin((string)skinMap["name"]);
foreach (KeyValuePair<string, Object> slotEntry in (Dictionary<string, Object>)skinMap.Value) { if (skinMap.ContainsKey("bones")) {
foreach (string entryName in (List<Object>)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<Object>)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<Object>)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<Object>)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<string, Object> slotEntry in (Dictionary<string, Object>)skinMap["attachments"]) {
int slotIndex = skeletonData.FindSlotIndex(slotEntry.Key); int slotIndex = skeletonData.FindSlotIndex(slotEntry.Key);
foreach (KeyValuePair<string, Object> entry in ((Dictionary<string, Object>)slotEntry.Value)) { foreach (KeyValuePair<string, Object> entry in ((Dictionary<string, Object>)slotEntry.Value)) {
try { try {
@ -272,6 +313,7 @@ namespace Spine {
} }
} }
} }
}
skeletonData.skins.Add(skin); skeletonData.skins.Add(skin);
if (skin.name == "default") skeletonData.defaultSkin = skin; if (skin.name == "default") skeletonData.defaultSkin = skin;
} }
@ -494,7 +536,7 @@ namespace Spine {
int frameIndex = 0; int frameIndex = 0;
foreach (Dictionary<string, Object> valueMap in values) { foreach (Dictionary<string, Object> valueMap in values) {
float time = (float)valueMap["time"]; float time = GetFloat(valueMap, "time", 0);
timeline.SetFrame(frameIndex++, time, (string)valueMap["name"]); timeline.SetFrame(frameIndex++, time, (string)valueMap["name"]);
} }
timelines.Add(timeline); timelines.Add(timeline);
@ -506,7 +548,7 @@ namespace Spine {
int frameIndex = 0; int frameIndex = 0;
foreach (Dictionary<string, Object> valueMap in values) { foreach (Dictionary<string, Object> valueMap in values) {
float time = (float)valueMap["time"]; float time = GetFloat(valueMap, "time", 0);
string c = (string)valueMap["color"]; string c = (string)valueMap["color"];
timeline.SetFrame(frameIndex, time, ToColor(c, 0), ToColor(c, 1), ToColor(c, 2), ToColor(c, 3)); timeline.SetFrame(frameIndex, time, ToColor(c, 0), ToColor(c, 1), ToColor(c, 2), ToColor(c, 3));
ReadCurve(valueMap, timeline, frameIndex); ReadCurve(valueMap, timeline, frameIndex);
@ -521,7 +563,7 @@ namespace Spine {
int frameIndex = 0; int frameIndex = 0;
foreach (Dictionary<string, Object> valueMap in values) { foreach (Dictionary<string, Object> valueMap in values) {
float time = (float)valueMap["time"]; float time = GetFloat(valueMap, "time", 0);
string light = (string)valueMap["light"]; string light = (string)valueMap["light"];
string dark = (string)valueMap["dark"]; string dark = (string)valueMap["dark"];
timeline.SetFrame(frameIndex, time, ToColor(light, 0), ToColor(light, 1), ToColor(light, 2), ToColor(light, 3), 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; int frameIndex = 0;
foreach (Dictionary<string, Object> valueMap in values) { foreach (Dictionary<string, Object> 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); ReadCurve(valueMap, timeline, frameIndex);
frameIndex++; frameIndex++;
} }
@ -563,9 +605,11 @@ namespace Spine {
} else if (timelineName == "translate" || timelineName == "scale" || timelineName == "shear") { } else if (timelineName == "translate" || timelineName == "scale" || timelineName == "shear") {
TranslateTimeline timeline; TranslateTimeline timeline;
float timelineScale = 1; float timelineScale = 1, defaultValue = 0;
if (timelineName == "scale") if (timelineName == "scale") {
timeline = new ScaleTimeline(values.Count); timeline = new ScaleTimeline(values.Count);
defaultValue = 1;
}
else if (timelineName == "shear") else if (timelineName == "shear")
timeline = new ShearTimeline(values.Count); timeline = new ShearTimeline(values.Count);
else { else {
@ -576,9 +620,9 @@ namespace Spine {
int frameIndex = 0; int frameIndex = 0;
foreach (Dictionary<string, Object> valueMap in values) { foreach (Dictionary<string, Object> valueMap in values) {
float time = (float)valueMap["time"]; float time = GetFloat(valueMap, "time", 0);
float x = GetFloat(valueMap, "x", 0); float x = GetFloat(valueMap, "x", defaultValue);
float y = GetFloat(valueMap, "y", 0); float y = GetFloat(valueMap, "y", defaultValue);
timeline.SetFrame(frameIndex, time, x * timelineScale, y * timelineScale); timeline.SetFrame(frameIndex, time, x * timelineScale, y * timelineScale);
ReadCurve(valueMap, timeline, frameIndex); ReadCurve(valueMap, timeline, frameIndex);
frameIndex++; frameIndex++;
@ -603,7 +647,7 @@ namespace Spine {
foreach (Dictionary<string, Object> valueMap in values) { foreach (Dictionary<string, Object> valueMap in values) {
timeline.SetFrame( timeline.SetFrame(
frameIndex, frameIndex,
(float)valueMap["time"], GetFloat(valueMap, "time", 0),
GetFloat(valueMap, "mix", 1), GetFloat(valueMap, "mix", 1),
GetBoolean(valueMap, "bendPositive", true) ? 1 : -1, GetBoolean(valueMap, "bendPositive", true) ? 1 : -1,
GetBoolean(valueMap, "compress", true), GetBoolean(valueMap, "compress", true),
@ -626,7 +670,7 @@ namespace Spine {
timeline.transformConstraintIndex = skeletonData.transformConstraints.IndexOf(constraint); timeline.transformConstraintIndex = skeletonData.transformConstraints.IndexOf(constraint);
int frameIndex = 0; int frameIndex = 0;
foreach (Dictionary<string, Object> valueMap in values) { foreach (Dictionary<string, Object> valueMap in values) {
float time = (float)valueMap["time"]; float time = GetFloat(valueMap, "time", 0);
float rotateMix = GetFloat(valueMap, "rotateMix", 1); float rotateMix = GetFloat(valueMap, "rotateMix", 1);
float translateMix = GetFloat(valueMap, "translateMix", 1); float translateMix = GetFloat(valueMap, "translateMix", 1);
float scaleMix = GetFloat(valueMap, "scaleMix", 1); float scaleMix = GetFloat(valueMap, "scaleMix", 1);
@ -641,8 +685,8 @@ namespace Spine {
} }
// Path constraint timelines. // Path constraint timelines.
if (map.ContainsKey("paths")) { if (map.ContainsKey("path")) {
foreach (KeyValuePair<string, Object> constraintMap in (Dictionary<string, Object>)map["paths"]) { foreach (KeyValuePair<string, Object> constraintMap in (Dictionary<string, Object>)map["path"]) {
int index = skeletonData.FindPathConstraintIndex(constraintMap.Key); int index = skeletonData.FindPathConstraintIndex(constraintMap.Key);
if (index == -1) throw new Exception("Path constraint not found: " + constraintMap.Key); if (index == -1) throw new Exception("Path constraint not found: " + constraintMap.Key);
PathConstraintData data = skeletonData.pathConstraints.Items[index]; PathConstraintData data = skeletonData.pathConstraints.Items[index];
@ -664,7 +708,7 @@ namespace Spine {
timeline.pathConstraintIndex = index; timeline.pathConstraintIndex = index;
int frameIndex = 0; int frameIndex = 0;
foreach (Dictionary<string, Object> valueMap in values) { foreach (Dictionary<string, Object> 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); ReadCurve(valueMap, timeline, frameIndex);
frameIndex++; frameIndex++;
} }
@ -676,7 +720,7 @@ namespace Spine {
timeline.pathConstraintIndex = index; timeline.pathConstraintIndex = index;
int frameIndex = 0; int frameIndex = 0;
foreach (Dictionary<string, Object> valueMap in values) { foreach (Dictionary<string, Object> 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); ReadCurve(valueMap, timeline, frameIndex);
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); ReadCurve(valueMap, timeline, frameIndex);
frameIndex++; frameIndex++;
} }
@ -770,7 +814,7 @@ namespace Spine {
for (int i = slotCount - 1; i >= 0; i--) for (int i = slotCount - 1; i >= 0; i--)
if (drawOrder[i] == -1) drawOrder[i] = unchanged[--unchangedIndex]; 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); timelines.Add(timeline);
duration = Math.Max(duration, timeline.frames[timeline.FrameCount - 1]); duration = Math.Max(duration, timeline.frames[timeline.FrameCount - 1]);
@ -784,7 +828,7 @@ namespace Spine {
foreach (Dictionary<string, Object> eventMap in eventsMap) { foreach (Dictionary<string, Object> eventMap in eventsMap) {
EventData eventData = skeletonData.FindEvent((string)eventMap["name"]); EventData eventData = skeletonData.FindEvent((string)eventMap["name"]);
if (eventData == null) throw new Exception("Event not found: " + 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), intValue = GetInt(eventMap, "int", eventData.Int),
floatValue = GetFloat(eventMap, "float", eventData.Float), floatValue = GetFloat(eventMap, "float", eventData.Float),
stringValue = GetString(eventMap, "string", eventData.String) stringValue = GetString(eventMap, "string", eventData.String)
@ -807,12 +851,12 @@ namespace Spine {
if (!valueMap.ContainsKey("curve")) if (!valueMap.ContainsKey("curve"))
return; return;
Object curveObject = valueMap["curve"]; Object curveObject = valueMap["curve"];
if (curveObject.Equals("stepped")) if (curveObject is string)
timeline.SetStepped(frameIndex); timeline.SetStepped(frameIndex);
else { else {
var curve = curveObject as List<Object>; var curve = curveObject as List<Object>;
if (curve != null) 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));
} }
} }

View File

@ -40,9 +40,13 @@ namespace Spine {
public class Skin { public class Skin {
internal string name; internal string name;
private OrderedDictionary attachments = new OrderedDictionary(SkinEntryComparer.Instance); // contains <SkinEntry, Attachment> private OrderedDictionary attachments = new OrderedDictionary(SkinEntryComparer.Instance); // contains <SkinEntry, Attachment>
internal readonly ExposedList<BoneData> bones = new ExposedList<BoneData>();
internal readonly ExposedList<ConstraintData> constraints = new ExposedList<ConstraintData>();
public string Name { get { return name; } } public string Name { get { return name; } }
public OrderedDictionary Attachments { get { return attachments; } } public OrderedDictionary Attachments { get { return attachments; } }
public ExposedList<BoneData> Bones { get { return bones; } }
public ExposedList<ConstraintData> Constraints { get { return constraints; } }
public Skin (string name) { public Skin (string name) {
if (name == null) throw new ArgumentNullException("name", "name cannot be null."); if (name == null) throw new ArgumentNullException("name", "name cannot be null.");
@ -59,13 +63,23 @@ namespace Spine {
///<summary>Adds all attachments, bones, and constraints from the specified skin to this skin.</summary> ///<summary>Adds all attachments, bones, and constraints from the specified skin to this skin.</summary>
public void AddSkin (Skin 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) foreach (SkinEntry entry in skin.attachments.Keys)
SetAttachment(entry.SlotIndex, entry.Name, entry.Attachment); SetAttachment(entry.SlotIndex, entry.Name, entry.Attachment);
} }
///<summary>Adds all attachments from the specified skin to this skin. Attachments are deep copied.</summary> ///<summary>Adds all attachments from the specified skin to this skin. Attachments are deep copied.</summary>
public void CopySkin (Skin skin) { 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) { foreach (SkinEntry entry in skin.attachments.Keys) {
Attachment attachment = entry.Attachment.Copy(); Attachment attachment = entry.Attachment.Copy();
@ -98,13 +112,20 @@ namespace Spine {
return entries; return entries;
} }
/// <summary>Returns all attachments for the given slot in this skin.</summary> /// <summary>Returns all attachments in this skin for the specified slot index.</summary>
/// <param name="slotIndex">The target slotIndex. To find the slot index, use <see cref="Spine.Skeleton.FindSlotIndex"/> or <see cref="Spine.SkeletonData.FindSlotIndex"/> /// <param name="slotIndex">The target slotIndex. To find the slot index, use <see cref="Spine.Skeleton.FindSlotIndex"/> or <see cref="Spine.SkeletonData.FindSlotIndex"/>
public void GetAttachments (int slotIndex, List<SkinEntry> attachments) { public void GetAttachments (int slotIndex, List<SkinEntry> attachments) {
foreach (SkinEntry entry in this.attachments.Keys) foreach (SkinEntry entry in this.attachments.Keys)
if (entry.SlotIndex == slotIndex) attachments.Add(entry); if (entry.SlotIndex == slotIndex) attachments.Add(entry);
} }
///<summary>Clears all attachments, bones, and constraints.</summary>
public void Clear () {
attachments.Clear();
bones.Clear();
constraints.Clear();
}
override public string ToString () { override public string ToString () {
return name; return name;
} }
@ -141,6 +162,7 @@ namespace Spine {
} }
} }
/// <summary>The name the attachment is associated with, equivalent to the skin placeholder name in the Spine editor.</summary>
public String Name { public String Name {
get { get {
return name; return name;

View File

@ -40,8 +40,11 @@ namespace Spine {
internal string attachmentName; internal string attachmentName;
internal BlendMode blendMode; internal BlendMode blendMode;
/// <summary>The index of the slot in <see cref="Skeleton.Slots"/>.</summary>
public int Index { get { return index; } } public int Index { get { return index; } }
/// <summary>The name of the slot, which is unique across all slots in the skeleton.</summary>
public string Name { get { return name; } } public string Name { get { return name; } }
/// <summary>The bone this slot belongs to.</summary>
public BoneData BoneData { get { return boneData; } } public BoneData BoneData { get { return boneData; } }
public float R { get { return r; } set { r = value; } } public float R { get { return r; } set { r = value; } }
public float G { get { return g; } set { g = 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 float B2 { get { return b2; } set { b2 = value; } }
public bool HasSecondColor { get { return hasSecondColor; } set { hasSecondColor = value; } } public bool HasSecondColor { get { return hasSecondColor; } set { hasSecondColor = value; } }
/// <summary>May be null.</summary> /// <summary>The name of the attachment that is visible for this slot in the setup pose, or null if no attachment is visible.</summary>
public String AttachmentName { get { return attachmentName; } set { attachmentName = value; } } public String AttachmentName { get { return attachmentName; } set { attachmentName = value; } }
/// <summary>The blend mode for drawing the slot's attachment.</summary>
public BlendMode BlendMode { get { return blendMode; } set { blendMode = value; } } public BlendMode BlendMode { get { return blendMode; } set { blendMode = value; } }
public SlotData (int index, String name, BoneData boneData) { public SlotData (int index, String name, BoneData boneData) {

View File

@ -37,7 +37,7 @@ namespace Spine {
/// <para> /// <para>
/// See <a href="http://esotericsoftware.com/spine-transform-constraints">Transform constraints</a> in the Spine User Guide.</para> /// See <a href="http://esotericsoftware.com/spine-transform-constraints">Transform constraints</a> in the Spine User Guide.</para>
/// </summary> /// </summary>
public class TransformConstraint : IConstraint { public class TransformConstraint : IUpdatable {
internal TransformConstraintData data; internal TransformConstraintData data;
internal ExposedList<Bone> bones; internal ExposedList<Bone> bones;
internal Bone target; internal Bone target;
@ -288,7 +288,6 @@ namespace Spine {
} }
} }
public int Order { get { return data.order; } }
/// <summary>The bones that will be modified by this transform constraint.</summary> /// <summary>The bones that will be modified by this transform constraint.</summary>
public ExposedList<Bone> Bones { get { return bones; } } public ExposedList<Bone> Bones { get { return bones; } }
/// <summary>The target bone whose world transform will be copied to the constrained bones.</summary> /// <summary>The target bone whose world transform will be copied to the constrained bones.</summary>

View File

@ -30,17 +30,13 @@
using System; using System;
namespace Spine { namespace Spine {
public class TransformConstraintData { public class TransformConstraintData : ConstraintData {
internal string name;
internal int order;
internal ExposedList<BoneData> bones = new ExposedList<BoneData>(); internal ExposedList<BoneData> bones = new ExposedList<BoneData>();
internal BoneData target; internal BoneData target;
internal float rotateMix, translateMix, scaleMix, shearMix; internal float rotateMix, translateMix, scaleMix, shearMix;
internal float offsetRotation, offsetX, offsetY, offsetScaleX, offsetScaleY, offsetShearY; internal float offsetRotation, offsetX, offsetY, offsetScaleX, offsetScaleY, offsetShearY;
internal bool relative, local; internal bool relative, local;
public string Name { get { return name; } }
public int Order { get { return order; } set { order = value; } }
public ExposedList<BoneData> Bones { get { return bones; } } public ExposedList<BoneData> Bones { get { return bones; } }
public BoneData Target { get { return target; } set { target = value; } } public BoneData Target { get { return target; } set { target = value; } }
public float RotateMix { get { return rotateMix; } set { rotateMix = 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 Relative { get { return relative; } set { relative = value; } }
public bool Local { get { return local; } set { local = value; } } public bool Local { get { return local; } set { local = value; } }
public TransformConstraintData (string name) { public TransformConstraintData (string name) : base(name) {
if (name == null) throw new ArgumentNullException("name", "name cannot be null.");
this.name = name;
}
override public string ToString () {
return name;
} }
} }
} }

View File

@ -504,8 +504,17 @@ namespace Spine.Unity.Editor {
if (!fieldMatchesSkin) { if (!fieldMatchesSkin) {
Skin skinToSet = string.IsNullOrEmpty(componentSkinName) ? null : skeletonRenderer.Skeleton.Data.FindSkin(componentSkinName); Skin skinToSet = string.IsNullOrEmpty(componentSkinName) ? null : skeletonRenderer.Skeleton.Data.FindSkin(componentSkinName);
skeletonRenderer.Skeleton.Skin = skinToSet; skeletonRenderer.Skeleton.SetSkin(skinToSet);
skeletonRenderer.Skeleton.SetSlotsToSetupPose(); 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(); skeletonRenderer.LateUpdate();
return true; return true;
} }

View File

@ -707,9 +707,10 @@ namespace Spine.Unity.Editor {
if (root == null || !root.ContainsKey("skins")) if (root == null || !root.ContainsKey("skins"))
return requiredPaths; return requiredPaths;
foreach (var skin in (Dictionary<string, object>)root["skins"]) { foreach (Dictionary<string, object> skinMap in (List<object>)root["skins"]) {
foreach (var slot in (Dictionary<string, object>)skin.Value) { if (!skinMap.ContainsKey("attachments"))
continue;
foreach (var slot in (Dictionary<string, object>)skinMap["attachments"]) {
foreach (var attachment in ((Dictionary<string, object>)slot.Value)) { foreach (var attachment in ((Dictionary<string, object>)slot.Value)) {
var data = ((Dictionary<string, object>)attachment.Value); var data = ((Dictionary<string, object>)attachment.Value);