mirror of
https://github.com/OpenSpace/OpenSpace.git
synced 2026-01-06 19:50:03 -06:00
Move shaders into their correct modules
Add path determination to modules that automatically sets correct path resolve tokens
This commit is contained in:
Submodule ext/ghoul updated: 711fd33d85...f48a0a6420
@@ -34,13 +34,14 @@ public:
|
||||
OpenSpaceModule() = default;
|
||||
virtual ~OpenSpaceModule() = default;
|
||||
|
||||
virtual bool initialize() { return true; }
|
||||
virtual bool deinitialize() { return true; }
|
||||
virtual bool initialize();
|
||||
virtual bool deinitialize();
|
||||
|
||||
std::string name() const { return _name; }
|
||||
std::string name() const;
|
||||
|
||||
protected:
|
||||
void setName(std::string name) { _name = std::move(name); }
|
||||
void setName(std::string name);
|
||||
std::string modulePath() const;
|
||||
|
||||
std::string _name;
|
||||
};
|
||||
|
||||
@@ -40,6 +40,31 @@ set(SOURCE_FILES
|
||||
)
|
||||
source_group("Source Files" FILES ${SOURCE_FILES})
|
||||
|
||||
set(SHADER_FILES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/shaders/constellationbounds_fs.glsl
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/shaders/constellationbounds_vs.glsl
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/shaders/ephemeris_fs.glsl
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/shaders/ephemeris_vs.glsl
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/shaders/fieldline_fs.glsl
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/shaders/fieldline_gs.glsl
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/shaders/fieldline_vs.glsl
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/shaders/imageplane_fs.glsl
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/shaders/imageplane_vs.glsl
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/shaders/model_fs.glsl
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/shaders/model_vs.glsl
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/shaders/path_fs.glsl
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/shaders/path_gs.glsl
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/shaders/path_vs.glsl
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/shaders/plane_fs.glsl
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/shaders/plane_vs.glsl
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/shaders/sphere_fs.glsl
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/shaders/sphere_vs.glsl
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/shaders/star_fs.glsl
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/shaders/star_ge.glsl
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/shaders/star_vs.glsl
|
||||
)
|
||||
source_group("Shader Files" FILES ${SHADER_FILES})
|
||||
|
||||
set(MODULE_CLASS_FILES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/basemodule.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/basemodule.cpp
|
||||
|
||||
@@ -48,9 +48,19 @@
|
||||
#include <modules/base/ephemeris/dynamicephemeris.h>
|
||||
#include <modules/base/ephemeris/spiceephemeris.h>
|
||||
|
||||
#include <ghoul/filesystem/filesystem>
|
||||
|
||||
namespace openspace {
|
||||
|
||||
BaseModule::BaseModule() {
|
||||
setName("Base");
|
||||
}
|
||||
|
||||
bool BaseModule::initialize() {
|
||||
bool success = OpenSpaceModule::initialize();
|
||||
if (!success)
|
||||
return false;
|
||||
|
||||
FactoryManager::ref().addFactory(new ghoul::TemplateFactory<planetgeometry::PlanetGeometry>);
|
||||
FactoryManager::ref().addFactory(new ghoul::TemplateFactory<modelgeometry::ModelGeometry>);
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ namespace openspace {
|
||||
|
||||
class BaseModule : public OpenSpaceModule {
|
||||
public:
|
||||
BaseModule();
|
||||
bool initialize() override;
|
||||
};
|
||||
|
||||
|
||||
@@ -91,8 +91,8 @@ RenderableConstellationBounds::~RenderableConstellationBounds() {
|
||||
|
||||
bool RenderableConstellationBounds::initialize() {
|
||||
_program = ghoul::opengl::ProgramObject::Build("ConstellationBounds",
|
||||
"${SHADERS}/modules/constellationbounds/constellationbounds_vs.glsl",
|
||||
"${SHADERS}/modules/constellationbounds/constellationbounds_fs.glsl");
|
||||
"${MODULE_BASE}/shaders/constellationbounds_vs.glsl",
|
||||
"${MODULE_BASE}/shaders/constellationbounds_fs.glsl");
|
||||
if (!_program)
|
||||
return false;
|
||||
|
||||
|
||||
@@ -128,8 +128,8 @@ bool RenderableModel::initialize() {
|
||||
if (_programObject == nullptr) {
|
||||
// NH shader
|
||||
_programObject = ghoul::opengl::ProgramObject::Build("ModelProgram",
|
||||
"${SHADERS}/modules/model/model_vs.glsl",
|
||||
"${SHADERS}/modules/model/model_fs.glsl");
|
||||
"${MODULE_BASE}/shaders/model_vs.glsl",
|
||||
"${MODULE_BASE}/shaders/model_fs.glsl");
|
||||
if (!_programObject)
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -100,9 +100,8 @@ bool RenderablePath::initialize() {
|
||||
|
||||
bool completeSuccess = true;
|
||||
_programObject = ghoul::opengl::ProgramObject::Build("PathProgram",
|
||||
"${SHADERS}/modules/trails/path_vs.glsl",
|
||||
"${SHADERS}/modules/trails/path_fs.glsl"
|
||||
//,"${SHADERS}/modules/trails/path_gs.glsl"
|
||||
"${MODULE_BASE}/shaders/path_vs.glsl",
|
||||
"${MODULE_BASE}/shaders/path_fs.glsl"
|
||||
);
|
||||
if (!_programObject)
|
||||
return false;
|
||||
@@ -200,45 +199,48 @@ void RenderablePath::update(const UpdateData& data) {
|
||||
}
|
||||
|
||||
void RenderablePath::calculatePath(std::string observer) {
|
||||
|
||||
double interval = (_stop - _start);
|
||||
int segments = static_cast<int>(interval /_increment);
|
||||
double lightTime;
|
||||
bool correctPosition = true;
|
||||
double interval = (_stop - _start);
|
||||
int segments = static_cast<int>(interval /_increment);
|
||||
|
||||
psc pscPos;
|
||||
double currentTime = _start;
|
||||
_vertexArray.resize(segments);
|
||||
if (segments == 0)
|
||||
return;
|
||||
|
||||
//float r, g, b;
|
||||
//float g = _lineColor[1];
|
||||
//float b = _lineColor[2];
|
||||
for (int i = 0; i < segments; i++) {
|
||||
correctPosition = SpiceManager::ref().getTargetPosition(_target, observer, _frame, "NONE", currentTime, pscPos, lightTime);
|
||||
pscPos[3] += 3;
|
||||
double lightTime;
|
||||
bool correctPosition = true;
|
||||
|
||||
psc pscPos;
|
||||
double currentTime = _start;
|
||||
_vertexArray.resize(segments);
|
||||
|
||||
//float r, g, b;
|
||||
//float g = _lineColor[1];
|
||||
//float b = _lineColor[2];
|
||||
for (int i = 0; i < segments; i++) {
|
||||
correctPosition = SpiceManager::ref().getTargetPosition(_target, observer, _frame, "NONE", currentTime, pscPos, lightTime);
|
||||
pscPos[3] += 3;
|
||||
|
||||
//if (!correctPosition) {
|
||||
// r = 1.f;
|
||||
// g = b = 0.5f;
|
||||
//}
|
||||
//else if ((i % 8) == 0) {
|
||||
// r = _lineColor[0];
|
||||
// g = _lineColor[1];
|
||||
// b = _lineColor[2];
|
||||
//}
|
||||
//else {
|
||||
// r = g = b = 0.6f;
|
||||
//}
|
||||
//add position
|
||||
_vertexArray[i] = { pscPos[0], pscPos[1], pscPos[2], pscPos[3] };
|
||||
//add color for position
|
||||
//_vertexArray[i + 1] = { r, g, b, a };
|
||||
currentTime += _increment;
|
||||
}
|
||||
_lastPosition = pscPos.dvec4();
|
||||
//if (!correctPosition) {
|
||||
// r = 1.f;
|
||||
// g = b = 0.5f;
|
||||
//}
|
||||
//else if ((i % 8) == 0) {
|
||||
// r = _lineColor[0];
|
||||
// g = _lineColor[1];
|
||||
// b = _lineColor[2];
|
||||
//}
|
||||
//else {
|
||||
// r = g = b = 0.6f;
|
||||
//}
|
||||
//add position
|
||||
_vertexArray[i] = { pscPos[0], pscPos[1], pscPos[2], pscPos[3] };
|
||||
//add color for position
|
||||
//_vertexArray[i + 1] = { r, g, b, a };
|
||||
currentTime += _increment;
|
||||
}
|
||||
_lastPosition = pscPos.dvec4();
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, _vBufferID);
|
||||
glBufferSubData(GL_ARRAY_BUFFER, 0, _vertexArray.size() * sizeof(VertexInfo), &_vertexArray[0]);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, _vBufferID);
|
||||
glBufferSubData(GL_ARRAY_BUFFER, 0, _vertexArray.size() * sizeof(VertexInfo), &_vertexArray[0]);
|
||||
}
|
||||
|
||||
void RenderablePath::sendToGPU() {
|
||||
|
||||
@@ -128,8 +128,8 @@ bool RenderablePlane::initialize() {
|
||||
if (_shader == nullptr) {
|
||||
// Plane Program
|
||||
_shader = ghoul::opengl::ProgramObject::Build("PlaneProgram",
|
||||
"${SHADERS}/modules/plane/plane_vs.glsl",
|
||||
"${SHADERS}/modules/plane/plane_fs.glsl");
|
||||
"${MODULE_BASE}/shaders/plane_vs.glsl",
|
||||
"${MODULE_BASE}/shaders/plane_fs.glsl");
|
||||
if (!_shader)
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -120,8 +120,8 @@ bool RenderableSphere::initialize() {
|
||||
|
||||
// pscstandard
|
||||
_shader = ghoul::opengl::ProgramObject::Build("Sphere",
|
||||
"${SHADERS}/modules/sphere/sphere_vs.glsl",
|
||||
"${SHADERS}/modules/sphere/sphere_fs.glsl");
|
||||
"${MODULES}/base/shaders/sphere_vs.glsl",
|
||||
"${MODULES}/base/shaders/sphere_fs.glsl");
|
||||
if (!_shader)
|
||||
return false;
|
||||
|
||||
|
||||
@@ -147,9 +147,9 @@ bool RenderableStars::initialize() {
|
||||
bool completeSuccess = true;
|
||||
|
||||
_program = ghoul::opengl::ProgramObject::Build("Star",
|
||||
"${SHADERS}/modules/stars/star_vs.glsl",
|
||||
"${SHADERS}/modules/stars/star_fs.glsl",
|
||||
"${SHADERS}/modules/stars/star_ge.glsl");
|
||||
"${MODULE_BASE}/shaders/star_vs.glsl",
|
||||
"${MODULE_BASE}/shaders/star_fs.glsl",
|
||||
"${MODULE_BASE}/shaders/star_ge.glsl");
|
||||
if (!_program)
|
||||
return false;
|
||||
completeSuccess &= loadData();
|
||||
|
||||
@@ -114,8 +114,8 @@ bool RenderableTrail::initialize() {
|
||||
|
||||
bool completeSuccess = true;
|
||||
_programObject = ghoul::opengl::ProgramObject::Build("EphemerisProgram",
|
||||
"${SHADERS}/modules/trails/ephemeris_vs.glsl",
|
||||
"${SHADERS}/modules/trails/ephemeris_fs.glsl");
|
||||
"${MODULE_BASE}/shaders/ephemeris_vs.glsl",
|
||||
"${MODULE_BASE}/shaders/ephemeris_fs.glsl");
|
||||
if (!_programObject)
|
||||
return false;
|
||||
|
||||
|
||||
@@ -43,7 +43,15 @@
|
||||
|
||||
namespace openspace {
|
||||
|
||||
NewHorizonsModule::NewHorizonsModule() {
|
||||
setName("NewHorizons");
|
||||
}
|
||||
|
||||
bool NewHorizonsModule::initialize() {
|
||||
bool success = OpenSpaceModule::initialize();
|
||||
if (!success)
|
||||
return false;
|
||||
|
||||
FactoryManager::ref().addFactory(new ghoul::TemplateFactory<planetgeometryprojection::PlanetGeometryProjection>);
|
||||
FactoryManager::ref().addFactory(new ghoul::TemplateFactory<Decoder>);
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ namespace openspace {
|
||||
|
||||
class NewHorizonsModule : public OpenSpaceModule {
|
||||
public:
|
||||
NewHorizonsModule();
|
||||
bool initialize() override;
|
||||
};
|
||||
|
||||
|
||||
@@ -77,8 +77,8 @@ bool RenderableCrawlingLine::initialize() {
|
||||
_frameCounter = 0;
|
||||
bool completeSuccess = true;
|
||||
_program = ghoul::opengl::ProgramObject::Build("RenderableCrawlingLine",
|
||||
"${SHADERS}/modules/crawlingline/crawlingline_vs.glsl",
|
||||
"${SHADERS}/modules/crawlingline/crawlingline_fs.glsl"
|
||||
"${MODULE_NEWHORIZONS}/shaders/crawlingline_vs.glsl",
|
||||
"${MODULE_NEWHORIZONS}/shaders/crawlingline_fs.glsl"
|
||||
);
|
||||
if (!_program)
|
||||
return false;
|
||||
|
||||
@@ -145,8 +145,8 @@ bool RenderableFov::initialize() {
|
||||
bool completeSuccess = true;
|
||||
if (_programObject == nullptr) {
|
||||
_programObject = ghoul::opengl::ProgramObject::Build("FovProgram",
|
||||
"${SHADERS}/modules/projection/fov_vs.glsl",
|
||||
"${SHADERS}/modules/projection/fov_fs.glsl");
|
||||
"${MODULE_NEWHORIZONS}/shaders/fov_vs.glsl",
|
||||
"${MODULE_NEWHORIZONS}/shaders/fov_fs.glsl");
|
||||
if (!_programObject)
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -98,8 +98,8 @@ bool RenderablePlaneProjection::initialize() {
|
||||
if (_shader == nullptr) {
|
||||
// Image Plane Program
|
||||
_shader = ghoul::opengl::ProgramObject::Build("ImagePlaneProgram",
|
||||
"${SHADERS}/modules/imageplane/imageplane_vs.glsl",
|
||||
"${SHADERS}/modules/imageplane/imageplane_fs.glsl");
|
||||
"${MODULE_BASE}/shaders/imageplane_vs.glsl",
|
||||
"${MODULE_BASE}/shaders/imageplane_fs.glsl");
|
||||
if (!_shader) return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -212,8 +212,8 @@ bool RenderablePlanetProjection::initialize() {
|
||||
if (_programObject == nullptr) {
|
||||
// projection program
|
||||
_programObject = ghoul::opengl::ProgramObject::Build("projectiveProgram",
|
||||
"${SHADERS}/modules/projection/projectiveTexture_vs.glsl",
|
||||
"${SHADERS}/modules/projection/projectiveTexture_fs.glsl");
|
||||
"${MODULES}/newhorizons/shaders/projectiveTexture_vs.glsl",
|
||||
"${MODULES}/newhorizons/shaders/projectiveTexture_fs.glsl");
|
||||
if (!_programObject)
|
||||
return false;
|
||||
}
|
||||
@@ -289,7 +289,7 @@ bool RenderablePlanetProjection::deinitialize(){
|
||||
return true;
|
||||
}
|
||||
bool RenderablePlanetProjection::isReady() const {
|
||||
return (_geometry != nullptr);
|
||||
return _geometry && _programObject;
|
||||
}
|
||||
|
||||
void RenderablePlanetProjection::imageProjectGPU(){
|
||||
|
||||
@@ -33,7 +33,14 @@
|
||||
|
||||
namespace openspace {
|
||||
|
||||
VolumeModule::VolumeModule() {
|
||||
setName("Volume");
|
||||
}
|
||||
|
||||
bool VolumeModule::initialize() {
|
||||
bool success = OpenSpaceModule::initialize();
|
||||
if (!success)
|
||||
return false;
|
||||
|
||||
auto fRenderable = FactoryManager::ref().factory<Renderable>();
|
||||
ghoul_assert(fRenderable, "No renderable factory existed");
|
||||
|
||||
@@ -31,6 +31,7 @@ namespace openspace {
|
||||
|
||||
class VolumeModule : public OpenSpaceModule {
|
||||
public:
|
||||
VolumeModule();
|
||||
bool initialize() override;
|
||||
};
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ return {
|
||||
SHADERS = "${BASE_PATH}/shaders",
|
||||
SHADERS_GENERATED = "${SHADERS}/generated",
|
||||
OPENSPACE_DATA = "${BASE_PATH}/openspace-data",
|
||||
MODULES = "${BASE_PATH}/modules",
|
||||
TESTDIR = "${BASE_PATH}/tests",
|
||||
CONFIG = "${BASE_PATH}/config",
|
||||
CACHE = "${BASE_PATH}/cache",
|
||||
|
||||
@@ -191,6 +191,8 @@ int ABuffer::addSamplerfile(const std::string& filename) {
|
||||
}
|
||||
|
||||
bool ABuffer::updateShader() {
|
||||
if (_resolveShader == nullptr)
|
||||
return false;
|
||||
bool s = _resolveShader->rebuildFromFile();
|
||||
if (s) {
|
||||
int startAt = 0;
|
||||
|
||||
@@ -110,9 +110,6 @@ OpenSpaceEngine::OpenSpaceEngine(std::string programName)
|
||||
SpiceManager::initialize();
|
||||
Time::initialize();
|
||||
ghoul::systemcapabilities::SystemCapabilities::initialize();
|
||||
|
||||
// Register modules
|
||||
_moduleEngine->initialize();
|
||||
}
|
||||
|
||||
OpenSpaceEngine::~OpenSpaceEngine() {
|
||||
@@ -226,6 +223,9 @@ bool OpenSpaceEngine::create(
|
||||
}
|
||||
}
|
||||
|
||||
// Register modules
|
||||
_engine->_moduleEngine->initialize();
|
||||
|
||||
// Create the cachemanager
|
||||
FileSys.createCacheManager(absPath("${" + ConfigurationManager::KeyCache + "}"), CacheVersion);
|
||||
_engine->_console->initialize();
|
||||
|
||||
@@ -198,6 +198,8 @@ void GUI::initialize() {
|
||||
void GUI::initializeGL() {
|
||||
_program = ghoul::opengl::ProgramObject::Build("GUI",
|
||||
"${SHADERS}/gui_vs.glsl", "${SHADERS}/gui_fs.glsl");
|
||||
if (!_program)
|
||||
return;
|
||||
|
||||
positionLocation = glGetAttribLocation(*_program, "in_position");
|
||||
uvLocation = glGetAttribLocation(*_program, "in_uv");
|
||||
|
||||
83
src/util/openspacemodule.cpp
Normal file
83
src/util/openspacemodule.cpp
Normal file
@@ -0,0 +1,83 @@
|
||||
/*****************************************************************************************
|
||||
* *
|
||||
* 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/util/openspacemodule.h>
|
||||
|
||||
#include <ghoul/filesystem/filesystem>
|
||||
#include <ghoul/logging/logmanager.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace {
|
||||
const std::string _loggerCat = "OpenSpaceModule";
|
||||
const std::string ModuleBaseToken = "MODULE_";
|
||||
}
|
||||
//ghoul::filesystem::FileSystem::TokenOpeningBraces
|
||||
//ghoul::filesystem::FileSystem::TokenClosingBraces
|
||||
namespace openspace {
|
||||
|
||||
bool OpenSpaceModule::initialize() {
|
||||
ghoul_assert(!(name().empty()), "Module name must be set before initialize call");
|
||||
std::string moduleNameUpper = name();
|
||||
std::transform(moduleNameUpper.begin(), moduleNameUpper.end(), moduleNameUpper.begin(), toupper);
|
||||
std::string moduleToken =
|
||||
ghoul::filesystem::FileSystem::TokenOpeningBraces +
|
||||
ModuleBaseToken +
|
||||
moduleNameUpper +
|
||||
ghoul::filesystem::FileSystem::TokenClosingBraces;
|
||||
|
||||
std::string path = modulePath();
|
||||
LDEBUG("Registering module path: " << moduleToken << ": " << path);
|
||||
FileSys.registerPathToken(moduleToken, path);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenSpaceModule::deinitialize() {
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string OpenSpaceModule::name() const {
|
||||
return _name;
|
||||
}
|
||||
|
||||
void OpenSpaceModule::setName(std::string name) {
|
||||
_name = std::move(name);
|
||||
}
|
||||
|
||||
std::string OpenSpaceModule::modulePath() const {
|
||||
std::string moduleName = name();
|
||||
std::transform(moduleName.begin(), moduleName.end(), moduleName.begin(), tolower);
|
||||
|
||||
if (FileSys.directoryExists("${MODULES}/" + moduleName))
|
||||
return absPath("${MODULES}/" + moduleName);
|
||||
|
||||
#ifdef EXTERNAL_MODULES_PATHS
|
||||
|
||||
#endif
|
||||
LERROR("Could not resolve path for module '" << name() << "'");
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
} // namespace openspace
|
||||
Reference in New Issue
Block a user