Updated spine-csharp to 3.1.05: linked meshes. (#543)

* Updated spine-csharp to 3.1.00: linked meshes.

* Updated SkeletonBinary.cs for 3.1.05

based on:
3a06b829cc
8c55aa1f62

* Fixed skeleton flip when rotation and scale are disabled.

Based on: b22669711d

* Fixed IK when the parent has nonuniform scale and the child Y != 0.

Based on: bf902936aa

* Removed redundant locals.
This commit is contained in:
John 2016-04-13 23:10:20 +08:00 committed by Nathan Sweet
parent 2b27761539
commit ba5e4073c3
9 changed files with 437 additions and 266 deletions

View File

@ -573,7 +573,8 @@ namespace Spine {
override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha) {
Slot slot = skeleton.slots.Items[slotIndex];
if (slot.attachment != attachment) return;
IFfdAttachment ffdAttachment = slot.attachment as IFfdAttachment; // == null if not FfdAttachment.
if (ffdAttachment == null || !ffdAttachment.ApplyFFD(attachment)) return;
float[] frames = this.frames;
if (time < frames[0]) return; // Time is before first frame.

View File

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

View File

@ -0,0 +1,5 @@
namespace Spine {
public interface IFfdAttachment {
bool ApplyFFD (Attachment sourceAttachment);
}
}

View File

@ -33,11 +33,13 @@ using System;
namespace Spine {
/// <summary>Attachment that displays a texture region using a mesh.</summary>
public class MeshAttachment : Attachment {
public class MeshAttachment : Attachment, IFfdAttachment {
internal float[] vertices, uvs, regionUVs;
internal int[] triangles;
internal float regionOffsetX, regionOffsetY, regionWidth, regionHeight, regionOriginalWidth, regionOriginalHeight;
internal float r = 1, g = 1, b = 1, a = 1;
internal MeshAttachment parentMesh;
internal bool inheritFFD;
public int HullLength { get; set; }
public float[] Vertices { get { return vertices; } set { vertices = value; } }
@ -64,6 +66,21 @@ namespace Spine {
public float RegionOriginalWidth { get { return regionOriginalWidth; } set { regionOriginalWidth = value; } }
public float RegionOriginalHeight { get { return regionOriginalHeight; } set { regionOriginalHeight = value; } } // Unrotated, unstripped size.
public bool InheritFFD { get { return inheritFFD; } set { inheritFFD = value; } }
public MeshAttachment ParentMesh {
get { return parentMesh; }
set {
parentMesh = value;
if (value != null) {
vertices = value.vertices;
regionUVs = value.regionUVs;
triangles = value.triangles;
HullLength = value.HullLength;
}
}
}
// Nonessential.
public int[] Edges { get; set; }
public float Width { get; set; }
@ -105,5 +122,9 @@ namespace Spine {
worldVertices[i + 1] = vx * m10 + vy * m11 + y;
}
}
public bool ApplyFFD (Attachment sourceAttachment) {
return this == sourceAttachment || (inheritFFD && parentMesh == sourceAttachment);
}
}
}

View File

@ -34,12 +34,14 @@ using System.Collections.Generic;
namespace Spine {
/// <summary>Attachment that displays a texture region using a mesh which can be deformed by bones.</summary>
public class WeightedMeshAttachment : Attachment {
public class WeightedMeshAttachment : Attachment, IFfdAttachment {
internal int[] bones;
internal float[] weights, uvs, regionUVs;
internal int[] triangles;
internal float regionOffsetX, regionOffsetY, regionWidth, regionHeight, regionOriginalWidth, regionOriginalHeight;
internal float r = 1, g = 1, b = 1, a = 1;
internal WeightedMeshAttachment parentMesh;
internal bool inheritFFD;
public int HullLength { get; set; }
public int[] Bones { get { return bones; } set { bones = value; } }
@ -67,6 +69,22 @@ namespace Spine {
public float RegionOriginalWidth { get { return regionOriginalWidth; } set { regionOriginalWidth = value; } }
public float RegionOriginalHeight { get { return regionOriginalHeight; } set { regionOriginalHeight = value; } } // Unrotated, unstripped size.
public bool InheritFFD { get { return inheritFFD; } set { inheritFFD = value; } }
public WeightedMeshAttachment ParentMesh {
get { return parentMesh; }
set {
parentMesh = value;
if (value != null) {
bones = value.bones;
weights = value.weights;
regionUVs = value.regionUVs;
triangles = value.triangles;
HullLength = value.HullLength;
}
}
}
// Nonessential.
public int[] Edges { get; set; }
public float Width { get; set; }
@ -129,5 +147,9 @@ namespace Spine {
}
}
}
public bool ApplyFFD (Attachment sourceAttachment) {
return this == sourceAttachment || (inheritFFD && parentMesh == sourceAttachment);
}
}
}

View File

@ -139,84 +139,78 @@ namespace Spine {
b = pa * lb + pb * ld;
c = pc * la + pd * lc;
d = pc * lb + pd * ld;
} else if (data.inheritRotation) { // No scale inheritance.
pa = 1;
pb = 0;
pc = 0;
pd = 1;
do {
cos = MathUtils.CosDeg(parent.appliedRotation);
sin = MathUtils.SinDeg(parent.appliedRotation);
float temp = pa * cos + pb * sin;
pb = pa * -sin + pb * cos;
pa = temp;
temp = pc * cos + pd * sin;
pd = pc * -sin + pd * cos;
pc = temp;
if (!parent.data.inheritRotation) break;
parent = parent.parent;
} while (parent != null);
a = pa * la + pb * lc;
b = pa * lb + pb * ld;
c = pc * la + pd * lc;
d = pc * lb + pd * ld;
if (skeleton.flipX) {
a = -a;
b = -b;
}
if (skeleton.flipY != yDown) {
c = -c;
d = -d;
}
} else if (data.inheritScale) { // No rotation inheritance.
pa = 1;
pb = 0;
pc = 0;
pd = 1;
do {
float r = parent.rotation;
cos = MathUtils.CosDeg(r);
sin = MathUtils.SinDeg(r);
float psx = parent.appliedScaleX, psy = parent.appliedScaleY;
float za = cos * psx, zb = -sin * psy, zc = sin * psx, zd = cos * psy;
float 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;
if (!parent.data.inheritScale) break;
parent = parent.parent;
} while (parent != null);
a = pa * la + pb * lc;
b = pa * lb + pb * ld;
c = pc * la + pd * lc;
d = pc * lb + pd * ld;
if (skeleton.flipX) {
a = -a;
b = -b;
}
if (skeleton.flipY != yDown) {
c = -c;
d = -d;
}
} else {
a = la;
b = lb;
c = lc;
d = ld;
if (data.inheritRotation) { // No scale inheritance.
pa = 1;
pb = 0;
pc = 0;
pd = 1;
do {
cos = MathUtils.CosDeg(parent.appliedRotation);
sin = MathUtils.SinDeg(parent.appliedRotation);
float temp = pa * cos + pb * sin;
pb = pa * -sin + pb * cos;
pa = temp;
temp = pc * cos + pd * sin;
pd = pc * -sin + pd * cos;
pc = temp;
if (!parent.data.inheritRotation) break;
parent = parent.parent;
} while (parent != null);
a = pa * la + pb * lc;
b = pa * lb + pb * ld;
c = pc * la + pd * lc;
d = pc * lb + pd * ld;
} else if (data.inheritScale) { // No rotation inheritance.
pa = 1;
pb = 0;
pc = 0;
pd = 1;
do {
float r = parent.rotation;
cos = MathUtils.CosDeg(r);
sin = MathUtils.SinDeg(r);
float psx = parent.appliedScaleX, psy = parent.appliedScaleY;
float za = cos * psx, zb = -sin * psy, zc = sin * psx, zd = cos * psy;
float 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;
if (!parent.data.inheritScale) break;
parent = parent.parent;
} while (parent != null);
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;
}
if (skeleton.flipX) {
a = -a;
b = -b;
}
if (skeleton.flipY != yDown) {
c = -c;
d = -d;
}
}
}
@ -238,9 +232,8 @@ namespace Spine {
}
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;
worldX = localX * a + localY * b + this.worldX;
worldY = localX * c + localY * d + this.worldY;
}
override public String ToString () {

View File

@ -98,7 +98,7 @@ namespace Spine {
/// <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;
float px = parent.x, py = parent.y, psx = parent.scaleX, psy = parent.scaleY;
int offset1, offset2, sign2;
if (psx < 0) {
psx = -psx;
@ -112,6 +112,13 @@ namespace Spine {
psy = -psy;
sign2 = -sign2;
}
float cx = child.x, cy = child.y, csx = child.scaleX;
bool u = Math.Abs(psx - psy) <= 0.0001f;
if (!u && cy != 0) {
child.worldX = parent.a * cx + parent.worldX;
child.worldY = parent.c * cx + parent.worldY;
cy = 0;
}
if (csx < 0) {
csx = -csx;
offset2 = 180;
@ -135,7 +142,7 @@ namespace Spine {
dy = (y * a - x * c) * invDet - py;
}
float l1 = (float)Math.Sqrt(dx * dx + dy * dy), l2 = child.data.length * csx, a1, a2;
if (Math.Abs(psx - psy) <= 0.0001f) {
if (u) {
l2 *= psx;
float cos = (tx * tx + ty * ty - l1 * l1 - l2 * l2) / (2 * l1 * l2);
if (cos < -1) cos = -1;
@ -144,7 +151,6 @@ namespace Spine {
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;
@ -202,17 +208,17 @@ namespace Spine {
}
}
outer:
float offset = MathUtils.Atan2(cy, child.x) * sign2;
a1 = (a1 - offset) * MathUtils.radDeg + offset1;
a2 = (a2 + offset) * MathUtils.radDeg * sign2 + offset2;
float oo = MathUtils.Atan2(cy, cx) * sign2;
a1 = (a1 - oo) * MathUtils.radDeg + offset1;
a2 = (a2 + oo) * 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);
parent.UpdateWorldTransform(px, py, rotation + (a1 - rotation) * alpha, parent.appliedScaleX, parent.appliedScaleY);
rotation = child.rotation;
child.UpdateWorldTransform(child.x, cy, rotation + (a2 - rotation) * alpha, child.scaleX, child.scaleY);
child.UpdateWorldTransform(cx, cy, rotation + (a2 - rotation) * alpha, child.appliedScaleX, child.appliedScaleY);
}
}
}

View File

@ -52,15 +52,17 @@ namespace Spine {
private AttachmentLoader attachmentLoader;
public float Scale { get; set; }
private char[] chars = new char[32];
private byte[] bytes = new byte[32];
private byte[] buffer = new byte[4];
private List<SkeletonJson.LinkedMesh> linkedMeshes = new List<SkeletonJson.LinkedMesh>();
public SkeletonBinary (params Atlas[] atlasArray)
: this(new AtlasAttachmentLoader(atlasArray)) {
}
public SkeletonBinary (AttachmentLoader attachmentLoader) {
if (attachmentLoader == null) throw new ArgumentNullException("attachmentLoader cannot be null.");
if (attachmentLoader == null) throw new ArgumentNullException("attachmentLoader");
this.attachmentLoader = attachmentLoader;
Scale = 1;
}
@ -97,7 +99,7 @@ namespace Spine {
#endif // !(UNITY)
public SkeletonData ReadSkeletonData (Stream input) {
if (input == null) throw new ArgumentNullException("input cannot be null.");
if (input == null) throw new ArgumentNullException("input");
float scale = Scale;
var skeletonData = new SkeletonData();
@ -116,11 +118,9 @@ namespace Spine {
}
// Bones.
for (int i = 0, n = ReadInt(input, true); i < n; i++) {
for (int i = 0, n = ReadVarint(input, true); i < n; i++) {
String name = ReadString(input);
BoneData parent = null;
int parentIndex = ReadInt(input, true) - 1;
if (parentIndex != -1) parent = skeletonData.bones.Items[parentIndex];
BoneData parent = i == 0 ? null : skeletonData.bones.Items[ReadVarint(input, true)];
BoneData boneData = new BoneData(name, parent);
boneData.x = ReadFloat(input) * scale;
boneData.y = ReadFloat(input) * scale;
@ -135,21 +135,21 @@ namespace Spine {
}
// IK constraints.
for (int i = 0, n = ReadInt(input, true); i < n; i++) {
for (int i = 0, n = ReadVarint(input, true); i < n; i++) {
IkConstraintData ikConstraintData = new IkConstraintData(ReadString(input));
for (int ii = 0, nn = ReadInt(input, true); ii < nn; ii++)
ikConstraintData.bones.Add(skeletonData.bones.Items[ReadInt(input, true)]);
ikConstraintData.target = skeletonData.bones.Items[ReadInt(input, true)];
for (int ii = 0, nn = ReadVarint(input, true); ii < nn; ii++)
ikConstraintData.bones.Add(skeletonData.bones.Items[ReadVarint(input, true)]);
ikConstraintData.target = skeletonData.bones.Items[ReadVarint(input, true)];
ikConstraintData.mix = ReadFloat(input);
ikConstraintData.bendDirection = ReadSByte(input);
skeletonData.ikConstraints.Add(ikConstraintData);
}
// Transform constraints.
for (int i = 0, n = ReadInt(input, true); i < n; i++) {
for (int i = 0, n = ReadVarint(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.bone = skeletonData.bones.Items[ReadVarint(input, true)];
transformConstraintData.target = skeletonData.bones.Items[ReadVarint(input, true)];
transformConstraintData.translateMix = ReadFloat(input);
transformConstraintData.x = ReadFloat(input) * scale;
transformConstraintData.y = ReadFloat(input) * scale;
@ -157,9 +157,9 @@ namespace Spine {
}
// Slots.
for (int i = 0, n = ReadInt(input, true); i < n; i++) {
for (int i = 0, n = ReadVarint(input, true); i < n; i++) {
String slotName = ReadString(input);
BoneData boneData = skeletonData.bones.Items[ReadInt(input, true)];
BoneData boneData = skeletonData.bones.Items[ReadVarint(input, true)];
SlotData slotData = new SlotData(slotName, boneData);
int color = ReadInt(input);
slotData.r = ((color & 0xff000000) >> 24) / 255f;
@ -167,7 +167,7 @@ namespace Spine {
slotData.b = ((color & 0x0000ff00) >> 8) / 255f;
slotData.a = ((color & 0x000000ff)) / 255f;
slotData.attachmentName = ReadString(input);
slotData.blendMode = (BlendMode)ReadInt(input, true);
slotData.blendMode = (BlendMode)ReadVarint(input, true);
skeletonData.slots.Add(slotData);
}
@ -179,20 +179,39 @@ namespace Spine {
}
// Skins.
for (int i = 0, n = ReadInt(input, true); i < n; i++)
for (int i = 0, n = ReadVarint(input, true); i < n; i++)
skeletonData.skins.Add(ReadSkin(input, ReadString(input), nonessential));
// Linked meshes.
for (int i = 0, n = linkedMeshes.Count; i < n; i++) {
SkeletonJson.LinkedMesh linkedMesh = linkedMeshes[i];
Skin skin = linkedMesh.skin == null ? skeletonData.DefaultSkin : skeletonData.FindSkin(linkedMesh.skin);
if (skin == null) throw new Exception("Skin not found: " + linkedMesh.skin);
Attachment parent = skin.GetAttachment(linkedMesh.slotIndex, linkedMesh.parent);
if (parent == null) throw new Exception("Parent mesh not found: " + linkedMesh.parent);
if (linkedMesh.mesh is MeshAttachment) {
MeshAttachment mesh = (MeshAttachment)linkedMesh.mesh;
mesh.ParentMesh = (MeshAttachment)parent;
mesh.UpdateUVs();
} else {
WeightedMeshAttachment mesh = (WeightedMeshAttachment)linkedMesh.mesh;
mesh.ParentMesh = (WeightedMeshAttachment)parent;
mesh.UpdateUVs();
}
}
linkedMeshes.Clear();
// Events.
for (int i = 0, n = ReadInt(input, true); i < n; i++) {
for (int i = 0, n = ReadVarint(input, true); i < n; i++) {
EventData eventData = new EventData(ReadString(input));
eventData.Int = ReadInt(input, false);
eventData.Int = ReadVarint(input, false);
eventData.Float = ReadFloat(input);
eventData.String = ReadString(input);
skeletonData.events.Add(eventData);
}
// Animations.
for (int i = 0, n = ReadInt(input, true); i < n; i++)
for (int i = 0, n = ReadVarint(input, true); i < n; i++)
ReadAnimation(ReadString(input), input, skeletonData);
skeletonData.bones.TrimExcess();
@ -206,40 +225,49 @@ namespace Spine {
/** @return May be null. */
private Skin ReadSkin (Stream input, String skinName, bool nonessential) {
int slotCount = ReadInt(input, true);
int slotCount = ReadVarint(input, true);
if (slotCount == 0) return null;
Skin skin = new Skin(skinName);
for (int i = 0; i < slotCount; i++) {
int slotIndex = ReadInt(input, true);
for (int ii = 0, nn = ReadInt(input, true); ii < nn; ii++) {
int slotIndex = ReadVarint(input, true);
for (int ii = 0, nn = ReadVarint(input, true); ii < nn; ii++) {
String name = ReadString(input);
skin.AddAttachment(slotIndex, name, ReadAttachment(input, skin, name, nonessential));
skin.AddAttachment(slotIndex, name, ReadAttachment(input, skin, slotIndex, name, nonessential));
}
}
return skin;
}
private Attachment ReadAttachment (Stream input, Skin skin, String attachmentName, bool nonessential) {
private Attachment ReadAttachment (Stream input, Skin skin, int slotIndex, String attachmentName, bool nonessential) {
float scale = Scale;
String name = ReadString(input);
if (name == null) name = attachmentName;
switch ((AttachmentType)input.ReadByte()) {
AttachmentType type = (AttachmentType)input.ReadByte();
switch (type) {
case AttachmentType.region: {
String path = ReadString(input);
float x = ReadFloat(input);
float y = ReadFloat(input);
float scaleX = ReadFloat(input);
float scaleY = ReadFloat(input);
float rotation = ReadFloat(input);
float width = ReadFloat(input);
float height = ReadFloat(input);
int color = ReadInt(input);
if (path == null) path = name;
RegionAttachment region = attachmentLoader.NewRegionAttachment(skin, name, path);
if (region == null) return null;
region.Path = path;
region.x = ReadFloat(input) * scale;
region.y = ReadFloat(input) * scale;
region.scaleX = ReadFloat(input);
region.scaleY = ReadFloat(input);
region.rotation = ReadFloat(input);
region.width = ReadFloat(input) * scale;
region.height = ReadFloat(input) * scale;
int color = ReadInt(input);
region.x = x * scale;
region.y = y * scale;
region.scaleX = scaleX;
region.scaleY = scaleY;
region.rotation = rotation;
region.width = width * scale;
region.height = height * scale;
region.r = ((color & 0xff000000) >> 24) / 255f;
region.g = ((color & 0x00ff0000) >> 16) / 255f;
region.b = ((color & 0x0000ff00) >> 8) / 255f;
@ -248,80 +276,159 @@ namespace Spine {
return region;
}
case AttachmentType.boundingbox: {
float[] vertices = ReadFloatArray(input, ReadVarint(input, true) * 2, scale);
BoundingBoxAttachment box = attachmentLoader.NewBoundingBoxAttachment(skin, name);
if (box == null) return null;
box.vertices = ReadFloatArray(input, scale);
box.vertices = vertices;
return box;
}
case AttachmentType.mesh: {
String path = ReadString(input);
int color = ReadInt(input);
int hullLength = 0;
int verticesLength = ReadVarint(input, true) * 2;
float[] uvs = ReadFloatArray(input, verticesLength, 1);
int[] triangles = ReadShortArray(input);
float[] vertices = ReadFloatArray(input, verticesLength, scale);
hullLength = ReadVarint(input, true);
int[] edges = null;
float width = 0, height = 0;
if (nonessential) {
edges = ReadShortArray(input);
width = ReadFloat(input);
height = ReadFloat(input);
}
if (path == null) path = name;
MeshAttachment mesh = attachmentLoader.NewMeshAttachment(skin, name, path);
if (mesh == null) return null;
mesh.Path = path;
mesh.regionUVs = ReadFloatArray(input, 1);
mesh.triangles = ReadShortArray(input);
mesh.vertices = ReadFloatArray(input, scale);
mesh.UpdateUVs();
int color = ReadInt(input);
mesh.r = ((color & 0xff000000) >> 24) / 255f;
mesh.g = ((color & 0x00ff0000) >> 16) / 255f;
mesh.b = ((color & 0x0000ff00) >> 8) / 255f;
mesh.a = ((color & 0x000000ff)) / 255f;
mesh.HullLength = ReadInt(input, true) * 2;
mesh.vertices = vertices;
mesh.triangles = triangles;
mesh.regionUVs = uvs;
mesh.UpdateUVs();
mesh.HullLength = hullLength;
if (nonessential) {
mesh.Edges = ReadIntArray(input);
mesh.Width = ReadFloat(input) * scale;
mesh.Height = ReadFloat(input) * scale;
mesh.Edges = edges;
mesh.Width = width * scale;
mesh.Height = height * scale;
}
return mesh;
}
case AttachmentType.linkedmesh: {
String path = ReadString(input);
int color = ReadInt(input);
String skinName = ReadString(input);
String parent = ReadString(input);
bool inheritFFD = ReadBoolean(input);
float width = 0, height = 0;
if (nonessential) {
width = ReadFloat(input);
height = ReadFloat(input);
}
if (path == null) path = name;
MeshAttachment mesh = attachmentLoader.NewMeshAttachment(skin, name, path);
if (mesh == null) return null;
mesh.Path = path;
mesh.r = ((color & 0xff000000) >> 24) / 255f;
mesh.g = ((color & 0x00ff0000) >> 16) / 255f;
mesh.b = ((color & 0x0000ff00) >> 8) / 255f;
mesh.a = ((color & 0x000000ff)) / 255f;
mesh.inheritFFD = inheritFFD;
if (nonessential) {
mesh.Width = width * scale;
mesh.Height = height * scale;
}
linkedMeshes.Add(new SkeletonJson.LinkedMesh(mesh, skinName, slotIndex, parent));
return mesh;
}
case AttachmentType.weightedmesh: {
String path = ReadString(input);
if (path == null) path = name;
WeightedMeshAttachment mesh = attachmentLoader.NewWeightedMeshAttachment(skin, name, path);
if (mesh == null) return null;
mesh.Path = path;
float[] uvs = ReadFloatArray(input, 1);
int color = ReadInt(input);
int vertexCount = ReadVarint(input, true);
float[] uvs = ReadFloatArray(input, vertexCount * 2, 1);
int[] triangles = ReadShortArray(input);
int vertexCount = ReadInt(input, true);
var weights = new List<float>(uvs.Length * 3 * 3);
var bones = new List<int>(uvs.Length * 3);
for (int i = 0; i < vertexCount; i++) {
int boneCount = (int)ReadFloat(input);
bones.Add(boneCount);
for (int nn = i + boneCount * 4; i < nn; i += 4) {
for (int ii = 0; i < boneCount; ii++) {
bones.Add((int)ReadFloat(input));
weights.Add(ReadFloat(input) * scale);
weights.Add(ReadFloat(input) * scale);
weights.Add(ReadFloat(input));
}
}
int hullLength = ReadVarint(input, true);
int[] edges = null;
float width = 0, height = 0;
if (nonessential) {
edges = ReadShortArray(input);
width = ReadFloat(input);
height = ReadFloat(input);
}
if (path == null) path = name;
WeightedMeshAttachment mesh = attachmentLoader.NewWeightedMeshAttachment(skin, name, path);
if (mesh == null) return null;
mesh.Path = path;
mesh.r = ((color & 0xff000000) >> 24) / 255f;
mesh.g = ((color & 0x00ff0000) >> 16) / 255f;
mesh.b = ((color & 0x0000ff00) >> 8) / 255f;
mesh.a = ((color & 0x000000ff)) / 255f;
mesh.bones = bones.ToArray();
mesh.weights = weights.ToArray();
mesh.triangles = triangles;
mesh.regionUVs = uvs;
mesh.UpdateUVs();
mesh.HullLength = hullLength * 2;
if (nonessential) {
mesh.Edges = edges;
mesh.Width = width * scale;
mesh.Height = height * scale;
}
//
return mesh;
}
case AttachmentType.weightedlinkedmesh: {
String path = ReadString(input);
int color = ReadInt(input);
String skinName = ReadString(input);
String parent = ReadString(input);
bool inheritFFD = ReadBoolean(input);
float width = 0, height = 0;
if (nonessential) {
width = ReadFloat(input);
height = ReadFloat(input);
}
if (path == null) path = name;
WeightedMeshAttachment mesh = attachmentLoader.NewWeightedMeshAttachment(skin, name, path);
if (mesh == null) return null;
mesh.Path = path;
mesh.r = ((color & 0xff000000) >> 24) / 255f;
mesh.g = ((color & 0x00ff0000) >> 16) / 255f;
mesh.b = ((color & 0x0000ff00) >> 8) / 255f;
mesh.a = ((color & 0x000000ff)) / 255f;
mesh.HullLength = ReadInt(input, true) * 2;
mesh.inheritFFD = inheritFFD;
if (nonessential) {
mesh.Edges = ReadIntArray(input);
mesh.Width = ReadFloat(input) * scale;
mesh.Height = ReadFloat(input) * scale;
mesh.Width = width * scale;
mesh.Height = height * scale;
}
linkedMeshes.Add(new SkeletonJson.LinkedMesh(mesh, skinName, slotIndex, parent));
return mesh;
}
}
return null;
}
private float[] ReadFloatArray (Stream input, float scale) {
int n = ReadInt(input, true);
private float[] ReadFloatArray (Stream input, int n, float scale) {
float[] array = new float[n];
if (scale == 1) {
for (int i = 0; i < n; i++)
@ -334,18 +441,10 @@ namespace Spine {
}
private int[] ReadShortArray (Stream input) {
int n = ReadInt(input, true);
int n = ReadVarint(input, true);
int[] array = new int[n];
for (int i = 0; i < n; i++)
array[i] = (input.ReadByte() << 8) + input.ReadByte();
return array;
}
private int[] ReadIntArray (Stream input) {
int n = ReadInt(input, true);
int[] array = new int[n];
for (int i = 0; i < n; i++)
array[i] = ReadInt(input, true);
for (int i = 0; i < n; i++)
array[i] = (input.ReadByte() << 8) | input.ReadByte();
return array;
}
@ -355,11 +454,11 @@ namespace Spine {
float duration = 0;
// Slot timelines.
for (int i = 0, n = ReadInt(input, true); i < n; i++) {
int slotIndex = ReadInt(input, true);
for (int ii = 0, nn = ReadInt(input, true); ii < nn; ii++) {
for (int i = 0, n = ReadVarint(input, true); i < n; i++) {
int slotIndex = ReadVarint(input, true);
for (int ii = 0, nn = ReadVarint(input, true); ii < nn; ii++) {
int timelineType = input.ReadByte();
int frameCount = ReadInt(input, true);
int frameCount = ReadVarint(input, true);
switch (timelineType) {
case TIMELINE_COLOR: {
ColorTimeline timeline = new ColorTimeline(frameCount);
@ -392,11 +491,11 @@ namespace Spine {
}
// Bone timelines.
for (int i = 0, n = ReadInt(input, true); i < n; i++) {
int boneIndex = ReadInt(input, true);
for (int ii = 0, nn = ReadInt(input, true); ii < nn; ii++) {
for (int i = 0, n = ReadVarint(input, true); i < n; i++) {
int boneIndex = ReadVarint(input, true);
for (int ii = 0, nn = ReadVarint(input, true); ii < nn; ii++) {
int timelineType = input.ReadByte();
int frameCount = ReadInt(input, true);
int frameCount = ReadVarint(input, true);
switch (timelineType) {
case TIMELINE_ROTATE: {
RotateTimeline timeline = new RotateTimeline(frameCount);
@ -434,9 +533,9 @@ namespace Spine {
}
// IK timelines.
for (int i = 0, n = ReadInt(input, true); i < n; i++) {
IkConstraintData ikConstraint = skeletonData.ikConstraints.Items[ReadInt(input, true)];
int frameCount = ReadInt(input, true);
for (int i = 0, n = ReadVarint(input, true); i < n; i++) {
IkConstraintData ikConstraint = skeletonData.ikConstraints.Items[ReadVarint(input, true)];
int frameCount = ReadVarint(input, true);
IkConstraintTimeline timeline = new IkConstraintTimeline(frameCount);
timeline.ikConstraintIndex = skeletonData.ikConstraints.IndexOf(ikConstraint);
for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) {
@ -448,13 +547,13 @@ namespace Spine {
}
// FFD timelines.
for (int i = 0, n = ReadInt(input, true); i < n; i++) {
Skin skin = skeletonData.skins.Items[ReadInt(input, true)];
for (int ii = 0, nn = ReadInt(input, true); ii < nn; ii++) {
int slotIndex = ReadInt(input, true);
for (int iii = 0, nnn = ReadInt(input, true); iii < nnn; iii++) {
for (int i = 0, n = ReadVarint(input, true); i < n; i++) {
Skin skin = skeletonData.skins.Items[ReadVarint(input, true)];
for (int ii = 0, nn = ReadVarint(input, true); ii < nn; ii++) {
int slotIndex = ReadVarint(input, true);
for (int iii = 0, nnn = ReadVarint(input, true); iii < nnn; iii++) {
Attachment attachment = skin.GetAttachment(slotIndex, ReadString(input));
int frameCount = ReadInt(input, true);
int frameCount = ReadVarint(input, true);
FFDTimeline timeline = new FFDTimeline(frameCount);
timeline.slotIndex = slotIndex;
timeline.attachment = attachment;
@ -468,7 +567,7 @@ namespace Spine {
else
vertexCount = ((WeightedMeshAttachment)attachment).weights.Length / 3 * 2;
int end = ReadInt(input, true);
int end = ReadVarint(input, true);
if (end == 0) {
if (attachment is MeshAttachment)
vertices = ((MeshAttachment)attachment).vertices;
@ -476,7 +575,7 @@ namespace Spine {
vertices = new float[vertexCount];
} else {
vertices = new float[vertexCount];
int start = ReadInt(input, true);
int start = ReadVarint(input, true);
end += start;
if (scale == 1) {
for (int v = start; v < end; v++)
@ -502,24 +601,24 @@ namespace Spine {
}
// Draw order timeline.
int drawOrderCount = ReadInt(input, true);
int drawOrderCount = ReadVarint(input, true);
if (drawOrderCount > 0) {
DrawOrderTimeline timeline = new DrawOrderTimeline(drawOrderCount);
int slotCount = skeletonData.slots.Count;
for (int i = 0; i < drawOrderCount; i++) {
int offsetCount = ReadInt(input, true);
int offsetCount = ReadVarint(input, true);
int[] drawOrder = new int[slotCount];
for (int ii = slotCount - 1; ii >= 0; ii--)
drawOrder[ii] = -1;
int[] unchanged = new int[slotCount - offsetCount];
int originalIndex = 0, unchangedIndex = 0;
for (int ii = 0; ii < offsetCount; ii++) {
int slotIndex = ReadInt(input, true);
int slotIndex = ReadVarint(input, true);
// Collect unchanged items.
while (originalIndex != slotIndex)
unchanged[unchangedIndex++] = originalIndex++;
// Set changed items.
drawOrder[originalIndex + ReadInt(input, true)] = originalIndex++;
drawOrder[originalIndex + ReadVarint(input, true)] = originalIndex++;
}
// Collect remaining unchanged items.
while (originalIndex < slotCount)
@ -534,14 +633,14 @@ namespace Spine {
}
// Event timeline.
int eventCount = ReadInt(input, true);
int eventCount = ReadVarint(input, true);
if (eventCount > 0) {
EventTimeline timeline = new EventTimeline(eventCount);
for (int i = 0; i < eventCount; i++) {
float time = ReadFloat(input);
EventData eventData = skeletonData.events.Items[ReadInt(input, true)];
EventData eventData = skeletonData.events.Items[ReadVarint(input, true)];
Event e = new Event(time, eventData);
e.Int = ReadInt(input, false);
e.Int = ReadVarint(input, false);
e.Float = ReadFloat(input);
e.String = ReadBoolean(input) ? ReadString(input) : eventData.String;
timeline.SetFrame(i, e);
@ -565,13 +664,13 @@ namespace Spine {
}
}
private sbyte ReadSByte (Stream input) {
private static sbyte ReadSByte (Stream input) {
int value = input.ReadByte();
if (value == -1) throw new EndOfStreamException();
return (sbyte)value;
}
private bool ReadBoolean (Stream input) {
private static bool ReadBoolean (Stream input) {
return input.ReadByte() != 0;
}
@ -583,11 +682,11 @@ namespace Spine {
return BitConverter.ToSingle(buffer, 0);
}
private int ReadInt (Stream input) {
private static int ReadInt (Stream input) {
return (input.ReadByte() << 24) + (input.ReadByte() << 16) + (input.ReadByte() << 8) + input.ReadByte();
}
private int ReadInt (Stream input, bool optimizePositive) {
private static int ReadVarint (Stream input, bool optimizePositive) {
int b = input.ReadByte();
int result = b & 0x7F;
if ((b & 0x80) != 0) {
@ -600,8 +699,7 @@ namespace Spine {
b = input.ReadByte();
result |= (b & 0x7F) << 21;
if ((b & 0x80) != 0) {
b = input.ReadByte();
result |= (b & 0x7F) << 28;
result |= (input.ReadByte() & 0x7F) << 28;
}
}
}
@ -610,53 +708,28 @@ namespace Spine {
}
private string ReadString (Stream input) {
int charCount = ReadInt(input, true);
switch (charCount) {
int byteCount = ReadVarint(input, true);
switch (byteCount) {
case 0:
return null;
case 1:
return "";
}
charCount--;
char[] chars = this.chars;
if (chars.Length < charCount) this.chars = chars = new char[charCount];
// Try to read 7 bit ASCII chars.
int charIndex = 0;
int b = 0;
while (charIndex < charCount) {
b = input.ReadByte();
if (b > 127) break;
chars[charIndex++] = (char)b;
}
// If a char was not ASCII, finish with slow path.
if (charIndex < charCount) ReadUtf8_slow(input, charCount, charIndex, b);
return new String(chars, 0, charCount);
byteCount--;
byte[] bytes = this.bytes;
if (bytes.Length < byteCount) bytes = new byte[byteCount];
ReadFully(input, bytes, 0, byteCount);
return System.Text.Encoding.UTF8.GetString(bytes, 0, byteCount);
}
private void ReadUtf8_slow (Stream input, int charCount, int charIndex, int b) {
char[] chars = this.chars;
while (true) {
switch (b >> 4) {
case 0:
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
chars[charIndex] = (char)b;
break;
case 12:
case 13:
chars[charIndex] = (char)((b & 0x1F) << 6 | input.ReadByte() & 0x3F);
break;
case 14:
chars[charIndex] = (char)((b & 0x0F) << 12 | (input.ReadByte() & 0x3F) << 6 | input.ReadByte() & 0x3F);
break;
private static void ReadFully (Stream input, byte[] b, int off, int len) {
while (len > 0) {
int count = input.Read(b, off, len);
if (count <= 0) {
throw new EndOfStreamException();
}
if (++charIndex >= charCount) break;
b = input.ReadByte() & 0xFF;
off += count;
len -= count;
}
}
}

View File

@ -46,6 +46,7 @@ namespace Spine {
public class SkeletonJson {
private AttachmentLoader attachmentLoader;
public float Scale { get; set; }
private List<LinkedMesh> linkedMeshes = new List<LinkedMesh>();
public SkeletonJson (params Atlas[] atlasArray)
: this(new AtlasAttachmentLoader(atlasArray)) {
@ -208,7 +209,7 @@ namespace Spine {
foreach (KeyValuePair<String, Object> slotEntry in (Dictionary<String, Object>)entry.Value) {
int slotIndex = skeletonData.FindSlotIndex(slotEntry.Key);
foreach (KeyValuePair<String, Object> attachmentEntry in ((Dictionary<String, Object>)slotEntry.Value)) {
Attachment attachment = ReadAttachment(skin, attachmentEntry.Key, (Dictionary<String, Object>)attachmentEntry.Value);
Attachment attachment = ReadAttachment(skin, slotIndex, attachmentEntry.Key, (Dictionary<String, Object>)attachmentEntry.Value);
if (attachment != null) skin.AddAttachment(slotIndex, attachmentEntry.Key, attachment);
}
}
@ -218,6 +219,25 @@ namespace Spine {
}
}
// Linked meshes.
for (int i = 0, n = linkedMeshes.Count; i < n; i++) {
LinkedMesh linkedMesh = linkedMeshes[i];
Skin skin = linkedMesh.skin == null ? skeletonData.defaultSkin : skeletonData.FindSkin(linkedMesh.skin);
if (skin == null) throw new Exception("Slot not found: " + linkedMesh.skin);
Attachment parent = skin.GetAttachment(linkedMesh.slotIndex, linkedMesh.parent);
if (parent == null) throw new Exception("Parent mesh not found: " + linkedMesh.parent);
if (linkedMesh.mesh is MeshAttachment) {
MeshAttachment mesh = (MeshAttachment)linkedMesh.mesh;
mesh.ParentMesh = (MeshAttachment)parent;
mesh.UpdateUVs();
} else {
WeightedMeshAttachment mesh = (WeightedMeshAttachment)linkedMesh.mesh;
mesh.ParentMesh = (WeightedMeshAttachment)parent;
mesh.UpdateUVs();
}
}
linkedMeshes.Clear();
// Events.
if (root.ContainsKey("events")) {
foreach (KeyValuePair<String, Object> entry in (Dictionary<String, Object>)root["events"]) {
@ -245,7 +265,7 @@ namespace Spine {
return skeletonData;
}
private Attachment ReadAttachment (Skin skin, String name, Dictionary<String, Object> map) {
private Attachment ReadAttachment (Skin skin, int slotIndex, String name, Dictionary<String, Object> map) {
if (map.ContainsKey("name"))
name = (String)map["name"];
@ -285,15 +305,11 @@ namespace Spine {
}
return region;
case AttachmentType.mesh: {
case AttachmentType.mesh:
case AttachmentType.linkedmesh: {
MeshAttachment mesh = attachmentLoader.NewMeshAttachment(skin, name, path);
if (mesh == null) return null;
mesh.Path = path;
mesh.vertices = GetFloatArray(map, "vertices", scale);
mesh.triangles = GetIntArray(map, "triangles");
mesh.regionUVs = GetFloatArray(map, "uvs", 1);
mesh.UpdateUVs();
if (map.ContainsKey("color")) {
var color = (String)map["color"];
@ -303,38 +319,31 @@ namespace Spine {
mesh.a = ToColor(color, 3);
}
mesh.HullLength = GetInt(map, "hull", 0) * 2;
if (map.ContainsKey("edges")) mesh.Edges = GetIntArray(map, "edges");
mesh.Width = GetInt(map, "width", 0) * scale;
mesh.Height = GetInt(map, "height", 0) * scale;
String parent = GetString(map, "parent", null);
if (parent == null) {
mesh.vertices = GetFloatArray(map, "vertices", scale);
mesh.triangles = GetIntArray(map, "triangles");
mesh.regionUVs = GetFloatArray(map, "uvs", 1);
mesh.UpdateUVs();
mesh.HullLength = GetInt(map, "hull", 0) * 2;
if (map.ContainsKey("edges")) mesh.Edges = GetIntArray(map, "edges");
} else {
mesh.InheritFFD = GetBoolean(map, "ffd", true);
linkedMeshes.Add(new LinkedMesh(mesh, GetString(map, "skin", null), slotIndex, parent));
}
return mesh;
}
case AttachmentType.weightedmesh: {
case AttachmentType.weightedmesh:
case AttachmentType.weightedlinkedmesh: {
WeightedMeshAttachment mesh = attachmentLoader.NewWeightedMeshAttachment(skin, name, path);
if (mesh == null) return null;
mesh.Path = path;
float[] uvs = GetFloatArray(map, "uvs", 1);
float[] vertices = GetFloatArray(map, "vertices", 1);
var weights = new List<float>(uvs.Length * 3 * 3);
var bones = new List<int>(uvs.Length * 3);
for (int i = 0, n = vertices.Length; i < n; ) {
int boneCount = (int)vertices[i++];
bones.Add(boneCount);
for (int nn = i + boneCount * 4; i < nn; ) {
bones.Add((int)vertices[i]);
weights.Add(vertices[i + 1] * scale);
weights.Add(vertices[i + 2] * scale);
weights.Add(vertices[i + 3]);
i += 4;
}
}
mesh.bones = bones.ToArray();
mesh.weights = weights.ToArray();
mesh.triangles = GetIntArray(map, "triangles");
mesh.regionUVs = uvs;
mesh.UpdateUVs();
if (map.ContainsKey("color")) {
var color = (String)map["color"];
@ -344,11 +353,39 @@ namespace Spine {
mesh.a = ToColor(color, 3);
}
mesh.HullLength = GetInt(map, "hull", 0) * 2;
if (map.ContainsKey("edges")) mesh.Edges = GetIntArray(map, "edges");
mesh.Width = GetInt(map, "width", 0) * scale;
mesh.Height = GetInt(map, "height", 0) * scale;
String parent = GetString(map, "parent", null);
if (parent == null) {
float[] uvs = GetFloatArray(map, "uvs", 1);
float[] vertices = GetFloatArray(map, "vertices", 1);
var weights = new List<float>(uvs.Length * 3 * 3);
var bones = new List<int>(uvs.Length * 3);
for (int i = 0, n = vertices.Length; i < n;) {
int boneCount = (int)vertices[i++];
bones.Add(boneCount);
for (int nn = i + boneCount * 4; i < nn;) {
bones.Add((int)vertices[i]);
weights.Add(vertices[i + 1] * scale);
weights.Add(vertices[i + 2] * scale);
weights.Add(vertices[i + 3]);
i += 4;
}
}
mesh.bones = bones.ToArray();
mesh.weights = weights.ToArray();
mesh.triangles = GetIntArray(map, "triangles");
mesh.regionUVs = uvs;
mesh.UpdateUVs();
mesh.HullLength = GetInt(map, "hull", 0) * 2;
if (map.ContainsKey("edges")) mesh.Edges = GetIntArray(map, "edges");
} else {
mesh.InheritFFD = GetBoolean(map, "ffd", true);
linkedMeshes.Add(new LinkedMesh(mesh, GetString(map, "skin", null), slotIndex, parent));
}
return mesh;
}
case AttachmentType.boundingbox:
@ -657,5 +694,18 @@ namespace Spine {
timeline.SetCurve(frameIndex, (float)curve[0], (float)curve[1], (float)curve[2], (float)curve[3]);
}
}
internal class LinkedMesh {
internal String parent, skin;
internal int slotIndex;
internal Attachment mesh;
public LinkedMesh (Attachment mesh, String skin, int slotIndex, String parent) {
this.mesh = mesh;
this.skin = skin;
this.slotIndex = slotIndex;
this.parent = parent;
}
}
}
}