2
0

GUIAnimFieldDisplay.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377
  1. //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
  2. //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Text;
  6. using BansheeEngine;
  7. namespace BansheeEditor
  8. {
  9. /** @addtogroup AnimationEditor
  10. * @{
  11. */
  12. internal class GUIAnimFieldDisplay
  13. {
  14. private SceneObject root;
  15. private GUIScrollArea scrollArea;
  16. private List<string> paths = new List<string>();
  17. private GUIAnimFieldEntry[] fields;
  18. public GUIAnimFieldDisplay(GUILayout layout, SceneObject root)
  19. {
  20. this.root = root;
  21. scrollArea = new GUIScrollArea();
  22. layout.AddElement(scrollArea);
  23. }
  24. public Action<string[]> OnSelectionChanged;
  25. public void SetFields(string[] paths)
  26. {
  27. this.paths.Clear();
  28. this.paths.AddRange(paths);
  29. Rebuild();
  30. }
  31. public void AddField(string path)
  32. {
  33. paths.Add(path);
  34. Rebuild();
  35. }
  36. private SerializableProperty FindProperty(string path)
  37. {
  38. if (string.IsNullOrEmpty(path) || root == null)
  39. return null;
  40. string[] entries = path.Split('/');
  41. // Find scene object referenced by the path
  42. SceneObject so = null;
  43. int pathIdx = 0;
  44. for (; pathIdx < entries.Length; pathIdx++)
  45. {
  46. string entry = entries[pathIdx];
  47. // Not a scene object, break
  48. if (entry[0] != '!')
  49. break;
  50. if (pathIdx == 0)
  51. so = root;
  52. else
  53. {
  54. string childName = entry.Substring(1, entry.Length - 1);
  55. so = so.FindChild(childName);
  56. if (so == null)
  57. break;
  58. }
  59. }
  60. // Cannot find scene object with specified hierarchy & name
  61. if (so == null)
  62. return null;
  63. pathIdx++;
  64. if (pathIdx >= entries.Length)
  65. return null;
  66. // If path is referencing a component, find it
  67. Component component = null;
  68. {
  69. string entry = entries[pathIdx];
  70. if (entry[0] == ':')
  71. {
  72. string componentName = entry.Substring(1, entry.Length - 1);
  73. Component[] components = so.GetComponents();
  74. component = Array.Find(components, x => x.GetType().Name == componentName);
  75. // Cannot find component with specified type
  76. if (component == null)
  77. return null;
  78. }
  79. }
  80. // Look for a field within a component
  81. if (component != null)
  82. {
  83. pathIdx++;
  84. if (pathIdx >= entries.Length)
  85. return null;
  86. SerializableObject componentObj = new SerializableObject(component);
  87. StringBuilder pathBuilder = new StringBuilder();
  88. for(; pathIdx < entries.Length; pathIdx++)
  89. pathBuilder.Append(entries[pathIdx] + "/");
  90. return componentObj.FindProperty(pathBuilder.ToString());
  91. }
  92. else // Field is one of the builtin ones on the SceneObject itself
  93. {
  94. if ((pathIdx + 1) < entries.Length)
  95. return null;
  96. string entry = entries[pathIdx];
  97. if (entry == "Position")
  98. {
  99. SerializableProperty property = new SerializableProperty(
  100. SerializableProperty.FieldType.Vector3,
  101. typeof(Vector3),
  102. () => so.LocalPosition,
  103. (x) => so.LocalPosition = (Vector3)x);
  104. return property;
  105. }
  106. else if (entry == "Rotation")
  107. {
  108. SerializableProperty property = new SerializableProperty(
  109. SerializableProperty.FieldType.Vector3,
  110. typeof(Vector3),
  111. () => so.LocalRotation.ToEuler(),
  112. (x) => so.LocalRotation = Quaternion.FromEuler((Vector3)x));
  113. return property;
  114. }
  115. else if (entry == "Scale")
  116. {
  117. SerializableProperty property = new SerializableProperty(
  118. SerializableProperty.FieldType.Vector3,
  119. typeof(Vector3),
  120. () => so.LocalScale,
  121. (x) => so.LocalScale = (Vector3)x);
  122. return property;
  123. }
  124. return null;
  125. }
  126. }
  127. private void Rebuild()
  128. {
  129. scrollArea.Layout.Clear();
  130. fields = null;
  131. if (paths == null || root == null)
  132. return;
  133. fields = new GUIAnimFieldEntry[paths.Count];
  134. for (int i = 0; i < paths.Count; i++)
  135. {
  136. SerializableProperty property = FindProperty(paths[i]);
  137. if (property != null)
  138. {
  139. switch (property.Type)
  140. {
  141. case SerializableProperty.FieldType.Vector2:
  142. fields[i] = new GUIAnimVec2Entry(scrollArea.Layout, paths[i]);
  143. break;
  144. case SerializableProperty.FieldType.Vector3:
  145. fields[i] = new GUIAnimVec3Entry(scrollArea.Layout, paths[i]);
  146. break;
  147. case SerializableProperty.FieldType.Vector4:
  148. fields[i] = new GUIAnimVec4Entry(scrollArea.Layout, paths[i]);
  149. break;
  150. case SerializableProperty.FieldType.Color:
  151. fields[i] = new GUIAnimColorEntry(scrollArea.Layout, paths[i]);
  152. break;
  153. case SerializableProperty.FieldType.Bool:
  154. case SerializableProperty.FieldType.Int:
  155. case SerializableProperty.FieldType.Float:
  156. fields[i] = new GUIAnimFieldEntry(scrollArea.Layout, paths[i]);
  157. break;
  158. }
  159. }
  160. else
  161. {
  162. // TODO - Add special field type for missing properties
  163. }
  164. }
  165. }
  166. }
  167. internal class GUIAnimFieldEntry
  168. {
  169. private const int MAX_PATH_LENGTH = 20;
  170. protected const int INDENT_AMOUNT = 10;
  171. private GUIToggle toggle;
  172. protected string path;
  173. public Action<string, bool> OnSelectionChanged;
  174. public GUIAnimFieldEntry(GUILayout layout, string path)
  175. {
  176. toggle = new GUIToggle(GetDisplayName(path), EditorStyles.SelectableLabel);
  177. layout.AddElement(toggle);
  178. // TODO - Show current value (if not complex)
  179. // TODO - Button to remove properties
  180. this.path = path;
  181. }
  182. public virtual void Toggle(bool on)
  183. { }
  184. private static string GetDisplayName(string path)
  185. {
  186. if (string.IsNullOrEmpty(path))
  187. return "";
  188. string soName;
  189. string compName;
  190. string propertyPath;
  191. GetNames(path, out soName, out compName, out propertyPath);
  192. if (soName == null || propertyPath == null)
  193. return "";
  194. string truncatedPropPath;
  195. if (propertyPath.Length > MAX_PATH_LENGTH)
  196. truncatedPropPath = "..." + propertyPath.Substring(propertyPath.Length - MAX_PATH_LENGTH);
  197. else
  198. truncatedPropPath = propertyPath;
  199. if (compName != null)
  200. return soName + "(" + compName + ")." + truncatedPropPath;
  201. else
  202. return soName + "." + truncatedPropPath;
  203. }
  204. private static void GetNames(string path, out string soName, out string compName, out string propertyPath)
  205. {
  206. string[] entries = path.Split('/');
  207. // Find name of the last scene object in the path
  208. int pathIdx = 0;
  209. for (; pathIdx < entries.Length; pathIdx++)
  210. {
  211. string entry = entries[pathIdx];
  212. // Not a scene object, break
  213. if (entry[0] != '!')
  214. break;
  215. }
  216. if (pathIdx >= entries.Length)
  217. {
  218. soName = null;
  219. compName = null;
  220. propertyPath = null;
  221. return;
  222. }
  223. soName = entries[pathIdx].Substring(1, entries[pathIdx].Length - 1);
  224. pathIdx++;
  225. if (pathIdx >= entries.Length)
  226. {
  227. compName = null;
  228. propertyPath = null;
  229. return;
  230. }
  231. // If path is referencing a component, find it
  232. {
  233. string entry = entries[pathIdx];
  234. if (entry[0] == ':')
  235. compName = entry.Substring(1, entry.Length - 1);
  236. else
  237. compName = null;
  238. }
  239. // Look for a field name
  240. if (compName != null)
  241. {
  242. pathIdx++;
  243. if (pathIdx >= entries.Length)
  244. {
  245. propertyPath = null;
  246. return;
  247. }
  248. }
  249. StringBuilder pathBuilder = new StringBuilder();
  250. for (; pathIdx < entries.Length; pathIdx++)
  251. pathBuilder.Append(entries[pathIdx] + "/");
  252. propertyPath = pathBuilder.ToString();
  253. }
  254. }
  255. internal class GUIAnimComplexEntry : GUIAnimFieldEntry
  256. {
  257. private GUIToggle[] children;
  258. private GUILayout childLayout;
  259. protected GUIAnimComplexEntry(GUILayout layout, string path, string[] childEntries)
  260. : base(layout, path)
  261. {
  262. childLayout = layout.AddLayoutX();
  263. childLayout.AddSpace(INDENT_AMOUNT);
  264. // TODO - Foldout to expand/collapse child layout
  265. GUILayout indentLayout = childLayout.AddLayoutY();
  266. children = new GUIToggle[childEntries.Length];
  267. for (int i = 0; i < childEntries.Length; i++)
  268. {
  269. int index = i;
  270. children[i] = new GUIToggle(new LocEdString(childEntries[i]), EditorStyles.SelectableLabel);
  271. children[i].OnToggled += x => { OnSelectionChanged?.Invoke(path + childEntries[index], x); };
  272. indentLayout.AddElement(children[i]);
  273. }
  274. Toggle(false);
  275. }
  276. public override void Toggle(bool on)
  277. {
  278. childLayout.Active = on;
  279. }
  280. }
  281. internal class GUIAnimVec2Entry : GUIAnimComplexEntry
  282. {
  283. public GUIAnimVec2Entry(GUILayout layout, string path)
  284. : base(layout, path, new[] { ".x", ".y" })
  285. { }
  286. }
  287. internal class GUIAnimVec3Entry : GUIAnimComplexEntry
  288. {
  289. public GUIAnimVec3Entry(GUILayout layout, string path)
  290. : base(layout, path, new[] { ".x", ".y", ".z" })
  291. { }
  292. }
  293. internal class GUIAnimVec4Entry : GUIAnimComplexEntry
  294. {
  295. public GUIAnimVec4Entry(GUILayout layout, string path)
  296. : base(layout, path, new[] { ".x", ".y", ".z", ".w" })
  297. { }
  298. }
  299. internal class GUIAnimColorEntry : GUIAnimComplexEntry
  300. {
  301. public GUIAnimColorEntry(GUILayout layout, string path)
  302. : base(layout, path, new[] { ".r", ".g", ".b", ".a" })
  303. { }
  304. }
  305. /** @} */
  306. }