Initial commit: Client Doc docs Server Tools
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a60dd41ef13d98647b9f963089feb7b0
|
||||
folderAsset: yes
|
||||
timeCreated: 1456265151
|
||||
licenseType: Free
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
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:
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user