Advertisement
Guest User

Untitled

a guest
Jun 26th, 2017
79
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 35.17 KB | None | 0 0
  1. /******************************************************************************
  2. * Spine Runtimes Software License v2.5
  3. *
  4. * Copyright (c) 2013-2016, Esoteric Software
  5. * All rights reserved.
  6. *
  7. * You are granted a perpetual, non-exclusive, non-sublicensable, and
  8. * non-transferable license to use, install, execute, and perform the Spine
  9. * Runtimes software and derivative works solely for personal or internal
  10. * use. Without the written permission of Esoteric Software (see Section 2 of
  11. * the Spine Software License Agreement), you may not (a) modify, translate,
  12. * adapt, or develop new applications using the Spine Runtimes or otherwise
  13. * create derivative works or improvements of the Spine Runtimes or (b) remove,
  14. * delete, alter, or obscure any trademarks or any copyright, trademark, patent,
  15. * or other intellectual property or proprietary rights notices on or in the
  16. * Software, including any copy thereof. Redistributions in binary or source
  17. * form must include this license and terms.
  18. *
  19. * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR
  20. * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
  21. * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
  22. * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  23. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  24. * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF
  25. * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
  26. * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  27. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  28. * POSSIBILITY OF SUCH DAMAGE.
  29. *****************************************************************************/
  30.  
  31. // Contributed by: Mitch Thompson
  32.  
  33. #define SPINE_SKELETON_ANIMATOR
  34. //#define SPINE_BAKING
  35.  
  36. using System;
  37. using System.Collections.Generic;
  38. using UnityEditor;
  39. using UnityEngine;
  40. using Spine;
  41.  
  42. namespace Spine.Unity.Editor {
  43. using Event = UnityEngine.Event;
  44. using Icons = SpineEditorUtilities.Icons;
  45.  
  46. [CustomEditor(typeof(SkeletonDataAsset)), CanEditMultipleObjects]
  47. public class SkeletonDataAssetInspector : UnityEditor.Editor {
  48. static bool showAnimationStateData = true;
  49. static bool showAnimationList = true;
  50. static bool showSlotList = false;
  51. static bool showAttachments = false;
  52.  
  53. #if SPINE_BAKING
  54. static bool isBakingExpanded = false;
  55. static bool bakeAnimations = true;
  56. static bool bakeIK = true;
  57. static SendMessageOptions bakeEventOptions = SendMessageOptions.DontRequireReceiver;
  58. const string ShowBakingPrefsKey = "SkeletonDataAssetInspector_showUnity";
  59. #endif
  60.  
  61. SerializedProperty atlasAssets, skeletonJSON, scale, fromAnimation, toAnimation, duration, defaultMix;
  62. #if SPINE_TK2D
  63. SerializedProperty spriteCollection;
  64. #endif
  65.  
  66. #if SPINE_SKELETON_ANIMATOR
  67. static bool isMecanimExpanded = false;
  68. SerializedProperty controller;
  69. #endif
  70.  
  71. bool m_initialized = false;
  72. SkeletonDataAsset m_skeletonDataAsset;
  73. SkeletonData m_skeletonData;
  74. string m_skeletonDataAssetGUID;
  75. bool needToSerialize;
  76.  
  77. readonly List<string> warnings = new List<string>();
  78.  
  79. GUIStyle activePlayButtonStyle, idlePlayButtonStyle;
  80. readonly GUIContent DefaultMixLabel = new GUIContent("Default Mix Duration", "Sets 'SkeletonDataAsset.defaultMix' in the asset and 'AnimationState.data.defaultMix' at runtime load time.");
  81.  
  82. void OnEnable () {
  83. SpineEditorUtilities.ConfirmInitialization();
  84. m_skeletonDataAsset = (SkeletonDataAsset)target;
  85.  
  86. atlasAssets = serializedObject.FindProperty("atlasAssets");
  87. skeletonJSON = serializedObject.FindProperty("skeletonJSON");
  88. scale = serializedObject.FindProperty("scale");
  89. fromAnimation = serializedObject.FindProperty("fromAnimation");
  90. toAnimation = serializedObject.FindProperty("toAnimation");
  91. duration = serializedObject.FindProperty("duration");
  92. defaultMix = serializedObject.FindProperty("defaultMix");
  93.  
  94. #if SPINE_SKELETON_ANIMATOR
  95. controller = serializedObject.FindProperty("controller");
  96. #endif
  97.  
  98. #if SPINE_TK2D
  99. atlasAssets.isExpanded = false;
  100. spriteCollection = serializedObject.FindProperty("spriteCollection");
  101. #else
  102. atlasAssets.isExpanded = true;
  103. #endif
  104.  
  105. #if SPINE_BAKING
  106. isBakingExpanded = EditorPrefs.GetBool(ShowBakingPrefsKey, false);
  107. #endif
  108.  
  109. m_skeletonDataAssetGUID = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(m_skeletonDataAsset));
  110. EditorApplication.update += EditorUpdate;
  111. RepopulateWarnings();
  112. m_skeletonData = warnings.Count == 0 ? m_skeletonDataAsset.GetSkeletonData(false) : null;
  113. }
  114.  
  115. void OnDestroy () {
  116. m_initialized = false;
  117. EditorApplication.update -= EditorUpdate;
  118. this.DestroyPreviewInstances();
  119. if (this.m_previewUtility != null) {
  120. this.m_previewUtility.Cleanup();
  121. this.m_previewUtility = null;
  122. }
  123. }
  124.  
  125. override public void OnInspectorGUI () {
  126. if (serializedObject.isEditingMultipleObjects) {
  127. using (new SpineInspectorUtility.BoxScope()) {
  128. EditorGUILayout.LabelField("SkeletonData", EditorStyles.boldLabel);
  129. EditorGUILayout.PropertyField(skeletonJSON, new GUIContent(skeletonJSON.displayName, Icons.spine));
  130. EditorGUILayout.PropertyField(scale);
  131. }
  132.  
  133. using (new SpineInspectorUtility.BoxScope()) {
  134. EditorGUILayout.LabelField("Atlas", EditorStyles.boldLabel);
  135. #if !SPINE_TK2D
  136. EditorGUILayout.PropertyField(atlasAssets, true);
  137. #else
  138. using (new EditorGUI.DisabledGroupScope(spriteCollection.objectReferenceValue != null)) {
  139. EditorGUILayout.PropertyField(atlasAssets, true);
  140. }
  141. EditorGUILayout.LabelField("spine-tk2d", EditorStyles.boldLabel);
  142. EditorGUILayout.PropertyField(spriteCollection, true);
  143. #endif
  144. }
  145.  
  146. using (new SpineInspectorUtility.BoxScope()) {
  147. EditorGUILayout.LabelField("Mix Settings", EditorStyles.boldLabel);
  148. SpineInspectorUtility.PropertyFieldWideLabel(defaultMix, DefaultMixLabel, 160);
  149. EditorGUILayout.Space();
  150. }
  151. return;
  152. }
  153.  
  154. {
  155. // Lazy initialization because accessing EditorStyles values in OnEnable during a recompile causes UnityEditor to throw null exceptions. (Unity 5.3.5)
  156. idlePlayButtonStyle = idlePlayButtonStyle ?? new GUIStyle(EditorStyles.miniButton);
  157. if (activePlayButtonStyle == null) {
  158. activePlayButtonStyle = new GUIStyle(idlePlayButtonStyle);
  159. activePlayButtonStyle.normal.textColor = Color.red;
  160. }
  161. }
  162.  
  163. serializedObject.Update();
  164.  
  165. EditorGUILayout.LabelField(new GUIContent(target.name + " (SkeletonDataAsset)", Icons.spine), EditorStyles.whiteLargeLabel);
  166. if (m_skeletonData != null) {
  167. EditorGUILayout.LabelField("(Drag and Drop to instantiate.)", EditorStyles.miniLabel);
  168. }
  169.  
  170. EditorGUI.BeginChangeCheck();
  171.  
  172. // SkeletonData
  173. using (new SpineInspectorUtility.BoxScope()) {
  174. using (new EditorGUILayout.HorizontalScope()) {
  175. EditorGUILayout.LabelField("SkeletonData", EditorStyles.boldLabel);
  176. // if (m_skeletonData != null) {
  177. // var sd = m_skeletonData;
  178. // string m = string.Format("{8} - {0} {1}\nBones: {2}\tConstraints: {5} IK + {6} Path + {7} Transform\nSlots: {3}\t\tSkins: {4}\n",
  179. // sd.Version, string.IsNullOrEmpty(sd.Version) ? "" : "export", sd.Bones.Count, sd.Slots.Count, sd.Skins.Count, sd.IkConstraints.Count, sd.PathConstraints.Count, sd.TransformConstraints.Count, skeletonJSON.objectReferenceValue.name);
  180. // EditorGUILayout.LabelField(new GUIContent("SkeletonData"), new GUIContent("+", m), EditorStyles.boldLabel);
  181. // }
  182. }
  183.  
  184. EditorGUILayout.PropertyField(skeletonJSON, new GUIContent(skeletonJSON.displayName, Icons.spine));
  185. EditorGUILayout.PropertyField(scale);
  186. }
  187.  
  188. // if (m_skeletonData != null) {
  189. // if (SpineInspectorUtility.CenteredButton(new GUIContent("Instantiate", Icons.spine, "Creates a new Spine GameObject in the active scene using this Skeleton Data.\nYou can also instantiate by dragging the SkeletonData asset from Project view into Scene View.")))
  190. // SpineEditorUtilities.ShowInstantiateContextMenu(this.m_skeletonDataAsset, Vector3.zero);
  191. // }
  192.  
  193. // Atlas
  194. using (new SpineInspectorUtility.BoxScope()) {
  195. EditorGUILayout.LabelField("Atlas", EditorStyles.boldLabel);
  196. #if !SPINE_TK2D
  197. EditorGUILayout.PropertyField(atlasAssets, true);
  198. #else
  199. using (new EditorGUI.DisabledGroupScope(spriteCollection.objectReferenceValue != null)) {
  200. EditorGUILayout.PropertyField(atlasAssets, true);
  201. }
  202. EditorGUILayout.LabelField("spine-tk2d", EditorStyles.boldLabel);
  203. EditorGUILayout.PropertyField(spriteCollection, true);
  204. #endif
  205. }
  206.  
  207. if (EditorGUI.EndChangeCheck()) {
  208. if (serializedObject.ApplyModifiedProperties()) {
  209. if (m_previewUtility != null) {
  210. m_previewUtility.Cleanup();
  211. m_previewUtility = null;
  212. }
  213. m_skeletonDataAsset.Clear();
  214. m_skeletonData = null;
  215. OnEnable(); // Should call RepopulateWarnings.
  216. return;
  217. }
  218. }
  219.  
  220. // Some code depends on the existence of m_skeletonAnimation instance.
  221. // If m_skeletonAnimation is lazy-instantiated elsewhere, this can cause contents to change between Layout and Repaint events, causing GUILayout control count errors.
  222. InitPreview();
  223. if (m_skeletonData != null) {
  224. GUILayout.Space(20f);
  225.  
  226. using (new SpineInspectorUtility.BoxScope()) {
  227. EditorGUILayout.LabelField("Mix Settings", EditorStyles.boldLabel);
  228. DrawAnimationStateInfo();
  229. EditorGUILayout.Space();
  230. }
  231.  
  232. EditorGUILayout.LabelField("Preview", EditorStyles.boldLabel);
  233. DrawAnimationList();
  234. EditorGUILayout.Space();
  235. DrawSlotList();
  236. EditorGUILayout.Space();
  237. DrawUnityTools();
  238. } else {
  239. #if !SPINE_TK2D
  240. // Reimport Button
  241. using (new EditorGUI.DisabledGroupScope(skeletonJSON.objectReferenceValue == null)) {
  242. if (GUILayout.Button(new GUIContent("Attempt Reimport", Icons.warning))) {
  243. DoReimport();
  244. }
  245. }
  246. #else
  247. EditorGUILayout.HelpBox("Couldn't load SkeletonData.", MessageType.Error);
  248. #endif
  249.  
  250. // List warnings.
  251. foreach (var line in warnings)
  252. EditorGUILayout.LabelField(new GUIContent(line, Icons.warning));
  253. }
  254.  
  255. if (!Application.isPlaying)
  256. serializedObject.ApplyModifiedProperties();
  257. }
  258.  
  259. void DrawUnityTools () {
  260. #if SPINE_SKELETON_ANIMATOR
  261. using (new SpineInspectorUtility.BoxScope()) {
  262. isMecanimExpanded = EditorGUILayout.Foldout(isMecanimExpanded, new GUIContent("SkeletonAnimator", Icons.unityIcon));
  263. if (isMecanimExpanded) {
  264. EditorGUI.indentLevel++;
  265. EditorGUILayout.PropertyField(controller, new GUIContent("Controller", Icons.controllerIcon));
  266. if (controller.objectReferenceValue == null) {
  267.  
  268. // Generate Mecanim Controller Button
  269. using (new GUILayout.HorizontalScope()) {
  270. GUILayout.Space(EditorGUIUtility.labelWidth);
  271. if (GUILayout.Button(new GUIContent("Generate Mecanim Controller"), GUILayout.Height(20)))
  272. SkeletonBaker.GenerateMecanimAnimationClips(m_skeletonDataAsset);
  273. }
  274. EditorGUILayout.HelpBox("SkeletonAnimator is the Mecanim alternative to SkeletonAnimation.\nIt is not required.", MessageType.Info);
  275.  
  276. } else {
  277.  
  278. // Update AnimationClips button.
  279. using (new GUILayout.HorizontalScope()) {
  280. GUILayout.Space(EditorGUIUtility.labelWidth);
  281. if (GUILayout.Button(new GUIContent("Force Update AnimationClips"), GUILayout.Height(20)))
  282. SkeletonBaker.GenerateMecanimAnimationClips(m_skeletonDataAsset);
  283. }
  284.  
  285. }
  286. EditorGUI.indentLevel--;
  287. }
  288. }
  289. #endif
  290.  
  291. #if SPINE_BAKING
  292. bool pre = isBakingExpanded;
  293. isBakingExpanded = EditorGUILayout.Foldout(isBakingExpanded, new GUIContent("Baking", Icons.unityIcon));
  294. if (pre != isBakingExpanded)
  295. EditorPrefs.SetBool(ShowBakingPrefsKey, isBakingExpanded);
  296.  
  297. if (isBakingExpanded) {
  298. EditorGUI.indentLevel++;
  299. const string BakingWarningMessage =
  300. // "WARNING!" +
  301. // "\nBaking is NOT the same as SkeletonAnimator!" +
  302. // "\n\n" +
  303. "The main use of Baking is to export Spine projects to be used without the Spine Runtime (ie: for sale on the Asset Store, or background objects that are animated only with a wind noise generator)" +
  304.  
  305. "\n\nBaking does not support the following:" +
  306. "\n\tDisabled transform inheritance" +
  307. "\n\tShear" +
  308. "\n\tColor Keys" +
  309. "\n\tDraw Order Keys" +
  310. "\n\tAll Constraint types" +
  311.  
  312. "\n\nCurves are sampled at 60fps and are not realtime." +
  313. "\nPlease read SkeletonBaker.cs comments for full details.";
  314. EditorGUILayout.HelpBox(BakingWarningMessage, MessageType.Info, true);
  315.  
  316. EditorGUI.indentLevel++;
  317. bakeAnimations = EditorGUILayout.Toggle("Bake Animations", bakeAnimations);
  318. using (new EditorGUI.DisabledGroupScope(!bakeAnimations)) {
  319. EditorGUI.indentLevel++;
  320. bakeIK = EditorGUILayout.Toggle("Bake IK", bakeIK);
  321. bakeEventOptions = (SendMessageOptions)EditorGUILayout.EnumPopup("Event Options", bakeEventOptions);
  322. EditorGUI.indentLevel--;
  323. }
  324.  
  325. // Bake Skin buttons.
  326. using (new GUILayout.HorizontalScope()) {
  327. if (GUILayout.Button(new GUIContent("Bake All Skins", Icons.unityIcon), GUILayout.Height(32), GUILayout.Width(150)))
  328. SkeletonBaker.BakeToPrefab(m_skeletonDataAsset, m_skeletonData.Skins, "", bakeAnimations, bakeIK, bakeEventOptions);
  329.  
  330. if (m_skeletonAnimation != null && m_skeletonAnimation.skeleton != null) {
  331. Skin bakeSkin = m_skeletonAnimation.skeleton.Skin;
  332.  
  333. string skinName = "<No Skin>";
  334. if (bakeSkin == null) {
  335. skinName = "Default";
  336. bakeSkin = m_skeletonData.Skins.Items[0];
  337. } else
  338. skinName = m_skeletonAnimation.skeleton.Skin.Name;
  339.  
  340. using (new GUILayout.VerticalScope()) {
  341. if (GUILayout.Button(new GUIContent("Bake \"" + skinName + "\"", Icons.unityIcon), GUILayout.Height(32), GUILayout.Width(250)))
  342. SkeletonBaker.BakeToPrefab(m_skeletonDataAsset, new ExposedList<Skin>(new [] { bakeSkin }), "", bakeAnimations, bakeIK, bakeEventOptions);
  343. using (new GUILayout.HorizontalScope()) {
  344. GUILayout.Label(new GUIContent("Skins", Icons.skinsRoot), GUILayout.Width(50));
  345. if (GUILayout.Button(skinName, EditorStyles.popup, GUILayout.Width(196))) {
  346. DrawSkinDropdown();
  347. }
  348. }
  349. }
  350. }
  351. }
  352.  
  353. EditorGUI.indentLevel--;
  354. EditorGUI.indentLevel--;
  355. }
  356. #endif
  357. }
  358.  
  359. void DoReimport () {
  360. SpineEditorUtilities.ImportSpineContent(new string[] { AssetDatabase.GetAssetPath(skeletonJSON.objectReferenceValue) }, true);
  361. if (m_previewUtility != null) {
  362. m_previewUtility.Cleanup();
  363. m_previewUtility = null;
  364. }
  365.  
  366. OnEnable(); // Should call RepopulateWarnings.
  367. EditorUtility.SetDirty(m_skeletonDataAsset);
  368. }
  369.  
  370. void DrawAnimationStateInfo () {
  371. showAnimationStateData = EditorGUILayout.Foldout(showAnimationStateData, "Animation State Data");
  372. if (!showAnimationStateData)
  373. return;
  374.  
  375. EditorGUI.BeginChangeCheck();
  376. SpineInspectorUtility.PropertyFieldWideLabel(defaultMix, DefaultMixLabel, 160);
  377.  
  378. var animations = new string[m_skeletonData.Animations.Count];
  379. for (int i = 0; i < animations.Length; i++)
  380. animations[i] = m_skeletonData.Animations.Items[i].Name;
  381.  
  382. for (int i = 0; i < fromAnimation.arraySize; i++) {
  383. SerializedProperty from = fromAnimation.GetArrayElementAtIndex(i);
  384. SerializedProperty to = toAnimation.GetArrayElementAtIndex(i);
  385. SerializedProperty durationProp = duration.GetArrayElementAtIndex(i);
  386. using (new EditorGUILayout.HorizontalScope()) {
  387. from.stringValue = animations[EditorGUILayout.Popup(Math.Max(Array.IndexOf(animations, from.stringValue), 0), animations)];
  388. to.stringValue = animations[EditorGUILayout.Popup(Math.Max(Array.IndexOf(animations, to.stringValue), 0), animations)];
  389. durationProp.floatValue = EditorGUILayout.FloatField(durationProp.floatValue);
  390. if (GUILayout.Button("Delete")) {
  391. duration.DeleteArrayElementAtIndex(i);
  392. toAnimation.DeleteArrayElementAtIndex(i);
  393. fromAnimation.DeleteArrayElementAtIndex(i);
  394. }
  395. }
  396. }
  397.  
  398. using (new EditorGUILayout.HorizontalScope()) {
  399. EditorGUILayout.Space();
  400. if (GUILayout.Button("Add Mix")) {
  401. duration.arraySize++;
  402. toAnimation.arraySize++;
  403. fromAnimation.arraySize++;
  404. }
  405. EditorGUILayout.Space();
  406. }
  407.  
  408. if (EditorGUI.EndChangeCheck()) {
  409. m_skeletonDataAsset.FillStateData();
  410. EditorUtility.SetDirty(m_skeletonDataAsset);
  411. serializedObject.ApplyModifiedProperties();
  412. needToSerialize = true;
  413. }
  414. }
  415.  
  416. void DrawAnimationList () {
  417. showAnimationList = EditorGUILayout.Foldout(showAnimationList, new GUIContent(string.Format("Animations [{0}]", m_skeletonData.Animations.Count), Icons.animationRoot));
  418. if (!showAnimationList)
  419. return;
  420.  
  421. if (m_skeletonAnimation != null && m_skeletonAnimation.state != null) {
  422. if (GUILayout.Button(new GUIContent("Setup Pose", Icons.skeleton), GUILayout.Width(105), GUILayout.Height(18))) {
  423. StopAnimation();
  424. m_skeletonAnimation.skeleton.SetToSetupPose();
  425. m_requireRefresh = true;
  426. }
  427. } else {
  428. EditorGUILayout.HelpBox("Animations can be previewed if you expand the Preview window below.", MessageType.Info);
  429. }
  430.  
  431. EditorGUILayout.LabelField("Name", "Duration");
  432. foreach (Spine.Animation animation in m_skeletonData.Animations) {
  433. using (new GUILayout.HorizontalScope()) {
  434. if (m_skeletonAnimation != null && m_skeletonAnimation.state != null) {
  435. var activeTrack = m_skeletonAnimation.state.GetCurrent(0);
  436. if (activeTrack != null && activeTrack.Animation == animation) {
  437. if (GUILayout.Button("\u25BA", activePlayButtonStyle, GUILayout.Width(24))) {
  438. StopAnimation();
  439. }
  440. } else {
  441. if (GUILayout.Button("\u25BA", idlePlayButtonStyle, GUILayout.Width(24))) {
  442. PlayAnimation(animation.Name, true);
  443. }
  444. }
  445. } else {
  446. GUILayout.Label("-", GUILayout.Width(24));
  447. }
  448. EditorGUILayout.LabelField(new GUIContent(animation.Name, Icons.animation), new GUIContent(animation.Duration.ToString("f3") + "s" + ("(" + (Mathf.RoundToInt(animation.Duration * 30)) + ")").PadLeft(12, ' ')));
  449. }
  450. }
  451. }
  452.  
  453. void DrawSlotList () {
  454. showSlotList = EditorGUILayout.Foldout(showSlotList, new GUIContent("Slots", Icons.slotRoot));
  455.  
  456. if (!showSlotList) return;
  457. if (m_skeletonAnimation == null || m_skeletonAnimation.skeleton == null) return;
  458.  
  459. EditorGUI.indentLevel++;
  460. showAttachments = EditorGUILayout.ToggleLeft("Show Attachments", showAttachments);
  461. var slotAttachments = new List<Attachment>();
  462. var slotAttachmentNames = new List<string>();
  463. var defaultSkinAttachmentNames = new List<string>();
  464. var defaultSkin = m_skeletonData.Skins.Items[0];
  465. Skin skin = m_skeletonAnimation.skeleton.Skin ?? defaultSkin;
  466. var slotsItems = m_skeletonAnimation.skeleton.Slots.Items;
  467.  
  468. for (int i = m_skeletonAnimation.skeleton.Slots.Count - 1; i >= 0; i--) {
  469. Slot slot = slotsItems[i];
  470. EditorGUILayout.LabelField(new GUIContent(slot.Data.Name, Icons.slot));
  471. if (showAttachments) {
  472.  
  473. EditorGUI.indentLevel++;
  474. slotAttachments.Clear();
  475. slotAttachmentNames.Clear();
  476. defaultSkinAttachmentNames.Clear();
  477.  
  478. skin.FindNamesForSlot(i, slotAttachmentNames);
  479. skin.FindAttachmentsForSlot(i, slotAttachments);
  480.  
  481. if (skin != defaultSkin) {
  482. defaultSkin.FindNamesForSlot(i, defaultSkinAttachmentNames);
  483. defaultSkin.FindNamesForSlot(i, slotAttachmentNames);
  484. defaultSkin.FindAttachmentsForSlot(i, slotAttachments);
  485. } else {
  486. defaultSkin.FindNamesForSlot(i, defaultSkinAttachmentNames);
  487. }
  488.  
  489. for (int a = 0; a < slotAttachments.Count; a++) {
  490. Attachment attachment = slotAttachments[a];
  491. string attachmentName = slotAttachmentNames[a];
  492. Texture2D icon = Icons.GetAttachmentIcon(attachment);
  493. bool initialState = slot.Attachment == attachment;
  494. bool toggled = EditorGUILayout.ToggleLeft(new GUIContent(attachmentName, icon), slot.Attachment == attachment);
  495.  
  496. if (!defaultSkinAttachmentNames.Contains(attachmentName)) {
  497. Rect skinPlaceHolderIconRect = GUILayoutUtility.GetLastRect();
  498. skinPlaceHolderIconRect.width = Icons.skinPlaceholder.width;
  499. skinPlaceHolderIconRect.height = Icons.skinPlaceholder.height;
  500. GUI.DrawTexture(skinPlaceHolderIconRect, Icons.skinPlaceholder);
  501. }
  502.  
  503. if (toggled != initialState) {
  504. slot.Attachment = toggled ? attachment : null;
  505. m_requireRefresh = true;
  506. }
  507. }
  508. EditorGUI.indentLevel--;
  509. }
  510. }
  511. EditorGUI.indentLevel--;
  512. }
  513.  
  514. void RepopulateWarnings () {
  515. warnings.Clear();
  516.  
  517. // Clear null entries.
  518. {
  519. bool hasNulls = false;
  520. foreach (var a in m_skeletonDataAsset.atlasAssets) {
  521. if (a == null) {
  522. hasNulls = true;
  523. break;
  524. }
  525. }
  526. if (hasNulls) {
  527. var trimmedAtlasAssets = new List<AtlasAsset>();
  528. foreach (var a in m_skeletonDataAsset.atlasAssets) {
  529. if (a != null) trimmedAtlasAssets.Add(a);
  530. }
  531. m_skeletonDataAsset.atlasAssets = trimmedAtlasAssets.ToArray();
  532. }
  533. serializedObject.Update();
  534. }
  535.  
  536. if (skeletonJSON.objectReferenceValue == null) {
  537. warnings.Add("Missing Skeleton JSON");
  538. } else {
  539. if (SpineEditorUtilities.IsSpineData((TextAsset)skeletonJSON.objectReferenceValue) == false) {
  540. warnings.Add("Skeleton data file is not a valid JSON or binary file.");
  541. } else {
  542. #if SPINE_TK2D
  543. bool searchForSpineAtlasAssets = true;
  544. bool isSpriteCollectionNull = spriteCollection.objectReferenceValue == null;
  545. if (!isSpriteCollectionNull) searchForSpineAtlasAssets = false;
  546. #else
  547. const bool searchForSpineAtlasAssets = true;
  548. #endif
  549.  
  550. if (searchForSpineAtlasAssets) {
  551. bool detectedNullAtlasEntry = false;
  552. var atlasList = new List<Atlas>();
  553. var actualAtlasAssets = m_skeletonDataAsset.atlasAssets;
  554.  
  555. for (int i = 0; i < actualAtlasAssets.Length; i++) {
  556. if (m_skeletonDataAsset.atlasAssets[i] == null) {
  557. detectedNullAtlasEntry = true;
  558. break;
  559. } else {
  560. atlasList.Add(actualAtlasAssets[i].GetAtlas());
  561. }
  562. }
  563.  
  564. if (detectedNullAtlasEntry) {
  565. warnings.Add("AtlasAsset elements should not be null.");
  566. } else {
  567. // Get requirements.
  568. var missingPaths = SpineEditorUtilities.GetRequiredAtlasRegions(AssetDatabase.GetAssetPath((TextAsset)skeletonJSON.objectReferenceValue));
  569.  
  570. foreach (var atlas in atlasList) {
  571. for (int i = 0; i < missingPaths.Count; i++) {
  572. if (atlas.FindRegion(missingPaths[i]) != null) {
  573. missingPaths.RemoveAt(i);
  574. i--;
  575. }
  576. }
  577. }
  578.  
  579. #if SPINE_TK2D
  580. if (missingPaths.Count > 0)
  581. warnings.Add("Missing regions. SkeletonDataAsset requires tk2DSpriteCollectionData or Spine AtlasAssets.");
  582. #endif
  583.  
  584. foreach (var str in missingPaths)
  585. warnings.Add("Missing Region: '" + str + "'");
  586.  
  587. }
  588. }
  589. }
  590. }
  591. }
  592.  
  593. #region Preview Window
  594. PreviewRenderUtility m_previewUtility;
  595. GameObject m_previewInstance;
  596. Vector2 previewDir;
  597. SkeletonAnimation m_skeletonAnimation;
  598. static readonly int SliderHash = "Slider".GetHashCode();
  599. float m_lastTime;
  600. bool m_playing;
  601. bool m_requireRefresh;
  602. Color m_originColor = new Color(0.3f, 0.3f, 0.3f, 1);
  603.  
  604. void StopAnimation () {
  605. if (m_skeletonAnimation == null) {
  606. Debug.LogWarning("Animation was stopped but preview doesn't exist. It's possible that the Preview Panel is closed.");
  607. }
  608.  
  609. m_skeletonAnimation.state.ClearTrack(0);
  610. m_playing = false;
  611. }
  612.  
  613. List<Spine.Event> m_animEvents = new List<Spine.Event>();
  614. List<float> m_animEventFrames = new List<float>();
  615.  
  616. void PlayAnimation (string animName, bool loop) {
  617. m_animEvents.Clear();
  618. m_animEventFrames.Clear();
  619.  
  620. m_skeletonAnimation.state.SetAnimation(0, animName, loop);
  621.  
  622. Spine.Animation a = m_skeletonAnimation.state.GetCurrent(0).Animation;
  623. foreach (Timeline t in a.Timelines) {
  624. if (t.GetType() == typeof(EventTimeline)) {
  625. var et = (EventTimeline)t;
  626. for (int i = 0; i < et.Events.Length; i++) {
  627. m_animEvents.Add(et.Events[i]);
  628. m_animEventFrames.Add(et.Frames[i]);
  629. }
  630. }
  631. }
  632.  
  633. m_playing = true;
  634. }
  635.  
  636. void InitPreview () {
  637. if (this.m_previewUtility == null) {
  638. this.m_lastTime = Time.realtimeSinceStartup;
  639. this.m_previewUtility = new PreviewRenderUtility(true);
  640. var c = this.m_previewUtility.m_Camera;
  641. c.orthographic = true;
  642. c.orthographicSize = 1;
  643. c.cullingMask = -2147483648;
  644. c.nearClipPlane = 0.01f;
  645. c.farClipPlane = 1000f;
  646. this.CreatePreviewInstances();
  647. }
  648. }
  649.  
  650. void CreatePreviewInstances () {
  651. this.DestroyPreviewInstances();
  652.  
  653. if (warnings.Count > 0) {
  654. m_skeletonDataAsset.Clear();
  655. return;
  656. }
  657.  
  658. var skeletonDataAsset = (SkeletonDataAsset)target;
  659. if (skeletonDataAsset.GetSkeletonData(false) == null)
  660. return;
  661.  
  662. if (this.m_previewInstance == null) {
  663. string skinName = EditorPrefs.GetString(m_skeletonDataAssetGUID + "_lastSkin", "");
  664.  
  665. try {
  666. m_previewInstance = SpineEditorUtilities.InstantiateSkeletonAnimation(skeletonDataAsset, skinName).gameObject;
  667.  
  668. if (m_previewInstance != null) {
  669. m_previewInstance.hideFlags = HideFlags.HideAndDontSave;
  670. m_previewInstance.layer = 0x1f;
  671. m_skeletonAnimation = m_previewInstance.GetComponent<SkeletonAnimation>();
  672. m_skeletonAnimation.initialSkinName = skinName;
  673. m_skeletonAnimation.LateUpdate();
  674. m_skeletonData = m_skeletonAnimation.skeletonDataAsset.GetSkeletonData(true);
  675. m_previewInstance.GetComponent<Renderer>().enabled = false;
  676. m_initialized = true;
  677. }
  678.  
  679. AdjustCameraGoals(true);
  680. } catch {
  681. DestroyPreviewInstances();
  682. }
  683.  
  684. }
  685. }
  686.  
  687. void DestroyPreviewInstances () {
  688. if (this.m_previewInstance != null) {
  689. DestroyImmediate(this.m_previewInstance);
  690. m_previewInstance = null;
  691. }
  692. m_initialized = false;
  693. }
  694.  
  695. public override bool HasPreviewGUI () {
  696. if (serializedObject.isEditingMultipleObjects) {
  697. // JOHN: Implement multi-preview.
  698. return false;
  699. }
  700.  
  701. for (int i = 0; i < atlasAssets.arraySize; i++) {
  702. var prop = atlasAssets.GetArrayElementAtIndex(i);
  703. if (prop.objectReferenceValue == null)
  704. return false;
  705. }
  706.  
  707. return skeletonJSON.objectReferenceValue != null;
  708. }
  709.  
  710. Texture m_previewTex = new Texture();
  711.  
  712. public override void OnInteractivePreviewGUI (Rect r, GUIStyle background) {
  713. this.InitPreview();
  714.  
  715. if (Event.current.type == EventType.Repaint) {
  716. if (m_requireRefresh) {
  717. this.m_previewUtility.BeginPreview(r, background);
  718. this.DoRenderPreview(true);
  719. this.m_previewTex = this.m_previewUtility.EndPreview();
  720. m_requireRefresh = false;
  721. }
  722. if (this.m_previewTex != null)
  723. GUI.DrawTexture(r, m_previewTex, ScaleMode.StretchToFill, false);
  724. }
  725.  
  726. DrawSkinToolbar(r);
  727. NormalizedTimeBar(r);
  728. // MITCH: left a todo: Implement panning
  729. // this.previewDir = Drag2D(this.previewDir, r);
  730. MouseScroll(r);
  731. }
  732.  
  733. float m_orthoGoal = 1;
  734. Vector3 m_posGoal = new Vector3(0, 0, -10);
  735. double m_adjustFrameEndTime = 0;
  736.  
  737. void AdjustCameraGoals (bool calculateMixTime) {
  738. if (this.m_previewInstance == null)
  739. return;
  740.  
  741. if (calculateMixTime) {
  742. if (m_skeletonAnimation.state.GetCurrent(0) != null)
  743. m_adjustFrameEndTime = EditorApplication.timeSinceStartup + m_skeletonAnimation.state.GetCurrent(0).Alpha;
  744. }
  745.  
  746. GameObject go = this.m_previewInstance;
  747. Bounds bounds = go.GetComponent<Renderer>().bounds;
  748. m_orthoGoal = bounds.size.y;
  749. m_posGoal = bounds.center + new Vector3(0, 0, -10f);
  750. }
  751.  
  752. void AdjustCameraGoals () {
  753. AdjustCameraGoals(false);
  754. }
  755.  
  756. void AdjustCamera () {
  757. if (m_previewUtility == null)
  758. return;
  759.  
  760. if (EditorApplication.timeSinceStartup < m_adjustFrameEndTime)
  761. AdjustCameraGoals();
  762.  
  763. float orthoSet = Mathf.Lerp(this.m_previewUtility.m_Camera.orthographicSize, m_orthoGoal, 0.1f);
  764.  
  765. this.m_previewUtility.m_Camera.orthographicSize = orthoSet;
  766.  
  767. float dist = Vector3.Distance(m_previewUtility.m_Camera.transform.position, m_posGoal);
  768. if(dist > 0f) {
  769. Vector3 pos = Vector3.Lerp(this.m_previewUtility.m_Camera.transform.position, m_posGoal, 0.1f);
  770. pos.x = 0;
  771. this.m_previewUtility.m_Camera.transform.position = pos;
  772. this.m_previewUtility.m_Camera.transform.rotation = Quaternion.identity;
  773. m_requireRefresh = true;
  774. }
  775. }
  776.  
  777. void DoRenderPreview (bool drawHandles) {
  778. GameObject go = this.m_previewInstance;
  779.  
  780. if (m_requireRefresh && go != null) {
  781. go.GetComponent<Renderer>().enabled = true;
  782.  
  783. if (!EditorApplication.isPlaying)
  784. m_skeletonAnimation.Update((Time.realtimeSinceStartup - m_lastTime));
  785.  
  786. m_lastTime = Time.realtimeSinceStartup;
  787.  
  788. if (!EditorApplication.isPlaying)
  789. m_skeletonAnimation.LateUpdate();
  790.  
  791. if (drawHandles) {
  792. Handles.SetCamera(m_previewUtility.m_Camera);
  793. Handles.color = m_originColor;
  794.  
  795. Handles.DrawLine(new Vector3(-1000 * m_skeletonDataAsset.scale, 0, 0), new Vector3(1000 * m_skeletonDataAsset.scale, 0, 0));
  796. Handles.DrawLine(new Vector3(0, 1000 * m_skeletonDataAsset.scale, 0), new Vector3(0, -1000 * m_skeletonDataAsset.scale, 0));
  797. }
  798.  
  799. this.m_previewUtility.m_Camera.Render();
  800.  
  801. if (drawHandles) {
  802. Handles.SetCamera(m_previewUtility.m_Camera);
  803. SpineHandles.DrawBoundingBoxes(m_skeletonAnimation.transform, m_skeletonAnimation.skeleton);
  804. if (showAttachments) SpineHandles.DrawPaths(m_skeletonAnimation.transform, m_skeletonAnimation.skeleton);
  805. }
  806.  
  807. go.GetComponent<Renderer>().enabled = false;
  808. }
  809.  
  810. }
  811.  
  812. void EditorUpdate () {
  813. AdjustCamera();
  814.  
  815. if (m_playing) {
  816. m_requireRefresh = true;
  817. Repaint();
  818. } else if (m_requireRefresh) {
  819. Repaint();
  820. }
  821. //else {
  822. //only needed if using smooth menus
  823. //}
  824.  
  825. if (needToSerialize) {
  826. needToSerialize = false;
  827. serializedObject.ApplyModifiedProperties();
  828. }
  829. }
  830.  
  831. void DrawSkinToolbar (Rect r) {
  832. if (m_skeletonAnimation == null)
  833. return;
  834.  
  835. if (m_skeletonAnimation.skeleton != null) {
  836. string label = (m_skeletonAnimation.skeleton != null && m_skeletonAnimation.skeleton.Skin != null) ? m_skeletonAnimation.skeleton.Skin.Name : "default";
  837.  
  838. Rect popRect = new Rect(r);
  839. popRect.y += 32;
  840. popRect.x += 4;
  841. popRect.height = 24;
  842. popRect.width = 40;
  843. EditorGUI.DropShadowLabel(popRect, new GUIContent("Skin", Icons.skinsRoot));
  844.  
  845. popRect.y += 11;
  846. popRect.width = 150;
  847. popRect.x += 44;
  848.  
  849. if (GUI.Button(popRect, label, EditorStyles.popup)) {
  850. DrawSkinDropdown();
  851. }
  852. }
  853. }
  854.  
  855. void NormalizedTimeBar (Rect r) {
  856. if (m_skeletonAnimation == null)
  857. return;
  858.  
  859. Rect barRect = new Rect(r);
  860. barRect.height = 32;
  861. barRect.x += 4;
  862. barRect.width -= 4;
  863.  
  864. GUI.Box(barRect, "");
  865.  
  866. Rect lineRect = new Rect(barRect);
  867. float width = lineRect.width;
  868. TrackEntry t = m_skeletonAnimation.state.GetCurrent(0);
  869.  
  870. if (t != null) {
  871. int loopCount = (int)(t.TrackTime / t.TrackEnd);
  872. float currentTime = t.TrackTime - (t.TrackEnd * loopCount);
  873. float normalizedTime = currentTime / t.Animation.Duration;
  874. float wrappedTime = normalizedTime % 1;
  875.  
  876. lineRect.x = barRect.x + (width * wrappedTime) - 0.5f;
  877. lineRect.width = 2;
  878.  
  879. GUI.color = Color.red;
  880. GUI.DrawTexture(lineRect, EditorGUIUtility.whiteTexture);
  881. GUI.color = Color.white;
  882.  
  883. for (int i = 0; i < m_animEvents.Count; i++) {
  884. float fr = m_animEventFrames[i];
  885. var evRect = new Rect(barRect);
  886. evRect.x = Mathf.Clamp(((fr / t.Animation.Duration) * width) - (Icons.userEvent.width / 2), barRect.x, float.MaxValue);
  887. evRect.width = Icons.userEvent.width;
  888. evRect.height = Icons.userEvent.height;
  889. evRect.y += Icons.userEvent.height;
  890. GUI.DrawTexture(evRect, Icons.userEvent);
  891.  
  892. Event ev = Event.current;
  893. if (ev.type == EventType.Repaint) {
  894. if (evRect.Contains(ev.mousePosition)) {
  895. Rect tooltipRect = new Rect(evRect);
  896. GUIStyle tooltipStyle = EditorStyles.helpBox;
  897. tooltipRect.width = tooltipStyle.CalcSize(new GUIContent(m_animEvents[i].Data.Name)).x;
  898. tooltipRect.y -= 4;
  899. tooltipRect.x += 4;
  900. GUI.Label(tooltipRect, m_animEvents[i].Data.Name, tooltipStyle);
  901. GUI.tooltip = m_animEvents[i].Data.Name;
  902. }
  903. }
  904. }
  905. }
  906. }
  907.  
  908. void MouseScroll (Rect position) {
  909. Event current = Event.current;
  910. int controlID = GUIUtility.GetControlID(SliderHash, FocusType.Passive);
  911. switch (current.GetTypeForControl(controlID)) {
  912. case EventType.ScrollWheel:
  913. if (position.Contains(current.mousePosition)) {
  914. m_orthoGoal += current.delta.y * 0.06f;
  915. m_orthoGoal = Mathf.Max(0.01f, m_orthoGoal);
  916. GUIUtility.hotControl = controlID;
  917. current.Use();
  918. }
  919. break;
  920. }
  921. }
  922.  
  923. // MITCH: left todo: Implement preview panning
  924. /*
  925. static Vector2 Drag2D(Vector2 scrollPosition, Rect position)
  926. {
  927. int controlID = GUIUtility.GetControlID(sliderHash, FocusType.Passive);
  928. UnityEngine.Event current = UnityEngine.Event.current;
  929. switch (current.GetTypeForControl(controlID))
  930. {
  931. case EventType.MouseDown:
  932. if (position.Contains(current.mousePosition) && (position.width > 50f))
  933. {
  934. GUIUtility.hotControl = controlID;
  935. current.Use();
  936. EditorGUIUtility.SetWantsMouseJumping(1);
  937. }
  938. return scrollPosition;
  939.  
  940. case EventType.MouseUp:
  941. if (GUIUtility.hotControl == controlID)
  942. {
  943. GUIUtility.hotControl = 0;
  944. }
  945. EditorGUIUtility.SetWantsMouseJumping(0);
  946. return scrollPosition;
  947.  
  948. case EventType.MouseMove:
  949. return scrollPosition;
  950.  
  951. case EventType.MouseDrag:
  952. if (GUIUtility.hotControl == controlID)
  953. {
  954. scrollPosition -= (Vector2) (((current.delta * (!current.shift ? ((float) 1) : ((float) 3))) / Mathf.Min(position.width, position.height)) * 140f);
  955. scrollPosition.y = Mathf.Clamp(scrollPosition.y, -90f, 90f);
  956. current.Use();
  957. GUI.changed = true;
  958. }
  959. return scrollPosition;
  960. }
  961. return scrollPosition;
  962. }
  963. */
  964.  
  965. public override GUIContent GetPreviewTitle () {
  966. return new GUIContent("Preview");
  967. }
  968.  
  969. public override void OnPreviewSettings () {
  970. const float SliderWidth = 100;
  971. if (!m_initialized) {
  972. GUILayout.HorizontalSlider(0, 0, 2, GUILayout.MaxWidth(SliderWidth));
  973. } else {
  974. float speed = GUILayout.HorizontalSlider(m_skeletonAnimation.timeScale, 0, 2, GUILayout.MaxWidth(SliderWidth));
  975.  
  976. const float SliderSnap = 0.25f;
  977. float y = speed / SliderSnap;
  978. int q = Mathf.RoundToInt(y);
  979. speed = q * SliderSnap;
  980.  
  981. m_skeletonAnimation.timeScale = speed;
  982. }
  983. }
  984.  
  985.  
  986. public override Texture2D RenderStaticPreview (string assetPath, UnityEngine.Object[] subAssets, int width, int height) {
  987. var tex = new Texture2D(width, height, TextureFormat.ARGB32, false);
  988.  
  989. this.InitPreview();
  990. if (this.m_previewUtility.m_Camera == null)
  991. return null;
  992.  
  993. m_requireRefresh = true;
  994. this.DoRenderPreview(false);
  995. AdjustCameraGoals(false);
  996. this.m_previewUtility.m_Camera.orthographicSize = m_orthoGoal / 2;
  997. this.m_previewUtility.m_Camera.transform.position = m_posGoal;
  998. this.m_previewUtility.BeginStaticPreview(new Rect(0, 0, width, height));
  999. this.DoRenderPreview(false);
  1000. tex = this.m_previewUtility.EndStaticPreview();
  1001. return tex;
  1002. }
  1003. #endregion
  1004.  
  1005. #region Skin Dropdown Context Menu
  1006. void DrawSkinDropdown () {
  1007. var menu = new GenericMenu();
  1008. foreach (Skin s in m_skeletonData.Skins)
  1009. menu.AddItem(new GUIContent(s.Name), this.m_skeletonAnimation.skeleton.Skin == s, SetSkin, s);
  1010.  
  1011. menu.ShowAsContext();
  1012. }
  1013.  
  1014. void SetSkin (object o) {
  1015. Skin skin = (Skin)o;
  1016. m_skeletonAnimation.initialSkinName = skin.Name;
  1017. m_skeletonAnimation.Initialize(true);
  1018. m_requireRefresh = true;
  1019. EditorPrefs.SetString(m_skeletonDataAssetGUID + "_lastSkin", skin.Name);
  1020. }
  1021. #endregion
  1022. }
  1023.  
  1024. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement