Explorar el Código

Added Scene::Instantiate() & Scene::InstantiateXML(), which load a partial scene from binary or XML.
Added editor functions Load node & Save node, which use the above functionality.
Fixed threading crash with multiple shadowed directional lights.

Lasse Öörni hace 14 años
padre
commit
7f2f278c58

+ 53 - 1
Bin/Data/Scripts/Editor/EditorScene.as

@@ -13,6 +13,7 @@ Scene@ editorScene;
 
 String sceneFileName;
 String sceneResourcePath;
+String nodeFileName;
 bool sceneModified = false;
 bool runUpdate = false;
 
@@ -35,7 +36,7 @@ void ClearSelection()
     editNode = null;
     editNodes.Clear();
     editComponents.Clear();
-    
+
     HideGizmo();
 }
 
@@ -234,6 +235,57 @@ void SaveScene(const String&in fileName)
     UpdateWindowTitle();
 }
 
+void InstantiateNode(const String&in fileName)
+{
+    if (!fileSystem.FileExists(fileName))
+    {
+        log.Error("No such node file " + fileName);
+        return;
+    }
+
+    File file(fileName, FILE_READ);
+    if (!file.open)
+        return;
+
+    Vector3 position = GetNewNodePosition();
+    Node@ newNode;
+
+    String extension = GetExtension(fileName);
+    if (extension != ".xml")
+        newNode = editorScene.Instantiate(file, position, Quaternion());
+    else
+        newNode = editorScene.InstantiateXML(file, position, Quaternion());
+
+    if (newNode !is null)
+    {
+        UpdateAndFocusNode(newNode);
+        nodeFileName = fileName;
+    }
+}
+
+void SaveNode(const String&in fileName)
+{
+    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
+        {
+            XMLFile xml;
+            XMLElement root = xml.CreateRoot("node");
+            selectedNodes[0].SaveXML(root);
+            xml.Save(file);
+        }
+
+        nodeFileName = fileName;
+    }
+}
+
 void UpdateScene(float timeStep)
 {
     if (runUpdate)

+ 58 - 11
Bin/Data/Scripts/Editor/EditorUI.as

@@ -9,6 +9,7 @@ Array<String> uiAllFilter = {"*.*"};
 Array<String> uiScriptFilter = {"*.as", "*.*"};
 uint uiSceneFilter = 0;
 String uiScenePath;
+String uiNodePath;
 String uiImportPath;
 String uiScriptPath;
 
@@ -59,6 +60,9 @@ void CreateMenuBar()
         filePopup.AddChild(CreateMenuItem("Save scene", 'S', QUAL_CTRL));
         filePopup.AddChild(CreateMenuItem("Save scene as", 'S', QUAL_SHIFT | QUAL_CTRL));
         filePopup.AddChild(CreateMenuDivider());
+        filePopup.AddChild(CreateMenuItem("Load node", 0, 0));
+        filePopup.AddChild(CreateMenuItem("Save node", 0, 0));
+        filePopup.AddChild(CreateMenuDivider());
         filePopup.AddChild(CreateMenuItem("Import model", 0, 0));
         filePopup.AddChild(CreateMenuItem("Import scene", 0, 0));
         filePopup.AddChild(CreateMenuItem("Run script", 0, 0));
@@ -84,7 +88,7 @@ void CreateMenuBar()
         editPopup.AddChild(CreateMenuItem("Toggle update", 'P', QUAL_CTRL));
         uiMenuBar.AddChild(editMenu);
     }
