diff --git a/assets/sun/sun.asset b/assets/sun/sun.asset index 2237677c5e..0b47186acf 100644 --- a/assets/sun/sun.asset +++ b/assets/sun/sun.asset @@ -3,8 +3,7 @@ local AssetHelper = asset.import('assethelper') asset.SolarSystem = { - Name = "SolarSystem", - Parent = "Root" + Name = "SolarSystem" } asset.SolarSystemBarycenter = { diff --git a/include/openspace/scene/scene.h b/include/openspace/scene/scene.h index c5a8730a39..25b4daf0b1 100644 --- a/include/openspace/scene/scene.h +++ b/include/openspace/scene/scene.h @@ -34,9 +34,11 @@ #include #include +#include #include #include + namespace ghoul { class Dictionary; } namespace openspace { @@ -72,9 +74,14 @@ public: void clear(); /** - * Set the root node of the scene + * Attach node to the root */ - void setRoot(std::unique_ptr root); + void attachNode(std::unique_ptr node); + + /** + * Detach node from the root + */ + std::unique_ptr detachNode(SceneGraphNode& node); /** * Set the camera of the scene @@ -99,7 +106,12 @@ public: /** * Return the root SceneGraphNode. */ - SceneGraphNode* root() const; + SceneGraphNode* root(); + + /** + * Return the root SceneGraphNode. + */ + const SceneGraphNode* root() const; /** * Return the scenegraph node with the specified name or nullptr if that @@ -110,17 +122,17 @@ public: /** * Add a node and all its children to the scene. */ - void addNode(SceneGraphNode* node, UpdateDependencies updateDeps = UpdateDependencies::Yes); + void registerNode(SceneGraphNode* node); /** * Remove a node and all its children from the scene. */ - void removeNode(SceneGraphNode* node, UpdateDependencies updateDeps = UpdateDependencies::Yes); + void unregisterNode(SceneGraphNode* node); /** - * Update dependencies. - */ - void updateDependencies(); + * Mark the node registry as dirty + */ + void markNodeRegistryDirty(); /** * Return a vector of all scene graph nodes in the scene. @@ -152,13 +164,19 @@ public: private: std::string generateJson() const override; + /** + * Update dependencies. + */ + void updateNodeRegistry(); + void sortTopologically(); - std::unique_ptr _root; std::unique_ptr _camera; std::vector _topologicallySortedNodes; std::vector _circularNodes; std::map _nodesByName; + bool _dirtyNodeRegistry; + SceneGraphNode _rootDummy; std::mutex _programUpdateLock; std::set _programsToUpdate; diff --git a/include/openspace/scene/scenegraphnode.h b/include/openspace/scene/scenegraphnode.h index de8d49b787..2fc8a14a36 100644 --- a/include/openspace/scene/scenegraphnode.h +++ b/include/openspace/scene/scenegraphnode.h @@ -83,14 +83,15 @@ public: void render(const RenderData& data, RendererTasks& tasks); void updateCamera(Camera* camera) const; - void attachChild(std::unique_ptr child, UpdateScene updateScene = UpdateScene::Yes); - std::unique_ptr detachChild(SceneGraphNode& child, UpdateScene updateScene = UpdateScene::Yes); - void setParent(SceneGraphNode& parent, UpdateScene updateScene = UpdateScene::Yes); + void attachChild(std::unique_ptr child); + std::unique_ptr detachChild(SceneGraphNode& child); + void clearChildren(); + void setParent(SceneGraphNode& parent); - void addDependency(SceneGraphNode& dependency, UpdateScene updateScene = UpdateScene::Yes); - void removeDependency(SceneGraphNode& dependency, UpdateScene updateScene = UpdateScene::Yes); - void clearDependencies(UpdateScene updateScene = UpdateScene::Yes); - void setDependencies(const std::vector& dependencies, UpdateScene updateScene = UpdateScene::Yes); + void addDependency(SceneGraphNode& dependency); + void removeDependency(SceneGraphNode& dependency); + void clearDependencies(); + void setDependencies(const std::vector& dependencies); const std::vector& dependencies() const; const std::vector& dependentNodes() const; diff --git a/src/engine/openspaceengine.cpp b/src/engine/openspaceengine.cpp index 9d0d7dabb1..cc457a3fd1 100644 --- a/src/engine/openspaceengine.cpp +++ b/src/engine/openspaceengine.cpp @@ -573,6 +573,7 @@ void OpenSpaceEngine::loadScene(const std::string& scenePath) { _renderEngine->setScene(nullptr); _renderEngine->setCamera(nullptr); _interactionHandler->setCamera(nullptr); + _scene->clear(); } _scene = std::make_unique(); diff --git a/src/scene/assetloader.cpp b/src/scene/assetloader.cpp index 75c8486ece..7282425a70 100644 --- a/src/scene/assetloader.cpp +++ b/src/scene/assetloader.cpp @@ -434,6 +434,10 @@ ghoul::Dictionary AssetLoader::Asset::dataDictionary() { } void AssetLoader::Asset::addDependency(Asset* asset) { + // Do nothing if the dependency already exists. + if (std::find(_dependencies.begin(), _dependencies.end(), asset) != _dependencies.end()) { + return; + } if (_initialized) { asset->initialize(); } diff --git a/src/scene/scene.cpp b/src/scene/scene.cpp index 93e6cf47f0..e04ffca607 100644 --- a/src/scene/scene.cpp +++ b/src/scene/scene.cpp @@ -32,7 +32,6 @@ #include #include #include -#include #include #include #include @@ -93,22 +92,23 @@ Scene::Scene() }, JsFilename ) -{ - std::unique_ptr rootNode = std::make_unique(); - rootNode->setName(SceneGraphNode::RootNodeName); - setRoot(std::move(rootNode)); + , _dirtyNodeRegistry(false) +{ + _rootDummy.setName(SceneGraphNode::RootNodeName); + _rootDummy.setScene(this); } -Scene::~Scene(){ +Scene::~Scene() { + clear(); + _rootDummy.setScene(nullptr); } - -void Scene::setRoot(std::unique_ptr root) { - if (_root) { - removeNode(_root.get()); - } - _root = std::move(root); - _root->setScene(this); - addNode(_root.get()); + +void Scene::attachNode(std::unique_ptr node) { + _rootDummy.attachChild(std::move(node)); +} + +std::unique_ptr Scene::detachNode(SceneGraphNode& node) { + return _rootDummy.detachChild(node); } void Scene::setCamera(std::unique_ptr camera) { @@ -119,35 +119,28 @@ Camera* Scene::camera() const { return _camera.get(); } -void Scene::addNode(SceneGraphNode* node, UpdateDependencies updateDeps) { - // Add the node and all its children. - node->traversePreOrder([this](SceneGraphNode* n) { - _topologicallySortedNodes.push_back(n); - _nodesByName[n->name()] = n; - }); - - if (updateDeps) { - updateDependencies(); - } +void Scene::registerNode(SceneGraphNode* node) { + _topologicallySortedNodes.push_back(node); + _nodesByName[node->name()] = node; + _dirtyNodeRegistry = true; } -void Scene::removeNode(SceneGraphNode* node, UpdateDependencies updateDeps) { - // Remove the node and all its children. - node->traversePostOrder([this](SceneGraphNode* node) { - _topologicallySortedNodes.erase( - std::remove(_topologicallySortedNodes.begin(), _topologicallySortedNodes.end(), node), - _topologicallySortedNodes.end() - ); - _nodesByName.erase(node->name()); - }); - - if (updateDeps) { - updateDependencies(); - } +void Scene::unregisterNode(SceneGraphNode* node) { + _topologicallySortedNodes.erase( + std::remove(_topologicallySortedNodes.begin(), _topologicallySortedNodes.end(), node), + _topologicallySortedNodes.end() + ); + _nodesByName.erase(node->name()); + _dirtyNodeRegistry = true; } -void Scene::updateDependencies() { +void Scene::markNodeRegistryDirty() { + _dirtyNodeRegistry = true; +} + +void Scene::updateNodeRegistry() { sortTopologically(); + _dirtyNodeRegistry = false; } void Scene::sortTopologically() { @@ -217,6 +210,9 @@ void Scene::sortTopologically() { } void Scene::update(const UpdateData& data) { + if (_dirtyNodeRegistry) { + updateNodeRegistry(); + } for (SceneGraphNode* node : _topologicallySortedNodes) { try { LTRACE("Scene::update(begin '" + node->name() + "')"); @@ -244,15 +240,19 @@ void Scene::render(const RenderData& data, RendererTasks& tasks) { void Scene::clear() { LINFO("Clearing current scene graph"); - _root = nullptr; + _rootDummy.clearChildren(); } const std::map& Scene::nodesByName() const { return _nodesByName; } -SceneGraphNode* Scene::root() const { - return _root.get(); +SceneGraphNode* Scene::root() { + return &_rootDummy; +} + +const SceneGraphNode* Scene::root() const { + return &_rootDummy; } SceneGraphNode* Scene::sceneGraphNode(const std::string& name) const { @@ -269,11 +269,25 @@ const std::vector& Scene::allSceneGraphNodes() const { SceneGraphNode* Scene::loadNode(const ghoul::Dictionary& dict) { + // First interpret the dictionary std::vector dependencyNames; + const std::string nodeName = dict.value(KeyName); + const bool hasParent = dict.hasKey(KeyParentName); + + SceneGraphNode* parent = nullptr; + if (hasParent) { + std::string parentName = dict.value(KeyParentName); + parent = sceneGraphNode(parentName); + if (!parent) { + LERROR("Could not find parent '" + parentName + "' for '" + nodeName + "'"); + return nullptr; + } + } - std::string nodeName = dict.value(KeyName); - std::string parentName = dict.value(KeyParentName); std::unique_ptr node = SceneGraphNode::createFromDictionary(dict); + if (!node) { + LERROR("Could not create node from dictionary: " + nodeName); + } if (dict.hasKey(SceneGraphNode::KeyDependencies)) { if (!dict.hasValue(SceneGraphNode::KeyDependencies)) { @@ -289,12 +303,7 @@ SceneGraphNode* Scene::loadNode(const ghoul::Dictionary& dict) { } } - SceneGraphNode* parent = sceneGraphNode(parentName); - if (!parent) { - LERROR("Could not find parent '" + parentName + "' for '" + nodeName + "'"); - return nullptr; - } - + // Make sure all dependencies are found std::vector dependencies; bool foundAllDeps = true; for (const auto& depName : dependencyNames) { @@ -311,9 +320,16 @@ SceneGraphNode* Scene::loadNode(const ghoul::Dictionary& dict) { return nullptr; } + // Now attach the node to the graph SceneGraphNode* rawNodePointer = node.get(); - node->setDependencies(dependencies, SceneGraphNode::UpdateScene::No); - parent->attachChild(std::move(node)); + + if (parent) { + parent->attachChild(std::move(node)); + } else { + attachNode(std::move(node)); + } + + rawNodePointer->setDependencies(dependencies); return rawNodePointer; } diff --git a/src/scene/scenegraphnode.cpp b/src/scene/scenegraphnode.cpp index 822d71edba..bbc9244204 100644 --- a/src/scene/scenegraphnode.cpp +++ b/src/scene/scenegraphnode.cpp @@ -204,13 +204,13 @@ bool SceneGraphNode::initialize() { bool SceneGraphNode::deinitialize() { LDEBUG("Deinitialize: " << name()); + setScene(nullptr); + if (_renderable) { _renderable->deinitialize(); _renderable = nullptr; } - _children.clear(); - - // reset variables + clearChildren(); _parent = nullptr; return true; @@ -356,47 +356,27 @@ void SceneGraphNode::render(const RenderData& data, RendererTasks& tasks) { // child->render(newData); } -void SceneGraphNode::setParent(SceneGraphNode& parent, UpdateScene updateScene) { +void SceneGraphNode::setParent(SceneGraphNode& parent) { ghoul_assert(_parent != nullptr, "Node must be attached to a parent"); - ghoul_assert( - !updateScene || _scene == parent._scene, - "For the scene to be updated, this object must belong to the same scene as the parent" - ); - ghoul_assert( - !updateScene || _parent->_scene == parent._scene, - "Old and new parent cannot belong to separate scenes" - ); - - parent.attachChild(_parent->detachChild(*this, UpdateScene::No), UpdateScene::No); - - if (_scene && updateScene) { - _scene->updateDependencies(); - } + parent.attachChild(_parent->detachChild(*this)); } -void SceneGraphNode::attachChild(std::unique_ptr child, UpdateScene updateScene) { +void SceneGraphNode::attachChild(std::unique_ptr child) { ghoul_assert(child != nullptr, "Child may not be null"); ghoul_assert(child->parent() == nullptr, "Child may not already have a parent"); + // Create link between parent and child child->_parent = this; - if (_scene) { - child->setScene(_scene); - } - SceneGraphNode* childRaw = child.get(); _children.push_back(std::move(child)); - if (_scene && updateScene) { - _scene->addNode(childRaw); - } + // Set scene of child (and children recursively) + childRaw->setScene(_scene); } -std::unique_ptr SceneGraphNode::detachChild(SceneGraphNode& child, UpdateScene updateScene) { +std::unique_ptr SceneGraphNode::detachChild(SceneGraphNode& child) { ghoul_assert(child._dependentNodes.empty(), "Nodes cannot depend on a node being detached"); ghoul_assert(child._parent != nullptr, "Node must be attached to a parent"); - - // Update of deps is deffered to the removal of the node from the scene - clearDependencies(UpdateScene::No); auto iter = std::find_if( _children.begin(), @@ -406,29 +386,49 @@ std::unique_ptr SceneGraphNode::detachChild(SceneGraphNode& chil } ); - if (_scene && updateScene) { - _scene->removeNode(&child); + if (iter == _children.end()) { + LERROR("Trying to detach a non-existing child"); } + traversePreOrder([](SceneGraphNode* node) { + node->clearDependencies(); + }); + + // Unset scene of child (and children recursively) + if (_scene) { + child.setScene(nullptr); + } + + // Remove link between parent and child + child._parent = nullptr; std::unique_ptr c = std::move(*iter); _children.erase(iter); - if (_scene) { - setScene(nullptr); - } return c; } -void SceneGraphNode::addDependency(SceneGraphNode& dependency, UpdateScene updateScene) { +void SceneGraphNode::clearChildren() { + traversePreOrder([](SceneGraphNode* node) { + node->clearDependencies(); + }); + for (auto& c : _children) { + if (_scene) { + c->setScene(nullptr); + } + c->_parent = nullptr; + } + _children.clear(); +} + +void SceneGraphNode::addDependency(SceneGraphNode& dependency) { dependency._dependentNodes.push_back(this); _dependencies.push_back(&dependency); - - if (_scene && updateScene) { - _scene->updateDependencies(); + if (_scene) { + _scene->markNodeRegistryDirty(); } } -void SceneGraphNode::removeDependency(SceneGraphNode& dependency, UpdateScene updateScene) { +void SceneGraphNode::removeDependency(SceneGraphNode& dependency) { dependency._dependentNodes.erase(std::remove_if( dependency._dependentNodes.begin(), dependency._dependentNodes.end(), @@ -444,12 +444,12 @@ void SceneGraphNode::removeDependency(SceneGraphNode& dependency, UpdateScene up } ), _dependencies.end()); - if (_scene && updateScene) { - _scene->updateDependencies(); + if (_scene) { + _scene->markNodeRegistryDirty(); } } -void SceneGraphNode::clearDependencies(UpdateScene updateScene) { +void SceneGraphNode::clearDependencies() { for (auto dependency : _dependencies) { dependency->_dependentNodes.erase(std::remove_if( dependency->_dependentNodes.begin(), @@ -461,21 +461,21 @@ void SceneGraphNode::clearDependencies(UpdateScene updateScene) { } _dependencies.clear(); - if (_scene && updateScene) { - _scene->updateDependencies(); + if (_scene) { + _scene->markNodeRegistryDirty(); } } -void SceneGraphNode::setDependencies(const std::vector& dependencies, UpdateScene updateScene) { - clearDependencies(UpdateScene::No); +void SceneGraphNode::setDependencies(const std::vector& dependencies) { + clearDependencies(); _dependencies = dependencies; for (auto dependency : dependencies) { dependency->_dependentNodes.push_back(this); } - if (_scene && updateScene) { - _scene->updateDependencies(); + if (_scene) { + _scene->markNodeRegistryDirty(); } } @@ -568,9 +568,26 @@ Scene* SceneGraphNode::scene() { } void SceneGraphNode::setScene(Scene* scene) { + // Unregister from previous scene, bottom up + traversePostOrder([scene](SceneGraphNode* node) { + if (node->_scene) { + node->_scene->unregisterNode(node); + } + node->_scene = nullptr; + }); + + if (!scene) { + return; + } + + // Register on new scene, top down traversePreOrder([scene](SceneGraphNode* node) { node->_scene = scene; + if (scene) { + scene->registerNode(node); + } }); + } std::vector SceneGraphNode::children() const { diff --git a/src/scene/scenegraphnode_doc.inl b/src/scene/scenegraphnode_doc.inl index 2766c91ee4..45452a4416 100644 --- a/src/scene/scenegraphnode_doc.inl +++ b/src/scene/scenegraphnode_doc.inl @@ -46,13 +46,12 @@ documentation::Documentation SceneGraphNode::Documentation() { { "Parent", new StringAnnotationVerifier( - "Must be a name for another scenegraph node, or 'Root'" + "If specified, this must be a name for another scenegraph node" ), "This names the parent of the currently specified scenegraph node. The " - "parent must not have been defined earlier, but must exist at loading time, " - "or the scenegraph node creation will fail. A special parent 'Root' is " - "available that denotes the root of the scenegraph.", - Optional::No + "parent must already exist in the scene graph. If not specified, the node " + "will be attached to the root of the scenegraph.", + Optional::Yes }, { "Renderable",