[csharp] Ported 3.6-beta changes.

This commit is contained in:
pharan 2017-01-28 23:20:23 +08:00
parent 7ac7afac2c
commit 3736a5c3dc
17 changed files with 610 additions and 85 deletions

View File

@ -126,7 +126,8 @@ namespace Spine {
Attachment, Color, Deform, //
Event, DrawOrder, //
IkConstraint, TransformConstraint, //
PathConstraintPosition, PathConstraintSpacing, PathConstraintMix
PathConstraintPosition, PathConstraintSpacing, PathConstraintMix, //
TwoColor
}
/// <summary>Base class for frames that use an interpolation bezier curve.</summary>
@ -546,6 +547,136 @@ namespace Spine {
}
}
public class TwoColorTimeline : CurveTimeline {
public const int ENTRIES = 8;
protected const int PREV_TIME = -8, PREV_R = -7, PREV_G = -6, PREV_B = -5, PREV_A = -4;
protected const int PREV_R2 = -3, PREV_G2 = -2, PREV_B2 = -1;
protected const int R = 1, G = 2, B = 3, A = 4, R2 = 5, G2 = 6, B2 = 7;
internal float[] frames; // time, r, g, b, a, r2, g2, b2, ...
public float[] Frames { get { return frames; } }
internal int slotIndex;
public int SlotIndex {
get { return slotIndex; }
set {
if (value < 0) throw new ArgumentOutOfRangeException("index must be >= 0.");
slotIndex = value;
}
}
public TwoColorTimeline (int frameCount) :
base(frameCount) {
frames = new float[frameCount * ENTRIES];
}
override public int PropertyId {
get { return ((int)TimelineType.TwoColor << 24) + slotIndex; }
}
/// <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, float r2, float g2, float b2) {
frameIndex *= ENTRIES;
frames[frameIndex] = time;
frames[frameIndex + R] = r;
frames[frameIndex + G] = g;
frames[frameIndex + B] = b;
frames[frameIndex + A] = a;
frames[frameIndex + R2] = r2;
frames[frameIndex + G2] = g2;
frames[frameIndex + B2] = b2;
}
override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, bool setupPose, bool mixingOut) {
Slot slot = skeleton.slots.Items[slotIndex];
float[] frames = this.frames;
if (time < frames[0]) { // Time is before first frame.
if (setupPose) {
// slot.color.set(slot.data.color);
// slot.darkColor.set(slot.data.darkColor);
var slotData = slot.data;
slot.r = slotData.r;
slot.g = slotData.g;
slot.b = slotData.b;
slot.a = slotData.a;
slot.r2 = slotData.r2;
slot.g2 = slotData.g2;
slot.b2 = slotData.b2;
}
return;
}
float r, g, b, a, r2, g2, b2;
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];
r2 = frames[i + PREV_R2];
g2 = frames[i + PREV_G2];
b2 = frames[i + PREV_B2];
} else {
// Interpolate between the previous frame and the current frame.
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];
r2 = frames[frame + PREV_R2];
g2 = frames[frame + PREV_G2];
b2 = frames[frame + PREV_B2];
float frameTime = frames[frame];
float percent = GetCurvePercent(frame / ENTRIES - 1,
1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime));
r += (frames[frame + R] - r) * percent;
g += (frames[frame + G] - g) * percent;
b += (frames[frame + B] - b) * percent;
a += (frames[frame + A] - a) * percent;
r2 += (frames[frame + R2] - r2) * percent;
g2 += (frames[frame + G2] - g2) * percent;
b2 += (frames[frame + B2] - b2) * percent;
}
if (alpha == 1) {
slot.r = r;
slot.g = g;
slot.b = b;
slot.a = a;
slot.r2 = r2;
slot.g2 = g2;
slot.b2 = b2;
} else {
float br, bg, bb, ba, br2, bg2, bb2;
if (setupPose) {
br = slot.data.r;
bg = slot.data.g;
bb = slot.data.b;
ba = slot.data.a;
br2 = slot.data.r2;
bg2 = slot.data.g2;
bb2 = slot.data.b2;
} else {
br = slot.r;
bg = slot.g;
bb = slot.b;
ba = slot.a;
br2 = slot.r2;
bg2 = slot.g2;
bb2 = slot.b2;
}
slot.r = br + ((r - br) * alpha);
slot.g = bg + ((g - bg) * alpha);
slot.b = bb + ((b - bb) * alpha);
slot.a = ba + ((a - ba) * alpha);
slot.r2 = br2 + ((r2 - br2) * alpha);
slot.g2 = bg2 + ((g2 - bg2) * alpha);
slot.b2 = bb2 + ((b2 - bb2) * alpha);
}
}
}
public class AttachmentTimeline : Timeline {
internal int slotIndex;
internal float[] frames;

View File

@ -805,6 +805,11 @@ namespace Spine {
/// <see cref="AnimationStateData"/> based on the animation before this animation (if any).
///
/// The mix duration must be set before <see cref="AnimationState.Update(float)"/> is next called.
/// <para>
/// When using <seealso cref="AnimationState.AddAnimation(int, Animation, bool, float)"/> with a
/// <code>delay</code> <seealso cref="Delay"/> is set using the mix duration from the <see cref=" AnimationStateData"/>
/// </para>
///
/// </summary>
public float MixDuration { get { return mixDuration; } set { mixDuration = value; } }

View File

@ -39,7 +39,7 @@ namespace Spine {
this.atlasArray = atlasArray;
}
public RegionAttachment NewRegionAttachment (Skin skin, String name, String path) {
public RegionAttachment NewRegionAttachment (Skin skin, string name, string path) {
AtlasRegion region = FindRegion(path);
if (region == null) throw new Exception("Region not found in atlas: " + path + " (region attachment: " + name + ")");
RegionAttachment attachment = new RegionAttachment(name);
@ -54,7 +54,7 @@ namespace Spine {
return attachment;
}
public MeshAttachment NewMeshAttachment (Skin skin, String name, String path) {
public MeshAttachment NewMeshAttachment (Skin skin, string name, string path) {
AtlasRegion region = FindRegion(path);
if (region == null) throw new Exception("Region not found in atlas: " + path + " (mesh attachment: " + name + ")");
MeshAttachment attachment = new MeshAttachment(name);
@ -73,12 +73,16 @@ namespace Spine {
return attachment;
}
public BoundingBoxAttachment NewBoundingBoxAttachment (Skin skin, String name) {
public BoundingBoxAttachment NewBoundingBoxAttachment (Skin skin, string name) {
return new BoundingBoxAttachment(name);
}
public PathAttachment NewPathAttachment (Skin skin, String name) {
return new PathAttachment (name);
public PathAttachment NewPathAttachment (Skin skin, string name) {
return new PathAttachment(name);
}
public PointAttachment NewPointAttachment (Skin skin, string name) {
return new PointAttachment(name);
}
public AtlasRegion FindRegion (string name) {

View File

@ -33,15 +33,17 @@ using System;
namespace Spine {
public interface AttachmentLoader {
/// <return>May be null to not load any attachment.</return>
RegionAttachment NewRegionAttachment (Skin skin, String name, String path);
RegionAttachment NewRegionAttachment (Skin skin, String name, string path);
/// <return>May be null to not load any attachment.</return>
MeshAttachment NewMeshAttachment (Skin skin, String name, String path);
MeshAttachment NewMeshAttachment (Skin skin, String name, string path);
/// <return>May be null to not load any attachment.</return>
BoundingBoxAttachment NewBoundingBoxAttachment (Skin skin, String name);
BoundingBoxAttachment NewBoundingBoxAttachment (Skin skin, string name);
/// <returns>May be null to not load any attachment</returns>
PathAttachment NewPathAttachment (Skin skin, String name);
PathAttachment NewPathAttachment (Skin skin, string name);
PointAttachment NewPointAttachment (Skin skin, string name);
}
}

View File

@ -30,6 +30,6 @@
namespace Spine {
public enum AttachmentType {
Region, Boundingbox, Mesh, Linkedmesh, Path
Region, Boundingbox, Mesh, Linkedmesh, Path, Point
}
}

View File

@ -43,6 +43,7 @@ namespace Spine {
public int HullLength { get { return hulllength; } set { hulllength = value; } }
public float[] RegionUVs { get { return regionUVs; } set { regionUVs = value; } }
/// <summary>The UV pair for each vertex, normalized within the entire texture. <seealso cref="MeshAttachment.UpdateUVs"/></summary>
public float[] UVs { get { return uvs; } set { uvs = value; } }
public int[] Triangles { get { return triangles; } set { triangles = value; } }

View File

@ -0,0 +1,61 @@
/******************************************************************************
* Spine Runtimes Software License v2.5
*
* Copyright (c) 2013-2016, 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 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 develop new applications using the Spine Runtimes or otherwise
* create derivative works or improvements of the Spine Runtimes 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, BUSINESS INTERRUPTION, OR LOSS OF
* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
namespace Spine {
/// <summary>
/// An attachment which is a single point and a rotation. This can be used to spawn projectiles, particles, etc. A bone can be
/// used in similar ways, but a PointAttachment is slightly less expensive to compute and can be hidden, shown, and placed in a
/// skin.
/// <p>
/// See <a href="http://esotericsoftware.com/spine-point-attachments">Point Attachments</a> in the Spine User Guide.
/// </summary>
public class PointAttachment : Attachment {
internal float x, y, rotation;
public float X { get { return x; } set { x = value; } }
public float Y { get { return y; } set { y = value; } }
public float Rotation { get { return rotation; } set { rotation = value; } }
public PointAttachment (string name)
: base(name) {
}
public void ComputeWorldPosition (Bone bone, float x, float y, out float ox, out float oy) {
bone.LocalToWorld(x, y, out ox, out oy);
}
public float ComputeWorldRotation (Bone bone) {
float cos = MathUtils.CosDeg(rotation), sin = MathUtils.SinDeg(rotation);
float ix = cos * bone.a + sin * bone.b;
float iy = cos * bone.c + sin * bone.d;
return MathUtils.Atan2(iy, ix) * MathUtils.RadDeg;
}
}
}

View File

@ -55,7 +55,8 @@ namespace Spine {
/// <param name="count">The number of world vertex values to output. Must be less than or equal to <see cref="WorldVerticesLength"/> - start.</param>
/// <param name="worldVertices">The output world vertices. Must have a length greater than or equal to <paramref name="offset"/> + <paramref name="count"/>.</param>
/// <param name="offset">The <paramref name="worldVertices"/> index to begin writing values.</param>
public void ComputeWorldVertices (Slot slot, int start, int count, float[] worldVertices, int offset) {
/// <param name="stride">The number of <paramref name="worldVertices"/> entries between the value pairs written.</param>
public void ComputeWorldVertices (Slot slot, int start, int count, float[] worldVertices, int offset, int stride = 2) {
count += offset;
Skeleton skeleton = slot.Skeleton;
var deformArray = slot.attachmentVertices;
@ -66,7 +67,7 @@ namespace Spine {
Bone bone = slot.bone;
float 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) {
for (int vv = start, w = offset; w < count; vv += 2, w += stride) {
float vx = vertices[vv], vy = vertices[vv + 1];
worldVertices[w] = vx * a + vy * b + x;
worldVertices[w + 1] = vx * c + vy * d + y;
@ -81,7 +82,7 @@ namespace Spine {
}
Bone[] skeletonBones = skeleton.Bones.Items;
if (deformArray.Count == 0) {
for (int w = offset, b = skip * 3; w < count; w += 2) {
for (int w = offset, b = skip * 3; w < count; w += stride) {
float wx = 0, wy = 0;
int n = bones[v++];
n += v;
@ -96,7 +97,7 @@ namespace Spine {
}
} else {
float[] deform = deformArray.Items;
for (int w = offset, b = skip * 3, f = skip << 1; w < count; w += 2) {
for (int w = offset, b = skip * 3, f = skip << 1; w < count; w += stride) {
float wx = 0, wy = 0;
int n = bones[v++];
n += v;

View File

@ -31,6 +31,14 @@
using System;
namespace Spine {
/// <summary>
/// Stores a bone's current pose.
/// <para>
/// A bone has a local transform which is used to compute its world transform. A bone also has an applied transform, which is a
/// local transform that can be applied to compute the world transform. The local transform and applied transform may differ if a
/// constraint or application code modifies the world transform after it was computed from the local transform.
/// </para>
/// </summary>
public class Bone : IUpdatable {
static public bool yDown;
@ -55,6 +63,7 @@ namespace Spine {
public Skeleton Skeleton { get { return skeleton; } }
public Bone Parent { get { return parent; } }
public ExposedList<Bone> Children { get { return children; } }
/// <summary>The local X translation.</summary>
public float X { get { return x; } set { x = value; } }
public float Y { get { return y; } set { y = value; } }
public float Rotation { get { return rotation; } set { rotation = value; } }
@ -240,34 +249,6 @@ namespace Spine {
shearY = data.shearY;
}
public float WorldToLocalRotationX {
get {
Bone parent = this.parent;
if (parent == null) return arotation;
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 arotation;
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;
appliedValid = false;
}
/// <summary>
/// Computes the individual applied transform values from the world transform. This can be useful to perform processing using
/// the applied transform after the world transform has been modified directly (eg, by a constraint)..
@ -328,6 +309,44 @@ namespace Spine {
worldY = localX * c + localY * d + this.worldY;
}
public float WorldToLocalRotationX {
get {
Bone parent = this.parent;
if (parent == null) return arotation;
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 arotation;
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 float WorldToLocalRotation (float worldRotation) {
float sin = MathUtils.SinDeg(worldRotation), cos = MathUtils.CosDeg(worldRotation);
return MathUtils.Atan2(a * sin - c * cos, d * cos - b * sin) * MathUtils.RadDeg;
}
public float LocalToWorldRotation (float localRotation) {
float sin = MathUtils.SinDeg(localRotation), cos = MathUtils.CosDeg(localRotation);
return MathUtils.Atan2(cos * c + sin * d, cos * a + sin * b) * 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;
appliedValid = false;
}
override public String ToString () {
return data.name;
}

View File

@ -91,10 +91,10 @@ namespace Spine {
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;
float setupLength = bone.data.length, x = setupLength * bone.a, y = setupLength * bone.c;
float length = (float)Math.Sqrt(x * x + y * y);
if (scale) lengths.Items[i] = setupLength;
spaces.Items[++i] = (lengthSpacing ? Math.Max(0, setupLength + spacing) : spacing) * length / setupLength;
}
} else {
for (int i = 1; i < spacesCount; i++)

View File

@ -194,15 +194,15 @@ namespace Spine {
var constrained = constraint.bones;
int boneCount = constrained.Count;
for (int ii = 0; ii < boneCount; ii++)
SortBone(constrained.Items[ii]);
for (int i = 0; i < boneCount; i++)
SortBone(constrained.Items[i]);
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; i < boneCount; i++)
SortReset(constrained.Items[i].children);
for (int i = 0; i < boneCount; i++)
constrained.Items[i].sorted = true;
}
private void SortTransformConstraint (TransformConstraint constraint) {
@ -210,15 +210,25 @@ namespace Spine {
var constrained = constraint.bones;
int boneCount = constrained.Count;
for (int ii = 0; ii < boneCount; ii++)
SortBone(constrained.Items[ii]);
// for (int ii = 0; ii < boneCount; ii++)
// SortBone(constrained.Items[ii]);
if (constraint.data.local) {
for (int i = 0; i < boneCount; i++) {
Bone child = constrained.Items[constrained.Count - 1];
SortBone(child.parent);
if (!updateCache.Contains(child)) updateCacheReset.Add(child);
}
} else {
for (int i = 0; i < boneCount; i++)
SortBone(constrained.Items[i]);
}
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; i < boneCount; i++)
SortReset(constrained.Items[i].children);
for (int i = 0; i < boneCount; i++)
constrained.Items[i].sorted = true;
}
private void SortPathConstraintAttachment (Skin skin, int slotIndex, Bone slotBone) {
@ -475,5 +485,53 @@ namespace Spine {
public void Update (float delta) {
time += delta;
}
/// <summary>Returns the axis aligned bounding box (AABB) of the region and mesh attachments for the current pose.</summary>
/// <param name="x">The horizontal distance between the skeleton origin and the left side of the AABB.</param>
/// <param name="y">The vertical distance between the skeleton origin and the bottom side of the AABB.</param>
/// <param name="width">The width of the AABB</param>
/// <param name="height">The height of the AABB.</param>
/// <param name="vertexBuffer">Reference to hold a float[]. May be a null reference. This method will assign it a new float[] with the appropriate size as needed.</param>
public void GetBounds (out float x, out float y, out float width, out float height, ref float[] vertexBuffer) {
float[] temp = vertexBuffer;
temp = temp ?? new float[8];
var drawOrder = this.drawOrder;
float minX = int.MaxValue, minY = int.MaxValue, maxX = int.MinValue, maxY = int.MinValue;
for (int i = 0, n = drawOrder.Count; i < n; i++) {
Slot slot = drawOrder.Items[i];
int verticesLength = 0;
float[] vertices = null;
Attachment attachment = slot.attachment;
var regionAttachment = attachment as RegionAttachment;
if (regionAttachment != null) {
verticesLength = 8;
if (temp.Length < 8) temp = new float[8];
regionAttachment.ComputeWorldVertices(slot.bone, temp);
} else {
var meshAttachment = attachment as MeshAttachment;
if (meshAttachment != null) {
MeshAttachment mesh = meshAttachment;
verticesLength = mesh.WorldVerticesLength;
if (temp.Length < verticesLength) temp = new float[verticesLength];
mesh.ComputeWorldVertices(slot, 0, verticesLength, temp, 0);
}
}
if (vertices != null) {
for (int ii = 0; ii < verticesLength; ii += 2) {
float vx = vertices[ii], vy = vertices[ii + 1];
minX = Math.Min(minX, vx);
minY = Math.Min(minY, vy);
maxX = Math.Max(maxX, vx);
maxY = Math.Max(maxY, vy);
}
}
}
x = minX;
y = minY;
width = maxX - minX;
height = maxY - minY;
vertexBuffer = temp;
}
}
}

View File

@ -50,6 +50,7 @@ namespace Spine {
public const int SLOT_ATTACHMENT = 0;
public const int SLOT_COLOR = 1;
public const int SLOT_TWO_COLOR = 2;
public const int PATH_POSITION = 0;
public const int PATH_SPACING = 1;
@ -182,6 +183,15 @@ namespace Spine {
slotData.g = ((color & 0x00ff0000) >> 16) / 255f;
slotData.b = ((color & 0x0000ff00) >> 8) / 255f;
slotData.a = ((color & 0x000000ff)) / 255f;
int darkColor = ReadInt(input);
if (darkColor != -1) {
slotData.hasSecondColor = true;
slotData.r2 = ((darkColor & 0xff000000) >> 24) / 255f;
slotData.g2 = ((darkColor & 0x00ff0000) >> 16) / 255f;
slotData.b2 = ((darkColor & 0x0000ff00) >> 8) / 255f;
}
slotData.attachmentName = ReadString(input);
slotData.blendMode = (BlendMode)ReadVarint(input, true);
skeletonData.slots.Add(slotData);
@ -425,7 +435,7 @@ namespace Spine {
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.
if (nonessential) ReadInt(input); //int color = nonessential ? ReadInt(input) : 0;
PathAttachment path = attachmentLoader.NewPathAttachment(skin, name);
if (path == null) return null;
@ -436,7 +446,21 @@ namespace Spine {
path.bones = vertices.bones;
path.lengths = lengths;
return path;
}
}
case AttachmentType.Point: {
float rotation = ReadFloat(input);
float x = ReadFloat(input);
float y = ReadFloat(input);
if (nonessential) ReadInt(input); //int color = nonessential ? ReadInt(input) : 0;
PointAttachment point = attachmentLoader.NewPointAttachment(skin, name);
if (point == null) return null;
point.x = x * scale;
point.y = y * scale;
point.rotation = rotation;
//if (nonessential) point.color = color;
return point;
}
}
return null;
}
@ -499,6 +523,15 @@ namespace Spine {
int timelineType = input.ReadByte();
int frameCount = ReadVarint(input, true);
switch (timelineType) {
case SLOT_ATTACHMENT: {
AttachmentTimeline timeline = new AttachmentTimeline(frameCount);
timeline.slotIndex = slotIndex;
for (int frameIndex = 0; frameIndex < frameCount; frameIndex++)
timeline.SetFrame(frameIndex, ReadFloat(input), ReadString(input));
timelines.Add(timeline);
duration = Math.Max(duration, timeline.frames[frameCount - 1]);
break;
}
case SLOT_COLOR: {
ColorTimeline timeline = new ColorTimeline(frameCount);
timeline.slotIndex = slotIndex;
@ -516,13 +549,26 @@ namespace Spine {
duration = Math.Max(duration, timeline.frames[(timeline.FrameCount - 1) * ColorTimeline.ENTRIES]);
break;
}
case SLOT_ATTACHMENT: {
AttachmentTimeline timeline = new AttachmentTimeline(frameCount);
case SLOT_TWO_COLOR: {
TwoColorTimeline timeline = new TwoColorTimeline(frameCount);
timeline.slotIndex = slotIndex;
for (int frameIndex = 0; frameIndex < frameCount; frameIndex++)
timeline.SetFrame(frameIndex, ReadFloat(input), ReadString(input));
for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) {
float time = ReadFloat(input);
int color = ReadInt(input);
float r = ((color & 0xff000000) >> 24) / 255f;
float g = ((color & 0x00ff0000) >> 16) / 255f;
float b = ((color & 0x0000ff00) >> 8) / 255f;
float a = ((color & 0x000000ff)) / 255f;
int color2 = ReadInt(input);
float r2 = ((color2 & 0xff000000) >> 24) / 255f;
float g2 = ((color2 & 0x00ff0000) >> 16) / 255f;
float b2 = ((color2 & 0x0000ff00) >> 8) / 255f;
timeline.SetFrame(frameIndex, time, r, g, b, a, r2, g2, b2);
if (frameIndex < frameCount - 1) ReadCurve(input, frameIndex, timeline);
}
timelines.Add(timeline);
duration = Math.Max(duration, timeline.frames[frameCount - 1]);
duration = Math.Max(duration, timeline.frames[(timeline.FrameCount - 1) * TwoColorTimeline.ENTRIES]);
break;
}
}

View File

@ -146,6 +146,14 @@ namespace Spine {
data.b = ToColor(color, 2);
data.a = ToColor(color, 3);
}
if (slotMap.ContainsKey("dark")) {
var color2 = (String)slotMap["dark"];
data.r2 = ToColor(color2, 0);
data.g2 = ToColor(color2, 1);
data.b2 = ToColor(color2, 2);
data.hasSecondColor = true;
}
data.attachmentName = GetString(slotMap, "attachment", null);
if (slotMap.ContainsKey("blend"))
@ -394,6 +402,17 @@ namespace Spine {
pathAttachment.lengths = GetFloatArray(map, "lengths", scale);
return pathAttachment;
}
case AttachmentType.Point: {
PointAttachment point = attachmentLoader.NewPointAttachment(skin, name);
if (point == null) return null;
point.x = GetFloat(map, "x", 0) * scale;
point.y = GetFloat(map, "y", 0) * scale;
point.rotation = GetFloat(map, "rotation", 0);
//string color = GetString(map, "color", null);
//if (color != null) point.color = color;
return point;
}
}
return null;
}
@ -441,7 +460,19 @@ namespace Spine {
foreach (KeyValuePair<String, Object> timelineEntry in timelineMap) {
var values = (List<Object>)timelineEntry.Value;
var timelineName = (String)timelineEntry.Key;
if (timelineName == "color") {
if (timelineName == "attachment") {
var timeline = new AttachmentTimeline(values.Count);
timeline.slotIndex = slotIndex;
int frameIndex = 0;
foreach (Dictionary<String, Object> valueMap in values) {
float time = (float)valueMap["time"];
timeline.SetFrame(frameIndex++, time, (String)valueMap["name"]);
}
timelines.Add(timeline);
duration = Math.Max(duration, timeline.frames[timeline.FrameCount - 1]);
} else if (timelineName == "color") {
var timeline = new ColorTimeline(values.Count);
timeline.slotIndex = slotIndex;
@ -456,17 +487,22 @@ namespace Spine {
timelines.Add(timeline);
duration = Math.Max(duration, timeline.frames[(timeline.FrameCount - 1) * ColorTimeline.ENTRIES]);
} else if (timelineName == "attachment") {
var timeline = new AttachmentTimeline(values.Count);
} else if (timelineName == "twoColor") {
var timeline = new TwoColorTimeline(values.Count);
timeline.slotIndex = slotIndex;
int frameIndex = 0;
foreach (Dictionary<String, Object> valueMap in values) {
float time = (float)valueMap["time"];
timeline.SetFrame(frameIndex++, time, (String)valueMap["name"]);
String c = (String)valueMap["light"];
String c2 = (String)valueMap["dark"];
timeline.SetFrame(frameIndex, time, ToColor(c, 0), ToColor(c, 1), ToColor(c, 2), ToColor(c, 3),
ToColor(c2, 0), ToColor(c2, 1), ToColor(c2, 2));
ReadCurve(valueMap, timeline, frameIndex);
frameIndex++;
}
timelines.Add(timeline);
duration = Math.Max(duration, timeline.frames[timeline.FrameCount - 1]);
duration = Math.Max(duration, timeline.frames[(timeline.FrameCount - 1) * ColorTimeline.ENTRIES]);
} else
throw new Exception("Invalid timeline type for a slot: " + timelineName + " (" + slotName + ")");

View File

@ -35,6 +35,8 @@ namespace Spine {
internal SlotData data;
internal Bone bone;
internal float r, g, b, a;
internal float r2, g2, b2;
internal bool hasSecondColor;
internal Attachment attachment;
internal float attachmentTime;
internal ExposedList<float> attachmentVertices = new ExposedList<float>();
@ -47,6 +49,11 @@ namespace Spine {
public float B { get { return b; } set { b = value; } }
public float A { get { return a; } set { a = value; } }
public float R2 { get { return r2; } set { r2 = value; } }
public float G2 { get { return g2; } set { g2 = value; } }
public float B2 { get { return b2; } set { b2 = value; } }
public bool HasSecondColor { get { return hasSecondColor; } set { hasSecondColor = value; } }
/// <summary>May be null.</summary>
public Attachment Attachment {
get { return attachment; }

View File

@ -33,19 +33,27 @@ using System;
namespace Spine {
public class SlotData {
internal int index;
internal String name;
internal string name;
internal BoneData boneData;
internal float r = 1, g = 1, b = 1, a = 1;
internal String attachmentName;
internal float r2 = 0, g2 = 0, b2 = 0;
internal bool hasSecondColor = false;
internal string attachmentName;
internal BlendMode blendMode;
public int Index { get { return index; } }
public String Name { get { return name; } }
public string Name { get { return name; } }
public BoneData BoneData { get { return boneData; } }
public 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 float R2 { get { return r2; } set { r2 = value; } }
public float G2 { get { return g2; } set { g2 = value; } }
public float B2 { get { return b2; } set { b2 = value; } }
public bool HasSecondColor { get { return hasSecondColor; } set { hasSecondColor = value; } }
/// <summary>May be null.</summary>
public String AttachmentName { get { return attachmentName; } set { attachmentName = value; } }
public BlendMode BlendMode { get { return blendMode; } set { blendMode = value; } }

View File

@ -67,15 +67,28 @@ namespace Spine {
}
public void Update () {
if (data.local) {
if (data.relative)
ApplyRelativeLocal();
else
ApplyAbsoluteLocal();
} else {
if (data.relative)
ApplyRelativeWorld();
else
ApplyAbsoluteWorld();
}
}
void ApplyAbsoluteWorld () {
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;
float degRadReflect = (ta * td - tb * tc > 0) ? MathUtils.DegRad : -MathUtils.DegRad;
float degRadReflect = ta * td - tb * tc > 0 ? MathUtils.DegRad : -MathUtils.DegRad;
float offsetRotation = data.offsetRotation * degRadReflect, offsetShearY = data.offsetShearY * degRadReflect;
var bones = this.bones;
var bonesItems = bones.Items;
for (int i = 0, n = bones.Count; i < n; i++) {
Bone bone = bonesItems[i];
Bone bone = bones.Items[i];
bool modified = false;
if (rotateMix != 0) {
@ -94,22 +107,20 @@ namespace Spine {
}
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;
float tx, ty; //Vector2 temp = this.temp;
target.LocalToWorld(data.offsetX, data.offsetY, out tx, out ty); //target.localToWorld(temp.set(data.offsetX, data.offsetY));
bone.worldX += (tx - bone.worldX) * translateMix;
bone.worldY += (ty - bone.worldY) * translateMix;
modified = true;
}
if (scaleMix > 0) {
float s = (float)Math.Sqrt(bone.a * bone.a + bone.c * bone.c);
float ts = (float)Math.Sqrt(ta * ta + tc * tc);
if (s > 0.00001f) s = (s + (ts - s + data.offsetScaleX) * scaleMix) / s;
if (s > 0.00001f) s = (s + ((float)Math.Sqrt(ta * ta + tc * tc) - s + data.offsetScaleX) * scaleMix) / s;
bone.a *= s;
bone.c *= s;
s = (float)Math.Sqrt(bone.b * bone.b + bone.d * bone.d);
ts = (float)Math.Sqrt(tb * tb + td * td);
if (s > 0.00001f) s = (s + (ts - s + data.offsetScaleY) * scaleMix) / s;
if (s > 0.00001f) s = (s + ((float)Math.Sqrt(tb * tb + td * td) - s + data.offsetScaleY) * scaleMix) / s;
bone.b *= s;
bone.d *= s;
modified = true;
@ -133,6 +144,137 @@ namespace Spine {
}
}
void ApplyRelativeWorld () {
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;
float degRadReflect = ta * td - tb * tc > 0 ? MathUtils.DegRad : -MathUtils.DegRad;
float offsetRotation = data.offsetRotation * degRadReflect, offsetShearY = data.offsetShearY * degRadReflect;
var bones = this.bones;
for (int i = 0, n = bones.Count; i < n; i++) {
Bone bone = bones.Items[i];
bool modified = false;
if (rotateMix != 0) {
float a = bone.a, b = bone.b, c = bone.c, d = bone.d;
float r = MathUtils.Atan2(tc, ta) + offsetRotation;
if (r > MathUtils.PI)
r -= MathUtils.PI2;
else if (r < -MathUtils.PI) r += MathUtils.PI2;
r *= 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;
modified = true;
}
if (translateMix != 0) {
float tx, ty; //Vector2 temp = this.temp;
target.LocalToWorld(data.offsetX, data.offsetY, out tx, out ty); //target.localToWorld(temp.set(data.offsetX, data.offsetY));
bone.worldX += tx * translateMix;
bone.worldY += ty * translateMix;
modified = true;
}
if (scaleMix > 0) {
float s = ((float)Math.Sqrt(ta * ta + tc * tc) - 1 + data.offsetScaleX) * scaleMix + 1;
bone.a *= s;
bone.c *= s;
s = ((float)Math.Sqrt(tb * tb + td * td) - 1 + data.offsetScaleY) * scaleMix + 1;
bone.b *= s;
bone.d *= s;
modified = true;
}
if (shearMix > 0) {
float r = MathUtils.Atan2(td, tb) - MathUtils.Atan2(tc, ta);
if (r > MathUtils.PI)
r -= MathUtils.PI2;
else if (r < -MathUtils.PI) r += MathUtils.PI2;
float b = bone.b, d = bone.d;
r = MathUtils.Atan2(d, b) + (r - MathUtils.PI / 2 + offsetShearY) * shearMix;
float s = (float)Math.Sqrt(b * b + d * d);
bone.b = MathUtils.Cos(r) * s;
bone.d = MathUtils.Sin(r) * s;
modified = true;
}
if (modified) bone.appliedValid = false;
}
}
void ApplyAbsoluteLocal () {
float rotateMix = this.rotateMix, translateMix = this.translateMix, scaleMix = this.scaleMix, shearMix = this.shearMix;
Bone target = this.target;
if (!target.appliedValid) target.UpdateAppliedTransform();
var bones = this.bones;
for (int i = 0, n = bones.Count; i < n; i++) {
Bone bone = bones.Items[i];
if (!bone.appliedValid) bone.UpdateAppliedTransform();
float rotation = bone.arotation;
if (rotateMix != 0) {
float r = target.arotation - rotation + data.offsetRotation;
r -= (16384 - (int)(16384.499999999996 - r / 360)) * 360;
rotation += r * rotateMix;
}
float x = bone.ax, y = bone.ay;
if (translateMix != 0) {
x += (target.ax - x + data.offsetX) * translateMix;
y += (target.ay - y + data.offsetY) * translateMix;
}
float scaleX = bone.ascaleX, scaleY = bone.ascaleY;
if (scaleMix > 0) {
if (scaleX > 0.00001f) scaleX = (scaleX + (target.ascaleX - scaleX + data.offsetScaleX) * scaleMix) / scaleX;
if (scaleY > 0.00001f) scaleY = (scaleY + (target.ascaleY - scaleY + data.offsetScaleY) * scaleMix) / scaleY;
}
float shearY = bone.ashearY;
if (shearMix > 0) {
float r = target.ashearY - shearY + data.offsetShearY;
r -= (16384 - (int)(16384.499999999996 - r / 360)) * 360;
bone.shearY += r * shearMix;
}
bone.UpdateWorldTransform(x, y, rotation, scaleX, scaleY, bone.ashearX, shearY);
}
}
void ApplyRelativeLocal () {
float rotateMix = this.rotateMix, translateMix = this.translateMix, scaleMix = this.scaleMix, shearMix = this.shearMix;
Bone target = this.target;
if (!target.appliedValid) target.UpdateAppliedTransform();
var bones = this.bones;
for (int i = 0, n = bones.Count; i < n; i++) {
Bone bone = bones.Items[i];
if (!bone.appliedValid) bone.UpdateAppliedTransform();
float rotation = bone.arotation;
if (rotateMix != 0) rotation += (target.arotation + data.offsetRotation) * rotateMix;
float x = bone.ax, y = bone.ay;
if (translateMix != 0) {
x += (target.ax + data.offsetX) * translateMix;
y += (target.ay + data.offsetY) * translateMix;
}
float scaleX = bone.ascaleX, scaleY = bone.ascaleY;
if (scaleMix > 0) {
if (scaleX > 0.00001f) scaleX *= ((target.ascaleX - 1 + data.offsetScaleX) * scaleMix) + 1;
if (scaleY > 0.00001f) scaleY *= ((target.ascaleY - 1 + data.offsetScaleY) * scaleMix) + 1;
}
float shearY = bone.ashearY;
if (shearMix > 0) shearY += (target.ashearY + data.offsetShearY) * shearMix;
bone.UpdateWorldTransform(x, y, rotation, scaleX, scaleY, bone.ashearX, shearY);
}
}
override public String ToString () {
return data.name;
}

View File

@ -38,6 +38,7 @@ namespace Spine {
internal BoneData target;
internal float rotateMix, translateMix, scaleMix, shearMix;
internal float offsetRotation, offsetX, offsetY, offsetScaleX, offsetScaleY, offsetShearY;
internal bool relative, local;
public String Name { get { return name; } }
public int Order { get { return order; } set { order = value; } }
@ -55,6 +56,9 @@ namespace Spine {
public float OffsetScaleY { get { return offsetScaleY; } set { offsetScaleY = value; } }
public float OffsetShearY { get { return offsetShearY; } set { offsetShearY = value; } }
public bool Relative { get { return relative; } set { relative = value; } }
public bool Local { get { return local; } set { local = value; } }
public TransformConstraintData (String name) {
if (name == null) throw new ArgumentNullException("name", "name cannot be null.");
this.name = name;