mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-02-04 14:24:53 +08:00
[csharp] Porting of major 4.0-beta runtime changes. Remaining Unity assets will be updated in separate commit. See #1796.
This commit is contained in:
parent
3ab0b72033
commit
d2529d410b
File diff suppressed because it is too large
Load Diff
@ -112,7 +112,7 @@ namespace Spine {
|
|||||||
|
|
||||||
// end of difference
|
// end of difference
|
||||||
private readonly EventQueue queue; // Initialized by constructor.
|
private readonly EventQueue queue; // Initialized by constructor.
|
||||||
private readonly HashSet<int> propertyIDs = new HashSet<int>();
|
private readonly HashSet<string> propertyIds = new HashSet<string>();
|
||||||
private bool animationsChanged;
|
private bool animationsChanged;
|
||||||
private float timeScale = 1;
|
private float timeScale = 1;
|
||||||
private int unkeyedState;
|
private int unkeyedState;
|
||||||
@ -244,7 +244,13 @@ namespace Spine {
|
|||||||
mix = 0; // Set to setup pose the last time the entry will be applied.
|
mix = 0; // Set to setup pose the last time the entry will be applied.
|
||||||
|
|
||||||
// Apply current entry.
|
// Apply current entry.
|
||||||
float animationLast = current.animationLast, animationTime = current.AnimationTime;
|
float animationLast = current.animationLast, animationTime = current.AnimationTime, applyTime = animationTime;
|
||||||
|
ExposedList<Event> applyEvents = events;
|
||||||
|
if (current.reverse) {
|
||||||
|
applyTime = current.animation.duration - applyTime;
|
||||||
|
applyEvents = null;
|
||||||
|
}
|
||||||
|
|
||||||
int timelineCount = current.animation.timelines.Count;
|
int timelineCount = current.animation.timelines.Count;
|
||||||
var timelines = current.animation.timelines;
|
var timelines = current.animation.timelines;
|
||||||
var timelinesItems = timelines.Items;
|
var timelinesItems = timelines.Items;
|
||||||
@ -252,9 +258,9 @@ namespace Spine {
|
|||||||
for (int ii = 0; ii < timelineCount; ii++) {
|
for (int ii = 0; ii < timelineCount; ii++) {
|
||||||
var timeline = timelinesItems[ii];
|
var timeline = timelinesItems[ii];
|
||||||
if (timeline is AttachmentTimeline)
|
if (timeline is AttachmentTimeline)
|
||||||
ApplyAttachmentTimeline((AttachmentTimeline)timeline, skeleton, animationTime, blend, true);
|
ApplyAttachmentTimeline((AttachmentTimeline)timeline, skeleton, applyTime, blend, true);
|
||||||
else
|
else
|
||||||
timeline.Apply(skeleton, animationLast, animationTime, events, mix, blend, MixDirection.In);
|
timeline.Apply(skeleton, animationLast, applyTime, applyEvents, mix, blend, MixDirection.In);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
var timelineMode = current.timelineMode.Items;
|
var timelineMode = current.timelineMode.Items;
|
||||||
@ -268,12 +274,12 @@ namespace Spine {
|
|||||||
MixBlend timelineBlend = timelineMode[ii] == AnimationState.Subsequent ? blend : MixBlend.Setup;
|
MixBlend timelineBlend = timelineMode[ii] == AnimationState.Subsequent ? blend : MixBlend.Setup;
|
||||||
var rotateTimeline = timeline as RotateTimeline;
|
var rotateTimeline = timeline as RotateTimeline;
|
||||||
if (rotateTimeline != null)
|
if (rotateTimeline != null)
|
||||||
ApplyRotateTimeline(rotateTimeline, skeleton, animationTime, mix, timelineBlend, timelinesRotation,
|
ApplyRotateTimeline(rotateTimeline, skeleton, applyTime, mix, timelineBlend, timelinesRotation,
|
||||||
ii << 1, firstFrame);
|
ii << 1, firstFrame);
|
||||||
else if (timeline is AttachmentTimeline)
|
else if (timeline is AttachmentTimeline)
|
||||||
ApplyAttachmentTimeline((AttachmentTimeline)timeline, skeleton, animationTime, blend, true);
|
ApplyAttachmentTimeline((AttachmentTimeline)timeline, skeleton, applyTime, blend, true);
|
||||||
else
|
else
|
||||||
timeline.Apply(skeleton, animationLast, animationTime, events, mix, timelineBlend, MixDirection.In);
|
timeline.Apply(skeleton, animationLast, applyTime, applyEvents, mix, timelineBlend, MixDirection.In);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
QueueEvents(current, animationTime);
|
QueueEvents(current, animationTime);
|
||||||
@ -314,17 +320,23 @@ namespace Spine {
|
|||||||
if (blend != MixBlend.First) blend = from.mixBlend; // Track 0 ignores track mix blend.
|
if (blend != MixBlend.First) blend = from.mixBlend; // Track 0 ignores track mix blend.
|
||||||
}
|
}
|
||||||
|
|
||||||
var eventBuffer = mix < from.eventThreshold ? this.events : null;
|
|
||||||
bool attachments = mix < from.attachmentThreshold, drawOrder = mix < from.drawOrderThreshold;
|
bool attachments = mix < from.attachmentThreshold, drawOrder = mix < from.drawOrderThreshold;
|
||||||
float animationLast = from.animationLast, animationTime = from.AnimationTime;
|
|
||||||
var timelines = from.animation.timelines;
|
var timelines = from.animation.timelines;
|
||||||
int timelineCount = timelines.Count;
|
int timelineCount = timelines.Count;
|
||||||
var timelinesItems = timelines.Items;
|
var timelinesItems = timelines.Items;
|
||||||
float alphaHold = from.alpha * to.interruptAlpha, alphaMix = alphaHold * (1 - mix);
|
float alphaHold = from.alpha * to.interruptAlpha, alphaMix = alphaHold * (1 - mix);
|
||||||
|
float animationLast = from.animationLast, animationTime = from.AnimationTime, applyTime = animationTime;
|
||||||
|
ExposedList<Event> events = null;
|
||||||
|
if (from.reverse)
|
||||||
|
applyTime = from.animation.duration - applyTime;
|
||||||
|
else {
|
||||||
|
if (mix < from.eventThreshold) events = this.events;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if (blend == MixBlend.Add) {
|
if (blend == MixBlend.Add) {
|
||||||
for (int i = 0; i < timelineCount; i++)
|
for (int i = 0; i < timelineCount; i++)
|
||||||
timelinesItems[i].Apply(skeleton, animationLast, animationTime, eventBuffer, alphaMix, blend, MixDirection.Out);
|
timelinesItems[i].Apply(skeleton, animationLast, applyTime, events, alphaMix, blend, MixDirection.Out);
|
||||||
} else {
|
} else {
|
||||||
var timelineMode = from.timelineMode.Items;
|
var timelineMode = from.timelineMode.Items;
|
||||||
var timelineHoldMix = from.timelineHoldMix.Items;
|
var timelineHoldMix = from.timelineHoldMix.Items;
|
||||||
@ -366,14 +378,14 @@ namespace Spine {
|
|||||||
from.totalAlpha += alpha;
|
from.totalAlpha += alpha;
|
||||||
var rotateTimeline = timeline as RotateTimeline;
|
var rotateTimeline = timeline as RotateTimeline;
|
||||||
if (rotateTimeline != null) {
|
if (rotateTimeline != null) {
|
||||||
ApplyRotateTimeline(rotateTimeline, skeleton, animationTime, alpha, timelineBlend, timelinesRotation,
|
ApplyRotateTimeline(rotateTimeline, skeleton, applyTime, alpha, timelineBlend, timelinesRotation, i << 1,
|
||||||
i << 1, firstFrame);
|
firstFrame);
|
||||||
} else if (timeline is AttachmentTimeline) {
|
} else if (timeline is AttachmentTimeline) {
|
||||||
ApplyAttachmentTimeline((AttachmentTimeline)timeline, skeleton, animationTime, timelineBlend, attachments);
|
ApplyAttachmentTimeline((AttachmentTimeline)timeline, skeleton, applyTime, timelineBlend, attachments);
|
||||||
} else {
|
} else {
|
||||||
if (drawOrder && timeline is DrawOrderTimeline && timelineBlend == MixBlend.Setup)
|
if (drawOrder && timeline is DrawOrderTimeline && timelineBlend == MixBlend.Setup)
|
||||||
direction = MixDirection.In;
|
direction = MixDirection.In;
|
||||||
timeline.Apply(skeleton, animationLast, animationTime, eventBuffer, alpha, timelineBlend, direction);
|
timeline.Apply(skeleton, animationLast, applyTime, events, alpha, timelineBlend, direction);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -393,7 +405,7 @@ namespace Spine {
|
|||||||
private void ApplyAttachmentTimeline (AttachmentTimeline timeline, Skeleton skeleton, float time, MixBlend blend,
|
private void ApplyAttachmentTimeline (AttachmentTimeline timeline, Skeleton skeleton, float time, MixBlend blend,
|
||||||
bool attachments) {
|
bool attachments) {
|
||||||
|
|
||||||
Slot slot = skeleton.slots.Items[timeline.slotIndex];
|
Slot slot = skeleton.slots.Items[timeline.SlotIndex];
|
||||||
if (!slot.bone.active) return;
|
if (!slot.bone.active) return;
|
||||||
|
|
||||||
float[] frames = timeline.frames;
|
float[] frames = timeline.frames;
|
||||||
@ -402,12 +414,7 @@ namespace Spine {
|
|||||||
SetAttachment(skeleton, slot, slot.data.attachmentName, attachments);
|
SetAttachment(skeleton, slot, slot.data.attachmentName, attachments);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
int frameIndex;
|
SetAttachment(skeleton, slot, timeline.AttachmentNames[Animation.Search(frames, time)], attachments);
|
||||||
if (time >= frames[frames.Length - 1]) // Time is after last frame.
|
|
||||||
frameIndex = frames.Length - 1;
|
|
||||||
else
|
|
||||||
frameIndex = Animation.BinarySearch(frames, time) - 1;
|
|
||||||
SetAttachment(skeleton, slot, timeline.attachmentNames[frameIndex], attachments);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If an attachment wasn't set (ie before the first frame or attachments is false), set the setup attachment later.
|
// If an attachment wasn't set (ie before the first frame or attachments is false), set the setup attachment later.
|
||||||
@ -432,7 +439,7 @@ namespace Spine {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Bone bone = skeleton.bones.Items[timeline.boneIndex];
|
Bone bone = skeleton.bones.Items[timeline.BoneIndex];
|
||||||
if (!bone.active) return;
|
if (!bone.active) return;
|
||||||
|
|
||||||
float[] frames = timeline.frames;
|
float[] frames = timeline.frames;
|
||||||
@ -441,7 +448,7 @@ namespace Spine {
|
|||||||
switch (blend) {
|
switch (blend) {
|
||||||
case MixBlend.Setup:
|
case MixBlend.Setup:
|
||||||
bone.rotation = bone.data.rotation;
|
bone.rotation = bone.data.rotation;
|
||||||
return;
|
goto default; // Fall through.
|
||||||
default:
|
default:
|
||||||
return;
|
return;
|
||||||
case MixBlend.First:
|
case MixBlend.First:
|
||||||
@ -451,21 +458,7 @@ namespace Spine {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
r1 = blend == MixBlend.Setup ? bone.data.rotation : bone.rotation;
|
r1 = blend == MixBlend.Setup ? bone.data.rotation : bone.rotation;
|
||||||
if (time >= frames[frames.Length - RotateTimeline.ENTRIES]) // Time is after last frame.
|
r2 = bone.data.rotation + timeline.GetCurveValue(time);
|
||||||
r2 = bone.data.rotation + frames[frames.Length + RotateTimeline.PREV_ROTATION];
|
|
||||||
else {
|
|
||||||
// Interpolate between the previous frame and the current frame.
|
|
||||||
int frame = Animation.BinarySearch(frames, time, RotateTimeline.ENTRIES);
|
|
||||||
float prevRotation = frames[frame + RotateTimeline.PREV_ROTATION];
|
|
||||||
float frameTime = frames[frame];
|
|
||||||
float percent = timeline.GetCurvePercent((frame >> 1) - 1,
|
|
||||||
1 - (time - frameTime) / (frames[frame + RotateTimeline.PREV_TIME] - frameTime));
|
|
||||||
|
|
||||||
r2 = frames[frame + RotateTimeline.ROTATION] - prevRotation;
|
|
||||||
r2 -= (16384 - (int)(16384.499999999996 - r2 / 360)) * 360;
|
|
||||||
r2 = prevRotation + r2 * percent + bone.data.rotation;
|
|
||||||
r2 -= (16384 - (int)(16384.499999999996 - r2 / 360)) * 360;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mix between rotations using the direction of the shortest route on the first frame.
|
// Mix between rotations using the direction of the shortest route on the first frame.
|
||||||
@ -494,8 +487,7 @@ namespace Spine {
|
|||||||
timelinesRotation[i] = total;
|
timelinesRotation[i] = total;
|
||||||
}
|
}
|
||||||
timelinesRotation[i + 1] = diff;
|
timelinesRotation[i + 1] = diff;
|
||||||
r1 += total * alpha;
|
bone.rotation = r1 + total * alpha;
|
||||||
bone.rotation = r1 - (16384 - (int)(16384.499999999996 - r1 / 360)) * 360;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void QueueEvents (TrackEntry entry, float animationTime) {
|
private void QueueEvents (TrackEntry entry, float animationTime) {
|
||||||
@ -577,10 +569,17 @@ namespace Spine {
|
|||||||
queue.Drain();
|
queue.Drain();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes the <see cref="TrackEntry.Next">next entry</see> and all entries after it for the specified entry.</summary>
|
||||||
|
public void ClearNext (TrackEntry entry) {
|
||||||
|
DisposeNext(entry.next);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>Sets the active TrackEntry for a given track number.</summary>
|
/// <summary>Sets the active TrackEntry for a given track number.</summary>
|
||||||
private void SetCurrent (int index, TrackEntry current, bool interrupt) {
|
private void SetCurrent (int index, TrackEntry current, bool interrupt) {
|
||||||
TrackEntry from = ExpandToIndex(index);
|
TrackEntry from = ExpandToIndex(index);
|
||||||
tracks.Items[index] = current;
|
tracks.Items[index] = current;
|
||||||
|
current.previous = null;
|
||||||
|
|
||||||
if (from != null) {
|
if (from != null) {
|
||||||
if (interrupt) queue.Interrupt(from);
|
if (interrupt) queue.Interrupt(from);
|
||||||
@ -647,7 +646,7 @@ namespace Spine {
|
|||||||
/// equivalent to calling <see cref="SetAnimation(int, Animation, bool)"/>.</summary>
|
/// equivalent to calling <see cref="SetAnimation(int, Animation, bool)"/>.</summary>
|
||||||
/// <param name="delay">
|
/// <param name="delay">
|
||||||
/// If > 0, sets <see cref="TrackEntry.Delay"/>. If <= 0, the delay set is the duration of the previous track entry
|
/// If > 0, sets <see cref="TrackEntry.Delay"/>. If <= 0, the delay set is the duration of the previous track entry
|
||||||
/// minus any mix duration (from the {@link AnimationStateData}) plus the specified <code>Delay</code> (ie the mix
|
/// minus any mix duration (from the <see cref="AnimationStateData"/> plus the specified <code>Delay</code> (ie the mix
|
||||||
/// ends at (<code>Delay</code> = 0) or before (<code>Delay</code> < 0) the previous track entry duration). If the
|
/// ends at (<code>Delay</code> = 0) or before (<code>Delay</code> < 0) the previous track entry duration). If the
|
||||||
/// previous entry is looping, its next loop completion is used instead of its duration.
|
/// previous entry is looping, its next loop completion is used instead of its duration.
|
||||||
/// </param>
|
/// </param>
|
||||||
@ -669,18 +668,8 @@ namespace Spine {
|
|||||||
queue.Drain();
|
queue.Drain();
|
||||||
} else {
|
} else {
|
||||||
last.next = entry;
|
last.next = entry;
|
||||||
if (delay <= 0) {
|
entry.previous = last;
|
||||||
float duration = last.animationEnd - last.animationStart;
|
if (delay <= 0) delay += last.TrackComplete - entry.mixDuration;
|
||||||
if (duration != 0) {
|
|
||||||
if (last.loop) {
|
|
||||||
delay += duration * (1 + (int)(last.trackTime / duration)); // Completion of next loop.
|
|
||||||
} else {
|
|
||||||
delay += Math.Max(duration, last.trackTime); // After duration, else next update.
|
|
||||||
}
|
|
||||||
delay -= data.GetMix(last.animation, animation);
|
|
||||||
} else
|
|
||||||
delay = last.trackTime; // Next update.
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
entry.delay = delay;
|
entry.delay = delay;
|
||||||
@ -698,11 +687,11 @@ namespace Spine {
|
|||||||
/// 0 still mixes out over one frame.</para>
|
/// 0 still mixes out over one frame.</para>
|
||||||
/// <para>
|
/// <para>
|
||||||
/// Mixing in is done by first setting an empty animation, then adding an animation using
|
/// Mixing in is done by first setting an empty animation, then adding an animation using
|
||||||
/// <see cref="AnimationState.AddAnimation(int, Animation, boolean, float)"/> and on the returned track entry, set the
|
/// <see cref="AnimationState.AddAnimation(int, Animation, bool, float)"/> with the desired delay (an empty animation has a duration of 0) and on
|
||||||
/// <see cref="TrackEntry.SetMixDuration(float)"/>. Mixing from an empty animation causes the new animation to be applied more and
|
/// the returned track entry, set the <see cref="TrackEntry.SetMixDuration(float)"/>. Mixing from an empty animation causes the new
|
||||||
/// more over the mix duration. Properties keyed in the new animation transition from the value from lower tracks or from the
|
/// animation to be applied more and more over the mix duration. Properties keyed in the new animation transition from the value
|
||||||
/// setup pose value if no lower tracks key the property to the value keyed in the new animation.</para>
|
/// from lower tracks or from the setup pose value if no lower tracks key the property to the value keyed in the new
|
||||||
/// </summary>
|
/// animation.</para></summary>
|
||||||
public TrackEntry SetEmptyAnimation (int trackIndex, float mixDuration) {
|
public TrackEntry SetEmptyAnimation (int trackIndex, float mixDuration) {
|
||||||
TrackEntry entry = SetAnimation(trackIndex, AnimationState.EmptyAnimation, false);
|
TrackEntry entry = SetAnimation(trackIndex, AnimationState.EmptyAnimation, false);
|
||||||
entry.mixDuration = mixDuration;
|
entry.mixDuration = mixDuration;
|
||||||
@ -725,10 +714,10 @@ namespace Spine {
|
|||||||
/// after the <see cref="AnimationState.Dispose"/> event occurs.
|
/// after the <see cref="AnimationState.Dispose"/> event occurs.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
public TrackEntry AddEmptyAnimation (int trackIndex, float mixDuration, float delay) {
|
public TrackEntry AddEmptyAnimation (int trackIndex, float mixDuration, float delay) {
|
||||||
if (delay <= 0) delay -= mixDuration;
|
TrackEntry entry = AddAnimation(trackIndex, AnimationState.EmptyAnimation, false, delay <= 0 ? 1 : delay);
|
||||||
TrackEntry entry = AddAnimation(trackIndex, AnimationState.EmptyAnimation, false, delay);
|
|
||||||
entry.mixDuration = mixDuration;
|
entry.mixDuration = mixDuration;
|
||||||
entry.trackEnd = mixDuration;
|
entry.trackEnd = mixDuration;
|
||||||
|
if (delay <= 0 && entry.previous != null) entry.delay = entry.previous.TrackComplete - entry.mixDuration;
|
||||||
return entry;
|
return entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -738,8 +727,9 @@ namespace Spine {
|
|||||||
public void SetEmptyAnimations (float mixDuration) {
|
public void SetEmptyAnimations (float mixDuration) {
|
||||||
bool oldDrainDisabled = queue.drainDisabled;
|
bool oldDrainDisabled = queue.drainDisabled;
|
||||||
queue.drainDisabled = true;
|
queue.drainDisabled = true;
|
||||||
|
var tracksItems = tracks.Items;
|
||||||
for (int i = 0, n = tracks.Count; i < n; i++) {
|
for (int i = 0, n = tracks.Count; i < n; i++) {
|
||||||
TrackEntry current = tracks.Items[i];
|
TrackEntry current = tracksItems[i];
|
||||||
if (current != null) SetEmptyAnimation(current.trackIndex, mixDuration);
|
if (current != null) SetEmptyAnimation(current.trackIndex, mixDuration);
|
||||||
}
|
}
|
||||||
queue.drainDisabled = oldDrainDisabled;
|
queue.drainDisabled = oldDrainDisabled;
|
||||||
@ -798,10 +788,10 @@ namespace Spine {
|
|||||||
animationsChanged = false;
|
animationsChanged = false;
|
||||||
|
|
||||||
// Process in the order that animations are applied.
|
// Process in the order that animations are applied.
|
||||||
propertyIDs.Clear();
|
propertyIds.Clear();
|
||||||
|
int n = tracks.Count;
|
||||||
var tracksItems = tracks.Items;
|
var tracksItems = tracks.Items;
|
||||||
for (int i = 0, n = tracks.Count; i < n; i++) {
|
for (int i = 0; i < n; i++) {
|
||||||
TrackEntry entry = tracksItems[i];
|
TrackEntry entry = tracksItems[i];
|
||||||
if (entry == null) continue;
|
if (entry == null) continue;
|
||||||
while (entry.mixingFrom != null) // Move to last entry, then iterate in reverse.
|
while (entry.mixingFrom != null) // Move to last entry, then iterate in reverse.
|
||||||
@ -814,6 +804,8 @@ namespace Spine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private void ComputeHold (TrackEntry entry) {
|
private void ComputeHold (TrackEntry entry) {
|
||||||
TrackEntry to = entry.mixingTo;
|
TrackEntry to = entry.mixingTo;
|
||||||
var timelines = entry.animation.timelines.Items;
|
var timelines = entry.animation.timelines.Items;
|
||||||
@ -821,11 +813,11 @@ namespace Spine {
|
|||||||
var timelineMode = entry.timelineMode.Resize(timelinesCount).Items; //timelineMode.setSize(timelinesCount);
|
var timelineMode = entry.timelineMode.Resize(timelinesCount).Items; //timelineMode.setSize(timelinesCount);
|
||||||
entry.timelineHoldMix.Clear();
|
entry.timelineHoldMix.Clear();
|
||||||
var timelineHoldMix = entry.timelineHoldMix.Resize(timelinesCount).Items; //timelineHoldMix.setSize(timelinesCount);
|
var timelineHoldMix = entry.timelineHoldMix.Resize(timelinesCount).Items; //timelineHoldMix.setSize(timelinesCount);
|
||||||
var propertyIDs = this.propertyIDs;
|
var propertyIds = this.propertyIds;
|
||||||
|
|
||||||
if (to != null && to.holdPrevious) {
|
if (to != null && to.holdPrevious) {
|
||||||
for (int i = 0; i < timelinesCount; i++)
|
for (int i = 0; i < timelinesCount; i++)
|
||||||
timelineMode[i] = propertyIDs.Add(timelines[i].PropertyId) ? AnimationState.HoldFirst : AnimationState.HoldSubsequent;
|
timelineMode[i] = propertyIds.AddAll(timelines[i].PropertyIds) ? AnimationState.HoldFirst : AnimationState.HoldSubsequent;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -833,15 +825,15 @@ namespace Spine {
|
|||||||
// outer:
|
// outer:
|
||||||
for (int i = 0; i < timelinesCount; i++) {
|
for (int i = 0; i < timelinesCount; i++) {
|
||||||
Timeline timeline = timelines[i];
|
Timeline timeline = timelines[i];
|
||||||
int id = timeline.PropertyId;
|
String[] ids = timeline.PropertyIds;
|
||||||
if (!propertyIDs.Add(id))
|
if (!propertyIds.AddAll(ids))
|
||||||
timelineMode[i] = AnimationState.Subsequent;
|
timelineMode[i] = AnimationState.Subsequent;
|
||||||
else if (to == null || timeline is AttachmentTimeline || timeline is DrawOrderTimeline
|
else if (to == null || timeline is AttachmentTimeline || timeline is DrawOrderTimeline
|
||||||
|| timeline is EventTimeline || !to.animation.HasTimeline(id)) {
|
|| timeline is EventTimeline || !to.animation.HasTimeline(ids)) {
|
||||||
timelineMode[i] = AnimationState.First;
|
timelineMode[i] = AnimationState.First;
|
||||||
} else {
|
} else {
|
||||||
for (TrackEntry next = to.mixingTo; next != null; next = next.mixingTo) {
|
for (TrackEntry next = to.mixingTo; next != null; next = next.mixingTo) {
|
||||||
if (next.animation.HasTimeline(id)) continue;
|
if (next.animation.HasTimeline(ids)) continue;
|
||||||
if (next.mixDuration > 0) {
|
if (next.mixDuration > 0) {
|
||||||
timelineMode[i] = AnimationState.HoldMix;
|
timelineMode[i] = AnimationState.HoldMix;
|
||||||
timelineHoldMix[i] = next;
|
timelineHoldMix[i] = next;
|
||||||
@ -892,8 +884,9 @@ namespace Spine {
|
|||||||
|
|
||||||
override public string ToString () {
|
override public string ToString () {
|
||||||
var buffer = new System.Text.StringBuilder();
|
var buffer = new System.Text.StringBuilder();
|
||||||
|
var tracksItems = tracks.Items;
|
||||||
for (int i = 0, n = tracks.Count; i < n; i++) {
|
for (int i = 0, n = tracks.Count; i < n; i++) {
|
||||||
TrackEntry entry = tracks.Items[i];
|
TrackEntry entry = tracksItems[i];
|
||||||
if (entry == null) continue;
|
if (entry == null) continue;
|
||||||
if (buffer.Length > 0) buffer.Append(", ");
|
if (buffer.Length > 0) buffer.Append(", ");
|
||||||
buffer.Append(entry.ToString());
|
buffer.Append(entry.ToString());
|
||||||
@ -912,7 +905,7 @@ namespace Spine {
|
|||||||
public class TrackEntry : Pool<TrackEntry>.IPoolable {
|
public class TrackEntry : Pool<TrackEntry>.IPoolable {
|
||||||
internal Animation animation;
|
internal Animation animation;
|
||||||
|
|
||||||
internal TrackEntry next, mixingFrom, mixingTo;
|
internal TrackEntry previous, next, mixingFrom, mixingTo;
|
||||||
// difference to libgdx reference: delegates are used for event callbacks instead of 'AnimationStateListener listener'.
|
// difference to libgdx reference: delegates are used for event callbacks instead of 'AnimationStateListener listener'.
|
||||||
public event AnimationState.TrackEntryDelegate Start, Interrupt, End, Dispose, Complete;
|
public event AnimationState.TrackEntryDelegate Start, Interrupt, End, Dispose, Complete;
|
||||||
public event AnimationState.TrackEntryEventDelegate Event;
|
public event AnimationState.TrackEntryEventDelegate Event;
|
||||||
@ -925,7 +918,7 @@ namespace Spine {
|
|||||||
|
|
||||||
internal int trackIndex;
|
internal int trackIndex;
|
||||||
|
|
||||||
internal bool loop, holdPrevious;
|
internal bool loop, holdPrevious, reverse;
|
||||||
internal float eventThreshold, attachmentThreshold, drawOrderThreshold;
|
internal float eventThreshold, attachmentThreshold, drawOrderThreshold;
|
||||||
internal float animationStart, animationEnd, animationLast, nextAnimationLast;
|
internal float animationStart, animationEnd, animationLast, nextAnimationLast;
|
||||||
internal float delay, trackTime, trackLast, nextTrackLast, trackEnd, timeScale = 1f;
|
internal float delay, trackTime, trackLast, nextTrackLast, trackEnd, timeScale = 1f;
|
||||||
@ -937,6 +930,7 @@ namespace Spine {
|
|||||||
|
|
||||||
// IPoolable.Reset()
|
// IPoolable.Reset()
|
||||||
public void Reset () {
|
public void Reset () {
|
||||||
|
previous = null;
|
||||||
next = null;
|
next = null;
|
||||||
mixingFrom = null;
|
mixingFrom = null;
|
||||||
mixingTo = null;
|
mixingTo = null;
|
||||||
@ -973,7 +967,10 @@ namespace Spine {
|
|||||||
/// track entry <see cref="TrackEntry.TrackTime"/> >= this track entry's <code>Delay</code>).</para>
|
/// track entry <see cref="TrackEntry.TrackTime"/> >= this track entry's <code>Delay</code>).</para>
|
||||||
/// <para>
|
/// <para>
|
||||||
/// <see cref="TrackEntry.TimeScale"/> affects the delay.</para>
|
/// <see cref="TrackEntry.TimeScale"/> affects the delay.</para>
|
||||||
/// </summary>
|
/// <para>
|
||||||
|
/// When using <see cref="AnimationState.AddAnimation(int, Animation, bool, float)"/> with a <code>delay</code> <= 0, the delay
|
||||||
|
/// is set using the mix duration from the <see cref="AnimationStateData"/>. If <see cref="mixDuration"/> is set afterward, the delay
|
||||||
|
/// may need to be adjusted.</summary>
|
||||||
public float Delay { get { return delay; } set { delay = value; } }
|
public float Delay { get { return delay; } set { delay = value; } }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -994,6 +991,21 @@ namespace Spine {
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public float TrackEnd { get { return trackEnd; } set { trackEnd = value; } }
|
public float TrackEnd { get { return trackEnd; } set { trackEnd = value; } }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If this track entry is non-looping, the track time in seconds when <see cref="AnimationEnd"/> is reached, or the current
|
||||||
|
/// <see cref="TrackTime"/> if it has already been reached. If this track entry is looping, the track time when this
|
||||||
|
/// animation will reach its next <see cref="AnimationEnd"/> (the next loop completion).</summary>
|
||||||
|
public float TrackComplete {
|
||||||
|
get {
|
||||||
|
float duration = animationEnd - animationStart;
|
||||||
|
if (duration != 0) {
|
||||||
|
if (loop) return duration * (1 + (int)(trackTime / duration)); // Completion of next loop.
|
||||||
|
if (trackTime < duration) return duration; // Before duration.
|
||||||
|
}
|
||||||
|
return trackTime; // Next update.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// <para>
|
/// <para>
|
||||||
/// Seconds when this animation starts, both initially and after looping. Defaults to 0.</para>
|
/// Seconds when this animation starts, both initially and after looping. Defaults to 0.</para>
|
||||||
@ -1043,11 +1055,13 @@ namespace Spine {
|
|||||||
/// Multiplier for the delta time when this track entry is updated, causing time for this animation to pass slower or
|
/// Multiplier for the delta time when this track entry is updated, causing time for this animation to pass slower or
|
||||||
/// faster. Defaults to 1.</para>
|
/// faster. Defaults to 1.</para>
|
||||||
/// <para>
|
/// <para>
|
||||||
|
/// Values < 0 are not supported. To play an animation in reverse, use <see cref="Reverse"/>.
|
||||||
|
/// <para>
|
||||||
/// <see cref="TrackEntry.MixTime"/> is not affected by track entry time scale, so <see cref="TrackEntry.MixDuration"/> may need to be adjusted to
|
/// <see cref="TrackEntry.MixTime"/> is not affected by track entry time scale, so <see cref="TrackEntry.MixDuration"/> may need to be adjusted to
|
||||||
/// match the animation speed.</para>
|
/// match the animation speed.</para>
|
||||||
/// <para>
|
/// <para>
|
||||||
/// When using <see cref="AnimationState.AddAnimation(int, Animation, boolean, float)"> with a <code>Delay</code> <= 0, note the
|
/// When using <see cref="AnimationState.AddAnimation(int, Animation, bool, float)"> with a <code>Delay</code> <= 0, the
|
||||||
/// {<see cref="TrackEntry.Delay"/> is set using the mix duration from the <see cref="AnimationStateData"/>, assuming time scale to be 1. If
|
/// <see cref="TrackEntry.Delay"/> is set using the mix duration from the <see cref="AnimationStateData"/>, assuming time scale to be 1. If
|
||||||
/// the time scale is not 1, the delay may need to be adjusted.</para>
|
/// the time scale is not 1, the delay may need to be adjusted.</para>
|
||||||
/// <para>
|
/// <para>
|
||||||
/// See AnimationState <see cref="AnimationState.TimeScale"/> for affecting all animations.</para>
|
/// See AnimationState <see cref="AnimationState.TimeScale"/> for affecting all animations.</para>
|
||||||
@ -1086,9 +1100,16 @@ namespace Spine {
|
|||||||
public float DrawOrderThreshold { get { return drawOrderThreshold; } set { drawOrderThreshold = value; } }
|
public float DrawOrderThreshold { get { return drawOrderThreshold; } set { drawOrderThreshold = value; } }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The animation queued to start after this animation, or null. <code>Next</code> makes up a linked list. </summary>
|
/// The animation queued to start after this animation, or null if there is none. <code>next</code> makes up a doubly linked
|
||||||
|
/// list.
|
||||||
|
/// <para>
|
||||||
|
/// See <see cref="AnimationState.ClearNext(TrackEntry)"/> to truncate the list.</para></summary>
|
||||||
public TrackEntry Next { get { return next; } }
|
public TrackEntry Next { get { return next; } }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The animation queued to play before this animation, or null. <code>previous</code> makes up a doubly linked list.</summary>
|
||||||
|
public TrackEntry Previous { get { return previous; } }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns true if at least one loop has been completed.</summary>
|
/// Returns true if at least one loop has been completed.</summary>
|
||||||
/// <seealso cref="TrackEntry.Complete"/>
|
/// <seealso cref="TrackEntry.Complete"/>
|
||||||
@ -1108,20 +1129,21 @@ namespace Spine {
|
|||||||
/// <para>
|
/// <para>
|
||||||
/// The <code>MixDuration</code> can be set manually rather than use the value from
|
/// The <code>MixDuration</code> can be set manually rather than use the value from
|
||||||
/// <see cref="AnimationStateData.GetMix(Animation, Animation)"/>. In that case, the <code>MixDuration</code> can be set for a new
|
/// <see cref="AnimationStateData.GetMix(Animation, Animation)"/>. In that case, the <code>MixDuration</code> can be set for a new
|
||||||
/// track entry only before <see cref="AnimationState.Update(float)"/> is first called.</para>
|
/// track entry only before <see cref="AnimationState.Update(float)"/> is first called.</para>
|
||||||
/// <para>
|
/// <para>
|
||||||
/// When using <seealso cref="AnimationState.AddAnimation(int, Animation, bool, float)"/> with a <code>Delay</code> <= 0, note the
|
/// When using <seealso cref="AnimationState.AddAnimation(int, Animation, bool, float)"/> with a <code>Delay</code> <= 0, the
|
||||||
/// <see cref="TrackEntry.Delay"/> is set using the mix duration from the <see cref=" AnimationStateData"/>, not a mix duration set
|
/// <see cref="TrackEntry.Delay"/> is set using the mix duration from the <see cref=" AnimationStateData"/>. If <code>mixDuration</code> is set
|
||||||
/// afterward.</para>
|
/// afterward, the delay may need to be adjusted. For example:
|
||||||
/// </summary>
|
/// <code>entry.Delay = entry.previous.TrackComplete - entry.MixDuration;</code>
|
||||||
|
/// </para></summary>
|
||||||
public float MixDuration { get { return mixDuration; } set { mixDuration = value; } }
|
public float MixDuration { get { return mixDuration; } set { mixDuration = value; } }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// <para>
|
/// <para>
|
||||||
/// Controls how properties keyed in the animation are mixed with lower tracks. Defaults to <see cref="MixBlend.Replace"/>, which
|
/// Controls how properties keyed in the animation are mixed with lower tracks. Defaults to <see cref="MixBlend.Replace"/>.
|
||||||
/// replaces the values from the lower tracks with the animation values. <see cref="MixBlend.Add"/> adds the animation values to
|
/// </para><para>
|
||||||
/// the values from the lower tracks.</para>
|
/// Track entries on track 0 ignore this setting and always use <see cref="MixBlend.First"/>.
|
||||||
/// <para>
|
/// </para><para>
|
||||||
/// The <code>MixBlend</code> can be set for a new track entry only before <see cref="AnimationState.Apply(Skeleton)"/> is first
|
/// The <code>MixBlend</code> can be set for a new track entry only before <see cref="AnimationState.Apply(Skeleton)"/> is first
|
||||||
/// called.</para>
|
/// called.</para>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -1153,6 +1175,10 @@ namespace Spine {
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public bool HoldPrevious { get { return holdPrevious; } set { holdPrevious = value; } }
|
public bool HoldPrevious { get { return holdPrevious; } set { holdPrevious = value; } }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If true, the animation will be applied in reverse. Events are not fired when an animation is applied in reverse.</summary>
|
||||||
|
public bool Reverse { get { return reverse; } set { reverse = value; } }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// <para>
|
/// <para>
|
||||||
/// Resets the rotation directions for mixing this entry's rotate timelines. This can be useful to avoid bones rotating the
|
/// Resets the rotation directions for mixing this entry's rotate timelines. This can be useful to avoid bones rotating the
|
||||||
@ -1331,4 +1357,14 @@ namespace Spine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class HashSetExtensions {
|
||||||
|
public static bool AddAll<T> (this HashSet<T> set, T[] addSet) {
|
||||||
|
bool anyItemAdded = false;
|
||||||
|
for (int i = 0, n = addSet.Length; i < n; ++i) {
|
||||||
|
var item = addSet[i];
|
||||||
|
anyItemAdded |= set.Add(item);
|
||||||
|
}
|
||||||
|
return anyItemAdded;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -39,7 +39,7 @@ namespace Spine {
|
|||||||
private Atlas[] atlasArray;
|
private Atlas[] atlasArray;
|
||||||
|
|
||||||
public AtlasAttachmentLoader (params Atlas[] atlasArray) {
|
public AtlasAttachmentLoader (params Atlas[] atlasArray) {
|
||||||
if (atlasArray == null) throw new ArgumentNullException("atlas array cannot be null.");
|
if (atlasArray == null) throw new ArgumentNullException("atlas", "atlas array cannot be null.");
|
||||||
this.atlasArray = atlasArray;
|
this.atlasArray = atlasArray;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -56,7 +56,7 @@ namespace Spine {
|
|||||||
|
|
||||||
deformAttachment = this;
|
deformAttachment = this;
|
||||||
lock (VertexAttachment.nextIdLock) {
|
lock (VertexAttachment.nextIdLock) {
|
||||||
id = (VertexAttachment.nextID++ & 65535) << 11;
|
id = VertexAttachment.nextID++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -116,6 +116,7 @@ namespace Spine {
|
|||||||
/// <summary>Returns the magnitide (always positive) of the world scale Y.</summary>
|
/// <summary>Returns the magnitide (always positive) of the world scale Y.</summary>
|
||||||
public float WorldScaleY { get { return (float)Math.Sqrt(b * b + d * d); } }
|
public float WorldScaleY { get { return (float)Math.Sqrt(b * b + d * d); } }
|
||||||
|
|
||||||
|
/// <summary>Copy constructor. Does not copy the <see cref="Children"/> bones.</summary>
|
||||||
/// <param name="parent">May be null.</param>
|
/// <param name="parent">May be null.</param>
|
||||||
public Bone (BoneData data, Skeleton skeleton, Bone parent) {
|
public Bone (BoneData data, Skeleton skeleton, Bone parent) {
|
||||||
if (data == null) throw new ArgumentNullException("data", "data cannot be null.");
|
if (data == null) throw new ArgumentNullException("data", "data cannot be null.");
|
||||||
@ -305,10 +306,10 @@ namespace Spine {
|
|||||||
|
|
||||||
public void WorldToLocal (float worldX, float worldY, out float localX, out float localY) {
|
public void WorldToLocal (float worldX, float worldY, out float localX, out float localY) {
|
||||||
float a = this.a, b = this.b, c = this.c, d = this.d;
|
float a = this.a, b = this.b, c = this.c, d = this.d;
|
||||||
float invDet = 1 / (a * d - b * c);
|
float det = a * d - b * c;
|
||||||
float x = worldX - this.worldX, y = worldY - this.worldY;
|
float x = worldX - this.worldX, y = worldY - this.worldY;
|
||||||
localX = (x * d * invDet - y * b * invDet);
|
localX = (x * d - y * b) / det;
|
||||||
localY = (y * a * invDet - x * c * invDet);
|
localY = (y * a - x * c) / det;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void LocalToWorld (float localX, float localY, out float worldX, out float worldY) {
|
public void LocalToWorld (float localX, float localY, out float worldX, out float worldY) {
|
||||||
|
|||||||
@ -79,20 +79,16 @@ namespace Spine {
|
|||||||
stretch = constraint.stretch;
|
stretch = constraint.stretch;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Applies the constraint to the constrained bones.</summary>
|
|
||||||
public void Apply () {
|
|
||||||
Update();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Update () {
|
public void Update () {
|
||||||
|
if (mix == 0) return;
|
||||||
Bone target = this.target;
|
Bone target = this.target;
|
||||||
ExposedList<Bone> bones = this.bones;
|
var bones = this.bones.Items;
|
||||||
switch (bones.Count) {
|
switch (this.bones.Count) {
|
||||||
case 1:
|
case 1:
|
||||||
Apply(bones.Items[0], target.worldX, target.worldY, compress, stretch, data.uniform, mix);
|
Apply(bones[0], target.worldX, target.worldY, compress, stretch, data.uniform, mix);
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
Apply(bones.Items[0], bones.Items[1], target.worldX, target.worldY, bendDirection, stretch, softness, mix);
|
Apply(bones[0], bones[1], target.worldX, target.worldY, bendDirection, stretch, softness, mix);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -157,6 +153,7 @@ namespace Spine {
|
|||||||
/// <summary>Applies 1 bone IK. The target is specified in the world coordinate system.</summary>
|
/// <summary>Applies 1 bone IK. The target is specified in the world coordinate system.</summary>
|
||||||
static public void Apply (Bone bone, float targetX, float targetY, bool compress, bool stretch, bool uniform,
|
static public void Apply (Bone bone, float targetX, float targetY, bool compress, bool stretch, bool uniform,
|
||||||
float alpha) {
|
float alpha) {
|
||||||
|
if (bone == null) throw new ArgumentNullException("bone", "bone cannot be null.");
|
||||||
if (!bone.appliedValid) bone.UpdateAppliedTransform();
|
if (!bone.appliedValid) bone.UpdateAppliedTransform();
|
||||||
Bone p = bone.parent;
|
Bone p = bone.parent;
|
||||||
|
|
||||||
@ -175,16 +172,16 @@ namespace Spine {
|
|||||||
float sc = pc / bone.skeleton.ScaleY;
|
float sc = pc / bone.skeleton.ScaleY;
|
||||||
pb = -sc * s * bone.skeleton.ScaleX;
|
pb = -sc * s * bone.skeleton.ScaleX;
|
||||||
pd = sa * s * bone.skeleton.ScaleY;
|
pd = sa * s * bone.skeleton.ScaleY;
|
||||||
rotationIK += (float)Math.Atan2(pc, pa) * MathUtils.RadDeg;
|
rotationIK += (float)Math.Atan2(sc, sa) * MathUtils.RadDeg;
|
||||||
goto default; // Fall through.
|
goto default; // Fall through.
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
float x = targetX - p.worldX, y = targetY - p.worldY;
|
float x = targetX - p.worldX, y = targetY - p.worldY;
|
||||||
float d = pa * pd - pb * pc;
|
float d = pa * pd - pb * pc;
|
||||||
tx = (x * pd - y * pb) / d - bone.ax;
|
tx = (x * pd - y * pb) / d - bone.ax;
|
||||||
ty = (y * pa - x * pc) / d - bone.ay;
|
ty = (y * pa - x * pc) / d - bone.ay;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rotationIK += (float)Math.Atan2(ty, tx) * MathUtils.RadDeg;
|
rotationIK += (float)Math.Atan2(ty, tx) * MathUtils.RadDeg;
|
||||||
@ -198,13 +195,10 @@ namespace Spine {
|
|||||||
if (compress || stretch) {
|
if (compress || stretch) {
|
||||||
switch (bone.data.transformMode) {
|
switch (bone.data.transformMode) {
|
||||||
case TransformMode.NoScale:
|
case TransformMode.NoScale:
|
||||||
tx = targetX - bone.worldX;
|
case TransformMode.NoScaleOrReflection:
|
||||||
ty = targetY - bone.worldY;
|
|
||||||
break;
|
|
||||||
case TransformMode.NoScaleOrReflection:
|
|
||||||
tx = targetX - bone.worldX;
|
tx = targetX - bone.worldX;
|
||||||
ty = targetY - bone.worldY;
|
ty = targetY - bone.worldY;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
float b = bone.data.length * sx, dd = (float)Math.Sqrt(tx * tx + ty * ty);
|
float b = bone.data.length * sx, dd = (float)Math.Sqrt(tx * tx + ty * ty);
|
||||||
if ((compress && dd < b) || (stretch && dd > b) && b > 0.0001f) {
|
if ((compress && dd < b) || (stretch && dd > b) && b > 0.0001f) {
|
||||||
@ -220,10 +214,8 @@ namespace Spine {
|
|||||||
/// <param name="child">A direct descendant of the parent bone.</param>
|
/// <param name="child">A direct descendant of the parent bone.</param>
|
||||||
static public void Apply (Bone parent, Bone child, float targetX, float targetY, int bendDir, bool stretch, float softness,
|
static public void Apply (Bone parent, Bone child, float targetX, float targetY, int bendDir, bool stretch, float softness,
|
||||||
float alpha) {
|
float alpha) {
|
||||||
if (alpha == 0) {
|
if (parent == null) throw new ArgumentNullException("parent", "parent cannot be null.");
|
||||||
child.UpdateWorldTransform();
|
if (child == null) throw new ArgumentNullException("child", "child cannot be null.");
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!parent.appliedValid) parent.UpdateAppliedTransform();
|
if (!parent.appliedValid) parent.UpdateAppliedTransform();
|
||||||
if (!child.appliedValid) child.UpdateAppliedTransform();
|
if (!child.appliedValid) child.UpdateAppliedTransform();
|
||||||
float px = parent.ax, py = parent.ay, psx = parent.ascaleX, sx = psx, psy = parent.ascaleY, csx = child.ascaleX;
|
float px = parent.ax, py = parent.ay, psx = parent.ascaleX, sx = psx, psy = parent.ascaleY, csx = child.ascaleX;
|
||||||
|
|||||||
@ -34,7 +34,7 @@ namespace Spine {
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// <para>
|
/// <para>
|
||||||
/// Stores the current pose for a path constraint. A path constraint adjusts the rotation, translation, and scale of the
|
/// Stores the current pose for a path constraint. A path constraint adjusts the rotation, translation, and scale of the
|
||||||
/// constrained bones so they follow a {@link PathAttachment}.</para>
|
/// constrained bones so they follow a <see cref="PathAttachment"/>.</para>
|
||||||
/// <para>
|
/// <para>
|
||||||
/// See <a href="http://esotericsoftware.com/spine-path-constraints">Path constraints</a> in the Spine User Guide.</para>
|
/// See <a href="http://esotericsoftware.com/spine-path-constraints">Path constraints</a> in the Spine User Guide.</para>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -82,11 +82,6 @@ namespace Spine {
|
|||||||
translateMix = constraint.translateMix;
|
translateMix = constraint.translateMix;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Applies the constraint to the constrained bones.</summary>
|
|
||||||
public void Apply () {
|
|
||||||
Update();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Update () {
|
public void Update () {
|
||||||
PathAttachment attachment = target.Attachment as PathAttachment;
|
PathAttachment attachment = target.Attachment as PathAttachment;
|
||||||
if (attachment == null) return;
|
if (attachment == null) return;
|
||||||
|
|||||||
@ -40,7 +40,6 @@ namespace Spine {
|
|||||||
internal ExposedList<TransformConstraint> transformConstraints;
|
internal ExposedList<TransformConstraint> transformConstraints;
|
||||||
internal ExposedList<PathConstraint> pathConstraints;
|
internal ExposedList<PathConstraint> pathConstraints;
|
||||||
internal ExposedList<IUpdatable> updateCache = new ExposedList<IUpdatable>();
|
internal ExposedList<IUpdatable> updateCache = new ExposedList<IUpdatable>();
|
||||||
internal ExposedList<Bone> updateCacheReset = new ExposedList<Bone>();
|
|
||||||
internal Skin skin;
|
internal Skin skin;
|
||||||
internal float r = 1, g = 1, b = 1, a = 1;
|
internal float r = 1, g = 1, b = 1, a = 1;
|
||||||
internal float time;
|
internal float time;
|
||||||
@ -55,7 +54,13 @@ namespace Spine {
|
|||||||
public ExposedList<IkConstraint> IkConstraints { get { return ikConstraints; } }
|
public ExposedList<IkConstraint> IkConstraints { get { return ikConstraints; } }
|
||||||
public ExposedList<PathConstraint> PathConstraints { get { return pathConstraints; } }
|
public ExposedList<PathConstraint> PathConstraints { get { return pathConstraints; } }
|
||||||
public ExposedList<TransformConstraint> TransformConstraints { get { return transformConstraints; } }
|
public ExposedList<TransformConstraint> TransformConstraints { get { return transformConstraints; } }
|
||||||
public Skin Skin { get { return skin; } set { SetSkin(value); } }
|
|
||||||
|
public Skin Skin {
|
||||||
|
/// <summary>The skeleton's current skin. May be null.</summary>
|
||||||
|
get { return skin; }
|
||||||
|
/// <summary>Sets a skin, <see cref="SetSkin(Skin)"/>.</summary>
|
||||||
|
set { SetSkin(value); }
|
||||||
|
}
|
||||||
public float R { get { return r; } set { r = value; } }
|
public float R { get { return r; } set { r = value; } }
|
||||||
public float G { get { return g; } set { g = value; } }
|
public float G { get { return g; } set { g = value; } }
|
||||||
public float B { get { return b; } set { b = value; } }
|
public float B { get { return b; } set { b = value; } }
|
||||||
@ -72,6 +77,7 @@ namespace Spine {
|
|||||||
[Obsolete("Use ScaleY instead. FlipY is when ScaleY is negative.")]
|
[Obsolete("Use ScaleY instead. FlipY is when ScaleY is negative.")]
|
||||||
public bool FlipY { get { return scaleY < 0; } set { scaleY = value ? -1f : 1f; } }
|
public bool FlipY { get { return scaleY < 0; } set { scaleY = value ? -1f : 1f; } }
|
||||||
|
|
||||||
|
/// <summary>Returns the root bone, or null if the skeleton has no bones.</summary>
|
||||||
public Bone RootBone {
|
public Bone RootBone {
|
||||||
get { return bones.Count == 0 ? null : bones.Items[0]; }
|
get { return bones.Count == 0 ? null : bones.Items[0]; }
|
||||||
}
|
}
|
||||||
@ -81,22 +87,23 @@ namespace Spine {
|
|||||||
this.data = data;
|
this.data = data;
|
||||||
|
|
||||||
bones = new ExposedList<Bone>(data.bones.Count);
|
bones = new ExposedList<Bone>(data.bones.Count);
|
||||||
|
var bonesItems = this.bones.Items;
|
||||||
foreach (BoneData boneData in data.bones) {
|
foreach (BoneData boneData in data.bones) {
|
||||||
Bone bone;
|
Bone bone;
|
||||||
if (boneData.parent == null) {
|
if (boneData.parent == null) {
|
||||||
bone = new Bone(boneData, this, null);
|
bone = new Bone(boneData, this, null);
|
||||||
} else {
|
} else {
|
||||||
Bone parent = bones.Items[boneData.parent.index];
|
Bone parent = bonesItems[boneData.parent.index];
|
||||||
bone = new Bone(boneData, this, parent);
|
bone = new Bone(boneData, this, parent);
|
||||||
parent.children.Add(bone);
|
parent.children.Add(bone);
|
||||||
}
|
}
|
||||||
bones.Add(bone);
|
this.bones.Add(bone);
|
||||||
}
|
}
|
||||||
|
|
||||||
slots = new ExposedList<Slot>(data.slots.Count);
|
slots = new ExposedList<Slot>(data.slots.Count);
|
||||||
drawOrder = new ExposedList<Slot>(data.slots.Count);
|
drawOrder = new ExposedList<Slot>(data.slots.Count);
|
||||||
foreach (SlotData slotData in data.slots) {
|
foreach (SlotData slotData in data.slots) {
|
||||||
Bone bone = bones.Items[slotData.boneData.index];
|
Bone bone = bonesItems[slotData.boneData.index];
|
||||||
Slot slot = new Slot(slotData, bone);
|
Slot slot = new Slot(slotData, bone);
|
||||||
slots.Add(slot);
|
slots.Add(slot);
|
||||||
drawOrder.Add(slot);
|
drawOrder.Add(slot);
|
||||||
@ -115,7 +122,7 @@ namespace Spine {
|
|||||||
pathConstraints.Add(new PathConstraint(pathConstraintData, this));
|
pathConstraints.Add(new PathConstraint(pathConstraintData, this));
|
||||||
|
|
||||||
UpdateCache();
|
UpdateCache();
|
||||||
UpdateWorldTransform();
|
//UpdateWorldTransform();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Caches information about bones and constraints. Must be called if the <see cref="Skin"/> is modified or if bones, constraints, or
|
/// <summary>Caches information about bones and constraints. Must be called if the <see cref="Skin"/> is modified or if bones, constraints, or
|
||||||
@ -123,7 +130,6 @@ namespace Spine {
|
|||||||
public void UpdateCache () {
|
public void UpdateCache () {
|
||||||
var updateCache = this.updateCache;
|
var updateCache = this.updateCache;
|
||||||
updateCache.Clear();
|
updateCache.Clear();
|
||||||
this.updateCacheReset.Clear();
|
|
||||||
|
|
||||||
int boneCount = this.bones.Items.Length;
|
int boneCount = this.bones.Items.Length;
|
||||||
var bones = this.bones;
|
var bones = this.bones;
|
||||||
@ -191,16 +197,19 @@ namespace Spine {
|
|||||||
Bone parent = constrained.Items[0];
|
Bone parent = constrained.Items[0];
|
||||||
SortBone(parent);
|
SortBone(parent);
|
||||||
|
|
||||||
if (constrained.Count > 1) {
|
if (constrained.Count == 1) {
|
||||||
Bone child = constrained.Items[constrained.Count - 1];
|
updateCache.Add(constraint);
|
||||||
if (!updateCache.Contains(child))
|
SortReset(parent.children);
|
||||||
updateCacheReset.Add(child);
|
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
Bone child = constrained.Items[constrained.Count - 1];
|
||||||
|
SortBone(child);
|
||||||
|
|
||||||
updateCache.Add(constraint);
|
updateCache.Add(constraint);
|
||||||
|
|
||||||
SortReset(parent.children);
|
SortReset(parent.children);
|
||||||
constrained.Items[constrained.Count - 1].sorted = true;
|
child.sorted = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SortPathConstraint (PathConstraint constraint) {
|
private void SortPathConstraint (PathConstraint constraint) {
|
||||||
@ -218,17 +227,17 @@ namespace Spine {
|
|||||||
Attachment attachment = slot.attachment;
|
Attachment attachment = slot.attachment;
|
||||||
if (attachment is PathAttachment) SortPathConstraintAttachment(attachment, slotBone);
|
if (attachment is PathAttachment) SortPathConstraintAttachment(attachment, slotBone);
|
||||||
|
|
||||||
var constrained = constraint.bones;
|
var constrained = constraint.bones.Items;
|
||||||
int boneCount = constrained.Count;
|
int boneCount = constraint.bones.Count;
|
||||||
for (int i = 0; i < boneCount; i++)
|
for (int i = 0; i < boneCount; i++)
|
||||||
SortBone(constrained.Items[i]);
|
SortBone(constrained[i]);
|
||||||
|
|
||||||
updateCache.Add(constraint);
|
updateCache.Add(constraint);
|
||||||
|
|
||||||
for (int i = 0; i < boneCount; i++)
|
for (int i = 0; i < boneCount; i++)
|
||||||
SortReset(constrained.Items[i].children);
|
SortReset(constrained[i].children);
|
||||||
for (int i = 0; i < boneCount; i++)
|
for (int i = 0; i < boneCount; i++)
|
||||||
constrained.Items[i].sorted = true;
|
constrained[i].sorted = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SortTransformConstraint (TransformConstraint constraint) {
|
private void SortTransformConstraint (TransformConstraint constraint) {
|
||||||
@ -238,25 +247,25 @@ namespace Spine {
|
|||||||
|
|
||||||
SortBone(constraint.target);
|
SortBone(constraint.target);
|
||||||
|
|
||||||
var constrained = constraint.bones;
|
var constrained = constraint.bones.Items;
|
||||||
int boneCount = constrained.Count;
|
int boneCount = constraint.bones.Count;
|
||||||
if (constraint.data.local) {
|
if (constraint.data.local) {
|
||||||
for (int i = 0; i < boneCount; i++) {
|
for (int i = 0; i < boneCount; i++) {
|
||||||
Bone child = constrained.Items[i];
|
Bone child = constrained[i];
|
||||||
SortBone(child.parent);
|
SortBone(child.parent);
|
||||||
if (!updateCache.Contains(child)) updateCacheReset.Add(child);
|
SortBone(child);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (int i = 0; i < boneCount; i++)
|
for (int i = 0; i < boneCount; i++)
|
||||||
SortBone(constrained.Items[i]);
|
SortBone(constrained[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
updateCache.Add(constraint);
|
updateCache.Add(constraint);
|
||||||
|
|
||||||
for (int i = 0; i < boneCount; i++)
|
for (int i = 0; i < boneCount; i++)
|
||||||
SortReset(constrained.Items[i].children);
|
SortReset(constrained[i].children);
|
||||||
for (int i = 0; i < boneCount; i++)
|
for (int i = 0; i < boneCount; i++)
|
||||||
constrained.Items[i].sorted = true;
|
constrained[i].sorted = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SortPathConstraintAttachment (Skin skin, int slotIndex, Bone slotBone) {
|
private void SortPathConstraintAttachment (Skin skin, int slotIndex, Bone slotBone) {
|
||||||
@ -271,12 +280,12 @@ namespace Spine {
|
|||||||
if (pathBones == null)
|
if (pathBones == null)
|
||||||
SortBone(slotBone);
|
SortBone(slotBone);
|
||||||
else {
|
else {
|
||||||
var bones = this.bones;
|
var bones = this.bones.Items;
|
||||||
for (int i = 0, n = pathBones.Length; i < n;) {
|
for (int i = 0, n = pathBones.Length; i < n;) {
|
||||||
int nn = pathBones[i++];
|
int nn = pathBones[i++];
|
||||||
nn += i;
|
nn += i;
|
||||||
while (i < nn)
|
while (i < nn)
|
||||||
SortBone(bones.Items[pathBones[i++]]);
|
SortBone(bones[pathBones[i++]]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -299,24 +308,17 @@ namespace Spine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Updates the world transform for each bone and applies constraints.</summary>
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates the world transform for each bone and applies all constraints.
|
||||||
|
/// <para>
|
||||||
|
/// See <a href="http://esotericsoftware.com/spine-runtime-skeletons#World-transforms">World transforms</a> in the Spine
|
||||||
|
/// Runtimes Guide.</para>
|
||||||
|
/// </summary>
|
||||||
public void UpdateWorldTransform () {
|
public void UpdateWorldTransform () {
|
||||||
var updateCacheReset = this.updateCacheReset;
|
var updateCache = this.updateCache.Items;
|
||||||
var updateCacheResetItems = updateCacheReset.Items;
|
for (int i = 0, n = this.updateCache.Count; i < n; i++)
|
||||||
for (int i = 0, n = updateCacheReset.Count; i < n; i++) {
|
updateCache[i].Update();
|
||||||
Bone bone = updateCacheResetItems[i];
|
|
||||||
bone.ax = bone.x;
|
|
||||||
bone.ay = bone.y;
|
|
||||||
bone.arotation = bone.rotation;
|
|
||||||
bone.ascaleX = bone.scaleX;
|
|
||||||
bone.ascaleY = bone.scaleY;
|
|
||||||
bone.ashearX = bone.shearX;
|
|
||||||
bone.ashearY = bone.shearY;
|
|
||||||
bone.appliedValid = true;
|
|
||||||
}
|
|
||||||
var updateItems = this.updateCache.Items;
|
|
||||||
for (int i = 0, n = updateCache.Count; i < n; i++)
|
|
||||||
updateItems[i].Update();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -324,22 +326,7 @@ namespace Spine {
|
|||||||
/// all constraints.
|
/// all constraints.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void UpdateWorldTransform (Bone parent) {
|
public void UpdateWorldTransform (Bone parent) {
|
||||||
// This partial update avoids computing the world transform for constrained bones when 1) the bone is not updated
|
if (parent == null) throw new ArgumentNullException("parent", "parent cannot be null.");
|
||||||
// before the constraint, 2) the constraint only needs to access the applied local transform, and 3) the constraint calls
|
|
||||||
// updateWorldTransform.
|
|
||||||
var updateCacheReset = this.updateCacheReset;
|
|
||||||
var updateCacheResetItems = updateCacheReset.Items;
|
|
||||||
for (int i = 0, n = updateCacheReset.Count; i < n; i++) {
|
|
||||||
Bone bone = updateCacheResetItems[i];
|
|
||||||
bone.ax = bone.x;
|
|
||||||
bone.ay = bone.y;
|
|
||||||
bone.arotation = bone.rotation;
|
|
||||||
bone.ascaleX = bone.scaleX;
|
|
||||||
bone.ascaleY = bone.scaleY;
|
|
||||||
bone.ashearX = bone.shearX;
|
|
||||||
bone.ashearY = bone.shearY;
|
|
||||||
bone.appliedValid = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply the parent bone transform to the root bone. The root bone always inherits scale, rotation and reflection.
|
// Apply the parent bone transform to the root bone. The root bone always inherits scale, rotation and reflection.
|
||||||
Bone rootBone = this.RootBone;
|
Bone rootBone = this.RootBone;
|
||||||
@ -358,10 +345,9 @@ namespace Spine {
|
|||||||
rootBone.d = (pc * lb + pd * ld) * scaleY;
|
rootBone.d = (pc * lb + pd * ld) * scaleY;
|
||||||
|
|
||||||
// Update everything except root bone.
|
// Update everything except root bone.
|
||||||
var updateCache = this.updateCache;
|
var updateCache = this.updateCache.Items;
|
||||||
var updateCacheItems = updateCache.Items;
|
for (int i = 0, n = this.updateCache.Count; i < n; i++) {
|
||||||
for (int i = 0, n = updateCache.Count; i < n; i++) {
|
var updatable = updateCache[i];
|
||||||
var updatable = updateCacheItems[i];
|
|
||||||
if (updatable != rootBone)
|
if (updatable != rootBone)
|
||||||
updatable.Update();
|
updatable.Update();
|
||||||
}
|
}
|
||||||
@ -375,13 +361,13 @@ namespace Spine {
|
|||||||
|
|
||||||
/// <summary>Sets the bones and constraints to their setup pose values.</summary>
|
/// <summary>Sets the bones and constraints to their setup pose values.</summary>
|
||||||
public void SetBonesToSetupPose () {
|
public void SetBonesToSetupPose () {
|
||||||
var bonesItems = this.bones.Items;
|
var bones = this.bones.Items;
|
||||||
for (int i = 0, n = bones.Count; i < n; i++)
|
for (int i = 0, n = this.bones.Count; i < n; i++)
|
||||||
bonesItems[i].SetToSetupPose();
|
bones[i].SetToSetupPose();
|
||||||
|
|
||||||
var ikConstraintsItems = this.ikConstraints.Items;
|
var ikConstraints = this.ikConstraints.Items;
|
||||||
for (int i = 0, n = ikConstraints.Count; i < n; i++) {
|
for (int i = 0, n = this.ikConstraints.Count; i < n; i++) {
|
||||||
IkConstraint constraint = ikConstraintsItems[i];
|
IkConstraint constraint = ikConstraints[i];
|
||||||
constraint.mix = constraint.data.mix;
|
constraint.mix = constraint.data.mix;
|
||||||
constraint.softness = constraint.data.softness;
|
constraint.softness = constraint.data.softness;
|
||||||
constraint.bendDirection = constraint.data.bendDirection;
|
constraint.bendDirection = constraint.data.bendDirection;
|
||||||
@ -389,9 +375,9 @@ namespace Spine {
|
|||||||
constraint.stretch = constraint.data.stretch;
|
constraint.stretch = constraint.data.stretch;
|
||||||
}
|
}
|
||||||
|
|
||||||
var transformConstraintsItems = this.transformConstraints.Items;
|
var transformConstraints = this.transformConstraints.Items;
|
||||||
for (int i = 0, n = transformConstraints.Count; i < n; i++) {
|
for (int i = 0, n = this.transformConstraints.Count; i < n; i++) {
|
||||||
TransformConstraint constraint = transformConstraintsItems[i];
|
TransformConstraint constraint = transformConstraints[i];
|
||||||
TransformConstraintData constraintData = constraint.data;
|
TransformConstraintData constraintData = constraint.data;
|
||||||
constraint.rotateMix = constraintData.rotateMix;
|
constraint.rotateMix = constraintData.rotateMix;
|
||||||
constraint.translateMix = constraintData.translateMix;
|
constraint.translateMix = constraintData.translateMix;
|
||||||
@ -399,9 +385,9 @@ namespace Spine {
|
|||||||
constraint.shearMix = constraintData.shearMix;
|
constraint.shearMix = constraintData.shearMix;
|
||||||
}
|
}
|
||||||
|
|
||||||
var pathConstraintItems = this.pathConstraints.Items;
|
var pathConstraints = this.pathConstraints.Items;
|
||||||
for (int i = 0, n = pathConstraints.Count; i < n; i++) {
|
for (int i = 0, n = this.pathConstraints.Count; i < n; i++) {
|
||||||
PathConstraint constraint = pathConstraintItems[i];
|
PathConstraint constraint = pathConstraints[i];
|
||||||
PathConstraintData constraintData = constraint.data;
|
PathConstraintData constraintData = constraint.data;
|
||||||
constraint.position = constraintData.position;
|
constraint.position = constraintData.position;
|
||||||
constraint.spacing = constraintData.spacing;
|
constraint.spacing = constraintData.spacing;
|
||||||
@ -411,23 +397,21 @@ namespace Spine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void SetSlotsToSetupPose () {
|
public void SetSlotsToSetupPose () {
|
||||||
var slots = this.slots;
|
var slots = this.slots.Items;
|
||||||
var slotsItems = slots.Items;
|
int n = this.slots.Count;
|
||||||
drawOrder.Clear();
|
Array.Copy(slots, 0, drawOrder.Items, 0, n);
|
||||||
for (int i = 0, n = slots.Count; i < n; i++)
|
for (int i = 0; i < n; i++)
|
||||||
drawOrder.Add(slotsItems[i]);
|
slots[i].SetToSetupPose();
|
||||||
|
|
||||||
for (int i = 0, n = slots.Count; i < n; i++)
|
|
||||||
slotsItems[i].SetToSetupPose();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>Finds a bone by comparing each bone's name. It is more efficient to cache the results of this method than to call it
|
||||||
|
/// repeatedly.</summary>
|
||||||
/// <returns>May be null.</returns>
|
/// <returns>May be null.</returns>
|
||||||
public Bone FindBone (string boneName) {
|
public Bone FindBone (string boneName) {
|
||||||
if (boneName == null) throw new ArgumentNullException("boneName", "boneName cannot be null.");
|
if (boneName == null) throw new ArgumentNullException("boneName", "boneName cannot be null.");
|
||||||
var bones = this.bones;
|
var bones = this.bones.Items;
|
||||||
var bonesItems = bones.Items;
|
for (int i = 0, n = this.bones.Count; i < n; i++) {
|
||||||
for (int i = 0, n = bones.Count; i < n; i++) {
|
Bone bone = bones[i];
|
||||||
Bone bone = bonesItems[i];
|
|
||||||
if (bone.data.name == boneName) return bone;
|
if (bone.data.name == boneName) return bone;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
@ -443,13 +427,14 @@ namespace Spine {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>Finds a slot by comparing each slot's name. It is more efficient to cache the results of this method than to call it
|
||||||
|
/// repeatedly.</summary>
|
||||||
/// <returns>May be null.</returns>
|
/// <returns>May be null.</returns>
|
||||||
public Slot FindSlot (string slotName) {
|
public Slot FindSlot (string slotName) {
|
||||||
if (slotName == null) throw new ArgumentNullException("slotName", "slotName cannot be null.");
|
if (slotName == null) throw new ArgumentNullException("slotName", "slotName cannot be null.");
|
||||||
var slots = this.slots;
|
var slots = this.slots.Items;
|
||||||
var slotsItems = slots.Items;
|
for (int i = 0, n = this.slots.Count; i < n; i++) {
|
||||||
for (int i = 0, n = slots.Count; i < n; i++) {
|
Slot slot = slots[i];
|
||||||
Slot slot = slotsItems[i];
|
|
||||||
if (slot.data.name == slotName) return slot;
|
if (slot.data.name == slotName) return slot;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
@ -461,11 +446,11 @@ namespace Spine {
|
|||||||
var slots = this.slots;
|
var slots = this.slots;
|
||||||
var slotsItems = slots.Items;
|
var slotsItems = slots.Items;
|
||||||
for (int i = 0, n = slots.Count; i < n; i++)
|
for (int i = 0, n = slots.Count; i < n; i++)
|
||||||
if (slotsItems[i].data.name.Equals(slotName)) return i;
|
if (slotsItems[i].data.name == slotName) return i;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Sets a skin by name (see SetSkin).</summary>
|
/// <summary>Sets a skin by name (<see cref="SetSkin(Skin)"/>).</summary>
|
||||||
public void SetSkin (string skinName) {
|
public void SetSkin (string skinName) {
|
||||||
Skin foundSkin = data.FindSkin(skinName);
|
Skin foundSkin = data.FindSkin(skinName);
|
||||||
if (foundSkin == null) throw new ArgumentException("Skin not found: " + skinName, "skinName");
|
if (foundSkin == null) throw new ArgumentException("Skin not found: " + skinName, "skinName");
|
||||||
@ -506,7 +491,7 @@ namespace Spine {
|
|||||||
UpdateCache();
|
UpdateCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Finds an attachment by looking in the {@link #skin} and {@link SkeletonData#defaultSkin} using the slot name and attachment name.</summary>
|
/// <summary>Finds an attachment by looking in the <see cref="Skeleton.Skin"/> and <see cref="SkeletonData.DefaultSkin"/> using the slot name and attachment name.</summary>
|
||||||
/// <returns>May be null.</returns>
|
/// <returns>May be null.</returns>
|
||||||
public Attachment GetAttachment (string slotName, string attachmentName) {
|
public Attachment GetAttachment (string slotName, string attachmentName) {
|
||||||
return GetAttachment(data.FindSlotIndex(slotName), attachmentName);
|
return GetAttachment(data.FindSlotIndex(slotName), attachmentName);
|
||||||
@ -543,34 +528,40 @@ namespace Spine {
|
|||||||
throw new Exception("Slot not found: " + slotName);
|
throw new Exception("Slot not found: " + slotName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>Finds an IK constraint by comparing each IK constraint's name. It is more efficient to cache the results of this method
|
||||||
|
/// than to call it repeatedly.</summary>
|
||||||
/// <returns>May be null.</returns>
|
/// <returns>May be null.</returns>
|
||||||
public IkConstraint FindIkConstraint (string constraintName) {
|
public IkConstraint FindIkConstraint (string constraintName) {
|
||||||
if (constraintName == null) throw new ArgumentNullException("constraintName", "constraintName cannot be null.");
|
if (constraintName == null) throw new ArgumentNullException("constraintName", "constraintName cannot be null.");
|
||||||
ExposedList<IkConstraint> ikConstraints = this.ikConstraints;
|
var ikConstraints = this.ikConstraints.Items;
|
||||||
for (int i = 0, n = ikConstraints.Count; i < n; i++) {
|
for (int i = 0, n = this.ikConstraints.Count; i < n; i++) {
|
||||||
IkConstraint ikConstraint = ikConstraints.Items[i];
|
IkConstraint ikConstraint = ikConstraints[i];
|
||||||
if (ikConstraint.data.name == constraintName) return ikConstraint;
|
if (ikConstraint.data.name == constraintName) return ikConstraint;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>Finds a transform constraint by comparing each transform constraint's name. It is more efficient to cache the results of
|
||||||
|
/// this method than to call it repeatedly.</summary>
|
||||||
/// <returns>May be null.</returns>
|
/// <returns>May be null.</returns>
|
||||||
public TransformConstraint FindTransformConstraint (string constraintName) {
|
public TransformConstraint FindTransformConstraint (string constraintName) {
|
||||||
if (constraintName == null) throw new ArgumentNullException("constraintName", "constraintName cannot be null.");
|
if (constraintName == null) throw new ArgumentNullException("constraintName", "constraintName cannot be null.");
|
||||||
ExposedList<TransformConstraint> transformConstraints = this.transformConstraints;
|
var transformConstraints = this.transformConstraints.Items;
|
||||||
for (int i = 0, n = transformConstraints.Count; i < n; i++) {
|
for (int i = 0, n = this.transformConstraints.Count; i < n; i++) {
|
||||||
TransformConstraint transformConstraint = transformConstraints.Items[i];
|
TransformConstraint transformConstraint = transformConstraints[i];
|
||||||
if (transformConstraint.data.Name == constraintName) return transformConstraint;
|
if (transformConstraint.data.Name == constraintName) return transformConstraint;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>Finds a path constraint by comparing each path constraint's name. It is more efficient to cache the results of this method
|
||||||
|
/// than to call it repeatedly.</summary>
|
||||||
/// <returns>May be null.</returns>
|
/// <returns>May be null.</returns>
|
||||||
public PathConstraint FindPathConstraint (string constraintName) {
|
public PathConstraint FindPathConstraint (string constraintName) {
|
||||||
if (constraintName == null) throw new ArgumentNullException("constraintName", "constraintName cannot be null.");
|
if (constraintName == null) throw new ArgumentNullException("constraintName", "constraintName cannot be null.");
|
||||||
ExposedList<PathConstraint> pathConstraints = this.pathConstraints;
|
var pathConstraints = this.pathConstraints.Items;
|
||||||
for (int i = 0, n = pathConstraints.Count; i < n; i++) {
|
for (int i = 0, n = this.pathConstraints.Count; i < n; i++) {
|
||||||
PathConstraint constraint = pathConstraints.Items[i];
|
PathConstraint constraint = pathConstraints[i];
|
||||||
if (constraint.data.Name.Equals(constraintName)) return constraint;
|
if (constraint.data.Name.Equals(constraintName)) return constraint;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
@ -589,10 +580,10 @@ namespace Spine {
|
|||||||
public void GetBounds (out float x, out float y, out float width, out float height, ref float[] vertexBuffer) {
|
public void GetBounds (out float x, out float y, out float width, out float height, ref float[] vertexBuffer) {
|
||||||
float[] temp = vertexBuffer;
|
float[] temp = vertexBuffer;
|
||||||
temp = temp ?? new float[8];
|
temp = temp ?? new float[8];
|
||||||
var drawOrderItems = this.drawOrder.Items;
|
var drawOrder = this.drawOrder.Items;
|
||||||
float minX = int.MaxValue, minY = int.MaxValue, maxX = int.MinValue, maxY = int.MinValue;
|
float minX = int.MaxValue, minY = int.MaxValue, maxX = int.MinValue, maxY = int.MinValue;
|
||||||
for (int i = 0, n = drawOrderItems.Length; i < n; i++) {
|
for (int i = 0, n = this.drawOrder.Count; i < n; i++) {
|
||||||
Slot slot = drawOrderItems[i];
|
Slot slot = drawOrder[i];
|
||||||
if (!slot.bone.active) continue;
|
if (!slot.bone.active) continue;
|
||||||
int verticesLength = 0;
|
int verticesLength = 0;
|
||||||
float[] vertices = null;
|
float[] vertices = null;
|
||||||
|
|||||||
@ -34,6 +34,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Runtime.Serialization;
|
||||||
|
|
||||||
#if WINDOWS_STOREAPP
|
#if WINDOWS_STOREAPP
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@ -41,7 +42,7 @@ using Windows.Storage;
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace Spine {
|
namespace Spine {
|
||||||
public class SkeletonBinary {
|
public class SkeletonBinary : SkeletonLoader {
|
||||||
public const int BONE_ROTATE = 0;
|
public const int BONE_ROTATE = 0;
|
||||||
public const int BONE_TRANSLATE = 1;
|
public const int BONE_TRANSLATE = 1;
|
||||||
public const int BONE_SCALE = 2;
|
public const int BONE_SCALE = 2;
|
||||||
@ -59,22 +60,15 @@ namespace Spine {
|
|||||||
public const int CURVE_STEPPED = 1;
|
public const int CURVE_STEPPED = 1;
|
||||||
public const int CURVE_BEZIER = 2;
|
public const int CURVE_BEZIER = 2;
|
||||||
|
|
||||||
public float Scale { get; set; }
|
public SkeletonBinary (AttachmentLoader attachmentLoader)
|
||||||
|
:base(attachmentLoader) {
|
||||||
private AttachmentLoader attachmentLoader;
|
}
|
||||||
private List<SkeletonJson.LinkedMesh> linkedMeshes = new List<SkeletonJson.LinkedMesh>();
|
|
||||||
|
|
||||||
public SkeletonBinary (params Atlas[] atlasArray)
|
public SkeletonBinary (params Atlas[] atlasArray)
|
||||||
: this(new AtlasAttachmentLoader(atlasArray)) {
|
: base(atlasArray) {
|
||||||
}
|
}
|
||||||
|
|
||||||
public SkeletonBinary (AttachmentLoader attachmentLoader) {
|
#if !ISUNITY && WINDOWS_STOREAPP
|
||||||
if (attachmentLoader == null) throw new ArgumentNullException("attachmentLoader");
|
|
||||||
this.attachmentLoader = attachmentLoader;
|
|
||||||
Scale = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if !ISUNITY && WINDOWS_STOREAPP
|
|
||||||
private async Task<SkeletonData> ReadFile(string path) {
|
private async Task<SkeletonData> ReadFile(string path) {
|
||||||
var folder = Windows.ApplicationModel.Package.Current.InstalledLocation;
|
var folder = Windows.ApplicationModel.Package.Current.InstalledLocation;
|
||||||
using (var input = new BufferedStream(await folder.GetFileAsync(path).AsTask().ConfigureAwait(false))) {
|
using (var input = new BufferedStream(await folder.GetFileAsync(path).AsTask().ConfigureAwait(false))) {
|
||||||
@ -84,11 +78,11 @@ namespace Spine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public SkeletonData ReadSkeletonData (String path) {
|
public override SkeletonData ReadSkeletonData (string path) {
|
||||||
return this.ReadFile(path).Result;
|
return this.ReadFile(path).Result;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
public SkeletonData ReadSkeletonData (String path) {
|
public override SkeletonData ReadSkeletonData (string path) {
|
||||||
#if WINDOWS_PHONE
|
#if WINDOWS_PHONE
|
||||||
using (var input = new BufferedStream(Microsoft.Xna.Framework.TitleContainer.OpenStream(path))) {
|
using (var input = new BufferedStream(Microsoft.Xna.Framework.TitleContainer.OpenStream(path))) {
|
||||||
#else
|
#else
|
||||||
@ -119,13 +113,13 @@ namespace Spine {
|
|||||||
|
|
||||||
public SkeletonData ReadSkeletonData (Stream file) {
|
public SkeletonData ReadSkeletonData (Stream file) {
|
||||||
if (file == null) throw new ArgumentNullException("file");
|
if (file == null) throw new ArgumentNullException("file");
|
||||||
float scale = Scale;
|
float scale = this.scale;
|
||||||
|
|
||||||
var skeletonData = new SkeletonData();
|
var skeletonData = new SkeletonData();
|
||||||
SkeletonInput input = new SkeletonInput(file);
|
SkeletonInput input = new SkeletonInput(file);
|
||||||
|
|
||||||
skeletonData.hash = input.ReadString();
|
long hash = input.ReadLong();
|
||||||
if (skeletonData.hash.Length == 0) skeletonData.hash = null;
|
skeletonData.hash = hash == 0 ? null : hash.ToString();
|
||||||
skeletonData.version = input.ReadString();
|
skeletonData.version = input.ReadString();
|
||||||
if (skeletonData.version.Length == 0) skeletonData.version = null;
|
if (skeletonData.version.Length == 0) skeletonData.version = null;
|
||||||
if ("3.8.75" == skeletonData.version)
|
if ("3.8.75" == skeletonData.version)
|
||||||
@ -151,16 +145,15 @@ namespace Spine {
|
|||||||
Object[] o;
|
Object[] o;
|
||||||
|
|
||||||
// Strings.
|
// Strings.
|
||||||
input.strings = new ExposedList<string>(n = input.ReadInt(true));
|
o = input.strings = new String[n = input.ReadInt(true)];
|
||||||
o = input.strings.Resize(n).Items;
|
|
||||||
for (int i = 0; i < n; i++)
|
for (int i = 0; i < n; i++)
|
||||||
o[i] = input.ReadString();
|
o[i] = input.ReadString();
|
||||||
|
|
||||||
// Bones.
|
// Bones.
|
||||||
o = skeletonData.bones.Resize(n = input.ReadInt(true)).Items;
|
var bones = skeletonData.bones.Resize(n = input.ReadInt(true)).Items;
|
||||||
for (int i = 0; i < n; i++) {
|
for (int i = 0; i < n; i++) {
|
||||||
String name = input.ReadString();
|
String name = input.ReadString();
|
||||||
BoneData parent = i == 0 ? null : skeletonData.bones.Items[input.ReadInt(true)];
|
BoneData parent = i == 0 ? null : bones[input.ReadInt(true)];
|
||||||
BoneData data = new BoneData(i, name, parent);
|
BoneData data = new BoneData(i, name, parent);
|
||||||
data.rotation = input.ReadFloat();
|
data.rotation = input.ReadFloat();
|
||||||
data.x = input.ReadFloat() * scale;
|
data.x = input.ReadFloat() * scale;
|
||||||
@ -169,18 +162,18 @@ namespace Spine {
|
|||||||
data.scaleY = input.ReadFloat();
|
data.scaleY = input.ReadFloat();
|
||||||
data.shearX = input.ReadFloat();
|
data.shearX = input.ReadFloat();
|
||||||
data.shearY = input.ReadFloat();
|
data.shearY = input.ReadFloat();
|
||||||
data.length = input.ReadFloat() * scale;
|
data.Length = input.ReadFloat() * scale;
|
||||||
data.transformMode = TransformModeValues[input.ReadInt(true)];
|
data.transformMode = TransformModeValues[input.ReadInt(true)];
|
||||||
data.skinRequired = input.ReadBoolean();
|
data.skinRequired = input.ReadBoolean();
|
||||||
if (nonessential) input.ReadInt(); // Skip bone color.
|
if (nonessential) input.ReadInt(); // Skip bone color.
|
||||||
o[i] = data;
|
bones[i] = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Slots.
|
// Slots.
|
||||||
o = skeletonData.slots.Resize(n = input.ReadInt(true)).Items;
|
var slots = skeletonData.slots.Resize(n = input.ReadInt(true)).Items;
|
||||||
for (int i = 0; i < n; i++) {
|
for (int i = 0; i < n; i++) {
|
||||||
String slotName = input.ReadString();
|
String slotName = input.ReadString();
|
||||||
BoneData boneData = skeletonData.bones.Items[input.ReadInt(true)];
|
BoneData boneData = bones[input.ReadInt(true)];
|
||||||
SlotData slotData = new SlotData(i, slotName, boneData);
|
SlotData slotData = new SlotData(i, slotName, boneData);
|
||||||
int color = input.ReadInt();
|
int color = input.ReadInt();
|
||||||
slotData.r = ((color & 0xff000000) >> 24) / 255f;
|
slotData.r = ((color & 0xff000000) >> 24) / 255f;
|
||||||
@ -198,7 +191,7 @@ namespace Spine {
|
|||||||
|
|
||||||
slotData.attachmentName = input.ReadStringRef();
|
slotData.attachmentName = input.ReadStringRef();
|
||||||
slotData.blendMode = (BlendMode)input.ReadInt(true);
|
slotData.blendMode = (BlendMode)input.ReadInt(true);
|
||||||
o[i] = slotData;
|
slots[i] = slotData;
|
||||||
}
|
}
|
||||||
|
|
||||||
// IK constraints.
|
// IK constraints.
|
||||||
@ -207,10 +200,10 @@ namespace Spine {
|
|||||||
IkConstraintData data = new IkConstraintData(input.ReadString());
|
IkConstraintData data = new IkConstraintData(input.ReadString());
|
||||||
data.order = input.ReadInt(true);
|
data.order = input.ReadInt(true);
|
||||||
data.skinRequired = input.ReadBoolean();
|
data.skinRequired = input.ReadBoolean();
|
||||||
Object[] bones = data.bones.Resize(nn = input.ReadInt(true)).Items;
|
var constraintBones = data.bones.Resize(nn = input.ReadInt(true)).Items;
|
||||||
for (int ii = 0; ii < nn; ii++)
|
for (int ii = 0; ii < nn; ii++)
|
||||||
bones[ii] = skeletonData.bones.Items[input.ReadInt(true)];
|
constraintBones[ii] = bones[input.ReadInt(true)];
|
||||||
data.target = skeletonData.bones.Items[input.ReadInt(true)];
|
data.target = bones[input.ReadInt(true)];
|
||||||
data.mix = input.ReadFloat();
|
data.mix = input.ReadFloat();
|
||||||
data.softness = input.ReadFloat() * scale;
|
data.softness = input.ReadFloat() * scale;
|
||||||
data.bendDirection = input.ReadSByte();
|
data.bendDirection = input.ReadSByte();
|
||||||
@ -226,10 +219,10 @@ namespace Spine {
|
|||||||
TransformConstraintData data = new TransformConstraintData(input.ReadString());
|
TransformConstraintData data = new TransformConstraintData(input.ReadString());
|
||||||
data.order = input.ReadInt(true);
|
data.order = input.ReadInt(true);
|
||||||
data.skinRequired = input.ReadBoolean();
|
data.skinRequired = input.ReadBoolean();
|
||||||
Object[] bones = data.bones.Resize(nn = input.ReadInt(true)).Items;
|
var constraintBones = data.bones.Resize(nn = input.ReadInt(true)).Items;
|
||||||
for (int ii = 0; ii < nn; ii++)
|
for (int ii = 0; ii < nn; ii++)
|
||||||
bones[ii] = skeletonData.bones.Items[input.ReadInt(true)];
|
constraintBones[ii] = bones[input.ReadInt(true)];
|
||||||
data.target = skeletonData.bones.Items[input.ReadInt(true)];
|
data.target = bones[input.ReadInt(true)];
|
||||||
data.local = input.ReadBoolean();
|
data.local = input.ReadBoolean();
|
||||||
data.relative = input.ReadBoolean();
|
data.relative = input.ReadBoolean();
|
||||||
data.offsetRotation = input.ReadFloat();
|
data.offsetRotation = input.ReadFloat();
|
||||||
@ -251,10 +244,10 @@ namespace Spine {
|
|||||||
PathConstraintData data = new PathConstraintData(input.ReadString());
|
PathConstraintData data = new PathConstraintData(input.ReadString());
|
||||||
data.order = input.ReadInt(true);
|
data.order = input.ReadInt(true);
|
||||||
data.skinRequired = input.ReadBoolean();
|
data.skinRequired = input.ReadBoolean();
|
||||||
Object[] bones = data.bones.Resize(nn = input.ReadInt(true)).Items;
|
Object[] constraintBones = data.bones.Resize(nn = input.ReadInt(true)).Items;
|
||||||
for (int ii = 0; ii < nn; ii++)
|
for (int ii = 0; ii < nn; ii++)
|
||||||
bones[ii] = skeletonData.bones.Items[input.ReadInt(true)];
|
constraintBones[ii] = bones[input.ReadInt(true)];
|
||||||
data.target = skeletonData.slots.Items[input.ReadInt(true)];
|
data.target = slots[input.ReadInt(true)];
|
||||||
data.positionMode = (PositionMode)Enum.GetValues(typeof(PositionMode)).GetValue(input.ReadInt(true));
|
data.positionMode = (PositionMode)Enum.GetValues(typeof(PositionMode)).GetValue(input.ReadInt(true));
|
||||||
data.spacingMode = (SpacingMode)Enum.GetValues(typeof(SpacingMode)).GetValue(input.ReadInt(true));
|
data.spacingMode = (SpacingMode)Enum.GetValues(typeof(SpacingMode)).GetValue(input.ReadInt(true));
|
||||||
data.rotateMode = (RotateMode)Enum.GetValues(typeof(RotateMode)).GetValue(input.ReadInt(true));
|
data.rotateMode = (RotateMode)Enum.GetValues(typeof(RotateMode)).GetValue(input.ReadInt(true));
|
||||||
@ -286,7 +279,7 @@ namespace Spine {
|
|||||||
// Linked meshes.
|
// Linked meshes.
|
||||||
n = linkedMeshes.Count;
|
n = linkedMeshes.Count;
|
||||||
for (int i = 0; i < n; i++) {
|
for (int i = 0; i < n; i++) {
|
||||||
SkeletonJson.LinkedMesh linkedMesh = linkedMeshes[i];
|
LinkedMesh linkedMesh = linkedMeshes[i];
|
||||||
Skin skin = linkedMesh.skin == null ? skeletonData.DefaultSkin : skeletonData.FindSkin(linkedMesh.skin);
|
Skin skin = linkedMesh.skin == null ? skeletonData.DefaultSkin : skeletonData.FindSkin(linkedMesh.skin);
|
||||||
if (skin == null) throw new Exception("Skin not found: " + linkedMesh.skin);
|
if (skin == null) throw new Exception("Skin not found: " + linkedMesh.skin);
|
||||||
Attachment parent = skin.GetAttachment(linkedMesh.slotIndex, linkedMesh.parent);
|
Attachment parent = skin.GetAttachment(linkedMesh.slotIndex, linkedMesh.parent);
|
||||||
@ -334,16 +327,21 @@ namespace Spine {
|
|||||||
} else {
|
} else {
|
||||||
skin = new Skin(input.ReadStringRef());
|
skin = new Skin(input.ReadStringRef());
|
||||||
Object[] bones = skin.bones.Resize(input.ReadInt(true)).Items;
|
Object[] bones = skin.bones.Resize(input.ReadInt(true)).Items;
|
||||||
|
var bonesItems = skeletonData.bones.Items;
|
||||||
for (int i = 0, n = skin.bones.Count; i < n; i++)
|
for (int i = 0, n = skin.bones.Count; i < n; i++)
|
||||||
bones[i] = skeletonData.bones.Items[input.ReadInt(true)];
|
bones[i] = bonesItems[input.ReadInt(true)];
|
||||||
|
|
||||||
|
var ikConstraintsItems = skeletonData.ikConstraints.Items;
|
||||||
for (int i = 0, n = input.ReadInt(true); i < n; i++)
|
for (int i = 0, n = input.ReadInt(true); i < n; i++)
|
||||||
skin.constraints.Add(skeletonData.ikConstraints.Items[input.ReadInt(true)]);
|
skin.constraints.Add(ikConstraintsItems[input.ReadInt(true)]);
|
||||||
|
var transformConstraintsItems = skeletonData.transformConstraints.Items;
|
||||||
for (int i = 0, n = input.ReadInt(true); i < n; i++)
|
for (int i = 0, n = input.ReadInt(true); i < n; i++)
|
||||||
skin.constraints.Add(skeletonData.transformConstraints.Items[input.ReadInt(true)]);
|
skin.constraints.Add(transformConstraintsItems[input.ReadInt(true)]);
|
||||||
|
var pathConstraintsItems = skeletonData.pathConstraints.Items;
|
||||||
for (int i = 0, n = input.ReadInt(true); i < n; i++)
|
for (int i = 0, n = input.ReadInt(true); i < n; i++)
|
||||||
skin.constraints.Add(skeletonData.pathConstraints.Items[input.ReadInt(true)]);
|
skin.constraints.Add(pathConstraintsItems[input.ReadInt(true)]);
|
||||||
skin.constraints.TrimExcess();
|
skin.constraints.TrimExcess();
|
||||||
|
|
||||||
slotCount = input.ReadInt(true);
|
slotCount = input.ReadInt(true);
|
||||||
}
|
}
|
||||||
for (int i = 0; i < slotCount; i++) {
|
for (int i = 0; i < slotCount; i++) {
|
||||||
@ -359,14 +357,12 @@ namespace Spine {
|
|||||||
|
|
||||||
private Attachment ReadAttachment (SkeletonInput input, SkeletonData skeletonData, Skin skin, int slotIndex,
|
private Attachment ReadAttachment (SkeletonInput input, SkeletonData skeletonData, Skin skin, int slotIndex,
|
||||||
String attachmentName, bool nonessential) {
|
String attachmentName, bool nonessential) {
|
||||||
|
float scale = this.scale;
|
||||||
float scale = Scale;
|
|
||||||
|
|
||||||
String name = input.ReadStringRef();
|
String name = input.ReadStringRef();
|
||||||
if (name == null) name = attachmentName;
|
if (name == null) name = attachmentName;
|
||||||
|
|
||||||
AttachmentType type = (AttachmentType)input.ReadByte();
|
switch ((AttachmentType)input.ReadByte()) {
|
||||||
switch (type) {
|
|
||||||
case AttachmentType.Region: {
|
case AttachmentType.Region: {
|
||||||
String path = input.ReadStringRef();
|
String path = input.ReadStringRef();
|
||||||
float rotation = input.ReadFloat();
|
float rotation = input.ReadFloat();
|
||||||
@ -529,7 +525,7 @@ namespace Spine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Vertices ReadVertices (SkeletonInput input, int vertexCount) {
|
private Vertices ReadVertices (SkeletonInput input, int vertexCount) {
|
||||||
float scale = Scale;
|
float scale = this.scale;
|
||||||
int verticesLength = vertexCount << 1;
|
int verticesLength = vertexCount << 1;
|
||||||
Vertices vertices = new Vertices();
|
Vertices vertices = new Vertices();
|
||||||
if(!input.ReadBoolean()) {
|
if(!input.ReadBoolean()) {
|
||||||
@ -574,66 +570,97 @@ namespace Spine {
|
|||||||
return array;
|
return array;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <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) {
|
private Animation ReadAnimation (String name, SkeletonInput input, SkeletonData skeletonData) {
|
||||||
var timelines = new ExposedList<Timeline>(32);
|
var timelines = new ExposedList<Timeline>(input.ReadInt(true));
|
||||||
float scale = Scale;
|
float scale = this.scale;
|
||||||
float duration = 0;
|
|
||||||
|
|
||||||
// Slot timelines.
|
// Slot timelines.
|
||||||
for (int i = 0, n = input.ReadInt(true); i < n; i++) {
|
for (int i = 0, n = input.ReadInt(true); i < n; i++) {
|
||||||
int slotIndex = input.ReadInt(true);
|
int slotIndex = input.ReadInt(true);
|
||||||
for (int ii = 0, nn = input.ReadInt(true); ii < nn; ii++) {
|
for (int ii = 0, nn = input.ReadInt(true); ii < nn; ii++) {
|
||||||
int timelineType = input.ReadByte();
|
int timelineType = input.ReadByte(), frameCount = input.ReadInt(true), frameLast = frameCount - 1;
|
||||||
int frameCount = input.ReadInt(true);
|
|
||||||
switch (timelineType) {
|
switch (timelineType) {
|
||||||
case SLOT_ATTACHMENT: {
|
case SLOT_ATTACHMENT: {
|
||||||
AttachmentTimeline timeline = new AttachmentTimeline(frameCount);
|
AttachmentTimeline timeline = new AttachmentTimeline(frameCount, slotIndex);
|
||||||
timeline.slotIndex = slotIndex;
|
for (int frame = 0; frame < frameCount; frame++)
|
||||||
for (int frameIndex = 0; frameIndex < frameCount; frameIndex++)
|
timeline.SetFrame(frame, input.ReadFloat(), input.ReadStringRef());
|
||||||
timeline.SetFrame(frameIndex, input.ReadFloat(), input.ReadStringRef());
|
timelines.Add(timeline);
|
||||||
timelines.Add(timeline);
|
break;
|
||||||
duration = Math.Max(duration, timeline.frames[frameCount - 1]);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case SLOT_COLOR: {
|
|
||||||
ColorTimeline timeline = new ColorTimeline(frameCount);
|
|
||||||
timeline.slotIndex = slotIndex;
|
|
||||||
for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) {
|
|
||||||
float time = input.ReadFloat();
|
|
||||||
int color = input.ReadInt();
|
|
||||||
float r = ((color & 0xff000000) >> 24) / 255f;
|
|
||||||
float g = ((color & 0x00ff0000) >> 16) / 255f;
|
|
||||||
float b = ((color & 0x0000ff00) >> 8) / 255f;
|
|
||||||
float a = ((color & 0x000000ff)) / 255f;
|
|
||||||
timeline.SetFrame(frameIndex, time, r, g, b, a);
|
|
||||||
if (frameIndex < frameCount - 1) ReadCurve(input, frameIndex, timeline);
|
|
||||||
}
|
}
|
||||||
timelines.Add(timeline);
|
case SLOT_COLOR: {
|
||||||
duration = Math.Max(duration, timeline.frames[(frameCount - 1) * ColorTimeline.ENTRIES]);
|
ColorTimeline timeline = new ColorTimeline(frameCount, input.ReadInt(true), slotIndex);
|
||||||
break;
|
|
||||||
}
|
|
||||||
case SLOT_TWO_COLOR: {
|
|
||||||
TwoColorTimeline timeline = new TwoColorTimeline(frameCount);
|
|
||||||
timeline.slotIndex = slotIndex;
|
|
||||||
for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) {
|
|
||||||
float time = input.ReadFloat();
|
float time = input.ReadFloat();
|
||||||
int color = input.ReadInt();
|
float r = input.Read() / 255f, g = input.Read() / 255f;
|
||||||
float r = ((color & 0xff000000) >> 24) / 255f;
|
float b = input.Read() / 255f, a = input.Read() / 255f;
|
||||||
float g = ((color & 0x00ff0000) >> 16) / 255f;
|
for (int frame = 0, bezier = 0; ; frame++) {
|
||||||
float b = ((color & 0x0000ff00) >> 8) / 255f;
|
timeline.SetFrame(frame, time, r, g, b, a);
|
||||||
float a = ((color & 0x000000ff)) / 255f;
|
if (frame == frameLast) break;
|
||||||
int color2 = input.ReadInt(); // 0x00rrggbb
|
float time2 = input.ReadFloat();
|
||||||
float r2 = ((color2 & 0x00ff0000) >> 16) / 255f;
|
float r2 = input.Read() / 255f, g2 = input.Read() / 255f;
|
||||||
float g2 = ((color2 & 0x0000ff00) >> 8) / 255f;
|
float b2 = input.Read() / 255f, a2 = input.Read() / 255f;
|
||||||
float b2 = ((color2 & 0x000000ff)) / 255f;
|
switch (input.ReadByte()) {
|
||||||
|
case CURVE_STEPPED:
|
||||||
timeline.SetFrame(frameIndex, time, r, g, b, a, r2, g2, b2);
|
timeline.SetStepped(frame);
|
||||||
if (frameIndex < frameCount - 1) ReadCurve(input, frameIndex, timeline);
|
break;
|
||||||
|
case CURVE_BEZIER:
|
||||||
|
SetBezier(input, timeline, bezier++, frame, 0, time, time2, r, r2, 1);
|
||||||
|
SetBezier(input, timeline, bezier++, frame, 1, time, time2, g, g2, 1);
|
||||||
|
SetBezier(input, timeline, bezier++, frame, 2, time, time2, b, b2, 1);
|
||||||
|
SetBezier(input, timeline, bezier++, frame, 3, time, time2, a, a2, 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
time = time2;
|
||||||
|
r = r2;
|
||||||
|
g = g2;
|
||||||
|
b = b2;
|
||||||
|
a = a2;
|
||||||
|
}
|
||||||
|
timelines.Add(timeline);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case SLOT_TWO_COLOR: {
|
||||||
|
TwoColorTimeline timeline = new TwoColorTimeline(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;
|
||||||
|
float r2 = input.Read() / 255f, g2 = input.Read() / 255f;
|
||||||
|
float b2 = input.Read() / 255f;
|
||||||
|
for (int frame = 0, bezier = 0; ; frame++) {
|
||||||
|
timeline.SetFrame(frame, time, r, g, b, a, r2, g2, b2);
|
||||||
|
if (frame == frameLast) break;
|
||||||
|
float time2 = input.ReadFloat();
|
||||||
|
float nr = input.Read() / 255f, ng = input.Read() / 255f;
|
||||||
|
float nb = input.Read() / 255f, na = input.Read() / 255f;
|
||||||
|
float nr2 = input.Read() / 255f, ng2 = input.Read() / 255f;
|
||||||
|
float nb2 = input.Read() / 255f;
|
||||||
|
switch (input.ReadByte()) {
|
||||||
|
case CURVE_STEPPED:
|
||||||
|
timeline.SetStepped(frame);
|
||||||
|
break;
|
||||||
|
case CURVE_BEZIER:
|
||||||
|
SetBezier(input, timeline, bezier++, frame, 0, time, time2, r, nr, 1);
|
||||||
|
SetBezier(input, timeline, bezier++, frame, 1, time, time2, g, ng, 1);
|
||||||
|
SetBezier(input, timeline, bezier++, frame, 2, time, time2, b, nb, 1);
|
||||||
|
SetBezier(input, timeline, bezier++, frame, 3, time, time2, a, na, 1);
|
||||||
|
SetBezier(input, timeline, bezier++, frame, 4, time, time2, r2, nr2, 1);
|
||||||
|
SetBezier(input, timeline, bezier++, frame, 5, time, time2, g2, ng2, 1);
|
||||||
|
SetBezier(input, timeline, bezier++, frame, 6, time, time2, b2, nb2, 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
time = time2;
|
||||||
|
r = nr;
|
||||||
|
g = ng;
|
||||||
|
b = nb;
|
||||||
|
a = na;
|
||||||
|
r2 = nr2;
|
||||||
|
g2 = ng2;
|
||||||
|
b2 = nb2;
|
||||||
|
}
|
||||||
|
timelines.Add(timeline);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
timelines.Add(timeline);
|
|
||||||
duration = Math.Max(duration, timeline.frames[(frameCount - 1) * TwoColorTimeline.ENTRIES]);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -642,76 +669,78 @@ namespace Spine {
|
|||||||
for (int i = 0, n = input.ReadInt(true); i < n; i++) {
|
for (int i = 0, n = input.ReadInt(true); i < n; i++) {
|
||||||
int boneIndex = input.ReadInt(true);
|
int boneIndex = input.ReadInt(true);
|
||||||
for (int ii = 0, nn = input.ReadInt(true); ii < nn; ii++) {
|
for (int ii = 0, nn = input.ReadInt(true); ii < nn; ii++) {
|
||||||
int timelineType = input.ReadByte();
|
switch (input.ReadByte()) {
|
||||||
int frameCount = input.ReadInt(true);
|
case BONE_ROTATE:
|
||||||
switch (timelineType) {
|
timelines.Add(ReadTimeline(input, new RotateTimeline(input.ReadInt(true), input.ReadInt(true), boneIndex), 1));
|
||||||
case BONE_ROTATE: {
|
|
||||||
RotateTimeline timeline = new RotateTimeline(frameCount);
|
|
||||||
timeline.boneIndex = boneIndex;
|
|
||||||
for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) {
|
|
||||||
timeline.SetFrame(frameIndex, input.ReadFloat(), input.ReadFloat());
|
|
||||||
if (frameIndex < frameCount - 1) ReadCurve(input, frameIndex, timeline);
|
|
||||||
}
|
|
||||||
timelines.Add(timeline);
|
|
||||||
duration = Math.Max(duration, timeline.frames[(frameCount - 1) * RotateTimeline.ENTRIES]);
|
|
||||||
break;
|
break;
|
||||||
}
|
case BONE_TRANSLATE:
|
||||||
case BONE_TRANSLATE:
|
timelines
|
||||||
case BONE_SCALE:
|
.Add(ReadTimeline(input, new TranslateTimeline(input.ReadInt(true), input.ReadInt(true), boneIndex), scale));
|
||||||
case BONE_SHEAR: {
|
break;
|
||||||
TranslateTimeline timeline;
|
case BONE_SCALE:
|
||||||
float timelineScale = 1;
|
timelines.Add(ReadTimeline(input, new ScaleTimeline(input.ReadInt(true), input.ReadInt(true), boneIndex), 1));
|
||||||
if (timelineType == BONE_SCALE)
|
break;
|
||||||
timeline = new ScaleTimeline(frameCount);
|
case BONE_SHEAR:
|
||||||
else if (timelineType == BONE_SHEAR)
|
timelines.Add(ReadTimeline(input, new ShearTimeline(input.ReadInt(true), input.ReadInt(true), boneIndex), 1));
|
||||||
timeline = new ShearTimeline(frameCount);
|
|
||||||
else {
|
|
||||||
timeline = new TranslateTimeline(frameCount);
|
|
||||||
timelineScale = scale;
|
|
||||||
}
|
|
||||||
timeline.boneIndex = boneIndex;
|
|
||||||
for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) {
|
|
||||||
timeline.SetFrame(frameIndex, input.ReadFloat(), input.ReadFloat() * timelineScale,
|
|
||||||
input.ReadFloat() * timelineScale);
|
|
||||||
if (frameIndex < frameCount - 1) ReadCurve(input, frameIndex, timeline);
|
|
||||||
}
|
|
||||||
timelines.Add(timeline);
|
|
||||||
duration = Math.Max(duration, timeline.frames[(frameCount - 1) * TranslateTimeline.ENTRIES]);
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// IK constraint timelines.
|
// IK constraint timelines.
|
||||||
for (int i = 0, n = input.ReadInt(true); i < n; i++) {
|
for (int i = 0, n = input.ReadInt(true); i < n; i++) {
|
||||||
int index = input.ReadInt(true);
|
int index = input.ReadInt(true), frameCount = input.ReadInt(true), frameLast = frameCount - 1;
|
||||||
int frameCount = input.ReadInt(true);
|
IkConstraintTimeline timeline = new IkConstraintTimeline(frameCount, input.ReadInt(true), index);
|
||||||
IkConstraintTimeline timeline = new IkConstraintTimeline(frameCount) {
|
float time = input.ReadFloat(), mix = input.ReadFloat(), softness = input.ReadFloat() * scale;
|
||||||
ikConstraintIndex = index
|
for (int frame = 0, bezier = 0; ; frame++) {
|
||||||
};
|
timeline.SetFrame(frame, time, mix, softness, input.ReadSByte(), input.ReadBoolean(), input.ReadBoolean());
|
||||||
for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) {
|
if (frame == frameLast) break;
|
||||||
timeline.SetFrame(frameIndex, input.ReadFloat(), input.ReadFloat(), input.ReadFloat() * scale, input.ReadSByte(), input.ReadBoolean(),
|
float time2 = input.ReadFloat(), mix2 = input.ReadFloat(), softness2 = input.ReadFloat() * scale;
|
||||||
input.ReadBoolean());
|
switch (input.ReadByte()) {
|
||||||
if (frameIndex < frameCount - 1) ReadCurve(input, frameIndex, timeline);
|
case CURVE_STEPPED:
|
||||||
|
timeline.SetStepped(frame);
|
||||||
|
break;
|
||||||
|
case CURVE_BEZIER:
|
||||||
|
SetBezier(input, timeline, bezier++, frame, 0, time, time2, mix, mix2, 1);
|
||||||
|
SetBezier(input, timeline, bezier++, frame, 1, time, time2, softness, softness2, scale);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
time = time2;
|
||||||
|
mix = mix2;
|
||||||
|
softness = softness2;
|
||||||
}
|
}
|
||||||
timelines.Add(timeline);
|
timelines.Add(timeline);
|
||||||
duration = Math.Max(duration, timeline.frames[(frameCount - 1) * IkConstraintTimeline.ENTRIES]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transform constraint timelines.
|
// Transform constraint timelines.
|
||||||
for (int i = 0, n = input.ReadInt(true); i < n; i++) {
|
for (int i = 0, n = input.ReadInt(true); i < n; i++) {
|
||||||
int index = input.ReadInt(true);
|
int index = input.ReadInt(true), frameCount = input.ReadInt(true), frameLast = frameCount - 1;
|
||||||
int frameCount = input.ReadInt(true);
|
TransformConstraintTimeline timeline = new TransformConstraintTimeline(frameCount, input.ReadInt(true), index);
|
||||||
TransformConstraintTimeline timeline = new TransformConstraintTimeline(frameCount);
|
float time = input.ReadFloat(), rotateMix = input.ReadFloat(), translateMix = input.ReadFloat(),
|
||||||
timeline.transformConstraintIndex = index;
|
scaleMix = input.ReadFloat(), shearMix = input.ReadFloat();
|
||||||
for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) {
|
for (int frame = 0, bezier = 0; ; frame++) {
|
||||||
timeline.SetFrame(frameIndex, input.ReadFloat(), input.ReadFloat(), input.ReadFloat(), input.ReadFloat(),
|
timeline.SetFrame(frame, time, rotateMix, translateMix, scaleMix, shearMix);
|
||||||
input.ReadFloat());
|
if (frame == frameLast) break;
|
||||||
if (frameIndex < frameCount - 1) ReadCurve(input, frameIndex, timeline);
|
float time2 = input.ReadFloat(), rotateMix2 = input.ReadFloat(), translateMix2 = input.ReadFloat(),
|
||||||
|
scaleMix2 = input.ReadFloat(), shearMix2 = input.ReadFloat();
|
||||||
|
switch (input.ReadByte()) {
|
||||||
|
case CURVE_STEPPED:
|
||||||
|
timeline.SetStepped(frame);
|
||||||
|
break;
|
||||||
|
case CURVE_BEZIER:
|
||||||
|
SetBezier(input, timeline, bezier++, frame, 0, time, time2, rotateMix, rotateMix2, 1);
|
||||||
|
SetBezier(input, timeline, bezier++, frame, 1, time, time2, translateMix, translateMix2, 1);
|
||||||
|
SetBezier(input, timeline, bezier++, frame, 2, time, time2, scaleMix, scaleMix2, 1);
|
||||||
|
SetBezier(input, timeline, bezier++, frame, 3, time, time2, shearMix, shearMix2, 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
time = time2;
|
||||||
|
rotateMix = rotateMix2;
|
||||||
|
translateMix = translateMix2;
|
||||||
|
scaleMix = scaleMix2;
|
||||||
|
shearMix = shearMix2;
|
||||||
}
|
}
|
||||||
timelines.Add(timeline);
|
timelines.Add(timeline);
|
||||||
duration = Math.Max(duration, timeline.frames[(frameCount - 1) * TransformConstraintTimeline.ENTRIES]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Path constraint timelines.
|
// Path constraint timelines.
|
||||||
@ -719,40 +748,21 @@ namespace Spine {
|
|||||||
int index = input.ReadInt(true);
|
int index = input.ReadInt(true);
|
||||||
PathConstraintData data = skeletonData.pathConstraints.Items[index];
|
PathConstraintData data = skeletonData.pathConstraints.Items[index];
|
||||||
for (int ii = 0, nn = input.ReadInt(true); ii < nn; ii++) {
|
for (int ii = 0, nn = input.ReadInt(true); ii < nn; ii++) {
|
||||||
int timelineType = input.ReadSByte();
|
switch (input.ReadByte()) {
|
||||||
int frameCount = input.ReadInt(true);
|
|
||||||
switch(timelineType) {
|
|
||||||
case PATH_POSITION:
|
case PATH_POSITION:
|
||||||
case PATH_SPACING: {
|
timelines
|
||||||
PathConstraintPositionTimeline timeline;
|
.Add(ReadTimeline(input, new PathConstraintPositionTimeline(input.ReadInt(true), input.ReadInt(true), index),
|
||||||
float timelineScale = 1;
|
data.positionMode == PositionMode.Fixed ? scale : 1));
|
||||||
if (timelineType == PATH_SPACING) {
|
break;
|
||||||
timeline = new PathConstraintSpacingTimeline(frameCount);
|
case PATH_SPACING:
|
||||||
if (data.spacingMode == SpacingMode.Length || data.spacingMode == SpacingMode.Fixed) timelineScale = scale;
|
timelines
|
||||||
} else {
|
.Add(ReadTimeline(input, new PathConstraintSpacingTimeline(input.ReadInt(true), input.ReadInt(true), index),
|
||||||
timeline = new PathConstraintPositionTimeline(frameCount);
|
data.spacingMode == SpacingMode.Length || data.spacingMode == SpacingMode.Fixed ? scale : 1));
|
||||||
if (data.positionMode == PositionMode.Fixed) timelineScale = scale;
|
break;
|
||||||
}
|
case PATH_MIX:
|
||||||
timeline.pathConstraintIndex = index;
|
timelines
|
||||||
for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) {
|
.Add(ReadTimeline(input, new PathConstraintMixTimeline(input.ReadInt(true), input.ReadInt(true), index), 1));
|
||||||
timeline.SetFrame(frameIndex, input.ReadFloat(), input.ReadFloat() * timelineScale);
|
break;
|
||||||
if (frameIndex < frameCount - 1) ReadCurve(input, frameIndex, timeline);
|
|
||||||
}
|
|
||||||
timelines.Add(timeline);
|
|
||||||
duration = Math.Max(duration, timeline.frames[(frameCount - 1) * PathConstraintPositionTimeline.ENTRIES]);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case PATH_MIX: {
|
|
||||||
PathConstraintMixTimeline timeline = new PathConstraintMixTimeline(frameCount);
|
|
||||||
timeline.pathConstraintIndex = index;
|
|
||||||
for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) {
|
|
||||||
timeline.SetFrame(frameIndex, input.ReadFloat(), input.ReadFloat(), input.ReadFloat());
|
|
||||||
if (frameIndex < frameCount - 1) ReadCurve(input, frameIndex, timeline);
|
|
||||||
}
|
|
||||||
timelines.Add(timeline);
|
|
||||||
duration = Math.Max(duration, timeline.frames[(frameCount - 1) * PathConstraintMixTimeline.ENTRIES]);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -763,18 +773,18 @@ namespace Spine {
|
|||||||
for (int ii = 0, nn = input.ReadInt(true); ii < nn; ii++) {
|
for (int ii = 0, nn = input.ReadInt(true); ii < nn; ii++) {
|
||||||
int slotIndex = input.ReadInt(true);
|
int slotIndex = input.ReadInt(true);
|
||||||
for (int iii = 0, nnn = input.ReadInt(true); iii < nnn; iii++) {
|
for (int iii = 0, nnn = input.ReadInt(true); iii < nnn; iii++) {
|
||||||
VertexAttachment attachment = (VertexAttachment)skin.GetAttachment(slotIndex, input.ReadStringRef());
|
String attachmentName = input.ReadStringRef();
|
||||||
bool weighted = attachment.bones != null;
|
VertexAttachment attachment = (VertexAttachment)skin.GetAttachment(slotIndex, attachmentName);
|
||||||
float[] vertices = attachment.vertices;
|
if (attachment == null) throw new SerializationException("Vertex attachment not found: " + attachmentName);
|
||||||
int deformLength = weighted ? vertices.Length / 3 * 2 : vertices.Length;
|
bool weighted = attachment.Bones != null;
|
||||||
|
float[] vertices = attachment.Vertices;
|
||||||
|
int deformLength = weighted ? (vertices.Length / 3) << 1 : vertices.Length;
|
||||||
|
|
||||||
int frameCount = input.ReadInt(true);
|
int frameCount = input.ReadInt(true), frameLast = frameCount - 1;
|
||||||
DeformTimeline timeline = new DeformTimeline(frameCount);
|
DeformTimeline timeline = new DeformTimeline(frameCount, input.ReadInt(true), slotIndex, attachment);
|
||||||
timeline.slotIndex = slotIndex;
|
|
||||||
timeline.attachment = attachment;
|
|
||||||
|
|
||||||
for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) {
|
float time = input.ReadFloat();
|
||||||
float time = input.ReadFloat();
|
for (int frame = 0, bezier = 0; ; frame++) {
|
||||||
float[] deform;
|
float[] deform;
|
||||||
int end = input.ReadInt(true);
|
int end = input.ReadInt(true);
|
||||||
if (end == 0)
|
if (end == 0)
|
||||||
@ -786,7 +796,8 @@ namespace Spine {
|
|||||||
if (scale == 1) {
|
if (scale == 1) {
|
||||||
for (int v = start; v < end; v++)
|
for (int v = start; v < end; v++)
|
||||||
deform[v] = input.ReadFloat();
|
deform[v] = input.ReadFloat();
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
for (int v = start; v < end; v++)
|
for (int v = start; v < end; v++)
|
||||||
deform[v] = input.ReadFloat() * scale;
|
deform[v] = input.ReadFloat() * scale;
|
||||||
}
|
}
|
||||||
@ -795,12 +806,20 @@ namespace Spine {
|
|||||||
deform[v] += vertices[v];
|
deform[v] += vertices[v];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
timeline.SetFrame(frame, time, deform);
|
||||||
timeline.SetFrame(frameIndex, time, deform);
|
if (frame == frameLast) break;
|
||||||
if (frameIndex < frameCount - 1) ReadCurve(input, frameIndex, timeline);
|
float time2 = input.ReadFloat();
|
||||||
|
switch (input.ReadByte()) {
|
||||||
|
case CURVE_STEPPED:
|
||||||
|
timeline.SetStepped(frame);
|
||||||
|
break;
|
||||||
|
case CURVE_BEZIER:
|
||||||
|
SetBezier(input, timeline, bezier++, frame, 0, time, time2, 0, 1, 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
time = time2;
|
||||||
}
|
}
|
||||||
timelines.Add(timeline);
|
timelines.Add(timeline);
|
||||||
duration = Math.Max(duration, timeline.frames[frameCount - 1]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -835,7 +854,6 @@ namespace Spine {
|
|||||||
timeline.SetFrame(i, time, drawOrder);
|
timeline.SetFrame(i, time, drawOrder);
|
||||||
}
|
}
|
||||||
timelines.Add(timeline);
|
timelines.Add(timeline);
|
||||||
duration = Math.Max(duration, timeline.frames[drawOrderCount - 1]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Event timeline.
|
// Event timeline.
|
||||||
@ -845,34 +863,75 @@ namespace Spine {
|
|||||||
for (int i = 0; i < eventCount; i++) {
|
for (int i = 0; i < eventCount; i++) {
|
||||||
float time = input.ReadFloat();
|
float time = input.ReadFloat();
|
||||||
EventData eventData = skeletonData.events.Items[input.ReadInt(true)];
|
EventData eventData = skeletonData.events.Items[input.ReadInt(true)];
|
||||||
Event e = new Event(time, eventData) {
|
Event e = new Event(time, eventData);
|
||||||
Int = input.ReadInt(false),
|
e.intValue = input.ReadInt(false);
|
||||||
Float = input.ReadFloat(),
|
e.floatValue = input.ReadFloat();
|
||||||
String = input.ReadBoolean() ? input.ReadString() : eventData.String
|
e.stringValue = input.ReadBoolean() ? input.ReadString() : eventData.String;
|
||||||
};
|
if (e.Data.AudioPath != null) {
|
||||||
if (e.data.AudioPath != null) {
|
|
||||||
e.volume = input.ReadFloat();
|
e.volume = input.ReadFloat();
|
||||||
e.balance = input.ReadFloat();
|
e.balance = input.ReadFloat();
|
||||||
}
|
}
|
||||||
timeline.SetFrame(i, e);
|
timeline.SetFrame(i, e);
|
||||||
}
|
}
|
||||||
timelines.Add(timeline);
|
timelines.Add(timeline);
|
||||||
duration = Math.Max(duration, timeline.frames[eventCount - 1]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
timelines.TrimExcess();
|
float duration = 0;
|
||||||
|
var items = timelines.Items;
|
||||||
|
for (int i = 0, n = timelines.Count; i < n; i++)
|
||||||
|
duration = Math.Max(duration, items[i].Duration);
|
||||||
return new Animation(name, timelines, duration);
|
return new Animation(name, timelines, duration);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ReadCurve (SkeletonInput input, int frameIndex, CurveTimeline timeline) {
|
/// <exception cref="IOException">Throws IOException when a read operation fails.</exception>
|
||||||
switch (input.ReadByte()) {
|
private Timeline ReadTimeline (SkeletonInput input, CurveTimeline1 timeline, float scale) {
|
||||||
case CURVE_STEPPED:
|
float time = input.ReadFloat(), value = input.ReadFloat() * scale;
|
||||||
timeline.SetStepped(frameIndex);
|
for (int frame = 0, bezier = 0, frameLast = timeline.FrameCount - 1;; frame++) {
|
||||||
break;
|
timeline.SetFrame(frame, time, value);
|
||||||
case CURVE_BEZIER:
|
if (frame == frameLast) break;
|
||||||
timeline.SetCurve(frameIndex, input.ReadFloat(), input.ReadFloat(), input.ReadFloat(), input.ReadFloat());
|
float time2 = input.ReadFloat(), value2 = input.ReadFloat() * scale;
|
||||||
break;
|
switch (input.ReadByte()) {
|
||||||
|
case CURVE_STEPPED:
|
||||||
|
timeline.SetStepped(frame);
|
||||||
|
break;
|
||||||
|
case CURVE_BEZIER:
|
||||||
|
SetBezier (input, timeline, bezier++, frame, 0, time, time2, value, value2, 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
time = time2;
|
||||||
|
value = value2;
|
||||||
}
|
}
|
||||||
|
return timeline;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <exception cref="IOException">Throws IOException when a read operation fails.</exception>
|
||||||
|
private Timeline ReadTimeline (SkeletonInput input, CurveTimeline2 timeline, float scale) {
|
||||||
|
float time = input.ReadFloat(), value1 = input.ReadFloat() * scale, value2 = input.ReadFloat() * scale;
|
||||||
|
for (int frame = 0, bezier = 0, frameLast = timeline.FrameCount - 1;; frame++) {
|
||||||
|
timeline.SetFrame(frame, time, value1, value2);
|
||||||
|
if (frame == frameLast) break;
|
||||||
|
float time2 = input.ReadFloat(), nvalue1 = input.ReadFloat() * scale, nvalue2 = input.ReadFloat() * scale;
|
||||||
|
switch (input.ReadByte()) {
|
||||||
|
case CURVE_STEPPED:
|
||||||
|
timeline.SetStepped(frame);
|
||||||
|
break;
|
||||||
|
case CURVE_BEZIER:
|
||||||
|
SetBezier(input, timeline, bezier++, frame, 0, time, time2, value1, nvalue1, scale);
|
||||||
|
SetBezier(input, timeline, bezier++, frame, 1, time, time2, value2, nvalue2, scale);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
time = time2;
|
||||||
|
value1 = nvalue1;
|
||||||
|
value2 = nvalue2;
|
||||||
|
}
|
||||||
|
return timeline;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <exception cref="IOException">Throws IOException when a read operation fails.</exception>
|
||||||
|
void SetBezier (SkeletonInput input, CurveTimeline timeline, int bezier, int frame, int value, float time1, float time2,
|
||||||
|
float value1, float value2, float scale) {
|
||||||
|
timeline.SetBezier(bezier, frame, value, time1, value1, input.ReadFloat(), input.ReadFloat() * scale, input.ReadFloat(),
|
||||||
|
input.ReadFloat() * scale, time2, value2);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class Vertices
|
internal class Vertices
|
||||||
@ -883,14 +942,18 @@ namespace Spine {
|
|||||||
|
|
||||||
internal class SkeletonInput {
|
internal class SkeletonInput {
|
||||||
private byte[] chars = new byte[32];
|
private byte[] chars = new byte[32];
|
||||||
private byte[] bytesBigEndian = new byte[4];
|
private byte[] bytesBigEndian = new byte[8];
|
||||||
internal ExposedList<String> strings;
|
internal string[] strings;
|
||||||
Stream input;
|
Stream input;
|
||||||
|
|
||||||
public SkeletonInput (Stream input) {
|
public SkeletonInput (Stream input) {
|
||||||
this.input = input;
|
this.input = input;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int Read () {
|
||||||
|
return input.ReadByte();
|
||||||
|
}
|
||||||
|
|
||||||
public byte ReadByte () {
|
public byte ReadByte () {
|
||||||
return (byte)input.ReadByte();
|
return (byte)input.ReadByte();
|
||||||
}
|
}
|
||||||
@ -922,6 +985,18 @@ namespace Spine {
|
|||||||
+ bytesBigEndian[3];
|
+ bytesBigEndian[3];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public long ReadLong () {
|
||||||
|
input.Read(bytesBigEndian, 0, 8);
|
||||||
|
return ((long)(bytesBigEndian[0]) << 56)
|
||||||
|
+ ((long)(bytesBigEndian[1]) << 48)
|
||||||
|
+ ((long)(bytesBigEndian[2]) << 40)
|
||||||
|
+ ((long)(bytesBigEndian[3]) << 32)
|
||||||
|
+ ((long)(bytesBigEndian[4]) << 24)
|
||||||
|
+ ((long)(bytesBigEndian[5]) << 16)
|
||||||
|
+ ((long)(bytesBigEndian[6]) << 8)
|
||||||
|
+ (long)(bytesBigEndian[7]);
|
||||||
|
}
|
||||||
|
|
||||||
public int ReadInt (bool optimizePositive) {
|
public int ReadInt (bool optimizePositive) {
|
||||||
int b = input.ReadByte();
|
int b = input.ReadByte();
|
||||||
int result = b & 0x7F;
|
int result = b & 0x7F;
|
||||||
@ -959,7 +1034,7 @@ namespace Spine {
|
|||||||
///<return>May be null.</return>
|
///<return>May be null.</return>
|
||||||
public String ReadStringRef () {
|
public String ReadStringRef () {
|
||||||
int index = ReadInt(true);
|
int index = ReadInt(true);
|
||||||
return index == 0 ? null : strings.Items[index - 1];
|
return index == 0 ? null : strings[index - 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ReadFully (byte[] buffer, int offset, int length) {
|
public void ReadFully (byte[] buffer, int offset, int length) {
|
||||||
@ -974,20 +1049,9 @@ namespace Spine {
|
|||||||
/// <summary>Returns the version string of binary skeleton data.</summary>
|
/// <summary>Returns the version string of binary skeleton data.</summary>
|
||||||
public string GetVersionString () {
|
public string GetVersionString () {
|
||||||
try {
|
try {
|
||||||
// Hash.
|
ReadLong(); // long hash
|
||||||
int byteCount = ReadInt(true);
|
string version = ReadString();
|
||||||
if (byteCount > 1) input.Position += byteCount - 1;
|
return version;
|
||||||
|
|
||||||
// Version.
|
|
||||||
byteCount = ReadInt(true);
|
|
||||||
if (byteCount > 1) {
|
|
||||||
byteCount--;
|
|
||||||
var buffer = new byte[byteCount];
|
|
||||||
ReadFully(buffer, 0, byteCount);
|
|
||||||
return System.Text.Encoding.UTF8.GetString(buffer, 0, byteCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new ArgumentException("Stream does not contain a valid binary Skeleton Data.", "input");
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new ArgumentException("Stream does not contain a valid binary Skeleton Data.\n" + e, "input");
|
throw new ArgumentException("Stream does not contain a valid binary Skeleton Data.\n" + e, "input");
|
||||||
}
|
}
|
||||||
|
|||||||
@ -176,7 +176,7 @@ namespace Spine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Returns the first bounding box attachment that contains the point, or null. When doing many checks, it is usually more
|
/// <summary>Returns the first bounding box attachment that contains the point, or null. When doing many checks, it is usually more
|
||||||
/// efficient to only call this method if {@link #aabbContainsPoint(float, float)} returns true.</summary>
|
/// efficient to only call this method if <see cref="AabbContainsPoint(float, float)"/> returns true.</summary>
|
||||||
public BoundingBoxAttachment ContainsPoint (float x, float y) {
|
public BoundingBoxAttachment ContainsPoint (float x, float y) {
|
||||||
ExposedList<Polygon> polygons = Polygons;
|
ExposedList<Polygon> polygons = Polygons;
|
||||||
for (int i = 0, n = polygons.Count; i < n; i++)
|
for (int i = 0, n = polygons.Count; i < n; i++)
|
||||||
@ -185,7 +185,7 @@ namespace Spine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Returns the first bounding box attachment that contains the line segment, or null. When doing many checks, it is usually
|
/// <summary>Returns the first bounding box attachment that contains the line segment, or null. When doing many checks, it is usually
|
||||||
/// more efficient to only call this method if {@link #aabbIntersectsSegment(float, float, float, float)} returns true.</summary>
|
/// more efficient to only call this method if <see cref="aabbIntersectsSegment(float, float, float, float)"/> returns true.</summary>
|
||||||
public BoundingBoxAttachment IntersectsSegment (float x1, float y1, float x2, float y2) {
|
public BoundingBoxAttachment IntersectsSegment (float x1, float y1, float x2, float y2) {
|
||||||
ExposedList<Polygon> polygons = Polygons;
|
ExposedList<Polygon> polygons = Polygons;
|
||||||
for (int i = 0, n = polygons.Count; i < n; i++)
|
for (int i = 0, n = polygons.Count; i < n; i++)
|
||||||
|
|||||||
@ -50,6 +50,8 @@ namespace Spine {
|
|||||||
internal float fps;
|
internal float fps;
|
||||||
internal string imagesPath, audioPath;
|
internal string imagesPath, audioPath;
|
||||||
|
|
||||||
|
///<summary>The skeleton's name, which by default is the name of the skeleton data file when possible, or null when a name hasn't been
|
||||||
|
///set.</summary>
|
||||||
public string Name { get { return name; } set { name = value; } }
|
public string Name { get { return name; } set { name = value; } }
|
||||||
|
|
||||||
/// <summary>The skeleton's bones, sorted parent first. The root bone is always the first bone.</summary>
|
/// <summary>The skeleton's bones, sorted parent first. The root bone is always the first bone.</summary>
|
||||||
@ -79,16 +81,18 @@ namespace Spine {
|
|||||||
public float Height { get { return height; } set { height = value; } }
|
public float Height { get { return height; } set { height = value; } }
|
||||||
/// <summary>The Spine version used to export this data, or null.</summary>
|
/// <summary>The Spine version used to export this data, or null.</summary>
|
||||||
public string Version { get { return version; } set { version = value; } }
|
public string Version { get { return version; } set { version = value; } }
|
||||||
|
|
||||||
|
///<summary>The skeleton data hash. This value will change if any of the skeleton data has changed.
|
||||||
|
///May be null.</summary>
|
||||||
public string Hash { get { return hash; } set { hash = value; } }
|
public string Hash { get { return hash; } set { hash = value; } }
|
||||||
|
|
||||||
/// <summary>The path to the images directory as defined in Spine. Available only when nonessential data was exported. May be null</summary>
|
|
||||||
public string ImagesPath { get { return imagesPath; } set { imagesPath = value; } }
|
public string ImagesPath { get { return imagesPath; } set { imagesPath = value; } }
|
||||||
|
|
||||||
/// <summary>The path to the audio directory defined in Spine. Available only when nonessential data was exported. May be null.</summary>
|
/// <summary> The path to the audio directory as defined in Spine. Available only when nonessential data was exported.
|
||||||
|
/// May be null.</summary>
|
||||||
public string AudioPath { get { return audioPath; } set { audioPath = value; } }
|
public string AudioPath { get { return audioPath; } set { audioPath = value; } }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>The dopesheet FPS in Spine, or zero if nonessential data was not exported.</summary>
|
||||||
/// The dopesheet FPS in Spine. Available only when nonessential data was exported.</summary>
|
|
||||||
public float Fps { get { return fps; } set { fps = value; } }
|
public float Fps { get { return fps; } set { fps = value; } }
|
||||||
|
|
||||||
// --- Bones.
|
// --- Bones.
|
||||||
@ -99,10 +103,9 @@ namespace Spine {
|
|||||||
/// <returns>May be null.</returns>
|
/// <returns>May be null.</returns>
|
||||||
public BoneData FindBone (string boneName) {
|
public BoneData FindBone (string boneName) {
|
||||||
if (boneName == null) throw new ArgumentNullException("boneName", "boneName cannot be null.");
|
if (boneName == null) throw new ArgumentNullException("boneName", "boneName cannot be null.");
|
||||||
var bones = this.bones;
|
var bones = this.bones.Items;
|
||||||
var bonesItems = bones.Items;
|
for (int i = 0, n = this.bones.Count; i < n; i++) {
|
||||||
for (int i = 0, n = bones.Count; i < n; i++) {
|
BoneData bone = bones[i];
|
||||||
BoneData bone = bonesItems[i];
|
|
||||||
if (bone.name == boneName) return bone;
|
if (bone.name == boneName) return bone;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
@ -111,10 +114,9 @@ namespace Spine {
|
|||||||
/// <returns>-1 if the bone was not found.</returns>
|
/// <returns>-1 if the bone was not found.</returns>
|
||||||
public int FindBoneIndex (string boneName) {
|
public int FindBoneIndex (string boneName) {
|
||||||
if (boneName == null) throw new ArgumentNullException("boneName", "boneName cannot be null.");
|
if (boneName == null) throw new ArgumentNullException("boneName", "boneName cannot be null.");
|
||||||
var bones = this.bones;
|
var bones = this.bones.Items;
|
||||||
var bonesItems = bones.Items;
|
for (int i = 0, n = this.bones.Count; i < n; i++)
|
||||||
for (int i = 0, n = bones.Count; i < n; i++)
|
if (bones[i].name == boneName) return i;
|
||||||
if (bonesItems[i].name == boneName) return i;
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -123,9 +125,9 @@ namespace Spine {
|
|||||||
/// <returns>May be null.</returns>
|
/// <returns>May be null.</returns>
|
||||||
public SlotData FindSlot (string slotName) {
|
public SlotData FindSlot (string slotName) {
|
||||||
if (slotName == null) throw new ArgumentNullException("slotName", "slotName cannot be null.");
|
if (slotName == null) throw new ArgumentNullException("slotName", "slotName cannot be null.");
|
||||||
ExposedList<SlotData> slots = this.slots;
|
var slots = this.slots.Items;
|
||||||
for (int i = 0, n = slots.Count; i < n; i++) {
|
for (int i = 0, n = this.slots.Count; i < n; i++) {
|
||||||
SlotData slot = slots.Items[i];
|
SlotData slot = slots[i];
|
||||||
if (slot.name == slotName) return slot;
|
if (slot.name == slotName) return slot;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
@ -165,9 +167,9 @@ namespace Spine {
|
|||||||
/// <returns>May be null.</returns>
|
/// <returns>May be null.</returns>
|
||||||
public Animation FindAnimation (string animationName) {
|
public Animation FindAnimation (string animationName) {
|
||||||
if (animationName == null) throw new ArgumentNullException("animationName", "animationName cannot be null.");
|
if (animationName == null) throw new ArgumentNullException("animationName", "animationName cannot be null.");
|
||||||
ExposedList<Animation> animations = this.animations;
|
var animations = this.animations.Items;
|
||||||
for (int i = 0, n = animations.Count; i < n; i++) {
|
for (int i = 0, n = this.animations.Count; i < n; i++) {
|
||||||
Animation animation = animations.Items[i];
|
Animation animation = animations[i];
|
||||||
if (animation.name == animationName) return animation;
|
if (animation.name == animationName) return animation;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
@ -178,9 +180,9 @@ namespace Spine {
|
|||||||
/// <returns>May be null.</returns>
|
/// <returns>May be null.</returns>
|
||||||
public IkConstraintData FindIkConstraint (string constraintName) {
|
public IkConstraintData FindIkConstraint (string constraintName) {
|
||||||
if (constraintName == null) throw new ArgumentNullException("constraintName", "constraintName cannot be null.");
|
if (constraintName == null) throw new ArgumentNullException("constraintName", "constraintName cannot be null.");
|
||||||
ExposedList<IkConstraintData> ikConstraints = this.ikConstraints;
|
var ikConstraints = this.ikConstraints.Items;
|
||||||
for (int i = 0, n = ikConstraints.Count; i < n; i++) {
|
for (int i = 0, n = this.ikConstraints.Count; i < n; i++) {
|
||||||
IkConstraintData ikConstraint = ikConstraints.Items[i];
|
IkConstraintData ikConstraint = ikConstraints[i];
|
||||||
if (ikConstraint.name == constraintName) return ikConstraint;
|
if (ikConstraint.name == constraintName) return ikConstraint;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
@ -191,9 +193,9 @@ namespace Spine {
|
|||||||
/// <returns>May be null.</returns>
|
/// <returns>May be null.</returns>
|
||||||
public TransformConstraintData FindTransformConstraint (string constraintName) {
|
public TransformConstraintData FindTransformConstraint (string constraintName) {
|
||||||
if (constraintName == null) throw new ArgumentNullException("constraintName", "constraintName cannot be null.");
|
if (constraintName == null) throw new ArgumentNullException("constraintName", "constraintName cannot be null.");
|
||||||
ExposedList<TransformConstraintData> transformConstraints = this.transformConstraints;
|
var transformConstraints = this.transformConstraints.Items;
|
||||||
for (int i = 0, n = transformConstraints.Count; i < n; i++) {
|
for (int i = 0, n = this.transformConstraints.Count; i < n; i++) {
|
||||||
TransformConstraintData transformConstraint = transformConstraints.Items[i];
|
TransformConstraintData transformConstraint = transformConstraints[i];
|
||||||
if (transformConstraint.name == constraintName) return transformConstraint;
|
if (transformConstraint.name == constraintName) return transformConstraint;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
@ -204,9 +206,9 @@ namespace Spine {
|
|||||||
/// <returns>May be null.</returns>
|
/// <returns>May be null.</returns>
|
||||||
public PathConstraintData FindPathConstraint (string constraintName) {
|
public PathConstraintData FindPathConstraint (string constraintName) {
|
||||||
if (constraintName == null) throw new ArgumentNullException("constraintName", "constraintName cannot be null.");
|
if (constraintName == null) throw new ArgumentNullException("constraintName", "constraintName cannot be null.");
|
||||||
ExposedList<PathConstraintData> pathConstraints = this.pathConstraints;
|
var pathConstraints = this.pathConstraints.Items;
|
||||||
for (int i = 0, n = pathConstraints.Count; i < n; i++) {
|
for (int i = 0, n = this.pathConstraints.Count; i < n; i++) {
|
||||||
PathConstraintData constraint = pathConstraints.Items[i];
|
PathConstraintData constraint = pathConstraints[i];
|
||||||
if (constraint.name.Equals(constraintName)) return constraint;
|
if (constraint.name.Equals(constraintName)) return constraint;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@ -41,23 +41,27 @@ using Windows.Storage;
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace Spine {
|
namespace Spine {
|
||||||
public class SkeletonJson {
|
|
||||||
public float Scale { get; set; }
|
|
||||||
|
|
||||||
private AttachmentLoader attachmentLoader;
|
/// <summary>
|
||||||
private List<LinkedMesh> linkedMeshes = new List<LinkedMesh>();
|
/// Loads skeleton data in the Spine JSON format.
|
||||||
|
/// <para>
|
||||||
|
/// JSON is human readable but the binary format is much smaller on disk and faster to load. See <see cref="SkeletonBinary"/>.</para>
|
||||||
|
/// <para>
|
||||||
|
/// See <a href="http://esotericsoftware.com/spine-json-format">Spine JSON format</a> and
|
||||||
|
/// <a href = "http://esotericsoftware.com/spine-loading-skeleton-data#JSON-and-binary-data" > JSON and binary data</a> in the Spine
|
||||||
|
/// Runtimes Guide.</para>
|
||||||
|
/// </summary>
|
||||||
|
public class SkeletonJson : SkeletonLoader {
|
||||||
|
|
||||||
|
public SkeletonJson (AttachmentLoader attachmentLoader)
|
||||||
|
: base(attachmentLoader) {
|
||||||
|
}
|
||||||
|
|
||||||
public SkeletonJson (params Atlas[] atlasArray)
|
public SkeletonJson (params Atlas[] atlasArray)
|
||||||
: this(new AtlasAttachmentLoader(atlasArray)) {
|
: base(atlasArray) {
|
||||||
}
|
}
|
||||||
|
|
||||||
public SkeletonJson (AttachmentLoader attachmentLoader) {
|
#if !IS_UNITY && WINDOWS_STOREAPP
|
||||||
if (attachmentLoader == null) throw new ArgumentNullException("attachmentLoader", "attachmentLoader cannot be null.");
|
|
||||||
this.attachmentLoader = attachmentLoader;
|
|
||||||
Scale = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if !IS_UNITY && WINDOWS_STOREAPP
|
|
||||||
private async Task<SkeletonData> ReadFile(string path) {
|
private async Task<SkeletonData> ReadFile(string path) {
|
||||||
var folder = Windows.ApplicationModel.Package.Current.InstalledLocation;
|
var folder = Windows.ApplicationModel.Package.Current.InstalledLocation;
|
||||||
var file = await folder.GetFileAsync(path).AsTask().ConfigureAwait(false);
|
var file = await folder.GetFileAsync(path).AsTask().ConfigureAwait(false);
|
||||||
@ -68,11 +72,11 @@ namespace Spine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public SkeletonData ReadSkeletonData (string path) {
|
public override SkeletonData ReadSkeletonData (string path) {
|
||||||
return this.ReadFile(path).Result;
|
return this.ReadFile(path).Result;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
public SkeletonData ReadSkeletonData (string path) {
|
public override SkeletonData ReadSkeletonData (string path) {
|
||||||
#if WINDOWS_PHONE
|
#if WINDOWS_PHONE
|
||||||
using (var reader = new StreamReader(Microsoft.Xna.Framework.TitleContainer.OpenStream(path))) {
|
using (var reader = new StreamReader(Microsoft.Xna.Framework.TitleContainer.OpenStream(path))) {
|
||||||
#else
|
#else
|
||||||
@ -88,7 +92,7 @@ namespace Spine {
|
|||||||
public SkeletonData ReadSkeletonData (TextReader reader) {
|
public SkeletonData ReadSkeletonData (TextReader reader) {
|
||||||
if (reader == null) throw new ArgumentNullException("reader", "reader cannot be null.");
|
if (reader == null) throw new ArgumentNullException("reader", "reader cannot be null.");
|
||||||
|
|
||||||
float scale = this.Scale;
|
float scale = this.scale;
|
||||||
var skeletonData = new SkeletonData();
|
var skeletonData = new SkeletonData();
|
||||||
|
|
||||||
var root = Json.Deserialize(reader) as Dictionary<string, Object>;
|
var root = Json.Deserialize(reader) as Dictionary<string, Object>;
|
||||||
@ -99,8 +103,6 @@ namespace Spine {
|
|||||||
var skeletonMap = (Dictionary<string, Object>)root["skeleton"];
|
var skeletonMap = (Dictionary<string, Object>)root["skeleton"];
|
||||||
skeletonData.hash = (string)skeletonMap["hash"];
|
skeletonData.hash = (string)skeletonMap["hash"];
|
||||||
skeletonData.version = (string)skeletonMap["spine"];
|
skeletonData.version = (string)skeletonMap["spine"];
|
||||||
if ("3.8.75" == skeletonData.version)
|
|
||||||
throw new Exception("Unsupported skeleton data, please export with a newer version of Spine.");
|
|
||||||
skeletonData.x = GetFloat(skeletonMap, "x", 0);
|
skeletonData.x = GetFloat(skeletonMap, "x", 0);
|
||||||
skeletonData.y = GetFloat(skeletonMap, "y", 0);
|
skeletonData.y = GetFloat(skeletonMap, "y", 0);
|
||||||
skeletonData.width = GetFloat(skeletonMap, "width", 0);
|
skeletonData.width = GetFloat(skeletonMap, "width", 0);
|
||||||
@ -283,6 +285,7 @@ namespace Spine {
|
|||||||
skin.bones.Add(bone);
|
skin.bones.Add(bone);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
skin.bones.TrimExcess();
|
||||||
if (skinMap.ContainsKey("ik")) {
|
if (skinMap.ContainsKey("ik")) {
|
||||||
foreach (string entryName in (List<Object>)skinMap["ik"]) {
|
foreach (string entryName in (List<Object>)skinMap["ik"]) {
|
||||||
IkConstraintData constraint = skeletonData.FindIkConstraint(entryName);
|
IkConstraintData constraint = skeletonData.FindIkConstraint(entryName);
|
||||||
@ -304,6 +307,7 @@ namespace Spine {
|
|||||||
skin.constraints.Add(constraint);
|
skin.constraints.Add(constraint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
skin.constraints.TrimExcess();
|
||||||
if (skinMap.ContainsKey("attachments")) {
|
if (skinMap.ContainsKey("attachments")) {
|
||||||
foreach (KeyValuePair<string, Object> slotEntry in (Dictionary<string, Object>)skinMap["attachments"]) {
|
foreach (KeyValuePair<string, Object> slotEntry in (Dictionary<string, Object>)skinMap["attachments"]) {
|
||||||
int slotIndex = skeletonData.FindSlotIndex(slotEntry.Key);
|
int slotIndex = skeletonData.FindSlotIndex(slotEntry.Key);
|
||||||
@ -358,7 +362,7 @@ namespace Spine {
|
|||||||
try {
|
try {
|
||||||
ReadAnimation((Dictionary<string, Object>)entry.Value, entry.Key, skeletonData);
|
ReadAnimation((Dictionary<string, Object>)entry.Value, entry.Key, skeletonData);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new Exception("Error reading animation: " + entry.Key, e);
|
throw new Exception("Error reading animation: " + entry.Key + "\n" + e.Message, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -373,7 +377,7 @@ namespace Spine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Attachment ReadAttachment (Dictionary<string, Object> map, Skin skin, int slotIndex, string name, SkeletonData skeletonData) {
|
private Attachment ReadAttachment (Dictionary<string, Object> map, Skin skin, int slotIndex, string name, SkeletonData skeletonData) {
|
||||||
float scale = this.Scale;
|
float scale = this.scale;
|
||||||
name = GetString(map, "name", name);
|
name = GetString(map, "name", name);
|
||||||
|
|
||||||
var typeName = GetString(map, "type", "region");
|
var typeName = GetString(map, "type", "region");
|
||||||
@ -438,7 +442,7 @@ namespace Spine {
|
|||||||
mesh.regionUVs = uvs;
|
mesh.regionUVs = uvs;
|
||||||
mesh.UpdateUVs();
|
mesh.UpdateUVs();
|
||||||
|
|
||||||
if (map.ContainsKey("hull")) mesh.HullLength = GetInt(map, "hull", 0) * 2;
|
if (map.ContainsKey("hull")) mesh.HullLength = GetInt(map, "hull", 0) << 1;
|
||||||
if (map.ContainsKey("edges")) mesh.Edges = GetIntArray(map, "edges");
|
if (map.ContainsKey("edges")) mesh.Edges = GetIntArray(map, "edges");
|
||||||
return mesh;
|
return mesh;
|
||||||
}
|
}
|
||||||
@ -505,7 +509,7 @@ namespace Spine {
|
|||||||
for (int i = 0, n = vertices.Length; i < n;) {
|
for (int i = 0, n = vertices.Length; i < n;) {
|
||||||
int boneCount = (int)vertices[i++];
|
int boneCount = (int)vertices[i++];
|
||||||
bones.Add(boneCount);
|
bones.Add(boneCount);
|
||||||
for (int nn = i + boneCount * 4; i < nn; i += 4) {
|
for (int nn = i + (boneCount << 2); i < nn; i += 4) {
|
||||||
bones.Add((int)vertices[i]);
|
bones.Add((int)vertices[i]);
|
||||||
weights.Add(vertices[i + 1] * this.Scale);
|
weights.Add(vertices[i + 1] * this.Scale);
|
||||||
weights.Add(vertices[i + 2] * this.Scale);
|
weights.Add(vertices[i + 2] * this.Scale);
|
||||||
@ -517,9 +521,8 @@ namespace Spine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void ReadAnimation (Dictionary<string, Object> map, string name, SkeletonData skeletonData) {
|
private void ReadAnimation (Dictionary<string, Object> map, string name, SkeletonData skeletonData) {
|
||||||
var scale = this.Scale;
|
var scale = this.scale;
|
||||||
var timelines = new ExposedList<Timeline>();
|
var timelines = new ExposedList<Timeline>();
|
||||||
float duration = 0;
|
|
||||||
|
|
||||||
// Slot timelines.
|
// Slot timelines.
|
||||||
if (map.ContainsKey("slots")) {
|
if (map.ContainsKey("slots")) {
|
||||||
@ -529,50 +532,117 @@ namespace Spine {
|
|||||||
var timelineMap = (Dictionary<string, Object>)entry.Value;
|
var timelineMap = (Dictionary<string, Object>)entry.Value;
|
||||||
foreach (KeyValuePair<string, Object> timelineEntry in timelineMap) {
|
foreach (KeyValuePair<string, Object> timelineEntry in timelineMap) {
|
||||||
var values = (List<Object>)timelineEntry.Value;
|
var values = (List<Object>)timelineEntry.Value;
|
||||||
|
if (values.Count == 0) continue;
|
||||||
var timelineName = (string)timelineEntry.Key;
|
var timelineName = (string)timelineEntry.Key;
|
||||||
if (timelineName == "attachment") {
|
if (timelineName == "attachment") {
|
||||||
var timeline = new AttachmentTimeline(values.Count);
|
var timeline = new AttachmentTimeline(values.Count, slotIndex);
|
||||||
timeline.slotIndex = slotIndex;
|
int frame = 0;
|
||||||
|
foreach (Dictionary<string, Object> keyMap in values) {
|
||||||
int frameIndex = 0;
|
timeline.SetFrame(frame++, GetFloat(keyMap, "time", 0), (string)keyMap["name"]);
|
||||||
foreach (Dictionary<string, Object> valueMap in values) {
|
|
||||||
float time = GetFloat(valueMap, "time", 0);
|
|
||||||
timeline.SetFrame(frameIndex++, time, (string)valueMap["name"]);
|
|
||||||
}
|
}
|
||||||
timelines.Add(timeline);
|
timelines.Add(timeline);
|
||||||
duration = Math.Max(duration, timeline.frames[timeline.FrameCount - 1]);
|
|
||||||
|
|
||||||
} else if (timelineName == "color") {
|
} else if (timelineName == "color") {
|
||||||
var timeline = new ColorTimeline(values.Count);
|
var timeline = new ColorTimeline(values.Count, values.Count << 2, slotIndex);
|
||||||
timeline.slotIndex = slotIndex;
|
|
||||||
|
|
||||||
int frameIndex = 0;
|
var keyMapEnumerator = values.GetEnumerator();
|
||||||
foreach (Dictionary<string, Object> valueMap in values) {
|
keyMapEnumerator.MoveNext();
|
||||||
float time = GetFloat(valueMap, "time", 0);
|
var keyMap = (Dictionary<string, Object>)keyMapEnumerator.Current;
|
||||||
string c = (string)valueMap["color"];
|
float time = GetFloat(keyMap, "time", 0);
|
||||||
timeline.SetFrame(frameIndex, time, ToColor(c, 0), ToColor(c, 1), ToColor(c, 2), ToColor(c, 3));
|
string color = (string)keyMap["color"];
|
||||||
ReadCurve(valueMap, timeline, frameIndex);
|
float r = ToColor(color, 0);
|
||||||
frameIndex++;
|
float g = ToColor(color, 1);
|
||||||
|
float b = ToColor(color, 2);
|
||||||
|
float a = ToColor(color, 3);
|
||||||
|
for (int frame = 0, bezier = 0;; frame++) {
|
||||||
|
timeline.SetFrame(frame, time, r, g, b, a);
|
||||||
|
bool hasNext = keyMapEnumerator.MoveNext();
|
||||||
|
if (!hasNext) {
|
||||||
|
timeline.Shrink(bezier);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
var nextMap = (Dictionary<string, Object>)keyMapEnumerator.Current;
|
||||||
|
|
||||||
|
float time2 = GetFloat(nextMap, "time", 0);
|
||||||
|
color = (string)nextMap["color"];
|
||||||
|
float nr = ToColor(color, 0);
|
||||||
|
float ng = ToColor(color, 1);
|
||||||
|
float nb = ToColor(color, 2);
|
||||||
|
float na = ToColor(color, 3);
|
||||||
|
|
||||||
|
if (keyMap.ContainsKey("curve")) {
|
||||||
|
object curve = keyMap["curve"];
|
||||||
|
bezier = ReadCurve(curve, timeline, bezier, frame, 0, time, time2, r, nr, 1);
|
||||||
|
bezier = ReadCurve(curve, timeline, bezier, frame, 1, time, time2, g, ng, 1);
|
||||||
|
bezier = ReadCurve(curve, timeline, bezier, frame, 2, time, time2, b, nb, 1);
|
||||||
|
bezier = ReadCurve(curve, timeline, bezier, frame, 3, time, time2, a, na, 1);
|
||||||
|
}
|
||||||
|
time = time2;
|
||||||
|
r = nr;
|
||||||
|
g = ng;
|
||||||
|
b = nb;
|
||||||
|
a = na;
|
||||||
|
keyMap = nextMap;
|
||||||
}
|
}
|
||||||
timelines.Add(timeline);
|
timelines.Add(timeline);
|
||||||
duration = Math.Max(duration, timeline.frames[(timeline.FrameCount - 1) * ColorTimeline.ENTRIES]);
|
|
||||||
|
|
||||||
} else if (timelineName == "twoColor") {
|
} else if (timelineName == "twoColor") {
|
||||||
var timeline = new TwoColorTimeline(values.Count);
|
var timeline = new TwoColorTimeline(values.Count, values.Count * 7, slotIndex);
|
||||||
timeline.slotIndex = slotIndex;
|
|
||||||
|
|
||||||
int frameIndex = 0;
|
var keyMapEnumerator = values.GetEnumerator();
|
||||||
foreach (Dictionary<string, Object> valueMap in values) {
|
keyMapEnumerator.MoveNext();
|
||||||
float time = GetFloat(valueMap, "time", 0);
|
var keyMap = (Dictionary<string, Object>)keyMapEnumerator.Current;
|
||||||
string light = (string)valueMap["light"];
|
float time = GetFloat(keyMap, "time", 0);
|
||||||
string dark = (string)valueMap["dark"];
|
string color = (string)keyMap["light"];
|
||||||
timeline.SetFrame(frameIndex, time, ToColor(light, 0), ToColor(light, 1), ToColor(light, 2), ToColor(light, 3),
|
float r = ToColor(color, 0);
|
||||||
ToColor(dark, 0, 6), ToColor(dark, 1, 6), ToColor(dark, 2, 6));
|
float g = ToColor(color, 1);
|
||||||
ReadCurve(valueMap, timeline, frameIndex);
|
float b = ToColor(color, 2);
|
||||||
frameIndex++;
|
float a = ToColor(color, 3);
|
||||||
|
color = (string)keyMap["dark"];
|
||||||
|
float r2 = ToColor(color, 0);
|
||||||
|
float g2 = ToColor(color, 1);
|
||||||
|
float b2 = ToColor(color, 2);
|
||||||
|
for (int frame = 0, bezier = 0; ; frame++) {
|
||||||
|
timeline.SetFrame(frame, time, r, g, b, a, r2, g2, b2);
|
||||||
|
bool hasNext = keyMapEnumerator.MoveNext();
|
||||||
|
if (!hasNext) {
|
||||||
|
timeline.Shrink(bezier);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
var nextMap = (Dictionary<string, Object>)keyMapEnumerator.Current;
|
||||||
|
|
||||||
|
float time2 = GetFloat(nextMap, "time", 0);
|
||||||
|
color = (string)nextMap["light"];
|
||||||
|
float nr = ToColor(color, 0);
|
||||||
|
float ng = ToColor(color, 1);
|
||||||
|
float nb = ToColor(color, 2);
|
||||||
|
float na = ToColor(color, 3);
|
||||||
|
color = (string)nextMap["dark"];
|
||||||
|
float nr2 = ToColor(color, 0);
|
||||||
|
float ng2 = ToColor(color, 1);
|
||||||
|
float nb2 = ToColor(color, 2);
|
||||||
|
|
||||||
|
if (keyMap.ContainsKey("curve")) {
|
||||||
|
object curve = keyMap["curve"];
|
||||||
|
bezier = ReadCurve(curve, timeline, bezier, frame, 0, time, time2, r, nr, 1);
|
||||||
|
bezier = ReadCurve(curve, timeline, bezier, frame, 1, time, time2, g, ng, 1);
|
||||||
|
bezier = ReadCurve(curve, timeline, bezier, frame, 2, time, time2, b, nb, 1);
|
||||||
|
bezier = ReadCurve(curve, timeline, bezier, frame, 3, time, time2, a, na, 1);
|
||||||
|
bezier = ReadCurve(curve, timeline, bezier, frame, 4, time, time2, r2, nr2, 1);
|
||||||
|
bezier = ReadCurve(curve, timeline, bezier, frame, 5, time, time2, g2, ng2, 1);
|
||||||
|
bezier = ReadCurve(curve, timeline, bezier, frame, 6, time, time2, b2, nb2, 1);
|
||||||
|
}
|
||||||
|
time = time2;
|
||||||
|
r = nr;
|
||||||
|
g = ng;
|
||||||
|
b = nb;
|
||||||
|
a = na;
|
||||||
|
r2 = nr2;
|
||||||
|
g2 = ng2;
|
||||||
|
b2 = nb2;
|
||||||
|
keyMap = nextMap;
|
||||||
}
|
}
|
||||||
timelines.Add(timeline);
|
timelines.Add(timeline);
|
||||||
duration = Math.Max(duration, timeline.frames[(timeline.FrameCount - 1) * TwoColorTimeline.ENTRIES]);
|
|
||||||
|
|
||||||
} else
|
} else
|
||||||
throw new Exception("Invalid timeline type for a slot: " + timelineName + " (" + slotName + ")");
|
throw new Exception("Invalid timeline type for a slot: " + timelineName + " (" + slotName + ")");
|
||||||
@ -589,47 +659,23 @@ namespace Spine {
|
|||||||
var timelineMap = (Dictionary<string, Object>)entry.Value;
|
var timelineMap = (Dictionary<string, Object>)entry.Value;
|
||||||
foreach (KeyValuePair<string, Object> timelineEntry in timelineMap) {
|
foreach (KeyValuePair<string, Object> timelineEntry in timelineMap) {
|
||||||
var values = (List<Object>)timelineEntry.Value;
|
var values = (List<Object>)timelineEntry.Value;
|
||||||
|
var keyMapEnumerator = values.GetEnumerator();
|
||||||
|
bool hasNext = keyMapEnumerator.MoveNext();
|
||||||
|
if (!hasNext) continue;
|
||||||
var timelineName = (string)timelineEntry.Key;
|
var timelineName = (string)timelineEntry.Key;
|
||||||
if (timelineName == "rotate") {
|
if (timelineName == "rotate")
|
||||||
var timeline = new RotateTimeline(values.Count);
|
timelines.Add(ReadTimeline(ref keyMapEnumerator, new RotateTimeline(values.Count, values.Count, boneIndex), 0, 1));
|
||||||
timeline.boneIndex = boneIndex;
|
else if (timelineName == "translate") {
|
||||||
|
TranslateTimeline timeline = new TranslateTimeline(values.Count, values.Count << 1, boneIndex);
|
||||||
int frameIndex = 0;
|
timelines.Add(ReadTimeline(ref keyMapEnumerator, timeline, "x", "y", 0, scale));
|
||||||
foreach (Dictionary<string, Object> valueMap in values) {
|
}
|
||||||
timeline.SetFrame(frameIndex, GetFloat(valueMap, "time", 0), GetFloat(valueMap, "angle", 0));
|
else if (timelineName == "scale") {
|
||||||
ReadCurve(valueMap, timeline, frameIndex);
|
ScaleTimeline timeline = new ScaleTimeline(values.Count, values.Count << 1, boneIndex);
|
||||||
frameIndex++;
|
timelines.Add(ReadTimeline(ref keyMapEnumerator, timeline, "x", "y", 1, 1));
|
||||||
}
|
}
|
||||||
timelines.Add(timeline);
|
else if (timelineName == "shear") {
|
||||||
duration = Math.Max(duration, timeline.frames[(timeline.FrameCount - 1) * RotateTimeline.ENTRIES]);
|
ShearTimeline timeline = new ShearTimeline(values.Count, values.Count << 1, boneIndex);
|
||||||
|
timelines.Add(ReadTimeline(ref keyMapEnumerator, timeline, "x", "y", 0, 1));
|
||||||
} else if (timelineName == "translate" || timelineName == "scale" || timelineName == "shear") {
|
|
||||||
TranslateTimeline timeline;
|
|
||||||
float timelineScale = 1, defaultValue = 0;
|
|
||||||
if (timelineName == "scale") {
|
|
||||||
timeline = new ScaleTimeline(values.Count);
|
|
||||||
defaultValue = 1;
|
|
||||||
}
|
|
||||||
else if (timelineName == "shear")
|
|
||||||
timeline = new ShearTimeline(values.Count);
|
|
||||||
else {
|
|
||||||
timeline = new TranslateTimeline(values.Count);
|
|
||||||
timelineScale = scale;
|
|
||||||
}
|
|
||||||
timeline.boneIndex = boneIndex;
|
|
||||||
|
|
||||||
int frameIndex = 0;
|
|
||||||
foreach (Dictionary<string, Object> valueMap in values) {
|
|
||||||
float time = GetFloat(valueMap, "time", 0);
|
|
||||||
float x = GetFloat(valueMap, "x", defaultValue);
|
|
||||||
float y = GetFloat(valueMap, "y", defaultValue);
|
|
||||||
timeline.SetFrame(frameIndex, time, x * timelineScale, y * timelineScale);
|
|
||||||
ReadCurve(valueMap, timeline, frameIndex);
|
|
||||||
frameIndex++;
|
|
||||||
}
|
|
||||||
timelines.Add(timeline);
|
|
||||||
duration = Math.Max(duration, timeline.frames[(timeline.FrameCount - 1) * TranslateTimeline.ENTRIES]);
|
|
||||||
|
|
||||||
} else
|
} else
|
||||||
throw new Exception("Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")");
|
throw new Exception("Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")");
|
||||||
}
|
}
|
||||||
@ -638,40 +684,82 @@ namespace Spine {
|
|||||||
|
|
||||||
// IK constraint timelines.
|
// IK constraint timelines.
|
||||||
if (map.ContainsKey("ik")) {
|
if (map.ContainsKey("ik")) {
|
||||||
foreach (KeyValuePair<string, Object> constraintMap in (Dictionary<string, Object>)map["ik"]) {
|
foreach (KeyValuePair<string, Object> timelineMap in (Dictionary<string, Object>)map["ik"]) {
|
||||||
IkConstraintData constraint = skeletonData.FindIkConstraint(constraintMap.Key);
|
var timelineMapValues = (List<Object>)timelineMap.Value;
|
||||||
var values = (List<Object>)constraintMap.Value;
|
var keyMapEnumerator = timelineMapValues.GetEnumerator();
|
||||||
var timeline = new IkConstraintTimeline(values.Count);
|
bool hasNext = keyMapEnumerator.MoveNext();
|
||||||
timeline.ikConstraintIndex = skeletonData.ikConstraints.IndexOf(constraint);
|
if (!hasNext) continue;
|
||||||
int frameIndex = 0;
|
var keyMap = (Dictionary<string, Object>)keyMapEnumerator.Current;
|
||||||
foreach (Dictionary<string, Object> valueMap in values) {
|
IkConstraintData constraint = skeletonData.FindIkConstraint(timelineMap.Key);
|
||||||
timeline.SetFrame(frameIndex, GetFloat(valueMap, "time", 0), GetFloat(valueMap, "mix", 1),
|
IkConstraintTimeline timeline = new IkConstraintTimeline(timelineMapValues.Count, timelineMapValues.Count << 1,
|
||||||
GetFloat(valueMap, "softness", 0) * scale, GetBoolean(valueMap, "bendPositive", true) ? 1 : -1,
|
skeletonData.IkConstraints.IndexOf(constraint));
|
||||||
GetBoolean(valueMap, "compress", false), GetBoolean(valueMap, "stretch", false));
|
float time = GetFloat(keyMap, "time", 0);
|
||||||
ReadCurve(valueMap, timeline, frameIndex);
|
float mix = GetFloat(keyMap, "mix", 1), softness = GetFloat(keyMap, "softness", 0) * scale;
|
||||||
frameIndex++;
|
for (int frame = 0, bezier = 0; ; frame++) {
|
||||||
|
timeline.SetFrame(frame, time, mix, softness, GetBoolean(keyMap, "bendPositive", true) ? 1 : -1,
|
||||||
|
GetBoolean(keyMap, "compress", false), GetBoolean(keyMap, "stretch", false));
|
||||||
|
hasNext = keyMapEnumerator.MoveNext();
|
||||||
|
if (!hasNext) {
|
||||||
|
timeline.Shrink(bezier);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
var nextMap = (Dictionary<string, Object>)keyMapEnumerator.Current;
|
||||||
|
float time2 = GetFloat(nextMap, "time", 0);
|
||||||
|
float mix2 = GetFloat(nextMap, "mix", 1), softness2 = GetFloat(nextMap, "softness", 0) * scale;
|
||||||
|
if (keyMap.ContainsKey("curve")) {
|
||||||
|
object curve = keyMap["curve"];
|
||||||
|
bezier = ReadCurve(curve, timeline, bezier, frame, 0, time, time2, mix, mix2, 1);
|
||||||
|
bezier = ReadCurve(curve, timeline, bezier, frame, 1, time, time2, softness, softness2, scale);
|
||||||
|
}
|
||||||
|
time = time2;
|
||||||
|
mix = mix2;
|
||||||
|
softness = softness2;
|
||||||
|
keyMap = nextMap;
|
||||||
}
|
}
|
||||||
timelines.Add(timeline);
|
timelines.Add(timeline);
|
||||||
duration = Math.Max(duration, timeline.frames[(timeline.FrameCount - 1) * IkConstraintTimeline.ENTRIES]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transform constraint timelines.
|
// Transform constraint timelines.
|
||||||
if (map.ContainsKey("transform")) {
|
if (map.ContainsKey("transform")) {
|
||||||
foreach (KeyValuePair<string, Object> constraintMap in (Dictionary<string, Object>)map["transform"]) {
|
foreach (KeyValuePair<string, Object> timelineMap in (Dictionary<string, Object>)map["transform"]) {
|
||||||
TransformConstraintData constraint = skeletonData.FindTransformConstraint(constraintMap.Key);
|
var timelineMapValues = (List<Object>)timelineMap.Value;
|
||||||
var values = (List<Object>)constraintMap.Value;
|
var keyMapEnumerator = timelineMapValues.GetEnumerator();
|
||||||
var timeline = new TransformConstraintTimeline(values.Count);
|
bool hasNext = keyMapEnumerator.MoveNext();
|
||||||
timeline.transformConstraintIndex = skeletonData.transformConstraints.IndexOf(constraint);
|
if (!hasNext) continue;
|
||||||
int frameIndex = 0;
|
var keyMap = (Dictionary<string, Object>)keyMapEnumerator.Current;
|
||||||
foreach (Dictionary<string, Object> valueMap in values) {
|
TransformConstraintData constraint = skeletonData.FindTransformConstraint(timelineMap.Key);
|
||||||
timeline.SetFrame(frameIndex, GetFloat(valueMap, "time", 0), GetFloat(valueMap, "rotateMix", 1),
|
TransformConstraintTimeline timeline = new TransformConstraintTimeline(timelineMapValues.Count, timelineMapValues.Count << 2,
|
||||||
GetFloat(valueMap, "translateMix", 1), GetFloat(valueMap, "scaleMix", 1), GetFloat(valueMap, "shearMix", 1));
|
skeletonData.TransformConstraints.IndexOf(constraint));
|
||||||
ReadCurve(valueMap, timeline, frameIndex);
|
float time = GetFloat(keyMap, "time", 0);
|
||||||
frameIndex++;
|
float rotateMix = GetFloat(keyMap, "rotateMix", 1), translateMix = GetFloat(keyMap, "translateMix", 1);
|
||||||
|
float scaleMix = GetFloat(keyMap, "scaleMix", 1), shearMix = GetFloat(keyMap, "shearMix", 1);
|
||||||
|
for (int frame = 0, bezier = 0; ; frame++) {
|
||||||
|
timeline.SetFrame(frame, time, rotateMix, translateMix, scaleMix, shearMix);
|
||||||
|
hasNext = keyMapEnumerator.MoveNext();
|
||||||
|
if (!hasNext) {
|
||||||
|
timeline.Shrink(bezier);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
var nextMap = (Dictionary<string, Object>)keyMapEnumerator.Current;
|
||||||
|
float time2 = GetFloat(nextMap, "time", 0);
|
||||||
|
float rotateMix2 = GetFloat(nextMap, "rotateMix", 1), translateMix2 = GetFloat(nextMap, "translateMix", 1);
|
||||||
|
float scaleMix2 = GetFloat(nextMap, "scaleMix", 1), shearMix2 = GetFloat(nextMap, "shearMix", 1);
|
||||||
|
if (keyMap.ContainsKey("curve")) {
|
||||||
|
object curve = keyMap["curve"];
|
||||||
|
bezier = ReadCurve(curve, timeline, bezier, frame, 0, time, time2, rotateMix, rotateMix2, 1);
|
||||||
|
bezier = ReadCurve(curve, timeline, bezier, frame, 1, time, time2, translateMix, translateMix2, 1);
|
||||||
|
bezier = ReadCurve(curve, timeline, bezier, frame, 2, time, time2, scaleMix, scaleMix2, 1);
|
||||||
|
bezier = ReadCurve(curve, timeline, bezier, frame, 3, time, time2, shearMix, shearMix2, 1);
|
||||||
|
}
|
||||||
|
time = time2;
|
||||||
|
rotateMix = rotateMix2;
|
||||||
|
translateMix = translateMix2;
|
||||||
|
scaleMix = scaleMix2;
|
||||||
|
shearMix = shearMix2;
|
||||||
|
keyMap = nextMap;
|
||||||
}
|
}
|
||||||
timelines.Add(timeline);
|
timelines.Add(timeline);
|
||||||
duration = Math.Max(duration, timeline.frames[(timeline.FrameCount - 1) * TransformConstraintTimeline.ENTRIES]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -684,40 +772,22 @@ namespace Spine {
|
|||||||
var timelineMap = (Dictionary<string, Object>)constraintMap.Value;
|
var timelineMap = (Dictionary<string, Object>)constraintMap.Value;
|
||||||
foreach (KeyValuePair<string, Object> timelineEntry in timelineMap) {
|
foreach (KeyValuePair<string, Object> timelineEntry in timelineMap) {
|
||||||
var values = (List<Object>)timelineEntry.Value;
|
var values = (List<Object>)timelineEntry.Value;
|
||||||
|
var keyMapEnumerator = values.GetEnumerator();
|
||||||
|
bool hasNext = keyMapEnumerator.MoveNext();
|
||||||
|
if (!hasNext) continue;
|
||||||
var timelineName = (string)timelineEntry.Key;
|
var timelineName = (string)timelineEntry.Key;
|
||||||
if (timelineName == "position" || timelineName == "spacing") {
|
if (timelineName == "position") {
|
||||||
PathConstraintPositionTimeline timeline;
|
CurveTimeline1 timeline = new PathConstraintPositionTimeline(values.Count, values.Count, index);
|
||||||
float timelineScale = 1;
|
timelines.Add(ReadTimeline(ref keyMapEnumerator, timeline, 0, data.positionMode == PositionMode.Fixed ? scale : 1));
|
||||||
if (timelineName == "spacing") {
|
}
|
||||||
timeline = new PathConstraintSpacingTimeline(values.Count);
|
else if (timelineName == "spacing") {
|
||||||
if (data.spacingMode == SpacingMode.Length || data.spacingMode == SpacingMode.Fixed) timelineScale = scale;
|
CurveTimeline1 timeline = new PathConstraintSpacingTimeline(values.Count, values.Count, index);
|
||||||
}
|
timelines.Add(ReadTimeline(ref keyMapEnumerator, timeline, 0,
|
||||||
else {
|
data.spacingMode == SpacingMode.Length || data.spacingMode == SpacingMode.Fixed ? scale : 1));
|
||||||
timeline = new PathConstraintPositionTimeline(values.Count);
|
|
||||||
if (data.positionMode == PositionMode.Fixed) timelineScale = scale;
|
|
||||||
}
|
|
||||||
timeline.pathConstraintIndex = index;
|
|
||||||
int frameIndex = 0;
|
|
||||||
foreach (Dictionary<string, Object> valueMap in values) {
|
|
||||||
timeline.SetFrame(frameIndex, GetFloat(valueMap, "time", 0), GetFloat(valueMap, timelineName, 0) * timelineScale);
|
|
||||||
ReadCurve(valueMap, timeline, frameIndex);
|
|
||||||
frameIndex++;
|
|
||||||
}
|
|
||||||
timelines.Add(timeline);
|
|
||||||
duration = Math.Max(duration, timeline.frames[(timeline.FrameCount - 1) * PathConstraintPositionTimeline.ENTRIES]);
|
|
||||||
}
|
}
|
||||||
else if (timelineName == "mix") {
|
else if (timelineName == "mix") {
|
||||||
PathConstraintMixTimeline timeline = new PathConstraintMixTimeline(values.Count);
|
CurveTimeline2 timeline = new PathConstraintMixTimeline(values.Count, values.Count << 1, index);
|
||||||
timeline.pathConstraintIndex = index;
|
timelines.Add(ReadTimeline(ref keyMapEnumerator, timeline, "rotateMix", "translateMix", 1, 1));
|
||||||
int frameIndex = 0;
|
|
||||||
foreach (Dictionary<string, Object> valueMap in values) {
|
|
||||||
timeline.SetFrame(frameIndex, GetFloat(valueMap, "time", 0), GetFloat(valueMap, "rotateMix", 1),
|
|
||||||
GetFloat(valueMap, "translateMix", 1));
|
|
||||||
ReadCurve(valueMap, timeline, frameIndex);
|
|
||||||
frameIndex++;
|
|
||||||
}
|
|
||||||
timelines.Add(timeline);
|
|
||||||
duration = Math.Max(duration, timeline.frames[(timeline.FrameCount - 1) * PathConstraintMixTimeline.ENTRIES]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -731,26 +801,26 @@ namespace Spine {
|
|||||||
int slotIndex = skeletonData.FindSlotIndex(slotMap.Key);
|
int slotIndex = skeletonData.FindSlotIndex(slotMap.Key);
|
||||||
if (slotIndex == -1) throw new Exception("Slot not found: " + slotMap.Key);
|
if (slotIndex == -1) throw new Exception("Slot not found: " + slotMap.Key);
|
||||||
foreach (KeyValuePair<string, Object> timelineMap in (Dictionary<string, Object>)slotMap.Value) {
|
foreach (KeyValuePair<string, Object> timelineMap in (Dictionary<string, Object>)slotMap.Value) {
|
||||||
var values = (List<Object>)timelineMap.Value;
|
var timelineMapValues = (List<Object>)timelineMap.Value;
|
||||||
|
var keyMapEnumerator = timelineMapValues.GetEnumerator();
|
||||||
|
bool hasNext = keyMapEnumerator.MoveNext();
|
||||||
|
if (!hasNext) continue;
|
||||||
|
var keyMap = (Dictionary<string, Object>)keyMapEnumerator.Current;
|
||||||
VertexAttachment attachment = (VertexAttachment)skin.GetAttachment(slotIndex, timelineMap.Key);
|
VertexAttachment attachment = (VertexAttachment)skin.GetAttachment(slotIndex, timelineMap.Key);
|
||||||
if (attachment == null) throw new Exception("Deform attachment not found: " + timelineMap.Key);
|
if (attachment == null) throw new Exception("Deform attachment not found: " + timelineMap.Key);
|
||||||
bool weighted = attachment.bones != null;
|
bool weighted = attachment.bones != null;
|
||||||
float[] vertices = attachment.vertices;
|
float[] vertices = attachment.vertices;
|
||||||
int deformLength = weighted ? vertices.Length / 3 * 2 : vertices.Length;
|
int deformLength = weighted ? (vertices.Length / 3) << 1 : vertices.Length;
|
||||||
|
DeformTimeline timeline = new DeformTimeline(timelineMapValues.Count, timelineMapValues.Count, slotIndex, attachment);
|
||||||
var timeline = new DeformTimeline(values.Count);
|
float time = GetFloat(keyMap, "time", 0);
|
||||||
timeline.slotIndex = slotIndex;
|
for (int frame = 0, bezier = 0; ; frame++) {
|
||||||
timeline.attachment = attachment;
|
|
||||||
|
|
||||||
int frameIndex = 0;
|
|
||||||
foreach (Dictionary<string, Object> valueMap in values) {
|
|
||||||
float[] deform;
|
float[] deform;
|
||||||
if (!valueMap.ContainsKey("vertices")) {
|
if (!keyMap.ContainsKey("vertices")) {
|
||||||
deform = weighted ? new float[deformLength] : vertices;
|
deform = weighted ? new float[deformLength] : vertices;
|
||||||
} else {
|
} else {
|
||||||
deform = new float[deformLength];
|
deform = new float[deformLength];
|
||||||
int start = GetInt(valueMap, "offset", 0);
|
int start = GetInt(keyMap, "offset", 0);
|
||||||
float[] verticesValue = GetFloatArray(valueMap, "vertices", 1);
|
float[] verticesValue = GetFloatArray(keyMap, "vertices", 1);
|
||||||
Array.Copy(verticesValue, 0, deform, start, verticesValue.Length);
|
Array.Copy(verticesValue, 0, deform, start, verticesValue.Length);
|
||||||
if (scale != 1) {
|
if (scale != 1) {
|
||||||
for (int i = start, n = i + verticesValue.Length; i < n; i++)
|
for (int i = start, n = i + verticesValue.Length; i < n; i++)
|
||||||
@ -763,12 +833,22 @@ namespace Spine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
timeline.SetFrame(frameIndex, GetFloat(valueMap, "time", 0), deform);
|
timeline.SetFrame(frame, time, deform);
|
||||||
ReadCurve(valueMap, timeline, frameIndex);
|
hasNext = keyMapEnumerator.MoveNext();
|
||||||
frameIndex++;
|
if (!hasNext) {
|
||||||
|
timeline.Shrink(bezier);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
var nextMap = (Dictionary<string, Object>)keyMapEnumerator.Current;
|
||||||
|
float time2 = GetFloat(nextMap, "time", 0);
|
||||||
|
if (keyMap.ContainsKey("curve")) {
|
||||||
|
object curve = keyMap["curve"];
|
||||||
|
bezier = ReadCurve(curve, timeline, bezier, frame, 0, time, time2, 0, 1, 1);
|
||||||
|
}
|
||||||
|
time = time2;
|
||||||
|
keyMap = nextMap;
|
||||||
}
|
}
|
||||||
timelines.Add(timeline);
|
timelines.Add(timeline);
|
||||||
duration = Math.Max(duration, timeline.frames[timeline.FrameCount - 1]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -779,7 +859,7 @@ namespace Spine {
|
|||||||
var values = (List<Object>)map[map.ContainsKey("drawOrder") ? "drawOrder" : "draworder"];
|
var values = (List<Object>)map[map.ContainsKey("drawOrder") ? "drawOrder" : "draworder"];
|
||||||
var timeline = new DrawOrderTimeline(values.Count);
|
var timeline = new DrawOrderTimeline(values.Count);
|
||||||
int slotCount = skeletonData.slots.Count;
|
int slotCount = skeletonData.slots.Count;
|
||||||
int frameIndex = 0;
|
int frame = 0;
|
||||||
foreach (Dictionary<string, Object> drawOrderMap in values) {
|
foreach (Dictionary<string, Object> drawOrderMap in values) {
|
||||||
int[] drawOrder = null;
|
int[] drawOrder = null;
|
||||||
if (drawOrderMap.ContainsKey("offsets")) {
|
if (drawOrderMap.ContainsKey("offsets")) {
|
||||||
@ -806,17 +886,17 @@ namespace Spine {
|
|||||||
for (int i = slotCount - 1; i >= 0; i--)
|
for (int i = slotCount - 1; i >= 0; i--)
|
||||||
if (drawOrder[i] == -1) drawOrder[i] = unchanged[--unchangedIndex];
|
if (drawOrder[i] == -1) drawOrder[i] = unchanged[--unchangedIndex];
|
||||||
}
|
}
|
||||||
timeline.SetFrame(frameIndex++, GetFloat(drawOrderMap, "time", 0), drawOrder);
|
timeline.SetFrame(frame, GetFloat(drawOrderMap, "time", 0), drawOrder);
|
||||||
|
++frame;
|
||||||
}
|
}
|
||||||
timelines.Add(timeline);
|
timelines.Add(timeline);
|
||||||
duration = Math.Max(duration, timeline.frames[timeline.FrameCount - 1]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Event timeline.
|
// Event timeline.
|
||||||
if (map.ContainsKey("events")) {
|
if (map.ContainsKey("events")) {
|
||||||
var eventsMap = (List<Object>)map["events"];
|
var eventsMap = (List<Object>)map["events"];
|
||||||
var timeline = new EventTimeline(eventsMap.Count);
|
var timeline = new EventTimeline(eventsMap.Count);
|
||||||
int frameIndex = 0;
|
int frame = 0;
|
||||||
foreach (Dictionary<string, Object> eventMap in eventsMap) {
|
foreach (Dictionary<string, Object> eventMap in eventsMap) {
|
||||||
EventData eventData = skeletonData.FindEvent((string)eventMap["name"]);
|
EventData eventData = skeletonData.FindEvent((string)eventMap["name"]);
|
||||||
if (eventData == null) throw new Exception("Event not found: " + eventMap["name"]);
|
if (eventData == null) throw new Exception("Event not found: " + eventMap["name"]);
|
||||||
@ -829,39 +909,97 @@ namespace Spine {
|
|||||||
e.volume = GetFloat(eventMap, "volume", eventData.Volume);
|
e.volume = GetFloat(eventMap, "volume", eventData.Volume);
|
||||||
e.balance = GetFloat(eventMap, "balance", eventData.Balance);
|
e.balance = GetFloat(eventMap, "balance", eventData.Balance);
|
||||||
}
|
}
|
||||||
timeline.SetFrame(frameIndex++, e);
|
timeline.SetFrame(frame, e);
|
||||||
|
++frame;
|
||||||
}
|
}
|
||||||
timelines.Add(timeline);
|
timelines.Add(timeline);
|
||||||
duration = Math.Max(duration, timeline.frames[timeline.FrameCount - 1]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
timelines.TrimExcess();
|
timelines.TrimExcess();
|
||||||
|
float duration = 0;
|
||||||
|
var items = timelines.Items;
|
||||||
|
for (int i = 0, n = timelines.Count; i < n; i++)
|
||||||
|
duration = Math.Max(duration, items[i].Duration);
|
||||||
skeletonData.animations.Add(new Animation(name, timelines, duration));
|
skeletonData.animations.Add(new Animation(name, timelines, duration));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ReadCurve (Dictionary<string, Object> valueMap, CurveTimeline timeline, int frameIndex) {
|
static Timeline ReadTimeline (ref List<object>.Enumerator keyMapEnumerator, CurveTimeline1 timeline, float defaultValue, float scale) {
|
||||||
if (!valueMap.ContainsKey("curve"))
|
var keyMap = (Dictionary<string, Object>)keyMapEnumerator.Current;
|
||||||
return;
|
float time = GetFloat(keyMap, "time", 0);
|
||||||
Object curveObject = valueMap["curve"];
|
float value = GetFloat(keyMap, "value", defaultValue) * scale;
|
||||||
if (curveObject is string)
|
int bezier = 0;
|
||||||
timeline.SetStepped(frameIndex);
|
for (int frame = 0; ; frame++) {
|
||||||
else
|
timeline.SetFrame(frame, time, value);
|
||||||
timeline.SetCurve(frameIndex, (float)curveObject, GetFloat(valueMap, "c2", 0), GetFloat(valueMap, "c3", 1), GetFloat(valueMap, "c4", 1));
|
bool hasNext = keyMapEnumerator.MoveNext();
|
||||||
|
if (!hasNext)
|
||||||
|
break;
|
||||||
|
var nextMap = (Dictionary<string, Object>)keyMapEnumerator.Current;
|
||||||
|
float time2 = GetFloat(nextMap, "time", 0);
|
||||||
|
float value2 = GetFloat(nextMap, "value", defaultValue) * scale;
|
||||||
|
if (keyMap.ContainsKey("curve")) {
|
||||||
|
object curve = keyMap["curve"];
|
||||||
|
bezier = ReadCurve(curve, timeline, bezier, frame, 0, time, time2, value, value2, scale);
|
||||||
|
}
|
||||||
|
time = time2;
|
||||||
|
value = value2;
|
||||||
|
keyMap = nextMap;
|
||||||
|
}
|
||||||
|
timeline.Shrink(bezier);
|
||||||
|
return timeline;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class LinkedMesh {
|
static Timeline ReadTimeline (ref List<object>.Enumerator keyMapEnumerator, CurveTimeline2 timeline, String name1, String name2, float defaultValue,
|
||||||
internal string parent, skin;
|
float scale) {
|
||||||
internal int slotIndex;
|
|
||||||
internal MeshAttachment mesh;
|
|
||||||
internal bool inheritDeform;
|
|
||||||
|
|
||||||
public LinkedMesh (MeshAttachment mesh, string skin, int slotIndex, string parent, bool inheritDeform) {
|
var keyMap = (Dictionary<string, Object>)keyMapEnumerator.Current;
|
||||||
this.mesh = mesh;
|
float time = GetFloat(keyMap, "time", 0);
|
||||||
this.skin = skin;
|
float value1 = GetFloat(keyMap, name1, defaultValue) * scale, value2 = GetFloat(keyMap, name2, defaultValue) * scale;
|
||||||
this.slotIndex = slotIndex;
|
int bezier = 0;
|
||||||
this.parent = parent;
|
for (int frame = 0; ; frame++) {
|
||||||
this.inheritDeform = inheritDeform;
|
timeline.SetFrame(frame, time, value1, value2);
|
||||||
|
bool hasNext = keyMapEnumerator.MoveNext();
|
||||||
|
if (!hasNext)
|
||||||
|
break;
|
||||||
|
var nextMap = (Dictionary<string, Object>)keyMapEnumerator.Current;
|
||||||
|
float time2 = GetFloat(nextMap, "time", 0);
|
||||||
|
float nvalue1 = GetFloat(nextMap, name1, defaultValue) * scale, nvalue2 = GetFloat(nextMap, name2, defaultValue) * scale;
|
||||||
|
if (keyMap.ContainsKey("curve")) {
|
||||||
|
object curve = keyMap["curve"];
|
||||||
|
bezier = ReadCurve(curve, timeline, bezier, frame, 0, time, time2, value1, nvalue1, scale);
|
||||||
|
bezier = ReadCurve(curve, timeline, bezier, frame, 1, time, time2, value2, nvalue2, scale);
|
||||||
|
}
|
||||||
|
time = time2;
|
||||||
|
value1 = nvalue1;
|
||||||
|
value2 = nvalue2;
|
||||||
|
keyMap = nextMap;
|
||||||
}
|
}
|
||||||
|
timeline.Shrink(bezier);
|
||||||
|
return timeline;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ReadCurve (object curve, CurveTimeline timeline, int bezier, int frame, int value, float time1, float time2,
|
||||||
|
float value1, float value2, float scale) {
|
||||||
|
|
||||||
|
if (curve is string) {
|
||||||
|
if (value != 0) timeline.SetStepped(frame);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var curveValues = (List<object>)curve;
|
||||||
|
int index = value << 2;
|
||||||
|
float cx1 = (float)curveValues[index];
|
||||||
|
++index;
|
||||||
|
float cy1 = ((float)curveValues[index]) * scale;
|
||||||
|
++index;
|
||||||
|
float cx2 = (float)curveValues[index];
|
||||||
|
++index;
|
||||||
|
float cy2 = (float)curveValues[index] * scale;
|
||||||
|
SetBezier(timeline, frame, value, bezier++, time1, value1, cx1, cy1, cx2, cy2, time2, value2);
|
||||||
|
}
|
||||||
|
return bezier;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void SetBezier (CurveTimeline timeline, int frame, int value, int bezier, float time1, float value1, float cx1, float cy1,
|
||||||
|
float cx2, float cy2, float time2, float value2) {
|
||||||
|
timeline.SetBezier(bezier, frame, value, time1, value1, cx1, cy1, cx2, cy2, time2, value2);
|
||||||
}
|
}
|
||||||
|
|
||||||
static float[] GetFloatArray(Dictionary<string, Object> map, string name, float scale) {
|
static float[] GetFloatArray(Dictionary<string, Object> map, string name, float scale) {
|
||||||
|
|||||||
92
spine-csharp/src/SkeletonLoader.cs
Normal file
92
spine-csharp/src/SkeletonLoader.cs
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
/******************************************************************************
|
||||||
|
* Spine Runtimes License Agreement
|
||||||
|
* Last updated January 1, 2020. Replaces all prior versions.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2013-2020, Esoteric Software LLC
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
*
|
||||||
|
* Otherwise, it is permitted to integrate the Spine Runtimes into software
|
||||||
|
* or otherwise create derivative works of the Spine Runtimes (collectively,
|
||||||
|
* "Products"), provided that each user of the Products must obtain their own
|
||||||
|
* Spine Editor license and redistribution of the Products in any form must
|
||||||
|
* include this license and copyright notice.
|
||||||
|
*
|
||||||
|
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
|
||||||
|
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
|
||||||
|
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
|
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Spine {
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Base class for loading skeleton data from a file.
|
||||||
|
/// <para>
|
||||||
|
/// See<a href="http://esotericsoftware.com/spine-loading-skeleton-data#JSON-and-binary-data">JSON and binary data</a> in the
|
||||||
|
/// Spine Runtimes Guide.</para>
|
||||||
|
/// </summary>
|
||||||
|
public abstract class SkeletonLoader {
|
||||||
|
protected readonly AttachmentLoader attachmentLoader;
|
||||||
|
protected float scale = 1;
|
||||||
|
protected readonly List<LinkedMesh> linkedMeshes = new List<LinkedMesh>();
|
||||||
|
|
||||||
|
/// <summary>Creates a skeleton loader that loads attachments using an <see cref="AtlasAttachmentLoader"/> with the specified atlas.
|
||||||
|
/// </summary>
|
||||||
|
public SkeletonLoader (params Atlas[] atlasArray) {
|
||||||
|
attachmentLoader = new AtlasAttachmentLoader(atlasArray);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Creates a skeleton loader that loads attachments using the specified attachment loader.
|
||||||
|
/// <para>See <a href='http://esotericsoftware.com/spine-loading-skeleton-data#JSON-and-binary-data'>Loading skeleton data</a> in the
|
||||||
|
/// Spine Runtimes Guide.</para></summary>
|
||||||
|
public SkeletonLoader (AttachmentLoader attachmentLoader) {
|
||||||
|
if (attachmentLoader == null) throw new ArgumentNullException("attachmentLoader", "attachmentLoader cannot be null.");
|
||||||
|
this.attachmentLoader = attachmentLoader;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Scales bone positions, image sizes, and translations as they are loaded. This allows different size images to be used at
|
||||||
|
/// runtime than were used in Spine.
|
||||||
|
/// <para>
|
||||||
|
/// See <a href="http://esotericsoftware.com/spine-loading-skeleton-data#Scaling">Scaling</a> in the Spine Runtimes Guide.</para>
|
||||||
|
/// </summary>
|
||||||
|
public float Scale {
|
||||||
|
get { return scale; }
|
||||||
|
set {
|
||||||
|
if (scale == 0) throw new ArgumentNullException("scale", "scale cannot be 0.");
|
||||||
|
this.scale = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract SkeletonData ReadSkeletonData (string path);
|
||||||
|
|
||||||
|
protected class LinkedMesh {
|
||||||
|
internal string parent, skin;
|
||||||
|
internal int slotIndex;
|
||||||
|
internal MeshAttachment mesh;
|
||||||
|
internal bool inheritDeform;
|
||||||
|
|
||||||
|
public LinkedMesh (MeshAttachment mesh, string skin, int slotIndex, string parent, bool inheritDeform) {
|
||||||
|
this.mesh = mesh;
|
||||||
|
this.skin = skin;
|
||||||
|
this.slotIndex = slotIndex;
|
||||||
|
this.parent = parent;
|
||||||
|
this.inheritDeform = inheritDeform;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -39,6 +39,8 @@ namespace Spine {
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class Skin {
|
public class Skin {
|
||||||
internal string name;
|
internal string name;
|
||||||
|
// Difference to reference implementation: using Dictionary<SkinKey, SkinEntry> instead of HashSet<SkinEntry>.
|
||||||
|
// Reason is that there is no efficient way to replace or access an already added element, losing any benefits.
|
||||||
private Dictionary<SkinKey, SkinEntry> attachments = new Dictionary<SkinKey, SkinEntry>(SkinKeyComparer.Instance);
|
private Dictionary<SkinKey, SkinEntry> attachments = new Dictionary<SkinKey, SkinEntry>(SkinKeyComparer.Instance);
|
||||||
internal readonly ExposedList<BoneData> bones = new ExposedList<BoneData>();
|
internal readonly ExposedList<BoneData> bones = new ExposedList<BoneData>();
|
||||||
internal readonly ExposedList<ConstraintData> constraints = new ExposedList<ConstraintData>();
|
internal readonly ExposedList<ConstraintData> constraints = new ExposedList<ConstraintData>();
|
||||||
@ -58,7 +60,6 @@ namespace Spine {
|
|||||||
/// If the name already exists for the slot, the previous value is replaced.</summary>
|
/// If the name already exists for the slot, the previous value is replaced.</summary>
|
||||||
public void SetAttachment (int slotIndex, string name, Attachment attachment) {
|
public void SetAttachment (int slotIndex, string name, Attachment attachment) {
|
||||||
if (attachment == null) throw new ArgumentNullException("attachment", "attachment cannot be null.");
|
if (attachment == null) throw new ArgumentNullException("attachment", "attachment cannot be null.");
|
||||||
if (slotIndex < 0) throw new ArgumentNullException("slotIndex", "slotIndex must be >= 0.");
|
|
||||||
attachments[new SkinKey(slotIndex, name)] = new SkinEntry(slotIndex, name, attachment);
|
attachments[new SkinKey(slotIndex, name)] = new SkinEntry(slotIndex, name, attachment);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,13 +105,14 @@ namespace Spine {
|
|||||||
|
|
||||||
/// <summary> Removes the attachment in the skin for the specified slot index and name, if any.</summary>
|
/// <summary> Removes the attachment in the skin for the specified slot index and name, if any.</summary>
|
||||||
public void RemoveAttachment (int slotIndex, string name) {
|
public void RemoveAttachment (int slotIndex, string name) {
|
||||||
if (slotIndex < 0) throw new ArgumentOutOfRangeException("slotIndex", "slotIndex must be >= 0");
|
|
||||||
attachments.Remove(new SkinKey(slotIndex, name));
|
attachments.Remove(new SkinKey(slotIndex, name));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Returns all attachments in this skin for the specified slot index.</summary>
|
/// <summary>Returns all attachments in this skin for the specified slot index.</summary>
|
||||||
/// <param name="slotIndex">The target slotIndex. To find the slot index, use <see cref="Spine.Skeleton.FindSlotIndex"/> or <see cref="Spine.SkeletonData.FindSlotIndex"/>
|
/// <param name="slotIndex">The target slotIndex. To find the slot index, use <see cref="Spine.Skeleton.FindSlotIndex"/> or <see cref="Spine.SkeletonData.FindSlotIndex"/>
|
||||||
public void GetAttachments (int slotIndex, List<SkinEntry> attachments) {
|
public void GetAttachments (int slotIndex, List<SkinEntry> attachments) {
|
||||||
|
if (slotIndex < 0) throw new ArgumentException("slotIndex must be >= 0.");
|
||||||
|
if (attachments == null) throw new ArgumentNullException("attachments", "attachments cannot be null.");
|
||||||
foreach (var item in this.attachments) {
|
foreach (var item in this.attachments) {
|
||||||
SkinEntry entry = item.Value;
|
SkinEntry entry = item.Value;
|
||||||
if (entry.slotIndex == slotIndex) attachments.Add(entry);
|
if (entry.slotIndex == slotIndex) attachments.Add(entry);
|
||||||
@ -176,10 +178,14 @@ namespace Spine {
|
|||||||
private struct SkinKey {
|
private struct SkinKey {
|
||||||
internal readonly int slotIndex;
|
internal readonly int slotIndex;
|
||||||
internal readonly string name;
|
internal readonly string name;
|
||||||
|
internal readonly int hashCode;
|
||||||
|
|
||||||
public SkinKey (int slotIndex, string name) {
|
public SkinKey (int slotIndex, string name) {
|
||||||
|
if (slotIndex < 0) throw new ArgumentException("slotIndex must be >= 0.");
|
||||||
|
if (name == null) throw new ArgumentNullException("name", "name cannot be null");
|
||||||
this.slotIndex = slotIndex;
|
this.slotIndex = slotIndex;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
|
this.hashCode = name.GetHashCode() + slotIndex * 37;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -191,7 +197,7 @@ namespace Spine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int IEqualityComparer<SkinKey>.GetHashCode (SkinKey e) {
|
int IEqualityComparer<SkinKey>.GetHashCode (SkinKey e) {
|
||||||
return e.name.GetHashCode() + e.slotIndex * 37;
|
return e.hashCode;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -32,7 +32,7 @@ using System;
|
|||||||
namespace Spine {
|
namespace Spine {
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Stores a slot's current pose. Slots organize attachments for {@link Skeleton#drawOrder} purposes and provide a place to store
|
/// Stores a slot's current pose. Slots organize attachments for <see cref="Skeleton.DrawOrder"/> purposes and provide a place to store
|
||||||
/// state for an attachment.State cannot be stored in an attachment itself because attachments are stateless and may be shared
|
/// state for an attachment.State cannot be stored in an attachment itself because attachments are stateless and may be shared
|
||||||
/// across multiple skeletons.
|
/// across multiple skeletons.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@ -76,12 +76,8 @@ namespace Spine {
|
|||||||
shearMix = constraint.shearMix;
|
shearMix = constraint.shearMix;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Applies the constraint to the constrained bones.</summary>
|
|
||||||
public void Apply () {
|
|
||||||
Update();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Update () {
|
public void Update () {
|
||||||
|
if (rotateMix == 0 && translateMix == 0 && scaleMix == 0 && shearMix == 0) return;
|
||||||
if (data.local) {
|
if (data.local) {
|
||||||
if (data.relative)
|
if (data.relative)
|
||||||
ApplyRelativeLocal();
|
ApplyRelativeLocal();
|
||||||
@ -101,10 +97,9 @@ namespace Spine {
|
|||||||
float ta = target.a, tb = target.b, tc = target.c, td = target.d;
|
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 degRadReflect = ta * td - tb * tc > 0 ? MathUtils.DegRad : -MathUtils.DegRad;
|
||||||
float offsetRotation = data.offsetRotation * degRadReflect, offsetShearY = data.offsetShearY * degRadReflect;
|
float offsetRotation = data.offsetRotation * degRadReflect, offsetShearY = data.offsetShearY * degRadReflect;
|
||||||
var bones = this.bones;
|
var bones = this.bones.Items;
|
||||||
for (int i = 0, n = bones.Count; i < n; i++) {
|
for (int i = 0, n = this.bones.Count; i < n; i++) {
|
||||||
Bone bone = bones.Items[i];
|
Bone bone = bones[i];
|
||||||
bool modified = false;
|
|
||||||
|
|
||||||
if (rotateMix != 0) {
|
if (rotateMix != 0) {
|
||||||
float a = bone.a, b = bone.b, c = bone.c, d = bone.d;
|
float a = bone.a, b = bone.b, c = bone.c, d = bone.d;
|
||||||
@ -118,7 +113,6 @@ namespace Spine {
|
|||||||
bone.b = cos * b - sin * d;
|
bone.b = cos * b - sin * d;
|
||||||
bone.c = sin * a + cos * c;
|
bone.c = sin * a + cos * c;
|
||||||
bone.d = sin * b + cos * d;
|
bone.d = sin * b + cos * d;
|
||||||
modified = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (translateMix != 0) {
|
if (translateMix != 0) {
|
||||||
@ -126,7 +120,6 @@ namespace Spine {
|
|||||||
target.LocalToWorld(data.offsetX, data.offsetY, out tx, out ty); //target.localToWorld(temp.set(data.offsetX, data.offsetY));
|
target.LocalToWorld(data.offsetX, data.offsetY, out tx, out ty); //target.localToWorld(temp.set(data.offsetX, data.offsetY));
|
||||||
bone.worldX += (tx - bone.worldX) * translateMix;
|
bone.worldX += (tx - bone.worldX) * translateMix;
|
||||||
bone.worldY += (ty - bone.worldY) * translateMix;
|
bone.worldY += (ty - bone.worldY) * translateMix;
|
||||||
modified = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (scaleMix > 0) {
|
if (scaleMix > 0) {
|
||||||
@ -138,7 +131,6 @@ namespace Spine {
|
|||||||
if (s != 0) s = (s + ((float)Math.Sqrt(tb * tb + td * td) - s + data.offsetScaleY) * scaleMix) / s;
|
if (s != 0) s = (s + ((float)Math.Sqrt(tb * tb + td * td) - s + data.offsetScaleY) * scaleMix) / s;
|
||||||
bone.b *= s;
|
bone.b *= s;
|
||||||
bone.d *= s;
|
bone.d *= s;
|
||||||
modified = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (shearMix > 0) {
|
if (shearMix > 0) {
|
||||||
@ -152,10 +144,9 @@ namespace Spine {
|
|||||||
float s = (float)Math.Sqrt(b * b + d * d);
|
float s = (float)Math.Sqrt(b * b + d * d);
|
||||||
bone.b = MathUtils.Cos(r) * s;
|
bone.b = MathUtils.Cos(r) * s;
|
||||||
bone.d = MathUtils.Sin(r) * s;
|
bone.d = MathUtils.Sin(r) * s;
|
||||||
modified = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (modified) bone.appliedValid = false;
|
bone.appliedValid = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -165,10 +156,9 @@ namespace Spine {
|
|||||||
float ta = target.a, tb = target.b, tc = target.c, td = target.d;
|
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 degRadReflect = ta * td - tb * tc > 0 ? MathUtils.DegRad : -MathUtils.DegRad;
|
||||||
float offsetRotation = data.offsetRotation * degRadReflect, offsetShearY = data.offsetShearY * degRadReflect;
|
float offsetRotation = data.offsetRotation * degRadReflect, offsetShearY = data.offsetShearY * degRadReflect;
|
||||||
var bones = this.bones;
|
var bones = this.bones.Items;
|
||||||
for (int i = 0, n = bones.Count; i < n; i++) {
|
for (int i = 0, n = this.bones.Count; i < n; i++) {
|
||||||
Bone bone = bones.Items[i];
|
Bone bone = bones[i];
|
||||||
bool modified = false;
|
|
||||||
|
|
||||||
if (rotateMix != 0) {
|
if (rotateMix != 0) {
|
||||||
float a = bone.a, b = bone.b, c = bone.c, d = bone.d;
|
float a = bone.a, b = bone.b, c = bone.c, d = bone.d;
|
||||||
@ -182,7 +172,6 @@ namespace Spine {
|
|||||||
bone.b = cos * b - sin * d;
|
bone.b = cos * b - sin * d;
|
||||||
bone.c = sin * a + cos * c;
|
bone.c = sin * a + cos * c;
|
||||||
bone.d = sin * b + cos * d;
|
bone.d = sin * b + cos * d;
|
||||||
modified = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (translateMix != 0) {
|
if (translateMix != 0) {
|
||||||
@ -190,7 +179,6 @@ namespace Spine {
|
|||||||
target.LocalToWorld(data.offsetX, data.offsetY, out tx, out ty); //target.localToWorld(temp.set(data.offsetX, data.offsetY));
|
target.LocalToWorld(data.offsetX, data.offsetY, out tx, out ty); //target.localToWorld(temp.set(data.offsetX, data.offsetY));
|
||||||
bone.worldX += tx * translateMix;
|
bone.worldX += tx * translateMix;
|
||||||
bone.worldY += ty * translateMix;
|
bone.worldY += ty * translateMix;
|
||||||
modified = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (scaleMix > 0) {
|
if (scaleMix > 0) {
|
||||||
@ -200,7 +188,6 @@ namespace Spine {
|
|||||||
s = ((float)Math.Sqrt(tb * tb + td * td) - 1 + data.offsetScaleY) * scaleMix + 1;
|
s = ((float)Math.Sqrt(tb * tb + td * td) - 1 + data.offsetScaleY) * scaleMix + 1;
|
||||||
bone.b *= s;
|
bone.b *= s;
|
||||||
bone.d *= s;
|
bone.d *= s;
|
||||||
modified = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (shearMix > 0) {
|
if (shearMix > 0) {
|
||||||
@ -213,10 +200,9 @@ namespace Spine {
|
|||||||
float s = (float)Math.Sqrt(b * b + d * d);
|
float s = (float)Math.Sqrt(b * b + d * d);
|
||||||
bone.b = MathUtils.Cos(r) * s;
|
bone.b = MathUtils.Cos(r) * s;
|
||||||
bone.d = MathUtils.Sin(r) * s;
|
bone.d = MathUtils.Sin(r) * s;
|
||||||
modified = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (modified) bone.appliedValid = false;
|
bone.appliedValid = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -224,9 +210,9 @@ namespace Spine {
|
|||||||
float rotateMix = this.rotateMix, translateMix = this.translateMix, scaleMix = this.scaleMix, shearMix = this.shearMix;
|
float rotateMix = this.rotateMix, translateMix = this.translateMix, scaleMix = this.scaleMix, shearMix = this.shearMix;
|
||||||
Bone target = this.target;
|
Bone target = this.target;
|
||||||
if (!target.appliedValid) target.UpdateAppliedTransform();
|
if (!target.appliedValid) target.UpdateAppliedTransform();
|
||||||
var bonesItems = this.bones.Items;
|
var bones = this.bones.Items;
|
||||||
for (int i = 0, n = this.bones.Count; i < n; i++) {
|
for (int i = 0, n = this.bones.Count; i < n; i++) {
|
||||||
Bone bone = bonesItems[i];
|
Bone bone = bones[i];
|
||||||
if (!bone.appliedValid) bone.UpdateAppliedTransform();
|
if (!bone.appliedValid) bone.UpdateAppliedTransform();
|
||||||
|
|
||||||
float rotation = bone.arotation;
|
float rotation = bone.arotation;
|
||||||
@ -263,9 +249,9 @@ namespace Spine {
|
|||||||
float rotateMix = this.rotateMix, translateMix = this.translateMix, scaleMix = this.scaleMix, shearMix = this.shearMix;
|
float rotateMix = this.rotateMix, translateMix = this.translateMix, scaleMix = this.scaleMix, shearMix = this.shearMix;
|
||||||
Bone target = this.target;
|
Bone target = this.target;
|
||||||
if (!target.appliedValid) target.UpdateAppliedTransform();
|
if (!target.appliedValid) target.UpdateAppliedTransform();
|
||||||
var bonesItems = this.bones.Items;
|
var bones = this.bones.Items;
|
||||||
for (int i = 0, n = this.bones.Count; i < n; i++) {
|
for (int i = 0, n = this.bones.Count; i < n; i++) {
|
||||||
Bone bone = bonesItems[i];
|
Bone bone = bones[i];
|
||||||
if (!bone.appliedValid) bone.UpdateAppliedTransform();
|
if (!bone.appliedValid) bone.UpdateAppliedTransform();
|
||||||
|
|
||||||
float rotation = bone.arotation;
|
float rotation = bone.arotation;
|
||||||
|
|||||||
@ -61,32 +61,35 @@ namespace Spine.Unity.Examples {
|
|||||||
|
|
||||||
// Build a reference collection of timelines to match
|
// Build a reference collection of timelines to match
|
||||||
// and a collection of dummy timelines that can be used to fill-in missing items.
|
// and a collection of dummy timelines that can be used to fill-in missing items.
|
||||||
var timelineDictionary = new Dictionary<int, Spine.Timeline>();
|
var timelineDictionary = new Dictionary<string, Spine.Timeline>();
|
||||||
foreach (var animation in animations) {
|
foreach (var animation in animations) {
|
||||||
foreach (var timeline in animation.Timelines) {
|
foreach (var timeline in animation.Timelines) {
|
||||||
if (timeline is EventTimeline) continue;
|
if (timeline is EventTimeline) continue;
|
||||||
|
|
||||||
int propertyID = timeline.PropertyId;
|
foreach (string propertyId in timeline.PropertyIds) {
|
||||||
if (!timelineDictionary.ContainsKey(propertyID)) {
|
if (!timelineDictionary.ContainsKey(propertyId)) {
|
||||||
timelineDictionary.Add(propertyID, GetFillerTimeline(timeline, skeletonData));
|
timelineDictionary.Add(propertyId, GetFillerTimeline(timeline, skeletonData));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var idsToMatch = new List<int>(timelineDictionary.Keys);
|
var idsToMatch = new List<string>(timelineDictionary.Keys);
|
||||||
|
|
||||||
// For each animation in the list, check for and add missing timelines.
|
// For each animation in the list, check for and add missing timelines.
|
||||||
var currentAnimationIDs = new HashSet<int>();
|
var currentAnimationIDs = new HashSet<string>();
|
||||||
foreach (var animation in animations) {
|
foreach (var animation in animations) {
|
||||||
currentAnimationIDs.Clear();
|
currentAnimationIDs.Clear();
|
||||||
foreach (var timeline in animation.Timelines) {
|
foreach (var timeline in animation.Timelines) {
|
||||||
if (timeline is EventTimeline) continue;
|
if (timeline is EventTimeline) continue;
|
||||||
currentAnimationIDs.Add(timeline.PropertyId);
|
foreach (string propertyId in timeline.PropertyIds) {
|
||||||
|
currentAnimationIDs.Add(propertyId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var animationTimelines = animation.Timelines;
|
var animationTimelines = animation.Timelines;
|
||||||
foreach (int propertyID in idsToMatch) {
|
foreach (string propertyId in idsToMatch) {
|
||||||
if (!currentAnimationIDs.Contains(propertyID))
|
if (!currentAnimationIDs.Contains(propertyId))
|
||||||
animationTimelines.Add(timelineDictionary[propertyID]);
|
animationTimelines.Add(timelineDictionary[propertyId]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -132,62 +135,52 @@ namespace Spine.Unity.Examples {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static RotateTimeline GetFillerTimeline (RotateTimeline timeline, SkeletonData skeletonData) {
|
static RotateTimeline GetFillerTimeline (RotateTimeline timeline, SkeletonData skeletonData) {
|
||||||
var t = new RotateTimeline(1);
|
var t = new RotateTimeline(1, 0, timeline.BoneIndex);
|
||||||
t.BoneIndex = timeline.BoneIndex;
|
|
||||||
t.SetFrame(0, 0, 0);
|
t.SetFrame(0, 0, 0);
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
static TranslateTimeline GetFillerTimeline (TranslateTimeline timeline, SkeletonData skeletonData) {
|
static TranslateTimeline GetFillerTimeline (TranslateTimeline timeline, SkeletonData skeletonData) {
|
||||||
var t = new TranslateTimeline(1);
|
var t = new TranslateTimeline(1, 0, timeline.BoneIndex);
|
||||||
t.BoneIndex = timeline.BoneIndex;
|
|
||||||
t.SetFrame(0, 0, 0, 0);
|
t.SetFrame(0, 0, 0, 0);
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
static ScaleTimeline GetFillerTimeline (ScaleTimeline timeline, SkeletonData skeletonData) {
|
static ScaleTimeline GetFillerTimeline (ScaleTimeline timeline, SkeletonData skeletonData) {
|
||||||
var t = new ScaleTimeline(1);
|
var t = new ScaleTimeline(1, 0, timeline.BoneIndex);
|
||||||
t.BoneIndex = timeline.BoneIndex;
|
|
||||||
t.SetFrame(0, 0, 0, 0);
|
t.SetFrame(0, 0, 0, 0);
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
static ShearTimeline GetFillerTimeline (ShearTimeline timeline, SkeletonData skeletonData) {
|
static ShearTimeline GetFillerTimeline (ShearTimeline timeline, SkeletonData skeletonData) {
|
||||||
var t = new ShearTimeline(1);
|
var t = new ShearTimeline(1, 0, timeline.BoneIndex);
|
||||||
t.BoneIndex = timeline.BoneIndex;
|
|
||||||
t.SetFrame(0, 0, 0, 0);
|
t.SetFrame(0, 0, 0, 0);
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
static AttachmentTimeline GetFillerTimeline (AttachmentTimeline timeline, SkeletonData skeletonData) {
|
static AttachmentTimeline GetFillerTimeline (AttachmentTimeline timeline, SkeletonData skeletonData) {
|
||||||
var t = new AttachmentTimeline(1);
|
var t = new AttachmentTimeline(1, timeline.SlotIndex);
|
||||||
t.SlotIndex = timeline.SlotIndex;
|
|
||||||
var slotData = skeletonData.Slots.Items[t.SlotIndex];
|
var slotData = skeletonData.Slots.Items[t.SlotIndex];
|
||||||
t.SetFrame(0, 0, slotData.AttachmentName);
|
t.SetFrame(0, 0, slotData.AttachmentName);
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
static ColorTimeline GetFillerTimeline (ColorTimeline timeline, SkeletonData skeletonData) {
|
static ColorTimeline GetFillerTimeline (ColorTimeline timeline, SkeletonData skeletonData) {
|
||||||
var t = new ColorTimeline(1);
|
var t = new ColorTimeline(1, 0, timeline.SlotIndex);
|
||||||
t.SlotIndex = timeline.SlotIndex;
|
|
||||||
var slotData = skeletonData.Slots.Items[t.SlotIndex];
|
var slotData = skeletonData.Slots.Items[t.SlotIndex];
|
||||||
t.SetFrame(0, 0, slotData.R, slotData.G, slotData.B, slotData.A);
|
t.SetFrame(0, 0, slotData.R, slotData.G, slotData.B, slotData.A);
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
static TwoColorTimeline GetFillerTimeline (TwoColorTimeline timeline, SkeletonData skeletonData) {
|
static TwoColorTimeline GetFillerTimeline (TwoColorTimeline timeline, SkeletonData skeletonData) {
|
||||||
var t = new TwoColorTimeline(1);
|
var t = new TwoColorTimeline(1, 0, timeline.SlotIndex);
|
||||||
t.SlotIndex = timeline.SlotIndex;
|
|
||||||
var slotData = skeletonData.Slots.Items[t.SlotIndex];
|
var slotData = skeletonData.Slots.Items[t.SlotIndex];
|
||||||
t.SetFrame(0, 0, slotData.R, slotData.G, slotData.B, slotData.A, slotData.R2, slotData.G2, slotData.B2);
|
t.SetFrame(0, 0, slotData.R, slotData.G, slotData.B, slotData.A, slotData.R2, slotData.G2, slotData.B2);
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
static DeformTimeline GetFillerTimeline (DeformTimeline timeline, SkeletonData skeletonData) {
|
static DeformTimeline GetFillerTimeline (DeformTimeline timeline, SkeletonData skeletonData) {
|
||||||
var t = new DeformTimeline(1);
|
var t = new DeformTimeline(1, 0, timeline.SlotIndex, timeline.Attachment);
|
||||||
t.SlotIndex = timeline.SlotIndex;
|
|
||||||
t.Attachment = timeline.Attachment;
|
|
||||||
|
|
||||||
if (t.Attachment.IsWeighted()) {
|
if (t.Attachment.IsWeighted()) {
|
||||||
t.SetFrame(0, 0, new float[t.Attachment.Vertices.Length]);
|
t.SetFrame(0, 0, new float[t.Attachment.Vertices.Length]);
|
||||||
} else {
|
} else {
|
||||||
@ -204,35 +197,35 @@ namespace Spine.Unity.Examples {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static IkConstraintTimeline GetFillerTimeline (IkConstraintTimeline timeline, SkeletonData skeletonData) {
|
static IkConstraintTimeline GetFillerTimeline (IkConstraintTimeline timeline, SkeletonData skeletonData) {
|
||||||
var t = new IkConstraintTimeline(1);
|
var t = new IkConstraintTimeline(1, 0, timeline.IkConstraintIndex);
|
||||||
var ikConstraintData = skeletonData.IkConstraints.Items[timeline.IkConstraintIndex];
|
var ikConstraintData = skeletonData.IkConstraints.Items[timeline.IkConstraintIndex];
|
||||||
t.SetFrame(0, 0, ikConstraintData.Mix, ikConstraintData.Softness, ikConstraintData.BendDirection, ikConstraintData.Compress, ikConstraintData.Stretch);
|
t.SetFrame(0, 0, ikConstraintData.Mix, ikConstraintData.Softness, ikConstraintData.BendDirection, ikConstraintData.Compress, ikConstraintData.Stretch);
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
static TransformConstraintTimeline GetFillerTimeline (TransformConstraintTimeline timeline, SkeletonData skeletonData) {
|
static TransformConstraintTimeline GetFillerTimeline (TransformConstraintTimeline timeline, SkeletonData skeletonData) {
|
||||||
var t = new TransformConstraintTimeline(1);
|
var t = new TransformConstraintTimeline(1, 0, timeline.TransformConstraintIndex);
|
||||||
var data = skeletonData.TransformConstraints.Items[timeline.TransformConstraintIndex];
|
var data = skeletonData.TransformConstraints.Items[timeline.TransformConstraintIndex];
|
||||||
t.SetFrame(0, 0, data.RotateMix, data.TranslateMix, data.ScaleMix, data.ShearMix);
|
t.SetFrame(0, 0, data.RotateMix, data.TranslateMix, data.ScaleMix, data.ShearMix);
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
static PathConstraintPositionTimeline GetFillerTimeline (PathConstraintPositionTimeline timeline, SkeletonData skeletonData) {
|
static PathConstraintPositionTimeline GetFillerTimeline (PathConstraintPositionTimeline timeline, SkeletonData skeletonData) {
|
||||||
var t = new PathConstraintPositionTimeline(1);
|
var t = new PathConstraintPositionTimeline(1, 0, timeline.PathConstraintIndex);
|
||||||
var data = skeletonData.PathConstraints.Items[timeline.PathConstraintIndex];
|
var data = skeletonData.PathConstraints.Items[timeline.PathConstraintIndex];
|
||||||
t.SetFrame(0, 0, data.Position);
|
t.SetFrame(0, 0, data.Position);
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
static PathConstraintSpacingTimeline GetFillerTimeline (PathConstraintSpacingTimeline timeline, SkeletonData skeletonData) {
|
static PathConstraintSpacingTimeline GetFillerTimeline (PathConstraintSpacingTimeline timeline, SkeletonData skeletonData) {
|
||||||
var t = new PathConstraintSpacingTimeline(1);
|
var t = new PathConstraintSpacingTimeline(1, 0, timeline.PathConstraintIndex);
|
||||||
var data = skeletonData.PathConstraints.Items[timeline.PathConstraintIndex];
|
var data = skeletonData.PathConstraints.Items[timeline.PathConstraintIndex];
|
||||||
t.SetFrame(0, 0, data.Spacing);
|
t.SetFrame(0, 0, data.Spacing);
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
static PathConstraintMixTimeline GetFillerTimeline (PathConstraintMixTimeline timeline, SkeletonData skeletonData) {
|
static PathConstraintMixTimeline GetFillerTimeline (PathConstraintMixTimeline timeline, SkeletonData skeletonData) {
|
||||||
var t = new PathConstraintMixTimeline(1);
|
var t = new PathConstraintMixTimeline(1, 0, timeline.PathConstraintIndex);
|
||||||
var data = skeletonData.PathConstraints.Items[timeline.PathConstraintIndex];
|
var data = skeletonData.PathConstraints.Items[timeline.PathConstraintIndex];
|
||||||
t.SetFrame(0, 0, data.RotateMix, data.TranslateMix);
|
t.SetFrame(0, 0, data.RotateMix, data.TranslateMix);
|
||||||
return t;
|
return t;
|
||||||
|
|||||||
@ -123,7 +123,6 @@ namespace Spine.Unity.Editor {
|
|||||||
EditorGUI.indentLevel = 0;
|
EditorGUI.indentLevel = 0;
|
||||||
|
|
||||||
var mixMode = layerMixModes.GetArrayElementAtIndex(i);
|
var mixMode = layerMixModes.GetArrayElementAtIndex(i);
|
||||||
var blendMode = layerBlendModes.GetArrayElementAtIndex(i);
|
|
||||||
rect.position += new Vector2(rect.width, 0);
|
rect.position += new Vector2(rect.width, 0);
|
||||||
rect.width = widthMixColumn;
|
rect.width = widthMixColumn;
|
||||||
EditorGUI.PropertyField(rect, mixMode, GUIContent.none);
|
EditorGUI.PropertyField(rect, mixMode, GUIContent.none);
|
||||||
|
|||||||
@ -1,9 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 18ee2876d53412642bbfa1070a1b947f
|
|
||||||
folderAsset: yes
|
|
||||||
timeCreated: 1527569487
|
|
||||||
licenseType: Free
|
|
||||||
DefaultImporter:
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
@ -1,9 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 1ad4318c20ec5674a9f4d7f786afd681
|
|
||||||
folderAsset: yes
|
|
||||||
timeCreated: 1496449217
|
|
||||||
licenseType: Free
|
|
||||||
DefaultImporter:
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
@ -1,47 +0,0 @@
|
|||||||
/******************************************************************************
|
|
||||||
* Spine Runtimes License Agreement
|
|
||||||
* Last updated January 1, 2020. Replaces all prior versions.
|
|
||||||
*
|
|
||||||
* Copyright (c) 2013-2020, Esoteric Software LLC
|
|
||||||
*
|
|
||||||
* 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
|
|
||||||
*
|
|
||||||
* Otherwise, it is permitted to integrate the Spine Runtimes into software
|
|
||||||
* or otherwise create derivative works of the Spine Runtimes (collectively,
|
|
||||||
* "Products"), provided that each user of the Products must obtain their own
|
|
||||||
* Spine Editor license and redistribution of the Products in any form must
|
|
||||||
* include this license and copyright notice.
|
|
||||||
*
|
|
||||||
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
|
|
||||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
||||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
|
|
||||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
||||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
|
|
||||||
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
|
|
||||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
||||||
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*****************************************************************************/
|
|
||||||
|
|
||||||
using UnityEngine;
|
|
||||||
using UnityEditor;
|
|
||||||
using Spine.Unity.Deprecated;
|
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace Spine.Unity.Editor {
|
|
||||||
using Editor = UnityEditor.Editor;
|
|
||||||
|
|
||||||
[Obsolete("The spine-unity 3.7 runtime introduced SkeletonDataModifierAssets BlendModeMaterials which replaced SlotBlendModes. Will be removed in spine-unity 3.9.", false)]
|
|
||||||
public class SlotBlendModesEditor : Editor {
|
|
||||||
|
|
||||||
[MenuItem("CONTEXT/SkeletonRenderer/Add Slot Blend Modes Component")]
|
|
||||||
static void AddSlotBlendModesComponent (MenuCommand command) {
|
|
||||||
var skeletonRenderer = (SkeletonRenderer)command.context;
|
|
||||||
skeletonRenderer.gameObject.AddComponent<SlotBlendModes>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,12 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: cbec7dc66dca80a419477536c23b7a0d
|
|
||||||
timeCreated: 1496449255
|
|
||||||
licenseType: Free
|
|
||||||
MonoImporter:
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
@ -40,8 +40,8 @@ namespace Spine.Unity {
|
|||||||
public static class SkeletonDataCompatibility {
|
public static class SkeletonDataCompatibility {
|
||||||
|
|
||||||
#if UNITY_EDITOR
|
#if UNITY_EDITOR
|
||||||
static readonly int[][] compatibleBinaryVersions = { new[] { 3, 9, 0 }, new[] { 3, 8, 0 } };
|
static readonly int[][] compatibleBinaryVersions = { new[] { 4, 0, 0 } };
|
||||||
static readonly int[][] compatibleJsonVersions = { new[] { 3, 9, 0 }, new[] { 3, 8, 0 } };
|
static readonly int[][] compatibleJsonVersions = { new[] { 4, 0, 0 } };
|
||||||
|
|
||||||
static bool wasVersionDialogShown = false;
|
static bool wasVersionDialogShown = false;
|
||||||
static readonly Regex jsonVersionRegex = new Regex(@"""spine""\s*:\s*""([^""]+)""", RegexOptions.CultureInvariant);
|
static readonly Regex jsonVersionRegex = new Regex(@"""spine""\s*:\s*""([^""]+)""", RegexOptions.CultureInvariant);
|
||||||
|
|||||||
@ -254,7 +254,6 @@ namespace Spine.Unity {
|
|||||||
private void OnClipAppliedCallback (Spine.Animation clip, AnimatorStateInfo stateInfo,
|
private void OnClipAppliedCallback (Spine.Animation clip, AnimatorStateInfo stateInfo,
|
||||||
int layerIndex, float time, bool isLooping, float weight) {
|
int layerIndex, float time, bool isLooping, float weight) {
|
||||||
|
|
||||||
float clipDuration = clip.duration == 0 ? 1 : clip.duration;
|
|
||||||
float speedFactor = stateInfo.speedMultiplier * stateInfo.speed;
|
float speedFactor = stateInfo.speedMultiplier * stateInfo.speed;
|
||||||
float lastTime = time - (Time.deltaTime * speedFactor);
|
float lastTime = time - (Time.deltaTime * speedFactor);
|
||||||
if (isLooping && clip.duration != 0) {
|
if (isLooping && clip.duration != 0) {
|
||||||
|
|||||||
@ -1,9 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 04817e31b917de6489f349dd332d7468
|
|
||||||
folderAsset: yes
|
|
||||||
timeCreated: 1563295668
|
|
||||||
licenseType: Free
|
|
||||||
DefaultImporter:
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
@ -1,9 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: dfdd78a071ca1a04bb64c6cc41e14aa0
|
|
||||||
folderAsset: yes
|
|
||||||
timeCreated: 1496447038
|
|
||||||
licenseType: Free
|
|
||||||
DefaultImporter:
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
@ -1,230 +0,0 @@
|
|||||||
/******************************************************************************
|
|
||||||
* Spine Runtimes License Agreement
|
|
||||||
* Last updated January 1, 2020. Replaces all prior versions.
|
|
||||||
*
|
|
||||||
* Copyright (c) 2013-2020, Esoteric Software LLC
|
|
||||||
*
|
|
||||||
* 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
|
|
||||||
*
|
|
||||||
* Otherwise, it is permitted to integrate the Spine Runtimes into software
|
|
||||||
* or otherwise create derivative works of the Spine Runtimes (collectively,
|
|
||||||
* "Products"), provided that each user of the Products must obtain their own
|
|
||||||
* Spine Editor license and redistribution of the Products in any form must
|
|
||||||
* include this license and copyright notice.
|
|
||||||
*
|
|
||||||
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
|
|
||||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
||||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
|
|
||||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
||||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
|
|
||||||
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
|
|
||||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
||||||
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*****************************************************************************/
|
|
||||||
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using UnityEngine;
|
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace Spine.Unity.Deprecated {
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Deprecated. The spine-unity 3.7 runtime introduced SkeletonDataModifierAssets BlendModeMaterials which replaced SlotBlendModes. See the
|
|
||||||
/// <see href="http://esotericsoftware.com/spine-unity-skeletondatamodifierassets#BlendModeMaterials">SkeletonDataModifierAssets BlendModeMaterials documentation page</see> and
|
|
||||||
/// <see href="http://esotericsoftware.com/forum/Slot-blending-not-work-11281">this forum thread</see> for further information.
|
|
||||||
/// This class will be removed in the spine-unity 3.9 runtime.
|
|
||||||
/// </summary>
|
|
||||||
[Obsolete("The spine-unity 3.7 runtime introduced SkeletonDataModifierAssets BlendModeMaterials which replaced SlotBlendModes. Will be removed in spine-unity 3.9.", false)]
|
|
||||||
[DisallowMultipleComponent]
|
|
||||||
public class SlotBlendModes : MonoBehaviour {
|
|
||||||
|
|
||||||
#region Internal Material Dictionary
|
|
||||||
public struct MaterialTexturePair {
|
|
||||||
public Texture2D texture2D;
|
|
||||||
public Material material;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal class MaterialWithRefcount {
|
|
||||||
public Material materialClone;
|
|
||||||
public int refcount = 1;
|
|
||||||
|
|
||||||
public MaterialWithRefcount(Material mat) {
|
|
||||||
this.materialClone = mat;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
static Dictionary<MaterialTexturePair, MaterialWithRefcount> materialTable;
|
|
||||||
internal static Dictionary<MaterialTexturePair, MaterialWithRefcount> MaterialTable {
|
|
||||||
get {
|
|
||||||
if (materialTable == null) materialTable = new Dictionary<MaterialTexturePair, MaterialWithRefcount>();
|
|
||||||
return materialTable;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal struct SlotMaterialTextureTuple {
|
|
||||||
public Slot slot;
|
|
||||||
public Texture2D texture2D;
|
|
||||||
public Material material;
|
|
||||||
|
|
||||||
public SlotMaterialTextureTuple(Slot slot, Material material, Texture2D texture) {
|
|
||||||
this.slot = slot;
|
|
||||||
this.material = material;
|
|
||||||
this.texture2D = texture;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static Material GetOrAddMaterialFor(Material materialSource, Texture2D texture) {
|
|
||||||
if (materialSource == null || texture == null) return null;
|
|
||||||
|
|
||||||
var mt = SlotBlendModes.MaterialTable;
|
|
||||||
MaterialWithRefcount matWithRefcount;
|
|
||||||
var key = new MaterialTexturePair { material = materialSource, texture2D = texture };
|
|
||||||
if (!mt.TryGetValue(key, out matWithRefcount)) {
|
|
||||||
matWithRefcount = new MaterialWithRefcount(new Material(materialSource));
|
|
||||||
var m = matWithRefcount.materialClone;
|
|
||||||
m.name = "(Clone)" + texture.name + "-" + materialSource.name;
|
|
||||||
m.mainTexture = texture;
|
|
||||||
mt[key] = matWithRefcount;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
matWithRefcount.refcount++;
|
|
||||||
}
|
|
||||||
return matWithRefcount.materialClone;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static MaterialWithRefcount GetExistingMaterialFor(Material materialSource, Texture2D texture)
|
|
||||||
{
|
|
||||||
if (materialSource == null || texture == null) return null;
|
|
||||||
|
|
||||||
var mt = SlotBlendModes.MaterialTable;
|
|
||||||
MaterialWithRefcount matWithRefcount;
|
|
||||||
var key = new MaterialTexturePair { material = materialSource, texture2D = texture };
|
|
||||||
if (!mt.TryGetValue(key, out matWithRefcount)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return matWithRefcount;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static void RemoveMaterialFromTable(Material materialSource, Texture2D texture) {
|
|
||||||
var mt = SlotBlendModes.MaterialTable;
|
|
||||||
var key = new MaterialTexturePair { material = materialSource, texture2D = texture };
|
|
||||||
mt.Remove(key);
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Inspector
|
|
||||||
public Material multiplyMaterialSource;
|
|
||||||
public Material screenMaterialSource;
|
|
||||||
|
|
||||||
Texture2D texture;
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
SlotMaterialTextureTuple[] slotsWithCustomMaterial = new SlotMaterialTextureTuple[0];
|
|
||||||
|
|
||||||
public bool Applied { get; private set; }
|
|
||||||
|
|
||||||
void Start() {
|
|
||||||
if (!Applied) Apply();
|
|
||||||
}
|
|
||||||
|
|
||||||
void OnDestroy() {
|
|
||||||
if (Applied) Remove();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Apply() {
|
|
||||||
GetTexture();
|
|
||||||
if (texture == null) return;
|
|
||||||
|
|
||||||
var skeletonRenderer = GetComponent<SkeletonRenderer>();
|
|
||||||
if (skeletonRenderer == null) return;
|
|
||||||
|
|
||||||
var slotMaterials = skeletonRenderer.CustomSlotMaterials;
|
|
||||||
|
|
||||||
int numSlotsWithCustomMaterial = 0;
|
|
||||||
foreach (var s in skeletonRenderer.Skeleton.Slots) {
|
|
||||||
switch (s.data.blendMode) {
|
|
||||||
case BlendMode.Multiply:
|
|
||||||
if (multiplyMaterialSource != null) {
|
|
||||||
slotMaterials[s] = GetOrAddMaterialFor(multiplyMaterialSource, texture);
|
|
||||||
++numSlotsWithCustomMaterial;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case BlendMode.Screen:
|
|
||||||
if (screenMaterialSource != null) {
|
|
||||||
slotMaterials[s] = GetOrAddMaterialFor(screenMaterialSource, texture);
|
|
||||||
++numSlotsWithCustomMaterial;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
slotsWithCustomMaterial = new SlotMaterialTextureTuple[numSlotsWithCustomMaterial];
|
|
||||||
int storedSlotIndex = 0;
|
|
||||||
foreach (var s in skeletonRenderer.Skeleton.Slots) {
|
|
||||||
switch (s.data.blendMode) {
|
|
||||||
case BlendMode.Multiply:
|
|
||||||
if (multiplyMaterialSource != null) {
|
|
||||||
slotsWithCustomMaterial[storedSlotIndex++] = new SlotMaterialTextureTuple(s, multiplyMaterialSource, texture);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case BlendMode.Screen:
|
|
||||||
if (screenMaterialSource != null) {
|
|
||||||
slotsWithCustomMaterial[storedSlotIndex++] = new SlotMaterialTextureTuple(s, screenMaterialSource, texture);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Applied = true;
|
|
||||||
skeletonRenderer.LateUpdate();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Remove() {
|
|
||||||
GetTexture();
|
|
||||||
if (texture == null) return;
|
|
||||||
|
|
||||||
var skeletonRenderer = GetComponent<SkeletonRenderer>();
|
|
||||||
if (skeletonRenderer == null) return;
|
|
||||||
|
|
||||||
var slotMaterials = skeletonRenderer.CustomSlotMaterials;
|
|
||||||
|
|
||||||
foreach (var slotWithCustomMat in slotsWithCustomMaterial) {
|
|
||||||
|
|
||||||
Slot s = slotWithCustomMat.slot;
|
|
||||||
Material storedMaterialSource = slotWithCustomMat.material;
|
|
||||||
Texture2D storedTexture = slotWithCustomMat.texture2D;
|
|
||||||
|
|
||||||
var matWithRefcount = GetExistingMaterialFor(storedMaterialSource, storedTexture);
|
|
||||||
if (--matWithRefcount.refcount == 0) {
|
|
||||||
RemoveMaterialFromTable(storedMaterialSource, storedTexture);
|
|
||||||
}
|
|
||||||
// we don't want to remove slotMaterials[s] if it has been changed in the meantime.
|
|
||||||
Material m;
|
|
||||||
if (slotMaterials.TryGetValue(s, out m)) {
|
|
||||||
var existingMat = matWithRefcount == null ? null : matWithRefcount.materialClone;
|
|
||||||
if (Material.ReferenceEquals(m, existingMat)) {
|
|
||||||
slotMaterials.Remove(s);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
slotsWithCustomMaterial = null;
|
|
||||||
|
|
||||||
Applied = false;
|
|
||||||
if (skeletonRenderer.valid) skeletonRenderer.LateUpdate();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void GetTexture() {
|
|
||||||
if (texture == null) {
|
|
||||||
var sr = GetComponent<SkeletonRenderer>(); if (sr == null) return;
|
|
||||||
var sda = sr.skeletonDataAsset; if (sda == null) return;
|
|
||||||
var aa = sda.atlasAssets[0]; if (aa == null) return;
|
|
||||||
var am = aa.PrimaryMaterial; if (am == null) return;
|
|
||||||
texture = am.mainTexture as Texture2D;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,16 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: f1f8243645ba2e74aa3564bd956eed89
|
|
||||||
timeCreated: 1496794038
|
|
||||||
licenseType: Free
|
|
||||||
MonoImporter:
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences:
|
|
||||||
- multiplyMaterialSource: {fileID: 2100000, guid: 53bf0ab317d032d418cf1252d68f51df,
|
|
||||||
type: 2}
|
|
||||||
- screenMaterialSource: {fileID: 2100000, guid: 73f0f46d3177c614baf0fa48d646a9be,
|
|
||||||
type: 2}
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
@ -38,28 +38,28 @@ namespace Spine.Unity.AnimationTools {
|
|||||||
/// SkeletonData can be accessed from Skeleton.Data or from SkeletonDataAsset.GetSkeletonData.
|
/// SkeletonData can be accessed from Skeleton.Data or from SkeletonDataAsset.GetSkeletonData.
|
||||||
/// If no SkeletonData is given, values are computed relative to setup pose instead of local-absolute.</summary>
|
/// If no SkeletonData is given, values are computed relative to setup pose instead of local-absolute.</summary>
|
||||||
public static Vector2 Evaluate (this TranslateTimeline timeline, float time, SkeletonData skeletonData = null) {
|
public static Vector2 Evaluate (this TranslateTimeline timeline, float time, SkeletonData skeletonData = null) {
|
||||||
const int PREV_TIME = -3, PREV_X = -2, PREV_Y = -1;
|
|
||||||
const int X = 1, Y = 2;
|
|
||||||
|
|
||||||
var frames = timeline.frames;
|
var frames = timeline.frames;
|
||||||
if (time < frames[0]) return Vector2.zero;
|
if (time < frames[0]) return Vector2.zero;
|
||||||
|
|
||||||
float x, y;
|
float x, y;
|
||||||
if (time >= frames[frames.Length - TranslateTimeline.ENTRIES]) { // Time is after last frame.
|
int i = Animation.Search(frames, time, TranslateTimeline.ENTRIES), curveType = (int)timeline.curves[i / TranslateTimeline.ENTRIES];
|
||||||
x = frames[frames.Length + PREV_X];
|
switch (curveType) {
|
||||||
y = frames[frames.Length + PREV_Y];
|
case TranslateTimeline.LINEAR:
|
||||||
}
|
float before = frames[i];
|
||||||
else {
|
x = frames[i + TranslateTimeline.VALUE1];
|
||||||
// Interpolate between the previous frame and the current frame.
|
y = frames[i + TranslateTimeline.VALUE2];
|
||||||
int frame = Animation.BinarySearch(frames, time, TranslateTimeline.ENTRIES);
|
float t = (time - before) / (frames[i + TranslateTimeline.ENTRIES] - before);
|
||||||
x = frames[frame + PREV_X];
|
x += (frames[i + TranslateTimeline.ENTRIES + TranslateTimeline.VALUE1] - x) * t;
|
||||||
y = frames[frame + PREV_Y];
|
y += (frames[i + TranslateTimeline.ENTRIES + TranslateTimeline.VALUE2] - y) * t;
|
||||||
float frameTime = frames[frame];
|
break;
|
||||||
float percent = timeline.GetCurvePercent(frame / TranslateTimeline.ENTRIES - 1,
|
case TranslateTimeline.STEPPED:
|
||||||
1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime));
|
x = frames[i + TranslateTimeline.VALUE1];
|
||||||
|
y = frames[i + TranslateTimeline.VALUE2];
|
||||||
x += (frames[frame + X] - x) * percent;
|
break;
|
||||||
y += (frames[frame + Y] - y) * percent;
|
default:
|
||||||
|
x = timeline.GetBezierValue(time, i, TranslateTimeline.VALUE1, curveType - TranslateTimeline.BEZIER);
|
||||||
|
y = timeline.GetBezierValue(time, i, TranslateTimeline.VALUE2, curveType + TranslateTimeline.BEZIER_SIZE - TranslateTimeline.BEZIER);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector2 xy = new Vector2(x, y);
|
Vector2 xy = new Vector2(x, y);
|
||||||
@ -67,7 +67,7 @@ namespace Spine.Unity.AnimationTools {
|
|||||||
return xy;
|
return xy;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
var boneData = skeletonData.bones.Items[timeline.boneIndex];
|
var boneData = skeletonData.bones.Items[timeline.BoneIndex];
|
||||||
return xy + new Vector2(boneData.x, boneData.y);
|
return xy + new Vector2(boneData.x, boneData.y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -82,7 +82,7 @@ namespace Spine.Unity.AnimationTools {
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
var translateTimeline = timeline as TranslateTimeline;
|
var translateTimeline = timeline as TranslateTimeline;
|
||||||
if (translateTimeline != null && translateTimeline.boneIndex == boneIndex)
|
if (translateTimeline != null && translateTimeline.BoneIndex == boneIndex)
|
||||||
return translateTimeline;
|
return translateTimeline;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@ -1 +1 @@
|
|||||||
This Spine-Unity runtime works with data exported from Spine Editor version: 3.8.xx and 3.9.xx
|
This Spine-Unity runtime works with data exported from Spine Editor version: 4.0.xx
|
||||||
Loading…
x
Reference in New Issue
Block a user