This commit is contained in:
Nim XD
2024-08-27 21:01:33 +05:30
parent 99eaf514fd
commit 121a1b7c73
31803 changed files with 623461 additions and 623399 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,114 +1,114 @@
/******************************************************************************
* 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 mix (crossfade) durations to be applied when AnimationState animations are changed.</summary>
public class AnimationStateData {
internal SkeletonData skeletonData;
readonly Dictionary<AnimationPair, float> animationToMixTime = new Dictionary<AnimationPair, float>(AnimationPairComparer.Instance);
internal float defaultMix;
/// <summary>The SkeletonData to look up animations when they are specified by name.</summary>
public SkeletonData SkeletonData { get { return skeletonData; } }
/// <summary>
/// The mix duration to use when no mix duration has been specifically defined between two animations.</summary>
public float DefaultMix { get { return defaultMix; } set { defaultMix = value; } }
public AnimationStateData (SkeletonData skeletonData) {
if (skeletonData == null) throw new ArgumentException("skeletonData cannot be null.", "skeletonData");
this.skeletonData = skeletonData;
}
/// <summary>Sets a mix duration by animation names.</summary>
public void SetMix (string fromName, string toName, float duration) {
Animation from = skeletonData.FindAnimation(fromName);
if (from == null) throw new ArgumentException("Animation not found: " + fromName, "fromName");
Animation to = skeletonData.FindAnimation(toName);
if (to == null) throw new ArgumentException("Animation not found: " + toName, "toName");
SetMix(from, to, duration);
}
/// <summary>Sets a mix duration when changing from the specified animation to the other.
/// See TrackEntry.MixDuration.</summary>
public void SetMix (Animation from, Animation to, float duration) {
if (from == null) throw new ArgumentNullException("from", "from cannot be null.");
if (to == null) throw new ArgumentNullException("to", "to cannot be null.");
AnimationPair key = new AnimationPair(from, to);
animationToMixTime.Remove(key);
animationToMixTime.Add(key, duration);
}
/// <summary>
/// The mix duration to use when changing from the specified animation to the other,
/// or the DefaultMix if no mix duration has been set.
/// </summary>
public float GetMix (Animation from, Animation to) {
if (from == null) throw new ArgumentNullException("from", "from cannot be null.");
if (to == null) throw new ArgumentNullException("to", "to cannot be null.");
AnimationPair key = new AnimationPair(from, to);
float duration;
if (animationToMixTime.TryGetValue(key, out duration)) return duration;
return defaultMix;
}
public struct AnimationPair {
public readonly Animation a1;
public readonly Animation a2;
public AnimationPair (Animation a1, Animation a2) {
this.a1 = a1;
this.a2 = a2;
}
public override string ToString () {
return a1.name + "->" + a2.name;
}
}
// Avoids boxing in the dictionary.
public class AnimationPairComparer : IEqualityComparer<AnimationPair> {
public static readonly AnimationPairComparer Instance = new AnimationPairComparer();
bool IEqualityComparer<AnimationPair>.Equals (AnimationPair x, AnimationPair y) {
return ReferenceEquals(x.a1, y.a1) && ReferenceEquals(x.a2, y.a2);
}
int IEqualityComparer<AnimationPair>.GetHashCode (AnimationPair obj) {
// from Tuple.CombineHashCodes // return (((h1 << 5) + h1) ^ h2);
int h1 = obj.a1.GetHashCode();
return (((h1 << 5) + h1) ^ obj.a2.GetHashCode());
}
}
}
}
/******************************************************************************
* 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 mix (crossfade) durations to be applied when AnimationState animations are changed.</summary>
public class AnimationStateData {
internal SkeletonData skeletonData;
readonly Dictionary<AnimationPair, float> animationToMixTime = new Dictionary<AnimationPair, float>(AnimationPairComparer.Instance);
internal float defaultMix;
/// <summary>The SkeletonData to look up animations when they are specified by name.</summary>
public SkeletonData SkeletonData { get { return skeletonData; } }
/// <summary>
/// The mix duration to use when no mix duration has been specifically defined between two animations.</summary>
public float DefaultMix { get { return defaultMix; } set { defaultMix = value; } }
public AnimationStateData (SkeletonData skeletonData) {
if (skeletonData == null) throw new ArgumentException("skeletonData cannot be null.", "skeletonData");
this.skeletonData = skeletonData;
}
/// <summary>Sets a mix duration by animation names.</summary>
public void SetMix (string fromName, string toName, float duration) {
Animation from = skeletonData.FindAnimation(fromName);
if (from == null) throw new ArgumentException("Animation not found: " + fromName, "fromName");
Animation to = skeletonData.FindAnimation(toName);
if (to == null) throw new ArgumentException("Animation not found: " + toName, "toName");
SetMix(from, to, duration);
}
/// <summary>Sets a mix duration when changing from the specified animation to the other.
/// See TrackEntry.MixDuration.</summary>
public void SetMix (Animation from, Animation to, float duration) {
if (from == null) throw new ArgumentNullException("from", "from cannot be null.");
if (to == null) throw new ArgumentNullException("to", "to cannot be null.");
AnimationPair key = new AnimationPair(from, to);
animationToMixTime.Remove(key);
animationToMixTime.Add(key, duration);
}
/// <summary>
/// The mix duration to use when changing from the specified animation to the other,
/// or the DefaultMix if no mix duration has been set.
/// </summary>
public float GetMix (Animation from, Animation to) {
if (from == null) throw new ArgumentNullException("from", "from cannot be null.");
if (to == null) throw new ArgumentNullException("to", "to cannot be null.");
AnimationPair key = new AnimationPair(from, to);
float duration;
if (animationToMixTime.TryGetValue(key, out duration)) return duration;
return defaultMix;
}
public struct AnimationPair {
public readonly Animation a1;
public readonly Animation a2;
public AnimationPair (Animation a1, Animation a2) {
this.a1 = a1;
this.a2 = a2;
}
public override string ToString () {
return a1.name + "->" + a2.name;
}
}
// Avoids boxing in the dictionary.
public class AnimationPairComparer : IEqualityComparer<AnimationPair> {
public static readonly AnimationPairComparer Instance = new AnimationPairComparer();
bool IEqualityComparer<AnimationPair>.Equals (AnimationPair x, AnimationPair y) {
return ReferenceEquals(x.a1, y.a1) && ReferenceEquals(x.a2, y.a2);
}
int IEqualityComparer<AnimationPair>.GetHashCode (AnimationPair obj) {
// from Tuple.CombineHashCodes // return (((h1 << 5) + h1) ^ h2);
int h1 = obj.a1.GetHashCode();
return (((h1 << 5) + h1) ^ obj.a2.GetHashCode());
}
}
}
}

View File

@@ -1,364 +1,364 @@
/******************************************************************************
* 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_5 || UNITY_5_3_OR_NEWER || UNITY_WSA || UNITY_WP8 || UNITY_WP8_1)
#define IS_UNITY
#endif
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Reflection;
#if WINDOWS_STOREAPP
using System.Threading.Tasks;
using Windows.Storage;
#endif
namespace Spine {
public class Atlas : IEnumerable<AtlasRegion> {
readonly List<AtlasPage> pages = new List<AtlasPage>();
List<AtlasRegion> regions = new List<AtlasRegion>();
TextureLoader textureLoader;
#region IEnumerable implementation
public IEnumerator<AtlasRegion> GetEnumerator () {
return regions.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator () {
return regions.GetEnumerator();
}
#endregion
public List<AtlasRegion> Regions { get { return regions; } }
public List<AtlasPage> Pages { get { return pages; } }
#if !(IS_UNITY)
#if WINDOWS_STOREAPP
private async Task ReadFile(string path, TextureLoader textureLoader) {
var folder = Windows.ApplicationModel.Package.Current.InstalledLocation;
var file = await folder.GetFileAsync(path).AsTask().ConfigureAwait(false);
using (var reader = new StreamReader(await file.OpenStreamForReadAsync().ConfigureAwait(false))) {
try {
Atlas atlas = new Atlas(reader, Path.GetDirectoryName(path), textureLoader);
this.pages = atlas.pages;
this.regions = atlas.regions;
this.textureLoader = atlas.textureLoader;
} catch (Exception ex) {
throw new Exception("Error reading atlas file: " + path, ex);
}
}
}
public Atlas(string path, TextureLoader textureLoader) {
this.ReadFile(path, textureLoader).Wait();
}
#else
public Atlas (string path, TextureLoader textureLoader) {
#if WINDOWS_PHONE
Stream stream = Microsoft.Xna.Framework.TitleContainer.OpenStream(path);
using (StreamReader reader = new StreamReader(stream)) {
#else
using (StreamReader reader = new StreamReader(path)) {
#endif // WINDOWS_PHONE
try {
Atlas atlas = new Atlas(reader, Path.GetDirectoryName(path), textureLoader);
this.pages = atlas.pages;
this.regions = atlas.regions;
this.textureLoader = atlas.textureLoader;
} catch (Exception ex) {
throw new Exception("Error reading atlas file: " + path, ex);
}
}
}
#endif // WINDOWS_STOREAPP
#endif
public Atlas (List<AtlasPage> pages, List<AtlasRegion> regions) {
if (pages == null) throw new ArgumentNullException("pages", "pages cannot be null.");
if (regions == null) throw new ArgumentNullException("regions", "regions cannot be null.");
this.pages = pages;
this.regions = regions;
this.textureLoader = null;
}
public Atlas (TextReader reader, string imagesDir, TextureLoader textureLoader) {
if (reader == null) throw new ArgumentNullException("reader", "reader cannot be null.");
if (imagesDir == null) throw new ArgumentNullException("imagesDir", "imagesDir cannot be null.");
if (textureLoader == null) throw new ArgumentNullException("textureLoader", "textureLoader cannot be null.");
this.textureLoader = textureLoader;
string[] entry = new string[5];
AtlasPage page = null;
AtlasRegion region = null;
var pageFields = new Dictionary<string, Action>(5);
pageFields.Add("size", () => {
page.width = int.Parse(entry[1], CultureInfo.InvariantCulture);
page.height = int.Parse(entry[2], CultureInfo.InvariantCulture);
});
pageFields.Add("format", () => {
page.format = (Format)Enum.Parse(typeof(Format), entry[1], false);
});
pageFields.Add("filter", () => {
page.minFilter = (TextureFilter)Enum.Parse(typeof(TextureFilter), entry[1], false);
page.magFilter = (TextureFilter)Enum.Parse(typeof(TextureFilter), entry[2], false);
});
pageFields.Add("repeat", () => {
if (entry[1].IndexOf('x') != -1) page.uWrap = TextureWrap.Repeat;
if (entry[1].IndexOf('y') != -1) page.vWrap = TextureWrap.Repeat;
});
pageFields.Add("pma", () => {
page.pma = entry[1] == "true";
});
var regionFields = new Dictionary<string, Action>(8);
regionFields.Add("xy", () => { // Deprecated, use bounds.
region.x = int.Parse(entry[1], CultureInfo.InvariantCulture);
region.y = int.Parse(entry[2], CultureInfo.InvariantCulture);
});
regionFields.Add("size", () => { // Deprecated, use bounds.
region.width = int.Parse(entry[1], CultureInfo.InvariantCulture);
region.height = int.Parse(entry[2], CultureInfo.InvariantCulture);
});
regionFields.Add("bounds", () => {
region.x = int.Parse(entry[1], CultureInfo.InvariantCulture);
region.y = int.Parse(entry[2], CultureInfo.InvariantCulture);
region.width = int.Parse(entry[3], CultureInfo.InvariantCulture);
region.height = int.Parse(entry[4], CultureInfo.InvariantCulture);
});
regionFields.Add("offset", () => { // Deprecated, use offsets.
region.offsetX = int.Parse(entry[1], CultureInfo.InvariantCulture);
region.offsetY = int.Parse(entry[2], CultureInfo.InvariantCulture);
});
regionFields.Add("orig", () => { // Deprecated, use offsets.
region.originalWidth = int.Parse(entry[1], CultureInfo.InvariantCulture);
region.originalHeight = int.Parse(entry[2], CultureInfo.InvariantCulture);
});
regionFields.Add("offsets", () => {
region.offsetX = int.Parse(entry[1], CultureInfo.InvariantCulture);
region.offsetY = int.Parse(entry[2], CultureInfo.InvariantCulture);
region.originalWidth = int.Parse(entry[3], CultureInfo.InvariantCulture);
region.originalHeight = int.Parse(entry[4], CultureInfo.InvariantCulture);
});
regionFields.Add("rotate", () => {
string value = entry[1];
if (value == "true")
region.degrees = 90;
else if (value != "false")
region.degrees = int.Parse(value, CultureInfo.InvariantCulture);
});
regionFields.Add("index", () => {
region.index = int.Parse(entry[1], CultureInfo.InvariantCulture);
});
string line = reader.ReadLine();
// Ignore empty lines before first entry.
while (line != null && line.Trim().Length == 0)
line = reader.ReadLine();
// Header entries.
while (true) {
if (line == null || line.Trim().Length == 0) break;
if (ReadEntry(entry, line) == 0) break; // Silently ignore all header fields.
line = reader.ReadLine();
}
// Page and region entries.
List<string> names = null;
List<int[]> values = null;
while (true) {
if (line == null) break;
if (line.Trim().Length == 0) {
page = null;
line = reader.ReadLine();
} else if (page == null) {
page = new AtlasPage();
page.name = line.Trim();
while (true) {
if (ReadEntry(entry, line = reader.ReadLine()) == 0) break;
Action field;
if (pageFields.TryGetValue(entry[0], out field)) field(); // Silently ignore unknown page fields.
}
textureLoader.Load(page, Path.Combine(imagesDir, page.name));
pages.Add(page);
} else {
region = new AtlasRegion();
region.page = page;
region.name = line;
while (true) {
int count = ReadEntry(entry, line = reader.ReadLine());
if (count == 0) break;
Action field;
if (regionFields.TryGetValue(entry[0], out field))
field();
else {
if (names == null) {
names = new List<string>(8);
values = new List<int[]>(8);
}
names.Add(entry[0]);
int[] entryValues = new int[count];
for (int i = 0; i < count; i++)
int.TryParse(entry[i + 1], NumberStyles.Any, CultureInfo.InvariantCulture, out entryValues[i]); // Silently ignore non-integer values.
values.Add(entryValues);
}
}
if (region.originalWidth == 0 && region.originalHeight == 0) {
region.originalWidth = region.width;
region.originalHeight = region.height;
}
if (names != null && names.Count > 0) {
region.names = names.ToArray();
region.values = values.ToArray();
names.Clear();
values.Clear();
}
region.u = region.x / (float)page.width;
region.v = region.y / (float)page.height;
if (region.degrees == 90) {
region.u2 = (region.x + region.height) / (float)page.width;
region.v2 = (region.y + region.width) / (float)page.height;
} else {
region.u2 = (region.x + region.width) / (float)page.width;
region.v2 = (region.y + region.height) / (float)page.height;
}
regions.Add(region);
}
}
}
static private int ReadEntry (string[] entry, string line) {
if (line == null) return 0;
line = line.Trim();
if (line.Length == 0) return 0;
int colon = line.IndexOf(':');
if (colon == -1) return 0;
entry[0] = line.Substring(0, colon).Trim();
for (int i = 1, lastMatch = colon + 1; ; i++) {
int comma = line.IndexOf(',', lastMatch);
if (comma == -1) {
entry[i] = line.Substring(lastMatch).Trim();
return i;
}
entry[i] = line.Substring(lastMatch, comma - lastMatch).Trim();
lastMatch = comma + 1;
if (i == 4) return 4;
}
}
public void FlipV () {
for (int i = 0, n = regions.Count; i < n; i++) {
AtlasRegion region = regions[i];
region.v = 1 - region.v;
region.v2 = 1 - region.v2;
}
}
/// <summary>Returns the first region found with the specified name. This method uses string comparison to find the region, so the result
/// should be cached rather than calling this method multiple times.</summary>
/// <returns>The region, or null.</returns>
public AtlasRegion FindRegion (string name) {
for (int i = 0, n = regions.Count; i < n; i++)
if (regions[i].name == name) return regions[i];
return null;
}
public void Dispose () {
if (textureLoader == null) return;
for (int i = 0, n = pages.Count; i < n; i++)
textureLoader.Unload(pages[i].rendererObject);
}
}
public enum Format {
Alpha,
Intensity,
LuminanceAlpha,
RGB565,
RGBA4444,
RGB888,
RGBA8888
}
public enum TextureFilter {
Nearest,
Linear,
MipMap,
MipMapNearestNearest,
MipMapLinearNearest,
MipMapNearestLinear,
MipMapLinearLinear
}
public enum TextureWrap {
MirroredRepeat,
ClampToEdge,
Repeat
}
public class AtlasPage {
public string name;
public int width, height;
public Format format = Format.RGBA8888;
public TextureFilter minFilter = TextureFilter.Nearest;
public TextureFilter magFilter = TextureFilter.Nearest;
public TextureWrap uWrap = TextureWrap.ClampToEdge;
public TextureWrap vWrap = TextureWrap.ClampToEdge;
public bool pma;
public object rendererObject;
public AtlasPage Clone () {
return MemberwiseClone() as AtlasPage;
}
}
public class AtlasRegion {
public AtlasPage page;
public string name;
public int x, y, width, height;
public float u, v, u2, v2;
public float offsetX, offsetY;
public int originalWidth, originalHeight;
public int degrees;
public bool rotate;
public int index;
public string[] names;
public int[][] values;
public AtlasRegion Clone () {
return MemberwiseClone() as AtlasRegion;
}
}
public interface TextureLoader {
void Load (AtlasPage page, string path);
void Unload (Object texture);
}
}
/******************************************************************************
* 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_5 || UNITY_5_3_OR_NEWER || UNITY_WSA || UNITY_WP8 || UNITY_WP8_1)
#define IS_UNITY
#endif
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Reflection;
#if WINDOWS_STOREAPP
using System.Threading.Tasks;
using Windows.Storage;
#endif
namespace Spine {
public class Atlas : IEnumerable<AtlasRegion> {
readonly List<AtlasPage> pages = new List<AtlasPage>();
List<AtlasRegion> regions = new List<AtlasRegion>();
TextureLoader textureLoader;
#region IEnumerable implementation
public IEnumerator<AtlasRegion> GetEnumerator () {
return regions.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator () {
return regions.GetEnumerator();
}
#endregion
public List<AtlasRegion> Regions { get { return regions; } }
public List<AtlasPage> Pages { get { return pages; } }
#if !(IS_UNITY)
#if WINDOWS_STOREAPP
private async Task ReadFile(string path, TextureLoader textureLoader) {
var folder = Windows.ApplicationModel.Package.Current.InstalledLocation;
var file = await folder.GetFileAsync(path).AsTask().ConfigureAwait(false);
using (var reader = new StreamReader(await file.OpenStreamForReadAsync().ConfigureAwait(false))) {
try {
Atlas atlas = new Atlas(reader, Path.GetDirectoryName(path), textureLoader);
this.pages = atlas.pages;
this.regions = atlas.regions;
this.textureLoader = atlas.textureLoader;
} catch (Exception ex) {
throw new Exception("Error reading atlas file: " + path, ex);
}
}
}
public Atlas(string path, TextureLoader textureLoader) {
this.ReadFile(path, textureLoader).Wait();
}
#else
public Atlas (string path, TextureLoader textureLoader) {
#if WINDOWS_PHONE
Stream stream = Microsoft.Xna.Framework.TitleContainer.OpenStream(path);
using (StreamReader reader = new StreamReader(stream)) {
#else
using (StreamReader reader = new StreamReader(path)) {
#endif // WINDOWS_PHONE
try {
Atlas atlas = new Atlas(reader, Path.GetDirectoryName(path), textureLoader);
this.pages = atlas.pages;
this.regions = atlas.regions;
this.textureLoader = atlas.textureLoader;
} catch (Exception ex) {
throw new Exception("Error reading atlas file: " + path, ex);
}
}
}
#endif // WINDOWS_STOREAPP
#endif
public Atlas (List<AtlasPage> pages, List<AtlasRegion> regions) {
if (pages == null) throw new ArgumentNullException("pages", "pages cannot be null.");
if (regions == null) throw new ArgumentNullException("regions", "regions cannot be null.");
this.pages = pages;
this.regions = regions;
this.textureLoader = null;
}
public Atlas (TextReader reader, string imagesDir, TextureLoader textureLoader) {
if (reader == null) throw new ArgumentNullException("reader", "reader cannot be null.");
if (imagesDir == null) throw new ArgumentNullException("imagesDir", "imagesDir cannot be null.");
if (textureLoader == null) throw new ArgumentNullException("textureLoader", "textureLoader cannot be null.");
this.textureLoader = textureLoader;
string[] entry = new string[5];
AtlasPage page = null;
AtlasRegion region = null;
var pageFields = new Dictionary<string, Action>(5);
pageFields.Add("size", () => {
page.width = int.Parse(entry[1], CultureInfo.InvariantCulture);
page.height = int.Parse(entry[2], CultureInfo.InvariantCulture);
});
pageFields.Add("format", () => {
page.format = (Format)Enum.Parse(typeof(Format), entry[1], false);
});
pageFields.Add("filter", () => {
page.minFilter = (TextureFilter)Enum.Parse(typeof(TextureFilter), entry[1], false);
page.magFilter = (TextureFilter)Enum.Parse(typeof(TextureFilter), entry[2], false);
});
pageFields.Add("repeat", () => {
if (entry[1].IndexOf('x') != -1) page.uWrap = TextureWrap.Repeat;
if (entry[1].IndexOf('y') != -1) page.vWrap = TextureWrap.Repeat;
});
pageFields.Add("pma", () => {
page.pma = entry[1] == "true";
});
var regionFields = new Dictionary<string, Action>(8);
regionFields.Add("xy", () => { // Deprecated, use bounds.
region.x = int.Parse(entry[1], CultureInfo.InvariantCulture);
region.y = int.Parse(entry[2], CultureInfo.InvariantCulture);
});
regionFields.Add("size", () => { // Deprecated, use bounds.
region.width = int.Parse(entry[1], CultureInfo.InvariantCulture);
region.height = int.Parse(entry[2], CultureInfo.InvariantCulture);
});
regionFields.Add("bounds", () => {
region.x = int.Parse(entry[1], CultureInfo.InvariantCulture);
region.y = int.Parse(entry[2], CultureInfo.InvariantCulture);
region.width = int.Parse(entry[3], CultureInfo.InvariantCulture);
region.height = int.Parse(entry[4], CultureInfo.InvariantCulture);
});
regionFields.Add("offset", () => { // Deprecated, use offsets.
region.offsetX = int.Parse(entry[1], CultureInfo.InvariantCulture);
region.offsetY = int.Parse(entry[2], CultureInfo.InvariantCulture);
});
regionFields.Add("orig", () => { // Deprecated, use offsets.
region.originalWidth = int.Parse(entry[1], CultureInfo.InvariantCulture);
region.originalHeight = int.Parse(entry[2], CultureInfo.InvariantCulture);
});
regionFields.Add("offsets", () => {
region.offsetX = int.Parse(entry[1], CultureInfo.InvariantCulture);
region.offsetY = int.Parse(entry[2], CultureInfo.InvariantCulture);
region.originalWidth = int.Parse(entry[3], CultureInfo.InvariantCulture);
region.originalHeight = int.Parse(entry[4], CultureInfo.InvariantCulture);
});
regionFields.Add("rotate", () => {
string value = entry[1];
if (value == "true")
region.degrees = 90;
else if (value != "false")
region.degrees = int.Parse(value, CultureInfo.InvariantCulture);
});
regionFields.Add("index", () => {
region.index = int.Parse(entry[1], CultureInfo.InvariantCulture);
});
string line = reader.ReadLine();
// Ignore empty lines before first entry.
while (line != null && line.Trim().Length == 0)
line = reader.ReadLine();
// Header entries.
while (true) {
if (line == null || line.Trim().Length == 0) break;
if (ReadEntry(entry, line) == 0) break; // Silently ignore all header fields.
line = reader.ReadLine();
}
// Page and region entries.
List<string> names = null;
List<int[]> values = null;
while (true) {
if (line == null) break;
if (line.Trim().Length == 0) {
page = null;
line = reader.ReadLine();
} else if (page == null) {
page = new AtlasPage();
page.name = line.Trim();
while (true) {
if (ReadEntry(entry, line = reader.ReadLine()) == 0) break;
Action field;
if (pageFields.TryGetValue(entry[0], out field)) field(); // Silently ignore unknown page fields.
}
textureLoader.Load(page, Path.Combine(imagesDir, page.name));
pages.Add(page);
} else {
region = new AtlasRegion();
region.page = page;
region.name = line;
while (true) {
int count = ReadEntry(entry, line = reader.ReadLine());
if (count == 0) break;
Action field;
if (regionFields.TryGetValue(entry[0], out field))
field();
else {
if (names == null) {
names = new List<string>(8);
values = new List<int[]>(8);
}
names.Add(entry[0]);
int[] entryValues = new int[count];
for (int i = 0; i < count; i++)
int.TryParse(entry[i + 1], NumberStyles.Any, CultureInfo.InvariantCulture, out entryValues[i]); // Silently ignore non-integer values.
values.Add(entryValues);
}
}
if (region.originalWidth == 0 && region.originalHeight == 0) {
region.originalWidth = region.width;
region.originalHeight = region.height;
}
if (names != null && names.Count > 0) {
region.names = names.ToArray();
region.values = values.ToArray();
names.Clear();
values.Clear();
}
region.u = region.x / (float)page.width;
region.v = region.y / (float)page.height;
if (region.degrees == 90) {
region.u2 = (region.x + region.height) / (float)page.width;
region.v2 = (region.y + region.width) / (float)page.height;
} else {
region.u2 = (region.x + region.width) / (float)page.width;
region.v2 = (region.y + region.height) / (float)page.height;
}
regions.Add(region);
}
}
}
static private int ReadEntry (string[] entry, string line) {
if (line == null) return 0;
line = line.Trim();
if (line.Length == 0) return 0;
int colon = line.IndexOf(':');
if (colon == -1) return 0;
entry[0] = line.Substring(0, colon).Trim();
for (int i = 1, lastMatch = colon + 1; ; i++) {
int comma = line.IndexOf(',', lastMatch);
if (comma == -1) {
entry[i] = line.Substring(lastMatch).Trim();
return i;
}
entry[i] = line.Substring(lastMatch, comma - lastMatch).Trim();
lastMatch = comma + 1;
if (i == 4) return 4;
}
}
public void FlipV () {
for (int i = 0, n = regions.Count; i < n; i++) {
AtlasRegion region = regions[i];
region.v = 1 - region.v;
region.v2 = 1 - region.v2;
}
}
/// <summary>Returns the first region found with the specified name. This method uses string comparison to find the region, so the result
/// should be cached rather than calling this method multiple times.</summary>
/// <returns>The region, or null.</returns>
public AtlasRegion FindRegion (string name) {
for (int i = 0, n = regions.Count; i < n; i++)
if (regions[i].name == name) return regions[i];
return null;
}
public void Dispose () {
if (textureLoader == null) return;
for (int i = 0, n = pages.Count; i < n; i++)
textureLoader.Unload(pages[i].rendererObject);
}
}
public enum Format {
Alpha,
Intensity,
LuminanceAlpha,
RGB565,
RGBA4444,
RGB888,
RGBA8888
}
public enum TextureFilter {
Nearest,
Linear,
MipMap,
MipMapNearestNearest,
MipMapLinearNearest,
MipMapNearestLinear,
MipMapLinearLinear
}
public enum TextureWrap {
MirroredRepeat,
ClampToEdge,
Repeat
}
public class AtlasPage {
public string name;
public int width, height;
public Format format = Format.RGBA8888;
public TextureFilter minFilter = TextureFilter.Nearest;
public TextureFilter magFilter = TextureFilter.Nearest;
public TextureWrap uWrap = TextureWrap.ClampToEdge;
public TextureWrap vWrap = TextureWrap.ClampToEdge;
public bool pma;
public object rendererObject;
public AtlasPage Clone () {
return MemberwiseClone() as AtlasPage;
}
}
public class AtlasRegion {
public AtlasPage page;
public string name;
public int x, y, width, height;
public float u, v, u2, v2;
public float offsetX, offsetY;
public int originalWidth, originalHeight;
public int degrees;
public bool rotate;
public int index;
public string[] names;
public int[][] values;
public AtlasRegion Clone () {
return MemberwiseClone() as AtlasRegion;
}
}
public interface TextureLoader {
void Load (AtlasPage page, string path);
void Unload (Object texture);
}
}

View File

@@ -1,108 +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 Spine {
/// <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;
}
}
}
/******************************************************************************
* 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>
/// 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

@@ -1,52 +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 Spine {
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; }
}
}
/******************************************************************************
* 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 {
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

@@ -1,48 +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 Spine {
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);
}
}
/******************************************************************************
* 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 {
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

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

View File

@@ -1,45 +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 Spine {
/// <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;
}
}
}
/******************************************************************************
* 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 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

@@ -1,48 +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 Spine {
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;
}
}
}
/******************************************************************************
* 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 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

@@ -1,221 +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 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 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;
}
}
}
/******************************************************************************
* 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 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

@@ -1,60 +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 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; } }
/// <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;
}
}
}
/******************************************************************************
* 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; } }
/// <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

@@ -1,67 +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 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;
}
}
}
/******************************************************************************
* 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,194 +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 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 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;
}
}
}
/******************************************************************************
* 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 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

@@ -1,153 +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 Spine {
/// <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;
}
}
}
/******************************************************************************
* 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>>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;
}
}
}

View File

@@ -1,34 +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 Spine {
public enum BlendMode {
Normal, Additive, Multiply, Screen
}
}
/******************************************************************************
* 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 {
public enum BlendMode {
Normal, Additive, Multiply, Screen
}
}

View File

@@ -1,378 +1,378 @@
/******************************************************************************
* 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>
/// Stores a bone's current pose.
/// <para>
/// A bone has a local transform which is used to compute its world transform. A bone also has an applied transform, which is a
/// local transform that can be applied to compute the world transform. The local transform and applied transform may differ if a
/// constraint or application code modifies the world transform after it was computed from the local transform.
/// </para>
/// </summary>
public class Bone : IUpdatable {
static public bool yDown;
internal BoneData data;
internal Skeleton skeleton;
internal Bone parent;
internal ExposedList<Bone> children = new ExposedList<Bone>();
internal float x, y, rotation, scaleX, scaleY, shearX, shearY;
internal float ax, ay, arotation, ascaleX, ascaleY, ashearX, ashearY;
internal float a, b, worldX;
internal float c, d, worldY;
internal bool sorted, active;
public BoneData Data { get { return data; } }
public Skeleton Skeleton { get { return skeleton; } }
public Bone Parent { get { return parent; } }
public ExposedList<Bone> Children { get { return children; } }
/// <summary>Returns false when the bone has not been computed because <see cref="BoneData.SkinRequired"/> is true and the
/// <see cref="Skeleton.Skin">active skin</see> does not <see cref="Skin.Bones">contain</see> this bone.</summary>
public bool Active { get { return active; } }
/// <summary>The local X translation.</summary>
public float X { get { return x; } set { x = value; } }
/// <summary>The local Y translation.</summary>
public float Y { get { return y; } set { y = value; } }
/// <summary>The local rotation.</summary>
public float Rotation { get { return rotation; } set { rotation = value; } }
/// <summary>The local scaleX.</summary>
public float ScaleX { get { return scaleX; } set { scaleX = value; } }
/// <summary>The local scaleY.</summary>
public float ScaleY { get { return scaleY; } set { scaleY = value; } }
/// <summary>The local shearX.</summary>
public float ShearX { get { return shearX; } set { shearX = value; } }
/// <summary>The local shearY.</summary>
public float ShearY { get { return shearY; } set { shearY = value; } }
/// <summary>The rotation, as calculated by any constraints.</summary>
public float AppliedRotation { get { return arotation; } set { arotation = value; } }
/// <summary>The applied local x translation.</summary>
public float AX { get { return ax; } set { ax = value; } }
/// <summary>The applied local y translation.</summary>
public float AY { get { return ay; } set { ay = value; } }
/// <summary>The applied local scaleX.</summary>
public float AScaleX { get { return ascaleX; } set { ascaleX = value; } }
/// <summary>The applied local scaleY.</summary>
public float AScaleY { get { return ascaleY; } set { ascaleY = value; } }
/// <summary>The applied local shearX.</summary>
public float AShearX { get { return ashearX; } set { ashearX = value; } }
/// <summary>The applied local shearY.</summary>
public float AShearY { get { return ashearY; } set { ashearY = value; } }
/// <summary>Part of the world transform matrix for the X axis. If changed, <see cref="UpdateAppliedTransform()"/> should be called.</summary>
public float A { get { return a; } set { a = value; } }
/// <summary>Part of the world transform matrix for the Y axis. If changed, <see cref="UpdateAppliedTransform()"/> should be called.</summary>
public float B { get { return b; } set { b = value; } }
/// <summary>Part of the world transform matrix for the X axis. If changed, <see cref="UpdateAppliedTransform()"/> should be called.</summary>
public float C { get { return c; } set { c = value; } }
/// <summary>Part of the world transform matrix for the Y axis. If changed, <see cref="UpdateAppliedTransform()"/> should be called.</summary>
public float D { get { return d; } set { d = value; } }
/// <summary>The world X position. If changed, <see cref="UpdateAppliedTransform()"/> should be called.</summary>
public float WorldX { get { return worldX; } set { worldX = value; } }
/// <summary>The world Y position. If changed, <see cref="UpdateAppliedTransform()"/> should be called.</summary>
public float WorldY { get { return worldY; } set { worldY = value; } }
public float WorldRotationX { get { return MathUtils.Atan2(c, a) * MathUtils.RadDeg; } }
public float WorldRotationY { get { return MathUtils.Atan2(d, b) * MathUtils.RadDeg; } }
/// <summary>Returns the magnitide (always positive) of the world scale X.</summary>
public float WorldScaleX { get { return (float)Math.Sqrt(a * a + c * c); } }
/// <summary>Returns the magnitide (always positive) of the world scale Y.</summary>
public float WorldScaleY { get { return (float)Math.Sqrt(b * b + d * d); } }
/// <summary>Copy constructor. Does not copy the <see cref="Children"/> bones.</summary>
/// <param name="parent">May be null.</param>
public Bone (BoneData data, Skeleton skeleton, Bone parent) {
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;
this.skeleton = skeleton;
this.parent = parent;
SetToSetupPose();
}
/// <summary>Computes the world transform using the parent bone and this bone's local applied transform.</summary>
public void Update () {
UpdateWorldTransform(ax, ay, arotation, ascaleX, ascaleY, ashearX, ashearY);
}
/// <summary>Computes the world transform using the parent bone and this bone's local transform.</summary>
public void UpdateWorldTransform () {
UpdateWorldTransform(x, y, rotation, scaleX, scaleY, shearX, shearY);
}
/// <summary>Computes the world transform using the parent bone and the specified local transform. The applied transform is set to the
/// specified local transform. Child bones are not updated.
/// <para>
/// See <a href="http://esotericsoftware.com/spine-runtime-skeletons#World-transforms">World transforms</a> in the Spine
/// Runtimes Guide.</para></summary>
public void UpdateWorldTransform (float x, float y, float rotation, float scaleX, float scaleY, float shearX, float shearY) {
ax = x;
ay = y;
arotation = rotation;
ascaleX = scaleX;
ascaleY = scaleY;
ashearX = shearX;
ashearY = shearY;
Bone parent = this.parent;
if (parent == null) { // Root bone.
float rotationY = rotation + 90 + shearY, sx = skeleton.ScaleX, sy = skeleton.ScaleY;
a = MathUtils.CosDeg(rotation + shearX) * scaleX * sx;
b = MathUtils.CosDeg(rotationY) * scaleY * sx;
c = MathUtils.SinDeg(rotation + shearX) * scaleX * sy;
d = MathUtils.SinDeg(rotationY) * scaleY * sy;
worldX = x * sx + skeleton.x;
worldY = y * sy + skeleton.y;
return;
}
float pa = parent.a, pb = parent.b, pc = parent.c, pd = parent.d;
worldX = pa * x + pb * y + parent.worldX;
worldY = pc * x + pd * y + parent.worldY;
switch (data.transformMode) {
case TransformMode.Normal: {
float rotationY = rotation + 90 + shearY;
float la = MathUtils.CosDeg(rotation + shearX) * scaleX;
float lb = MathUtils.CosDeg(rotationY) * scaleY;
float lc = MathUtils.SinDeg(rotation + shearX) * scaleX;
float ld = MathUtils.SinDeg(rotationY) * scaleY;
a = pa * la + pb * lc;
b = pa * lb + pb * ld;
c = pc * la + pd * lc;
d = pc * lb + pd * ld;
return;
}
case TransformMode.OnlyTranslation: {
float rotationY = rotation + 90 + shearY;
a = MathUtils.CosDeg(rotation + shearX) * scaleX;
b = MathUtils.CosDeg(rotationY) * scaleY;
c = MathUtils.SinDeg(rotation + shearX) * scaleX;
d = MathUtils.SinDeg(rotationY) * scaleY;
break;
}
case TransformMode.NoRotationOrReflection: {
float s = pa * pa + pc * pc, prx;
if (s > 0.0001f) {
s = Math.Abs(pa * pd - pb * pc) / s;
pa /= skeleton.ScaleX;
pc /= skeleton.ScaleY;
pb = pc * s;
pd = pa * s;
prx = MathUtils.Atan2(pc, pa) * MathUtils.RadDeg;
} else {
pa = 0;
pc = 0;
prx = 90 - MathUtils.Atan2(pd, pb) * MathUtils.RadDeg;
}
float rx = rotation + shearX - prx;
float ry = rotation + shearY - prx + 90;
float la = MathUtils.CosDeg(rx) * scaleX;
float lb = MathUtils.CosDeg(ry) * scaleY;
float lc = MathUtils.SinDeg(rx) * scaleX;
float ld = MathUtils.SinDeg(ry) * scaleY;
a = pa * la - pb * lc;
b = pa * lb - pb * ld;
c = pc * la + pd * lc;
d = pc * lb + pd * ld;
break;
}
case TransformMode.NoScale:
case TransformMode.NoScaleOrReflection: {
float cos = MathUtils.CosDeg(rotation), sin = MathUtils.SinDeg(rotation);
float za = (pa * cos + pb * sin) / skeleton.ScaleX;
float zc = (pc * cos + pd * sin) / skeleton.ScaleY;
float s = (float)Math.Sqrt(za * za + zc * zc);
if (s > 0.00001f) s = 1 / s;
za *= s;
zc *= s;
s = (float)Math.Sqrt(za * za + zc * zc);
if (data.transformMode == TransformMode.NoScale
&& (pa * pd - pb * pc < 0) != (skeleton.ScaleX < 0 != skeleton.ScaleY < 0)) s = -s;
float r = MathUtils.PI / 2 + MathUtils.Atan2(zc, za);
float zb = MathUtils.Cos(r) * s;
float zd = MathUtils.Sin(r) * s;
float la = MathUtils.CosDeg(shearX) * scaleX;
float lb = MathUtils.CosDeg(90 + shearY) * scaleY;
float lc = MathUtils.SinDeg(shearX) * scaleX;
float ld = MathUtils.SinDeg(90 + shearY) * scaleY;
a = za * la + zb * lc;
b = za * lb + zb * ld;
c = zc * la + zd * lc;
d = zc * lb + zd * ld;
break;
}
}
a *= skeleton.ScaleX;
b *= skeleton.ScaleX;
c *= skeleton.ScaleY;
d *= skeleton.ScaleY;
}
public void SetToSetupPose () {
BoneData data = this.data;
x = data.x;
y = data.y;
rotation = data.rotation;
scaleX = data.scaleX;
scaleY = data.scaleY;
shearX = data.shearX;
shearY = data.shearY;
}
/// <summary>
/// Computes the applied transform values from the world transform.
/// <para>
/// If the world transform is modified (by a constraint, <see cref="RotateWorld(float)"/>, etc) then this method should be called so
/// the applied transform matches the world transform. The applied transform may be needed by other code (eg to apply another
/// constraint).
/// </para><para>
/// Some information is ambiguous in the world transform, such as -1,-1 scale versus 180 rotation. The applied transform after
/// calling this method is equivalent to the local transform used to compute the world transform, but may not be identical.
/// </para></summary>
public void UpdateAppliedTransform () {
Bone parent = this.parent;
if (parent == null) {
ax = worldX - skeleton.x;
ay = worldY - skeleton.y;
arotation = MathUtils.Atan2(c, a) * MathUtils.RadDeg;
ascaleX = (float)Math.Sqrt(a * a + c * c);
ascaleY = (float)Math.Sqrt(b * b + d * d);
ashearX = 0;
ashearY = MathUtils.Atan2(a * b + c * d, a * d - b * c) * MathUtils.RadDeg;
return;
}
float pa = parent.a, pb = parent.b, pc = parent.c, pd = parent.d;
float pid = 1 / (pa * pd - pb * pc);
float dx = worldX - parent.worldX, dy = worldY - parent.worldY;
ax = (dx * pd * pid - dy * pb * pid);
ay = (dy * pa * pid - dx * pc * pid);
float ia = pid * pd;
float id = pid * pa;
float ib = pid * pb;
float ic = pid * pc;
float ra = ia * a - ib * c;
float rb = ia * b - ib * d;
float rc = id * c - ic * a;
float rd = id * d - ic * b;
ashearX = 0;
ascaleX = (float)Math.Sqrt(ra * ra + rc * rc);
if (ascaleX > 0.0001f) {
float det = ra * rd - rb * rc;
ascaleY = det / ascaleX;
ashearY = MathUtils.Atan2(ra * rb + rc * rd, det) * MathUtils.RadDeg;
arotation = MathUtils.Atan2(rc, ra) * MathUtils.RadDeg;
} else {
ascaleX = 0;
ascaleY = (float)Math.Sqrt(rb * rb + rd * rd);
ashearY = 0;
arotation = 90 - MathUtils.Atan2(rd, rb) * MathUtils.RadDeg;
}
}
public void WorldToLocal (float worldX, float worldY, out float localX, out float localY) {
float a = this.a, b = this.b, c = this.c, d = this.d;
float det = a * d - b * c;
float x = worldX - this.worldX, y = worldY - this.worldY;
localX = (x * d - y * b) / det;
localY = (y * a - x * c) / det;
}
public void LocalToWorld (float localX, float localY, out float worldX, out float worldY) {
worldX = localX * a + localY * b + this.worldX;
worldY = localX * c + localY * d + this.worldY;
}
public float WorldToLocalRotationX {
get {
Bone parent = this.parent;
if (parent == null) return arotation;
float pa = parent.a, pb = parent.b, pc = parent.c, pd = parent.d, a = this.a, c = this.c;
return MathUtils.Atan2(pa * c - pc * a, pd * a - pb * c) * MathUtils.RadDeg;
}
}
public float WorldToLocalRotationY {
get {
Bone parent = this.parent;
if (parent == null) return arotation;
float pa = parent.a, pb = parent.b, pc = parent.c, pd = parent.d, b = this.b, d = this.d;
return MathUtils.Atan2(pa * d - pc * b, pd * b - pb * d) * MathUtils.RadDeg;
}
}
public float WorldToLocalRotation (float worldRotation) {
float sin = MathUtils.SinDeg(worldRotation), cos = MathUtils.CosDeg(worldRotation);
return MathUtils.Atan2(a * sin - c * cos, d * cos - b * sin) * MathUtils.RadDeg + rotation - shearX;
}
public float LocalToWorldRotation (float localRotation) {
localRotation -= rotation - shearX;
float sin = MathUtils.SinDeg(localRotation), cos = MathUtils.CosDeg(localRotation);
return MathUtils.Atan2(cos * c + sin * d, cos * a + sin * b) * MathUtils.RadDeg;
}
/// <summary>
/// Rotates the world transform the specified amount.
/// <para>
/// After changes are made to the world transform, <see cref="UpdateAppliedTransform()"/> should be called and <see cref="Update()"/> will
/// need to be called on any child bones, recursively.
/// </para></summary>
public void RotateWorld (float degrees) {
float a = this.a, b = this.b, c = this.c, d = this.d;
float cos = MathUtils.CosDeg(degrees), sin = MathUtils.SinDeg(degrees);
this.a = cos * a - sin * c;
this.b = cos * b - sin * d;
this.c = sin * a + cos * c;
this.d = sin * b + cos * d;
}
override public string ToString () {
return data.name;
}
}
}
/******************************************************************************
* 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>
/// Stores a bone's current pose.
/// <para>
/// A bone has a local transform which is used to compute its world transform. A bone also has an applied transform, which is a
/// local transform that can be applied to compute the world transform. The local transform and applied transform may differ if a
/// constraint or application code modifies the world transform after it was computed from the local transform.
/// </para>
/// </summary>
public class Bone : IUpdatable {
static public bool yDown;
internal BoneData data;
internal Skeleton skeleton;
internal Bone parent;
internal ExposedList<Bone> children = new ExposedList<Bone>();
internal float x, y, rotation, scaleX, scaleY, shearX, shearY;
internal float ax, ay, arotation, ascaleX, ascaleY, ashearX, ashearY;
internal float a, b, worldX;
internal float c, d, worldY;
internal bool sorted, active;
public BoneData Data { get { return data; } }
public Skeleton Skeleton { get { return skeleton; } }
public Bone Parent { get { return parent; } }
public ExposedList<Bone> Children { get { return children; } }
/// <summary>Returns false when the bone has not been computed because <see cref="BoneData.SkinRequired"/> is true and the
/// <see cref="Skeleton.Skin">active skin</see> does not <see cref="Skin.Bones">contain</see> this bone.</summary>
public bool Active { get { return active; } }
/// <summary>The local X translation.</summary>
public float X { get { return x; } set { x = value; } }
/// <summary>The local Y translation.</summary>
public float Y { get { return y; } set { y = value; } }
/// <summary>The local rotation.</summary>
public float Rotation { get { return rotation; } set { rotation = value; } }
/// <summary>The local scaleX.</summary>
public float ScaleX { get { return scaleX; } set { scaleX = value; } }
/// <summary>The local scaleY.</summary>
public float ScaleY { get { return scaleY; } set { scaleY = value; } }
/// <summary>The local shearX.</summary>
public float ShearX { get { return shearX; } set { shearX = value; } }
/// <summary>The local shearY.</summary>
public float ShearY { get { return shearY; } set { shearY = value; } }
/// <summary>The rotation, as calculated by any constraints.</summary>
public float AppliedRotation { get { return arotation; } set { arotation = value; } }
/// <summary>The applied local x translation.</summary>
public float AX { get { return ax; } set { ax = value; } }
/// <summary>The applied local y translation.</summary>
public float AY { get { return ay; } set { ay = value; } }
/// <summary>The applied local scaleX.</summary>
public float AScaleX { get { return ascaleX; } set { ascaleX = value; } }
/// <summary>The applied local scaleY.</summary>
public float AScaleY { get { return ascaleY; } set { ascaleY = value; } }
/// <summary>The applied local shearX.</summary>
public float AShearX { get { return ashearX; } set { ashearX = value; } }
/// <summary>The applied local shearY.</summary>
public float AShearY { get { return ashearY; } set { ashearY = value; } }
/// <summary>Part of the world transform matrix for the X axis. If changed, <see cref="UpdateAppliedTransform()"/> should be called.</summary>
public float A { get { return a; } set { a = value; } }
/// <summary>Part of the world transform matrix for the Y axis. If changed, <see cref="UpdateAppliedTransform()"/> should be called.</summary>
public float B { get { return b; } set { b = value; } }
/// <summary>Part of the world transform matrix for the X axis. If changed, <see cref="UpdateAppliedTransform()"/> should be called.</summary>
public float C { get { return c; } set { c = value; } }
/// <summary>Part of the world transform matrix for the Y axis. If changed, <see cref="UpdateAppliedTransform()"/> should be called.</summary>
public float D { get { return d; } set { d = value; } }
/// <summary>The world X position. If changed, <see cref="UpdateAppliedTransform()"/> should be called.</summary>
public float WorldX { get { return worldX; } set { worldX = value; } }
/// <summary>The world Y position. If changed, <see cref="UpdateAppliedTransform()"/> should be called.</summary>
public float WorldY { get { return worldY; } set { worldY = value; } }
public float WorldRotationX { get { return MathUtils.Atan2(c, a) * MathUtils.RadDeg; } }
public float WorldRotationY { get { return MathUtils.Atan2(d, b) * MathUtils.RadDeg; } }
/// <summary>Returns the magnitide (always positive) of the world scale X.</summary>
public float WorldScaleX { get { return (float)Math.Sqrt(a * a + c * c); } }
/// <summary>Returns the magnitide (always positive) of the world scale Y.</summary>
public float WorldScaleY { get { return (float)Math.Sqrt(b * b + d * d); } }
/// <summary>Copy constructor. Does not copy the <see cref="Children"/> bones.</summary>
/// <param name="parent">May be null.</param>
public Bone (BoneData data, Skeleton skeleton, Bone parent) {
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;
this.skeleton = skeleton;
this.parent = parent;
SetToSetupPose();
}
/// <summary>Computes the world transform using the parent bone and this bone's local applied transform.</summary>
public void Update () {
UpdateWorldTransform(ax, ay, arotation, ascaleX, ascaleY, ashearX, ashearY);
}
/// <summary>Computes the world transform using the parent bone and this bone's local transform.</summary>
public void UpdateWorldTransform () {
UpdateWorldTransform(x, y, rotation, scaleX, scaleY, shearX, shearY);
}
/// <summary>Computes the world transform using the parent bone and the specified local transform. The applied transform is set to the
/// specified local transform. Child bones are not updated.
/// <para>
/// See <a href="http://esotericsoftware.com/spine-runtime-skeletons#World-transforms">World transforms</a> in the Spine
/// Runtimes Guide.</para></summary>
public void UpdateWorldTransform (float x, float y, float rotation, float scaleX, float scaleY, float shearX, float shearY) {
ax = x;
ay = y;
arotation = rotation;
ascaleX = scaleX;
ascaleY = scaleY;
ashearX = shearX;
ashearY = shearY;
Bone parent = this.parent;
if (parent == null) { // Root bone.
float rotationY = rotation + 90 + shearY, sx = skeleton.ScaleX, sy = skeleton.ScaleY;
a = MathUtils.CosDeg(rotation + shearX) * scaleX * sx;
b = MathUtils.CosDeg(rotationY) * scaleY * sx;
c = MathUtils.SinDeg(rotation + shearX) * scaleX * sy;
d = MathUtils.SinDeg(rotationY) * scaleY * sy;
worldX = x * sx + skeleton.x;
worldY = y * sy + skeleton.y;
return;
}
float pa = parent.a, pb = parent.b, pc = parent.c, pd = parent.d;
worldX = pa * x + pb * y + parent.worldX;
worldY = pc * x + pd * y + parent.worldY;
switch (data.transformMode) {
case TransformMode.Normal: {
float rotationY = rotation + 90 + shearY;
float la = MathUtils.CosDeg(rotation + shearX) * scaleX;
float lb = MathUtils.CosDeg(rotationY) * scaleY;
float lc = MathUtils.SinDeg(rotation + shearX) * scaleX;
float ld = MathUtils.SinDeg(rotationY) * scaleY;
a = pa * la + pb * lc;
b = pa * lb + pb * ld;
c = pc * la + pd * lc;
d = pc * lb + pd * ld;
return;
}
case TransformMode.OnlyTranslation: {
float rotationY = rotation + 90 + shearY;
a = MathUtils.CosDeg(rotation + shearX) * scaleX;
b = MathUtils.CosDeg(rotationY) * scaleY;
c = MathUtils.SinDeg(rotation + shearX) * scaleX;
d = MathUtils.SinDeg(rotationY) * scaleY;
break;
}
case TransformMode.NoRotationOrReflection: {
float s = pa * pa + pc * pc, prx;
if (s > 0.0001f) {
s = Math.Abs(pa * pd - pb * pc) / s;
pa /= skeleton.ScaleX;
pc /= skeleton.ScaleY;
pb = pc * s;
pd = pa * s;
prx = MathUtils.Atan2(pc, pa) * MathUtils.RadDeg;
} else {
pa = 0;
pc = 0;
prx = 90 - MathUtils.Atan2(pd, pb) * MathUtils.RadDeg;
}
float rx = rotation + shearX - prx;
float ry = rotation + shearY - prx + 90;
float la = MathUtils.CosDeg(rx) * scaleX;
float lb = MathUtils.CosDeg(ry) * scaleY;
float lc = MathUtils.SinDeg(rx) * scaleX;
float ld = MathUtils.SinDeg(ry) * scaleY;
a = pa * la - pb * lc;
b = pa * lb - pb * ld;
c = pc * la + pd * lc;
d = pc * lb + pd * ld;
break;
}
case TransformMode.NoScale:
case TransformMode.NoScaleOrReflection: {
float cos = MathUtils.CosDeg(rotation), sin = MathUtils.SinDeg(rotation);
float za = (pa * cos + pb * sin) / skeleton.ScaleX;
float zc = (pc * cos + pd * sin) / skeleton.ScaleY;
float s = (float)Math.Sqrt(za * za + zc * zc);
if (s > 0.00001f) s = 1 / s;
za *= s;
zc *= s;
s = (float)Math.Sqrt(za * za + zc * zc);
if (data.transformMode == TransformMode.NoScale
&& (pa * pd - pb * pc < 0) != (skeleton.ScaleX < 0 != skeleton.ScaleY < 0)) s = -s;
float r = MathUtils.PI / 2 + MathUtils.Atan2(zc, za);
float zb = MathUtils.Cos(r) * s;
float zd = MathUtils.Sin(r) * s;
float la = MathUtils.CosDeg(shearX) * scaleX;
float lb = MathUtils.CosDeg(90 + shearY) * scaleY;
float lc = MathUtils.SinDeg(shearX) * scaleX;
float ld = MathUtils.SinDeg(90 + shearY) * scaleY;
a = za * la + zb * lc;
b = za * lb + zb * ld;
c = zc * la + zd * lc;
d = zc * lb + zd * ld;
break;
}
}
a *= skeleton.ScaleX;
b *= skeleton.ScaleX;
c *= skeleton.ScaleY;
d *= skeleton.ScaleY;
}
public void SetToSetupPose () {
BoneData data = this.data;
x = data.x;
y = data.y;
rotation = data.rotation;
scaleX = data.scaleX;
scaleY = data.scaleY;
shearX = data.shearX;
shearY = data.shearY;
}
/// <summary>
/// Computes the applied transform values from the world transform.
/// <para>
/// If the world transform is modified (by a constraint, <see cref="RotateWorld(float)"/>, etc) then this method should be called so
/// the applied transform matches the world transform. The applied transform may be needed by other code (eg to apply another
/// constraint).
/// </para><para>
/// Some information is ambiguous in the world transform, such as -1,-1 scale versus 180 rotation. The applied transform after
/// calling this method is equivalent to the local transform used to compute the world transform, but may not be identical.
/// </para></summary>
public void UpdateAppliedTransform () {
Bone parent = this.parent;
if (parent == null) {
ax = worldX - skeleton.x;
ay = worldY - skeleton.y;
arotation = MathUtils.Atan2(c, a) * MathUtils.RadDeg;
ascaleX = (float)Math.Sqrt(a * a + c * c);
ascaleY = (float)Math.Sqrt(b * b + d * d);
ashearX = 0;
ashearY = MathUtils.Atan2(a * b + c * d, a * d - b * c) * MathUtils.RadDeg;
return;
}
float pa = parent.a, pb = parent.b, pc = parent.c, pd = parent.d;
float pid = 1 / (pa * pd - pb * pc);
float dx = worldX - parent.worldX, dy = worldY - parent.worldY;
ax = (dx * pd * pid - dy * pb * pid);
ay = (dy * pa * pid - dx * pc * pid);
float ia = pid * pd;
float id = pid * pa;
float ib = pid * pb;
float ic = pid * pc;
float ra = ia * a - ib * c;
float rb = ia * b - ib * d;
float rc = id * c - ic * a;
float rd = id * d - ic * b;
ashearX = 0;
ascaleX = (float)Math.Sqrt(ra * ra + rc * rc);
if (ascaleX > 0.0001f) {
float det = ra * rd - rb * rc;
ascaleY = det / ascaleX;
ashearY = MathUtils.Atan2(ra * rb + rc * rd, det) * MathUtils.RadDeg;
arotation = MathUtils.Atan2(rc, ra) * MathUtils.RadDeg;
} else {
ascaleX = 0;
ascaleY = (float)Math.Sqrt(rb * rb + rd * rd);
ashearY = 0;
arotation = 90 - MathUtils.Atan2(rd, rb) * MathUtils.RadDeg;
}
}
public void WorldToLocal (float worldX, float worldY, out float localX, out float localY) {
float a = this.a, b = this.b, c = this.c, d = this.d;
float det = a * d - b * c;
float x = worldX - this.worldX, y = worldY - this.worldY;
localX = (x * d - y * b) / det;
localY = (y * a - x * c) / det;
}
public void LocalToWorld (float localX, float localY, out float worldX, out float worldY) {
worldX = localX * a + localY * b + this.worldX;
worldY = localX * c + localY * d + this.worldY;
}
public float WorldToLocalRotationX {
get {
Bone parent = this.parent;
if (parent == null) return arotation;
float pa = parent.a, pb = parent.b, pc = parent.c, pd = parent.d, a = this.a, c = this.c;
return MathUtils.Atan2(pa * c - pc * a, pd * a - pb * c) * MathUtils.RadDeg;
}
}
public float WorldToLocalRotationY {
get {
Bone parent = this.parent;
if (parent == null) return arotation;
float pa = parent.a, pb = parent.b, pc = parent.c, pd = parent.d, b = this.b, d = this.d;
return MathUtils.Atan2(pa * d - pc * b, pd * b - pb * d) * MathUtils.RadDeg;
}
}
public float WorldToLocalRotation (float worldRotation) {
float sin = MathUtils.SinDeg(worldRotation), cos = MathUtils.CosDeg(worldRotation);
return MathUtils.Atan2(a * sin - c * cos, d * cos - b * sin) * MathUtils.RadDeg + rotation - shearX;
}
public float LocalToWorldRotation (float localRotation) {
localRotation -= rotation - shearX;
float sin = MathUtils.SinDeg(localRotation), cos = MathUtils.CosDeg(localRotation);
return MathUtils.Atan2(cos * c + sin * d, cos * a + sin * b) * MathUtils.RadDeg;
}
/// <summary>
/// Rotates the world transform the specified amount.
/// <para>
/// After changes are made to the world transform, <see cref="UpdateAppliedTransform()"/> should be called and <see cref="Update()"/> will
/// need to be called on any child bones, recursively.
/// </para></summary>
public void RotateWorld (float degrees) {
float a = this.a, b = this.b, c = this.c, d = this.d;
float cos = MathUtils.CosDeg(degrees), sin = MathUtils.SinDeg(degrees);
this.a = cos * a - sin * c;
this.b = cos * b - sin * d;
this.c = sin * a + cos * c;
this.d = sin * b + cos * d;
}
override public string ToString () {
return data.name;
}
}
}

View File

@@ -1,105 +1,105 @@
/******************************************************************************
* 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 BoneData {
internal int index;
internal string name;
internal BoneData parent;
internal float length;
internal float x, y, rotation, scaleX = 1, scaleY = 1, shearX, shearY;
internal TransformMode transformMode = TransformMode.Normal;
internal bool skinRequired;
/// <summary>The index of the bone in Skeleton.Bones</summary>
public int Index { get { return index; } }
/// <summary>The name of the bone, which is unique across all bones in the skeleton.</summary>
public string Name { get { return name; } }
/// <summary>May be null.</summary>
public BoneData Parent { get { return parent; } }
public float Length { get { return length; } set { length = value; } }
/// <summary>Local X translation.</summary>
public float X { get { return x; } set { x = value; } }
/// <summary>Local Y translation.</summary>
public float Y { get { return y; } set { y = value; } }
/// <summary>Local rotation.</summary>
public float Rotation { get { return rotation; } set { rotation = value; } }
/// <summary>Local scaleX.</summary>
public float ScaleX { get { return scaleX; } set { scaleX = value; } }
/// <summary>Local scaleY.</summary>
public float ScaleY { get { return scaleY; } set { scaleY = value; } }
/// <summary>Local shearX.</summary>
public float ShearX { get { return shearX; } set { shearX = value; } }
/// <summary>Local shearY.</summary>
public float ShearY { get { return shearY; } set { shearY = value; } }
/// <summary>The transform mode for how parent world transforms affect this bone.</summary>
public TransformMode TransformMode { get { return transformMode; } set { transformMode = value; } }
///<summary>When true, <see cref="Skeleton.UpdateWorldTransform()"/> only updates this bone if the <see cref="Skeleton.Skin"/> contains this
/// bone.</summary>
/// <seealso cref="Skin.Bones"/>
public bool SkinRequired { get { return skinRequired; } set { skinRequired = value; } }
/// <param name="parent">May be null.</param>
public BoneData (int index, string name, BoneData parent) {
if (index < 0) throw new ArgumentException("index must be >= 0", "index");
if (name == null) throw new ArgumentNullException("name", "name cannot be null.");
this.index = index;
this.name = name;
this.parent = parent;
}
override public string ToString () {
return name;
}
}
[Flags]
public enum TransformMode {
//0000 0 Flip Scale Rotation
Normal = 0, // 0000
OnlyTranslation = 7, // 0111
NoRotationOrReflection = 1, // 0001
NoScale = 2, // 0010
NoScaleOrReflection = 6, // 0110
}
}
/******************************************************************************
* 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 BoneData {
internal int index;
internal string name;
internal BoneData parent;
internal float length;
internal float x, y, rotation, scaleX = 1, scaleY = 1, shearX, shearY;
internal TransformMode transformMode = TransformMode.Normal;
internal bool skinRequired;
/// <summary>The index of the bone in Skeleton.Bones</summary>
public int Index { get { return index; } }
/// <summary>The name of the bone, which is unique across all bones in the skeleton.</summary>
public string Name { get { return name; } }
/// <summary>May be null.</summary>
public BoneData Parent { get { return parent; } }
public float Length { get { return length; } set { length = value; } }
/// <summary>Local X translation.</summary>
public float X { get { return x; } set { x = value; } }
/// <summary>Local Y translation.</summary>
public float Y { get { return y; } set { y = value; } }
/// <summary>Local rotation.</summary>
public float Rotation { get { return rotation; } set { rotation = value; } }
/// <summary>Local scaleX.</summary>
public float ScaleX { get { return scaleX; } set { scaleX = value; } }
/// <summary>Local scaleY.</summary>
public float ScaleY { get { return scaleY; } set { scaleY = value; } }
/// <summary>Local shearX.</summary>
public float ShearX { get { return shearX; } set { shearX = value; } }
/// <summary>Local shearY.</summary>
public float ShearY { get { return shearY; } set { shearY = value; } }
/// <summary>The transform mode for how parent world transforms affect this bone.</summary>
public TransformMode TransformMode { get { return transformMode; } set { transformMode = value; } }
///<summary>When true, <see cref="Skeleton.UpdateWorldTransform()"/> only updates this bone if the <see cref="Skeleton.Skin"/> contains this
/// bone.</summary>
/// <seealso cref="Skin.Bones"/>
public bool SkinRequired { get { return skinRequired; } set { skinRequired = value; } }
/// <param name="parent">May be null.</param>
public BoneData (int index, string name, BoneData parent) {
if (index < 0) throw new ArgumentException("index must be >= 0", "index");
if (name == null) throw new ArgumentNullException("name", "name cannot be null.");
this.index = index;
this.name = name;
this.parent = parent;
}
override public string ToString () {
return name;
}
}
[Flags]
public enum TransformMode {
//0000 0 Flip Scale Rotation
Normal = 0, // 0000
OnlyTranslation = 7, // 0111
NoRotationOrReflection = 1, // 0001
NoScale = 2, // 0010
NoScaleOrReflection = 6, // 0110
}
}

View File

@@ -1,61 +1,61 @@
/******************************************************************************
* 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>The base class for all constraint datas.</summary>
public abstract class ConstraintData {
internal readonly string name;
internal int order;
internal bool skinRequired;
public ConstraintData (string name) {
if (name == null) throw new ArgumentNullException("name", "name cannot be null.");
this.name = name;
}
/// <summary> The constraint's name, which is unique across all constraints in the skeleton of the same type.</summary>
public string Name { get { return name; } }
///<summary>The ordinal of this constraint for the order a skeleton's constraints will be applied by
/// <see cref="Skeleton.UpdateWorldTransform()"/>.</summary>
public int Order { get { return order; } set { order = value; } }
///<summary>When true, <see cref="Skeleton.UpdateWorldTransform()"/> only updates this constraint if the <see cref="Skeleton.Skin"/> contains
/// this constraint.</summary>
///<seealso cref="Skin.Constraints"/>
public bool SkinRequired { get { return skinRequired; } set { skinRequired = value; } }
override public string ToString () {
return name;
}
}
}
/******************************************************************************
* 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>The base class for all constraint datas.</summary>
public abstract class ConstraintData {
internal readonly string name;
internal int order;
internal bool skinRequired;
public ConstraintData (string name) {
if (name == null) throw new ArgumentNullException("name", "name cannot be null.");
this.name = name;
}
/// <summary> The constraint's name, which is unique across all constraints in the skeleton of the same type.</summary>
public string Name { get { return name; } }
///<summary>The ordinal of this constraint for the order a skeleton's constraints will be applied by
/// <see cref="Skeleton.UpdateWorldTransform()"/>.</summary>
public int Order { get { return order; } set { order = value; } }
///<summary>When true, <see cref="Skeleton.UpdateWorldTransform()"/> only updates this constraint if the <see cref="Skeleton.Skin"/> contains
/// this constraint.</summary>
///<seealso cref="Skin.Constraints"/>
public bool SkinRequired { get { return skinRequired; } set { skinRequired = value; } }
override public string ToString () {
return name;
}
}
}

View File

@@ -1,64 +1,64 @@
/******************************************************************************
* 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>Stores the current pose values for an Event.</summary>
public class Event {
internal readonly EventData data;
internal readonly float time;
internal int intValue;
internal float floatValue;
internal string stringValue;
internal float volume;
internal float balance;
public EventData Data { get { return data; } }
/// <summary>The animation time this event was keyed.</summary>
public float Time { get { return time; } }
public int Int { get { return intValue; } set { intValue = value; } }
public float Float { get { return floatValue; } set { floatValue = value; } }
public string String { get { return stringValue; } set { stringValue = value; } }
public float Volume { get { return volume; } set { volume = value; } }
public float Balance { get { return balance; } set { balance = value; } }
public Event (float time, EventData data) {
if (data == null) throw new ArgumentNullException("data", "data cannot be null.");
this.time = time;
this.data = data;
}
override public string ToString () {
return this.data.Name;
}
}
}
/******************************************************************************
* 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>Stores the current pose values for an Event.</summary>
public class Event {
internal readonly EventData data;
internal readonly float time;
internal int intValue;
internal float floatValue;
internal string stringValue;
internal float volume;
internal float balance;
public EventData Data { get { return data; } }
/// <summary>The animation time this event was keyed.</summary>
public float Time { get { return time; } }
public int Int { get { return intValue; } set { intValue = value; } }
public float Float { get { return floatValue; } set { floatValue = value; } }
public string String { get { return stringValue; } set { stringValue = value; } }
public float Volume { get { return volume; } set { volume = value; } }
public float Balance { get { return balance; } set { balance = value; } }
public Event (float time, EventData data) {
if (data == null) throw new ArgumentNullException("data", "data cannot be null.");
this.time = time;
this.data = data;
}
override public string ToString () {
return this.data.Name;
}
}
}

View File

@@ -1,56 +1,56 @@
/******************************************************************************
* 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>Stores the setup pose values for an Event.</summary>
public class EventData {
internal string name;
/// <summary>The name of the event, which is unique across all events in the skeleton.</summary>
public string Name { get { return name; } }
public int Int { get; set; }
public float Float { get; set; }
public string @String { get; set; }
public string AudioPath { get; set; }
public float Volume { get; set; }
public float Balance { get; set; }
public EventData (string name) {
if (name == null) throw new ArgumentNullException("name", "name cannot be null.");
this.name = name;
}
override public string ToString () {
return Name;
}
}
}
/******************************************************************************
* 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>Stores the setup pose values for an Event.</summary>
public class EventData {
internal string name;
/// <summary>The name of the event, which is unique across all events in the skeleton.</summary>
public string Name { get { return name; } }
public int Int { get; set; }
public float Float { get; set; }
public string @String { get; set; }
public string AudioPath { get; set; }
public float Volume { get; set; }
public float Balance { get; set; }
public EventData (string name) {
if (name == null) throw new ArgumentNullException("name", "name cannot be null.");
this.name = name;
}
override public string ToString () {
return Name;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,42 +1,42 @@
/******************************************************************************
* 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; }
}
}
/******************************************************************************
* 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,368 +1,368 @@
/******************************************************************************
* 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;
}
public void Update () {
if (mix == 0) return;
Bone target = this.target;
var bones = this.bones.Items;
switch (this.bones.Count) {
case 1:
Apply(bones[0], target.worldX, target.worldY, compress, stretch, data.uniform, mix);
break;
case 2:
Apply(bones[0], bones[1], target.worldX, target.worldY, bendDirection, stretch, data.uniform, 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 rotation.
/// <para>
/// For two bone IK: if the parent bone has local nonuniform scale, the child bone's local Y translation is set to 0.
/// </para></summary>
public float Mix {
get { return mix; }
set { mix = value; }
}
/// <summary>For two bone IK, the target bone's distance from the maximum reach of the bones where rotation begins to slow. The bones
/// will not straighten completely until the target is this far out of range.</summary>
public float Softness {
get { return softness; }
set { softness = value; }
}
/// <summary>For two bone IK, controls the bend direction of the IK bones, either 1 or -1.</summary>
public int BendDirection {
get { return bendDirection; }
set { bendDirection = value; }
}
/// <summary>For one bone IK, when true and 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 and the target is out of range, the parent bone is scaled to reach it.
/// <para>
/// For two bone IK: 1) the child bone's local Y translation is set to 0,
/// 2) stretch is not applied if <see cref="Softness"/> is > 0,
/// and 3) if the parent bone has local nonuniform scale, stretch is not applied.
/// </para></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 == null) throw new ArgumentNullException("bone", "bone cannot be null.");
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(sc, sa) * 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:
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, bool uniform,
float softness, float alpha) {
if (parent == null) throw new ArgumentNullException("parent", "parent cannot be null.");
if (child == null) throw new ArgumentNullException("child", "child cannot be null.");
float px = parent.ax, py = parent.ay, psx = parent.ascaleX, psy = parent.ascaleY, sx = psx, sy = psy, 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 || stretch) {
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) * 0.5f;
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;
a2 = MathUtils.PI * bendDir;
} else if (cos > 1) {
cos = 1;
a2 = 0;
if (stretch) {
a = ((float)Math.Sqrt(dd) / (l1 + l2) - 1) * alpha + 1;
sx *= a;
if (uniform) sy *= a;
}
} else
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) * 0.5f;
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) * 0.5f) {
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, sy, 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);
}
}
}
/******************************************************************************
* 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;
}
public void Update () {
if (mix == 0) return;
Bone target = this.target;
var bones = this.bones.Items;
switch (this.bones.Count) {
case 1:
Apply(bones[0], target.worldX, target.worldY, compress, stretch, data.uniform, mix);
break;
case 2:
Apply(bones[0], bones[1], target.worldX, target.worldY, bendDirection, stretch, data.uniform, 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 rotation.
/// <para>
/// For two bone IK: if the parent bone has local nonuniform scale, the child bone's local Y translation is set to 0.
/// </para></summary>
public float Mix {
get { return mix; }
set { mix = value; }
}
/// <summary>For two bone IK, the target bone's distance from the maximum reach of the bones where rotation begins to slow. The bones
/// will not straighten completely until the target is this far out of range.</summary>
public float Softness {
get { return softness; }
set { softness = value; }
}
/// <summary>For two bone IK, controls the bend direction of the IK bones, either 1 or -1.</summary>
public int BendDirection {
get { return bendDirection; }
set { bendDirection = value; }
}
/// <summary>For one bone IK, when true and 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 and the target is out of range, the parent bone is scaled to reach it.
/// <para>
/// For two bone IK: 1) the child bone's local Y translation is set to 0,
/// 2) stretch is not applied if <see cref="Softness"/> is > 0,
/// and 3) if the parent bone has local nonuniform scale, stretch is not applied.
/// </para></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 == null) throw new ArgumentNullException("bone", "bone cannot be null.");
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(sc, sa) * 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:
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, bool uniform,
float softness, float alpha) {
if (parent == null) throw new ArgumentNullException("parent", "parent cannot be null.");
if (child == null) throw new ArgumentNullException("child", "child cannot be null.");
float px = parent.ax, py = parent.ay, psx = parent.ascaleX, psy = parent.ascaleY, sx = psx, sy = psy, 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 || stretch) {
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) * 0.5f;
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;
a2 = MathUtils.PI * bendDir;
} else if (cos > 1) {
cos = 1;
a2 = 0;
if (stretch) {
a = ((float)Math.Sqrt(dd) / (l1 + l2) - 1) * alpha + 1;
sx *= a;
if (uniform) sy *= a;
}
} else
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) * 0.5f;
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) * 0.5f) {
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, sy, 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,103 +1,103 @@
/******************************************************************************
* 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 constrained and unconstrained rotation.
/// <para>
/// For two bone IK: if the parent bone has local nonuniform scale, the child bone's local Y translation is set to 0.
/// </para></summary>
public float Mix {
get { return mix; }
set { mix = value; }
}
/// <summary>For two bone IK, the target bone's distance from the maximum reach of the bones where rotation begins to slow. The bones
/// will not straighten completely until the target is this far out of range.</summary>
public float Softness {
get { return softness; }
set { softness = value; }
}
/// <summary>For two bone IK, controls the bend direction of the IK bones, either 1 or -1.</summary>
public int BendDirection {
get { return bendDirection; }
set { bendDirection = value; }
}
/// <summary>For one bone IK, when true and 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 and the target is out of range, the parent bone is scaled to reach it.
/// <para>
/// For two bone IK: 1) the child bone's local Y translation is set to 0,
/// 2) stretch is not applied if <see cref="Softness"/> is > 0,
/// and 3) if the parent bone has local nonuniform scale, stretch is not applied.</para></summary>
public bool Stretch {
get { return stretch; }
set { stretch = value; }
}
/// <summary>
/// When true and <see cref="Compress"/> or <see cref="Stretch"/> is used, the bone is scaled on both the X and Y axes.
/// </summary>
public bool Uniform {
get { return uniform; }
set { uniform = value; }
}
}
}
/******************************************************************************
* 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 constrained and unconstrained rotation.
/// <para>
/// For two bone IK: if the parent bone has local nonuniform scale, the child bone's local Y translation is set to 0.
/// </para></summary>
public float Mix {
get { return mix; }
set { mix = value; }
}
/// <summary>For two bone IK, the target bone's distance from the maximum reach of the bones where rotation begins to slow. The bones
/// will not straighten completely until the target is this far out of range.</summary>
public float Softness {
get { return softness; }
set { softness = value; }
}
/// <summary>For two bone IK, controls the bend direction of the IK bones, either 1 or -1.</summary>
public int BendDirection {
get { return bendDirection; }
set { bendDirection = value; }
}
/// <summary>For one bone IK, when true and 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 and the target is out of range, the parent bone is scaled to reach it.
/// <para>
/// For two bone IK: 1) the child bone's local Y translation is set to 0,
/// 2) stretch is not applied if <see cref="Softness"/> is > 0,
/// and 3) if the parent bone has local nonuniform scale, stretch is not applied.</para></summary>
public bool Stretch {
get { return stretch; }
set { stretch = value; }
}
/// <summary>
/// When true and <see cref="Compress"/> or <see cref="Stretch"/> is used, the bone is scaled on both the X and Y axes.
/// </summary>
public bool Uniform {
get { return uniform; }
set { uniform = value; }
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,173 +1,173 @@
/******************************************************************************
* 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;
}
}
}
/******************************************************************************
* 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;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,72 +1,72 @@
/******************************************************************************
* 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, mixRotate, mixX, mixY;
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; } }
/// <summary> A percentage (0-1) that controls the mix between the constrained and unconstrained rotation.</summary>
public float RotateMix { get { return mixRotate; } set { mixRotate = value; } }
/// <summary> A percentage (0-1) that controls the mix between the constrained and unconstrained translation X.</summary>
public float MixX { get { return mixX; } set { mixX = value; } }
/// <summary> A percentage (0-1) that controls the mix between the constrained and unconstrained translation Y.</summary>
public float MixY { get { return mixY; } set { mixY = value; } }
}
public enum PositionMode {
Fixed, Percent
}
public enum SpacingMode {
Length, Fixed, Percent, Proportional
}
public enum RotateMode {
Tangent, Chain, ChainScale
}
}
/******************************************************************************
* 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, mixRotate, mixX, mixY;
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; } }
/// <summary> A percentage (0-1) that controls the mix between the constrained and unconstrained rotation.</summary>
public float RotateMix { get { return mixRotate; } set { mixRotate = value; } }
/// <summary> A percentage (0-1) that controls the mix between the constrained and unconstrained translation X.</summary>
public float MixX { get { return mixX; } set { mixX = value; } }
/// <summary> A percentage (0-1) that controls the mix between the constrained and unconstrained translation Y.</summary>
public float MixY { get { return mixY; } set { mixY = value; } }
}
public enum PositionMode {
Fixed, Percent
}
public enum SpacingMode {
Length, Fixed, Percent, Proportional
}
public enum RotateMode {
Tangent, Chain, ChainScale
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,233 +1,233 @@
/******************************************************************************
* 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>
/// Collects each BoundingBoxAttachment that is visible and computes the world vertices for its polygon.
/// The polygon vertices are provided along with convenience methods for doing hit detection.
/// </summary>
public class SkeletonBounds {
private ExposedList<Polygon> polygonPool = new ExposedList<Polygon>();
private float minX, minY, maxX, maxY;
public ExposedList<BoundingBoxAttachment> BoundingBoxes { get; private set; }
public ExposedList<Polygon> Polygons { get; private set; }
public float MinX { get { return minX; } set { minX = value; } }
public float MinY { get { return minY; } set { minY = value; } }
public float MaxX { get { return maxX; } set { maxX = value; } }
public float MaxY { get { return maxY; } set { maxY = value; } }
public float Width { get { return maxX - minX; } }
public float Height { get { return maxY - minY; } }
public SkeletonBounds () {
BoundingBoxes = new ExposedList<BoundingBoxAttachment>();
Polygons = new ExposedList<Polygon>();
}
/// <summary>
/// Clears any previous polygons, finds all visible bounding box attachments,
/// and computes the world vertices for each bounding box's polygon.</summary>
/// <param name="skeleton">The skeleton.</param>
/// <param name="updateAabb">
/// If true, the axis aligned bounding box containing all the polygons is computed.
/// If false, the SkeletonBounds AABB methods will always return true.
/// </param>
public void Update (Skeleton skeleton, bool updateAabb) {
ExposedList<BoundingBoxAttachment> boundingBoxes = BoundingBoxes;
ExposedList<Polygon> polygons = Polygons;
Slot[] slots = skeleton.slots.Items;
int slotCount = skeleton.slots.Count;
boundingBoxes.Clear();
for (int i = 0, n = polygons.Count; i < n; i++)
polygonPool.Add(polygons.Items[i]);
polygons.Clear();
for (int i = 0; i < slotCount; i++) {
Slot slot = slots[i];
if (!slot.bone.active) continue;
BoundingBoxAttachment boundingBox = slot.attachment as BoundingBoxAttachment;
if (boundingBox == null) continue;
boundingBoxes.Add(boundingBox);
Polygon polygon = null;
int poolCount = polygonPool.Count;
if (poolCount > 0) {
polygon = polygonPool.Items[poolCount - 1];
polygonPool.RemoveAt(poolCount - 1);
} else
polygon = new Polygon();
polygons.Add(polygon);
int count = boundingBox.worldVerticesLength;
polygon.Count = count;
if (polygon.Vertices.Length < count) polygon.Vertices = new float[count];
boundingBox.ComputeWorldVertices(slot, polygon.Vertices);
}
if (updateAabb) {
AabbCompute();
} else {
minX = int.MinValue;
minY = int.MinValue;
maxX = int.MaxValue;
maxY = int.MaxValue;
}
}
private void AabbCompute () {
float minX = int.MaxValue, minY = int.MaxValue, maxX = int.MinValue, maxY = int.MinValue;
Polygon[] polygons = Polygons.Items;
for (int i = 0, n = Polygons.Count; i < n; i++) {
Polygon polygon = polygons[i];
float[] vertices = polygon.Vertices;
for (int ii = 0, nn = polygon.Count; ii < nn; ii += 2) {
float x = vertices[ii];
float y = vertices[ii + 1];
minX = Math.Min(minX, x);
minY = Math.Min(minY, y);
maxX = Math.Max(maxX, x);
maxY = Math.Max(maxY, y);
}
}
this.minX = minX;
this.minY = minY;
this.maxX = maxX;
this.maxY = maxY;
}
/// <summary>Returns true if the axis aligned bounding box contains the point.</summary>
public bool AabbContainsPoint (float x, float y) {
return x >= minX && x <= maxX && y >= minY && y <= maxY;
}
/// <summary>Returns true if the axis aligned bounding box intersects the line segment.</summary>
public bool AabbIntersectsSegment (float x1, float y1, float x2, float y2) {
float minX = this.minX;
float minY = this.minY;
float maxX = this.maxX;
float maxY = this.maxY;
if ((x1 <= minX && x2 <= minX) || (y1 <= minY && y2 <= minY) || (x1 >= maxX && x2 >= maxX) || (y1 >= maxY && y2 >= maxY))
return false;
float m = (y2 - y1) / (x2 - x1);
float y = m * (minX - x1) + y1;
if (y > minY && y < maxY) return true;
y = m * (maxX - x1) + y1;
if (y > minY && y < maxY) return true;
float x = (minY - y1) / m + x1;
if (x > minX && x < maxX) return true;
x = (maxY - y1) / m + x1;
if (x > minX && x < maxX) return true;
return false;
}
/// <summary>Returns true if the axis aligned bounding box intersects the axis aligned bounding box of the specified bounds.</summary>
public bool AabbIntersectsSkeleton (SkeletonBounds bounds) {
return minX < bounds.maxX && maxX > bounds.minX && minY < bounds.maxY && maxY > bounds.minY;
}
/// <summary>Returns true if the polygon contains the point.</summary>
public bool ContainsPoint (Polygon polygon, float x, float y) {
float[] vertices = polygon.Vertices;
int nn = polygon.Count;
int prevIndex = nn - 2;
bool inside = false;
for (int ii = 0; ii < nn; ii += 2) {
float vertexY = vertices[ii + 1];
float prevY = vertices[prevIndex + 1];
if ((vertexY < y && prevY >= y) || (prevY < y && vertexY >= y)) {
float vertexX = vertices[ii];
if (vertexX + (y - vertexY) / (prevY - vertexY) * (vertices[prevIndex] - vertexX) < x) inside = !inside;
}
prevIndex = ii;
}
return inside;
}
/// <summary>Returns the first bounding box attachment that contains the point, or null. When doing many checks, it is usually more
/// efficient to only call this method if <see cref="AabbContainsPoint(float, float)"/> returns true.</summary>
public BoundingBoxAttachment ContainsPoint (float x, float y) {
Polygon[] polygons = Polygons.Items;
for (int i = 0, n = Polygons.Count; i < n; i++)
if (ContainsPoint(polygons[i], x, y)) return BoundingBoxes.Items[i];
return null;
}
/// <summary>Returns the first bounding box attachment that contains the line segment, or null. When doing many checks, it is usually
/// more efficient to only call this method if <see cref="aabbIntersectsSegment(float, float, float, float)"/> returns true.</summary>
public BoundingBoxAttachment IntersectsSegment (float x1, float y1, float x2, float y2) {
Polygon[] polygons = Polygons.Items;
for (int i = 0, n = Polygons.Count; i < n; i++)
if (IntersectsSegment(polygons[i], x1, y1, x2, y2)) return BoundingBoxes.Items[i];
return null;
}
/// <summary>Returns true if the polygon contains the line segment.</summary>
public bool IntersectsSegment (Polygon polygon, float x1, float y1, float x2, float y2) {
float[] vertices = polygon.Vertices;
int nn = polygon.Count;
float width12 = x1 - x2, height12 = y1 - y2;
float det1 = x1 * y2 - y1 * x2;
float x3 = vertices[nn - 2], y3 = vertices[nn - 1];
for (int ii = 0; ii < nn; ii += 2) {
float x4 = vertices[ii], y4 = vertices[ii + 1];
float det2 = x3 * y4 - y3 * x4;
float width34 = x3 - x4, height34 = y3 - y4;
float det3 = width12 * height34 - height12 * width34;
float x = (det1 * width34 - width12 * det2) / det3;
if (((x >= x3 && x <= x4) || (x >= x4 && x <= x3)) && ((x >= x1 && x <= x2) || (x >= x2 && x <= x1))) {
float y = (det1 * height34 - height12 * det2) / det3;
if (((y >= y3 && y <= y4) || (y >= y4 && y <= y3)) && ((y >= y1 && y <= y2) || (y >= y2 && y <= y1))) return true;
}
x3 = x4;
y3 = y4;
}
return false;
}
public Polygon GetPolygon (BoundingBoxAttachment attachment) {
int index = BoundingBoxes.IndexOf(attachment);
return index == -1 ? null : Polygons.Items[index];
}
}
public class Polygon {
public float[] Vertices { get; set; }
public int Count { get; set; }
public Polygon () {
Vertices = new float[16];
}
}
}
/******************************************************************************
* 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>
/// Collects each BoundingBoxAttachment that is visible and computes the world vertices for its polygon.
/// The polygon vertices are provided along with convenience methods for doing hit detection.
/// </summary>
public class SkeletonBounds {
private ExposedList<Polygon> polygonPool = new ExposedList<Polygon>();
private float minX, minY, maxX, maxY;
public ExposedList<BoundingBoxAttachment> BoundingBoxes { get; private set; }
public ExposedList<Polygon> Polygons { get; private set; }
public float MinX { get { return minX; } set { minX = value; } }
public float MinY { get { return minY; } set { minY = value; } }
public float MaxX { get { return maxX; } set { maxX = value; } }
public float MaxY { get { return maxY; } set { maxY = value; } }
public float Width { get { return maxX - minX; } }
public float Height { get { return maxY - minY; } }
public SkeletonBounds () {
BoundingBoxes = new ExposedList<BoundingBoxAttachment>();
Polygons = new ExposedList<Polygon>();
}
/// <summary>
/// Clears any previous polygons, finds all visible bounding box attachments,
/// and computes the world vertices for each bounding box's polygon.</summary>
/// <param name="skeleton">The skeleton.</param>
/// <param name="updateAabb">
/// If true, the axis aligned bounding box containing all the polygons is computed.
/// If false, the SkeletonBounds AABB methods will always return true.
/// </param>
public void Update (Skeleton skeleton, bool updateAabb) {
ExposedList<BoundingBoxAttachment> boundingBoxes = BoundingBoxes;
ExposedList<Polygon> polygons = Polygons;
Slot[] slots = skeleton.slots.Items;
int slotCount = skeleton.slots.Count;
boundingBoxes.Clear();
for (int i = 0, n = polygons.Count; i < n; i++)
polygonPool.Add(polygons.Items[i]);
polygons.Clear();
for (int i = 0; i < slotCount; i++) {
Slot slot = slots[i];
if (!slot.bone.active) continue;
BoundingBoxAttachment boundingBox = slot.attachment as BoundingBoxAttachment;
if (boundingBox == null) continue;
boundingBoxes.Add(boundingBox);
Polygon polygon = null;
int poolCount = polygonPool.Count;
if (poolCount > 0) {
polygon = polygonPool.Items[poolCount - 1];
polygonPool.RemoveAt(poolCount - 1);
} else
polygon = new Polygon();
polygons.Add(polygon);
int count = boundingBox.worldVerticesLength;
polygon.Count = count;
if (polygon.Vertices.Length < count) polygon.Vertices = new float[count];
boundingBox.ComputeWorldVertices(slot, polygon.Vertices);
}
if (updateAabb) {
AabbCompute();
} else {
minX = int.MinValue;
minY = int.MinValue;
maxX = int.MaxValue;
maxY = int.MaxValue;
}
}
private void AabbCompute () {
float minX = int.MaxValue, minY = int.MaxValue, maxX = int.MinValue, maxY = int.MinValue;
Polygon[] polygons = Polygons.Items;
for (int i = 0, n = Polygons.Count; i < n; i++) {
Polygon polygon = polygons[i];
float[] vertices = polygon.Vertices;
for (int ii = 0, nn = polygon.Count; ii < nn; ii += 2) {
float x = vertices[ii];
float y = vertices[ii + 1];
minX = Math.Min(minX, x);
minY = Math.Min(minY, y);
maxX = Math.Max(maxX, x);
maxY = Math.Max(maxY, y);
}
}
this.minX = minX;
this.minY = minY;
this.maxX = maxX;
this.maxY = maxY;
}
/// <summary>Returns true if the axis aligned bounding box contains the point.</summary>
public bool AabbContainsPoint (float x, float y) {
return x >= minX && x <= maxX && y >= minY && y <= maxY;
}
/// <summary>Returns true if the axis aligned bounding box intersects the line segment.</summary>
public bool AabbIntersectsSegment (float x1, float y1, float x2, float y2) {
float minX = this.minX;
float minY = this.minY;
float maxX = this.maxX;
float maxY = this.maxY;
if ((x1 <= minX && x2 <= minX) || (y1 <= minY && y2 <= minY) || (x1 >= maxX && x2 >= maxX) || (y1 >= maxY && y2 >= maxY))
return false;
float m = (y2 - y1) / (x2 - x1);
float y = m * (minX - x1) + y1;
if (y > minY && y < maxY) return true;
y = m * (maxX - x1) + y1;
if (y > minY && y < maxY) return true;
float x = (minY - y1) / m + x1;
if (x > minX && x < maxX) return true;
x = (maxY - y1) / m + x1;
if (x > minX && x < maxX) return true;
return false;
}
/// <summary>Returns true if the axis aligned bounding box intersects the axis aligned bounding box of the specified bounds.</summary>
public bool AabbIntersectsSkeleton (SkeletonBounds bounds) {
return minX < bounds.maxX && maxX > bounds.minX && minY < bounds.maxY && maxY > bounds.minY;
}
/// <summary>Returns true if the polygon contains the point.</summary>
public bool ContainsPoint (Polygon polygon, float x, float y) {
float[] vertices = polygon.Vertices;
int nn = polygon.Count;
int prevIndex = nn - 2;
bool inside = false;
for (int ii = 0; ii < nn; ii += 2) {
float vertexY = vertices[ii + 1];
float prevY = vertices[prevIndex + 1];
if ((vertexY < y && prevY >= y) || (prevY < y && vertexY >= y)) {
float vertexX = vertices[ii];
if (vertexX + (y - vertexY) / (prevY - vertexY) * (vertices[prevIndex] - vertexX) < x) inside = !inside;
}
prevIndex = ii;
}
return inside;
}
/// <summary>Returns the first bounding box attachment that contains the point, or null. When doing many checks, it is usually more
/// efficient to only call this method if <see cref="AabbContainsPoint(float, float)"/> returns true.</summary>
public BoundingBoxAttachment ContainsPoint (float x, float y) {
Polygon[] polygons = Polygons.Items;
for (int i = 0, n = Polygons.Count; i < n; i++)
if (ContainsPoint(polygons[i], x, y)) return BoundingBoxes.Items[i];
return null;
}
/// <summary>Returns the first bounding box attachment that contains the line segment, or null. When doing many checks, it is usually
/// more efficient to only call this method if <see cref="aabbIntersectsSegment(float, float, float, float)"/> returns true.</summary>
public BoundingBoxAttachment IntersectsSegment (float x1, float y1, float x2, float y2) {
Polygon[] polygons = Polygons.Items;
for (int i = 0, n = Polygons.Count; i < n; i++)
if (IntersectsSegment(polygons[i], x1, y1, x2, y2)) return BoundingBoxes.Items[i];
return null;
}
/// <summary>Returns true if the polygon contains the line segment.</summary>
public bool IntersectsSegment (Polygon polygon, float x1, float y1, float x2, float y2) {
float[] vertices = polygon.Vertices;
int nn = polygon.Count;
float width12 = x1 - x2, height12 = y1 - y2;
float det1 = x1 * y2 - y1 * x2;
float x3 = vertices[nn - 2], y3 = vertices[nn - 1];
for (int ii = 0; ii < nn; ii += 2) {
float x4 = vertices[ii], y4 = vertices[ii + 1];
float det2 = x3 * y4 - y3 * x4;
float width34 = x3 - x4, height34 = y3 - y4;
float det3 = width12 * height34 - height12 * width34;
float x = (det1 * width34 - width12 * det2) / det3;
if (((x >= x3 && x <= x4) || (x >= x4 && x <= x3)) && ((x >= x1 && x <= x2) || (x >= x2 && x <= x1))) {
float y = (det1 * height34 - height12 * det2) / det3;
if (((y >= y3 && y <= y4) || (y >= y4 && y <= y3)) && ((y >= y1 && y <= y2) || (y >= y2 && y <= y1))) return true;
}
x3 = x4;
y3 = y4;
}
return false;
}
public Polygon GetPolygon (BoundingBoxAttachment attachment) {
int index = BoundingBoxes.IndexOf(attachment);
return index == -1 ? null : Polygons.Items[index];
}
}
public class Polygon {
public float[] Vertices { get; set; }
public int Count { get; set; }
public Polygon () {
Vertices = new float[16];
}
}
}

View File

@@ -1,292 +1,292 @@
/******************************************************************************
* 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 SkeletonClipping {
internal readonly Triangulator triangulator = new Triangulator();
internal readonly ExposedList<float> clippingPolygon = new ExposedList<float>();
internal readonly ExposedList<float> clipOutput = new ExposedList<float>(128);
internal readonly ExposedList<float> clippedVertices = new ExposedList<float>(128);
internal readonly ExposedList<int> clippedTriangles = new ExposedList<int>(128);
internal readonly ExposedList<float> clippedUVs = new ExposedList<float>(128);
internal readonly ExposedList<float> scratch = new ExposedList<float>();
internal ClippingAttachment clipAttachment;
internal ExposedList<ExposedList<float>> clippingPolygons;
public ExposedList<float> ClippedVertices { get { return clippedVertices; } }
public ExposedList<int> ClippedTriangles { get { return clippedTriangles; } }
public ExposedList<float> ClippedUVs { get { return clippedUVs; } }
public bool IsClipping { get { return clipAttachment != null; } }
public int ClipStart (Slot slot, ClippingAttachment clip) {
if (clipAttachment != null) return 0;
clipAttachment = clip;
int n = clip.worldVerticesLength;
float[] vertices = clippingPolygon.Resize(n).Items;
clip.ComputeWorldVertices(slot, 0, n, vertices, 0, 2);
MakeClockwise(clippingPolygon);
clippingPolygons = triangulator.Decompose(clippingPolygon, triangulator.Triangulate(clippingPolygon));
foreach (var polygon in clippingPolygons) {
MakeClockwise(polygon);
polygon.Add(polygon.Items[0]);
polygon.Add(polygon.Items[1]);
}
return clippingPolygons.Count;
}
public void ClipEnd (Slot slot) {
if (clipAttachment != null && clipAttachment.endSlot == slot.data) ClipEnd();
}
public void ClipEnd () {
if (clipAttachment == null) return;
clipAttachment = null;
clippingPolygons = null;
clippedVertices.Clear();
clippedTriangles.Clear();
clippingPolygon.Clear();
}
public void ClipTriangles (float[] vertices, int verticesLength, int[] triangles, int trianglesLength, float[] uvs) {
ExposedList<float> clipOutput = this.clipOutput, clippedVertices = this.clippedVertices;
var clippedTriangles = this.clippedTriangles;
var polygons = clippingPolygons.Items;
int polygonsCount = clippingPolygons.Count;
int index = 0;
clippedVertices.Clear();
clippedUVs.Clear();
clippedTriangles.Clear();
//outer:
for (int i = 0; i < trianglesLength; i += 3) {
int vertexOffset = triangles[i] << 1;
float x1 = vertices[vertexOffset], y1 = vertices[vertexOffset + 1];
float u1 = uvs[vertexOffset], v1 = uvs[vertexOffset + 1];
vertexOffset = triangles[i + 1] << 1;
float x2 = vertices[vertexOffset], y2 = vertices[vertexOffset + 1];
float u2 = uvs[vertexOffset], v2 = uvs[vertexOffset + 1];
vertexOffset = triangles[i + 2] << 1;
float x3 = vertices[vertexOffset], y3 = vertices[vertexOffset + 1];
float u3 = uvs[vertexOffset], v3 = uvs[vertexOffset + 1];
for (int p = 0; p < polygonsCount; p++) {
int s = clippedVertices.Count;
if (Clip(x1, y1, x2, y2, x3, y3, polygons[p], clipOutput)) {
int clipOutputLength = clipOutput.Count;
if (clipOutputLength == 0) continue;
float d0 = y2 - y3, d1 = x3 - x2, d2 = x1 - x3, d4 = y3 - y1;
float d = 1 / (d0 * d2 + d1 * (y1 - y3));
int clipOutputCount = clipOutputLength >> 1;
float[] clipOutputItems = clipOutput.Items;
float[] clippedVerticesItems = clippedVertices.Resize(s + clipOutputCount * 2).Items;
float[] clippedUVsItems = clippedUVs.Resize(s + clipOutputCount * 2).Items;
for (int ii = 0; ii < clipOutputLength; ii += 2) {
float x = clipOutputItems[ii], y = clipOutputItems[ii + 1];
clippedVerticesItems[s] = x;
clippedVerticesItems[s + 1] = y;
float c0 = x - x3, c1 = y - y3;
float a = (d0 * c0 + d1 * c1) * d;
float b = (d4 * c0 + d2 * c1) * d;
float c = 1 - a - b;
clippedUVsItems[s] = u1 * a + u2 * b + u3 * c;
clippedUVsItems[s + 1] = v1 * a + v2 * b + v3 * c;
s += 2;
}
s = clippedTriangles.Count;
int[] clippedTrianglesItems = clippedTriangles.Resize(s + 3 * (clipOutputCount - 2)).Items;
clipOutputCount--;
for (int ii = 1; ii < clipOutputCount; ii++) {
clippedTrianglesItems[s] = index;
clippedTrianglesItems[s + 1] = index + ii;
clippedTrianglesItems[s + 2] = index + ii + 1;
s += 3;
}
index += clipOutputCount + 1;
} else {
float[] clippedVerticesItems = clippedVertices.Resize(s + 3 * 2).Items;
float[] clippedUVsItems = clippedUVs.Resize(s + 3 * 2).Items;
clippedVerticesItems[s] = x1;
clippedVerticesItems[s + 1] = y1;
clippedVerticesItems[s + 2] = x2;
clippedVerticesItems[s + 3] = y2;
clippedVerticesItems[s + 4] = x3;
clippedVerticesItems[s + 5] = y3;
clippedUVsItems[s] = u1;
clippedUVsItems[s + 1] = v1;
clippedUVsItems[s + 2] = u2;
clippedUVsItems[s + 3] = v2;
clippedUVsItems[s + 4] = u3;
clippedUVsItems[s + 5] = v3;
s = clippedTriangles.Count;
int[] clippedTrianglesItems = clippedTriangles.Resize(s + 3).Items;
clippedTrianglesItems[s] = index;
clippedTrianglesItems[s + 1] = index + 1;
clippedTrianglesItems[s + 2] = index + 2;
index += 3;
break; //continue outer;
}
}
}
}
/** Clips the input triangle against the convex, clockwise clipping area. If the triangle lies entirely within the clipping
* area, false is returned. The clipping area must duplicate the first vertex at the end of the vertices list. */
internal bool Clip (float x1, float y1, float x2, float y2, float x3, float y3, ExposedList<float> clippingArea, ExposedList<float> output) {
var originalOutput = output;
var clipped = false;
// Avoid copy at the end.
ExposedList<float> input = null;
if (clippingArea.Count % 4 >= 2) {
input = output;
output = scratch;
} else {
input = scratch;
}
input.Clear();
input.Add(x1);
input.Add(y1);
input.Add(x2);
input.Add(y2);
input.Add(x3);
input.Add(y3);
input.Add(x1);
input.Add(y1);
output.Clear();
float[] clippingVertices = clippingArea.Items;
int clippingVerticesLast = clippingArea.Count - 4;
for (int i = 0; ; i += 2) {
float edgeX = clippingVertices[i], edgeY = clippingVertices[i + 1];
float edgeX2 = clippingVertices[i + 2], edgeY2 = clippingVertices[i + 3];
float deltaX = edgeX - edgeX2, deltaY = edgeY - edgeY2;
float[] inputVertices = input.Items;
int inputVerticesLength = input.Count - 2, outputStart = output.Count;
for (int ii = 0; ii < inputVerticesLength; ii += 2) {
float inputX = inputVertices[ii], inputY = inputVertices[ii + 1];
float inputX2 = inputVertices[ii + 2], inputY2 = inputVertices[ii + 3];
bool side2 = deltaX * (inputY2 - edgeY2) - deltaY * (inputX2 - edgeX2) > 0;
if (deltaX * (inputY - edgeY2) - deltaY * (inputX - edgeX2) > 0) {
if (side2) { // v1 inside, v2 inside
output.Add(inputX2);
output.Add(inputY2);
continue;
}
// v1 inside, v2 outside
float c0 = inputY2 - inputY, c2 = inputX2 - inputX;
float s = c0 * (edgeX2 - edgeX) - c2 * (edgeY2 - edgeY);
if (Math.Abs(s) > 0.000001f) {
float ua = (c2 * (edgeY - inputY) - c0 * (edgeX - inputX)) / s;
output.Add(edgeX + (edgeX2 - edgeX) * ua);
output.Add(edgeY + (edgeY2 - edgeY) * ua);
} else {
output.Add(edgeX);
output.Add(edgeY);
}
} else if (side2) { // v1 outside, v2 inside
float c0 = inputY2 - inputY, c2 = inputX2 - inputX;
float s = c0 * (edgeX2 - edgeX) - c2 * (edgeY2 - edgeY);
if (Math.Abs(s) > 0.000001f) {
float ua = (c2 * (edgeY - inputY) - c0 * (edgeX - inputX)) / s;
output.Add(edgeX + (edgeX2 - edgeX) * ua);
output.Add(edgeY + (edgeY2 - edgeY) * ua);
} else {
output.Add(edgeX);
output.Add(edgeY);
}
output.Add(inputX2);
output.Add(inputY2);
}
clipped = true;
}
if (outputStart == output.Count) { // All edges outside.
originalOutput.Clear();
return true;
}
output.Add(output.Items[0]);
output.Add(output.Items[1]);
if (i == clippingVerticesLast) break;
var temp = output;
output = input;
output.Clear();
input = temp;
}
if (originalOutput != output) {
originalOutput.Clear();
for (int i = 0, n = output.Count - 2; i < n; i++)
originalOutput.Add(output.Items[i]);
} else
originalOutput.Resize(originalOutput.Count - 2);
return clipped;
}
public static void MakeClockwise (ExposedList<float> polygon) {
float[] vertices = polygon.Items;
int verticeslength = polygon.Count;
float area = vertices[verticeslength - 2] * vertices[1] - vertices[0] * vertices[verticeslength - 1], p1x, p1y, p2x, p2y;
for (int i = 0, n = verticeslength - 3; i < n; i += 2) {
p1x = vertices[i];
p1y = vertices[i + 1];
p2x = vertices[i + 2];
p2y = vertices[i + 3];
area += p1x * p2y - p2x * p1y;
}
if (area < 0) return;
for (int i = 0, lastX = verticeslength - 2, n = verticeslength >> 1; i < n; i += 2) {
float x = vertices[i], y = vertices[i + 1];
int other = lastX - i;
vertices[i] = vertices[other];
vertices[i + 1] = vertices[other + 1];
vertices[other] = x;
vertices[other + 1] = y;
}
}
}
}
/******************************************************************************
* 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 SkeletonClipping {
internal readonly Triangulator triangulator = new Triangulator();
internal readonly ExposedList<float> clippingPolygon = new ExposedList<float>();
internal readonly ExposedList<float> clipOutput = new ExposedList<float>(128);
internal readonly ExposedList<float> clippedVertices = new ExposedList<float>(128);
internal readonly ExposedList<int> clippedTriangles = new ExposedList<int>(128);
internal readonly ExposedList<float> clippedUVs = new ExposedList<float>(128);
internal readonly ExposedList<float> scratch = new ExposedList<float>();
internal ClippingAttachment clipAttachment;
internal ExposedList<ExposedList<float>> clippingPolygons;
public ExposedList<float> ClippedVertices { get { return clippedVertices; } }
public ExposedList<int> ClippedTriangles { get { return clippedTriangles; } }
public ExposedList<float> ClippedUVs { get { return clippedUVs; } }
public bool IsClipping { get { return clipAttachment != null; } }
public int ClipStart (Slot slot, ClippingAttachment clip) {
if (clipAttachment != null) return 0;
clipAttachment = clip;
int n = clip.worldVerticesLength;
float[] vertices = clippingPolygon.Resize(n).Items;
clip.ComputeWorldVertices(slot, 0, n, vertices, 0, 2);
MakeClockwise(clippingPolygon);
clippingPolygons = triangulator.Decompose(clippingPolygon, triangulator.Triangulate(clippingPolygon));
foreach (var polygon in clippingPolygons) {
MakeClockwise(polygon);
polygon.Add(polygon.Items[0]);
polygon.Add(polygon.Items[1]);
}
return clippingPolygons.Count;
}
public void ClipEnd (Slot slot) {
if (clipAttachment != null && clipAttachment.endSlot == slot.data) ClipEnd();
}
public void ClipEnd () {
if (clipAttachment == null) return;
clipAttachment = null;
clippingPolygons = null;
clippedVertices.Clear();
clippedTriangles.Clear();
clippingPolygon.Clear();
}
public void ClipTriangles (float[] vertices, int verticesLength, int[] triangles, int trianglesLength, float[] uvs) {
ExposedList<float> clipOutput = this.clipOutput, clippedVertices = this.clippedVertices;
var clippedTriangles = this.clippedTriangles;
var polygons = clippingPolygons.Items;
int polygonsCount = clippingPolygons.Count;
int index = 0;
clippedVertices.Clear();
clippedUVs.Clear();
clippedTriangles.Clear();
//outer:
for (int i = 0; i < trianglesLength; i += 3) {
int vertexOffset = triangles[i] << 1;
float x1 = vertices[vertexOffset], y1 = vertices[vertexOffset + 1];
float u1 = uvs[vertexOffset], v1 = uvs[vertexOffset + 1];
vertexOffset = triangles[i + 1] << 1;
float x2 = vertices[vertexOffset], y2 = vertices[vertexOffset + 1];
float u2 = uvs[vertexOffset], v2 = uvs[vertexOffset + 1];
vertexOffset = triangles[i + 2] << 1;
float x3 = vertices[vertexOffset], y3 = vertices[vertexOffset + 1];
float u3 = uvs[vertexOffset], v3 = uvs[vertexOffset + 1];
for (int p = 0; p < polygonsCount; p++) {
int s = clippedVertices.Count;
if (Clip(x1, y1, x2, y2, x3, y3, polygons[p], clipOutput)) {
int clipOutputLength = clipOutput.Count;
if (clipOutputLength == 0) continue;
float d0 = y2 - y3, d1 = x3 - x2, d2 = x1 - x3, d4 = y3 - y1;
float d = 1 / (d0 * d2 + d1 * (y1 - y3));
int clipOutputCount = clipOutputLength >> 1;
float[] clipOutputItems = clipOutput.Items;
float[] clippedVerticesItems = clippedVertices.Resize(s + clipOutputCount * 2).Items;
float[] clippedUVsItems = clippedUVs.Resize(s + clipOutputCount * 2).Items;
for (int ii = 0; ii < clipOutputLength; ii += 2) {
float x = clipOutputItems[ii], y = clipOutputItems[ii + 1];
clippedVerticesItems[s] = x;
clippedVerticesItems[s + 1] = y;
float c0 = x - x3, c1 = y - y3;
float a = (d0 * c0 + d1 * c1) * d;
float b = (d4 * c0 + d2 * c1) * d;
float c = 1 - a - b;
clippedUVsItems[s] = u1 * a + u2 * b + u3 * c;
clippedUVsItems[s + 1] = v1 * a + v2 * b + v3 * c;
s += 2;
}
s = clippedTriangles.Count;
int[] clippedTrianglesItems = clippedTriangles.Resize(s + 3 * (clipOutputCount - 2)).Items;
clipOutputCount--;
for (int ii = 1; ii < clipOutputCount; ii++) {
clippedTrianglesItems[s] = index;
clippedTrianglesItems[s + 1] = index + ii;
clippedTrianglesItems[s + 2] = index + ii + 1;
s += 3;
}
index += clipOutputCount + 1;
} else {
float[] clippedVerticesItems = clippedVertices.Resize(s + 3 * 2).Items;
float[] clippedUVsItems = clippedUVs.Resize(s + 3 * 2).Items;
clippedVerticesItems[s] = x1;
clippedVerticesItems[s + 1] = y1;
clippedVerticesItems[s + 2] = x2;
clippedVerticesItems[s + 3] = y2;
clippedVerticesItems[s + 4] = x3;
clippedVerticesItems[s + 5] = y3;
clippedUVsItems[s] = u1;
clippedUVsItems[s + 1] = v1;
clippedUVsItems[s + 2] = u2;
clippedUVsItems[s + 3] = v2;
clippedUVsItems[s + 4] = u3;
clippedUVsItems[s + 5] = v3;
s = clippedTriangles.Count;
int[] clippedTrianglesItems = clippedTriangles.Resize(s + 3).Items;
clippedTrianglesItems[s] = index;
clippedTrianglesItems[s + 1] = index + 1;
clippedTrianglesItems[s + 2] = index + 2;
index += 3;
break; //continue outer;
}
}
}
}
/** Clips the input triangle against the convex, clockwise clipping area. If the triangle lies entirely within the clipping
* area, false is returned. The clipping area must duplicate the first vertex at the end of the vertices list. */
internal bool Clip (float x1, float y1, float x2, float y2, float x3, float y3, ExposedList<float> clippingArea, ExposedList<float> output) {
var originalOutput = output;
var clipped = false;
// Avoid copy at the end.
ExposedList<float> input = null;
if (clippingArea.Count % 4 >= 2) {
input = output;
output = scratch;
} else {
input = scratch;
}
input.Clear();
input.Add(x1);
input.Add(y1);
input.Add(x2);
input.Add(y2);
input.Add(x3);
input.Add(y3);
input.Add(x1);
input.Add(y1);
output.Clear();
float[] clippingVertices = clippingArea.Items;
int clippingVerticesLast = clippingArea.Count - 4;
for (int i = 0; ; i += 2) {
float edgeX = clippingVertices[i], edgeY = clippingVertices[i + 1];
float edgeX2 = clippingVertices[i + 2], edgeY2 = clippingVertices[i + 3];
float deltaX = edgeX - edgeX2, deltaY = edgeY - edgeY2;
float[] inputVertices = input.Items;
int inputVerticesLength = input.Count - 2, outputStart = output.Count;
for (int ii = 0; ii < inputVerticesLength; ii += 2) {
float inputX = inputVertices[ii], inputY = inputVertices[ii + 1];
float inputX2 = inputVertices[ii + 2], inputY2 = inputVertices[ii + 3];
bool side2 = deltaX * (inputY2 - edgeY2) - deltaY * (inputX2 - edgeX2) > 0;
if (deltaX * (inputY - edgeY2) - deltaY * (inputX - edgeX2) > 0) {
if (side2) { // v1 inside, v2 inside
output.Add(inputX2);
output.Add(inputY2);
continue;
}
// v1 inside, v2 outside
float c0 = inputY2 - inputY, c2 = inputX2 - inputX;
float s = c0 * (edgeX2 - edgeX) - c2 * (edgeY2 - edgeY);
if (Math.Abs(s) > 0.000001f) {
float ua = (c2 * (edgeY - inputY) - c0 * (edgeX - inputX)) / s;
output.Add(edgeX + (edgeX2 - edgeX) * ua);
output.Add(edgeY + (edgeY2 - edgeY) * ua);
} else {
output.Add(edgeX);
output.Add(edgeY);
}
} else if (side2) { // v1 outside, v2 inside
float c0 = inputY2 - inputY, c2 = inputX2 - inputX;
float s = c0 * (edgeX2 - edgeX) - c2 * (edgeY2 - edgeY);
if (Math.Abs(s) > 0.000001f) {
float ua = (c2 * (edgeY - inputY) - c0 * (edgeX - inputX)) / s;
output.Add(edgeX + (edgeX2 - edgeX) * ua);
output.Add(edgeY + (edgeY2 - edgeY) * ua);
} else {
output.Add(edgeX);
output.Add(edgeY);
}
output.Add(inputX2);
output.Add(inputY2);
}
clipped = true;
}
if (outputStart == output.Count) { // All edges outside.
originalOutput.Clear();
return true;
}
output.Add(output.Items[0]);
output.Add(output.Items[1]);
if (i == clippingVerticesLast) break;
var temp = output;
output = input;
output.Clear();
input = temp;
}
if (originalOutput != output) {
originalOutput.Clear();
for (int i = 0, n = output.Count - 2; i < n; i++)
originalOutput.Add(output.Items[i]);
} else
originalOutput.Resize(originalOutput.Count - 2);
return clipped;
}
public static void MakeClockwise (ExposedList<float> polygon) {
float[] vertices = polygon.Items;
int verticeslength = polygon.Count;
float area = vertices[verticeslength - 2] * vertices[1] - vertices[0] * vertices[verticeslength - 1], p1x, p1y, p2x, p2y;
for (int i = 0, n = verticeslength - 3; i < n; i += 2) {
p1x = vertices[i];
p1y = vertices[i + 1];
p2x = vertices[i + 2];
p2y = vertices[i + 3];
area += p1x * p2y - p2x * p1y;
}
if (area < 0) return;
for (int i = 0, lastX = verticeslength - 2, n = verticeslength >> 1; i < n; i += 2) {
float x = vertices[i], y = vertices[i + 1];
int other = lastX - i;
vertices[i] = vertices[other];
vertices[i + 1] = vertices[other + 1];
vertices[other] = x;
vertices[other + 1] = y;
}
}
}
}

