diff --git a/data/scene/sun/sun.mod b/data/scene/sun/sun.mod index 3bd4e2218e..abe0f87cb9 100644 --- a/data/scene/sun/sun.mod +++ b/data/scene/sun/sun.mod @@ -1,4 +1,9 @@ return { + -- Solar system module + { + Name = "SolarSystem", + Parent = "Root" + }, -- Sun barycenter module { Name = "SolarSystemBarycenter", diff --git a/ext/ghoul b/ext/ghoul index f341d33a3b..57f9d38b3a 160000 --- a/ext/ghoul +++ b/ext/ghoul @@ -1 +1 @@ -Subproject commit f341d33a3bf968d7d6304039a111ad295b315939 +Subproject commit 57f9d38b3a36eb9295feae9f4036c7a4b19312fa diff --git a/include/openspace/engine/openspaceengine.h b/include/openspace/engine/openspaceengine.h index 9070e07f92..e9311c053a 100644 --- a/include/openspace/engine/openspaceengine.h +++ b/include/openspace/engine/openspaceengine.h @@ -52,6 +52,7 @@ class ModuleEngine; class WindowWrapper; class SettingsEngine; class TimeManager; +class SceneManager; class SyncEngine; class ParallelConnection; @@ -76,6 +77,7 @@ public: void setMaster(bool master); double runTime(); void setRunTime(double t); + void loadScene(const std::string& scenePath); // Guaranteed to return a valid pointer ConfigurationManager& configurationManager(); @@ -112,6 +114,7 @@ public: void externalControlCallback(const char* receivedChars, int size, int clientId); void encode(); void decode(); + void scheduleLoadScene(const std::string& scenePath); void enableBarrier(); void disableBarrier(); @@ -145,6 +148,7 @@ private: std::unique_ptr _configurationManager; std::unique_ptr _interactionHandler; std::unique_ptr _renderEngine; + std::unique_ptr _sceneManager; std::unique_ptr _scriptEngine; std::unique_ptr _scriptScheduler; std::unique_ptr _networkEngine; @@ -165,6 +169,9 @@ private: // Others std::unique_ptr _globalPropertyNamespace; + bool _switchScene; + std::string _scenePath; + bool _isMaster; double _runTime; diff --git a/include/openspace/engine/syncengine.h b/include/openspace/engine/syncengine.h index aa87c909f9..a099a24d22 100644 --- a/include/openspace/engine/syncengine.h +++ b/include/openspace/engine/syncengine.h @@ -85,6 +85,11 @@ public: */ void removeSyncable(Syncable* syncable); + /** + * Remove multiple Syncables from being synchronized over the SGCT cluster + */ + void removeSyncables(const std::vector& syncables); + private: /** diff --git a/include/openspace/interaction/interactionhandler.h b/include/openspace/interaction/interactionhandler.h index f4e9589573..50845e73ed 100644 --- a/include/openspace/interaction/interactionhandler.h +++ b/include/openspace/interaction/interactionhandler.h @@ -144,7 +144,6 @@ private: // Properties properties::StringProperty _origin; - properties::StringProperty _coordinateSystem; properties::BoolProperty _rotationalFriction; properties::BoolProperty _horizontalFriction; diff --git a/include/openspace/rendering/renderengine.h b/include/openspace/rendering/renderengine.h index 725bfcbfa7..614f1418ae 100644 --- a/include/openspace/rendering/renderengine.h +++ b/include/openspace/rendering/renderengine.h @@ -53,8 +53,8 @@ class PerformanceManager; // Forward declare to minimize dependencies class Camera; class SyncBuffer; - class Scene; +class SceneManager; class Renderer; class RaycasterManager; class ScreenLog; @@ -79,13 +79,14 @@ public: static const std::vector FrametimeTypes; RenderEngine(); - ~RenderEngine(); + ~RenderEngine() = default; bool initialize(); bool deinitialize(); - void setSceneGraph(Scene* sceneGraph); + void setScene(Scene* scene); Scene* scene(); + void updateScene(); Camera* camera() const; Renderer* renderer() const; @@ -95,7 +96,7 @@ public: // sgct wrapped functions bool initializeGL(); - void updateSceneGraph(); + void updateShaderPrograms(); void updateFade(); void updateRenderer(); @@ -153,6 +154,11 @@ public: */ void postRaycast(ghoul::opengl::ProgramObject& programObject); + /** + * Set the camera to use for rendering + */ + void setCamera(Camera* camera); + void setRendererFromString(const std::string& method); @@ -174,9 +180,6 @@ public: */ static scripting::LuaLibrary luaLibrary(); - // This is a temporary method to change the origin of the coordinate system ---abock - void changeViewPoint(std::string origin); - // Temporary fade functionality void startFading(int direction, float fadeDuration); @@ -193,9 +196,9 @@ private: void renderInformation(); - Camera* _mainCamera; - Scene* _sceneGraph; - RaycasterManager* _raycasterManager; + Camera* _camera; + Scene* _scene; + std::unique_ptr _raycasterManager; properties::BoolProperty _performanceMeasurements; std::unique_ptr _performanceManager; diff --git a/include/openspace/scene/scene.h b/include/openspace/scene/scene.h index 31dec8b3dd..2db72e10e5 100644 --- a/include/openspace/scene/scene.h +++ b/include/openspace/scene/scene.h @@ -35,8 +35,6 @@ #include #include #include -#include - #include #include @@ -48,28 +46,48 @@ class SceneGraphNode; // SceneGraphFinishedLoading class Scene { public: + + using UpdateDependencies = ghoul::Boolean; + + struct InvalidSceneError : ghoul::RuntimeError { + /** + * \param message The reason that caused this exception to be thrown + * \param component The optional compoment that caused this exception to be thrown + * \pre message may not be empty + */ + explicit InvalidSceneError(const std::string& message, const std::string& component = ""); + }; + // constructors & destructor Scene(); ~Scene(); /** - * Initalizes the SceneGraph by loading modules from the ${SCENEPATH} directory + * Initalizes the SceneGraph */ - bool initialize(); - - /* - * Clean up everything - */ - bool deinitialize(); + void initialize(); /* * Load the scenegraph from the provided folder */ - void scheduleLoadSceneFile(const std::string& sceneDescriptionFilePath); - void clearSceneGraph(); + //void scheduleLoadSceneFile(const std::string& sceneDescriptionFilePath); + void clear(); - void loadModule(const std::string& modulePath); + /* + * Set the root node of the scene + */ + void setRoot(std::unique_ptr root); + /* + * Set the root node of the scene + */ + void setCamera(std::unique_ptr camera); + + /** + * Return the camera + */ + Camera* camera() const; + /* * Updates all SceneGraphNodes relative positions */ @@ -96,13 +114,20 @@ public: */ SceneGraphNode* sceneGraphNode(const std::string& name) const; - std::vector allSceneGraphNodes() const; + void addNode(SceneGraphNode* node, UpdateDependencies updateDeps = UpdateDependencies::Yes); - SceneGraph& sceneGraph(); + void removeNode(SceneGraphNode* node, UpdateDependencies updateDeps = UpdateDependencies::Yes); + + void updateDependencies(); + + void sortTopologically(); + + const std::vector& allSceneGraphNodes() const; + + const std::map& nodesByName() const; + + void writePropertyDocumentation(const std::string& filename, const std::string& type, const std::string& sceneFilename); - void addSceneGraphNode(SceneGraphNode* node){ - _graph.addSceneGraphNode(node); - } /** * Returns the Lua library that contains all Lua functions available to change the * scene graph. The functions contained are @@ -115,39 +140,16 @@ public: static documentation::Documentation Documentation(); -private: - bool loadSceneInternal(const std::string& sceneDescriptionFilePath); - - void writePropertyDocumentation(const std::string& filename, const std::string& type, const std::string& sceneFilename); - - std::string _focus; - - // actual scenegraph - SceneGraph _graph; - //SceneGraphNode* _root; - //std::vector _nodes; - //std::map _allNodes; - - std::string _sceneGraphToLoad; +private: + std::unique_ptr _root; + std::unique_ptr _camera; + std::vector _topologicallySortedNodes; + std::vector _circularNodes; + std::map _nodesByName; std::mutex _programUpdateLock; std::set _programsToUpdate; std::vector> _programs; - - typedef std::map NodeMap; - typedef std::multimap DependencyMap; - typedef std::vector LoadedList; - - struct LoadMaps { - NodeMap nodes; - DependencyMap dependencies; - LoadedList loadedNodes; - }; - - void loadModules(const std::string& directory, const ghoul::Dictionary& dictionary); - void loadModule(LoadMaps& m,const std::string& modulePath, lua_State* state); - void loadNodes(const std::string& parentName, LoadMaps& m); - void loadNode(const ghoul::Dictionary& dictionary); }; } // namespace openspace diff --git a/include/openspace/scene/scenegraphnode.h b/include/openspace/scene/scenegraphnode.h index b028462909..703a081137 100644 --- a/include/openspace/scene/scenegraphnode.h +++ b/include/openspace/scene/scenegraphnode.h @@ -48,6 +48,8 @@ namespace openspace { class SceneGraphNode : public properties::PropertyOwner { public: + using UpdateScene = ghoul::Boolean; + struct PerformanceRecord { long long renderTime; // time in ns long long updateTimeRenderable; // time in ns @@ -64,21 +66,33 @@ public: SceneGraphNode(); ~SceneGraphNode(); - static SceneGraphNode* createFromDictionary(const ghoul::Dictionary& dictionary); + static std::unique_ptr createFromDictionary(const ghoul::Dictionary& dictionary); bool initialize(); bool deinitialize(); + void traversePreOrder(std::function fn); + void traversePostOrder(std::function fn); void update(const UpdateData& data); void evaluate(const Camera* camera, const psc& parentPosition = psc()); void render(const RenderData& data, RendererTasks& tasks); void updateCamera(Camera* camera) const; - //void addNode(SceneGraphNode* child); + 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 addChild(SceneGraphNode* child); - void setParent(SceneGraphNode* parent); - //bool abandonChild(SceneGraphNode* child); + + 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); + + const std::vector& dependencies() const; + const std::vector& dependentNodes() const; + + Scene* scene(); + void setScene(Scene* scene); glm::dvec3 position() const; const glm::dmat3& rotationMatrix() const; @@ -89,7 +103,7 @@ public: double worldScale() const; SceneGraphNode* parent() const; - const std::vector& children() const; + std::vector children() const; PowerScaledScalar calculateBoundingSphere(); PowerScaledScalar boundingSphere() const; @@ -105,14 +119,15 @@ public: static documentation::Documentation Documentation(); private: - bool sphereInsideFrustum(const psc& s_pos, const PowerScaledScalar& s_rad, const Camera* camera); - glm::dvec3 calculateWorldPosition() const; glm::dmat3 calculateWorldRotation() const; double calculateWorldScale() const; - std::vector _children; + std::vector> _children; SceneGraphNode* _parent; + std::vector _dependencies; + std::vector _dependentNodes; + Scene* _scene; PerformanceRecord _performanceRecord; diff --git a/include/openspace/scene/sceneloader.h b/include/openspace/scene/sceneloader.h new file mode 100644 index 0000000000..181413fe46 --- /dev/null +++ b/include/openspace/scene/sceneloader.h @@ -0,0 +1,92 @@ +/***************************************************************************************** +* * +* OpenSpace * +* * +* Copyright (c) 2014-2016 * +* * +* Permission is hereby granted, free of charge, to any person obtaining a copy of this * +* software and associated documentation files (the "Software"), to deal in the Software * +* without restriction, including without limitation the rights to use, copy, modify, * +* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * +* permit persons to whom the Software is furnished to do so, subject to the following * +* conditions: * +* * +* The above copyright notice and this permission notice shall be included in all copies * +* or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * +* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * +* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * +* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * +* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * +* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * +****************************************************************************************/ + +#ifndef __OPENSPACE_CORE___SCENELOADER___H__ +#define __OPENSPACE_CORE___SCENELOADER___H__ + +#include +#include + +#include +#include + +#include +#include + +namespace openspace { + +class Scene; + +class SceneLoader { +public: + struct LoadedNode { + LoadedNode( + const std::string& nodeName, + const std::string& parentName, + const std::vector& deps, + std::unique_ptr n) + { + name = nodeName; + parent = parentName; + dependencies = deps; + node = std::move(n); + } + + std::string name; + std::string parent; + std::vector dependencies; + std::unique_ptr node; + }; + + struct LoadedCamera { + LoadedCamera( + const std::string& parentName, + std::unique_ptr c) + { + parent = parentName; + camera = std::move(c); + } + std::string parent; + std::unique_ptr camera; + }; + + SceneLoader() = default; + ~SceneLoader() = default; + + std::unique_ptr loadScene(const std::string& path); + std::vector importDirectory(Scene& scene, const std::string& directory); + SceneGraphNode* importNodeDictionary(Scene& scene, const ghoul::Dictionary& dictionary); + +private: + SceneLoader::LoadedNode loadNode(const ghoul::Dictionary& dictionary); + std::vector loadModule(const std::string& path, lua_State* luaState); + std::vector loadDirectory(const std::string& path, lua_State* luaState); + + SceneLoader::LoadedCamera loadCamera(const ghoul::Dictionary& dictionary); + std::vector addLoadedNodes(Scene& scene, std::vector nodes); +}; + +} + +#endif // __OPENSPACE_CORE___SCENELOADER___H__ diff --git a/include/openspace/scene/scenegraph.h b/include/openspace/scene/scenemanager.h similarity index 62% rename from include/openspace/scene/scenegraph.h rename to include/openspace/scene/scenemanager.h index effc22a3cc..48f515fe7e 100644 --- a/include/openspace/scene/scenegraph.h +++ b/include/openspace/scene/scenemanager.h @@ -21,55 +21,27 @@ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ****************************************************************************************/ - -#ifndef __OPENSPACE_CORE___SCENEGRAPH___H__ -#define __OPENSPACE_CORE___SCENEGRAPH___H__ + +#ifndef __OPENSPACE_CORE___SCENEMANAGER___H__ +#define __OPENSPACE_CORE___SCENEMANAGER___H__ #include -#include +#include namespace openspace { -class SceneGraphNode; +class Scene; -class SceneGraph { +class SceneManager { public: - SceneGraph(); - ~SceneGraph(); - - void clear(); - bool loadFromFile(const std::string& sceneDescription); - - // Returns if addition was successful - bool addSceneGraphNode(SceneGraphNode* node); - bool removeSceneGraphNode(SceneGraphNode* node); - - const std::vector& nodes() const; - - SceneGraphNode* rootNode() const; - SceneGraphNode* sceneGraphNode(const std::string& name) const; - + SceneManager() = default; + ~SceneManager() = default; + Scene* loadScene(const std::string& path); + void unloadScene(Scene& scene); private: - struct SceneGraphNodeInternal { - ~SceneGraphNodeInternal(); - - SceneGraphNode* node = nullptr; - // From nodes that are dependent on this one - std::vector incomingEdges; - // To nodes that this node depends on - std::vector outgoingEdges; - }; - - bool nodeIsDependentOnRoot(SceneGraphNodeInternal* node); - bool sortTopologically(); - - SceneGraphNodeInternal* nodeByName(const std::string& name); - - SceneGraphNode* _rootNode; - std::vector _nodes; - std::vector _topologicalSortedNodes; + std::vector> _scenes; }; -} // namespace openspace +} -#endif // __OPENSPACE_CORE___SCENEGRAPH___H__ +#endif // __OPENSPACE_CORE___SCENEMANAGER___H__ diff --git a/include/openspace/util/camera.h b/include/openspace/util/camera.h index 87184d080d..7a394c15f3 100644 --- a/include/openspace/util/camera.h +++ b/include/openspace/util/camera.h @@ -39,6 +39,7 @@ namespace openspace { class SyncBuffer; +class SceneGraphNode; /** * This class still needs some more love. Suggested improvements: @@ -92,6 +93,7 @@ public: void setRotation(Quat rotation); void setScaling(glm::vec2 scaling); void setMaxFov(float fov); + void setParent(SceneGraphNode* parent); // Relative mutators void rotate(Quat rotation); @@ -109,6 +111,7 @@ public: const Quat& rotationQuaternion() const; float maxFov() const; float sinMaxFov() const; + SceneGraphNode* parent() const; // @TODO this should simply be called viewMatrix! // Or it needs to be changed so that it actually is combined. Right now it is @@ -181,6 +184,7 @@ private: SyncData _position; SyncData _rotation; SyncData _scaling; + SceneGraphNode* _parent; // _focusPosition to be removed diff --git a/modules/newhorizons/rendering/renderableplaneprojection.cpp b/modules/newhorizons/rendering/renderableplaneprojection.cpp index 4c405ee4a2..82adbd6acd 100644 --- a/modules/newhorizons/rendering/renderableplaneprojection.cpp +++ b/modules/newhorizons/rendering/renderableplaneprojection.cpp @@ -265,8 +265,9 @@ void RenderablePlaneProjection::updatePlane(const Image& img, double currentTime if (!_moving) { SceneGraphNode* thisNode = OsEng.renderEngine().scene()->sceneGraphNode(_name); SceneGraphNode* newParent = OsEng.renderEngine().scene()->sceneGraphNode(_target.node); - if (thisNode != nullptr && newParent != nullptr) - thisNode->setParent(newParent); + if (thisNode != nullptr && newParent != nullptr) { + thisNode->setParent(*newParent); + } } const GLfloat vertex_data[] = { // square of two triangles drawn within fov in target coordinates diff --git a/modules/newhorizons/rendering/renderableplanetprojection.cpp b/modules/newhorizons/rendering/renderableplanetprojection.cpp index 3902c44978..c53065bf0f 100644 --- a/modules/newhorizons/rendering/renderableplanetprojection.cpp +++ b/modules/newhorizons/rendering/renderableplanetprojection.cpp @@ -80,7 +80,7 @@ Documentation RenderablePlanetProjection::Documentation() { }, { keyGeometry, - new ReferencingVerifier("base_geometry_planet"), + new ReferencingVerifier("space_geometry_planet"), "The geometry that is used for rendering this planet.", Optional::No }, @@ -127,7 +127,7 @@ RenderablePlanetProjection::RenderablePlanetProjection(const ghoul::Dictionary& documentation::testSpecificationAndThrow( Documentation(), dictionary, - "RenderablePlanetProject" + "RenderablePlanetProjection" ); std::string name; diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6522114623..e906ca5a9a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -127,7 +127,8 @@ set(OPENSPACE_SOURCE ${OPENSPACE_BASE_DIR}/src/scene/scene.cpp ${OPENSPACE_BASE_DIR}/src/scene/scene_doc.inl ${OPENSPACE_BASE_DIR}/src/scene/scene_lua.inl - ${OPENSPACE_BASE_DIR}/src/scene/scenegraph.cpp + ${OPENSPACE_BASE_DIR}/src/scene/sceneloader.cpp + ${OPENSPACE_BASE_DIR}/src/scene/scenemanager.cpp ${OPENSPACE_BASE_DIR}/src/scene/scenegraphnode.cpp ${OPENSPACE_BASE_DIR}/src/scene/scenegraphnode_doc.inl ${OPENSPACE_BASE_DIR}/src/scripting/lualibrary.cpp @@ -269,7 +270,8 @@ set(OPENSPACE_HEADER ${OPENSPACE_BASE_DIR}/include/openspace/scene/rotation.h ${OPENSPACE_BASE_DIR}/include/openspace/scene/scale.h ${OPENSPACE_BASE_DIR}/include/openspace/scene/scene.h - ${OPENSPACE_BASE_DIR}/include/openspace/scene/scenegraph.h + ${OPENSPACE_BASE_DIR}/include/openspace/scene/sceneloader.h + ${OPENSPACE_BASE_DIR}/include/openspace/scene/scenemanager.h ${OPENSPACE_BASE_DIR}/include/openspace/scene/scenegraphnode.h ${OPENSPACE_BASE_DIR}/include/openspace/scripting/lualibrary.h ${OPENSPACE_BASE_DIR}/include/openspace/scripting/script_helper.h diff --git a/src/engine/openspaceengine.cpp b/src/engine/openspaceengine.cpp index 3759113f64..99604ca209 100644 --- a/src/engine/openspaceengine.cpp +++ b/src/engine/openspaceengine.cpp @@ -47,7 +47,7 @@ #include #include #include -#include +#include #include #include #include @@ -121,16 +121,20 @@ namespace { namespace openspace { namespace properties { - class Property; +class Property; } - + +class Scene; + OpenSpaceEngine* OpenSpaceEngine::_engine = nullptr; -OpenSpaceEngine::OpenSpaceEngine(std::string programName, - std::unique_ptr windowWrapper) +OpenSpaceEngine::OpenSpaceEngine( + std::string programName, + std::unique_ptr windowWrapper) : _configurationManager(new ConfigurationManager) , _interactionHandler(new interaction::InteractionHandler) , _renderEngine(new RenderEngine) + , _sceneManager(new SceneManager) , _scriptEngine(new scripting::ScriptEngine) , _scriptScheduler(new scripting::ScriptScheduler) , _networkEngine(new NetworkEngine) @@ -149,6 +153,8 @@ OpenSpaceEngine::OpenSpaceEngine(std::string programName, , _parallelConnection(new ParallelConnection) , _windowWrapper(std::move(windowWrapper)) , _globalPropertyNamespace(new properties::PropertyOwner) + , _switchScene(false) + , _scenePath("") , _isMaster(false) , _runTime(0.0) , _isInShutdownMode(false) @@ -190,6 +196,11 @@ OpenSpaceEngine::~OpenSpaceEngine() { #ifdef OPENSPACE_MODULE_ONSCREENGUI_ENABLED _gui->deinitializeGL(); #endif + + _renderEngine->setScene(nullptr); + _renderEngine->setCamera(nullptr); + _sceneManager = nullptr; + _interactionHandler->deinitialize(); _renderEngine->deinitialize(); @@ -217,12 +228,12 @@ OpenSpaceEngine& OpenSpaceEngine::ref() { } bool OpenSpaceEngine::create(int argc, char** argv, - std::unique_ptr windowWrapper, - std::vector& sgctArguments) + std::unique_ptr windowWrapper, + std::vector& sgctArguments) { ghoul_assert(!_engine, "OpenSpaceEngine was already created"); ghoul_assert(windowWrapper != nullptr, "No Window Wrapper was provided"); - + ghoul::initialize(); // Initialize the LogManager and add the console log as this will be used every time @@ -323,13 +334,13 @@ bool OpenSpaceEngine::create(int argc, char** argv, "${CACHE}", cacheFolder, ghoul::filesystem::FileSystem::Override::Yes - ); + ); } // Initialize the requested logs from the configuration file _engine->configureLogging(); - LINFOC("OpenSpace Version", + LINFOC("OpenSpace Version", OPENSPACE_VERSION_MAJOR << "." << OPENSPACE_VERSION_MINOR << "." << OPENSPACE_VERSION_PATCH << " (" << OPENSPACE_VERSION_STRING << ")"); @@ -488,33 +499,123 @@ bool OpenSpaceEngine::initialize() { // Load a light and a monospaced font loadFonts(); - // Initialize the Scene - Scene* sceneGraph = new Scene; - sceneGraph->initialize(); - std::string scenePath = ""; configurationManager().getValue(ConfigurationManager::KeyConfigScene, scenePath); - sceneGraph->scheduleLoadSceneFile(scenePath); - // Initialize the RenderEngine - _renderEngine->setSceneGraph(sceneGraph); _renderEngine->initialize(); - _renderEngine->setGlobalBlackOutFactor(0.0); - _renderEngine->startFading(1, 3.0); + scheduleLoadScene(scenePath); + LINFO("Finished initializing"); + return true; +} + +void OpenSpaceEngine::scheduleLoadScene(const std::string& scenePath) { + _switchScene = true; + _scenePath = scenePath; +} + +void OpenSpaceEngine::loadScene(const std::string& scenePath) { + windowWrapper().setSynchronization(false); + OnExit( + [this]() { windowWrapper().setSynchronization(true); } + ); + // Run start up scripts try { runPreInitializationScripts(scenePath); } + catch (const ghoul::RuntimeError& e) { + LERRORC(e.component, e.message); + } + + + Scene* scene; + try { + scene = _sceneManager->loadScene(scenePath); + } catch (const ghoul::FileNotFoundError& e) { + LERRORC(e.component, e.message); + return; + } catch (const Scene::InvalidSceneError& e) { + LERRORC(e.component, e.message); + return; + } catch (const ghoul::RuntimeError& e) { + LERRORC(e.component, e.message); + return; + } + + Scene* previousScene = _renderEngine->scene(); + if (previousScene) { + _syncEngine->removeSyncables(Time::ref().getSyncables()); + _syncEngine->removeSyncables(_renderEngine->getSyncables()); + _syncEngine->removeSyncable(_scriptEngine.get()); + + _renderEngine->setScene(nullptr); + _renderEngine->setCamera(nullptr); + _sceneManager->unloadScene(*previousScene); + } + + // Initialize the RenderEngine + _renderEngine->setScene(scene); + _renderEngine->setCamera(scene->camera()); + _renderEngine->setGlobalBlackOutFactor(0.0); + _renderEngine->startFading(1, 3.0); + + + scene->initialize(); + _interactionHandler->setCamera(scene->camera()); + + try { + runPostInitializationScripts(scenePath); + } catch (const ghoul::RuntimeError& e) { LFATALC(e.component, e.message); } + // Write keyboard documentation. + const std::string KeyboardShortcutsType = + ConfigurationManager::KeyKeyboardShortcuts + "." + + ConfigurationManager::PartType; + + const std::string KeyboardShortcutsFile = + ConfigurationManager::KeyKeyboardShortcuts + "." + + ConfigurationManager::PartFile; + + std::string type, file; + bool hasType = configurationManager().getValue(KeyboardShortcutsType, type); + bool hasFile = configurationManager().getValue(KeyboardShortcutsFile, file); + + if (hasType && hasFile) { + interactionHandler().writeKeyboardDocumentation(type, file); + } + + // If a PropertyDocumentationFile was specified, generate it now. + const std::string KeyPropertyDocumentationType = + ConfigurationManager::KeyPropertyDocumentation + '.' + + ConfigurationManager::PartType; + + const std::string KeyPropertyDocumentationFile = + ConfigurationManager::KeyPropertyDocumentation + '.' + + ConfigurationManager::PartFile; + + hasType = configurationManager().hasKey(KeyPropertyDocumentationType); + hasFile = configurationManager().hasKey(KeyPropertyDocumentationFile); + + if (hasType && hasFile) { + std::string propertyDocumentationType; + OsEng.configurationManager().getValue(KeyPropertyDocumentationType, propertyDocumentationType); + std::string propertyDocumentationFile; + OsEng.configurationManager().getValue(KeyPropertyDocumentationFile, propertyDocumentationFile); + + propertyDocumentationFile = absPath(propertyDocumentationFile); + scene->writePropertyDocumentation(propertyDocumentationFile, propertyDocumentationType, scenePath); + } + + #ifdef OPENSPACE_MODULE_ONSCREENGUI_ENABLED LINFO("Initializing GUI"); _gui->initialize(); _gui->_globalProperty.setSource( - [&]() { + [&]() { std::vector res = { _settingsEngine.get(), _interactionHandler.get(), @@ -548,14 +649,14 @@ bool OpenSpaceEngine::initialize() { groups.end(), std::back_inserter(res), [](const auto& val) { - return val.second.get(); + return val.second.get(); } ); return res; } ); #endif - + #endif #ifdef OPENSPACE_MODULE_ISWA_ENABLED @@ -565,9 +666,6 @@ bool OpenSpaceEngine::initialize() { _syncEngine->addSyncables(Time::ref().getSyncables()); _syncEngine->addSyncables(_renderEngine->getSyncables()); _syncEngine->addSyncable(_scriptEngine.get()); - - LINFO("Finished initializing"); - return true; } @@ -865,6 +963,11 @@ void OpenSpaceEngine::setRunTime(double d){ void OpenSpaceEngine::preSynchronization() { FileSys.triggerFilesystemEvents(); + if (_switchScene) { + loadScene(_scenePath); + _switchScene = false; + } + if (_isFirstRenderingFirstFrame) { _windowWrapper->setSynchronization(false); } @@ -883,7 +986,7 @@ void OpenSpaceEngine::preSynchronization() { _interactionHandler->updateInputStates(dt); - _renderEngine->updateSceneGraph(); + _renderEngine->updateScene(); _interactionHandler->updateCamera(dt); _renderEngine->camera()->invalidateCache(); @@ -902,7 +1005,7 @@ void OpenSpaceEngine::postSynchronizationPreDraw() { _shutdownCountdown -= _windowWrapper->averageDeltaTime(); } - _renderEngine->updateSceneGraph(); + _renderEngine->updateScene(); _renderEngine->updateFade(); _renderEngine->updateRenderer(); _renderEngine->updateScreenSpaceRenderables(); diff --git a/src/engine/settingsengine.cpp b/src/engine/settingsengine.cpp index 808a2fb5ca..81557134e0 100644 --- a/src/engine/settingsengine.cpp +++ b/src/engine/settingsengine.cpp @@ -141,19 +141,23 @@ void SettingsEngine::initSceneFiles() { // Load all matching files in the Scene // TODO: match regex with either with new ghoul readFiles or local code std::string sceneDir = "${SCENE}"; + std::string pathSep(1, ghoul::filesystem::FileSystem::PathSeparator); + std::vector scenes = ghoul::filesystem::Directory(sceneDir).readFiles(); for (std::size_t i = 0; i < scenes.size(); ++i) { - std::size_t found = scenes[i].find_last_of("/\\"); + std::size_t found = scenes[i].find_last_of(pathSep); _scenes.addOption(i, scenes[i].substr(found+1)); } // Set interaction to change ConfigurationManager and schedule the load _scenes.onChange( - [this]() { + [this, sceneDir, pathSep]() { std::string sceneFile = _scenes.getDescriptionByValue(_scenes); OsEng.configurationManager().setValue( ConfigurationManager::KeyConfigScene, sceneFile); - OsEng.renderEngine().scene()->scheduleLoadSceneFile(sceneFile); + std::string fullPath = + sceneDir + pathSep + sceneFile; + OsEng.scheduleLoadScene(fullPath); } ); } diff --git a/src/engine/syncengine.cpp b/src/engine/syncengine.cpp index 1960169917..c2add63f16 100644 --- a/src/engine/syncengine.cpp +++ b/src/engine/syncengine.cpp @@ -44,7 +44,6 @@ namespace openspace { } - void SyncEngine::presync(bool isMaster) { for (const auto& syncable : _syncables) { syncable->presync(isMaster); @@ -73,8 +72,6 @@ namespace openspace { } } - - void SyncEngine::addSyncable(Syncable* syncable) { _syncables.push_back(syncable); } @@ -92,4 +89,10 @@ namespace openspace { ); } + void SyncEngine::removeSyncables(const std::vector& syncables) { + for (const auto& syncable : syncables) { + removeSyncable(syncable); + } + } + } diff --git a/src/interaction/interactionhandler.cpp b/src/interaction/interactionhandler.cpp index 94be1f99dd..b37e5040c2 100644 --- a/src/interaction/interactionhandler.cpp +++ b/src/interaction/interactionhandler.cpp @@ -72,7 +72,6 @@ namespace interaction { // InteractionHandler InteractionHandler::InteractionHandler() : _origin("origin", "Origin", "") - , _coordinateSystem("coordinateSystem", "Coordinate System", "") , _rotationalFriction("rotationalFriction", "Rotational Friction", true) , _horizontalFriction("horizontalFriction", "Horizontal Friction", true) , _verticalFriction("verticalFriction", "Vertical Friction", true) @@ -91,10 +90,6 @@ InteractionHandler::InteractionHandler() resetCameraDirection(); }); - _coordinateSystem.onChange([this]() { - OsEng.renderEngine().changeViewPoint(_coordinateSystem.value()); - }); - // Create the interactionModes _inputState = std::make_unique(); // Inject the same mouse states to both orbital and global interaction mode @@ -137,7 +132,6 @@ InteractionHandler::InteractionHandler() // Add the properties addProperty(_origin); - addProperty(_coordinateSystem); addProperty(_rotationalFriction); addProperty(_horizontalFriction); @@ -175,6 +169,7 @@ void InteractionHandler::setFocusNode(SceneGraphNode* node) { void InteractionHandler::setCamera(Camera* camera) { _camera = camera; + setFocusNode(_camera->parent()); } void InteractionHandler::resetCameraDirection() { diff --git a/src/rendering/renderengine.cpp b/src/rendering/renderengine.cpp index 85fd0b77a6..dd430e7f79 100644 --- a/src/rendering/renderengine.cpp +++ b/src/rendering/renderengine.cpp @@ -44,7 +44,6 @@ #include #include -#include #include #include #include @@ -113,14 +112,14 @@ const std::vector RenderEngine::FrametimeTypes({ }); RenderEngine::RenderEngine() - : _mainCamera(nullptr) + : _camera(nullptr) , _performanceMeasurements("performanceMeasurements", "Performance Measurements") , _frametimeType( "frametimeType", "Type of the frametime display", properties::OptionProperty::DisplayType::Dropdown ) - , _sceneGraph(nullptr) + , _scene(nullptr) , _renderer(nullptr) , _rendererImplementation(RendererImplementation::Invalid) , _performanceManager(nullptr) @@ -166,23 +165,11 @@ RenderEngine::RenderEngine() addProperty(_frametimeType); } -RenderEngine::~RenderEngine() { - delete _sceneGraph; - _sceneGraph = nullptr; - - delete _mainCamera; - delete _raycasterManager; - -} - bool RenderEngine::deinitialize() { for (auto screenspacerenderable : _screenSpaceRenderables) { screenspacerenderable->deinitialize(); } - MissionManager::deinitialize(); - - _sceneGraph->clearSceneGraph(); return true; } @@ -209,8 +196,7 @@ bool RenderEngine::initialize() { _frameNumber = 0; std::string renderingMethod = DefaultRenderingMethod; - // If the user specified a rendering method that he would like to use, use that - + // Set rendering method to the user-defined one if any. if (OsEng.configurationManager().hasKeyAndValue(KeyRenderingMethod)) { renderingMethod = OsEng.configurationManager().value(KeyRenderingMethod); } else { @@ -224,20 +210,12 @@ bool RenderEngine::initialize() { } } - _raycasterManager = new RaycasterManager(); + _raycasterManager = std::make_unique(); _nAaSamples = OsEng.windowWrapper().currentNumberOfAaSamples(); LINFO("Seting renderer from string: " << renderingMethod); setRendererFromString(renderingMethod); - // init camera and set temporary position and scaling - _mainCamera = new Camera(); - - OsEng.interactionHandler().setCamera(_mainCamera); - if (_renderer) { - _renderer->setCamera(_mainCamera); - } - #ifdef GHOUL_USE_DEVIL ghoul::io::TextureReader::ref().addReader(std::make_shared()); #endif // GHOUL_USE_DEVIL @@ -279,79 +257,6 @@ bool RenderEngine::initializeGL() { throw; } - - - // ALL OF THIS HAS TO BE CHECKED - // ---abock - - -// sgct::Engine::instance()->setNearAndFarClippingPlanes(0.001f, 1000.0f); - // sgct::Engine::instance()->setNearAndFarClippingPlanes(0.1f, 30.0f); - - // calculating the maximum field of view for the camera, used to - // determine visibility of objects in the scene graph -/* if (sgct::Engine::instance()->getCurrentRenderTarget() == sgct::Engine::NonLinearBuffer) { - // fisheye mode, looking upwards to the "dome" - glm::vec4 upDirection(0, 1, 0, 0); - - // get the tilt and rotate the view - const float tilt = wPtr->getFisheyeTilt(); - glm::mat4 tiltMatrix - = glm::rotate(glm::mat4(1.0f), tilt, glm::vec3(1.0f, 0.0f, 0.0f)); - const glm::vec4 viewdir = tiltMatrix * upDirection; - - // set the tilted view and the FOV - _mainCamera->setCameraDirection(glm::vec3(viewdir[0], viewdir[1], viewdir[2])); - _mainCamera->setMaxFov(wPtr->getFisheyeFOV()); - _mainCamera->setLookUpVector(glm::vec3(0.0, 1.0, 0.0)); - } - else {*/ - // get corner positions, calculating the forth to easily calculate center - - // glm::vec3 corners[4]; - // sgct::SGCTWindow* wPtr = sgct::Engine::instance()->getWindowPtr(0); - // sgct_core::BaseViewport* vp = wPtr->getViewport(0); - // sgct_core::SGCTProjectionPlane* projectionPlane = vp->getProjectionPlane(); - - // corners[0] = *(projectionPlane->getCoordinatePtr(sgct_core::SGCTProjectionPlane::LowerLeft)); - // corners[1] = *(projectionPlane->getCoordinatePtr(sgct_core::SGCTProjectionPlane::UpperLeft)); - // corners[2] = *(projectionPlane->getCoordinatePtr(sgct_core::SGCTProjectionPlane::UpperRight)); - // corners[3] = glm::vec3(corners[2][0], corners[0][1], corners[2][2]); - // - // const glm::vec3 center = (corners[0] + corners[1] + corners[2] + corners[3]); - //// - //const glm::vec3 eyePosition = sgct_core::ClusterManager::instance()->getDefaultUserPtr()->getPos(); - ////// get viewdirection, stores the direction in the camera, used for culling - //const glm::vec3 viewdir = glm::normalize(eyePosition - center); - - //const glm::vec3 upVector = corners[0] - corners[1]; - - - - //_mainCamera->setCameraDirection(glm::normalize(-viewdir)); - //_mainCamera->setCameraDirection(glm::vec3(0.f, 0.f, -1.f)); - //_mainCamera->setLookUpVector(glm::normalize(upVector)); - //_mainCamera->setLookUpVector(glm::vec3(0.f, 1.f, 0.f)); - - // set the initial fov to be 0.0 which means everything will be culled - //float maxFov = 0.0f; - float maxFov = std::numeric_limits::max(); - - //// for each corner - //for (int i = 0; i < 4; ++i) { - // // calculate radians to corner - // glm::vec3 dir = glm::normalize(eyePosition - corners[i]); - // float radsbetween = acos(glm::dot(viewdir, dir)) - // / (glm::length(viewdir) * glm::length(dir)); - - // // the angle to a corner is larger than the current maxima - // if (radsbetween > maxFov) { - // maxFov = radsbetween; - // } - //} - _mainCamera->setMaxFov(maxFov); - //} - LINFO("Initializing Log"); std::unique_ptr log = std::make_unique(ScreenLogTimeToLive); _log = log.get(); @@ -361,8 +266,8 @@ bool RenderEngine::initializeGL() { return true; } -void RenderEngine::updateSceneGraph() { - _sceneGraph->update({ +void RenderEngine::updateScene() { + _scene->update({ glm::dvec3(0), glm::dmat3(1), 1, @@ -373,13 +278,7 @@ void RenderEngine::updateSceneGraph() { _performanceManager != nullptr }); - _sceneGraph->evaluate(_mainCamera); - - //Allow focus node to update camera (enables camera-following) - //FIX LATER: THIS CAUSES MASTER NODE TO BE ONE FRAME AHEAD OF SLAVES - //if (const SceneGraphNode* node = OsEng.ref().interactionHandler().focusNode()){ - //node->updateCamera(_mainCamera); - //} + _scene->evaluate(_camera); } void RenderEngine::updateShaderPrograms() { @@ -466,8 +365,8 @@ void RenderEngine::updateFade() { } void RenderEngine::render(const glm::mat4& projectionMatrix, const glm::mat4& viewMatrix){ - _mainCamera->sgctInternal.setViewMatrix(viewMatrix); - _mainCamera->sgctInternal.setProjectionMatrix(projectionMatrix); + _camera->sgctInternal.setViewMatrix(viewMatrix); + _camera->sgctInternal.setProjectionMatrix(projectionMatrix); if (!(OsEng.isMaster() && _disableMasterRendering) && !OsEng.windowWrapper().isGuiWindow()) { _renderer->render(_globalBlackOutFactor, _performanceManager != nullptr); @@ -546,20 +445,29 @@ void RenderEngine::toggleInfoText(bool b) { } Scene* RenderEngine::scene() { - ghoul_assert(_sceneGraph, "Scenegraph not initialized"); - return _sceneGraph; + return _scene; } RaycasterManager& RenderEngine::raycasterManager() { return *_raycasterManager; } -void RenderEngine::setSceneGraph(Scene* sceneGraph) { - _sceneGraph = sceneGraph; +void RenderEngine::setScene(Scene* scene) { + _scene = scene; + if (_renderer) { + _renderer->setScene(scene); + } +} + +void RenderEngine::setCamera(Camera* camera) { + _camera = camera; + if (_renderer) { + _renderer->setCamera(camera); + } } Camera* RenderEngine::camera() const { - return _mainCamera; + return _camera; } Renderer* RenderEngine::renderer() const { @@ -718,8 +626,8 @@ void RenderEngine::setRenderer(std::unique_ptr renderer) { _renderer->setResolution(renderingResolution()); _renderer->setNAaSamples(_nAaSamples); _renderer->initialize(); - _renderer->setCamera(_mainCamera); - _renderer->setScene(_sceneGraph); + _renderer->setCamera(_camera); + _renderer->setScene(_scene); } @@ -803,395 +711,6 @@ performance::PerformanceManager* RenderEngine::performanceManager() { return _performanceManager.get(); } -// This method is temporary and will be removed once the scalegraph is in effect ---abock -void RenderEngine::changeViewPoint(std::string origin) { -// SceneGraphNode* solarSystemBarycenterNode = scene()->sceneGraphNode("SolarSystemBarycenter"); -// SceneGraphNode* plutoBarycenterNode = scene()->sceneGraphNode("PlutoBarycenter"); -// SceneGraphNode* newHorizonsNode = scene()->sceneGraphNode("NewHorizons"); -// SceneGraphNode* newHorizonsPathNodeJ = scene()->sceneGraphNode("NewHorizonsPathJupiter"); -// SceneGraphNode* newHorizonsPathNodeP = scene()->sceneGraphNode("NewHorizonsPathPluto"); -//// SceneGraphNode* cg67pNode = scene()->sceneGraphNode("67P"); -//// SceneGraphNode* rosettaNode = scene()->sceneGraphNode("Rosetta"); -// -// RenderablePath* nhPath; -// -// SceneGraphNode* jupiterBarycenterNode = scene()->sceneGraphNode("JupiterBarycenter"); -// -// //SceneGraphNode* newHorizonsGhostNode = scene()->sceneGraphNode("NewHorizonsGhost"); -// //SceneGraphNode* dawnNode = scene()->sceneGraphNode("Dawn"); -// //SceneGraphNode* vestaNode = scene()->sceneGraphNode("Vesta"); -// -// // if (solarSystemBarycenterNode == nullptr || plutoBarycenterNode == nullptr || -// //jupiterBarycenterNode == nullptr) { -// // LERROR("Necessary nodes does not exist"); -// //return; -// // } -// -// if (origin == "Pluto") { -// if (newHorizonsPathNodeP) { -// Renderable* R = newHorizonsPathNodeP->renderable(); -// newHorizonsPathNodeP->setParent(plutoBarycenterNode); -// nhPath = static_cast(R); -// nhPath->calculatePath("PLUTO BARYCENTER"); -// } -// -// plutoBarycenterNode->setParent(scene()->sceneGraphNode("SolarSystem")); -// plutoBarycenterNode->setEphemeris(new StaticEphemeris); -// -// solarSystemBarycenterNode->setParent(plutoBarycenterNode); -// newHorizonsNode->setParent(plutoBarycenterNode); -// //newHorizonsGhostNode->setParent(plutoBarycenterNode); -// -// //dawnNode->setParent(plutoBarycenterNode); -// //vestaNode->setParent(plutoBarycenterNode); -// -// //newHorizonsTrailNode->setParent(plutoBarycenterNode); -// ghoul::Dictionary solarDictionary = -// { -// { std::string("Type"), std::string("Spice") }, -// { std::string("Body"), std::string("SUN") }, -// { std::string("Reference"), std::string("GALACTIC") }, -// { std::string("Observer"), std::string("PLUTO BARYCENTER") }, -// { std::string("Kernels"), ghoul::Dictionary() } -// }; -// -// ghoul::Dictionary jupiterDictionary = -// { -// { std::string("Type"), std::string("Spice") }, -// { std::string("Body"), std::string("JUPITER BARYCENTER") }, -// { std::string("Reference"), std::string("GALACTIC") }, -// { std::string("Observer"), std::string("PLUTO BARYCENTER") }, -// { std::string("Kernels"), ghoul::Dictionary() } -// }; -// -// ghoul::Dictionary newHorizonsDictionary = -// { -// { std::string("Type"), std::string("Spice") }, -// { std::string("Body"), std::string("NEW HORIZONS") }, -// { std::string("Reference"), std::string("GALACTIC") }, -// { std::string("Observer"), std::string("PLUTO BARYCENTER") }, -// { std::string("Kernels"), ghoul::Dictionary() } -// }; -// -// solarSystemBarycenterNode->setEphemeris(new SpiceEphemeris(solarDictionary)); -// jupiterBarycenterNode->setEphemeris(new SpiceEphemeris(jupiterDictionary)); -// newHorizonsNode->setEphemeris(new SpiceEphemeris(newHorizonsDictionary)); -// //newHorizonsTrailNode->setEphemeris(new SpiceEphemeris(newHorizonsDictionary)); -// -// -// //ghoul::Dictionary dawnDictionary = -// //{ -// // { std::string("Type"), std::string("Spice") }, -// // { std::string("Body"), std::string("DAWN") }, -// // { std::string("Reference"), std::string("GALACTIC") }, -// // { std::string("Observer"), std::string("PLUTO BARYCENTER") }, -// // { std::string("Kernels"), ghoul::Dictionary() } -// //}; -// //dawnNode->setEphemeris(new SpiceEphemeris(dawnDictionary)); -// // -// //ghoul::Dictionary vestaDictionary = -// //{ -// // { std::string("Type"), std::string("Spice") }, -// // { std::string("Body"), std::string("VESTA") }, -// // { std::string("Reference"), std::string("GALACTIC") }, -// // { std::string("Observer"), std::string("PLUTO BARYCENTER") }, -// // { std::string("Kernels"), ghoul::Dictionary() } -// //}; -// //vestaNode->setEphemeris(new SpiceEphemeris(vestaDictionary)); -// -// -// //ghoul::Dictionary newHorizonsGhostDictionary = -// //{ -// // { std::string("Type"), std::string("Spice") }, -// // { std::string("Body"), std::string("NEW HORIZONS") }, -// // { std::string("EphmerisGhosting"), std::string("TRUE") }, -// // { std::string("Reference"), std::string("GALACTIC") }, -// // { std::string("Observer"), std::string("PLUTO BARYCENTER") }, -// // { std::string("Kernels"), ghoul::Dictionary() } -// //}; -// //newHorizonsGhostNode->setEphemeris(new SpiceEphemeris(newHorizonsGhostDictionary)); -// -// return; -// } -// if (origin == "Sun") { -// solarSystemBarycenterNode->setParent(scene()->sceneGraphNode("SolarSystem")); -// -// if (plutoBarycenterNode) -// plutoBarycenterNode->setParent(solarSystemBarycenterNode); -// jupiterBarycenterNode->setParent(solarSystemBarycenterNode); -// if (newHorizonsNode) -// newHorizonsNode->setParent(solarSystemBarycenterNode); -// //newHorizonsGhostNode->setParent(solarSystemBarycenterNode); -// -// //newHorizonsTrailNode->setParent(solarSystemBarycenterNode); -// //dawnNode->setParent(solarSystemBarycenterNode); -// //vestaNode->setParent(solarSystemBarycenterNode); -// -// ghoul::Dictionary plutoDictionary = -// { -// { std::string("Type"), std::string("Spice") }, -// { std::string("Body"), std::string("PLUTO BARYCENTER") }, -// { std::string("Reference"), std::string("GALACTIC") }, -// { std::string("Observer"), std::string("SUN") }, -// { std::string("Kernels"), ghoul::Dictionary() } -// }; -// ghoul::Dictionary jupiterDictionary = -// { -// { std::string("Type"), std::string("Spice") }, -// { std::string("Body"), std::string("JUPITER BARYCENTER") }, -// { std::string("Reference"), std::string("GALACTIC") }, -// { std::string("Observer"), std::string("SUN") }, -// { std::string("Kernels"), ghoul::Dictionary() } -// }; -// -// solarSystemBarycenterNode->setEphemeris(new StaticEphemeris); -// jupiterBarycenterNode->setEphemeris(new SpiceEphemeris(jupiterDictionary)); -// if (plutoBarycenterNode) -// plutoBarycenterNode->setEphemeris(new SpiceEphemeris(plutoDictionary)); -// -// ghoul::Dictionary newHorizonsDictionary = -// { -// { std::string("Type"), std::string("Spice") }, -// { std::string("Body"), std::string("NEW HORIZONS") }, -// { std::string("Reference"), std::string("GALACTIC") }, -// { std::string("Observer"), std::string("SUN") }, -// { std::string("Kernels"), ghoul::Dictionary() } -// }; -// if (newHorizonsNode) -// newHorizonsNode->setEphemeris(new SpiceEphemeris(newHorizonsDictionary)); -// //newHorizonsTrailNode->setEphemeris(new SpiceEphemeris(newHorizonsDictionary)); -// -// -// //ghoul::Dictionary dawnDictionary = -// //{ -// // { std::string("Type"), std::string("Spice") }, -// // { std::string("Body"), std::string("DAWN") }, -// // { std::string("Reference"), std::string("GALACTIC") }, -// // { std::string("Observer"), std::string("SUN") }, -// // { std::string("Kernels"), ghoul::Dictionary() } -// //}; -// //dawnNode->setEphemeris(new SpiceEphemeris(dawnDictionary)); -// // -// //ghoul::Dictionary vestaDictionary = -// //{ -// // { std::string("Type"), std::string("Spice") }, -// // { std::string("Body"), std::string("VESTA") }, -// // { std::string("Reference"), std::string("GALACTIC") }, -// // { std::string("Observer"), std::string("SUN") }, -// // { std::string("Kernels"), ghoul::Dictionary() } -// //}; -// //vestaNode->setEphemeris(new SpiceEphemeris(vestaDictionary)); -// -// -// //ghoul::Dictionary newHorizonsGhostDictionary = -// //{ -// // { std::string("Type"), std::string("Spice") }, -// // { std::string("Body"), std::string("NEW HORIZONS") }, -// // { std::string("EphmerisGhosting"), std::string("TRUE") }, -// // { std::string("Reference"), std::string("GALACTIC") }, -// // { std::string("Observer"), std::string("JUPITER BARYCENTER") }, -// // { std::string("Kernels"), ghoul::Dictionary() } -// //}; -// //newHorizonsGhostNode->setEphemeris(new SpiceEphemeris(newHorizonsGhostDictionary)); -// -// return; -// } -// if (origin == "Jupiter") { -// if (newHorizonsPathNodeJ) { -// Renderable* R = newHorizonsPathNodeJ->renderable(); -// newHorizonsPathNodeJ->setParent(jupiterBarycenterNode); -// nhPath = static_cast(R); -// nhPath->calculatePath("JUPITER BARYCENTER"); -// } -// -// jupiterBarycenterNode->setParent(scene()->sceneGraphNode("SolarSystem")); -// jupiterBarycenterNode->setEphemeris(new StaticEphemeris); -// -// solarSystemBarycenterNode->setParent(jupiterBarycenterNode); -// if (newHorizonsNode) -// newHorizonsNode->setParent(jupiterBarycenterNode); -// //newHorizonsTrailNode->setParent(jupiterBarycenterNode); -// -// //dawnNode->setParent(jupiterBarycenterNode); -// //vestaNode->setParent(jupiterBarycenterNode); -// -// -// ghoul::Dictionary solarDictionary = -// { -// { std::string("Type"), std::string("Spice") }, -// { std::string("Body"), std::string("SUN") }, -// { std::string("Reference"), std::string("GALACTIC") }, -// { std::string("Observer"), std::string("JUPITER BARYCENTER") }, -// { std::string("Kernels"), ghoul::Dictionary() } -// }; -// -// ghoul::Dictionary plutoDictionary = -// { -// { std::string("Type"), std::string("Spice") }, -// { std::string("Body"), std::string("PlUTO BARYCENTER") }, -// { std::string("Reference"), std::string("GALACTIC") }, -// { std::string("Observer"), std::string("JUPITER BARYCENTER") }, -// { std::string("Kernels"), ghoul::Dictionary() } -// }; -// -// ghoul::Dictionary newHorizonsDictionary = -// { -// { std::string("Type"), std::string("Spice") }, -// { std::string("Body"), std::string("NEW HORIZONS") }, -// { std::string("Reference"), std::string("GALACTIC") }, -// { std::string("Observer"), std::string("JUPITER BARYCENTER") }, -// { std::string("Kernels"), ghoul::Dictionary() } -// }; -// solarSystemBarycenterNode->setEphemeris(new SpiceEphemeris(solarDictionary)); -// if (plutoBarycenterNode) -// plutoBarycenterNode->setEphemeris(new SpiceEphemeris(plutoDictionary)); -// if (newHorizonsNode) -// newHorizonsNode->setEphemeris(new SpiceEphemeris(newHorizonsDictionary)); -// //newHorizonsGhostNode->setParent(jupiterBarycenterNode); -// //newHorizonsTrailNode->setEphemeris(new SpiceEphemeris(newHorizonsDictionary)); -// -// -// //ghoul::Dictionary dawnDictionary = -// //{ -// // { std::string("Type"), std::string("Spice") }, -// // { std::string("Body"), std::string("DAWN") }, -// // { std::string("Reference"), std::string("GALACTIC") }, -// // { std::string("Observer"), std::string("JUPITER BARYCENTER") }, -// // { std::string("Kernels"), ghoul::Dictionary() } -// //}; -// //dawnNode->setEphemeris(new SpiceEphemeris(dawnDictionary)); -// // -// //ghoul::Dictionary vestaDictionary = -// //{ -// // { std::string("Type"), std::string("Spice") }, -// // { std::string("Body"), std::string("VESTA") }, -// // { std::string("Reference"), std::string("GALACTIC") }, -// // { std::string("Observer"), std::string("JUPITER BARYCENTER") }, -// // { std::string("Kernels"), ghoul::Dictionary() } -// //}; -// //vestaNode->setEphemeris(new SpiceEphemeris(vestaDictionary)); -// -// -// -// //ghoul::Dictionary newHorizonsGhostDictionary = -// //{ -// // { std::string("Type"), std::string("Spice") }, -// // { std::string("Body"), std::string("NEW HORIZONS") }, -// // { std::string("EphmerisGhosting"), std::string("TRUE") }, -// // { std::string("Reference"), std::string("GALACTIC") }, -// // { std::string("Observer"), std::string("JUPITER BARYCENTER") }, -// // { std::string("Kernels"), ghoul::Dictionary() } -// //}; -// //newHorizonsGhostNode->setEphemeris(new SpiceEphemeris(newHorizonsGhostDictionary)); -// //newHorizonsGhostNode->setParent(jupiterBarycenterNode); -// -// -// return; -// } -// //if (origin == "Vesta") { -// // -// // vestaNode->setParent(scene()->sceneGraphNode("SolarSystem")); -// // vestaNode->setEphemeris(new StaticEphemeris); -// // -// // solarSystemBarycenterNode->setParent(vestaNode); -// // newHorizonsNode->setParent(vestaNode); -// // -// // dawnNode->setParent(vestaNode); -// // plutoBarycenterNode->setParent(vestaNode); -// // -// // -// // ghoul::Dictionary plutoDictionary = -// // { -// // { std::string("Type"), std::string("Spice") }, -// // { std::string("Body"), std::string("PLUTO BARYCENTER") }, -// // { std::string("Reference"), std::string("GALACTIC") }, -// // { std::string("Observer"), std::string("VESTA") }, -// // { std::string("Kernels"), ghoul::Dictionary() } -// // }; -// // ghoul::Dictionary solarDictionary = -// // { -// // { std::string("Type"), std::string("Spice") }, -// // { std::string("Body"), std::string("SUN") }, -// // { std::string("Reference"), std::string("GALACTIC") }, -// // { std::string("Observer"), std::string("VESTA") }, -// // { std::string("Kernels"), ghoul::Dictionary() } -// // }; -// // -// // ghoul::Dictionary jupiterDictionary = -// // { -// // { std::string("Type"), std::string("Spice") }, -// // { std::string("Body"), std::string("JUPITER BARYCENTER") }, -// // { std::string("Reference"), std::string("GALACTIC") }, -// // { std::string("Observer"), std::string("VESTA") }, -// // { std::string("Kernels"), ghoul::Dictionary() } -// // }; -// // -// // solarSystemBarycenterNode->setEphemeris(new SpiceEphemeris(solarDictionary)); -// // plutoBarycenterNode->setEphemeris(new SpiceEphemeris(plutoDictionary)); -// // jupiterBarycenterNode->setEphemeris(new SpiceEphemeris(jupiterDictionary)); -// // -// // ghoul::Dictionary newHorizonsDictionary = -// // { -// // { std::string("Type"), std::string("Spice") }, -// // { std::string("Body"), std::string("NEW HORIZONS") }, -// // { std::string("Reference"), std::string("GALACTIC") }, -// // { std::string("Observer"), std::string("VESTA") }, -// // { std::string("Kernels"), ghoul::Dictionary() } -// // }; -// // newHorizonsNode->setEphemeris(new SpiceEphemeris(newHorizonsDictionary)); -// // -// // ghoul::Dictionary dawnDictionary = -// // { -// // { std::string("Type"), std::string("Spice") }, -// // { std::string("Body"), std::string("DAWN") }, -// // { std::string("Reference"), std::string("GALACTIC") }, -// // { std::string("Observer"), std::string("VESTA") }, -// // { std::string("Kernels"), ghoul::Dictionary() } -// // }; -// // dawnNode->setEphemeris(new SpiceEphemeris(dawnDictionary)); -// // vestaNode->setEphemeris(new StaticEphemeris); -// // -// // return; -// //} -// -// if (origin == "67P") { -// SceneGraphNode* rosettaNode = scene()->sceneGraphNode("Rosetta"); -// SceneGraphNode* cgNode = scene()->sceneGraphNode("67P"); -// //jupiterBarycenterNode->setParent(solarSystemBarycenterNode); -// //plutoBarycenterNode->setParent(solarSystemBarycenterNode); -// solarSystemBarycenterNode->setParent(cgNode); -// rosettaNode->setParent(cgNode); -// -// ghoul::Dictionary solarDictionary = -// { -// { std::string("Type"), std::string("Spice") }, -// { std::string("Body"), std::string("SUN") }, -// { std::string("Reference"), std::string("GALACTIC") }, -// { std::string("Observer"), std::string("CHURYUMOV-GERASIMENKO") }, -// { std::string("Kernels"), ghoul::Dictionary() } -// }; -// solarSystemBarycenterNode->setEphemeris(new SpiceEphemeris(solarDictionary)); -// -// ghoul::Dictionary rosettaDictionary = -// { -// { std::string("Type"), std::string("Spice") }, -// { std::string("Body"), std::string("ROSETTA") }, -// { std::string("Reference"), std::string("GALACTIC") }, -// { std::string("Observer"), std::string("CHURYUMOV-GERASIMENKO") }, -// { std::string("Kernels"), ghoul::Dictionary() } -// }; -// -// cgNode->setParent(scene()->sceneGraphNode("SolarSystem")); -// rosettaNode->setEphemeris(new SpiceEphemeris(rosettaDictionary)); -// cgNode->setEphemeris(new StaticEphemeris); -// -// return; -// -// } -// -// LFATAL("This function is being misused with an argument of '" << origin << "'"); -} - void RenderEngine::setShowFrameNumber(bool enabled){ _showFrameNumber = enabled; } @@ -1370,21 +889,12 @@ void RenderEngine::renderInformation() { #ifdef OPENSPACE_MODULE_NEWHORIZONS_ENABLED -//<<<<<<< HEAD bool hasNewHorizons = scene()->sceneGraphNode("NewHorizons"); double currentTime = Time::ref().j2000Seconds(); if (MissionManager::ref().hasCurrentMission()) { const Mission& mission = MissionManager::ref().currentMission(); -//======= -// bool hasNewHorizons = scene()->sceneGraphNode("NewHorizons"); -// double currentTime = Time::ref().currentTime(); -//>>>>>>> develop -// -// if (MissionManager::ref().hasCurrentMission()) { -// -// const Mission& mission = MissionManager::ref().currentMission(); if (mission.phases().size() > 0) { @@ -1734,7 +1244,11 @@ void RenderEngine::renderScreenLog() { } std::vector RenderEngine::getSyncables(){ - return _mainCamera->getSyncables(); + if (_camera) { + return _camera->getSyncables(); + } else { + return std::vector(); + } } void RenderEngine::sortScreenspaceRenderables() { diff --git a/src/scene/scene.cpp b/src/scene/scene.cpp index 7b0185b8a6..28eb1610c3 100644 --- a/src/scene/scene.cpp +++ b/src/scene/scene.cpp @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -53,6 +54,8 @@ #include #include #include +#include +#include #ifdef OPENSPACE_MODULE_ONSCREENGUI_ENABLED #include @@ -82,142 +85,121 @@ namespace { namespace openspace { -Scene::Scene() : _focus(SceneGraphNode::RootNodeName) {} +Scene::Scene() {} -Scene::~Scene() { - deinitialize(); +Scene::~Scene() {} + +void Scene::setRoot(std::unique_ptr root) { + if (_root) { + removeNode(_root.get()); + } + _root = std::move(root); + _root->setScene(this); + addNode(_root.get()); } -bool Scene::initialize() { - LDEBUG("Initializing SceneGraph"); - return true; +void Scene::setCamera(std::unique_ptr camera) { + _camera = std::move(camera); } -bool Scene::deinitialize() { - clearSceneGraph(); - return true; +Camera* Scene::camera() const { + return _camera.get(); } -void Scene::update(const UpdateData& data) { - if (!_sceneGraphToLoad.empty()) { - OsEng.renderEngine().scene()->clearSceneGraph(); - try { - loadSceneInternal(_sceneGraphToLoad); +void Scene::addNode(SceneGraphNode* node, Scene::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(); + } +} - // Reset the InteractionManager to Orbital/default mode - // TODO: Decide if it belongs in the scene and/or how it gets reloaded - //OsEng.interactionHandler().setInteractionMode("Orbital"); +void Scene::removeNode(SceneGraphNode* node, Scene::UpdateDependencies updateDeps) { + // Remove the node and all its children. + node->traversePostOrder([this](SceneGraphNode* n) { + _topologicallySortedNodes.erase(std::remove(_topologicallySortedNodes.begin(), _topologicallySortedNodes.end(), n), _topologicallySortedNodes.end()); + _nodesByName.erase(n->name()); + }); + + if (updateDeps) { + updateDependencies(); + } +} - // After loading the scene, the keyboard bindings have been set - const std::string KeyboardShortcutsType = - ConfigurationManager::KeyKeyboardShortcuts + "." + - ConfigurationManager::PartType; +void Scene::updateDependencies() { + sortTopologically(); +} - const std::string KeyboardShortcutsFile = - ConfigurationManager::KeyKeyboardShortcuts + "." + - ConfigurationManager::PartFile; +void Scene::sortTopologically() { + std::copy(_circularNodes.begin(), _circularNodes.end(), std::back_inserter(_topologicallySortedNodes)); + _circularNodes.clear(); + + ghoul_assert(_topologicallySortedNodes.size() == _nodesByName.size(), "Number of scene graph nodes is inconsistent"); + + if (_topologicallySortedNodes.empty()) + return; + + // Only the Root node can have an in-degree of 0 + SceneGraphNode* root = _nodesByName[SceneGraphNode::RootNodeName]; + if (!root) { + throw Scene::InvalidSceneError("No root node found"); + } - std::string type; - std::string file; - bool hasType = OsEng.configurationManager().getValue( - KeyboardShortcutsType, type - ); - - bool hasFile = OsEng.configurationManager().getValue( - KeyboardShortcutsFile, file - ); - - if (hasType && hasFile) { - OsEng.interactionHandler().writeKeyboardDocumentation(type, file); + + std::unordered_map inDegrees; + for (SceneGraphNode* node : _topologicallySortedNodes) { + size_t inDegree = node->dependencies().size(); + if (node->parent() != nullptr) { + inDegree++; + inDegrees[node] = inDegree; + } + } + + std::stack zeroInDegreeNodes; + zeroInDegreeNodes.push(root); + + std::vector nodes; + nodes.reserve(_topologicallySortedNodes.size()); + while (!zeroInDegreeNodes.empty()) { + SceneGraphNode* node = zeroInDegreeNodes.top(); + nodes.push_back(node); + zeroInDegreeNodes.pop(); + + for (auto& n : node->dependentNodes()) { + auto it = inDegrees.find(n); + it->second -= 1; + if (it->second == 0) { + zeroInDegreeNodes.push(n); + inDegrees.erase(it); } - - LINFO("Loaded " << _sceneGraphToLoad); - _sceneGraphToLoad = ""; } - catch (const ghoul::RuntimeError& e) { - LERROR(e.what()); - _sceneGraphToLoad = ""; - return; + for (auto& n : node->children()) { + auto it = inDegrees.find(n); + it->second -= 1; + if (it->second == 0) { + zeroInDegreeNodes.push(n); + inDegrees.erase(it); + } } } - - for (SceneGraphNode* node : _graph.nodes()) { - try { - node->update(data); - } - catch (const ghoul::RuntimeError& e) { - LERRORC(e.component, e.what()); - } + if (inDegrees.size() > 0) { + LERROR("The scene contains circular dependencies. " << inDegrees.size() << " nodes will be disabled."); } -} -void Scene::evaluate(Camera* camera) { - for (SceneGraphNode* node : _graph.nodes()) - node->evaluate(camera); - //_root->evaluate(camera); -} - -void Scene::render(const RenderData& data, RendererTasks& tasks) { - for (SceneGraphNode* node : _graph.nodes()) { - node->render(data, tasks); + for (auto& it : inDegrees) { + _circularNodes.push_back(it.first); } + + _topologicallySortedNodes = nodes; } -void Scene::scheduleLoadSceneFile(const std::string& sceneDescriptionFilePath) { - _sceneGraphToLoad = sceneDescriptionFilePath; -} - -void Scene::clearSceneGraph() { - LINFO("Clearing current scene graph"); - // deallocate the scene graph. Recursive deallocation will occur - _graph.clear(); - //if (_root) { - // _root->deinitialize(); - // delete _root; - // _root = nullptr; - //} - - // _nodes.erase(_nodes.begin(), _nodes.end()); - // _allNodes.erase(_allNodes.begin(), _allNodes.end()); - - _focus.clear(); -} - -bool Scene::loadSceneInternal(const std::string& sceneDescriptionFilePath) { - ghoul::Dictionary dictionary; - - OsEng.windowWrapper().setSynchronization(false); - OnExit( - [](){ OsEng.windowWrapper().setSynchronization(true); } - ); - - lua_State* state = ghoul::lua::createNewLuaState(); - OnExit( - // Delete the Lua state at the end of the scope, no matter what - [state](){ ghoul::lua::destroyLuaState(state); } - ); - - OsEng.scriptEngine().initializeLuaState(state); - - ghoul::lua::loadDictionaryFromFile( - sceneDescriptionFilePath, - dictionary, - state - ); - - // Perform testing against the documentation/specification - openspace::documentation::testSpecificationAndThrow( - Scene::Documentation(), - dictionary, - "Scene" - ); - - - _graph.loadFromFile(sceneDescriptionFilePath); - - // Initialize all nodes - for (SceneGraphNode* node : _graph.nodes()) { +void Scene::initialize() { + for (SceneGraphNode* node : _topologicallySortedNodes) { try { bool success = node->initialize(); if (success) @@ -229,221 +211,64 @@ bool Scene::loadSceneInternal(const std::string& sceneDescriptionFilePath) { LERRORC(_loggerCat + "(" + e.component + ")", e.what()); } } - - // update the position of all nodes - // TODO need to check this; unnecessary? (ab) - for (SceneGraphNode* node : _graph.nodes()) { - try { - node->update({ - glm::dvec3(0), - glm::dmat3(1), - 1, - Time::ref().j2000Seconds() }); - } - catch (const ghoul::RuntimeError& e) { - LERRORC(e.component, e.message); - } - } - - for (auto it = _graph.nodes().rbegin(); it != _graph.nodes().rend(); ++it) - (*it)->calculateBoundingSphere(); - - // Read the camera dictionary and set the camera state - ghoul::Dictionary cameraDictionary; - if (dictionary.getValue(KeyCamera, cameraDictionary)) { - OsEng.interactionHandler().setCameraStateFromDictionary(cameraDictionary); - } - - // If a PropertyDocumentationFile was specified, generate it now - const std::string KeyPropertyDocumentationType = - ConfigurationManager::KeyPropertyDocumentation + '.' + - ConfigurationManager::PartType; - - const std::string KeyPropertyDocumentationFile = - ConfigurationManager::KeyPropertyDocumentation + '.' + - ConfigurationManager::PartFile; - - const bool hasType = OsEng.configurationManager().hasKey(KeyPropertyDocumentationType); - const bool hasFile = OsEng.configurationManager().hasKey(KeyPropertyDocumentationFile); - if (hasType && hasFile) { - std::string propertyDocumentationType; - OsEng.configurationManager().getValue(KeyPropertyDocumentationType, propertyDocumentationType); - std::string propertyDocumentationFile; - OsEng.configurationManager().getValue(KeyPropertyDocumentationFile, propertyDocumentationFile); - - propertyDocumentationFile = absPath(propertyDocumentationFile); - writePropertyDocumentation(propertyDocumentationFile, propertyDocumentationType, sceneDescriptionFilePath); - } - - - OsEng.runPostInitializationScripts(sceneDescriptionFilePath); - - OsEng.enableBarrier(); - - return true; } -//void Scene::loadModules( -// const std::string& directory, -// const ghoul::Dictionary& dictionary) -//{ -// // Struct containing dependencies and nodes -// LoadMaps m; -// -// // Get the common directory -// std::string commonDirectory(_defaultCommonDirectory); -// dictionary.getValue(constants::scenegraph::keyCommonFolder, commonDirectory); -// FileSys.registerPathToken(_commonModuleToken, commonDirectory); -// -// lua_State* state = ghoul::lua::createNewLuaState(); -// OsEng.scriptEngine()->initializeLuaState(state); -// -// LDEBUG("Loading common module folder '" << commonDirectory << "'"); -// // Load common modules into LoadMaps struct -// loadModule(m, FileSys.pathByAppendingComponent(directory, commonDirectory), state); -// -// // Load the rest of the modules into LoadMaps struct -// ghoul::Dictionary moduleDictionary; -// if (dictionary.getValue(constants::scenegraph::keyModules, moduleDictionary)) { -// std::vector keys = moduleDictionary.keys(); -// std::sort(keys.begin(), keys.end()); -// for (const std::string& key : keys) { -// std::string moduleFolder; -// if (moduleDictionary.getValue(key, moduleFolder)) { -// loadModule(m, FileSys.pathByAppendingComponent(directory, moduleFolder), state); -// } -// } -// } -// -// // Load and construct scenegraphnodes from LoadMaps struct -// loadNodes(SceneGraphNode::RootNodeName, m); -// -// // Remove loaded nodes from dependency list -// for(const auto& name: m.loadedNodes) { -// m.dependencies.erase(name); -// } -// -// // Check to see what dependencies are not resolved. -// for(auto& node: m.dependencies) { -// LWARNING( -// "'" << node.second << "'' not loaded, parent '" -// << node.first << "' not defined!"); -// } -//} +void Scene::update(const UpdateData& data) { + for (auto& node : _topologicallySortedNodes) { + try { + node->update(data); + } + catch (const ghoul::RuntimeError& e) { + LERRORC(e.component, e.what()); + } + } +} -//void Scene::loadModule(LoadMaps& m,const std::string& modulePath, lua_State* state) { -// auto pos = modulePath.find_last_of(ghoul::filesystem::FileSystem::PathSeparator); -// if (pos == modulePath.npos) { -// LERROR("Bad format for module path: " << modulePath); -// return; -// } -// -// std::string fullModule = modulePath + modulePath.substr(pos) + _moduleExtension; -// LDEBUG("Loading nodes from: " << fullModule); -// -// ghoul::filesystem::Directory oldDirectory = FileSys.currentDirectory(); -// FileSys.setCurrentDirectory(modulePath); -// -// ghoul::Dictionary moduleDictionary; -// ghoul::lua::loadDictionaryFromFile(fullModule, moduleDictionary, state); -// std::vector keys = moduleDictionary.keys(); -// for (const std::string& key : keys) { -// if (!moduleDictionary.hasValue(key)) { -// LERROR("SceneGraphElement '" << key << "' is not a table in module '" -// << fullModule << "'"); -// continue; -// } -// -// ghoul::Dictionary element; -// std::string nodeName; -// std::string parentName; -// -// moduleDictionary.getValue(key, element); -// element.setValue(constants::scenegraph::keyPathModule, modulePath); -// -// element.getValue(constants::scenegraphnode::keyName, nodeName); -// element.getValue(constants::scenegraphnode::keyParentName, parentName); -// -// m.nodes[nodeName] = element; -// m.dependencies.emplace(parentName,nodeName); -// } -// -// FileSys.setCurrentDirectory(oldDirectory); -//} +void Scene::evaluate(Camera* camera) { + for (auto& node : _topologicallySortedNodes) { + try { + node->evaluate(camera); + } + catch (const ghoul::RuntimeError& e) { + LERRORC(e.component, e.what()); + } + } +} -//void Scene::loadNodes(const std::string& parentName, LoadMaps& m) { -// auto eqRange = m.dependencies.equal_range(parentName); -// for (auto it = eqRange.first; it != eqRange.second; ++it) { -// auto node = m.nodes.find((*it).second); -// loadNode(node->second); -// loadNodes((*it).second, m); -// } -// m.loadedNodes.emplace_back(parentName); -//} -// -//void Scene::loadNode(const ghoul::Dictionary& dictionary) { -// SceneGraphNode* node = SceneGraphNode::createFromDictionary(dictionary); -// if(node) { -// _allNodes.emplace(node->name(), node); -// _nodes.push_back(node); -// } -//} +void Scene::render(const RenderData& data, RendererTasks& tasks) { + for (auto& node : _topologicallySortedNodes) { + try { + node->render(data, tasks); + } + catch (const ghoul::RuntimeError& e) { + LERRORC(e.component, e.what()); + } + } +} -//void SceneGraph::loadModule(const std::string& modulePath) { -// auto pos = modulePath.find_last_of(ghoul::filesystem::FileSystem::PathSeparator); -// if (pos == modulePath.npos) { -// LERROR("Bad format for module path: " << modulePath); -// return; -// } -// -// std::string fullModule = modulePath + modulePath.substr(pos) + _moduleExtension; -// LDEBUG("Loading modules from: " << fullModule); -// -// ghoul::filesystem::Directory oldDirectory = FileSys.currentDirectory(); -// FileSys.setCurrentDirectory(modulePath); -// -// ghoul::Dictionary moduleDictionary; -// ghoul::lua::loadDictionaryFromFile(fullModule, moduleDictionary); -// std::vector keys = moduleDictionary.keys(); -// for (const std::string& key : keys) { -// if (!moduleDictionary.hasValue(key)) { -// LERROR("SceneGraphElement '" << key << "' is not a table in module '" -// << fullModule << "'"); -// continue; -// } -// -// ghoul::Dictionary element; -// moduleDictionary.getValue(key, element); -// -// element.setValue(constants::scenegraph::keyPathModule, modulePath); -// -// //each element in this new dictionary becomes a scenegraph node. -// SceneGraphNode* node = SceneGraphNode::createFromDictionary(element); -// -// _allNodes.emplace(node->name(), node); -// _nodes.push_back(node); -// } -// -// FileSys.setCurrentDirectory(oldDirectory); -// -// // Print the tree -// //printTree(_root); -//} +void Scene::clear() { + LINFO("Clearing current scene graph"); + _root = nullptr; +} + +const std::map& Scene::nodesByName() const { + return _nodesByName; +} SceneGraphNode* Scene::root() const { - return _graph.rootNode(); + return _root.get(); } SceneGraphNode* Scene::sceneGraphNode(const std::string& name) const { - return _graph.sceneGraphNode(name); + auto it = _nodesByName.find(name); + if (it != _nodesByName.end()) { + return it->second; + } + return nullptr; } -std::vector Scene::allSceneGraphNodes() const { - return _graph.nodes(); -} - -SceneGraph& Scene::sceneGraph() { - return _graph; +const std::vector& Scene::allSceneGraphNodes() const { + return _topologicallySortedNodes; } void Scene::writePropertyDocumentation(const std::string& filename, const std::string& type, const std::string& sceneFilename) { @@ -454,7 +279,7 @@ void Scene::writePropertyDocumentation(const std::string& filename, const std::s file.open(filename); using properties::Property; - for (SceneGraphNode* node : _graph.nodes()) { + for (SceneGraphNode* node : allSceneGraphNodes()) { std::vector properties = node->propertiesRecursive(); if (!properties.empty()) { file << node->name() << std::endl; @@ -544,7 +369,7 @@ void Scene::writePropertyDocumentation(const std::string& filename, const std::s std::stringstream json; json << "["; - std::vector nodes = _graph.nodes(); + std::vector nodes = allSceneGraphNodes(); if (!nodes.empty()) { json << std::accumulate( std::next(nodes.begin()), @@ -595,45 +420,6 @@ void Scene::writePropertyDocumentation(const std::string& filename, const std::s << "\t\n" << "\t\n" << "\n"; - - /* - - html << "\n" - << "\t\n" - << "\t\tProperties\n" - << "\t\n" - << "\n" - << "\n" - << "\t\n\n" - << "\t\n" - << "\t\t\n" - << "\t\t\t\n" - << "\t\t\t\n" - << "\t\t\t\n" - << "\t\t\n" - << "\t\n" - << "\t\n"; - - for (SceneGraphNode* node : _graph.nodes()) { - for (properties::Property* p : node->propertiesRecursive()) { - html << "\t\t\n" - << "\t\t\t\n" - << "\t\t\t\n" - << "\t\t\t\n" - << "\t\t\n"; - } - - if (!node->propertiesRecursive().empty()) { - html << "\t\n"; - } - - } - - html << "\t\n" - << "
Properties
IDTypeDescription
" << p->fullyQualifiedIdentifier() << "" << p->className() << "" << p->guiName() << "
\n" - << ";"; - - */ file << html.str(); } else @@ -700,4 +486,8 @@ scripting::LuaLibrary Scene::luaLibrary() { }; } +Scene::InvalidSceneError::InvalidSceneError(const std::string& error, const std::string& comp) + : ghoul::RuntimeError(error, comp) +{} + } // namespace openspace diff --git a/src/scene/scene_lua.inl b/src/scene/scene_lua.inl index 68d7b3bd4e..1a36ae2ded 100644 --- a/src/scene/scene_lua.inl +++ b/src/scene/scene_lua.inl @@ -213,9 +213,8 @@ int loadScene(lua_State* L) { SCRIPT_CHECK_ARGUMENTS("loadScene", L, 1, nArguments); std::string sceneFile = luaL_checkstring(L, -1); - - OsEng.renderEngine().scene()->scheduleLoadSceneFile(sceneFile); - + + OsEng.scheduleLoadScene(sceneFile); return 0; } @@ -234,22 +233,11 @@ int addSceneGraphNode(lua_State* L) { return 0; } - SceneGraphNode* node = SceneGraphNode::createFromDictionary(d); - - std::string parent = d.value(SceneGraphNode::KeyParentName); - SceneGraphNode* parentNode = OsEng.renderEngine().scene()->sceneGraphNode(parent); - if (!parentNode) { - LERRORC( - "addSceneGraphNode", - errorLocation(L) << "Could not find parent node '" << parent << "'" - ); - return 0; - } - node->setParent(parentNode); - node->initialize(); - OsEng.renderEngine().scene()->sceneGraph().addSceneGraphNode(node); + SceneLoader loader; + SceneGraphNode* importedNode = loader.importNodeDictionary(*OsEng.renderEngine().scene(), d); + importedNode->initialize(); - return 0; + return 1; } int removeSceneGraphNode(lua_State* L) { @@ -257,21 +245,25 @@ int removeSceneGraphNode(lua_State* L) { int nArguments = lua_gettop(L); SCRIPT_CHECK_ARGUMENTS("removeSceneGraphNode", L, 1, nArguments); - + std::string nodeName = luaL_checkstring(L, -1); SceneGraphNode* node = OsEng.renderEngine().scene()->sceneGraphNode(nodeName); if (!node) { LERRORC( "removeSceneGraphNode", errorLocation(L) << "Could not find node '" << nodeName << "'" - ); + ); return 0; } - - OsEng.renderEngine().scene()->sceneGraph().removeSceneGraphNode(node); - node->deinitialize(); - delete node; - + SceneGraphNode* parent = node->parent(); + if (!parent) { + LERRORC( + "removeSceneGraphNode", + errorLocation(L) << "Cannot remove root node" + ); + return 0; + } + parent->detachChild(*node); return 1; } diff --git a/src/scene/scenegraph.cpp b/src/scene/scenegraph.cpp deleted file mode 100644 index ed78e988c0..0000000000 --- a/src/scene/scenegraph.cpp +++ /dev/null @@ -1,578 +0,0 @@ -/***************************************************************************************** - * * - * OpenSpace * - * * - * Copyright (c) 2014-2016 * - * * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this * - * software and associated documentation files (the "Software"), to deal in the Software * - * without restriction, including without limitation the rights to use, copy, modify, * - * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * - * permit persons to whom the Software is furnished to do so, subject to the following * - * conditions: * - * * - * The above copyright notice and this permission notice shall be included in all copies * - * or substantial portions of the Software. * - * * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * - * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * - * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * - * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * - ****************************************************************************************/ - -#include - -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include - -#ifdef _MSC_VER -#ifdef OPENSPACE_ENABLE_VLD -#include -#endif -#endif - -namespace { - const std::string _loggerCat = "SceneGraph"; - const std::string _moduleExtension = ".mod"; - const std::string _defaultCommonDirectory = "common"; - const std::string _commonModuleToken = "${COMMON_MODULE}"; - - const std::string KeyPathScene = "ScenePath"; - const std::string KeyModules = "Modules"; - const std::string KeyCommonFolder = "CommonFolder"; - const std::string KeyPathModule = "ModulePath"; -} - -namespace openspace { - -SceneGraph::SceneGraphNodeInternal::~SceneGraphNodeInternal() { - delete node; -} - -SceneGraph::SceneGraph() - : _rootNode(nullptr) -{} - -SceneGraph::~SceneGraph() { - clear(); -} - -void SceneGraph::clear() { - // Untested ---abock - for (SceneGraphNodeInternal* n : _nodes) - delete n; - - _nodes.clear(); - _rootNode = nullptr; -} - -bool SceneGraph::loadFromFile(const std::string& sceneDescription) { - clear(); // Move this to a later stage to retain a proper scenegraph when the loading fails ---abock - - std::string absSceneFile = absPath(sceneDescription); - - // See if scene file exists - using RawPath = ghoul::filesystem::FileSystem::RawPath; - if (!FileSys.fileExists(absSceneFile, RawPath::Yes)) { - LERROR("Could not load scene file '" << absSceneFile << "'. " << - "File not found"); - return false; - } - LINFO("Loading SceneGraph from file '" << absSceneFile << "'"); - - - lua_State* state = ghoul::lua::createNewLuaState(); - OnExit( - // Delete the Lua state at the end of the scope, no matter what - [state](){ghoul::lua::destroyLuaState(state);} - ); - - OsEng.scriptEngine().initializeLuaState(state); - - - // Load dictionary - ghoul::Dictionary sceneDictionary; - try { - ghoul::lua::loadDictionaryFromFile( - absSceneFile, - sceneDictionary, - state - ); - } - catch (...) { - return false; - } - - std::string sceneDescriptionDirectory = ghoul::filesystem::File( - absSceneFile, - ghoul::filesystem::File::RawPath::Yes - ).directoryName(); - std::string sceneDirectory("."); - sceneDictionary.getValue(KeyPathScene, sceneDirectory); - - // The scene path could either be an absolute or relative path to the description - // paths directory - std::string relativeCandidate = sceneDescriptionDirectory + - ghoul::filesystem::FileSystem::PathSeparator + sceneDirectory; - std::string absoluteCandidate = absPath(sceneDirectory); - - if (FileSys.directoryExists(relativeCandidate)) - sceneDirectory = relativeCandidate; - else if (FileSys.directoryExists(absoluteCandidate)) - sceneDirectory = absoluteCandidate; - else { - LERROR("The '" << KeyPathScene << "' pointed to a " - "path '" << sceneDirectory << "' that did not exist"); - return false; - } - - ghoul::Dictionary moduleDictionary; - bool success = sceneDictionary.getValue(KeyModules, moduleDictionary); - if (!success) - // There are no modules that are loaded - return true; - -// lua_State* state = ghoul::lua::createNewLuaState(); -// OsEng.scriptEngine().initializeLuaState(state); - - // Above we generated a ghoul::Dictionary from the scene file; now we run the scene - // file again to load any variables defined inside into the state that is passed to - // the modules. This allows us to specify global variables that can then be used - // inside the modules to toggle settings - ghoul::lua::runScriptFile(state, absSceneFile); - - // Get the common directory - bool commonFolderSpecified = sceneDictionary.hasKey(KeyCommonFolder); - bool commonFolderCorrectType = sceneDictionary.hasKeyAndValue(KeyCommonFolder); - - if (commonFolderSpecified) { - if (commonFolderCorrectType) { - std::string commonFolder = sceneDictionary.value(KeyCommonFolder); - std::string fullCommonFolder = FileSys.pathByAppendingComponent( - sceneDirectory, - commonFolder - ); - if (!FileSys.directoryExists(fullCommonFolder)) - LERROR("Specified common folder '" << fullCommonFolder << "' did not exist"); - else { - if (!commonFolder.empty()) { - FileSys.registerPathToken( - _commonModuleToken, commonFolder, - ghoul::filesystem::FileSystem::Override::Yes - ); - size_t nKeys = moduleDictionary.size(); - moduleDictionary.setValue( - std::to_string(nKeys + 1), - commonFolder - ); - } - } - } - else - LERROR("Specification for 'common' folder has invalid type"); - } - - std::vector keys = moduleDictionary.keys(); - - std::map> dependencies; - std::map parents; - - _rootNode = new SceneGraphNode; - _rootNode->setName(SceneGraphNode::RootNodeName); - SceneGraphNodeInternal* internalRoot = new SceneGraphNodeInternal; - internalRoot->node = _rootNode; - _nodes.push_back(internalRoot); - - std::sort(keys.begin(), keys.end()); - ghoul::filesystem::Directory oldDirectory = FileSys.currentDirectory(); - for (const std::string& key : keys) { - std::string fullModuleName = moduleDictionary.value(key); - - std::replace(fullModuleName.begin(), fullModuleName.end(), '/', FileSys.PathSeparator); - - std::string modulePath = FileSys.pathByAppendingComponent(sceneDirectory, fullModuleName); - - std::string moduleName = fullModuleName; - std::string::size_type pos = fullModuleName.find_last_of(FileSys.PathSeparator); - if (pos != std::string::npos) - moduleName = fullModuleName.substr(pos + 1); - - if (!FileSys.directoryExists(modulePath)) { - LERROR("Could not load module '" << moduleName << "'. Directory did not exist"); - continue; - } - - std::string moduleFile = FileSys.pathByAppendingComponent( - modulePath, - moduleName + _moduleExtension - ); - - struct ModuleInformation { - ghoul::Dictionary dictionary; - std::string moduleFile; - std::string modulePath; - std::string moduleName; - }; - std::vector moduleDictionaries; - if (FileSys.fileExists(moduleFile)) { - // We have a module file, so it is a direct include - try { - ghoul::Dictionary moduleDictionary; - ghoul::lua::loadDictionaryFromFile(moduleFile, moduleDictionary, state); - moduleDictionaries.push_back({ - moduleDictionary, - moduleFile, - modulePath, - moduleName - }); - } - catch (const ghoul::lua::LuaRuntimeException& e) { - LERRORC(e.component, e.message); - continue; - } - } - else { - // If we do not have a module file, we have to include all subdirectories - using ghoul::filesystem::Directory; - using std::string; - std::vector directories = Directory(modulePath).readDirectories(); - - for (const string& s : directories) { - std::string::size_type pos = s.find_last_of(FileSys.PathSeparator); - if (pos == std::string::npos) { - LERROR("Error parsing subdirectory name '" << s << "'"); - continue; - } - string moduleName = s.substr(pos+1); - - string submodulePath = s; - string moduleFile = FileSys.pathByAppendingComponent(submodulePath, moduleName) + _moduleExtension; -// string moduleName = s; - - if (!FileSys.fileExists(moduleFile)) { - continue; - } - - // We have a module file, so it is a direct include - try { - ghoul::Dictionary moduleDictionary; - ghoul::lua::loadDictionaryFromFile(moduleFile, moduleDictionary, state); - moduleDictionaries.push_back({ - moduleDictionary, - moduleFile, - submodulePath, - moduleName - }); - } - catch (const ghoul::lua::LuaRuntimeException& e) { - LERRORC(e.component, e.message); - continue; - } - - } - - } - - auto addModule = [this, &dependencies, &parents](const ModuleInformation& moduleInformation) { - const ghoul::Dictionary& moduleDictionary = moduleInformation.dictionary; - const std::string& moduleFile = moduleInformation.moduleFile; - const std::string& modulePath = moduleInformation.modulePath; - const std::string& moduleName = moduleInformation.moduleName; - - std::vector keys = moduleDictionary.keys(); - for (const std::string& key : keys) { - if (!moduleDictionary.hasValue(key)) { - LERROR("SceneGraphNode '" << key << "' is not a table in module '" - << moduleFile << "'"); - continue; - } - - ghoul::Dictionary element; - std::string nodeName; - std::string parentName; - - moduleDictionary.getValue(key, element); - element.setValue(KeyPathModule, modulePath); - - element.getValue(SceneGraphNode::KeyName, nodeName); - element.getValue(SceneGraphNode::KeyParentName, parentName); - - FileSys.setCurrentDirectory(modulePath); - LDEBUGC("Create from dictionary", "Node name: " << nodeName << " Parent name:" << parentName << " Path: " << modulePath); - SceneGraphNode* node = SceneGraphNode::createFromDictionary(element); - if (node == nullptr) { - LERROR("Error loading SceneGraphNode '" << nodeName << "' in module '" << moduleName << "'"); - continue; - } - - dependencies[nodeName].push_back(parentName); - parents[nodeName] = parentName; - // Also include loaded dependencies - - if (element.hasKey(SceneGraphNode::KeyDependencies)) { - if (element.hasValue(SceneGraphNode::KeyDependencies)) { - ghoul::Dictionary nodeDependencies; - element.getValue(SceneGraphNode::KeyDependencies, nodeDependencies); - - std::vector keys = nodeDependencies.keys(); - for (const std::string& key : keys) { - std::string value = nodeDependencies.value(key); - dependencies[nodeName].push_back(value); - } - } - else { - LERROR("Dependencies did not have the corrent type"); - } - } - - - SceneGraphNodeInternal* internalNode = new SceneGraphNodeInternal; - internalNode->node = node; - _nodes.push_back(internalNode); - } - }; - - for (const ModuleInformation& i : moduleDictionaries) { - try { - LINFO("Adding module: " << i.moduleName); - addModule(i); - } - catch (const documentation::SpecificationError& specError) { - LERROR("Error loading module: " << i.moduleName); - LERRORC(specError.component, specError.message); - for (const auto& offense : specError.result.offenses) { - LERRORC(offense.offender, std::to_string(offense.reason)); - } - } - } - } -// ghoul::lua::destroyLuaState(state); - FileSys.setCurrentDirectory(oldDirectory); - - for (SceneGraphNodeInternal* node : _nodes) { - if (node->node == _rootNode) - continue; - std::string parent = parents[node->node->name()]; - SceneGraphNode* parentNode = sceneGraphNode(parent); - if (parentNode == nullptr) { - LERROR("Could not find parent '" << parent << "' for '" << node->node->name() << "'"); - continue; - } - - node->node->setParent(parentNode); - parentNode->addChild(node->node); - - } - - // Setup dependencies - for (SceneGraphNodeInternal* node : _nodes) { - std::vector nodeDependencies = dependencies[node->node->name()]; - - for (const std::string& dep : nodeDependencies) { - SceneGraphNodeInternal* n = nodeByName(dep); - if (n == nullptr) { - LERROR("Dependent node '" << dep << "' was not loaded for '" <node->name() << "'"); - continue; - } - node->outgoingEdges.push_back(n); - n->incomingEdges.push_back(node); - } - } - - std::vector nodesToDelete; - for (SceneGraphNodeInternal* node : _nodes) { - if (!nodeIsDependentOnRoot(node)) { - LERROR("Node '" << node->node->name() << "' has no direct connection to Root."); - nodesToDelete.push_back(node); - } - } - - for (SceneGraphNodeInternal* node : nodesToDelete) { - _nodes.erase(std::find(_nodes.begin(), _nodes.end(), node)); - delete node; - } - - bool s = sortTopologically(); - if (!s) { - LERROR("Topological sort failed"); - return false; - } - - return true; -} - -bool SceneGraph::nodeIsDependentOnRoot(SceneGraphNodeInternal* node) { - if (node->node->name() == SceneGraphNode::RootNodeName) - return true; - else { - for (SceneGraphNodeInternal* n : node->outgoingEdges) { - bool dep = nodeIsDependentOnRoot(n); - if (dep) - return true; - } - return false; - } -} - -bool SceneGraph::sortTopologically() { - if (_nodes.empty()) - return true; - - // Only the Root node can have an in-degree of 0 - SceneGraphNodeInternal* root = nodeByName(SceneGraphNode::RootNodeName); - ghoul_assert(root != nullptr, "Could not find Root node"); - - std::stack zeroInDegreeNodes; - zeroInDegreeNodes.push(root); - - std::unordered_map inDegrees; - for (SceneGraphNodeInternal* node : _nodes) - inDegrees[node] = node->outgoingEdges.size(); - //inDegrees[node] = node->incomingEdges.size(); - - _topologicalSortedNodes.clear(); - _topologicalSortedNodes.reserve(_nodes.size()); - while (!zeroInDegreeNodes.empty()) { - SceneGraphNodeInternal* node = zeroInDegreeNodes.top(); - - _topologicalSortedNodes.push_back(node->node); - zeroInDegreeNodes.pop(); - - //for (SceneGraphNodeInternal* n : node->outgoingEdges) { - for (SceneGraphNodeInternal* n : node->incomingEdges) { - inDegrees[n] -= 1; - if (inDegrees[n] == 0) - zeroInDegreeNodes.push(n); - } - - } - - return true; -} - -bool SceneGraph::addSceneGraphNode(SceneGraphNode* node) { - // @TODO rework this ---abock - ghoul_assert(node, "Node must not be nullptr"); - - SceneGraphNodeInternal* internalNode = new SceneGraphNodeInternal; - internalNode->node = node; - - auto it = std::find_if( - _nodes.begin(), - _nodes.end(), - [node](SceneGraphNodeInternal* i) { - return i->node == node->parent(); - } - ); - - if (it == _nodes.end()) { - LERROR("Parent node was not found"); - delete internalNode; - return false; - } - - (*it)->incomingEdges.push_back(internalNode); - internalNode->outgoingEdges.push_back(*it); - - _nodes.push_back(internalNode); - sortTopologically(); - - return true; -} - -bool SceneGraph::removeSceneGraphNode(SceneGraphNode* node) { - // @TODO rework this ---abock - ghoul_assert(node, "Node must not be nullptr"); - - auto it = std::find_if( - _nodes.begin(), - _nodes.end(), - [node](SceneGraphNodeInternal* i) { - return i->node == node; - } - ); - - if (it == _nodes.end()) { - LERROR("The node '" << node->name() << "' did not exist in the scenegraph"); - return false; - } - - // Remove internal node from the list of nodes - //SceneGraphNodeInternal* internalNode = *it; - _nodes.erase(it); - - if (OsEng.interactionHandler().focusNode() == node) - OsEng.interactionHandler().setFocusNode(node->parent()); - - sortTopologically(); - -#if 0 - SceneGraphNodeInternal* parentInternalNode = nodeByName(node->parent()->name()); - ghoul_assert(parentInternalNode, "Could not find internal parent node"); - - // Reparent its children to its parent - for (SceneGraphNode* c : node->children()) - c->setParent(node->parent()); - - // Reset the dependencies accordingly - // VERY untested ---abock - for (SceneGraphNodeInternal* c : internalNode->incomingEdges) { - parentInternalNode->outgoingEdges.insert(parentInternalNode->outgoingEdges.end(), c->outgoingEdges.begin(), c->outgoingEdges.end()); - parentInternalNode->incomingEdges.insert(parentInternalNode->incomingEdges.end(), c->incomingEdges.begin(), c->incomingEdges.end()); - } - -#endif - - return true; -} - -SceneGraph::SceneGraphNodeInternal* SceneGraph::nodeByName(const std::string& name) { - auto it = std::find_if( - _nodes.begin(), - _nodes.end(), - [name](SceneGraphNodeInternal* node) { - return node->node->name() == name; - } - ); - - if (it == _nodes.end()) - return nullptr; - else - return *it; -} - -const std::vector& SceneGraph::nodes() const { - return _topologicalSortedNodes; -} - -SceneGraphNode* SceneGraph::rootNode() const { - return _rootNode; -} - -SceneGraphNode* SceneGraph::sceneGraphNode(const std::string& name) const { - auto it = std::find_if( - _nodes.begin(), - _nodes.end(), - [name](SceneGraphNodeInternal* node) { - return node->node->name() == name; - } - ); - if (it != _nodes.end()) - return (*it)->node; - else - return nullptr; -} - -} // namespace openspace diff --git a/src/scene/scenegraphnode.cpp b/src/scene/scenegraphnode.cpp index c7d58b84ea..0207cdc761 100644 --- a/src/scene/scenegraphnode.cpp +++ b/src/scene/scenegraphnode.cpp @@ -66,14 +66,14 @@ const std::string SceneGraphNode::KeyName = "Name"; const std::string SceneGraphNode::KeyParentName = "Parent"; const std::string SceneGraphNode::KeyDependencies = "Dependencies"; -SceneGraphNode* SceneGraphNode::createFromDictionary(const ghoul::Dictionary& dictionary){ +std::unique_ptr SceneGraphNode::createFromDictionary(const ghoul::Dictionary& dictionary){ openspace::documentation::testSpecificationAndThrow( SceneGraphNode::Documentation(), dictionary, "SceneGraphNode" ); - SceneGraphNode* result = new SceneGraphNode; + std::unique_ptr result = std::make_unique(); std::string name = dictionary.value(KeyName); result->setName(name); @@ -88,7 +88,6 @@ SceneGraphNode* SceneGraphNode::createFromDictionary(const ghoul::Dictionary& di if (result->_renderable == nullptr) { LERROR("Failed to create renderable for SceneGraphNode '" << result->name() << "'"); - delete result; return nullptr; } result->addPropertySubOwner(result->_renderable); @@ -103,7 +102,6 @@ SceneGraphNode* SceneGraphNode::createFromDictionary(const ghoul::Dictionary& di if (result->_transform.translation == nullptr) { LERROR("Failed to create ephemeris for SceneGraphNode '" << result->name() << "'"); - delete result; return nullptr; } result->addPropertySubOwner(result->_transform.translation.get()); @@ -118,7 +116,6 @@ SceneGraphNode* SceneGraphNode::createFromDictionary(const ghoul::Dictionary& di if (result->_transform.rotation == nullptr) { LERROR("Failed to create rotation for SceneGraphNode '" << result->name() << "'"); - delete result; return nullptr; } result->addPropertySubOwner(result->_transform.rotation.get()); @@ -133,7 +130,6 @@ SceneGraphNode* SceneGraphNode::createFromDictionary(const ghoul::Dictionary& di if (result->_transform.scale == nullptr) { LERROR("Failed to create scale for SceneGraphNode '" << result->name() << "'"); - delete result; return nullptr; } result->addPropertySubOwner(result->_transform.scale.get()); @@ -142,11 +138,12 @@ SceneGraphNode* SceneGraphNode::createFromDictionary(const ghoul::Dictionary& di LDEBUG("Successfully created SceneGraphNode '" << result->name() << "'"); - return result; + return std::move(result); } SceneGraphNode::SceneGraphNode() : _parent(nullptr) + , _scene(nullptr) , _transform { std::make_unique(), std::make_unique(), @@ -201,6 +198,20 @@ bool SceneGraphNode::deinitialize() { return true; } +void SceneGraphNode::traversePreOrder(std::function fn) { + fn(this); + for (auto& child : _children) { + child->traversePreOrder(fn); + } +} + +void SceneGraphNode::traversePostOrder(std::function fn) { + for (auto& child : _children) { + child->traversePostOrder(fn); + } + fn(this); +} + void SceneGraphNode::update(const UpdateData& data) { if (_transform.translation) { if (data.doPerformanceMeasurement) { @@ -366,26 +377,135 @@ void SceneGraphNode::render(const RenderData& data, RendererTasks& tasks) { // _children.push_back(child); //} -void SceneGraphNode::setParent(SceneGraphNode* parent) { - _parent = parent; +void SceneGraphNode::setParent(SceneGraphNode& parent, UpdateScene updateScene) { + 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(); + } } -void SceneGraphNode::addChild(SceneGraphNode* child) { - _children.push_back(child); +void SceneGraphNode::attachChild(std::unique_ptr child, UpdateScene updateScene) { + ghoul_assert(child->parent() == nullptr, "Child may not already have a parent"); + + child->_parent = this; + if (_scene) { + child->setScene(_scene); + } + + _children.push_back(std::move(child)); + + if (_scene && updateScene) { + _scene->addNode(child.get()); + } +} + +std::unique_ptr SceneGraphNode::detachChild(SceneGraphNode& child, UpdateScene updateScene) { + 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(), + _children.end(), + [&child] (const auto& c) { + return &child == c.get(); + } + ); + + std::unique_ptr c = std::move(*iter); + _children.erase(iter); + + if (_scene && updateScene) { + _scene->removeNode(&child); + } + + if (_scene) { + setScene(nullptr); + } + return std::move(c); +} + +void SceneGraphNode::addDependency(SceneGraphNode& dependency, UpdateScene updateScene) { + dependency._dependentNodes.push_back(this); + _dependencies.push_back(&dependency); + + if (_scene && updateScene) { + _scene->updateDependencies(); + } +} + +void SceneGraphNode::removeDependency(SceneGraphNode& dependency, UpdateScene updateScene) { + dependency._dependentNodes.erase(std::remove_if( + dependency._dependentNodes.begin(), + dependency._dependentNodes.end(), + [this](const auto& d) { + return this == d; + } + ), dependency._dependentNodes.end()); + _dependencies.erase(std::remove_if( + _dependencies.begin(), + _dependencies.end(), + [&dependency](const auto& d) { + return &dependency == d; + } + ), _dependencies.end()); + + if (_scene && updateScene) { + _scene->updateDependencies(); + } +} + +void SceneGraphNode::clearDependencies(UpdateScene updateScene) { + for (auto& dependency : _dependencies) { + dependency->_dependentNodes.erase(std::remove_if( + dependency->_dependentNodes.begin(), + dependency->_dependentNodes.end(), + [this](const auto& d) { + return this == d; + } + ), dependency->_dependentNodes.end()); + } + _dependencies.clear(); + + if (_scene && updateScene) { + _scene->updateDependencies(); + } +} + +void SceneGraphNode::setDependencies(const std::vector& dependencies, UpdateScene updateScene) { + clearDependencies(UpdateScene::No); + + _dependencies = dependencies; + for (auto& dependency : dependencies) { + dependency->_dependentNodes.push_back(this); + } + + if (_scene && updateScene) { + _scene->updateDependencies(); + } } -//not used anymore @AA -//bool SceneGraphNode::abandonChild(SceneGraphNode* child) { -// std::vector < SceneGraphNode* >::iterator it = std::find(_children.begin(), _children.end(), child); -// -// if (it != _children.end()){ -// _children.erase(it); -// return true; -// } -// -// return false; -//} +const std::vector& SceneGraphNode::dependencies() const { + return _dependencies; +} + +const std::vector& SceneGraphNode::dependentNodes() const { + return _dependentNodes; +} glm::dvec3 SceneGraphNode::position() const { @@ -451,15 +571,28 @@ double SceneGraphNode::calculateWorldScale() const { } } -SceneGraphNode* SceneGraphNode::parent() const -{ +SceneGraphNode* SceneGraphNode::parent() const { return _parent; } -const std::vector& SceneGraphNode::children() const{ - return _children; + +Scene* SceneGraphNode::scene() { + return _scene; +} + +void SceneGraphNode::setScene(Scene* scene) { + traversePreOrder([scene](SceneGraphNode* node) { + node->_scene = scene; + }); +} + +std::vector SceneGraphNode::children() const { + std::vector nodes; + for (auto& child : _children) { + nodes.push_back(child.get()); + } + return nodes; } -// bounding sphere PowerScaledScalar SceneGraphNode::calculateBoundingSphere(){ // set the bounding sphere to 0.0 _boundingSphere = 0.0; @@ -490,7 +623,6 @@ PowerScaledScalar SceneGraphNode::calculateBoundingSphere(){ if(renderableBS > _boundingSphere) _boundingSphere = renderableBS; } - //LINFO("Bounding Sphere of '" << name() << "': " << _boundingSphere); return _boundingSphere; } @@ -499,13 +631,11 @@ PowerScaledScalar SceneGraphNode::boundingSphere() const{ return _boundingSphere; } -// renderable void SceneGraphNode::setRenderable(Renderable* renderable) { _renderable = renderable; } -const Renderable* SceneGraphNode::renderable() const -{ +const Renderable* SceneGraphNode::renderable() const { return _renderable; } @@ -513,7 +643,7 @@ Renderable* SceneGraphNode::renderable() { return _renderable; } -// private helper methods +/* bool SceneGraphNode::sphereInsideFrustum(const psc& s_pos, const PowerScaledScalar& s_rad, const Camera* camera) { @@ -544,15 +674,16 @@ bool SceneGraphNode::sphereInsideFrustum(const psc& s_pos, const PowerScaledScal return false; } } +*/ SceneGraphNode* SceneGraphNode::childNode(const std::string& name) { if (this->name() == name) return this; else - for (SceneGraphNode* it : _children) { + for (auto& it : _children) { SceneGraphNode* tmp = it->childNode(name); - if (tmp != nullptr) + if (tmp) return tmp; } return nullptr; diff --git a/src/scene/sceneloader.cpp b/src/scene/sceneloader.cpp new file mode 100644 index 0000000000..ebda2dba7e --- /dev/null +++ b/src/scene/sceneloader.cpp @@ -0,0 +1,391 @@ +/***************************************************************************************** +* * +* OpenSpace * +* * +* Copyright (c) 2014-2016 * +* * +* Permission is hereby granted, free of charge, to any person obtaining a copy of this * +* software and associated documentation files (the "Software"), to deal in the Software * +* without restriction, including without limitation the rights to use, copy, modify, * +* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * +* permit persons to whom the Software is furnished to do so, subject to the following * +* conditions: * +* * +* The above copyright notice and this permission notice shall be included in all copies * +* or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * +* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * +* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * +* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * +* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * +* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * +****************************************************************************************/ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +namespace { + const std::string _loggerCat = "SceneLoader"; + const std::string KeyPathScene = "ScenePath"; + const std::string KeyModules = "Modules"; + const std::string ModuleExtension = ".mod"; + const std::string KeyPathModule = "ModulePath"; + + const std::string RootNodeName = "Root"; + const std::string KeyName = "Name"; + const std::string KeyParentName = "Parent"; + const std::string KeyDependencies = "Dependencies"; + const std::string KeyCamera = "Camera"; + const std::string KeyCameraFocus = "Focus"; + const std::string KeyCameraPosition = "Position"; + const std::string KeyCameraRotation = "Rotation"; +} + +struct ModuleInformation { + ghoul::Dictionary dictionary; + std::string moduleFile; + std::string modulePath; + std::string moduleName; +}; + +namespace openspace { + +std::unique_ptr SceneLoader::loadScene(const std::string& path) { + // Set up lua state. + lua_State* state = ghoul::lua::createNewLuaState(); + OnExit( + // Delete the Lua state at the end of the scope, no matter what. + [state]() {ghoul::lua::destroyLuaState(state); } + ); + OsEng.scriptEngine().initializeLuaState(state); + + std::string absScenePath = absPath(path); + ghoul::filesystem::File sceneFile(absScenePath); + std::string sceneDirectory = sceneFile.directoryName(); + + ghoul::Dictionary sceneDictionary; + if (!FileSys.fileExists(absScenePath)) { + throw ghoul::FileNotFoundError(absScenePath); + } + ghoul::lua::loadDictionaryFromFile(absScenePath, sceneDictionary, state); + + documentation::testSpecificationAndThrow(Scene::Documentation(), sceneDictionary, "Scene"); + + std::string relativeSceneDirectory = "."; + sceneDictionary.getValue(KeyPathScene, relativeSceneDirectory); + std::string modulesPath = FileSys.absPath(sceneDirectory + FileSys.PathSeparator + relativeSceneDirectory); + + ghoul::Dictionary moduleDictionary; + sceneDictionary.getValue(KeyModules, moduleDictionary); + + // Above we generated a ghoul::Dictionary from the scene file; now we run the scene + // file again to load any variables defined inside into the state that is passed to + // the modules. This allows us to specify global variables that can then be used + // inside the modules to toggle settings. + ghoul::lua::runScriptFile(state, absScenePath); + std::vector keys = moduleDictionary.keys(); + ghoul::filesystem::Directory oldDirectory = FileSys.currentDirectory(); + + std::vector allNodes; + + for (const std::string& key : keys) { + std::string fullModuleName = moduleDictionary.value(key); + std::replace(fullModuleName.begin(), fullModuleName.end(), '/', FileSys.PathSeparator); + std::string modulePath = FileSys.pathByAppendingComponent(modulesPath, fullModuleName); + + std::vector nodes = loadDirectory(modulePath, state); + std::move(nodes.begin(), nodes.end(), std::back_inserter(allNodes)); + } + + FileSys.setCurrentDirectory(oldDirectory); + + std::unique_ptr scene = std::make_unique(); + + std::unique_ptr rootNode = std::make_unique(); + rootNode->setName(SceneGraphNode::RootNodeName); + scene->setRoot(std::move(rootNode)); + + addLoadedNodes(*scene, std::move(allNodes)); + + ghoul::Dictionary cameraDictionary; + sceneDictionary.getValue(KeyCamera, cameraDictionary); + LoadedCamera loadedCamera = loadCamera(cameraDictionary); + + auto& nodeMap = scene->nodesByName(); + auto it = nodeMap.find(loadedCamera.parent); + if (it != nodeMap.end()) { + loadedCamera.camera->setParent(it->second); + } + + scene->setCamera(std::move(loadedCamera.camera)); + + return std::move(scene); +} + +std::vector SceneLoader::importDirectory(Scene& scene, const std::string& path) { + lua_State* state = ghoul::lua::createNewLuaState(); + OnExit( + // Delete the Lua state at the end of the scope, no matter what. + [state]() {ghoul::lua::destroyLuaState(state); } + ); + OsEng.scriptEngine().initializeLuaState(state); + + std::string absDirectoryPath = absPath(path); + + ghoul::filesystem::Directory oldDirectory = FileSys.currentDirectory(); + std::vector nodes = loadDirectory(path, state); + FileSys.setCurrentDirectory(oldDirectory); + return addLoadedNodes(scene, std::move(nodes)); +} + +SceneGraphNode* SceneLoader::importNodeDictionary(Scene& scene, const ghoul::Dictionary& dict) { + std::vector loadedNodes; + loadedNodes.push_back(loadNode(dict)); + std::vector nodes = addLoadedNodes(scene, std::move(loadedNodes)); + if (nodes.size() == 1) { + return nodes[0]; + } + return nullptr; +} + +SceneLoader::LoadedCamera SceneLoader::loadCamera(const ghoul::Dictionary& cameraDict) { + std::string focus; + glm::vec3 cameraPosition; + glm::vec4 cameraRotation; + + bool readSuccessful = true; + readSuccessful &= cameraDict.getValue(KeyCameraFocus, focus); + readSuccessful &= cameraDict.getValue(KeyCameraPosition, cameraPosition); + readSuccessful &= cameraDict.getValue(KeyCameraRotation, cameraRotation); + + std::unique_ptr camera = std::make_unique(); + + camera->setPositionVec3(cameraPosition); + camera->setRotation(glm::dquat( + cameraRotation.x, cameraRotation.y, cameraRotation.z, cameraRotation.w)); + + LoadedCamera loadedCamera(focus, std::move(camera)); + + if (!readSuccessful) { + throw Scene::InvalidSceneError( + "Position, Rotation and Focus need to be defined for camera dictionary."); + } + + return loadedCamera; +} + + +std::vector SceneLoader::loadDirectory( + const std::string& path, + lua_State* luaState) +{ + std::string::size_type pos = path.find_last_of(FileSys.PathSeparator); + if (pos == std::string::npos) { + LERROR("Error parsing directory name '" << path << "'"); + return std::vector(); + } + std::string moduleName = path.substr(pos + 1); + std::string moduleFile = FileSys.pathByAppendingComponent(path, moduleName) + ModuleExtension; + + if (FileSys.fileExists(moduleFile)) { + // TODO: Get rid of changing the working directory (global state is bad) -- emiax + // This requires refactoring all renderables to not use relative paths in constructors. + FileSys.setCurrentDirectory(ghoul::filesystem::Directory(path)); + + // We have a module file, so it is a direct include. + return loadModule(moduleFile, luaState); + } else { + std::vector allLoadedNodes; + // If we do not have a module file, we have to include all subdirectories. + using ghoul::filesystem::Directory; + using std::string; + std::vector directories = Directory(path).readDirectories(); + + for (const string& s : directories) { + //std::string submodulePath = FileSys.pathByAppendingComponent(path, s); + std::vector loadedNodes = loadDirectory(s, luaState); + std::move(loadedNodes.begin(), loadedNodes.end(), std::back_inserter(allLoadedNodes)); + } + return allLoadedNodes; + } +} + + +SceneLoader::LoadedNode SceneLoader::loadNode(const ghoul::Dictionary& dictionary) { + std::vector dependencies; + + std::string nodeName = dictionary.value(KeyName); + std::string parentName = dictionary.value(KeyParentName); + std::unique_ptr node = SceneGraphNode::createFromDictionary(dictionary); + + if (dictionary.hasKey(SceneGraphNode::KeyDependencies)) { + if (!dictionary.hasValue(SceneGraphNode::KeyDependencies)) { + LERROR("Dependencies did not have the corrent type"); + } + ghoul::Dictionary nodeDependencies; + dictionary.getValue(SceneGraphNode::KeyDependencies, nodeDependencies); + + std::vector keys = nodeDependencies.keys(); + for (const std::string& key : keys) { + std::string value = nodeDependencies.value(key); + dependencies.push_back(value); + } + } + return SceneLoader::LoadedNode(nodeName, parentName, dependencies, std::move(node)); +} + + +std::vector SceneLoader::loadModule(const std::string& path, lua_State* luaState) { + ghoul::Dictionary moduleDictionary; + try { + ghoul::lua::loadDictionaryFromFile(path, moduleDictionary, luaState); + } catch (const ghoul::lua::LuaRuntimeException& e) { + LERRORC(e.component, e.message); + return std::vector(); + } + + std::vector loadedNodes; + std::vector keys = moduleDictionary.keys(); + for (const std::string& key : keys) { + ghoul::Dictionary nodeDictionary; + if (!moduleDictionary.getValue(key, nodeDictionary)) { + LERROR("Node dictionary did not have the corrent type"); + continue; + } + try { + loadedNodes.push_back(loadNode(nodeDictionary)); + } catch (ghoul::RuntimeError& e) { + LERROR("Failed loading node from " << path << ": " << e.message << ", " << e.component); + } + } + return loadedNodes; +}; + +std::vector SceneLoader::addLoadedNodes(Scene& scene, std::vector loadedNodes) { + std::map existingNodes = scene.nodesByName(); + std::map addedNodes; + + // Populate map of nodes to be added. + // Also track new branches of nodes that are attached + // to allow for recovery in case an invalid scene is generated. + for (auto& loadedNode : loadedNodes) { + std::string name = loadedNode.name; + if (existingNodes.count(name) > 0) { + LERROR("Node with name '" + name + "' already exists in scene"); + continue; + } + if (addedNodes.count(name) > 0) { + LERROR("Duplicate node names '" + name + "' among loaded nodes"); + } + + SceneGraphNode* node = loadedNode.node.get(); + addedNodes[name] = node; + } + + // Find a node by name among the exising nodes and the added nodes. + auto findNode = [&existingNodes, &addedNodes](const std::string name) { + std::map::iterator it; + if ((it = existingNodes.find(name)) != existingNodes.end()) { + return it->second; + } + if ((it = addedNodes.find(name)) != addedNodes.end()) { + return it->second; + } + return static_cast(nullptr); + }; + + + std::vector attachedBranches; + std::vector> badNodes; + + // Attach each node to its parent and set up dependencies. + for (auto& loadedNode : loadedNodes) { + std::string parentName = loadedNode.parent; + std::vector dependencyNames = loadedNode.dependencies; + + SceneGraphNode* parent = findNode(parentName); + if (!parent) { + LERROR("Could not find parent '" + parentName + "' for '" + loadedNode.name + "'"); + badNodes.push_back(std::move(loadedNode.node)); + continue; + } + + std::vector dependencies; + bool foundAllDeps = true; + for (const auto& depName : dependencyNames) { + SceneGraphNode* dep = findNode(depName); + if (!dep) { + LERROR("Could not find dependency '" + depName + "' for '" + loadedNode.name + "'"); + foundAllDeps = false; + continue; + } + dependencies.push_back(dep); + } + + if (!foundAllDeps) { + badNodes.push_back(std::move(loadedNode.node)); + continue; + } + + SceneGraphNode* child = loadedNode.node.get(); + parent->attachChild(std::move(loadedNode.node), SceneGraphNode::UpdateScene::No); + child->setDependencies(dependencies, SceneGraphNode::UpdateScene::No); + + if (existingNodes.find(parentName) != existingNodes.end()) { + attachedBranches.push_back(child); + } + } + + // Add the nodes to the scene. + for (auto& node : attachedBranches) { + scene.addNode(node, Scene::UpdateDependencies::No); + } + + // Remove all bad nodes (parent or deps missing) and all their children and dependent nodes. + // Use unsorted set `visited` to avoid infinite loop in case of circular deps. + std::unordered_set visited; + for (size_t i = 0; i < badNodes.size(); i++) { + auto& badNode = badNodes[i]; + for (auto c : badNode->children()) { + visited.insert(c); + badNodes.push_back(std::move(badNode->detachChild(*c, SceneGraphNode::UpdateScene::No))); + } + for (auto& d : badNode->dependentNodes()) { + SceneGraphNode* parent = d->parent(); + if (visited.count(d) == 0) { + visited.insert(d); + if (parent) { + badNodes.push_back(std::move(parent->detachChild(*d, SceneGraphNode::UpdateScene::No))); + } + } + } + } + // Warn for nodes that lack connection to the root. + for (auto& node : addedNodes) { + if (!node.second->scene()) { + LWARNING("Node '" << node.first << "' is not connected to the root and will not be added to the scene"); + } + } + // Update dependencies: sort nodes topologically. + scene.updateDependencies(); + + // Return a vector of all added nodes. + std::vector addedNodesVector; + for (auto& it : addedNodes) { + addedNodesVector.push_back(it.second); + } + return addedNodesVector; +} +} diff --git a/src/scene/scenemanager.cpp b/src/scene/scenemanager.cpp new file mode 100644 index 0000000000..14ff89754c --- /dev/null +++ b/src/scene/scenemanager.cpp @@ -0,0 +1,48 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2016 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#include +#include +#include +#include +#include + +namespace openspace { + +Scene* SceneManager::loadScene(const std::string& path) { + SceneLoader loader; + std::unique_ptr scene = loader.loadScene(path); + Scene* s = scene.get(); + if (s) { + _scenes.push_back(std::move(scene)); + } + return s; +} + +void SceneManager::unloadScene(Scene& scene) { + std::remove_if(_scenes.begin(), _scenes.end(), [&scene] (auto& s) { + return s.get() == &scene; + }); +} +} diff --git a/src/util/camera.cpp b/src/util/camera.cpp index 0dd135f3dc..a1725f8d04 100644 --- a/src/util/camera.cpp +++ b/src/util/camera.cpp @@ -103,6 +103,10 @@ namespace openspace { _cachedSinMaxFov.isDirty = true; } + void Camera::setParent(SceneGraphNode* parent) { + _parent = parent; + } + // Relative mutators void Camera::rotate(Quat rotation) { std::lock_guard _lock(_mutex); @@ -167,6 +171,10 @@ namespace openspace { return _cachedSinMaxFov.datum; } + SceneGraphNode * Camera::parent() const { + return _parent; + } + const Camera::Mat4& Camera::viewRotationMatrix() const { if (_cachedViewRotationMatrix.isDirty) { _cachedViewRotationMatrix.datum = glm::mat4_cast(glm::inverse((glm::dquat)_rotation)); diff --git a/tests/SceneLoaderTest/child-and-dependent/child-and-dependent.mod b/tests/SceneLoaderTest/child-and-dependent/child-and-dependent.mod new file mode 100644 index 0000000000..6647d01029 --- /dev/null +++ b/tests/SceneLoaderTest/child-and-dependent/child-and-dependent.mod @@ -0,0 +1,9 @@ +return { + { + Name = "ChildAndDependent", + Parent = "NoDependency", + Dependencies = { + "Dependent" + } + } +} diff --git a/tests/ScenegraphLoaderTest/direct-dependency/direct-dependency.mod b/tests/SceneLoaderTest/child/child.mod similarity index 61% rename from tests/ScenegraphLoaderTest/direct-dependency/direct-dependency.mod rename to tests/SceneLoaderTest/child/child.mod index 75f2243918..a62e974246 100644 --- a/tests/ScenegraphLoaderTest/direct-dependency/direct-dependency.mod +++ b/tests/SceneLoaderTest/child/child.mod @@ -1,6 +1,6 @@ return { { - Name = "DirectDependency", + Name = "Child", Parent = "NoDependency" } } diff --git a/tests/ScenegraphLoaderTest/circular-dependency/circular-dependency.mod b/tests/SceneLoaderTest/circular-dependency/circular-dependency.mod similarity index 67% rename from tests/ScenegraphLoaderTest/circular-dependency/circular-dependency.mod rename to tests/SceneLoaderTest/circular-dependency/circular-dependency.mod index 901da062d3..a80e97c17c 100644 --- a/tests/ScenegraphLoaderTest/circular-dependency/circular-dependency.mod +++ b/tests/SceneLoaderTest/circular-dependency/circular-dependency.mod @@ -1,7 +1,8 @@ return { { Name = "CircularDependency1", - Parent = "CircularDependency2" + Parent = "Root", + Dependencies = {"CircularDependency2"} }, { Name = "CircularDependency2", diff --git a/tests/SceneLoaderTest/dependent/dependent.mod b/tests/SceneLoaderTest/dependent/dependent.mod new file mode 100644 index 0000000000..e343bd01bf --- /dev/null +++ b/tests/SceneLoaderTest/dependent/dependent.mod @@ -0,0 +1,7 @@ +return { + { + Name = "Dependent", + Parent = "Root", + Dependencies = {"NoDependency"} + } +} diff --git a/tests/ScenegraphLoaderTest/illformed.scene b/tests/SceneLoaderTest/illformed.scene similarity index 100% rename from tests/ScenegraphLoaderTest/illformed.scene rename to tests/SceneLoaderTest/illformed.scene diff --git a/tests/ScenegraphLoaderTest/illformedNonExistingCommon.scene b/tests/SceneLoaderTest/illformedInvalidScene.scene similarity index 71% rename from tests/ScenegraphLoaderTest/illformedNonExistingCommon.scene rename to tests/SceneLoaderTest/illformedInvalidScene.scene index 7d7f367e42..f5df997fb9 100644 --- a/tests/ScenegraphLoaderTest/illformedNonExistingCommon.scene +++ b/tests/SceneLoaderTest/illformedInvalidScene.scene @@ -1,6 +1,6 @@ -- Malformed script return { - ScenePath = ".", + ScenePath = 1337, Modules = { } } diff --git a/tests/ScenegraphLoaderTest/illformedWrongType.scene b/tests/SceneLoaderTest/illformedWrongType.scene similarity index 54% rename from tests/ScenegraphLoaderTest/illformedWrongType.scene rename to tests/SceneLoaderTest/illformedWrongType.scene index 2ab37b2a58..fa35cc663d 100644 --- a/tests/ScenegraphLoaderTest/illformedWrongType.scene +++ b/tests/SceneLoaderTest/illformedWrongType.scene @@ -1,8 +1,6 @@ -- Malformed script return { ScenePath = ".", - CommonFolder = 2, - Modules = { - } + Modules = 2 } diff --git a/tests/ScenegraphLoaderTest/no-dependency/no-dependency.mod b/tests/SceneLoaderTest/no-dependency/no-dependency.mod similarity index 100% rename from tests/ScenegraphLoaderTest/no-dependency/no-dependency.mod rename to tests/SceneLoaderTest/no-dependency/no-dependency.mod diff --git a/tests/ScenegraphLoaderTest/scene-folder/direct-dependency/direct-dependency.mod b/tests/SceneLoaderTest/scene-folder/child/child.mod similarity index 61% rename from tests/ScenegraphLoaderTest/scene-folder/direct-dependency/direct-dependency.mod rename to tests/SceneLoaderTest/scene-folder/child/child.mod index 75f2243918..a62e974246 100644 --- a/tests/ScenegraphLoaderTest/scene-folder/direct-dependency/direct-dependency.mod +++ b/tests/SceneLoaderTest/scene-folder/child/child.mod @@ -1,6 +1,6 @@ return { { - Name = "DirectDependency", + Name = "Child", Parent = "NoDependency" } } diff --git a/tests/ScenegraphLoaderTest/scene-folder/no-dependency/no-dependency.mod b/tests/SceneLoaderTest/scene-folder/no-dependency/no-dependency.mod similarity index 100% rename from tests/ScenegraphLoaderTest/scene-folder/no-dependency/no-dependency.mod rename to tests/SceneLoaderTest/scene-folder/no-dependency/no-dependency.mod diff --git a/tests/ScenegraphLoaderTest/test00-location.scene b/tests/SceneLoaderTest/test00-location.scene similarity index 54% rename from tests/ScenegraphLoaderTest/test00-location.scene rename to tests/SceneLoaderTest/test00-location.scene index b67e0be095..6e24f5dbb5 100644 --- a/tests/ScenegraphLoaderTest/test00-location.scene +++ b/tests/SceneLoaderTest/test00-location.scene @@ -1,7 +1,11 @@ -- Loading an empty common and module dictionary in a different folder return { ScenePath = "scene-folder", - CommonFolder = "", + Camera = { + Position = {0.0, 0.0, 0.0}, + Rotation = {0.0, 0.0, 0.0, 0.0}, + Focus = "Root" + }, Modules = { } } diff --git a/tests/SceneLoaderTest/test00.scene b/tests/SceneLoaderTest/test00.scene new file mode 100644 index 0000000000..8c1364107e --- /dev/null +++ b/tests/SceneLoaderTest/test00.scene @@ -0,0 +1,12 @@ +-- Loading an empty common and module dictionary +return { + ScenePath = ".", + Camera = { + Position = {0.0, 0.0, 0.0}, + Rotation = {0.0, 0.0, 0.0, 0.0}, + Focus = "Root" + }, + Modules = { + } +} + diff --git a/tests/ScenegraphLoaderTest/test02-location.scene b/tests/SceneLoaderTest/test01-location.scene similarity index 56% rename from tests/ScenegraphLoaderTest/test02-location.scene rename to tests/SceneLoaderTest/test01-location.scene index 1c2122cd76..2fd9c83689 100644 --- a/tests/ScenegraphLoaderTest/test02-location.scene +++ b/tests/SceneLoaderTest/test01-location.scene @@ -1,7 +1,11 @@ -- Loading a module without dependencies in a different folder return { ScenePath = "scene-folder", - CommonFolder = "common", + Camera = { + Position = {0.0, 0.0, 0.0}, + Rotation = {0.0, 0.0, 0.0, 0.0}, + Focus = "Root" + }, Modules = { "no-dependency" } diff --git a/tests/ScenegraphLoaderTest/test02.scene b/tests/SceneLoaderTest/test01.scene similarity index 50% rename from tests/ScenegraphLoaderTest/test02.scene rename to tests/SceneLoaderTest/test01.scene index 7493bb998f..cd185211b7 100644 --- a/tests/ScenegraphLoaderTest/test02.scene +++ b/tests/SceneLoaderTest/test01.scene @@ -1,7 +1,11 @@ -- Loading a module without dependencies return { ScenePath = ".", - CommonFolder = "common", + Camera = { + Position = {0.0, 0.0, 0.0}, + Rotation = {0.0, 0.0, 0.0, 0.0}, + Focus = "Root" + }, Modules = { "no-dependency" } diff --git a/tests/ScenegraphLoaderTest/test04-location.scene b/tests/SceneLoaderTest/test02-location.scene similarity index 55% rename from tests/ScenegraphLoaderTest/test04-location.scene rename to tests/SceneLoaderTest/test02-location.scene index 0859e85505..76c3edc1d9 100644 --- a/tests/ScenegraphLoaderTest/test04-location.scene +++ b/tests/SceneLoaderTest/test02-location.scene @@ -1,10 +1,14 @@ -- Loading a module with a direct dependency in a different folder return { ScenePath = "scene-folder", - CommonFolder = "common", + Camera = { + Position = {0.0, 0.0, 0.0}, + Rotation = {0.0, 0.0, 0.0, 0.0}, + Focus = "Root" + }, Modules = { "no-dependency", - "direct-dependency" + "child" } } diff --git a/tests/SceneLoaderTest/test02.scene b/tests/SceneLoaderTest/test02.scene new file mode 100644 index 0000000000..ecf5bfaa61 --- /dev/null +++ b/tests/SceneLoaderTest/test02.scene @@ -0,0 +1,14 @@ +-- Loading a module with a direct dependency +return { + ScenePath = ".", + Camera = { + Position = {0.0, 0.0, 0.0}, + Rotation = {0.0, 0.0, 0.0, 0.0}, + Focus = "Root" + }, + Modules = { + "no-dependency", + "child" + } +} + diff --git a/tests/SceneLoaderTest/test03.scene b/tests/SceneLoaderTest/test03.scene new file mode 100644 index 0000000000..edd6f2f746 --- /dev/null +++ b/tests/SceneLoaderTest/test03.scene @@ -0,0 +1,14 @@ +-- Multiple Dependencies +return { + ScenePath = ".", + Camera = { + Position = {0.0, 0.0, 0.0}, + Rotation = {0.0, 0.0, 0.0, 0.0}, + Focus = "Root" + }, + Modules = { + "no-dependency", + "dependent" + } +} + diff --git a/tests/SceneLoaderTest/test04.scene b/tests/SceneLoaderTest/test04.scene new file mode 100644 index 0000000000..1f6b91f672 --- /dev/null +++ b/tests/SceneLoaderTest/test04.scene @@ -0,0 +1,16 @@ +-- Multiple Dependencies +return { + ScenePath = ".", + Camera = { + Position = {0.0, 0.0, 0.0}, + Rotation = {0.0, 0.0, 0.0, 0.0}, + Focus = "Root" + }, + Modules = { + "child", + "child-and-dependent", + "no-dependency", + "dependent" + } +} + diff --git a/tests/SceneLoaderTest/test05.scene b/tests/SceneLoaderTest/test05.scene new file mode 100644 index 0000000000..1b603d2184 --- /dev/null +++ b/tests/SceneLoaderTest/test05.scene @@ -0,0 +1,13 @@ +-- Circular Dependencies +return { + ScenePath = ".", + Camera = { + Position = {0.0, 0.0, 0.0}, + Rotation = {0.0, 0.0, 0.0, 0.0}, + Focus = "Root" + }, + Modules = { + "circular-dependency" + } +} + diff --git a/tests/ScenegraphLoaderTest/common-dependency/common-dependency.mod b/tests/ScenegraphLoaderTest/common-dependency/common-dependency.mod deleted file mode 100644 index 313ebbd599..0000000000 --- a/tests/ScenegraphLoaderTest/common-dependency/common-dependency.mod +++ /dev/null @@ -1,6 +0,0 @@ -return { - { - Name = "CommonDependency", - Parent = "Common" - } -} diff --git a/tests/ScenegraphLoaderTest/common/common.mod b/tests/ScenegraphLoaderTest/common/common.mod deleted file mode 100644 index 3e9ef8146d..0000000000 --- a/tests/ScenegraphLoaderTest/common/common.mod +++ /dev/null @@ -1,6 +0,0 @@ -return { - { - Name = "Common", - Parent = "Root", - }, -} \ No newline at end of file diff --git a/tests/ScenegraphLoaderTest/illformedInvalidScene.scene b/tests/ScenegraphLoaderTest/illformedInvalidScene.scene deleted file mode 100644 index 6ac24028f9..0000000000 --- a/tests/ScenegraphLoaderTest/illformedInvalidScene.scene +++ /dev/null @@ -1,8 +0,0 @@ --- Malformed script -return { - ScenePath = "foobar", - CommonFolder = "", - Modules = { - } -} - diff --git a/tests/ScenegraphLoaderTest/illformedWrongCommon.scene b/tests/ScenegraphLoaderTest/illformedWrongCommon.scene deleted file mode 100644 index f46e3d9b57..0000000000 --- a/tests/ScenegraphLoaderTest/illformedWrongCommon.scene +++ /dev/null @@ -1,8 +0,0 @@ --- Malformed script -return { - ScenePath = ".", - CommonFolder = "nonexisting", - Modules = { - } -} - diff --git a/tests/ScenegraphLoaderTest/multiple-dependencies/multiple-dependencies.mod b/tests/ScenegraphLoaderTest/multiple-dependencies/multiple-dependencies.mod deleted file mode 100644 index aee4427a42..0000000000 --- a/tests/ScenegraphLoaderTest/multiple-dependencies/multiple-dependencies.mod +++ /dev/null @@ -1,9 +0,0 @@ -return { - { - Name = "MultipleDependencies", - Parent = "NoDependency", - Dependency = { - "DirectDependency" - } - } -} diff --git a/tests/ScenegraphLoaderTest/scene-folder/common-dependency/common-dependency.mod b/tests/ScenegraphLoaderTest/scene-folder/common-dependency/common-dependency.mod deleted file mode 100644 index 313ebbd599..0000000000 --- a/tests/ScenegraphLoaderTest/scene-folder/common-dependency/common-dependency.mod +++ /dev/null @@ -1,6 +0,0 @@ -return { - { - Name = "CommonDependency", - Parent = "Common" - } -} diff --git a/tests/ScenegraphLoaderTest/scene-folder/common/common.mod b/tests/ScenegraphLoaderTest/scene-folder/common/common.mod deleted file mode 100644 index 3e9ef8146d..0000000000 --- a/tests/ScenegraphLoaderTest/scene-folder/common/common.mod +++ /dev/null @@ -1,6 +0,0 @@ -return { - { - Name = "Common", - Parent = "Root", - }, -} \ No newline at end of file diff --git a/tests/ScenegraphLoaderTest/scene-folder/multiple-dependencies/multiple-dependencies.mod b/tests/ScenegraphLoaderTest/scene-folder/multiple-dependencies/multiple-dependencies.mod deleted file mode 100644 index aee4427a42..0000000000 --- a/tests/ScenegraphLoaderTest/scene-folder/multiple-dependencies/multiple-dependencies.mod +++ /dev/null @@ -1,9 +0,0 @@ -return { - { - Name = "MultipleDependencies", - Parent = "NoDependency", - Dependency = { - "DirectDependency" - } - } -} diff --git a/tests/ScenegraphLoaderTest/test00.scene b/tests/ScenegraphLoaderTest/test00.scene deleted file mode 100644 index 3f8a3f419e..0000000000 --- a/tests/ScenegraphLoaderTest/test00.scene +++ /dev/null @@ -1,8 +0,0 @@ --- Loading an empty common and module dictionary -return { - ScenePath = ".", - CommonFolder = "", - Modules = { - } -} - diff --git a/tests/ScenegraphLoaderTest/test01-location.scene b/tests/ScenegraphLoaderTest/test01-location.scene deleted file mode 100644 index ee76285169..0000000000 --- a/tests/ScenegraphLoaderTest/test01-location.scene +++ /dev/null @@ -1,8 +0,0 @@ --- Loading an empty module dictionary in a different folder -return { - ScenePath = "scene-folder", - CommonFolder = "common", - Modules = { - } -} - diff --git a/tests/ScenegraphLoaderTest/test01.scene b/tests/ScenegraphLoaderTest/test01.scene deleted file mode 100644 index 4bcc23b96b..0000000000 --- a/tests/ScenegraphLoaderTest/test01.scene +++ /dev/null @@ -1,8 +0,0 @@ --- Loading an empty module dictionary -return { - ScenePath = ".", - CommonFolder = "common", - Modules = { - } -} - diff --git a/tests/ScenegraphLoaderTest/test03-location.scene b/tests/ScenegraphLoaderTest/test03-location.scene deleted file mode 100644 index b91e9a8cb5..0000000000 --- a/tests/ScenegraphLoaderTest/test03-location.scene +++ /dev/null @@ -1,9 +0,0 @@ --- Loading a module with a dependency on the common module in a different folder -return { - ScenePath = "scene-folder", - CommonFolder = "common", - Modules = { - "common-dependency" - } -} - diff --git a/tests/ScenegraphLoaderTest/test03.scene b/tests/ScenegraphLoaderTest/test03.scene deleted file mode 100644 index 8af305f661..0000000000 --- a/tests/ScenegraphLoaderTest/test03.scene +++ /dev/null @@ -1,9 +0,0 @@ --- Loading a module with a dependency on the common module -return { - ScenePath = ".", - CommonFolder = "common", - Modules = { - "common-dependency" - } -} - diff --git a/tests/ScenegraphLoaderTest/test04.scene b/tests/ScenegraphLoaderTest/test04.scene deleted file mode 100644 index 43acf7474d..0000000000 --- a/tests/ScenegraphLoaderTest/test04.scene +++ /dev/null @@ -1,10 +0,0 @@ --- Loading a module with a direct dependency -return { - ScenePath = ".", - CommonFolder = "common", - Modules = { - "no-dependency", - "direct-dependency" - } -} - diff --git a/tests/ScenegraphLoaderTest/test05-location.scene b/tests/ScenegraphLoaderTest/test05-location.scene deleted file mode 100644 index 2e069d79d3..0000000000 --- a/tests/ScenegraphLoaderTest/test05-location.scene +++ /dev/null @@ -1,11 +0,0 @@ --- Multiple Dependencies in a different folder -return { - ScenePath = "scene-folder", - CommonFolder = "common", - Modules = { - "multiple-dependencies", - "no-dependency", - "direct-dependency" - } -} - diff --git a/tests/ScenegraphLoaderTest/test05.scene b/tests/ScenegraphLoaderTest/test05.scene deleted file mode 100644 index b1605fe364..0000000000 --- a/tests/ScenegraphLoaderTest/test05.scene +++ /dev/null @@ -1,11 +0,0 @@ --- Multiple Dependencies -return { - ScenePath = ".", - CommonFolder = "common", - Modules = { - "multiple-dependencies", - "no-dependency", - "direct-dependency" - } -} - diff --git a/tests/ScenegraphLoaderTest/test06-location.scene b/tests/ScenegraphLoaderTest/test06-location.scene deleted file mode 100644 index f3af92d9de..0000000000 --- a/tests/ScenegraphLoaderTest/test06-location.scene +++ /dev/null @@ -1,9 +0,0 @@ --- Circular Dependencies in a different folder -return { - ScenePath = "scene-folder", - Modules = { - "common", - "circular-dependency" - } -} - diff --git a/tests/ScenegraphLoaderTest/test06.scene b/tests/ScenegraphLoaderTest/test06.scene deleted file mode 100644 index 524a44010e..0000000000 --- a/tests/ScenegraphLoaderTest/test06.scene +++ /dev/null @@ -1,9 +0,0 @@ --- Circular Dependencies -return { - ScenePath = ".", - Modules = { - "common", - "circular-dependency" - } -} - diff --git a/tests/main.cpp b/tests/main.cpp index a0c491f33b..ce493d6203 100644 --- a/tests/main.cpp +++ b/tests/main.cpp @@ -33,7 +33,7 @@ // test files #include #include -#include +#include #ifdef OPENSPACE_MODULE_GLOBEBROWSING_ENABLED //#include diff --git a/tests/test_scenegraphloader.inl b/tests/test_scenegraphloader.inl deleted file mode 100644 index 3561d3018e..0000000000 --- a/tests/test_scenegraphloader.inl +++ /dev/null @@ -1,381 +0,0 @@ -/***************************************************************************************** - * * - * OpenSpace * - * * - * Copyright (c) 2014-2016 * - * * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this * - * software and associated documentation files (the "Software"), to deal in the Software * - * without restriction, including without limitation the rights to use, copy, modify, * - * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * - * permit persons to whom the Software is furnished to do so, subject to the following * - * conditions: * - * * - * The above copyright notice and this permission notice shall be included in all copies * - * or substantial portions of the Software. * - * * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * - * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * - * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * - * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * - ****************************************************************************************/ - -#include "gtest/gtest.h" - -#include - -#include - -class SceneGraphLoaderTest : public testing::Test {}; - -//TEST_F(SceneGraphLoaderTest, NonExistingFileTest) { -// const std::string file = "NonExistingFile"; -// -// std::vector nodes; -// bool success = openspace::SceneGraphLoader::load(file, nodes); -// -// EXPECT_FALSE(success) << "Unsuccessful loading"; -// EXPECT_TRUE(nodes.empty()) << "Empty scenegraph nodes list"; -//} -// -//TEST_F(SceneGraphLoaderTest, IllformedFileTest) { -// const std::string file = absPath("${TESTDIR}/SceneGraphLoaderTest/illformed.scene"); -// -// std::vector nodes; -// bool success = openspace::SceneGraphLoader::load(file, nodes); -// -// EXPECT_FALSE(success) << "Unsuccessful loading"; -// EXPECT_TRUE(nodes.empty()) << "Empty scenegraph nodes list"; -//} -// -//TEST_F(SceneGraphLoaderTest, IllformedFileTestWrongCommonFolderType) { -// const std::string file = absPath("${TESTDIR}/SceneGraphLoaderTest/illformedWrongType.scene"); -// -// std::vector nodes; -// bool success = openspace::SceneGraphLoader::load(file, nodes); -// -// EXPECT_FALSE(success) << "Unsuccessful loading"; -// EXPECT_TRUE(nodes.empty()) << "Empty scenegraph nodes list"; -//} -// -//TEST_F(SceneGraphLoaderTest, IllformedFileTestInvalidSceneFolder) { -// const std::string file = absPath("${TESTDIR}/SceneGraphLoaderTest/illformedInvalidScene.scene"); -// -// std::vector nodes; -// bool success = openspace::SceneGraphLoader::load(file, nodes); -// -// EXPECT_FALSE(success) << "Unsuccessful loading"; -// EXPECT_TRUE(nodes.empty()) << "Empty scenegraph nodes list"; -//} -// -//TEST_F(SceneGraphLoaderTest, IllformedFileTestWrongCommonFolder) { -// const std::string file = absPath("${TESTDIR}/SceneGraphLoaderTest/illformedWrongCommon.scene"); -// -// std::vector nodes; -// bool success = openspace::SceneGraphLoader::load(file, nodes); -// -// EXPECT_FALSE(success) << "Unsuccessful loading"; -// EXPECT_TRUE(nodes.empty()) << "Empty scenegraph nodes list"; -//} -// -//TEST_F(SceneGraphLoaderTest, IllformedFileTestNonExistingCommonFolder) { -// const std::string file = absPath("${TESTDIR}/SceneGraphLoaderTest/illformedNonExistingCommon.scene"); -// -// std::vector nodes; -// bool success = openspace::SceneGraphLoader::load(file, nodes); -// -// EXPECT_FALSE(success) << "Unsuccessful loading"; -// EXPECT_TRUE(nodes.empty()) << "Empty scenegraph nodes list"; -//} -// -//TEST_F(SceneGraphLoaderTest, Test00) { -// const std::string file = absPath("${TESTDIR}/SceneGraphLoaderTest/test00.scene"); -// -// std::vector nodes; -// bool success = openspace::SceneGraphLoader::load(file, nodes); -// -// ASSERT_TRUE(success) << "Successful loading"; -// EXPECT_TRUE(nodes.empty()) << "No scenegraph nodes loaded"; -//} -// -//TEST_F(SceneGraphLoaderTest, Test00Location) { -// const std::string file = absPath("${TESTDIR}/SceneGraphLoaderTest/test00-location.scene"); -// -// std::vector nodes; -// bool success = openspace::SceneGraphLoader::load(file, nodes); -// -// ASSERT_TRUE(success) << "Successful loading"; -// EXPECT_TRUE(nodes.empty()) << "No scenegraph nodes loaded"; -//} -// -//TEST_F(SceneGraphLoaderTest, AbsoluteScenePath) { -// const std::string scenePath = absPath("${TEMPORARY}/tmp.scene"); -// std::ofstream scene(scenePath.c_str()); -// -// scene << "return {" << std::endl << -// " ScenePath = \"" << absPath("${TESTDIR}/SceneGraphLoaderTest/scene-folder") << -// "\"," << std::endl << -// " CommonFolder = \"\"," << std::endl << -// " Modules = {}}" << std::endl; -// scene.close(); -// -// std::vector nodes; -// bool success = openspace::SceneGraphLoader::load(scenePath, nodes); -// -// ASSERT_TRUE(success) << "Successful loading"; -// EXPECT_TRUE(nodes.empty()) << "No scenegraph nodes loaded"; -//} -// -//TEST_F(SceneGraphLoaderTest, Test01) { -// const std::string file = absPath("${TESTDIR}/SceneGraphLoaderTest/test01.scene"); -// -// std::vector nodes; -// bool success = openspace::SceneGraphLoader::load(file, nodes); -// -// ASSERT_TRUE(success) << "Successful loading"; -// ASSERT_TRUE(nodes.size() == 1) << "Correct number of nodes"; -// EXPECT_TRUE(nodes[0]->name() == "Common") << "Correct node loaded"; -//} -// -//TEST_F(SceneGraphLoaderTest, Test01Location) { -// const std::string file = absPath("${TESTDIR}/SceneGraphLoaderTest/test01-location.scene"); -// -// std::vector nodes; -// bool success = openspace::SceneGraphLoader::load(file, nodes); -// -// ASSERT_TRUE(success) << "Successful loading"; -// ASSERT_TRUE(nodes.size() == 1) << "Correct number of nodes"; -// EXPECT_TRUE(nodes[0]->name() == "Common") << "Correct node loaded"; -//} -// -//TEST_F(SceneGraphLoaderTest, Test02) { -// const std::string file = absPath("${TESTDIR}/SceneGraphLoaderTest/test02.scene"); -// -// std::vector nodes; -// bool success = openspace::SceneGraphLoader::load(file, nodes); -// -// ASSERT_TRUE(success) << "Successful loading"; -// ASSERT_TRUE(nodes.size() == 2) << "Correct number of nodes"; -// bool found = false; -// for (openspace::SceneGraphNode* n : nodes) -// if (n->name() == "NoDependency") -// found = true; -// -// EXPECT_TRUE(found) << "Correct node loaded"; -//} -// -//TEST_F(SceneGraphLoaderTest, Test02Location) { -// const std::string file = absPath("${TESTDIR}/SceneGraphLoaderTest/test02-location.scene"); -// -// std::vector nodes; -// bool success = openspace::SceneGraphLoader::load(file, nodes); -// -// ASSERT_TRUE(success) << "Successful loading"; -// ASSERT_TRUE(nodes.size() == 2) << "Correct number of nodes"; -// bool found = false; -// for (openspace::SceneGraphNode* n : nodes) -// if (n->name() == "NoDependency") -// found = true; -// -// EXPECT_TRUE(found) << "Correct node loaded"; -//} -// -//TEST_F(SceneGraphLoaderTest, Test03) { -// const std::string file = absPath("${TESTDIR}/SceneGraphLoaderTest/test03.scene"); -// -// std::vector nodes; -// bool success = openspace::SceneGraphLoader::load(file, nodes); -// -// ASSERT_TRUE(success) << "Successful loading"; -// ASSERT_TRUE(nodes.size() == 2) << "Correct number of nodes"; -// bool found = false; -// for (openspace::SceneGraphNode* n : nodes) -// if (n->name() == "CommonDependency") -// found = true; -// -// EXPECT_TRUE(found) << "Correct node loaded"; -//} -// -//TEST_F(SceneGraphLoaderTest, Test03Location) { -// const std::string file = absPath("${TESTDIR}/SceneGraphLoaderTest/test03-location.scene"); -// -// std::vector nodes; -// bool success = openspace::SceneGraphLoader::load(file, nodes); -// -// ASSERT_TRUE(success) << "Successful loading"; -// ASSERT_TRUE(nodes.size() == 2) << "Correct number of nodes"; -// bool found = false; -// for (openspace::SceneGraphNode* n : nodes) -// if (n->name() == "CommonDependency") -// found = true; -// -// EXPECT_TRUE(found) << "Correct node loaded"; -//} -// -//TEST_F(SceneGraphLoaderTest, Test04) { -// const std::string file = absPath("${TESTDIR}/SceneGraphLoaderTest/test04.scene"); -// -// std::vector nodes; -// bool success = openspace::SceneGraphLoader::load(file, nodes); -// -// ASSERT_TRUE(success) << "Successful loading"; -// ASSERT_TRUE(nodes.size() == 3) << "Correct number of nodes"; -// bool found = false; -// for (openspace::SceneGraphNode* n : nodes) -// if (n->name() == "DirectDependency") -// found = true; -// -// EXPECT_TRUE(found) << "Correct node loaded"; -//} -// -//TEST_F(SceneGraphLoaderTest, Test04Location) { -// const std::string file = absPath("${TESTDIR}/SceneGraphLoaderTest/test04-location.scene"); -// -// std::vector nodes; -// bool success = openspace::SceneGraphLoader::load(file, nodes); -// -// ASSERT_TRUE(success) << "Successful loading"; -// ASSERT_TRUE(nodes.size() == 3) << "Correct number of nodes"; -// bool found = false; -// for (openspace::SceneGraphNode* n : nodes) -// if (n->name() == "DirectDependency") -// found = true; -// -// EXPECT_TRUE(found) << "Correct node loaded"; -//} -// -//TEST_F(SceneGraphLoaderTest, Test05) { -// const std::string file = absPath("${TESTDIR}/SceneGraphLoaderTest/test05.scene"); -// -// std::vector nodes; -// bool success = openspace::SceneGraphLoader::load(file, nodes); -// -// ASSERT_TRUE(success) << "Successful loading"; -// ASSERT_TRUE(nodes.size() == 4) << "Correct number of nodes"; -// bool found = false; -// for (openspace::SceneGraphNode* n : nodes) -// if (n->name() == "MultipleDependencies") -// found = true; -// -// EXPECT_TRUE(found) << "Correct node loaded"; -//} -// -//TEST_F(SceneGraphLoaderTest, Test05Location) { -// const std::string file = absPath("${TESTDIR}/SceneGraphLoaderTest/test05-location.scene"); -// -// std::vector nodes; -// bool success = openspace::SceneGraphLoader::load(file, nodes); -// -// ASSERT_TRUE(success) << "Successful loading"; -// ASSERT_TRUE(nodes.size() == 4) << "Correct number of nodes"; -// bool found = false; -// for (openspace::SceneGraphNode* n : nodes) -// if (n->name() == "MultipleDependencies") -// found = true; -// -// EXPECT_TRUE(found) << "Correct node loaded"; -//} -// -//TEST_F(SceneGraphLoaderTest, Test06) { -// const std::string file = absPath("${TESTDIR}/SceneGraphLoaderTest/test06.scene"); -// -// std::vector nodes; -// bool success = openspace::SceneGraphLoader::load(file, nodes); -// -// ASSERT_FALSE(success) << "Successful loading"; -// ASSERT_TRUE(nodes.empty()) << "Correct number of nodes"; -//} -// -//TEST_F(SceneGraphLoaderTest, Test06Location) { -// const std::string file = absPath("${TESTDIR}/SceneGraphLoaderTest/test06-location.scene"); -// -// std::vector nodes; -// bool success = openspace::SceneGraphLoader::load(file, nodes); -// -// ASSERT_TRUE(success) << "Successful loading"; -// ASSERT_TRUE(nodes.size() == 4) << "Correct number of nodes"; -// bool found = false; -// for (openspace::SceneGraphNode* n : nodes) -// if (n->name() == "MultipleDependencies") -// found = true; -// -// EXPECT_TRUE(found) << "No scenegraph nodes loaded"; -//} -// -//// -//// -//// -////TEST_F(SceneGraphTest, SceneGraphNode) { -//// -//// openspace::SceneGraphNode *node = -//// openspace::SceneGraphNode::createFromDictionary(ghoul::Dictionary()); -//// -//// // Should not have a renderable and position should be 0,0,0,0 (undefined). -//// EXPECT_EQ(nullptr, node->renderable()); -//// EXPECT_EQ(openspace::psc(), node->position()); -//// -//// delete node; -//// ghoul::Dictionary nodeDictionary; -//// -//// ghoul::Dictionary positionDictionary; -//// ghoul::Dictionary positionPositionArrayDictionary; -//// -//// ghoul::Dictionary renderableDictionary; -//// -//// renderableDictionary.setValue("Type", std::string("RenderablePlanet")); -//// -//// positionPositionArrayDictionary.setValue("1", 1.0); -//// positionPositionArrayDictionary.setValue("2", 1.0); -//// positionPositionArrayDictionary.setValue("3", 1.0); -//// positionPositionArrayDictionary.setValue("4", 1.0); -//// -//// positionDictionary.setValue("Type", std::string("Static")); -//// positionDictionary.setValue("Position", positionPositionArrayDictionary); -//// -//// nodeDictionary.setValue("Position", positionDictionary); -//// nodeDictionary.setValue("Renderable", renderableDictionary); -//// -//// node = -//// openspace::SceneGraphNode::createFromDictionary(nodeDictionary); -//// -//// // This node should have a renderable (probably no good values but an existing one) -//// EXPECT_TRUE(node->renderable()); -//// -//// // position should be initialized -//// EXPECT_EQ(openspace::psc(1.0,1.0,1.0,1.0), node->position()); -//// -//// delete node; -////} -//// -////TEST_F(SceneGraphTest, Loading) { -//// -//// -//// // Should not successfully load a non existing scenegraph -//// EXPECT_FALSE(_scenegraph->loadScene(absPath("${TESTDIR}/ScenegraphTestNonExisting"), absPath("${TESTDIR}"))); -//// -//// // Existing scenegraph should load -//// EXPECT_TRUE(_scenegraph->loadScene(absPath("${TESTDIR}/ScenegraphTest"), absPath("${TESTDIR}"))); -//// // TODO need to check for correctness -//// -//// // This loading should fail regardless of existing or not since the -//// // scenegraph is already loaded -//// EXPECT_FALSE(_scenegraph->loadScene(absPath("${TESTDIR}/ScenegraphTest"), absPath("${TESTDIR}"))); -////} -//// -////TEST_F(SceneGraphTest, Reinitializing) { -//// -//// // Existing scenegraph should load -//// EXPECT_TRUE(_scenegraph->loadScene(absPath("${TESTDIR}/ScenegraphTest"), absPath("${TESTDIR}"))); -//// -//// _scenegraph->deinitialize(); -//// -//// // Existing scenegraph should load -//// EXPECT_TRUE(_scenegraph->loadScene(absPath("${TESTDIR}/ScenegraphTest"), absPath("${TESTDIR}"))); -//// // TODO need to check for correctness -////} -// - - - diff --git a/tests/test_sceneloader.inl b/tests/test_sceneloader.inl new file mode 100644 index 0000000000..89cf0ac446 --- /dev/null +++ b/tests/test_sceneloader.inl @@ -0,0 +1,265 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2016 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#include "gtest/gtest.h" + +#include +#include +#include +#include + +#include +#include + + +//class SceneLoaderTest : public testing::Test {}; + +TEST(SceneLoaderTest, NonExistingFileTest) { + const std::string file = absPath("NonExistingFile"); + + openspace::SceneLoader loader; + EXPECT_THROW(loader.loadScene(file), ghoul::FileNotFoundError); +} + +TEST(SceneLoaderTest, IllformedFileTest) { + const std::string file = absPath("${TESTDIR}/SceneLoaderTest/illformed.scene"); + + openspace::SceneLoader loader; + EXPECT_THROW(loader.loadScene(file), ghoul::lua::LuaRuntimeException); +} + + +TEST(SceneLoaderTest, IllformedFileTestInvalidSceneFolder) { + const std::string file = absPath("${TESTDIR}/SceneLoaderTest/illformedInvalidScene.scene"); + + openspace::SceneLoader loader; + EXPECT_THROW(loader.loadScene(file), openspace::documentation::SpecificationError); +} + +TEST(SceneLoaderTest, IllformedFileTestWrongType) { + const std::string file = absPath("${TESTDIR}/SceneLoaderTest/illformedWrongType.scene"); + + openspace::SceneLoader loader; + EXPECT_THROW(loader.loadScene(file), openspace::documentation::SpecificationError); +} + + +TEST(SceneLoaderTest, AbsoluteScenePath) { + const std::string scenePath = absPath("${TEMPORARY}/tmp.scene"); + std::ofstream sceneFile(scenePath.c_str()); + + ghoul::DictionaryLuaFormatter formatter; + ghoul::Dictionary sceneFileDictionary; + ghoul::Dictionary cameraDictionary; + + cameraDictionary.setValue("Position", glm::vec3(0.0)); + cameraDictionary.setValue("Rotation", glm::vec4(0.0)); + cameraDictionary.setValue("Focus", "Root"); + + sceneFileDictionary.setValue("ScenePath", absPath("${TESTDIR}/SceneLoaderTest/scene-folder")); + sceneFileDictionary.setValue("Modules", ghoul::Dictionary()); + sceneFileDictionary.setValue("Camera", cameraDictionary); + + sceneFile << "return " << formatter.format(sceneFileDictionary); + sceneFile.close(); + + openspace::SceneLoader loader; + std::unique_ptr scene = loader.loadScene(scenePath); + ASSERT_NE(scene, nullptr) << "loadScene returned nullptr"; + std::vector nodes = scene->allSceneGraphNodes(); + EXPECT_EQ(nodes.size(), 1) << "Expected scene to consist of one root node"; +} + +TEST(SceneLoaderTest, Test00) { + const std::string file = absPath("${TESTDIR}/SceneLoaderTest/test00.scene"); + + openspace::SceneLoader loader; + std::unique_ptr scene = loader.loadScene(file); + + ASSERT_NE(scene, nullptr) << "loadScene returned nullptr"; + std::vector nodes = scene->allSceneGraphNodes(); + EXPECT_EQ(nodes.size(), 1) << "Expected scene to consist of one root node"; +} + + +TEST(SceneLoaderTest, Test00Location) { + const std::string file = absPath("${TESTDIR}/SceneLoaderTest/test00-location.scene"); + + openspace::SceneLoader loader; + std::unique_ptr scene = loader.loadScene(file); + + ASSERT_NE(scene, nullptr) << "loadScene returned nullptr"; + std::vector nodes = scene->allSceneGraphNodes(); + EXPECT_EQ(nodes.size(), 1) << "Expected scene to consist of one root node"; +} + + +TEST(SceneLoaderTest, Test01) { + const std::string file = absPath("${TESTDIR}/SceneLoaderTest/test01.scene"); + + openspace::SceneLoader loader; + std::unique_ptr scene = loader.loadScene(file); + + ASSERT_NE(scene, nullptr) << "loadScene returned nullptr"; + std::vector nodes = scene->allSceneGraphNodes(); + EXPECT_EQ(nodes.size(), 2) << "Expected scene to consist of two nodes"; + + std::map nodesByName = scene->nodesByName(); + EXPECT_EQ(nodesByName.size(), 2) << "Expected scene to consist of two nodes"; + EXPECT_EQ(nodesByName["Root"]->name(), "Root"); + EXPECT_EQ(nodesByName["NoDependency"]->name(), "NoDependency"); + EXPECT_EQ(nodesByName["NoDependency"]->parent(), nodesByName["Root"]); +} + +TEST(SceneLoaderTest, Test01Location) { + const std::string file = absPath("${TESTDIR}/SceneLoaderTest/test01-location.scene"); + + openspace::SceneLoader loader; + std::unique_ptr scene = loader.loadScene(file); + + ASSERT_NE(scene, nullptr) << "loadScene returned nullptr"; + std::vector nodes = scene->allSceneGraphNodes(); + EXPECT_EQ(nodes.size(), 2) << "Expected scene to consist of two nodes"; + + std::map nodesByName = scene->nodesByName(); + EXPECT_EQ(nodesByName.size(), 2) << "Expected scene to consist of two nodes"; + EXPECT_EQ(nodesByName["Root"]->name(), "Root"); + EXPECT_EQ(nodesByName["NoDependency"]->name(), "NoDependency"); + EXPECT_EQ(nodesByName["NoDependency"]->parent(), nodesByName["Root"]); +} + +TEST(SceneLoaderTest, Test02) { + const std::string file = absPath("${TESTDIR}/SceneLoaderTest/test02.scene"); + + openspace::SceneLoader loader; + std::unique_ptr scene = loader.loadScene(file); + + ASSERT_NE(scene, nullptr) << "loadScene returned nullptr"; + std::vector nodes = scene->allSceneGraphNodes(); + EXPECT_EQ(nodes.size(), 3) << "Expected scene to consist of two nodes"; + + std::map nodesByName = scene->nodesByName(); + EXPECT_EQ(nodesByName.size(), 3) << "Expected scene to consist of two nodes"; + EXPECT_EQ(nodesByName["Root"]->name(), "Root"); + EXPECT_EQ(nodesByName["NoDependency"]->name(), "NoDependency"); + EXPECT_EQ(nodesByName["NoDependency"]->parent(), nodesByName["Root"]); + EXPECT_EQ(nodesByName["Child"]->parent(), nodesByName["NoDependency"]); + + EXPECT_EQ(nodesByName["Root"]->dependencies().size(), 0); + EXPECT_EQ(nodesByName["NoDependency"]->dependencies().size(), 0); + EXPECT_EQ(nodesByName["Child"]->dependencies().size(), 0); +} + +TEST(SceneLoaderTest, Test02Location) { + const std::string file = absPath("${TESTDIR}/SceneLoaderTest/test02-location.scene"); + + openspace::SceneLoader loader; + std::unique_ptr scene = loader.loadScene(file); + + ASSERT_NE(scene, nullptr) << "loadScene returned nullptr"; + std::vector nodes = scene->allSceneGraphNodes(); + EXPECT_EQ(nodes.size(), 3) << "Expected scene to consist of three nodes"; + + std::map nodesByName = scene->nodesByName(); + EXPECT_EQ(nodesByName.size(), 3) << "Expected scene to consist of three nodes"; + EXPECT_EQ(nodesByName["Root"]->name(), "Root"); + EXPECT_EQ(nodesByName["NoDependency"]->name(), "NoDependency"); + EXPECT_EQ(nodesByName["Child"]->name(), "Child"); + + EXPECT_EQ(nodesByName["NoDependency"]->parent(), nodesByName["Root"]); + EXPECT_EQ(nodesByName["Child"]->parent(), nodesByName["NoDependency"]); + + EXPECT_EQ(nodesByName["Root"]->dependencies().size(), 0); + EXPECT_EQ(nodesByName["NoDependency"]->dependencies().size(), 0); + EXPECT_EQ(nodesByName["Child"]->dependencies().size(), 0); +} + +TEST(SceneLoaderTest, Test03) { + const std::string file = absPath("${TESTDIR}/SceneLoaderTest/test03.scene"); + + openspace::SceneLoader loader; + std::unique_ptr scene = loader.loadScene(file); + + ASSERT_NE(scene, nullptr) << "loadScene returned nullptr"; + std::vector nodes = scene->allSceneGraphNodes(); + EXPECT_EQ(nodes.size(), 3) << "Expected scene to consist of three nodes"; + + std::map nodesByName = scene->nodesByName(); + EXPECT_EQ(nodesByName.size(), 3) << "Expected scene to consist of three nodes"; + EXPECT_EQ(nodesByName["Root"]->name(), "Root"); + EXPECT_EQ(nodesByName["NoDependency"]->name(), "NoDependency"); + EXPECT_EQ(nodesByName["Dependent"]->name(), "Dependent"); + + EXPECT_EQ(nodesByName["NoDependency"]->parent(), nodesByName["Root"]); + EXPECT_EQ(nodesByName["Dependent"]->parent(), nodesByName["Root"]); + + EXPECT_EQ(nodesByName["Root"]->dependencies().size(), 0); + EXPECT_EQ(nodesByName["NoDependency"]->dependencies().size(), 0); + EXPECT_EQ(nodesByName["Dependent"]->dependencies().size(), 1); + + EXPECT_EQ(nodesByName["Dependent"]->dependencies()[0], nodesByName["NoDependency"]); +} + +TEST(SceneLoaderTest, Test04) { + const std::string file = absPath("${TESTDIR}/SceneLoaderTest/test04.scene"); + + openspace::SceneLoader loader; + std::unique_ptr scene = loader.loadScene(file); + + ASSERT_NE(scene, nullptr) << "loadScene returned nullptr"; + std::vector nodes = scene->allSceneGraphNodes(); + EXPECT_EQ(nodes.size(), 5) << "Expected scene to consist of five nodes"; + + std::map nodesByName = scene->nodesByName(); + EXPECT_EQ(nodesByName.size(), 5) << "Expected scene to consist of five nodes"; + EXPECT_EQ(nodesByName["Root"]->name(), "Root"); + EXPECT_EQ(nodesByName["NoDependency"]->name(), "NoDependency"); + EXPECT_EQ(nodesByName["Dependent"]->name(), "Dependent"); + EXPECT_EQ(nodesByName["ChildAndDependent"]->name(), "ChildAndDependent"); + + EXPECT_EQ(nodesByName["NoDependency"]->parent(), nodesByName["Root"]); + EXPECT_EQ(nodesByName["Child"]->parent(), nodesByName["NoDependency"]); + EXPECT_EQ(nodesByName["Dependent"]->parent(), nodesByName["Root"]); + EXPECT_EQ(nodesByName["ChildAndDependent"]->parent(), nodesByName["NoDependency"]); + + EXPECT_EQ(nodesByName["Root"]->dependencies().size(), 0); + EXPECT_EQ(nodesByName["NoDependency"]->dependencies().size(), 0); + + EXPECT_EQ(nodesByName["Dependent"]->dependencies().size(), 1); + EXPECT_EQ(nodesByName["Dependent"]->dependencies()[0], nodesByName["NoDependency"]); + + EXPECT_EQ(nodesByName["ChildAndDependent"]->dependencies().size(), 1); + EXPECT_EQ(nodesByName["ChildAndDependent"]->dependencies()[0], nodesByName["Dependent"]); +} + +TEST(SceneLoaderTest, Test05) { + const std::string file = absPath("${TESTDIR}/SceneLoaderTest/test05.scene"); + + openspace::SceneLoader loader; + std::unique_ptr scene = loader.loadScene(file); + std::vector nodes = scene->allSceneGraphNodes(); + + EXPECT_EQ(nodes.size(), 1); + // TODO: Add more tests regarding circular deps. +}