增加spine4.0

This commit is contained in:
ww-rm
2025-03-04 19:36:05 +08:00
parent 0e30172dfd
commit d4ac38c7ff
47 changed files with 14011 additions and 1 deletions

View File

@@ -0,0 +1,108 @@
/******************************************************************************
* 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 SpineRuntime40 {
/// <summary>
/// An AttachmentLoader that configures attachments using texture regions from an Atlas.
/// See <a href='http://esotericsoftware.com/spine-loading-skeleton-data#JSON-and-binary-data'>Loading Skeleton Data</a> in the Spine Runtimes Guide.
/// </summary>
public class AtlasAttachmentLoader : AttachmentLoader {
private Atlas[] atlasArray;
public AtlasAttachmentLoader (params Atlas[] atlasArray) {
if (atlasArray == null) throw new ArgumentNullException("atlas", "atlas array cannot be null.");
this.atlasArray = atlasArray;
}
public RegionAttachment NewRegionAttachment (Skin skin, string name, string path) {
AtlasRegion region = FindRegion(path);
if (region == null) throw new ArgumentException(string.Format("Region not found in atlas: {0} (region attachment: {1})", path, name));
RegionAttachment attachment = new RegionAttachment(name);
attachment.RendererObject = region;
attachment.SetUVs(region.u, region.v, region.u2, region.v2, region.degrees);
attachment.regionOffsetX = region.offsetX;
attachment.regionOffsetY = region.offsetY;
attachment.regionWidth = region.width;
attachment.regionHeight = region.height;
attachment.regionOriginalWidth = region.originalWidth;
attachment.regionOriginalHeight = region.originalHeight;
return attachment;
}
public MeshAttachment NewMeshAttachment (Skin skin, string name, string path) {
AtlasRegion region = FindRegion(path);
if (region == null) throw new ArgumentException(string.Format("Region not found in atlas: {0} (region attachment: {1})", path, name));
MeshAttachment attachment = new MeshAttachment(name);
attachment.RendererObject = region;
attachment.RegionU = region.u;
attachment.RegionV = region.v;
attachment.RegionU2 = region.u2;
attachment.RegionV2 = region.v2;
attachment.RegionDegrees = region.degrees;
attachment.regionOffsetX = region.offsetX;
attachment.regionOffsetY = region.offsetY;
attachment.regionWidth = region.width;
attachment.regionHeight = region.height;
attachment.regionOriginalWidth = region.originalWidth;
attachment.regionOriginalHeight = region.originalHeight;
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);
}
public AtlasRegion FindRegion (string name) {
AtlasRegion region;
for (int i = 0; i < atlasArray.Length; i++) {
region = atlasArray[i].FindRegion(name);
if (region != null)
return region;
}
return null;
}
}
}

View File

@@ -0,0 +1,52 @@
/******************************************************************************
* 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 SpineRuntime40 {
abstract public class Attachment {
public string Name { get; private set; }
protected Attachment (string name) {
if (name == null) throw new ArgumentNullException("name", "name cannot be null");
Name = name;
}
override public string ToString () {
return Name;
}
///<summary>Returns a copy of the attachment.</summary>
public abstract Attachment Copy ();
}
public interface IHasRendererObject {
object RendererObject { get; set; }
}
}

View File

@@ -0,0 +1,48 @@
/******************************************************************************
* 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 SpineRuntime40 {
public interface AttachmentLoader {
/// <return>May be null to not load any attachment.</return>
RegionAttachment NewRegionAttachment (Skin skin, string name, string path);
/// <return>May be null to not load any attachment.</return>
MeshAttachment NewMeshAttachment (Skin skin, string name, string path);
/// <return>May be null to not load any attachment.</return>
BoundingBoxAttachment NewBoundingBoxAttachment (Skin skin, string name);
/// <returns>May be null to not load any attachment</returns>
PathAttachment NewPathAttachment (Skin skin, string name);
PointAttachment NewPointAttachment (Skin skin, string name);
ClippingAttachment NewClippingAttachment (Skin skin, string name);
}
}

View File

@@ -0,0 +1,34 @@
/******************************************************************************
* 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 SpineRuntime40 {
public enum AttachmentType {
Region, Boundingbox, Mesh, Linkedmesh, Path, Point, Clipping
}
}

View File

@@ -0,0 +1,45 @@
/******************************************************************************
* 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 SpineRuntime40 {
/// <summary>Attachment that has a polygon for bounds checking.</summary>
public class BoundingBoxAttachment : VertexAttachment {
public BoundingBoxAttachment (string name)
: base(name) {
}
public override Attachment Copy () {
BoundingBoxAttachment copy = new BoundingBoxAttachment(this.Name);
CopyTo(copy);
return copy;
}
}
}

View File

@@ -0,0 +1,48 @@
/******************************************************************************
* 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 SpineRuntime40 {
public class ClippingAttachment : VertexAttachment {
internal SlotData endSlot;
public SlotData EndSlot { get { return endSlot; } set { endSlot = value; } }
public ClippingAttachment (string name) : base(name) {
}
public override Attachment Copy () {
ClippingAttachment copy = new ClippingAttachment(this.Name);
CopyTo(copy);
copy.endSlot = endSlot;
return copy;
}
}
}

View File

@@ -0,0 +1,221 @@
/******************************************************************************
* 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 SpineRuntime40 {
/// <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 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.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.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

@@ -0,0 +1,60 @@
/******************************************************************************
* 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 SpineRuntime40 {
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; } }
/// <summary>If true, the start and end knots are connected.</summary>
public bool Closed { get { return closed; } set { closed = value; } }
/// <summary>If true, additional calculations are performed to make computing positions along the path more accurate and movement along
/// the path have a constant speed.</summary>
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

@@ -0,0 +1,67 @@
/******************************************************************************
* 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 SpineRuntime40 {
/// <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

@@ -0,0 +1,194 @@
/******************************************************************************
* 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 SpineRuntime40 {
/// <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 regionScaleX = width / regionOriginalWidth * scaleX;
float regionScaleY = height / regionOriginalHeight * scaleY;
float localX = -width / 2 * scaleX + regionOffsetX * regionScaleX;
float localY = -height / 2 * scaleY + regionOffsetY * regionScaleY;
float localX2 = localX + regionWidth * regionScaleX;
float localY2 = localY + regionHeight * regionScaleY;
float cos = MathUtils.CosDeg(this.rotation);
float sin = MathUtils.SinDeg(this.rotation);
float x = this.x, 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, int degrees) {
float[] uvs = this.uvs;
// UV values differ from spine-libgdx.
if (degrees == 90) {
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

@@ -0,0 +1,153 @@
/******************************************************************************
* 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 SpineRuntime40 {
/// <summary>>An attachment with vertices that are transformed by one or more bones and can be deformed by a slot's
/// <see cref="Slot.Deform"/>.</summary>
public abstract class VertexAttachment : Attachment {
static int nextID = 0;
static readonly Object nextIdLock = new Object();
internal readonly int id;
internal int[] bones;
internal float[] vertices;
internal int worldVerticesLength;
internal VertexAttachment deformAttachment;
/// <summary>Gets a unique ID for this attachment.</summary>
public int Id { get { return id; } }
public int[] Bones { get { return bones; } set { bones = value; } }
public float[] Vertices { get { return vertices; } set { vertices = value; } }
public int WorldVerticesLength { get { return worldVerticesLength; } set { worldVerticesLength = value; } }
///<summary>Deform keys for the deform attachment are also applied to this attachment.
/// May be null if no deform keys should be applied.</summary>
public VertexAttachment DeformAttachment { get { return deformAttachment; } set { deformAttachment = value; } }
public VertexAttachment (string name)
: base(name) {
deformAttachment = this;
lock (VertexAttachment.nextIdLock) {
id = VertexAttachment.nextID++;
}
}
public void ComputeWorldVertices (Slot slot, float[] worldVertices) {
ComputeWorldVertices(slot, 0, worldVerticesLength, worldVertices, 0);
}
/// <summary>
/// Transforms the attachment's local <see cref="Vertices"/> to world coordinates. If the slot's <see cref="Slot.Deform"/> is
/// not empty, it is used to deform the vertices.
/// <para />
/// See <a href="http://esotericsoftware.com/spine-runtime-skeletons#World-transforms">World transforms</a> in the Spine
/// Runtimes Guide.
/// </summary>
/// <param name="start">The index of the first <see cref="Vertices"/> value to transform. Each vertex has 2 values, x and y.</param>
/// <param name="count">The number of world vertex values to output. Must be less than or equal to <see cref="WorldVerticesLength"/> - start.</param>
/// <param name="worldVertices">The output world vertices. Must have a length greater than or equal to <paramref name="offset"/> + <paramref name="count"/>.</param>
/// <param name="offset">The <paramref name="worldVertices"/> index to begin writing values.</param>
/// <param name="stride">The number of <paramref name="worldVertices"/> entries between the value pairs written.</param>
public void ComputeWorldVertices (Slot slot, int start, int count, float[] worldVertices, int offset, int stride = 2) {
count = offset + (count >> 1) * stride;
ExposedList<float> deformArray = slot.deform;
float[] vertices = this.vertices;
int[] bones = this.bones;
if (bones == null) {
if (deformArray.Count > 0) vertices = deformArray.Items;
Bone bone = slot.bone;
float x = bone.worldX, y = bone.worldY;
float a = bone.a, b = bone.b, c = bone.c, d = bone.d;
for (int vv = start, w = offset; w < count; vv += 2, w += stride) {
float vx = vertices[vv], vy = vertices[vv + 1];
worldVertices[w] = vx * a + vy * b + x;
worldVertices[w + 1] = vx * c + vy * d + y;
}
return;
}
int v = 0, skip = 0;
for (int i = 0; i < start; i += 2) {
int n = bones[v];
v += n + 1;
skip += n;
}
Bone[] skeletonBones = slot.bone.skeleton.bones.Items;
if (deformArray.Count == 0) {
for (int w = offset, b = skip * 3; w < count; w += stride) {
float wx = 0, wy = 0;
int n = bones[v++];
n += v;
for (; v < n; v++, b += 3) {
Bone bone = skeletonBones[bones[v]];
float vx = vertices[b], vy = vertices[b + 1], weight = vertices[b + 2];
wx += (vx * bone.a + vy * bone.b + bone.worldX) * weight;
wy += (vx * bone.c + vy * bone.d + bone.worldY) * weight;
}
worldVertices[w] = wx;
worldVertices[w + 1] = wy;
}
} else {
float[] deform = deformArray.Items;
for (int w = offset, b = skip * 3, f = skip << 1; w < count; w += stride) {
float wx = 0, wy = 0;
int n = bones[v++];
n += v;
for (; v < n; v++, b += 3, f += 2) {
Bone bone = skeletonBones[bones[v]];
float vx = vertices[b] + deform[f], vy = vertices[b + 1] + deform[f + 1], weight = vertices[b + 2];
wx += (vx * bone.a + vy * bone.b + bone.worldX) * weight;
wy += (vx * bone.c + vy * bone.d + bone.worldY) * weight;
}
worldVertices[w] = wx;
worldVertices[w + 1] = wy;
}
}
}
///<summary>Does not copy id (generated) or name (set on construction).</summary>
internal void CopyTo (VertexAttachment attachment) {
if (bones != null) {
attachment.bones = new int[bones.Length];
Array.Copy(bones, 0, attachment.bones, 0, bones.Length);
} else
attachment.bones = null;
if (vertices != null) {
attachment.vertices = new float[vertices.Length];
Array.Copy(vertices, 0, attachment.vertices, 0, vertices.Length);
} else
attachment.vertices = null;
attachment.worldVerticesLength = worldVerticesLength;
attachment.deformAttachment = deformAttachment;
}
}
}