View File

@@ -1,205 +1,205 @@
/******************************************************************************
* 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>Stores the setup pose and all of the stateless data for a skeleton.</summary>
public class SkeletonData {
internal string name;
internal ExposedList<BoneData> bones = new ExposedList<BoneData>(); // Ordered parents first
internal ExposedList<SlotData> slots = new ExposedList<SlotData>(); // Setup pose draw order.
internal ExposedList<Skin> skins = new ExposedList<Skin>();
internal Skin defaultSkin;
internal ExposedList<EventData> events = new ExposedList<EventData>();
internal ExposedList<Animation> animations = new ExposedList<Animation>();
internal ExposedList<IkConstraintData> ikConstraints = new ExposedList<IkConstraintData>();
internal ExposedList<TransformConstraintData> transformConstraints = new ExposedList<TransformConstraintData>();
internal ExposedList<PathConstraintData> pathConstraints = new ExposedList<PathConstraintData>();
internal float x, y, width, height;
internal string version, hash;
// Nonessential.
internal float fps;
internal string imagesPath, audioPath;
///<summary>The skeleton's name, which by default is the name of the skeleton data file when possible, or null when a name hasn't been
///set.</summary>
public string Name { get { return name; } set { name = value; } }
/// <summary>The skeleton's bones, sorted parent first. The root bone is always the first bone.</summary>
public ExposedList<BoneData> Bones { get { return bones; } }
public ExposedList<SlotData> Slots { get { return slots; } }
/// <summary>All skins, including the default skin.</summary>
public ExposedList<Skin> Skins { get { return skins; } set { skins = value; } }
/// <summary>
/// The skeleton's default skin.
/// By default this skin contains all attachments that were not in a skin in Spine.
/// </summary>
/// <return>May be null.</return>
public Skin DefaultSkin { get { return defaultSkin; } set { defaultSkin = value; } }
public ExposedList<EventData> Events { get { return events; } set { events = value; } }
public ExposedList<Animation> Animations { get { return animations; } set { animations = value; } }
public ExposedList<IkConstraintData> IkConstraints { get { return ikConstraints; } set { ikConstraints = value; } }
public ExposedList<TransformConstraintData> TransformConstraints { get { return transformConstraints; } set { transformConstraints = value; } }
public ExposedList<PathConstraintData> PathConstraints { get { return pathConstraints; } set { pathConstraints = value; } }
public float X { get { return x; } set { x = value; } }
public float Y { get { return y; } set { y = value; } }
public float Width { get { return width; } set { width = value; } }
public float Height { get { return height; } set { height = value; } }
/// <summary>The Spine version used to export this data, or null.</summary>
public string Version { get { return version; } set { version = value; } }
///<summary>The skeleton data hash. This value will change if any of the skeleton data has changed.
///May be null.</summary>
public string Hash { get { return hash; } set { hash = value; } }
public string ImagesPath { get { return imagesPath; } set { imagesPath = value; } }
/// <summary> The path to the audio directory as defined in Spine. Available only when nonessential data was exported.
/// May be null.</summary>
public string AudioPath { get { return audioPath; } set { audioPath = value; } }
/// <summary>The dopesheet FPS in Spine, or zero if nonessential data was not exported.</summary>
public float Fps { get { return fps; } set { fps = value; } }
// --- Bones.
/// <summary>
/// Finds a bone by comparing each bone's name.
/// It is more efficient to cache the results of this method than to call it multiple times.</summary>
/// <returns>May be null.</returns>
public BoneData FindBone (string boneName) {
if (boneName == null) throw new ArgumentNullException("boneName", "boneName cannot be null.");
var bones = this.bones.Items;
for (int i = 0, n = this.bones.Count; i < n; i++) {
BoneData bone = bones[i];
if (bone.name == boneName) return bone;
}
return null;
}
// --- Slots.
/// <returns>May be null.</returns>
public SlotData FindSlot (string slotName) {
if (slotName == null) throw new ArgumentNullException("slotName", "slotName cannot be null.");
var slots = this.slots.Items;
for (int i = 0, n = this.slots.Count; i < n; i++) {
SlotData slot = slots[i];
if (slot.name == slotName) return slot;
}
return null;
}
// --- Skins.
/// <returns>May be null.</returns>
public Skin FindSkin (string skinName) {
if (skinName == null) throw new ArgumentNullException("skinName", "skinName cannot be null.");
foreach (Skin skin in skins)
if (skin.name == skinName) return skin;
return null;
}
// --- Events.
/// <returns>May be null.</returns>
public EventData FindEvent (string eventDataName) {
if (eventDataName == null) throw new ArgumentNullException("eventDataName", "eventDataName cannot be null.");
foreach (EventData eventData in events)
if (eventData.name == eventDataName) return eventData;
return null;
}
// --- Animations.
/// <returns>May be null.</returns>
public Animation FindAnimation (string animationName) {
if (animationName == null) throw new ArgumentNullException("animationName", "animationName cannot be null.");
var animations = this.animations.Items;
for (int i = 0, n = this.animations.Count; i < n; i++) {
Animation animation = animations[i];
if (animation.name == animationName) return animation;
}
return null;
}
// --- IK constraints.
/// <returns>May be null.</returns>
public IkConstraintData FindIkConstraint (string constraintName) {
if (constraintName == null) throw new ArgumentNullException("constraintName", "constraintName cannot be null.");
var ikConstraints = this.ikConstraints.Items;
for (int i = 0, n = this.ikConstraints.Count; i < n; i++) {
IkConstraintData ikConstraint = ikConstraints[i];
if (ikConstraint.name == constraintName) return ikConstraint;
}
return null;
}
// --- Transform constraints.
/// <returns>May be null.</returns>
public TransformConstraintData FindTransformConstraint (string constraintName) {
if (constraintName == null) throw new ArgumentNullException("constraintName", "constraintName cannot be null.");
var transformConstraints = this.transformConstraints.Items;
for (int i = 0, n = this.transformConstraints.Count; i < n; i++) {
TransformConstraintData transformConstraint = transformConstraints[i];
if (transformConstraint.name == constraintName) return transformConstraint;
}
return null;
}
// --- Path constraints.
/// <returns>May be null.</returns>
public PathConstraintData FindPathConstraint (string constraintName) {
if (constraintName == null) throw new ArgumentNullException("constraintName", "constraintName cannot be null.");
var pathConstraints = this.pathConstraints.Items;
for (int i = 0, n = this.pathConstraints.Count; i < n; i++) {
PathConstraintData constraint = pathConstraints[i];
if (constraint.name.Equals(constraintName)) return constraint;
}
return null;
}
// ---
override public string ToString () {
return name ?? base.ToString();
}
}
}
/******************************************************************************
* 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>Stores the setup pose and all of the stateless data for a skeleton.</summary>
public class SkeletonData {
internal string name;
internal ExposedList<BoneData> bones = new ExposedList<BoneData>(); // Ordered parents first
internal ExposedList<SlotData> slots = new ExposedList<SlotData>(); // Setup pose draw order.
internal ExposedList<Skin> skins = new ExposedList<Skin>();
internal Skin defaultSkin;
internal ExposedList<EventData> events = new ExposedList<EventData>();
internal ExposedList<Animation> animations = new ExposedList<Animation>();
internal ExposedList<IkConstraintData> ikConstraints = new ExposedList<IkConstraintData>();
internal ExposedList<TransformConstraintData> transformConstraints = new ExposedList<TransformConstraintData>();
internal ExposedList<PathConstraintData> pathConstraints = new ExposedList<PathConstraintData>();
internal float x, y, width, height;
internal string version, hash;
// Nonessential.
internal float fps;
internal string imagesPath, audioPath;
///<summary>The skeleton's name, which by default is the name of the skeleton data file when possible, or null when a name hasn't been
///set.</summary>
public string Name { get { return name; } set { name = value; } }
/// <summary>The skeleton's bones, sorted parent first. The root bone is always the first bone.</summary>
public ExposedList<BoneData> Bones { get { return bones; } }
public ExposedList<SlotData> Slots { get { return slots; } }
/// <summary>All skins, including the default skin.</summary>
public ExposedList<Skin> Skins { get { return skins; } set { skins = value; } }
/// <summary>
/// The skeleton's default skin.
/// By default this skin contains all attachments that were not in a skin in Spine.
/// </summary>
/// <return>May be null.</return>
public Skin DefaultSkin { get { return defaultSkin; } set { defaultSkin = value; } }
public ExposedList<EventData> Events { get { return events; } set { events = value; } }
public ExposedList<Animation> Animations { get { return animations; } set { animations = value; } }
public ExposedList<IkConstraintData> IkConstraints { get { return ikConstraints; } set { ikConstraints = value; } }
public ExposedList<TransformConstraintData> TransformConstraints { get { return transformConstraints; } set { transformConstraints = value; } }
public ExposedList<PathConstraintData> PathConstraints { get { return pathConstraints; } set { pathConstraints = value; } }
public float X { get { return x; } set { x = value; } }
public float Y { get { return y; } set { y = value; } }
public float Width { get { return width; } set { width = value; } }
public float Height { get { return height; } set { height = value; } }
/// <summary>The Spine version used to export this data, or null.</summary>
public string Version { get { return version; } set { version = value; } }
///<summary>The skeleton data hash. This value will change if any of the skeleton data has changed.
///May be null.</summary>
public string Hash { get { return hash; } set { hash = value; } }
public string ImagesPath { get { return imagesPath; } set { imagesPath = value; } }
/// <summary> The path to the audio directory as defined in Spine. Available only when nonessential data was exported.
/// May be null.</summary>
public string AudioPath { get { return audioPath; } set { audioPath = value; } }
/// <summary>The dopesheet FPS in Spine, or zero if nonessential data was not exported.</summary>
public float Fps { get { return fps; } set { fps = value; } }
// --- Bones.
/// <summary>
/// Finds a bone by comparing each bone's name.
/// It is more efficient to cache the results of this method than to call it multiple times.</summary>
/// <returns>May be null.</returns>
public BoneData FindBone (string boneName) {
if (boneName == null) throw new ArgumentNullException("boneName", "boneName cannot be null.");
var bones = this.bones.Items;
for (int i = 0, n = this.bones.Count; i < n; i++) {
BoneData bone = bones[i];
if (bone.name == boneName) return bone;
}
return null;
}
// --- Slots.
/// <returns>May be null.</returns>
public SlotData FindSlot (string slotName) {
if (slotName == null) throw new ArgumentNullException("slotName", "slotName cannot be null.");
var slots = this.slots.Items;
for (int i = 0, n = this.slots.Count; i < n; i++) {
SlotData slot = slots[i];
if (slot.name == slotName) return slot;
}
return null;
}
// --- Skins.
/// <returns>May be null.</returns>
public Skin FindSkin (string skinName) {
if (skinName == null) throw new ArgumentNullException("skinName", "skinName cannot be null.");
foreach (Skin skin in skins)
if (skin.name == skinName) return skin;
return null;
}
// --- Events.
/// <returns>May be null.</returns>
public EventData FindEvent (string eventDataName) {
if (eventDataName == null) throw new ArgumentNullException("eventDataName", "eventDataName cannot be null.");
foreach (EventData eventData in events)
if (eventData.name == eventDataName) return eventData;
return null;
}
// --- Animations.
/// <returns>May be null.</returns>
public Animation FindAnimation (string animationName) {
if (animationName == null) throw new ArgumentNullException("animationName", "animationName cannot be null.");
var animations = this.animations.Items;
for (int i = 0, n = this.animations.Count; i < n; i++) {
Animation animation = animations[i];
if (animation.name == animationName) return animation;
}
return null;
}
// --- IK constraints.
/// <returns>May be null.</returns>
public IkConstraintData FindIkConstraint (string constraintName) {
if (constraintName == null) throw new ArgumentNullException("constraintName", "constraintName cannot be null.");
var ikConstraints = this.ikConstraints.Items;
for (int i = 0, n = this.ikConstraints.Count; i < n; i++) {
IkConstraintData ikConstraint = ikConstraints[i];
if (ikConstraint.name == constraintName) return ikConstraint;
}
return null;
}
// --- Transform constraints.
/// <returns>May be null.</returns>
public TransformConstraintData FindTransformConstraint (string constraintName) {
if (constraintName == null) throw new ArgumentNullException("constraintName", "constraintName cannot be null.");
var transformConstraints = this.transformConstraints.Items;
for (int i = 0, n = this.transformConstraints.Count; i < n; i++) {
TransformConstraintData transformConstraint = transformConstraints[i];
if (transformConstraint.name == constraintName) return transformConstraint;
}
return null;
}
// --- Path constraints.
/// <returns>May be null.</returns>
public PathConstraintData FindPathConstraint (string constraintName) {
if (constraintName == null) throw new ArgumentNullException("constraintName", "constraintName cannot be null.");
var pathConstraints = this.pathConstraints.Items;
for (int i = 0, n = this.pathConstraints.Count; i < n; i++) {
PathConstraintData constraint = pathConstraints[i];
if (constraint.name.Equals(constraintName)) return constraint;
}
return null;
}
// ---
override public string ToString () {
return name ?? base.ToString();
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,92 +1,92 @@
/******************************************************************************
* 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;
using System.IO;
namespace Spine {
/// <summary>
/// Base class for loading skeleton data from a file.
/// <para>
/// See<a href="http://esotericsoftware.com/spine-loading-skeleton-data#JSON-and-binary-data">JSON and binary data</a> in the
/// Spine Runtimes Guide.</para>
/// </summary>
public abstract class SkeletonLoader {
protected readonly AttachmentLoader attachmentLoader;
protected float scale = 1;
protected readonly List<LinkedMesh> linkedMeshes = new List<LinkedMesh>();
/// <summary>Creates a skeleton loader that loads attachments using an <see cref="AtlasAttachmentLoader"/> with the specified atlas.
/// </summary>
public SkeletonLoader (params Atlas[] atlasArray) {
attachmentLoader = new AtlasAttachmentLoader(atlasArray);
}
/// <summary>Creates a skeleton loader that loads attachments using the specified attachment loader.
/// <para>See <a href='http://esotericsoftware.com/spine-loading-skeleton-data#JSON-and-binary-data'>Loading skeleton data</a> in the
/// Spine Runtimes Guide.</para></summary>
public SkeletonLoader (AttachmentLoader attachmentLoader) {
if (attachmentLoader == null) throw new ArgumentNullException("attachmentLoader", "attachmentLoader cannot be null.");
this.attachmentLoader = attachmentLoader;
}
/// <summary>Scales bone positions, image sizes, and translations as they are loaded. This allows different size images to be used at
/// runtime than were used in Spine.
/// <para>
/// See <a href="http://esotericsoftware.com/spine-loading-skeleton-data#Scaling">Scaling</a> in the Spine Runtimes Guide.</para>
/// </summary>
public float Scale {
get { return scale; }
set {
if (scale == 0) throw new ArgumentNullException("scale", "scale cannot be 0.");
this.scale = value;
}
}
public abstract SkeletonData ReadSkeletonData (string path);
protected class LinkedMesh {
internal string parent, skin;
internal int slotIndex;
internal MeshAttachment mesh;
internal bool inheritDeform;
public LinkedMesh (MeshAttachment mesh, string skin, int slotIndex, string parent, bool inheritDeform) {
this.mesh = mesh;
this.skin = skin;
this.slotIndex = slotIndex;
this.parent = parent;
this.inheritDeform = inheritDeform;
}
}
}
}
/******************************************************************************
* 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;
using System.IO;
namespace Spine {
/// <summary>
/// Base class for loading skeleton data from a file.
/// <para>
/// See<a href="http://esotericsoftware.com/spine-loading-skeleton-data#JSON-and-binary-data">JSON and binary data</a> in the
/// Spine Runtimes Guide.</para>
/// </summary>
public abstract class SkeletonLoader {
protected readonly AttachmentLoader attachmentLoader;
protected float scale = 1;
protected readonly List<LinkedMesh> linkedMeshes = new List<LinkedMesh>();
/// <summary>Creates a skeleton loader that loads attachments using an <see cref="AtlasAttachmentLoader"/> with the specified atlas.
/// </summary>
public SkeletonLoader (params Atlas[] atlasArray) {
attachmentLoader = new AtlasAttachmentLoader(atlasArray);
}
/// <summary>Creates a skeleton loader that loads attachments using the specified attachment loader.
/// <para>See <a href='http://esotericsoftware.com/spine-loading-skeleton-data#JSON-and-binary-data'>Loading skeleton data</a> in the
/// Spine Runtimes Guide.</para></summary>
public SkeletonLoader (AttachmentLoader attachmentLoader) {
if (attachmentLoader == null) throw new ArgumentNullException("attachmentLoader", "attachmentLoader cannot be null.");
this.attachmentLoader = attachmentLoader;
}
/// <summary>Scales bone positions, image sizes, and translations as they are loaded. This allows different size images to be used at
/// runtime than were used in Spine.
/// <para>
/// See <a href="http://esotericsoftware.com/spine-loading-skeleton-data#Scaling">Scaling</a> in the Spine Runtimes Guide.</para>
/// </summary>
public float Scale {
get { return scale; }
set {
if (scale == 0) throw new ArgumentNullException("scale", "scale cannot be 0.");
this.scale = value;
}
}
public abstract SkeletonData ReadSkeletonData (string path);
protected class LinkedMesh {
internal string parent, skin;
internal int slotIndex;
internal MeshAttachment mesh;
internal bool inheritDeform;
public LinkedMesh (MeshAttachment mesh, string skin, int slotIndex, string parent, bool inheritDeform) {
this.mesh = mesh;
this.skin = skin;
this.slotIndex = slotIndex;
this.parent = parent;
this.inheritDeform = inheritDeform;
}
}
}
}

View File

@@ -1,206 +1,206 @@
/******************************************************************************
* 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;
using System.Collections.Generic;
namespace Spine {
/// <summary>Stores attachments by slot index and attachment name.
/// <para>See SkeletonData <see cref="Spine.SkeletonData.DefaultSkin"/>, Skeleton <see cref="Spine.Skeleton.Skin"/>, and
/// <a href="http://esotericsoftware.com/spine-runtime-skins">Runtime skins</a> in the Spine Runtimes Guide.</para>
/// </summary>
public class Skin {
internal string name;
// Difference to reference implementation: using Dictionary<SkinKey, SkinEntry> instead of HashSet<SkinEntry>.
// Reason is that there is no efficient way to replace or access an already added element, losing any benefits.
private Dictionary<SkinKey, SkinEntry> attachments = new Dictionary<SkinKey, SkinEntry>(SkinKeyComparer.Instance);
internal readonly ExposedList<BoneData> bones = new ExposedList<BoneData>();
internal readonly ExposedList<ConstraintData> constraints = new ExposedList<ConstraintData>();
public string Name { get { return name; } }
///<summary>Returns all attachments contained in this skin.</summary>
public ICollection<SkinEntry> Attachments { get { return attachments.Values; } }
public ExposedList<BoneData> Bones { get { return bones; } }
public ExposedList<ConstraintData> Constraints { get { return constraints; } }
public Skin (string name) {
if (name == null) throw new ArgumentNullException("name", "name cannot be null.");
this.name = name;
}
/// <summary>Adds an attachment to the skin for the specified slot index and name.
/// If the name already exists for the slot, the previous value is replaced.</summary>
public void SetAttachment (int slotIndex, string name, Attachment attachment) {
if (attachment == null) throw new ArgumentNullException("attachment", "attachment cannot be null.");
attachments[new SkinKey(slotIndex, name)] = new SkinEntry(slotIndex, name, attachment);
}
///<summary>Adds all attachments, bones, and constraints from the specified skin to this skin.</summary>
public void AddSkin (Skin skin) {
foreach (BoneData data in skin.bones)
if (!bones.Contains(data)) bones.Add(data);
foreach (ConstraintData data in skin.constraints)
if (!constraints.Contains(data)) constraints.Add(data);
foreach (var item in skin.attachments) {
SkinEntry entry = item.Value;
SetAttachment(entry.slotIndex, entry.name, entry.attachment);
}
}
///<summary>Adds all attachments from the specified skin to this skin. Attachments are deep copied.</summary>
public void CopySkin (Skin skin) {
foreach (BoneData data in skin.bones)
if (!bones.Contains(data)) bones.Add(data);
foreach (ConstraintData data in skin.constraints)
if (!constraints.Contains(data)) constraints.Add(data);
foreach (var item in skin.attachments) {
SkinEntry entry = item.Value;
if (entry.attachment is MeshAttachment) {
SetAttachment(entry.slotIndex, entry.name,
entry.attachment != null ? ((MeshAttachment)entry.attachment).NewLinkedMesh() : null);
} else
SetAttachment(entry.slotIndex, entry.name, entry.attachment != null ? entry.attachment.Copy() : null);
}
}
/// <summary>Returns the attachment for the specified slot index and name, or null.</summary>
/// <returns>May be null.</returns>
public Attachment GetAttachment (int slotIndex, string name) {
SkinEntry entry;
bool containsKey = attachments.TryGetValue(new SkinKey(slotIndex, name), out entry);
return containsKey ? entry.attachment : null;
}
/// <summary> Removes the attachment in the skin for the specified slot index and name, if any.</summary>
public void RemoveAttachment (int slotIndex, string name) {
attachments.Remove(new SkinKey(slotIndex, name));
}
/// <summary>Returns all attachments in this skin for the specified slot index.</summary>
/// <param name="slotIndex">The target slotIndex. To find the slot index, use <see cref="Spine.SkeletonData.FindSlot"/> and <see cref="Spine.SlotData.Index"/>.
public void GetAttachments (int slotIndex, List<SkinEntry> attachments) {
if (slotIndex < 0) throw new ArgumentException("slotIndex must be >= 0.");
if (attachments == null) throw new ArgumentNullException("attachments", "attachments cannot be null.");
foreach (var item in this.attachments) {
SkinEntry entry = item.Value;
if (entry.slotIndex == slotIndex) attachments.Add(entry);
}
}
///<summary>Clears all attachments, bones, and constraints.</summary>
public void Clear () {
attachments.Clear();
bones.Clear();
constraints.Clear();
}
override public string ToString () {
return name;
}
/// <summary>Attach all attachments from this skin if the corresponding attachment from the old skin is currently attached.</summary>
internal void AttachAll (Skeleton skeleton, Skin oldSkin) {
Slot[] slots = skeleton.slots.Items;
foreach (var item in oldSkin.attachments) {
SkinEntry entry = item.Value;
int slotIndex = entry.slotIndex;
Slot slot = slots[slotIndex];
if (slot.Attachment == entry.attachment) {
Attachment attachment = GetAttachment(slotIndex, entry.name);
if (attachment != null) slot.Attachment = attachment;
}
}
}
/// <summary>Stores an entry in the skin consisting of the slot index, name, and attachment.</summary>
public struct SkinEntry {
internal readonly int slotIndex;
internal readonly string name;
internal readonly Attachment attachment;
public SkinEntry (int slotIndex, string name, Attachment attachment) {
this.slotIndex = slotIndex;
this.name = name;
this.attachment = attachment;
}
public int SlotIndex {
get {
return slotIndex;
}
}
/// <summary>The name the attachment is associated with, equivalent to the skin placeholder name in the Spine editor.</summary>
public String Name {
get {
return name;
}
}
public Attachment Attachment {
get {
return attachment;
}
}
}
private struct SkinKey {
internal readonly int slotIndex;
internal readonly string name;
internal readonly int hashCode;
public SkinKey (int slotIndex, string name) {
if (slotIndex < 0) throw new ArgumentException("slotIndex must be >= 0.");
if (name == null) throw new ArgumentNullException("name", "name cannot be null");
this.slotIndex = slotIndex;
this.name = name;
this.hashCode = name.GetHashCode() + slotIndex * 37;
}
}
class SkinKeyComparer : IEqualityComparer<SkinKey> {
internal static readonly SkinKeyComparer Instance = new SkinKeyComparer();
bool IEqualityComparer<SkinKey>.Equals (SkinKey e1, SkinKey e2) {
return e1.slotIndex == e2.slotIndex && string.Equals(e1.name, e2.name, StringComparison.Ordinal);
}
int IEqualityComparer<SkinKey>.GetHashCode (SkinKey e) {
return e.hashCode;
}
}
}
}
/******************************************************************************
* 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;
using System.Collections.Generic;
namespace Spine {
/// <summary>Stores attachments by slot index and attachment name.
/// <para>See SkeletonData <see cref="Spine.SkeletonData.DefaultSkin"/>, Skeleton <see cref="Spine.Skeleton.Skin"/>, and
/// <a href="http://esotericsoftware.com/spine-runtime-skins">Runtime skins</a> in the Spine Runtimes Guide.</para>
/// </summary>
public class Skin {
internal string name;
// Difference to reference implementation: using Dictionary<SkinKey, SkinEntry> instead of HashSet<SkinEntry>.
// Reason is that there is no efficient way to replace or access an already added element, losing any benefits.
private Dictionary<SkinKey, SkinEntry> attachments = new Dictionary<SkinKey, SkinEntry>(SkinKeyComparer.Instance);
internal readonly ExposedList<BoneData> bones = new ExposedList<BoneData>();
internal readonly ExposedList<ConstraintData> constraints = new ExposedList<ConstraintData>();
public string Name { get { return name; } }
///<summary>Returns all attachments contained in this skin.</summary>
public ICollection<SkinEntry> Attachments { get { return attachments.Values; } }
public ExposedList<BoneData> Bones { get { return bones; } }
public ExposedList<ConstraintData> Constraints { get { return constraints; } }
public Skin (string name) {
if (name == null) throw new ArgumentNullException("name", "name cannot be null.");
this.name = name;
}
/// <summary>Adds an attachment to the skin for the specified slot index and name.
/// If the name already exists for the slot, the previous value is replaced.</summary>
public void SetAttachment (int slotIndex, string name, Attachment attachment) {
if (attachment == null) throw new ArgumentNullException("attachment", "attachment cannot be null.");
attachments[new SkinKey(slotIndex, name)] = new SkinEntry(slotIndex, name, attachment);
}
///<summary>Adds all attachments, bones, and constraints from the specified skin to this skin.</summary>
public void AddSkin (Skin skin) {
foreach (BoneData data in skin.bones)
if (!bones.Contains(data)) bones.Add(data);
foreach (ConstraintData data in skin.constraints)
if (!constraints.Contains(data)) constraints.Add(data);
foreach (var item in skin.attachments) {
SkinEntry entry = item.Value;
SetAttachment(entry.slotIndex, entry.name, entry.attachment);
}
}
///<summary>Adds all attachments from the specified skin to this skin. Attachments are deep copied.</summary>
public void CopySkin (Skin skin) {
foreach (BoneData data in skin.bones)
if (!bones.Contains(data)) bones.Add(data);
foreach (ConstraintData data in skin.constraints)
if (!constraints.Contains(data)) constraints.Add(data);
foreach (var item in skin.attachments) {
SkinEntry entry = item.Value;
if (entry.attachment is MeshAttachment) {
SetAttachment(entry.slotIndex, entry.name,
entry.attachment != null ? ((MeshAttachment)entry.attachment).NewLinkedMesh() : null);
} else
SetAttachment(entry.slotIndex, entry.name, entry.attachment != null ? entry.attachment.Copy() : null);
}
}
/// <summary>Returns the attachment for the specified slot index and name, or null.</summary>
/// <returns>May be null.</returns>
public Attachment GetAttachment (int slotIndex, string name) {
SkinEntry entry;
bool containsKey = attachments.TryGetValue(new SkinKey(slotIndex, name), out entry);
return containsKey ? entry.attachment : null;
}
/// <summary> Removes the attachment in the skin for the specified slot index and name, if any.</summary>
public void RemoveAttachment (int slotIndex, string name) {
attachments.Remove(new SkinKey(slotIndex, name));
}
/// <summary>Returns all attachments in this skin for the specified slot index.</summary>
/// <param name="slotIndex">The target slotIndex. To find the slot index, use <see cref="Spine.SkeletonData.FindSlot"/> and <see cref="Spine.SlotData.Index"/>.
public void GetAttachments (int slotIndex, List<SkinEntry> attachments) {
if (slotIndex < 0) throw new ArgumentException("slotIndex must be >= 0.");
if (attachments == null) throw new ArgumentNullException("attachments", "attachments cannot be null.");
foreach (var item in this.attachments) {
SkinEntry entry = item.Value;
if (entry.slotIndex == slotIndex) attachments.Add(entry);
}
}
///<summary>Clears all attachments, bones, and constraints.</summary>
public void Clear () {
attachments.Clear();
bones.Clear();
constraints.Clear();
}
override public string ToString () {
return name;
}
/// <summary>Attach all attachments from this skin if the corresponding attachment from the old skin is currently attached.</summary>
internal void AttachAll (Skeleton skeleton, Skin oldSkin) {
Slot[] slots = skeleton.slots.Items;
foreach (var item in oldSkin.attachments) {
SkinEntry entry = item.Value;
int slotIndex = entry.slotIndex;
Slot slot = slots[slotIndex];
if (slot.Attachment == entry.attachment) {
Attachment attachment = GetAttachment(slotIndex, entry.name);
if (attachment != null) slot.Attachment = attachment;
}
}
}
/// <summary>Stores an entry in the skin consisting of the slot index, name, and attachment.</summary>
public struct SkinEntry {
internal readonly int slotIndex;
internal readonly string name;
internal readonly Attachment attachment;
public SkinEntry (int slotIndex, string name, Attachment attachment) {
this.slotIndex = slotIndex;
this.name = name;
this.attachment = attachment;
}
public int SlotIndex {
get {
return slotIndex;
}
}
/// <summary>The name the attachment is associated with, equivalent to the skin placeholder name in the Spine editor.</summary>
public String Name {
get {
return name;
}
}
public Attachment Attachment {
get {
return attachment;
}
}
}
private struct SkinKey {
internal readonly int slotIndex;
internal readonly string name;
internal readonly int hashCode;
public SkinKey (int slotIndex, string name) {
if (slotIndex < 0) throw new ArgumentException("slotIndex must be >= 0.");
if (name == null) throw new ArgumentNullException("name", "name cannot be null");
this.slotIndex = slotIndex;
this.name = name;
this.hashCode = name.GetHashCode() + slotIndex * 37;
}
}
class SkinKeyComparer : IEqualityComparer<SkinKey> {
internal static readonly SkinKeyComparer Instance = new SkinKeyComparer();
bool IEqualityComparer<SkinKey>.Equals (SkinKey e1, SkinKey e2) {
return e1.slotIndex == e2.slotIndex && string.Equals(e1.name, e2.name, StringComparison.Ordinal);
}
int IEqualityComparer<SkinKey>.GetHashCode (SkinKey e) {
return e.hashCode;
}
}
}
}

View File

@@ -1,200 +1,200 @@
/******************************************************************************
* 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>
/// Stores a slot's current pose. Slots organize attachments for <see cref="Skeleton.DrawOrder"/> purposes and provide a place to store
/// state for an attachment.State cannot be stored in an attachment itself because attachments are stateless and may be shared
/// across multiple skeletons.
/// </summary>
public class Slot {
internal SlotData data;
internal Bone bone;
internal float r, g, b, a;
internal float r2, g2, b2;
internal bool hasSecondColor;
internal Attachment attachment;
internal float attachmentTime;
internal ExposedList<float> deform = new ExposedList<float>();
internal int attachmentState;
public Slot (SlotData data, Bone bone) {
if (data == null) throw new ArgumentNullException("data", "data cannot be null.");
if (bone == null) throw new ArgumentNullException("bone", "bone cannot be null.");
this.data = data;
this.bone = bone;
// darkColor = data.darkColor == null ? null : new Color();
if (data.hasSecondColor) {
r2 = g2 = b2 = 0;
}
SetToSetupPose();
}
/// <summary>Copy constructor.</summary>
public Slot (Slot slot, Bone bone) {
if (slot == null) throw new ArgumentNullException("slot", "slot cannot be null.");
if (bone == null) throw new ArgumentNullException("bone", "bone cannot be null.");
data = slot.data;
this.bone = bone;
r = slot.r;
g = slot.g;
b = slot.b;
a = slot.a;
// darkColor = slot.darkColor == null ? null : new Color(slot.darkColor);
if (slot.hasSecondColor) {
r2 = slot.r2;
g2 = slot.g2;
b2 = slot.b2;
} else {
r2 = g2 = b2 = 0;
}
hasSecondColor = slot.hasSecondColor;
attachment = slot.attachment;
attachmentTime = slot.attachmentTime;
deform.AddRange(slot.deform);
}
/// <summary>The slot's setup pose data.</summary>
public SlotData Data { get { return data; } }
/// <summary>The bone this slot belongs to.</summary>
public Bone Bone { get { return bone; } }
/// <summary>The skeleton this slot belongs to.</summary>
public Skeleton Skeleton { get { return bone.skeleton; } }
/// <summary>The color used to tint the slot's attachment. If <see cref="HasSecondColor"/> is set, this is used as the light color for two
/// color tinting.</summary>
public float R { get { return r; } set { r = value; } }
/// <summary>The color used to tint the slot's attachment. If <see cref="HasSecondColor"/> is set, this is used as the light color for two
/// color tinting.</summary>
public float G { get { return g; } set { g = value; } }
/// <summary>The color used to tint the slot's attachment. If <see cref="HasSecondColor"/> is set, this is used as the light color for two
/// color tinting.</summary>
public float B { get { return b; } set { b = value; } }
/// <summary>The color used to tint the slot's attachment. If <see cref="HasSecondColor"/> is set, this is used as the light color for two
/// color tinting.</summary>
public float A { get { return a; } set { a = value; } }
public void ClampColor () {
r = MathUtils.Clamp(r, 0, 1);
g = MathUtils.Clamp(g, 0, 1);
b = MathUtils.Clamp(b, 0, 1);
a = MathUtils.Clamp(a, 0, 1);
}
/// <summary>The dark color used to tint the slot's attachment for two color tinting, ignored if two color tinting is not used.</summary>
/// <seealso cref="HasSecondColor"/>
public float R2 { get { return r2; } set { r2 = value; } }
/// <summary>The dark color used to tint the slot's attachment for two color tinting, ignored if two color tinting is not used.</summary>
/// <seealso cref="HasSecondColor"/>
public float G2 { get { return g2; } set { g2 = value; } }
/// <summary>The dark color used to tint the slot's attachment for two color tinting, ignored if two color tinting is not used.</summary>
/// <seealso cref="HasSecondColor"/>
public float B2 { get { return b2; } set { b2 = value; } }
/// <summary>Whether R2 G2 B2 are used to tint the slot's attachment for two color tinting. False if two color tinting is not used.</summary>
public bool HasSecondColor { get { return data.hasSecondColor; } set { data.hasSecondColor = value; } }
public void ClampSecondColor () {
r2 = MathUtils.Clamp(r2, 0, 1);
g2 = MathUtils.Clamp(g2, 0, 1);
b2 = MathUtils.Clamp(b2, 0, 1);
}
public Attachment Attachment {
/// <summary>The current attachment for the slot, or null if the slot has no attachment.</summary>
get { return attachment; }
/// <summary>
/// Sets the slot's attachment and, if the attachment changed, resets <see cref="AttachmentTime"/> and clears the <see cref="Deform"/>.
/// The deform is not cleared if the old attachment has the same <see cref="VertexAttachment.DeformAttachment"/> as the specified
/// attachment.</summary>
/// <param name="value">May be null.</param>
set {
if (attachment == value) return;
if (!(value is VertexAttachment) || !(this.attachment is VertexAttachment)
|| ((VertexAttachment)value).DeformAttachment != ((VertexAttachment)this.attachment).DeformAttachment) {
deform.Clear();
}
this.attachment = value;
attachmentTime = bone.skeleton.time;
}
}
/// <summary> The time that has elapsed since the last time the attachment was set or cleared. Relies on Skeleton
/// <see cref="Skeleton.Time"/></summary>
public float AttachmentTime {
get { return bone.skeleton.time - attachmentTime; }
set { attachmentTime = bone.skeleton.time - value; }
}
/// <summary> Vertices to deform the slot's attachment. For an unweighted mesh, the entries are local positions for each vertex. For a
/// weighted mesh, the entries are an offset for each vertex which will be added to the mesh's local vertex positions.
/// <para />
/// See <see cref="VertexAttachment.ComputeWorldVertices(Slot, int, int, float[], int, int)"/> and <see cref="DeformTimeline"/>.</summary>
public ExposedList<float> Deform {
get {
return deform;
}
set {
if (deform == null) throw new ArgumentNullException("deform", "deform cannot be null.");
deform = value;
}
}
/// <summary>Sets this slot to the setup pose.</summary>
public void SetToSetupPose () {
r = data.r;
g = data.g;
b = data.b;
a = data.a;
// if (darkColor != null) darkColor.set(data.darkColor);
if (HasSecondColor) {
r2 = data.r2;
g2 = data.g2;
b2 = data.b2;
}
if (data.attachmentName == null)
Attachment = null;
else {
attachment = null;
Attachment = bone.skeleton.GetAttachment(data.index, data.attachmentName);
}
}
override public string ToString () {
return data.name;
}
}
}
/******************************************************************************
* 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>
/// Stores a slot's current pose. Slots organize attachments for <see cref="Skeleton.DrawOrder"/> purposes and provide a place to store
/// state for an attachment.State cannot be stored in an attachment itself because attachments are stateless and may be shared
/// across multiple skeletons.
/// </summary>
public class Slot {
internal SlotData data;
internal Bone bone;
internal float r, g, b, a;
internal float r2, g2, b2;
internal bool hasSecondColor;
internal Attachment attachment;
internal float attachmentTime;
internal ExposedList<float> deform = new ExposedList<float>();
internal int attachmentState;
public Slot (SlotData data, Bone bone) {
if (data == null) throw new ArgumentNullException("data", "data cannot be null.");
if (bone == null) throw new ArgumentNullException("bone", "bone cannot be null.");
this.data = data;
this.bone = bone;
// darkColor = data.darkColor == null ? null : new Color();
if (data.hasSecondColor) {
r2 = g2 = b2 = 0;
}
SetToSetupPose();
}
/// <summary>Copy constructor.</summary>
public Slot (Slot slot, Bone bone) {
if (slot == null) throw new ArgumentNullException("slot", "slot cannot be null.");
if (bone == null) throw new ArgumentNullException("bone", "bone cannot be null.");
data = slot.data;
this.bone = bone;
r = slot.r;
g = slot.g;
b = slot.b;
a = slot.a;
// darkColor = slot.darkColor == null ? null : new Color(slot.darkColor);
if (slot.hasSecondColor) {
r2 = slot.r2;
g2 = slot.g2;
b2 = slot.b2;
} else {
r2 = g2 = b2 = 0;
}
hasSecondColor = slot.hasSecondColor;
attachment = slot.attachment;
attachmentTime = slot.attachmentTime;
deform.AddRange(slot.deform);
}
/// <summary>The slot's setup pose data.</summary>
public SlotData Data { get { return data; } }
/// <summary>The bone this slot belongs to.</summary>
public Bone Bone { get { return bone; } }
/// <summary>The skeleton this slot belongs to.</summary>
public Skeleton Skeleton { get { return bone.skeleton; } }
/// <summary>The color used to tint the slot's attachment. If <see cref="HasSecondColor"/> is set, this is used as the light color for two
/// color tinting.</summary>
public float R { get { return r; } set { r = value; } }
/// <summary>The color used to tint the slot's attachment. If <see cref="HasSecondColor"/> is set, this is used as the light color for two
/// color tinting.</summary>
public float G { get { return g; } set { g = value; } }
/// <summary>The color used to tint the slot's attachment. If <see cref="HasSecondColor"/> is set, this is used as the light color for two
/// color tinting.</summary>
public float B { get { return b; } set { b = value; } }
/// <summary>The color used to tint the slot's attachment. If <see cref="HasSecondColor"/> is set, this is used as the light color for two
/// color tinting.</summary>
public float A { get { return a; } set { a = value; } }
public void ClampColor () {
r = MathUtils.Clamp(r, 0, 1);
g = MathUtils.Clamp(g, 0, 1);
b = MathUtils.Clamp(b, 0, 1);
a = MathUtils.Clamp(a, 0, 1);
}
/// <summary>The dark color used to tint the slot's attachment for two color tinting, ignored if two color tinting is not used.</summary>
/// <seealso cref="HasSecondColor"/>
public float R2 { get { return r2; } set { r2 = value; } }
/// <summary>The dark color used to tint the slot's attachment for two color tinting, ignored if two color tinting is not used.</summary>
/// <seealso cref="HasSecondColor"/>
public float G2 { get { return g2; } set { g2 = value; } }
/// <summary>The dark color used to tint the slot's attachment for two color tinting, ignored if two color tinting is not used.</summary>
/// <seealso cref="HasSecondColor"/>
public float B2 { get { return b2; } set { b2 = value; } }
/// <summary>Whether R2 G2 B2 are used to tint the slot's attachment for two color tinting. False if two color tinting is not used.</summary>
public bool HasSecondColor { get { return data.hasSecondColor; } set { data.hasSecondColor = value; } }
public void ClampSecondColor () {
r2 = MathUtils.Clamp(r2, 0, 1);
g2 = MathUtils.Clamp(g2, 0, 1);
b2 = MathUtils.Clamp(b2, 0, 1);
}
public Attachment Attachment {
/// <summary>The current attachment for the slot, or null if the slot has no attachment.</summary>
get { return attachment; }
/// <summary>
/// Sets the slot's attachment and, if the attachment changed, resets <see cref="AttachmentTime"/> and clears the <see cref="Deform"/>.
/// The deform is not cleared if the old attachment has the same <see cref="VertexAttachment.DeformAttachment"/> as the specified
/// attachment.</summary>
/// <param name="value">May be null.</param>
set {
if (attachment == value) return;
if (!(value is VertexAttachment) || !(this.attachment is VertexAttachment)
|| ((VertexAttachment)value).DeformAttachment != ((VertexAttachment)this.attachment).DeformAttachment) {
deform.Clear();
}
this.attachment = value;
attachmentTime = bone.skeleton.time;
}
}
/// <summary> The time that has elapsed since the last time the attachment was set or cleared. Relies on Skeleton
/// <see cref="Skeleton.Time"/></summary>
public float AttachmentTime {
get { return bone.skeleton.time - attachmentTime; }
set { attachmentTime = bone.skeleton.time - value; }
}
/// <summary> Vertices to deform the slot's attachment. For an unweighted mesh, the entries are local positions for each vertex. For a
/// weighted mesh, the entries are an offset for each vertex which will be added to the mesh's local vertex positions.
/// <para />
/// See <see cref="VertexAttachment.ComputeWorldVertices(Slot, int, int, float[], int, int)"/> and <see cref="DeformTimeline"/>.</summary>
public ExposedList<float> Deform {
get {
return deform;
}
set {
if (deform == null) throw new ArgumentNullException("deform", "deform cannot be null.");
deform = value;
}
}
/// <summary>Sets this slot to the setup pose.</summary>
public void SetToSetupPose () {
r = data.r;
g = data.g;
b = data.b;
a = data.a;
// if (darkColor != null) darkColor.set(data.darkColor);
if (HasSecondColor) {
r2 = data.r2;
g2 = data.g2;
b2 = data.b2;
}
if (data.attachmentName == null)
Attachment = null;
else {
attachment = null;
Attachment = bone.skeleton.GetAttachment(data.index, data.attachmentName);
}
}
override public string ToString () {
return data.name;
}
}
}

View File

@@ -1,77 +1,77 @@
/******************************************************************************
* 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 SlotData {
internal int index;
internal string name;
internal BoneData boneData;
internal float r = 1, g = 1, b = 1, a = 1;
internal float r2 = 0, g2 = 0, b2 = 0;
internal bool hasSecondColor = false;
internal string attachmentName;
internal BlendMode blendMode;
/// <summary>The index of the slot in <see cref="Skeleton.Slots"/>.</summary>
public int Index { get { return index; } }
/// <summary>The name of the slot, which is unique across all slots in the skeleton.</summary>
public string Name { get { return name; } }
/// <summary>The bone this slot belongs to.</summary>
public BoneData BoneData { get { return boneData; } }
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 float R2 { get { return r2; } set { r2 = value; } }
public float G2 { get { return g2; } set { g2 = value; } }
public float B2 { get { return b2; } set { b2 = value; } }
public bool HasSecondColor { get { return hasSecondColor; } set { hasSecondColor = value; } }
/// <summary>The name of the attachment that is visible for this slot in the setup pose, or null if no attachment is visible.</summary>
public String AttachmentName { get { return attachmentName; } set { attachmentName = value; } }
/// <summary>The blend mode for drawing the slot's attachment.</summary>
public BlendMode BlendMode { get { return blendMode; } set { blendMode = value; } }
public SlotData (int index, String name, BoneData boneData) {
if (index < 0) throw new ArgumentException("index must be >= 0.", "index");
if (name == null) throw new ArgumentNullException("name", "name cannot be null.");
if (boneData == null) throw new ArgumentNullException("boneData", "boneData cannot be null.");
this.index = index;
this.name = name;
this.boneData = boneData;
}
override public string ToString () {
return name;
}
}
}
/******************************************************************************
* 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 SlotData {
internal int index;
internal string name;
internal BoneData boneData;
internal float r = 1, g = 1, b = 1, a = 1;
internal float r2 = 0, g2 = 0, b2 = 0;
internal bool hasSecondColor = false;
internal string attachmentName;
internal BlendMode blendMode;
/// <summary>The index of the slot in <see cref="Skeleton.Slots"/>.</summary>
public int Index { get { return index; } }
/// <summary>The name of the slot, which is unique across all slots in the skeleton.</summary>
public string Name { get { return name; } }
/// <summary>The bone this slot belongs to.</summary>
public BoneData BoneData { get { return boneData; } }
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 float R2 { get { return r2; } set { r2 = value; } }
public float G2 { get { return g2; } set { g2 = value; } }
public float B2 { get { return b2; } set { b2 = value; } }
public bool HasSecondColor { get { return hasSecondColor; } set { hasSecondColor = value; } }
/// <summary>The name of the attachment that is visible for this slot in the setup pose, or null if no attachment is visible.</summary>
public String AttachmentName { get { return attachmentName; } set { attachmentName = value; } }
/// <summary>The blend mode for drawing the slot's attachment.</summary>
public BlendMode BlendMode { get { return blendMode; } set { blendMode = value; } }
public SlotData (int index, String name, BoneData boneData) {
if (index < 0) throw new ArgumentException("index must be >= 0.", "index");
if (name == null) throw new ArgumentNullException("name", "name cannot be null.");
if (boneData == null) throw new ArgumentNullException("boneData", "boneData cannot be null.");
this.index = index;
this.name = name;
this.boneData = boneData;
}
override public string ToString () {
return name;
}
}
}

View File

@@ -1,311 +1,311 @@
/******************************************************************************
* 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 transform constraint. A transform constraint adjusts the world transform of the constrained
/// bones to match that of the target bone.</para>
/// <para>
/// See <a href="http://esotericsoftware.com/spine-transform-constraints">Transform constraints</a> in the Spine User Guide.</para>
/// </summary>
public class TransformConstraint : IUpdatable {
internal TransformConstraintData data;
internal ExposedList<Bone> bones;
internal Bone target;
internal float mixRotate, mixX, mixY, mixScaleX, mixScaleY, mixShearY;
internal bool active;
public TransformConstraint (TransformConstraintData 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;
mixRotate = data.mixRotate;
mixX = data.mixX;
mixY = data.mixY;
mixScaleX = data.mixScaleX;
mixScaleY = data.mixScaleY;
mixShearY = data.mixShearY;
bones = new ExposedList<Bone>();
foreach (BoneData boneData in data.bones)
bones.Add(skeleton.FindBone(boneData.name));
target = skeleton.FindBone(data.target.name);
}
/// <summary>Copy constructor.</summary>
public TransformConstraint (TransformConstraint 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];
mixRotate = constraint.mixRotate;
mixX = constraint.mixX;
mixY = constraint.mixY;
mixScaleX = constraint.mixScaleX;
mixScaleY = constraint.mixScaleY;
mixShearY = constraint.mixShearY;
}
public void Update () {
if (mixRotate == 0 && mixX == 0 && mixY == 0 && mixScaleX == 0 && mixScaleX == 0 && mixShearY == 0) return;
if (data.local) {
if (data.relative)
ApplyRelativeLocal();
else
ApplyAbsoluteLocal();
} else {
if (data.relative)
ApplyRelativeWorld();
else
ApplyAbsoluteWorld();
}
}
void ApplyAbsoluteWorld () {
float mixRotate = this.mixRotate, mixX = this.mixX, mixY = this.mixY, mixScaleX = this.mixScaleX,
mixScaleY = this.mixScaleY, mixShearY = this.mixShearY;
bool translate = mixX != 0 || mixY != 0;
Bone target = this.target;
float ta = target.a, tb = target.b, tc = target.c, td = target.d;
float degRadReflect = ta * td - tb * tc > 0 ? MathUtils.DegRad : -MathUtils.DegRad;
float offsetRotation = data.offsetRotation * degRadReflect, offsetShearY = data.offsetShearY * degRadReflect;
var bones = this.bones.Items;
for (int i = 0, n = this.bones.Count; i < n; i++) {
Bone bone = bones[i];
if (mixRotate != 0) {
float a = bone.a, b = bone.b, c = bone.c, d = bone.d;
float r = MathUtils.Atan2(tc, ta) - MathUtils.Atan2(c, a) + offsetRotation;
if (r > MathUtils.PI)
r -= MathUtils.PI2;
else if (r < -MathUtils.PI) //
r += MathUtils.PI2;
r *= mixRotate;
float 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;
}
if (translate) {
float tx, ty; //Vector2 temp = this.temp;
target.LocalToWorld(data.offsetX, data.offsetY, out tx, out ty); //target.localToWorld(temp.set(data.offsetX, data.offsetY));
bone.worldX += (tx - bone.worldX) * mixX;
bone.worldY += (ty - bone.worldY) * mixY;
}
if (mixScaleX != 0) {
float s = (float)Math.Sqrt(bone.a * bone.a + bone.c * bone.c);
if (s != 0) s = (s + ((float)Math.Sqrt(ta * ta + tc * tc) - s + data.offsetScaleX) * mixScaleX) / s;
bone.a *= s;
bone.c *= s;
}
if (mixScaleY != 0) {
float s = (float)Math.Sqrt(bone.b * bone.b + bone.d * bone.d);
if (s != 0) s = (s + ((float)Math.Sqrt(tb * tb + td * td) - s + data.offsetScaleY) * mixScaleY) / s;
bone.b *= s;
bone.d *= s;
}
if (mixShearY > 0) {
float b = bone.b, d = bone.d;
float by = MathUtils.Atan2(d, b);
float r = MathUtils.Atan2(td, tb) - MathUtils.Atan2(tc, ta) - (by - MathUtils.Atan2(bone.c, bone.a));
if (r > MathUtils.PI)
r -= MathUtils.PI2;
else if (r < -MathUtils.PI) //
r += MathUtils.PI2;
r = by + (r + offsetShearY) * mixShearY;
float s = (float)Math.Sqrt(b * b + d * d);
bone.b = MathUtils.Cos(r) * s;
bone.d = MathUtils.Sin(r) * s;
}
bone.UpdateAppliedTransform();
}
}
void ApplyRelativeWorld () {
float mixRotate = this.mixRotate, mixX = this.mixX, mixY = this.mixY, mixScaleX = this.mixScaleX,
mixScaleY = this.mixScaleY, mixShearY = this.mixShearY;
bool translate = mixX != 0 || mixY != 0;
Bone target = this.target;
float ta = target.a, tb = target.b, tc = target.c, td = target.d;
float degRadReflect = ta * td - tb * tc > 0 ? MathUtils.DegRad : -MathUtils.DegRad;
float offsetRotation = data.offsetRotation * degRadReflect, offsetShearY = data.offsetShearY * degRadReflect;
var bones = this.bones.Items;
for (int i = 0, n = this.bones.Count; i < n; i++) {
Bone bone = bones[i];
if (mixRotate != 0) {
float a = bone.a, b = bone.b, c = bone.c, d = bone.d;
float r = MathUtils.Atan2(tc, ta) + offsetRotation;
if (r > MathUtils.PI)
r -= MathUtils.PI2;
else if (r < -MathUtils.PI) //
r += MathUtils.PI2;
r *= mixRotate;
float 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;
}
if (translate) {
float tx, ty; //Vector2 temp = this.temp;
target.LocalToWorld(data.offsetX, data.offsetY, out tx, out ty); //target.localToWorld(temp.set(data.offsetX, data.offsetY));
bone.worldX += tx * mixX;
bone.worldY += ty * mixY;
}
if (mixScaleX != 0) {
float s = ((float)Math.Sqrt(ta * ta + tc * tc) - 1 + data.offsetScaleX) * mixScaleX + 1;
bone.a *= s;
bone.c *= s;
}
if (mixScaleY != 0) {
float s = ((float)Math.Sqrt(tb * tb + td * td) - 1 + data.offsetScaleY) * mixScaleY + 1;
bone.b *= s;
bone.d *= s;
}
if (mixShearY > 0) {
float r = MathUtils.Atan2(td, tb) - MathUtils.Atan2(tc, ta);
if (r > MathUtils.PI)
r -= MathUtils.PI2;
else if (r < -MathUtils.PI) //
r += MathUtils.PI2;
float b = bone.b, d = bone.d;
r = MathUtils.Atan2(d, b) + (r - MathUtils.PI / 2 + offsetShearY) * mixShearY;
float s = (float)Math.Sqrt(b * b + d * d);
bone.b = MathUtils.Cos(r) * s;
bone.d = MathUtils.Sin(r) * s;
}
bone.UpdateAppliedTransform();
}
}
void ApplyAbsoluteLocal () {
float mixRotate = this.mixRotate, mixX = this.mixX, mixY = this.mixY, mixScaleX = this.mixScaleX,
mixScaleY = this.mixScaleY, mixShearY = this.mixShearY;
Bone target = this.target;
var bones = this.bones.Items;
for (int i = 0, n = this.bones.Count; i < n; i++) {
Bone bone = bones[i];
float rotation = bone.arotation;
if (mixRotate != 0) {
float r = target.arotation - rotation + data.offsetRotation;
r -= (16384 - (int)(16384.499999999996 - r / 360)) * 360;
rotation += r * mixRotate;
}
float x = bone.ax, y = bone.ay;
x += (target.ax - x + data.offsetX) * mixX;
y += (target.ay - y + data.offsetY) * mixY;
float scaleX = bone.ascaleX, scaleY = bone.ascaleY;
if (mixScaleX != 0 && scaleX != 0)
scaleX = (scaleX + (target.ascaleX - scaleX + data.offsetScaleX) * mixScaleX) / scaleX;
if (mixScaleY != 0 && scaleY != 0)
scaleY = (scaleY + (target.ascaleY - scaleY + data.offsetScaleY) * mixScaleY) / scaleY;
float shearY = bone.ashearY;
if (mixShearY != 0) {
float r = target.ashearY - shearY + data.offsetShearY;
r -= (16384 - (int)(16384.499999999996 - r / 360)) * 360;
shearY += r * mixShearY;
}
bone.UpdateWorldTransform(x, y, rotation, scaleX, scaleY, bone.ashearX, shearY);
}
}
void ApplyRelativeLocal () {
float mixRotate = this.mixRotate, mixX = this.mixX, mixY = this.mixY, mixScaleX = this.mixScaleX,
mixScaleY = this.mixScaleY, mixShearY = this.mixShearY;
Bone target = this.target;
var bones = this.bones.Items;
for (int i = 0, n = this.bones.Count; i < n; i++) {
Bone bone = bones[i];
float rotation = bone.arotation + (target.arotation + data.offsetRotation) * mixRotate;
float x = bone.ax + (target.ax + data.offsetX) * mixX;
float y = bone.ay + (target.ay + data.offsetY) * mixY;
float scaleX = bone.ascaleX * (((target.ascaleX - 1 + data.offsetScaleX) * mixScaleX) + 1);
float scaleY = bone.ascaleY * (((target.ascaleY - 1 + data.offsetScaleY) * mixScaleY) + 1);
float shearY = bone.ashearY + (target.ashearY + data.offsetShearY) * mixShearY;
bone.UpdateWorldTransform(x, y, rotation, scaleX, scaleY, bone.ashearX, shearY);
}
}
/// <summary>The bones that will be modified by this transform constraint.</summary>
public ExposedList<Bone> Bones { get { return bones; } }
/// <summary>The target bone whose world transform will be copied to the constrained bones.</summary>
public Bone Target { get { return target; } set { target = value; } }
/// <summary>A percentage (0-1) that controls the mix between the constrained and unconstrained rotation.</summary>
public float MixRotate { get { return mixRotate; } set { mixRotate = value; } }
/// <summary>A percentage (0-1) that controls the mix between the constrained and unconstrained translation X.</summary>
public float MixX { get { return mixX; } set { mixX = value; } }
/// <summary>A percentage (0-1) that controls the mix between the constrained and unconstrained translation Y.</summary>
public float MixY { get { return mixY; } set { mixY = value; } }
/// <summary>A percentage (0-1) that controls the mix between the constrained and unconstrained scale X.</summary>
public float MixScaleX { get { return mixScaleX; } set { mixScaleX = value; } }
/// <summary>A percentage (0-1) that controls the mix between the constrained and unconstrained scale Y.</summary>
public float MixScaleY { get { return mixScaleY; } set { mixScaleY = value; } }
/// <summary>A percentage (0-1) that controls the mix between the constrained and unconstrained shear Y.</summary>
public float MixShearY { get { return mixShearY; } set { mixShearY = value; } }
public bool Active { get { return active; } }
/// <summary>The transform constraint's setup pose data.</summary>
public TransformConstraintData Data { get { return data; } }
override public string ToString () {
return data.name;
}
}
}
/******************************************************************************
* 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 transform constraint. A transform constraint adjusts the world transform of the constrained
/// bones to match that of the target bone.</para>
/// <para>
/// See <a href="http://esotericsoftware.com/spine-transform-constraints">Transform constraints</a> in the Spine User Guide.</para>
/// </summary>
public class TransformConstraint : IUpdatable {
internal TransformConstraintData data;
internal ExposedList<Bone> bones;
internal Bone target;
internal float mixRotate, mixX, mixY, mixScaleX, mixScaleY, mixShearY;
internal bool active;
public TransformConstraint (TransformConstraintData 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;
mixRotate = data.mixRotate;
mixX = data.mixX;
mixY = data.mixY;
mixScaleX = data.mixScaleX;
mixScaleY = data.mixScaleY;
mixShearY = data.mixShearY;
bones = new ExposedList<Bone>();
foreach (BoneData boneData in data.bones)
bones.Add(skeleton.FindBone(boneData.name));
target = skeleton.FindBone(data.target.name);
}
/// <summary>Copy constructor.</summary>
public TransformConstraint (TransformConstraint 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];
mixRotate = constraint.mixRotate;
mixX = constraint.mixX;
mixY = constraint.mixY;
mixScaleX = constraint.mixScaleX;
mixScaleY = constraint.mixScaleY;
mixShearY = constraint.mixShearY;
}
public void Update () {
if (mixRotate == 0 && mixX == 0 && mixY == 0 && mixScaleX == 0 && mixScaleX == 0 && mixShearY == 0) return;
if (data.local) {
if (data.relative)
ApplyRelativeLocal();
else
ApplyAbsoluteLocal();
} else {
if (data.relative)
ApplyRelativeWorld();
else
ApplyAbsoluteWorld();
}
}
void ApplyAbsoluteWorld () {
float mixRotate = this.mixRotate, mixX = this.mixX, mixY = this.mixY, mixScaleX = this.mixScaleX,
mixScaleY = this.mixScaleY, mixShearY = this.mixShearY;
bool translate = mixX != 0 || mixY != 0;
Bone target = this.target;
float ta = target.a, tb = target.b, tc = target.c, td = target.d;
float degRadReflect = ta * td - tb * tc > 0 ? MathUtils.DegRad : -MathUtils.DegRad;
float offsetRotation = data.offsetRotation * degRadReflect, offsetShearY = data.offsetShearY * degRadReflect;
var bones = this.bones.Items;
for (int i = 0, n = this.bones.Count; i < n; i++) {
Bone bone = bones[i];
if (mixRotate != 0) {
float a = bone.a, b = bone.b, c = bone.c, d = bone.d;
float r = MathUtils.Atan2(tc, ta) - MathUtils.Atan2(c, a) + offsetRotation;
if (r > MathUtils.PI)
r -= MathUtils.PI2;
else if (r < -MathUtils.PI) //
r += MathUtils.PI2;
r *= mixRotate;
float 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;
}
if (translate) {
float tx, ty; //Vector2 temp = this.temp;
target.LocalToWorld(data.offsetX, data.offsetY, out tx, out ty); //target.localToWorld(temp.set(data.offsetX, data.offsetY));
bone.worldX += (tx - bone.worldX) * mixX;
bone.worldY += (ty - bone.worldY) * mixY;
}
if (mixScaleX != 0) {
float s = (float)Math.Sqrt(bone.a * bone.a + bone.c * bone.c);
if (s != 0) s = (s + ((float)Math.Sqrt(ta * ta + tc * tc) - s + data.offsetScaleX) * mixScaleX) / s;
bone.a *= s;
bone.c *= s;
}
if (mixScaleY != 0) {
float s = (float)Math.Sqrt(bone.b * bone.b + bone.d * bone.d);
if (s != 0) s = (s + ((float)Math.Sqrt(tb * tb + td * td) - s + data.offsetScaleY) * mixScaleY) / s;
bone.b *= s;
bone.d *= s;
}
if (mixShearY > 0) {
float b = bone.b, d = bone.d;
float by = MathUtils.Atan2(d, b);
float r = MathUtils.Atan2(td, tb) - MathUtils.Atan2(tc, ta) - (by - MathUtils.Atan2(bone.c, bone.a));
if (r > MathUtils.PI)
r -= MathUtils.PI2;
else if (r < -MathUtils.PI) //
r += MathUtils.PI2;
r = by + (r + offsetShearY) * mixShearY;
float s = (float)Math.Sqrt(b * b + d * d);
bone.b = MathUtils.Cos(r) * s;
bone.d = MathUtils.Sin(r) * s;
}
bone.UpdateAppliedTransform();
}
}
void ApplyRelativeWorld () {
float mixRotate = this.mixRotate, mixX = this.mixX, mixY = this.mixY, mixScaleX = this.mixScaleX,
mixScaleY = this.mixScaleY, mixShearY = this.mixShearY;
bool translate = mixX != 0 || mixY != 0;
Bone target = this.target;
float ta = target.a, tb = target.b, tc = target.c, td = target.d;
float degRadReflect = ta * td - tb * tc > 0 ? MathUtils.DegRad : -MathUtils.DegRad;
float offsetRotation = data.offsetRotation * degRadReflect, offsetShearY = data.offsetShearY * degRadReflect;
var bones = this.bones.Items;
for (int i = 0, n = this.bones.Count; i < n; i++) {
Bone bone = bones[i];
if (mixRotate != 0) {
float a = bone.a, b = bone.b, c = bone.c, d = bone.d;
float r = MathUtils.Atan2(tc, ta) + offsetRotation;
if (r > MathUtils.PI)
r -= MathUtils.PI2;
else if (r < -MathUtils.PI) //
r += MathUtils.PI2;
r *= mixRotate;
float 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;
}
if (translate) {
float tx, ty; //Vector2 temp = this.temp;
target.LocalToWorld(data.offsetX, data.offsetY, out tx, out ty); //target.localToWorld(temp.set(data.offsetX, data.offsetY));
bone.worldX += tx * mixX;
bone.worldY += ty * mixY;
}
if (mixScaleX != 0) {
float s = ((float)Math.Sqrt(ta * ta + tc * tc) - 1 + data.offsetScaleX) * mixScaleX + 1;
bone.a *= s;
bone.c *= s;
}
if (mixScaleY != 0) {
float s = ((float)Math.Sqrt(tb * tb + td * td) - 1 + data.offsetScaleY) * mixScaleY + 1;
bone.b *= s;
bone.d *= s;
}
if (mixShearY > 0) {
float r = MathUtils.Atan2(td, tb) - MathUtils.Atan2(tc, ta);
if (r > MathUtils.PI)
r -= MathUtils.PI2;
else if (r < -MathUtils.PI) //
r += MathUtils.PI2;
float b = bone.b, d = bone.d;
r = MathUtils.Atan2(d, b) + (r - MathUtils.PI / 2 + offsetShearY) * mixShearY;
float s = (float)Math.Sqrt(b * b + d * d);
bone.b = MathUtils.Cos(r) * s;
bone.d = MathUtils.Sin(r) * s;
}
bone.UpdateAppliedTransform();
}
}
void ApplyAbsoluteLocal () {
float mixRotate = this.mixRotate, mixX = this.mixX, mixY = this.mixY, mixScaleX = this.mixScaleX,
mixScaleY = this.mixScaleY, mixShearY = this.mixShearY;
Bone target = this.target;
var bones = this.bones.Items;
for (int i = 0, n = this.bones.Count; i < n; i++) {
Bone bone = bones[i];
float rotation = bone.arotation;
if (mixRotate != 0) {
float r = target.arotation - rotation + data.offsetRotation;
r -= (16384 - (int)(16384.499999999996 - r / 360)) * 360;
rotation += r * mixRotate;
}
float x = bone.ax, y = bone.ay;
x += (target.ax - x + data.offsetX) * mixX;
y += (target.ay - y + data.offsetY) * mixY;
float scaleX = bone.ascaleX, scaleY = bone.ascaleY;
if (mixScaleX != 0 && scaleX != 0)
scaleX = (scaleX + (target.ascaleX - scaleX + data.offsetScaleX) * mixScaleX) / scaleX;
if (mixScaleY != 0 && scaleY != 0)
scaleY = (scaleY + (target.ascaleY - scaleY + data.offsetScaleY) * mixScaleY) / scaleY;
float shearY = bone.ashearY;
if (mixShearY != 0) {
float r = target.ashearY - shearY + data.offsetShearY;
r -= (16384 - (int)(16384.499999999996 - r / 360)) * 360;
shearY += r * mixShearY;
}
bone.UpdateWorldTransform(x, y, rotation, scaleX, scaleY, bone.ashearX, shearY);
}
}
void ApplyRelativeLocal () {
float mixRotate = this.mixRotate, mixX = this.mixX, mixY = this.mixY, mixScaleX = this.mixScaleX,
mixScaleY = this.mixScaleY, mixShearY = this.mixShearY;
Bone target = this.target;
var bones = this.bones.Items;
for (int i = 0, n = this.bones.Count; i < n; i++) {
Bone bone = bones[i];
float rotation = bone.arotation + (target.arotation + data.offsetRotation) * mixRotate;
float x = bone.ax + (target.ax + data.offsetX) * mixX;
float y = bone.ay + (target.ay + data.offsetY) * mixY;
float scaleX = bone.ascaleX * (((target.ascaleX - 1 + data.offsetScaleX) * mixScaleX) + 1);
float scaleY = bone.ascaleY * (((target.ascaleY - 1 + data.offsetScaleY) * mixScaleY) + 1);
float shearY = bone.ashearY + (target.ashearY + data.offsetShearY) * mixShearY;
bone.UpdateWorldTransform(x, y, rotation, scaleX, scaleY, bone.ashearX, shearY);
}
}
/// <summary>The bones that will be modified by this transform constraint.</summary>
public ExposedList<Bone> Bones { get { return bones; } }
/// <summary>The target bone whose world transform will be copied to the constrained bones.</summary>
public Bone Target { get { return target; } set { target = value; } }
/// <summary>A percentage (0-1) that controls the mix between the constrained and unconstrained rotation.</summary>
public float MixRotate { get { return mixRotate; } set { mixRotate = value; } }
/// <summary>A percentage (0-1) that controls the mix between the constrained and unconstrained translation X.</summary>
public float MixX { get { return mixX; } set { mixX = value; } }
/// <summary>A percentage (0-1) that controls the mix between the constrained and unconstrained translation Y.</summary>
public float MixY { get { return mixY; } set { mixY = value; } }
/// <summary>A percentage (0-1) that controls the mix between the constrained and unconstrained scale X.</summary>
public float MixScaleX { get { return mixScaleX; } set { mixScaleX = value; } }
/// <summary>A percentage (0-1) that controls the mix between the constrained and unconstrained scale Y.</summary>
public float MixScaleY { get { return mixScaleY; } set { mixScaleY = value; } }
/// <summary>A percentage (0-1) that controls the mix between the constrained and unconstrained shear Y.</summary>
public float MixShearY { get { return mixShearY; } set { mixShearY = value; } }
public bool Active { get { return active; } }
/// <summary>The transform constraint's setup pose data.</summary>
public TransformConstraintData Data { get { return data; } }
override public string ToString () {
return data.name;
}
}
}

View File

@@ -1,68 +1,68 @@
/******************************************************************************
* 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 TransformConstraintData : ConstraintData {
internal ExposedList<BoneData> bones = new ExposedList<BoneData>();
internal BoneData target;
internal float mixRotate, mixX, mixY, mixScaleX, mixScaleY, mixShearY;
internal float offsetRotation, offsetX, offsetY, offsetScaleX, offsetScaleY, offsetShearY;
internal bool relative, local;
public ExposedList<BoneData> Bones { get { return bones; } }
public BoneData Target { get { return target; } set { target = value; } }
/// <summary>A percentage (0-1) that controls the mix between the constrained and unconstrained rotation.</summary>
public float MixRotate { get { return mixRotate; } set { mixRotate = value; } }
/// <summary>A percentage (0-1) that controls the mix between the constrained and unconstrained translation X.</summary>
public float MixX { get { return mixX; } set { mixX = value; } }
/// <summary>A percentage (0-1) that controls the mix between the constrained and unconstrained translation Y.</summary>
public float MixY { get { return mixY; } set { mixY = value; } }
/// <summary>A percentage (0-1) that controls the mix between the constrained and unconstrained scale X.</summary>
public float MixScaleX { get { return mixScaleX; } set { mixScaleX = value; } }
/// <summary>A percentage (0-1) that controls the mix between the constrained and unconstrained scale Y.</summary>
public float MixScaleY { get { return mixScaleY; } set { mixScaleY = value; } }
/// <summary>A percentage (0-1) that controls the mix between the constrained and unconstrained shear Y.</summary>
public float MixShearY { get { return mixShearY; } set { mixShearY = value; } }
public float OffsetRotation { get { return offsetRotation; } set { offsetRotation = value; } }
public float OffsetX { get { return offsetX; } set { offsetX = value; } }
public float OffsetY { get { return offsetY; } set { offsetY = value; } }
public float OffsetScaleX { get { return offsetScaleX; } set { offsetScaleX = value; } }
public float OffsetScaleY { get { return offsetScaleY; } set { offsetScaleY = value; } }
public float OffsetShearY { get { return offsetShearY; } set { offsetShearY = value; } }
public bool Relative { get { return relative; } set { relative = value; } }
public bool Local { get { return local; } set { local = value; } }
public TransformConstraintData (string name) : base(name) {
}
}
}
/******************************************************************************
* 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 TransformConstraintData : ConstraintData {
internal ExposedList<BoneData> bones = new ExposedList<BoneData>();
internal BoneData target;
internal float mixRotate, mixX, mixY, mixScaleX, mixScaleY, mixShearY;
internal float offsetRotation, offsetX, offsetY, offsetScaleX, offsetScaleY, offsetShearY;
internal bool relative, local;
public ExposedList<BoneData> Bones { get { return bones; } }
public BoneData Target { get { return target; } set { target = value; } }
/// <summary>A percentage (0-1) that controls the mix between the constrained and unconstrained rotation.</summary>
public float MixRotate { get { return mixRotate; } set { mixRotate = value; } }
/// <summary>A percentage (0-1) that controls the mix between the constrained and unconstrained translation X.</summary>
public float MixX { get { return mixX; } set { mixX = value; } }
/// <summary>A percentage (0-1) that controls the mix between the constrained and unconstrained translation Y.</summary>
public float MixY { get { return mixY; } set { mixY = value; } }
/// <summary>A percentage (0-1) that controls the mix between the constrained and unconstrained scale X.</summary>
public float MixScaleX { get { return mixScaleX; } set { mixScaleX = value; } }
/// <summary>A percentage (0-1) that controls the mix between the constrained and unconstrained scale Y.</summary>
public float MixScaleY { get { return mixScaleY; } set { mixScaleY = value; } }
/// <summary>A percentage (0-1) that controls the mix between the constrained and unconstrained shear Y.</summary>
public float MixShearY { get { return mixShearY; } set { mixShearY = value; } }
public float OffsetRotation { get { return offsetRotation; } set { offsetRotation = value; } }
public float OffsetX { get { return offsetX; } set { offsetX = value; } }
public float OffsetY { get { return offsetY; } set { offsetY = value; } }
public float OffsetScaleX { get { return offsetScaleX; } set { offsetScaleX = value; } }
public float OffsetScaleY { get { return offsetScaleY; } set { offsetScaleY = value; } }
public float OffsetShearY { get { return offsetShearY; } set { offsetShearY = value; } }
public bool Relative { get { return relative; } set { relative = value; } }
public bool Local { get { return local; } set { local = value; } }
public TransformConstraintData (string name) : base(name) {
}
}
}

View File

@@ -1,275 +1,275 @@
/******************************************************************************
* 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 Triangulator {
private readonly ExposedList<ExposedList<float>> convexPolygons = new ExposedList<ExposedList<float>>();
private readonly ExposedList<ExposedList<int>> convexPolygonsIndices = new ExposedList<ExposedList<int>>();
private readonly ExposedList<int> indicesArray = new ExposedList<int>();
private readonly ExposedList<bool> isConcaveArray = new ExposedList<bool>();
private readonly ExposedList<int> triangles = new ExposedList<int>();
private readonly Pool<ExposedList<float>> polygonPool = new Pool<ExposedList<float>>();
private readonly Pool<ExposedList<int>> polygonIndicesPool = new Pool<ExposedList<int>>();
public ExposedList<int> Triangulate (ExposedList<float> verticesArray) {
var vertices = verticesArray.Items;
int vertexCount = verticesArray.Count >> 1;
var indicesArray = this.indicesArray;
indicesArray.Clear();
int[] indices = indicesArray.Resize(vertexCount).Items;
for (int i = 0; i < vertexCount; i++)
indices[i] = i;
var isConcaveArray = this.isConcaveArray;
bool[] isConcave = isConcaveArray.Resize(vertexCount).Items;
for (int i = 0, n = vertexCount; i < n; ++i)
isConcave[i] = IsConcave(i, vertexCount, vertices, indices);
var triangles = this.triangles;
triangles.Clear();
triangles.EnsureCapacity(Math.Max(0, vertexCount - 2) << 2);
while (vertexCount > 3) {
// Find ear tip.
int previous = vertexCount - 1, i = 0, next = 1;
// outer:
while (true) {
if (!isConcave[i]) {
int p1 = indices[previous] << 1, p2 = indices[i] << 1, p3 = indices[next] << 1;
float p1x = vertices[p1], p1y = vertices[p1 + 1];
float p2x = vertices[p2], p2y = vertices[p2 + 1];
float p3x = vertices[p3], p3y = vertices[p3 + 1];
for (int ii = (next + 1) % vertexCount; ii != previous; ii = (ii + 1) % vertexCount) {
if (!isConcave[ii]) continue;
int v = indices[ii] << 1;
float vx = vertices[v], vy = vertices[v + 1];
if (PositiveArea(p3x, p3y, p1x, p1y, vx, vy)) {
if (PositiveArea(p1x, p1y, p2x, p2y, vx, vy)) {
if (PositiveArea(p2x, p2y, p3x, p3y, vx, vy)) goto break_outer; // break outer;
}
}
}
break;
}
break_outer:
if (next == 0) {
do {
if (!isConcave[i]) break;
i--;
} while (i > 0);
break;
}
previous = i;
i = next;
next = (next + 1) % vertexCount;
}
// Cut ear tip.
triangles.Add(indices[(vertexCount + i - 1) % vertexCount]);
triangles.Add(indices[i]);
triangles.Add(indices[(i + 1) % vertexCount]);
indicesArray.RemoveAt(i);
isConcaveArray.RemoveAt(i);
vertexCount--;
int previousIndex = (vertexCount + i - 1) % vertexCount;
int nextIndex = i == vertexCount ? 0 : i;
isConcave[previousIndex] = IsConcave(previousIndex, vertexCount, vertices, indices);
isConcave[nextIndex] = IsConcave(nextIndex, vertexCount, vertices, indices);
}
if (vertexCount == 3) {
triangles.Add(indices[2]);
triangles.Add(indices[0]);
triangles.Add(indices[1]);
}
return triangles;
}
public ExposedList<ExposedList<float>> Decompose (ExposedList<float> verticesArray, ExposedList<int> triangles) {
var vertices = verticesArray.Items;
var convexPolygons = this.convexPolygons;
for (int i = 0, n = convexPolygons.Count; i < n; i++)
polygonPool.Free(convexPolygons.Items[i]);
convexPolygons.Clear();
var convexPolygonsIndices = this.convexPolygonsIndices;
for (int i = 0, n = convexPolygonsIndices.Count; i < n; i++)
polygonIndicesPool.Free(convexPolygonsIndices.Items[i]);
convexPolygonsIndices.Clear();
var polygonIndices = polygonIndicesPool.Obtain();
polygonIndices.Clear();
var polygon = polygonPool.Obtain();
polygon.Clear();
// Merge subsequent triangles if they form a triangle fan.
int fanBaseIndex = -1, lastWinding = 0;
int[] trianglesItems = triangles.Items;
for (int i = 0, n = triangles.Count; i < n; i += 3) {
int t1 = trianglesItems[i] << 1, t2 = trianglesItems[i + 1] << 1, t3 = trianglesItems[i + 2] << 1;
float x1 = vertices[t1], y1 = vertices[t1 + 1];
float x2 = vertices[t2], y2 = vertices[t2 + 1];
float x3 = vertices[t3], y3 = vertices[t3 + 1];
// If the base of the last triangle is the same as this triangle, check if they form a convex polygon (triangle fan).
var merged = false;
if (fanBaseIndex == t1) {
int o = polygon.Count - 4;
float[] p = polygon.Items;
int winding1 = Winding(p[o], p[o + 1], p[o + 2], p[o + 3], x3, y3);
int winding2 = Winding(x3, y3, p[0], p[1], p[2], p[3]);
if (winding1 == lastWinding && winding2 == lastWinding) {
polygon.Add(x3);
polygon.Add(y3);
polygonIndices.Add(t3);
merged = true;
}
}
// Otherwise make this triangle the new base.
if (!merged) {
if (polygon.Count > 0) {
convexPolygons.Add(polygon);
convexPolygonsIndices.Add(polygonIndices);
} else {
polygonPool.Free(polygon);
polygonIndicesPool.Free(polygonIndices);
}
polygon = polygonPool.Obtain();
polygon.Clear();
polygon.Add(x1);
polygon.Add(y1);
polygon.Add(x2);
polygon.Add(y2);
polygon.Add(x3);
polygon.Add(y3);
polygonIndices = polygonIndicesPool.Obtain();
polygonIndices.Clear();
polygonIndices.Add(t1);
polygonIndices.Add(t2);
polygonIndices.Add(t3);
lastWinding = Winding(x1, y1, x2, y2, x3, y3);
fanBaseIndex = t1;
}
}
if (polygon.Count > 0) {
convexPolygons.Add(polygon);
convexPolygonsIndices.Add(polygonIndices);
}
// Go through the list of polygons and try to merge the remaining triangles with the found triangle fans.
for (int i = 0, n = convexPolygons.Count; i < n; i++) {
polygonIndices = convexPolygonsIndices.Items[i];
if (polygonIndices.Count == 0) continue;
int firstIndex = polygonIndices.Items[0];
int lastIndex = polygonIndices.Items[polygonIndices.Count - 1];
polygon = convexPolygons.Items[i];
int o = polygon.Count - 4;
float[] p = polygon.Items;
float prevPrevX = p[o], prevPrevY = p[o + 1];
float prevX = p[o + 2], prevY = p[o + 3];
float firstX = p[0], firstY = p[1];
float secondX = p[2], secondY = p[3];
int winding = Winding(prevPrevX, prevPrevY, prevX, prevY, firstX, firstY);
for (int ii = 0; ii < n; ii++) {
if (ii == i) continue;
var otherIndices = convexPolygonsIndices.Items[ii];
if (otherIndices.Count != 3) continue;
int otherFirstIndex = otherIndices.Items[0];
int otherSecondIndex = otherIndices.Items[1];
int otherLastIndex = otherIndices.Items[2];
var otherPoly = convexPolygons.Items[ii];
float x3 = otherPoly.Items[otherPoly.Count - 2], y3 = otherPoly.Items[otherPoly.Count - 1];
if (otherFirstIndex != firstIndex || otherSecondIndex != lastIndex) continue;
int winding1 = Winding(prevPrevX, prevPrevY, prevX, prevY, x3, y3);
int winding2 = Winding(x3, y3, firstX, firstY, secondX, secondY);
if (winding1 == winding && winding2 == winding) {
otherPoly.Clear();
otherIndices.Clear();
polygon.Add(x3);
polygon.Add(y3);
polygonIndices.Add(otherLastIndex);
prevPrevX = prevX;
prevPrevY = prevY;
prevX = x3;
prevY = y3;
ii = 0;
}
}
}
// Remove empty polygons that resulted from the merge step above.
for (int i = convexPolygons.Count - 1; i >= 0; i--) {
polygon = convexPolygons.Items[i];
if (polygon.Count == 0) {
convexPolygons.RemoveAt(i);
polygonPool.Free(polygon);
polygonIndices = convexPolygonsIndices.Items[i];
convexPolygonsIndices.RemoveAt(i);
polygonIndicesPool.Free(polygonIndices);
}
}
return convexPolygons;
}
static private bool IsConcave (int index, int vertexCount, float[] vertices, int[] indices) {
int previous = indices[(vertexCount + index - 1) % vertexCount] << 1;
int current = indices[index] << 1;
int next = indices[(index + 1) % vertexCount] << 1;
return !PositiveArea(vertices[previous], vertices[previous + 1], vertices[current], vertices[current + 1], vertices[next],
vertices[next + 1]);
}
static private bool PositiveArea (float p1x, float p1y, float p2x, float p2y, float p3x, float p3y) {
return p1x * (p3y - p2y) + p2x * (p1y - p3y) + p3x * (p2y - p1y) >= 0;
}
static private int Winding (float p1x, float p1y, float p2x, float p2y, float p3x, float p3y) {
float px = p2x - p1x, py = p2y - p1y;
return p3x * py - p3y * px + px * p1y - p1x * py >= 0 ? 1 : -1;
}
}
}
/******************************************************************************
* 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 Triangulator {
private readonly ExposedList<ExposedList<float>> convexPolygons = new ExposedList<ExposedList<float>>();
private readonly ExposedList<ExposedList<int>> convexPolygonsIndices = new ExposedList<ExposedList<int>>();
private readonly ExposedList<int> indicesArray = new ExposedList<int>();
private readonly ExposedList<bool> isConcaveArray = new ExposedList<bool>();
private readonly ExposedList<int> triangles = new ExposedList<int>();
private readonly Pool<ExposedList<float>> polygonPool = new Pool<ExposedList<float>>();
private readonly Pool<ExposedList<int>> polygonIndicesPool = new Pool<ExposedList<int>>();
public ExposedList<int> Triangulate (ExposedList<float> verticesArray) {
var vertices = verticesArray.Items;
int vertexCount = verticesArray.Count >> 1;
var indicesArray = this.indicesArray;
indicesArray.Clear();
int[] indices = indicesArray.Resize(vertexCount).Items;
for (int i = 0; i < vertexCount; i++)
indices[i] = i;
var isConcaveArray = this.isConcaveArray;
bool[] isConcave = isConcaveArray.Resize(vertexCount).Items;
for (int i = 0, n = vertexCount; i < n; ++i)
isConcave[i] = IsConcave(i, vertexCount, vertices, indices);
var triangles = this.triangles;
triangles.Clear();
triangles.EnsureCapacity(Math.Max(0, vertexCount - 2) << 2);
while (vertexCount > 3) {
// Find ear tip.
int previous = vertexCount - 1, i = 0, next = 1;
// outer:
while (true) {
if (!isConcave[i]) {
int p1 = indices[previous] << 1, p2 = indices[i] << 1, p3 = indices[next] << 1;
float p1x = vertices[p1], p1y = vertices[p1 + 1];
float p2x = vertices[p2], p2y = vertices[p2 + 1];
float p3x = vertices[p3], p3y = vertices[p3 + 1];
for (int ii = (next + 1) % vertexCount; ii != previous; ii = (ii + 1) % vertexCount) {
if (!isConcave[ii]) continue;
int v = indices[ii] << 1;
float vx = vertices[v], vy = vertices[v + 1];
if (PositiveArea(p3x, p3y, p1x, p1y, vx, vy)) {
if (PositiveArea(p1x, p1y, p2x, p2y, vx, vy)) {
if (PositiveArea(p2x, p2y, p3x, p3y, vx, vy)) goto break_outer; // break outer;
}
}
}
break;
}
break_outer:
if (next == 0) {
do {
if (!isConcave[i]) break;
i--;
} while (i > 0);
break;
}
previous = i;
i = next;
next = (next + 1) % vertexCount;
}
// Cut ear tip.
triangles.Add(indices[(vertexCount + i - 1) % vertexCount]);
triangles.Add(indices[i]);
triangles.Add(indices[(i + 1) % vertexCount]);
indicesArray.RemoveAt(i);
isConcaveArray.RemoveAt(i);
vertexCount--;
int previousIndex = (vertexCount + i - 1) % vertexCount;
int nextIndex = i == vertexCount ? 0 : i;
isConcave[previousIndex] = IsConcave(previousIndex, vertexCount, vertices, indices);
isConcave[nextIndex] = IsConcave(nextIndex, vertexCount, vertices, indices);
}
if (vertexCount == 3) {
triangles.Add(indices[2]);
triangles.Add(indices[0]);
triangles.Add(indices[1]);
}
return triangles;
}
public ExposedList<ExposedList<float>> Decompose (ExposedList<float> verticesArray, ExposedList<int> triangles) {
var vertices = verticesArray.Items;
var convexPolygons = this.convexPolygons;
for (int i = 0, n = convexPolygons.Count; i < n; i++)
polygonPool.Free(convexPolygons.Items[i]);
convexPolygons.Clear();
var convexPolygonsIndices = this.convexPolygonsIndices;
for (int i = 0, n = convexPolygonsIndices.Count; i < n; i++)
polygonIndicesPool.Free(convexPolygonsIndices.Items[i]);
convexPolygonsIndices.Clear();
var polygonIndices = polygonIndicesPool.Obtain();
polygonIndices.Clear();
var polygon = polygonPool.Obtain();
polygon.Clear();
// Merge subsequent triangles if they form a triangle fan.
int fanBaseIndex = -1, lastWinding = 0;
int[] trianglesItems = triangles.Items;
for (int i = 0, n = triangles.Count; i < n; i += 3) {
int t1 = trianglesItems[i] << 1, t2 = trianglesItems[i + 1] << 1, t3 = trianglesItems[i + 2] << 1;
float x1 = vertices[t1], y1 = vertices[t1 + 1];
float x2 = vertices[t2], y2 = vertices[t2 + 1];
float x3 = vertices[t3], y3 = vertices[t3 + 1];
// If the base of the last triangle is the same as this triangle, check if they form a convex polygon (triangle fan).
var merged = false;
if (fanBaseIndex == t1) {
int o = polygon.Count - 4;
float[] p = polygon.Items;
int winding1 = Winding(p[o], p[o + 1], p[o + 2], p[o + 3], x3, y3);
int winding2 = Winding(x3, y3, p[0], p[1], p[2], p[3]);
if (winding1 == lastWinding && winding2 == lastWinding) {
polygon.Add(x3);
polygon.Add(y3);
polygonIndices.Add(t3);
merged = true;
}
}
// Otherwise make this triangle the new base.
if (!merged) {
if (polygon.Count > 0) {
convexPolygons.Add(polygon);
convexPolygonsIndices.Add(polygonIndices);
} else {
polygonPool.Free(polygon);
polygonIndicesPool.Free(polygonIndices);
}
polygon = polygonPool.Obtain();
polygon.Clear();
polygon.Add(x1);
polygon.Add(y1);
polygon.Add(x2);
polygon.Add(y2);
polygon.Add(x3);
polygon.Add(y3);
polygonIndices = polygonIndicesPool.Obtain();
polygonIndices.Clear();
polygonIndices.Add(t1);
polygonIndices.Add(t2);
polygonIndices.Add(t3);
lastWinding = Winding(x1, y1, x2, y2, x3, y3);
fanBaseIndex = t1;
}
}
if (polygon.Count > 0) {
convexPolygons.Add(polygon);
convexPolygonsIndices.Add(polygonIndices);
}
// Go through the list of polygons and try to merge the remaining triangles with the found triangle fans.
for (int i = 0, n = convexPolygons.Count; i < n; i++) {
polygonIndices = convexPolygonsIndices.Items[i];
if (polygonIndices.Count == 0) continue;
int firstIndex = polygonIndices.Items[0];
int lastIndex = polygonIndices.Items[polygonIndices.Count - 1];
polygon = convexPolygons.Items[i];
int o = polygon.Count - 4;
float[] p = polygon.Items;
float prevPrevX = p[o], prevPrevY = p[o + 1];
float prevX = p[o + 2], prevY = p[o + 3];
float firstX = p[0], firstY = p[1];
float secondX = p[2], secondY = p[3];
int winding = Winding(prevPrevX, prevPrevY, prevX, prevY, firstX, firstY);
for (int ii = 0; ii < n; ii++) {
if (ii == i) continue;
var otherIndices = convexPolygonsIndices.Items[ii];
if (otherIndices.Count != 3) continue;
int otherFirstIndex = otherIndices.Items[0];
int otherSecondIndex = otherIndices.Items[1];
int otherLastIndex = otherIndices.Items[2];
var otherPoly = convexPolygons.Items[ii];
float x3 = otherPoly.Items[otherPoly.Count - 2], y3 = otherPoly.Items[otherPoly.Count - 1];
if (otherFirstIndex != firstIndex || otherSecondIndex != lastIndex) continue;
int winding1 = Winding(prevPrevX, prevPrevY, prevX, prevY, x3, y3);
int winding2 = Winding(x3, y3, firstX, firstY, secondX, secondY);
if (winding1 == winding && winding2 == winding) {
otherPoly.Clear();
otherIndices.Clear();
polygon.Add(x3);
polygon.Add(y3);
polygonIndices.Add(otherLastIndex);
prevPrevX = prevX;
prevPrevY = prevY;
prevX = x3;
prevY = y3;
ii = 0;
}
}
}
// Remove empty polygons that resulted from the merge step above.
for (int i = convexPolygons.Count - 1; i >= 0; i--) {
polygon = convexPolygons.Items[i];
if (polygon.Count == 0) {
convexPolygons.RemoveAt(i);
polygonPool.Free(polygon);
polygonIndices = convexPolygonsIndices.Items[i];
convexPolygonsIndices.RemoveAt(i);
polygonIndicesPool.Free(polygonIndices);
}
}
return convexPolygons;
}
static private bool IsConcave (int index, int vertexCount, float[] vertices, int[] indices) {
int previous = indices[(vertexCount + index - 1) % vertexCount] << 1;
int current = indices[index] << 1;
int next = indices[(index + 1) % vertexCount] << 1;
return !PositiveArea(vertices[previous], vertices[previous + 1], vertices[current], vertices[current + 1], vertices[next],
vertices[next + 1]);
}
static private bool PositiveArea (float p1x, float p1y, float p2x, float p2y, float p3x, float p3y) {
return p1x * (p3y - p2y) + p2x * (p1y - p3y) + p3x * (p2y - p1y) >= 0;
}
static private int Winding (float p1x, float p1y, float p2x, float p2y, float p3x, float p3y) {
float px = p2x - p1x, py = p2y - p1y;
return p3x * py - p3y * px + px * p1y - p1x * py >= 0 ? 1 : -1;
}
}
}

View File

@@ -1,31 +1,31 @@
{
"name": "com.esotericsoftware.spine.spine-csharp",
"displayName": "spine-csharp Runtime",
"description": "This plugin provides the spine-csharp core runtime.",
"version": "4.0.0",
"unity": "2018.3",
"author": {
"name": "Esoteric Software",
"email": "contact@esotericsoftware.com",
"url": "http://esotericsoftware.com/"
},
"dependencies": {
},
"repository": {
"type": "git",
"url": "git@github.com:EsotericSoftware/spine-runtimes.git"
},
"keywords": [
"spine",
"spine-csharp",
"runtimes",
"2d",
"skeletal",
"animation"
],
"license": "SEE LICENSE IN LICENSE",
"bugs": {
"url": "https://github.com/EsotericSoftware/spine-runtimes/issues"
},
"homepage": "https://github.com/EsotericSoftware/spine-runtimes#readme"
}
{
"name": "com.esotericsoftware.spine.spine-csharp",
"displayName": "spine-csharp Runtime",
"description": "This plugin provides the spine-csharp core runtime.",
"version": "4.0.0",
"unity": "2018.3",
"author": {
"name": "Esoteric Software",
"email": "contact@esotericsoftware.com",
"url": "http://esotericsoftware.com/"
},
"dependencies": {
},
"repository": {
"type": "git",
"url": "git@github.com:EsotericSoftware/spine-runtimes.git"
},
"keywords": [
"spine",
"spine-csharp",
"runtimes",
"2d",
"skeletal",
"animation"
],
"license": "SEE LICENSE IN LICENSE",
"bugs": {
"url": "https://github.com/EsotericSoftware/spine-runtimes/issues"
},
"homepage": "https://github.com/EsotericSoftware/spine-runtimes#readme"
}

View File

@@ -1,4 +1,4 @@
{
"name": "spine-csharp",
"references": []
}
{
"name": "spine-csharp",
"references": []
}