EditorScene.as 36 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295
  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. Node@ lastSelectedNode;
  17. Array<Node@> selectedNodes;
  18. Array<Component@> selectedComponents;
  19. Node@ editNode;
  20. Array<Node@> editNodes;
  21. Array<Component@> editComponents;
  22. uint numEditableComponentsPerNode = 1;
  23. Array<XMLFile@> sceneCopyBuffer;
  24. bool suppressSceneChanges = false;
  25. bool inSelectionModify = false;
  26. bool skipMruScene = false;
  27. Array<EditActionGroup> undoStack;
  28. uint undoStackPos = 0;
  29. bool revertOnPause = false;
  30. XMLFile@ revertData;
  31. void ClearSceneSelection()
  32. {
  33. selectedNodes.Clear();
  34. selectedComponents.Clear();
  35. editNode = null;
  36. editNodes.Clear();
  37. editComponents.Clear();
  38. numEditableComponentsPerNode = 1;
  39. HideGizmo();
  40. }
  41. void CreateScene()
  42. {
  43. // Create a scene only once here
  44. editorScene = Scene();
  45. // Allow access to the scene from the console
  46. script.defaultScene = editorScene;
  47. // Always pause the scene, and do updates manually
  48. editorScene.updateEnabled = false;
  49. }
  50. bool ResetScene()
  51. {
  52. ui.cursor.shape = CS_BUSY;
  53. if (messageBoxCallback is null && sceneModified)
  54. {
  55. MessageBox@ messageBox = MessageBox("Scene has been modified.\nContinue to reset?", "Warning");
  56. if (messageBox.window !is null)
  57. {
  58. Button@ cancelButton = messageBox.window.GetChild("CancelButton", true);
  59. cancelButton.visible = true;
  60. cancelButton.focus = true;
  61. SubscribeToEvent(messageBox, "MessageACK", "HandleMessageAcknowledgement");
  62. messageBoxCallback = @ResetScene;
  63. return false;
  64. }
  65. }
  66. else
  67. messageBoxCallback = null;
  68. // Clear stored script attributes
  69. scriptAttributes.Clear();
  70. suppressSceneChanges = true;
  71. // Create a scene with default values, these will be overridden when loading scenes
  72. editorScene.Clear();
  73. editorScene.CreateComponent("Octree");
  74. editorScene.CreateComponent("DebugRenderer");
  75. // Release resources that became unused after the scene clear
  76. cache.ReleaseAllResources(false);
  77. sceneModified = false;
  78. revertData = null;
  79. StopSceneUpdate();
  80. UpdateWindowTitle();
  81. DisableInspectorLock();
  82. UpdateHierarchyItem(editorScene, true);
  83. ClearEditActions();
  84. suppressSceneChanges = false;
  85. ResetCamera();
  86. CreateGizmo();
  87. CreateGrid();
  88. SetActiveViewport(viewports[0]);
  89. return true;
  90. }
  91. void SetResourcePath(String newPath, bool usePreferredDir = true, bool additive = false)
  92. {
  93. if (newPath.empty)
  94. return;
  95. if (!IsAbsolutePath(newPath))
  96. newPath = fileSystem.currentDir + newPath;
  97. if (usePreferredDir)
  98. newPath = AddTrailingSlash(cache.GetPreferredResourceDir(newPath));
  99. else
  100. newPath = AddTrailingSlash(newPath);
  101. if (newPath == sceneResourcePath)
  102. return;
  103. // Remove the old scene resource path if any. However make sure that the default data paths do not get removed
  104. if (!additive)
  105. {
  106. cache.ReleaseAllResources(false);
  107. renderer.ReloadShaders();
  108. String check = AddTrailingSlash(sceneResourcePath);
  109. bool isDefaultResourcePath = check.Compare(fileSystem.programDir + "Data/", false) == 0 ||
  110. check.Compare(fileSystem.programDir + "CoreData/", false) == 0;
  111. if (!sceneResourcePath.empty && !isDefaultResourcePath)
  112. cache.RemoveResourceDir(sceneResourcePath);
  113. }
  114. else
  115. {
  116. // If additive (path of a loaded prefab) check that the new path isn't already part of an old path
  117. Array<String>@ resourceDirs = cache.resourceDirs;
  118. for (uint i = 0; i < resourceDirs.length; ++i)
  119. {
  120. if (newPath.StartsWith(resourceDirs[i], false))
  121. return;
  122. }
  123. }
  124. // Add resource path as first priority so that it takes precedence over the default data paths
  125. cache.AddResourceDir(newPath, 0);
  126. RebuildResourceDatabase();
  127. if (!additive)
  128. {
  129. sceneResourcePath = newPath;
  130. uiScenePath = GetResourceSubPath(newPath, "Scenes");
  131. uiElementPath = GetResourceSubPath(newPath, "UI");
  132. uiNodePath = GetResourceSubPath(newPath, "Objects");
  133. uiScriptPath = GetResourceSubPath(newPath, "Scripts");
  134. uiParticlePath = GetResourceSubPath(newPath, "Particle");
  135. }
  136. }
  137. String GetResourceSubPath(String basePath, const String&in subPath)
  138. {
  139. basePath = AddTrailingSlash(basePath);
  140. if (fileSystem.DirExists(basePath + subPath))
  141. return AddTrailingSlash(basePath + subPath);
  142. else
  143. return basePath;
  144. }
  145. bool LoadScene(const String&in fileName)
  146. {
  147. if (fileName.empty)
  148. return false;
  149. ui.cursor.shape = CS_BUSY;
  150. // Always load the scene from the filesystem, not from resource paths
  151. if (!fileSystem.FileExists(fileName))
  152. {
  153. MessageBox("No such scene.\n" + fileName);
  154. return false;
  155. }
  156. File file(fileName, FILE_READ);
  157. if (!file.open)
  158. {
  159. MessageBox("Could not open file.\n" + fileName);
  160. return false;
  161. }
  162. // Reset stored script attributes.
  163. scriptAttributes.Clear();
  164. // Add the scene's resource path in case it's necessary
  165. String newScenePath = GetPath(fileName);
  166. if (!rememberResourcePath || !sceneResourcePath.StartsWith(newScenePath, false))
  167. SetResourcePath(newScenePath);
  168. suppressSceneChanges = true;
  169. sceneModified = false;
  170. revertData = null;
  171. StopSceneUpdate();
  172. String extension = GetExtension(fileName);
  173. bool loaded;
  174. if (extension != ".xml")
  175. loaded = editorScene.Load(file);
  176. else
  177. loaded = editorScene.LoadXML(file);
  178. // Release resources which are not used by the new scene
  179. cache.ReleaseAllResources(false);
  180. // Always pause the scene, and do updates manually
  181. editorScene.updateEnabled = false;
  182. UpdateWindowTitle();
  183. DisableInspectorLock();
  184. UpdateHierarchyItem(editorScene, true);
  185. ClearEditActions();
  186. suppressSceneChanges = false;
  187. // global variable to mostly bypass adding mru upon importing tempscene
  188. if (!skipMruScene)
  189. UpdateSceneMru(fileName);
  190. skipMruScene = false;
  191. ResetCamera();
  192. CreateGizmo();
  193. CreateGrid();
  194. SetActiveViewport(viewports[0]);
  195. // Store all ScriptInstance and LuaScriptInstance attributes
  196. UpdateScriptInstances();
  197. return loaded;
  198. }
  199. bool SaveScene(const String&in fileName)
  200. {
  201. if (fileName.empty)
  202. return false;
  203. ui.cursor.shape = CS_BUSY;
  204. // Unpause when saving so that the scene will work properly when loaded outside the editor
  205. editorScene.updateEnabled = true;
  206. MakeBackup(fileName);
  207. File file(fileName, FILE_WRITE);
  208. String extension = GetExtension(fileName);
  209. bool success = (extension != ".xml" ? editorScene.Save(file) : editorScene.SaveXML(file));
  210. RemoveBackup(success, fileName);
  211. editorScene.updateEnabled = false;
  212. if (success)
  213. {
  214. UpdateSceneMru(fileName);
  215. sceneModified = false;
  216. UpdateWindowTitle();
  217. }
  218. else
  219. MessageBox("Could not save scene successfully!\nSee Urho3D.log for more detail.");
  220. return success;
  221. }
  222. bool SaveSceneWithExistingName()
  223. {
  224. if (editorScene.fileName.empty || editorScene.fileName == TEMP_SCENE_NAME)
  225. return PickFile();
  226. else
  227. return SaveScene(editorScene.fileName);
  228. }
  229. Node@ CreateNode(CreateMode mode)
  230. {
  231. Node@ newNode = null;
  232. if (editNode !is null)
  233. newNode = editNode.CreateChild("", mode);
  234. else
  235. newNode = editorScene.CreateChild("", mode);
  236. newNode.worldPosition = GetNewNodePosition();
  237. // Create an undo action for the create
  238. CreateNodeAction action;
  239. action.Define(newNode);
  240. SaveEditAction(action);
  241. SetSceneModified();
  242. FocusNode(newNode);
  243. return newNode;
  244. }
  245. void CreateComponent(const String&in componentType)
  246. {
  247. // If this is the root node, do not allow to create duplicate scene-global components
  248. if (editNode is editorScene && CheckForExistingGlobalComponent(editNode, componentType))
  249. return;
  250. // Group for storing undo actions
  251. EditActionGroup group;
  252. // For now, make a local node's all components local
  253. /// \todo Allow to specify the createmode
  254. for (uint i = 0; i < editNodes.length; ++i)
  255. {
  256. Component@ newComponent = editNodes[i].CreateComponent(componentType, editNodes[i].id < FIRST_LOCAL_ID ? REPLICATED : LOCAL);
  257. if (newComponent !is null)
  258. {
  259. // Some components such as CollisionShape do not create their internal object before the first call to ApplyAttributes()
  260. // to prevent unnecessary initialization with default values. Call now
  261. newComponent.ApplyAttributes();
  262. CreateComponentAction action;
  263. action.Define(newComponent);
  264. group.actions.Push(action);
  265. }
  266. }
  267. SaveEditActionGroup(group);
  268. SetSceneModified();
  269. // Although the edit nodes selection are not changed, call to ensure attribute inspector notices new components of the edit nodes
  270. HandleHierarchyListSelectionChange();
  271. }
  272. void CreateLoadedComponent(Component@ component)
  273. {
  274. if (component is null) return;
  275. CreateComponentAction action;
  276. action.Define(component);
  277. SaveEditAction(action);
  278. SetSceneModified();
  279. FocusComponent(component);
  280. }
  281. Node@ LoadNode(const String&in fileName, Node@ parent = null)
  282. {
  283. if (fileName.empty)
  284. return null;
  285. if (!fileSystem.FileExists(fileName))
  286. {
  287. MessageBox("No such node file.\n" + fileName);
  288. return null;
  289. }
  290. File file(fileName, FILE_READ);
  291. if (!file.open)
  292. {
  293. MessageBox("Could not open file.\n" + fileName);
  294. return null;
  295. }
  296. ui.cursor.shape = CS_BUSY;
  297. // Before instantiating, add object's resource path if necessary
  298. SetResourcePath(GetPath(fileName), true, true);
  299. Ray cameraRay = camera.GetScreenRay(0.5, 0.5); // Get ray at view center
  300. Vector3 position, normal;
  301. GetSpawnPosition(cameraRay, newNodeDistance, position, normal, 0, true);
  302. Node@ newNode = InstantiateNodeFromFile(file, position, Quaternion(), 1, parent, instantiateMode);
  303. if (newNode !is null)
  304. {
  305. FocusNode(newNode);
  306. instantiateFileName = fileName;
  307. }
  308. return newNode;
  309. }
  310. Node@ InstantiateNodeFromFile(File@ file, const Vector3& position, const Quaternion& rotation, float scaleMod = 1.0f, Node@ parent = null, CreateMode mode = REPLICATED)
  311. {
  312. if (file is null)
  313. return null;
  314. Node@ newNode;
  315. uint numSceneComponent = editorScene.numComponents;
  316. suppressSceneChanges = true;
  317. String extension = GetExtension(file.name);
  318. if (extension != ".xml")
  319. newNode = editorScene.Instantiate(file, position, rotation, mode);
  320. else
  321. newNode = editorScene.InstantiateXML(file, position, rotation, mode);
  322. suppressSceneChanges = false;
  323. if (parent !is null)
  324. newNode.parent = parent;
  325. if (newNode !is null)
  326. {
  327. newNode.scale = newNode.scale * scaleMod;
  328. if (alignToAABBBottom)
  329. {
  330. Drawable@ drawable = GetFirstDrawable(newNode);
  331. if (drawable !is null)
  332. {
  333. BoundingBox aabb = drawable.worldBoundingBox;
  334. Vector3 aabbBottomCenter(aabb.center.x, aabb.min.y, aabb.center.z);
  335. Vector3 offset = aabbBottomCenter - newNode.worldPosition;
  336. newNode.worldPosition = newNode.worldPosition - offset;
  337. }
  338. }
  339. // Create an undo action for the load
  340. CreateNodeAction action;
  341. action.Define(newNode);
  342. SaveEditAction(action);
  343. SetSceneModified();
  344. if (numSceneComponent != editorScene.numComponents)
  345. UpdateHierarchyItem(editorScene);
  346. else
  347. UpdateHierarchyItem(newNode);
  348. }
  349. return newNode;
  350. }
  351. bool SaveNode(const String&in fileName)
  352. {
  353. if (fileName.empty)
  354. return false;
  355. ui.cursor.shape = CS_BUSY;
  356. MakeBackup(fileName);
  357. File file(fileName, FILE_WRITE);
  358. if (!file.open)
  359. {
  360. MessageBox("Could not open file.\n" + fileName);
  361. return false;
  362. }
  363. String extension = GetExtension(fileName);
  364. bool success = (extension != ".xml" ? editNode.Save(file) : editNode.SaveXML(file));
  365. RemoveBackup(success, fileName);
  366. if (success)
  367. instantiateFileName = fileName;
  368. else
  369. MessageBox("Could not save node successfully!\nSee Urho3D.log for more detail.");
  370. return success;
  371. }
  372. void UpdateScene(float timeStep)
  373. {
  374. if (runUpdate)
  375. editorScene.Update(timeStep);
  376. }
  377. void StopSceneUpdate()
  378. {
  379. runUpdate = false;
  380. audio.Stop();
  381. toolBarDirty = true;
  382. // If scene should revert on update stop, load saved data now
  383. if (revertOnPause && revertData !is null)
  384. {
  385. suppressSceneChanges = true;
  386. editorScene.Clear();
  387. editorScene.LoadXML(revertData.GetRoot());
  388. CreateGrid();
  389. UpdateHierarchyItem(editorScene, true);
  390. ClearEditActions();
  391. suppressSceneChanges = false;
  392. }
  393. revertData = null;
  394. }
  395. void StartSceneUpdate()
  396. {
  397. runUpdate = true;
  398. // Run audio playback only when scene is updating, so that audio components' time-dependent attributes stay constant when
  399. // paused (similar to physics)
  400. audio.Play();
  401. toolBarDirty = true;
  402. // Save scene data for reverting if enabled
  403. if (revertOnPause)
  404. {
  405. revertData = XMLFile();
  406. XMLElement root = revertData.CreateRoot("scene");
  407. editorScene.SaveXML(root);
  408. }
  409. else
  410. revertData = null;
  411. }
  412. bool ToggleSceneUpdate()
  413. {
  414. if (!runUpdate)
  415. StartSceneUpdate();
  416. else
  417. StopSceneUpdate();
  418. return true;
  419. }
  420. bool ShowLayerMover()
  421. {
  422. return ShowLayerEditor();
  423. }
  424. void SetSceneModified()
  425. {
  426. if (!sceneModified)
  427. {
  428. sceneModified = true;
  429. UpdateWindowTitle();
  430. }
  431. }
  432. bool SceneDelete()
  433. {
  434. ui.cursor.shape = CS_BUSY;
  435. BeginSelectionModify();
  436. // Clear the selection now to prevent repopulation of selectedNodes and selectedComponents combo
  437. hierarchyList.ClearSelection();
  438. // Group for storing undo actions
  439. EditActionGroup group;
  440. // Remove nodes
  441. for (uint i = 0; i < selectedNodes.length; ++i)
  442. {
  443. Node@ node = selectedNodes[i];
  444. if (node.parent is null || node.scene is null)
  445. continue; // Root or already deleted
  446. uint nodeIndex = GetListIndex(node);
  447. // Create undo action
  448. DeleteNodeAction action;
  449. action.Define(node);
  450. group.actions.Push(action);
  451. node.Remove();
  452. SetSceneModified();
  453. // If deleting only one node, select the next item in the same index
  454. if (selectedNodes.length == 1 && selectedComponents.empty)
  455. hierarchyList.selection = nodeIndex;
  456. }
  457. // Then remove components, if they still remain
  458. for (uint i = 0; i < selectedComponents.length; ++i)
  459. {
  460. Component@ component = selectedComponents[i];
  461. Node@ node = component.node;
  462. if (node is null)
  463. continue; // Already deleted
  464. uint index = GetComponentListIndex(component);
  465. uint nodeIndex = GetListIndex(node);
  466. if (index == NO_ITEM || nodeIndex == NO_ITEM)
  467. continue;
  468. // Do not allow to remove the Octree, DebugRenderer or MaterialCache2D or DrawableProxy2D from the root node
  469. if (node is editorScene && (component.typeName == "Octree" || component.typeName == "DebugRenderer" ||
  470. component.typeName == "MaterialCache2D" || component.typeName == "DrawableProxy2D"))
  471. continue;
  472. // Create undo action
  473. DeleteComponentAction action;
  474. action.Define(component);
  475. group.actions.Push(action);
  476. node.RemoveComponent(component);
  477. SetSceneModified();
  478. // If deleting only one component, select the next item in the same index
  479. if (selectedComponents.length == 1 && selectedNodes.empty)
  480. hierarchyList.selection = index;
  481. }
  482. SaveEditActionGroup(group);
  483. EndSelectionModify();
  484. return true;
  485. }
  486. bool SceneCut()
  487. {
  488. return SceneCopy() && SceneDelete();
  489. }
  490. bool SceneCopy()
  491. {
  492. ui.cursor.shape = CS_BUSY;
  493. sceneCopyBuffer.Clear();
  494. // Copy components
  495. if (!selectedComponents.empty)
  496. {
  497. for (uint i = 0; i < selectedComponents.length; ++i)
  498. {
  499. XMLFile@ xml = XMLFile();
  500. XMLElement rootElem = xml.CreateRoot("component");
  501. selectedComponents[i].SaveXML(rootElem);
  502. rootElem.SetBool("local", selectedComponents[i].id >= FIRST_LOCAL_ID);
  503. sceneCopyBuffer.Push(xml);
  504. }
  505. }
  506. // Copy nodes.
  507. else
  508. {
  509. for (uint i = 0; i < selectedNodes.length; ++i)
  510. {
  511. // Skip the root scene node as it cannot be copied
  512. if (selectedNodes[i] is editorScene)
  513. continue;
  514. XMLFile@ xml = XMLFile();
  515. XMLElement rootElem = xml.CreateRoot("node");
  516. selectedNodes[i].SaveXML(rootElem);
  517. rootElem.SetBool("local", selectedNodes[i].id >= FIRST_LOCAL_ID);
  518. sceneCopyBuffer.Push(xml);
  519. }
  520. }
  521. return true;
  522. }
  523. bool ScenePaste(bool pasteRoot = false, bool duplication = false)
  524. {
  525. ui.cursor.shape = CS_BUSY;
  526. // Group for storing undo actions
  527. EditActionGroup group;
  528. for (uint i = 0; i < sceneCopyBuffer.length; ++i)
  529. {
  530. XMLElement rootElem = sceneCopyBuffer[i].root;
  531. String mode = rootElem.name;
  532. if (mode == "component" && editNode !is null)
  533. {
  534. // If this is the root node, do not allow to create duplicate scene-global components
  535. if (editNode is editorScene && CheckForExistingGlobalComponent(editNode, rootElem.GetAttribute("type")))
  536. return false;
  537. // If copied component was local, make the new local too
  538. Component@ newComponent = editNode.CreateComponent(rootElem.GetAttribute("type"), rootElem.GetBool("local") ? LOCAL :
  539. REPLICATED);
  540. if (newComponent is null)
  541. return false;
  542. newComponent.LoadXML(rootElem);
  543. newComponent.ApplyAttributes();
  544. // Create an undo action
  545. CreateComponentAction action;
  546. action.Define(newComponent);
  547. group.actions.Push(action);
  548. }
  549. else if (mode == "node")
  550. {
  551. // If copied node was local, make the new local too
  552. Node@ newNode;
  553. // Are we pasting into the root node?
  554. if (pasteRoot)
  555. newNode = editorScene.CreateChild("", rootElem.GetBool("local") ? LOCAL : REPLICATED);
  556. else
  557. {
  558. // If we are duplicating, paste into the selected nodes parent
  559. if (duplication)
  560. {
  561. if (editNode !is null && editNode.parent !is null)
  562. newNode = editNode.parent.CreateChild("", rootElem.GetBool("local") ? LOCAL : REPLICATED);
  563. else
  564. newNode = editorScene.CreateChild("", rootElem.GetBool("local") ? LOCAL : REPLICATED);
  565. }
  566. // If we aren't duplicating, paste into the selected node
  567. else
  568. {
  569. newNode = editNode.CreateChild("", rootElem.GetBool("local") ? LOCAL : REPLICATED);
  570. }
  571. }
  572. newNode.LoadXML(rootElem);
  573. // Create an undo action
  574. CreateNodeAction action;
  575. action.Define(newNode);
  576. group.actions.Push(action);
  577. }
  578. }
  579. SaveEditActionGroup(group);
  580. SetSceneModified();
  581. return true;
  582. }
  583. bool SceneDuplicate()
  584. {
  585. Array<XMLFile@> copy = sceneCopyBuffer;
  586. if (!SceneCopy())
  587. {
  588. sceneCopyBuffer = copy;
  589. return false;
  590. }
  591. if (!ScenePaste(false, true))
  592. {
  593. sceneCopyBuffer = copy;
  594. return false;
  595. }
  596. sceneCopyBuffer = copy;
  597. return true;
  598. }
  599. bool SceneUnparent()
  600. {
  601. if (!CheckHierarchyWindowFocus() || !selectedComponents.empty || selectedNodes.empty)
  602. return false;
  603. ui.cursor.shape = CS_BUSY;
  604. // Group for storing undo actions
  605. EditActionGroup group;
  606. // Parent selected nodes to root
  607. Array<Node@> changedNodes;
  608. for (uint i = 0; i < selectedNodes.length; ++i)
  609. {
  610. Node@ sourceNode = selectedNodes[i];
  611. if (sourceNode.parent is null || sourceNode.parent is editorScene)
  612. continue; // Root or already parented to root
  613. // Perform the reparenting, continue loop even if action fails
  614. ReparentNodeAction action;
  615. action.Define(sourceNode, editorScene);
  616. group.actions.Push(action);
  617. SceneChangeParent(sourceNode, editorScene, false);
  618. changedNodes.Push(sourceNode);
  619. }
  620. // Reselect the changed nodes at their new position in the list
  621. for (uint i = 0; i < changedNodes.length; ++i)
  622. hierarchyList.AddSelection(GetListIndex(changedNodes[i]));
  623. SaveEditActionGroup(group);
  624. SetSceneModified();
  625. return true;
  626. }
  627. bool NodesParentToLastSelected()
  628. {
  629. if (lastSelectedNode is null)
  630. return false;
  631. if (!CheckHierarchyWindowFocus() || !selectedComponents.empty || selectedNodes.empty)
  632. return false;
  633. ui.cursor.shape = CS_BUSY;
  634. // Group for storing undo actions
  635. EditActionGroup group;
  636. // Parent selected nodes to root
  637. Array<Node@> changedNodes;
  638. // Find new parent node it selected last
  639. Node@ lastNode = lastSelectedNode;
  640. for (uint i = 0; i < selectedNodes.length; ++i)
  641. {
  642. Node@ sourceNode = selectedNodes[i];
  643. if ( sourceNode.id == lastNode.id)
  644. continue; // Skip last node it is parent
  645. if (sourceNode.parent.id == lastNode.id)
  646. continue; // Root or already parented to root
  647. // Perform the reparenting, continue loop even if action fails
  648. ReparentNodeAction action;
  649. action.Define(sourceNode, lastNode);
  650. group.actions.Push(action);
  651. SceneChangeParent(sourceNode, lastNode, false);
  652. changedNodes.Push(sourceNode);
  653. }
  654. // Reselect the changed nodes at their new position in the list
  655. for (uint i = 0; i < changedNodes.length; ++i)
  656. hierarchyList.AddSelection(GetListIndex(changedNodes[i]));
  657. SaveEditActionGroup(group);
  658. SetSceneModified();
  659. return true;
  660. }
  661. bool SceneToggleEnable()
  662. {
  663. if (!CheckHierarchyWindowFocus())
  664. return false;
  665. ui.cursor.shape = CS_BUSY;
  666. EditActionGroup group;
  667. // Toggle enabled state of nodes recursively
  668. for (uint i = 0; i < selectedNodes.length; ++i)
  669. {
  670. // Do not attempt to disable the Scene
  671. if (selectedNodes[i].typeName == "Node")
  672. {
  673. bool oldEnabled = selectedNodes[i].enabled;
  674. selectedNodes[i].SetEnabledRecursive(!oldEnabled);
  675. // Create undo action
  676. ToggleNodeEnabledAction action;
  677. action.Define(selectedNodes[i], oldEnabled);
  678. group.actions.Push(action);
  679. }
  680. }
  681. for (uint i = 0; i < selectedComponents.length; ++i)
  682. {
  683. // Some components purposefully do not expose the Enabled attribute, and it does not affect them in any way
  684. // (Octree, PhysicsWorld). Check that the first attribute is in fact called "Is Enabled"
  685. if (selectedComponents[i].numAttributes > 0 && selectedComponents[i].attributeInfos[0].name == "Is Enabled")
  686. {
  687. bool oldEnabled = selectedComponents[i].enabled;
  688. selectedComponents[i].enabled = !oldEnabled;
  689. // Create undo action
  690. EditAttributeAction action;
  691. action.Define(selectedComponents[i], 0, Variant(oldEnabled));
  692. group.actions.Push(action);
  693. }
  694. }
  695. SaveEditActionGroup(group);
  696. SetSceneModified();
  697. return true;
  698. }
  699. bool SceneEnableAllNodes()
  700. {
  701. if (!CheckHierarchyWindowFocus())
  702. return false;
  703. ui.cursor.shape = CS_BUSY;
  704. EditActionGroup group;
  705. // Toggle enabled state of nodes recursively
  706. Array<Node@> allNodes;
  707. allNodes = editorScene.GetChildren(true);
  708. for (uint i = 0; i < allNodes.length; ++i)
  709. {
  710. // Do not attempt to disable the Scene
  711. if (allNodes[i].typeName == "Node")
  712. {
  713. bool oldEnabled = allNodes[i].enabled;
  714. if (oldEnabled == false)
  715. allNodes[i].SetEnabledRecursive(true);
  716. // Create undo action
  717. ToggleNodeEnabledAction action;
  718. action.Define(allNodes[i], oldEnabled);
  719. group.actions.Push(action);
  720. }
  721. }
  722. Array<Component@> allComponents;
  723. allComponents = editorScene.GetComponents();
  724. for (uint i = 0; i < allComponents.length; ++i)
  725. {
  726. // Some components purposefully do not expose the Enabled attribute, and it does not affect them in any way
  727. // (Octree, PhysicsWorld). Check that the first attribute is in fact called "Is Enabled"
  728. if (allComponents[i].numAttributes > 0 && allComponents[i].attributeInfos[0].name == "Is Enabled")
  729. {
  730. bool oldEnabled = allComponents[i].enabled;
  731. allComponents[i].enabled = true;
  732. // Create undo action
  733. EditAttributeAction action;
  734. action.Define(allComponents[i], 0, Variant(oldEnabled));
  735. group.actions.Push(action);
  736. }
  737. }
  738. SaveEditActionGroup(group);
  739. SetSceneModified();
  740. return true;
  741. }
  742. bool SceneChangeParent(Node@ sourceNode, Node@ targetNode, bool createUndoAction = true)
  743. {
  744. // Create undo action if requested
  745. if (createUndoAction)
  746. {
  747. ReparentNodeAction action;
  748. action.Define(sourceNode, targetNode);
  749. SaveEditAction(action);
  750. }
  751. sourceNode.parent = targetNode;
  752. SetSceneModified();
  753. // Return true if success
  754. if (sourceNode.parent is targetNode)
  755. {
  756. UpdateNodeAttributes(); // Parent change may have changed local transform
  757. return true;
  758. }
  759. else
  760. return false;
  761. }
  762. bool SceneChangeParent(Node@ sourceNode, Array<Node@> sourceNodes, Node@ targetNode, bool createUndoAction = true)
  763. {
  764. // Create undo action if requested
  765. if (createUndoAction)
  766. {
  767. ReparentNodeAction action;
  768. action.Define(sourceNodes, targetNode);
  769. SaveEditAction(action);
  770. }
  771. for (uint i = 0; i < sourceNodes.length; ++i)
  772. {
  773. Node@ node = sourceNodes[i];
  774. node.parent = targetNode;
  775. }
  776. SetSceneModified();
  777. // Return true if success
  778. if (sourceNode.parent is targetNode)
  779. {
  780. UpdateNodeAttributes(); // Parent change may have changed local transform
  781. return true;
  782. }
  783. else
  784. return false;
  785. }
  786. bool SceneResetPosition()
  787. {
  788. if (editNode !is null)
  789. {
  790. Transform oldTransform;
  791. oldTransform.Define(editNode);
  792. editNode.position = Vector3(0.0, 0.0, 0.0);
  793. // Create undo action
  794. EditNodeTransformAction action;
  795. action.Define(editNode, oldTransform);
  796. SaveEditAction(action);
  797. SetSceneModified();
  798. UpdateNodeAttributes();
  799. return true;
  800. }
  801. else
  802. return false;
  803. }
  804. bool SceneResetRotation()
  805. {
  806. if (editNode !is null)
  807. {
  808. Transform oldTransform;
  809. oldTransform.Define(editNode);
  810. editNode.rotation = Quaternion();
  811. // Create undo action
  812. EditNodeTransformAction action;
  813. action.Define(editNode, oldTransform);
  814. SaveEditAction(action);
  815. SetSceneModified();
  816. UpdateNodeAttributes();
  817. return true;
  818. }
  819. else
  820. return false;
  821. }
  822. bool SceneResetScale()
  823. {
  824. if (editNode !is null)
  825. {
  826. Transform oldTransform;
  827. oldTransform.Define(editNode);
  828. editNode.scale = Vector3(1.0, 1.0, 1.0);
  829. // Create undo action
  830. EditNodeTransformAction action;
  831. action.Define(editNode, oldTransform);
  832. SaveEditAction(action);
  833. SetSceneModified();
  834. UpdateNodeAttributes();
  835. return true;
  836. }
  837. else
  838. return false;
  839. }
  840. bool SceneSelectAll()
  841. {
  842. BeginSelectionModify();
  843. Array<Node@> rootLevelNodes = editorScene.GetChildren();
  844. Array<uint> indices;
  845. for (uint i = 0; i < rootLevelNodes.length; ++i)
  846. indices.Push(GetListIndex(rootLevelNodes[i]));
  847. hierarchyList.SetSelections(indices);
  848. EndSelectionModify();
  849. return true;
  850. }
  851. bool SceneResetToDefault()
  852. {
  853. ui.cursor.shape = CS_BUSY;
  854. // Group for storing undo actions
  855. EditActionGroup group;
  856. // Reset selected component to their default
  857. if (!selectedComponents.empty)
  858. {
  859. for (uint i = 0; i < selectedComponents.length; ++i)
  860. {
  861. Component@ component = selectedComponents[i];
  862. ResetAttributesAction action;
  863. action.Define(component);
  864. group.actions.Push(action);
  865. component.ResetToDefault();
  866. component.ApplyAttributes();
  867. for (uint j = 0; j < component.numAttributes; ++j)
  868. PostEditAttribute(component, j);
  869. }
  870. }
  871. // OR reset selected nodes to their default
  872. else
  873. {
  874. for (uint i = 0; i < selectedNodes.length; ++i)
  875. {
  876. Node@ node = selectedNodes[i];
  877. ResetAttributesAction action;
  878. action.Define(node);
  879. group.actions.Push(action);
  880. node.ResetToDefault();
  881. node.ApplyAttributes();
  882. for (uint j = 0; j < node.numAttributes; ++j)
  883. PostEditAttribute(node, j);
  884. }
  885. }
  886. SaveEditActionGroup(group);
  887. SetSceneModified();
  888. attributesFullDirty = true;
  889. return true;
  890. }
  891. bool SceneRebuildNavigation()
  892. {
  893. ui.cursor.shape = CS_BUSY;
  894. Array<Component@>@ navMeshes = editorScene.GetComponents("NavigationMesh", true);
  895. if (navMeshes.empty)
  896. {
  897. @navMeshes = editorScene.GetComponents("DynamicNavigationMesh", true);
  898. if (navMeshes.empty)
  899. {
  900. MessageBox("No NavigationMesh components in the scene, nothing to rebuild.");
  901. return false;
  902. }
  903. }
  904. bool success = true;
  905. for (uint i = 0; i < navMeshes.length; ++i)
  906. {
  907. NavigationMesh@ navMesh = navMeshes[i];
  908. if (!navMesh.Build())
  909. success = false;
  910. }
  911. return success;
  912. }
  913. bool SceneAddChildrenStaticModelGroup()
  914. {
  915. StaticModelGroup@ smg = cast<StaticModelGroup>(editComponents.length > 0 ? editComponents[0] : null);
  916. if (smg is null && editNode !is null)
  917. smg = editNode.GetComponent("StaticModelGroup");
  918. if (smg is null)
  919. {
  920. MessageBox("Must have a StaticModelGroup component selected.");
  921. return false;
  922. }
  923. uint attrIndex = GetAttributeIndex(smg, "Instance Nodes");
  924. Variant oldValue = smg.attributes[attrIndex];
  925. Array<Node@> children = smg.node.GetChildren(true);
  926. for (uint i = 0; i < children.length; ++i)
  927. smg.AddInstanceNode(children[i]);
  928. EditAttributeAction action;
  929. action.Define(smg, attrIndex, oldValue);
  930. SaveEditAction(action);
  931. SetSceneModified();
  932. FocusComponent(smg);
  933. return true;
  934. }
  935. bool SceneSetChildrenSplinePath(bool makeCycle)
  936. {
  937. SplinePath@ sp = cast<SplinePath>(editComponents.length > 0 ? editComponents[0] : null);
  938. if (sp is null && editNode !is null)
  939. sp = editNode.GetComponent("SplinePath");
  940. if (sp is null)
  941. {
  942. MessageBox("Must have a SplinePath component selected.");
  943. return false;
  944. }
  945. uint attrIndex = GetAttributeIndex(sp, "Control Points");
  946. Variant oldValue = sp.attributes[attrIndex];
  947. Array<Node@> children = sp.node.GetChildren(true);
  948. if (children.length >= 2)
  949. {
  950. sp.ClearControlPoints();
  951. for (uint i = 0; i < children.length; ++i)
  952. sp.AddControlPoint(children[i]);
  953. }
  954. else
  955. {
  956. MessageBox("You must have a minimum two children Nodes in selected Node.");
  957. return false;
  958. }
  959. if (makeCycle)
  960. sp.AddControlPoint(children[0]);
  961. EditAttributeAction action;
  962. action.Define(sp, attrIndex, oldValue);
  963. SaveEditAction(action);
  964. SetSceneModified();
  965. FocusComponent(sp);
  966. return true;
  967. }
  968. void AssignMaterial(StaticModel@ model, String materialPath)
  969. {
  970. Material@ material = cache.GetResource("Material", materialPath);
  971. if (material is null)
  972. return;
  973. ResourceRefList materials = model.GetAttribute("Material").GetResourceRefList();
  974. Array<String> oldMaterials;
  975. for(uint i = 0; i < materials.length; ++i)
  976. oldMaterials.Push(materials.names[i]);
  977. model.material = material;
  978. AssignMaterialAction action;
  979. action.Define(model, oldMaterials, material);
  980. SaveEditAction(action);
  981. SetSceneModified();
  982. FocusComponent(model);
  983. }
  984. void UpdateSceneMru(String filename)
  985. {
  986. while (uiRecentScenes.Find(filename) > -1)
  987. uiRecentScenes.Erase(uiRecentScenes.Find(filename));
  988. uiRecentScenes.Insert(0, filename);
  989. for (uint i = uiRecentScenes.length - 1; i >= maxRecentSceneCount; i--)
  990. uiRecentScenes.Erase(i);
  991. PopulateMruScenes();
  992. }
  993. Drawable@ GetFirstDrawable(Node@ node)
  994. {
  995. Array<Node@> nodes = node.GetChildren(true);
  996. nodes.Insert(0, node);
  997. for (uint i = 0; i < nodes.length; ++i)
  998. {
  999. Array<Component@> components = nodes[i].GetComponents();
  1000. for (uint j = 0; j < components.length; ++j)
  1001. {
  1002. Drawable@ drawable = cast<Drawable>(components[j]);
  1003. if (drawable !is null)
  1004. return drawable;
  1005. }
  1006. }
  1007. return null;
  1008. }
  1009. void AssignModel(StaticModel@ assignee, String modelPath)
  1010. {
  1011. Model@ model = cache.GetResource("Model", modelPath);
  1012. if (model is null)
  1013. return;
  1014. Model@ oldModel = assignee.model;
  1015. assignee.model = model;
  1016. AssignModelAction action;
  1017. action.Define(assignee, oldModel, model);
  1018. SaveEditAction(action);
  1019. SetSceneModified();
  1020. FocusComponent(assignee);
  1021. }
  1022. void CreateModelWithStaticModel(String filepath, Node@ parent)
  1023. {
  1024. if (parent is null)
  1025. return;
  1026. /// \todo should be able to specify the createmode
  1027. if (parent is editorScene)
  1028. parent = CreateNode(REPLICATED);
  1029. Model@ model = cache.GetResource("Model", filepath);
  1030. if (model is null)
  1031. return;
  1032. StaticModel@ staticModel = parent.GetOrCreateComponent("StaticModel");
  1033. staticModel.model = model;
  1034. CreateLoadedComponent(staticModel);
  1035. }
  1036. void CreateModelWithAnimatedModel(String filepath, Node@ parent)
  1037. {
  1038. if (parent is null)
  1039. return;
  1040. /// \todo should be able to specify the createmode
  1041. if (parent is editorScene)
  1042. parent = CreateNode(REPLICATED);
  1043. Model@ model = cache.GetResource("Model", filepath);
  1044. if (model is null)
  1045. return;
  1046. AnimatedModel@ animatedModel = parent.GetOrCreateComponent("AnimatedModel");
  1047. animatedModel.model = model;
  1048. CreateLoadedComponent(animatedModel);
  1049. }