[csharp][unity] Port of commit 3789ec0: Make sequence rendering thread-safe. See #2989.

This commit is contained in:
Harald Csaszar 2026-03-10 15:23:41 +01:00
parent 25942f2519
commit 73a50ef12d
23 changed files with 403 additions and 439 deletions

View File

@ -331,6 +331,7 @@
- Attachment `ComputeWorldVertices()` methods now take an additional `skeleton` parameter
- Renamed timeline constraint index methods to use unified `ConstraintIndex` property
- Reorganized timeline class hierarchy with new base classes
- Removed `AtlasAttachmentLoader` method `AtlasRegion FindRegion(string name)` from public interface. Added `protected AtlasRegion FindRegion(string name, string path)` instead which may be overridden instead when deriving your own subclass.
### Unity

View File

@ -1881,12 +1881,12 @@ namespace Spine {
private const int MODE = 1, DELAY = 2;
readonly int slotIndex;
readonly IHasTextureRegion attachment;
readonly IHasSequence attachment;
public SequenceTimeline (int frameCount, int slotIndex, Attachment attachment)
: base(frameCount, (int)Property.Sequence + "|" + slotIndex + "|" + ((IHasTextureRegion)attachment).Sequence.Id) {
: base(frameCount, (int)Property.Sequence + "|" + slotIndex + "|" + ((IHasSequence)attachment).Sequence.Id) {
this.slotIndex = slotIndex;
this.attachment = (IHasTextureRegion)attachment;
this.attachment = (IHasSequence)attachment;
}
public override int FrameEntries {
@ -1927,8 +1927,6 @@ namespace Spine {
if ((vertexAttachment == null)
|| vertexAttachment.TimelineAttachment != attachment) return;
}
Sequence sequence = ((IHasTextureRegion)slotAttachment).Sequence;
if (sequence == null) return;
if (direction == MixDirection.Out) {
if (blend == MixBlend.Setup) pose.SequenceIndex = -1;
@ -1946,7 +1944,7 @@ namespace Spine {
int modeAndIndex = (int)frames[i + MODE];
float delay = frames[i + DELAY];
int index = modeAndIndex >> 4, count = sequence.Regions.Length;
int index = modeAndIndex >> 4, count = (((IHasSequence)slotAttachment).Sequence).Regions.Length;
SequenceMode mode = (SequenceMode)(modeAndIndex & 0xf);
if (mode != SequenceMode.Hold) {
index += (int)((time - before) / delay + 0.0001f);

View File

@ -49,40 +49,32 @@ namespace Spine {
this.allowMissingRegions = allowMissingRegions;
}
private void LoadSequence (string name, string basePath, Sequence sequence) {
protected void FindRegions (string name, string basePath, Sequence sequence) {
TextureRegion[] regions = sequence.Regions;
for (int i = 0, n = regions.Length; i < n; i++) {
string path = sequence.GetPath(basePath, i);
regions[i] = FindRegion(path);
if (regions[i] == null && !allowMissingRegions)
throw new ArgumentException(string.Format("Region not found in atlas: {0} (region attachment: {1})", path, name));
regions[i] = FindRegion(name, sequence.GetPath(basePath, i));
}
}
protected AtlasRegion FindRegion (string name, string path) {
for (int i = 0; i < atlasArray.Length; i++) {
AtlasRegion region = atlasArray[i].FindRegion(path);
if (region != null)
return region;
}
if (!allowMissingRegions)
throw new ArgumentException(string.Format("Region not found in atlas: {0} (attachment: {1})", path, name));
return null;
}
public RegionAttachment NewRegionAttachment (Skin skin, string name, string path, Sequence sequence) {
var attachment = new RegionAttachment(name);
if (sequence != null)
LoadSequence(name, path, sequence);
else {
AtlasRegion region = FindRegion(path);
if (region == null && !allowMissingRegions)
throw new ArgumentException(string.Format("Region not found in atlas: {0} (region attachment: {1})", path, name));
attachment.Region = region;
}
return attachment;
FindRegions(name, path, sequence);
return new RegionAttachment(name, sequence);
}
public MeshAttachment NewMeshAttachment (Skin skin, string name, string path, Sequence sequence) {
var attachment = new MeshAttachment(name);
if (sequence != null)
LoadSequence(name, path, sequence);
else {
AtlasRegion region = FindRegion(path);
if (region == null && !allowMissingRegions)
throw new ArgumentException(string.Format("Region not found in atlas: {0} (region attachment: {1})", path, name));
attachment.Region = region;
}
return attachment;
FindRegions(name, path, sequence);
return new MeshAttachment(name, sequence);
}
public BoundingBoxAttachment NewBoundingBoxAttachment (Skin skin, string name) {
@ -100,17 +92,5 @@ namespace Spine {
public ClippingAttachment NewClippingAttachment (Skin skin, string name) {
return new ClippingAttachment(name);
}
public AtlasRegion FindRegion (string name) {
AtlasRegion region;
for (int i = 0; i < atlasArray.Length; i++) {
region = atlasArray[i].FindRegion(name);
if (region != null)
return region;
}
return null;
}
}
}

View File

@ -36,25 +36,12 @@ namespace Spine {
using Color32F = UnityEngine.Color;
#endif
public interface IHasTextureRegion {
/// <summary>The name used to find the <see cref="Region"/></summary>
public interface IHasSequence {
string Path { get; set; }
/// <summary>
/// Sets the region used to draw the attachment. After setting the region or if the region's properties are changed,
/// <see cref="UpdateRegion()"/> must be called.
/// </summary>
TextureRegion Region { get; set; }
/// <summary>
/// Updates any values the attachment calculates using the <see cref="Region"/>. Must be called after setting the
/// <see cref="Region"/> or if the region's properties are changed.
/// </summary>
void UpdateRegion ();
Color32F GetColor ();
void SetColor (Color32F color);
void SetColor (float r, float g, float b, float a);
Sequence Sequence { get; set; }
Sequence Sequence { get; }
void UpdateSequence ();
}
}

View File

@ -39,32 +39,25 @@ namespace Spine {
#endif
/// <summary>Attachment that displays a texture region using a mesh.</summary>
public class MeshAttachment : VertexAttachment, IHasTextureRegion {
internal TextureRegion region;
internal string path;
internal float[] regionUVs, uvs;
public class MeshAttachment : VertexAttachment, IHasSequence {
internal readonly Sequence sequence;
internal float[] regionUVs;
internal int[] triangles;
internal int hullLength;
internal string path;
// Color is a struct, set to protected to prevent
// Color color = slot.color; color.a = 0.5;
// modifying just a copy of the struct instead of the original
// object as in reference implementation.
protected Color32F color = new Color32F(1, 1, 1, 1);
internal int hullLength;
private MeshAttachment parentMesh;
private Sequence sequence;
public TextureRegion Region {
get { return region; }
set {
if (value == null) throw new ArgumentNullException("region", "region cannot be null.");
region = value;
}
}
public int HullLength { get { return hullLength; } set { hullLength = value; } }
/// <summary>The UV pair for each vertex, normalized within the texture region.</summary>
public float[] RegionUVs { get { return regionUVs; } set { regionUVs = value; } }
/// <summary>The UV pair for each vertex, normalized within the entire texture.
/// <seealso cref="MeshAttachment.UpdateRegion"/></summary>
public float[] UVs { get { return uvs; } set { uvs = value; } }
public int[] Triangles { get { return triangles; } set { triangles = value; } }
public Color32F GetColor () {
@ -80,7 +73,7 @@ namespace Spine {
}
public string Path { get { return path; } set { path = value; } }
public Sequence Sequence { get { return sequence; } set { sequence = value; } }
public Sequence Sequence { get { return sequence; } }
public MeshAttachment ParentMesh {
get { return parentMesh; }
@ -101,12 +94,18 @@ namespace Spine {
}
// Nonessential.
/// <summary>
/// Vertex index pairs describing edges for controlling triangulation, or be null if nonessential data was not exported. Mesh
/// triangles never cross edges. Triangulation is not performed at runtime.
/// </summary>
public int[] Edges { get; set; }
public float Width { get; set; }
public float Height { get; set; }
public MeshAttachment (string name)
public MeshAttachment (string name, Sequence sequence)
: base(name) {
if (sequence == null) throw new ArgumentException("sequence cannot be null.", "sequence");
this.sequence = sequence;
}
/// <summary>Copy constructor. Use <see cref="NewLinkedMesh"/> if the other mesh is a linked mesh.</summary>
@ -115,21 +114,17 @@ namespace Spine {
if (parentMesh != null) throw new ArgumentException("Use newLinkedMesh to copy a linked mesh.");
region = other.region;
path = other.path;
color = other.color;
regionUVs = new float[other.regionUVs.Length];
Array.Copy(other.regionUVs, 0, regionUVs, 0, regionUVs.Length);
uvs = new float[other.uvs.Length];
Array.Copy(other.uvs, 0, uvs, 0, uvs.Length);
triangles = new int[other.triangles.Length];
Array.Copy(other.triangles, 0, triangles, 0, triangles.Length);
hullLength = other.hullLength;
sequence = other.sequence == null ? null : new Sequence(other.sequence);
sequence = new Sequence(other.sequence);
// Nonessential.
if (other.Edges != null) {
@ -140,58 +135,81 @@ namespace Spine {
Height = other.Height;
}
public void UpdateSequence () {
sequence.Update(this);
}
public void UpdateRegion () {
float[] regionUVs = this.regionUVs;
if (this.uvs == null || this.uvs.Length != regionUVs.Length) this.uvs = new float[regionUVs.Length];
float[] uvs = this.uvs;
/// <summary>Returns a new mesh with this mesh set as the <see cref="ParentMesh"/>.
public MeshAttachment NewLinkedMesh () {
var mesh = new MeshAttachment(Name, new Sequence(sequence));
mesh.timelineAttachment = timelineAttachment;
mesh.path = path;
mesh.color = color;
mesh.ParentMesh = parentMesh != null ? parentMesh : this;
mesh.UpdateSequence();
return mesh;
}
public override Attachment Copy () {
return parentMesh != null ? NewLinkedMesh() : new MeshAttachment(this);
}
/// <summary>
/// Computes <see cref="Sequence.GetUVs(int)">UVs</see> for a mesh attachment.
/// </summary>
/// <param name="uvs">Output array for the computed UVs, same length as regionUVs.</param>
internal static void ComputeUVs (TextureRegion region, float[] regionUVs, float[] uvs) {
int n = uvs.Length;
float u, v, width, height;
if (region is AtlasRegion) {
u = this.region.u;
v = this.region.v;
AtlasRegion region = (AtlasRegion)this.region;
// Note: difference from reference implementation.
// Covers rotation since region.width and height are already setup accordingly.
float textureWidth = this.region.width / (region.u2 - region.u);
float textureHeight = this.region.height / (region.v2 - region.v);
switch (region.degrees) {
case 90:
u -= (region.originalHeight - region.offsetY - region.packedWidth) / textureWidth;
v -= (region.originalWidth - region.offsetX - region.packedHeight) / textureHeight;
width = region.originalHeight / textureWidth;
height = region.originalWidth / textureHeight;
AtlasRegion r = region as AtlasRegion;
if (r != null) {
u = r.u;
v = r.v;
float textureWidth = region.width / (region.u2 - region.u);
float textureHeight = region.height / (region.v2 - region.v);
switch (r.degrees) {
case 90: {
u -= (r.originalHeight - r.offsetY - r.packedWidth) / textureWidth;
v -= (r.originalWidth - r.offsetX - r.packedHeight) / textureHeight;
width = r.originalHeight / textureWidth;
height = r.originalWidth / textureHeight;
for (int i = 0; i < n; i += 2) {
uvs[i] = u + regionUVs[i + 1] * width;
uvs[i + 1] = v + (1 - regionUVs[i]) * height;
}
return;
case 180:
u -= (region.originalWidth - region.offsetX - region.packedWidth) / textureWidth;
v -= region.offsetY / textureHeight;
width = region.originalWidth / textureWidth;
height = region.originalHeight / textureHeight;
}
case 180: {
u -= (r.originalWidth - r.offsetX - r.packedWidth) / textureWidth;
v -= r.offsetY / textureHeight;
width = r.originalWidth / textureWidth;
height = r.originalHeight / textureHeight;
for (int i = 0; i < n; i += 2) {
uvs[i] = u + (1 - regionUVs[i]) * width;
uvs[i + 1] = v + (1 - regionUVs[i + 1]) * height;
}
return;
case 270:
u -= region.offsetY / textureWidth;
v -= region.offsetX / textureHeight;
width = region.originalHeight / textureWidth;
height = region.originalWidth / textureHeight;
}
case 270: {
u -= r.offsetY / textureWidth;
v -= r.offsetX / textureHeight;
width = r.originalHeight / textureWidth;
height = r.originalWidth / textureHeight;
for (int i = 0; i < n; i += 2) {
uvs[i] = u + (1 - regionUVs[i + 1]) * width;
uvs[i + 1] = v + regionUVs[i] * height;
}
return;
}
u -= region.offsetX / textureWidth;
v -= (region.originalHeight - region.offsetY - region.packedHeight) / textureHeight;
width = region.originalWidth / textureWidth;
height = region.originalHeight / textureHeight;
default: {
u -= r.offsetX / textureWidth;
v -= (r.originalHeight - r.offsetY - r.packedHeight) / textureHeight;
width = r.originalWidth / textureWidth;
height = r.originalHeight / textureHeight;
break;
}
}
} else if (region == null) {
u = v = 0;
width = height = 1;
@ -206,29 +224,5 @@ namespace Spine {
uvs[i + 1] = v + regionUVs[i + 1] * height;
}
}
/// <summary>If the attachment has a <see cref="Sequence"/>, the region may be changed.</summary>
override public void ComputeWorldVertices (Skeleton skeleton, Slot slot, int start, int count, float[] worldVertices, int offset,
int stride = 2) {
if (sequence != null) sequence.Apply(slot.AppliedPose, this);
base.ComputeWorldVertices(skeleton, slot, start, count, worldVertices, offset, stride);
}
/// <summary>Returns a new mesh with this mesh set as the <see cref="ParentMesh"/>.
public MeshAttachment NewLinkedMesh () {
var mesh = new MeshAttachment(Name);
mesh.timelineAttachment = timelineAttachment;
mesh.region = region;
mesh.path = path;
mesh.color = color;
mesh.ParentMesh = parentMesh != null ? parentMesh : this;
if (mesh.Region != null) mesh.UpdateRegion();
return mesh;
}
public override Attachment Copy () {
return parentMesh != null ? NewLinkedMesh() : new MeshAttachment(this);
}
}
}

View File

@ -39,21 +39,19 @@ namespace Spine {
#endif
/// <summary>Attachment that displays a texture region.</summary>
public class RegionAttachment : Attachment, IHasTextureRegion {
public class RegionAttachment : Attachment, IHasSequence {
public const int BLX = 0, BLY = 1;
public const int ULX = 2, ULY = 3;
public const int URX = 4, URY = 5;
public const int BRX = 6, BRY = 7;
internal TextureRegion region;
internal readonly Sequence sequence;
internal float x, y, rotation, scaleX = 1, scaleY = 1, width, height;
internal float[] offset = new float[8], uvs = new float[8];
// Color is a struct, set to protected to prevent
// Color color = slot.color; color.a = 0.5;
// modifying just a copy of the struct instead of the original
// object as in reference implementation.
protected Color32F color = new Color32F(1, 1, 1, 1);
internal Sequence sequence;
public float X { get { return x; } set { x = value; } }
public float Y { get { return y; } set { y = value; } }
@ -76,22 +74,17 @@ namespace Spine {
}
public string Path { get; set; }
public TextureRegion Region { get { return region; } set { region = value; } }
public Sequence Sequence { get { return sequence; } }
/// <summary>For each of the 4 vertices, a pair of <c>x,y</c> values that is the local position of the vertex.</summary>
/// <seealso cref="UpdateRegion"/>
public float[] Offset { get { return offset; } }
public float[] UVs { get { return uvs; } }
public Sequence Sequence { get { return sequence; } set { sequence = value; } }
public RegionAttachment (string name)
public RegionAttachment (string name, Sequence sequence)
: base(name) {
if (sequence == null) throw new ArgumentException("sequence cannot be null.", "sequence");
this.sequence = sequence;
}
/// <summary>Copy constructor.</summary>
public RegionAttachment (RegionAttachment other)
: base(other) {
region = other.region;
Path = other.Path;
x = other.x;
y = other.y;
@ -100,41 +93,94 @@ namespace Spine {
rotation = other.rotation;
width = other.width;
height = other.height;
Array.Copy(other.uvs, 0, uvs, 0, 8);
Array.Copy(other.offset, 0, offset, 0, 8);
color = other.color;
sequence = other.sequence == null ? null : new Sequence(other.sequence);
sequence = new Sequence(other.sequence);
}
/// <summary>Calculates the <see cref="Offset"/> and <see cref="UVs"/> using the region and the attachment's transform. Must be called if the
/// region, the region's properties, or the transform are changed.</summary>
public void UpdateRegion () {
float width = Width, height = Height;
float localX2 = width / 2;
float localY2 = height / 2;
float localX = -localX2;
float localY = -localY2;
/// <summary><para>
/// Transforms the attachment's four vertices to world coordinates. If the attachment has a <see cref="Sequence"/> the region may
/// be changed.</para>
/// <para>
/// See <see href='https://esotericsoftware.com/spine-runtime-skeletons#World-transforms'>World transforms</a> in the Spine
/// Runtimes Guide.</para></summary>
/// <param name="worldVertices">The output world vertices. Must have a length greater than or equal to offset + 8.</param>
/// <param name="vertexOffsets">The vertex <see cref="Sequence.GetOffsets(int)">offsets</see>.</param>
/// <param name="offset">The worldVertices index to begin writing values.</param>
/// <param name="stride">The number of worldVertices entries between the value pairs written.</param>
public void ComputeWorldVertices (Slot slot, float[] vertexOffsets, float[] worldVertices, int offset, int stride = 2) {
BonePose bone = slot.Bone.AppliedPose;
float bwx = bone.worldX, bwy = bone.worldY;
float a = bone.a, b = bone.b, c = bone.c, d = bone.d;
// Vertex order is different from RegionAttachment.java
float offsetX = vertexOffsets[BRX]; // 0
float offsetY = vertexOffsets[BRY]; // 1
worldVertices[offset] = offsetX * a + offsetY * b + bwx; // bl
worldVertices[offset + 1] = offsetX * c + offsetY * d + bwy;
offset += stride;
offsetX = vertexOffsets[BLX]; // 2
offsetY = vertexOffsets[BLY]; // 3
worldVertices[offset] = offsetX * a + offsetY * b + bwx; // ul
worldVertices[offset + 1] = offsetX * c + offsetY * d + bwy;
offset += stride;
offsetX = vertexOffsets[ULX]; // 4
offsetY = vertexOffsets[ULY]; // 5
worldVertices[offset] = offsetX * a + offsetY * b + bwx; // ur
worldVertices[offset + 1] = offsetX * c + offsetY * d + bwy;
offset += stride;
offsetX = vertexOffsets[URX]; // 6
offsetY = vertexOffsets[URY]; // 7
worldVertices[offset] = offsetX * a + offsetY * b + bwx; // br
worldVertices[offset + 1] = offsetX * c + offsetY * d + bwy;
//offset += stride;
}
/// <summary>
/// Returns the vertex <see cref="Sequence.GetOffsets(int)">offsets</see> for the specified slot pose.
/// </summary>
public float[] GetOffsets (SlotPose pose) {
return sequence.GetOffsets(sequence.ResolveIndex(pose));
}
public void UpdateSequence () {
sequence.Update(this);
}
public override Attachment Copy () {
return new RegionAttachment(this);
}
/// <summary>
/// Computes <see cref="Sequence.GetUVs(int)">UVs</see> and <see cref="Sequence.GetOffsets(int)">offsets</see> for a region attachment.
/// </summary>
/// <param name="uvs">Output array for the computed UVs, length of 8.</param>
/// <param name="offset">Output array for the computed vertex offsets, length of 8.</param>
internal static void ComputeUVs (TextureRegion region, float x, float y, float scaleX, float scaleY, float rotation, float width,
float height, float[] offset, float[] uvs) {
float localX2 = width / 2, localY2 = height / 2;
float localX = -localX2, localY = -localY2;
bool rotated = false;
if (region is AtlasRegion) {
AtlasRegion region = (AtlasRegion)this.region;
localX += region.offsetX / region.originalWidth * width;
localY += region.offsetY / region.originalHeight * height;
if (region.degrees == 90) {
AtlasRegion r = region as AtlasRegion;
if (r != null) {
localX += r.offsetX / r.originalWidth * width;
localY += r.offsetY / r.originalHeight * height;
if (r.degrees == 90) {
rotated = true;
localX2 -= (region.originalWidth - region.offsetX - region.packedHeight) / region.originalWidth * width;
localY2 -= (region.originalHeight - region.offsetY - region.packedWidth) / region.originalHeight * height;
localX2 -= (r.originalWidth - r.offsetX - r.packedHeight) / r.originalWidth * width;
localY2 -= (r.originalHeight - r.offsetY - r.packedWidth) / r.originalHeight * height;
} else {
localX2 -= (region.originalWidth - region.offsetX - region.packedWidth) / region.originalWidth * width;
localY2 -= (region.originalHeight - region.offsetY - region.packedHeight) / region.originalHeight * height;
localX2 -= (r.originalWidth - r.offsetX - r.packedWidth) / r.originalWidth * width;
localY2 -= (r.originalHeight - r.offsetY - r.packedHeight) / r.originalHeight * height;
}
}
float scaleX = ScaleX, scaleY = ScaleY;
localX *= scaleX;
localY *= scaleY;
localX2 *= scaleX;
localY2 *= scaleY;
float r = Rotation * MathUtils.DegRad, cos = (float)Math.Cos(r), sin = (float)Math.Sin(r);
float x = X, y = Y;
float rot = rotation * MathUtils.DegRad, cos = (float)Math.Cos(rot), sin = (float)Math.Sin(rot);
float localXCos = localX * cos + x;
float localXSin = localX * sin;
float localYCos = localY * cos + y;
@ -143,7 +189,6 @@ namespace Spine {
float localX2Sin = localX2 * sin;
float localY2Cos = localY2 * cos + y;
float localY2Sin = localY2 * sin;
float[] offset = this.offset;
offset[BLX] = localXCos - localYSin;
offset[BLY] = localYCos + localXSin;
offset[ULX] = localXCos - localY2Sin;
@ -152,8 +197,6 @@ namespace Spine {
offset[URY] = localY2Cos + localX2Sin;
offset[BRX] = localX2Cos - localYSin;
offset[BRY] = localYCos + localX2Sin;
float[] uvs = this.uvs;
if (region == null) {
uvs[BLX] = 0;
uvs[BLY] = 0;
@ -163,71 +206,23 @@ namespace Spine {
uvs[URY] = 1;
uvs[BRX] = 1;
uvs[BRY] = 0;
} else if (rotated) {
uvs[BLX] = region.u2;
uvs[BLY] = region.v;
uvs[ULX] = region.u2;
uvs[ULY] = region.v2;
uvs[URX] = region.u;
uvs[URY] = region.v2;
uvs[BRX] = region.u;
uvs[BRY] = region.v;
} else {
uvs[BLX] = region.u2;
uvs[BLY] = region.v2;
uvs[ULX] = region.u;
uvs[ULY] = region.v2;
uvs[URX] = region.u;
uvs[URY] = region.v;
uvs[BRX] = region.u2;
uvs[BRY] = region.v;
if (rotated) {
uvs[BLY] = region.v;
uvs[ULX] = region.u2;
uvs[URY] = region.v2;
uvs[BRX] = region.u;
} else {
uvs[BLY] = region.v2;
uvs[ULX] = region.u;
uvs[URY] = region.v;
uvs[BRX] = region.u2;
}
}
}
/// <summary>
/// Transforms the attachment's four vertices to world coordinates. If the attachment has a <see cref="Sequence"/> the region may
/// be changed.</summary>
/// <param name="bone">The parent bone.</param>
/// <param name="worldVertices">The output world vertices. Must have a length greater than or equal to offset + 8.</param>
/// <param name="offset">The worldVertices index to begin writing values.</param>
/// <param name="stride">The number of worldVertices entries between the value pairs written.</param>
public void ComputeWorldVertices (Slot slot, float[] worldVertices, int offset, int stride = 2) {
if (sequence != null) sequence.Apply(slot.AppliedPose, this);
float[] vertexOffset = this.offset;
BonePose bone = slot.Bone.AppliedPose;
float bwx = bone.worldX, bwy = bone.worldY;
float a = bone.a, b = bone.b, c = bone.c, d = bone.d;
float offsetX, offsetY;
// Vertex order is different from RegionAttachment.java
offsetX = vertexOffset[BRX]; // 0
offsetY = vertexOffset[BRY]; // 1
worldVertices[offset] = offsetX * a + offsetY * b + bwx; // bl
worldVertices[offset + 1] = offsetX * c + offsetY * d + bwy;
offset += stride;
offsetX = vertexOffset[BLX]; // 2
offsetY = vertexOffset[BLY]; // 3
worldVertices[offset] = offsetX * a + offsetY * b + bwx; // ul
worldVertices[offset + 1] = offsetX * c + offsetY * d + bwy;
offset += stride;
offsetX = vertexOffset[ULX]; // 4
offsetY = vertexOffset[ULY]; // 5
worldVertices[offset] = offsetX * a + offsetY * b + bwx; // ur
worldVertices[offset + 1] = offsetX * c + offsetY * d + bwy;
offset += stride;
offsetX = vertexOffset[URX]; // 6
offsetY = vertexOffset[URY]; // 7
worldVertices[offset] = offsetX * a + offsetY * b + bwx; // br
worldVertices[offset + 1] = offsetX * c + offsetY * d + bwy;
//offset += stride;
}
public override Attachment Copy () {
return new RegionAttachment(this);
}
}
}

View File

@ -31,12 +31,18 @@ using System;
using System.Text;
namespace Spine {
/// <summary>
/// Holds texture regions, UVs, and vertex offsets for rendering a region or mesh attachment. <see cref="Regions"/> must
/// be populated and <see cref="Update(IHasSequence)"/> called before use.
/// </summary>
public class Sequence {
static int nextID = 0;
static readonly Object nextIdLock = new Object();
internal readonly int id;
internal readonly TextureRegion[] regions;
internal readonly bool pathSuffix;
internal float[][] uvs, offsets;
internal int start, digits, setupIndex;
public int Start { get { return start; } set { start = value; } }
@ -44,14 +50,16 @@ namespace Spine {
/// <summary>The index of the region to show for the setup pose.</summary>
public int SetupIndex { get { return setupIndex; } set { setupIndex = value; } }
public TextureRegion[] Regions { get { return regions; } }
public bool PathSuffix { get { return pathSuffix; } }
/// <summary>Returns a unique ID for this attachment.</summary>
public int Id { get { return id; } }
public Sequence (int count) {
public Sequence (int count, bool pathSuffix) {
lock (Sequence.nextIdLock) {
id = Sequence.nextID++;
}
regions = new TextureRegion[count];
this.pathSuffix = pathSuffix;
}
/// <summary>Copy constructor.</summary>
@ -59,26 +67,82 @@ namespace Spine {
lock (Sequence.nextIdLock) {
id = Sequence.nextID++;
}
regions = new TextureRegion[other.regions.Length];
Array.Copy(other.regions, 0, regions, 0, regions.Length);
int regionCount = other.regions.Length;
regions = new TextureRegion[regionCount];
Array.Copy(other.regions, 0, regions, 0, regionCount);
start = other.start;
digits = other.digits;
setupIndex = other.setupIndex;
}
pathSuffix = other.pathSuffix;
public void Apply (SlotPose slot, IHasTextureRegion attachment) {
int index = slot.SequenceIndex;
if (index == -1) index = setupIndex;
if (index >= regions.Length) index = regions.Length - 1;
TextureRegion region = regions[index];
if (attachment.Region != region) {
attachment.Region = region;
attachment.UpdateRegion();
if (other.uvs != null) {
int length = other.uvs[0].Length;
uvs = new float[regionCount][];
for (int i = 0; i < regionCount; i++) {
uvs[i] = new float[length];
Array.Copy(other.uvs[i], 0, uvs[i], 0, length);
}
}
if (other.offsets != null) {
offsets = new float[regionCount][];
for (int i = 0; i < regionCount; i++) {
offsets[i] = new float[8];
Array.Copy(other.offsets[i], 0, offsets[i], 0, 8);
}
}
}
public void Update (IHasSequence attachment) {
int regionCount = regions.Length;
RegionAttachment region = attachment as RegionAttachment;
if (region != null) {
uvs = new float[regionCount][];
offsets = new float[regionCount][];
for (int i = 0; i < regionCount; i++) {
uvs[i] = new float[8];
offsets[i] = new float[8];
RegionAttachment.ComputeUVs(regions[i], region.x, region.y, region.scaleX, region.scaleY, region.rotation,
region.width, region.height, offsets[i], uvs[i]);
}
} else {
MeshAttachment mesh = attachment as MeshAttachment;
if (mesh != null) {
float[] regionUVs = mesh.regionUVs;
uvs = new float[regionCount][];
offsets = null;
for (int i = 0; i < regionCount; i++) {
uvs[i] = new float[regionUVs.Length];
MeshAttachment.ComputeUVs(regions[i], regionUVs, uvs[i]);
}
}
}
}
public int ResolveIndex (SlotPose pose) {
int index = pose.SequenceIndex;
if (index == -1) index = setupIndex;
if (index >= regions.Length) index = regions.Length - 1;
return index;
}
public TextureRegion GetRegion (int index) {
return regions[index];
}
public float[] GetUVs (int index) {
return uvs[index];
}
/// <summary>
/// Returns vertex offsets from the center of a <see cref="RegionAttachment"/>. Invalid to call for a <see cref="MeshAttachment"/>.
/// </summary>
public float[] GetOffsets (int index) {
return offsets[index];
}
public string GetPath (string basePath, int index) {
if (!pathSuffix) return basePath;
var buffer = new StringBuilder(basePath.Length + digits);
buffer.Append(basePath);
string frame = (start + index).ToString();

View File

@ -459,7 +459,7 @@ namespace Spine {
verticesLength = 8;
vertices = temp;
if (vertices.Length < 8) vertices = temp = new float[8];
region.ComputeWorldVertices(slot, vertices, 0, 2);
region.ComputeWorldVertices(slot, region.GetOffsets(slot.applied), vertices, 0, 2);
triangles = quadTriangles;
} else {
MeshAttachment mesh = attachment as MeshAttachment;

View File

@ -461,7 +461,7 @@ namespace Spine {
if (parent == null) throw new Exception("Parent mesh not found: " + linkedMesh.parent);
linkedMesh.mesh.TimelineAttachment = linkedMesh.inheritTimelines ? (VertexAttachment)parent : linkedMesh.mesh;
linkedMesh.mesh.ParentMesh = (MeshAttachment)parent;
if (linkedMesh.mesh.Region != null) linkedMesh.mesh.UpdateRegion();
linkedMesh.mesh.UpdateSequence();
}
linkedMeshes.Clear();
@ -546,7 +546,7 @@ namespace Spine {
case AttachmentType.Region: {
string path = (flags & 16) != 0 ? input.ReadStringRef() : null;
uint color = (flags & 32) != 0 ? (uint)input.ReadInt() : 0xffffffff;
Sequence sequence = (flags & 64) != 0 ? ReadSequence(input) : null;
Sequence sequence = ReadSequence(input, (flags & 64) != 0);
float rotation = (flags & 128) != 0 ? input.ReadFloat() : 0;
float x = input.ReadFloat();
float y = input.ReadFloat();
@ -567,8 +567,7 @@ namespace Spine {
region.width = width * scale;
region.height = height * scale;
region.SetColor(color.RGBA8888ToColor());
region.sequence = sequence;
if (region.Region != null) region.UpdateRegion();
region.UpdateSequence();
return region;
}
case AttachmentType.Boundingbox: {
@ -586,7 +585,7 @@ namespace Spine {
case AttachmentType.Mesh: {
string path = (flags & 16) != 0 ? input.ReadStringRef() : name;
uint color = (flags & 32) != 0 ? (uint)input.ReadInt() : 0xffffffff;
Sequence sequence = (flags & 64) != 0 ? ReadSequence(input) : null;
Sequence sequence = ReadSequence(input, (flags & 64) != 0);
int hullLength = input.ReadInt(true);
Vertices vertices = ReadVertices(input, (flags & 128) != 0);
float[] uvs = ReadFloatArray(input, vertices.length, 1);
@ -604,25 +603,24 @@ namespace Spine {
if (mesh == null) return null;
mesh.Path = path;
mesh.SetColor(color.RGBA8888ToColor());
mesh.HullLength = hullLength << 1;
mesh.bones = vertices.bones;
mesh.vertices = vertices.vertices;
mesh.WorldVerticesLength = vertices.length;
mesh.triangles = triangles;
mesh.regionUVs = uvs;
if (mesh.Region != null) mesh.UpdateRegion();
mesh.HullLength = hullLength << 1;
mesh.Sequence = sequence;
mesh.triangles = triangles;
if (nonessential) {
mesh.Edges = edges;
mesh.Width = width * scale;
mesh.Height = height * scale;
}
mesh.UpdateSequence();
return mesh;
}
case AttachmentType.Linkedmesh: {
string path = (flags & 16) != 0 ? input.ReadStringRef() : name;
uint color = (flags & 32) != 0 ? (uint)input.ReadInt() : 0xffffffff;
Sequence sequence = (flags & 64) != 0 ? ReadSequence(input) : null;
Sequence sequence = ReadSequence(input, (flags & 64) != 0);
bool inheritTimelines = (flags & 128) != 0;
int skinIndex = input.ReadInt(true);
string parent = input.ReadStringRef();
@ -636,7 +634,6 @@ namespace Spine {
if (mesh == null) return null;
mesh.Path = path;
mesh.SetColor(color.RGBA8888ToColor());
mesh.Sequence = sequence;
if (nonessential) {
mesh.Width = width * scale;
mesh.Height = height * scale;
@ -696,8 +693,9 @@ namespace Spine {
return null;
}
private Sequence ReadSequence (SkeletonInput input) {
var sequence = new Sequence(input.ReadInt(true));
private Sequence ReadSequence (SkeletonInput input, bool hasPathSuffix) {
if (!hasPathSuffix) return new Sequence(1, false);
var sequence = new Sequence(input.ReadInt(true), true);
sequence.Start = input.ReadInt(true);
sequence.Digits = input.ReadInt(true);
sequence.SetupIndex = input.ReadInt(true);

View File

@ -494,7 +494,7 @@ namespace Spine {
if (parent == null) throw new Exception("Parent mesh not found: " + linkedMesh.parent);
linkedMesh.mesh.TimelineAttachment = linkedMesh.inheritTimelines ? (VertexAttachment)parent : linkedMesh.mesh;
linkedMesh.mesh.ParentMesh = (MeshAttachment)parent;
if (linkedMesh.mesh.Region != null) linkedMesh.mesh.UpdateRegion();
linkedMesh.mesh.UpdateSequence();
}
linkedMeshes.Clear();
@ -596,14 +596,13 @@ namespace Spine {
region.rotation = GetFloat(map, "rotation", 0);
region.width = GetFloat(map, "width") * scale;
region.height = GetFloat(map, "height") * scale;
region.sequence = sequence;
if (map.ContainsKey("color")) {
string color = (string)map["color"];
region.SetColor(ToColor32(color, 8));
}
if (region.Region != null) region.UpdateRegion();
region.UpdateSequence();
return region;
}
case AttachmentType.Boundingbox:
@ -628,7 +627,6 @@ namespace Spine {
mesh.Width = GetFloat(map, "width", 0) * scale;
mesh.Height = GetFloat(map, "height", 0) * scale;
mesh.Sequence = sequence;
string parent = GetString(map, "parent", null);
if (parent != null) {
@ -640,10 +638,10 @@ namespace Spine {
ReadVertices(map, mesh, uvs.Length);
mesh.triangles = GetIntArray(map, "triangles");
mesh.regionUVs = uvs;
if (mesh.Region != null) mesh.UpdateRegion();
if (map.ContainsKey("hull")) mesh.HullLength = GetInt(map, "hull", 0) << 1;
if (map.ContainsKey("edges")) mesh.Edges = GetIntArray(map, "edges");
mesh.UpdateSequence();
return mesh;
}
case AttachmentType.Path: {
@ -693,8 +691,8 @@ namespace Spine {
public static Sequence ReadSequence (object sequenceJson) {
Dictionary<string, object> map = sequenceJson as Dictionary<string, Object>;
if (map == null) return null;
var sequence = new Sequence(GetInt(map, "count"));
if (map == null) return new Sequence(1, false);
var sequence = new Sequence(GetInt(map, "count"), true);
sequence.start = GetInt(map, "start", 1);
sequence.digits = GetInt(map, "digits", 0);
sequence.setupIndex = GetInt(map, "setup", 0);

View File

@ -2,7 +2,7 @@
"name": "com.esotericsoftware.spine.spine-csharp",
"displayName": "spine-csharp Runtime",
"description": "This plugin provides the spine-csharp core runtime.",
"version": "4.3.12",
"version": "4.3.13",
"unity": "2018.3",
"author": {
"name": "Esoteric Software",

View File

@ -300,24 +300,14 @@ namespace Spine.Unity.Editor {
}
public RegionAttachment NewRegionAttachment (Skin skin, string name, string path, Sequence sequence) {
RegionAttachment regionAttachment = new RegionAttachment(name);
if (sequence != null)
LoadSequence(path, sequence);
else {
requirementList.Add(path);
AssignDummyRegion(regionAttachment);
}
RegionAttachment regionAttachment = new RegionAttachment(name, sequence);
LoadSequence(path, sequence);
return regionAttachment;
}
public MeshAttachment NewMeshAttachment (Skin skin, string name, string path, Sequence sequence) {
MeshAttachment meshAttachment = new MeshAttachment(name);
if (sequence != null)
LoadSequence(path, sequence);
else {
requirementList.Add(path);
AssignDummyRegion(meshAttachment);
}
MeshAttachment meshAttachment = new MeshAttachment(name, sequence);
LoadSequence(path, sequence);
return meshAttachment;
}
@ -344,10 +334,6 @@ namespace Spine.Unity.Editor {
requirementList.Add(path);
}
}
private static void AssignDummyRegion (IHasTextureRegion attachment) {
attachment.Region = new AtlasRegion();
}
}
#endregion

View File

@ -520,8 +520,10 @@ namespace Spine.Unity.Editor {
bone.AppliedPose.UpdateWorldTransform(skeleton);
float[] floatVerts = new float[8];
attachment.ComputeWorldVertices(slot, floatVerts, 0);
Vector2[] uvs = ExtractUV(attachment.UVs);
Sequence sequence = attachment.Sequence;
int sequenceIndex = 0;
attachment.ComputeWorldVertices(slot, sequence.GetOffsets(sequenceIndex), floatVerts, 0);
Vector2[] uvs = ExtractUV(sequence.GetUVs(sequenceIndex));
Vector3[] verts = ExtractVerts(floatVerts);
//unrotate verts now that they're centered
@ -559,7 +561,10 @@ namespace Spine.Unity.Editor {
float[] floatVerts = new float[attachment.WorldVerticesLength];
attachment.ComputeWorldVertices(skeleton, slot, floatVerts);
Vector2[] uvs = ExtractUV(attachment.UVs);
Sequence sequence = attachment.Sequence;
int sequenceIndex = 0;
Vector2[] uvs = ExtractUV(sequence.GetUVs(sequenceIndex));
Vector3[] verts = ExtractVerts(floatVerts);
int[] triangles = attachment.Triangles;
@ -624,7 +629,10 @@ namespace Spine.Unity.Editor {
float[] floatVerts = new float[attachment.WorldVerticesLength];
attachment.ComputeWorldVertices(skeleton, skeleton.Slots.Items[slotIndex], floatVerts);
Vector2[] uvs = ExtractUV(attachment.UVs);
Sequence sequence = attachment.Sequence;
int sequenceIndex = 0;
Vector2[] uvs = ExtractUV(sequence.GetUVs(sequenceIndex));
Vector3[] verts = ExtractVerts(floatVerts);
int[] triangles = attachment.Triangles;

View File

@ -88,7 +88,7 @@ namespace Spine.Unity {
skin.GetAttachments(slotIndex, skinEntries);
foreach (Skin.SkinEntry entry in skinEntries) {
if (entry.Attachment is IHasTextureRegion) {
if (entry.Attachment is IHasSequence) {
requiresBlendModeMaterials = true;
return true;
}
@ -165,22 +165,15 @@ namespace Spine.Unity {
skin.GetAttachments(slotIndex, skinEntries);
foreach (Skin.SkinEntry entry in skinEntries) {
IHasTextureRegion renderableAttachment = entry.Attachment as IHasTextureRegion;
IHasSequence renderableAttachment = entry.Attachment as IHasSequence;
if (renderableAttachment != null) {
AtlasRegion originalRegion = (AtlasRegion)renderableAttachment.Region;
if (originalRegion != null) {
anyCreationFailed |= createForRegionFunc(
ref replacementMaterials, ref anyReplacementMaterialsChanged,
originalRegion, materialTemplate, materialSuffix, skeletonDataAsset);
} else {
Sequence sequence = renderableAttachment.Sequence;
if (sequence != null && sequence.Regions != null) {
for (int i = 0, count = sequence.Regions.Length; i < count; ++i) {
originalRegion = (AtlasRegion)sequence.Regions[i];
anyCreationFailed |= createForRegionFunc(
ref replacementMaterials, ref anyReplacementMaterialsChanged,
originalRegion, materialTemplate, materialSuffix, skeletonDataAsset);
}
Sequence sequence = renderableAttachment.Sequence;
if (sequence != null && sequence.Regions != null) {
for (int i = 0, count = sequence.Regions.Length; i < count; ++i) {
AtlasRegion originalRegion = (AtlasRegion)sequence.Regions[i];
anyCreationFailed |= createForRegionFunc(
ref replacementMaterials, ref anyReplacementMaterialsChanged,
originalRegion, materialTemplate, materialSuffix, skeletonDataAsset);
}
}
}
@ -268,20 +261,12 @@ namespace Spine.Unity {
skin.GetAttachments(slotIndex, skinEntries);
foreach (Skin.SkinEntry entry in skinEntries) {
IHasTextureRegion renderableAttachment = entry.Attachment as IHasTextureRegion;
IHasSequence renderableAttachment = entry.Attachment as IHasSequence;
if (renderableAttachment != null) {
if (renderableAttachment.Sequence != null) {
TextureRegion[] regions = renderableAttachment.Sequence.Regions;
for (int i = 0; i < regions.Length; ++i) {
regions[i] = CloneAtlasRegionWithMaterial(
(AtlasRegion)regions[i], replacementMaterials);
}
if (regions.Length > 0) {
renderableAttachment.Region = regions[0];
}
} else if (renderableAttachment.Region != null) {
renderableAttachment.Region = CloneAtlasRegionWithMaterial(
(AtlasRegion)renderableAttachment.Region, replacementMaterials);
TextureRegion[] regions = renderableAttachment.Sequence.Regions;
for (int i = 0; i < regions.Length; ++i) {
regions[i] = CloneAtlasRegionWithMaterial(
(AtlasRegion)regions[i], replacementMaterials);
}
}
}

View File

@ -57,16 +57,12 @@ namespace Spine.Unity {
}
public RegionAttachment NewRegionAttachment (Skin skin, string name, string path, Sequence sequence) {
RegionAttachment attachment = new RegionAttachment(name) {
Region = EmptyRegion
};
RegionAttachment attachment = new RegionAttachment(name, new Sequence(1, false));
return attachment;
}
public MeshAttachment NewMeshAttachment (Skin skin, string name, string path, Sequence sequence) {
MeshAttachment attachment = new MeshAttachment(name) {
Region = EmptyRegion
};
MeshAttachment attachment = new MeshAttachment(name, new Sequence(1, false));
return attachment;
}

View File

@ -254,15 +254,17 @@ namespace Spine.Unity {
RegionAttachment regionAttachment = attachment as RegionAttachment;
if (regionAttachment != null) {
if (regionAttachment.Sequence != null) regionAttachment.Sequence.Apply(slot.AppliedPose, regionAttachment);
rendererObject = regionAttachment.Region;
Sequence sequence = regionAttachment.Sequence;
int sequenceIndex = sequence.ResolveIndex(slot.AppliedPose);
rendererObject = sequence.GetRegion(sequenceIndex);
attachmentVertexCount = 4;
attachmentTriangleCount = 6;
} else {
MeshAttachment meshAttachment = attachment as MeshAttachment;
if (meshAttachment != null) {
if (meshAttachment.Sequence != null) meshAttachment.Sequence.Apply(slot.AppliedPose, meshAttachment);
rendererObject = meshAttachment.Region;
Sequence sequence = meshAttachment.Sequence;
int sequenceIndex = sequence.ResolveIndex(slot.AppliedPose);
rendererObject = sequence.GetRegion(sequenceIndex);
attachmentVertexCount = meshAttachment.WorldVerticesLength >> 1;
attachmentTriangleCount = meshAttachment.Triangles.Length;
} else {
@ -323,10 +325,11 @@ namespace Spine.Unity {
#endif
) continue;
Attachment attachment = slot.AppliedPose.Attachment;
IHasTextureRegion rendererAttachment = attachment as IHasTextureRegion;
IHasSequence rendererAttachment = attachment as IHasSequence;
if (rendererAttachment != null) {
if (rendererAttachment.Sequence != null) rendererAttachment.Sequence.Apply(slot.AppliedPose, rendererAttachment);
AtlasRegion atlasRegion = (AtlasRegion)rendererAttachment.Region;
Sequence sequence = rendererAttachment.Sequence;
int sequenceIndex = sequence.ResolveIndex(slot.AppliedPose);
AtlasRegion atlasRegion = (AtlasRegion)sequence.GetRegion(sequenceIndex);
Material material = (Material)atlasRegion.page.rendererObject;
if (lastRendererMaterial != material) {
if (lastRendererMaterial != null)
@ -399,8 +402,9 @@ namespace Spine.Unity {
RegionAttachment regionAttachment = attachment as RegionAttachment;
if (regionAttachment != null) {
if (regionAttachment.Sequence != null) regionAttachment.Sequence.Apply(slot.AppliedPose, regionAttachment);
region = regionAttachment.Region;
Sequence sequence = regionAttachment.Sequence;
int sequenceIndex = sequence.ResolveIndex(slot.AppliedPose);
region = sequence.GetRegion(sequenceIndex);
#if SPINE_TRIANGLECHECK
attachmentVertexCount = 4;
attachmentTriangleCount = 6;
@ -408,8 +412,9 @@ namespace Spine.Unity {
} else {
MeshAttachment meshAttachment = attachment as MeshAttachment;
if (meshAttachment != null) {
if (meshAttachment.Sequence != null) meshAttachment.Sequence.Apply(slot.AppliedPose, meshAttachment);
region = meshAttachment.Region;
Sequence sequence = meshAttachment.Sequence;
int sequenceIndex = sequence.ResolveIndex(slot.AppliedPose);
region = sequence.GetRegion(sequenceIndex);
#if SPINE_TRIANGLECHECK
attachmentVertexCount = meshAttachment.WorldVerticesLength >> 1;
attachmentTriangleCount = meshAttachment.Triangles.Length;
@ -620,8 +625,10 @@ namespace Spine.Unity {
// Identify and prepare values.
RegionAttachment region = attachment as RegionAttachment;
if (region != null) {
region.ComputeWorldVertices(slot, workingVerts, 0);
uvs = region.UVs;
Sequence sequence = region.Sequence;
int sequenceIndex = sequence.ResolveIndex(slotPose);
region.ComputeWorldVertices(slot, sequence.GetOffsets(sequenceIndex), workingVerts, 0);
uvs = sequence.GetUVs(sequenceIndex);
attachmentTriangleIndices = regionTriangles;
regionC = region.GetColor();
attachmentVertexCount = 4;
@ -634,8 +641,10 @@ namespace Spine.Unity {
workingVerts = new float[meshVerticesLength];
this.tempVerts = workingVerts;
}
mesh.ComputeWorldVertices(skeleton, slot, 0, meshVerticesLength, workingVerts, 0); //meshAttachment.ComputeWorldVertices(slot, tempVerts);
uvs = mesh.UVs;
Sequence sequence = mesh.Sequence;
int sequenceIndex = sequence.ResolveIndex(slotPose);
mesh.ComputeWorldVertices(skeleton, slot, 0, meshVerticesLength, workingVerts, 0);
uvs = sequence.GetUVs(sequenceIndex);
attachmentTriangleIndices = mesh.Triangles;
regionC = mesh.GetColor();
attachmentVertexCount = meshVerticesLength >> 1; // meshVertexCount / 2;
@ -942,7 +951,10 @@ namespace Spine.Unity {
RegionAttachment regionAttachment = attachment as RegionAttachment;
if (regionAttachment != null) {
regionAttachment.ComputeWorldVertices(slot, tempVerts, 0);
Sequence sequence = regionAttachment.Sequence;
int sequenceIndex = sequence.ResolveIndex(slotPose);
regionAttachment.ComputeWorldVertices(slot, sequence.GetOffsets(sequenceIndex), tempVerts, 0);
Color regionC = regionAttachment.GetColor();
Color combinedC = skeletonC * slotC * regionC;
@ -978,7 +990,7 @@ namespace Spine.Unity {
cbi[vertexIndex] = color; cbi[vertexIndex + 1] = color; cbi[vertexIndex + 2] = color; cbi[vertexIndex + 3] = color;
float[] regionUVs = regionAttachment.UVs;
float[] regionUVs = sequence.GetUVs(sequenceIndex);
ubi[vertexIndex] = new Vector2(regionUVs[RegionAttachment.BLX], regionUVs[RegionAttachment.BLY]);
ubi[vertexIndex + 1] = new Vector2(regionUVs[RegionAttachment.BRX], regionUVs[RegionAttachment.BRY]);
ubi[vertexIndex + 2] = new Vector2(regionUVs[RegionAttachment.ULX], regionUVs[RegionAttachment.ULY]);
@ -1032,7 +1044,9 @@ namespace Spine.Unity {
color.b = (byte)(combinedC.b * 255);
}
float[] attachmentUVs = meshAttachment.UVs;
Sequence sequence = meshAttachment.Sequence;
int sequenceIndex = sequence.ResolveIndex(slotPose);
float[] attachmentUVs = sequence.GetUVs(sequenceIndex);
// Potential first attachment bounds initialization. See conditions in RegionAttachment logic.
if (vertexIndex == 0) {

View File

@ -134,31 +134,13 @@ namespace Spine.Unity.TK2D {
}
public RegionAttachment NewRegionAttachment (Skin skin, String name, String path, Sequence sequence) {
RegionAttachment attachment = new RegionAttachment(name);
if (sequence != null)
LoadSequence(name, path, sequence);
else {
AtlasRegion region = ProcessSpriteDefinition(path);
if (region == null)
throw new ArgumentException(string.Format("Region not found in atlas: {0} (region attachment: {1})", path, name));
attachment.Region = region;
attachment.Path = path;
}
return attachment;
LoadSequence(name, path, sequence);
return new RegionAttachment(name, sequence);
}
public MeshAttachment NewMeshAttachment (Skin skin, String name, String path, Sequence sequence) {
MeshAttachment attachment = new MeshAttachment(name);
if (sequence != null)
LoadSequence(name, path, sequence);
else {
AtlasRegion region = ProcessSpriteDefinition(path);
if (region == null)
throw new ArgumentException(string.Format("Region not found in atlas: {0} (region attachment: {1})", path, name));
attachment.Region = region;
attachment.Path = path;
}
return attachment;
LoadSequence(name, path, sequence);
return new MeshAttachment(name, sequence);
}
public BoundingBoxAttachment NewBoundingBoxAttachment (Skin skin, String name) {

View File

@ -77,10 +77,12 @@ namespace Spine.Unity {
if (templateMaterial == null) continue;
foreach (Skin.SkinEntry entry in entryBuffer) {
IHasTextureRegion renderableAttachment = entry.Attachment as IHasTextureRegion;
if (renderableAttachment != null) {
renderableAttachment.Region = materialCache.CloneAtlasRegionWithMaterial(
(AtlasRegion)renderableAttachment.Region, templateMaterial);
IHasSequence renderableAttachment = entry.Attachment as IHasSequence;
if (renderableAttachment == null) continue;
TextureRegion[] regions = renderableAttachment.Sequence.Regions;
for (int i = 0; i < regions.Length; ++i) {
regions[i] = materialCache.CloneAtlasRegionWithMaterial(
(AtlasRegion)regions[i], templateMaterial);
}
}
}

View File

@ -498,7 +498,8 @@ namespace Spine.Unity.AttachmentTools {
}
originalRegions.Clear();
if (!object.ReferenceEquals(sourceAttachments, outputAttachments)) {
bool isInPlaceOperation = object.ReferenceEquals(sourceAttachments, outputAttachments);
if (!isInPlaceOperation) {
outputAttachments.Clear();
outputAttachments.AddRange(sourceAttachments);
}
@ -507,40 +508,22 @@ namespace Spine.Unity.AttachmentTools {
for (int attachmentIndex = 0, n = sourceAttachments.Count; attachmentIndex < n; attachmentIndex++) {
Attachment originalAttachment = sourceAttachments[attachmentIndex];
if (originalAttachment is IHasTextureRegion) {
MeshAttachment originalMeshAttachment = originalAttachment as MeshAttachment;
IHasTextureRegion originalTextureAttachment = (IHasTextureRegion)originalAttachment;
Attachment newAttachment = (originalTextureAttachment.Sequence != null) ? originalAttachment :
(originalMeshAttachment != null) ? originalMeshAttachment.NewLinkedMesh() :
originalAttachment.Copy();
IHasTextureRegion newTextureAttachment = (IHasTextureRegion)newAttachment;
AtlasRegion region = newTextureAttachment.Region as AtlasRegion;
if (region == null && originalTextureAttachment.Sequence != null)
region = (AtlasRegion)originalTextureAttachment.Sequence.Regions[0];
if (originalAttachment is IHasSequence) {
IHasSequence originalTextureAttachment = (IHasSequence)originalAttachment;
Attachment newAttachment = originalAttachment.Copy();
AtlasRegion firstRegion = (AtlasRegion)originalTextureAttachment.Sequence.Regions[0];
int existingIndex;
if (existingRegions.TryGetValue(region, out existingIndex)) {
if (existingRegions.TryGetValue(firstRegion, out existingIndex)) {
regionIndices.Add(existingIndex);
} else {
existingRegions.Add(region, newRegionIndex);
existingRegions.Add(firstRegion, newRegionIndex);
Sequence originalSequence = originalTextureAttachment.Sequence;
if (originalSequence != null) {
newTextureAttachment.Sequence = new Sequence(originalSequence);
for (int i = 0, regionCount = originalSequence.Regions.Length; i < regionCount; ++i) {
AtlasRegion sequenceRegion = (AtlasRegion)originalSequence.Regions[i];
AddRegionTexturesToPack(numTextureParamsToRepack, sequenceRegion,
settings.textureFormat, settings.mipmaps, settings.additionalTextureFormats,
settings.additionalTexturePropertyIDsToCopy, settings.additionalTextureIsLinear);
originalRegions.Add(sequenceRegion);
regionIndices.Add(newRegionIndex);
newRegionIndex++;
}
} else {
AddRegionTexturesToPack(numTextureParamsToRepack, region,
for (int i = 0, regionCount = originalSequence.Regions.Length; i < regionCount; ++i) {
AtlasRegion sequenceRegion = (AtlasRegion)originalSequence.Regions[i];
AddRegionTexturesToPack(numTextureParamsToRepack, sequenceRegion,
settings.textureFormat, settings.mipmaps, settings.additionalTextureFormats,
settings.additionalTexturePropertyIDsToCopy, settings.additionalTextureIsLinear);
originalRegions.Add(region);
originalRegions.Add(sequenceRegion);
regionIndices.Add(newRegionIndex);
newRegionIndex++;
}
@ -616,32 +599,24 @@ namespace Spine.Unity.AttachmentTools {
// Map the cloned attachments to the repacked atlas.
for (int attachmentIndex = 0, repackedIndex = 0, n = outputAttachments.Count;
attachmentIndex < n;
++attachmentIndex, ++repackedIndex) {
attachmentIndex < n; ++attachmentIndex, ++repackedIndex) {
Attachment attachment = outputAttachments[attachmentIndex];
IHasTextureRegion textureAttachment = attachment as IHasTextureRegion;
IHasSequence textureAttachment = attachment as IHasSequence;
if (textureAttachment != null) {
if (textureAttachment.Sequence != null) {
TextureRegion[] regions = textureAttachment.Sequence.Regions;
for (int r = 0, regionCount = regions.Length; r < regionCount; ++r) {
TextureRegion originalRegion = regions[r];
TextureRegion repackedRegion = repackedRegions[regionIndices[repackedIndex++]];
TextureRegion[] regions = textureAttachment.Sequence.Regions;
for (int r = 0, regionCount = regions.Length; r < regionCount; ++r) {
TextureRegion originalRegion = regions[r];
TextureRegion repackedRegion = repackedRegions[regionIndices[repackedIndex++]];
if (enableBlendModes) {
AssignBlendMode(ref repackedRegion, originalRegion, normalShader, ref blendModePages,
additiveMaterialSource, multiplyMaterialSource, screenMaterialSource);
regions[r] = repackedRegion;
}
textureAttachment.Region = regions[0];
--repackedIndex;
} else {
TextureRegion originalRegion = textureAttachment.Region;
TextureRegion repackedRegion = repackedRegions[regionIndices[repackedIndex]];
if (enableBlendModes)
AssignBlendMode(ref repackedRegion, originalRegion, normalShader, ref blendModePages,
additiveMaterialSource, multiplyMaterialSource, screenMaterialSource);
textureAttachment.Region = repackedRegion;
regions[r] = repackedRegion;
}
textureAttachment.UpdateRegion();
--repackedIndex;
textureAttachment.UpdateSequence();
}
}
@ -1075,7 +1050,7 @@ namespace Spine.Unity.AttachmentTools {
}
static bool IsRenderable (Attachment a) {
return a is IHasTextureRegion;
return a is IHasSequence;
}
/// <summary>

View File

@ -74,9 +74,10 @@ namespace Spine.Unity.AttachmentTools {
if (region == null) throw new System.ArgumentNullException("region");
// (AtlasAttachmentLoader.cs)
RegionAttachment attachment = new RegionAttachment(attachmentName);
Sequence sequence = new Sequence(1, false);
sequence.Regions[0] = region;
RegionAttachment attachment = new RegionAttachment(attachmentName, sequence);
attachment.Region = region;
attachment.Path = region.name;
attachment.ScaleX = 1;
attachment.ScaleY = 1;
@ -84,15 +85,11 @@ namespace Spine.Unity.AttachmentTools {
attachment.SetColor(Color.white);
// pass OriginalWidth and OriginalHeight because UpdateOffset uses it in its calculation.
TextureRegion textreRegion = attachment.Region;
AtlasRegion atlasRegion = textreRegion as AtlasRegion;
float originalWidth = atlasRegion != null ? atlasRegion.originalWidth : textreRegion.width;
float originalHeight = atlasRegion != null ? atlasRegion.originalHeight : textreRegion.height;
attachment.Width = originalWidth * scale;
attachment.Height = originalHeight * scale;
attachment.Width = region.originalWidth * scale;
attachment.Height = region.originalHeight * scale;
attachment.SetColor(Color.white);
attachment.UpdateRegion();
attachment.UpdateSequence();
return attachment;
}
@ -166,8 +163,10 @@ namespace Spine.Unity.AttachmentTools {
float scale = 1f / sprite.pixelsPerUnit;
if (useOriginalRegionScale) {
RegionAttachment regionAttachment = attachment as RegionAttachment;
if (regionAttachment != null)
scale = regionAttachment.Width / regionAttachment.Region.OriginalWidth;
if (regionAttachment != null) {
var firstRegion = regionAttachment.Sequence.GetRegion(0);
scale = regionAttachment.Width / firstRegion.OriginalWidth;
}
}
attachment.SetRegion(atlasRegion, useOriginalRegionSize, scale);
}
@ -181,17 +180,18 @@ namespace Spine.Unity.AttachmentTools {
public static void SetRegion (this Attachment attachment, AtlasRegion atlasRegion, bool useOriginalRegionSize = false, float scale = 0.01f) {
RegionAttachment regionAttachment = attachment as RegionAttachment;
if (regionAttachment != null) {
regionAttachment.Region = atlasRegion;
regionAttachment.Sequence.Regions[0] = atlasRegion;
if (!useOriginalRegionSize) {
regionAttachment.Width = atlasRegion.width * scale;
regionAttachment.Height = atlasRegion.height * scale;
}
regionAttachment.UpdateRegion();
regionAttachment.UpdateSequence();
} else {
MeshAttachment meshAttachment = attachment as MeshAttachment;
if (meshAttachment != null) {
meshAttachment.Region = atlasRegion;
meshAttachment.UpdateRegion();
meshAttachment.Sequence.Regions[0] = atlasRegion;
meshAttachment.UpdateSequence();
}
}
}

View File

@ -229,9 +229,10 @@ namespace Spine.Unity {
#region Attachments
public static Material GetMaterial (this Attachment a) {
object rendererObject = null;
IHasTextureRegion renderableAttachment = a as IHasTextureRegion;
if (renderableAttachment != null)
rendererObject = renderableAttachment.Region;
IHasSequence renderableAttachment = a as IHasSequence;
if (renderableAttachment != null) {
rendererObject = renderableAttachment.Sequence.Regions[0];
}
if (rendererObject == null)
return null;

View File

@ -2,7 +2,7 @@
"name": "com.esotericsoftware.spine.spine-unity",
"displayName": "spine-unity Runtime",
"description": "This plugin provides the spine-unity runtime core and examples. Spine Examples can be installed via the Samples tab.",
"version": "4.3.53",
"version": "4.3.54",
"unity": "2018.3",
"author": {
"name": "Esoteric Software",