From 90bfdba422472be243a8e2385288a853c6d7a40d Mon Sep 17 00:00:00 2001 From: Harald Csaszar Date: Tue, 8 Apr 2025 17:19:01 +0200 Subject: [PATCH] [csharp][unity] Port of Transform constraint property mapping and cumulated 4.3 changes. Excluding import scale fixes. --- spine-csharp/src/Animation.cs | 4 +- spine-csharp/src/AnimationState.cs | 20 +- spine-csharp/src/AnimationStateData.cs | 4 +- .../src/Attachments/AtlasAttachmentLoader.cs | 4 +- .../src/Attachments/MeshAttachment.cs | 2 +- spine-csharp/src/Attachments/Sequence.cs | 2 +- spine-csharp/src/PathConstraint.cs | 12 +- spine-csharp/src/PathConstraintData.cs | 4 +- spine-csharp/src/PhysicsConstraint.cs | 2 +- spine-csharp/src/Skeleton.cs | 16 +- spine-csharp/src/SkeletonBinary.cs | 180 ++++++---- spine-csharp/src/SkeletonJson.cs | 321 ++++++++++++------ spine-csharp/src/TransformConstraint.cs | 233 ++----------- spine-csharp/src/TransformConstraintData.cs | 249 +++++++++++++- .../Editor/Utility/SpineHandles.cs | 6 +- .../Editor/Windows/SkeletonDebugWindow.cs | 4 +- .../RootMotion/SkeletonRootMotionBase.cs | 22 +- .../SkeletonUtility/SkeletonUtility.cs | 2 +- 18 files changed, 664 insertions(+), 423 deletions(-) diff --git a/spine-csharp/src/Animation.cs b/spine-csharp/src/Animation.cs index cbc6b0f2f..d64474de0 100644 --- a/spine-csharp/src/Animation.cs +++ b/spine-csharp/src/Animation.cs @@ -63,7 +63,7 @@ namespace Spine { Timeline[] timelinesItems = timelines.Items; for (int t = 0; t < timelinesCount; ++t) idCount += timelinesItems[t].PropertyIds.Length; - string[] propertyIds = new string[idCount]; + var propertyIds = new string[idCount]; int currentId = 0; for (int t = 0; t < timelinesCount; ++t) { string[] ids = timelinesItems[t].PropertyIds; @@ -320,7 +320,7 @@ namespace Spine { public void Shrink (int bezierCount) { int size = FrameCount + bezierCount * BEZIER_SIZE; if (curves.Length > size) { - float[] newCurves = new float[size]; + var newCurves = new float[size]; Array.Copy(curves, 0, newCurves, 0, size); curves = newCurves; } diff --git a/spine-csharp/src/AnimationState.cs b/spine-csharp/src/AnimationState.cs index 98221fa9c..ebe2a20b3 100644 --- a/spine-csharp/src/AnimationState.cs +++ b/spine-csharp/src/AnimationState.cs @@ -7,7 +7,7 @@ * Integration of the Spine Runtimes into software or otherwise creating * derivative works of the Spine Runtimes is permitted under the terms and * conditions of Section 2 of the Spine Editor License Agreement: - * http://esotericsoftware.com/spine-editor-license + * https://esotericsoftware.com/spine-editor-license * * Otherwise, it is permitted to integrate the Spine Runtimes into software * or otherwise create derivative works of the Spine Runtimes (collectively, @@ -37,7 +37,7 @@ namespace Spine { /// Applies animations over time, queues animations for later playback, mixes (crossfading) between animations, and applies /// multiple animations on top of each other (layering). /// - /// See Applying Animations in the Spine Runtimes Guide. + /// See Applying Animations in the Spine Runtimes Guide. /// public class AnimationState { internal static readonly Animation EmptyAnimation = new Animation("", new ExposedList(), 0); @@ -87,7 +87,7 @@ namespace Spine { internal void OnEvent (TrackEntry entry, Event e) { if (Event != null) Event(entry, e); } public delegate void TrackEntryDelegate (TrackEntry trackEntry); - /// See + /// See /// API Reference documentation pages here for details. Usage in C# and spine-unity is explained /// here /// on the spine-unity documentation pages. @@ -726,7 +726,7 @@ namespace Spine { return AddAnimation(trackIndex, animation, loop, delay); } - /// Adds an animation to be played after the current or last queued animation for a track. If the track is empty, it is + /// Adds an animation to be played after the current or last queued animation for a track. If the track has no entries, this is /// equivalent to calling . /// /// If > 0, sets . If <= 0, the delay set is the duration of the previous track entry @@ -774,8 +774,10 @@ namespace Spine { /// with the desired delay (an empty animation has a duration of 0) and on /// the returned track entry, set the . Mixing from an empty animation causes the new /// animation to be applied more and more over the mix duration. Properties keyed in the new animation transition from the value - /// from lower tracks or from the setup pose value if no lower tracks key the property to the value keyed in the new - /// animation. + /// from lower tracks or from the setup pose value if no lower tracks key the property to the value keyed in the new animation. + /// + /// See Empty animations in the Spine + /// Runtimes Guide. public TrackEntry SetEmptyAnimation (int trackIndex, float mixDuration) { TrackEntry entry = SetAnimation(trackIndex, AnimationState.EmptyAnimation, false); entry.mixDuration = mixDuration; @@ -785,7 +787,7 @@ namespace Spine { /// /// Adds an empty animation to be played after the current or last queued animation for a track, and sets the track entry's - /// . If the track is empty, it is equivalent to calling + /// . If the track has no entries, this is equivalent to calling /// . /// /// Track number. @@ -967,7 +969,7 @@ namespace Spine { public ExposedList Tracks { get { return tracks; } } override public string ToString () { - System.Text.StringBuilder buffer = new System.Text.StringBuilder(); + var buffer = new System.Text.StringBuilder(); TrackEntry[] tracksItems = tracks.Items; for (int i = 0, n = tracks.Count; i < n; i++) { TrackEntry entry = tracksItems[i]; @@ -991,7 +993,7 @@ namespace Spine { internal TrackEntry previous, next, mixingFrom, mixingTo; // difference to libgdx reference: delegates are used for event callbacks instead of 'AnimationStateListener listener'. - /// See + /// See /// API Reference documentation pages here for details. Usage in C# and spine-unity is explained /// here /// on the spine-unity documentation pages. diff --git a/spine-csharp/src/AnimationStateData.cs b/spine-csharp/src/AnimationStateData.cs index 88079cff4..38dfa4a33 100644 --- a/spine-csharp/src/AnimationStateData.cs +++ b/spine-csharp/src/AnimationStateData.cs @@ -64,7 +64,7 @@ namespace Spine { public void SetMix (Animation from, Animation to, float duration) { if (from == null) throw new ArgumentNullException("from", "from cannot be null."); if (to == null) throw new ArgumentNullException("to", "to cannot be null."); - AnimationPair key = new AnimationPair(from, to); + var key = new AnimationPair(from, to); animationToMixTime.Remove(key); animationToMixTime.Add(key, duration); } @@ -76,7 +76,7 @@ namespace Spine { public float GetMix (Animation from, Animation to) { if (from == null) throw new ArgumentNullException("from", "from cannot be null."); if (to == null) throw new ArgumentNullException("to", "to cannot be null."); - AnimationPair key = new AnimationPair(from, to); + var key = new AnimationPair(from, to); float duration; if (animationToMixTime.TryGetValue(key, out duration)) return duration; return defaultMix; diff --git a/spine-csharp/src/Attachments/AtlasAttachmentLoader.cs b/spine-csharp/src/Attachments/AtlasAttachmentLoader.cs index 92a2fafae..65c41cfd9 100644 --- a/spine-csharp/src/Attachments/AtlasAttachmentLoader.cs +++ b/spine-csharp/src/Attachments/AtlasAttachmentLoader.cs @@ -53,7 +53,7 @@ namespace Spine { } public RegionAttachment NewRegionAttachment (Skin skin, string name, string path, Sequence sequence) { - RegionAttachment attachment = new RegionAttachment(name); + var attachment = new RegionAttachment(name); if (sequence != null) LoadSequence(name, path, sequence); else { @@ -66,7 +66,7 @@ namespace Spine { } public MeshAttachment NewMeshAttachment (Skin skin, string name, string path, Sequence sequence) { - MeshAttachment attachment = new MeshAttachment(name); + var attachment = new MeshAttachment(name); if (sequence != null) LoadSequence(name, path, sequence); else { diff --git a/spine-csharp/src/Attachments/MeshAttachment.cs b/spine-csharp/src/Attachments/MeshAttachment.cs index cf55ee748..85a9685f8 100644 --- a/spine-csharp/src/Attachments/MeshAttachment.cs +++ b/spine-csharp/src/Attachments/MeshAttachment.cs @@ -199,7 +199,7 @@ namespace Spine { /// Returns a new mesh with this mesh set as the . public MeshAttachment NewLinkedMesh () { - MeshAttachment mesh = new MeshAttachment(Name); + var mesh = new MeshAttachment(Name); mesh.timelineAttachment = timelineAttachment; mesh.region = region; diff --git a/spine-csharp/src/Attachments/Sequence.cs b/spine-csharp/src/Attachments/Sequence.cs index ac78ee07c..81d148588 100644 --- a/spine-csharp/src/Attachments/Sequence.cs +++ b/spine-csharp/src/Attachments/Sequence.cs @@ -79,7 +79,7 @@ namespace Spine { } public string GetPath (string basePath, int index) { - StringBuilder buffer = new StringBuilder(basePath.Length + digits); + var buffer = new StringBuilder(basePath.Length + digits); buffer.Append(basePath); string frame = (start + index).ToString(); for (int i = digits - frame.Length; i > 0; i--) diff --git a/spine-csharp/src/PathConstraint.cs b/spine-csharp/src/PathConstraint.cs index 3af363fa6..1f21c3fbb 100644 --- a/spine-csharp/src/PathConstraint.cs +++ b/spine-csharp/src/PathConstraint.cs @@ -45,7 +45,7 @@ namespace Spine { internal readonly PathConstraintData data; internal readonly ExposedList bones; - internal Slot target; + internal Slot slot; internal float position, spacing, mixRotate, mixX, mixY; internal bool active; @@ -63,7 +63,7 @@ namespace Spine { foreach (BoneData boneData in data.bones) bones.Add(skeleton.bones.Items[boneData.index]); - target = skeleton.slots.Items[data.target.index]; + slot = skeleton.slots.Items[data.slot.index]; position = data.position; spacing = data.spacing; @@ -98,7 +98,7 @@ namespace Spine { } public void Update (Physics physics) { - PathAttachment attachment = target.Attachment as PathAttachment; + PathAttachment attachment = slot.Attachment as PathAttachment; if (attachment == null) return; float mixRotate = this.mixRotate, mixX = this.mixX, mixY = this.mixY; @@ -171,7 +171,7 @@ namespace Spine { tip = data.rotateMode == RotateMode.Chain; } else { tip = false; - Bone p = target.bone; + Bone p = slot.bone; offsetRotation *= p.a * p.d - p.b * p.c > 0 ? MathUtils.DegRad : -MathUtils.DegRad; } for (int i = 0, p = 3; i < boneCount; i++, p += 3) { @@ -223,7 +223,7 @@ namespace Spine { } float[] ComputeWorldPositions (PathAttachment path, int spacesCount, bool tangents) { - Slot target = this.target; + Slot target = this.slot; float position = this.position; float[] spaces = this.spaces.Items, output = this.positions.Resize(spacesCount * 3 + 2).Items, world; bool closed = path.Closed; @@ -506,7 +506,7 @@ namespace Spine { /// The bones that will be modified by this path constraint. public ExposedList Bones { get { return bones; } } /// The slot whose path attachment will be used to constrained the bones. - public Slot Target { get { return target; } set { target = value; } } + public Slot Target { get { return slot; } set { slot = value; } } public bool Active { get { return active; } } /// The path constraint's setup pose data. public PathConstraintData Data { get { return data; } } diff --git a/spine-csharp/src/PathConstraintData.cs b/spine-csharp/src/PathConstraintData.cs index 89f6e4627..5025d53d8 100644 --- a/spine-csharp/src/PathConstraintData.cs +++ b/spine-csharp/src/PathConstraintData.cs @@ -32,7 +32,7 @@ using System; namespace Spine { public class PathConstraintData : ConstraintData { internal ExposedList bones = new ExposedList(); - internal SlotData target; + internal SlotData slot; internal PositionMode positionMode; internal SpacingMode spacingMode; internal RotateMode rotateMode; @@ -43,7 +43,7 @@ namespace Spine { } public ExposedList Bones { get { return bones; } } - public SlotData Target { get { return target; } set { target = value; } } + public SlotData Slot { get { return slot; } set { slot = value; } } public PositionMode PositionMode { get { return positionMode; } set { positionMode = value; } } public SpacingMode SpacingMode { get { return spacingMode; } set { spacingMode = value; } } public RotateMode RotateMode { get { return rotateMode; } set { rotateMode = value; } } diff --git a/spine-csharp/src/PhysicsConstraint.cs b/spine-csharp/src/PhysicsConstraint.cs index d3dc00528..0c751947e 100644 --- a/spine-csharp/src/PhysicsConstraint.cs +++ b/spine-csharp/src/PhysicsConstraint.cs @@ -39,7 +39,7 @@ namespace Spine { /// public class PhysicsConstraint : IUpdatable { internal readonly PhysicsConstraintData data; - public Bone bone; + internal Bone bone; internal float inertia, strength, damping, massInverse, wind, gravity, mix; bool reset = true; diff --git a/spine-csharp/src/Skeleton.cs b/spine-csharp/src/Skeleton.cs index ac3b6a98f..44bfa4f1f 100644 --- a/spine-csharp/src/Skeleton.cs +++ b/spine-csharp/src/Skeleton.cs @@ -289,8 +289,7 @@ namespace Spine { && (!constraint.data.skinRequired || (skin != null && skin.constraints.Contains(constraint.data))); if (!constraint.active) return; - Bone target = constraint.target; - SortBone(target); + SortBone(constraint.target); ExposedList constrained = constraint.bones; Bone parent = constrained.Items[0]; @@ -311,15 +310,15 @@ namespace Spine { } private void SortTransformConstraint (TransformConstraint constraint) { - constraint.active = constraint.target.active + constraint.active = constraint.source.active && (!constraint.data.skinRequired || (skin != null && skin.constraints.Contains(constraint.data))); if (!constraint.active) return; - SortBone(constraint.target); + SortBone(constraint.source); Bone[] constrained = constraint.bones.Items; int boneCount = constraint.bones.Count; - if (constraint.data.local) { + if (constraint.data.localSource) { for (int i = 0; i < boneCount; i++) { Bone child = constrained[i]; SortBone(child.parent); @@ -339,19 +338,18 @@ namespace Spine { } private void SortPathConstraint (PathConstraint constraint) { - constraint.active = constraint.target.bone.active + constraint.active = constraint.slot.bone.active && (!constraint.data.skinRequired || (skin != null && skin.constraints.Contains(constraint.data))); if (!constraint.active) return; - Slot slot = constraint.target; + Slot slot = constraint.slot; 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); - Attachment attachment = slot.attachment; - if (attachment is PathAttachment) SortPathConstraintAttachment(attachment, slotBone); + SortPathConstraintAttachment(slot.attachment, slotBone); Bone[] constrained = constraint.bones.Items; int boneCount = constraint.bones.Count; diff --git a/spine-csharp/src/SkeletonBinary.cs b/spine-csharp/src/SkeletonBinary.cs index df365dda2..08c32d0b3 100644 --- a/spine-csharp/src/SkeletonBinary.cs +++ b/spine-csharp/src/SkeletonBinary.cs @@ -42,6 +42,22 @@ using Windows.Storage; #endif namespace Spine { + + using FromProperty = TransformConstraintData.FromProperty; + using FromRotate = TransformConstraintData.FromRotate; + using FromScaleX = TransformConstraintData.FromScaleX; + using FromScaleY = TransformConstraintData.FromScaleY; + using FromShearY = TransformConstraintData.FromShearY; + using FromX = TransformConstraintData.FromX; + using FromY = TransformConstraintData.FromY; + using ToProperty = TransformConstraintData.ToProperty; + using ToRotate = TransformConstraintData.ToRotate; + using ToScaleX = TransformConstraintData.ToScaleX; + using ToScaleY = TransformConstraintData.ToScaleY; + using ToShearY = TransformConstraintData.ToShearY; + using ToX = TransformConstraintData.ToX; + using ToY = TransformConstraintData.ToY; + public class SkeletonBinary : SkeletonLoader { public const int BONE_ROTATE = 0; public const int BONE_TRANSLATE = 1; @@ -122,8 +138,7 @@ namespace Spine { /// Returns the version string of binary skeleton data. public static string GetVersionString (Stream file) { if (file == null) throw new ArgumentNullException("file"); - - SkeletonInput input = new SkeletonInput(file); + var input = new SkeletonInput(file); return input.GetVersionString(); } @@ -131,8 +146,8 @@ namespace Spine { if (file == null) throw new ArgumentNullException("file"); float scale = this.scale; - SkeletonData skeletonData = new SkeletonData(); - SkeletonInput input = new SkeletonInput(file); + var skeletonData = new SkeletonData(); + var input = new SkeletonInput(file); long hash = input.ReadLong(); skeletonData.hash = hash == 0 ? null : hash.ToString(); @@ -171,7 +186,7 @@ namespace Spine { for (int i = 0; i < n; i++) { String name = input.ReadString(); BoneData parent = i == 0 ? null : bones[input.ReadInt(true)]; - BoneData data = new BoneData(i, name, parent); + var data = new BoneData(i, name, parent); data.rotation = input.ReadFloat(); data.x = input.ReadFloat() * scale; data.y = input.ReadFloat() * scale; @@ -196,7 +211,7 @@ namespace Spine { String slotName = input.ReadString(); BoneData boneData = bones[input.ReadInt(true)]; - SlotData slotData = new SlotData(i, slotName, boneData); + var slotData = new SlotData(i, slotName, boneData); int color = input.ReadInt(); slotData.r = ((color & 0xff000000) >> 24) / 255f; slotData.g = ((color & 0x00ff0000) >> 16) / 255f; @@ -222,7 +237,7 @@ namespace Spine { // IK constraints. o = skeletonData.ikConstraints.Resize(n = input.ReadInt(true)).Items; for (int i = 0, nn; i < n; i++) { - IkConstraintData data = new IkConstraintData(input.ReadString()); + var data = new IkConstraintData(input.ReadString()); data.order = input.ReadInt(true); BoneData[] constraintBones = data.bones.Resize(nn = input.ReadInt(true)).Items; for (int ii = 0; ii < nn; ii++) @@ -242,42 +257,72 @@ namespace Spine { // Transform constraints. o = skeletonData.transformConstraints.Resize(n = input.ReadInt(true)).Items; for (int i = 0, nn; i < n; i++) { - TransformConstraintData data = new TransformConstraintData(input.ReadString()); + var data = new TransformConstraintData(input.ReadString()); data.order = input.ReadInt(true); BoneData[] constraintBones = data.bones.Resize(nn = input.ReadInt(true)).Items; for (int ii = 0; ii < nn; ii++) constraintBones[ii] = bones[input.ReadInt(true)]; - data.target = bones[input.ReadInt(true)]; + data.source = bones[input.ReadInt(true)]; int flags = input.Read(); data.skinRequired = (flags & 1) != 0; - data.local = (flags & 2) != 0; - data.relative = (flags & 4) != 0; - if ((flags & 8) != 0) data.offsetRotation = input.ReadFloat(); - if ((flags & 16) != 0) data.offsetX = input.ReadFloat() * scale; - if ((flags & 32) != 0) data.offsetY = input.ReadFloat() * scale; - if ((flags & 64) != 0) data.offsetScaleX = input.ReadFloat(); - if ((flags & 128) != 0) data.offsetScaleY = input.ReadFloat(); + data.localSource = (flags & 2) != 0; + data.localTarget = (flags & 4) != 0; + data.additive = (flags & 8) != 0; + data.clamp = (flags & 16) != 0; + FromProperty[] froms = data.properties.Resize(nn = flags >> 5).Items; + for (int ii = 0, tn; ii < nn; ii++) { + FromProperty from; + switch (input.ReadSByte()) { + case 0: from = new FromRotate(); break; + case 1: from = new FromX(); break; + case 2: from = new FromY(); break; + case 3: from = new FromScaleX(); break; + case 4: from = new FromScaleY(); break; + case 5: from = new FromShearY(); break; + default: from = null; break; + }; + from.offset = input.ReadFloat() * scale; + ToProperty[] tos = from.to.Resize(tn = input.ReadSByte()).Items; + for (int t = 0; t < tn; t++) { + ToProperty to; + switch (input.ReadSByte()) { + case 0: to = new ToRotate(); break; + case 1: to = new ToX(); break; + case 2: to = new ToY(); break; + case 3: to = new ToScaleX(); break; + case 4: to = new ToScaleY(); break; + case 5: to = new ToShearY(); break; + default: to = null; break; + }; + to.offset = input.ReadFloat() * scale; + to.max = input.ReadFloat() * scale; + to.scale = input.ReadFloat(); + tos[t] = to; + } + froms[ii] = from; + } flags = input.Read(); - if ((flags & 1) != 0) data.offsetShearY = input.ReadFloat(); - if ((flags & 2) != 0) data.mixRotate = input.ReadFloat(); - if ((flags & 4) != 0) data.mixX = input.ReadFloat(); - if ((flags & 8) != 0) data.mixY = input.ReadFloat(); - if ((flags & 16) != 0) data.mixScaleX = input.ReadFloat(); - if ((flags & 32) != 0) data.mixScaleY = input.ReadFloat(); - if ((flags & 64) != 0) data.mixShearY = input.ReadFloat(); + if ((flags & 1) != 0) data.offsetX = input.ReadFloat(); + if ((flags & 2) != 0) data.offsetY = input.ReadFloat(); + if ((flags & 4) != 0) data.mixRotate = input.ReadFloat(); + if ((flags & 8) != 0) data.mixX = input.ReadFloat(); + if ((flags & 16) != 0) data.mixY = input.ReadFloat(); + if ((flags & 32) != 0) data.mixScaleX = input.ReadFloat(); + if ((flags & 64) != 0) data.mixScaleY = input.ReadFloat(); + if ((flags & 128) != 0) data.mixShearY = input.ReadFloat(); o[i] = data; } // Path constraints o = skeletonData.pathConstraints.Resize(n = input.ReadInt(true)).Items; for (int i = 0, nn; i < n; i++) { - PathConstraintData data = new PathConstraintData(input.ReadString()); + var data = new PathConstraintData(input.ReadString()); data.order = input.ReadInt(true); data.skinRequired = input.ReadBoolean(); BoneData[] constraintBones = data.bones.Resize(nn = input.ReadInt(true)).Items; for (int ii = 0; ii < nn; ii++) constraintBones[ii] = bones[input.ReadInt(true)]; - data.target = slots[input.ReadInt(true)]; + data.slot = slots[input.ReadInt(true)]; int flags = input.Read(); data.positionMode = (PositionMode)Enum.GetValues(typeof(PositionMode)).GetValue(flags & 1); data.spacingMode = (SpacingMode)Enum.GetValues(typeof(SpacingMode)).GetValue((flags >> 1) & 3); @@ -297,7 +342,7 @@ namespace Spine { // Physics constraints. o = skeletonData.physicsConstraints.Resize(n = input.ReadInt(true)).Items; for (int i = 0; i < n; i++) { - PhysicsConstraintData data = new PhysicsConstraintData(input.ReadString()); + var data = new PhysicsConstraintData(input.ReadString()); data.order = input.ReadInt(true); data.bone = bones[input.ReadInt(true)]; int flags = input.Read(); @@ -358,7 +403,7 @@ namespace Spine { // Events. o = skeletonData.events.Resize(n = input.ReadInt(true)).Items; for (int i = 0; i < n; i++) { - EventData data = new EventData(input.ReadString()); + var data = new EventData(input.ReadString()); data.Int = input.ReadInt(false); data.Float = input.ReadFloat(); data.String = input.ReadString(); @@ -547,7 +592,7 @@ namespace Spine { bool closed = (flags & 16) != 0; bool constantSpeed = (flags & 32) != 0; Vertices vertices = ReadVertices(input, (flags & 64) != 0); - float[] lengths = new float[vertices.length / 6]; + var lengths = new float[vertices.length / 6]; for (int i = 0, n = lengths.Length; i < n; i++) lengths[i] = input.ReadFloat() * scale; if (nonessential) input.ReadInt(); //int color = nonessential ? input.ReadInt() : 0; @@ -596,7 +641,7 @@ namespace Spine { } private Sequence ReadSequence (SkeletonInput input) { - Sequence sequence = new Sequence(input.ReadInt(true)); + var sequence = new Sequence(input.ReadInt(true)); sequence.Start = input.ReadInt(true); sequence.Digits = input.ReadInt(true); sequence.SetupIndex = input.ReadInt(true); @@ -606,14 +651,14 @@ namespace Spine { private Vertices ReadVertices (SkeletonInput input, bool weighted) { float scale = this.scale; int vertexCount = input.ReadInt(true); - Vertices vertices = new Vertices(); + var vertices = new Vertices(); vertices.length = vertexCount << 1; if (!weighted) { vertices.vertices = ReadFloatArray(input, vertices.length, scale); return vertices; } - ExposedList weights = new ExposedList(vertices.length * 3 * 3); - ExposedList bonesArray = new ExposedList(vertices.length * 3); + var weights = new ExposedList(vertices.length * 3 * 3); + var bonesArray = new ExposedList(vertices.length * 3); for (int i = 0; i < vertexCount; i++) { int boneCount = input.ReadInt(true); bonesArray.Add(boneCount); @@ -631,7 +676,7 @@ namespace Spine { } private float[] ReadFloatArray (SkeletonInput input, int n, float scale) { - float[] array = new float[n]; + var array = new float[n]; if (scale == 1) { for (int i = 0; i < n; i++) array[i] = input.ReadFloat(); @@ -643,7 +688,7 @@ namespace Spine { } private int[] ReadShortArray (SkeletonInput input, int n) { - int[] array = new int[n]; + var array = new int[n]; for (int i = 0; i < n; i++) array[i] = input.ReadInt(true); return array; @@ -652,7 +697,7 @@ namespace Spine { /// SerializationException will be thrown when a Vertex attachment is not found. /// Throws IOException when a read operation fails. private Animation ReadAnimation (String name, SkeletonInput input, SkeletonData skeletonData) { - ExposedList timelines = new ExposedList(input.ReadInt(true)); + var timelines = new ExposedList(input.ReadInt(true)); float scale = this.scale; // Slot timelines. @@ -662,14 +707,14 @@ namespace Spine { int timelineType = input.ReadUByte(), frameCount = input.ReadInt(true), frameLast = frameCount - 1; switch (timelineType) { case SLOT_ATTACHMENT: { - AttachmentTimeline timeline = new AttachmentTimeline(frameCount, slotIndex); + var timeline = new AttachmentTimeline(frameCount, slotIndex); for (int frame = 0; frame < frameCount; frame++) timeline.SetFrame(frame, input.ReadFloat(), input.ReadStringRef()); timelines.Add(timeline); break; } case SLOT_RGBA: { - RGBATimeline timeline = new RGBATimeline(frameCount, input.ReadInt(true), slotIndex); + var timeline = new RGBATimeline(frameCount, input.ReadInt(true), slotIndex); float time = input.ReadFloat(); float r = input.Read() / 255f, g = input.Read() / 255f; float b = input.Read() / 255f, a = input.Read() / 255f; @@ -700,7 +745,7 @@ namespace Spine { break; } case SLOT_RGB: { - RGBTimeline timeline = new RGBTimeline(frameCount, input.ReadInt(true), slotIndex); + var timeline = new RGBTimeline(frameCount, input.ReadInt(true), slotIndex); float time = input.ReadFloat(); float r = input.Read() / 255f, g = input.Read() / 255f, b = input.Read() / 255f; for (int frame = 0, bezier = 0; ; frame++) { @@ -727,7 +772,7 @@ namespace Spine { break; } case SLOT_RGBA2: { - RGBA2Timeline timeline = new RGBA2Timeline(frameCount, input.ReadInt(true), slotIndex); + var timeline = new RGBA2Timeline(frameCount, input.ReadInt(true), slotIndex); float time = input.ReadFloat(); float r = input.Read() / 255f, g = input.Read() / 255f; float b = input.Read() / 255f, a = input.Read() / 255f; @@ -766,7 +811,7 @@ namespace Spine { break; } case SLOT_RGB2: { - RGB2Timeline timeline = new RGB2Timeline(frameCount, input.ReadInt(true), slotIndex); + var timeline = new RGB2Timeline(frameCount, input.ReadInt(true), slotIndex); float time = input.ReadFloat(); float r = input.Read() / 255f, g = input.Read() / 255f, b = input.Read() / 255f; float r2 = input.Read() / 255f, g2 = input.Read() / 255f, b2 = input.Read() / 255f; @@ -801,7 +846,7 @@ namespace Spine { break; } case SLOT_ALPHA: { - AlphaTimeline timeline = new AlphaTimeline(frameCount, input.ReadInt(true), slotIndex); + var timeline = new AlphaTimeline(frameCount, input.ReadInt(true), slotIndex); float time = input.ReadFloat(), a = input.Read() / 255f; for (int frame = 0, bezier = 0; ; frame++) { timeline.SetFrame(frame, time, a); @@ -832,7 +877,7 @@ namespace Spine { for (int ii = 0, nn = input.ReadInt(true); ii < nn; ii++) { int type = input.ReadUByte(), frameCount = input.ReadInt(true); if (type == BONE_INHERIT) { - InheritTimeline timeline = new InheritTimeline(frameCount, boneIndex); + var timeline = new InheritTimeline(frameCount, boneIndex); for (int frame = 0; frame < frameCount; frame++) timeline.SetFrame(frame, input.ReadFloat(), InheritEnum.Values[input.ReadUByte()]); timelines.Add(timeline); @@ -877,7 +922,7 @@ namespace Spine { // IK constraint timelines. for (int i = 0, n = input.ReadInt(true); i < n; i++) { int index = input.ReadInt(true), frameCount = input.ReadInt(true), frameLast = frameCount - 1; - IkConstraintTimeline timeline = new IkConstraintTimeline(frameCount, input.ReadInt(true), index); + var timeline = new IkConstraintTimeline(frameCount, input.ReadInt(true), index); int flags = input.Read(); float time = input.ReadFloat(), mix = (flags & 1) != 0 ? ((flags & 2) != 0 ? input.ReadFloat() : 1) : 0; float softness = (flags & 4) != 0 ? input.ReadFloat() * scale : 0; @@ -904,7 +949,7 @@ namespace Spine { // Transform constraint timelines. for (int i = 0, n = input.ReadInt(true); i < n; i++) { int index = input.ReadInt(true), frameCount = input.ReadInt(true), frameLast = frameCount - 1; - TransformConstraintTimeline timeline = new TransformConstraintTimeline(frameCount, input.ReadInt(true), index); + var timeline = new TransformConstraintTimeline(frameCount, input.ReadInt(true), index); float time = input.ReadFloat(), mixRotate = input.ReadFloat(), mixX = input.ReadFloat(), mixY = input.ReadFloat(), mixScaleX = input.ReadFloat(), mixScaleY = input.ReadFloat(), mixShearY = input.ReadFloat(); for (int frame = 0, bezier = 0; ; frame++) { @@ -943,16 +988,18 @@ namespace Spine { for (int ii = 0, nn = input.ReadInt(true); ii < nn; ii++) { int type = input.ReadUByte(), frameCount = input.ReadInt(true), bezierCount = input.ReadInt(true); switch (type) { - case PATH_POSITION: + case PATH_POSITION: { ReadTimeline(input, timelines, new PathConstraintPositionTimeline(frameCount, bezierCount, index), data.positionMode == PositionMode.Fixed ? scale : 1); break; - case PATH_SPACING: + } + case PATH_SPACING: { ReadTimeline(input, timelines, new PathConstraintSpacingTimeline(frameCount, bezierCount, index), data.spacingMode == SpacingMode.Length || data.spacingMode == SpacingMode.Fixed ? scale : 1); break; - case PATH_MIX: - PathConstraintMixTimeline timeline = new PathConstraintMixTimeline(frameCount, bezierCount, index); + } + case PATH_MIX: { + var timeline = new PathConstraintMixTimeline(frameCount, bezierCount, index); float time = input.ReadFloat(), mixRotate = input.ReadFloat(), mixX = input.ReadFloat(), mixY = input.ReadFloat(); for (int frame = 0, bezier = 0, frameLast = timeline.FrameCount - 1; ; frame++) { timeline.SetFrame(frame, time, mixRotate, mixX, mixY); @@ -977,6 +1024,7 @@ namespace Spine { timelines.Add(timeline); break; } + } } } @@ -986,36 +1034,40 @@ namespace Spine { for (int ii = 0, nn = input.ReadInt(true); ii < nn; ii++) { int type = input.ReadUByte(), frameCount = input.ReadInt(true); if (type == PHYSICS_RESET) { - PhysicsConstraintResetTimeline timeline = new PhysicsConstraintResetTimeline(frameCount, index); + var timeline = new PhysicsConstraintResetTimeline(frameCount, index); for (int frame = 0; frame < frameCount; frame++) timeline.SetFrame(frame, input.ReadFloat()); timelines.Add(timeline); continue; } int bezierCount = input.ReadInt(true); + PhysicsConstraintTimeline newTimeline; switch (type) { case PHYSICS_INERTIA: - ReadTimeline(input, timelines, new PhysicsConstraintInertiaTimeline(frameCount, bezierCount, index), 1); + newTimeline = new PhysicsConstraintInertiaTimeline(frameCount, bezierCount, index); break; case PHYSICS_STRENGTH: - ReadTimeline(input, timelines, new PhysicsConstraintStrengthTimeline(frameCount, bezierCount, index), 1); + newTimeline = new PhysicsConstraintStrengthTimeline(frameCount, bezierCount, index); break; case PHYSICS_DAMPING: - ReadTimeline(input, timelines, new PhysicsConstraintDampingTimeline(frameCount, bezierCount, index), 1); + newTimeline = new PhysicsConstraintDampingTimeline(frameCount, bezierCount, index); break; case PHYSICS_MASS: - ReadTimeline(input, timelines, new PhysicsConstraintMassTimeline(frameCount, bezierCount, index), 1); + newTimeline = new PhysicsConstraintMassTimeline(frameCount, bezierCount, index); break; case PHYSICS_WIND: - ReadTimeline(input, timelines, new PhysicsConstraintWindTimeline(frameCount, bezierCount, index), 1); + newTimeline = new PhysicsConstraintWindTimeline(frameCount, bezierCount, index); break; case PHYSICS_GRAVITY: - ReadTimeline(input, timelines, new PhysicsConstraintGravityTimeline(frameCount, bezierCount, index), 1); + newTimeline = new PhysicsConstraintGravityTimeline(frameCount, bezierCount, index); break; case PHYSICS_MIX: - ReadTimeline(input, timelines, new PhysicsConstraintMixTimeline(frameCount, bezierCount, index), 1); + newTimeline = new PhysicsConstraintMixTimeline(frameCount, bezierCount, index); break; + default: + throw new SerializationException(); } + ReadTimeline(input, timelines, newTimeline, 1); } } @@ -1037,7 +1089,7 @@ namespace Spine { float[] vertices = vertexAttachment.Vertices; int deformLength = weighted ? (vertices.Length / 3) << 1 : vertices.Length; - DeformTimeline timeline = new DeformTimeline(frameCount, input.ReadInt(true), slotIndex, vertexAttachment); + var timeline = new DeformTimeline(frameCount, input.ReadInt(true), slotIndex, vertexAttachment); float time = input.ReadFloat(); for (int frame = 0, bezier = 0; ; frame++) { @@ -1078,7 +1130,7 @@ namespace Spine { break; } case ATTACHMENT_SEQUENCE: { - SequenceTimeline timeline = new SequenceTimeline(frameCount, slotIndex, attachment); + var timeline = new SequenceTimeline(frameCount, slotIndex, attachment); for (int frame = 0; frame < frameCount; frame++) { float time = input.ReadFloat(); int modeAndIndex = input.ReadInt(); @@ -1096,15 +1148,15 @@ namespace Spine { // Draw order timeline. int drawOrderCount = input.ReadInt(true); if (drawOrderCount > 0) { - DrawOrderTimeline timeline = new DrawOrderTimeline(drawOrderCount); + var timeline = new DrawOrderTimeline(drawOrderCount); int slotCount = skeletonData.slots.Count; for (int i = 0; i < drawOrderCount; i++) { float time = input.ReadFloat(); int offsetCount = input.ReadInt(true); - int[] drawOrder = new int[slotCount]; + var drawOrder = new int[slotCount]; for (int ii = slotCount - 1; ii >= 0; ii--) drawOrder[ii] = -1; - int[] unchanged = new int[slotCount - offsetCount]; + var unchanged = new int[slotCount - offsetCount]; int originalIndex = 0, unchangedIndex = 0; for (int ii = 0; ii < offsetCount; ii++) { int slotIndex = input.ReadInt(true); @@ -1128,11 +1180,11 @@ namespace Spine { // Event timeline. int eventCount = input.ReadInt(true); if (eventCount > 0) { - EventTimeline timeline = new EventTimeline(eventCount); + var timeline = new EventTimeline(eventCount); for (int i = 0; i < eventCount; i++) { float time = input.ReadFloat(); EventData eventData = skeletonData.events.Items[input.ReadInt(true)]; - Event e = new Event(time, eventData); + var e = new Event(time, eventData); e.intValue = input.ReadInt(false); e.floatValue = input.ReadFloat(); e.stringValue = input.ReadString(); @@ -1353,7 +1405,7 @@ namespace Spine { byteCount = ReadInt(true); if (byteCount > 1 && byteCount <= 13) { byteCount--; - byte[] buffer = new byte[byteCount]; + var buffer = new byte[byteCount]; ReadFully(buffer, 0, byteCount); return System.Text.Encoding.UTF8.GetString(buffer, 0, byteCount); } diff --git a/spine-csharp/src/SkeletonJson.cs b/spine-csharp/src/SkeletonJson.cs index 6a1b31119..593170bbd 100644 --- a/spine-csharp/src/SkeletonJson.cs +++ b/spine-csharp/src/SkeletonJson.cs @@ -42,6 +42,21 @@ using Windows.Storage; namespace Spine { + using FromProperty = TransformConstraintData.FromProperty; + using FromRotate = TransformConstraintData.FromRotate; + using FromScaleX = TransformConstraintData.FromScaleX; + using FromScaleY = TransformConstraintData.FromScaleY; + using FromShearY = TransformConstraintData.FromShearY; + using FromX = TransformConstraintData.FromX; + using FromY = TransformConstraintData.FromY; + using ToProperty = TransformConstraintData.ToProperty; + using ToRotate = TransformConstraintData.ToRotate; + using ToScaleX = TransformConstraintData.ToScaleX; + using ToScaleY = TransformConstraintData.ToScaleY; + using ToShearY = TransformConstraintData.ToShearY; + using ToX = TransformConstraintData.ToX; + using ToY = TransformConstraintData.ToY; + /// /// Loads skeleton data in the Spine JSON format. /// @@ -94,7 +109,7 @@ namespace Spine { if (reader == null) throw new ArgumentNullException("reader", "reader cannot be null."); float scale = this.scale; - SkeletonData skeletonData = new SkeletonData(); + var skeletonData = new SkeletonData(); Dictionary root = Json.Deserialize(reader) as Dictionary; if (root == null) throw new Exception("Invalid JSON."); @@ -123,7 +138,7 @@ namespace Spine { if (parent == null) throw new Exception("Parent bone not found: " + boneMap["parent"]); } - BoneData data = new BoneData(skeletonData.Bones.Count, (string)boneMap["name"], 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; @@ -148,7 +163,7 @@ namespace Spine { string boneName = (string)slotMap["bone"]; BoneData boneData = skeletonData.FindBone(boneName); if (boneData == null) throw new Exception("Slot bone not found: " + boneName); - SlotData data = new SlotData(skeletonData.Slots.Count, slotName, boneData); + var data = new SlotData(skeletonData.Slots.Count, slotName, boneData); if (slotMap.ContainsKey("color")) { string color = (string)slotMap["color"]; @@ -179,7 +194,7 @@ namespace Spine { // IK constraints. if (root.ContainsKey("ik")) { foreach (Dictionary constraintMap in (List)root["ik"]) { - IkConstraintData data = new IkConstraintData((string)constraintMap["name"]); + var data = new IkConstraintData((string)constraintMap["name"]); data.order = GetInt(constraintMap, "order", 0); data.skinRequired = GetBoolean(constraintMap, "skin", false); @@ -208,7 +223,7 @@ namespace Spine { // Transform constraints. if (root.ContainsKey("transform")) { foreach (Dictionary constraintMap in (List)root["transform"]) { - TransformConstraintData data = new TransformConstraintData((string)constraintMap["name"]); + var data = new TransformConstraintData((string)constraintMap["name"]); data.order = GetInt(constraintMap, "order", 0); data.skinRequired = GetBoolean(constraintMap, "skin", false); @@ -220,26 +235,89 @@ namespace Spine { } } - string targetName = (string)constraintMap["target"]; - data.target = skeletonData.FindBone(targetName); - if (data.target == null) throw new Exception("Transform constraint target bone not found: " + targetName); + string sourceName = (string)constraintMap["source"]; + data.source = skeletonData.FindBone(sourceName); + if (data.source == null) throw new Exception("Transform constraint source bone not found: " + sourceName); - data.local = GetBoolean(constraintMap, "local", false); - data.relative = GetBoolean(constraintMap, "relative", false); + data.localSource = GetBoolean(constraintMap, "localSource", false); + data.additive = GetBoolean(constraintMap, "additive", false); + data.clamp = GetBoolean(constraintMap, "clamp", false); - data.offsetRotation = GetFloat(constraintMap, "rotation", 0); - data.offsetX = GetFloat(constraintMap, "x", 0) * scale; - data.offsetY = GetFloat(constraintMap, "y", 0) * scale; - data.offsetScaleX = GetFloat(constraintMap, "scaleX", 0); - data.offsetScaleY = GetFloat(constraintMap, "scaleY", 0); - data.offsetShearY = GetFloat(constraintMap, "shearY", 0); + bool rotate = false, x = false, y = false, scaleX = false, scaleY = false, shearY = false; + if (constraintMap.ContainsKey("properties")) { + foreach (KeyValuePair fromEntryObject in (Dictionary)constraintMap["properties"]) { + var fromEntry = (Dictionary)fromEntryObject.Value; + string fromEntryName = fromEntryObject.Key; - data.mixRotate = GetFloat(constraintMap, "mixRotate", 1); - data.mixX = GetFloat(constraintMap, "mixX", 1); - data.mixY = GetFloat(constraintMap, "mixY", data.mixX); - data.mixScaleX = GetFloat(constraintMap, "mixScaleX", 1); - data.mixScaleY = GetFloat(constraintMap, "mixScaleY", data.mixScaleX); - data.mixShearY = GetFloat(constraintMap, "mixShearY", 1); + FromProperty from; + switch (fromEntryName) { + case "rotate": from = new FromRotate(); break; + case "x": from = new FromX(); break; + case "y": from = new FromY(); break; + case "scaleX": from = new FromScaleX(); break; + case "scaleY": from = new FromScaleY(); break; + case "shearY": from = new FromShearY(); break; + default: throw new Exception("Invalid transform constraint from property: " + fromEntryName); + }; + + from.offset = GetFloat(fromEntry, "offset", 0) * scale; + if (fromEntry.ContainsKey("to")) { + foreach (KeyValuePair toEntryObject in (Dictionary)fromEntry["to"]) { + var toEntry = (Dictionary)toEntryObject.Value; + string toEntryName = toEntryObject.Key; + + ToProperty to; + switch (toEntryName) { + case "rotate": { + rotate = true; + to = new ToRotate(); + break; + } + case "x": { + x = true; + to = new ToX(); + break; + } + case "y": { + y = true; + to = new ToY(); + break; + } + case "scaleX": { + scaleX = true; + to = new ToScaleX(); + break; + } + case "scaleY": { + scaleY = true; + to = new ToScaleY(); + break; + } + case "shearY": { + shearY = true; + to = new ToShearY(); + break; + } + default: throw new Exception("Invalid transform constraint to property: " + toEntryName); + } + to.offset = GetFloat(toEntry, "offset", 0) * scale; + to.max = GetFloat(toEntry, "max", 1) * scale; + to.scale = GetFloat(toEntry, "scale"); + from.to.Add(to); + } + } + if (from.to.Count != 0) data.properties.Add(from); + } + } + + data.offsetX = GetFloat(constraintMap, "x", 0); + data.offsetY = GetFloat(constraintMap, "y", 0); + if (rotate) data.mixRotate = GetFloat(constraintMap, "mixRotate", 1); + if (x) data.mixX = GetFloat(constraintMap, "mixX", 1); + if (y) data.mixY = GetFloat(constraintMap, "mixY", data.mixX); + if (scaleX) data.mixScaleX = GetFloat(constraintMap, "mixScaleX", 1); + if (scaleY) data.mixScaleY = GetFloat(constraintMap, "mixScaleY", data.mixScaleX); + if (shearY) data.mixShearY = GetFloat(constraintMap, "mixShearY", 1); skeletonData.transformConstraints.Add(data); } @@ -248,7 +326,7 @@ namespace Spine { // Path constraints. if (root.ContainsKey("path")) { foreach (Dictionary constraintMap in (List)root["path"]) { - PathConstraintData data = new PathConstraintData((string)constraintMap["name"]); + var data = new PathConstraintData((string)constraintMap["name"]); data.order = GetInt(constraintMap, "order", 0); data.skinRequired = GetBoolean(constraintMap, "skin", false); @@ -260,9 +338,9 @@ namespace Spine { } } - string targetName = (string)constraintMap["target"]; - data.target = skeletonData.FindSlot(targetName); - if (data.target == null) throw new Exception("Path target slot not found: " + targetName); + string slotName = (string)constraintMap["slot"]; + data.slot = skeletonData.FindSlot(slotName); + if (data.slot == null) throw new Exception("Path slot not found: " + slotName); data.positionMode = (PositionMode)Enum.Parse(typeof(PositionMode), GetString(constraintMap, "positionMode", "percent"), true); data.spacingMode = (SpacingMode)Enum.Parse(typeof(SpacingMode), GetString(constraintMap, "spacingMode", "length"), true); @@ -283,7 +361,7 @@ namespace Spine { // Physics constraints. if (root.ContainsKey("physics")) { foreach (Dictionary constraintMap in (List)root["physics"]) { - PhysicsConstraintData data = new PhysicsConstraintData((string)constraintMap["name"]); + var data = new PhysicsConstraintData((string)constraintMap["name"]); data.order = GetInt(constraintMap, "order", 0); data.skinRequired = GetBoolean(constraintMap, "skin", false); @@ -320,7 +398,7 @@ namespace Spine { // Skins. if (root.ContainsKey("skins")) { foreach (Dictionary skinMap in (List)root["skins"]) { - Skin skin = new Skin((string)skinMap["name"]); + var skin = new Skin((string)skinMap["name"]); if (skinMap.ContainsKey("bones")) { foreach (string entryName in (List)skinMap["bones"]) { BoneData bone = skeletonData.FindBone(entryName); @@ -393,7 +471,7 @@ namespace Spine { if (root.ContainsKey("events")) { foreach (KeyValuePair entry in (Dictionary)root["events"]) { Dictionary entryMap = (Dictionary)entry.Value; - EventData data = new EventData(entry.Key); + var data = new EventData(entry.Key); data.Int = GetInt(entryMap, "int", 0); data.Float = GetFloat(entryMap, "float", 0); data.String = GetString(entryMap, "string", string.Empty); @@ -553,7 +631,7 @@ namespace Spine { public static Sequence ReadSequence (object sequenceJson) { Dictionary map = sequenceJson as Dictionary; if (map == null) return null; - Sequence sequence = new Sequence(GetInt(map, "count")); + var sequence = new Sequence(GetInt(map, "count")); sequence.start = GetInt(map, "start", 1); sequence.digits = GetInt(map, "digits", 0); sequence.setupIndex = GetInt(map, "setup", 0); @@ -573,8 +651,8 @@ namespace Spine { attachment.vertices = vertices; return; } - ExposedList weights = new ExposedList(verticesLength * 3 * 3); - ExposedList bones = new ExposedList(verticesLength * 3); + var weights = new ExposedList(verticesLength * 3 * 3); + var bones = new ExposedList(verticesLength * 3); for (int i = 0, n = vertices.Length; i < n;) { int boneCount = (int)vertices[i++]; bones.Add(boneCount); @@ -598,7 +676,7 @@ namespace Spine { private void ReadAnimation (Dictionary map, string name, SkeletonData skeletonData) { float scale = this.scale; - ExposedList timelines = new ExposedList(); + var timelines = new ExposedList(); // Slot timelines. if (map.ContainsKey("slots")) { @@ -611,16 +689,18 @@ namespace Spine { int frames = values.Count; if (frames == 0) continue; string timelineName = (string)timelineEntry.Key; - if (timelineName == "attachment") { - AttachmentTimeline timeline = new AttachmentTimeline(frames, slotIndex); + switch (timelineName) { + case "attachment": { + var timeline = new AttachmentTimeline(frames, slotIndex); int frame = 0; foreach (Dictionary keyMap in values) { timeline.SetFrame(frame++, GetFloat(keyMap, "time", 0), GetString(keyMap, "name", null)); } timelines.Add(timeline); - - } else if (timelineName == "rgba") { - RGBATimeline timeline = new RGBATimeline(frames, frames << 2, slotIndex); + break; + } + case "rgba": { + var timeline = new RGBATimeline(frames, frames << 2, slotIndex); List.Enumerator keyMapEnumerator = values.GetEnumerator(); keyMapEnumerator.MoveNext(); @@ -661,9 +741,10 @@ namespace Spine { keyMap = nextMap; } timelines.Add(timeline); - - } else if (timelineName == "rgb") { - RGBTimeline timeline = new RGBTimeline(frames, frames * 3, slotIndex); + break; + } + case "rgb": { + var timeline = new RGBTimeline(frames, frames * 3, slotIndex); List.Enumerator keyMapEnumerator = values.GetEnumerator(); keyMapEnumerator.MoveNext(); @@ -700,14 +781,16 @@ namespace Spine { keyMap = nextMap; } timelines.Add(timeline); - - } else if (timelineName == "alpha") { + break; + } + case "alpha": { List.Enumerator keyMapEnumerator = values.GetEnumerator(); keyMapEnumerator.MoveNext(); timelines.Add(ReadTimeline(ref keyMapEnumerator, new AlphaTimeline(frames, frames, slotIndex), 0, 1)); - - } else if (timelineName == "rgba2") { - RGBA2Timeline timeline = new RGBA2Timeline(frames, frames * 7, slotIndex); + break; + } + case "rgba2": { + var timeline = new RGBA2Timeline(frames, frames * 7, slotIndex); List.Enumerator keyMapEnumerator = values.GetEnumerator(); keyMapEnumerator.MoveNext(); @@ -762,9 +845,10 @@ namespace Spine { keyMap = nextMap; } timelines.Add(timeline); - - } else if (timelineName == "rgb2") { - RGB2Timeline timeline = new RGB2Timeline(frames, frames * 6, slotIndex); + break; + } + case "rgb2": { + var timeline = new RGB2Timeline(frames, frames * 6, slotIndex); List.Enumerator keyMapEnumerator = values.GetEnumerator(); keyMapEnumerator.MoveNext(); @@ -815,9 +899,11 @@ namespace Spine { keyMap = nextMap; } timelines.Add(timeline); - - } else + break; + } + default: throw new Exception("Invalid timeline type for a slot: " + timelineName + " (" + slotName + ")"); + } } } } @@ -842,33 +928,46 @@ namespace Spine { if (!keyMapEnumerator.MoveNext()) continue; int frames = values.Count; string timelineName = (string)timelineEntry.Key; - if (timelineName == "rotate") + switch (timelineName) { + case "rotate": timelines.Add(ReadTimeline(ref keyMapEnumerator, new RotateTimeline(frames, frames, boneIndex), 0, 1)); - else if (timelineName == "translate") { - TranslateTimeline timeline = new TranslateTimeline(frames, frames << 1, boneIndex); - timelines.Add(ReadTimeline(ref keyMapEnumerator, timeline, "x", "y", 0, scale)); - } else if (timelineName == "translatex") { + break; + case "translate": { + timelines.Add(ReadTimeline(ref keyMapEnumerator, new TranslateTimeline(frames, frames << 1, boneIndex), "x", "y", 0, scale)); + break; + } + case "translatex": { timelines .Add(ReadTimeline(ref keyMapEnumerator, new TranslateXTimeline(frames, frames, boneIndex), 0, scale)); - } else if (timelineName == "translatey") { + break; + } + case "translatey": { timelines .Add(ReadTimeline(ref keyMapEnumerator, new TranslateYTimeline(frames, frames, boneIndex), 0, scale)); - } else if (timelineName == "scale") { - ScaleTimeline timeline = new ScaleTimeline(frames, frames << 1, boneIndex); - timelines.Add(ReadTimeline(ref keyMapEnumerator, timeline, "x", "y", 1, 1)); - } else if (timelineName == "scalex") + break; + } + case "scale": { + timelines.Add(ReadTimeline(ref keyMapEnumerator, new ScaleTimeline(frames, frames << 1, boneIndex), "x", "y", 1, 1)); + break; + } + case "scalex": timelines.Add(ReadTimeline(ref keyMapEnumerator, new ScaleXTimeline(frames, frames, boneIndex), 1, 1)); - else if (timelineName == "scaley") + break; + case "scaley": timelines.Add(ReadTimeline(ref keyMapEnumerator, new ScaleYTimeline(frames, frames, boneIndex), 1, 1)); - else if (timelineName == "shear") { - ShearTimeline timeline = new ShearTimeline(frames, frames << 1, boneIndex); - timelines.Add(ReadTimeline(ref keyMapEnumerator, timeline, "x", "y", 0, 1)); - } else if (timelineName == "shearx") + break; + case "shear": { + timelines.Add(ReadTimeline(ref keyMapEnumerator, new ShearTimeline(frames, frames << 1, boneIndex), "x", "y", 0, 1)); + break; + } + case "shearx": timelines.Add(ReadTimeline(ref keyMapEnumerator, new ShearXTimeline(frames, frames, boneIndex), 0, 1)); - else if (timelineName == "sheary") + break; + case "sheary": timelines.Add(ReadTimeline(ref keyMapEnumerator, new ShearYTimeline(frames, frames, boneIndex), 0, 1)); - else if (timelineName == "inherit") { - InheritTimeline timeline = new InheritTimeline(frames, boneIndex); + break; + case "inherit": { + var timeline = new InheritTimeline(frames, boneIndex); for (int frame = 0; ; frame++) { Dictionary keyMap = (Dictionary)keyMapEnumerator.Current; float time = GetFloat(keyMap, "time", 0); @@ -879,8 +978,11 @@ namespace Spine { } } timelines.Add(timeline); - } else + break; + } + default: throw new Exception("Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"); + } } } } @@ -893,7 +995,7 @@ namespace Spine { if (!keyMapEnumerator.MoveNext()) continue; Dictionary keyMap = (Dictionary)keyMapEnumerator.Current; IkConstraintData constraint = skeletonData.FindIkConstraint(timelineMap.Key); - IkConstraintTimeline timeline = new IkConstraintTimeline(values.Count, values.Count << 1, + var timeline = new IkConstraintTimeline(values.Count, values.Count << 1, skeletonData.IkConstraints.IndexOf(constraint)); float time = GetFloat(keyMap, "time", 0); float mix = GetFloat(keyMap, "mix", 1), softness = GetFloat(keyMap, "softness", 0) * scale; @@ -929,7 +1031,7 @@ namespace Spine { if (!keyMapEnumerator.MoveNext()) continue; Dictionary keyMap = (Dictionary)keyMapEnumerator.Current; TransformConstraintData constraint = skeletonData.FindTransformConstraint(timelineMap.Key); - TransformConstraintTimeline timeline = new TransformConstraintTimeline(values.Count, values.Count * 6, + var timeline = new TransformConstraintTimeline(values.Count, values.Count * 6, skeletonData.TransformConstraints.IndexOf(constraint)); float time = GetFloat(keyMap, "time", 0); float mixRotate = GetFloat(keyMap, "mixRotate", 1), mixShearY = GetFloat(keyMap, "mixShearY", 1); @@ -982,15 +1084,20 @@ namespace Spine { int frames = values.Count; string timelineName = (string)timelineEntry.Key; - if (timelineName == "position") { + switch (timelineName) { + case "position": { CurveTimeline1 timeline = new PathConstraintPositionTimeline(frames, frames, constraintIndex); timelines.Add(ReadTimeline(ref keyMapEnumerator, timeline, 0, constraint.positionMode == PositionMode.Fixed ? scale : 1)); - } else if (timelineName == "spacing") { + break; + } + case "spacing": { CurveTimeline1 timeline = new PathConstraintSpacingTimeline(frames, frames, constraintIndex); timelines.Add(ReadTimeline(ref keyMapEnumerator, timeline, 0, constraint.spacingMode == SpacingMode.Length || constraint.spacingMode == SpacingMode.Fixed ? scale : 1)); - } else if (timelineName == "mix") { - PathConstraintMixTimeline timeline = new PathConstraintMixTimeline(frames, frames * 3, constraintIndex); + break; + } + case "mix": { + var timeline = new PathConstraintMixTimeline(frames, frames * 3, constraintIndex); Dictionary keyMap = (Dictionary)keyMapEnumerator.Current; float time = GetFloat(keyMap, "time", 0); float mixRotate = GetFloat(keyMap, "mixRotate", 1); @@ -1018,6 +1125,8 @@ namespace Spine { keyMap = nextMap; } timelines.Add(timeline); + break; + } } } } @@ -1040,33 +1149,41 @@ namespace Spine { int frames = values.Count; string timelineName = (string)timelineEntry.Key; - if (timelineName == "reset") { - PhysicsConstraintResetTimeline timeline1 = new PhysicsConstraintResetTimeline(frames, index); + CurveTimeline1 timeline; + switch (timelineName) { + case "reset": { + var resetTimeline = new PhysicsConstraintResetTimeline(frames, index); int frame = 0; foreach (Dictionary keyMap in values) { - timeline1.SetFrame(frame++, GetFloat(keyMap, "time", 0)); + resetTimeline.SetFrame(frame++, GetFloat(keyMap, "time", 0)); } - timelines.Add(timeline1); + timelines.Add(resetTimeline); continue; } - - CurveTimeline1 timeline; - if (timelineName == "inertia") + case "inertia": timeline = new PhysicsConstraintInertiaTimeline(frames, frames, index); - else if (timelineName == "strength") + break; + case "strength": timeline = new PhysicsConstraintStrengthTimeline(frames, frames, index); - else if (timelineName == "damping") + break; + case "damping": timeline = new PhysicsConstraintDampingTimeline(frames, frames, index); - else if (timelineName == "mass") + break; + case "mass": timeline = new PhysicsConstraintMassTimeline(frames, frames, index); - else if (timelineName == "wind") + break; + case "wind": timeline = new PhysicsConstraintWindTimeline(frames, frames, index); - else if (timelineName == "gravity") + break; + case "gravity": timeline = new PhysicsConstraintGravityTimeline(frames, frames, index); - else if (timelineName == "mix") // + break; + case "mix": timeline = new PhysicsConstraintMixTimeline(frames, frames, index); - else + break; + default: continue; + } timelines.Add(ReadTimeline(ref keyMapEnumerator, timeline, 0, 1)); } } @@ -1089,13 +1206,14 @@ namespace Spine { Dictionary keyMap = (Dictionary)keyMapEnumerator.Current; int frames = values.Count; string timelineName = timelineMap.Key; - if (timelineName == "deform") { + switch (timelineName) { + case "deform": { VertexAttachment vertexAttachment = (VertexAttachment)attachment; bool weighted = vertexAttachment.bones != null; float[] vertices = vertexAttachment.vertices; int deformLength = weighted ? (vertices.Length / 3) << 1 : vertices.Length; - DeformTimeline timeline = new DeformTimeline(frames, frames, slot.Index, vertexAttachment); + var timeline = new DeformTimeline(frames, frames, slot.Index, vertexAttachment); float time = GetFloat(keyMap, "time", 0); for (int frame = 0, bezier = 0; ; frame++) { float[] deform; @@ -1132,8 +1250,10 @@ namespace Spine { keyMap = nextMap; } timelines.Add(timeline); - } else if (timelineName == "sequence") { - SequenceTimeline timeline = new SequenceTimeline(frames, slot.index, attachment); + break; + } + case "sequence": { + var timeline = new SequenceTimeline(frames, slot.index, attachment); float lastDelay = 0; for (int frame = 0; keyMap != null; keyMap = keyMapEnumerator.MoveNext() ? (Dictionary)keyMapEnumerator.Current : null, frame++) { @@ -1146,6 +1266,8 @@ namespace Spine { lastDelay = delay; } timelines.Add(timeline); + break; + } } } } @@ -1156,7 +1278,7 @@ namespace Spine { // Draw order timeline. if (map.ContainsKey("drawOrder")) { List values = (List)map["drawOrder"]; - DrawOrderTimeline timeline = new DrawOrderTimeline(values.Count); + var timeline = new DrawOrderTimeline(values.Count); int slotCount = skeletonData.slots.Count; int frame = 0; foreach (Dictionary keyMap in values) { @@ -1193,12 +1315,12 @@ namespace Spine { // Event timeline. if (map.ContainsKey("events")) { List eventsMap = (List)map["events"]; - EventTimeline timeline = new EventTimeline(eventsMap.Count); + var timeline = new EventTimeline(eventsMap.Count); int frame = 0; foreach (Dictionary keyMap in eventsMap) { EventData eventData = skeletonData.FindEvent((string)keyMap["name"]); if (eventData == null) throw new Exception("Event not found: " + keyMap["name"]); - Event e = new Event(GetFloat(keyMap, "time", 0), eventData) { + var e = new Event(GetFloat(keyMap, "time", 0), eventData) { intValue = GetInt(keyMap, "int", eventData.Int), floatValue = GetFloat(keyMap, "float", eventData.Float), stringValue = GetString(keyMap, "string", eventData.String) @@ -1319,6 +1441,11 @@ namespace Spine { return (float)map[name]; } + static float GetFloat (Dictionary map, string name) { + if (!map.ContainsKey(name)) throw new ArgumentException("Named value not found: " + name); + return (float)map[name]; + } + static int GetInt (Dictionary map, string name, int defaultValue) { if (!map.ContainsKey(name)) return defaultValue; return (int)(float)map[name]; diff --git a/spine-csharp/src/TransformConstraint.cs b/spine-csharp/src/TransformConstraint.cs index 5bf47be5c..2a4e14d49 100644 --- a/spine-csharp/src/TransformConstraint.cs +++ b/spine-csharp/src/TransformConstraint.cs @@ -30,19 +30,21 @@ using System; namespace Spine { + using FromProperty = TransformConstraintData.FromProperty; using Physics = Skeleton.Physics; + using ToProperty = TransformConstraintData.ToProperty; /// /// /// Stores the current pose for a transform constraint. A transform constraint adjusts the world transform of the constrained - /// bones to match that of the target bone. + /// bones to match that of the source bone. /// /// See Transform constraints in the Spine User Guide. /// public class TransformConstraint : IUpdatable { internal readonly TransformConstraintData data; internal readonly ExposedList bones; - internal Bone target; + internal Bone source; internal float mixRotate, mixX, mixY, mixScaleX, mixScaleY, mixShearY; internal bool active; @@ -56,7 +58,7 @@ namespace Spine { foreach (BoneData boneData in data.bones) bones.Add(skeleton.bones.Items[boneData.index]); - target = skeleton.bones.Items[data.target.index]; + source = skeleton.bones.Items[data.source.index]; mixRotate = data.mixRotate; mixX = data.mixX; @@ -90,205 +92,44 @@ namespace Spine { public void Update (Physics physics) { if (mixRotate == 0 && mixX == 0 && mixY == 0 && mixScaleX == 0 && mixScaleY == 0 && mixShearY == 0) return; - if (data.local) { - if (data.relative) - ApplyRelativeLocal(); + + TransformConstraintData data = this.data; + bool localFrom = data.localSource, localTarget = data.localTarget, additive = data.additive, clamp = data.clamp; + Bone source = this.source; + FromProperty[] fromItems = data.properties.Items; + int fn = data.properties.Count; + Bone[] bones = this.bones.Items; + for (int i = 0, n = this.bones.Count; i < n; i++) { + var bone = bones[i]; + for (int f = 0; f < fn; f++) { + FromProperty from = fromItems[f]; + float value = from.Value(data, source, localFrom) - from.offset; + ToProperty[] toItems = from.to.Items; + for (int t = 0, tn = from.to.Count; t < tn; t++) { + var to = (ToProperty)toItems[t]; + if (to.Mix(this) != 0) { + float clamped = to.offset + value * to.scale; + if (clamp) { + if (to.offset < to.max) + clamped = MathUtils.Clamp(clamped, to.offset, to.max); + else + clamped = MathUtils.Clamp(clamped, to.max, to.offset); + } + to.Apply(this, bone, clamped, localTarget, additive); + } + } + } + if (localTarget) + bone.Update(Skeleton.Physics.None); // note: reference implementation passes null, ignored parameter else - ApplyAbsoluteLocal(); - } else { - if (data.relative) - ApplyRelativeWorld(); - else - ApplyAbsoluteWorld(); - } - } - - void ApplyAbsoluteWorld () { - float mixRotate = this.mixRotate, mixX = this.mixX, mixY = this.mixY, mixScaleX = this.mixScaleX, - mixScaleY = this.mixScaleY, mixShearY = this.mixShearY; - bool translate = mixX != 0 || mixY != 0; - - Bone target = this.target; - float ta = target.a, tb = target.b, tc = target.c, td = target.d; - float degRadReflect = ta * td - tb * tc > 0 ? MathUtils.DegRad : -MathUtils.DegRad; - float offsetRotation = data.offsetRotation * degRadReflect, offsetShearY = data.offsetShearY * degRadReflect; - - Bone[] bones = this.bones.Items; - for (int i = 0, n = this.bones.Count; i < n; i++) { - Bone bone = bones[i]; - - if (mixRotate != 0) { - float a = bone.a, b = bone.b, c = bone.c, d = bone.d; - float r = MathUtils.Atan2(tc, ta) - MathUtils.Atan2(c, a) + offsetRotation; - if (r > MathUtils.PI) - r -= MathUtils.PI2; - else if (r < -MathUtils.PI) // - r += MathUtils.PI2; - r *= mixRotate; - float cos = MathUtils.Cos(r), sin = MathUtils.Sin(r); - bone.a = cos * a - sin * c; - bone.b = cos * b - sin * d; - bone.c = sin * a + cos * c; - bone.d = sin * b + cos * d; - } - - if (translate) { - float tx, ty; //Vector2 temp = this.temp; - target.LocalToWorld(data.offsetX, data.offsetY, out tx, out ty); //target.localToWorld(temp.set(data.offsetX, data.offsetY)); - bone.worldX += (tx - bone.worldX) * mixX; - bone.worldY += (ty - bone.worldY) * mixY; - } - - if (mixScaleX != 0) { - float s = (float)Math.Sqrt(bone.a * bone.a + bone.c * bone.c); - if (s != 0) s = (s + ((float)Math.Sqrt(ta * ta + tc * tc) - s + data.offsetScaleX) * mixScaleX) / s; - bone.a *= s; - bone.c *= s; - } - if (mixScaleY != 0) { - float s = (float)Math.Sqrt(bone.b * bone.b + bone.d * bone.d); - if (s != 0) s = (s + ((float)Math.Sqrt(tb * tb + td * td) - s + data.offsetScaleY) * mixScaleY) / s; - bone.b *= s; - bone.d *= s; - } - - if (mixShearY > 0) { - float b = bone.b, d = bone.d; - float by = MathUtils.Atan2(d, b); - float r = MathUtils.Atan2(td, tb) - MathUtils.Atan2(tc, ta) - (by - MathUtils.Atan2(bone.c, bone.a)); - if (r > MathUtils.PI) - r -= MathUtils.PI2; - else if (r < -MathUtils.PI) // - r += MathUtils.PI2; - r = by + (r + offsetShearY) * mixShearY; - float s = (float)Math.Sqrt(b * b + d * d); - bone.b = MathUtils.Cos(r) * s; - bone.d = MathUtils.Sin(r) * s; - } - - bone.UpdateAppliedTransform(); - } - } - - void ApplyRelativeWorld () { - float mixRotate = this.mixRotate, mixX = this.mixX, mixY = this.mixY, mixScaleX = this.mixScaleX, - mixScaleY = this.mixScaleY, mixShearY = this.mixShearY; - bool translate = mixX != 0 || mixY != 0; - - Bone target = this.target; - float ta = target.a, tb = target.b, tc = target.c, td = target.d; - float degRadReflect = ta * td - tb * tc > 0 ? MathUtils.DegRad : -MathUtils.DegRad; - float offsetRotation = data.offsetRotation * degRadReflect, offsetShearY = data.offsetShearY * degRadReflect; - - Bone[] bones = this.bones.Items; - for (int i = 0, n = this.bones.Count; i < n; i++) { - Bone bone = bones[i]; - - if (mixRotate != 0) { - float a = bone.a, b = bone.b, c = bone.c, d = bone.d; - float r = MathUtils.Atan2(tc, ta) + offsetRotation; - if (r > MathUtils.PI) - r -= MathUtils.PI2; - else if (r < -MathUtils.PI) // - r += MathUtils.PI2; - r *= mixRotate; - float cos = MathUtils.Cos(r), sin = MathUtils.Sin(r); - bone.a = cos * a - sin * c; - bone.b = cos * b - sin * d; - bone.c = sin * a + cos * c; - bone.d = sin * b + cos * d; - } - - if (translate) { - float tx, ty; //Vector2 temp = this.temp; - target.LocalToWorld(data.offsetX, data.offsetY, out tx, out ty); //target.localToWorld(temp.set(data.offsetX, data.offsetY)); - bone.worldX += tx * mixX; - bone.worldY += ty * mixY; - } - - if (mixScaleX != 0) { - float s = ((float)Math.Sqrt(ta * ta + tc * tc) - 1 + data.offsetScaleX) * mixScaleX + 1; - bone.a *= s; - bone.c *= s; - } - if (mixScaleY != 0) { - float s = ((float)Math.Sqrt(tb * tb + td * td) - 1 + data.offsetScaleY) * mixScaleY + 1; - bone.b *= s; - bone.d *= s; - } - - if (mixShearY > 0) { - float r = MathUtils.Atan2(td, tb) - MathUtils.Atan2(tc, ta); - if (r > MathUtils.PI) - r -= MathUtils.PI2; - else if (r < -MathUtils.PI) // - r += MathUtils.PI2; - float b = bone.b, d = bone.d; - r = MathUtils.Atan2(d, b) + (r - MathUtils.PI / 2 + offsetShearY) * mixShearY; - float s = (float)Math.Sqrt(b * b + d * d); - bone.b = MathUtils.Cos(r) * s; - bone.d = MathUtils.Sin(r) * s; - } - - bone.UpdateAppliedTransform(); - } - } - - void ApplyAbsoluteLocal () { - float mixRotate = this.mixRotate, mixX = this.mixX, mixY = this.mixY, mixScaleX = this.mixScaleX, - mixScaleY = this.mixScaleY, mixShearY = this.mixShearY; - - Bone target = this.target; - - Bone[] bones = this.bones.Items; - for (int i = 0, n = this.bones.Count; i < n; i++) { - Bone bone = bones[i]; - - float rotation = bone.arotation; - if (mixRotate != 0) rotation += (target.arotation - rotation + data.offsetRotation) * mixRotate; - - float x = bone.ax, y = bone.ay; - x += (target.ax - x + data.offsetX) * mixX; - y += (target.ay - y + data.offsetY) * mixY; - - float scaleX = bone.ascaleX, scaleY = bone.ascaleY; - if (mixScaleX != 0 && scaleX != 0) - scaleX = (scaleX + (target.ascaleX - scaleX + data.offsetScaleX) * mixScaleX) / scaleX; - if (mixScaleY != 0 && scaleY != 0) - scaleY = (scaleY + (target.ascaleY - scaleY + data.offsetScaleY) * mixScaleY) / scaleY; - - float shearY = bone.ashearY; - if (mixShearY != 0) shearY += (target.ashearY - shearY + data.offsetShearY) * mixShearY; - - bone.UpdateWorldTransform(x, y, rotation, scaleX, scaleY, bone.ashearX, shearY); - } - } - - void ApplyRelativeLocal () { - float mixRotate = this.mixRotate, mixX = this.mixX, mixY = this.mixY, mixScaleX = this.mixScaleX, - mixScaleY = this.mixScaleY, mixShearY = this.mixShearY; - - Bone target = this.target; - - Bone[] bones = this.bones.Items; - for (int i = 0, n = this.bones.Count; i < n; i++) { - Bone bone = bones[i]; - - float rotation = bone.arotation + (target.arotation + data.offsetRotation) * mixRotate; - float x = bone.ax + (target.ax + data.offsetX) * mixX; - float y = bone.ay + (target.ay + data.offsetY) * mixY; - float scaleX = bone.ascaleX * (((target.ascaleX - 1 + data.offsetScaleX) * mixScaleX) + 1); - float scaleY = bone.ascaleY * (((target.ascaleY - 1 + data.offsetScaleY) * mixScaleY) + 1); - float shearY = bone.ashearY + (target.ashearY + data.offsetShearY) * mixShearY; - - bone.UpdateWorldTransform(x, y, rotation, scaleX, scaleY, bone.ashearX, shearY); + bone.UpdateAppliedTransform(); } } /// 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. - public Bone Target { get { return target; } set { target = value; } } + /// The bone whose world transform will be copied to the constrained bones. + public Bone Source { get { return source; } set { source = value; } } /// A percentage (0-1) that controls the mix between the constrained and unconstrained rotation. public float MixRotate { get { return mixRotate; } set { mixRotate = value; } } /// A percentage (0-1) that controls the mix between the constrained and unconstrained translation X. diff --git a/spine-csharp/src/TransformConstraintData.cs b/spine-csharp/src/TransformConstraintData.cs index b15ce4b5e..342c35c7d 100644 --- a/spine-csharp/src/TransformConstraintData.cs +++ b/spine-csharp/src/TransformConstraintData.cs @@ -31,14 +31,31 @@ using System; namespace Spine { public class TransformConstraintData : ConstraintData { - internal ExposedList bones = new ExposedList(); - internal BoneData target; - internal float mixRotate, mixX, mixY, mixScaleX, mixScaleY, mixShearY; - internal float offsetRotation, offsetX, offsetY, offsetScaleX, offsetScaleY, offsetShearY; - internal bool relative, local; + internal readonly ExposedList bones = new ExposedList(); + internal BoneData source; + internal float offsetX, offsetY, mixRotate, mixX, mixY, mixScaleX, mixScaleY, mixShearY; + internal bool localSource, localTarget, additive, clamp; + internal readonly ExposedList properties = new ExposedList(); + + public TransformConstraintData (string name) : base(name) { + } public ExposedList Bones { get { return bones; } } - public BoneData Target { get { return target; } set { target = value; } } + + /// The bone whose world transform will be copied to the constrained bones. + public BoneData Source { + get { return source; } + set { + if (source == null) throw new ArgumentNullException("Source", "source cannot be null."); + source = value; + } + } + + /// The mapping of transform properties to other transform properties. + public ExposedList Properties { + get { return properties; } + } + /// A percentage (0-1) that controls the mix between the constrained and unconstrained rotation. public float MixRotate { get { return mixRotate; } set { mixRotate = value; } } /// A percentage (0-1) that controls the mix between the constrained and unconstrained translation X. @@ -51,18 +68,222 @@ namespace Spine { public float MixScaleY { get { return mixScaleY; } set { mixScaleY = value; } } /// A percentage (0-1) that controls the mix between the constrained and unconstrained shear Y. public float MixShearY { get { return mixShearY; } set { mixShearY = value; } } - - public float OffsetRotation { get { return offsetRotation; } set { offsetRotation = value; } } + /// An offset added to the constrained bone X translation. public float OffsetX { get { return offsetX; } set { offsetX = value; } } + /// An offset added to the constrained bone Y translation. public float OffsetY { get { return offsetY; } set { offsetY = value; } } - public float OffsetScaleX { get { return offsetScaleX; } set { offsetScaleX = value; } } - public float OffsetScaleY { get { return offsetScaleY; } set { offsetScaleY = value; } } - public float OffsetShearY { get { return offsetShearY; } set { offsetShearY = value; } } + /// Reads the source bone's local transform instead of its world transform. + public bool LocalSource { get { return localSource; } set { localSource = value; } } + /// Sets the constrained bones' local transforms instead of their world transforms. + public bool LocalTarget { get { return localTarget; } set { localTarget = value; } } + /// Adds the source bone transform to the constrained bones instead of setting it absolutely. + public bool Additive { get { return additive; } set { additive = value; } } + /// Prevents constrained bones from exceeding the ranged defined by and + /// . + public bool Clamp { get { return clamp; } set { clamp = value; } } - public bool Relative { get { return relative; } set { relative = value; } } - public bool Local { get { return local; } set { local = value; } } + /// Source property for a . + abstract public class FromProperty { + /// The value of this property that corresponds to . + public float offset; - public TransformConstraintData (string name) : base(name) { + /// Constrained properties. + public readonly ExposedList to = new ExposedList(); + + /// Reads this property from the specified bone. + abstract public float Value (TransformConstraintData data, Bone source, bool local); + } + + ///Constrained property for a . + abstract public class ToProperty { + /// The value of this property that corresponds to . + public float offset; + + /// The maximum value of this property when clamped. + public float max; + + /// The scale of the value in relation to this property. + public float scale; + + /// Reads the mix for this property from the specified constraint. + public abstract float Mix (TransformConstraint constraint); + + /// Applies the value to this property. + public abstract void Apply (TransformConstraint constraint, Bone bone, float value, bool local, bool additive); + } + + public class FromRotate : FromProperty { + public override float Value (TransformConstraintData data, Bone source, bool local) { + return local ? source.arotation : MathUtils.Atan2(source.c, source.a) * MathUtils.RadDeg; + } + } + + public class ToRotate : ToProperty { + public override float Mix (TransformConstraint constraint) { + return constraint.mixRotate; + } + + public override void Apply (TransformConstraint constraint, Bone bone, float value, bool local, bool additive) { + if (local) { + if (!additive) value -= bone.arotation; + bone.arotation += value * constraint.mixRotate; + } else { + float a = bone.a, b = bone.b, c = bone.c, d = bone.d; + value *= MathUtils.DegRad; + if (!additive) value -= MathUtils.Atan2(c, a); + if (value > MathUtils.PI) + value -= MathUtils.PI2; + else if (value < -MathUtils.PI) // + value += MathUtils.PI2; + value *= constraint.mixRotate; + float cos = MathUtils.Cos(value), sin = MathUtils.Sin(value); + bone.a = cos * a - sin * c; + bone.b = cos * b - sin * d; + bone.c = sin * a + cos * c; + bone.d = sin * b + cos * d; + } + } + } + + public class FromX : FromProperty { + public override float Value (TransformConstraintData data, Bone source, bool local) { + return local ? source.ax + data.offsetX : data.offsetX * source.a + data.offsetY * source.b + source.worldX; + } + } + + public class ToX : ToProperty { + public override float Mix (TransformConstraint constraint) { + return constraint.mixX; + } + + public override void Apply (TransformConstraint constraint, Bone bone, float value, bool local, bool additive) { + if (local) { + if (!additive) value -= bone.ax; + bone.ax += value * constraint.mixX; + } else { + if (!additive) value -= bone.worldX; + bone.worldX += value * constraint.mixX; + } + } + } + + public class FromY : FromProperty { + public override float Value (TransformConstraintData data, Bone source, bool local) { + return local ? source.ay + data.offsetY : data.offsetX * source.c + data.offsetY * source.d + source.worldY; + } + } + + public class ToY : ToProperty { + public override float Mix (TransformConstraint constraint) { + return constraint.mixY; + } + + public override void Apply (TransformConstraint constraint, Bone bone, float value, bool local, bool additive) { + if (local) { + if (!additive) value -= bone.ay; + bone.ay += value * constraint.mixY; + } else { + if (!additive) value -= bone.worldY; + bone.worldY += value * constraint.mixY; + } + } + } + + public class FromScaleX : FromProperty { + public override float Value (TransformConstraintData data, Bone source, bool local) { + return local ? source.ascaleX : (float)Math.Sqrt(source.a * source.a + source.c * source.c); + } + } + + public class ToScaleX : ToProperty { + public override float Mix (TransformConstraint constraint) { + return constraint.mixScaleX; + } + + public override void Apply (TransformConstraint constraint, Bone bone, float value, bool local, bool additive) { + if (local) { + if (additive) + bone.ascaleX *= 1 + ((value - 1) * constraint.mixScaleX); + else if (bone.ascaleX != 0) // + bone.ascaleX = 1 + (value / bone.ascaleX - 1) * constraint.mixScaleX; + } else { + float s; + if (additive) + s = 1 + (value - 1) * constraint.mixScaleX; + else { + s = (float)Math.Sqrt(bone.a * bone.a + bone.c * bone.c); + if (s != 0) s = 1 + (value / s - 1) * constraint.mixScaleX; + } + bone.a *= s; + bone.c *= s; + } + } + } + + public class FromScaleY : FromProperty { + public override float Value (TransformConstraintData data, Bone source, bool local) { + return local ? source.ascaleY : (float)Math.Sqrt(source.b * source.b + source.d * source.d); + } + } + + public class ToScaleY : ToProperty { + public override float Mix (TransformConstraint constraint) { + return constraint.mixScaleY; + } + + public override void Apply (TransformConstraint constraint, Bone bone, float value, bool local, bool additive) { + if (local) { + if (additive) + bone.ascaleY *= 1 + ((value - 1) * constraint.mixScaleY); + else if (bone.ascaleY != 0) // + bone.ascaleY = 1 + (value / bone.ascaleY - 1) * constraint.mixScaleY; + } else { + float s; + if (additive) + s = 1 + (value - 1) * constraint.mixScaleY; + else { + s = (float)Math.Sqrt(bone.b * bone.b + bone.d * bone.d); + if (s != 0) s = 1 + (value / s - 1) * constraint.mixScaleY; + } + bone.b *= s; + bone.d *= s; + } + } + } + + public class FromShearY : FromProperty { + public override float Value (TransformConstraintData data, Bone source, bool local) { + return local ? source.ashearY : (MathUtils.Atan2(source.d, source.b) - MathUtils.Atan2(source.c, source.a)) * MathUtils.RadDeg - 90; + } + } + + public class ToShearY : ToProperty { + public override float Mix (TransformConstraint constraint) { + return constraint.mixShearY; + } + + public override void Apply (TransformConstraint constraint, Bone bone, float value, bool local, bool additive) { + if (local) { + if (!additive) value -= bone.ashearY; + bone.ashearY += value * constraint.mixShearY; + } else { + float b = bone.b, d = bone.d, by = MathUtils.Atan2(d, b); + value = (value + 90) * MathUtils.DegRad; + if (additive) + value -= MathUtils.PI / 2; + else { + value -= by - MathUtils.Atan2(bone.c, bone.a); + if (value > MathUtils.PI) + value -= MathUtils.PI2; + else if (value < -MathUtils.PI) // + value += MathUtils.PI2; + } + value = by + value * constraint.mixShearY; + float s = (float)Math.Sqrt(b * b + d * d); + bone.b = MathUtils.Cos(value) * s; + bone.d = MathUtils.Sin(value) * s; + } + } } } } diff --git a/spine-unity/Assets/Spine/Editor/spine-unity/Editor/Utility/SpineHandles.cs b/spine-unity/Assets/Spine/Editor/spine-unity/Editor/Utility/SpineHandles.cs index 6a5b0b761..6b7064fc8 100644 --- a/spine-unity/Assets/Spine/Editor/spine-unity/Editor/Utility/SpineHandles.cs +++ b/spine-unity/Assets/Spine/Editor/spine-unity/Editor/Utility/SpineHandles.cs @@ -397,8 +397,8 @@ namespace Spine.Unity.Editor { // Transform Constraints handleColor = SpineHandles.TransformContraintColor; foreach (TransformConstraint tc in skeleton.TransformConstraints) { - Bone targetBone = tc.Target; - targetPos = targetBone.GetWorldPosition(transform, skeletonRenderScale, offset); + Bone sourceBone = tc.Source; + targetPos = sourceBone.GetWorldPosition(transform, skeletonRenderScale, offset); if (tc.MixX > 0 || tc.MixY > 0) { if ((tc.MixX > 0 && tc.MixX != 1f) || @@ -411,7 +411,7 @@ namespace Spine.Unity.Editor { } SpineHandles.DrawBoneCircle(targetPos, handleColor, normal, 1.3f * skeletonRenderScale); Handles.color = handleColor; - SpineHandles.DrawCrosshairs(targetPos, 0.2f, targetBone.A, targetBone.B, targetBone.C, targetBone.D, transform, skeletonRenderScale); + SpineHandles.DrawCrosshairs(targetPos, 0.2f, sourceBone.A, sourceBone.B, sourceBone.C, sourceBone.D, transform, skeletonRenderScale); } } diff --git a/spine-unity/Assets/Spine/Editor/spine-unity/Editor/Windows/SkeletonDebugWindow.cs b/spine-unity/Assets/Spine/Editor/spine-unity/Editor/Windows/SkeletonDebugWindow.cs index 2af4e2a3c..5547bc5dc 100644 --- a/spine-unity/Assets/Spine/Editor/spine-unity/Editor/Windows/SkeletonDebugWindow.cs +++ b/spine-unity/Assets/Spine/Editor/spine-unity/Editor/Windows/SkeletonDebugWindow.cs @@ -415,7 +415,7 @@ namespace Spine.Unity.Editor { foreach (TransformConstraint c in skeleton.TransformConstraints) { EditorGUILayout.LabelField(SpineInspectorUtility.TempContent(c.Data.Name, Icons.constraintTransform)); EditorGUI.BeginDisabledGroup(true); - FalseDropDown("Goal", c.Data.Target.Name, Icons.bone); + FalseDropDown("Source", c.Data.Source.Name, Icons.bone); EditorGUI.EndDisabledGroup(); EditorGUI.BeginChangeCheck(); @@ -469,7 +469,7 @@ namespace Spine.Unity.Editor { foreach (PathConstraint c in skeleton.PathConstraints) { EditorGUILayout.LabelField(SpineInspectorUtility.TempContent(c.Data.Name, Icons.constraintPath)); EditorGUI.BeginDisabledGroup(true); - FalseDropDown("Path Slot", c.Data.Target.Name, Icons.slot); + FalseDropDown("Path Slot", c.Data.Slot.Name, Icons.slot); Attachment activeAttachment = c.Target.Attachment; FalseDropDown("Active Path", activeAttachment != null ? activeAttachment.Name : "", activeAttachment is PathAttachment ? Icons.path : null); EditorGUILayout.LabelField("PositionMode." + c.Data.PositionMode); diff --git a/spine-unity/Assets/Spine/Runtime/spine-unity/Components/RootMotion/SkeletonRootMotionBase.cs b/spine-unity/Assets/Spine/Runtime/spine-unity/Components/RootMotion/SkeletonRootMotionBase.cs index 81fc288b8..e34d4104c 100644 --- a/spine-unity/Assets/Spine/Runtime/spine-unity/Components/RootMotion/SkeletonRootMotionBase.cs +++ b/spine-unity/Assets/Spine/Runtime/spine-unity/Components/RootMotion/SkeletonRootMotionBase.cs @@ -446,8 +446,8 @@ namespace Spine.Unity { if (useLastConstraintPos) constraintPos = transformConstraintLastPos[GetConstraintLastPosIndex(constraintIndex)]; else { - Bone targetBone = constraint.Target; - constraintPos = new Vector2(targetBone.X, targetBone.Y); + Bone sourceBone = constraint.Source; + constraintPos = new Vector2(sourceBone.X, sourceBone.Y); } pos = new Vector2( pos.x * invMixXY.x + constraintPos.x * mixXY.x, @@ -465,8 +465,8 @@ namespace Spine.Unity { if (useLastConstraintRotation) constraintRotation = transformConstraintLastRotation[GetConstraintLastPosIndex(constraintIndex)]; else { - Bone targetBone = constraint.Target; - constraintRotation = targetBone.Rotation; + Bone sourceBone = constraint.Source; + constraintRotation = sourceBone.Rotation; } rotation = rotation * invMixRotate + constraintRotation * mixRotate; } @@ -474,16 +474,16 @@ namespace Spine.Unity { void UpdateLastConstraintPos (TransformConstraint[] transformConstraintsItems) { foreach (int constraintIndex in this.transformConstraintIndices) { TransformConstraint constraint = transformConstraintsItems[constraintIndex]; - Bone targetBone = constraint.Target; - transformConstraintLastPos[GetConstraintLastPosIndex(constraintIndex)] = new Vector2(targetBone.X, targetBone.Y); + Bone sourceBone = constraint.Source; + transformConstraintLastPos[GetConstraintLastPosIndex(constraintIndex)] = new Vector2(sourceBone.X, sourceBone.Y); } } void UpdateLastConstraintRotation (TransformConstraint[] transformConstraintsItems) { foreach (int constraintIndex in this.transformConstraintIndices) { TransformConstraint constraint = transformConstraintsItems[constraintIndex]; - Bone targetBone = constraint.Target; - transformConstraintLastRotation[GetConstraintLastPosIndex(constraintIndex)] = targetBone.Rotation; + Bone sourceBone = constraint.Source; + transformConstraintLastRotation[GetConstraintLastPosIndex(constraintIndex)] = sourceBone.Rotation; } } @@ -524,10 +524,10 @@ namespace Spine.Unity { TransformConstraint constraint = constraintsItems[i]; if (constraint.Bones.Contains(rootMotionBone)) { transformConstraintIndices.Add(i); - Bone targetBone = constraint.Target; - Vector2 constraintPos = new Vector2(targetBone.X, targetBone.Y); + Bone sourceBone = constraint.Source; + Vector2 constraintPos = new Vector2(sourceBone.X, sourceBone.Y); transformConstraintLastPos.Add(constraintPos); - transformConstraintLastRotation.Add(targetBone.Rotation); + transformConstraintLastRotation.Add(sourceBone.Rotation); } } } diff --git a/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonUtility/SkeletonUtility.cs b/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonUtility/SkeletonUtility.cs index b37f56ad1..95a842c4b 100644 --- a/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonUtility/SkeletonUtility.cs +++ b/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonUtility/SkeletonUtility.cs @@ -360,7 +360,7 @@ namespace Spine.Unity { ExposedList transformConstraints = skeleton.TransformConstraints; for (int i = 0, n = transformConstraints.Count; i < n; i++) - constraintTargets.Add(transformConstraints.Items[i].Target); + constraintTargets.Add(transformConstraints.Items[i].Source); List boneComponents = this.boneComponents; for (int i = 0, n = boneComponents.Count; i < n; i++) {