Importer improvements

Attributes
Multiple atlas support
More examples
This commit is contained in:
Fenrisul 2015-01-20 00:08:06 -08:00
parent b3b3b25a83
commit 444a536dad
66 changed files with 3706 additions and 442 deletions

View File

@ -32,15 +32,15 @@ using System;
namespace Spine {
public class AtlasAttachmentLoader : AttachmentLoader {
private Atlas atlas;
private Atlas[] atlasArray;
public AtlasAttachmentLoader (Atlas atlas) {
if (atlas == null) throw new ArgumentNullException("atlas cannot be null.");
this.atlas = atlas;
public AtlasAttachmentLoader (params Atlas[] atlasArray) {
if (atlasArray == null) throw new ArgumentNullException("atlas array cannot be null.");
this.atlasArray = atlasArray;
}
public RegionAttachment NewRegionAttachment (Skin skin, String name, String path) {
AtlasRegion region = atlas.FindRegion(path);
AtlasRegion region = FindRegion(path);
if (region == null) throw new Exception("Region not found in atlas: " + path + " (region attachment: " + name + ")");
RegionAttachment attachment = new RegionAttachment(name);
attachment.RendererObject = region;
@ -55,7 +55,7 @@ namespace Spine {
}
public MeshAttachment NewMeshAttachment (Skin skin, String name, String path) {
AtlasRegion region = atlas.FindRegion(path);
AtlasRegion region = FindRegion(path);
if (region == null) throw new Exception("Region not found in atlas: " + path + " (mesh attachment: " + name + ")");
MeshAttachment attachment = new MeshAttachment(name);
attachment.RendererObject = region;
@ -74,7 +74,7 @@ namespace Spine {
}
public SkinnedMeshAttachment NewSkinnedMeshAttachment (Skin skin, String name, String path) {
AtlasRegion region = atlas.FindRegion(path);
AtlasRegion region = FindRegion(path);
if (region == null) throw new Exception("Region not found in atlas: " + path + " (skinned mesh attachment: " + name + ")");
SkinnedMeshAttachment attachment = new SkinnedMeshAttachment(name);
attachment.RendererObject = region;
@ -95,5 +95,17 @@ namespace Spine {
public BoundingBoxAttachment NewBoundingBoxAttachment (Skin skin, String name) {
return new BoundingBoxAttachment(name);
}
public AtlasRegion FindRegion(string name) {
AtlasRegion region;
for (int i = 0; i < atlasArray.Length; i++) {
region = atlasArray[i].FindRegion(name);
if (region != null)
return region;
}
return null;
}
}
}

View File

@ -42,8 +42,8 @@ namespace Spine {
private AttachmentLoader attachmentLoader;
public float Scale { get; set; }
public SkeletonJson (Atlas atlas)
: this(new AtlasAttachmentLoader(atlas)) {
public SkeletonJson (params Atlas[] atlasArray)
: this(new AtlasAttachmentLoader(atlasArray)) {
}
public SkeletonJson (AttachmentLoader attachmentLoader) {

View File

@ -0,0 +1,4 @@
fileFormatVersion: 2
guid: 63212ccaf5776bd489cba58fb67a2233
DefaultImporter:
userData:

Binary file not shown.

View File

@ -0,0 +1,4 @@
fileFormatVersion: 2
guid: c5673b83016f67a4c99772dfb7b3c437
DefaultImporter:
userData:

View File

@ -71,11 +71,17 @@ public class BasicPlatformerController : MonoBehaviour {
#if UNITY_4_5
[Header("Animation")]
#endif
[SpineAnimation(dataSource: "skeletonAnimation")]
public string walkName = "Walk";
[SpineAnimation(dataSource: "skeletonAnimation")]
public string runName = "Run";
[SpineAnimation(dataSource: "skeletonAnimation")]
public string idleName = "Idle";
[SpineAnimation(dataSource: "skeletonAnimation")]
public string jumpName = "Jump";
[SpineAnimation(dataSource: "skeletonAnimation")]
public string fallName = "Fall";
[SpineAnimation(dataSource: "skeletonAnimation")]
public string crouchName = "Crouch";
#if UNITY_4_5

View File

@ -0,0 +1,17 @@
using UnityEngine;
using System.Collections;
public class Chimera : MonoBehaviour {
public SkeletonDataAsset skeletonDataSource;
[SpineAttachment(currentSkinOnly: false, returnFullPath: true, dataSource: "skeletonDataSource")]
public string attachmentPath;
[SpineSlot]
public string targetSlot;
void Start() {
GetComponent<SkeletonRenderer>().skeleton.FindSlot(targetSlot).Attachment = SpineAttachment.GetAttachment(attachmentPath, skeletonDataSource);
}
}

View File

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

View File

@ -0,0 +1,51 @@
using UnityEngine;
using System.Collections;
public class FootSoldierExample : MonoBehaviour {
[SpineAnimation("Idle")]
public string idleAnimation;
[SpineAnimation]
public string attackAnimation;
[SpineSlot]
public string eyesSlot;
[SpineAttachment(currentSkinOnly: true, slot: "eyesSlot")]
public string eyesOpenAttachment;
[SpineAttachment(currentSkinOnly: true, slot: "eyesSlot")]
public string blinkAttachment;
[Range(0, 0.2f)]
public float blinkDuration = 0.05f;
private SkeletonAnimation skeletonAnimation;
void Awake() {
skeletonAnimation = GetComponent<SkeletonAnimation>();
}
void Start() {
skeletonAnimation.state.SetAnimation(0, idleAnimation, true);
StartCoroutine("Blink");
}
void Update() {
if (Input.GetKey(KeyCode.Space)) {
if (skeletonAnimation.state.GetCurrent(0).Animation.Name != attackAnimation) {
skeletonAnimation.state.SetAnimation(0, attackAnimation, false);
skeletonAnimation.state.AddAnimation(0, idleAnimation, true, 0);
}
}
}
IEnumerator Blink() {
while (true) {
yield return new WaitForSeconds(Random.Range(0.25f, 3f));
skeletonAnimation.skeleton.SetAttachment(eyesSlot, blinkAttachment);
yield return new WaitForSeconds(blinkDuration);
skeletonAnimation.skeleton.SetAttachment(eyesSlot, eyesOpenAttachment);
}
}
}

View File

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

View File

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

View File

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

View File

@ -0,0 +1,34 @@
Equipment.png
size: 512,128
format: RGBA8888
filter: Linear,Linear
repeat: none
Equipment/shield1
rotate: true
xy: 220, 33
size: 71, 118
orig: 256, 256
offset: 92, 69
index: -1
Equipment/shield2
rotate: true
xy: 340, 22
size: 82, 111
orig: 256, 256
offset: 87, 72
index: -1
Equipment/sword1
rotate: false
xy: 2, 2
size: 161, 31
orig: 512, 256
offset: 217, 112
index: -1
Equipment/sword4
rotate: false
xy: 2, 35
size: 216, 69
orig: 512, 256
offset: 200, 94
index: -1

View File

@ -0,0 +1,4 @@
fileFormatVersion: 2
guid: 4f0639ff8bc42314d8d62ee0f7ba541f
TextScriptImporter:
userData:

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

View File

@ -0,0 +1,47 @@
fileFormatVersion: 2
guid: ddb89f63d0296cf4f8572b0448bb6b30
TextureImporter:
fileIDToRecycleName: {}
serializedVersion: 2
mipmaps:
mipMapMode: 0
enableMipMap: 0
linearTexture: 0
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: 2048
textureSettings:
filterMode: -1
aniso: -1
mipBias: -1
wrapMode: -1
nPOTScale: 1
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: 0
textureType: -1
buildTargetSettings: []
spriteSheet:
sprites: []
spritePackingTag:
userData:

View File

@ -0,0 +1,4 @@
fileFormatVersion: 2
guid: c574489dd067c2b4cb4dc165a4c410cc
NativeFormatImporter:
userData:

View File

@ -0,0 +1,4 @@
fileFormatVersion: 2
guid: e6e388faa521c96449984cfa4b60d74e
NativeFormatImporter:
userData:

View File

@ -0,0 +1,90 @@
FS_White.png
size: 256,256
format: RGBA8888
filter: Linear,Linear
repeat: none
White/arm
rotate: false
xy: 143, 156
size: 111, 98
orig: 111, 98
offset: 0, 0
index: -1
White/arm 2
rotate: false
xy: 95, 36
size: 46, 79
orig: 46, 79
offset: 0, 0
index: -1
White/body
rotate: false
xy: 2, 12
size: 91, 103
orig: 91, 103
offset: 0, 0
index: -1
White/eyes
rotate: true
xy: 195, 87
size: 67, 31
orig: 67, 31
offset: 0, 0
index: -1
White/eyes blink
rotate: true
xy: 228, 87
size: 67, 22
orig: 67, 22
offset: 0, 0
index: -1
White/feet
rotate: false
xy: 95, 2
size: 50, 32
orig: 50, 32
offset: 0, 0
index: -1
White/feet 2
rotate: false
xy: 193, 58
size: 55, 27
orig: 55, 27
offset: 0, 0
index: -1
White/hand
rotate: false
xy: 147, 9
size: 32, 28
orig: 32, 28
offset: 0, 0
index: -1
White/head 1
rotate: false
xy: 2, 117
size: 139, 137
orig: 139, 137
offset: 0, 0
index: -1
White/leg
rotate: false
xy: 143, 39
size: 48, 55
orig: 48, 55
offset: 0, 0
index: -1
White/leg 2
rotate: false
xy: 143, 96
size: 50, 58
orig: 50, 58
offset: 0, 0
index: -1
White/mouth
rotate: false
xy: 193, 35
size: 28, 21
orig: 28, 21
offset: 0, 0
index: -1

View File

@ -0,0 +1,4 @@
fileFormatVersion: 2
guid: 5ca7c05342912804eb0a2fd5bbe85b58
TextScriptImporter:
userData:

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

View File

@ -0,0 +1,47 @@
fileFormatVersion: 2
guid: 57b57f94df266f94ea0981915a4472e1
TextureImporter:
fileIDToRecycleName: {}
serializedVersion: 2
mipmaps:
mipMapMode: 0
enableMipMap: 0
linearTexture: 0
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: 2048
textureSettings:
filterMode: -1
aniso: -1
mipBias: -1
wrapMode: -1
nPOTScale: 1
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: 0
textureType: -1
buildTargetSettings: []
spriteSheet:
sprites: []
spritePackingTag:
userData:

View File

@ -0,0 +1,4 @@
fileFormatVersion: 2
guid: e3c7e74834cd8424f8735ba05e94a688
NativeFormatImporter:
userData:

View File

@ -0,0 +1,4 @@
fileFormatVersion: 2
guid: 5a3598dafa118754db95756064347da7
NativeFormatImporter:
userData:

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,4 @@
fileFormatVersion: 2
guid: b075ad1d14f5db64b941d266e53de57d
TextScriptImporter:
userData:

View File

@ -0,0 +1,4 @@
fileFormatVersion: 2
guid: e57cdb51287d3924ebb2ececf816733b
NativeFormatImporter:
userData:

View File

@ -0,0 +1,5 @@
Copyright (c) 2014, XDTech
The project file and images in this "FootSoldier" project are provided for
demonstration purposes only and may not be redistributed for any reason nor
used as the basis for derivative work.

View File

@ -0,0 +1,4 @@
fileFormatVersion: 2
guid: 6fe8b327d2ab4bd438a63ffec150a911
TextScriptImporter:
userData:

View File

@ -0,0 +1,5 @@
Copyright (c) 2014, XDTech
The project file and images in this "Hero" project are provided for
demonstration purposes only and may not be redistributed for any reason nor
used as the basis for derivative work.

View File

@ -0,0 +1,4 @@
fileFormatVersion: 2
guid: 0d9db1748e0cf9f408129df308299463
TextScriptImporter:
userData:

View File

@ -0,0 +1,49 @@
using UnityEngine;
using System.Collections;
using Spine;
public class AtlasRegionAttacher : MonoBehaviour {
[System.Serializable]
public class SlotRegionPair {
[SpineSlot]
public string slot;
[SpineAtlasRegion]
public string region;
}
public AtlasAsset atlasAsset;
public SlotRegionPair[] attachments;
[HideInInspector]
public SkeletonRenderer skeletonRenderer;
Atlas atlas;
void Start() {
atlas = atlasAsset.GetAtlas();
this.skeletonRenderer = GetComponent<SkeletonRenderer>();
AtlasAttachmentLoader loader = new AtlasAttachmentLoader(atlas);
float scaleMultiplier = skeletonRenderer.skeletonDataAsset.scale;
var enumerator = attachments.GetEnumerator();
while (enumerator.MoveNext()) {
var entry = (SlotRegionPair)enumerator.Current;
var regionAttachment = loader.NewRegionAttachment(null, entry.region, entry.region);
regionAttachment.Width = regionAttachment.RegionOriginalWidth * scaleMultiplier;
regionAttachment.Height = regionAttachment.RegionOriginalHeight * scaleMultiplier;
regionAttachment.SetColor(new Color(1, 1, 1, 1));
regionAttachment.UpdateOffset();
var slot = this.skeletonRenderer.skeleton.FindSlot(entry.slot);
slot.Attachment = regionAttachment;
}
}
}

View File

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

View File

@ -28,8 +28,14 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEditor;
using UnityEngine;
using Spine;
[CustomEditor(typeof(AtlasAsset))]
public class AtlasAssetInspector : Editor {
@ -46,9 +52,23 @@ public class AtlasAssetInspector : Editor {
EditorGUILayout.PropertyField(atlasFile);
EditorGUILayout.PropertyField(materials, true);
if (atlasFile.objectReferenceValue != null) {
Atlas atlas = asset.GetAtlas();
FieldInfo field = typeof(Atlas).GetField("regions", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.NonPublic);
List<AtlasRegion> regions = (List<AtlasRegion>)field.GetValue(atlas);
EditorGUILayout.LabelField("Regions");
EditorGUI.indentLevel++;
for (int i = 0; i < regions.Count; i++) {
EditorGUILayout.LabelField(regions[i].name);
}
EditorGUI.indentLevel--;
}
if (serializedObject.ApplyModifiedProperties() ||
(Event.current.type == EventType.ValidateCommand && Event.current.commandName == "UndoRedoPerformed")
(UnityEngine.Event.current.type == EventType.ValidateCommand && UnityEngine.Event.current.commandName == "UndoRedoPerformed")
) {
asset.Reset();
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 649 B

View File

@ -0,0 +1,47 @@
fileFormatVersion: 2
guid: 4a1646cf39026224c85ecba92d7d6948
TextureImporter:
fileIDToRecycleName: {}
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: -1
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

@ -5,8 +5,8 @@ TextureImporter:
serializedVersion: 2
mipmaps:
mipMapMode: 0
enableMipMap: 1
linearTexture: 0
enableMipMap: 0
linearTexture: 1
correctGamma: 0
fadeOut: 0
borderMipMap: 0
@ -25,10 +25,10 @@ TextureImporter:
maxTextureSize: 1024
textureSettings:
filterMode: -1
aniso: -1
aniso: 1
mipBias: -1
wrapMode: -1
nPOTScale: 1
wrapMode: 1
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 0
@ -38,8 +38,8 @@ TextureImporter:
spritePivot: {x: .5, y: .5}
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spritePixelsToUnits: 100
alphaIsTransparency: 0
textureType: -1
alphaIsTransparency: 1
textureType: 2
buildTargetSettings: []
spriteSheet:
sprites: []

Binary file not shown.

After

Width:  |  Height:  |  Size: 698 B

View File

@ -0,0 +1,47 @@
fileFormatVersion: 2
guid: 0b1bcb09fa228d049ba3c9ea6a57e1ee
TextureImporter:
fileIDToRecycleName: {}
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: -1
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

@ -44,42 +44,46 @@ using Spine;
[CustomEditor(typeof(SkeletonDataAsset))]
public class SkeletonDataAssetInspector : Editor {
private SerializedProperty atlasAsset, skeletonJSON, scale, fromAnimation, toAnimation, duration, defaultMix;
private bool showAnimationStateData = true;
#if UNITY_4_3
private bool m_showAnimationList = true;
#else
private AnimBool m_showAnimationList = new AnimBool(true);
#endif
static bool showAnimationStateData = true;
static bool showAnimationList = true;
static bool showSlotList = false;
static bool showAttachments = false;
private SerializedProperty atlasAssets, skeletonJSON, scale, fromAnimation, toAnimation, duration, defaultMix;
private bool m_initialized = false;
private SkeletonDataAsset m_skeletonDataAsset;
private SkeletonData m_skeletonData;
private string m_skeletonDataAssetGUID;
void OnEnable () {
try {
atlasAsset = serializedObject.FindProperty("atlasAsset");
List<string> warnings = new List<string>();
void OnEnable() {
try {
atlasAssets = serializedObject.FindProperty("atlasAssets");
skeletonJSON = serializedObject.FindProperty("skeletonJSON");
scale = serializedObject.FindProperty("scale");
fromAnimation = serializedObject.FindProperty("fromAnimation");
toAnimation = serializedObject.FindProperty("toAnimation");
duration = serializedObject.FindProperty("duration");
defaultMix = serializedObject.FindProperty("defaultMix");
m_skeletonDataAsset = (SkeletonDataAsset)target;
m_skeletonDataAssetGUID = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(m_skeletonDataAsset));
EditorApplication.update += Update;
EditorApplication.update += Update;
} catch {
}
m_skeletonData = m_skeletonDataAsset.GetSkeletonData(true);
RepopulateWarnings();
}
void OnDestroy () {
void OnDestroy() {
m_initialized = false;
EditorApplication.update -= Update;
this.DestroyPreviewInstances();
@ -88,13 +92,13 @@ public class SkeletonDataAssetInspector : Editor {
this.m_previewUtility = null;
}
}
override public void OnInspectorGUI () {
override public void OnInspectorGUI() {
serializedObject.Update();
SkeletonDataAsset asset = (SkeletonDataAsset)target;
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(atlasAsset);
EditorGUILayout.PropertyField(atlasAssets, true);
EditorGUILayout.PropertyField(skeletonJSON);
EditorGUILayout.PropertyField(scale);
if (EditorGUI.EndChangeCheck()) {
@ -102,142 +106,301 @@ public class SkeletonDataAssetInspector : Editor {
m_previewUtility.Cleanup();
m_previewUtility = null;
}
RepopulateWarnings();
}
SkeletonData skeletonData = asset.GetSkeletonData(asset.atlasAsset == null || asset.skeletonJSON == null);
if (skeletonData != null) {
showAnimationStateData = EditorGUILayout.Foldout(showAnimationStateData, "Animation State Data");
if (showAnimationStateData) {
EditorGUILayout.PropertyField(defaultMix);
// Animation names
String[] animations = new String[skeletonData.Animations.Count];
for (int i = 0; i < animations.Length; i++)
animations[i] = skeletonData.Animations[i].Name;
for (int i = 0; i < fromAnimation.arraySize; i++) {
SerializedProperty from = fromAnimation.GetArrayElementAtIndex(i);
SerializedProperty to = toAnimation.GetArrayElementAtIndex(i);
SerializedProperty durationProp = duration.GetArrayElementAtIndex(i);
EditorGUILayout.BeginHorizontal();
from.stringValue = animations[EditorGUILayout.Popup(Math.Max(Array.IndexOf(animations, from.stringValue), 0), animations)];
to.stringValue = animations[EditorGUILayout.Popup(Math.Max(Array.IndexOf(animations, to.stringValue), 0), animations)];
durationProp.floatValue = EditorGUILayout.FloatField(durationProp.floatValue);
if (GUILayout.Button("Delete")) {
duration.DeleteArrayElementAtIndex(i);
toAnimation.DeleteArrayElementAtIndex(i);
fromAnimation.DeleteArrayElementAtIndex(i);
}
EditorGUILayout.EndHorizontal();
}
EditorGUILayout.BeginHorizontal();
EditorGUILayout.Space();
if (GUILayout.Button("Add Mix")) {
duration.arraySize++;
toAnimation.arraySize++;
fromAnimation.arraySize++;
}
EditorGUILayout.Space();
EditorGUILayout.EndHorizontal();
}
if (m_skeletonData != null) {
DrawAnimationStateInfo();
DrawAnimationList();
DrawSlotList();
if (GUILayout.Button(new GUIContent("Setup Pose", SpineEditorUtilities.Icons.skeleton), GUILayout.Width(105), GUILayout.Height(18))) {
StopAnimation();
m_skeletonAnimation.skeleton.SetToSetupPose();
m_requireRefresh = true;
}
#if UNITY_4_3
m_showAnimationList = EditorGUILayout.Foldout(m_showAnimationList, new GUIContent("Animations", SpineEditorUtilities.Icons.animationRoot));
if(m_showAnimationList){
#else
m_showAnimationList.target = EditorGUILayout.Foldout(m_showAnimationList.target, new GUIContent("Animations", SpineEditorUtilities.Icons.animationRoot));
if (EditorGUILayout.BeginFadeGroup(m_showAnimationList.faded)) {
#endif
EditorGUILayout.LabelField("Name", "Duration");
foreach (Spine.Animation a in skeletonData.Animations) {
GUILayout.BeginHorizontal();
if (m_skeletonAnimation != null && m_skeletonAnimation.state != null) {
if (m_skeletonAnimation.state.GetCurrent(0) != null && m_skeletonAnimation.state.GetCurrent(0).Animation == a) {
GUI.contentColor = Color.black;
if (GUILayout.Button("\u25BA", GUILayout.Width(24))) {
StopAnimation();
}
GUI.contentColor = Color.white;
} else {
if (GUILayout.Button("\u25BA", GUILayout.Width(24))) {
PlayAnimation(a.Name, true);
}
}
} else {
GUILayout.Label("?", GUILayout.Width(24));
}
EditorGUILayout.LabelField(new GUIContent(a.Name, SpineEditorUtilities.Icons.animation), new GUIContent(a.Duration.ToString("f3") + "s" + ("(" + (Mathf.RoundToInt(a.Duration * 30)) + ")").PadLeft(12, ' ')));
GUILayout.EndHorizontal();
}
}
#if !UNITY_4_3
EditorGUILayout.EndFadeGroup();
#endif
} else {
//Show Warnings
foreach (var str in warnings)
EditorGUILayout.LabelField(new GUIContent(str, SpineEditorUtilities.Icons.warning));
}
if (!Application.isPlaying) {
if (serializedObject.ApplyModifiedProperties() ||
(UnityEngine.Event.current.type == EventType.ValidateCommand && UnityEngine.Event.current.commandName == "UndoRedoPerformed")
) {
) {
asset.Reset();
}
}
}
void DrawAnimationStateInfo() {
showAnimationStateData = EditorGUILayout.Foldout(showAnimationStateData, "Animation State Data");
if (!showAnimationStateData)
return;
EditorGUILayout.PropertyField(defaultMix);
// Animation names
String[] animations = new String[m_skeletonData.Animations.Count];
for (int i = 0; i < animations.Length; i++)
animations[i] = m_skeletonData.Animations[i].Name;
for (int i = 0; i < fromAnimation.arraySize; i++) {
SerializedProperty from = fromAnimation.GetArrayElementAtIndex(i);
SerializedProperty to = toAnimation.GetArrayElementAtIndex(i);
SerializedProperty durationProp = duration.GetArrayElementAtIndex(i);
EditorGUILayout.BeginHorizontal();
from.stringValue = animations[EditorGUILayout.Popup(Math.Max(Array.IndexOf(animations, from.stringValue), 0), animations)];
to.stringValue = animations[EditorGUILayout.Popup(Math.Max(Array.IndexOf(animations, to.stringValue), 0), animations)];
durationProp.floatValue = EditorGUILayout.FloatField(durationProp.floatValue);
if (GUILayout.Button("Delete")) {
duration.DeleteArrayElementAtIndex(i);
toAnimation.DeleteArrayElementAtIndex(i);
fromAnimation.DeleteArrayElementAtIndex(i);
}
EditorGUILayout.EndHorizontal();
}
EditorGUILayout.BeginHorizontal();
EditorGUILayout.Space();
if (GUILayout.Button("Add Mix")) {
duration.arraySize++;
toAnimation.arraySize++;
fromAnimation.arraySize++;
}
EditorGUILayout.Space();
EditorGUILayout.EndHorizontal();
}
void DrawAnimationList() {
showAnimationList = EditorGUILayout.Foldout(showAnimationList, new GUIContent("Animations", SpineEditorUtilities.Icons.animationRoot));
if (!showAnimationList)
return;
if (GUILayout.Button(new GUIContent("Setup Pose", SpineEditorUtilities.Icons.skeleton), GUILayout.Width(105), GUILayout.Height(18))) {
StopAnimation();
m_skeletonAnimation.skeleton.SetToSetupPose();
m_requireRefresh = true;
}
EditorGUILayout.LabelField("Name", "Duration");
foreach (Spine.Animation a in m_skeletonData.Animations) {
GUILayout.BeginHorizontal();
if (m_skeletonAnimation != null && m_skeletonAnimation.state != null) {
if (m_skeletonAnimation.state.GetCurrent(0) != null && m_skeletonAnimation.state.GetCurrent(0).Animation == a) {
GUI.contentColor = Color.black;
if (GUILayout.Button("\u25BA", GUILayout.Width(24))) {
StopAnimation();
}
GUI.contentColor = Color.white;
} else {
if (GUILayout.Button("\u25BA", GUILayout.Width(24))) {
PlayAnimation(a.Name, true);
}
}
} else {
GUILayout.Label("?", GUILayout.Width(24));
}
EditorGUILayout.LabelField(new GUIContent(a.Name, SpineEditorUtilities.Icons.animation), new GUIContent(a.Duration.ToString("f3") + "s" + ("(" + (Mathf.RoundToInt(a.Duration * 30)) + ")").PadLeft(12, ' ')));
GUILayout.EndHorizontal();
}
}
void DrawSlotList() {
showSlotList = EditorGUILayout.Foldout(showSlotList, new GUIContent("Slots", SpineEditorUtilities.Icons.slotRoot));
if (!showSlotList)
return;
if (m_skeletonAnimation == null || m_skeletonAnimation.skeleton == null)
return;
EditorGUI.indentLevel++;
try {
showAttachments = EditorGUILayout.ToggleLeft("Show Attachments", showAttachments);
} catch {
return;
}
List<Attachment> slotAttachments = new List<Attachment>();
List<string> slotAttachmentNames = new List<string>();
List<string> defaultSkinAttachmentNames = new List<string>();
var defaultSkin = m_skeletonData.Skins[0];
Skin skin = m_skeletonAnimation.skeleton.Skin;
if (skin == null) {
skin = defaultSkin;
}
for (int i = m_skeletonAnimation.skeleton.Slots.Count-1; i >= 0; i--) {
Slot slot = m_skeletonAnimation.skeleton.Slots[i];
EditorGUILayout.LabelField(new GUIContent(slot.Data.Name, SpineEditorUtilities.Icons.slot));
if (showAttachments) {
EditorGUI.indentLevel++;
slotAttachments.Clear();
slotAttachmentNames.Clear();
defaultSkinAttachmentNames.Clear();
skin.FindNamesForSlot(i, slotAttachmentNames);
skin.FindAttachmentsForSlot(i, slotAttachments);
if (skin != defaultSkin) {
defaultSkin.FindNamesForSlot(i, defaultSkinAttachmentNames);
defaultSkin.FindNamesForSlot(i, slotAttachmentNames);
defaultSkin.FindAttachmentsForSlot(i, slotAttachments);
} else {
defaultSkin.FindNamesForSlot(i, defaultSkinAttachmentNames);
}
for (int a = 0; a < slotAttachments.Count; a++) {
Attachment attachment = slotAttachments[a];
string name = slotAttachmentNames[a];
Texture2D icon = null;
var type = attachment.GetType();
if (type == typeof(RegionAttachment))
icon = SpineEditorUtilities.Icons.image;
else if (type == typeof(MeshAttachment))
icon = SpineEditorUtilities.Icons.mesh;
else if (type == typeof(BoundingBoxAttachment))
icon = SpineEditorUtilities.Icons.boundingBox;
else if (type == typeof(SkinnedMeshAttachment))
icon = SpineEditorUtilities.Icons.weights;
else
icon = SpineEditorUtilities.Icons.warning;
//TODO: Waterboard Nate
//if (name != attachment.Name)
//icon = SpineEditorUtilities.Icons.skinPlaceholder;
bool initialState = slot.Attachment == attachment;
bool toggled = EditorGUILayout.ToggleLeft(new GUIContent(name, icon), slot.Attachment == attachment);
if (!defaultSkinAttachmentNames.Contains(name)) {
Rect skinPlaceHolderIconRect = GUILayoutUtility.GetLastRect();
skinPlaceHolderIconRect.width = SpineEditorUtilities.Icons.skinPlaceholder.width;
skinPlaceHolderIconRect.height = SpineEditorUtilities.Icons.skinPlaceholder.height;
GUI.DrawTexture(skinPlaceHolderIconRect, SpineEditorUtilities.Icons.skinPlaceholder);
}
if (toggled != initialState) {
if (toggled) {
slot.Attachment = attachment;
} else {
slot.Attachment = null;
}
m_requireRefresh = true;
}
}
EditorGUI.indentLevel--;
}
}
EditorGUI.indentLevel--;
}
void RepopulateWarnings() {
warnings.Clear();
if (skeletonJSON.objectReferenceValue == null)
warnings.Add("Missing Skeleton JSON");
else {
if (SpineEditorUtilities.IsSpineJSON((TextAsset)skeletonJSON.objectReferenceValue) == false) {
warnings.Add("Skeleton JSON is not a Valid JSON file");
} else {
bool detectedNullAtlasEntry = false;
List<Atlas> atlasList = new List<Atlas>();
for (int i = 0; i < atlasAssets.arraySize; i++) {
if (atlasAssets.GetArrayElementAtIndex(i).objectReferenceValue == null) {
detectedNullAtlasEntry = true;
break;
} else {
atlasList.Add(((AtlasAsset)atlasAssets.GetArrayElementAtIndex(i).objectReferenceValue).GetAtlas());
}
}
if (detectedNullAtlasEntry)
warnings.Add("AtlasAsset elements cannot be Null");
else {
//get requirements
var missingPaths = SpineEditorUtilities.GetRequiredAtlasRegions(AssetDatabase.GetAssetPath((TextAsset)skeletonJSON.objectReferenceValue));
foreach (var atlas in atlasList) {
for (int i = 0; i < missingPaths.Count; i++) {
if (atlas.FindRegion(missingPaths[i]) != null) {
missingPaths.RemoveAt(i);
i--;
}
}
}
foreach (var str in missingPaths)
warnings.Add("Missing Region: '" + str + "'");
}
}
}
}
//preview window stuff
private PreviewRenderUtility m_previewUtility;
private GameObject m_previewInstance;
private Vector2 previewDir;
private SkeletonAnimation m_skeletonAnimation;
private SkeletonData m_skeletonData;
//private SkeletonData m_skeletonData;
private static int sliderHash = "Slider".GetHashCode();
private float m_lastTime;
private bool m_playing;
private bool m_requireRefresh;
private Color m_originColor = new Color(0.3f, 0.3f, 0.3f, 1);
private void StopAnimation () {
private void StopAnimation() {
m_skeletonAnimation.state.ClearTrack(0);
m_playing = false;
}
List<Spine.Event> m_animEvents = new List<Spine.Event>();
List<float> m_animEventFrames = new List<float>();
private void PlayAnimation (string animName, bool loop) {
private void PlayAnimation(string animName, bool loop) {
m_animEvents.Clear();
m_animEventFrames.Clear();
m_skeletonAnimation.state.SetAnimation(0, animName, loop);
Spine.Animation a = m_skeletonAnimation.state.GetCurrent(0).Animation;
foreach (Timeline t in a.Timelines) {
if (t.GetType() == typeof(EventTimeline)) {
EventTimeline et = (EventTimeline)t;
for (int i = 0; i < et.Events.Length; i++) {
m_animEvents.Add(et.Events[i]);
m_animEventFrames.Add(et.Frames[i]);
}
}
}
m_playing = true;
}
private void InitPreview () {
private void InitPreview() {
if (this.m_previewUtility == null) {
this.m_lastTime = Time.realtimeSinceStartup;
this.m_previewUtility = new PreviewRenderUtility(true);
@ -247,48 +410,59 @@ public class SkeletonDataAssetInspector : Editor {
this.CreatePreviewInstances();
}
}
private void CreatePreviewInstances () {
private void CreatePreviewInstances() {
this.DestroyPreviewInstances();
if (this.m_previewInstance == null) {
string skinName = EditorPrefs.GetString(m_skeletonDataAssetGUID + "_lastSkin", "");
m_previewInstance = SpineEditorUtilities.SpawnAnimatedSkeleton((SkeletonDataAsset)target, skinName).gameObject;
m_previewInstance.hideFlags = HideFlags.HideAndDontSave;
m_previewInstance.layer = 0x1f;
m_skeletonAnimation = m_previewInstance.GetComponent<SkeletonAnimation>();
m_skeletonAnimation.initialSkinName = skinName;
m_skeletonAnimation.LateUpdate();
m_skeletonData = m_skeletonAnimation.skeletonDataAsset.GetSkeletonData(true);
m_previewInstance.renderer.enabled = false;
m_initialized = true;
AdjustCameraGoals(true);
try {
string skinName = EditorPrefs.GetString(m_skeletonDataAssetGUID + "_lastSkin", "");
m_previewInstance = SpineEditorUtilities.SpawnAnimatedSkeleton((SkeletonDataAsset)target, skinName).gameObject;
m_previewInstance.hideFlags = HideFlags.HideAndDontSave;
m_previewInstance.layer = 0x1f;
m_skeletonAnimation = m_previewInstance.GetComponent<SkeletonAnimation>();
m_skeletonAnimation.initialSkinName = skinName;
m_skeletonAnimation.LateUpdate();
m_skeletonData = m_skeletonAnimation.skeletonDataAsset.GetSkeletonData(true);
m_previewInstance.renderer.enabled = false;
m_initialized = true;
AdjustCameraGoals(true);
} catch {
}
}
}
private void DestroyPreviewInstances () {
private void DestroyPreviewInstances() {
if (this.m_previewInstance != null) {
DestroyImmediate(this.m_previewInstance);
m_previewInstance = null;
}
m_initialized = false;
}
public override bool HasPreviewGUI () {
public override bool HasPreviewGUI() {
//TODO: validate json data
for (int i = 0; i < atlasAssets.arraySize; i++) {
var prop = atlasAssets.GetArrayElementAtIndex(i);
if (prop.objectReferenceValue == null)
return false;
}
return skeletonJSON.objectReferenceValue != null;
}
Texture m_previewTex = new Texture();
public override void OnInteractivePreviewGUI (Rect r, GUIStyle background) {
public override void OnInteractivePreviewGUI(Rect r, GUIStyle background) {
this.InitPreview();
if (UnityEngine.Event.current.type == EventType.Repaint) {
if (m_requireRefresh) {
this.m_previewUtility.BeginPreview(r, background);
@ -299,50 +473,54 @@ public class SkeletonDataAssetInspector : Editor {
if (this.m_previewTex != null)
GUI.DrawTexture(r, m_previewTex, ScaleMode.StretchToFill, false);
}
DrawSkinToolbar(r);
NormalizedTimeBar(r);
//TODO: implement panning
// this.previewDir = Drag2D(this.previewDir, r);
MouseScroll(r);
}
float m_orthoGoal = 1;
Vector3 m_posGoal = new Vector3(0, 0, -10);
double m_adjustFrameEndTime = 0;
private void AdjustCameraGoals (bool calculateMixTime) {
private void AdjustCameraGoals(bool calculateMixTime) {
if (this.m_previewInstance == null)
return;
if (calculateMixTime) {
if (m_skeletonAnimation.state.GetCurrent(0) != null) {
m_adjustFrameEndTime = EditorApplication.timeSinceStartup + m_skeletonAnimation.state.GetCurrent(0).Mix;
}
}
GameObject go = this.m_previewInstance;
Bounds bounds = go.renderer.bounds;
m_orthoGoal = bounds.size.y;
m_posGoal = bounds.center + new Vector3(0, 0, -10);
}
private void AdjustCameraGoals () {
private void AdjustCameraGoals() {
AdjustCameraGoals(false);
}
private void AdjustCamera () {
private void AdjustCamera() {
if (m_previewUtility == null)
return;
if (EditorApplication.timeSinceStartup < m_adjustFrameEndTime) {
AdjustCameraGoals();
}
float orthoSet = Mathf.Lerp(this.m_previewUtility.m_Camera.orthographicSize, m_orthoGoal, 0.1f);
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) {
Vector3 pos = Vector3.Lerp(this.m_previewUtility.m_Camera.transform.position, m_posGoal, 0.1f);
@ -352,138 +530,138 @@ public class SkeletonDataAssetInspector : Editor {
m_requireRefresh = true;
}
}
private void DoRenderPreview (bool drawHandles) {
private void DoRenderPreview(bool drawHandles) {
GameObject go = this.m_previewInstance;
if (m_requireRefresh) {
if (m_requireRefresh && go != null) {
go.renderer.enabled = true;
if (EditorApplication.isPlaying) {
//do nothing
} else {
m_skeletonAnimation.Update((Time.realtimeSinceStartup - m_lastTime));
}
m_lastTime = Time.realtimeSinceStartup;
if (!EditorApplication.isPlaying)
m_skeletonAnimation.LateUpdate();
if (drawHandles) {
Handles.SetCamera(m_previewUtility.m_Camera);
Handles.color = m_originColor;
Handles.DrawLine(new Vector3(-1000 * m_skeletonDataAsset.scale, 0, 0), new Vector3(1000 * m_skeletonDataAsset.scale, 0, 0));
Handles.DrawLine(new Vector3(0, 1000 * m_skeletonDataAsset.scale, 0), new Vector3(0, -1000 * m_skeletonDataAsset.scale, 0));
}
this.m_previewUtility.m_Camera.Render();
go.renderer.enabled = false;
}
}
void Update () {
void Update() {
AdjustCamera();
if (m_playing) {
m_requireRefresh = true;
Repaint();
} else if (m_requireRefresh) {
Repaint();
} else {
#if !UNITY_4_3
if (m_showAnimationList.isAnimating)
Repaint();
#endif
}
Repaint();
} else {
//only needed if using smooth menus
}
}
void DrawSkinToolbar (Rect r) {
void DrawSkinToolbar(Rect r) {
if (m_skeletonAnimation == null)
return;
if (m_skeletonAnimation.skeleton != null) {
string label = (m_skeletonAnimation.skeleton != null && m_skeletonAnimation.skeleton.Skin != null) ? m_skeletonAnimation.skeleton.Skin.Name : "default";
Rect popRect = new Rect(r);
popRect.y += 32;
popRect.x += 4;
popRect.height = 24;
popRect.width = 40;
EditorGUI.DropShadowLabel(popRect, new GUIContent("Skin", SpineEditorUtilities.Icons.skinsRoot));
popRect.y += 11;
popRect.width = 150;
popRect.x += 44;
if (GUI.Button(popRect, label, EditorStyles.popup)) {
SelectSkinContext();
}
}
}
void SelectSkinContext () {
void SelectSkinContext() {
GenericMenu menu = new GenericMenu();
foreach (Skin s in m_skeletonData.Skins) {
menu.AddItem(new GUIContent(s.Name), this.m_skeletonAnimation.skeleton.Skin == s, SetSkin, (object)s);
}
menu.ShowAsContext();
}
void SetSkin (object o) {
void SetSkin(object o) {
Skin skin = (Skin)o;
m_skeletonAnimation.initialSkinName = skin.Name;
m_skeletonAnimation.Reset();
m_requireRefresh = true;
EditorPrefs.SetString(m_skeletonDataAssetGUID + "_lastSkin", skin.Name);
}
void NormalizedTimeBar (Rect r) {
void NormalizedTimeBar(Rect r) {
if (m_skeletonAnimation == null)
return;
Rect barRect = new Rect(r);
barRect.height = 32;
barRect.x += 4;
barRect.width -= 4;
GUI.Box(barRect, "");
Rect lineRect = new Rect(barRect);
float width = lineRect.width;
TrackEntry t = m_skeletonAnimation.state.GetCurrent(0);
if (t != null) {
int loopCount = (int)(t.Time / t.EndTime);
float currentTime = t.Time - (t.EndTime * loopCount);
float normalizedTime = currentTime / t.Animation.Duration;
lineRect.x = barRect.x + (width * normalizedTime) - 0.5f;
lineRect.width = 2;
GUI.color = Color.red;
GUI.DrawTexture(lineRect, EditorGUIUtility.whiteTexture);
GUI.color = Color.white;
for (int i = 0; i < m_animEvents.Count; i++) {
//TODO: Tooltip
//Spine.Event spev = animEvents[i];
float fr = m_animEventFrames[i];
Rect evRect = new Rect(barRect);
evRect.x = Mathf.Clamp(((fr / t.Animation.Duration) * width) - (SpineEditorUtilities.Icons._event.width / 2), barRect.x, float.MaxValue);
evRect.width = SpineEditorUtilities.Icons._event.width;
evRect.height = SpineEditorUtilities.Icons._event.height;
evRect.y += SpineEditorUtilities.Icons._event.height;
GUI.DrawTexture(evRect, SpineEditorUtilities.Icons._event);
//TODO: Tooltip
/*
UnityEngine.Event ev = UnityEngine.Event.current;
@ -500,24 +678,24 @@ public class SkeletonDataAssetInspector : Editor {
}
}
}
void MouseScroll (Rect position) {
void MouseScroll(Rect position) {
UnityEngine.Event current = UnityEngine.Event.current;
int controlID = GUIUtility.GetControlID(sliderHash, FocusType.Passive);
switch (current.GetTypeForControl(controlID)) {
case EventType.ScrollWheel:
if (position.Contains(current.mousePosition)) {
m_orthoGoal += current.delta.y * ((SkeletonDataAsset)target).scale * 10;
GUIUtility.hotControl = controlID;
current.Use();
}
break;
case EventType.ScrollWheel:
if (position.Contains(current.mousePosition)) {
m_orthoGoal += current.delta.y * ((SkeletonDataAsset)target).scale * 10;
GUIUtility.hotControl = controlID;
current.Use();
}
break;
}
}
//TODO: Implement preview panning
/*
static Vector2 Drag2D(Vector2 scrollPosition, Rect position)
@ -559,45 +737,45 @@ public class SkeletonDataAssetInspector : Editor {
return scrollPosition;
}
*/
public override GUIContent GetPreviewTitle () {
public override GUIContent GetPreviewTitle() {
return new GUIContent("Preview");
}
public override void OnPreviewSettings () {
public override void OnPreviewSettings() {
if (!m_initialized) {
GUILayout.HorizontalSlider(0, 0, 2, GUILayout.MaxWidth(64));
} else {
float speed = GUILayout.HorizontalSlider(m_skeletonAnimation.timeScale, 0, 2, GUILayout.MaxWidth(64));
//snap to nearest 0.25
float y = speed / 0.25f;
int q = Mathf.RoundToInt(y);
speed = q * 0.25f;
m_skeletonAnimation.timeScale = speed;
}
}
//TODO: Fix first-import error
//TODO: Update preview without thumbnail
public override Texture2D RenderStaticPreview (string assetPath, UnityEngine.Object[] subAssets, int width, int height) {
public override Texture2D RenderStaticPreview(string assetPath, UnityEngine.Object[] subAssets, int width, int height) {
Texture2D tex = new Texture2D(width, height, TextureFormat.ARGB32, false);
this.InitPreview();
if (this.m_previewUtility.m_Camera == null)
return null;
m_requireRefresh = true;
this.DoRenderPreview(false);
AdjustCameraGoals(false);
this.m_previewUtility.m_Camera.orthographicSize = m_orthoGoal / 2;
this.m_previewUtility.m_Camera.transform.position = m_posGoal;
this.m_previewUtility.BeginStaticPreview(new Rect(0, 0, width, height));
this.DoRenderPreview(false);
//TODO: Figure out why this is throwing errors on first attempt
// if(m_previewUtility != null){
// Handles.SetCamera(this.m_previewUtility.m_Camera);

View File

@ -33,7 +33,7 @@ using UnityEngine;
[CustomEditor(typeof(SkeletonRenderer))]
public class SkeletonRendererInspector : Editor {
protected SerializedProperty skeletonDataAsset, initialSkinName, normals, tangents, meshes, immutableTriangles;
protected SerializedProperty skeletonDataAsset, initialSkinName, normals, tangents, meshes, immutableTriangles, submeshSeparators;
protected virtual void OnEnable () {
skeletonDataAsset = serializedObject.FindProperty("skeletonDataAsset");
@ -42,6 +42,7 @@ public class SkeletonRendererInspector : Editor {
tangents = serializedObject.FindProperty("calculateTangents");
meshes = serializedObject.FindProperty("renderMeshes");
immutableTriangles = serializedObject.FindProperty("immutableTriangles");
submeshSeparators = serializedObject.FindProperty("submeshSeparators");
}
protected virtual void gui () {
@ -52,8 +53,11 @@ public class SkeletonRendererInspector : Editor {
float reloadWidth = GUI.skin.label.CalcSize(new GUIContent("Reload")).x + 20;
if (GUILayout.Button("Reload", GUILayout.Width(reloadWidth))) {
if (component.skeletonDataAsset != null) {
if (component.skeletonDataAsset.atlasAsset != null)
component.skeletonDataAsset.atlasAsset.Reset();
foreach (AtlasAsset aa in component.skeletonDataAsset.atlasAssets) {
if (aa != null)
aa.Reset();
}
component.skeletonDataAsset.Reset();
}
component.Reset();
@ -92,6 +96,7 @@ public class SkeletonRendererInspector : Editor {
new GUIContent("Immutable Triangles", "Enable to optimize rendering for skeletons that never change attachment visbility"));
EditorGUILayout.PropertyField(normals);
EditorGUILayout.PropertyField(tangents);
EditorGUILayout.PropertyField(submeshSeparators, true);
}
override public void OnInspectorGUI () {

View File

@ -0,0 +1,456 @@
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;
public struct SpineDrawerValuePair {
public string str;
public SerializedProperty property;
public SpineDrawerValuePair(string val, SerializedProperty property) {
this.str = val;
this.property = property;
}
}
[CustomPropertyDrawer(typeof(SpineSlot))]
public class SpineSlotDrawer : 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;
}
SpineSlot attrib = (SpineSlot)attribute;
var skeletonDataAssetProperty = property.serializedObject.FindProperty(attrib.dataSource);
if (skeletonDataAssetProperty != null) {
if (skeletonDataAssetProperty.objectReferenceValue is SkeletonDataAsset) {
skeletonDataAsset = (SkeletonDataAsset)skeletonDataAssetProperty.objectReferenceValue;
} else if (skeletonDataAssetProperty.objectReferenceValue is SkeletonRenderer) {
var renderer = (SkeletonRenderer)skeletonDataAssetProperty.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.GetComponent<SkeletonRenderer>() != null) {
var skeletonRenderer = component.GetComponent<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) {
SpineSlot attrib = (SpineSlot)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.Slots.Count; i++) {
string name = data.Slots[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(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 skeletonDataAssetProperty = property.serializedObject.FindProperty(attrib.dataSource);
if (skeletonDataAssetProperty != null) {
if (skeletonDataAssetProperty.objectReferenceValue is SkeletonDataAsset) {
skeletonDataAsset = (SkeletonDataAsset)skeletonDataAssetProperty.objectReferenceValue;
} else if (skeletonDataAssetProperty.objectReferenceValue is SkeletonRenderer) {
var renderer = (SkeletonRenderer)skeletonDataAssetProperty.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.GetComponent<SkeletonRenderer>() != null) {
var skeletonRenderer = component.GetComponent<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();
menu.AddDisabledItem(new GUIContent(skeletonDataAsset.name));
menu.AddSeparator("");
for (int i = 0; i < data.Skins.Count; i++) {
string name = data.Skins[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(SpineAtlasRegion))]
public class SpineAtlasRegionDrawer : PropertyDrawer {
Component component;
SerializedProperty atlasProp;
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;
}
component = (Component)property.serializedObject.targetObject;
if (component != null)
atlasProp = property.serializedObject.FindProperty("atlasAsset");
else
atlasProp = null;
if (atlasProp == null) {
EditorGUI.LabelField(position, "ERROR:", "Must have AtlasAsset variable!");
return;
} else if (atlasProp.objectReferenceValue == null) {
EditorGUI.LabelField(position, "ERROR:", "Atlas variable must not be null!");
return;
} else if (atlasProp.objectReferenceValue.GetType() != typeof(AtlasAsset)) {
EditorGUI.LabelField(position, "ERROR:", "Atlas variable must be of type AtlasAsset!");
}
position = EditorGUI.PrefixLabel(position, label);
if (GUI.Button(position, property.stringValue, EditorStyles.popup)) {
Selector(property);
}
}
void Selector(SerializedProperty property) {
GenericMenu menu = new GenericMenu();
AtlasAsset atlasAsset = (AtlasAsset)atlasProp.objectReferenceValue;
Atlas atlas = atlasAsset.GetAtlas();
FieldInfo field = typeof(Atlas).GetField("regions", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.NonPublic);
List<AtlasRegion> regions = (List<AtlasRegion>)field.GetValue(atlas);
for (int i = 0; i < regions.Count; i++) {
string name = regions[i].name;
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 : 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 skeletonDataAssetProperty = property.serializedObject.FindProperty(attrib.dataSource);
if (skeletonDataAssetProperty != null) {
if (skeletonDataAssetProperty.objectReferenceValue is SkeletonDataAsset) {
skeletonDataAsset = (SkeletonDataAsset)skeletonDataAssetProperty.objectReferenceValue;
} else if (skeletonDataAssetProperty.objectReferenceValue is SkeletonRenderer) {
var renderer = (SkeletonRenderer)skeletonDataAssetProperty.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.GetComponent<SkeletonRenderer>() != null) {
var skeletonRenderer = component.GetComponent<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[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 skeletonDataAssetProperty = property.serializedObject.FindProperty(attrib.dataSource);
if (skeletonDataAssetProperty != null) {
if (skeletonDataAssetProperty.objectReferenceValue is SkeletonDataAsset) {
skeletonDataAsset = (SkeletonDataAsset)skeletonDataAssetProperty.objectReferenceValue;
} else if (skeletonDataAssetProperty.objectReferenceValue is SkeletonRenderer) {
var renderer = (SkeletonRenderer)skeletonDataAssetProperty.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.GetComponent<SkeletonRenderer>() != null) {
skeletonRenderer = component.GetComponent<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 {
foreach (Skin skin in data.Skins) {
if (skin != null)
validSkins.Add(skin);
}
}
GenericMenu menu = new GenericMenu();
List<string> attachmentNames = 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[0];
SerializedProperty slotProperty = property.serializedObject.FindProperty(attrib.slotSource);
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[i].Name.ToLower().Contains(slotMatch) == false)
continue;
attachmentNames.Clear();
skin.FindNamesForSlot(i, attachmentNames);
if (skin != defaultSkin)
defaultSkin.FindNamesForSlot(i, attachmentNames);
for (int a = 0; a < attachmentNames.Count; a++) {
string attachmentPath = attachmentNames[a];
string menuPath = prefix + data.Slots[i].Name + "/" + attachmentPath;
string name = attachmentNames[a];
if (attrib.returnFullPath)
name = skin.Name + "/" + data.Slots[i].Name + "/" + attachmentPath;
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;
}
}

View File

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

View File

@ -39,6 +39,8 @@ using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Linq;
using System.Reflection;
using Spine;
[InitializeOnLoad]
@ -51,10 +53,12 @@ public class SpineEditorUtilities : AssetPostprocessor {
public static Texture2D poseBones;
public static Texture2D boneNib;
public static Texture2D slot;
public static Texture2D slotRoot;
public static Texture2D skinPlaceholder;
public static Texture2D image;
public static Texture2D boundingBox;
public static Texture2D mesh;
public static Texture2D weights;
public static Texture2D skin;
public static Texture2D skinsRoot;
public static Texture2D animation;
@ -78,7 +82,7 @@ public class SpineEditorUtilities : AssetPostprocessor {
new Vector3(0.1f, 0.1f, 0)
};
_boneMesh.uv = new Vector2[4];
_boneMesh.triangles = new int[6]{0,1,2,2,3,0};
_boneMesh.triangles = new int[6] { 0, 1, 2, 2, 3, 0 };
_boneMesh.RecalculateBounds();
_boneMesh.RecalculateNormals();
}
@ -108,17 +112,19 @@ public class SpineEditorUtilities : AssetPostprocessor {
internal static Material _boneMaterial;
public static void Initialize () {
public static void Initialize() {
skeleton = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-skeleton.png");
nullBone = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-null.png");
bone = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-bone.png");
poseBones = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-poseBones.png");
boneNib = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-boneNib.png");
slot = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-slot.png");
slotRoot = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-slotRoot.png");
skinPlaceholder = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-skinPlaceholder.png");
image = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-image.png");
boundingBox = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-boundingBox.png");
mesh = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-mesh.png");
weights = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-weights.png");
skin = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-skinPlaceholder.png");
skinsRoot = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-skinsRoot.png");
animation = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-animation.png");
@ -140,12 +146,12 @@ public class SpineEditorUtilities : AssetPostprocessor {
public static float defaultScale = 0.01f;
public static float defaultMix = 0.2f;
public static string defaultShader = "Spine/Skeleton";
static SpineEditorUtilities () {
static SpineEditorUtilities() {
DirectoryInfo rootDir = new DirectoryInfo(Application.dataPath);
FileInfo[] files = rootDir.GetFiles("SpineEditorUtilities.cs", SearchOption.AllDirectories);
editorPath = Path.GetDirectoryName(files[0].FullName.Replace("\\", "/").Replace(Application.dataPath, "Assets"));
editorGUIPath = editorPath + "/GUI";
editorGUIPath = editorPath + "/GUI";
Icons.Initialize();
@ -158,7 +164,7 @@ public class SpineEditorUtilities : AssetPostprocessor {
HierarchyWindowChanged();
}
static void HierarchyWindowChanged () {
static void HierarchyWindowChanged() {
skeletonRendererTable.Clear();
skeletonUtilityBoneTable.Clear();
@ -172,122 +178,299 @@ public class SpineEditorUtilities : AssetPostprocessor {
skeletonUtilityBoneTable.Add(b.gameObject.GetInstanceID(), b);
}
static void HierarchyWindowItemOnGUI (int instanceId, Rect selectionRect) {
static void HierarchyWindowItemOnGUI(int instanceId, Rect selectionRect) {
if (skeletonRendererTable.ContainsKey(instanceId)) {
Rect r = new Rect(selectionRect);
Rect r = new Rect(selectionRect);
r.x = r.width - 15;
r.width = 15;
GUI.Label(r, Icons.spine);
} else if (skeletonUtilityBoneTable.ContainsKey(instanceId)) {
Rect r = new Rect(selectionRect);
r.x -= 26;
Rect r = new Rect(selectionRect);
r.x -= 26;
if (skeletonUtilityBoneTable[instanceId] != null) {
if (skeletonUtilityBoneTable[instanceId].transform.childCount == 0)
r.x += 13;
r.y += 2;
if (skeletonUtilityBoneTable[instanceId] != null) {
if (skeletonUtilityBoneTable[instanceId].transform.childCount == 0)
r.x += 13;
r.width = 13;
r.height = 13;
r.y += 2;
if (skeletonUtilityBoneTable[instanceId].mode == SkeletonUtilityBone.Mode.Follow) {
GUI.DrawTexture(r, Icons.bone);
} else {
GUI.DrawTexture(r, Icons.poseBones);
}
r.width = 13;
r.height = 13;
if (skeletonUtilityBoneTable[instanceId].mode == SkeletonUtilityBone.Mode.Follow) {
GUI.DrawTexture(r, Icons.bone);
} else {
GUI.DrawTexture(r, Icons.poseBones);
}
}
}
[MenuItem("Assets/Spine/Ingest")]
static void IngestSpineProjectFromSelection () {
TextAsset spineJson = null;
TextAsset atlasText = null;
List<TextAsset> spineJsonList = new List<TextAsset>();
foreach (UnityEngine.Object o in Selection.objects) {
if (o.GetType() != typeof(TextAsset))
continue;
string fileName = Path.GetFileName(AssetDatabase.GetAssetPath(o));
if (fileName.EndsWith(".json"))
spineJson = (TextAsset)o;
else if (fileName.EndsWith(".atlas.txt"))
atlasText = (TextAsset)o;
}
if (spineJson == null) {
EditorUtility.DisplayDialog("Error!", "Spine JSON file not found in selection!", "OK");
return;
}
string primaryName = Path.GetFileNameWithoutExtension(spineJson.name);
string assetPath = Path.GetDirectoryName(AssetDatabase.GetAssetPath(spineJson));
if (atlasText == null) {
string atlasPath = assetPath + "/" + primaryName + ".atlas.txt";
atlasText = (TextAsset)AssetDatabase.LoadAssetAtPath(atlasPath, typeof(TextAsset));
}
AtlasAsset atlasAsset = IngestSpineAtlas(atlasText);
IngestSpineProject(spineJson, atlasAsset);
}
static void OnPostprocessAllAssets (string[] imported, string[] deleted, string[] moved, string[] movedFromAssetPaths) {
//debug
// return;
AtlasAsset sharedAtlas = null;
static void OnPostprocessAllAssets(string[] imported, string[] deleted, string[] moved, string[] movedFromAssetPaths) {
System.Array.Sort<string>(imported);
List<string> atlasPaths = new List<string>();
List<string> imagePaths = new List<string>();
List<string> skeletonPaths = new List<string>();
foreach (string str in imported) {
if (Path.GetExtension(str).ToLower() == ".json") {
TextAsset spineJson = (TextAsset)AssetDatabase.LoadAssetAtPath(str, typeof(TextAsset));
if (IsSpineJSON(spineJson)) {
if (sharedAtlas != null) {
string spinePath = Path.GetDirectoryName(AssetDatabase.GetAssetPath(spineJson));
string atlasPath = Path.GetDirectoryName(AssetDatabase.GetAssetPath(sharedAtlas));
if (spinePath != atlasPath)
sharedAtlas = null;
string extension = Path.GetExtension(str).ToLower();
switch (extension) {
case ".txt":
if (str.EndsWith(".atlas.txt")) {
atlasPaths.Add(str);
}
SkeletonDataAsset data = AutoIngestSpineProject(spineJson, sharedAtlas);
if (data == null)
continue;
sharedAtlas = data.atlasAsset;
string dir = Path.GetDirectoryName(Path.GetDirectoryName(AssetDatabase.GetAssetPath(data)));
string prefabPath = Path.Combine(dir, data.skeletonJSON.name + ".prefab").Replace("\\", "/");
if (File.Exists(prefabPath) == false) {
SkeletonAnimation anim = SpawnAnimatedSkeleton(data);
PrefabUtility.CreatePrefab(prefabPath, anim.gameObject, ReplacePrefabOptions.ReplaceNameBased);
if (EditorApplication.isPlaying)
GameObject.Destroy(anim.gameObject);
else
GameObject.DestroyImmediate(anim.gameObject);
} else {
break;
case ".png":
case ".jpg":
imagePaths.Add(str);
break;
case ".json":
TextAsset spineJson = (TextAsset)AssetDatabase.LoadAssetAtPath(str, typeof(TextAsset));
if (IsSpineJSON(spineJson)) {
skeletonPaths.Add(str);
}
break;
}
}
List<AtlasAsset> atlases = new List<AtlasAsset>();
//import atlases first
foreach (string ap in atlasPaths) {
TextAsset atlasText = (TextAsset)AssetDatabase.LoadAssetAtPath(ap, typeof(TextAsset));
AtlasAsset atlas = IngestSpineAtlas(atlasText);
atlases.Add(atlas);
}
//import skeletons and match them with atlases
bool abortSkeletonImport = false;
foreach (string sp in skeletonPaths) {
string dir = Path.GetDirectoryName(sp);
var localAtlases = FindAtlasesAtPath(dir);
var requiredPaths = GetRequiredAtlasRegions(sp);
var atlasMatch = GetMatchingAtlas(requiredPaths, localAtlases);
if (atlasMatch != null) {
IngestSpineProject(AssetDatabase.LoadAssetAtPath(sp, typeof(TextAsset)) as TextAsset, atlasMatch);
} else {
bool resolved = false;
while (!resolved) {
int result = EditorUtility.DisplayDialogComplex("Skeleton JSON Import Error!", "Could not find matching AtlasAsset for " + Path.GetFileNameWithoutExtension(sp), "Select", "Skip", "Abort");
switch (result) {
case -1:
Debug.Log("Select Atlas");
AtlasAsset selectedAtlas = GetAtlasDialog(Path.GetDirectoryName(sp));
if (selectedAtlas != null) {
localAtlases.Clear();
localAtlases.Add(selectedAtlas);
atlasMatch = GetMatchingAtlas(requiredPaths, localAtlases);
if (atlasMatch != null) {
resolved = true;
IngestSpineProject(AssetDatabase.LoadAssetAtPath(sp, typeof(TextAsset)) as TextAsset, atlasMatch);
}
}
break;
case 0:
var atlasList = MultiAtlasDialog(requiredPaths, Path.GetDirectoryName(sp), Path.GetFileNameWithoutExtension(sp));
if (atlasList != null)
IngestSpineProject(AssetDatabase.LoadAssetAtPath(sp, typeof(TextAsset)) as TextAsset, atlasList.ToArray());
resolved = true;
break;
case 1:
Debug.Log("Skipped importing: " + Path.GetFileName(sp));
resolved = true;
break;
case 2:
//abort
abortSkeletonImport = true;
resolved = true;
break;
}
}
}
if (abortSkeletonImport)
break;
}
//TODO: any post processing of images
}
static List<AtlasAsset> MultiAtlasDialog(List<string> requiredPaths, string initialDirectory, string header = "") {
List<AtlasAsset> atlasAssets = new List<AtlasAsset>();
bool resolved = false;
string lastAtlasPath = initialDirectory;
while (!resolved) {
StringBuilder sb = new StringBuilder();
sb.AppendLine(header);
sb.AppendLine("Atlases:");
if (atlasAssets.Count == 0) {
sb.AppendLine("\t--none--");
}
for (int i = 0; i < atlasAssets.Count; i++) {
sb.AppendLine("\t" + atlasAssets[i].name);
}
sb.AppendLine();
sb.AppendLine("Missing Regions:");
List<string> missingRegions = new List<string>(requiredPaths);
foreach (var atlasAsset in atlasAssets) {
var atlas = atlasAsset.GetAtlas();
for (int i = 0; i < missingRegions.Count; i++) {
if (atlas.FindRegion(missingRegions[i]) != null) {
missingRegions.RemoveAt(i);
i--;
}
}
}
if (missingRegions.Count == 0) {
break;
}
for (int i = 0; i < missingRegions.Count; i++) {
sb.AppendLine("\t" + missingRegions[i]);
}
int result = EditorUtility.DisplayDialogComplex("Atlas Selection", sb.ToString(), "Select", "Finish", "Abort");
switch(result){
case 0:
AtlasAsset selectedAtlasAsset = GetAtlasDialog(lastAtlasPath);
if (selectedAtlasAsset != null) {
var atlas = selectedAtlasAsset.GetAtlas();
bool hasValidRegion = false;
foreach (string str in missingRegions) {
if (atlas.FindRegion(str) != null) {
hasValidRegion = true;
break;
}
}
atlasAssets.Add(selectedAtlasAsset);
}
break;
case 1:
resolved = true;
break;
case 2:
atlasAssets = null;
resolved = true;
break;
}
}
return atlasAssets;
}
static AtlasAsset GetAtlasDialog(string dirPath) {
string path = EditorUtility.OpenFilePanel("Select AtlasAsset...", dirPath, "asset");
if (path == "")
return null;
int subLen = Application.dataPath.Length - 6;
string assetRelativePath = path.Substring(subLen, path.Length - subLen).Replace("\\", "/");
Object obj = AssetDatabase.LoadAssetAtPath(assetRelativePath, typeof(AtlasAsset));
if (obj == null || obj.GetType() != typeof(AtlasAsset))
return null;
return (AtlasAsset)obj;
}
public static List<string> GetRequiredAtlasRegions(string jsonPath) {
List<string> requiredPaths = new List<string>();
TextAsset spineJson = (TextAsset)AssetDatabase.LoadAssetAtPath(jsonPath, typeof(TextAsset));
StringReader reader = new StringReader(spineJson.text);
var root = Json.Deserialize(reader) as Dictionary<string, object>;
foreach (KeyValuePair<string, object> entry in (Dictionary<string, object>)root["skins"]) {
foreach (KeyValuePair<string, object> slotEntry in (Dictionary<string, object>)entry.Value) {
foreach (KeyValuePair<string, object> attachmentEntry in ((Dictionary<string, object>)slotEntry.Value)) {
var data = ((Dictionary<string, object>)attachmentEntry.Value);
if (data.ContainsKey("path"))
requiredPaths.Add((string)data["path"]);
else if (data.ContainsKey("name"))
requiredPaths.Add((string)data["name"]);
else
requiredPaths.Add(attachmentEntry.Key);
//requiredPaths.Add((string)sdf["path"]);
}
}
}
return requiredPaths;
}
static AtlasAsset GetMatchingAtlas(List<string> requiredPaths, List<AtlasAsset> atlasAssets) {
AtlasAsset atlasAssetMatch = null;
foreach (AtlasAsset a in atlasAssets) {
Atlas atlas = a.GetAtlas();
bool failed = false;
foreach (string regionPath in requiredPaths) {
if (atlas.FindRegion(regionPath) == null) {
failed = true;
break;
}
}
if (!failed) {
atlasAssetMatch = a;
break;
}
}
return atlasAssetMatch;
}
static bool IsSpineJSON (TextAsset asset) {
static List<AtlasAsset> FindAtlasesAtPath(string path) {
List<AtlasAsset> arr = new List<AtlasAsset>();
DirectoryInfo dir = new DirectoryInfo(path);
FileInfo[] assetInfoArr = dir.GetFiles("*.asset");
int subLen = Application.dataPath.Length - 6;
foreach (var f in assetInfoArr) {
string assetRelativePath = f.FullName.Substring(subLen, f.FullName.Length - subLen).Replace("\\", "/");
Object obj = AssetDatabase.LoadAssetAtPath(assetRelativePath, typeof(AtlasAsset));
if (obj != null) {
arr.Add(obj as AtlasAsset);
}
}
return arr;
}
public static bool IsSpineJSON(TextAsset asset) {
object obj = Json.Deserialize(new StringReader(asset.text));
if (obj == null) {
Debug.LogError("Is not valid JSON");
@ -307,51 +490,7 @@ public class SpineEditorUtilities : AssetPostprocessor {
return true;
}
static SkeletonDataAsset AutoIngestSpineProject (TextAsset spineJson, Object atlasSource = null) {
TextAsset atlasText = null;
AtlasAsset atlasAsset = null;
if (atlasSource != null) {
if (atlasSource.GetType() == typeof(TextAsset)) {
atlasText = (TextAsset)atlasSource;
} else if (atlasSource.GetType() == typeof(AtlasAsset)) {
atlasAsset = (AtlasAsset)atlasSource;
}
}
if (atlasText == null && atlasAsset == null) {
string primaryName = Path.GetFileNameWithoutExtension(spineJson.name);
string assetPath = Path.GetDirectoryName(AssetDatabase.GetAssetPath(spineJson));
if (atlasText == null) {
string atlasPath = assetPath + "/" + primaryName + ".atlas.txt";
atlasText = (TextAsset)AssetDatabase.LoadAssetAtPath(atlasPath, typeof(TextAsset));
if (atlasText == null) {
//can't find atlas, likely because using a shared atlas
bool abort = !EditorUtility.DisplayDialog("Atlas not Found", "Expecting " + spineJson.name + ".atlas\n" + "Press OK to select Atlas", "OK", "Abort");
if (abort) {
//do nothing, let it error later
} else {
string path = EditorUtility.OpenFilePanel("Find Atlas source...", Path.GetDirectoryName(Application.dataPath) + "/" + assetPath, "txt");
if (path != "") {
path = path.Replace("\\", "/");
path = path.Replace(Application.dataPath.Replace("\\", "/"), "Assets");
atlasText = (TextAsset)AssetDatabase.LoadAssetAtPath(path, typeof(TextAsset));
}
}
}
}
}
if (atlasAsset == null)
atlasAsset = IngestSpineAtlas(atlasText);
return IngestSpineProject(spineJson, atlasAsset);
}
static AtlasAsset IngestSpineAtlas (TextAsset atlasText) {
static AtlasAsset IngestSpineAtlas(TextAsset atlasText) {
if (atlasText == null) {
Debug.LogWarning("Atlas source cannot be null!");
return null;
@ -376,17 +515,17 @@ public class SpineEditorUtilities : AssetPostprocessor {
string[] atlasLines = atlasStr.Split('\n');
List<string> pageFiles = new List<string>();
for (int i = 0; i < atlasLines.Length-1; i++) {
for (int i = 0; i < atlasLines.Length - 1; i++) {
if (atlasLines[i].Length == 0)
pageFiles.Add(atlasLines[i + 1]);
}
atlasAsset.materials = new Material[pageFiles.Count];
for (int i = 0; i < pageFiles.Count; i++) {
string texturePath = assetPath + "/" + pageFiles[i];
Texture2D texture = (Texture2D)AssetDatabase.LoadAssetAtPath(texturePath, typeof(Texture2D));
TextureImporter texImporter = (TextureImporter)TextureImporter.GetAtPath(texturePath);
texImporter.textureFormat = TextureImporterFormat.AutomaticTruecolor;
texImporter.mipmapEnabled = false;
@ -396,13 +535,13 @@ public class SpineEditorUtilities : AssetPostprocessor {
EditorUtility.SetDirty(texImporter);
AssetDatabase.ImportAsset(texturePath);
AssetDatabase.SaveAssets();
string pageName = Path.GetFileNameWithoutExtension(pageFiles[i]);
//because this looks silly
if (pageName == primaryName && pageFiles.Count == 1)
pageName = "Material";
string materialPath = assetPath + "/" + primaryName + "_" + pageName + ".mat";
Material mat = (Material)AssetDatabase.LoadAssetAtPath(materialPath, typeof(Material));
@ -410,12 +549,12 @@ public class SpineEditorUtilities : AssetPostprocessor {
mat = new Material(Shader.Find(defaultShader));
AssetDatabase.CreateAsset(mat, materialPath);
}
mat.mainTexture = texture;
EditorUtility.SetDirty(mat);
AssetDatabase.SaveAssets();
atlasAsset.materials[i] = mat;
}
@ -429,40 +568,41 @@ public class SpineEditorUtilities : AssetPostprocessor {
return (AtlasAsset)AssetDatabase.LoadAssetAtPath(atlasPath, typeof(AtlasAsset));
}
static SkeletonDataAsset IngestSpineProject (TextAsset spineJson, AtlasAsset atlasAsset = null) {
static SkeletonDataAsset IngestSpineProject(TextAsset spineJson, params AtlasAsset[] atlasAssets) {
string primaryName = Path.GetFileNameWithoutExtension(spineJson.name);
string assetPath = Path.GetDirectoryName(AssetDatabase.GetAssetPath(spineJson));
string filePath = assetPath + "/" + primaryName + "_SkeletonData.asset";
if (spineJson != null && atlasAsset != null) {
if (spineJson != null && atlasAssets != null) {
SkeletonDataAsset skelDataAsset = (SkeletonDataAsset)AssetDatabase.LoadAssetAtPath(filePath, typeof(SkeletonDataAsset));
if (skelDataAsset == null) {
skelDataAsset = SkeletonDataAsset.CreateInstance<SkeletonDataAsset>();
skelDataAsset.atlasAsset = atlasAsset;
skelDataAsset.atlasAssets = atlasAssets;
skelDataAsset.skeletonJSON = spineJson;
skelDataAsset.fromAnimation = new string[0];
skelDataAsset.toAnimation = new string[0];
skelDataAsset.duration = new float[0];
skelDataAsset.defaultMix = defaultMix;
skelDataAsset.scale = defaultScale;
AssetDatabase.CreateAsset(skelDataAsset, filePath);
AssetDatabase.SaveAssets();
} else {
skelDataAsset.atlasAssets = atlasAssets;
skelDataAsset.Reset();
skelDataAsset.GetSkeletonData(true);
}
return skelDataAsset;
} else {
EditorUtility.DisplayDialog("Error!", "Must specify both Spine JSON and Atlas TextAsset", "OK");
EditorUtility.DisplayDialog("Error!", "Must specify both Spine JSON and AtlasAsset array", "OK");
return null;
}
}
[MenuItem("Assets/Spine/Spawn")]
static void SpawnAnimatedSkeleton () {
static void SpawnAnimatedSkeleton() {
Object[] arr = Selection.objects;
foreach (Object o in arr) {
string guid = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(o));
@ -474,59 +614,66 @@ public class SpineEditorUtilities : AssetPostprocessor {
}
[MenuItem("Assets/Spine/Spawn", true)]
static bool ValidateSpawnAnimatedSkeleton () {
static bool ValidateSpawnAnimatedSkeleton() {
Object[] arr = Selection.objects;
if (arr.Length == 0)
return false;
foreach (Object o in arr) {
if (o.GetType() != typeof(SkeletonDataAsset))
return false;
}
return true;
}
public static SkeletonAnimation SpawnAnimatedSkeleton (SkeletonDataAsset skeletonDataAsset, string skinName) {
public static SkeletonAnimation SpawnAnimatedSkeleton(SkeletonDataAsset skeletonDataAsset, string skinName) {
return SpawnAnimatedSkeleton(skeletonDataAsset, skeletonDataAsset.GetSkeletonData(true).FindSkin(skinName));
}
public static SkeletonAnimation SpawnAnimatedSkeleton (SkeletonDataAsset skeletonDataAsset, Skin skin = null) {
public static SkeletonAnimation SpawnAnimatedSkeleton(SkeletonDataAsset skeletonDataAsset, Skin skin = null) {
GameObject go = new GameObject(skeletonDataAsset.name.Replace("_SkeletonData", ""), typeof(MeshFilter), typeof(MeshRenderer), typeof(SkeletonAnimation));
SkeletonAnimation anim = go.GetComponent<SkeletonAnimation>();
anim.skeletonDataAsset = skeletonDataAsset;
bool requiresNormals = false;
foreach (Material m in anim.skeletonDataAsset.atlasAsset.materials) {
if (m.shader.name.Contains("Lit")) {
requiresNormals = true;
break;
foreach (AtlasAsset atlasAsset in anim.skeletonDataAsset.atlasAssets) {
foreach (Material m in atlasAsset.materials) {
if (m.shader.name.Contains("Lit")) {
requiresNormals = true;
break;
}
}
}
anim.calculateNormals = requiresNormals;
SkeletonData data = skeletonDataAsset.GetSkeletonData(true);
if (data == null) {
string reloadAtlasPath = AssetDatabase.GetAssetPath(skeletonDataAsset.atlasAsset);
skeletonDataAsset.atlasAsset = (AtlasAsset)AssetDatabase.LoadAssetAtPath(reloadAtlasPath, typeof(AtlasAsset));
for(int i = 0; i < skeletonDataAsset.atlasAssets.Length; i++){
string reloadAtlasPath = AssetDatabase.GetAssetPath(skeletonDataAsset.atlasAssets[i]);
skeletonDataAsset.atlasAssets[i] = (AtlasAsset)AssetDatabase.LoadAssetAtPath(reloadAtlasPath, typeof(AtlasAsset));
}
data = skeletonDataAsset.GetSkeletonData(true);
}
if (skin == null)
skin = data.DefaultSkin;
if (skin == null)
skin = data.Skins[0];
anim.Reset();
anim.skeleton.SetSkin(skin);
anim.initialSkinName = skin.Name;
anim.skeleton.Update(1);
anim.state.Update(1);
anim.state.Apply(anim.skeleton);
@ -534,4 +681,4 @@ public class SpineEditorUtilities : AssetPostprocessor {
return anim;
}
}
}

View File

@ -35,7 +35,7 @@ using UnityEngine;
using Spine;
public class SkeletonDataAsset : ScriptableObject {
public AtlasAsset atlasAsset;
public AtlasAsset[] atlasAssets;
public TextAsset skeletonJSON;
public float scale = 1;
public String[] fromAnimation;
@ -45,13 +45,14 @@ public class SkeletonDataAsset : ScriptableObject {
private SkeletonData skeletonData;
private AnimationStateData stateData;
public void Reset () {
public void Reset() {
skeletonData = null;
stateData = null;
}
public SkeletonData GetSkeletonData (bool quiet) {
if (atlasAsset == null) {
public SkeletonData GetSkeletonData(bool quiet) {
if (atlasAssets == null) {
atlasAssets = new AtlasAsset[0];
if (!quiet)
Debug.LogError("Atlas not set for SkeletonData asset: " + name, this);
Reset();
@ -65,16 +66,33 @@ public class SkeletonDataAsset : ScriptableObject {
return null;
}
Atlas atlas = atlasAsset.GetAtlas();
if (atlas == null) {
if (atlasAssets.Length == 0) {
Reset();
return null;
}
Atlas[] atlasArr = new Atlas[atlasAssets.Length];
for (int i = 0; i < atlasAssets.Length; i++) {
if (atlasAssets[i] == null) {
Reset();
return null;
}
atlasArr[i] = atlasAssets[i].GetAtlas();
if (atlasArr[i] == null) {
Reset();
return null;
}
}
if (skeletonData != null)
return skeletonData;
SkeletonJson json = new SkeletonJson(atlas);
SkeletonJson json = new SkeletonJson(atlasArr);
json.Scale = scale;
try {
skeletonData = json.ReadSkeletonData(new StringReader(skeletonJSON.text));
@ -95,7 +113,7 @@ public class SkeletonDataAsset : ScriptableObject {
return skeletonData;
}
public AnimationStateData GetAnimationStateData () {
public AnimationStateData GetAnimationStateData() {
if (stateData != null)
return stateData;
GetSkeletonData(false);

View File

@ -38,71 +38,71 @@ using System.Collections;
using Spine;
public static class SkeletonExtensions {
public static void SetColor (this Slot slot, Color color) {
public static void SetColor(this Slot slot, Color color) {
slot.A = color.a;
slot.R = color.r;
slot.G = color.g;
slot.B = color.b;
}
public static void SetColor (this Slot slot, Color32 color) {
public static void SetColor(this Slot slot, Color32 color) {
slot.A = color.a / 255f;
slot.R = color.r / 255f;
slot.G = color.g / 255f;
slot.B = color.b / 255f;
}
public static void SetColor (this RegionAttachment attachment, Color color) {
public static void SetColor(this RegionAttachment attachment, Color color) {
attachment.A = color.a;
attachment.R = color.r;
attachment.G = color.g;
attachment.B = color.b;
}
public static void SetColor (this RegionAttachment attachment, Color32 color) {
public static void SetColor(this RegionAttachment attachment, Color32 color) {
attachment.A = color.a / 255f;
attachment.R = color.r / 255f;
attachment.G = color.g / 255f;
attachment.B = color.b / 255f;
}
public static void SetColor (this MeshAttachment attachment, Color color) {
public static void SetColor(this MeshAttachment attachment, Color color) {
attachment.A = color.a;
attachment.R = color.r;
attachment.G = color.g;
attachment.B = color.b;
}
public static void SetColor (this MeshAttachment attachment, Color32 color) {
public static void SetColor(this MeshAttachment attachment, Color32 color) {
attachment.A = color.a / 255f;
attachment.R = color.r / 255f;
attachment.G = color.g / 255f;
attachment.B = color.b / 255f;
}
public static void SetColor (this SkinnedMeshAttachment attachment, Color color) {
public static void SetColor(this SkinnedMeshAttachment attachment, Color color) {
attachment.A = color.a;
attachment.R = color.r;
attachment.G = color.g;
attachment.B = color.b;
}
public static void SetColor (this SkinnedMeshAttachment attachment, Color32 color) {
public static void SetColor(this SkinnedMeshAttachment attachment, Color32 color) {
attachment.A = color.a / 255f;
attachment.R = color.r / 255f;
attachment.G = color.g / 255f;
attachment.B = color.b / 255f;
}
public static void SetPosition (this Bone bone, Vector2 position) {
public static void SetPosition(this Bone bone, Vector2 position) {
bone.X = position.x;
bone.Y = position.y;
}
public static void SetPosition (this Bone bone, Vector3 position) {
public static void SetPosition(this Bone bone, Vector3 position) {
bone.X = position.x;
bone.Y = position.y;
}
}
}

View File

@ -51,6 +51,14 @@ public class SkeletonRenderer : MonoBehaviour {
public float zSpacing;
public bool renderMeshes = true, immutableTriangles;
public bool logErrors = false;
[SpineSlot]
public string[] submeshSeparators = new string[0];
[HideInInspector]
public List<Slot> submeshSeparatorSlots = new List<Slot>();
private MeshFilter meshFilter;
private Mesh mesh1, mesh2;
private bool useMesh1;
@ -62,6 +70,7 @@ public class SkeletonRenderer : MonoBehaviour {
private Material[] sharedMaterials = new Material[0];
private readonly List<Material> submeshMaterials = new List<Material>();
private readonly List<Submesh> submeshes = new List<Submesh>();
public virtual void Reset () {
if (meshFilter != null)
@ -99,6 +108,12 @@ public class SkeletonRenderer : MonoBehaviour {
skeleton = new Skeleton(skeletonData);
if (initialSkinName != null && initialSkinName.Length > 0 && initialSkinName != "default")
skeleton.SetSkin(initialSkinName);
submeshSeparatorSlots.Clear();
for (int i = 0; i < submeshSeparators.Length; i++) {
submeshSeparatorSlots.Add(skeleton.FindSlot(submeshSeparators[i]));
}
if (OnReset != null)
OnReset(this);
}
@ -156,7 +171,8 @@ public class SkeletonRenderer : MonoBehaviour {
// Populate submesh when material changes.
Material material = (Material)((AtlasRegion)rendererObject).page.rendererObject;
if ((lastMaterial != material && lastMaterial != null) || slot.Data.name[0] == '*') {
if ((lastMaterial != material && lastMaterial != null) || submeshSeparatorSlots.Contains(slot)) {
AddSubmesh(lastMaterial, submeshStartSlotIndex, i, submeshTriangleCount, submeshFirstVertex, false);
submeshTriangleCount = 0;
submeshFirstVertex = vertexCount;

View File

@ -4,6 +4,7 @@ using System.Collections.Generic;
public class SkeletonUtilityKinematicShadow : MonoBehaviour {
public bool hideShadow = true;
public Transform parent;
Dictionary<Transform, Transform> shadowTable;
GameObject shadowRoot;
@ -12,7 +13,10 @@ public class SkeletonUtilityKinematicShadow : MonoBehaviour {
if (hideShadow)
shadowRoot.hideFlags = HideFlags.HideInHierarchy;
shadowRoot.transform.parent = transform.root;
if(parent == null)
shadowRoot.transform.parent = transform.root;
else
shadowRoot.transform.parent = parent;
shadowTable = new Dictionary<Transform, Transform>();

View File

@ -0,0 +1,111 @@
using UnityEngine;
using System.Collections;
public class SpineSlot : PropertyAttribute {
public string startsWith = "";
public string dataSource = "";
/// <summary>
///
/// </summary>
/// <param name="startsWith"></param>
/// <param name="dataSource">SerializedProperty name containing a reference to either a SkeletonRenderer or a SkeletonDataAsset</param>
public SpineSlot(string startsWith = "", string dataSource = "") {
this.startsWith = startsWith;
this.dataSource = dataSource;
}
}
public class SpineSkin : PropertyAttribute {
public string startsWith = "";
public string dataSource = "";
/// <summary>
///
/// </summary>
/// <param name="startsWith"></param>
/// <param name="dataSource">SerializedProperty name containing a reference to either a SkeletonRenderer or a SkeletonDataAsset</param>
public SpineSkin(string startsWith = "", string dataSource = "") {
this.startsWith = startsWith;
this.dataSource = dataSource;
}
}
public class SpineAtlasRegion : PropertyAttribute {
}
public class SpineAnimation : PropertyAttribute {
public string startsWith = "";
public string dataSource = "";
/// <summary>
///
/// </summary>
/// <param name="startsWith"></param>
/// <param name="dataSource">SerializedProperty name containing a reference to either a SkeletonRenderer or a SkeletonDataAsset</param>
public SpineAnimation(string startsWith = "", string dataSource = "") {
this.startsWith = startsWith;
this.dataSource = dataSource;
}
}
public class SpineAttachment : PropertyAttribute {
public bool returnFullPath;
public bool currentSkinOnly;
public string dataSource = "";
public string slotSource = "";
public SpineAttachment() {
}
public SpineAttachment(bool currentSkinOnly = true, bool returnFullPath = false, string slot = "", string dataSource = "") {
this.currentSkinOnly = currentSkinOnly;
this.returnFullPath = returnFullPath;
this.slotSource = slot;
this.dataSource = dataSource;
}
public static Hierarchy GetHierarchy(string fullPath) {
return new Hierarchy(fullPath);
}
public static Spine.Attachment GetAttachment(string fullPath, Spine.SkeletonData skeletonData) {
var hierarchy = SpineAttachment.GetHierarchy(fullPath);
if (hierarchy.name == "")
return null;
return skeletonData.FindSkin(hierarchy.skin).GetAttachment(skeletonData.FindSlotIndex(hierarchy.slot), hierarchy.name);
}
public static Spine.Attachment GetAttachment(string fullPath, SkeletonDataAsset skeletonDataAsset) {
return GetAttachment(fullPath, skeletonDataAsset.GetSkeletonData(true));
}
public struct Hierarchy {
public string skin;
public string slot;
public string name;
public Hierarchy(string fullPath) {
string[] chunks = fullPath.Split(new char[]{'/'}, System.StringSplitOptions.RemoveEmptyEntries);
if (chunks.Length == 0) {
skin = "";
slot = "";
name = "";
return;
}
else if (chunks.Length < 2) {
throw new System.Exception("Cannot generate Attachment Hierarchy from string! Not enough components! [" + fullPath + "]");
}
skin = chunks[0];
slot = chunks[1];
name = "";
for (int i = 2; i < chunks.Length; i++) {
name += chunks[i];
}
}
}
}

View File

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