[csharp][unity] Port of Transform constraint property mapping and cumulated 4.3 changes. Excluding import scale fixes.

This commit is contained in:
Harald Csaszar 2025-04-08 17:19:01 +02:00
parent 0ab3a8251a
commit 90bfdba422
18 changed files with 664 additions and 423 deletions

View File

@ -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;
}

View File

@ -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).</para>
/// <para>
/// See <a href='http://esotericsoftware.com/spine-applying-animations/'>Applying Animations</a> in the Spine Runtimes Guide.</para>
/// See <see href='https://esotericsoftware.com/spine-applying-animations/'>Applying Animations</a> in the Spine Runtimes Guide.</para>
/// </summary>
public class AnimationState {
internal static readonly Animation EmptyAnimation = new Animation("<empty>", new ExposedList<Timeline>(), 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);
/// <summary>See <see href="http://esotericsoftware.com/spine-api-reference#AnimationStateListener-Methods">
/// <summary>See <see href="https://esotericsoftware.com/spine-api-reference#AnimationStateListener-Methods">
/// API Reference documentation pages here</see> for details. Usage in C# and spine-unity is explained
/// <see href="https://esotericsoftware.com/spine-unity-main-components#Processing-AnimationState-Events">here</see>
/// on the spine-unity documentation pages.</summary>
@ -726,7 +726,7 @@ namespace Spine {
return AddAnimation(trackIndex, animation, loop, delay);
}
/// <summary>Adds an animation to be played after the current or last queued animation for a track. If the track is empty, it is
/// <summary>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 <see cref="SetAnimation(int, Animation, bool)"/>.</summary>
/// <param name="delay">
/// If &gt; 0, sets <see cref="TrackEntry.Delay"/>. If &lt;= 0, the delay set is the duration of the previous track entry
@ -774,8 +774,10 @@ namespace Spine {
/// <see cref="AnimationState.AddAnimation(int, Animation, bool, float)"/> with the desired delay (an empty animation has a duration of 0) and on
/// the returned track entry, set the <see cref="TrackEntry.SetMixDuration(float)"/>. 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.</para></summary>
/// from lower tracks or from the setup pose value if no lower tracks key the property to the value keyed in the new animation.</para>
/// <para>
/// See <see href='https://esotericsoftware.com/spine-applying-animations/#Empty-animations'>Empty animations</a> in the Spine
/// Runtimes Guide.</para></summary>
public TrackEntry SetEmptyAnimation (int trackIndex, float mixDuration) {
TrackEntry entry = SetAnimation(trackIndex, AnimationState.EmptyAnimation, false);
entry.mixDuration = mixDuration;
@ -785,7 +787,7 @@ namespace Spine {
/// <summary>
/// Adds an empty animation to be played after the current or last queued animation for a track, and sets the track entry's
/// <see cref="TrackEntry.MixDuration"/>. If the track is empty, it is equivalent to calling
/// <see cref="TrackEntry.MixDuration"/>. If the track has no entries, this is equivalent to calling
/// <see cref="AnimationState.SetEmptyAnimation(int, float)"/>.</summary>
/// <seealso cref="AnimationState.SetEmptyAnimation(int, float)"/>
/// <param name="trackIndex">Track number.</param>
@ -967,7 +969,7 @@ namespace Spine {
public ExposedList<TrackEntry> 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'.
/// <summary>See <see href="http://esotericsoftware.com/spine-api-reference#AnimationStateListener-Methods">
/// <summary>See <see href="https://esotericsoftware.com/spine-api-reference#AnimationStateListener-Methods">
/// API Reference documentation pages here</see> for details. Usage in C# and spine-unity is explained
/// <see href="https://esotericsoftware.com/spine-unity-main-components#Processing-AnimationState-Events">here</see>
/// on the spine-unity documentation pages.</summary>

View File

@ -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;

View File

@ -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 {

View File

@ -199,7 +199,7 @@ namespace Spine {
/// <summary>Returns a new mesh with this mesh set as the <see cref="ParentMesh"/>.
public MeshAttachment NewLinkedMesh () {
MeshAttachment mesh = new MeshAttachment(Name);
var mesh = new MeshAttachment(Name);
mesh.timelineAttachment = timelineAttachment;
mesh.region = region;

View File

@ -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--)

View File

@ -45,7 +45,7 @@ namespace Spine {
internal readonly PathConstraintData data;
internal readonly ExposedList<Bone> 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 {
/// <summary>The bones that will be modified by this path constraint.</summary>
public ExposedList<Bone> Bones { get { return bones; } }
/// <summary>The slot whose path attachment will be used to constrained the bones.</summary>
public Slot Target { get { return target; } set { target = value; } }
public Slot Target { get { return slot; } set { slot = value; } }
public bool Active { get { return active; } }
/// <summary>The path constraint's setup pose data.</summary>
public PathConstraintData Data { get { return data; } }

View File

@ -32,7 +32,7 @@ using System;
namespace Spine {
public class PathConstraintData : ConstraintData {
internal ExposedList<BoneData> bones = new ExposedList<BoneData>();
internal SlotData target;
internal SlotData slot;
internal PositionMode positionMode;
internal SpacingMode spacingMode;
internal RotateMode rotateMode;
@ -43,7 +43,7 @@ namespace Spine {
}
public ExposedList<BoneData> 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; } }

View File

@ -39,7 +39,7 @@ namespace Spine {
/// </summary>
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;

View File

@ -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<Bone> 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;

View File

@ -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 {
/// <summary>Returns the version string of binary skeleton data.</summary>
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<float> weights = new ExposedList<float>(vertices.length * 3 * 3);
ExposedList<int> bonesArray = new ExposedList<int>(vertices.length * 3);
var weights = new ExposedList<float>(vertices.length * 3 * 3);
var bonesArray = new ExposedList<int>(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 {
/// <exception cref="SerializationException">SerializationException will be thrown when a Vertex attachment is not found.</exception>
/// <exception cref="IOException">Throws IOException when a read operation fails.</exception>
private Animation ReadAnimation (String name, SkeletonInput input, SkeletonData skeletonData) {
ExposedList<Timeline> timelines = new ExposedList<Timeline>(input.ReadInt(true));
var timelines = new ExposedList<Timeline>(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);
}

View File

@ -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;
/// <summary>
/// Loads skeleton data in the Spine JSON format.
/// <para>
@ -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<string, object> root = Json.Deserialize(reader) as Dictionary<string, Object>;
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<string, Object> constraintMap in (List<Object>)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<string, Object> constraintMap in (List<Object>)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<string, Object> fromEntryObject in (Dictionary<string, Object>)constraintMap["properties"]) {
var fromEntry = (Dictionary<string, Object>)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<string, Object> toEntryObject in (Dictionary<string, Object>)fromEntry["to"]) {
var toEntry = (Dictionary<string, Object>)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<string, Object> constraintMap in (List<Object>)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<string, Object> constraintMap in (List<Object>)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<string, object> skinMap in (List<object>)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<Object>)skinMap["bones"]) {
BoneData bone = skeletonData.FindBone(entryName);
@ -393,7 +471,7 @@ namespace Spine {
if (root.ContainsKey("events")) {
foreach (KeyValuePair<string, Object> entry in (Dictionary<string, Object>)root["events"]) {
Dictionary<string, object> entryMap = (Dictionary<string, Object>)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<string, object> map = sequenceJson as Dictionary<string, Object>;
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<float> weights = new ExposedList<float>(verticesLength * 3 * 3);
ExposedList<int> bones = new ExposedList<int>(verticesLength * 3);
var weights = new ExposedList<float>(verticesLength * 3 * 3);
var bones = new ExposedList<int>(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<string, Object> map, string name, SkeletonData skeletonData) {
float scale = this.scale;
ExposedList<Timeline> timelines = new ExposedList<Timeline>();
var timelines = new ExposedList<Timeline>();
// 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<string, Object> 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<object>.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<object>.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<object>.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<object>.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<object>.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<string, object> keyMap = (Dictionary<string, Object>)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<string, object> keyMap = (Dictionary<string, Object>)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<string, object> keyMap = (Dictionary<string, Object>)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<string, object> keyMap = (Dictionary<string, Object>)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<string, Object> 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<string, object> keyMap = (Dictionary<string, Object>)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<string, Object>)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<object> values = (List<Object>)map["drawOrder"];
DrawOrderTimeline timeline = new DrawOrderTimeline(values.Count);
var timeline = new DrawOrderTimeline(values.Count);
int slotCount = skeletonData.slots.Count;
int frame = 0;
foreach (Dictionary<string, Object> keyMap in values) {
@ -1193,12 +1315,12 @@ namespace Spine {
// Event timeline.
if (map.ContainsKey("events")) {
List<object> eventsMap = (List<Object>)map["events"];
EventTimeline timeline = new EventTimeline(eventsMap.Count);
var timeline = new EventTimeline(eventsMap.Count);
int frame = 0;
foreach (Dictionary<string, Object> 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<string, Object> map, string name) {
if (!map.ContainsKey(name)) throw new ArgumentException("Named value not found: " + name);
return (float)map[name];
}
static int GetInt (Dictionary<string, Object> map, string name, int defaultValue) {
if (!map.ContainsKey(name)) return defaultValue;
return (int)(float)map[name];

View File

@ -30,19 +30,21 @@
using System;
namespace Spine {
using FromProperty = TransformConstraintData.FromProperty;
using Physics = Skeleton.Physics;
using ToProperty = TransformConstraintData.ToProperty;
/// <summary>
/// <para>
/// 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.</para>
/// bones to match that of the source bone.</para>
/// <para>
/// See <a href="http://esotericsoftware.com/spine-transform-constraints">Transform constraints</a> in the Spine User Guide.</para>
/// </summary>
public class TransformConstraint : IUpdatable {
internal readonly TransformConstraintData data;
internal readonly ExposedList<Bone> 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();
}
}
/// <summary>The bones that will be modified by this transform constraint.</summary>
public ExposedList<Bone> Bones { get { return bones; } }
/// <summary>The target bone whose world transform will be copied to the constrained bones.</summary>
public Bone Target { get { return target; } set { target = value; } }
/// <summary>The bone whose world transform will be copied to the constrained bones.</summary>
public Bone Source { get { return source; } set { source = value; } }
/// <summary>A percentage (0-1) that controls the mix between the constrained and unconstrained rotation.</summary>
public float MixRotate { get { return mixRotate; } set { mixRotate = value; } }
/// <summary>A percentage (0-1) that controls the mix between the constrained and unconstrained translation X.</summary>

View File

@ -31,14 +31,31 @@ using System;
namespace Spine {
public class TransformConstraintData : ConstraintData {
internal ExposedList<BoneData> bones = new ExposedList<BoneData>();
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<BoneData> bones = new ExposedList<BoneData>();
internal BoneData source;
internal float offsetX, offsetY, mixRotate, mixX, mixY, mixScaleX, mixScaleY, mixShearY;
internal bool localSource, localTarget, additive, clamp;
internal readonly ExposedList<FromProperty> properties = new ExposedList<FromProperty>();
public TransformConstraintData (string name) : base(name) {
}
public ExposedList<BoneData> Bones { get { return bones; } }
public BoneData Target { get { return target; } set { target = value; } }
/// <summary>The bone whose world transform will be copied to the constrained bones.</summary>
public BoneData Source {
get { return source; }
set {
if (source == null) throw new ArgumentNullException("Source", "source cannot be null.");
source = value;
}
}
/// <summary>The mapping of transform properties to other transform properties.</summary>
public ExposedList<FromProperty> Properties {
get { return properties; }
}
/// <summary>A percentage (0-1) that controls the mix between the constrained and unconstrained rotation.</summary>
public float MixRotate { get { return mixRotate; } set { mixRotate = value; } }
/// <summary>A percentage (0-1) that controls the mix between the constrained and unconstrained translation X.</summary>
@ -51,18 +68,222 @@ namespace Spine {
public float MixScaleY { get { return mixScaleY; } set { mixScaleY = value; } }
/// <summary>A percentage (0-1) that controls the mix between the constrained and unconstrained shear Y.</summary>
public float MixShearY { get { return mixShearY; } set { mixShearY = value; } }
public float OffsetRotation { get { return offsetRotation; } set { offsetRotation = value; } }
/// <summary>An offset added to the constrained bone X translation.</summary>
public float OffsetX { get { return offsetX; } set { offsetX = value; } }
/// <summary>An offset added to the constrained bone Y translation.</summary>
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; } }
/// <summary>Reads the source bone's local transform instead of its world transform.</summary>
public bool LocalSource { get { return localSource; } set { localSource = value; } }
/// <summary>Sets the constrained bones' local transforms instead of their world transforms.</summary>
public bool LocalTarget { get { return localTarget; } set { localTarget = value; } }
/// <summary>Adds the source bone transform to the constrained bones instead of setting it absolutely.</summary>
public bool Additive { get { return additive; } set { additive = value; } }
/// <summary>Prevents constrained bones from exceeding the ranged defined by <see cref="ToProperty.offset"/> and
/// <see cref="ToProperty.max"/>.</summary>
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; } }
/// <summary>Source property for a <see cref="TransformConstraint"/>.</summary>
abstract public class FromProperty {
/// <summary>The value of this property that corresponds to <see cref="ToProperty.offset"/>.</summary>
public float offset;
public TransformConstraintData (string name) : base(name) {
/// <summary>Constrained properties.</summary>
public readonly ExposedList<ToProperty> to = new ExposedList<ToProperty>();
/// <summary>Reads this property from the specified bone.</summary>
abstract public float Value (TransformConstraintData data, Bone source, bool local);
}
///<summary>Constrained property for a <see cref="TransformConstraint"/>.</summary>
abstract public class ToProperty {
/// <summary>The value of this property that corresponds to <see cref="FromProperty.offset"/>.</summary>
public float offset;
/// <summary>The maximum value of this property when <see cref="TransformConstraintData.clamp"/> clamped.</summary>
public float max;
/// <summary>The scale of the <see cref="FromProperty"/> value in relation to this property.</summary>
public float scale;
/// <summary>Reads the mix for this property from the specified constraint.</summary>
public abstract float Mix (TransformConstraint constraint);
/// <summary>Applies the value to this property.</summary>
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;
}
}
}
}
}

View File

@ -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);
}
}

View File

@ -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 : "<None>", activeAttachment is PathAttachment ? Icons.path : null);
EditorGUILayout.LabelField("PositionMode." + c.Data.PositionMode);

View File

@ -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);
}
}
}

View File

@ -360,7 +360,7 @@ namespace Spine.Unity {
ExposedList<TransformConstraint> 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<SkeletonUtilityBone> boneComponents = this.boneComponents;
for (int i = 0, n = boneComponents.Count; i < n; i++) {