mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-03-26 22:49:01 +08:00
[csharp][monogame][unity][xna] Updated to 3.3.x (#623)
* [spine-csharp] Ported 3.3 changes of Animation and inner classes. Added MathUtils.Clamp
* [spine-csharp] Ported 3.3 changes of AnimationStateData. Updated .gitignore to exclude .meta files from spine-csharp
* [spine-csharp] Ported 3.3 changes of Bone
* [spine-csharp] Ported 3.3 changes of BoneData
* [spine-csharp] Ported 3.3 changes of Event
* [spine-csharp] Ported 3.3 changes to IKConstraint. Also got rid of the hideous labeled break. Replaced with while and sprinkled continues and break :D
* [spine-csharp] Mario is not good with labeled breaks. Fixed with goto
* [spine-csharp] Ported more 3.3 changes, only SkeletonJson and SkeletonBinary left. Unity runtime also needs updating with new changes
* [spine-csharp] fixed compilation errors initially not reported by Mono CS
* Minor clean up.
Every space has its place.™
* Match csharp 3.3 refactorings.
* [Unity] Minor cleanup in SkeletonBaker.
* Better exception messages.
* Removed extra usings. Matched whitespace style.
* Fixed Bone.cs method PascalCase and xml documentation.
* Fixed Exception ctor arguments.
* Fixed single bone IK mixing.
Matched libgdx commit:
e0ee18a088
* [spine-csharp] Port of 3.3 changes to SkeletonJson, incomplete
* [Unity] Ragdoll: account for shear and new constraints.
* [spine-csharp] Ported 3.3 changes to SkeletonJson
* [spine-csharp] Ported 3.3 changes to SkeletonBinary. Time for testing and debugging
* [spine-csharp] Fixed up XNA runtime
* Added simple example data in new exports/ folder
* [spine-csharp] Fixed bug in Slot construtor, wasn't assigning index. Fixed bug in CurveTimeline, argument guard was wrong. Fixed bugs in SkeletonJson, indexing to get duration of a timeline was wrong. Added simple example to spine-xna for easier debugging
* [spine-csharp] Fixed porting bug in ColorTimeline#apply, indexing was wrong
* [spine-csharp] Fixed SkeletonJson#ReadVertices, calling resize on temp lists was wrong. Updated goblin sample in spine-xna, fixed mesh attachment rendering in spine-xna.
* [spine-xna] Added binary exports, modified XNA game
* [spine-xna] Added tank example
* Some cleanup. Relabeled generic todos.
* Prevent SpineEditorUtilities from orphaning failed instantiations.
* Ignore PathAttachment when checking for required atlas regions.
* [spine-csharp] the great spaces to tabs battle
* [spine-csharp] Fixed inherit deform.
* [spine-csharp] Match Skeleton.java properties and stuff.
* [spine-csharp] Fixed enums, all upper cased now, using Enum.ParseType in case-insensitive mode
* [csharp] Ported 206e7f983c4df4d27fee6cac05d152eb2295c8b0 to csharp runtime. Fixes attachment keys on different tracks
* [csharp] Updated README.md
* [xna] [monogame] [unity] Updated README.md
* Revert "[csharp] Ported 206e7f983c4df4d27fee6cac05d152eb2295c8b0 to csharp runtime. Fixes attachment keys on different tracks"
This reverts commit 175216868dd00b4ae31cc717022242308c150f6a.
* [csharp] Fix to AttachmentTimeline#apply, fix for the fix for the fix for the revert for the fix
* [csharp] Matched and fixed more comments, summaries, exception messages and formatting.
* [csharp] Fix deformed weighted vertices condition + match libgdx closer.
* [csharp] Use internal ExposedList array for critical methods.
* [csharp] SkeletonJson and SkeletonBinary minor formatting and fixes.
* [unity] Match changes and fixes in spine-csharp 3.3 + better editor messages.
* [unity] Updated sample scenes and files.
* [csharp] Some formatting got left behind.
* [unity] New readme links + Removed redundant info.
* [exports] Remove dummy project.
This commit is contained in:
parent
20460fbdaa
commit
1c19365325
3
.gitignore
vendored
3
.gitignore
vendored
@ -12,6 +12,7 @@ target
|
|||||||
*.user
|
*.user
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|
||||||
|
|
||||||
.idea/
|
.idea/
|
||||||
build/
|
build/
|
||||||
|
|
||||||
@ -39,7 +40,9 @@ spine-cocos2d-iphone/spine-cocos2d-iphone-ios.xcodeproj/project.xcworkspace/xcsh
|
|||||||
|
|
||||||
spine-csharp/bin
|
spine-csharp/bin
|
||||||
spine-csharp/obj
|
spine-csharp/obj
|
||||||
|
spine-csharp/src/*.meta
|
||||||
spine-csharp/src/*.cs.meta
|
spine-csharp/src/*.cs.meta
|
||||||
|
spine-csharp/src/Attachments/*.cs.meta
|
||||||
|
|
||||||
spine-monogame/xamarinstudio-ios/src/bin
|
spine-monogame/xamarinstudio-ios/src/bin
|
||||||
spine-monogame/xamarinstudio-ios/src/obj
|
spine-monogame/xamarinstudio-ios/src/obj
|
||||||
|
|||||||
@ -10,14 +10,14 @@ The Spine Runtimes are developed with the intent to be used with data exported f
|
|||||||
|
|
||||||
## Spine version
|
## Spine version
|
||||||
|
|
||||||
spine-csharp works with data exported from Spine 3.2.01. Updating spine-csharp to [v3.3](https://github.com/EsotericSoftware/spine-runtimes/issues/613) is in progress.
|
spine-csharp works with data exported from Spine 3.3.07.
|
||||||
|
|
||||||
spine-csharp supports all Spine features.
|
spine-csharp supports all Spine features.
|
||||||
|
|
||||||
## Setup
|
## Setup
|
||||||
|
|
||||||
1. Download the Spine Runtimes source using [git](https://help.github.com/articles/set-up-git) or by downloading it [as a zip](https://github.com/EsotericSoftware/spine-runtimes/archive/master.zip).
|
1. Download the Spine Runtimes source using [git](https://help.github.com/articles/set-up-git) or by downloading it [as a zip](https://github.com/EsotericSoftware/spine-runtimes/archive/master.zip).
|
||||||
1. Open the `spine-csharp.sln` Visual C# 2010 Express project file.
|
1. Open the `spine-csharp.sln` Visual Studio 2015 Community project file.
|
||||||
|
|
||||||
Alternatively, the contents of the `spine-csharp/src` directory can be copied into your project.
|
Alternatively, the contents of the `spine-csharp/src` directory can be copied into your project.
|
||||||
|
|
||||||
|
|||||||
@ -70,10 +70,10 @@
|
|||||||
<Compile Include="src\Attachments\AttachmentLoader.cs" />
|
<Compile Include="src\Attachments\AttachmentLoader.cs" />
|
||||||
<Compile Include="src\Attachments\AttachmentType.cs" />
|
<Compile Include="src\Attachments\AttachmentType.cs" />
|
||||||
<Compile Include="src\Attachments\BoundingBoxAttachment.cs" />
|
<Compile Include="src\Attachments\BoundingBoxAttachment.cs" />
|
||||||
<Compile Include="src\Attachments\IFfdAttachment.cs" />
|
|
||||||
<Compile Include="src\Attachments\MeshAttachment.cs" />
|
<Compile Include="src\Attachments\MeshAttachment.cs" />
|
||||||
|
<Compile Include="src\Attachments\PathAttachment.cs" />
|
||||||
<Compile Include="src\Attachments\RegionAttachment.cs" />
|
<Compile Include="src\Attachments\RegionAttachment.cs" />
|
||||||
<Compile Include="src\Attachments\WeightedMeshAttachment.cs" />
|
<Compile Include="src\Attachments\VertexAttachment.cs" />
|
||||||
<Compile Include="src\BlendMode.cs">
|
<Compile Include="src\BlendMode.cs">
|
||||||
<SubType>Code</SubType>
|
<SubType>Code</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
@ -105,6 +105,8 @@
|
|||||||
<Compile Include="src\MathUtils.cs">
|
<Compile Include="src\MathUtils.cs">
|
||||||
<SubType>Code</SubType>
|
<SubType>Code</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="src\PathConstraint.cs" />
|
||||||
|
<Compile Include="src\PathConstraintData.cs" />
|
||||||
<Compile Include="src\Skeleton.cs">
|
<Compile Include="src\Skeleton.cs">
|
||||||
<SubType>Code</SubType>
|
<SubType>Code</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
|||||||
@ -43,8 +43,8 @@ namespace Spine {
|
|||||||
public float Duration { get { return duration; } set { duration = value; } }
|
public float Duration { get { return duration; } set { duration = value; } }
|
||||||
|
|
||||||
public Animation (String name, ExposedList<Timeline> timelines, float duration) {
|
public Animation (String name, ExposedList<Timeline> timelines, float duration) {
|
||||||
if (name == null) throw new ArgumentNullException("name cannot be null.");
|
if (name == null) throw new ArgumentNullException("name", "name cannot be null.");
|
||||||
if (timelines == null) throw new ArgumentNullException("timelines cannot be null.");
|
if (timelines == null) throw new ArgumentNullException("timelines", "timelines cannot be null.");
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.timelines = timelines;
|
this.timelines = timelines;
|
||||||
this.duration = duration;
|
this.duration = duration;
|
||||||
@ -52,9 +52,9 @@ namespace Spine {
|
|||||||
|
|
||||||
/// <summary>Poses the skeleton at the specified time for this animation.</summary>
|
/// <summary>Poses the skeleton at the specified time for this animation.</summary>
|
||||||
/// <param name="lastTime">The last time the animation was applied.</param>
|
/// <param name="lastTime">The last time the animation was applied.</param>
|
||||||
/// <param name="events">Any triggered events are added.</param>
|
/// <param name="events">Any triggered events are added. May be null.</param>
|
||||||
public void Apply (Skeleton skeleton, float lastTime, float time, bool loop, ExposedList<Event> events) {
|
public void Apply (Skeleton skeleton, float lastTime, float time, bool loop, ExposedList<Event> events) {
|
||||||
if (skeleton == null) throw new ArgumentNullException("skeleton cannot be null.");
|
if (skeleton == null) throw new ArgumentNullException("skeleton", "skeleton cannot be null.");
|
||||||
|
|
||||||
if (loop && duration != 0) {
|
if (loop && duration != 0) {
|
||||||
time %= duration;
|
time %= duration;
|
||||||
@ -68,10 +68,10 @@ namespace Spine {
|
|||||||
|
|
||||||
/// <summary>Poses the skeleton at the specified time for this animation mixed with the current pose.</summary>
|
/// <summary>Poses the skeleton at the specified time for this animation mixed with the current pose.</summary>
|
||||||
/// <param name="lastTime">The last time the animation was applied.</param>
|
/// <param name="lastTime">The last time the animation was applied.</param>
|
||||||
/// <param name="events">Any triggered events are added.</param>
|
/// <param name="events">Any triggered events are added. May be null.</param>
|
||||||
/// <param name="alpha">The amount of this animation that affects the current pose.</param>
|
/// <param name="alpha">The amount of this animation that affects the current pose.</param>
|
||||||
public void Mix (Skeleton skeleton, float lastTime, float time, bool loop, ExposedList<Event> events, float alpha) {
|
public void Mix (Skeleton skeleton, float lastTime, float time, bool loop, ExposedList<Event> events, float alpha) {
|
||||||
if (skeleton == null) throw new ArgumentNullException("skeleton cannot be null.");
|
if (skeleton == null) throw new ArgumentNullException("skeleton", "skeleton cannot be null.");
|
||||||
|
|
||||||
if (loop && duration != 0) {
|
if (loop && duration != 0) {
|
||||||
time %= duration;
|
time %= duration;
|
||||||
@ -131,12 +131,13 @@ namespace Spine {
|
|||||||
/// <summary>Base class for frames that use an interpolation bezier curve.</summary>
|
/// <summary>Base class for frames that use an interpolation bezier curve.</summary>
|
||||||
abstract public class CurveTimeline : Timeline {
|
abstract public class CurveTimeline : Timeline {
|
||||||
protected const float LINEAR = 0, STEPPED = 1, BEZIER = 2;
|
protected const float LINEAR = 0, STEPPED = 1, BEZIER = 2;
|
||||||
protected const int BEZIER_SEGMENTS = 10, BEZIER_SIZE = BEZIER_SEGMENTS * 2 - 1;
|
protected const int BEZIER_SIZE = 10 * 2 - 1;
|
||||||
|
|
||||||
private float[] curves; // type, x, y, ...
|
private float[] curves; // type, x, y, ...
|
||||||
public int FrameCount { get { return curves.Length / BEZIER_SIZE + 1; } }
|
public int FrameCount { get { return curves.Length / BEZIER_SIZE + 1; } }
|
||||||
|
|
||||||
public CurveTimeline (int frameCount) {
|
public CurveTimeline (int frameCount) {
|
||||||
|
if (frameCount <= 0) throw new ArgumentException("frameCount must be > 0: " + frameCount, "frameCount");
|
||||||
curves = new float[(frameCount - 1) * BEZIER_SIZE];
|
curves = new float[(frameCount - 1) * BEZIER_SIZE];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -154,12 +155,10 @@ namespace Spine {
|
|||||||
/// cx1 and cx2 are from 0 to 1, representing the percent of time between the two keyframes. cy1 and cy2 are the percent of
|
/// cx1 and cx2 are from 0 to 1, representing the percent of time between the two keyframes. cy1 and cy2 are the percent of
|
||||||
/// the difference between the keyframe's values.</summary>
|
/// the difference between the keyframe's values.</summary>
|
||||||
public void SetCurve (int frameIndex, float cx1, float cy1, float cx2, float cy2) {
|
public void SetCurve (int frameIndex, float cx1, float cy1, float cx2, float cy2) {
|
||||||
float subdiv1 = 1f / BEZIER_SEGMENTS, subdiv2 = subdiv1 * subdiv1, subdiv3 = subdiv2 * subdiv1;
|
float tmpx = (-cx1 * 2 + cx2) * 0.03f, tmpy = (-cy1 * 2 + cy2) * 0.03f;
|
||||||
float pre1 = 3 * subdiv1, pre2 = 3 * subdiv2, pre4 = 6 * subdiv2, pre5 = 6 * subdiv3;
|
float dddfx = ((cx1 - cx2) * 3 + 1) * 0.006f, dddfy = ((cy1 - cy2) * 3 + 1) * 0.006f;
|
||||||
float tmp1x = -cx1 * 2 + cx2, tmp1y = -cy1 * 2 + cy2, tmp2x = (cx1 - cx2) * 3 + 1, tmp2y = (cy1 - cy2) * 3 + 1;
|
float ddfx = tmpx * 2 + dddfx, ddfy = tmpy * 2 + dddfy;
|
||||||
float dfx = cx1 * pre1 + tmp1x * pre2 + tmp2x * subdiv3, dfy = cy1 * pre1 + tmp1y * pre2 + tmp2y * subdiv3;
|
float dfx = cx1 * 0.3f + tmpx + dddfx * 0.16666667f, dfy = cy1 * 0.3f + tmpy + dddfy * 0.16666667f;
|
||||||
float ddfx = tmp1x * pre4 + tmp2x * pre5, ddfy = tmp1y * pre4 + tmp2y * pre5;
|
|
||||||
float dddfx = tmp2x * pre5, dddfy = tmp2y * pre5;
|
|
||||||
|
|
||||||
int i = frameIndex * BEZIER_SIZE;
|
int i = frameIndex * BEZIER_SIZE;
|
||||||
float[] curves = this.curves;
|
float[] curves = this.curves;
|
||||||
@ -179,6 +178,7 @@ namespace Spine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public float GetCurvePercent (int frameIndex, float percent) {
|
public float GetCurvePercent (int frameIndex, float percent) {
|
||||||
|
percent = MathUtils.Clamp (percent, 0, 1);
|
||||||
float[] curves = this.curves;
|
float[] curves = this.curves;
|
||||||
int i = frameIndex * BEZIER_SIZE;
|
int i = frameIndex * BEZIER_SIZE;
|
||||||
float type = curves[i];
|
float type = curves[i];
|
||||||
@ -209,8 +209,9 @@ namespace Spine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public class RotateTimeline : CurveTimeline {
|
public class RotateTimeline : CurveTimeline {
|
||||||
internal const int PREV_TIME = -2;
|
public const int ENTRIES = 2;
|
||||||
internal const int VALUE = 1;
|
internal const int PREV_TIME = -2, PREV_ROTATION = -1;
|
||||||
|
internal const int ROTATION = 1;
|
||||||
|
|
||||||
internal int boneIndex;
|
internal int boneIndex;
|
||||||
internal float[] frames;
|
internal float[] frames;
|
||||||
@ -224,10 +225,10 @@ namespace Spine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Sets the time and value of the specified keyframe.</summary>
|
/// <summary>Sets the time and value of the specified keyframe.</summary>
|
||||||
public void SetFrame (int frameIndex, float time, float angle) {
|
public void SetFrame (int frameIndex, float time, float degrees) {
|
||||||
frameIndex *= 2;
|
frameIndex <<= 1;
|
||||||
frames[frameIndex] = time;
|
frames[frameIndex] = time;
|
||||||
frames[frameIndex + 1] = angle;
|
frames[frameIndex + ROTATION] = degrees;
|
||||||
}
|
}
|
||||||
|
|
||||||
override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha) {
|
override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha) {
|
||||||
@ -238,8 +239,8 @@ namespace Spine {
|
|||||||
|
|
||||||
float amount;
|
float amount;
|
||||||
|
|
||||||
if (time >= frames[frames.Length - 2]) { // Time is after last frame.
|
if (time >= frames[frames.Length - ENTRIES]) { // Time is after last frame.
|
||||||
amount = bone.data.rotation + frames[frames.Length - 1] - bone.rotation;
|
amount = bone.data.rotation + frames[frames.Length + PREV_ROTATION] - bone.rotation;
|
||||||
while (amount > 180)
|
while (amount > 180)
|
||||||
amount -= 360;
|
amount -= 360;
|
||||||
while (amount < -180)
|
while (amount < -180)
|
||||||
@ -249,18 +250,17 @@ namespace Spine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Interpolate between the previous frame and the current frame.
|
// Interpolate between the previous frame and the current frame.
|
||||||
int frame = Animation.binarySearch(frames, time, 2);
|
int frame = Animation.binarySearch(frames, time, ENTRIES);
|
||||||
float prevFrameValue = frames[frame - 1];
|
float prevRotation = frames[frame + PREV_ROTATION];
|
||||||
float frameTime = frames[frame];
|
float frameTime = frames[frame];
|
||||||
float percent = 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime);
|
float percent = GetCurvePercent((frame >> 1) - 1, 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime));
|
||||||
percent = GetCurvePercent((frame >> 1) - 1, percent < 0 ? 0 : (percent > 1 ? 1 : percent));
|
|
||||||
|
|
||||||
amount = frames[frame + VALUE] - prevFrameValue;
|
amount = frames[frame + ROTATION] - prevRotation;
|
||||||
while (amount > 180)
|
while (amount > 180)
|
||||||
amount -= 360;
|
amount -= 360;
|
||||||
while (amount < -180)
|
while (amount < -180)
|
||||||
amount += 360;
|
amount += 360;
|
||||||
amount = bone.data.rotation + (prevFrameValue + amount * percent) - bone.rotation;
|
amount = bone.data.rotation + (prevRotation + amount * percent) - bone.rotation;
|
||||||
while (amount > 180)
|
while (amount > 180)
|
||||||
amount -= 360;
|
amount -= 360;
|
||||||
while (amount < -180)
|
while (amount < -180)
|
||||||
@ -270,9 +270,9 @@ namespace Spine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public class TranslateTimeline : CurveTimeline {
|
public class TranslateTimeline : CurveTimeline {
|
||||||
protected const int PREV_TIME = -3;
|
public const int ENTRIES = 3;
|
||||||
protected const int X = 1;
|
protected const int PREV_TIME = -3, PREV_X = -2, PREV_Y = -1;
|
||||||
protected const int Y = 2;
|
protected const int X = 1, Y = 2;
|
||||||
|
|
||||||
internal int boneIndex;
|
internal int boneIndex;
|
||||||
internal float[] frames;
|
internal float[] frames;
|
||||||
@ -282,15 +282,15 @@ namespace Spine {
|
|||||||
|
|
||||||
public TranslateTimeline (int frameCount)
|
public TranslateTimeline (int frameCount)
|
||||||
: base(frameCount) {
|
: base(frameCount) {
|
||||||
frames = new float[frameCount * 3];
|
frames = new float[frameCount * ENTRIES];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Sets the time and value of the specified keyframe.</summary>
|
/// <summary>Sets the time and value of the specified keyframe.</summary>
|
||||||
public void SetFrame (int frameIndex, float time, float x, float y) {
|
public void SetFrame (int frameIndex, float time, float x, float y) {
|
||||||
frameIndex *= 3;
|
frameIndex *= ENTRIES;
|
||||||
frames[frameIndex] = time;
|
frames[frameIndex] = time;
|
||||||
frames[frameIndex + 1] = x;
|
frames[frameIndex + X] = x;
|
||||||
frames[frameIndex + 2] = y;
|
frames[frameIndex + Y] = y;
|
||||||
}
|
}
|
||||||
|
|
||||||
override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha) {
|
override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha) {
|
||||||
@ -299,22 +299,21 @@ namespace Spine {
|
|||||||
|
|
||||||
Bone bone = skeleton.bones.Items[boneIndex];
|
Bone bone = skeleton.bones.Items[boneIndex];
|
||||||
|
|
||||||
if (time >= frames[frames.Length - 3]) { // Time is after last frame.
|
if (time >= frames[frames.Length - ENTRIES]) { // Time is after last frame.
|
||||||
bone.x += (bone.data.x + frames[frames.Length - 2] - bone.x) * alpha;
|
bone.x += (bone.data.x + frames[frames.Length + PREV_X] - bone.x) * alpha;
|
||||||
bone.y += (bone.data.y + frames[frames.Length - 1] - bone.y) * alpha;
|
bone.y += (bone.data.y + frames[frames.Length + PREV_Y] - bone.y) * alpha;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Interpolate between the previous frame and the current frame.
|
// Interpolate between the previous frame and the current frame.
|
||||||
int frame = Animation.binarySearch(frames, time, 3);
|
int frame = Animation.binarySearch(frames, time, ENTRIES);
|
||||||
float prevFrameX = frames[frame - 2];
|
float prevX = frames[frame + PREV_X];
|
||||||
float prevFrameY = frames[frame - 1];
|
float prevY = frames[frame + PREV_Y];
|
||||||
float frameTime = frames[frame];
|
float frameTime = frames[frame];
|
||||||
float percent = 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime);
|
float percent = GetCurvePercent(frame / ENTRIES - 1, 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime));
|
||||||
percent = GetCurvePercent(frame / 3 - 1, percent < 0 ? 0 : (percent > 1 ? 1 : percent));
|
|
||||||
|
|
||||||
bone.x += (bone.data.x + prevFrameX + (frames[frame + X] - prevFrameX) * percent - bone.x) * alpha;
|
bone.x += (bone.data.x + prevX + (frames[frame + X] - prevX) * percent - bone.x) * alpha;
|
||||||
bone.y += (bone.data.y + prevFrameY + (frames[frame + Y] - prevFrameY) * percent - bone.y) * alpha;
|
bone.y += (bone.data.y + prevY + (frames[frame + Y] - prevY) * percent - bone.y) * alpha;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -328,28 +327,27 @@ namespace Spine {
|
|||||||
if (time < frames[0]) return; // Time is before first frame.
|
if (time < frames[0]) return; // Time is before first frame.
|
||||||
|
|
||||||
Bone bone = skeleton.bones.Items[boneIndex];
|
Bone bone = skeleton.bones.Items[boneIndex];
|
||||||
if (time >= frames[frames.Length - 3]) { // Time is after last frame.
|
if (time >= frames[frames.Length - ENTRIES]) { // Time is after last frame.
|
||||||
bone.scaleX += (bone.data.scaleX * frames[frames.Length - 2] - bone.scaleX) * alpha;
|
bone.scaleX += (bone.data.scaleX * frames[frames.Length + PREV_X] - bone.scaleX) * alpha;
|
||||||
bone.scaleY += (bone.data.scaleY * frames[frames.Length - 1] - bone.scaleY) * alpha;
|
bone.scaleY += (bone.data.scaleY * frames[frames.Length + PREV_Y] - bone.scaleY) * alpha;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Interpolate between the previous frame and the current frame.
|
// Interpolate between the previous frame and the current frame.
|
||||||
int frame = Animation.binarySearch(frames, time, 3);
|
int frame = Animation.binarySearch(frames, time, ENTRIES);
|
||||||
float prevFrameX = frames[frame - 2];
|
float prevX = frames[frame + PREV_X];
|
||||||
float prevFrameY = frames[frame - 1];
|
float prevY = frames[frame + PREV_Y];
|
||||||
float frameTime = frames[frame];
|
float frameTime = frames[frame];
|
||||||
float percent = 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime);
|
float percent = GetCurvePercent(frame / ENTRIES - 1, 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime));
|
||||||
percent = GetCurvePercent(frame / 3 - 1, percent < 0 ? 0 : (percent > 1 ? 1 : percent));
|
|
||||||
|
|
||||||
bone.scaleX += (bone.data.scaleX * (prevFrameX + (frames[frame + X] - prevFrameX) * percent) - bone.scaleX) * alpha;
|
bone.scaleX += (bone.data.scaleX * (prevX + (frames[frame + X] - prevX) * percent) - bone.scaleX) * alpha;
|
||||||
bone.scaleY += (bone.data.scaleY * (prevFrameY + (frames[frame + Y] - prevFrameY) * percent) - bone.scaleY) * alpha;
|
bone.scaleY += (bone.data.scaleY * (prevY + (frames[frame + Y] - prevY) * percent) - bone.scaleY) * alpha;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ShearTimeline : TranslateTimeline {
|
public class ShearTimeline : TranslateTimeline {
|
||||||
public ShearTimeline (int frameCount)
|
public ShearTimeline (int frameCount)
|
||||||
: base (frameCount) {
|
: base(frameCount) {
|
||||||
}
|
}
|
||||||
|
|
||||||
override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha) {
|
override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha) {
|
||||||
@ -357,31 +355,28 @@ namespace Spine {
|
|||||||
if (time < frames[0]) return; // Time is before first frame.
|
if (time < frames[0]) return; // Time is before first frame.
|
||||||
|
|
||||||
Bone bone = skeleton.bones.Items[boneIndex];
|
Bone bone = skeleton.bones.Items[boneIndex];
|
||||||
if (time >= frames[frames.Length - 3]) { // Time is after last frame.
|
if (time >= frames[frames.Length - ENTRIES]) { // Time is after last frame.
|
||||||
bone.shearX += (bone.data.shearX + frames[frames.Length - 2] - bone.shearX) * alpha;
|
bone.shearX += (bone.data.shearX + frames[frames.Length + PREV_X] - bone.shearX) * alpha;
|
||||||
bone.shearY += (bone.data.shearY + frames[frames.Length - 1] - bone.shearY) * alpha;
|
bone.shearY += (bone.data.shearY + frames[frames.Length + PREV_Y] - bone.shearY) * alpha;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Interpolate between the previous frame and the current frame.
|
// Interpolate between the previous frame and the current frame.
|
||||||
int frame = Animation.binarySearch(frames, time, 3);
|
int frame = Animation.binarySearch(frames, time, ENTRIES);
|
||||||
float prevFrameX = frames[frame - 2];
|
float prevX = frames[frame + PREV_X];
|
||||||
float prevFrameY = frames[frame - 1];
|
float prevY = frames[frame + PREV_Y];
|
||||||
float frameTime = frames[frame];
|
float frameTime = frames[frame];
|
||||||
float percent = 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime);
|
float percent = GetCurvePercent(frame / ENTRIES - 1, 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime));
|
||||||
percent = GetCurvePercent(frame / 3 - 1, percent < 0 ? 0 : (percent > 1 ? 1 : percent));
|
|
||||||
|
|
||||||
bone.shearX += (bone.data.shearX + (prevFrameX + (frames[frame + X] - prevFrameX) * percent) - bone.shearX) * alpha;
|
bone.shearX += (bone.data.shearX + (prevX + (frames[frame + X] - prevX) * percent) - bone.shearX) * alpha;
|
||||||
bone.shearY += (bone.data.shearY + (prevFrameY + (frames[frame + Y] - prevFrameY) * percent) - bone.shearY) * alpha;
|
bone.shearY += (bone.data.shearY + (prevY + (frames[frame + Y] - prevY) * percent) - bone.shearY) * alpha;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ColorTimeline : CurveTimeline {
|
public class ColorTimeline : CurveTimeline {
|
||||||
protected const int PREV_TIME = -5;
|
public const int ENTRIES = 5;
|
||||||
protected const int R = 1;
|
protected const int PREV_TIME = -5, PREV_R = -4, PREV_G = -3, PREV_B = -2, PREV_A = -1;
|
||||||
protected const int G = 2;
|
protected const int R = 1, G = 2, B = 3, A = 4;
|
||||||
protected const int B = 3;
|
|
||||||
protected const int A = 4;
|
|
||||||
|
|
||||||
internal int slotIndex;
|
internal int slotIndex;
|
||||||
internal float[] frames;
|
internal float[] frames;
|
||||||
@ -391,17 +386,17 @@ namespace Spine {
|
|||||||
|
|
||||||
public ColorTimeline (int frameCount)
|
public ColorTimeline (int frameCount)
|
||||||
: base(frameCount) {
|
: base(frameCount) {
|
||||||
frames = new float[frameCount * 5];
|
frames = new float[frameCount * ENTRIES];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Sets the time and value of the specified keyframe.</summary>
|
/// <summary>Sets the time and value of the specified keyframe.</summary>
|
||||||
public void SetFrame (int frameIndex, float time, float r, float g, float b, float a) {
|
public void SetFrame (int frameIndex, float time, float r, float g, float b, float a) {
|
||||||
frameIndex *= 5;
|
frameIndex *= ENTRIES;
|
||||||
frames[frameIndex] = time;
|
frames[frameIndex] = time;
|
||||||
frames[frameIndex + 1] = r;
|
frames[frameIndex + R] = r;
|
||||||
frames[frameIndex + 2] = g;
|
frames[frameIndex + G] = g;
|
||||||
frames[frameIndex + 3] = b;
|
frames[frameIndex + B] = b;
|
||||||
frames[frameIndex + 4] = a;
|
frames[frameIndex + A] = a;
|
||||||
}
|
}
|
||||||
|
|
||||||
override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha) {
|
override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha) {
|
||||||
@ -409,23 +404,23 @@ namespace Spine {
|
|||||||
if (time < frames[0]) return; // Time is before first frame.
|
if (time < frames[0]) return; // Time is before first frame.
|
||||||
|
|
||||||
float r, g, b, a;
|
float r, g, b, a;
|
||||||
if (time >= frames[frames.Length - 5]) { // Time is after last frame.
|
if (time >= frames[frames.Length - ENTRIES]) { // Time is after last frame.
|
||||||
int i = frames.Length - 1;
|
int i = frames.Length;
|
||||||
r = frames[i - 3];
|
r = frames[i + PREV_R];
|
||||||
g = frames[i - 2];
|
g = frames[i + PREV_G];
|
||||||
b = frames[i - 1];
|
b = frames[i + PREV_B];
|
||||||
a = frames[i];
|
a = frames[i + PREV_A];
|
||||||
} else {
|
} else {
|
||||||
// Interpolate between the previous frame and the current frame.
|
// Interpolate between the previous frame and the current frame.
|
||||||
int frame = Animation.binarySearch(frames, time, 5);
|
int frame = Animation.binarySearch(frames, time, ENTRIES);
|
||||||
|
r = frames[frame + PREV_R];
|
||||||
|
g = frames[frame + PREV_G];
|
||||||
|
b = frames[frame + PREV_B];
|
||||||
|
a = frames[frame + PREV_A];
|
||||||
float frameTime = frames[frame];
|
float frameTime = frames[frame];
|
||||||
float percent = 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime);
|
float percent = GetCurvePercent(frame / ENTRIES - 1,
|
||||||
percent = GetCurvePercent(frame / 5 - 1, percent < 0 ? 0 : (percent > 1 ? 1 : percent));
|
1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime));
|
||||||
|
|
||||||
r = frames[frame - 4];
|
|
||||||
g = frames[frame - 3];
|
|
||||||
b = frames[frame - 2];
|
|
||||||
a = frames[frame - 1];
|
|
||||||
r += (frames[frame + R] - r) * percent;
|
r += (frames[frame + R] - r) * percent;
|
||||||
g += (frames[frame + G] - g) * percent;
|
g += (frames[frame + G] - g) * percent;
|
||||||
b += (frames[frame + B] - b) * percent;
|
b += (frames[frame + B] - b) * percent;
|
||||||
@ -469,18 +464,17 @@ namespace Spine {
|
|||||||
|
|
||||||
public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha) {
|
public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha) {
|
||||||
float[] frames = this.frames;
|
float[] frames = this.frames;
|
||||||
if (time < frames[0]) {
|
if (time < frames[0]) return; // Time is before first frame.
|
||||||
if (lastTime > time) Apply(skeleton, lastTime, int.MaxValue, null, 0);
|
|
||||||
return;
|
|
||||||
} else if (lastTime > time) //
|
|
||||||
lastTime = -1;
|
|
||||||
|
|
||||||
int frameIndex = (time >= frames[frames.Length - 1] ? frames.Length : Animation.binarySearch(frames, time)) - 1;
|
int frameIndex;
|
||||||
if (frames[frameIndex] < lastTime) return;
|
if (time >= frames[frames.Length - 1]) // Time is after last frame.
|
||||||
|
frameIndex = frames.Length - 1;
|
||||||
|
else
|
||||||
|
frameIndex = Animation.binarySearch(frames, time, 1) - 1;
|
||||||
|
|
||||||
String attachmentName = attachmentNames[frameIndex];
|
String attachmentName = attachmentNames[frameIndex];
|
||||||
skeleton.slots.Items[slotIndex].Attachment =
|
skeleton.slots.Items[slotIndex]
|
||||||
attachmentName == null ? null : skeleton.GetAttachment(slotIndex, attachmentName);
|
.Attachment = attachmentName == null ? null : skeleton.GetAttachment(slotIndex, attachmentName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -503,7 +497,7 @@ namespace Spine {
|
|||||||
events[frameIndex] = e;
|
events[frameIndex] = e;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Fires events for frames > lastTime and <= time.</summary>
|
/// <summary>Fires events for frames > lastTime and <= time.</summary>
|
||||||
public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha) {
|
public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha) {
|
||||||
if (firedEvents == null) return;
|
if (firedEvents == null) return;
|
||||||
float[] frames = this.frames;
|
float[] frames = this.frames;
|
||||||
@ -570,24 +564,26 @@ namespace Spine {
|
|||||||
for (int i = 0, n = slots.Count; i < n; i++)
|
for (int i = 0, n = slots.Count; i < n; i++)
|
||||||
drawOrder.Add(slots.Items[i]);
|
drawOrder.Add(slots.Items[i]);
|
||||||
} else {
|
} else {
|
||||||
|
var drawOrderItems = drawOrder.Items;
|
||||||
|
var slotsItems = slots.Items;
|
||||||
for (int i = 0, n = drawOrderToSetupIndex.Length; i < n; i++)
|
for (int i = 0, n = drawOrderToSetupIndex.Length; i < n; i++)
|
||||||
drawOrder.Items[i] = slots.Items[drawOrderToSetupIndex[i]];
|
drawOrderItems[i] = slotsItems[drawOrderToSetupIndex[i]];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class FfdTimeline : CurveTimeline {
|
public class DeformTimeline : CurveTimeline {
|
||||||
internal int slotIndex;
|
internal int slotIndex;
|
||||||
internal float[] frames;
|
internal float[] frames;
|
||||||
private float[][] frameVertices;
|
private float[][] frameVertices;
|
||||||
internal Attachment attachment;
|
internal VertexAttachment attachment;
|
||||||
|
|
||||||
public int SlotIndex { get { return slotIndex; } set { slotIndex = value; } }
|
public int SlotIndex { get { return slotIndex; } set { slotIndex = value; } }
|
||||||
public float[] Frames { get { return frames; } set { frames = value; } } // time, ...
|
public float[] Frames { get { return frames; } set { frames = value; } } // time, ...
|
||||||
public float[][] Vertices { get { return frameVertices; } set { frameVertices = value; } }
|
public float[][] Vertices { get { return frameVertices; } set { frameVertices = value; } }
|
||||||
public Attachment Attachment { get { return attachment; } set { attachment = value; } }
|
public VertexAttachment Attachment { get { return attachment; } set { attachment = value; } }
|
||||||
|
|
||||||
public FfdTimeline (int frameCount)
|
public DeformTimeline (int frameCount)
|
||||||
: base(frameCount) {
|
: base(frameCount) {
|
||||||
frames = new float[frameCount];
|
frames = new float[frameCount];
|
||||||
frameVertices = new float[frameCount][];
|
frameVertices = new float[frameCount][];
|
||||||
@ -601,8 +597,8 @@ namespace Spine {
|
|||||||
|
|
||||||
override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha) {
|
override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha) {
|
||||||
Slot slot = skeleton.slots.Items[slotIndex];
|
Slot slot = skeleton.slots.Items[slotIndex];
|
||||||
IFfdAttachment ffdAttachment = slot.attachment as IFfdAttachment;
|
VertexAttachment slotAttachment = slot.attachment as VertexAttachment;
|
||||||
if (ffdAttachment == null || !ffdAttachment.ApplyFFD(attachment)) return;
|
if (slotAttachment == null || !slotAttachment.ApplyDeform(attachment)) return;
|
||||||
|
|
||||||
float[] frames = this.frames;
|
float[] frames = this.frames;
|
||||||
if (time < frames[0]) return; // Time is before first frame.
|
if (time < frames[0]) return; // Time is before first frame.
|
||||||
@ -610,15 +606,12 @@ namespace Spine {
|
|||||||
float[][] frameVertices = this.frameVertices;
|
float[][] frameVertices = this.frameVertices;
|
||||||
int vertexCount = frameVertices[0].Length;
|
int vertexCount = frameVertices[0].Length;
|
||||||
|
|
||||||
float[] vertices = slot.attachmentVertices;
|
var verticesArray = slot.attachmentVertices;
|
||||||
if (slot.attachmentVerticesCount != vertexCount) alpha = 1; // Don't mix from uninitialized slot vertices.
|
if (verticesArray.Count != vertexCount) alpha = 1; // Don't mix from uninitialized slot vertices.
|
||||||
|
// verticesArray.SetSize(vertexCount) // Ensure size and preemptively set count.
|
||||||
// Ensure capacity
|
if (verticesArray.Capacity < vertexCount) verticesArray.Capacity = vertexCount;
|
||||||
if (vertices.Length < vertexCount) {
|
verticesArray.Count = vertexCount;
|
||||||
vertices = new float[vertexCount];
|
float[] vertices = verticesArray.Items;
|
||||||
slot.attachmentVertices = vertices;
|
|
||||||
}
|
|
||||||
slot.attachmentVerticesCount = vertexCount;
|
|
||||||
|
|
||||||
if (time >= frames[frames.Length - 1]) { // Time is after last frame.
|
if (time >= frames[frames.Length - 1]) { // Time is after last frame.
|
||||||
float[] lastVertices = frameVertices[frames.Length - 1];
|
float[] lastVertices = frameVertices[frames.Length - 1];
|
||||||
@ -634,12 +627,10 @@ namespace Spine {
|
|||||||
|
|
||||||
// Interpolate between the previous frame and the current frame.
|
// Interpolate between the previous frame and the current frame.
|
||||||
int frame = Animation.binarySearch(frames, time);
|
int frame = Animation.binarySearch(frames, time);
|
||||||
float frameTime = frames[frame];
|
|
||||||
float percent = 1 - (time - frameTime) / (frames[frame - 1] - frameTime);
|
|
||||||
percent = GetCurvePercent(frame - 1, percent < 0 ? 0 : (percent > 1 ? 1 : percent));
|
|
||||||
|
|
||||||
float[] prevVertices = frameVertices[frame - 1];
|
float[] prevVertices = frameVertices[frame - 1];
|
||||||
float[] nextVertices = frameVertices[frame];
|
float[] nextVertices = frameVertices[frame];
|
||||||
|
float frameTime = frames[frame];
|
||||||
|
float percent = GetCurvePercent(frame - 1, 1 - (time - frameTime) / (frames[frame - 1] - frameTime));
|
||||||
|
|
||||||
if (alpha < 1) {
|
if (alpha < 1) {
|
||||||
for (int i = 0; i < vertexCount; i++) {
|
for (int i = 0; i < vertexCount; i++) {
|
||||||
@ -657,10 +648,9 @@ namespace Spine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public class IkConstraintTimeline : CurveTimeline {
|
public class IkConstraintTimeline : CurveTimeline {
|
||||||
private const int PREV_TIME = -3;
|
public const int ENTRIES = 3;
|
||||||
private const int PREV_MIX = -2;
|
private const int PREV_TIME = -3, PREV_MIX = -2, PREV_BEND_DIRECTION = -1;
|
||||||
private const int PREV_BEND_DIRECTION = -1;
|
private const int MIX = 1, BEND_DIRECTION = 2;
|
||||||
private const int MIX = 1;
|
|
||||||
|
|
||||||
internal int ikConstraintIndex;
|
internal int ikConstraintIndex;
|
||||||
internal float[] frames;
|
internal float[] frames;
|
||||||
@ -670,15 +660,15 @@ namespace Spine {
|
|||||||
|
|
||||||
public IkConstraintTimeline (int frameCount)
|
public IkConstraintTimeline (int frameCount)
|
||||||
: base(frameCount) {
|
: base(frameCount) {
|
||||||
frames = new float[frameCount * 3];
|
frames = new float[frameCount * ENTRIES];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Sets the time, mix and bend direction of the specified keyframe.</summary>
|
/// <summary>Sets the time, mix and bend direction of the specified keyframe.</summary>
|
||||||
public void SetFrame (int frameIndex, float time, float mix, int bendDirection) {
|
public void SetFrame (int frameIndex, float time, float mix, int bendDirection) {
|
||||||
frameIndex *= 3;
|
frameIndex *= ENTRIES;
|
||||||
frames[frameIndex] = time;
|
frames[frameIndex] = time;
|
||||||
frames[frameIndex + 1] = mix;
|
frames[frameIndex + MIX] = mix;
|
||||||
frames[frameIndex + 2] = bendDirection;
|
frames[frameIndex + BEND_DIRECTION] = bendDirection;
|
||||||
}
|
}
|
||||||
|
|
||||||
override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha) {
|
override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha) {
|
||||||
@ -687,34 +677,27 @@ namespace Spine {
|
|||||||
|
|
||||||
IkConstraint constraint = skeleton.ikConstraints.Items[ikConstraintIndex];
|
IkConstraint constraint = skeleton.ikConstraints.Items[ikConstraintIndex];
|
||||||
|
|
||||||
if (time >= frames[frames.Length - 3]) { // Time is after last frame.
|
if (time >= frames[frames.Length - ENTRIES]) { // Time is after last frame.
|
||||||
constraint.mix += (frames[frames.Length + PREV_MIX] - constraint.mix) * alpha;
|
constraint.mix += (frames[frames.Length + PREV_MIX] - constraint.mix) * alpha;
|
||||||
constraint.bendDirection = (int)frames[frames.Length + PREV_BEND_DIRECTION];
|
constraint.bendDirection = (int)frames[frames.Length + PREV_BEND_DIRECTION];
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Interpolate between the previous frame and the current frame.
|
// Interpolate between the previous frame and the current frame.
|
||||||
int frame = Animation.binarySearch(frames, time, 3);
|
int frame = Animation.binarySearch(frames, time, ENTRIES);
|
||||||
float frameTime = frames[frame];
|
|
||||||
float percent = 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime);
|
|
||||||
percent = GetCurvePercent(frame / 3 - 1, percent < 0 ? 0 : (percent > 1 ? 1 : percent));
|
|
||||||
|
|
||||||
float mix = frames[frame + PREV_MIX];
|
float mix = frames[frame + PREV_MIX];
|
||||||
|
float frameTime = frames[frame];
|
||||||
|
float percent = GetCurvePercent(frame / ENTRIES - 1, 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime));
|
||||||
|
|
||||||
constraint.mix += (mix + (frames[frame + MIX] - mix) * percent - constraint.mix) * alpha;
|
constraint.mix += (mix + (frames[frame + MIX] - mix) * percent - constraint.mix) * alpha;
|
||||||
constraint.bendDirection = (int)frames[frame + PREV_BEND_DIRECTION];
|
constraint.bendDirection = (int)frames[frame + PREV_BEND_DIRECTION];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class TransformConstraintTimeline : CurveTimeline {
|
public class TransformConstraintTimeline : CurveTimeline {
|
||||||
private const int PREV_TIME = -5;
|
public const int ENTRIES = 5;
|
||||||
private const int PREV_ROTATE_MIX = -4;
|
private const int PREV_TIME = -5, PREV_ROTATE = -4, PREV_TRANSLATE = -3, PREV_SCALE = -2, PREV_SHEAR = -1;
|
||||||
private const int PREV_TRANSLATE_MIX = -3;
|
private const int ROTATE = 1, TRANSLATE = 2, SCALE = 3, SHEAR = 4;
|
||||||
private const int PREV_SCALE_MIX = -2;
|
|
||||||
private const int PREV_SHEAR_MIX = -1;
|
|
||||||
private const int ROTATE_MIX = 1;
|
|
||||||
private const int TRANSLATE_MIX = 2;
|
|
||||||
private const int SCALE_MIX = 3;
|
|
||||||
private const int SHEAR_MIX = 4;
|
|
||||||
|
|
||||||
internal int transformConstraintIndex;
|
internal int transformConstraintIndex;
|
||||||
internal float[] frames;
|
internal float[] frames;
|
||||||
@ -724,16 +707,16 @@ namespace Spine {
|
|||||||
|
|
||||||
public TransformConstraintTimeline (int frameCount)
|
public TransformConstraintTimeline (int frameCount)
|
||||||
: base(frameCount) {
|
: base(frameCount) {
|
||||||
frames = new float[frameCount * 5];
|
frames = new float[frameCount * ENTRIES];
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetFrame (int frameIndex, float time, float rotateMix, float translateMix, float scaleMix, float shearMix) {
|
public void SetFrame (int frameIndex, float time, float rotateMix, float translateMix, float scaleMix, float shearMix) {
|
||||||
frameIndex *= 5;
|
frameIndex *= ENTRIES;
|
||||||
frames[frameIndex] = time;
|
frames[frameIndex] = time;
|
||||||
frames[frameIndex + 1] = rotateMix;
|
frames[frameIndex + ROTATE] = rotateMix;
|
||||||
frames[frameIndex + 2] = translateMix;
|
frames[frameIndex + TRANSLATE] = translateMix;
|
||||||
frames[frameIndex + 3] = scaleMix;
|
frames[frameIndex + SCALE] = scaleMix;
|
||||||
frames[frameIndex + 4] = shearMix;
|
frames[frameIndex + SHEAR] = shearMix;
|
||||||
}
|
}
|
||||||
|
|
||||||
override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha) {
|
override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha) {
|
||||||
@ -742,29 +725,151 @@ namespace Spine {
|
|||||||
|
|
||||||
TransformConstraint constraint = skeleton.transformConstraints.Items[transformConstraintIndex];
|
TransformConstraint constraint = skeleton.transformConstraints.Items[transformConstraintIndex];
|
||||||
|
|
||||||
if (time >= frames[frames.Length - 5]) { // Time is after last frame.
|
if (time >= frames[frames.Length - ENTRIES]) { // Time is after last frame.
|
||||||
int i = frames.Length - 1;
|
int i = frames.Length;
|
||||||
constraint.rotateMix += (frames[i - 3] - constraint.rotateMix) * alpha;
|
constraint.rotateMix += (frames[i + PREV_ROTATE] - constraint.rotateMix) * alpha;
|
||||||
constraint.translateMix += (frames[i - 2] - constraint.translateMix) * alpha;
|
constraint.translateMix += (frames[i + PREV_TRANSLATE] - constraint.translateMix) * alpha;
|
||||||
constraint.scaleMix += (frames[i - 1] - constraint.scaleMix) * alpha;
|
constraint.scaleMix += (frames[i + PREV_SCALE] - constraint.scaleMix) * alpha;
|
||||||
constraint.shearMix += (frames[i] - constraint.shearMix) * alpha;
|
constraint.shearMix += (frames[i + PREV_SHEAR] - constraint.shearMix) * alpha;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Interpolate between the previous frame and the current frame.
|
// Interpolate between the previous frame and the current frame.
|
||||||
int frame = Animation.binarySearch(frames, time, 5);
|
int frame = Animation.binarySearch(frames, time, ENTRIES);
|
||||||
float frameTime = frames[frame];
|
float frameTime = frames[frame];
|
||||||
float percent = 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime);
|
float percent = GetCurvePercent(frame / ENTRIES - 1, 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime));
|
||||||
percent = GetCurvePercent(frame / 5 - 1, percent < 0 ? 0 : (percent > 1 ? 1 : percent));
|
|
||||||
|
|
||||||
float rotate = frames[frame + PREV_ROTATE_MIX];
|
float rotate = frames[frame + PREV_ROTATE];
|
||||||
float translate = frames[frame + PREV_TRANSLATE_MIX];
|
float translate = frames[frame + PREV_TRANSLATE];
|
||||||
float scale = frames[frame + PREV_SCALE_MIX];
|
float scale = frames[frame + PREV_SCALE];
|
||||||
float shear = frames[frame + PREV_SHEAR_MIX];
|
float shear = frames[frame + PREV_SHEAR];
|
||||||
constraint.rotateMix += (rotate + (frames[frame + ROTATE_MIX] - rotate) * percent - constraint.rotateMix) * alpha;
|
constraint.rotateMix += (rotate + (frames[frame + ROTATE] - rotate) * percent - constraint.rotateMix) * alpha;
|
||||||
constraint.translateMix += (translate + (frames[frame + TRANSLATE_MIX] - translate) * percent - constraint.translateMix) * alpha;
|
constraint.translateMix += (translate + (frames[frame + TRANSLATE] - translate) * percent - constraint.translateMix)
|
||||||
constraint.scaleMix += (scale + (frames[frame + SCALE_MIX] - scale) * percent - constraint.scaleMix) * alpha;
|
* alpha;
|
||||||
constraint.shearMix += (shear + (frames[frame + SHEAR_MIX] - shear) * percent - constraint.shearMix) * alpha;
|
constraint.scaleMix += (scale + (frames[frame + SCALE] - scale) * percent - constraint.scaleMix) * alpha;
|
||||||
|
constraint.shearMix += (shear + (frames[frame + SHEAR] - shear) * percent - constraint.shearMix) * alpha;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PathConstraintPositionTimeline : CurveTimeline {
|
||||||
|
public const int ENTRIES = 2;
|
||||||
|
protected const int PREV_TIME = -2, PREV_VALUE = -1;
|
||||||
|
protected const int VALUE = 1;
|
||||||
|
|
||||||
|
internal int pathConstraintIndex;
|
||||||
|
internal float[] frames;
|
||||||
|
|
||||||
|
public PathConstraintPositionTimeline (int frameCount)
|
||||||
|
: base(frameCount) {
|
||||||
|
frames = new float[frameCount * ENTRIES];
|
||||||
|
}
|
||||||
|
|
||||||
|
public int PathConstraintIndex { get { return pathConstraintIndex; } set { pathConstraintIndex = value; } }
|
||||||
|
public float[] Frames { get { return frames; } set { frames = value; } } // time, position, ...
|
||||||
|
|
||||||
|
/// <summary>Sets the time and value of the specified keyframe.</summary>
|
||||||
|
public void SetFrame (int frameIndex, float time, float value) {
|
||||||
|
frameIndex *= ENTRIES;
|
||||||
|
frames[frameIndex] = time;
|
||||||
|
frames[frameIndex + VALUE] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> events, float alpha) {
|
||||||
|
float[] frames = this.frames;
|
||||||
|
if (time < frames[0]) return; // Time is before first frame.
|
||||||
|
|
||||||
|
PathConstraint constraint = skeleton.pathConstraints.Items[pathConstraintIndex];
|
||||||
|
|
||||||
|
if (time >= frames[frames.Length - ENTRIES]) { // Time is after last frame.
|
||||||
|
int i = frames.Length;
|
||||||
|
constraint.position += (frames[i + PREV_VALUE] - constraint.position) * alpha;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Interpolate between the previous frame and the current frame.
|
||||||
|
int frame = Animation.binarySearch(frames, time, ENTRIES);
|
||||||
|
float position = frames[frame + PREV_VALUE];
|
||||||
|
float frameTime = frames[frame];
|
||||||
|
float percent = GetCurvePercent(frame / ENTRIES - 1, 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime));
|
||||||
|
|
||||||
|
constraint.position += (position + (frames[frame + VALUE] - position) * percent - constraint.position) * alpha;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PathConstraintSpacingTimeline : PathConstraintPositionTimeline {
|
||||||
|
public PathConstraintSpacingTimeline (int frameCount)
|
||||||
|
: base(frameCount) {
|
||||||
|
}
|
||||||
|
|
||||||
|
override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> events, float alpha) {
|
||||||
|
float[] frames = this.frames;
|
||||||
|
if (time < frames[0]) return; // Time is before first frame.
|
||||||
|
|
||||||
|
PathConstraint constraint = skeleton.pathConstraints.Items[pathConstraintIndex];
|
||||||
|
|
||||||
|
if (time >= frames[frames.Length - ENTRIES]) { // Time is after last frame.
|
||||||
|
int i = frames.Length;
|
||||||
|
constraint.spacing += (frames[i + PREV_VALUE] - constraint.spacing) * alpha;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Interpolate between the previous frame and the current frame.
|
||||||
|
int frame = Animation.binarySearch(frames, time, ENTRIES);
|
||||||
|
float spacing = frames[frame + PREV_VALUE];
|
||||||
|
float frameTime = frames[frame];
|
||||||
|
float percent = GetCurvePercent(frame / ENTRIES - 1, 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime));
|
||||||
|
|
||||||
|
constraint.spacing += (spacing + (frames[frame + VALUE] - spacing) * percent - constraint.spacing) * alpha;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PathConstraintMixTimeline : CurveTimeline {
|
||||||
|
public const int ENTRIES = 3;
|
||||||
|
private const int PREV_TIME = -3, PREV_ROTATE = -2, PREV_TRANSLATE = -1;
|
||||||
|
private const int ROTATE = 1, TRANSLATE = 2;
|
||||||
|
|
||||||
|
internal int pathConstraintIndex;
|
||||||
|
internal float[] frames;
|
||||||
|
|
||||||
|
public int PathConstraintIndex { get { return pathConstraintIndex; } set { pathConstraintIndex = value; } }
|
||||||
|
public float[] Frames { get { return frames; } set { frames = value; } } // time, rotate mix, translate mix, ...
|
||||||
|
|
||||||
|
public PathConstraintMixTimeline (int frameCount)
|
||||||
|
: base(frameCount) {
|
||||||
|
frames = new float[frameCount * ENTRIES];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Sets the time and mixes of the specified keyframe. */
|
||||||
|
public void SetFrame (int frameIndex, float time, float rotateMix, float translateMix) {
|
||||||
|
frameIndex *= ENTRIES;
|
||||||
|
frames[frameIndex] = time;
|
||||||
|
frames[frameIndex + ROTATE] = rotateMix;
|
||||||
|
frames[frameIndex + TRANSLATE] = translateMix;
|
||||||
|
}
|
||||||
|
|
||||||
|
override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> events, float alpha) {
|
||||||
|
float[] frames = this.frames;
|
||||||
|
if (time < frames[0]) return; // Time is before first frame.
|
||||||
|
|
||||||
|
PathConstraint constraint = skeleton.pathConstraints.Items[pathConstraintIndex];
|
||||||
|
|
||||||
|
if (time >= frames[frames.Length - ENTRIES]) { // Time is after last frame.
|
||||||
|
int i = frames.Length;
|
||||||
|
constraint.rotateMix += (frames[i + PREV_ROTATE] - constraint.rotateMix) * alpha;
|
||||||
|
constraint.translateMix += (frames[i + PREV_TRANSLATE] - constraint.translateMix) * alpha;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Interpolate between the previous frame and the current frame.
|
||||||
|
int frame = Animation.binarySearch(frames, time, ENTRIES);
|
||||||
|
float rotate = frames[frame + PREV_ROTATE];
|
||||||
|
float translate = frames[frame + PREV_TRANSLATE];
|
||||||
|
float frameTime = frames[frame];
|
||||||
|
float percent = GetCurvePercent(frame / ENTRIES - 1, 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime));
|
||||||
|
|
||||||
|
constraint.rotateMix += (rotate + (frames[frame + ROTATE] - rotate) * percent - constraint.rotateMix) * alpha;
|
||||||
|
constraint.translateMix += (translate + (frames[frame + TRANSLATE] - translate) * percent - constraint.translateMix)
|
||||||
|
* alpha;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -41,6 +41,8 @@ namespace Spine {
|
|||||||
private float timeScale = 1;
|
private float timeScale = 1;
|
||||||
|
|
||||||
public AnimationStateData Data { get { return data; } }
|
public AnimationStateData Data { get { return data; } }
|
||||||
|
/// <summary>A list of tracks that have animations, which may contain nulls.</summary>
|
||||||
|
public ExposedList<TrackEntry> Tracks { get { return tracks; } }
|
||||||
public float TimeScale { get { return timeScale; } set { timeScale = value; } }
|
public float TimeScale { get { return timeScale; } set { timeScale = value; } }
|
||||||
|
|
||||||
public delegate void StartEndDelegate (AnimationState state, int trackIndex);
|
public delegate void StartEndDelegate (AnimationState state, int trackIndex);
|
||||||
@ -54,7 +56,7 @@ namespace Spine {
|
|||||||
public event CompleteDelegate Complete;
|
public event CompleteDelegate Complete;
|
||||||
|
|
||||||
public AnimationState (AnimationStateData data) {
|
public AnimationState (AnimationStateData data) {
|
||||||
if (data == null) throw new ArgumentNullException("data cannot be null.");
|
if (data == null) throw new ArgumentNullException("data", "data cannot be null.");
|
||||||
this.data = data;
|
this.data = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -187,15 +189,16 @@ namespace Spine {
|
|||||||
if (Start != null) Start(this, index);
|
if (Start != null) Start(this, index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <seealso cref="SetAnimation(int, Animation, bool)" />
|
||||||
public TrackEntry SetAnimation (int trackIndex, String animationName, bool loop) {
|
public TrackEntry SetAnimation (int trackIndex, String animationName, bool loop) {
|
||||||
Animation animation = data.skeletonData.FindAnimation(animationName);
|
Animation animation = data.skeletonData.FindAnimation(animationName);
|
||||||
if (animation == null) throw new ArgumentException("Animation not found: " + animationName);
|
if (animation == null) throw new ArgumentException("Animation not found: " + animationName, "animationName");
|
||||||
return SetAnimation(trackIndex, animation, loop);
|
return SetAnimation(trackIndex, animation, loop);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Set the current animation. Any queued animations are cleared.</summary>
|
/// <summary>Set the current animation. Any queued animations are cleared.</summary>
|
||||||
public TrackEntry SetAnimation (int trackIndex, Animation animation, bool loop) {
|
public TrackEntry SetAnimation (int trackIndex, Animation animation, bool loop) {
|
||||||
if (animation == null) throw new ArgumentException("animation cannot be null.");
|
if (animation == null) throw new ArgumentNullException("animation", "animation cannot be null.");
|
||||||
TrackEntry entry = new TrackEntry();
|
TrackEntry entry = new TrackEntry();
|
||||||
entry.animation = animation;
|
entry.animation = animation;
|
||||||
entry.loop = loop;
|
entry.loop = loop;
|
||||||
@ -205,16 +208,17 @@ namespace Spine {
|
|||||||
return entry;
|
return entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <seealso cref="AddAnimation(int, Animation, bool, float)" />
|
||||||
public TrackEntry AddAnimation (int trackIndex, String animationName, bool loop, float delay) {
|
public TrackEntry AddAnimation (int trackIndex, String animationName, bool loop, float delay) {
|
||||||
Animation animation = data.skeletonData.FindAnimation(animationName);
|
Animation animation = data.skeletonData.FindAnimation(animationName);
|
||||||
if (animation == null) throw new ArgumentException("Animation not found: " + animationName);
|
if (animation == null) throw new ArgumentException("Animation not found: " + animationName, "animationName");
|
||||||
return AddAnimation(trackIndex, animation, loop, delay);
|
return AddAnimation(trackIndex, animation, loop, delay);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Adds an animation to be played delay seconds after the current or last queued animation.</summary>
|
/// <summary>Adds an animation to be played delay seconds after the current or last queued animation.</summary>
|
||||||
/// <param name="delay">May be <= 0 to use duration of previous animation minus any mix duration plus the negative delay.</param>
|
/// <param name="delay">May be <= 0 to use duration of previous animation minus any mix duration plus the negative delay.</param>
|
||||||
public TrackEntry AddAnimation (int trackIndex, Animation animation, bool loop, float delay) {
|
public TrackEntry AddAnimation (int trackIndex, Animation animation, bool loop, float delay) {
|
||||||
if (animation == null) throw new ArgumentException("animation cannot be null.");
|
if (animation == null) throw new ArgumentNullException("animation", "animation cannot be null.");
|
||||||
TrackEntry entry = new TrackEntry();
|
TrackEntry entry = new TrackEntry();
|
||||||
entry.animation = animation;
|
entry.animation = animation;
|
||||||
entry.loop = loop;
|
entry.loop = loop;
|
||||||
|
|||||||
@ -42,6 +42,7 @@ namespace Spine {
|
|||||||
public float DefaultMix { get { return defaultMix; } set { defaultMix = value; } }
|
public float DefaultMix { get { return defaultMix; } set { defaultMix = value; } }
|
||||||
|
|
||||||
public AnimationStateData (SkeletonData skeletonData) {
|
public AnimationStateData (SkeletonData skeletonData) {
|
||||||
|
if (skeletonData == null) throw new ArgumentException ("skeletonData cannot be null.");
|
||||||
this.skeletonData = skeletonData;
|
this.skeletonData = skeletonData;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -54,8 +55,8 @@ namespace Spine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void SetMix (Animation from, Animation to, float duration) {
|
public void SetMix (Animation from, Animation to, float duration) {
|
||||||
if (from == null) throw new ArgumentNullException("from cannot be null.");
|
if (from == null) throw new ArgumentNullException("from", "from cannot be null.");
|
||||||
if (to == null) throw new ArgumentNullException("to cannot be null.");
|
if (to == null) throw new ArgumentNullException("to", "to cannot be null.");
|
||||||
AnimationPair key = new AnimationPair(from, to);
|
AnimationPair key = new AnimationPair(from, to);
|
||||||
animationToMixTime.Remove(key);
|
animationToMixTime.Remove(key);
|
||||||
animationToMixTime.Add(key, duration);
|
animationToMixTime.Add(key, duration);
|
||||||
|
|||||||
@ -74,29 +74,14 @@ namespace Spine {
|
|||||||
return attachment;
|
return attachment;
|
||||||
}
|
}
|
||||||
|
|
||||||
public WeightedMeshAttachment NewWeightedMeshAttachment (Skin skin, String name, String path) {
|
|
||||||
AtlasRegion region = FindRegion(path);
|
|
||||||
if (region == null) throw new Exception("Region not found in atlas: " + path + " (weighted mesh attachment: " + name + ")");
|
|
||||||
WeightedMeshAttachment attachment = new WeightedMeshAttachment(name);
|
|
||||||
attachment.RendererObject = region;
|
|
||||||
attachment.RegionU = region.u;
|
|
||||||
attachment.RegionV = region.v;
|
|
||||||
attachment.RegionU2 = region.u2;
|
|
||||||
attachment.RegionV2 = region.v2;
|
|
||||||
attachment.RegionRotate = region.rotate;
|
|
||||||
attachment.regionOffsetX = region.offsetX;
|
|
||||||
attachment.regionOffsetY = region.offsetY;
|
|
||||||
attachment.regionWidth = region.width;
|
|
||||||
attachment.regionHeight = region.height;
|
|
||||||
attachment.regionOriginalWidth = region.originalWidth;
|
|
||||||
attachment.regionOriginalHeight = region.originalHeight;
|
|
||||||
return attachment;
|
|
||||||
}
|
|
||||||
|
|
||||||
public BoundingBoxAttachment NewBoundingBoxAttachment (Skin skin, String name) {
|
public BoundingBoxAttachment NewBoundingBoxAttachment (Skin skin, String name) {
|
||||||
return new BoundingBoxAttachment(name);
|
return new BoundingBoxAttachment(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public PathAttachment NewPathAttachment (Skin skin, String name) {
|
||||||
|
return new PathAttachment (name);
|
||||||
|
}
|
||||||
|
|
||||||
public AtlasRegion FindRegion (string name) {
|
public AtlasRegion FindRegion (string name) {
|
||||||
AtlasRegion region;
|
AtlasRegion region;
|
||||||
|
|
||||||
|
|||||||
@ -36,7 +36,7 @@ namespace Spine {
|
|||||||
public String Name { get; private set; }
|
public String Name { get; private set; }
|
||||||
|
|
||||||
public Attachment (String name) {
|
public Attachment (String name) {
|
||||||
if (name == null) throw new ArgumentNullException("name cannot be null.");
|
if (name == null) throw new ArgumentNullException("name", "name cannot be null");
|
||||||
Name = name;
|
Name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -39,10 +39,10 @@ namespace Spine {
|
|||||||
/// <return>May be null to not load any attachment.</return>
|
/// <return>May be null to not load any attachment.</return>
|
||||||
MeshAttachment NewMeshAttachment (Skin skin, String name, String path);
|
MeshAttachment NewMeshAttachment (Skin skin, String name, String path);
|
||||||
|
|
||||||
/// <return>May be null to not load any attachment.</return>
|
|
||||||
WeightedMeshAttachment NewWeightedMeshAttachment (Skin skin, String name, String path);
|
|
||||||
|
|
||||||
/// <return>May be null to not load any attachment.</return>
|
/// <return>May be null to not load any attachment.</return>
|
||||||
BoundingBoxAttachment NewBoundingBoxAttachment (Skin skin, String name);
|
BoundingBoxAttachment NewBoundingBoxAttachment (Skin skin, String name);
|
||||||
|
|
||||||
|
/// <returns>May be null to not load any attachment</returns>
|
||||||
|
PathAttachment NewPathAttachment (Skin skin, String name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -31,6 +31,6 @@
|
|||||||
|
|
||||||
namespace Spine {
|
namespace Spine {
|
||||||
public enum AttachmentType {
|
public enum AttachmentType {
|
||||||
region, boundingbox, mesh, weightedmesh, linkedmesh, weightedlinkedmesh
|
Region, Boundingbox, Mesh, Linkedmesh, Path
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -33,29 +33,9 @@ using System;
|
|||||||
|
|
||||||
namespace Spine {
|
namespace Spine {
|
||||||
/// <summary>Attachment that has a polygon for bounds checking.</summary>
|
/// <summary>Attachment that has a polygon for bounds checking.</summary>
|
||||||
public class BoundingBoxAttachment : Attachment {
|
public class BoundingBoxAttachment : VertexAttachment {
|
||||||
internal float[] vertices;
|
|
||||||
|
|
||||||
public float[] Vertices { get { return vertices; } set { vertices = value; } }
|
|
||||||
|
|
||||||
public BoundingBoxAttachment (string name)
|
public BoundingBoxAttachment (string name)
|
||||||
: base(name) {
|
: base(name) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <param name="worldVertices">Must have at least the same length as this attachment's vertices.</param>
|
|
||||||
public void ComputeWorldVertices (Bone bone, float[] worldVertices) {
|
|
||||||
float x = bone.skeleton.x + bone.worldX, y = bone.skeleton.y + bone.worldY;
|
|
||||||
float m00 = bone.a;
|
|
||||||
float m01 = bone.b;
|
|
||||||
float m10 = bone.c;
|
|
||||||
float m11 = bone.d;
|
|
||||||
float[] vertices = this.vertices;
|
|
||||||
for (int i = 0, n = vertices.Length; i < n; i += 2) {
|
|
||||||
float px = vertices[i];
|
|
||||||
float py = vertices[i + 1];
|
|
||||||
worldVertices[i] = px * m00 + py * m01 + x;
|
|
||||||
worldVertices[i + 1] = px * m10 + py * m11 + y;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -33,16 +33,16 @@ using System;
|
|||||||
|
|
||||||
namespace Spine {
|
namespace Spine {
|
||||||
/// <summary>Attachment that displays a texture region using a mesh.</summary>
|
/// <summary>Attachment that displays a texture region using a mesh.</summary>
|
||||||
public class MeshAttachment : Attachment, IFfdAttachment {
|
public class MeshAttachment : VertexAttachment {
|
||||||
internal float[] vertices, uvs, regionUVs;
|
|
||||||
internal int[] triangles;
|
|
||||||
internal float regionOffsetX, regionOffsetY, regionWidth, regionHeight, regionOriginalWidth, regionOriginalHeight;
|
internal float regionOffsetX, regionOffsetY, regionWidth, regionHeight, regionOriginalWidth, regionOriginalHeight;
|
||||||
|
internal float[] uvs, regionUVs;
|
||||||
|
internal int[] triangles;
|
||||||
internal float r = 1, g = 1, b = 1, a = 1;
|
internal float r = 1, g = 1, b = 1, a = 1;
|
||||||
|
internal int hulllength;
|
||||||
internal MeshAttachment parentMesh;
|
internal MeshAttachment parentMesh;
|
||||||
internal bool inheritFFD;
|
internal bool inheritDeform;
|
||||||
|
|
||||||
public int HullLength { get; set; }
|
public int HullLength { get { return hulllength; } set { hulllength = value; } }
|
||||||
public float[] Vertices { get { return vertices; } set { vertices = value; } }
|
|
||||||
public float[] RegionUVs { get { return regionUVs; } set { regionUVs = value; } }
|
public float[] RegionUVs { get { return regionUVs; } set { regionUVs = value; } }
|
||||||
public float[] UVs { get { return uvs; } set { uvs = value; } }
|
public float[] UVs { get { return uvs; } set { uvs = value; } }
|
||||||
public int[] Triangles { get { return triangles; } set { triangles = value; } }
|
public int[] Triangles { get { return triangles; } set { triangles = value; } }
|
||||||
@ -66,7 +66,7 @@ namespace Spine {
|
|||||||
public float RegionOriginalWidth { get { return regionOriginalWidth; } set { regionOriginalWidth = value; } }
|
public float RegionOriginalWidth { get { return regionOriginalWidth; } set { regionOriginalWidth = value; } }
|
||||||
public float RegionOriginalHeight { get { return regionOriginalHeight; } set { regionOriginalHeight = value; } } // Unrotated, unstripped size.
|
public float RegionOriginalHeight { get { return regionOriginalHeight; } set { regionOriginalHeight = value; } } // Unrotated, unstripped size.
|
||||||
|
|
||||||
public bool InheritFFD { get { return inheritFFD; } set { inheritFFD = value; } }
|
public bool InheritDeform { get { return inheritDeform; } set { inheritDeform = value; } }
|
||||||
|
|
||||||
public MeshAttachment ParentMesh {
|
public MeshAttachment ParentMesh {
|
||||||
get { return parentMesh; }
|
get { return parentMesh; }
|
||||||
@ -74,6 +74,7 @@ namespace Spine {
|
|||||||
parentMesh = value;
|
parentMesh = value;
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
vertices = value.vertices;
|
vertices = value.vertices;
|
||||||
|
worldVerticesLength = value.worldVerticesLength;
|
||||||
regionUVs = value.regionUVs;
|
regionUVs = value.regionUVs;
|
||||||
triangles = value.triangles;
|
triangles = value.triangles;
|
||||||
HullLength = value.HullLength;
|
HullLength = value.HullLength;
|
||||||
@ -111,23 +112,8 @@ namespace Spine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ComputeWorldVertices (Slot slot, float[] worldVertices) {
|
override public bool ApplyDeform (VertexAttachment sourceAttachment) {
|
||||||
Bone bone = slot.bone;
|
return this == sourceAttachment || (inheritDeform && parentMesh == sourceAttachment);
|
||||||
float x = bone.skeleton.x + bone.worldX, y = bone.skeleton.y + bone.worldY;
|
|
||||||
float m00 = bone.a, m01 = bone.b, m10 = bone.c, m11 = bone.d;
|
|
||||||
float[] vertices = this.vertices;
|
|
||||||
int verticesCount = vertices.Length;
|
|
||||||
if (slot.attachmentVerticesCount == verticesCount) vertices = slot.AttachmentVertices;
|
|
||||||
for (int i = 0; i < verticesCount; i += 2) {
|
|
||||||
float vx = vertices[i];
|
|
||||||
float vy = vertices[i + 1];
|
|
||||||
worldVertices[i] = vx * m00 + vy * m01 + x;
|
|
||||||
worldVertices[i + 1] = vx * m10 + vy * m11 + y;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool ApplyFFD (Attachment sourceAttachment) {
|
|
||||||
return this == sourceAttachment || (inheritFFD && parentMesh == sourceAttachment);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -29,8 +29,21 @@
|
|||||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Spine {
|
namespace Spine {
|
||||||
public interface IFfdAttachment {
|
public class PathAttachment : VertexAttachment {
|
||||||
bool ApplyFFD (Attachment sourceAttachment);
|
internal float[] lengths;
|
||||||
|
internal bool closed, constantSpeed;
|
||||||
|
|
||||||
|
/// <summary>The length in the setup pose from the start of the path to the end of each curve.</summary>
|
||||||
|
public float[] Lengths { get { return lengths; } set { lengths = value; } }
|
||||||
|
public bool Closed { get { return closed; } set { closed = value; } }
|
||||||
|
public bool ConstantSpeed { get { return constantSpeed; } set { constantSpeed = value; } }
|
||||||
|
|
||||||
|
public PathAttachment (String name)
|
||||||
|
: base(name) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -111,9 +111,9 @@ namespace Spine {
|
|||||||
float localY = -height / 2 * scaleY + regionOffsetY * regionScaleY;
|
float localY = -height / 2 * scaleY + regionOffsetY * regionScaleY;
|
||||||
float localX2 = localX + regionWidth * regionScaleX;
|
float localX2 = localX + regionWidth * regionScaleX;
|
||||||
float localY2 = localY + regionHeight * regionScaleY;
|
float localY2 = localY + regionHeight * regionScaleY;
|
||||||
float radians = rotation * (float)Math.PI / 180;
|
float rotation = this.rotation;
|
||||||
float cos = (float)Math.Cos(radians);
|
float cos = MathUtils.CosDeg(rotation);
|
||||||
float sin = (float)Math.Sin(radians);
|
float sin = MathUtils.SinDeg(rotation);
|
||||||
float x = this.x;
|
float x = this.x;
|
||||||
float y = this.y;
|
float y = this.y;
|
||||||
float localXCos = localX * cos + x;
|
float localXCos = localX * cos + x;
|
||||||
@ -136,17 +136,18 @@ namespace Spine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void ComputeWorldVertices (Bone bone, float[] worldVertices) {
|
public void ComputeWorldVertices (Bone bone, float[] worldVertices) {
|
||||||
float x = bone.skeleton.x + bone.worldX, y = bone.skeleton.y + bone.worldY;
|
Skeleton skeleton = bone.skeleton;
|
||||||
float m00 = bone.a, m01 = bone.b, m10 = bone.c, m11 = bone.d;
|
float x = skeleton.x + bone.worldX, y = skeleton.y + bone.worldY;
|
||||||
|
float a = bone.a, b = bone.b, c = bone.c, d = bone.d;
|
||||||
float[] offset = this.offset;
|
float[] offset = this.offset;
|
||||||
worldVertices[X1] = offset[X1] * m00 + offset[Y1] * m01 + x;
|
worldVertices[X1] = offset[X1] * a + offset[Y1] * b + x;
|
||||||
worldVertices[Y1] = offset[X1] * m10 + offset[Y1] * m11 + y;
|
worldVertices[Y1] = offset[X1] * c + offset[Y1] * d + y;
|
||||||
worldVertices[X2] = offset[X2] * m00 + offset[Y2] * m01 + x;
|
worldVertices[X2] = offset[X2] * a + offset[Y2] * b + x;
|
||||||
worldVertices[Y2] = offset[X2] * m10 + offset[Y2] * m11 + y;
|
worldVertices[Y2] = offset[X2] * c + offset[Y2] * d + y;
|
||||||
worldVertices[X3] = offset[X3] * m00 + offset[Y3] * m01 + x;
|
worldVertices[X3] = offset[X3] * a + offset[Y3] * b + x;
|
||||||
worldVertices[Y3] = offset[X3] * m10 + offset[Y3] * m11 + y;
|
worldVertices[Y3] = offset[X3] * c + offset[Y3] * d + y;
|
||||||
worldVertices[X4] = offset[X4] * m00 + offset[Y4] * m01 + x;
|
worldVertices[X4] = offset[X4] * a + offset[Y4] * b + x;
|
||||||
worldVertices[Y4] = offset[X4] * m10 + offset[Y4] * m11 + y;
|
worldVertices[Y4] = offset[X4] * c + offset[Y4] * d + y;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
114
spine-csharp/src/Attachments/VertexAttachment.cs
Normal file
114
spine-csharp/src/Attachments/VertexAttachment.cs
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
/******************************************************************************
|
||||||
|
* Spine Runtimes Software License
|
||||||
|
* Version 2.3
|
||||||
|
*
|
||||||
|
* Copyright (c) 2013-2015, Esoteric Software
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* You are granted a perpetual, non-exclusive, non-sublicensable and
|
||||||
|
* non-transferable license to use, install, execute and perform the Spine
|
||||||
|
* Runtimes Software (the "Software") and derivative works solely for personal
|
||||||
|
* or internal use. Without the written permission of Esoteric Software (see
|
||||||
|
* Section 2 of the Spine Software License Agreement), you may not (a) modify,
|
||||||
|
* translate, adapt or otherwise create derivative works, improvements of the
|
||||||
|
* Software or develop new applications using the Software or (b) remove,
|
||||||
|
* delete, alter or obscure any trademarks or any copyright, trademark, patent
|
||||||
|
* or other intellectual property or proprietary rights notices on or in the
|
||||||
|
* Software, including any copy thereof. Redistributions in binary or source
|
||||||
|
* form must include this license and terms.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "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 BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||||
|
* OR BUSINESS INTERRUPTION) 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 THIS SOFTWARE, EVEN IF
|
||||||
|
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Spine {
|
||||||
|
/// <summary>>An attachment with vertices that are transformed by one or more bones and can be deformed by a slot's vertices.</summary>
|
||||||
|
public class VertexAttachment : Attachment {
|
||||||
|
internal int[] bones;
|
||||||
|
internal float[] vertices;
|
||||||
|
internal int worldVerticesLength;
|
||||||
|
|
||||||
|
public int[] Bones { get { return bones; } set { bones = value; } }
|
||||||
|
public float[] Vertices { get { return vertices; } set { vertices = value; } }
|
||||||
|
public int WorldVerticesLength { get { return worldVerticesLength; } set { worldVerticesLength = value; } }
|
||||||
|
|
||||||
|
public VertexAttachment (String name)
|
||||||
|
: base(name) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ComputeWorldVertices (Slot slot, float[] worldVertices) {
|
||||||
|
ComputeWorldVertices(slot, 0, worldVerticesLength, worldVertices, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ComputeWorldVertices (Slot slot, int start, int count, float[] worldVertices, int offset) {
|
||||||
|
count += offset;
|
||||||
|
Skeleton skeleton = slot.Skeleton;
|
||||||
|
float x = skeleton.x, y = skeleton.y;
|
||||||
|
var deformArray = slot.attachmentVertices;
|
||||||
|
float[] vertices = this.vertices;
|
||||||
|
int[] bones = this.bones;
|
||||||
|
if (bones == null) {
|
||||||
|
if (deformArray.Count > 0) vertices = deformArray.Items;
|
||||||
|
Bone bone = slot.bone;
|
||||||
|
x += bone.worldX;
|
||||||
|
y += bone.worldY;
|
||||||
|
float a = bone.a, b = bone.b, c = bone.c, d = bone.d;
|
||||||
|
for (int vv = start, w = offset; w < count; vv += 2, w += 2) {
|
||||||
|
float vx = vertices[vv], vy = vertices[vv + 1];
|
||||||
|
worldVertices[w] = vx * a + vy * b + x;
|
||||||
|
worldVertices[w + 1] = vx * c + vy * d + y;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int v = 0, skip = 0;
|
||||||
|
for (int i = 0; i < start; i += 2) {
|
||||||
|
int n = bones[v];
|
||||||
|
v += n + 1;
|
||||||
|
skip += n;
|
||||||
|
}
|
||||||
|
Bone[] skeletonBones = skeleton.Bones.Items;
|
||||||
|
if (deformArray.Count == 0) {
|
||||||
|
for (int w = offset, b = skip * 3; w < count; w += 2) {
|
||||||
|
float wx = x, wy = y;
|
||||||
|
for (int n = bones[v++] + v; v < n; v++, b += 3) {
|
||||||
|
Bone bone = skeletonBones[bones[v]];
|
||||||
|
float vx = vertices[b], vy = vertices[b + 1], weight = vertices[b + 2];
|
||||||
|
wx += (vx * bone.a + vy * bone.b + bone.worldX) * weight;
|
||||||
|
wy += (vx * bone.c + vy * bone.d + bone.worldY) * weight;
|
||||||
|
}
|
||||||
|
worldVertices[w] = wx;
|
||||||
|
worldVertices[w + 1] = wy;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
float[] deform = deformArray.Items;
|
||||||
|
for (int w = offset, b = skip * 3, f = skip << 1; w < count; w += 2) {
|
||||||
|
float wx = x, wy = y;
|
||||||
|
for (int n = bones[v++] + v; v < n; v++, b += 3, f += 2) {
|
||||||
|
Bone bone = skeletonBones[bones[v]];
|
||||||
|
float vx = vertices[b] + deform[f], vy = vertices[b + 1] + deform[f + 1], weight = vertices[b + 2];
|
||||||
|
wx += (vx * bone.a + vy * bone.b + bone.worldX) * weight;
|
||||||
|
wy += (vx * bone.c + vy * bone.d + bone.worldY) * weight;
|
||||||
|
}
|
||||||
|
worldVertices[w] = wx;
|
||||||
|
worldVertices[w + 1] = wy;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Returns true if a deform originally applied to the specified attachment should be applied to this attachment.</summary>
|
||||||
|
virtual public bool ApplyDeform (VertexAttachment sourceAttachment) {
|
||||||
|
return this == sourceAttachment;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,158 +0,0 @@
|
|||||||
/******************************************************************************
|
|
||||||
* Spine Runtimes Software License
|
|
||||||
* Version 2.3
|
|
||||||
*
|
|
||||||
* Copyright (c) 2013-2015, Esoteric Software
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* You are granted a perpetual, non-exclusive, non-sublicensable and
|
|
||||||
* non-transferable license to use, install, execute and perform the Spine
|
|
||||||
* Runtimes Software (the "Software") and derivative works solely for personal
|
|
||||||
* or internal use. Without the written permission of Esoteric Software (see
|
|
||||||
* Section 2 of the Spine Software License Agreement), you may not (a) modify,
|
|
||||||
* translate, adapt or otherwise create derivative works, improvements of the
|
|
||||||
* Software or develop new applications using the Software or (b) remove,
|
|
||||||
* delete, alter or obscure any trademarks or any copyright, trademark, patent
|
|
||||||
* or other intellectual property or proprietary rights notices on or in the
|
|
||||||
* Software, including any copy thereof. Redistributions in binary or source
|
|
||||||
* form must include this license and terms.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "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 BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
||||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
|
||||||
* OR BUSINESS INTERRUPTION) 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 THIS SOFTWARE, EVEN IF
|
|
||||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*****************************************************************************/
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace Spine {
|
|
||||||
/// <summary>Attachment that displays a texture region using a mesh which can be deformed by bones.</summary>
|
|
||||||
public class WeightedMeshAttachment : Attachment, IFfdAttachment {
|
|
||||||
internal int[] bones;
|
|
||||||
internal float[] weights, uvs, regionUVs;
|
|
||||||
internal int[] triangles;
|
|
||||||
internal float regionOffsetX, regionOffsetY, regionWidth, regionHeight, regionOriginalWidth, regionOriginalHeight;
|
|
||||||
internal float r = 1, g = 1, b = 1, a = 1;
|
|
||||||
internal WeightedMeshAttachment parentMesh;
|
|
||||||
internal bool inheritFFD;
|
|
||||||
|
|
||||||
public int HullLength { get; set; }
|
|
||||||
public int[] Bones { get { return bones; } set { bones = value; } }
|
|
||||||
public float[] Weights { get { return weights; } set { weights = value; } }
|
|
||||||
public float[] RegionUVs { get { return regionUVs; } set { regionUVs = value; } }
|
|
||||||
public float[] UVs { get { return uvs; } set { uvs = value; } }
|
|
||||||
public int[] Triangles { get { return triangles; } set { triangles = value; } }
|
|
||||||
|
|
||||||
public float R { get { return r; } set { r = value; } }
|
|
||||||
public float G { get { return g; } set { g = value; } }
|
|
||||||
public float B { get { return b; } set { b = value; } }
|
|
||||||
public float A { get { return a; } set { a = value; } }
|
|
||||||
|
|
||||||
public String Path { get; set; }
|
|
||||||
public Object RendererObject { get; set; }
|
|
||||||
public float RegionU { get; set; }
|
|
||||||
public float RegionV { get; set; }
|
|
||||||
public float RegionU2 { get; set; }
|
|
||||||
public float RegionV2 { get; set; }
|
|
||||||
public bool RegionRotate { get; set; }
|
|
||||||
public float RegionOffsetX { get { return regionOffsetX; } set { regionOffsetX = value; } }
|
|
||||||
public float RegionOffsetY { get { return regionOffsetY; } set { regionOffsetY = value; } } // Pixels stripped from the bottom left, unrotated.
|
|
||||||
public float RegionWidth { get { return regionWidth; } set { regionWidth = value; } }
|
|
||||||
public float RegionHeight { get { return regionHeight; } set { regionHeight = value; } } // Unrotated, stripped size.
|
|
||||||
public float RegionOriginalWidth { get { return regionOriginalWidth; } set { regionOriginalWidth = value; } }
|
|
||||||
public float RegionOriginalHeight { get { return regionOriginalHeight; } set { regionOriginalHeight = value; } } // Unrotated, unstripped size.
|
|
||||||
|
|
||||||
public bool InheritFFD { get { return inheritFFD; } set { inheritFFD = value; } }
|
|
||||||
|
|
||||||
public WeightedMeshAttachment ParentMesh {
|
|
||||||
get { return parentMesh; }
|
|
||||||
set {
|
|
||||||
parentMesh = value;
|
|
||||||
if (value != null) {
|
|
||||||
bones = value.bones;
|
|
||||||
weights = value.weights;
|
|
||||||
regionUVs = value.regionUVs;
|
|
||||||
triangles = value.triangles;
|
|
||||||
HullLength = value.HullLength;
|
|
||||||
Edges = value.Edges;
|
|
||||||
Width = value.Width;
|
|
||||||
Height = value.Height;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Nonessential.
|
|
||||||
public int[] Edges { get; set; }
|
|
||||||
public float Width { get; set; }
|
|
||||||
public float Height { get; set; }
|
|
||||||
|
|
||||||
public WeightedMeshAttachment (string name)
|
|
||||||
: base(name) {
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UpdateUVs () {
|
|
||||||
float u = RegionU, v = RegionV, width = RegionU2 - RegionU, height = RegionV2 - RegionV;
|
|
||||||
float[] regionUVs = this.regionUVs;
|
|
||||||
if (this.uvs == null || this.uvs.Length != regionUVs.Length) this.uvs = new float[regionUVs.Length];
|
|
||||||
float[] uvs = this.uvs;
|
|
||||||
if (RegionRotate) {
|
|
||||||
for (int i = 0, n = uvs.Length; i < n; i += 2) {
|
|
||||||
uvs[i] = u + regionUVs[i + 1] * width;
|
|
||||||
uvs[i + 1] = v + height - regionUVs[i] * height;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (int i = 0, n = uvs.Length; i < n; i += 2) {
|
|
||||||
uvs[i] = u + regionUVs[i] * width;
|
|
||||||
uvs[i + 1] = v + regionUVs[i + 1] * height;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ComputeWorldVertices (Slot slot, float[] worldVertices) {
|
|
||||||
Skeleton skeleton = slot.bone.skeleton;
|
|
||||||
ExposedList<Bone> skeletonBones = skeleton.bones;
|
|
||||||
float x = skeleton.x, y = skeleton.y;
|
|
||||||
float[] weights = this.weights;
|
|
||||||
int[] bones = this.bones;
|
|
||||||
if (slot.attachmentVerticesCount == 0) {
|
|
||||||
for (int w = 0, v = 0, b = 0, n = bones.Length; v < n; w += 2) {
|
|
||||||
float wx = 0, wy = 0;
|
|
||||||
int nn = bones[v++] + v;
|
|
||||||
for (; v < nn; v++, b += 3) {
|
|
||||||
Bone bone = skeletonBones.Items[bones[v]];
|
|
||||||
float vx = weights[b], vy = weights[b + 1], weight = weights[b + 2];
|
|
||||||
wx += (vx * bone.a + vy * bone.b + bone.worldX) * weight;
|
|
||||||
wy += (vx * bone.c + vy * bone.d + bone.worldY) * weight;
|
|
||||||
}
|
|
||||||
worldVertices[w] = wx + x;
|
|
||||||
worldVertices[w + 1] = wy + y;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
float[] ffd = slot.attachmentVertices;
|
|
||||||
for (int w = 0, v = 0, b = 0, f = 0, n = bones.Length; v < n; w += 2) {
|
|
||||||
float wx = 0, wy = 0;
|
|
||||||
int nn = bones[v++] + v;
|
|
||||||
for (; v < nn; v++, b += 3, f += 2) {
|
|
||||||
Bone bone = skeletonBones.Items[bones[v]];
|
|
||||||
float vx = weights[b] + ffd[f], vy = weights[b + 1] + ffd[f + 1], weight = weights[b + 2];
|
|
||||||
wx += (vx * bone.a + vy * bone.b + bone.worldX) * weight;
|
|
||||||
wy += (vx * bone.c + vy * bone.d + bone.worldY) * weight;
|
|
||||||
}
|
|
||||||
worldVertices[w] = wx + x;
|
|
||||||
worldVertices[w + 1] = wy + y;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool ApplyFFD (Attachment sourceAttachment) {
|
|
||||||
return this == sourceAttachment || (inheritFFD && parentMesh == sourceAttachment);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -30,7 +30,6 @@
|
|||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace Spine {
|
namespace Spine {
|
||||||
public class Bone : IUpdatable {
|
public class Bone : IUpdatable {
|
||||||
@ -41,12 +40,14 @@ namespace Spine {
|
|||||||
internal Bone parent;
|
internal Bone parent;
|
||||||
internal ExposedList<Bone> children = new ExposedList<Bone>();
|
internal ExposedList<Bone> children = new ExposedList<Bone>();
|
||||||
internal float x, y, rotation, scaleX, scaleY, shearX, shearY;
|
internal float x, y, rotation, scaleX, scaleY, shearX, shearY;
|
||||||
internal float appliedRotation, appliedScaleX, appliedScaleY;
|
internal float appliedRotation;
|
||||||
|
|
||||||
internal float a, b, worldX;
|
internal float a, b, worldX;
|
||||||
internal float c, d, worldY;
|
internal float c, d, worldY;
|
||||||
internal float worldSignX, worldSignY;
|
internal float worldSignX, worldSignY;
|
||||||
|
|
||||||
|
internal bool sorted;
|
||||||
|
|
||||||
public BoneData Data { get { return data; } }
|
public BoneData Data { get { return data; } }
|
||||||
public Skeleton Skeleton { get { return skeleton; } }
|
public Skeleton Skeleton { get { return skeleton; } }
|
||||||
public Bone Parent { get { return parent; } }
|
public Bone Parent { get { return parent; } }
|
||||||
@ -56,10 +57,6 @@ namespace Spine {
|
|||||||
public float Rotation { get { return rotation; } set { rotation = value; } }
|
public float Rotation { get { return rotation; } set { rotation = value; } }
|
||||||
/// <summary>The rotation, as calculated by any constraints.</summary>
|
/// <summary>The rotation, as calculated by any constraints.</summary>
|
||||||
public float AppliedRotation { get { return appliedRotation; } set { appliedRotation = value; } }
|
public float AppliedRotation { get { return appliedRotation; } set { appliedRotation = value; } }
|
||||||
/// <summary>The scale X, as calculated by any constraints.</summary>
|
|
||||||
public float AppliedScaleX { get { return appliedScaleX; } set { appliedScaleX = value; } }
|
|
||||||
/// <summary>The scale Y, as calculated by any constraints.</summary>
|
|
||||||
public float AppliedScaleY { get { return appliedScaleY; } set { appliedScaleY = value; } }
|
|
||||||
public float ScaleX { get { return scaleX; } set { scaleX = value; } }
|
public float ScaleX { get { return scaleX; } set { scaleX = value; } }
|
||||||
public float ScaleY { get { return scaleY; } set { scaleY = value; } }
|
public float ScaleY { get { return scaleY; } set { scaleY = value; } }
|
||||||
public float ShearX { get { return shearX; } set { shearX = value; } }
|
public float ShearX { get { return shearX; } set { shearX = value; } }
|
||||||
@ -80,29 +77,27 @@ namespace Spine {
|
|||||||
|
|
||||||
/// <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 cannot be null.");
|
if (data == null) throw new ArgumentNullException("data", "data cannot be null.");
|
||||||
if (skeleton == null) throw new ArgumentNullException("skeleton cannot be null.");
|
if (skeleton == null) throw new ArgumentNullException("skeleton", "skeleton cannot be null.");
|
||||||
this.data = data;
|
this.data = data;
|
||||||
this.skeleton = skeleton;
|
this.skeleton = skeleton;
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
SetToSetupPose();
|
SetToSetupPose();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Same as {@link #updateWorldTransform()}. This method exists for Bone to implement {@link Updatable}.</summary>
|
/// <summary>Same as <see cref="UpdateWorldTransform"/>. This method exists for Bone to implement <see cref="Spine.IUpdatable"/>.</summary>
|
||||||
public void Update () {
|
public void Update () {
|
||||||
UpdateWorldTransform(x, y, rotation, scaleX, scaleY, shearX, shearY);
|
UpdateWorldTransform(x, y, rotation, scaleX, scaleY, shearX, shearY);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Computes the world SRT using the parent bone and this bone's local SRT.</summary>
|
/// <summary>Computes the world transform using the parent bone and this bone's local transform.</summary>
|
||||||
public void UpdateWorldTransform () {
|
public void UpdateWorldTransform () {
|
||||||
UpdateWorldTransform(x, y, rotation, scaleX, scaleY, shearX, shearY);
|
UpdateWorldTransform(x, y, rotation, scaleX, scaleY, shearX, shearY);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Computes the world SRT using the parent bone and the specified local SRT.</summary>
|
/// <summary>Computes the world transform using the parent bone and the specified local transform.</summary>
|
||||||
public void UpdateWorldTransform (float x, float y, float rotation, float scaleX, float scaleY, float shearX, float shearY) {
|
public void UpdateWorldTransform (float x, float y, float rotation, float scaleX, float scaleY, float shearX, float shearY) {
|
||||||
appliedRotation = rotation;
|
appliedRotation = rotation;
|
||||||
appliedScaleX = scaleX;
|
|
||||||
appliedScaleY = scaleY;
|
|
||||||
|
|
||||||
float rotationY = rotation + 90 + shearY;
|
float rotationY = rotation + 90 + shearY;
|
||||||
float la = MathUtils.CosDeg(rotation + shearX) * scaleX, lb = MathUtils.CosDeg(rotationY) * scaleY;
|
float la = MathUtils.CosDeg(rotation + shearX) * scaleX, lb = MathUtils.CosDeg(rotationY) * scaleY;
|
||||||
@ -152,10 +147,10 @@ namespace Spine {
|
|||||||
do {
|
do {
|
||||||
float cos = MathUtils.CosDeg(parent.appliedRotation), sin = MathUtils.SinDeg(parent.appliedRotation);
|
float cos = MathUtils.CosDeg(parent.appliedRotation), sin = MathUtils.SinDeg(parent.appliedRotation);
|
||||||
float temp = pa * cos + pb * sin;
|
float temp = pa * cos + pb * sin;
|
||||||
pb = pa * -sin + pb * cos;
|
pb = pb * cos - pa * sin;
|
||||||
pa = temp;
|
pa = temp;
|
||||||
temp = pc * cos + pd * sin;
|
temp = pc * cos + pd * sin;
|
||||||
pd = pc * -sin + pd * cos;
|
pd = pd * cos - pc * sin;
|
||||||
pc = temp;
|
pc = temp;
|
||||||
|
|
||||||
if (!parent.data.inheritRotation) break;
|
if (!parent.data.inheritRotation) break;
|
||||||
@ -171,24 +166,22 @@ namespace Spine {
|
|||||||
pc = 0;
|
pc = 0;
|
||||||
pd = 1;
|
pd = 1;
|
||||||
do {
|
do {
|
||||||
float r = parent.appliedRotation, cos = MathUtils.CosDeg(r), sin = MathUtils.SinDeg(r);
|
float cos = MathUtils.CosDeg(parent.appliedRotation), sin = MathUtils.SinDeg(parent.appliedRotation);
|
||||||
float psx = parent.appliedScaleX, psy = parent.appliedScaleY;
|
float psx = parent.scaleX, psy = parent.scaleY;
|
||||||
float za = cos * psx, zb = -sin * psy, zc = sin * psx, zd = cos * psy;
|
float za = cos * psx, zb = sin * psy, zc = sin * psx, zd = cos * psy;
|
||||||
float temp = pa * za + pb * zc;
|
float temp = pa * za + pb * zc;
|
||||||
pb = pa * zb + pb * zd;
|
pb = pb * zd - pa * zb;
|
||||||
pa = temp;
|
pa = temp;
|
||||||
temp = pc * za + pd * zc;
|
temp = pc * za + pd * zc;
|
||||||
pd = pc * zb + pd * zd;
|
pd = pd * zd - pc * zb;
|
||||||
pc = temp;
|
pc = temp;
|
||||||
|
|
||||||
if (psx < 0) r = -r;
|
if (psx >= 0) sin = -sin;
|
||||||
cos = MathUtils.CosDeg(-r);
|
|
||||||
sin = MathUtils.SinDeg(-r);
|
|
||||||
temp = pa * cos + pb * sin;
|
temp = pa * cos + pb * sin;
|
||||||
pb = pa * -sin + pb * cos;
|
pb = pb * cos - pa * sin;
|
||||||
pa = temp;
|
pa = temp;
|
||||||
temp = pc * cos + pd * sin;
|
temp = pc * cos + pd * sin;
|
||||||
pd = pc * -sin + pd * cos;
|
pd = pd * cos - pc * sin;
|
||||||
pc = temp;
|
pc = temp;
|
||||||
|
|
||||||
if (!parent.data.inheritScale) break;
|
if (!parent.data.inheritScale) break;
|
||||||
@ -226,10 +219,86 @@ namespace Spine {
|
|||||||
shearY = data.shearY;
|
shearY = data.shearY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public float WorldToLocalRotationX {
|
||||||
|
get {
|
||||||
|
Bone parent = this.parent;
|
||||||
|
if (parent == null) return rotation;
|
||||||
|
float pa = parent.a, pb = parent.b, pc = parent.c, pd = parent.d, a = this.a, c = this.c;
|
||||||
|
return MathUtils.Atan2(pa * c - pc * a, pd * a - pb * c) * MathUtils.radDeg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public float WorldToLocalRotationY {
|
||||||
|
get {
|
||||||
|
Bone parent = this.parent;
|
||||||
|
if (parent == null) return rotation;
|
||||||
|
float pa = parent.a, pb = parent.b, pc = parent.c, pd = parent.d, b = this.b, d = this.d;
|
||||||
|
return MathUtils.Atan2(pa * d - pc * b, pd * b - pb * d) * MathUtils.radDeg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RotateWorld (float degrees) {
|
||||||
|
float a = this.a, b = this.b, c = this.c, d = this.d;
|
||||||
|
float cos = MathUtils.CosDeg(degrees), sin = MathUtils.SinDeg(degrees);
|
||||||
|
this.a = cos * a - sin * c;
|
||||||
|
this.b = cos * b - sin * d;
|
||||||
|
this.c = sin * a + cos * c;
|
||||||
|
this.d = sin * b + cos * d;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Computes the local transform from the world transform. This can be useful to perform processing on the local transform
|
||||||
|
/// after the world transform has been modified directly (eg, by a constraint).
|
||||||
|
///
|
||||||
|
/// Some redundant information is lost by the world transform, such as -1,-1 scale versus 180 rotation. The computed local
|
||||||
|
/// transform values may differ from the original values but are functionally the same.
|
||||||
|
/// </summary>
|
||||||
|
public void UpdateLocalTransform () {
|
||||||
|
Bone parent = this.parent;
|
||||||
|
if (parent == null) {
|
||||||
|
x = worldX;
|
||||||
|
y = worldY;
|
||||||
|
rotation = MathUtils.Atan2(c, a) * MathUtils.radDeg;
|
||||||
|
scaleX = (float)Math.Sqrt(a * a + c * c);
|
||||||
|
scaleY = (float)Math.Sqrt(b * b + d * d);
|
||||||
|
float det = a * d - b * c;
|
||||||
|
shearX = 0;
|
||||||
|
shearY = MathUtils.Atan2(a * b + c * d, det) * MathUtils.radDeg;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
float pa = parent.a, pb = parent.b, pc = parent.c, pd = parent.d;
|
||||||
|
float pid = 1 / (pa * pd - pb * pc);
|
||||||
|
float dx = worldX - parent.worldX, dy = worldY - parent.worldY;
|
||||||
|
x = (dx * pd * pid - dy * pb * pid);
|
||||||
|
y = (dy * pa * pid - dx * pc * pid);
|
||||||
|
float ia = pid * pd;
|
||||||
|
float id = pid * pa;
|
||||||
|
float ib = pid * pb;
|
||||||
|
float ic = pid * pc;
|
||||||
|
float ra = ia * a - ib * c;
|
||||||
|
float rb = ia * b - ib * d;
|
||||||
|
float rc = id * c - ic * a;
|
||||||
|
float rd = id * d - ic * b;
|
||||||
|
shearX = 0;
|
||||||
|
scaleX = (float)Math.Sqrt(ra * ra + rc * rc);
|
||||||
|
if (scaleX > 0.0001f) {
|
||||||
|
float det = ra * rd - rb * rc;
|
||||||
|
scaleY = det / scaleX;
|
||||||
|
shearY = MathUtils.Atan2(ra * rb + rc * rd, det) * MathUtils.radDeg;
|
||||||
|
rotation = MathUtils.Atan2(rc, ra) * MathUtils.radDeg;
|
||||||
|
} else {
|
||||||
|
scaleX = 0;
|
||||||
|
scaleY = (float)Math.Sqrt(rb * rb + rd * rd);
|
||||||
|
shearY = 0;
|
||||||
|
rotation = 90 - MathUtils.Atan2(rd, rb) * MathUtils.radDeg;
|
||||||
|
}
|
||||||
|
appliedRotation = rotation;
|
||||||
|
}
|
||||||
|
|
||||||
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 x = worldX - this.worldX, y = worldY - this.worldY;
|
|
||||||
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 invDet = 1 / (a * d - b * c);
|
||||||
|
float x = worldX - this.worldX, y = worldY - this.worldY;
|
||||||
localX = (x * d * invDet - y * b * invDet);
|
localX = (x * d * invDet - y * b * invDet);
|
||||||
localY = (y * a * invDet - x * c * invDet);
|
localY = (y * a * invDet - x * c * invDet);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -33,14 +33,17 @@ using System;
|
|||||||
|
|
||||||
namespace Spine {
|
namespace Spine {
|
||||||
public class BoneData {
|
public class BoneData {
|
||||||
internal BoneData parent;
|
internal int index;
|
||||||
internal String name;
|
internal String name;
|
||||||
internal float length, x, y, rotation, scaleX = 1, scaleY = 1, shearX, shearY;
|
internal BoneData parent;
|
||||||
internal bool inheritScale = true, inheritRotation = true;
|
internal float length;
|
||||||
|
internal float x, y, rotation, scaleX = 1, scaleY = 1, shearX, shearY;
|
||||||
|
internal bool inheritRotation = true, inheritScale = true;
|
||||||
|
|
||||||
/// <summary>May be null.</summary>
|
/// <summary>May be null.</summary>
|
||||||
public BoneData Parent { get { return parent; } }
|
public int Index { get { return index; } }
|
||||||
public String Name { get { return name; } }
|
public String Name { get { return name; } }
|
||||||
|
public BoneData Parent { get { return parent; } }
|
||||||
public float Length { get { return length; } set { length = value; } }
|
public float Length { get { return length; } set { length = value; } }
|
||||||
public float X { get { return x; } set { x = value; } }
|
public float X { get { return x; } set { x = value; } }
|
||||||
public float Y { get { return y; } set { y = value; } }
|
public float Y { get { return y; } set { y = value; } }
|
||||||
@ -49,12 +52,14 @@ namespace Spine {
|
|||||||
public float ScaleY { get { return scaleY; } set { scaleY = value; } }
|
public float ScaleY { get { return scaleY; } set { scaleY = value; } }
|
||||||
public float ShearX { get { return shearX; } set { shearX = value; } }
|
public float ShearX { get { return shearX; } set { shearX = value; } }
|
||||||
public float ShearY { get { return shearY; } set { shearY = value; } }
|
public float ShearY { get { return shearY; } set { shearY = value; } }
|
||||||
public bool InheritScale { get { return inheritScale; } set { inheritScale = value; } }
|
|
||||||
public bool InheritRotation { get { return inheritRotation; } set { inheritRotation = value; } }
|
public bool InheritRotation { get { return inheritRotation; } set { inheritRotation = value; } }
|
||||||
|
public bool InheritScale { get { return inheritScale; } set { inheritScale = value; } }
|
||||||
|
|
||||||
/// <param name="parent">May be null.</param>
|
/// <param name="parent">May be null.</param>
|
||||||
public BoneData (String name, BoneData parent) {
|
public BoneData (int index, String name, BoneData parent) {
|
||||||
if (name == null) throw new ArgumentNullException("name cannot be null.");
|
if (index < 0) throw new ArgumentException("index must be >= 0", "index");
|
||||||
|
if (name == null) throw new ArgumentNullException("name", "name cannot be null.");
|
||||||
|
this.index = index;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -40,6 +40,7 @@ namespace Spine {
|
|||||||
public float Time { get; private set; }
|
public float Time { get; private set; }
|
||||||
|
|
||||||
public Event (float time, EventData data) {
|
public Event (float time, EventData data) {
|
||||||
|
if (data == null) throw new ArgumentNullException("data", "data cannot be null.");
|
||||||
Time = time;
|
Time = time;
|
||||||
Data = data;
|
Data = data;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -41,7 +41,7 @@ namespace Spine {
|
|||||||
public String String { get; set; }
|
public String String { get; set; }
|
||||||
|
|
||||||
public EventData (String name) {
|
public EventData (String name) {
|
||||||
if (name == null) throw new ArgumentNullException("name cannot be null.");
|
if (name == null) throw new ArgumentNullException("name", "name cannot be null.");
|
||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -89,6 +89,12 @@ namespace Spine {
|
|||||||
Capacity = Math.Max(Math.Max(Capacity * 2, DefaultCapacity), minimumSize);
|
Capacity = Math.Max(Math.Max(Capacity * 2, DefaultCapacity), minimumSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ExposedList<T> Resize (int newSize) {
|
||||||
|
if (newSize > Items.Length) Array.Resize(ref Items, newSize);
|
||||||
|
Count = newSize;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
private void CheckRange (int idx, int count) {
|
private void CheckRange (int idx, int count) {
|
||||||
if (idx < 0)
|
if (idx < 0)
|
||||||
throw new ArgumentOutOfRangeException("index");
|
throw new ArgumentOutOfRangeException("index");
|
||||||
|
|||||||
@ -30,15 +30,16 @@
|
|||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace Spine {
|
namespace Spine {
|
||||||
public class IkConstraint : IUpdatable {
|
public class IkConstraint : IUpdatable {
|
||||||
internal IkConstraintData data;
|
internal IkConstraintData data;
|
||||||
internal ExposedList<Bone> bones = new ExposedList<Bone>();
|
internal ExposedList<Bone> bones = new ExposedList<Bone>();
|
||||||
internal Bone target;
|
internal Bone target;
|
||||||
internal int bendDirection;
|
|
||||||
internal float mix;
|
internal float mix;
|
||||||
|
internal int bendDirection;
|
||||||
|
|
||||||
|
internal int level;
|
||||||
|
|
||||||
public IkConstraintData Data { get { return data; } }
|
public IkConstraintData Data { get { return data; } }
|
||||||
public ExposedList<Bone> Bones { get { return bones; } }
|
public ExposedList<Bone> Bones { get { return bones; } }
|
||||||
@ -47,8 +48,8 @@ namespace Spine {
|
|||||||
public float Mix { get { return mix; } set { mix = value; } }
|
public float Mix { get { return mix; } set { mix = value; } }
|
||||||
|
|
||||||
public IkConstraint (IkConstraintData data, Skeleton skeleton) {
|
public IkConstraint (IkConstraintData data, Skeleton skeleton) {
|
||||||
if (data == null) throw new ArgumentNullException("data cannot be null.");
|
if (data == null) throw new ArgumentNullException("data", "data cannot be null.");
|
||||||
if (skeleton == null) throw new ArgumentNullException("skeleton cannot be null.");
|
if (skeleton == null) throw new ArgumentNullException("skeleton", "skeleton cannot be null.");
|
||||||
this.data = data;
|
this.data = data;
|
||||||
mix = data.mix;
|
mix = data.mix;
|
||||||
bendDirection = data.bendDirection;
|
bendDirection = data.bendDirection;
|
||||||
@ -87,21 +88,24 @@ namespace Spine {
|
|||||||
float id = 1 / (pp.a * pp.d - pp.b * pp.c);
|
float id = 1 / (pp.a * pp.d - pp.b * pp.c);
|
||||||
float x = targetX - pp.worldX, y = targetY - pp.worldY;
|
float x = targetX - pp.worldX, y = targetY - pp.worldY;
|
||||||
float tx = (x * pp.d - y * pp.b) * id - bone.x, ty = (y * pp.a - x * pp.c) * id - bone.y;
|
float tx = (x * pp.d - y * pp.b) * id - bone.x, ty = (y * pp.a - x * pp.c) * id - bone.y;
|
||||||
float rotationIK = MathUtils.Atan2(ty, tx) * MathUtils.radDeg - bone.shearX;
|
float rotationIK = MathUtils.Atan2(ty, tx) * MathUtils.radDeg - bone.shearX - bone.rotation;
|
||||||
if (bone.scaleX < 0) rotationIK += 180;
|
if (bone.scaleX < 0) rotationIK += 180;
|
||||||
if (rotationIK > 180)
|
if (rotationIK > 180)
|
||||||
rotationIK -= 360;
|
rotationIK -= 360;
|
||||||
else if (rotationIK < -180) rotationIK += 360;
|
else if (rotationIK < -180) rotationIK += 360;
|
||||||
bone.UpdateWorldTransform(bone.x, bone.y, bone.rotation + (rotationIK - bone.rotation) * alpha, bone.appliedScaleX,
|
bone.UpdateWorldTransform(bone.x, bone.y, bone.rotation + rotationIK * alpha, bone.scaleX, bone.scaleY,
|
||||||
bone.appliedScaleY, bone.shearX, bone.shearY);
|
bone.shearX, bone.shearY);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Adjusts the parent and child bone rotations so the tip of the child is as close to the target position as
|
/// <summary>Adjusts the parent and child bone rotations so the tip of the child is as close to the target position as
|
||||||
/// possible. The target is specified in the world coordinate system.</summary>
|
/// possible. The target is specified in the world coordinate system.</summary>
|
||||||
/// <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, float alpha) {
|
static public void Apply (Bone parent, Bone child, float targetX, float targetY, int bendDir, float alpha) {
|
||||||
if (alpha == 0) return;
|
if (alpha == 0) {
|
||||||
float px = parent.x, py = parent.y, psx = parent.appliedScaleX, psy = parent.appliedScaleY;
|
child.UpdateWorldTransform ();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
float px = parent.x, py = parent.y, psx = parent.scaleX, psy = parent.scaleY, csx = child.scaleX;
|
||||||
int os1, os2, s2;
|
int os1, os2, s2;
|
||||||
if (psx < 0) {
|
if (psx < 0) {
|
||||||
psx = -psx;
|
psx = -psx;
|
||||||
@ -115,44 +119,55 @@ namespace Spine {
|
|||||||
psy = -psy;
|
psy = -psy;
|
||||||
s2 = -s2;
|
s2 = -s2;
|
||||||
}
|
}
|
||||||
float cx = child.x, cy = child.y, csx = child.appliedScaleX;
|
|
||||||
bool u = Math.Abs(psx - psy) <= 0.0001f;
|
|
||||||
if (!u && cy != 0) {
|
|
||||||
child.worldX = parent.a * cx + parent.worldX;
|
|
||||||
child.worldY = parent.c * cx + parent.worldY;
|
|
||||||
cy = 0;
|
|
||||||
}
|
|
||||||
if (csx < 0) {
|
if (csx < 0) {
|
||||||
csx = -csx;
|
csx = -csx;
|
||||||
os2 = 180;
|
os2 = 180;
|
||||||
} else
|
} else
|
||||||
os2 = 0;
|
os2 = 0;
|
||||||
|
float cx = child.x, cy, cwx, cwy, a = parent.a, b = parent.b, c = parent.c, d = parent.d;
|
||||||
|
bool u = Math.Abs(psx - psy) <= 0.0001f;
|
||||||
|
if (!u) {
|
||||||
|
cy = 0;
|
||||||
|
cwx = a * cx + parent.worldX;
|
||||||
|
cwy = c * cx + parent.worldY;
|
||||||
|
} else {
|
||||||
|
cy = child.y;
|
||||||
|
cwx = a * cx + b * cy + parent.worldX;
|
||||||
|
cwy = c * cx + d * cy + parent.worldY;
|
||||||
|
}
|
||||||
Bone pp = parent.parent;
|
Bone pp = parent.parent;
|
||||||
float ppa = pp.a, ppb = pp.b, ppc = pp.c, ppd = pp.d, id = 1 / (ppa * ppd - ppb * ppc);
|
a = pp.a;
|
||||||
float x = targetX - pp.worldX, y = targetY - pp.worldY;
|
b = pp.b;
|
||||||
float tx = (x * ppd - y * ppb) * id - px, ty = (y * ppa - x * ppc) * id - py;
|
c = pp.c;
|
||||||
x = child.worldX - pp.worldX;
|
d = pp.d;
|
||||||
y = child.worldY - pp.worldY;
|
float id = 1 / (a * d - b * c), x = targetX - pp.worldX, y = targetY - pp.worldY;
|
||||||
float dx = (x * ppd - y * ppb) * id - px, dy = (y * ppa - x * ppc) * id - py;
|
float tx = (x * d - y * b) * id - px, ty = (y * a - x * c) * id - py;
|
||||||
|
x = cwx - pp.worldX;
|
||||||
|
y = cwy - pp.worldY;
|
||||||
|
float dx = (x * d - y * b) * id - px, dy = (y * a - x * c) * id - py;
|
||||||
float l1 = (float)Math.Sqrt(dx * dx + dy * dy), l2 = child.data.length * csx, a1, a2;
|
float l1 = (float)Math.Sqrt(dx * dx + dy * dy), l2 = child.data.length * csx, a1, a2;
|
||||||
if (u) {
|
if (u) {
|
||||||
l2 *= psx;
|
l2 *= psx;
|
||||||
float cos = (tx * tx + ty * ty - l1 * l1 - l2 * l2) / (2 * l1 * l2);
|
float cos = (tx * tx + ty * ty - l1 * l1 - l2 * l2) / (2 * l1 * l2);
|
||||||
if (cos < -1) cos = -1;
|
if (cos < -1)
|
||||||
|
cos = -1;
|
||||||
else if (cos > 1) cos = 1;
|
else if (cos > 1) cos = 1;
|
||||||
a2 = (float)Math.Acos(cos) * bendDir;
|
a2 = (float)Math.Acos(cos) * bendDir;
|
||||||
float a = l1 + l2 * cos, o = l2 * MathUtils.Sin(a2);
|
a = l1 + l2 * cos;
|
||||||
a1 = MathUtils.Atan2(ty * a - tx * o, tx * a + ty * o);
|
b = l2 * MathUtils.Sin(a2);
|
||||||
|
a1 = MathUtils.Atan2(ty * a - tx * b, tx * a + ty * b);
|
||||||
} else {
|
} else {
|
||||||
float a = psx * l2, b = psy * l2, ta = MathUtils.Atan2(ty, tx);
|
a = psx * l2;
|
||||||
float aa = a * a, bb = b * b, ll = l1 * l1, dd = tx * tx + ty * ty;
|
b = psy * l2;
|
||||||
float c0 = bb * ll + aa * dd - aa * bb, c1 = -2 * bb * l1, c2 = bb - aa;
|
float aa = a * a, bb = b * b, dd = tx * tx + ty * ty, ta = MathUtils.Atan2(ty, tx);
|
||||||
float d = c1 * c1 - 4 * c2 * c0;
|
c = bb * l1 * l1 + aa * dd - aa * bb;
|
||||||
|
float c1 = -2 * bb * l1, c2 = bb - aa;
|
||||||
|
d = c1 * c1 - 4 * c2 * c;
|
||||||
if (d >= 0) {
|
if (d >= 0) {
|
||||||
float q = (float)Math.Sqrt(d);
|
float q = (float)Math.Sqrt(d);
|
||||||
if (c1 < 0) q = -q;
|
if (c1 < 0) q = -q;
|
||||||
q = -(c1 + q) / 2;
|
q = -(c1 + q) / 2;
|
||||||
float r0 = q / c2, r1 = c0 / q;
|
float r0 = q / c2, r1 = c / q;
|
||||||
float r = Math.Abs(r0) < Math.Abs(r1) ? r0 : r1;
|
float r = Math.Abs(r0) < Math.Abs(r1) ? r0 : r1;
|
||||||
if (r * r <= dd) {
|
if (r * r <= dd) {
|
||||||
y = (float)Math.Sqrt(dd - r * r) * bendDir;
|
y = (float)Math.Sqrt(dd - r * r) * bendDir;
|
||||||
@ -203,16 +218,18 @@ namespace Spine {
|
|||||||
}
|
}
|
||||||
outer:
|
outer:
|
||||||
float os = MathUtils.Atan2(cy, cx) * s2;
|
float os = MathUtils.Atan2(cy, cx) * s2;
|
||||||
a1 = (a1 - os) * MathUtils.radDeg + os1;
|
|
||||||
a2 = ((a2 + os) * MathUtils.radDeg - child.shearX) * s2 + os2;
|
|
||||||
if (a1 > 180) a1 -= 360;
|
|
||||||
else if (a1 < -180) a1 += 360;
|
|
||||||
if (a2 > 180) a2 -= 360;
|
|
||||||
else if (a2 < -180) a2 += 360;
|
|
||||||
float rotation = parent.rotation;
|
float rotation = parent.rotation;
|
||||||
parent.UpdateWorldTransform(px, py, rotation + (a1 - rotation) * alpha, parent.appliedScaleX, parent.appliedScaleY, 0, 0);
|
a1 = (a1 - os) * MathUtils.radDeg + os1 - rotation;
|
||||||
|
if (a1 > 180)
|
||||||
|
a1 -= 360;
|
||||||
|
else if (a1 < -180) a1 += 360;
|
||||||
|
parent.UpdateWorldTransform(px, py, rotation + a1 * alpha, parent.scaleX, parent.scaleY, 0, 0);
|
||||||
rotation = child.rotation;
|
rotation = child.rotation;
|
||||||
child.UpdateWorldTransform(cx, cy, rotation + (a2 - rotation) * alpha, child.appliedScaleX, child.appliedScaleY, child.shearX, child.shearY);
|
a2 = ((a2 + os) * MathUtils.radDeg - child.shearX) * s2 + os2 - rotation;
|
||||||
|
if (a2 > 180)
|
||||||
|
a2 -= 360;
|
||||||
|
else if (a2 < -180) a2 += 360;
|
||||||
|
child.UpdateWorldTransform(cx, cy, rotation + a2 * alpha, child.scaleX, child.scaleY, child.shearX, child.shearY);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -47,7 +47,7 @@ namespace Spine {
|
|||||||
public float Mix { get { return mix; } set { mix = value; } }
|
public float Mix { get { return mix; } set { mix = value; } }
|
||||||
|
|
||||||
public IkConstraintData (String name) {
|
public IkConstraintData (String name) {
|
||||||
if (name == null) throw new ArgumentNullException("name cannot be null.");
|
if (name == null) throw new ArgumentNullException("name", "name cannot be null.");
|
||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -54,22 +54,22 @@ namespace Spine {
|
|||||||
sin[(int)(i * degToIndex) & SIN_MASK] = (float)Math.Sin(i * degRad);
|
sin[(int)(i * degToIndex) & SIN_MASK] = (float)Math.Sin(i * degRad);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns the sine in radians from a lookup table. */
|
/// <summary>Returns the sine in radians from a lookup table.</summary>
|
||||||
static public float Sin (float radians) {
|
static public float Sin (float radians) {
|
||||||
return sin[(int)(radians * radToIndex) & SIN_MASK];
|
return sin[(int)(radians * radToIndex) & SIN_MASK];
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns the cosine in radians from a lookup table. */
|
/// <summary>Returns the cosine in radians from a lookup table.</summary>
|
||||||
static public float Cos (float radians) {
|
static public float Cos (float radians) {
|
||||||
return sin[(int)((radians + PI / 2) * radToIndex) & SIN_MASK];
|
return sin[(int)((radians + PI / 2) * radToIndex) & SIN_MASK];
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns the sine in radians from a lookup table. */
|
/// <summary>Returns the sine in radians from a lookup table.</summary>
|
||||||
static public float SinDeg (float degrees) {
|
static public float SinDeg (float degrees) {
|
||||||
return sin[(int)(degrees * degToIndex) & SIN_MASK];
|
return sin[(int)(degrees * degToIndex) & SIN_MASK];
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns the cosine in radians from a lookup table. */
|
/// <summary>Returns the cosine in radians from a lookup table.</summary>
|
||||||
static public float CosDeg (float degrees) {
|
static public float CosDeg (float degrees) {
|
||||||
return sin[(int)((degrees + 90) * degToIndex) & SIN_MASK];
|
return sin[(int)((degrees + 90) * degToIndex) & SIN_MASK];
|
||||||
}
|
}
|
||||||
@ -91,5 +91,11 @@ namespace Spine {
|
|||||||
atan = PI / 2 - z / (z * z + 0.28f);
|
atan = PI / 2 - z / (z * z + 0.28f);
|
||||||
return y < 0f ? atan - PI : atan;
|
return y < 0f ? atan - PI : atan;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static public float Clamp (float value, float min, float max) {
|
||||||
|
if (value < min) return min;
|
||||||
|
if (value > max) return max;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
400
spine-csharp/src/PathConstraint.cs
Normal file
400
spine-csharp/src/PathConstraint.cs
Normal file
@ -0,0 +1,400 @@
|
|||||||
|
/******************************************************************************
|
||||||
|
* Spine Runtimes Software License
|
||||||
|
* Version 2.3
|
||||||
|
*
|
||||||
|
* Copyright (c) 2013-2015, Esoteric Software
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* You are granted a perpetual, non-exclusive, non-sublicensable and
|
||||||
|
* non-transferable license to use, install, execute and perform the Spine
|
||||||
|
* Runtimes Software (the "Software") and derivative works solely for personal
|
||||||
|
* or internal use. Without the written permission of Esoteric Software (see
|
||||||
|
* Section 2 of the Spine Software License Agreement), you may not (a) modify,
|
||||||
|
* translate, adapt or otherwise create derivative works, improvements of the
|
||||||
|
* Software or develop new applications using the Software or (b) remove,
|
||||||
|
* delete, alter or obscure any trademarks or any copyright, trademark, patent
|
||||||
|
* or other intellectual property or proprietary rights notices on or in the
|
||||||
|
* Software, including any copy thereof. Redistributions in binary or source
|
||||||
|
* form must include this license and terms.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "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 BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||||
|
* OR BUSINESS INTERRUPTION) 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 THIS SOFTWARE, EVEN IF
|
||||||
|
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Spine {
|
||||||
|
public class PathConstraint : IUpdatable {
|
||||||
|
private const int NONE = -1, BEFORE = -2, AFTER = -3;
|
||||||
|
|
||||||
|
internal PathConstraintData data;
|
||||||
|
internal ExposedList<Bone> bones;
|
||||||
|
internal Slot target;
|
||||||
|
internal float position, spacing, rotateMix, translateMix;
|
||||||
|
|
||||||
|
internal ExposedList<float> spaces = new ExposedList<float>(), positions = new ExposedList<float>();
|
||||||
|
internal ExposedList<float> world = new ExposedList<float>(), curves = new ExposedList<float>(), lengths = new ExposedList<float>();
|
||||||
|
internal float[] segments = new float[10];
|
||||||
|
|
||||||
|
public float Position { get { return position; } set { position = value; } }
|
||||||
|
public float Spacing { get { return spacing; } set { spacing = value; } }
|
||||||
|
public float RotateMix { get { return rotateMix; } set { rotateMix = value; } }
|
||||||
|
public float TranslateMix { get { return translateMix; } set { translateMix = value; } }
|
||||||
|
public ExposedList<Bone> Bones { get { return bones; } }
|
||||||
|
public Slot Target { get { return target; } set { target = value; } }
|
||||||
|
public PathConstraintData Data { get { return data; } }
|
||||||
|
|
||||||
|
public PathConstraint (PathConstraintData data, Skeleton skeleton) {
|
||||||
|
if (data == null) throw new ArgumentNullException("data", "data cannot be null.");
|
||||||
|
if (skeleton == null) throw new ArgumentNullException("skeleton", "skeleton cannot be null.");
|
||||||
|
this.data = data;
|
||||||
|
bones = new ExposedList<Bone>(data.Bones.Count);
|
||||||
|
foreach (BoneData boneData in data.bones)
|
||||||
|
bones.Add(skeleton.FindBone(boneData.name));
|
||||||
|
target = skeleton.FindSlot(data.target.name);
|
||||||
|
position = data.position;
|
||||||
|
spacing = data.spacing;
|
||||||
|
rotateMix = data.rotateMix;
|
||||||
|
translateMix = data.translateMix;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Apply () {
|
||||||
|
Update();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Update () {
|
||||||
|
PathAttachment attachment = target.Attachment as PathAttachment;
|
||||||
|
if (attachment == null) return;
|
||||||
|
|
||||||
|
float rotateMix = this.rotateMix, translateMix = this.translateMix;
|
||||||
|
bool translate = translateMix > 0, rotate = rotateMix > 0;
|
||||||
|
if (!translate && !rotate) return;
|
||||||
|
|
||||||
|
PathConstraintData data = this.data;
|
||||||
|
SpacingMode spacingMode = data.spacingMode;
|
||||||
|
bool lengthSpacing = spacingMode == SpacingMode.Length;
|
||||||
|
RotateMode rotateMode = data.rotateMode;
|
||||||
|
bool tangents = rotateMode == RotateMode.Tangent, scale = rotateMode == RotateMode.ChainScale;
|
||||||
|
int boneCount = this.bones.Count, spacesCount = tangents ? boneCount : boneCount + 1;
|
||||||
|
Bone[] bones = this.bones.Items;
|
||||||
|
ExposedList<float> spaces = this.spaces.Resize(spacesCount), lengths = null;
|
||||||
|
float spacing = this.spacing;
|
||||||
|
if (scale || lengthSpacing) {
|
||||||
|
if (scale) lengths = this.lengths.Resize(boneCount);
|
||||||
|
for (int i = 0, n = spacesCount - 1; i < n;) {
|
||||||
|
Bone bone = bones[i];
|
||||||
|
float length = bone.data.length, x = length * bone.a, y = length * bone.c;
|
||||||
|
length = (float)Math.Sqrt(x * x + y * y);
|
||||||
|
if (scale) lengths.Items[i] = length;
|
||||||
|
spaces.Items[++i] = lengthSpacing ? Math.Max(0, length + spacing) : spacing;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (int i = 1; i < spacesCount; i++)
|
||||||
|
spaces.Items[i] = spacing;
|
||||||
|
}
|
||||||
|
|
||||||
|
float[] positions = ComputeWorldPositions(attachment, spacesCount, tangents,
|
||||||
|
data.positionMode == PositionMode.Percent, spacingMode == SpacingMode.Percent);
|
||||||
|
Skeleton skeleton = target.Skeleton;
|
||||||
|
float skeletonX = skeleton.x, skeletonY = skeleton.y;
|
||||||
|
float boneX = positions[0], boneY = positions[1], offsetRotation = data.offsetRotation;
|
||||||
|
bool tip = rotateMode == RotateMode.Chain && offsetRotation == 0;
|
||||||
|
for (int i = 0, p = 3; i < boneCount; i++, p += 3) {
|
||||||
|
Bone bone = (Bone)bones[i];
|
||||||
|
bone.worldX += (boneX - skeletonX - bone.worldX) * translateMix;
|
||||||
|
bone.worldY += (boneY - skeletonY - bone.worldY) * translateMix;
|
||||||
|
float x = positions[p], y = positions[p + 1], dx = x - boneX, dy = y - boneY;
|
||||||
|
if (scale) {
|
||||||
|
float length = lengths.Items[i];
|
||||||
|
if (length != 0) {
|
||||||
|
float s = ((float)Math.Sqrt(dx * dx + dy * dy) / length - 1) * rotateMix + 1;
|
||||||
|
bone.a *= s;
|
||||||
|
bone.c *= s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
boneX = x;
|
||||||
|
boneY = y;
|
||||||
|
if (rotate) {
|
||||||
|
float a = bone.a, b = bone.b, c = bone.c, d = bone.d, r, cos, sin;
|
||||||
|
if (tangents)
|
||||||
|
r = positions[p - 1];
|
||||||
|
else if (spaces.Items[i + 1] == 0)
|
||||||
|
r = positions[p + 2];
|
||||||
|
else
|
||||||
|
r = MathUtils.Atan2(dy, dx);
|
||||||
|
r -= MathUtils.Atan2(c, a) - offsetRotation * MathUtils.degRad;
|
||||||
|
if (tip) {
|
||||||
|
cos = MathUtils.Cos(r);
|
||||||
|
sin = MathUtils.Sin(r);
|
||||||
|
float length = bone.data.length;
|
||||||
|
boneX += (length * (cos * a - sin * c) - dx) * rotateMix;
|
||||||
|
boneY += (length * (sin * a + cos * c) - dy) * rotateMix;
|
||||||
|
}
|
||||||
|
if (r > MathUtils.PI)
|
||||||
|
r -= MathUtils.PI2;
|
||||||
|
else if (r < -MathUtils.PI) //
|
||||||
|
r += MathUtils.PI2;
|
||||||
|
r *= rotateMix;
|
||||||
|
cos = MathUtils.Cos(r);
|
||||||
|
sin = MathUtils.Sin(r);
|
||||||
|
bone.a = cos * a - sin * c;
|
||||||
|
bone.b = cos * b - sin * d;
|
||||||
|
bone.c = sin * a + cos * c;
|
||||||
|
bone.d = sin * b + cos * d;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float[] ComputeWorldPositions (PathAttachment path, int spacesCount, bool tangents, bool percentPosition,
|
||||||
|
bool percentSpacing) {
|
||||||
|
|
||||||
|
Slot target = this.target;
|
||||||
|
float position = this.position;
|
||||||
|
float[] spaces = this.spaces.Items, output = this.positions.Resize(spacesCount * 3 + 2).Items, world;
|
||||||
|
bool closed = path.Closed;
|
||||||
|
int verticesLength = path.WorldVerticesLength, curveCount = verticesLength / 6, prevCurve = NONE;
|
||||||
|
|
||||||
|
float pathLength;
|
||||||
|
if (!path.ConstantSpeed) {
|
||||||
|
float[] lengths = path.Lengths;
|
||||||
|
curveCount -= closed ? 1 : 2;
|
||||||
|
pathLength = lengths[curveCount];
|
||||||
|
if (percentPosition) position *= pathLength;
|
||||||
|
if (percentSpacing) {
|
||||||
|
for (int i = 0; i < spacesCount; i++)
|
||||||
|
spaces[i] *= pathLength;
|
||||||
|
}
|
||||||
|
world = this.world.Resize(8).Items;
|
||||||
|
for (int i = 0, o = 0, curve = 0; i < spacesCount; i++, o += 3) {
|
||||||
|
float space = spaces[i];
|
||||||
|
position += space;
|
||||||
|
float p = position;
|
||||||
|
|
||||||
|
if (closed) {
|
||||||
|
p %= pathLength;
|
||||||
|
if (p < 0) p += pathLength;
|
||||||
|
curve = 0;
|
||||||
|
} else if (p < 0) {
|
||||||
|
if (prevCurve != BEFORE) {
|
||||||
|
prevCurve = BEFORE;
|
||||||
|
path.ComputeWorldVertices(target, 2, 4, world, 0);
|
||||||
|
}
|
||||||
|
AddBeforePosition(p, world, 0, output, o);
|
||||||
|
continue;
|
||||||
|
} else if (p > pathLength) {
|
||||||
|
if (prevCurve != AFTER) {
|
||||||
|
prevCurve = AFTER;
|
||||||
|
path.ComputeWorldVertices(target, verticesLength - 6, 4, world, 0);
|
||||||
|
}
|
||||||
|
AddAfterPosition(p - pathLength, world, 0, output, o);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine curve containing position.
|
||||||
|
for (;; curve++) {
|
||||||
|
float length = lengths[curve];
|
||||||
|
if (p > length) continue;
|
||||||
|
if (curve == 0)
|
||||||
|
p /= length;
|
||||||
|
else {
|
||||||
|
float prev = lengths[curve - 1];
|
||||||
|
p = (p - prev) / (length - prev);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (curve != prevCurve) {
|
||||||
|
prevCurve = curve;
|
||||||
|
if (closed && curve == curveCount) {
|
||||||
|
path.ComputeWorldVertices(target, verticesLength - 4, 4, world, 0);
|
||||||
|
path.ComputeWorldVertices(target, 0, 4, world, 4);
|
||||||
|
} else
|
||||||
|
path.ComputeWorldVertices(target, curve * 6 + 2, 8, world, 0);
|
||||||
|
}
|
||||||
|
AddCurvePosition(p, world[0], world[1], world[2], world[3], world[4], world[5], world[6], world[7], output, o,
|
||||||
|
tangents || (i > 0 && space == 0));
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
// World vertices.
|
||||||
|
if (closed) {
|
||||||
|
verticesLength += 2;
|
||||||
|
world = this.world.Resize(verticesLength).Items;
|
||||||
|
path.ComputeWorldVertices(target, 2, verticesLength - 4, world, 0);
|
||||||
|
path.ComputeWorldVertices(target, 0, 2, world, verticesLength - 4);
|
||||||
|
world[verticesLength - 2] = world[0];
|
||||||
|
world[verticesLength - 1] = world[1];
|
||||||
|
} else {
|
||||||
|
curveCount--;
|
||||||
|
verticesLength -= 4;
|
||||||
|
world = this.world.Resize(verticesLength).Items;
|
||||||
|
path.ComputeWorldVertices(target, 2, verticesLength, world, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Curve lengths.
|
||||||
|
float[] curves = this.curves.Resize(curveCount).Items;
|
||||||
|
pathLength = 0;
|
||||||
|
float x1 = world[0], y1 = world[1], cx1 = 0, cy1 = 0, cx2 = 0, cy2 = 0, x2 = 0, y2 = 0;
|
||||||
|
float tmpx, tmpy, dddfx, dddfy, ddfx, ddfy, dfx, dfy;
|
||||||
|
for (int i = 0, w = 2; i < curveCount; i++, w += 6) {
|
||||||
|
cx1 = world[w];
|
||||||
|
cy1 = world[w + 1];
|
||||||
|
cx2 = world[w + 2];
|
||||||
|
cy2 = world[w + 3];
|
||||||
|
x2 = world[w + 4];
|
||||||
|
y2 = world[w + 5];
|
||||||
|
tmpx = (x1 - cx1 * 2 + cx2) * 0.1875f;
|
||||||
|
tmpy = (y1 - cy1 * 2 + cy2) * 0.1875f;
|
||||||
|
dddfx = ((cx1 - cx2) * 3 - x1 + x2) * 0.09375f;
|
||||||
|
dddfy = ((cy1 - cy2) * 3 - y1 + y2) * 0.09375f;
|
||||||
|
ddfx = tmpx * 2 + dddfx;
|
||||||
|
ddfy = tmpy * 2 + dddfy;
|
||||||
|
dfx = (cx1 - x1) * 0.75f + tmpx + dddfx * 0.16666667f;
|
||||||
|
dfy = (cy1 - y1) * 0.75f + tmpy + dddfy * 0.16666667f;
|
||||||
|
pathLength += (float)Math.Sqrt(dfx * dfx + dfy * dfy);
|
||||||
|
dfx += ddfx;
|
||||||
|
dfy += ddfy;
|
||||||
|
ddfx += dddfx;
|
||||||
|
ddfy += dddfy;
|
||||||
|
pathLength += (float)Math.Sqrt(dfx * dfx + dfy * dfy);
|
||||||
|
dfx += ddfx;
|
||||||
|
dfy += ddfy;
|
||||||
|
pathLength += (float)Math.Sqrt(dfx * dfx + dfy * dfy);
|
||||||
|
dfx += ddfx + dddfx;
|
||||||
|
dfy += ddfy + dddfy;
|
||||||
|
pathLength += (float)Math.Sqrt(dfx * dfx + dfy * dfy);
|
||||||
|
curves[i] = pathLength;
|
||||||
|
x1 = x2;
|
||||||
|
y1 = y2;
|
||||||
|
}
|
||||||
|
if (percentPosition) position *= pathLength;
|
||||||
|
if (percentSpacing) {
|
||||||
|
for (int i = 0; i < spacesCount; i++)
|
||||||
|
spaces[i] *= pathLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
float[] segments = this.segments;
|
||||||
|
float curveLength = 0;
|
||||||
|
for (int i = 0, o = 0, curve = 0, segment = 0; i < spacesCount; i++, o += 3) {
|
||||||
|
float space = spaces[i];
|
||||||
|
position += space;
|
||||||
|
float p = position;
|
||||||
|
|
||||||
|
if (closed) {
|
||||||
|
p %= pathLength;
|
||||||
|
if (p < 0) p += pathLength;
|
||||||
|
curve = 0;
|
||||||
|
} else if (p < 0) {
|
||||||
|
AddBeforePosition(p, world, 0, output, o);
|
||||||
|
continue;
|
||||||
|
} else if (p > pathLength) {
|
||||||
|
AddAfterPosition(p - pathLength, world, verticesLength - 4, output, o);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine curve containing position.
|
||||||
|
for (;; curve++) {
|
||||||
|
float length = curves[curve];
|
||||||
|
if (p > length) continue;
|
||||||
|
if (curve == 0)
|
||||||
|
p /= length;
|
||||||
|
else {
|
||||||
|
float prev = curves[curve - 1];
|
||||||
|
p = (p - prev) / (length - prev);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Curve segment lengths.
|
||||||
|
if (curve != prevCurve) {
|
||||||
|
prevCurve = curve;
|
||||||
|
int ii = curve * 6;
|
||||||
|
x1 = world[ii];
|
||||||
|
y1 = world[ii + 1];
|
||||||
|
cx1 = world[ii + 2];
|
||||||
|
cy1 = world[ii + 3];
|
||||||
|
cx2 = world[ii + 4];
|
||||||
|
cy2 = world[ii + 5];
|
||||||
|
x2 = world[ii + 6];
|
||||||
|
y2 = world[ii + 7];
|
||||||
|
tmpx = (x1 - cx1 * 2 + cx2) * 0.03f;
|
||||||
|
tmpy = (y1 - cy1 * 2 + cy2) * 0.03f;
|
||||||
|
dddfx = ((cx1 - cx2) * 3 - x1 + x2) * 0.006f;
|
||||||
|
dddfy = ((cy1 - cy2) * 3 - y1 + y2) * 0.006f;
|
||||||
|
ddfx = tmpx * 2 + dddfx;
|
||||||
|
ddfy = tmpy * 2 + dddfy;
|
||||||
|
dfx = (cx1 - x1) * 0.3f + tmpx + dddfx * 0.16666667f;
|
||||||
|
dfy = (cy1 - y1) * 0.3f + tmpy + dddfy * 0.16666667f;
|
||||||
|
curveLength = (float)Math.Sqrt(dfx * dfx + dfy * dfy);
|
||||||
|
segments[0] = curveLength;
|
||||||
|
for (ii = 1; ii < 8; ii++) {
|
||||||
|
dfx += ddfx;
|
||||||
|
dfy += ddfy;
|
||||||
|
ddfx += dddfx;
|
||||||
|
ddfy += dddfy;
|
||||||
|
curveLength += (float)Math.Sqrt(dfx * dfx + dfy * dfy);
|
||||||
|
segments[ii] = curveLength;
|
||||||
|
}
|
||||||
|
dfx += ddfx;
|
||||||
|
dfy += ddfy;
|
||||||
|
curveLength += (float)Math.Sqrt(dfx * dfx + dfy * dfy);
|
||||||
|
segments[8] = curveLength;
|
||||||
|
dfx += ddfx + dddfx;
|
||||||
|
dfy += ddfy + dddfy;
|
||||||
|
curveLength += (float)Math.Sqrt(dfx * dfx + dfy * dfy);
|
||||||
|
segments[9] = curveLength;
|
||||||
|
segment = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Weight by segment length.
|
||||||
|
p *= curveLength;
|
||||||
|
for (;; segment++) {
|
||||||
|
float length = segments[segment];
|
||||||
|
if (p > length) continue;
|
||||||
|
if (segment == 0)
|
||||||
|
p /= length;
|
||||||
|
else {
|
||||||
|
float prev = segments[segment - 1];
|
||||||
|
p = segment + (p - prev) / (length - prev);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
AddCurvePosition(p * 0.1f, x1, y1, cx1, cy1, cx2, cy2, x2, y2, output, o, tangents || (i > 0 && space == 0));
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddBeforePosition (float p, float[] temp, int i, float[] output, int o) {
|
||||||
|
float x1 = temp[i], y1 = temp[i + 1], dx = temp[i + 2] - x1, dy = temp[i + 3] - y1, r = MathUtils.Atan2(dy, dx);
|
||||||
|
output[o] = x1 + p * MathUtils.Cos(r);
|
||||||
|
output[o + 1] = y1 + p * MathUtils.Sin(r);
|
||||||
|
output[o + 2] = r;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddAfterPosition (float p, float[] temp, int i, float[] output, int o) {
|
||||||
|
float x1 = temp[i + 2], y1 = temp[i + 3], dx = x1 - temp[i], dy = y1 - temp[i + 1], r = MathUtils.Atan2(dy, dx);
|
||||||
|
output[o] = x1 + p * MathUtils.Cos(r);
|
||||||
|
output[o + 1] = y1 + p * MathUtils.Sin(r);
|
||||||
|
output[o + 2] = r;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddCurvePosition (float p, float x1, float y1, float cx1, float cy1, float cx2, float cy2, float x2, float y2,
|
||||||
|
float[] output, int o, bool tangents) {
|
||||||
|
if (p == 0) p = 0.0001f;
|
||||||
|
float tt = p * p, ttt = tt * p, u = 1 - p, uu = u * u, uuu = uu * u;
|
||||||
|
float ut = u * p, ut3 = ut * 3, uut3 = u * ut3, utt3 = ut3 * p;
|
||||||
|
float x = x1 * uuu + cx1 * uut3 + cx2 * utt3 + x2 * ttt, y = y1 * uuu + cy1 * uut3 + cy2 * utt3 + y2 * ttt;
|
||||||
|
output[o] = x;
|
||||||
|
output[o + 1] = y;
|
||||||
|
if (tangents) output[o + 2] = (float)Math.Atan2(y - (y1 * uu + cy1 * ut * 2 + cy2 * tt), x - (x1 * uu + cx1 * ut * 2 + cx2 * tt));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
74
spine-csharp/src/PathConstraintData.cs
Normal file
74
spine-csharp/src/PathConstraintData.cs
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
/******************************************************************************
|
||||||
|
* Spine Runtimes Software License
|
||||||
|
* Version 2.3
|
||||||
|
*
|
||||||
|
* Copyright (c) 2013-2015, Esoteric Software
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* You are granted a perpetual, non-exclusive, non-sublicensable and
|
||||||
|
* non-transferable license to use, install, execute and perform the Spine
|
||||||
|
* Runtimes Software (the "Software") and derivative works solely for personal
|
||||||
|
* or internal use. Without the written permission of Esoteric Software (see
|
||||||
|
* Section 2 of the Spine Software License Agreement), you may not (a) modify,
|
||||||
|
* translate, adapt or otherwise create derivative works, improvements of the
|
||||||
|
* Software or develop new applications using the Software or (b) remove,
|
||||||
|
* delete, alter or obscure any trademarks or any copyright, trademark, patent
|
||||||
|
* or other intellectual property or proprietary rights notices on or in the
|
||||||
|
* Software, including any copy thereof. Redistributions in binary or source
|
||||||
|
* form must include this license and terms.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "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 BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||||
|
* OR BUSINESS INTERRUPTION) 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 THIS SOFTWARE, EVEN IF
|
||||||
|
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Spine {
|
||||||
|
public class PathConstraintData {
|
||||||
|
internal String name;
|
||||||
|
internal ExposedList<BoneData> bones = new ExposedList<BoneData>();
|
||||||
|
internal SlotData target;
|
||||||
|
internal PositionMode positionMode;
|
||||||
|
internal SpacingMode spacingMode;
|
||||||
|
internal RotateMode rotateMode;
|
||||||
|
internal float offsetRotation;
|
||||||
|
internal float position, spacing, rotateMix, translateMix;
|
||||||
|
|
||||||
|
public ExposedList<BoneData> Bones { get { return bones; } }
|
||||||
|
public SlotData Target { get { return target; } set { target = value; } }
|
||||||
|
public PositionMode PositionMode { get { return positionMode; } set { positionMode = value; } }
|
||||||
|
public SpacingMode SpacingMode { get { return spacingMode; } set { spacingMode = value; } }
|
||||||
|
public RotateMode RotateMode { get { return rotateMode; } set { rotateMode = value; } }
|
||||||
|
public float OffsetRotation { get { return offsetRotation; } set { offsetRotation = value; } }
|
||||||
|
public float Position { get { return position; } set { position = value; } }
|
||||||
|
public float Spacing { get { return spacing; } set { spacing = value; } }
|
||||||
|
public float RotateMix { get { return rotateMix; } set { rotateMix = value; } }
|
||||||
|
public float TranslateMix { get { return translateMix; } set { translateMix = value; } }
|
||||||
|
public String Name { get { return name; } }
|
||||||
|
|
||||||
|
public PathConstraintData (String name) {
|
||||||
|
if (name == null) throw new ArgumentNullException("name", "name cannot be null.");
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum PositionMode {
|
||||||
|
Fixed, Percent
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum SpacingMode {
|
||||||
|
Length, Fixed, Percent
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum RotateMode {
|
||||||
|
Tangent, Chain, ChainScale
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -38,9 +38,10 @@ namespace Spine {
|
|||||||
internal ExposedList<Bone> bones;
|
internal ExposedList<Bone> bones;
|
||||||
internal ExposedList<Slot> slots;
|
internal ExposedList<Slot> slots;
|
||||||
internal ExposedList<Slot> drawOrder;
|
internal ExposedList<Slot> drawOrder;
|
||||||
internal ExposedList<IkConstraint> ikConstraints;
|
internal ExposedList<IkConstraint> ikConstraints, ikConstraintsSorted;
|
||||||
internal ExposedList<TransformConstraint> transformConstraints;
|
internal ExposedList<TransformConstraint> transformConstraints;
|
||||||
private ExposedList<IUpdatable> updateCache = new ExposedList<IUpdatable>();
|
internal ExposedList<PathConstraint> pathConstraints;
|
||||||
|
internal ExposedList<IUpdatable> updateCache = new ExposedList<IUpdatable>();
|
||||||
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;
|
||||||
@ -49,9 +50,12 @@ namespace Spine {
|
|||||||
|
|
||||||
public SkeletonData Data { get { return data; } }
|
public SkeletonData Data { get { return data; } }
|
||||||
public ExposedList<Bone> Bones { get { return bones; } }
|
public ExposedList<Bone> Bones { get { return bones; } }
|
||||||
|
public ExposedList<IUpdatable> UpdateCacheList { get { return updateCache; } }
|
||||||
public ExposedList<Slot> Slots { get { return slots; } }
|
public ExposedList<Slot> Slots { get { return slots; } }
|
||||||
public ExposedList<Slot> DrawOrder { get { return drawOrder; } }
|
public ExposedList<Slot> DrawOrder { get { return drawOrder; } }
|
||||||
public ExposedList<IkConstraint> IkConstraints { get { return ikConstraints; } set { ikConstraints = value; } }
|
public ExposedList<IkConstraint> IkConstraints { get { return ikConstraints; } }
|
||||||
|
public ExposedList<PathConstraint> PathConstraints { get { return pathConstraints; } }
|
||||||
|
public ExposedList<TransformConstraint> TransformConstraints { get { return transformConstraints; } }
|
||||||
public Skin Skin { get { return skin; } set { skin = value; } }
|
public Skin Skin { get { return skin; } set { skin = 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; } }
|
||||||
@ -64,33 +68,37 @@ namespace Spine {
|
|||||||
public bool FlipY { get { return flipY; } set { flipY = value; } }
|
public bool FlipY { get { return flipY; } set { flipY = value; } }
|
||||||
|
|
||||||
public Bone RootBone {
|
public Bone RootBone {
|
||||||
get {
|
get { return bones.Count == 0 ? null : bones.Items[0]; }
|
||||||
return bones.Count == 0 ? null : bones.Items[0];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Skeleton (SkeletonData data) {
|
public Skeleton (SkeletonData data) {
|
||||||
if (data == null) throw new ArgumentNullException("data cannot be null.");
|
if (data == null) throw new ArgumentNullException("data", "data cannot be null.");
|
||||||
this.data = data;
|
this.data = data;
|
||||||
|
|
||||||
bones = new ExposedList<Bone>(data.bones.Count);
|
bones = new ExposedList<Bone>(data.bones.Count);
|
||||||
foreach (BoneData boneData in data.bones) {
|
foreach (BoneData boneData in data.bones) {
|
||||||
Bone parent = boneData.parent == null ? null : bones.Items[data.bones.IndexOf(boneData.parent)];
|
Bone bone;
|
||||||
Bone bone = new Bone(boneData, this, parent);
|
if (boneData.parent == null) {
|
||||||
if (parent != null) parent.children.Add(bone);
|
bone = new Bone (boneData, this, null);
|
||||||
|
} else {
|
||||||
|
Bone parent = bones.Items[boneData.parent.index];
|
||||||
|
bone = new Bone (boneData, this, parent);
|
||||||
|
parent.children.Add (bone);
|
||||||
|
}
|
||||||
bones.Add(bone);
|
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[data.bones.IndexOf(slotData.boneData)];
|
Bone bone = bones.Items[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);
|
||||||
}
|
}
|
||||||
|
|
||||||
ikConstraints = new ExposedList<IkConstraint>(data.ikConstraints.Count);
|
ikConstraints = new ExposedList<IkConstraint>(data.ikConstraints.Count);
|
||||||
|
ikConstraintsSorted = new ExposedList<IkConstraint>(data.ikConstraints.Count);
|
||||||
foreach (IkConstraintData ikConstraintData in data.ikConstraints)
|
foreach (IkConstraintData ikConstraintData in data.ikConstraints)
|
||||||
ikConstraints.Add(new IkConstraint(ikConstraintData, this));
|
ikConstraints.Add(new IkConstraint(ikConstraintData, this));
|
||||||
|
|
||||||
@ -98,46 +106,150 @@ namespace Spine {
|
|||||||
foreach (TransformConstraintData transformConstraintData in data.transformConstraints)
|
foreach (TransformConstraintData transformConstraintData in data.transformConstraints)
|
||||||
transformConstraints.Add(new TransformConstraint(transformConstraintData, this));
|
transformConstraints.Add(new TransformConstraint(transformConstraintData, this));
|
||||||
|
|
||||||
|
pathConstraints = new ExposedList<PathConstraint> (data.pathConstraints.Count);
|
||||||
|
foreach (PathConstraintData pathConstraintData in data.pathConstraints)
|
||||||
|
pathConstraints.Add(new PathConstraint(pathConstraintData, this));
|
||||||
|
|
||||||
UpdateCache();
|
UpdateCache();
|
||||||
UpdateWorldTransform();
|
UpdateWorldTransform();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Caches information about bones and constraints. Must be called if bones or constraints are added
|
/// <summary>Caches information about bones and constraints. Must be called if bones, constraints or weighted path attachments are added
|
||||||
/// or removed.</summary>
|
/// or removed.</summary>
|
||||||
public void UpdateCache () {
|
public void UpdateCache () {
|
||||||
ExposedList<Bone> bones = this.bones;
|
|
||||||
ExposedList<IUpdatable> updateCache = this.updateCache;
|
ExposedList<IUpdatable> updateCache = this.updateCache;
|
||||||
ExposedList<IkConstraint> ikConstraints = this.ikConstraints;
|
|
||||||
ExposedList<TransformConstraint> transformConstraints = this.transformConstraints;
|
|
||||||
int ikConstraintsCount = ikConstraints.Count;
|
|
||||||
int transformConstraintsCount = transformConstraints.Count;
|
|
||||||
updateCache.Clear();
|
updateCache.Clear();
|
||||||
for (int i = 0, n = bones.Count; i < n; i++) {
|
|
||||||
Bone bone = bones.Items[i];
|
ExposedList<Bone> bones = this.bones;
|
||||||
updateCache.Add(bone);
|
for (int i = 0, n = bones.Count; i < n; i++)
|
||||||
for (int ii = 0; ii < ikConstraintsCount; ii++) {
|
bones.Items[i].sorted = false;
|
||||||
IkConstraint ikConstraint = ikConstraints.Items[ii];
|
|
||||||
if (bone == ikConstraint.bones.Items[ikConstraint.bones.Count - 1]) {
|
ExposedList<IkConstraint> ikConstraints = this.ikConstraintsSorted;
|
||||||
updateCache.Add(ikConstraint);
|
ikConstraints.Clear();
|
||||||
break;
|
ikConstraints.AddRange(this.ikConstraints);
|
||||||
|
int ikCount = ikConstraints.Count;
|
||||||
|
for (int i = 0, level, n = ikCount; i < n; i++) {
|
||||||
|
IkConstraint ik = ikConstraints.Items[i];
|
||||||
|
Bone bone = ik.bones.Items[0].Parent;
|
||||||
|
for (level = 0; bone != null; level++)
|
||||||
|
bone = bone.Parent;
|
||||||
|
ik.level = level;
|
||||||
}
|
}
|
||||||
|
for (int i = 1, ii; i < ikCount; i++) {
|
||||||
|
IkConstraint ik = ikConstraints.Items [i];
|
||||||
|
int level = ik.level;
|
||||||
|
for (ii = i - 1; ii >= 0; ii--) {
|
||||||
|
IkConstraint other = ikConstraints.Items[ii];
|
||||||
|
if (other.level < level) break;
|
||||||
|
ikConstraints.Items[ii + 1] = other;
|
||||||
|
}
|
||||||
|
ikConstraints.Items[ii + 1] = ik;
|
||||||
|
}
|
||||||
|
for (int i = 0, n = ikConstraints.Count; i < n; i++) {
|
||||||
|
IkConstraint constraint = ikConstraints.Items[i];
|
||||||
|
Bone target = constraint.target;
|
||||||
|
SortBone(target);
|
||||||
|
|
||||||
|
ExposedList<Bone> constrained = constraint.bones;
|
||||||
|
Bone parent = constrained.Items [0];
|
||||||
|
SortBone(parent);
|
||||||
|
|
||||||
|
updateCache.Add(constraint);
|
||||||
|
|
||||||
|
SortReset(parent.children);
|
||||||
|
constrained.Items[constrained.Count - 1].sorted = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ExposedList<PathConstraint> pathConstraints = this.pathConstraints;
|
||||||
|
for (int i = 0, n = pathConstraints.Count; i < n; i++) {
|
||||||
|
PathConstraint constraint = pathConstraints.Items[i];
|
||||||
|
|
||||||
|
Slot slot = constraint.Target;
|
||||||
|
int slotIndex = slot.data.index;
|
||||||
|
Bone slotBone = slot.bone;
|
||||||
|
if (skin != null) SortPathConstraintAttachment(skin, slotIndex, slotBone);
|
||||||
|
if (data.defaultSkin != null && data.defaultSkin != skin)
|
||||||
|
SortPathConstraintAttachment(data.defaultSkin, slotIndex, slotBone);
|
||||||
|
for (int ii = 0, nn = data.skins.Count; ii < nn; ii++)
|
||||||
|
SortPathConstraintAttachment(data.skins.Items[ii], slotIndex, slotBone);
|
||||||
|
|
||||||
|
PathAttachment attachment = slot.Attachment as PathAttachment;
|
||||||
|
if (attachment != null) SortPathConstraintAttachment(attachment, slotBone);
|
||||||
|
|
||||||
|
ExposedList<Bone> constrained = constraint.bones;
|
||||||
|
int boneCount = constrained.Count;
|
||||||
|
for (int ii = 0; ii < boneCount; ii++)
|
||||||
|
SortBone(constrained.Items[ii]);
|
||||||
|
|
||||||
|
updateCache.Add(constraint);
|
||||||
|
|
||||||
|
for (int ii = 0; ii < boneCount; ii++)
|
||||||
|
SortReset(constrained.Items[ii].children);
|
||||||
|
for (int ii = 0; ii < boneCount; ii++)
|
||||||
|
constrained.Items[ii].sorted = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ExposedList<TransformConstraint> transformConstraints = this.transformConstraints;
|
||||||
|
for (int i = 0, n = transformConstraints.Count; i < n; i++) {
|
||||||
|
TransformConstraint constraint = transformConstraints.Items[i];
|
||||||
|
|
||||||
|
SortBone(constraint.target);
|
||||||
|
|
||||||
|
ExposedList<Bone> constrained = constraint.bones;
|
||||||
|
int boneCount = constrained.Count;
|
||||||
|
for (int ii = 0; ii < boneCount; ii++)
|
||||||
|
SortBone(constrained.Items[ii]);
|
||||||
|
|
||||||
|
updateCache.Add(constraint);
|
||||||
|
|
||||||
|
for (int ii = 0; ii < boneCount; ii++)
|
||||||
|
SortReset(constrained.Items[ii].children);
|
||||||
|
for (int ii = 0; ii < boneCount; ii++)
|
||||||
|
constrained.Items[ii].sorted = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0, n = bones.Count; i < n; i++)
|
||||||
|
SortBone(bones.Items[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SortPathConstraintAttachment (Skin skin, int slotIndex, Bone slotBone) {
|
||||||
|
foreach (var entry in skin.Attachments)
|
||||||
|
if (entry.Key.slotIndex == slotIndex) SortPathConstraintAttachment(entry.Value, slotBone);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SortPathConstraintAttachment (Attachment attachment, Bone slotBone) {
|
||||||
|
var pathAttachment = attachment as PathAttachment;
|
||||||
|
if (pathAttachment == null) return;
|
||||||
|
int[] pathBones = pathAttachment.bones;
|
||||||
|
if (pathBones == null)
|
||||||
|
SortBone(slotBone);
|
||||||
|
else {
|
||||||
|
var bones = this.bones;
|
||||||
|
for (int i = 0, n = pathBones.Length; i < n; i++)
|
||||||
|
SortBone(bones.Items[pathBones[i]]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < transformConstraintsCount; i++) {
|
private void SortBone (Bone bone) {
|
||||||
TransformConstraint transformConstraint = transformConstraints.Items[i];
|
if (bone.sorted) return;
|
||||||
for (int ii = updateCache.Count - 1; i >= 0; ii--) {
|
Bone parent = bone.parent;
|
||||||
if (updateCache.Items[ii] == transformConstraint.bone) {
|
if (parent != null) SortBone(parent);
|
||||||
updateCache.Insert(ii + 1, transformConstraint);
|
bone.sorted = true;
|
||||||
break;
|
updateCache.Add(bone);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void SortReset (ExposedList<Bone> bones) {
|
||||||
|
var bonesItems = bones.Items;
|
||||||
|
for (int i = 0, n = bones.Count; i < n; i++) {
|
||||||
|
Bone bone = bonesItems[i];
|
||||||
|
if (bone.sorted) SortReset(bone.children);
|
||||||
|
bone.sorted = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Updates the world transform for each bone and applies constraints.</summary>
|
/// <summary>Updates the world transform for each bone and applies constraints.</summary>
|
||||||
public void UpdateWorldTransform () {
|
public void UpdateWorldTransform () {
|
||||||
ExposedList<IUpdatable> updateCache = this.updateCache;
|
var updateCache = this.updateCache;
|
||||||
for (int i = 0, n = updateCache.Count; i < n; i++)
|
for (int i = 0, n = updateCache.Count; i < n; i++)
|
||||||
updateCache.Items[i].Update();
|
updateCache.Items[i].Update();
|
||||||
}
|
}
|
||||||
@ -150,44 +262,56 @@ 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 () {
|
||||||
ExposedList<Bone> bones = this.bones;
|
var bonesItems = this.bones.Items;
|
||||||
for (int i = 0, n = bones.Count; i < n; i++)
|
for (int i = 0, n = bones.Count; i < n; i++)
|
||||||
bones.Items[i].SetToSetupPose();
|
bonesItems[i].SetToSetupPose();
|
||||||
|
|
||||||
ExposedList<IkConstraint> ikConstraints = this.ikConstraints;
|
var ikConstraintsItems = this.ikConstraints.Items;
|
||||||
for (int i = 0, n = ikConstraints.Count; i < n; i++) {
|
for (int i = 0, n = ikConstraints.Count; i < n; i++) {
|
||||||
IkConstraint constraint = ikConstraints.Items[i];
|
IkConstraint constraint = ikConstraintsItems[i];
|
||||||
constraint.bendDirection = constraint.data.bendDirection;
|
constraint.bendDirection = constraint.data.bendDirection;
|
||||||
constraint.mix = constraint.data.mix;
|
constraint.mix = constraint.data.mix;
|
||||||
}
|
}
|
||||||
|
|
||||||
ExposedList<TransformConstraint> transformConstraints = this.transformConstraints;
|
var transformConstraintsItems = this.transformConstraints.Items;
|
||||||
for (int i = 0, n = transformConstraints.Count; i < n; i++) {
|
for (int i = 0, n = transformConstraints.Count; i < n; i++) {
|
||||||
TransformConstraint constraint = transformConstraints.Items[i];
|
TransformConstraint constraint = transformConstraintsItems[i];
|
||||||
TransformConstraintData data = constraint.data;
|
TransformConstraintData data = constraint.data;
|
||||||
constraint.rotateMix = data.rotateMix;
|
constraint.rotateMix = data.rotateMix;
|
||||||
constraint.translateMix = data.translateMix;
|
constraint.translateMix = data.translateMix;
|
||||||
constraint.scaleMix = data.scaleMix;
|
constraint.scaleMix = data.scaleMix;
|
||||||
constraint.shearMix = data.shearMix;
|
constraint.shearMix = data.shearMix;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var pathConstraints = this.pathConstraints;
|
||||||
|
for (int i = 0, n = pathConstraints.Count; i < n; i++) {
|
||||||
|
PathConstraint constraint = pathConstraints.Items[i];
|
||||||
|
PathConstraintData data = constraint.data;
|
||||||
|
constraint.position = data.position;
|
||||||
|
constraint.spacing = data.spacing;
|
||||||
|
constraint.rotateMix = data.rotateMix;
|
||||||
|
constraint.translateMix = data.translateMix;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetSlotsToSetupPose () {
|
public void SetSlotsToSetupPose () {
|
||||||
ExposedList<Slot> slots = this.slots;
|
var slots = this.slots;
|
||||||
|
var slotsItems = slots.Items;
|
||||||
drawOrder.Clear();
|
drawOrder.Clear();
|
||||||
for (int i = 0, n = slots.Count; i < n; i++)
|
for (int i = 0, n = slots.Count; i < n; i++)
|
||||||
drawOrder.Add(slots.Items[i]);
|
drawOrder.Add(slotsItems[i]);
|
||||||
|
|
||||||
for (int i = 0, n = slots.Count; i < n; i++)
|
for (int i = 0, n = slots.Count; i < n; i++)
|
||||||
slots.Items[i].SetToSetupPose(i);
|
slotsItems[i].SetToSetupPose();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <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 cannot be null.");
|
if (boneName == null) throw new ArgumentNullException("boneName", "boneName cannot be null.");
|
||||||
ExposedList<Bone> bones = this.bones;
|
var bones = this.bones;
|
||||||
|
var bonesItems = bones.Items;
|
||||||
for (int i = 0, n = bones.Count; i < n; i++) {
|
for (int i = 0, n = bones.Count; i < n; i++) {
|
||||||
Bone bone = bones.Items[i];
|
Bone bone = bonesItems[i];
|
||||||
if (bone.data.name == boneName) return bone;
|
if (bone.data.name == boneName) return bone;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
@ -195,19 +319,21 @@ 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 cannot be null.");
|
if (boneName == null) throw new ArgumentNullException("boneName", "boneName cannot be null.");
|
||||||
ExposedList<Bone> bones = this.bones;
|
var bones = this.bones;
|
||||||
|
var bonesItems = bones.Items;
|
||||||
for (int i = 0, n = bones.Count; i < n; i++)
|
for (int i = 0, n = bones.Count; i < n; i++)
|
||||||
if (bones.Items[i].data.name == boneName) return i;
|
if (bonesItems[i].data.name == boneName) return i;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <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 cannot be null.");
|
if (slotName == null) throw new ArgumentNullException("slotName", "slotName cannot be null.");
|
||||||
ExposedList<Slot> slots = this.slots;
|
var slots = this.slots;
|
||||||
|
var slotsItems = slots.Items;
|
||||||
for (int i = 0, n = slots.Count; i < n; i++) {
|
for (int i = 0, n = slots.Count; i < n; i++) {
|
||||||
Slot slot = slots.Items[i];
|
Slot slot = slotsItems[i];
|
||||||
if (slot.data.name == slotName) return slot;
|
if (slot.data.name == slotName) return slot;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
@ -215,17 +341,18 @@ namespace Spine {
|
|||||||
|
|
||||||
/// <returns>-1 if the bone was not found.</returns>
|
/// <returns>-1 if the bone was not found.</returns>
|
||||||
public int FindSlotIndex (String slotName) {
|
public int FindSlotIndex (String slotName) {
|
||||||
if (slotName == null) throw new ArgumentNullException("slotName cannot be null.");
|
if (slotName == null) throw new ArgumentNullException("slotName", "slotName cannot be null.");
|
||||||
ExposedList<Slot> slots = this.slots;
|
var slots = this.slots;
|
||||||
|
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 (slots.Items[i].data.name.Equals(slotName)) return i;
|
if (slotsItems[i].data.name.Equals(slotName)) return i;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Sets a skin by name (see SetSkin).</summary>
|
/// <summary>Sets a skin by name (see SetSkin).</summary>
|
||||||
public void SetSkin (String skinName) {
|
public void SetSkin (String skinName) {
|
||||||
Skin skin = data.FindSkin(skinName);
|
Skin skin = data.FindSkin(skinName);
|
||||||
if (skin == null) throw new ArgumentException("Skin not found: " + skinName);
|
if (skin == null) throw new ArgumentException("Skin not found: " + skinName, "skinName");
|
||||||
SetSkin(skin);
|
SetSkin(skin);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -259,7 +386,7 @@ namespace Spine {
|
|||||||
|
|
||||||
/// <returns>May be null.</returns>
|
/// <returns>May be null.</returns>
|
||||||
public Attachment GetAttachment (int slotIndex, String attachmentName) {
|
public Attachment GetAttachment (int slotIndex, String attachmentName) {
|
||||||
if (attachmentName == null) throw new ArgumentNullException("attachmentName cannot be null.");
|
if (attachmentName == null) throw new ArgumentNullException("attachmentName", "attachmentName cannot be null.");
|
||||||
if (skin != null) {
|
if (skin != null) {
|
||||||
Attachment attachment = skin.GetAttachment(slotIndex, attachmentName);
|
Attachment attachment = skin.GetAttachment(slotIndex, attachmentName);
|
||||||
if (attachment != null) return attachment;
|
if (attachment != null) return attachment;
|
||||||
@ -270,7 +397,7 @@ namespace Spine {
|
|||||||
|
|
||||||
/// <param name="attachmentName">May be null.</param>
|
/// <param name="attachmentName">May be null.</param>
|
||||||
public void SetAttachment (String slotName, String attachmentName) {
|
public void SetAttachment (String slotName, String attachmentName) {
|
||||||
if (slotName == null) throw new ArgumentNullException("slotName cannot be null.");
|
if (slotName == null) throw new ArgumentNullException("slotName", "slotName cannot be null.");
|
||||||
ExposedList<Slot> slots = this.slots;
|
ExposedList<Slot> slots = this.slots;
|
||||||
for (int i = 0, n = slots.Count; i < n; i++) {
|
for (int i = 0, n = slots.Count; i < n; i++) {
|
||||||
Slot slot = slots.Items[i];
|
Slot slot = slots.Items[i];
|
||||||
@ -287,9 +414,9 @@ namespace Spine {
|
|||||||
throw new Exception("Slot not found: " + slotName);
|
throw new Exception("Slot not found: " + slotName);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @return May be null. */
|
/// <returns>May be null.</returns>
|
||||||
public IkConstraint FindIkConstraint (String constraintName) {
|
public IkConstraint FindIkConstraint (String constraintName) {
|
||||||
if (constraintName == null) throw new ArgumentNullException("constraintName cannot be null.");
|
if (constraintName == null) throw new ArgumentNullException("constraintName", "constraintName cannot be null.");
|
||||||
ExposedList<IkConstraint> ikConstraints = this.ikConstraints;
|
ExposedList<IkConstraint> ikConstraints = this.ikConstraints;
|
||||||
for (int i = 0, n = ikConstraints.Count; i < n; i++) {
|
for (int i = 0, n = ikConstraints.Count; i < n; i++) {
|
||||||
IkConstraint ikConstraint = ikConstraints.Items[i];
|
IkConstraint ikConstraint = ikConstraints.Items[i];
|
||||||
@ -298,9 +425,9 @@ namespace Spine {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @return May be null. */
|
/// <returns>May be null.</returns>
|
||||||
public TransformConstraint FindTransformConstraint (String constraintName) {
|
public TransformConstraint FindTransformConstraint (String constraintName) {
|
||||||
if (constraintName == null) throw new ArgumentNullException("constraintName cannot be null.");
|
if (constraintName == null) throw new ArgumentNullException("constraintName", "constraintName cannot be null.");
|
||||||
ExposedList<TransformConstraint> transformConstraints = this.transformConstraints;
|
ExposedList<TransformConstraint> transformConstraints = this.transformConstraints;
|
||||||
for (int i = 0, n = transformConstraints.Count; i < n; i++) {
|
for (int i = 0, n = transformConstraints.Count; i < n; i++) {
|
||||||
TransformConstraint transformConstraint = transformConstraints.Items[i];
|
TransformConstraint transformConstraint = transformConstraints.Items[i];
|
||||||
@ -309,6 +436,17 @@ namespace Spine {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <returns>May be null.</returns>
|
||||||
|
public PathConstraint FindPathConstraint (String constraintName) {
|
||||||
|
if (constraintName == null) throw new ArgumentNullException("constraintName", "constraintName cannot be null.");
|
||||||
|
ExposedList<PathConstraint> pathConstraints = this.pathConstraints;
|
||||||
|
for (int i = 0, n = pathConstraints.Count; i < n; i++) {
|
||||||
|
PathConstraint constraint = pathConstraints.Items[i];
|
||||||
|
if (constraint.data.name.Equals(constraintName)) return constraint;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public void Update (float delta) {
|
public void Update (float delta) {
|
||||||
time += delta;
|
time += delta;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -44,12 +44,17 @@ using Windows.Storage;
|
|||||||
|
|
||||||
namespace Spine {
|
namespace Spine {
|
||||||
public class SkeletonBinary {
|
public class SkeletonBinary {
|
||||||
public const int TIMELINE_ROTATE = 0;
|
public const int BONE_ROTATE = 0;
|
||||||
public const int TIMELINE_TRANSLATE = 1;
|
public const int BONE_TRANSLATE = 1;
|
||||||
public const int TIMELINE_SCALE = 2;
|
public const int BONE_SCALE = 2;
|
||||||
public const int TIMELINE_SHEAR = 3;
|
public const int BONE_SHEAR = 3;
|
||||||
public const int TIMELINE_ATTACHMENT = 4;
|
|
||||||
public const int TIMELINE_COLOR = 5;
|
public const int SLOT_ATTACHMENT = 0;
|
||||||
|
public const int SLOT_COLOR = 1;
|
||||||
|
|
||||||
|
public const int PATH_POSITION = 0;
|
||||||
|
public const int PATH_SPACING = 1;
|
||||||
|
public const int PATH_MIX = 2;
|
||||||
|
|
||||||
public const int CURVE_LINEAR = 0;
|
public const int CURVE_LINEAR = 0;
|
||||||
public const int CURVE_STEPPED = 1;
|
public const int CURVE_STEPPED = 1;
|
||||||
@ -122,55 +127,26 @@ namespace Spine {
|
|||||||
for (int i = 0, n = ReadVarint(input, true); i < n; i++) {
|
for (int i = 0, n = ReadVarint(input, true); i < n; i++) {
|
||||||
String name = ReadString(input);
|
String name = ReadString(input);
|
||||||
BoneData parent = i == 0 ? null : skeletonData.bones.Items[ReadVarint(input, true)];
|
BoneData parent = i == 0 ? null : skeletonData.bones.Items[ReadVarint(input, true)];
|
||||||
BoneData boneData = new BoneData(name, parent);
|
BoneData data = new BoneData(i, name, parent);
|
||||||
boneData.rotation = ReadFloat(input);
|
data.rotation = ReadFloat(input);
|
||||||
boneData.x = ReadFloat(input) * scale;
|
data.x = ReadFloat(input) * scale;
|
||||||
boneData.y = ReadFloat(input) * scale;
|
data.y = ReadFloat(input) * scale;
|
||||||
boneData.scaleX = ReadFloat(input);
|
data.scaleX = ReadFloat(input);
|
||||||
boneData.scaleY = ReadFloat(input);
|
data.scaleY = ReadFloat(input);
|
||||||
boneData.shearX = ReadFloat(input);
|
data.shearX = ReadFloat(input);
|
||||||
boneData.shearY = ReadFloat(input);
|
data.shearY = ReadFloat(input);
|
||||||
boneData.length = ReadFloat(input) * scale;
|
data.length = ReadFloat(input) * scale;
|
||||||
boneData.inheritRotation = ReadBoolean(input);
|
data.inheritRotation = ReadBoolean(input);
|
||||||
boneData.inheritScale = ReadBoolean(input);
|
data.inheritScale = ReadBoolean(input);
|
||||||
if (nonessential) ReadInt(input); // Skip bone color.
|
if (nonessential) ReadInt(input); // Skip bone color.
|
||||||
skeletonData.bones.Add(boneData);
|
skeletonData.bones.Add(data);
|
||||||
}
|
|
||||||
|
|
||||||
// IK constraints.
|
|
||||||
for (int i = 0, n = ReadVarint(input, true); i < n; i++) {
|
|
||||||
IkConstraintData ikConstraintData = new IkConstraintData(ReadString(input));
|
|
||||||
for (int ii = 0, nn = ReadVarint(input, true); ii < nn; ii++)
|
|
||||||
ikConstraintData.bones.Add(skeletonData.bones.Items[ReadVarint(input, true)]);
|
|
||||||
ikConstraintData.target = skeletonData.bones.Items[ReadVarint(input, true)];
|
|
||||||
ikConstraintData.mix = ReadFloat(input);
|
|
||||||
ikConstraintData.bendDirection = ReadSByte(input);
|
|
||||||
skeletonData.ikConstraints.Add(ikConstraintData);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Transform constraints.
|
|
||||||
for (int i = 0, n = ReadVarint(input, true); i < n; i++) {
|
|
||||||
TransformConstraintData transformConstraintData = new TransformConstraintData(ReadString(input));
|
|
||||||
transformConstraintData.bone = skeletonData.bones.Items[ReadVarint(input, true)];
|
|
||||||
transformConstraintData.target = skeletonData.bones.Items[ReadVarint(input, true)];
|
|
||||||
transformConstraintData.offsetRotation = ReadFloat(input);
|
|
||||||
transformConstraintData.offsetX = ReadFloat(input) * scale;
|
|
||||||
transformConstraintData.offsetY = ReadFloat(input) * scale;
|
|
||||||
transformConstraintData.offsetScaleX = ReadFloat(input);
|
|
||||||
transformConstraintData.offsetScaleY = ReadFloat(input);
|
|
||||||
transformConstraintData.offsetShearY = ReadFloat(input);
|
|
||||||
transformConstraintData.rotateMix = ReadFloat(input);
|
|
||||||
transformConstraintData.translateMix = ReadFloat(input);
|
|
||||||
transformConstraintData.scaleMix = ReadFloat(input);
|
|
||||||
transformConstraintData.shearMix = ReadFloat(input);
|
|
||||||
skeletonData.transformConstraints.Add(transformConstraintData);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Slots.
|
// Slots.
|
||||||
for (int i = 0, n = ReadVarint(input, true); i < n; i++) {
|
for (int i = 0, n = ReadVarint(input, true); i < n; i++) {
|
||||||
String slotName = ReadString(input);
|
String slotName = ReadString(input);
|
||||||
BoneData boneData = skeletonData.bones.Items[ReadVarint(input, true)];
|
BoneData boneData = skeletonData.bones.Items[ReadVarint(input, true)];
|
||||||
SlotData slotData = new SlotData(slotName, boneData);
|
SlotData slotData = new SlotData(i, slotName, boneData);
|
||||||
int color = ReadInt(input);
|
int color = ReadInt(input);
|
||||||
slotData.r = ((color & 0xff000000) >> 24) / 255f;
|
slotData.r = ((color & 0xff000000) >> 24) / 255f;
|
||||||
slotData.g = ((color & 0x00ff0000) >> 16) / 255f;
|
slotData.g = ((color & 0x00ff0000) >> 16) / 255f;
|
||||||
@ -181,6 +157,55 @@ namespace Spine {
|
|||||||
skeletonData.slots.Add(slotData);
|
skeletonData.slots.Add(slotData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IK constraints.
|
||||||
|
for (int i = 0, n = ReadVarint(input, true); i < n; i++) {
|
||||||
|
IkConstraintData data = new IkConstraintData(ReadString(input));
|
||||||
|
for (int ii = 0, nn = ReadVarint(input, true); ii < nn; ii++)
|
||||||
|
data.bones.Add(skeletonData.bones.Items[ReadVarint(input, true)]);
|
||||||
|
data.target = skeletonData.bones.Items[ReadVarint(input, true)];
|
||||||
|
data.mix = ReadFloat(input);
|
||||||
|
data.bendDirection = ReadSByte(input);
|
||||||
|
skeletonData.ikConstraints.Add(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transform constraints.
|
||||||
|
for (int i = 0, n = ReadVarint(input, true); i < n; i++) {
|
||||||
|
TransformConstraintData data = new TransformConstraintData(ReadString(input));
|
||||||
|
for (int ii = 0, nn = ReadVarint(input, true); ii < nn; ii++)
|
||||||
|
data.bones.Add(skeletonData.bones.Items[ReadVarint(input, true)]);
|
||||||
|
data.target = skeletonData.bones.Items[ReadVarint(input, true)];
|
||||||
|
data.offsetRotation = ReadFloat(input);
|
||||||
|
data.offsetX = ReadFloat(input) * scale;
|
||||||
|
data.offsetY = ReadFloat(input) * scale;
|
||||||
|
data.offsetScaleX = ReadFloat(input);
|
||||||
|
data.offsetScaleY = ReadFloat(input);
|
||||||
|
data.offsetShearY = ReadFloat(input);
|
||||||
|
data.rotateMix = ReadFloat(input);
|
||||||
|
data.translateMix = ReadFloat(input);
|
||||||
|
data.scaleMix = ReadFloat(input);
|
||||||
|
data.shearMix = ReadFloat(input);
|
||||||
|
skeletonData.transformConstraints.Add(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Path constraints
|
||||||
|
for (int i = 0, n = ReadVarint(input, true); i < n; i++) {
|
||||||
|
PathConstraintData data = new PathConstraintData(ReadString(input));
|
||||||
|
for (int ii = 0, nn = ReadVarint(input, true); ii < nn; ii++)
|
||||||
|
data.bones.Add(skeletonData.bones.Items[ReadVarint(input, true)]);
|
||||||
|
data.target = skeletonData.slots.Items[ReadVarint(input, true)];
|
||||||
|
data.positionMode = (PositionMode)Enum.GetValues(typeof(PositionMode)).GetValue(ReadVarint(input, true));
|
||||||
|
data.spacingMode = (SpacingMode)Enum.GetValues(typeof(SpacingMode)).GetValue(ReadVarint(input, true));
|
||||||
|
data.rotateMode = (RotateMode)Enum.GetValues(typeof(RotateMode)).GetValue(ReadVarint(input, true));
|
||||||
|
data.offsetRotation = ReadFloat(input);
|
||||||
|
data.position = ReadFloat(input);
|
||||||
|
if (data.positionMode == PositionMode.Fixed) data.position *= scale;
|
||||||
|
data.spacing = ReadFloat(input);
|
||||||
|
if (data.spacingMode == SpacingMode.Length || data.spacingMode == SpacingMode.Fixed) data.spacing *= scale;
|
||||||
|
data.rotateMix = ReadFloat(input);
|
||||||
|
data.translateMix = ReadFloat(input);
|
||||||
|
skeletonData.pathConstraints.Add(data);
|
||||||
|
}
|
||||||
|
|
||||||
// Default skin.
|
// Default skin.
|
||||||
Skin defaultSkin = ReadSkin(input, "default", nonessential);
|
Skin defaultSkin = ReadSkin(input, "default", nonessential);
|
||||||
if (defaultSkin != null) {
|
if (defaultSkin != null) {
|
||||||
@ -199,25 +224,18 @@ namespace Spine {
|
|||||||
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);
|
||||||
if (parent == null) throw new Exception("Parent mesh not found: " + linkedMesh.parent);
|
if (parent == null) throw new Exception("Parent mesh not found: " + linkedMesh.parent);
|
||||||
if (linkedMesh.mesh is MeshAttachment) {
|
linkedMesh.mesh.ParentMesh = (MeshAttachment)parent;
|
||||||
MeshAttachment mesh = (MeshAttachment)linkedMesh.mesh;
|
linkedMesh.mesh.UpdateUVs();
|
||||||
mesh.ParentMesh = (MeshAttachment)parent;
|
|
||||||
mesh.UpdateUVs();
|
|
||||||
} else {
|
|
||||||
WeightedMeshAttachment mesh = (WeightedMeshAttachment)linkedMesh.mesh;
|
|
||||||
mesh.ParentMesh = (WeightedMeshAttachment)parent;
|
|
||||||
mesh.UpdateUVs();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
linkedMeshes.Clear();
|
linkedMeshes.Clear();
|
||||||
|
|
||||||
// Events.
|
// Events.
|
||||||
for (int i = 0, n = ReadVarint(input, true); i < n; i++) {
|
for (int i = 0, n = ReadVarint(input, true); i < n; i++) {
|
||||||
EventData eventData = new EventData(ReadString(input));
|
EventData data = new EventData(ReadString(input));
|
||||||
eventData.Int = ReadVarint(input, false);
|
data.Int = ReadVarint(input, false);
|
||||||
eventData.Float = ReadFloat(input);
|
data.Float = ReadFloat(input);
|
||||||
eventData.String = ReadString(input);
|
data.String = ReadString(input);
|
||||||
skeletonData.events.Add(eventData);
|
skeletonData.events.Add(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Animations.
|
// Animations.
|
||||||
@ -230,10 +248,12 @@ namespace Spine {
|
|||||||
skeletonData.events.TrimExcess();
|
skeletonData.events.TrimExcess();
|
||||||
skeletonData.animations.TrimExcess();
|
skeletonData.animations.TrimExcess();
|
||||||
skeletonData.ikConstraints.TrimExcess();
|
skeletonData.ikConstraints.TrimExcess();
|
||||||
|
skeletonData.pathConstraints.TrimExcess();
|
||||||
return skeletonData;
|
return skeletonData;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @return May be null. */
|
|
||||||
|
/// <returns>May be null.</returns>
|
||||||
private Skin ReadSkin (Stream input, String skinName, bool nonessential) {
|
private Skin ReadSkin (Stream input, String skinName, bool nonessential) {
|
||||||
int slotCount = ReadVarint(input, true);
|
int slotCount = ReadVarint(input, true);
|
||||||
if (slotCount == 0) return null;
|
if (slotCount == 0) return null;
|
||||||
@ -256,7 +276,7 @@ namespace Spine {
|
|||||||
|
|
||||||
AttachmentType type = (AttachmentType)input.ReadByte();
|
AttachmentType type = (AttachmentType)input.ReadByte();
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case AttachmentType.region: {
|
case AttachmentType.Region: {
|
||||||
String path = ReadString(input);
|
String path = ReadString(input);
|
||||||
float rotation = ReadFloat(input);
|
float rotation = ReadFloat(input);
|
||||||
float x = ReadFloat(input);
|
float x = ReadFloat(input);
|
||||||
@ -285,96 +305,25 @@ namespace Spine {
|
|||||||
region.UpdateOffset();
|
region.UpdateOffset();
|
||||||
return region;
|
return region;
|
||||||
}
|
}
|
||||||
case AttachmentType.boundingbox: {
|
case AttachmentType.Boundingbox: {
|
||||||
float[] vertices = ReadFloatArray(input, ReadVarint(input, true) * 2, scale);
|
int vertexCount = ReadVarint(input, true);
|
||||||
|
Vertices vertices = ReadVertices(input, vertexCount);
|
||||||
|
if (nonessential) ReadInt(input); //int color = nonessential ? ReadInt(input) : 0; // Avoid unused local warning.
|
||||||
|
|
||||||
BoundingBoxAttachment box = attachmentLoader.NewBoundingBoxAttachment(skin, name);
|
BoundingBoxAttachment box = attachmentLoader.NewBoundingBoxAttachment(skin, name);
|
||||||
if (box == null) return null;
|
if (box == null) return null;
|
||||||
box.vertices = vertices;
|
box.worldVerticesLength = vertexCount << 1;
|
||||||
|
box.vertices = vertices.vertices;
|
||||||
|
box.bones = vertices.bones;
|
||||||
return box;
|
return box;
|
||||||
}
|
}
|
||||||
case AttachmentType.mesh: {
|
case AttachmentType.Mesh: {
|
||||||
String path = ReadString(input);
|
|
||||||
int color = ReadInt(input);
|
|
||||||
int hullLength = 0;
|
|
||||||
int verticesLength = ReadVarint(input, true) * 2;
|
|
||||||
float[] uvs = ReadFloatArray(input, verticesLength, 1);
|
|
||||||
int[] triangles = ReadShortArray(input);
|
|
||||||
float[] vertices = ReadFloatArray(input, verticesLength, scale);
|
|
||||||
hullLength = ReadVarint(input, true);
|
|
||||||
int[] edges = null;
|
|
||||||
float width = 0, height = 0;
|
|
||||||
if (nonessential) {
|
|
||||||
edges = ReadShortArray(input);
|
|
||||||
width = ReadFloat(input);
|
|
||||||
height = ReadFloat(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (path == null) path = name;
|
|
||||||
MeshAttachment mesh = attachmentLoader.NewMeshAttachment(skin, name, path);
|
|
||||||
if (mesh == null) return null;
|
|
||||||
mesh.Path = path;
|
|
||||||
mesh.r = ((color & 0xff000000) >> 24) / 255f;
|
|
||||||
mesh.g = ((color & 0x00ff0000) >> 16) / 255f;
|
|
||||||
mesh.b = ((color & 0x0000ff00) >> 8) / 255f;
|
|
||||||
mesh.a = ((color & 0x000000ff)) / 255f;
|
|
||||||
mesh.vertices = vertices;
|
|
||||||
mesh.triangles = triangles;
|
|
||||||
mesh.regionUVs = uvs;
|
|
||||||
mesh.UpdateUVs();
|
|
||||||
mesh.HullLength = hullLength;
|
|
||||||
if (nonessential) {
|
|
||||||
mesh.Edges = edges;
|
|
||||||
mesh.Width = width * scale;
|
|
||||||
mesh.Height = height * scale;
|
|
||||||
}
|
|
||||||
return mesh;
|
|
||||||
}
|
|
||||||
case AttachmentType.linkedmesh: {
|
|
||||||
String path = ReadString(input);
|
|
||||||
int color = ReadInt(input);
|
|
||||||
String skinName = ReadString(input);
|
|
||||||
String parent = ReadString(input);
|
|
||||||
bool inheritFFD = ReadBoolean(input);
|
|
||||||
float width = 0, height = 0;
|
|
||||||
if (nonessential) {
|
|
||||||
width = ReadFloat(input);
|
|
||||||
height = ReadFloat(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (path == null) path = name;
|
|
||||||
MeshAttachment mesh = attachmentLoader.NewMeshAttachment(skin, name, path);
|
|
||||||
if (mesh == null) return null;
|
|
||||||
mesh.Path = path;
|
|
||||||
mesh.r = ((color & 0xff000000) >> 24) / 255f;
|
|
||||||
mesh.g = ((color & 0x00ff0000) >> 16) / 255f;
|
|
||||||
mesh.b = ((color & 0x0000ff00) >> 8) / 255f;
|
|
||||||
mesh.a = ((color & 0x000000ff)) / 255f;
|
|
||||||
mesh.inheritFFD = inheritFFD;
|
|
||||||
if (nonessential) {
|
|
||||||
mesh.Width = width * scale;
|
|
||||||
mesh.Height = height * scale;
|
|
||||||
}
|
|
||||||
linkedMeshes.Add(new SkeletonJson.LinkedMesh(mesh, skinName, slotIndex, parent));
|
|
||||||
return mesh;
|
|
||||||
}
|
|
||||||
case AttachmentType.weightedmesh: {
|
|
||||||
String path = ReadString(input);
|
String path = ReadString(input);
|
||||||
int color = ReadInt(input);
|
int color = ReadInt(input);
|
||||||
int vertexCount = ReadVarint(input, true);
|
int vertexCount = ReadVarint(input, true);
|
||||||
float[] uvs = ReadFloatArray(input, vertexCount * 2, 1);
|
float[] uvs = ReadFloatArray(input, vertexCount << 1, 1);
|
||||||
int[] triangles = ReadShortArray(input);
|
int[] triangles = ReadShortArray(input);
|
||||||
var weights = new List<float>(uvs.Length * 3 * 3);
|
Vertices vertices = ReadVertices(input, vertexCount);
|
||||||
var bones = new List<int>(uvs.Length * 3);
|
|
||||||
for (int i = 0; i < vertexCount; i++) {
|
|
||||||
int boneCount = (int)ReadFloat(input);
|
|
||||||
bones.Add(boneCount);
|
|
||||||
for (int ii = 0; ii < boneCount; ii++) {
|
|
||||||
bones.Add((int)ReadFloat(input));
|
|
||||||
weights.Add(ReadFloat(input) * scale);
|
|
||||||
weights.Add(ReadFloat(input) * scale);
|
|
||||||
weights.Add(ReadFloat(input));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
int hullLength = ReadVarint(input, true);
|
int hullLength = ReadVarint(input, true);
|
||||||
int[] edges = null;
|
int[] edges = null;
|
||||||
float width = 0, height = 0;
|
float width = 0, height = 0;
|
||||||
@ -385,33 +334,33 @@ namespace Spine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (path == null) path = name;
|
if (path == null) path = name;
|
||||||
WeightedMeshAttachment mesh = attachmentLoader.NewWeightedMeshAttachment(skin, name, path);
|
MeshAttachment mesh = attachmentLoader.NewMeshAttachment(skin, name, path);
|
||||||
if (mesh == null) return null;
|
if (mesh == null) return null;
|
||||||
mesh.Path = path;
|
mesh.Path = path;
|
||||||
mesh.r = ((color & 0xff000000) >> 24) / 255f;
|
mesh.r = ((color & 0xff000000) >> 24) / 255f;
|
||||||
mesh.g = ((color & 0x00ff0000) >> 16) / 255f;
|
mesh.g = ((color & 0x00ff0000) >> 16) / 255f;
|
||||||
mesh.b = ((color & 0x0000ff00) >> 8) / 255f;
|
mesh.b = ((color & 0x0000ff00) >> 8) / 255f;
|
||||||
mesh.a = ((color & 0x000000ff)) / 255f;
|
mesh.a = ((color & 0x000000ff)) / 255f;
|
||||||
mesh.bones = bones.ToArray();
|
mesh.bones = vertices.bones;
|
||||||
mesh.weights = weights.ToArray();
|
mesh.vertices = vertices.vertices;
|
||||||
|
mesh.WorldVerticesLength = vertexCount << 1;
|
||||||
mesh.triangles = triangles;
|
mesh.triangles = triangles;
|
||||||
mesh.regionUVs = uvs;
|
mesh.regionUVs = uvs;
|
||||||
mesh.UpdateUVs();
|
mesh.UpdateUVs();
|
||||||
mesh.HullLength = hullLength * 2;
|
mesh.HullLength = hullLength << 1;
|
||||||
if (nonessential) {
|
if (nonessential) {
|
||||||
mesh.Edges = edges;
|
mesh.Edges = edges;
|
||||||
mesh.Width = width * scale;
|
mesh.Width = width * scale;
|
||||||
mesh.Height = height * scale;
|
mesh.Height = height * scale;
|
||||||
}
|
}
|
||||||
//
|
|
||||||
return mesh;
|
return mesh;
|
||||||
}
|
}
|
||||||
case AttachmentType.weightedlinkedmesh: {
|
case AttachmentType.Linkedmesh: {
|
||||||
String path = ReadString(input);
|
String path = ReadString(input);
|
||||||
int color = ReadInt(input);
|
int color = ReadInt(input);
|
||||||
String skinName = ReadString(input);
|
String skinName = ReadString(input);
|
||||||
String parent = ReadString(input);
|
String parent = ReadString(input);
|
||||||
bool inheritFFD = ReadBoolean(input);
|
bool inheritDeform = ReadBoolean(input);
|
||||||
float width = 0, height = 0;
|
float width = 0, height = 0;
|
||||||
if (nonessential) {
|
if (nonessential) {
|
||||||
width = ReadFloat(input);
|
width = ReadFloat(input);
|
||||||
@ -419,14 +368,14 @@ namespace Spine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (path == null) path = name;
|
if (path == null) path = name;
|
||||||
WeightedMeshAttachment mesh = attachmentLoader.NewWeightedMeshAttachment(skin, name, path);
|
MeshAttachment mesh = attachmentLoader.NewMeshAttachment(skin, name, path);
|
||||||
if (mesh == null) return null;
|
if (mesh == null) return null;
|
||||||
mesh.Path = path;
|
mesh.Path = path;
|
||||||
mesh.r = ((color & 0xff000000) >> 24) / 255f;
|
mesh.r = ((color & 0xff000000) >> 24) / 255f;
|
||||||
mesh.g = ((color & 0x00ff0000) >> 16) / 255f;
|
mesh.g = ((color & 0x00ff0000) >> 16) / 255f;
|
||||||
mesh.b = ((color & 0x0000ff00) >> 8) / 255f;
|
mesh.b = ((color & 0x0000ff00) >> 8) / 255f;
|
||||||
mesh.a = ((color & 0x000000ff)) / 255f;
|
mesh.a = ((color & 0x000000ff)) / 255f;
|
||||||
mesh.inheritFFD = inheritFFD;
|
mesh.inheritDeform = inheritDeform;
|
||||||
if (nonessential) {
|
if (nonessential) {
|
||||||
mesh.Width = width * scale;
|
mesh.Width = width * scale;
|
||||||
mesh.Height = height * scale;
|
mesh.Height = height * scale;
|
||||||
@ -434,10 +383,56 @@ namespace Spine {
|
|||||||
linkedMeshes.Add(new SkeletonJson.LinkedMesh(mesh, skinName, slotIndex, parent));
|
linkedMeshes.Add(new SkeletonJson.LinkedMesh(mesh, skinName, slotIndex, parent));
|
||||||
return mesh;
|
return mesh;
|
||||||
}
|
}
|
||||||
|
case AttachmentType.Path: {
|
||||||
|
bool closed = ReadBoolean(input);
|
||||||
|
bool constantSpeed = ReadBoolean(input);
|
||||||
|
int vertexCount = ReadVarint(input, true);
|
||||||
|
Vertices vertices = ReadVertices(input, vertexCount);
|
||||||
|
float[] lengths = new float[vertexCount / 3];
|
||||||
|
for (int i = 0, n = lengths.Length; i < n; i++)
|
||||||
|
lengths[i] = ReadFloat(input) * scale;
|
||||||
|
if (nonessential) ReadInt(input); //int color = nonessential ? ReadInt(input) : 0; // Avoid unused local warning.
|
||||||
|
|
||||||
|
PathAttachment path = attachmentLoader.NewPathAttachment(skin, name);
|
||||||
|
if (path == null) return null;
|
||||||
|
path.closed = closed;
|
||||||
|
path.constantSpeed = constantSpeed;
|
||||||
|
path.worldVerticesLength = vertexCount << 1;
|
||||||
|
path.vertices = vertices.vertices;
|
||||||
|
path.bones = vertices.bones;
|
||||||
|
path.lengths = lengths;
|
||||||
|
return path;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Vertices ReadVertices (Stream input, int vertexCount) {
|
||||||
|
float scale = Scale;
|
||||||
|
int verticesLength = vertexCount << 1;
|
||||||
|
Vertices vertices = new Vertices();
|
||||||
|
if(!ReadBoolean(input)) {
|
||||||
|
vertices.vertices = ReadFloatArray(input, verticesLength, scale);
|
||||||
|
return vertices;
|
||||||
|
}
|
||||||
|
var weights = new ExposedList<float>(verticesLength * 3 * 3);
|
||||||
|
var bonesArray = new ExposedList<int>(verticesLength * 3);
|
||||||
|
for (int i = 0; i < vertexCount; i++) {
|
||||||
|
int boneCount = ReadVarint(input, true);
|
||||||
|
bonesArray.Add(boneCount);
|
||||||
|
for (int ii = 0; ii < boneCount; ii++) {
|
||||||
|
bonesArray.Add(ReadVarint(input, true));
|
||||||
|
weights.Add(ReadFloat(input) * scale);
|
||||||
|
weights.Add(ReadFloat(input) * scale);
|
||||||
|
weights.Add(ReadFloat(input));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vertices.vertices = weights.ToArray();
|
||||||
|
vertices.bones = bonesArray.ToArray();
|
||||||
|
return vertices;
|
||||||
|
}
|
||||||
|
|
||||||
private float[] ReadFloatArray (Stream input, int n, float scale) {
|
private float[] ReadFloatArray (Stream input, int n, float scale) {
|
||||||
float[] array = new float[n];
|
float[] array = new float[n];
|
||||||
if (scale == 1) {
|
if (scale == 1) {
|
||||||
@ -470,7 +465,7 @@ namespace Spine {
|
|||||||
int timelineType = input.ReadByte();
|
int timelineType = input.ReadByte();
|
||||||
int frameCount = ReadVarint(input, true);
|
int frameCount = ReadVarint(input, true);
|
||||||
switch (timelineType) {
|
switch (timelineType) {
|
||||||
case TIMELINE_COLOR: {
|
case SLOT_COLOR: {
|
||||||
ColorTimeline timeline = new ColorTimeline(frameCount);
|
ColorTimeline timeline = new ColorTimeline(frameCount);
|
||||||
timeline.slotIndex = slotIndex;
|
timeline.slotIndex = slotIndex;
|
||||||
for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) {
|
for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) {
|
||||||
@ -484,10 +479,10 @@ namespace Spine {
|
|||||||
if (frameIndex < frameCount - 1) ReadCurve(input, frameIndex, timeline);
|
if (frameIndex < frameCount - 1) ReadCurve(input, frameIndex, timeline);
|
||||||
}
|
}
|
||||||
timelines.Add(timeline);
|
timelines.Add(timeline);
|
||||||
duration = Math.Max(duration, timeline.frames[frameCount * 5 - 5]);
|
duration = Math.Max(duration, timeline.frames[(timeline.FrameCount - 1) * ColorTimeline.ENTRIES]);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case TIMELINE_ATTACHMENT: {
|
case SLOT_ATTACHMENT: {
|
||||||
AttachmentTimeline timeline = new AttachmentTimeline(frameCount);
|
AttachmentTimeline timeline = new AttachmentTimeline(frameCount);
|
||||||
timeline.slotIndex = slotIndex;
|
timeline.slotIndex = slotIndex;
|
||||||
for (int frameIndex = 0; frameIndex < frameCount; frameIndex++)
|
for (int frameIndex = 0; frameIndex < frameCount; frameIndex++)
|
||||||
@ -507,7 +502,7 @@ namespace Spine {
|
|||||||
int timelineType = input.ReadByte();
|
int timelineType = input.ReadByte();
|
||||||
int frameCount = ReadVarint(input, true);
|
int frameCount = ReadVarint(input, true);
|
||||||
switch (timelineType) {
|
switch (timelineType) {
|
||||||
case TIMELINE_ROTATE: {
|
case BONE_ROTATE: {
|
||||||
RotateTimeline timeline = new RotateTimeline(frameCount);
|
RotateTimeline timeline = new RotateTimeline(frameCount);
|
||||||
timeline.boneIndex = boneIndex;
|
timeline.boneIndex = boneIndex;
|
||||||
for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) {
|
for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) {
|
||||||
@ -515,17 +510,17 @@ namespace Spine {
|
|||||||
if (frameIndex < frameCount - 1) ReadCurve(input, frameIndex, timeline);
|
if (frameIndex < frameCount - 1) ReadCurve(input, frameIndex, timeline);
|
||||||
}
|
}
|
||||||
timelines.Add(timeline);
|
timelines.Add(timeline);
|
||||||
duration = Math.Max(duration, timeline.frames[frameCount * 2 - 2]);
|
duration = Math.Max(duration, timeline.frames[(frameCount - 1) * RotateTimeline.ENTRIES]);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case TIMELINE_TRANSLATE:
|
case BONE_TRANSLATE:
|
||||||
case TIMELINE_SCALE:
|
case BONE_SCALE:
|
||||||
case TIMELINE_SHEAR: {
|
case BONE_SHEAR: {
|
||||||
TranslateTimeline timeline;
|
TranslateTimeline timeline;
|
||||||
float timelineScale = 1;
|
float timelineScale = 1;
|
||||||
if (timelineType == TIMELINE_SCALE)
|
if (timelineType == BONE_SCALE)
|
||||||
timeline = new ScaleTimeline(frameCount);
|
timeline = new ScaleTimeline(frameCount);
|
||||||
else if (timelineType == TIMELINE_SHEAR)
|
else if (timelineType == BONE_SHEAR)
|
||||||
timeline = new ShearTimeline(frameCount);
|
timeline = new ShearTimeline(frameCount);
|
||||||
else {
|
else {
|
||||||
timeline = new TranslateTimeline(frameCount);
|
timeline = new TranslateTimeline(frameCount);
|
||||||
@ -538,7 +533,7 @@ namespace Spine {
|
|||||||
if (frameIndex < frameCount - 1) ReadCurve(input, frameIndex, timeline);
|
if (frameIndex < frameCount - 1) ReadCurve(input, frameIndex, timeline);
|
||||||
}
|
}
|
||||||
timelines.Add(timeline);
|
timelines.Add(timeline);
|
||||||
duration = Math.Max(duration, timeline.frames[frameCount * 3 - 3]);
|
duration = Math.Max(duration, timeline.frames[(frameCount - 1) * TranslateTimeline.ENTRIES]);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -547,78 +542,115 @@ namespace Spine {
|
|||||||
|
|
||||||
// IK timelines.
|
// IK timelines.
|
||||||
for (int i = 0, n = ReadVarint(input, true); i < n; i++) {
|
for (int i = 0, n = ReadVarint(input, true); i < n; i++) {
|
||||||
IkConstraintData constraint = skeletonData.ikConstraints.Items[ReadVarint(input, true)];
|
int index = ReadVarint(input, true);
|
||||||
int frameCount = ReadVarint(input, true);
|
int frameCount = ReadVarint(input, true);
|
||||||
IkConstraintTimeline timeline = new IkConstraintTimeline(frameCount);
|
IkConstraintTimeline timeline = new IkConstraintTimeline(frameCount);
|
||||||
timeline.ikConstraintIndex = skeletonData.ikConstraints.IndexOf(constraint);
|
timeline.ikConstraintIndex = index;
|
||||||
for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) {
|
for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) {
|
||||||
timeline.SetFrame(frameIndex, ReadFloat(input), ReadFloat(input), ReadSByte(input));
|
timeline.SetFrame(frameIndex, ReadFloat(input), ReadFloat(input), ReadSByte(input));
|
||||||
if (frameIndex < frameCount - 1) ReadCurve(input, frameIndex, timeline);
|
if (frameIndex < frameCount - 1) ReadCurve(input, frameIndex, timeline);
|
||||||
}
|
}
|
||||||
timelines.Add(timeline);
|
timelines.Add(timeline);
|
||||||
duration = Math.Max(duration, timeline.frames[frameCount * 3 - 3]);
|
duration = Math.Max(duration, timeline.frames[(frameCount - 1) * IkConstraintTimeline.ENTRIES]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transform constraint timelines.
|
// Transform constraint timelines.
|
||||||
for (int i = 0, n = ReadVarint(input, true); i < n; i++) {
|
for (int i = 0, n = ReadVarint(input, true); i < n; i++) {
|
||||||
TransformConstraintData constraint = skeletonData.transformConstraints.Items[ReadVarint(input, true)];
|
int index = ReadVarint(input, true);
|
||||||
int frameCount = ReadVarint(input, true);
|
int frameCount = ReadVarint(input, true);
|
||||||
TransformConstraintTimeline timeline = new TransformConstraintTimeline(frameCount);
|
TransformConstraintTimeline timeline = new TransformConstraintTimeline(frameCount);
|
||||||
timeline.transformConstraintIndex = skeletonData.transformConstraints.IndexOf(constraint);
|
timeline.transformConstraintIndex = index;
|
||||||
for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) {
|
for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) {
|
||||||
timeline.SetFrame(frameIndex, ReadFloat(input), ReadFloat(input), ReadFloat(input), ReadFloat(input), ReadFloat(input));
|
timeline.SetFrame(frameIndex, ReadFloat(input), ReadFloat(input), ReadFloat(input), ReadFloat(input), ReadFloat(input));
|
||||||
if (frameIndex < frameCount - 1) ReadCurve(input, frameIndex, timeline);
|
if (frameIndex < frameCount - 1) ReadCurve(input, frameIndex, timeline);
|
||||||
}
|
}
|
||||||
timelines.Add(timeline);
|
timelines.Add(timeline);
|
||||||
duration = Math.Max(duration, timeline.frames[frameCount * 5 - 5]);
|
duration = Math.Max(duration, timeline.frames[(frameCount - 1) * TransformConstraintTimeline.ENTRIES]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// FFD timelines.
|
// Path constraint timelines.
|
||||||
|
for (int i = 0, n = ReadVarint(input, true); i < n; i++) {
|
||||||
|
int index = ReadVarint(input, true);
|
||||||
|
PathConstraintData data = skeletonData.pathConstraints.Items[index];
|
||||||
|
for (int ii = 0, nn = ReadVarint(input, true); ii < nn; ii++) {
|
||||||
|
int timelineType = ReadSByte(input);
|
||||||
|
int frameCount = ReadVarint(input, true);
|
||||||
|
switch(timelineType) {
|
||||||
|
case PATH_POSITION:
|
||||||
|
case PATH_SPACING: {
|
||||||
|
PathConstraintPositionTimeline timeline;
|
||||||
|
float timelineScale = 1;
|
||||||
|
if (timelineType == PATH_SPACING) {
|
||||||
|
timeline = new PathConstraintSpacingTimeline(frameCount);
|
||||||
|
if (data.spacingMode == SpacingMode.Length || data.spacingMode == SpacingMode.Fixed) timelineScale = scale;
|
||||||
|
} else {
|
||||||
|
timeline = new PathConstraintPositionTimeline(frameCount);
|
||||||
|
if (data.positionMode == PositionMode.Fixed) timelineScale = scale;
|
||||||
|
}
|
||||||
|
timeline.pathConstraintIndex = index;
|
||||||
|
for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) {
|
||||||
|
timeline.SetFrame(frameIndex, ReadFloat(input), ReadFloat(input) * timelineScale);
|
||||||
|
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, ReadFloat(input), ReadFloat(input), ReadFloat(input));
|
||||||
|
if (frameIndex < frameCount - 1) ReadCurve(input, frameIndex, timeline);
|
||||||
|
}
|
||||||
|
timelines.Add(timeline);
|
||||||
|
duration = Math.Max(duration, timeline.frames[(frameCount - 1) * PathConstraintMixTimeline.ENTRIES]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deform timelines.
|
||||||
for (int i = 0, n = ReadVarint(input, true); i < n; i++) {
|
for (int i = 0, n = ReadVarint(input, true); i < n; i++) {
|
||||||
Skin skin = skeletonData.skins.Items[ReadVarint(input, true)];
|
Skin skin = skeletonData.skins.Items[ReadVarint(input, true)];
|
||||||
for (int ii = 0, nn = ReadVarint(input, true); ii < nn; ii++) {
|
for (int ii = 0, nn = ReadVarint(input, true); ii < nn; ii++) {
|
||||||
int slotIndex = ReadVarint(input, true);
|
int slotIndex = ReadVarint(input, true);
|
||||||
for (int iii = 0, nnn = ReadVarint(input, true); iii < nnn; iii++) {
|
for (int iii = 0, nnn = ReadVarint(input, true); iii < nnn; iii++) {
|
||||||
Attachment attachment = skin.GetAttachment(slotIndex, ReadString(input));
|
VertexAttachment attachment = (VertexAttachment)skin.GetAttachment(slotIndex, ReadString(input));
|
||||||
|
bool weighted = attachment.bones != null;
|
||||||
|
float[] vertices = attachment.vertices;
|
||||||
|
int deformLength = weighted ? vertices.Length / 3 * 2 : vertices.Length;
|
||||||
|
|
||||||
int frameCount = ReadVarint(input, true);
|
int frameCount = ReadVarint(input, true);
|
||||||
FfdTimeline timeline = new FfdTimeline(frameCount);
|
DeformTimeline timeline = new DeformTimeline(frameCount);
|
||||||
timeline.slotIndex = slotIndex;
|
timeline.slotIndex = slotIndex;
|
||||||
timeline.attachment = attachment;
|
timeline.attachment = attachment;
|
||||||
|
|
||||||
for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) {
|
for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) {
|
||||||
float time = ReadFloat(input);
|
float time = ReadFloat(input);
|
||||||
|
float[] deform;
|
||||||
float[] vertices;
|
|
||||||
int vertexCount;
|
|
||||||
if (attachment is MeshAttachment)
|
|
||||||
vertexCount = ((MeshAttachment)attachment).vertices.Length;
|
|
||||||
else
|
|
||||||
vertexCount = ((WeightedMeshAttachment)attachment).weights.Length / 3 * 2;
|
|
||||||
|
|
||||||
int end = ReadVarint(input, true);
|
int end = ReadVarint(input, true);
|
||||||
if (end == 0) {
|
if (end == 0)
|
||||||
if (attachment is MeshAttachment)
|
deform = weighted ? new float[deformLength] : vertices;
|
||||||
vertices = ((MeshAttachment)attachment).vertices;
|
else {
|
||||||
else
|
deform = new float[deformLength];
|
||||||
vertices = new float[vertexCount];
|
|
||||||
} else {
|
|
||||||
vertices = new float[vertexCount];
|
|
||||||
int start = ReadVarint(input, true);
|
int start = ReadVarint(input, true);
|
||||||
end += start;
|
end += start;
|
||||||
if (scale == 1) {
|
if (scale == 1) {
|
||||||
for (int v = start; v < end; v++)
|
for (int v = start; v < end; v++)
|
||||||
vertices[v] = ReadFloat(input);
|
deform[v] = ReadFloat(input);
|
||||||
} else {
|
} else {
|
||||||
for (int v = start; v < end; v++)
|
for (int v = start; v < end; v++)
|
||||||
vertices[v] = ReadFloat(input) * scale;
|
deform[v] = ReadFloat(input) * scale;
|
||||||
}
|
}
|
||||||
if (attachment is MeshAttachment) {
|
if (!weighted) {
|
||||||
float[] meshVertices = ((MeshAttachment)attachment).vertices;
|
for (int v = 0, vn = deform.Length; v < vn; v++)
|
||||||
for (int v = 0, vn = vertices.Length; v < vn; v++)
|
deform[v] += vertices[v];
|
||||||
vertices[v] += meshVertices[v];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
timeline.SetFrame(frameIndex, time, vertices);
|
timeline.SetFrame(frameIndex, time, deform);
|
||||||
if (frameIndex < frameCount - 1) ReadCurve(input, frameIndex, timeline);
|
if (frameIndex < frameCount - 1) ReadCurve(input, frameIndex, timeline);
|
||||||
}
|
}
|
||||||
timelines.Add(timeline);
|
timelines.Add(timeline);
|
||||||
@ -756,5 +788,10 @@ namespace Spine {
|
|||||||
length -= count;
|
length -= count;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal class Vertices {
|
||||||
|
public int[] bones;
|
||||||
|
public float[] vertices;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -30,7 +30,6 @@
|
|||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace Spine {
|
namespace Spine {
|
||||||
public class SkeletonBounds {
|
public class SkeletonBounds {
|
||||||
@ -80,7 +79,7 @@ namespace Spine {
|
|||||||
int count = boundingBox.Vertices.Length;
|
int count = boundingBox.Vertices.Length;
|
||||||
polygon.Count = count;
|
polygon.Count = count;
|
||||||
if (polygon.Vertices.Length < count) polygon.Vertices = new float[count];
|
if (polygon.Vertices.Length < count) polygon.Vertices = new float[count];
|
||||||
boundingBox.ComputeWorldVertices(slot.bone, polygon.Vertices);
|
boundingBox.ComputeWorldVertices(slot, polygon.Vertices);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (updateAabb) aabbCompute();
|
if (updateAabb) aabbCompute();
|
||||||
|
|||||||
@ -30,7 +30,6 @@
|
|||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace Spine {
|
namespace Spine {
|
||||||
public class SkeletonData {
|
public class SkeletonData {
|
||||||
@ -43,6 +42,7 @@ namespace Spine {
|
|||||||
internal ExposedList<Animation> animations = new ExposedList<Animation>();
|
internal ExposedList<Animation> animations = new ExposedList<Animation>();
|
||||||
internal ExposedList<IkConstraintData> ikConstraints = new ExposedList<IkConstraintData>();
|
internal ExposedList<IkConstraintData> ikConstraints = new ExposedList<IkConstraintData>();
|
||||||
internal ExposedList<TransformConstraintData> transformConstraints = new ExposedList<TransformConstraintData>();
|
internal ExposedList<TransformConstraintData> transformConstraints = new ExposedList<TransformConstraintData>();
|
||||||
|
internal ExposedList<PathConstraintData> pathConstraints = new ExposedList<PathConstraintData>();
|
||||||
internal float width, height;
|
internal float width, height;
|
||||||
internal String version, hash, imagesPath;
|
internal String version, hash, imagesPath;
|
||||||
|
|
||||||
@ -55,6 +55,8 @@ namespace Spine {
|
|||||||
public ExposedList<EventData> Events { get { return events; } set { events = value; } }
|
public ExposedList<EventData> Events { get { return events; } set { events = value; } }
|
||||||
public ExposedList<Animation> Animations { get { return animations; } set { animations = value; } }
|
public ExposedList<Animation> Animations { get { return animations; } set { animations = value; } }
|
||||||
public ExposedList<IkConstraintData> IkConstraints { get { return ikConstraints; } set { ikConstraints = value; } }
|
public ExposedList<IkConstraintData> IkConstraints { get { return ikConstraints; } set { ikConstraints = value; } }
|
||||||
|
public ExposedList<TransformConstraintData> TransformConstraints { get { return transformConstraints; } set { transformConstraints = value; } }
|
||||||
|
public ExposedList<PathConstraintData> PathConstraints { get { return pathConstraints; } set { pathConstraints = value; } }
|
||||||
public float Width { get { return width; } set { width = value; } }
|
public float Width { get { return width; } set { width = value; } }
|
||||||
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.</summary>
|
/// <summary>The Spine version used to export this data.</summary>
|
||||||
@ -65,7 +67,7 @@ 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 cannot be null.");
|
if (boneName == null) throw new ArgumentNullException("boneName", "boneName cannot be null.");
|
||||||
ExposedList<BoneData> bones = this.bones;
|
ExposedList<BoneData> bones = this.bones;
|
||||||
for (int i = 0, n = bones.Count; i < n; i++) {
|
for (int i = 0, n = bones.Count; i < n; i++) {
|
||||||
BoneData bone = bones.Items[i];
|
BoneData bone = bones.Items[i];
|
||||||
@ -76,7 +78,7 @@ 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 cannot be null.");
|
if (boneName == null) throw new ArgumentNullException("boneName", "boneName cannot be null.");
|
||||||
ExposedList<BoneData> bones = this.bones;
|
ExposedList<BoneData> bones = this.bones;
|
||||||
for (int i = 0, n = bones.Count; i < n; i++)
|
for (int i = 0, n = bones.Count; i < n; i++)
|
||||||
if (bones.Items[i].name == boneName) return i;
|
if (bones.Items[i].name == boneName) return i;
|
||||||
@ -87,7 +89,7 @@ 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 cannot be null.");
|
if (slotName == null) throw new ArgumentNullException("slotName", "slotName cannot be null.");
|
||||||
ExposedList<SlotData> slots = this.slots;
|
ExposedList<SlotData> slots = this.slots;
|
||||||
for (int i = 0, n = slots.Count; i < n; i++) {
|
for (int i = 0, n = slots.Count; i < n; i++) {
|
||||||
SlotData slot = slots.Items[i];
|
SlotData slot = slots.Items[i];
|
||||||
@ -96,9 +98,9 @@ namespace Spine {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <returns>-1 if the bone was not found.</returns>
|
/// <returns>-1 if the slot was not found.</returns>
|
||||||
public int FindSlotIndex (String slotName) {
|
public int FindSlotIndex (String slotName) {
|
||||||
if (slotName == null) throw new ArgumentNullException("slotName cannot be null.");
|
if (slotName == null) throw new ArgumentNullException("slotName", "slotName cannot be null.");
|
||||||
ExposedList<SlotData> slots = this.slots;
|
ExposedList<SlotData> slots = this.slots;
|
||||||
for (int i = 0, n = slots.Count; i < n; i++)
|
for (int i = 0, n = slots.Count; i < n; i++)
|
||||||
if (slots.Items[i].name == slotName) return i;
|
if (slots.Items[i].name == slotName) return i;
|
||||||
@ -109,7 +111,7 @@ namespace Spine {
|
|||||||
|
|
||||||
/// <returns>May be null.</returns>
|
/// <returns>May be null.</returns>
|
||||||
public Skin FindSkin (String skinName) {
|
public Skin FindSkin (String skinName) {
|
||||||
if (skinName == null) throw new ArgumentNullException("skinName cannot be null.");
|
if (skinName == null) throw new ArgumentNullException("skinName", "skinName cannot be null.");
|
||||||
foreach (Skin skin in skins)
|
foreach (Skin skin in skins)
|
||||||
if (skin.name == skinName) return skin;
|
if (skin.name == skinName) return skin;
|
||||||
return null;
|
return null;
|
||||||
@ -119,7 +121,7 @@ namespace Spine {
|
|||||||
|
|
||||||
/// <returns>May be null.</returns>
|
/// <returns>May be null.</returns>
|
||||||
public EventData FindEvent (String eventDataName) {
|
public EventData FindEvent (String eventDataName) {
|
||||||
if (eventDataName == null) throw new ArgumentNullException("eventDataName cannot be null.");
|
if (eventDataName == null) throw new ArgumentNullException("eventDataName", "eventDataName cannot be null.");
|
||||||
foreach (EventData eventData in events)
|
foreach (EventData eventData in events)
|
||||||
if (eventData.name == eventDataName) return eventData;
|
if (eventData.name == eventDataName) return eventData;
|
||||||
return null;
|
return null;
|
||||||
@ -129,7 +131,7 @@ 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 cannot be null.");
|
if (animationName == null) throw new ArgumentNullException("animationName", "animationName cannot be null.");
|
||||||
ExposedList<Animation> animations = this.animations;
|
ExposedList<Animation> animations = this.animations;
|
||||||
for (int i = 0, n = animations.Count; i < n; i++) {
|
for (int i = 0, n = animations.Count; i < n; i++) {
|
||||||
Animation animation = animations.Items[i];
|
Animation animation = animations.Items[i];
|
||||||
@ -142,7 +144,7 @@ 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 cannot be null.");
|
if (constraintName == null) throw new ArgumentNullException("constraintName", "constraintName cannot be null.");
|
||||||
ExposedList<IkConstraintData> ikConstraints = this.ikConstraints;
|
ExposedList<IkConstraintData> ikConstraints = this.ikConstraints;
|
||||||
for (int i = 0, n = ikConstraints.Count; i < n; i++) {
|
for (int i = 0, n = ikConstraints.Count; i < n; i++) {
|
||||||
IkConstraintData ikConstraint = ikConstraints.Items[i];
|
IkConstraintData ikConstraint = ikConstraints.Items[i];
|
||||||
@ -155,7 +157,7 @@ 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 cannot be null.");
|
if (constraintName == null) throw new ArgumentNullException("constraintName", "constraintName cannot be null.");
|
||||||
ExposedList<TransformConstraintData> transformConstraints = this.transformConstraints;
|
ExposedList<TransformConstraintData> transformConstraints = this.transformConstraints;
|
||||||
for (int i = 0, n = transformConstraints.Count; i < n; i++) {
|
for (int i = 0, n = transformConstraints.Count; i < n; i++) {
|
||||||
TransformConstraintData transformConstraint = transformConstraints.Items[i];
|
TransformConstraintData transformConstraint = transformConstraints.Items[i];
|
||||||
@ -164,6 +166,28 @@ namespace Spine {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- Path constraints.
|
||||||
|
|
||||||
|
/// <returns>May be null.</returns>
|
||||||
|
public PathConstraintData FindPathConstraint (String constraintName) {
|
||||||
|
if (constraintName == null) throw new ArgumentNullException("constraintName", "constraintName cannot be null.");
|
||||||
|
ExposedList<PathConstraintData> pathConstraints = this.pathConstraints;
|
||||||
|
for (int i = 0, n = pathConstraints.Count; i < n; i++) {
|
||||||
|
PathConstraintData constraint = pathConstraints.Items[i];
|
||||||
|
if (constraint.name.Equals(constraintName)) return constraint;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <returns>-1 if the path constraint was not found.</returns>
|
||||||
|
public int FindPathConstraintIndex (String pathConstraintName) {
|
||||||
|
if (pathConstraintName == null) throw new ArgumentNullException("pathConstraintName", "pathConstraintName cannot be null.");
|
||||||
|
ExposedList<PathConstraintData> pathConstraints = this.pathConstraints;
|
||||||
|
for (int i = 0, n = pathConstraints.Count; i < n; i++)
|
||||||
|
if (pathConstraints.Items[i].name.Equals(pathConstraintName)) return i;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
// ---
|
// ---
|
||||||
|
|
||||||
override public String ToString () {
|
override public String ToString () {
|
||||||
|
|||||||
@ -54,7 +54,7 @@ namespace Spine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public SkeletonJson (AttachmentLoader attachmentLoader) {
|
public SkeletonJson (AttachmentLoader attachmentLoader) {
|
||||||
if (attachmentLoader == null) throw new ArgumentNullException("attachmentLoader cannot be null.");
|
if (attachmentLoader == null) throw new ArgumentNullException("attachmentLoader", "attachmentLoader cannot be null.");
|
||||||
this.attachmentLoader = attachmentLoader;
|
this.attachmentLoader = attachmentLoader;
|
||||||
Scale = 1;
|
Scale = 1;
|
||||||
}
|
}
|
||||||
@ -79,7 +79,7 @@ namespace Spine {
|
|||||||
Stream stream = Microsoft.Xna.Framework.TitleContainer.OpenStream(path);
|
Stream stream = Microsoft.Xna.Framework.TitleContainer.OpenStream(path);
|
||||||
using (StreamReader reader = new StreamReader(stream)) {
|
using (StreamReader reader = new StreamReader(stream)) {
|
||||||
#else
|
#else
|
||||||
using (StreamReader reader = new StreamReader(path)) {
|
using (var reader = new StreamReader(path)) {
|
||||||
#endif // WINDOWS_PHONE
|
#endif // WINDOWS_PHONE
|
||||||
SkeletonData skeletonData = ReadSkeletonData(reader);
|
SkeletonData skeletonData = ReadSkeletonData(reader);
|
||||||
skeletonData.name = Path.GetFileNameWithoutExtension(path);
|
skeletonData.name = Path.GetFileNameWithoutExtension(path);
|
||||||
@ -89,7 +89,7 @@ namespace Spine {
|
|||||||
#endif // WINDOWS_STOREAPP
|
#endif // WINDOWS_STOREAPP
|
||||||
|
|
||||||
public SkeletonData ReadSkeletonData (TextReader reader) {
|
public SkeletonData ReadSkeletonData (TextReader reader) {
|
||||||
if (reader == null) throw new ArgumentNullException("reader cannot be null.");
|
if (reader == null) throw new ArgumentNullException("reader", "reader cannot be null.");
|
||||||
|
|
||||||
var scale = this.Scale;
|
var scale = this.Scale;
|
||||||
var skeletonData = new SkeletonData();
|
var skeletonData = new SkeletonData();
|
||||||
@ -114,69 +114,19 @@ namespace Spine {
|
|||||||
if (parent == null)
|
if (parent == null)
|
||||||
throw new Exception("Parent bone not found: " + boneMap["parent"]);
|
throw new Exception("Parent bone not found: " + boneMap["parent"]);
|
||||||
}
|
}
|
||||||
var boneData = new BoneData((String)boneMap["name"], parent);
|
var data = new BoneData(skeletonData.Bones.Count, (String)boneMap["name"], parent);
|
||||||
boneData.length = GetFloat(boneMap, "length", 0) * scale;
|
data.length = GetFloat(boneMap, "length", 0) * scale;
|
||||||
boneData.x = GetFloat(boneMap, "x", 0) * scale;
|
data.x = GetFloat(boneMap, "x", 0) * scale;
|
||||||
boneData.y = GetFloat(boneMap, "y", 0) * scale;
|
data.y = GetFloat(boneMap, "y", 0) * scale;
|
||||||
boneData.rotation = GetFloat(boneMap, "rotation", 0);
|
data.rotation = GetFloat(boneMap, "rotation", 0);
|
||||||
boneData.scaleX = GetFloat(boneMap, "scaleX", 1);
|
data.scaleX = GetFloat(boneMap, "scaleX", 1);
|
||||||
boneData.scaleY = GetFloat(boneMap, "scaleY", 1);
|
data.scaleY = GetFloat(boneMap, "scaleY", 1);
|
||||||
boneData.shearX = GetFloat(boneMap, "shearX", 0);
|
data.shearX = GetFloat(boneMap, "shearX", 0);
|
||||||
boneData.shearY = GetFloat(boneMap, "shearY", 0);
|
data.shearY = GetFloat(boneMap, "shearY", 0);
|
||||||
boneData.inheritScale = GetBoolean(boneMap, "inheritScale", true);
|
data.inheritRotation = GetBoolean(boneMap, "inheritRotation", true);
|
||||||
boneData.inheritRotation = GetBoolean(boneMap, "inheritRotation", true);
|
data.inheritScale = GetBoolean(boneMap, "inheritScale", true);
|
||||||
skeletonData.bones.Add(boneData);
|
|
||||||
}
|
|
||||||
|
|
||||||
// IK constraints.
|
skeletonData.bones.Add(data);
|
||||||
if (root.ContainsKey("ik")) {
|
|
||||||
foreach (Dictionary<String, Object> ikMap in (List<Object>)root["ik"]) {
|
|
||||||
IkConstraintData ikConstraintData = new IkConstraintData((String)ikMap["name"]);
|
|
||||||
|
|
||||||
foreach (String boneName in (List<Object>)ikMap["bones"]) {
|
|
||||||
BoneData bone = skeletonData.FindBone(boneName);
|
|
||||||
if (bone == null) throw new Exception("IK bone not found: " + boneName);
|
|
||||||
ikConstraintData.bones.Add(bone);
|
|
||||||
}
|
|
||||||
|
|
||||||
String targetName = (String)ikMap["target"];
|
|
||||||
ikConstraintData.target = skeletonData.FindBone(targetName);
|
|
||||||
if (ikConstraintData.target == null) throw new Exception("Target bone not found: " + targetName);
|
|
||||||
|
|
||||||
ikConstraintData.bendDirection = GetBoolean(ikMap, "bendPositive", true) ? 1 : -1;
|
|
||||||
ikConstraintData.mix = GetFloat(ikMap, "mix", 1);
|
|
||||||
|
|
||||||
skeletonData.ikConstraints.Add(ikConstraintData);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Transform constraints.
|
|
||||||
if (root.ContainsKey("transform")) {
|
|
||||||
foreach (Dictionary<String, Object> transformMap in (List<Object>)root["transform"]) {
|
|
||||||
TransformConstraintData transformConstraintData = new TransformConstraintData((String)transformMap["name"]);
|
|
||||||
|
|
||||||
String boneName = (String)transformMap["bone"];
|
|
||||||
transformConstraintData.bone = skeletonData.FindBone(boneName);
|
|
||||||
if (transformConstraintData.bone == null) throw new Exception("Bone not found: " + boneName);
|
|
||||||
|
|
||||||
String targetName = (String)transformMap["target"];
|
|
||||||
transformConstraintData.target = skeletonData.FindBone(targetName);
|
|
||||||
if (transformConstraintData.target == null) throw new Exception("Target bone not found: " + targetName);
|
|
||||||
|
|
||||||
transformConstraintData.offsetRotation = GetFloat(transformMap, "rotation", 0);
|
|
||||||
transformConstraintData.offsetX = GetFloat(transformMap, "x", 0) * scale;
|
|
||||||
transformConstraintData.offsetY = GetFloat(transformMap, "y", 0) * scale;
|
|
||||||
transformConstraintData.offsetScaleX = GetFloat(transformMap, "scaleX", 0);
|
|
||||||
transformConstraintData.offsetScaleY = GetFloat(transformMap, "scaleY", 0);
|
|
||||||
transformConstraintData.offsetShearY = GetFloat(transformMap, "shearY", 0);
|
|
||||||
|
|
||||||
transformConstraintData.rotateMix = GetFloat(transformMap, "rotateMix", 1);
|
|
||||||
transformConstraintData.translateMix = GetFloat(transformMap, "translateMix", 1);
|
|
||||||
transformConstraintData.scaleMix = GetFloat(transformMap, "scaleMix", 1);
|
|
||||||
transformConstraintData.shearMix = GetFloat(transformMap, "shearMix", 1);
|
|
||||||
|
|
||||||
skeletonData.transformConstraints.Add(transformConstraintData);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Slots.
|
// Slots.
|
||||||
@ -185,44 +135,126 @@ namespace Spine {
|
|||||||
var slotName = (String)slotMap["name"];
|
var slotName = (String)slotMap["name"];
|
||||||
var boneName = (String)slotMap["bone"];
|
var boneName = (String)slotMap["bone"];
|
||||||
BoneData boneData = skeletonData.FindBone(boneName);
|
BoneData boneData = skeletonData.FindBone(boneName);
|
||||||
if (boneData == null)
|
if (boneData == null) throw new Exception("Slot bone not found: " + boneName);
|
||||||
throw new Exception("Slot bone not found: " + boneName);
|
var data = new SlotData(skeletonData.Slots.Count, slotName, boneData);
|
||||||
var slotData = new SlotData(slotName, boneData);
|
|
||||||
|
|
||||||
if (slotMap.ContainsKey("color")) {
|
if (slotMap.ContainsKey("color")) {
|
||||||
var color = (String)slotMap["color"];
|
var color = (String)slotMap["color"];
|
||||||
slotData.r = ToColor(color, 0);
|
data.r = ToColor(color, 0);
|
||||||
slotData.g = ToColor(color, 1);
|
data.g = ToColor(color, 1);
|
||||||
slotData.b = ToColor(color, 2);
|
data.b = ToColor(color, 2);
|
||||||
slotData.a = ToColor(color, 3);
|
data.a = ToColor(color, 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (slotMap.ContainsKey("attachment"))
|
data.attachmentName = GetString(slotMap, "attachment", null);
|
||||||
slotData.attachmentName = (String)slotMap["attachment"];
|
|
||||||
|
|
||||||
if (slotMap.ContainsKey("blend"))
|
if (slotMap.ContainsKey("blend"))
|
||||||
slotData.blendMode = (BlendMode)Enum.Parse(typeof(BlendMode), (String)slotMap["blend"], false);
|
data.blendMode = (BlendMode)Enum.Parse(typeof(BlendMode), (String)slotMap["blend"], false);
|
||||||
else
|
else
|
||||||
slotData.blendMode = BlendMode.normal;
|
data.blendMode = BlendMode.normal;
|
||||||
|
skeletonData.slots.Add(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
skeletonData.slots.Add(slotData);
|
// IK constraints.
|
||||||
|
if (root.ContainsKey("ik")) {
|
||||||
|
foreach (Dictionary<String, Object> constraintMap in (List<Object>)root["ik"]) {
|
||||||
|
IkConstraintData data = new IkConstraintData((String)constraintMap["name"]);
|
||||||
|
|
||||||
|
foreach (String boneName in (List<Object>)constraintMap["bones"]) {
|
||||||
|
BoneData bone = skeletonData.FindBone(boneName);
|
||||||
|
if (bone == null) throw new Exception("IK constraint bone not found: " + boneName);
|
||||||
|
data.bones.Add(bone);
|
||||||
|
}
|
||||||
|
|
||||||
|
String targetName = (String)constraintMap["target"];
|
||||||
|
data.target = skeletonData.FindBone(targetName);
|
||||||
|
if (data.target == null) throw new Exception("Target bone not found: " + targetName);
|
||||||
|
|
||||||
|
data.bendDirection = GetBoolean(constraintMap, "bendPositive", true) ? 1 : -1;
|
||||||
|
data.mix = GetFloat(constraintMap, "mix", 1);
|
||||||
|
|
||||||
|
skeletonData.ikConstraints.Add(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transform constraints.
|
||||||
|
if (root.ContainsKey("transform")) {
|
||||||
|
foreach (Dictionary<String, Object> constraintMap in (List<Object>)root["transform"]) {
|
||||||
|
TransformConstraintData data = new TransformConstraintData((String)constraintMap["name"]);
|
||||||
|
|
||||||
|
foreach (String boneName in (List<Object>)constraintMap["bones"]) {
|
||||||
|
BoneData bone = skeletonData.FindBone(boneName);
|
||||||
|
if (bone == null) throw new Exception("Transform constraint bone not found: " + boneName);
|
||||||
|
data.bones.Add(bone);
|
||||||
|
}
|
||||||
|
|
||||||
|
String targetName = (String)constraintMap["target"];
|
||||||
|
data.target = skeletonData.FindBone(targetName);
|
||||||
|
if (data.target == null) throw new Exception("Target bone not found: " + targetName);
|
||||||
|
|
||||||
|
data.offsetRotation = GetFloat(constraintMap, "rotation", 0);
|
||||||
|
data.offsetX = GetFloat(constraintMap, "x", 0) * scale;
|
||||||
|
data.offsetY = GetFloat(constraintMap, "y", 0) * scale;
|
||||||
|
data.offsetScaleX = GetFloat(constraintMap, "scaleX", 0);
|
||||||
|
data.offsetScaleY = GetFloat(constraintMap, "scaleY", 0);
|
||||||
|
data.offsetShearY = GetFloat(constraintMap, "shearY", 0);
|
||||||
|
|
||||||
|
data.rotateMix = GetFloat(constraintMap, "rotateMix", 1);
|
||||||
|
data.translateMix = GetFloat(constraintMap, "translateMix", 1);
|
||||||
|
data.scaleMix = GetFloat(constraintMap, "scaleMix", 1);
|
||||||
|
data.shearMix = GetFloat(constraintMap, "shearMix", 1);
|
||||||
|
|
||||||
|
skeletonData.transformConstraints.Add(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Path constraints.
|
||||||
|
if(root.ContainsKey("path")) {
|
||||||
|
foreach (Dictionary<String, Object> constraintMap in (List<Object>)root["path"]) {
|
||||||
|
PathConstraintData data = new PathConstraintData((String)constraintMap["name"]);
|
||||||
|
|
||||||
|
foreach (String boneName in (List<Object>)constraintMap["bones"]) {
|
||||||
|
BoneData bone = skeletonData.FindBone(boneName);
|
||||||
|
if (bone == null) throw new Exception("Path bone not found: " + boneName);
|
||||||
|
data.bones.Add(bone);
|
||||||
|
}
|
||||||
|
|
||||||
|
String targetName = (String)constraintMap["target"];
|
||||||
|
data.target = skeletonData.FindSlot(targetName);
|
||||||
|
if (data.target == null) throw new Exception("Target slot not found: " + targetName);
|
||||||
|
|
||||||
|
data.positionMode = (PositionMode)Enum.Parse(typeof(PositionMode), GetString(constraintMap, "positionMode", "percent"), true);
|
||||||
|
data.spacingMode = (SpacingMode)Enum.Parse(typeof(SpacingMode), GetString(constraintMap, "spacingMode", "length"), true);
|
||||||
|
data.rotateMode = (RotateMode)Enum.Parse(typeof(RotateMode), GetString(constraintMap, "rotateMode", "tangent"), true);
|
||||||
|
data.offsetRotation = GetFloat(constraintMap, "rotation", 0);
|
||||||
|
data.position = GetFloat(constraintMap, "position", 0);
|
||||||
|
if (data.positionMode == PositionMode.Fixed) data.position *= scale;
|
||||||
|
data.spacing = GetFloat(constraintMap, "spacing", 0);
|
||||||
|
if (data.spacingMode == SpacingMode.Length || data.spacingMode == SpacingMode.Fixed) data.spacing *= scale;
|
||||||
|
data.rotateMix = GetFloat(constraintMap, "rotateMix", 1);
|
||||||
|
data.translateMix = GetFloat(constraintMap, "translateMix", 1);
|
||||||
|
|
||||||
|
skeletonData.pathConstraints.Add(data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skins.
|
// Skins.
|
||||||
if (root.ContainsKey("skins")) {
|
if (root.ContainsKey("skins")) {
|
||||||
foreach (KeyValuePair<String, Object> entry in (Dictionary<String, Object>)root["skins"]) {
|
foreach (KeyValuePair<String, Object> skinMap in (Dictionary<String, Object>)root["skins"]) {
|
||||||
var skin = new Skin(entry.Key);
|
var skin = new Skin(skinMap.Key);
|
||||||
foreach (KeyValuePair<String, Object> slotEntry in (Dictionary<String, Object>)entry.Value) {
|
foreach (KeyValuePair<String, Object> slotEntry in (Dictionary<String, Object>)skinMap.Value) {
|
||||||
int slotIndex = skeletonData.FindSlotIndex(slotEntry.Key);
|
int slotIndex = skeletonData.FindSlotIndex(slotEntry.Key);
|
||||||
foreach (KeyValuePair<String, Object> attachmentEntry in ((Dictionary<String, Object>)slotEntry.Value)) {
|
foreach (KeyValuePair<String, Object> entry in ((Dictionary<String, Object>)slotEntry.Value)) {
|
||||||
Attachment attachment = ReadAttachment(skin, slotIndex, attachmentEntry.Key, (Dictionary<String, Object>)attachmentEntry.Value);
|
try {
|
||||||
if (attachment != null) skin.AddAttachment(slotIndex, attachmentEntry.Key, attachment);
|
Attachment attachment = ReadAttachment((Dictionary<String, Object>)entry.Value, skin, slotIndex, entry.Key);
|
||||||
|
if (attachment != null) skin.AddAttachment(slotIndex, entry.Key, attachment);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new Exception("Error reading attachment: " + entry.Key + ", skin: " + skin, e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
skeletonData.skins.Add(skin);
|
skeletonData.skins.Add(skin);
|
||||||
if (skin.name == "default")
|
if (skin.name == "default") skeletonData.defaultSkin = skin;
|
||||||
skeletonData.defaultSkin = skin;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -233,15 +265,8 @@ namespace Spine {
|
|||||||
if (skin == null) throw new Exception("Slot not found: " + linkedMesh.skin);
|
if (skin == null) throw new Exception("Slot not found: " + linkedMesh.skin);
|
||||||
Attachment parent = skin.GetAttachment(linkedMesh.slotIndex, linkedMesh.parent);
|
Attachment parent = skin.GetAttachment(linkedMesh.slotIndex, linkedMesh.parent);
|
||||||
if (parent == null) throw new Exception("Parent mesh not found: " + linkedMesh.parent);
|
if (parent == null) throw new Exception("Parent mesh not found: " + linkedMesh.parent);
|
||||||
if (linkedMesh.mesh is MeshAttachment) {
|
linkedMesh.mesh.ParentMesh = (MeshAttachment)parent;
|
||||||
MeshAttachment mesh = (MeshAttachment)linkedMesh.mesh;
|
linkedMesh.mesh.UpdateUVs();
|
||||||
mesh.ParentMesh = (MeshAttachment)parent;
|
|
||||||
mesh.UpdateUVs();
|
|
||||||
} else {
|
|
||||||
WeightedMeshAttachment mesh = (WeightedMeshAttachment)linkedMesh.mesh;
|
|
||||||
mesh.ParentMesh = (WeightedMeshAttachment)parent;
|
|
||||||
mesh.UpdateUVs();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
linkedMeshes.Clear();
|
linkedMeshes.Clear();
|
||||||
|
|
||||||
@ -249,18 +274,23 @@ namespace Spine {
|
|||||||
if (root.ContainsKey("events")) {
|
if (root.ContainsKey("events")) {
|
||||||
foreach (KeyValuePair<String, Object> entry in (Dictionary<String, Object>)root["events"]) {
|
foreach (KeyValuePair<String, Object> entry in (Dictionary<String, Object>)root["events"]) {
|
||||||
var entryMap = (Dictionary<String, Object>)entry.Value;
|
var entryMap = (Dictionary<String, Object>)entry.Value;
|
||||||
var eventData = new EventData(entry.Key);
|
var data = new EventData(entry.Key);
|
||||||
eventData.Int = GetInt(entryMap, "int", 0);
|
data.Int = GetInt(entryMap, "int", 0);
|
||||||
eventData.Float = GetFloat(entryMap, "float", 0);
|
data.Float = GetFloat(entryMap, "float", 0);
|
||||||
eventData.String = GetString(entryMap, "string", null);
|
data.String = GetString(entryMap, "string", null);
|
||||||
skeletonData.events.Add(eventData);
|
skeletonData.events.Add(data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Animations.
|
// Animations.
|
||||||
if (root.ContainsKey("animations")) {
|
if (root.ContainsKey("animations")) {
|
||||||
foreach (KeyValuePair<String, Object> entry in (Dictionary<String, Object>)root["animations"])
|
foreach (KeyValuePair<String, Object> entry in (Dictionary<String, Object>)root["animations"]) {
|
||||||
ReadAnimation(entry.Key, (Dictionary<String, Object>)entry.Value, skeletonData);
|
try {
|
||||||
|
ReadAnimation((Dictionary<String, Object>)entry.Value, entry.Key, skeletonData);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new Exception("Error reading animation: " + entry.Key, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
skeletonData.bones.TrimExcess();
|
skeletonData.bones.TrimExcess();
|
||||||
@ -272,25 +302,20 @@ namespace Spine {
|
|||||||
return skeletonData;
|
return skeletonData;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Attachment ReadAttachment (Skin skin, int slotIndex, String name, Dictionary<String, Object> map) {
|
private Attachment ReadAttachment (Dictionary<String, Object> map, Skin skin, int slotIndex, String name) {
|
||||||
if (map.ContainsKey("name"))
|
|
||||||
name = (String)map["name"];
|
|
||||||
|
|
||||||
var scale = this.Scale;
|
var scale = this.Scale;
|
||||||
|
name = GetString(map, "name", name);
|
||||||
|
|
||||||
var type = AttachmentType.region;
|
var typeName = GetString(map, "type", "region");
|
||||||
if (map.ContainsKey("type")) {
|
|
||||||
var typeName = (String)map["type"];
|
|
||||||
if (typeName == "skinnedmesh") typeName = "weightedmesh";
|
if (typeName == "skinnedmesh") typeName = "weightedmesh";
|
||||||
type = (AttachmentType)Enum.Parse(typeof(AttachmentType), typeName , false);
|
if (typeName == "weightedmesh") typeName = "mesh";
|
||||||
}
|
if (typeName == "weightedlinkedmesh") typeName = "linkedmesh";
|
||||||
|
var type = (AttachmentType)Enum.Parse(typeof(AttachmentType), typeName, true);
|
||||||
|
|
||||||
String path = name;
|
String path = GetString(map, "path", name);
|
||||||
if (map.ContainsKey("path"))
|
|
||||||
path = (String)map["path"];
|
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case AttachmentType.region:
|
case AttachmentType.Region:
|
||||||
RegionAttachment region = attachmentLoader.NewRegionAttachment(skin, name, path);
|
RegionAttachment region = attachmentLoader.NewRegionAttachment(skin, name, path);
|
||||||
if (region == null) return null;
|
if (region == null) return null;
|
||||||
region.Path = path;
|
region.Path = path;
|
||||||
@ -311,9 +336,15 @@ namespace Spine {
|
|||||||
region.a = ToColor(color, 3);
|
region.a = ToColor(color, 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
region.UpdateOffset();
|
||||||
return region;
|
return region;
|
||||||
case AttachmentType.mesh:
|
case AttachmentType.Boundingbox:
|
||||||
case AttachmentType.linkedmesh: {
|
BoundingBoxAttachment box = attachmentLoader.NewBoundingBoxAttachment(skin, name);
|
||||||
|
if (box == null) return null;
|
||||||
|
ReadVertices(map, box, GetInt(map, "vertexCount", 0) << 1);
|
||||||
|
return box;
|
||||||
|
case AttachmentType.Mesh:
|
||||||
|
case AttachmentType.Linkedmesh: {
|
||||||
MeshAttachment mesh = attachmentLoader.NewMeshAttachment(skin, name, path);
|
MeshAttachment mesh = attachmentLoader.NewMeshAttachment(skin, name, path);
|
||||||
if (mesh == null) return null;
|
if (mesh == null) return null;
|
||||||
mesh.Path = path;
|
mesh.Path = path;
|
||||||
@ -326,145 +357,83 @@ namespace Spine {
|
|||||||
mesh.a = ToColor(color, 3);
|
mesh.a = ToColor(color, 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
mesh.Width = GetInt(map, "width", 0) * scale;
|
mesh.Width = GetFloat(map, "width", 0) * scale;
|
||||||
mesh.Height = GetInt(map, "height", 0) * scale;
|
mesh.Height = GetFloat(map, "height", 0) * scale;
|
||||||
|
|
||||||
String parent = GetString(map, "parent", null);
|
String parent = GetString(map, "parent", null);
|
||||||
if (parent == null) {
|
if (parent != null) {
|
||||||
mesh.vertices = GetFloatArray(map, "vertices", scale);
|
mesh.InheritDeform = GetBoolean(map, "deform", true);
|
||||||
mesh.triangles = GetIntArray(map, "triangles");
|
|
||||||
mesh.regionUVs = GetFloatArray(map, "uvs", 1);
|
|
||||||
mesh.UpdateUVs();
|
|
||||||
|
|
||||||
mesh.HullLength = GetInt(map, "hull", 0) * 2;
|
|
||||||
if (map.ContainsKey("edges")) mesh.Edges = GetIntArray(map, "edges");
|
|
||||||
} else {
|
|
||||||
mesh.InheritFFD = GetBoolean(map, "ffd", true);
|
|
||||||
linkedMeshes.Add(new LinkedMesh(mesh, GetString(map, "skin", null), slotIndex, parent));
|
linkedMeshes.Add(new LinkedMesh(mesh, GetString(map, "skin", null), slotIndex, parent));
|
||||||
}
|
|
||||||
|
|
||||||
return mesh;
|
return mesh;
|
||||||
}
|
}
|
||||||
case AttachmentType.weightedmesh:
|
|
||||||
case AttachmentType.weightedlinkedmesh: {
|
|
||||||
WeightedMeshAttachment mesh = attachmentLoader.NewWeightedMeshAttachment(skin, name, path);
|
|
||||||
if (mesh == null) return null;
|
|
||||||
|
|
||||||
mesh.Path = path;
|
float[] uvs = GetFloatArray(map, "uvs", 1);
|
||||||
|
ReadVertices(map, mesh, uvs.Length);
|
||||||
|
mesh.triangles = GetIntArray(map, "triangles");
|
||||||
|
mesh.regionUVs = uvs;
|
||||||
|
mesh.UpdateUVs();
|
||||||
|
|
||||||
if (map.ContainsKey("color")) {
|
if (map.ContainsKey("hull")) mesh.HullLength = GetInt(map, "hull", 0) * 2;
|
||||||
var color = (String)map["color"];
|
if (map.ContainsKey("edges")) mesh.Edges = GetIntArray(map, "edges");
|
||||||
mesh.r = ToColor(color, 0);
|
return mesh;
|
||||||
mesh.g = ToColor(color, 1);
|
}
|
||||||
mesh.b = ToColor(color, 2);
|
case AttachmentType.Path: {
|
||||||
mesh.a = ToColor(color, 3);
|
PathAttachment pathAttachment = attachmentLoader.NewPathAttachment(skin, name);
|
||||||
|
if (pathAttachment == null) return null;
|
||||||
|
pathAttachment.closed = GetBoolean(map, "closed", false);
|
||||||
|
pathAttachment.constantSpeed = GetBoolean(map, "constantSpeed", true);
|
||||||
|
|
||||||
|
int vertexCount = GetInt(map, "vertexCount", 0);
|
||||||
|
ReadVertices(map, pathAttachment, vertexCount << 1);
|
||||||
|
|
||||||
|
// potential BOZO see Java impl
|
||||||
|
pathAttachment.lengths = GetFloatArray(map, "lengths", scale);
|
||||||
|
return pathAttachment;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
mesh.Width = GetInt(map, "width", 0) * scale;
|
private void ReadVertices (Dictionary<String, Object> map, VertexAttachment attachment, int verticesLength) {
|
||||||
mesh.Height = GetInt(map, "height", 0) * scale;
|
attachment.WorldVerticesLength = verticesLength;
|
||||||
|
|
||||||
String parent = GetString(map, "parent", null);
|
|
||||||
if (parent == null) {
|
|
||||||
float[] uvs = GetFloatArray(map, "uvs", 1);
|
|
||||||
float[] vertices = GetFloatArray(map, "vertices", 1);
|
float[] vertices = GetFloatArray(map, "vertices", 1);
|
||||||
var weights = new List<float>(uvs.Length * 3 * 3);
|
float scale = Scale;
|
||||||
var bones = new List<int>(uvs.Length * 3);
|
if (verticesLength == vertices.Length) {
|
||||||
|
if (scale != 1) {
|
||||||
|
for (int i = 0; i < vertices.Length; i++) {
|
||||||
|
vertices[i] *= scale;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
attachment.vertices = vertices;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ExposedList<float> weights = new ExposedList<float>(verticesLength * 3 * 3);
|
||||||
|
ExposedList<int> bones = new ExposedList<int>(verticesLength * 3);
|
||||||
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 * 4; i < nn; i += 4) {
|
||||||
bones.Add((int)vertices[i]);
|
bones.Add((int)vertices[i]);
|
||||||
weights.Add(vertices[i + 1] * scale);
|
weights.Add(vertices[i + 1] * this.Scale);
|
||||||
weights.Add(vertices[i + 2] * scale);
|
weights.Add(vertices[i + 2] * this.Scale);
|
||||||
weights.Add(vertices[i + 3]);
|
weights.Add(vertices[i + 3]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mesh.bones = bones.ToArray();
|
attachment.bones = bones.ToArray();
|
||||||
mesh.weights = weights.ToArray();
|
attachment.vertices = weights.ToArray();
|
||||||
mesh.triangles = GetIntArray(map, "triangles");
|
|
||||||
mesh.regionUVs = uvs;
|
|
||||||
mesh.UpdateUVs();
|
|
||||||
|
|
||||||
mesh.HullLength = GetInt(map, "hull", 0) * 2;
|
|
||||||
if (map.ContainsKey("edges")) mesh.Edges = GetIntArray(map, "edges");
|
|
||||||
} else {
|
|
||||||
mesh.InheritFFD = GetBoolean(map, "ffd", true);
|
|
||||||
linkedMeshes.Add(new LinkedMesh(mesh, GetString(map, "skin", null), slotIndex, parent));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return mesh;
|
private void ReadAnimation (Dictionary<String, Object> map, String name, SkeletonData skeletonData) {
|
||||||
}
|
var scale = this.Scale;
|
||||||
case AttachmentType.boundingbox:
|
|
||||||
BoundingBoxAttachment box = attachmentLoader.NewBoundingBoxAttachment(skin, name);
|
|
||||||
if (box == null) return null;
|
|
||||||
box.vertices = GetFloatArray(map, "vertices", scale);
|
|
||||||
return box;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private float[] GetFloatArray (Dictionary<String, Object> map, String name, float scale) {
|
|
||||||
var list = (List<Object>)map[name];
|
|
||||||
var values = new float[list.Count];
|
|
||||||
if (scale == 1) {
|
|
||||||
for (int i = 0, n = list.Count; i < n; i++)
|
|
||||||
values[i] = (float)list[i];
|
|
||||||
} else {
|
|
||||||
for (int i = 0, n = list.Count; i < n; i++)
|
|
||||||
values[i] = (float)list[i] * scale;
|
|
||||||
}
|
|
||||||
return values;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int[] GetIntArray (Dictionary<String, Object> map, String name) {
|
|
||||||
var list = (List<Object>)map[name];
|
|
||||||
var values = new int[list.Count];
|
|
||||||
for (int i = 0, n = list.Count; i < n; i++)
|
|
||||||
values[i] = (int)(float)list[i];
|
|
||||||
return values;
|
|
||||||
}
|
|
||||||
|
|
||||||
private float GetFloat (Dictionary<String, Object> map, String name, float defaultValue) {
|
|
||||||
if (!map.ContainsKey(name))
|
|
||||||
return defaultValue;
|
|
||||||
return (float)map[name];
|
|
||||||
}
|
|
||||||
|
|
||||||
private int GetInt (Dictionary<String, Object> map, String name, int defaultValue) {
|
|
||||||
if (!map.ContainsKey(name))
|
|
||||||
return defaultValue;
|
|
||||||
return (int)(float)map[name];
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool GetBoolean (Dictionary<String, Object> map, String name, bool defaultValue) {
|
|
||||||
if (!map.ContainsKey(name))
|
|
||||||
return defaultValue;
|
|
||||||
return (bool)map[name];
|
|
||||||
}
|
|
||||||
|
|
||||||
private String GetString (Dictionary<String, Object> map, String name, String defaultValue) {
|
|
||||||
if (!map.ContainsKey(name))
|
|
||||||
return defaultValue;
|
|
||||||
return (String)map[name];
|
|
||||||
}
|
|
||||||
|
|
||||||
private float ToColor (String hexString, int colorIndex) {
|
|
||||||
if (hexString.Length != 8)
|
|
||||||
throw new ArgumentException("Color hexidecimal length must be 8, recieved: " + hexString);
|
|
||||||
return Convert.ToInt32(hexString.Substring(colorIndex * 2, 2), 16) / (float)255;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ReadAnimation (String name, Dictionary<String, Object> map, SkeletonData skeletonData) {
|
|
||||||
var timelines = new ExposedList<Timeline>();
|
var timelines = new ExposedList<Timeline>();
|
||||||
float duration = 0;
|
float duration = 0;
|
||||||
var scale = this.Scale;
|
|
||||||
|
|
||||||
|
// Slot timelines.
|
||||||
if (map.ContainsKey("slots")) {
|
if (map.ContainsKey("slots")) {
|
||||||
foreach (KeyValuePair<String, Object> entry in (Dictionary<String, Object>)map["slots"]) {
|
foreach (KeyValuePair<String, Object> entry in (Dictionary<String, Object>)map["slots"]) {
|
||||||
String slotName = entry.Key;
|
String slotName = entry.Key;
|
||||||
int slotIndex = skeletonData.FindSlotIndex(slotName);
|
int slotIndex = skeletonData.FindSlotIndex(slotName);
|
||||||
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 timelineName = (String)timelineEntry.Key;
|
var timelineName = (String)timelineEntry.Key;
|
||||||
@ -477,11 +446,11 @@ namespace Spine {
|
|||||||
float time = (float)valueMap["time"];
|
float time = (float)valueMap["time"];
|
||||||
String c = (String)valueMap["color"];
|
String c = (String)valueMap["color"];
|
||||||
timeline.SetFrame(frameIndex, time, ToColor(c, 0), ToColor(c, 1), ToColor(c, 2), ToColor(c, 3));
|
timeline.SetFrame(frameIndex, time, ToColor(c, 0), ToColor(c, 1), ToColor(c, 2), ToColor(c, 3));
|
||||||
ReadCurve(timeline, frameIndex, valueMap);
|
ReadCurve(valueMap, timeline, frameIndex);
|
||||||
frameIndex++;
|
frameIndex++;
|
||||||
}
|
}
|
||||||
timelines.Add(timeline);
|
timelines.Add(timeline);
|
||||||
duration = Math.Max(duration, timeline.frames[timeline.FrameCount * 5 - 5]);
|
duration = Math.Max(duration, timeline.frames[timeline.FrameCount -1] * ColorTimeline.ENTRIES);
|
||||||
|
|
||||||
} else if (timelineName == "attachment") {
|
} else if (timelineName == "attachment") {
|
||||||
var timeline = new AttachmentTimeline(values.Count);
|
var timeline = new AttachmentTimeline(values.Count);
|
||||||
@ -501,13 +470,12 @@ namespace Spine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Bone timelines.
|
||||||
if (map.ContainsKey("bones")) {
|
if (map.ContainsKey("bones")) {
|
||||||
foreach (KeyValuePair<String, Object> entry in (Dictionary<String, Object>)map["bones"]) {
|
foreach (KeyValuePair<String, Object> entry in (Dictionary<String, Object>)map["bones"]) {
|
||||||
String boneName = entry.Key;
|
String boneName = entry.Key;
|
||||||
int boneIndex = skeletonData.FindBoneIndex(boneName);
|
int boneIndex = skeletonData.FindBoneIndex(boneName);
|
||||||
if (boneIndex == -1)
|
if (boneIndex == -1) throw new Exception("Bone not found: " + boneName);
|
||||||
throw new Exception("Bone not found: " + boneName);
|
|
||||||
|
|
||||||
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;
|
||||||
@ -518,13 +486,12 @@ namespace Spine {
|
|||||||
|
|
||||||
int frameIndex = 0;
|
int frameIndex = 0;
|
||||||
foreach (Dictionary<String, Object> valueMap in values) {
|
foreach (Dictionary<String, Object> valueMap in values) {
|
||||||
float time = (float)valueMap["time"];
|
timeline.SetFrame(frameIndex, (float)valueMap["time"], (float)valueMap["angle"]);
|
||||||
timeline.SetFrame(frameIndex, time, (float)valueMap["angle"]);
|
ReadCurve(valueMap, timeline, frameIndex);
|
||||||
ReadCurve(timeline, frameIndex, valueMap);
|
|
||||||
frameIndex++;
|
frameIndex++;
|
||||||
}
|
}
|
||||||
timelines.Add(timeline);
|
timelines.Add(timeline);
|
||||||
duration = Math.Max(duration, timeline.frames[timeline.FrameCount * 2 - 2]);
|
duration = Math.Max(duration, timeline.frames[(timeline.FrameCount - 1) * RotateTimeline.ENTRIES]);
|
||||||
|
|
||||||
} else if (timelineName == "translate" || timelineName == "scale" || timelineName == "shear") {
|
} else if (timelineName == "translate" || timelineName == "scale" || timelineName == "shear") {
|
||||||
TranslateTimeline timeline;
|
TranslateTimeline timeline;
|
||||||
@ -544,12 +511,12 @@ namespace Spine {
|
|||||||
float time = (float)valueMap["time"];
|
float time = (float)valueMap["time"];
|
||||||
float x = GetFloat(valueMap, "x", 0);
|
float x = GetFloat(valueMap, "x", 0);
|
||||||
float y = GetFloat(valueMap, "y", 0);
|
float y = GetFloat(valueMap, "y", 0);
|
||||||
timeline.SetFrame(frameIndex, time, (float)x * timelineScale, (float)y * timelineScale);
|
timeline.SetFrame(frameIndex, time, x * timelineScale, y * timelineScale);
|
||||||
ReadCurve(timeline, frameIndex, valueMap);
|
ReadCurve(valueMap, timeline, frameIndex);
|
||||||
frameIndex++;
|
frameIndex++;
|
||||||
}
|
}
|
||||||
timelines.Add(timeline);
|
timelines.Add(timeline);
|
||||||
duration = Math.Max(duration, timeline.frames[timeline.FrameCount * 3 - 3]);
|
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 + ")");
|
||||||
@ -557,7 +524,7 @@ namespace Spine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// IK 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> constraintMap in (Dictionary<String, Object>)map["ik"]) {
|
||||||
IkConstraintData constraint = skeletonData.FindIkConstraint(constraintMap.Key);
|
IkConstraintData constraint = skeletonData.FindIkConstraint(constraintMap.Key);
|
||||||
@ -570,11 +537,11 @@ namespace Spine {
|
|||||||
float mix = GetFloat(valueMap, "mix", 1);
|
float mix = GetFloat(valueMap, "mix", 1);
|
||||||
bool bendPositive = GetBoolean(valueMap, "bendPositive", true);
|
bool bendPositive = GetBoolean(valueMap, "bendPositive", true);
|
||||||
timeline.SetFrame(frameIndex, time, mix, bendPositive ? 1 : -1);
|
timeline.SetFrame(frameIndex, time, mix, bendPositive ? 1 : -1);
|
||||||
ReadCurve(timeline, frameIndex, valueMap);
|
ReadCurve(valueMap, timeline, frameIndex);
|
||||||
frameIndex++;
|
frameIndex++;
|
||||||
}
|
}
|
||||||
timelines.Add(timeline);
|
timelines.Add(timeline);
|
||||||
duration = Math.Max(duration, timeline.frames[timeline.FrameCount * 3 - 3]);
|
duration = Math.Max(duration, timeline.frames[(timeline.FrameCount - 1) * IkConstraintTimeline.ENTRIES]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -593,62 +560,103 @@ namespace Spine {
|
|||||||
float scaleMix = GetFloat(valueMap, "scaleMix", 1);
|
float scaleMix = GetFloat(valueMap, "scaleMix", 1);
|
||||||
float shearMix = GetFloat(valueMap, "shearMix", 1);
|
float shearMix = GetFloat(valueMap, "shearMix", 1);
|
||||||
timeline.SetFrame(frameIndex, time, rotateMix, translateMix, scaleMix, shearMix);
|
timeline.SetFrame(frameIndex, time, rotateMix, translateMix, scaleMix, shearMix);
|
||||||
ReadCurve(timeline, frameIndex, valueMap);
|
ReadCurve(valueMap, timeline, frameIndex);
|
||||||
frameIndex++;
|
frameIndex++;
|
||||||
}
|
}
|
||||||
timelines.Add(timeline);
|
timelines.Add(timeline);
|
||||||
duration = Math.Max(duration, timeline.frames[timeline.FrameCount * 5 - 5]);
|
duration = Math.Max(duration, timeline.frames[(timeline.FrameCount - 1) * TransformConstraintTimeline.ENTRIES]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// FFD timelines.
|
// Path constraint timelines.
|
||||||
if (map.ContainsKey("ffd")) {
|
if (map.ContainsKey("paths")) {
|
||||||
foreach (KeyValuePair<String, Object> ffdMap in (Dictionary<String, Object>)map["ffd"]) {
|
foreach (KeyValuePair<String, Object> constraintMap in (Dictionary<String, Object>)map["paths"]) {
|
||||||
Skin skin = skeletonData.FindSkin(ffdMap.Key);
|
int index = skeletonData.FindPathConstraintIndex(constraintMap.Key);
|
||||||
foreach (KeyValuePair<String, Object> slotMap in (Dictionary<String, Object>)ffdMap.Value) {
|
if (index == -1) throw new Exception("Path constraint not found: " + constraintMap.Key);
|
||||||
|
PathConstraintData data = skeletonData.pathConstraints.Items[index];
|
||||||
|
var timelineMap = (Dictionary<String, Object>)constraintMap.Value;
|
||||||
|
foreach (KeyValuePair<String, Object> timelineEntry in timelineMap) {
|
||||||
|
var values = (List<Object>)timelineEntry.Value;
|
||||||
|
var timelineName = (String)timelineEntry.Key;
|
||||||
|
if (timelineName == "position" || timelineName == "spacing") {
|
||||||
|
PathConstraintPositionTimeline timeline;
|
||||||
|
float timelineScale = 1;
|
||||||
|
if (timelineName == "spacing") {
|
||||||
|
timeline = new PathConstraintSpacingTimeline(values.Count);
|
||||||
|
if (data.spacingMode == SpacingMode.Length || data.spacingMode == SpacingMode.Fixed) timelineScale = scale;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
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, (float)valueMap["time"], 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") {
|
||||||
|
PathConstraintMixTimeline timeline = new PathConstraintMixTimeline(values.Count);
|
||||||
|
timeline.pathConstraintIndex = index;
|
||||||
|
int frameIndex = 0;
|
||||||
|
foreach (Dictionary<String, Object> valueMap in values) {
|
||||||
|
timeline.SetFrame(frameIndex, (float)valueMap["time"], 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]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deform timelines.
|
||||||
|
if (map.ContainsKey("deform")) {
|
||||||
|
foreach (KeyValuePair<String, Object> deformMap in (Dictionary<String, Object>)map["deform"]) {
|
||||||
|
Skin skin = skeletonData.FindSkin(deformMap.Key);
|
||||||
|
foreach (KeyValuePair<String, Object> slotMap in (Dictionary<String, Object>)deformMap.Value) {
|
||||||
int slotIndex = skeletonData.FindSlotIndex(slotMap.Key);
|
int slotIndex = skeletonData.FindSlotIndex(slotMap.Key);
|
||||||
foreach (KeyValuePair<String, Object> meshMap in (Dictionary<String, Object>)slotMap.Value) {
|
if (slotIndex == -1) throw new Exception("Slot not found: " + slotMap.Key);
|
||||||
var values = (List<Object>)meshMap.Value;
|
foreach (KeyValuePair<String, Object> timelineMap in (Dictionary<String, Object>)slotMap.Value) {
|
||||||
var timeline = new FfdTimeline(values.Count);
|
var values = (List<Object>)timelineMap.Value;
|
||||||
Attachment attachment = skin.GetAttachment(slotIndex, meshMap.Key);
|
VertexAttachment attachment = (VertexAttachment)skin.GetAttachment(slotIndex, timelineMap.Key);
|
||||||
if (attachment == null) throw new Exception("FFD attachment not found: " + meshMap.Key);
|
if (attachment == null) throw new Exception("Deform attachment not found: " + timelineMap.Key);
|
||||||
|
bool weighted = attachment.bones != null;
|
||||||
|
float[] vertices = attachment.vertices;
|
||||||
|
int deformLength = weighted ? vertices.Length / 3 * 2 : vertices.Length;
|
||||||
|
|
||||||
|
var timeline = new DeformTimeline(values.Count);
|
||||||
timeline.slotIndex = slotIndex;
|
timeline.slotIndex = slotIndex;
|
||||||
timeline.attachment = attachment;
|
timeline.attachment = attachment;
|
||||||
|
|
||||||
int vertexCount;
|
|
||||||
if (attachment is MeshAttachment)
|
|
||||||
vertexCount = ((MeshAttachment)attachment).vertices.Length;
|
|
||||||
else
|
|
||||||
vertexCount = ((WeightedMeshAttachment)attachment).Weights.Length / 3 * 2;
|
|
||||||
|
|
||||||
int frameIndex = 0;
|
int frameIndex = 0;
|
||||||
foreach (Dictionary<String, Object> valueMap in values) {
|
foreach (Dictionary<String, Object> valueMap in values) {
|
||||||
float[] vertices;
|
float[] deform;
|
||||||
if (!valueMap.ContainsKey("vertices")) {
|
if (!valueMap.ContainsKey("vertices")) {
|
||||||
if (attachment is MeshAttachment)
|
deform = weighted ? new float[deformLength] : vertices;
|
||||||
vertices = ((MeshAttachment)attachment).vertices;
|
|
||||||
else
|
|
||||||
vertices = new float[vertexCount];
|
|
||||||
} else {
|
} else {
|
||||||
var verticesValue = (List<Object>)valueMap["vertices"];
|
deform = new float[deformLength];
|
||||||
vertices = new float[vertexCount];
|
|
||||||
int start = GetInt(valueMap, "offset", 0);
|
int start = GetInt(valueMap, "offset", 0);
|
||||||
if (scale == 1) {
|
float[] verticesValue = GetFloatArray(valueMap, "vertices", 1);
|
||||||
for (int i = 0, n = verticesValue.Count; i < n; i++)
|
Array.Copy(verticesValue, 0, deform, start, verticesValue.Length);
|
||||||
vertices[i + start] = (float)verticesValue[i];
|
if (scale != 1) {
|
||||||
} else {
|
for (int i = start, n = i + verticesValue.Length; i < n; i++)
|
||||||
for (int i = 0, n = verticesValue.Count; i < n; i++)
|
deform[i] *= scale;
|
||||||
vertices[i + start] = (float)verticesValue[i] * scale;
|
|
||||||
}
|
}
|
||||||
if (attachment is MeshAttachment) {
|
|
||||||
float[] meshVertices = ((MeshAttachment)attachment).vertices;
|
if (!weighted) {
|
||||||
for (int i = 0; i < vertexCount; i++)
|
for (int i = 0; i < deformLength; i++)
|
||||||
vertices[i] += meshVertices[i];
|
deform[i] += vertices[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
timeline.SetFrame(frameIndex, (float)valueMap["time"], vertices);
|
timeline.SetFrame(frameIndex, (float)valueMap["time"], deform);
|
||||||
ReadCurve(timeline, frameIndex, valueMap);
|
ReadCurve(valueMap, timeline, frameIndex);
|
||||||
frameIndex++;
|
frameIndex++;
|
||||||
}
|
}
|
||||||
timelines.Add(timeline);
|
timelines.Add(timeline);
|
||||||
@ -658,6 +666,7 @@ namespace Spine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Draw order timeline.
|
||||||
if (map.ContainsKey("drawOrder") || map.ContainsKey("draworder")) {
|
if (map.ContainsKey("drawOrder") || map.ContainsKey("draworder")) {
|
||||||
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);
|
||||||
@ -695,6 +704,7 @@ namespace Spine {
|
|||||||
duration = Math.Max(duration, timeline.frames[timeline.FrameCount - 1]);
|
duration = Math.Max(duration, timeline.frames[timeline.FrameCount - 1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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);
|
||||||
@ -716,14 +726,15 @@ namespace Spine {
|
|||||||
skeletonData.animations.Add(new Animation(name, timelines, duration));
|
skeletonData.animations.Add(new Animation(name, timelines, duration));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ReadCurve (CurveTimeline timeline, int frameIndex, Dictionary<String, Object> valueMap) {
|
static void ReadCurve (Dictionary<String, Object> valueMap, CurveTimeline timeline, int frameIndex) {
|
||||||
if (!valueMap.ContainsKey("curve"))
|
if (!valueMap.ContainsKey("curve"))
|
||||||
return;
|
return;
|
||||||
Object curveObject = valueMap["curve"];
|
Object curveObject = valueMap["curve"];
|
||||||
if (curveObject.Equals("stepped"))
|
if (curveObject.Equals("stepped"))
|
||||||
timeline.SetStepped(frameIndex);
|
timeline.SetStepped(frameIndex);
|
||||||
else if (curveObject is List<Object>) {
|
else {
|
||||||
var curve = (List<Object>)curveObject;
|
var curve = curveObject as List<Object>;
|
||||||
|
if (curve != null)
|
||||||
timeline.SetCurve(frameIndex, (float)curve[0], (float)curve[1], (float)curve[2], (float)curve[3]);
|
timeline.SetCurve(frameIndex, (float)curve[0], (float)curve[1], (float)curve[2], (float)curve[3]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -731,14 +742,65 @@ namespace Spine {
|
|||||||
internal class LinkedMesh {
|
internal class LinkedMesh {
|
||||||
internal String parent, skin;
|
internal String parent, skin;
|
||||||
internal int slotIndex;
|
internal int slotIndex;
|
||||||
internal Attachment mesh;
|
internal MeshAttachment mesh;
|
||||||
|
|
||||||
public LinkedMesh (Attachment mesh, String skin, int slotIndex, String parent) {
|
public LinkedMesh (MeshAttachment mesh, String skin, int slotIndex, String parent) {
|
||||||
this.mesh = mesh;
|
this.mesh = mesh;
|
||||||
this.skin = skin;
|
this.skin = skin;
|
||||||
this.slotIndex = slotIndex;
|
this.slotIndex = slotIndex;
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static float[] GetFloatArray(Dictionary<String, Object> map, String name, float scale) {
|
||||||
|
var list = (List<Object>)map[name];
|
||||||
|
var values = new float[list.Count];
|
||||||
|
if (scale == 1) {
|
||||||
|
for (int i = 0, n = list.Count; i < n; i++)
|
||||||
|
values[i] = (float)list[i];
|
||||||
|
} else {
|
||||||
|
for (int i = 0, n = list.Count; i < n; i++)
|
||||||
|
values[i] = (float)list[i] * scale;
|
||||||
|
}
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int[] GetIntArray(Dictionary<String, Object> map, String name) {
|
||||||
|
var list = (List<Object>)map[name];
|
||||||
|
var values = new int[list.Count];
|
||||||
|
for (int i = 0, n = list.Count; i < n; i++)
|
||||||
|
values[i] = (int)(float)list[i];
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
||||||
|
static float GetFloat(Dictionary<String, Object> map, String name, float defaultValue) {
|
||||||
|
if (!map.ContainsKey(name))
|
||||||
|
return defaultValue;
|
||||||
|
return (float)map[name];
|
||||||
|
}
|
||||||
|
|
||||||
|
static int GetInt(Dictionary<String, Object> map, String name, int defaultValue) {
|
||||||
|
if (!map.ContainsKey(name))
|
||||||
|
return defaultValue;
|
||||||
|
return (int)(float)map[name];
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool GetBoolean(Dictionary<String, Object> map, String name, bool defaultValue) {
|
||||||
|
if (!map.ContainsKey(name))
|
||||||
|
return defaultValue;
|
||||||
|
return (bool)map[name];
|
||||||
|
}
|
||||||
|
|
||||||
|
static String GetString(Dictionary<String, Object> map, String name, String defaultValue) {
|
||||||
|
if (!map.ContainsKey(name))
|
||||||
|
return defaultValue;
|
||||||
|
return (String)map[name];
|
||||||
|
}
|
||||||
|
|
||||||
|
static float ToColor(String hexString, int colorIndex) {
|
||||||
|
if (hexString.Length != 8)
|
||||||
|
throw new ArgumentException("Color hexidecimal length must be 8, recieved: " + hexString, "hexString");
|
||||||
|
return Convert.ToInt32(hexString.Substring(colorIndex * 2, 2), 16) / (float)255;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -40,14 +40,15 @@ namespace Spine {
|
|||||||
new Dictionary<AttachmentKeyTuple, Attachment>(AttachmentKeyTupleComparer.Instance);
|
new Dictionary<AttachmentKeyTuple, Attachment>(AttachmentKeyTupleComparer.Instance);
|
||||||
|
|
||||||
public String Name { get { return name; } }
|
public String Name { get { return name; } }
|
||||||
|
public Dictionary<AttachmentKeyTuple, Attachment> Attachments { get { return attachments; } }
|
||||||
|
|
||||||
public Skin (String name) {
|
public Skin (String name) {
|
||||||
if (name == null) throw new ArgumentNullException("name cannot be null.");
|
if (name == null) throw new ArgumentNullException("name", "name cannot be null.");
|
||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddAttachment (int slotIndex, String name, Attachment attachment) {
|
public void AddAttachment (int slotIndex, String name, Attachment attachment) {
|
||||||
if (attachment == null) throw new ArgumentNullException("attachment cannot be null.");
|
if (attachment == null) throw new ArgumentNullException("attachment", "attachment cannot be null.");
|
||||||
attachments[new AttachmentKeyTuple(slotIndex, name)] = attachment;
|
attachments[new AttachmentKeyTuple(slotIndex, name)] = attachment;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,13 +60,13 @@ namespace Spine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void FindNamesForSlot (int slotIndex, List<String> names) {
|
public void FindNamesForSlot (int slotIndex, List<String> names) {
|
||||||
if (names == null) throw new ArgumentNullException("names cannot be null.");
|
if (names == null) throw new ArgumentNullException("names", "names cannot be null.");
|
||||||
foreach (AttachmentKeyTuple key in attachments.Keys)
|
foreach (AttachmentKeyTuple key in attachments.Keys)
|
||||||
if (key.slotIndex == slotIndex) names.Add(key.name);
|
if (key.slotIndex == slotIndex) names.Add(key.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void FindAttachmentsForSlot (int slotIndex, List<Attachment> attachments) {
|
public void FindAttachmentsForSlot (int slotIndex, List<Attachment> attachments) {
|
||||||
if (attachments == null) throw new ArgumentNullException("attachments cannot be null.");
|
if (attachments == null) throw new ArgumentNullException("attachments", "attachments cannot be null.");
|
||||||
foreach (KeyValuePair<AttachmentKeyTuple, Attachment> entry in this.attachments)
|
foreach (KeyValuePair<AttachmentKeyTuple, Attachment> entry in this.attachments)
|
||||||
if (entry.Key.slotIndex == slotIndex) attachments.Add(entry.Value);
|
if (entry.Key.slotIndex == slotIndex) attachments.Add(entry.Value);
|
||||||
}
|
}
|
||||||
@ -86,7 +87,7 @@ namespace Spine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct AttachmentKeyTuple {
|
public struct AttachmentKeyTuple {
|
||||||
public readonly int slotIndex;
|
public readonly int slotIndex;
|
||||||
public readonly string name;
|
public readonly string name;
|
||||||
internal readonly int nameHashCode;
|
internal readonly int nameHashCode;
|
||||||
|
|||||||
@ -38,8 +38,7 @@ namespace Spine {
|
|||||||
internal float r, g, b, a;
|
internal float r, g, b, a;
|
||||||
internal Attachment attachment;
|
internal Attachment attachment;
|
||||||
internal float attachmentTime;
|
internal float attachmentTime;
|
||||||
internal float[] attachmentVertices = new float[0];
|
internal ExposedList<float> attachmentVertices = new ExposedList<float>();
|
||||||
internal int attachmentVerticesCount;
|
|
||||||
|
|
||||||
public SlotData Data { get { return data; } }
|
public SlotData Data { get { return data; } }
|
||||||
public Bone Bone { get { return bone; } }
|
public Bone Bone { get { return bone; } }
|
||||||
@ -51,38 +50,31 @@ namespace Spine {
|
|||||||
|
|
||||||
/// <summary>May be null.</summary>
|
/// <summary>May be null.</summary>
|
||||||
public Attachment Attachment {
|
public Attachment Attachment {
|
||||||
get {
|
get { return attachment; }
|
||||||
return attachment;
|
|
||||||
}
|
|
||||||
set {
|
set {
|
||||||
if (attachment == value) return;
|
if (attachment == value) return;
|
||||||
attachment = value;
|
attachment = value;
|
||||||
attachmentTime = bone.skeleton.time;
|
attachmentTime = bone.skeleton.time;
|
||||||
attachmentVerticesCount = 0;
|
attachmentVertices.Clear(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public float AttachmentTime {
|
public float AttachmentTime {
|
||||||
get {
|
get { return bone.skeleton.time - attachmentTime; }
|
||||||
return bone.skeleton.time - attachmentTime;
|
set { attachmentTime = bone.skeleton.time - value; }
|
||||||
}
|
|
||||||
set {
|
|
||||||
attachmentTime = bone.skeleton.time - value;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public float[] AttachmentVertices { get { return attachmentVertices; } set { attachmentVertices = value; } }
|
public ExposedList<float> AttachmentVertices { get { return attachmentVertices; } set { attachmentVertices = value; } }
|
||||||
public int AttachmentVerticesCount { get { return attachmentVerticesCount; } set { attachmentVerticesCount = value; } }
|
|
||||||
|
|
||||||
public Slot (SlotData data, Bone bone) {
|
public Slot (SlotData data, Bone bone) {
|
||||||
if (data == null) throw new ArgumentNullException("data cannot be null.");
|
if (data == null) throw new ArgumentNullException("data", "data cannot be null.");
|
||||||
if (bone == null) throw new ArgumentNullException("bone cannot be null.");
|
if (bone == null) throw new ArgumentNullException("bone", "bone cannot be null.");
|
||||||
this.data = data;
|
this.data = data;
|
||||||
this.bone = bone;
|
this.bone = bone;
|
||||||
SetToSetupPose();
|
SetToSetupPose();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void SetToSetupPose (int slotIndex) {
|
public void SetToSetupPose () {
|
||||||
r = data.r;
|
r = data.r;
|
||||||
g = data.g;
|
g = data.g;
|
||||||
b = data.b;
|
b = data.b;
|
||||||
@ -91,14 +83,10 @@ namespace Spine {
|
|||||||
Attachment = null;
|
Attachment = null;
|
||||||
else {
|
else {
|
||||||
attachment = null;
|
attachment = null;
|
||||||
Attachment = bone.skeleton.GetAttachment(slotIndex, data.attachmentName);
|
Attachment = bone.skeleton.GetAttachment(data.index, data.attachmentName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetToSetupPose () {
|
|
||||||
SetToSetupPose(bone.skeleton.data.slots.IndexOf(data));
|
|
||||||
}
|
|
||||||
|
|
||||||
override public String ToString () {
|
override public String ToString () {
|
||||||
return data.name;
|
return data.name;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -33,12 +33,14 @@ using System;
|
|||||||
|
|
||||||
namespace Spine {
|
namespace Spine {
|
||||||
public class SlotData {
|
public class SlotData {
|
||||||
|
internal int index;
|
||||||
internal String name;
|
internal String name;
|
||||||
internal BoneData boneData;
|
internal BoneData boneData;
|
||||||
internal float r = 1, g = 1, b = 1, a = 1;
|
internal float r = 1, g = 1, b = 1, a = 1;
|
||||||
internal String attachmentName;
|
internal String attachmentName;
|
||||||
internal BlendMode blendMode;
|
internal BlendMode blendMode;
|
||||||
|
|
||||||
|
public int Index { get { return index; } }
|
||||||
public String Name { get { return name; } }
|
public String Name { get { return name; } }
|
||||||
public BoneData BoneData { get { return boneData; } }
|
public BoneData BoneData { get { return boneData; } }
|
||||||
public float R { get { return r; } set { r = value; } }
|
public float R { get { return r; } set { r = value; } }
|
||||||
@ -49,9 +51,11 @@ namespace Spine {
|
|||||||
public String AttachmentName { get { return attachmentName; } set { attachmentName = value; } }
|
public String AttachmentName { get { return attachmentName; } set { attachmentName = value; } }
|
||||||
public BlendMode BlendMode { get { return blendMode; } set { blendMode = value; } }
|
public BlendMode BlendMode { get { return blendMode; } set { blendMode = value; } }
|
||||||
|
|
||||||
public SlotData (String name, BoneData boneData) {
|
public SlotData (int index, String name, BoneData boneData) {
|
||||||
if (name == null) throw new ArgumentNullException("name cannot be null.");
|
if (index < 0) throw new ArgumentException ("index must be >= 0.", "index");
|
||||||
if (boneData == null) throw new ArgumentNullException("boneData cannot be null.");
|
if (name == null) throw new ArgumentNullException("name", "name cannot be null.");
|
||||||
|
if (boneData == null) throw new ArgumentNullException("boneData", "boneData cannot be null.");
|
||||||
|
this.index = index;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.boneData = boneData;
|
this.boneData = boneData;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -30,46 +30,35 @@
|
|||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace Spine {
|
namespace Spine {
|
||||||
public class TransformConstraint : IUpdatable {
|
public class TransformConstraint : IUpdatable {
|
||||||
internal TransformConstraintData data;
|
internal TransformConstraintData data;
|
||||||
internal Bone bone, target;
|
internal ExposedList<Bone> bones;
|
||||||
|
internal Bone target;
|
||||||
internal float rotateMix, translateMix, scaleMix, shearMix;
|
internal float rotateMix, translateMix, scaleMix, shearMix;
|
||||||
internal float offsetRotation, offsetX, offsetY, offsetScaleX, offsetScaleY, offsetShearY;
|
|
||||||
|
|
||||||
public TransformConstraintData Data { get { return data; } }
|
public TransformConstraintData Data { get { return data; } }
|
||||||
public Bone Bone { get { return bone; } set { bone = value; } }
|
public ExposedList<Bone> Bones { get { return bones; } }
|
||||||
public Bone Target { get { return target; } set { target = value; } }
|
public Bone Target { get { return target; } set { target = value; } }
|
||||||
public float RotateMix { get { return rotateMix; } set { rotateMix = value; } }
|
public float RotateMix { get { return rotateMix; } set { rotateMix = value; } }
|
||||||
public float TranslateMix { get { return translateMix; } set { translateMix = value; } }
|
public float TranslateMix { get { return translateMix; } set { translateMix = value; } }
|
||||||
public float ScaleMix { get { return scaleMix; } set { scaleMix = value; } }
|
public float ScaleMix { get { return scaleMix; } set { scaleMix = value; } }
|
||||||
public float ShearMix { get { return shearMix; } set { shearMix = value; } }
|
public float ShearMix { get { return shearMix; } set { shearMix = value; } }
|
||||||
|
|
||||||
public float OffsetRotation { get { return offsetRotation; } set { offsetRotation = value; } }
|
|
||||||
public float OffsetX { get { return offsetX; } set { offsetX = value; } }
|
|
||||||
public float OffsetY { get { return offsetY; } set { offsetY = value; } }
|
|
||||||
public float OffsetScaleX { get { return offsetScaleX; } set { offsetScaleX = value; } }
|
|
||||||
public float OffsetScaleY { get { return offsetScaleY; } set { offsetScaleY = value; } }
|
|
||||||
public float OffsetShearY { get { return offsetShearY; } set { offsetShearY = value; } }
|
|
||||||
|
|
||||||
public TransformConstraint (TransformConstraintData data, Skeleton skeleton) {
|
public TransformConstraint (TransformConstraintData data, Skeleton skeleton) {
|
||||||
if (data == null) throw new ArgumentNullException("data cannot be null.");
|
if (data == null) throw new ArgumentNullException("data", "data cannot be null.");
|
||||||
if (skeleton == null) throw new ArgumentNullException("skeleton cannot be null.");
|
if (skeleton == null) throw new ArgumentNullException("skeleton", "skeleton cannot be null.");
|
||||||
this.data = data;
|
this.data = data;
|
||||||
translateMix = data.translateMix;
|
|
||||||
rotateMix = data.rotateMix;
|
rotateMix = data.rotateMix;
|
||||||
|
translateMix = data.translateMix;
|
||||||
scaleMix = data.scaleMix;
|
scaleMix = data.scaleMix;
|
||||||
shearMix = data.shearMix;
|
shearMix = data.shearMix;
|
||||||
offsetRotation = data.offsetRotation;
|
|
||||||
offsetX = data.offsetX;
|
|
||||||
offsetY = data.offsetY;
|
|
||||||
offsetScaleX = data.offsetScaleX;
|
|
||||||
offsetScaleY = data.offsetScaleY;
|
|
||||||
offsetShearY = data.offsetShearY;
|
|
||||||
|
|
||||||
bone = skeleton.FindBone(data.bone.name);
|
bones = new ExposedList<Bone>();
|
||||||
|
foreach (BoneData boneData in data.bones)
|
||||||
|
bones.Add (skeleton.FindBone (boneData.name));
|
||||||
|
|
||||||
target = skeleton.FindBone(data.target.name);
|
target = skeleton.FindBone(data.target.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,12 +67,16 @@ namespace Spine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void Update () {
|
public void Update () {
|
||||||
Bone bone = this.bone;
|
float rotateMix = this.rotateMix, translateMix = this.translateMix, scaleMix = this.scaleMix, shearMix = this.shearMix;
|
||||||
Bone target = this.target;
|
Bone target = this.target;
|
||||||
|
float ta = target.a, tb = target.b, tc = target.c, td = target.d;
|
||||||
|
ExposedList<Bone> bones = this.bones;
|
||||||
|
for (int i = 0, n = bones.Count; i < n; i++) {
|
||||||
|
Bone bone = bones.Items[i];
|
||||||
|
|
||||||
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;
|
||||||
float r = MathUtils.Atan2(target.c, target.a) - MathUtils.Atan2(c, a) + offsetRotation * MathUtils.degRad;
|
float r = (float)Math.Atan2(tc, ta) - (float)Math.Atan2(c, a) + data.offsetRotation * MathUtils.degRad;
|
||||||
if (r > MathUtils.PI)
|
if (r > MathUtils.PI)
|
||||||
r -= MathUtils.PI2;
|
r -= MathUtils.PI2;
|
||||||
else if (r < -MathUtils.PI) r += MathUtils.PI2;
|
else if (r < -MathUtils.PI) r += MathUtils.PI2;
|
||||||
@ -95,15 +88,22 @@ namespace Spine {
|
|||||||
bone.d = sin * b + cos * d;
|
bone.d = sin * b + cos * d;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (translateMix > 0) {
|
||||||
|
float tempx, tempy;
|
||||||
|
target.LocalToWorld(data.offsetX, data.offsetY, out tempx, out tempy);
|
||||||
|
bone.worldX += (tempx - bone.worldX) * translateMix;
|
||||||
|
bone.worldY += (tempy - bone.worldY) * translateMix;
|
||||||
|
}
|
||||||
|
|
||||||
if (scaleMix > 0) {
|
if (scaleMix > 0) {
|
||||||
float bs = (float)Math.Sqrt(bone.a * bone.a + bone.c * bone.c);
|
float bs = (float)Math.Sqrt(bone.a * bone.a + bone.c * bone.c);
|
||||||
float ts = (float)Math.Sqrt(target.a * target.a + target.c * target.c);
|
float ts = (float)Math.Sqrt(ta * ta + tc * tc);
|
||||||
float s = bs > 0.00001f ? (bs + (ts - bs + offsetScaleX) * scaleMix) / bs : 0;
|
float s = bs > 0.00001f ? (bs + (ts - bs + data.offsetScaleX) * scaleMix) / bs : 0;
|
||||||
bone.a *= s;
|
bone.a *= s;
|
||||||
bone.c *= s;
|
bone.c *= s;
|
||||||
bs = (float)Math.Sqrt(bone.b * bone.b + bone.d * bone.d);
|
bs = (float)Math.Sqrt(bone.b * bone.b + bone.d * bone.d);
|
||||||
ts = (float)Math.Sqrt(target.b * target.b + target.d * target.d);
|
ts = (float)Math.Sqrt(tb * tb + td * td);
|
||||||
s = bs > 0.00001f ? (bs + (ts - bs + offsetScaleY) * scaleMix) / bs : 0;
|
s = bs > 0.00001f ? (bs + (ts - bs + data.offsetScaleY) * scaleMix) / bs : 0;
|
||||||
bone.b *= s;
|
bone.b *= s;
|
||||||
bone.d *= s;
|
bone.d *= s;
|
||||||
}
|
}
|
||||||
@ -111,22 +111,15 @@ namespace Spine {
|
|||||||
if (shearMix > 0) {
|
if (shearMix > 0) {
|
||||||
float b = bone.b, d = bone.d;
|
float b = bone.b, d = bone.d;
|
||||||
float by = MathUtils.Atan2(d, b);
|
float by = MathUtils.Atan2(d, b);
|
||||||
float r = MathUtils.Atan2(target.d, target.b) - MathUtils.Atan2(target.c, target.a) - (by - MathUtils.Atan2(bone.c, bone.a));
|
float r = MathUtils.Atan2(td, tb) - MathUtils.Atan2(tc, ta) - (by - MathUtils.Atan2(bone.c, bone.a));
|
||||||
if (r > MathUtils.PI)
|
if (r > MathUtils.PI)
|
||||||
r -= MathUtils.PI2;
|
r -= MathUtils.PI2;
|
||||||
else if (r < -MathUtils.PI) r += MathUtils.PI2;
|
else if (r < -MathUtils.PI) r += MathUtils.PI2;
|
||||||
r = by + (r + offsetShearY * MathUtils.degRad) * shearMix;
|
r = by + (r + data.offsetShearY * MathUtils.degRad) * shearMix;
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
float translateMix = this.translateMix;
|
|
||||||
if (translateMix > 0) {
|
|
||||||
float tx, ty;
|
|
||||||
target.LocalToWorld(offsetX, offsetY, out tx, out ty);
|
|
||||||
bone.worldX += (tx - bone.worldX) * translateMix;
|
|
||||||
bone.worldY += (ty - bone.worldY) * translateMix;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -30,17 +30,17 @@
|
|||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace Spine {
|
namespace Spine {
|
||||||
public class TransformConstraintData {
|
public class TransformConstraintData {
|
||||||
internal String name;
|
internal String name;
|
||||||
internal BoneData bone, target;
|
internal ExposedList<BoneData> bones = new ExposedList<BoneData>();
|
||||||
|
internal BoneData target;
|
||||||
internal float rotateMix, translateMix, scaleMix, shearMix;
|
internal float rotateMix, translateMix, scaleMix, shearMix;
|
||||||
internal float offsetRotation, offsetX, offsetY, offsetScaleX, offsetScaleY, offsetShearY;
|
internal float offsetRotation, offsetX, offsetY, offsetScaleX, offsetScaleY, offsetShearY;
|
||||||
|
|
||||||
public String Name { get { return name; } }
|
public String Name { get { return name; } }
|
||||||
public BoneData Bone { get { return bone; } set { bone = value; } }
|
public ExposedList<BoneData> Bones { get { return bones; } }
|
||||||
public BoneData Target { get { return target; } set { target = value; } }
|
public BoneData Target { get { return target; } set { target = value; } }
|
||||||
public float RotateMix { get { return rotateMix; } set { rotateMix = value; } }
|
public float RotateMix { get { return rotateMix; } set { rotateMix = value; } }
|
||||||
public float TranslateMix { get { return translateMix; } set { translateMix = value; } }
|
public float TranslateMix { get { return translateMix; } set { translateMix = value; } }
|
||||||
@ -55,7 +55,7 @@ namespace Spine {
|
|||||||
public float OffsetShearY { get { return offsetShearY; } set { offsetShearY = value; } }
|
public float OffsetShearY { get { return offsetShearY; } set { offsetShearY = value; } }
|
||||||
|
|
||||||
public TransformConstraintData (String name) {
|
public TransformConstraintData (String name) {
|
||||||
if (name == null) throw new ArgumentNullException("name cannot be null.");
|
if (name == null) throw new ArgumentNullException("name", "name cannot be null.");
|
||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -167,7 +167,7 @@ public class SkeletonJson {
|
|||||||
|
|
||||||
String targetName = constraintMap.getString("target");
|
String targetName = constraintMap.getString("target");
|
||||||
data.target = skeletonData.findBone(targetName);
|
data.target = skeletonData.findBone(targetName);
|
||||||
if (data.target == null) throw new SerializationException("Target bone not found: " + targetName);
|
if (data.target == null) throw new SerializationException("IK target bone not found: " + targetName);
|
||||||
|
|
||||||
data.bendDirection = constraintMap.getBoolean("bendPositive", true) ? 1 : -1;
|
data.bendDirection = constraintMap.getBoolean("bendPositive", true) ? 1 : -1;
|
||||||
data.mix = constraintMap.getFloat("mix", 1);
|
data.mix = constraintMap.getFloat("mix", 1);
|
||||||
@ -182,13 +182,13 @@ public class SkeletonJson {
|
|||||||
for (JsonValue boneMap = constraintMap.getChild("bones"); boneMap != null; boneMap = boneMap.next) {
|
for (JsonValue boneMap = constraintMap.getChild("bones"); boneMap != null; boneMap = boneMap.next) {
|
||||||
String boneName = boneMap.asString();
|
String boneName = boneMap.asString();
|
||||||
BoneData bone = skeletonData.findBone(boneName);
|
BoneData bone = skeletonData.findBone(boneName);
|
||||||
if (bone == null) throw new SerializationException("Path bone not found: " + boneName);
|
if (bone == null) throw new SerializationException("Transform constraint bone not found: " + boneName);
|
||||||
data.bones.add(bone);
|
data.bones.add(bone);
|
||||||
}
|
}
|
||||||
|
|
||||||
String targetName = constraintMap.getString("target");
|
String targetName = constraintMap.getString("target");
|
||||||
data.target = skeletonData.findBone(targetName);
|
data.target = skeletonData.findBone(targetName);
|
||||||
if (data.target == null) throw new SerializationException("Target bone not found: " + targetName);
|
if (data.target == null) throw new SerializationException("Transform constraint target bone not found: " + targetName);
|
||||||
|
|
||||||
data.offsetRotation = constraintMap.getFloat("rotation", 0);
|
data.offsetRotation = constraintMap.getFloat("rotation", 0);
|
||||||
data.offsetX = constraintMap.getFloat("x", 0) * scale;
|
data.offsetX = constraintMap.getFloat("x", 0) * scale;
|
||||||
@ -218,7 +218,7 @@ public class SkeletonJson {
|
|||||||
|
|
||||||
String targetName = constraintMap.getString("target");
|
String targetName = constraintMap.getString("target");
|
||||||
data.target = skeletonData.findSlot(targetName);
|
data.target = skeletonData.findSlot(targetName);
|
||||||
if (data.target == null) throw new SerializationException("Target slot not found: " + targetName);
|
if (data.target == null) throw new SerializationException("Path target slot not found: " + targetName);
|
||||||
|
|
||||||
data.positionMode = PositionMode.valueOf(constraintMap.getString("positionMode", "percent"));
|
data.positionMode = PositionMode.valueOf(constraintMap.getString("positionMode", "percent"));
|
||||||
data.spacingMode = SpacingMode.valueOf(constraintMap.getString("spacingMode", "length"));
|
data.spacingMode = SpacingMode.valueOf(constraintMap.getString("spacingMode", "length"));
|
||||||
|
|||||||
@ -10,7 +10,7 @@ The Spine Runtimes are developed with the intent to be used with data exported f
|
|||||||
|
|
||||||
## Spine version
|
## Spine version
|
||||||
|
|
||||||
spine-monogame works with data exported from Spine 3.2.01. Updating spine-monogame to [v3.3](https://github.com/EsotericSoftware/spine-runtimes/issues/613) is in progress.
|
spine-monogame works with data exported from Spine 3.3.07.
|
||||||
|
|
||||||
spine-monogame supports all Spine features.
|
spine-monogame supports all Spine features.
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -50,9 +50,27 @@ public class SpineboyBeginnerModel : MonoBehaviour {
|
|||||||
IEnumerator JumpRoutine () {
|
IEnumerator JumpRoutine () {
|
||||||
if (state == SpineBeginnerBodyState.Jumping) yield break; // Don't jump when already jumping.
|
if (state == SpineBeginnerBodyState.Jumping) yield break; // Don't jump when already jumping.
|
||||||
|
|
||||||
// Fake jumping.
|
|
||||||
state = SpineBeginnerBodyState.Jumping;
|
state = SpineBeginnerBodyState.Jumping;
|
||||||
yield return new WaitForSeconds(1.2f);
|
|
||||||
|
// Terribly-coded Fake jumping.
|
||||||
|
{
|
||||||
|
var pos = transform.localPosition;
|
||||||
|
const float jumpTime = 1.2f;
|
||||||
|
const float half = jumpTime * 0.5f;
|
||||||
|
const float jumpPower = 20f;
|
||||||
|
for (float t = 0; t < half; t += Time.deltaTime) {
|
||||||
|
float d = jumpPower * (half - t);
|
||||||
|
transform.Translate((d * Time.deltaTime) * Vector3.up);
|
||||||
|
yield return null;
|
||||||
|
}
|
||||||
|
for (float t = 0; t < half; t += Time.deltaTime) {
|
||||||
|
float d = jumpPower * t;
|
||||||
|
transform.Translate((d * Time.deltaTime) * Vector3.down);
|
||||||
|
yield return null;
|
||||||
|
}
|
||||||
|
transform.localPosition = pos;
|
||||||
|
}
|
||||||
|
|
||||||
state = SpineBeginnerBodyState.Idle;
|
state = SpineBeginnerBodyState.Idle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
@ -1,6 +1,6 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: 6bc52290ef03f2846ba38d67e2823598
|
guid: 6bc52290ef03f2846ba38d67e2823598
|
||||||
timeCreated: 1455501336
|
timeCreated: 1467205225
|
||||||
licenseType: Free
|
licenseType: Free
|
||||||
TextureImporter:
|
TextureImporter:
|
||||||
fileIDToRecycleName: {}
|
fileIDToRecycleName: {}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: 12c126994123f12468cf4c5a2684078a
|
guid: 12c126994123f12468cf4c5a2684078a
|
||||||
timeCreated: 1455501336
|
timeCreated: 1467205225
|
||||||
licenseType: Free
|
licenseType: Free
|
||||||
TextureImporter:
|
TextureImporter:
|
||||||
fileIDToRecycleName: {}
|
fileIDToRecycleName: {}
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1 +1,36 @@
|
|||||||
{"skeleton":{"hash":"LxkXoqW3SPQ5weFYd4V71R86WUk","spine":"2.0.20","width":868,"height":322},"bones":[{"name":"root"},{"name":"L_Eye","parent":"root","x":-223.18,"y":2.99},{"name":"R_Eye","parent":"root","x":237.32,"y":6.4}],"slots":[{"name":"EyeWhite","bone":"root","attachment":"EyeWhite"},{"name":"R_Eye","bone":"R_Eye","attachment":"R_Eye"},{"name":"L_Eye","bone":"L_Eye","attachment":"L_Eye"},{"name":"EyeLines","bone":"root","attachment":"EyeLines"}],"skins":{"default":{"EyeLines":{"EyeLines":{"y":28,"width":868,"height":322}},"EyeWhite":{"EyeWhite":{"x":3,"y":2,"width":700,"height":148}},"L_Eye":{"L_Eye":{"x":-0.82,"y":2,"width":148,"height":148}},"R_Eye":{"R_Eye":{"x":0.67,"y":-1.4,"width":148,"height":148}}}}}
|
{
|
||||||
|
"skeleton": {
|
||||||
|
"hash": "4JK7uGWbzO7qeQSyyuOyKLXQ5oI",
|
||||||
|
"spine": "3.3.07",
|
||||||
|
"width": 868,
|
||||||
|
"height": 322,
|
||||||
|
"images": "C:\\Users\\John Eric\\Desktop\\old exports"
|
||||||
|
},
|
||||||
|
"bones": [
|
||||||
|
{ "name": "root" },
|
||||||
|
{ "name": "L_Eye", "parent": "root", "x": -223.18, "y": 2.99 },
|
||||||
|
{ "name": "R_Eye", "parent": "root", "x": 237.32, "y": 6.4 }
|
||||||
|
],
|
||||||
|
"slots": [
|
||||||
|
{ "name": "EyeWhite", "bone": "root", "attachment": "EyeWhite" },
|
||||||
|
{ "name": "R_Eye", "bone": "R_Eye", "attachment": "R_Eye" },
|
||||||
|
{ "name": "L_Eye", "bone": "L_Eye", "attachment": "L_Eye" },
|
||||||
|
{ "name": "EyeLines", "bone": "root", "attachment": "EyeLines" }
|
||||||
|
],
|
||||||
|
"skins": {
|
||||||
|
"default": {
|
||||||
|
"EyeLines": {
|
||||||
|
"EyeLines": { "y": 28, "width": 868, "height": 322 }
|
||||||
|
},
|
||||||
|
"EyeWhite": {
|
||||||
|
"EyeWhite": { "x": 3, "y": 2, "width": 700, "height": 148 }
|
||||||
|
},
|
||||||
|
"L_Eye": {
|
||||||
|
"L_Eye": { "x": -0.82, "y": 2, "width": 148, "height": 148 }
|
||||||
|
},
|
||||||
|
"R_Eye": {
|
||||||
|
"R_Eye": { "x": 0.67, "y": -1.4, "width": 148, "height": 148 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,6 +1,6 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: 49441e5a1682e564694545bd9b509785
|
guid: 49441e5a1682e564694545bd9b509785
|
||||||
timeCreated: 1455501336
|
timeCreated: 1467205225
|
||||||
licenseType: Free
|
licenseType: Free
|
||||||
TextureImporter:
|
TextureImporter:
|
||||||
fileIDToRecycleName: {}
|
fileIDToRecycleName: {}
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,6 +1,6 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: ddb89f63d0296cf4f8572b0448bb6b30
|
guid: ddb89f63d0296cf4f8572b0448bb6b30
|
||||||
timeCreated: 1455501337
|
timeCreated: 1467205225
|
||||||
licenseType: Free
|
licenseType: Free
|
||||||
TextureImporter:
|
TextureImporter:
|
||||||
fileIDToRecycleName: {}
|
fileIDToRecycleName: {}
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
@ -1,6 +1,6 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: 57b57f94df266f94ea0981915a4472e1
|
guid: 57b57f94df266f94ea0981915a4472e1
|
||||||
timeCreated: 1455501336
|
timeCreated: 1467205225
|
||||||
licenseType: Free
|
licenseType: Free
|
||||||
TextureImporter:
|
TextureImporter:
|
||||||
fileIDToRecycleName: {}
|
fileIDToRecycleName: {}
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
@ -1,18 +1,18 @@
|
|||||||
{
|
{
|
||||||
"skeleton": { "hash": "uZfvh80BNvvngM3EGMfAkHebg00", "spine": "2.1.08", "width": 147.68, "height": 268.92, "images": "./images/" },
|
"skeleton": { "hash": "Nxia9637znam+9FXwv6fILb3hpo", "spine": "3.3.07", "width": 0, "height": 0, "images": "./images/" },
|
||||||
"bones": [
|
"bones": [
|
||||||
{ "name": "Root" },
|
{ "name": "Root" },
|
||||||
{ "name": "Hip", "parent": "Root", "x": -0.93, "y": 73.4 },
|
{ "name": "Hip", "parent": "Root", "x": -0.93, "y": 73.4 },
|
||||||
{ "name": "Body", "parent": "Hip", "length": 60.98, "x": 2.46, "y": -7.69, "rotation": 89.52 },
|
{ "name": "Body", "parent": "Hip", "length": 60.98, "rotation": 89.52, "x": 2.46, "y": -7.69 },
|
||||||
{ "name": "Leg", "parent": "Hip", "length": 31.39, "x": -20.32, "y": -13.85, "rotation": -105.82 },
|
{ "name": "Arm", "parent": "Body", "length": 51.63, "rotation": 166.67, "x": 49.91, "y": 37.34 },
|
||||||
{ "name": "Leg2", "parent": "Hip", "length": 31.09, "x": 22.48, "y": -12.01, "rotation": -74.17 },
|
{ "name": "Arm2", "parent": "Body", "length": 52.61, "rotation": -157.17, "x": 53.81, "y": -28.52 },
|
||||||
{ "name": "Arm", "parent": "Body", "length": 51.63, "x": 49.91, "y": 37.35, "rotation": 166.67 },
|
{ "name": "Leg", "parent": "Hip", "length": 31.39, "rotation": -105.82, "x": -20.32, "y": -13.85 },
|
||||||
{ "name": "Arm2", "parent": "Body", "length": 52.61, "x": 53.81, "y": -28.52, "rotation": -157.17 },
|
{ "name": "Feet", "parent": "Leg", "length": 15.4, "rotation": 14.56, "x": 39.56, "y": 1.59 },
|
||||||
{ "name": "Feet", "parent": "Leg", "length": 15.4, "x": 39.56, "y": 1.59, "rotation": 14.56 },
|
{ "name": "Leg2", "parent": "Hip", "length": 31.09, "rotation": -74.17, "x": 22.48, "y": -12.01 },
|
||||||
{ "name": "Feet2", "parent": "Leg2", "length": 12.32, "x": 41.33, "y": 0.12, "rotation": -17.19 },
|
{ "name": "Feet2", "parent": "Leg2", "length": 12.32, "rotation": -17.19, "x": 41.33, "y": 0.12 },
|
||||||
{ "name": "Head", "parent": "Body", "length": 65.29, "x": 73.6, "y": 1.09, "rotation": -88.23 },
|
{ "name": "Head", "parent": "Body", "length": 65.29, "rotation": -88.23, "x": 73.6, "y": 1.09 },
|
||||||
{ "name": "Shield", "parent": "Arm", "x": 45.01, "y": -2.1, "rotation": 123.56 },
|
{ "name": "Shield", "parent": "Arm", "rotation": 123.56, "x": 45.01, "y": -2.09 },
|
||||||
{ "name": "Weapon", "parent": "Arm2", "length": 137.65, "x": 48.2, "y": 12.78, "rotation": 92.5 }
|
{ "name": "Weapon", "parent": "Arm2", "length": 137.64, "rotation": 92.5, "x": 48.2, "y": 12.78 }
|
||||||
],
|
],
|
||||||
"slots": [
|
"slots": [
|
||||||
{ "name": "Arm2", "bone": "Arm2", "attachment": "Arm2" },
|
{ "name": "Arm2", "bone": "Arm2", "attachment": "Arm2" },
|
||||||
@ -61,7 +61,7 @@
|
|||||||
"Leg": { "name": "leg", "path": "White/leg", "x": 16.86, "y": -4.3, "rotation": 104.82, "width": 48, "height": 55 }
|
"Leg": { "name": "leg", "path": "White/leg", "x": 16.86, "y": -4.3, "rotation": 104.82, "width": 48, "height": 55 }
|
||||||
},
|
},
|
||||||
"Leg2": {
|
"Leg2": {
|
||||||
"Leg2": { "name": "leg 2", "path": "White/leg 2", "x": 16.44, "y": -2.11, "rotation": 74.17, "width": 50, "height": 58 }
|
"Leg2": { "name": "leg 2", "path": "White/leg 2", "x": 16.44, "y": -2.1, "rotation": 74.17, "width": 50, "height": 58 }
|
||||||
},
|
},
|
||||||
"Mouth": {
|
"Mouth": {
|
||||||
"Closed": { "path": "White/mouth", "x": 10.96, "y": 3.69, "rotation": -1.29, "width": 28, "height": 21 }
|
"Closed": { "path": "White/mouth", "x": 10.96, "y": 3.69, "rotation": -1.29, "width": 28, "height": 21 }
|
||||||
@ -239,11 +239,11 @@
|
|||||||
"curve": [ 0.25, 0, 0.75, 1 ]
|
"curve": [ 0.25, 0, 0.75, 1 ]
|
||||||
},
|
},
|
||||||
{ "time": 0.3333, "x": 4.5, "y": -0.72 },
|
{ "time": 0.3333, "x": 4.5, "y": -0.72 },
|
||||||
{ "time": 0.4666, "x": -1.04, "y": -2.35, "curve": "stepped" },
|
{ "time": 0.4666, "x": -1.04, "y": -2.34, "curve": "stepped" },
|
||||||
{
|
{
|
||||||
"time": 0.6,
|
"time": 0.6,
|
||||||
"x": -1.04,
|
"x": -1.04,
|
||||||
"y": -2.35,
|
"y": -2.34,
|
||||||
"curve": [ 0.25, 0, 0.75, 1 ]
|
"curve": [ 0.25, 0, 0.75, 1 ]
|
||||||
},
|
},
|
||||||
{ "time": 0.8333, "x": 0, "y": 0 }
|
{ "time": 0.8333, "x": 0, "y": 0 }
|
||||||
@ -345,7 +345,7 @@
|
|||||||
"translate": [
|
"translate": [
|
||||||
{ "time": 0, "x": 0, "y": 0 },
|
{ "time": 0, "x": 0, "y": 0 },
|
||||||
{ "time": 0.2, "x": -50.02, "y": -6.59 },
|
{ "time": 0.2, "x": -50.02, "y": -6.59 },
|
||||||
{ "time": 0.3333, "x": -83.36, "y": -37.67 }
|
{ "time": 0.3333, "x": -83.36, "y": -37.66 }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"Body": {
|
"Body": {
|
||||||
@ -491,7 +491,7 @@
|
|||||||
"y": 0,
|
"y": 0,
|
||||||
"curve": [ 0.25, 0, 0.758, 0.67 ]
|
"curve": [ 0.25, 0, 0.758, 0.67 ]
|
||||||
},
|
},
|
||||||
{ "time": 0.3333, "x": -4.47, "y": 3.29 }
|
{ "time": 0.3333, "x": -4.46, "y": 3.29 }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"Leg2": {
|
"Leg2": {
|
||||||
@ -686,7 +686,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"time": 0.3666,
|
"time": 0.3666,
|
||||||
"angle": -2.34,
|
"angle": -2.33,
|
||||||
"curve": [ 0.25, 0, 0.75, 1 ]
|
"curve": [ 0.25, 0, 0.75, 1 ]
|
||||||
},
|
},
|
||||||
{ "time": 0.8666, "angle": 0 },
|
{ "time": 0.8666, "angle": 0 },
|
||||||
@ -1518,17 +1518,17 @@
|
|||||||
{
|
{
|
||||||
"time": 0,
|
"time": 0,
|
||||||
"x": -0.84,
|
"x": -0.84,
|
||||||
"y": -4.45,
|
"y": -4.44,
|
||||||
"curve": [ 0.25, 0, 0.75, 1 ]
|
"curve": [ 0.25, 0, 0.75, 1 ]
|
||||||
},
|
},
|
||||||
{ "time": 0.2, "x": -1.03, "y": -4.7, "curve": "stepped" },
|
{ "time": 0.2, "x": -1.03, "y": -4.69, "curve": "stepped" },
|
||||||
{
|
{
|
||||||
"time": 0.5333,
|
"time": 0.5333,
|
||||||
"x": -1.03,
|
"x": -1.03,
|
||||||
"y": -4.7,
|
"y": -4.69,
|
||||||
"curve": [ 0.25, 0, 0.75, 1 ]
|
"curve": [ 0.25, 0, 0.75, 1 ]
|
||||||
},
|
},
|
||||||
{ "time": 0.6666, "x": -0.84, "y": -4.45 }
|
{ "time": 0.6666, "x": -0.84, "y": -4.44 }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"Feet2": {
|
"Feet2": {
|
||||||
|
|||||||
Binary file not shown.
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"skeleton": { "hash": "IKLl/62j+Y1bsQ8rdHoVK9PDip8", "spine": "2.1.27", "width": 250, "height": 60, "images": "./images/" },
|
"skeleton": { "hash": "C69Pg+RG3DYyHmt9bOPYVrESJBQ", "spine": "3.3.07", "width": 250, "height": 60, "images": "./images/" },
|
||||||
"bones": [
|
"bones": [
|
||||||
{ "name": "root" },
|
{ "name": "root" },
|
||||||
{ "name": "Bar", "parent": "root", "x": -112.29 }
|
{ "name": "Bar", "parent": "root", "x": -112.29 }
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: a11301aad15ed6b4995485a02a81b132
|
guid: a11301aad15ed6b4995485a02a81b132
|
||||||
timeCreated: 1455501336
|
timeCreated: 1467205225
|
||||||
licenseType: Free
|
licenseType: Free
|
||||||
TextureImporter:
|
TextureImporter:
|
||||||
fileIDToRecycleName: {}
|
fileIDToRecycleName: {}
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,4 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 3586e5ccd2041c24eb20eb4764168abd
|
|
||||||
TextScriptImporter:
|
|
||||||
userData:
|
|
||||||
File diff suppressed because one or more lines are too long
@ -1,4 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: f3a3248bc50115241ae81702fde448eb
|
|
||||||
TextScriptImporter:
|
|
||||||
userData:
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 157 KiB |
Binary file not shown.
@ -1,4 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: df2c4ad0c6709fd4f9b1c19ab43878ae
|
|
||||||
NativeFormatImporter:
|
|
||||||
userData:
|
|
||||||
Binary file not shown.
@ -1,4 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 64a66fecd89237b478156e7cc4d2da4a
|
|
||||||
NativeFormatImporter:
|
|
||||||
userData:
|
|
||||||
Binary file not shown.
@ -1,4 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 066917a2cc5e8824b9b7e2944feee6f1
|
|
||||||
NativeFormatImporter:
|
|
||||||
userData:
|
|
||||||
@ -1,292 +1,292 @@
|
|||||||
|
|
||||||
goblins-mesh.png
|
goblins.png
|
||||||
size: 1024,128
|
size: 795,142
|
||||||
format: RGBA8888
|
format: RGBA8888
|
||||||
filter: Linear,Linear
|
filter: Linear,Linear
|
||||||
repeat: none
|
repeat: none
|
||||||
dagger
|
dagger
|
||||||
rotate: true
|
rotate: false
|
||||||
xy: 372, 100
|
xy: 1, 33
|
||||||
size: 26, 108
|
size: 26, 108
|
||||||
orig: 26, 108
|
orig: 26, 108
|
||||||
offset: 0, 0
|
offset: 0, 0
|
||||||
index: -1
|
index: -1
|
||||||
goblin/eyes-closed
|
goblin/eyes-closed
|
||||||
rotate: false
|
rotate: false
|
||||||
xy: 2, 7
|
xy: 760, 129
|
||||||
size: 34, 12
|
size: 34, 12
|
||||||
orig: 34, 12
|
orig: 34, 12
|
||||||
offset: 0, 0
|
offset: 0, 0
|
||||||
index: -1
|
index: -1
|
||||||
goblin/head
|
goblin/head
|
||||||
rotate: false
|
rotate: true
|
||||||
xy: 107, 36
|
xy: 110, 38
|
||||||
size: 103, 66
|
size: 103, 66
|
||||||
orig: 103, 66
|
orig: 103, 66
|
||||||
offset: 0, 0
|
offset: 0, 0
|
||||||
index: -1
|
index: -1
|
||||||
goblin/left-arm
|
goblin/left-arm
|
||||||
rotate: false
|
rotate: true
|
||||||
xy: 901, 56
|
xy: 659, 7
|
||||||
size: 37, 35
|
size: 37, 35
|
||||||
orig: 37, 35
|
orig: 37, 35
|
||||||
offset: 0, 0
|
offset: 0, 0
|
||||||
index: -1
|
index: -1
|
||||||
goblin/left-foot
|
goblin/left-foot
|
||||||
rotate: false
|
rotate: false
|
||||||
xy: 929, 95
|
xy: 1, 1
|
||||||
size: 65, 31
|
size: 65, 31
|
||||||
orig: 65, 31
|
orig: 65, 31
|
||||||
offset: 0, 0
|
offset: 0, 0
|
||||||
index: -1
|
index: -1
|
||||||
goblin/left-hand
|
goblin/left-hand
|
||||||
rotate: false
|
rotate: false
|
||||||
xy: 452, 2
|
xy: 347, 3
|
||||||
size: 36, 41
|
size: 36, 41
|
||||||
orig: 36, 41
|
orig: 36, 41
|
||||||
offset: 0, 0
|
offset: 0, 0
|
||||||
index: -1
|
index: -1
|
||||||
goblin/left-lower-leg
|
goblin/left-lower-leg
|
||||||
rotate: true
|
rotate: false
|
||||||
xy: 713, 93
|
xy: 420, 71
|
||||||
size: 33, 70
|
size: 33, 70
|
||||||
orig: 33, 70
|
orig: 33, 70
|
||||||
offset: 0, 0
|
offset: 0, 0
|
||||||
index: -1
|
index: -1
|
||||||
goblin/left-shoulder
|
goblin/left-shoulder
|
||||||
rotate: false
|
rotate: true
|
||||||
xy: 610, 44
|
xy: 684, 48
|
||||||
size: 29, 44
|
size: 29, 44
|
||||||
orig: 29, 44
|
orig: 29, 44
|
||||||
offset: 0, 0
|
offset: 0, 0
|
||||||
index: -1
|
index: -1
|
||||||
goblin/left-upper-leg
|
goblin/left-upper-leg
|
||||||
rotate: true
|
rotate: false
|
||||||
xy: 638, 93
|
xy: 315, 68
|
||||||
size: 33, 73
|
size: 33, 73
|
||||||
orig: 33, 73
|
orig: 33, 73
|
||||||
offset: 0, 0
|
offset: 0, 0
|
||||||
index: -1
|
index: -1
|
||||||
goblin/neck
|
goblin/neck
|
||||||
rotate: false
|
rotate: false
|
||||||
xy: 490, 2
|
xy: 384, 3
|
||||||
size: 36, 41
|
size: 36, 41
|
||||||
orig: 36, 41
|
orig: 36, 41
|
||||||
offset: 0, 0
|
offset: 0, 0
|
||||||
index: -1
|
index: -1
|
||||||
goblin/pelvis
|
goblin/pelvis
|
||||||
rotate: false
|
rotate: false
|
||||||
xy: 482, 45
|
xy: 221, 1
|
||||||
size: 62, 43
|
size: 62, 43
|
||||||
orig: 62, 43
|
orig: 62, 43
|
||||||
offset: 0, 0
|
offset: 0, 0
|
||||||
index: -1
|
index: -1
|
||||||
goblin/right-arm
|
goblin/right-arm
|
||||||
rotate: true
|
rotate: false
|
||||||
xy: 690, 2
|
xy: 732, 91
|
||||||
size: 23, 50
|
size: 23, 50
|
||||||
orig: 23, 50
|
orig: 23, 50
|
||||||
offset: 0, 0
|
offset: 0, 0
|
||||||
index: -1
|
index: -1
|
||||||
goblin/right-foot
|
goblin/right-foot
|
||||||
rotate: false
|
rotate: true
|
||||||
xy: 771, 58
|
xy: 624, 78
|
||||||
size: 63, 33
|
size: 63, 33
|
||||||
orig: 63, 33
|
orig: 63, 33
|
||||||
offset: 0, 0
|
offset: 0, 0
|
||||||
index: -1
|
index: -1
|
||||||
goblin/right-hand
|
goblin/right-hand
|
||||||
rotate: false
|
rotate: false
|
||||||
xy: 940, 56
|
xy: 585, 7
|
||||||
size: 36, 37
|
size: 36, 37
|
||||||
orig: 36, 37
|
orig: 36, 37
|
||||||
offset: 0, 0
|
offset: 0, 0
|
||||||
index: -1
|
index: -1
|
||||||
goblin/right-lower-leg
|
goblin/right-lower-leg
|
||||||
rotate: true
|
rotate: true
|
||||||
xy: 482, 90
|
xy: 67, 1
|
||||||
size: 36, 76
|
size: 36, 76
|
||||||
orig: 36, 76
|
orig: 36, 76
|
||||||
offset: 0, 0
|
offset: 0, 0
|
||||||
index: -1
|
index: -1
|
||||||
goblin/right-shoulder
|
goblin/right-shoulder
|
||||||
rotate: true
|
rotate: true
|
||||||
xy: 602, 3
|
xy: 493, 5
|
||||||
size: 39, 45
|
size: 39, 45
|
||||||
orig: 39, 45
|
orig: 39, 45
|
||||||
offset: 0, 0
|
offset: 0, 0
|
||||||
index: -1
|
index: -1
|
||||||
goblin/right-upper-leg
|
goblin/right-upper-leg
|
||||||
rotate: true
|
rotate: false
|
||||||
xy: 641, 57
|
xy: 554, 78
|
||||||
size: 34, 63
|
size: 34, 63
|
||||||
orig: 34, 63
|
orig: 34, 63
|
||||||
offset: 0, 0
|
offset: 0, 0
|
||||||
index: -1
|
index: -1
|
||||||
goblin/torso
|
goblin/torso
|
||||||
rotate: true
|
rotate: false
|
||||||
xy: 212, 34
|
xy: 177, 45
|
||||||
size: 68, 96
|
size: 68, 96
|
||||||
orig: 68, 96
|
orig: 68, 96
|
||||||
offset: 0, 0
|
offset: 0, 0
|
||||||
index: -1
|
index: -1
|
||||||
goblin/undie-straps
|
goblin/undie-straps
|
||||||
rotate: false
|
rotate: true
|
||||||
xy: 380, 5
|
xy: 692, 86
|
||||||
size: 55, 19
|
size: 55, 19
|
||||||
orig: 55, 19
|
orig: 55, 19
|
||||||
offset: 0, 0
|
offset: 0, 0
|
||||||
index: -1
|
index: -1
|
||||||
goblin/undies
|
goblin/undies
|
||||||
rotate: false
|
rotate: false
|
||||||
xy: 174, 5
|
xy: 756, 99
|
||||||
size: 36, 29
|
size: 36, 29
|
||||||
orig: 36, 29
|
orig: 36, 29
|
||||||
offset: 0, 0
|
offset: 0, 0
|
||||||
index: -1
|
index: -1
|
||||||
goblingirl/eyes-closed
|
goblingirl/eyes-closed
|
||||||
rotate: false
|
rotate: true
|
||||||
xy: 269, 11
|
xy: 729, 48
|
||||||
size: 37, 21
|
size: 37, 21
|
||||||
orig: 37, 21
|
orig: 37, 21
|
||||||
offset: 0, 0
|
offset: 0, 0
|
||||||
index: -1
|
index: -1
|
||||||
goblingirl/head
|
goblingirl/head
|
||||||
rotate: false
|
rotate: true
|
||||||
xy: 2, 21
|
xy: 28, 38
|
||||||
size: 103, 81
|
size: 103, 81
|
||||||
orig: 103, 81
|
orig: 103, 81
|
||||||
offset: 0, 0
|
offset: 0, 0
|
||||||
index: -1
|
index: -1
|
||||||
goblingirl/left-arm
|
goblingirl/left-arm
|
||||||
rotate: true
|
rotate: true
|
||||||
xy: 978, 56
|
xy: 724, 10
|
||||||
size: 37, 35
|
size: 37, 35
|
||||||
orig: 37, 35
|
orig: 37, 35
|
||||||
offset: 0, 0
|
offset: 0, 0
|
||||||
index: -1
|
index: -1
|
||||||
goblingirl/left-foot
|
goblingirl/left-foot
|
||||||
rotate: false
|
rotate: true
|
||||||
xy: 107, 3
|
xy: 522, 76
|
||||||
size: 65, 31
|
size: 65, 31
|
||||||
orig: 65, 31
|
orig: 65, 31
|
||||||
offset: 0, 0
|
offset: 0, 0
|
||||||
index: -1
|
index: -1
|
||||||
goblingirl/left-hand
|
goblingirl/left-hand
|
||||||
rotate: false
|
rotate: false
|
||||||
xy: 565, 2
|
xy: 457, 4
|
||||||
size: 35, 40
|
size: 35, 40
|
||||||
orig: 35, 40
|
orig: 35, 40
|
||||||
offset: 0, 0
|
offset: 0, 0
|
||||||
index: -1
|
index: -1
|
||||||
goblingirl/left-lower-leg
|
goblingirl/left-lower-leg
|
||||||
rotate: true
|
rotate: false
|
||||||
xy: 785, 93
|
xy: 454, 71
|
||||||
size: 33, 70
|
size: 33, 70
|
||||||
orig: 33, 70
|
orig: 33, 70
|
||||||
offset: 0, 0
|
offset: 0, 0
|
||||||
index: -1
|
index: -1
|
||||||
goblingirl/left-shoulder
|
goblingirl/left-shoulder
|
||||||
rotate: true
|
rotate: false
|
||||||
xy: 690, 27
|
xy: 695, 1
|
||||||
size: 28, 46
|
size: 28, 46
|
||||||
orig: 28, 46
|
orig: 28, 46
|
||||||
offset: 0, 0
|
offset: 0, 0
|
||||||
index: -1
|
index: -1
|
||||||
goblingirl/left-upper-leg
|
goblingirl/left-upper-leg
|
||||||
rotate: true
|
rotate: false
|
||||||
xy: 857, 93
|
xy: 488, 71
|
||||||
size: 33, 70
|
size: 33, 70
|
||||||
orig: 33, 70
|
orig: 33, 70
|
||||||
offset: 0, 0
|
offset: 0, 0
|
||||||
index: -1
|
index: -1
|
||||||
goblingirl/neck
|
goblingirl/neck
|
||||||
rotate: false
|
rotate: false
|
||||||
xy: 528, 2
|
xy: 421, 3
|
||||||
size: 35, 41
|
size: 35, 41
|
||||||
orig: 35, 41
|
orig: 35, 41
|
||||||
offset: 0, 0
|
offset: 0, 0
|
||||||
index: -1
|
index: -1
|
||||||
goblingirl/pelvis
|
goblingirl/pelvis
|
||||||
rotate: false
|
rotate: false
|
||||||
xy: 546, 45
|
xy: 284, 1
|
||||||
size: 62, 43
|
size: 62, 43
|
||||||
orig: 62, 43
|
orig: 62, 43
|
||||||
offset: 0, 0
|
offset: 0, 0
|
||||||
index: -1
|
index: -1
|
||||||
goblingirl/right-arm
|
goblingirl/right-arm
|
||||||
rotate: false
|
rotate: false
|
||||||
xy: 452, 48
|
xy: 756, 48
|
||||||
size: 28, 50
|
size: 28, 50
|
||||||
orig: 28, 50
|
orig: 28, 50
|
||||||
offset: 0, 0
|
offset: 0, 0
|
||||||
index: -1
|
index: -1
|
||||||
goblingirl/right-foot
|
goblingirl/right-foot
|
||||||
rotate: false
|
rotate: true
|
||||||
xy: 836, 58
|
xy: 658, 78
|
||||||
size: 63, 33
|
size: 63, 33
|
||||||
orig: 63, 33
|
orig: 63, 33
|
||||||
offset: 0, 0
|
offset: 0, 0
|
||||||
index: -1
|
index: -1
|
||||||
goblingirl/right-hand
|
goblingirl/right-hand
|
||||||
rotate: true
|
rotate: false
|
||||||
xy: 771, 20
|
xy: 622, 7
|
||||||
size: 36, 37
|
size: 36, 37
|
||||||
orig: 36, 37
|
orig: 36, 37
|
||||||
offset: 0, 0
|
offset: 0, 0
|
||||||
index: -1
|
index: -1
|
||||||
goblingirl/right-lower-leg
|
goblingirl/right-lower-leg
|
||||||
rotate: true
|
rotate: true
|
||||||
xy: 560, 90
|
xy: 144, 1
|
||||||
size: 36, 76
|
size: 36, 76
|
||||||
orig: 36, 76
|
orig: 36, 76
|
||||||
offset: 0, 0
|
offset: 0, 0
|
||||||
index: -1
|
index: -1
|
||||||
goblingirl/right-shoulder
|
goblingirl/right-shoulder
|
||||||
rotate: false
|
rotate: true
|
||||||
xy: 649, 10
|
xy: 539, 5
|
||||||
size: 39, 45
|
size: 39, 45
|
||||||
orig: 39, 45
|
orig: 39, 45
|
||||||
offset: 0, 0
|
offset: 0, 0
|
||||||
index: -1
|
index: -1
|
||||||
goblingirl/right-upper-leg
|
goblingirl/right-upper-leg
|
||||||
rotate: true
|
rotate: false
|
||||||
xy: 706, 57
|
xy: 589, 78
|
||||||
size: 34, 63
|
size: 34, 63
|
||||||
orig: 34, 63
|
orig: 34, 63
|
||||||
offset: 0, 0
|
offset: 0, 0
|
||||||
index: -1
|
index: -1
|
||||||
goblingirl/torso
|
goblingirl/torso
|
||||||
rotate: false
|
rotate: false
|
||||||
xy: 310, 2
|
xy: 246, 45
|
||||||
size: 68, 96
|
size: 68, 96
|
||||||
orig: 68, 96
|
orig: 68, 96
|
||||||
offset: 0, 0
|
offset: 0, 0
|
||||||
index: -1
|
index: -1
|
||||||
goblingirl/undie-straps
|
goblingirl/undie-straps
|
||||||
rotate: false
|
rotate: true
|
||||||
xy: 212, 13
|
xy: 712, 86
|
||||||
size: 55, 19
|
size: 55, 19
|
||||||
orig: 55, 19
|
orig: 55, 19
|
||||||
offset: 0, 0
|
offset: 0, 0
|
||||||
index: -1
|
index: -1
|
||||||
goblingirl/undies
|
goblingirl/undies
|
||||||
rotate: false
|
rotate: true
|
||||||
xy: 810, 27
|
xy: 760, 11
|
||||||
size: 36, 29
|
size: 36, 29
|
||||||
orig: 36, 29
|
orig: 36, 29
|
||||||
offset: 0, 0
|
offset: 0, 0
|
||||||
index: -1
|
index: -1
|
||||||
shield
|
shield
|
||||||
rotate: false
|
rotate: false
|
||||||
xy: 380, 26
|
xy: 349, 69
|
||||||
size: 70, 72
|
size: 70, 72
|
||||||
orig: 70, 72
|
orig: 70, 72
|
||||||
offset: 0, 0
|
offset: 0, 0
|
||||||
index: -1
|
index: -1
|
||||||
spear
|
spear
|
||||||
rotate: true
|
rotate: true
|
||||||
xy: 2, 104
|
xy: 315, 45
|
||||||
size: 22, 368
|
size: 22, 368
|
||||||
orig: 22, 368
|
orig: 22, 368
|
||||||
offset: 0, 0
|
offset: 0, 0
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 179f09b47e5402545a1aa69bf5cb2cba
|
||||||
|
timeCreated: 1467115504
|
||||||
|
licenseType: Free
|
||||||
|
TextScriptImporter:
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
1060
spine-unity/Assets/Examples/Spine/Goblins/goblins.json
Normal file
1060
spine-unity/Assets/Examples/Spine/Goblins/goblins.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: c3921acb20cbc25418859f1b213d3d3f
|
||||||
|
timeCreated: 1467115504
|
||||||
|
licenseType: Free
|
||||||
|
TextScriptImporter:
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
BIN
spine-unity/Assets/Examples/Spine/Goblins/goblins.png
Normal file
BIN
spine-unity/Assets/Examples/Spine/Goblins/goblins.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 160 KiB |
@ -1,6 +1,6 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: 803c2e614a63081439fde6276d110661
|
guid: 5fb7efec30c79cb46a705e0d04debb04
|
||||||
timeCreated: 1455501336
|
timeCreated: 1467205225
|
||||||
licenseType: Free
|
licenseType: Free
|
||||||
TextureImporter:
|
TextureImporter:
|
||||||
fileIDToRecycleName: {}
|
fileIDToRecycleName: {}
|
||||||
@ -30,15 +30,15 @@ TextureImporter:
|
|||||||
maxTextureSize: 2048
|
maxTextureSize: 2048
|
||||||
textureSettings:
|
textureSettings:
|
||||||
filterMode: -1
|
filterMode: -1
|
||||||
aniso: -1
|
aniso: 16
|
||||||
mipBias: -1
|
mipBias: -1
|
||||||
wrapMode: -1
|
wrapMode: 1
|
||||||
nPOTScale: 1
|
nPOTScale: 0
|
||||||
lightmap: 0
|
lightmap: 0
|
||||||
rGBM: 0
|
rGBM: 0
|
||||||
compressionQuality: 50
|
compressionQuality: 50
|
||||||
allowsAlphaSplitting: 0
|
allowsAlphaSplitting: 0
|
||||||
spriteMode: 0
|
spriteMode: 1
|
||||||
spriteExtrude: 1
|
spriteExtrude: 1
|
||||||
spriteMeshType: 1
|
spriteMeshType: 1
|
||||||
alignment: 0
|
alignment: 0
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
%YAML 1.1
|
||||||
|
%TAG !u! tag:unity3d.com,2011:
|
||||||
|
--- !u!114 &11400000
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_PrefabParentObject: {fileID: 0}
|
||||||
|
m_PrefabInternal: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 0}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: a6b194f808b1af6499c93410e504af42, type: 3}
|
||||||
|
m_Name: goblins_Atlas
|
||||||
|
m_EditorClassIdentifier:
|
||||||
|
atlasFile: {fileID: 4900000, guid: 179f09b47e5402545a1aa69bf5cb2cba, type: 3}
|
||||||
|
materials:
|
||||||
|
- {fileID: 2100000, guid: 54091ef934c41eb4192f72bfd8e3bcc9, type: 2}
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: bb54bdab69af2bb49b35577b80dcaad9
|
||||||
|
timeCreated: 1467115504
|
||||||
|
licenseType: Free
|
||||||
|
NativeFormatImporter:
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@ -0,0 +1,30 @@
|
|||||||
|
%YAML 1.1
|
||||||
|
%TAG !u! tag:unity3d.com,2011:
|
||||||
|
--- !u!21 &2100000
|
||||||
|
Material:
|
||||||
|
serializedVersion: 6
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_PrefabParentObject: {fileID: 0}
|
||||||
|
m_PrefabInternal: {fileID: 0}
|
||||||
|
m_Name: goblins_Material
|
||||||
|
m_Shader: {fileID: 4800000, guid: 1e8a610c9e01c3648bac42585e5fc676, type: 3}
|
||||||
|
m_ShaderKeywords:
|
||||||
|
m_LightmapFlags: 5
|
||||||
|
m_CustomRenderQueue: -1
|
||||||
|
stringTagMap: {}
|
||||||
|
m_SavedProperties:
|
||||||
|
serializedVersion: 2
|
||||||
|
m_TexEnvs:
|
||||||
|
data:
|
||||||
|
first:
|
||||||
|
name: _MainTex
|
||||||
|
second:
|
||||||
|
m_Texture: {fileID: 2800000, guid: 5fb7efec30c79cb46a705e0d04debb04, type: 3}
|
||||||
|
m_Scale: {x: 1, y: 1}
|
||||||
|
m_Offset: {x: 0, y: 0}
|
||||||
|
m_Floats:
|
||||||
|
data:
|
||||||
|
first:
|
||||||
|
name: _Cutoff
|
||||||
|
second: 0.1
|
||||||
|
m_Colors: {}
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 54091ef934c41eb4192f72bfd8e3bcc9
|
||||||
|
timeCreated: 1467115504
|
||||||
|
licenseType: Free
|
||||||
|
NativeFormatImporter:
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user