This commit is contained in:
JA
2026-06-27 03:37:16 +08:00
parent 7a8d4a5d83
commit 3059e71422
465 changed files with 2675 additions and 58818 deletions

View File

@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 01cbef8f24d105f4bafa9668d669e040
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View File

@@ -1,139 +0,0 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
using UnityEditor;
using UnityEngine;
using Spine;
namespace Spine.Unity.Editor {
[CustomEditor(typeof(SkeletonAnimation))]
[CanEditMultipleObjects]
public class SkeletonAnimationInspector : SkeletonRendererInspector {
protected SerializedProperty animationName, loop, timeScale, autoReset;
protected bool wasAnimationParameterChanged = false;
protected bool requireRepaint;
readonly GUIContent LoopLabel = new GUIContent("Loop", "Whether or not .AnimationName should loop. This only applies to the initial animation specified in the inspector, or any subsequent Animations played through .AnimationName. Animations set through state.SetAnimation are unaffected.");
readonly GUIContent TimeScaleLabel = new GUIContent("Time Scale", "The rate at which animations progress over time. 1 means normal speed. 0.5 means 50% speed.");
protected override void OnEnable () {
base.OnEnable();
animationName = serializedObject.FindProperty("_animationName");
loop = serializedObject.FindProperty("loop");
timeScale = serializedObject.FindProperty("timeScale");
}
protected override void DrawInspectorGUI (bool multi) {
base.DrawInspectorGUI(multi);
if (!TargetIsValid) return;
bool sameData = SpineInspectorUtility.TargetsUseSameData(serializedObject);
foreach (var o in targets)
TrySetAnimation(o as SkeletonAnimation);
EditorGUILayout.Space();
if (!sameData) {
EditorGUILayout.DelayedTextField(animationName);
} else {
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(animationName);
wasAnimationParameterChanged |= EditorGUI.EndChangeCheck(); // Value used in the next update.
}
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(loop, LoopLabel);
wasAnimationParameterChanged |= EditorGUI.EndChangeCheck(); // Value used in the next update.
EditorGUILayout.PropertyField(timeScale, TimeScaleLabel);
foreach (var o in targets) {
var component = o as SkeletonAnimation;
component.timeScale = Mathf.Max(component.timeScale, 0);
}
EditorGUILayout.Space();
SkeletonRootMotionParameter();
serializedObject.ApplyModifiedProperties();
if (!isInspectingPrefab) {
if (requireRepaint) {
UnityEditorInternal.InternalEditorUtility.RepaintAllViews();
requireRepaint = false;
}
}
}
protected void TrySetAnimation (SkeletonAnimation skeletonAnimation) {
if (skeletonAnimation == null) return;
if (!skeletonAnimation.valid || skeletonAnimation.AnimationState == null)
return;
TrackEntry current = skeletonAnimation.AnimationState.GetCurrent(0);
if (!isInspectingPrefab) {
string activeAnimation = (current != null) ? current.Animation.Name : "";
bool activeLoop = (current != null) ? current.Loop : false;
bool animationParameterChanged = this.wasAnimationParameterChanged &&
((activeAnimation != animationName.stringValue) || (activeLoop != loop.boolValue));
if (animationParameterChanged) {
this.wasAnimationParameterChanged = false;
var skeleton = skeletonAnimation.Skeleton;
var state = skeletonAnimation.AnimationState;
if (!Application.isPlaying) {
if (state != null) state.ClearTrack(0);
skeleton.SetToSetupPose();
}
Spine.Animation animationToUse = skeleton.Data.FindAnimation(animationName.stringValue);
if (!Application.isPlaying) {
if (animationToUse != null) {
skeletonAnimation.AnimationState.SetAnimation(0, animationToUse, loop.boolValue);
}
skeletonAnimation.Update(0);
skeletonAnimation.LateUpdate();
requireRepaint = true;
} else {
if (animationToUse != null)
state.SetAnimation(0, animationToUse, loop.boolValue);
else
state.ClearTrack(0);
}
}
// Reflect animationName serialized property in the inspector even if SetAnimation API was used.
if (Application.isPlaying) {
if (current != null && current.Animation != null) {
if (skeletonAnimation.AnimationName != animationName.stringValue)
animationName.stringValue = current.Animation.Name;
}
}
}
}
}
}

View File

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

View File

@@ -1,159 +0,0 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEditor;
using UnityEngine;
using Spine.Unity.Examples;
namespace Spine.Unity.Editor {
// This script is not intended for use with code. See spine-unity documentation page for additional information.
[CustomEditor(typeof(SkeletonGraphicCustomMaterials))]
public class SkeletonGraphicCustomMaterialsInspector : UnityEditor.Editor {
List<SkeletonGraphicCustomMaterials.AtlasMaterialOverride> componentCustomMaterialOverrides, _customMaterialOverridesPrev;
List<SkeletonGraphicCustomMaterials.AtlasTextureOverride> componentCustomTextureOverrides, _customTextureOverridesPrev;
SkeletonGraphicCustomMaterials component;
const BindingFlags PrivateInstance = BindingFlags.Instance | BindingFlags.NonPublic;
MethodInfo RemoveCustomMaterialOverrides, RemoveCustomTextureOverrides, SetCustomMaterialOverrides, SetCustomTextureOverrides;
#region SkeletonGraphic context menu
[MenuItem("CONTEXT/SkeletonGraphic/Add Basic Serialized Custom Materials")]
static void AddSkeletonGraphicCustomMaterials (MenuCommand menuCommand) {
var skeletonGraphic = (SkeletonGraphic)menuCommand.context;
var newComponent = skeletonGraphic.gameObject.AddComponent<SkeletonGraphicCustomMaterials>();
Undo.RegisterCreatedObjectUndo(newComponent, "Add Basic Serialized Custom Materials");
}
[MenuItem("CONTEXT/SkeletonGraphic/Add Basic Serialized Custom Materials", true)]
static bool AddSkeletonGraphicCustomMaterials_Validate (MenuCommand menuCommand) {
var skeletonGraphic = (SkeletonGraphic)menuCommand.context;
return (skeletonGraphic.GetComponent<SkeletonGraphicCustomMaterials>() == null);
}
#endregion
void OnEnable () {
Type cm = typeof(SkeletonGraphicCustomMaterials);
RemoveCustomMaterialOverrides = cm.GetMethod("RemoveCustomMaterialOverrides", PrivateInstance);
RemoveCustomTextureOverrides = cm.GetMethod("RemoveCustomTextureOverrides", PrivateInstance);
SetCustomMaterialOverrides = cm.GetMethod("SetCustomMaterialOverrides", PrivateInstance);
SetCustomTextureOverrides = cm.GetMethod("SetCustomTextureOverrides", PrivateInstance);
}
public override void OnInspectorGUI () {
component = (SkeletonGraphicCustomMaterials)target;
var skeletonGraphic = component.skeletonGraphic;
// Draw the default inspector
DrawDefaultInspector();
if (serializedObject.isEditingMultipleObjects)
return;
if (componentCustomMaterialOverrides == null) {
Type cm = typeof(SkeletonGraphicCustomMaterials);
componentCustomMaterialOverrides = cm.GetField("customMaterialOverrides", PrivateInstance).GetValue(component) as List<SkeletonGraphicCustomMaterials.AtlasMaterialOverride>;
componentCustomTextureOverrides = cm.GetField("customTextureOverrides", PrivateInstance).GetValue(component) as List<SkeletonGraphicCustomMaterials.AtlasTextureOverride>;
if (componentCustomMaterialOverrides == null) {
Debug.Log("Reflection failed.");
return;
}
}
// Fill with current values at start
if (_customMaterialOverridesPrev == null || _customTextureOverridesPrev == null) {
_customMaterialOverridesPrev = CopyList(componentCustomMaterialOverrides);
_customTextureOverridesPrev = CopyList(componentCustomTextureOverrides);
}
// Compare new values with saved. If change is detected:
// store new values, restore old values, remove overrides, restore new values, restore overrides.
// 1. Store new values
var customMaterialOverridesNew = CopyList(componentCustomMaterialOverrides);
var customTextureOverridesNew = CopyList(componentCustomTextureOverrides);
// Detect changes
if (!_customMaterialOverridesPrev.SequenceEqual(customMaterialOverridesNew) ||
!_customTextureOverridesPrev.SequenceEqual(customTextureOverridesNew)) {
// 2. Restore old values
componentCustomMaterialOverrides.Clear();
componentCustomTextureOverrides.Clear();
componentCustomMaterialOverrides.AddRange(_customMaterialOverridesPrev);
componentCustomTextureOverrides.AddRange(_customTextureOverridesPrev);
// 3. Remove overrides
RemoveCustomMaterials();
// 4. Restore new values
componentCustomMaterialOverrides.Clear();
componentCustomTextureOverrides.Clear();
componentCustomMaterialOverrides.AddRange(customMaterialOverridesNew);
componentCustomTextureOverrides.AddRange(customTextureOverridesNew);
// 5. Restore overrides
SetCustomMaterials();
if (skeletonGraphic != null)
skeletonGraphic.LateUpdate();
}
_customMaterialOverridesPrev = CopyList(componentCustomMaterialOverrides);
_customTextureOverridesPrev = CopyList(componentCustomTextureOverrides);
if (SpineInspectorUtility.LargeCenteredButton(SpineInspectorUtility.TempContent("Clear and Reapply Changes", tooltip: "Removes all non-serialized overrides in the SkeletonGraphic and reapplies the overrides on this component."))) {
if (skeletonGraphic != null) {
skeletonGraphic.CustomMaterialOverride.Clear();
skeletonGraphic.CustomTextureOverride.Clear();
RemoveCustomMaterials();
SetCustomMaterials();
skeletonGraphic.LateUpdate();
}
}
}
void RemoveCustomMaterials () {
RemoveCustomMaterialOverrides.Invoke(component, null);
RemoveCustomTextureOverrides.Invoke(component, null);
}
void SetCustomMaterials () {
SetCustomMaterialOverrides.Invoke(component, null);
SetCustomTextureOverrides.Invoke(component, null);
}
static List<T> CopyList<T> (List<T> list) {
return list.GetRange(0, list.Count);
}
}
}

View File

@@ -1,12 +0,0 @@
fileFormatVersion: 2
guid: 349bf125947e3aa4bb78690fec69ea17
timeCreated: 1588789940
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,436 +0,0 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#if UNITY_2018_3 || UNITY_2019 || UNITY_2018_3_OR_NEWER
#define NEW_PREFAB_SYSTEM
#endif
using UnityEngine;
using UnityEditor;
namespace Spine.Unity.Editor {
using Icons = SpineEditorUtilities.Icons;
[CustomEditor(typeof(SkeletonGraphic))]
[CanEditMultipleObjects]
public class SkeletonGraphicInspector : UnityEditor.Editor {
const string SeparatorSlotNamesFieldName = "separatorSlotNames";
const string ReloadButtonString = "Reload";
protected GUIContent SkeletonDataAssetLabel;
static GUILayoutOption reloadButtonWidth;
static GUILayoutOption ReloadButtonWidth { get { return reloadButtonWidth = reloadButtonWidth ?? GUILayout.Width(GUI.skin.label.CalcSize(new GUIContent(ReloadButtonString)).x + 20); } }
static GUIStyle ReloadButtonStyle { get { return EditorStyles.miniButton; } }
SerializedProperty material, color;
SerializedProperty skeletonDataAsset, initialSkinName;
SerializedProperty startingAnimation, startingLoop, timeScale, freeze, updateWhenInvisible, unscaledTime, tintBlack;
SerializedProperty initialFlipX, initialFlipY;
SerializedProperty meshGeneratorSettings;
SerializedProperty allowMultipleCanvasRenderers, separatorSlotNames, enableSeparatorSlots, updateSeparatorPartLocation;
SerializedProperty raycastTarget;
SkeletonGraphic thisSkeletonGraphic;
protected bool isInspectingPrefab;
protected bool slotsReapplyRequired = false;
protected bool forceReloadQueued = false;
protected bool TargetIsValid {
get {
if (serializedObject.isEditingMultipleObjects) {
foreach (var o in targets) {
var component = (SkeletonGraphic)o;
if (!component.IsValid)
return false;
}
return true;
}
else {
var component = (SkeletonGraphic)target;
return component.IsValid;
}
}
}
void OnEnable () {
#if NEW_PREFAB_SYSTEM
isInspectingPrefab = false;
#else
isInspectingPrefab = (PrefabUtility.GetPrefabType(target) == PrefabType.Prefab);
#endif
SpineEditorUtilities.ConfirmInitialization();
// Labels
SkeletonDataAssetLabel = new GUIContent("SkeletonData Asset", Icons.spine);
var so = this.serializedObject;
thisSkeletonGraphic = target as SkeletonGraphic;
// MaskableGraphic
material = so.FindProperty("m_Material");
color = so.FindProperty("m_Color");
raycastTarget = so.FindProperty("m_RaycastTarget");
// SkeletonRenderer
skeletonDataAsset = so.FindProperty("skeletonDataAsset");
initialSkinName = so.FindProperty("initialSkinName");
initialFlipX = so.FindProperty("initialFlipX");
initialFlipY = so.FindProperty("initialFlipY");
// SkeletonAnimation
startingAnimation = so.FindProperty("startingAnimation");
startingLoop = so.FindProperty("startingLoop");
timeScale = so.FindProperty("timeScale");
unscaledTime = so.FindProperty("unscaledTime");
freeze = so.FindProperty("freeze");
updateWhenInvisible = so.FindProperty("updateWhenInvisible");
meshGeneratorSettings = so.FindProperty("meshGenerator").FindPropertyRelative("settings");
meshGeneratorSettings.isExpanded = SkeletonRendererInspector.advancedFoldout;
allowMultipleCanvasRenderers = so.FindProperty("allowMultipleCanvasRenderers");
updateSeparatorPartLocation = so.FindProperty("updateSeparatorPartLocation");
enableSeparatorSlots = so.FindProperty("enableSeparatorSlots");
separatorSlotNames = so.FindProperty("separatorSlotNames");
separatorSlotNames.isExpanded = true;
}
public override void OnInspectorGUI () {
if (UnityEngine.Event.current.type == EventType.Layout) {
if (forceReloadQueued) {
forceReloadQueued = false;
foreach (var c in targets) {
SpineEditorUtilities.ReloadSkeletonDataAssetAndComponent(c as SkeletonGraphic);
}
}
else {
foreach (var c in targets) {
var component = c as SkeletonGraphic;
if (!component.IsValid) {
SpineEditorUtilities.ReinitializeComponent(component);
if (!component.IsValid) continue;
}
}
}
}
bool wasChanged = false;
EditorGUI.BeginChangeCheck();
using (new EditorGUILayout.HorizontalScope(EditorStyles.helpBox)) {
SpineInspectorUtility.PropertyFieldFitLabel(skeletonDataAsset, SkeletonDataAssetLabel);
if (GUILayout.Button(ReloadButtonString, ReloadButtonStyle, ReloadButtonWidth))
forceReloadQueued = true;
}
EditorGUILayout.PropertyField(material);
EditorGUILayout.PropertyField(color);
if (thisSkeletonGraphic.skeletonDataAsset == null) {
EditorGUILayout.HelpBox("You need to assign a SkeletonDataAsset first.", MessageType.Info);
serializedObject.ApplyModifiedProperties();
serializedObject.Update();
return;
}
string errorMessage = null;
if (SpineEditorUtilities.Preferences.componentMaterialWarning &&
MaterialChecks.IsMaterialSetupProblematic(thisSkeletonGraphic, ref errorMessage)) {
EditorGUILayout.HelpBox(errorMessage, MessageType.Error, true);
}
bool isSingleRendererOnly = (!allowMultipleCanvasRenderers.hasMultipleDifferentValues && allowMultipleCanvasRenderers.boolValue == false);
bool isSeparationEnabledButNotMultipleRenderers =
isSingleRendererOnly && (!enableSeparatorSlots.hasMultipleDifferentValues && enableSeparatorSlots.boolValue == true);
bool meshRendersIncorrectlyWithSingleRenderer =
isSingleRendererOnly && SkeletonHasMultipleSubmeshes();
if (isSeparationEnabledButNotMultipleRenderers || meshRendersIncorrectlyWithSingleRenderer)
meshGeneratorSettings.isExpanded = true;
using (new SpineInspectorUtility.BoxScope()) {
EditorGUILayout.PropertyField(meshGeneratorSettings, SpineInspectorUtility.TempContent("Advanced..."), includeChildren: true);
SkeletonRendererInspector.advancedFoldout = meshGeneratorSettings.isExpanded;
if (meshGeneratorSettings.isExpanded) {
EditorGUILayout.Space();
using (new SpineInspectorUtility.IndentScope()) {
EditorGUILayout.BeginHorizontal();
EditorGUILayout.PropertyField(allowMultipleCanvasRenderers, SpineInspectorUtility.TempContent("Multiple CanvasRenderers"));
if (GUILayout.Button(new GUIContent("Trim Renderers", "Remove currently unused CanvasRenderer GameObjects. These will be regenerated whenever needed."),
EditorStyles.miniButton, GUILayout.Width(100f))) {
foreach (var skeletonGraphic in targets) {
((SkeletonGraphic)skeletonGraphic).TrimRenderers();
}
}
EditorGUILayout.EndHorizontal();
EditorGUILayout.PropertyField(updateWhenInvisible);
// warning box
if (isSeparationEnabledButNotMultipleRenderers) {
using (new SpineInspectorUtility.BoxScope()) {
meshGeneratorSettings.isExpanded = true;
EditorGUILayout.LabelField(SpineInspectorUtility.TempContent("'Multiple Canvas Renderers' must be enabled\nwhen 'Enable Separation' is enabled.", Icons.warning), GUILayout.Height(42), GUILayout.Width(340));
}
}
else if (meshRendersIncorrectlyWithSingleRenderer) {
using (new SpineInspectorUtility.BoxScope()) {
meshGeneratorSettings.isExpanded = true;
EditorGUILayout.LabelField(SpineInspectorUtility.TempContent("This mesh uses multiple atlas pages. You\n" +
"need to enable 'Multiple Canvas Renderers'\n" +
"for correct rendering. Consider packing\n" +
"attachments to a single atlas page if possible.", Icons.warning), GUILayout.Height(60), GUILayout.Width(340));
}
}
}
EditorGUILayout.Space();
SeparatorsField(separatorSlotNames, enableSeparatorSlots, updateSeparatorPartLocation);
}
}
EditorGUILayout.Space();
EditorGUILayout.PropertyField(initialSkinName);
{
var rect = GUILayoutUtility.GetRect(EditorGUIUtility.currentViewWidth, EditorGUIUtility.singleLineHeight);
EditorGUI.PrefixLabel(rect, SpineInspectorUtility.TempContent("Initial Flip"));
rect.x += EditorGUIUtility.labelWidth;
rect.width = 30f;
SpineInspectorUtility.ToggleLeft(rect, initialFlipX, SpineInspectorUtility.TempContent("X", tooltip: "initialFlipX"));
rect.x += 35f;
SpineInspectorUtility.ToggleLeft(rect, initialFlipY, SpineInspectorUtility.TempContent("Y", tooltip: "initialFlipY"));
}
EditorGUILayout.Space();
EditorGUILayout.LabelField("Animation", EditorStyles.boldLabel);
EditorGUILayout.PropertyField(startingAnimation);
EditorGUILayout.PropertyField(startingLoop);
EditorGUILayout.PropertyField(timeScale);
EditorGUILayout.PropertyField(unscaledTime, SpineInspectorUtility.TempContent(unscaledTime.displayName, tooltip: "If checked, this will use Time.unscaledDeltaTime to make this update independent of game Time.timeScale. Instance SkeletonGraphic.timeScale will still be applied."));
EditorGUILayout.Space();
EditorGUILayout.PropertyField(freeze);
EditorGUILayout.Space();
SkeletonRendererInspector.SkeletonRootMotionParameter(targets);
EditorGUILayout.Space();
EditorGUILayout.LabelField("UI", EditorStyles.boldLabel);
EditorGUILayout.PropertyField(raycastTarget);
EditorGUILayout.BeginHorizontal(GUILayout.Height(EditorGUIUtility.singleLineHeight + 5));
EditorGUILayout.PrefixLabel("Match RectTransform with Mesh");
if (GUILayout.Button("Match", EditorStyles.miniButton, GUILayout.Width(65f))) {
foreach (var skeletonGraphic in targets) {
MatchRectTransformWithBounds((SkeletonGraphic)skeletonGraphic);
}
}
EditorGUILayout.EndHorizontal();
if (TargetIsValid && !isInspectingPrefab) {
EditorGUILayout.Space();
if (SpineInspectorUtility.CenteredButton(new GUIContent("Add Skeleton Utility", Icons.skeletonUtility), 21, true, 200f))
foreach (var t in targets) {
var component = t as Component;
if (component.GetComponent<SkeletonUtility>() == null) {
component.gameObject.AddComponent<SkeletonUtility>();
}
}
}
wasChanged |= EditorGUI.EndChangeCheck();
if (wasChanged) {
serializedObject.ApplyModifiedProperties();
slotsReapplyRequired = true;
}
if (slotsReapplyRequired && UnityEngine.Event.current.type == EventType.Repaint) {
foreach (var target in targets) {
var skeletonGraphic = (SkeletonGraphic)target;
skeletonGraphic.ReapplySeparatorSlotNames();
skeletonGraphic.LateUpdate();
SceneView.RepaintAll();
}
slotsReapplyRequired = false;
}
}
protected bool SkeletonHasMultipleSubmeshes () {
foreach (var target in targets) {
var skeletonGraphic = (SkeletonGraphic)target;
if (skeletonGraphic.HasMultipleSubmeshInstructions())
return true;
}
return false;
}
public static void SetSeparatorSlotNames (SkeletonRenderer skeletonRenderer, string[] newSlotNames) {
var field = SpineInspectorUtility.GetNonPublicField(typeof(SkeletonRenderer), SeparatorSlotNamesFieldName);
field.SetValue(skeletonRenderer, newSlotNames);
}
public static string[] GetSeparatorSlotNames (SkeletonRenderer skeletonRenderer) {
var field = SpineInspectorUtility.GetNonPublicField(typeof(SkeletonRenderer), SeparatorSlotNamesFieldName);
return field.GetValue(skeletonRenderer) as string[];
}
public static void SeparatorsField (SerializedProperty separatorSlotNames, SerializedProperty enableSeparatorSlots,
SerializedProperty updateSeparatorPartLocation) {
bool multi = separatorSlotNames.serializedObject.isEditingMultipleObjects;
bool hasTerminalSlot = false;
if (!multi) {
var sr = separatorSlotNames.serializedObject.targetObject as ISkeletonComponent;
var skeleton = sr.Skeleton;
int lastSlot = skeleton.Slots.Count - 1;
if (skeleton != null) {
for (int i = 0, n = separatorSlotNames.arraySize; i < n; i++) {
int index = skeleton.FindSlotIndex(separatorSlotNames.GetArrayElementAtIndex(i).stringValue);
if (index == 0 || index == lastSlot) {
hasTerminalSlot = true;
break;
}
}
}
}
string terminalSlotWarning = hasTerminalSlot ? " (!)" : "";
using (new EditorGUILayout.VerticalScope(EditorStyles.helpBox)) {
const string SeparatorsDescription = "Stored names of slots where the Skeleton's render will be split into different batches. This is used by separate components that split the render into different MeshRenderers or GameObjects.";
if (separatorSlotNames.isExpanded) {
EditorGUILayout.PropertyField(separatorSlotNames, SpineInspectorUtility.TempContent(separatorSlotNames.displayName + terminalSlotWarning, Icons.slotRoot, SeparatorsDescription), true);
GUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
if (GUILayout.Button("+", GUILayout.MaxWidth(28f), GUILayout.MaxHeight(15f))) {
separatorSlotNames.arraySize++;
}
GUILayout.EndHorizontal();
}
else
EditorGUILayout.PropertyField(separatorSlotNames, new GUIContent(separatorSlotNames.displayName + string.Format("{0} [{1}]", terminalSlotWarning, separatorSlotNames.arraySize), SeparatorsDescription), true);
EditorGUILayout.PropertyField(enableSeparatorSlots, SpineInspectorUtility.TempContent("Enable Separation", tooltip: "Whether to enable separation at the above separator slots."));
EditorGUILayout.PropertyField(updateSeparatorPartLocation, SpineInspectorUtility.TempContent("Update Part Location", tooltip:"Update separator part GameObject location to match the position of the SkeletonGraphic. This can be helpful when re-parenting parts to a different GameObject."));
}
}
#region Menus
[MenuItem("CONTEXT/SkeletonGraphic/Match RectTransform with Mesh Bounds")]
static void MatchRectTransformWithBounds (MenuCommand command) {
var skeletonGraphic = (SkeletonGraphic)command.context;
MatchRectTransformWithBounds(skeletonGraphic);
}
static void MatchRectTransformWithBounds (SkeletonGraphic skeletonGraphic) {
if (!skeletonGraphic.MatchRectTransformWithBounds())
Debug.Log("Mesh was not previously generated.");
}
[MenuItem("GameObject/Spine/SkeletonGraphic (UnityUI)", false, 15)]
static public void SkeletonGraphicCreateMenuItem () {
var parentGameObject = Selection.activeObject as GameObject;
var parentTransform = parentGameObject == null ? null : parentGameObject.GetComponent<RectTransform>();
if (parentTransform == null)
Debug.LogWarning("Your new SkeletonGraphic will not be visible until it is placed under a Canvas");
var gameObject = NewSkeletonGraphicGameObject("New SkeletonGraphic");
gameObject.transform.SetParent(parentTransform, false);
EditorUtility.FocusProjectWindow();
Selection.activeObject = gameObject;
EditorGUIUtility.PingObject(Selection.activeObject);
}
// SpineEditorUtilities.InstantiateDelegate. Used by drag and drop.
public static Component SpawnSkeletonGraphicFromDrop (SkeletonDataAsset data) {
return InstantiateSkeletonGraphic(data);
}
public static SkeletonGraphic InstantiateSkeletonGraphic (SkeletonDataAsset skeletonDataAsset, string skinName) {
return InstantiateSkeletonGraphic(skeletonDataAsset, skeletonDataAsset.GetSkeletonData(true).FindSkin(skinName));
}
public static SkeletonGraphic InstantiateSkeletonGraphic (SkeletonDataAsset skeletonDataAsset, Skin skin = null) {
string spineGameObjectName = string.Format("SkeletonGraphic ({0})", skeletonDataAsset.name.Replace("_SkeletonData", ""));
var go = NewSkeletonGraphicGameObject(spineGameObjectName);
var graphic = go.GetComponent<SkeletonGraphic>();
graphic.skeletonDataAsset = skeletonDataAsset;
SkeletonData data = skeletonDataAsset.GetSkeletonData(true);
if (data == null) {
for (int i = 0; i < skeletonDataAsset.atlasAssets.Length; i++) {
string reloadAtlasPath = AssetDatabase.GetAssetPath(skeletonDataAsset.atlasAssets[i]);
skeletonDataAsset.atlasAssets[i] = (AtlasAssetBase)AssetDatabase.LoadAssetAtPath(reloadAtlasPath, typeof(AtlasAssetBase));
}
data = skeletonDataAsset.GetSkeletonData(true);
}
skin = skin ?? data.DefaultSkin ?? data.Skins.Items[0];
graphic.MeshGenerator.settings.zSpacing = SpineEditorUtilities.Preferences.defaultZSpacing;
graphic.startingLoop = SpineEditorUtilities.Preferences.defaultInstantiateLoop;
graphic.Initialize(false);
if (skin != null) graphic.Skeleton.SetSkin(skin);
graphic.initialSkinName = skin.Name;
graphic.Skeleton.UpdateWorldTransform();
graphic.UpdateMesh();
return graphic;
}
static GameObject NewSkeletonGraphicGameObject (string gameObjectName) {
var go = EditorInstantiation.NewGameObject(gameObjectName, true, typeof(RectTransform), typeof(CanvasRenderer), typeof(SkeletonGraphic));
var graphic = go.GetComponent<SkeletonGraphic>();
graphic.material = SkeletonGraphicInspector.DefaultSkeletonGraphicMaterial;
return go;
}
public static Material DefaultSkeletonGraphicMaterial {
get {
var guids = AssetDatabase.FindAssets("SkeletonGraphicDefault t:material");
if (guids.Length <= 0) return null;
var firstAssetPath = AssetDatabase.GUIDToAssetPath(guids[0]);
if (string.IsNullOrEmpty(firstAssetPath)) return null;
var firstMaterial = AssetDatabase.LoadAssetAtPath<Material>(firstAssetPath);
return firstMaterial;
}
}
#endregion
}
}

View File

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

View File

@@ -1,153 +0,0 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
// Contributed by: Mitch Thompson
using UnityEditor;
using UnityEngine;
namespace Spine.Unity.Editor {
[CustomEditor(typeof(SkeletonMecanim))]
[CanEditMultipleObjects]
public class SkeletonMecanimInspector : SkeletonRendererInspector {
public static bool mecanimSettingsFoldout;
protected SerializedProperty autoReset;
protected SerializedProperty useCustomMixMode;
protected SerializedProperty layerMixModes;
protected SerializedProperty layerBlendModes;
protected override void OnEnable () {
base.OnEnable();
SerializedProperty mecanimTranslator = serializedObject.FindProperty("translator");
autoReset = mecanimTranslator.FindPropertyRelative("autoReset");
useCustomMixMode = mecanimTranslator.FindPropertyRelative("useCustomMixMode");
layerMixModes = mecanimTranslator.FindPropertyRelative("layerMixModes");
layerBlendModes = mecanimTranslator.FindPropertyRelative("layerBlendModes");
}
protected override void DrawInspectorGUI (bool multi) {
AddRootMotionComponentIfEnabled();
base.DrawInspectorGUI(multi);
using (new SpineInspectorUtility.BoxScope()) {
mecanimSettingsFoldout = EditorGUILayout.Foldout(mecanimSettingsFoldout, "Mecanim Translator");
if (mecanimSettingsFoldout) {
EditorGUILayout.PropertyField(autoReset, new GUIContent("Auto Reset",
"When set to true, the skeleton state is mixed out to setup-" +
"pose when an animation finishes, according to the " +
"animation's keyed items."));
EditorGUILayout.PropertyField(useCustomMixMode, new GUIContent("Custom MixMode",
"When disabled, the recommended MixMode is used according to the layer blend mode. Enable to specify a custom MixMode for each Mecanim layer."));
if (useCustomMixMode.hasMultipleDifferentValues || useCustomMixMode.boolValue == true) {
DrawLayerSettings();
EditorGUILayout.Space();
}
}
}
}
protected void AddRootMotionComponentIfEnabled () {
foreach (var t in targets) {
var component = t as Component;
var animator = component.GetComponent<Animator>();
if (animator != null && animator.applyRootMotion) {
if (component.GetComponent<SkeletonMecanimRootMotion>() == null) {
component.gameObject.AddComponent<SkeletonMecanimRootMotion>();
}
}
}
}
protected void DrawLayerSettings () {
string[] layerNames = GetLayerNames();
float widthLayerColumn = 140;
float widthMixColumn = 84;
using (new GUILayout.HorizontalScope()) {
var rect = GUILayoutUtility.GetRect(EditorGUIUtility.currentViewWidth, EditorGUIUtility.singleLineHeight);
rect.width = widthLayerColumn;
EditorGUI.LabelField(rect, SpineInspectorUtility.TempContent("Mecanim Layer"), EditorStyles.boldLabel);
var savedIndent = EditorGUI.indentLevel;
EditorGUI.indentLevel = 0;
rect.position += new Vector2(rect.width, 0);
rect.width = widthMixColumn;
EditorGUI.LabelField(rect, SpineInspectorUtility.TempContent("Mix Mode"), EditorStyles.boldLabel);
EditorGUI.indentLevel = savedIndent;
}
using (new SpineInspectorUtility.IndentScope()) {
int layerCount = layerMixModes.arraySize;
for (int i = 0; i < layerCount; ++i) {
using (new GUILayout.HorizontalScope()) {
string layerName = i < layerNames.Length ? layerNames[i] : ("Layer " + i);
var rect = GUILayoutUtility.GetRect(EditorGUIUtility.currentViewWidth, EditorGUIUtility.singleLineHeight);
rect.width = widthLayerColumn;
EditorGUI.PrefixLabel(rect, SpineInspectorUtility.TempContent(layerName));
var savedIndent = EditorGUI.indentLevel;
EditorGUI.indentLevel = 0;
var mixMode = layerMixModes.GetArrayElementAtIndex(i);
rect.position += new Vector2(rect.width, 0);
rect.width = widthMixColumn;
EditorGUI.PropertyField(rect, mixMode, GUIContent.none);
EditorGUI.indentLevel = savedIndent;
}
}
}
}
protected string[] GetLayerNames () {
int maxLayerCount = 0;
int maxIndex = 0;
for (int i = 0; i < targets.Length; ++i) {
var skeletonMecanim = ((SkeletonMecanim)targets[i]);
int count = skeletonMecanim.Translator.MecanimLayerCount;
if (count > maxLayerCount) {
maxLayerCount = count;
maxIndex = i;
}
}
if (maxLayerCount == 0)
return new string[0];
var skeletonMecanimMaxLayers = ((SkeletonMecanim)targets[maxIndex]);
return skeletonMecanimMaxLayers.Translator.MecanimLayerNames;
}
}
}

View File

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

View File

@@ -1,81 +0,0 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
using UnityEditor;
using UnityEngine;
namespace Spine.Unity.Editor {
[CustomEditor(typeof(SkeletonMecanimRootMotion))]
[CanEditMultipleObjects]
public class SkeletonMecanimRootMotionInspector : SkeletonRootMotionBaseInspector {
protected SerializedProperty mecanimLayerFlags;
protected GUIContent mecanimLayersLabel;
protected override void OnEnable () {
base.OnEnable();
mecanimLayerFlags = serializedObject.FindProperty("mecanimLayerFlags");
mecanimLayersLabel = new UnityEngine.GUIContent("Mecanim Layers", "Mecanim layers to apply root motion at. Defaults to the first Mecanim layer.");
}
override public void OnInspectorGUI () {
base.MainPropertyFields();
MecanimLayerMaskPropertyField();
base.OptionalPropertyFields();
serializedObject.ApplyModifiedProperties();
}
protected string[] GetLayerNames () {
int maxLayerCount = 0;
int maxIndex = 0;
for (int i = 0; i < targets.Length; ++i) {
var skeletonMecanim = ((SkeletonMecanimRootMotion)targets[i]).SkeletonMecanim;
int count = skeletonMecanim.Translator.MecanimLayerCount;
if (count > maxLayerCount) {
maxLayerCount = count;
maxIndex = i;
}
}
if (maxLayerCount == 0)
return new string[0];
var skeletonMecanimMaxLayers = ((SkeletonMecanimRootMotion)targets[maxIndex]).SkeletonMecanim;
return skeletonMecanimMaxLayers.Translator.MecanimLayerNames;
}
protected void MecanimLayerMaskPropertyField () {
string[] layerNames = GetLayerNames();
if (layerNames.Length > 0)
mecanimLayerFlags.intValue = EditorGUILayout.MaskField(
mecanimLayersLabel, mecanimLayerFlags.intValue, layerNames);
}
}
}

View File

@@ -1,12 +0,0 @@
fileFormatVersion: 2
guid: 4613924c50d66cf458f0db803776dd2f
timeCreated: 1593175106
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,165 +0,0 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#define SPINE_OPTIONAL_MATERIALOVERRIDE
// Contributed by: Lost Polygon
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEditor;
using UnityEngine;
using Spine.Unity.Examples;
namespace Spine.Unity.Editor {
// This script is not intended for use with code. See the readme.txt file in SkeletonRendererCustomMaterials folder to learn more.
[CustomEditor(typeof(SkeletonRendererCustomMaterials))]
public class SkeletonRendererCustomMaterialsInspector : UnityEditor.Editor {
List<SkeletonRendererCustomMaterials.AtlasMaterialOverride> componentCustomMaterialOverrides, _customMaterialOverridesPrev;
List<SkeletonRendererCustomMaterials.SlotMaterialOverride> componentCustomSlotMaterials, _customSlotMaterialsPrev;
SkeletonRendererCustomMaterials component;
const BindingFlags PrivateInstance = BindingFlags.Instance | BindingFlags.NonPublic;
MethodInfo RemoveCustomMaterialOverrides, RemoveCustomSlotMaterials, SetCustomMaterialOverrides, SetCustomSlotMaterials;
#region SkeletonRenderer context menu
[MenuItem("CONTEXT/SkeletonRenderer/Add Basic Serialized Custom Materials")]
static void AddSkeletonRendererCustomMaterials (MenuCommand menuCommand) {
var skeletonRenderer = (SkeletonRenderer)menuCommand.context;
var newComponent = skeletonRenderer.gameObject.AddComponent<SkeletonRendererCustomMaterials>();
Undo.RegisterCreatedObjectUndo(newComponent, "Add Basic Serialized Custom Materials");
}
[MenuItem("CONTEXT/SkeletonRenderer/Add Basic Serialized Custom Materials", true)]
static bool AddSkeletonRendererCustomMaterials_Validate (MenuCommand menuCommand) {
var skeletonRenderer = (SkeletonRenderer)menuCommand.context;
return (skeletonRenderer.GetComponent<SkeletonRendererCustomMaterials>() == null);
}
#endregion
void OnEnable () {
Type cm = typeof(SkeletonRendererCustomMaterials);
RemoveCustomMaterialOverrides = cm.GetMethod("RemoveCustomMaterialOverrides", PrivateInstance);
RemoveCustomSlotMaterials = cm.GetMethod("RemoveCustomSlotMaterials", PrivateInstance);
SetCustomMaterialOverrides = cm.GetMethod("SetCustomMaterialOverrides", PrivateInstance);
SetCustomSlotMaterials = cm.GetMethod("SetCustomSlotMaterials", PrivateInstance);
}
public override void OnInspectorGUI () {
component = (SkeletonRendererCustomMaterials)target;
var skeletonRenderer = component.skeletonRenderer;
// Draw the default inspector
DrawDefaultInspector();
if (serializedObject.isEditingMultipleObjects)
return;
if (componentCustomMaterialOverrides == null) {
Type cm = typeof(SkeletonRendererCustomMaterials);
componentCustomMaterialOverrides = cm.GetField("customMaterialOverrides", PrivateInstance).GetValue(component) as List<SkeletonRendererCustomMaterials.AtlasMaterialOverride>;
componentCustomSlotMaterials = cm.GetField("customSlotMaterials", PrivateInstance).GetValue(component) as List<SkeletonRendererCustomMaterials.SlotMaterialOverride>;
if (componentCustomMaterialOverrides == null) {
Debug.Log("Reflection failed.");
return;
}
}
// Fill with current values at start
if (_customMaterialOverridesPrev == null || _customSlotMaterialsPrev == null) {
_customMaterialOverridesPrev = CopyList(componentCustomMaterialOverrides);
_customSlotMaterialsPrev = CopyList(componentCustomSlotMaterials);
}
// Compare new values with saved. If change is detected:
// store new values, restore old values, remove overrides, restore new values, restore overrides.
// 1. Store new values
var customMaterialOverridesNew = CopyList(componentCustomMaterialOverrides);
var customSlotMaterialsNew = CopyList(componentCustomSlotMaterials);
// Detect changes
if (!_customMaterialOverridesPrev.SequenceEqual(customMaterialOverridesNew) ||
!_customSlotMaterialsPrev.SequenceEqual(customSlotMaterialsNew)) {
// 2. Restore old values
componentCustomMaterialOverrides.Clear();
componentCustomSlotMaterials.Clear();
componentCustomMaterialOverrides.AddRange(_customMaterialOverridesPrev);
componentCustomSlotMaterials.AddRange(_customSlotMaterialsPrev);
// 3. Remove overrides
RemoveCustomMaterials();
// 4. Restore new values
componentCustomMaterialOverrides.Clear();
componentCustomSlotMaterials.Clear();
componentCustomMaterialOverrides.AddRange(customMaterialOverridesNew);
componentCustomSlotMaterials.AddRange(customSlotMaterialsNew);
// 5. Restore overrides
SetCustomMaterials();
if (skeletonRenderer != null)
skeletonRenderer.LateUpdate();
}
_customMaterialOverridesPrev = CopyList(componentCustomMaterialOverrides);
_customSlotMaterialsPrev = CopyList(componentCustomSlotMaterials);
if (SpineInspectorUtility.LargeCenteredButton(SpineInspectorUtility.TempContent("Clear and Reapply Changes", tooltip: "Removes all non-serialized overrides in the SkeletonRenderer and reapplies the overrides on this component."))) {
if (skeletonRenderer != null) {
#if SPINE_OPTIONAL_MATERIALOVERRIDE
skeletonRenderer.CustomMaterialOverride.Clear();
#endif
skeletonRenderer.CustomSlotMaterials.Clear();
RemoveCustomMaterials();
SetCustomMaterials();
skeletonRenderer.LateUpdate();
}
}
}
void RemoveCustomMaterials () {
RemoveCustomMaterialOverrides.Invoke(component, null);
RemoveCustomSlotMaterials.Invoke(component, null);
}
void SetCustomMaterials () {
SetCustomMaterialOverrides.Invoke(component, null);
SetCustomSlotMaterials.Invoke(component, null);
}
static List<T> CopyList<T> (List<T> list) {
return list.GetRange(0, list.Count);
}
}
}

View File

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

View File

@@ -1,593 +0,0 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#if UNITY_2018_3 || UNITY_2019 || UNITY_2018_3_OR_NEWER
#define NEW_PREFAB_SYSTEM
#else
#define NO_PREFAB_MESH
#endif
#if UNITY_2018_1_OR_NEWER
#define PER_MATERIAL_PROPERTY_BLOCKS
#endif
#if UNITY_2017_1_OR_NEWER
#define BUILT_IN_SPRITE_MASK_COMPONENT
#endif
using UnityEditor;
using System.Collections.Generic;
using UnityEngine;
using System.Reflection;
namespace Spine.Unity.Editor {
using Event = UnityEngine.Event;
using Icons = SpineEditorUtilities.Icons;
[CustomEditor(typeof(SkeletonRenderer))]
[CanEditMultipleObjects]
public class SkeletonRendererInspector : UnityEditor.Editor {
public static bool advancedFoldout;
const string SeparatorSlotNamesFieldName = "separatorSlotNames";
protected SerializedProperty skeletonDataAsset, initialSkinName;
protected SerializedProperty initialFlipX, initialFlipY;
protected SerializedProperty updateWhenInvisible, singleSubmesh, separatorSlotNames, clearStateOnDisable, immutableTriangles, fixDrawOrder;
protected SerializedProperty normals, tangents, zSpacing, pmaVertexColors, tintBlack; // MeshGenerator settings
protected SerializedProperty maskInteraction;
protected SerializedProperty maskMaterialsNone, maskMaterialsInside, maskMaterialsOutside;
protected SpineInspectorUtility.SerializedSortingProperties sortingProperties;
protected bool isInspectingPrefab;
protected bool forceReloadQueued = false;
protected bool setMaskNoneMaterialsQueued = false;
protected bool setInsideMaskMaterialsQueued = false;
protected bool setOutsideMaskMaterialsQueued = false;
protected bool deleteInsideMaskMaterialsQueued = false;
protected bool deleteOutsideMaskMaterialsQueued = false;
protected GUIContent SkeletonDataAssetLabel, SkeletonUtilityButtonContent;
protected GUIContent PMAVertexColorsLabel, ClearStateOnDisableLabel, ZSpacingLabel, ImmubleTrianglesLabel, TintBlackLabel, UpdateWhenInvisibleLabel, SingleSubmeshLabel, FixDrawOrderLabel;
protected GUIContent NormalsLabel, TangentsLabel, MaskInteractionLabel;
protected GUIContent MaskMaterialsHeadingLabel, MaskMaterialsNoneLabel, MaskMaterialsInsideLabel, MaskMaterialsOutsideLabel;
protected GUIContent SetMaterialButtonLabel, ClearMaterialButtonLabel, DeleteMaterialButtonLabel;
const string ReloadButtonString = "Reload";
static GUILayoutOption reloadButtonWidth;
static GUILayoutOption ReloadButtonWidth { get { return reloadButtonWidth = reloadButtonWidth ?? GUILayout.Width(GUI.skin.label.CalcSize(new GUIContent(ReloadButtonString)).x + 20); } }
static GUIStyle ReloadButtonStyle { get { return EditorStyles.miniButton; } }
protected bool TargetIsValid {
get {
if (serializedObject.isEditingMultipleObjects) {
foreach (var o in targets) {
var component = (SkeletonRenderer)o;
if (!component.valid)
return false;
}
return true;
} else {
var component = (SkeletonRenderer)target;
return component.valid;
}
}
}
protected virtual void OnEnable () {
#if NEW_PREFAB_SYSTEM
isInspectingPrefab = false;
#else
isInspectingPrefab = (PrefabUtility.GetPrefabType(target) == PrefabType.Prefab);
#endif
SpineEditorUtilities.ConfirmInitialization();
// Labels
SkeletonDataAssetLabel = new GUIContent("SkeletonData Asset", Icons.spine);
SkeletonUtilityButtonContent = new GUIContent("Add Skeleton Utility", Icons.skeletonUtility);
ImmubleTrianglesLabel = new GUIContent("Immutable Triangles", "Enable to optimize rendering for skeletons that never change attachment visbility");
PMAVertexColorsLabel = new GUIContent("PMA Vertex Colors", "Use this if you are using the default Spine/Skeleton shader or any premultiply-alpha shader.");
ClearStateOnDisableLabel = new GUIContent("Clear State On Disable", "Use this if you are pooling or enabling/disabling your Spine GameObject.");
ZSpacingLabel = new GUIContent("Z Spacing", "A value other than 0 adds a space between each rendered attachment to prevent Z Fighting when using shaders that read or write to the depth buffer. Large values may cause unwanted parallax and spaces depending on camera setup.");
NormalsLabel = new GUIContent("Add Normals", "Use this if your shader requires vertex normals. A more efficient solution for 2D setups is to modify the shader to assume a single normal value for the whole mesh.");
TangentsLabel = new GUIContent("Solve Tangents", "Calculates the tangents per frame. Use this if you are using lit shaders (usually with normal maps) that require vertex tangents.");
TintBlackLabel = new GUIContent("Tint Black (!)", "Adds black tint vertex data to the mesh as UV2 and UV3. Black tinting requires that the shader interpret UV2 and UV3 as black tint colors for this effect to work. You may also use the default [Spine/Skeleton Tint Black] shader.\n\nIf you only need to tint the whole skeleton and not individual parts, the [Spine/Skeleton Tint] shader is recommended for better efficiency and changing/animating the _Black material property via MaterialPropertyBlock.");
SingleSubmeshLabel = new GUIContent("Use Single Submesh", "Simplifies submesh generation by assuming you are only using one Material and need only one submesh. This is will disable multiple materials, render separation, and custom slot materials.");
UpdateWhenInvisibleLabel = new GUIContent("Update When Invisible", "Update mode used when the MeshRenderer becomes invisible. Update mode is automatically reset to UpdateMode.FullUpdate when the mesh becomes visible again.");
FixDrawOrderLabel = new GUIContent("Fix Draw Order", "Applies only when 3+ submeshes are used (2+ materials with alternating order, e.g. \"A B A\"). If true, GPU instancing will be disabled at all materials and MaterialPropertyBlocks are assigned at each material to prevent aggressive batching of submeshes by e.g. the LWRP renderer, leading to incorrect draw order (e.g. \"A1 B A2\" changed to \"A1A2 B\"). You can disable this parameter when everything is drawn correctly to save the additional performance cost. Note: the GPU instancing setting will remain disabled at affected material assets after exiting play mode, you have to enable it manually if you accidentally enabled this parameter.");
MaskInteractionLabel = new GUIContent("Mask Interaction", "SkeletonRenderer's interaction with a Sprite Mask.");
MaskMaterialsHeadingLabel = new GUIContent("Mask Interaction Materials", "Materials used for different interaction with sprite masks.");
MaskMaterialsNoneLabel = new GUIContent("Normal Materials", "Normal materials used when Mask Interaction is set to None.");
MaskMaterialsInsideLabel = new GUIContent("Inside Mask", "Materials used when Mask Interaction is set to Inside Mask.");
MaskMaterialsOutsideLabel = new GUIContent("Outside Mask", "Materials used when Mask Interaction is set to Outside Mask.");
SetMaterialButtonLabel = new GUIContent("Set", "Prepares material references for switching to the corresponding Mask Interaction mode at runtime. Creates the required materials if they do not exist.");
ClearMaterialButtonLabel = new GUIContent("Clear", "Clears unused material references. Note: when switching to the corresponding Mask Interaction mode at runtime, a new material is generated on the fly.");
DeleteMaterialButtonLabel = new GUIContent("Delete", "Clears unused material references and deletes the corresponding assets. Note: when switching to the corresponding Mask Interaction mode at runtime, a new material is generated on the fly.");
var so = this.serializedObject;
skeletonDataAsset = so.FindProperty("skeletonDataAsset");
initialSkinName = so.FindProperty("initialSkinName");
initialFlipX = so.FindProperty("initialFlipX");
initialFlipY = so.FindProperty("initialFlipY");
normals = so.FindProperty("addNormals");
tangents = so.FindProperty("calculateTangents");
immutableTriangles = so.FindProperty("immutableTriangles");
pmaVertexColors = so.FindProperty("pmaVertexColors");
clearStateOnDisable = so.FindProperty("clearStateOnDisable");
tintBlack = so.FindProperty("tintBlack");
updateWhenInvisible = so.FindProperty("updateWhenInvisible");
singleSubmesh = so.FindProperty("singleSubmesh");
fixDrawOrder = so.FindProperty("fixDrawOrder");
maskInteraction = so.FindProperty("maskInteraction");
maskMaterialsNone = so.FindProperty("maskMaterials.materialsMaskDisabled");
maskMaterialsInside = so.FindProperty("maskMaterials.materialsInsideMask");
maskMaterialsOutside = so.FindProperty("maskMaterials.materialsOutsideMask");
separatorSlotNames = so.FindProperty("separatorSlotNames");
separatorSlotNames.isExpanded = true;
zSpacing = so.FindProperty("zSpacing");
SerializedObject renderersSerializedObject = SpineInspectorUtility.GetRenderersSerializedObject(serializedObject); // Allows proper multi-edit behavior.
sortingProperties = new SpineInspectorUtility.SerializedSortingProperties(renderersSerializedObject);
}
public void OnSceneGUI () {
var skeletonRenderer = (SkeletonRenderer)target;
var skeleton = skeletonRenderer.Skeleton;
var transform = skeletonRenderer.transform;
if (skeleton == null) return;
SpineHandles.DrawBones(transform, skeleton);
}
override public void OnInspectorGUI () {
bool multi = serializedObject.isEditingMultipleObjects;
DrawInspectorGUI(multi);
HandleSkinChange();
if (serializedObject.ApplyModifiedProperties() || SpineInspectorUtility.UndoRedoPerformed(Event.current) ||
AreAnyMaskMaterialsMissing()) {
if (!Application.isPlaying) {
foreach (var o in targets)
SpineEditorUtilities.ReinitializeComponent((SkeletonRenderer)o);
SceneView.RepaintAll();
}
}
}
protected virtual void DrawInspectorGUI (bool multi) {
// Initialize.
if (Event.current.type == EventType.Layout) {
if (forceReloadQueued) {
forceReloadQueued = false;
foreach (var c in targets) {
SpineEditorUtilities.ReloadSkeletonDataAssetAndComponent(c as SkeletonRenderer);
}
} else {
foreach (var c in targets) {
var component = c as SkeletonRenderer;
if (!component.valid) {
SpineEditorUtilities.ReinitializeComponent(component);
if (!component.valid) continue;
}
}
}
#if BUILT_IN_SPRITE_MASK_COMPONENT
if (setMaskNoneMaterialsQueued) {
setMaskNoneMaterialsQueued = false;
foreach (var c in targets)
EditorSetMaskMaterials(c as SkeletonRenderer, SpriteMaskInteraction.None);
}
if (setInsideMaskMaterialsQueued) {
setInsideMaskMaterialsQueued = false;
foreach (var c in targets)
EditorSetMaskMaterials(c as SkeletonRenderer, SpriteMaskInteraction.VisibleInsideMask);
}
if (setOutsideMaskMaterialsQueued) {
setOutsideMaskMaterialsQueued = false;
foreach (var c in targets)
EditorSetMaskMaterials(c as SkeletonRenderer, SpriteMaskInteraction.VisibleOutsideMask);
}
if (deleteInsideMaskMaterialsQueued) {
deleteInsideMaskMaterialsQueued = false;
foreach (var c in targets)
EditorDeleteMaskMaterials(c as SkeletonRenderer, SpriteMaskInteraction.VisibleInsideMask);
}
if (deleteOutsideMaskMaterialsQueued) {
deleteOutsideMaskMaterialsQueued = false;
foreach (var c in targets)
EditorDeleteMaskMaterials(c as SkeletonRenderer, SpriteMaskInteraction.VisibleOutsideMask);
}
#endif
#if NO_PREFAB_MESH
if (isInspectingPrefab) {
foreach (var c in targets) {
var component = (SkeletonRenderer)c;
MeshFilter meshFilter = component.GetComponent<MeshFilter>();
if (meshFilter != null && meshFilter.sharedMesh != null)
meshFilter.sharedMesh = null;
}
}
#endif
}
bool valid = TargetIsValid;
// Fields.
if (multi) {
using (new EditorGUILayout.HorizontalScope(EditorStyles.helpBox)) {
SpineInspectorUtility.PropertyFieldFitLabel(skeletonDataAsset, SkeletonDataAssetLabel);
if (GUILayout.Button(ReloadButtonString, ReloadButtonStyle, ReloadButtonWidth))
forceReloadQueued = true;
}
if (valid) EditorGUILayout.PropertyField(initialSkinName, SpineInspectorUtility.TempContent("Initial Skin"));
} else {
var component = (SkeletonRenderer)target;
using (new EditorGUILayout.HorizontalScope(EditorStyles.helpBox)) {
SpineInspectorUtility.PropertyFieldFitLabel(skeletonDataAsset, SkeletonDataAssetLabel);
if (component.valid) {
if (GUILayout.Button(ReloadButtonString, ReloadButtonStyle, ReloadButtonWidth))
forceReloadQueued = true;
}
}
if (component.skeletonDataAsset == null) {
EditorGUILayout.HelpBox("Skeleton Data Asset required", MessageType.Warning);
return;
}
if (!SpineEditorUtilities.SkeletonDataAssetIsValid(component.skeletonDataAsset)) {
EditorGUILayout.HelpBox("Skeleton Data Asset error. Please check Skeleton Data Asset.", MessageType.Error);
return;
}
if (valid)
EditorGUILayout.PropertyField(initialSkinName, SpineInspectorUtility.TempContent("Initial Skin"));
}
EditorGUILayout.Space();
// Sorting Layers
SpineInspectorUtility.SortingPropertyFields(sortingProperties, applyModifiedProperties: true);
if (maskInteraction != null) EditorGUILayout.PropertyField(maskInteraction, MaskInteractionLabel);
if (!valid)
return;
string errorMessage = null;
if (SpineEditorUtilities.Preferences.componentMaterialWarning &&
MaterialChecks.IsMaterialSetupProblematic((SkeletonRenderer)this.target, ref errorMessage)) {
EditorGUILayout.HelpBox(errorMessage, MessageType.Error, true);
}
// More Render Options...
using (new SpineInspectorUtility.BoxScope()) {
EditorGUI.BeginChangeCheck();
EditorGUILayout.BeginHorizontal(GUILayout.Height(EditorGUIUtility.singleLineHeight + 5));
advancedFoldout = EditorGUILayout.Foldout(advancedFoldout, "Advanced");
if (advancedFoldout) {
EditorGUILayout.Space();
if (GUILayout.Button("Debug", EditorStyles.miniButton, GUILayout.Width(65f)))
SkeletonDebugWindow.Init();
} else {
EditorGUILayout.Space();
}
EditorGUILayout.EndHorizontal();
if (advancedFoldout) {
using (new SpineInspectorUtility.IndentScope()) {
using (new EditorGUILayout.HorizontalScope()) {
SpineInspectorUtility.ToggleLeftLayout(initialFlipX);
SpineInspectorUtility.ToggleLeftLayout(initialFlipY);
EditorGUILayout.Space();
}
EditorGUILayout.Space();
EditorGUILayout.LabelField("Renderer Settings", EditorStyles.boldLabel);
using (new SpineInspectorUtility.LabelWidthScope()) {
// Optimization options
if (updateWhenInvisible != null) EditorGUILayout.PropertyField(updateWhenInvisible, UpdateWhenInvisibleLabel);
if (singleSubmesh != null) EditorGUILayout.PropertyField(singleSubmesh, SingleSubmeshLabel);
#if PER_MATERIAL_PROPERTY_BLOCKS
if (fixDrawOrder != null) EditorGUILayout.PropertyField(fixDrawOrder, FixDrawOrderLabel);
#endif
if (immutableTriangles != null) EditorGUILayout.PropertyField(immutableTriangles, ImmubleTrianglesLabel);
EditorGUILayout.PropertyField(clearStateOnDisable, ClearStateOnDisableLabel);
EditorGUILayout.Space();
}
SeparatorsField(separatorSlotNames);
EditorGUILayout.Space();
// Render options
const float MinZSpacing = -0.1f;
const float MaxZSpacing = 0f;
EditorGUILayout.Slider(zSpacing, MinZSpacing, MaxZSpacing, ZSpacingLabel);
EditorGUILayout.Space();
using (new SpineInspectorUtility.LabelWidthScope()) {
EditorGUILayout.LabelField(SpineInspectorUtility.TempContent("Vertex Data", SpineInspectorUtility.UnityIcon<MeshFilter>()), EditorStyles.boldLabel);
if (pmaVertexColors != null) EditorGUILayout.PropertyField(pmaVertexColors, PMAVertexColorsLabel);
EditorGUILayout.PropertyField(tintBlack, TintBlackLabel);
// Optional fields. May be disabled in SkeletonRenderer.
if (normals != null) EditorGUILayout.PropertyField(normals, NormalsLabel);
if (tangents != null) EditorGUILayout.PropertyField(tangents, TangentsLabel);
}
#if BUILT_IN_SPRITE_MASK_COMPONENT
EditorGUILayout.Space();
if (maskMaterialsNone.arraySize > 0 || maskMaterialsInside.arraySize > 0 || maskMaterialsOutside.arraySize > 0) {
EditorGUILayout.LabelField(SpineInspectorUtility.TempContent("Mask Interaction Materials", SpineInspectorUtility.UnityIcon<SpriteMask>()), EditorStyles.boldLabel);
bool differentMaskModesSelected = maskInteraction.hasMultipleDifferentValues;
int activeMaskInteractionValue = differentMaskModesSelected ? -1 : maskInteraction.intValue;
bool ignoredParam = true;
MaskMaterialsEditingField(ref setMaskNoneMaterialsQueued, ref ignoredParam, maskMaterialsNone, MaskMaterialsNoneLabel,
differentMaskModesSelected, allowDelete : false, isActiveMaterial : activeMaskInteractionValue == (int)SpriteMaskInteraction.None);
MaskMaterialsEditingField(ref setInsideMaskMaterialsQueued, ref deleteInsideMaskMaterialsQueued, maskMaterialsInside, MaskMaterialsInsideLabel,
differentMaskModesSelected, allowDelete: true, isActiveMaterial: activeMaskInteractionValue == (int)SpriteMaskInteraction.VisibleInsideMask);
MaskMaterialsEditingField(ref setOutsideMaskMaterialsQueued, ref deleteOutsideMaskMaterialsQueued, maskMaterialsOutside, MaskMaterialsOutsideLabel,
differentMaskModesSelected, allowDelete : true, isActiveMaterial: activeMaskInteractionValue == (int)SpriteMaskInteraction.VisibleOutsideMask);
}
#endif
EditorGUILayout.Space();
if (valid && !isInspectingPrefab) {
if (multi) {
// Support multi-edit SkeletonUtility button.
// EditorGUILayout.Space();
// bool addSkeletonUtility = GUILayout.Button(buttonContent, GUILayout.Height(30));
// foreach (var t in targets) {
// var component = t as Component;
// if (addSkeletonUtility && component.GetComponent<SkeletonUtility>() == null)
// component.gameObject.AddComponent<SkeletonUtility>();
// }
} else {
var component = (Component)target;
if (component.GetComponent<SkeletonUtility>() == null) {
if (SpineInspectorUtility.CenteredButton(SkeletonUtilityButtonContent, 21, true, 200f))
component.gameObject.AddComponent<SkeletonUtility>();
}
}
}
EditorGUILayout.Space();
}
}
if (EditorGUI.EndChangeCheck())
SceneView.RepaintAll();
}
}
protected void SkeletonRootMotionParameter() {
SkeletonRootMotionParameter(targets);
}
public static void SkeletonRootMotionParameter(Object[] targets) {
int rootMotionComponentCount = 0;
foreach (var t in targets) {
var component = t as Component;
if (component.GetComponent<SkeletonRootMotion>() != null) {
++rootMotionComponentCount;
}
}
bool allHaveRootMotion = rootMotionComponentCount == targets.Length;
bool anyHaveRootMotion = rootMotionComponentCount > 0;
using (new GUILayout.HorizontalScope()) {
EditorGUILayout.PrefixLabel("Root Motion");
if (!allHaveRootMotion) {
if (GUILayout.Button(SpineInspectorUtility.TempContent("Add Component", Icons.constraintTransform), GUILayout.MaxWidth(130), GUILayout.Height(18))) {
foreach (var t in targets) {
var component = t as Component;
if (component.GetComponent<SkeletonRootMotion>() == null) {
component.gameObject.AddComponent<SkeletonRootMotion>();
}
}
}
}
if (anyHaveRootMotion) {
if (GUILayout.Button(SpineInspectorUtility.TempContent("Remove Component", Icons.constraintTransform), GUILayout.MaxWidth(140), GUILayout.Height(18))) {
foreach (var t in targets) {
var component = t as Component;
var rootMotionComponent = component.GetComponent<SkeletonRootMotion>();
if (rootMotionComponent != null) {
DestroyImmediate(rootMotionComponent);
}
}
}
}
}
}
public static void SetSeparatorSlotNames (SkeletonRenderer skeletonRenderer, string[] newSlotNames) {
var field = SpineInspectorUtility.GetNonPublicField(typeof(SkeletonRenderer), SeparatorSlotNamesFieldName);
field.SetValue(skeletonRenderer, newSlotNames);
}
public static string[] GetSeparatorSlotNames (SkeletonRenderer skeletonRenderer) {
var field = SpineInspectorUtility.GetNonPublicField(typeof(SkeletonRenderer), SeparatorSlotNamesFieldName);
return field.GetValue(skeletonRenderer) as string[];
}
public static void SeparatorsField (SerializedProperty separatorSlotNames) {
bool multi = separatorSlotNames.serializedObject.isEditingMultipleObjects;
bool hasTerminalSlot = false;
if (!multi) {
var sr = separatorSlotNames.serializedObject.targetObject as ISkeletonComponent;
var skeleton = sr.Skeleton;
int lastSlot = skeleton.Slots.Count - 1;
if (skeleton != null) {
for (int i = 0, n = separatorSlotNames.arraySize; i < n; i++) {
int index = skeleton.FindSlotIndex(separatorSlotNames.GetArrayElementAtIndex(i).stringValue);
if (index == 0 || index == lastSlot) {
hasTerminalSlot = true;
break;
}
}
}
}
string terminalSlotWarning = hasTerminalSlot ? " (!)" : "";
using (new EditorGUILayout.VerticalScope(EditorStyles.helpBox)) {
const string SeparatorsDescription = "Stored names of slots where the Skeleton's render will be split into different batches. This is used by separate components that split the render into different MeshRenderers or GameObjects.";
if (separatorSlotNames.isExpanded) {
EditorGUILayout.PropertyField(separatorSlotNames, SpineInspectorUtility.TempContent(separatorSlotNames.displayName + terminalSlotWarning, Icons.slotRoot, SeparatorsDescription), true);
GUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
if (GUILayout.Button("+", GUILayout.MaxWidth(28f), GUILayout.MaxHeight(15f))) {
separatorSlotNames.arraySize++;
}
GUILayout.EndHorizontal();
EditorGUILayout.Space();
} else
EditorGUILayout.PropertyField(separatorSlotNames, new GUIContent(separatorSlotNames.displayName + string.Format("{0} [{1}]", terminalSlotWarning, separatorSlotNames.arraySize), SeparatorsDescription), true);
}
}
public void MaskMaterialsEditingField(ref bool wasSetRequested, ref bool wasDeleteRequested,
SerializedProperty maskMaterials, GUIContent label,
bool differentMaskModesSelected, bool allowDelete, bool isActiveMaterial) {
using (new EditorGUILayout.HorizontalScope()) {
EditorGUILayout.LabelField(label, isActiveMaterial ? EditorStyles.boldLabel : EditorStyles.label, GUILayout.MinWidth(80f), GUILayout.MaxWidth(140));
EditorGUILayout.LabelField(maskMaterials.hasMultipleDifferentValues ? "-" : maskMaterials.arraySize.ToString(), EditorStyles.miniLabel, GUILayout.Width(42f));
bool enableSetButton = differentMaskModesSelected || maskMaterials.arraySize == 0;
bool enableClearButtons = differentMaskModesSelected || (maskMaterials.arraySize != 0 && !isActiveMaterial);
EditorGUI.BeginDisabledGroup(!enableSetButton);
if (GUILayout.Button(SetMaterialButtonLabel, EditorStyles.miniButtonLeft, GUILayout.Width(46f))) {
wasSetRequested = true;
}
EditorGUI.EndDisabledGroup();
EditorGUI.BeginDisabledGroup(!enableClearButtons);
{
if (GUILayout.Button(ClearMaterialButtonLabel, allowDelete ? EditorStyles.miniButtonMid : EditorStyles.miniButtonRight, GUILayout.Width(46f))) {
maskMaterials.ClearArray();
}
else if (allowDelete && GUILayout.Button(DeleteMaterialButtonLabel, EditorStyles.miniButtonRight, GUILayout.Width(46f))) {
wasDeleteRequested = true;
}
if (!allowDelete)
GUILayout.Space(46f);
}
EditorGUI.EndDisabledGroup();
}
}
void HandleSkinChange() {
if (!Application.isPlaying && Event.current.type == EventType.Layout && !initialSkinName.hasMultipleDifferentValues) {
bool mismatchDetected = false;
string newSkinName = initialSkinName.stringValue;
foreach (var o in targets) {
mismatchDetected |= UpdateIfSkinMismatch((SkeletonRenderer)o, newSkinName);
}
if (mismatchDetected) {
mismatchDetected = false;
UnityEditorInternal.InternalEditorUtility.RepaintAllViews();
}
}
}
static bool UpdateIfSkinMismatch (SkeletonRenderer skeletonRenderer, string componentSkinName) {
if (!skeletonRenderer.valid || skeletonRenderer.EditorSkipSkinSync) return false;
var skin = skeletonRenderer.Skeleton.Skin;
string skeletonSkinName = skin != null ? skin.Name : null;
bool defaultCase = skin == null && string.IsNullOrEmpty(componentSkinName);
bool fieldMatchesSkin = defaultCase || string.Equals(componentSkinName, skeletonSkinName, System.StringComparison.Ordinal);
if (!fieldMatchesSkin) {
Skin skinToSet = string.IsNullOrEmpty(componentSkinName) ? null : skeletonRenderer.Skeleton.Data.FindSkin(componentSkinName);
skeletonRenderer.Skeleton.SetSkin(skinToSet);
skeletonRenderer.Skeleton.SetSlotsToSetupPose();
// Note: the UpdateIfSkinMismatch concept shall be replaced with e.g. an OnValidate based
// solution or in a separate commit. The current solution does not repaint the Game view because
// it is first applying values and in the next editor pass is calling this skin-changing method.
if (skeletonRenderer is SkeletonAnimation)
((SkeletonAnimation) skeletonRenderer).Update(0f);
else if (skeletonRenderer is SkeletonMecanim)
((SkeletonMecanim) skeletonRenderer).Update();
skeletonRenderer.LateUpdate();
return true;
}
return false;
}
bool AreAnyMaskMaterialsMissing() {
#if BUILT_IN_SPRITE_MASK_COMPONENT
foreach (var o in targets) {
var component = (SkeletonRenderer)o;
if (!component.valid)
continue;
if (SpineMaskUtilities.AreMaskMaterialsMissing(component))
return true;
}
#endif
return false;
}
#if BUILT_IN_SPRITE_MASK_COMPONENT
static void EditorSetMaskMaterials(SkeletonRenderer component, SpriteMaskInteraction maskType)
{
if (component == null) return;
if (!SpineEditorUtilities.SkeletonDataAssetIsValid(component.SkeletonDataAsset)) return;
SpineMaskUtilities.EditorInitMaskMaterials(component, component.maskMaterials, maskType);
}
static void EditorDeleteMaskMaterials(SkeletonRenderer component, SpriteMaskInteraction maskType) {
if (component == null) return;
if (!SpineEditorUtilities.SkeletonDataAssetIsValid(component.SkeletonDataAsset)) return;
SpineMaskUtilities.EditorDeleteMaskMaterials(component.maskMaterials, maskType);
}
#endif
}
}

View File

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

View File

@@ -1,113 +0,0 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
using UnityEditor;
using UnityEngine;
namespace Spine.Unity.Editor {
[CustomEditor(typeof(SkeletonRootMotionBase))]
[CanEditMultipleObjects]
public class SkeletonRootMotionBaseInspector : UnityEditor.Editor {
protected SerializedProperty rootMotionBoneName;
protected SerializedProperty transformPositionX;
protected SerializedProperty transformPositionY;
protected SerializedProperty rootMotionScaleX;
protected SerializedProperty rootMotionScaleY;
protected SerializedProperty rootMotionTranslateXPerY;
protected SerializedProperty rootMotionTranslateYPerX;
protected SerializedProperty rigidBody2D;
protected SerializedProperty rigidBody;
protected GUIContent rootMotionBoneNameLabel;
protected GUIContent transformPositionXLabel;
protected GUIContent transformPositionYLabel;
protected GUIContent rootMotionScaleXLabel;
protected GUIContent rootMotionScaleYLabel;
protected GUIContent rootMotionTranslateXPerYLabel;
protected GUIContent rootMotionTranslateYPerXLabel;
protected GUIContent rigidBody2DLabel;
protected GUIContent rigidBodyLabel;
protected virtual void OnEnable () {
rootMotionBoneName = serializedObject.FindProperty("rootMotionBoneName");
transformPositionX = serializedObject.FindProperty("transformPositionX");
transformPositionY = serializedObject.FindProperty("transformPositionY");
rootMotionScaleX = serializedObject.FindProperty("rootMotionScaleX");
rootMotionScaleY = serializedObject.FindProperty("rootMotionScaleY");
rootMotionTranslateXPerY = serializedObject.FindProperty("rootMotionTranslateXPerY");
rootMotionTranslateYPerX = serializedObject.FindProperty("rootMotionTranslateYPerX");
rigidBody2D = serializedObject.FindProperty("rigidBody2D");
rigidBody = serializedObject.FindProperty("rigidBody");
rootMotionBoneNameLabel = new UnityEngine.GUIContent("Root Motion Bone", "The bone to take the motion from.");
transformPositionXLabel = new UnityEngine.GUIContent("X", "Root transform position (X)");
transformPositionYLabel = new UnityEngine.GUIContent("Y", "Use the Y-movement of the bone.");
rootMotionScaleXLabel = new UnityEngine.GUIContent("Root Motion Scale (X)", "Scale applied to the horizontal root motion delta. Can be used for delta compensation to e.g. stretch a jump to the desired distance.");
rootMotionScaleYLabel = new UnityEngine.GUIContent("Root Motion Scale (Y)", "Scale applied to the vertical root motion delta. Can be used for delta compensation to e.g. stretch a jump to the desired distance.");
rootMotionTranslateXPerYLabel = new UnityEngine.GUIContent("Root Motion Translate (X)", "Added X translation per root motion Y delta. Can be used for delta compensation when scaling is not enough, to e.g. offset a horizontal jump to a vertically different goal.");
rootMotionTranslateYPerXLabel = new UnityEngine.GUIContent("Root Motion Translate (Y)", "Added Y translation per root motion X delta. Can be used for delta compensation when scaling is not enough, to e.g. offset a horizontal jump to a vertically different goal.");
rigidBody2DLabel = new UnityEngine.GUIContent("Rigidbody2D",
"Optional Rigidbody2D: Assign a Rigidbody2D here if you want " +
" to apply the root motion to the rigidbody instead of the Transform." +
"\n\n" +
"Note that animation and physics updates are not always in sync." +
"Some jitter may result at certain framerates.");
rigidBodyLabel = new UnityEngine.GUIContent("Rigidbody",
"Optional Rigidbody: Assign a Rigidbody here if you want " +
" to apply the root motion to the rigidbody instead of the Transform." +
"\n\n" +
"Note that animation and physics updates are not always in sync." +
"Some jitter may result at certain framerates.");
}
public override void OnInspectorGUI () {
MainPropertyFields();
OptionalPropertyFields();
serializedObject.ApplyModifiedProperties();
}
protected virtual void MainPropertyFields () {
EditorGUILayout.PropertyField(rootMotionBoneName, rootMotionBoneNameLabel);
EditorGUILayout.PropertyField(transformPositionX, transformPositionXLabel);
EditorGUILayout.PropertyField(transformPositionY, transformPositionYLabel);
EditorGUILayout.PropertyField(rootMotionScaleX, rootMotionScaleXLabel);
EditorGUILayout.PropertyField(rootMotionScaleY, rootMotionScaleYLabel);
EditorGUILayout.PropertyField(rootMotionTranslateXPerY, rootMotionTranslateXPerYLabel);
EditorGUILayout.PropertyField(rootMotionTranslateYPerX, rootMotionTranslateYPerXLabel);
}
protected virtual void OptionalPropertyFields () {
EditorGUILayout.PropertyField(rigidBody2D, rigidBody2DLabel);
EditorGUILayout.PropertyField(rigidBody, rigidBodyLabel);
}
}
}

View File

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

View File

@@ -1,79 +0,0 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
using UnityEditor;
using UnityEngine;
namespace Spine.Unity.Editor {
[CustomEditor(typeof(SkeletonRootMotion))]
[CanEditMultipleObjects]
public class SkeletonRootMotionInspector : SkeletonRootMotionBaseInspector {
protected SerializedProperty animationTrackFlags;
protected GUIContent animationTrackFlagsLabel;
string[] TrackNames;
protected override void OnEnable () {
base.OnEnable();
animationTrackFlags = serializedObject.FindProperty("animationTrackFlags");
animationTrackFlagsLabel = new UnityEngine.GUIContent("Animation Tracks",
"Animation tracks to apply root motion at. Defaults to the first" +
" animation track (index 0).");
}
override public void OnInspectorGUI () {
base.MainPropertyFields();
AnimationTracksPropertyField();
base.OptionalPropertyFields();
serializedObject.ApplyModifiedProperties();
}
protected void AnimationTracksPropertyField () {
if (TrackNames == null) {
InitTrackNames();
}
animationTrackFlags.intValue = EditorGUILayout.MaskField(
animationTrackFlagsLabel, animationTrackFlags.intValue, TrackNames);
}
protected void InitTrackNames () {
int numEntries = 32;
TrackNames = new string[numEntries];
for (int i = 0; i < numEntries; ++i) {
TrackNames[i] = string.Format("Track {0}", i);
}
}
}
}

View File

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

View File

@@ -1,541 +0,0 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
// Contributed by: Mitch Thompson
#if UNITY_2019_2_OR_NEWER
#define HINGE_JOINT_NEW_BEHAVIOUR
#endif
using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
using Spine;
namespace Spine.Unity.Editor {
using Icons = SpineEditorUtilities.Icons;
[CustomEditor(typeof(SkeletonUtilityBone)), CanEditMultipleObjects]
public class SkeletonUtilityBoneInspector : UnityEditor.Editor {
SerializedProperty mode, boneName, zPosition, position, rotation, scale, overrideAlpha, hierarchy, parentReference;
GUIContent hierarchyLabel;
//multi selected flags
bool containsFollows, containsOverrides, multiObject;
//single selected helpers
SkeletonUtilityBone utilityBone;
SkeletonUtility skeletonUtility;
bool canCreateHingeChain = false;
Dictionary<Slot, List<BoundingBoxAttachment>> boundingBoxTable = new Dictionary<Slot, List<BoundingBoxAttachment>>();
void OnEnable () {
mode = this.serializedObject.FindProperty("mode");
boneName = this.serializedObject.FindProperty("boneName");
zPosition = this.serializedObject.FindProperty("zPosition");
position = this.serializedObject.FindProperty("position");
rotation = this.serializedObject.FindProperty("rotation");
scale = this.serializedObject.FindProperty("scale");
overrideAlpha = this.serializedObject.FindProperty("overrideAlpha");
hierarchy = this.serializedObject.FindProperty("hierarchy");
hierarchyLabel = new GUIContent("Skeleton Utility Parent");
parentReference = this.serializedObject.FindProperty("parentReference");
utilityBone = (SkeletonUtilityBone)target;
skeletonUtility = utilityBone.hierarchy;
EvaluateFlags();
if (!utilityBone.valid && skeletonUtility != null) {
if (skeletonUtility.skeletonRenderer != null)
skeletonUtility.skeletonRenderer.Initialize(false);
if (skeletonUtility.skeletonGraphic != null)
skeletonUtility.skeletonGraphic.Initialize(false);
}
canCreateHingeChain = CanCreateHingeChain();
boundingBoxTable.Clear();
if (multiObject) return;
if (utilityBone.bone == null) return;
var skeleton = utilityBone.bone.Skeleton;
int slotCount = skeleton.Slots.Count;
Skin skin = skeleton.Skin;
if (skeleton.Skin == null)
skin = skeleton.Data.DefaultSkin;
for(int i = 0; i < slotCount; i++){
Slot slot = skeletonUtility.Skeleton.Slots.Items[i];
if (slot.Bone == utilityBone.bone) {
var slotAttachments = new List<Skin.SkinEntry>();
int slotIndex = skeleton.FindSlotIndex(slot.Data.Name);
skin.GetAttachments(slotIndex, slotAttachments);
var boundingBoxes = new List<BoundingBoxAttachment>();
foreach (var att in slotAttachments) {
var boundingBoxAttachment = att.Attachment as BoundingBoxAttachment;
if (boundingBoxAttachment != null)
boundingBoxes.Add(boundingBoxAttachment);
}
if (boundingBoxes.Count > 0)
boundingBoxTable.Add(slot, boundingBoxes);
}
}
}
void EvaluateFlags () {
if (Selection.objects.Length == 1) {
containsFollows = utilityBone.mode == SkeletonUtilityBone.Mode.Follow;
containsOverrides = utilityBone.mode == SkeletonUtilityBone.Mode.Override;
} else {
int boneCount = 0;
foreach (Object o in Selection.objects) {
var go = o as GameObject;
if (go != null) {
SkeletonUtilityBone sub = go.GetComponent<SkeletonUtilityBone>();
if (sub != null) {
boneCount++;
containsFollows |= (sub.mode == SkeletonUtilityBone.Mode.Follow);
containsOverrides |= (sub.mode == SkeletonUtilityBone.Mode.Override);
}
}
}
multiObject |= (boneCount > 1);
}
}
public override void OnInspectorGUI () {
serializedObject.Update();
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(mode);
if (EditorGUI.EndChangeCheck()) {
containsOverrides = mode.enumValueIndex == 1;
containsFollows = mode.enumValueIndex == 0;
}
using (new EditorGUI.DisabledGroupScope(multiObject)) {
string str = boneName.stringValue;
if (str == "")
str = "<None>";
if (multiObject)
str = "<Multiple>";
using (new GUILayout.HorizontalScope()) {
EditorGUILayout.PrefixLabel("Bone");
if (GUILayout.Button(str, EditorStyles.popup)) {
BoneSelectorContextMenu(str, ((SkeletonUtilityBone)target).hierarchy.Skeleton.Bones, "<None>", TargetBoneSelected);
}
}
}
EditorGUILayout.PropertyField(zPosition);
EditorGUILayout.PropertyField(position);
EditorGUILayout.PropertyField(rotation);
EditorGUILayout.PropertyField(scale);
using (new EditorGUI.DisabledGroupScope(containsFollows)) {
EditorGUILayout.PropertyField(overrideAlpha);
EditorGUILayout.PropertyField(parentReference);
EditorGUILayout.PropertyField(hierarchy, hierarchyLabel);
}
EditorGUILayout.Space();
using (new GUILayout.HorizontalScope()) {
EditorGUILayout.Space();
using (new EditorGUI.DisabledGroupScope(multiObject || !utilityBone.valid || utilityBone.bone == null || utilityBone.bone.Children.Count == 0)) {
if (GUILayout.Button(SpineInspectorUtility.TempContent("Add Child Bone", Icons.bone), GUILayout.MinWidth(120), GUILayout.Height(24)))
BoneSelectorContextMenu("", utilityBone.bone.Children, "<Recursively>", SpawnChildBoneSelected);
}
using (new EditorGUI.DisabledGroupScope(multiObject || !utilityBone.valid || utilityBone.bone == null || containsOverrides)) {
if (GUILayout.Button(SpineInspectorUtility.TempContent("Add Override", Icons.poseBones), GUILayout.MinWidth(120), GUILayout.Height(24)))
SpawnOverride();
}
EditorGUILayout.Space();
}
EditorGUILayout.Space();
using (new GUILayout.HorizontalScope()) {
EditorGUILayout.Space();
using (new EditorGUI.DisabledGroupScope(multiObject || !utilityBone.valid || !canCreateHingeChain)) {
if (GUILayout.Button(SpineInspectorUtility.TempContent("Create 3D Hinge Chain", Icons.hingeChain), GUILayout.MinWidth(120), GUILayout.Height(24)))
CreateHingeChain();
if (GUILayout.Button(SpineInspectorUtility.TempContent("Create 2D Hinge Chain", Icons.hingeChain), GUILayout.MinWidth(120), GUILayout.Height(24)))
CreateHingeChain2D();
}
EditorGUILayout.Space();
}
using (new EditorGUI.DisabledGroupScope(multiObject || boundingBoxTable.Count == 0)) {
EditorGUILayout.LabelField(SpineInspectorUtility.TempContent("Bounding Boxes", Icons.boundingBox), EditorStyles.boldLabel);
foreach (var entry in boundingBoxTable){
Slot slot = entry.Key;
var boundingBoxes = entry.Value;
EditorGUI.indentLevel++;
EditorGUILayout.LabelField(slot.Data.Name);
EditorGUI.indentLevel++;
{
foreach (var box in boundingBoxes) {
using (new GUILayout.HorizontalScope()) {
GUILayout.Space(30);
string buttonLabel = box.IsWeighted() ? box.Name + " (!)" : box.Name;
if (GUILayout.Button(buttonLabel, GUILayout.Width(200))) {
utilityBone.bone.Skeleton.UpdateWorldTransform();
var bbTransform = utilityBone.transform.Find("[BoundingBox]" + box.Name); // Use FindChild in older versions of Unity.
if (bbTransform != null) {
var originalCollider = bbTransform.GetComponent<PolygonCollider2D>();
if (originalCollider != null)
SkeletonUtility.SetColliderPointsLocal(originalCollider, slot, box);
else
SkeletonUtility.AddBoundingBoxAsComponent(box, slot, bbTransform.gameObject);
} else {
var newPolygonCollider = SkeletonUtility.AddBoundingBoxGameObject(null, box, slot, utilityBone.transform);
bbTransform = newPolygonCollider.transform;
}
EditorGUIUtility.PingObject(bbTransform);
}
}
}
}
EditorGUI.indentLevel--;
EditorGUI.indentLevel--;
}
}
BoneFollowerInspector.RecommendRigidbodyButton(utilityBone);
serializedObject.ApplyModifiedProperties();
}
static void BoneSelectorContextMenu (string current, ExposedList<Bone> bones, string topValue, GenericMenu.MenuFunction2 callback) {
var menu = new GenericMenu();
if (topValue != "")
menu.AddItem(new GUIContent(topValue), current == topValue, callback, null);
for (int i = 0; i < bones.Count; i++)
menu.AddItem(new GUIContent(bones.Items[i].Data.Name), bones.Items[i].Data.Name == current, callback, bones.Items[i]);
menu.ShowAsContext();
}
void TargetBoneSelected (object obj) {
if (obj == null) {
boneName.stringValue = "";
serializedObject.ApplyModifiedProperties();
} else {
var bone = (Bone)obj;
boneName.stringValue = bone.Data.Name;
serializedObject.ApplyModifiedProperties();
utilityBone.Reset();
}
}
void SpawnChildBoneSelected (object obj) {
if (obj == null) {
// Add recursively
foreach (var bone in utilityBone.bone.Children) {
GameObject go = skeletonUtility.SpawnBoneRecursively(bone, utilityBone.transform, utilityBone.mode, utilityBone.position, utilityBone.rotation, utilityBone.scale);
SkeletonUtilityBone[] newUtilityBones = go.GetComponentsInChildren<SkeletonUtilityBone>();
foreach (SkeletonUtilityBone utilBone in newUtilityBones)
SkeletonUtilityInspector.AttachIcon(utilBone);
}
} else {
var bone = (Bone)obj;
GameObject go = skeletonUtility.SpawnBone(bone, utilityBone.transform, utilityBone.mode, utilityBone.position, utilityBone.rotation, utilityBone.scale);
SkeletonUtilityInspector.AttachIcon(go.GetComponent<SkeletonUtilityBone>());
Selection.activeGameObject = go;
EditorGUIUtility.PingObject(go);
}
}
void SpawnOverride () {
GameObject go = skeletonUtility.SpawnBone(utilityBone.bone, utilityBone.transform.parent, SkeletonUtilityBone.Mode.Override, utilityBone.position, utilityBone.rotation, utilityBone.scale);
go.name = go.name + " [Override]";
SkeletonUtilityInspector.AttachIcon(go.GetComponent<SkeletonUtilityBone>());
Selection.activeGameObject = go;
EditorGUIUtility.PingObject(go);
}
bool CanCreateHingeChain () {
if (utilityBone == null)
return false;
if (utilityBone.GetComponent<Rigidbody>() != null || utilityBone.GetComponent<Rigidbody2D>() != null)
return false;
if (utilityBone.bone != null && utilityBone.bone.Children.Count == 0)
return false;
var rigidbodies = utilityBone.GetComponentsInChildren<Rigidbody>();
var rigidbodies2D = utilityBone.GetComponentsInChildren<Rigidbody2D>();
return rigidbodies.Length <= 0 && rigidbodies2D.Length <= 0;
}
void CreateHingeChain2D () {
var kinematicParentUtilityBone = utilityBone.transform.parent.GetComponent<SkeletonUtilityBone>();
if (kinematicParentUtilityBone == null) {
UnityEditor.EditorUtility.DisplayDialog("No parent SkeletonUtilityBone found!", "Please select the first physically moving chain node, having a parent GameObject with a SkeletonUtilityBone component attached.", "OK");
return;
}
float mass = 10;
const float rotationLimit = 20.0f;
SetSkeletonUtilityToFlipByRotation();
kinematicParentUtilityBone.mode = SkeletonUtilityBone.Mode.Follow;
kinematicParentUtilityBone.position = kinematicParentUtilityBone.rotation = kinematicParentUtilityBone.scale = kinematicParentUtilityBone.zPosition = true;
GameObject commonParentObject = new GameObject(skeletonUtility.name + " HingeChain Parent " + utilityBone.name);
var commonParentActivateOnFlip = commonParentObject.AddComponent<ActivateBasedOnFlipDirection>();
commonParentActivateOnFlip.skeletonRenderer = skeletonUtility.skeletonRenderer;
commonParentActivateOnFlip.skeletonGraphic = skeletonUtility.skeletonGraphic;
// HingeChain Parent
// Needs to be on top hierarchy level (not attached to the moving skeleton at least) for physics to apply proper momentum.
GameObject normalChainParentObject = new GameObject("HingeChain");
normalChainParentObject.transform.SetParent(commonParentObject.transform);
commonParentActivateOnFlip.activeOnNormalX = normalChainParentObject;
//var followRotationComponent = normalChainParentObject.AddComponent<FollowSkeletonUtilityRootRotation>();
//followRotationComponent.reference = skeletonUtility.boneRoot;
// Follower Kinematic Rigidbody
GameObject followerKinematicObject = new GameObject(kinematicParentUtilityBone.name + " Follower");
followerKinematicObject.transform.parent = normalChainParentObject.transform;
var followerRigidbody = followerKinematicObject.AddComponent<Rigidbody2D>();
followerRigidbody.mass = mass;
followerRigidbody.isKinematic = true;
followerKinematicObject.AddComponent<FollowLocationRigidbody2D>().reference = kinematicParentUtilityBone.transform;
followerKinematicObject.transform.position = kinematicParentUtilityBone.transform.position;
followerKinematicObject.transform.rotation = kinematicParentUtilityBone.transform.rotation;
// Child Bones
var utilityBones = utilityBone.GetComponentsInChildren<SkeletonUtilityBone>();
var childBoneParentReference = followerKinematicObject.transform;
for (int i = 0; i < utilityBones.Length; ++i) {
var childBone = utilityBones[i];
mass *= 0.75f;
childBone.parentReference = (i == 0) ? kinematicParentUtilityBone.transform : childBoneParentReference;
childBone.transform.SetParent(normalChainParentObject.transform, true); // we need a flat hierarchy of all Joint objects in Unity.
AttachRigidbodyAndCollider2D(childBone);
childBone.mode = SkeletonUtilityBone.Mode.Override;
childBone.scale = childBone.position = childBone.zPosition = false;
HingeJoint2D joint = childBone.gameObject.AddComponent<HingeJoint2D>();
joint.connectedBody = childBoneParentReference.GetComponent<Rigidbody2D>();
joint.useLimits = true;
ApplyJoint2DAngleLimits(joint, rotationLimit, childBoneParentReference, childBone.transform);
childBone.GetComponent<Rigidbody2D>().mass = mass;
childBoneParentReference = childBone.transform;
}
Duplicate2DHierarchyForFlippedChains(normalChainParentObject, commonParentActivateOnFlip, skeletonUtility.transform, rotationLimit);
UnityEditor.Selection.activeGameObject = commonParentObject;
}
void ApplyJoint2DAngleLimits (HingeJoint2D joint, float rotationLimit, Transform parentBone, Transform bone) {
#if HINGE_JOINT_NEW_BEHAVIOUR
float referenceAngle = (parentBone.eulerAngles.z - bone.eulerAngles.z + 360f) % 360f;
float minAngle = referenceAngle - rotationLimit;
float maxAngle = referenceAngle + rotationLimit;
if (maxAngle > 270f) {
minAngle -= 360f;
maxAngle -= 360f;
}
if (minAngle < -90f) {
minAngle += 360f;
maxAngle += 360f;
}
#else
float minAngle = - rotationLimit;
float maxAngle = rotationLimit;
#endif
joint.limits = new JointAngleLimits2D {
min = minAngle,
max = maxAngle
};
}
void Duplicate2DHierarchyForFlippedChains (GameObject normalChainParentObject, ActivateBasedOnFlipDirection commonParentActivateOnFlip,
Transform skeletonUtilityRoot, float rotationLimit) {
GameObject mirroredChain = GameObject.Instantiate(normalChainParentObject, normalChainParentObject.transform.position,
normalChainParentObject.transform.rotation, commonParentActivateOnFlip.transform);
mirroredChain.name = normalChainParentObject.name + " FlippedX";
commonParentActivateOnFlip.activeOnFlippedX = mirroredChain;
var followerKinematicObject = mirroredChain.GetComponentInChildren<FollowLocationRigidbody2D>();
followerKinematicObject.followFlippedX = true;
FlipBone2DHorizontal(followerKinematicObject.transform, skeletonUtilityRoot);
var childBoneJoints = mirroredChain.GetComponentsInChildren<HingeJoint2D>();
Transform prevRotatedChild = null;
Transform parentTransformForAngles = followerKinematicObject.transform;
for (int i = 0; i < childBoneJoints.Length; ++i) {
var joint = childBoneJoints[i];
FlipBone2DHorizontal(joint.transform, skeletonUtilityRoot);
ApplyJoint2DAngleLimits(joint, rotationLimit, parentTransformForAngles, joint.transform);
GameObject rotatedChild = GameObject.Instantiate(joint.gameObject, joint.transform, true);
rotatedChild.name = joint.name + " rotated";
var rotationEulerAngles = rotatedChild.transform.localEulerAngles;
rotationEulerAngles.x = 180;
rotatedChild.transform.localEulerAngles = rotationEulerAngles;
DestroyImmediate(rotatedChild.GetComponent<HingeJoint2D>());
DestroyImmediate(rotatedChild.GetComponent<BoxCollider2D>());
DestroyImmediate(rotatedChild.GetComponent<Rigidbody2D>());
DestroyImmediate(joint.gameObject.GetComponent<SkeletonUtilityBone>());
if (i > 0) {
var utilityBone = rotatedChild.GetComponent<SkeletonUtilityBone>();
utilityBone.parentReference = prevRotatedChild;
}
prevRotatedChild = rotatedChild.transform;
parentTransformForAngles = joint.transform;
}
mirroredChain.SetActive(false);
}
void FlipBone2DHorizontal(Transform bone, Transform mirrorPosition) {
Vector3 position = bone.position;
position.x = 2 * mirrorPosition.position.x - position.x; // = mirrorPosition + (mirrorPosition - bone.position)
bone.position = position;
Vector3 boneZ = bone.forward;
Vector3 boneX = bone.right;
boneX.x *= -1;
bone.rotation = Quaternion.LookRotation(boneZ, Vector3.Cross(boneZ, boneX));
}
void CreateHingeChain () {
var kinematicParentUtilityBone = utilityBone.transform.parent.GetComponent<SkeletonUtilityBone>();
if (kinematicParentUtilityBone == null) {
UnityEditor.EditorUtility.DisplayDialog("No parent SkeletonUtilityBone found!", "Please select the first physically moving chain node, having a parent GameObject with a SkeletonUtilityBone component attached.", "OK");
return;
}
SetSkeletonUtilityToFlipByRotation();
kinematicParentUtilityBone.mode = SkeletonUtilityBone.Mode.Follow;
kinematicParentUtilityBone.position = kinematicParentUtilityBone.rotation = kinematicParentUtilityBone.scale = kinematicParentUtilityBone.zPosition = true;
// HingeChain Parent
// Needs to be on top hierarchy level (not attached to the moving skeleton at least) for physics to apply proper momentum.
GameObject chainParentObject = new GameObject(skeletonUtility.name + " HingeChain Parent " + utilityBone.name);
var followRotationComponent = chainParentObject.AddComponent<FollowSkeletonUtilityRootRotation>();
followRotationComponent.reference = skeletonUtility.boneRoot;
// Follower Kinematic Rigidbody
GameObject followerKinematicObject = new GameObject(kinematicParentUtilityBone.name + " Follower");
followerKinematicObject.transform.parent = chainParentObject.transform;
var followerRigidbody = followerKinematicObject.AddComponent<Rigidbody>();
followerRigidbody.mass = 10;
followerRigidbody.isKinematic = true;
followerKinematicObject.AddComponent<FollowLocationRigidbody>().reference = kinematicParentUtilityBone.transform;
followerKinematicObject.transform.position = kinematicParentUtilityBone.transform.position;
followerKinematicObject.transform.rotation = kinematicParentUtilityBone.transform.rotation;
// Child Bones
var utilityBones = utilityBone.GetComponentsInChildren<SkeletonUtilityBone>();
var childBoneParentReference = followerKinematicObject.transform;
foreach (var childBone in utilityBones) {
childBone.parentReference = childBoneParentReference;
childBone.transform.SetParent(chainParentObject.transform, true); // we need a flat hierarchy of all Joint objects in Unity.
AttachRigidbodyAndCollider(childBone);
childBone.mode = SkeletonUtilityBone.Mode.Override;
HingeJoint joint = childBone.gameObject.AddComponent<HingeJoint>();
joint.axis = Vector3.forward;
joint.connectedBody = childBoneParentReference.GetComponent<Rigidbody>();
joint.useLimits = true;
joint.limits = new JointLimits {
min = -20,
max = 20
};
childBone.GetComponent<Rigidbody>().mass = childBoneParentReference.transform.GetComponent<Rigidbody>().mass * 0.75f;
childBoneParentReference = childBone.transform;
}
UnityEditor.Selection.activeGameObject = chainParentObject;
}
void SetSkeletonUtilityToFlipByRotation () {
if (!skeletonUtility.flipBy180DegreeRotation) {
skeletonUtility.flipBy180DegreeRotation = true;
Debug.Log("Set SkeletonUtility " + skeletonUtility.name + " to flip by rotation instead of negative scale (required).", skeletonUtility);
}
}
static void AttachRigidbodyAndCollider (SkeletonUtilityBone utilBone, bool enableCollider = false) {
if (utilBone.GetComponent<Collider>() == null) {
if (utilBone.bone.Data.Length == 0) {
SphereCollider sphere = utilBone.gameObject.AddComponent<SphereCollider>();
sphere.radius = 0.1f;
sphere.enabled = enableCollider;
} else {
float length = utilBone.bone.Data.Length;
BoxCollider box = utilBone.gameObject.AddComponent<BoxCollider>();
box.size = new Vector3(length, length / 3f, 0.2f);
box.center = new Vector3(length / 2f, 0, 0);
box.enabled = enableCollider;
}
}
utilBone.gameObject.AddComponent<Rigidbody>();
}
static void AttachRigidbodyAndCollider2D(SkeletonUtilityBone utilBone, bool enableCollider = false) {
if (utilBone.GetComponent<Collider2D>() == null) {
if (utilBone.bone.Data.Length == 0) {
var sphere = utilBone.gameObject.AddComponent<CircleCollider2D>();
sphere.radius = 0.1f;
sphere.enabled = enableCollider;
}
else {
float length = utilBone.bone.Data.Length;
var box = utilBone.gameObject.AddComponent<BoxCollider2D>();
box.size = new Vector3(length, length / 3f, 0.2f);
box.offset = new Vector3(length / 2f, 0, 0);
box.enabled = enableCollider;
}
}
utilBone.gameObject.AddComponent<Rigidbody2D>();
}
}
}

View File

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

View File

@@ -1,192 +0,0 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#if UNITY_2018_3 || UNITY_2019 || UNITY_2018_3_OR_NEWER
#define NEW_PREFAB_SYSTEM
#endif
using UnityEngine;
using UnityEditor;
using UnityEditor.AnimatedValues;
using System.Collections.Generic;
using Spine;
using System.Reflection;
namespace Spine.Unity.Editor {
using Icons = SpineEditorUtilities.Icons;
[CustomEditor(typeof(SkeletonUtility))]
public class SkeletonUtilityInspector : UnityEditor.Editor {
SkeletonUtility skeletonUtility;
Skeleton skeleton;
SkeletonRenderer skeletonRenderer;
SkeletonGraphic skeletonGraphic;
#if !NEW_PREFAB_SYSTEM
bool isPrefab;
#endif
readonly GUIContent SpawnHierarchyButtonLabel = new GUIContent("Spawn Hierarchy", Icons.skeleton);
void OnEnable () {
skeletonUtility = (SkeletonUtility)target;
skeletonRenderer = skeletonUtility.skeletonRenderer;
skeletonGraphic = skeletonUtility.skeletonGraphic;
skeleton = skeletonUtility.Skeleton;
if (skeleton == null) {
if (skeletonRenderer != null) {
skeletonRenderer.Initialize(false);
skeletonRenderer.LateUpdate();
}
else if (skeletonGraphic != null) {
skeletonGraphic.Initialize(false);
skeletonGraphic.LateUpdate();
}
skeleton = skeletonUtility.Skeleton;
}
if ((skeletonRenderer != null && !skeletonRenderer.valid) ||
(skeletonGraphic != null && !skeletonGraphic.IsValid)) return;
#if !NEW_PREFAB_SYSTEM
isPrefab |= PrefabUtility.GetPrefabType(this.target) == PrefabType.Prefab;
#endif
}
public override void OnInspectorGUI () {
#if !NEW_PREFAB_SYSTEM
if (isPrefab) {
GUILayout.Label(new GUIContent("Cannot edit Prefabs", Icons.warning));
return;
}
#endif
serializedObject.Update();
if ((skeletonRenderer != null && !skeletonRenderer.valid) ||
(skeletonGraphic != null && !skeletonGraphic.IsValid)) {
GUILayout.Label(new GUIContent("Spine Component invalid. Check Skeleton Data Asset.", Icons.warning));
return;
}
EditorGUILayout.PropertyField(serializedObject.FindProperty("boneRoot"), SpineInspectorUtility.TempContent("Skeleton Root"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("flipBy180DegreeRotation"), SpineInspectorUtility.TempContent("Flip by Rotation", null,
"If true, Skeleton.ScaleX and Skeleton.ScaleY are followed " +
"by 180 degree rotation. If false, negative Transform scale is used. " +
"Note that using negative scale is consistent with previous behaviour (hence the default), " +
"however causes serious problems with rigidbodies and physics. Therefore, it is recommended to " +
"enable this parameter where possible. When creating hinge chains for a chain of skeleton bones " +
"via SkeletonUtilityBone, it is mandatory to have this parameter enabled."));
bool hasRootBone = skeletonUtility.boneRoot != null;
if (!hasRootBone)
EditorGUILayout.HelpBox("No hierarchy found. Use Spawn Hierarchy to generate GameObjects for bones.", MessageType.Info);
using (new EditorGUI.DisabledGroupScope(hasRootBone)) {
if (SpineInspectorUtility.LargeCenteredButton(SpawnHierarchyButtonLabel))
SpawnHierarchyContextMenu();
}
if (hasRootBone) {
if (SpineInspectorUtility.CenteredButton(new GUIContent("Remove Hierarchy"))) {
Undo.RegisterCompleteObjectUndo(skeletonUtility, "Remove Hierarchy");
Undo.DestroyObjectImmediate(skeletonUtility.boneRoot.gameObject);
skeletonUtility.boneRoot = null;
}
}
serializedObject.ApplyModifiedProperties();
}
void SpawnHierarchyContextMenu () {
var menu = new GenericMenu();
menu.AddItem(new GUIContent("Follow all bones"), false, SpawnFollowHierarchy);
menu.AddItem(new GUIContent("Follow (Root Only)"), false, SpawnFollowHierarchyRootOnly);
menu.AddSeparator("");
menu.AddItem(new GUIContent("Override all bones"), false, SpawnOverrideHierarchy);
menu.AddItem(new GUIContent("Override (Root Only)"), false, SpawnOverrideHierarchyRootOnly);
menu.ShowAsContext();
}
public static void AttachIcon (SkeletonUtilityBone boneComponent) {
Skeleton skeleton = boneComponent.hierarchy.Skeleton;
Texture2D icon = boneComponent.bone.Data.Length == 0 ? Icons.nullBone : Icons.boneNib;
foreach (IkConstraint c in skeleton.IkConstraints)
if (c.Target == boneComponent.bone) {
icon = Icons.constraintNib;
break;
}
typeof(EditorGUIUtility).InvokeMember("SetIconForObject", BindingFlags.InvokeMethod | BindingFlags.Static | BindingFlags.NonPublic, null, null, new object[2] {
boneComponent.gameObject,
icon
});
}
static void AttachIconsToChildren (Transform root) {
if (root != null) {
var utilityBones = root.GetComponentsInChildren<SkeletonUtilityBone>();
foreach (var utilBone in utilityBones)
AttachIcon(utilBone);
}
}
void SpawnFollowHierarchy () {
Undo.RegisterCompleteObjectUndo(skeletonUtility, "Spawn Hierarchy");
Selection.activeGameObject = skeletonUtility.SpawnHierarchy(SkeletonUtilityBone.Mode.Follow, true, true, true);
AttachIconsToChildren(skeletonUtility.boneRoot);
}
void SpawnFollowHierarchyRootOnly () {
Undo.RegisterCompleteObjectUndo(skeletonUtility, "Spawn Root");
Selection.activeGameObject = skeletonUtility.SpawnRoot(SkeletonUtilityBone.Mode.Follow, true, true, true);
AttachIconsToChildren(skeletonUtility.boneRoot);
}
void SpawnOverrideHierarchy () {
Undo.RegisterCompleteObjectUndo(skeletonUtility, "Spawn Hierarchy");
Selection.activeGameObject = skeletonUtility.SpawnHierarchy(SkeletonUtilityBone.Mode.Override, true, true, true);
AttachIconsToChildren(skeletonUtility.boneRoot);
}
void SpawnOverrideHierarchyRootOnly () {
Undo.RegisterCompleteObjectUndo(skeletonUtility, "Spawn Root");
Selection.activeGameObject = skeletonUtility.SpawnRoot(SkeletonUtilityBone.Mode.Override, true, true, true);
AttachIconsToChildren(skeletonUtility.boneRoot);
}
}
}

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 563 B

View File

@@ -1,46 +0,0 @@
fileFormatVersion: 2
guid: 68defdbc95b30a74a9ad396bfc9a2277
TextureImporter:
serializedVersion: 2
mipmaps:
mipMapMode: 0
enableMipMap: 0
linearTexture: 1
correctGamma: 0
fadeOut: 0
borderMipMap: 0
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: .25
normalMapFilter: 0
isReadable: 0
grayScaleToAlpha: 0
generateCubemap: 0
seamlessCubemap: 0
textureFormat: -3
maxTextureSize: 1024
textureSettings:
filterMode: -1
aniso: 1
mipBias: -1
wrapMode: 1
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: .5, y: .5}
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spritePixelsToUnits: 100
alphaIsTransparency: 1
textureType: 2
buildTargetSettings: []
spriteSheet:
sprites: []
spritePackingTag:
userData:

View File

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

View File

@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 687d9be457ea4eb44bf09c35c95ee5cd
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View File

@@ -1,174 +0,0 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
namespace Spine.Unity.Editor {
using Editor = UnityEditor.Editor;
using Icons = SpineEditorUtilities.Icons;
public class SkeletonBakingWindow : EditorWindow {
const bool IsUtilityWindow = true;
[MenuItem("CONTEXT/SkeletonDataAsset/Skeleton Baking", false, 5000)]
public static void Init (MenuCommand command) {
var window = EditorWindow.GetWindow<SkeletonBakingWindow>(IsUtilityWindow);
window.minSize = new Vector2(330f, 530f);
window.maxSize = new Vector2(600f, 1000f);
window.titleContent = new GUIContent("Skeleton Baking", Icons.spine);
window.skeletonDataAsset = command.context as SkeletonDataAsset;
window.Show();
}
public SkeletonDataAsset skeletonDataAsset;
[SpineSkin(dataField:"skeletonDataAsset")]
public string skinToBake = "default";
// Settings
bool bakeAnimations = false;
bool bakeIK = true;
SendMessageOptions bakeEventOptions;
SerializedObject so;
Skin bakeSkin;
void DataAssetChanged () {
bakeSkin = null;
}
void OnGUI () {
so = so ?? new SerializedObject(this);
EditorGUIUtility.wideMode = true;
EditorGUILayout.LabelField("Spine Skeleton Prefab Baking", EditorStyles.boldLabel);
const string BakingWarningMessage = "\nSkeleton baking is not the primary use case for Spine skeletons." +
"\nUse baking if you have specialized uses, such as simplified skeletons with movement driven by physics." +
"\n\nBaked Skeletons do not support the following:" +
"\n\tDisabled rotation or scale inheritance" +
"\n\tLocal Shear" +
"\n\tAll Constraint types" +
"\n\tWeighted mesh verts with more than 4 bound bones" +
"\n\nBaked Animations do not support the following:" +
"\n\tMesh Deform Keys" +
"\n\tColor Keys" +
"\n\tDraw Order Keys" +
"\n\nAnimation Curves are sampled at 60fps and are not realtime." +
"\nConstraint animations are also baked into animation curves." +
"\nSee SkeletonBaker.cs comments for full details.\n";
EditorGUILayout.HelpBox(BakingWarningMessage, MessageType.Info, true);
EditorGUI.BeginChangeCheck();
var skeletonDataAssetProperty = so.FindProperty("skeletonDataAsset");
EditorGUILayout.PropertyField(skeletonDataAssetProperty, SpineInspectorUtility.TempContent("SkeletonDataAsset", Icons.spine));
if (EditorGUI.EndChangeCheck()) {
so.ApplyModifiedProperties();
DataAssetChanged();
}
EditorGUILayout.Space();
if (skeletonDataAsset == null) return;
var skeletonData = skeletonDataAsset.GetSkeletonData(false);
if (skeletonData == null) return;
bool hasExtraSkins = skeletonData.Skins.Count > 1;
using (new SpineInspectorUtility.BoxScope(false)) {
EditorGUILayout.LabelField(skeletonDataAsset.name, EditorStyles.boldLabel);
using (new SpineInspectorUtility.IndentScope()) {
EditorGUILayout.LabelField(SpineInspectorUtility.TempContent("Bones: " + skeletonData.Bones.Count, Icons.bone));
EditorGUILayout.LabelField(SpineInspectorUtility.TempContent("Slots: " + skeletonData.Slots.Count, Icons.slotRoot));
if (hasExtraSkins) {
EditorGUILayout.LabelField(SpineInspectorUtility.TempContent("Skins: " + skeletonData.Skins.Count, Icons.skinsRoot));
EditorGUILayout.LabelField(SpineInspectorUtility.TempContent("Current skin attachments: " + (bakeSkin == null ? 0 : bakeSkin.Attachments.Count), Icons.skinPlaceholder));
} else if (skeletonData.Skins.Count == 1) {
EditorGUILayout.LabelField(SpineInspectorUtility.TempContent("Skins: 1 (only default Skin)", Icons.skinsRoot));
}
int totalAttachments = 0;
foreach (var s in skeletonData.Skins)
totalAttachments += s.Attachments.Count;
EditorGUILayout.LabelField(SpineInspectorUtility.TempContent("Total Attachments: " + totalAttachments, Icons.genericAttachment));
}
}
using (new SpineInspectorUtility.BoxScope(false)) {
EditorGUILayout.LabelField("Animations", EditorStyles.boldLabel);
EditorGUILayout.LabelField(SpineInspectorUtility.TempContent("Animations: " + skeletonData.Animations.Count, Icons.animation));
using (new SpineInspectorUtility.IndentScope()) {
bakeAnimations = EditorGUILayout.Toggle(SpineInspectorUtility.TempContent("Bake Animations", Icons.animationRoot), bakeAnimations);
using (new EditorGUI.DisabledScope(!bakeAnimations)) {
using (new SpineInspectorUtility.IndentScope()) {
bakeIK = EditorGUILayout.Toggle(SpineInspectorUtility.TempContent("Bake IK", Icons.constraintIK), bakeIK);
bakeEventOptions = (SendMessageOptions)EditorGUILayout.EnumPopup(SpineInspectorUtility.TempContent("Event Options", Icons.userEvent), bakeEventOptions);
}
}
}
}
EditorGUILayout.Space();
if (!string.IsNullOrEmpty(skinToBake) && UnityEngine.Event.current.type == EventType.Repaint)
bakeSkin = skeletonData.FindSkin(skinToBake) ?? skeletonData.DefaultSkin;
var prefabIcon = EditorGUIUtility.FindTexture("PrefabModel Icon");
if (hasExtraSkins) {
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(so.FindProperty("skinToBake"));
if (EditorGUI.EndChangeCheck()) {
so.ApplyModifiedProperties();
Repaint();
}
if (SpineInspectorUtility.LargeCenteredButton(SpineInspectorUtility.TempContent(string.Format("Bake Skeleton with Skin ({0})", (bakeSkin == null ? "default" : bakeSkin.Name)), prefabIcon))) {
SkeletonBaker.BakeToPrefab(skeletonDataAsset, new ExposedList<Skin>(new[] { bakeSkin }), "", bakeAnimations, bakeIK, bakeEventOptions);
}
if (SpineInspectorUtility.LargeCenteredButton(SpineInspectorUtility.TempContent(string.Format("Bake All ({0} skins)", skeletonData.Skins.Count), prefabIcon))) {
SkeletonBaker.BakeToPrefab(skeletonDataAsset, skeletonData.Skins, "", bakeAnimations, bakeIK, bakeEventOptions);
}
} else {
if (SpineInspectorUtility.LargeCenteredButton(SpineInspectorUtility.TempContent("Bake Skeleton", prefabIcon))) {
SkeletonBaker.BakeToPrefab(skeletonDataAsset, new ExposedList<Skin>(new[] { bakeSkin }), "", bakeAnimations, bakeIK, bakeEventOptions);
}
}
}
}
}

View File

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

View File

@@ -1,592 +0,0 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
// With contributions from: Mitch Thompson
#if UNITY_2018_3 || UNITY_2019 || UNITY_2018_3_OR_NEWER
#define NEW_PREFAB_SYSTEM
#else
#define NO_PREFAB_MESH
#endif
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using UnityEditor.AnimatedValues;
namespace Spine.Unity.Editor {
using Editor = UnityEditor.Editor;
using Icons = SpineEditorUtilities.Icons;
public class SkeletonDebugWindow : EditorWindow {
const bool IsUtilityWindow = true;
internal static bool showBoneNames, showPaths = true, showShapes = true, showConstraints = true;
[MenuItem("CONTEXT/SkeletonRenderer/Open Skeleton Debug Window", false, 5000)]
public static void Init () {
var window = EditorWindow.GetWindow<SkeletonDebugWindow>(IsUtilityWindow);
window.minSize = new Vector2(330f, 360f);
window.maxSize = new Vector2(600f, 4000f);
window.titleContent = new GUIContent("Skeleton Debug", Icons.spine);
window.Show();
window.OnSelectionChange();
}
static AnimBool showSkeleton = new AnimBool(true);
static AnimBool showSlotsTree = new AnimBool(false);
static AnimBool showConstraintsTree = new AnimBool(false);
static AnimBool showDrawOrderTree = new AnimBool(false);
static AnimBool showEventDataTree = new AnimBool(false);
static AnimBool showDataTree = new AnimBool(false);
static AnimBool showInspectBoneTree = new AnimBool(false);
Vector2 scrollPos;
GUIContent SlotsRootLabel, SkeletonRootLabel;
GUIStyle BoldFoldoutStyle;
public SkeletonRenderer skeletonRenderer;
Skeleton skeleton;
Skin activeSkin;
bool isPrefab;
SerializedProperty bpo;
Bone bone;
[SpineBone(dataField:"skeletonRenderer")]
public string boneName;
readonly Dictionary<Slot, List<Skin.SkinEntry>> attachmentTable = new Dictionary<Slot, List<Skin.SkinEntry>>();
static bool staticLostValues = true;
void OnSceneGUI (SceneView sceneView) {
if (skeleton == null || skeletonRenderer == null || !skeletonRenderer.valid || isPrefab)
return;
var transform = skeletonRenderer.transform;
if (showPaths) SpineHandles.DrawPaths(transform, skeleton);
if (showConstraints) SpineHandles.DrawConstraints(transform, skeleton);
if (showBoneNames) SpineHandles.DrawBoneNames(transform, skeleton);
if (showShapes) SpineHandles.DrawBoundingBoxes(transform, skeleton);
if (bone != null) {
SpineHandles.DrawBone(skeletonRenderer.transform, bone, 1.5f, Color.cyan);
Handles.Label(bone.GetWorldPosition(skeletonRenderer.transform) + (Vector3.down * 0.15f), bone.Data.Name, SpineHandles.BoneNameStyle);
}
}
void OnSelectionChange () {
#if UNITY_2019_1_OR_NEWER
SceneView.duringSceneGui -= this.OnSceneGUI;
SceneView.duringSceneGui += this.OnSceneGUI;
#else
SceneView.onSceneGUIDelegate -= this.OnSceneGUI;
SceneView.onSceneGUIDelegate += this.OnSceneGUI;
#endif
bool noSkeletonRenderer = false;
var selectedObject = Selection.activeGameObject;
if (selectedObject == null) {
noSkeletonRenderer = true;
} else {
var selectedSkeletonRenderer = selectedObject.GetComponent<SkeletonRenderer>();
if (selectedSkeletonRenderer == null) {
noSkeletonRenderer = true;
} else if (skeletonRenderer != selectedSkeletonRenderer) {
bone = null;
if (skeletonRenderer != null && skeletonRenderer.SkeletonDataAsset != selectedSkeletonRenderer.SkeletonDataAsset)
boneName = null;
skeletonRenderer = selectedSkeletonRenderer;
skeletonRenderer.Initialize(false);
skeletonRenderer.LateUpdate();
skeleton = skeletonRenderer.skeleton;
#if NEW_PREFAB_SYSTEM
isPrefab = false;
#else
isPrefab |= PrefabUtility.GetPrefabType(selectedObject) == PrefabType.Prefab;
#endif
UpdateAttachments();
}
}
if (noSkeletonRenderer) Clear();
Repaint();
}
void Clear () {
skeletonRenderer = null;
skeleton = null;
attachmentTable.Clear();
isPrefab = false;
boneName = string.Empty;
bone = null;
#if UNITY_2019_1_OR_NEWER
SceneView.duringSceneGui -= this.OnSceneGUI;
#else
SceneView.onSceneGUIDelegate -= this.OnSceneGUI;
#endif
}
void OnDestroy () {
Clear();
}
static void FalseDropDown (string label, string stringValue, Texture2D icon = null, bool disabledGroup = false) {
if (disabledGroup) EditorGUI.BeginDisabledGroup(true);
var pos = EditorGUILayout.GetControlRect(true);
pos = EditorGUI.PrefixLabel(pos, SpineInspectorUtility.TempContent(label));
GUI.Button(pos, SpineInspectorUtility.TempContent(stringValue, icon), EditorStyles.popup);
if (disabledGroup) EditorGUI.EndDisabledGroup();
}
// Window GUI
void OnGUI () {
bool requireRepaint = false;
if (staticLostValues) {
Clear();
OnSelectionChange();
staticLostValues = false;
requireRepaint = true;
}
if (SlotsRootLabel == null) {
SlotsRootLabel = new GUIContent("Slots", Icons.slotRoot);
SkeletonRootLabel = new GUIContent("Skeleton", Icons.skeleton);
BoldFoldoutStyle = new GUIStyle(EditorStyles.foldout);
BoldFoldoutStyle.fontStyle = FontStyle.Bold;
BoldFoldoutStyle.stretchWidth = true;
BoldFoldoutStyle.fixedWidth = 0;
}
EditorGUILayout.Space();
EditorGUI.BeginDisabledGroup(true);
EditorGUILayout.ObjectField(SpineInspectorUtility.TempContent("Debug Selection", Icons.spine), skeletonRenderer, typeof(SkeletonRenderer), true);
EditorGUI.EndDisabledGroup();
if (skeleton == null || skeletonRenderer == null) {
EditorGUILayout.HelpBox("No SkeletonRenderer Spine GameObject selected.", MessageType.Info);
return;
}
if (isPrefab) {
EditorGUILayout.HelpBox("SkeletonDebug only debugs Spine GameObjects in the scene.", MessageType.Warning);
return;
}
if (!skeletonRenderer.valid) {
EditorGUILayout.HelpBox("Spine Component is invalid. Check SkeletonData Asset.", MessageType.Error);
return;
}
if (activeSkin != skeleton.Skin)
UpdateAttachments();
scrollPos = EditorGUILayout.BeginScrollView(scrollPos);
using (new SpineInspectorUtility.BoxScope(false)) {
if (SpineInspectorUtility.CenteredButton(SpineInspectorUtility.TempContent("Skeleton.SetToSetupPose()"))) {
skeleton.SetToSetupPose();
requireRepaint = true;
}
EditorGUI.BeginChangeCheck();
EditorGUILayout.LabelField("Scene View", EditorStyles.boldLabel);
using (new SpineInspectorUtility.LabelWidthScope()) {
showBoneNames = EditorGUILayout.Toggle("Show Bone Names", showBoneNames);
showPaths = EditorGUILayout.Toggle("Show Paths", showPaths);
showShapes = EditorGUILayout.Toggle("Show Shapes", showShapes);
showConstraints = EditorGUILayout.Toggle("Show Constraints", showConstraints);
}
requireRepaint |= EditorGUI.EndChangeCheck();
// Skeleton
showSkeleton.target = EditorGUILayout.Foldout(showSkeleton.target, SkeletonRootLabel, BoldFoldoutStyle);
if (showSkeleton.faded > 0) {
using (new SpineInspectorUtility.IndentScope()) {
using (new EditorGUILayout.FadeGroupScope(showSkeleton.faded)) {
EditorGUI.BeginChangeCheck();
EditorGUI.BeginDisabledGroup(true);
FalseDropDown(".Skin", skeleton.Skin != null ? skeletonRenderer.Skeleton.Skin.Name : "<None>", Icons.skin);
EditorGUI.EndDisabledGroup();
// Flip
skeleton.ScaleX = EditorGUILayout.DelayedFloatField(".ScaleX", skeleton.ScaleX);
skeleton.ScaleY = EditorGUILayout.DelayedFloatField(".ScaleY", skeleton.ScaleY);
//EditorGUILayout.BeginHorizontal(GUILayout.MaxWidth(160f));
////EditorGUILayout.LabelField("Scale", GUILayout.Width(EditorGUIUtility.labelWidth - 20f));
//GUILayout.EndHorizontal();
// Color
skeleton.SetColor(EditorGUILayout.ColorField(".R .G .B .A", skeleton.GetColor()));
requireRepaint |= EditorGUI.EndChangeCheck();
}
}
}
// Bone
showInspectBoneTree.target = EditorGUILayout.Foldout(showInspectBoneTree.target, SpineInspectorUtility.TempContent("Bone", Icons.bone), BoldFoldoutStyle);
if (showInspectBoneTree.faded > 0) {
using (new SpineInspectorUtility.IndentScope()) {
using (new EditorGUILayout.FadeGroupScope(showInspectBoneTree.faded)) {
showBoneNames = EditorGUILayout.Toggle("Show Bone Names", showBoneNames);
if (bpo == null) bpo = new SerializedObject(this).FindProperty("boneName");
EditorGUILayout.PropertyField(bpo, SpineInspectorUtility.TempContent("Bone"));
if (!string.IsNullOrEmpty(bpo.stringValue)) {
if (bone == null || bone.Data.Name != bpo.stringValue) {
bone = skeleton.FindBone(bpo.stringValue);
}
if (bone != null) {
using (new EditorGUI.DisabledGroupScope(true)) {
var wm = EditorGUIUtility.wideMode;
EditorGUIUtility.wideMode = true;
EditorGUILayout.Slider("Local Rotation", ViewRound(bone.Rotation), -180f, 180f);
EditorGUILayout.Vector2Field("Local Position", RoundVector2(bone.X, bone.Y));
EditorGUILayout.Vector2Field("Local Scale", RoundVector2(bone.ScaleX, bone.ScaleY));
EditorGUILayout.Vector2Field("Local Shear", RoundVector2(bone.ShearX, bone.ShearY));
EditorGUILayout.Space();
var boneParent = bone.Parent;
if (boneParent != null) FalseDropDown("Parent", boneParent.Data.Name, Icons.bone);
const string RoundFormat = "0.##";
var lw = EditorGUIUtility.labelWidth;
var fw = EditorGUIUtility.fieldWidth;
EditorGUIUtility.labelWidth *= 0.25f;
EditorGUIUtility.fieldWidth *= 0.5f;
EditorGUILayout.LabelField("LocalToWorld");
EditorGUILayout.BeginHorizontal();
EditorGUILayout.Space();
EditorGUILayout.TextField(".A", bone.A.ToString(RoundFormat));
EditorGUILayout.TextField(".B", bone.B.ToString(RoundFormat));
EditorGUILayout.EndHorizontal();
EditorGUILayout.BeginHorizontal();
EditorGUILayout.Space();
EditorGUILayout.TextField(".C", bone.C.ToString(RoundFormat));
EditorGUILayout.TextField(".D", bone.D.ToString(RoundFormat));
EditorGUILayout.EndHorizontal();
EditorGUIUtility.labelWidth = lw * 0.5f;
EditorGUILayout.BeginHorizontal();
EditorGUILayout.Space();
EditorGUILayout.Space();
EditorGUILayout.TextField(".WorldX", bone.WorldX.ToString(RoundFormat));
EditorGUILayout.TextField(".WorldY", bone.WorldY.ToString(RoundFormat));
EditorGUILayout.EndHorizontal();
EditorGUIUtility.labelWidth = lw;
EditorGUIUtility.fieldWidth = fw;
EditorGUIUtility.wideMode = wm;
}
}
requireRepaint = true;
} else {
bone = null;
}
}
}
}
// Slots
int preSlotsIndent = EditorGUI.indentLevel;
showSlotsTree.target = EditorGUILayout.Foldout(showSlotsTree.target, SlotsRootLabel, BoldFoldoutStyle);
if (showSlotsTree.faded > 0) {
using (new EditorGUILayout.FadeGroupScope(showSlotsTree.faded)) {
if (SpineInspectorUtility.CenteredButton(SpineInspectorUtility.TempContent("Skeleton.SetSlotsToSetupPose()"))) {
skeleton.SetSlotsToSetupPose();
requireRepaint = true;
}
int baseIndent = EditorGUI.indentLevel;
foreach (KeyValuePair<Slot, List<Skin.SkinEntry>> pair in attachmentTable) {
Slot slot = pair.Key;
using (new EditorGUILayout.HorizontalScope()) {
EditorGUI.indentLevel = baseIndent + 1;
EditorGUILayout.LabelField(SpineInspectorUtility.TempContent(slot.Data.Name, Icons.slot), GUILayout.ExpandWidth(false));
EditorGUI.BeginChangeCheck();
Color c = EditorGUILayout.ColorField(new Color(slot.R, slot.G, slot.B, slot.A), GUILayout.Width(60));
if (EditorGUI.EndChangeCheck()) {
slot.SetColor(c);
requireRepaint = true;
}
}
foreach (var skinEntry in pair.Value) {
var attachment = skinEntry.Attachment;
GUI.contentColor = slot.Attachment == attachment ? Color.white : Color.grey;
EditorGUI.indentLevel = baseIndent + 2;
var icon = Icons.GetAttachmentIcon(attachment);
bool isAttached = (attachment == slot.Attachment);
bool swap = EditorGUILayout.ToggleLeft(SpineInspectorUtility.TempContent(attachment.Name, icon), attachment == slot.Attachment);
if (isAttached != swap) {
slot.Attachment = isAttached ? null : attachment;
requireRepaint = true;
}
GUI.contentColor = Color.white;
}
}
}
}
EditorGUI.indentLevel = preSlotsIndent;
// Constraints
const string NoneText = "<none>";
showConstraintsTree.target = EditorGUILayout.Foldout(showConstraintsTree.target, SpineInspectorUtility.TempContent("Constraints", Icons.constraintRoot), BoldFoldoutStyle);
if (showConstraintsTree.faded > 0) {
using (new SpineInspectorUtility.IndentScope()) {
using (new EditorGUILayout.FadeGroupScope(showConstraintsTree.faded)) {
const float MixMin = 0f;
const float MixMax = 1f;
EditorGUI.BeginChangeCheck();
showConstraints = EditorGUILayout.Toggle("Show Constraints", showConstraints);
requireRepaint |= EditorGUI.EndChangeCheck();
EditorGUILayout.Space();
EditorGUILayout.LabelField(SpineInspectorUtility.TempContent(string.Format("IK Constraints ({0})", skeleton.IkConstraints.Count), Icons.constraintIK), EditorStyles.boldLabel);
using (new SpineInspectorUtility.IndentScope()) {
if (skeleton.IkConstraints.Count > 0) {
foreach (var c in skeleton.IkConstraints) {
EditorGUILayout.LabelField(SpineInspectorUtility.TempContent(c.Data.Name, Icons.constraintIK));
FalseDropDown("Goal", c.Data.Target.Name, Icons.bone, true);
using (new EditorGUI.DisabledGroupScope(true)) {
EditorGUILayout.Toggle(SpineInspectorUtility.TempContent("Data.Uniform", tooltip: "Uniformly scales a bone when Ik stretches or compresses."), c.Data.Uniform);
}
EditorGUI.BeginChangeCheck();
c.Mix = EditorGUILayout.Slider("Mix", c.Mix, MixMin, MixMax);
c.BendDirection = EditorGUILayout.Toggle(SpineInspectorUtility.TempContent("Bend Clockwise", tooltip: "IkConstraint.BendDirection == 1 if clockwise; -1 if counterclockwise."), c.BendDirection > 0) ? 1 : -1;
c.Compress = EditorGUILayout.Toggle(SpineInspectorUtility.TempContent("Compress", tooltip: "Compress single bone IK when the target too close. Not applied when parent bone has nonuniform scale."), c.Compress);
c.Stretch = EditorGUILayout.Toggle(SpineInspectorUtility.TempContent("Stretch", tooltip: "Stretch the parent bone when the target is out of range. Not applied when parent bone has nonuniform scale."), c.Stretch);
if (EditorGUI.EndChangeCheck()) requireRepaint = true;
EditorGUILayout.Space();
}
} else {
EditorGUILayout.LabelField(NoneText);
}
}
EditorGUILayout.LabelField(SpineInspectorUtility.TempContent(string.Format("Transform Constraints ({0})", skeleton.TransformConstraints.Count), Icons.constraintTransform), EditorStyles.boldLabel);
using (new SpineInspectorUtility.IndentScope()) {
if (skeleton.TransformConstraints.Count > 0) {
foreach (var c in skeleton.TransformConstraints) {
EditorGUILayout.LabelField(SpineInspectorUtility.TempContent(c.Data.Name, Icons.constraintTransform));
EditorGUI.BeginDisabledGroup(true);
FalseDropDown("Goal", c.Data.Target.Name, Icons.bone);
EditorGUI.EndDisabledGroup();
EditorGUI.BeginChangeCheck();
c.TranslateMix = EditorGUILayout.Slider("TranslateMix", c.TranslateMix, MixMin, MixMax);
c.RotateMix = EditorGUILayout.Slider("RotateMix", c.RotateMix, MixMin, MixMax);
c.ScaleMix = EditorGUILayout.Slider("ScaleMix", c.ScaleMix, MixMin, MixMax);
c.ShearMix = EditorGUILayout.Slider("ShearMix", c.ShearMix, MixMin, MixMax);
if (EditorGUI.EndChangeCheck()) requireRepaint = true;
EditorGUILayout.Space();
}
} else {
EditorGUILayout.LabelField(NoneText);
}
}
EditorGUILayout.LabelField(SpineInspectorUtility.TempContent(string.Format("Path Constraints ({0})", skeleton.PathConstraints.Count), Icons.constraintPath), EditorStyles.boldLabel);
EditorGUI.BeginChangeCheck();
showPaths = EditorGUILayout.Toggle("Show Paths", showPaths);
requireRepaint |= EditorGUI.EndChangeCheck();
using (new SpineInspectorUtility.IndentScope()) {
if (skeleton.PathConstraints.Count > 0) {
foreach (var c in skeleton.PathConstraints) {
EditorGUILayout.LabelField(SpineInspectorUtility.TempContent(c.Data.Name, Icons.constraintPath));
EditorGUI.BeginDisabledGroup(true);
FalseDropDown("Path Slot", c.Data.Target.Name, Icons.slot);
var activeAttachment = c.Target.Attachment;
FalseDropDown("Active Path", activeAttachment != null ? activeAttachment.Name : "<None>", activeAttachment is PathAttachment ? Icons.path : null);
EditorGUILayout.LabelField("PositionMode." + c.Data.PositionMode);
EditorGUILayout.LabelField("SpacingMode." + c.Data.SpacingMode);
EditorGUILayout.LabelField("RotateMode." + c.Data.RotateMode);
EditorGUI.EndDisabledGroup();
EditorGUI.BeginChangeCheck();
c.RotateMix = EditorGUILayout.Slider("RotateMix", c.RotateMix, MixMin, MixMax);
c.TranslateMix = EditorGUILayout.Slider("TranslateMix", c.TranslateMix, MixMin, MixMax);
c.Position = EditorGUILayout.FloatField("Position", c.Position);
c.Spacing = EditorGUILayout.FloatField("Spacing", c.Spacing);
if (EditorGUI.EndChangeCheck()) requireRepaint = true;
EditorGUILayout.Space();
}
} else {
EditorGUILayout.LabelField(NoneText);
}
}
}
}
}
showDrawOrderTree.target = EditorGUILayout.Foldout(showDrawOrderTree.target, SpineInspectorUtility.TempContent("Draw Order and Separators", Icons.slotRoot), BoldFoldoutStyle);
//var separatorSlotNamesField =
//SpineInspectorUtility.ge
if (showDrawOrderTree.faded > 0) {
using (new SpineInspectorUtility.IndentScope()) {
using (new EditorGUILayout.FadeGroupScope(showDrawOrderTree.faded)) {
const string SeparatorString = "------------- v SEPARATOR v -------------";
if (Application.isPlaying) {
foreach (var slot in skeleton.DrawOrder) {
if (skeletonRenderer.separatorSlots.Contains(slot)) EditorGUILayout.LabelField(SeparatorString);
using (new EditorGUI.DisabledScope(!slot.Bone.Active)) {
EditorGUILayout.LabelField(SpineInspectorUtility.TempContent(slot.Data.Name, Icons.slot), GUILayout.ExpandWidth(false));
}
}
} else {
foreach (var slot in skeleton.DrawOrder) {
var slotNames = SkeletonRendererInspector.GetSeparatorSlotNames(skeletonRenderer);
for (int i = 0, n = slotNames.Length; i < n; i++) {
if (string.Equals(slotNames[i], slot.Data.Name, System.StringComparison.Ordinal)) {
EditorGUILayout.LabelField(SeparatorString);
break;
}
}
using (new EditorGUI.DisabledScope(!slot.Bone.Active)) {
EditorGUILayout.LabelField(SpineInspectorUtility.TempContent(slot.Data.Name, Icons.slot), GUILayout.ExpandWidth(false));
}
}
}
}
}
}
showEventDataTree.target = EditorGUILayout.Foldout(showEventDataTree.target, SpineInspectorUtility.TempContent("Events", Icons.userEvent), BoldFoldoutStyle);
if (showEventDataTree.faded > 0) {
using (new SpineInspectorUtility.IndentScope()) {
using (new EditorGUILayout.FadeGroupScope(showEventDataTree.faded)) {
if (skeleton.Data.Events.Count > 0) {
foreach (var e in skeleton.Data.Events) {
EditorGUILayout.LabelField(SpineInspectorUtility.TempContent(e.Name, Icons.userEvent));
}
} else {
EditorGUILayout.LabelField(NoneText);
}
}
}
}
showDataTree.target = EditorGUILayout.Foldout(showDataTree.target, SpineInspectorUtility.TempContent("Data Counts", Icons.spine), BoldFoldoutStyle);
if (showDataTree.faded > 0) {
using (new SpineInspectorUtility.IndentScope()) {
using (new EditorGUILayout.FadeGroupScope(showDataTree.faded)) {
using (new SpineInspectorUtility.LabelWidthScope()) {
var skeletonData = skeleton.Data;
EditorGUILayout.LabelField(SpineInspectorUtility.TempContent("Bones", Icons.bone, "Skeleton.Data.Bones"), new GUIContent(skeletonData.Bones.Count.ToString()));
EditorGUILayout.LabelField(SpineInspectorUtility.TempContent("Slots", Icons.slotRoot, "Skeleton.Data.Slots"), new GUIContent(skeletonData.Slots.Count.ToString()));
EditorGUILayout.LabelField(SpineInspectorUtility.TempContent("Skins", Icons.skinsRoot, "Skeleton.Data.Skins"), new GUIContent(skeletonData.Skins.Count.ToString()));
EditorGUILayout.LabelField(SpineInspectorUtility.TempContent("Events", Icons.userEvent, "Skeleton.Data.Events"), new GUIContent(skeletonData.Events.Count.ToString()));
EditorGUILayout.LabelField(SpineInspectorUtility.TempContent("IK Constraints", Icons.constraintIK, "Skeleton.Data.IkConstraints"), new GUIContent(skeletonData.IkConstraints.Count.ToString()));
EditorGUILayout.LabelField(SpineInspectorUtility.TempContent("Transform Constraints", Icons.constraintTransform, "Skeleton.Data.TransformConstraints"), new GUIContent(skeletonData.TransformConstraints.Count.ToString()));
EditorGUILayout.LabelField(SpineInspectorUtility.TempContent("Path Constraints", Icons.constraintPath, "Skeleton.Data.PathConstraints"), new GUIContent(skeletonData.PathConstraints.Count.ToString()));
}
}
}
}
if (IsAnimating(showSlotsTree, showSkeleton, showConstraintsTree, showDrawOrderTree, showEventDataTree, showInspectBoneTree, showDataTree))
Repaint();
}
if (requireRepaint) {
skeletonRenderer.LateUpdate();
Repaint();
SceneView.RepaintAll();
}
EditorGUILayout.EndScrollView();
}
static float ViewRound (float x) {
const float Factor = 100f;
const float Divisor = 1f/Factor;
return Mathf.Round(x * Factor) * Divisor;
}
static Vector2 RoundVector2 (float x, float y) {
const float Factor = 100f;
const float Divisor = 1f/Factor;
return new Vector2(Mathf.Round(x * Factor) * Divisor, Mathf.Round(y * Factor) * Divisor);
}
static bool IsAnimating (params AnimBool[] animBools) {
foreach (var a in animBools)
if (a.isAnimating) return true;
return false;
}
void UpdateAttachments () {
//skeleton = skeletonRenderer.skeleton;
Skin defaultSkin = skeleton.Data.DefaultSkin;
Skin skin = skeleton.Skin ?? defaultSkin;
bool notDefaultSkin = skin != defaultSkin;
attachmentTable.Clear();
for (int i = skeleton.Slots.Count - 1; i >= 0; i--) {
var attachments = new List<Skin.SkinEntry>();
attachmentTable.Add(skeleton.Slots.Items[i], attachments);
// Add skin attachments.
skin.GetAttachments(i, attachments);
if (notDefaultSkin && defaultSkin != null) // Add default skin attachments.
defaultSkin.GetAttachments(i, attachments);
}
activeSkin = skeleton.Skin;
}
}
}

View File

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

View File

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

View File

@@ -1,58 +0,0 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
using UnityEngine;
using UnityEditor;
using Spine.Unity.Editor;
namespace Spine.Unity.Examples {
[CustomEditor(typeof(SkeletonPartsRenderer))]
public class SkeletonRenderPartInspector : UnityEditor.Editor {
SpineInspectorUtility.SerializedSortingProperties sortingProperties;
void OnEnable () {
sortingProperties = new SpineInspectorUtility.SerializedSortingProperties(SpineInspectorUtility.GetRenderersSerializedObject(serializedObject));
}
public override void OnInspectorGUI () {
SpineInspectorUtility.SortingPropertyFields(sortingProperties, true);
if (!serializedObject.isEditingMultipleObjects) {
EditorGUILayout.Space();
if (SpineInspectorUtility.LargeCenteredButton(new GUIContent("Select SkeletonRenderer", SpineEditorUtilities.Icons.spine))) {
var thisSkeletonPartsRenderer = target as SkeletonPartsRenderer;
var srs = thisSkeletonPartsRenderer.GetComponentInParent<SkeletonRenderSeparator>();
if (srs != null && srs.partsRenderers.Contains(thisSkeletonPartsRenderer) && srs.SkeletonRenderer != null)
Selection.activeGameObject = srs.SkeletonRenderer.gameObject;
}
}
}
}
}

View File

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

View File

@@ -1,323 +0,0 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#if UNITY_2018_3 || UNITY_2019 || UNITY_2018_3_OR_NEWER
#define NEW_PREFAB_SYSTEM
#endif
using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
using Spine.Unity;
using Spine.Unity.Editor;
namespace Spine.Unity.Examples {
[CustomEditor(typeof(SkeletonRenderSeparator))]
public class SkeletonRenderSeparatorInspector : UnityEditor.Editor {
SkeletonRenderSeparator component;
// Properties
SerializedProperty skeletonRenderer_, copyPropertyBlock_, copyMeshRendererFlags_, partsRenderers_;
static bool partsRenderersExpanded = false;
// For separator field.
SerializedObject skeletonRendererSerializedObject;
SerializedProperty separatorNamesProp;
static bool skeletonRendererExpanded = true;
bool slotsReapplyRequired = false;
bool partsRendererInitRequired = false;
void OnEnable () {
if (component == null)
component = target as SkeletonRenderSeparator;
skeletonRenderer_ = serializedObject.FindProperty("skeletonRenderer");
copyPropertyBlock_ = serializedObject.FindProperty("copyPropertyBlock");
copyMeshRendererFlags_ = serializedObject.FindProperty("copyMeshRendererFlags");
var partsRenderers = component.partsRenderers;
partsRenderers_ = serializedObject.FindProperty("partsRenderers");
partsRenderers_.isExpanded = partsRenderersExpanded || // last state
partsRenderers.Contains(null) || // null items found
partsRenderers.Count < 1 || // no parts renderers
(skeletonRenderer_.objectReferenceValue != null && SkeletonRendererSeparatorCount + 1 > partsRenderers.Count); // not enough parts renderers
}
int SkeletonRendererSeparatorCount {
get {
if (Application.isPlaying)
return component.SkeletonRenderer.separatorSlots.Count;
else
return separatorNamesProp == null ? 0 : separatorNamesProp.arraySize;
}
}
public override void OnInspectorGUI () {
// Restore mesh part for undo logic after undo of "Add Parts Renderer".
// Triggers regeneration and assignment of the mesh filter's mesh.
bool isMeshFilterAlwaysNull = false;
#if UNITY_EDITOR && NEW_PREFAB_SYSTEM
// Don't store mesh or material at the prefab, otherwise it will permanently reload
var prefabType = UnityEditor.PrefabUtility.GetPrefabAssetType(component);
if (UnityEditor.PrefabUtility.IsPartOfPrefabAsset(component) &&
(prefabType == UnityEditor.PrefabAssetType.Regular || prefabType == UnityEditor.PrefabAssetType.Variant)) {
isMeshFilterAlwaysNull = true;
}
#endif
if (!isMeshFilterAlwaysNull && component.GetComponent<MeshFilter>() && component.GetComponent<MeshFilter>().sharedMesh == null) {
component.OnDisable();
component.OnEnable();
}
var componentRenderers = component.partsRenderers;
int totalParts;
using (new SpineInspectorUtility.LabelWidthScope()) {
bool componentEnabled = component.enabled;
bool checkBox = EditorGUILayout.Toggle("Enable Separator", componentEnabled);
if (checkBox != componentEnabled)
component.enabled = checkBox;
if (component.SkeletonRenderer.disableRenderingOnOverride && !component.enabled)
EditorGUILayout.HelpBox("By default, SkeletonRenderer's MeshRenderer is disabled while the SkeletonRenderSeparator takes over rendering. It is re-enabled when SkeletonRenderSeparator is disabled.", MessageType.Info);
EditorGUILayout.PropertyField(copyPropertyBlock_);
EditorGUILayout.PropertyField(copyMeshRendererFlags_);
}
// SkeletonRenderer Box
using (new SpineInspectorUtility.BoxScope(false)) {
// Fancy SkeletonRenderer foldout reference field
{
EditorGUI.indentLevel++;
EditorGUI.BeginChangeCheck();
var foldoutSkeletonRendererRect = EditorGUILayout.GetControlRect(true, EditorGUIUtility.singleLineHeight);
EditorGUI.PropertyField(foldoutSkeletonRendererRect, skeletonRenderer_);
if (EditorGUI.EndChangeCheck())
serializedObject.ApplyModifiedProperties();
if (component.SkeletonRenderer != null) {
skeletonRendererExpanded = EditorGUI.Foldout(foldoutSkeletonRendererRect, skeletonRendererExpanded, "");
}
EditorGUI.indentLevel--;
}
int separatorCount = 0;
EditorGUI.BeginChangeCheck();
if (component.SkeletonRenderer != null) {
// Separators from SkeletonRenderer
{
bool skeletonRendererMismatch = skeletonRendererSerializedObject != null && skeletonRendererSerializedObject.targetObject != component.SkeletonRenderer;
if (separatorNamesProp == null || skeletonRendererMismatch) {
if (component.SkeletonRenderer != null) {
skeletonRendererSerializedObject = new SerializedObject(component.SkeletonRenderer);
separatorNamesProp = skeletonRendererSerializedObject.FindProperty("separatorSlotNames");
separatorNamesProp.isExpanded = true;
}
}
if (separatorNamesProp != null) {
if (skeletonRendererExpanded) {
EditorGUI.indentLevel++;
SkeletonRendererInspector.SeparatorsField(separatorNamesProp);
EditorGUI.indentLevel--;
}
separatorCount = this.SkeletonRendererSeparatorCount;
}
}
if (SkeletonRendererSeparatorCount == 0) {
EditorGUILayout.HelpBox("Separators are empty. Change the size to 1 and choose a slot if you want the render to be separated.", MessageType.Info);
}
}
if (EditorGUI.EndChangeCheck()) {
skeletonRendererSerializedObject.ApplyModifiedProperties();
if (!Application.isPlaying)
slotsReapplyRequired = true;
}
totalParts = separatorCount + 1;
var counterStyle = skeletonRendererExpanded ? EditorStyles.label : EditorStyles.miniLabel;
EditorGUILayout.LabelField(string.Format("{0}: separates into {1}.", SpineInspectorUtility.Pluralize(separatorCount, "separator", "separators"), SpineInspectorUtility.Pluralize(totalParts, "part", "parts") ), counterStyle);
}
// Parts renderers
using (new SpineInspectorUtility.BoxScope(false)) {
EditorGUI.indentLevel++;
EditorGUILayout.PropertyField(this.partsRenderers_, true);
EditorGUI.indentLevel--;
// Null items warning
bool nullItemsFound = componentRenderers.Contains(null);
if (nullItemsFound)
EditorGUILayout.HelpBox("Some items in the parts renderers list are null and may cause problems.\n\nYou can right-click on that element and choose 'Delete Array Element' to remove it.", MessageType.Warning);
// (Button) Match Separators count
if (separatorNamesProp != null) {
int currentRenderers = 0;
foreach (var r in componentRenderers) {
if (r != null)
currentRenderers++;
}
int extraRenderersNeeded = totalParts - currentRenderers;
if (component.enabled && component.SkeletonRenderer != null && extraRenderersNeeded > 0) {
EditorGUILayout.HelpBox(string.Format("Insufficient parts renderers. Some parts will not be rendered."), MessageType.Warning);
string addMissingLabel = string.Format("Add the missing renderer{1} ({0}) ", extraRenderersNeeded, SpineInspectorUtility.PluralThenS(extraRenderersNeeded));
if (GUILayout.Button(addMissingLabel, GUILayout.Height(30f))) {
AddPartsRenderer(extraRenderersNeeded);
DetectOrphanedPartsRenderers(component);
partsRendererInitRequired = true;
}
}
}
if (partsRenderers_.isExpanded != partsRenderersExpanded) partsRenderersExpanded = partsRenderers_.isExpanded;
if (partsRenderers_.isExpanded) {
using (new EditorGUILayout.HorizontalScope()) {
// (Button) Destroy Renderers button
if (componentRenderers.Count > 0) {
if (GUILayout.Button("Clear Parts Renderers")) {
// Do you really want to destroy all?
Undo.RegisterCompleteObjectUndo(component, "Clear Parts Renderers");
if (EditorUtility.DisplayDialog("Destroy Renderers", "Do you really want to destroy all the Parts Renderer GameObjects in the list?", "Destroy", "Cancel")) {
foreach (var r in componentRenderers) {
if (r != null)
Undo.DestroyObjectImmediate(r.gameObject);
}
componentRenderers.Clear();
// Do you also want to destroy orphans? (You monster.)
DetectOrphanedPartsRenderers(component);
}
}
}
// (Button) Add Part Renderer button
if (GUILayout.Button("Add Parts Renderer")) {
AddPartsRenderer(1);
partsRendererInitRequired = true;
}
}
}
}
serializedObject.ApplyModifiedProperties();
if (partsRendererInitRequired) {
Undo.RegisterCompleteObjectUndo(component.GetComponent<MeshRenderer>(), "Add Parts Renderers");
component.OnEnable();
partsRendererInitRequired = false;
}
if (slotsReapplyRequired && UnityEngine.Event.current.type == EventType.Repaint) {
component.SkeletonRenderer.ReapplySeparatorSlotNames();
component.SkeletonRenderer.LateUpdate();
SceneView.RepaintAll();
slotsReapplyRequired = false;
}
}
public void AddPartsRenderer (int count) {
var componentRenderers = component.partsRenderers;
bool emptyFound = componentRenderers.Contains(null);
if (emptyFound) {
bool userClearEntries = EditorUtility.DisplayDialog("Empty entries found", "Null entries found. Do you want to remove null entries before adding the new renderer? ", "Clear Empty Entries", "Don't Clear");
if (userClearEntries) componentRenderers.RemoveAll(x => x == null);
}
Undo.RegisterCompleteObjectUndo(component, "Add Parts Renderers");
for (int i = 0; i < count; i++) {
int index = componentRenderers.Count;
var smr = SkeletonPartsRenderer.NewPartsRendererGameObject(component.transform, index.ToString());
Undo.RegisterCreatedObjectUndo(smr.gameObject, "New Parts Renderer GameObject.");
componentRenderers.Add(smr);
// increment renderer sorting order.
if (index == 0) continue;
var prev = componentRenderers[index - 1]; if (prev == null) continue;
var prevMeshRenderer = prev.GetComponent<MeshRenderer>();
var currentMeshRenderer = smr.GetComponent<MeshRenderer>();
if (prevMeshRenderer == null || currentMeshRenderer == null) continue;
int prevSortingLayer = prevMeshRenderer.sortingLayerID;
int prevSortingOrder = prevMeshRenderer.sortingOrder;
currentMeshRenderer.sortingLayerID = prevSortingLayer;
currentMeshRenderer.sortingOrder = prevSortingOrder + SkeletonRenderSeparator.DefaultSortingOrderIncrement;
}
}
/// <summary>Detects orphaned parts renderers and offers to delete them.</summary>
public void DetectOrphanedPartsRenderers (SkeletonRenderSeparator component) {
var children = component.GetComponentsInChildren<SkeletonPartsRenderer>();
var orphans = new System.Collections.Generic.List<SkeletonPartsRenderer>();
foreach (var r in children) {
if (!component.partsRenderers.Contains(r))
orphans.Add(r);
}
if (orphans.Count > 0) {
if (EditorUtility.DisplayDialog("Destroy Submesh Renderers", "Unassigned renderers were found. Do you want to delete them? (These may belong to another Render Separator in the same hierarchy. If you don't have another Render Separator component in the children of this GameObject, it's likely safe to delete. Warning: This operation cannot be undone.)", "Delete", "Cancel")) {
foreach (var o in orphans) {
Undo.DestroyObjectImmediate(o.gameObject);
}
}
}
}
#region SkeletonRenderer Context Menu Item
[MenuItem ("CONTEXT/SkeletonRenderer/Add Skeleton Render Separator")]
static void AddRenderSeparatorComponent (MenuCommand cmd) {
var skeletonRenderer = cmd.context as SkeletonRenderer;
var newComponent = skeletonRenderer.gameObject.AddComponent<SkeletonRenderSeparator>();
Undo.RegisterCreatedObjectUndo(newComponent, "Add SkeletonRenderSeparator");
}
// Validate
[MenuItem ("CONTEXT/SkeletonRenderer/Add Skeleton Render Separator", true)]
static bool ValidateAddRenderSeparatorComponent (MenuCommand cmd) {
var skeletonRenderer = cmd.context as SkeletonRenderer;
var separator = skeletonRenderer.GetComponent<SkeletonRenderSeparator>();
bool separatorNotOnObject = separator == null;
return separatorNotOnObject;
}
#endregion
}
}

View File

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

View File

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

View File

@@ -1,47 +0,0 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
using UnityEngine;
using UnityEditor;
using Spine.Unity.Deprecated;
using System;
namespace Spine.Unity.Editor {
using Editor = UnityEditor.Editor;
[Obsolete("The spine-unity 3.7 runtime introduced SkeletonDataModifierAssets BlendModeMaterials which replaced SlotBlendModes. Will be removed in spine-unity 3.9.", false)]
public class SlotBlendModesEditor : Editor {
[MenuItem("CONTEXT/SkeletonRenderer/Add Slot Blend Modes Component")]
static void AddSlotBlendModesComponent (MenuCommand command) {
var skeletonRenderer = (SkeletonRenderer)command.context;
skeletonRenderer.gameObject.AddComponent<SlotBlendModes>();
}
}
}

View File

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

View File

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

View File

@@ -1,635 +0,0 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
using System;
using System.Collections.Generic;
namespace Spine {
public class Skeleton {
internal SkeletonData data;
internal ExposedList<Bone> bones;
internal ExposedList<Slot> slots;
internal ExposedList<Slot> drawOrder;
internal ExposedList<IkConstraint> ikConstraints;
internal ExposedList<TransformConstraint> transformConstraints;
internal ExposedList<PathConstraint> pathConstraints;
internal ExposedList<IUpdatable> updateCache = new ExposedList<IUpdatable>();
internal ExposedList<Bone> updateCacheReset = new ExposedList<Bone>();
internal Skin skin;
internal float r = 1, g = 1, b = 1, a = 1;
internal float time;
private float scaleX = 1, scaleY = 1;
internal float x, y;
public SkeletonData Data { get { return data; } }
public ExposedList<Bone> Bones { get { return bones; } }
public ExposedList<IUpdatable> UpdateCacheList { get { return updateCache; } }
public ExposedList<Slot> Slots { get { return slots; } }
public ExposedList<Slot> DrawOrder { get { return drawOrder; } }
public ExposedList<IkConstraint> IkConstraints { get { return ikConstraints; } }
public ExposedList<PathConstraint> PathConstraints { get { return pathConstraints; } }
public ExposedList<TransformConstraint> TransformConstraints { get { return transformConstraints; } }
public Skin Skin { get { return skin; } set { SetSkin(value); } }
public float R { get { return r; } set { r = value; } }
public float G { get { return g; } set { g = value; } }
public float B { get { return b; } set { b = value; } }
public float A { get { return a; } set { a = value; } }
public float Time { get { return time; } set { time = value; } }
public float X { get { return x; } set { x = value; } }
public float Y { get { return y; } set { y = value; } }
public float ScaleX { get { return scaleX; } set { scaleX = value; } }
public float ScaleY { get { return scaleY * (Bone.yDown ? -1 : 1); } set { scaleY = value; } }
[Obsolete("Use ScaleX instead. FlipX is when ScaleX is negative.")]
public bool FlipX { get { return scaleX < 0; } set { scaleX = value ? -1f : 1f; } }
[Obsolete("Use ScaleY instead. FlipY is when ScaleY is negative.")]
public bool FlipY { get { return scaleY < 0; } set { scaleY = value ? -1f : 1f; } }
public Bone RootBone {
get { return bones.Count == 0 ? null : bones.Items[0]; }
}
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);
foreach (BoneData boneData in data.bones) {
Bone bone;
if (boneData.parent == null) {
bone = new Bone(boneData, this, null);
} else {
Bone parent = bones.Items[boneData.parent.index];
bone = new Bone(boneData, this, parent);
parent.children.Add(bone);
}
bones.Add(bone);
}
slots = new ExposedList<Slot>(data.slots.Count);
drawOrder = new ExposedList<Slot>(data.slots.Count);
foreach (SlotData slotData in data.slots) {
Bone bone = bones.Items[slotData.boneData.index];
Slot slot = new Slot(slotData, bone);
slots.Add(slot);
drawOrder.Add(slot);
}
ikConstraints = new ExposedList<IkConstraint>(data.ikConstraints.Count);
foreach (IkConstraintData ikConstraintData in data.ikConstraints)
ikConstraints.Add(new IkConstraint(ikConstraintData, this));
transformConstraints = new ExposedList<TransformConstraint>(data.transformConstraints.Count);
foreach (TransformConstraintData transformConstraintData in data.transformConstraints)
transformConstraints.Add(new TransformConstraint(transformConstraintData, this));
pathConstraints = new ExposedList<PathConstraint> (data.pathConstraints.Count);
foreach (PathConstraintData pathConstraintData in data.pathConstraints)
pathConstraints.Add(new PathConstraint(pathConstraintData, this));
UpdateCache();
UpdateWorldTransform();
}
/// <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 () {
var updateCache = this.updateCache;
updateCache.Clear();
this.updateCacheReset.Clear();
int boneCount = this.bones.Items.Length;
var bones = this.bones;
for (int i = 0; i < boneCount; i++) {
Bone bone = bones.Items[i];
bone.sorted = bone.data.skinRequired;
bone.active = !bone.sorted;
}
if (skin != null) {
Object[] skinBones = skin.bones.Items;
for (int i = 0, n = skin.bones.Count; i < n; i++) {
Bone bone = (Bone)bones.Items[((BoneData)skinBones[i]).index];
do {
bone.sorted = false;
bone.active = true;
bone = bone.parent;
} while (bone != null);
}
}
int ikCount = this.ikConstraints.Count, transformCount = this.transformConstraints.Count, pathCount = this.pathConstraints.Count;
var ikConstraints = this.ikConstraints;
var transformConstraints = this.transformConstraints;
var pathConstraints = this.pathConstraints;
int constraintCount = ikCount + transformCount + pathCount;
//outer:
for (int i = 0; i < constraintCount; i++) {
for (int ii = 0; ii < ikCount; ii++) {
IkConstraint constraint = ikConstraints.Items[ii];
if (constraint.data.order == i) {
SortIkConstraint(constraint);
goto continue_outer; //continue outer;
}
}
for (int ii = 0; ii < transformCount; ii++) {
TransformConstraint constraint = transformConstraints.Items[ii];
if (constraint.data.order == i) {
SortTransformConstraint(constraint);
goto continue_outer; //continue outer;
}
}
for (int ii = 0; ii < pathCount; ii++) {
PathConstraint constraint = pathConstraints.Items[ii];
if (constraint.data.order == i) {
SortPathConstraint(constraint);
goto continue_outer; //continue outer;
}
}
continue_outer: {}
}
for (int i = 0; i < boneCount; i++)
SortBone(bones.Items[i]);
}
private void SortIkConstraint (IkConstraint constraint) {
constraint.active = constraint.target.active
&& (!constraint.data.skinRequired || (skin != null && skin.constraints.Contains(constraint.data)));
if (!constraint.active) return;
Bone target = constraint.target;
SortBone(target);
var constrained = constraint.bones;
Bone parent = constrained.Items[0];
SortBone(parent);
if (constrained.Count > 1) {
Bone child = constrained.Items[constrained.Count - 1];
if (!updateCache.Contains(child))
updateCacheReset.Add(child);
}
updateCache.Add(constraint);
SortReset(parent.children);
constrained.Items[constrained.Count - 1].sorted = true;
}
private void SortPathConstraint (PathConstraint constraint) {
constraint.active = constraint.target.bone.active
&& (!constraint.data.skinRequired || (skin != null && skin.constraints.Contains(constraint.data)));
if (!constraint.active) return;
Slot slot = constraint.target;
int slotIndex = slot.data.index;
Bone slotBone = slot.bone;
if (skin != null) SortPathConstraintAttachment(skin, slotIndex, slotBone);
if (data.defaultSkin != null && data.defaultSkin != skin)
SortPathConstraintAttachment(data.defaultSkin, slotIndex, slotBone);
Attachment attachment = slot.attachment;
if (attachment is PathAttachment) SortPathConstraintAttachment(attachment, slotBone);
var constrained = constraint.bones;
int boneCount = constrained.Count;
for (int i = 0; i < boneCount; i++)
SortBone(constrained.Items[i]);
updateCache.Add(constraint);
for (int i = 0; i < boneCount; i++)
SortReset(constrained.Items[i].children);
for (int i = 0; i < boneCount; i++)
constrained.Items[i].sorted = true;
}
private void SortTransformConstraint (TransformConstraint constraint) {
constraint.active = constraint.target.active
&& (!constraint.data.skinRequired || (skin != null && skin.constraints.Contains(constraint.data)));
if (!constraint.active) return;
SortBone(constraint.target);
var constrained = constraint.bones;
int boneCount = constrained.Count;
if (constraint.data.local) {
for (int i = 0; i < boneCount; i++) {
Bone child = constrained.Items[i];
SortBone(child.parent);
if (!updateCache.Contains(child)) updateCacheReset.Add(child);
}
} else {
for (int i = 0; i < boneCount; i++)
SortBone(constrained.Items[i]);
}
updateCache.Add(constraint);
for (int i = 0; i < boneCount; i++)
SortReset(constrained.Items[i].children);
for (int i = 0; i < boneCount; i++)
constrained.Items[i].sorted = true;
}
private void SortPathConstraintAttachment (Skin skin, int slotIndex, Bone slotBone) {
foreach (var entryObj in skin.Attachments.Keys) {
var entry = (Skin.SkinEntry)entryObj;
if (entry.SlotIndex == slotIndex) SortPathConstraintAttachment(entry.Attachment, slotBone);
}
}
private void SortPathConstraintAttachment (Attachment attachment, Bone slotBone) {
if (!(attachment is PathAttachment)) return;
int[] pathBones = ((PathAttachment)attachment).bones;
if (pathBones == null)
SortBone(slotBone);
else {
var bones = this.bones;
for (int i = 0, n = pathBones.Length; i < n;) {
int nn = pathBones[i++];
nn += i;
while (i < nn)
SortBone(bones.Items[pathBones[i++]]);
}
}
}
private void SortBone (Bone bone) {
if (bone.sorted) return;
Bone parent = bone.parent;
if (parent != null) SortBone(parent);
bone.sorted = true;
updateCache.Add(bone);
}
private static void SortReset (ExposedList<Bone> bones) {
var bonesItems = bones.Items;
for (int i = 0, n = bones.Count; i < n; i++) {
Bone bone = bonesItems[i];
if (!bone.active) continue;
if (bone.sorted) SortReset(bone.children);
bone.sorted = false;
}
}
/// <summary>Updates the world transform for each bone and applies constraints.</summary>
public void UpdateWorldTransform () {
var updateCacheReset = this.updateCacheReset;
var updateCacheResetItems = updateCacheReset.Items;
for (int i = 0, n = updateCacheReset.Count; i < n; i++) {
Bone bone = updateCacheResetItems[i];
bone.ax = bone.x;
bone.ay = bone.y;
bone.arotation = bone.rotation;
bone.ascaleX = bone.scaleX;
bone.ascaleY = bone.scaleY;
bone.ashearX = bone.shearX;
bone.ashearY = bone.shearY;
bone.appliedValid = true;
}
var updateItems = this.updateCache.Items;
for (int i = 0, n = updateCache.Count; i < n; i++)
updateItems[i].Update();
}
/// <summary>
/// Temporarily sets the root bone as a child of the specified bone, then updates the world transform for each bone and applies
/// all constraints.
/// </summary>
public void UpdateWorldTransform (Bone parent) {
// This partial update avoids computing the world transform for constrained bones when 1) the bone is not updated
// before the constraint, 2) the constraint only needs to access the applied local transform, and 3) the constraint calls
// updateWorldTransform.
var updateCacheReset = this.updateCacheReset;
var updateCacheResetItems = updateCacheReset.Items;
for (int i = 0, n = updateCacheReset.Count; i < n; i++) {
Bone bone = updateCacheResetItems[i];
bone.ax = bone.x;
bone.ay = bone.y;
bone.arotation = bone.rotation;
bone.ascaleX = bone.scaleX;
bone.ascaleY = bone.scaleY;
bone.ashearX = bone.shearX;
bone.ashearY = bone.shearY;
bone.appliedValid = true;
}
// Apply the parent bone transform to the root bone. The root bone always inherits scale, rotation and reflection.
Bone rootBone = this.RootBone;
float pa = parent.a, pb = parent.b, pc = parent.c, pd = parent.d;
rootBone.worldX = pa * x + pb * y + parent.worldX;
rootBone.worldY = pc * x + pd * y + parent.worldY;
float rotationY = rootBone.rotation + 90 + rootBone.shearY;
float la = MathUtils.CosDeg(rootBone.rotation + rootBone.shearX) * rootBone.scaleX;
float lb = MathUtils.CosDeg(rotationY) * rootBone.scaleY;
float lc = MathUtils.SinDeg(rootBone.rotation + rootBone.shearX) * rootBone.scaleX;
float ld = MathUtils.SinDeg(rotationY) * rootBone.scaleY;
rootBone.a = (pa * la + pb * lc) * scaleX;
rootBone.b = (pa * lb + pb * ld) * scaleX;
rootBone.c = (pc * la + pd * lc) * scaleY;
rootBone.d = (pc * lb + pd * ld) * scaleY;
// Update everything except root bone.
var updateCache = this.updateCache;
var updateCacheItems = updateCache.Items;
for (int i = 0, n = updateCache.Count; i < n; i++) {
var updatable = updateCacheItems[i];
if (updatable != rootBone)
updatable.Update();
}
}
/// <summary>Sets the bones, constraints, and slots to their setup pose values.</summary>
public void SetToSetupPose () {
SetBonesToSetupPose();
SetSlotsToSetupPose();
}
/// <summary>Sets the bones and constraints to their setup pose values.</summary>
public void SetBonesToSetupPose () {
var bonesItems = this.bones.Items;
for (int i = 0, n = bones.Count; i < n; i++)
bonesItems[i].SetToSetupPose();
var ikConstraintsItems = this.ikConstraints.Items;
for (int i = 0, n = ikConstraints.Count; i < n; i++) {
IkConstraint constraint = ikConstraintsItems[i];
constraint.mix = constraint.data.mix;
constraint.softness = constraint.data.softness;
constraint.bendDirection = constraint.data.bendDirection;
constraint.compress = constraint.data.compress;
constraint.stretch = constraint.data.stretch;
}
var transformConstraintsItems = this.transformConstraints.Items;
for (int i = 0, n = transformConstraints.Count; i < n; i++) {
TransformConstraint constraint = transformConstraintsItems[i];
TransformConstraintData constraintData = constraint.data;
constraint.rotateMix = constraintData.rotateMix;
constraint.translateMix = constraintData.translateMix;
constraint.scaleMix = constraintData.scaleMix;
constraint.shearMix = constraintData.shearMix;
}
var pathConstraintItems = this.pathConstraints.Items;
for (int i = 0, n = pathConstraints.Count; i < n; i++) {
PathConstraint constraint = pathConstraintItems[i];
PathConstraintData constraintData = constraint.data;
constraint.position = constraintData.position;
constraint.spacing = constraintData.spacing;
constraint.rotateMix = constraintData.rotateMix;
constraint.translateMix = constraintData.translateMix;
}
}
public void SetSlotsToSetupPose () {
var slots = this.slots;
var slotsItems = slots.Items;
drawOrder.Clear();
for (int i = 0, n = slots.Count; i < n; i++)
drawOrder.Add(slotsItems[i]);
for (int i = 0, n = slots.Count; i < n; i++)
slotsItems[i].SetToSetupPose();
}
/// <returns>May be null.</returns>
public Bone FindBone (string boneName) {
if (boneName == null) throw new ArgumentNullException("boneName", "boneName cannot be null.");
var bones = this.bones;
var bonesItems = bones.Items;
for (int i = 0, n = bones.Count; i < n; i++) {
Bone bone = bonesItems[i];
if (bone.data.name == boneName) return bone;
}
return null;
}
/// <returns>-1 if the bone was not found.</returns>
public int FindBoneIndex (string boneName) {
if (boneName == null) throw new ArgumentNullException("boneName", "boneName cannot be null.");
var bones = this.bones;
var bonesItems = bones.Items;
for (int i = 0, n = bones.Count; i < n; i++)
if (bonesItems[i].data.name == boneName) return i;
return -1;
}
/// <returns>May be null.</returns>
public Slot FindSlot (string slotName) {
if (slotName == null) throw new ArgumentNullException("slotName", "slotName cannot be null.");
var slots = this.slots;
var slotsItems = slots.Items;
for (int i = 0, n = slots.Count; i < n; i++) {
Slot slot = slotsItems[i];
if (slot.data.name == slotName) return slot;
}
return null;
}
/// <returns>-1 if the bone was not found.</returns>
public int FindSlotIndex (string slotName) {
if (slotName == null) throw new ArgumentNullException("slotName", "slotName cannot be null.");
var slots = this.slots;
var slotsItems = slots.Items;
for (int i = 0, n = slots.Count; i < n; i++)
if (slotsItems[i].data.name.Equals(slotName)) return i;
return -1;
}
/// <summary>Sets a skin by name (see SetSkin).</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 the <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.SetSlotsToSetupPose()"/>.
/// 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 {
ExposedList<Slot> slots = this.slots;
for (int i = 0, n = slots.Count; i < n; i++) {
Slot slot = slots.Items[i];
string name = slot.data.attachmentName;
if (name != null) {
Attachment attachment = newSkin.GetAttachment(i, name);
if (attachment != null) slot.Attachment = attachment;
}
}
}
}
skin = newSkin;
UpdateCache();
}
/// <summary>Finds an attachment by looking in the {@link #skin} and {@link SkeletonData#defaultSkin} using the slot name and attachment name.</summary>
/// <returns>May be null.</returns>
public Attachment GetAttachment (string slotName, string attachmentName) {
return GetAttachment(data.FindSlotIndex(slotName), attachmentName);
}
/// <summary>Finds an attachment by looking in the skin and skeletonData.defaultSkin using the slot index and attachment name.First the skin is checked and if the attachment was not found, the default skin is checked.</summary>
/// <returns>May be null.</returns>
public Attachment GetAttachment (int slotIndex, string attachmentName) {
if (attachmentName == null) throw new ArgumentNullException("attachmentName", "attachmentName cannot be null.");
if (skin != null) {
Attachment attachment = skin.GetAttachment(slotIndex, attachmentName);
if (attachment != null) return attachment;
}
return data.defaultSkin != null ? data.defaultSkin.GetAttachment(slotIndex, attachmentName) : null;
}
/// <summary>A convenience method to set an attachment by finding the slot with FindSlot, finding the attachment with GetAttachment, then setting the slot's slot.Attachment.</summary>
/// <param name="attachmentName">May be null to clear the slot's attachment.</param>
public void SetAttachment (string slotName, string attachmentName) {
if (slotName == null) throw new ArgumentNullException("slotName", "slotName cannot be null.");
ExposedList<Slot> slots = this.slots;
for (int i = 0, n = slots.Count; i < n; i++) {
Slot slot = slots.Items[i];
if (slot.data.name == slotName) {
Attachment attachment = null;
if (attachmentName != null) {
attachment = GetAttachment(i, attachmentName);
if (attachment == null) throw new Exception("Attachment not found: " + attachmentName + ", for slot: " + slotName);
}
slot.Attachment = attachment;
return;
}
}
throw new Exception("Slot not found: " + slotName);
}
/// <returns>May be null.</returns>
public IkConstraint FindIkConstraint (string constraintName) {
if (constraintName == null) throw new ArgumentNullException("constraintName", "constraintName cannot be null.");
ExposedList<IkConstraint> ikConstraints = this.ikConstraints;
for (int i = 0, n = ikConstraints.Count; i < n; i++) {
IkConstraint ikConstraint = ikConstraints.Items[i];
if (ikConstraint.data.name == constraintName) return ikConstraint;
}
return null;
}
/// <returns>May be null.</returns>
public TransformConstraint FindTransformConstraint (string constraintName) {
if (constraintName == null) throw new ArgumentNullException("constraintName", "constraintName cannot be null.");
ExposedList<TransformConstraint> transformConstraints = this.transformConstraints;
for (int i = 0, n = transformConstraints.Count; i < n; i++) {
TransformConstraint transformConstraint = transformConstraints.Items[i];
if (transformConstraint.data.Name == constraintName) return transformConstraint;
}
return null;
}
/// <returns>May be null.</returns>
public PathConstraint FindPathConstraint (string constraintName) {
if (constraintName == null) throw new ArgumentNullException("constraintName", "constraintName cannot be null.");
ExposedList<PathConstraint> pathConstraints = this.pathConstraints;
for (int i = 0, n = pathConstraints.Count; i < n; i++) {
PathConstraint constraint = pathConstraints.Items[i];
if (constraint.data.Name.Equals(constraintName)) return constraint;
}
return null;
}
public void Update (float delta) {
time += delta;
}
/// <summary>Returns the axis aligned bounding box (AABB) of the region and mesh attachments for the current 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) {
float[] temp = vertexBuffer;
temp = temp ?? new float[8];
var drawOrderItems = this.drawOrder.Items;
float minX = int.MaxValue, minY = int.MaxValue, maxX = int.MinValue, maxY = int.MinValue;
for (int i = 0, n = drawOrderItems.Length; i < n; i++) {
Slot slot = drawOrderItems[i];
if (!slot.bone.active) continue;
int verticesLength = 0;
float[] vertices = null;
Attachment attachment = slot.attachment;
var regionAttachment = attachment as RegionAttachment;
if (regionAttachment != null) {
verticesLength = 8;
vertices = temp;
if (vertices.Length < 8) vertices = temp = new float[8];
regionAttachment.ComputeWorldVertices(slot.bone, temp, 0);
} else {
var meshAttachment = attachment as MeshAttachment;
if (meshAttachment != null) {
MeshAttachment mesh = meshAttachment;
verticesLength = mesh.WorldVerticesLength;
vertices = temp;
if (vertices.Length < verticesLength) vertices = temp = new float[verticesLength];
mesh.ComputeWorldVertices(slot, 0, verticesLength, temp, 0);
}
}
if (vertices != null) {
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);
}
}
}
x = minX;
y = minY;
width = maxX - minX;
height = maxY - minY;
vertexBuffer = temp;
}
}
}

View File

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

View File

@@ -1,997 +0,0 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#if (UNITY_5 || UNITY_5_3_OR_NEWER || UNITY_WSA || UNITY_WP8 || UNITY_WP8_1)
#define IS_UNITY
#endif
using System;
using System.IO;
using System.Collections.Generic;
#if WINDOWS_STOREAPP
using System.Threading.Tasks;
using Windows.Storage;
#endif
namespace Spine {
public class SkeletonBinary {
public const int BONE_ROTATE = 0;
public const int BONE_TRANSLATE = 1;
public const int BONE_SCALE = 2;
public const int BONE_SHEAR = 3;
public const int SLOT_ATTACHMENT = 0;
public const int SLOT_COLOR = 1;
public const int SLOT_TWO_COLOR = 2;
public const int PATH_POSITION = 0;
public const int PATH_SPACING = 1;
public const int PATH_MIX = 2;
public const int CURVE_LINEAR = 0;
public const int CURVE_STEPPED = 1;
public const int CURVE_BEZIER = 2;
public float Scale { get; set; }
private AttachmentLoader attachmentLoader;
private List<SkeletonJson.LinkedMesh> linkedMeshes = new List<SkeletonJson.LinkedMesh>();
public SkeletonBinary (params Atlas[] atlasArray)
: this(new AtlasAttachmentLoader(atlasArray)) {
}
public SkeletonBinary (AttachmentLoader attachmentLoader) {
if (attachmentLoader == null) throw new ArgumentNullException("attachmentLoader");
this.attachmentLoader = attachmentLoader;
Scale = 1;
}
#if !ISUNITY && WINDOWS_STOREAPP
private async Task<SkeletonData> ReadFile(string path) {
var folder = Windows.ApplicationModel.Package.Current.InstalledLocation;
using (var input = new BufferedStream(await folder.GetFileAsync(path).AsTask().ConfigureAwait(false))) {
SkeletonData skeletonData = ReadSkeletonData(input);
skeletonData.Name = Path.GetFileNameWithoutExtension(path);
return skeletonData;
}
}
public SkeletonData ReadSkeletonData (String path) {
return this.ReadFile(path).Result;
}
#else
public SkeletonData ReadSkeletonData (String path) {
#if WINDOWS_PHONE
using (var input = new BufferedStream(Microsoft.Xna.Framework.TitleContainer.OpenStream(path))) {
#else
using (var input = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read)) {
#endif
SkeletonData skeletonData = ReadSkeletonData(input);
skeletonData.name = Path.GetFileNameWithoutExtension(path);
return skeletonData;
}
}
#endif // WINDOWS_STOREAPP
public static readonly TransformMode[] TransformModeValues = {
TransformMode.Normal,
TransformMode.OnlyTranslation,
TransformMode.NoRotationOrReflection,
TransformMode.NoScale,
TransformMode.NoScaleOrReflection
};
/// <summary>Returns the version string of binary skeleton data.</summary>
public static string GetVersionString (Stream file) {
if (file == null) throw new ArgumentNullException("file");
SkeletonInput input = new SkeletonInput(file);
return input.GetVersionString();
}
public SkeletonData ReadSkeletonData (Stream file) {
if (file == null) throw new ArgumentNullException("file");
float scale = Scale;
var skeletonData = new SkeletonData();
SkeletonInput input = new SkeletonInput(file);
skeletonData.hash = input.ReadString();
if (skeletonData.hash.Length == 0) skeletonData.hash = null;
skeletonData.version = input.ReadString();
if (skeletonData.version.Length == 0) skeletonData.version = null;
if ("3.8.75" == skeletonData.version)
throw new Exception("Unsupported skeleton data, please export with a newer version of Spine.");
skeletonData.x = input.ReadFloat();
skeletonData.y = input.ReadFloat();
skeletonData.width = input.ReadFloat();
skeletonData.height = input.ReadFloat();
bool nonessential = input.ReadBoolean();
if (nonessential) {
skeletonData.fps = input.ReadFloat();
skeletonData.imagesPath = input.ReadString();
if (string.IsNullOrEmpty(skeletonData.imagesPath)) skeletonData.imagesPath = null;
skeletonData.audioPath = input.ReadString();
if (string.IsNullOrEmpty(skeletonData.audioPath)) skeletonData.audioPath = null;
}
int n;
Object[] o;
// Strings.
input.strings = new ExposedList<string>(n = input.ReadInt(true));
o = input.strings.Resize(n).Items;
for (int i = 0; i < n; i++)
o[i] = input.ReadString();
// Bones.
o = skeletonData.bones.Resize(n = input.ReadInt(true)).Items;
for (int i = 0; i < n; i++) {
String name = input.ReadString();
BoneData parent = i == 0 ? null : skeletonData.bones.Items[input.ReadInt(true)];
BoneData data = new BoneData(i, name, parent);
data.rotation = input.ReadFloat();
data.x = input.ReadFloat() * scale;
data.y = input.ReadFloat() * scale;
data.scaleX = input.ReadFloat();
data.scaleY = input.ReadFloat();
data.shearX = input.ReadFloat();
data.shearY = input.ReadFloat();
data.length = input.ReadFloat() * scale;
data.transformMode = TransformModeValues[input.ReadInt(true)];
data.skinRequired = input.ReadBoolean();
if (nonessential) input.ReadInt(); // Skip bone color.
o[i] = data;
}
// Slots.
o = skeletonData.slots.Resize(n = input.ReadInt(true)).Items;
for (int i = 0; i < n; i++) {
String slotName = input.ReadString();
BoneData boneData = skeletonData.bones.Items[input.ReadInt(true)];
SlotData slotData = new SlotData(i, slotName, boneData);
int color = input.ReadInt();
slotData.r = ((color & 0xff000000) >> 24) / 255f;
slotData.g = ((color & 0x00ff0000) >> 16) / 255f;
slotData.b = ((color & 0x0000ff00) >> 8) / 255f;
slotData.a = ((color & 0x000000ff)) / 255f;
int darkColor = input.ReadInt(); // 0x00rrggbb
if (darkColor != -1) {
slotData.hasSecondColor = true;
slotData.r2 = ((darkColor & 0x00ff0000) >> 16) / 255f;
slotData.g2 = ((darkColor & 0x0000ff00) >> 8) / 255f;
slotData.b2 = ((darkColor & 0x000000ff)) / 255f;
}
slotData.attachmentName = input.ReadStringRef();
slotData.blendMode = (BlendMode)input.ReadInt(true);
o[i] = slotData;
}
// IK constraints.
o = skeletonData.ikConstraints.Resize(n = input.ReadInt(true)).Items;
for (int i = 0, nn; i < n; i++) {
IkConstraintData data = new IkConstraintData(input.ReadString());
data.order = input.ReadInt(true);
data.skinRequired = input.ReadBoolean();
Object[] bones = data.bones.Resize(nn = input.ReadInt(true)).Items;
for (int ii = 0; ii < nn; ii++)
bones[ii] = skeletonData.bones.Items[input.ReadInt(true)];
data.target = skeletonData.bones.Items[input.ReadInt(true)];
data.mix = input.ReadFloat();
data.softness = input.ReadFloat() * scale;
data.bendDirection = input.ReadSByte();
data.compress = input.ReadBoolean();
data.stretch = input.ReadBoolean();
data.uniform = input.ReadBoolean();
o[i] = data;
}
// Transform constraints.
o = skeletonData.transformConstraints.Resize(n = input.ReadInt(true)).Items;
for (int i = 0, nn; i < n; i++) {
TransformConstraintData data = new TransformConstraintData(input.ReadString());
data.order = input.ReadInt(true);
data.skinRequired = input.ReadBoolean();
Object[] bones = data.bones.Resize(nn = input.ReadInt(true)).Items;
for (int ii = 0; ii < nn; ii++)
bones[ii] = skeletonData.bones.Items[input.ReadInt(true)];
data.target = skeletonData.bones.Items[input.ReadInt(true)];
data.local = input.ReadBoolean();
data.relative = input.ReadBoolean();
data.offsetRotation = input.ReadFloat();
data.offsetX = input.ReadFloat() * scale;
data.offsetY = input.ReadFloat() * scale;
data.offsetScaleX = input.ReadFloat();
data.offsetScaleY = input.ReadFloat();
data.offsetShearY = input.ReadFloat();
data.rotateMix = input.ReadFloat();
data.translateMix = input.ReadFloat();
data.scaleMix = input.ReadFloat();
data.shearMix = input.ReadFloat();
o[i] = data;
}
// Path constraints
o = skeletonData.pathConstraints.Resize(n = input.ReadInt(true)).Items;
for (int i = 0, nn; i < n; i++) {
PathConstraintData data = new PathConstraintData(input.ReadString());
data.order = input.ReadInt(true);
data.skinRequired = input.ReadBoolean();
Object[] bones = data.bones.Resize(nn = input.ReadInt(true)).Items;
for (int ii = 0; ii < nn; ii++)
bones[ii] = skeletonData.bones.Items[input.ReadInt(true)];
data.target = skeletonData.slots.Items[input.ReadInt(true)];
data.positionMode = (PositionMode)Enum.GetValues(typeof(PositionMode)).GetValue(input.ReadInt(true));
data.spacingMode = (SpacingMode)Enum.GetValues(typeof(SpacingMode)).GetValue(input.ReadInt(true));
data.rotateMode = (RotateMode)Enum.GetValues(typeof(RotateMode)).GetValue(input.ReadInt(true));
data.offsetRotation = input.ReadFloat();
data.position = input.ReadFloat();
if (data.positionMode == PositionMode.Fixed) data.position *= scale;
data.spacing = input.ReadFloat();
if (data.spacingMode == SpacingMode.Length || data.spacingMode == SpacingMode.Fixed) data.spacing *= scale;
data.rotateMix = input.ReadFloat();
data.translateMix = input.ReadFloat();
o[i] = data;
}
// Default skin.
Skin defaultSkin = ReadSkin(input, skeletonData, true, nonessential);
if (defaultSkin != null) {
skeletonData.defaultSkin = defaultSkin;
skeletonData.skins.Add(defaultSkin);
}
// Skins.
{
int i = skeletonData.skins.Count;
o = skeletonData.skins.Resize(n = i + input.ReadInt(true)).Items;
for (; i < n; i++)
o[i] = ReadSkin(input, skeletonData, false, nonessential);
}
// Linked meshes.
n = linkedMeshes.Count;
for (int i = 0; i < n; i++) {
SkeletonJson.LinkedMesh linkedMesh = linkedMeshes[i];
Skin skin = linkedMesh.skin == null ? skeletonData.DefaultSkin : skeletonData.FindSkin(linkedMesh.skin);
if (skin == null) throw new Exception("Skin not found: " + linkedMesh.skin);
Attachment parent = skin.GetAttachment(linkedMesh.slotIndex, linkedMesh.parent);
if (parent == null) throw new Exception("Parent mesh not found: " + linkedMesh.parent);
linkedMesh.mesh.DeformAttachment = linkedMesh.inheritDeform ? (VertexAttachment)parent : linkedMesh.mesh;
linkedMesh.mesh.ParentMesh = (MeshAttachment)parent;
linkedMesh.mesh.UpdateUVs();
}
linkedMeshes.Clear();
// Events.
o = skeletonData.events.Resize(n = input.ReadInt(true)).Items;
for (int i = 0; i < n; i++) {
EventData data = new EventData(input.ReadStringRef());
data.Int = input.ReadInt(false);
data.Float = input.ReadFloat();
data.String = input.ReadString();
data.AudioPath = input.ReadString();
if (data.AudioPath != null) {
data.Volume = input.ReadFloat();
data.Balance = input.ReadFloat();
}
o[i] = data;
}
// Animations.
o = skeletonData.animations.Resize(n = input.ReadInt(true)).Items;
for (int i = 0; i < n; i++)
o[i] = ReadAnimation(input.ReadString(), input, skeletonData);
return skeletonData;
}
/// <returns>May be null.</returns>
private Skin ReadSkin (SkeletonInput input, SkeletonData skeletonData, bool defaultSkin, bool nonessential) {
Skin skin;
int slotCount;
if (defaultSkin) {
slotCount = input.ReadInt(true);
if (slotCount == 0) return null;
skin = new Skin("default");
} else {
skin = new Skin(input.ReadStringRef());
Object[] bones = skin.bones.Resize(input.ReadInt(true)).Items;
for (int i = 0, n = skin.bones.Count; i < n; i++)
bones[i] = skeletonData.bones.Items[input.ReadInt(true)];
for (int i = 0, n = input.ReadInt(true); i < n; i++)
skin.constraints.Add(skeletonData.ikConstraints.Items[input.ReadInt(true)]);
for (int i = 0, n = input.ReadInt(true); i < n; i++)
skin.constraints.Add(skeletonData.transformConstraints.Items[input.ReadInt(true)]);
for (int i = 0, n = input.ReadInt(true); i < n; i++)
skin.constraints.Add(skeletonData.pathConstraints.Items[input.ReadInt(true)]);
skin.constraints.TrimExcess();
slotCount = input.ReadInt(true);
}
for (int i = 0; i < slotCount; i++) {
int slotIndex = input.ReadInt(true);
for (int ii = 0, nn = input.ReadInt(true); ii < nn; ii++) {
String name = input.ReadStringRef();
Attachment attachment = ReadAttachment(input, skeletonData, skin, slotIndex, name, nonessential);
if (attachment != null) skin.SetAttachment(slotIndex, name, attachment);
}
}
return skin;
}
private Attachment ReadAttachment (SkeletonInput input, SkeletonData skeletonData, Skin skin, int slotIndex,
String attachmentName, bool nonessential) {
float scale = Scale;
String name = input.ReadStringRef();
if (name == null) name = attachmentName;
AttachmentType type = (AttachmentType)input.ReadByte();
switch (type) {
case AttachmentType.Region: {
String path = input.ReadStringRef();
float rotation = input.ReadFloat();
float x = input.ReadFloat();
float y = input.ReadFloat();
float scaleX = input.ReadFloat();
float scaleY = input.ReadFloat();
float width = input.ReadFloat();
float height = input.ReadFloat();
int color = input.ReadInt();
if (path == null) path = name;
RegionAttachment region = attachmentLoader.NewRegionAttachment(skin, name, path);
if (region == null) return null;
region.Path = path;
region.x = x * scale;
region.y = y * scale;
region.scaleX = scaleX;
region.scaleY = scaleY;
region.rotation = rotation;
region.width = width * scale;
region.height = height * scale;
region.r = ((color & 0xff000000) >> 24) / 255f;
region.g = ((color & 0x00ff0000) >> 16) / 255f;
region.b = ((color & 0x0000ff00) >> 8) / 255f;
region.a = ((color & 0x000000ff)) / 255f;
region.UpdateOffset();
return region;
}
case AttachmentType.Boundingbox: {
int vertexCount = input.ReadInt(true);
Vertices vertices = ReadVertices(input, vertexCount);
if (nonessential) input.ReadInt(); //int color = nonessential ? input.ReadInt() : 0; // Avoid unused local warning.
BoundingBoxAttachment box = attachmentLoader.NewBoundingBoxAttachment(skin, name);
if (box == null) return null;
box.worldVerticesLength = vertexCount << 1;
box.vertices = vertices.vertices;
box.bones = vertices.bones;
// skipped porting: if (nonessential) Color.rgba8888ToColor(box.getColor(), color);
return box;
}
case AttachmentType.Mesh: {
String path = input.ReadStringRef();
int color = input.ReadInt();
int vertexCount = input.ReadInt(true);
float[] uvs = ReadFloatArray(input, vertexCount << 1, 1);
int[] triangles = ReadShortArray(input);
Vertices vertices = ReadVertices(input, vertexCount);
int hullLength = input.ReadInt(true);
int[] edges = null;
float width = 0, height = 0;
if (nonessential) {
edges = ReadShortArray(input);
width = input.ReadFloat();
height = input.ReadFloat();
}
if (path == null) path = name;
MeshAttachment mesh = attachmentLoader.NewMeshAttachment(skin, name, path);
if (mesh == null) return null;
mesh.Path = path;
mesh.r = ((color & 0xff000000) >> 24) / 255f;
mesh.g = ((color & 0x00ff0000) >> 16) / 255f;
mesh.b = ((color & 0x0000ff00) >> 8) / 255f;
mesh.a = ((color & 0x000000ff)) / 255f;
mesh.bones = vertices.bones;
mesh.vertices = vertices.vertices;
mesh.WorldVerticesLength = vertexCount << 1;
mesh.triangles = triangles;
mesh.regionUVs = uvs;
mesh.UpdateUVs();
mesh.HullLength = hullLength << 1;
if (nonessential) {
mesh.Edges = edges;
mesh.Width = width * scale;
mesh.Height = height * scale;
}
return mesh;
}
case AttachmentType.Linkedmesh: {
String path = input.ReadStringRef();
int color = input.ReadInt();
String skinName = input.ReadStringRef();
String parent = input.ReadStringRef();
bool inheritDeform = input.ReadBoolean();
float width = 0, height = 0;
if (nonessential) {
width = input.ReadFloat();
height = input.ReadFloat();
}
if (path == null) path = name;
MeshAttachment mesh = attachmentLoader.NewMeshAttachment(skin, name, path);
if (mesh == null) return null;
mesh.Path = path;
mesh.r = ((color & 0xff000000) >> 24) / 255f;
mesh.g = ((color & 0x00ff0000) >> 16) / 255f;
mesh.b = ((color & 0x0000ff00) >> 8) / 255f;
mesh.a = ((color & 0x000000ff)) / 255f;
if (nonessential) {
mesh.Width = width * scale;
mesh.Height = height * scale;
}
linkedMeshes.Add(new SkeletonJson.LinkedMesh(mesh, skinName, slotIndex, parent, inheritDeform));
return mesh;
}
case AttachmentType.Path: {
bool closed = input.ReadBoolean();
bool constantSpeed = input.ReadBoolean();
int vertexCount = input.ReadInt(true);
Vertices vertices = ReadVertices(input, vertexCount);
float[] lengths = new float[vertexCount / 3];
for (int i = 0, n = lengths.Length; i < n; i++)
lengths[i] = input.ReadFloat() * scale;
if (nonessential) input.ReadInt(); //int color = nonessential ? input.ReadInt() : 0;
PathAttachment path = attachmentLoader.NewPathAttachment(skin, name);
if (path == null) return null;
path.closed = closed;
path.constantSpeed = constantSpeed;
path.worldVerticesLength = vertexCount << 1;
path.vertices = vertices.vertices;
path.bones = vertices.bones;
path.lengths = lengths;
// skipped porting: if (nonessential) Color.rgba8888ToColor(path.getColor(), color);
return path;
}
case AttachmentType.Point: {
float rotation = input.ReadFloat();
float x = input.ReadFloat();
float y = input.ReadFloat();
if (nonessential) input.ReadInt(); //int color = nonessential ? input.ReadInt() : 0;
PointAttachment point = attachmentLoader.NewPointAttachment(skin, name);
if (point == null) return null;
point.x = x * scale;
point.y = y * scale;
point.rotation = rotation;
// skipped porting: if (nonessential) point.color = color;
return point;
}
case AttachmentType.Clipping: {
int endSlotIndex = input.ReadInt(true);
int vertexCount = input.ReadInt(true);
Vertices vertices = ReadVertices(input, vertexCount);
if (nonessential) input.ReadInt();
ClippingAttachment clip = attachmentLoader.NewClippingAttachment(skin, name);
if (clip == null) return null;
clip.EndSlot = skeletonData.slots.Items[endSlotIndex];
clip.worldVerticesLength = vertexCount << 1;
clip.vertices = vertices.vertices;
clip.bones = vertices.bones;
// skipped porting: if (nonessential) Color.rgba8888ToColor(clip.getColor(), color);
return clip;
}
}
return null;
}
private Vertices ReadVertices (SkeletonInput input, int vertexCount) {
float scale = Scale;
int verticesLength = vertexCount << 1;
Vertices vertices = new Vertices();
if(!input.ReadBoolean()) {
vertices.vertices = ReadFloatArray(input, verticesLength, scale);
return vertices;
}
var weights = new ExposedList<float>(verticesLength * 3 * 3);
var bonesArray = new ExposedList<int>(verticesLength * 3);
for (int i = 0; i < vertexCount; i++) {
int boneCount = input.ReadInt(true);
bonesArray.Add(boneCount);
for (int ii = 0; ii < boneCount; ii++) {
bonesArray.Add(input.ReadInt(true));
weights.Add(input.ReadFloat() * scale);
weights.Add(input.ReadFloat() * scale);
weights.Add(input.ReadFloat());
}
}
vertices.vertices = weights.ToArray();
vertices.bones = bonesArray.ToArray();
return vertices;
}
private float[] ReadFloatArray (SkeletonInput input, int n, float scale) {
float[] array = new float[n];
if (scale == 1) {
for (int i = 0; i < n; i++)
array[i] = input.ReadFloat();
} else {
for (int i = 0; i < n; i++)
array[i] = input.ReadFloat() * scale;
}
return array;
}
private int[] ReadShortArray (SkeletonInput input) {
int n = input.ReadInt(true);
int[] array = new int[n];
for (int i = 0; i < n; i++)
array[i] = (input.ReadByte() << 8) | input.ReadByte();
return array;
}
private Animation ReadAnimation (String name, SkeletonInput input, SkeletonData skeletonData) {
var timelines = new ExposedList<Timeline>(32);
float scale = Scale;
float duration = 0;
// Slot timelines.
for (int i = 0, n = input.ReadInt(true); i < n; i++) {
int slotIndex = input.ReadInt(true);
for (int ii = 0, nn = input.ReadInt(true); ii < nn; ii++) {
int timelineType = input.ReadByte();
int frameCount = input.ReadInt(true);
switch (timelineType) {
case SLOT_ATTACHMENT: {
AttachmentTimeline timeline = new AttachmentTimeline(frameCount);
timeline.slotIndex = slotIndex;
for (int frameIndex = 0; frameIndex < frameCount; frameIndex++)
timeline.SetFrame(frameIndex, input.ReadFloat(), input.ReadStringRef());
timelines.Add(timeline);
duration = Math.Max(duration, timeline.frames[frameCount - 1]);
break;
}
case SLOT_COLOR: {
ColorTimeline timeline = new ColorTimeline(frameCount);
timeline.slotIndex = slotIndex;
for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) {
float time = input.ReadFloat();
int color = input.ReadInt();
float r = ((color & 0xff000000) >> 24) / 255f;
float g = ((color & 0x00ff0000) >> 16) / 255f;
float b = ((color & 0x0000ff00) >> 8) / 255f;
float a = ((color & 0x000000ff)) / 255f;
timeline.SetFrame(frameIndex, time, r, g, b, a);
if (frameIndex < frameCount - 1) ReadCurve(input, frameIndex, timeline);
}
timelines.Add(timeline);
duration = Math.Max(duration, timeline.frames[(frameCount - 1) * ColorTimeline.ENTRIES]);
break;
}
case SLOT_TWO_COLOR: {
TwoColorTimeline timeline = new TwoColorTimeline(frameCount);
timeline.slotIndex = slotIndex;
for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) {
float time = input.ReadFloat();
int color = input.ReadInt();
float r = ((color & 0xff000000) >> 24) / 255f;
float g = ((color & 0x00ff0000) >> 16) / 255f;
float b = ((color & 0x0000ff00) >> 8) / 255f;
float a = ((color & 0x000000ff)) / 255f;
int color2 = input.ReadInt(); // 0x00rrggbb
float r2 = ((color2 & 0x00ff0000) >> 16) / 255f;
float g2 = ((color2 & 0x0000ff00) >> 8) / 255f;
float b2 = ((color2 & 0x000000ff)) / 255f;
timeline.SetFrame(frameIndex, time, r, g, b, a, r2, g2, b2);
if (frameIndex < frameCount - 1) ReadCurve(input, frameIndex, timeline);
}
timelines.Add(timeline);
duration = Math.Max(duration, timeline.frames[(frameCount - 1) * TwoColorTimeline.ENTRIES]);
break;
}
}
}
}
// Bone timelines.
for (int i = 0, n = input.ReadInt(true); i < n; i++) {
int boneIndex = input.ReadInt(true);
for (int ii = 0, nn = input.ReadInt(true); ii < nn; ii++) {
int timelineType = input.ReadByte();
int frameCount = input.ReadInt(true);
switch (timelineType) {
case BONE_ROTATE: {
RotateTimeline timeline = new RotateTimeline(frameCount);
timeline.boneIndex = boneIndex;
for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) {
timeline.SetFrame(frameIndex, input.ReadFloat(), input.ReadFloat());
if (frameIndex < frameCount - 1) ReadCurve(input, frameIndex, timeline);
}
timelines.Add(timeline);
duration = Math.Max(duration, timeline.frames[(frameCount - 1) * RotateTimeline.ENTRIES]);
break;
}
case BONE_TRANSLATE:
case BONE_SCALE:
case BONE_SHEAR: {
TranslateTimeline timeline;
float timelineScale = 1;
if (timelineType == BONE_SCALE)
timeline = new ScaleTimeline(frameCount);
else if (timelineType == BONE_SHEAR)
timeline = new ShearTimeline(frameCount);
else {
timeline = new TranslateTimeline(frameCount);
timelineScale = scale;
}
timeline.boneIndex = boneIndex;
for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) {
timeline.SetFrame(frameIndex, input.ReadFloat(), input.ReadFloat() * timelineScale,
input.ReadFloat() * timelineScale);
if (frameIndex < frameCount - 1) ReadCurve(input, frameIndex, timeline);
}
timelines.Add(timeline);
duration = Math.Max(duration, timeline.frames[(frameCount - 1) * TranslateTimeline.ENTRIES]);
break;
}
}
}
}
// IK constraint timelines.
for (int i = 0, n = input.ReadInt(true); i < n; i++) {
int index = input.ReadInt(true);
int frameCount = input.ReadInt(true);
IkConstraintTimeline timeline = new IkConstraintTimeline(frameCount) {
ikConstraintIndex = index
};
for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) {
timeline.SetFrame(frameIndex, input.ReadFloat(), input.ReadFloat(), input.ReadFloat() * scale, input.ReadSByte(), input.ReadBoolean(),
input.ReadBoolean());
if (frameIndex < frameCount - 1) ReadCurve(input, frameIndex, timeline);
}
timelines.Add(timeline);
duration = Math.Max(duration, timeline.frames[(frameCount - 1) * IkConstraintTimeline.ENTRIES]);
}
// Transform constraint timelines.
for (int i = 0, n = input.ReadInt(true); i < n; i++) {
int index = input.ReadInt(true);
int frameCount = input.ReadInt(true);
TransformConstraintTimeline timeline = new TransformConstraintTimeline(frameCount);
timeline.transformConstraintIndex = index;
for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) {
timeline.SetFrame(frameIndex, input.ReadFloat(), input.ReadFloat(), input.ReadFloat(), input.ReadFloat(),
input.ReadFloat());
if (frameIndex < frameCount - 1) ReadCurve(input, frameIndex, timeline);
}
timelines.Add(timeline);
duration = Math.Max(duration, timeline.frames[(frameCount - 1) * TransformConstraintTimeline.ENTRIES]);
}
// Path constraint timelines.
for (int i = 0, n = input.ReadInt(true); i < n; i++) {
int index = input.ReadInt(true);
PathConstraintData data = skeletonData.pathConstraints.Items[index];
for (int ii = 0, nn = input.ReadInt(true); ii < nn; ii++) {
int timelineType = input.ReadSByte();
int frameCount = input.ReadInt(true);
switch(timelineType) {
case PATH_POSITION:
case PATH_SPACING: {
PathConstraintPositionTimeline timeline;
float timelineScale = 1;
if (timelineType == PATH_SPACING) {
timeline = new PathConstraintSpacingTimeline(frameCount);
if (data.spacingMode == SpacingMode.Length || data.spacingMode == SpacingMode.Fixed) timelineScale = scale;
} else {
timeline = new PathConstraintPositionTimeline(frameCount);
if (data.positionMode == PositionMode.Fixed) timelineScale = scale;
}
timeline.pathConstraintIndex = index;
for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) {
timeline.SetFrame(frameIndex, input.ReadFloat(), input.ReadFloat() * timelineScale);
if (frameIndex < frameCount - 1) ReadCurve(input, frameIndex, timeline);
}
timelines.Add(timeline);
duration = Math.Max(duration, timeline.frames[(frameCount - 1) * PathConstraintPositionTimeline.ENTRIES]);
break;
}
case PATH_MIX: {
PathConstraintMixTimeline timeline = new PathConstraintMixTimeline(frameCount);
timeline.pathConstraintIndex = index;
for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) {
timeline.SetFrame(frameIndex, input.ReadFloat(), input.ReadFloat(), input.ReadFloat());
if (frameIndex < frameCount - 1) ReadCurve(input, frameIndex, timeline);
}
timelines.Add(timeline);
duration = Math.Max(duration, timeline.frames[(frameCount - 1) * PathConstraintMixTimeline.ENTRIES]);
break;
}
}
}
}
// Deform timelines.
for (int i = 0, n = input.ReadInt(true); i < n; i++) {
Skin skin = skeletonData.skins.Items[input.ReadInt(true)];
for (int ii = 0, nn = input.ReadInt(true); ii < nn; ii++) {
int slotIndex = input.ReadInt(true);
for (int iii = 0, nnn = input.ReadInt(true); iii < nnn; iii++) {
VertexAttachment attachment = (VertexAttachment)skin.GetAttachment(slotIndex, input.ReadStringRef());
bool weighted = attachment.bones != null;
float[] vertices = attachment.vertices;
int deformLength = weighted ? vertices.Length / 3 * 2 : vertices.Length;
int frameCount = input.ReadInt(true);
DeformTimeline timeline = new DeformTimeline(frameCount);
timeline.slotIndex = slotIndex;
timeline.attachment = attachment;
for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) {
float time = input.ReadFloat();
float[] deform;
int end = input.ReadInt(true);
if (end == 0)
deform = weighted ? new float[deformLength] : vertices;
else {
deform = new float[deformLength];
int start = input.ReadInt(true);
end += start;
if (scale == 1) {
for (int v = start; v < end; v++)
deform[v] = input.ReadFloat();
} else {
for (int v = start; v < end; v++)
deform[v] = input.ReadFloat() * scale;
}
if (!weighted) {
for (int v = 0, vn = deform.Length; v < vn; v++)
deform[v] += vertices[v];
}
}
timeline.SetFrame(frameIndex, time, deform);
if (frameIndex < frameCount - 1) ReadCurve(input, frameIndex, timeline);
}
timelines.Add(timeline);
duration = Math.Max(duration, timeline.frames[frameCount - 1]);
}
}
}
// Draw order timeline.
int drawOrderCount = input.ReadInt(true);
if (drawOrderCount > 0) {
DrawOrderTimeline timeline = new DrawOrderTimeline(drawOrderCount);
int slotCount = skeletonData.slots.Count;
for (int i = 0; i < drawOrderCount; i++) {
float time = input.ReadFloat();
int offsetCount = input.ReadInt(true);
int[] drawOrder = new int[slotCount];
for (int ii = slotCount - 1; ii >= 0; ii--)
drawOrder[ii] = -1;
int[] unchanged = new int[slotCount - offsetCount];
int originalIndex = 0, unchangedIndex = 0;
for (int ii = 0; ii < offsetCount; ii++) {
int slotIndex = input.ReadInt(true);
// Collect unchanged items.
while (originalIndex != slotIndex)
unchanged[unchangedIndex++] = originalIndex++;
// Set changed items.
drawOrder[originalIndex + input.ReadInt(true)] = originalIndex++;
}
// Collect remaining unchanged items.
while (originalIndex < slotCount)
unchanged[unchangedIndex++] = originalIndex++;
// Fill in unchanged items.
for (int ii = slotCount - 1; ii >= 0; ii--)
if (drawOrder[ii] == -1) drawOrder[ii] = unchanged[--unchangedIndex];
timeline.SetFrame(i, time, drawOrder);
}
timelines.Add(timeline);
duration = Math.Max(duration, timeline.frames[drawOrderCount - 1]);
}
// Event timeline.
int eventCount = input.ReadInt(true);
if (eventCount > 0) {
EventTimeline timeline = new EventTimeline(eventCount);
for (int i = 0; i < eventCount; i++) {
float time = input.ReadFloat();
EventData eventData = skeletonData.events.Items[input.ReadInt(true)];
Event e = new Event(time, eventData) {
Int = input.ReadInt(false),
Float = input.ReadFloat(),
String = input.ReadBoolean() ? input.ReadString() : eventData.String
};
if (e.data.AudioPath != null) {
e.volume = input.ReadFloat();
e.balance = input.ReadFloat();
}
timeline.SetFrame(i, e);
}
timelines.Add(timeline);
duration = Math.Max(duration, timeline.frames[eventCount - 1]);
}
timelines.TrimExcess();
return new Animation(name, timelines, duration);
}
private void ReadCurve (SkeletonInput input, int frameIndex, CurveTimeline timeline) {
switch (input.ReadByte()) {
case CURVE_STEPPED:
timeline.SetStepped(frameIndex);
break;
case CURVE_BEZIER:
timeline.SetCurve(frameIndex, input.ReadFloat(), input.ReadFloat(), input.ReadFloat(), input.ReadFloat());
break;
}
}
internal class Vertices
{
public int[] bones;
public float[] vertices;
}
internal class SkeletonInput {
private byte[] chars = new byte[32];
private byte[] bytesBigEndian = new byte[4];
internal ExposedList<String> strings;
Stream input;
public SkeletonInput (Stream input) {
this.input = input;
}
public byte ReadByte () {
return (byte)input.ReadByte();
}
public sbyte ReadSByte () {
int value = input.ReadByte();
if (value == -1) throw new EndOfStreamException();
return (sbyte)value;
}
public bool ReadBoolean () {
return input.ReadByte() != 0;
}
public float ReadFloat () {
input.Read(bytesBigEndian, 0, 4);
chars[3] = bytesBigEndian[0];
chars[2] = bytesBigEndian[1];
chars[1] = bytesBigEndian[2];
chars[0] = bytesBigEndian[3];
return BitConverter.ToSingle(chars, 0);
}
public int ReadInt () {
input.Read(bytesBigEndian, 0, 4);
return (bytesBigEndian[0] << 24)
+ (bytesBigEndian[1] << 16)
+ (bytesBigEndian[2] << 8)
+ bytesBigEndian[3];
}
public int ReadInt (bool optimizePositive) {
int b = input.ReadByte();
int result = b & 0x7F;
if ((b & 0x80) != 0) {
b = input.ReadByte();
result |= (b & 0x7F) << 7;
if ((b & 0x80) != 0) {
b = input.ReadByte();
result |= (b & 0x7F) << 14;
if ((b & 0x80) != 0) {
b = input.ReadByte();
result |= (b & 0x7F) << 21;
if ((b & 0x80) != 0) result |= (input.ReadByte() & 0x7F) << 28;
}
}
}
return optimizePositive ? result : ((result >> 1) ^ -(result & 1));
}
public string ReadString () {
int byteCount = ReadInt(true);
switch (byteCount) {
case 0:
return null;
case 1:
return "";
}
byteCount--;
byte[] buffer = this.chars;
if (buffer.Length < byteCount) buffer = new byte[byteCount];
ReadFully(buffer, 0, byteCount);
return System.Text.Encoding.UTF8.GetString(buffer, 0, byteCount);
}
///<return>May be null.</return>
public String ReadStringRef () {
int index = ReadInt(true);
return index == 0 ? null : strings.Items[index - 1];
}
public void ReadFully (byte[] buffer, int offset, int length) {
while (length > 0) {
int count = input.Read(buffer, offset, length);
if (count <= 0) throw new EndOfStreamException();
offset += count;
length -= count;
}
}
/// <summary>Returns the version string of binary skeleton data.</summary>
public string GetVersionString () {
try {
// Hash.
int byteCount = ReadInt(true);
if (byteCount > 1) input.Position += byteCount - 1;
// Version.
byteCount = ReadInt(true);
if (byteCount > 1) {
byteCount--;
var buffer = new byte[byteCount];
ReadFully(buffer, 0, byteCount);
return System.Text.Encoding.UTF8.GetString(buffer, 0, byteCount);
}
throw new ArgumentException("Stream does not contain a valid binary Skeleton Data.", "input");
} catch (Exception e) {
throw new ArgumentException("Stream does not contain a valid binary Skeleton Data.\n" + e, "input");
}
}
}
}
}

View File

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

View File

@@ -1,234 +0,0 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
using System;
namespace Spine {
/// <summary>
/// Collects each BoundingBoxAttachment that is visible and computes the world vertices for its polygon.
/// The polygon vertices are provided along with convenience methods for doing hit detection.
/// </summary>
public class SkeletonBounds {
private ExposedList<Polygon> polygonPool = new ExposedList<Polygon>();
private float minX, minY, maxX, maxY;
public ExposedList<BoundingBoxAttachment> BoundingBoxes { get; private set; }
public ExposedList<Polygon> Polygons { get; private set; }
public float MinX { get { return minX; } set { minX = value; } }
public float MinY { get { return minY; } set { minY = value; } }
public float MaxX { get { return maxX; } set { maxX = value; } }
public float MaxY { get { return maxY; } set { maxY = value; } }
public float Width { get { return maxX - minX; } }
public float Height { get { return maxY - minY; } }
public SkeletonBounds () {
BoundingBoxes = new ExposedList<BoundingBoxAttachment>();
Polygons = new ExposedList<Polygon>();
}
/// <summary>
/// Clears any previous polygons, finds all visible bounding box attachments,
/// and computes the world vertices for each bounding box's polygon.</summary>
/// <param name="skeleton">The skeleton.</param>
/// <param name="updateAabb">
/// If true, the axis aligned bounding box containing all the polygons is computed.
/// If false, the SkeletonBounds AABB methods will always return true.
/// </param>
public void Update (Skeleton skeleton, bool updateAabb) {
ExposedList<BoundingBoxAttachment> boundingBoxes = BoundingBoxes;
ExposedList<Polygon> polygons = Polygons;
ExposedList<Slot> slots = skeleton.slots;
int slotCount = slots.Count;
boundingBoxes.Clear();
for (int i = 0, n = polygons.Count; i < n; i++)
polygonPool.Add(polygons.Items[i]);
polygons.Clear();
for (int i = 0; i < slotCount; i++) {
Slot slot = slots.Items[i];
if (!slot.bone.active) continue;
BoundingBoxAttachment boundingBox = slot.attachment as BoundingBoxAttachment;
if (boundingBox == null) continue;
boundingBoxes.Add(boundingBox);
Polygon polygon = null;
int poolCount = polygonPool.Count;
if (poolCount > 0) {
polygon = polygonPool.Items[poolCount - 1];
polygonPool.RemoveAt(poolCount - 1);
} else
polygon = new Polygon();
polygons.Add(polygon);
int count = boundingBox.worldVerticesLength;
polygon.Count = count;
if (polygon.Vertices.Length < count) polygon.Vertices = new float[count];
boundingBox.ComputeWorldVertices(slot, polygon.Vertices);
}
if (updateAabb) {
AabbCompute();
} else {
minX = int.MinValue;
minY = int.MinValue;
maxX = int.MaxValue;
maxY = int.MaxValue;
}
}
private void AabbCompute () {
float minX = int.MaxValue, minY = int.MaxValue, maxX = int.MinValue, maxY = int.MinValue;
ExposedList<Polygon> polygons = Polygons;
for (int i = 0, n = polygons.Count; i < n; i++) {
Polygon polygon = polygons.Items[i];
float[] vertices = polygon.Vertices;
for (int ii = 0, nn = polygon.Count; ii < nn; ii += 2) {
float x = vertices[ii];
float y = vertices[ii + 1];
minX = Math.Min(minX, x);
minY = Math.Min(minY, y);
maxX = Math.Max(maxX, x);
maxY = Math.Max(maxY, y);
}
}
this.minX = minX;
this.minY = minY;
this.maxX = maxX;
this.maxY = maxY;
}
/// <summary>Returns true if the axis aligned bounding box contains the point.</summary>
public bool AabbContainsPoint (float x, float y) {
return x >= minX && x <= maxX && y >= minY && y <= maxY;
}
/// <summary>Returns true if the axis aligned bounding box intersects the line segment.</summary>
public bool AabbIntersectsSegment (float x1, float y1, float x2, float y2) {
float minX = this.minX;
float minY = this.minY;
float maxX = this.maxX;
float maxY = this.maxY;
if ((x1 <= minX && x2 <= minX) || (y1 <= minY && y2 <= minY) || (x1 >= maxX && x2 >= maxX) || (y1 >= maxY && y2 >= maxY))
return false;
float m = (y2 - y1) / (x2 - x1);
float y = m * (minX - x1) + y1;
if (y > minY && y < maxY) return true;
y = m * (maxX - x1) + y1;
if (y > minY && y < maxY) return true;
float x = (minY - y1) / m + x1;
if (x > minX && x < maxX) return true;
x = (maxY - y1) / m + x1;
if (x > minX && x < maxX) return true;
return false;
}
/// <summary>Returns true if the axis aligned bounding box intersects the axis aligned bounding box of the specified bounds.</summary>
public bool AabbIntersectsSkeleton (SkeletonBounds bounds) {
return minX < bounds.maxX && maxX > bounds.minX && minY < bounds.maxY && maxY > bounds.minY;
}
/// <summary>Returns true if the polygon contains the point.</summary>
public bool ContainsPoint (Polygon polygon, float x, float y) {
float[] vertices = polygon.Vertices;
int nn = polygon.Count;
int prevIndex = nn - 2;
bool inside = false;
for (int ii = 0; ii < nn; ii += 2) {
float vertexY = vertices[ii + 1];
float prevY = vertices[prevIndex + 1];
if ((vertexY < y && prevY >= y) || (prevY < y && vertexY >= y)) {
float vertexX = vertices[ii];
if (vertexX + (y - vertexY) / (prevY - vertexY) * (vertices[prevIndex] - vertexX) < x) inside = !inside;
}
prevIndex = ii;
}
return inside;
}
/// <summary>Returns the first bounding box attachment that contains the point, or null. When doing many checks, it is usually more
/// efficient to only call this method if {@link #aabbContainsPoint(float, float)} returns true.</summary>
public BoundingBoxAttachment ContainsPoint (float x, float y) {
ExposedList<Polygon> polygons = Polygons;
for (int i = 0, n = polygons.Count; i < n; i++)
if (ContainsPoint(polygons.Items[i], x, y)) return BoundingBoxes.Items[i];
return null;
}
/// <summary>Returns the first bounding box attachment that contains the line segment, or null. When doing many checks, it is usually
/// more efficient to only call this method if {@link #aabbIntersectsSegment(float, float, float, float)} returns true.</summary>
public BoundingBoxAttachment IntersectsSegment (float x1, float y1, float x2, float y2) {
ExposedList<Polygon> polygons = Polygons;
for (int i = 0, n = polygons.Count; i < n; i++)
if (IntersectsSegment(polygons.Items[i], x1, y1, x2, y2)) return BoundingBoxes.Items[i];
return null;
}
/// <summary>Returns true if the polygon contains the line segment.</summary>
public bool IntersectsSegment (Polygon polygon, float x1, float y1, float x2, float y2) {
float[] vertices = polygon.Vertices;
int nn = polygon.Count;
float width12 = x1 - x2, height12 = y1 - y2;
float det1 = x1 * y2 - y1 * x2;
float x3 = vertices[nn - 2], y3 = vertices[nn - 1];
for (int ii = 0; ii < nn; ii += 2) {
float x4 = vertices[ii], y4 = vertices[ii + 1];
float det2 = x3 * y4 - y3 * x4;
float width34 = x3 - x4, height34 = y3 - y4;
float det3 = width12 * height34 - height12 * width34;
float x = (det1 * width34 - width12 * det2) / det3;
if (((x >= x3 && x <= x4) || (x >= x4 && x <= x3)) && ((x >= x1 && x <= x2) || (x >= x2 && x <= x1))) {
float y = (det1 * height34 - height12 * det2) / det3;
if (((y >= y3 && y <= y4) || (y >= y4 && y <= y3)) && ((y >= y1 && y <= y2) || (y >= y2 && y <= y1))) return true;
}
x3 = x4;
y3 = y4;
}
return false;
}
public Polygon GetPolygon (BoundingBoxAttachment attachment) {
int index = BoundingBoxes.IndexOf(attachment);
return index == -1 ? null : Polygons.Items[index];
}
}
public class Polygon {
public float[] Vertices { get; set; }
public int Count { get; set; }
public Polygon () {
Vertices = new float[16];
}
}
}

View File

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

View File

@@ -1,296 +0,0 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
using System;
namespace Spine {
public class SkeletonClipping {
internal readonly Triangulator triangulator = new Triangulator();
internal readonly ExposedList<float> clippingPolygon = new ExposedList<float>();
internal readonly ExposedList<float> clipOutput = new ExposedList<float>(128);
internal readonly ExposedList<float> clippedVertices = new ExposedList<float>(128);
internal readonly ExposedList<int> clippedTriangles = new ExposedList<int>(128);
internal readonly ExposedList<float> clippedUVs = new ExposedList<float>(128);
internal readonly ExposedList<float> scratch = new ExposedList<float>();
internal ClippingAttachment clipAttachment;
internal ExposedList<ExposedList<float>> clippingPolygons;
public ExposedList<float> ClippedVertices { get { return clippedVertices; } }
public ExposedList<int> ClippedTriangles { get { return clippedTriangles; } }
public ExposedList<float> ClippedUVs { get { return clippedUVs; } }
public bool IsClipping { get { return clipAttachment != null; } }
public int ClipStart (Slot slot, ClippingAttachment clip) {
if (clipAttachment != null) return 0;
clipAttachment = clip;
int n = clip.worldVerticesLength;
float[] vertices = clippingPolygon.Resize(n).Items;
clip.ComputeWorldVertices(slot, 0, n, vertices, 0, 2);
MakeClockwise(clippingPolygon);
clippingPolygons = triangulator.Decompose(clippingPolygon, triangulator.Triangulate(clippingPolygon));
foreach (var polygon in clippingPolygons) {
MakeClockwise(polygon);
polygon.Add(polygon.Items[0]);
polygon.Add(polygon.Items[1]);
}
return clippingPolygons.Count;
}
public void ClipEnd (Slot slot) {
if (clipAttachment != null && clipAttachment.endSlot == slot.data) ClipEnd();
}
public void ClipEnd () {
if (clipAttachment == null) return;
clipAttachment = null;
clippingPolygons = null;
clippedVertices.Clear();
clippedTriangles.Clear();
clippingPolygon.Clear();
}
public void ClipTriangles (float[] vertices, int verticesLength, int[] triangles, int trianglesLength, float[] uvs) {
ExposedList<float> clipOutput = this.clipOutput, clippedVertices = this.clippedVertices;
var clippedTriangles = this.clippedTriangles;
var polygons = clippingPolygons.Items;
int polygonsCount = clippingPolygons.Count;
int index = 0;
clippedVertices.Clear();
clippedUVs.Clear();
clippedTriangles.Clear();
//outer:
for (int i = 0; i < trianglesLength; i += 3) {
int vertexOffset = triangles[i] << 1;
float x1 = vertices[vertexOffset], y1 = vertices[vertexOffset + 1];
float u1 = uvs[vertexOffset], v1 = uvs[vertexOffset + 1];
vertexOffset = triangles[i + 1] << 1;
float x2 = vertices[vertexOffset], y2 = vertices[vertexOffset + 1];
float u2 = uvs[vertexOffset], v2 = uvs[vertexOffset + 1];
vertexOffset = triangles[i + 2] << 1;
float x3 = vertices[vertexOffset], y3 = vertices[vertexOffset + 1];
float u3 = uvs[vertexOffset], v3 = uvs[vertexOffset + 1];
for (int p = 0; p < polygonsCount; p++) {
int s = clippedVertices.Count;
if (Clip(x1, y1, x2, y2, x3, y3, polygons[p], clipOutput)) {
int clipOutputLength = clipOutput.Count;
if (clipOutputLength == 0) continue;
float d0 = y2 - y3, d1 = x3 - x2, d2 = x1 - x3, d4 = y3 - y1;
float d = 1 / (d0 * d2 + d1 * (y1 - y3));
int clipOutputCount = clipOutputLength >> 1;
float[] clipOutputItems = clipOutput.Items;
float[] clippedVerticesItems = clippedVertices.Resize(s + clipOutputCount * 2).Items;
float[] clippedUVsItems = clippedUVs.Resize(s + clipOutputCount * 2).Items;
for (int ii = 0; ii < clipOutputLength; ii += 2) {
float x = clipOutputItems[ii], y = clipOutputItems[ii + 1];
clippedVerticesItems[s] = x;
clippedVerticesItems[s + 1] = y;
float c0 = x - x3, c1 = y - y3;
float a = (d0 * c0 + d1 * c1) * d;
float b = (d4 * c0 + d2 * c1) * d;
float c = 1 - a - b;
clippedUVsItems[s] = u1 * a + u2 * b + u3 * c;
clippedUVsItems[s + 1] = v1 * a + v2 * b + v3 * c;
s += 2;
}
s = clippedTriangles.Count;
int[] clippedTrianglesItems = clippedTriangles.Resize(s + 3 * (clipOutputCount - 2)).Items;
clipOutputCount--;
for (int ii = 1; ii < clipOutputCount; ii++) {
clippedTrianglesItems[s] = index;
clippedTrianglesItems[s + 1] = index + ii;
clippedTrianglesItems[s + 2] = index + ii + 1;
s += 3;
}
index += clipOutputCount + 1;
}
else {
float[] clippedVerticesItems = clippedVertices.Resize(s + 3 * 2).Items;
float[] clippedUVsItems = clippedUVs.Resize(s + 3 * 2).Items;
clippedVerticesItems[s] = x1;
clippedVerticesItems[s + 1] = y1;
clippedVerticesItems[s + 2] = x2;
clippedVerticesItems[s + 3] = y2;
clippedVerticesItems[s + 4] = x3;
clippedVerticesItems[s + 5] = y3;
clippedUVsItems[s] = u1;
clippedUVsItems[s + 1] = v1;
clippedUVsItems[s + 2] = u2;
clippedUVsItems[s + 3] = v2;
clippedUVsItems[s + 4] = u3;
clippedUVsItems[s + 5] = v3;
s = clippedTriangles.Count;
int[] clippedTrianglesItems = clippedTriangles.Resize(s + 3).Items;
clippedTrianglesItems[s] = index;
clippedTrianglesItems[s + 1] = index + 1;
clippedTrianglesItems[s + 2] = index + 2;
index += 3;
break; //continue outer;
}
}
}
}
/** Clips the input triangle against the convex, clockwise clipping area. If the triangle lies entirely within the clipping
* area, false is returned. The clipping area must duplicate the first vertex at the end of the vertices list. */
internal bool Clip (float x1, float y1, float x2, float y2, float x3, float y3, ExposedList<float> clippingArea, ExposedList<float> output) {
var originalOutput = output;
var clipped = false;
// Avoid copy at the end.
ExposedList<float> input = null;
if (clippingArea.Count % 4 >= 2) {
input = output;
output = scratch;
} else {
input = scratch;
}
input.Clear();
input.Add(x1);
input.Add(y1);
input.Add(x2);
input.Add(y2);
input.Add(x3);
input.Add(y3);
input.Add(x1);
input.Add(y1);
output.Clear();
float[] clippingVertices = clippingArea.Items;
int clippingVerticesLast = clippingArea.Count - 4;
for (int i = 0; ; i += 2) {
float edgeX = clippingVertices[i], edgeY = clippingVertices[i + 1];
float edgeX2 = clippingVertices[i + 2], edgeY2 = clippingVertices[i + 3];
float deltaX = edgeX - edgeX2, deltaY = edgeY - edgeY2;
float[] inputVertices = input.Items;
int inputVerticesLength = input.Count - 2, outputStart = output.Count;
for (int ii = 0; ii < inputVerticesLength; ii += 2) {
float inputX = inputVertices[ii], inputY = inputVertices[ii + 1];
float inputX2 = inputVertices[ii + 2], inputY2 = inputVertices[ii + 3];
bool side2 = deltaX * (inputY2 - edgeY2) - deltaY * (inputX2 - edgeX2) > 0;
if (deltaX * (inputY - edgeY2) - deltaY * (inputX - edgeX2) > 0) {
if (side2) { // v1 inside, v2 inside
output.Add(inputX2);
output.Add(inputY2);
continue;
}
// v1 inside, v2 outside
float c0 = inputY2 - inputY, c2 = inputX2 - inputX;
float s = c0 * (edgeX2 - edgeX) - c2 * (edgeY2 - edgeY);
if (Math.Abs(s) > 0.000001f) {
float ua = (c2 * (edgeY - inputY) - c0 * (edgeX - inputX)) / s;
output.Add(edgeX + (edgeX2 - edgeX) * ua);
output.Add(edgeY + (edgeY2 - edgeY) * ua);
} else {
output.Add(edgeX);
output.Add(edgeY);
}
}
else if (side2) { // v1 outside, v2 inside
float c0 = inputY2 - inputY, c2 = inputX2 - inputX;
float s = c0 * (edgeX2 - edgeX) - c2 * (edgeY2 - edgeY);
if (Math.Abs(s) > 0.000001f) {
float ua = (c2 * (edgeY - inputY) - c0 * (edgeX - inputX)) / s;
output.Add(edgeX + (edgeX2 - edgeX) * ua);
output.Add(edgeY + (edgeY2 - edgeY) * ua);
} else {
output.Add(edgeX);
output.Add(edgeY);
}
output.Add(inputX2);
output.Add(inputY2);
}
clipped = true;
}
if (outputStart == output.Count) { // All edges outside.
originalOutput.Clear();
return true;
}
output.Add(output.Items[0]);
output.Add(output.Items[1]);
if (i == clippingVerticesLast) break;
var temp = output;
output = input;
output.Clear();
input = temp;
}
if (originalOutput != output) {
originalOutput.Clear();
for (int i = 0, n = output.Count - 2; i < n; i++) {
originalOutput.Add(output.Items[i]);
}
} else {
originalOutput.Resize(originalOutput.Count - 2);
}
return clipped;
}
public static void MakeClockwise (ExposedList<float> polygon) {
float[] vertices = polygon.Items;
int verticeslength = polygon.Count;
float area = vertices[verticeslength - 2] * vertices[1] - vertices[0] * vertices[verticeslength - 1], p1x, p1y, p2x, p2y;
for (int i = 0, n = verticeslength - 3; i < n; i += 2) {
p1x = vertices[i];
p1y = vertices[i + 1];
p2x = vertices[i + 2];
p2y = vertices[i + 3];
area += p1x * p2y - p2x * p1y;
}
if (area < 0) return;
for (int i = 0, lastX = verticeslength - 2, n = verticeslength >> 1; i < n; i += 2) {
float x = vertices[i], y = vertices[i + 1];
int other = lastX - i;
vertices[i] = vertices[other];
vertices[i + 1] = vertices[other + 1];
vertices[other] = x;
vertices[other + 1] = y;
}
}
}
}

View File

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

View File

@@ -1,230 +0,0 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
using System;
namespace Spine {
/// <summary>Stores the setup pose and all of the stateless data for a skeleton.</summary>
public class SkeletonData {
internal string name;
internal ExposedList<BoneData> bones = new ExposedList<BoneData>(); // Ordered parents first
internal ExposedList<SlotData> slots = new ExposedList<SlotData>(); // Setup pose draw order.
internal ExposedList<Skin> skins = new ExposedList<Skin>();
internal Skin defaultSkin;
internal ExposedList<EventData> events = new ExposedList<EventData>();
internal ExposedList<Animation> animations = new ExposedList<Animation>();
internal ExposedList<IkConstraintData> ikConstraints = new ExposedList<IkConstraintData>();
internal ExposedList<TransformConstraintData> transformConstraints = new ExposedList<TransformConstraintData>();
internal ExposedList<PathConstraintData> pathConstraints = new ExposedList<PathConstraintData>();
internal float x , y, width, height;
internal string version, hash;
// Nonessential.
internal float fps;
internal string imagesPath, audioPath;
public string Name { get { return name; } set { name = value; } }
/// <summary>The skeleton's bones, sorted parent first. The root bone is always the first bone.</summary>
public ExposedList<BoneData> Bones { get { return bones; } }
public ExposedList<SlotData> Slots { get { return slots; } }
/// <summary>All skins, including the default skin.</summary>
public ExposedList<Skin> Skins { get { return skins; } set { skins = value; } }
/// <summary>
/// The skeleton's default skin.
/// By default this skin contains all attachments that were not in a skin in Spine.
/// </summary>
/// <return>May be null.</return>
public Skin DefaultSkin { get { return defaultSkin; } set { defaultSkin = value; } }
public ExposedList<EventData> Events { get { return events; } set { events = value; } }
public ExposedList<Animation> Animations { get { return animations; } set { animations = value; } }
public ExposedList<IkConstraintData> IkConstraints { get { return ikConstraints; } set { ikConstraints = value; } }
public ExposedList<TransformConstraintData> TransformConstraints { get { return transformConstraints; } set { transformConstraints = value; } }
public ExposedList<PathConstraintData> PathConstraints { get { return pathConstraints; } set { pathConstraints = value; } }
public float X { get { return x; } set { x = value; } }
public float Y { get { return y; } set { y = value; } }
public float Width { get { return width; } set { width = value; } }
public float Height { get { return height; } set { height = value; } }
/// <summary>The Spine version used to export this data, or null.</summary>
public string Version { get { return version; } set { version = value; } }
public string Hash { get { return hash; } set { hash = value; } }
/// <summary>The path to the images directory as defined in Spine. Available only when nonessential data was exported. May be null</summary>
public string ImagesPath { get { return imagesPath; } set { imagesPath = value; } }
/// <summary>The path to the audio directory defined in Spine. Available only when nonessential data was exported. May be null.</summary>
public string AudioPath { get { return audioPath; } set { audioPath = value; } }
/// <summary>
/// The dopesheet FPS in Spine. Available only when nonessential data was exported.</summary>
public float Fps { get { return fps; } set { fps = value; } }
// --- Bones.
/// <summary>
/// Finds a bone by comparing each bone's name.
/// It is more efficient to cache the results of this method than to call it multiple times.</summary>
/// <returns>May be null.</returns>
public BoneData FindBone (string boneName) {
if (boneName == null) throw new ArgumentNullException("boneName", "boneName cannot be null.");
var bones = this.bones;
var bonesItems = bones.Items;
for (int i = 0, n = bones.Count; i < n; i++) {
BoneData bone = bonesItems[i];
if (bone.name == boneName) return bone;
}
return null;
}
/// <returns>-1 if the bone was not found.</returns>
public int FindBoneIndex (string boneName) {
if (boneName == null) throw new ArgumentNullException("boneName", "boneName cannot be null.");
var bones = this.bones;
var bonesItems = bones.Items;
for (int i = 0, n = bones.Count; i < n; i++)
if (bonesItems[i].name == boneName) return i;
return -1;
}
// --- Slots.
/// <returns>May be null.</returns>
public SlotData FindSlot (string slotName) {
if (slotName == null) throw new ArgumentNullException("slotName", "slotName cannot be null.");
ExposedList<SlotData> slots = this.slots;
for (int i = 0, n = slots.Count; i < n; i++) {
SlotData slot = slots.Items[i];
if (slot.name == slotName) return slot;
}
return null;
}
/// <returns>-1 if the slot was not found.</returns>
public int FindSlotIndex (string slotName) {
if (slotName == null) throw new ArgumentNullException("slotName", "slotName cannot be null.");
ExposedList<SlotData> slots = this.slots;
for (int i = 0, n = slots.Count; i < n; i++)
if (slots.Items[i].name == slotName) return i;
return -1;
}
// --- Skins.
/// <returns>May be null.</returns>
public Skin FindSkin (string skinName) {
if (skinName == null) throw new ArgumentNullException("skinName", "skinName cannot be null.");
foreach (Skin skin in skins)
if (skin.name == skinName) return skin;
return null;
}
// --- Events.
/// <returns>May be null.</returns>
public EventData FindEvent (string eventDataName) {
if (eventDataName == null) throw new ArgumentNullException("eventDataName", "eventDataName cannot be null.");
foreach (EventData eventData in events)
if (eventData.name == eventDataName) return eventData;
return null;
}
// --- Animations.
/// <returns>May be null.</returns>
public Animation FindAnimation (string animationName) {
if (animationName == null) throw new ArgumentNullException("animationName", "animationName cannot be null.");
ExposedList<Animation> animations = this.animations;
for (int i = 0, n = animations.Count; i < n; i++) {
Animation animation = animations.Items[i];
if (animation.name == animationName) return animation;
}
return null;
}
// --- IK constraints.
/// <returns>May be null.</returns>
public IkConstraintData FindIkConstraint (string constraintName) {
if (constraintName == null) throw new ArgumentNullException("constraintName", "constraintName cannot be null.");
ExposedList<IkConstraintData> ikConstraints = this.ikConstraints;
for (int i = 0, n = ikConstraints.Count; i < n; i++) {
IkConstraintData ikConstraint = ikConstraints.Items[i];
if (ikConstraint.name == constraintName) return ikConstraint;
}
return null;
}
// --- Transform constraints.
/// <returns>May be null.</returns>
public TransformConstraintData FindTransformConstraint (string constraintName) {
if (constraintName == null) throw new ArgumentNullException("constraintName", "constraintName cannot be null.");
ExposedList<TransformConstraintData> transformConstraints = this.transformConstraints;
for (int i = 0, n = transformConstraints.Count; i < n; i++) {
TransformConstraintData transformConstraint = transformConstraints.Items[i];
if (transformConstraint.name == constraintName) return transformConstraint;
}
return null;
}
// --- Path constraints.
/// <returns>May be null.</returns>
public PathConstraintData FindPathConstraint (string constraintName) {
if (constraintName == null) throw new ArgumentNullException("constraintName", "constraintName cannot be null.");
ExposedList<PathConstraintData> pathConstraints = this.pathConstraints;
for (int i = 0, n = pathConstraints.Count; i < n; i++) {
PathConstraintData constraint = pathConstraints.Items[i];
if (constraint.name.Equals(constraintName)) return constraint;
}
return null;
}
/// <returns>-1 if the path constraint was not found.</returns>
public int FindPathConstraintIndex (string pathConstraintName) {
if (pathConstraintName == null) throw new ArgumentNullException("pathConstraintName", "pathConstraintName cannot be null.");
ExposedList<PathConstraintData> pathConstraints = this.pathConstraints;
for (int i = 0, n = pathConstraints.Count; i < n; i++)
if (pathConstraints.Items[i].name.Equals(pathConstraintName)) return i;
return -1;
}
// ---
override public string ToString () {
return name ?? base.ToString();
}
}
}

View File

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

View File

@@ -1,918 +0,0 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#if (UNITY_5 || UNITY_5_3_OR_NEWER || UNITY_WSA || UNITY_WP8 || UNITY_WP8_1)
#define IS_UNITY
#endif
using System;
using System.IO;
using System.Collections.Generic;
#if WINDOWS_STOREAPP
using System.Threading.Tasks;
using Windows.Storage;
#endif
namespace Spine {
public class SkeletonJson {
public float Scale { get; set; }
private AttachmentLoader attachmentLoader;
private List<LinkedMesh> linkedMeshes = new List<LinkedMesh>();
public SkeletonJson (params Atlas[] atlasArray)
: this(new AtlasAttachmentLoader(atlasArray)) {
}
public SkeletonJson (AttachmentLoader attachmentLoader) {
if (attachmentLoader == null) throw new ArgumentNullException("attachmentLoader", "attachmentLoader cannot be null.");
this.attachmentLoader = attachmentLoader;
Scale = 1;
}
#if !IS_UNITY && WINDOWS_STOREAPP
private async Task<SkeletonData> ReadFile(string path) {
var folder = Windows.ApplicationModel.Package.Current.InstalledLocation;
var file = await folder.GetFileAsync(path).AsTask().ConfigureAwait(false);
using (var reader = new StreamReader(await file.OpenStreamForReadAsync().ConfigureAwait(false))) {
SkeletonData skeletonData = ReadSkeletonData(reader);
skeletonData.Name = Path.GetFileNameWithoutExtension(path);
return skeletonData;
}
}
public SkeletonData ReadSkeletonData (string path) {
return this.ReadFile(path).Result;
}
#else
public SkeletonData ReadSkeletonData (string path) {
#if WINDOWS_PHONE
using (var reader = new StreamReader(Microsoft.Xna.Framework.TitleContainer.OpenStream(path))) {
#else
using (var reader = new StreamReader(new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read))) {
#endif
SkeletonData skeletonData = ReadSkeletonData(reader);
skeletonData.name = Path.GetFileNameWithoutExtension(path);
return skeletonData;
}
}
#endif
public SkeletonData ReadSkeletonData (TextReader reader) {
if (reader == null) throw new ArgumentNullException("reader", "reader cannot be null.");
float scale = this.Scale;
var skeletonData = new SkeletonData();
var root = Json.Deserialize(reader) as Dictionary<string, Object>;
if (root == null) throw new Exception("Invalid JSON.");
// Skeleton.
if (root.ContainsKey("skeleton")) {
var skeletonMap = (Dictionary<string, Object>)root["skeleton"];
skeletonData.hash = (string)skeletonMap["hash"];
skeletonData.version = (string)skeletonMap["spine"];
if ("3.8.75" == skeletonData.version)
throw new Exception("Unsupported skeleton data, please export with a newer version of Spine.");
skeletonData.x = GetFloat(skeletonMap, "x", 0);
skeletonData.y = GetFloat(skeletonMap, "y", 0);
skeletonData.width = GetFloat(skeletonMap, "width", 0);
skeletonData.height = GetFloat(skeletonMap, "height", 0);
skeletonData.fps = GetFloat(skeletonMap, "fps", 30);
skeletonData.imagesPath = GetString(skeletonMap, "images", null);
skeletonData.audioPath = GetString(skeletonMap, "audio", null);
}
// Bones.
if (root.ContainsKey("bones")) {
foreach (Dictionary<string, Object> boneMap in (List<Object>)root["bones"]) {
BoneData parent = null;
if (boneMap.ContainsKey("parent")) {
parent = skeletonData.FindBone((string)boneMap["parent"]);
if (parent == null)
throw new Exception("Parent bone not found: " + boneMap["parent"]);
}
var data = new BoneData(skeletonData.Bones.Count, (string)boneMap["name"], parent);
data.length = GetFloat(boneMap, "length", 0) * scale;
data.x = GetFloat(boneMap, "x", 0) * scale;
data.y = GetFloat(boneMap, "y", 0) * scale;
data.rotation = GetFloat(boneMap, "rotation", 0);
data.scaleX = GetFloat(boneMap, "scaleX", 1);
data.scaleY = GetFloat(boneMap, "scaleY", 1);
data.shearX = GetFloat(boneMap, "shearX", 0);
data.shearY = GetFloat(boneMap, "shearY", 0);
string tm = GetString(boneMap, "transform", TransformMode.Normal.ToString());
data.transformMode = (TransformMode)Enum.Parse(typeof(TransformMode), tm, true);
data.skinRequired = GetBoolean(boneMap, "skin", false);
skeletonData.bones.Add(data);
}
}
// Slots.
if (root.ContainsKey("slots")) {
foreach (Dictionary<string, Object> slotMap in (List<Object>)root["slots"]) {
var slotName = (string)slotMap["name"];
var boneName = (string)slotMap["bone"];
BoneData boneData = skeletonData.FindBone(boneName);
if (boneData == null) throw new Exception("Slot bone not found: " + boneName);
var data = new SlotData(skeletonData.Slots.Count, slotName, boneData);
if (slotMap.ContainsKey("color")) {
string color = (string)slotMap["color"];
data.r = ToColor(color, 0);
data.g = ToColor(color, 1);
data.b = ToColor(color, 2);
data.a = ToColor(color, 3);
}
if (slotMap.ContainsKey("dark")) {
var color2 = (string)slotMap["dark"];
data.r2 = ToColor(color2, 0, 6); // expectedLength = 6. ie. "RRGGBB"
data.g2 = ToColor(color2, 1, 6);
data.b2 = ToColor(color2, 2, 6);
data.hasSecondColor = true;
}
data.attachmentName = GetString(slotMap, "attachment", null);
if (slotMap.ContainsKey("blend"))
data.blendMode = (BlendMode)Enum.Parse(typeof(BlendMode), (string)slotMap["blend"], true);
else
data.blendMode = BlendMode.Normal;
skeletonData.slots.Add(data);
}
}
// IK constraints.
if (root.ContainsKey("ik")) {
foreach (Dictionary<string, Object> constraintMap in (List<Object>)root["ik"]) {
IkConstraintData data = new IkConstraintData((string)constraintMap["name"]);
data.order = GetInt(constraintMap, "order", 0);
data.skinRequired = GetBoolean(constraintMap,"skin", false);
if (constraintMap.ContainsKey("bones")) {
foreach (string boneName in (List<Object>)constraintMap["bones"]) {
BoneData bone = skeletonData.FindBone(boneName);
if (bone == null) throw new Exception("IK bone not found: " + boneName);
data.bones.Add(bone);
}
}
string targetName = (string)constraintMap["target"];
data.target = skeletonData.FindBone(targetName);
if (data.target == null) throw new Exception("IK target bone not found: " + targetName);
data.mix = GetFloat(constraintMap, "mix", 1);
data.softness = GetFloat(constraintMap, "softness", 0) * scale;
data.bendDirection = GetBoolean(constraintMap, "bendPositive", true) ? 1 : -1;
data.compress = GetBoolean(constraintMap, "compress", false);
data.stretch = GetBoolean(constraintMap, "stretch", false);
data.uniform = GetBoolean(constraintMap, "uniform", false);
skeletonData.ikConstraints.Add(data);
}
}
// Transform constraints.
if (root.ContainsKey("transform")) {
foreach (Dictionary<string, Object> constraintMap in (List<Object>)root["transform"]) {
TransformConstraintData data = new TransformConstraintData((string)constraintMap["name"]);
data.order = GetInt(constraintMap, "order", 0);
data.skinRequired = GetBoolean(constraintMap,"skin", false);
if (constraintMap.ContainsKey("bones")) {
foreach (string boneName in (List<Object>)constraintMap["bones"]) {
BoneData bone = skeletonData.FindBone(boneName);
if (bone == null) throw new Exception("Transform constraint bone not found: " + boneName);
data.bones.Add(bone);
}
}
string targetName = (string)constraintMap["target"];
data.target = skeletonData.FindBone(targetName);
if (data.target == null) throw new Exception("Transform constraint target bone not found: " + targetName);
data.local = GetBoolean(constraintMap, "local", false);
data.relative = GetBoolean(constraintMap, "relative", false);
data.offsetRotation = GetFloat(constraintMap, "rotation", 0);
data.offsetX = GetFloat(constraintMap, "x", 0) * scale;
data.offsetY = GetFloat(constraintMap, "y", 0) * scale;
data.offsetScaleX = GetFloat(constraintMap, "scaleX", 0);
data.offsetScaleY = GetFloat(constraintMap, "scaleY", 0);
data.offsetShearY = GetFloat(constraintMap, "shearY", 0);
data.rotateMix = GetFloat(constraintMap, "rotateMix", 1);
data.translateMix = GetFloat(constraintMap, "translateMix", 1);
data.scaleMix = GetFloat(constraintMap, "scaleMix", 1);
data.shearMix = GetFloat(constraintMap, "shearMix", 1);
skeletonData.transformConstraints.Add(data);
}
}
// Path constraints.
if(root.ContainsKey("path")) {
foreach (Dictionary<string, Object> constraintMap in (List<Object>)root["path"]) {
PathConstraintData data = new PathConstraintData((string)constraintMap["name"]);
data.order = GetInt(constraintMap, "order", 0);
data.skinRequired = GetBoolean(constraintMap,"skin", false);
if (constraintMap.ContainsKey("bones")) {
foreach (string boneName in (List<Object>)constraintMap["bones"]) {
BoneData bone = skeletonData.FindBone(boneName);
if (bone == null) throw new Exception("Path bone not found: " + boneName);
data.bones.Add(bone);
}
}
string targetName = (string)constraintMap["target"];
data.target = skeletonData.FindSlot(targetName);
if (data.target == null) throw new Exception("Path target slot not found: " + targetName);
data.positionMode = (PositionMode)Enum.Parse(typeof(PositionMode), GetString(constraintMap, "positionMode", "percent"), true);
data.spacingMode = (SpacingMode)Enum.Parse(typeof(SpacingMode), GetString(constraintMap, "spacingMode", "length"), true);
data.rotateMode = (RotateMode)Enum.Parse(typeof(RotateMode), GetString(constraintMap, "rotateMode", "tangent"), true);
data.offsetRotation = GetFloat(constraintMap, "rotation", 0);
data.position = GetFloat(constraintMap, "position", 0);
if (data.positionMode == PositionMode.Fixed) data.position *= scale;
data.spacing = GetFloat(constraintMap, "spacing", 0);
if (data.spacingMode == SpacingMode.Length || data.spacingMode == SpacingMode.Fixed) data.spacing *= scale;
data.rotateMix = GetFloat(constraintMap, "rotateMix", 1);
data.translateMix = GetFloat(constraintMap, "translateMix", 1);
skeletonData.pathConstraints.Add(data);
}
}
// Skins.
if (root.ContainsKey("skins")) {
foreach (Dictionary<string, object> skinMap in (List<object>)root["skins"]) {
Skin skin = new Skin((string)skinMap["name"]);
if (skinMap.ContainsKey("bones")) {
foreach (string entryName in (List<Object>)skinMap["bones"]) {
BoneData bone = skeletonData.FindBone(entryName);
if (bone == null) throw new Exception("Skin bone not found: " + entryName);
skin.bones.Add(bone);
}
}
if (skinMap.ContainsKey("ik")) {
foreach (string entryName in (List<Object>)skinMap["ik"]) {
IkConstraintData constraint = skeletonData.FindIkConstraint(entryName);
if (constraint == null) throw new Exception("Skin IK constraint not found: " + entryName);
skin.constraints.Add(constraint);
}
}
if (skinMap.ContainsKey("transform")) {
foreach (string entryName in (List<Object>)skinMap["transform"]) {
TransformConstraintData constraint = skeletonData.FindTransformConstraint(entryName);
if (constraint == null) throw new Exception("Skin transform constraint not found: " + entryName);
skin.constraints.Add(constraint);
}
}
if (skinMap.ContainsKey("path")) {
foreach (string entryName in (List<Object>)skinMap["path"]) {
PathConstraintData constraint = skeletonData.FindPathConstraint(entryName);
if (constraint == null) throw new Exception("Skin path constraint not found: " + entryName);
skin.constraints.Add(constraint);
}
}
if (skinMap.ContainsKey("attachments")) {
foreach (KeyValuePair<string, Object> slotEntry in (Dictionary<string, Object>)skinMap["attachments"]) {
int slotIndex = skeletonData.FindSlotIndex(slotEntry.Key);
foreach (KeyValuePair<string, Object> entry in ((Dictionary<string, Object>)slotEntry.Value)) {
try {
Attachment attachment = ReadAttachment((Dictionary<string, Object>)entry.Value, skin, slotIndex, entry.Key, skeletonData);
if (attachment != null) skin.SetAttachment(slotIndex, entry.Key, attachment);
} catch (Exception e) {
throw new Exception("Error reading attachment: " + entry.Key + ", skin: " + skin, e);
}
}
}
}
skeletonData.skins.Add(skin);
if (skin.name == "default") skeletonData.defaultSkin = skin;
}
}
// Linked meshes.
for (int i = 0, n = linkedMeshes.Count; i < n; i++) {
LinkedMesh linkedMesh = linkedMeshes[i];
Skin skin = linkedMesh.skin == null ? skeletonData.defaultSkin : skeletonData.FindSkin(linkedMesh.skin);
if (skin == null) throw new Exception("Slot not found: " + linkedMesh.skin);
Attachment parent = skin.GetAttachment(linkedMesh.slotIndex, linkedMesh.parent);
if (parent == null) throw new Exception("Parent mesh not found: " + linkedMesh.parent);
linkedMesh.mesh.DeformAttachment = linkedMesh.inheritDeform ? (VertexAttachment)parent : linkedMesh.mesh;
linkedMesh.mesh.ParentMesh = (MeshAttachment)parent;
linkedMesh.mesh.UpdateUVs();
}
linkedMeshes.Clear();
// Events.
if (root.ContainsKey("events")) {
foreach (KeyValuePair<string, Object> entry in (Dictionary<string, Object>)root["events"]) {
var entryMap = (Dictionary<string, Object>)entry.Value;
var data = new EventData(entry.Key);
data.Int = GetInt(entryMap, "int", 0);
data.Float = GetFloat(entryMap, "float", 0);
data.String = GetString(entryMap, "string", string.Empty);
data.AudioPath = GetString(entryMap, "audio", null);
if (data.AudioPath != null) {
data.Volume = GetFloat(entryMap, "volume", 1);
data.Balance = GetFloat(entryMap, "balance", 0);
}
skeletonData.events.Add(data);
}
}
// Animations.
if (root.ContainsKey("animations")) {
foreach (KeyValuePair<string, Object> entry in (Dictionary<string, Object>)root["animations"]) {
try {
ReadAnimation((Dictionary<string, Object>)entry.Value, entry.Key, skeletonData);
} catch (Exception e) {
throw new Exception("Error reading animation: " + entry.Key, e);
}
}
}
skeletonData.bones.TrimExcess();
skeletonData.slots.TrimExcess();
skeletonData.skins.TrimExcess();
skeletonData.events.TrimExcess();
skeletonData.animations.TrimExcess();
skeletonData.ikConstraints.TrimExcess();
return skeletonData;
}
private Attachment ReadAttachment (Dictionary<string, Object> map, Skin skin, int slotIndex, string name, SkeletonData skeletonData) {
float scale = this.Scale;
name = GetString(map, "name", name);
var typeName = GetString(map, "type", "region");
var type = (AttachmentType)Enum.Parse(typeof(AttachmentType), typeName, true);
string path = GetString(map, "path", name);
switch (type) {
case AttachmentType.Region:
RegionAttachment region = attachmentLoader.NewRegionAttachment(skin, name, path);
if (region == null) return null;
region.Path = path;
region.x = GetFloat(map, "x", 0) * scale;
region.y = GetFloat(map, "y", 0) * scale;
region.scaleX = GetFloat(map, "scaleX", 1);
region.scaleY = GetFloat(map, "scaleY", 1);
region.rotation = GetFloat(map, "rotation", 0);
region.width = GetFloat(map, "width", 32) * scale;
region.height = GetFloat(map, "height", 32) * scale;
if (map.ContainsKey("color")) {
var color = (string)map["color"];
region.r = ToColor(color, 0);
region.g = ToColor(color, 1);
region.b = ToColor(color, 2);
region.a = ToColor(color, 3);
}
region.UpdateOffset();
return region;
case AttachmentType.Boundingbox:
BoundingBoxAttachment box = attachmentLoader.NewBoundingBoxAttachment(skin, name);
if (box == null) return null;
ReadVertices(map, box, GetInt(map, "vertexCount", 0) << 1);
return box;
case AttachmentType.Mesh:
case AttachmentType.Linkedmesh: {
MeshAttachment mesh = attachmentLoader.NewMeshAttachment(skin, name, path);
if (mesh == null) return null;
mesh.Path = path;
if (map.ContainsKey("color")) {
var color = (string)map["color"];
mesh.r = ToColor(color, 0);
mesh.g = ToColor(color, 1);
mesh.b = ToColor(color, 2);
mesh.a = ToColor(color, 3);
}
mesh.Width = GetFloat(map, "width", 0) * scale;
mesh.Height = GetFloat(map, "height", 0) * scale;
string parent = GetString(map, "parent", null);
if (parent != null) {
linkedMeshes.Add(new LinkedMesh(mesh, GetString(map, "skin", null), slotIndex, parent, GetBoolean(map, "deform", true)));
return mesh;
}
float[] uvs = GetFloatArray(map, "uvs", 1);
ReadVertices(map, mesh, uvs.Length);
mesh.triangles = GetIntArray(map, "triangles");
mesh.regionUVs = uvs;
mesh.UpdateUVs();
if (map.ContainsKey("hull")) mesh.HullLength = GetInt(map, "hull", 0) * 2;
if (map.ContainsKey("edges")) mesh.Edges = GetIntArray(map, "edges");
return mesh;
}
case AttachmentType.Path: {
PathAttachment pathAttachment = attachmentLoader.NewPathAttachment(skin, name);
if (pathAttachment == null) return null;
pathAttachment.closed = GetBoolean(map, "closed", false);
pathAttachment.constantSpeed = GetBoolean(map, "constantSpeed", true);
int vertexCount = GetInt(map, "vertexCount", 0);
ReadVertices(map, pathAttachment, vertexCount << 1);
// potential BOZO see Java impl
pathAttachment.lengths = GetFloatArray(map, "lengths", scale);
return pathAttachment;
}
case AttachmentType.Point: {
PointAttachment point = attachmentLoader.NewPointAttachment(skin, name);
if (point == null) return null;
point.x = GetFloat(map, "x", 0) * scale;
point.y = GetFloat(map, "y", 0) * scale;
point.rotation = GetFloat(map, "rotation", 0);
//string color = GetString(map, "color", null);
//if (color != null) point.color = color;
return point;
}
case AttachmentType.Clipping: {
ClippingAttachment clip = attachmentLoader.NewClippingAttachment(skin, name);
if (clip == null) return null;
string end = GetString(map, "end", null);
if (end != null) {
SlotData slot = skeletonData.FindSlot(end);
if (slot == null) throw new Exception("Clipping end slot not found: " + end);
clip.EndSlot = slot;
}
ReadVertices(map, clip, GetInt(map, "vertexCount", 0) << 1);
//string color = GetString(map, "color", null);
// if (color != null) clip.color = color;
return clip;
}
}
return null;
}
private void ReadVertices (Dictionary<string, Object> map, VertexAttachment attachment, int verticesLength) {
attachment.WorldVerticesLength = verticesLength;
float[] vertices = GetFloatArray(map, "vertices", 1);
float scale = Scale;
if (verticesLength == vertices.Length) {
if (scale != 1) {
for (int i = 0; i < vertices.Length; i++) {
vertices[i] *= scale;
}
}
attachment.vertices = vertices;
return;
}
ExposedList<float> weights = new ExposedList<float>(verticesLength * 3 * 3);
ExposedList<int> bones = new ExposedList<int>(verticesLength * 3);
for (int i = 0, n = vertices.Length; i < n;) {
int boneCount = (int)vertices[i++];
bones.Add(boneCount);
for (int nn = i + boneCount * 4; i < nn; i += 4) {
bones.Add((int)vertices[i]);
weights.Add(vertices[i + 1] * this.Scale);
weights.Add(vertices[i + 2] * this.Scale);
weights.Add(vertices[i + 3]);
}
}
attachment.bones = bones.ToArray();
attachment.vertices = weights.ToArray();
}
private void ReadAnimation (Dictionary<string, Object> map, string name, SkeletonData skeletonData) {
var scale = this.Scale;
var timelines = new ExposedList<Timeline>();
float duration = 0;
// Slot timelines.
if (map.ContainsKey("slots")) {
foreach (KeyValuePair<string, Object> entry in (Dictionary<string, Object>)map["slots"]) {
string slotName = entry.Key;
int slotIndex = skeletonData.FindSlotIndex(slotName);
var timelineMap = (Dictionary<string, Object>)entry.Value;
foreach (KeyValuePair<string, Object> timelineEntry in timelineMap) {
var values = (List<Object>)timelineEntry.Value;
var timelineName = (string)timelineEntry.Key;
if (timelineName == "attachment") {
var timeline = new AttachmentTimeline(values.Count);
timeline.slotIndex = slotIndex;
int frameIndex = 0;
foreach (Dictionary<string, Object> valueMap in values) {
float time = GetFloat(valueMap, "time", 0);
timeline.SetFrame(frameIndex++, time, (string)valueMap["name"]);
}
timelines.Add(timeline);
duration = Math.Max(duration, timeline.frames[timeline.FrameCount - 1]);
} else if (timelineName == "color") {
var timeline = new ColorTimeline(values.Count);
timeline.slotIndex = slotIndex;
int frameIndex = 0;
foreach (Dictionary<string, Object> valueMap in values) {
float time = GetFloat(valueMap, "time", 0);
string c = (string)valueMap["color"];
timeline.SetFrame(frameIndex, time, ToColor(c, 0), ToColor(c, 1), ToColor(c, 2), ToColor(c, 3));
ReadCurve(valueMap, timeline, frameIndex);
frameIndex++;
}
timelines.Add(timeline);
duration = Math.Max(duration, timeline.frames[(timeline.FrameCount - 1) * ColorTimeline.ENTRIES]);
} else if (timelineName == "twoColor") {
var timeline = new TwoColorTimeline(values.Count);
timeline.slotIndex = slotIndex;
int frameIndex = 0;
foreach (Dictionary<string, Object> valueMap in values) {
float time = GetFloat(valueMap, "time", 0);
string light = (string)valueMap["light"];
string dark = (string)valueMap["dark"];
timeline.SetFrame(frameIndex, time, ToColor(light, 0), ToColor(light, 1), ToColor(light, 2), ToColor(light, 3),
ToColor(dark, 0, 6), ToColor(dark, 1, 6), ToColor(dark, 2, 6));
ReadCurve(valueMap, timeline, frameIndex);
frameIndex++;
}
timelines.Add(timeline);
duration = Math.Max(duration, timeline.frames[(timeline.FrameCount - 1) * TwoColorTimeline.ENTRIES]);
} else
throw new Exception("Invalid timeline type for a slot: " + timelineName + " (" + slotName + ")");
}
}
}
// Bone timelines.
if (map.ContainsKey("bones")) {
foreach (KeyValuePair<string, Object> entry in (Dictionary<string, Object>)map["bones"]) {
string boneName = entry.Key;
int boneIndex = skeletonData.FindBoneIndex(boneName);
if (boneIndex == -1) throw new Exception("Bone not found: " + boneName);
var timelineMap = (Dictionary<string, Object>)entry.Value;
foreach (KeyValuePair<string, Object> timelineEntry in timelineMap) {
var values = (List<Object>)timelineEntry.Value;
var timelineName = (string)timelineEntry.Key;
if (timelineName == "rotate") {
var timeline = new RotateTimeline(values.Count);
timeline.boneIndex = boneIndex;
int frameIndex = 0;
foreach (Dictionary<string, Object> valueMap in values) {
timeline.SetFrame(frameIndex, GetFloat(valueMap, "time", 0), GetFloat(valueMap, "angle", 0));
ReadCurve(valueMap, timeline, frameIndex);
frameIndex++;
}
timelines.Add(timeline);
duration = Math.Max(duration, timeline.frames[(timeline.FrameCount - 1) * RotateTimeline.ENTRIES]);
} else if (timelineName == "translate" || timelineName == "scale" || timelineName == "shear") {
TranslateTimeline timeline;
float timelineScale = 1, defaultValue = 0;
if (timelineName == "scale") {
timeline = new ScaleTimeline(values.Count);
defaultValue = 1;
}
else if (timelineName == "shear")
timeline = new ShearTimeline(values.Count);
else {
timeline = new TranslateTimeline(values.Count);
timelineScale = scale;
}
timeline.boneIndex = boneIndex;
int frameIndex = 0;
foreach (Dictionary<string, Object> valueMap in values) {
float time = GetFloat(valueMap, "time", 0);
float x = GetFloat(valueMap, "x", defaultValue);
float y = GetFloat(valueMap, "y", defaultValue);
timeline.SetFrame(frameIndex, time, x * timelineScale, y * timelineScale);
ReadCurve(valueMap, timeline, frameIndex);
frameIndex++;
}
timelines.Add(timeline);
duration = Math.Max(duration, timeline.frames[(timeline.FrameCount - 1) * TranslateTimeline.ENTRIES]);
} else
throw new Exception("Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")");
}
}
}
// IK constraint timelines.
if (map.ContainsKey("ik")) {
foreach (KeyValuePair<string, Object> constraintMap in (Dictionary<string, Object>)map["ik"]) {
IkConstraintData constraint = skeletonData.FindIkConstraint(constraintMap.Key);
var values = (List<Object>)constraintMap.Value;
var timeline = new IkConstraintTimeline(values.Count);
timeline.ikConstraintIndex = skeletonData.ikConstraints.IndexOf(constraint);
int frameIndex = 0;
foreach (Dictionary<string, Object> valueMap in values) {
timeline.SetFrame(frameIndex, GetFloat(valueMap, "time", 0), GetFloat(valueMap, "mix", 1),
GetFloat(valueMap, "softness", 0) * scale, GetBoolean(valueMap, "bendPositive", true) ? 1 : -1,
GetBoolean(valueMap, "compress", false), GetBoolean(valueMap, "stretch", false));
ReadCurve(valueMap, timeline, frameIndex);
frameIndex++;
}
timelines.Add(timeline);
duration = Math.Max(duration, timeline.frames[(timeline.FrameCount - 1) * IkConstraintTimeline.ENTRIES]);
}
}
// Transform constraint timelines.
if (map.ContainsKey("transform")) {
foreach (KeyValuePair<string, Object> constraintMap in (Dictionary<string, Object>)map["transform"]) {
TransformConstraintData constraint = skeletonData.FindTransformConstraint(constraintMap.Key);
var values = (List<Object>)constraintMap.Value;
var timeline = new TransformConstraintTimeline(values.Count);
timeline.transformConstraintIndex = skeletonData.transformConstraints.IndexOf(constraint);
int frameIndex = 0;
foreach (Dictionary<string, Object> valueMap in values) {
timeline.SetFrame(frameIndex, GetFloat(valueMap, "time", 0), GetFloat(valueMap, "rotateMix", 1),
GetFloat(valueMap, "translateMix", 1), GetFloat(valueMap, "scaleMix", 1), GetFloat(valueMap, "shearMix", 1));
ReadCurve(valueMap, timeline, frameIndex);
frameIndex++;
}
timelines.Add(timeline);
duration = Math.Max(duration, timeline.frames[(timeline.FrameCount - 1) * TransformConstraintTimeline.ENTRIES]);
}
}
// Path constraint timelines.
if (map.ContainsKey("path")) {
foreach (KeyValuePair<string, Object> constraintMap in (Dictionary<string, Object>)map["path"]) {
int index = skeletonData.FindPathConstraintIndex(constraintMap.Key);
if (index == -1) throw new Exception("Path constraint not found: " + constraintMap.Key);
PathConstraintData data = skeletonData.pathConstraints.Items[index];
var timelineMap = (Dictionary<string, Object>)constraintMap.Value;
foreach (KeyValuePair<string, Object> timelineEntry in timelineMap) {
var values = (List<Object>)timelineEntry.Value;
var timelineName = (string)timelineEntry.Key;
if (timelineName == "position" || timelineName == "spacing") {
PathConstraintPositionTimeline timeline;
float timelineScale = 1;
if (timelineName == "spacing") {
timeline = new PathConstraintSpacingTimeline(values.Count);
if (data.spacingMode == SpacingMode.Length || data.spacingMode == SpacingMode.Fixed) timelineScale = scale;
}
else {
timeline = new PathConstraintPositionTimeline(values.Count);
if (data.positionMode == PositionMode.Fixed) timelineScale = scale;
}
timeline.pathConstraintIndex = index;
int frameIndex = 0;
foreach (Dictionary<string, Object> valueMap in values) {
timeline.SetFrame(frameIndex, GetFloat(valueMap, "time", 0), GetFloat(valueMap, timelineName, 0) * timelineScale);
ReadCurve(valueMap, timeline, frameIndex);
frameIndex++;
}
timelines.Add(timeline);
duration = Math.Max(duration, timeline.frames[(timeline.FrameCount - 1) * PathConstraintPositionTimeline.ENTRIES]);
}
else if (timelineName == "mix") {
PathConstraintMixTimeline timeline = new PathConstraintMixTimeline(values.Count);
timeline.pathConstraintIndex = index;
int frameIndex = 0;
foreach (Dictionary<string, Object> valueMap in values) {
timeline.SetFrame(frameIndex, GetFloat(valueMap, "time", 0), GetFloat(valueMap, "rotateMix", 1),
GetFloat(valueMap, "translateMix", 1));
ReadCurve(valueMap, timeline, frameIndex);
frameIndex++;
}
timelines.Add(timeline);
duration = Math.Max(duration, timeline.frames[(timeline.FrameCount - 1) * PathConstraintMixTimeline.ENTRIES]);
}
}
}
}
// Deform timelines.
if (map.ContainsKey("deform")) {
foreach (KeyValuePair<string, Object> deformMap in (Dictionary<string, Object>)map["deform"]) {
Skin skin = skeletonData.FindSkin(deformMap.Key);
foreach (KeyValuePair<string, Object> slotMap in (Dictionary<string, Object>)deformMap.Value) {
int slotIndex = skeletonData.FindSlotIndex(slotMap.Key);
if (slotIndex == -1) throw new Exception("Slot not found: " + slotMap.Key);
foreach (KeyValuePair<string, Object> timelineMap in (Dictionary<string, Object>)slotMap.Value) {
var values = (List<Object>)timelineMap.Value;
VertexAttachment attachment = (VertexAttachment)skin.GetAttachment(slotIndex, timelineMap.Key);
if (attachment == null) throw new Exception("Deform attachment not found: " + timelineMap.Key);
bool weighted = attachment.bones != null;
float[] vertices = attachment.vertices;
int deformLength = weighted ? vertices.Length / 3 * 2 : vertices.Length;
var timeline = new DeformTimeline(values.Count);
timeline.slotIndex = slotIndex;
timeline.attachment = attachment;
int frameIndex = 0;
foreach (Dictionary<string, Object> valueMap in values) {
float[] deform;
if (!valueMap.ContainsKey("vertices")) {
deform = weighted ? new float[deformLength] : vertices;
} else {
deform = new float[deformLength];
int start = GetInt(valueMap, "offset", 0);
float[] verticesValue = GetFloatArray(valueMap, "vertices", 1);
Array.Copy(verticesValue, 0, deform, start, verticesValue.Length);
if (scale != 1) {
for (int i = start, n = i + verticesValue.Length; i < n; i++)
deform[i] *= scale;
}
if (!weighted) {
for (int i = 0; i < deformLength; i++)
deform[i] += vertices[i];
}
}
timeline.SetFrame(frameIndex, GetFloat(valueMap, "time", 0), deform);
ReadCurve(valueMap, timeline, frameIndex);
frameIndex++;
}
timelines.Add(timeline);
duration = Math.Max(duration, timeline.frames[timeline.FrameCount - 1]);
}
}
}
}
// Draw order timeline.
if (map.ContainsKey("drawOrder") || map.ContainsKey("draworder")) {
var values = (List<Object>)map[map.ContainsKey("drawOrder") ? "drawOrder" : "draworder"];
var timeline = new DrawOrderTimeline(values.Count);
int slotCount = skeletonData.slots.Count;
int frameIndex = 0;
foreach (Dictionary<string, Object> drawOrderMap in values) {
int[] drawOrder = null;
if (drawOrderMap.ContainsKey("offsets")) {
drawOrder = new int[slotCount];
for (int i = slotCount - 1; i >= 0; i--)
drawOrder[i] = -1;
var offsets = (List<Object>)drawOrderMap["offsets"];
int[] unchanged = new int[slotCount - offsets.Count];
int originalIndex = 0, unchangedIndex = 0;
foreach (Dictionary<string, Object> offsetMap in offsets) {
int slotIndex = skeletonData.FindSlotIndex((string)offsetMap["slot"]);
if (slotIndex == -1) throw new Exception("Slot not found: " + offsetMap["slot"]);
// Collect unchanged items.
while (originalIndex != slotIndex)
unchanged[unchangedIndex++] = originalIndex++;
// Set changed items.
int index = originalIndex + (int)(float)offsetMap["offset"];
drawOrder[index] = originalIndex++;
}
// Collect remaining unchanged items.
while (originalIndex < slotCount)
unchanged[unchangedIndex++] = originalIndex++;
// Fill in unchanged items.
for (int i = slotCount - 1; i >= 0; i--)
if (drawOrder[i] == -1) drawOrder[i] = unchanged[--unchangedIndex];
}
timeline.SetFrame(frameIndex++, GetFloat(drawOrderMap, "time", 0), drawOrder);
}
timelines.Add(timeline);
duration = Math.Max(duration, timeline.frames[timeline.FrameCount - 1]);
}
// Event timeline.
if (map.ContainsKey("events")) {
var eventsMap = (List<Object>)map["events"];
var timeline = new EventTimeline(eventsMap.Count);
int frameIndex = 0;
foreach (Dictionary<string, Object> eventMap in eventsMap) {
EventData eventData = skeletonData.FindEvent((string)eventMap["name"]);
if (eventData == null) throw new Exception("Event not found: " + eventMap["name"]);
var e = new Event(GetFloat(eventMap, "time", 0), eventData) {
intValue = GetInt(eventMap, "int", eventData.Int),
floatValue = GetFloat(eventMap, "float", eventData.Float),
stringValue = GetString(eventMap, "string", eventData.String)
};
if (e.data.AudioPath != null) {
e.volume = GetFloat(eventMap, "volume", eventData.Volume);
e.balance = GetFloat(eventMap, "balance", eventData.Balance);
}
timeline.SetFrame(frameIndex++, e);
}
timelines.Add(timeline);
duration = Math.Max(duration, timeline.frames[timeline.FrameCount - 1]);
}
timelines.TrimExcess();
skeletonData.animations.Add(new Animation(name, timelines, duration));
}
static void ReadCurve (Dictionary<string, Object> valueMap, CurveTimeline timeline, int frameIndex) {
if (!valueMap.ContainsKey("curve"))
return;
Object curveObject = valueMap["curve"];
if (curveObject is string)
timeline.SetStepped(frameIndex);
else
timeline.SetCurve(frameIndex, (float)curveObject, GetFloat(valueMap, "c2", 0), GetFloat(valueMap, "c3", 1), GetFloat(valueMap, "c4", 1));
}
internal class LinkedMesh {
internal string parent, skin;
internal int slotIndex;
internal MeshAttachment mesh;
internal bool inheritDeform;
public LinkedMesh (MeshAttachment mesh, string skin, int slotIndex, string parent, bool inheritDeform) {
this.mesh = mesh;
this.skin = skin;
this.slotIndex = slotIndex;
this.parent = parent;
this.inheritDeform = inheritDeform;
}
}
static float[] GetFloatArray(Dictionary<string, Object> map, string name, float scale) {
var list = (List<Object>)map[name];
var values = new float[list.Count];
if (scale == 1) {
for (int i = 0, n = list.Count; i < n; i++)
values[i] = (float)list[i];
} else {
for (int i = 0, n = list.Count; i < n; i++)
values[i] = (float)list[i] * scale;
}
return values;
}
static int[] GetIntArray(Dictionary<string, Object> map, string name) {
var list = (List<Object>)map[name];
var values = new int[list.Count];
for (int i = 0, n = list.Count; i < n; i++)
values[i] = (int)(float)list[i];
return values;
}
static float GetFloat(Dictionary<string, Object> map, string name, float defaultValue) {
if (!map.ContainsKey(name))
return defaultValue;
return (float)map[name];
}
static int GetInt(Dictionary<string, Object> map, string name, int defaultValue) {
if (!map.ContainsKey(name))
return defaultValue;
return (int)(float)map[name];
}
static bool GetBoolean(Dictionary<string, Object> map, string name, bool defaultValue) {
if (!map.ContainsKey(name))
return defaultValue;
return (bool)map[name];
}
static string GetString(Dictionary<string, Object> map, string name, string defaultValue) {
if (!map.ContainsKey(name))
return defaultValue;
return (string)map[name];
}
static float ToColor(string hexString, int colorIndex, int expectedLength = 8) {
if (hexString.Length != expectedLength)
throw new ArgumentException("Color hexidecimal length must be " + expectedLength + ", recieved: " + hexString, "hexString");
return Convert.ToInt32(hexString.Substring(colorIndex * 2, 2), 16) / (float)255;
}
}
}

View File

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

View File

@@ -1,193 +0,0 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
using System;
using System.Collections;
using System.Collections.Generic;
using Spine.Collections;
namespace Spine {
/// <summary>Stores attachments by slot index and attachment name.
/// <para>See SkeletonData <see cref="Spine.SkeletonData.DefaultSkin"/>, Skeleton <see cref="Spine.Skeleton.Skin"/>, and
/// <a href="http://esotericsoftware.com/spine-runtime-skins">Runtime skins</a> in the Spine Runtimes Guide.</para>
/// </summary>
public class Skin {
internal string name;
private OrderedDictionary<SkinEntry, Attachment> attachments = new OrderedDictionary<SkinEntry, Attachment>(SkinEntryComparer.Instance);
internal readonly ExposedList<BoneData> bones = new ExposedList<BoneData>();
internal readonly ExposedList<ConstraintData> constraints = new ExposedList<ConstraintData>();
public string Name { get { return name; } }
public OrderedDictionary<SkinEntry, Attachment> Attachments { get { return attachments; } }
public ExposedList<BoneData> Bones { get { return bones; } }
public ExposedList<ConstraintData> Constraints { get { return constraints; } }
public Skin (string name) {
if (name == null) throw new ArgumentNullException("name", "name cannot be null.");
this.name = name;
}
/// <summary>Adds an attachment to the skin for the specified slot index and name.
/// If the name already exists for the slot, the previous value is replaced.</summary>
public void SetAttachment (int slotIndex, string name, Attachment attachment) {
if (attachment == null) throw new ArgumentNullException("attachment", "attachment cannot be null.");
if (slotIndex < 0) throw new ArgumentNullException("slotIndex", "slotIndex must be >= 0.");
attachments[new SkinEntry(slotIndex, name, attachment)] = attachment;
}
///<summary>Adds all attachments, bones, and constraints from the specified skin to this skin.</summary>
public void AddSkin (Skin skin) {
foreach (BoneData data in skin.bones)
if (!bones.Contains(data)) bones.Add(data);
foreach (ConstraintData data in skin.constraints)
if (!constraints.Contains(data)) constraints.Add(data);
foreach (SkinEntry entry in skin.attachments.Keys)
SetAttachment(entry.SlotIndex, entry.Name, entry.Attachment);
}
///<summary>Adds all attachments from the specified skin to this skin. Attachments are deep copied.</summary>
public void CopySkin (Skin skin) {
foreach (BoneData data in skin.bones)
if (!bones.Contains(data)) bones.Add(data);
foreach (ConstraintData data in skin.constraints)
if (!constraints.Contains(data)) constraints.Add(data);
foreach (SkinEntry entry in skin.attachments.Keys) {
if (entry.Attachment is MeshAttachment)
SetAttachment(entry.SlotIndex, entry.Name,
entry.Attachment != null ? ((MeshAttachment)entry.Attachment).NewLinkedMesh() : null);
else
SetAttachment(entry.SlotIndex, entry.Name, entry.Attachment != null ? entry.Attachment.Copy() : null);
}
}
/// <summary>Returns the attachment for the specified slot index and name, or null.</summary>
/// <returns>May be null.</returns>
public Attachment GetAttachment (int slotIndex, string name) {
var lookup = new SkinEntry(slotIndex, name, null);
Attachment attachment = null;
bool containsKey = attachments.TryGetValue(lookup, out attachment);
return containsKey ? attachment : null;
}
/// <summary> Removes the attachment in the skin for the specified slot index and name, if any.</summary>
public void RemoveAttachment (int slotIndex, string name) {
if (slotIndex < 0) throw new ArgumentOutOfRangeException("slotIndex", "slotIndex must be >= 0");
var lookup = new SkinEntry(slotIndex, name, null);
attachments.Remove(lookup);
}
///<summary>Returns all attachments contained in this skin.</summary>
public ICollection<SkinEntry> GetAttachments () {
return this.attachments.Keys;
}
/// <summary>Returns all attachments in this skin for the specified slot index.</summary>
/// <param name="slotIndex">The target slotIndex. To find the slot index, use <see cref="Spine.Skeleton.FindSlotIndex"/> or <see cref="Spine.SkeletonData.FindSlotIndex"/>
public void GetAttachments (int slotIndex, List<SkinEntry> attachments) {
foreach (SkinEntry entry in this.attachments.Keys)
if (entry.SlotIndex == slotIndex) attachments.Add(entry);
}
///<summary>Clears all attachments, bones, and constraints.</summary>
public void Clear () {
attachments.Clear();
bones.Clear();
constraints.Clear();
}
override public string ToString () {
return name;
}
/// <summary>Attach all attachments from this skin if the corresponding attachment from the old skin is currently attached.</summary>
internal void AttachAll (Skeleton skeleton, Skin oldSkin) {
foreach (SkinEntry entry in oldSkin.attachments.Keys) {
int slotIndex = entry.SlotIndex;
Slot slot = skeleton.slots.Items[slotIndex];
if (slot.Attachment == entry.Attachment) {
Attachment attachment = GetAttachment(slotIndex, entry.Name);
if (attachment != null) slot.Attachment = attachment;
}
}
}
/// <summary>Stores an entry in the skin consisting of the slot index, name, and attachment.</summary>
public struct SkinEntry {
private readonly int slotIndex;
private readonly string name;
private readonly Attachment attachment;
internal readonly int hashCode;
public SkinEntry (int slotIndex, string name, Attachment attachment) {
this.slotIndex = slotIndex;
this.name = name;
this.attachment = attachment;
this.hashCode = this.name.GetHashCode() + this.slotIndex * 37;
}
public int SlotIndex {
get {
return slotIndex;
}
}
/// <summary>The name the attachment is associated with, equivalent to the skin placeholder name in the Spine editor.</summary>
public String Name {
get {
return name;
}
}
public Attachment Attachment {
get {
return attachment;
}
}
}
// Avoids boxing in the dictionary and is necessary to omit entry.attachment in the comparison.
class SkinEntryComparer : IEqualityComparer<SkinEntry> {
internal static readonly SkinEntryComparer Instance = new SkinEntryComparer();
bool IEqualityComparer<SkinEntry>.Equals (SkinEntry e1, SkinEntry e2) {
if (e1.SlotIndex != e2.SlotIndex) return false;
if (!string.Equals(e1.Name, e2.Name, StringComparison.Ordinal)) return false;
return true;
}
int IEqualityComparer<SkinEntry>.GetHashCode (SkinEntry e) {
return e.Name.GetHashCode() + e.SlotIndex * 37;
}
}
}
}

View File

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

View File

@@ -1,196 +0,0 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
using System;
namespace Spine {
/// <summary>
/// Stores a slot's current pose. Slots organize attachments for {@link Skeleton#drawOrder} purposes and provide a place to store
/// state for an attachment.State cannot be stored in an attachment itself because attachments are stateless and may be shared
/// across multiple skeletons.
/// </summary>
public class Slot {
internal SlotData data;
internal Bone bone;
internal float r, g, b, a;
internal float r2, g2, b2;
internal bool hasSecondColor;
internal Attachment attachment;
internal float attachmentTime;
internal ExposedList<float> deform = new ExposedList<float>();
internal int attachmentState;
public Slot (SlotData data, Bone bone) {
if (data == null) throw new ArgumentNullException("data", "data cannot be null.");
if (bone == null) throw new ArgumentNullException("bone", "bone cannot be null.");
this.data = data;
this.bone = bone;
// darkColor = data.darkColor == null ? null : new Color();
if (data.hasSecondColor) {
r2 = g2 = b2 = 0;
}
SetToSetupPose();
}
/// <summary>Copy constructor.</summary>
public Slot(Slot slot, Bone bone) {
if (slot == null) throw new ArgumentNullException("slot", "slot cannot be null.");
if (bone == null) throw new ArgumentNullException("bone", "bone cannot be null.");
data = slot.data;
this.bone = bone;
r = slot.r;
g = slot.g;
b = slot.b;
a = slot.a;
// darkColor = slot.darkColor == null ? null : new Color(slot.darkColor);
if (slot.hasSecondColor) {
r2 = slot.r2;
g2 = slot.g2;
b2 = slot.b2;
} else {
r2 = g2 = b2 = 0;
}
hasSecondColor = slot.hasSecondColor;
attachment = slot.attachment;
attachmentTime = slot.attachmentTime;
deform.AddRange(slot.deform);
}
/// <summary>The slot's setup pose data.</summary>
public SlotData Data { get { return data; } }
/// <summary>The bone this slot belongs to.</summary>
public Bone Bone { get { return bone; } }
/// <summary>The skeleton this slot belongs to.</summary>
public Skeleton Skeleton { get { return bone.skeleton; } }
/// <summary>The color used to tint the slot's attachment. If <see cref="HasSecondColor"/> is set, this is used as the light color for two
/// color tinting.</summary>
public float R { get { return r; } set { r = value; } }
/// <summary>The color used to tint the slot's attachment. If <see cref="HasSecondColor"/> is set, this is used as the light color for two
/// color tinting.</summary>
public float G { get { return g; } set { g = value; } }
/// <summary>The color used to tint the slot's attachment. If <see cref="HasSecondColor"/> is set, this is used as the light color for two
/// color tinting.</summary>
public float B { get { return b; } set { b = value; } }
/// <summary>The color used to tint the slot's attachment. If <see cref="HasSecondColor"/> is set, this is used as the light color for two
/// color tinting.</summary>
public float A { get { return a; } set { a = value; } }
public void ClampColor() {
r = MathUtils.Clamp(r, 0, 1);
g = MathUtils.Clamp(g, 0, 1);
b = MathUtils.Clamp(b, 0, 1);
a = MathUtils.Clamp(a, 0, 1);
}
/// <summary>The dark color used to tint the slot's attachment for two color tinting, ignored if two color tinting is not used.</summary>
/// <seealso cref="HasSecondColor"/>
public float R2 { get { return r2; } set { r2 = value; } }
/// <summary>The dark color used to tint the slot's attachment for two color tinting, ignored if two color tinting is not used.</summary>
/// <seealso cref="HasSecondColor"/>
public float G2 { get { return g2; } set { g2 = value; } }
/// <summary>The dark color used to tint the slot's attachment for two color tinting, ignored if two color tinting is not used.</summary>
/// <seealso cref="HasSecondColor"/>
public float B2 { get { return b2; } set { b2 = value; } }
/// <summary>Whether R2 G2 B2 are used to tint the slot's attachment for two color tinting. False if two color tinting is not used.</summary>
public bool HasSecondColor { get { return data.hasSecondColor; } set { data.hasSecondColor = value; } }
public void ClampSecondColor () {
r2 = MathUtils.Clamp(r2, 0, 1);
g2 = MathUtils.Clamp(g2, 0, 1);
b2 = MathUtils.Clamp(b2, 0, 1);
}
public Attachment Attachment {
/// <summary>The current attachment for the slot, or null if the slot has no attachment.</summary>
get { return attachment; }
/// <summary>
/// Sets the slot's attachment and, if the attachment changed, resets <see cref="AttachmentTime"/> and clears
/// <see cref="Deform">.</summary>
/// <param name="value">May be null.</param>
set {
if (attachment == value) return;
attachment = value;
attachmentTime = bone.skeleton.time;
deform.Clear(false);
}
}
/// <summary> The time that has elapsed since the last time the attachment was set or cleared. Relies on Skeleton
/// <see cref="Skeleton.Time"/></summary>
public float AttachmentTime {
get { return bone.skeleton.time - attachmentTime; }
set { attachmentTime = bone.skeleton.time - value; }
}
/// <summary> Vertices to deform the slot's attachment. For an unweighted mesh, the entries are local positions for each vertex. For a
/// weighted mesh, the entries are an offset for each vertex which will be added to the mesh's local vertex positions.
/// <para />
/// See <see cref="VertexAttachment.ComputeWorldVertices(Slot, int, int, float[], int, int)"/> and <see cref="DeformTimeline"/>.</summary>
public ExposedList<float> Deform {
get {
return deform;
}
set {
if (deform == null) throw new ArgumentNullException("deform", "deform cannot be null.");
deform = value;
}
}
/// <summary>Sets this slot to the setup pose.</summary>
public void SetToSetupPose () {
r = data.r;
g = data.g;
b = data.b;
a = data.a;
// if (darkColor != null) darkColor.set(data.darkColor);
if (HasSecondColor) {
r2 = data.r2;
g2 = data.g2;
b2 = data.b2;
}
if (data.attachmentName == null)
Attachment = null;
else {
attachment = null;
Attachment = bone.skeleton.GetAttachment(data.index, data.attachmentName);
}
}
override public string ToString () {
return data.name;
}
}
}

View File

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

View File

@@ -1,77 +0,0 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
using System;
namespace Spine {
public class SlotData {
internal int index;
internal string name;
internal BoneData boneData;
internal float r = 1, g = 1, b = 1, a = 1;
internal float r2 = 0, g2 = 0, b2 = 0;
internal bool hasSecondColor = false;
internal string attachmentName;
internal BlendMode blendMode;
/// <summary>The index of the slot in <see cref="Skeleton.Slots"/>.</summary>
public int Index { get { return index; } }
/// <summary>The name of the slot, which is unique across all slots in the skeleton.</summary>
public string Name { get { return name; } }
/// <summary>The bone this slot belongs to.</summary>
public BoneData BoneData { get { return boneData; } }
public float R { get { return r; } set { r = value; } }
public float G { get { return g; } set { g = value; } }
public float B { get { return b; } set { b = value; } }
public float A { get { return a; } set { a = value; } }
public float R2 { get { return r2; } set { r2 = value; } }
public float G2 { get { return g2; } set { g2 = value; } }
public float B2 { get { return b2; } set { b2 = value; } }
public bool HasSecondColor { get { return hasSecondColor; } set { hasSecondColor = value; } }
/// <summary>The name of the attachment that is visible for this slot in the setup pose, or null if no attachment is visible.</summary>
public String AttachmentName { get { return attachmentName; } set { attachmentName = value; } }
/// <summary>The blend mode for drawing the slot's attachment.</summary>
public BlendMode BlendMode { get { return blendMode; } set { blendMode = value; } }
public SlotData (int index, String name, BoneData boneData) {
if (index < 0) throw new ArgumentException ("index must be >= 0.", "index");
if (name == null) throw new ArgumentNullException("name", "name cannot be null.");
if (boneData == null) throw new ArgumentNullException("boneData", "boneData cannot be null.");
this.index = index;
this.name = name;
this.boneData = boneData;
}
override public string ToString () {
return name;
}
}
}

View File

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

View File

@@ -1,262 +0,0 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
using System;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using CompatibilityProblemInfo = Spine.Unity.SkeletonDataCompatibility.CompatibilityProblemInfo;
namespace Spine.Unity {
[CreateAssetMenu(fileName = "New SkeletonDataAsset", menuName = "Spine/SkeletonData Asset")]
public class SkeletonDataAsset : ScriptableObject {
#region Inspector
public AtlasAssetBase[] atlasAssets = new AtlasAssetBase[0];
#if SPINE_TK2D
public tk2dSpriteCollectionData spriteCollection;
public float scale = 1f;
#else
public float scale = 0.01f;
#endif
public TextAsset skeletonJSON;
public bool isUpgradingBlendModeMaterials = false;
public BlendModeMaterials blendModeMaterials = new BlendModeMaterials();
[Tooltip("Use SkeletonDataModifierAssets to apply changes to the SkeletonData after being loaded, such as apply blend mode Materials to Attachments under slots with special blend modes.")]
public List<SkeletonDataModifierAsset> skeletonDataModifiers = new List<SkeletonDataModifierAsset>();
[SpineAnimation(includeNone: false)]
public string[] fromAnimation = new string[0];
[SpineAnimation(includeNone: false)]
public string[] toAnimation = new string[0];
public float[] duration = new float[0];
public float defaultMix;
public RuntimeAnimatorController controller;
public bool IsLoaded { get { return this.skeletonData != null; } }
void Reset () {
Clear();
}
#endregion
SkeletonData skeletonData;
AnimationStateData stateData;
#region Runtime Instantiation
/// <summary>
/// Creates a runtime SkeletonDataAsset.</summary>
public static SkeletonDataAsset CreateRuntimeInstance (TextAsset skeletonDataFile, AtlasAssetBase atlasAsset, bool initialize, float scale = 0.01f) {
return CreateRuntimeInstance(skeletonDataFile, new [] {atlasAsset}, initialize, scale);
}
/// <summary>
/// Creates a runtime SkeletonDataAsset.</summary>
public static SkeletonDataAsset CreateRuntimeInstance (TextAsset skeletonDataFile, AtlasAssetBase[] atlasAssets, bool initialize, float scale = 0.01f) {
SkeletonDataAsset skeletonDataAsset = ScriptableObject.CreateInstance<SkeletonDataAsset>();
skeletonDataAsset.Clear();
skeletonDataAsset.skeletonJSON = skeletonDataFile;
skeletonDataAsset.atlasAssets = atlasAssets;
skeletonDataAsset.scale = scale;
if (initialize)
skeletonDataAsset.GetSkeletonData(true);
return skeletonDataAsset;
}
#endregion
/// <summary>Clears the loaded SkeletonData and AnimationStateData. Use this to force a reload for the next time GetSkeletonData is called.</summary>
public void Clear () {
skeletonData = null;
stateData = null;
}
public AnimationStateData GetAnimationStateData () {
if (stateData != null)
return stateData;
GetSkeletonData(false);
return stateData;
}
/// <summary>Loads, caches and returns the SkeletonData from the skeleton data file. Returns the cached SkeletonData after the first time it is called. Pass false to prevent direct errors from being logged.</summary>
public SkeletonData GetSkeletonData (bool quiet) {
if (skeletonJSON == null) {
if (!quiet)
Debug.LogError("Skeleton JSON file not set for SkeletonData asset: " + name, this);
Clear();
return null;
}
// Disabled to support attachmentless/skinless SkeletonData.
// if (atlasAssets == null) {
// atlasAssets = new AtlasAsset[0];
// if (!quiet)
// Debug.LogError("Atlas not set for SkeletonData asset: " + name, this);
// Clear();
// return null;
// }
// #if !SPINE_TK2D
// if (atlasAssets.Length == 0) {
// Clear();
// return null;
// }
// #else
// if (atlasAssets.Length == 0 && spriteCollection == null) {
// Clear();
// return null;
// }
// #endif
if (skeletonData != null)
return skeletonData;
AttachmentLoader attachmentLoader;
float skeletonDataScale;
Atlas[] atlasArray = this.GetAtlasArray();
#if !SPINE_TK2D
attachmentLoader = (atlasArray.Length == 0) ? (AttachmentLoader)new RegionlessAttachmentLoader() : (AttachmentLoader)new AtlasAttachmentLoader(atlasArray);
skeletonDataScale = scale;
#else
if (spriteCollection != null) {
attachmentLoader = new Spine.Unity.TK2D.SpriteCollectionAttachmentLoader(spriteCollection);
skeletonDataScale = (1.0f / (spriteCollection.invOrthoSize * spriteCollection.halfTargetHeight) * scale);
} else {
if (atlasArray.Length == 0) {
Reset();
if (!quiet) Debug.LogError("Atlas not set for SkeletonData asset: " + name, this);
return null;
}
attachmentLoader = new AtlasAttachmentLoader(atlasArray);
skeletonDataScale = scale;
}
#endif
bool hasBinaryExtension = skeletonJSON.name.ToLower().Contains(".skel");
SkeletonData loadedSkeletonData = null;
try {
if (hasBinaryExtension)
loadedSkeletonData = SkeletonDataAsset.ReadSkeletonData(skeletonJSON.bytes, attachmentLoader, skeletonDataScale);
else
loadedSkeletonData = SkeletonDataAsset.ReadSkeletonData(skeletonJSON.text, attachmentLoader, skeletonDataScale);
} catch (Exception ex) {
if (!quiet)
Debug.LogError("Error reading skeleton JSON file for SkeletonData asset: " + name + "\n" + ex.Message + "\n" + ex.StackTrace, skeletonJSON);
}
#if UNITY_EDITOR
if (loadedSkeletonData == null && !quiet && skeletonJSON != null) {
string problemDescription = null;
bool isSpineSkeletonData;
SkeletonDataCompatibility.VersionInfo fileVersion = SkeletonDataCompatibility.GetVersionInfo(skeletonJSON, out isSpineSkeletonData, ref problemDescription);
if (problemDescription != null) {
if (!quiet)
Debug.LogError(problemDescription, skeletonJSON);
return null;
}
CompatibilityProblemInfo compatibilityProblemInfo = SkeletonDataCompatibility.GetCompatibilityProblemInfo(fileVersion);
if (compatibilityProblemInfo != null) {
SkeletonDataCompatibility.DisplayCompatibilityProblem(compatibilityProblemInfo.DescriptionString(), skeletonJSON);
return null;
}
}
#endif
if (loadedSkeletonData == null)
return null;
if (skeletonDataModifiers != null) {
foreach (var modifier in skeletonDataModifiers) {
if (modifier != null && !(isUpgradingBlendModeMaterials && modifier is BlendModeMaterialsAsset)) {
modifier.Apply(loadedSkeletonData);
}
}
}
if (!isUpgradingBlendModeMaterials)
blendModeMaterials.ApplyMaterials(loadedSkeletonData);
this.InitializeWithData(loadedSkeletonData);
return skeletonData;
}
internal void InitializeWithData (SkeletonData sd) {
this.skeletonData = sd;
this.stateData = new AnimationStateData(skeletonData);
FillStateData();
}
public void FillStateData () {
if (stateData != null) {
stateData.defaultMix = defaultMix;
for (int i = 0, n = fromAnimation.Length; i < n; i++) {
if (fromAnimation[i].Length == 0 || toAnimation[i].Length == 0)
continue;
stateData.SetMix(fromAnimation[i], toAnimation[i], duration[i]);
}
}
}
internal Atlas[] GetAtlasArray () {
var returnList = new System.Collections.Generic.List<Atlas>(atlasAssets.Length);
for (int i = 0; i < atlasAssets.Length; i++) {
var aa = atlasAssets[i];
if (aa == null) continue;
var a = aa.GetAtlas();
if (a == null) continue;
returnList.Add(a);
}
return returnList.ToArray();
}
internal static SkeletonData ReadSkeletonData (byte[] bytes, AttachmentLoader attachmentLoader, float scale) {
using (var input = new MemoryStream(bytes)) {
var binary = new SkeletonBinary(attachmentLoader) {
Scale = scale
};
return binary.ReadSkeletonData(input);
}
}
internal static SkeletonData ReadSkeletonData (string text, AttachmentLoader attachmentLoader, float scale) {
var input = new StringReader(text);
var json = new SkeletonJson(attachmentLoader) {
Scale = scale
};
return json.ReadSkeletonData(input);
}
}
}

View File

@@ -1,20 +0,0 @@
fileFormatVersion: 2
guid: f1b3b4b945939a54ea0b23d3396115fb
timeCreated: 1536403985
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences:
- multiplyMaterialTemplate: {fileID: 2100000, guid: 53bf0ab317d032d418cf1252d68f51df,
type: 2}
- screenMaterialTemplate: {fileID: 2100000, guid: 73f0f46d3177c614baf0fa48d646a9be,
type: 2}
- additiveMaterialTemplate: {fileID: 2100000, guid: 4deba332d47209e4780b3c5fcf0e3745,
type: 2}
- skeletonJSON: {instanceID: 0}
- controller: {instanceID: 0}
executionOrder: 0
icon: {fileID: 2800000, guid: 68defdbc95b30a74a9ad396bfc9a2277, type: 3}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,215 +0,0 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using System;
#if UNITY_EDITOR
using System.Globalization;
using System.Text.RegularExpressions;
#endif
namespace Spine.Unity {
public static class SkeletonDataCompatibility {
#if UNITY_EDITOR
static readonly int[][] compatibleBinaryVersions = { new[] { 3, 8, 0 } };
static readonly int[][] compatibleJsonVersions = { new[] { 3, 8, 0 } };
static bool wasVersionDialogShown = false;
static readonly Regex jsonVersionRegex = new Regex(@"""spine""\s*:\s*""([^""]+)""", RegexOptions.CultureInvariant);
#endif
public enum SourceType {
Json,
Binary
}
[System.Serializable]
public class VersionInfo {
public string rawVersion = null;
public int[] version = null;
public SourceType sourceType;
}
[System.Serializable]
public class CompatibilityProblemInfo {
public VersionInfo actualVersion;
public int[][] compatibleVersions;
public string explicitProblemDescription = null;
public string DescriptionString () {
if (!string.IsNullOrEmpty(explicitProblemDescription))
return explicitProblemDescription;
string compatibleVersionString = "";
string optionalOr = null;
foreach (int[] version in compatibleVersions) {
compatibleVersionString += string.Format("{0}{1}.{2}", optionalOr, version[0], version[1]);
optionalOr = " or ";
}
return string.Format("Skeleton data could not be loaded. Data version: {0}. Required version: {1}.\nPlease re-export skeleton data with Spine {1} or change runtime to version {2}.{3}.",
actualVersion.rawVersion, compatibleVersionString, actualVersion.version[0], actualVersion.version[1]);
}
}
#if UNITY_EDITOR
public static VersionInfo GetVersionInfo (TextAsset asset, out bool isSpineSkeletonData, ref string problemDescription) {
isSpineSkeletonData = false;
if (asset == null)
return null;
VersionInfo fileVersion = new VersionInfo();
bool hasBinaryExtension = asset.name.Contains(".skel");
fileVersion.sourceType = hasBinaryExtension ? SourceType.Binary : SourceType.Json;
bool isJsonFileByContent = IsJsonFile(asset);
if (hasBinaryExtension == isJsonFileByContent) {
if (hasBinaryExtension) {
problemDescription = string.Format("Failed to read '{0}'. Extension is '.skel.bytes' but content looks like a '.json' file.\n"
+ "Did you choose the wrong extension upon export?\n", asset.name);
}
else {
problemDescription = string.Format("Failed to read '{0}'. Extension is '.json' but content looks like binary 'skel.bytes' file.\n"
+ "Did you choose the wrong extension upon export?\n", asset.name);
}
isSpineSkeletonData = false;
return null;
}
if (fileVersion.sourceType == SourceType.Binary) {
try {
using (var memStream = new MemoryStream(asset.bytes)) {
fileVersion.rawVersion = SkeletonBinary.GetVersionString(memStream);
}
}
catch (System.Exception e) {
problemDescription = string.Format("Failed to read '{0}'. It is likely not a binary Spine SkeletonData file.\n{1}", asset.name, e);
isSpineSkeletonData = false;
return null;
}
}
else {
Match match = jsonVersionRegex.Match(asset.text);
if (match != null) {
fileVersion.rawVersion = match.Groups[1].Value;
}
else {
object obj = Json.Deserialize(new StringReader(asset.text));
if (obj == null) {
problemDescription = string.Format("'{0}' is not valid JSON.", asset.name);
isSpineSkeletonData = false;
return null;
}
var root = obj as Dictionary<string, object>;
if (root == null) {
problemDescription = string.Format("'{0}' is not compatible JSON. Parser returned an incorrect type while parsing version info.", asset.name);
isSpineSkeletonData = false;
return null;
}
if (root.ContainsKey("skeleton")) {
var skeletonInfo = (Dictionary<string, object>)root["skeleton"];
object jv;
skeletonInfo.TryGetValue("spine", out jv);
fileVersion.rawVersion = jv as string;
}
}
}
if (string.IsNullOrEmpty(fileVersion.rawVersion)) {
// very likely not a Spine skeleton json file at all. Could be another valid json file, don't report errors.
isSpineSkeletonData = false;
return null;
}
var versionSplit = fileVersion.rawVersion.Split('.');
try {
fileVersion.version = new[]{ int.Parse(versionSplit[0], CultureInfo.InvariantCulture),
int.Parse(versionSplit[1], CultureInfo.InvariantCulture) };
}
catch (System.Exception e) {
problemDescription = string.Format("Failed to read version info at skeleton '{0}'. It is likely not a valid Spine SkeletonData file.\n{1}", asset.name, e);
isSpineSkeletonData = false;
return null;
}
isSpineSkeletonData = true;
return fileVersion;
}
public static bool IsJsonFile (TextAsset file) {
byte[] content = file.bytes;
const int maxCharsToCheck = 256;
int numCharsToCheck = Math.Min(content.Length, maxCharsToCheck);
int i = 0;
if (content.Length >= 3 && content[0] == 0xEF && content[1] == 0xBB && content[2] == 0xBF) // skip potential BOM
i = 3;
for (; i < numCharsToCheck; ++i) {
char c = (char)content[i];
if (char.IsWhiteSpace(c))
continue;
return c == '{';
}
return true;
}
public static CompatibilityProblemInfo GetCompatibilityProblemInfo (VersionInfo fileVersion) {
if (fileVersion == null) {
return null; // it's most likely not a Spine skeleton file, e.g. another json file. don't report problems.
}
CompatibilityProblemInfo info = new CompatibilityProblemInfo();
info.actualVersion = fileVersion;
info.compatibleVersions = (fileVersion.sourceType == SourceType.Binary) ? compatibleBinaryVersions
: compatibleJsonVersions;
foreach (var compatibleVersion in info.compatibleVersions) {
bool majorMatch = fileVersion.version[0] == compatibleVersion[0];
bool minorMatch = fileVersion.version[1] == compatibleVersion[1];
if (majorMatch && minorMatch) {
return null; // is compatible, thus no problem info returned
}
}
return info;
}
public static void DisplayCompatibilityProblem (string descriptionString, TextAsset spineJson) {
if (!wasVersionDialogShown) {
wasVersionDialogShown = true;
UnityEditor.EditorUtility.DisplayDialog("Version mismatch!", descriptionString, "OK");
}
Debug.LogError(string.Format("Error importing skeleton '{0}': {1}",
spineJson.name, descriptionString), spineJson);
}
#endif // UNITY_EDITOR
}
}

View File

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

View File

@@ -1,39 +0,0 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace Spine.Unity {
/// <summary>Can be stored by SkeletonDataAsset to automatically apply modifications to loaded SkeletonData.</summary>
public abstract class SkeletonDataModifierAsset : ScriptableObject {
public abstract void Apply (SkeletonData skeletonData);
}
}

View File

@@ -1,12 +0,0 @@
fileFormatVersion: 2
guid: 79a44aba1f342f440965874280b4c318
timeCreated: 1536412736
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,121 +0,0 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
using UnityEngine;
using System.Collections.Generic;
using Spine.Unity.AnimationTools;
namespace Spine.Unity {
/// <summary>
/// Add this component to a SkeletonMecanim GameObject
/// to turn motion of a selected root bone into Transform or RigidBody motion.
/// Local bone translation movement is used as motion.
/// All top-level bones of the skeleton are moved to compensate the root
/// motion bone location, keeping the distance relationship between bones intact.
/// </summary>
/// <remarks>
/// Only compatible with <c>SkeletonMecanim</c>.
/// For <c>SkeletonAnimation</c> or <c>SkeletonGraphic</c> please use
/// <see cref="SkeletonRootMotion">SkeletonRootMotion</see> instead.
/// </remarks>
[HelpURL("http://esotericsoftware.com/spine-unity#SkeletonMecanimRootMotion")]
public class SkeletonMecanimRootMotion : SkeletonRootMotionBase {
#region Inspector
const int DefaultMecanimLayerFlags = -1;
public int mecanimLayerFlags = DefaultMecanimLayerFlags;
#endregion
protected Vector2 movementDelta;
SkeletonMecanim skeletonMecanim;
public SkeletonMecanim SkeletonMecanim {
get {
return skeletonMecanim ? skeletonMecanim : skeletonMecanim = GetComponent<SkeletonMecanim>();
}
}
public override Vector2 GetRemainingRootMotion (int layerIndex) {
var pair = skeletonMecanim.Translator.GetActiveAnimationAndTime(layerIndex);
var animation = pair.Key;
var time = pair.Value;
if (animation == null)
return Vector2.zero;
float start = time;
float end = animation.duration;
return GetAnimationRootMotion(start, end, animation);
}
public override RootMotionInfo GetRootMotionInfo (int layerIndex) {
var pair = skeletonMecanim.Translator.GetActiveAnimationAndTime(layerIndex);
var animation = pair.Key;
var time = pair.Value;
if (animation == null)
return new RootMotionInfo();
return GetAnimationRootMotionInfo(animation, time);
}
protected override void Reset () {
base.Reset();
mecanimLayerFlags = DefaultMecanimLayerFlags;
}
protected override void Start () {
base.Start();
skeletonMecanim = GetComponent<SkeletonMecanim>();
if (skeletonMecanim) {
skeletonMecanim.Translator.OnClipApplied -= OnClipApplied;
skeletonMecanim.Translator.OnClipApplied += OnClipApplied;
}
}
void OnClipApplied(Spine.Animation animation, int layerIndex, float weight,
float time, float lastTime, bool playsBackward) {
if (((mecanimLayerFlags & 1<<layerIndex) == 0) || weight == 0)
return;
if (!playsBackward) {
movementDelta += weight * GetAnimationRootMotion(lastTime, time, animation);
}
else {
movementDelta -= weight * GetAnimationRootMotion(time, lastTime, animation);
}
}
protected override Vector2 CalculateAnimationsMovementDelta () {
// Note: movement delta is not gather after animation but
// in OnClipApplied after every applied animation.
Vector2 result = movementDelta;
movementDelta = Vector2.zero;
return result;
}
}
}

View File

@@ -1,12 +0,0 @@
fileFormatVersion: 2
guid: 95813afe390494344a6ce2cbc8bfb7d1
timeCreated: 1592849332
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,157 +0,0 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
using UnityEngine;
using System.Collections.Generic;
using Spine.Unity.AnimationTools;
namespace Spine.Unity {
/// <summary>
/// Add this component to a SkeletonAnimation or SkeletonGraphic GameObject
/// to turn motion of a selected root bone into Transform or RigidBody motion.
/// Local bone translation movement is used as motion.
/// All top-level bones of the skeleton are moved to compensate the root
/// motion bone location, keeping the distance relationship between bones intact.
/// </summary>
/// <remarks>
/// Only compatible with SkeletonAnimation (or other components that implement
/// ISkeletonComponent, ISkeletonAnimation and IAnimationStateComponent).
/// For <c>SkeletonMecanim</c> please use
/// <see cref="SkeletonMecanimRootMotion">SkeletonMecanimRootMotion</see> instead.
/// </remarks>
[HelpURL("http://esotericsoftware.com/spine-unity#SkeletonRootMotion")]
public class SkeletonRootMotion : SkeletonRootMotionBase {
#region Inspector
const int DefaultAnimationTrackFlags = -1;
public int animationTrackFlags = DefaultAnimationTrackFlags;
#endregion
AnimationState animationState;
Canvas canvas;
public override Vector2 GetRemainingRootMotion (int trackIndex) {
TrackEntry track = animationState.GetCurrent(trackIndex);
if (track == null)
return Vector2.zero;
var animation = track.Animation;
float start = track.AnimationTime;
float end = animation.duration;
return GetAnimationRootMotion(start, end, animation);
}
public override RootMotionInfo GetRootMotionInfo (int trackIndex) {
TrackEntry track = animationState.GetCurrent(trackIndex);
if (track == null)
return new RootMotionInfo();
var animation = track.Animation;
float time = track.AnimationTime;
return GetAnimationRootMotionInfo(track.Animation, time);
}
protected override float AdditionalScale {
get {
return canvas ? canvas.referencePixelsPerUnit: 1.0f;
}
}
protected override void Reset () {
base.Reset();
animationTrackFlags = DefaultAnimationTrackFlags;
}
protected override void Start () {
base.Start();
var animstateComponent = skeletonComponent as IAnimationStateComponent;
this.animationState = (animstateComponent != null) ? animstateComponent.AnimationState : null;
if (this.GetComponent<CanvasRenderer>() != null) {
canvas = this.GetComponentInParent<Canvas>();
}
}
protected override Vector2 CalculateAnimationsMovementDelta () {
Vector2 localDelta = Vector2.zero;
int trackCount = animationState.Tracks.Count;
for (int trackIndex = 0; trackIndex < trackCount; ++trackIndex) {
// note: animationTrackFlags != -1 below covers trackIndex >= 32,
// with -1 corresponding to entry "everything" of the dropdown list.
if (animationTrackFlags != -1 && (animationTrackFlags & 1 << trackIndex) == 0)
continue;
TrackEntry track = animationState.GetCurrent(trackIndex);
TrackEntry next = null;
while (track != null) {
var animation = track.Animation;
float start = track.animationLast;
float end = track.AnimationTime;
var currentDelta = GetAnimationRootMotion(start, end, animation);
if (currentDelta != Vector2.zero) {
ApplyMixAlphaToDelta(ref currentDelta, next, track);
localDelta += currentDelta;
}
// Traverse mixingFrom chain.
next = track;
track = track.mixingFrom;
}
}
return localDelta;
}
void ApplyMixAlphaToDelta (ref Vector2 currentDelta, TrackEntry next, TrackEntry track) {
// Apply mix alpha to the delta position (based on AnimationState.cs).
float mix;
if (next != null) {
if (next.mixDuration == 0) { // Single frame mix to undo mixingFrom changes.
mix = 1;
}
else {
mix = next.mixTime / next.mixDuration;
if (mix > 1) mix = 1;
}
float mixAndAlpha = track.alpha * next.interruptAlpha * (1 - mix);
currentDelta *= mixAndAlpha;
}
else {
if (track.mixDuration == 0) {
mix = 1;
}
else {
mix = track.alpha * (track.mixTime / track.mixDuration);
if (mix > 1) mix = 1;
}
currentDelta *= mix;
}
}
}
}

View File

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

View File

@@ -1,322 +0,0 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
using UnityEngine;
using System.Collections.Generic;
using Spine.Unity.AnimationTools;
using System;
namespace Spine.Unity {
/// <summary>
/// Base class for skeleton root motion components.
/// </summary>
abstract public class SkeletonRootMotionBase : MonoBehaviour {
#region Inspector
[SpineBone]
[SerializeField]
protected string rootMotionBoneName = "root";
public bool transformPositionX = true;
public bool transformPositionY = true;
public float rootMotionScaleX = 1;
public float rootMotionScaleY = 1;
/// <summary>Skeleton space X translation per skeleton space Y translation root motion.</summary>
public float rootMotionTranslateXPerY = 0;
/// <summary>Skeleton space Y translation per skeleton space X translation root motion.</summary>
public float rootMotionTranslateYPerX = 0;
[Header("Optional")]
public Rigidbody2D rigidBody2D;
public Rigidbody rigidBody;
public bool UsesRigidbody {
get { return rigidBody != null || rigidBody2D != null; }
}
#endregion
protected ISkeletonComponent skeletonComponent;
protected Bone rootMotionBone;
protected int rootMotionBoneIndex;
protected List<Bone> topLevelBones = new List<Bone>();
protected Vector2 initialOffset = Vector2.zero;
protected Vector2 tempSkeletonDisplacement;
protected Vector2 rigidbodyDisplacement;
protected virtual void Reset () {
FindRigidbodyComponent();
}
protected virtual void Start () {
skeletonComponent = GetComponent<ISkeletonComponent>();
GatherTopLevelBones();
SetRootMotionBone(rootMotionBoneName);
if (rootMotionBone != null)
initialOffset = new Vector2(rootMotionBone.x, rootMotionBone.y);
var skeletonAnimation = skeletonComponent as ISkeletonAnimation;
if (skeletonAnimation != null) {
skeletonAnimation.UpdateLocal -= HandleUpdateLocal;
skeletonAnimation.UpdateLocal += HandleUpdateLocal;
}
}
protected virtual void FixedUpdate () {
if (!this.isActiveAndEnabled)
return; // Root motion is only applied when component is enabled.
if (rigidBody2D != null) {
rigidBody2D.MovePosition(new Vector2(transform.position.x, transform.position.y)
+ rigidbodyDisplacement);
}
if (rigidBody != null) {
rigidBody.MovePosition(transform.position
+ new Vector3(rigidbodyDisplacement.x, rigidbodyDisplacement.y, 0));
}
Vector2 parentBoneScale;
GetScaleAffectingRootMotion(out parentBoneScale);
ClearEffectiveBoneOffsets(parentBoneScale);
rigidbodyDisplacement = Vector2.zero;
tempSkeletonDisplacement = Vector2.zero;
}
protected virtual void OnDisable () {
rigidbodyDisplacement = Vector2.zero;
tempSkeletonDisplacement = Vector2.zero;
}
protected void FindRigidbodyComponent () {
rigidBody2D = this.GetComponent<Rigidbody2D>();
if (!rigidBody2D)
rigidBody = this.GetComponent<Rigidbody>();
if (!rigidBody2D && !rigidBody) {
rigidBody2D = this.GetComponentInParent<Rigidbody2D>();
if (!rigidBody2D)
rigidBody = this.GetComponentInParent<Rigidbody>();
}
}
protected virtual float AdditionalScale { get { return 1.0f; } }
abstract protected Vector2 CalculateAnimationsMovementDelta ();
abstract public Vector2 GetRemainingRootMotion (int trackIndex = 0);
public struct RootMotionInfo {
public Vector2 start;
public Vector2 current;
public Vector2 mid;
public Vector2 end;
public bool timeIsPastMid;
};
abstract public RootMotionInfo GetRootMotionInfo (int trackIndex = 0);
public void SetRootMotionBone (string name) {
var skeleton = skeletonComponent.Skeleton;
int index = skeleton.FindBoneIndex(name);
if (index >= 0) {
this.rootMotionBoneIndex = index;
this.rootMotionBone = skeleton.bones.Items[index];
}
else {
Debug.Log("Bone named \"" + name + "\" could not be found.");
this.rootMotionBoneIndex = 0;
this.rootMotionBone = skeleton.RootBone;
}
}
public void AdjustRootMotionToDistance (Vector2 distanceToTarget, int trackIndex = 0, bool adjustX = true, bool adjustY = true,
float minX = 0, float maxX = float.MaxValue, float minY = 0, float maxY = float.MaxValue,
bool allowXTranslation = false, bool allowYTranslation = false) {
Vector2 distanceToTargetSkeletonSpace = (Vector2)transform.InverseTransformVector(distanceToTarget);
Vector2 scaleAffectingRootMotion = GetScaleAffectingRootMotion();
if (UsesRigidbody)
distanceToTargetSkeletonSpace -= tempSkeletonDisplacement;
Vector2 remainingRootMotionSkeletonSpace = GetRemainingRootMotion(trackIndex);
remainingRootMotionSkeletonSpace.Scale(scaleAffectingRootMotion);
if (remainingRootMotionSkeletonSpace.x == 0)
remainingRootMotionSkeletonSpace.x = 0.0001f;
if (remainingRootMotionSkeletonSpace.y == 0)
remainingRootMotionSkeletonSpace.y = 0.0001f;
if (adjustX)
rootMotionScaleX = Math.Min(maxX, Math.Max(minX, distanceToTargetSkeletonSpace.x / remainingRootMotionSkeletonSpace.x));
if (adjustY)
rootMotionScaleY = Math.Min(maxY, Math.Max(minY, distanceToTargetSkeletonSpace.y / remainingRootMotionSkeletonSpace.y));
if (allowXTranslation)
rootMotionTranslateXPerY = (distanceToTargetSkeletonSpace.x - remainingRootMotionSkeletonSpace.x * rootMotionScaleX) / remainingRootMotionSkeletonSpace.y;
if (allowYTranslation)
rootMotionTranslateYPerX = (distanceToTargetSkeletonSpace.y - remainingRootMotionSkeletonSpace.y * rootMotionScaleY) / remainingRootMotionSkeletonSpace.x;
}
public Vector2 GetAnimationRootMotion (Animation animation) {
return GetAnimationRootMotion(0, animation.duration, animation);
}
public Vector2 GetAnimationRootMotion (float startTime, float endTime,
Animation animation) {
var timeline = animation.FindTranslateTimelineForBone(rootMotionBoneIndex);
if (timeline != null) {
return GetTimelineMovementDelta(startTime, endTime, timeline, animation);
}
return Vector2.zero;
}
public RootMotionInfo GetAnimationRootMotionInfo (Animation animation, float currentTime) {
RootMotionInfo rootMotion = new RootMotionInfo();
var timeline = animation.FindTranslateTimelineForBone(rootMotionBoneIndex);
if (timeline != null) {
float duration = animation.duration;
float mid = duration * 0.5f;
rootMotion.start = timeline.Evaluate(0);
rootMotion.current = timeline.Evaluate(currentTime);
rootMotion.mid = timeline.Evaluate(mid);
rootMotion.end = timeline.Evaluate(duration);
rootMotion.timeIsPastMid = currentTime > mid;
}
return rootMotion;
}
Vector2 GetTimelineMovementDelta (float startTime, float endTime,
TranslateTimeline timeline, Animation animation) {
Vector2 currentDelta;
if (startTime > endTime) // Looped
currentDelta = (timeline.Evaluate(animation.duration) - timeline.Evaluate(startTime))
+ (timeline.Evaluate(endTime) - timeline.Evaluate(0));
else if (startTime != endTime) // Non-looped
currentDelta = timeline.Evaluate(endTime) - timeline.Evaluate(startTime);
else
currentDelta = Vector2.zero;
return currentDelta;
}
void GatherTopLevelBones () {
topLevelBones.Clear();
var skeleton = skeletonComponent.Skeleton;
foreach (var bone in skeleton.Bones) {
if (bone.Parent == null)
topLevelBones.Add(bone);
}
}
void HandleUpdateLocal (ISkeletonAnimation animatedSkeletonComponent) {
if (!this.isActiveAndEnabled)
return; // Root motion is only applied when component is enabled.
var boneLocalDelta = CalculateAnimationsMovementDelta();
Vector2 parentBoneScale;
Vector2 skeletonDelta = GetSkeletonSpaceMovementDelta(boneLocalDelta, out parentBoneScale);
ApplyRootMotion(skeletonDelta, parentBoneScale);
}
void ApplyRootMotion (Vector2 skeletonDelta, Vector2 parentBoneScale) {
// Apply root motion to Transform or RigidBody;
if (UsesRigidbody) {
rigidbodyDisplacement += (Vector2)transform.TransformVector(skeletonDelta);
// Accumulated displacement is applied on the next Physics update in FixedUpdate.
// Until the next Physics update, tempBoneDisplacement is offsetting bone locations
// to prevent stutter which would otherwise occur if we don't move every Update.
tempSkeletonDisplacement += skeletonDelta;
SetEffectiveBoneOffsetsTo(tempSkeletonDisplacement, parentBoneScale);
}
else {
transform.position += transform.TransformVector(skeletonDelta);
ClearEffectiveBoneOffsets(parentBoneScale);
}
}
Vector2 GetScaleAffectingRootMotion () {
Vector2 parentBoneScale;
return GetScaleAffectingRootMotion(out parentBoneScale);
}
Vector2 GetScaleAffectingRootMotion (out Vector2 parentBoneScale) {
var skeleton = skeletonComponent.Skeleton;
Vector2 totalScale = Vector2.one;
totalScale.x *= skeleton.ScaleX;
totalScale.y *= skeleton.ScaleY;
parentBoneScale = Vector2.one;
Bone scaleBone = rootMotionBone;
while ((scaleBone = scaleBone.parent) != null) {
parentBoneScale.x *= scaleBone.ScaleX;
parentBoneScale.y *= scaleBone.ScaleY;
}
totalScale = Vector2.Scale(totalScale, parentBoneScale);
totalScale *= AdditionalScale;
return totalScale;
}
Vector2 GetSkeletonSpaceMovementDelta (Vector2 boneLocalDelta, out Vector2 parentBoneScale) {
Vector2 skeletonDelta = boneLocalDelta;
Vector2 totalScale = GetScaleAffectingRootMotion(out parentBoneScale);
skeletonDelta.Scale(totalScale);
Vector2 rootMotionTranslation = new Vector2(
rootMotionTranslateXPerY * skeletonDelta.y,
rootMotionTranslateYPerX * skeletonDelta.x);
skeletonDelta.x *= rootMotionScaleX;
skeletonDelta.y *= rootMotionScaleY;
skeletonDelta.x += rootMotionTranslation.x;
skeletonDelta.y += rootMotionTranslation.y;
if (!transformPositionX) skeletonDelta.x = 0f;
if (!transformPositionY) skeletonDelta.y = 0f;
return skeletonDelta;
}
void SetEffectiveBoneOffsetsTo (Vector2 displacementSkeletonSpace, Vector2 parentBoneScale) {
// Move top level bones in opposite direction of the root motion bone
var skeleton = skeletonComponent.Skeleton;
foreach (var topLevelBone in topLevelBones) {
if (topLevelBone == rootMotionBone) {
if (transformPositionX) topLevelBone.x = displacementSkeletonSpace.x / skeleton.ScaleX;
if (transformPositionY) topLevelBone.y = displacementSkeletonSpace.y / skeleton.ScaleY;
}
else {
float offsetX = (initialOffset.x - rootMotionBone.x) * parentBoneScale.x;
float offsetY = (initialOffset.y - rootMotionBone.y) * parentBoneScale.y;
if (transformPositionX) topLevelBone.x = (displacementSkeletonSpace.x / skeleton.ScaleX) + offsetX;
if (transformPositionY) topLevelBone.y = (displacementSkeletonSpace.y / skeleton.ScaleY) + offsetY;
}
}
}
void ClearEffectiveBoneOffsets (Vector2 parentBoneScale) {
SetEffectiveBoneOffsetsTo(Vector2.zero, parentBoneScale);
}
}
}

View File

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

View File

@@ -1,251 +0,0 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#if UNITY_2018_3 || UNITY_2019 || UNITY_2018_3_OR_NEWER
#define NEW_PREFAB_SYSTEM
#endif
using UnityEngine;
namespace Spine.Unity {
#if NEW_PREFAB_SYSTEM
[ExecuteAlways]
#else
[ExecuteInEditMode]
#endif
[AddComponentMenu("Spine/SkeletonAnimation")]
[HelpURL("http://esotericsoftware.com/spine-unity#SkeletonAnimation-Component")]
public class SkeletonAnimation : SkeletonRenderer, ISkeletonAnimation, IAnimationStateComponent {
#region IAnimationStateComponent
/// <summary>
/// This is the Spine.AnimationState object of this SkeletonAnimation. You can control animations through it.
/// Note that this object, like .skeleton, is not guaranteed to exist in Awake. Do all accesses and caching to it in Start</summary>
public Spine.AnimationState state;
/// <summary>
/// This is the Spine.AnimationState object of this SkeletonAnimation. You can control animations through it.
/// Note that this object, like .skeleton, is not guaranteed to exist in Awake. Do all accesses and caching to it in Start</summary>
public Spine.AnimationState AnimationState {
get {
Initialize(false);
return this.state;
}
}
private bool wasUpdatedAfterInit = true;
#endregion
#region Bone Callbacks ISkeletonAnimation
protected event UpdateBonesDelegate _BeforeApply;
protected event UpdateBonesDelegate _UpdateLocal;
protected event UpdateBonesDelegate _UpdateWorld;
protected event UpdateBonesDelegate _UpdateComplete;
/// <summary>
/// Occurs before the animations are applied.
/// Use this callback when you want to change the skeleton state before animations are applied on top.
/// </summary>
public event UpdateBonesDelegate BeforeApply { add { _BeforeApply += value; } remove { _BeforeApply -= value; } }
/// <summary>
/// Occurs after the animations are applied and before world space values are resolved.
/// Use this callback when you want to set bone local values.
/// </summary>
public event UpdateBonesDelegate UpdateLocal { add { _UpdateLocal += value; } remove { _UpdateLocal -= value; } }
/// <summary>
/// Occurs after the Skeleton's bone world space values are resolved (including all constraints).
/// Using this callback will cause the world space values to be solved an extra time.
/// Use this callback if want to use bone world space values, and also set bone local values.</summary>
public event UpdateBonesDelegate UpdateWorld { add { _UpdateWorld += value; } remove { _UpdateWorld -= value; } }
/// <summary>
/// Occurs after the Skeleton's bone world space values are resolved (including all constraints).
/// Use this callback if you want to use bone world space values, but don't intend to modify bone local values.
/// This callback can also be used when setting world position and the bone matrix.</summary>
public event UpdateBonesDelegate UpdateComplete { add { _UpdateComplete += value; } remove { _UpdateComplete -= value; } }
#endregion
#region Serialized state and Beginner API
[SerializeField]
[SpineAnimation]
private string _animationName;
/// <summary>
/// Setting this property sets the animation of the skeleton. If invalid, it will store the animation name for the next time the skeleton is properly initialized.
/// Getting this property gets the name of the currently playing animation. If invalid, it will return the last stored animation name set through this property.</summary>
public string AnimationName {
get {
if (!valid) {
return _animationName;
} else {
TrackEntry entry = state.GetCurrent(0);
return entry == null ? null : entry.Animation.Name;
}
}
set {
Initialize(false);
if (_animationName == value) {
TrackEntry entry = state.GetCurrent(0);
if (entry != null && entry.loop == loop)
return;
}
_animationName = value;
if (string.IsNullOrEmpty(value)) {
state.ClearTrack(0);
} else {
var animationObject = skeletonDataAsset.GetSkeletonData(false).FindAnimation(value);
if (animationObject != null)
state.SetAnimation(0, animationObject, loop);
}
}
}
/// <summary>Whether or not <see cref="AnimationName"/> should loop. This only applies to the initial animation specified in the inspector, or any subsequent Animations played through .AnimationName. Animations set through state.SetAnimation are unaffected.</summary>
public bool loop;
/// <summary>
/// The rate at which animations progress over time. 1 means 100%. 0.5 means 50%.</summary>
/// <remarks>AnimationState and TrackEntry also have their own timeScale. These are combined multiplicatively.</remarks>
public float timeScale = 1;
#endregion
#region Runtime Instantiation
/// <summary>Adds and prepares a SkeletonAnimation component to a GameObject at runtime.</summary>
/// <returns>The newly instantiated SkeletonAnimation</returns>
public static SkeletonAnimation AddToGameObject (GameObject gameObject, SkeletonDataAsset skeletonDataAsset,
bool quiet = false) {
return SkeletonRenderer.AddSpineComponent<SkeletonAnimation>(gameObject, skeletonDataAsset, quiet);
}
/// <summary>Instantiates a new UnityEngine.GameObject and adds a prepared SkeletonAnimation component to it.</summary>
/// <returns>The newly instantiated SkeletonAnimation component.</returns>
public static SkeletonAnimation NewSkeletonAnimationGameObject (SkeletonDataAsset skeletonDataAsset,
bool quiet = false) {
return SkeletonRenderer.NewSpineGameObject<SkeletonAnimation>(skeletonDataAsset, quiet);
}
#endregion
/// <summary>
/// Clears the previously generated mesh, resets the skeleton's pose, and clears all previously active animations.</summary>
public override void ClearState () {
base.ClearState();
if (state != null) state.ClearTracks();
}
/// <summary>
/// Initialize this component. Attempts to load the SkeletonData and creates the internal Spine objects and buffers.</summary>
/// <param name="overwrite">If set to <c>true</c>, force overwrite an already initialized object.</param>
public override void Initialize (bool overwrite, bool quiet = false) {
if (valid && !overwrite)
return;
base.Initialize(overwrite, quiet);
if (!valid)
return;
state = new Spine.AnimationState(skeletonDataAsset.GetAnimationStateData());
wasUpdatedAfterInit = false;
if (!string.IsNullOrEmpty(_animationName)) {
var animationObject = skeletonDataAsset.GetSkeletonData(false).FindAnimation(_animationName);
if (animationObject != null) {
state.SetAnimation(0, animationObject, loop);
#if UNITY_EDITOR
if (!Application.isPlaying)
Update(0f);
#endif
}
}
}
void Update () {
#if UNITY_EDITOR
if (!Application.isPlaying) {
Update(0f);
return;
}
#endif
Update(Time.deltaTime);
}
/// <summary>Progresses the AnimationState according to the given deltaTime, and applies it to the Skeleton. Use Time.deltaTime to update manually. Use deltaTime 0 to update without progressing the time.</summary>
public void Update (float deltaTime) {
if (!valid || state == null)
return;
wasUpdatedAfterInit = true;
if (updateMode < UpdateMode.OnlyAnimationStatus)
return;
UpdateAnimationStatus(deltaTime);
if (updateMode == UpdateMode.OnlyAnimationStatus)
return;
ApplyAnimation();
}
protected void UpdateAnimationStatus (float deltaTime) {
deltaTime *= timeScale;
skeleton.Update(deltaTime);
state.Update(deltaTime);
}
protected void ApplyAnimation () {
if (_BeforeApply != null)
_BeforeApply(this);
if (updateMode != UpdateMode.OnlyEventTimelines)
state.Apply(skeleton);
else
state.ApplyEventTimelinesOnly(skeleton);
if (_UpdateLocal != null)
_UpdateLocal(this);
skeleton.UpdateWorldTransform();
if (_UpdateWorld != null) {
_UpdateWorld(this);
skeleton.UpdateWorldTransform();
}
if (_UpdateComplete != null) {
_UpdateComplete(this);
}
}
public override void LateUpdate () {
// instantiation can happen from Update() after this component, leading to a missing Update() call.
if (!wasUpdatedAfterInit) Update(0);
base.LateUpdate();
}
}
}

View File

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

View File

@@ -1,825 +0,0 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#if UNITY_2018_3 || UNITY_2019 || UNITY_2018_3_OR_NEWER
#define NEW_PREFAB_SYSTEM
#endif
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
namespace Spine.Unity {
#if NEW_PREFAB_SYSTEM
[ExecuteAlways]
#else
[ExecuteInEditMode]
#endif
[RequireComponent(typeof(CanvasRenderer), typeof(RectTransform)), DisallowMultipleComponent]
[AddComponentMenu("Spine/SkeletonGraphic (Unity UI Canvas)")]
[HelpURL("http://esotericsoftware.com/spine-unity#SkeletonGraphic-Component")]
public class SkeletonGraphic : MaskableGraphic, ISkeletonComponent, IAnimationStateComponent, ISkeletonAnimation, IHasSkeletonDataAsset {
#region Inspector
public SkeletonDataAsset skeletonDataAsset;
public SkeletonDataAsset SkeletonDataAsset { get { return skeletonDataAsset; } }
[SpineSkin(dataField:"skeletonDataAsset", defaultAsEmptyString:true)]
public string initialSkinName;
public bool initialFlipX, initialFlipY;
[SpineAnimation(dataField:"skeletonDataAsset")]
public string startingAnimation;
public bool startingLoop;
public float timeScale = 1f;
public bool freeze;
/// <summary>Update mode to optionally limit updates to e.g. only apply animations but not update the mesh.</summary>
public UpdateMode UpdateMode { get { return updateMode; } set { updateMode = value; } }
protected UpdateMode updateMode = UpdateMode.FullUpdate;
/// <summary>Update mode used when the MeshRenderer becomes invisible
/// (when <c>OnBecameInvisible()</c> is called). Update mode is automatically
/// reset to <c>UpdateMode.FullUpdate</c> when the mesh becomes visible again.</summary>
public UpdateMode updateWhenInvisible = UpdateMode.FullUpdate;
public bool unscaledTime;
public bool allowMultipleCanvasRenderers = false;
public List<CanvasRenderer> canvasRenderers = new List<CanvasRenderer>();
protected List<RawImage> rawImages = new List<RawImage>();
protected int usedRenderersCount = 0;
// Submesh Separation
public const string SeparatorPartGameObjectName = "Part";
/// <summary>Slot names used to populate separatorSlots list when the Skeleton is initialized. Changing this after initialization does nothing.</summary>
[SerializeField] [SpineSlot] protected string[] separatorSlotNames = new string[0];
/// <summary>Slots that determine where the render is split. This is used by components such as SkeletonRenderSeparator so that the skeleton can be rendered by two separate renderers on different GameObjects.</summary>
[System.NonSerialized] public readonly List<Slot> separatorSlots = new List<Slot>();
public bool enableSeparatorSlots = false;
[SerializeField] protected List<Transform> separatorParts = new List<Transform>();
public List<Transform> SeparatorParts { get { return separatorParts; } }
public bool updateSeparatorPartLocation = true;
private bool wasUpdatedAfterInit = true;
private Texture baseTexture = null;
#if UNITY_EDITOR
protected override void OnValidate () {
// This handles Scene View preview.
base.OnValidate ();
if (this.IsValid) {
if (skeletonDataAsset == null) {
Clear();
} else if (skeletonDataAsset.skeletonJSON == null) {
Clear();
} else if (skeletonDataAsset.GetSkeletonData(true) != skeleton.data) {
Clear();
Initialize(true);
if (!allowMultipleCanvasRenderers && (skeletonDataAsset.atlasAssets.Length > 1 || skeletonDataAsset.atlasAssets[0].MaterialCount > 1))
Debug.LogError("Unity UI does not support multiple textures per Renderer. Please enable 'Advanced - Multiple CanvasRenderers' to generate the required CanvasRenderer GameObjects. Otherwise your skeleton will not be rendered correctly.", this);
} else {
if (freeze) return;
if (!string.IsNullOrEmpty(initialSkinName)) {
var skin = skeleton.data.FindSkin(initialSkinName);
if (skin != null) {
if (skin == skeleton.data.defaultSkin)
skeleton.SetSkin((Skin)null);
else
skeleton.SetSkin(skin);
}
}
// Only provide visual feedback to inspector changes in Unity Editor Edit mode.
if (!Application.isPlaying) {
skeleton.ScaleX = this.initialFlipX ? -1 : 1;
skeleton.ScaleY = this.initialFlipY ? -1 : 1;
state.ClearTrack(0);
skeleton.SetToSetupPose();
if (!string.IsNullOrEmpty(startingAnimation)) {
state.SetAnimation(0, startingAnimation, startingLoop);
Update(0f);
}
}
}
} else {
// Under some circumstances (e.g. sometimes on the first import) OnValidate is called
// before SpineEditorUtilities.ImportSpineContent, causing an unnecessary exception.
// The (skeletonDataAsset.skeletonJSON != null) condition serves to prevent this exception.
if (skeletonDataAsset != null && skeletonDataAsset.skeletonJSON != null)
Initialize(true);
}
}
protected override void Reset () {
base.Reset();
if (material == null || material.shader != Shader.Find("Spine/SkeletonGraphic"))
Debug.LogWarning("SkeletonGraphic works best with the SkeletonGraphic material.");
}
#endif
#endregion
#region Runtime Instantiation
/// <summary>Create a new GameObject with a SkeletonGraphic component.</summary>
/// <param name="material">Material for the canvas renderer to use. Usually, the default SkeletonGraphic material will work.</param>
public static SkeletonGraphic NewSkeletonGraphicGameObject (SkeletonDataAsset skeletonDataAsset, Transform parent, Material material) {
var sg = SkeletonGraphic.AddSkeletonGraphicComponent(new GameObject("New Spine GameObject"), skeletonDataAsset, material);
if (parent != null) sg.transform.SetParent(parent, false);
return sg;
}
/// <summary>Add a SkeletonGraphic component to a GameObject.</summary>
/// <param name="material">Material for the canvas renderer to use. Usually, the default SkeletonGraphic material will work.</param>
public static SkeletonGraphic AddSkeletonGraphicComponent (GameObject gameObject, SkeletonDataAsset skeletonDataAsset, Material material) {
var c = gameObject.AddComponent<SkeletonGraphic>();
if (skeletonDataAsset != null) {
c.material = material;
c.skeletonDataAsset = skeletonDataAsset;
c.Initialize(false);
}
return c;
}
#endregion
#region Overrides
[System.NonSerialized] readonly Dictionary<Texture, Texture> customTextureOverride = new Dictionary<Texture, Texture>();
/// <summary>Use this Dictionary to override a Texture with a different Texture.</summary>
public Dictionary<Texture, Texture> CustomTextureOverride { get { return customTextureOverride; } }
[System.NonSerialized] readonly Dictionary<Texture, Material> customMaterialOverride = new Dictionary<Texture, Material>();
/// <summary>Use this Dictionary to override the Material where the Texture was used at the original atlas.</summary>
public Dictionary<Texture, Material> CustomMaterialOverride { get { return customMaterialOverride; } }
// This is used by the UI system to determine what to put in the MaterialPropertyBlock.
Texture overrideTexture;
public Texture OverrideTexture {
get { return overrideTexture; }
set {
overrideTexture = value;
canvasRenderer.SetTexture(this.mainTexture); // Refresh canvasRenderer's texture. Make sure it handles null.
}
}
#endregion
#region Internals
public override Texture mainTexture {
get {
if (overrideTexture != null) return overrideTexture;
return baseTexture;
}
}
protected override void Awake () {
base.Awake ();
this.onCullStateChanged.AddListener(OnCullStateChanged);
SyncRawImagesWithCanvasRenderers();
if (!this.IsValid) {
#if UNITY_EDITOR
// workaround for special import case of open scene where OnValidate and Awake are
// called in wrong order, before setup of Spine assets.
if (!Application.isPlaying) {
if (this.skeletonDataAsset != null && this.skeletonDataAsset.skeletonJSON == null)
return;
}
#endif
Initialize(false);
Rebuild(CanvasUpdate.PreRender);
}
}
protected override void OnDestroy () {
Clear();
base.OnDestroy();
}
public override void Rebuild (CanvasUpdate update) {
base.Rebuild(update);
if (canvasRenderer.cull) return;
if (update == CanvasUpdate.PreRender) UpdateMesh(keepRendererCount : true);
if (allowMultipleCanvasRenderers) canvasRenderer.Clear();
}
protected override void OnDisable () {
base.OnDisable();
foreach (var canvasRenderer in canvasRenderers) {
canvasRenderer.Clear();
}
}
public virtual void Update () {
#if UNITY_EDITOR
if (!Application.isPlaying) {
Update(0f);
return;
}
#endif
if (freeze) return;
Update(unscaledTime ? Time.unscaledDeltaTime : Time.deltaTime);
}
public virtual void Update (float deltaTime) {
if (!this.IsValid) return;
wasUpdatedAfterInit = true;
if (updateMode < UpdateMode.OnlyAnimationStatus)
return;
UpdateAnimationStatus(deltaTime);
if (updateMode == UpdateMode.OnlyAnimationStatus)
return;
ApplyAnimation();
}
protected void SyncRawImagesWithCanvasRenderers () {
rawImages.Clear();
foreach (var canvasRenderer in canvasRenderers) {
var rawImage = canvasRenderer.GetComponent<RawImage>();
if (rawImage == null) {
rawImage = canvasRenderer.gameObject.AddComponent<RawImage>();
rawImage.maskable = this.maskable;
rawImage.raycastTarget = false;
}
rawImages.Add(rawImage);
}
}
protected void UpdateAnimationStatus (float deltaTime) {
deltaTime *= timeScale;
skeleton.Update(deltaTime);
state.Update(deltaTime);
}
protected void ApplyAnimation () {
if (BeforeApply != null)
BeforeApply(this);
if (updateMode != UpdateMode.OnlyEventTimelines)
state.Apply(skeleton);
else
state.ApplyEventTimelinesOnly(skeleton);
if (UpdateLocal != null)
UpdateLocal(this);
skeleton.UpdateWorldTransform();
if (UpdateWorld != null) {
UpdateWorld(this);
skeleton.UpdateWorldTransform();
}
if (UpdateComplete != null)
UpdateComplete(this);
}
public void LateUpdate () {
// instantiation can happen from Update() after this component, leading to a missing Update() call.
if (!wasUpdatedAfterInit) Update(0);
if (freeze) return;
if (updateMode != UpdateMode.FullUpdate) return;
UpdateMesh();
}
protected void OnCullStateChanged (bool culled) {
if (culled)
OnBecameInvisible();
else
OnBecameVisible();
}
public void OnBecameVisible () {
updateMode = UpdateMode.FullUpdate;
}
public void OnBecameInvisible () {
updateMode = updateWhenInvisible;
}
public void ReapplySeparatorSlotNames () {
if (!IsValid)
return;
separatorSlots.Clear();
for (int i = 0, n = separatorSlotNames.Length; i < n; i++) {
string slotName = separatorSlotNames[i];
if (slotName == "")
continue;
var slot = skeleton.FindSlot(slotName);
if (slot != null) {
separatorSlots.Add(slot);
}
#if UNITY_EDITOR
else
{
Debug.LogWarning(slotName + " is not a slot in " + skeletonDataAsset.skeletonJSON.name);
}
#endif
}
UpdateSeparatorPartParents();
}
#endregion
#region API
protected Skeleton skeleton;
public Skeleton Skeleton {
get {
Initialize(false);
return skeleton;
}
set {
skeleton = value;
}
}
public SkeletonData SkeletonData { get { return skeleton == null ? null : skeleton.data; } }
public bool IsValid { get { return skeleton != null; } }
public delegate void SkeletonRendererDelegate (SkeletonGraphic skeletonGraphic);
/// <summary>OnRebuild is raised after the Skeleton is successfully initialized.</summary>
public event SkeletonRendererDelegate OnRebuild;
/// <summary>OnMeshAndMaterialsUpdated is at the end of LateUpdate after the Mesh and
/// all materials have been updated.</summary>
public event SkeletonRendererDelegate OnMeshAndMaterialsUpdated;
protected Spine.AnimationState state;
public Spine.AnimationState AnimationState {
get {
Initialize(false);
return state;
}
}
[SerializeField] protected Spine.Unity.MeshGenerator meshGenerator = new MeshGenerator();
public Spine.Unity.MeshGenerator MeshGenerator { get { return this.meshGenerator; } }
DoubleBuffered<Spine.Unity.MeshRendererBuffers.SmartMesh> meshBuffers;
SkeletonRendererInstruction currentInstructions = new SkeletonRendererInstruction();
readonly ExposedList<Mesh> meshes = new ExposedList<Mesh>();
public Mesh GetLastMesh () {
return meshBuffers.GetCurrent().mesh;
}
public bool MatchRectTransformWithBounds () {
UpdateMesh();
if (!this.allowMultipleCanvasRenderers)
return MatchRectTransformSingleRenderer();
else
return MatchRectTransformMultipleRenderers();
}
protected bool MatchRectTransformSingleRenderer () {
Mesh mesh = this.GetLastMesh();
if (mesh == null) {
return false;
}
if (mesh.vertexCount == 0) {
this.rectTransform.sizeDelta = new Vector2(50f, 50f);
this.rectTransform.pivot = new Vector2(0.5f, 0.5f);
return false;
}
mesh.RecalculateBounds();
SetRectTransformBounds(mesh.bounds);
return true;
}
protected bool MatchRectTransformMultipleRenderers () {
bool anyBoundsAdded = false;
Bounds combinedBounds = new Bounds();
for (int i = 0; i < canvasRenderers.Count; ++i) {
var canvasRenderer = canvasRenderers[i];
if (!canvasRenderer.gameObject.activeSelf)
continue;
Mesh mesh = meshes.Items[i];
if (mesh == null || mesh.vertexCount == 0)
continue;
mesh.RecalculateBounds();
var bounds = mesh.bounds;
if (anyBoundsAdded)
combinedBounds.Encapsulate(bounds);
else {
anyBoundsAdded = true;
combinedBounds = bounds;
}
}
if (!anyBoundsAdded) {
this.rectTransform.sizeDelta = new Vector2(50f, 50f);
this.rectTransform.pivot = new Vector2(0.5f, 0.5f);
return false;
}
SetRectTransformBounds(combinedBounds);
return true;
}
private void SetRectTransformBounds (Bounds combinedBounds) {
var size = combinedBounds.size;
var center = combinedBounds.center;
var p = new Vector2(
0.5f - (center.x / size.x),
0.5f - (center.y / size.y)
);
this.rectTransform.sizeDelta = size;
this.rectTransform.pivot = p;
}
public event UpdateBonesDelegate BeforeApply;
public event UpdateBonesDelegate UpdateLocal;
public event UpdateBonesDelegate UpdateWorld;
public event UpdateBonesDelegate UpdateComplete;
/// <summary> Occurs after the vertex data populated every frame, before the vertices are pushed into the mesh.</summary>
public event Spine.Unity.MeshGeneratorDelegate OnPostProcessVertices;
public void Clear () {
skeleton = null;
canvasRenderer.Clear();
for (int i = 0; i < canvasRenderers.Count; ++i)
canvasRenderers[i].Clear();
DestroyMeshes();
DisposeMeshBuffers();
}
public void TrimRenderers () {
var newList = new List<CanvasRenderer>();
foreach (var canvasRenderer in canvasRenderers) {
if (canvasRenderer.gameObject.activeSelf) {
newList.Add(canvasRenderer);
}
else {
if (Application.isEditor && !Application.isPlaying)
DestroyImmediate(canvasRenderer.gameObject);
else
Destroy(canvasRenderer.gameObject);
}
}
canvasRenderers = newList;
SyncRawImagesWithCanvasRenderers();
}
public void Initialize (bool overwrite) {
if (this.IsValid && !overwrite) return;
if (this.skeletonDataAsset == null) return;
var skeletonData = this.skeletonDataAsset.GetSkeletonData(false);
if (skeletonData == null) return;
if (skeletonDataAsset.atlasAssets.Length <= 0 || skeletonDataAsset.atlasAssets[0].MaterialCount <= 0) return;
this.state = new Spine.AnimationState(skeletonDataAsset.GetAnimationStateData());
if (state == null) {
Clear();
return;
}
this.skeleton = new Skeleton(skeletonData) {
ScaleX = this.initialFlipX ? -1 : 1,
ScaleY = this.initialFlipY ? -1 : 1
};
InitMeshBuffers();
baseTexture = skeletonDataAsset.atlasAssets[0].PrimaryMaterial.mainTexture;
canvasRenderer.SetTexture(this.mainTexture); // Needed for overwriting initializations.
// Set the initial Skin and Animation
if (!string.IsNullOrEmpty(initialSkinName))
skeleton.SetSkin(initialSkinName);
separatorSlots.Clear();
for (int i = 0; i < separatorSlotNames.Length; i++)
separatorSlots.Add(skeleton.FindSlot(separatorSlotNames[i]));
wasUpdatedAfterInit = false;
if (!string.IsNullOrEmpty(startingAnimation)) {
var animationObject = skeletonDataAsset.GetSkeletonData(false).FindAnimation(startingAnimation);
if (animationObject != null) {
state.SetAnimation(0, animationObject, startingLoop);
#if UNITY_EDITOR
if (!Application.isPlaying)
Update(0f);
#endif
}
}
if (OnRebuild != null)
OnRebuild(this);
}
public void UpdateMesh (bool keepRendererCount = false) {
if (!this.IsValid) return;
skeleton.SetColor(this.color);
var currentInstructions = this.currentInstructions;
if (!this.allowMultipleCanvasRenderers) {
UpdateMeshSingleCanvasRenderer();
}
else {
UpdateMeshMultipleCanvasRenderers(currentInstructions, keepRendererCount);
}
if (OnMeshAndMaterialsUpdated != null)
OnMeshAndMaterialsUpdated(this);
}
public bool HasMultipleSubmeshInstructions () {
if (!IsValid)
return false;
return MeshGenerator.RequiresMultipleSubmeshesByDrawOrder(skeleton);
}
#endregion
protected void InitMeshBuffers () {
if (meshBuffers != null) {
meshBuffers.GetNext().Clear();
meshBuffers.GetNext().Clear();
}
else {
meshBuffers = new DoubleBuffered<MeshRendererBuffers.SmartMesh>();
}
}
protected void DisposeMeshBuffers () {
if (meshBuffers != null) {
meshBuffers.GetNext().Dispose();
meshBuffers.GetNext().Dispose();
meshBuffers = null;
}
}
protected void UpdateMeshSingleCanvasRenderer () {
if (canvasRenderers.Count > 0)
DisableUnusedCanvasRenderers(usedCount : 0);
var smartMesh = meshBuffers.GetNext();
MeshGenerator.GenerateSingleSubmeshInstruction(currentInstructions, skeleton, null);
bool updateTriangles = SkeletonRendererInstruction.GeometryNotEqual(currentInstructions, smartMesh.instructionUsed);
meshGenerator.Begin();
if (currentInstructions.hasActiveClipping && currentInstructions.submeshInstructions.Count > 0) {
meshGenerator.AddSubmesh(currentInstructions.submeshInstructions.Items[0], updateTriangles);
}
else {
meshGenerator.BuildMeshWithArrays(currentInstructions, updateTriangles);
}
if (canvas != null) meshGenerator.ScaleVertexData(canvas.referencePixelsPerUnit);
if (OnPostProcessVertices != null) OnPostProcessVertices.Invoke(this.meshGenerator.Buffers);
var mesh = smartMesh.mesh;
meshGenerator.FillVertexData(mesh);
if (updateTriangles) meshGenerator.FillTriangles(mesh);
meshGenerator.FillLateVertexData(mesh);
canvasRenderer.SetMesh(mesh);
smartMesh.instructionUsed.Set(currentInstructions);
if (currentInstructions.submeshInstructions.Count > 0) {
var material = currentInstructions.submeshInstructions.Items[0].material;
if (material != null && baseTexture != material.mainTexture) {
baseTexture = material.mainTexture;
if (overrideTexture == null)
canvasRenderer.SetTexture(this.mainTexture);
}
}
//this.UpdateMaterial(); // note: This would allocate memory.
usedRenderersCount = 0;
}
protected void UpdateMeshMultipleCanvasRenderers (SkeletonRendererInstruction currentInstructions, bool keepRendererCount) {
MeshGenerator.GenerateSkeletonRendererInstruction(currentInstructions, skeleton, null,
enableSeparatorSlots ? separatorSlots : null,
enableSeparatorSlots ? separatorSlots.Count > 0 : false,
false);
int submeshCount = currentInstructions.submeshInstructions.Count;
if (keepRendererCount && submeshCount != usedRenderersCount)
return;
EnsureCanvasRendererCount(submeshCount);
EnsureMeshesCount(submeshCount);
EnsureSeparatorPartCount();
var c = canvas;
float scale = (c == null) ? 100 : c.referencePixelsPerUnit;
// Generate meshes.
var meshesItems = meshes.Items;
bool useOriginalTextureAndMaterial = (customMaterialOverride.Count == 0 && customTextureOverride.Count == 0);
int separatorSlotGroupIndex = 0;
Transform parent = this.separatorSlots.Count == 0 ? this.transform : this.separatorParts[0];
if (updateSeparatorPartLocation) {
for (int p = 0; p < this.separatorParts.Count; ++p) {
separatorParts[p].position = this.transform.position;
separatorParts[p].rotation = this.transform.rotation;
}
}
int targetSiblingIndex = 0;
for (int i = 0; i < submeshCount; i++) {
var submeshInstructionItem = currentInstructions.submeshInstructions.Items[i];
meshGenerator.Begin();
meshGenerator.AddSubmesh(submeshInstructionItem);
var targetMesh = meshesItems[i];
meshGenerator.ScaleVertexData(scale);
if (OnPostProcessVertices != null) OnPostProcessVertices.Invoke(this.meshGenerator.Buffers);
meshGenerator.FillVertexData(targetMesh);
meshGenerator.FillTriangles(targetMesh);
meshGenerator.FillLateVertexData(targetMesh);
var submeshMaterial = submeshInstructionItem.material;
var canvasRenderer = canvasRenderers[i];
if (i >= usedRenderersCount)
canvasRenderer.gameObject.SetActive(true);
canvasRenderer.SetMesh(targetMesh);
canvasRenderer.materialCount = 1;
if (canvasRenderer.transform.parent != parent.transform) {
canvasRenderer.transform.SetParent(parent.transform, false);
canvasRenderer.transform.localPosition = Vector3.zero;
}
canvasRenderer.transform.SetSiblingIndex(targetSiblingIndex++);
if (submeshInstructionItem.forceSeparate) {
targetSiblingIndex = 0;
parent = separatorParts[++separatorSlotGroupIndex];
}
if (useOriginalTextureAndMaterial)
canvasRenderer.SetMaterial(this.materialForRendering, submeshMaterial.mainTexture);
else {
var originalTexture = submeshMaterial.mainTexture;
Material usedMaterial;
Texture usedTexture;
if (!customMaterialOverride.TryGetValue(originalTexture, out usedMaterial))
usedMaterial = material;
if (!customTextureOverride.TryGetValue(originalTexture, out usedTexture))
usedTexture = originalTexture;
canvasRenderer.SetMaterial(usedMaterial, usedTexture);
}
}
DisableUnusedCanvasRenderers(usedCount : submeshCount);
usedRenderersCount = submeshCount;
}
protected void EnsureCanvasRendererCount (int targetCount) {
#if UNITY_EDITOR
RemoveNullCanvasRenderers();
#endif
int currentCount = canvasRenderers.Count;
for (int i = currentCount; i < targetCount; ++i) {
var go = new GameObject(string.Format("Renderer{0}", i), typeof(RectTransform));
go.transform.SetParent(this.transform, false);
go.transform.localPosition = Vector3.zero;
var canvasRenderer = go.AddComponent<CanvasRenderer>();
canvasRenderers.Add(canvasRenderer);
var rawImage = go.AddComponent<RawImage>();
rawImage.maskable = this.maskable;
rawImage.raycastTarget = false;
rawImages.Add(rawImage);
}
}
protected void DisableUnusedCanvasRenderers (int usedCount) {
#if UNITY_EDITOR
RemoveNullCanvasRenderers();
#endif
for (int i = usedCount; i < canvasRenderers.Count; i++) {
canvasRenderers[i].Clear();
canvasRenderers[i].gameObject.SetActive(false);
}
}
#if UNITY_EDITOR
private void RemoveNullCanvasRenderers () {
if (Application.isEditor && !Application.isPlaying) {
for (int i = canvasRenderers.Count - 1; i >= 0; --i) {
if (canvasRenderers[i] == null) {
canvasRenderers.RemoveAt(i);
}
}
}
}
#endif
protected void EnsureMeshesCount (int targetCount) {
int oldCount = meshes.Count;
meshes.EnsureCapacity(targetCount);
for (int i = oldCount; i < targetCount; i++)
meshes.Add(SpineMesh.NewSkeletonMesh());
}
protected void DestroyMeshes () {
foreach (var mesh in meshes) {
#if UNITY_EDITOR
if (Application.isEditor && !Application.isPlaying)
UnityEngine.Object.DestroyImmediate(mesh);
else
UnityEngine.Object.Destroy(mesh);
#else
UnityEngine.Object.Destroy(mesh);
#endif
}
meshes.Clear();
}
protected void EnsureSeparatorPartCount () {
#if UNITY_EDITOR
RemoveNullSeparatorParts();
#endif
int targetCount = separatorSlots.Count + 1;
if (targetCount == 1)
return;
#if UNITY_EDITOR
if (Application.isEditor && !Application.isPlaying) {
for (int i = separatorParts.Count-1; i >= 0; --i) {
if (separatorParts[i] == null) {
separatorParts.RemoveAt(i);
}
}
}
#endif
int currentCount = separatorParts.Count;
for (int i = currentCount; i < targetCount; ++i) {
var go = new GameObject(string.Format("{0}[{1}]", SeparatorPartGameObjectName, i), typeof(RectTransform));
go.transform.SetParent(this.transform, false);
go.transform.localPosition = Vector3.zero;
separatorParts.Add(go.transform);
}
}
protected void UpdateSeparatorPartParents () {
int usedCount = separatorSlots.Count + 1;
if (usedCount == 1) {
usedCount = 0; // placed directly at the SkeletonGraphic parent
for (int i = 0; i < canvasRenderers.Count; ++i) {
var canvasRenderer = canvasRenderers[i];
if (canvasRenderer.transform.parent.name.Contains(SeparatorPartGameObjectName)) {
canvasRenderer.transform.SetParent(this.transform, false);
canvasRenderer.transform.localPosition = Vector3.zero;
}
}
}
for (int i = 0; i < separatorParts.Count; ++i) {
bool isUsed = i < usedCount;
separatorParts[i].gameObject.SetActive(isUsed);
}
}
#if UNITY_EDITOR
private void RemoveNullSeparatorParts () {
if (Application.isEditor && !Application.isPlaying) {
for (int i = separatorParts.Count - 1; i >= 0; --i) {
if (separatorParts[i] == null) {
separatorParts.RemoveAt(i);
}
}
}
}
#endif
}
}

View File

@@ -1,14 +0,0 @@
fileFormatVersion: 2
guid: d85b887af7e6c3f45a2e2d2920d641bc
timeCreated: 1455576193
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences:
- m_Material: {fileID: 2100000, guid: b66cf7a186d13054989b33a5c90044e4, type: 2}
- skeletonDataAsset: {instanceID: 0}
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,661 +0,0 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
using UnityEngine;
using System.Collections.Generic;
namespace Spine.Unity {
[RequireComponent(typeof(Animator))]
[HelpURL("http://esotericsoftware.com/spine-unity#SkeletonMecanim-Component")]
public class SkeletonMecanim : SkeletonRenderer, ISkeletonAnimation {
[SerializeField] protected MecanimTranslator translator;
public MecanimTranslator Translator { get { return translator; } }
private bool wasUpdatedAfterInit = true;
#region Bone Callbacks (ISkeletonAnimation)
protected event UpdateBonesDelegate _BeforeApply;
protected event UpdateBonesDelegate _UpdateLocal;
protected event UpdateBonesDelegate _UpdateWorld;
protected event UpdateBonesDelegate _UpdateComplete;
/// <summary>
/// Occurs before the animations are applied.
/// Use this callback when you want to change the skeleton state before animations are applied on top.
/// </summary>
public event UpdateBonesDelegate BeforeApply { add { _BeforeApply += value; } remove { _BeforeApply -= value; } }
/// <summary>
/// Occurs after the animations are applied and before world space values are resolved.
/// Use this callback when you want to set bone local values.</summary>
public event UpdateBonesDelegate UpdateLocal { add { _UpdateLocal += value; } remove { _UpdateLocal -= value; } }
/// <summary>
/// Occurs after the Skeleton's bone world space values are resolved (including all constraints).
/// Using this callback will cause the world space values to be solved an extra time.
/// Use this callback if want to use bone world space values, and also set bone local values.</summary>
public event UpdateBonesDelegate UpdateWorld { add { _UpdateWorld += value; } remove { _UpdateWorld -= value; } }
/// <summary>
/// Occurs after the Skeleton's bone world space values are resolved (including all constraints).
/// Use this callback if you want to use bone world space values, but don't intend to modify bone local values.
/// This callback can also be used when setting world position and the bone matrix.</summary>
public event UpdateBonesDelegate UpdateComplete { add { _UpdateComplete += value; } remove { _UpdateComplete -= value; } }
#endregion
public override void Initialize (bool overwrite, bool quiet = false) {
if (valid && !overwrite)
return;
base.Initialize(overwrite, quiet);
if (!valid)
return;
if (translator == null) translator = new MecanimTranslator();
translator.Initialize(GetComponent<Animator>(), this.skeletonDataAsset);
wasUpdatedAfterInit = false;
}
public void Update () {
if (!valid) return;
wasUpdatedAfterInit = true;
// animation status is kept by Mecanim Animator component
if (updateMode <= UpdateMode.OnlyAnimationStatus)
return;
ApplyAnimation();
}
protected void ApplyAnimation () {
if (_BeforeApply != null)
_BeforeApply(this);
#if UNITY_EDITOR
var translatorAnimator = translator.Animator;
if (translatorAnimator != null && !translatorAnimator.isInitialized)
translatorAnimator.Rebind();
if (Application.isPlaying) {
translator.Apply(skeleton);
}
else {
if (translatorAnimator != null && translatorAnimator.isInitialized &&
translatorAnimator.isActiveAndEnabled && translatorAnimator.runtimeAnimatorController != null) {
// Note: Rebind is required to prevent warning "Animator is not playing an AnimatorController" with prefabs
translatorAnimator.Rebind();
translator.Apply(skeleton);
}
}
#else
translator.Apply(skeleton);
#endif
// UpdateWorldTransform and Bone Callbacks
{
if (_UpdateLocal != null)
_UpdateLocal(this);
skeleton.UpdateWorldTransform();
if (_UpdateWorld != null) {
_UpdateWorld(this);
skeleton.UpdateWorldTransform();
}
if (_UpdateComplete != null)
_UpdateComplete(this);
}
}
public override void LateUpdate () {
// instantiation can happen from Update() after this component, leading to a missing Update() call.
if (!wasUpdatedAfterInit) Update();
base.LateUpdate();
}
[System.Serializable]
public class MecanimTranslator {
const float WeightEpsilon = 0.0001f;
#region Inspector
public bool autoReset = true;
public bool useCustomMixMode = true;
public MixMode[] layerMixModes = new MixMode[0];
public MixBlend[] layerBlendModes = new MixBlend[0];
#endregion
public delegate void OnClipAppliedDelegate (Spine.Animation clip, int layerIndex, float weight,
float time, float lastTime, bool playsBackward);
protected event OnClipAppliedDelegate _OnClipApplied;
public event OnClipAppliedDelegate OnClipApplied { add { _OnClipApplied += value; } remove { _OnClipApplied -= value; } }
public enum MixMode { AlwaysMix, MixNext, Hard }
readonly Dictionary<int, Spine.Animation> animationTable = new Dictionary<int, Spine.Animation>(IntEqualityComparer.Instance);
readonly Dictionary<AnimationClip, int> clipNameHashCodeTable = new Dictionary<AnimationClip, int>(AnimationClipEqualityComparer.Instance);
readonly List<Animation> previousAnimations = new List<Animation>();
protected class ClipInfos {
public bool isInterruptionActive = false;
public bool isLastFrameOfInterruption = false;
public int clipInfoCount = 0;
public int nextClipInfoCount = 0;
public int interruptingClipInfoCount = 0;
public readonly List<AnimatorClipInfo> clipInfos = new List<AnimatorClipInfo>();
public readonly List<AnimatorClipInfo> nextClipInfos = new List<AnimatorClipInfo>();
public readonly List<AnimatorClipInfo> interruptingClipInfos = new List<AnimatorClipInfo>();
public AnimatorStateInfo stateInfo;
public AnimatorStateInfo nextStateInfo;
public AnimatorStateInfo interruptingStateInfo;
public float interruptingClipTimeAddition = 0;
}
protected ClipInfos[] layerClipInfos = new ClipInfos[0];
Animator animator;
public Animator Animator { get { return this.animator; } }
public int MecanimLayerCount {
get {
if (!animator)
return 0;
return animator.layerCount;
}
}
public string[] MecanimLayerNames {
get {
if (!animator)
return new string[0];
string[] layerNames = new string[animator.layerCount];
for (int i = 0; i < animator.layerCount; ++i) {
layerNames[i] = animator.GetLayerName(i);
}
return layerNames;
}
}
public void Initialize(Animator animator, SkeletonDataAsset skeletonDataAsset) {
this.animator = animator;
previousAnimations.Clear();
animationTable.Clear();
var data = skeletonDataAsset.GetSkeletonData(true);
foreach (var a in data.Animations)
animationTable.Add(a.Name.GetHashCode(), a);
clipNameHashCodeTable.Clear();
ClearClipInfosForLayers();
}
private bool ApplyAnimation (Skeleton skeleton, AnimatorClipInfo info, AnimatorStateInfo stateInfo,
int layerIndex, float layerWeight, MixBlend layerBlendMode, bool useClipWeight1 = false) {
float weight = info.weight * layerWeight;
if (weight < WeightEpsilon)
return false;
var clip = GetAnimation(info.clip);
if (clip == null)
return false;
var time = AnimationTime(stateInfo.normalizedTime, info.clip.length,
info.clip.isLooping, stateInfo.speed < 0);
weight = useClipWeight1 ? layerWeight : weight;
clip.Apply(skeleton, 0, time, info.clip.isLooping, null,
weight, layerBlendMode, MixDirection.In);
if (_OnClipApplied != null)
OnClipAppliedCallback(clip, stateInfo, layerIndex, time, info.clip.isLooping, weight);
return true;
}
private bool ApplyInterruptionAnimation (Skeleton skeleton,
bool interpolateWeightTo1, AnimatorClipInfo info, AnimatorStateInfo stateInfo,
int layerIndex, float layerWeight, MixBlend layerBlendMode, float interruptingClipTimeAddition,
bool useClipWeight1 = false) {
float clipWeight = interpolateWeightTo1 ? (info.weight + 1.0f) * 0.5f : info.weight;
float weight = clipWeight * layerWeight;
if (weight < WeightEpsilon)
return false;
var clip = GetAnimation(info.clip);
if (clip == null)
return false;
var time = AnimationTime(stateInfo.normalizedTime + interruptingClipTimeAddition,
info.clip.length, stateInfo.speed < 0);
weight = useClipWeight1 ? layerWeight : weight;
clip.Apply(skeleton, 0, time, info.clip.isLooping, null,
weight, layerBlendMode, MixDirection.In);
if (_OnClipApplied != null) {
OnClipAppliedCallback(clip, stateInfo, layerIndex, time, info.clip.isLooping, weight);
}
return true;
}
private void OnClipAppliedCallback (Spine.Animation clip, AnimatorStateInfo stateInfo,
int layerIndex, float time, bool isLooping, float weight) {
float speedFactor = stateInfo.speedMultiplier * stateInfo.speed;
float lastTime = time - (Time.deltaTime * speedFactor);
if (isLooping && clip.duration != 0) {
time %= clip.duration;
lastTime %= clip.duration;
}
_OnClipApplied(clip, layerIndex, weight, time, lastTime, speedFactor < 0);
}
public void Apply (Skeleton skeleton) {
#if UNITY_EDITOR
if (!Application.isPlaying) {
GetLayerBlendModes();
}
#endif
if (layerMixModes.Length < animator.layerCount) {
int oldSize = layerMixModes.Length;
System.Array.Resize<MixMode>(ref layerMixModes, animator.layerCount);
for (int layer = oldSize; layer < animator.layerCount; ++layer) {
bool isAdditiveLayer = false;
if (layer < layerBlendModes.Length)
isAdditiveLayer = layerBlendModes[layer] == MixBlend.Add;
layerMixModes[layer] = isAdditiveLayer ? MixMode.AlwaysMix : MixMode.MixNext;
}
}
InitClipInfosForLayers();
for (int layer = 0, n = animator.layerCount; layer < n; layer++) {
GetStateUpdatesFromAnimator(layer);
}
// Clear Previous
if (autoReset) {
var previousAnimations = this.previousAnimations;
for (int i = 0, n = previousAnimations.Count; i < n; i++)
previousAnimations[i].SetKeyedItemsToSetupPose(skeleton);
previousAnimations.Clear();
for (int layer = 0, n = animator.layerCount; layer < n; layer++) {
float layerWeight = (layer == 0) ? 1 : animator.GetLayerWeight(layer); // Animator.GetLayerWeight always returns 0 on the first layer. Should be interpreted as 1.
if (layerWeight <= 0) continue;
AnimatorStateInfo nextStateInfo = animator.GetNextAnimatorStateInfo(layer);
bool hasNext = nextStateInfo.fullPathHash != 0;
int clipInfoCount, nextClipInfoCount, interruptingClipInfoCount;
IList<AnimatorClipInfo> clipInfo, nextClipInfo, interruptingClipInfo;
bool isInterruptionActive, shallInterpolateWeightTo1;
GetAnimatorClipInfos(layer, out isInterruptionActive, out clipInfoCount, out nextClipInfoCount, out interruptingClipInfoCount,
out clipInfo, out nextClipInfo, out interruptingClipInfo, out shallInterpolateWeightTo1);
for (int c = 0; c < clipInfoCount; c++) {
var info = clipInfo[c];
float weight = info.weight * layerWeight; if (weight < WeightEpsilon) continue;
var clip = GetAnimation(info.clip);
if (clip != null)
previousAnimations.Add(clip);
}
if (hasNext) {
for (int c = 0; c < nextClipInfoCount; c++) {
var info = nextClipInfo[c];
float weight = info.weight * layerWeight; if (weight < WeightEpsilon) continue;
var clip = GetAnimation(info.clip);
if (clip != null)
previousAnimations.Add(clip);
}
}
if (isInterruptionActive) {
for (int c = 0; c < interruptingClipInfoCount; c++)
{
var info = interruptingClipInfo[c];
float clipWeight = shallInterpolateWeightTo1 ? (info.weight + 1.0f) * 0.5f : info.weight;
float weight = clipWeight * layerWeight; if (weight < WeightEpsilon) continue;
var clip = GetAnimation(info.clip);
if (clip != null)
previousAnimations.Add(clip);
}
}
}
}
// Apply
for (int layer = 0, n = animator.layerCount; layer < n; layer++) {
float layerWeight = (layer == 0) ? 1 : animator.GetLayerWeight(layer); // Animator.GetLayerWeight always returns 0 on the first layer. Should be interpreted as 1.
bool isInterruptionActive;
AnimatorStateInfo stateInfo;
AnimatorStateInfo nextStateInfo;
AnimatorStateInfo interruptingStateInfo;
float interruptingClipTimeAddition;
GetAnimatorStateInfos(layer, out isInterruptionActive, out stateInfo, out nextStateInfo, out interruptingStateInfo, out interruptingClipTimeAddition);
bool hasNext = nextStateInfo.fullPathHash != 0;
int clipInfoCount, nextClipInfoCount, interruptingClipInfoCount;
IList<AnimatorClipInfo> clipInfo, nextClipInfo, interruptingClipInfo;
bool interpolateWeightTo1;
GetAnimatorClipInfos(layer, out isInterruptionActive, out clipInfoCount, out nextClipInfoCount, out interruptingClipInfoCount,
out clipInfo, out nextClipInfo, out interruptingClipInfo, out interpolateWeightTo1);
MixBlend layerBlendMode = (layer < layerBlendModes.Length) ? layerBlendModes[layer] : MixBlend.Replace;
MixMode mode = GetMixMode(layer, layerBlendMode);
if (mode == MixMode.AlwaysMix) {
// Always use Mix instead of Applying the first non-zero weighted clip.
for (int c = 0; c < clipInfoCount; c++) {
ApplyAnimation(skeleton, clipInfo[c], stateInfo, layer, layerWeight, layerBlendMode);
}
if (hasNext) {
for (int c = 0; c < nextClipInfoCount; c++) {
ApplyAnimation(skeleton, nextClipInfo[c], nextStateInfo, layer, layerWeight, layerBlendMode);
}
}
if (isInterruptionActive) {
for (int c = 0; c < interruptingClipInfoCount; c++)
{
ApplyInterruptionAnimation(skeleton, interpolateWeightTo1,
interruptingClipInfo[c], interruptingStateInfo,
layer, layerWeight, layerBlendMode, interruptingClipTimeAddition);
}
}
} else { // case MixNext || Hard
// Apply first non-zero weighted clip
int c = 0;
for (; c < clipInfoCount; c++) {
if (!ApplyAnimation(skeleton, clipInfo[c], stateInfo, layer, layerWeight, layerBlendMode, useClipWeight1:true))
continue;
++c; break;
}
// Mix the rest
for (; c < clipInfoCount; c++) {
ApplyAnimation(skeleton, clipInfo[c], stateInfo, layer, layerWeight, layerBlendMode);
}
c = 0;
if (hasNext) {
// Apply next clip directly instead of mixing (ie: no crossfade, ignores mecanim transition weights)
if (mode == MixMode.Hard) {
for (; c < nextClipInfoCount; c++) {
if (!ApplyAnimation(skeleton, nextClipInfo[c], nextStateInfo, layer, layerWeight, layerBlendMode, useClipWeight1:true))
continue;
++c; break;
}
}
// Mix the rest
for (; c < nextClipInfoCount; c++) {
if (!ApplyAnimation(skeleton, nextClipInfo[c], nextStateInfo, layer, layerWeight, layerBlendMode))
continue;
}
}
c = 0;
if (isInterruptionActive) {
// Apply next clip directly instead of mixing (ie: no crossfade, ignores mecanim transition weights)
if (mode == MixMode.Hard) {
for (; c < interruptingClipInfoCount; c++) {
if (ApplyInterruptionAnimation(skeleton, interpolateWeightTo1,
interruptingClipInfo[c], interruptingStateInfo,
layer, layerWeight, layerBlendMode, interruptingClipTimeAddition, useClipWeight1:true)) {
++c; break;
}
}
}
// Mix the rest
for (; c < interruptingClipInfoCount; c++) {
ApplyInterruptionAnimation(skeleton, interpolateWeightTo1,
interruptingClipInfo[c], interruptingStateInfo,
layer, layerWeight, layerBlendMode, interruptingClipTimeAddition);
}
}
}
}
}
public KeyValuePair<Spine.Animation, float> GetActiveAnimationAndTime (int layer) {
if (layer >= layerClipInfos.Length)
return new KeyValuePair<Spine.Animation, float>(null, 0);
var layerInfos = layerClipInfos[layer];
bool isInterruptionActive = layerInfos.isInterruptionActive;
AnimationClip clip = null;
Spine.Animation animation = null;
AnimatorStateInfo stateInfo;
if (isInterruptionActive && layerInfos.interruptingClipInfoCount > 0) {
clip = layerInfos.interruptingClipInfos[0].clip;
stateInfo = layerInfos.interruptingStateInfo;
}
else {
clip = layerInfos.clipInfos[0].clip;
stateInfo = layerInfos.stateInfo;
}
animation = GetAnimation(clip);
float time = AnimationTime(stateInfo.normalizedTime, clip.length,
clip.isLooping, stateInfo.speed < 0);
return new KeyValuePair<Animation, float>(animation, time);
}
static float AnimationTime (float normalizedTime, float clipLength, bool loop, bool reversed) {
float time = AnimationTime(normalizedTime, clipLength, reversed);
if (loop) return time;
const float EndSnapEpsilon = 1f / 30f; // Workaround for end-duration keys not being applied.
return (clipLength - time < EndSnapEpsilon) ? clipLength : time; // return a time snapped to clipLength;
}
static float AnimationTime (float normalizedTime, float clipLength, bool reversed) {
if (reversed)
normalizedTime = (1 - normalizedTime);
if (normalizedTime < 0.0f)
normalizedTime = (normalizedTime % 1.0f) + 1.0f;
return normalizedTime * clipLength;
}
void InitClipInfosForLayers () {
if (layerClipInfos.Length < animator.layerCount) {
System.Array.Resize<ClipInfos>(ref layerClipInfos, animator.layerCount);
for (int layer = 0, n = animator.layerCount; layer < n; ++layer) {
if (layerClipInfos[layer] == null)
layerClipInfos[layer] = new ClipInfos();
}
}
}
void ClearClipInfosForLayers () {
for (int layer = 0, n = layerClipInfos.Length; layer < n; ++layer) {
if (layerClipInfos[layer] == null)
layerClipInfos[layer] = new ClipInfos();
else {
layerClipInfos[layer].isInterruptionActive = false;
layerClipInfos[layer].isLastFrameOfInterruption = false;
layerClipInfos[layer].clipInfos.Clear();
layerClipInfos[layer].nextClipInfos.Clear();
layerClipInfos[layer].interruptingClipInfos.Clear();
}
}
}
private MixMode GetMixMode (int layer, MixBlend layerBlendMode) {
if (useCustomMixMode) {
MixMode mode = layerMixModes[layer];
// Note: at additive blending it makes no sense to use constant weight 1 at a fadeout anim add1 as
// with override layers, so we use AlwaysMix instead to use the proper weights.
// AlwaysMix leads to the expected result = lower_layer + lerp(add1, add2, transition_weight).
if (layerBlendMode == MixBlend.Add && mode == MixMode.MixNext) {
mode = MixMode.AlwaysMix;
layerMixModes[layer] = mode;
}
return mode;
}
else {
return layerBlendMode == MixBlend.Add ? MixMode.AlwaysMix : MixMode.MixNext;
}
}
#if UNITY_EDITOR
void GetLayerBlendModes() {
if (layerBlendModes.Length < animator.layerCount) {
System.Array.Resize<MixBlend>(ref layerBlendModes, animator.layerCount);
}
for (int layer = 0, n = animator.layerCount; layer < n; ++layer) {
var controller = animator.runtimeAnimatorController as UnityEditor.Animations.AnimatorController;
if (controller != null) {
layerBlendModes[layer] = MixBlend.First;
if (layer > 0) {
layerBlendModes[layer] = controller.layers[layer].blendingMode == UnityEditor.Animations.AnimatorLayerBlendingMode.Additive ?
MixBlend.Add : MixBlend.Replace;
}
}
}
}
#endif
void GetStateUpdatesFromAnimator (int layer) {
var layerInfos = layerClipInfos[layer];
int clipInfoCount = animator.GetCurrentAnimatorClipInfoCount(layer);
int nextClipInfoCount = animator.GetNextAnimatorClipInfoCount(layer);
var clipInfos = layerInfos.clipInfos;
var nextClipInfos = layerInfos.nextClipInfos;
var interruptingClipInfos = layerInfos.interruptingClipInfos;
layerInfos.isInterruptionActive = (clipInfoCount == 0 && clipInfos.Count != 0 &&
nextClipInfoCount == 0 && nextClipInfos.Count != 0);
// Note: during interruption, GetCurrentAnimatorClipInfoCount and GetNextAnimatorClipInfoCount
// are returning 0 in calls above. Therefore we keep previous clipInfos and nextClipInfos
// until the interruption is over.
if (layerInfos.isInterruptionActive) {
// Note: The last frame of a transition interruption
// will have fullPathHash set to 0, therefore we have to use previous
// frame's infos about interruption clips and correct some values
// accordingly (normalizedTime and weight).
var interruptingStateInfo = animator.GetNextAnimatorStateInfo(layer);
layerInfos.isLastFrameOfInterruption = interruptingStateInfo.fullPathHash == 0;
if (!layerInfos.isLastFrameOfInterruption) {
animator.GetNextAnimatorClipInfo(layer, interruptingClipInfos);
layerInfos.interruptingClipInfoCount = interruptingClipInfos.Count;
float oldTime = layerInfos.interruptingStateInfo.normalizedTime;
float newTime = interruptingStateInfo.normalizedTime;
layerInfos.interruptingClipTimeAddition = newTime - oldTime;
layerInfos.interruptingStateInfo = interruptingStateInfo;
}
} else {
layerInfos.clipInfoCount = clipInfoCount;
layerInfos.nextClipInfoCount = nextClipInfoCount;
layerInfos.interruptingClipInfoCount = 0;
layerInfos.isLastFrameOfInterruption = false;
if (clipInfos.Capacity < clipInfoCount) clipInfos.Capacity = clipInfoCount;
if (nextClipInfos.Capacity < nextClipInfoCount) nextClipInfos.Capacity = nextClipInfoCount;
animator.GetCurrentAnimatorClipInfo(layer, clipInfos);
animator.GetNextAnimatorClipInfo(layer, nextClipInfos);
layerInfos.stateInfo = animator.GetCurrentAnimatorStateInfo(layer);
layerInfos.nextStateInfo = animator.GetNextAnimatorStateInfo(layer);
}
}
void GetAnimatorClipInfos (
int layer,
out bool isInterruptionActive,
out int clipInfoCount,
out int nextClipInfoCount,
out int interruptingClipInfoCount,
out IList<AnimatorClipInfo> clipInfo,
out IList<AnimatorClipInfo> nextClipInfo,
out IList<AnimatorClipInfo> interruptingClipInfo,
out bool shallInterpolateWeightTo1) {
var layerInfos = layerClipInfos[layer];
isInterruptionActive = layerInfos.isInterruptionActive;
clipInfoCount = layerInfos.clipInfoCount;
nextClipInfoCount = layerInfos.nextClipInfoCount;
interruptingClipInfoCount = layerInfos.interruptingClipInfoCount;
clipInfo = layerInfos.clipInfos;
nextClipInfo = layerInfos.nextClipInfos;
interruptingClipInfo = isInterruptionActive ? layerInfos.interruptingClipInfos : null;
shallInterpolateWeightTo1 = layerInfos.isLastFrameOfInterruption;
}
void GetAnimatorStateInfos (
int layer,
out bool isInterruptionActive,
out AnimatorStateInfo stateInfo,
out AnimatorStateInfo nextStateInfo,
out AnimatorStateInfo interruptingStateInfo,
out float interruptingClipTimeAddition) {
var layerInfos = layerClipInfos[layer];
isInterruptionActive = layerInfos.isInterruptionActive;
stateInfo = layerInfos.stateInfo;
nextStateInfo = layerInfos.nextStateInfo;
interruptingStateInfo = layerInfos.interruptingStateInfo;
interruptingClipTimeAddition = layerInfos.isLastFrameOfInterruption ? layerInfos.interruptingClipTimeAddition : 0;
}
Spine.Animation GetAnimation (AnimationClip clip) {
int clipNameHashCode;
if (!clipNameHashCodeTable.TryGetValue(clip, out clipNameHashCode)) {
clipNameHashCode = clip.name.GetHashCode();
clipNameHashCodeTable.Add(clip, clipNameHashCode);
}
Spine.Animation animation;
animationTable.TryGetValue(clipNameHashCode, out animation);
return animation;
}
class AnimationClipEqualityComparer : IEqualityComparer<AnimationClip> {
internal static readonly IEqualityComparer<AnimationClip> Instance = new AnimationClipEqualityComparer();
public bool Equals (AnimationClip x, AnimationClip y) { return x.GetInstanceID() == y.GetInstanceID(); }
public int GetHashCode (AnimationClip o) { return o.GetInstanceID(); }
}
class IntEqualityComparer : IEqualityComparer<int> {
internal static readonly IEqualityComparer<int> Instance = new IntEqualityComparer();
public bool Equals (int x, int y) { return x == y; }
public int GetHashCode(int o) { return o; }
}
}
}
}

View File

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

View File

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

View File

@@ -1,151 +0,0 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
using UnityEngine;
namespace Spine.Unity {
[RequireComponent(typeof(MeshRenderer), typeof(MeshFilter))]
[HelpURL("http://esotericsoftware.com/spine-unity#SkeletonRenderSeparator")]
public class SkeletonPartsRenderer : MonoBehaviour {
#region Properties
MeshGenerator meshGenerator;
public MeshGenerator MeshGenerator {
get {
LazyIntialize();
return meshGenerator;
}
}
MeshRenderer meshRenderer;
public MeshRenderer MeshRenderer {
get {
LazyIntialize();
return meshRenderer;
}
}
MeshFilter meshFilter;
public MeshFilter MeshFilter {
get {
LazyIntialize();
return meshFilter;
}
}
#endregion
#region Callback Delegates
public delegate void SkeletonPartsRendererDelegate (SkeletonPartsRenderer skeletonPartsRenderer);
/// <summary>OnMeshAndMaterialsUpdated is called at the end of LateUpdate after the Mesh and
/// all materials have been updated.</summary>
public event SkeletonPartsRendererDelegate OnMeshAndMaterialsUpdated;
#endregion
MeshRendererBuffers buffers;
SkeletonRendererInstruction currentInstructions = new SkeletonRendererInstruction();
void LazyIntialize () {
if (buffers == null) {
buffers = new MeshRendererBuffers();
buffers.Initialize();
if (meshGenerator != null) return;
meshGenerator = new MeshGenerator();
meshFilter = GetComponent<MeshFilter>();
meshRenderer = GetComponent<MeshRenderer>();
currentInstructions.Clear();
}
}
public void ClearMesh () {
LazyIntialize();
meshFilter.sharedMesh = null;
}
public void RenderParts (ExposedList<SubmeshInstruction> instructions, int startSubmesh, int endSubmesh) {
LazyIntialize();
// STEP 1: Create instruction
var smartMesh = buffers.GetNextMesh();
currentInstructions.SetWithSubset(instructions, startSubmesh, endSubmesh);
bool updateTriangles = SkeletonRendererInstruction.GeometryNotEqual(currentInstructions, smartMesh.instructionUsed);
// STEP 2: Generate mesh buffers.
var currentInstructionsSubmeshesItems = currentInstructions.submeshInstructions.Items;
meshGenerator.Begin();
if (currentInstructions.hasActiveClipping) {
for (int i = 0; i < currentInstructions.submeshInstructions.Count; i++)
meshGenerator.AddSubmesh(currentInstructionsSubmeshesItems[i], updateTriangles);
} else {
meshGenerator.BuildMeshWithArrays(currentInstructions, updateTriangles);
}
buffers.UpdateSharedMaterials(currentInstructions.submeshInstructions);
// STEP 3: modify mesh.
var mesh = smartMesh.mesh;
if (meshGenerator.VertexCount <= 0) { // Clear an empty mesh
updateTriangles = false;
mesh.Clear();
} else {
meshGenerator.FillVertexData(mesh);
if (updateTriangles) {
meshGenerator.FillTriangles(mesh);
meshRenderer.sharedMaterials = buffers.GetUpdatedSharedMaterialsArray();
} else if (buffers.MaterialsChangedInLastUpdate()) {
meshRenderer.sharedMaterials = buffers.GetUpdatedSharedMaterialsArray();
}
meshGenerator.FillLateVertexData(mesh);
}
meshFilter.sharedMesh = mesh;
smartMesh.instructionUsed.Set(currentInstructions);
if (OnMeshAndMaterialsUpdated != null)
OnMeshAndMaterialsUpdated(this);
}
public void SetPropertyBlock (MaterialPropertyBlock block) {
LazyIntialize();
meshRenderer.SetPropertyBlock(block);
}
public static SkeletonPartsRenderer NewPartsRendererGameObject (Transform parent, string name, int sortingOrder = 0) {
var go = new GameObject(name, typeof(MeshFilter), typeof(MeshRenderer));
go.transform.SetParent(parent, false);
var returnComponent = go.AddComponent<SkeletonPartsRenderer>();
returnComponent.MeshRenderer.sortingOrder = sortingOrder;
return returnComponent;
}
}
}

View File

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

View File

@@ -1,269 +0,0 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#if UNITY_2018_3 || UNITY_2019 || UNITY_2018_3_OR_NEWER
#define NEW_PREFAB_SYSTEM
#endif
#define SPINE_OPTIONAL_RENDEROVERRIDE
using UnityEngine;
using System.Collections.Generic;
namespace Spine.Unity {
#if NEW_PREFAB_SYSTEM
[ExecuteAlways]
#else
[ExecuteInEditMode]
#endif
[HelpURL("http://esotericsoftware.com/spine-unity#SkeletonRenderSeparator")]
public class SkeletonRenderSeparator : MonoBehaviour {
public const int DefaultSortingOrderIncrement = 5;
#region Inspector
[SerializeField]
protected SkeletonRenderer skeletonRenderer;
public SkeletonRenderer SkeletonRenderer {
get { return skeletonRenderer; }
set {
#if SPINE_OPTIONAL_RENDEROVERRIDE
if (skeletonRenderer != null)
skeletonRenderer.GenerateMeshOverride -= HandleRender;
#endif
skeletonRenderer = value;
if (value == null)
this.enabled = false;
}
}
MeshRenderer mainMeshRenderer;
public bool copyPropertyBlock = true;
[Tooltip("Copies MeshRenderer flags into each parts renderer")]
public bool copyMeshRendererFlags = true;
public List<Spine.Unity.SkeletonPartsRenderer> partsRenderers = new List<SkeletonPartsRenderer>();
#if UNITY_EDITOR
void Reset () {
if (skeletonRenderer == null)
skeletonRenderer = GetComponent<SkeletonRenderer>();
}
#endif
#endregion
#region Callback Delegates
/// <summary>OnMeshAndMaterialsUpdated is called at the end of LateUpdate after the Mesh and
/// all materials have been updated.</summary>
public event SkeletonRenderer.SkeletonRendererDelegate OnMeshAndMaterialsUpdated;
#endregion
#region Runtime Instantiation
/// <summary>Adds a SkeletonRenderSeparator and child SkeletonPartsRenderer GameObjects to a given SkeletonRenderer.</summary>
/// <returns>The to skeleton renderer.</returns>
/// <param name="skeletonRenderer">The target SkeletonRenderer or SkeletonAnimation.</param>
/// <param name="sortingLayerID">Sorting layer to be used for the parts renderers.</param>
/// <param name="extraPartsRenderers">Number of additional SkeletonPartsRenderers on top of the ones determined by counting the number of separator slots.</param>
/// <param name="sortingOrderIncrement">The integer to increment the sorting order per SkeletonPartsRenderer to separate them.</param>
/// <param name="baseSortingOrder">The sorting order value of the first SkeletonPartsRenderer.</param>
/// <param name="addMinimumPartsRenderers">If set to <c>true</c>, a minimum number of SkeletonPartsRenderer GameObjects (determined by separatorSlots.Count + 1) will be added.</param>
public static SkeletonRenderSeparator AddToSkeletonRenderer (SkeletonRenderer skeletonRenderer, int sortingLayerID = 0, int extraPartsRenderers = 0, int sortingOrderIncrement = DefaultSortingOrderIncrement, int baseSortingOrder = 0, bool addMinimumPartsRenderers = true) {
if (skeletonRenderer == null) {
Debug.Log("Tried to add SkeletonRenderSeparator to a null SkeletonRenderer reference.");
return null;
}
var srs = skeletonRenderer.gameObject.AddComponent<SkeletonRenderSeparator>();
srs.skeletonRenderer = skeletonRenderer;
skeletonRenderer.Initialize(false);
int count = extraPartsRenderers;
if (addMinimumPartsRenderers)
count = extraPartsRenderers + skeletonRenderer.separatorSlots.Count + 1;
var skeletonRendererTransform = skeletonRenderer.transform;
var componentRenderers = srs.partsRenderers;
for (int i = 0; i < count; i++) {
var spr = SkeletonPartsRenderer.NewPartsRendererGameObject(skeletonRendererTransform, i.ToString());
var mr = spr.MeshRenderer;
mr.sortingLayerID = sortingLayerID;
mr.sortingOrder = baseSortingOrder + (i * sortingOrderIncrement);
componentRenderers.Add(spr);
}
srs.OnEnable();
#if UNITY_EDITOR
// Make sure editor updates properly in edit mode.
if (!Application.isPlaying) {
skeletonRenderer.enabled = false;
skeletonRenderer.enabled = true;
skeletonRenderer.LateUpdate();
}
#endif
return srs;
}
/// <summary>Add a child SkeletonPartsRenderer GameObject to this SkeletonRenderSeparator.</summary>
public SkeletonPartsRenderer AddPartsRenderer (int sortingOrderIncrement = DefaultSortingOrderIncrement, string name = null) {
int sortingLayerID = 0;
int sortingOrder = 0;
if (partsRenderers.Count > 0) {
var previous = partsRenderers[partsRenderers.Count - 1];
var previousMeshRenderer = previous.MeshRenderer;
sortingLayerID = previousMeshRenderer.sortingLayerID;
sortingOrder = previousMeshRenderer.sortingOrder + sortingOrderIncrement;
}
if (string.IsNullOrEmpty(name))
name = partsRenderers.Count.ToString();
var spr = SkeletonPartsRenderer.NewPartsRendererGameObject(skeletonRenderer.transform, name);
partsRenderers.Add(spr);
var mr = spr.MeshRenderer;
mr.sortingLayerID = sortingLayerID;
mr.sortingOrder = sortingOrder;
return spr;
}
#endregion
public void OnEnable () {
if (skeletonRenderer == null) return;
if (copiedBlock == null) copiedBlock = new MaterialPropertyBlock();
mainMeshRenderer = skeletonRenderer.GetComponent<MeshRenderer>();
#if SPINE_OPTIONAL_RENDEROVERRIDE
skeletonRenderer.GenerateMeshOverride -= HandleRender;
skeletonRenderer.GenerateMeshOverride += HandleRender;
#endif
if (copyMeshRendererFlags) {
var lightProbeUsage = mainMeshRenderer.lightProbeUsage;
bool receiveShadows = mainMeshRenderer.receiveShadows;
var reflectionProbeUsage = mainMeshRenderer.reflectionProbeUsage;
var shadowCastingMode = mainMeshRenderer.shadowCastingMode;
var motionVectorGenerationMode = mainMeshRenderer.motionVectorGenerationMode;
var probeAnchor = mainMeshRenderer.probeAnchor;
for (int i = 0; i < partsRenderers.Count; i++) {
var currentRenderer = partsRenderers[i];
if (currentRenderer == null) continue; // skip null items.
var mr = currentRenderer.MeshRenderer;
mr.lightProbeUsage = lightProbeUsage;
mr.receiveShadows = receiveShadows;
mr.reflectionProbeUsage = reflectionProbeUsage;
mr.shadowCastingMode = shadowCastingMode;
mr.motionVectorGenerationMode = motionVectorGenerationMode;
mr.probeAnchor = probeAnchor;
}
}
}
public void OnDisable () {
if (skeletonRenderer == null) return;
#if SPINE_OPTIONAL_RENDEROVERRIDE
skeletonRenderer.GenerateMeshOverride -= HandleRender;
#endif
skeletonRenderer.LateUpdate();
foreach (var partsRenderer in partsRenderers) {
if (partsRenderer != null)
partsRenderer.ClearMesh();
}
}
MaterialPropertyBlock copiedBlock;
void HandleRender (SkeletonRendererInstruction instruction) {
int rendererCount = partsRenderers.Count;
if (rendererCount <= 0) return;
if (copyPropertyBlock)
mainMeshRenderer.GetPropertyBlock(copiedBlock);
var settings = new MeshGenerator.Settings {
addNormals = skeletonRenderer.addNormals,
calculateTangents = skeletonRenderer.calculateTangents,
immutableTriangles = false, // parts cannot do immutable triangles.
pmaVertexColors = skeletonRenderer.pmaVertexColors,
tintBlack = skeletonRenderer.tintBlack,
useClipping = true,
zSpacing = skeletonRenderer.zSpacing
};
var submeshInstructions = instruction.submeshInstructions;
var submeshInstructionsItems = submeshInstructions.Items;
int lastSubmeshInstruction = submeshInstructions.Count - 1;
int rendererIndex = 0;
var currentRenderer = partsRenderers[rendererIndex];
for (int si = 0, start = 0; si <= lastSubmeshInstruction; si++) {
if (currentRenderer == null)
continue;
if (submeshInstructionsItems[si].forceSeparate || si == lastSubmeshInstruction) {
// Apply properties
var meshGenerator = currentRenderer.MeshGenerator;
meshGenerator.settings = settings;
if (copyPropertyBlock)
currentRenderer.SetPropertyBlock(copiedBlock);
// Render
currentRenderer.RenderParts(instruction.submeshInstructions, start, si + 1);
start = si + 1;
rendererIndex++;
if (rendererIndex < rendererCount) {
currentRenderer = partsRenderers[rendererIndex];
} else {
// Not enough renderers. Skip the rest of the instructions.
break;
}
}
}
if (OnMeshAndMaterialsUpdated != null)
OnMeshAndMaterialsUpdated(this.skeletonRenderer);
// Clear extra renderers if they exist.
for (; rendererIndex < rendererCount; rendererIndex++) {
currentRenderer = partsRenderers[rendererIndex];
if (currentRenderer != null)
partsRenderers[rendererIndex].ClearMesh();
}
}
}
}

View File

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

View File

@@ -1,711 +0,0 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#if UNITY_2018_3 || UNITY_2019 || UNITY_2018_3_OR_NEWER
#define NEW_PREFAB_SYSTEM
#endif
#if UNITY_2018_1_OR_NEWER
#define PER_MATERIAL_PROPERTY_BLOCKS
#endif
#if UNITY_2017_1_OR_NEWER
#define BUILT_IN_SPRITE_MASK_COMPONENT
#endif
#if UNITY_2019_3_OR_NEWER
#define CONFIGURABLE_ENTER_PLAY_MODE
#endif
#define SPINE_OPTIONAL_RENDEROVERRIDE
#define SPINE_OPTIONAL_MATERIALOVERRIDE
using System.Collections.Generic;
using UnityEngine;
namespace Spine.Unity {
/// <summary>Base class of animated Spine skeleton components. This component manages and renders a skeleton.</summary>
#if NEW_PREFAB_SYSTEM
[ExecuteAlways]
#else
[ExecuteInEditMode]
#endif
[RequireComponent(typeof(MeshFilter), typeof(MeshRenderer)), DisallowMultipleComponent]
[HelpURL("http://esotericsoftware.com/spine-unity#SkeletonRenderer-Component")]
public class SkeletonRenderer : MonoBehaviour, ISkeletonComponent, IHasSkeletonDataAsset {
public SkeletonDataAsset skeletonDataAsset;
#region Initialization settings
/// <summary>Skin name to use when the Skeleton is initialized.</summary>
[SpineSkin(defaultAsEmptyString:true)] public string initialSkinName;
/// <summary>Enable this parameter when overwriting the Skeleton's skin from an editor script.
/// Otherwise any changes will be overwritten by the next inspector update.</summary>
#if UNITY_EDITOR
public bool EditorSkipSkinSync {
get { return editorSkipSkinSync; }
set { editorSkipSkinSync = value; }
}
protected bool editorSkipSkinSync = false;
#endif
/// <summary>Flip X and Y to use when the Skeleton is initialized.</summary>
public bool initialFlipX, initialFlipY;
#endregion
#region Advanced Render Settings
/// <summary>Update mode to optionally limit updates to e.g. only apply animations but not update the mesh.</summary>
public UpdateMode UpdateMode { get { return updateMode; } set { updateMode = value; } }
protected UpdateMode updateMode = UpdateMode.FullUpdate;
/// <summary>Update mode used when the MeshRenderer becomes invisible
/// (when <c>OnBecameInvisible()</c> is called). Update mode is automatically
/// reset to <c>UpdateMode.FullUpdate</c> when the mesh becomes visible again.</summary>
public UpdateMode updateWhenInvisible = UpdateMode.FullUpdate;
// Submesh Separation
/// <summary>Slot names used to populate separatorSlots list when the Skeleton is initialized. Changing this after initialization does nothing.</summary>
[UnityEngine.Serialization.FormerlySerializedAs("submeshSeparators")][SerializeField][SpineSlot] protected string[] separatorSlotNames = new string[0];
/// <summary>Slots that determine where the render is split. This is used by components such as SkeletonRenderSeparator so that the skeleton can be rendered by two separate renderers on different GameObjects.</summary>
[System.NonSerialized] public readonly List<Slot> separatorSlots = new List<Slot>();
// Render Settings
[Range(-0.1f, 0f)] public float zSpacing;
/// <summary>Use Spine's clipping feature. If false, ClippingAttachments will be ignored.</summary>
public bool useClipping = true;
/// <summary>If true, triangles will not be updated. Enable this as an optimization if the skeleton does not make use of attachment swapping or hiding, or draw order keys. Otherwise, setting this to false may cause errors in rendering.</summary>
public bool immutableTriangles = false;
/// <summary>Multiply vertex color RGB with vertex color alpha. Set this to true if the shader used for rendering is a premultiplied alpha shader. Setting this to false disables single-batch additive slots.</summary>
public bool pmaVertexColors = true;
/// <summary>Clears the state of the render and skeleton when this component or its GameObject is disabled. This prevents previous state from being retained when it is enabled again. When pooling your skeleton, setting this to true can be helpful.</summary>
public bool clearStateOnDisable = false;
/// <summary>If true, second colors on slots will be added to the output Mesh as UV2 and UV3. A special "tint black" shader that interprets UV2 and UV3 as black point colors is required to render this properly.</summary>
public bool tintBlack = false;
/// <summary>If true, the renderer assumes the skeleton only requires one Material and one submesh to render. This allows the MeshGenerator to skip checking for changes in Materials. Enable this as an optimization if the skeleton only uses one Material.</summary>
/// <remarks>This disables SkeletonRenderSeparator functionality.</remarks>
public bool singleSubmesh = false;
#if PER_MATERIAL_PROPERTY_BLOCKS
/// <summary> Applies only when 3+ submeshes are used (2+ materials with alternating order, e.g. "A B A").
/// If true, GPU instancing is disabled at all materials and MaterialPropertyBlocks are assigned at each
/// material to prevent aggressive batching of submeshes by e.g. the LWRP renderer, leading to incorrect
/// draw order (e.g. "A1 B A2" changed to "A1A2 B").
/// You can disable this parameter when everything is drawn correctly to save the additional performance cost.
/// </summary>
public bool fixDrawOrder = false;
#endif
/// <summary>If true, the mesh generator adds normals to the output mesh. For better performance and reduced memory requirements, use a shader that assumes the desired normal.</summary>
[UnityEngine.Serialization.FormerlySerializedAs("calculateNormals")] public bool addNormals = false;
/// <summary>If true, tangents are calculated every frame and added to the Mesh. Enable this when using a shader that uses lighting that requires tangents.</summary>
public bool calculateTangents = false;
#if BUILT_IN_SPRITE_MASK_COMPONENT
/// <summary>This enum controls the mode under which the sprite will interact with the masking system.</summary>
/// <remarks>Interaction modes with <see cref="UnityEngine.SpriteMask"/> components are identical to Unity's <see cref="UnityEngine.SpriteRenderer"/>,
/// see https://docs.unity3d.com/ScriptReference/SpriteMaskInteraction.html. </remarks>
public SpriteMaskInteraction maskInteraction = SpriteMaskInteraction.None;
[System.Serializable]
public class SpriteMaskInteractionMaterials {
public bool AnyMaterialCreated {
get {
return materialsMaskDisabled.Length > 0 ||
materialsInsideMask.Length > 0 ||
materialsOutsideMask.Length > 0;
}
}
/// <summary>Material references for switching material sets at runtime when <see cref="SkeletonRenderer.maskInteraction"/> changes to <see cref="SpriteMaskInteraction.None"/>.</summary>
public Material[] materialsMaskDisabled = new Material[0];
/// <summary>Material references for switching material sets at runtime when <see cref="SkeletonRenderer.maskInteraction"/> changes to <see cref="SpriteMaskInteraction.VisibleInsideMask"/>.</summary>
public Material[] materialsInsideMask = new Material[0];
/// <summary>Material references for switching material sets at runtime when <see cref="SkeletonRenderer.maskInteraction"/> changes to <see cref="SpriteMaskInteraction.VisibleOutsideMask"/>.</summary>
public Material[] materialsOutsideMask = new Material[0];
}
/// <summary>Material references for switching material sets at runtime when <see cref="SkeletonRenderer.maskInteraction"/> changes.</summary>
public SpriteMaskInteractionMaterials maskMaterials = new SpriteMaskInteractionMaterials();
/// <summary>Shader property ID used for the Stencil comparison function.</summary>
public static readonly int STENCIL_COMP_PARAM_ID = Shader.PropertyToID("_StencilComp");
/// <summary>Shader property value used as Stencil comparison function for <see cref="SpriteMaskInteraction.None"/>.</summary>
public const UnityEngine.Rendering.CompareFunction STENCIL_COMP_MASKINTERACTION_NONE = UnityEngine.Rendering.CompareFunction.Always;
/// <summary>Shader property value used as Stencil comparison function for <see cref="SpriteMaskInteraction.VisibleInsideMask"/>.</summary>
public const UnityEngine.Rendering.CompareFunction STENCIL_COMP_MASKINTERACTION_VISIBLE_INSIDE = UnityEngine.Rendering.CompareFunction.LessEqual;
/// <summary>Shader property value used as Stencil comparison function for <see cref="SpriteMaskInteraction.VisibleOutsideMask"/>.</summary>
public const UnityEngine.Rendering.CompareFunction STENCIL_COMP_MASKINTERACTION_VISIBLE_OUTSIDE = UnityEngine.Rendering.CompareFunction.Greater;
#if UNITY_EDITOR
private static bool haveStencilParametersBeenFixed = false;
#endif
#endif // #if BUILT_IN_SPRITE_MASK_COMPONENT
#endregion
#region Overrides
#if SPINE_OPTIONAL_RENDEROVERRIDE
// These are API for anything that wants to take over rendering for a SkeletonRenderer.
public bool disableRenderingOnOverride = true;
public delegate void InstructionDelegate (SkeletonRendererInstruction instruction);
event InstructionDelegate generateMeshOverride;
/// <summary>Allows separate code to take over rendering for this SkeletonRenderer component. The subscriber is passed a SkeletonRendererInstruction argument to determine how to render a skeleton.</summary>
public event InstructionDelegate GenerateMeshOverride {
add {
generateMeshOverride += value;
if (disableRenderingOnOverride && generateMeshOverride != null) {
Initialize(false);
if (meshRenderer)
meshRenderer.enabled = false;
}
}
remove {
generateMeshOverride -= value;
if (disableRenderingOnOverride && generateMeshOverride == null) {
Initialize(false);
if (meshRenderer)
meshRenderer.enabled = true;
}
}
}
/// <summary> Occurs after the vertex data is populated every frame, before the vertices are pushed into the mesh.</summary>
public event Spine.Unity.MeshGeneratorDelegate OnPostProcessVertices;
#endif
#if SPINE_OPTIONAL_MATERIALOVERRIDE
[System.NonSerialized] readonly Dictionary<Material, Material> customMaterialOverride = new Dictionary<Material, Material>();
/// <summary>Use this Dictionary to override a Material with a different Material.</summary>
public Dictionary<Material, Material> CustomMaterialOverride { get { return customMaterialOverride; } }
#endif
[System.NonSerialized] readonly Dictionary<Slot, Material> customSlotMaterials = new Dictionary<Slot, Material>();
/// <summary>Use this Dictionary to use a different Material to render specific Slots.</summary>
public Dictionary<Slot, Material> CustomSlotMaterials { get { return customSlotMaterials; } }
#endregion
#region Mesh Generator
[System.NonSerialized] readonly SkeletonRendererInstruction currentInstructions = new SkeletonRendererInstruction();
readonly MeshGenerator meshGenerator = new MeshGenerator();
[System.NonSerialized] readonly MeshRendererBuffers rendererBuffers = new MeshRendererBuffers();
#endregion
#region Cached component references
MeshRenderer meshRenderer;
MeshFilter meshFilter;
#endregion
#region Skeleton
[System.NonSerialized] public bool valid;
[System.NonSerialized] public Skeleton skeleton;
public Skeleton Skeleton {
get {
Initialize(false);
return skeleton;
}
}
#endregion
public delegate void SkeletonRendererDelegate (SkeletonRenderer skeletonRenderer);
/// <summary>OnRebuild is raised after the Skeleton is successfully initialized.</summary>
public event SkeletonRendererDelegate OnRebuild;
/// <summary>OnMeshAndMaterialsUpdated is called at the end of LateUpdate after the Mesh and
/// all materials have been updated.</summary>
public event SkeletonRendererDelegate OnMeshAndMaterialsUpdated;
public SkeletonDataAsset SkeletonDataAsset { get { return skeletonDataAsset; } } // ISkeletonComponent
#region Runtime Instantiation
public static T NewSpineGameObject<T> (SkeletonDataAsset skeletonDataAsset, bool quiet = false) where T : SkeletonRenderer {
return SkeletonRenderer.AddSpineComponent<T>(new GameObject("New Spine GameObject"), skeletonDataAsset, quiet);
}
/// <summary>Add and prepare a Spine component that derives from SkeletonRenderer to a GameObject at runtime.</summary>
/// <typeparam name="T">T should be SkeletonRenderer or any of its derived classes.</typeparam>
public static T AddSpineComponent<T> (GameObject gameObject, SkeletonDataAsset skeletonDataAsset, bool quiet = false) where T : SkeletonRenderer {
var c = gameObject.AddComponent<T>();
if (skeletonDataAsset != null) {
c.skeletonDataAsset = skeletonDataAsset;
c.Initialize(false, quiet);
}
return c;
}
/// <summary>Applies MeshGenerator settings to the SkeletonRenderer and its internal MeshGenerator.</summary>
public void SetMeshSettings (MeshGenerator.Settings settings) {
this.calculateTangents = settings.calculateTangents;
this.immutableTriangles = settings.immutableTriangles;
this.pmaVertexColors = settings.pmaVertexColors;
this.tintBlack = settings.tintBlack;
this.useClipping = settings.useClipping;
this.zSpacing = settings.zSpacing;
this.meshGenerator.settings = settings;
}
#endregion
public virtual void Awake () {
Initialize(false);
updateMode = updateWhenInvisible;
}
#if UNITY_EDITOR && CONFIGURABLE_ENTER_PLAY_MODE
public virtual void Start () {
Initialize(false);
}
#endif
void OnDisable () {
if (clearStateOnDisable && valid)
ClearState();
}
void OnDestroy () {
rendererBuffers.Dispose();
valid = false;
}
/// <summary>
/// Clears the previously generated mesh and resets the skeleton's pose.</summary>
public virtual void ClearState () {
var meshFilter = GetComponent<MeshFilter>();
if (meshFilter != null) meshFilter.sharedMesh = null;
currentInstructions.Clear();
if (skeleton != null) skeleton.SetToSetupPose();
}
/// <summary>
/// Sets a minimum buffer size for the internal MeshGenerator to prevent excess allocations during animation.
/// </summary>
public void EnsureMeshGeneratorCapacity (int minimumVertexCount) {
meshGenerator.EnsureVertexCapacity(minimumVertexCount);
}
/// <summary>
/// Initialize this component. Attempts to load the SkeletonData and creates the internal Skeleton object and buffers.</summary>
/// <param name="overwrite">If set to <c>true</c>, it will overwrite internal objects if they were already generated. Otherwise, the initialized component will ignore subsequent calls to initialize.</param>
public virtual void Initialize (bool overwrite, bool quiet = false) {
if (valid && !overwrite)
return;
// Clear
{
// Note: do not reset meshFilter.sharedMesh or meshRenderer.sharedMaterial to null,
// otherwise constant reloading will be triggered at prefabs.
currentInstructions.Clear();
rendererBuffers.Clear();
meshGenerator.Begin();
skeleton = null;
valid = false;
}
if (skeletonDataAsset == null)
return;
SkeletonData skeletonData = skeletonDataAsset.GetSkeletonData(false);
if (skeletonData == null) return;
valid = true;
meshFilter = GetComponent<MeshFilter>();
meshRenderer = GetComponent<MeshRenderer>();
rendererBuffers.Initialize();
skeleton = new Skeleton(skeletonData) {
ScaleX = initialFlipX ? -1 : 1,
ScaleY = initialFlipY ? -1 : 1
};
if (!string.IsNullOrEmpty(initialSkinName) && !string.Equals(initialSkinName, "default", System.StringComparison.Ordinal))
skeleton.SetSkin(initialSkinName);
separatorSlots.Clear();
for (int i = 0; i < separatorSlotNames.Length; i++)
separatorSlots.Add(skeleton.FindSlot(separatorSlotNames[i]));
LateUpdate(); // Generate mesh for the first frame it exists.
if (OnRebuild != null)
OnRebuild(this);
#if UNITY_EDITOR
if (!Application.isPlaying) {
string errorMessage = null;
if (!quiet && MaterialChecks.IsMaterialSetupProblematic(this, ref errorMessage))
Debug.LogWarningFormat(this, "Problematic material setup at {0}: {1}", this.name, errorMessage);
}
#endif
}
/// <summary>
/// Generates a new UnityEngine.Mesh from the internal Skeleton.</summary>
public virtual void LateUpdate () {
if (!valid) return;
#if UNITY_EDITOR && NEW_PREFAB_SYSTEM
// Don't store mesh or material at the prefab, otherwise it will permanently reload
var prefabType = UnityEditor.PrefabUtility.GetPrefabAssetType(this);
if (UnityEditor.PrefabUtility.IsPartOfPrefabAsset(this) &&
(prefabType == UnityEditor.PrefabAssetType.Regular || prefabType == UnityEditor.PrefabAssetType.Variant)) {
return;
}
#endif
if (updateMode != UpdateMode.FullUpdate) return;
#if SPINE_OPTIONAL_RENDEROVERRIDE
bool doMeshOverride = generateMeshOverride != null;
if ((!meshRenderer.enabled) && !doMeshOverride) return;
#else
const bool doMeshOverride = false;
if (!meshRenderer.enabled) return;
#endif
var currentInstructions = this.currentInstructions;
var workingSubmeshInstructions = currentInstructions.submeshInstructions;
var currentSmartMesh = rendererBuffers.GetNextMesh(); // Double-buffer for performance.
bool updateTriangles;
if (this.singleSubmesh) {
// STEP 1. Determine a SmartMesh.Instruction. Split up instructions into submeshes. =============================================
MeshGenerator.GenerateSingleSubmeshInstruction(currentInstructions, skeleton, skeletonDataAsset.atlasAssets[0].PrimaryMaterial);
// STEP 1.9. Post-process workingInstructions. ==================================================================================
#if SPINE_OPTIONAL_MATERIALOVERRIDE
if (customMaterialOverride.Count > 0) // isCustomMaterialOverridePopulated
MeshGenerator.TryReplaceMaterials(workingSubmeshInstructions, customMaterialOverride);
#endif
// STEP 2. Update vertex buffer based on verts from the attachments. ===========================================================
meshGenerator.settings = new MeshGenerator.Settings {
pmaVertexColors = this.pmaVertexColors,
zSpacing = this.zSpacing,
useClipping = this.useClipping,
tintBlack = this.tintBlack,
calculateTangents = this.calculateTangents,
addNormals = this.addNormals
};
meshGenerator.Begin();
updateTriangles = SkeletonRendererInstruction.GeometryNotEqual(currentInstructions, currentSmartMesh.instructionUsed);
if (currentInstructions.hasActiveClipping) {
meshGenerator.AddSubmesh(workingSubmeshInstructions.Items[0], updateTriangles);
} else {
meshGenerator.BuildMeshWithArrays(currentInstructions, updateTriangles);
}
} else {
// STEP 1. Determine a SmartMesh.Instruction. Split up instructions into submeshes. =============================================
MeshGenerator.GenerateSkeletonRendererInstruction(currentInstructions, skeleton, customSlotMaterials, separatorSlots, doMeshOverride, this.immutableTriangles);
// STEP 1.9. Post-process workingInstructions. ==================================================================================
#if SPINE_OPTIONAL_MATERIALOVERRIDE
if (customMaterialOverride.Count > 0) // isCustomMaterialOverridePopulated
MeshGenerator.TryReplaceMaterials(workingSubmeshInstructions, customMaterialOverride);
#endif
#if SPINE_OPTIONAL_RENDEROVERRIDE
if (doMeshOverride) {
this.generateMeshOverride(currentInstructions);
if (disableRenderingOnOverride) return;
}
#endif
updateTriangles = SkeletonRendererInstruction.GeometryNotEqual(currentInstructions, currentSmartMesh.instructionUsed);
// STEP 2. Update vertex buffer based on verts from the attachments. ===========================================================
meshGenerator.settings = new MeshGenerator.Settings {
pmaVertexColors = this.pmaVertexColors,
zSpacing = this.zSpacing,
useClipping = this.useClipping,
tintBlack = this.tintBlack,
calculateTangents = this.calculateTangents,
addNormals = this.addNormals
};
meshGenerator.Begin();
if (currentInstructions.hasActiveClipping)
meshGenerator.BuildMesh(currentInstructions, updateTriangles);
else
meshGenerator.BuildMeshWithArrays(currentInstructions, updateTriangles);
}
if (OnPostProcessVertices != null) OnPostProcessVertices.Invoke(this.meshGenerator.Buffers);
// STEP 3. Move the mesh data into a UnityEngine.Mesh ===========================================================================
var currentMesh = currentSmartMesh.mesh;
meshGenerator.FillVertexData(currentMesh);
rendererBuffers.UpdateSharedMaterials(workingSubmeshInstructions);
bool materialsChanged = rendererBuffers.MaterialsChangedInLastUpdate();
if (updateTriangles) { // Check if the triangles should also be updated.
meshGenerator.FillTriangles(currentMesh);
meshRenderer.sharedMaterials = rendererBuffers.GetUpdatedSharedMaterialsArray();
} else if (materialsChanged) {
meshRenderer.sharedMaterials = rendererBuffers.GetUpdatedSharedMaterialsArray();
}
if (materialsChanged && (this.maskMaterials.AnyMaterialCreated)) {
this.maskMaterials = new SpriteMaskInteractionMaterials();
}
meshGenerator.FillLateVertexData(currentMesh);
// STEP 4. The UnityEngine.Mesh is ready. Set it as the MeshFilter's mesh. Store the instructions used for that mesh. ===========
meshFilter.sharedMesh = currentMesh;
currentSmartMesh.instructionUsed.Set(currentInstructions);
#if BUILT_IN_SPRITE_MASK_COMPONENT
if (meshRenderer != null) {
AssignSpriteMaskMaterials();
}
#endif
#if PER_MATERIAL_PROPERTY_BLOCKS
if (fixDrawOrder && meshRenderer.sharedMaterials.Length > 2) {
SetMaterialSettingsToFixDrawOrder();
}
#endif
if (OnMeshAndMaterialsUpdated != null)
OnMeshAndMaterialsUpdated(this);
}
public void OnBecameVisible () {
UpdateMode previousUpdateMode = updateMode;
updateMode = UpdateMode.FullUpdate;
if (previousUpdateMode != UpdateMode.FullUpdate)
LateUpdate(); // OnBecameVisible is called after LateUpdate()
}
public void OnBecameInvisible () {
updateMode = updateWhenInvisible;
}
public void FindAndApplySeparatorSlots (string startsWith, bool clearExistingSeparators = true, bool updateStringArray = false) {
if (string.IsNullOrEmpty(startsWith)) return;
FindAndApplySeparatorSlots(
(slotName) => slotName.StartsWith(startsWith),
clearExistingSeparators,
updateStringArray
);
}
public void FindAndApplySeparatorSlots (System.Func<string, bool> slotNamePredicate, bool clearExistingSeparators = true, bool updateStringArray = false) {
if (slotNamePredicate == null) return;
if (!valid) return;
if (clearExistingSeparators)
separatorSlots.Clear();
var slots = skeleton.slots;
foreach (var slot in slots) {
if (slotNamePredicate.Invoke(slot.data.name))
separatorSlots.Add(slot);
}
if (updateStringArray) {
var detectedSeparatorNames = new List<string>();
foreach (var slot in skeleton.slots) {
string slotName = slot.data.name;
if (slotNamePredicate.Invoke(slotName))
detectedSeparatorNames.Add(slotName);
}
if (!clearExistingSeparators) {
string[] originalNames = this.separatorSlotNames;
foreach (string originalName in originalNames)
detectedSeparatorNames.Add(originalName);
}
this.separatorSlotNames = detectedSeparatorNames.ToArray();
}
}
public void ReapplySeparatorSlotNames () {
if (!valid)
return;
separatorSlots.Clear();
for (int i = 0, n = separatorSlotNames.Length; i < n; i++) {
var slot = skeleton.FindSlot(separatorSlotNames[i]);
if (slot != null) {
separatorSlots.Add(slot);
}
#if UNITY_EDITOR
else if (!string.IsNullOrEmpty(separatorSlotNames[i]))
{
Debug.LogWarning(separatorSlotNames[i] + " is not a slot in " + skeletonDataAsset.skeletonJSON.name);
}
#endif
}
}
#if BUILT_IN_SPRITE_MASK_COMPONENT
private void AssignSpriteMaskMaterials()
{
#if UNITY_EDITOR
if (!Application.isPlaying && !UnityEditor.EditorApplication.isUpdating) {
EditorFixStencilCompParameters();
}
#endif
if (Application.isPlaying) {
if (maskInteraction != SpriteMaskInteraction.None && maskMaterials.materialsMaskDisabled.Length == 0)
maskMaterials.materialsMaskDisabled = meshRenderer.sharedMaterials;
}
if (maskMaterials.materialsMaskDisabled.Length > 0 && maskMaterials.materialsMaskDisabled[0] != null &&
maskInteraction == SpriteMaskInteraction.None) {
this.meshRenderer.materials = maskMaterials.materialsMaskDisabled;
}
else if (maskInteraction == SpriteMaskInteraction.VisibleInsideMask) {
if (maskMaterials.materialsInsideMask.Length == 0 || maskMaterials.materialsInsideMask[0] == null) {
if (!InitSpriteMaskMaterialsInsideMask())
return;
}
this.meshRenderer.materials = maskMaterials.materialsInsideMask;
}
else if (maskInteraction == SpriteMaskInteraction.VisibleOutsideMask) {
if (maskMaterials.materialsOutsideMask.Length == 0 || maskMaterials.materialsOutsideMask[0] == null) {
if (!InitSpriteMaskMaterialsOutsideMask())
return;
}
this.meshRenderer.materials = maskMaterials.materialsOutsideMask;
}
}
private bool InitSpriteMaskMaterialsInsideMask()
{
return InitSpriteMaskMaterialsForMaskType(STENCIL_COMP_MASKINTERACTION_VISIBLE_INSIDE, ref maskMaterials.materialsInsideMask);
}
private bool InitSpriteMaskMaterialsOutsideMask()
{
return InitSpriteMaskMaterialsForMaskType(STENCIL_COMP_MASKINTERACTION_VISIBLE_OUTSIDE, ref maskMaterials.materialsOutsideMask);
}
private bool InitSpriteMaskMaterialsForMaskType(UnityEngine.Rendering.CompareFunction maskFunction, ref Material[] materialsToFill)
{
#if UNITY_EDITOR
if (!Application.isPlaying) {
return false;
}
#endif
var originalMaterials = maskMaterials.materialsMaskDisabled;
materialsToFill = new Material[originalMaterials.Length];
for (int i = 0; i < originalMaterials.Length; i++) {
Material newMaterial = new Material(originalMaterials[i]);
newMaterial.SetFloat(STENCIL_COMP_PARAM_ID, (int)maskFunction);
materialsToFill[i] = newMaterial;
}
return true;
}
#if UNITY_EDITOR
private void EditorFixStencilCompParameters() {
if (!haveStencilParametersBeenFixed && HasAnyStencilComp0Material()) {
haveStencilParametersBeenFixed = true;
FixAllProjectMaterialsStencilCompParameters();
}
}
private void FixAllProjectMaterialsStencilCompParameters() {
string[] materialGUIDS = UnityEditor.AssetDatabase.FindAssets("t:material");
foreach (var guid in materialGUIDS) {
string path = UnityEditor.AssetDatabase.GUIDToAssetPath(guid);
if (!string.IsNullOrEmpty(path)) {
var mat = UnityEditor.AssetDatabase.LoadAssetAtPath<Material>(path);
if (mat.HasProperty(STENCIL_COMP_PARAM_ID) && mat.GetFloat(STENCIL_COMP_PARAM_ID) == 0) {
mat.SetFloat(STENCIL_COMP_PARAM_ID, (int)STENCIL_COMP_MASKINTERACTION_NONE);
}
}
}
UnityEditor.AssetDatabase.Refresh();
UnityEditor.AssetDatabase.SaveAssets();
}
private bool HasAnyStencilComp0Material() {
if (meshRenderer == null)
return false;
foreach (var mat in meshRenderer.sharedMaterials) {
if (mat != null && mat.HasProperty(STENCIL_COMP_PARAM_ID)) {
float currentCompValue = mat.GetFloat(STENCIL_COMP_PARAM_ID);
if (currentCompValue == 0)
return true;
}
}
return false;
}
#endif // UNITY_EDITOR
#endif //#if BUILT_IN_SPRITE_MASK_COMPONENT
#if PER_MATERIAL_PROPERTY_BLOCKS
private MaterialPropertyBlock reusedPropertyBlock;
public static readonly int SUBMESH_DUMMY_PARAM_ID = Shader.PropertyToID("_Submesh");
/// <summary>
/// This method was introduced as a workaround for too aggressive submesh draw call batching,
/// leading to incorrect draw order when 3+ materials are used at submeshes in alternating order.
/// Otherwise, e.g. when using Lightweight Render Pipeline, deliberately separated draw calls
/// "A1 B A2" are reordered to "A1A2 B", regardless of batching-related project settings.
/// </summary>
private void SetMaterialSettingsToFixDrawOrder() {
if (reusedPropertyBlock == null) reusedPropertyBlock = new MaterialPropertyBlock();
bool hasPerRendererBlock = meshRenderer.HasPropertyBlock();
if (hasPerRendererBlock) {
meshRenderer.GetPropertyBlock(reusedPropertyBlock);
}
for (int i = 0; i < meshRenderer.sharedMaterials.Length; ++i) {
if (!meshRenderer.sharedMaterials[i])
continue;
if (!hasPerRendererBlock) meshRenderer.GetPropertyBlock(reusedPropertyBlock, i);
// Note: this parameter shall not exist at any shader, then Unity will create separate
// material instances (not in terms of memory cost or leakage).
reusedPropertyBlock.SetFloat(SUBMESH_DUMMY_PARAM_ID, i);
meshRenderer.SetPropertyBlock(reusedPropertyBlock, i);
meshRenderer.sharedMaterials[i].enableInstancing = false;
}
}
#endif
}
}

View File

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

View File

@@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: a7236dbdc6a4e5a4989483dac97aee0b
folderAsset: yes
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,211 +0,0 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#if UNITY_2018_3 || UNITY_2019 || UNITY_2018_3_OR_NEWER
#define NEW_PREFAB_SYSTEM
#endif
using System;
using System.Collections.Generic;
using UnityEngine;
namespace Spine.Unity {
#if NEW_PREFAB_SYSTEM
[ExecuteAlways]
#else
[ExecuteInEditMode]
#endif
[HelpURL("http://esotericsoftware.com/spine-unity#SkeletonGraphicCustomMaterials")]
public class SkeletonGraphicCustomMaterials : MonoBehaviour {
#region Inspector
public SkeletonGraphic skeletonGraphic;
[SerializeField] protected List<AtlasMaterialOverride> customMaterialOverrides = new List<AtlasMaterialOverride>();
[SerializeField] protected List<AtlasTextureOverride> customTextureOverrides = new List<AtlasTextureOverride>();
#if UNITY_EDITOR
void Reset () {
skeletonGraphic = GetComponent<SkeletonGraphic>();
// Populate material list
if (skeletonGraphic != null && skeletonGraphic.skeletonDataAsset != null) {
var atlasAssets = skeletonGraphic.skeletonDataAsset.atlasAssets;
var initialAtlasMaterialOverrides = new List<AtlasMaterialOverride>();
foreach (AtlasAssetBase atlasAsset in atlasAssets) {
foreach (Material atlasMaterial in atlasAsset.Materials) {
var atlasMaterialOverride = new AtlasMaterialOverride {
overrideEnabled = false,
originalTexture = atlasMaterial.mainTexture
};
initialAtlasMaterialOverrides.Add(atlasMaterialOverride);
}
}
customMaterialOverrides = initialAtlasMaterialOverrides;
}
// Populate texture list
if (skeletonGraphic != null && skeletonGraphic.skeletonDataAsset != null) {
var atlasAssets = skeletonGraphic.skeletonDataAsset.atlasAssets;
var initialAtlasTextureOverrides = new List<AtlasTextureOverride>();
foreach (AtlasAssetBase atlasAsset in atlasAssets) {
foreach (Material atlasMaterial in atlasAsset.Materials) {
var atlasTextureOverride = new AtlasTextureOverride {
overrideEnabled = false,
originalTexture = atlasMaterial.mainTexture
};
initialAtlasTextureOverrides.Add(atlasTextureOverride);
}
}
customTextureOverrides = initialAtlasTextureOverrides;
}
}
#endif
#endregion
void SetCustomMaterialOverrides () {
if (skeletonGraphic == null) {
Debug.LogError("skeletonGraphic == null");
return;
}
for (int i = 0; i < customMaterialOverrides.Count; i++) {
AtlasMaterialOverride atlasMaterialOverride = customMaterialOverrides[i];
if (atlasMaterialOverride.overrideEnabled)
skeletonGraphic.CustomMaterialOverride[atlasMaterialOverride.originalTexture] = atlasMaterialOverride.replacementMaterial;
}
}
void RemoveCustomMaterialOverrides () {
if (skeletonGraphic == null) {
Debug.LogError("skeletonGraphic == null");
return;
}
for (int i = 0; i < customMaterialOverrides.Count; i++) {
AtlasMaterialOverride atlasMaterialOverride = customMaterialOverrides[i];
Material currentMaterial;
if (!skeletonGraphic.CustomMaterialOverride.TryGetValue(atlasMaterialOverride.originalTexture, out currentMaterial))
continue;
// Do not revert the material if it was changed by something else
if (currentMaterial != atlasMaterialOverride.replacementMaterial)
continue;
skeletonGraphic.CustomMaterialOverride.Remove(atlasMaterialOverride.originalTexture);
}
}
void SetCustomTextureOverrides () {
if (skeletonGraphic == null) {
Debug.LogError("skeletonGraphic == null");
return;
}
for (int i = 0; i < customTextureOverrides.Count; i++) {
AtlasTextureOverride atlasTextureOverride = customTextureOverrides[i];
if (atlasTextureOverride.overrideEnabled)
skeletonGraphic.CustomTextureOverride[atlasTextureOverride.originalTexture] = atlasTextureOverride.replacementTexture;
}
}
void RemoveCustomTextureOverrides () {
if (skeletonGraphic == null) {
Debug.LogError("skeletonGraphic == null");
return;
}
for (int i = 0; i < customTextureOverrides.Count; i++) {
AtlasTextureOverride atlasTextureOverride = customTextureOverrides[i];
Texture currentTexture;
if (!skeletonGraphic.CustomTextureOverride.TryGetValue(atlasTextureOverride.originalTexture, out currentTexture))
continue;
// Do not revert the material if it was changed by something else
if (currentTexture != atlasTextureOverride.replacementTexture)
continue;
skeletonGraphic.CustomTextureOverride.Remove(atlasTextureOverride.originalTexture);
}
}
// OnEnable applies the overrides at runtime, and when the editor loads.
void OnEnable () {
if (skeletonGraphic == null)
skeletonGraphic = GetComponent<SkeletonGraphic>();
if (skeletonGraphic == null) {
Debug.LogError("skeletonGraphic == null");
return;
}
skeletonGraphic.Initialize(false);
SetCustomMaterialOverrides();
SetCustomTextureOverrides();
}
// OnDisable removes the overrides at runtime, and in the editor when the component is disabled or destroyed.
void OnDisable () {
if (skeletonGraphic == null) {
Debug.LogError("skeletonGraphic == null");
return;
}
RemoveCustomMaterialOverrides();
RemoveCustomTextureOverrides();
}
[Serializable]
public struct AtlasMaterialOverride : IEquatable<AtlasMaterialOverride> {
public bool overrideEnabled;
public Texture originalTexture;
public Material replacementMaterial;
public bool Equals (AtlasMaterialOverride other) {
return overrideEnabled == other.overrideEnabled && originalTexture == other.originalTexture && replacementMaterial == other.replacementMaterial;
}
}
[Serializable]
public struct AtlasTextureOverride : IEquatable<AtlasTextureOverride> {
public bool overrideEnabled;
public Texture originalTexture;
public Texture replacementTexture;
public bool Equals (AtlasTextureOverride other) {
return overrideEnabled == other.overrideEnabled && originalTexture == other.originalTexture && replacementTexture == other.replacementTexture;
}
}
}
}

View File

@@ -1,12 +0,0 @@
fileFormatVersion: 2
guid: 6c8717e10b272bf42b05d363ac2679a6
timeCreated: 1588789074
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,212 +0,0 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#if UNITY_2018_3 || UNITY_2019 || UNITY_2018_3_OR_NEWER
#define NEW_PREFAB_SYSTEM
#endif
#define SPINE_OPTIONAL_MATERIALOVERRIDE
// Contributed by: Lost Polygon
using System;
using System.Collections.Generic;
using UnityEngine;
namespace Spine.Unity {
#if NEW_PREFAB_SYSTEM
[ExecuteAlways]
#else
[ExecuteInEditMode]
#endif
[HelpURL("http://esotericsoftware.com/spine-unity#SkeletonRendererCustomMaterials")]
public class SkeletonRendererCustomMaterials : MonoBehaviour {
#region Inspector
public SkeletonRenderer skeletonRenderer;
[SerializeField] protected List<SlotMaterialOverride> customSlotMaterials = new List<SlotMaterialOverride>();
[SerializeField] protected List<AtlasMaterialOverride> customMaterialOverrides = new List<AtlasMaterialOverride>();
#if UNITY_EDITOR
void Reset () {
skeletonRenderer = GetComponent<SkeletonRenderer>();
// Populate atlas list
if (skeletonRenderer != null && skeletonRenderer.skeletonDataAsset != null) {
var atlasAssets = skeletonRenderer.skeletonDataAsset.atlasAssets;
var initialAtlasMaterialOverrides = new List<AtlasMaterialOverride>();
foreach (AtlasAssetBase atlasAsset in atlasAssets) {
foreach (Material atlasMaterial in atlasAsset.Materials) {
var atlasMaterialOverride = new AtlasMaterialOverride {
overrideDisabled = true,
originalMaterial = atlasMaterial
};
initialAtlasMaterialOverrides.Add(atlasMaterialOverride);
}
}
customMaterialOverrides = initialAtlasMaterialOverrides;
}
}
#endif
#endregion
void SetCustomSlotMaterials () {
if (skeletonRenderer == null) {
Debug.LogError("skeletonRenderer == null");
return;
}
for (int i = 0; i < customSlotMaterials.Count; i++) {
SlotMaterialOverride slotMaterialOverride = customSlotMaterials[i];
if (slotMaterialOverride.overrideDisabled || string.IsNullOrEmpty(slotMaterialOverride.slotName))
continue;
Slot slotObject = skeletonRenderer.skeleton.FindSlot(slotMaterialOverride.slotName);
skeletonRenderer.CustomSlotMaterials[slotObject] = slotMaterialOverride.material;
}
}
void RemoveCustomSlotMaterials () {
if (skeletonRenderer == null) {
Debug.LogError("skeletonRenderer == null");
return;
}
for (int i = 0; i < customSlotMaterials.Count; i++) {
SlotMaterialOverride slotMaterialOverride = customSlotMaterials[i];
if (string.IsNullOrEmpty(slotMaterialOverride.slotName))
continue;
Slot slotObject = skeletonRenderer.skeleton.FindSlot(slotMaterialOverride.slotName);
Material currentMaterial;
if (!skeletonRenderer.CustomSlotMaterials.TryGetValue(slotObject, out currentMaterial))
continue;
// Do not revert the material if it was changed by something else
if (currentMaterial != slotMaterialOverride.material)
continue;
skeletonRenderer.CustomSlotMaterials.Remove(slotObject);
}
}
void SetCustomMaterialOverrides () {
if (skeletonRenderer == null) {
Debug.LogError("skeletonRenderer == null");
return;
}
#if SPINE_OPTIONAL_MATERIALOVERRIDE
for (int i = 0; i < customMaterialOverrides.Count; i++) {
AtlasMaterialOverride atlasMaterialOverride = customMaterialOverrides[i];
if (atlasMaterialOverride.overrideDisabled)
continue;
skeletonRenderer.CustomMaterialOverride[atlasMaterialOverride.originalMaterial] = atlasMaterialOverride.replacementMaterial;
}
#endif
}
void RemoveCustomMaterialOverrides () {
if (skeletonRenderer == null) {
Debug.LogError("skeletonRenderer == null");
return;
}
#if SPINE_OPTIONAL_MATERIALOVERRIDE
for (int i = 0; i < customMaterialOverrides.Count; i++) {
AtlasMaterialOverride atlasMaterialOverride = customMaterialOverrides[i];
Material currentMaterial;
if (!skeletonRenderer.CustomMaterialOverride.TryGetValue(atlasMaterialOverride.originalMaterial, out currentMaterial))
continue;
// Do not revert the material if it was changed by something else
if (currentMaterial != atlasMaterialOverride.replacementMaterial)
continue;
skeletonRenderer.CustomMaterialOverride.Remove(atlasMaterialOverride.originalMaterial);
}
#endif
}
// OnEnable applies the overrides at runtime, and when the editor loads.
void OnEnable () {
if (skeletonRenderer == null)
skeletonRenderer = GetComponent<SkeletonRenderer>();
if (skeletonRenderer == null) {
Debug.LogError("skeletonRenderer == null");
return;
}
skeletonRenderer.Initialize(false);
SetCustomMaterialOverrides();
SetCustomSlotMaterials();
}
// OnDisable removes the overrides at runtime, and in the editor when the component is disabled or destroyed.
void OnDisable () {
if (skeletonRenderer == null) {
Debug.LogError("skeletonRenderer == null");
return;
}
RemoveCustomMaterialOverrides();
RemoveCustomSlotMaterials();
}
[Serializable]
public struct SlotMaterialOverride : IEquatable<SlotMaterialOverride> {
public bool overrideDisabled;
[SpineSlot]
public string slotName;
public Material material;
public bool Equals (SlotMaterialOverride other) {
return overrideDisabled == other.overrideDisabled && slotName == other.slotName && material == other.material;
}
}
[Serializable]
public struct AtlasMaterialOverride : IEquatable<AtlasMaterialOverride> {
public bool overrideDisabled;
public Material originalMaterial;
public Material replacementMaterial;
public bool Equals (AtlasMaterialOverride other) {
return overrideDisabled == other.overrideDisabled && originalMaterial == other.originalMaterial && replacementMaterial == other.replacementMaterial;
}
}
}
}

View File

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

View File

@@ -1,5 +0,0 @@
fileFormatVersion: 2
guid: f6e0caaafe294de48af468a6a9321473
folderAsset: yes
DefaultImporter:
userData:

View File

@@ -1,469 +0,0 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#if UNITY_2018_3 || UNITY_2019 || UNITY_2018_3_OR_NEWER
#define NEW_PREFAB_SYSTEM
#endif
using UnityEngine;
using System.Collections.Generic;
namespace Spine.Unity {
#if NEW_PREFAB_SYSTEM
[ExecuteAlways]
#else
[ExecuteInEditMode]
#endif
[RequireComponent(typeof(ISkeletonAnimation))]
[HelpURL("http://esotericsoftware.com/spine-unity#SkeletonUtility")]
public sealed class SkeletonUtility : MonoBehaviour {
#region BoundingBoxAttachment
public static PolygonCollider2D AddBoundingBoxGameObject (Skeleton skeleton, string skinName, string slotName, string attachmentName, Transform parent, bool isTrigger = true) {
Skin skin = string.IsNullOrEmpty(skinName) ? skeleton.data.defaultSkin : skeleton.data.FindSkin(skinName);
if (skin == null) {
Debug.LogError("Skin " + skinName + " not found!");
return null;
}
var attachment = skin.GetAttachment(skeleton.FindSlotIndex(slotName), attachmentName);
if (attachment == null) {
Debug.LogFormat("Attachment in slot '{0}' named '{1}' not found in skin '{2}'.", slotName, attachmentName, skin.name);
return null;
}
var box = attachment as BoundingBoxAttachment;
if (box != null) {
var slot = skeleton.FindSlot(slotName);
return AddBoundingBoxGameObject(box.Name, box, slot, parent, isTrigger);
} else {
Debug.LogFormat("Attachment '{0}' was not a Bounding Box.", attachmentName);
return null;
}
}
public static PolygonCollider2D AddBoundingBoxGameObject (string name, BoundingBoxAttachment box, Slot slot, Transform parent, bool isTrigger = true) {
var go = new GameObject("[BoundingBox]" + (string.IsNullOrEmpty(name) ? box.Name : name));
#if UNITY_EDITOR
if (!Application.isPlaying)
UnityEditor.Undo.RegisterCreatedObjectUndo(go, "Spawn BoundingBox");
# endif
var got = go.transform;
got.parent = parent;
got.localPosition = Vector3.zero;
got.localRotation = Quaternion.identity;
got.localScale = Vector3.one;
return AddBoundingBoxAsComponent(box, slot, go, isTrigger);
}
public static PolygonCollider2D AddBoundingBoxAsComponent (BoundingBoxAttachment box, Slot slot, GameObject gameObject, bool isTrigger = true) {
if (box == null) return null;
var collider = gameObject.AddComponent<PolygonCollider2D>();
collider.isTrigger = isTrigger;
SetColliderPointsLocal(collider, slot, box);
return collider;
}
public static void SetColliderPointsLocal (PolygonCollider2D collider, Slot slot, BoundingBoxAttachment box, float scale = 1.0f) {
if (box == null) return;
if (box.IsWeighted()) Debug.LogWarning("UnityEngine.PolygonCollider2D does not support weighted or animated points. Collider points will not be animated and may have incorrect orientation. If you want to use it as a collider, please remove weights and animations from the bounding box in Spine editor.");
var verts = box.GetLocalVertices(slot, null);
if (scale != 1.0f) {
for (int i = 0, n = verts.Length; i < n; ++i)
verts[i] *= scale;
}
collider.SetPath(0, verts);
}
public static Bounds GetBoundingBoxBounds (BoundingBoxAttachment boundingBox, float depth = 0) {
float[] floats = boundingBox.Vertices;
int floatCount = floats.Length;
Bounds bounds = new Bounds();
bounds.center = new Vector3(floats[0], floats[1], 0);
for (int i = 2; i < floatCount; i += 2)
bounds.Encapsulate(new Vector3(floats[i], floats[i + 1], 0));
Vector3 size = bounds.size;
size.z = depth;
bounds.size = size;
return bounds;
}
public static Rigidbody2D AddBoneRigidbody2D (GameObject gameObject, bool isKinematic = true, float gravityScale = 0f) {
var rb = gameObject.GetComponent<Rigidbody2D>();
if (rb == null) {
rb = gameObject.AddComponent<Rigidbody2D>();
rb.isKinematic = isKinematic;
rb.gravityScale = gravityScale;
}
return rb;
}
#endregion
public delegate void SkeletonUtilityDelegate ();
public event SkeletonUtilityDelegate OnReset;
public Transform boneRoot;
/// <summary>
/// If true, <see cref="Skeleton.ScaleX"/> and <see cref="Skeleton.ScaleY"/> are followed
/// by 180 degree rotation. If false, negative Transform scale is used.
/// Note that using negative scale is consistent with previous behaviour (hence the default),
/// however causes serious problems with rigidbodies and physics. Therefore, it is recommended to
/// enable this parameter where possible. When creating hinge chains for a chain of skeleton bones
/// via <see cref="SkeletonUtilityBone"/>, it is mandatory to have <c>flipBy180DegreeRotation</c> enabled.
/// </summary>
public bool flipBy180DegreeRotation = false;
void Update () {
var skeleton = skeletonComponent.Skeleton;
if (skeleton != null && boneRoot != null) {
if (flipBy180DegreeRotation) {
boneRoot.localScale = new Vector3(Mathf.Abs(skeleton.ScaleX), Mathf.Abs(skeleton.ScaleY), 1f);
boneRoot.eulerAngles = new Vector3(skeleton.ScaleY > 0 ? 0 : 180,
skeleton.ScaleX > 0 ? 0 : 180,
0);
}
else {
boneRoot.localScale = new Vector3(skeleton.ScaleX, skeleton.ScaleY, 1f);
}
}
if (canvas != null) {
positionScale = canvas.referencePixelsPerUnit;
}
}
[HideInInspector] public SkeletonRenderer skeletonRenderer;
[HideInInspector] public SkeletonGraphic skeletonGraphic;
private Canvas canvas;
[System.NonSerialized] public ISkeletonAnimation skeletonAnimation;
private ISkeletonComponent skeletonComponent;
[System.NonSerialized] public List<SkeletonUtilityBone> boneComponents = new List<SkeletonUtilityBone>();
[System.NonSerialized] public List<SkeletonUtilityConstraint> constraintComponents = new List<SkeletonUtilityConstraint>();
public ISkeletonComponent SkeletonComponent {
get {
if (skeletonComponent == null) {
skeletonComponent = skeletonRenderer != null ? skeletonRenderer.GetComponent<ISkeletonComponent>() :
skeletonGraphic != null ? skeletonGraphic.GetComponent<ISkeletonComponent>() :
GetComponent<ISkeletonComponent>();
}
return skeletonComponent;
}
}
public Skeleton Skeleton {
get {
if (SkeletonComponent == null)
return null;
return skeletonComponent.Skeleton;
}
}
public bool IsValid {
get {
return (skeletonRenderer != null && skeletonRenderer.valid) ||
(skeletonGraphic != null && skeletonGraphic.IsValid);
}
}
public float PositionScale { get { return positionScale; } }
float positionScale = 1.0f;
bool hasOverrideBones;
bool hasConstraints;
bool needToReprocessBones;
public void ResubscribeEvents () {
OnDisable();
OnEnable();
}
void OnEnable () {
if (skeletonRenderer == null) {
skeletonRenderer = GetComponent<SkeletonRenderer>();
}
if (skeletonGraphic == null) {
skeletonGraphic = GetComponent<SkeletonGraphic>();
}
if (skeletonAnimation == null) {
skeletonAnimation = skeletonRenderer != null ? skeletonRenderer.GetComponent<ISkeletonAnimation>() :
skeletonGraphic != null ? skeletonGraphic.GetComponent<ISkeletonAnimation>() :
GetComponent<ISkeletonAnimation>();
}
if (skeletonComponent == null) {
skeletonComponent = skeletonRenderer != null ? skeletonRenderer.GetComponent<ISkeletonComponent>() :
skeletonGraphic != null ? skeletonGraphic.GetComponent<ISkeletonComponent>() :
GetComponent<ISkeletonComponent>();
}
if (skeletonRenderer != null) {
skeletonRenderer.OnRebuild -= HandleRendererReset;
skeletonRenderer.OnRebuild += HandleRendererReset;
}
else if (skeletonGraphic != null) {
skeletonGraphic.OnRebuild -= HandleRendererReset;
skeletonGraphic.OnRebuild += HandleRendererReset;
canvas = skeletonGraphic.canvas;
if (canvas == null)
canvas = skeletonGraphic.GetComponentInParent<Canvas>();
if (canvas == null)
positionScale = 100.0f;
}
if (skeletonAnimation != null) {
skeletonAnimation.UpdateLocal -= UpdateLocal;
skeletonAnimation.UpdateLocal += UpdateLocal;
}
CollectBones();
}
void Start () {
//recollect because order of operations failure when switching between game mode and edit mode...
CollectBones();
}
void OnDisable () {
if (skeletonRenderer != null)
skeletonRenderer.OnRebuild -= HandleRendererReset;
if (skeletonGraphic != null)
skeletonGraphic.OnRebuild -= HandleRendererReset;
if (skeletonAnimation != null) {
skeletonAnimation.UpdateLocal -= UpdateLocal;
skeletonAnimation.UpdateWorld -= UpdateWorld;
skeletonAnimation.UpdateComplete -= UpdateComplete;
}
}
void HandleRendererReset (SkeletonRenderer r) {
if (OnReset != null) OnReset();
CollectBones();
}
void HandleRendererReset (SkeletonGraphic g) {
if (OnReset != null) OnReset();
CollectBones();
}
public void RegisterBone (SkeletonUtilityBone bone) {
if (boneComponents.Contains(bone)) {
return;
} else {
boneComponents.Add(bone);
needToReprocessBones = true;
}
}
public void UnregisterBone (SkeletonUtilityBone bone) {
boneComponents.Remove(bone);
}
public void RegisterConstraint (SkeletonUtilityConstraint constraint) {
if (constraintComponents.Contains(constraint))
return;
else {
constraintComponents.Add(constraint);
needToReprocessBones = true;
}
}
public void UnregisterConstraint (SkeletonUtilityConstraint constraint) {
constraintComponents.Remove(constraint);
}
public void CollectBones () {
var skeleton = skeletonComponent.Skeleton;
if (skeleton == null) return;
if (boneRoot != null) {
var constraintTargets = new List<System.Object>();
var ikConstraints = skeleton.IkConstraints;
for (int i = 0, n = ikConstraints.Count; i < n; i++)
constraintTargets.Add(ikConstraints.Items[i].target);
var transformConstraints = skeleton.TransformConstraints;
for (int i = 0, n = transformConstraints.Count; i < n; i++)
constraintTargets.Add(transformConstraints.Items[i].target);
var boneComponents = this.boneComponents;
for (int i = 0, n = boneComponents.Count; i < n; i++) {
var b = boneComponents[i];
if (b.bone == null) {
b.DoUpdate(SkeletonUtilityBone.UpdatePhase.Local);
if (b.bone == null) continue;
}
hasOverrideBones |= (b.mode == SkeletonUtilityBone.Mode.Override);
hasConstraints |= constraintTargets.Contains(b.bone);
}
hasConstraints |= constraintComponents.Count > 0;
if (skeletonAnimation != null) {
skeletonAnimation.UpdateWorld -= UpdateWorld;
skeletonAnimation.UpdateComplete -= UpdateComplete;
if (hasOverrideBones || hasConstraints)
skeletonAnimation.UpdateWorld += UpdateWorld;
if (hasConstraints)
skeletonAnimation.UpdateComplete += UpdateComplete;
}
needToReprocessBones = false;
} else {
boneComponents.Clear();
constraintComponents.Clear();
}
}
void UpdateLocal (ISkeletonAnimation anim) {
if (needToReprocessBones)
CollectBones();
var boneComponents = this.boneComponents;
if (boneComponents == null) return;
for (int i = 0, n = boneComponents.Count; i < n; i++)
boneComponents[i].transformLerpComplete = false;
UpdateAllBones(SkeletonUtilityBone.UpdatePhase.Local);
}
void UpdateWorld (ISkeletonAnimation anim) {
UpdateAllBones(SkeletonUtilityBone.UpdatePhase.World);
for (int i = 0, n = constraintComponents.Count; i < n; i++)
constraintComponents[i].DoUpdate();
}
void UpdateComplete (ISkeletonAnimation anim) {
UpdateAllBones(SkeletonUtilityBone.UpdatePhase.Complete);
}
void UpdateAllBones (SkeletonUtilityBone.UpdatePhase phase) {
if (boneRoot == null)
CollectBones();
var boneComponents = this.boneComponents;
if (boneComponents == null) return;
for (int i = 0, n = boneComponents.Count; i < n; i++)
boneComponents[i].DoUpdate(phase);
}
public Transform GetBoneRoot () {
if (boneRoot != null)
return boneRoot;
var boneRootObject = new GameObject("SkeletonUtility-SkeletonRoot");
#if UNITY_EDITOR
if (!Application.isPlaying)
UnityEditor.Undo.RegisterCreatedObjectUndo(boneRootObject, "Spawn Bone");
#endif
if (skeletonGraphic != null)
boneRootObject.AddComponent<RectTransform>();
boneRoot = boneRootObject.transform;
boneRoot.SetParent(transform);
boneRoot.localPosition = Vector3.zero;
boneRoot.localRotation = Quaternion.identity;
boneRoot.localScale = Vector3.one;
return boneRoot;
}
public GameObject SpawnRoot (SkeletonUtilityBone.Mode mode, bool pos, bool rot, bool sca) {
GetBoneRoot();
Skeleton skeleton = this.skeletonComponent.Skeleton;
GameObject go = SpawnBone(skeleton.RootBone, boneRoot, mode, pos, rot, sca);
CollectBones();
return go;
}
public GameObject SpawnHierarchy (SkeletonUtilityBone.Mode mode, bool pos, bool rot, bool sca) {
GetBoneRoot();
Skeleton skeleton = this.skeletonComponent.Skeleton;
GameObject go = SpawnBoneRecursively(skeleton.RootBone, boneRoot, mode, pos, rot, sca);
CollectBones();
return go;
}
public GameObject SpawnBoneRecursively (Bone bone, Transform parent, SkeletonUtilityBone.Mode mode, bool pos, bool rot, bool sca) {
GameObject go = SpawnBone(bone, parent, mode, pos, rot, sca);
ExposedList<Bone> childrenBones = bone.Children;
for (int i = 0, n = childrenBones.Count; i < n; i++) {
Bone child = childrenBones.Items[i];
SpawnBoneRecursively(child, go.transform, mode, pos, rot, sca);
}
return go;
}
public GameObject SpawnBone (Bone bone, Transform parent, SkeletonUtilityBone.Mode mode, bool pos, bool rot, bool sca) {
GameObject go = new GameObject(bone.Data.Name);
#if UNITY_EDITOR
if (!Application.isPlaying)
UnityEditor.Undo.RegisterCreatedObjectUndo(go, "Spawn Bone");
#endif
if (skeletonGraphic != null)
go.AddComponent<RectTransform>();
var goTransform = go.transform;
goTransform.SetParent(parent);
SkeletonUtilityBone b = go.AddComponent<SkeletonUtilityBone>();
b.hierarchy = this;
b.position = pos;
b.rotation = rot;
b.scale = sca;
b.mode = mode;
b.zPosition = true;
b.Reset();
b.bone = bone;
b.boneName = bone.Data.Name;
b.valid = true;
if (mode == SkeletonUtilityBone.Mode.Override) {
if (rot) goTransform.localRotation = Quaternion.Euler(0, 0, b.bone.AppliedRotation);
if (pos) goTransform.localPosition = new Vector3(b.bone.X * positionScale, b.bone.Y * positionScale, 0);
goTransform.localScale = new Vector3(b.bone.scaleX, b.bone.scaleY, 0);
}
return go;
}
}
}

View File

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

View File

@@ -1,243 +0,0 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#if UNITY_2018_3 || UNITY_2019 || UNITY_2018_3_OR_NEWER
#define NEW_PREFAB_SYSTEM
#endif
using UnityEngine;
namespace Spine.Unity {
/// <summary>Sets a GameObject's transform to match a bone on a Spine skeleton.</summary>
#if NEW_PREFAB_SYSTEM
[ExecuteAlways]
#else
[ExecuteInEditMode]
#endif
[AddComponentMenu("Spine/SkeletonUtilityBone")]
[HelpURL("http://esotericsoftware.com/spine-unity#SkeletonUtilityBone")]
public class SkeletonUtilityBone : MonoBehaviour {
public enum Mode {
Follow,
Override
}
public enum UpdatePhase {
Local,
World,
Complete
}
#region Inspector
/// <summary>If a bone isn't set, boneName is used to find the bone.</summary>
public string boneName;
public Transform parentReference;
public Mode mode;
public bool position, rotation, scale, zPosition = true;
[Range(0f, 1f)]
public float overrideAlpha = 1;
#endregion
public SkeletonUtility hierarchy;
[System.NonSerialized] public Bone bone;
[System.NonSerialized] public bool transformLerpComplete;
[System.NonSerialized] public bool valid;
Transform cachedTransform;
Transform skeletonTransform;
bool incompatibleTransformMode;
public bool IncompatibleTransformMode { get { return incompatibleTransformMode; } }
public void Reset () {
bone = null;
cachedTransform = transform;
valid = hierarchy != null && hierarchy.IsValid;
if (!valid)
return;
skeletonTransform = hierarchy.transform;
hierarchy.OnReset -= HandleOnReset;
hierarchy.OnReset += HandleOnReset;
DoUpdate(UpdatePhase.Local);
}
void OnEnable () {
if (hierarchy == null) hierarchy = transform.GetComponentInParent<SkeletonUtility>();
if (hierarchy == null) return;
hierarchy.RegisterBone(this);
hierarchy.OnReset += HandleOnReset;
}
void HandleOnReset () {
Reset();
}
void OnDisable () {
if (hierarchy != null) {
hierarchy.OnReset -= HandleOnReset;
hierarchy.UnregisterBone(this);
}
}
public void DoUpdate (UpdatePhase phase) {
if (!valid) {
Reset();
return;
}
var skeleton = hierarchy.Skeleton;
if (bone == null) {
if (string.IsNullOrEmpty(boneName)) return;
bone = skeleton.FindBone(boneName);
if (bone == null) {
Debug.LogError("Bone not found: " + boneName, this);
return;
}
}
if (!bone.Active) return;
float positionScale = hierarchy.PositionScale;
var thisTransform = cachedTransform;
float skeletonFlipRotation = Mathf.Sign(skeleton.ScaleX * skeleton.ScaleY);
if (mode == Mode.Follow) {
switch (phase) {
case UpdatePhase.Local:
if (position)
thisTransform.localPosition = new Vector3(bone.x * positionScale, bone.y * positionScale, 0);
if (rotation) {
if (bone.data.transformMode.InheritsRotation()) {
thisTransform.localRotation = Quaternion.Euler(0, 0, bone.rotation);
} else {
Vector3 euler = skeletonTransform.rotation.eulerAngles;
thisTransform.rotation = Quaternion.Euler(euler.x, euler.y, euler.z + (bone.WorldRotationX * skeletonFlipRotation));
}
}
if (scale) {
thisTransform.localScale = new Vector3(bone.scaleX, bone.scaleY, 1f);
incompatibleTransformMode = BoneTransformModeIncompatible(bone);
}
break;
case UpdatePhase.World:
case UpdatePhase.Complete:
// Use Applied transform values (ax, ay, AppliedRotation, ascale) if world values were modified by constraints.
if (!bone.appliedValid) {
bone.UpdateAppliedTransform();
}
if (position)
thisTransform.localPosition = new Vector3(bone.ax * positionScale, bone.ay * positionScale, 0);
if (rotation) {
if (bone.data.transformMode.InheritsRotation()) {
thisTransform.localRotation = Quaternion.Euler(0, 0, bone.AppliedRotation);
} else {
Vector3 euler = skeletonTransform.rotation.eulerAngles;
thisTransform.rotation = Quaternion.Euler(euler.x, euler.y, euler.z + (bone.WorldRotationX * skeletonFlipRotation));
}
}
if (scale) {
thisTransform.localScale = new Vector3(bone.ascaleX, bone.ascaleY, 1f);
incompatibleTransformMode = BoneTransformModeIncompatible(bone);
}
break;
}
} else if (mode == Mode.Override) {
if (transformLerpComplete)
return;
if (parentReference == null) {
if (position) {
Vector3 clp = thisTransform.localPosition / positionScale;
bone.x = Mathf.Lerp(bone.x, clp.x, overrideAlpha);
bone.y = Mathf.Lerp(bone.y, clp.y, overrideAlpha);
}
if (rotation) {
float angle = Mathf.LerpAngle(bone.Rotation, thisTransform.localRotation.eulerAngles.z, overrideAlpha);
bone.Rotation = angle;
bone.AppliedRotation = angle;
}
if (scale) {
Vector3 cls = thisTransform.localScale;
bone.scaleX = Mathf.Lerp(bone.scaleX, cls.x, overrideAlpha);
bone.scaleY = Mathf.Lerp(bone.scaleY, cls.y, overrideAlpha);
}
} else {
if (transformLerpComplete)
return;
if (position) {
Vector3 pos = parentReference.InverseTransformPoint(thisTransform.position) / positionScale;
bone.x = Mathf.Lerp(bone.x, pos.x, overrideAlpha);
bone.y = Mathf.Lerp(bone.y, pos.y, overrideAlpha);
}
if (rotation) {
float angle = Mathf.LerpAngle(bone.Rotation, Quaternion.LookRotation(Vector3.forward, parentReference.InverseTransformDirection(thisTransform.up)).eulerAngles.z, overrideAlpha);
bone.Rotation = angle;
bone.AppliedRotation = angle;
}
if (scale) {
Vector3 cls = thisTransform.localScale;
bone.scaleX = Mathf.Lerp(bone.scaleX, cls.x, overrideAlpha);
bone.scaleY = Mathf.Lerp(bone.scaleY, cls.y, overrideAlpha);
}
incompatibleTransformMode = BoneTransformModeIncompatible(bone);
}
transformLerpComplete = true;
}
}
public static bool BoneTransformModeIncompatible (Bone bone) {
return !bone.data.transformMode.InheritsScale();
}
public void AddBoundingBox (string skinName, string slotName, string attachmentName) {
SkeletonUtility.AddBoneRigidbody2D(transform.gameObject);
SkeletonUtility.AddBoundingBoxGameObject(bone.skeleton, skinName, slotName, attachmentName, transform);
}
#if UNITY_EDITOR
void OnDrawGizmos () {
if (IncompatibleTransformMode)
Gizmos.DrawIcon(transform.position + new Vector3(0, 0.128f, 0), "icon-warning");
}
#endif
}
}

View File

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

View File

@@ -1,62 +0,0 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#if UNITY_2018_3 || UNITY_2019 || UNITY_2018_3_OR_NEWER
#define NEW_PREFAB_SYSTEM
#endif
using UnityEngine;
namespace Spine.Unity {
#if NEW_PREFAB_SYSTEM
[ExecuteAlways]
#else
[ExecuteInEditMode]
#endif
[RequireComponent(typeof(SkeletonUtilityBone))]
[HelpURL("http://esotericsoftware.com/spine-unity#SkeletonUtilityConstraint")]
public abstract class SkeletonUtilityConstraint : MonoBehaviour {
protected SkeletonUtilityBone bone;
protected SkeletonUtility hierarchy;
protected virtual void OnEnable () {
bone = GetComponent<SkeletonUtilityBone>();
hierarchy = transform.GetComponentInParent<SkeletonUtility>();
hierarchy.RegisterConstraint(this);
}
protected virtual void OnDisable () {
hierarchy.UnregisterConstraint(this);
}
public abstract void DoUpdate ();
}
}

View File

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

View File

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

View File

@@ -1,230 +0,0 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated January 1, 2020. Replaces all prior versions.
*
* Copyright (c) 2013-2020, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
using System.Collections.Generic;
using UnityEngine;
using System;
namespace Spine.Unity.Deprecated {
/// <summary>
/// Deprecated. The spine-unity 3.7 runtime introduced SkeletonDataModifierAssets BlendModeMaterials which replaced SlotBlendModes. See the
/// <see href="http://esotericsoftware.com/spine-unity-skeletondatamodifierassets#BlendModeMaterials">SkeletonDataModifierAssets BlendModeMaterials documentation page</see> and
/// <see href="http://esotericsoftware.com/forum/Slot-blending-not-work-11281">this forum thread</see> for further information.
/// This class will be removed in the spine-unity 3.9 runtime.
/// </summary>
[Obsolete("The spine-unity 3.7 runtime introduced SkeletonDataModifierAssets BlendModeMaterials which replaced SlotBlendModes. Will be removed in spine-unity 3.9.", false)]
[DisallowMultipleComponent]
public class SlotBlendModes : MonoBehaviour {
#region Internal Material Dictionary
public struct MaterialTexturePair {
public Texture2D texture2D;
public Material material;
}
internal class MaterialWithRefcount {
public Material materialClone;
public int refcount = 1;
public MaterialWithRefcount(Material mat) {
this.materialClone = mat;
}
}
static Dictionary<MaterialTexturePair, MaterialWithRefcount> materialTable;
internal static Dictionary<MaterialTexturePair, MaterialWithRefcount> MaterialTable {
get {
if (materialTable == null) materialTable = new Dictionary<MaterialTexturePair, MaterialWithRefcount>();
return materialTable;
}
}
internal struct SlotMaterialTextureTuple {
public Slot slot;
public Texture2D texture2D;
public Material material;
public SlotMaterialTextureTuple(Slot slot, Material material, Texture2D texture) {
this.slot = slot;
this.material = material;
this.texture2D = texture;
}
}
internal static Material GetOrAddMaterialFor(Material materialSource, Texture2D texture) {
if (materialSource == null || texture == null) return null;
var mt = SlotBlendModes.MaterialTable;
MaterialWithRefcount matWithRefcount;
var key = new MaterialTexturePair { material = materialSource, texture2D = texture };
if (!mt.TryGetValue(key, out matWithRefcount)) {
matWithRefcount = new MaterialWithRefcount(new Material(materialSource));
var m = matWithRefcount.materialClone;
m.name = "(Clone)" + texture.name + "-" + materialSource.name;
m.mainTexture = texture;
mt[key] = matWithRefcount;
}
else {
matWithRefcount.refcount++;
}
return matWithRefcount.materialClone;
}
internal static MaterialWithRefcount GetExistingMaterialFor(Material materialSource, Texture2D texture)
{
if (materialSource == null || texture == null) return null;
var mt = SlotBlendModes.MaterialTable;
MaterialWithRefcount matWithRefcount;
var key = new MaterialTexturePair { material = materialSource, texture2D = texture };
if (!mt.TryGetValue(key, out matWithRefcount)) {
return null;
}
return matWithRefcount;
}
internal static void RemoveMaterialFromTable(Material materialSource, Texture2D texture) {
var mt = SlotBlendModes.MaterialTable;
var key = new MaterialTexturePair { material = materialSource, texture2D = texture };
mt.Remove(key);
}
#endregion
#region Inspector
public Material multiplyMaterialSource;
public Material screenMaterialSource;
Texture2D texture;
#endregion
SlotMaterialTextureTuple[] slotsWithCustomMaterial = new SlotMaterialTextureTuple[0];
public bool Applied { get; private set; }
void Start() {
if (!Applied) Apply();
}
void OnDestroy() {
if (Applied) Remove();
}
public void Apply() {
GetTexture();
if (texture == null) return;
var skeletonRenderer = GetComponent<SkeletonRenderer>();
if (skeletonRenderer == null) return;
var slotMaterials = skeletonRenderer.CustomSlotMaterials;
int numSlotsWithCustomMaterial = 0;
foreach (var s in skeletonRenderer.Skeleton.Slots) {
switch (s.data.blendMode) {
case BlendMode.Multiply:
if (multiplyMaterialSource != null) {
slotMaterials[s] = GetOrAddMaterialFor(multiplyMaterialSource, texture);
++numSlotsWithCustomMaterial;
}
break;
case BlendMode.Screen:
if (screenMaterialSource != null) {
slotMaterials[s] = GetOrAddMaterialFor(screenMaterialSource, texture);
++numSlotsWithCustomMaterial;
}
break;
}
}
slotsWithCustomMaterial = new SlotMaterialTextureTuple[numSlotsWithCustomMaterial];
int storedSlotIndex = 0;
foreach (var s in skeletonRenderer.Skeleton.Slots) {
switch (s.data.blendMode) {
case BlendMode.Multiply:
if (multiplyMaterialSource != null) {
slotsWithCustomMaterial[storedSlotIndex++] = new SlotMaterialTextureTuple(s, multiplyMaterialSource, texture);
}
break;
case BlendMode.Screen:
if (screenMaterialSource != null) {
slotsWithCustomMaterial[storedSlotIndex++] = new SlotMaterialTextureTuple(s, screenMaterialSource, texture);
}
break;
}
}
Applied = true;
skeletonRenderer.LateUpdate();
}
public void Remove() {
GetTexture();
if (texture == null) return;
var skeletonRenderer = GetComponent<SkeletonRenderer>();
if (skeletonRenderer == null) return;
var slotMaterials = skeletonRenderer.CustomSlotMaterials;
foreach (var slotWithCustomMat in slotsWithCustomMaterial) {
Slot s = slotWithCustomMat.slot;
Material storedMaterialSource = slotWithCustomMat.material;
Texture2D storedTexture = slotWithCustomMat.texture2D;
var matWithRefcount = GetExistingMaterialFor(storedMaterialSource, storedTexture);
if (--matWithRefcount.refcount == 0) {
RemoveMaterialFromTable(storedMaterialSource, storedTexture);
}
// we don't want to remove slotMaterials[s] if it has been changed in the meantime.
Material m;
if (slotMaterials.TryGetValue(s, out m)) {
var existingMat = matWithRefcount == null ? null : matWithRefcount.materialClone;
if (Material.ReferenceEquals(m, existingMat)) {
slotMaterials.Remove(s);
}
}
}
slotsWithCustomMaterial = null;
Applied = false;
if (skeletonRenderer.valid) skeletonRenderer.LateUpdate();
}
public void GetTexture() {
if (texture == null) {
var sr = GetComponent<SkeletonRenderer>(); if (sr == null) return;
var sda = sr.skeletonDataAsset; if (sda == null) return;
var aa = sda.atlasAssets[0]; if (aa == null) return;
var am = aa.PrimaryMaterial; if (am == null) return;
texture = am.mainTexture as Texture2D;
}
}
}
}

View File

@@ -1,16 +0,0 @@
fileFormatVersion: 2
guid: f1f8243645ba2e74aa3564bd956eed89
timeCreated: 1496794038
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences:
- multiplyMaterialSource: {fileID: 2100000, guid: 53bf0ab317d032d418cf1252d68f51df,
type: 2}
- screenMaterialSource: {fileID: 2100000, guid: 73f0f46d3177c614baf0fa48d646a9be,
type: 2}
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

Some files were not shown because too many files have changed in this diff Show More