diff --git a/spine-csharp/src/Animation.cs b/spine-csharp/src/Animation.cs index a802ef797..4953d23ef 100644 --- a/spine-csharp/src/Animation.cs +++ b/spine-csharp/src/Animation.cs @@ -573,7 +573,8 @@ namespace Spine { override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList 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. diff --git a/spine-csharp/src/Attachments/AttachmentType.cs b/spine-csharp/src/Attachments/AttachmentType.cs index c1187915c..80543490f 100644 --- a/spine-csharp/src/Attachments/AttachmentType.cs +++ b/spine-csharp/src/Attachments/AttachmentType.cs @@ -31,6 +31,6 @@ namespace Spine { public enum AttachmentType { - region, boundingbox, mesh, weightedmesh + region, boundingbox, mesh, weightedmesh, linkedmesh, weightedlinkedmesh } } diff --git a/spine-csharp/src/Attachments/IFfdAttachment.cs b/spine-csharp/src/Attachments/IFfdAttachment.cs new file mode 100644 index 000000000..8e234f3a2 --- /dev/null +++ b/spine-csharp/src/Attachments/IFfdAttachment.cs @@ -0,0 +1,5 @@ +namespace Spine { + public interface IFfdAttachment { + bool ApplyFFD (Attachment sourceAttachment); + } +} \ No newline at end of file diff --git a/spine-csharp/src/Attachments/MeshAttachment.cs b/spine-csharp/src/Attachments/MeshAttachment.cs index de4d569b8..3d0174862 100644 --- a/spine-csharp/src/Attachments/MeshAttachment.cs +++ b/spine-csharp/src/Attachments/MeshAttachment.cs @@ -33,11 +33,13 @@ using System; namespace Spine { /// Attachment that displays a texture region using a mesh. - 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); + } } } diff --git a/spine-csharp/src/Attachments/WeightedMeshAttachment.cs b/spine-csharp/src/Attachments/WeightedMeshAttachment.cs index a82567020..324db1d1c 100644 --- a/spine-csharp/src/Attachments/WeightedMeshAttachment.cs +++ b/spine-csharp/src/Attachments/WeightedMeshAttachment.cs @@ -34,12 +34,14 @@ using System.Collections.Generic; namespace Spine { /// Attachment that displays a texture region using a mesh which can be deformed by bones. - 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); + } } } diff --git a/spine-csharp/src/Bone.cs b/spine-csharp/src/Bone.cs index 9ab20166c..d1aeee7e1 100644 --- a/spine-csharp/src/Bone.cs +++ b/spine-csharp/src/Bone.cs @@ -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 () { diff --git a/spine-csharp/src/IkConstraint.cs b/spine-csharp/src/IkConstraint.cs index 4faf851f9..c5212850c 100644 --- a/spine-csharp/src/IkConstraint.cs +++ b/spine-csharp/src/IkConstraint.cs @@ -98,7 +98,7 @@ namespace Spine { /// A direct descendant of the parent bone. 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); } } } diff --git a/spine-csharp/src/SkeletonBinary.cs b/spine-csharp/src/SkeletonBinary.cs index 17cf6f7e9..f63367aaa 100644 --- a/spine-csharp/src/SkeletonBinary.cs +++ b/spine-csharp/src/SkeletonBinary.cs @@ -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 linkedMeshes = new List(); + 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(uvs.Length * 3 * 3); var bones = new List(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; } } } diff --git a/spine-csharp/src/SkeletonJson.cs b/spine-csharp/src/SkeletonJson.cs index 06af98882..8c22b8d6e 100644 --- a/spine-csharp/src/SkeletonJson.cs +++ b/spine-csharp/src/SkeletonJson.cs @@ -46,6 +46,7 @@ namespace Spine { public class SkeletonJson { private AttachmentLoader attachmentLoader; public float Scale { get; set; } + private List linkedMeshes = new List(); public SkeletonJson (params Atlas[] atlasArray) : this(new AtlasAttachmentLoader(atlasArray)) { @@ -208,7 +209,7 @@ namespace Spine { foreach (KeyValuePair slotEntry in (Dictionary)entry.Value) { int slotIndex = skeletonData.FindSlotIndex(slotEntry.Key); foreach (KeyValuePair attachmentEntry in ((Dictionary)slotEntry.Value)) { - Attachment attachment = ReadAttachment(skin, attachmentEntry.Key, (Dictionary)attachmentEntry.Value); + Attachment attachment = ReadAttachment(skin, slotIndex, attachmentEntry.Key, (Dictionary)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 entry in (Dictionary)root["events"]) { @@ -245,7 +265,7 @@ namespace Spine { return skeletonData; } - private Attachment ReadAttachment (Skin skin, String name, Dictionary map) { + private Attachment ReadAttachment (Skin skin, int slotIndex, String name, Dictionary 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(uvs.Length * 3 * 3); - var bones = new List(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(uvs.Length * 3 * 3); + var bones = new List(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; + } + } } }