mirror of
https://github.com/OpenSpace/OpenSpace.git
synced 2026-01-23 12:40:01 -06:00
416 lines
15 KiB
C++
416 lines
15 KiB
C++
/*****************************************************************************************
|
|
* *
|
|
* OpenSpace *
|
|
* *
|
|
* Copyright (c) 2014-2015 *
|
|
* *
|
|
* 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 <openspace/scene/scenegraph.h>
|
|
|
|
#include <openspace/engine/openspaceengine.h>
|
|
#include <openspace/scene/scenegraphnode.h>
|
|
#include <openspace/rendering/renderengine.h>
|
|
|
|
#include <ghoul/filesystem/filesystem.h>
|
|
#include <ghoul/logging/logmanager.h>
|
|
#include <ghoul/lua/lua_helper.h>
|
|
|
|
#include <stack>
|
|
|
|
#ifdef _MSC_VER
|
|
#ifdef OPENSPACE_ENABLE_VLD
|
|
#include <vld.h>
|
|
#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
|
|
if (!FileSys.fileExists(absSceneFile, true)) {
|
|
LERROR("Could not load scene file '" << absSceneFile << "'. " <<
|
|
"File not found");
|
|
return false;
|
|
}
|
|
LINFO("Loading SceneGraph from file '" << absSceneFile << "'");
|
|
|
|
// Load dictionary
|
|
ghoul::Dictionary sceneDictionary;
|
|
bool success = ghoul::lua::loadDictionaryFromFile(absSceneFile, sceneDictionary);
|
|
if (!success)
|
|
return false;
|
|
|
|
std::string sceneDescriptionDirectory =
|
|
ghoul::filesystem::File(absSceneFile, true).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;
|
|
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);
|
|
|
|
// Get the common directory
|
|
bool commonFolderSpecified = sceneDictionary.hasKey(KeyCommonFolder);
|
|
bool commonFolderCorrectType = sceneDictionary.hasKeyAndValue<std::string>(KeyCommonFolder);
|
|
|
|
if (commonFolderSpecified) {
|
|
if (commonFolderCorrectType) {
|
|
std::string commonFolder = sceneDictionary.value<std::string>(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);
|
|
size_t nKeys = moduleDictionary.size();
|
|
moduleDictionary.setValue(std::to_string(nKeys + 1), commonFolder);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
LERROR("Specification for 'common' folder has invalid type");
|
|
}
|
|
|
|
std::vector<std::string> keys = moduleDictionary.keys();
|
|
|
|
std::map<std::string, std::vector<std::string>> dependencies;
|
|
std::map<std::string, std::string> 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 moduleName = moduleDictionary.value<std::string>(key);
|
|
std::string modulePath = FileSys.pathByAppendingComponent(sceneDirectory, moduleName);
|
|
|
|
if (!FileSys.directoryExists(modulePath)) {
|
|
LERROR("Could not load module '" << moduleName << "'. Directory did not exist");
|
|
continue;
|
|
}
|
|
|
|
std::string moduleFile = FileSys.pathByAppendingComponent(
|
|
modulePath,
|
|
moduleName + _moduleExtension
|
|
);
|
|
|
|
if (!FileSys.fileExists(moduleFile)) {
|
|
LERROR("Could not load module file '" << moduleFile << "'. File did not exist");
|
|
continue;
|
|
}
|
|
|
|
ghoul::Dictionary moduleDictionary;
|
|
bool s = ghoul::lua::loadDictionaryFromFile(moduleFile, moduleDictionary, state);
|
|
if (!s)
|
|
continue;
|
|
|
|
std::vector<std::string> keys = moduleDictionary.keys();
|
|
for (const std::string& key : keys) {
|
|
if (!moduleDictionary.hasValue<ghoul::Dictionary>(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);
|
|
SceneGraphNode* node = SceneGraphNode::createFromDictionary(element);
|
|
if (node == nullptr) {
|
|
LERROR("Error loading SceneGraphNode '" << nodeName << "' in module '" << moduleName << "'");
|
|
continue;
|
|
//clear();
|
|
//return false;
|
|
}
|
|
|
|
dependencies[nodeName].push_back(parentName);
|
|
parents[nodeName] = parentName;
|
|
// Also include loaded dependencies
|
|
|
|
if (element.hasKey(SceneGraphNode::KeyDependencies)) {
|
|
if (element.hasValue<ghoul::Dictionary>(SceneGraphNode::KeyDependencies)) {
|
|
ghoul::Dictionary nodeDependencies;
|
|
element.getValue(SceneGraphNode::KeyDependencies, nodeDependencies);
|
|
|
|
std::vector<std::string> keys = nodeDependencies.keys();
|
|
for (const std::string& key : keys) {
|
|
std::string value = nodeDependencies.value<std::string>(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);
|
|
}
|
|
}
|
|
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() << "'");
|
|
}
|
|
|
|
node->node->setParent(parentNode);
|
|
}
|
|
|
|
// Setup dependencies
|
|
for (SceneGraphNodeInternal* node : _nodes) {
|
|
std::vector<std::string> 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->node->name() << "'");
|
|
continue;
|
|
}
|
|
node->outgoingEdges.push_back(n);
|
|
n->incomingEdges.push_back(node);
|
|
}
|
|
}
|
|
|
|
std::vector<SceneGraphNodeInternal*> 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<SceneGraphNodeInternal*> zeroInDegreeNodes;
|
|
zeroInDegreeNodes.push(root);
|
|
|
|
std::unordered_map<SceneGraphNodeInternal*, size_t> 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);
|
|
}
|
|
|
|
}
|
|
/*
|
|
RenderEngine::ABufferImplementation i = OsEng.renderEngine()->aBufferImplementation();
|
|
if (i == RenderEngine::ABufferImplementation::FrameBuffer) {
|
|
auto it = std::find_if(
|
|
_topologicalSortedNodes.begin(),
|
|
_topologicalSortedNodes.end(),
|
|
[](SceneGraphNode* node) {
|
|
return node->name() == "Stars";
|
|
}
|
|
);
|
|
SceneGraphNode* n = *it;
|
|
_topologicalSortedNodes.erase(it);
|
|
_topologicalSortedNodes.insert(_topologicalSortedNodes.begin() + 3, n);
|
|
|
|
it = std::find_if(
|
|
_topologicalSortedNodes.begin(),
|
|
_topologicalSortedNodes.end(),
|
|
[](SceneGraphNode* node) {
|
|
return node->name() == "MilkyWay";
|
|
}
|
|
);
|
|
n = *it;
|
|
_topologicalSortedNodes.erase(it);
|
|
_topologicalSortedNodes.insert(_topologicalSortedNodes.begin() + 2, n);
|
|
}
|
|
*/
|
|
|
|
return true;
|
|
}
|
|
|
|
bool SceneGraph::addSceneGraphNode(SceneGraphNode* node) {
|
|
return true;
|
|
}
|
|
|
|
bool SceneGraph::removeSceneGraphNode(SceneGraphNode* node) {
|
|
// How to handle orphaned nodes? (reparent to root?) --- abock
|
|
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<SceneGraphNode*>& 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
|