This commit is contained in:
JA
2026-06-27 03:36:46 +08:00
parent aec4e97d27
commit 7a8d4a5d83
1215 changed files with 48271 additions and 146052 deletions

View File

@@ -1,224 +0,0 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
using System;
namespace Spine {
/// <summary>Attachment that displays a texture region using a mesh.</summary>
public class MeshAttachment : VertexAttachment, IHasRendererObject {
internal float regionOffsetX, regionOffsetY, regionWidth, regionHeight, regionOriginalWidth, regionOriginalHeight;
private MeshAttachment parentMesh;
internal float[] uvs, regionUVs;
internal int[] triangles;
internal float r = 1, g = 1, b = 1, a = 1;
internal int hulllength;
public int HullLength { get { return hulllength; } set { hulllength = value; } }
public float[] RegionUVs { get { return regionUVs; } set { regionUVs = value; } }
/// <summary>The UV pair for each vertex, normalized within the entire texture. <seealso cref="MeshAttachment.UpdateUVs"/></summary>
public float[] UVs { get { return uvs; } set { uvs = value; } }
public int[] Triangles { get { return triangles; } set { triangles = value; } }
public float R { get { return r; } set { r = value; } }
public float G { get { return g; } set { g = value; } }
public float B { get { return b; } set { b = value; } }
public float A { get { return a; } set { a = value; } }
public string Path { get; set; }
public object RendererObject { get; set; }
public float RegionU { get; set; }
public float RegionV { get; set; }
public float RegionU2 { get; set; }
public float RegionV2 { get; set; }
public bool RegionRotate { get; set; }
public int RegionDegrees { get; set; }
public float RegionOffsetX { get { return regionOffsetX; } set { regionOffsetX = value; } }
public float RegionOffsetY { get { return regionOffsetY; } set { regionOffsetY = value; } } // Pixels stripped from the bottom left, unrotated.
public float RegionWidth { get { return regionWidth; } set { regionWidth = value; } }
public float RegionHeight { get { return regionHeight; } set { regionHeight = value; } } // Unrotated, stripped size.
public float RegionOriginalWidth { get { return regionOriginalWidth; } set { regionOriginalWidth = value; } }
public float RegionOriginalHeight { get { return regionOriginalHeight; } set { regionOriginalHeight = value; } } // Unrotated, unstripped size.
public MeshAttachment ParentMesh {
get { return parentMesh; }
set {
parentMesh = value;
if (value != null) {
bones = value.bones;
vertices = value.vertices;
worldVerticesLength = value.worldVerticesLength;
regionUVs = value.regionUVs;
triangles = value.triangles;
HullLength = value.HullLength;
Edges = value.Edges;
Width = value.Width;
Height = value.Height;
}
}
}
// Nonessential.
public int[] Edges { get; set; }
public float Width { get; set; }
public float Height { get; set; }
public MeshAttachment (string name)
: base(name) {
}
public void UpdateUVs () {
float[] regionUVs = this.regionUVs;
if (this.uvs == null || this.uvs.Length != regionUVs.Length) this.uvs = new float[regionUVs.Length];
float[] uvs = this.uvs;
float u = RegionU, v = RegionV, width = 0, height = 0;
if (RegionDegrees == 90) {
float textureHeight = this.regionWidth / (RegionV2 - RegionV);
float textureWidth = this.regionHeight / (RegionU2 - RegionU);
u -= (RegionOriginalHeight - RegionOffsetY - RegionHeight) / textureWidth;
v -= (RegionOriginalWidth - RegionOffsetX - RegionWidth) / textureHeight;
width = RegionOriginalHeight / textureWidth;
height = RegionOriginalWidth / textureHeight;
for (int i = 0, n = uvs.Length; i < n; i += 2) {
uvs[i] = u + regionUVs[i + 1] * width;
uvs[i + 1] = v + (1 - regionUVs[i]) * height;
}
} else if (RegionDegrees == 180) {
float textureWidth = this.regionWidth / (RegionU2 - RegionU);
float textureHeight = this.regionHeight / (RegionV2 - RegionV);
u -= (RegionOriginalWidth - RegionOffsetX - RegionWidth) / textureWidth;
v -= RegionOffsetY / textureHeight;
width = RegionOriginalWidth / textureWidth;
height = RegionOriginalHeight / textureHeight;
for (int i = 0, n = uvs.Length; i < n; i += 2) {
uvs[i] = u + (1 - regionUVs[i]) * width;
uvs[i + 1] = v + (1 - regionUVs[i + 1]) * height;
}
} else if (RegionDegrees == 270) {
float textureWidth = this.regionWidth / (RegionU2 - RegionU);
float textureHeight = this.regionHeight / (RegionV2 - RegionV);
u -= RegionOffsetY / textureWidth;
v -= RegionOffsetX / textureHeight;
width = RegionOriginalHeight / textureWidth;
height = RegionOriginalWidth / textureHeight;
for (int i = 0, n = uvs.Length; i<n; i += 2) {
uvs[i] = u + (1 - regionUVs[i + 1]) * width;
uvs[i + 1] = v + regionUVs[i] * height;
}
} else {
float textureWidth = this.regionWidth / (RegionU2 - RegionU);
float textureHeight = this.regionHeight / (RegionV2 - RegionV);
u -= RegionOffsetX / textureWidth;
v -= (RegionOriginalHeight - RegionOffsetY - RegionHeight) / textureHeight;
width = RegionOriginalWidth / textureWidth;
height = RegionOriginalHeight / textureHeight;
for (int i = 0, n = uvs.Length; i < n; i += 2) {
uvs[i] = u + regionUVs[i] * width;
uvs[i + 1] = v + regionUVs[i + 1] * height;
}
}
}
public override Attachment Copy () {
if (parentMesh != null) return NewLinkedMesh();
MeshAttachment copy = new MeshAttachment(this.Name);
copy.RendererObject = RendererObject;
copy.regionOffsetX = regionOffsetX;
copy.regionOffsetY = regionOffsetY;
copy.regionWidth = regionWidth;
copy.regionHeight = regionHeight;
copy.regionOriginalWidth = regionOriginalWidth;
copy.regionOriginalHeight = regionOriginalHeight;
copy.RegionRotate = RegionRotate;
copy.RegionDegrees = RegionDegrees;
copy.RegionU = RegionU;
copy.RegionV = RegionV;
copy.RegionU2 = RegionU2;
copy.RegionV2 = RegionV2;
copy.Path = Path;
copy.r = r;
copy.g = g;
copy.b = b;
copy.a = a;
CopyTo(copy);
copy.regionUVs = new float[regionUVs.Length];
Array.Copy(regionUVs, 0, copy.regionUVs, 0, regionUVs.Length);
copy.uvs = new float[uvs.Length];
Array.Copy(uvs, 0, copy.uvs, 0, uvs.Length);
copy.triangles = new int[triangles.Length];
Array.Copy(triangles, 0, copy.triangles, 0, triangles.Length);
copy.HullLength = HullLength;
// Nonessential.
if (Edges != null) {
copy.Edges = new int[Edges.Length];
Array.Copy(Edges, 0, copy.Edges, 0, Edges.Length);
}
copy.Width = Width;
copy.Height = Height;
return copy;
}
///<summary>Returns a new mesh with this mesh set as the <see cref="ParentMesh"/>.
public MeshAttachment NewLinkedMesh () {
MeshAttachment mesh = new MeshAttachment(Name);
mesh.RendererObject = RendererObject;
mesh.regionOffsetX = regionOffsetX;
mesh.regionOffsetY = regionOffsetY;
mesh.regionWidth = regionWidth;
mesh.regionHeight = regionHeight;
mesh.regionOriginalWidth = regionOriginalWidth;
mesh.regionOriginalHeight = regionOriginalHeight;
mesh.RegionDegrees = RegionDegrees;
mesh.RegionRotate = RegionRotate;
mesh.RegionU = RegionU;
mesh.RegionV = RegionV;
mesh.RegionU2 = RegionU2;
mesh.RegionV2 = RegionV2;
mesh.Path = Path;
mesh.r = r;
mesh.g = g;
mesh.b = b;
mesh.a = a;
mesh.deformAttachment = deformAttachment;
mesh.ParentMesh = parentMesh != null ? parentMesh : this;
mesh.UpdateUVs();
return mesh;
}
}
}

View File

@@ -1,12 +0,0 @@
fileFormatVersion: 2
guid: b7f7514a003143844b6d01ecc93ed4d5
timeCreated: 1466772712
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,57 +0,0 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
using System;
using System.Collections.Generic;
namespace Spine {
public class PathAttachment : VertexAttachment {
internal float[] lengths;
internal bool closed, constantSpeed;
/// <summary>The length in the setup pose from the start of the path to the end of each curve.</summary>
public float[] Lengths { get { return lengths; } set { lengths = value; } }
public bool Closed { get { return closed; } set { closed = value; } }
public bool ConstantSpeed { get { return constantSpeed; } set { constantSpeed = value; } }
public PathAttachment (String name)
: base(name) {
}
public override Attachment Copy () {
PathAttachment copy = new PathAttachment(this.Name);
CopyTo(copy);
copy.lengths = new float[lengths.Length];
Array.Copy(lengths, 0, copy.lengths, 0, lengths.Length);
copy.closed = closed;
copy.constantSpeed = constantSpeed;
return copy;
}
}
}

View File

@@ -1,12 +0,0 @@
fileFormatVersion: 2
guid: c77d9bf384a1e9f41966464e7e3b4870
timeCreated: 1466772712
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,67 +0,0 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
namespace Spine {
/// <summary>
/// An attachment which is a single point and a rotation. This can be used to spawn projectiles, particles, etc. A bone can be
/// used in similar ways, but a PointAttachment is slightly less expensive to compute and can be hidden, shown, and placed in a
/// skin.
/// <p>
/// See <a href="http://esotericsoftware.com/spine-point-attachments">Point Attachments</a> in the Spine User Guide.
/// </summary>
public class PointAttachment : Attachment {
internal float x, y, rotation;
public float X { get { return x; } set { x = value; } }
public float Y { get { return y; } set { y = value; } }
public float Rotation { get { return rotation; } set { rotation = value; } }
public PointAttachment (string name)
: base(name) {
}
public void ComputeWorldPosition (Bone bone, out float ox, out float oy) {
bone.LocalToWorld(this.x, this.y, out ox, out oy);
}
public float ComputeWorldRotation (Bone bone) {
float cos = MathUtils.CosDeg(rotation), sin = MathUtils.SinDeg(rotation);
float ix = cos * bone.a + sin * bone.b;
float iy = cos * bone.c + sin * bone.d;
return MathUtils.Atan2(iy, ix) * MathUtils.RadDeg;
}
public override Attachment Copy () {
PointAttachment copy = new PointAttachment(this.Name);
copy.x = x;
copy.y = y;
copy.rotation = rotation;
return copy;
}
}
}

View File

@@ -1,12 +0,0 @@
fileFormatVersion: 2
guid: 4fdde4cc4df0952468946f4f913dcb36
timeCreated: 1485603478
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,208 +0,0 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
using System;
namespace Spine {
/// <summary>Attachment that displays a texture region.</summary>
public class RegionAttachment : Attachment, IHasRendererObject {
public const int BLX = 0;
public const int BLY = 1;
public const int ULX = 2;
public const int ULY = 3;
public const int URX = 4;
public const int URY = 5;
public const int BRX = 6;
public const int BRY = 7;
internal float x, y, rotation, scaleX = 1, scaleY = 1, width, height;
internal float regionOffsetX, regionOffsetY, regionWidth, regionHeight, regionOriginalWidth, regionOriginalHeight;
internal float[] offset = new float[8], uvs = new float[8];
internal float r = 1, g = 1, b = 1, a = 1;
public float X { get { return x; } set { x = value; } }
public float Y { get { return y; } set { y = value; } }
public float Rotation { get { return rotation; } set { rotation = value; } }
public float ScaleX { get { return scaleX; } set { scaleX = value; } }
public float ScaleY { get { return scaleY; } set { scaleY = value; } }
public float Width { get { return width; } set { width = value; } }
public float Height { get { return height; } set { height = value; } }
public float R { get { return r; } set { r = value; } }
public float G { get { return g; } set { g = value; } }
public float B { get { return b; } set { b = value; } }
public float A { get { return a; } set { a = value; } }
public string Path { get; set; }
public object RendererObject { get; set; }
public float RegionOffsetX { get { return regionOffsetX; } set { regionOffsetX = value; } }
public float RegionOffsetY { get { return regionOffsetY; } set { regionOffsetY = value; } } // Pixels stripped from the bottom left, unrotated.
public float RegionWidth { get { return regionWidth; } set { regionWidth = value; } }
public float RegionHeight { get { return regionHeight; } set { regionHeight = value; } } // Unrotated, stripped size.
public float RegionOriginalWidth { get { return regionOriginalWidth; } set { regionOriginalWidth = value; } }
public float RegionOriginalHeight { get { return regionOriginalHeight; } set { regionOriginalHeight = value; } } // Unrotated, unstripped size.
public float[] Offset { get { return offset; } }
public float[] UVs { get { return uvs; } }
public RegionAttachment (string name)
: base(name) {
}
public void UpdateOffset () {
float width = this.width;
float height = this.height;
float localX2 = width * 0.5f;
float localY2 = height * 0.5f;
float localX = -localX2;
float localY = -localY2;
if (regionOriginalWidth != 0) { // if (region != null)
localX += regionOffsetX / regionOriginalWidth * width;
localY += regionOffsetY / regionOriginalHeight * height;
localX2 -= (regionOriginalWidth - regionOffsetX - regionWidth) / regionOriginalWidth * width;
localY2 -= (regionOriginalHeight - regionOffsetY - regionHeight) / regionOriginalHeight * height;
}
float scaleX = this.scaleX;
float scaleY = this.scaleY;
localX *= scaleX;
localY *= scaleY;
localX2 *= scaleX;
localY2 *= scaleY;
float rotation = this.rotation;
float cos = MathUtils.CosDeg(rotation);
float sin = MathUtils.SinDeg(rotation);
float x = this.x;
float y = this.y;
float localXCos = localX * cos + x;
float localXSin = localX * sin;
float localYCos = localY * cos + y;
float localYSin = localY * sin;
float localX2Cos = localX2 * cos + x;
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;
offset[ULY] = localY2Cos + localXSin;
offset[URX] = localX2Cos - localY2Sin;
offset[URY] = localY2Cos + localX2Sin;
offset[BRX] = localX2Cos - localYSin;
offset[BRY] = localYCos + localX2Sin;
}
public void SetUVs (float u, float v, float u2, float v2, bool rotate) {
float[] uvs = this.uvs;
// UV values differ from RegionAttachment.java
if (rotate) {
uvs[URX] = u;
uvs[URY] = v2;
uvs[BRX] = u;
uvs[BRY] = v;
uvs[BLX] = u2;
uvs[BLY] = v;
uvs[ULX] = u2;
uvs[ULY] = v2;
} else {
uvs[ULX] = u;
uvs[ULY] = v2;
uvs[URX] = u;
uvs[URY] = v;
uvs[BRX] = u2;
uvs[BRY] = v;
uvs[BLX] = u2;
uvs[BLY] = v2;
}
}
/// <summary>Transforms the attachment's four vertices to world coordinates.</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 (Bone bone, float[] worldVertices, int offset, int stride = 2) {
float[] vertexOffset = this.offset;
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 () {
RegionAttachment copy = new RegionAttachment(this.Name);
copy.RendererObject = RendererObject;
copy.regionOffsetX = regionOffsetX;
copy.regionOffsetY = regionOffsetY;
copy.regionWidth = regionWidth;
copy.regionHeight = regionHeight;
copy.regionOriginalWidth = regionOriginalWidth;
copy.regionOriginalHeight = regionOriginalHeight;
copy.Path = Path;
copy.x = x;
copy.y = y;
copy.scaleX = scaleX;
copy.scaleY = scaleY;
copy.rotation = rotation;
copy.width = width;
copy.height = height;
Array.Copy(uvs, 0, copy.uvs, 0, 8);
Array.Copy(offset, 0, copy.offset, 0, 8);
copy.r = r;
copy.g = g;
copy.b = b;
copy.a = a;
return copy;
}
}
}

