mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-03-26 22:49:01 +08:00
Merge remote-tracking branch 'esotericsoftware/master'
This commit is contained in:
commit
92fb16b7c2
@ -70,6 +70,7 @@
|
||||
<Compile Include="src\AnimationState.cs" />
|
||||
<Compile Include="src\Event.cs" />
|
||||
<Compile Include="src\EventData.cs" />
|
||||
<Compile Include="src\ExposedList.cs" />
|
||||
<Compile Include="src\IkConstraint.cs" />
|
||||
<Compile Include="src\IkConstraintData.cs" />
|
||||
<Compile Include="src\Json.cs" />
|
||||
|
||||
@ -110,6 +110,7 @@
|
||||
<Compile Include="src\IkConstraintData.cs" />
|
||||
<Compile Include="src\Event.cs" />
|
||||
<Compile Include="src\EventData.cs" />
|
||||
<Compile Include="src\ExposedList.cs" />
|
||||
<Compile Include="src\Json.cs" />
|
||||
<Compile Include="src\Skeleton.cs" />
|
||||
<Compile Include="src\SkeletonBinary.cs" />
|
||||
|
||||
@ -114,7 +114,10 @@ namespace Spine {
|
||||
} else {
|
||||
float previousTime = previous.time;
|
||||
if (!previous.loop && previousTime > previous.endTime) previousTime = previous.endTime;
|
||||
previous.animation.Apply(skeleton, previousTime, previousTime, previous.loop, null);
|
||||
previous.animation.Apply(skeleton, previous.lastTime, previousTime, previous.loop, null);
|
||||
// Remove the line above, and uncomment the line below, to allow previous animations to fire events during mixing.
|
||||
//previous.animation.Apply(skeleton, previous.lastTime, previousTime, previous.loop, events);
|
||||
previous.lastTime = previousTime;
|
||||
|
||||
float alpha = current.mixTime / current.mixDuration * current.mix;
|
||||
if (alpha >= 1) {
|
||||
|
||||
@ -94,6 +94,7 @@ namespace Spine {
|
||||
ikConstraints.Add(new IkConstraint(ikConstraintData, this));
|
||||
|
||||
UpdateCache();
|
||||
UpdateWorldTransform();
|
||||
}
|
||||
|
||||
/// <summary>Caches information about bones and IK constraints. Must be called if bones or IK constraints are added or
|
||||
|
||||
@ -36,19 +36,20 @@ using Spine;
|
||||
|
||||
[CustomEditor(typeof(SkeletonAnimation))]
|
||||
public class SkeletonAnimationInspector : SkeletonRendererInspector {
|
||||
protected SerializedProperty animationName, loop, timeScale;
|
||||
protected bool isPrefab;
|
||||
protected SerializedProperty animationName, loop, timeScale, autoReset;
|
||||
protected bool m_isPrefab;
|
||||
protected GUIContent autoResetLabel;
|
||||
|
||||
protected override void OnEnable () {
|
||||
base.OnEnable();
|
||||
animationName = serializedObject.FindProperty("_animationName");
|
||||
loop = serializedObject.FindProperty("loop");
|
||||
timeScale = serializedObject.FindProperty("timeScale");
|
||||
autoReset = serializedObject.FindProperty("autoReset");
|
||||
autoResetLabel = new GUIContent("Generic Auto-reset");
|
||||
|
||||
if (PrefabUtility.GetPrefabType(this.target) == PrefabType.Prefab)
|
||||
isPrefab = true;
|
||||
|
||||
|
||||
m_isPrefab = true;
|
||||
}
|
||||
|
||||
protected override void gui () {
|
||||
@ -96,11 +97,12 @@ public class SkeletonAnimationInspector : SkeletonRendererInspector {
|
||||
|
||||
EditorGUILayout.PropertyField(loop);
|
||||
EditorGUILayout.PropertyField(timeScale);
|
||||
EditorGUILayout.PropertyField(autoReset, autoResetLabel);
|
||||
component.timeScale = Math.Max(component.timeScale, 0);
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
if (!isPrefab) {
|
||||
if (!m_isPrefab) {
|
||||
if (component.GetComponent<SkeletonUtility>() == null) {
|
||||
if (GUILayout.Button(new GUIContent("Add Skeleton Utility", SpineEditorUtilities.Icons.skeletonUtility), GUILayout.Height(30))) {
|
||||
component.gameObject.AddComponent<SkeletonUtility>();
|
||||
|
||||
@ -648,7 +648,7 @@ public class SkeletonDataAssetInspector : Editor {
|
||||
this.m_previewUtility.m_Camera.orthographicSize = orthoSet;
|
||||
|
||||
float dist = Vector3.Distance(m_previewUtility.m_Camera.transform.position, m_posGoal);
|
||||
if (dist > 60f * ((SkeletonDataAsset)target).scale) {
|
||||
if(dist > 0f) {
|
||||
Vector3 pos = Vector3.Lerp(this.m_previewUtility.m_Camera.transform.position, m_posGoal, 0.1f);
|
||||
pos.x = 0;
|
||||
this.m_previewUtility.m_Camera.transform.position = pos;
|
||||
@ -857,7 +857,7 @@ public class SkeletonDataAssetInspector : Editor {
|
||||
case EventType.ScrollWheel:
|
||||
if (position.Contains(current.mousePosition)) {
|
||||
|
||||
m_orthoGoal += current.delta.y * ((SkeletonDataAsset)target).scale * 10;
|
||||
m_orthoGoal += current.delta.y;
|
||||
GUIUtility.hotControl = controlID;
|
||||
current.Use();
|
||||
}
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* Spine Attribute Drawers created by Mitch Thompson
|
||||
* Full irrevocable rights and permissions granted to Esoteric Software
|
||||
@ -8,9 +7,6 @@ using UnityEngine;
|
||||
using UnityEditor;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Spine;
|
||||
|
||||
@ -25,10 +21,9 @@ public struct SpineDrawerValuePair {
|
||||
}
|
||||
}
|
||||
|
||||
[CustomPropertyDrawer(typeof(SpineSlot))]
|
||||
public class SpineSlotDrawer : PropertyDrawer {
|
||||
SkeletonDataAsset skeletonDataAsset;
|
||||
|
||||
public abstract class SpineTreeItemDrawerBase<T> : PropertyDrawer where T:SpineAttributeBase {
|
||||
protected SkeletonDataAsset skeletonDataAsset;
|
||||
protected T TargetAttribute { get { return (T)attribute; } }
|
||||
|
||||
public override void OnGUI (Rect position, SerializedProperty property, GUIContent label) {
|
||||
if (property.propertyType != SerializedPropertyType.String) {
|
||||
@ -36,9 +31,7 @@ public class SpineSlotDrawer : PropertyDrawer {
|
||||
return;
|
||||
}
|
||||
|
||||
SpineSlot attrib = (SpineSlot)attribute;
|
||||
|
||||
var dataProperty = property.serializedObject.FindProperty(attrib.dataField);
|
||||
var dataProperty = property.serializedObject.FindProperty(TargetAttribute.dataField);
|
||||
|
||||
if (dataProperty != null) {
|
||||
if (dataProperty.objectReferenceValue is SkeletonDataAsset) {
|
||||
@ -73,21 +66,38 @@ public class SpineSlotDrawer : PropertyDrawer {
|
||||
|
||||
}
|
||||
|
||||
void Selector(SerializedProperty property) {
|
||||
SpineSlot attrib = (SpineSlot)attribute;
|
||||
protected virtual void Selector (SerializedProperty property) {
|
||||
SkeletonData data = skeletonDataAsset.GetSkeletonData(true);
|
||||
if (data == null)
|
||||
return;
|
||||
|
||||
GenericMenu menu = new GenericMenu();
|
||||
PopulateMenu (menu, property, this.TargetAttribute, data);
|
||||
menu.ShowAsContext();
|
||||
}
|
||||
|
||||
menu.AddDisabledItem(new GUIContent(skeletonDataAsset.name));
|
||||
menu.AddSeparator("");
|
||||
protected abstract void PopulateMenu (GenericMenu menu, SerializedProperty property, T targetAttribute, SkeletonData data);
|
||||
|
||||
protected virtual void HandleSelect (object val) {
|
||||
var pair = (SpineDrawerValuePair)val;
|
||||
pair.property.stringValue = pair.str;
|
||||
pair.property.serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
|
||||
public override float GetPropertyHeight(SerializedProperty property, GUIContent label) {
|
||||
return 18;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[CustomPropertyDrawer(typeof(SpineSlot))]
|
||||
public class SpineSlotDrawer : SpineTreeItemDrawerBase<SpineSlot> {
|
||||
|
||||
protected override void PopulateMenu (GenericMenu menu, SerializedProperty property, SpineSlot targetAttribute, SkeletonData data) {
|
||||
for (int i = 0; i < data.Slots.Count; i++) {
|
||||
string name = data.Slots.Items[i].Name;
|
||||
if (name.StartsWith(attrib.startsWith)) {
|
||||
if (attrib.containsBoundingBoxes) {
|
||||
if (name.StartsWith(targetAttribute.startsWith)) {
|
||||
if (targetAttribute.containsBoundingBoxes) {
|
||||
|
||||
int slotIndex = i;
|
||||
|
||||
@ -116,99 +126,166 @@ public class SpineSlotDrawer : PropertyDrawer {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
menu.ShowAsContext();
|
||||
}
|
||||
|
||||
void HandleSelect(object val) {
|
||||
var pair = (SpineDrawerValuePair)val;
|
||||
pair.property.stringValue = pair.str;
|
||||
pair.property.serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
|
||||
public override float GetPropertyHeight(SerializedProperty property, GUIContent label) {
|
||||
return 18;
|
||||
}
|
||||
}
|
||||
|
||||
[CustomPropertyDrawer(typeof(SpineSkin))]
|
||||
public class SpineSkinDrawer : PropertyDrawer {
|
||||
SkeletonDataAsset skeletonDataAsset;
|
||||
|
||||
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) {
|
||||
if (property.propertyType != SerializedPropertyType.String) {
|
||||
EditorGUI.LabelField(position, "ERROR:", "May only apply to type string");
|
||||
return;
|
||||
}
|
||||
|
||||
SpineSkin attrib = (SpineSkin)attribute;
|
||||
|
||||
var dataProperty = property.serializedObject.FindProperty(attrib.dataField);
|
||||
|
||||
if (dataProperty != null) {
|
||||
if (dataProperty.objectReferenceValue is SkeletonDataAsset) {
|
||||
skeletonDataAsset = (SkeletonDataAsset)dataProperty.objectReferenceValue;
|
||||
} else if (dataProperty.objectReferenceValue is SkeletonRenderer) {
|
||||
var renderer = (SkeletonRenderer)dataProperty.objectReferenceValue;
|
||||
if (renderer != null)
|
||||
skeletonDataAsset = renderer.skeletonDataAsset;
|
||||
} else {
|
||||
EditorGUI.LabelField(position, "ERROR:", "Invalid reference type");
|
||||
return;
|
||||
}
|
||||
|
||||
} else if (property.serializedObject.targetObject is Component) {
|
||||
var component = (Component)property.serializedObject.targetObject;
|
||||
if (component.GetComponentInChildren<SkeletonRenderer>() != null) {
|
||||
var skeletonRenderer = component.GetComponentInChildren<SkeletonRenderer>();
|
||||
skeletonDataAsset = skeletonRenderer.skeletonDataAsset;
|
||||
}
|
||||
}
|
||||
|
||||
if (skeletonDataAsset == null) {
|
||||
EditorGUI.LabelField(position, "ERROR:", "Must have reference to a SkeletonDataAsset");
|
||||
return;
|
||||
}
|
||||
|
||||
position = EditorGUI.PrefixLabel(position, label);
|
||||
|
||||
if (GUI.Button(position, property.stringValue, EditorStyles.popup)) {
|
||||
Selector(property);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Selector(SerializedProperty property) {
|
||||
SpineSkin attrib = (SpineSkin)attribute;
|
||||
SkeletonData data = skeletonDataAsset.GetSkeletonData(true);
|
||||
if (data == null)
|
||||
return;
|
||||
|
||||
GenericMenu menu = new GenericMenu();
|
||||
public class SpineSkinDrawer : SpineTreeItemDrawerBase<SpineSkin> {
|
||||
|
||||
protected override void PopulateMenu (GenericMenu menu, SerializedProperty property, SpineSkin targetAttribute, SkeletonData data) {
|
||||
menu.AddDisabledItem(new GUIContent(skeletonDataAsset.name));
|
||||
menu.AddSeparator("");
|
||||
|
||||
for (int i = 0; i < data.Skins.Count; i++) {
|
||||
string name = data.Skins.Items[i].Name;
|
||||
if (name.StartsWith(attrib.startsWith))
|
||||
if (name.StartsWith(targetAttribute.startsWith))
|
||||
menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property));
|
||||
}
|
||||
|
||||
menu.ShowAsContext();
|
||||
}
|
||||
|
||||
void HandleSelect(object val) {
|
||||
var pair = (SpineDrawerValuePair)val;
|
||||
pair.property.stringValue = pair.str;
|
||||
pair.property.serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
|
||||
public override float GetPropertyHeight(SerializedProperty property, GUIContent label) {
|
||||
return 18;
|
||||
[CustomPropertyDrawer(typeof(SpineAnimation))]
|
||||
public class SpineAnimationDrawer : SpineTreeItemDrawerBase<SpineAnimation> {
|
||||
protected override void PopulateMenu (GenericMenu menu, SerializedProperty property, SpineAnimation targetAttribute, SkeletonData data) {
|
||||
var animations = skeletonDataAsset.GetAnimationStateData().SkeletonData.Animations;
|
||||
for (int i = 0; i < animations.Count; i++) {
|
||||
string name = animations.Items[i].Name;
|
||||
if (name.StartsWith(targetAttribute.startsWith))
|
||||
menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[CustomPropertyDrawer(typeof(SpineEventData))]
|
||||
public class SpineEventDataDrawer : SpineTreeItemDrawerBase<SpineEventData> {
|
||||
protected override void PopulateMenu (GenericMenu menu, SerializedProperty property, SpineEventData targetAttribute, SkeletonData data) {
|
||||
var events = skeletonDataAsset.GetSkeletonData(false).Events;
|
||||
for (int i = 0; i < events.Count; i++) {
|
||||
string name = events.Items[i].Name;
|
||||
if (name.StartsWith(targetAttribute.startsWith))
|
||||
menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[CustomPropertyDrawer(typeof(SpineAttachment))]
|
||||
public class SpineAttachmentDrawer : SpineTreeItemDrawerBase<SpineAttachment> {
|
||||
protected override void PopulateMenu (GenericMenu menu, SerializedProperty property, SpineAttachment targetAttribute, SkeletonData data) {
|
||||
List<Skin> validSkins = new List<Skin>();
|
||||
SkeletonRenderer skeletonRenderer = null;
|
||||
|
||||
if (property.serializedObject.targetObject is Component) {
|
||||
var component = (Component)property.serializedObject.targetObject;
|
||||
if (component.GetComponentInChildren<SkeletonRenderer>() != null) {
|
||||
skeletonRenderer = component.GetComponentInChildren<SkeletonRenderer>();
|
||||
if (skeletonDataAsset != skeletonRenderer.skeletonDataAsset) {
|
||||
Debug.LogError("DataField SkeletonDataAsset and SkeletonRenderer/SkeletonAnimation's SkeletonDataAsset do not match. Remove the explicit dataField parameter of your [SpineAttachment] field.");
|
||||
}
|
||||
|
||||
skeletonDataAsset = skeletonRenderer.skeletonDataAsset;
|
||||
}
|
||||
}
|
||||
|
||||
if (skeletonRenderer != null && targetAttribute.currentSkinOnly) {
|
||||
if (skeletonRenderer.skeleton.Skin != null) {
|
||||
validSkins.Add(skeletonRenderer.skeleton.Skin);
|
||||
} else {
|
||||
validSkins.Add(data.Skins.Items[0]);
|
||||
}
|
||||
} else {
|
||||
foreach (Skin skin in data.Skins) {
|
||||
if (skin != null)
|
||||
validSkins.Add(skin);
|
||||
}
|
||||
}
|
||||
|
||||
List<string> attachmentNames = new List<string>();
|
||||
List<string> placeholderNames = new List<string>();
|
||||
|
||||
string prefix = "";
|
||||
|
||||
if (skeletonRenderer != null && targetAttribute.currentSkinOnly)
|
||||
menu.AddDisabledItem(new GUIContent(skeletonRenderer.gameObject.name + " (SkeletonRenderer)"));
|
||||
else
|
||||
menu.AddDisabledItem(new GUIContent(skeletonDataAsset.name));
|
||||
menu.AddSeparator("");
|
||||
|
||||
menu.AddItem(new GUIContent("Null"), property.stringValue == "", HandleSelect, new SpineDrawerValuePair("", property));
|
||||
menu.AddSeparator("");
|
||||
|
||||
Skin defaultSkin = data.Skins.Items[0];
|
||||
|
||||
SerializedProperty slotProperty = property.serializedObject.FindProperty(targetAttribute.slotField);
|
||||
string slotMatch = "";
|
||||
if (slotProperty != null) {
|
||||
if (slotProperty.propertyType == SerializedPropertyType.String) {
|
||||
slotMatch = slotProperty.stringValue.ToLower();
|
||||
}
|
||||
}
|
||||
|
||||
foreach (Skin skin in validSkins) {
|
||||
string skinPrefix = skin.Name + "/";
|
||||
|
||||
if (validSkins.Count > 1)
|
||||
prefix = skinPrefix;
|
||||
|
||||
for (int i = 0; i < data.Slots.Count; i++) {
|
||||
if (slotMatch.Length > 0 && data.Slots.Items[i].Name.ToLower().Contains(slotMatch) == false)
|
||||
continue;
|
||||
|
||||
attachmentNames.Clear();
|
||||
placeholderNames.Clear();
|
||||
|
||||
skin.FindNamesForSlot(i, attachmentNames);
|
||||
if (skin != defaultSkin) {
|
||||
defaultSkin.FindNamesForSlot(i, attachmentNames);
|
||||
skin.FindNamesForSlot(i, placeholderNames);
|
||||
}
|
||||
|
||||
|
||||
for (int a = 0; a < attachmentNames.Count; a++) {
|
||||
|
||||
string attachmentPath = attachmentNames[a];
|
||||
string menuPath = prefix + data.Slots.Items[i].Name + "/" + attachmentPath;
|
||||
string name = attachmentNames[a];
|
||||
|
||||
if (targetAttribute.returnAttachmentPath)
|
||||
name = skin.Name + "/" + data.Slots.Items[i].Name + "/" + attachmentPath;
|
||||
|
||||
if (targetAttribute.placeholdersOnly && placeholderNames.Contains(attachmentPath) == false) {
|
||||
menu.AddDisabledItem(new GUIContent(menuPath));
|
||||
} else {
|
||||
menu.AddItem(new GUIContent(menuPath), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[CustomPropertyDrawer(typeof(SpineBone))]
|
||||
public class SpineBoneDrawer : SpineTreeItemDrawerBase<SpineBone> {
|
||||
|
||||
protected override void PopulateMenu (GenericMenu menu, SerializedProperty property, SpineBone targetAttribute, SkeletonData data) {
|
||||
menu.AddDisabledItem(new GUIContent(skeletonDataAsset.name));
|
||||
menu.AddSeparator("");
|
||||
|
||||
for (int i = 0; i < data.Bones.Count; i++) {
|
||||
string name = data.Bones.Items[i].Name;
|
||||
if (name.StartsWith(targetAttribute.startsWith))
|
||||
menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[CustomPropertyDrawer(typeof(SpineAtlasRegion))]
|
||||
public class SpineAtlasRegionDrawer : PropertyDrawer {
|
||||
Component component;
|
||||
@ -262,323 +339,10 @@ public class SpineAtlasRegionDrawer : PropertyDrawer {
|
||||
menu.ShowAsContext();
|
||||
}
|
||||
|
||||
void HandleSelect(object val) {
|
||||
static void HandleSelect (object val) {
|
||||
var pair = (SpineDrawerValuePair)val;
|
||||
pair.property.stringValue = pair.str;
|
||||
pair.property.serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
|
||||
public override float GetPropertyHeight(SerializedProperty property, GUIContent label) {
|
||||
return 18;
|
||||
}
|
||||
}
|
||||
|
||||
[CustomPropertyDrawer(typeof(SpineAnimation))]
|
||||
public class SpineAnimationDrawer : PropertyDrawer {
|
||||
SkeletonDataAsset skeletonDataAsset;
|
||||
|
||||
|
||||
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) {
|
||||
|
||||
|
||||
if (property.propertyType != SerializedPropertyType.String) {
|
||||
EditorGUI.LabelField(position, "ERROR:", "May only apply to type string");
|
||||
return;
|
||||
}
|
||||
|
||||
SpineAnimation attrib = (SpineAnimation)attribute;
|
||||
|
||||
var dataProperty = property.serializedObject.FindProperty(attrib.dataField);
|
||||
|
||||
if (dataProperty != null) {
|
||||
if (dataProperty.objectReferenceValue is SkeletonDataAsset) {
|
||||
skeletonDataAsset = (SkeletonDataAsset)dataProperty.objectReferenceValue;
|
||||
} else if (dataProperty.objectReferenceValue is SkeletonRenderer) {
|
||||
var renderer = (SkeletonRenderer)dataProperty.objectReferenceValue;
|
||||
if (renderer != null)
|
||||
skeletonDataAsset = renderer.skeletonDataAsset;
|
||||
} else {
|
||||
EditorGUI.LabelField(position, "ERROR:", "Invalid reference type");
|
||||
return;
|
||||
}
|
||||
} else if (property.serializedObject.targetObject is Component) {
|
||||
var component = (Component)property.serializedObject.targetObject;
|
||||
if (component.GetComponentInChildren<SkeletonRenderer>() != null) {
|
||||
var skeletonRenderer = component.GetComponentInChildren<SkeletonRenderer>();
|
||||
skeletonDataAsset = skeletonRenderer.skeletonDataAsset;
|
||||
}
|
||||
}
|
||||
|
||||
if (skeletonDataAsset == null) {
|
||||
EditorGUI.LabelField(position, "ERROR:", "Must have reference to a SkeletonDataAsset");
|
||||
return;
|
||||
}
|
||||
|
||||
position = EditorGUI.PrefixLabel(position, label);
|
||||
|
||||
if (GUI.Button(position, property.stringValue, EditorStyles.popup)) {
|
||||
Selector(property);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Selector(SerializedProperty property) {
|
||||
|
||||
SpineAnimation attrib = (SpineAnimation)attribute;
|
||||
|
||||
GenericMenu menu = new GenericMenu();
|
||||
|
||||
var animations = skeletonDataAsset.GetAnimationStateData().SkeletonData.Animations;
|
||||
for (int i = 0; i < animations.Count; i++) {
|
||||
string name = animations.Items[i].Name;
|
||||
if (name.StartsWith(attrib.startsWith))
|
||||
menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property));
|
||||
}
|
||||
|
||||
menu.ShowAsContext();
|
||||
}
|
||||
|
||||
void HandleSelect(object val) {
|
||||
var pair = (SpineDrawerValuePair)val;
|
||||
pair.property.stringValue = pair.str;
|
||||
pair.property.serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
|
||||
public override float GetPropertyHeight(SerializedProperty property, GUIContent label) {
|
||||
return 18;
|
||||
}
|
||||
}
|
||||
|
||||
[CustomPropertyDrawer(typeof(SpineAttachment))]
|
||||
public class SpineAttachmentDrawer : PropertyDrawer {
|
||||
|
||||
SkeletonDataAsset skeletonDataAsset;
|
||||
SkeletonRenderer skeletonRenderer;
|
||||
|
||||
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) {
|
||||
|
||||
if (property.propertyType != SerializedPropertyType.String) {
|
||||
EditorGUI.LabelField(position, "ERROR:", "May only apply to type string");
|
||||
return;
|
||||
}
|
||||
|
||||
SpineAttachment attrib = (SpineAttachment)attribute;
|
||||
|
||||
var dataProperty = property.serializedObject.FindProperty(attrib.dataField);
|
||||
|
||||
if (dataProperty != null) {
|
||||
if (dataProperty.objectReferenceValue is SkeletonDataAsset) {
|
||||
skeletonDataAsset = (SkeletonDataAsset)dataProperty.objectReferenceValue;
|
||||
} else if (dataProperty.objectReferenceValue is SkeletonRenderer) {
|
||||
var renderer = (SkeletonRenderer)dataProperty.objectReferenceValue;
|
||||
if (renderer != null)
|
||||
skeletonDataAsset = renderer.skeletonDataAsset;
|
||||
else {
|
||||
EditorGUI.LabelField(position, "ERROR:", "No SkeletonRenderer");
|
||||
}
|
||||
} else {
|
||||
EditorGUI.LabelField(position, "ERROR:", "Invalid reference type");
|
||||
return;
|
||||
}
|
||||
|
||||
} else if (property.serializedObject.targetObject is Component) {
|
||||
var component = (Component)property.serializedObject.targetObject;
|
||||
if (component.GetComponentInChildren<SkeletonRenderer>() != null) {
|
||||
skeletonRenderer = component.GetComponentInChildren<SkeletonRenderer>();
|
||||
skeletonDataAsset = skeletonRenderer.skeletonDataAsset;
|
||||
}
|
||||
}
|
||||
|
||||
if (skeletonDataAsset == null && skeletonRenderer == null) {
|
||||
EditorGUI.LabelField(position, "ERROR:", "Must have reference to a SkeletonDataAsset or SkeletonRenderer");
|
||||
return;
|
||||
}
|
||||
|
||||
position = EditorGUI.PrefixLabel(position, label);
|
||||
|
||||
if (GUI.Button(position, property.stringValue, EditorStyles.popup)) {
|
||||
Selector(property);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Selector(SerializedProperty property) {
|
||||
SkeletonData data = skeletonDataAsset.GetSkeletonData(true);
|
||||
|
||||
if (data == null)
|
||||
return;
|
||||
|
||||
SpineAttachment attrib = (SpineAttachment)attribute;
|
||||
|
||||
List<Skin> validSkins = new List<Skin>();
|
||||
|
||||
if (skeletonRenderer != null && attrib.currentSkinOnly) {
|
||||
if (skeletonRenderer.skeleton.Skin != null) {
|
||||
validSkins.Add(skeletonRenderer.skeleton.Skin);
|
||||
} else {
|
||||
validSkins.Add(data.Skins.Items[0]);
|
||||
}
|
||||
} else {
|
||||
foreach (Skin skin in data.Skins) {
|
||||
if (skin != null)
|
||||
validSkins.Add(skin);
|
||||
}
|
||||
}
|
||||
|
||||
GenericMenu menu = new GenericMenu();
|
||||
List<string> attachmentNames = new List<string>();
|
||||
List<string> placeholderNames = new List<string>();
|
||||
|
||||
string prefix = "";
|
||||
|
||||
if (skeletonRenderer != null && attrib.currentSkinOnly)
|
||||
menu.AddDisabledItem(new GUIContent(skeletonRenderer.gameObject.name + " (SkeletonRenderer)"));
|
||||
else
|
||||
menu.AddDisabledItem(new GUIContent(skeletonDataAsset.name));
|
||||
menu.AddSeparator("");
|
||||
|
||||
menu.AddItem(new GUIContent("Null"), property.stringValue == "", HandleSelect, new SpineDrawerValuePair("", property));
|
||||
menu.AddSeparator("");
|
||||
|
||||
Skin defaultSkin = data.Skins.Items[0];
|
||||
|
||||
SerializedProperty slotProperty = property.serializedObject.FindProperty(attrib.slotField);
|
||||
string slotMatch = "";
|
||||
if (slotProperty != null) {
|
||||
if (slotProperty.propertyType == SerializedPropertyType.String) {
|
||||
slotMatch = slotProperty.stringValue.ToLower();
|
||||
}
|
||||
}
|
||||
|
||||
foreach (Skin skin in validSkins) {
|
||||
string skinPrefix = skin.Name + "/";
|
||||
|
||||
if (validSkins.Count > 1)
|
||||
prefix = skinPrefix;
|
||||
|
||||
for (int i = 0; i < data.Slots.Count; i++) {
|
||||
if (slotMatch.Length > 0 && data.Slots.Items[i].Name.ToLower().Contains(slotMatch) == false)
|
||||
continue;
|
||||
|
||||
attachmentNames.Clear();
|
||||
placeholderNames.Clear();
|
||||
|
||||
skin.FindNamesForSlot(i, attachmentNames);
|
||||
if (skin != defaultSkin) {
|
||||
defaultSkin.FindNamesForSlot(i, attachmentNames);
|
||||
skin.FindNamesForSlot(i, placeholderNames);
|
||||
}
|
||||
|
||||
|
||||
for (int a = 0; a < attachmentNames.Count; a++) {
|
||||
|
||||
string attachmentPath = attachmentNames[a];
|
||||
string menuPath = prefix + data.Slots.Items[i].Name + "/" + attachmentPath;
|
||||
string name = attachmentNames[a];
|
||||
|
||||
if (attrib.returnAttachmentPath)
|
||||
name = skin.Name + "/" + data.Slots.Items[i].Name + "/" + attachmentPath;
|
||||
|
||||
if (attrib.placeholdersOnly && placeholderNames.Contains(attachmentPath) == false) {
|
||||
menu.AddDisabledItem(new GUIContent(menuPath));
|
||||
} else {
|
||||
menu.AddItem(new GUIContent(menuPath), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
menu.ShowAsContext();
|
||||
}
|
||||
|
||||
void HandleSelect(object val) {
|
||||
var pair = (SpineDrawerValuePair)val;
|
||||
pair.property.stringValue = pair.str;
|
||||
pair.property.serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
|
||||
public override float GetPropertyHeight(SerializedProperty property, GUIContent label) {
|
||||
return 18;
|
||||
}
|
||||
}
|
||||
|
||||
[CustomPropertyDrawer(typeof(SpineBone))]
|
||||
public class SpineBoneDrawer : PropertyDrawer {
|
||||
SkeletonDataAsset skeletonDataAsset;
|
||||
|
||||
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) {
|
||||
if (property.propertyType != SerializedPropertyType.String) {
|
||||
EditorGUI.LabelField(position, "ERROR:", "May only apply to type string");
|
||||
return;
|
||||
}
|
||||
|
||||
SpineBone attrib = (SpineBone)attribute;
|
||||
|
||||
var dataProperty = property.serializedObject.FindProperty(attrib.dataField);
|
||||
|
||||
if (dataProperty != null) {
|
||||
if (dataProperty.objectReferenceValue is SkeletonDataAsset) {
|
||||
skeletonDataAsset = (SkeletonDataAsset)dataProperty.objectReferenceValue;
|
||||
} else if (dataProperty.objectReferenceValue is SkeletonRenderer) {
|
||||
var renderer = (SkeletonRenderer)dataProperty.objectReferenceValue;
|
||||
if (renderer != null)
|
||||
skeletonDataAsset = renderer.skeletonDataAsset;
|
||||
} else {
|
||||
EditorGUI.LabelField(position, "ERROR:", "Invalid reference type");
|
||||
return;
|
||||
}
|
||||
|
||||
} else if (property.serializedObject.targetObject is Component) {
|
||||
var component = (Component)property.serializedObject.targetObject;
|
||||
if (component.GetComponentInChildren<SkeletonRenderer>() != null) {
|
||||
var skeletonRenderer = component.GetComponentInChildren<SkeletonRenderer>();
|
||||
skeletonDataAsset = skeletonRenderer.skeletonDataAsset;
|
||||
}
|
||||
}
|
||||
|
||||
if (skeletonDataAsset == null) {
|
||||
EditorGUI.LabelField(position, "ERROR:", "Must have reference to a SkeletonDataAsset");
|
||||
return;
|
||||
}
|
||||
|
||||
position = EditorGUI.PrefixLabel(position, label);
|
||||
|
||||
if (GUI.Button(position, property.stringValue, EditorStyles.popup)) {
|
||||
Selector(property);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Selector(SerializedProperty property) {
|
||||
SpineBone attrib = (SpineBone)attribute;
|
||||
SkeletonData data = skeletonDataAsset.GetSkeletonData(true);
|
||||
if (data == null)
|
||||
return;
|
||||
|
||||
GenericMenu menu = new GenericMenu();
|
||||
|
||||
menu.AddDisabledItem(new GUIContent(skeletonDataAsset.name));
|
||||
menu.AddSeparator("");
|
||||
|
||||
for (int i = 0; i < data.Bones.Count; i++) {
|
||||
string name = data.Bones.Items[i].Name;
|
||||
if (name.StartsWith(attrib.startsWith))
|
||||
menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property));
|
||||
}
|
||||
|
||||
menu.ShowAsContext();
|
||||
}
|
||||
|
||||
void HandleSelect(object val) {
|
||||
var pair = (SpineDrawerValuePair)val;
|
||||
pair.property.stringValue = pair.str;
|
||||
pair.property.serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
|
||||
public override float GetPropertyHeight(SerializedProperty property, GUIContent label) {
|
||||
return 18;
|
||||
}
|
||||
}
|
||||
@ -764,8 +764,8 @@ public class SpineEditorUtilities : AssetPostprocessor {
|
||||
string[] atlasLines = atlasStr.Split('\n');
|
||||
List<string> pageFiles = new List<string>();
|
||||
for (int i = 0; i < atlasLines.Length - 1; i++) {
|
||||
if (atlasLines[i].Length == 0)
|
||||
pageFiles.Add(atlasLines[i + 1]);
|
||||
if (atlasLines[i].Trim().Length == 0)
|
||||
pageFiles.Add(atlasLines[i + 1].Trim());
|
||||
}
|
||||
|
||||
atlasAsset.materials = new Material[pageFiles.Count];
|
||||
|
||||
@ -30,7 +30,6 @@
|
||||
*****************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using Spine;
|
||||
@ -38,12 +37,12 @@ using Spine;
|
||||
[ExecuteInEditMode]
|
||||
[AddComponentMenu("Spine/SkeletonAnimation")]
|
||||
public class SkeletonAnimation : SkeletonRenderer, ISkeletonAnimation {
|
||||
public float timeScale = 1;
|
||||
public bool loop;
|
||||
|
||||
/// <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;
|
||||
|
||||
|
||||
|
||||
public event UpdateBonesDelegate UpdateLocal {
|
||||
add { _UpdateLocal += value; }
|
||||
remove { _UpdateLocal -= value; }
|
||||
@ -63,18 +62,21 @@ public class SkeletonAnimation : SkeletonRenderer, ISkeletonAnimation {
|
||||
protected event UpdateBonesDelegate _UpdateWorld;
|
||||
protected event UpdateBonesDelegate _UpdateComplete;
|
||||
|
||||
// TODO: Make this a safe getter. Lazy-initialize and avoid double-initialization.
|
||||
public Skeleton Skeleton {
|
||||
get {
|
||||
return this.skeleton;
|
||||
}
|
||||
get { return this.skeleton; }
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private String
|
||||
_animationName;
|
||||
private String _animationName;
|
||||
|
||||
public String AnimationName {
|
||||
get {
|
||||
if (!valid) {
|
||||
Debug.LogWarning("You tried access AnimationName but the SkeletonAnimation was not valid. Try checking your Skeleton Data for errors.");
|
||||
return null;
|
||||
}
|
||||
|
||||
TrackEntry entry = state.GetCurrent(0);
|
||||
return entry == null ? null : entry.Animation.Name;
|
||||
}
|
||||
@ -82,6 +84,12 @@ public class SkeletonAnimation : SkeletonRenderer, ISkeletonAnimation {
|
||||
if (_animationName == value)
|
||||
return;
|
||||
_animationName = value;
|
||||
|
||||
if (!valid) {
|
||||
Debug.LogWarning("You tried to change AnimationName but the SkeletonAnimation was not valid. Try checking your Skeleton Data for errors.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (value == null || value.Length == 0)
|
||||
state.ClearTrack(0);
|
||||
else
|
||||
@ -89,18 +97,63 @@ public class SkeletonAnimation : SkeletonRenderer, ISkeletonAnimation {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Whether or not an animation 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>
|
||||
#if UNITY_5
|
||||
[Tooltip("Whether or not an animation 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.")]
|
||||
#endif
|
||||
public bool loop;
|
||||
|
||||
/// <summary>
|
||||
/// The rate at which animations progress over time. 1 means 100%. 0.5 means 50%.
|
||||
/// AnimationState and TrackEntry also have their own timeScale. These are combined multiplicatively.</summary>
|
||||
#if UNITY_5
|
||||
[Tooltip("The rate at which animations progress over time. 1 means 100%. 0.5 means 50%.")]
|
||||
#endif
|
||||
public float timeScale = 1;
|
||||
|
||||
#if UNITY_5
|
||||
[Tooltip("Setting this to true makes the SkeletonAnimation behave similar to Spine editor. New animations will not inherit the pose from a previous animation. If you need to intermittently and programmatically pose your skeleton, leave this false.")]
|
||||
#endif
|
||||
[SerializeField]
|
||||
protected bool autoReset = false;
|
||||
|
||||
/// <summary>
|
||||
/// Setting this to true makes the SkeletonAnimation behave similar to Spine editor.
|
||||
/// New animations will not inherit the pose from a previous animation.
|
||||
/// If you need to intermittently and programmatically pose your skeleton, leave this false.</summary>
|
||||
public bool AutoReset {
|
||||
get { return this.autoReset; }
|
||||
set {
|
||||
if (!autoReset && value) {
|
||||
state.Start -= HandleNewAnimationAutoreset; // make sure there isn't a double-subscription.
|
||||
state.Start += HandleNewAnimationAutoreset;
|
||||
}
|
||||
autoReset = value;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Reset () {
|
||||
base.Reset();
|
||||
if (!valid)
|
||||
return;
|
||||
|
||||
state = new Spine.AnimationState(skeletonDataAsset.GetAnimationStateData());
|
||||
|
||||
if (autoReset) {
|
||||
state.Start += HandleNewAnimationAutoreset;
|
||||
}
|
||||
|
||||
if (_animationName != null && _animationName.Length > 0) {
|
||||
state.SetAnimation(0, _animationName, loop);
|
||||
Update(0);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void HandleNewAnimationAutoreset (Spine.AnimationState state, int trackIndex) {
|
||||
if (!autoReset) return;
|
||||
if (skeleton != null) skeleton.SetToSetupPose();
|
||||
}
|
||||
|
||||
public virtual void Update () {
|
||||
Update(Time.deltaTime);
|
||||
}
|
||||
|
||||
@ -108,7 +108,7 @@ public class SkeletonDataAsset : ScriptableObject {
|
||||
#else
|
||||
if (spriteCollection != null) {
|
||||
attachmentLoader = new SpriteCollectionAttachmentLoader(spriteCollection);
|
||||
skeletonDataScale = (1.0f / (spriteCollection.invOrthoSize * spriteCollection.halfTargetHeight) * scale) * 100f;
|
||||
skeletonDataScale = (1.0f / (spriteCollection.invOrthoSize * spriteCollection.halfTargetHeight) * scale);
|
||||
} else {
|
||||
if (atlasArr.Length == 0) {
|
||||
Reset();
|
||||
|
||||
@ -39,39 +39,46 @@ using Spine;
|
||||
public class SkeletonRenderer : MonoBehaviour {
|
||||
|
||||
public delegate void SkeletonRendererDelegate (SkeletonRenderer skeletonRenderer);
|
||||
|
||||
public SkeletonRendererDelegate OnReset;
|
||||
[System.NonSerialized]
|
||||
public bool valid;
|
||||
[System.NonSerialized]
|
||||
public Skeleton skeleton;
|
||||
|
||||
public SkeletonDataAsset skeletonDataAsset;
|
||||
public String initialSkinName;
|
||||
|
||||
#region Advanced
|
||||
public bool calculateNormals, calculateTangents;
|
||||
public float zSpacing;
|
||||
public bool renderMeshes = true, immutableTriangles;
|
||||
public bool frontFacing;
|
||||
public bool logErrors = false;
|
||||
|
||||
[SpineSlot]
|
||||
public string[] submeshSeparators = new string[0];
|
||||
// Submesh Separation
|
||||
[SpineSlot] public string[] submeshSeparators = new string[0];
|
||||
[HideInInspector] public List<Slot> submeshSeparatorSlots = new List<Slot>();
|
||||
#endregion
|
||||
|
||||
[HideInInspector]
|
||||
public List<Slot> submeshSeparatorSlots = new List<Slot>();
|
||||
[System.NonSerialized] public bool valid;
|
||||
[System.NonSerialized] public Skeleton skeleton;
|
||||
|
||||
private MeshRenderer meshRenderer;
|
||||
private MeshFilter meshFilter;
|
||||
|
||||
private Mesh mesh1, mesh2;
|
||||
private bool useMesh1;
|
||||
|
||||
private float[] tempVertices = new float[8];
|
||||
private Vector3[] vertices;
|
||||
private Color32[] colors;
|
||||
private Vector2[] uvs;
|
||||
private Material[] sharedMaterials = new Material[0];
|
||||
|
||||
private MeshState meshState = new MeshState();
|
||||
private readonly ExposedList<Material> submeshMaterials = new ExposedList<Material>();
|
||||
private readonly ExposedList<Submesh> submeshes = new ExposedList<Submesh>();
|
||||
private SkeletonUtilitySubmeshRenderer[] submeshRenderers;
|
||||
private MeshState meshState = new MeshState();
|
||||
|
||||
public virtual void Awake () {
|
||||
Reset();
|
||||
}
|
||||
|
||||
public virtual void Reset () {
|
||||
if (meshFilter != null)
|
||||
@ -144,10 +151,6 @@ public class SkeletonRenderer : MonoBehaviour {
|
||||
submeshRenderers = GetComponentsInChildren<SkeletonUtilitySubmeshRenderer>();
|
||||
}
|
||||
|
||||
public virtual void Awake () {
|
||||
Reset();
|
||||
}
|
||||
|
||||
public virtual void OnDestroy () {
|
||||
if (mesh1 != null) {
|
||||
if (Application.isPlaying)
|
||||
@ -167,7 +170,7 @@ public class SkeletonRenderer : MonoBehaviour {
|
||||
mesh2 = null;
|
||||
}
|
||||
|
||||
private Mesh newMesh () {
|
||||
private static Mesh newMesh () {
|
||||
Mesh mesh = new Mesh();
|
||||
mesh.name = "Skeleton Mesh";
|
||||
mesh.hideFlags = HideFlags.HideAndDontSave;
|
||||
@ -185,6 +188,7 @@ public class SkeletonRenderer : MonoBehaviour {
|
||||
|
||||
// Count vertices and submesh triangles.
|
||||
int vertexCount = 0;
|
||||
|
||||
int submeshTriangleCount = 0, submeshFirstVertex = 0, submeshStartSlotIndex = 0;
|
||||
Material lastMaterial = null;
|
||||
ExposedList<Slot> drawOrder = skeleton.drawOrder;
|
||||
@ -193,26 +197,41 @@ public class SkeletonRenderer : MonoBehaviour {
|
||||
bool renderMeshes = this.renderMeshes;
|
||||
|
||||
// Clear last state of attachments and submeshes
|
||||
MeshState.SingleMeshState stateTemp = meshState.stateTemp;
|
||||
stateTemp.attachments.Clear(true);
|
||||
stateTemp.UpdateDrawOrderCount(drawOrderCount);
|
||||
MeshState.SingleMeshState workingState = meshState.buffer;
|
||||
var workingAttachments = workingState.attachments;
|
||||
var workingFlips = workingState.attachmentsFlipState;
|
||||
var workingSubmeshArguments = workingState.addSubmeshArguments;
|
||||
workingAttachments.Clear(true);
|
||||
workingState.UpdateAttachmentCount(drawOrderCount);
|
||||
workingSubmeshArguments.Clear(false);
|
||||
|
||||
MeshState.SingleMeshState storedState = useMesh1 ? meshState.stateMesh1 : meshState.stateMesh2;
|
||||
var storedAttachments = storedState.attachments;
|
||||
var storedFlips = storedState.attachmentsFlipState;
|
||||
|
||||
bool mustUpdateMeshStructure = storedState.requiresUpdate || // Force update if the mesh was cleared. (prevents flickering due to incorrect state)
|
||||
drawOrder.Count != storedAttachments.Count || // Number of slots changed (when does this happen?)
|
||||
immutableTriangles != storedState.immutableTriangles; // Immutable Triangles flag changed.
|
||||
|
||||
stateTemp.addSubmeshArguments.Clear(false);
|
||||
for (int i = 0; i < drawOrderCount; i++) {
|
||||
Slot slot = drawOrder.Items[i];
|
||||
Bone bone = slot.bone;
|
||||
Attachment attachment = slot.attachment;
|
||||
|
||||
object rendererObject;
|
||||
object rendererObject; // An AtlasRegion in plain Spine-Unity. Spine-TK2D hooks into TK2D's system. eventual source of Material object.
|
||||
int attachmentVertexCount, attachmentTriangleCount;
|
||||
bool worldScaleXIsPositive = bone.worldScaleX >= 0f;
|
||||
bool worldScaleYIsPositive = bone.worldScaleY >= 0f;
|
||||
bool worldScaleIsSameSigns = (worldScaleXIsPositive && worldScaleYIsPositive) ||
|
||||
(!worldScaleXIsPositive && !worldScaleYIsPositive);
|
||||
bool flip = frontFacing && ((bone.worldFlipX != bone.worldFlipY) == worldScaleIsSameSigns);
|
||||
stateTemp.attachmentsFlipState.Items[i] = flip;
|
||||
|
||||
stateTemp.attachments.Items[i] = attachment;
|
||||
// Handle flipping for normals (for lighting).
|
||||
bool worldScaleIsSameSigns = ((bone.worldScaleY >= 0f) == (bone.worldScaleX >= 0f));
|
||||
bool flip = frontFacing && ((bone.worldFlipX != bone.worldFlipY) == worldScaleIsSameSigns); // TODO: bone flipX and flipY will be removed in Spine 3.0
|
||||
|
||||
workingFlips.Items[i] = flip;
|
||||
workingAttachments.Items[i] = attachment;
|
||||
|
||||
mustUpdateMeshStructure = mustUpdateMeshStructure || // Always prefer short circuited or. || and not |=.
|
||||
(attachment != storedAttachments.Items[i]) || // Attachment order changed. // This relies on the drawOrder.Count != storedAttachments.Count check above as a bounds check.
|
||||
(flip != storedFlips.Items[i]); // Flip states changed.
|
||||
|
||||
RegionAttachment regionAttachment = attachment as RegionAttachment;
|
||||
if (regionAttachment != null) {
|
||||
rendererObject = regionAttachment.RendererObject;
|
||||
@ -237,17 +256,27 @@ public class SkeletonRenderer : MonoBehaviour {
|
||||
}
|
||||
}
|
||||
|
||||
// Populate submesh when material changes.
|
||||
#if !SPINE_TK2D
|
||||
Material material = (Material)((AtlasRegion)rendererObject).page.rendererObject;
|
||||
#else
|
||||
Material material = (rendererObject.GetType() == typeof(Material)) ? (Material)rendererObject : (Material)((AtlasRegion)rendererObject).page.rendererObject;
|
||||
#endif
|
||||
|
||||
// Populate submesh when material changes. (or when forced to separate by a submeshSeparator)
|
||||
if ((lastMaterial != null && lastMaterial.GetInstanceID() != material.GetInstanceID()) ||
|
||||
(submeshSeparatorSlotsCount > 0 && submeshSeparatorSlots.Contains(slot))) {
|
||||
stateTemp.addSubmeshArguments.Add(
|
||||
new MeshState.AddSubmeshArguments(lastMaterial, submeshStartSlotIndex, i, submeshTriangleCount, submeshFirstVertex, false)
|
||||
|
||||
workingSubmeshArguments.Add(
|
||||
new MeshState.AddSubmeshArguments {
|
||||
material = lastMaterial,
|
||||
startSlot = submeshStartSlotIndex,
|
||||
endSlot = i,
|
||||
triangleCount = submeshTriangleCount,
|
||||
firstVertex = submeshFirstVertex,
|
||||
isLastSubmesh = false
|
||||
}
|
||||
);
|
||||
|
||||
submeshTriangleCount = 0;
|
||||
submeshFirstVertex = vertexCount;
|
||||
submeshStartSlotIndex = i;
|
||||
@ -257,24 +286,41 @@ public class SkeletonRenderer : MonoBehaviour {
|
||||
submeshTriangleCount += attachmentTriangleCount;
|
||||
vertexCount += attachmentVertexCount;
|
||||
}
|
||||
stateTemp.addSubmeshArguments.Add(
|
||||
new MeshState.AddSubmeshArguments(lastMaterial, submeshStartSlotIndex, drawOrderCount, submeshTriangleCount, submeshFirstVertex, true)
|
||||
|
||||
|
||||
workingSubmeshArguments.Add(
|
||||
new MeshState.AddSubmeshArguments {
|
||||
material = lastMaterial,
|
||||
startSlot = submeshStartSlotIndex,
|
||||
endSlot = drawOrderCount,
|
||||
triangleCount = submeshTriangleCount,
|
||||
firstVertex = submeshFirstVertex,
|
||||
isLastSubmesh = true
|
||||
}
|
||||
);
|
||||
|
||||
bool mustUpdateMeshStructure = CheckIfMustUpdateMeshStructure(stateTemp.attachments, stateTemp.attachmentsFlipState, stateTemp.addSubmeshArguments);
|
||||
mustUpdateMeshStructure = mustUpdateMeshStructure ||
|
||||
this.sharedMaterials.Length != workingSubmeshArguments.Count || // Material array changed in size
|
||||
CheckIfMustUpdateMeshStructure(workingSubmeshArguments); // Submesh Argument Array changed.
|
||||
|
||||
// CheckIfMustUpdateMaterialArray (workingMaterials, sharedMaterials)
|
||||
if (!mustUpdateMeshStructure) {
|
||||
// Narrow phase material array check.
|
||||
var workingMaterials = workingSubmeshArguments.Items;
|
||||
for (int i = 0, n = sharedMaterials.Length; i < n; i++) {
|
||||
if (this.sharedMaterials[i] != workingMaterials[i].material) { // Bounds check is implied above.
|
||||
mustUpdateMeshStructure = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NOT ELSE
|
||||
|
||||
if (mustUpdateMeshStructure) {
|
||||
submeshMaterials.Clear();
|
||||
for (int i = 0, n = stateTemp.addSubmeshArguments.Count; i < n; i++) {
|
||||
MeshState.AddSubmeshArguments arguments = stateTemp.addSubmeshArguments.Items[i];
|
||||
AddSubmesh(
|
||||
arguments.material,
|
||||
arguments.startSlot,
|
||||
arguments.endSlot,
|
||||
arguments.triangleCount,
|
||||
arguments.firstVertex,
|
||||
arguments.lastSubmesh,
|
||||
stateTemp.attachmentsFlipState
|
||||
);
|
||||
this.submeshMaterials.Clear();
|
||||
for (int i = 0, n = workingSubmeshArguments.Count; i < n; i++) {
|
||||
AddSubmesh(workingSubmeshArguments.Items[i], workingFlips);
|
||||
}
|
||||
|
||||
// Set materials.
|
||||
@ -286,6 +332,7 @@ public class SkeletonRenderer : MonoBehaviour {
|
||||
meshRenderer.sharedMaterials = sharedMaterials;
|
||||
}
|
||||
|
||||
|
||||
// Ensure mesh data is the right size.
|
||||
Vector3[] vertices = this.vertices;
|
||||
bool newTriangles = vertexCount > vertices.Length;
|
||||
@ -294,8 +341,12 @@ public class SkeletonRenderer : MonoBehaviour {
|
||||
this.vertices = vertices = new Vector3[vertexCount];
|
||||
this.colors = new Color32[vertexCount];
|
||||
this.uvs = new Vector2[vertexCount];
|
||||
|
||||
mesh1.Clear();
|
||||
mesh2.Clear();
|
||||
meshState.stateMesh1.requiresUpdate = true;
|
||||
meshState.stateMesh2.requiresUpdate = true;
|
||||
|
||||
} else {
|
||||
// Too many vertices, zero the extra.
|
||||
Vector3 zero = Vector3.zero;
|
||||
@ -502,6 +553,9 @@ public class SkeletonRenderer : MonoBehaviour {
|
||||
mesh.subMeshCount = submeshCount;
|
||||
for (int i = 0; i < submeshCount; ++i)
|
||||
mesh.SetTriangles(submeshes.Items[i].triangles, i);
|
||||
|
||||
// Done updating mesh.
|
||||
storedState.requiresUpdate = false;
|
||||
}
|
||||
|
||||
Vector3 meshBoundsExtents = meshBoundsMax - meshBoundsMin;
|
||||
@ -528,22 +582,23 @@ public class SkeletonRenderer : MonoBehaviour {
|
||||
}
|
||||
|
||||
// Update previous state
|
||||
MeshState.SingleMeshState currentMeshState = useMesh1 ? meshState.stateMesh1 : meshState.stateMesh2;
|
||||
currentMeshState.immutableTriangles = immutableTriangles;
|
||||
storedState.immutableTriangles = immutableTriangles;
|
||||
|
||||
currentMeshState.attachments.Clear(true);
|
||||
currentMeshState.attachments.GrowIfNeeded(stateTemp.attachments.Capacity);
|
||||
currentMeshState.attachments.Count = stateTemp.attachments.Count;
|
||||
stateTemp.attachments.CopyTo(currentMeshState.attachments.Items);
|
||||
storedAttachments.Clear(true);
|
||||
storedAttachments.GrowIfNeeded(workingAttachments.Capacity);
|
||||
storedAttachments.Count = workingAttachments.Count;
|
||||
workingAttachments.CopyTo(storedAttachments.Items);
|
||||
|
||||
currentMeshState.attachmentsFlipState.GrowIfNeeded(stateTemp.attachmentsFlipState.Capacity);
|
||||
currentMeshState.attachmentsFlipState.Count = stateTemp.attachmentsFlipState.Count;
|
||||
stateTemp.attachmentsFlipState.CopyTo(currentMeshState.attachmentsFlipState.Items);
|
||||
storedFlips.GrowIfNeeded(workingFlips.Capacity);
|
||||
storedFlips.Count = workingFlips.Count;
|
||||
workingFlips.CopyTo(storedFlips.Items);
|
||||
|
||||
currentMeshState.addSubmeshArguments.GrowIfNeeded(stateTemp.addSubmeshArguments.Capacity);
|
||||
currentMeshState.addSubmeshArguments.Count = stateTemp.addSubmeshArguments.Count;
|
||||
stateTemp.addSubmeshArguments.CopyTo(currentMeshState.addSubmeshArguments.Items);
|
||||
storedState.addSubmeshArguments.GrowIfNeeded(workingSubmeshArguments.Capacity);
|
||||
storedState.addSubmeshArguments.Count = workingSubmeshArguments.Count;
|
||||
workingSubmeshArguments.CopyTo(storedState.addSubmeshArguments.Items);
|
||||
|
||||
|
||||
// Submesh Renderers
|
||||
if (submeshRenderers.Length > 0) {
|
||||
for (int i = 0; i < submeshRenderers.Length; i++) {
|
||||
SkeletonUtilitySubmeshRenderer submeshRenderer = submeshRenderers[i];
|
||||
@ -558,59 +613,32 @@ public class SkeletonRenderer : MonoBehaviour {
|
||||
useMesh1 = !useMesh1;
|
||||
}
|
||||
|
||||
private bool CheckIfMustUpdateMeshStructure (ExposedList<Attachment> attachmentsTemp, ExposedList<bool> attachmentsFlipStateTemp, ExposedList<MeshState.AddSubmeshArguments> addSubmeshArgumentsTemp) {
|
||||
private bool CheckIfMustUpdateMeshStructure (ExposedList<MeshState.AddSubmeshArguments> workingAddSubmeshArguments) {
|
||||
#if UNITY_EDITOR
|
||||
if (!Application.isPlaying)
|
||||
return true;
|
||||
#endif
|
||||
|
||||
// Check if any mesh settings were changed
|
||||
bool mustUpdateMeshStructure =
|
||||
immutableTriangles != (useMesh1 ? meshState.stateMesh1.immutableTriangles : meshState.stateMesh2.immutableTriangles);
|
||||
|
||||
if (mustUpdateMeshStructure)
|
||||
return true;
|
||||
|
||||
// Check if any attachments were enabled/disabled
|
||||
// or submesh structures has changed
|
||||
MeshState.SingleMeshState currentMeshState = useMesh1 ? meshState.stateMesh1 : meshState.stateMesh2;
|
||||
ExposedList<Attachment> attachmentsCurrentMesh = currentMeshState.attachments;
|
||||
|
||||
// Check if submesh structures has changed
|
||||
ExposedList<MeshState.AddSubmeshArguments> addSubmeshArgumentsCurrentMesh = currentMeshState.addSubmeshArguments;
|
||||
ExposedList<bool> attachmentsFlipStateCurrentMesh = currentMeshState.attachmentsFlipState;
|
||||
|
||||
// Check attachments
|
||||
int attachmentCount = attachmentsTemp.Count;
|
||||
if (attachmentsCurrentMesh.Count != attachmentCount)
|
||||
return true;
|
||||
|
||||
for (int i = 0; i < attachmentCount; i++) {
|
||||
if (attachmentsCurrentMesh.Items[i] != attachmentsTemp.Items[i])
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check flip state
|
||||
for (int i = 0; i < attachmentCount; i++) {
|
||||
if (attachmentsFlipStateCurrentMesh.Items[i] != attachmentsFlipStateTemp.Items[i])
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check submeshes
|
||||
int submeshCount = addSubmeshArgumentsTemp.Count;
|
||||
int submeshCount = workingAddSubmeshArguments.Count;
|
||||
if (addSubmeshArgumentsCurrentMesh.Count != submeshCount)
|
||||
return true;
|
||||
|
||||
for (int i = 0; i < submeshCount; i++) {
|
||||
if (!addSubmeshArgumentsCurrentMesh.Items[i].Equals(ref addSubmeshArgumentsTemp.Items[i]))
|
||||
if (!addSubmeshArgumentsCurrentMesh.Items[i].Equals(ref workingAddSubmeshArguments.Items[i]))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Stores vertices and triangles for a single material. */
|
||||
private void AddSubmesh (Material material, int startSlot, int endSlot, int triangleCount, int firstVertex, bool lastSubmesh, ExposedList<bool> flipStates) {
|
||||
private void AddSubmesh (MeshState.AddSubmeshArguments submeshArguments, ExposedList<bool> flipStates) {
|
||||
int submeshIndex = submeshMaterials.Count;
|
||||
submeshMaterials.Add(material);
|
||||
submeshMaterials.Add(submeshArguments.material);
|
||||
|
||||
if (submeshes.Count <= submeshIndex)
|
||||
submeshes.Add(new Submesh());
|
||||
@ -618,10 +646,13 @@ public class SkeletonRenderer : MonoBehaviour {
|
||||
return;
|
||||
|
||||
Submesh submesh = submeshes.Items[submeshIndex];
|
||||
|
||||
int[] triangles = submesh.triangles;
|
||||
|
||||
int triangleCount = submeshArguments.triangleCount;
|
||||
int firstVertex = submeshArguments.firstVertex;
|
||||
|
||||
int trianglesCapacity = triangles.Length;
|
||||
if (lastSubmesh && trianglesCapacity > triangleCount) {
|
||||
if (submeshArguments.isLastSubmesh && trianglesCapacity > triangleCount) {
|
||||
// Last submesh may have more triangles than required, so zero triangles to the end.
|
||||
for (int i = triangleCount; i < trianglesCapacity; i++)
|
||||
triangles[i] = 0;
|
||||
@ -637,8 +668,8 @@ public class SkeletonRenderer : MonoBehaviour {
|
||||
if (submesh.firstVertex != firstVertex || submesh.triangleCount < triangleCount) {
|
||||
submesh.triangleCount = triangleCount;
|
||||
submesh.firstVertex = firstVertex;
|
||||
int drawOrderIndex = 0;
|
||||
for (int i = 0; i < triangleCount; i += 6, firstVertex += 4, drawOrderIndex++) {
|
||||
//int drawOrderIndex = 0;
|
||||
for (int i = 0; i < triangleCount; i += 6, firstVertex += 4/*, drawOrderIndex++*/) {
|
||||
triangles[i] = firstVertex;
|
||||
triangles[i + 1] = firstVertex + 2;
|
||||
triangles[i + 2] = firstVertex + 1;
|
||||
@ -650,14 +681,16 @@ public class SkeletonRenderer : MonoBehaviour {
|
||||
return;
|
||||
}
|
||||
|
||||
// Store triangles.
|
||||
// Iterate through all slots and store their triangles.
|
||||
ExposedList<Slot> drawOrder = skeleton.DrawOrder;
|
||||
for (int i = startSlot, triangleIndex = 0; i < endSlot; i++) {
|
||||
int triangleIndex = 0; // Modified by loop
|
||||
for (int i = submeshArguments.startSlot, n = submeshArguments.endSlot; i < n; i++) {
|
||||
Slot slot = drawOrder.Items[i];
|
||||
Attachment attachment = slot.attachment;
|
||||
|
||||
bool flip = flipStates.Items[i];
|
||||
|
||||
// Add RegionAttachment triangles
|
||||
if (attachment is RegionAttachment) {
|
||||
if (!flip) {
|
||||
triangles[triangleIndex] = firstVertex;
|
||||
@ -679,16 +712,18 @@ public class SkeletonRenderer : MonoBehaviour {
|
||||
firstVertex += 4;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Add (Skinned)MeshAttachment triangles
|
||||
int[] attachmentTriangles;
|
||||
int attachmentVertexCount;
|
||||
MeshAttachment meshAttachment = attachment as MeshAttachment;
|
||||
if (meshAttachment != null) {
|
||||
attachmentVertexCount = meshAttachment.vertices.Length >> 1;
|
||||
attachmentVertexCount = meshAttachment.vertices.Length >> 1; // length/2
|
||||
attachmentTriangles = meshAttachment.triangles;
|
||||
} else {
|
||||
SkinnedMeshAttachment skinnedMeshAttachment = attachment as SkinnedMeshAttachment;
|
||||
if (skinnedMeshAttachment != null) {
|
||||
attachmentVertexCount = skinnedMeshAttachment.uvs.Length >> 1;
|
||||
attachmentVertexCount = skinnedMeshAttachment.uvs.Length >> 1; // length/2
|
||||
attachmentTriangles = skinnedMeshAttachment.triangles;
|
||||
} else
|
||||
continue;
|
||||
@ -728,22 +763,23 @@ public class SkeletonRenderer : MonoBehaviour {
|
||||
|
||||
private class MeshState {
|
||||
public int vertexCount;
|
||||
public readonly SingleMeshState stateTemp = new SingleMeshState();
|
||||
public readonly SingleMeshState buffer = new SingleMeshState();
|
||||
public readonly SingleMeshState stateMesh1 = new SingleMeshState();
|
||||
public readonly SingleMeshState stateMesh2 = new SingleMeshState();
|
||||
|
||||
public class SingleMeshState {
|
||||
public bool immutableTriangles;
|
||||
public bool requiresUpdate;
|
||||
public readonly ExposedList<Attachment> attachments = new ExposedList<Attachment>();
|
||||
public readonly ExposedList<bool> attachmentsFlipState = new ExposedList<bool>();
|
||||
public readonly ExposedList<AddSubmeshArguments> addSubmeshArguments = new ExposedList<AddSubmeshArguments>();
|
||||
|
||||
public void UpdateDrawOrderCount(int drawOrderCount) {
|
||||
attachmentsFlipState.GrowIfNeeded(drawOrderCount);
|
||||
attachmentsFlipState.Count = drawOrderCount;
|
||||
public void UpdateAttachmentCount (int attachmentCount) {
|
||||
attachmentsFlipState.GrowIfNeeded(attachmentCount);
|
||||
attachmentsFlipState.Count = attachmentCount;
|
||||
|
||||
attachments.GrowIfNeeded(drawOrderCount);
|
||||
attachments.Count = drawOrderCount;
|
||||
attachments.GrowIfNeeded(attachmentCount);
|
||||
attachments.Count = attachmentCount;
|
||||
}
|
||||
}
|
||||
|
||||
@ -753,22 +789,13 @@ public class SkeletonRenderer : MonoBehaviour {
|
||||
public int endSlot;
|
||||
public int triangleCount;
|
||||
public int firstVertex;
|
||||
public bool lastSubmesh;
|
||||
|
||||
public AddSubmeshArguments (Material material, int startSlot, int endSlot, int triangleCount, int firstVertex, bool lastSubmesh) {
|
||||
this.material = material;
|
||||
this.startSlot = startSlot;
|
||||
this.endSlot = endSlot;
|
||||
this.triangleCount = triangleCount;
|
||||
this.firstVertex = firstVertex;
|
||||
this.lastSubmesh = lastSubmesh;
|
||||
}
|
||||
public bool isLastSubmesh;
|
||||
|
||||
public bool Equals (ref AddSubmeshArguments other) {
|
||||
return
|
||||
!ReferenceEquals(material, null) &&
|
||||
!ReferenceEquals(other.material, null) &&
|
||||
material.GetInstanceID() == other.material.GetInstanceID() &&
|
||||
//!ReferenceEquals(material, null) &&
|
||||
//!ReferenceEquals(other.material, null) &&
|
||||
//material.GetInstanceID() == other.material.GetInstanceID() &&
|
||||
startSlot == other.startSlot &&
|
||||
endSlot == other.endSlot &&
|
||||
triangleCount == other.triangleCount &&
|
||||
|
||||
@ -7,9 +7,12 @@
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
|
||||
public class SpineSlot : PropertyAttribute {
|
||||
public string startsWith = "";
|
||||
public abstract class SpineAttributeBase : PropertyAttribute {
|
||||
public string dataField = "";
|
||||
public string startsWith = "";
|
||||
}
|
||||
|
||||
public class SpineSlot : SpineAttributeBase {
|
||||
public bool containsBoundingBoxes = false;
|
||||
|
||||
/// <summary>
|
||||
@ -28,10 +31,22 @@ public class SpineSlot : PropertyAttribute {
|
||||
}
|
||||
}
|
||||
|
||||
public class SpineSkin : PropertyAttribute {
|
||||
public string startsWith = "";
|
||||
public string dataField = "";
|
||||
public class SpineEventData : SpineAttributeBase {
|
||||
/// <summary>
|
||||
/// Smart popup menu for Spine Events (Spine.EventData)
|
||||
/// </summary>
|
||||
/// <param name="startsWith">Filters popup results to elements that begin with supplied string.</param>
|
||||
/// <param name="dataField">If specified, a locally scoped field with the name supplied by in dataField will be used to fill the popup results.
|
||||
/// Valid types are SkeletonDataAsset and SkeletonRenderer (and derivatives).
|
||||
/// If left empty and the script the attribute is applied to is derived from Component, GetComponent<SkeletonRenderer>() will be called as a fallback.
|
||||
/// </param>
|
||||
public SpineEventData(string startsWith = "", string dataField = "") {
|
||||
this.startsWith = startsWith;
|
||||
this.dataField = dataField;
|
||||
}
|
||||
}
|
||||
|
||||
public class SpineSkin : SpineAttributeBase {
|
||||
/// <summary>
|
||||
/// Smart popup menu for Spine Skins
|
||||
/// </summary>
|
||||
@ -45,10 +60,7 @@ public class SpineSkin : PropertyAttribute {
|
||||
this.dataField = dataField;
|
||||
}
|
||||
}
|
||||
public class SpineAnimation : PropertyAttribute {
|
||||
public string startsWith = "";
|
||||
public string dataField = "";
|
||||
|
||||
public class SpineAnimation : SpineAttributeBase {
|
||||
/// <summary>
|
||||
/// Smart popup menu for Spine Animations
|
||||
/// </summary>
|
||||
@ -63,18 +75,12 @@ public class SpineAnimation : PropertyAttribute {
|
||||
}
|
||||
}
|
||||
|
||||
public class SpineAttachment : PropertyAttribute {
|
||||
public class SpineAttachment : SpineAttributeBase {
|
||||
public bool returnAttachmentPath = false;
|
||||
public bool currentSkinOnly = false;
|
||||
public bool placeholdersOnly = false;
|
||||
public string dataField = "";
|
||||
public string slotField = "";
|
||||
|
||||
|
||||
public SpineAttachment () {
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Smart popup menu for Spine Attachments
|
||||
/// </summary>
|
||||
@ -122,7 +128,8 @@ public class SpineAttachment : PropertyAttribute {
|
||||
slot = "";
|
||||
name = "";
|
||||
return;
|
||||
} else if (chunks.Length < 2) {
|
||||
}
|
||||
else if (chunks.Length < 2) {
|
||||
throw new System.Exception("Cannot generate Attachment Hierarchy from string! Not enough components! [" + fullPath + "]");
|
||||
}
|
||||
skin = chunks[0];
|
||||
@ -135,10 +142,7 @@ public class SpineAttachment : PropertyAttribute {
|
||||
}
|
||||
}
|
||||
|
||||
public class SpineBone : PropertyAttribute {
|
||||
public string startsWith = "";
|
||||
public string dataField = "";
|
||||
|
||||
public class SpineBone : SpineAttributeBase {
|
||||
/// <summary>
|
||||
/// Smart popup menu for Spine Bones
|
||||
/// </summary>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user