Updated spine-csharp to Spine v3.

This commit is contained in:
NathanSweet 2016-02-14 22:51:18 +01:00
parent 4b7fc36328
commit 08b74f5b18
27 changed files with 2343 additions and 588 deletions

View File

@ -53,35 +53,91 @@
</ItemGroup>
<ItemGroup>
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="src\Animation.cs" />
<Compile Include="src\AnimationStateData.cs" />
<Compile Include="src\Atlas.cs" />
<Compile Include="src\Attachments\AttachmentLoader.cs" />
<Compile Include="src\Animation.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="src\AnimationState.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="src\AnimationStateData.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="src\Atlas.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="src\Attachments\AtlasAttachmentLoader.cs" />
<Compile Include="src\Attachments\Attachment.cs" />
<Compile Include="src\Attachments\AttachmentLoader.cs" />
<Compile Include="src\Attachments\AttachmentType.cs" />
<Compile Include="src\Attachments\BoundingBoxAttachment.cs" />
<Compile Include="src\Attachments\MeshAttachment.cs" />
<Compile Include="src\Attachments\RegionAttachment.cs" />
<Compile Include="src\Attachments\SkinnedMeshAttachment.cs" />
<Compile Include="src\BlendMode.cs" />
<Compile Include="src\Bone.cs" />
<Compile Include="src\BoneData.cs" />
<Compile Include="src\AnimationState.cs" />
<Compile Include="src\Event.cs" />
<Compile Include="src\EventData.cs" />
<Compile Include="src\ExposedList.cs" />
<Compile Include="src\IkConstraint.cs" />
<Compile Include="src\IkConstraintData.cs" />
<Compile Include="src\Json.cs" />
<Compile Include="src\Skeleton.cs" />
<Compile Include="src\SkeletonData.cs" />
<Compile Include="src\SkeletonJson.cs" />
<Compile Include="src\SkeletonBinary.cs" />
<Compile Include="src\Skin.cs" />
<Compile Include="src\Slot.cs" />
<Compile Include="src\SlotData.cs" />
<Compile Include="src\Attachments\WeightedMeshAttachment.cs" />
<Compile Include="src\BlendMode.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="src\Bone.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="src\BoneData.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="src\Event.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="src\EventData.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="src\ExposedList.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="src\IkConstraint.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="src\IkConstraintData.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="src\IUpdatable.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="src\Json.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="src\MathUtils.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="src\Skeleton.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="src\SkeletonBinary.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="src\SkeletonBounds.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="src\SkeletonData.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="src\SkeletonJson.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="src\Skin.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="src\Slot.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="src\SlotData.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="src\TransformConstraint.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="src\TransformConstraintData.cs">
<SubType>Code</SubType>
</Compile>
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<!--
To modify your build process, add your task inside one of the targets below and uncomment it.
@ -91,4 +147,4 @@
<Target Name="AfterBuild">
</Target>
-->
</Project>
</Project>

View File

@ -674,52 +674,4 @@ namespace Spine {
ikConstraint.bendDirection = (int)frames[frameIndex + PREV_FRAME_BEND_DIRECTION];
}
}
public class FlipXTimeline : Timeline {
internal int boneIndex;
internal float[] frames;
public int BoneIndex { get { return boneIndex; } set { boneIndex = value; } }
public float[] Frames { get { return frames; } set { frames = value; } } // time, flip, ...
public int FrameCount { get { return frames.Length >> 1; } }
public FlipXTimeline (int frameCount) {
frames = new float[frameCount << 1];
}
/// <summary>Sets the time and value of the specified keyframe.</summary>
public void SetFrame (int frameIndex, float time, bool flip) {
frameIndex *= 2;
frames[frameIndex] = time;
frames[frameIndex + 1] = flip ? 1 : 0;
}
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;
int frameIndex = (time >= frames[frames.Length - 2] ? frames.Length : Animation.binarySearch(frames, time, 2)) - 2;
if (frames[frameIndex] < lastTime) return;
SetFlip(skeleton.bones.Items[boneIndex], frames[frameIndex + 1] != 0);
}
virtual protected void SetFlip (Bone bone, bool flip) {
bone.flipX = flip;
}
}
public class FlipYTimeline : FlipXTimeline {
public FlipYTimeline (int frameCount)
: base(frameCount) {
}
override protected void SetFlip (Bone bone, bool flip) {
bone.flipY = flip;
}
}
}

View File

@ -74,10 +74,10 @@ namespace Spine {
return attachment;
}
public SkinnedMeshAttachment NewSkinnedMeshAttachment (Skin skin, String name, String path) {
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 + " (skinned mesh attachment: " + name + ")");
SkinnedMeshAttachment attachment = new SkinnedMeshAttachment(name);
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;

View File