View File

@@ -1,12 +0,0 @@
fileFormatVersion: 2
guid: 89cefdd024734a941952a05d2b5dff71
timeCreated: 1466772712
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,633 +0,0 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
/******************************************************************************
* Thanks to Travis Parks
* https://github.com/jehugaleahsa/truncon.collections.OrderedDictionary
*****************************************************************************/
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
namespace Spine.Collections
{
/// <summary>
/// Represents a dictionary that tracks the order that items were added.
/// </summary>
/// <typeparam name="TKey">The type of the dictionary keys.</typeparam>
/// <typeparam name="TValue">The type of the dictionary values.</typeparam>
/// <remarks>
/// This dictionary makes it possible to get the index of a key and a key based on an index.
/// It can be costly to find the index of a key because it must be searched for linearly.
/// It can be costly to insert a key/value pair because other key's indexes must be adjusted.
/// It can be costly to remove a key/value pair because other keys' indexes must be adjusted.
/// </remarks>
[DebuggerDisplay("Count = {Count}")]
[DebuggerTypeProxy(typeof(OrderedDictionaryDebugView<,>))]
public sealed class OrderedDictionary<TKey, TValue> : IDictionary<TKey, TValue>, IList<KeyValuePair<TKey, TValue>>
{
private readonly Dictionary<TKey, int> dictionary;
private readonly List<TKey> keys;
private readonly List<TValue> values;
private int version;
private const string CollectionModifiedMessage = "Collection was modified; enumeration operation may not execute.";
private const string EditReadOnlyListMessage = "An attempt was made to edit a read-only list.";
private const string IndexOutOfRangeMessage = "The index is negative or outside the bounds of the collection.";
/// <summary>
/// Initializes a new instance of an OrderedDictionary.
/// </summary>
public OrderedDictionary ()
: this(0, null) {
}
/// <summary>
/// Initializes a new instance of an OrderedDictionary.
/// </summary>
/// <param name="capacity">The initial capacity of the dictionary.</param>
/// <exception cref="System.ArgumentOutOfRangeException">The capacity is less than zero.</exception>
public OrderedDictionary (int capacity)
: this(capacity, null) {
}
/// <summary>
/// Initializes a new instance of an OrderedDictionary.
/// </summary>
/// <param name="comparer">The equality comparer to use to compare keys.</param>
public OrderedDictionary (IEqualityComparer<TKey> comparer)
: this(0, comparer) {
}
/// <summary>
/// Initializes a new instance of an OrderedDictionary.
/// </summary>
/// <param name="capacity">The initial capacity of the dictionary.</param>
/// <param name="comparer">The equality comparer to use to compare keys.</param>
public OrderedDictionary (int capacity, IEqualityComparer<TKey> comparer) {
dictionary = new Dictionary<TKey, int>(capacity, comparer ?? EqualityComparer<TKey>.Default);
keys = new List<TKey>(capacity);
values = new List<TValue>(capacity);
}
/// <summary>
/// Gets the equality comparer used to compare keys.
/// </summary>
public IEqualityComparer<TKey> Comparer {
get {
return dictionary.Comparer;
}
}
/// <summary>
/// Adds the given key/value pair to the dictionary.
/// </summary>
/// <param name="key">The key to add to the dictionary.</param>
/// <param name="value">The value to associated with the key.</param>
/// <exception cref="System.ArgumentException">The given key already exists in the dictionary.</exception>
/// <exception cref="System.ArgumentNullException">The key is null.</exception>
public void Add (TKey key, TValue value) {
dictionary.Add(key, values.Count);
keys.Add(key);
values.Add(value);
++version;
}
/// <summary>
/// Inserts the given key/value pair at the specified index.
/// </summary>
/// <param name="index">The index to insert the key/value pair.</param>
/// <param name="key">The key to insert.</param>
/// <param name="value">The value to insert.</param>
/// <exception cref="System.ArgumentException">The given key already exists in the dictionary.</exception>
/// <exception cref="System.ArgumentNullException">The key is null.</exception>
/// <exception cref="System.ArgumentOutOfRangeException">The index is negative -or- larger than the size of the dictionary.</exception>
public void Insert (int index, TKey key, TValue value) {
if (index < 0 || index > values.Count) {
throw new ArgumentOutOfRangeException("index", index, IndexOutOfRangeMessage);
}
dictionary.Add(key, index);
for (int keyIndex = index; keyIndex != keys.Count; ++keyIndex) {
var otherKey = keys[keyIndex];
dictionary[otherKey] += 1;
}
keys.Insert(index, key);
values.Insert(index, value);
++version;
}
/// <summary>
/// Determines whether the given key exists in the dictionary.
/// </summary>
/// <param name="key">The key to look for.</param>
/// <returns>True if the key exists in the dictionary; otherwise, false.</returns>
/// <exception cref="System.ArgumentNullException">The key is null.</exception>
public bool ContainsKey (TKey key) {
return dictionary.ContainsKey(key);
}
/// <summary>
/// Gets the key at the given index.
/// </summary>
/// <param name="index">The index of the key to get.</param>
/// <returns>The key at the given index.</returns>
/// <exception cref="System.ArgumentOutOfRangeException">The index is negative -or- larger than the number of keys.</exception>
public TKey GetKey (int index) {
return keys[index];
}
/// <summary>
/// Gets the index of the given key.
/// </summary>
/// <param name="key">The key to get the index of.</param>
/// <returns>The index of the key in the dictionary -or- -1 if the key is not found.</returns>
/// <remarks>The operation runs in O(n).</remarks>
public int IndexOf (TKey key) {
int index;
if (dictionary.TryGetValue(key, out index)) {
return index;
}
return -1;
}
/// <summary>
/// Gets the keys in the dictionary in the order they were added.
/// </summary>
public KeyCollection Keys {
get {
return new KeyCollection(this.dictionary);
}
}
/// <summary>
/// Removes the key/value pair with the given key from the dictionary.
/// </summary>
/// <param name="key">The key of the pair to remove.</param>
/// <returns>True if the key was found and the pair removed; otherwise, false.</returns>
/// <exception cref="System.ArgumentNullException">The key is null.</exception>
public bool Remove (TKey key) {
int index;
if (dictionary.TryGetValue(key, out index)) {
RemoveAt(index);
return true;
}
return false;
}
/// <summary>
/// Removes the key/value pair at the given index.
/// </summary>
/// <param name="index">The index of the key/value pair to remove.</param>
/// <exception cref="System.ArgumentOutOfRangeException">The index is negative -or- larger than the size of the dictionary.</exception>
public void RemoveAt (int index) {
var key = keys[index];
for (int keyIndex = index + 1; keyIndex < keys.Count; ++keyIndex) {
var otherKey = keys[keyIndex];
dictionary[otherKey] -= 1;
}
dictionary.Remove(key);
keys.RemoveAt(index);
values.RemoveAt(index);
++version;
}
/// <summary>
/// Tries to get the value associated with the given key. If the key is not found,
/// default(TValue) value is stored in the value.
/// </summary>
/// <param name="key">The key to get the value for.</param>
/// <param name="value">The value used to hold the results.</param>
/// <returns>True if the key was found; otherwise, false.</returns>
/// <exception cref="System.ArgumentNullException">The key is null.</exception>
public bool TryGetValue (TKey key, out TValue value) {
int index;
if (dictionary.TryGetValue(key, out index)) {
value = values[index];
return true;
}
value = default(TValue);
return false;
}
/// <summary>
/// Gets the values in the dictionary.
/// </summary>
public ValueCollection Values {
get {
return new ValueCollection(values);
}
}
/// <summary>
/// Gets or sets the value at the given index.
/// </summary>
/// <param name="index">The index of the value to get.</param>
/// <returns>The value at the given index.</returns>
/// <exception cref="System.ArgumentOutOfRangeException">The index is negative -or- beyond the length of the dictionary.</exception>
public TValue this[int index] {
get {
return values[index];
}
set {
values[index] = value;
}
}
/// <summary>
/// Gets or sets the value associated with the given key.
/// </summary>
/// <param name="key">The key to get the associated value by or to associate with the value.</param>
/// <returns>The value associated with the given key.</returns>
/// <exception cref="System.ArgumentNullException">The key is null.</exception>
/// <exception cref="System.Collections.Generic.KeyNotFoundException">The key is not in the dictionary.</exception>
public TValue this[TKey key] {
get {
return values[dictionary[key]];
}
set {
int index;
if (dictionary.TryGetValue(key, out index)) {
keys[index] = key;
values[index] = value;
}
else {
Add(key, value);
}
}
}
/// <summary>
/// Removes all key/value pairs from the dictionary.
/// </summary>
public void Clear () {
dictionary.Clear();
keys.Clear();
values.Clear();
++version;
}
/// <summary>
/// Gets the number of key/value pairs in the dictionary.
/// </summary>
public int Count {
get {
return dictionary.Count;
}
}
/// <summary>
/// Gets the key/value pairs in the dictionary in the order they were added.
/// </summary>
/// <returns>An enumerator over the key/value pairs in the dictionary.</returns>
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator () {
int startVersion = version;
for (int index = 0; index != keys.Count; ++index) {
var key = keys[index];
var value = values[index];
yield return new KeyValuePair<TKey, TValue>(key, value);
if (version != startVersion) {
throw new InvalidOperationException(CollectionModifiedMessage);
}
}
}
int IList<KeyValuePair<TKey, TValue>>.IndexOf (KeyValuePair<TKey, TValue> item) {
int index;
if (dictionary.TryGetValue(item.Key, out index) && Equals(values[index], item.Value)) {
return index;
}
return -1;
}
void IList<KeyValuePair<TKey, TValue>>.Insert (int index, KeyValuePair<TKey, TValue> item) {
Insert(index, item.Key, item.Value);
}
KeyValuePair<TKey, TValue> IList<KeyValuePair<TKey, TValue>>.this[int index] {
get {
TKey key = keys[index];
TValue value = values[index];
return new KeyValuePair<TKey, TValue>(key, value);
}
set {
TKey key = keys[index];
if (dictionary.Comparer.Equals(key, value.Key)) {
dictionary[value.Key] = index;
}
else {
dictionary.Add(value.Key, index); // will throw if key already exists
dictionary.Remove(key);
}
keys[index] = value.Key;
values[index] = value.Value;
}
}
ICollection<TKey> IDictionary<TKey, TValue>.Keys {
get {
return Keys;
}
}
ICollection<TValue> IDictionary<TKey, TValue>.Values {
get {
return Values;
}
}
void ICollection<KeyValuePair<TKey, TValue>>.Add (KeyValuePair<TKey, TValue> item) {
Add(item.Key, item.Value);
}
bool ICollection<KeyValuePair<TKey, TValue>>.Contains (KeyValuePair<TKey, TValue> item) {
int index;
if (dictionary.TryGetValue(item.Key, out index) && Equals(values[index], item.Value)) {
return true;
}
return false;
}
void ICollection<KeyValuePair<TKey, TValue>>.CopyTo (KeyValuePair<TKey, TValue>[] array, int arrayIndex) {
if (array == null) {
throw new ArgumentNullException("array");
}
if (arrayIndex < 0) {
throw new ArgumentOutOfRangeException("arrayIndex", arrayIndex, IndexOutOfRangeMessage);
}
for (int index = 0; index != keys.Count && arrayIndex < array.Length; ++index, ++arrayIndex) {
var key = keys[index];
var value = values[index];
array[arrayIndex] = new KeyValuePair<TKey, TValue>(key, value);
}
}
bool ICollection<KeyValuePair<TKey, TValue>>.IsReadOnly {
get {
return false;
}
}
bool ICollection<KeyValuePair<TKey, TValue>>.Remove (KeyValuePair<TKey, TValue> item) {
ICollection<KeyValuePair<TKey, TValue>> self = this;
if (self.Contains(item)) {
return Remove(item.Key);
}
return false;
}
IEnumerator IEnumerable.GetEnumerator () {
return GetEnumerator();
}
/// <summary>
/// Wraps the keys in an OrderDictionary.
/// </summary>
public sealed class KeyCollection : ICollection<TKey>
{
private readonly Dictionary<TKey, int> dictionary;
/// <summary>
/// Initializes a new instance of a KeyCollection.
/// </summary>
/// <param name="dictionary">The OrderedDictionary whose keys to wrap.</param>
/// <exception cref="System.ArgumentNullException">The dictionary is null.</exception>
internal KeyCollection (Dictionary<TKey, int> dictionary) {
this.dictionary = dictionary;
}
/// <summary>
/// Copies the keys from the OrderedDictionary to the given array, starting at the given index.
/// </summary>
/// <param name="array">The array to copy the keys to.</param>
/// <param name="arrayIndex">The index into the array to start copying the keys.</param>
/// <exception cref="System.ArgumentNullException">The array is null.</exception>
/// <exception cref="System.ArgumentOutOfRangeException">The arrayIndex is negative.</exception>
/// <exception cref="System.ArgumentException">The array, starting at the given index, is not large enough to contain all the keys.</exception>
public void CopyTo (TKey[] array, int arrayIndex) {
dictionary.Keys.CopyTo(array, arrayIndex);
}
/// <summary>
/// Gets the number of keys in the OrderedDictionary.
/// </summary>
public int Count {
get {
return dictionary.Count;
}
}
/// <summary>
/// Gets an enumerator over the keys in the OrderedDictionary.
/// </summary>
/// <returns>The enumerator.</returns>
public IEnumerator<TKey> GetEnumerator () {
return dictionary.Keys.GetEnumerator();
}
[EditorBrowsable(EditorBrowsableState.Never)]
bool ICollection<TKey>.Contains (TKey item) {
return dictionary.ContainsKey(item);
}
[EditorBrowsable(EditorBrowsableState.Never)]
void ICollection<TKey>.Add (TKey item) {
throw new NotSupportedException(EditReadOnlyListMessage);
}
[EditorBrowsable(EditorBrowsableState.Never)]
void ICollection<TKey>.Clear () {
throw new NotSupportedException(EditReadOnlyListMessage);
}
[EditorBrowsable(EditorBrowsableState.Never)]
bool ICollection<TKey>.IsReadOnly {
get {
return true;
}
}
[EditorBrowsable(EditorBrowsableState.Never)]
bool ICollection<TKey>.Remove (TKey item) {
throw new NotSupportedException(EditReadOnlyListMessage);
}
IEnumerator IEnumerable.GetEnumerator () {
return GetEnumerator();
}
}
/// <summary>
/// Wraps the keys in an OrderDictionary.
/// </summary>
public sealed class ValueCollection : ICollection<TValue>
{
private readonly List<TValue> values;
/// <summary>
/// Initializes a new instance of a ValueCollection.
/// </summary>
/// <param name="values">The OrderedDictionary whose keys to wrap.</param>
/// <exception cref="System.ArgumentNullException">The dictionary is null.</exception>
internal ValueCollection (List<TValue> values) {
this.values = values;
}
/// <summary>
/// Copies the values from the OrderedDictionary to the given array, starting at the given index.
/// </summary>
/// <param name="array">The array to copy the values to.</param>
/// <param name="arrayIndex">The index into the array to start copying the values.</param>
/// <exception cref="System.ArgumentNullException">The array is null.</exception>
/// <exception cref="System.ArgumentOutOfRangeException">The arrayIndex is negative.</exception>
/// <exception cref="System.ArgumentException">The array, starting at the given index, is not large enough to contain all the values.</exception>
public void CopyTo (TValue[] array, int arrayIndex) {
values.CopyTo(array, arrayIndex);
}
/// <summary>
/// Gets the number of values in the OrderedDictionary.
/// </summary>
public int Count {
get {
return values.Count;
}
}
/// <summary>
/// Gets an enumerator over the values in the OrderedDictionary.
/// </summary>
/// <returns>The enumerator.</returns>
public IEnumerator<TValue> GetEnumerator () {
return values.GetEnumerator();
}
[EditorBrowsable(EditorBrowsableState.Never)]
bool ICollection<TValue>.Contains (TValue item) {
return values.Contains(item);
}
[EditorBrowsable(EditorBrowsableState.Never)]
void ICollection<TValue>.Add (TValue item) {
throw new NotSupportedException(EditReadOnlyListMessage);
}
[EditorBrowsable(EditorBrowsableState.Never)]
void ICollection<TValue>.Clear () {
throw new NotSupportedException(EditReadOnlyListMessage);
}
[EditorBrowsable(EditorBrowsableState.Never)]
bool ICollection<TValue>.IsReadOnly {
get {
return true;
}
}
[EditorBrowsable(EditorBrowsableState.Never)]
bool ICollection<TValue>.Remove (TValue item) {
throw new NotSupportedException(EditReadOnlyListMessage);
}
IEnumerator IEnumerable.GetEnumerator () {
return GetEnumerator();
}
}
}
internal class OrderedDictionaryDebugView<TKey, TValue>
{
private readonly OrderedDictionary<TKey, TValue> dictionary;
public OrderedDictionaryDebugView (OrderedDictionary<TKey, TValue> dictionary) {
this.dictionary = dictionary;
}
[DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
public KeyValuePair<TKey, TValue>[] Items {
get {
return dictionary.ToArray();
}
}
}
/// <summary>
/// Provides extensions methods for constructing instances of <see cref="OrderedDictionary{TKey, TValue}"/>.
/// </summary>
public static class CollectionExtensions
{
#region ToOrderedDictionary
/// <summary>
/// Creates a new OrderedDictionary from the given collection, using the key selector to extract the key.
/// </summary>
/// <typeparam name="TSource">The type of the items in the collection.</typeparam>
/// <typeparam name="TKey">The type of the key.</typeparam>
/// <param name="source">The items to created the OrderedDictionary from.</param>
/// <param name="keySelector">A delegate that can extract a key from an item in the collection.</param>
/// <returns>An OrderedDictionary mapping the extracted keys to their values.</returns>
public static OrderedDictionary<TKey, TSource> ToOrderedDictionary<TSource, TKey> (this IEnumerable<TSource> source, Func<TSource, TKey> keySelector) {
return ToOrderedDictionary(source, keySelector, null);
}
/// <summary>
/// Creates a new OrderedDictionary from the given collection, using the key selector to extract the key.
/// The key comparer is passed to the OrderedDictionary for comparing the extracted keys.
/// </summary>
/// <typeparam name="TSource">The type of the items in the collection.</typeparam>
/// <typeparam name="TKey">The type of the key.</typeparam>
/// <param name="source">The items to created the OrderedDictionary from.</param>
/// <param name="keySelector">A delegate that can extract a key from an item in the collection.</param>
/// <param name="comparer">The key equality comparer to use to compare keys in the dictionary.</param>
/// <returns>An OrderedDictionary mapping the extracted keys to their values.</returns>
public static OrderedDictionary<TKey, TSource> ToOrderedDictionary<TSource, TKey> (
this IEnumerable<TSource> source,
Func<TSource, TKey> keySelector,
IEqualityComparer<TKey> comparer) {
if (source == null) {
throw new ArgumentNullException("source");
}
if (keySelector == null) {
throw new ArgumentNullException("keySelector");
}
var dictionary = new OrderedDictionary<TKey, TSource>(comparer);
foreach (TSource item in source) {
TKey key = keySelector(item);
dictionary.Add(key, item);
}
return dictionary;
}
#endregion
}
}

View File

@@ -1,12 +0,0 @@
fileFormatVersion: 2
guid: ca4b738078ed756469d5d97bcca93e6f
timeCreated: 1636570216
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,42 +0,0 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
namespace Spine {
///<summary>The interface for items updated by <see cref="Skeleton.UpdateWorldTransform()"/>.</summary>
public interface IUpdatable {
void Update ();
///<summary>Returns false when this item has not been updated because a skin is required and the <see cref="Skeleton.Skin">active
/// skin</see> does not contain this item.</summary>
/// <seealso cref="Skin.Bones"/>
/// <seealso cref="Skin.Constraints"/>
bool Active { get; }
}
}

View File

@@ -1,12 +0,0 @@
fileFormatVersion: 2
guid: 44a51df5672fe4249b6763960587a017
timeCreated: 1456265154
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,365 +0,0 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
using System;
namespace Spine {
/// <summary>
/// <para>
/// Stores the current pose for an IK constraint. An IK constraint adjusts the rotation of 1 or 2 constrained bones so the tip of
/// the last bone is as close to the target bone as possible.</para>
/// <para>
/// See <a href="http://esotericsoftware.com/spine-ik-constraints">IK constraints</a> in the Spine User Guide.</para>
/// </summary>
public class IkConstraint : IUpdatable {
internal IkConstraintData data;
internal ExposedList<Bone> bones = new ExposedList<Bone>();
internal Bone target;
internal int bendDirection;
internal bool compress, stretch;
internal float mix = 1, softness;
internal bool active;
public IkConstraint (IkConstraintData data, Skeleton skeleton) {
if (data == null) throw new ArgumentNullException("data", "data cannot be null.");
if (skeleton == null) throw new ArgumentNullException("skeleton", "skeleton cannot be null.");
this.data = data;
mix = data.mix;
softness = data.softness;
bendDirection = data.bendDirection;
compress = data.compress;
stretch = data.stretch;
bones = new ExposedList<Bone>(data.bones.Count);
foreach (BoneData boneData in data.bones)
bones.Add(skeleton.FindBone(boneData.name));
target = skeleton.FindBone(data.target.name);
}
/// <summary>Copy constructor.</summary>
public IkConstraint (IkConstraint constraint, Skeleton skeleton) {
if (constraint == null) throw new ArgumentNullException("constraint cannot be null.");
if (skeleton == null) throw new ArgumentNullException("skeleton cannot be null.");
data = constraint.data;
bones = new ExposedList<Bone>(constraint.Bones.Count);
foreach (Bone bone in constraint.Bones)
bones.Add(skeleton.Bones.Items[bone.data.index]);
target = skeleton.Bones.Items[constraint.target.data.index];
mix = constraint.mix;
softness = constraint.softness;
bendDirection = constraint.bendDirection;
compress = constraint.compress;
stretch = constraint.stretch;
}
/// <summary>Applies the constraint to the constrained bones.</summary>
public void Apply () {
Update();
}
public void Update () {
Bone target = this.target;
ExposedList<Bone> bones = this.bones;
switch (bones.Count) {
case 1:
Apply(bones.Items[0], target.worldX, target.worldY, compress, stretch, data.uniform, mix);
break;
case 2:
Apply(bones.Items[0], bones.Items[1], target.worldX, target.worldY, bendDirection, stretch, softness, mix);
break;
}
}
/// <summary>The bones that will be modified by this IK constraint.</summary>
public ExposedList<Bone> Bones {
get { return bones; }
}
/// <summary>The bone that is the IK target.</summary>
public Bone Target {
get { return target; }
set { target = value; }
}
/// <summary>A percentage (0-1) that controls the mix between the constrained and unconstrained rotations.</summary>
public float Mix {
get { return mix; }
set { mix = value; }
}
///<summary>For two bone IK, the distance from the maximum reach of the bones that rotation will slow.</summary>
public float Softness {
get { return softness; }
set { softness = value; }
}
/// <summary>Controls the bend direction of the IK bones, either 1 or -1.</summary>
public int BendDirection {
get { return bendDirection; }
set { bendDirection = value; }
}
/// <summary>
/// When true and only a single bone is being constrained, if the target is too close, the bone is scaled to reach it.</summary>
public bool Compress {
get { return compress; }
set { compress = value; }
}
/// <summary>
/// When true, if the target is out of range, the parent bone is scaled to reach it. If more than one bone is being constrained
/// and the parent bone has local nonuniform scale, stretch is not applied.</summary>
public bool Stretch {
get { return stretch; }
set { stretch = value; }
}
public bool Active {
get { return active; }
}
/// <summary>The IK constraint's setup pose data.</summary>
public IkConstraintData Data {
get { return data; }
}
override public string ToString () {
return data.name;
}
/// <summary>Applies 1 bone IK. The target is specified in the world coordinate system.</summary>
static public void Apply (Bone bone, float targetX, float targetY, bool compress, bool stretch, bool uniform,
float alpha) {
if (!bone.appliedValid) bone.UpdateAppliedTransform();
Bone p = bone.parent;
float pa = p.a, pb = p.b, pc = p.c, pd = p.d;
float rotationIK = -bone.ashearX - bone.arotation;
float tx = 0, ty = 0;
switch(bone.data.transformMode) {
case TransformMode.OnlyTranslation:
tx = targetX - bone.worldX;
ty = targetY - bone.worldY;
break;
case TransformMode.NoRotationOrReflection: {
float s = Math.Abs(pa * pd - pb * pc) / (pa * pa + pc * pc);
float sa = pa / bone.skeleton.ScaleX;
float sc = pc / bone.skeleton.ScaleY;
pb = -sc * s * bone.skeleton.ScaleX;
pd = sa * s * bone.skeleton.ScaleY;
rotationIK += (float)Math.Atan2(pc, pa) * MathUtils.RadDeg;
goto default; // Fall through.
}
default: {
float x = targetX - p.worldX, y = targetY - p.worldY;
float d = pa * pd - pb * pc;
tx = (x * pd - y * pb) / d - bone.ax;
ty = (y * pa - x * pc) / d - bone.ay;
break;
}
}
rotationIK += (float)Math.Atan2(ty, tx) * MathUtils.RadDeg;
if (bone.ascaleX < 0) rotationIK += 180;
if (rotationIK > 180)
rotationIK -= 360;
else if (rotationIK < -180) //
rotationIK += 360;
float sx = bone.ascaleX, sy = bone.ascaleY;
if (compress || stretch) {
switch (bone.data.transformMode) {
case TransformMode.NoScale:
tx = targetX - bone.worldX;
ty = targetY - bone.worldY;
break;
case TransformMode.NoScaleOrReflection:
tx = targetX - bone.worldX;
ty = targetY - bone.worldY;
break;
}
float b = bone.data.length * sx, dd = (float)Math.Sqrt(tx * tx + ty * ty);
if ((compress && dd < b) || (stretch && dd > b) && b > 0.0001f) {
float s = (dd / b - 1) * alpha + 1;
sx *= s;
if (uniform) sy *= s;
}
}
bone.UpdateWorldTransform(bone.ax, bone.ay, bone.arotation + rotationIK * alpha, sx, sy, bone.ashearX, bone.ashearY);
}
/// <summary>Applies 2 bone IK. The target is specified in the world coordinate system.</summary>
/// <param name="child">A direct descendant of the parent bone.</param>
static public void Apply (Bone parent, Bone child, float targetX, float targetY, int bendDir, bool stretch, float softness,
float alpha) {
if (alpha == 0) {
child.UpdateWorldTransform();
return;
}
if (!parent.appliedValid) parent.UpdateAppliedTransform();
if (!child.appliedValid) child.UpdateAppliedTransform();
float px = parent.ax, py = parent.ay, psx = parent.ascaleX, sx = psx, psy = parent.ascaleY, csx = child.ascaleX;
int os1, os2, s2;
if (psx < 0) {
psx = -psx;
os1 = 180;
s2 = -1;
} else {
os1 = 0;
s2 = 1;
}
if (psy < 0) {
psy = -psy;
s2 = -s2;
}
if (csx < 0) {
csx = -csx;
os2 = 180;
} else
os2 = 0;
float cx = child.ax, cy, cwx, cwy, a = parent.a, b = parent.b, c = parent.c, d = parent.d;
bool u = Math.Abs(psx - psy) <= 0.0001f;
if (!u) {
cy = 0;
cwx = a * cx + parent.worldX;
cwy = c * cx + parent.worldY;
} else {
cy = child.ay;
cwx = a * cx + b * cy + parent.worldX;
cwy = c * cx + d * cy + parent.worldY;
}
Bone pp = parent.parent;
a = pp.a;
b = pp.b;
c = pp.c;
d = pp.d;
float id = 1 / (a * d - b * c), x = cwx - pp.worldX, y = cwy - pp.worldY;
float dx = (x * d - y * b) * id - px, dy = (y * a - x * c) * id - py;
float l1 = (float)Math.Sqrt(dx * dx + dy * dy), l2 = child.data.length * csx, a1, a2;
if (l1 < 0.0001f) {
Apply(parent, targetX, targetY, false, stretch, false, alpha);
child.UpdateWorldTransform(cx, cy, 0, child.ascaleX, child.ascaleY, child.ashearX, child.ashearY);
return;
}
x = targetX - pp.worldX;
y = targetY - pp.worldY;
float tx = (x * d - y * b) * id - px, ty = (y * a - x * c) * id - py;
float dd = tx * tx + ty * ty;
if (softness != 0) {
softness *= psx * (csx + 1) / 2;
float td = (float)Math.Sqrt(dd), sd = td - l1 - l2 * psx + softness;
if (sd > 0) {
float p = Math.Min(1, sd / (softness * 2)) - 1;
p = (sd - softness * (1 - p * p)) / td;
tx -= p * tx;
ty -= p * ty;
dd = tx * tx + ty * ty;
}
}
if (u) {
l2 *= psx;
float cos = (dd - l1 * l1 - l2 * l2) / (2 * l1 * l2);
if (cos < -1)
cos = -1;
else if (cos > 1) {
cos = 1;
if (stretch) sx *= ((float)Math.Sqrt(dd) / (l1 + l2) - 1) * alpha + 1;
}
a2 = (float)Math.Acos(cos) * bendDir;
a = l1 + l2 * cos;
b = l2 * (float)Math.Sin(a2);
a1 = (float)Math.Atan2(ty * a - tx * b, tx * a + ty * b);
} else {
a = psx * l2;
b = psy * l2;
float aa = a * a, bb = b * b, ta = (float)Math.Atan2(ty, tx);
c = bb * l1 * l1 + aa * dd - aa * bb;
float c1 = -2 * bb * l1, c2 = bb - aa;
d = c1 * c1 - 4 * c2 * c;
if (d >= 0) {
float q = (float)Math.Sqrt(d);
if (c1 < 0) q = -q;
q = -(c1 + q) / 2;
float r0 = q / c2, r1 = c / q;
float r = Math.Abs(r0) < Math.Abs(r1) ? r0 : r1;
if (r * r <= dd) {
y = (float)Math.Sqrt(dd - r * r) * bendDir;
a1 = ta - (float)Math.Atan2(y, r);
a2 = (float)Math.Atan2(y / psy, (r - l1) / psx);
goto break_outer; // break outer;
}
}
float minAngle = MathUtils.PI, minX = l1 - a, minDist = minX * minX, minY = 0;
float maxAngle = 0, maxX = l1 + a, maxDist = maxX * maxX, maxY = 0;
c = -a * l1 / (aa - bb);
if (c >= -1 && c <= 1) {
c = (float)Math.Acos(c);
x = a * (float)Math.Cos(c) + l1;
y = b * (float)Math.Sin(c);
d = x * x + y * y;
if (d < minDist) {
minAngle = c;
minDist = d;
minX = x;
minY = y;
}
if (d > maxDist) {
maxAngle = c;
maxDist = d;
maxX = x;
maxY = y;
}
}
if (dd <= (minDist + maxDist) / 2) {
a1 = ta - (float)Math.Atan2(minY * bendDir, minX);
a2 = minAngle * bendDir;
} else {
a1 = ta - (float)Math.Atan2(maxY * bendDir, maxX);
a2 = maxAngle * bendDir;
}
}
break_outer:
float os = (float)Math.Atan2(cy, cx) * s2;
float rotation = parent.arotation;
a1 = (a1 - os) * MathUtils.RadDeg + os1 - rotation;
if (a1 > 180)
a1 -= 360;
else if (a1 < -180) a1 += 360;
parent.UpdateWorldTransform(px, py, rotation + a1 * alpha, sx, parent.ascaleY, 0, 0);
rotation = child.arotation;
a2 = ((a2 + os) * MathUtils.RadDeg - child.ashearX) * s2 + os2 - rotation;
if (a2 > 180)
a2 -= 360;
else if (a2 < -180) a2 += 360;
child.UpdateWorldTransform(cx, cy, rotation + a2 * alpha, child.ascaleX, child.ascaleY, child.ashearX, child.ashearY);
}
}
}

View File

@@ -1,12 +0,0 @@
fileFormatVersion: 2
guid: 916f8e6534860cc40824adfc2916baa7
timeCreated: 1456265155
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,99 +0,0 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
using System;
using System.Collections.Generic;
namespace Spine {
/// <summary>Stores the setup pose for an IkConstraint.</summary>
public class IkConstraintData : ConstraintData {
internal ExposedList<BoneData> bones = new ExposedList<BoneData>();
internal BoneData target;
internal int bendDirection = 1;
internal bool compress, stretch, uniform;
internal float mix = 1, softness;
public IkConstraintData (string name) : base(name) {
}
/// <summary>The bones that are constrained by this IK Constraint.</summary>
public ExposedList<BoneData> Bones {
get { return bones; }
}
/// <summary>The bone that is the IK target.</summary>
public BoneData Target {
get { return target; }
set { target = value; }
}
/// <summary>
/// A percentage (0-1) that controls the mix between the constraint and unconstrained rotations.</summary>
public float Mix {
get { return mix; }
set { mix = value; }
}
///<summary>For two bone IK, the distance from the maximum reach of the bones that rotation will slow.</summary>
public float Softness {
get { return softness; }
set { softness = value; }
}
/// <summary>Controls the bend direction of the IK bones, either 1 or -1.</summary>
public int BendDirection {
get { return bendDirection; }
set { bendDirection = value; }
}
/// <summary>
/// When true, and only a single bone is being constrained,
/// if the target is too close, the bone is scaled to reach it. </summary>
public bool Compress {
get { return compress; }
set { compress = value; }
}
/// <summary>
/// When true, if the target is out of range, the parent bone is scaled on the X axis to reach it.
/// If the bone has local nonuniform scale, stretching is not applied.</summary>
public bool Stretch {
get { return stretch; }
set { stretch = value; }
}
/// <summary>
/// When true, only a single bone is being constrained and Compress or Stretch is used,
/// the bone is scaled both on the X and Y axes.</summary>
public bool Uniform {
get { return uniform; }
set { uniform = value; }
}
}
}

View File

@@ -1,12 +0,0 @@
fileFormatVersion: 2
guid: 94ad1e9256073264785f806086a000ba
timeCreated: 1456265155
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,531 +0,0 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
using System;
using System.IO;
using System.Text;
using System.Collections;
using System.Globalization;
using System.Collections.Generic;
namespace Spine {
public static class Json {
public static object Deserialize (TextReader text) {
var parser = new SharpJson.JsonDecoder();
parser.parseNumbersAsFloat = true;
return parser.Decode(text.ReadToEnd());
}
}
}
/**
* Copyright (c) 2016 Adriano Tinoco d'Oliveira Rezende
*
* Based on the JSON parser by Patrick van Bergen
* http://techblog.procurios.nl/k/news/view/14605/14863/how-do-i-write-my-own-parser-(for-json).html
*
* Changes made:
*
* - Optimized parser speed (deserialize roughly near 3x faster than original)
* - Added support to handle lexer/parser error messages with line numbers
* - Added more fine grained control over type conversions during the parsing
* - Refactory API (Separate Lexer code from Parser code and the Encoder from Decoder)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
* and associated documentation files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all copies or substantial
* portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
* LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace SharpJson
{
class Lexer
{
public enum Token {
None,
Null,
True,
False,
Colon,
Comma,
String,
Number,
CurlyOpen,
CurlyClose,
SquaredOpen,
SquaredClose,
};
public bool hasError {
get {
return !success;
}
}
public int lineNumber {
get;
private set;
}
public bool parseNumbersAsFloat {
get;
set;
}
char[] json;
int index = 0;
bool success = true;
char[] stringBuffer = new char[4096];
public Lexer(string text)
{
Reset();
json = text.ToCharArray();
parseNumbersAsFloat = false;
}
public void Reset()
{
index = 0;
lineNumber = 1;
success = true;
}
public string ParseString()
{
int idx = 0;
StringBuilder builder = null;
SkipWhiteSpaces();
// "
char c = json[index++];
bool failed = false;
bool complete = false;
while (!complete && !failed) {
if (index == json.Length)
break;
c = json[index++];
if (c == '"') {
complete = true;
break;
} else if (c == '\\') {
if (index == json.Length)
break;
c = json[index++];
switch (c) {
case '"':
stringBuffer[idx++] = '"';
break;
case '\\':
stringBuffer[idx++] = '\\';
break;
case '/':
stringBuffer[idx++] = '/';
break;
case 'b':
stringBuffer[idx++] = '\b';
break;
case'f':
stringBuffer[idx++] = '\f';
break;
case 'n':
stringBuffer[idx++] = '\n';
break;
case 'r':
stringBuffer[idx++] = '\r';
break;
case 't':
stringBuffer[idx++] = '\t';
break;
case 'u':
int remainingLength = json.Length - index;
if (remainingLength >= 4) {
var hex = new string(json, index, 4);
// XXX: handle UTF
stringBuffer[idx++] = (char) Convert.ToInt32(hex, 16);
// skip 4 chars
index += 4;
} else {
failed = true;
}
break;
}
} else {
stringBuffer[idx++] = c;
}
if (idx >= stringBuffer.Length) {
if (builder == null)
builder = new StringBuilder();
builder.Append(stringBuffer, 0, idx);
idx = 0;
}
}
if (!complete) {
success = false;
return null;
}
if (builder != null)
return builder.ToString ();
else
return new string (stringBuffer, 0, idx);
}
string GetNumberString()
{
SkipWhiteSpaces();
int lastIndex = GetLastIndexOfNumber(index);
int charLength = (lastIndex - index) + 1;
var result = new string (json, index, charLength);
index = lastIndex + 1;
return result;
}
public float ParseFloatNumber()
{
float number;
var str = GetNumberString ();
if (!float.TryParse (str, NumberStyles.Float, CultureInfo.InvariantCulture, out number))
return 0;
return number;
}
public double ParseDoubleNumber()
{
double number;
var str = GetNumberString ();
if (!double.TryParse(str, NumberStyles.Any, CultureInfo.InvariantCulture, out number))
return 0;
return number;
}
int GetLastIndexOfNumber(int index)
{
int lastIndex;
for (lastIndex = index; lastIndex < json.Length; lastIndex++) {
char ch = json[lastIndex];
if ((ch < '0' || ch > '9') && ch != '+' && ch != '-'
&& ch != '.' && ch != 'e' && ch != 'E')
break;
}
return lastIndex - 1;
}
void SkipWhiteSpaces()
{
for (; index < json.Length; index++) {
char ch = json[index];
if (ch == '\n')
lineNumber++;
if (!char.IsWhiteSpace(json[index]))
break;
}
}
public Token LookAhead()
{
SkipWhiteSpaces();
int savedIndex = index;
return NextToken(json, ref savedIndex);
}
public Token NextToken()
{
SkipWhiteSpaces();
return NextToken(json, ref index);
}
static Token NextToken(char[] json, ref int index)
{
if (index == json.Length)
return Token.None;
char c = json[index++];
switch (c) {
case '{':
return Token.CurlyOpen;
case '}':
return Token.CurlyClose;
case '[':
return Token.SquaredOpen;
case ']':
return Token.SquaredClose;
case ',':
return Token.Comma;
case '"':
return Token.String;
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
case '-':
return Token.Number;
case ':':
return Token.Colon;
}
index--;
int remainingLength = json.Length - index;
// false
if (remainingLength >= 5) {
if (json[index] == 'f' &&
json[index + 1] == 'a' &&
json[index + 2] == 'l' &&
json[index + 3] == 's' &&
json[index + 4] == 'e') {
index += 5;
return Token.False;
}
}
// true
if (remainingLength >= 4) {
if (json[index] == 't' &&
json[index + 1] == 'r' &&
json[index + 2] == 'u' &&
json[index + 3] == 'e') {
index += 4;
return Token.True;
}
}
// null
if (remainingLength >= 4) {
if (json[index] == 'n' &&
json[index + 1] == 'u' &&
json[index + 2] == 'l' &&
json[index + 3] == 'l') {
index += 4;
return Token.Null;
}
}
return Token.None;
}
}
public class JsonDecoder
{
public string errorMessage {
get;
private set;
}
public bool parseNumbersAsFloat {
get;
set;
}
Lexer lexer;
public JsonDecoder()
{
errorMessage = null;
parseNumbersAsFloat = false;
}
public object Decode(string text)
{
errorMessage = null;
lexer = new Lexer(text);
lexer.parseNumbersAsFloat = parseNumbersAsFloat;
return ParseValue();
}
public static object DecodeText(string text)
{
var builder = new JsonDecoder();
return builder.Decode(text);
}
IDictionary<string, object> ParseObject()
{
var table = new Dictionary<string, object>();
// {
lexer.NextToken();
while (true) {
var token = lexer.LookAhead();
switch (token) {
case Lexer.Token.None:
TriggerError("Invalid token");
return null;
case Lexer.Token.Comma:
lexer.NextToken();
break;
case Lexer.Token.CurlyClose:
lexer.NextToken();
return table;
default:
// name
string name = EvalLexer(lexer.ParseString());
if (errorMessage != null)
return null;
// :
token = lexer.NextToken();
if (token != Lexer.Token.Colon) {
TriggerError("Invalid token; expected ':'");
return null;
}
// value
object value = ParseValue();
if (errorMessage != null)
return null;
table[name] = value;
break;
}
}
//return null; // Unreachable code
}
IList<object> ParseArray()
{
var array = new List<object>();
// [
lexer.NextToken();
while (true) {
var token = lexer.LookAhead();
switch (token) {
case Lexer.Token.None:
TriggerError("Invalid token");
return null;
case Lexer.Token.Comma:
lexer.NextToken();
break;
case Lexer.Token.SquaredClose:
lexer.NextToken();
return array;
default:
object value = ParseValue();
if (errorMessage != null)
return null;
array.Add(value);
break;
}
}
//return null; // Unreachable code
}
object ParseValue()
{
switch (lexer.LookAhead()) {
case Lexer.Token.String:
return EvalLexer(lexer.ParseString());
case Lexer.Token.Number:
if (parseNumbersAsFloat)
return EvalLexer(lexer.ParseFloatNumber());
else
return EvalLexer(lexer.ParseDoubleNumber());
case Lexer.Token.CurlyOpen:
return ParseObject();
case Lexer.Token.SquaredOpen:
return ParseArray();
case Lexer.Token.True:
lexer.NextToken();
return true;
case Lexer.Token.False:
lexer.NextToken();
return false;
case Lexer.Token.Null:
lexer.NextToken();
return null;
case Lexer.Token.None:
break;
}
TriggerError("Unable to parse value");
return null;
}
void TriggerError(string message)
{
errorMessage = string.Format("Error: '{0}' at line {1}",
message, lexer.lineNumber);
}
T EvalLexer<T>(T value)
{
if (lexer.hasError)
TriggerError("Lexical error ocurred");
return value;
}
}
}

View File

@@ -1,12 +0,0 @@
fileFormatVersion: 2
guid: 371f40ecc08b2eb4cbec49585d41e2c3
timeCreated: 1456265153
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,173 +0,0 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
//#define USE_FAST_SIN_COS_ATAN2_APPROXIMATIONS
using System;
namespace Spine {
public static class MathUtils {
public const float PI = 3.1415927f;
public const float PI2 = PI * 2;
public const float RadDeg = 180f / PI;
public const float DegRad = PI / 180;
static Random random = new Random();
#if USE_FAST_SIN_COS_ATAN2_APPROXIMATIONS
const int SIN_BITS = 14; // 16KB. Adjust for accuracy.
const int SIN_MASK = ~(-1 << SIN_BITS);
const int SIN_COUNT = SIN_MASK + 1;
const float RadFull = PI * 2;
const float DegFull = 360;
const float RadToIndex = SIN_COUNT / RadFull;
const float DegToIndex = SIN_COUNT / DegFull;
static float[] sin = new float[SIN_COUNT];
static MathUtils () {
for (int i = 0; i < SIN_COUNT; i++)
sin[i] = (float)Math.Sin((i + 0.5f) / SIN_COUNT * RadFull);
for (int i = 0; i < 360; i += 90)
sin[(int)(i * DegToIndex) & SIN_MASK] = (float)Math.Sin(i * DegRad);
}
/// <summary>Returns the sine of a given angle in radians from a lookup table.</summary>
static public float Sin (float radians) {
return sin[(int)(radians * RadToIndex) & SIN_MASK];
}
/// <summary>Returns the cosine of a given angle in radians from a lookup table.</summary>
static public float Cos (float radians) {
return sin[(int)((radians + PI / 2) * RadToIndex) & SIN_MASK];
}
/// <summary>Returns the sine of a given angle in degrees from a lookup table.</summary>
static public float SinDeg (float degrees) {
return sin[(int)(degrees * DegToIndex) & SIN_MASK];
}
/// <summary>Returns the cosine of a given angle in degrees from a lookup table.</summary>
static public float CosDeg (float degrees) {
return sin[(int)((degrees + 90) * DegToIndex) & SIN_MASK];
}
/// <summary>Returns atan2 in radians, faster but less accurate than Math.Atan2. Average error of 0.00231 radians (0.1323
/// degrees), largest error of 0.00488 radians (0.2796 degrees).</summary>
static public float Atan2 (float y, float x) {
if (x == 0f) {
if (y > 0f) return PI / 2;
if (y == 0f) return 0f;
return -PI / 2;
}
float atan, z = y / x;
if (Math.Abs(z) < 1f) {
atan = z / (1f + 0.28f * z * z);
if (x < 0f) return atan + (y < 0f ? -PI : PI);
return atan;
}
atan = PI / 2 - z / (z * z + 0.28f);
return y < 0f ? atan - PI : atan;
}
#else
/// <summary>Returns the sine of a given angle in radians.</summary>
static public float Sin (float radians) {
return (float)Math.Sin(radians);
}
/// <summary>Returns the cosine of a given angle in radians.</summary>
static public float Cos (float radians) {
return (float)Math.Cos(radians);
}
/// <summary>Returns the sine of a given angle in degrees.</summary>
static public float SinDeg (float degrees) {
return (float)Math.Sin(degrees * DegRad);
}
/// <summary>Returns the cosine of a given angle in degrees.</summary>
static public float CosDeg (float degrees) {
return (float)Math.Cos(degrees * DegRad);
}
/// <summary>Returns the atan2 using Math.Atan2.</summary>
static public float Atan2 (float y, float x) {
return (float)Math.Atan2(y, x);
}
#endif
static public float Clamp (float value, float min, float max) {
if (value < min) return min;
if (value > max) return max;
return value;
}
static public float RandomTriangle(float min, float max) {
return RandomTriangle(min, max, (min + max) * 0.5f);
}
static public float RandomTriangle(float min, float max, float mode) {
float u = (float)random.NextDouble();
float d = max - min;
if (u <= (mode - min) / d) return min + (float)Math.Sqrt(u * d * (mode - min));
return max - (float)Math.Sqrt((1 - u) * d * (max - mode));
}
}
public abstract class IInterpolation {
public static IInterpolation Pow2 = new Pow(2);
public static IInterpolation Pow2Out = new PowOut(2);
protected abstract float Apply(float a);
public float Apply(float start, float end, float a) {
return start + (end - start) * Apply(a);
}
}
public class Pow: IInterpolation {
public float Power { get; set; }
public Pow(float power) {
Power = power;
}
protected override float Apply(float a) {
if (a <= 0.5f) return (float)Math.Pow(a * 2, Power) / 2;
return (float)Math.Pow((a - 1) * 2, Power) / (Power % 2 == 0 ? -2 : 2) + 1;
}
}
public class PowOut : Pow {
public PowOut(float power) : base(power) {
}
protected override float Apply(float a) {
return (float)Math.Pow(a - 1, Power) * (Power % 2 == 0 ? -1 : 1) + 1;
}
}
}

View File

@@ -1,12 +0,0 @@
fileFormatVersion: 2
guid: 03b653e54c5403b4191f5003d64c6e18
timeCreated: 1456265153
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,467 +0,0 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
using System;
namespace Spine {
/// <summary>
/// <para>
/// Stores the current pose for a path constraint. A path constraint adjusts the rotation, translation, and scale of the
/// constrained bones so they follow a {@link PathAttachment}.</para>
/// <para>
/// See <a href="http://esotericsoftware.com/spine-path-constraints">Path constraints</a> in the Spine User Guide.</para>
/// </summary>
public class PathConstraint : IUpdatable {
const int NONE = -1, BEFORE = -2, AFTER = -3;
const float Epsilon = 0.00001f;
internal PathConstraintData data;
internal ExposedList<Bone> bones;
internal Slot target;
internal float position, spacing, rotateMix, translateMix;
internal bool active;
internal ExposedList<float> spaces = new ExposedList<float>(), positions = new ExposedList<float>();
internal ExposedList<float> world = new ExposedList<float>(), curves = new ExposedList<float>(), lengths = new ExposedList<float>();
internal float[] segments = new float[10];
public PathConstraint (PathConstraintData data, Skeleton skeleton) {
if (data == null) throw new ArgumentNullException("data", "data cannot be null.");
if (skeleton == null) throw new ArgumentNullException("skeleton", "skeleton cannot be null.");
this.data = data;
bones = new ExposedList<Bone>(data.Bones.Count);
foreach (BoneData boneData in data.bones)
bones.Add(skeleton.FindBone(boneData.name));
target = skeleton.FindSlot(data.target.name);
position = data.position;
spacing = data.spacing;
rotateMix = data.rotateMix;
translateMix = data.translateMix;
}
/// <summary>Copy constructor.</summary>
public PathConstraint (PathConstraint constraint, Skeleton skeleton) {
if (constraint == null) throw new ArgumentNullException("constraint cannot be null.");
if (skeleton == null) throw new ArgumentNullException("skeleton cannot be null.");
data = constraint.data;
bones = new ExposedList<Bone>(constraint.Bones.Count);
foreach (Bone bone in constraint.Bones)
bones.Add(skeleton.Bones.Items[bone.data.index]);
target = skeleton.slots.Items[constraint.target.data.index];
position = constraint.position;
spacing = constraint.spacing;
rotateMix = constraint.rotateMix;
translateMix = constraint.translateMix;
}
/// <summary>Applies the constraint to the constrained bones.</summary>
public void Apply () {
Update();
}
public void Update () {
PathAttachment attachment = target.Attachment as PathAttachment;
if (attachment == null) return;
float rotateMix = this.rotateMix, translateMix = this.translateMix;
bool translate = translateMix > 0, rotate = rotateMix > 0;
if (!translate && !rotate) return;
PathConstraintData data = this.data;
bool percentSpacing = data.spacingMode == SpacingMode.Percent;
RotateMode rotateMode = data.rotateMode;
bool tangents = rotateMode == RotateMode.Tangent, scale = rotateMode == RotateMode.ChainScale;
int boneCount = this.bones.Count, spacesCount = tangents ? boneCount : boneCount + 1;
Bone[] bonesItems = this.bones.Items;
ExposedList<float> spaces = this.spaces.Resize(spacesCount), lengths = null;
float spacing = this.spacing;
if (scale || !percentSpacing) {
if (scale) lengths = this.lengths.Resize(boneCount);
bool lengthSpacing = data.spacingMode == SpacingMode.Length;
for (int i = 0, n = spacesCount - 1; i < n;) {
Bone bone = bonesItems[i];
float setupLength = bone.data.length;
if (setupLength < PathConstraint.Epsilon) {
if (scale) lengths.Items[i] = 0;
spaces.Items[++i] = 0;
} else if (percentSpacing) {
if (scale) {
float x = setupLength * bone.a, y = setupLength * bone.c;
float length = (float)Math.Sqrt(x * x + y * y);
lengths.Items[i] = length;
}
spaces.Items[++i] = spacing;
} else {
float x = setupLength * bone.a, y = setupLength * bone.c;
float length = (float)Math.Sqrt(x * x + y * y);
if (scale) lengths.Items[i] = length;
spaces.Items[++i] = (lengthSpacing ? setupLength + spacing : spacing) * length / setupLength;
}
}
} else {
for (int i = 1; i < spacesCount; i++)
spaces.Items[i] = spacing;
}
float[] positions = ComputeWorldPositions(attachment, spacesCount, tangents,
data.positionMode == PositionMode.Percent, percentSpacing);
float boneX = positions[0], boneY = positions[1], offsetRotation = data.offsetRotation;
bool tip;
if (offsetRotation == 0) {
tip = rotateMode == RotateMode.Chain;
} else {
tip = false;
Bone p = target.bone;
offsetRotation *= p.a * p.d - p.b * p.c > 0 ? MathUtils.DegRad : -MathUtils.DegRad;
}
for (int i = 0, p = 3; i < boneCount; i++, p += 3) {
Bone bone = bonesItems[i];
bone.worldX += (boneX - bone.worldX) * translateMix;
bone.worldY += (boneY - bone.worldY) * translateMix;
float x = positions[p], y = positions[p + 1], dx = x - boneX, dy = y - boneY;
if (scale) {
float length = lengths.Items[i];
if (length >= PathConstraint.Epsilon) {
float s = ((float)Math.Sqrt(dx * dx + dy * dy) / length - 1) * rotateMix + 1;
bone.a *= s;
bone.c *= s;
}
}
boneX = x;
boneY = y;
if (rotate) {
float a = bone.a, b = bone.b, c = bone.c, d = bone.d, r, cos, sin;
if (tangents)
r = positions[p - 1];
else if (spaces.Items[i + 1] < PathConstraint.Epsilon)
r = positions[p + 2];
else
r = MathUtils.Atan2(dy, dx);
r -= MathUtils.Atan2(c, a);
if (tip) {
cos = MathUtils.Cos(r);
sin = MathUtils.Sin(r);
float length = bone.data.length;
boneX += (length * (cos * a - sin * c) - dx) * rotateMix;
boneY += (length * (sin * a + cos * c) - dy) * rotateMix;
} else
r += offsetRotation;
if (r > MathUtils.PI)
r -= MathUtils.PI2;
else if (r < -MathUtils.PI) //
r += MathUtils.PI2;
r *= rotateMix;
cos = MathUtils.Cos(r);
sin = MathUtils.Sin(r);
bone.a = cos * a - sin * c;
bone.b = cos * b - sin * d;
bone.c = sin * a + cos * c;
bone.d = sin * b + cos * d;
}
bone.appliedValid = false;
}
}
float[] ComputeWorldPositions (PathAttachment path, int spacesCount, bool tangents, bool percentPosition,
bool percentSpacing) {
Slot target = this.target;
float position = this.position;
float[] spacesItems = this.spaces.Items, output = this.positions.Resize(spacesCount * 3 + 2).Items, world;
bool closed = path.Closed;
int verticesLength = path.WorldVerticesLength, curveCount = verticesLength / 6, prevCurve = NONE;
float pathLength = 0;
if (!path.ConstantSpeed) {
float[] lengths = path.Lengths;
curveCount -= closed ? 1 : 2;
pathLength = lengths[curveCount];
if (percentPosition) position *= pathLength;
if (percentSpacing) {
for (int i = 1; i < spacesCount; i++)
spacesItems[i] *= pathLength;
}
world = this.world.Resize(8).Items;
for (int i = 0, o = 0, curve = 0; i < spacesCount; i++, o += 3) {
float space = spacesItems[i];
position += space;
float p = position;
if (closed) {
p %= pathLength;
if (p < 0) p += pathLength;
curve = 0;
} else if (p < 0) {
if (prevCurve != BEFORE) {
prevCurve = BEFORE;
path.ComputeWorldVertices(target, 2, 4, world, 0, 2);
}
AddBeforePosition(p, world, 0, output, o);
continue;
} else if (p > pathLength) {
if (prevCurve != AFTER) {
prevCurve = AFTER;
path.ComputeWorldVertices(target, verticesLength - 6, 4, world, 0, 2);
}
AddAfterPosition(p - pathLength, world, 0, output, o);
continue;
}
// Determine curve containing position.
for (;; curve++) {
float length = lengths[curve];
if (p > length) continue;
if (curve == 0)
p /= length;
else {
float prev = lengths[curve - 1];
p = (p - prev) / (length - prev);
}
break;
}
if (curve != prevCurve) {
prevCurve = curve;
if (closed && curve == curveCount) {
path.ComputeWorldVertices(target, verticesLength - 4, 4, world, 0, 2);
path.ComputeWorldVertices(target, 0, 4, world, 4, 2);
} else
path.ComputeWorldVertices(target, curve * 6 + 2, 8, world, 0, 2);
}
AddCurvePosition(p, world[0], world[1], world[2], world[3], world[4], world[5], world[6], world[7], output, o,
tangents || (i > 0 && space < PathConstraint.Epsilon));
}
return output;
}
// World vertices.
if (closed) {
verticesLength += 2;
world = this.world.Resize(verticesLength).Items;
path.ComputeWorldVertices(target, 2, verticesLength - 4, world, 0, 2);
path.ComputeWorldVertices(target, 0, 2, world, verticesLength - 4, 2);
world[verticesLength - 2] = world[0];
world[verticesLength - 1] = world[1];
} else {
curveCount--;
verticesLength -= 4;
world = this.world.Resize(verticesLength).Items;
path.ComputeWorldVertices(target, 2, verticesLength, world, 0, 2);
}
// Curve lengths.
float[] curves = this.curves.Resize(curveCount).Items;
pathLength = 0;
float x1 = world[0], y1 = world[1], cx1 = 0, cy1 = 0, cx2 = 0, cy2 = 0, x2 = 0, y2 = 0;
float tmpx, tmpy, dddfx, dddfy, ddfx, ddfy, dfx, dfy;
for (int i = 0, w = 2; i < curveCount; i++, w += 6) {
cx1 = world[w];
cy1 = world[w + 1];
cx2 = world[w + 2];
cy2 = world[w + 3];
x2 = world[w + 4];
y2 = world[w + 5];
tmpx = (x1 - cx1 * 2 + cx2) * 0.1875f;
tmpy = (y1 - cy1 * 2 + cy2) * 0.1875f;
dddfx = ((cx1 - cx2) * 3 - x1 + x2) * 0.09375f;
dddfy = ((cy1 - cy2) * 3 - y1 + y2) * 0.09375f;
ddfx = tmpx * 2 + dddfx;
ddfy = tmpy * 2 + dddfy;
dfx = (cx1 - x1) * 0.75f + tmpx + dddfx * 0.16666667f;
dfy = (cy1 - y1) * 0.75f + tmpy + dddfy * 0.16666667f;
pathLength += (float)Math.Sqrt(dfx * dfx + dfy * dfy);
dfx += ddfx;
dfy += ddfy;
ddfx += dddfx;
ddfy += dddfy;
pathLength += (float)Math.Sqrt(dfx * dfx + dfy * dfy);
dfx += ddfx;
dfy += ddfy;
pathLength += (float)Math.Sqrt(dfx * dfx + dfy * dfy);
dfx += ddfx + dddfx;
dfy += ddfy + dddfy;
pathLength += (float)Math.Sqrt(dfx * dfx + dfy * dfy);
curves[i] = pathLength;
x1 = x2;
y1 = y2;
}
if (percentPosition)
position *= pathLength;
else
position *= pathLength / path.lengths[curveCount - 1];
if (percentSpacing) {
for (int i = 1; i < spacesCount; i++)
spacesItems[i] *= pathLength;
}
float[] segments = this.segments;
float curveLength = 0;
for (int i = 0, o = 0, curve = 0, segment = 0; i < spacesCount; i++, o += 3) {
float space = spacesItems[i];
position += space;
float p = position;
if (closed) {
p %= pathLength;
if (p < 0) p += pathLength;
curve = 0;
} else if (p < 0) {
AddBeforePosition(p, world, 0, output, o);
continue;
} else if (p > pathLength) {
AddAfterPosition(p - pathLength, world, verticesLength - 4, output, o);
continue;
}
// Determine curve containing position.
for (;; curve++) {
float length = curves[curve];
if (p > length) continue;
if (curve == 0)
p /= length;
else {
float prev = curves[curve - 1];
p = (p - prev) / (length - prev);
}
break;
}
// Curve segment lengths.
if (curve != prevCurve) {
prevCurve = curve;
int ii = curve * 6;
x1 = world[ii];
y1 = world[ii + 1];
cx1 = world[ii + 2];
cy1 = world[ii + 3];
cx2 = world[ii + 4];
cy2 = world[ii + 5];
x2 = world[ii + 6];
y2 = world[ii + 7];
tmpx = (x1 - cx1 * 2 + cx2) * 0.03f;
tmpy = (y1 - cy1 * 2 + cy2) * 0.03f;
dddfx = ((cx1 - cx2) * 3 - x1 + x2) * 0.006f;
dddfy = ((cy1 - cy2) * 3 - y1 + y2) * 0.006f;
ddfx = tmpx * 2 + dddfx;
ddfy = tmpy * 2 + dddfy;
dfx = (cx1 - x1) * 0.3f + tmpx + dddfx * 0.16666667f;
dfy = (cy1 - y1) * 0.3f + tmpy + dddfy * 0.16666667f;
curveLength = (float)Math.Sqrt(dfx * dfx + dfy * dfy);
segments[0] = curveLength;
for (ii = 1; ii < 8; ii++) {
dfx += ddfx;
dfy += ddfy;
ddfx += dddfx;
ddfy += dddfy;
curveLength += (float)Math.Sqrt(dfx * dfx + dfy * dfy);
segments[ii] = curveLength;
}
dfx += ddfx;
dfy += ddfy;
curveLength += (float)Math.Sqrt(dfx * dfx + dfy * dfy);
segments[8] = curveLength;
dfx += ddfx + dddfx;
dfy += ddfy + dddfy;
curveLength += (float)Math.Sqrt(dfx * dfx + dfy * dfy);
segments[9] = curveLength;
segment = 0;
}
// Weight by segment length.
p *= curveLength;
for (;; segment++) {
float length = segments[segment];
if (p > length) continue;
if (segment == 0)
p /= length;
else {
float prev = segments[segment - 1];
p = segment + (p - prev) / (length - prev);
}
break;
}
AddCurvePosition(p * 0.1f, x1, y1, cx1, cy1, cx2, cy2, x2, y2, output, o, tangents || (i > 0 && space < PathConstraint.Epsilon));
}
return output;
}
static void AddBeforePosition (float p, float[] temp, int i, float[] output, int o) {
float x1 = temp[i], y1 = temp[i + 1], dx = temp[i + 2] - x1, dy = temp[i + 3] - y1, r = MathUtils.Atan2(dy, dx);
output[o] = x1 + p * MathUtils.Cos(r);
output[o + 1] = y1 + p * MathUtils.Sin(r);
output[o + 2] = r;
}
static void AddAfterPosition (float p, float[] temp, int i, float[] output, int o) {
float x1 = temp[i + 2], y1 = temp[i + 3], dx = x1 - temp[i], dy = y1 - temp[i + 1], r = MathUtils.Atan2(dy, dx);
output[o] = x1 + p * MathUtils.Cos(r);
output[o + 1] = y1 + p * MathUtils.Sin(r);
output[o + 2] = r;
}
static void AddCurvePosition (float p, float x1, float y1, float cx1, float cy1, float cx2, float cy2, float x2, float y2,
float[] output, int o, bool tangents) {
if (p < PathConstraint.Epsilon || float.IsNaN(p)) {
output[o] = x1;
output[o + 1] = y1;
output[o + 2] = (float)Math.Atan2(cy1 - y1, cx1 - x1);
return;
}
float tt = p * p, ttt = tt * p, u = 1 - p, uu = u * u, uuu = uu * u;
float ut = u * p, ut3 = ut * 3, uut3 = u * ut3, utt3 = ut3 * p;
float x = x1 * uuu + cx1 * uut3 + cx2 * utt3 + x2 * ttt, y = y1 * uuu + cy1 * uut3 + cy2 * utt3 + y2 * ttt;
output[o] = x;
output[o + 1] = y;
if (tangents) {
if (p < 0.001f)
output[o + 2] = (float)Math.Atan2(cy1 - y1, cx1 - x1);
else
output[o + 2] = (float)Math.Atan2(y - (y1 * uu + cy1 * ut * 2 + cy2 * tt), x - (x1 * uu + cx1 * ut * 2 + cx2 * tt));
}
}
/// <summary>The position along the path.</summary>
public float Position { get { return position; } set { position = value; } }
/// <summary>The spacing between bones.</summary>
public float Spacing { get { return spacing; } set { spacing = value; } }
/// <summary>A percentage (0-1) that controls the mix between the constrained and unconstrained rotations.</summary>
public float RotateMix { get { return rotateMix; } set { rotateMix = value; } }
/// <summary>A percentage (0-1) that controls the mix between the constrained and unconstrained translations.</summary>
public float TranslateMix { get { return translateMix; } set { translateMix = value; } }
/// <summary>The bones that will be modified by this path constraint.</summary>
public ExposedList<Bone> Bones { get { return bones; } }
/// <summary>The slot whose path attachment will be used to constrained the bones.</summary>
public Slot Target { get { return target; } set { target = value; } }
public bool Active { get { return active; } }
/// <summary>The path constraint's setup pose data.</summary>
public PathConstraintData Data { get { return data; } }
}
}

View File

@@ -1,12 +0,0 @@
fileFormatVersion: 2
guid: 731d05fbc2874c74984813ce4c5bb8df
timeCreated: 1467213650
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,68 +0,0 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
using System;
namespace Spine {
public class PathConstraintData : ConstraintData {
internal ExposedList<BoneData> bones = new ExposedList<BoneData>();
internal SlotData target;
internal PositionMode positionMode;
internal SpacingMode spacingMode;
internal RotateMode rotateMode;
internal float offsetRotation;
internal float position, spacing, rotateMix, translateMix;
public PathConstraintData (string name) : base(name) {
}
public ExposedList<BoneData> Bones { get { return bones; } }
public SlotData Target { get { return target; } set { target = value; } }
public PositionMode PositionMode { get { return positionMode; } set { positionMode = value; } }
public SpacingMode SpacingMode { get { return spacingMode; } set { spacingMode = value; } }
public RotateMode RotateMode { get { return rotateMode; } set { rotateMode = value; } }
public float OffsetRotation { get { return offsetRotation; } set { offsetRotation = value; } }
public float Position { get { return position; } set { position = value; } }
public float Spacing { get { return spacing; } set { spacing = value; } }
public float RotateMix { get { return rotateMix; } set { rotateMix = value; } }
public float TranslateMix { get { return translateMix; } set { translateMix = value; } }
}
public enum PositionMode {
Fixed, Percent
}
public enum SpacingMode {
Length, Fixed, Percent
}
public enum RotateMode {
Tangent, Chain, ChainScale
}
}

View File

@@ -1,12 +0,0 @@
fileFormatVersion: 2
guid: 9d836858269be96428428fb6764dfc3a
timeCreated: 1467213651
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,83 +0,0 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
using System;
using UnityEngine;
namespace Spine.Unity {
public class RegionlessAttachmentLoader : AttachmentLoader {
static AtlasRegion emptyRegion;
static AtlasRegion EmptyRegion {
get {
if (emptyRegion == null) {
emptyRegion = new AtlasRegion {
name = "Empty AtlasRegion",
page = new AtlasPage {
name = "Empty AtlasPage",
rendererObject = new Material(Shader.Find("Spine/Special/HiddenPass")) { name = "NoRender Material" }
}
};
}
return emptyRegion;
}
}
public RegionAttachment NewRegionAttachment (Skin skin, string name, string path) {
RegionAttachment attachment = new RegionAttachment(name) {
RendererObject = EmptyRegion
};
return attachment;
}
public MeshAttachment NewMeshAttachment (Skin skin, string name, string path) {
MeshAttachment attachment = new MeshAttachment(name) {
RendererObject = EmptyRegion
};
return attachment;
}
public BoundingBoxAttachment NewBoundingBoxAttachment (Skin skin, string name) {
return new BoundingBoxAttachment(name);
}
public PathAttachment NewPathAttachment (Skin skin, string name) {
return new PathAttachment(name);
}
public PointAttachment NewPointAttachment (Skin skin, string name) {
return new PointAttachment(name);
}
public ClippingAttachment NewClippingAttachment (Skin skin, string name) {
return new ClippingAttachment(name);
}
}
}

View File

@@ -1,12 +0,0 @@
fileFormatVersion: 2
guid: 15f0f78b87720c047a320c5e0e3f91b7
timeCreated: 1520505662
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,164 +0,0 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#if UNITY_2018_3 || UNITY_2019 || UNITY_2018_3_OR_NEWER
#define NEW_PREFAB_SYSTEM
#endif
using UnityEngine;
namespace Spine.Unity {
#if NEW_PREFAB_SYSTEM
[ExecuteAlways]
#else
[ExecuteInEditMode]
#endif
[AddComponentMenu("Spine/Point Follower")]
[HelpURL("http://esotericsoftware.com/spine-unity#PointFollower")]
public class PointFollower : MonoBehaviour, IHasSkeletonRenderer, IHasSkeletonComponent {
public SkeletonRenderer skeletonRenderer;
public SkeletonRenderer SkeletonRenderer { get { return this.skeletonRenderer; } }
public ISkeletonComponent SkeletonComponent { get { return skeletonRenderer as ISkeletonComponent; } }
[SpineSlot(dataField:"skeletonRenderer", includeNone: true)]
public string slotName;
[SpineAttachment(slotField:"slotName", dataField: "skeletonRenderer", fallbackToTextField:true, includeNone: true)]
public string pointAttachmentName;
public bool followRotation = true;
public bool followSkeletonFlip = true;
public bool followSkeletonZPosition = false;
Transform skeletonTransform;
bool skeletonTransformIsParent;
PointAttachment point;
Bone bone;
bool valid;
public bool IsValid { get { return valid; } }
public void Initialize () {
valid = skeletonRenderer != null && skeletonRenderer.valid;
if (!valid)
return;
UpdateReferences();
#if UNITY_EDITOR
if (Application.isEditor) LateUpdate();
#endif
}
private void HandleRebuildRenderer (SkeletonRenderer skeletonRenderer) {
Initialize();
}
void UpdateReferences () {
skeletonTransform = skeletonRenderer.transform;
skeletonRenderer.OnRebuild -= HandleRebuildRenderer;
skeletonRenderer.OnRebuild += HandleRebuildRenderer;
skeletonTransformIsParent = Transform.ReferenceEquals(skeletonTransform, transform.parent);
bone = null;
point = null;
if (!string.IsNullOrEmpty(pointAttachmentName)) {
var skeleton = skeletonRenderer.Skeleton;
int slotIndex = skeleton.FindSlotIndex(slotName);
if (slotIndex >= 0) {
var slot = skeleton.slots.Items[slotIndex];
bone = slot.bone;
point = skeleton.GetAttachment(slotIndex, pointAttachmentName) as PointAttachment;
}
}
}
void OnDestroy () {
if (skeletonRenderer != null)
skeletonRenderer.OnRebuild -= HandleRebuildRenderer;
}
public void LateUpdate () {
#if UNITY_EDITOR
if (!Application.isPlaying) skeletonTransformIsParent = Transform.ReferenceEquals(skeletonTransform, transform.parent);
#endif
if (point == null) {
if (string.IsNullOrEmpty(pointAttachmentName)) return;
UpdateReferences();
if (point == null) return;
}
Vector2 worldPos;
point.ComputeWorldPosition(bone, out worldPos.x, out worldPos.y);
float rotation = point.ComputeWorldRotation(bone);
Transform thisTransform = this.transform;
if (skeletonTransformIsParent) {
// Recommended setup: Use local transform properties if Spine GameObject is the immediate parent
thisTransform.localPosition = new Vector3(worldPos.x, worldPos.y, followSkeletonZPosition ? 0f : thisTransform.localPosition.z);
if (followRotation) {
float halfRotation = rotation * 0.5f * Mathf.Deg2Rad;
var q = default(Quaternion);
q.z = Mathf.Sin(halfRotation);
q.w = Mathf.Cos(halfRotation);
thisTransform.localRotation = q;
}
} else {
// For special cases: Use transform world properties if transform relationship is complicated
Vector3 targetWorldPosition = skeletonTransform.TransformPoint(new Vector3(worldPos.x, worldPos.y, 0f));
if (!followSkeletonZPosition)
targetWorldPosition.z = thisTransform.position.z;
Transform transformParent = thisTransform.parent;
if (transformParent != null) {
Matrix4x4 m = transformParent.localToWorldMatrix;
if (m.m00 * m.m11 - m.m01 * m.m10 < 0) // Determinant2D is negative
rotation = -rotation;
}
if (followRotation) {
Vector3 transformWorldRotation = skeletonTransform.rotation.eulerAngles;
thisTransform.SetPositionAndRotation(targetWorldPosition, Quaternion.Euler(transformWorldRotation.x, transformWorldRotation.y, transformWorldRotation.z + rotation));
} else {
thisTransform.position = targetWorldPosition;
}
}
if (followSkeletonFlip) {
Vector3 localScale = thisTransform.localScale;
localScale.y = Mathf.Abs(localScale.y) * Mathf.Sign(bone.skeleton.ScaleX * bone.skeleton.ScaleY);
thisTransform.localScale = localScale;
}
}
}
}

View File

@@ -1,12 +0,0 @@
fileFormatVersion: 2
guid: daf461e4341180341a648c07e1899528
timeCreated: 1518094986
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,9 +0,0 @@
fileFormatVersion: 2
guid: 2b957aa69dae9f948bacdeec549d28ea
folderAsset: yes
timeCreated: 1593173800
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,81 +0,0 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
namespace Spine.Unity {
public enum UpdateMode {
Nothing = 0,
OnlyAnimationStatus,
OnlyEventTimelines = 4, // added as index 4 to keep scene behavior unchanged.
EverythingExceptMesh = 2,
FullUpdate,
//Reserved 4 for OnlyEventTimelines
};
public delegate void UpdateBonesDelegate (ISkeletonAnimation animated);
/// <summary>A Spine-Unity Component that animates a Skeleton but not necessarily with a Spine.AnimationState.</summary>
public interface ISkeletonAnimation {
event UpdateBonesDelegate UpdateLocal;
event UpdateBonesDelegate UpdateWorld;
event UpdateBonesDelegate UpdateComplete;
Skeleton Skeleton { get; }
}
/// <summary>Holds a reference to a SkeletonDataAsset.</summary>
public interface IHasSkeletonDataAsset {
/// <summary>Gets the SkeletonDataAsset of the Spine Component.</summary>
SkeletonDataAsset SkeletonDataAsset { get; }
}
/// <summary>A Spine-Unity Component that manages a Spine.Skeleton instance, instantiated from a SkeletonDataAsset.</summary>
public interface ISkeletonComponent {
/// <summary>Gets the SkeletonDataAsset of the Spine Component.</summary>
//[System.Obsolete]
SkeletonDataAsset SkeletonDataAsset { get; }
/// <summary>Gets the Spine.Skeleton instance of the Spine Component. This is equivalent to SkeletonRenderer's .skeleton.</summary>
Skeleton Skeleton { get; }
}
/// <summary>A Spine-Unity Component that uses a Spine.AnimationState to animate its skeleton.</summary>
public interface IAnimationStateComponent {
/// <summary>Gets the Spine.AnimationState of the animated Spine Component. This is equivalent to SkeletonAnimation.state.</summary>
AnimationState AnimationState { get; }
}
/// <summary>A Spine-Unity Component that holds a reference to a SkeletonRenderer.</summary>
public interface IHasSkeletonRenderer {
SkeletonRenderer SkeletonRenderer { get; }
}
/// <summary>A Spine-Unity Component that holds a reference to an ISkeletonComponent.</summary>
public interface IHasSkeletonComponent {
ISkeletonComponent SkeletonComponent { get; }
}
}

View File

@@ -1,10 +0,0 @@
fileFormatVersion: 2
guid: a7b480b941568134891f411137bfbf55
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,9 +0,0 @@
fileFormatVersion: 2
guid: 43897010c7e77c54897cb98c1ddf84f1
folderAsset: yes
timeCreated: 1455128695
licenseType: Free
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,9 +0,0 @@
fileFormatVersion: 2
guid: c5d065c4fe677ad4495a852580ec32fa
folderAsset: yes
timeCreated: 1455493477
licenseType: Free
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,12 +0,0 @@
fileFormatVersion: 2
guid: 311447d6e56727c4dad7611d5fa5afbf
timeCreated: 1563322425
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,135 +0,0 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
// Not for optimization. Do not disable.
#define SPINE_TRIANGLECHECK // Avoid calling SetTriangles at the cost of checking for mesh differences (vertex counts, memberwise attachment list compare) every frame.
//#define SPINE_DEBUG
using UnityEngine;
using System;
using System.Collections.Generic;
namespace Spine.Unity {
/// <summary>A double-buffered Mesh, and a shared material array, bundled for use by Spine components that need to push a Mesh and materials to a Unity MeshRenderer and MeshFilter.</summary>
public class MeshRendererBuffers : IDisposable {
DoubleBuffered<SmartMesh> doubleBufferedMesh;
internal readonly ExposedList<Material> submeshMaterials = new ExposedList<Material>();
internal Material[] sharedMaterials = new Material[0];
public void Initialize () {
if (doubleBufferedMesh != null) {
doubleBufferedMesh.GetNext().Clear();
doubleBufferedMesh.GetNext().Clear();
submeshMaterials.Clear();
} else {
doubleBufferedMesh = new DoubleBuffered<SmartMesh>();
}
}
/// <summary>Returns a sharedMaterials array for use on a MeshRenderer.</summary>
/// <returns></returns>
public Material[] GetUpdatedSharedMaterialsArray () {
if (submeshMaterials.Count == sharedMaterials.Length)
submeshMaterials.CopyTo(sharedMaterials);
else
sharedMaterials = submeshMaterials.ToArray();
return sharedMaterials;
}
/// <summary>Returns true if the materials were modified since the buffers were last updated.</summary>
public bool MaterialsChangedInLastUpdate () {
int newSubmeshMaterials = submeshMaterials.Count;
var sharedMaterials = this.sharedMaterials;
if (newSubmeshMaterials != sharedMaterials.Length) return true;
var submeshMaterialsItems = submeshMaterials.Items;
for (int i = 0; i < newSubmeshMaterials; i++)
if (!Material.ReferenceEquals(submeshMaterialsItems[i], sharedMaterials[i])) return true; //if (submeshMaterialsItems[i].GetInstanceID() != sharedMaterials[i].GetInstanceID()) return true;
return false;
}
/// <summary>Updates the internal shared materials array with the given instruction list.</summary>
public void UpdateSharedMaterials (ExposedList<SubmeshInstruction> instructions) {
int newSize = instructions.Count;
{ //submeshMaterials.Resize(instructions.Count);
if (newSize > submeshMaterials.Items.Length)
Array.Resize(ref submeshMaterials.Items, newSize);
submeshMaterials.Count = newSize;
}
var submeshMaterialsItems = submeshMaterials.Items;
var instructionsItems = instructions.Items;
for (int i = 0; i < newSize; i++)
submeshMaterialsItems[i] = instructionsItems[i].material;
}
public SmartMesh GetNextMesh () {
return doubleBufferedMesh.GetNext();
}
public void Clear () {
sharedMaterials = new Material[0];
submeshMaterials.Clear();
}
public void Dispose () {
if (doubleBufferedMesh == null) return;
doubleBufferedMesh.GetNext().Dispose();
doubleBufferedMesh.GetNext().Dispose();
doubleBufferedMesh = null;
}
///<summary>This is a Mesh that also stores the instructions SkeletonRenderer generated for it.</summary>
public class SmartMesh : IDisposable {
public Mesh mesh = SpineMesh.NewSkeletonMesh();
public SkeletonRendererInstruction instructionUsed = new SkeletonRendererInstruction();
public void Clear () {
mesh.Clear();
instructionUsed.Clear();
}
public void Dispose () {
if (mesh != null) {
#if UNITY_EDITOR
if (Application.isEditor && !Application.isPlaying)
UnityEngine.Object.DestroyImmediate(mesh);
else
UnityEngine.Object.Destroy(mesh);
#else
UnityEngine.Object.Destroy(mesh);
#endif
}
mesh = null;
}
}
}
}

View File

@@ -1,12 +0,0 @@
fileFormatVersion: 2
guid: b1ab80744ac17724dbc0d15fdb6f4727
timeCreated: 1563322425
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,9 +0,0 @@
fileFormatVersion: 2
guid: a7935399edad9a14bb5708cf59c94b67
folderAsset: yes
timeCreated: 1455489260
licenseType: Free
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,9 +0,0 @@
fileFormatVersion: 2
guid: e8c87a44b93daed4383ca2ca5dfb3c43
folderAsset: yes
timeCreated: 1573827942
licenseType: Free
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,46 +0,0 @@
Contributed by ToddRivers
# Unity Sprite Shaders
An Uber Shader specialised for rendering Sprites in Unity.
Even though it's designed for Sprites it can be used for a whole range of uses. It supports a wide range of optional shader features that won't effect performance unless they are used.
It also supports per-pixel effects such as normal maps and diffuse ramping whilst using Vertex Lit rendering.
### Lighting
The shaders support lighting using both Forward Rendering and Vertex Lit Rendering.
Forward rendering is more accurate but is slower and crucially means the sprite has to write to depth using alpha clipping to avoid overdraw.
Vertex lit means all lighting can be done in one pass meaning full alpha can be used.
### Normal Mapping
Normals maps are supported in both lighting modes (in Vertex Lit rendering data for normal mapping is packed into texture channels and then processed per pixel).
### Blend Modes
Easily switch between blend modes including pre-multiplied alpha, additive, multiply etc.
### Rim Lighting
Camera-space rim lighting is supported in both lighting modes.
### Diffuse Ramp
A ramp texture is optionally supported for toon shading effects.
### Shadows
Shadows are supported using alpha clipping.
### Gradient based Ambient lighting
Both lighting modes support using a gradient for ambient light. In Vertex Lit mode the Spherical Harmonics is approximated from the ground, equator and sky colors.
### Emission Map
An optional emission map is supported.
### Camera Space Normals
As sprites are 2d their normals will always be constant. The shaders allow you to define a fixed normal in camera space rather than pass through mesh normals.
This not only saves vertex throughput but means lighting looks less 'flat' for rendering sprites with a perspective camera.
### Color Adjustment
The shaders allow optional adjustment of hue / saturation and brightness as well as applying a solid color overlay effect for flashing a sprite to a solid color (eg. for damage effects).
### Fog
Fog is optionally supported
## To Use
On your object's material click the drop down for shader and select Spine\Sprite\Pixel Lit, Vertex Lit or Unlit.

View File

@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: cecd0ea162097a94c89a97af6baf0a66
timeCreated: 1479457854
licenseType: Free
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,66 +0,0 @@
Shader "Hidden/Spine/Bones" {
Properties {
_Color ("Color", Color) = (0.5,0.5,0.5,0.5)
_MainTex ("Particle Texture", 2D) = "white" {}
}
Category {
Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" }
Blend SrcAlpha OneMinusSrcAlpha
AlphaTest Greater .01
ColorMask RGB
Lighting Off Cull Off ZTest Always ZWrite Off Fog { Mode Off }
SubShader {
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
// #pragma multi_compile_particles
#include "UnityCG.cginc"
sampler2D _MainTex;
fixed4 _Color;
struct appdata_t {
float4 vertex : POSITION;
fixed4 color : COLOR;
float2 texcoord : TEXCOORD0;
};
struct v2f {
float4 vertex : SV_POSITION;
fixed4 color : COLOR;
float2 texcoord : TEXCOORD0;
// #ifdef SOFTPARTICLES_ON
// float4 projPos : TEXCOORD1;
// #endif
};
float4 _MainTex_ST;
v2f vert (appdata_t v) {
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
// #ifdef SOFTPARTICLES_ON
// o.projPos = ComputeScreenPos (o.vertex);
// COMPUTE_EYEDEPTH(o.projPos.z);
// #endif
o.color = v.color;
o.texcoord = TRANSFORM_TEX(v.texcoord,_MainTex);
return o;
}
sampler2D_float _CameraDepthTexture;
fixed4 frag (v2f i) : SV_Target {
return i.color * _Color * tex2D(_MainTex, i.texcoord);
}
ENDCG
}
}
}
}

View File

@@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: 66988de88a15abd4e8846c6805485f57
ShaderImporter:
defaultTextures: []
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,37 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!21 &2100000
Material:
serializedVersion: 6
m_ObjectHideFlags: 0
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 0}
m_Name: HiddenPass
m_Shader: {fileID: 4800000, guid: 913475501bf19374c84390868a9d6d3d, type: 3}
m_ShaderKeywords:
m_LightmapFlags: 5
m_CustomRenderQueue: -1
stringTagMap: {}
m_SavedProperties:
serializedVersion: 2
m_TexEnvs:
- first:
name: _MainTex
second:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
m_Floats:
- first:
name: _Cutoff
second: 0.1
- first:
name: _InvFade
second: 1
m_Colors:
- first:
name: _Color
second: {r: 1, g: 1, b: 1, a: 1}
- first:
name: _TintColor
second: {r: 0.5, g: 0.5, b: 0.5, a: 0}

View File

@@ -1,6 +0,0 @@
fileFormatVersion: 2
guid: 43227e5adadc6f24bb4bf74b92a56fb4
NativeFormatImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,12 +0,0 @@
Shader "Spine/Special/HiddenPass" {
SubShader
{
Tags {"Queue" = "Geometry-1" }
Lighting Off
Pass
{
ZWrite Off
ColorMask 0
}
}
}

View File

@@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: 913475501bf19374c84390868a9d6d3d
ShaderImporter:
defaultTextures: []
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,324 +0,0 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
using System.Collections.Generic;
using UnityEngine;
#if UNITY_EDITOR
namespace Spine.Unity {
/// <summary>Utility class providing methods to check material settings for incorrect combinations.</summary>
public class MaterialChecks {
static readonly int STRAIGHT_ALPHA_PARAM_ID = Shader.PropertyToID("_StraightAlphaInput");
static readonly string ALPHAPREMULTIPLY_ON_KEYWORD = "_ALPHAPREMULTIPLY_ON";
static readonly string STRAIGHT_ALPHA_KEYWORD = "_STRAIGHT_ALPHA_INPUT";
static readonly string[] FIXED_NORMALS_KEYWORDS = {
"_FIXED_NORMALS_VIEWSPACE",
"_FIXED_NORMALS_VIEWSPACE_BACKFACE",
"_FIXED_NORMALS_MODELSPACE",
"_FIXED_NORMALS_MODELSPACE_BACKFACE",
"_FIXED_NORMALS_WORLDSPACE"
};
static readonly string NORMALMAP_KEYWORD = "_NORMALMAP";
static readonly string CANVAS_GROUP_COMPATIBLE_KEYWORD = "_CANVAS_GROUP_COMPATIBLE";
public static readonly string kPMANotSupportedLinearMessage =
"\nWarning: Premultiply-alpha atlas textures not supported in Linear color space!\n\nPlease\n"
+ "a) re-export atlas as straight alpha texture with 'premultiply alpha' unchecked\n"
+ " (if you have already done this, please set the 'Straight Alpha Texture' Material parameter to 'true') or\n"
+ "b) switch to Gamma color space via\nProject Settings - Player - Other Settings - Color Space.\n";
public static readonly string kZSpacingRequiredMessage =
"\nWarning: Z Spacing required on selected shader! Otherwise you will receive incorrect results.\n\nPlease\n"
+ "1) make sure at least minimal 'Z Spacing' is set at the SkeletonRenderer/SkeletonAnimation component under 'Advanced' and\n"
+ "2) ensure that the skeleton has overlapping parts on different Z depth. You can adjust this in Spine via draw order.\n";
public static readonly string kZSpacingRecommendedMessage =
"\nWarning: Z Spacing recommended on selected shader configuration!\n\nPlease\n"
+ "1) make sure at least minimal 'Z Spacing' is set at the SkeletonRenderer/SkeletonAnimation component under 'Advanced' and\n"
+ "2) ensure that the skeleton has overlapping parts on different Z depth. You can adjust this in Spine via draw order.\n";
public static readonly string kAddNormalsMessage =
"\nWarning: 'Add Normals' required when not using 'Fixed Normals'!\n\nPlease\n"
+ "a) enable 'Add Normals' at the SkeletonRenderer/SkeletonAnimation component under 'Advanced' or\n"
+ "b) enable 'Fixed Normals' at the Material.\n";
public static readonly string kSolveTangentsMessage =
"\nWarning: 'Solve Tangents' required when using a Normal Map!\n\nPlease\n"
+ "a) enable 'Solve Tangents' at the SkeletonRenderer/SkeletonAnimation component under 'Advanced' or\n"
+ "b) clear the 'Normal Map' parameter at the Material.\n";
public static readonly string kNoSkeletonGraphicMaterialMessage =
"\nWarning: Normal non-UI shaders other than 'Spine/SkeletonGraphic *' are not compatible with 'SkeletonGraphic' components! "
+ "This will lead to incorrect rendering on some devices.\n\n"
+ "Please change the assigned Material to e.g. 'SkeletonGraphicDefault' or change the used shader to one of the 'Spine/SkeletonGraphic *' shaders.\n\n"
+ "Note that 'Spine/SkeletonGraphic *' shall still be used when using URP.\n";
public static readonly string kNoSkeletonGraphicTintBlackMaterialMessage =
"\nWarning: Only enable 'Canvas Group Tint Black' when using a 'SkeletonGraphic Tint Black' shader!\n"
+ "This will lead to incorrect rendering.\n\nPlease\n"
+ "a) disable 'Canvas Group Tint Black' under 'Advanced' or\n"
+ "b) use a 'SkeletonGraphic Tint Black' Material if you need Tint Black on a CanvasGroup.\n";
public static readonly string kTintBlackMessage =
"\nWarning: 'Advanced - Tint Black' required when using any 'Tint Black' shader!\n\nPlease\n"
+ "a) enable 'Tint Black' at the SkeletonRenderer/SkeletonGraphic component under 'Advanced' or\n"
+ "b) use a different shader at the Material.\n";
public static readonly string kCanvasTintBlackMessage =
"\nWarning: Canvas 'Additional Shader Channels' 'uv1' and 'uv2' are required when 'Advanced - Tint Black' is enabled!\n\n"
+ "Please enable both 'uv1' and 'uv2' channels at the parent Canvas component parameter 'Additional Shader Channels'.\n";
public static readonly string kCanvasGroupCompatibleMessage =
"\nWarning: 'Canvas Group Tint Black' is enabled at SkeletonGraphic but not 'CanvasGroup Compatible' at the Material!\n\nPlease\n"
+ "a) enable 'CanvasGroup Compatible' at the Material or\n"
+ "b) disable 'Canvas Group Tint Black' at the SkeletonGraphic component under 'Advanced'.\n"
+ "You may want to duplicate the 'SkeletonGraphicDefault' material and change settings at the duplicate to not affect all instances.";
public static bool IsMaterialSetupProblematic (SkeletonRenderer renderer, ref string errorMessage) {
var materials = renderer.GetComponent<Renderer>().sharedMaterials;
bool isProblematic = false;
foreach (var material in materials) {
if (material == null) continue;
isProblematic |= IsMaterialSetupProblematic(material, ref errorMessage);
if (renderer.zSpacing == 0) {
isProblematic |= IsZSpacingRequired(material, ref errorMessage);
}
if (renderer.addNormals == false && RequiresMeshNormals(material)) {
isProblematic = true;
errorMessage += kAddNormalsMessage;
}
if (renderer.calculateTangents == false && RequiresTangents(material)) {
isProblematic = true;
errorMessage += kSolveTangentsMessage;
}
if (renderer.tintBlack == false && RequiresTintBlack(material)) {
isProblematic = true;
errorMessage += kTintBlackMessage;
}
}
return isProblematic;
}
public static bool IsMaterialSetupProblematic(SkeletonGraphic skeletonGraphic, ref string errorMessage)
{
var material = skeletonGraphic.material;
bool isProblematic = false;
if (material) {
isProblematic |= IsMaterialSetupProblematic(material, ref errorMessage);
var settings = skeletonGraphic.MeshGenerator.settings;
if (settings.zSpacing == 0) {
isProblematic |= IsZSpacingRequired(material, ref errorMessage);
}
if (IsSpineNonSkeletonGraphicMaterial(material)) {
isProblematic = true;
errorMessage += kNoSkeletonGraphicMaterialMessage;
}
if (settings.tintBlack == false && RequiresTintBlack(material)) {
isProblematic = true;
errorMessage += kTintBlackMessage;
}
if (settings.tintBlack == true && CanvasNotSetupForTintBlack(skeletonGraphic)) {
isProblematic = true;
errorMessage += kCanvasTintBlackMessage;
}
if (settings.canvasGroupTintBlack == true && !IsSkeletonGraphicTintBlackMaterial(material)) {
isProblematic = true;
errorMessage += kNoSkeletonGraphicTintBlackMaterialMessage;
}
if (settings.canvasGroupTintBlack == true && !IsCanvasGroupCompatible(material)) {
isProblematic = true;
errorMessage += kCanvasGroupCompatibleMessage;
}
}
return isProblematic;
}
public static bool IsMaterialSetupProblematic(Material material, ref string errorMessage) {
return !IsColorSpaceSupported(material, ref errorMessage);
}
public static bool IsZSpacingRequired(Material material, ref string errorMessage) {
bool hasForwardAddPass = material.FindPass("FORWARD_DELTA") >= 0;
if (hasForwardAddPass) {
errorMessage += kZSpacingRequiredMessage;
return true;
}
bool zWrite = material.HasProperty("_ZWrite") && material.GetFloat("_ZWrite") > 0.0f;
if (zWrite) {
errorMessage += kZSpacingRecommendedMessage;
return true;
}
return false;
}
public static bool IsColorSpaceSupported (Material material, ref string errorMessage) {
if (QualitySettings.activeColorSpace == ColorSpace.Linear) {
if (IsPMAMaterial(material)) {
errorMessage += kPMANotSupportedLinearMessage;
return false;
}
}
return true;
}
public static bool UsesSpineShader (Material material) {
return material.shader.name.Contains("Spine/");
}
public static bool IsTextureSetupProblematic (Material material, ColorSpace colorSpace,
bool sRGBTexture, bool mipmapEnabled, bool alphaIsTransparency,
string texturePath, string materialPath,
ref string errorMessage) {
if (material == null || !UsesSpineShader(material)) {
return false;
}
bool isProblematic = false;
if (IsPMAMaterial(material)) {
// 'sRGBTexture = true' generates incorrectly weighted mipmaps at PMA textures,
// causing white borders due to undesired custom weighting.
if (sRGBTexture && mipmapEnabled && colorSpace == ColorSpace.Gamma) {
errorMessage += string.Format("`{0}` : Problematic Texture Settings found: " +
"When enabling `Generate Mip Maps` in Gamma color space, it is recommended " +
"to disable `sRGB (Color Texture)` on `Premultiply alpha` textures. Otherwise " +
"you will receive white border artifacts on an atlas exported with default " +
"`Premultiply alpha` settings.\n" +
"(You can disable this warning in `Edit - Preferences - Spine`)\n", texturePath);
isProblematic = true;
}
if (alphaIsTransparency) {
string materialName = System.IO.Path.GetFileName(materialPath);
errorMessage += string.Format("`{0}` and material `{1}` : Problematic " +
"Texture / Material Settings found: It is recommended to disable " +
"`Alpha Is Transparency` on `Premultiply alpha` textures.\n" +
"Assuming `Premultiply alpha` texture because `Straight Alpha Texture` " +
"is disabled at material). " +
"(You can disable this warning in `Edit - Preferences - Spine`)\n", texturePath, materialName);
isProblematic = true;
}
}
else { // straight alpha texture
if (!alphaIsTransparency) {
string materialName = System.IO.Path.GetFileName(materialPath);
errorMessage += string.Format("`{0}` and material `{1}` : Incorrect" +
"Texture / Material Settings found: It is strongly recommended " +
"to enable `Alpha Is Transparency` on `Straight alpha` textures.\n" +
"Assuming `Straight alpha` texture because `Straight Alpha Texture` " +
"is enabled at material). " +
"(You can disable this warning in `Edit - Preferences - Spine`)\n", texturePath, materialName);
isProblematic = true;
}
}
return isProblematic;
}
public static void EnablePMAAtMaterial (Material material, bool enablePMA) {
if (material.HasProperty(STRAIGHT_ALPHA_PARAM_ID)) {
material.SetInt(STRAIGHT_ALPHA_PARAM_ID, enablePMA ? 0 : 1);
if (enablePMA)
material.DisableKeyword(STRAIGHT_ALPHA_KEYWORD);
else
material.EnableKeyword(STRAIGHT_ALPHA_KEYWORD);
}
else {
if (enablePMA)
material.EnableKeyword(ALPHAPREMULTIPLY_ON_KEYWORD);
else
material.DisableKeyword(ALPHAPREMULTIPLY_ON_KEYWORD);
}
}
static bool IsPMAMaterial (Material material) {
bool usesAlphaPremultiplyKeyword = IsSpriteShader(material);
if (usesAlphaPremultiplyKeyword)
return material.IsKeywordEnabled(ALPHAPREMULTIPLY_ON_KEYWORD);
else
return material.HasProperty(STRAIGHT_ALPHA_PARAM_ID) && material.GetInt(STRAIGHT_ALPHA_PARAM_ID) == 0;
}
static bool IsURP3DMaterial (Material material) {
return material.shader.name.Contains("Universal Render Pipeline/Spine");
}
static bool IsSpineNonSkeletonGraphicMaterial (Material material) {
return material.shader.name.Contains("Spine") && !material.shader.name.Contains("SkeletonGraphic");
}
static bool IsSkeletonGraphicTintBlackMaterial (Material material) {
return material.shader.name.Contains("Spine") && material.shader.name.Contains("SkeletonGraphic")
&& material.shader.name.Contains("Black");
}
static bool AreShadowsDisabled (Material material) {
return material.IsKeywordEnabled("_RECEIVE_SHADOWS_OFF");
}
static bool RequiresMeshNormals (Material material) {
bool anyFixedNormalSet = false;
foreach (string fixedNormalKeyword in FIXED_NORMALS_KEYWORDS) {
if (material.IsKeywordEnabled(fixedNormalKeyword)) {
anyFixedNormalSet = true;
break;
}
}
bool isShaderWithMeshNormals = IsSpriteShader(material);
return isShaderWithMeshNormals && !anyFixedNormalSet;
}
static bool IsSpriteShader (Material material) {
string shaderName = material.shader.name;
return shaderName.Contains("Spine/Sprite/Pixel Lit") ||
shaderName.Contains("Spine/Sprite/Vertex Lit") ||
shaderName.Contains("2D/Spine/Sprite") || // covers both URP and LWRP
shaderName.Contains("Pipeline/Spine/Sprite"); // covers both URP and LWRP
}
static bool RequiresTintBlack (Material material) {
bool isTintBlackShader =
material.shader.name.Contains("Spine") &&
material.shader.name.Contains("Tint Black");
return isTintBlackShader;
}
static bool RequiresTangents (Material material) {
return material.IsKeywordEnabled(NORMALMAP_KEYWORD);
}
static bool IsCanvasGroupCompatible (Material material) {
return material.IsKeywordEnabled(CANVAS_GROUP_COMPATIBLE_KEYWORD);
}
static bool CanvasNotSetupForTintBlack (SkeletonGraphic skeletonGraphic) {
Canvas canvas = skeletonGraphic.canvas;
if (!canvas)
return false;
var requiredChannels =
AdditionalCanvasShaderChannels.TexCoord1 |
AdditionalCanvasShaderChannels.TexCoord2;
return (canvas.additionalShaderChannels & requiredChannels) != requiredChannels;
}
}
}
#endif // UNITY_EDITOR

View File

@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: e68fa8db689084946adce454b83e6d4a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: