PageRenderTime 39ms CodeModel.GetById 22ms app.highlight 12ms RepoModel.GetById 0ms app.codeStats 1ms

/Assets/Spine/Editor/spine-unity/Modules/SkeletonRenderSeparator/Editor/SkeletonRenderSeparatorInspector.cs

https://gitlab.com/hoangduy09100/falling-box
C# | 323 lines | 228 code | 49 blank | 46 comment | 80 complexity | ce02c2654c6569620ce9c04fcde6b975 MD5 | raw file
  1/******************************************************************************
  2 * Spine Runtimes License Agreement
  3 * Last updated January 1, 2020. Replaces all prior versions.
  4 *
  5 * Copyright (c) 2013-2020, Esoteric Software LLC
  6 *
  7 * Integration of the Spine Runtimes into software or otherwise creating
  8 * derivative works of the Spine Runtimes is permitted under the terms and
  9 * conditions of Section 2 of the Spine Editor License Agreement:
 10 * http://esotericsoftware.com/spine-editor-license
 11 *
 12 * Otherwise, it is permitted to integrate the Spine Runtimes into software
 13 * or otherwise create derivative works of the Spine Runtimes (collectively,
 14 * "Products"), provided that each user of the Products must obtain their own
 15 * Spine Editor license and redistribution of the Products in any form must
 16 * include this license and copyright notice.
 17 *
 18 * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
 19 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 21 * DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
 22 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
 24 * BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
 25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 27 * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 28 *****************************************************************************/
 29
 30#if UNITY_2018_3 || UNITY_2019 || UNITY_2018_3_OR_NEWER
 31#define NEW_PREFAB_SYSTEM
 32#endif
 33
 34using UnityEngine;
 35using UnityEditor;
 36
 37using System.Collections.Generic;
 38
 39using Spine.Unity;
 40using Spine.Unity.Editor;
 41
 42namespace Spine.Unity.Examples {
 43
 44	[CustomEditor(typeof(SkeletonRenderSeparator))]
 45	public class SkeletonRenderSeparatorInspector : UnityEditor.Editor {
 46		SkeletonRenderSeparator component;
 47
 48		// Properties
 49		SerializedProperty skeletonRenderer_, copyPropertyBlock_, copyMeshRendererFlags_, partsRenderers_;
 50		static bool partsRenderersExpanded = false;
 51
 52		// For separator field.
 53		SerializedObject skeletonRendererSerializedObject;
 54		SerializedProperty separatorNamesProp;
 55		static bool skeletonRendererExpanded = true;
 56		bool slotsReapplyRequired = false;
 57		bool partsRendererInitRequired = false;
 58
 59		void OnEnable () {
 60			if (component == null)
 61				component = target as SkeletonRenderSeparator;
 62
 63			skeletonRenderer_ = serializedObject.FindProperty("skeletonRenderer");
 64			copyPropertyBlock_ = serializedObject.FindProperty("copyPropertyBlock");
 65			copyMeshRendererFlags_ = serializedObject.FindProperty("copyMeshRendererFlags");
 66
 67			var partsRenderers = component.partsRenderers;
 68			partsRenderers_ = serializedObject.FindProperty("partsRenderers");
 69			partsRenderers_.isExpanded = partsRenderersExpanded ||	// last state
 70				partsRenderers.Contains(null) ||	// null items found
 71				partsRenderers.Count < 1 ||			// no parts renderers
 72				(skeletonRenderer_.objectReferenceValue != null && SkeletonRendererSeparatorCount + 1 > partsRenderers.Count); // not enough parts renderers
 73		}
 74
 75		int SkeletonRendererSeparatorCount {
 76			get {
 77				if (Application.isPlaying)
 78					return component.SkeletonRenderer.separatorSlots.Count;
 79				else
 80					return separatorNamesProp == null ? 0 : separatorNamesProp.arraySize;
 81			}
 82		}
 83
 84		public override void OnInspectorGUI () {
 85
 86			// Restore mesh part for undo logic after undo of "Add Parts Renderer".
 87			// Triggers regeneration and assignment of the mesh filter's mesh.
 88
 89			bool isMeshFilterAlwaysNull = false;
 90			#if UNITY_EDITOR && NEW_PREFAB_SYSTEM
 91			// Don't store mesh or material at the prefab, otherwise it will permanently reload
 92			var prefabType = UnityEditor.PrefabUtility.GetPrefabAssetType(component);
 93			if (UnityEditor.PrefabUtility.IsPartOfPrefabAsset(component) &&
 94				(prefabType == UnityEditor.PrefabAssetType.Regular || prefabType == UnityEditor.PrefabAssetType.Variant)) {
 95				isMeshFilterAlwaysNull = true;
 96			}
 97			#endif
 98
 99			if (!isMeshFilterAlwaysNull && component.GetComponent<MeshFilter>() && component.GetComponent<MeshFilter>().sharedMesh == null) {
100				component.OnDisable();
101				component.OnEnable();
102			}
103
104			var componentRenderers = component.partsRenderers;
105			int totalParts;
106
107			using (new SpineInspectorUtility.LabelWidthScope()) {
108				bool componentEnabled = component.enabled;
109				bool checkBox = EditorGUILayout.Toggle("Enable Separator", componentEnabled);
110				if (checkBox != componentEnabled)
111					component.enabled = checkBox;
112				if (component.SkeletonRenderer.disableRenderingOnOverride && !component.enabled)
113					EditorGUILayout.HelpBox("By default, SkeletonRenderer's MeshRenderer is disabled while the SkeletonRenderSeparator takes over rendering. It is re-enabled when SkeletonRenderSeparator is disabled.", MessageType.Info);
114
115				EditorGUILayout.PropertyField(copyPropertyBlock_);
116				EditorGUILayout.PropertyField(copyMeshRendererFlags_);
117			}
118
119			// SkeletonRenderer Box
120			using (new SpineInspectorUtility.BoxScope(false)) {
121				// Fancy SkeletonRenderer foldout reference field
122				{
123					EditorGUI.indentLevel++;
124					EditorGUI.BeginChangeCheck();
125					var foldoutSkeletonRendererRect = EditorGUILayout.GetControlRect(true, EditorGUIUtility.singleLineHeight);
126					EditorGUI.PropertyField(foldoutSkeletonRendererRect, skeletonRenderer_);
127					if (EditorGUI.EndChangeCheck())
128						serializedObject.ApplyModifiedProperties();
129					if (component.SkeletonRenderer != null) {
130						skeletonRendererExpanded = EditorGUI.Foldout(foldoutSkeletonRendererRect, skeletonRendererExpanded, "");
131					}
132					EditorGUI.indentLevel--;
133				}
134
135				int separatorCount = 0;
136				EditorGUI.BeginChangeCheck();
137				if (component.SkeletonRenderer != null) {
138					// Separators from SkeletonRenderer
139					{
140						bool skeletonRendererMismatch = skeletonRendererSerializedObject != null && skeletonRendererSerializedObject.targetObject != component.SkeletonRenderer;
141						if (separatorNamesProp == null || skeletonRendererMismatch) {
142							if (component.SkeletonRenderer != null) {
143								skeletonRendererSerializedObject = new SerializedObject(component.SkeletonRenderer);
144								separatorNamesProp = skeletonRendererSerializedObject.FindProperty("separatorSlotNames");
145								separatorNamesProp.isExpanded = true;
146							}
147						}
148
149						if (separatorNamesProp != null) {
150							if (skeletonRendererExpanded) {
151								EditorGUI.indentLevel++;
152								SkeletonRendererInspector.SeparatorsField(separatorNamesProp);
153								EditorGUI.indentLevel--;
154							}
155							separatorCount = this.SkeletonRendererSeparatorCount;
156						}
157					}
158
159					if (SkeletonRendererSeparatorCount == 0) {
160						EditorGUILayout.HelpBox("Separators are empty. Change the size to 1 and choose a slot if you want the render to be separated.", MessageType.Info);
161					}
162				}
163
164				if (EditorGUI.EndChangeCheck()) {
165					skeletonRendererSerializedObject.ApplyModifiedProperties();
166
167					if (!Application.isPlaying)
168						slotsReapplyRequired = true;
169				}
170
171
172				totalParts = separatorCount + 1;
173				var counterStyle = skeletonRendererExpanded ? EditorStyles.label : EditorStyles.miniLabel;
174				EditorGUILayout.LabelField(string.Format("{0}: separates into {1}.", SpineInspectorUtility.Pluralize(separatorCount, "separator", "separators"), SpineInspectorUtility.Pluralize(totalParts, "part", "parts") ), counterStyle);
175			}
176
177			// Parts renderers
178			using (new SpineInspectorUtility.BoxScope(false)) {
179				EditorGUI.indentLevel++;
180				EditorGUILayout.PropertyField(this.partsRenderers_, true);
181				EditorGUI.indentLevel--;
182
183				// Null items warning
184				bool nullItemsFound = componentRenderers.Contains(null);
185				if (nullItemsFound)
186					EditorGUILayout.HelpBox("Some items in the parts renderers list are null and may cause problems.\n\nYou can right-click on that element and choose 'Delete Array Element' to remove it.", MessageType.Warning);
187
188				// (Button) Match Separators count
189				if (separatorNamesProp != null) {
190					int currentRenderers = 0;
191					foreach (var r in componentRenderers) {
192						if (r != null)
193							currentRenderers++;
194					}
195					int extraRenderersNeeded = totalParts - currentRenderers;
196
197					if (component.enabled && component.SkeletonRenderer != null && extraRenderersNeeded > 0) {
198						EditorGUILayout.HelpBox(string.Format("Insufficient parts renderers. Some parts will not be rendered."), MessageType.Warning);
199						string addMissingLabel = string.Format("Add the missing renderer{1} ({0}) ", extraRenderersNeeded, SpineInspectorUtility.PluralThenS(extraRenderersNeeded));
200						if (GUILayout.Button(addMissingLabel, GUILayout.Height(30f))) {
201							AddPartsRenderer(extraRenderersNeeded);
202							DetectOrphanedPartsRenderers(component);
203							partsRendererInitRequired = true;
204						}
205					}
206				}
207
208				if (partsRenderers_.isExpanded != partsRenderersExpanded) partsRenderersExpanded = partsRenderers_.isExpanded;
209				if (partsRenderers_.isExpanded) {
210					using (new EditorGUILayout.HorizontalScope()) {
211						// (Button) Destroy Renderers button
212						if (componentRenderers.Count > 0) {
213							if (GUILayout.Button("Clear Parts Renderers")) {
214								// Do you really want to destroy all?
215								Undo.RegisterCompleteObjectUndo(component, "Clear Parts Renderers");
216								if (EditorUtility.DisplayDialog("Destroy Renderers", "Do you really want to destroy all the Parts Renderer GameObjects in the list?", "Destroy", "Cancel")) {
217									foreach (var r in componentRenderers) {
218										if (r != null)
219											Undo.DestroyObjectImmediate(r.gameObject);
220									}
221									componentRenderers.Clear();
222									// Do you also want to destroy orphans? (You monster.)
223									DetectOrphanedPartsRenderers(component);
224								}
225							}
226						}
227
228						// (Button) Add Part Renderer button
229						if (GUILayout.Button("Add Parts Renderer")) {
230							AddPartsRenderer(1);
231							partsRendererInitRequired = true;
232						}
233					}
234				}
235			}
236
237			serializedObject.ApplyModifiedProperties();
238
239			if (partsRendererInitRequired) {
240				Undo.RegisterCompleteObjectUndo(component.GetComponent<MeshRenderer>(), "Add Parts Renderers");
241				component.OnEnable();
242				partsRendererInitRequired = false;
243			}
244
245			if (slotsReapplyRequired && UnityEngine.Event.current.type == EventType.Repaint) {
246				component.SkeletonRenderer.ReapplySeparatorSlotNames();
247				component.SkeletonRenderer.LateUpdate();
248				SceneView.RepaintAll();
249				slotsReapplyRequired = false;
250			}
251		}
252
253		public void AddPartsRenderer (int count) {
254			var componentRenderers = component.partsRenderers;
255			bool emptyFound = componentRenderers.Contains(null);
256			if (emptyFound) {
257				bool userClearEntries = EditorUtility.DisplayDialog("Empty entries found", "Null entries found. Do you want to remove null entries before adding the new renderer? ", "Clear Empty Entries", "Don't Clear");
258				if (userClearEntries) componentRenderers.RemoveAll(x => x == null);
259			}
260
261			Undo.RegisterCompleteObjectUndo(component, "Add Parts Renderers");
262			for (int i = 0; i < count; i++) {
263				int index = componentRenderers.Count;
264				var smr = SkeletonPartsRenderer.NewPartsRendererGameObject(component.transform, index.ToString());
265				Undo.RegisterCreatedObjectUndo(smr.gameObject, "New Parts Renderer GameObject.");
266				componentRenderers.Add(smr);
267
268				// increment renderer sorting order.
269				if (index == 0) continue;
270				var prev = componentRenderers[index - 1]; if (prev == null) continue;
271
272				var prevMeshRenderer = prev.GetComponent<MeshRenderer>();
273				var currentMeshRenderer = smr.GetComponent<MeshRenderer>();
274				if (prevMeshRenderer == null || currentMeshRenderer == null) continue;
275
276				int prevSortingLayer = prevMeshRenderer.sortingLayerID;
277				int prevSortingOrder = prevMeshRenderer.sortingOrder;
278				currentMeshRenderer.sortingLayerID = prevSortingLayer;
279				currentMeshRenderer.sortingOrder = prevSortingOrder + SkeletonRenderSeparator.DefaultSortingOrderIncrement;
280			}
281
282		}
283
284		/// <summary>Detects orphaned parts renderers and offers to delete them.</summary>
285		public void DetectOrphanedPartsRenderers (SkeletonRenderSeparator component) {
286			var children = component.GetComponentsInChildren<SkeletonPartsRenderer>();
287
288			var orphans = new System.Collections.Generic.List<SkeletonPartsRenderer>();
289			foreach (var r in children) {
290				if (!component.partsRenderers.Contains(r))
291					orphans.Add(r);
292			}
293
294			if (orphans.Count > 0) {
295				if (EditorUtility.DisplayDialog("Destroy Submesh Renderers", "Unassigned renderers were found. Do you want to delete them? (These may belong to another Render Separator in the same hierarchy. If you don't have another Render Separator component in the children of this GameObject, it's likely safe to delete. Warning: This operation cannot be undone.)", "Delete", "Cancel")) {
296					foreach (var o in orphans) {
297						Undo.DestroyObjectImmediate(o.gameObject);
298					}
299				}
300			}
301		}
302
303		#region SkeletonRenderer Context Menu Item
304		[MenuItem ("CONTEXT/SkeletonRenderer/Add Skeleton Render Separator")]
305		static void AddRenderSeparatorComponent (MenuCommand cmd) {
306			var skeletonRenderer = cmd.context as SkeletonRenderer;
307			var newComponent = skeletonRenderer.gameObject.AddComponent<SkeletonRenderSeparator>();
308
309			Undo.RegisterCreatedObjectUndo(newComponent, "Add SkeletonRenderSeparator");
310		}
311
312		// Validate
313		[MenuItem ("CONTEXT/SkeletonRenderer/Add Skeleton Render Separator", true)]
314		static bool ValidateAddRenderSeparatorComponent (MenuCommand cmd) {
315			var skeletonRenderer = cmd.context as SkeletonRenderer;
316			var separator = skeletonRenderer.GetComponent<SkeletonRenderSeparator>();
317			bool separatorNotOnObject = separator == null;
318			return separatorNotOnObject;
319		}
320		#endregion
321
322	}
323}