EditorViewSelectableOrigins.as 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435
  1. const bool DEFAULT_SHOW_NAMES_FOR_ALL = false;
  2. const int ORIGIN_STEP_UPDATE = 10;
  3. const int NAMES_SIZE = 11;
  4. const StringHash ORIGIN_NODEID_VAR("OriginNodeID");
  5. const Color ORIGIN_COLOR(1.0f,1.0f,1.0f,1.0f);
  6. const Color ORIGIN_COLOR_SELECTED(0.0f,1.0f,1.0f,1.0f);
  7. const Color ORIGIN_COLOR_DISABLED(1.0f,0.0f,0.0f,1.0f);
  8. const Color ORIGIN_COLOR_TEXT(1.0f,1.0f,1.0f,0.3f);
  9. const Color ORIGIN_COLOR_SELECTED_TEXT(1.0f,1.0f,1.0f,1.0f);
  10. const IntVector2 ORIGIN_ICON_SIZE(14,14);
  11. const IntVector2 ORIGIN_ICON_SIZE_SELECTED(18,18);
  12. const float ORIGINS_VISIBLITY_RANGE = 32.0f;
  13. const IntVector2 ORIGINOFFSETICON(8,8);
  14. const IntVector2 ORIGINOFFSETICONSELECTED(10,8);
  15. bool showNamesForAll = DEFAULT_SHOW_NAMES_FOR_ALL;
  16. bool EditorOriginShow = false;
  17. bool rebuildSceneOrigins = true;
  18. bool isOriginsHovered = false;
  19. int EditorOriginUITimeToUpdate = 0;
  20. int EditorOriginUITimeToSceneNodeRead = 0;
  21. int prevSelectedID;
  22. int selectedNodeInfoState = 0;
  23. int originHoveredIndex = -1;
  24. UIElement@ EditorOriginUIContainer = null;
  25. Text@ selectedNodeName = null;
  26. BorderImage@ selectedNodeOrigin = null;
  27. Array<BorderImage@> selectedNodeOriginChilds;
  28. Array<Text@> selectedNodeNameChilds;
  29. Array<Node@> originsNodes;
  30. Array<BorderImage@> originsIcons;
  31. Array<Text@> originsNames;
  32. void CreateOriginsContainer()
  33. {
  34. if (editorScene is null) return;
  35. EditorOriginUIContainer = UIElement();
  36. EditorOriginUIContainer.position = IntVector2(0,0);
  37. EditorOriginUIContainer.size = IntVector2(graphics.width,graphics.height);
  38. EditorOriginUIContainer.priority = -1000;
  39. EditorOriginUIContainer.focusMode = FM_NOTFOCUSABLE;
  40. EditorOriginUIContainer.bringToBack = true;
  41. EditorOriginUIContainer.name ="DebugOriginsContainer";
  42. EditorOriginUIContainer.temporary = true;
  43. ui.root.AddChild(EditorOriginUIContainer);
  44. }
  45. void HandleOriginToggled(StringHash eventType, VariantMap& eventData)
  46. {
  47. UIElement@ origin = eventData["Element"].GetPtr();
  48. if (origin is null) return;
  49. if (EditorPaintSelectionShow) return;
  50. if (IsSceneOrigin(origin))
  51. {
  52. int nodeID = origin.vars[ORIGIN_NODEID_VAR].GetInt();
  53. if (editorScene !is null)
  54. {
  55. bool goBackAndSelectNodeParent = input.qualifierDown[QUAL_CTRL];
  56. bool multiSelect = input.qualifierDown[QUAL_SHIFT];
  57. WeakHandle handle = editorScene.GetNode(nodeID);
  58. if (handle.Get() !is null) {
  59. Node@ selectedNodeByOrigin = handle.Get();
  60. if (selectedNodeByOrigin !is null)
  61. {
  62. if (goBackAndSelectNodeParent)
  63. SelectNode(selectedNodeByOrigin.parent, false);
  64. else
  65. SelectNode(selectedNodeByOrigin, multiSelect);
  66. }
  67. }
  68. }
  69. }
  70. }
  71. void ShowOrigins(bool isVisible = true)
  72. {
  73. EditorOriginShow = isVisible;
  74. if (EditorOriginUIContainer is null)
  75. CreateOriginsContainer();
  76. EditorOriginUIContainer.visible = isVisible;
  77. }
  78. void UpdateOrigins()
  79. {
  80. // Early out if Origins are disabled
  81. if (!EditorOriginShow) return;
  82. CheckKeyboardQualifers();
  83. if (editorScene is null || EditorOriginUITimeToUpdate > time.systemTime) return;
  84. EditorOriginUIContainer = ui.root.GetChild("DebugOriginsContainer");
  85. // Since editor not clear UIs then do loading new scenes, this creation called once per Editor's starting event
  86. // for other scenes we use the same container
  87. if (EditorOriginUIContainer is null)
  88. {
  89. CreateOriginsContainer();
  90. }
  91. if (EditorOriginUIContainer !is null)
  92. {
  93. // Set visibility for all origins
  94. EditorOriginUIContainer.visible = EditorOriginShow;
  95. if (viewportMode!=VIEWPORT_SINGLE)
  96. EditorOriginUIContainer.visible = false;
  97. // Forced read nodes for some reason:
  98. if ((originsNodes.length < 1) || rebuildSceneOrigins)
  99. {
  100. originsNodes = editorScene.GetChildren(true);
  101. // If we are hot have free origins icons in arrays, resize x 2
  102. if (originsIcons.length < originsNodes.length)
  103. {
  104. EditorOriginUIContainer.RemoveAllChildren();
  105. originsIcons.Clear();
  106. originsNames.Clear();
  107. originsIcons.Resize(originsNodes.length * 2);
  108. originsNames.Resize(originsNodes.length * 2);
  109. if (originsIcons.length > 0)
  110. {
  111. for (int i=0; i < originsIcons.length; i++)
  112. {
  113. CreateOrigin(i, false);
  114. }
  115. }
  116. }
  117. // If this rebuild pass after new scene loading or add/delete node - reset flag to default
  118. if (rebuildSceneOrigins)
  119. rebuildSceneOrigins = false;
  120. }
  121. if (originsNodes.length > 0)
  122. {
  123. // Get selected node for feeding proper arrray's UIElements with slyte colorig and additional info on ALT
  124. Node@ selectedNode = null;
  125. if (selectedNodes.length > 0)
  126. {
  127. selectedNode = selectedNodes[0];
  128. }
  129. else if (selectedComponents.length > 0)
  130. {
  131. selectedNode = selectedComponents[0].node;
  132. }
  133. // Update existed origins (every 10 ms)
  134. if (originsNodes.length > 0 )
  135. {
  136. for (int i=0; i < originsNodes.length; i++)
  137. {
  138. Vector3 eyeDir = originsNodes[i].worldPosition - cameraNode.worldPosition;
  139. float distance = (eyeDir).length;
  140. eyeDir.Normalize();
  141. Vector3 cameraDir = (cameraNode.worldRotation * Vector3(0.0f, 0.0f, 1.0f)).Normalized();
  142. float angleCameraDirVsDirToNode = eyeDir.DotProduct(cameraDir);
  143. // if node in range and in camera view (clip back sibe)
  144. if (distance < ORIGINS_VISIBLITY_RANGE && angleCameraDirVsDirToNode > 0.7f)
  145. {
  146. // turn on origin and move
  147. MoveOrigin(i, true);
  148. if (isThisNodeOneOfSelected(originsNodes[i]))
  149. {
  150. ShowSelectedNodeOrigin(originsNodes[i], i);
  151. originsNames[i].visible = true;
  152. }
  153. else
  154. {
  155. if (showNamesForAll || (isOriginsHovered && originHoveredIndex == i))
  156. originsNames[i].text = NodeInfo(originsNodes[i], selectedNodeInfoState);
  157. }
  158. }
  159. else
  160. {
  161. // turn-off origin
  162. VisibilityOrigin(i, false);
  163. }
  164. }
  165. // Hide non used origins
  166. for (int j=originsNodes.length; j < originsIcons.length; j++)
  167. {
  168. VisibilityOrigin(j, false);
  169. }
  170. }
  171. }
  172. }
  173. EditorOriginUITimeToUpdate = time.systemTime + ORIGIN_STEP_UPDATE;
  174. }
  175. bool isThisNodeOneOfSelected(Node@ node)
  176. {
  177. if (selectedNodes.length < 1) return false;
  178. for (int i = 0; i < selectedNodes.length; i++)
  179. {
  180. if (node is selectedNodes[i])
  181. return true;
  182. }
  183. return false;
  184. }
  185. void ShowSelectedNodeOrigin(Node@ node, int index)
  186. {
  187. if (node !is null)
  188. {
  189. // just keep node's text and node's origin icon position in actual view
  190. Viewport@ vp = activeViewport.viewport;
  191. Vector2 sp = activeViewport.camera.WorldToScreenPoint(node.worldPosition);
  192. //originsIcons[index].position = IntVector2(10+int(vp.rect.left + sp.x * vp.rect.right), -5 + int(vp.rect.top + sp.y* vp.rect.bottom));
  193. originsIcons[index].position = IntVector2(int(vp.rect.left + sp.x * vp.rect.right) - ORIGINOFFSETICONSELECTED.x, int(vp.rect.top + sp.y* vp.rect.bottom) - ORIGINOFFSETICONSELECTED.y);
  194. originsNames[index].color = ORIGIN_COLOR_SELECTED_TEXT;
  195. if (originsNodes[index].enabled)
  196. originsIcons[index].color = ORIGIN_COLOR_SELECTED;
  197. else
  198. originsIcons[index].color = ORIGIN_COLOR_DISABLED;
  199. originsIcons[index].SetFixedSize(ORIGIN_ICON_SIZE_SELECTED.x,ORIGIN_ICON_SIZE_SELECTED.y);
  200. // if selected node chaged, reset some vars
  201. if (prevSelectedID != node.id)
  202. {
  203. prevSelectedID = node.id;
  204. selectedNodeInfoState = 0;
  205. originsIcons[index].vars[ORIGIN_NODEID_VAR] = node.id;
  206. }
  207. // We always update to keep and feed alt-info with actual info about node components
  208. Array<Component@> components = node.GetComponents();
  209. Array<String> componentsShortInfo;
  210. Array<String> componentsDetailInfo;
  211. componentsShortInfo.Resize(components.length);
  212. componentsDetailInfo.Resize(components.length);
  213. // Add std info node name + tags
  214. originsNames[index].text = NodeInfo(node, selectedNodeInfoState) + "\n";
  215. }
  216. }
  217. void CreateOrigin(int index, bool isVisible = false)
  218. {
  219. if (originsIcons.length < index) return;
  220. originsIcons[index] = BorderImage("Icon");
  221. originsIcons[index].temporary = true;
  222. originsIcons[index].SetFixedSize(ORIGIN_ICON_SIZE.x,ORIGIN_ICON_SIZE.y);
  223. originsIcons[index].texture = cache.GetResource("Texture2D", "Textures/Editor/EditorIcons.png");
  224. originsIcons[index].imageRect = IntRect(0,0,14,14);
  225. originsIcons[index].priority = -1000;
  226. originsIcons[index].color = ORIGIN_COLOR;
  227. originsIcons[index].bringToBack = true;
  228. originsIcons[index].enabled = true;
  229. originsIcons[index].selected = true;
  230. originsIcons[index].visible = isVisible;
  231. EditorOriginUIContainer.AddChild(originsIcons[index]);
  232. originsNames[index] = Text();
  233. originsNames[index].visible = false;
  234. originsNames[index].SetFont(cache.GetResource("Font", "Fonts/Anonymous Pro.ttf"), NAMES_SIZE);
  235. originsNames[index].color = ORIGIN_COLOR_TEXT;
  236. //originsNames[index].textEffect = TE_STROKE;
  237. originsNames[index].temporary = true;
  238. originsNames[index].bringToBack = true;
  239. originsNames[index].priority = -1000;
  240. originsNames[index].enabled = false;
  241. EditorOriginUIContainer.AddChild(originsNames[index]);
  242. }
  243. void MoveOrigin(int index, bool isVisible = false)
  244. {
  245. if (originsIcons.length < index) return;
  246. if (originsIcons[index] is null) return;
  247. if (originsNodes[index].temporary)
  248. {
  249. originsIcons[index].visible = false;
  250. originsNames[index].visible = false;
  251. return;
  252. }
  253. Viewport@ vp = activeViewport.viewport;
  254. Vector2 sp = activeViewport.camera.WorldToScreenPoint(originsNodes[index].worldPosition);
  255. originsIcons[index].SetFixedSize(ORIGIN_ICON_SIZE.x,ORIGIN_ICON_SIZE.y);
  256. if (originsNodes[index].enabled)
  257. originsIcons[index].color = ORIGIN_COLOR;
  258. else
  259. originsIcons[index].color = ORIGIN_COLOR_DISABLED;
  260. originsIcons[index].position = IntVector2(int(vp.rect.left + sp.x * vp.rect.right) - ORIGINOFFSETICON.x, int(vp.rect.top + sp.y* vp.rect.bottom) - ORIGINOFFSETICON.y);
  261. originsIcons[index].visible = isVisible;
  262. originsIcons[index].vars[ORIGIN_NODEID_VAR] = originsNodes[index].id;
  263. originsNames[index].position = IntVector2(10+int(vp.rect.left + sp.x * vp.rect.right), -5 + int(vp.rect.top + sp.y* vp.rect.bottom));
  264. if (isOriginsHovered && originHoveredIndex == index)
  265. {
  266. originsNames[index].visible = true;
  267. originsNames[index].color = ORIGIN_COLOR_SELECTED_TEXT;
  268. }
  269. else
  270. {
  271. originsNames[index].visible = showNamesForAll ? isVisible : false;
  272. originsNames[index].color = ORIGIN_COLOR_TEXT;
  273. }
  274. }
  275. void VisibilityOrigin(int index, bool isVisible = false)
  276. {
  277. originsIcons[index].visible = isVisible;
  278. originsNames[index].visible = isVisible;
  279. }
  280. bool IsSceneOrigin(UIElement@ element)
  281. {
  282. if (originsIcons.length < 1) return false;
  283. for (int i=0; i < originsIcons.length; i++)
  284. {
  285. if (element is originsIcons[i])
  286. {
  287. originHoveredIndex = i;
  288. return true;
  289. }
  290. }
  291. originHoveredIndex = -1;
  292. return false;
  293. }
  294. void CheckKeyboardQualifers()
  295. {
  296. // if pressed alt we inc state for info
  297. bool showAltInfo = input.keyPress[KEY_ALT];
  298. if (showAltInfo)
  299. if (selectedNodeInfoState < 3) selectedNodeInfoState += 1;
  300. // if pressed ctrl we reset info state
  301. bool hideAltInfo = input.qualifierDown[QUAL_CTRL];
  302. if (hideAltInfo)
  303. selectedNodeInfoState = 0;
  304. bool showNameForOther = false;
  305. // In-B.mode Key_Space are busy by quick menu, so we use other key for B.mode
  306. if (hotKeyMode == HOTKEYS_MODE_BLENDER)
  307. showNameForOther = (input.keyPress[KEY_TAB] && ui.focusElement is null);
  308. else
  309. showNameForOther = (input.keyPress[KEY_SPACE] && ui.focusElement is null);
  310. if (showNameForOther)
  311. showNamesForAll =!showNamesForAll;
  312. }
  313. String NodeInfo(Node& node, int st)
  314. {
  315. String result = "";
  316. if (node !is editorScene)
  317. {
  318. if (node.name.empty)
  319. result = "Node";
  320. else
  321. result = node.name;
  322. // Add node's tags if wey are exist
  323. if (st > 0 && node.tags.length > 0)
  324. {
  325. result = result + "\n[";
  326. for (int i=0;i<node.tags.length; i++)
  327. {
  328. result = result + " " + node.tags[i];
  329. }
  330. result = result + " ] ";
  331. }
  332. }
  333. else
  334. result = "Scene Origin";
  335. return result;
  336. }
  337. void HandleSceneLoadedForOrigins()
  338. {
  339. rebuildSceneOrigins = true;
  340. }
  341. void HandleOriginsHoverBegin(StringHash eventType, VariantMap& eventData)
  342. {
  343. UIElement@ origin = eventData["Element"].GetPtr();
  344. if (origin is null)
  345. return;
  346. if (IsSceneOrigin(origin))
  347. {
  348. VariantMap data;
  349. data["Element"] = originsIcons[originHoveredIndex];
  350. data["Id"] = originHoveredIndex;
  351. data["NodeId"] = originsIcons[originHoveredIndex].vars[ORIGIN_NODEID_VAR].GetInt();
  352. SendEvent(EDITOR_EVENT_ORIGIN_START_HOVER, data);
  353. isOriginsHovered = true;
  354. }
  355. }
  356. void HandleOriginsHoverEnd(StringHash eventType, VariantMap& eventData)
  357. {
  358. UIElement@ origin = eventData["Element"].GetPtr();
  359. if (origin is null)
  360. return;
  361. if (IsSceneOrigin(origin))
  362. {
  363. VariantMap data;
  364. data["Element"] = originsIcons[originHoveredIndex];
  365. data["Id"] = originHoveredIndex;
  366. data["NodeId"] = originsIcons[originHoveredIndex].vars[ORIGIN_NODEID_VAR].GetInt();
  367. SendEvent(EDITOR_EVENT_ORIGIN_END_HOVER, data);
  368. isOriginsHovered = false;
  369. }
  370. }