Initial commit: Client Doc docs Server Tools
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fb7099b9c6ce91740b7041dabb0752c2
|
||||
timeCreated: 1456265156
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ec2f0e7143c8a174994595883f4b1e33
|
||||
timeCreated: 1456265155
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,114 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated April 5, 2025. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2026, 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 <see cref="AnimationState"/> animations are changed on the same track.</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.");
|
||||
var key = new AnimationPair(from, to);
|
||||
animationToMixTime.Remove(key);
|
||||
animationToMixTime.Add(key, duration);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the mix duration to use when changing from the specified animation to the other on the same track,
|
||||
/// or the <see cref="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.");
|
||||
var 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e03d60c517d9b974db35b9fd144a1d09
|
||||
timeCreated: 1456265155
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,372 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated April 5, 2025. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2026, 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 (StreamReader 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;
|
||||
|
||||
Dictionary<string, Action> 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";
|
||||
});
|
||||
|
||||
Dictionary<string, Action> 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;
|
||||
|
||||
int tempSwap = region.packedWidth;
|
||||
region.packedWidth = region.packedHeight;
|
||||
region.packedHeight = tempSwap;
|
||||
} 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 : TextureRegion {
|
||||
public AtlasPage page;
|
||||
public string name;
|
||||
public int x, y;
|
||||
public float offsetX, offsetY;
|
||||
public int originalWidth, originalHeight;
|
||||
public int packedWidth { get { return width; } set { width = value; } }
|
||||
public int packedHeight { get { return height; } set { height = value; } }
|
||||
public int degrees;
|
||||
public bool rotate;
|
||||
public int index;
|
||||
public string[] names;
|
||||
public int[][] values;
|
||||
|
||||
override public int OriginalWidth { get { return originalWidth; } }
|
||||
override public int OriginalHeight { get { return originalHeight; } }
|
||||
|
||||
public AtlasRegion Clone () {
|
||||
return MemberwiseClone() as AtlasRegion;
|
||||
}
|
||||
}
|
||||
|
||||
public interface TextureLoader {
|
||||
void Load (AtlasPage page, string path);
|
||||
void Unload (Object texture);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 60626307629cc034bafd42c53a901fff
|
||||
timeCreated: 1456265154
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2afe1c6b912aac54abb5925ca4ac52c2
|
||||
folderAsset: yes
|
||||
timeCreated: 1456265152
|
||||
licenseType: Free
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,102 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated April 5, 2025. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2026, 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 <see cref="AttachmentLoader"/> that configures attachments using texture regions from an <see cref="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;
|
||||
/// <summary>If true, <see cref="FindRegion(string, string)"/> may return null. If false, an error is raised if the texture region is not
|
||||
/// found. Default is false.</summary>
|
||||
public bool allowMissingRegions;
|
||||
|
||||
public AtlasAttachmentLoader (params Atlas[] atlasArray)
|
||||
: this(false, atlasArray) {
|
||||
}
|
||||
|
||||
public AtlasAttachmentLoader (bool allowMissingRegions, params Atlas[] atlasArray) {
|
||||
if (atlasArray == null) throw new ArgumentNullException("atlas", "atlas array cannot be null.");
|
||||
this.atlasArray = atlasArray;
|
||||
this.allowMissingRegions = allowMissingRegions;
|
||||
}
|
||||
|
||||
/// <summary>Sets each <see cref="Sequence.Regions"/> by calling <see cref="FindRegion(string, string)"/> for each texture region using
|
||||
/// <see cref="Sequence.GetPath(string, int)"/>.</summary>
|
||||
protected void FindRegions (string name, string basePath, Sequence sequence) {
|
||||
TextureRegion[] regions = sequence.Regions;
|
||||
for (int i = 0, n = regions.Length; i < n; i++) {
|
||||
regions[i] = FindRegion(name, sequence.GetPath(basePath, i));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Looks for the region with the specified path. If not found and <see cref="allowMissingRegions"/> is false, an error is
|
||||
/// raised.</summary>
|
||||
protected AtlasRegion FindRegion (string name, string path) {
|
||||
for (int i = 0; i < atlasArray.Length; i++) {
|
||||
AtlasRegion region = atlasArray[i].FindRegion(path);
|
||||
if (region != null)
|
||||
return region;
|
||||
}
|
||||
if (!allowMissingRegions)
|
||||
throw new ArgumentException(string.Format("Region not found in atlas: {0} (attachment: {1})", path, name));
|
||||
return null;
|
||||
}
|
||||
|
||||
public RegionAttachment NewRegionAttachment (Skin skin, string placeholder, string name, string path, Sequence sequence) {
|
||||
FindRegions(name, path, sequence);
|
||||
return new RegionAttachment(name, sequence);
|
||||
}
|
||||
|
||||
public MeshAttachment NewMeshAttachment (Skin skin, string placeholder, string name, string path, Sequence sequence) {
|
||||
FindRegions(name, path, sequence);
|
||||
return new MeshAttachment(name, sequence);
|
||||
}
|
||||
|
||||
public BoundingBoxAttachment NewBoundingBoxAttachment (Skin skin, string placeholder, string name) {
|
||||
return new BoundingBoxAttachment(name);
|
||||
}
|
||||
|
||||
public PathAttachment NewPathAttachment (Skin skin, string placeholder, string name) {
|
||||
return new PathAttachment(name);
|
||||
}
|
||||
|
||||
public PointAttachment NewPointAttachment (Skin skin, string placeholder, string name) {
|
||||
return new PointAttachment(name);
|
||||
}
|
||||
|
||||
public ClippingAttachment NewClippingAttachment (Skin skin, string placeholder, string name) {
|
||||
return new ClippingAttachment(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3e6ff30e27c28344bad3e67d308c94cd
|
||||
timeCreated: 1466772712
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,95 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated April 5, 2025. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2026, 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>The base class for all attachments. Multiple <see cref="Skeleton"/> instances, slots, or skins can use the same
|
||||
/// attachments.</summary>
|
||||
abstract public class Attachment {
|
||||
static private readonly int[] Empty = new int[0];
|
||||
|
||||
internal Attachment timelineAttachment;
|
||||
internal int[] timelineSlots = Empty;
|
||||
|
||||
/// <summary>The attachment's name.</summary>
|
||||
public string Name { get; }
|
||||
|
||||
/// <summary>Timelines for the timeline attachment are also applied to this attachment.
|
||||
/// May be null if no attachment-specific timelines should be applied.</summary>
|
||||
public Attachment TimelineAttachment { get { return timelineAttachment; } set { timelineAttachment = value; } }
|
||||
|
||||
/// <summary>
|
||||
/// Slots that can have attachments whose <see cref="timelineAttachment"/> is this attachment.
|
||||
/// </summary>
|
||||
public int[] TimelineSlots { get { return timelineSlots; } set { timelineSlots = value; } }
|
||||
|
||||
protected Attachment (string name) {
|
||||
if (name == null) throw new ArgumentNullException("name", "name cannot be null");
|
||||
this.Name = name;
|
||||
timelineAttachment = this;
|
||||
}
|
||||
|
||||
/// <summary>Copy constructor.</summary>
|
||||
protected Attachment (Attachment other) {
|
||||
Name = other.Name;
|
||||
timelineAttachment = other.timelineAttachment;
|
||||
timelineSlots = other.timelineSlots;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the <c>slotIndex</c> or any <see cref="timelineSlots"/> have an attachment whose <see cref="timelineAttachment"/> is
|
||||
/// this attachment.
|
||||
/// </summary>
|
||||
/// <param name="slots">The <see cref="Skeleton.Slots"/>.</param>
|
||||
/// <param name="slotIndex">The timeline's primary slot index.</param>
|
||||
public bool IsTimelineActive (Slot[] slots, int slotIndex, bool appliedPose) {
|
||||
Slot slot = slots[slotIndex];
|
||||
if (slot.Bone.Active) {
|
||||
Attachment other = (appliedPose ? slot.AppliedPose : slot.Pose).Attachment;
|
||||
if (other != null && other.timelineAttachment == this) return true;
|
||||
}
|
||||
for (int i = 0, n = timelineSlots.Length; i < n; i++) {
|
||||
slot = slots[timelineSlots[i]];
|
||||
if (!slot.Bone.Active) continue;
|
||||
Attachment other = (appliedPose ? slot.AppliedPose : slot.Pose).Attachment;
|
||||
if (other != null && other.timelineAttachment == this) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
override public string ToString () {
|
||||
return Name;
|
||||
}
|
||||
|
||||
/// <summary>Returns a copy of the attachment.</summary>
|
||||
public abstract Attachment Copy ();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 05b56321b2ddd8145a888746bc6ab917
|
||||
timeCreated: 1456265153
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,48 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated April 5, 2025. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2026, 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 placeholder, string name, string path, Sequence sequence);
|
||||
|
||||
/// <return>May be null to not load any attachment.</return>
|
||||
MeshAttachment NewMeshAttachment (Skin skin, string placeholder, string name, string path, Sequence sequence);
|
||||
|
||||
/// <return>May be null to not load any attachment.</return>
|
||||
BoundingBoxAttachment NewBoundingBoxAttachment (Skin skin, string placeholder, string name);
|
||||
|
||||
/// <returns>May be null to not load any attachment</returns>
|
||||
PathAttachment NewPathAttachment (Skin skin, string placeholder, string name);
|
||||
|
||||
PointAttachment NewPointAttachment (Skin skin, string placeholder, string name);
|
||||
|
||||
ClippingAttachment NewClippingAttachment (Skin skin, string placeholder, string name);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 95466a4f5a30dca4aa69e8ee7df8ae85
|
||||
timeCreated: 1466772712
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,34 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated April 5, 2025. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2026, 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, Sequence
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d6b1941960a9f6f47be3e865554d8695
|
||||
timeCreated: 1466772712
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,48 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated April 5, 2025. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2026, 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) {
|
||||
}
|
||||
|
||||
/// <summary>Copy constructor.</summary>
|
||||
protected BoundingBoxAttachment (BoundingBoxAttachment other)
|
||||
: base(other) {
|
||||
}
|
||||
|
||||
public override Attachment Copy () {
|
||||
return new BoundingBoxAttachment(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cd8ad8fc0f5bce448ba26d096ab32e85
|
||||
timeCreated: 1466772712
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,69 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated April 5, 2025. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2026, 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;
|
||||
internal bool convex, inverse;
|
||||
|
||||
/// <summary>Clipping is performed between the clipping attachment's slot and the end slot. If null, clipping is done until the end of
|
||||
/// the skeleton's rendering.</summary>
|
||||
public SlotData EndSlot { get { return endSlot; } set { endSlot = value; } }
|
||||
|
||||
/// <summary>
|
||||
/// When true the clipping polygon is treated as convex for more efficient clipping. If the polygon deforms to concave then the
|
||||
/// convex hull is used.When false the clipping polygon can be concave and if so has an additional CPU cost.Inverse clipping
|
||||
/// always uses convex.
|
||||
/// </summary>
|
||||
public bool Convex { get { return convex; } set { convex = value; } }
|
||||
|
||||
/// <summary>
|
||||
/// When false, everything inside the clipping polygon is visible. When true, everything outside the clipping polygon is
|
||||
/// visible and clipping is convex.
|
||||
/// </summary>
|
||||
public bool Inverse { get { return inverse; } set { inverse = value; } }
|
||||
|
||||
public ClippingAttachment (string name) : base(name) {
|
||||
}
|
||||
|
||||
/// <summary>Copy constructor.</summary>
|
||||
protected ClippingAttachment (ClippingAttachment other)
|
||||
: base(other) {
|
||||
endSlot = other.endSlot;
|
||||
convex = other.convex;
|
||||
inverse = other.inverse;
|
||||
}
|
||||
|
||||
public override Attachment Copy () {
|
||||
return new ClippingAttachment(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3380954b107f38b4c85a4cdfeceace42
|
||||
timeCreated: 1492744746
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,51 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated April 5, 2025. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2026, 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
|
||||
|
||||
namespace Spine {
|
||||
#if IS_UNITY
|
||||
using Color32F = UnityEngine.Color;
|
||||
#endif
|
||||
|
||||
/// <summary>Interface for an attachment that gets 1 or more texture regions from a <see cref="Sequence"/>.</summary>
|
||||
public interface IHasSequence {
|
||||
/// <summary>The base path for the attachment's texture region.</summary>
|
||||
string Path { get; set; }
|
||||
/// <summary>The color the attachment is tinted, to be combined with <see cref="SlotPose.Color"/>.</summary>
|
||||
Color32F GetColor ();
|
||||
void SetColor (Color32F color);
|
||||
void SetColor (float r, float g, float b, float a);
|
||||
/// <summary>The sequence that provides texture regions, UVs, and vertex offsets for rendering this attachment.</summary>
|
||||
Sequence Sequence { get; }
|
||||
void UpdateSequence ();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d0e8b0a33cae75d498aa8c328787cafb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,236 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated April 5, 2025. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2026, 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;
|
||||
|
||||
namespace Spine {
|
||||
#if IS_UNITY
|
||||
using Color32F = UnityEngine.Color;
|
||||
#endif
|
||||
|
||||
/// <summary>Attachment that displays a texture region using a mesh.</summary>
|
||||
public class MeshAttachment : VertexAttachment, IHasSequence {
|
||||
internal readonly Sequence sequence;
|
||||
internal float[] regionUVs;
|
||||
internal int[] triangles;
|
||||
internal int hullLength;
|
||||
internal string path;
|
||||
// Color is a struct, set to protected to prevent
|
||||
// Color color = slot.color; color.a = 0.5;
|
||||
// modifying just a copy of the struct instead of the original
|
||||
// object as in reference implementation.
|
||||
protected Color32F color = new Color32F(1, 1, 1, 1);
|
||||
private MeshAttachment sourceMesh;
|
||||
|
||||
public int HullLength { get { return hullLength; } set { hullLength = value; } }
|
||||
|
||||
/// <summary>The UV pair for each vertex, normalized within the texture region.</summary>
|
||||
public float[] RegionUVs { get { return regionUVs; } set { regionUVs = value; } }
|
||||
/// <summary>Triplets of vertex indices which describe the mesh's triangulation.</summary>
|
||||
public int[] Triangles { get { return triangles; } set { triangles = value; } }
|
||||
|
||||
public Color32F GetColor () {
|
||||
return color;
|
||||
}
|
||||
|
||||
public void SetColor (Color32F color) {
|
||||
this.color = color;
|
||||
}
|
||||
|
||||
public void SetColor (float r, float g, float b, float a) {
|
||||
color = new Color32F(r, g, b, a);
|
||||
}
|
||||
|
||||
public string Path { get { return path; } set { path = value; } }
|
||||
public Sequence Sequence { get { return sequence; } }
|
||||
|
||||
/// <summary>
|
||||
/// The source mesh if this is a linked mesh, else null. A linked mesh shares the
|
||||
/// <see cref="VertexAttachment.Bones">Bones</see>, <see cref="VertexAttachment.Vertices">Vertices</see>,
|
||||
/// <see cref="RegionUVs"/>, <see cref="Triangles"/>, <see cref="HullLength"/>, <see cref="Edges"/>,
|
||||
/// <see cref="Width"/>, <see cref="Height"/> with the
|
||||
/// source mesh, but may have a different <see cref="name"/> or <see cref="path"/>, and therefore a different texture region.
|
||||
/// </summary>
|
||||
public MeshAttachment SourceMesh {
|
||||
get { return sourceMesh; }
|
||||
set {
|
||||
sourceMesh = 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.
|
||||
/// <summary>
|
||||
/// Vertex index pairs describing edges for controlling triangulation, or null if nonessential data was not exported. Mesh
|
||||
/// triangles do not cross edges. Triangulation is not performed at runtime.
|
||||
/// </summary>
|
||||
public int[] Edges { get; set; }
|
||||
public float Width { get; set; }
|
||||
public float Height { get; set; }
|
||||
|
||||
public MeshAttachment (string name, Sequence sequence)
|
||||
: base(name) {
|
||||
if (sequence == null) throw new ArgumentException("sequence cannot be null.", "sequence");
|
||||
this.sequence = sequence;
|
||||
}
|
||||
|
||||
/// <summary>Copy constructor. Use <see cref="NewLinkedMesh"/> if the other mesh is a linked mesh.</summary>
|
||||
protected MeshAttachment (MeshAttachment other)
|
||||
: base(other) {
|
||||
|
||||
if (sourceMesh != null) throw new ArgumentException("Use newLinkedMesh to copy a linked mesh.");
|
||||
|
||||
path = other.path;
|
||||
color = other.color;
|
||||
|
||||
regionUVs = new float[other.regionUVs.Length];
|
||||
Array.Copy(other.regionUVs, 0, regionUVs, 0, regionUVs.Length);
|
||||
|
||||
triangles = new int[other.triangles.Length];
|
||||
Array.Copy(other.triangles, 0, triangles, 0, triangles.Length);
|
||||
|
||||
hullLength = other.hullLength;
|
||||
sequence = new Sequence(other.sequence);
|
||||
|
||||
// Nonessential.
|
||||
if (other.Edges != null) {
|
||||
Edges = new int[other.Edges.Length];
|
||||
Array.Copy(other.Edges, 0, Edges, 0, Edges.Length);
|
||||
}
|
||||
Width = other.Width;
|
||||
Height = other.Height;
|
||||
}
|
||||
|
||||
public void UpdateSequence () {
|
||||
sequence.Update(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a new mesh with the <see cref="SourceMesh"/> set to this mesh's source mesh, if any, else to this mesh.
|
||||
/// </summary>
|
||||
public MeshAttachment NewLinkedMesh () {
|
||||
var mesh = new MeshAttachment(Name, new Sequence(sequence));
|
||||
|
||||
mesh.timelineAttachment = timelineAttachment;
|
||||
mesh.path = path;
|
||||
mesh.color = color;
|
||||
mesh.SourceMesh = sourceMesh != null ? sourceMesh : this;
|
||||
mesh.UpdateSequence();
|
||||
return mesh;
|
||||
}
|
||||
|
||||
public override Attachment Copy () {
|
||||
return sourceMesh != null ? NewLinkedMesh() : new MeshAttachment(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes <see cref="Sequence.GetUVs(int)">UVs</see> for a mesh attachment.
|
||||
/// </summary>
|
||||
/// <param name="uvs">Output array for the computed UVs, same length as regionUVs.</param>
|
||||
internal static void ComputeUVs (TextureRegion region, float[] regionUVs, float[] uvs) {
|
||||
int n = uvs.Length;
|
||||
float u, v, width, height;
|
||||
AtlasRegion r = region as AtlasRegion;
|
||||
if (r != null) {
|
||||
u = r.u;
|
||||
v = r.v;
|
||||
float textureWidth = region.width / (region.u2 - region.u);
|
||||
float textureHeight = region.height / (region.v2 - region.v);
|
||||
switch (r.degrees) {
|
||||
case 90: {
|
||||
u -= (r.originalHeight - r.offsetY - r.packedWidth) / textureWidth;
|
||||
v -= (r.originalWidth - r.offsetX - r.packedHeight) / textureHeight;
|
||||
width = r.originalHeight / textureWidth;
|
||||
height = r.originalWidth / textureHeight;
|
||||
for (int i = 0; i < n; i += 2) {
|
||||
uvs[i] = u + regionUVs[i + 1] * width;
|
||||
uvs[i + 1] = v + (1 - regionUVs[i]) * height;
|
||||
}
|
||||
return;
|
||||
}
|
||||
case 180: {
|
||||
u -= (r.originalWidth - r.offsetX - r.packedWidth) / textureWidth;
|
||||
v -= r.offsetY / textureHeight;
|
||||
width = r.originalWidth / textureWidth;
|
||||
height = r.originalHeight / textureHeight;
|
||||
for (int i = 0; i < n; i += 2) {
|
||||
uvs[i] = u + (1 - regionUVs[i]) * width;
|
||||
uvs[i + 1] = v + (1 - regionUVs[i + 1]) * height;
|
||||
}
|
||||
return;
|
||||
}
|
||||
case 270: {
|
||||
u -= r.offsetY / textureWidth;
|
||||
v -= r.offsetX / textureHeight;
|
||||
width = r.originalHeight / textureWidth;
|
||||
height = r.originalWidth / textureHeight;
|
||||
for (int i = 0; i < n; i += 2) {
|
||||
uvs[i] = u + (1 - regionUVs[i + 1]) * width;
|
||||
uvs[i + 1] = v + regionUVs[i] * height;
|
||||
}
|
||||
return;
|
||||
}
|
||||
default: {
|
||||
u -= r.offsetX / textureWidth;
|
||||
v -= (r.originalHeight - r.offsetY - r.packedHeight) / textureHeight;
|
||||
width = r.originalWidth / textureWidth;
|
||||
height = r.originalHeight / textureHeight;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (region == null) {
|
||||
u = v = 0;
|
||||
width = height = 1;
|
||||
} else {
|
||||
u = region.u;
|
||||
v = region.v;
|
||||
width = region.u2 - u;
|
||||
height = region.v2 - v;
|
||||
}
|
||||
for (int i = 0; i < n; i += 2) {
|
||||
uvs[i] = u + regionUVs[i] * width;
|
||||
uvs[i + 1] = v + regionUVs[i + 1] * height;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b7f7514a003143844b6d01ecc93ed4d5
|
||||
timeCreated: 1466772712
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,65 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated April 5, 2025. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2026, 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 so movement along
|
||||
/// the path has a constant speed.</summary>
|
||||
public bool ConstantSpeed { get { return constantSpeed; } set { constantSpeed = value; } }
|
||||
|
||||
public PathAttachment (String name)
|
||||
: base(name) {
|
||||
}
|
||||
|
||||
/// <summary>Copy constructor.</summary>
|
||||
protected PathAttachment (PathAttachment other)
|
||||
: base(other) {
|
||||
|
||||
lengths = new float[other.lengths.Length];
|
||||
Array.Copy(other.lengths, 0, lengths, 0, lengths.Length);
|
||||
|
||||
closed = other.closed;
|
||||
constantSpeed = other.constantSpeed;
|
||||
}
|
||||
|
||||
public override Attachment Copy () {
|
||||
return new PathAttachment(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c77d9bf384a1e9f41966464e7e3b4870
|
||||
timeCreated: 1466772712
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,78 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated April 5, 2025. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2026, 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 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="https://esotericsoftware.com/spine-points">Point Attachments</a> in the Spine User Guide.
|
||||
/// </summary>
|
||||
public class PointAttachment : Attachment {
|
||||
internal float x, y, rotation;
|
||||
/// <summary>The local x position.</summary>
|
||||
public float X { get { return x; } set { x = value; } }
|
||||
/// <summary>The local y position.</summary>
|
||||
public float Y { get { return y; } set { y = value; } }
|
||||
/// <summary>The local rotation in degrees, counter clockwise.</summary>
|
||||
public float Rotation { get { return rotation; } set { rotation = value; } }
|
||||
|
||||
public PointAttachment (string name)
|
||||
: base(name) {
|
||||
}
|
||||
|
||||
/// <summary>Copy constructor.</summary>
|
||||
protected PointAttachment (PointAttachment other)
|
||||
: base(other) {
|
||||
x = other.x;
|
||||
y = other.y;
|
||||
rotation = other.rotation;
|
||||
}
|
||||
|
||||
/// <summary>Computes the world position from the local position.</summary>
|
||||
public void ComputeWorldPosition (BonePose bone, out float ox, out float oy) {
|
||||
bone.LocalToWorld(this.x, this.y, out ox, out oy);
|
||||
}
|
||||
|
||||
/// <summary>Computes the world rotation from the local rotation.</summary>
|
||||
public float ComputeWorldRotation (BonePose bone) {
|
||||
float r = rotation * MathUtils.DegRad, cos = (float)Math.Cos(r), sin = (float)Math.Sin(r);
|
||||
float x = cos * bone.a + sin * bone.b;
|
||||
float y = cos * bone.c + sin * bone.d;
|
||||
return MathUtils.Atan2Deg(y, x);
|
||||
}
|
||||
|
||||
public override Attachment Copy () {
|
||||
return new PointAttachment(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4fdde4cc4df0952468946f4f913dcb36
|
||||
timeCreated: 1485603478
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,229 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated April 5, 2025. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2026, 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;
|
||||
|
||||
namespace Spine {
|
||||
#if IS_UNITY
|
||||
using Color32F = UnityEngine.Color;
|
||||
#endif
|
||||
|
||||
/// <summary>Attachment that displays a texture region.</summary>
|
||||
public class RegionAttachment : Attachment, IHasSequence {
|
||||
public const int BLX = 0, BLY = 1;
|
||||
public const int ULX = 2, ULY = 3;
|
||||
public const int URX = 4, URY = 5;
|
||||
public const int BRX = 6, BRY = 7;
|
||||
|
||||
internal readonly Sequence sequence;
|
||||
internal float x, y, rotation, scaleX = 1, scaleY = 1, width, height;
|
||||
// Color is a struct, set to protected to prevent
|
||||
// Color color = slot.color; color.a = 0.5;
|
||||
// modifying just a copy of the struct instead of the original
|
||||
// object as in reference implementation.
|
||||
protected Color32F color = new Color32F(1, 1, 1, 1);
|
||||
|
||||
public float X { get { return x; } set { x = value; } }
|
||||
public float Y { get { return y; } set { y = value; } }
|
||||
/// <summary>The local rotation in degrees, counter clockwise.</summary>
|
||||
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 Color32F GetColor () {
|
||||
return color;
|
||||
}
|
||||
|
||||
public void SetColor (Color32F color) {
|
||||
this.color = color;
|
||||
}
|
||||
|
||||
public void SetColor (float r, float g, float b, float a) {
|
||||
color = new Color32F(r, g, b, a);
|
||||
}
|
||||
|
||||
public string Path { get; set; }
|
||||
public Sequence Sequence { get { return sequence; } }
|
||||
|
||||
public RegionAttachment (string name, Sequence sequence)
|
||||
: base(name) {
|
||||
if (sequence == null) throw new ArgumentException("sequence cannot be null.", "sequence");
|
||||
this.sequence = sequence;
|
||||
}
|
||||
|
||||
/// <summary>Copy constructor.</summary>
|
||||
public RegionAttachment (RegionAttachment other)
|
||||
: base(other) {
|
||||
Path = other.Path;
|
||||
x = other.x;
|
||||
y = other.y;
|
||||
scaleX = other.scaleX;
|
||||
scaleY = other.scaleY;
|
||||
rotation = other.rotation;
|
||||
width = other.width;
|
||||
height = other.height;
|
||||
color = other.color;
|
||||
sequence = new Sequence(other.sequence);
|
||||
}
|
||||
|
||||
/// <summary><para>
|
||||
/// Transforms the attachment's four vertices to world coordinates. If the attachment has a <see cref="Sequence"/> the region may
|
||||
/// be changed.</para>
|
||||
/// <para>
|
||||
/// See <see href='https://esotericsoftware.com/spine-runtime-skeletons#World-transforms'>World transforms</a> in the Spine
|
||||
/// Runtimes Guide.</para></summary>
|
||||
/// <param name="worldVertices">The output world vertices. Must have a length greater than or equal to offset + 8.</param>
|
||||
/// <param name="vertexOffsets">The vertex <see cref="Sequence.GetOffsets(int)">offsets</see>.</param>
|
||||
/// <param name="offset">The worldVertices index to begin writing values.</param>
|
||||
/// <param name="stride">The number of worldVertices entries between the value pairs written.</param>
|
||||
public void ComputeWorldVertices (Slot slot, float[] vertexOffsets, float[] worldVertices, int offset, int stride = 2) {
|
||||
BonePose bone = slot.Bone.AppliedPose;
|
||||
float bwx = bone.worldX, bwy = bone.worldY;
|
||||
float a = bone.a, b = bone.b, c = bone.c, d = bone.d;
|
||||
|
||||
// Vertex order is different from RegionAttachment.java
|
||||
float offsetX = vertexOffsets[BRX]; // 0
|
||||
float offsetY = vertexOffsets[BRY]; // 1
|
||||
worldVertices[offset] = offsetX * a + offsetY * b + bwx; // bl
|
||||
worldVertices[offset + 1] = offsetX * c + offsetY * d + bwy;
|
||||
offset += stride;
|
||||
|
||||
offsetX = vertexOffsets[BLX]; // 2
|
||||
offsetY = vertexOffsets[BLY]; // 3
|
||||
worldVertices[offset] = offsetX * a + offsetY * b + bwx; // ul
|
||||
worldVertices[offset + 1] = offsetX * c + offsetY * d + bwy;
|
||||
offset += stride;
|
||||
|
||||
offsetX = vertexOffsets[ULX]; // 4
|
||||
offsetY = vertexOffsets[ULY]; // 5
|
||||
worldVertices[offset] = offsetX * a + offsetY * b + bwx; // ur
|
||||
worldVertices[offset + 1] = offsetX * c + offsetY * d + bwy;
|
||||
offset += stride;
|
||||
|
||||
offsetX = vertexOffsets[URX]; // 6
|
||||
offsetY = vertexOffsets[URY]; // 7
|
||||
worldVertices[offset] = offsetX * a + offsetY * b + bwx; // br
|
||||
worldVertices[offset + 1] = offsetX * c + offsetY * d + bwy;
|
||||
//offset += stride;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the vertex <see cref="Sequence.GetOffsets(int)">offsets</see> for the specified slot pose.
|
||||
/// </summary>
|
||||
public float[] GetOffsets (SlotPose pose) {
|
||||
return sequence.GetOffsets(sequence.ResolveIndex(pose));
|
||||
}
|
||||
|
||||
public void UpdateSequence () {
|
||||
sequence.Update(this);
|
||||
}
|
||||
|
||||
public override Attachment Copy () {
|
||||
return new RegionAttachment(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes <see cref="Sequence.GetUVs(int)">UVs</see> and <see cref="Sequence.GetOffsets(int)">offsets</see> for a region attachment.
|
||||
/// </summary>
|
||||
/// <param name="uvs">Output array for the computed UVs, length of 8.</param>
|
||||
/// <param name="offset">Output array for the computed vertex offsets, length of 8.</param>
|
||||
internal static void ComputeUVs (TextureRegion region, float x, float y, float scaleX, float scaleY, float rotation, float width,
|
||||
float height, float[] offset, float[] uvs) {
|
||||
float localX2 = width / 2, localY2 = height / 2;
|
||||
float localX = -localX2, localY = -localY2;
|
||||
bool rotated = false;
|
||||
AtlasRegion r = region as AtlasRegion;
|
||||
if (r != null) {
|
||||
localX += r.offsetX / r.originalWidth * width;
|
||||
localY += r.offsetY / r.originalHeight * height;
|
||||
if (r.degrees == 90) {
|
||||
rotated = true;
|
||||
localX2 -= (r.originalWidth - r.offsetX - r.packedHeight) / r.originalWidth * width;
|
||||
localY2 -= (r.originalHeight - r.offsetY - r.packedWidth) / r.originalHeight * height;
|
||||
} else {
|
||||
localX2 -= (r.originalWidth - r.offsetX - r.packedWidth) / r.originalWidth * width;
|
||||
localY2 -= (r.originalHeight - r.offsetY - r.packedHeight) / r.originalHeight * height;
|
||||
}
|
||||
}
|
||||
localX *= scaleX;
|
||||
localY *= scaleY;
|
||||
localX2 *= scaleX;
|
||||
localY2 *= scaleY;
|
||||
float rot = rotation * MathUtils.DegRad, cos = (float)Math.Cos(rot), sin = (float)Math.Sin(rot);
|
||||
float localXCos = localX * cos + x;
|
||||
float localXSin = localX * sin;
|
||||
float localYCos = localY * cos + y;
|
||||
float localYSin = localY * sin;
|
||||
float localX2Cos = localX2 * cos + x;
|
||||
float localX2Sin = localX2 * sin;
|
||||
float localY2Cos = localY2 * cos + y;
|
||||
float localY2Sin = localY2 * sin;
|
||||
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;
|
||||
if (region == null) {
|
||||
uvs[BLX] = 0;
|
||||
uvs[BLY] = 0;
|
||||
uvs[ULX] = 0;
|
||||
uvs[ULY] = 1;
|
||||
uvs[URX] = 1;
|
||||
uvs[URY] = 1;
|
||||
uvs[BRX] = 1;
|
||||
uvs[BRY] = 0;
|
||||
} else {
|
||||
uvs[BLX] = region.u2;
|
||||
uvs[ULY] = region.v2;
|
||||
uvs[URX] = region.u;
|
||||
uvs[BRY] = region.v;
|
||||
if (rotated) {
|
||||
uvs[BLY] = region.v;
|
||||
uvs[ULX] = region.u2;
|
||||
uvs[URY] = region.v2;
|
||||
uvs[BRX] = region.u;
|
||||
} else {
|
||||
uvs[BLY] = region.v2;
|
||||
uvs[ULX] = region.u;
|
||||
uvs[URY] = region.v;
|
||||
uvs[BRX] = region.u2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 89cefdd024734a941952a05d2b5dff71
|
||||
timeCreated: 1466772712
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,173 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated April 5, 2025. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2026, 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.Text;
|
||||
|
||||
namespace Spine {
|
||||
/// <summary>
|
||||
/// Holds texture regions, UVs, and vertex offsets for rendering a region or mesh attachment. <see cref="Regions"/> must
|
||||
/// be populated and <see cref="Update(IHasSequence)"/> called before use.
|
||||
/// </summary>
|
||||
public class Sequence {
|
||||
static int nextID = 0;
|
||||
static readonly Object nextIdLock = new Object();
|
||||
|
||||
internal readonly int id;
|
||||
internal readonly TextureRegion[] regions;
|
||||
internal readonly bool pathSuffix;
|
||||
internal float[][] uvs, offsets;
|
||||
internal int start, digits, setupIndex;
|
||||
|
||||
/// <summary>The starting number for the numeric <see cref="GetPath(string, int)">path</see> suffix.</summary>
|
||||
public int Start { get { return start; } set { start = value; } }
|
||||
/// <summary>The minimum number of digits in the numeric <see cref="GetPath(string, int)">path</see> suffix, for zero
|
||||
/// padding. 0 for no zero padding.</summary>
|
||||
public int Digits { get { return digits; } set { digits = value; } }
|
||||
/// <summary>The index of the region to show for the setup pose.</summary>
|
||||
public int SetupIndex { get { return setupIndex; } set { setupIndex = value; } }
|
||||
/// <summary>The list of texture regions this sequence will display.</summary>
|
||||
public TextureRegion[] Regions { get { return regions; } }
|
||||
/// <summary>Returns true if the <see cref="GetPath(string, int)">path</see> has a numeric suffix.</summary>
|
||||
public bool HasPathSuffix { get { return pathSuffix; } }
|
||||
/// <summary>Returns a unique ID for this attachment.</summary>
|
||||
public int Id { get { return id; } }
|
||||
|
||||
/// <param name="count">The number of texture regions this sequence will display.</param>
|
||||
/// <param name="pathSuffix">If true, the <see cref="GetPath(string, int)">path</see> has a numeric suffix. If false, all
|
||||
/// regions will use the same path, so <c>count</c> should be 1.</param>
|
||||
public Sequence (int count, bool pathSuffix) {
|
||||
lock (Sequence.nextIdLock) {
|
||||
id = Sequence.nextID++;
|
||||
}
|
||||
regions = new TextureRegion[count];
|
||||
this.pathSuffix = pathSuffix;
|
||||
}
|
||||
|
||||
/// <summary>Copy constructor.</summary>
|
||||
public Sequence (Sequence other) {
|
||||
lock (Sequence.nextIdLock) {
|
||||
id = Sequence.nextID++;
|
||||
}
|
||||
int regionCount = other.regions.Length;
|
||||
regions = new TextureRegion[regionCount];
|
||||
Array.Copy(other.regions, 0, regions, 0, regionCount);
|
||||
|
||||
start = other.start;
|
||||
digits = other.digits;
|
||||
setupIndex = other.setupIndex;
|
||||
pathSuffix = other.pathSuffix;
|
||||
|
||||
if (other.uvs != null) {
|
||||
int length = other.uvs[0].Length;
|
||||
uvs = new float[regionCount][];
|
||||
for (int i = 0; i < regionCount; i++) {
|
||||
uvs[i] = new float[length];
|
||||
Array.Copy(other.uvs[i], 0, uvs[i], 0, length);
|
||||
}
|
||||
}
|
||||
if (other.offsets != null) {
|
||||
offsets = new float[regionCount][];
|
||||
for (int i = 0; i < regionCount; i++) {
|
||||
offsets[i] = new float[8];
|
||||
Array.Copy(other.offsets[i], 0, offsets[i], 0, 8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Update (IHasSequence attachment) {
|
||||
int regionCount = regions.Length;
|
||||
RegionAttachment region = attachment as RegionAttachment;
|
||||
if (region != null) {
|
||||
uvs = new float[regionCount][];
|
||||
offsets = new float[regionCount][];
|
||||
for (int i = 0; i < regionCount; i++) {
|
||||
uvs[i] = new float[8];
|
||||
offsets[i] = new float[8];
|
||||
RegionAttachment.ComputeUVs(regions[i], region.x, region.y, region.scaleX, region.scaleY, region.rotation,
|
||||
region.width, region.height, offsets[i], uvs[i]);
|
||||
}
|
||||
} else {
|
||||
MeshAttachment mesh = attachment as MeshAttachment;
|
||||
if (mesh != null) {
|
||||
float[] regionUVs = mesh.regionUVs;
|
||||
uvs = new float[regionCount][];
|
||||
offsets = null;
|
||||
for (int i = 0; i < regionCount; i++) {
|
||||
uvs[i] = new float[regionUVs.Length];
|
||||
MeshAttachment.ComputeUVs(regions[i], regionUVs, uvs[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Returns the <see cref="Regions"/> index for the <see cref="SlotPose.SequenceIndex"/>.</summary>
|
||||
public int ResolveIndex (SlotPose pose) {
|
||||
int index = pose.SequenceIndex;
|
||||
if (index == -1) index = setupIndex;
|
||||
if (index >= regions.Length) index = regions.Length - 1;
|
||||
return index;
|
||||
}
|
||||
|
||||
/// <summary>Returns the texture region from <see cref="Regions"/> for the specified index.</summary>
|
||||
public TextureRegion GetRegion (int index) {
|
||||
return regions[index];
|
||||
}
|
||||
|
||||
/// <summary>Returns the UVs for the specified index. <see cref="Regions">Regions</see> must be populated and
|
||||
/// <see cref="Update(IHasSequence)"/> called before calling this method.</summary>
|
||||
public float[] GetUVs (int index) {
|
||||
return uvs[index];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns vertex offsets from the center of a <see cref="RegionAttachment"/>. Invalid to call for a <see cref="MeshAttachment"/>.
|
||||
/// </summary>
|
||||
public float[] GetOffsets (int index) {
|
||||
return offsets[index];
|
||||
}
|
||||
|
||||
/// <summary>Returns the specified base path with an optional numeric suffix for the specified index.</summary>
|
||||
public string GetPath (string basePath, int index) {
|
||||
if (!pathSuffix) return basePath;
|
||||
var buffer = new StringBuilder(basePath.Length + digits);
|
||||
buffer.Append(basePath);
|
||||
string frame = (start + index).ToString();
|
||||
for (int i = digits - frame.Length; i > 0; i--)
|
||||
buffer.Append('0');
|
||||
buffer.Append(frame);
|
||||
return buffer.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Controls how <see cref="Sequence.Regions"/> are displayed over time.</summary>
|
||||
public enum SequenceMode {
|
||||
Hold, Once, Loop, Pingpong, OnceReverse, LoopReverse, PingpongReverse
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 522632bd4e297fe47acf78100bfd8689
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,159 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated April 5, 2025. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2026, 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>Base class for an attachment with vertices that are transformed by one or more bones and can be deformed by
|
||||
/// <see cref="SlotPose.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;
|
||||
|
||||
/// <summary>Gets a unique ID for this attachment.</summary>
|
||||
public int Id { get { return id; } }
|
||||
/// <summary>The bones that affect the <see cref="Vertices"/>. The entries are, for each vertex, the number of bones affecting the
|
||||
/// vertex followed by that many bone indices, which is <see cref="Skeleton.Bones"/> index. Null if this attachment has no
|
||||
/// weights.</summary>
|
||||
public int[] Bones { get { return bones; } set { bones = value; } }
|
||||
/// <summary>The vertex positions in the bone's coordinate system. For a non-weighted attachment, the values are <c>x,y</c> pairs
|
||||
/// for each vertex. For a weighted attachment, the values are <c>x,y,weight</c> triplets for each bone affecting each
|
||||
/// vertex.</summary>
|
||||
public float[] Vertices { get { return vertices; } set { vertices = value; } }
|
||||
public int WorldVerticesLength { get { return worldVerticesLength; } set { worldVerticesLength = value; } }
|
||||
|
||||
public VertexAttachment (string name)
|
||||
: base(name) {
|
||||
|
||||
lock (VertexAttachment.nextIdLock) {
|
||||
id = VertexAttachment.nextID++;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Copy constructor.</summary>
|
||||
public VertexAttachment (VertexAttachment other)
|
||||
: base(other) {
|
||||
|
||||
lock (VertexAttachment.nextIdLock) {
|
||||
id = VertexAttachment.nextID++;
|
||||
}
|
||||
timelineAttachment = other.timelineAttachment;
|
||||
if (other.bones != null) {
|
||||
bones = new int[other.bones.Length];
|
||||
Array.Copy(other.bones, 0, bones, 0, bones.Length);
|
||||
} else
|
||||
bones = null;
|
||||
|
||||
if (other.vertices != null) {
|
||||
vertices = new float[other.vertices.Length];
|
||||
Array.Copy(other.vertices, 0, vertices, 0, vertices.Length);
|
||||
} else
|
||||
vertices = null;
|
||||
|
||||
worldVerticesLength = other.worldVerticesLength;
|
||||
}
|
||||
|
||||
public void ComputeWorldVertices (Skeleton skeleton, Slot slot, float[] worldVertices) {
|
||||
ComputeWorldVertices(skeleton, slot, 0, worldVerticesLength, worldVertices, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Transforms the attachment's local <see cref="Vertices"/> to world coordinates. If <see cref="SlotPose.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 virtual void ComputeWorldVertices (Skeleton skeleton, Slot slot, int start, int count, float[] worldVertices, int offset, int stride = 2) {
|
||||
count = offset + (count >> 1) * stride;
|
||||
ExposedList<float> deformArray = slot.AppliedPose.deform;
|
||||
float[] vertices = this.vertices;
|
||||
int[] bones = this.bones;
|
||||
if (bones == null) {
|
||||
if (deformArray.Count > 0) vertices = deformArray.Items;
|
||||
BonePose bone = slot.bone.AppliedPose;
|
||||
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 = 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) {
|
||||
BonePose bone = skeletonBones[bones[v]].AppliedPose;
|
||||
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) {
|
||||
BonePose bone = skeletonBones[bones[v]].AppliedPose;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8b40cfb462a8b774891e1604e5360d32
|
||||
timeCreated: 1466772712
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,34 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated April 5, 2025. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2026, 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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b08ef68b8e39f40498ef24ef12cca281
|
||||
timeCreated: 1456265155
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,71 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated April 5, 2025. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2026, 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>A node in a skeleton's hierarchy with a transform that affects its children and their attachments. A bone has a
|
||||
/// number of poses:
|
||||
/// <list type="bullet">
|
||||
/// <item><see cref="Data"/>: The setup pose.</item>
|
||||
/// <item><see cref="Pose"/>: The unconstrained local pose. Set by animations and application code.</item>
|
||||
/// <item><see cref="AppliedPose"/>: The local pose to use for rendering. Possibly modified by constraints.</item>
|
||||
/// <item>World transform: the local pose combined with the parent world transform. Computed on a pose by
|
||||
/// <see cref="BonePose.UpdateWorldTransform(Skeleton)"/> and <see cref="Skeleton.UpdateWorldTransform(Physics)"/>.</item>
|
||||
/// </list>
|
||||
/// </summary>
|
||||
public class Bone : PosedActive<BoneData, BonePose> {
|
||||
static public bool yDown;
|
||||
|
||||
internal Bone parent;
|
||||
internal ExposedList<Bone> children = new ExposedList<Bone>(4);
|
||||
|
||||
internal bool sorted;
|
||||
|
||||
public Bone (BoneData data, Bone parent)
|
||||
: base(data, new BonePose(), new BonePose()) {
|
||||
this.parent = parent;
|
||||
appliedPose.bone = this;
|
||||
constrainedPose.bone = this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copy constructor. Does not copy the <see cref="Children"/> bones.
|
||||
/// </summary>
|
||||
public Bone (Bone bone, Bone parent)
|
||||
: this(bone.data, parent) {
|
||||
pose.Set(bone.pose);
|
||||
}
|
||||
|
||||
public Bone Parent { get { return parent; } }
|
||||
public ExposedList<Bone> Children { get { return children; } }
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ed00e3a4b386a964fb0f1c7ffd5544e5
|
||||
timeCreated: 1456265155
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,89 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated April 5, 2025. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2026, 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>
|
||||
/// The setup pose for a bone.
|
||||
/// </summary>
|
||||
public class BoneData : PosedData<BonePose> {
|
||||
internal int index;
|
||||
internal BoneData parent;
|
||||
internal float length;
|
||||
|
||||
/// <param name="parent">May be null.</param>
|
||||
public BoneData (int index, string name, BoneData parent)
|
||||
: base(name, new BonePose()) {
|
||||
|
||||
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.parent = parent;
|
||||
}
|
||||
|
||||
/// <summary>Copy constructor.</summary>
|
||||
/// <param name="parent">May be null.</param>
|
||||
public BoneData (BoneData data, BoneData parent)
|
||||
: this(data.index, data.name, parent) {
|
||||
length = data.length;
|
||||
setupPose.Set(data.setupPose);
|
||||
}
|
||||
|
||||
/// <summary>The <see cref="Skeleton.Bones"/> index.</summary>
|
||||
public int Index { get { return index; } }
|
||||
|
||||
/// <summary>May be null.</summary>
|
||||
public BoneData Parent { get { return parent; } }
|
||||
|
||||
public float Length { get { return length; } set { length = value; } }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines how a bone inherits world transforms from parent bones.
|
||||
/// </summary>
|
||||
public enum Inherit {
|
||||
Normal,
|
||||
OnlyTranslation,
|
||||
NoRotationOrReflection,
|
||||
NoScale,
|
||||
NoScaleOrReflection
|
||||
}
|
||||
|
||||
public class InheritEnum {
|
||||
public static readonly Inherit[] Values = {
|
||||
Inherit.Normal,
|
||||
Inherit.OnlyTranslation,
|
||||
Inherit.NoRotationOrReflection,
|
||||
Inherit.NoScale,
|
||||
Inherit.NoScaleOrReflection
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2cf831005966832449a5de742752e578
|
||||
timeCreated: 1456265153
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,470 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated April 5, 2025. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2026, 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>The applied local pose and world transform for a bone. This is the <see cref="Bone.Pose"/> with constraints applied and the
|
||||
/// world transform computed by <see cref="Skeleton.UpdateWorldTransform(Physics)"/> and <see cref="UpdateWorldTransform(Skeleton)"/>.
|
||||
/// <para>
|
||||
/// If the world transform is changed, call <see cref="UpdateLocalTransform(Skeleton)"/> before using the local transform. The local
|
||||
/// transform may be needed by other code (eg to apply another constraint).</para>
|
||||
/// <para>
|
||||
/// After changing the world transform, call <see cref="UpdateWorldTransform(Skeleton)"/> on every descendant bone. It may be more
|
||||
/// convenient to modify the local transform instead, then call <see cref="Skeleton.UpdateWorldTransform(Physics)"/> to update the world
|
||||
/// transforms for all bones and apply constraints.</para>
|
||||
/// </summary>
|
||||
public class BonePose : IPose<BonePose>, IUpdate {
|
||||
public Bone bone;
|
||||
|
||||
internal float x, y, rotation, scaleX, scaleY, shearX, shearY;
|
||||
internal Inherit inherit;
|
||||
|
||||
internal float a, b, worldX;
|
||||
internal float c, d, worldY;
|
||||
internal int world, local;
|
||||
|
||||
public void Set (BonePose pose) {
|
||||
if (pose == null) throw new ArgumentNullException("pose", "pose cannot be null.");
|
||||
x = pose.x;
|
||||
y = pose.y;
|
||||
rotation = pose.rotation;
|
||||
scaleX = pose.scaleX;
|
||||
scaleY = pose.scaleY;
|
||||
shearX = pose.shearX;
|
||||
shearY = pose.shearY;
|
||||
inherit = pose.inherit;
|
||||
}
|
||||
|
||||
/// <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>Sets local x and y translation.</summary>
|
||||
public void SetPosition (float x, float y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
/// <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>Sets local scaleX and scaleY.</summary>
|
||||
public void SetScale (float scaleX, float scaleY) {
|
||||
this.scaleX = scaleX;
|
||||
this.scaleY = scaleY;
|
||||
}
|
||||
|
||||
/// <summary>Sets local scaleX and scaleY to the same value.</summary>
|
||||
public void SetScale (float scale) {
|
||||
scaleX = scale;
|
||||
scaleY = scale;
|
||||
}
|
||||
|
||||
/// <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>Determines how parent world transforms affect this bone.</summary>
|
||||
public Inherit Inherit { get { return inherit; } set { inherit = value; } }
|
||||
|
||||
/// <summary>
|
||||
/// Called by <see cref="Skeleton.UpdateCache()"/> to compute the world transform, if needed.
|
||||
/// </summary>
|
||||
public void Update (Skeleton skeleton, Physics physics) {
|
||||
if (world != skeleton.update) UpdateWorldTransform(skeleton);
|
||||
}
|
||||
|
||||
/// <summary>Computes the world transform using the parent bone's world transform and this applied local pose. 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 (Skeleton skeleton) {
|
||||
if (local == skeleton.update)
|
||||
UpdateLocalTransform(skeleton);
|
||||
else
|
||||
world = skeleton.update;
|
||||
|
||||
if (bone.parent == null) { // Root bone.
|
||||
float sx = skeleton.scaleX, sy = skeleton.ScaleY;
|
||||
float rx = (rotation + shearX) * MathUtils.DegRad;
|
||||
float ry = (rotation + 90 + shearY) * MathUtils.DegRad;
|
||||
a = (float)Math.Cos(rx) * scaleX * sx;
|
||||
b = (float)Math.Cos(ry) * scaleY * sx;
|
||||
c = (float)Math.Sin(rx) * scaleX * sy;
|
||||
d = (float)Math.Sin(ry) * scaleY * sy;
|
||||
worldX = x * sx + skeleton.x;
|
||||
worldY = y * sy + skeleton.y;
|
||||
return;
|
||||
}
|
||||
|
||||
BonePose parent = bone.parent.appliedPose;
|
||||
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 (inherit) {
|
||||
case Inherit.Normal: {
|
||||
float rx = (rotation + shearX) * MathUtils.DegRad;
|
||||
float ry = (rotation + 90 + shearY) * MathUtils.DegRad;
|
||||
float la = (float)Math.Cos(rx) * scaleX;
|
||||
float lb = (float)Math.Cos(ry) * scaleY;
|
||||
float lc = (float)Math.Sin(rx) * scaleX;
|
||||
float ld = (float)Math.Sin(ry) * scaleY;
|
||||
a = pa * la + pb * lc;
|
||||
b = pa * lb + pb * ld;
|
||||
c = pc * la + pd * lc;
|
||||
d = pc * lb + pd * ld;
|
||||
break;
|
||||
}
|
||||
case Inherit.OnlyTranslation: {
|
||||
float sx = skeleton.scaleX, sy = skeleton.ScaleY;
|
||||
float rx = (rotation + shearX) * MathUtils.DegRad;
|
||||
float ry = (rotation + 90 + shearY) * MathUtils.DegRad;
|
||||
a = (float)Math.Cos(rx) * scaleX * sx;
|
||||
b = (float)Math.Cos(ry) * scaleY * sx;
|
||||
c = (float)Math.Sin(rx) * scaleX * sy;
|
||||
d = (float)Math.Sin(ry) * scaleY * sy;
|
||||
break;
|
||||
}
|
||||
case Inherit.NoRotationOrReflection: {
|
||||
float sx = skeleton.scaleX, sy = skeleton.ScaleY, sxi = 1 / sx, syi = 1 / sy;
|
||||
pa *= sxi;
|
||||
pc *= syi;
|
||||
float s = pa * pa + pc * pc, r;
|
||||
if (s > MathUtils.EpsilonSq) {
|
||||
s = Math.Abs(pa * pd * syi - pb * sxi * pc) / s;
|
||||
pb = pc * s;
|
||||
pd = pa * s;
|
||||
r = rotation - MathUtils.Atan2Deg(pc, pa);
|
||||
} else {
|
||||
pa = 0;
|
||||
pc = 0;
|
||||
r = rotation - 90 + MathUtils.Atan2Deg(pd, pb);
|
||||
}
|
||||
float rx = (r + shearX) * MathUtils.DegRad;
|
||||
float ry = (r + shearY + 90) * MathUtils.DegRad;
|
||||
float la = (float)Math.Cos(rx) * scaleX;
|
||||
float lb = (float)Math.Cos(ry) * scaleY;
|
||||
float lc = (float)Math.Sin(rx) * scaleX;
|
||||
float ld = (float)Math.Sin(ry) * scaleY;
|
||||
a = (pa * la - pb * lc) * sx;
|
||||
b = (pa * lb - pb * ld) * sx;
|
||||
c = (pc * la + pd * lc) * sy;
|
||||
d = (pc * lb + pd * ld) * sy;
|
||||
break;
|
||||
}
|
||||
case Inherit.NoScale:
|
||||
case Inherit.NoScaleOrReflection: {
|
||||
float sx = skeleton.scaleX, sy = skeleton.ScaleY, sxi = 1 / sx, syi = 1 / sy;
|
||||
float r = rotation * MathUtils.DegRad, cos = (float)Math.Cos(r), sin = (float)Math.Sin(r);
|
||||
float za = (pa * cos + pb * sin) * sxi;
|
||||
float zc = (pc * cos + pd * sin) * syi;
|
||||
float s = 1 / (float)Math.Sqrt(za * za + zc * zc);
|
||||
za *= s;
|
||||
zc *= s;
|
||||
float zb = -zc, zd = za;
|
||||
if (inherit == Inherit.NoScale && pa * pd - pb * pc < 0 != (sx < 0 != sy < 0)) {
|
||||
zb = -zb;
|
||||
zd = -zd;
|
||||
}
|
||||
float rx = shearX * MathUtils.DegRad;
|
||||
float ry = (90 + shearY) * MathUtils.DegRad;
|
||||
float la = (float)Math.Cos(rx) * scaleX;
|
||||
float lb = (float)Math.Cos(ry) * scaleY;
|
||||
float lc = (float)Math.Sin(rx) * scaleX;
|
||||
float ld = (float)Math.Sin(ry) * scaleY;
|
||||
a = (za * la + zb * lc) * sx;
|
||||
b = (za * lb + zb * ld) * sx;
|
||||
c = (zc * la + zd * lc) * sy;
|
||||
d = (zc * lb + zd * ld) * sy;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes the local transform values from the world transform.
|
||||
/// <para>
|
||||
/// Some information is ambiguous in the world transform, such as -1,-1 scale versus 180 rotation. The local 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 UpdateLocalTransform (Skeleton skeleton) {
|
||||
local = 0;
|
||||
world = skeleton.update;
|
||||
|
||||
float sx = skeleton.scaleX, sy = skeleton.ScaleY;
|
||||
if (bone.parent == null) {
|
||||
float sxi = 1 / sx, syi = 1 / sy;
|
||||
x = (worldX - skeleton.x) * sxi;
|
||||
y = (worldY - skeleton.y) * syi;
|
||||
Set(a * sxi, b * sxi, c * syi, d * syi, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
BonePose parent = bone.parent.appliedPose;
|
||||
float pa = parent.a, pb = parent.b, pc = parent.c, pd = parent.d;
|
||||
float pad = pa * pd - pb * pc, pid = 1 / pad;
|
||||
float ia = pd * pid, ib = pb * pid, ic = pc * pid, id = pa * pid;
|
||||
float dx = worldX - parent.worldX, dy = worldY - parent.worldY;
|
||||
x = dx * ia - dy * ib;
|
||||
y = dy * id - dx * ic;
|
||||
|
||||
switch (inherit) {
|
||||
case Inherit.Normal: {
|
||||
Set(ia * a - ib * c, ia * b - ib * d, id * c - ic * a, id * d - ic * b, 0);
|
||||
break;
|
||||
}
|
||||
case Inherit.OnlyTranslation: {
|
||||
float sxi = 1 / sx, syi = 1 / sy;
|
||||
Set(a * sxi, b * sxi, c * syi, d * syi, 0);
|
||||
break;
|
||||
}
|
||||
case Inherit.NoRotationOrReflection: {
|
||||
float sxi = 1 / sx, syi = 1 / sy;
|
||||
pa *= sxi;
|
||||
pc *= syi;
|
||||
float wa = a * sxi, wb = b * sxi, wc = c * syi, wd = d * syi;
|
||||
float s = 1 / (pa * pa + pc * pc), det = 1 / Math.Abs(pad * sxi * syi);
|
||||
Set((pa * wa + pc * wc) * s, (pa * wb + pc * wd) * s, (pa * wc - pc * wa) * det, (pa * wd - pc * wb) * det,
|
||||
MathUtils.Atan2Deg(pc, pa));
|
||||
break;
|
||||
}
|
||||
case Inherit.NoScale:
|
||||
case Inherit.NoScaleOrReflection: {
|
||||
float sxi = 1 / sx, syi = 1 / sy;
|
||||
float wa = a * sxi, wb = b * sxi, wc = c * syi, wd = d * syi;
|
||||
float tx = pd * a - pb * c, ty = pa * c - pc * a;
|
||||
if (pad < 0) {
|
||||
tx = -tx;
|
||||
ty = -ty;
|
||||
}
|
||||
float r = MathUtils.Atan2Deg(ty, tx);
|
||||
rotation = r;
|
||||
r *= MathUtils.DegRad;
|
||||
float cos = (float)Math.Cos(r), sin = (float)Math.Sin(r);
|
||||
float za = (pa * cos + pb * sin) * sxi;
|
||||
float zc = (pc * cos + pd * sin) * syi;
|
||||
float s = 1 / (float)Math.Sqrt(za * za + zc * zc);
|
||||
za *= s;
|
||||
zc *= s;
|
||||
float si = inherit == Inherit.NoScale && pad < 0 != (sx < 0 != sy < 0) ? -1 : 1;
|
||||
Set(za * wa + zc * wc, za * wb + zc * wd, (za * wc - zc * wa) * si, (za * wd - zc * wb) * si);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Set (float ra, float rb, float rc, float rd) {
|
||||
float x = ra * ra + rc * rc, y = rb * rb + rd * rd;
|
||||
if (x > MathUtils.EpsilonSq) {
|
||||
shearX = MathUtils.Atan2Deg(rc, ra);
|
||||
scaleX = (float)Math.Sqrt(x);
|
||||
} else {
|
||||
shearX = 0;
|
||||
scaleX = 0;
|
||||
}
|
||||
scaleY = (float)Math.Sqrt(y);
|
||||
if (y > MathUtils.EpsilonSq) {
|
||||
shearY = MathUtils.Atan2Deg(rd, rb);
|
||||
if (ra * rd - rb * rc < 0) {
|
||||
scaleY = -scaleY;
|
||||
shearY += 90;
|
||||
} else
|
||||
shearY -= 90;
|
||||
if (shearY > 180)
|
||||
shearY -= 360;
|
||||
else if (shearY <= -180) //
|
||||
shearY += 360;
|
||||
} else
|
||||
shearY = 0;
|
||||
}
|
||||
|
||||
private void Set (float ra, float rb, float rc, float rd, float ro) {
|
||||
shearX = 0;
|
||||
float x = ra * ra + rc * rc, y = rb * rb + rd * rd;
|
||||
if (x > MathUtils.EpsilonSq) {
|
||||
float r = MathUtils.Atan2Deg(rc, ra);
|
||||
rotation = r + ro;
|
||||
scaleX = (float)Math.Sqrt(x);
|
||||
scaleY = (float)Math.Sqrt(y);
|
||||
if (y > MathUtils.EpsilonSq) {
|
||||
shearY = MathUtils.Atan2Deg(rd, rb);
|
||||
if (ra * rd - rb * rc < 0) {
|
||||
scaleY = -scaleY;
|
||||
shearY += 90 - r;
|
||||
} else
|
||||
shearY -= 90 + r;
|
||||
if (shearY > 180)
|
||||
shearY -= 360;
|
||||
else if (shearY <= -180) //
|
||||
shearY += 360;
|
||||
} else
|
||||
shearY = 0;
|
||||
} else {
|
||||
scaleX = 0;
|
||||
scaleY = (float)Math.Sqrt(y);
|
||||
shearY = 0;
|
||||
rotation = y > MathUtils.EpsilonSq ? MathUtils.Atan2Deg(rd, rb) - 90 + ro : ro;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If the world transform has been modified by constraints and the local transform no longer matches,
|
||||
/// <see cref="UpdateLocalTransform(Skeleton)"/> is called. Call this after <see cref="Skeleton.UpdateWorldTransform(Physics)"/> before
|
||||
/// using the applied local transform.
|
||||
/// </summary>
|
||||
public void ValidateLocalTransform (Skeleton skeleton) {
|
||||
if (local == skeleton.update) UpdateLocalTransform(skeleton);
|
||||
}
|
||||
|
||||
internal void ModifyLocal (Skeleton skeleton) {
|
||||
if (local == skeleton.update) UpdateLocalTransform(skeleton);
|
||||
world = 0;
|
||||
ResetWorld(skeleton.update);
|
||||
}
|
||||
|
||||
internal void ModifyWorld (int update) {
|
||||
local = update;
|
||||
world = update;
|
||||
ResetWorld(update);
|
||||
}
|
||||
|
||||
private void ResetWorld (int update) {
|
||||
Bone[] children = bone.children.Items;
|
||||
for (int i = 0, n = bone.children.Count; i < n; i++) {
|
||||
BonePose child = children[i].appliedPose;
|
||||
if (child.world == update) {
|
||||
child.world = 0;
|
||||
child.local = 0;
|
||||
child.ResetWorld(update);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>The world transform <c>[a b][c d]</c> x-axis x component.</summary>
|
||||
public float A { get { return a; } set { a = value; } }
|
||||
/// <summary>The world transform <c>[a b][c d]</c> y-axis x component.</summary>
|
||||
public float B { get { return b; } set { b = value; } }
|
||||
/// <summary>The world transform <c>[a b][c d]</c> x-axis y component.</summary>
|
||||
public float C { get { return c; } set { c = value; } }
|
||||
/// <summary>The world transform <c>[a b][c d]</c> y-axis y component.</summary>
|
||||
public float D { get { return d; } set { d = value; } }
|
||||
|
||||
/// <summary>The world X position.</summary>
|
||||
public float WorldX { get { return worldX; } set { worldX = value; } }
|
||||
/// <summary>The world Y position.</summary>
|
||||
public float WorldY { get { return worldY; } set { worldY = value; } }
|
||||
/// <summary>The world rotation for the X axis, calculated using <see cref="a"/> and <see cref="c"/>. This is the direction the
|
||||
/// bone is pointing.</summary>
|
||||
public float WorldRotationX { get { return MathUtils.Atan2Deg(c, a); } }
|
||||
/// <summary>The world rotation for the Y axis, calculated using <see cref="b"/> and <see cref="d"/>.</summary>
|
||||
public float WorldRotationY { get { return MathUtils.Atan2Deg(d, b); } }
|
||||
|
||||
/// <summary>Returns the magnitude (always positive) of the world scale X, calculated using <see cref="a"/> and <see cref="c"/>.</summary>
|
||||
public float WorldScaleX { get { return (float)Math.Sqrt(a * a + c * c); } }
|
||||
/// <summary>Returns the magnitude (always positive) of the world scale Y, calculated using <see cref="b"/> and <see cref="d"/>.</summary>
|
||||
public float WorldScaleY { get { return (float)Math.Sqrt(b * b + d * d); } }
|
||||
|
||||
/// <summary>Transforms a point from world coordinates to the bone's local coordinates.</summary>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>Transforms a point from the bone's local coordinates to world coordinates.</summary>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>Transforms a point from world coordinates to the parent bone's local coordinates.</summary>
|
||||
public void WorldToParent (float worldX, float worldY, out float parentX, out float parentY) {
|
||||
if (bone.parent == null) {
|
||||
parentX = worldX;
|
||||
parentY = worldY;
|
||||
} else {
|
||||
bone.parent.appliedPose.WorldToLocal(worldX, worldY, out parentX, out parentY);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Transforms a point from the parent bone's coordinates to world coordinates.</summary>
|
||||
public void ParentToWorld (float parentX, float parentY, out float worldX, out float worldY) {
|
||||
if (bone.parent == null) {
|
||||
worldX = parentX;
|
||||
worldY = parentY;
|
||||
} else {
|
||||
bone.parent.appliedPose.LocalToWorld(parentX, parentY, out worldX, out worldY);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Transforms a world rotation to a local rotation.</summary>
|
||||
public float WorldToLocalRotation (float worldRotation) {
|
||||
worldRotation *= MathUtils.DegRad;
|
||||
float sin = (float)Math.Sin(worldRotation), cos = (float)Math.Cos(worldRotation);
|
||||
return MathUtils.Atan2Deg(a * sin - c * cos, d * cos - b * sin) + rotation - shearX;
|
||||
}
|
||||
|
||||
/// <summary>Transforms a local rotation to a world rotation.</summary>
|
||||
public float LocalToWorldRotation (float localRotation) {
|
||||
localRotation = (localRotation - rotation - shearX) * MathUtils.DegRad;
|
||||
float sin = (float)Math.Sin(localRotation), cos = (float)Math.Cos(localRotation);
|
||||
return MathUtils.Atan2Deg(cos * c + sin * d, cos * a + sin * b);
|
||||
}
|
||||
|
||||
/// <summary>Rotates the world transform the specified amount.</summary>
|
||||
public void RotateWorld (float degrees) {
|
||||
degrees *= MathUtils.DegRad;
|
||||
float sin = (float)Math.Sin(degrees), cos = (float)Math.Cos(degrees);
|
||||
float ra = a, rb = b;
|
||||
a = cos * ra - sin * c;
|
||||
b = cos * rb - sin * d;
|
||||
c = sin * ra + cos * c;
|
||||
d = sin * rb + cos * d;
|
||||
}
|
||||
|
||||
override public string ToString () {
|
||||
return bone.data.name;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 024ad131f32a77e45bc91fc1599a8e0f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,115 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated April 5, 2025. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2026, 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_3_OR_NEWER
|
||||
#define IS_UNITY
|
||||
#endif
|
||||
|
||||
#if !IS_UNITY
|
||||
namespace Spine {
|
||||
/// <summary>
|
||||
/// 32 bit floating point color to be used with XNA/Monogame.
|
||||
/// </summary>
|
||||
public struct Color32F {
|
||||
public float r, g, b, a;
|
||||
|
||||
public Color32F (float r, float g, float b, float a = 1.0f) {
|
||||
this.r = r;
|
||||
this.g = g;
|
||||
this.b = b;
|
||||
this.a = a;
|
||||
}
|
||||
|
||||
public Color32F (Microsoft.Xna.Framework.Color xnaColor)
|
||||
: this(xnaColor.R / 255f, xnaColor.G / 255f, xnaColor.B / 255f, xnaColor.A / 255f) {
|
||||
}
|
||||
|
||||
public static implicit operator Color32F (Microsoft.Xna.Framework.Color xnaColor) {
|
||||
return new Color32F(xnaColor);
|
||||
}
|
||||
|
||||
public static implicit operator Microsoft.Xna.Framework.Color (Color32F c) {
|
||||
return new Microsoft.Xna.Framework.Color(
|
||||
(byte)(c.r * 255),
|
||||
(byte)(c.g * 255),
|
||||
(byte)(c.b * 255),
|
||||
(byte)(c.a * 255)
|
||||
);
|
||||
}
|
||||
|
||||
public override string ToString () {
|
||||
return string.Format("RGBA({0}, {1}, {2}, {3})", r, g, b, a);
|
||||
}
|
||||
|
||||
public static Color32F operator + (Color32F c1, Color32F c2) {
|
||||
return new Color32F(c1.r + c2.r, c1.g + c2.g, c1.b + c2.b, c1.a + c2.a);
|
||||
}
|
||||
|
||||
public static Color32F operator - (Color32F c1, Color32F c2) {
|
||||
return new Color32F(c1.r - c2.r, c1.g - c2.g, c1.b - c2.b, c1.a - c2.a);
|
||||
}
|
||||
|
||||
public static Color32F operator * (Color32F c1, Color32F c2) {
|
||||
return new Color32F(c1.r * c2.r, c1.g * c2.g, c1.b * c2.b, c1.a * c2.a);
|
||||
}
|
||||
}
|
||||
|
||||
static class ColorExtensions {
|
||||
public static Color32F Clamp (this Color32F color) {
|
||||
color.r = MathUtils.Clamp(color.r, 0, 1);
|
||||
color.g = MathUtils.Clamp(color.g, 0, 1);
|
||||
color.b = MathUtils.Clamp(color.b, 0, 1);
|
||||
color.a = MathUtils.Clamp(color.a, 0, 1);
|
||||
return color;
|
||||
}
|
||||
|
||||
public static Color32F ClampRGB (this Color32F color) {
|
||||
color.r = MathUtils.Clamp(color.r, 0, 1);
|
||||
color.g = MathUtils.Clamp(color.g, 0, 1);
|
||||
color.b = MathUtils.Clamp(color.b, 0, 1);
|
||||
return color;
|
||||
}
|
||||
|
||||
public static Color32F RGBA8888ToColor (this uint rgba8888) {
|
||||
float r = ((rgba8888 & 0xff000000) >> 24) / 255f;
|
||||
float g = ((rgba8888 & 0x00ff0000) >> 16) / 255f;
|
||||
float b = ((rgba8888 & 0x0000ff00) >> 8) / 255f;
|
||||
float a = ((rgba8888 & 0x000000ff)) / 255f;
|
||||
return new Color32F(r, g, b, a);
|
||||
}
|
||||
|
||||
public static Color32F XRGB888ToColor (this uint xrgb888) {
|
||||
float r = ((xrgb888 & 0x00ff0000) >> 16) / 255f;
|
||||
float g = ((xrgb888 & 0x0000ff00) >> 8) / 255f;
|
||||
float b = ((xrgb888 & 0x000000ff)) / 255f;
|
||||
return new Color32F(r, g, b);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3eb49ca43afc71d41bdadef9155891e0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,98 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated April 5, 2025. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2026, 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_3_OR_NEWER
|
||||
#define IS_UNITY
|
||||
#endif
|
||||
|
||||
#if !IS_UNITY
|
||||
namespace Spine {
|
||||
/// <summary>
|
||||
/// 32 bit floating point color to be used with other game toolkits than Unity and XNA/Monogame.
|
||||
/// </summary>
|
||||
public struct Color32F {
|
||||
public float r, g, b, a;
|
||||
|
||||
public Color32F (float r, float g, float b, float a = 1.0f) {
|
||||
this.r = r;
|
||||
this.g = g;
|
||||
this.b = b;
|
||||
this.a = a;
|
||||
}
|
||||
|
||||
public override string ToString () {
|
||||
return string.Format("RGBA({0}, {1}, {2}, {3})", r, g, b, a);
|
||||
}
|
||||
|
||||
public static Color32F operator + (Color32F c1, Color32F c2) {
|
||||
return new Color32F(c1.r + c2.r, c1.g + c2.g, c1.b + c2.b, c1.a + c2.a);
|
||||
}
|
||||
|
||||
public static Color32F operator - (Color32F c1, Color32F c2) {
|
||||
return new Color32F(c1.r - c2.r, c1.g - c2.g, c1.b - c2.b, c1.a - c2.a);
|
||||
}
|
||||
|
||||
public static Color32F operator * (Color32F c1, Color32F c2) {
|
||||
return new Color32F(c1.r * c2.r, c1.g * c2.g, c1.b * c2.b, c1.a * c2.a);
|
||||
}
|
||||
}
|
||||
|
||||
static class ColorExtensions {
|
||||
public static Color32F Clamp (this Color32F color) {
|
||||
color.r = MathUtils.Clamp(color.r, 0, 1);
|
||||
color.g = MathUtils.Clamp(color.g, 0, 1);
|
||||
color.b = MathUtils.Clamp(color.b, 0, 1);
|
||||
color.a = MathUtils.Clamp(color.a, 0, 1);
|
||||
return color;
|
||||
}
|
||||
|
||||
public static Color32F ClampRGB (this Color32F color) {
|
||||
color.r = MathUtils.Clamp(color.r, 0, 1);
|
||||
color.g = MathUtils.Clamp(color.g, 0, 1);
|
||||
color.b = MathUtils.Clamp(color.b, 0, 1);
|
||||
return color;
|
||||
}
|
||||
|
||||
public static Color32F RGBA8888ToColor (this uint rgba8888) {
|
||||
float r = ((rgba8888 & 0xff000000) >> 24) / 255f;
|
||||
float g = ((rgba8888 & 0x00ff0000) >> 16) / 255f;
|
||||
float b = ((rgba8888 & 0x0000ff00) >> 8) / 255f;
|
||||
float a = ((rgba8888 & 0x000000ff)) / 255f;
|
||||
return new Color32F(r, g, b, a);
|
||||
}
|
||||
|
||||
public static Color32F XRGB888ToColor (this uint xrgb888) {
|
||||
float r = ((xrgb888 & 0x00ff0000) >> 16) / 255f;
|
||||
float g = ((xrgb888 & 0x0000ff00) >> 8) / 255f;
|
||||
float b = ((xrgb888 & 0x000000ff)) / 255f;
|
||||
return new Color32F(r, g, b);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b6c7cc15ba45b0045a609f65b360220a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,70 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated April 5, 2025. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2026, 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_3_OR_NEWER
|
||||
#define IS_UNITY
|
||||
#endif
|
||||
|
||||
#if IS_UNITY
|
||||
namespace Spine {
|
||||
using Color32F = UnityEngine.Color;
|
||||
|
||||
static class ColorExtensions {
|
||||
public static Color32F Clamp (this Color32F color) {
|
||||
color.r = MathUtils.Clamp(color.r, 0, 1);
|
||||
color.g = MathUtils.Clamp(color.g, 0, 1);
|
||||
color.b = MathUtils.Clamp(color.b, 0, 1);
|
||||
color.a = MathUtils.Clamp(color.a, 0, 1);
|
||||
return color;
|
||||
}
|
||||
|
||||
public static Color32F ClampRGB (this Color32F color) {
|
||||
color.r = MathUtils.Clamp(color.r, 0, 1);
|
||||
color.g = MathUtils.Clamp(color.g, 0, 1);
|
||||
color.b = MathUtils.Clamp(color.b, 0, 1);
|
||||
return color;
|
||||
}
|
||||
|
||||
public static Color32F RGBA8888ToColor(this uint rgba8888) {
|
||||
float r = ((rgba8888 & 0xff000000) >> 24) / 255f;
|
||||
float g = ((rgba8888 & 0x00ff0000) >> 16) / 255f;
|
||||
float b = ((rgba8888 & 0x0000ff00) >> 8) / 255f;
|
||||
float a = ((rgba8888 & 0x000000ff)) / 255f;
|
||||
return new Color32F(r, g, b, a);
|
||||
}
|
||||
|
||||
public static Color32F XRGB888ToColor (this uint xrgb888) {
|
||||
float r = ((xrgb888 & 0x00ff0000) >> 16) / 255f;
|
||||
float g = ((xrgb888 & 0x0000ff00) >> 8) / 255f;
|
||||
float b = ((xrgb888 & 0x000000ff)) / 255f;
|
||||
return new Color32F(r, g, b);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c96d35d696c85a44ab68037d151dde5d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,63 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated April 5, 2025. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2026, 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 interface IConstraint : IPosedActive, IPosed {
|
||||
IConstraintData IData { get; }
|
||||
IConstraint Copy (Skeleton skeleton);
|
||||
bool IsSourceActive { get; }
|
||||
void Sort (Skeleton skeleton);
|
||||
void Update (Skeleton skeleton, Physics physics);
|
||||
}
|
||||
|
||||
/// <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 abstract class Constraint<T, D, P> : PosedActive<D, P>, IUpdate, IConstraint
|
||||
where T : Constraint<T, D, P>
|
||||
where D : ConstraintData<T, P>
|
||||
where P : IPose<P> {
|
||||
|
||||
public Constraint (D data, P pose, P constrained)
|
||||
: base(data, pose, constrained) {
|
||||
}
|
||||
|
||||
public IConstraintData IData { get { return data; } }
|
||||
abstract public IConstraint Copy (Skeleton skeleton);
|
||||
abstract public void Sort (Skeleton skeleton);
|
||||
public virtual bool IsSourceActive { get { return true; } }
|
||||
abstract public void Update (Skeleton skeleton, Physics physics);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 66115e19768098240ace7f8ad9cb4d4c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,60 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated April 5, 2025. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2026, 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>
|
||||
/// Determines how the <see cref="BonePose.scaleY"/> changes when <see cref="BonePose.ScaleX"/> is set.
|
||||
/// </summary>
|
||||
public enum ScaleYMode {
|
||||
/// <summary>scaleY is not changed.</summary>
|
||||
None,
|
||||
/// <summary>scaleY is multiplied by the scaleX factor, preserving the bone's aspect ratio.</summary>
|
||||
Uniform,
|
||||
/// <summary>scaleY is divided by the scaleX factor, preserving the bone's area.</summary>
|
||||
Volume
|
||||
}
|
||||
|
||||
public interface IConstraintData : IPosedData {
|
||||
string Name { get; }
|
||||
IConstraint Create (Skeleton skeleton);
|
||||
}
|
||||
|
||||
public abstract class ConstraintData<T, P> : PosedData<P>, IConstraintData
|
||||
where T : IConstraint
|
||||
where P : IPose<P> {
|
||||
public ConstraintData (string name, P setup)
|
||||
: base(name, setup) {
|
||||
}
|
||||
|
||||
abstract public IConstraint Create (Skeleton skeleton);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 510354e70dba52c49b35b2c27cd8ed3c
|
||||
timeCreated: 1560867071
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,94 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated April 5, 2025. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2026, 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 the skeleton's draw order, which is the order that each slot's attachment is rendered.
|
||||
/// </summary>
|
||||
public class DrawOrder {
|
||||
internal readonly ExposedList<Slot> setupPose, pose, constrainedPose;
|
||||
internal ExposedList<Slot> appliedPose;
|
||||
|
||||
public DrawOrder (ExposedList<Slot> setupPose) {
|
||||
this.setupPose = setupPose;
|
||||
pose = new ExposedList<Slot>(setupPose);
|
||||
constrainedPose = new ExposedList<Slot>();
|
||||
appliedPose = pose;
|
||||
}
|
||||
|
||||
/// <summary>Sets the unconstrained draw order to the setup pose order.</summary>
|
||||
public void SetupPose () {
|
||||
pose.EnsureSize(setupPose.Count);
|
||||
Array.Copy(setupPose.Items, 0, pose.Items, 0, setupPose.Count);
|
||||
}
|
||||
|
||||
/// <summary>The unconstrained draw order, set by animations and application code.</summary>
|
||||
public ExposedList<Slot> Pose {
|
||||
get {
|
||||
return pose;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The constrained draw order for rendering. If no constraints modify the draw order, this is the same as <see cref="pose"/>.
|
||||
/// Otherwise it is a copy of <see cref="pose"/> modified by constraints.
|
||||
/// </summary>
|
||||
public ExposedList<Slot> AppliedPose {
|
||||
get {
|
||||
return appliedPose;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the applied pose to the unconstrained pose, for when no constraints will modify the draw order.
|
||||
/// </summary>
|
||||
internal void Unconstrained () {
|
||||
appliedPose = pose;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the applied pose to the constrained pose, in anticipation of the applied pose being modified by constraints.
|
||||
/// </summary>
|
||||
internal void Constrained () {
|
||||
appliedPose = constrainedPose;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies the unconstrained pose to the constrained pose, as a starting point for constraints to be applied.
|
||||
/// </summary>
|
||||
internal void ResetConstrained () { // Port: resetConstrained
|
||||
constrainedPose.EnsureSize(pose.Count);
|
||||
Array.Copy(pose.Items, 0, constrainedPose.Items, 0, pose.Count);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 589afc4ff646f4c4fb8d1b6084d660a6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,71 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated April 5, 2025. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2026, 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>Fired by <see cref="EventTimeline"/> when specific animation times are reached.
|
||||
/// <para>See <see cref="Timeline.Apply(Skeleton, float, float, ExposedList{Event}, float, bool, bool, bool, bool)"/> and
|
||||
/// <a href="https://esotericsoftware.com/spine-events">Events</a> in the Spine User Guide.</para></summary>
|
||||
public class Event {
|
||||
internal readonly float time;
|
||||
internal readonly EventData data;
|
||||
internal int intValue;
|
||||
internal float floatValue;
|
||||
internal string stringValue;
|
||||
internal float volume, balance;
|
||||
|
||||
/// <summary>The event's setup pose data.</summary>
|
||||
public EventData Data { get { return data; } }
|
||||
/// <summary>The animation time this event was keyed, or -1 for the setup pose.</summary>
|
||||
public float Time { get { return time; } }
|
||||
|
||||
/// <summary>The integer payload for this event.</summary>
|
||||
public int Int { get { return intValue; } set { intValue = value; } }
|
||||
/// <summary>The float payload for this event.</summary>
|
||||
public float Float { get { return floatValue; } set { floatValue = value; } }
|
||||
/// <summary>The string payload for this event.</summary>
|
||||
public string String { get { return stringValue; } set { stringValue = value; } }
|
||||
|
||||
/// <summary>If an audio path is set, the volume for the audio.</summary>
|
||||
public float Volume { get { return volume; } set { volume = value; } }
|
||||
/// <summary>If an audio path is set, the left/right balance for the audio.</summary>
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dec0d9d780605944eb4514125ab6350b
|
||||
timeCreated: 1456265155
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,57 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated April 5, 2025. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2026, 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;
|
||||
internal readonly Event setupPose;
|
||||
|
||||
/// <summary>The setup values that are shared by all events with this data.</summary>
|
||||
public Event SetupPose { get { return setupPose; } }
|
||||
|
||||
/// <summary>The name of the event, unique across all events in the skeleton.
|
||||
/// <para>See <see cref="SkeletonData.FindEvent(string)"/>.</para></summary>
|
||||
public string Name { get { return name; } }
|
||||
|
||||
/// <summary>Path to an audio file relative to the audio folder as defined in Spine.</summary>
|
||||
public string AudioPath { get; set; }
|
||||
|
||||
public EventData (string name) {
|
||||
setupPose = new Event(-1, this);
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
override public string ToString () {
|
||||
return Name;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 37bbfb9fb268a644ba75052961a42b81
|
||||
timeCreated: 1456265153
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,646 @@
|
||||
//
|
||||
// System.Collections.Generic.List
|
||||
//
|
||||
// Authors:
|
||||
// Ben Maurer (bmaurer@ximian.com)
|
||||
// Martin Baulig (martin@ximian.com)
|
||||
// Carlos Alberto Cortez (calberto.cortez@gmail.com)
|
||||
// David Waite (mass@akuma.org)
|
||||
//
|
||||
// Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com)
|
||||
// Copyright (C) 2005 David Waite
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Spine {
|
||||
[DebuggerDisplay("Count={Count}")]
|
||||
public class ExposedList<T> : IEnumerable<T> {
|
||||
public T[] Items;
|
||||
public int Count;
|
||||
private const int DefaultCapacity = 4;
|
||||
private static readonly T[] EmptyArray = new T[0];
|
||||
private int version;
|
||||
|
||||
public ExposedList () {
|
||||
Items = EmptyArray;
|
||||
}
|
||||
|
||||
public ExposedList (IEnumerable<T> collection) {
|
||||
CheckCollection(collection);
|
||||
|
||||
// initialize to needed size (if determinable)
|
||||
ICollection<T> c = collection as ICollection<T>;
|
||||
if (c == null) {
|
||||
Items = EmptyArray;
|
||||
AddEnumerable(collection);
|
||||
} else {
|
||||
Items = new T[c.Count];
|
||||
AddCollection(c);
|
||||
}
|
||||
}
|
||||
|
||||
public ExposedList (int capacity) {
|
||||
if (capacity < 0)
|
||||
throw new ArgumentOutOfRangeException("capacity");
|
||||
Items = new T[capacity];
|
||||
}
|
||||
|
||||
internal ExposedList (T[] data, int size) {
|
||||
Items = data;
|
||||
Count = size;
|
||||
}
|
||||
|
||||
public void Add (T item) {
|
||||
// If we check to see if we need to grow before trying to grow
|
||||
// we can speed things up by 25%
|
||||
if (Count == Items.Length)
|
||||
GrowIfNeeded(1);
|
||||
Items[Count++] = item;
|
||||
version++;
|
||||
}
|
||||
|
||||
public void GrowIfNeeded (int addedCount) {
|
||||
int minimumSize = Count + addedCount;
|
||||
if (minimumSize > Items.Length)
|
||||
Capacity = Math.Max(Math.Max(Capacity * 2, DefaultCapacity), minimumSize);
|
||||
}
|
||||
|
||||
public ExposedList<T> Resize (int newSize) {
|
||||
int itemsLength = Items.Length;
|
||||
T[] oldItems = Items;
|
||||
if (newSize > itemsLength) {
|
||||
Array.Resize(ref Items, newSize);
|
||||
} else if (newSize < itemsLength) {
|
||||
// Allow nulling of T reference type to allow GC.
|
||||
for (int i = newSize; i < itemsLength; i++)
|
||||
oldItems[i] = default(T);
|
||||
}
|
||||
Count = newSize;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ExposedList<T> EnsureSize (int newSize) {
|
||||
int itemsLength = Items.Length;
|
||||
if (newSize > itemsLength) {
|
||||
Array.Resize(ref Items, newSize);
|
||||
}
|
||||
Count = newSize;
|
||||
return this;
|
||||
}
|
||||
|
||||
public void EnsureCapacity (int min) {
|
||||
if (Items.Length < min) {
|
||||
int newCapacity = Items.Length == 0 ? DefaultCapacity : Items.Length * 2;
|
||||
//if ((uint)newCapacity > Array.MaxArrayLength) newCapacity = Array.MaxArrayLength;
|
||||
if (newCapacity < min) newCapacity = min;
|
||||
Capacity = newCapacity;
|
||||
}
|
||||
}
|
||||
|
||||
private void CheckRange (int index, int count) {
|
||||
if (index < 0)
|
||||
throw new ArgumentOutOfRangeException("index");
|
||||
|
||||
if (count < 0)
|
||||
throw new ArgumentOutOfRangeException("count");
|
||||
|
||||
if ((uint)index + (uint)count > (uint)Count)
|
||||
throw new ArgumentException("index and count exceed length of list");
|
||||
}
|
||||
|
||||
private void AddCollection (ICollection<T> collection) {
|
||||
int collectionCount = collection.Count;
|
||||
if (collectionCount == 0)
|
||||
return;
|
||||
|
||||
GrowIfNeeded(collectionCount);
|
||||
collection.CopyTo(Items, Count);
|
||||
Count += collectionCount;
|
||||
}
|
||||
|
||||
private void AddEnumerable (IEnumerable<T> enumerable) {
|
||||
foreach (T t in enumerable) {
|
||||
Add(t);
|
||||
}
|
||||
}
|
||||
|
||||
// Additional overload provided because ExposedList<T> only implements IEnumerable<T>,
|
||||
// leading to sub-optimal behavior: It grows multiple times as it assumes not
|
||||
// to know the final size ahead of insertion.
|
||||
public void AddRange (ExposedList<T> list) {
|
||||
CheckCollection(list);
|
||||
|
||||
int collectionCount = list.Count;
|
||||
if (collectionCount == 0)
|
||||
return;
|
||||
|
||||
GrowIfNeeded(collectionCount);
|
||||
list.CopyTo(Items, Count);
|
||||
Count += collectionCount;
|
||||
|
||||
version++;
|
||||
}
|
||||
|
||||
public void AddRange (IEnumerable<T> collection) {
|
||||
CheckCollection(collection);
|
||||
|
||||
ICollection<T> c = collection as ICollection<T>;
|
||||
if (c != null)
|
||||
AddCollection(c);
|
||||
else
|
||||
AddEnumerable(collection);
|
||||
version++;
|
||||
}
|
||||
|
||||
public int BinarySearch (T item) {
|
||||
return Array.BinarySearch<T>(Items, 0, Count, item);
|
||||
}
|
||||
|
||||
public int BinarySearch (T item, IComparer<T> comparer) {
|
||||
return Array.BinarySearch<T>(Items, 0, Count, item, comparer);
|
||||
}
|
||||
|
||||
public int BinarySearch (int index, int count, T item, IComparer<T> comparer) {
|
||||
CheckRange(index, count);
|
||||
return Array.BinarySearch<T>(Items, index, count, item, comparer);
|
||||
}
|
||||
|
||||
public void Clear (bool clearArray = true) {
|
||||
if (clearArray)
|
||||
Array.Clear(Items, 0, Items.Length);
|
||||
|
||||
Count = 0;
|
||||
version++;
|
||||
}
|
||||
|
||||
public bool Contains (T item) {
|
||||
return Array.IndexOf<T>(Items, item, 0, Count) != -1;
|
||||
}
|
||||
|
||||
public ExposedList<TOutput> ConvertAll<TOutput> (Converter<T, TOutput> converter) {
|
||||
if (converter == null)
|
||||
throw new ArgumentNullException("converter");
|
||||
ExposedList<TOutput> u = new ExposedList<TOutput>(Count);
|
||||
u.Count = Count;
|
||||
T[] items = Items;
|
||||
TOutput[] uItems = u.Items;
|
||||
for (int i = 0; i < Count; i++)
|
||||
uItems[i] = converter(items[i]);
|
||||
return u;
|
||||
}
|
||||
|
||||
public void CopyTo (T[] array) {
|
||||
Array.Copy(Items, 0, array, 0, Count);
|
||||
}
|
||||
|
||||
public void CopyTo (T[] array, int arrayIndex) {
|
||||
Array.Copy(Items, 0, array, arrayIndex, Count);
|
||||
}
|
||||
|
||||
public void CopyTo (int index, T[] array, int arrayIndex, int count) {
|
||||
CheckRange(index, count);
|
||||
Array.Copy(Items, index, array, arrayIndex, count);
|
||||
}
|
||||
|
||||
public bool Exists (Predicate<T> match) {
|
||||
CheckMatch(match);
|
||||
return GetIndex(0, Count, match) != -1;
|
||||
}
|
||||
|
||||
public T Find (Predicate<T> match) {
|
||||
CheckMatch(match);
|
||||
int i = GetIndex(0, Count, match);
|
||||
return (i != -1) ? Items[i] : default(T);
|
||||
}
|
||||
|
||||
private static void CheckMatch (Predicate<T> match) {
|
||||
if (match == null)
|
||||
throw new ArgumentNullException("match");
|
||||
}
|
||||
|
||||
public ExposedList<T> FindAll (Predicate<T> match) {
|
||||
CheckMatch(match);
|
||||
return FindAllList(match);
|
||||
}
|
||||
|
||||
private ExposedList<T> FindAllList (Predicate<T> match) {
|
||||
ExposedList<T> results = new ExposedList<T>();
|
||||
for (int i = 0; i < Count; i++)
|
||||
if (match(Items[i]))
|
||||
results.Add(Items[i]);
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
public int FindIndex (Predicate<T> match) {
|
||||
CheckMatch(match);
|
||||
return GetIndex(0, Count, match);
|
||||
}
|
||||
|
||||
public int FindIndex (int startIndex, Predicate<T> match) {
|
||||
CheckMatch(match);
|
||||
CheckIndex(startIndex);
|
||||
return GetIndex(startIndex, Count - startIndex, match);
|
||||
}
|
||||
|
||||
public int FindIndex (int startIndex, int count, Predicate<T> match) {
|
||||
CheckMatch(match);
|
||||
CheckRange(startIndex, count);
|
||||
return GetIndex(startIndex, count, match);
|
||||
}
|
||||
|
||||
private int GetIndex (int startIndex, int count, Predicate<T> match) {
|
||||
int end = startIndex + count;
|
||||
for (int i = startIndex; i < end; i++)
|
||||
if (match(Items[i]))
|
||||
return i;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
public T FindLast (Predicate<T> match) {
|
||||
CheckMatch(match);
|
||||
int i = GetLastIndex(0, Count, match);
|
||||
return i == -1 ? default(T) : Items[i];
|
||||
}
|
||||
|
||||
public int FindLastIndex (Predicate<T> match) {
|
||||
CheckMatch(match);
|
||||
return GetLastIndex(0, Count, match);
|
||||
}
|
||||
|
||||
public int FindLastIndex (int startIndex, Predicate<T> match) {
|
||||
CheckMatch(match);
|
||||
CheckIndex(startIndex);
|
||||
return GetLastIndex(0, startIndex + 1, match);
|
||||
}
|
||||
|
||||
public int FindLastIndex (int startIndex, int count, Predicate<T> match) {
|
||||
CheckMatch(match);
|
||||
int start = startIndex - count + 1;
|
||||
CheckRange(start, count);
|
||||
return GetLastIndex(start, count, match);
|
||||
}
|
||||
|
||||
private int GetLastIndex (int startIndex, int count, Predicate<T> match) {
|
||||
// unlike FindLastIndex, takes regular params for search range
|
||||
for (int i = startIndex + count; i != startIndex;)
|
||||
if (match(Items[--i]))
|
||||
return i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
public void ForEach (Action<T> action) {
|
||||
if (action == null)
|
||||
throw new ArgumentNullException("action");
|
||||
for (int i = 0; i < Count; i++)
|
||||
action(Items[i]);
|
||||
}
|
||||
|
||||
public Enumerator GetEnumerator () {
|
||||
return new Enumerator(this);
|
||||
}
|
||||
|
||||
public ExposedList<T> GetRange (int index, int count) {
|
||||
CheckRange(index, count);
|
||||
T[] tmpArray = new T[count];
|
||||
Array.Copy(Items, index, tmpArray, 0, count);
|
||||
return new ExposedList<T>(tmpArray, count);
|
||||
}
|
||||
|
||||
public int IndexOf (T item) {
|
||||
return Array.IndexOf<T>(Items, item, 0, Count);
|
||||
}
|
||||
|
||||
public int IndexOf (T item, int index) {
|
||||
CheckIndex(index);
|
||||
return Array.IndexOf<T>(Items, item, index, Count - index);
|
||||
}
|
||||
|
||||
public int IndexOf (T item, int index, int count) {
|
||||
if (index < 0)
|
||||
throw new ArgumentOutOfRangeException("index");
|
||||
|
||||
if (count < 0)
|
||||
throw new ArgumentOutOfRangeException("count");
|
||||
|
||||
if ((uint)index + (uint)count > (uint)Count)
|
||||
throw new ArgumentOutOfRangeException("index and count exceed length of list");
|
||||
|
||||
return Array.IndexOf<T>(Items, item, index, count);
|
||||
}
|
||||
|
||||
private void Shift (int start, int delta) {
|
||||
if (delta < 0)
|
||||
start -= delta;
|
||||
|
||||
if (start < Count)
|
||||
Array.Copy(Items, start, Items, start + delta, Count - start);
|
||||
|
||||
Count += delta;
|
||||
|
||||
if (delta < 0)
|
||||
Array.Clear(Items, Count, -delta);
|
||||
}
|
||||
|
||||
private void CheckIndex (int index) {
|
||||
if (index < 0 || (uint)index > (uint)Count)
|
||||
throw new ArgumentOutOfRangeException("index");
|
||||
}
|
||||
|
||||
public void Insert (int index, T item) {
|
||||
CheckIndex(index);
|
||||
if (Count == Items.Length)
|
||||
GrowIfNeeded(1);
|
||||
Shift(index, 1);
|
||||
Items[index] = item;
|
||||
version++;
|
||||
}
|
||||
|
||||
private void CheckCollection (IEnumerable<T> collection) {
|
||||
if (collection == null)
|
||||
throw new ArgumentNullException("collection");
|
||||
}
|
||||
|
||||
public void InsertRange (int index, IEnumerable<T> collection) {
|
||||
CheckCollection(collection);
|
||||
CheckIndex(index);
|
||||
if (collection == this) {
|
||||
T[] buffer = new T[Count];
|
||||
CopyTo(buffer, 0);
|
||||
GrowIfNeeded(Count);
|
||||
Shift(index, buffer.Length);
|
||||
Array.Copy(buffer, 0, Items, index, buffer.Length);
|
||||
} else {
|
||||
ICollection<T> c = collection as ICollection<T>;
|
||||
if (c != null)
|
||||
InsertCollection(index, c);
|
||||
else
|
||||
InsertEnumeration(index, collection);
|
||||
}
|
||||
version++;
|
||||
}
|
||||
|
||||
private void InsertCollection (int index, ICollection<T> collection) {
|
||||
int collectionCount = collection.Count;
|
||||
GrowIfNeeded(collectionCount);
|
||||
|
||||
Shift(index, collectionCount);
|
||||
collection.CopyTo(Items, index);
|
||||
}
|
||||
|
||||
private void InsertEnumeration (int index, IEnumerable<T> enumerable) {
|
||||
foreach (T t in enumerable)
|
||||
Insert(index++, t);
|
||||
}
|
||||
|
||||
public int LastIndexOf (T item) {
|
||||
return Array.LastIndexOf<T>(Items, item, Count - 1, Count);
|
||||
}
|
||||
|
||||
public int LastIndexOf (T item, int index) {
|
||||
CheckIndex(index);
|
||||
return Array.LastIndexOf<T>(Items, item, index, index + 1);
|
||||
}
|
||||
|
||||
public int LastIndexOf (T item, int index, int count) {
|
||||
if (index < 0)
|
||||
throw new ArgumentOutOfRangeException("index", index, "index is negative");
|
||||
|
||||
if (count < 0)
|
||||
throw new ArgumentOutOfRangeException("count", count, "count is negative");
|
||||
|
||||
if (index - count + 1 < 0)
|
||||
throw new ArgumentOutOfRangeException("count", count, "count is too large");
|
||||
|
||||
return Array.LastIndexOf<T>(Items, item, index, count);
|
||||
}
|
||||
|
||||
public bool Remove (T item) {
|
||||
int loc = IndexOf(item);
|
||||
if (loc != -1)
|
||||
RemoveAt(loc);
|
||||
|
||||
return loc != -1;
|
||||
}
|
||||
|
||||
public int RemoveAll (Predicate<T> match) {
|
||||
CheckMatch(match);
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
|
||||
// Find the first item to remove
|
||||
for (i = 0; i < Count; i++)
|
||||
if (match(Items[i]))
|
||||
break;
|
||||
|
||||
if (i == Count)
|
||||
return 0;
|
||||
|
||||
version++;
|
||||
|
||||
// Remove any additional items
|
||||
for (j = i + 1; j < Count; j++) {
|
||||
if (!match(Items[j]))
|
||||
Items[i++] = Items[j];
|
||||
}
|
||||
if (j - i > 0)
|
||||
Array.Clear(Items, i, j - i);
|
||||
|
||||
Count = i;
|
||||
return (j - i);
|
||||
}
|
||||
|
||||
public void RemoveAt (int index) {
|
||||
if (index < 0 || (uint)index >= (uint)Count)
|
||||
throw new ArgumentOutOfRangeException("index");
|
||||
Shift(index, -1);
|
||||
Array.Clear(Items, Count, 1);
|
||||
version++;
|
||||
}
|
||||
|
||||
// Spine Added Method
|
||||
// Based on Stack<T>.Pop(); https://referencesource.microsoft.com/#mscorlib/system/collections/stack.cs
|
||||
/// <summary>Pops the last item of the list. If the list is empty, Pop throws an InvalidOperationException.</summary>
|
||||
public T Pop () {
|
||||
if (Count == 0)
|
||||
throw new InvalidOperationException("List is empty. Nothing to pop.");
|
||||
|
||||
int i = Count - 1;
|
||||
T item = Items[i];
|
||||
Items[i] = default(T);
|
||||
Count--;
|
||||
version++;
|
||||
return item;
|
||||
}
|
||||
|
||||
public void RemoveRange (int index, int count) {
|
||||
CheckRange(index, count);
|
||||
if (count > 0) {
|
||||
Shift(index, -count);
|
||||
Array.Clear(Items, Count, count);
|
||||
version++;
|
||||
}
|
||||
}
|
||||
|
||||
public void Reverse () {
|
||||
Array.Reverse(Items, 0, Count);
|
||||
version++;
|
||||
}
|
||||
|
||||
public void Reverse (int index, int count) {
|
||||
CheckRange(index, count);
|
||||
Array.Reverse(Items, index, count);
|
||||
version++;
|
||||
}
|
||||
|
||||
public void Sort () {
|
||||
Array.Sort<T>(Items, 0, Count, Comparer<T>.Default);
|
||||
version++;
|
||||
}
|
||||
|
||||
public void Sort (IComparer<T> comparer) {
|
||||
Array.Sort<T>(Items, 0, Count, comparer);
|
||||
version++;
|
||||
}
|
||||
|
||||
public void Sort (Comparison<T> comparison) {
|
||||
Array.Sort<T>(Items, comparison);
|
||||
version++;
|
||||
}
|
||||
|
||||
public void Sort (int index, int count, IComparer<T> comparer) {
|
||||
CheckRange(index, count);
|
||||
Array.Sort<T>(Items, index, count, comparer);
|
||||
version++;
|
||||
}
|
||||
|
||||
public T[] ToArray () {
|
||||
T[] t = new T[Count];
|
||||
Array.Copy(Items, t, Count);
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
public void TrimExcess () {
|
||||
Capacity = Count;
|
||||
}
|
||||
|
||||
public bool TrueForAll (Predicate<T> match) {
|
||||
CheckMatch(match);
|
||||
|
||||
for (int i = 0; i < Count; i++)
|
||||
if (!match(Items[i]))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public int Capacity {
|
||||
get {
|
||||
return Items.Length;
|
||||
}
|
||||
set {
|
||||
if ((uint)value < (uint)Count)
|
||||
throw new ArgumentOutOfRangeException();
|
||||
|
||||
Array.Resize(ref Items, value);
|
||||
}
|
||||
}
|
||||
|
||||
#region Interface implementations.
|
||||
|
||||
IEnumerator<T> IEnumerable<T>.GetEnumerator () {
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator () {
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public struct Enumerator : IEnumerator<T>, IDisposable {
|
||||
private ExposedList<T> l;
|
||||
private int next;
|
||||
private int ver;
|
||||
private T current;
|
||||
|
||||
internal Enumerator (ExposedList<T> l)
|
||||
: this() {
|
||||
this.l = l;
|
||||
ver = l.version;
|
||||
}
|
||||
|
||||
public void Dispose () {
|
||||
l = null;
|
||||
}
|
||||
|
||||
private void VerifyState () {
|
||||
if (l == null)
|
||||
throw new ObjectDisposedException(GetType().FullName);
|
||||
if (ver != l.version)
|
||||
throw new InvalidOperationException(
|
||||
"Collection was modified; enumeration operation may not execute.");
|
||||
}
|
||||
|
||||
public bool MoveNext () {
|
||||
VerifyState();
|
||||
|
||||
if (next < 0)
|
||||
return false;
|
||||
|
||||
if (next < l.Count) {
|
||||
current = l.Items[next++];
|
||||
return true;
|
||||
}
|
||||
|
||||
next = -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
public T Current {
|
||||
get {
|
||||
return current;
|
||||
}
|
||||
}
|
||||
|
||||
void IEnumerator.Reset () {
|
||||
VerifyState();
|
||||
next = 0;
|
||||
}
|
||||
|
||||
object IEnumerator.Current {
|
||||
get {
|
||||
VerifyState();
|
||||
if (next <= 0)
|
||||
throw new InvalidOperationException();
|
||||
return current;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 89690af94a880744989712505f2957b1
|
||||
timeCreated: 1456265154
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,38 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated April 5, 2025. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2026, 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 interface for an object representing a pose.</summary>
|
||||
public interface IPose<P> {
|
||||
/// <summary>Sets this pose to the specified pose.</summary>
|
||||
void Set (P pose);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 114be1b77921d6645ac5974d5b33f2eb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,37 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated April 5, 2025. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2026, 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(Physics)"/>.</summary>
|
||||
public interface IUpdate {
|
||||
/// <param name="physics">Determines how physics and other non-deterministic updates are applied.</param>
|
||||
void Update (Skeleton skeleton, Physics physics);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fba7d1e93605e0f499b6208390e798c2
|
||||
timeCreated: 1560785125
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,332 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated April 5, 2025. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2026, 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>
|
||||
/// Adjusts the local rotation of 1 or 2 constrained bones so the world position of 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 : Constraint<IkConstraint, IkConstraintData, IkConstraintPose> {
|
||||
internal readonly ExposedList<BonePose> bones;
|
||||
internal Bone target;
|
||||
|
||||
public IkConstraint (IkConstraintData data, Skeleton skeleton)
|
||||
: base(data, new IkConstraintPose(), new IkConstraintPose()) {
|
||||
if (skeleton == null) throw new ArgumentNullException("skeleton", "skeleton cannot be null.");
|
||||
|
||||
bones = new ExposedList<BonePose>(data.bones.Count);
|
||||
foreach (BoneData boneData in data.bones)
|
||||
bones.Add(skeleton.bones.Items[boneData.index].constrainedPose);
|
||||
|
||||
target = skeleton.bones.Items[data.target.index];
|
||||
}
|
||||
|
||||
override public IConstraint Copy (Skeleton skeleton) {
|
||||
var copy = new IkConstraint(data, skeleton);
|
||||
copy.pose.Set(pose);
|
||||
return copy;
|
||||
}
|
||||
|
||||
/// <summary>Applies the constraint to the constrained bones.</summary>
|
||||
override public void Update (Skeleton skeleton, Physics physics) {
|
||||
IkConstraintPose p = appliedPose;
|
||||
if (p.mix == 0) return;
|
||||
BonePose target = this.target.appliedPose;
|
||||
BonePose[] bones = this.bones.Items;
|
||||
switch (this.bones.Count) {
|
||||
case 1:
|
||||
Apply(skeleton, bones[0], target.worldX, target.worldY, p.compress, p.stretch, data.scaleY, p.mix);
|
||||
break;
|
||||
case 2:
|
||||
Apply(skeleton, bones[0], bones[1], target.worldX, target.worldY, p.bendDirection, p.stretch, data.scaleY,
|
||||
p.softness, p.mix);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
override public void Sort (Skeleton skeleton) {
|
||||
skeleton.SortBone(target);
|
||||
Bone parent = bones.Items[0].bone;
|
||||
skeleton.SortBone(parent);
|
||||
skeleton.updateCache.Add(this);
|
||||
parent.sorted = false;
|
||||
skeleton.SortReset(parent.children);
|
||||
skeleton.Constrained(parent);
|
||||
if (bones.Count > 1) skeleton.Constrained(bones.Items[1].bone);
|
||||
}
|
||||
|
||||
override public bool IsSourceActive { get { return target.active; } }
|
||||
|
||||
/// <summary>The bones that will be modified by this IK constraint.</summary>
|
||||
public ExposedList<BonePose> Bones {
|
||||
get { return bones; }
|
||||
}
|
||||
|
||||
/// <summary>The bone that is the IK target.</summary>
|
||||
public Bone Target {
|
||||
get { return target; }
|
||||
set { target = value; }
|
||||
}
|
||||
|
||||
/// <summary>Applies 1 bone IK. The target is specified in the world coordinate system.</summary>
|
||||
static public void Apply (Skeleton skeleton, BonePose bone, float targetX, float targetY, bool compress, bool stretch,
|
||||
ScaleYMode scaleY, float mix) {
|
||||
if (bone == null) throw new ArgumentNullException("bone", "bone cannot be null.");
|
||||
bone.ModifyLocal(skeleton);
|
||||
BonePose p = bone.bone.parent.appliedPose;
|
||||
|
||||
float pa = p.a, pb = p.b, pc = p.c, pd = p.d;
|
||||
float rotationIK = -bone.shearX - bone.rotation, tx, ty;
|
||||
switch (bone.inherit) {
|
||||
case Inherit.OnlyTranslation:
|
||||
tx = (targetX - bone.worldX) * Math.Sign(skeleton.ScaleX);
|
||||
ty = (targetY - bone.worldY) * Math.Sign(skeleton.ScaleY);
|
||||
break;
|
||||
case Inherit.NoRotationOrReflection: {
|
||||
float s = Math.Abs(pa * pd - pb * pc) / Math.Max(MathUtils.Epsilon, pa * pa + pc * pc);
|
||||
float sa = pa / skeleton.scaleX;
|
||||
float sc = pc / skeleton.ScaleY;
|
||||
pb = -sc * s * skeleton.scaleX;
|
||||
pd = sa * s * skeleton.ScaleY;
|
||||
rotationIK += MathUtils.Atan2Deg(sc, sa);
|
||||
goto default; // Fall through.
|
||||
}
|
||||
default: {
|
||||
float x = targetX - p.worldX, y = targetY - p.worldY;
|
||||
float d = pa * pd - pb * pc;
|
||||
if (Math.Abs(d) <= MathUtils.Epsilon) {
|
||||
tx = 0;
|
||||
ty = 0;
|
||||
} else {
|
||||
tx = (x * pd - y * pb) / d - bone.x;
|
||||
ty = (y * pa - x * pc) / d - bone.y;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
rotationIK += MathUtils.Atan2Deg(ty, tx);
|
||||
if (bone.scaleX < 0) rotationIK += 180;
|
||||
if (rotationIK > 180)
|
||||
rotationIK -= 360;
|
||||
else if (rotationIK <= -180) //
|
||||
rotationIK += 360;
|
||||
bone.rotation += rotationIK * mix;
|
||||
if (compress || stretch) {
|
||||
switch (bone.inherit) {
|
||||
case Inherit.NoScale:
|
||||
case Inherit.NoScaleOrReflection:
|
||||
tx = targetX - bone.worldX;
|
||||
ty = targetY - bone.worldY;
|
||||
break;
|
||||
}
|
||||
float b = bone.bone.data.length * bone.scaleX;
|
||||
if (b > MathUtils.Epsilon) {
|
||||
float dd = tx * tx + ty * ty;
|
||||
if ((compress && dd < b * b) || (stretch && dd > b * b)) {
|
||||
float s = ((float)Math.Sqrt(dd) / b - 1) * mix + 1;
|
||||
bone.scaleX *= s;
|
||||
switch (scaleY) {
|
||||
case ScaleYMode.Uniform:
|
||||
bone.scaleY *= s;
|
||||
break;
|
||||
case ScaleYMode.Volume:
|
||||
bone.scaleY /= s < 0.7f ? 0.25f + 0.642857f * s : s;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <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 (Skeleton skeleton, BonePose parent, BonePose child, float targetX, float targetY, int bendDir,
|
||||
bool stretch, ScaleYMode scaleY, float softness, float mix) {
|
||||
if (parent == null) throw new ArgumentNullException("parent", "parent cannot be null.");
|
||||
if (child == null) throw new ArgumentNullException("child", "child cannot be null.");
|
||||
if (parent.inherit != Inherit.Normal || child.inherit != Inherit.Normal) return;
|
||||
parent.ModifyLocal(skeleton);
|
||||
child.ModifyLocal(skeleton);
|
||||
float px = parent.x, py = parent.y, psx = parent.scaleX, psy = parent.scaleY, csx = child.scaleX;
|
||||
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 cwx, cwy, a = parent.a, b = parent.b, c = parent.c, d = parent.d;
|
||||
bool u = Math.Abs(psx - psy) <= MathUtils.Epsilon;
|
||||
if (!u || stretch) {
|
||||
child.y = 0;
|
||||
cwx = a * child.x + parent.worldX;
|
||||
cwy = c * child.x + parent.worldY;
|
||||
} else {
|
||||
cwx = a * child.x + b * child.y + parent.worldX;
|
||||
cwy = c * child.x + d * child.y + parent.worldY;
|
||||
}
|
||||
BonePose pp = parent.bone.parent.appliedPose;
|
||||
a = pp.a;
|
||||
b = pp.b;
|
||||
c = pp.c;
|
||||
d = pp.d;
|
||||
float id = a * d - b * c, x = cwx - pp.worldX, y = cwy - pp.worldY;
|
||||
id = Math.Abs(id) <= MathUtils.Epsilon ? 0 : 1 / id;
|
||||
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.bone.data.length * csx, a1, a2;
|
||||
if (l1 < MathUtils.Epsilon) {
|
||||
Apply(skeleton, parent, targetX, targetY, false, stretch, ScaleYMode.None, mix);
|
||||
child.rotation = 0;
|
||||
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) * mix + 1;
|
||||
parent.scaleX *= a;
|
||||
switch (scaleY) {
|
||||
case ScaleYMode.Uniform:
|
||||
parent.scaleY *= a;
|
||||
break;
|
||||
case ScaleYMode.Volume:
|
||||
parent.scaleY /= a < 0.7f ? 0.25f + 0.642857f * a : a;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} 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;
|
||||
r0 = dd - r * r;
|
||||
if (r0 >= 0) {
|
||||
y = (float)Math.Sqrt(r0) * 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(child.y, child.x) * s2;
|
||||
a1 = (a1 - os) * MathUtils.RadDeg + os1 - parent.rotation;
|
||||
if (a1 > 180)
|
||||
a1 -= 360;
|
||||
else if (a1 <= -180)
|
||||
a1 += 360;
|
||||
parent.rotation += a1 * mix;
|
||||
a2 = ((a2 + os) * MathUtils.RadDeg - child.shearX) * s2 + os2 - child.rotation;
|
||||
if (a2 > 180)
|
||||
a2 -= 360;
|
||||
else if (a2 <= -180)
|
||||
a2 += 360;
|
||||
child.rotation += a2 * mix;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 916f8e6534860cc40824adfc2916baa7
|
||||
timeCreated: 1456265155
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,68 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated April 5, 2025. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2026, 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<IkConstraint, IkConstraintPose> {
|
||||
internal ExposedList<BoneData> bones = new ExposedList<BoneData>(2);
|
||||
internal BoneData target;
|
||||
internal ScaleYMode scaleY = ScaleYMode.None;
|
||||
|
||||
public IkConstraintData (string name)
|
||||
: base(name, new IkConstraintPose()) {
|
||||
}
|
||||
|
||||
override public IConstraint Create (Skeleton skeleton) {
|
||||
return new IkConstraint(this, skeleton);
|
||||
}
|
||||
|
||||
/// <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>
|
||||
/// Determines how the <see cref="BonePose.scaleY"/> changes when <see cref="IkConstraintPose.Compress"/> or
|
||||
/// <see cref="IkConstraintPose.Stretch"/> set <see cref="BonePose.ScaleX"/>.
|
||||
/// </summary>
|
||||
public ScaleYMode ScaleY {
|
||||
get { return scaleY; }
|
||||
set { scaleY = value; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 94ad1e9256073264785f806086a000ba
|
||||
timeCreated: 1456265155
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,87 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated April 5, 2025. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2026, 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 pose for an IK constraint.</summary>
|
||||
public class IkConstraintPose : IPose<IkConstraintPose> {
|
||||
internal int bendDirection;
|
||||
internal bool compress, stretch;
|
||||
internal float mix, softness;
|
||||
|
||||
public void Set (IkConstraintPose pose) {
|
||||
mix = pose.mix;
|
||||
softness = pose.softness;
|
||||
bendDirection = pose.bendDirection;
|
||||
compress = pose.compress;
|
||||
stretch = pose.stretch;
|
||||
}
|
||||
|
||||
/// <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; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f7b8113cda0e502458c2084ec31e0cca
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,457 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated April 5, 2025. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2026, 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:
|
||||
* https://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>
|
||||
/// Takes a linear value in the range of 0-1 and outputs a (usually) non-linear, interpolated value.
|
||||
/// </summary>
|
||||
public abstract class Interpolation {
|
||||
/// <param name="a">Alpha value between 0 and 1.</param>
|
||||
abstract public float Apply (float a);
|
||||
|
||||
/// <param name="a">Alpha value between 0 and 1.</param>
|
||||
public float Apply (float start, float end, float a) {
|
||||
return start + (end - start) * Apply(a);
|
||||
}
|
||||
|
||||
public static readonly Interpolation Linear = new LinearInterpolation();
|
||||
/// <summary>Aka "smoothstep".</summary>
|
||||
public static readonly Interpolation Smooth = new SmoothInterpolation();
|
||||
public static readonly Interpolation Smooth2 = new Smooth2Interpolation();
|
||||
/// <summary>By Ken Perlin.</summary>
|
||||
public static readonly Interpolation Smoother = new SmootherInterpolation();
|
||||
public static readonly Interpolation Fade = Smoother;
|
||||
|
||||
public static readonly PowInterpolation Pow2 = new PowInterpolation(2);
|
||||
/// <summary>Slow, then fast.</summary>
|
||||
public static readonly PowInInterpolation Pow2In = new PowInInterpolation(2);
|
||||
public static readonly PowInInterpolation SlowFast = Pow2In;
|
||||
/// <summary>Fast, then slow.</summary>
|
||||
public static readonly PowOutInterpolation Pow2Out = new PowOutInterpolation(2);
|
||||
public static readonly PowOutInterpolation FastSlow = Pow2Out;
|
||||
public static readonly Interpolation Pow2InInverse = new Pow2InInverseInterpolation();
|
||||
public static readonly Interpolation Pow2OutInverse = new Pow2OutInverseInterpolation();
|
||||
|
||||
public static readonly PowInterpolation Pow3 = new PowInterpolation(3);
|
||||
public static readonly PowInInterpolation Pow3In = new PowInInterpolation(3);
|
||||
public static readonly PowOutInterpolation Pow3Out = new PowOutInterpolation(3);
|
||||
public static readonly Interpolation Pow3InInverse = new Pow3InInverseInterpolation();
|
||||
public static readonly Interpolation Pow3OutInverse = new Pow3OutInverseInterpolation();
|
||||
|
||||
public static readonly PowInterpolation Pow4 = new PowInterpolation(4);
|
||||
public static readonly PowInInterpolation Pow4In = new PowInInterpolation(4);
|
||||
public static readonly PowOutInterpolation Pow4Out = new PowOutInterpolation(4);
|
||||
|
||||
public static readonly PowInterpolation Pow5 = new PowInterpolation(5);
|
||||
public static readonly PowInInterpolation Pow5In = new PowInInterpolation(5);
|
||||
public static readonly PowOutInterpolation Pow5Out = new PowOutInterpolation(5);
|
||||
|
||||
public static readonly Interpolation Sine = new SineInterpolation();
|
||||
public static readonly Interpolation SineIn = new SineInInterpolation();
|
||||
public static readonly Interpolation SineOut = new SineOutInterpolation();
|
||||
|
||||
public static readonly ExpInterpolation Exp10 = new ExpInterpolation(2, 10);
|
||||
public static readonly ExpInInterpolation Exp10In = new ExpInInterpolation(2, 10);
|
||||
public static readonly ExpOutInterpolation Exp10Out = new ExpOutInterpolation(2, 10);
|
||||
|
||||
public static readonly ExpInterpolation Exp5 = new ExpInterpolation(2, 5);
|
||||
public static readonly ExpInInterpolation Exp5In = new ExpInInterpolation(2, 5);
|
||||
public static readonly ExpOutInterpolation Exp5Out = new ExpOutInterpolation(2, 5);
|
||||
|
||||
public static readonly Interpolation Circle = new CircleInterpolation();
|
||||
public static readonly Interpolation CircleIn = new CircleInInterpolation();
|
||||
public static readonly Interpolation CircleOut = new CircleOutInterpolation();
|
||||
|
||||
public static readonly ElasticInterpolation Elastic = new ElasticInterpolation(2, 10, 7, 1);
|
||||
public static readonly ElasticInInterpolation ElasticIn = new ElasticInInterpolation(2, 10, 6, 1);
|
||||
public static readonly ElasticOutInterpolation ElasticOut = new ElasticOutInterpolation(2, 10, 7, 1);
|
||||
|
||||
public static readonly SwingInterpolation Swing = new SwingInterpolation(1.5f);
|
||||
public static readonly SwingInInterpolation SwingIn = new SwingInInterpolation(2f);
|
||||
public static readonly SwingOutInterpolation SwingOut = new SwingOutInterpolation(2f);
|
||||
|
||||
public static readonly BounceInterpolation Bounce = new BounceInterpolation(4);
|
||||
public static readonly BounceInInterpolation BounceIn = new BounceInInterpolation(4);
|
||||
public static readonly BounceOutInterpolation BounceOut = new BounceOutInterpolation(4);
|
||||
|
||||
#region Implementation Classes
|
||||
class LinearInterpolation : Interpolation {
|
||||
public override float Apply (float a) {
|
||||
return a;
|
||||
}
|
||||
}
|
||||
|
||||
class SmoothInterpolation : Interpolation {
|
||||
public override float Apply (float a) {
|
||||
return a * a * (3 - 2 * a);
|
||||
}
|
||||
}
|
||||
|
||||
class Smooth2Interpolation : Interpolation {
|
||||
public override float Apply (float a) {
|
||||
a = a * a * (3 - 2 * a);
|
||||
return a * a * (3 - 2 * a);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>By Ken Perlin.</summary>
|
||||
class SmootherInterpolation : Interpolation {
|
||||
public override float Apply (float a) {
|
||||
return a * a * a * (a * (a * 6 - 15) + 10);
|
||||
}
|
||||
}
|
||||
|
||||
public class PowInterpolation : Interpolation {
|
||||
protected readonly int power;
|
||||
|
||||
public PowInterpolation (int power) {
|
||||
this.power = power;
|
||||
}
|
||||
|
||||
public 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 PowInInterpolation : PowInterpolation {
|
||||
public PowInInterpolation (int power) : base(power) {
|
||||
}
|
||||
|
||||
public override float Apply (float a) {
|
||||
return (float)Math.Pow(a, power);
|
||||
}
|
||||
}
|
||||
|
||||
public class PowOutInterpolation : PowInterpolation {
|
||||
public PowOutInterpolation (int power) : base(power) {
|
||||
}
|
||||
|
||||
public override float Apply (float a) {
|
||||
return (float)Math.Pow(a - 1, power) * (power % 2 == 0 ? -1 : 1) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
class Pow2InInverseInterpolation : Interpolation {
|
||||
public override float Apply (float a) {
|
||||
if (a < MathUtils.FloatRoundingError) return 0;
|
||||
return (float)Math.Sqrt(a);
|
||||
}
|
||||
}
|
||||
|
||||
class Pow2OutInverseInterpolation : Interpolation {
|
||||
public override float Apply (float a) {
|
||||
if (a < MathUtils.FloatRoundingError) return 0;
|
||||
if (a > 1) return 1;
|
||||
return 1 - (float)Math.Sqrt(-(a - 1));
|
||||
}
|
||||
}
|
||||
|
||||
class Pow3InInverseInterpolation : Interpolation {
|
||||
public override float Apply (float a) {
|
||||
return MathUtils.Cbrt(a);
|
||||
}
|
||||
}
|
||||
|
||||
class Pow3OutInverseInterpolation : Interpolation {
|
||||
public override float Apply (float a) {
|
||||
return 1 - MathUtils.Cbrt(-(a - 1));
|
||||
}
|
||||
}
|
||||
|
||||
class SineInterpolation : Interpolation {
|
||||
public override float Apply (float a) {
|
||||
return (1 - MathUtils.Cos(a * MathUtils.PI)) / 2;
|
||||
}
|
||||
}
|
||||
|
||||
class SineInInterpolation : Interpolation {
|
||||
public override float Apply (float a) {
|
||||
return 1 - MathUtils.Cos(a * MathUtils.HalfPi);
|
||||
}
|
||||
}
|
||||
|
||||
class SineOutInterpolation : Interpolation {
|
||||
public override float Apply (float a) {
|
||||
return MathUtils.Sin(a * MathUtils.HalfPi);
|
||||
}
|
||||
}
|
||||
|
||||
public class ExpInterpolation : Interpolation {
|
||||
protected readonly float value, power, min, scale;
|
||||
|
||||
public ExpInterpolation (float value, float power) {
|
||||
this.value = value;
|
||||
this.power = power;
|
||||
min = (float)Math.Pow(value, -power);
|
||||
scale = 1 / (1 - min);
|
||||
}
|
||||
|
||||
public override float Apply (float a) {
|
||||
if (a <= 0.5f) return ((float)Math.Pow(value, power * (a * 2 - 1)) - min) * scale / 2;
|
||||
return (2 - ((float)Math.Pow(value, -power * (a * 2 - 1)) - min) * scale) / 2;
|
||||
}
|
||||
}
|
||||
|
||||
public class ExpInInterpolation : ExpInterpolation {
|
||||
public ExpInInterpolation (float value, float power) : base(value, power) {
|
||||
}
|
||||
|
||||
public override float Apply (float a) {
|
||||
return ((float)Math.Pow(value, power * (a - 1)) - min) * scale;
|
||||
}
|
||||
}
|
||||
|
||||
public class ExpOutInterpolation : ExpInterpolation {
|
||||
public ExpOutInterpolation (float value, float power) : base(value, power) {
|
||||
}
|
||||
|
||||
public override float Apply (float a) {
|
||||
return 1 - ((float)Math.Pow(value, -power * a) - min) * scale;
|
||||
}
|
||||
}
|
||||
|
||||
class CircleInterpolation : Interpolation {
|
||||
public override float Apply (float a) {
|
||||
if (a <= 0.5f) {
|
||||
a *= 2;
|
||||
return (1 - (float)Math.Sqrt(1 - a * a)) / 2;
|
||||
}
|
||||
|
||||
a--;
|
||||
a *= 2;
|
||||
return ((float)Math.Sqrt(1 - a * a) + 1) / 2;
|
||||
}
|
||||
}
|
||||
|
||||
class CircleInInterpolation : Interpolation {
|
||||
public override float Apply (float a) {
|
||||
return 1 - (float)Math.Sqrt(1 - a * a);
|
||||
}
|
||||
}
|
||||
|
||||
class CircleOutInterpolation : Interpolation {
|
||||
public override float Apply (float a) {
|
||||
a--;
|
||||
return (float)Math.Sqrt(1 - a * a);
|
||||
}
|
||||
}
|
||||
|
||||
public class ElasticInterpolation : Interpolation {
|
||||
protected readonly float value, power, scale, bounces;
|
||||
|
||||
public ElasticInterpolation (float value, float power, int bounces, float scale) {
|
||||
this.value = value;
|
||||
this.power = power;
|
||||
this.scale = scale;
|
||||
this.bounces = bounces * MathUtils.PI * (bounces % 2 == 0 ? 1 : -1);
|
||||
}
|
||||
|
||||
public override float Apply (float a) {
|
||||
if (a <= 0.5f) {
|
||||
a *= 2;
|
||||
return (float)Math.Pow(value, power * (a - 1)) * MathUtils.Sin(a * bounces) * scale / 2;
|
||||
}
|
||||
a = 1 - a;
|
||||
a *= 2;
|
||||
return 1 - (float)Math.Pow(value, power * (a - 1)) * MathUtils.Sin(a * bounces) * scale / 2;
|
||||
}
|
||||
}
|
||||
|
||||
public class ElasticInInterpolation : ElasticInterpolation {
|
||||
public ElasticInInterpolation (float value, float power, int bounces, float scale)
|
||||
: base(value, power, bounces, scale) {
|
||||
}
|
||||
|
||||
public override float Apply (float a) {
|
||||
if (a >= 0.99) return 1;
|
||||
return (float)Math.Pow(value, power * (a - 1)) * MathUtils.Sin(a * bounces) * scale;
|
||||
}
|
||||
}
|
||||
|
||||
public class ElasticOutInterpolation : ElasticInterpolation {
|
||||
public ElasticOutInterpolation (float value, float power, int bounces, float scale)
|
||||
: base(value, power, bounces, scale) {
|
||||
}
|
||||
|
||||
public override float Apply (float a) {
|
||||
if (a == 0) return 0;
|
||||
a = 1 - a;
|
||||
return 1 - (float)Math.Pow(value, power * (a - 1)) * MathUtils.Sin(a * bounces) * scale;
|
||||
}
|
||||
}
|
||||
|
||||
public class SwingInterpolation : Interpolation {
|
||||
readonly float scale;
|
||||
|
||||
public SwingInterpolation (float scale) {
|
||||
this.scale = scale * 2;
|
||||
}
|
||||
|
||||
public override float Apply (float a) {
|
||||
if (a <= 0.5f) {
|
||||
a *= 2;
|
||||
return a * a * ((scale + 1) * a - scale) / 2;
|
||||
}
|
||||
a--;
|
||||
a *= 2;
|
||||
return a * a * ((scale + 1) * a + scale) / 2 + 1;
|
||||
}
|
||||
}
|
||||
|
||||
public class SwingOutInterpolation : Interpolation {
|
||||
readonly float scale;
|
||||
|
||||
public SwingOutInterpolation (float scale) {
|
||||
this.scale = scale;
|
||||
}
|
||||
|
||||
public override float Apply (float a) {
|
||||
a--;
|
||||
return a * a * ((scale + 1) * a + scale) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
public class SwingInInterpolation : Interpolation {
|
||||
readonly float scale;
|
||||
|
||||
public SwingInInterpolation (float scale) {
|
||||
this.scale = scale;
|
||||
}
|
||||
|
||||
public override float Apply (float a) {
|
||||
return a * a * ((scale + 1) * a - scale);
|
||||
}
|
||||
}
|
||||
|
||||
public class BounceOutInterpolation : Interpolation {
|
||||
protected readonly float[] widths, heights;
|
||||
|
||||
public BounceOutInterpolation (float[] widths, float[] heights) {
|
||||
if (widths.Length != heights.Length)
|
||||
throw new ArgumentException("Must be the same number of widths and heights.");
|
||||
this.widths = widths;
|
||||
this.heights = heights;
|
||||
}
|
||||
|
||||
public BounceOutInterpolation (int bounces) {
|
||||
if (bounces < 2 || bounces > 5) throw new ArgumentException("bounces cannot be < 2 or > 5: " + bounces);
|
||||
widths = new float[bounces];
|
||||
heights = new float[bounces];
|
||||
heights[0] = 1;
|
||||
switch (bounces) {
|
||||
case 2:
|
||||
widths[0] = 0.6f;
|
||||
widths[1] = 0.4f;
|
||||
heights[1] = 0.33f;
|
||||
break;
|
||||
case 3:
|
||||
widths[0] = 0.4f;
|
||||
widths[1] = 0.4f;
|
||||
widths[2] = 0.2f;
|
||||
heights[1] = 0.33f;
|
||||
heights[2] = 0.1f;
|
||||
break;
|
||||
case 4:
|
||||
widths[0] = 0.34f;
|
||||
widths[1] = 0.34f;
|
||||
widths[2] = 0.2f;
|
||||
widths[3] = 0.15f;
|
||||
heights[1] = 0.26f;
|
||||
heights[2] = 0.11f;
|
||||
heights[3] = 0.03f;
|
||||
break;
|
||||
case 5:
|
||||
widths[0] = 0.3f;
|
||||
widths[1] = 0.3f;
|
||||
widths[2] = 0.2f;
|
||||
widths[3] = 0.1f;
|
||||
widths[4] = 0.1f;
|
||||
heights[1] = 0.45f;
|
||||
heights[2] = 0.3f;
|
||||
heights[3] = 0.15f;
|
||||
heights[4] = 0.06f;
|
||||
break;
|
||||
}
|
||||
widths[0] *= 2;
|
||||
}
|
||||
|
||||
public override float Apply (float a) {
|
||||
if (a == 1) return 1;
|
||||
a += widths[0] / 2;
|
||||
float width = 0, height = 0;
|
||||
for (int i = 0, n = widths.Length; i < n; i++) {
|
||||
width = widths[i];
|
||||
if (a <= width) {
|
||||
height = heights[i];
|
||||
break;
|
||||
}
|
||||
a -= width;
|
||||
}
|
||||
a /= width;
|
||||
float z = 4 / width * height * a;
|
||||
return 1 - (z - z * a) * width;
|
||||
}
|
||||
}
|
||||
|
||||
public class BounceInterpolation : BounceOutInterpolation {
|
||||
public BounceInterpolation (float[] widths, float[] heights) : base(widths, heights) {
|
||||
}
|
||||
|
||||
public BounceInterpolation (int bounces) : base(bounces) {
|
||||
}
|
||||
|
||||
float Out (float a) {
|
||||
float test = a + widths[0] / 2;
|
||||
if (test < widths[0]) return test / (widths[0] / 2) - 1;
|
||||
return base.Apply(a);
|
||||
}
|
||||
|
||||
public override float Apply (float a) {
|
||||
if (a <= 0.5f) return (1 - Out(1 - a * 2)) / 2;
|
||||
return Out(a * 2 - 1) / 2 + 0.5f;
|
||||
}
|
||||
}
|
||||
|
||||
public class BounceInInterpolation : BounceOutInterpolation {
|
||||
public BounceInInterpolation (float[] widths, float[] heights) : base(widths, heights) {
|
||||
}
|
||||
|
||||
public BounceInInterpolation (int bounces) : base(bounces) {
|
||||
}
|
||||
|
||||
public override float Apply (float a) {
|
||||
return 1 - base.Apply(1 - a);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 16a8b0f8fcd20fa4a9a930b2a97f93f5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,517 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated April 5, 2025. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2026, 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;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace Spine {
|
||||
public static class Json {
|
||||
public static object Deserialize (TextReader text) {
|
||||
SharpJson.JsonDecoder parser = new SharpJson.JsonDecoder();
|
||||
parser.parseNumbersAsFloat = true;
|
||||
return parser.Decode(text.ReadToEnd());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copyright (c) 2016 Adriano Tinoco d'Oliveira Rezende
|
||||
*
|
||||
* Based on the JSON parser by Patrick van Bergen
|
||||
* http://techblog.procurios.nl/k/news/view/14605/14863/how-do-i-write-my-own-parser-(for-json).html
|
||||
*
|
||||
* Changes made:
|
||||
*
|
||||
* - Optimized parser speed (deserialize roughly near 3x faster than original)
|
||||
* - Added support to handle lexer/parser error messages with line numbers
|
||||
* - Added more fine grained control over type conversions during the parsing
|
||||
* - Refactory API (Separate Lexer code from Parser code and the Encoder from Decoder)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
|
||||
* and associated documentation files (the "Software"), to deal in the Software without restriction,
|
||||
* including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
* The above copyright notice and this permission notice shall be included in all copies or substantial
|
||||
* portions of the Software.
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
|
||||
* LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
|
||||
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
namespace SharpJson {
|
||||
class Lexer {
|
||||
public enum Token {
|
||||
None,
|
||||
Null,
|
||||
True,
|
||||
False,
|
||||
Colon,
|
||||
Comma,
|
||||
String,
|
||||
Number,
|
||||
CurlyOpen,
|
||||
CurlyClose,
|
||||
SquaredOpen,
|
||||
SquaredClose,
|
||||
};
|
||||
|
||||
public bool hasError {
|
||||
get {
|
||||
return !success;
|
||||
}
|
||||
}
|
||||
|
||||
public int lineNumber {
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public bool parseNumbersAsFloat {
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
char[] json;
|
||||
int index = 0;
|
||||
bool success = true;
|
||||
char[] stringBuffer = new char[4096];
|
||||
|
||||
public Lexer (string text) {
|
||||
Reset();
|
||||
|
||||
json = text.ToCharArray();
|
||||
parseNumbersAsFloat = false;
|
||||
}
|
||||
|
||||
public void Reset () {
|
||||
index = 0;
|
||||
lineNumber = 1;
|
||||
success = true;
|
||||
}
|
||||
|
||||
public string ParseString () {
|
||||
int idx = 0;
|
||||
StringBuilder builder = null;
|
||||
|
||||
SkipWhiteSpaces();
|
||||
|
||||
// "
|
||||
char c = json[index++];
|
||||
|
||||
bool failed = false;
|
||||
bool complete = false;
|
||||
|
||||
while (!complete && !failed) {
|
||||
if (index == json.Length)
|
||||
break;
|
||||
|
||||
c = json[index++];
|
||||
if (c == '"') {
|
||||
complete = true;
|
||||
break;
|
||||
} else if (c == '\\') {
|
||||
if (index == json.Length)
|
||||
break;
|
||||
|
||||
c = json[index++];
|
||||
|
||||
switch (c) {
|
||||
case '"':
|
||||
stringBuffer[idx++] = '"';
|
||||
break;
|
||||
case '\\':
|
||||
stringBuffer[idx++] = '\\';
|
||||
break;
|
||||
case '/':
|
||||
stringBuffer[idx++] = '/';
|
||||
break;
|
||||
case 'b':
|
||||
stringBuffer[idx++] = '\b';
|
||||
break;
|
||||
case 'f':
|
||||
stringBuffer[idx++] = '\f';
|
||||
break;
|
||||
case 'n':
|
||||
stringBuffer[idx++] = '\n';
|
||||
break;
|
||||
case 'r':
|
||||
stringBuffer[idx++] = '\r';
|
||||
break;
|
||||
case 't':
|
||||
stringBuffer[idx++] = '\t';
|
||||
break;
|
||||
case 'u':
|
||||
int remainingLength = json.Length - index;
|
||||
if (remainingLength >= 4) {
|
||||
string hex = new string(json, index, 4);
|
||||
|
||||
// XXX: handle UTF
|
||||
stringBuffer[idx++] = (char)Convert.ToInt32(hex, 16);
|
||||
|
||||
// skip 4 chars
|
||||
index += 4;
|
||||
} else {
|
||||
failed = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
stringBuffer[idx++] = c;
|
||||
}
|
||||
|
||||
if (idx >= stringBuffer.Length) {
|
||||
if (builder == null)
|
||||
builder = new StringBuilder();
|
||||
|
||||
builder.Append(stringBuffer, 0, idx);
|
||||
idx = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!complete) {
|
||||
success = false;
|
||||
return null;
|
||||
}
|
||||
|
||||
if (builder != null)
|
||||
return builder.ToString();
|
||||
else
|
||||
return new string(stringBuffer, 0, idx);
|
||||
}
|
||||
|
||||
string GetNumberString () {
|
||||
SkipWhiteSpaces();
|
||||
|
||||
int lastIndex = GetLastIndexOfNumber(index);
|
||||
int charLength = (lastIndex - index) + 1;
|
||||
|
||||
string result = new string(json, index, charLength);
|
||||
|
||||
index = lastIndex + 1;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public float ParseFloatNumber () {
|
||||
float number;
|
||||
string str = GetNumberString();
|
||||
|
||||
if (!float.TryParse(str, NumberStyles.Float, CultureInfo.InvariantCulture, out number))
|
||||
return 0;
|
||||
|
||||
return number;
|
||||
}
|
||||
|
||||
public double ParseDoubleNumber () {
|
||||
double number;
|
||||
string str = GetNumberString();
|
||||
|
||||
if (!double.TryParse(str, NumberStyles.Any, CultureInfo.InvariantCulture, out number))
|
||||
return 0;
|
||||
|
||||
return number;
|
||||
}
|
||||
|
||||
int GetLastIndexOfNumber (int index) {
|
||||
int lastIndex;
|
||||
|
||||
for (lastIndex = index; lastIndex < json.Length; lastIndex++) {
|
||||
char ch = json[lastIndex];
|
||||
|
||||
if ((ch < '0' || ch > '9') && ch != '+' && ch != '-'
|
||||
&& ch != '.' && ch != 'e' && ch != 'E')
|
||||
break;
|
||||
}
|
||||
|
||||
return lastIndex - 1;
|
||||
}
|
||||
|
||||
void SkipWhiteSpaces () {
|
||||
for (; index < json.Length; index++) {
|
||||
char ch = json[index];
|
||||
|
||||
if (ch == '\n')
|
||||
lineNumber++;
|
||||
|
||||
if (!char.IsWhiteSpace(json[index]))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public Token LookAhead () {
|
||||
SkipWhiteSpaces();
|
||||
|
||||
int savedIndex = index;
|
||||
return NextToken(json, ref savedIndex);
|
||||
}
|
||||
|
||||
public Token NextToken () {
|
||||
SkipWhiteSpaces();
|
||||
return NextToken(json, ref index);
|
||||
}
|
||||
|
||||
static Token NextToken (char[] json, ref int index) {
|
||||
if (index == json.Length)
|
||||
return Token.None;
|
||||
|
||||
char c = json[index++];
|
||||
|
||||
switch (c) {
|
||||
case '{':
|
||||
return Token.CurlyOpen;
|
||||
case '}':
|
||||
return Token.CurlyClose;
|
||||
case '[':
|
||||
return Token.SquaredOpen;
|
||||
case ']':
|
||||
return Token.SquaredClose;
|
||||
case ',':
|
||||
return Token.Comma;
|
||||
case '"':
|
||||
return Token.String;
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
case '8':
|
||||
case '9':
|
||||
case '-':
|
||||
return Token.Number;
|
||||
case ':':
|
||||
return Token.Colon;
|
||||
}
|
||||
|
||||
index--;
|
||||
|
||||
int remainingLength = json.Length - index;
|
||||
|
||||
// false
|
||||
if (remainingLength >= 5) {
|
||||
if (json[index] == 'f' &&
|
||||
json[index + 1] == 'a' &&
|
||||
json[index + 2] == 'l' &&
|
||||
json[index + 3] == 's' &&
|
||||
json[index + 4] == 'e') {
|
||||
index += 5;
|
||||
return Token.False;
|
||||
}
|
||||
}
|
||||
|
||||
// true
|
||||
if (remainingLength >= 4) {
|
||||
if (json[index] == 't' &&
|
||||
json[index + 1] == 'r' &&
|
||||
json[index + 2] == 'u' &&
|
||||
json[index + 3] == 'e') {
|
||||
index += 4;
|
||||
return Token.True;
|
||||
}
|
||||
}
|
||||
|
||||
// null
|
||||
if (remainingLength >= 4) {
|
||||
if (json[index] == 'n' &&
|
||||
json[index + 1] == 'u' &&
|
||||
json[index + 2] == 'l' &&
|
||||
json[index + 3] == 'l') {
|
||||
index += 4;
|
||||
return Token.Null;
|
||||
}
|
||||
}
|
||||
|
||||
return Token.None;
|
||||
}
|
||||
}
|
||||
|
||||
public class JsonDecoder {
|
||||
public string errorMessage {
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public bool parseNumbersAsFloat {
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
Lexer lexer;
|
||||
|
||||
public JsonDecoder () {
|
||||
errorMessage = null;
|
||||
parseNumbersAsFloat = false;
|
||||
}
|
||||
|
||||
public object Decode (string text) {
|
||||
errorMessage = null;
|
||||
|
||||
lexer = new Lexer(text);
|
||||
lexer.parseNumbersAsFloat = parseNumbersAsFloat;
|
||||
|
||||
return ParseValue();
|
||||
}
|
||||
|
||||
public static object DecodeText (string text) {
|
||||
JsonDecoder builder = new JsonDecoder();
|
||||
return builder.Decode(text);
|
||||
}
|
||||
|
||||
IDictionary<string, object> ParseObject () {
|
||||
Dictionary<string, object> table = new Dictionary<string, object>();
|
||||
|
||||
// {
|
||||
lexer.NextToken();
|
||||
|
||||
while (true) {
|
||||
Lexer.Token token = lexer.LookAhead();
|
||||
|
||||
switch (token) {
|
||||
case Lexer.Token.None:
|
||||
TriggerError("Invalid token");
|
||||
return null;
|
||||
case Lexer.Token.Comma:
|
||||
lexer.NextToken();
|
||||
break;
|
||||
case Lexer.Token.CurlyClose:
|
||||
lexer.NextToken();
|
||||
return table;
|
||||
default:
|
||||
// name
|
||||
string name = EvalLexer(lexer.ParseString());
|
||||
|
||||
if (errorMessage != null)
|
||||
return null;
|
||||
|
||||
// :
|
||||
token = lexer.NextToken();
|
||||
|
||||
if (token != Lexer.Token.Colon) {
|
||||
TriggerError("Invalid token; expected ':'");
|
||||
return null;
|
||||
}
|
||||
|
||||
// value
|
||||
object value = ParseValue();
|
||||
|
||||
if (errorMessage != null)
|
||||
return null;
|
||||
|
||||
table[name] = value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//return null; // Unreachable code
|
||||
}
|
||||
|
||||
IList<object> ParseArray () {
|
||||
List<object> array = new List<object>();
|
||||
|
||||
// [
|
||||
lexer.NextToken();
|
||||
|
||||
while (true) {
|
||||
Lexer.Token token = lexer.LookAhead();
|
||||
|
||||
switch (token) {
|
||||
case Lexer.Token.None:
|
||||
TriggerError("Invalid token");
|
||||
return null;
|
||||
case Lexer.Token.Comma:
|
||||
lexer.NextToken();
|
||||
break;
|
||||
case Lexer.Token.SquaredClose:
|
||||
lexer.NextToken();
|
||||
return array;
|
||||
default:
|
||||
object value = ParseValue();
|
||||
|
||||
if (errorMessage != null)
|
||||
return null;
|
||||
|
||||
array.Add(value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//return null; // Unreachable code
|
||||
}
|
||||
|
||||
object ParseValue () {
|
||||
switch (lexer.LookAhead()) {
|
||||
case Lexer.Token.String:
|
||||
return EvalLexer(lexer.ParseString());
|
||||
case Lexer.Token.Number:
|
||||
if (parseNumbersAsFloat)
|
||||
return EvalLexer(lexer.ParseFloatNumber());
|
||||
else
|
||||
return EvalLexer(lexer.ParseDoubleNumber());
|
||||
case Lexer.Token.CurlyOpen:
|
||||
return ParseObject();
|
||||
case Lexer.Token.SquaredOpen:
|
||||
return ParseArray();
|
||||
case Lexer.Token.True:
|
||||
lexer.NextToken();
|
||||
return true;
|
||||
case Lexer.Token.False:
|
||||
lexer.NextToken();
|
||||
return false;
|
||||
case Lexer.Token.Null:
|
||||
lexer.NextToken();
|
||||
return null;
|
||||
case Lexer.Token.None:
|
||||
break;
|
||||
}
|
||||
|
||||
TriggerError("Unable to parse value");
|
||||
return null;
|
||||
}
|
||||
|
||||
void TriggerError (string message) {
|
||||
errorMessage = string.Format("Error: '{0}' at line {1}",
|
||||
message, lexer.lineNumber);
|
||||
}
|
||||
|
||||
T EvalLexer<T> (T value) {
|
||||
if (lexer.hasError)
|
||||
TriggerError("Lexical error ocurred");
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 371f40ecc08b2eb4cbec49585d41e2c3
|
||||
timeCreated: 1456265153
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,163 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated April 5, 2025. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2026, 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 Epsilon = 0.00001f, EpsilonSq = Epsilon * Epsilon;
|
||||
public const float PI = 3.1415927f, PI2 = PI * 2, InvPI2 = 1 / PI2;
|
||||
public const float RadiansToDegrees = 180f / PI, RadDeg = RadiansToDegrees;
|
||||
public const float DegreesToRadians = PI / 180, DegRad = DegreesToRadians;
|
||||
|
||||
public const float FloatRoundingError = 0.000001f;
|
||||
public const float HalfPi = PI / 2;
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
static public float Atan2Deg (float y, float x) {
|
||||
return Atan2(y, x) * RadDeg;
|
||||
}
|
||||
|
||||
/// <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);
|
||||
}
|
||||
|
||||
|
||||
static public float Atan2Deg (float y, float x) {
|
||||
return (float)Math.Atan2(y, x) * RadDeg;
|
||||
}
|
||||
|
||||
|
||||
/// <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 Cbrt (float x) {
|
||||
return x < 0f ? -(float)Math.Pow(-x, 1.0 / 3.0) : (float)Math.Pow(x, 1.0 / 3.0);
|
||||
}
|
||||
|
||||
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 Clamp01 (float value) {
|
||||
if (value < 0) return 0;
|
||||
if (value > 1) return 1;
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 03b653e54c5403b4191f5003d64c6e18
|
||||
timeCreated: 1456265153
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,518 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated April 5, 2025. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2026, 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>
|
||||
/// Adjusts the rotation, translation, and scale of the constrained bones so they follow a <see cref="PathAttachment"/>.</para>
|
||||
/// <para>
|
||||
/// See <a href="http://esotericsoftware.com/spine-path-constraints">Path constraints</a> in the Spine User Guide.</para>
|
||||
/// </summary>
|
||||
public class PathConstraint : Constraint<PathConstraint, PathConstraintData, PathConstraintPose> {
|
||||
const int NONE = -1, BEFORE = -2, AFTER = -3;
|
||||
|
||||
internal readonly ExposedList<BonePose> bones;
|
||||
internal Slot slot;
|
||||
|
||||
internal readonly ExposedList<float> spaces = new ExposedList<float>(), positions = new ExposedList<float>();
|
||||
internal readonly ExposedList<float> world = new ExposedList<float>(), curves = new ExposedList<float>(), lengths = new ExposedList<float>();
|
||||
internal readonly float[] segments = new float[10];
|
||||
|
||||
public PathConstraint (PathConstraintData data, Skeleton skeleton)
|
||||
: base(data, new PathConstraintPose(), new PathConstraintPose()) {
|
||||
if (skeleton == null) throw new ArgumentNullException("skeleton", "skeleton cannot be null.");
|
||||
|
||||
bones = new ExposedList<BonePose>(data.Bones.Count);
|
||||
foreach (BoneData boneData in data.bones)
|
||||
bones.Add(skeleton.bones.Items[boneData.index].constrainedPose);
|
||||
|
||||
slot = skeleton.slots.Items[data.slot.index];
|
||||
}
|
||||
|
||||
override public IConstraint Copy (Skeleton skeleton) {
|
||||
var copy = new PathConstraint(data, skeleton);
|
||||
copy.pose.Set(pose);
|
||||
return copy;
|
||||
}
|
||||
|
||||
public static void ArraysFill (float[] a, int fromIndex, int toIndex, float val) {
|
||||
for (int i = fromIndex; i < toIndex; i++)
|
||||
a[i] = val;
|
||||
}
|
||||
|
||||
override public void Update (Skeleton skeleton, Physics physics) {
|
||||
PathAttachment pathAttachment = slot.appliedPose.Attachment as PathAttachment;
|
||||
if (pathAttachment == null) return;
|
||||
|
||||
PathConstraintPose p = appliedPose;
|
||||
float mixRotate = p.mixRotate, mixX = p.mixX, mixY = p.mixY;
|
||||
if (mixRotate == 0 && mixX == 0 && mixY == 0) return;
|
||||
|
||||
PathConstraintData data = this.data;
|
||||
bool tangents = data.rotateMode == RotateMode.Tangent, scale = data.rotateMode == RotateMode.ChainScale;
|
||||
int boneCount = this.bones.Count, spacesCount = tangents ? boneCount : boneCount + 1;
|
||||
BonePose[] bonesItems = this.bones.Items;
|
||||
float[] spaces = this.spaces.EnsureSize(spacesCount).Items, lengths = scale ? this.lengths.EnsureSize(boneCount).Items : null;
|
||||
float spacing = p.spacing;
|
||||
switch (data.spacingMode) {
|
||||
case SpacingMode.Percent:
|
||||
if (scale) {
|
||||
for (int i = 0, n = spacesCount - 1; i < n; i++) {
|
||||
BonePose bone = bonesItems[i];
|
||||
float setupLength = bone.bone.data.length;
|
||||
float x = setupLength * bone.a, y = setupLength * bone.c;
|
||||
lengths[i] = (float)Math.Sqrt(x * x + y * y);
|
||||
}
|
||||
}
|
||||
ArraysFill(spaces, 1, spacesCount, spacing);
|
||||
break;
|
||||
case SpacingMode.Proportional: {
|
||||
float sum = 0;
|
||||
for (int i = 0, n = spacesCount - 1; i < n;) {
|
||||
BonePose bone = bonesItems[i];
|
||||
float setupLength = bone.bone.data.length;
|
||||
if (setupLength < MathUtils.Epsilon) {
|
||||
if (scale) lengths[i] = 0;
|
||||
spaces[++i] = spacing;
|
||||
} else {
|
||||
float x = setupLength * bone.a, y = setupLength * bone.c;
|
||||
float length = (float)Math.Sqrt(x * x + y * y);
|
||||
if (scale) lengths[i] = length;
|
||||
spaces[++i] = length;
|
||||
sum += length;
|
||||
}
|
||||
}
|
||||
if (sum > 0) {
|
||||
sum = spacesCount / sum * spacing;
|
||||
for (int i = 1; i < spacesCount; i++)
|
||||
spaces[i] *= sum;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
bool lengthSpacing = data.spacingMode == SpacingMode.Length;
|
||||
for (int i = 0, n = spacesCount - 1; i < n;) {
|
||||
BonePose bone = bonesItems[i];
|
||||
float setupLength = bone.bone.data.length;
|
||||
if (setupLength < MathUtils.Epsilon) {
|
||||
if (scale) lengths[i] = 0;
|
||||
spaces[++i] = spacing;
|
||||
} else {
|
||||
float x = setupLength * bone.a, y = setupLength * bone.c;
|
||||
float length = (float)Math.Sqrt(x * x + y * y);
|
||||
if (scale) lengths[i] = length;
|
||||
spaces[++i] = (lengthSpacing ? Math.Max(0, setupLength + spacing) : spacing) * length / setupLength;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
float[] positions = ComputeWorldPositions(skeleton, pathAttachment, spacesCount, tangents);
|
||||
float boneX = positions[0], boneY = positions[1], offsetRotation = data.offsetRotation;
|
||||
bool tip;
|
||||
if (offsetRotation == 0) {
|
||||
tip = data.rotateMode == RotateMode.Chain;
|
||||
} else {
|
||||
tip = false;
|
||||
BonePose bone = slot.bone.appliedPose;
|
||||
offsetRotation *= bone.a * bone.d - bone.b * bone.c > 0 ? MathUtils.DegRad : -MathUtils.DegRad;
|
||||
}
|
||||
for (int i = 0, ip = 3, u = skeleton.update; i < boneCount; i++, ip += 3) {
|
||||
BonePose bone = bonesItems[i];
|
||||
bone.worldX += (boneX - bone.worldX) * mixX;
|
||||
bone.worldY += (boneY - bone.worldY) * mixY;
|
||||
float x = positions[ip], y = positions[ip + 1], dx = x - boneX, dy = y - boneY;
|
||||
if (scale) {
|
||||
float length = lengths[i];
|
||||
if (length >= MathUtils.Epsilon) {
|
||||
float s = ((float)Math.Sqrt(dx * dx + dy * dy) / length - 1) * mixRotate + 1;
|
||||
bone.a *= s;
|
||||
bone.c *= s;
|
||||
}
|
||||
}
|
||||
boneX = x;
|
||||
boneY = y;
|
||||
if (mixRotate > 0) {
|
||||
float a = bone.a, b = bone.b, c = bone.c, d = bone.d, r, cos, sin;
|
||||
if (tangents)
|
||||
r = positions[ip - 1];
|
||||
else if (spaces[i + 1] < MathUtils.Epsilon)
|
||||
r = positions[ip + 2];
|
||||
else
|
||||
r = MathUtils.Atan2(dy, dx);
|
||||
r -= MathUtils.Atan2(c, a);
|
||||
if (tip) {
|
||||
cos = MathUtils.Cos(r);
|
||||
sin = MathUtils.Sin(r);
|
||||
float length = bone.bone.data.length;
|
||||
boneX += (length * (cos * a - sin * c) - dx) * mixRotate;
|
||||
boneY += (length * (sin * a + cos * c) - dy) * mixRotate;
|
||||
} else
|
||||
r += offsetRotation;
|
||||
if (r > MathUtils.PI)
|
||||
r -= MathUtils.PI2;
|
||||
else if (r < -MathUtils.PI) //
|
||||
r += MathUtils.PI2;
|
||||
r *= mixRotate;
|
||||
cos = MathUtils.Cos(r);
|
||||
sin = MathUtils.Sin(r);
|
||||
bone.a = cos * a - sin * c;
|
||||
bone.b = cos * b - sin * d;
|
||||
bone.c = sin * a + cos * c;
|
||||
bone.d = sin * b + cos * d;
|
||||
}
|
||||
bone.ModifyWorld(u);
|
||||
}
|
||||
}
|
||||
|
||||
float[] ComputeWorldPositions (Skeleton skeleton, PathAttachment path, int spacesCount, bool tangents) {
|
||||
Slot slot = this.slot;
|
||||
float position = appliedPose.position;
|
||||
float[] spaces = this.spaces.Items, output = this.positions.EnsureSize(spacesCount * 3 + 2).Items, world;
|
||||
bool closed = path.Closed;
|
||||
int verticesLength = path.WorldVerticesLength, curveCount = verticesLength / 6, prevCurve = NONE;
|
||||
|
||||
float pathLength, multiplier;
|
||||
if (!path.ConstantSpeed) {
|
||||
float[] lengths = path.Lengths;
|
||||
curveCount -= closed ? 1 : 2;
|
||||
pathLength = lengths[curveCount];
|
||||
|
||||
if (data.positionMode == PositionMode.Percent) position *= pathLength;
|
||||
|
||||
switch (data.spacingMode) {
|
||||
case SpacingMode.Percent:
|
||||
multiplier = pathLength;
|
||||
break;
|
||||
case SpacingMode.Proportional:
|
||||
multiplier = pathLength / spacesCount;
|
||||
break;
|
||||
default:
|
||||
multiplier = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
world = this.world.EnsureSize(8).Items;
|
||||
for (int i = 0, o = 0, curve = 0; i < spacesCount; i++, o += 3) {
|
||||
float space = spaces[i] * multiplier;
|
||||
position += space;
|
||||
float p = position;
|
||||
|
||||
if (closed) {
|
||||
p %= pathLength;
|
||||
if (p < 0) p += pathLength;
|
||||
curve = 0;
|
||||
} else if (p < 0) {
|
||||
if (prevCurve != BEFORE) {
|
||||
prevCurve = BEFORE;
|
||||
path.ComputeWorldVertices(skeleton, slot, 2, 4, world, 0, 2);
|
||||
}
|
||||
AddBeforePosition(p, world, 0, output, o);
|
||||
continue;
|
||||
} else if (p > pathLength) {
|
||||
if (prevCurve != AFTER) {
|
||||
prevCurve = AFTER;
|
||||
path.ComputeWorldVertices(skeleton, slot, verticesLength - 6, 4, world, 0, 2);
|
||||
}
|
||||
AddAfterPosition(p - pathLength, world, 0, output, o);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Determine curve containing position.
|
||||
for (; ; curve++) {
|
||||
float length = lengths[curve];
|
||||
if (p > length) continue;
|
||||
if (curve == 0)
|
||||
p /= length;
|
||||
else {
|
||||
float prev = lengths[curve - 1];
|
||||
p = (p - prev) / (length - prev);
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (curve != prevCurve) {
|
||||
prevCurve = curve;
|
||||
if (closed && curve == curveCount) {
|
||||
path.ComputeWorldVertices(skeleton, slot, verticesLength - 4, 4, world, 0, 2);
|
||||
path.ComputeWorldVertices(skeleton, slot, 0, 4, world, 4, 2);
|
||||
} else
|
||||
path.ComputeWorldVertices(skeleton, slot, curve * 6 + 2, 8, world, 0, 2);
|
||||
}
|
||||
AddCurvePosition(p, world[0], world[1], world[2], world[3], world[4], world[5], world[6], world[7], output, o,
|
||||
tangents || (i > 0 && space < MathUtils.Epsilon));
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
// World vertices.
|
||||
if (closed) {
|
||||
verticesLength += 2;
|
||||
world = this.world.EnsureSize(verticesLength).Items;
|
||||
path.ComputeWorldVertices(skeleton, slot, 2, verticesLength - 4, world, 0, 2);
|
||||
path.ComputeWorldVertices(skeleton, slot, 0, 2, world, verticesLength - 4, 2);
|
||||
world[verticesLength - 2] = world[0];
|
||||
world[verticesLength - 1] = world[1];
|
||||
} else {
|
||||
curveCount--;
|
||||
verticesLength -= 4;
|
||||
world = this.world.EnsureSize(verticesLength).Items;
|
||||
path.ComputeWorldVertices(skeleton, slot, 2, verticesLength, world, 0, 2);
|
||||
}
|
||||
|
||||
// Curve lengths.
|
||||
float[] curves = this.curves.EnsureSize(curveCount).Items;
|
||||
pathLength = 0;
|
||||
float x1 = world[0], y1 = world[1], cx1 = 0, cy1 = 0, cx2 = 0, cy2 = 0, x2 = 0, y2 = 0;
|
||||
float tmpx, tmpy, dddfx, dddfy, ddfx, ddfy, dfx, dfy;
|
||||
for (int i = 0, w = 2; i < curveCount; i++, w += 6) {
|
||||
cx1 = world[w];
|
||||
cy1 = world[w + 1];
|
||||
cx2 = world[w + 2];
|
||||
cy2 = world[w + 3];
|
||||
x2 = world[w + 4];
|
||||
y2 = world[w + 5];
|
||||
tmpx = (x1 - cx1 * 2 + cx2) * 0.1875f;
|
||||
tmpy = (y1 - cy1 * 2 + cy2) * 0.1875f;
|
||||
dddfx = ((cx1 - cx2) * 3 - x1 + x2) * 0.09375f;
|
||||
dddfy = ((cy1 - cy2) * 3 - y1 + y2) * 0.09375f;
|
||||
ddfx = tmpx * 2 + dddfx;
|
||||
ddfy = tmpy * 2 + dddfy;
|
||||
dfx = (cx1 - x1) * 0.75f + tmpx + dddfx * 0.16666667f;
|
||||
dfy = (cy1 - y1) * 0.75f + tmpy + dddfy * 0.16666667f;
|
||||
pathLength += (float)Math.Sqrt(dfx * dfx + dfy * dfy);
|
||||
dfx += ddfx;
|
||||
dfy += ddfy;
|
||||
ddfx += dddfx;
|
||||
ddfy += dddfy;
|
||||
pathLength += (float)Math.Sqrt(dfx * dfx + dfy * dfy);
|
||||
dfx += ddfx;
|
||||
dfy += ddfy;
|
||||
pathLength += (float)Math.Sqrt(dfx * dfx + dfy * dfy);
|
||||
dfx += ddfx + dddfx;
|
||||
dfy += ddfy + dddfy;
|
||||
pathLength += (float)Math.Sqrt(dfx * dfx + dfy * dfy);
|
||||
curves[i] = pathLength;
|
||||
x1 = x2;
|
||||
y1 = y2;
|
||||
}
|
||||
|
||||
if (data.positionMode == PositionMode.Percent) position *= pathLength;
|
||||
|
||||
switch (data.spacingMode) {
|
||||
case SpacingMode.Percent:
|
||||
multiplier = pathLength;
|
||||
break;
|
||||
case SpacingMode.Proportional:
|
||||
multiplier = pathLength / spacesCount;
|
||||
break;
|
||||
default:
|
||||
multiplier = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
float[] segments = this.segments;
|
||||
float curveLength = 0;
|
||||
for (int i = 0, o = 0, curve = 0, segment = 0; i < spacesCount; i++, o += 3) {
|
||||
float space = spaces[i] * multiplier;
|
||||
position += space;
|
||||
float p = position;
|
||||
|
||||
if (closed) {
|
||||
p %= pathLength;
|
||||
if (p < 0) p += pathLength;
|
||||
curve = 0;
|
||||
segment = 0;
|
||||
} else if (p < 0) {
|
||||
AddBeforePosition(p, world, 0, output, o);
|
||||
continue;
|
||||
} else if (p > pathLength) {
|
||||
AddAfterPosition(p - pathLength, world, verticesLength - 4, output, o);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Determine curve containing position.
|
||||
for (; ; curve++) {
|
||||
float length = curves[curve];
|
||||
if (p > length) continue;
|
||||
if (curve == 0)
|
||||
p /= length;
|
||||
else {
|
||||
float prev = curves[curve - 1];
|
||||
p = (p - prev) / (length - prev);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Curve segment lengths.
|
||||
if (curve != prevCurve) {
|
||||
prevCurve = curve;
|
||||
int ii = curve * 6;
|
||||
x1 = world[ii];
|
||||
y1 = world[ii + 1];
|
||||
cx1 = world[ii + 2];
|
||||
cy1 = world[ii + 3];
|
||||
cx2 = world[ii + 4];
|
||||
cy2 = world[ii + 5];
|
||||
x2 = world[ii + 6];
|
||||
y2 = world[ii + 7];
|
||||
tmpx = (x1 - cx1 * 2 + cx2) * 0.03f;
|
||||
tmpy = (y1 - cy1 * 2 + cy2) * 0.03f;
|
||||
dddfx = ((cx1 - cx2) * 3 - x1 + x2) * 0.006f;
|
||||
dddfy = ((cy1 - cy2) * 3 - y1 + y2) * 0.006f;
|
||||
ddfx = tmpx * 2 + dddfx;
|
||||
ddfy = tmpy * 2 + dddfy;
|
||||
dfx = (cx1 - x1) * 0.3f + tmpx + dddfx * 0.16666667f;
|
||||
dfy = (cy1 - y1) * 0.3f + tmpy + dddfy * 0.16666667f;
|
||||
curveLength = (float)Math.Sqrt(dfx * dfx + dfy * dfy);
|
||||
segments[0] = curveLength;
|
||||
for (ii = 1; ii < 8; ii++) {
|
||||
dfx += ddfx;
|
||||
dfy += ddfy;
|
||||
ddfx += dddfx;
|
||||
ddfy += dddfy;
|
||||
curveLength += (float)Math.Sqrt(dfx * dfx + dfy * dfy);
|
||||
segments[ii] = curveLength;
|
||||
}
|
||||
dfx += ddfx;
|
||||
dfy += ddfy;
|
||||
curveLength += (float)Math.Sqrt(dfx * dfx + dfy * dfy);
|
||||
segments[8] = curveLength;
|
||||
dfx += ddfx + dddfx;
|
||||
dfy += ddfy + dddfy;
|
||||
curveLength += (float)Math.Sqrt(dfx * dfx + dfy * dfy);
|
||||
segments[9] = curveLength;
|
||||
segment = 0;
|
||||
}
|
||||
|
||||
// Weight by segment length.
|
||||
p *= curveLength;
|
||||
for (; ; segment++) {
|
||||
float length = segments[segment];
|
||||
if (p > length) continue;
|
||||
if (segment == 0)
|
||||
p /= length;
|
||||
else {
|
||||
float prev = segments[segment - 1];
|
||||
p = segment + (p - prev) / (length - prev);
|
||||
}
|
||||
break;
|
||||
}
|
||||
AddCurvePosition(p * 0.1f, x1, y1, cx1, cy1, cx2, cy2, x2, y2, output, o, tangents || (i > 0 && space < MathUtils.Epsilon));
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
static void AddBeforePosition (float p, float[] temp, int i, float[] output, int o) {
|
||||
float x1 = temp[i], y1 = temp[i + 1], dx = temp[i + 2] - x1, dy = temp[i + 3] - y1, r = MathUtils.Atan2(dy, dx);
|
||||
output[o] = x1 + p * MathUtils.Cos(r);
|
||||
output[o + 1] = y1 + p * MathUtils.Sin(r);
|
||||
output[o + 2] = r;
|
||||
}
|
||||
|
||||
static void AddAfterPosition (float p, float[] temp, int i, float[] output, int o) {
|
||||
float x1 = temp[i + 2], y1 = temp[i + 3], dx = x1 - temp[i], dy = y1 - temp[i + 1], r = MathUtils.Atan2(dy, dx);
|
||||
output[o] = x1 + p * MathUtils.Cos(r);
|
||||
output[o + 1] = y1 + p * MathUtils.Sin(r);
|
||||
output[o + 2] = r;
|
||||
}
|
||||
|
||||
static void AddCurvePosition (float p, float x1, float y1, float cx1, float cy1, float cx2, float cy2, float x2, float y2,
|
||||
float[] output, int o, bool tangents) {
|
||||
if (p < MathUtils.Epsilon || float.IsNaN(p)) {
|
||||
output[o] = x1;
|
||||
output[o + 1] = y1;
|
||||
output[o + 2] = (float)Math.Atan2(cy1 - y1, cx1 - x1);
|
||||
return;
|
||||
}
|
||||
float tt = p * p, ttt = tt * p, u = 1 - p, uu = u * u, uuu = uu * u;
|
||||
float ut = u * p, ut3 = ut * 3, uut3 = u * ut3, utt3 = ut3 * p;
|
||||
float x = x1 * uuu + cx1 * uut3 + cx2 * utt3 + x2 * ttt, y = y1 * uuu + cy1 * uut3 + cy2 * utt3 + y2 * ttt;
|
||||
output[o] = x;
|
||||
output[o + 1] = y;
|
||||
if (tangents) {
|
||||
if (p < 0.001f)
|
||||
output[o + 2] = (float)Math.Atan2(cy1 - y1, cx1 - x1);
|
||||
else
|
||||
output[o + 2] = (float)Math.Atan2(y - (y1 * uu + cy1 * ut * 2 + cy2 * tt), x - (x1 * uu + cx1 * ut * 2 + cx2 * tt));
|
||||
}
|
||||
}
|
||||
|
||||
override public void Sort (Skeleton skeleton) {
|
||||
int slotIndex = slot.Data.index;
|
||||
Bone slotBone = slot.bone;
|
||||
if (skeleton.skin != null) SortPathSlot(skeleton, skeleton.skin, slotIndex, slotBone);
|
||||
if (skeleton.data.defaultSkin != null && skeleton.data.defaultSkin != skeleton.skin)
|
||||
SortPathSlot(skeleton, skeleton.data.defaultSkin, slotIndex, slotBone);
|
||||
SortPath(skeleton, slot.pose.attachment, slotBone);
|
||||
BonePose[] bones = this.bones.Items;
|
||||
int boneCount = this.bones.Count;
|
||||
for (int i = 0; i < boneCount; i++) {
|
||||
Bone bone = bones[i].bone;
|
||||
skeleton.SortBone(bone);
|
||||
skeleton.Constrained(bone);
|
||||
}
|
||||
skeleton.updateCache.Add(this);
|
||||
for (int i = 0; i < boneCount; i++)
|
||||
skeleton.SortReset(bones[i].bone.children);
|
||||
for (int i = 0; i < boneCount; i++)
|
||||
bones[i].bone.sorted = true;
|
||||
}
|
||||
|
||||
private void SortPathSlot (Skeleton skeleton, Skin skin, int slotIndex, Bone slotBone) {
|
||||
foreach (Skin.SkinEntry entry in skin.Attachments)
|
||||
if (entry.SlotIndex == slotIndex) SortPath(skeleton, entry.Attachment, slotBone);
|
||||
}
|
||||
|
||||
private void SortPath (Skeleton skeleton, Attachment attachment, Bone slotBone) {
|
||||
if (!(attachment is PathAttachment)) return;
|
||||
int[] pathBones = ((PathAttachment)attachment).bones;
|
||||
if (pathBones == null)
|
||||
skeleton.SortBone(slotBone);
|
||||
else {
|
||||
Bone[] bones = skeleton.bones.Items;
|
||||
for (int i = 0, n = pathBones.Length; i < n;) {
|
||||
int nn = pathBones[i++];
|
||||
nn += i;
|
||||
while (i < nn)
|
||||
skeleton.SortBone(bones[pathBones[i++]]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override public bool IsSourceActive { get { return slot.bone.active; } }
|
||||
|
||||
/// <summary>The bones that will be modified by this path constraint.</summary>
|
||||
public ExposedList<BonePose> Bones { get { return bones; } }
|
||||
/// <summary>The slot whose path attachment will be used to constrained the bones.</summary>
|
||||
public Slot Slot { get { return slot; } set { slot = value; } }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 731d05fbc2874c74984813ce4c5bb8df
|
||||
timeCreated: 1467213650
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,68 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated April 5, 2025. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2026, 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<PathConstraint, PathConstraintPose> {
|
||||
internal ExposedList<BoneData> bones = new ExposedList<BoneData>();
|
||||
internal SlotData slot;
|
||||
internal PositionMode positionMode;
|
||||
internal SpacingMode spacingMode;
|
||||
internal RotateMode rotateMode;
|
||||
internal float offsetRotation;
|
||||
|
||||
public PathConstraintData (string name)
|
||||
: base(name, new PathConstraintPose()) {
|
||||
}
|
||||
|
||||
override public IConstraint Create (Skeleton skeleton) {
|
||||
return new PathConstraint(this, skeleton);
|
||||
}
|
||||
|
||||
public ExposedList<BoneData> Bones { get { return bones; } }
|
||||
public SlotData Slot { get { return slot; } set { slot = 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 enum PositionMode {
|
||||
Fixed, Percent
|
||||
}
|
||||
|
||||
public enum SpacingMode {
|
||||
Length, Fixed, Percent, Proportional
|
||||
}
|
||||
|
||||
public enum RotateMode {
|
||||
Tangent, Chain, ChainScale
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9d836858269be96428428fb6764dfc3a
|
||||
timeCreated: 1467213651
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,59 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated April 5, 2025. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2026, 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 pose for a path constraint.
|
||||
/// </summary>
|
||||
public class PathConstraintPose : IPose<PathConstraintPose> {
|
||||
internal float position, spacing, mixRotate, mixX, mixY;
|
||||
|
||||
public void Set (PathConstraintPose pose) {
|
||||
position = pose.position;
|
||||
spacing = pose.spacing;
|
||||
mixRotate = pose.mixRotate;
|
||||
mixX = pose.mixX;
|
||||
mixY = pose.mixY;
|
||||
}
|
||||
|
||||
/// <summary>The position along the path.</summary>
|
||||
public float Position { get { return position; } set { position = value; } }
|
||||
/// <summary>The spacing between bones.</summary>
|
||||
public float Spacing { get { return spacing; } set { spacing = value; } }
|
||||
/// <summary>A percentage (0-1) that controls the mix between the constrained and unconstrained rotations.</summary>
|
||||
public float 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; } }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 40244ea3e8f247f4fac4363b219d8418
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,46 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated April 5, 2025. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2026, 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>Determines how physics and other non-deterministic updates are applied.</summary>
|
||||
public enum Physics {
|
||||
/// <summary>Physics are not updated or applied.</summary>
|
||||
None,
|
||||
|
||||
/// <summary>Physics are <see cref="PhysicsConstraint.Reset(Skeleton)">reset</see>.</summary>
|
||||
Reset,
|
||||
|
||||
/// <summary>Physics are updated and the pose from physics is applied.</summary>
|
||||
Update,
|
||||
|
||||
/// <summary>Physics are not updated but the pose from physics is applied.</summary>
|
||||
Pose
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2dab58587078e30468faff31f1ad4fa9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,317 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated April 5, 2025. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2026, 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>
|
||||
/// Applies physics to a bone.
|
||||
/// <para>
|
||||
/// See <a href="http://esotericsoftware.com/spine-physics-constraints">Physics constraints</a> in the Spine User Guide.</para>
|
||||
/// </summary>
|
||||
public class PhysicsConstraint : Constraint<PhysicsConstraint, PhysicsConstraintData, PhysicsConstraintPose> {
|
||||
internal BonePose bone;
|
||||
|
||||
bool reset = true;
|
||||
float ux, uy, cx, cy, tx, ty;
|
||||
float xOffset, xLag, xVelocity;
|
||||
float yOffset, yLag, yVelocity;
|
||||
float rotateOffset, rotateLag, rotateVelocity;
|
||||
float scaleOffset, scaleLag, scaleVelocity;
|
||||
|
||||
float remaining, lastTime;
|
||||
|
||||
public PhysicsConstraint (PhysicsConstraintData data, Skeleton skeleton)
|
||||
: base(data, new PhysicsConstraintPose(), new PhysicsConstraintPose()) {
|
||||
if (skeleton == null) throw new ArgumentNullException("skeleton", "skeleton cannot be null.");
|
||||
|
||||
bone = skeleton.bones.Items[data.bone.index].constrainedPose;
|
||||
}
|
||||
|
||||
override public IConstraint Copy (Skeleton skeleton) {
|
||||
var copy = new PhysicsConstraint(data, skeleton);
|
||||
copy.pose.Set(pose);
|
||||
return copy;
|
||||
}
|
||||
|
||||
/// <summary>Resets all physics state that was the result of previous movement. Use this after moving a bone to prevent physics
|
||||
/// from reacting to the movement.</summary>
|
||||
public void Reset (Skeleton skeleton) {
|
||||
remaining = 0;
|
||||
lastTime = skeleton.time;
|
||||
reset = true;
|
||||
xOffset = 0;
|
||||
xLag = 0;
|
||||
xVelocity = 0;
|
||||
yOffset = 0;
|
||||
yLag = 0;
|
||||
yVelocity = 0;
|
||||
rotateOffset = 0;
|
||||
rotateLag = 0;
|
||||
rotateVelocity = 0;
|
||||
scaleOffset = 0;
|
||||
scaleLag = 0;
|
||||
scaleVelocity = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Translates the physics constraint so the next <see cref="Update(Skeleton, Physics)"/> forces are applied as if the bone moved an
|
||||
/// additional amount in world space.
|
||||
/// </summary>
|
||||
public void Translate (float x, float y) {
|
||||
ux -= x;
|
||||
uy -= y;
|
||||
cx -= x;
|
||||
cy -= y;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rotates the physics constraint so the next <see cref="Update(Skeleton, Physics)"/> forces are applied as if the bone rotated around
|
||||
/// the specified point in world space.
|
||||
/// </summary>
|
||||
public void Rotate (float x, float y, float degrees) {
|
||||
float r = degrees * MathUtils.DegRad, cos = (float)Math.Cos(r), sin = (float)Math.Sin(r);
|
||||
float dx = cx - x, dy = cy - y;
|
||||
Translate(dx * cos - dy * sin - dx, dx * sin + dy * cos - dy);
|
||||
}
|
||||
|
||||
/// <summary>Applies the constraint to the constrained bones.</summary>
|
||||
override public void Update (Skeleton skeleton, Physics physics) {
|
||||
PhysicsConstraintPose p = appliedPose;
|
||||
float mix = p.mix;
|
||||
if (mix == 0) return;
|
||||
|
||||
bool x = data.x > 0, y = data.y > 0, rotateOrShearX = data.rotate > 0 || data.shearX > 0, scaleX = data.scaleX > 0;
|
||||
BonePose bone = this.bone;
|
||||
float l = bone.bone.data.length, t = data.step, z = 0;
|
||||
|
||||
switch (physics) {
|
||||
case Physics.None:
|
||||
return;
|
||||
case Physics.Reset:
|
||||
Reset(skeleton);
|
||||
goto case Physics.Update; // Fall through.
|
||||
case Physics.Update:
|
||||
float delta = Math.Max(skeleton.time - lastTime, 0), aa = remaining;
|
||||
remaining += delta;
|
||||
lastTime = skeleton.time;
|
||||
|
||||
float bx = bone.worldX, by = bone.worldY;
|
||||
if (reset) {
|
||||
reset = false;
|
||||
ux = bx;
|
||||
uy = by;
|
||||
} else {
|
||||
float a = remaining, i = p.inertia, f = skeleton.data.referenceScale, d = -1, m = 0, e = 0, qx = data.limit * delta,
|
||||
qy = qx * Math.Abs(skeleton.ScaleY);
|
||||
qx *= Math.Abs(skeleton.ScaleX);
|
||||
|
||||
if (x || y) {
|
||||
if (x) {
|
||||
float u = (ux - bx) * i;
|
||||
xOffset += u > qx ? qx : u < -qx ? -qx : u;
|
||||
ux = bx;
|
||||
}
|
||||
if (y) {
|
||||
float u = (uy - by) * i;
|
||||
yOffset += u > qy ? qy : u < -qy ? -qy : u;
|
||||
uy = by;
|
||||
}
|
||||
if (a >= t) {
|
||||
float xs = xOffset, ys = yOffset;
|
||||
d = (float)Math.Pow(p.damping, 60 * t);
|
||||
m = t * p.massInverse;
|
||||
e = p.strength;
|
||||
float w = f * p.wind, g = f * p.gravity;
|
||||
float ax = (w * skeleton.windX + g * skeleton.gravityX) * skeleton.scaleX;
|
||||
float ay = (w * skeleton.windY + g * skeleton.gravityY) * skeleton.ScaleY;
|
||||
do {
|
||||
if (x) {
|
||||
xVelocity += (ax - xOffset * e) * m;
|
||||
xOffset += xVelocity * t;
|
||||
xVelocity *= d;
|
||||
}
|
||||
if (y) {
|
||||
yVelocity -= (ay + yOffset * e) * m;
|
||||
yOffset += yVelocity * t;
|
||||
yVelocity *= d;
|
||||
}
|
||||
a -= t;
|
||||
} while (a >= t);
|
||||
xLag = xOffset - xs;
|
||||
yLag = yOffset - ys;
|
||||
}
|
||||
z = Math.Max(0, 1 - a / t);
|
||||
if (x) bone.worldX += (xOffset - xLag * z) * mix * data.x;
|
||||
if (y) bone.worldY += (yOffset - yLag * z) * mix * data.y;
|
||||
}
|
||||
if (rotateOrShearX || scaleX) {
|
||||
float ca = (float)Math.Atan2(bone.c, bone.a), c, s, mr = 0, dx = cx - bone.worldX, dy = cy - bone.worldY;
|
||||
if (dx > qx)
|
||||
dx = qx;
|
||||
else if (dx < -qx)
|
||||
dx = -qx;
|
||||
if (dy > qy)
|
||||
dy = qy;
|
||||
else if (dy < -qy)
|
||||
dy = -qy;
|
||||
if (rotateOrShearX) {
|
||||
mr = (data.rotate + data.shearX) * mix;
|
||||
z = rotateLag * Math.Max(0, 1 - aa / t);
|
||||
float r = (float)Math.Atan2(dy + ty, dx + tx) - ca - (rotateOffset - z) * mr;
|
||||
rotateOffset += (r - (float)Math.Ceiling(r * MathUtils.InvPI2 - 0.5f) * MathUtils.PI2) * i;
|
||||
r = (rotateOffset - z) * mr + ca;
|
||||
c = (float)Math.Cos(r);
|
||||
s = (float)Math.Sin(r);
|
||||
if (scaleX) {
|
||||
r = l * bone.WorldScaleX;
|
||||
if (r > 0) scaleOffset += (dx * c + dy * s) * i / r;
|
||||
}
|
||||
} else {
|
||||
c = (float)Math.Cos(ca);
|
||||
s = (float)Math.Sin(ca);
|
||||
float r = l * bone.WorldScaleX - scaleLag * Math.Max(0, 1 - aa / t);
|
||||
if (r > 0) scaleOffset += (dx * c + dy * s) * i / r;
|
||||
}
|
||||
a = remaining;
|
||||
if (a >= t) {
|
||||
if (d == -1) {
|
||||
d = (float)Math.Pow(p.damping, 60 * t);
|
||||
m = t * p.massInverse;
|
||||
e = p.strength;
|
||||
}
|
||||
float ax = p.wind * skeleton.windX + p.gravity * skeleton.gravityX;
|
||||
float ay = p.wind * skeleton.windY + p.gravity * skeleton.gravityY;
|
||||
float rs = rotateOffset, ss = scaleOffset, h = l / f;
|
||||
if (Spine.Bone.yDown) ay = -ay;
|
||||
while (true) {
|
||||
a -= t;
|
||||
if (scaleX) {
|
||||
scaleVelocity += (ax * c - ay * s - scaleOffset * e) * m;
|
||||
scaleOffset += scaleVelocity * t;
|
||||
scaleVelocity *= d;
|
||||
}
|
||||
if (rotateOrShearX) {
|
||||
rotateVelocity -= ((ax * s + ay * c) * h + rotateOffset * e) * m;
|
||||
rotateOffset += rotateVelocity * t;
|
||||
rotateVelocity *= d;
|
||||
if (a < t) break;
|
||||
float r = rotateOffset * mr + ca;
|
||||
c = (float)Math.Cos(r);
|
||||
s = (float)Math.Sin(r);
|
||||
} else if (a < t) //
|
||||
break;
|
||||
}
|
||||
rotateLag = rotateOffset - rs;
|
||||
scaleLag = scaleOffset - ss;
|
||||
}
|
||||
z = Math.Max(0, 1 - a / t);
|
||||
}
|
||||
remaining = a;
|
||||
}
|
||||
cx = bone.worldX;
|
||||
cy = bone.worldY;
|
||||
break;
|
||||
case Physics.Pose:
|
||||
z = Math.Max(0, 1 - remaining / t);
|
||||
if (x) bone.worldX += (xOffset - xLag * z) * mix * data.x;
|
||||
if (y) bone.worldY += (yOffset - yLag * z) * mix * data.y;
|
||||
break;
|
||||
}
|
||||
|
||||
if (rotateOrShearX) {
|
||||
float o = (rotateOffset - rotateLag * z) * mix, s, c, a;
|
||||
if (data.shearX > 0) {
|
||||
float r = 0;
|
||||
if (data.rotate > 0) {
|
||||
r = o * data.rotate;
|
||||
s = (float)Math.Sin(r);
|
||||
c = (float)Math.Cos(r);
|
||||
a = bone.b;
|
||||
bone.b = c * a - s * bone.d;
|
||||
bone.d = s * a + c * bone.d;
|
||||
}
|
||||
r += o * data.shearX;
|
||||
s = (float)Math.Sin(r);
|
||||
c = (float)Math.Cos(r);
|
||||
a = bone.a;
|
||||
bone.a = c * a - s * bone.c;
|
||||
bone.c = s * a + c * bone.c;
|
||||
} else {
|
||||
o *= data.rotate;
|
||||
s = (float)Math.Sin(o);
|
||||
c = (float)Math.Cos(o);
|
||||
a = bone.a;
|
||||
bone.a = c * a - s * bone.c;
|
||||
bone.c = s * a + c * bone.c;
|
||||
a = bone.b;
|
||||
bone.b = c * a - s * bone.d;
|
||||
bone.d = s * a + c * bone.d;
|
||||
}
|
||||
}
|
||||
if (scaleX) {
|
||||
float s = 1 + (scaleOffset - scaleLag * z) * mix * data.scaleX;
|
||||
bone.a *= s;
|
||||
bone.c *= s;
|
||||
switch (data.scaleYMode) {
|
||||
case ScaleYMode.Uniform: {
|
||||
bone.b *= s;
|
||||
bone.d *= s;
|
||||
break;
|
||||
}
|
||||
case ScaleYMode.Volume: {
|
||||
s = Math.Abs(s);
|
||||
s = s >= 0.7f ? 1 / s : 4 - 3.67347f * s;
|
||||
bone.b *= s;
|
||||
bone.d *= s;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (physics != Physics.Pose) {
|
||||
tx = l * bone.a;
|
||||
ty = l * bone.c;
|
||||
}
|
||||
bone.ModifyWorld(skeleton.update);
|
||||
}
|
||||
|
||||
override public void Sort (Skeleton skeleton) {
|
||||
Bone bone = this.bone.bone;
|
||||
skeleton.SortBone(bone);
|
||||
skeleton.updateCache.Add(this);
|
||||
skeleton.SortReset(bone.children);
|
||||
skeleton.Constrained(bone);
|
||||
}
|
||||
|
||||
override public bool IsSourceActive { get { return bone.bone.active; } }
|
||||
|
||||
/// <summary>The bone constrained by this physics constraint.</summary>
|
||||
public BonePose Bone { get { return bone; } set { bone = value; } }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e2816491d178b3b4986920107586ce55
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,92 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated April 5, 2025. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2026, 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>
|
||||
/// Stores the setup pose for a <see cref="PhysicsConstraint"/>.
|
||||
/// <para>
|
||||
/// See <a href="http://esotericsoftware.com/spine-physics-constraints">Physics constraints</a> in the Spine User Guide.</para>
|
||||
/// </summary>
|
||||
public class PhysicsConstraintData : ConstraintData<PhysicsConstraint, PhysicsConstraintPose> {
|
||||
internal BoneData bone;
|
||||
internal float x, y, rotate, scaleX, shearX, limit, step;
|
||||
internal bool inertiaGlobal, strengthGlobal, dampingGlobal, massGlobal, windGlobal, gravityGlobal, mixGlobal;
|
||||
internal ScaleYMode scaleYMode = ScaleYMode.None;
|
||||
|
||||
public PhysicsConstraintData (string name)
|
||||
: base(name, new PhysicsConstraintPose()) {
|
||||
}
|
||||
|
||||
override public IConstraint Create (Skeleton skeleton) {
|
||||
return new PhysicsConstraint(this, skeleton);
|
||||
}
|
||||
|
||||
/// <summary>The bone constrained by this physics constraint.</summary>
|
||||
public BoneData Bone { get { return bone; } }
|
||||
|
||||
/// <summary>The time in milliseconds required to advance the physics simulation one step.</summary>
|
||||
public float Step { get { return step; } set { step = value; } }
|
||||
/// <summary>Physics influence on x translation, 0-1.</summary>
|
||||
public float X { get { return x; } set { x = value; } }
|
||||
/// <summary>Physics influence on y translation, 0-1.</summary>
|
||||
public float Y { get { return y; } set { y = value; } }
|
||||
/// <summary>Physics influence on rotation, 0-1.</summary>
|
||||
public float Rotate { get { return rotate; } set { rotate = value; } }
|
||||
/// <summary>Physics influence on scaleX, 0-1.</summary>
|
||||
public float ScaleX { get { return scaleX; } set { scaleX = value; } }
|
||||
/// <summary>Physics influence on shearX, 0-1.</summary>
|
||||
public float ShearX { get { return shearX; } set { shearX = value; } }
|
||||
/// <summary>Movement greater than the limit will not have a greater effect on physics.</summary>
|
||||
public float Limit { get { return limit; } set { limit = value; } }
|
||||
|
||||
/// <summary>
|
||||
/// Determines how the <see cref="BonePose.scaleY"/> changes when <see cref="PhysicsConstraintData.scaleX"/> sets
|
||||
/// <see cref="BonePose.scaleX"/>
|
||||
/// </summary>
|
||||
public ScaleYMode ScaleYMode {
|
||||
get { return scaleYMode; }
|
||||
set { scaleYMode = value; }
|
||||
}
|
||||
|
||||
/// <summary>True when this constraint's inertia is controlled by global slider timelines.</summary>
|
||||
public bool InertiaGlobal { get { return inertiaGlobal; } set { inertiaGlobal = value; } }
|
||||
/// <summary>True when this constraint's strength is controlled by global slider timelines.</summary>
|
||||
public bool StrengthGlobal { get { return strengthGlobal; } set { strengthGlobal = value; } }
|
||||
/// <summary>True when this constraint's damping is controlled by global slider timelines.</summary>
|
||||
public bool DampingGlobal { get { return dampingGlobal; } set { dampingGlobal = value; } }
|
||||
/// <summary>True when this constraint's mass is controlled by global slider timelines.</summary>
|
||||
public bool MassGlobal { get { return massGlobal; } set { massGlobal = value; } }
|
||||
/// <summary>True when this constraint's wind is controlled by global slider timelines.</summary>
|
||||
public bool WindGlobal { get { return windGlobal; } set { windGlobal = value; } }
|
||||
/// <summary>True when this constraint's gravity is controlled by global slider timelines.</summary>
|
||||
public bool GravityGlobal { get { return gravityGlobal; } set { gravityGlobal = value; } }
|
||||
/// <summary>True when this constraint's mix is controlled by global slider timelines.</summary>
|
||||
public bool MixGlobal { get { return mixGlobal; } set { mixGlobal = value; } }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 438688f6194e6dc40953a23d05d48e1a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,65 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated April 5, 2025. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2026, 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 pose for a physics constraint.
|
||||
/// </summary>
|
||||
public class PhysicsConstraintPose : IPose<PhysicsConstraintPose> {
|
||||
internal float inertia, strength, damping, massInverse, wind, gravity, mix;
|
||||
|
||||
public void Set (PhysicsConstraintPose pose) {
|
||||
inertia = pose.inertia;
|
||||
strength = pose.strength;
|
||||
damping = pose.damping;
|
||||
massInverse = pose.massInverse;
|
||||
wind = pose.wind;
|
||||
gravity = pose.gravity;
|
||||
mix = pose.mix;
|
||||
}
|
||||
|
||||
/// <summary>Controls how much bone movement is converted into physics movement.</summary>
|
||||
public float Inertia { get { return inertia; } set { inertia = value; } }
|
||||
/// <summary>The amount of force used to return properties to the unconstrained value.</summary>
|
||||
public float Strength { get { return strength; } set { strength = value; } }
|
||||
/// <summary>Reduces the speed of physics movements, with more of a reduction at higher speeds.</summary>
|
||||
public float Damping { get { return damping; } set { damping = value; } }
|
||||
/// <summary>Determines susceptibility to acceleration.</summary>
|
||||
public float MassInverse { get { return massInverse; } set { massInverse = value; } }
|
||||
/// <summary>Applies a constant force along the <see cref="Skeleton.WindX"/>, <see cref="Skeleton.WindY"/> vector.</summary>
|
||||
public float Wind { get { return wind; } set { wind = value; } }
|
||||
/// <summary>Applies a constant force along the <see cref="Skeleton.GravityX"/>, <see cref="Skeleton.GravityY"/> vector.</summary>
|
||||
public float Gravity { get { return gravity; } set { gravity = value; } }
|
||||
/// <summary>A percentage (0+) that controls the mix between the constrained and unconstrained poses.</summary>
|
||||
public float Mix { get { return mix; } set { mix = value; } }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6974fd8e5e17807499e8fed214659cf9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,106 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated April 5, 2025. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2026, 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 {
|
||||
internal interface IPosedInternal {
|
||||
// replaces "object.pose == object.appliedPose" of reference implementation.
|
||||
bool PoseEqualsApplied { get; }
|
||||
// replaces "object.appliedPose = object.pose" of reference implementation.
|
||||
void Unconstrained ();
|
||||
// replaces "object.appliedPose = object.constrainedPose" of reference implementation.
|
||||
void Constrained ();
|
||||
// replaces "object.appliedPose.Set(object.pose)" of reference implementation.
|
||||
void ResetConstrained ();
|
||||
}
|
||||
|
||||
public interface IPosed {
|
||||
void SetupPose ();
|
||||
}
|
||||
|
||||
/// <summary>The base class for an object with a number of poses:
|
||||
/// <list type="bullet">
|
||||
/// <item><see cref="Data"/>: The setup pose.</item>
|
||||
/// <item><see cref="Pose"/>: The unconstrained pose. Set by animations and application code.</item>
|
||||
/// <item><see cref="AppliedPose"/>: The pose to use for rendering. Possibly modified by constraints.</item>
|
||||
/// </list>
|
||||
/// </summary>
|
||||
public class Posed<D, P> : IPosed, IPosedInternal
|
||||
where D : PosedData<P>
|
||||
where P : IPose<P> {
|
||||
|
||||
internal readonly D data;
|
||||
internal readonly P pose, constrainedPose;
|
||||
internal P appliedPose;
|
||||
|
||||
protected Posed (D data, P pose, P constrainedPose) {
|
||||
if (data == null) throw new ArgumentNullException("data", "data cannot be null.");
|
||||
this.data = data;
|
||||
this.pose = pose;
|
||||
this.constrainedPose = constrainedPose;
|
||||
appliedPose = pose;
|
||||
}
|
||||
|
||||
/// <summary>Sets the unconstrained pose to the setup pose.</summary>
|
||||
public virtual void SetupPose () {
|
||||
pose.Set(data.setupPose);
|
||||
}
|
||||
|
||||
bool IPosedInternal.PoseEqualsApplied {
|
||||
get { return (object)pose == (object)appliedPose; }
|
||||
}
|
||||
|
||||
void IPosedInternal.Unconstrained () {
|
||||
appliedPose = pose;
|
||||
}
|
||||
|
||||
void IPosedInternal.Constrained () {
|
||||
appliedPose = constrainedPose;
|
||||
}
|
||||
|
||||
void IPosedInternal.ResetConstrained () {
|
||||
appliedPose.Set(pose);
|
||||
}
|
||||
|
||||
/// <summary>The setup pose data. May be shared with multiple instances.</summary>
|
||||
public D Data { get { return data; } }
|
||||
|
||||
/// <summary>The unconstrained pose for this object, set by animations and application code.</summary>
|
||||
public P Pose { get { return pose; } }
|
||||
|
||||
/// <summary>The pose to use for rendering. If no constraints modify this pose, this is the same as <see cref="Pose"/>. Otherwise it is a
|
||||
/// copy of <see cref="Pose"/> modified by constraints.</summary>
|
||||
public P AppliedPose { get { return appliedPose; } }
|
||||
|
||||
override public string ToString () {
|
||||
return data.name;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 208c5c9065683a84e8166c4ce75c8951
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,55 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated April 5, 2025. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2026, 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 interface IPosedActive {
|
||||
bool Active { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>A posed object that may be active or inactive.</summary>
|
||||
public class PosedActive<D, P> : Posed<D, P>, IPosedActive
|
||||
where D : PosedData<P>
|
||||
where P : IPose<P> {
|
||||
|
||||
internal bool active;
|
||||
|
||||
protected PosedActive (D data, P pose, P constrained)
|
||||
: base(data, pose, constrained) {
|
||||
SetupPose();
|
||||
}
|
||||
|
||||
/// <summary>Returns false when this constraint won't be updated by
|
||||
/// <see cref="Skeleton.UpdateWorldTransform(Physics)"/> because a skin is required and the
|
||||
/// <see cref="Skeleton.Skin">active skin</see> does not contain this item. See <see cref="Skin.Bones"/>,
|
||||
/// <see cref="Skin.Constraints"/>, <see cref="PosedData{P}.SkinRequired"/>, and <see cref="Skeleton.UpdateCache()"/>.</summary>
|
||||
public bool Active { get { return active; } set { active = value; } }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d3e0989017e310f44900496191b7d268
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,72 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated April 5, 2025. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2026, 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 interface IPosedData {
|
||||
bool SkinRequired { get; }
|
||||
}
|
||||
|
||||
/// <summary>The base class for storing setup data for a posed object. May be shared with multiple instances.</summary>
|
||||
public class PosedData<P> : IPosedData
|
||||
where P : IPose<P> {
|
||||
|
||||
internal readonly string name;
|
||||
internal readonly P setupPose;
|
||||
internal bool skinRequired;
|
||||
|
||||
protected PosedData (string name, P setupPose) {
|
||||
if (name == null) throw new ArgumentNullException("name", "name cannot be null.");
|
||||
this.name = name;
|
||||
this.setupPose = setupPose;
|
||||
}
|
||||
|
||||
public string Name { get { return name; } }
|
||||
|
||||
/// <summary>The setup pose that most animations are relative to.</summary>
|
||||
public P GetSetupPose () {
|
||||
return setupPose;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When true, <see cref="Skeleton.UpdateWorldTransform(Physics)"/> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 984c45a4e280725489d63d5bf5cf526e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,605 @@
|
||||
/******************************************************************************
|
||||
* Spine Runtimes License Agreement
|
||||
* Last updated April 5, 2025. Replaces all prior versions.
|
||||
*
|
||||
* Copyright (c) 2013-2026, 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_3_OR_NEWER
|
||||
#define IS_UNITY
|
||||
#endif
|
||||
|
||||
using System;
|
||||
|
||||
namespace Spine {
|
||||
#if IS_UNITY
|
||||
using Color32F = UnityEngine.Color;
|
||||
#endif
|
||||
/// <summary>Stores bones and slots to be posed by animations and application code. Multiple skeleton instances can share the same
|
||||
/// <see cref="SkeletonData"/>, including animations, attachments, and skins.
|
||||
/// <para>After posing, call <see cref="UpdateWorldTransform(Physics)"/> to apply constraints and compute world transforms for
|
||||
/// rendering.</para>
|
||||
/// <para>See <see href="https://esotericsoftware.com/spine-runtime-architecture#Instance-objects">Instance objects</see> in the
|
||||
/// Spine Runtimes Guide.</para></summary>
|
||||
public class Skeleton {
|
||||
static private readonly int[] quadTriangles = { 0, 1, 2, 2, 3, 0 };
|
||||
internal SkeletonData data;
|
||||
internal ExposedList<Bone> bones;
|
||||
internal ExposedList<Slot> slots;
|
||||
internal readonly DrawOrder drawOrder;
|
||||
internal ExposedList<IConstraint> constraints;
|
||||
internal ExposedList<PhysicsConstraint> physics;
|
||||
internal ExposedList<object> updateCache = new ExposedList<object>();
|
||||
internal ExposedList<IPosedInternal> resetCache = new ExposedList<IPosedInternal>(16);
|
||||
internal Skin skin;
|
||||
// Color is a struct, set to protected to prevent
|
||||
// Color32F color = slot.color; color.a = 0.5;
|
||||
// modifying just a copy of the struct instead of the original
|
||||
// object as in reference implementation.
|
||||
protected Color32F color;
|
||||
internal float x, y, scaleX = 1, time, windX = 1, windY = 0, gravityX = 0, gravityY = 1;
|
||||
/// <summary>Private to enforce usage of ScaleY getter taking Bone.yDown into account.</summary>
|
||||
private float scaleY = 1;
|
||||
internal int update;
|
||||
|
||||
public Skeleton (SkeletonData data) {
|
||||
if (data == null) throw new ArgumentNullException("data", "data cannot be null.");
|
||||
this.data = data;
|
||||
|
||||
bones = new ExposedList<Bone>(data.bones.Count);
|
||||
Bone[] bonesItems = this.bones.Items;
|
||||
foreach (BoneData boneData in data.bones) {
|
||||
Bone bone;
|
||||
if (boneData.parent == null) {
|
||||
bone = new Bone(boneData, null);
|
||||
} else {
|
||||
Bone parent = bonesItems[boneData.parent.index];
|
||||
bone = new Bone(boneData, parent);
|
||||
parent.children.Add(bone);
|
||||
}
|
||||
this.bones.Add(bone);
|
||||
}
|
||||
|
||||
slots = new ExposedList<Slot>(data.slots.Count);
|
||||
foreach (SlotData slotData in data.slots)
|
||||
slots.Add(new Slot(slotData, this));
|
||||
drawOrder = new DrawOrder(slots);
|
||||
|
||||
physics = new ExposedList<PhysicsConstraint>(8);
|
||||
constraints = new ExposedList<IConstraint>(data.constraints.Count);
|
||||
foreach (IConstraintData constraintData in data.constraints) {
|
||||
IConstraint constraint = constraintData.Create(this);
|
||||
PhysicsConstraint physicsConstraint = constraint as PhysicsConstraint;
|
||||
if (physicsConstraint != null) physics.Add(physicsConstraint);
|
||||
constraints.Add(constraint);
|
||||
}
|
||||
physics.TrimExcess();
|
||||
|
||||
color = new Color32F(1, 1, 1, 1);
|
||||
|
||||
UpdateCache();
|
||||
}
|
||||
|
||||
/// <summary>Copy constructor.</summary>
|
||||
public Skeleton (Skeleton skeleton) {
|
||||
if (skeleton == null) throw new ArgumentNullException("skeleton", "skeleton cannot be null.");
|
||||
data = skeleton.data;
|
||||
|
||||
bones = new ExposedList<Bone>(skeleton.bones.Count);
|
||||
foreach (Bone bone in skeleton.bones) {
|
||||
Bone newBone;
|
||||
if (bone.parent == null)
|
||||
newBone = new Bone(bone, null);
|
||||
else {
|
||||
Bone parent = bones.Items[bone.parent.data.index];
|
||||
newBone = new Bone(bone, parent);
|
||||
parent.children.Add(newBone);
|
||||
}
|
||||
bones.Add(newBone);
|
||||
}
|
||||
|
||||
slots = new ExposedList<Slot>(skeleton.slots.Count);
|
||||
Bone[] bonesItems = bones.Items;
|
||||
foreach (Slot slot in skeleton.slots)
|
||||
slots.Add(new Slot(slot, bonesItems[slot.bone.data.index], this));
|
||||
|
||||
drawOrder = new DrawOrder(slots);
|
||||
drawOrder.pose.Clear();
|
||||
foreach (Slot slot in skeleton.drawOrder.pose)
|
||||
drawOrder.pose.Add(slots.Items[slot.data.index]);
|
||||
|
||||
physics = new ExposedList<PhysicsConstraint>(skeleton.physics.Count);
|
||||
constraints = new ExposedList<IConstraint>(skeleton.constraints.Count);
|
||||
foreach (IConstraint other in skeleton.constraints) {
|
||||
IConstraint constraint = other.Copy(this);
|
||||
PhysicsConstraint physicsConstraint = constraint as PhysicsConstraint;
|
||||
if (physicsConstraint != null) physics.Add(physicsConstraint);
|
||||
constraints.Add(constraint);
|
||||
}
|
||||
|
||||
skin = skeleton.skin;
|
||||
color = skeleton.color;
|
||||
x = skeleton.x;
|
||||
y = skeleton.y;
|
||||
scaleX = skeleton.scaleX;
|
||||
scaleY = skeleton.scaleY;
|
||||
time = skeleton.time;
|
||||
|
||||
UpdateCache();
|
||||
}
|
||||
|
||||
/// <summary>Caches information about bones and constraints. Must be called if the <see cref="Skin"/> is modified or if bones, constraints, or
|
||||
/// constraints, or weighted path attachments are added or removed.</summary>
|
||||
public void UpdateCache () {
|
||||
updateCache.Clear();
|
||||
resetCache.Clear();
|
||||
|
||||
drawOrder.Unconstrained();
|
||||
Slot[] slots = this.slots.Items;
|
||||
for (int i = 0, n = this.slots.Count; i < n; i++) {
|
||||
((IPosedInternal)slots[i]).Unconstrained();
|
||||
}
|
||||
|
||||
int boneCount = this.bones.Count;
|
||||
Bone[] bones = this.bones.Items;
|
||||
for (int i = 0; i < boneCount; i++) {
|
||||
Bone bone = bones[i];
|
||||
bone.sorted = bone.data.skinRequired;
|
||||
bone.active = !bone.sorted;
|
||||
((IPosedInternal)bone).Unconstrained();
|
||||
}
|
||||
if (skin != null) {
|
||||
BoneData[] skinBones = skin.bones.Items;
|
||||
for (int i = 0, n = skin.bones.Count; i < n; i++) {
|
||||
Bone bone = bones[skinBones[i].index];
|
||||
do {
|
||||
bone.sorted = false;
|
||||
bone.active = true;
|
||||
bone = bone.parent;
|
||||
} while (bone != null);
|
||||
}
|
||||
}
|
||||
IConstraint[] constraints = this.constraints.Items;
|
||||
|
||||
{ // scope added to prevent compile error of n already being declared in enclosing scope
|
||||
int n = this.constraints.Count;
|
||||
for (int i = 0; i < n; i++) {
|
||||
((IPosedInternal)constraints[i]).Unconstrained();
|
||||
}
|
||||
for (int i = 0; i < n; i++) {
|
||||
IConstraint constraint = constraints[i];
|
||||
constraint.Active = constraint.IsSourceActive
|
||||
&& (!constraint.IData.SkinRequired || (skin != null && skin.constraints.Contains(constraint.IData)));
|
||||
if (constraint.Active) constraint.Sort(this);
|
||||
}
|
||||
|
||||
for (int i = 0; i < boneCount; i++)
|
||||
SortBone(bones[i]);
|
||||
|
||||
object[] updateCache = this.updateCache.Items;
|
||||
n = this.updateCache.Count;
|
||||
for (int i = 0; i < n; i++) {
|
||||
Bone bone = updateCache[i] as Bone;
|
||||
if (bone != null) updateCache[i] = bone.appliedPose;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void Constrained (IPosedInternal obj) {
|
||||
if (obj.PoseEqualsApplied) { // if (obj.pose == obj.appliedPose) {
|
||||
obj.Constrained();
|
||||
resetCache.Add(obj);
|
||||
}
|
||||
}
|
||||
|
||||
internal void SortBone (Bone bone) {
|
||||
if (bone.sorted || !bone.active) return;
|
||||
Bone parent = bone.parent;
|
||||
if (parent != null) SortBone(parent);
|
||||
bone.sorted = true;
|
||||
updateCache.Add(bone);
|
||||
}
|
||||
|
||||
internal void SortReset (ExposedList<Bone> bones) {
|
||||
Bone[] items = bones.Items;
|
||||
for (int i = 0, n = bones.Count; i < n; i++) {
|
||||
Bone bone = items[i];
|
||||
if (bone.active) {
|
||||
if (bone.sorted) SortReset(bone.children);
|
||||
bone.sorted = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the world transform for each bone and applies all constraints.
|
||||
/// <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 (Physics physics) {
|
||||
update++;
|
||||
|
||||
if (drawOrder.appliedPose == drawOrder.constrainedPose) drawOrder.ResetConstrained();
|
||||
IPosedInternal[] resetCache = this.resetCache.Items;
|
||||
for (int i = 0, n = this.resetCache.Count; i < n; i++) {
|
||||
resetCache[i].ResetConstrained();
|
||||
}
|
||||
|
||||
object[] updateCache = this.updateCache.Items;
|
||||
for (int i = 0, n = this.updateCache.Count; i < n; i++)
|
||||
((IUpdate)updateCache[i]).Update(this, physics);
|
||||
}
|
||||
|
||||
/// <summary>Sets the bones, constraints, slots, and draw order to their setup pose values.</summary>
|
||||
public void SetupPose () {
|
||||
SetupPoseBones();
|
||||
SetupPoseSlots();
|
||||
}
|
||||
|
||||
/// <summary>Sets the bones and constraints to their setup pose values.</summary>
|
||||
public void SetupPoseBones () {
|
||||
Bone[] bones = this.bones.Items;
|
||||
for (int i = 0, n = this.bones.Count; i < n; i++)
|
||||
bones[i].SetupPose();
|
||||
|
||||
IConstraint[] constraints = this.constraints.Items;
|
||||
for (int i = 0, n = this.constraints.Count; i < n; i++)
|
||||
constraints[i].SetupPose();
|
||||
}
|
||||
|
||||
/// <summary>Sets the slots and draw order to their setup pose values.</summary>
|
||||
public void SetupPoseSlots () {
|
||||
drawOrder.SetupPose();
|
||||
Slot[] slots = this.slots.Items;
|
||||
for (int i = 0, n = this.slots.Count; i < n; i++)
|
||||
slots[i].SetupPose();
|
||||
}
|
||||
|
||||
/// <summary>The skeleton's setup pose data.</summary>
|
||||
public SkeletonData Data { get { return data; } }
|
||||
/// <summary>The skeleton's bones, sorted parent first. The root bone is always the first bone.</summary>
|
||||
public ExposedList<Bone> Bones { get { return bones; } }
|
||||
/// <summary>
|
||||
/// The list of bones and constraints, sorted in the order they should be updated, as computed by <see cref="UpdateCache()"/>.
|
||||
/// </summary>
|
||||
public ExposedList<object> UpdateCacheList { get { return updateCache; } }
|
||||
/// <summary>Returns the root bone, or null if the skeleton has no bones.</summary>
|
||||
public Bone RootBone {
|
||||
get { return bones.Count == 0 ? null : bones.Items[0]; }
|
||||
}
|
||||
|
||||
/// <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
|
||||
/// repeatedly.</summary>
|
||||
/// <returns>May be null.</returns>
|
||||
public Bone FindBone (string boneName) {
|
||||
if (boneName == null) throw new ArgumentNullException("boneName", "boneName cannot be null.");
|
||||
Bone[] bones = this.bones.Items;
|
||||
for (int i = 0, n = this.bones.Count; i < n; i++) {
|
||||
Bone bone = bones[i];
|
||||
if (bone.data.name == boneName) return bone;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>The skeleton's slots. To add a slot, also add it to <see cref="DrawOrder.Pose"/>.</summary>
|
||||
public ExposedList<Slot> Slots { get { return slots; } }
|
||||
|
||||
/// <summary>Finds a slot by comparing each slot's name. It is more efficient to cache the results of this method than to call it
|
||||
/// repeatedly.</summary>
|
||||
/// <returns>May be null.</returns>
|
||||
public Slot FindSlot (string slotName) {
|
||||
if (slotName == null) throw new ArgumentNullException("slotName", "slotName cannot be null.");
|
||||
Slot[] slots = this.slots.Items;
|
||||
for (int i = 0, n = this.slots.Count; i < n; i++) {
|
||||
Slot slot = slots[i];
|
||||
if (slot.data.name == slotName) return slot;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The skeleton's draw order. Use <see cref="DrawOrder.AppliedPose"/> for rendering and
|
||||
/// <see cref="DrawOrder.Pose"/> for changing the draw order.
|
||||
/// </summary>
|
||||
public DrawOrder DrawOrder {
|
||||
get { return drawOrder; }
|
||||
}
|
||||
|
||||
/// <summary>The skeleton's current skin. May be null. See <see cref="SetSkin(Spine.Skin)"/></summary>
|
||||
public Skin Skin {
|
||||
/// <summary>The skeleton's current skin. May be null.</summary>
|
||||
get { return skin; }
|
||||
/// <summary>Sets a skin, <see cref="SetSkin(Skin)"/>.</summary>
|
||||
set { SetSkin(value); }
|
||||
}
|
||||
|
||||
/// <summary>Sets a skin by name (see <see cref="SetSkin(Spine.Skin)"/>).</summary>
|
||||
public void SetSkin (string skinName) {
|
||||
Skin foundSkin = data.FindSkin(skinName);
|
||||
if (foundSkin == null) throw new ArgumentException("Skin not found: " + skinName, "skinName");
|
||||
SetSkin(foundSkin);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>Sets the skin used to look up attachments before looking in <see cref="SkeletonData.DefaultSkin"/>. If the skin is
|
||||
/// changed, <see cref="UpdateCache()"/> is called.
|
||||
/// </para>
|
||||
/// <para>Attachments from the new skin are attached if the corresponding attachment from the old skin was attached. If there was no
|
||||
/// old skin, each slot's setup mode attachment is attached from the new skin.
|
||||
/// </para>
|
||||
/// <para>After changing the skin, the visible attachments can be reset to those attached in the setup pose by calling
|
||||
/// <see cref="Skeleton.SetupPoseSlots()"/>. Also, often <see cref="AnimationState.Apply(Skeleton)"/> is called before the next time the skeleton is
|
||||
/// rendered to allow any attachment keys in the current animation(s) to hide or show attachments from the new skin.</para>
|
||||
/// </summary>
|
||||
/// <param name="newSkin">May be null.</param>
|
||||
public void SetSkin (Skin newSkin) {
|
||||
if (newSkin == skin) return;
|
||||
if (newSkin != null) {
|
||||
if (skin != null)
|
||||
newSkin.AttachAll(this, skin);
|
||||
else {
|
||||
Slot[] slots = this.slots.Items;
|
||||
for (int i = 0, n = this.slots.Count; i < n; i++) {
|
||||
Slot slot = slots[i];
|
||||
string name = slot.data.attachmentName;
|
||||
if (name != null) {
|
||||
Attachment attachment = newSkin.GetAttachment(i, name);
|
||||
if (attachment != null) slot.pose.Attachment = attachment;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
skin = newSkin;
|
||||
UpdateCache();
|
||||
}
|
||||
|
||||
/// <summary>Finds an attachment by looking in the <see cref="Skeleton.Skin"/> and <see cref="SkeletonData.DefaultSkin"/> using the slot name and attachment
|
||||
/// name.</summary>
|
||||
/// <returns>May be null.</returns>
|
||||
/// <seealso cref="GetAttachment(int, string)"/>
|
||||
public Attachment GetAttachment (string slotName, string placeholder) {
|
||||
SlotData slot = data.FindSlot(slotName);
|
||||
if (slot == null) throw new ArgumentException("Slot not found: " + slotName, "slotName");
|
||||
return GetAttachment(slot.index, placeholder);
|
||||
}
|
||||
|
||||
/// <summary>Finds an attachment by looking in the skin and skeletonData.defaultSkin using the slot index and skin
|
||||
/// placeholder name. First the skin is checked and if the attachment was not found, the default skin is checked.</summary>
|
||||
/// <para>
|
||||
/// See <a href="http://esotericsoftware.com/spine-runtime-skins">Runtime skins</a> in the Spine Runtimes Guide.</para>
|
||||
/// <returns>May be null.</returns>
|
||||
public Attachment GetAttachment (int slotIndex, string placeholder) {
|
||||
if (placeholder == null) throw new ArgumentNullException("placeholder", "placeholder cannot be null.");
|
||||
if (skin != null) {
|
||||
Attachment attachment = skin.GetAttachment(slotIndex, placeholder);
|
||||
if (attachment != null) return attachment;
|
||||
}
|
||||
if (data.defaultSkin != null) return data.defaultSkin.GetAttachment(slotIndex, placeholder);
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>A convenience method to set an attachment by finding the slot with <see cref="FindSlot(string)"/>, finding the attachment with
|
||||
/// <see cref="GetAttachment(int, string)"/>, then setting the slot's <see cref="SlotPose.Attachment"/>.</summary>
|
||||
/// <param name="placeholder">May be null to clear the slot's attachment.</param>
|
||||
public void SetAttachment (string slotName, string placeholder) {
|
||||
if (slotName == null) throw new ArgumentNullException("slotName", "slotName cannot be null.");
|
||||
|
||||
Slot slot = FindSlot(slotName);
|
||||
if (slot == null) throw new ArgumentException("Slot not found: " + slotName, "slotName");
|
||||
Attachment attachment = null;
|
||||
if (placeholder != null) {
|
||||
attachment = GetAttachment(slot.data.index, placeholder);
|
||||
if (attachment == null)
|
||||
throw new ArgumentException("Attachment not found: " + placeholder + ", for slot: " + slotName, "placeholder");
|
||||
}
|
||||
slot.pose.Attachment = attachment;
|
||||
}
|
||||
|
||||
/// <summary>The skeleton's constraints.</summary>
|
||||
public ExposedList<IConstraint> Constraints { get { return constraints; } }
|
||||
/// <summary>The skeleton's physics constraints.</summary>
|
||||
public ExposedList<PhysicsConstraint> PhysicsConstraints { get { return physics; } }
|
||||
|
||||
/// <summary>Finds a constraint of the specified type by comparing each constraint'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 T FindConstraint<T> (string constraintName) where T : class, IConstraint {
|
||||
if (constraintName == null) throw new ArgumentNullException("constraintName", "constraintName cannot be null.");
|
||||
|
||||
IConstraint[] constraints = this.constraints.Items;
|
||||
for (int i = 0, n = this.constraints.Count; i < n; i++) {
|
||||
IConstraint constraint = constraints[i];
|
||||
if (constraint is T && constraint.IData.Name == constraintName) return (T)constraint;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>Returns the axis aligned bounding box (AABB) of the region and mesh attachments for the applied pose.</summary>
|
||||
/// <param name="x">The horizontal distance between the skeleton origin and the left side of the AABB.</param>
|
||||
/// <param name="y">The vertical distance between the skeleton origin and the bottom side of the AABB.</param>
|
||||
/// <param name="width">The width of the AABB</param>
|
||||
/// <param name="height">The height of the AABB.</param>
|
||||
/// <param name="vertexBuffer">Reference to hold a float[]. May be a null reference. This method will assign it a new float[] with the appropriate size as needed.</param>
|
||||
public void GetBounds (out float x, out float y, out float width, out float height, ref float[] vertexBuffer,
|
||||
SkeletonClipping clipper = null) {
|
||||
|
||||
float[] temp = vertexBuffer;
|
||||
temp = temp ?? new float[8];
|
||||
ExposedList<Slot> drawOrder = this.drawOrder.appliedPose;
|
||||
Slot[] slots = drawOrder.Items;
|
||||
float minX = int.MaxValue, minY = int.MaxValue, maxX = int.MinValue, maxY = int.MinValue;
|
||||
for (int i = 0, n = drawOrder.Count; i < n; i++) {
|
||||
Slot slot = slots[i];
|
||||
if (!slot.bone.active) continue;
|
||||
int verticesLength = 0;
|
||||
float[] vertices = null;
|
||||
int[] triangles = null;
|
||||
Attachment attachment = slot.appliedPose.attachment;
|
||||
RegionAttachment region = attachment as RegionAttachment;
|
||||
if (region != null) {
|
||||
verticesLength = 8;
|
||||
vertices = temp;
|
||||
if (vertices.Length < 8) vertices = temp = new float[8];
|
||||
region.ComputeWorldVertices(slot, region.GetOffsets(slot.appliedPose), vertices, 0, 2);
|
||||
triangles = quadTriangles;
|
||||
} else {
|
||||
MeshAttachment mesh = attachment as MeshAttachment;
|
||||
if (mesh != null) {
|
||||
verticesLength = mesh.WorldVerticesLength;
|
||||
vertices = temp;
|
||||
if (vertices.Length < verticesLength) vertices = temp = new float[verticesLength];
|
||||
mesh.ComputeWorldVertices(this, slot, 0, verticesLength, temp, 0, 2);
|
||||
triangles = mesh.Triangles;
|
||||
} else if (clipper != null) {
|
||||
ClippingAttachment clip = attachment as ClippingAttachment;
|
||||
if (clip != null) {
|
||||
clipper.ClipEnd(slot);
|
||||
clipper.ClipStart(this, slot, clip);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (vertices != null) {
|
||||
if (clipper != null && clipper.IsClipping && clipper.ClipTriangles(vertices, triangles, triangles.Length)) {
|
||||
vertices = clipper.ClippedVertices.Items;
|
||||
verticesLength = clipper.ClippedVertices.Count;
|
||||
}
|
||||
|
||||
for (int ii = 0; ii < verticesLength; ii += 2) {
|
||||
float vx = vertices[ii], vy = vertices[ii + 1];
|
||||
minX = Math.Min(minX, vx);
|
||||
minY = Math.Min(minY, vy);
|
||||
maxX = Math.Max(maxX, vx);
|
||||
maxY = Math.Max(maxY, vy);
|
||||
}
|
||||
}
|
||||
if (clipper != null) clipper.ClipEnd(slot);
|
||||
}
|
||||
if (clipper != null) clipper.ClipEnd();
|
||||
x = minX;
|
||||
y = minY;
|
||||
width = maxX - minX;
|
||||
height = maxY - minY;
|
||||
vertexBuffer = temp;
|
||||
}
|
||||
|
||||
/// <returns>A copy of the color to tint all the skeleton's attachments.</returns>
|
||||
public Color32F GetColor () {
|
||||
return color;
|
||||
}
|
||||
|
||||
/// <summary>Sets the color to tint all the skeleton's attachments.</summary>
|
||||
public void SetColor (Color32F color) {
|
||||
this.color = color;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A convenience method for setting the skeleton color. The color can also be set by
|
||||
/// <see cref="SetColor(Color32F)"/>
|
||||
/// </summary>
|
||||
public void SetColor (float r, float g, float b, float a) {
|
||||
color = new Color32F(r, g, b, a);
|
||||
}
|
||||
|
||||
/// <summary><para> Scales the entire skeleton on the X axis.</para>
|
||||
/// <para>
|
||||
/// Bones that do not inherit scale are still affected by this property.</para></summary>
|
||||
public float ScaleX { get { return scaleX; } set { scaleX = value; } }
|
||||
/// <summary><para> Scales the entire skeleton on the Y axis.</para>
|
||||
/// <para>
|
||||
/// Bones that do not inherit scale are still affected by this property.</para></summary>
|
||||
public float ScaleY { get { return scaleY * (Bone.yDown ? -1 : 1); } set { scaleY = value; } }
|
||||
|
||||
/// <summary>
|
||||
/// Scales the entire skeleton on the X and Y axes.
|
||||
/// <para>
|
||||
/// Bones that do not inherit scale are still affected by this property.
|
||||
/// </para></summary>
|
||||
public void SetScale (float scaleX, float scaleY) {
|
||||
this.scaleX = scaleX;
|
||||
this.scaleY = scaleY;
|
||||
}
|
||||
|
||||
/// <summary><para>The skeleton X position, which is added to the root bone worldX position.</para>
|
||||
/// <para>
|
||||
/// Bones that do not inherit translation are still affected by this property.</para></summary>
|
||||
public float X { get { return x; } set { x = value; } }
|
||||
/// <summary><para>The skeleton Y position, which is added to the root bone worldY position.</para>
|
||||
/// <para>
|
||||
/// Bones that do not inherit translation are still affected by this property.</para></summary>
|
||||
public float Y { get { return y; } set { y = value; } }
|
||||
|
||||
/// <summary>
|
||||
/// Sets the skeleton X and Y position, which is added to the root bone worldX and worldY position.
|
||||
/// <para>
|
||||
/// Bones that do not inherit translation are still affected by this property.</para></summary>
|
||||
public void SetPosition (float x, float y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
/// <summary>The x component of a vector that defines the direction <see cref="PhysicsConstraintPose.Wind"/> is applied.</summary>
|
||||
public float WindX { get { return windX; } set { windX = value; } }
|
||||
/// <summary>The y component of a vector that defines the direction <see cref="PhysicsConstraintPose.Wind"/> is applied.</summary>
|
||||
public float WindY { get { return windY; } set { windY = value; } }
|
||||
/// <summary>The x component of a vector that defines the direction <see cref="PhysicsConstraintPose.Gravity"/> is applied.</summary>
|
||||
public float GravityX { get { return gravityX; } set { gravityX = value; } }
|
||||
/// <summary>The y component of a vector that defines the direction <see cref="PhysicsConstraintPose.Gravity"/> is applied.</summary>
|
||||
public float GravityY { get { return gravityY; } set { gravityY = value; } }
|
||||
|
||||
/// <summary>
|
||||
/// Calls <see cref="PhysicsConstraint.Translate(float, float)"/> for each physics constraint.
|
||||
/// </summary>
|
||||
public void PhysicsTranslate (float x, float y) {
|
||||
PhysicsConstraint[] physicsConstraints = this.physics.Items;
|
||||
for (int i = 0, n = this.physics.Count; i < n; i++)
|
||||
physicsConstraints[i].Translate(x, y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calls <see cref="PhysicsConstraint.Rotate(float, float, float)"/> for each physics constraint.
|
||||
/// </summary>
|
||||
public void PhysicsRotate (float x, float y, float degrees) {
|
||||
PhysicsConstraint[] physicsConstraints = this.physics.Items;
|
||||
for (int i = 0, n = this.physics.Count; i < n; i++)
|
||||
physicsConstraints[i].Rotate(x, y, degrees);
|
||||
}
|
||||
|
||||
/// <summary>Returns the skeleton's time, used for time-based manipulations, such as <see cref="PhysicsConstraint"/>.</summary>
|
||||
/// <seealso cref="Update(float)"/>
|
||||
public float Time { get { return time; } set { time = value; } }
|
||||
|
||||
/// <summary>Increments the skeleton's <see cref="time"/>.</summary>
|
||||
public void Update (float delta) {
|
||||
time += delta;
|
||||
}
|
||||
|
||||
override public string ToString () {
|
||||
return data.name;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 12ac3c1c7546be24fb9625d3c850619d
|
||||
timeCreated: 1456265153
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user