EditorScene.as 24 KB

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