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
|
||||
.DS_Store
|
||||
|
||||
|
||||
.idea/
|
||||
build/
|
||||
|
||||
@ -39,7 +40,9 @@ spine-cocos2d-iphone/spine-cocos2d-iphone-ios.xcodeproj/project.xcworkspace/xcsh
|
||||
|
||||
spine-csharp/bin
|
||||
spine-csharp/obj
|
||||
spine-csharp/src/*.meta
|
||||
spine-csharp/src/*.cs.meta
|
||||
spine-csharp/src/Attachments/*.cs.meta
|
||||
|
||||
spine-monogame/xamarinstudio-ios/src/bin
|
||||
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-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.
|
||||
|
||||
## 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. 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.
|
||||
|
||||
|
||||
@ -70,10 +70,10 @@
|
||||
<Compile Include="src\Attachments\AttachmentLoader.cs" />
|
||||
<Compile Include="src\Attachments\AttachmentType.cs" />
|
||||
<Compile Include="src\Attachments\BoundingBoxAttachment.cs" />
|
||||
<Compile Include="src\Attachments\IFfdAttachment.cs" />
|
||||
<Compile Include="src\Attachments\MeshAttachment.cs" />
|
||||
<Compile Include="src\Attachments\PathAttachment.cs" />
|
||||
<Compile Include="src\Attachments\RegionAttachment.cs" />
|
||||
<Compile Include="src\Attachments\WeightedMeshAttachment.cs" />
|
||||
<Compile Include="src\Attachments\VertexAttachment.cs" />
|
||||
<Compile Include="src\BlendMode.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
@ -105,6 +105,8 @@
|
||||
<Compile Include="src\MathUtils.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="src\PathConstraint.cs" />
|
||||
<Compile Include="src\PathConstraintData.cs" />
|
||||
<Compile Include="src\Skeleton.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
|
||||
@ -43,8 +43,8 @@ namespace Spine {
|
||||
public float Duration { get { return duration; } set { duration = value; } }
|
||||
|
||||
public Animation (String name, ExposedList<Timeline> timelines, float duration) {
|
||||
if (name == null) throw new ArgumentNullException("name cannot be null.");
|
||||
if (timelines == null) throw new ArgumentNullException("timelines cannot be null.");
|
||||
if (name == null) throw new ArgumentNullException("name", "name cannot be null.");
|
||||
if (timelines == null) throw new ArgumentNullException("timelines", "timelines cannot be null.");
|
||||
this.name = name;
|
||||
this.timelines = timelines;
|
||||
this.duration = duration;
|
||||
@ -52,9 +52,9 @@ namespace Spine {
|
||||
|
||||
/// <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="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) {
|
||||
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) {
|
||||
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>
|
||||
/// <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>
|
||||
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) {
|
||||
time %= duration;
|
||||
@ -131,12 +131,13 @@ namespace Spine {
|
||||
/// <summary>Base class for frames that use an interpolation bezier curve.</summary>
|
||||
abstract public class CurveTimeline : Timeline {
|
||||
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, ...
|
||||
public int FrameCount { get { return curves.Length / BEZIER_SIZE + 1; } }
|
||||
|
||||
public CurveTimeline (int frameCount) {
|
||||
if (frameCount <= 0) throw new ArgumentException("frameCount must be > 0: " + frameCount, "frameCount");
|
||||
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
|
||||
/// the difference between the keyframe's values.</summary>
|
||||
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 pre1 = 3 * subdiv1, pre2 = 3 * subdiv2, pre4 = 6 * subdiv2, pre5 = 6 * subdiv3;
|
||||
float tmp1x = -cx1 * 2 + cx2, tmp1y = -cy1 * 2 + cy2, tmp2x = (cx1 - cx2) * 3 + 1, tmp2y = (cy1 - cy2) * 3 + 1;
|
||||
float dfx = cx1 * pre1 + tmp1x * pre2 + tmp2x * subdiv3, dfy = cy1 * pre1 + tmp1y * pre2 + tmp2y * subdiv3;
|
||||
float ddfx = tmp1x * pre4 + tmp2x * pre5, ddfy = tmp1y * pre4 + tmp2y * pre5;
|
||||
float dddfx = tmp2x * pre5, dddfy = tmp2y * pre5;
|
||||
float tmpx = (-cx1 * 2 + cx2) * 0.03f, tmpy = (-cy1 * 2 + cy2) * 0.03f;
|
||||
float dddfx = ((cx1 - cx2) * 3 + 1) * 0.006f, dddfy = ((cy1 - cy2) * 3 + 1) * 0.006f;
|
||||
float ddfx = tmpx * 2 + dddfx, ddfy = tmpy * 2 + dddfy;
|
||||
float dfx = cx1 * 0.3f + tmpx + dddfx * 0.16666667f, dfy = cy1 * 0.3f + tmpy + dddfy * 0.16666667f;
|
||||
|
||||
int i = frameIndex * BEZIER_SIZE;
|
||||
float[] curves = this.curves;
|
||||
@ -179,6 +178,7 @@ namespace Spine {
|
||||
}
|
||||
|
||||
public float GetCurvePercent (int frameIndex, float percent) {
|
||||
percent = MathUtils.Clamp (percent, 0, 1);
|
||||
float[] curves = this.curves;
|
||||
int i = frameIndex * BEZIER_SIZE;
|
||||
float type = curves[i];
|
||||
@ -209,8 +209,9 @@ namespace Spine {
|
||||
}
|
||||
|
||||
public class RotateTimeline : CurveTimeline {
|
||||
internal const int PREV_TIME = -2;
|
||||
internal const int VALUE = 1;
|
||||
public const int ENTRIES = 2;
|
||||
internal const int PREV_TIME = -2, PREV_ROTATION = -1;
|
||||
internal const int ROTATION = 1;
|
||||
|
||||
internal int boneIndex;
|
||||
internal float[] frames;
|
||||
@ -224,10 +225,10 @@ namespace Spine {
|
||||
}
|
||||
|
||||
/// <summary>Sets the time and value of the specified keyframe.</summary>
|
||||
public void SetFrame (int frameIndex, float time, float angle) {
|
||||
frameIndex *= 2;
|
||||
public void SetFrame (int frameIndex, float time, float degrees) {
|
||||
frameIndex <<= 1;
|
||||
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) {
|
||||
@ -238,8 +239,8 @@ namespace Spine {
|
||||
|
||||
float amount;
|
||||
|
||||
if (time >= frames[frames.Length - 2]) { // Time is after last frame.
|
||||
amount = bone.data.rotation + frames[frames.Length - 1] - bone.rotation;
|
||||
if (time >= frames[frames.Length - ENTRIES]) { // Time is after last frame.
|
||||
amount = bone.data.rotation + frames[frames.Length + PREV_ROTATION] - bone.rotation;
|
||||
while (amount > 180)
|
||||
amount -= 360;
|
||||
while (amount < -180)
|
||||
@ -249,18 +250,17 @@ namespace Spine {
|
||||
}
|
||||
|
||||
// Interpolate between the previous frame and the current frame.
|
||||
int frame = Animation.binarySearch(frames, time, 2);
|
||||
float prevFrameValue = frames[frame - 1];
|
||||
int frame = Animation.binarySearch(frames, time, ENTRIES);
|
||||
float prevRotation = frames[frame + PREV_ROTATION];
|
||||
float frameTime = frames[frame];
|
||||
float percent = 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime);
|
||||
percent = GetCurvePercent((frame >> 1) - 1, percent < 0 ? 0 : (percent > 1 ? 1 : percent));
|
||||
float percent = GetCurvePercent((frame >> 1) - 1, 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime));
|
||||
|
||||
amount = frames[frame + VALUE] - prevFrameValue;
|
||||
amount = frames[frame + ROTATION] - prevRotation;
|
||||
while (amount > 180)
|
||||
amount -= 360;
|
||||
while (amount < -180)
|
||||
amount += 360;
|
||||
amount = bone.data.rotation + (prevFrameValue + amount * percent) - bone.rotation;
|
||||
amount = bone.data.rotation + (prevRotation + amount * percent) - bone.rotation;
|
||||
while (amount > 180)
|
||||
amount -= 360;
|
||||
while (amount < -180)
|
||||
@ -270,9 +270,9 @@ namespace Spine {
|
||||
}
|
||||
|
||||
public class TranslateTimeline : CurveTimeline {
|
||||
protected const int PREV_TIME = -3;
|
||||
protected const int X = 1;
|
||||
protected const int Y = 2;
|
||||
public const int ENTRIES = 3;
|
||||
protected const int PREV_TIME = -3, PREV_X = -2, PREV_Y = -1;
|
||||
protected const int X = 1, Y = 2;
|
||||
|
||||
internal int boneIndex;
|
||||
internal float[] frames;
|
||||
@ -282,15 +282,15 @@ namespace Spine {
|
||||
|
||||
public TranslateTimeline (int frameCount)
|
||||
: base(frameCount) {
|
||||
frames = new float[frameCount * 3];
|
||||
frames = new float[frameCount * ENTRIES];
|
||||
}
|
||||
|
||||
/// <summary>Sets the time and value of the specified keyframe.</summary>
|
||||
public void SetFrame (int frameIndex, float time, float x, float y) {
|
||||
frameIndex *= 3;
|
||||
frameIndex *= ENTRIES;
|
||||
frames[frameIndex] = time;
|
||||
frames[frameIndex + 1] = x;
|
||||
frames[frameIndex + 2] = y;
|
||||
frames[frameIndex + X] = x;
|
||||
frames[frameIndex + Y] = y;
|
||||
}
|
||||
|
||||
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];
|
||||
|
||||
if (time >= frames[frames.Length - 3]) { // Time is after last frame.
|
||||
bone.x += (bone.data.x + frames[frames.Length - 2] - bone.x) * alpha;
|
||||
bone.y += (bone.data.y + frames[frames.Length - 1] - bone.y) * alpha;
|
||||
if (time >= frames[frames.Length - ENTRIES]) { // Time is after last frame.
|
||||
bone.x += (bone.data.x + frames[frames.Length + PREV_X] - bone.x) * alpha;
|
||||
bone.y += (bone.data.y + frames[frames.Length + PREV_Y] - bone.y) * alpha;
|
||||
return;
|
||||
}
|
||||
|
||||
// Interpolate between the previous frame and the current frame.
|
||||
int frame = Animation.binarySearch(frames, time, 3);
|
||||
float prevFrameX = frames[frame - 2];
|
||||
float prevFrameY = frames[frame - 1];
|
||||
int frame = Animation.binarySearch(frames, time, ENTRIES);
|
||||
float prevX = frames[frame + PREV_X];
|
||||
float prevY = frames[frame + PREV_Y];
|
||||
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 percent = GetCurvePercent(frame / ENTRIES - 1, 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime));
|
||||
|
||||
bone.x += (bone.data.x + prevFrameX + (frames[frame + X] - prevFrameX) * percent - bone.x) * alpha;
|
||||
bone.y += (bone.data.y + prevFrameY + (frames[frame + Y] - prevFrameY) * percent - bone.y) * alpha;
|
||||
bone.x += (bone.data.x + prevX + (frames[frame + X] - prevX) * percent - bone.x) * 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.
|
||||
|
||||
Bone bone = skeleton.bones.Items[boneIndex];
|
||||
if (time >= frames[frames.Length - 3]) { // Time is after last frame.
|
||||
bone.scaleX += (bone.data.scaleX * frames[frames.Length - 2] - bone.scaleX) * alpha;
|
||||
bone.scaleY += (bone.data.scaleY * frames[frames.Length - 1] - bone.scaleY) * alpha;
|
||||
if (time >= frames[frames.Length - ENTRIES]) { // Time is after last frame.
|
||||
bone.scaleX += (bone.data.scaleX * frames[frames.Length + PREV_X] - bone.scaleX) * alpha;
|
||||
bone.scaleY += (bone.data.scaleY * frames[frames.Length + PREV_Y] - bone.scaleY) * alpha;
|
||||
return;
|
||||
}
|
||||
|
||||
// Interpolate between the previous frame and the current frame.
|
||||
int frame = Animation.binarySearch(frames, time, 3);
|
||||
float prevFrameX = frames[frame - 2];
|
||||
float prevFrameY = frames[frame - 1];
|
||||
int frame = Animation.binarySearch(frames, time, ENTRIES);
|
||||
float prevX = frames[frame + PREV_X];
|
||||
float prevY = frames[frame + PREV_Y];
|
||||
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 percent = GetCurvePercent(frame / ENTRIES - 1, 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime));
|
||||
|
||||
bone.scaleX += (bone.data.scaleX * (prevFrameX + (frames[frame + X] - prevFrameX) * percent) - bone.scaleX) * alpha;
|
||||
bone.scaleY += (bone.data.scaleY * (prevFrameY + (frames[frame + Y] - prevFrameY) * percent) - bone.scaleY) * alpha;
|
||||
bone.scaleX += (bone.data.scaleX * (prevX + (frames[frame + X] - prevX) * percent) - bone.scaleX) * alpha;
|
||||
bone.scaleY += (bone.data.scaleY * (prevY + (frames[frame + Y] - prevY) * percent) - bone.scaleY) * alpha;
|
||||
}
|
||||
}
|
||||
|
||||
public class ShearTimeline : TranslateTimeline {
|
||||
public ShearTimeline (int frameCount)
|
||||
: base (frameCount) {
|
||||
: base(frameCount) {
|
||||
}
|
||||
|
||||
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.
|
||||
|
||||
Bone bone = skeleton.bones.Items[boneIndex];
|
||||
if (time >= frames[frames.Length - 3]) { // Time is after last frame.
|
||||
bone.shearX += (bone.data.shearX + frames[frames.Length - 2] - bone.shearX) * alpha;
|
||||
bone.shearY += (bone.data.shearY + frames[frames.Length - 1] - bone.shearY) * alpha;
|
||||
if (time >= frames[frames.Length - ENTRIES]) { // Time is after last frame.
|
||||
bone.shearX += (bone.data.shearX + frames[frames.Length + PREV_X] - bone.shearX) * alpha;
|
||||
bone.shearY += (bone.data.shearY + frames[frames.Length + PREV_Y] - bone.shearY) * alpha;
|
||||
return;
|
||||
}
|
||||
|
||||
// Interpolate between the previous frame and the current frame.
|
||||
int frame = Animation.binarySearch(frames, time, 3);
|
||||
float prevFrameX = frames[frame - 2];
|
||||
float prevFrameY = frames[frame - 1];
|
||||
int frame = Animation.binarySearch(frames, time, ENTRIES);
|
||||
float prevX = frames[frame + PREV_X];
|
||||
float prevY = frames[frame + PREV_Y];
|
||||
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 percent = GetCurvePercent(frame / ENTRIES - 1, 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime));
|
||||
|
||||
bone.shearX += (bone.data.shearX + (prevFrameX + (frames[frame + X] - prevFrameX) * percent) - bone.shearX) * alpha;
|
||||
bone.shearY += (bone.data.shearY + (prevFrameY + (frames[frame + Y] - prevFrameY) * percent) - bone.shearY) * alpha;
|
||||
bone.shearX += (bone.data.shearX + (prevX + (frames[frame + X] - prevX) * percent) - bone.shearX) * alpha;
|
||||
bone.shearY += (bone.data.shearY + (prevY + (frames[frame + Y] - prevY) * percent) - bone.shearY) * alpha;
|
||||
}
|
||||
}
|
||||
|
||||
public class ColorTimeline : CurveTimeline {
|
||||
protected const int PREV_TIME = -5;
|
||||
protected const int R = 1;
|
||||
protected const int G = 2;
|
||||
protected const int B = 3;
|
||||
protected const int A = 4;
|
||||
public const int ENTRIES = 5;
|
||||
protected const int PREV_TIME = -5, PREV_R = -4, PREV_G = -3, PREV_B = -2, PREV_A = -1;
|
||||
protected const int R = 1, G = 2, B = 3, A = 4;
|
||||
|
||||
internal int slotIndex;
|
||||
internal float[] frames;
|
||||
@ -391,17 +386,17 @@ namespace Spine {
|
||||
|
||||
public ColorTimeline (int frameCount)
|
||||
: base(frameCount) {
|
||||
frames = new float[frameCount * 5];
|
||||
frames = new float[frameCount * ENTRIES];
|
||||
}
|
||||
|
||||
/// <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) {
|
||||
frameIndex *= 5;
|
||||
frameIndex *= ENTRIES;
|
||||
frames[frameIndex] = time;
|
||||
frames[frameIndex + 1] = r;
|
||||
frames[frameIndex + 2] = g;
|
||||
frames[frameIndex + 3] = b;
|
||||
frames[frameIndex + 4] = a;
|
||||
frames[frameIndex + R] = r;
|
||||
frames[frameIndex + G] = g;
|
||||
frames[frameIndex + B] = b;
|
||||
frames[frameIndex + A] = a;
|
||||
}
|
||||
|
||||
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.
|
||||
|
||||
float r, g, b, a;
|
||||
if (time >= frames[frames.Length - 5]) { // Time is after last frame.
|
||||
int i = frames.Length - 1;
|
||||
r = frames[i - 3];
|
||||
g = frames[i - 2];
|
||||
b = frames[i - 1];
|
||||
a = frames[i];
|
||||
if (time >= frames[frames.Length - ENTRIES]) { // Time is after last frame.
|
||||
int i = frames.Length;
|
||||
r = frames[i + PREV_R];
|
||||
g = frames[i + PREV_G];
|
||||
b = frames[i + PREV_B];
|
||||
a = frames[i + PREV_A];
|
||||
} else {
|
||||
// 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 percent = 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime);
|
||||
percent = GetCurvePercent(frame / 5 - 1, percent < 0 ? 0 : (percent > 1 ? 1 : percent));
|
||||
float percent = GetCurvePercent(frame / ENTRIES - 1,
|
||||
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;
|
||||
g += (frames[frame + G] - g) * 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) {
|
||||
float[] frames = this.frames;
|
||||
if (time < frames[0]) {
|
||||
if (lastTime > time) Apply(skeleton, lastTime, int.MaxValue, null, 0);
|
||||
return;
|
||||
} else if (lastTime > time) //
|
||||
lastTime = -1;
|
||||
if (time < frames[0]) return; // Time is before first frame.
|
||||
|
||||
int frameIndex = (time >= frames[frames.Length - 1] ? frames.Length : Animation.binarySearch(frames, time)) - 1;
|
||||
if (frames[frameIndex] < lastTime) return;
|
||||
int frameIndex;
|
||||
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];
|
||||
skeleton.slots.Items[slotIndex].Attachment =
|
||||
attachmentName == null ? null : skeleton.GetAttachment(slotIndex, attachmentName);
|
||||
skeleton.slots.Items[slotIndex]
|
||||
.Attachment = attachmentName == null ? null : skeleton.GetAttachment(slotIndex, attachmentName);
|
||||
}
|
||||
}
|
||||
|
||||
@ -503,7 +497,7 @@ namespace Spine {
|
||||
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) {
|
||||
if (firedEvents == null) return;
|
||||
float[] frames = this.frames;
|
||||
@ -570,24 +564,26 @@ namespace Spine {
|
||||
for (int i = 0, n = slots.Count; i < n; i++)
|
||||
drawOrder.Add(slots.Items[i]);
|
||||
} else {
|
||||
var drawOrderItems = drawOrder.Items;
|
||||
var slotsItems = slots.Items;
|
||||
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 float[] frames;
|
||||
private float[][] frameVertices;
|
||||
internal Attachment attachment;
|
||||
internal VertexAttachment attachment;
|
||||
|
||||
public int SlotIndex { get { return slotIndex; } set { slotIndex = value; } }
|
||||
public float[] Frames { get { return frames; } set { frames = value; } } // time, ...
|
||||
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) {
|
||||
frames = 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) {
|
||||
Slot slot = skeleton.slots.Items[slotIndex];
|
||||
IFfdAttachment ffdAttachment = slot.attachment as IFfdAttachment;
|
||||
if (ffdAttachment == null || !ffdAttachment.ApplyFFD(attachment)) return;
|
||||
VertexAttachment slotAttachment = slot.attachment as VertexAttachment;
|
||||
if (slotAttachment == null || !slotAttachment.ApplyDeform(attachment)) return;
|
||||
|
||||
float[] frames = this.frames;
|
||||
if (time < frames[0]) return; // Time is before first frame.
|
||||
@ -610,15 +606,12 @@ namespace Spine {
|
||||
float[][] frameVertices = this.frameVertices;
|
||||
int vertexCount = frameVertices[0].Length;
|
||||
|
||||
float[] vertices = slot.attachmentVertices;
|
||||
if (slot.attachmentVerticesCount != vertexCount) alpha = 1; // Don't mix from uninitialized slot vertices.
|
||||
|
||||
// Ensure capacity
|
||||
if (vertices.Length < vertexCount) {
|
||||
vertices = new float[vertexCount];
|
||||
slot.attachmentVertices = vertices;
|
||||
}
|
||||
slot.attachmentVerticesCount = vertexCount;
|
||||
var verticesArray = slot.attachmentVertices;
|
||||
if (verticesArray.Count != vertexCount) alpha = 1; // Don't mix from uninitialized slot vertices.
|
||||
// verticesArray.SetSize(vertexCount) // Ensure size and preemptively set count.
|
||||
if (verticesArray.Capacity < vertexCount) verticesArray.Capacity = vertexCount;
|
||||
verticesArray.Count = vertexCount;
|
||||
float[] vertices = verticesArray.Items;
|
||||
|
||||
if (time >= frames[frames.Length - 1]) { // Time is after last frame.
|
||||
float[] lastVertices = frameVertices[frames.Length - 1];
|
||||
@ -634,12 +627,10 @@ namespace Spine {
|
||||
|
||||
// Interpolate between the previous frame and the current frame.
|
||||
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[] nextVertices = frameVertices[frame];
|
||||
float frameTime = frames[frame];
|
||||
float percent = GetCurvePercent(frame - 1, 1 - (time - frameTime) / (frames[frame - 1] - frameTime));
|
||||
|
||||
if (alpha < 1) {
|
||||
for (int i = 0; i < vertexCount; i++) {
|
||||
@ -657,10 +648,9 @@ namespace Spine {
|
||||
}
|
||||
|
||||
public class IkConstraintTimeline : CurveTimeline {
|
||||
private const int PREV_TIME = -3;
|
||||
private const int PREV_MIX = -2;
|
||||
private const int PREV_BEND_DIRECTION = -1;
|
||||
private const int MIX = 1;
|
||||
public const int ENTRIES = 3;
|
||||
private const int PREV_TIME = -3, PREV_MIX = -2, PREV_BEND_DIRECTION = -1;
|
||||
private const int MIX = 1, BEND_DIRECTION = 2;
|
||||
|
||||
internal int ikConstraintIndex;
|
||||
internal float[] frames;
|
||||
@ -670,15 +660,15 @@ namespace Spine {
|
||||
|
||||
public IkConstraintTimeline (int 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>
|
||||
public void SetFrame (int frameIndex, float time, float mix, int bendDirection) {
|
||||
frameIndex *= 3;
|
||||
frameIndex *= ENTRIES;
|
||||
frames[frameIndex] = time;
|
||||
frames[frameIndex + 1] = mix;
|
||||
frames[frameIndex + 2] = bendDirection;
|
||||
frames[frameIndex + MIX] = mix;
|
||||
frames[frameIndex + BEND_DIRECTION] = bendDirection;
|
||||
}
|
||||
|
||||
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];
|
||||
|
||||
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.bendDirection = (int)frames[frames.Length + PREV_BEND_DIRECTION];
|
||||
return;
|
||||
}
|
||||
|
||||
// Interpolate between the previous frame and the current frame.
|
||||
int frame = Animation.binarySearch(frames, time, 3);
|
||||
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));
|
||||
|
||||
int frame = Animation.binarySearch(frames, time, ENTRIES);
|
||||
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.bendDirection = (int)frames[frame + PREV_BEND_DIRECTION];
|
||||
}
|
||||
}
|
||||
|
||||
public class TransformConstraintTimeline : CurveTimeline {
|
||||
private const int PREV_TIME = -5;
|
||||
private const int PREV_ROTATE_MIX = -4;
|
||||
private const int PREV_TRANSLATE_MIX = -3;
|
||||
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;
|
||||
public const int ENTRIES = 5;
|
||||
private const int PREV_TIME = -5, PREV_ROTATE = -4, PREV_TRANSLATE = -3, PREV_SCALE = -2, PREV_SHEAR = -1;
|
||||
private const int ROTATE = 1, TRANSLATE = 2, SCALE = 3, SHEAR = 4;
|
||||
|
||||
internal int transformConstraintIndex;
|
||||
internal float[] frames;
|
||||
@ -724,16 +707,16 @@ namespace Spine {
|
||||
|
||||
public TransformConstraintTimeline (int 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) {
|
||||
frameIndex *= 5;
|
||||
frameIndex *= ENTRIES;
|
||||
frames[frameIndex] = time;
|
||||
frames[frameIndex + 1] = rotateMix;
|
||||
frames[frameIndex + 2] = translateMix;
|
||||
frames[frameIndex + 3] = scaleMix;
|
||||
frames[frameIndex + 4] = shearMix;
|
||||
frames[frameIndex + ROTATE] = rotateMix;
|
||||
frames[frameIndex + TRANSLATE] = translateMix;
|
||||
frames[frameIndex + SCALE] = scaleMix;
|
||||
frames[frameIndex + SHEAR] = shearMix;
|
||||
}
|
||||
|
||||
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];
|
||||
|
||||
if (time >= frames[frames.Length - 5]) { // Time is after last frame.
|
||||
int i = frames.Length - 1;
|
||||
constraint.rotateMix += (frames[i - 3] - constraint.rotateMix) * alpha;
|
||||
constraint.translateMix += (frames[i - 2] - constraint.translateMix) * alpha;
|
||||
constraint.scaleMix += (frames[i - 1] - constraint.scaleMix) * alpha;
|
||||
constraint.shearMix += (frames[i] - constraint.shearMix) * alpha;
|
||||
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;
|
||||
constraint.scaleMix += (frames[i + PREV_SCALE] - constraint.scaleMix) * alpha;
|
||||
constraint.shearMix += (frames[i + PREV_SHEAR] - constraint.shearMix) * alpha;
|
||||
return;
|
||||
}
|
||||
|
||||
// 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 percent = 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime);
|
||||
percent = GetCurvePercent(frame / 5 - 1, percent < 0 ? 0 : (percent > 1 ? 1 : percent));
|
||||
float percent = GetCurvePercent(frame / ENTRIES - 1, 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime));
|
||||
|
||||
float rotate = frames[frame + PREV_ROTATE_MIX];
|
||||
float translate = frames[frame + PREV_TRANSLATE_MIX];
|
||||
float scale = frames[frame + PREV_SCALE_MIX];
|
||||
float shear = frames[frame + PREV_SHEAR_MIX];
|
||||
constraint.rotateMix += (rotate + (frames[frame + ROTATE_MIX] - rotate) * percent - constraint.rotateMix) * alpha;
|
||||
constraint.translateMix += (translate + (frames[frame + TRANSLATE_MIX] - translate) * percent - constraint.translateMix) * alpha;
|
||||
constraint.scaleMix += (scale + (frames[frame + SCALE_MIX] - scale) * percent - constraint.scaleMix) * alpha;
|
||||
constraint.shearMix += (shear + (frames[frame + SHEAR_MIX] - shear) * percent - constraint.shearMix) * alpha;
|
||||
float rotate = frames[frame + PREV_ROTATE];
|
||||
float translate = frames[frame + PREV_TRANSLATE];
|
||||
float scale = frames[frame + PREV_SCALE];
|
||||
float shear = frames[frame + PREV_SHEAR];
|
||||
constraint.rotateMix += (rotate + (frames[frame + ROTATE] - rotate) * percent - constraint.rotateMix) * alpha;
|
||||
constraint.translateMix += (translate + (frames[frame + TRANSLATE] - translate) * percent - constraint.translateMix)
|
||||
* 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;
|
||||
|
||||
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 delegate void StartEndDelegate (AnimationState state, int trackIndex);
|
||||
@ -54,7 +56,7 @@ namespace Spine {
|
||||
public event CompleteDelegate Complete;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@ -187,15 +189,16 @@ namespace Spine {
|
||||
if (Start != null) Start(this, index);
|
||||
}
|
||||
|
||||
/// <seealso cref="SetAnimation(int, Animation, bool)" />
|
||||
public TrackEntry SetAnimation (int trackIndex, String animationName, bool loop) {
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>Set the current animation. Any queued animations are cleared.</summary>
|
||||
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();
|
||||
entry.animation = animation;
|
||||
entry.loop = loop;
|
||||
@ -205,16 +208,17 @@ namespace Spine {
|
||||
return entry;
|
||||
}
|
||||
|
||||
/// <seealso cref="AddAnimation(int, Animation, bool, float)" />
|
||||
public TrackEntry AddAnimation (int trackIndex, String animationName, bool loop, float delay) {
|
||||
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);
|
||||
}
|
||||
|
||||
/// <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) {
|
||||
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();
|
||||
entry.animation = animation;
|
||||
entry.loop = loop;
|
||||
|
||||
@ -42,6 +42,7 @@ namespace Spine {
|
||||
public float DefaultMix { get { return defaultMix; } set { defaultMix = value; } }
|
||||
|
||||
public AnimationStateData (SkeletonData skeletonData) {
|
||||
if (skeletonData == null) throw new ArgumentException ("skeletonData cannot be null.");
|
||||
this.skeletonData = skeletonData;
|
||||
}
|
||||
|
||||
@ -54,8 +55,8 @@ namespace Spine {
|
||||
}
|
||||
|
||||
public void SetMix (Animation from, Animation to, float duration) {
|
||||
if (from == null) throw new ArgumentNullException("from cannot be null.");
|
||||
if (to == null) throw new ArgumentNullException("to cannot be null.");
|
||||
if (from == null) throw new ArgumentNullException("from", "from cannot be null.");
|
||||
if (to == null) throw new ArgumentNullException("to", "to cannot be null.");
|
||||
AnimationPair key = new AnimationPair(from, to);
|
||||
animationToMixTime.Remove(key);
|
||||
animationToMixTime.Add(key, duration);
|
||||
|
||||
@ -72,31 +72,16 @@ namespace Spine {
|
||||
attachment.regionOriginalWidth = region.originalWidth;
|
||||
attachment.regionOriginalHeight = region.originalHeight;
|
||||
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) {
|
||||
return new BoundingBoxAttachment(name);
|
||||
}
|
||||
|
||||
public PathAttachment NewPathAttachment (Skin skin, String name) {
|
||||
return new PathAttachment (name);
|
||||
}
|
||||
|
||||
public AtlasRegion FindRegion (string name) {
|
||||
AtlasRegion region;
|
||||
|
||||
|
||||
@ -36,7 +36,7 @@ namespace Spine {
|
||||
public String Name { get; private set; }
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@ -39,10 +39,10 @@ namespace Spine {
|
||||
/// <return>May be null to not load any attachment.</return>
|
||||
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>
|
||||
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 {
|
||||
public enum AttachmentType {
|
||||
region, boundingbox, mesh, weightedmesh, linkedmesh, weightedlinkedmesh
|
||||
Region, Boundingbox, Mesh, Linkedmesh, Path
|
||||
}
|
||||
}
|
||||
|
||||
@ -33,29 +33,9 @@ using System;
|
||||
|
||||
namespace Spine {
|
||||
/// <summary>Attachment that has a polygon for bounds checking.</summary>
|
||||
public class BoundingBoxAttachment : Attachment {
|
||||
internal float[] vertices;
|
||||
|
||||
public float[] Vertices { get { return vertices; } set { vertices = value; } }
|
||||
|
||||
public class BoundingBoxAttachment : VertexAttachment {
|
||||
public BoundingBoxAttachment (string 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 {
|
||||
/// <summary>Attachment that displays a texture region using a mesh.</summary>
|
||||
public class MeshAttachment : Attachment, IFfdAttachment {
|
||||
internal float[] vertices, uvs, regionUVs;
|
||||
internal int[] triangles;
|
||||
public class MeshAttachment : VertexAttachment {
|
||||
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 int hulllength;
|
||||
internal MeshAttachment parentMesh;
|
||||
internal bool inheritFFD;
|
||||
internal bool inheritDeform;
|
||||
|
||||
public int HullLength { get; set; }
|
||||
public float[] Vertices { get { return vertices; } set { vertices = value; } }
|
||||
public int HullLength { get { return hulllength; } set { hulllength = 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; } }
|
||||
@ -66,7 +66,7 @@ namespace Spine {
|
||||
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 bool InheritDeform { get { return inheritDeform; } set { inheritDeform = value; } }
|
||||
|
||||
public MeshAttachment ParentMesh {
|
||||
get { return parentMesh; }
|
||||
@ -74,6 +74,7 @@ namespace Spine {
|
||||
parentMesh = value;
|
||||
if (value != null) {
|
||||
vertices = value.vertices;
|
||||
worldVerticesLength = value.worldVerticesLength;
|
||||
regionUVs = value.regionUVs;
|
||||
triangles = value.triangles;
|
||||
HullLength = value.HullLength;
|
||||
@ -111,23 +112,8 @@ namespace Spine {
|
||||
}
|
||||
}
|
||||
|
||||
public void ComputeWorldVertices (Slot slot, float[] worldVertices) {
|
||||
Bone bone = slot.bone;
|
||||
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);
|
||||
override public bool ApplyDeform (VertexAttachment sourceAttachment) {
|
||||
return this == sourceAttachment || (inheritDeform && parentMesh == sourceAttachment);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -29,8 +29,21 @@
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Spine {
|
||||
public interface IFfdAttachment {
|
||||
bool ApplyFFD (Attachment sourceAttachment);
|
||||
public class PathAttachment : VertexAttachment {
|
||||
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 localX2 = localX + regionWidth * regionScaleX;
|
||||
float localY2 = localY + regionHeight * regionScaleY;
|
||||
float radians = rotation * (float)Math.PI / 180;
|
||||
float cos = (float)Math.Cos(radians);
|
||||
float sin = (float)Math.Sin(radians);
|
||||
float rotation = this.rotation;
|
||||
float cos = MathUtils.CosDeg(rotation);
|
||||
float sin = MathUtils.SinDeg(rotation);
|
||||
float x = this.x;
|
||||
float y = this.y;
|
||||
float localXCos = localX * cos + x;
|
||||
@ -136,17 +136,18 @@ namespace Spine {
|
||||
}
|
||||
|
||||
public void ComputeWorldVertices (Bone bone, float[] worldVertices) {
|
||||
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;
|
||||
Skeleton skeleton = bone.skeleton;
|
||||
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;
|
||||
worldVertices[X1] = offset[X1] * m00 + offset[Y1] * m01 + x;
|
||||
worldVertices[Y1] = offset[X1] * m10 + offset[Y1] * m11 + y;
|
||||
worldVertices[X2] = offset[X2] * m00 + offset[Y2] * m01 + x;
|
||||
worldVertices[Y2] = offset[X2] * m10 + offset[Y2] * m11 + y;
|
||||
worldVertices[X3] = offset[X3] * m00 + offset[Y3] * m01 + x;
|
||||
worldVertices[Y3] = offset[X3] * m10 + offset[Y3] * m11 + y;
|
||||
worldVertices[X4] = offset[X4] * m00 + offset[Y4] * m01 + x;
|
||||
worldVertices[Y4] = offset[X4] * m10 + offset[Y4] * m11 + y;
|
||||
worldVertices[X1] = offset[X1] * a + offset[Y1] * b + x;
|
||||
worldVertices[Y1] = offset[X1] * c + offset[Y1] * d + y;
|
||||
worldVertices[X2] = offset[X2] * a + offset[Y2] * b + x;
|
||||
worldVertices[Y2] = offset[X2] * c + offset[Y2] * d + y;
|
||||
worldVertices[X3] = offset[X3] * a + offset[Y3] * b + x;
|
||||
worldVertices[Y3] = offset[X3] * c + offset[Y3] * d + y;
|
||||
worldVertices[X4] = offset[X4] * a + offset[Y4] * b + x;
|
||||
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.Collections.Generic;
|
||||
|
||||
namespace Spine {
|
||||
public class Bone : IUpdatable {
|
||||
@ -41,12 +40,14 @@ namespace Spine {
|
||||
internal Bone parent;
|
||||
internal ExposedList<Bone> children = new ExposedList<Bone>();
|
||||
internal float x, y, rotation, scaleX, scaleY, shearX, shearY;
|
||||
internal float appliedRotation, appliedScaleX, appliedScaleY;
|
||||
internal float appliedRotation;
|
||||
|
||||
internal float a, b, worldX;
|
||||
internal float c, d, worldY;
|
||||
internal float worldSignX, worldSignY;
|
||||
|
||||
internal bool sorted;
|
||||
|
||||
public BoneData Data { get { return data; } }
|
||||
public Skeleton Skeleton { get { return skeleton; } }
|
||||
public Bone Parent { get { return parent; } }
|
||||
@ -56,10 +57,6 @@ namespace Spine {
|
||||
public float Rotation { get { return rotation; } set { rotation = value; } }
|
||||
/// <summary>The rotation, as calculated by any constraints.</summary>
|
||||
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 ScaleY { get { return scaleY; } set { scaleY = value; } }
|
||||
public float ShearX { get { return shearX; } set { shearX = value; } }
|
||||
@ -80,29 +77,27 @@ namespace Spine {
|
||||
|
||||
/// <param name="parent">May be null.</param>
|
||||
public Bone (BoneData data, Skeleton skeleton, Bone parent) {
|
||||
if (data == null) throw new ArgumentNullException("data cannot be null.");
|
||||
if (skeleton == null) throw new ArgumentNullException("skeleton cannot be null.");
|
||||
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;
|
||||
this.skeleton = skeleton;
|
||||
this.parent = parent;
|
||||
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 () {
|
||||
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 () {
|
||||
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) {
|
||||
appliedRotation = rotation;
|
||||
appliedScaleX = scaleX;
|
||||
appliedScaleY = scaleY;
|
||||
|
||||
float rotationY = rotation + 90 + shearY;
|
||||
float la = MathUtils.CosDeg(rotation + shearX) * scaleX, lb = MathUtils.CosDeg(rotationY) * scaleY;
|
||||
@ -152,10 +147,10 @@ namespace Spine {
|
||||
do {
|
||||
float cos = MathUtils.CosDeg(parent.appliedRotation), sin = MathUtils.SinDeg(parent.appliedRotation);
|
||||
float temp = pa * cos + pb * sin;
|
||||
pb = pa * -sin + pb * cos;
|
||||
pb = pb * cos - pa * sin;
|
||||
pa = temp;
|
||||
temp = pc * cos + pd * sin;
|
||||
pd = pc * -sin + pd * cos;
|
||||
pd = pd * cos - pc * sin;
|
||||
pc = temp;
|
||||
|
||||
if (!parent.data.inheritRotation) break;
|
||||
@ -171,24 +166,22 @@ namespace Spine {
|
||||
pc = 0;
|
||||
pd = 1;
|
||||
do {
|
||||
float r = parent.appliedRotation, cos = MathUtils.CosDeg(r), sin = MathUtils.SinDeg(r);
|
||||
float psx = parent.appliedScaleX, psy = parent.appliedScaleY;
|
||||
float za = cos * psx, zb = -sin * psy, zc = sin * psx, zd = cos * psy;
|
||||
float cos = MathUtils.CosDeg(parent.appliedRotation), sin = MathUtils.SinDeg(parent.appliedRotation);
|
||||
float psx = parent.scaleX, psy = parent.scaleY;
|
||||
float za = cos * psx, zb = sin * psy, zc = sin * psx, zd = cos * psy;
|
||||
float temp = pa * za + pb * zc;
|
||||
pb = pa * zb + pb * zd;
|
||||
pb = pb * zd - pa * zb;
|
||||
pa = temp;
|
||||
temp = pc * za + pd * zc;
|
||||
pd = pc * zb + pd * zd;
|
||||
pd = pd * zd - pc * zb;
|
||||
pc = temp;
|
||||
|
||||
if (psx < 0) r = -r;
|
||||
cos = MathUtils.CosDeg(-r);
|
||||
sin = MathUtils.SinDeg(-r);
|
||||
if (psx >= 0) sin = -sin;
|
||||
temp = pa * cos + pb * sin;
|
||||
pb = pa * -sin + pb * cos;
|
||||
pb = pb * cos - pa * sin;
|
||||
pa = temp;
|
||||
temp = pc * cos + pd * sin;
|
||||
pd = pc * -sin + pd * cos;
|
||||
pd = pd * cos - pc * sin;
|
||||
pc = temp;
|
||||
|
||||
if (!parent.data.inheritScale) break;
|
||||
@ -226,10 +219,86 @@ namespace Spine {
|
||||
shearY = data.shearY;
|
||||
}
|
||||
|
||||
public void WorldToLocal (float worldX, float worldY, out float localX, out float localY) {
|
||||
float x = worldX - this.worldX, y = worldY - this.worldY;
|
||||
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) {
|
||||
float a = this.a, b = this.b, c = this.c, d = this.d;
|
||||
float invDet = 1 / (a * d - b * c);
|
||||
float x = worldX - this.worldX, y = worldY - this.worldY;
|
||||
localX = (x * d * invDet - y * b * invDet);
|
||||
localY = (y * a * invDet - x * c * invDet);
|
||||
}
|
||||
|
||||
@ -33,14 +33,17 @@ using System;
|
||||
|
||||
namespace Spine {
|
||||
public class BoneData {
|
||||
internal BoneData parent;
|
||||
internal int index;
|
||||
internal String name;
|
||||
internal float length, x, y, rotation, scaleX = 1, scaleY = 1, shearX, shearY;
|
||||
internal bool inheritScale = true, inheritRotation = true;
|
||||
internal BoneData parent;
|
||||
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>
|
||||
public BoneData Parent { get { return parent; } }
|
||||
public int Index { get { return index; } }
|
||||
public String Name { get { return name; } }
|
||||
public BoneData Parent { get { return parent; } }
|
||||
public float Length { get { return length; } set { length = value; } }
|
||||
public float X { get { return x; } set { x = 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 ShearX { get { return shearX; } set { shearX = 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 InheritScale { get { return inheritScale; } set { inheritScale = value; } }
|
||||
|
||||
/// <param name="parent">May be null.</param>
|
||||
public BoneData (String name, BoneData parent) {
|
||||
if (name == null) throw new ArgumentNullException("name cannot be null.");
|
||||
public BoneData (int index, String name, BoneData parent) {
|
||||
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.parent = parent;
|
||||
}
|
||||
|
||||
@ -40,6 +40,7 @@ namespace Spine {
|
||||
public float Time { get; private set; }
|
||||
|
||||
public Event (float time, EventData data) {
|
||||
if (data == null) throw new ArgumentNullException("data", "data cannot be null.");
|
||||
Time = time;
|
||||
Data = data;
|
||||
}
|
||||
|
||||
@ -41,7 +41,7 @@ namespace Spine {
|
||||
public String String { get; set; }
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@ -89,6 +89,12 @@ namespace Spine {
|
||||
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) {
|
||||
if (idx < 0)
|
||||
throw new ArgumentOutOfRangeException("index");
|
||||
@ -580,4 +586,4 @@ namespace Spine {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -30,15 +30,16 @@
|
||||
*****************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Spine {
|
||||
public class IkConstraint : IUpdatable {
|
||||
internal IkConstraintData data;
|
||||
internal ExposedList<Bone> bones = new ExposedList<Bone>();
|
||||
internal Bone target;
|
||||
internal int bendDirection;
|
||||
internal float mix;
|
||||
internal int bendDirection;
|
||||
|
||||
internal int level;
|
||||
|
||||
public IkConstraintData Data { get { return data; } }
|
||||
public ExposedList<Bone> Bones { get { return bones; } }
|
||||
@ -47,8 +48,8 @@ namespace Spine {
|
||||
public float Mix { get { return mix; } set { mix = value; } }
|
||||
|
||||
public IkConstraint (IkConstraintData data, Skeleton skeleton) {
|
||||
if (data == null) throw new ArgumentNullException("data cannot be null.");
|
||||
if (skeleton == null) throw new ArgumentNullException("skeleton cannot be null.");
|
||||
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;
|
||||
mix = data.mix;
|
||||
bendDirection = data.bendDirection;
|
||||
@ -87,21 +88,24 @@ namespace Spine {
|
||||
float id = 1 / (pp.a * pp.d - pp.b * pp.c);
|
||||
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 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 (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.appliedScaleY, bone.shearX, bone.shearY);
|
||||
bone.UpdateWorldTransform(bone.x, bone.y, bone.rotation + rotationIK * alpha, bone.scaleX, bone.scaleY,
|
||||
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
|
||||
/// possible. The target is specified in the world coordinate system.</summary>
|
||||
/// <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) {
|
||||
if (alpha == 0) return;
|
||||
float px = parent.x, py = parent.y, psx = parent.appliedScaleX, psy = parent.appliedScaleY;
|
||||
if (alpha == 0) {
|
||||
child.UpdateWorldTransform ();
|
||||
return;
|
||||
}
|
||||
float px = parent.x, py = parent.y, psx = parent.scaleX, psy = parent.scaleY, csx = child.scaleX;
|
||||
int os1, os2, s2;
|
||||
if (psx < 0) {
|
||||
psx = -psx;
|
||||
@ -115,44 +119,55 @@ namespace Spine {
|
||||
psy = -psy;
|
||||
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) {
|
||||
csx = -csx;
|
||||
os2 = 180;
|
||||
} else
|
||||
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;
|
||||
float ppa = pp.a, ppb = pp.b, ppc = pp.c, ppd = pp.d, id = 1 / (ppa * ppd - ppb * ppc);
|
||||
float x = targetX - pp.worldX, y = targetY - pp.worldY;
|
||||
float tx = (x * ppd - y * ppb) * id - px, ty = (y * ppa - x * ppc) * id - py;
|
||||
x = child.worldX - pp.worldX;
|
||||
y = child.worldY - pp.worldY;
|
||||
float dx = (x * ppd - y * ppb) * id - px, dy = (y * ppa - x * ppc) * id - py;
|
||||
a = pp.a;
|
||||
b = pp.b;
|
||||
c = pp.c;
|
||||
d = pp.d;
|
||||
float id = 1 / (a * d - b * c), x = targetX - pp.worldX, y = targetY - pp.worldY;
|
||||
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;
|
||||
if (u) {
|
||||
l2 *= psx;
|
||||
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;
|
||||
a2 = (float)Math.Acos(cos) * bendDir;
|
||||
float a = l1 + l2 * cos, o = l2 * MathUtils.Sin(a2);
|
||||
a1 = MathUtils.Atan2(ty * a - tx * o, tx * a + ty * o);
|
||||
a = l1 + l2 * cos;
|
||||
b = l2 * MathUtils.Sin(a2);
|
||||
a1 = MathUtils.Atan2(ty * a - tx * b, tx * a + ty * b);
|
||||
} else {
|
||||
float a = psx * l2, b = psy * l2, ta = MathUtils.Atan2(ty, tx);
|
||||
float aa = a * a, bb = b * b, ll = l1 * l1, dd = tx * tx + ty * ty;
|
||||
float c0 = bb * ll + aa * dd - aa * bb, c1 = -2 * bb * l1, c2 = bb - aa;
|
||||
float d = c1 * c1 - 4 * c2 * c0;
|
||||
a = psx * l2;
|
||||
b = psy * l2;
|
||||
float aa = a * a, bb = b * b, dd = tx * tx + ty * ty, ta = MathUtils.Atan2(ty, tx);
|
||||
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) {
|
||||
float q = (float)Math.Sqrt(d);
|
||||
if (c1 < 0) q = -q;
|
||||
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;
|
||||
if (r * r <= dd) {
|
||||
y = (float)Math.Sqrt(dd - r * r) * bendDir;
|
||||
@ -201,18 +216,20 @@ namespace Spine {
|
||||
a2 = maxAngle * bendDir;
|
||||
}
|
||||
}
|
||||
outer:
|
||||
outer:
|
||||
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;
|
||||
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;
|
||||
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 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;
|
||||
}
|
||||
|
||||
|
||||
@ -54,22 +54,22 @@ namespace Spine {
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
return sin[(int)((degrees + 90) * degToIndex) & SIN_MASK];
|
||||
}
|
||||
@ -91,5 +91,11 @@ namespace Spine {
|
||||
atan = PI / 2 - z / (z * z + 0.28f);
|
||||
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<Slot> slots;
|
||||
internal ExposedList<Slot> drawOrder;
|
||||
internal ExposedList<IkConstraint> ikConstraints;
|
||||
internal ExposedList<IkConstraint> ikConstraints, ikConstraintsSorted;
|
||||
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 float r = 1, g = 1, b = 1, a = 1;
|
||||
internal float time;
|
||||
@ -49,9 +50,12 @@ namespace Spine {
|
||||
|
||||
public SkeletonData Data { get { return data; } }
|
||||
public ExposedList<Bone> Bones { get { return bones; } }
|
||||
public ExposedList<IUpdatable> UpdateCacheList { get { return updateCache; } }
|
||||
public ExposedList<Slot> Slots { get { return slots; } }
|
||||
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 float R { get { return r; } set { r = 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 Bone RootBone {
|
||||
get {
|
||||
return bones.Count == 0 ? null : bones.Items[0];
|
||||
}
|
||||
get { return bones.Count == 0 ? null : bones.Items[0]; }
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
bones = new ExposedList<Bone>(data.bones.Count);
|
||||
foreach (BoneData boneData in data.bones) {
|
||||
Bone parent = boneData.parent == null ? null : bones.Items[data.bones.IndexOf(boneData.parent)];
|
||||
Bone bone = new Bone(boneData, this, parent);
|
||||
if (parent != null) parent.children.Add(bone);
|
||||
Bone bone;
|
||||
if (boneData.parent == null) {
|
||||
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);
|
||||
}
|
||||
|
||||
slots = new ExposedList<Slot>(data.slots.Count);
|
||||
drawOrder = new ExposedList<Slot>(data.slots.Count);
|
||||
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);
|
||||
slots.Add(slot);
|
||||
drawOrder.Add(slot);
|
||||
}
|
||||
|
||||
ikConstraints = new ExposedList<IkConstraint>(data.ikConstraints.Count);
|
||||
ikConstraintsSorted = new ExposedList<IkConstraint>(data.ikConstraints.Count);
|
||||
foreach (IkConstraintData ikConstraintData in data.ikConstraints)
|
||||
ikConstraints.Add(new IkConstraint(ikConstraintData, this));
|
||||
|
||||
@ -98,46 +106,150 @@ namespace Spine {
|
||||
foreach (TransformConstraintData transformConstraintData in data.transformConstraints)
|
||||
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();
|
||||
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>
|
||||
public void UpdateCache () {
|
||||
ExposedList<Bone> bones = this.bones;
|
||||
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();
|
||||
for (int i = 0, n = bones.Count; i < n; i++) {
|
||||
Bone bone = bones.Items[i];
|
||||
updateCache.Add(bone);
|
||||
for (int ii = 0; ii < ikConstraintsCount; ii++) {
|
||||
IkConstraint ikConstraint = ikConstraints.Items[ii];
|
||||
if (bone == ikConstraint.bones.Items[ikConstraint.bones.Count - 1]) {
|
||||
updateCache.Add(ikConstraint);
|
||||
break;
|
||||
}
|
||||
|
||||
ExposedList<Bone> bones = this.bones;
|
||||
for (int i = 0, n = bones.Count; i < n; i++)
|
||||
bones.Items[i].sorted = false;
|
||||
|
||||
ExposedList<IkConstraint> ikConstraints = this.ikConstraintsSorted;
|
||||
ikConstraints.Clear();
|
||||
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;
|
||||
}
|
||||
|
||||
for (int i = 0; i < transformConstraintsCount; i++) {
|
||||
TransformConstraint transformConstraint = transformConstraints.Items[i];
|
||||
for (int ii = updateCache.Count - 1; i >= 0; ii--) {
|
||||
if (updateCache.Items[ii] == transformConstraint.bone) {
|
||||
updateCache.Insert(ii + 1, transformConstraint);
|
||||
break;
|
||||
}
|
||||
}
|
||||
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]]);
|
||||
}
|
||||
}
|
||||
|
||||
private void SortBone (Bone bone) {
|
||||
if (bone.sorted) return;
|
||||
Bone parent = bone.parent;
|
||||
if (parent != null) SortBone(parent);
|
||||
bone.sorted = true;
|
||||
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>
|
||||
public void UpdateWorldTransform () {
|
||||
ExposedList<IUpdatable> updateCache = this.updateCache;
|
||||
var updateCache = this.updateCache;
|
||||
for (int i = 0, n = updateCache.Count; i < n; i++)
|
||||
updateCache.Items[i].Update();
|
||||
}
|
||||
@ -150,44 +262,56 @@ namespace Spine {
|
||||
|
||||
/// <summary>Sets the bones and constraints to their setup pose values.</summary>
|
||||
public void SetBonesToSetupPose () {
|
||||
ExposedList<Bone> bones = this.bones;
|
||||
var bonesItems = this.bones.Items;
|
||||
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++) {
|
||||
IkConstraint constraint = ikConstraints.Items[i];
|
||||
IkConstraint constraint = ikConstraintsItems[i];
|
||||
constraint.bendDirection = constraint.data.bendDirection;
|
||||
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++) {
|
||||
TransformConstraint constraint = transformConstraints.Items[i];
|
||||
TransformConstraint constraint = transformConstraintsItems[i];
|
||||
TransformConstraintData data = constraint.data;
|
||||
constraint.rotateMix = data.rotateMix;
|
||||
constraint.translateMix = data.translateMix;
|
||||
constraint.scaleMix = data.scaleMix;
|
||||
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 () {
|
||||
ExposedList<Slot> slots = this.slots;
|
||||
var slots = this.slots;
|
||||
var slotsItems = slots.Items;
|
||||
drawOrder.Clear();
|
||||
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++)
|
||||
slots.Items[i].SetToSetupPose(i);
|
||||
slotsItems[i].SetToSetupPose();
|
||||
}
|
||||
|
||||
/// <returns>May be null.</returns>
|
||||
public Bone FindBone (String boneName) {
|
||||
if (boneName == null) throw new ArgumentNullException("boneName cannot be null.");
|
||||
ExposedList<Bone> bones = this.bones;
|
||||
if (boneName == null) throw new ArgumentNullException("boneName", "boneName cannot be null.");
|
||||
var bones = this.bones;
|
||||
var bonesItems = bones.Items;
|
||||
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;
|
||||
}
|
||||
return null;
|
||||
@ -195,19 +319,21 @@ namespace Spine {
|
||||
|
||||
/// <returns>-1 if the bone was not found.</returns>
|
||||
public int FindBoneIndex (String boneName) {
|
||||
if (boneName == null) throw new ArgumentNullException("boneName cannot be null.");
|
||||
ExposedList<Bone> bones = this.bones;
|
||||
if (boneName == null) throw new ArgumentNullException("boneName", "boneName cannot be null.");
|
||||
var bones = this.bones;
|
||||
var bonesItems = bones.Items;
|
||||
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;
|
||||
}
|
||||
|
||||
/// <returns>May be null.</returns>
|
||||
public Slot FindSlot (String slotName) {
|
||||
if (slotName == null) throw new ArgumentNullException("slotName cannot be null.");
|
||||
ExposedList<Slot> slots = this.slots;
|
||||
if (slotName == null) throw new ArgumentNullException("slotName", "slotName cannot be null.");
|
||||
var slots = this.slots;
|
||||
var slotsItems = slots.Items;
|
||||
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;
|
||||
}
|
||||
return null;
|
||||
@ -215,17 +341,18 @@ namespace Spine {
|
||||
|
||||
/// <returns>-1 if the bone was not found.</returns>
|
||||
public int FindSlotIndex (String slotName) {
|
||||
if (slotName == null) throw new ArgumentNullException("slotName cannot be null.");
|
||||
ExposedList<Slot> slots = this.slots;
|
||||
if (slotName == null) throw new ArgumentNullException("slotName", "slotName cannot be null.");
|
||||
var slots = this.slots;
|
||||
var slotsItems = slots.Items;
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>Sets a skin by name (see SetSkin).</summary>
|
||||
public void SetSkin (String 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);
|
||||
}
|
||||
|
||||
@ -259,7 +386,7 @@ namespace Spine {
|
||||
|
||||
/// <returns>May be null.</returns>
|
||||
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) {
|
||||
Attachment attachment = skin.GetAttachment(slotIndex, attachmentName);
|
||||
if (attachment != null) return attachment;
|
||||
@ -270,7 +397,7 @@ namespace Spine {
|
||||
|
||||
/// <param name="attachmentName">May be null.</param>
|
||||
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;
|
||||
for (int i = 0, n = slots.Count; i < n; i++) {
|
||||
Slot slot = slots.Items[i];
|
||||
@ -286,10 +413,10 @@ namespace Spine {
|
||||
}
|
||||
throw new Exception("Slot not found: " + slotName);
|
||||
}
|
||||
|
||||
/** @return May be null. */
|
||||
|
||||
/// <returns>May be null.</returns>
|
||||
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;
|
||||
for (int i = 0, n = ikConstraints.Count; i < n; i++) {
|
||||
IkConstraint ikConstraint = ikConstraints.Items[i];
|
||||
@ -298,9 +425,9 @@ namespace Spine {
|
||||
return null;
|
||||
}
|
||||
|
||||
/** @return May be null. */
|
||||
/// <returns>May be null.</returns>
|
||||
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;
|
||||
for (int i = 0, n = transformConstraints.Count; i < n; i++) {
|
||||
TransformConstraint transformConstraint = transformConstraints.Items[i];
|
||||
@ -309,6 +436,17 @@ namespace Spine {
|
||||
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) {
|
||||
time += delta;
|
||||
}
|
||||
|
||||
@ -44,12 +44,17 @@ using Windows.Storage;
|
||||
|
||||
namespace Spine {
|
||||
public class SkeletonBinary {
|
||||
public const int TIMELINE_ROTATE = 0;
|
||||
public const int TIMELINE_TRANSLATE = 1;
|
||||
public const int TIMELINE_SCALE = 2;
|
||||
public const int TIMELINE_SHEAR = 3;
|
||||
public const int TIMELINE_ATTACHMENT = 4;
|
||||
public const int TIMELINE_COLOR = 5;
|
||||
public const int BONE_ROTATE = 0;
|
||||
public const int BONE_TRANSLATE = 1;
|
||||
public const int BONE_SCALE = 2;
|
||||
public const int BONE_SHEAR = 3;
|
||||
|
||||
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_STEPPED = 1;
|
||||
@ -122,55 +127,26 @@ namespace Spine {
|
||||
for (int i = 0, n = ReadVarint(input, true); i < n; i++) {
|
||||
String name = ReadString(input);
|
||||
BoneData parent = i == 0 ? null : skeletonData.bones.Items[ReadVarint(input, true)];
|
||||
BoneData boneData = new BoneData(name, parent);
|
||||
boneData.rotation = ReadFloat(input);
|
||||
boneData.x = ReadFloat(input) * scale;
|
||||
boneData.y = ReadFloat(input) * scale;
|
||||
boneData.scaleX = ReadFloat(input);
|
||||
boneData.scaleY = ReadFloat(input);
|
||||
boneData.shearX = ReadFloat(input);
|
||||
boneData.shearY = ReadFloat(input);
|
||||
boneData.length = ReadFloat(input) * scale;
|
||||
boneData.inheritRotation = ReadBoolean(input);
|
||||
boneData.inheritScale = ReadBoolean(input);
|
||||
BoneData data = new BoneData(i, name, parent);
|
||||
data.rotation = ReadFloat(input);
|
||||
data.x = ReadFloat(input) * scale;
|
||||
data.y = ReadFloat(input) * scale;
|
||||
data.scaleX = ReadFloat(input);
|
||||
data.scaleY = ReadFloat(input);
|
||||
data.shearX = ReadFloat(input);
|
||||
data.shearY = ReadFloat(input);
|
||||
data.length = ReadFloat(input) * scale;
|
||||
data.inheritRotation = ReadBoolean(input);
|
||||
data.inheritScale = ReadBoolean(input);
|
||||
if (nonessential) ReadInt(input); // Skip bone color.
|
||||
skeletonData.bones.Add(boneData);
|
||||
}
|
||||
|
||||
// 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);
|
||||
skeletonData.bones.Add(data);
|
||||
}
|
||||
|
||||
// Slots.
|
||||
for (int i = 0, n = ReadVarint(input, true); i < n; i++) {
|
||||
String slotName = ReadString(input);
|
||||
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);
|
||||
slotData.r = ((color & 0xff000000) >> 24) / 255f;
|
||||
slotData.g = ((color & 0x00ff0000) >> 16) / 255f;
|
||||
@ -181,6 +157,55 @@ namespace Spine {
|
||||
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.
|
||||
Skin defaultSkin = ReadSkin(input, "default", nonessential);
|
||||
if (defaultSkin != null) {
|
||||
@ -199,25 +224,18 @@ namespace Spine {
|
||||
if (skin == null) throw new Exception("Skin not found: " + linkedMesh.skin);
|
||||
Attachment parent = skin.GetAttachment(linkedMesh.slotIndex, linkedMesh.parent);
|
||||
if (parent == null) throw new Exception("Parent mesh not found: " + linkedMesh.parent);
|
||||
if (linkedMesh.mesh is MeshAttachment) {
|
||||
MeshAttachment mesh = (MeshAttachment)linkedMesh.mesh;
|
||||
mesh.ParentMesh = (MeshAttachment)parent;
|
||||
mesh.UpdateUVs();
|
||||
} else {
|
||||
WeightedMeshAttachment mesh = (WeightedMeshAttachment)linkedMesh.mesh;
|
||||
mesh.ParentMesh = (WeightedMeshAttachment)parent;
|
||||
mesh.UpdateUVs();
|
||||
}
|
||||
linkedMesh.mesh.ParentMesh = (MeshAttachment)parent;
|
||||
linkedMesh.mesh.UpdateUVs();
|
||||
}
|
||||
linkedMeshes.Clear();
|
||||
|
||||
// Events.
|
||||
for (int i = 0, n = ReadVarint(input, true); i < n; i++) {
|
||||
EventData eventData = new EventData(ReadString(input));
|
||||
eventData.Int = ReadVarint(input, false);
|
||||
eventData.Float = ReadFloat(input);
|
||||
eventData.String = ReadString(input);
|
||||
skeletonData.events.Add(eventData);
|
||||
EventData data = new EventData(ReadString(input));
|
||||
data.Int = ReadVarint(input, false);
|
||||
data.Float = ReadFloat(input);
|
||||
data.String = ReadString(input);
|
||||
skeletonData.events.Add(data);
|
||||
}
|
||||
|
||||
// Animations.
|
||||
@ -230,10 +248,12 @@ namespace Spine {
|
||||
skeletonData.events.TrimExcess();
|
||||
skeletonData.animations.TrimExcess();
|
||||
skeletonData.ikConstraints.TrimExcess();
|
||||
skeletonData.pathConstraints.TrimExcess();
|
||||
return skeletonData;
|
||||
}
|
||||
|
||||
/** @return May be null. */
|
||||
|
||||
/// <returns>May be null.</returns>
|
||||
private Skin ReadSkin (Stream input, String skinName, bool nonessential) {
|
||||
int slotCount = ReadVarint(input, true);
|
||||
if (slotCount == 0) return null;
|
||||
@ -256,7 +276,7 @@ namespace Spine {
|
||||
|
||||
AttachmentType type = (AttachmentType)input.ReadByte();
|
||||
switch (type) {
|
||||
case AttachmentType.region: {
|
||||
case AttachmentType.Region: {
|
||||
String path = ReadString(input);
|
||||
float rotation = ReadFloat(input);
|
||||
float x = ReadFloat(input);
|
||||
@ -285,96 +305,25 @@ namespace Spine {
|
||||
region.UpdateOffset();
|
||||
return region;
|
||||
}
|
||||
case AttachmentType.boundingbox: {
|
||||
float[] vertices = ReadFloatArray(input, ReadVarint(input, true) * 2, scale);
|
||||
case AttachmentType.Boundingbox: {
|
||||
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);
|
||||
if (box == null) return null;
|
||||
box.vertices = vertices;
|
||||
box.worldVerticesLength = vertexCount << 1;
|
||||
box.vertices = vertices.vertices;
|
||||
box.bones = vertices.bones;
|
||||
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 vertexCount = ReadVarint(input, true);
|
||||
float[] uvs = ReadFloatArray(input, vertexCount << 1, 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);
|
||||
int color = ReadInt(input);
|
||||
int vertexCount = ReadVarint(input, true);
|
||||
float[] uvs = ReadFloatArray(input, vertexCount * 2, 1);
|
||||
int[] triangles = ReadShortArray(input);
|
||||
var weights = new List<float>(uvs.Length * 3 * 3);
|
||||
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));
|
||||
}
|
||||
}
|
||||
Vertices vertices = ReadVertices(input, vertexCount);
|
||||
int hullLength = ReadVarint(input, true);
|
||||
int[] edges = null;
|
||||
float width = 0, height = 0;
|
||||
@ -385,33 +334,33 @@ namespace Spine {
|
||||
}
|
||||
|
||||
if (path == null) path = name;
|
||||
WeightedMeshAttachment mesh = attachmentLoader.NewWeightedMeshAttachment(skin, name, path);
|
||||
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.bones = bones.ToArray();
|
||||
mesh.weights = weights.ToArray();
|
||||
mesh.bones = vertices.bones;
|
||||
mesh.vertices = vertices.vertices;
|
||||
mesh.WorldVerticesLength = vertexCount << 1;
|
||||
mesh.triangles = triangles;
|
||||
mesh.regionUVs = uvs;
|
||||
mesh.UpdateUVs();
|
||||
mesh.HullLength = hullLength * 2;
|
||||
mesh.HullLength = hullLength << 1;
|
||||
if (nonessential) {
|
||||
mesh.Edges = edges;
|
||||
mesh.Width = width * scale;
|
||||
mesh.Height = height * scale;
|
||||
}
|
||||
//
|
||||
return mesh;
|
||||
}
|
||||
case AttachmentType.weightedlinkedmesh: {
|
||||
case AttachmentType.Linkedmesh: {
|
||||
String path = ReadString(input);
|
||||
int color = ReadInt(input);
|
||||
String skinName = ReadString(input);
|
||||
String parent = ReadString(input);
|
||||
bool inheritFFD = ReadBoolean(input);
|
||||
bool inheritDeform = ReadBoolean(input);
|
||||
float width = 0, height = 0;
|
||||
if (nonessential) {
|
||||
width = ReadFloat(input);
|
||||
@ -419,14 +368,14 @@ namespace Spine {
|
||||
}
|
||||
|
||||
if (path == null) path = name;
|
||||
WeightedMeshAttachment mesh = attachmentLoader.NewWeightedMeshAttachment(skin, name, path);
|
||||
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;
|
||||
mesh.inheritDeform = inheritDeform;
|
||||
if (nonessential) {
|
||||
mesh.Width = width * scale;
|
||||
mesh.Height = height * scale;
|
||||
@ -434,10 +383,56 @@ namespace Spine {
|
||||
linkedMeshes.Add(new SkeletonJson.LinkedMesh(mesh, skinName, slotIndex, parent));
|
||||
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;
|
||||
}
|
||||
|
||||
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) {
|
||||
float[] array = new float[n];
|
||||
if (scale == 1) {
|
||||
@ -470,7 +465,7 @@ namespace Spine {
|
||||
int timelineType = input.ReadByte();
|
||||
int frameCount = ReadVarint(input, true);
|
||||
switch (timelineType) {
|
||||
case TIMELINE_COLOR: {
|
||||
case SLOT_COLOR: {
|
||||
ColorTimeline timeline = new ColorTimeline(frameCount);
|
||||
timeline.slotIndex = slotIndex;
|
||||
for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) {
|
||||
@ -484,10 +479,10 @@ namespace Spine {
|
||||
if (frameIndex < frameCount - 1) ReadCurve(input, frameIndex, 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;
|
||||
}
|
||||
case TIMELINE_ATTACHMENT: {
|
||||
case SLOT_ATTACHMENT: {
|
||||
AttachmentTimeline timeline = new AttachmentTimeline(frameCount);
|
||||
timeline.slotIndex = slotIndex;
|
||||
for (int frameIndex = 0; frameIndex < frameCount; frameIndex++)
|
||||
@ -507,7 +502,7 @@ namespace Spine {
|
||||
int timelineType = input.ReadByte();
|
||||
int frameCount = ReadVarint(input, true);
|
||||
switch (timelineType) {
|
||||
case TIMELINE_ROTATE: {
|
||||
case BONE_ROTATE: {
|
||||
RotateTimeline timeline = new RotateTimeline(frameCount);
|
||||
timeline.boneIndex = boneIndex;
|
||||
for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) {
|
||||
@ -515,17 +510,17 @@ namespace Spine {
|
||||
if (frameIndex < frameCount - 1) ReadCurve(input, frameIndex, timeline);
|
||||
}
|
||||
timelines.Add(timeline);
|
||||
duration = Math.Max(duration, timeline.frames[frameCount * 2 - 2]);
|
||||
duration = Math.Max(duration, timeline.frames[(frameCount - 1) * RotateTimeline.ENTRIES]);
|
||||
break;
|
||||
}
|
||||
case TIMELINE_TRANSLATE:
|
||||
case TIMELINE_SCALE:
|
||||
case TIMELINE_SHEAR: {
|
||||
case BONE_TRANSLATE:
|
||||
case BONE_SCALE:
|
||||
case BONE_SHEAR: {
|
||||
TranslateTimeline timeline;
|
||||
float timelineScale = 1;
|
||||
if (timelineType == TIMELINE_SCALE)
|
||||
if (timelineType == BONE_SCALE)
|
||||
timeline = new ScaleTimeline(frameCount);
|
||||
else if (timelineType == TIMELINE_SHEAR)
|
||||
else if (timelineType == BONE_SHEAR)
|
||||
timeline = new ShearTimeline(frameCount);
|
||||
else {
|
||||
timeline = new TranslateTimeline(frameCount);
|
||||
@ -538,7 +533,7 @@ namespace Spine {
|
||||
if (frameIndex < frameCount - 1) ReadCurve(input, frameIndex, timeline);
|
||||
}
|
||||
timelines.Add(timeline);
|
||||
duration = Math.Max(duration, timeline.frames[frameCount * 3 - 3]);
|
||||
duration = Math.Max(duration, timeline.frames[(frameCount - 1) * TranslateTimeline.ENTRIES]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -546,81 +541,118 @@ namespace Spine {
|
||||
}
|
||||
|
||||
// IK timelines.
|
||||
for (int i = 0, n = ReadVarint(input, true); i < n; i++) {
|
||||
IkConstraintData constraint = skeletonData.ikConstraints.Items[ReadVarint(input, true)];
|
||||
for (int i = 0, n = ReadVarint(input, true); i < n; i++) {
|
||||
int index = ReadVarint(input, true);
|
||||
int frameCount = ReadVarint(input, true);
|
||||
IkConstraintTimeline timeline = new IkConstraintTimeline(frameCount);
|
||||
timeline.ikConstraintIndex = skeletonData.ikConstraints.IndexOf(constraint);
|
||||
timeline.ikConstraintIndex = index;
|
||||
for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) {
|
||||
timeline.SetFrame(frameIndex, ReadFloat(input), ReadFloat(input), ReadSByte(input));
|
||||
if (frameIndex < frameCount - 1) ReadCurve(input, frameIndex, 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.
|
||||
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);
|
||||
TransformConstraintTimeline timeline = new TransformConstraintTimeline(frameCount);
|
||||
timeline.transformConstraintIndex = skeletonData.transformConstraints.IndexOf(constraint);
|
||||
timeline.transformConstraintIndex = index;
|
||||
for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) {
|
||||
timeline.SetFrame(frameIndex, ReadFloat(input), ReadFloat(input), ReadFloat(input), ReadFloat(input), ReadFloat(input));
|
||||
if (frameIndex < frameCount - 1) ReadCurve(input, frameIndex, 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++) {
|
||||
Skin skin = skeletonData.skins.Items[ReadVarint(input, true)];
|
||||
for (int ii = 0, nn = ReadVarint(input, true); ii < nn; ii++) {
|
||||
int slotIndex = ReadVarint(input, true);
|
||||
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);
|
||||
FfdTimeline timeline = new FfdTimeline(frameCount);
|
||||
DeformTimeline timeline = new DeformTimeline(frameCount);
|
||||
timeline.slotIndex = slotIndex;
|
||||
timeline.attachment = attachment;
|
||||
|
||||
for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) {
|
||||
float time = ReadFloat(input);
|
||||
|
||||
float[] vertices;
|
||||
int vertexCount;
|
||||
if (attachment is MeshAttachment)
|
||||
vertexCount = ((MeshAttachment)attachment).vertices.Length;
|
||||
else
|
||||
vertexCount = ((WeightedMeshAttachment)attachment).weights.Length / 3 * 2;
|
||||
|
||||
float[] deform;
|
||||
int end = ReadVarint(input, true);
|
||||
if (end == 0) {
|
||||
if (attachment is MeshAttachment)
|
||||
vertices = ((MeshAttachment)attachment).vertices;
|
||||
else
|
||||
vertices = new float[vertexCount];
|
||||
} else {
|
||||
vertices = new float[vertexCount];
|
||||
if (end == 0)
|
||||
deform = weighted ? new float[deformLength] : vertices;
|
||||
else {
|
||||
deform = new float[deformLength];
|
||||
int start = ReadVarint(input, true);
|
||||
end += start;
|
||||
if (scale == 1) {
|
||||
for (int v = start; v < end; v++)
|
||||
vertices[v] = ReadFloat(input);
|
||||
deform[v] = ReadFloat(input);
|
||||
} else {
|
||||
for (int v = start; v < end; v++)
|
||||
vertices[v] = ReadFloat(input) * scale;
|
||||
deform[v] = ReadFloat(input) * scale;
|
||||
}
|
||||
if (attachment is MeshAttachment) {
|
||||
float[] meshVertices = ((MeshAttachment)attachment).vertices;
|
||||
for (int v = 0, vn = vertices.Length; v < vn; v++)
|
||||
vertices[v] += meshVertices[v];
|
||||
if (!weighted) {
|
||||
for (int v = 0, vn = deform.Length; v < vn; v++)
|
||||
deform[v] += vertices[v];
|
||||
}
|
||||
}
|
||||
|
||||
timeline.SetFrame(frameIndex, time, vertices);
|
||||
timeline.SetFrame(frameIndex, time, deform);
|
||||
if (frameIndex < frameCount - 1) ReadCurve(input, frameIndex, timeline);
|
||||
}
|
||||
}
|
||||
timelines.Add(timeline);
|
||||
duration = Math.Max(duration, timeline.frames[frameCount - 1]);
|
||||
}
|
||||
@ -756,5 +788,10 @@ namespace Spine {
|
||||
length -= count;
|
||||
}
|
||||
}
|
||||
|
||||
internal class Vertices {
|
||||
public int[] bones;
|
||||
public float[] vertices;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -30,7 +30,6 @@
|
||||
*****************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Spine {
|
||||
public class SkeletonBounds {
|
||||
@ -80,7 +79,7 @@ namespace Spine {
|
||||
int count = boundingBox.Vertices.Length;
|
||||
polygon.Count = 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();
|
||||
|
||||
@ -30,7 +30,6 @@
|
||||
*****************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Spine {
|
||||
public class SkeletonData {
|
||||
@ -43,6 +42,7 @@ namespace Spine {
|
||||
internal ExposedList<Animation> animations = new ExposedList<Animation>();
|
||||
internal ExposedList<IkConstraintData> ikConstraints = new ExposedList<IkConstraintData>();
|
||||
internal ExposedList<TransformConstraintData> transformConstraints = new ExposedList<TransformConstraintData>();
|
||||
internal ExposedList<PathConstraintData> pathConstraints = new ExposedList<PathConstraintData>();
|
||||
internal float width, height;
|
||||
internal String version, hash, imagesPath;
|
||||
|
||||
@ -55,6 +55,8 @@ namespace Spine {
|
||||
public ExposedList<EventData> Events { get { return events; } set { events = value; } }
|
||||
public ExposedList<Animation> Animations { get { return animations; } set { animations = 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 Height { get { return height; } set { height = value; } }
|
||||
/// <summary>The Spine version used to export this data.</summary>
|
||||
@ -65,7 +67,7 @@ namespace Spine {
|
||||
|
||||
/// <returns>May be null.</returns>
|
||||
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;
|
||||
for (int i = 0, n = bones.Count; i < n; i++) {
|
||||
BoneData bone = bones.Items[i];
|
||||
@ -76,7 +78,7 @@ namespace Spine {
|
||||
|
||||
/// <returns>-1 if the bone was not found.</returns>
|
||||
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;
|
||||
for (int i = 0, n = bones.Count; i < n; i++)
|
||||
if (bones.Items[i].name == boneName) return i;
|
||||
@ -87,7 +89,7 @@ namespace Spine {
|
||||
|
||||
/// <returns>May be null.</returns>
|
||||
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;
|
||||
for (int i = 0, n = slots.Count; i < n; i++) {
|
||||
SlotData slot = slots.Items[i];
|
||||
@ -96,9 +98,9 @@ namespace Spine {
|
||||
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) {
|
||||
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;
|
||||
for (int i = 0, n = slots.Count; i < n; i++)
|
||||
if (slots.Items[i].name == slotName) return i;
|
||||
@ -109,7 +111,7 @@ namespace Spine {
|
||||
|
||||
/// <returns>May be null.</returns>
|
||||
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)
|
||||
if (skin.name == skinName) return skin;
|
||||
return null;
|
||||
@ -119,7 +121,7 @@ namespace Spine {
|
||||
|
||||
/// <returns>May be null.</returns>
|
||||
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)
|
||||
if (eventData.name == eventDataName) return eventData;
|
||||
return null;
|
||||
@ -129,7 +131,7 @@ namespace Spine {
|
||||
|
||||
/// <returns>May be null.</returns>
|
||||
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;
|
||||
for (int i = 0, n = animations.Count; i < n; i++) {
|
||||
Animation animation = animations.Items[i];
|
||||
@ -142,7 +144,7 @@ namespace Spine {
|
||||
|
||||
/// <returns>May be null.</returns>
|
||||
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;
|
||||
for (int i = 0, n = ikConstraints.Count; i < n; i++) {
|
||||
IkConstraintData ikConstraint = ikConstraints.Items[i];
|
||||
@ -155,7 +157,7 @@ namespace Spine {
|
||||
|
||||
/// <returns>May be null.</returns>
|
||||
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;
|
||||
for (int i = 0, n = transformConstraints.Count; i < n; i++) {
|
||||
TransformConstraintData transformConstraint = transformConstraints.Items[i];
|
||||
@ -164,6 +166,28 @@ namespace Spine {
|
||||
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 () {
|
||||
|
||||
@ -54,7 +54,7 @@ namespace Spine {
|
||||
}
|
||||
|
||||
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;
|
||||
Scale = 1;
|
||||
}
|
||||
@ -79,7 +79,7 @@ namespace Spine {
|
||||
Stream stream = Microsoft.Xna.Framework.TitleContainer.OpenStream(path);
|
||||
using (StreamReader reader = new StreamReader(stream)) {
|
||||
#else
|
||||
using (StreamReader reader = new StreamReader(path)) {
|
||||
using (var reader = new StreamReader(path)) {
|
||||
#endif // WINDOWS_PHONE
|
||||
SkeletonData skeletonData = ReadSkeletonData(reader);
|
||||
skeletonData.name = Path.GetFileNameWithoutExtension(path);
|
||||
@ -89,7 +89,7 @@ namespace Spine {
|
||||
#endif // WINDOWS_STOREAPP
|
||||
|
||||
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 skeletonData = new SkeletonData();
|
||||
@ -114,69 +114,19 @@ namespace Spine {
|
||||
if (parent == null)
|
||||
throw new Exception("Parent bone not found: " + boneMap["parent"]);
|
||||
}
|
||||
var boneData = new BoneData((String)boneMap["name"], parent);
|
||||
boneData.length = GetFloat(boneMap, "length", 0) * scale;
|
||||
boneData.x = GetFloat(boneMap, "x", 0) * scale;
|
||||
boneData.y = GetFloat(boneMap, "y", 0) * scale;
|
||||
boneData.rotation = GetFloat(boneMap, "rotation", 0);
|
||||
boneData.scaleX = GetFloat(boneMap, "scaleX", 1);
|
||||
boneData.scaleY = GetFloat(boneMap, "scaleY", 1);
|
||||
boneData.shearX = GetFloat(boneMap, "shearX", 0);
|
||||
boneData.shearY = GetFloat(boneMap, "shearY", 0);
|
||||
boneData.inheritScale = GetBoolean(boneMap, "inheritScale", true);
|
||||
boneData.inheritRotation = GetBoolean(boneMap, "inheritRotation", true);
|
||||
skeletonData.bones.Add(boneData);
|
||||
}
|
||||
var data = new BoneData(skeletonData.Bones.Count, (String)boneMap["name"], parent);
|
||||
data.length = GetFloat(boneMap, "length", 0) * scale;
|
||||
data.x = GetFloat(boneMap, "x", 0) * scale;
|
||||
data.y = GetFloat(boneMap, "y", 0) * scale;
|
||||
data.rotation = GetFloat(boneMap, "rotation", 0);
|
||||
data.scaleX = GetFloat(boneMap, "scaleX", 1);
|
||||
data.scaleY = GetFloat(boneMap, "scaleY", 1);
|
||||
data.shearX = GetFloat(boneMap, "shearX", 0);
|
||||
data.shearY = GetFloat(boneMap, "shearY", 0);
|
||||
data.inheritRotation = GetBoolean(boneMap, "inheritRotation", true);
|
||||
data.inheritScale = GetBoolean(boneMap, "inheritScale", true);
|
||||
|
||||
// IK constraints.
|
||||
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);
|
||||
}
|
||||
skeletonData.bones.Add(data);
|
||||
}
|
||||
|
||||
// Slots.
|
||||
@ -185,44 +135,126 @@ namespace Spine {
|
||||
var slotName = (String)slotMap["name"];
|
||||
var boneName = (String)slotMap["bone"];
|
||||
BoneData boneData = skeletonData.FindBone(boneName);
|
||||
if (boneData == null)
|
||||
throw new Exception("Slot bone not found: " + boneName);
|
||||
var slotData = new SlotData(slotName, boneData);
|
||||
if (boneData == null) throw new Exception("Slot bone not found: " + boneName);
|
||||
var data = new SlotData(skeletonData.Slots.Count, slotName, boneData);
|
||||
|
||||
if (slotMap.ContainsKey("color")) {
|
||||
var color = (String)slotMap["color"];
|
||||
slotData.r = ToColor(color, 0);
|
||||
slotData.g = ToColor(color, 1);
|
||||
slotData.b = ToColor(color, 2);
|
||||
slotData.a = ToColor(color, 3);
|
||||
data.r = ToColor(color, 0);
|
||||
data.g = ToColor(color, 1);
|
||||
data.b = ToColor(color, 2);
|
||||
data.a = ToColor(color, 3);
|
||||
}
|
||||
|
||||
data.attachmentName = GetString(slotMap, "attachment", null);
|
||||
if (slotMap.ContainsKey("blend"))
|
||||
data.blendMode = (BlendMode)Enum.Parse(typeof(BlendMode), (String)slotMap["blend"], false);
|
||||
else
|
||||
data.blendMode = BlendMode.normal;
|
||||
skeletonData.slots.Add(data);
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
if (slotMap.ContainsKey("attachment"))
|
||||
slotData.attachmentName = (String)slotMap["attachment"];
|
||||
String targetName = (String)constraintMap["target"];
|
||||
data.target = skeletonData.FindBone(targetName);
|
||||
if (data.target == null) throw new Exception("Target bone not found: " + targetName);
|
||||
|
||||
if (slotMap.ContainsKey("blend"))
|
||||
slotData.blendMode = (BlendMode)Enum.Parse(typeof(BlendMode), (String)slotMap["blend"], false);
|
||||
else
|
||||
slotData.blendMode = BlendMode.normal;
|
||||
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);
|
||||
|
||||
skeletonData.slots.Add(slotData);
|
||||
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.
|
||||
if (root.ContainsKey("skins")) {
|
||||
foreach (KeyValuePair<String, Object> entry in (Dictionary<String, Object>)root["skins"]) {
|
||||
var skin = new Skin(entry.Key);
|
||||
foreach (KeyValuePair<String, Object> slotEntry in (Dictionary<String, Object>)entry.Value) {
|
||||
foreach (KeyValuePair<String, Object> skinMap in (Dictionary<String, Object>)root["skins"]) {
|
||||
var skin = new Skin(skinMap.Key);
|
||||
foreach (KeyValuePair<String, Object> slotEntry in (Dictionary<String, Object>)skinMap.Value) {
|
||||
int slotIndex = skeletonData.FindSlotIndex(slotEntry.Key);
|
||||
foreach (KeyValuePair<String, Object> attachmentEntry in ((Dictionary<String, Object>)slotEntry.Value)) {
|
||||
Attachment attachment = ReadAttachment(skin, slotIndex, attachmentEntry.Key, (Dictionary<String, Object>)attachmentEntry.Value);
|
||||
if (attachment != null) skin.AddAttachment(slotIndex, attachmentEntry.Key, attachment);
|
||||
}
|
||||
foreach (KeyValuePair<String, Object> entry in ((Dictionary<String, Object>)slotEntry.Value)) {
|
||||
try {
|
||||
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);
|
||||
if (skin.name == "default")
|
||||
skeletonData.defaultSkin = skin;
|
||||
if (skin.name == "default") skeletonData.defaultSkin = skin;
|
||||
}
|
||||
}
|
||||
|
||||
@ -233,15 +265,8 @@ namespace Spine {
|
||||
if (skin == null) throw new Exception("Slot not found: " + linkedMesh.skin);
|
||||
Attachment parent = skin.GetAttachment(linkedMesh.slotIndex, linkedMesh.parent);
|
||||
if (parent == null) throw new Exception("Parent mesh not found: " + linkedMesh.parent);
|
||||
if (linkedMesh.mesh is MeshAttachment) {
|
||||
MeshAttachment mesh = (MeshAttachment)linkedMesh.mesh;
|
||||
mesh.ParentMesh = (MeshAttachment)parent;
|
||||
mesh.UpdateUVs();
|
||||
} else {
|
||||
WeightedMeshAttachment mesh = (WeightedMeshAttachment)linkedMesh.mesh;
|
||||
mesh.ParentMesh = (WeightedMeshAttachment)parent;
|
||||
mesh.UpdateUVs();
|
||||
}
|
||||
linkedMesh.mesh.ParentMesh = (MeshAttachment)parent;
|
||||
linkedMesh.mesh.UpdateUVs();
|
||||
}
|
||||
linkedMeshes.Clear();
|
||||
|
||||
@ -249,18 +274,23 @@ namespace Spine {
|
||||
if (root.ContainsKey("events")) {
|
||||
foreach (KeyValuePair<String, Object> entry in (Dictionary<String, Object>)root["events"]) {
|
||||
var entryMap = (Dictionary<String, Object>)entry.Value;
|
||||
var eventData = new EventData(entry.Key);
|
||||
eventData.Int = GetInt(entryMap, "int", 0);
|
||||
eventData.Float = GetFloat(entryMap, "float", 0);
|
||||
eventData.String = GetString(entryMap, "string", null);
|
||||
skeletonData.events.Add(eventData);
|
||||
var data = new EventData(entry.Key);
|
||||
data.Int = GetInt(entryMap, "int", 0);
|
||||
data.Float = GetFloat(entryMap, "float", 0);
|
||||
data.String = GetString(entryMap, "string", null);
|
||||
skeletonData.events.Add(data);
|
||||
}
|
||||
}
|
||||
|
||||
// Animations.
|
||||
if (root.ContainsKey("animations")) {
|
||||
foreach (KeyValuePair<String, Object> entry in (Dictionary<String, Object>)root["animations"])
|
||||
ReadAnimation(entry.Key, (Dictionary<String, Object>)entry.Value, skeletonData);
|
||||
foreach (KeyValuePair<String, Object> entry in (Dictionary<String, Object>)root["animations"]) {
|
||||
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();
|
||||
@ -272,25 +302,20 @@ namespace Spine {
|
||||
return skeletonData;
|
||||
}
|
||||
|
||||
private Attachment ReadAttachment (Skin skin, int slotIndex, String name, Dictionary<String, Object> map) {
|
||||
if (map.ContainsKey("name"))
|
||||
name = (String)map["name"];
|
||||
|
||||
private Attachment ReadAttachment (Dictionary<String, Object> map, Skin skin, int slotIndex, String name) {
|
||||
var scale = this.Scale;
|
||||
name = GetString(map, "name", name);
|
||||
|
||||
var type = AttachmentType.region;
|
||||
if (map.ContainsKey("type")) {
|
||||
var typeName = (String)map["type"];
|
||||
if (typeName == "skinnedmesh") typeName = "weightedmesh";
|
||||
type = (AttachmentType)Enum.Parse(typeof(AttachmentType), typeName , false);
|
||||
}
|
||||
var typeName = GetString(map, "type", "region");
|
||||
if (typeName == "skinnedmesh") typeName = "weightedmesh";
|
||||
if (typeName == "weightedmesh") typeName = "mesh";
|
||||
if (typeName == "weightedlinkedmesh") typeName = "linkedmesh";
|
||||
var type = (AttachmentType)Enum.Parse(typeof(AttachmentType), typeName, true);
|
||||
|
||||
String path = name;
|
||||
if (map.ContainsKey("path"))
|
||||
path = (String)map["path"];
|
||||
String path = GetString(map, "path", name);
|
||||
|
||||
switch (type) {
|
||||
case AttachmentType.region:
|
||||
case AttachmentType.Region:
|
||||
RegionAttachment region = attachmentLoader.NewRegionAttachment(skin, name, path);
|
||||
if (region == null) return null;
|
||||
region.Path = path;
|
||||
@ -311,9 +336,15 @@ namespace Spine {
|
||||
region.a = ToColor(color, 3);
|
||||
}
|
||||
|
||||
region.UpdateOffset();
|
||||
return region;
|
||||
case AttachmentType.mesh:
|
||||
case AttachmentType.linkedmesh: {
|
||||
case AttachmentType.Boundingbox:
|
||||
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);
|
||||
if (mesh == null) return null;
|
||||
mesh.Path = path;
|
||||
@ -326,145 +357,83 @@ namespace Spine {
|
||||
mesh.a = ToColor(color, 3);
|
||||
}
|
||||
|
||||
mesh.Width = GetInt(map, "width", 0) * scale;
|
||||
mesh.Height = GetInt(map, "height", 0) * scale;
|
||||
mesh.Width = GetFloat(map, "width", 0) * scale;
|
||||
mesh.Height = GetFloat(map, "height", 0) * scale;
|
||||
|
||||
String parent = GetString(map, "parent", null);
|
||||
if (parent == null) {
|
||||
mesh.vertices = GetFloatArray(map, "vertices", scale);
|
||||
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);
|
||||
if (parent != null) {
|
||||
mesh.InheritDeform = GetBoolean(map, "deform", true);
|
||||
linkedMeshes.Add(new LinkedMesh(mesh, GetString(map, "skin", null), slotIndex, parent));
|
||||
return mesh;
|
||||
}
|
||||
|
||||
float[] uvs = GetFloatArray(map, "uvs", 1);
|
||||
ReadVertices(map, mesh, uvs.Length);
|
||||
mesh.triangles = GetIntArray(map, "triangles");
|
||||
mesh.regionUVs = uvs;
|
||||
mesh.UpdateUVs();
|
||||
|
||||
if (map.ContainsKey("hull")) mesh.HullLength = GetInt(map, "hull", 0) * 2;
|
||||
if (map.ContainsKey("edges")) mesh.Edges = GetIntArray(map, "edges");
|
||||
return mesh;
|
||||
}
|
||||
case AttachmentType.weightedmesh:
|
||||
case AttachmentType.weightedlinkedmesh: {
|
||||
WeightedMeshAttachment mesh = attachmentLoader.NewWeightedMeshAttachment(skin, name, path);
|
||||
if (mesh == null) return null;
|
||||
case AttachmentType.Path: {
|
||||
PathAttachment pathAttachment = attachmentLoader.NewPathAttachment(skin, name);
|
||||
if (pathAttachment == null) return null;
|
||||
pathAttachment.closed = GetBoolean(map, "closed", false);
|
||||
pathAttachment.constantSpeed = GetBoolean(map, "constantSpeed", true);
|
||||
|
||||
mesh.Path = path;
|
||||
int vertexCount = GetInt(map, "vertexCount", 0);
|
||||
ReadVertices(map, pathAttachment, vertexCount << 1);
|
||||
|
||||
if (map.ContainsKey("color")) {
|
||||
var color = (String)map["color"];
|
||||
mesh.r = ToColor(color, 0);
|
||||
mesh.g = ToColor(color, 1);
|
||||
mesh.b = ToColor(color, 2);
|
||||
mesh.a = ToColor(color, 3);
|
||||
}
|
||||
|
||||
mesh.Width = GetInt(map, "width", 0) * scale;
|
||||
mesh.Height = GetInt(map, "height", 0) * scale;
|
||||
|
||||
String parent = GetString(map, "parent", null);
|
||||
if (parent == null) {
|
||||
float[] uvs = GetFloatArray(map, "uvs", 1);
|
||||
float[] vertices = GetFloatArray(map, "vertices", 1);
|
||||
var weights = new List<float>(uvs.Length * 3 * 3);
|
||||
var bones = new List<int>(uvs.Length * 3);
|
||||
for (int i = 0, n = vertices.Length; i < n;) {
|
||||
int boneCount = (int)vertices[i++];
|
||||
bones.Add(boneCount);
|
||||
for (int nn = i + boneCount * 4; i < nn; i += 4) {
|
||||
bones.Add((int)vertices[i]);
|
||||
weights.Add(vertices[i + 1] * scale);
|
||||
weights.Add(vertices[i + 2] * scale);
|
||||
weights.Add(vertices[i + 3]);
|
||||
}
|
||||
}
|
||||
mesh.bones = bones.ToArray();
|
||||
mesh.weights = 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;
|
||||
// potential BOZO see Java impl
|
||||
pathAttachment.lengths = GetFloatArray(map, "lengths", scale);
|
||||
return pathAttachment;
|
||||
}
|
||||
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;
|
||||
private void ReadVertices (Dictionary<String, Object> map, VertexAttachment attachment, int verticesLength) {
|
||||
attachment.WorldVerticesLength = verticesLength;
|
||||
float[] vertices = GetFloatArray(map, "vertices", 1);
|
||||
float scale = Scale;
|
||||
if (verticesLength == vertices.Length) {
|
||||
if (scale != 1) {
|
||||
for (int i = 0; i < vertices.Length; i++) {
|
||||
vertices[i] *= scale;
|
||||
}
|
||||
}
|
||||
attachment.vertices = vertices;
|
||||
return;
|
||||
}
|
||||
return values;
|
||||
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;) {
|
||||
int boneCount = (int)vertices[i++];
|
||||
bones.Add(boneCount);
|
||||
for (int nn = i + boneCount * 4; i < nn; i += 4) {
|
||||
bones.Add((int)vertices[i]);
|
||||
weights.Add(vertices[i + 1] * this.Scale);
|
||||
weights.Add(vertices[i + 2] * this.Scale);
|
||||
weights.Add(vertices[i + 3]);
|
||||
}
|
||||
}
|
||||
attachment.bones = bones.ToArray();
|
||||
attachment.vertices = weights.ToArray();
|
||||
}
|
||||
|
||||
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) {
|
||||
private void ReadAnimation (Dictionary<String, Object> map, String name, SkeletonData skeletonData) {
|
||||
var scale = this.Scale;
|
||||
var timelines = new ExposedList<Timeline>();
|
||||
float duration = 0;
|
||||
var scale = this.Scale;
|
||||
|
||||
// Slot timelines.
|
||||
if (map.ContainsKey("slots")) {
|
||||
foreach (KeyValuePair<String, Object> entry in (Dictionary<String, Object>)map["slots"]) {
|
||||
String slotName = entry.Key;
|
||||
int slotIndex = skeletonData.FindSlotIndex(slotName);
|
||||
var timelineMap = (Dictionary<String, Object>)entry.Value;
|
||||
|
||||
foreach (KeyValuePair<String, Object> timelineEntry in timelineMap) {
|
||||
var values = (List<Object>)timelineEntry.Value;
|
||||
var timelineName = (String)timelineEntry.Key;
|
||||
@ -477,11 +446,11 @@ namespace Spine {
|
||||
float time = (float)valueMap["time"];
|
||||
String c = (String)valueMap["color"];
|
||||
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++;
|
||||
}
|
||||
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") {
|
||||
var timeline = new AttachmentTimeline(values.Count);
|
||||
@ -501,13 +470,12 @@ namespace Spine {
|
||||
}
|
||||
}
|
||||
|
||||
// Bone timelines.
|
||||
if (map.ContainsKey("bones")) {
|
||||
foreach (KeyValuePair<String, Object> entry in (Dictionary<String, Object>)map["bones"]) {
|
||||
String boneName = entry.Key;
|
||||
int boneIndex = skeletonData.FindBoneIndex(boneName);
|
||||
if (boneIndex == -1)
|
||||
throw new Exception("Bone not found: " + boneName);
|
||||
|
||||
if (boneIndex == -1) throw new Exception("Bone not found: " + boneName);
|
||||
var timelineMap = (Dictionary<String, Object>)entry.Value;
|
||||
foreach (KeyValuePair<String, Object> timelineEntry in timelineMap) {
|
||||
var values = (List<Object>)timelineEntry.Value;
|
||||
@ -518,13 +486,12 @@ namespace Spine {
|
||||
|
||||
int frameIndex = 0;
|
||||
foreach (Dictionary<String, Object> valueMap in values) {
|
||||
float time = (float)valueMap["time"];
|
||||
timeline.SetFrame(frameIndex, time, (float)valueMap["angle"]);
|
||||
ReadCurve(timeline, frameIndex, valueMap);
|
||||
timeline.SetFrame(frameIndex, (float)valueMap["time"], (float)valueMap["angle"]);
|
||||
ReadCurve(valueMap, timeline, frameIndex);
|
||||
frameIndex++;
|
||||
}
|
||||
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") {
|
||||
TranslateTimeline timeline;
|
||||
@ -544,12 +511,12 @@ namespace Spine {
|
||||
float time = (float)valueMap["time"];
|
||||
float x = GetFloat(valueMap, "x", 0);
|
||||
float y = GetFloat(valueMap, "y", 0);
|
||||
timeline.SetFrame(frameIndex, time, (float)x * timelineScale, (float)y * timelineScale);
|
||||
ReadCurve(timeline, frameIndex, valueMap);
|
||||
timeline.SetFrame(frameIndex, time, x * timelineScale, y * timelineScale);
|
||||
ReadCurve(valueMap, timeline, frameIndex);
|
||||
frameIndex++;
|
||||
}
|
||||
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
|
||||
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")) {
|
||||
foreach (KeyValuePair<String, Object> constraintMap in (Dictionary<String, Object>)map["ik"]) {
|
||||
IkConstraintData constraint = skeletonData.FindIkConstraint(constraintMap.Key);
|
||||
@ -570,11 +537,11 @@ namespace Spine {
|
||||
float mix = GetFloat(valueMap, "mix", 1);
|
||||
bool bendPositive = GetBoolean(valueMap, "bendPositive", true);
|
||||
timeline.SetFrame(frameIndex, time, mix, bendPositive ? 1 : -1);
|
||||
ReadCurve(timeline, frameIndex, valueMap);
|
||||
ReadCurve(valueMap, timeline, frameIndex);
|
||||
frameIndex++;
|
||||
}
|
||||
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 shearMix = GetFloat(valueMap, "shearMix", 1);
|
||||
timeline.SetFrame(frameIndex, time, rotateMix, translateMix, scaleMix, shearMix);
|
||||
ReadCurve(timeline, frameIndex, valueMap);
|
||||
ReadCurve(valueMap, timeline, frameIndex);
|
||||
frameIndex++;
|
||||
}
|
||||
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.
|
||||
if (map.ContainsKey("ffd")) {
|
||||
foreach (KeyValuePair<String, Object> ffdMap in (Dictionary<String, Object>)map["ffd"]) {
|
||||
Skin skin = skeletonData.FindSkin(ffdMap.Key);
|
||||
foreach (KeyValuePair<String, Object> slotMap in (Dictionary<String, Object>)ffdMap.Value) {
|
||||
int slotIndex = skeletonData.FindSlotIndex(slotMap.Key);
|
||||
foreach (KeyValuePair<String, Object> meshMap in (Dictionary<String, Object>)slotMap.Value) {
|
||||
var values = (List<Object>)meshMap.Value;
|
||||
var timeline = new FfdTimeline(values.Count);
|
||||
Attachment attachment = skin.GetAttachment(slotIndex, meshMap.Key);
|
||||
if (attachment == null) throw new Exception("FFD attachment not found: " + meshMap.Key);
|
||||
timeline.slotIndex = slotIndex;
|
||||
timeline.attachment = attachment;
|
||||
|
||||
int vertexCount;
|
||||
if (attachment is MeshAttachment)
|
||||
vertexCount = ((MeshAttachment)attachment).vertices.Length;
|
||||
else
|
||||
vertexCount = ((WeightedMeshAttachment)attachment).Weights.Length / 3 * 2;
|
||||
|
||||
// Path constraint timelines.
|
||||
if (map.ContainsKey("paths")) {
|
||||
foreach (KeyValuePair<String, Object> constraintMap in (Dictionary<String, Object>)map["paths"]) {
|
||||
int index = skeletonData.FindPathConstraintIndex(constraintMap.Key);
|
||||
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) {
|
||||
float[] vertices;
|
||||
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);
|
||||
if (slotIndex == -1) throw new Exception("Slot not found: " + slotMap.Key);
|
||||
foreach (KeyValuePair<String, Object> timelineMap in (Dictionary<String, Object>)slotMap.Value) {
|
||||
var values = (List<Object>)timelineMap.Value;
|
||||
VertexAttachment attachment = (VertexAttachment)skin.GetAttachment(slotIndex, timelineMap.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.attachment = attachment;
|
||||
|
||||
int frameIndex = 0;
|
||||
foreach (Dictionary<String, Object> valueMap in values) {
|
||||
float[] deform;
|
||||
if (!valueMap.ContainsKey("vertices")) {
|
||||
if (attachment is MeshAttachment)
|
||||
vertices = ((MeshAttachment)attachment).vertices;
|
||||
else
|
||||
vertices = new float[vertexCount];
|
||||
deform = weighted ? new float[deformLength] : vertices;
|
||||
} else {
|
||||
var verticesValue = (List<Object>)valueMap["vertices"];
|
||||
vertices = new float[vertexCount];
|
||||
deform = new float[deformLength];
|
||||
int start = GetInt(valueMap, "offset", 0);
|
||||
if (scale == 1) {
|
||||
for (int i = 0, n = verticesValue.Count; i < n; i++)
|
||||
vertices[i + start] = (float)verticesValue[i];
|
||||
} else {
|
||||
for (int i = 0, n = verticesValue.Count; i < n; i++)
|
||||
vertices[i + start] = (float)verticesValue[i] * scale;
|
||||
float[] verticesValue = GetFloatArray(valueMap, "vertices", 1);
|
||||
Array.Copy(verticesValue, 0, deform, start, verticesValue.Length);
|
||||
if (scale != 1) {
|
||||
for (int i = start, n = i + verticesValue.Length; i < n; i++)
|
||||
deform[i] *= scale;
|
||||
}
|
||||
if (attachment is MeshAttachment) {
|
||||
float[] meshVertices = ((MeshAttachment)attachment).vertices;
|
||||
for (int i = 0; i < vertexCount; i++)
|
||||
vertices[i] += meshVertices[i];
|
||||
|
||||
if (!weighted) {
|
||||
for (int i = 0; i < deformLength; i++)
|
||||
deform[i] += vertices[i];
|
||||
}
|
||||
}
|
||||
|
||||
timeline.SetFrame(frameIndex, (float)valueMap["time"], vertices);
|
||||
ReadCurve(timeline, frameIndex, valueMap);
|
||||
timeline.SetFrame(frameIndex, (float)valueMap["time"], deform);
|
||||
ReadCurve(valueMap, timeline, frameIndex);
|
||||
frameIndex++;
|
||||
}
|
||||
timelines.Add(timeline);
|
||||
@ -658,6 +666,7 @@ namespace Spine {
|
||||
}
|
||||
}
|
||||
|
||||
// Draw order timeline.
|
||||
if (map.ContainsKey("drawOrder") || map.ContainsKey("draworder")) {
|
||||
var values = (List<Object>)map[map.ContainsKey("drawOrder") ? "drawOrder" : "draworder"];
|
||||
var timeline = new DrawOrderTimeline(values.Count);
|
||||
@ -695,6 +704,7 @@ namespace Spine {
|
||||
duration = Math.Max(duration, timeline.frames[timeline.FrameCount - 1]);
|
||||
}
|
||||
|
||||
// Event timeline.
|
||||
if (map.ContainsKey("events")) {
|
||||
var eventsMap = (List<Object>)map["events"];
|
||||
var timeline = new EventTimeline(eventsMap.Count);
|
||||
@ -716,29 +726,81 @@ namespace Spine {
|
||||
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"))
|
||||
return;
|
||||
Object curveObject = valueMap["curve"];
|
||||
if (curveObject.Equals("stepped"))
|
||||
timeline.SetStepped(frameIndex);
|
||||
else if (curveObject is List<Object>) {
|
||||
var curve = (List<Object>)curveObject;
|
||||
timeline.SetCurve(frameIndex, (float)curve[0], (float)curve[1], (float)curve[2], (float)curve[3]);
|
||||
else {
|
||||
var curve = curveObject as List<Object>;
|
||||
if (curve != null)
|
||||
timeline.SetCurve(frameIndex, (float)curve[0], (float)curve[1], (float)curve[2], (float)curve[3]);
|
||||
}
|
||||
}
|
||||
|
||||
internal class LinkedMesh {
|
||||
internal String parent, skin;
|
||||
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.skin = skin;
|
||||
this.slotIndex = slotIndex;
|
||||
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);
|
||||
|
||||
public String Name { get { return name; } }
|
||||
public Dictionary<AttachmentKeyTuple, Attachment> Attachments { get { return attachments; } }
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@ -59,13 +60,13 @@ namespace Spine {
|
||||
}
|
||||
|
||||
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)
|
||||
if (key.slotIndex == slotIndex) names.Add(key.name);
|
||||
}
|
||||
|
||||
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)
|
||||
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 string name;
|
||||
internal readonly int nameHashCode;
|
||||
|
||||
@ -38,8 +38,7 @@ namespace Spine {
|
||||
internal float r, g, b, a;
|
||||
internal Attachment attachment;
|
||||
internal float attachmentTime;
|
||||
internal float[] attachmentVertices = new float[0];
|
||||
internal int attachmentVerticesCount;
|
||||
internal ExposedList<float> attachmentVertices = new ExposedList<float>();
|
||||
|
||||
public SlotData Data { get { return data; } }
|
||||
public Bone Bone { get { return bone; } }
|
||||
@ -51,38 +50,31 @@ namespace Spine {
|
||||
|
||||
/// <summary>May be null.</summary>
|
||||
public Attachment Attachment {
|
||||
get {
|
||||
return attachment;
|
||||
}
|
||||
get { return attachment; }
|
||||
set {
|
||||
if (attachment == value) return;
|
||||
attachment = value;
|
||||
attachmentTime = bone.skeleton.time;
|
||||
attachmentVerticesCount = 0;
|
||||
attachmentVertices.Clear(false);
|
||||
}
|
||||
}
|
||||
|
||||
public float AttachmentTime {
|
||||
get {
|
||||
return bone.skeleton.time - attachmentTime;
|
||||
}
|
||||
set {
|
||||
attachmentTime = bone.skeleton.time - value;
|
||||
}
|
||||
get { return bone.skeleton.time - attachmentTime; }
|
||||
set { attachmentTime = bone.skeleton.time - value; }
|
||||
}
|
||||
|
||||
public float[] AttachmentVertices { get { return attachmentVertices; } set { attachmentVertices = value; } }
|
||||
public int AttachmentVerticesCount { get { return attachmentVerticesCount; } set { attachmentVerticesCount = value; } }
|
||||
public ExposedList<float> AttachmentVertices { get { return attachmentVertices; } set { attachmentVertices = value; } }
|
||||
|
||||
public Slot (SlotData data, Bone bone) {
|
||||
if (data == null) throw new ArgumentNullException("data cannot be null.");
|
||||
if (bone == null) throw new ArgumentNullException("bone cannot be null.");
|
||||
if (data == null) throw new ArgumentNullException("data", "data cannot be null.");
|
||||
if (bone == null) throw new ArgumentNullException("bone", "bone cannot be null.");
|
||||
this.data = data;
|
||||
this.bone = bone;
|
||||
SetToSetupPose();
|
||||
}
|
||||
|
||||
internal void SetToSetupPose (int slotIndex) {
|
||||
public void SetToSetupPose () {
|
||||
r = data.r;
|
||||
g = data.g;
|
||||
b = data.b;
|
||||
@ -91,14 +83,10 @@ namespace Spine {
|
||||
Attachment = null;
|
||||
else {
|
||||
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 () {
|
||||
return data.name;
|
||||
}
|
||||
|
||||
@ -33,12 +33,14 @@ using System;
|
||||
|
||||
namespace Spine {
|
||||
public class SlotData {
|
||||
internal int index;
|
||||
internal String name;
|
||||
internal BoneData boneData;
|
||||
internal float r = 1, g = 1, b = 1, a = 1;
|
||||
internal String attachmentName;
|
||||
internal BlendMode blendMode;
|
||||
|
||||
public int Index { get { return index; } }
|
||||
public String Name { get { return name; } }
|
||||
public BoneData BoneData { get { return boneData; } }
|
||||
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 BlendMode BlendMode { get { return blendMode; } set { blendMode = value; } }
|
||||
|
||||
public SlotData (String name, BoneData boneData) {
|
||||
if (name == null) throw new ArgumentNullException("name cannot be null.");
|
||||
if (boneData == null) throw new ArgumentNullException("boneData cannot be null.");
|
||||
public SlotData (int index, String name, BoneData boneData) {
|
||||
if (index < 0) throw new ArgumentException ("index must be >= 0.", "index");
|
||||
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.boneData = boneData;
|
||||
}
|
||||
|
||||
@ -30,46 +30,35 @@
|
||||
*****************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Spine {
|
||||
public class TransformConstraint : IUpdatable {
|
||||
internal TransformConstraintData data;
|
||||
internal Bone bone, target;
|
||||
internal ExposedList<Bone> bones;
|
||||
internal Bone target;
|
||||
internal float rotateMix, translateMix, scaleMix, shearMix;
|
||||
internal float offsetRotation, offsetX, offsetY, offsetScaleX, offsetScaleY, offsetShearY;
|
||||
|
||||
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 float RotateMix { get { return rotateMix; } set { rotateMix = value; } }
|
||||
public float TranslateMix { get { return translateMix; } set { translateMix = value; } }
|
||||
public float ScaleMix { get { return scaleMix; } set { scaleMix = 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) {
|
||||
if (data == null) throw new ArgumentNullException("data cannot be null.");
|
||||
if (skeleton == null) throw new ArgumentNullException("skeleton cannot be null.");
|
||||
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;
|
||||
translateMix = data.translateMix;
|
||||
rotateMix = data.rotateMix;
|
||||
translateMix = data.translateMix;
|
||||
scaleMix = data.scaleMix;
|
||||
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);
|
||||
}
|
||||
|
||||
@ -78,55 +67,59 @@ namespace Spine {
|
||||
}
|
||||
|
||||
public void Update () {
|
||||
Bone bone = this.bone;
|
||||
float rotateMix = this.rotateMix, translateMix = this.translateMix, scaleMix = this.scaleMix, shearMix = this.shearMix;
|
||||
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) {
|
||||
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;
|
||||
if (r > MathUtils.PI)
|
||||
r -= MathUtils.PI2;
|
||||
else if (r < -MathUtils.PI) r += MathUtils.PI2;
|
||||
r *= rotateMix;
|
||||
float cos = MathUtils.Cos(r), sin = MathUtils.Sin(r);
|
||||
bone.a = cos * a - sin * c;
|
||||
bone.b = cos * b - sin * d;
|
||||
bone.c = sin * a + cos * c;
|
||||
bone.d = sin * b + cos * d;
|
||||
}
|
||||
if (rotateMix > 0) {
|
||||
float a = bone.a, b = bone.b, c = bone.c, d = bone.d;
|
||||
float r = (float)Math.Atan2(tc, ta) - (float)Math.Atan2(c, a) + data.offsetRotation * MathUtils.degRad;
|
||||
if (r > MathUtils.PI)
|
||||
r -= MathUtils.PI2;
|
||||
else if (r < -MathUtils.PI) r += MathUtils.PI2;
|
||||
r *= rotateMix;
|
||||
float cos = MathUtils.Cos(r), sin = MathUtils.Sin(r);
|
||||
bone.a = cos * a - sin * c;
|
||||
bone.b = cos * b - sin * d;
|
||||
bone.c = sin * a + cos * c;
|
||||
bone.d = sin * b + cos * d;
|
||||
}
|
||||
|
||||
if (scaleMix > 0) {
|
||||
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 s = bs > 0.00001f ? (bs + (ts - bs + offsetScaleX) * scaleMix) / bs : 0;
|
||||
bone.a *= s;
|
||||
bone.c *= s;
|
||||
bs = (float)Math.Sqrt(bone.b * bone.b + bone.d * bone.d);
|
||||
ts = (float)Math.Sqrt(target.b * target.b + target.d * target.d);
|
||||
s = bs > 0.00001f ? (bs + (ts - bs + offsetScaleY) * scaleMix) / bs : 0;
|
||||
bone.b *= s;
|
||||
bone.d *= s;
|
||||
}
|
||||
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 (shearMix > 0) {
|
||||
float b = bone.b, d = bone.d;
|
||||
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));
|
||||
if (r > MathUtils.PI)
|
||||
r -= MathUtils.PI2;
|
||||
else if (r < -MathUtils.PI) r += MathUtils.PI2;
|
||||
r = by + (r + offsetShearY * MathUtils.degRad) * shearMix;
|
||||
float s = (float)Math.Sqrt(b * b + d * d);
|
||||
bone.b = MathUtils.Cos(r) * s;
|
||||
bone.d = MathUtils.Sin(r) * s;
|
||||
}
|
||||
if (scaleMix > 0) {
|
||||
float bs = (float)Math.Sqrt(bone.a * bone.a + bone.c * bone.c);
|
||||
float ts = (float)Math.Sqrt(ta * ta + tc * tc);
|
||||
float s = bs > 0.00001f ? (bs + (ts - bs + data.offsetScaleX) * scaleMix) / bs : 0;
|
||||
bone.a *= s;
|
||||
bone.c *= s;
|
||||
bs = (float)Math.Sqrt(bone.b * bone.b + bone.d * bone.d);
|
||||
ts = (float)Math.Sqrt(tb * tb + td * td);
|
||||
s = bs > 0.00001f ? (bs + (ts - bs + data.offsetScaleY) * scaleMix) / bs : 0;
|
||||
bone.b *= s;
|
||||
bone.d *= 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;
|
||||
if (shearMix > 0) {
|
||||
float b = bone.b, d = bone.d;
|
||||
float by = MathUtils.Atan2(d, b);
|
||||
float r = MathUtils.Atan2(td, tb) - MathUtils.Atan2(tc, ta) - (by - MathUtils.Atan2(bone.c, bone.a));
|
||||
if (r > MathUtils.PI)
|
||||
r -= MathUtils.PI2;
|
||||
else if (r < -MathUtils.PI) r += MathUtils.PI2;
|
||||
r = by + (r + data.offsetShearY * MathUtils.degRad) * shearMix;
|
||||
float s = (float)Math.Sqrt(b * b + d * d);
|
||||
bone.b = MathUtils.Cos(r) * s;
|
||||
bone.d = MathUtils.Sin(r) * s;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -30,17 +30,17 @@
|
||||
*****************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Spine {
|
||||
public class TransformConstraintData {
|
||||
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 offsetRotation, offsetX, offsetY, offsetScaleX, offsetScaleY, offsetShearY;
|
||||
|
||||
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 float RotateMix { get { return rotateMix; } set { rotateMix = 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 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;
|
||||
}
|
||||
|
||||
|
||||
@ -167,7 +167,7 @@ public class SkeletonJson {
|
||||
|
||||
String targetName = constraintMap.getString("target");
|
||||
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.mix = constraintMap.getFloat("mix", 1);
|
||||
@ -182,13 +182,13 @@ public class SkeletonJson {
|
||||
for (JsonValue boneMap = constraintMap.getChild("bones"); boneMap != null; boneMap = boneMap.next) {
|
||||
String boneName = boneMap.asString();
|
||||
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);
|
||||
}
|
||||
|
||||
String targetName = constraintMap.getString("target");
|
||||
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.offsetX = constraintMap.getFloat("x", 0) * scale;
|
||||
@ -218,7 +218,7 @@ public class SkeletonJson {
|
||||
|
||||
String targetName = constraintMap.getString("target");
|
||||
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.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-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.
|
||||
|
||||
|
||||
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 () {
|
||||
if (state == SpineBeginnerBodyState.Jumping) yield break; // Don't jump when already jumping.
|
||||
|
||||
// Fake 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;
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
guid: 6bc52290ef03f2846ba38d67e2823598
|
||||
timeCreated: 1455501336
|
||||
timeCreated: 1467205225
|
||||
licenseType: Free
|
||||
TextureImporter:
|
||||
fileIDToRecycleName: {}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 12c126994123f12468cf4c5a2684078a
|
||||
timeCreated: 1455501336
|
||||
timeCreated: 1467205225
|
||||
licenseType: Free
|
||||
TextureImporter:
|
||||
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
|
||||
guid: 49441e5a1682e564694545bd9b509785
|
||||
timeCreated: 1455501336
|
||||
timeCreated: 1467205225
|
||||
licenseType: Free
|
||||
TextureImporter:
|
||||
fileIDToRecycleName: {}
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,6 +1,6 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ddb89f63d0296cf4f8572b0448bb6b30
|
||||
timeCreated: 1455501337
|
||||
timeCreated: 1467205225
|
||||
licenseType: Free
|
||||
TextureImporter:
|
||||
fileIDToRecycleName: {}
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@ -1,6 +1,6 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 57b57f94df266f94ea0981915a4472e1
|
||||
timeCreated: 1455501336
|
||||
timeCreated: 1467205225
|
||||
licenseType: Free
|
||||
TextureImporter:
|
||||
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": [
|
||||
{ "name": "Root" },
|
||||
{ "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": "Leg", "parent": "Hip", "length": 31.39, "x": -20.32, "y": -13.85, "rotation": -105.82 },
|
||||
{ "name": "Leg2", "parent": "Hip", "length": 31.09, "x": 22.48, "y": -12.01, "rotation": -74.17 },
|
||||
{ "name": "Arm", "parent": "Body", "length": 51.63, "x": 49.91, "y": 37.35, "rotation": 166.67 },
|
||||
{ "name": "Arm2", "parent": "Body", "length": 52.61, "x": 53.81, "y": -28.52, "rotation": -157.17 },
|
||||
{ "name": "Feet", "parent": "Leg", "length": 15.4, "x": 39.56, "y": 1.59, "rotation": 14.56 },
|
||||
{ "name": "Feet2", "parent": "Leg2", "length": 12.32, "x": 41.33, "y": 0.12, "rotation": -17.19 },
|
||||
{ "name": "Head", "parent": "Body", "length": 65.29, "x": 73.6, "y": 1.09, "rotation": -88.23 },
|
||||
{ "name": "Shield", "parent": "Arm", "x": 45.01, "y": -2.1, "rotation": 123.56 },
|
||||
{ "name": "Weapon", "parent": "Arm2", "length": 137.65, "x": 48.2, "y": 12.78, "rotation": 92.5 }
|
||||
{ "name": "Body", "parent": "Hip", "length": 60.98, "rotation": 89.52, "x": 2.46, "y": -7.69 },
|
||||
{ "name": "Arm", "parent": "Body", "length": 51.63, "rotation": 166.67, "x": 49.91, "y": 37.34 },
|
||||
{ "name": "Arm2", "parent": "Body", "length": 52.61, "rotation": -157.17, "x": 53.81, "y": -28.52 },
|
||||
{ "name": "Leg", "parent": "Hip", "length": 31.39, "rotation": -105.82, "x": -20.32, "y": -13.85 },
|
||||
{ "name": "Feet", "parent": "Leg", "length": 15.4, "rotation": 14.56, "x": 39.56, "y": 1.59 },
|
||||
{ "name": "Leg2", "parent": "Hip", "length": 31.09, "rotation": -74.17, "x": 22.48, "y": -12.01 },
|
||||
{ "name": "Feet2", "parent": "Leg2", "length": 12.32, "rotation": -17.19, "x": 41.33, "y": 0.12 },
|
||||
{ "name": "Head", "parent": "Body", "length": 65.29, "rotation": -88.23, "x": 73.6, "y": 1.09 },
|
||||
{ "name": "Shield", "parent": "Arm", "rotation": 123.56, "x": 45.01, "y": -2.09 },
|
||||
{ "name": "Weapon", "parent": "Arm2", "length": 137.64, "rotation": 92.5, "x": 48.2, "y": 12.78 }
|
||||
],
|
||||
"slots": [
|
||||
{ "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 }
|
||||
},
|
||||
"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": {
|
||||
"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 ]
|
||||
},
|
||||
{ "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,
|
||||
"x": -1.04,
|
||||
"y": -2.35,
|
||||
"y": -2.34,
|
||||
"curve": [ 0.25, 0, 0.75, 1 ]
|
||||
},
|
||||
{ "time": 0.8333, "x": 0, "y": 0 }
|
||||
@ -345,7 +345,7 @@
|
||||
"translate": [
|
||||
{ "time": 0, "x": 0, "y": 0 },
|
||||
{ "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": {
|
||||
@ -491,7 +491,7 @@
|
||||
"y": 0,
|
||||
"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": {
|
||||
@ -686,7 +686,7 @@
|
||||
},
|
||||
{
|
||||
"time": 0.3666,
|
||||
"angle": -2.34,
|
||||
"angle": -2.33,
|
||||
"curve": [ 0.25, 0, 0.75, 1 ]
|
||||
},
|
||||
{ "time": 0.8666, "angle": 0 },
|
||||
@ -1518,17 +1518,17 @@
|
||||
{
|
||||
"time": 0,
|
||||
"x": -0.84,
|
||||
"y": -4.45,
|
||||
"y": -4.44,
|
||||
"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,
|
||||
"x": -1.03,
|
||||
"y": -4.7,
|
||||
"y": -4.69,
|
||||
"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": {
|
||||
|
||||
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": [
|
||||
{ "name": "root" },
|
||||
{ "name": "Bar", "parent": "root", "x": -112.29 }
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a11301aad15ed6b4995485a02a81b132
|
||||
timeCreated: 1455501336
|
||||
timeCreated: 1467205225
|
||||
licenseType: Free
|
||||
TextureImporter:
|
||||
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
|
||||
size: 1024,128
|
||||
goblins.png
|
||||
size: 795,142
|
||||
format: RGBA8888
|
||||
filter: Linear,Linear
|
||||
repeat: none
|
||||
dagger
|
||||
rotate: true
|
||||
xy: 372, 100
|
||||
rotate: false
|
||||
xy: 1, 33
|
||||
size: 26, 108
|
||||
orig: 26, 108
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
goblin/eyes-closed
|
||||
rotate: false
|
||||
xy: 2, 7
|
||||
xy: 760, 129
|
||||
size: 34, 12
|
||||
orig: 34, 12
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
goblin/head
|
||||
rotate: false
|
||||
xy: 107, 36
|
||||
rotate: true
|
||||
xy: 110, 38
|
||||
size: 103, 66
|
||||
orig: 103, 66
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
goblin/left-arm
|
||||
rotate: false
|
||||
xy: 901, 56
|
||||
rotate: true
|
||||
xy: 659, 7
|
||||
size: 37, 35
|
||||
orig: 37, 35
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
goblin/left-foot
|
||||
rotate: false
|
||||
xy: 929, 95
|
||||
xy: 1, 1
|
||||
size: 65, 31
|
||||
orig: 65, 31
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
goblin/left-hand
|
||||
rotate: false
|
||||
xy: 452, 2
|
||||
xy: 347, 3
|
||||
size: 36, 41
|
||||
orig: 36, 41
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
goblin/left-lower-leg
|
||||
rotate: true
|
||||
xy: 713, 93
|
||||
rotate: false
|
||||
xy: 420, 71
|
||||
size: 33, 70
|
||||
orig: 33, 70
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
goblin/left-shoulder
|
||||
rotate: false
|
||||
xy: 610, 44
|
||||
rotate: true
|
||||
xy: 684, 48
|
||||
size: 29, 44
|
||||
orig: 29, 44
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
goblin/left-upper-leg
|
||||
rotate: true
|
||||
xy: 638, 93
|
||||
rotate: false
|
||||
xy: 315, 68
|
||||
size: 33, 73
|
||||
orig: 33, 73
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
goblin/neck
|
||||
rotate: false
|
||||
xy: 490, 2
|
||||
xy: 384, 3
|
||||
size: 36, 41
|
||||
orig: 36, 41
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
goblin/pelvis
|
||||
rotate: false
|
||||
xy: 482, 45
|
||||
xy: 221, 1
|
||||
size: 62, 43
|
||||
orig: 62, 43
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
goblin/right-arm
|
||||
rotate: true
|
||||
xy: 690, 2
|
||||
rotate: false
|
||||
xy: 732, 91
|
||||
size: 23, 50
|
||||
orig: 23, 50
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
goblin/right-foot
|
||||
rotate: false
|
||||
xy: 771, 58
|
||||
rotate: true
|
||||
xy: 624, 78
|
||||
size: 63, 33
|
||||
orig: 63, 33
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
goblin/right-hand
|
||||
rotate: false
|
||||
xy: 940, 56
|
||||
xy: 585, 7
|
||||
size: 36, 37
|
||||
orig: 36, 37
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
goblin/right-lower-leg
|
||||
rotate: true
|
||||
xy: 482, 90
|
||||
xy: 67, 1
|
||||
size: 36, 76
|
||||
orig: 36, 76
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
goblin/right-shoulder
|
||||
rotate: true
|
||||
xy: 602, 3
|
||||
xy: 493, 5
|
||||
size: 39, 45
|
||||
orig: 39, 45
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
goblin/right-upper-leg
|
||||
rotate: true
|
||||
xy: 641, 57
|
||||
rotate: false
|
||||
xy: 554, 78
|
||||
size: 34, 63
|
||||
orig: 34, 63
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
goblin/torso
|
||||
rotate: true
|
||||
xy: 212, 34
|
||||
rotate: false
|
||||
xy: 177, 45
|
||||
size: 68, 96
|
||||
orig: 68, 96
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
goblin/undie-straps
|
||||
rotate: false
|
||||
xy: 380, 5
|
||||
rotate: true
|
||||
xy: 692, 86
|
||||
size: 55, 19
|
||||
orig: 55, 19
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
goblin/undies
|
||||
rotate: false
|
||||
xy: 174, 5
|
||||
xy: 756, 99
|
||||
size: 36, 29
|
||||
orig: 36, 29
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
goblingirl/eyes-closed
|
||||
rotate: false
|
||||
xy: 269, 11
|
||||
rotate: true
|
||||
xy: 729, 48
|
||||
size: 37, 21
|
||||
orig: 37, 21
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
goblingirl/head
|
||||
rotate: false
|
||||
xy: 2, 21
|
||||
rotate: true
|
||||
xy: 28, 38
|
||||
size: 103, 81
|
||||
orig: 103, 81
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
goblingirl/left-arm
|
||||
rotate: true
|
||||
xy: 978, 56
|
||||
xy: 724, 10
|
||||
size: 37, 35
|
||||
orig: 37, 35
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
goblingirl/left-foot
|
||||
rotate: false
|
||||
xy: 107, 3
|
||||
rotate: true
|
||||
xy: 522, 76
|
||||
size: 65, 31
|
||||
orig: 65, 31
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
goblingirl/left-hand
|
||||
rotate: false
|
||||
xy: 565, 2
|
||||
xy: 457, 4
|
||||
size: 35, 40
|
||||
orig: 35, 40
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
goblingirl/left-lower-leg
|
||||
rotate: true
|
||||
xy: 785, 93
|
||||
rotate: false
|
||||
xy: 454, 71
|
||||
size: 33, 70
|
||||
orig: 33, 70
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
goblingirl/left-shoulder
|
||||
rotate: true
|
||||
xy: 690, 27
|
||||
rotate: false
|
||||
xy: 695, 1
|
||||
size: 28, 46
|
||||
orig: 28, 46
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
goblingirl/left-upper-leg
|
||||
rotate: true
|
||||
xy: 857, 93
|
||||
rotate: false
|
||||
xy: 488, 71
|
||||
size: 33, 70
|
||||
orig: 33, 70
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
goblingirl/neck
|
||||
rotate: false
|
||||
xy: 528, 2
|
||||
xy: 421, 3
|
||||
size: 35, 41
|
||||
orig: 35, 41
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
goblingirl/pelvis
|
||||
rotate: false
|
||||
xy: 546, 45
|
||||
xy: 284, 1
|
||||
size: 62, 43
|
||||
orig: 62, 43
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
goblingirl/right-arm
|
||||
rotate: false
|
||||
xy: 452, 48
|
||||
xy: 756, 48
|
||||
size: 28, 50
|
||||
orig: 28, 50
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
goblingirl/right-foot
|
||||
rotate: false
|
||||
xy: 836, 58
|
||||
rotate: true
|
||||
xy: 658, 78
|
||||
size: 63, 33
|
||||
orig: 63, 33
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
goblingirl/right-hand
|
||||
rotate: true
|
||||
xy: 771, 20
|
||||
rotate: false
|
||||
xy: 622, 7
|
||||
size: 36, 37
|
||||
orig: 36, 37
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
goblingirl/right-lower-leg
|
||||
rotate: true
|
||||
xy: 560, 90
|
||||
xy: 144, 1
|
||||
size: 36, 76
|
||||
orig: 36, 76
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
goblingirl/right-shoulder
|
||||
rotate: false
|
||||
xy: 649, 10
|
||||
rotate: true
|
||||
xy: 539, 5
|
||||
size: 39, 45
|
||||
orig: 39, 45
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
goblingirl/right-upper-leg
|
||||
rotate: true
|
||||
xy: 706, 57
|
||||
rotate: false
|
||||
xy: 589, 78
|
||||
size: 34, 63
|
||||
orig: 34, 63
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
goblingirl/torso
|
||||
rotate: false
|
||||
xy: 310, 2
|
||||
xy: 246, 45
|
||||
size: 68, 96
|
||||
orig: 68, 96
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
goblingirl/undie-straps
|
||||
rotate: false
|
||||
xy: 212, 13
|
||||
rotate: true
|
||||
xy: 712, 86
|
||||
size: 55, 19
|
||||
orig: 55, 19
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
goblingirl/undies
|
||||
rotate: false
|
||||
xy: 810, 27
|
||||
rotate: true
|
||||
xy: 760, 11
|
||||
size: 36, 29
|
||||
orig: 36, 29
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
shield
|
||||
rotate: false
|
||||
xy: 380, 26
|
||||
xy: 349, 69
|
||||
size: 70, 72
|
||||
orig: 70, 72
|
||||
offset: 0, 0
|
||||
index: -1
|
||||
spear
|
||||
rotate: true
|
||||
xy: 2, 104
|
||||
xy: 315, 45
|
||||
size: 22, 368
|
||||
orig: 22, 368
|
||||
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
|
||||
guid: 803c2e614a63081439fde6276d110661
|
||||
timeCreated: 1455501336
|
||||
guid: 5fb7efec30c79cb46a705e0d04debb04
|
||||
timeCreated: 1467205225
|
||||
licenseType: Free
|
||||
TextureImporter:
|
||||
fileIDToRecycleName: {}
|
||||
@ -30,15 +30,15 @@ TextureImporter:
|
||||
maxTextureSize: 2048
|
||||
textureSettings:
|
||||
filterMode: -1
|
||||
aniso: -1
|
||||
aniso: 16
|
||||
mipBias: -1
|
||||
wrapMode: -1
|
||||
nPOTScale: 1
|
||||
wrapMode: 1
|
||||
nPOTScale: 0
|
||||
lightmap: 0
|
||||
rGBM: 0
|
||||
compressionQuality: 50
|
||||
allowsAlphaSplitting: 0
|
||||
spriteMode: 0
|
||||
spriteMode: 1
|
||||
spriteExtrude: 1
|
||||
spriteMeshType: 1
|
||||
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