-    
+
     {
         Menu@ fileMenu = CreateMenu("View");
         Window@ filePopup = fileMenu.popup;
@@ -149,7 +153,7 @@ Menu@ CreateMenu(const String&in title)
 void CreateFileSelector(const String&in title, const String&in ok, const String&in cancel, const String&in initialPath, Array<String>@ filters,
     uint initialFilter)
 {
-    // Within the editor UI, the file selector is a kind of a "singleton". When the previous one is overwritten, also 
+    // Within the editor UI, the file selector is a kind of a "singleton". When the previous one is overwritten, also
     // the events subscribed from it are disconnected, so new ones are safe to subscribe.
     uiFileSelector = FileSelector();
     uiFileSelector.style = uiStyle;
@@ -193,7 +197,7 @@ void UpdateWindowTitle()
         sceneName = "Untitled";
     if (sceneModified)
         sceneName += "*";
-    
+
     graphics.windowTitle = "Urho3D editor - " + sceneName;
 }
 
@@ -206,7 +210,7 @@ void HandleMenuSelected(StringHash eventType, VariantMap& eventData)
     String action = menu.name;
     if (action.empty)
         return;
-        
+
     // Close the menu now
     UIElement@ menuParent = menu.parent;
     Menu@ topLevelMenu = menuParent.vars["Origin"].GetUIElement();
@@ -217,7 +221,7 @@ void HandleMenuSelected(StringHash eventType, VariantMap& eventData)
     {
         if (action == "New scene")
             CreateScene();
-            
+
         if (action == "Open scene")
         {
             CreateFileSelector("Open scene", "Open", "Cancel", uiScenePath, uiSceneFilters, uiSceneFilter);
@@ -234,6 +238,19 @@ void HandleMenuSelected(StringHash eventType, VariantMap& eventData)
             SubscribeToEvent(uiFileSelector, "FileSelected", "HandleSaveSceneFile");
         }
 
+        if (action == "Load node")
+        {
+            CreateFileSelector("Load node", "Load", "Cancel", uiNodePath, uiSceneFilters, uiSceneFilter);
+            SubscribeToEvent(uiFileSelector, "FileSelected", "HandleLoadNodeFile");
+        }
+
+        if (action == "Save node" && selectedNodes.length == 1 && selectedNodes[0] !is editorScene)
+        {
+            CreateFileSelector("Save node", "Save", "Cancel", uiNodePath, uiSceneFilters, uiSceneFilter);
+            uiFileSelector.fileName = GetFileNameAndExtension(nodeFileName);
+            SubscribeToEvent(uiFileSelector, "FileSelected", "HandleSaveNodeFile");
+        }
+
         if (action == "Import model")
         {
             CreateFileSelector("Import model", "Import", "Cancel", uiImportPath, uiAllFilter, 0);
@@ -245,7 +262,7 @@ void HandleMenuSelected(StringHash eventType, VariantMap& eventData)
             CreateFileSelector("Import scene", "Import", "Cancel", uiImportPath, uiAllFilter, 0);
             SubscribeToEvent(uiFileSelector, "FileSelected", "HandleImportScene");
         }
-        
+
         if (action == "Run script")
         {
             CreateFileSelector("Run script", "Run", "Cancel", uiScriptPath, uiScriptFilter, 0);
@@ -271,19 +288,19 @@ void HandleMenuSelected(StringHash eventType, VariantMap& eventData)
 
     if (action == "Editor settings")
         ShowEditorSettingsDialog();
-    
+
     if (action == "Cut")
         SceneCut();
-    
+
     if (action == "Copy")
         SceneCopy();
-    
+
     if (action == "Paste")
         ScenePaste();
-    
+
     if (action == "Delete")
         SceneDelete();
-        
+
     if (action == "Unparent")
         SceneUnparent();
 
@@ -327,6 +344,36 @@ void HandleSaveSceneFile(StringHash eventType, VariantMap& eventData)
     SaveScene(fileName);
 }
 
+void HandleLoadNodeFile(StringHash eventType, VariantMap& eventData)
+{
+    // Save filter for next time
+    uiSceneFilter = uiFileSelector.filterIndex;
+    uiNodePath = uiFileSelector.path;
+    CloseFileSelector();
+
+    // Check for cancel
+    if (!eventData["OK"].GetBool())
+        return;
+
+    String fileName = eventData["FileName"].GetString();
+    InstantiateNode(fileName);
+}
+
+void HandleSaveNodeFile(StringHash eventType, VariantMap& eventData)
+{
+    // Save filter for next time
+    uiSceneFilter = uiFileSelector.filterIndex;
+    uiNodePath = uiFileSelector.path;
+    CloseFileSelector();
+
+    // Check for cancel
+    if (!eventData["OK"].GetBool())
+        return;
+
+    String fileName = eventData["FileName"].GetString();
+    SaveNode(fileName);
+}
+
 void HandleImportModel(StringHash eventType, VariantMap& eventData)
 {
     // Save path for next time

+ 3 - 0
Docs/ScriptAPI.dox

@@ -1337,6 +1337,9 @@ Methods:<br>
 - bool LoadAsync(File@)
 - bool LoadAsyncXML(File@)
 - void StopAsyncLoading()
+- Node@ Instantiate(File@, const Vector3&, const Quaternion&, CreateMode arg3 = REPLICATED)
+- Node@ InstantiateXML(File@, const Vector3&, const Quaternion&, CreateMode arg3 = REPLICATED)
+- Node@ InstantiateXML(XMLFile@, const Vector3&, const Quaternion&, CreateMode arg3 = REPLICATED)
 - void Clear()
 - void AddRequiredPackageFile(PackageFile@)
 - void ClearRequiredPackageFiles()

+ 27 - 0
Engine/Engine/SceneAPI.cpp

@@ -75,6 +75,30 @@ static bool SceneSaveXML(File* file, Scene* ptr)
         return false;
 }
 
+static Node* SceneInstantiate(File* file, const Vector3& position, const Quaternion& rotation, CreateMode mode, Scene* ptr)
+{
+    if (file)
+        return ptr->Instantiate(*file, position, rotation, mode);
+    else
+        return 0;
+}
+
+static Node* SceneInstantiateXML(File* file, const Vector3& position, const Quaternion& rotation, CreateMode mode, Scene* ptr)
+{
+    if (file)
+        return ptr->InstantiateXML(*file, position, rotation, mode);
+    else
+        return 0;
+}
+
+static Node* SceneInstantiateXMLFile(XMLFile* xmlFile, const Vector3& position, const Quaternion& rotation, CreateMode mode, Scene* ptr)
+{
+    if (xmlFile)
+        return ptr->InstantiateXML(xmlFile->GetRoot(), position, rotation, mode);
+    else
+        return 0;
+}
+
 static CScriptArray* SceneGetRequiredPackageFiles(Scene* ptr)
 {
     return VectorToHandleArray<PackageFile>(ptr->GetRequiredPackageFiles(), "Array<PackageFile@>");
@@ -113,6 +137,9 @@ static void RegisterScene(asIScriptEngine* engine)
     engine->RegisterObjectMethod("Scene", "bool LoadAsync(File@+)", asMETHOD(Scene, LoadAsync), asCALL_THISCALL);
     engine->RegisterObjectMethod("Scene", "bool LoadAsyncXML(File@+)", asMETHOD(Scene, LoadAsyncXML), asCALL_THISCALL);
     engine->RegisterObjectMethod("Scene", "void StopAsyncLoading()", asMETHOD(Scene, StopAsyncLoading), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Scene", "Node@+ Instantiate(File@+, const Vector3&in, const Quaternion&in, CreateMode mode = REPLICATED)", asFUNCTION(SceneInstantiate), asCALL_CDECL_OBJLAST);
+    engine->RegisterObjectMethod("Scene", "Node@+ InstantiateXML(File@+, const Vector3&in, const Quaternion&in, CreateMode mode = REPLICATED)", asFUNCTION(SceneInstantiateXML), asCALL_CDECL_OBJLAST);
+    engine->RegisterObjectMethod("Scene", "Node@+ InstantiateXML(XMLFile@+, const Vector3&in, const Quaternion&in, CreateMode mode = REPLICATED)", asFUNCTION(SceneInstantiateXMLFile), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod("Scene", "void Clear()", asMETHOD(Scene, Clear), asCALL_THISCALL);
     engine->RegisterObjectMethod("Scene", "void AddRequiredPackageFile(PackageFile@+)", asMETHOD(Scene, AddRequiredPackageFile), asCALL_THISCALL);
     engine->RegisterObjectMethod("Scene", "void ClearRequiredPackageFiles()", asMETHOD(Scene, ClearRequiredPackageFiles), asCALL_THISCALL);

+ 8 - 7
Engine/Graphics/View.cpp

@@ -1783,7 +1783,8 @@ void View::SetupDirLightShadowCamera(Camera* shadowCamera, Light* light, float n
     }
     
     Frustum splitFrustum = camera_->GetSplitFrustum(nearSplit, farSplit);
-    frustumVolume_.Define(splitFrustum);
+    Polyhedron frustumVolume;
+    frustumVolume.Define(splitFrustum);
     // If focusing enabled, clip the frustum volume by the combined bounding box of the lit geometries within the frustum
     if (parameters.focus_)
     {
@@ -1801,23 +1802,23 @@ void View::SetupDirLightShadowCamera(Camera* shadowCamera, Light* light, float n
         }
         if (litGeometriesBox.defined_)
         {
-            frustumVolume_.Clip(litGeometriesBox);
+            frustumVolume.Clip(litGeometriesBox);
             // If volume became empty, restore it to avoid zero size
-            if (frustumVolume_.Empty())
-                frustumVolume_.Define(splitFrustum);
+            if (frustumVolume.Empty())
+                frustumVolume.Define(splitFrustum);
         }
     }
     
     // Transform frustum volume to light space
     Matrix3x4 lightView(shadowCamera->GetInverseWorldTransform());
-    frustumVolume_.Transform(lightView);
+    frustumVolume.Transform(lightView);
     
     // Fit the frustum volume inside a bounding box. If uniform size, use a sphere instead
     BoundingBox shadowBox;
     if (!parameters.nonUniform_)
-        shadowBox.Define(Sphere(frustumVolume_));
+        shadowBox.Define(Sphere(frustumVolume));
     else
-        shadowBox.Define(frustumVolume_);
+        shadowBox.Define(frustumVolume);
     
     shadowCamera->SetOrthographic(true);
     shadowCamera->SetAspectRatio(1.0f);

+ 0 - 2
Engine/Graphics/View.h

@@ -215,8 +215,6 @@ private:
     BoundingBox sceneBox_;
     /// Combined bounding box of visible geometries in view space.
     BoundingBox sceneViewBox_;
-    /// Volume for frustum clipping.
-    Polyhedron frustumVolume_;
     /// Per-thread octree query results.
     Vector<PODVector<Drawable*> > tempDrawables_;
     /// Per-thread octree zone query results.

+ 4 - 4
Engine/Scene/Node.cpp

@@ -1025,7 +1025,7 @@ bool Node::Load(Deserializer& source, SceneResolver& resolver, bool readChildren
         VectorBuffer compBuffer(source, source.ReadVLE());
         ShortStringHash compType = compBuffer.ReadShortStringHash();
         unsigned compID = compBuffer.ReadUInt();
-        Component* newComponent = CreateComponent(compType, compID, compID < FIRST_LOCAL_ID ? REPLICATED : LOCAL);
+        Component* newComponent = CreateComponent(compType, compID, id_ < FIRST_LOCAL_ID ? REPLICATED : LOCAL);
         if (newComponent)
         {
             resolver.AddComponent(compID, newComponent);
@@ -1041,7 +1041,7 @@ bool Node::Load(Deserializer& source, SceneResolver& resolver, bool readChildren
     for (unsigned i = 0; i < numChildren; ++i)
     {
         unsigned nodeID = source.ReadUInt();
-        Node* newNode = CreateChild(nodeID, nodeID < FIRST_LOCAL_ID ? REPLICATED : LOCAL);
+        Node* newNode = CreateChild(nodeID, id_ < FIRST_LOCAL_ID ? REPLICATED : LOCAL);
         resolver.AddNode(nodeID, newNode);
         if (!newNode->Load(source, resolver))
             return false;
@@ -1064,7 +1064,7 @@ bool Node::LoadXML(const XMLElement& source, SceneResolver& resolver, bool readC
     {
         String typeName = compElem.GetString("type");
         unsigned compID = compElem.GetInt("id");
-        Component* newComponent = CreateComponent(ShortStringHash(typeName), compID, compID < FIRST_LOCAL_ID ? REPLICATED : LOCAL);
+        Component* newComponent = CreateComponent(ShortStringHash(typeName), compID, id_ < FIRST_LOCAL_ID ? REPLICATED : LOCAL);
         if (newComponent)
         {
             resolver.AddComponent(compID, newComponent);
@@ -1082,7 +1082,7 @@ bool Node::LoadXML(const XMLElement& source, SceneResolver& resolver, bool readC
     while (childElem)
     {
         unsigned nodeID = childElem.GetInt("id");
-        Node* newNode = CreateChild(nodeID, nodeID < FIRST_LOCAL_ID ? REPLICATED : LOCAL);
+        Node* newNode = CreateChild(nodeID, id_ < FIRST_LOCAL_ID ? REPLICATED : LOCAL);
         resolver.AddNode(nodeID, newNode);
         if (!newNode->LoadXML(childElem, resolver))
             return false;

+ 53 - 4
Engine/Scene/Scene.cpp

@@ -229,14 +229,14 @@ bool Scene::LoadAsyncXML(File* file)
     
     StopAsyncLoading();
     
-    SharedPtr<XMLFile> xmlFile(new XMLFile(context_));
-    if (!xmlFile->Load(*file))
+    SharedPtr<XMLFile> xml(new XMLFile(context_));
+    if (!xml->Load(*file))
         return false;
     
     LOGINFO("Loading scene from " + file->GetName());
     
     Clear();
-    XMLElement rootElement = xmlFile->GetRoot();
+    XMLElement rootElement = xml->GetRoot();
     
     // Store own old ID for resolving possible root node references
     unsigned nodeID = rootElement.GetInt("id");
@@ -250,7 +250,7 @@ bool Scene::LoadAsyncXML(File* file)
     XMLElement childNodeElement = rootElement.GetChild("node");
     asyncLoading_ = true;
     asyncProgress_.file_ = file;
-    asyncProgress_.xmlFile_ = xmlFile;
+    asyncProgress_.xmlFile_ = xml;
     asyncProgress_.xmlElement_ = childNodeElement;
     asyncProgress_.loadedNodes_ = 0;
     asyncProgress_.totalNodes_ = 0;
@@ -274,6 +274,55 @@ void Scene::StopAsyncLoading()
     resolver_.Reset();
 }
 
+Node* Scene::Instantiate(Deserializer& source, const Vector3& position, const Quaternion& rotation, CreateMode mode)
+{
+    SceneResolver resolver;
+    unsigned nodeID = source.ReadInt();
+    Node* node = CreateChild(nodeID, mode);
+    resolver.AddNode(nodeID, node);
+    if (node->Load(source, resolver))
+    {
+        resolver.Resolve();
+        node->ApplyAttributes();
+        node->SetTransform(position, rotation);
+        return node;
+    }
+    else
+    {
+        node->Remove();
+        return 0;
+    }
+}
+
+Node* Scene::InstantiateXML(Deserializer& source, const Vector3& position, const Quaternion& rotation, CreateMode mode)
+{
+    SharedPtr<XMLFile> xml(new XMLFile(context_));
+    if (!xml->Load(source))
+        return false;
+    
+    return InstantiateXML(xml->GetRoot(), position, rotation, mode);
+}
+
+Node* Scene::InstantiateXML(const XMLElement& source, const Vector3& position, const Quaternion& rotation, CreateMode mode)
+{
+    SceneResolver resolver;
+    unsigned nodeID = source.GetInt("id");
+    Node* node = CreateChild(nodeID, mode);
+    resolver.AddNode(nodeID, node);
+    if (node->LoadXML(source, resolver))
+    {
+        resolver.Resolve();
+        node->ApplyAttributes();
+        node->SetTransform(position, rotation);
+        return node;
+    }
+    else
+    {
+        node->Remove();
+        return 0;
+    }
+}
+
 void Scene::Clear()
 {
     StopAsyncLoading();

+ 6 - 0
Engine/Scene/Scene.h

@@ -84,6 +84,12 @@ public:
     bool LoadAsyncXML(File* file);
     /// Stop asynchronous loading.
     void StopAsyncLoading();
+    /// Instantiate scene content from binary data. Return root node if successful.
+    Node* Instantiate(Deserializer& source, const Vector3& position, const Quaternion& rotation, CreateMode mode = REPLICATED);
+    /// Instantiate scene content from XML data. Return root node if successful.
+    Node* InstantiateXML(Deserializer& source, const Vector3& position, const Quaternion& rotation, CreateMode mode = REPLICATED);
+    /// Instantiate scene content from XML data. Return root node if successful.
+    Node* InstantiateXML(const XMLElement& source, const Vector3& position, const Quaternion& rotation, CreateMode mode = REPLICATED);
     /// Clear scene completely of nodes and components.
     void Clear();
     /// %Set active flag. Only active scenes will be updated automatically.