EditorScene.as 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929
  1. // Urho3D editor scene handling
  2. #include "Scripts/Editor/EditorHierarchyWindow.as"
  3. #include "Scripts/Editor/EditorInspectorWindow.as"
  4. const int PICK_GEOMETRIES = 0;
  5. const int PICK_LIGHTS = 1;
  6. const int PICK_ZONES = 2;
  7. const int PICK_RIGIDBODIES = 3;
  8. const int PICK_UI_ELEMENTS = 4;
  9. const int MAX_PICK_MODES = 5;
  10. const int MAX_UNDOSTACK_SIZE = 256;
  11. Scene@ editorScene;
  12. String instantiateFileName;
  13. CreateMode instantiateMode = REPLICATED;
  14. bool sceneModified = false;
  15. bool runUpdate = false;
  16. Array<Node@> selectedNodes;
  17. Array<Component@> selectedComponents;
  18. Node@ editNode;
  19. Array<Node@> editNodes;
  20. Array<Component@> editComponents;
  21. uint numEditableComponentsPerNode = 1;
  22. Array<XMLFile@> sceneCopyBuffer;
  23. bool suppressSceneChanges = false;
  24. bool inSelectionModify = false;
  25. bool skipMruScene = false;
  26. Array<EditActionGroup> undoStack;
  27. uint undoStackPos = 0;
  28. bool revertOnPause = false;
  29. XMLFile@ revertData;
  30. void ClearSceneSelection()
  31. {
  32. selectedNodes.Clear();
  33. selectedComponents.Clear();
  34. editNode = null;
  35. editNodes.Clear();
  36. editComponents.Clear();
  37. numEditableComponentsPerNode = 1;
  38. HideGizmo();
  39. }
  40. void CreateScene()
  41. {
  42. // Create a scene only once here
  43. editorScene = Scene();
  44. // Allow access to the scene from the console
  45. script.defaultScene = editorScene;
  46. // Always pause the scene, and do updates manually
  47. editorScene.updateEnabled = false;
  48. }
  49. bool ResetScene()
  50. {
  51. ui.cursor.shape = CS_BUSY;
  52. if (messageBoxCallback is null && sceneModified)
  53. {
  54. MessageBox@ messageBox = MessageBox("Scene has been modified.\nContinue to reset?", "Warning");
  55. if (messageBox.window !is null)
  56. {
  57. Button@ cancelButton = messageBox.window.GetChild("CancelButton", true);
  58. cancelButton.visible = true;
  59. cancelButton.focus = true;
  60. SubscribeToEvent(messageBox, "MessageACK", "HandleMessageAcknowledgement");
  61. messageBoxCallback = @ResetScene;
  62. return false;
  63. }
  64. }
  65. else
  66. messageBoxCallback = null;
  67. suppressSceneChanges = true;
  68. // Create a scene with default values, these will be overridden when loading scenes
  69. editorScene.Clear();
  70. editorScene.CreateComponent("Octree");
  71. editorScene.CreateComponent("PhysicsWorld");
  72. editorScene.CreateComponent("DebugRenderer");
  73. // Release resources that became unused after the scene clear
  74. cache.ReleaseAllResources(false);
  75. sceneModified = false;
  76. revertData = null;
  77. StopSceneUpdate();
  78. UpdateWindowTitle();
  79. UpdateHierarchyItem(editorScene, true);
  80. ClearEditActions();
  81. suppressSceneChanges = false;
  82. ResetCamera();
  83. CreateGizmo();
  84. CreateGrid();
  85. SetActiveViewport(viewports[0]);
  86. return true;
  87. }
  88. void SetResourcePath(String newPath, bool usePreferredDir = true, bool additive = false)
  89. {
  90. if (newPath.empty)
  91. return;
  92. if (usePreferredDir)
  93. newPath = AddTrailingSlash(cache.GetPreferredResourceDir(newPath));
  94. else
  95. newPath = AddTrailingSlash(newPath);
  96. if (newPath == sceneResourcePath)
  97. return;
  98. // Remove the old scene resource path if any. However make sure that the default data paths do not get removed
  99. if (!additive)
  100. {
  101. cache.ReleaseAllResources(false);
  102. renderer.ReloadShaders();
  103. String check = AddTrailingSlash(sceneResourcePath);
  104. bool isDefaultResourcePath = check.Compare(fileSystem.programDir + "Data/", false) == 0 ||
  105. check.Compare(fileSystem.programDir + "CoreData/", false) == 0;
  106. if (!sceneResourcePath.empty && !isDefaultResourcePath)
  107. cache.RemoveResourceDir(sceneResourcePath);
  108. }
  109. cache.AddResourceDir(newPath);
  110. if (!additive)
  111. {
  112. sceneResourcePath = newPath;
  113. uiScenePath = GetResourceSubPath(newPath, "Scenes");
  114. uiElementPath = GetResourceSubPath(newPath, "UI");
  115. uiNodePath = GetResourceSubPath(newPath, "Objects");
  116. uiScriptPath = GetResourceSubPath(newPath, "Scripts");
  117. uiParticlePath = GetResourceSubPath(newPath, "Particle");
  118. }
  119. }
  120. String GetResourceSubPath(String basePath, const String&in subPath)
  121. {
  122. basePath = AddTrailingSlash(basePath);
  123. if (fileSystem.DirExists(basePath + subPath))
  124. return AddTrailingSlash(basePath + subPath);
  125. else
  126. return basePath;
  127. }
  128. bool LoadScene(const String&in fileName)
  129. {
  130. if (fileName.empty)
  131. return false;
  132. ui.cursor.shape = CS_BUSY;
  133. // Always load the scene from the filesystem, not from resource paths
  134. if (!fileSystem.FileExists(fileName))
  135. {
  136. MessageBox("No such scene.\n" + fileName);
  137. return false;
  138. }
  139. File file(fileName, FILE_READ);
  140. if (!file.open)
  141. {
  142. MessageBox("Could not open file.\n" + fileName);
  143. return false;
  144. }
  145. // Add the scene's resource path in case it's necessary
  146. String newScenePath = GetPath(fileName);
  147. if (!rememberResourcePath || !sceneResourcePath.StartsWith(newScenePath, false))
  148. SetResourcePath(newScenePath);
  149. suppressSceneChanges = true;
  150. sceneModified = false;
  151. revertData = null;
  152. StopSceneUpdate();
  153. String extension = GetExtension(fileName);
  154. bool loaded;
  155. if (extension != ".xml")
  156. loaded = editorScene.Load(file);
  157. else
  158. loaded = editorScene.LoadXML(file);
  159. // Release resources which are not used by the new scene
  160. cache.ReleaseAllResources(false);
  161. // Always pause the scene, and do updates manually
  162. editorScene.updateEnabled = false;
  163. UpdateWindowTitle();
  164. UpdateHierarchyItem(editorScene, true);
  165. ClearEditActions();
  166. suppressSceneChanges = false;
  167. // global variable to mostly bypass adding mru upon importing tempscene
  168. if (!skipMruScene)
  169. UpdateSceneMru(fileName);
  170. skipMruScene = false;
  171. ResetCamera();
  172. CreateGizmo();
  173. CreateGrid();
  174. SetActiveViewport(viewports[0]);
  175. return loaded;
  176. }
  177. bool SaveScene(const String&in fileName)
  178. {
  179. if (fileName.empty)
  180. return false;
  181. ui.cursor.shape = CS_BUSY;
  182. // Unpause when saving so that the scene will work properly when loaded outside the editor
  183. editorScene.updateEnabled = true;
  184. File file(fileName, FILE_WRITE);
  185. String extension = GetExtension(fileName);
  186. bool success = (extension != ".xml" ? editorScene.Save(file) : editorScene.SaveXML(file));
  187. editorScene.updateEnabled = false;
  188. if (success)
  189. {
  190. UpdateSceneMru(fileName);
  191. sceneModified = false;
  192. UpdateWindowTitle();
  193. }
  194. else
  195. MessageBox("Could not save scene successfully!\nSee Urho3D.log for more detail.");
  196. return success;
  197. }
  198. bool SaveSceneWithExistingName()
  199. {
  200. if (editorScene.fileName.empty || editorScene.fileName == TEMP_SCENE_NAME)
  201. return PickFile();
  202. else
  203. return SaveScene(editorScene.fileName);
  204. }
  205. void CreateNode(CreateMode mode)
  206. {
  207. Node@ newNode = editorScene.CreateChild("", mode);
  208. // Set the new node a certain distance from the camera
  209. newNode.position = GetNewNodePosition();
  210. // Create an undo action for the create
  211. CreateNodeAction action;
  212. action.Define(newNode);
  213. SaveEditAction(action);
  214. SetSceneModified();
  215. FocusNode(newNode);
  216. }
  217. void CreateComponent(const String&in componentType)
  218. {
  219. // If this is the root node, do not allow to create duplicate scene-global components
  220. if (editNode is editorScene && CheckForExistingGlobalComponent(editNode, componentType))
  221. return;
  222. // Group for storing undo actions
  223. EditActionGroup group;
  224. // For now, make a local node's all components local
  225. /// \todo Allow to specify the createmode
  226. for (uint i = 0; i < editNodes.length; ++i)
  227. {
  228. Component@ newComponent = editNodes[i].CreateComponent(componentType, editNodes[i].id < FIRST_LOCAL_ID ? REPLICATED : LOCAL);
  229. if (newComponent !is null)
  230. {
  231. // Some components such as CollisionShape do not create their internal object before the first call to ApplyAttributes()
  232. // to prevent unnecessary initialization with default values. Call now
  233. newComponent.ApplyAttributes();
  234. CreateComponentAction action;
  235. action.Define(newComponent);
  236. group.actions.Push(action);
  237. }
  238. }
  239. SaveEditActionGroup(group);
  240. SetSceneModified();
  241. // Although the edit nodes selection are not changed, call to ensure attribute inspector notices new components of the edit nodes
  242. HandleHierarchyListSelectionChange();
  243. }
  244. void LoadNode(const String&in fileName)
  245. {
  246. if (fileName.empty)
  247. return;
  248. if (!fileSystem.FileExists(fileName))
  249. {
  250. MessageBox("No such node file.\n" + fileName);
  251. return;
  252. }
  253. File file(fileName, FILE_READ);
  254. if (!file.open)
  255. {
  256. MessageBox("Could not open file.\n" + fileName);
  257. return;
  258. }
  259. ui.cursor.shape = CS_BUSY;
  260. // Before instantiating, add object's resource path if necessary
  261. SetResourcePath(GetPath(fileName), true, true);
  262. Vector3 position = GetNewNodePosition();
  263. Node@ newNode;
  264. String extension = GetExtension(fileName);
  265. if (extension != ".xml")
  266. newNode = editorScene.Instantiate(file, position, Quaternion(), instantiateMode);
  267. else
  268. newNode = editorScene.InstantiateXML(file, position, Quaternion(), instantiateMode);
  269. if (newNode !is null)
  270. {
  271. // Create an undo action for the load
  272. CreateNodeAction action;
  273. action.Define(newNode);
  274. SaveEditAction(action);
  275. SetSceneModified();
  276. FocusNode(newNode);
  277. instantiateFileName = fileName;
  278. }
  279. }
  280. bool SaveNode(const String&in fileName)
  281. {
  282. if (fileName.empty)
  283. return false;
  284. ui.cursor.shape = CS_BUSY;
  285. File file(fileName, FILE_WRITE);
  286. if (!file.open)
  287. {
  288. MessageBox("Could not open file.\n" + fileName);
  289. return false;
  290. }
  291. String extension = GetExtension(fileName);
  292. bool success = (extension != ".xml" ? editNode.Save(file) : editNode.SaveXML(file));
  293. if (success)
  294. instantiateFileName = fileName;
  295. else
  296. MessageBox("Could not save node successfully!\nSee Urho3D.log for more detail.");
  297. return success;
  298. }
  299. void UpdateScene(float timeStep)
  300. {
  301. if (runUpdate)
  302. editorScene.Update(timeStep);
  303. }
  304. void StopSceneUpdate()
  305. {
  306. runUpdate = false;
  307. audio.Stop();
  308. toolBarDirty = true;
  309. // If scene should revert on update stop, load saved data now
  310. if (revertOnPause && revertData !is null)
  311. {
  312. suppressSceneChanges = true;
  313. editorScene.Clear();
  314. editorScene.LoadXML(revertData.GetRoot());
  315. UpdateHierarchyItem(editorScene, true);
  316. ClearEditActions();
  317. suppressSceneChanges = false;
  318. }
  319. revertData = null;
  320. }
  321. void StartSceneUpdate()
  322. {
  323. runUpdate = true;
  324. // Run audio playback only when scene is updating, so that audio components' time-dependent attributes stay constant when
  325. // paused (similar to physics)
  326. audio.Play();
  327. toolBarDirty = true;
  328. // Save scene data for reverting if enabled
  329. if (revertOnPause)
  330. {
  331. revertData = XMLFile();
  332. XMLElement root = revertData.CreateRoot("scene");
  333. editorScene.SaveXML(root);
  334. }
  335. else
  336. revertData = null;
  337. }
  338. bool ToggleSceneUpdate()
  339. {
  340. if (!runUpdate)
  341. StartSceneUpdate();
  342. else
  343. StopSceneUpdate();
  344. return true;
  345. }
  346. void SetSceneModified()
  347. {
  348. if (!sceneModified)
  349. {
  350. sceneModified = true;
  351. UpdateWindowTitle();
  352. }
  353. }
  354. bool SceneDelete()
  355. {
  356. ui.cursor.shape = CS_BUSY;
  357. BeginSelectionModify();
  358. // Clear the selection now to prevent repopulation of selectedNodes and selectedComponents combo
  359. hierarchyList.ClearSelection();
  360. // Group for storing undo actions
  361. EditActionGroup group;
  362. // Remove nodes
  363. for (uint i = 0; i < selectedNodes.length; ++i)
  364. {
  365. Node@ node = selectedNodes[i];
  366. if (node.parent is null || node.scene is null)
  367. continue; // Root or already deleted
  368. uint nodeIndex = GetListIndex(node);
  369. // Create undo action
  370. DeleteNodeAction action;
  371. action.Define(node);
  372. group.actions.Push(action);
  373. node.Remove();
  374. SetSceneModified();
  375. // If deleting only one node, select the next item in the same index
  376. if (selectedNodes.length == 1 && selectedComponents.empty)
  377. hierarchyList.selection = nodeIndex;
  378. }
  379. // Then remove components, if they still remain
  380. for (uint i = 0; i < selectedComponents.length; ++i)
  381. {
  382. Component@ component = selectedComponents[i];
  383. Node@ node = component.node;
  384. if (node is null)
  385. continue; // Already deleted
  386. uint index = GetComponentListIndex(component);
  387. uint nodeIndex = GetListIndex(node);
  388. if (index == NO_ITEM || nodeIndex == NO_ITEM)
  389. continue;
  390. // Do not allow to remove the Octree, PhysicsWorld or DebugRenderer from the root node
  391. if (node is editorScene && (component.typeName == "Octree" || component.typeName == "PhysicsWorld" ||
  392. component.typeName == "DebugRenderer"))
  393. continue;
  394. // Create undo action
  395. DeleteComponentAction action;
  396. action.Define(component);
  397. group.actions.Push(action);
  398. node.RemoveComponent(component);
  399. SetSceneModified();
  400. // If deleting only one component, select the next item in the same index
  401. if (selectedComponents.length == 1 && selectedNodes.empty)
  402. hierarchyList.selection = index;
  403. }
  404. SaveEditActionGroup(group);
  405. EndSelectionModify();
  406. return true;
  407. }
  408. bool SceneCut()
  409. {
  410. return SceneCopy() && SceneDelete();
  411. }
  412. bool SceneCopy()
  413. {
  414. ui.cursor.shape = CS_BUSY;
  415. sceneCopyBuffer.Clear();
  416. // Copy components
  417. if (!selectedComponents.empty)
  418. {
  419. for (uint i = 0; i < selectedComponents.length; ++i)
  420. {
  421. XMLFile@ xml = XMLFile();
  422. XMLElement rootElem = xml.CreateRoot("component");
  423. selectedComponents[i].SaveXML(rootElem);
  424. rootElem.SetBool("local", selectedComponents[i].id >= FIRST_LOCAL_ID);
  425. sceneCopyBuffer.Push(xml);
  426. }
  427. }
  428. // Copy nodes.
  429. else
  430. {
  431. for (uint i = 0; i < selectedNodes.length; ++i)
  432. {
  433. // Skip the root scene node as it cannot be copied
  434. if (selectedNodes[i] is editorScene)
  435. continue;
  436. XMLFile@ xml = XMLFile();
  437. XMLElement rootElem = xml.CreateRoot("node");
  438. selectedNodes[i].SaveXML(rootElem);
  439. rootElem.SetBool("local", selectedNodes[i].id >= FIRST_LOCAL_ID);
  440. sceneCopyBuffer.Push(xml);
  441. }
  442. }
  443. return true;
  444. }
  445. bool ScenePaste()
  446. {
  447. ui.cursor.shape = CS_BUSY;
  448. // Group for storing undo actions
  449. EditActionGroup group;
  450. for (uint i = 0; i < sceneCopyBuffer.length; ++i)
  451. {
  452. XMLElement rootElem = sceneCopyBuffer[i].root;
  453. String mode = rootElem.name;
  454. if (mode == "component")
  455. {
  456. // If this is the root node, do not allow to create duplicate scene-global components
  457. if (editNode is editorScene && CheckForExistingGlobalComponent(editNode, rootElem.GetAttribute("type")))
  458. return false;
  459. // If copied component was local, make the new local too
  460. Component@ newComponent = editNode.CreateComponent(rootElem.GetAttribute("type"), rootElem.GetBool("local") ? LOCAL :
  461. REPLICATED);
  462. if (newComponent is null)
  463. return false;
  464. newComponent.LoadXML(rootElem);
  465. newComponent.ApplyAttributes();
  466. // Create an undo action
  467. CreateComponentAction action;
  468. action.Define(newComponent);
  469. group.actions.Push(action);
  470. }
  471. else if (mode == "node")
  472. {
  473. // Make the paste go always to the root node, no matter of the selected node
  474. // If copied node was local, make the new local too
  475. Node@ newNode = editorScene.CreateChild("", rootElem.GetBool("local") ? LOCAL : REPLICATED);
  476. newNode.LoadXML(rootElem);
  477. // Create an undo action
  478. CreateNodeAction action;
  479. action.Define(newNode);
  480. group.actions.Push(action);
  481. }
  482. }
  483. SaveEditActionGroup(group);
  484. SetSceneModified();
  485. return true;
  486. }
  487. bool SceneUnparent()
  488. {
  489. if (!CheckHierarchyWindowFocus() || !selectedComponents.empty || selectedNodes.empty)
  490. return false;
  491. ui.cursor.shape = CS_BUSY;
  492. // Group for storing undo actions
  493. EditActionGroup group;
  494. // Parent selected nodes to root
  495. Array<Node@> changedNodes;
  496. for (uint i = 0; i < selectedNodes.length; ++i)
  497. {
  498. Node@ sourceNode = selectedNodes[i];
  499. if (sourceNode.parent is null || sourceNode.parent is editorScene)
  500. continue; // Root or already parented to root
  501. // Perform the reparenting, continue loop even if action fails
  502. ReparentNodeAction action;
  503. action.Define(sourceNode, editorScene);
  504. group.actions.Push(action);
  505. SceneChangeParent(sourceNode, editorScene, false);
  506. changedNodes.Push(sourceNode);
  507. }
  508. // Reselect the changed nodes at their new position in the list
  509. for (uint i = 0; i < changedNodes.length; ++i)
  510. hierarchyList.AddSelection(GetListIndex(changedNodes[i]));
  511. SaveEditActionGroup(group);
  512. SetSceneModified();
  513. return true;
  514. }
  515. bool SceneToggleEnable()
  516. {
  517. if (!CheckHierarchyWindowFocus())
  518. return false;
  519. ui.cursor.shape = CS_BUSY;
  520. EditActionGroup group;
  521. // Toggle enabled state of nodes recursively
  522. for (uint i = 0; i < selectedNodes.length; ++i)
  523. {
  524. // Do not attempt to disable the Scene
  525. if (selectedNodes[i].typeName == "Node")
  526. {
  527. bool oldEnabled = selectedNodes[i].enabled;
  528. selectedNodes[i].SetEnabled(!oldEnabled, true);
  529. // Create undo action
  530. ToggleNodeEnabledAction action;
  531. action.Define(selectedNodes[i], oldEnabled);
  532. group.actions.Push(action);
  533. }
  534. }
  535. for (uint i = 0; i < selectedComponents.length; ++i)
  536. {
  537. // Some components purposefully do not expose the Enabled attribute, and it does not affect them in any way
  538. // (Octree, PhysicsWorld). Check that the first attribute is in fact called "Is Enabled"
  539. if (selectedComponents[i].numAttributes > 0 && selectedComponents[i].attributeInfos[0].name == "Is Enabled")
  540. {
  541. bool oldEnabled = selectedComponents[i].enabled;
  542. selectedComponents[i].enabled = !oldEnabled;
  543. // Create undo action
  544. EditAttributeAction action;
  545. action.Define(selectedComponents[i], 0, Variant(oldEnabled));
  546. group.actions.Push(action);
  547. }
  548. }
  549. SaveEditActionGroup(group);
  550. SetSceneModified();
  551. return true;
  552. }
  553. bool SceneChangeParent(Node@ sourceNode, Node@ targetNode, bool createUndoAction = true)
  554. {
  555. // Create undo action if requested
  556. if (createUndoAction)
  557. {
  558. ReparentNodeAction action;
  559. action.Define(sourceNode, targetNode);
  560. SaveEditAction(action);
  561. }
  562. sourceNode.parent = targetNode;
  563. SetSceneModified();
  564. // Return true if success
  565. if (sourceNode.parent is targetNode)
  566. {
  567. UpdateNodeAttributes(); // Parent change may have changed local transform
  568. return true;
  569. }
  570. else
  571. return false;
  572. }
  573. bool SceneChangeParent(Node@ sourceNode, Array<Node@> sourceNodes, Node@ targetNode, bool createUndoAction = true)
  574. {
  575. // Create undo action if requested
  576. if (createUndoAction)
  577. {
  578. ReparentNodeAction action;
  579. action.Define(sourceNodes, targetNode);
  580. SaveEditAction(action);
  581. }
  582. for (uint i = 0; i < sourceNodes.length; i++)
  583. {
  584. Node@ node = sourceNodes[i];
  585. node.parent = targetNode;
  586. }
  587. SetSceneModified();
  588. // Return true if success
  589. if (sourceNode.parent is targetNode)
  590. {
  591. UpdateNodeAttributes(); // Parent change may have changed local transform
  592. return true;
  593. }
  594. else
  595. return false;
  596. }
  597. bool SceneResetPosition()
  598. {
  599. if (editNode !is null)
  600. {
  601. Transform oldTransform;
  602. oldTransform.Define(editNode);
  603. editNode.position = Vector3(0.0, 0.0, 0.0);
  604. // Create undo action
  605. EditNodeTransformAction action;
  606. action.Define(editNode, oldTransform);
  607. SaveEditAction(action);
  608. SetSceneModified();
  609. UpdateNodeAttributes();
  610. return true;
  611. }
  612. else
  613. return false;
  614. }
  615. bool SceneResetRotation()
  616. {
  617. if (editNode !is null)
  618. {
  619. Transform oldTransform;
  620. oldTransform.Define(editNode);
  621. editNode.rotation = Quaternion();
  622. // Create undo action
  623. EditNodeTransformAction action;
  624. action.Define(editNode, oldTransform);
  625. SaveEditAction(action);
  626. SetSceneModified();
  627. UpdateNodeAttributes();
  628. return true;
  629. }
  630. else
  631. return false;
  632. }
  633. bool SceneResetScale()
  634. {
  635. if (editNode !is null)
  636. {
  637. Transform oldTransform;
  638. oldTransform.Define(editNode);
  639. editNode.scale = Vector3(1.0, 1.0, 1.0);
  640. // Create undo action
  641. EditNodeTransformAction action;
  642. action.Define(editNode, oldTransform);
  643. SaveEditAction(action);
  644. SetSceneModified();
  645. UpdateNodeAttributes();
  646. return true;
  647. }
  648. else
  649. return false;
  650. }
  651. bool SceneSelectAll()
  652. {
  653. BeginSelectionModify();
  654. Array<Node@> rootLevelNodes = editorScene.GetChildren();
  655. Array<uint> indices;
  656. for (uint i = 0; i < rootLevelNodes.length; ++i)
  657. indices.Push(GetListIndex(rootLevelNodes[i]));
  658. hierarchyList.SetSelections(indices);
  659. EndSelectionModify();
  660. return true;
  661. }
  662. bool SceneResetToDefault()
  663. {
  664. ui.cursor.shape = CS_BUSY;
  665. // Group for storing undo actions
  666. EditActionGroup group;
  667. // Reset selected component to their default
  668. if (!selectedComponents.empty)
  669. {
  670. for (uint i = 0; i < selectedComponents.length; ++i)
  671. {
  672. Component@ component = selectedComponents[i];
  673. ResetAttributesAction action;
  674. action.Define(component);
  675. group.actions.Push(action);
  676. component.ResetToDefault();
  677. component.ApplyAttributes();
  678. for (uint j = 0; j < component.numAttributes; ++j)
  679. PostEditAttribute(component, j);
  680. }
  681. }
  682. // OR reset selected nodes to their default
  683. else
  684. {
  685. for (uint i = 0; i < selectedNodes.length; ++i)
  686. {
  687. Node@ node = selectedNodes[i];
  688. ResetAttributesAction action;
  689. action.Define(node);
  690. group.actions.Push(action);
  691. node.ResetToDefault();
  692. node.ApplyAttributes();
  693. for (uint j = 0; j < node.numAttributes; ++j)
  694. PostEditAttribute(node, j);
  695. }
  696. }
  697. SaveEditActionGroup(group);
  698. SetSceneModified();
  699. attributesFullDirty = true;
  700. return true;
  701. }
  702. bool SceneRebuildNavigation()
  703. {
  704. ui.cursor.shape = CS_BUSY;
  705. Array<Component@>@ navMeshes = editorScene.GetComponents("NavigationMesh", true);
  706. bool success = true;
  707. for (uint i = 0; i < navMeshes.length; ++i)
  708. {
  709. NavigationMesh@ navMesh = navMeshes[i];
  710. if (!navMesh.Build())
  711. success = false;
  712. }
  713. return success;
  714. }
  715. bool LoadParticleData(const String&in fileName)
  716. {
  717. if (fileName.empty)
  718. return false;
  719. XMLFile xmlFile;
  720. if (!xmlFile.Load(File(fileName, FILE_READ)))
  721. return false;
  722. for (uint i = 0; i < editComponents.length; ++i)
  723. {
  724. ParticleEmitter@ emitter = cast<ParticleEmitter>(editComponents[i]);
  725. if (emitter !is null)
  726. emitter.Load(xmlFile);
  727. }
  728. return true;
  729. }
  730. bool SaveParticleData(const String&in fileName)
  731. {
  732. if (fileName.empty || editComponents.length != 1)
  733. return false;
  734. ParticleEmitter@ emitter = cast<ParticleEmitter>(editComponents[0]);
  735. if (emitter !is null)
  736. {
  737. XMLFile xmlFile;
  738. emitter.Save(xmlFile);
  739. return xmlFile.Save(File(fileName, FILE_WRITE));
  740. }
  741. return false;
  742. }
  743. void UpdateSceneMru(String filename)
  744. {
  745. while (uiRecentScenes.Find(filename) > -1)
  746. uiRecentScenes.Erase(uiRecentScenes.Find(filename));
  747. uiRecentScenes.Insert(0, filename);
  748. for(uint i=uiRecentScenes.length-1;i>=maxRecentSceneCount;i--)
  749. uiRecentScenes.Erase(i);
  750. PopulateMruScenes();
  751. }