@ -40,7 +40,7 @@ namespace Spine {
MeshAttachment NewMeshAttachment (Skin skin, String name, String path);
/// <return>May be null to not load any attachment.</return>
SkinnedMeshAttachment NewSkinnedMeshAttachment (Skin skin, String name, String path);
WeightedMeshAttachment NewWeightedMeshAttachment (Skin skin, String name, String path);
/// <return>May be null to not load any attachment.</return>
BoundingBoxAttachment NewBoundingBoxAttachment (Skin skin, String name);

View File

@ -31,6 +31,6 @@
namespace Spine {
public enum AttachmentType {
region, boundingbox, mesh, skinnedmesh
region, boundingbox, mesh, weightedmesh
}
}

View File

@ -45,10 +45,10 @@ namespace Spine {
/// <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.m00;
float m01 = bone.m01;
float m10 = bone.m10;
float m11 = bone.m11;
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];

View File

@ -32,7 +32,7 @@
using System;
namespace Spine {
/// <summary>Attachment that displays a texture region.</summary>
/// <summary>Attachment that displays a texture region using a mesh.</summary>
public class MeshAttachment : Attachment {
internal float[] vertices, uvs, regionUVs;
internal int[] triangles;
@ -94,7 +94,7 @@ 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.m00, m01 = bone.m01, m10 = bone.m10, m11 = bone.m11;
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;

View File

@ -137,7 +137,7 @@ 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.m00, m01 = bone.m01, m10 = bone.m10, m11 = bone.m11;
float m00 = bone.a, m01 = bone.b, m10 = bone.c, m11 = bone.d;
float[] offset = this.offset;
worldVertices[X1] = offset[X1] * m00 + offset[Y1] * m01 + x;
worldVertices[Y1] = offset[X1] * m10 + offset[Y1] * m11 + y;

View File

@ -33,8 +33,8 @@ using System;
using System.Collections.Generic;
namespace Spine {
/// <summary>Attachment that displays a texture region.</summary>
public class SkinnedMeshAttachment : Attachment {
/// <summary>Attachment that displays a texture region using a mesh which can be deformed by bones.</summary>
public class WeightedMeshAttachment : Attachment {
internal int[] bones;
internal float[] weights, uvs, regionUVs;
internal int[] triangles;
@ -72,7 +72,7 @@ namespace Spine {
public float Width { get; set; }
public float Height { get; set; }
public SkinnedMeshAttachment (string name)
public WeightedMeshAttachment (string name)
: base(name) {
}
@ -107,8 +107,8 @@ namespace Spine {
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.m00 + vy * bone.m01 + bone.worldX) * weight;
wy += (vx * bone.m10 + vy * bone.m11 + bone.worldY) * weight;
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;
@ -121,8 +121,8 @@ namespace Spine {
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.m00 + vy * bone.m01 + bone.worldX) * weight;
wy += (vx * bone.m10 + vy * bone.m11 + bone.worldY) * weight;
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;

View File

@ -33,18 +33,19 @@ using System;
using System.Collections.Generic;
namespace Spine {
public class Bone {
public class Bone : IUpdatable {
static public bool yDown;
internal BoneData data;
internal Skeleton skeleton;
internal Bone parent;
internal ExposedList<Bone> children = new ExposedList<Bone>();
internal float x, y, rotation, rotationIK, scaleX, scaleY;
internal bool flipX, flipY;
internal float m00, m01, m10, m11;
internal float worldX, worldY, worldRotation, worldScaleX, worldScaleY;
internal bool worldFlipX, worldFlipY;
internal float x, y, rotation, scaleX, scaleY;
internal float appliedRotation, appliedScaleX, appliedScaleY;
internal float a, b, worldX;
internal float c, d, worldY;
internal float worldSignX, worldSignY;
public BoneData Data { get { return data; } }
public Skeleton Skeleton { get { return skeleton; } }
@ -52,26 +53,28 @@ namespace Spine {
public ExposedList<Bone> Children { get { return children; } }
public float X { get { return x; } set { x = value; } }
public float Y { get { return y; } set { y = value; } }
/// <summary>The forward kinetics rotation.</summary>
public float Rotation { get { return rotation; } set { rotation = value; } }
/// <summary>The inverse kinetics rotation, as calculated by any IK constraints.</summary>
public float RotationIK { get { return rotationIK; } set { rotationIK = 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 bool FlipX { get { return flipX; } set { flipX = value; } }
public bool FlipY { get { return flipY; } set { flipY = value; } }
public float M00 { get { return m00; } }
public float M01 { get { return m01; } }
public float M10 { get { return m10; } }
public float M11 { get { return m11; } }
public float A { get { return a; } }
public float B { get { return b; } }
public float C { get { return c; } }
public float D { get { return d; } }
public float WorldX { get { return worldX; } }
public float WorldY { get { return worldY; } }
public float WorldRotation { get { return worldRotation; } }
public float WorldScaleX { get { return worldScaleX; } }
public float WorldScaleY { get { return worldScaleY; } }
public bool WorldFlipX { get { return worldFlipX; } set { worldFlipX = value; } }
public bool WorldFlipY { get { return worldFlipY; } set { worldFlipY = value; } }
public float WorldSignX { get { return worldSignX; } }
public float WorldSignY { get { return worldSignY; } }
public float WorldRotationX { get { return MathUtils.Atan2(c, a) * MathUtils.radDeg; } }
public float WorldRotationY { get { return MathUtils.Atan2(d, b) * MathUtils.radDeg; } }
public float WorldScaleX { get { return (float)Math.Sqrt(a * a + b * b) * worldSignX; } }
public float WorldScaleY { get { return (float)Math.Sqrt(c * c + d * d) * worldSignY; } }
/// <param name="parent">May be null.</param>
public Bone (BoneData data, Skeleton skeleton, Bone parent) {
@ -83,80 +86,155 @@ namespace Spine {
SetToSetupPose();
}
/// <summary>Computes the world SRT using the parent bone and the local SRT.</summary>
/// <summary>Computes the world SRT using the parent bone and this bone's local SRT.</summary>
public void UpdateWorldTransform () {
Bone parent = this.parent;
float x = this.x, y = this.y;
if (parent != null) {
worldX = x * parent.m00 + y * parent.m01 + parent.worldX;
worldY = x * parent.m10 + y * parent.m11 + parent.worldY;
if (data.inheritScale) {
worldScaleX = parent.worldScaleX * scaleX;
worldScaleY = parent.worldScaleY * scaleY;
} else {
worldScaleX = scaleX;
worldScaleY = scaleY;
}
worldRotation = data.inheritRotation ? parent.worldRotation + rotationIK : rotationIK;
worldFlipX = parent.worldFlipX != flipX;
worldFlipY = parent.worldFlipY != flipY;
} else {
Skeleton skeleton = this.skeleton;
bool skeletonFlipX = skeleton.flipX, skeletonFlipY = skeleton.flipY;
worldX = skeletonFlipX ? -x : x;
worldY = skeletonFlipY != yDown ? -y : y;
worldScaleX = scaleX;
worldScaleY = scaleY;
worldRotation = rotationIK;
worldFlipX = skeletonFlipX != flipX;
worldFlipY = skeletonFlipY != flipY;
}
float radians = worldRotation * (float)Math.PI / 180;
float cos = (float)Math.Cos(radians);
float sin = (float)Math.Sin(radians);
if (worldFlipX) {
m00 = -cos * worldScaleX;
m01 = sin * worldScaleY;
} else {
m00 = cos * worldScaleX;
m01 = -sin * worldScaleY;
}
if (worldFlipY != yDown) {
m10 = -sin * worldScaleX;
m11 = -cos * worldScaleY;
} else {
m10 = sin * worldScaleX;
m11 = cos * worldScaleY;
}
UpdateWorldTransform(x, y, rotation, scaleX, scaleY);
}
/// <summary>Computes the world SRT using the parent bone and the specified local SRT.</summary>
public void UpdateWorldTransform (float x, float y, float rotation, float scaleX, float scaleY) {
appliedRotation = rotation;
appliedScaleX = scaleX;
appliedScaleY = scaleY;
float cos = MathUtils.CosDeg(rotation), sin = MathUtils.SinDeg(rotation);
float la = cos * scaleX, lb = -sin * scaleY, lc = sin * scaleX, ld = cos * scaleY;
Bone parent = this.parent;
if (parent == null) { // Root bone.
Skeleton skeleton = this.skeleton;
if (skeleton.flipX) {
la = -la;
lc = -lc;
scaleX = -scaleX;
x = -x;
}
if (skeleton.flipY != yDown) {
lb = -lb;
ld = -ld;
scaleY = -scaleY;
y = -y;
}
a = la;
b = lb;
c = lc;
d = ld;
worldX = x;
worldY = y;
worldSignX = Math.Sign(scaleX);
worldSignY = Math.Sign(scaleY);
return;
}
float pa = parent.a, pb = parent.b, pc = parent.c, pd = parent.d;
worldX = pa * x + pb * y + parent.worldX;
worldY = pc * x + pd * y + parent.worldY;
worldSignX = parent.worldSignX * Math.Sign(scaleX);
worldSignY = parent.worldSignY * Math.Sign(scaleY);
if (data.inheritRotation && data.inheritScale) {
a = pa * la + pb * lc;
b = pa * lb + pb * ld;
c = pc * la + pd * lc;
d = pc * lb + pd * ld;
} else if (data.inheritRotation) { // No scale inheritance.
Bone p = parent;
pa = 1;
pb = 0;
pc = 0;
pd = 1;
while (p != null) {
cos = MathUtils.CosDeg(p.appliedRotation);
sin = MathUtils.SinDeg(p.appliedRotation);
float a = pa * cos + pb * sin;
float b = pa * -sin + pb * cos;
float c = pc * cos + pd * sin;
float d = pc * -sin + pd * cos;
pa = a;
pb = b;
pc = c;
pd = d;
p = p.parent;
}
if (yDown) {
pb = -pb;
pd = -pd;
}
this.a = pa * la + pb * lc;
this.b = pa * lb + pb * ld;
this.c = pc * la + pd * lc;
this.d = pc * lb + pd * ld;
} else if (data.inheritScale) { // No rotation inheritance.
Bone p = parent;
pa = 1;
pb = 0;
pc = 0;
pd = 1;
while (p != null) {
float r = p.rotation;
cos = MathUtils.CosDeg(r);
sin = MathUtils.SinDeg(r);
float psx = p.appliedScaleX, psy = p.appliedScaleY;
float za = cos * psx, zb = -sin * psy, zc = sin * psx, zd = cos * psy;
float temp = pa * za + pb * zc;
pb = pa * zb + pb * zd;
pa = temp;
temp = pc * za + pd * zc;
pd = pc * zb + pd * zd;
pc = temp;
if (psx < 0) r = -r;
cos = MathUtils.CosDeg(-r);
sin = MathUtils.SinDeg(-r);
temp = pa * cos + pb * sin;
pb = pa * -sin + pb * cos;
pa = temp;
temp = pc * cos + pd * sin;
pd = pc * -sin + pd * cos;
pc = temp;
p = p.parent;
}
if (yDown) {
pb = -pb;
pd = -pd;
}
a = pa * la + pb * lc;
b = pa * lb + pb * ld;
c = pc * la + pd * lc;
d = pc * lb + pd * ld;
} else {
a = la;
b = lb;
c = lc;
d = ld;
}
}
public void Update () {
UpdateWorldTransform(x, y, rotation, scaleX, scaleY);
}
public void SetToSetupPose () {
BoneData data = this.data;
x = data.x;
y = data.y;
rotation = data.rotation;
rotationIK = rotation;
scaleX = data.scaleX;
scaleY = data.scaleY;
flipX = data.flipX;
flipY = data.flipY;
BoneData data = this.data;
x = data.x;
y = data.y;
rotation = data.rotation;
scaleX = data.scaleX;
scaleY = data.scaleY;
}
public void worldToLocal (float worldX, float worldY, out float localX, out float localY) {
float dx = worldX - this.worldX, dy = worldY - this.worldY;
float m00 = this.m00, m10 = this.m10, m01 = this.m01, m11 = this.m11;
if (worldFlipX != (worldFlipY != yDown)) {
m00 = -m00;
m11 = -m11;
}
float invDet = 1 / (m00 * m11 - m01 * m10);
localX = (dx * m00 * invDet - dy * m01 * invDet);
localY = (dy * m11 * invDet - dx * m10 * invDet);
public void WorldToLocal (float worldX, float worldY, out float localX, out float localY) {
float x = worldX - this.worldX, y = worldY - this.worldY;
float a = this.a, b = this.b, c = this.c, d = this.d;
float invDet = 1 / (a * d - b * c);
localX = (x * a * invDet - y * b * invDet);
localY = (y * d * invDet - x * c * invDet);
}
public void localToWorld (float localX, float localY, out float worldX, out float worldY) {
worldX = localX * m00 + localY * m01 + this.worldX;
worldY = localX * m10 + localY * m11 + this.worldY;
public void LocalToWorld (float localX, float localY, out float worldX, out float worldY) {
float x = localX, y = localY;
worldX = x * a + y * b + this.worldX;
worldY = x * c + y * d + this.worldY;
}
override public String ToString () {

View File

@ -36,7 +36,6 @@ namespace Spine {
internal BoneData parent;
internal String name;
internal float length, x, y, rotation, scaleX = 1, scaleY = 1;
internal bool flipX, flipY;
internal bool inheritScale = true, inheritRotation = true;
/// <summary>May be null.</summary>
@ -48,8 +47,6 @@ namespace Spine {
public float Rotation { get { return rotation; } set { rotation = value; } }
public float ScaleX { get { return scaleX; } set { scaleX = value; } }
public float ScaleY { get { return scaleY; } set { scaleY = value; } }
public bool FlipX { get { return flipX; } set { flipX = value; } }
public bool FlipY { get { return flipY; } set { flipY = value; } }
public bool InheritScale { get { return inheritScale; } set { inheritScale = value; } }
public bool InheritRotation { get { return inheritRotation; } set { inheritRotation = value; } }

View File

@ -37,8 +37,9 @@ namespace Spine {
public int Int { get; set; }
public float Float { get; set; }
public String String { get; set; }
public float Time { get; private set; }
public Event (EventData data) {
public Event (float time, EventData data) {
Data = data;
}

View File

@ -0,0 +1,38 @@
/******************************************************************************
* 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 interface IUpdatable {
void Update ();
}
}

View File

@ -33,9 +33,7 @@ using System;
using System.Collections.Generic;
namespace Spine {
public class IkConstraint {
private const float radDeg = 180 / (float)Math.PI;
public class IkConstraint : IUpdatable {
internal IkConstraintData data;
internal ExposedList<Bone> bones = new ExposedList<Bone>();
internal Bone target;
@ -61,15 +59,19 @@ namespace Spine {
target = skeleton.FindBone(data.target.name);
}
public void apply () {
public void Update () {
Apply();
}
public void Apply () {
Bone target = this.target;
ExposedList<Bone> bones = this.bones;
switch (bones.Count) {
case 1:
apply(bones.Items[0], target.worldX, target.worldY, mix);
Apply(bones.Items[0], target.worldX, target.worldY, mix);
break;
case 2:
apply(bones.Items[0], bones.Items[1], target.worldX, target.worldY, bendDirection, mix);
Apply(bones.Items[0], bones.Items[1], target.worldX, target.worldY, bendDirection, mix);
break;
}
}
@ -80,72 +82,137 @@ namespace Spine {
/// <summary>Adjusts the bone rotation so the tip is as close to the target position as possible. The target is specified
/// in the world coordinate system.</summary>
static public void apply (Bone bone, float targetX, float targetY, float alpha) {
float parentRotation = (!bone.data.inheritRotation || bone.parent == null) ? 0 : bone.parent.worldRotation;
static public void Apply (Bone bone, float targetX, float targetY, float alpha) {
float parentRotation = bone.parent == null ? 0 : bone.parent.WorldRotationX;
float rotation = bone.rotation;
float rotationIK = (float)Math.Atan2(targetY - bone.worldY, targetX - bone.worldX) * radDeg;
if (bone.worldFlipX != (bone.worldFlipY != Bone.yDown)) rotationIK = -rotationIK;
rotationIK -= parentRotation;
bone.rotationIK = rotation + (rotationIK - rotation) * alpha;
float rotationIK = MathUtils.Atan2(targetY - bone.worldY, targetX - bone.worldX) * MathUtils.radDeg - parentRotation;
if (bone.worldSignX != bone.worldSignY) rotationIK = 360 - rotationIK;
if (rotationIK > 180) rotationIK -= 360;
else if (rotationIK < -180) rotationIK += 360;
bone.UpdateWorldTransform(bone.x, bone.y, rotation + (rotationIK - rotation) * alpha, bone.scaleX, bone.scaleY);
}
/// <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">Any descendant bone of the parent.</param>
static public void apply (Bone parent, Bone child, float targetX, float targetY, int bendDirection, float alpha) {
float childRotation = child.rotation, parentRotation = parent.rotation;
if (alpha == 0) {
child.rotationIK = childRotation;
parent.rotationIK = parentRotation;
return;
}
float positionX, positionY;
Bone parentParent = parent.parent;
if (parentParent != null) {
parentParent.worldToLocal(targetX, targetY, out positionX, out positionY);
targetX = (positionX - parent.x) * parentParent.worldScaleX;
targetY = (positionY - parent.y) * parentParent.worldScaleY;
/// <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.scaleX, psy = parent.scaleY, csx = child.scaleX, cy = child.y;
int offset1, offset2, sign2;
if (psx < 0) {
psx = -psx;
offset1 = 180;
sign2 = -1;
} else {
targetX -= parent.x;
targetY -= parent.y;
offset1 = 0;
sign2 = 1;
}
if (child.parent == parent) {
positionX = child.x;
positionY = child.y;
if (psy < 0) {
psy = -psy;
sign2 = -sign2;
}
if (csx < 0) {
csx = -csx;
offset2 = 180;
} else
offset2 = 0;
Bone pp = parent.parent;
float tx, ty, dx, dy;
if (pp == null) {
tx = targetX - px;
ty = targetY - py;
dx = child.worldX - px;
dy = child.worldY - py;
} else {
child.parent.localToWorld(child.x, child.y, out positionX, out positionY);
parent.worldToLocal(positionX, positionY, out positionX, out positionY);
float a = pp.a, b = pp.b, c = pp.c, d = pp.d, invDet = 1 / (a * d - b * c);
float wx = pp.worldX, wy = pp.worldY, x = targetX - wx, y = targetY - wy;
tx = (x * d - y * b) * invDet - px;
ty = (y * a - x * c) * invDet - py;
x = child.worldX - wx;
y = child.worldY - wy;
dx = (x * d - y * b) * invDet - px;
dy = (y * a - x * c) * invDet - py;
}
float childX = positionX * parent.worldScaleX, childY = positionY * parent.worldScaleY;
float offset = (float)Math.Atan2(childY, childX);
float len1 = (float)Math.Sqrt(childX * childX + childY * childY), len2 = child.data.length * child.worldScaleX;
// Based on code by Ryan Juckett with permission: Copyright (c) 2008-2009 Ryan Juckett, http://www.ryanjuckett.com/
float cosDenom = 2 * len1 * len2;
if (cosDenom < 0.0001f) {
child.rotationIK = childRotation + ((float)Math.Atan2(targetY, targetX) * radDeg - parentRotation - childRotation)
* alpha;
return;
float l1 = (float)Math.Sqrt(dx * dx + dy * dy), l2 = child.data.length * csx, a1, a2;
if (Math.Abs(psx - psy) <= 0.0001f) {
l2 *= psx;
float cos = (tx * tx + ty * ty - l1 * l1 - l2 * l2) / (2 * l1 * l2);
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);
} else {
cy = 0;
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;
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 r = Math.Abs(r0) < Math.Abs(r1) ? r0 : r1;
if (r * r <= dd) {
float y1 = (float)Math.Sqrt(dd - r * r) * bendDir;
a1 = ta - MathUtils.Atan2(y1, r);
a2 = MathUtils.Atan2(y1 / psy, (r - l1) / psx);
goto outer;
}
}
float minAngle = 0, minDist = float.MaxValue, minX = 0, minY = 0;
float maxAngle = 0, maxDist = 0, maxX = 0, maxY = 0;
float x = l1 + a, dist = x * x;
if (dist > maxDist) {
maxAngle = 0;
maxDist = dist;
maxX = x;
}
x = l1 - a;
dist = x * x;
if (dist < minDist) {
minAngle = MathUtils.PI;
minDist = dist;
minX = x;
}
float angle = (float)Math.Acos(-a * l1 / (aa - bb));
x = a * MathUtils.Cos(angle) + l1;
float y = b * MathUtils.Sin(angle);
dist = x * x + y * y;
if (dist < minDist) {
minAngle = angle;
minDist = dist;
minX = x;
minY = y;
}
if (dist > maxDist) {
maxAngle = angle;
maxDist = dist;
maxX = x;
maxY = y;
}
if (dd <= (minDist + maxDist) / 2) {
a1 = ta - MathUtils.Atan2(minY * bendDir, minX);
a2 = minAngle * bendDir;
} else {
a1 = ta - MathUtils.Atan2(maxY * bendDir, maxX);
a2 = maxAngle * bendDir;
}
}
float cos = (targetX * targetX + targetY * targetY - len1 * len1 - len2 * len2) / cosDenom;
if (cos < -1)
cos = -1;
else if (cos > 1)
cos = 1;
float childAngle = (float)Math.Acos(cos) * bendDirection;
float adjacent = len1 + len2 * cos, opposite = len2 * (float)Math.Sin(childAngle);
float parentAngle = (float)Math.Atan2(targetY * adjacent - targetX * opposite, targetX * adjacent + targetY * opposite);
float rotation = (parentAngle - offset) * radDeg - parentRotation;
if (rotation > 180)
rotation -= 360;
else if (rotation < -180) //
rotation += 360;
parent.rotationIK = parentRotation + rotation * alpha;
rotation = (childAngle + offset) * radDeg - childRotation;
if (rotation > 180)
rotation -= 360;
else if (rotation < -180) //
rotation += 360;
child.rotationIK = childRotation + (rotation + parent.worldRotation - child.parent.worldRotation) * alpha;
outer:
float offset = MathUtils.Atan2(cy, child.x) * sign2;
a1 = (a1 - offset) * MathUtils.radDeg + offset1;
a2 = (a2 + offset) * MathUtils.radDeg * sign2 + offset2;
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(parent.x, parent.y, rotation + (a1 - rotation) * alpha, parent.scaleX, parent.scaleY);
rotation = child.rotation;
child.UpdateWorldTransform(child.x, cy, rotation + (a2 - rotation) * alpha, child.scaleX, child.scaleY);
}
}
}

View File

@ -0,0 +1,94 @@
/******************************************************************************
* 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 MathUtils {
static public float PI = 3.1415927f;
static public float radDeg = 180f / PI;
static public float degRad = PI / 180;
static private int SIN_BITS = 14; // 16KB. Adjust for accuracy.
static private int SIN_MASK = ~(-1 << SIN_BITS);
static private int SIN_COUNT = SIN_MASK + 1;
static private float radFull = PI * 2;
static private float degFull = 360;
static private float radToIndex = SIN_COUNT / radFull;
static private float degToIndex = SIN_COUNT / degFull;
static float[] sin = new float[SIN_COUNT];
static MathUtils () {
for (int i = 0; i < SIN_COUNT; i++)
sin[i] = (float)Math.Sin((i + 0.5f) / SIN_COUNT * radFull);
for (int i = 0; i < 360; i += 90)
sin[(int)(i * degToIndex) & SIN_MASK] = (float)Math.Sin(i * degRad);
}
/** Returns the sine in radians from a lookup table. */
static public float Sin (float radians) {
return sin[(int)(radians * radToIndex) & SIN_MASK];
}
/** Returns the cosine in radians from a lookup table. */
static public float Cos (float radians) {
return sin[(int)((radians + PI / 2) * radToIndex) & SIN_MASK];
}
/** Returns the sine in radians from a lookup table. */
static public float SinDeg (float degrees) {
return sin[(int)(degrees * degToIndex) & SIN_MASK];
}
/** Returns the cosine in radians from a lookup table. */
static public float CosDeg (float degrees) {
return sin[(int)((degrees + 90) * degToIndex) & SIN_MASK];
}
/// <summary>Returns atan2 in radians, faster but less accurate than Math.Atan2. Average error of 0.00231 radians (0.1323
/// degrees), largest error of 0.00488 radians (0.2796 degrees).</summary>
static public float Atan2 (float y, float x) {
if (x == 0f) {
if (y > 0f) return PI / 2;
if (y == 0f) return 0f;
return -PI / 2;
}
float atan, z = y / x;
if (Math.Abs(z) < 1f) {
atan = z / (1f + 0.28f * z * z);
if (x < 0f) return atan + (y < 0f ? -PI : PI);
return atan;
}
atan = PI / 2 - z / (z * z + 0.28f);
return y < 0f ? atan - PI : atan;
}
}
}

View File

@ -39,7 +39,8 @@ namespace Spine {
internal ExposedList<Slot> slots;
internal ExposedList<Slot> drawOrder;
internal ExposedList<IkConstraint> ikConstraints;
private ExposedList<ExposedList<Bone>> boneCache = new ExposedList<ExposedList<Bone>>();
internal ExposedList<TransformConstraint> transformConstraints;
private ExposedList<IUpdatable> updateCache = new ExposedList<IUpdatable>();
internal Skin skin;
internal float r = 1, g = 1, b = 1, a = 1;
internal float time;
@ -89,81 +90,62 @@ namespace Spine {
drawOrder.Add(slot);
}
ikConstraints = new ExposedList<IkConstraint>(data.ikConstraints.Count);
foreach (IkConstraintData ikConstraintData in data.ikConstraints)
ikConstraints.Add(new IkConstraint(ikConstraintData, this));
ikConstraints = new ExposedList<IkConstraint>(data.ikConstraints.Count);
foreach (IkConstraintData ikConstraintData in data.ikConstraints)
ikConstraints.Add(new IkConstraint(ikConstraintData, this));
transformConstraints = new ExposedList<TransformConstraint>(data.transformConstraints.Count);
foreach (TransformConstraintData transformConstraintData in data.transformConstraints)
transformConstraints.Add(new TransformConstraint(transformConstraintData, this));
UpdateCache();
UpdateWorldTransform();
}
/// <summary>Caches information about bones and IK constraints. Must be called if bones or IK constraints are added or
/// removed.</summary>
/// <summary>Caches information about bones and constraints. Must be called if bones or constraints are added
/// or removed.</summary>
public void UpdateCache () {
ExposedList<ExposedList<Bone>> boneCache = this.boneCache;
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 arrayCount = ikConstraintsCount + 1;
if (boneCache.Count > arrayCount) boneCache.RemoveRange(arrayCount, boneCache.Count - arrayCount);
for (int i = 0, n = boneCache.Count; i < n; i++)
boneCache.Items[i].Clear();
while (boneCache.Count < arrayCount)
boneCache.Add(new ExposedList<Bone>());
ExposedList<Bone> nonIkBones = boneCache.Items[0];
int transformConstraintsCount = transformConstraints.Count;
updateCache.Clear();
for (int i = 0, n = bones.Count; i < n; i++) {
Bone bone = bones.Items[i];
Bone current = bone;
do {
for (int ii = 0; ii < ikConstraintsCount; ii++) {
IkConstraint ikConstraint = ikConstraints.Items[ii];
Bone parent = ikConstraint.bones.Items[0];
Bone child = ikConstraint.bones.Items[ikConstraint.bones.Count - 1];
while (true) {
if (current == child) {
boneCache.Items[ii].Add(bone);
boneCache.Items[ii + 1].Add(bone);
goto outer;
}
if (child == parent) break;
child = child.parent;
}
updateCache.Add(bone);
for (int ii = 0; ii < transformConstraintsCount; ii++) {
TransformConstraint transformConstraint = transformConstraints.Items[ii];
if (bone == transformConstraint.bone) {
updateCache.Add(transformConstraint);
break;
}
current = current.parent;
} while (current != null);
nonIkBones.Add(bone);
outer: { }
}
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;
}
}
}
}
/// <summary>Updates the world transform for each bone and applies IK constraints.</summary>
public void UpdateWorldTransform () {
ExposedList<Bone> bones = this.bones;
for (int ii = 0, nn = bones.Count; ii < nn; ii++) {
Bone bone = bones.Items[ii];
bone.rotationIK = bone.rotation;
}
ExposedList<ExposedList<Bone>> boneCache = this.boneCache;
ExposedList<IkConstraint> ikConstraints = this.ikConstraints;
int i = 0, last = boneCache.Count - 1;
while (true) {
ExposedList<Bone> updateBones = boneCache.Items[i];
for (int ii = 0, nn = updateBones.Count; ii < nn; ii++)
updateBones.Items[ii].UpdateWorldTransform();
if (i == last) break;
ikConstraints.Items[i].apply();
i++;
}
ExposedList<IUpdatable> updateCache = this.updateCache;
for (int i = 0, n = updateCache.Count; i < n; i++)
updateCache.Items[i].Update();
}
/// <summary>Sets the bones and slots to their setup pose values.</summary>
/// <summary>Sets the bones, constraints, and slots to their setup pose values.</summary>
public void SetToSetupPose () {
SetBonesToSetupPose();
SetSlotsToSetupPose();
}
/// <summary>Sets the bones and constraints to their setup pose values.</summary>
public void SetBonesToSetupPose () {
ExposedList<Bone> bones = this.bones;
for (int i = 0, n = bones.Count; i < n; i++)
@ -171,9 +153,17 @@ namespace Spine {
ExposedList<IkConstraint> ikConstraints = this.ikConstraints;
for (int i = 0, n = ikConstraints.Count; i < n; i++) {
IkConstraint ikConstraint = ikConstraints.Items[i];
ikConstraint.bendDirection = ikConstraint.data.bendDirection;
ikConstraint.mix = ikConstraint.data.mix;
IkConstraint constraint = ikConstraints.Items[i];
constraint.bendDirection = constraint.data.bendDirection;
constraint.mix = constraint.data.mix;
}
ExposedList<TransformConstraint> transformConstraints = this.transformConstraints;
for (int i = 0, n = transformConstraints.Count; i < n; i++) {
TransformConstraint constraint = transformConstraints.Items[i];
constraint.translateMix = constraint.data.translateMix;
constraint.x = constraint.data.x;
constraint.y = constraint.data.y;
}
}
@ -293,12 +283,23 @@ namespace Spine {
}
/** @return May be null. */
public IkConstraint FindIkConstraint (String ikConstraintName) {
if (ikConstraintName == null) throw new ArgumentNullException("ikConstraintName cannot be null.");
public IkConstraint FindIkConstraint (String constraintName) {
if (constraintName == null) throw new ArgumentNullException("constraintName cannot be null.");
ExposedList<IkConstraint> ikConstraints = this.ikConstraints;
for (int i = 0, n = ikConstraints.Count; i < n; i++) {
IkConstraint ikConstraint = ikConstraints.Items[i];
if (ikConstraint.data.name == ikConstraintName) return ikConstraint;
if (ikConstraint.data.name == constraintName) return ikConstraint;
}
return null;
}
/** @return May be null. */
public TransformConstraint FindTransformConstraint (String constraintName) {
if (constraintName == null) throw new ArgumentNullException("constraintName cannot be null.");
ExposedList<TransformConstraint> transformConstraints = this.transformConstraints;
for (int i = 0, n = transformConstraints.Count; i < n; i++) {
TransformConstraint transformConstraint = transformConstraints.Items[i];
if (transformConstraint.data.name == constraintName) return transformConstraint;
}
return null;
}

View File

@ -45,8 +45,6 @@ namespace Spine {
public const int TIMELINE_TRANSLATE = 2;
public const int TIMELINE_ATTACHMENT = 3;
public const int TIMELINE_COLOR = 4;
public const int TIMELINE_FLIPX = 5;
public const int TIMELINE_FLIPY = 6;
public const int CURVE_LINEAR = 0;
public const int CURVE_STEPPED = 1;
@ -130,8 +128,6 @@ namespace Spine {
boneData.scaleY = ReadFloat(input);
boneData.rotation = ReadFloat(input);
boneData.length = ReadFloat(input) * scale;
boneData.flipX = ReadBoolean(input);
boneData.flipY = ReadBoolean(input);
boneData.inheritScale = ReadBoolean(input);
boneData.inheritRotation = ReadBoolean(input);
if (nonessential) ReadInt(input); // Skip bone color.
@ -149,6 +145,17 @@ namespace Spine {
skeletonData.ikConstraints.Add(ikConstraintData);
}
// Transform constraints.
for (int i = 0, n = ReadInt(input, true); i < n; i++) {
TransformConstraintData transformConstraintData = new TransformConstraintData(ReadString(input));
transformConstraintData.bone = skeletonData.bones.Items[ReadInt(input, true)];
transformConstraintData.target = skeletonData.bones.Items[ReadInt(input, true)];
transformConstraintData.translateMix = ReadFloat(input);
transformConstraintData.x = ReadFloat(input);
transformConstraintData.y = ReadFloat(input);
skeletonData.transformConstraints.Add(transformConstraintData);
}
// Slots.
for (int i = 0, n = ReadInt(input, true); i < n; i++) {
String slotName = ReadString(input);
@ -269,10 +276,10 @@ namespace Spine {
}
return mesh;
}
case AttachmentType.skinnedmesh: {
case AttachmentType.weightedmesh: {
String path = ReadString(input);
if (path == null) path = name;
SkinnedMeshAttachment mesh = attachmentLoader.NewSkinnedMeshAttachment(skin, name, path);
WeightedMeshAttachment mesh = attachmentLoader.NewWeightedMeshAttachment(skin, name, path);
if (mesh == null) return null;
mesh.Path = path;
float[] uvs = ReadFloatArray(input, 1);
@ -422,17 +429,6 @@ namespace Spine {
duration = Math.Max(duration, timeline.frames[frameCount * 3 - 3]);
break;
}
case TIMELINE_FLIPX:
case TIMELINE_FLIPY: {
FlipXTimeline timeline = timelineType == TIMELINE_FLIPX ? new FlipXTimeline(frameCount) : new FlipYTimeline(
frameCount);
timeline.boneIndex = boneIndex;
for (int frameIndex = 0; frameIndex < frameCount; frameIndex++)
timeline.SetFrame(frameIndex, ReadFloat(input), ReadBoolean(input));
timelines.Add(timeline);
duration = Math.Max(duration, timeline.frames[frameCount * 2 - 2]);
break;
}
}
}
}
@ -470,7 +466,7 @@ namespace Spine {
if (attachment is MeshAttachment)
vertexCount = ((MeshAttachment)attachment).vertices.Length;
else
vertexCount = ((SkinnedMeshAttachment)attachment).weights.Length / 3 * 2;
vertexCount = ((WeightedMeshAttachment)attachment).weights.Length / 3 * 2;
int end = ReadInt(input, true);
if (end == 0) {
@ -544,7 +540,7 @@ namespace Spine {
for (int i = 0; i < eventCount; i++) {
float time = ReadFloat(input);
EventData eventData = skeletonData.events.Items[ReadInt(input, true)];
Event e = new Event(eventData);
Event e = new Event(time, eventData);
e.Int = ReadInt(input, false);
e.Float = ReadFloat(input);
e.String = ReadBoolean(input) ? ReadString(input) : eventData.String;

View File

@ -42,6 +42,7 @@ namespace Spine {
internal ExposedList<EventData> events = new ExposedList<EventData>();
internal ExposedList<Animation> animations = new ExposedList<Animation>();
internal ExposedList<IkConstraintData> ikConstraints = new ExposedList<IkConstraintData>();
internal ExposedList<TransformConstraintData> transformConstraints = new ExposedList<TransformConstraintData>();
internal float width, height;
internal String version, hash, imagesPath;
@ -140,12 +141,25 @@ namespace Spine {
// --- IK constraints.
/// <returns>May be null.</returns>
public IkConstraintData FindIkConstraint (String ikConstraintName) {
if (ikConstraintName == null) throw new ArgumentNullException("ikConstraintName cannot be null.");
public IkConstraintData FindIkConstraint (String constraintName) {
if (constraintName == null) throw new ArgumentNullException("constraintName cannot be null.");
ExposedList<IkConstraintData> ikConstraints = this.ikConstraints;
for (int i = 0, n = ikConstraints.Count; i < n; i++) {
IkConstraintData ikConstraint = ikConstraints.Items[i];
if (ikConstraint.name == ikConstraintName) return ikConstraint;
if (ikConstraint.name == constraintName) return ikConstraint;
}
return null;
}
// --- Transform constraints.
/// <returns>May be null.</returns>
public TransformConstraintData FindTransformConstraint (String constraintName) {
if (constraintName == null) throw new ArgumentNullException("constraintName cannot be null.");
ExposedList<TransformConstraintData> transformConstraints = this.transformConstraints;
for (int i = 0, n = transformConstraints.Count; i < n; i++) {
TransformConstraintData transformConstraint = transformConstraints.Items[i];
if (transformConstraint.name == constraintName) return transformConstraint;
}
return null;
}

View File

@ -118,8 +118,6 @@ namespace Spine {
boneData.rotation = GetFloat(boneMap, "rotation", 0);
boneData.scaleX = GetFloat(boneMap, "scaleX", 1);
boneData.scaleY = GetFloat(boneMap, "scaleY", 1);
boneData.flipX = GetBoolean(boneMap, "flipX", false);
boneData.flipY = GetBoolean(boneMap, "flipY", false);
boneData.inheritScale = GetBoolean(boneMap, "inheritScale", true);
boneData.inheritRotation = GetBoolean(boneMap, "inheritRotation", true);
skeletonData.bones.Add(boneData);
@ -147,6 +145,27 @@ namespace Spine {
}
}
// Transform constraints.
if (root.ContainsKey("transform")) {
foreach (Dictionary<String, Object> transformMap in (List<Object>)root["ik"]) {
TransformConstraintData transformConstraintData = new TransformConstraintData((String)transformMap["name"]);
String boneName = (String)transformMap["bone"];
transformConstraintData.target = skeletonData.FindBone(boneName);
if (transformConstraintData.target == 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.translateMix = GetFloat(transformMap, "mix", 1);
transformConstraintData.x = GetFloat(transformMap, "x", 0);
transformConstraintData.y = GetFloat(transformMap, "y", 0);
skeletonData.transformConstraints.Add(transformConstraintData);
}
}
// Slots.
if (root.ContainsKey("slots")) {
foreach (Dictionary<String, Object> slotMap in (List<Object>)root["slots"]) {
@ -226,8 +245,11 @@ namespace Spine {
name = (String)map["name"];
var type = AttachmentType.region;
if (map.ContainsKey("type"))
type = (AttachmentType)Enum.Parse(typeof(AttachmentType), (String)map["type"], false);
if (map.ContainsKey("type")) {
var typeName = (String)map["type"];
if (typeName == "skinnedmesh") typeName = "weightedmesh";
type = (AttachmentType)Enum.Parse(typeof(AttachmentType), typeName , false);
}
String path = name;
if (map.ContainsKey("path"))
@ -281,8 +303,8 @@ namespace Spine {
return mesh;
}
case AttachmentType.skinnedmesh: {
SkinnedMeshAttachment mesh = attachmentLoader.NewSkinnedMeshAttachment(skin, name, path);
case AttachmentType.weightedmesh: {
WeightedMeshAttachment mesh = attachmentLoader.NewWeightedMeshAttachment(skin, name, path);
if (mesh == null) return null;
mesh.Path = path;
@ -478,21 +500,6 @@ namespace Spine {
timelines.Add(timeline);
duration = Math.Max(duration, timeline.frames[timeline.FrameCount * 3 - 3]);
} else if (timelineName == "flipX" || timelineName == "flipY") {
bool x = timelineName == "flipX";
var timeline = x ? new FlipXTimeline(values.Count) : new FlipYTimeline(values.Count);
timeline.boneIndex = boneIndex;
String field = x ? "x" : "y";
int frameIndex = 0;
foreach (Dictionary<String, Object> valueMap in values) {
float time = (float)valueMap["time"];
timeline.SetFrame(frameIndex, time, valueMap.ContainsKey(field) ? (bool)valueMap[field] : false);
frameIndex++;
}
timelines.Add(timeline);
duration = Math.Max(duration, timeline.frames[timeline.FrameCount * 2 - 2]);
} else
throw new Exception("Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")");
}
@ -536,7 +543,7 @@ namespace Spine {
if (attachment is MeshAttachment)
vertexCount = ((MeshAttachment)attachment).vertices.Length;
else
vertexCount = ((SkinnedMeshAttachment)attachment).Weights.Length / 3 * 2;
vertexCount = ((WeightedMeshAttachment)attachment).Weights.Length / 3 * 2;
int frameIndex = 0;
foreach (Dictionary<String, Object> valueMap in values) {
@ -619,11 +626,12 @@ namespace Spine {
foreach (Dictionary<String, Object> eventMap in eventsMap) {
EventData eventData = skeletonData.FindEvent((String)eventMap["name"]);
if (eventData == null) throw new Exception("Event not found: " + eventMap["name"]);
var e = new Event(eventData);
float time = (float)eventMap["time"];
var e = new Event(time, eventData);
e.Int = GetInt(eventMap, "int", eventData.Int);
e.Float = GetFloat(eventMap, "float", eventData.Float);
e.String = GetString(eventMap, "string", eventData.String);
timeline.SetFrame(frameIndex++, (float)eventMap["time"], e);
timeline.SetFrame(frameIndex++, time, e);
}
timelines.Add(timeline);
duration = Math.Max(duration, timeline.frames[timeline.FrameCount - 1]);

View File

@ -0,0 +1,82 @@
/******************************************************************************
* 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 {
public class TransformConstraint : IUpdatable {
internal TransformConstraintData data;
internal Bone bone, target;
internal float translateMix;
internal float x, y;
public TransformConstraintData Data { get { return data; } }
public Bone Bone { get { return bone; } set { bone = value; } }
public Bone Target { get { return target; } set { target = value; } }
public float TranslateMix { get { return translateMix; } set { translateMix = value; } }
public float X { get { return x; } set { x = value; } }
public float Y { get { return y; } set { y = 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.");
this.data = data;
translateMix = data.translateMix;
x = data.x;
y = data.y;
if (skeleton != null) {
bone = skeleton.FindBone(data.bone.name);
target = skeleton.FindBone(data.target.name);
}
}
public void Update () {
Apply();
}
public void Apply () {
float translateMix = this.translateMix;
if (translateMix > 0) {
Bone bone = this.bone;
float tx, ty;
target.LocalToWorld(x, y, out tx, out ty);
bone.worldX += (tx - bone.worldX) * translateMix;
bone.worldY += (ty - bone.worldY) * translateMix;
}
}
override public String ToString () {
return data.name;
}
}
}

View File

@ -0,0 +1,58 @@
/******************************************************************************
* 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 {
public class TransformConstraintData {
internal String name;
internal BoneData bone, target;
internal float translateMix;
internal float x, y;
public String Name { get { return name; } }
public BoneData Bone { get { return bone; } set { bone = value; } }
public BoneData Target { get { return target; } set { target = value; } }
public float TranslateMix { get { return translateMix; } set { translateMix = value; } }
public float X { get { return x; } set { x = value; } }
public float Y { get { return y; } set { y = value; } }
public TransformConstraintData (String name) {
if (name == null) throw new ArgumentNullException("name cannot be null.");
this.name = name;
}
override public String ToString () {
return name;
}
}
}

File diff suppressed because one or more lines are too long

Binary file not shown.

View File

@ -125,9 +125,9 @@
<Content Include="GameThumbnail.png" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\spine-csharp\spine-csharp_xna.csproj">
<Project>{DF7AAFE1-401E-4278-8F9A-3DD2B8CC3D0A}</Project>
<Name>spine-csharp_xna</Name>
<ProjectReference Include="..\..\spine-csharp\spine-csharp.csproj">
<Project>{94144e22-2431-4a8f-ac04-dec22f7edd8f}</Project>
<Name>spine-csharp</Name>
</ProjectReference>
<ProjectReference Include="..\spine-xna.csproj">
<Project>{7F8F2327-C016-49C8-BB4D-F3F77971961E}</Project>

View File

@ -138,13 +138,13 @@
</BootstrapperPackage>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\spine-csharp\spine-csharp_xna.csproj">
<Project>{DF7AAFE1-401E-4278-8F9A-3DD2B8CC3D0A}</Project>
<Name>spine-csharp_xna</Name>
</ProjectReference>
<Compile Include="src\SkeletonMeshRenderer.cs" />
</ItemGroup>
<ItemGroup>
<Compile Include="src\SkeletonMeshRenderer.cs" />
<ProjectReference Include="..\spine-csharp\spine-csharp.csproj">
<Project>{94144e22-2431-4a8f-ac04-dec22f7edd8f}</Project>
<Name>spine-csharp</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<Import Project="$(MSBuildExtensionsPath)\Microsoft\XNA Game Studio\Microsoft.Xna.GameStudio.targets" />

View File

@ -1,30 +1,55 @@

Microsoft Visual Studio Solution File, Format Version 11.00
# Visual C# Express 2010
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Express 2012 for Windows Desktop
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "spine-xna", "spine-xna.csproj", "{7F8F2327-C016-49C8-BB4D-F3F77971961E}"
ProjectSection(ProjectDependencies) = postProject
{94144E22-2431-4A8F-AC04-DEC22F7EDD8F} = {94144E22-2431-4A8F-AC04-DEC22F7EDD8F}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "spine-xna-example", "example\spine-xna-example.csproj", "{29CC4385-294A-4885-A3E8-FD4825E0CFDD}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "spine-csharp_xna", "..\spine-csharp\spine-csharp_xna.csproj", "{DF7AAFE1-401E-4278-8F9A-3DD2B8CC3D0A}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "spine-csharp", "..\spine-csharp\spine-csharp.csproj", "{94144E22-2431-4A8F-AC04-DEC22F7EDD8F}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|Mixed Platforms = Debug|Mixed Platforms
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|Mixed Platforms = Release|Mixed Platforms
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{7F8F2327-C016-49C8-BB4D-F3F77971961E}.Debug|Any CPU.ActiveCfg = Debug|x86
{7F8F2327-C016-49C8-BB4D-F3F77971961E}.Debug|Mixed Platforms.ActiveCfg = Debug|x86
{7F8F2327-C016-49C8-BB4D-F3F77971961E}.Debug|Mixed Platforms.Build.0 = Debug|x86
{7F8F2327-C016-49C8-BB4D-F3F77971961E}.Debug|x86.ActiveCfg = Debug|x86
{7F8F2327-C016-49C8-BB4D-F3F77971961E}.Debug|x86.Build.0 = Debug|x86
{7F8F2327-C016-49C8-BB4D-F3F77971961E}.Release|Any CPU.ActiveCfg = Release|x86
{7F8F2327-C016-49C8-BB4D-F3F77971961E}.Release|Mixed Platforms.ActiveCfg = Release|x86
{7F8F2327-C016-49C8-BB4D-F3F77971961E}.Release|Mixed Platforms.Build.0 = Release|x86
{7F8F2327-C016-49C8-BB4D-F3F77971961E}.Release|x86.ActiveCfg = Release|x86
{7F8F2327-C016-49C8-BB4D-F3F77971961E}.Release|x86.Build.0 = Release|x86
{29CC4385-294A-4885-A3E8-FD4825E0CFDD}.Debug|Any CPU.ActiveCfg = Debug|x86
{29CC4385-294A-4885-A3E8-FD4825E0CFDD}.Debug|Mixed Platforms.ActiveCfg = Debug|x86
{29CC4385-294A-4885-A3E8-FD4825E0CFDD}.Debug|Mixed Platforms.Build.0 = Debug|x86
{29CC4385-294A-4885-A3E8-FD4825E0CFDD}.Debug|x86.ActiveCfg = Debug|x86
{29CC4385-294A-4885-A3E8-FD4825E0CFDD}.Debug|x86.Build.0 = Debug|x86
{29CC4385-294A-4885-A3E8-FD4825E0CFDD}.Release|Any CPU.ActiveCfg = Release|x86
{29CC4385-294A-4885-A3E8-FD4825E0CFDD}.Release|Mixed Platforms.ActiveCfg = Release|x86
{29CC4385-294A-4885-A3E8-FD4825E0CFDD}.Release|Mixed Platforms.Build.0 = Release|x86
{29CC4385-294A-4885-A3E8-FD4825E0CFDD}.Release|x86.ActiveCfg = Release|x86
{29CC4385-294A-4885-A3E8-FD4825E0CFDD}.Release|x86.Build.0 = Release|x86
{DF7AAFE1-401E-4278-8F9A-3DD2B8CC3D0A}.Debug|x86.ActiveCfg = Debug|x86
{DF7AAFE1-401E-4278-8F9A-3DD2B8CC3D0A}.Debug|x86.Build.0 = Debug|x86
{DF7AAFE1-401E-4278-8F9A-3DD2B8CC3D0A}.Release|x86.ActiveCfg = Release|x86
{DF7AAFE1-401E-4278-8F9A-3DD2B8CC3D0A}.Release|x86.Build.0 = Release|x86
{94144E22-2431-4A8F-AC04-DEC22F7EDD8F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{94144E22-2431-4A8F-AC04-DEC22F7EDD8F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{94144E22-2431-4A8F-AC04-DEC22F7EDD8F}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{94144E22-2431-4A8F-AC04-DEC22F7EDD8F}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{94144E22-2431-4A8F-AC04-DEC22F7EDD8F}.Debug|x86.ActiveCfg = Debug|Any CPU
{94144E22-2431-4A8F-AC04-DEC22F7EDD8F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{94144E22-2431-4A8F-AC04-DEC22F7EDD8F}.Release|Any CPU.Build.0 = Release|Any CPU
{94144E22-2431-4A8F-AC04-DEC22F7EDD8F}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{94144E22-2431-4A8F-AC04-DEC22F7EDD8F}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{94144E22-2431-4A8F-AC04-DEC22F7EDD8F}.Release|x86.ActiveCfg = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@ -189,8 +189,8 @@ namespace Spine {
itemVertices[ii].TextureCoordinate.X = uvs[v];
itemVertices[ii].TextureCoordinate.Y = uvs[v + 1];
}
} else if (attachment is SkinnedMeshAttachment) {
SkinnedMeshAttachment mesh = (SkinnedMeshAttachment)attachment;
} else if (attachment is WeightedMeshAttachment) {
WeightedMeshAttachment mesh = (WeightedMeshAttachment)attachment;
int vertexCount = mesh.UVs.Length;
if (vertices.Length < vertexCount) vertices = new float[vertexCount];
mesh.ComputeWorldVertices(slot, vertices);