multiple xr toolkit package
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

VRC_EditorTools.cs 22KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457
  1. #if UNITY_EDITOR
  2. using UnityEngine;
  3. using UnityEditor;
  4. using System.Collections.Generic;
  5. using System.Linq;
  6. using UnityEditorInternal;
  7. using System.Reflection;
  8. using System;
  9. namespace VRCSDK2
  10. {
  11. public static class VRC_EditorTools
  12. {
  13. private static LayerMask LayerMaskPopupInternal(LayerMask selectedValue, System.Func<int, string[], int> showMask)
  14. {
  15. string[] layerNames = InternalEditorUtility.layers;
  16. List<int> layerNumbers = new List<int>();
  17. foreach (string layer in layerNames)
  18. layerNumbers.Add(LayerMask.NameToLayer(layer));
  19. int mask = 0;
  20. for (int idx = 0; idx < layerNumbers.Count; ++idx)
  21. if (((1 << layerNumbers[idx]) & selectedValue.value) > 0)
  22. mask |= (1 << idx);
  23. mask = showMask(mask, layerNames);
  24. selectedValue.value = 0;
  25. for (int idx = 0; idx < layerNumbers.Count; ++idx)
  26. if (((1 << idx) & mask) > 0)
  27. selectedValue.value |= (1 << layerNumbers[idx]);
  28. return selectedValue;
  29. }
  30. public static LayerMask LayerMaskPopup(LayerMask selectedValue, params GUILayoutOption[] options)
  31. {
  32. return LayerMaskPopupInternal(selectedValue, (mask, layerNames) => EditorGUILayout.MaskField(mask, layerNames, options));
  33. }
  34. public static LayerMask LayerMaskPopup(string label, LayerMask selectedValue, params GUILayoutOption[] options)
  35. {
  36. return LayerMaskPopupInternal(selectedValue, (mask, layerNames) => EditorGUILayout.MaskField(label, mask, layerNames, options));
  37. }
  38. public static LayerMask LayerMaskPopup(Rect rect, LayerMask selectedValue, GUIStyle style = null)
  39. {
  40. System.Func<int, string[], int> show = (mask, layerNames) =>
  41. {
  42. if (style == null)
  43. return EditorGUI.MaskField(rect, mask, layerNames);
  44. else
  45. return EditorGUI.MaskField(rect, mask, layerNames, style);
  46. };
  47. return LayerMaskPopupInternal(selectedValue, show);
  48. }
  49. public static LayerMask LayerMaskPopup(Rect rect, string label, LayerMask selectedValue, GUIStyle style = null)
  50. {
  51. System.Func<int, string[], int> show = (mask, layerNames) =>
  52. {
  53. if (style == null)
  54. return EditorGUI.MaskField(rect, label, mask, layerNames);
  55. else
  56. return EditorGUI.MaskField(rect, label, mask, layerNames, style);
  57. };
  58. return LayerMaskPopupInternal(selectedValue, show);
  59. }
  60. private static T FilteredEnumPopupInternal<T>(T selectedValue, System.Func<T, bool> predicate, System.Func<int, string[], int> showPopup, System.Func<string, string> rename) where T : struct, System.IConvertible
  61. {
  62. if (!typeof(T).IsEnum)
  63. throw new System.ArgumentException(typeof(T).Name + " is not an Enum", "T");
  64. T[] ary = System.Enum.GetValues(typeof(T)).Cast<T>().Where(v => predicate(v)).ToArray();
  65. string[] names = ary.Select(e => System.Enum.GetName(typeof(T), e)).ToArray();
  66. int selectedIdx = 0;
  67. for (; selectedIdx < ary.Length; ++selectedIdx)
  68. if (ary[selectedIdx].Equals(selectedValue))
  69. break;
  70. if (selectedIdx == ary.Length)
  71. selectedIdx = 0;
  72. if (ary.Length == 0)
  73. throw new System.ArgumentException("Predicate filtered out all options", "predicate");
  74. if (rename != null)
  75. return ary[showPopup(selectedIdx, names.Select(rename).ToArray())];
  76. else
  77. return ary[showPopup(selectedIdx, names)];
  78. }
  79. private static void FilteredEnumPopupInternal<T>(SerializedProperty enumProperty, System.Func<T, bool> predicate, System.Func<int, string[], int> showPopup, System.Func<string, string> rename) where T : struct, System.IConvertible
  80. {
  81. string selectedName = enumProperty.enumNames[enumProperty.enumValueIndex];
  82. T selectedValue = FilteredEnumPopupInternal<T>((T)System.Enum.Parse(typeof(T), selectedName), predicate, showPopup, rename);
  83. selectedName = selectedValue.ToString();
  84. for (int idx = 0; idx < enumProperty.enumNames.Length; ++idx)
  85. if (enumProperty.enumNames[idx] == selectedName)
  86. {
  87. enumProperty.enumValueIndex = idx;
  88. break;
  89. }
  90. }
  91. public static T FilteredEnumPopup<T>(string label, T selectedValue, System.Func<T, bool> predicate, System.Func<string, string> rename = null, params GUILayoutOption[] options) where T : struct, System.IConvertible
  92. {
  93. return FilteredEnumPopupInternal(selectedValue, predicate, (selectedIdx, names) => EditorGUILayout.Popup(label, selectedIdx, names, options), rename);
  94. }
  95. public static T FilteredEnumPopup<T>(T selectedValue, System.Func<T, bool> predicate, System.Func<string, string> rename = null, params GUILayoutOption[] options) where T : struct, System.IConvertible
  96. {
  97. return FilteredEnumPopupInternal(selectedValue, predicate, (selectedIdx, names) => EditorGUILayout.Popup(selectedIdx, names, options), rename);
  98. }
  99. public static T FilteredEnumPopup<T>(Rect rect, string label, T selectedValue, System.Func<T, bool> predicate, System.Func<string, string> rename = null, GUIStyle style = null) where T : struct, System.IConvertible
  100. {
  101. System.Func<int, string[], int> show = (selectedIdx, names) =>
  102. {
  103. if (style != null)
  104. return EditorGUI.Popup(rect, label, selectedIdx, names, style);
  105. else
  106. return EditorGUI.Popup(rect, label, selectedIdx, names);
  107. };
  108. return FilteredEnumPopupInternal(selectedValue, predicate, show, rename);
  109. }
  110. public static T FilteredEnumPopup<T>(Rect rect, T selectedValue, System.Func<T, bool> predicate, System.Func<string, string> rename = null, GUIStyle style = null) where T : struct, System.IConvertible
  111. {
  112. System.Func<int, string[], int> show = (selectedIdx, names) =>
  113. {
  114. if (style != null)
  115. return EditorGUI.Popup(rect, selectedIdx, names, style);
  116. else
  117. return EditorGUI.Popup(rect, selectedIdx, names);
  118. };
  119. return FilteredEnumPopupInternal(selectedValue, predicate, show, rename);
  120. }
  121. public static void FilteredEnumPopup<T>(string label, SerializedProperty selectedValue, System.Func<T, bool> predicate, System.Func<string, string> rename = null, params GUILayoutOption[] options) where T : struct, System.IConvertible
  122. {
  123. FilteredEnumPopupInternal(selectedValue, predicate, (selectedIdx, names) => EditorGUILayout.Popup(label, selectedIdx, names, options), rename);
  124. }
  125. public static void FilteredEnumPopup<T>(SerializedProperty selectedValue, System.Func<T, bool> predicate, System.Func<string, string> rename = null, params GUILayoutOption[] options) where T : struct, System.IConvertible
  126. {
  127. FilteredEnumPopupInternal(selectedValue, predicate, (selectedIdx, names) => EditorGUILayout.Popup(selectedIdx, names, options), rename);
  128. }
  129. public static void FilteredEnumPopup<T>(Rect rect, string label, SerializedProperty selectedValue, System.Func<T, bool> predicate, System.Func<string, string> rename = null, GUIStyle style = null) where T : struct, System.IConvertible
  130. {
  131. System.Func<int, string[], int> show = (selectedIdx, names) =>
  132. {
  133. if (style != null)
  134. return EditorGUI.Popup(rect, label, selectedIdx, names, style);
  135. else
  136. return EditorGUI.Popup(rect, label, selectedIdx, names);
  137. };
  138. FilteredEnumPopupInternal(selectedValue, predicate, show, rename);
  139. }
  140. public static void FilteredEnumPopup<T>(Rect rect, SerializedProperty selectedValue, System.Func<T, bool> predicate, System.Func<string, string> rename = null, GUIStyle style = null) where T : struct, System.IConvertible
  141. {
  142. System.Func<int, string[], int> show = (selectedIdx, names) =>
  143. {
  144. if (style != null)
  145. return EditorGUI.Popup(rect, selectedIdx, names, style);
  146. else
  147. return EditorGUI.Popup(rect, selectedIdx, names);
  148. };
  149. FilteredEnumPopupInternal(selectedValue, predicate, show, rename);
  150. }
  151. private static VRC_Trigger.TriggerEvent CustomTriggerPopupInternal(VRC_Trigger sourceTrigger, VRC_Trigger.TriggerEvent selectedValue, System.Func<int, string[], int> show)
  152. {
  153. if (sourceTrigger == null)
  154. return null;
  155. VRC_Trigger.TriggerEvent[] actionsAry = sourceTrigger.Triggers.Where(t => t.TriggerType == VRC_Trigger.TriggerType.Custom).ToArray();
  156. string[] names = actionsAry.Select(t => t.Name).ToArray();
  157. int selectedIdx = Math.Max(0, names.Length - 1);
  158. if (selectedValue != null)
  159. for (; selectedIdx > 0; --selectedIdx)
  160. if (names[selectedIdx] == selectedValue.Name)
  161. break;
  162. if (actionsAry.Length == 0)
  163. return null;
  164. return actionsAry[show(selectedIdx, names)];
  165. }
  166. public static VRC_Trigger.TriggerEvent CustomTriggerPopup(Rect rect, VRC_Trigger sourceTrigger, VRC_Trigger.TriggerEvent selectedValue, GUIStyle style = null)
  167. {
  168. System.Func<int, string[], int> show = (selectedIdx, names) =>
  169. {
  170. if (style != null)
  171. return EditorGUI.Popup(rect, selectedIdx, names, style);
  172. else
  173. return EditorGUI.Popup(rect, selectedIdx, names);
  174. };
  175. return CustomTriggerPopupInternal(sourceTrigger, selectedValue, show);
  176. }
  177. public static VRC_Trigger.TriggerEvent CustomTriggerPopup(Rect rect, string label, VRC_Trigger sourceTrigger, VRC_Trigger.TriggerEvent selectedValue, GUIStyle style = null)
  178. {
  179. System.Func<int, string[], int> show = (selectedIdx, names) =>
  180. {
  181. if (style != null)
  182. return EditorGUI.Popup(rect, label, selectedIdx, names, style);
  183. else
  184. return EditorGUI.Popup(rect, label, selectedIdx, names);
  185. };
  186. return CustomTriggerPopupInternal(sourceTrigger, selectedValue, show);
  187. }
  188. public static VRC_Trigger.TriggerEvent CustomTriggerPopup(VRC_Trigger sourceTrigger, VRC_Trigger.TriggerEvent selectedValue, params GUILayoutOption[] options)
  189. {
  190. return CustomTriggerPopupInternal(sourceTrigger, selectedValue, (selectedIdx, names) => EditorGUILayout.Popup(selectedIdx, names, options));
  191. }
  192. public static VRC_Trigger.TriggerEvent CustomTriggerPopup(string label, VRC_Trigger sourceTrigger, VRC_Trigger.TriggerEvent selectedValue, params GUILayoutOption[] options)
  193. {
  194. return CustomTriggerPopupInternal(sourceTrigger, selectedValue, (selectedIdx, names) => EditorGUILayout.Popup(label, selectedIdx, names, options));
  195. }
  196. public static VRC_Trigger.TriggerEvent CustomTriggerPopup(string label, VRC_Trigger sourceTrigger, string selectedValue, params GUILayoutOption[] options)
  197. {
  198. if (sourceTrigger == null)
  199. return null;
  200. return CustomTriggerPopup(label, sourceTrigger, sourceTrigger.Triggers.FirstOrDefault(t => t.TriggerType == VRC_Trigger.TriggerType.Custom && t.Name == selectedValue), options);
  201. }
  202. public static VRC_Trigger.TriggerEvent CustomTriggerPopup(VRC_Trigger sourceTrigger, string selectedValue, params GUILayoutOption[] options)
  203. {
  204. if (sourceTrigger == null)
  205. return null;
  206. return CustomTriggerPopup(sourceTrigger, sourceTrigger.Triggers.FirstOrDefault(t => t.TriggerType == VRC_Trigger.TriggerType.Custom && t.Name == selectedValue), options);
  207. }
  208. public static VRC_Trigger.TriggerEvent CustomTriggerPopup(Rect rect, VRC_Trigger sourceTrigger, string selectedValue, GUIStyle style = null)
  209. {
  210. if (sourceTrigger == null)
  211. return null;
  212. return CustomTriggerPopup(rect, sourceTrigger, sourceTrigger.Triggers.FirstOrDefault(t => t.TriggerType == VRC_Trigger.TriggerType.Custom && t.Name == selectedValue), style);
  213. }
  214. public static VRC_Trigger.TriggerEvent CustomTriggerPopup(Rect rect, string label, VRC_Trigger sourceTrigger, string selectedValue, GUIStyle style = null)
  215. {
  216. if (sourceTrigger == null)
  217. return null;
  218. return CustomTriggerPopup(rect, label, sourceTrigger, sourceTrigger.Triggers.FirstOrDefault(t => t.TriggerType == VRC_Trigger.TriggerType.Custom && t.Name == selectedValue), style);
  219. }
  220. private static void InternalSerializedCustomTriggerPopup(SerializedProperty triggersProperty, SerializedProperty customProperty, System.Func<int, string[], int> show, System.Action fail)
  221. {
  222. if (customProperty == null || (customProperty.propertyType != SerializedPropertyType.String))
  223. throw new ArgumentException("Expected a string for customProperty");
  224. if (triggersProperty == null || (!triggersProperty.isArray && triggersProperty.propertyType != SerializedPropertyType.ObjectReference))
  225. throw new ArgumentException("Expected an object or array for triggersProperty");
  226. List<String> customNames = new List<string>();
  227. bool allNull = true;
  228. if (triggersProperty.isArray)
  229. {
  230. int idx;
  231. for (idx = 0; idx < triggersProperty.arraySize; ++idx)
  232. {
  233. GameObject obj = triggersProperty.GetArrayElementAtIndex(idx).objectReferenceValue as GameObject;
  234. if (obj != null)
  235. {
  236. customNames = obj.GetComponent<VRC_Trigger>().Triggers.Where(t => t.TriggerType == VRC_Trigger.TriggerType.Custom).Select(t => t.Name).ToList();
  237. allNull = false;
  238. break;
  239. }
  240. }
  241. for (; idx < triggersProperty.arraySize; ++idx)
  242. {
  243. GameObject obj = triggersProperty.GetArrayElementAtIndex(idx).objectReferenceValue as GameObject;
  244. if (obj != null)
  245. {
  246. List<string> thisCustomNames = obj.GetComponent<VRC_Trigger>().Triggers.Where(t => t.TriggerType == VRC_Trigger.TriggerType.Custom).Select(t => t.Name).ToList();
  247. customNames.RemoveAll(s => thisCustomNames.Contains(s) == false);
  248. }
  249. }
  250. }
  251. else
  252. {
  253. GameObject obj = triggersProperty.objectReferenceValue as GameObject;
  254. if (obj != null)
  255. {
  256. allNull = false;
  257. customNames = obj.GetComponent<VRC_Trigger>().Triggers.Where(t => t.TriggerType == VRC_Trigger.TriggerType.Custom).Select(t => t.Name).ToList();
  258. }
  259. }
  260. if (customNames.Count == 0 && !allNull && triggersProperty.isArray)
  261. {
  262. fail();
  263. customProperty.stringValue = "";
  264. }
  265. else
  266. {
  267. if (customNames.Count == 0)
  268. customNames.Add("");
  269. int selectedIdx = Math.Max(0, customNames.Count - 1);
  270. if (!string.IsNullOrEmpty(customProperty.stringValue))
  271. for (; selectedIdx > 0; --selectedIdx)
  272. if (customNames[selectedIdx] == customProperty.stringValue)
  273. break;
  274. selectedIdx = show(selectedIdx, customNames.ToArray());
  275. customProperty.stringValue = customNames[selectedIdx];
  276. }
  277. }
  278. public static void CustomTriggerPopup(string label, SerializedProperty triggersProperty, SerializedProperty customProperty, params GUILayoutOption[] options)
  279. {
  280. InternalSerializedCustomTriggerPopup(triggersProperty, customProperty, (idx, names) => EditorGUILayout.Popup(label, idx, names, options), () => EditorGUILayout.HelpBox("Receivers do not have Custom Triggers which share names.", MessageType.Warning));
  281. }
  282. public static void CustomTriggerPopup(SerializedProperty triggersProperty, SerializedProperty customProperty, params GUILayoutOption[] options)
  283. {
  284. InternalSerializedCustomTriggerPopup(triggersProperty, customProperty, (idx, names) => EditorGUILayout.Popup(idx, names, options), () => EditorGUILayout.HelpBox("Receivers do not have Custom Triggers which share names.", MessageType.Warning));
  285. }
  286. public static void CustomTriggerPopup(Rect rect, SerializedProperty triggersProperty, SerializedProperty customProperty, GUIStyle style = null)
  287. {
  288. InternalSerializedCustomTriggerPopup(triggersProperty, customProperty, (idx, names) => style == null ? EditorGUI.Popup(rect, idx, names) : EditorGUI.Popup(rect, idx, names, style), () => EditorGUI.HelpBox(rect, "Receivers do not have Custom Triggers which share names.", MessageType.Warning));
  289. }
  290. public static void CustomTriggerPopup(Rect rect, string label, SerializedProperty triggersProperty, SerializedProperty customProperty, GUIStyle style = null)
  291. {
  292. InternalSerializedCustomTriggerPopup(triggersProperty, customProperty, (idx, names) => style == null ? EditorGUI.Popup(rect, label, idx, names) : EditorGUI.Popup(rect, label, idx, names, style), () => EditorGUI.HelpBox(rect, "Receivers do not have Custom Triggers which share names.", MessageType.Warning));
  293. }
  294. public static void DrawTriggerActionCallback(string actionLabel, VRC_Trigger trigger, VRC_EventHandler.VrcEvent e)
  295. {
  296. VRC_Trigger.TriggerEvent triggerEvent = VRC_EditorTools.CustomTriggerPopup(actionLabel, trigger, e.ParameterString);
  297. e.ParameterString = triggerEvent == null ? null : triggerEvent.Name;
  298. }
  299. public static Dictionary<string, List<MethodInfo>> GetSharedAccessibleMethodsOnGameObjects(SerializedProperty objectsProperty)
  300. {
  301. Dictionary<string, List<MethodInfo>> methods = new Dictionary<string, List<MethodInfo>>();
  302. int idx = 0;
  303. for (; idx < objectsProperty.arraySize; ++idx)
  304. {
  305. SerializedProperty prop = objectsProperty.GetArrayElementAtIndex(idx);
  306. GameObject obj = prop.objectReferenceValue != null ? prop.objectReferenceValue as GameObject : null;
  307. if (obj != null)
  308. {
  309. methods = VRC_EditorTools.GetAccessibleMethodsOnGameObject(obj);
  310. break;
  311. }
  312. }
  313. List<string> toRemove = new List<string>();
  314. for (; idx < objectsProperty.arraySize; ++idx)
  315. {
  316. SerializedProperty prop = objectsProperty.GetArrayElementAtIndex(idx);
  317. GameObject obj = prop.objectReferenceValue != null ? prop.objectReferenceValue as GameObject : null;
  318. if (obj != null)
  319. {
  320. Dictionary<string, List<MethodInfo>> thisObjMethods = VRC_EditorTools.GetAccessibleMethodsOnGameObject(obj);
  321. foreach (string className in methods.Keys.Where(s => thisObjMethods.Keys.Contains(s) == false))
  322. toRemove.Add(className);
  323. }
  324. }
  325. foreach (string className in toRemove)
  326. methods.Remove(className);
  327. return methods;
  328. }
  329. public static Dictionary<string, List<MethodInfo>> GetAccessibleMethodsOnGameObject(GameObject go)
  330. {
  331. Dictionary<string, List<MethodInfo>> methods = new Dictionary<string, List<MethodInfo>>();
  332. if (go == null)
  333. return methods;
  334. Component[] cs = go.GetComponents<Component>();
  335. if (cs == null)
  336. return methods;
  337. foreach (Component c in cs.Where(co => co != null))
  338. {
  339. Type t = c.GetType();
  340. if (methods.ContainsKey(t.Name))
  341. continue;
  342. // if component is the eventhandler
  343. if (t == typeof(VRC_EventHandler))
  344. continue;
  345. List<MethodInfo> l = GetAccessibleMethodsForClass(t);
  346. methods.Add(t.Name, l);
  347. }
  348. return methods;
  349. }
  350. public static List<MethodInfo> GetAccessibleMethodsForClass(Type t)
  351. {
  352. // Get the public methods.
  353. MethodInfo[] myArrayMethodInfo = t.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
  354. List<MethodInfo> methods = new List<MethodInfo>();
  355. // if component is in UnityEngine namespace, skip it
  356. if (!string.IsNullOrEmpty(t.Namespace) && (t.Namespace.Contains("UnityEngine") || t.Namespace.Contains("UnityEditor")))
  357. return methods;
  358. bool isVRCSDK2 = !string.IsNullOrEmpty(t.Namespace) && t.Namespace.Contains("VRCSDK2");
  359. // Display information for all methods.
  360. for (int i = 0; i < myArrayMethodInfo.Length; i++)
  361. {
  362. MethodInfo myMethodInfo = (MethodInfo)myArrayMethodInfo[i];
  363. // if it's VRCSDK2, require RPC
  364. if (isVRCSDK2 && myMethodInfo.GetCustomAttributes(typeof(VRCSDK2.RPC), true).Length == 0)
  365. continue;
  366. methods.Add(myMethodInfo);
  367. }
  368. return methods;
  369. }
  370. public static byte[] ReadBytesFromProperty(SerializedProperty property)
  371. {
  372. byte[] bytes = new byte[property.arraySize];
  373. for (int idx = 0; idx < property.arraySize; ++idx)
  374. bytes[idx] = (byte)property.GetArrayElementAtIndex(idx).intValue;
  375. return bytes;
  376. }
  377. public static void WriteBytesToProperty(SerializedProperty property, byte[] bytes)
  378. {
  379. property.arraySize = bytes != null ? bytes.Length : 0;
  380. for (int idx = 0; idx < property.arraySize; ++idx)
  381. property.GetArrayElementAtIndex(idx).intValue = (int)bytes[idx];
  382. }
  383. }
  384. }
  385. #endif