| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600 |
- // Urho3D editor scene handling
- #include "Scripts/Editor/EditorSceneWindow.as"
- #include "Scripts/Editor/EditorNodeWindow.as"
- const int PICK_GEOMETRIES = 0;
- const int PICK_LIGHTS = 1;
- const int PICK_ZONES = 2;
- const int PICK_RIGIDBODIES = 3;
- const int MAX_PICK_MODES = 4;
- Scene@ editorScene;
- String sceneFileName;
- String instantiateFileName;
- CreateMode instantiateMode = REPLICATED;
- bool sceneModified = false;
- bool runUpdate = false;
- Array<Node@> selectedNodes;
- Array<Component@> selectedComponents;
- Node@ editNode;
- Array<Node@> editNodes;
- Array<Component@> editComponents;
- Array<XMLFile@> copyBuffer;
- bool copyBufferLocal = false;
- bool copyBufferExpanded = false;
- bool inSelectionModify = false;
- void ClearSelection()
- {
- selectedNodes.Clear();
- selectedComponents.Clear();
- editNode = null;
- editNodes.Clear();
- editComponents.Clear();
- HideGizmo();
- }
- void CreateScene()
- {
- ClearSelection();
- // Create a scene with default values, these will be overridden when loading scenes
- editorScene = Scene("");
- Octree@ octree = editorScene.CreateComponent("Octree");
- PhysicsWorld@ physicsWorld = editorScene.CreateComponent("PhysicsWorld");
- octree.Resize(BoundingBox(-1000.0, 1000.0), 8);
- editorScene.CreateComponent("DebugRenderer");
- // Allow access to the scene from the console
- script.defaultScene = editorScene;
-
- // Always pause the scene, and do updates manually
- editorScene.active = false;
- if (sceneWindow !is null)
- {
- UpdateSceneWindow();
- UpdateNodeWindow();
- }
- runUpdate = false;
- sceneFileName = "";
- UpdateWindowTitle();
- CreateCamera();
- CreateGizmo();
- }
- void SetResourcePath(String newPath, bool usePreferredDir = true)
- {
- if (newPath.empty)
- return;
-
- if (usePreferredDir)
- newPath = AddTrailingSlash(cache.GetPreferredResourceDir(newPath));
- else
- newPath = AddTrailingSlash(newPath);
- if (newPath == sceneResourcePath)
- return;
- cache.ReleaseAllResources(false);
- renderer.ReloadShaders();
- // Remove the old scene resource path if any. However make sure that the default data paths do not get removed
- if (!sceneResourcePath.empty && !sceneResourcePath.Contains(fileSystem.programDir))
- cache.RemoveResourceDir(sceneResourcePath);
- cache.AddResourceDir(newPath);
- sceneResourcePath = newPath;
- }
- Array<Resource@> GetSceneResources()
- {
- Array<Resource@> sceneResources;
- Array<Node@> allNodes = editorScene.GetChildren(true);
- for (uint i = 0; i < allNodes.length; ++i)
- {
- for (uint j = 0; j < allNodes[i].numComponents; ++j)
- {
- Component@ comp = allNodes[i].components[j];
- for (uint k = 0; k < comp.numAttributes; ++k)
- {
- Variant attr = comp.attributes[k];
- if (attr.type == VAR_RESOURCEREF)
- {
- ResourceRef ref = attr.GetResourceRef();
- Resource@ resource = cache.GetResource(ref.type, ref.id);
- if (resource !is null)
- AddResourceIfUnique(sceneResources, resource);
- }
- else if (attr.type == VAR_RESOURCEREFLIST)
- {
- ResourceRefList refList = attr.GetResourceRefList();
- for (uint l = 0; l < refList.length; ++l)
- {
- Resource@ resource = cache.GetResource(refList.type, refList.ids[l]);
- if (resource !is null)
- AddResourceIfUnique(sceneResources, resource);
- }
- }
- else if (attr.type == VAR_VARIANTVECTOR)
- {
- Array<Variant>@ variants = attr.GetVariantVector();
- for (uint l = 0; l < variants.length; ++l)
- {
- if (variants[l].type == VAR_RESOURCEREF)
- {
- ResourceRef ref = variants[l].GetResourceRef();
- Resource@ resource = cache.GetResource(ref.type, ref.id);
- if (resource !is null)
- AddResourceIfUnique(sceneResources, resource);
- }
- }
- }
- }
- }
- }
- return sceneResources;
- }
- void AddResourceIfUnique(Array<Resource@>@ sceneResources, Resource@ resource)
- {
- for (uint i = 0; i < sceneResources.length; ++i)
- {
- if (sceneResources[i] is resource)
- return;
- }
- sceneResources.Push(resource);
- }
- void ReloadResources()
- {
- Array<Resource@> sceneResources = GetSceneResources();
- for (uint i = 0; i < sceneResources.length; ++i)
- {
- // Handle material textures manually
- Material@ mat = cast<Material>(sceneResources[i]);
- if (mat !is null)
- {
- for (int j = 0; j < MAX_MATERIAL_TEXTURE_UNITS; ++j)
- {
- Texture@ tex = mat.textures[j];
- if (tex !is null)
- AddResourceIfUnique(sceneResources, tex);
- }
- }
- }
- for (uint i = 0; i < sceneResources.length; ++i)
- cache.ReloadResource(sceneResources[i]);
- }
- void LoadScene(const String&in fileName)
- {
- if (fileName.empty)
- return;
-
- ui.cursor.shape = CS_BUSY;
-
- // Always load the scene from the filesystem, not from resource paths
- if (!fileSystem.FileExists(fileName))
- {
- log.Error("No such scene: " + fileName);
- return;
- }
- File file(fileName, FILE_READ);
- if (!file.open)
- return;
- // Clear the old scene
- ClearSelection();
- ClearSceneWindow();
- editorScene.Clear();
- // Add the new resource path
- SetResourcePath(GetPath(fileName));
- String extension = GetExtension(fileName);
- if (extension != ".xml")
- editorScene.Load(file);
- else
- editorScene.LoadXML(file);
- // Always pause the scene, and do updates manually
- editorScene.active = false;
- sceneFileName = fileName;
- sceneModified = false;
- runUpdate = false;
- UpdateWindowTitle();
- UpdateSceneWindow();
- UpdateNodeWindow();
- ResetCamera();
- CreateGizmo();
- }
- void SaveScene(const String&in fileName)
- {
- if (fileName.empty || GetFileName(fileName).empty)
- return;
- // Unpause when saving so that the scene will work properly when loaded outside the editor
- editorScene.active = true;
- File file(fileName, FILE_WRITE);
- String extension = GetExtension(fileName);
- if (extension != ".xml")
- editorScene.Save(file);
- else
- editorScene.SaveXML(file);
- editorScene.active = false;
- sceneFileName = fileName;
- sceneModified = false;
- UpdateWindowTitle();
- }
- void LoadNode(const String&in fileName)
- {
- if (fileName.empty)
- return;
-
- if (!fileSystem.FileExists(fileName))
- {
- log.Error("No such node file " + fileName);
- return;
- }
- File file(fileName, FILE_READ);
- if (!file.open)
- return;
- // Before instantiating, set resource path if empty
- if (sceneResourcePath.empty)
- SetResourcePath(GetPath(fileName));
- Vector3 position = GetNewNodePosition();
- Node@ newNode;
- String extension = GetExtension(fileName);
- if (extension != ".xml")
- newNode = editorScene.Instantiate(file, position, Quaternion(), instantiateMode);
- else
- newNode = editorScene.InstantiateXML(file, position, Quaternion(), instantiateMode);
- if (newNode !is null)
- {
- UpdateAndFocusNode(newNode);
- instantiateFileName = fileName;
- }
- }
- void SaveNode(const String&in fileName)
- {
- if (fileName.empty || GetFileName(fileName).empty)
- return;
- if (selectedNodes.length == 1)
- {
- File file(fileName, FILE_WRITE);
- if (!file.open)
- return;
- String extension = GetExtension(fileName);
- if (extension != ".xml")
- selectedNodes[0].Save(file);
- else
- selectedNodes[0].SaveXML(file);
- instantiateFileName = fileName;
- }
- }
- void UpdateScene(float timeStep)
- {
- if (runUpdate)
- editorScene.Update(timeStep);
- }
- void BeginModify(uint nodeID)
- {
- // Undo/Redo can be implemented here
- }
- void EndModify(uint nodeID)
- {
- // Undo/Redo can be implemented here
- if (!sceneModified)
- {
- sceneModified = true;
- UpdateWindowTitle();
- }
- }
- void BeginSelectionModify()
- {
- // A large operation on selected nodes is about to begin. Disable intermediate selection updates
- inSelectionModify = true;
- }
- void EndSelectionModify()
- {
- // The large operation on selected nodes has ended. Update node/component selection now
- inSelectionModify = false;
- HandleSceneWindowSelectionChange();
- }
- bool SceneDelete()
- {
- if (!CheckSceneWindowFocus() || (selectedComponents.empty && selectedNodes.empty))
- return false;
- BeginSelectionModify();
- ListView@ list = sceneWindow.GetChild("NodeList", true);
- // Remove nodes
- for (uint i = 0; i < selectedNodes.length; ++i)
- {
- Node@ node = selectedNodes[i];
- if (node.parent is null || node.scene is null)
- continue; // Root or already deleted
- uint id = node.id;
- uint nodeIndex = GetNodeListIndex(node);
- BeginModify(id);
- node.Remove();
- EndModify(id);
- UpdateSceneWindowNode(nodeIndex, null);
- // If deleting only one node, select the next item in the same index
- if (selectedNodes.length == 1 && selectedComponents.empty)
- list.selection = nodeIndex;
- }
- // Then remove components, if they still remain
- for (uint i = 0; i < selectedComponents.length; ++i)
- {
- Component@ component = selectedComponents[i];
- Node@ node = component.node;
- if (node is null)
- continue; // Already deleted
- uint index = GetComponentListIndex(component);
- uint nodeIndex = GetNodeListIndex(node);
- if (index == NO_ITEM || nodeIndex == NO_ITEM)
- continue;
- // Do not allow to remove the Octree, PhysicsWorld or DebugRenderer from the root node
- if (node is editorScene && (component.typeName == "Octree" || component.typeName == "PhysicsWorld" ||
- component.typeName == "DebugRenderer"))
- continue;
- uint id = node.id;
- BeginModify(id);
- node.RemoveComponent(component);
- EndModify(id);
- UpdateSceneWindowNode(nodeIndex, node);
- // If deleting only one component, select the next item in the same index
- if (selectedComponents.length == 1 && selectedNodes.empty)
- list.selection = index;
- }
- EndSelectionModify();
- return true;
- }
- bool SceneCut()
- {
- if (SceneCopy())
- return SceneDelete();
- else
- return false;
- }
- bool SceneCopy()
- {
- if ((selectedNodes.empty && selectedComponents.empty) || !CheckSceneWindowFocus())
- return false;
- // Must have either only components, or only nodes
- if (!selectedNodes.empty && !selectedComponents.empty)
- return false;
- ListView@ list = sceneWindow.GetChild("NodeList", true);
- copyBuffer.Clear();
- // Copy components
- if (!selectedComponents.empty)
- {
- for (uint i = 0; i < selectedComponents.length; ++i)
- {
- XMLFile@ xml = XMLFile();
- XMLElement rootElem = xml.CreateRoot("component");
- selectedComponents[i].SaveXML(rootElem);
- rootElem.SetBool("local", selectedComponents[i].id >= FIRST_LOCAL_ID);
- copyBuffer.Push(xml);
- }
- return true;
- }
- // Copy node. The root node can not be copied
- else
- {
- for (uint i = 0; i < selectedNodes.length; ++i)
- {
- if (selectedNodes[i] is editorScene)
- return false;
- }
- for (uint i = 0; i < selectedNodes.length; ++i)
- {
- XMLFile@ xml = XMLFile();
- XMLElement rootElem = xml.CreateRoot("node");
- selectedNodes[i].SaveXML(rootElem);
- rootElem.SetBool("local", selectedNodes[i].id >= FIRST_LOCAL_ID);
- copyBuffer.Push(xml);
- }
- copyBufferExpanded = SaveExpandedStatus(GetNodeListIndex(selectedNodes[0]));
- return true;
- }
- }
- bool ScenePaste()
- {
- if (editNode is null || !CheckSceneWindowFocus() || copyBuffer.empty)
- return false;
- ListView@ list = sceneWindow.GetChild("NodeList", true);
- bool pasteComponents = false;
- for (uint i = 0; i < copyBuffer.length; ++i)
- {
- XMLElement rootElem = copyBuffer[i].root;
- String mode = rootElem.name;
- if (mode == "component")
- {
- pasteComponents = true;
- // If this is the root node, do not allow to create duplicate scene-global components
- if (editNode is editorScene && CheckForExistingGlobalComponent(editNode, rootElem.GetAttribute("type")))
- return false;
- BeginModify(editNode.id);
- // If copied component was local, make the new local too
- Component@ newComponent = editNode.CreateComponent(rootElem.GetAttribute("type"), rootElem.GetBool("local") ? LOCAL :
- REPLICATED);
- if (newComponent is null)
- {
- EndModify(editNode.id);
- return false;
- }
- newComponent.LoadXML(rootElem);
- newComponent.ApplyAttributes();
- EndModify(editNode.id);
- }
- else if (mode == "node")
- {
- // Make the paste go always to the root node, no matter of the selected node
- BeginModify(editorScene.id);
- // If copied node was local, make the new local too
- Node@ newNode = editorScene.CreateChild("", rootElem.GetBool("local") ? LOCAL : REPLICATED);
- BeginModify(newNode.id);
- newNode.LoadXML(rootElem);
- newNode.ApplyAttributes();
- EndModify(newNode.id);
- EndModify(editorScene.id);
- uint addIndex = GetParentAddIndex(newNode);
- UpdateSceneWindowNode(addIndex, newNode);
- RestoreExpandedStatus(addIndex, copyBufferExpanded);
- }
- }
- if (pasteComponents)
- UpdateSceneWindowNode(editNode);
- return true;
- }
- void SceneUnparent()
- {
- if (!CheckSceneWindowFocus() || !selectedComponents.empty || selectedNodes.empty)
- return;
- ListView@ list = sceneWindow.GetChild("NodeList", true);
- list.contentElement.DisableLayoutUpdate();
- // Parent selected nodes to root
- for (uint i = 0; i < selectedNodes.length; ++i)
- {
- Node@ sourceNode = selectedNodes[i];
- if (sourceNode.parent is null || sourceNode.parent is editorScene)
- continue; // Root or already parented to root
- Node@ targetNode = editorScene;
- // Perform the reparenting
- BeginModify(targetNode.id);
- BeginModify(sourceNode.id);
- sourceNode.parent = targetNode;
- EndModify(sourceNode.id);
- EndModify(targetNode.id);
- ListView@ list = sceneWindow.GetChild("NodeList", true);
- uint sourceIndex = GetNodeListIndex(sourceNode);
- bool expanded = SaveExpandedStatus(sourceIndex);
- list.RemoveItem(sourceIndex);
- uint addIndex = GetParentAddIndex(sourceNode);
- UpdateSceneWindowNode(addIndex, sourceNode);
- UpdateNodeAttributes();
- RestoreExpandedStatus(addIndex, expanded);
- }
- list.contentElement.EnableLayoutUpdate();
- list.contentElement.UpdateLayout();
- }
- void SceneResetPosition()
- {
- if (editNode !is null)
- {
- editNode.position = Vector3(0.0, 0.0, 0.0);
- UpdateNodeAttributes();
- }
- }
- void SceneResetRotation()
- {
- if (editNode !is null)
- {
- editNode.rotation = Quaternion();
- UpdateNodeAttributes();
- }
- }
- void SceneResetScale()
- {
- if (editNode !is null)
- {
- editNode.scale = Vector3(1.0, 1.0, 1.0);
- UpdateNodeAttributes();
- }
- }
- void SceneSelectAll()
- {
- ListView@ list = sceneWindow.GetChild("NodeList", true);
- if (!list.selections.empty)
- {
- BeginSelectionModify();
- list.ClearSelection();
- EndSelectionModify();
- }
- else
- {
- BeginSelectionModify();
- Array<Node@> rootLevelNodes = editorScene.GetChildren();
- Array<uint> indices;
- for (uint i = 0; i < rootLevelNodes.length; ++i)
- indices.Push(GetNodeListIndex(rootLevelNodes[i]));
- list.SetSelections(indices);
- EndSelectionModify();
- }
- }
|