EditorScene.as 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600
  1. // Urho3D editor scene handling
  2. #include "Scripts/Editor/EditorSceneWindow.as"
  3. #include "Scripts/Editor/EditorNodeWindow.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 MAX_PICK_MODES = 4;
  9. Scene@ editorScene;
  10. String sceneFileName;
  11. String instantiateFileName;
  12. CreateMode instantiateMode = REPLICATED;
  13. bool sceneModified = false;
  14. bool runUpdate = false;
  15. Array<Node@> selectedNodes;
  16. Array<Component@> selectedComponents;
  17. Node@ editNode;
  18. Array<Node@> editNodes;
  19. Array<Component@> editComponents;
  20. Array<XMLFile@> copyBuffer;
  21. bool copyBufferLocal = false;
  22. bool copyBufferExpanded = false;
  23. bool inSelectionModify = false;
  24. void ClearSelection()
  25. {
  26. selectedNodes.Clear();
  27. selectedComponents.Clear();
  28. editNode = null;
  29. editNodes.Clear();
  30. editComponents.Clear();
  31. HideGizmo();
  32. }
  33. void CreateScene()
  34. {
  35. ClearSelection();
  36. // Create a scene with default values, these will be overridden when loading scenes
  37. editorScene = Scene("");
  38. Octree@ octree = editorScene.CreateComponent("Octree");
  39. PhysicsWorld@ physicsWorld = editorScene.CreateComponent("PhysicsWorld");
  40. octree.Resize(BoundingBox(-1000.0, 1000.0), 8);
  41. editorScene.CreateComponent("DebugRenderer");
  42. // Allow access to the scene from the console
  43. script.defaultScene = editorScene;
  44. // Always pause the scene, and do updates manually
  45. editorScene.active = false;
  46. if (sceneWindow !is null)
  47. {
  48. UpdateSceneWindow();
  49. UpdateNodeWindow();
  50. }
  51. runUpdate = false;
  52. sceneFileName = "";
  53. UpdateWindowTitle();
  54. CreateCamera();
  55. CreateGizmo();
  56. }
  57. void SetResourcePath(String newPath, bool usePreferredDir = true)
  58. {
  59. if (newPath.empty)
  60. return;
  61. if (usePreferredDir)
  62. newPath = AddTrailingSlash(cache.GetPreferredResourceDir(newPath));
  63. else
  64. newPath = AddTrailingSlash(newPath);
  65. if (newPath == sceneResourcePath)
  66. return;
  67. cache.ReleaseAllResources(false);
  68. renderer.ReloadShaders();
  69. // Remove the old scene resource path if any. However make sure that the default data paths do not get removed
  70. if (!sceneResourcePath.empty && !sceneResourcePath.Contains(fileSystem.programDir))
  71. cache.RemoveResourceDir(sceneResourcePath);
  72. cache.AddResourceDir(newPath);
  73. sceneResourcePath = newPath;
  74. }
  75. Array<Resource@> GetSceneResources()
  76. {
  77. Array<Resource@> sceneResources;
  78. Array<Node@> allNodes = editorScene.GetChildren(true);
  79. for (uint i = 0; i < allNodes.length; ++i)
  80. {
  81. for (uint j = 0; j < allNodes[i].numComponents; ++j)
  82. {
  83. Component@ comp = allNodes[i].components[j];
  84. for (uint k = 0; k < comp.numAttributes; ++k)
  85. {
  86. Variant attr = comp.attributes[k];
  87. if (attr.type == VAR_RESOURCEREF)
  88. {
  89. ResourceRef ref = attr.GetResourceRef();
  90. Resource@ resource = cache.GetResource(ref.type, ref.id);
  91. if (resource !is null)
  92. AddResourceIfUnique(sceneResources, resource);
  93. }
  94. else if (attr.type == VAR_RESOURCEREFLIST)
  95. {
  96. ResourceRefList refList = attr.GetResourceRefList();
  97. for (uint l = 0; l < refList.length; ++l)
  98. {
  99. Resource@ resource = cache.GetResource(refList.type, refList.ids[l]);
  100. if (resource !is null)
  101. AddResourceIfUnique(sceneResources, resource);
  102. }
  103. }
  104. else if (attr.type == VAR_VARIANTVECTOR)
  105. {
  106. Array<Variant>@ variants = attr.GetVariantVector();
  107. for (uint l = 0; l < variants.length; ++l)
  108. {
  109. if (variants[l].type == VAR_RESOURCEREF)
  110. {
  111. ResourceRef ref = variants[l].GetResourceRef();
  112. Resource@ resource = cache.GetResource(ref.type, ref.id);
  113. if (resource !is null)
  114. AddResourceIfUnique(sceneResources, resource);
  115. }
  116. }
  117. }
  118. }
  119. }
  120. }
  121. return sceneResources;
  122. }
  123. void AddResourceIfUnique(Array<Resource@>@ sceneResources, Resource@ resource)
  124. {
  125. for (uint i = 0; i < sceneResources.length; ++i)
  126. {
  127. if (sceneResources[i] is resource)
  128. return;
  129. }
  130. sceneResources.Push(resource);
  131. }
  132. void ReloadResources()
  133. {
  134. Array<Resource@> sceneResources = GetSceneResources();
  135. for (uint i = 0; i < sceneResources.length; ++i)
  136. {
  137. // Handle material textures manually
  138. Material@ mat = cast<Material>(sceneResources[i]);
  139. if (mat !is null)
  140. {
  141. for (int j = 0; j < MAX_MATERIAL_TEXTURE_UNITS; ++j)
  142. {
  143. Texture@ tex = mat.textures[j];
  144. if (tex !is null)
  145. AddResourceIfUnique(sceneResources, tex);
  146. }
  147. }
  148. }
  149. for (uint i = 0; i < sceneResources.length; ++i)
  150. cache.ReloadResource(sceneResources[i]);
  151. }
  152. void LoadScene(const String&in fileName)
  153. {
  154. if (fileName.empty)
  155. return;
  156. ui.cursor.shape = CS_BUSY;
  157. // Always load the scene from the filesystem, not from resource paths
  158. if (!fileSystem.FileExists(fileName))
  159. {
  160. log.Error("No such scene: " + fileName);
  161. return;
  162. }
  163. File file(fileName, FILE_READ);
  164. if (!file.open)
  165. return;
  166. // Clear the old scene
  167. ClearSelection();
  168. ClearSceneWindow();
  169. editorScene.Clear();
  170. // Add the new resource path
  171. SetResourcePath(GetPath(fileName));
  172. String extension = GetExtension(fileName);
  173. if (extension != ".xml")
  174. editorScene.Load(file);
  175. else
  176. editorScene.LoadXML(file);
  177. // Always pause the scene, and do updates manually
  178. editorScene.active = false;
  179. sceneFileName = fileName;
  180. sceneModified = false;
  181. runUpdate = false;
  182. UpdateWindowTitle();
  183. UpdateSceneWindow();
  184. UpdateNodeWindow();
  185. ResetCamera();
  186. CreateGizmo();
  187. }
  188. void SaveScene(const String&in fileName)
  189. {
  190. if (fileName.empty || GetFileName(fileName).empty)
  191. return;
  192. // Unpause when saving so that the scene will work properly when loaded outside the editor
  193. editorScene.active = true;
  194. File file(fileName, FILE_WRITE);
  195. String extension = GetExtension(fileName);
  196. if (extension != ".xml")
  197. editorScene.Save(file);
  198. else
  199. editorScene.SaveXML(file);
  200. editorScene.active = false;
  201. sceneFileName = fileName;
  202. sceneModified = false;
  203. UpdateWindowTitle();
  204. }
  205. void LoadNode(const String&in fileName)
  206. {
  207. if (fileName.empty)
  208. return;
  209. if (!fileSystem.FileExists(fileName))
  210. {
  211. log.Error("No such node file " + fileName);
  212. return;
  213. }
  214. File file(fileName, FILE_READ);
  215. if (!file.open)
  216. return;
  217. // Before instantiating, set resource path if empty
  218. if (sceneResourcePath.empty)
  219. SetResourcePath(GetPath(fileName));
  220. Vector3 position = GetNewNodePosition();
  221. Node@ newNode;
  222. String extension = GetExtension(fileName);
  223. if (extension != ".xml")
  224. newNode = editorScene.Instantiate(file, position, Quaternion(), instantiateMode);
  225. else
  226. newNode = editorScene.InstantiateXML(file, position, Quaternion(), instantiateMode);
  227. if (newNode !is null)
  228. {
  229. UpdateAndFocusNode(newNode);
  230. instantiateFileName = fileName;
  231. }
  232. }
  233. void SaveNode(const String&in fileName)
  234. {
  235. if (fileName.empty || GetFileName(fileName).empty)
  236. return;
  237. if (selectedNodes.length == 1)
  238. {
  239. File file(fileName, FILE_WRITE);
  240. if (!file.open)
  241. return;
  242. String extension = GetExtension(fileName);
  243. if (extension != ".xml")
  244. selectedNodes[0].Save(file);
  245. else
  246. selectedNodes[0].SaveXML(file);
  247. instantiateFileName = fileName;
  248. }
  249. }
  250. void UpdateScene(float timeStep)
  251. {
  252. if (runUpdate)
  253. editorScene.Update(timeStep);
  254. }
  255. void BeginModify(uint nodeID)
  256. {
  257. // Undo/Redo can be implemented here
  258. }
  259. void EndModify(uint nodeID)
  260. {
  261. // Undo/Redo can be implemented here
  262. if (!sceneModified)
  263. {
  264. sceneModified = true;
  265. UpdateWindowTitle();
  266. }
  267. }
  268. void BeginSelectionModify()
  269. {
  270. // A large operation on selected nodes is about to begin. Disable intermediate selection updates
  271. inSelectionModify = true;
  272. }
  273. void EndSelectionModify()
  274. {
  275. // The large operation on selected nodes has ended. Update node/component selection now
  276. inSelectionModify = false;
  277. HandleSceneWindowSelectionChange();
  278. }
  279. bool SceneDelete()
  280. {
  281. if (!CheckSceneWindowFocus() || (selectedComponents.empty && selectedNodes.empty))
  282. return false;
  283. BeginSelectionModify();
  284. ListView@ list = sceneWindow.GetChild("NodeList", true);
  285. // Remove nodes
  286. for (uint i = 0; i < selectedNodes.length; ++i)
  287. {
  288. Node@ node = selectedNodes[i];
  289. if (node.parent is null || node.scene is null)
  290. continue; // Root or already deleted
  291. uint id = node.id;
  292. uint nodeIndex = GetNodeListIndex(node);
  293. BeginModify(id);
  294. node.Remove();
  295. EndModify(id);
  296. UpdateSceneWindowNode(nodeIndex, null);
  297. // If deleting only one node, select the next item in the same index
  298. if (selectedNodes.length == 1 && selectedComponents.empty)
  299. list.selection = nodeIndex;
  300. }
  301. // Then remove components, if they still remain
  302. for (uint i = 0; i < selectedComponents.length; ++i)
  303. {
  304. Component@ component = selectedComponents[i];
  305. Node@ node = component.node;
  306. if (node is null)
  307. continue; // Already deleted
  308. uint index = GetComponentListIndex(component);
  309. uint nodeIndex = GetNodeListIndex(node);
  310. if (index == NO_ITEM || nodeIndex == NO_ITEM)
  311. continue;
  312. // Do not allow to remove the Octree, PhysicsWorld or DebugRenderer from the root node
  313. if (node is editorScene && (component.typeName == "Octree" || component.typeName == "PhysicsWorld" ||
  314. component.typeName == "DebugRenderer"))
  315. continue;
  316. uint id = node.id;
  317. BeginModify(id);
  318. node.RemoveComponent(component);
  319. EndModify(id);
  320. UpdateSceneWindowNode(nodeIndex, node);
  321. // If deleting only one component, select the next item in the same index
  322. if (selectedComponents.length == 1 && selectedNodes.empty)
  323. list.selection = index;
  324. }
  325. EndSelectionModify();
  326. return true;
  327. }
  328. bool SceneCut()
  329. {
  330. if (SceneCopy())
  331. return SceneDelete();
  332. else
  333. return false;
  334. }
  335. bool SceneCopy()
  336. {
  337. if ((selectedNodes.empty && selectedComponents.empty) || !CheckSceneWindowFocus())
  338. return false;
  339. // Must have either only components, or only nodes
  340. if (!selectedNodes.empty && !selectedComponents.empty)
  341. return false;
  342. ListView@ list = sceneWindow.GetChild("NodeList", true);
  343. copyBuffer.Clear();
  344. // Copy components
  345. if (!selectedComponents.empty)
  346. {
  347. for (uint i = 0; i < selectedComponents.length; ++i)
  348. {
  349. XMLFile@ xml = XMLFile();
  350. XMLElement rootElem = xml.CreateRoot("component");
  351. selectedComponents[i].SaveXML(rootElem);
  352. rootElem.SetBool("local", selectedComponents[i].id >= FIRST_LOCAL_ID);
  353. copyBuffer.Push(xml);
  354. }
  355. return true;
  356. }
  357. // Copy node. The root node can not be copied
  358. else
  359. {
  360. for (uint i = 0; i < selectedNodes.length; ++i)
  361. {
  362. if (selectedNodes[i] is editorScene)
  363. return false;
  364. }
  365. for (uint i = 0; i < selectedNodes.length; ++i)
  366. {
  367. XMLFile@ xml = XMLFile();
  368. XMLElement rootElem = xml.CreateRoot("node");
  369. selectedNodes[i].SaveXML(rootElem);
  370. rootElem.SetBool("local", selectedNodes[i].id >= FIRST_LOCAL_ID);
  371. copyBuffer.Push(xml);
  372. }
  373. copyBufferExpanded = SaveExpandedStatus(GetNodeListIndex(selectedNodes[0]));
  374. return true;
  375. }
  376. }
  377. bool ScenePaste()
  378. {
  379. if (editNode is null || !CheckSceneWindowFocus() || copyBuffer.empty)
  380. return false;
  381. ListView@ list = sceneWindow.GetChild("NodeList", true);
  382. bool pasteComponents = false;
  383. for (uint i = 0; i < copyBuffer.length; ++i)
  384. {
  385. XMLElement rootElem = copyBuffer[i].root;
  386. String mode = rootElem.name;
  387. if (mode == "component")
  388. {
  389. pasteComponents = true;
  390. // If this is the root node, do not allow to create duplicate scene-global components
  391. if (editNode is editorScene && CheckForExistingGlobalComponent(editNode, rootElem.GetAttribute("type")))
  392. return false;
  393. BeginModify(editNode.id);
  394. // If copied component was local, make the new local too
  395. Component@ newComponent = editNode.CreateComponent(rootElem.GetAttribute("type"), rootElem.GetBool("local") ? LOCAL :
  396. REPLICATED);
  397. if (newComponent is null)
  398. {
  399. EndModify(editNode.id);
  400. return false;
  401. }
  402. newComponent.LoadXML(rootElem);
  403. newComponent.ApplyAttributes();
  404. EndModify(editNode.id);
  405. }
  406. else if (mode == "node")
  407. {
  408. // Make the paste go always to the root node, no matter of the selected node
  409. BeginModify(editorScene.id);
  410. // If copied node was local, make the new local too
  411. Node@ newNode = editorScene.CreateChild("", rootElem.GetBool("local") ? LOCAL : REPLICATED);
  412. BeginModify(newNode.id);
  413. newNode.LoadXML(rootElem);
  414. newNode.ApplyAttributes();
  415. EndModify(newNode.id);
  416. EndModify(editorScene.id);
  417. uint addIndex = GetParentAddIndex(newNode);
  418. UpdateSceneWindowNode(addIndex, newNode);
  419. RestoreExpandedStatus(addIndex, copyBufferExpanded);
  420. }
  421. }
  422. if (pasteComponents)
  423. UpdateSceneWindowNode(editNode);
  424. return true;
  425. }
  426. void SceneUnparent()
  427. {
  428. if (!CheckSceneWindowFocus() || !selectedComponents.empty || selectedNodes.empty)
  429. return;
  430. ListView@ list = sceneWindow.GetChild("NodeList", true);
  431. list.contentElement.DisableLayoutUpdate();
  432. // Parent selected nodes to root
  433. for (uint i = 0; i < selectedNodes.length; ++i)
  434. {
  435. Node@ sourceNode = selectedNodes[i];
  436. if (sourceNode.parent is null || sourceNode.parent is editorScene)
  437. continue; // Root or already parented to root
  438. Node@ targetNode = editorScene;
  439. // Perform the reparenting
  440. BeginModify(targetNode.id);
  441. BeginModify(sourceNode.id);
  442. sourceNode.parent = targetNode;
  443. EndModify(sourceNode.id);
  444. EndModify(targetNode.id);
  445. ListView@ list = sceneWindow.GetChild("NodeList", true);
  446. uint sourceIndex = GetNodeListIndex(sourceNode);
  447. bool expanded = SaveExpandedStatus(sourceIndex);
  448. list.RemoveItem(sourceIndex);
  449. uint addIndex = GetParentAddIndex(sourceNode);
  450. UpdateSceneWindowNode(addIndex, sourceNode);
  451. UpdateNodeAttributes();
  452. RestoreExpandedStatus(addIndex, expanded);
  453. }
  454. list.contentElement.EnableLayoutUpdate();
  455. list.contentElement.UpdateLayout();
  456. }
  457. void SceneResetPosition()
  458. {
  459. if (editNode !is null)
  460. {
  461. editNode.position = Vector3(0.0, 0.0, 0.0);
  462. UpdateNodeAttributes();
  463. }
  464. }
  465. void SceneResetRotation()
  466. {
  467. if (editNode !is null)
  468. {
  469. editNode.rotation = Quaternion();
  470. UpdateNodeAttributes();
  471. }
  472. }
  473. void SceneResetScale()
  474. {
  475. if (editNode !is null)
  476. {
  477. editNode.scale = Vector3(1.0, 1.0, 1.0);
  478. UpdateNodeAttributes();
  479. }
  480. }
  481. void SceneSelectAll()
  482. {
  483. ListView@ list = sceneWindow.GetChild("NodeList", true);
  484. if (!list.selections.empty)
  485. {
  486. BeginSelectionModify();
  487. list.ClearSelection();
  488. EndSelectionModify();
  489. }
  490. else
  491. {
  492. BeginSelectionModify();
  493. Array<Node@> rootLevelNodes = editorScene.GetChildren();
  494. Array<uint> indices;
  495. for (uint i = 0; i < rootLevelNodes.length; ++i)
  496. indices.Push(GetNodeListIndex(rootLevelNodes[i]));
  497. list.SetSelections(indices);
  498. EndSelectionModify();
  499. }
  500. }