diff --git a/apps/OpenSpace/ext/launcher/src/launcherwindow.cpp b/apps/OpenSpace/ext/launcher/src/launcherwindow.cpp index 631c1bb27b..7c9f2b0a27 100644 --- a/apps/OpenSpace/ext/launcher/src/launcherwindow.cpp +++ b/apps/OpenSpace/ext/launcher/src/launcherwindow.cpp @@ -141,12 +141,14 @@ LauncherWindow::LauncherWindow(bool profileEnabled, bool sgctConfigEnabled, std::string sgctConfigName, QWidget* parent) : QMainWindow(parent) - , _assetPath(absPath(globalConfig.pathTokens.at("ASSETS")) + '/') - , _userAssetPath(absPath(globalConfig.pathTokens.at("USER_ASSETS")) + '/') - , _configPath(absPath(globalConfig.pathTokens.at("CONFIG")) + '/') - , _userConfigPath(absPath(globalConfig.pathTokens.at("USER_CONFIG")) + '/') - , _profilePath(absPath(globalConfig.pathTokens.at("PROFILES")) + '/') - , _userProfilePath(absPath(globalConfig.pathTokens.at("USER_PROFILES")) + '/') + , _assetPath(absPath(globalConfig.pathTokens.at("ASSETS")).string() + '/') + , _userAssetPath(absPath(globalConfig.pathTokens.at("USER_ASSETS")).string() + '/') + , _configPath(absPath(globalConfig.pathTokens.at("CONFIG")).string() + '/') + , _userConfigPath(absPath(globalConfig.pathTokens.at("USER_CONFIG")).string() + '/') + , _profilePath(absPath(globalConfig.pathTokens.at("PROFILES")).string() + '/') + , _userProfilePath( + absPath(globalConfig.pathTokens.at("USER_PROFILES")).string() + '/' + ) , _readOnlyProfiles(globalConfig.readOnlyProfiles) { Q_INIT_RESOURCE(resources); @@ -179,10 +181,12 @@ LauncherWindow::LauncherWindow(bool profileEnabled, _windowConfigBox->setEnabled(sgctConfigEnabled); - std::string p = absPath(globalConfig.pathTokens.at("SYNC") + "/http/launcher_images"); + std::filesystem::path p = absPath( + globalConfig.pathTokens.at("SYNC") + "/http/launcher_images" + ); if (std::filesystem::exists(p)) { try { - setBackgroundImage(p); + setBackgroundImage(p.string()); } catch (const std::exception& e) { std::cerr << "Error occurrred while reading background images: " << e.what(); diff --git a/apps/OpenSpace/ext/launcher/src/profile/scriptlogdialog.cpp b/apps/OpenSpace/ext/launcher/src/profile/scriptlogdialog.cpp index c82fd8aa57..906a855c13 100644 --- a/apps/OpenSpace/ext/launcher/src/profile/scriptlogdialog.cpp +++ b/apps/OpenSpace/ext/launcher/src/profile/scriptlogdialog.cpp @@ -39,7 +39,7 @@ ScriptlogDialog::ScriptlogDialog(QWidget* parent) setWindowTitle("Scriptlog"); createWidgets(); - QFile file(QString::fromStdString(absPath("${LOGS}/scriptLog.txt"))); + QFile file(QString::fromStdString(absPath("${LOGS}/scriptLog.txt").string())); if (file.open(QIODevice::ReadOnly | QIODevice::Text)) { QTextStream in(&file); while (!in.atEnd()) { diff --git a/apps/OpenSpace/ext/sgct b/apps/OpenSpace/ext/sgct index 669fbc16a9..2a3ef78f72 160000 --- a/apps/OpenSpace/ext/sgct +++ b/apps/OpenSpace/ext/sgct @@ -1 +1 @@ -Subproject commit 669fbc16a9910b28333427f5e07235baa43ea7a6 +Subproject commit 2a3ef78f721e919531bb05b0cedab2dabe2bb0be diff --git a/apps/OpenSpace/main.cpp b/apps/OpenSpace/main.cpp index 9adc728a07..b4a2d5bc6f 100644 --- a/apps/OpenSpace/main.cpp +++ b/apps/OpenSpace/main.cpp @@ -61,6 +61,7 @@ #include #include #include +#include #include #ifdef WIN32 @@ -223,9 +224,9 @@ void mainInitFunc(GLFWwindow*) { // // We save the startup value of the screenshots just in case we want to add a date // to them later in the RenderEngine - std::string screenshotPath = absPath("${SCREENSHOTS}"); + std::filesystem::path screenshotPath = absPath("${SCREENSHOTS}"); FileSys.registerPathToken("${STARTUP_SCREENSHOT}", screenshotPath); - Settings::instance().setCapturePath(screenshotPath); + Settings::instance().setCapturePath(screenshotPath.string()); LDEBUG("Initializing OpenSpace Engine started"); global::openSpaceEngine->initialize(); @@ -234,11 +235,11 @@ void mainInitFunc(GLFWwindow*) { #ifndef __APPLE__ // Apparently: "Cocoa: Regular windows do not have icons on macOS" { - std::string path = absPath("${DATA}/openspace-icon.png"); + std::filesystem::path path = absPath("${DATA}/openspace-icon.png"); int x; int y; int n; - unsigned char* data = stbi_load(path.c_str(), &x, &y, &n, 0); + unsigned char* data = stbi_load(path.string().c_str(), &x, &y, &n, 0); GLFWimage icons[1]; icons[0].pixels = data; @@ -1039,7 +1040,7 @@ int main(int argc, char* argv[]) { // to make it possible to find other files in the same directory. FileSys.registerPathToken( "${BIN}", - ghoul::filesystem::File(absPath(argv[0])).directoryName(), + std::filesystem::path(argv[0]).parent_path(), ghoul::filesystem::FileSystem::Override::Yes ); @@ -1092,28 +1093,32 @@ int main(int argc, char* argv[]) { std::string windowConfiguration; try { // Find configuration - std::string configurationFilePath = commandlineArguments.configurationName; - if (commandlineArguments.configurationName.empty()) { + std::filesystem::path configurationFilePath; + if (!commandlineArguments.configurationName.empty()) { + configurationFilePath = absPath(commandlineArguments.configurationName); + } + else { LDEBUG("Finding configuration"); configurationFilePath = configuration::findConfiguration(); } - configurationFilePath = absPath(configurationFilePath); - if (!FileSys.fileExists(configurationFilePath)) { - LFATALC("main", "Could not find configuration: " + configurationFilePath); + if (!std::filesystem::is_regular_file(configurationFilePath)) { + LFATALC( + "main", + fmt::format("Could not find configuration {}", configurationFilePath) + ); exit(EXIT_FAILURE); } - LINFO(fmt::format("Configuration Path: '{}'", configurationFilePath)); + LINFO(fmt::format("Configuration Path: {}", configurationFilePath)); // Register the base path as the directory where the configuration file lives - std::string base = ghoul::filesystem::File(configurationFilePath).directoryName(); - constexpr const char* BasePathToken = "${BASE}"; - FileSys.registerPathToken(BasePathToken, base); + std::filesystem::path base = configurationFilePath.parent_path(); + FileSys.registerPathToken("${BASE}", base); // Loading configuration from disk LDEBUG("Loading configuration from disk"); *global::configuration = configuration::loadConfigurationFromFile( - configurationFilePath, + configurationFilePath.string(), commandlineArguments.configurationOverride ); @@ -1217,7 +1222,7 @@ int main(int argc, char* argv[]) { // as well as the configuration file that sgct is supposed to use arguments.insert(arguments.begin(), argv[0]); arguments.insert(arguments.begin() + 1, "-config"); - arguments.insert(arguments.begin() + 2, absPath(windowConfiguration)); + arguments.insert(arguments.begin() + 2, absPath(windowConfiguration).string()); // Need to set this before the creation of the sgct::Engine @@ -1233,7 +1238,7 @@ int main(int argc, char* argv[]) { LDEBUG("Creating SGCT Engine"); std::vector arg(argv + 1, argv + argc); Configuration config = parseArguments(arg); - config::Cluster cluster = loadCluster(absPath(windowConfiguration)); + config::Cluster cluster = loadCluster(absPath(windowConfiguration).string()); Engine::Callbacks callbacks; callbacks.initOpenGL = mainInitFunc; diff --git a/apps/TaskRunner/main.cpp b/apps/TaskRunner/main.cpp index 7e038fe34d..1bd16c6394 100644 --- a/apps/TaskRunner/main.cpp +++ b/apps/TaskRunner/main.cpp @@ -32,7 +32,6 @@ #include #include #include -#include #include #include #include @@ -117,18 +116,21 @@ int main(int argc, char** argv) { // to make it possible to find other files in the same directory. FileSys.registerPathToken( "${BIN}", - ghoul::filesystem::File(absPath(argv[0])).directoryName(), + std::filesystem::path(argv[0]).parent_path(), ghoul::filesystem::FileSystem::Override::Yes ); - std::string configFile = configuration::findConfiguration(); + std::filesystem::path configFile = configuration::findConfiguration(); // Register the base path as the directory where the configuration file lives - std::string base = ghoul::filesystem::File(configFile).directoryName(); + std::filesystem::path base = configFile.parent_path(); constexpr const char* BasePathToken = "${BASE}"; FileSys.registerPathToken(BasePathToken, base); - *global::configuration = configuration::loadConfigurationFromFile(configFile, ""); + *global::configuration = configuration::loadConfigurationFromFile( + configFile.string(), + "" + ); openspace::global::openSpaceEngine->registerPathTokens(); global::openSpaceEngine->initialize(); @@ -160,7 +162,7 @@ int main(int argc, char** argv) { // If no task file was specified in as argument, run in CLI mode. LINFO(fmt::format("Task root: {}", absPath("${TASKS}"))); - FileSys.setCurrentDirectory(ghoul::filesystem::Directory(absPath("${TASKS}"))); + std::filesystem::current_path(absPath("${TASKS}")); std::cout << "TASK > "; while (std::cin >> tasksPath) { diff --git a/data/assets/examples/discs.asset b/data/assets/examples/discs.asset index 5dcdbc8924..9bb583e96d 100644 --- a/data/assets/examples/discs.asset +++ b/data/assets/examples/discs.asset @@ -1,8 +1,8 @@ local assetHelper = asset.require('util/asset_helper') -- @TODO (emmbr 2020-02-03) Potential threading issue later on? This will run on the main thread -local cyanTexture = openspace.createSingeColorImage("example_disc_color1", {0.0, 1.0, 1.0}) -local purpleTexture = openspace.createSingeColorImage("example_disc_color2", {0.5, 0.0, 0.5}) +local cyanTexture = openspace.createSingleColorImage("example_disc_color1", {0.0, 1.0, 1.0}) +local purpleTexture = openspace.createSingleColorImage("example_disc_color2", {0.5, 0.0, 0.5}) local BasicDisc = { Identifier = "BasicDisc", diff --git a/data/assets/util/add_marker.asset b/data/assets/util/add_marker.asset new file mode 100644 index 0000000000..1d62a2f00c --- /dev/null +++ b/data/assets/util/add_marker.asset @@ -0,0 +1,19 @@ +local icons = asset.syncedResource({ + Name = "Icons", + Type = "HttpSynchronization", + Identifier = "icons", + Version = 1 +}) + +asset.onInitialize(function() + openspace.addScreenSpaceRenderable({ + Identifier = "target-marker", + Name = "Target Marker", + Type = "ScreenSpaceImageLocal", + TexturePath = icons .. '/target.png' + }) +end) + +asset.onDeinitialize(function() + openspace.removeScreenSpaceRenderable('target-marker'); +end) diff --git a/ext/ghoul b/ext/ghoul index 3c0d660924..d85c1597e4 160000 --- a/ext/ghoul +++ b/ext/ghoul @@ -1 +1 @@ -Subproject commit 3c0d660924ea66ab65703cba7f794193c3c17d67 +Subproject commit d85c1597e436898b67a6ebc9f98885032bec44bb diff --git a/include/openspace/engine/configuration.h b/include/openspace/engine/configuration.h index 98fbb8518c..83970d9fdf 100644 --- a/include/openspace/engine/configuration.h +++ b/include/openspace/engine/configuration.h @@ -27,6 +27,7 @@ #include #include +#include #include #include #include @@ -85,7 +86,7 @@ struct Configuration { bool shouldUseScreenshotDate = false; std::string onScreenTextScaling = "window"; - bool usePerSceneCache = false; + bool usePerProfileCache = false; bool isRenderingOnMasterDisabled = false; glm::dvec3 globalRotation = glm::dvec3(0.0); @@ -129,9 +130,9 @@ struct Configuration { ghoul::lua::LuaState state; }; -std::string findConfiguration(const std::string& filename = "openspace.cfg"); +std::filesystem::path findConfiguration(const std::string& filename = "openspace.cfg"); -Configuration loadConfigurationFromFile(const std::string& filename, +Configuration loadConfigurationFromFile(const std::filesystem::path& filename, const std::string& overrideScript); } // namespace openspace::configuration diff --git a/include/openspace/engine/downloadmanager.h b/include/openspace/engine/downloadmanager.h index 26b0fa21f6..1bfb97a229 100644 --- a/include/openspace/engine/downloadmanager.h +++ b/include/openspace/engine/downloadmanager.h @@ -26,6 +26,7 @@ #define __OPENSPACE_CORE___DOWNLOADMANAGER___H__ #include +#include #include #include #include @@ -99,7 +100,7 @@ public: // finishedCallback - callback when download finished (happens on different thread) // progressCallback - callback for status during (happens on different thread) std::shared_ptr downloadFile(const std::string& url, - const ghoul::filesystem::File& file, + const std::filesystem::path& file, OverrideFile overrideFile = OverrideFile::Yes, FailOnError failOnError = FailOnError::No, unsigned int timeout_secs = 0, DownloadFinishedCallback finishedCallback = DownloadFinishedCallback(), diff --git a/include/openspace/interaction/tasks/convertrecfileversiontask.h b/include/openspace/interaction/tasks/convertrecfileversiontask.h index a707146095..8c25c18990 100644 --- a/include/openspace/interaction/tasks/convertrecfileversiontask.h +++ b/include/openspace/interaction/tasks/convertrecfileversiontask.h @@ -29,11 +29,9 @@ #include #include - +#include #include - - namespace openspace::interaction { class ConvertRecFileVersionTask : public Task { @@ -48,7 +46,7 @@ public: private: std::string _inFilename; - std::string _inFilePath; + std::filesystem::path _inFilePath; std::string _valueFunctionLua; }; diff --git a/include/openspace/interaction/tasks/convertrecformattask.h b/include/openspace/interaction/tasks/convertrecformattask.h index 9258d70406..989dd0bd6c 100644 --- a/include/openspace/interaction/tasks/convertrecformattask.h +++ b/include/openspace/interaction/tasks/convertrecformattask.h @@ -29,7 +29,7 @@ #include #include - +#include #include namespace openspace::interaction { @@ -52,8 +52,8 @@ private: void convertToBinary(); void determineFormatType(); std::string addFileSuffix(const std::string& filePath, const std::string& suffix); - std::string _inFilePath; - std::string _outFilePath; + std::filesystem::path _inFilePath; + std::filesystem::path _outFilePath; std::ifstream _iFile; std::ofstream _oFile; SessionRecording::DataMode _fileFormatType; diff --git a/include/openspace/rendering/renderengine.h b/include/openspace/rendering/renderengine.h index f698af2248..cc6698f165 100644 --- a/include/openspace/rendering/renderengine.h +++ b/include/openspace/rendering/renderengine.h @@ -34,6 +34,7 @@ #include #include #include +#include namespace ghoul { namespace fontrendering { class Font; } @@ -109,12 +110,13 @@ public: std::vector screenSpaceRenderables() const; std::unique_ptr buildRenderProgram( - const std::string& name, const std::string& vsPath, std::string fsPath, - ghoul::Dictionary data = ghoul::Dictionary()); + const std::string& name, const std::filesystem::path& vsPath, + std::filesystem::path fsPath, ghoul::Dictionary data = ghoul::Dictionary()); std::unique_ptr buildRenderProgram( - const std::string& name, const std::string& vsPath, std::string fsPath, - const std::string& csPath, ghoul::Dictionary data = ghoul::Dictionary()); + const std::string& name, const std::filesystem::path& vsPath, + std::filesystem::path fsPath, const std::filesystem::path& csPath, + ghoul::Dictionary data = ghoul::Dictionary()); void removeRenderProgram(ghoul::opengl::ProgramObject* program); diff --git a/include/openspace/rendering/screenspacerenderable.h b/include/openspace/rendering/screenspacerenderable.h index 189ae16d12..bffec324c4 100644 --- a/include/openspace/rendering/screenspacerenderable.h +++ b/include/openspace/rendering/screenspacerenderable.h @@ -104,11 +104,12 @@ protected: properties::Vec3Property _localRotation; properties::FloatProperty _scale; + properties::Vec3Property _multiplyColor; properties::FloatProperty _opacity; properties::TriggerProperty _delete; glm::ivec2 _objectSize = glm::ivec2(0); - UniformCache(alpha, modelTransform, viewProj, texture) _uniformCache; + UniformCache(color, alpha, modelTransform, viewProj, texture) _uniformCache; std::unique_ptr _shader; }; diff --git a/include/openspace/rendering/texturecomponent.h b/include/openspace/rendering/texturecomponent.h index 5c4d905a7c..1940e05fac 100644 --- a/include/openspace/rendering/texturecomponent.h +++ b/include/openspace/rendering/texturecomponent.h @@ -26,6 +26,7 @@ #define __OPENSPACE_CORE___TEXTURECOMPONENT___H__ #include +#include namespace ghoul::filesystem { class File; } namespace ghoul::opengl {class Texture; } @@ -48,7 +49,7 @@ public: void uploadToGpu(); // Loads a texture from a file on disk - void loadFromFile(const std::string& path); + void loadFromFile(const std::filesystem::path& path); // Function to call in a renderable's update function to make sure // the texture is kept up to date diff --git a/include/openspace/rendering/transferfunction.h b/include/openspace/rendering/transferfunction.h index cdad470af7..cca8ef5dc0 100644 --- a/include/openspace/rendering/transferfunction.h +++ b/include/openspace/rendering/transferfunction.h @@ -26,6 +26,7 @@ #define __OPENSPACE_CORE___TRANSFERFUNCTION___H__ #include +#include #include #include #include @@ -58,7 +59,7 @@ private: void setTextureFromImage(); void uploadTexture(); - std::string _filepath; + std::filesystem::path _filepath; std::unique_ptr _file; std::shared_ptr _texture; bool _needsUpdate = false; diff --git a/include/openspace/scene/assetloader.h b/include/openspace/scene/assetloader.h index 779e98e892..141dc5aa2c 100644 --- a/include/openspace/scene/assetloader.h +++ b/include/openspace/scene/assetloader.h @@ -26,6 +26,7 @@ #define __OPENSPACE_CORE___ASSETLOADER___H__ #include +#include #include #include #include @@ -169,7 +170,7 @@ private: void tearDownAssetLuaTable(Asset* asset); std::shared_ptr getAsset(const std::string& name); - ghoul::filesystem::Directory currentDirectory() const; + std::filesystem::path currentDirectory() const; void setCurrentAsset(Asset* asset); void addLuaDependencyTable(Asset* dependant, Asset* dependency); diff --git a/include/openspace/scripting/lualibrary.h b/include/openspace/scripting/lualibrary.h index 1c123f6014..698da1ca6a 100644 --- a/include/openspace/scripting/lualibrary.h +++ b/include/openspace/scripting/lualibrary.h @@ -26,6 +26,7 @@ #define __OPENSPACE_CORE___LUALIBRARY___H__ #include +#include #include #include @@ -58,7 +59,7 @@ struct LuaLibrary { /// The list of all C-based callback functions for this library std::vector functions; /// A list of script files that are executed for each Lua state - std::vector scripts = std::vector(); + std::vector scripts = std::vector(); /// This struct contains information about a function or constant that is defined in /// a Lua script diff --git a/modules/atmosphere/rendering/atmospheredeferredcaster.cpp b/modules/atmosphere/rendering/atmospheredeferredcaster.cpp index 0a27be4596..59a664be99 100644 --- a/modules/atmosphere/rendering/atmospheredeferredcaster.cpp +++ b/modules/atmosphere/rendering/atmospheredeferredcaster.cpp @@ -113,7 +113,7 @@ namespace { constexpr const char* GlslDeferredcastVsPath = "${MODULES}/atmosphere/shaders/atmosphere_deferred_vs.glsl"; - constexpr const float ATM_EPS = 2.f; + constexpr const float ATM_EPS = 2000.f; constexpr const float KM_TO_M = 1000.f; @@ -213,7 +213,7 @@ void AtmosphereDeferredcaster::preRaycast(const RenderData& renderData, renderData.camera.sgctInternal.projectionMatrix() ) * renderData.camera.combinedViewMatrix(); - const float totalAtmosphere = (_atmosphereRadius + ATM_EPS)* KM_TO_M; + const double totalAtmosphere = (scaledRadius + ATM_EPS); if (!isAtmosphereInFrustum(MV, tPlanetPosWorld, totalAtmosphere)) { program.setUniform(_uniformCache.cullAtmosphere, 1); } diff --git a/modules/base/rendering/renderabledisc.cpp b/modules/base/rendering/renderabledisc.cpp index ca17160453..f00b2134dc 100644 --- a/modules/base/rendering/renderabledisc.cpp +++ b/modules/base/rendering/renderabledisc.cpp @@ -96,7 +96,7 @@ RenderableDisc::RenderableDisc(const ghoul::Dictionary& dictionary) const Parameters p = codegen::bake(dictionary); _texturePath = p.texture.string(); - _texturePath.onChange([&]() { _texture->loadFromFile(_texturePath); }); + _texturePath.onChange([&]() { _texture->loadFromFile(_texturePath.value()); }); addProperty(_texturePath); _size.setViewOption(properties::Property::ViewOptions::Logarithmic); @@ -129,7 +129,7 @@ void RenderableDisc::initialize() { void RenderableDisc::initializeGL() { initializeShader(); - _texture->loadFromFile(_texturePath); + _texture->loadFromFile(_texturePath.value()); _texture->uploadToGpu(); _plane->initialize(); diff --git a/modules/base/rendering/renderablelabels.h b/modules/base/rendering/renderablelabels.h index a1cad2dd6d..cdc29dad88 100644 --- a/modules/base/rendering/renderablelabels.h +++ b/modules/base/rendering/renderablelabels.h @@ -123,7 +123,6 @@ private: std::shared_ptr _font; - std::string _speckFile; std::string _colorMapFile; std::string _labelFile; std::string _colorOptionString; diff --git a/modules/base/rendering/renderablemodel.cpp b/modules/base/rendering/renderablemodel.cpp index f7003a17cb..48ee1f6dc8 100644 --- a/modules/base/rendering/renderablemodel.cpp +++ b/modules/base/rendering/renderablemodel.cpp @@ -285,7 +285,7 @@ RenderableModel::RenderableModel(const ghoul::Dictionary& dictionary) } } - std::string file = absPath(p.geometryFile.string()); + std::string file = absPath(p.geometryFile.string()).string(); _geometry = ghoul::io::ModelReader::ref().loadModel( file, ghoul::io::ModelReader::ForceRenderInvisible(_forceRenderInvisible), diff --git a/modules/base/rendering/renderableplane.cpp b/modules/base/rendering/renderableplane.cpp index 1c98a72f31..dab62cd6d4 100644 --- a/modules/base/rendering/renderableplane.cpp +++ b/modules/base/rendering/renderableplane.cpp @@ -102,7 +102,7 @@ namespace { // [[codegen::verbatim(BlendModeInfo.description)]] std::optional blendMode; - // [[codegen::verbatim(BlendModeInfo.description)]] + // [[codegen::verbatim(MultiplyColorInfo.description)]] std::optional multiplyColor [[codegen::color()]]; }; #include "renderableplane_codegen.cpp" @@ -290,6 +290,7 @@ void RenderablePlane::render(const RenderData& data, RendererTasks&) { glBindVertexArray(_quad); glDrawArrays(GL_TRIANGLES, 0, 6); + glBindVertexArray(0); if (additiveBlending) { glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); @@ -342,6 +343,7 @@ void RenderablePlane::createPlane() { sizeof(GLfloat) * 6, reinterpret_cast(sizeof(GLfloat) * 4) ); + glBindVertexArray(0); } } // namespace openspace diff --git a/modules/base/rendering/renderableplaneimagelocal.cpp b/modules/base/rendering/renderableplaneimagelocal.cpp index b19206d0c5..e4edb680ed 100644 --- a/modules/base/rendering/renderableplaneimagelocal.cpp +++ b/modules/base/rendering/renderableplaneimagelocal.cpp @@ -101,14 +101,12 @@ RenderablePlaneImageLocal::RenderablePlaneImageLocal(const ghoul::Dictionary& di addProperty(_blendMode); - _texturePath = absPath(p.texture); - _textureFile = std::make_unique(_texturePath); + _texturePath = absPath(p.texture).string(); + _textureFile = std::make_unique(_texturePath.value()); addProperty(_texturePath); _texturePath.onChange([this]() { loadTexture(); }); - _textureFile->setCallback( - [this](const ghoul::filesystem::File&) { _textureIsDirty = true; } - ); + _textureFile->setCallback([this]() { _textureIsDirty = true; }); if (p.renderType.has_value()) { switch (*p.renderType) { @@ -193,7 +191,7 @@ void RenderablePlaneImageLocal::loadTexture() { std::to_string(hash), [path = _texturePath]() -> std::unique_ptr { std::unique_ptr texture = - ghoul::io::TextureReader::ref().loadTexture(absPath(path)); + ghoul::io::TextureReader::ref().loadTexture(absPath(path).string()); LDEBUGC( "RenderablePlaneImageLocal", @@ -209,10 +207,8 @@ void RenderablePlaneImageLocal::loadTexture() { BaseModule::TextureManager.release(t); - _textureFile = std::make_unique(_texturePath); - _textureFile->setCallback( - [&](const ghoul::filesystem::File&) { _textureIsDirty = true; } - ); + _textureFile = std::make_unique(_texturePath.value()); + _textureFile->setCallback([this]() { _textureIsDirty = true; }); } } diff --git a/modules/base/rendering/renderableplaneimageonline.cpp b/modules/base/rendering/renderableplaneimageonline.cpp index 368aa34439..a86fbd2f39 100644 --- a/modules/base/rendering/renderableplaneimageonline.cpp +++ b/modules/base/rendering/renderableplaneimageonline.cpp @@ -75,8 +75,6 @@ RenderablePlaneImageOnline::RenderablePlaneImageOnline( const Parameters p = codegen::bake(dictionary); _texturePath.onChange([this]() { _textureIsDirty = true; }); - addProperty(_texturePath); - _texturePath = p.url; addProperty(_texturePath); } diff --git a/modules/base/rendering/screenspaceimagelocal.cpp b/modules/base/rendering/screenspaceimagelocal.cpp index 3c74143c2f..48686c487b 100644 --- a/modules/base/rendering/screenspaceimagelocal.cpp +++ b/modules/base/rendering/screenspaceimagelocal.cpp @@ -32,6 +32,7 @@ #include #include #include +#include #include namespace { @@ -81,7 +82,7 @@ ScreenSpaceImageLocal::ScreenSpaceImageLocal(const ghoul::Dictionary& dictionary setIdentifier(identifier); _texturePath.onChange([this]() { - if (!FileSys.fileExists(FileSys.absolutePath(_texturePath))) { + if (!std::filesystem::is_regular_file(absPath(_texturePath))) { LWARNINGC( "ScreenSpaceImageLocal", fmt::format("Image {} did not exist for {}", _texturePath, _identifier) @@ -94,8 +95,8 @@ ScreenSpaceImageLocal::ScreenSpaceImageLocal(const ghoul::Dictionary& dictionary addProperty(_texturePath); if (p.texturePath.has_value()) { - if (FileSys.fileExists(FileSys.absolutePath(*p.texturePath))) { - _texturePath = FileSys.absolutePath(*p.texturePath); + if (std::filesystem::is_regular_file(absPath(*p.texturePath))) { + _texturePath = absPath(*p.texturePath).string(); } else { LWARNINGC( @@ -115,7 +116,7 @@ bool ScreenSpaceImageLocal::deinitializeGL() { void ScreenSpaceImageLocal::update() { if (_textureIsDirty && !_texturePath.value().empty()) { std::unique_ptr texture = - ghoul::io::TextureReader::ref().loadTexture(absPath(_texturePath)); + ghoul::io::TextureReader::ref().loadTexture(absPath(_texturePath).string()); if (texture) { // Images don't need to start on 4-byte boundaries, for example if the diff --git a/modules/base/rendering/screenspaceimageonline.cpp b/modules/base/rendering/screenspaceimageonline.cpp index ef5c527175..9754cc737a 100644 --- a/modules/base/rendering/screenspaceimageonline.cpp +++ b/modules/base/rendering/screenspaceimageonline.cpp @@ -81,8 +81,8 @@ ScreenSpaceImageOnline::ScreenSpaceImageOnline(const ghoul::Dictionary& dictiona setIdentifier(std::move(identifier)); _texturePath.onChange([this]() { _textureIsDirty = true; }); - addProperty(_texturePath); _texturePath = p.url.value_or(_texturePath); + addProperty(_texturePath); } ScreenSpaceImageOnline::~ScreenSpaceImageOnline() {} // NOLINT diff --git a/modules/base/rotation/luarotation.cpp b/modules/base/rotation/luarotation.cpp index f6f0f822a5..e685453327 100644 --- a/modules/base/rotation/luarotation.cpp +++ b/modules/base/rotation/luarotation.cpp @@ -69,17 +69,15 @@ LuaRotation::LuaRotation() _luaScriptFile.onChange([&]() { requireUpdate(); - _fileHandle = std::make_unique(_luaScriptFile); - _fileHandle->setCallback([&](const ghoul::filesystem::File&) { - requireUpdate(); - }); + _fileHandle = std::make_unique(_luaScriptFile.value()); + _fileHandle->setCallback([this]() { requireUpdate(); }); }); } LuaRotation::LuaRotation(const ghoul::Dictionary& dictionary) : LuaRotation() { const Parameters p = codegen::bake(dictionary); - _luaScriptFile = absPath(p.script); + _luaScriptFile = absPath(p.script).string(); } glm::dmat3 LuaRotation::matrix(const UpdateData& data) const { diff --git a/modules/base/scale/luascale.cpp b/modules/base/scale/luascale.cpp index 8ea195e15c..7bbd761dad 100644 --- a/modules/base/scale/luascale.cpp +++ b/modules/base/scale/luascale.cpp @@ -68,16 +68,14 @@ LuaScale::LuaScale() _luaScriptFile.onChange([&]() { requireUpdate(); - _fileHandle = std::make_unique(_luaScriptFile); - _fileHandle->setCallback([&](const ghoul::filesystem::File&) { - requireUpdate(); - }); + _fileHandle = std::make_unique(_luaScriptFile.value()); + _fileHandle->setCallback([this]() { requireUpdate(); }); }); } LuaScale::LuaScale(const ghoul::Dictionary& dictionary) : LuaScale() { const Parameters p = codegen::bake(dictionary); - _luaScriptFile = absPath(p.script); + _luaScriptFile = absPath(p.script).string(); } glm::dvec3 LuaScale::scaleValue(const UpdateData& data) const { diff --git a/modules/base/shaders/screenspace_fs.glsl b/modules/base/shaders/screenspace_fs.glsl index 7c0f7e1b57..fabb553c14 100644 --- a/modules/base/shaders/screenspace_fs.glsl +++ b/modules/base/shaders/screenspace_fs.glsl @@ -29,13 +29,13 @@ in vec2 vs_st; in vec4 vs_position; uniform sampler2D texture1; -uniform float Alpha; +uniform vec3 MultiplyColor = vec3(1.0, 1.0, 1.0); +uniform float Alpha = 1.0; Fragment getFragment() { Fragment frag; - frag.color = texture(texture1, vs_st); - frag.color.a = Alpha * frag.color.a; + frag.color = texture(texture1, vs_st) * vec4(MultiplyColor, Alpha); if (frag.color.a == 0.0) { discard; } diff --git a/modules/base/translation/luatranslation.cpp b/modules/base/translation/luatranslation.cpp index 8f850d1f28..4e2b527e5d 100644 --- a/modules/base/translation/luatranslation.cpp +++ b/modules/base/translation/luatranslation.cpp @@ -69,8 +69,8 @@ LuaTranslation::LuaTranslation() _luaScriptFile.onChange([&]() { requireUpdate(); - _fileHandle = std::make_unique(_luaScriptFile); - _fileHandle->setCallback([&](const ghoul::filesystem::File&) { + _fileHandle = std::make_unique(_luaScriptFile.value()); + _fileHandle->setCallback([this]() { requireUpdate(); notifyObservers(); }); @@ -79,7 +79,7 @@ LuaTranslation::LuaTranslation() LuaTranslation::LuaTranslation(const ghoul::Dictionary& dictionary) : LuaTranslation() { const Parameters p = codegen::bake(dictionary); - _luaScriptFile = absPath(p.script); + _luaScriptFile = absPath(p.script).string(); } glm::dvec3 LuaTranslation::position(const UpdateData& data) const { diff --git a/modules/digitaluniverse/rendering/renderablebillboardscloud.cpp b/modules/digitaluniverse/rendering/renderablebillboardscloud.cpp index 3da6f82553..41fd2f8e23 100644 --- a/modules/digitaluniverse/rendering/renderablebillboardscloud.cpp +++ b/modules/digitaluniverse/rendering/renderablebillboardscloud.cpp @@ -47,6 +47,7 @@ #include #include #include +#include #include #include #include @@ -381,7 +382,7 @@ RenderableBillboardsCloud::RenderableBillboardsCloud(const ghoul::Dictionary& di const Parameters p = codegen::bake(dictionary); if (p.file.has_value()) { - _speckFile = absPath(*p.file); + _speckFile = absPath(*p.file).string(); } _hasSpeckFile = p.file.has_value(); @@ -433,24 +434,22 @@ RenderableBillboardsCloud::RenderableBillboardsCloud(const ghoul::Dictionary& di } } else { - LWARNING("No unit given for RenderableBillboardsCloud. Using meters as units."); + LWARNING("No unit given for RenderableBillboardsCloud. Using meters as units"); _unit = Meter; } if (p.texture.has_value()) { - _spriteTexturePath = absPath(*p.texture); + _spriteTexturePath = absPath(*p.texture).string(); _spriteTexturePath.onChange([&]() { _spriteTextureIsDirty = true; }); // @TODO (abock, 2021-01-31) I don't know why we only add this property if the // texture is given, but I think it's a bug addProperty(_spriteTexturePath); - } _hasSpriteTexture = p.texture.has_value(); - if (p.colorMap.has_value()) { - _colorMapFile = absPath(*p.colorMap); + _colorMapFile = absPath(*p.colorMap).string(); _hasColorMapFile = true; if (p.colorOption.has_value()) { @@ -517,7 +516,7 @@ RenderableBillboardsCloud::RenderableBillboardsCloud(const ghoul::Dictionary& di _drawLabels = p.drawLabels.value_or(_drawLabels); addProperty(_drawLabels); - _labelFile = absPath(*p.labelFile); + _labelFile = absPath(*p.labelFile).string(); _hasLabel = true; _textColor = p.textColor.value_or(_textColor); @@ -571,14 +570,14 @@ RenderableBillboardsCloud::RenderableBillboardsCloud(const ghoul::Dictionary& di _setRangeFromData.onChange([this]() { const int colorMapInUse = - _hasColorMapFile ? _variableDataPositionMap[_colorOptionString] : 0; + _hasColorMapFile ? _dataset.index(_colorOptionString) : 0; float minValue = std::numeric_limits::max(); - float maxValue = std::numeric_limits::min(); - for (size_t i = 0; i < _fullData.size(); i += _nValuesPerAstronomicalObject) { - float colorIdx = _fullData[i + 3 + colorMapInUse]; - maxValue = colorIdx >= maxValue ? colorIdx : maxValue; - minValue = colorIdx < minValue ? colorIdx : minValue; + float maxValue = -std::numeric_limits::max(); + for (const speck::Dataset::Entry& e : _dataset.entries) { + float color = e.data[colorMapInUse]; + minValue = std::min(minValue, color); + maxValue = std::max(maxValue, color); } _optionColorRangeData = glm::vec2(minValue, maxValue); @@ -591,15 +590,25 @@ RenderableBillboardsCloud::RenderableBillboardsCloud(const ghoul::Dictionary& di } bool RenderableBillboardsCloud::isReady() const { - return ((_program != nullptr) && (!_fullData.empty())) || (!_labelData.empty()); + return (_program && (!_dataset.entries.empty())) || (!_labelset.entries.empty()); } void RenderableBillboardsCloud::initialize() { ZoneScoped - bool success = loadData(); - if (!success) { - throw ghoul::RuntimeError("Error loading data"); + if (_hasSpeckFile) { + _dataset = speck::data::loadFileWithCache(_speckFile); + } + + if (_hasColorMapFile) { + _colorMap = speck::color::loadFileWithCache(_colorMapFile); + } + + if (!_labelFile.empty()) { + _labelset = speck::label::loadFileWithCache(_labelFile); + for (speck::Labelset::Entry& e : _labelset.entries) { + e.position = glm::vec3(_transformationMatrix * glm::dvec4(e.position, 1.0)); + } } if (!_colorOptionString.empty() && (_colorRangeData.size() > 1)) { @@ -645,7 +654,7 @@ void RenderableBillboardsCloud::initializeGL() { } if (_hasLabel) { - if (_font == nullptr) { + if (!_font) { size_t _fontSize = 50; _font = global::fontManager->font( "Mono", @@ -749,11 +758,7 @@ void RenderableBillboardsCloud::renderBillboards(const RenderData& data, _program->setUniform(_uniformCache.hasColormap, _hasColorMapFile); glBindVertexArray(_vao); - const GLsizei nAstronomicalObjects = static_cast( - _fullData.size() / _nValuesPerAstronomicalObject - ); - glDrawArrays(GL_POINTS, 0, nAstronomicalObjects); - + glDrawArrays(GL_POINTS, 0, static_cast(_dataset.entries.size())); glBindVertexArray(0); _program->deactivate(); @@ -767,10 +772,7 @@ void RenderableBillboardsCloud::renderLabels(const RenderData& data, const glm::dvec3& orthoUp, float fadeInVariable) { - glm::vec4 textColor = glm::vec4( - glm::vec3(_textColor), - _textOpacity * fadeInVariable - ); + glm::vec4 textColor = glm::vec4(glm::vec3(_textColor), _textOpacity * fadeInVariable); ghoul::fontrendering::FontRenderer::ProjectedLabelsInformation labelInfo; labelInfo.orthoRight = orthoRight; @@ -785,14 +787,13 @@ void RenderableBillboardsCloud::renderLabels(const RenderData& data, labelInfo.enableDepth = true; labelInfo.enableFalseDepth = false; - for (const std::pair& pair : _labelData) { - //glm::vec3 scaledPos(_transformationMatrix * glm::dvec4(pair.first, 1.0)); - glm::vec3 scaledPos(pair.first); + for (const speck::Labelset::Entry& e : _labelset.entries) { + glm::vec3 scaledPos(e.position); scaledPos *= unitToMeter(_unit); ghoul::fontrendering::FontRenderer::defaultProjectionRenderer().render( *_font, scaledPos, - pair.second, + e.text, textColor, labelInfo ); @@ -800,14 +801,16 @@ void RenderableBillboardsCloud::renderLabels(const RenderData& data, } void RenderableBillboardsCloud::render(const RenderData& data, RendererTasks&) { - float fadeInVariable = 1.f; + float fadeInVar = 1.f; if (!_disableFadeInDistance) { float distCamera = static_cast(glm::length(data.camera.positionVec3())); const glm::vec2 fadeRange = _fadeInDistance; - const float a = 1.f / ((fadeRange.y - fadeRange.x) * unitToMeter(_unit)); + const float a = static_cast( + 1.f / ((fadeRange.y - fadeRange.x) * unitToMeter(_unit)) + ); const float b = -(fadeRange.x / (fadeRange.y - fadeRange.x)); const float funcValue = a * distCamera + b; - fadeInVariable *= funcValue > 1.f ? 1.f : funcValue; + fadeInVar *= funcValue > 1.f ? 1.f : funcValue; if (funcValue < 0.01f) { return; @@ -840,23 +843,11 @@ void RenderableBillboardsCloud::render(const RenderData& data, RendererTasks&) { glm::dvec3 orthoUp = glm::normalize(glm::cross(cameraViewDirectionWorld, orthoRight)); if (_hasSpeckFile && _drawElements) { - renderBillboards( - data, - modelMatrix, - orthoRight, - orthoUp, - fadeInVariable - ); + renderBillboards(data, modelMatrix, orthoRight, orthoUp, fadeInVar); } if (_drawLabels && _hasLabel) { - renderLabels( - data, - modelViewProjectionMatrix, - orthoRight, - orthoUp, - fadeInVariable - ); + renderLabels(data, modelViewProjectionMatrix, orthoRight, orthoUp, fadeInVar); } } @@ -868,9 +859,9 @@ void RenderableBillboardsCloud::update(const UpdateData&) { TracyGpuZone("Data dirty") LDEBUG("Regenerating data"); - createDataSlice(); + std::vector slice = createDataSlice(); - int size = static_cast(_slicedData.size()); + int size = static_cast(slice.size()); if (_vao == 0) { glGenVertexArrays(1, &_vao); @@ -883,12 +874,7 @@ void RenderableBillboardsCloud::update(const UpdateData&) { glBindVertexArray(_vao); glBindBuffer(GL_ARRAY_BUFFER, _vbo); - glBufferData( - GL_ARRAY_BUFFER, - size * sizeof(float), - &_slicedData[0], - GL_STATIC_DRAW - ); + glBufferData(GL_ARRAY_BUFFER, size * sizeof(float), slice.data(), GL_STATIC_DRAW); GLint positionAttrib = _program->attributeLocation("in_position"); if (_hasColorMapFile && _hasDatavarSize) { @@ -898,7 +884,7 @@ void RenderableBillboardsCloud::update(const UpdateData&) { 4, GL_FLOAT, GL_FALSE, - sizeof(float) * 9, + 9 * sizeof(float), nullptr ); @@ -909,8 +895,8 @@ void RenderableBillboardsCloud::update(const UpdateData&) { 4, GL_FLOAT, GL_FALSE, - sizeof(float) * 9, - reinterpret_cast(sizeof(float) * 4) + 9 * sizeof(float), + reinterpret_cast(4 * sizeof(float)) ); GLint dvarScalingAttrib = _program->attributeLocation("in_dvarScaling"); @@ -920,8 +906,8 @@ void RenderableBillboardsCloud::update(const UpdateData&) { 1, GL_FLOAT, GL_FALSE, - sizeof(float) * 9, - reinterpret_cast(sizeof(float) * 8) + 9 * sizeof(float), + reinterpret_cast(8 * sizeof(float)) ); } else if (_hasColorMapFile) { @@ -931,7 +917,7 @@ void RenderableBillboardsCloud::update(const UpdateData&) { 4, GL_FLOAT, GL_FALSE, - sizeof(float) * 8, + 8 * sizeof(float), nullptr ); @@ -942,8 +928,8 @@ void RenderableBillboardsCloud::update(const UpdateData&) { 4, GL_FLOAT, GL_FALSE, - sizeof(float) * 8, - reinterpret_cast(sizeof(float) * 4) + 8 * sizeof(float), + reinterpret_cast(4 * sizeof(float)) ); } else if (_hasDatavarSize) { @@ -953,7 +939,7 @@ void RenderableBillboardsCloud::update(const UpdateData&) { 4, GL_FLOAT, GL_FALSE, - sizeof(float) * 8, + 8 * sizeof(float), nullptr ); @@ -964,8 +950,8 @@ void RenderableBillboardsCloud::update(const UpdateData&) { 1, GL_FLOAT, GL_FALSE, - sizeof(float) * 5, - reinterpret_cast(sizeof(float) * 4) + 5 * sizeof(float), + reinterpret_cast(4 * sizeof(float)) ); } else { @@ -999,7 +985,7 @@ void RenderableBillboardsCloud::update(const UpdateData&) { [path = _spriteTexturePath]() -> std::unique_ptr { LINFO(fmt::format("Loaded texture from '{}'", absPath(path))); std::unique_ptr t = - ghoul::io::TextureReader::ref().loadTexture(absPath(path)); + ghoul::io::TextureReader::ref().loadTexture(absPath(path).string()); t->uploadTexture(); t->setFilter(ghoul::opengl::Texture::FilterMode::AnisotropicMipMap); t->purgeFromRAM(); @@ -1012,434 +998,9 @@ void RenderableBillboardsCloud::update(const UpdateData&) { } } -bool RenderableBillboardsCloud::loadData() { - bool success = true; - - success &= loadSpeckData(); - - if (_hasColorMapFile) { - if (!_hasSpeckFile) { - success = true; - } - success &= readColorMapFile(); - } - - success &= loadLabelData(); - - return success; -} - -bool RenderableBillboardsCloud::loadSpeckData() { - if (!_hasSpeckFile) { - return true; - } - bool success = true; - const std::string& cachedFile = FileSys.cacheManager()->cachedFilename( - ghoul::filesystem::File(_speckFile), - "RenderableDUMeshes|" + identifier(), - ghoul::filesystem::CacheManager::Persistent::Yes - ); - - const bool hasCachedFile = FileSys.fileExists(cachedFile); - if (hasCachedFile) { - LINFO(fmt::format( - "Cached file '{}' used for Speck file '{}'", - cachedFile, _speckFile - )); - - success = loadCachedFile(cachedFile); - if (success) { - return true; - } - else { - FileSys.cacheManager()->removeCacheFile(_speckFile); - // Intentional fall-through to the 'else' to generate the cache - // file for the next run - } - } - else { - LINFO(fmt::format("Cache for Speck file '{}' not found", _speckFile)); - } - LINFO(fmt::format("Loading Speck file '{}'", _speckFile)); - - success = readSpeckFile(); - if (!success) { - return false; - } - - success &= saveCachedFile(cachedFile); - return success; -} - -bool RenderableBillboardsCloud::loadLabelData() { - if (_labelFile.empty()) { - return true; - } - bool success = true; - // I disabled the cache as it didn't work on Mac --- abock - const std::string& cachedFile = FileSys.cacheManager()->cachedFilename( - ghoul::filesystem::File(_labelFile), - ghoul::filesystem::CacheManager::Persistent::Yes - ); - if (!_hasSpeckFile && !_hasColorMapFile) { - success = true; - } - const bool hasCachedFile = FileSys.fileExists(cachedFile); - if (hasCachedFile) { - LINFO(fmt::format( - "Cached file '{}' used for Label file '{}'", - cachedFile, _labelFile - )); - - success &= loadCachedFile(cachedFile); - if (!success) { - FileSys.cacheManager()->removeCacheFile(_labelFile); - // Intentional fall-through to the 'else' to generate the cache - // file for the next run - } - } - else { - LINFO(fmt::format("Cache for Label file '{}' not found", _labelFile)); - LINFO(fmt::format("Loading Label file '{}'", _labelFile)); - - success &= readLabelFile(); - if (!success) { - return false; - } - } - - return success; -} - -bool RenderableBillboardsCloud::readSpeckFile() { - std::ifstream file(_speckFile); - if (!file.good()) { - LERROR(fmt::format("Failed to open Speck file '{}'", _speckFile)); - return false; - } - - _nValuesPerAstronomicalObject = 0; - - // The beginning of the speck file has a header that either contains comments - // (signaled by a preceding '#') or information about the structure of the file - // (signaled by the keywords 'datavar', 'texturevar', and 'texture') - std::string line; - while (true) { - std::getline(file, line); - - // Guard against wrong line endings (copying files from Windows to Mac) causes - // lines to have a final \r - if (!line.empty() && line.back() == '\r') { - line = line.substr(0, line.length() - 1); - } - - if (line.empty() || line[0] == '#') { - continue; - } - - if (line.substr(0, 7) != "datavar" && - line.substr(0, 10) != "texturevar" && - line.substr(0, 7) != "texture" && - line.substr(0, 10) != "polyorivar" && - line.substr(0, 10) != "maxcomment") - { - // Started reading data - break; - } - - if (line.substr(0, 7) == "datavar") { - // datavar lines are structured as follows: - // datavar # description - // where # is the index of the data variable; so if we repeatedly overwrite - // the 'nValues' variable with the latest index, we will end up with the total - // number of values (+3 since X Y Z are not counted in the Speck file index) - std::stringstream str(line); - - std::string dummy; - str >> dummy; // command - str >> _nValuesPerAstronomicalObject; // variable index - dummy.clear(); - str >> dummy; // variable name - - _variableDataPositionMap.insert({ dummy, _nValuesPerAstronomicalObject }); - - // We want the number, but the index is 0 based - _nValuesPerAstronomicalObject += 1; - } - } - - _nValuesPerAstronomicalObject += 3; // X Y Z are not counted in the Speck file indices - - - - do { - // Guard against wrong line endings (copying files from Windows to Mac) causes - // lines to have a final \r - if (!line.empty() && line.back() == '\r') { - line = line.substr(0, line.length() - 1); - } - - if (line.empty()) { - std::getline(file, line); - continue; - } - else if (line[0] == '#') { - std::getline(file, line); - continue; - } - - std::stringstream str(line); - std::vector values(_nValuesPerAstronomicalObject); - - for (int i = 0; i < _nValuesPerAstronomicalObject; ++i) { - str >> values[i]; - } - - _fullData.insert(_fullData.end(), values.begin(), values.end()); - - // reads new line - std::getline(file, line); - } while (!file.eof()); - - return true; -} - -bool RenderableBillboardsCloud::readColorMapFile() { - std::string _file = _colorMapFile; - std::ifstream file(_file); - if (!file.good()) { - LERROR(fmt::format("Failed to open Color Map file '{}'", _file)); - return false; - } - - std::size_t numberOfColors = 0; - - // The beginning of the speck file has a header that either contains comments - // (signaled by a preceding '#') or information about the structure of the file - // (signaled by the keywords 'datavar', 'texturevar', and 'texture') - std::string line; - while (true) { - // std::streampos position = file.tellg(); - std::getline(file, line); - - if (line[0] == '#' || line.empty()) { - continue; - } - - // Initial number of colors - std::locale loc; - if (std::isdigit(line[0], loc)) { - std::string::size_type sz; - numberOfColors = std::stoi(line, &sz); - break; - } - else if (file.eof()) { - return false; - } - } - - for (size_t i = 0; i < numberOfColors; ++i) { - std::getline(file, line); - std::stringstream str(line); - - glm::vec4 color; - // Each color in the colormap must be defined as (R,G,B,A) - for (int j = 0; j < 4; ++j) { - str >> color[j]; - } - - _colorMapData.push_back(color); - } - - return true; -} - -bool RenderableBillboardsCloud::readLabelFile() { - std::string _file = _labelFile; - std::ifstream file(_file); - if (!file.good()) { - LERROR(fmt::format("Failed to open Label file '{}'", _file)); - return false; - } - - // The beginning of the speck file has a header that either contains comments - // (signaled by a preceding '#') or information about the structure of the file - // (signaled by the keywords 'datavar', 'texturevar', and 'texture') - std::string line; - while (true) { - std::streampos position = file.tellg(); - std::getline(file, line); - - // Guard against wrong line endings (copying files from Windows to Mac) causes - // lines to have a final \r - if (!line.empty() && line.back() == '\r') { - line = line.substr(0, line.length() - 1); - } - - if (line.empty() || line[0] == '#') { - continue; - } - - if (line.substr(0, 9) != "textcolor") { - // we read a line that doesn't belong to the header, so we have to jump back - // before the beginning of the current line - file.seekg(position); - continue; - } - - if (line.substr(0, 9) == "textcolor") { - // textcolor lines are structured as follows: - // textcolor # description - // where # is color text defined in configuration file - std::stringstream str(line); - - // TODO: handle cases of labels with different colors - break; - } - } - - - do { - std::vector values(_nValuesPerAstronomicalObject); - - std::getline(file, line); - - // Guard against wrong line endings (copying files from Windows to Mac) causes - // lines to have a final \r - if (!line.empty() && line.back() == '\r') { - line = line.substr(0, line.length() - 1); - } - - if (line.empty()) { - continue; - } - - std::stringstream str(line); - - glm::vec3 position = glm::vec3(0.f); - for (int j = 0; j < 3; ++j) { - str >> position[j]; - } - - std::string dummy; - str >> dummy; // text keyword - - std::string label; - str >> label; - dummy.clear(); - - while (str >> dummy) { - if (dummy == "#") { - break; - } - - label += " " + dummy; - dummy.clear(); - } - - glm::vec3 transformedPos = glm::vec3( - _transformationMatrix * glm::dvec4(position, 1.0) - ); - _labelData.emplace_back(std::make_pair(transformedPos, label)); - } while (!file.eof()); - - return true; -} - -bool RenderableBillboardsCloud::loadCachedFile(const std::string& file) { - std::ifstream fileStream(file, std::ifstream::binary); - if (!fileStream.good()) { - LERROR(fmt::format("Error opening file '{}' for loading cache file", file)); - return false; - } - int8_t version = 0; - fileStream.read(reinterpret_cast(&version), sizeof(int8_t)); - if (version != CurrentCacheVersion) { - LINFO("The format of the cached file has changed: deleting old cache"); - fileStream.close(); - FileSys.deleteFile(file); - return false; - } - - int32_t nValues = 0; - fileStream.read(reinterpret_cast(&nValues), sizeof(int32_t)); - fileStream.read( - reinterpret_cast(&_nValuesPerAstronomicalObject), - sizeof(int32_t) - ); - - _fullData.resize(nValues); - fileStream.read( - reinterpret_cast(&_fullData[0]), - nValues * sizeof(_fullData[0]) - ); - - if (_hasColorMapFile) { - int32_t nItems = 0; - fileStream.read(reinterpret_cast(&nItems), sizeof(int32_t)); - - for (int i = 0; i < nItems; ++i) { - int32_t keySize = 0; - fileStream.read(reinterpret_cast(&keySize), sizeof(int32_t)); - std::vector buffer(keySize); - fileStream.read(buffer.data(), keySize); - - std::string key(buffer.begin(), buffer.end()); - int32_t value = 0; - fileStream.read(reinterpret_cast(&value), sizeof(int32_t)); - - _variableDataPositionMap.insert({ key, value }); - } - } - - bool success = fileStream.good(); - return success; -} - -bool RenderableBillboardsCloud::saveCachedFile(const std::string& file) const { - std::ofstream fileStream(file, std::ofstream::binary); - if (!fileStream.good()) { - LERROR(fmt::format("Error opening file '{}' for save cache file", file)); - return false; - } - fileStream.write(reinterpret_cast(&CurrentCacheVersion), sizeof(int8_t)); - - int32_t nValues = static_cast(_fullData.size()); - if (nValues == 0) { - LERROR("Error writing cache: No values were loaded"); - return false; - } - fileStream.write(reinterpret_cast(&nValues), sizeof(int32_t)); - - int32_t nValuesPerAstronomicalObject = static_cast( - _nValuesPerAstronomicalObject - ); - fileStream.write( - reinterpret_cast(&nValuesPerAstronomicalObject), - sizeof(int32_t) - ); - - size_t nBytes = nValues * sizeof(_fullData[0]); - fileStream.write(reinterpret_cast(&_fullData[0]), nBytes); - - if (_hasColorMapFile) { - int32_t nItems = static_cast(_variableDataPositionMap.size()); - fileStream.write(reinterpret_cast(&nItems), sizeof(int32_t)); - - for (const std::pair& pair : _variableDataPositionMap) { - int32_t keySize = static_cast(pair.first.size()); - fileStream.write(reinterpret_cast(&keySize), sizeof(int32_t)); - fileStream.write(pair.first.data(), keySize); - int32_t value = static_cast(pair.second); - fileStream.write(reinterpret_cast(&value), sizeof(int32_t)); - } - } - - return fileStream.good(); -} - double RenderableBillboardsCloud::unitToMeter(Unit unit) const { - switch (_unit) { + // @TODO (abock, 2021-05-10) This should be moved to a centralized conversion code + switch (unit) { case Meter: return 1.0; case Kilometer: return 1e3; case Parsec: return PARSEC; @@ -1451,76 +1012,58 @@ double RenderableBillboardsCloud::unitToMeter(Unit unit) const { } } -void RenderableBillboardsCloud::createDataSlice() { +std::vector RenderableBillboardsCloud::createDataSlice() { ZoneScoped - _slicedData.clear(); - - if (_fullData.empty() || _nValuesPerAstronomicalObject == 0) { - return; + if (_dataset.entries.empty()) { + return std::vector(); } + std::vector result; if (_hasColorMapFile) { - _slicedData.reserve(8 * (_fullData.size() / _nValuesPerAstronomicalObject)); + result.reserve(8 * _dataset.entries.size()); } else { - _slicedData.reserve(4 * (_fullData.size() / _nValuesPerAstronomicalObject)); + result.reserve(4 * _dataset.entries.size()); } // what datavar in use for the index color - int colorMapInUse = - _hasColorMapFile ? _variableDataPositionMap[_colorOptionString] : 0; + int colorMapInUse = _hasColorMapFile ? _dataset.index(_colorOptionString) : 0; // what datavar in use for the size scaling (if present) - int sizeScalingInUse = _hasDatavarSize ? - _variableDataPositionMap[_datavarSizeOptionString] : -1; - - auto addDatavarSizeScalling = [&](size_t i, int datavarInUse) { - _slicedData.push_back(_fullData[i + 3 + datavarInUse]); - }; - - auto addPosition = [&](const glm::vec4& pos) { - for (int j = 0; j < 4; ++j) { - _slicedData.push_back(pos[j]); - } - }; + int sizeScalingInUse = + _hasDatavarSize ? _dataset.index(_datavarSizeOptionString) : -1; float minColorIdx = std::numeric_limits::max(); - float maxColorIdx = std::numeric_limits::min(); - - for (size_t i = 0; i < _fullData.size(); i += _nValuesPerAstronomicalObject) { - float colorIdx = _fullData[i + 3 + colorMapInUse]; - maxColorIdx = colorIdx >= maxColorIdx ? colorIdx : maxColorIdx; - minColorIdx = colorIdx < minColorIdx ? colorIdx : minColorIdx; + float maxColorIdx = -std::numeric_limits::max(); + for (const speck::Dataset::Entry& e : _dataset.entries) { + float color = e.data[colorMapInUse]; + minColorIdx = std::min(color, minColorIdx); + maxColorIdx = std::max(color, maxColorIdx); } double maxRadius = 0.0; float biggestCoord = -1.f; - for (size_t i = 0; i < _fullData.size(); i += _nValuesPerAstronomicalObject) { + for (const speck::Dataset::Entry& e : _dataset.entries) { glm::vec3 transformedPos = glm::vec3(_transformationMatrix * glm::vec4( - _fullData[i + 0], - _fullData[i + 1], - _fullData[i + 2], - 1.0 + e.position, 1.0 )); glm::vec4 position(transformedPos, static_cast(_unit)); const double unitMeter = unitToMeter(_unit); glm::dvec3 p = glm::dvec3(position) * unitMeter; const double r = glm::length(p); - if (r > maxRadius) { - maxRadius = r; - } + maxRadius = std::max(maxRadius, r); if (_hasColorMapFile) { for (int j = 0; j < 4; ++j) { - _slicedData.push_back(position[j]); - biggestCoord = biggestCoord < position[j] ? position[j] : biggestCoord; + result.push_back(position[j]); } + biggestCoord = std::max(biggestCoord, glm::compMax(position)); // Note: if exact colormap option is not selected, the first color and the // last color in the colormap file are the outliers colors. - float variableColor = _fullData[i + 3 + colorMapInUse]; + float variableColor = e.data[colorMapInUse]; float cmax, cmin; if (_colorRangeData.empty()) { @@ -1536,39 +1079,37 @@ void RenderableBillboardsCloud::createDataSlice() { if (_isColorMapExact) { int colorIndex = variableColor + cmin; for (int j = 0; j < 4; ++j) { - _slicedData.push_back(_colorMapData[colorIndex][j]); + result.push_back(_colorMap.entries[colorIndex][j]); } } else { if (_useLinearFiltering) { - const float value = variableColor; - - float valueT = (value - cmin) / (cmax - cmin); // in [0, 1) + float valueT = (variableColor - cmin) / (cmax - cmin); // in [0, 1) valueT = std::clamp(valueT, 0.f, 1.f); - const float idx = valueT * (_colorMapData.size() - 1); + const float idx = valueT * (_colorMap.entries.size() - 1); const int floorIdx = static_cast(std::floor(idx)); const int ceilIdx = static_cast(std::ceil(idx)); - const glm::vec4 floorColor = _colorMapData[floorIdx]; - const glm::vec4 ceilColor = _colorMapData[ceilIdx]; + const glm::vec4 floorColor = _colorMap.entries[floorIdx]; + const glm::vec4 ceilColor = _colorMap.entries[ceilIdx]; if (floorColor != ceilColor) { const glm::vec4 c = floorColor + idx * (ceilColor - floorColor); - _slicedData.push_back(c.r); - _slicedData.push_back(c.g); - _slicedData.push_back(c.b); - _slicedData.push_back(c.a); + result.push_back(c.r); + result.push_back(c.g); + result.push_back(c.b); + result.push_back(c.a); } else { - _slicedData.push_back(floorColor.r); - _slicedData.push_back(floorColor.g); - _slicedData.push_back(floorColor.b); - _slicedData.push_back(floorColor.a); + result.push_back(floorColor.r); + result.push_back(floorColor.g); + result.push_back(floorColor.b); + result.push_back(floorColor.a); } } else { - float ncmap = static_cast(_colorMapData.size()); + float ncmap = static_cast(_colorMap.entries.size()); float normalization = ((cmax != cmin) && (ncmap > 2)) ? (ncmap - 2) / (cmax - cmin) : 0; int colorIndex = (variableColor - cmin) * normalization + 1; @@ -1576,25 +1117,30 @@ void RenderableBillboardsCloud::createDataSlice() { colorIndex = colorIndex >= ncmap ? ncmap - 1 : colorIndex; for (int j = 0; j < 4; ++j) { - _slicedData.push_back(_colorMapData[colorIndex][j]); + result.push_back(_colorMap.entries[colorIndex][j]); } } } if (_hasDatavarSize) { - addDatavarSizeScalling(i, sizeScalingInUse); + result.push_back(e.data[sizeScalingInUse]); } } else if (_hasDatavarSize) { - addDatavarSizeScalling(i, sizeScalingInUse); - addPosition(position); + result.push_back(e.data[sizeScalingInUse]); + for (int j = 0; j < 4; ++j) { + result.push_back(position[j]); + } } else { - addPosition(position); + for (int j = 0; j < 4; ++j) { + result.push_back(position[j]); + } } } setBoundingSphere(maxRadius); _fadeInDistance.setMaxValue(glm::vec2(10.f * biggestCoord)); + return result; } void RenderableBillboardsCloud::createPolygonTexture() { @@ -1654,20 +1200,13 @@ void RenderableBillboardsCloud::loadPolygonGeometryForRendering() { glBindVertexArray(_polygonVao); glBindBuffer(GL_ARRAY_BUFFER, _polygonVbo); - const GLfloat vertex_data[] = { + constexpr const std::array VertexData = { // x y z w 0.f, 0.f, 0.f, 1.f, }; - glBufferData(GL_ARRAY_BUFFER, sizeof(vertex_data), vertex_data, GL_STATIC_DRAW); - glVertexAttribPointer( - 0, - 4, - GL_FLOAT, - GL_FALSE, - sizeof(GLfloat) * 4, - nullptr - ); + glBufferData(GL_ARRAY_BUFFER, sizeof(VertexData), VertexData.data(), GL_STATIC_DRAW); + glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), nullptr); glEnableVertexAttribArray(0); glBindVertexArray(0); } @@ -1682,8 +1221,8 @@ void RenderableBillboardsCloud::renderPolygonGeometry(GLuint vao) { ); program->activate(); - static const float black[] = { 0.f, 0.f, 0.f, 0.f }; - glClearBufferfv(GL_COLOR, 0, black); + constexpr const glm::vec4 Black = glm::vec4(0.f, 0.f, 0.f, 0.f); + glClearBufferfv(GL_COLOR, 0, glm::value_ptr(Black)); program->setUniform("sides", _polygonSides); program->setUniform("polygonColor", _pointColor); diff --git a/modules/digitaluniverse/rendering/renderablebillboardscloud.h b/modules/digitaluniverse/rendering/renderablebillboardscloud.h index 4f8d9a45f0..2d0ab0582f 100644 --- a/modules/digitaluniverse/rendering/renderablebillboardscloud.h +++ b/modules/digitaluniverse/rendering/renderablebillboardscloud.h @@ -27,6 +27,7 @@ #include +#include #include #include #include @@ -78,7 +79,7 @@ private: }; double unitToMeter(Unit unit) const; - void createDataSlice(); + std::vector createDataSlice(); void createPolygonTexture(); void renderToTexture(GLuint textureToRenderTo, GLuint textureWidth, GLuint textureHeight); @@ -89,15 +90,6 @@ private: void renderLabels(const RenderData& data, const glm::dmat4& modelViewProjectionMatrix, const glm::dvec3& orthoRight, const glm::dvec3& orthoUp, float fadeInVariable); - bool loadData(); - bool loadSpeckData(); - bool loadLabelData(); - bool readSpeckFile(); - bool readColorMapFile(); - bool readLabelFile(); - bool loadCachedFile(const std::string& file); - bool saveCachedFile(const std::string& file) const; - bool _hasSpeckFile = false; bool _dataIsDirty = true; bool _textColorIsDirty = true; @@ -135,8 +127,6 @@ private: properties::FloatProperty _correctionSizeFactor; properties::BoolProperty _useLinearFiltering; properties::TriggerProperty _setRangeFromData; - - // DEBUG: properties::OptionProperty _renderOption; ghoul::opengl::Texture* _polygonTexture = nullptr; @@ -144,8 +134,9 @@ private: ghoul::opengl::ProgramObject* _program = nullptr; ghoul::opengl::ProgramObject* _renderToPolygonProgram = nullptr; - UniformCache(cameraViewProjectionMatrix, modelMatrix, cameraPos, cameraLookup, - renderOption, minBillboardSize, maxBillboardSize, correctionSizeEndDistance, + UniformCache( + cameraViewProjectionMatrix, modelMatrix, cameraPos, cameraLookup, renderOption, + minBillboardSize, maxBillboardSize, correctionSizeEndDistance, correctionSizeFactor, color, alphaValue, scaleFactor, up, right, fadeInValue, screenSize, spriteTexture, hasColormap, enabledRectSizeControl, hasDvarScaling ) _uniformCache; @@ -160,17 +151,14 @@ private: Unit _unit = Parsec; - std::vector _slicedData; - std::vector _fullData; - std::vector _colorMapData; + speck::Dataset _dataset; + speck::Labelset _labelset; + speck::ColorMap _colorMap; + std::vector _colorRangeData; - std::vector> _labelData; - std::unordered_map _variableDataPositionMap; std::unordered_map _optionConversionMap; std::unordered_map _optionConversionSizeMap; - int _nValuesPerAstronomicalObject = 0; - glm::dmat4 _transformationMatrix = glm::dmat4(1.0); GLuint _vao = 0; diff --git a/modules/digitaluniverse/rendering/renderabledumeshes.cpp b/modules/digitaluniverse/rendering/renderabledumeshes.cpp index d73465cc19..11a2c5ddd9 100644 --- a/modules/digitaluniverse/rendering/renderabledumeshes.cpp +++ b/modules/digitaluniverse/rendering/renderabledumeshes.cpp @@ -44,6 +44,7 @@ #include #include #include +#include #include #include @@ -202,7 +203,7 @@ RenderableDUMeshes::RenderableDUMeshes(const ghoul::Dictionary& dictionary) addProperty(_opacity); registerUpdateRenderBinFromOpacity(); - _speckFile = absPath(p.file); + _speckFile = absPath(p.file).string(); _hasSpeckFile = true; _drawElements.onChange([&]() { _hasSpeckFile = !_hasSpeckFile; }); addProperty(_drawElements); @@ -256,7 +257,7 @@ RenderableDUMeshes::RenderableDUMeshes(const ghoul::Dictionary& dictionary) addProperty(_drawLabels); if (p.labelFile.has_value()) { - _labelFile = absPath(*p.labelFile); + _labelFile = absPath(*p.labelFile).string(); _hasLabel = true; _textColor = p.textColor.value_or(_textColor); @@ -288,7 +289,7 @@ RenderableDUMeshes::RenderableDUMeshes(const ghoul::Dictionary& dictionary) bool RenderableDUMeshes::isReady() const { return (_program != nullptr) && - (!_renderingMeshesMap.empty() || (!_labelData.empty())); + (!_renderingMeshesMap.empty() || (!_labelset.entries.empty())); } void RenderableDUMeshes::initializeGL() { @@ -431,13 +432,13 @@ void RenderableDUMeshes::renderLabels(const RenderData& data, glm::vec4 textColor = glm::vec4(glm::vec3(_textColor), _textOpacity); - for (const std::pair& pair : _labelData) { - glm::vec3 scaledPos(pair.first); + for (const speck::Labelset::Entry& e : _labelset.entries) { + glm::vec3 scaledPos(e.position); scaledPos *= scale; ghoul::fontrendering::FontRenderer::defaultProjectionRenderer().render( *_font, scaledPos, - pair.second, + e.text, textColor, labelInfo ); @@ -494,31 +495,7 @@ void RenderableDUMeshes::update(const UpdateData&) { bool RenderableDUMeshes::loadData() { bool success = false; if (_hasSpeckFile) { - // I disabled the cache as it didn't work on Mac --- abock - // std::string cachedFile = FileSys.cacheManager()->cachedFilename( - // _speckFile, - // ghoul::filesystem::CacheManager::Persistent::Yes - // ); - - // bool hasCachedFile = FileSys.fileExists(cachedFile); - // if (hasCachedFile) { - // LINFO( - // "Cached file '" << cachedFile << - // "' used for Speck file '" << _speckFile << "'" - // ); - - // success = loadCachedFile(cachedFile); - // if (!success) { - // FileSys.cacheManager()->removeCacheFile(_speckFile); - // // Intentional fall-through to the 'else' to generate the cache - // // file for the next run - // } - // } - // else - // { - // LINFO("Cache for Speck file '" << _speckFile << "' not found"); LINFO(fmt::format("Loading Speck file '{}'", _speckFile)); - success = readSpeckFile(); if (!success) { return false; @@ -527,35 +504,7 @@ bool RenderableDUMeshes::loadData() { std::string labelFile = _labelFile; if (!labelFile.empty()) { - // I disabled the cache as it didn't work on Mac --- abock - // std::string cachedFile = FileSys.cacheManager()->cachedFilename( - // labelFile, - // ghoul::filesystem::CacheManager::Persistent::Yes - // ); - // bool hasCachedFile = FileSys.fileExists(cachedFile); - // if (hasCachedFile) { - // LINFO( - // "Cached file '" << cachedFile << "' used for Label file '" << - // labelFile << "'" - // ); - - // success &= loadCachedFile(cachedFile); - // if (!success) { - // FileSys.cacheManager()->removeCacheFile(labelFile); - // // Intentional fall-through to the 'else' to generate the cache - // // file for the next run - // } - // } - // else { - // LINFO("Cache for Label file '" << labelFile << "' not found"); - LINFO(fmt::format("Loading Label file '{}'", labelFile)); - - success &= readLabelFile(); - if (!success) { - return false; - } - - // } + _labelset = speck::label::loadFileWithCache(_labelFile); } return success; @@ -688,158 +637,6 @@ bool RenderableDUMeshes::readSpeckFile() { return true; } -bool RenderableDUMeshes::readLabelFile() { - std::ifstream file(_labelFile); - if (!file.good()) { - LERROR(fmt::format("Failed to open Label file '{}'", _labelFile)); - return false; - } - - // The beginning of the speck file has a header that either contains comments - // (signaled by a preceding '#') or information about the structure of the file - // (signaled by the keywords 'datavar', 'texturevar', and 'texture') - std::string line; - while (true) { - std::streampos position = file.tellg(); - std::getline(file, line); - - // Guard against wrong line endings (copying files from Windows to Mac) causes - // lines to have a final \r - if (!line.empty() && line.back() == '\r') { - line = line.substr(0, line.length() - 1); - } - - - if (line.empty() || line[0] == '#') { - continue; - } - - if (line.substr(0, 9) != "textcolor") { - // we read a line that doesn't belong to the header, so we have to jump back - // before the beginning of the current line - file.seekg(position); - continue; - } - - if (line.substr(0, 9) == "textcolor") { - // textcolor lines are structured as follows: - // textcolor # description - // where # is color text defined in configuration file - std::stringstream str(line); - - // TODO: handle cases of labels with different colors - break; - } - } - - do { - std::vector values(_nValuesPerAstronomicalObject); - - std::getline(file, line); - - // Guard against wrong line endings (copying files from Windows to Mac) causes - // lines to have a final \r - if (!line.empty() && line.back() == '\r') { - line = line.substr(0, line.length() - 1); - } - - if (line.empty()) { - continue; - } - - std::stringstream str(line); - - glm::vec3 position = glm::vec3(0.f); - for (int j = 0; j < 3; ++j) { - str >> position[j]; - } - - std::string dummy; - str >> dummy; // text keyword - - std::string label; - str >> label; - dummy.clear(); - - while (str >> dummy) { - label += " " + dummy; - dummy.clear(); - } - - _labelData.emplace_back(std::make_pair(position, label)); - - } while (!file.eof()); - - return true; -} - -bool RenderableDUMeshes::loadCachedFile(const std::string& file) { - std::ifstream fileStream(file, std::ifstream::binary); - if (!fileStream.good()) { - LERROR(fmt::format("Error opening file '{}' for loading cache file", file)); - return false; - } - - int8_t version = 0; - fileStream.read(reinterpret_cast(&version), sizeof(int8_t)); - if (version != CurrentCacheVersion) { - LINFO("The format of the cached file has changed: deleting old cache"); - fileStream.close(); - FileSys.deleteFile(file); - return false; - } - - int32_t nValues = 0; - fileStream.read(reinterpret_cast(&nValues), sizeof(int32_t)); - fileStream.read( - reinterpret_cast(&_nValuesPerAstronomicalObject), - sizeof(int32_t) - ); - - _fullData.resize(nValues); - fileStream.read( - reinterpret_cast(&_fullData[0]), - nValues * sizeof(_fullData[0]) - ); - - bool success = fileStream.good(); - return success; -} - -bool RenderableDUMeshes::saveCachedFile(const std::string& file) const { - std::ofstream fileStream(file, std::ofstream::binary); - if (!fileStream.good()) { - LERROR(fmt::format("Error opening file '{}' for save cache file", file)); - return false; - } - - fileStream.write( - reinterpret_cast(&CurrentCacheVersion), - sizeof(int8_t) - ); - - const int32_t nValues = static_cast(_fullData.size()); - if (nValues == 0) { - LERROR("Error writing cache: No values were loaded"); - return false; - } - fileStream.write(reinterpret_cast(&nValues), sizeof(int32_t)); - - const int32_t nValuesPerAstronomicalObject = static_cast( - _nValuesPerAstronomicalObject - ); - fileStream.write( - reinterpret_cast(&nValuesPerAstronomicalObject), - sizeof(int32_t) - ); - - const size_t nBytes = nValues * sizeof(_fullData[0]); - fileStream.write(reinterpret_cast(&_fullData[0]), nBytes); - - const bool success = fileStream.good(); - return success; -} - void RenderableDUMeshes::createMeshes() { if (!(_dataIsDirty && _hasSpeckFile)) { return; diff --git a/modules/digitaluniverse/rendering/renderabledumeshes.h b/modules/digitaluniverse/rendering/renderabledumeshes.h index 04d32ae50f..020e76d0c1 100644 --- a/modules/digitaluniverse/rendering/renderabledumeshes.h +++ b/modules/digitaluniverse/rendering/renderabledumeshes.h @@ -27,6 +27,7 @@ #include +#include #include #include #include @@ -108,9 +109,6 @@ private: bool loadData(); bool readSpeckFile(); - bool readLabelFile(); - bool loadCachedFile(const std::string& file); - bool saveCachedFile(const std::string& file) const; bool _hasSpeckFile = false; bool _dataIsDirty = true; @@ -140,7 +138,7 @@ private: Unit _unit = Parsec; std::vector _fullData; - std::vector> _labelData; + speck::Labelset _labelset; int _nValuesPerAstronomicalObject = 0; std::unordered_map _meshColorMap; diff --git a/modules/digitaluniverse/rendering/renderableplanescloud.cpp b/modules/digitaluniverse/rendering/renderableplanescloud.cpp index 7d754f9308..cbd9cc70dd 100644 --- a/modules/digitaluniverse/rendering/renderableplanescloud.cpp +++ b/modules/digitaluniverse/rendering/renderableplanescloud.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -41,6 +42,7 @@ #include #include #include +#include #include #include #include @@ -49,11 +51,12 @@ namespace { constexpr const char* _loggerCat = "RenderablePlanesCloud"; constexpr const char* ProgramObjectName = "RenderablePlanesCloud"; + constexpr const int PlanesVertexDataSize = 36; + constexpr std::array UniformNames = { "modelViewProjectionTransform", "alphaValue", "fadeInValue", "galaxyTexture" }; - constexpr int8_t CurrentCacheVersion = 2; constexpr double PARSEC = 0.308567756E17; enum BlendMode { @@ -271,7 +274,7 @@ RenderablePlanesCloud::RenderablePlanesCloud(const ghoul::Dictionary& dictionary addProperty(_opacity); if (p.file.has_value()) { - _speckFile = absPath(*p.file); + _speckFile = absPath(*p.file).string(); _hasSpeckFile = true; _drawElements.onChange([&]() { _hasSpeckFile = !_hasSpeckFile; }); addProperty(_drawElements); @@ -319,7 +322,7 @@ RenderablePlanesCloud::RenderablePlanesCloud(const ghoul::Dictionary& dictionary _scaleFactor.onChange([&]() { _dataIsDirty = true; }); if (p.labelFile.has_value()) { - _labelFile = absPath(*p.labelFile); + _labelFile = absPath(*p.labelFile).string(); _hasLabel = true; _textColor = p.textColor.value_or(_textColor); @@ -367,12 +370,11 @@ RenderablePlanesCloud::RenderablePlanesCloud(const ghoul::Dictionary& dictionary } } - _texturesPath = absPath(p.texturePath); + _texturesPath = absPath(p.texturePath).string(); _luminosityVar = p.luminosity.value_or(_luminosityVar); _sluminosity = p.scaleLuminosity.value_or(_sluminosity); - if (p.fadeInDistances.has_value()) { _fadeInDistance = *p.fadeInDistances; _disableFadeInDistance = false; @@ -388,15 +390,25 @@ RenderablePlanesCloud::RenderablePlanesCloud(const ghoul::Dictionary& dictionary } bool RenderablePlanesCloud::isReady() const { - return ((_program != nullptr) && (!_fullData.empty())) || (!_labelData.empty()); + return (_program && (!_dataset.entries.empty())) || (!_labelset.entries.empty()); } void RenderablePlanesCloud::initialize() { ZoneScoped - const bool success = loadData(); - if (!success) { - throw ghoul::RuntimeError("Error loading data"); + if (_hasSpeckFile && std::filesystem::is_regular_file(_speckFile)) { + _dataset = speck::data::loadFileWithCache(_speckFile); + if (_dataset.entries.empty()) { + throw ghoul::RuntimeError("Error loading data"); + } + } + + if (!_labelFile.empty()) { + LINFO(fmt::format("Loading Label file '{}'", _labelFile)); + _labelset = speck::label::loadFileWithCache(_labelFile); + for (speck::Labelset::Entry& e : _labelset.entries) { + e.position = glm::vec3(_transformationMatrix * glm::dvec4(e.position, 1.0)); + } } } @@ -417,7 +429,6 @@ void RenderablePlanesCloud::initializeGL() { ghoul::opengl::updateUniformLocations(*_program, _uniformCache, UniformNames); createPlanes(); - loadTextures(); if (_hasLabel) { @@ -433,7 +444,6 @@ void RenderablePlanesCloud::initializeGL() { } } - void RenderablePlanesCloud::deleteDataGPUAndCPU() { for (std::unordered_map::reference pAMapItem : _planesMap) { glDeleteBuffers(1, &pAMapItem.second.vbo); @@ -484,8 +494,7 @@ void RenderablePlanesCloud::renderPlanes(const RenderData&, _program->setUniform(_uniformCache.galaxyTexture, unit); int currentTextureIndex = -1; - for (std::unordered_map::reference pAMapItem : _planesMap) - { + for (std::unordered_map::reference pAMapItem : _planesMap) { // For planes with undefined textures references if (pAMapItem.first == 30) { continue; @@ -514,35 +523,8 @@ void RenderablePlanesCloud::renderLabels(const RenderData& data, const glm::dvec3& orthoRight, const glm::dvec3& orthoUp, float fadeInVariable) { - float scale = 0.f; - switch (_unit) { - case Meter: - scale = 1.f; - break; - case Kilometer: - scale = 1e3f; - break; - case Parsec: - scale = static_cast(PARSEC); - break; - case Kiloparsec: - scale = static_cast(1e3 * PARSEC); - break; - case Megaparsec: - scale = static_cast(1e6 * PARSEC); - break; - case Gigaparsec: - scale = static_cast(1e9 * PARSEC); - break; - case GigalightYears: - scale = static_cast(306391534.73091 * PARSEC); - break; - } - - glm::vec4 textColor = glm::vec4( - glm::vec3(_textColor), - _textOpacity * fadeInVariable - ); + double scale = unitToMeter(_unit); + glm::vec4 textColor = glm::vec4(glm::vec3(_textColor), _textOpacity * fadeInVariable); ghoul::fontrendering::FontRenderer::ProjectedLabelsInformation labelInfo; labelInfo.orthoRight = orthoRight; @@ -557,14 +539,12 @@ void RenderablePlanesCloud::renderLabels(const RenderData& data, labelInfo.enableDepth = true; labelInfo.enableFalseDepth = false; - for (const std::pair& pair : _labelData) { - //glm::vec3 scaledPos(_transformationMatrix * glm::dvec4(pair.first, 1.0)); - glm::vec3 scaledPos(pair.first); - scaledPos *= scale; + for (const speck::Labelset::Entry& e : _labelset.entries) { + glm::dvec3 scaledPos = glm::dvec3(e.position) * scale; ghoul::fontrendering::FontRenderer::defaultProjectionRenderer().render( *_font, scaledPos, - pair.second, + e.text, textColor, labelInfo ); @@ -597,8 +577,7 @@ void RenderablePlanesCloud::render(const RenderData& data, RendererTasks&) { const glm::dmat4 modelViewMatrix = data.camera.combinedViewMatrix() * modelMatrix; const glm::mat4 projectionMatrix = data.camera.projectionMatrix(); - const glm::dmat4 modelViewProjectionMatrix = glm::dmat4(projectionMatrix) * - modelViewMatrix; + const glm::dmat4 mvpMatrix = glm::dmat4(projectionMatrix) * modelViewMatrix; const glm::dmat4 invMVPParts = glm::inverse(modelMatrix) * glm::inverse(data.camera.combinedViewMatrix()) * @@ -615,13 +594,7 @@ void RenderablePlanesCloud::render(const RenderData& data, RendererTasks&) { } if (_hasLabel) { - renderLabels( - data, - modelViewProjectionMatrix, - orthoRight, - orthoUp, - fadeInVariable - ); + renderLabels(data, mvpMatrix, orthoRight, orthoUp, fadeInVariable); } } @@ -638,442 +611,48 @@ void RenderablePlanesCloud::update(const UpdateData&) { } } -bool RenderablePlanesCloud::loadData() { - bool success = false; - if (_hasSpeckFile) { - // I disabled the cache as it didn't work on Mac --- abock - // std::string cachedFile = FileSys.cacheManager()->cachedFilename( - // _speckFile, - // ghoul::filesystem::CacheManager::Persistent::Yes - // ); +void RenderablePlanesCloud::loadTextures() { + for (const speck::Dataset::Texture& tex : _dataset.textures) { + std::filesystem::path fullPath = absPath(_texturesPath + '/' + tex.file); + std::filesystem::path pngPath = fullPath; + pngPath.replace_extension(".png"); - // bool hasCachedFile = FileSys.fileExists(cachedFile); - // if (hasCachedFile) { - // LINFO( - // "Cached file '" << cachedFile << - // "' used for Speck file '" << _speckFile << "'" - // ); - - // success = loadCachedFile(cachedFile); - // if (!success) { - // FileSys.cacheManager()->removeCacheFile(_speckFile); - // // Intentional fall-through to the 'else' to generate the cache - // // file for the next run - // } - // } - // else - // { - // LINFO("Cache for Speck file '" << _speckFile << "' not found"); - LINFO(fmt::format("Loading Speck file '{}'", _speckFile)); - - success = readSpeckFile(); - if (!success) { - return false; - } - - // LINFO("Saving cache"); - //success &= saveCachedFile(cachedFile); - // } - } - - if (!_labelFile.empty()) { - // I disabled the cache as it didn't work on Mac --- abock - // std::string cachedFile = FileSys.cacheManager()->cachedFilename( - // _labelFile, - // ghoul::filesystem::CacheManager::Persistent::Yes - // ); - // bool hasCachedFile = FileSys.fileExists(cachedFile); - // if (hasCachedFile) { - // LINFO( - // "Cached file '" << cachedFile << - // "' used for Label file '" << _labelFile << "'" - // ); - // - // success &= loadCachedFile(cachedFile); - // if (!success) { - // FileSys.cacheManager()->removeCacheFile(_labelFile); - // // Intentional fall-through to the 'else' to generate the cache - // // file for the next run - // } - // } - // else - // { - // LINFO("Cache for Label file '" << _labelFile << "' not found"); - LINFO(fmt::format("Loading Label file '{}'", _labelFile)); - - success &= readLabelFile(); - if (!success) { - return false; - } - - // } - } - - return success; -} - -bool RenderablePlanesCloud::loadTextures() { - if (!_textureFileMap.empty()) { - for (const std::pair& pair : _textureFileMap) { - const auto& p = _textureMap.insert(std::make_pair( - pair.first, - ghoul::io::TextureReader::ref().loadTexture(pair.second) + std::filesystem::path path; + if (std::filesystem::is_regular_file(fullPath)) { + path = fullPath; + } + else if (std::filesystem::is_regular_file(pngPath)) { + path = pngPath; + } + else { + // We can't really recover from this as it would crash during rendering anyway + throw ghoul::RuntimeError(fmt::format( + "Could not find image file '{}'", tex.file )); - if (p.second) { - LINFOC( - "RenderablePlanesCloud", - fmt::format("Loaded texture from '{}'", pair.second) - ); - p.first->second->uploadTexture(); - p.first->second->setFilter( - ghoul::opengl::Texture::FilterMode::LinearMipMap - ); - p.first->second->purgeFromRAM(); - } - } - } - else { - return false; - } - return true; -} - -bool RenderablePlanesCloud::readSpeckFile() { - std::ifstream file(_speckFile); - if (!file.good()) { - LERROR(fmt::format("Failed to open Speck file '{}'", _speckFile)); - return false; - } - - _nValuesPerAstronomicalObject = 0; - - // The beginning of the speck file has a header that either contains comments - // (signaled by a preceding '#') or information about the structure of the file - // (signaled by the keywords 'datavar', 'texturevar', and 'texture') - std::string line; - while (true) { - std::getline(file, line); - - // Guard against wrong line endings (copying files from Windows to Mac) causes - // lines to have a final \r - if (!line.empty() && line.back() == '\r') { - line = line.substr(0, line.length() -1); } - if (line.empty() || line[0] == '#') { - continue; + std::unique_ptr t = + ghoul::io::TextureReader::ref().loadTexture(path.string()); + + if (t) { + LINFOC("RenderablePlanesCloud", fmt::format("Loaded texture '{}'", path)); + t->uploadTexture(); + t->setFilter(ghoul::opengl::Texture::FilterMode::LinearMipMap); + t->purgeFromRAM(); + } + else { + // Same here, we won't be able to recover from this nullptr + throw ghoul::RuntimeError(fmt::format( + "Could not find image file '{}'", tex.file + )); } - if (line.substr(0, 7) != "datavar" && - line.substr(0, 10) != "texturevar" && - line.substr(0, 7) != "texture" && - line.substr(0, 10) != "polyorivar" && - line.substr(0, 10) != "maxcomment") - { - // Started reading data - break; - } - - if (line.substr(0, 7) == "datavar") { - // datavar lines are structured as follows: - // datavar # description - // where # is the index of the data variable; so if we repeatedly overwrite - // the 'nValues' variable with the latest index, we will end up with the total - // number of values (+3 since X Y Z are not counted in the Speck file index) - std::stringstream str(line); - - std::string dummy; - str >> dummy; // command - str >> _nValuesPerAstronomicalObject; // variable index - dummy.clear(); - str >> dummy; // variable name - - // +3 because of the x, y and z at the begining of each line. - _variableDataPositionMap.insert({ dummy, _nValuesPerAstronomicalObject + 3}); - - if ((dummy == "orientation") || (dummy == "ori")) { // 3d vectors u and v - // We want the number, but the index is 0 based - _nValuesPerAstronomicalObject += 6; - } - else { - // We want the number, but the index is 0 based - _nValuesPerAstronomicalObject += 1; - } - } - - if (line.substr(0, 10) == "polyorivar") { - _planeStartingIndexPos = 0; - std::stringstream str(line); - - std::string dummy; - str >> dummy; // command - str >> _planeStartingIndexPos; - _planeStartingIndexPos += 3; // 3 for xyz - } - - if (line.substr(0, 10) == "texturevar") { - _textureVariableIndex = 0; - std::stringstream str(line); - - std::string dummy; - str >> dummy; // command - str >> _textureVariableIndex; - _textureVariableIndex += 3; // 3 for xyz - } - - if (line.substr(0, 8) == "texture ") { - std::stringstream str(line); - - std::size_t found = line.find('-'); - - int textureIndex = 0; - - std::string dummy; - str >> dummy; // command - - if (found != std::string::npos) { - std::string option; // Not being used right now. - str >> option; - } - - str >> textureIndex; - std::string fileName; - str >> fileName; // texture file name - - std::string fullPath = absPath(_texturesPath + '/' + fileName); - std::string pngPath = - ghoul::filesystem::File(fullPath).fullBaseName() + ".png"; - - if (FileSys.fileExists(fullPath)) { - _textureFileMap.insert({ textureIndex, fullPath }); - - } - else if (FileSys.fileExists(pngPath)) { - _textureFileMap.insert({ textureIndex, pngPath }); - } - else { - LWARNING(fmt::format("Could not find image file {}", fileName)); - _textureFileMap.insert({ textureIndex, "" }); - } - } - } - - _nValuesPerAstronomicalObject += 3; // X Y Z are not counted in the Speck file indices - - do { - - // Guard against wrong line endings (copying files from Windows to Mac) causes - // lines to have a final \r - if (!line.empty() && line.back() == '\r') { - line = line.substr(0, line.length() -1); - } - - if (line.empty()) { - std::getline(file, line); - continue; - } - else if (line[0] == '#') { - std::getline(file, line); - continue; - } - - std::stringstream str(line); - - glm::vec3 u(0.f); - glm::vec3 v(0.f); - - std::vector values(_nValuesPerAstronomicalObject); - - for (int i = 0; i < _nValuesPerAstronomicalObject; ++i) { - str >> values[i]; - if ((i >= _planeStartingIndexPos) && - (i <= _planeStartingIndexPos + 6)) { // vectors u and v - int index = i - _planeStartingIndexPos; - switch (index) { - case 0: - u.x = values[i]; - break; - case 1: - u.y = values[i]; - break; - case 2: - u.z = values[i]; - break; - case 3: - v.x = values[i]; - break; - case 4: - v.y = values[i]; - break; - case 5: - v.z = values[i]; - break; - } - } - } - _fullData.insert(_fullData.end(), values.begin(), values.end()); - - // reads new line - std::getline(file, line); - } while (!file.eof()); - - return true; -} - -bool RenderablePlanesCloud::readLabelFile() { - std::ifstream file(_labelFile); - if (!file.good()) { - LERROR(fmt::format("Failed to open Label file '{}'", _labelFile)); - return false; - } - - // The beginning of the speck file has a header that either contains comments - // (signaled by a preceding '#') or information about the structure of the file - // (signaled by the keywords 'datavar', 'texturevar', and 'texture') - std::string line; - while (true) { - std::streampos position = file.tellg(); - std::getline(file, line); - - // Guard against wrong line endings (copying files from Windows to Mac) causes - // lines to have a final \r - if (!line.empty() && line.back() == '\r') { - line = line.substr(0, line.length() -1); - } - - if (line.empty() || line[0] == '#') { - continue; - } - - if (line.substr(0, 9) != "textcolor") { - // we read a line that doesn't belong to the header, so we have to jump back - // before the beginning of the current line - file.seekg(position); - continue; - } - - if (line.substr(0, 9) == "textcolor") { - // textcolor lines are structured as follows: - // textcolor # description - // where # is color text defined in configuration file - std::stringstream str(line); - - // TODO: handle cases of labels with different colors - break; - } - } - - do { - std::vector values(_nValuesPerAstronomicalObject); - - std::getline(file, line); - - // Guard against wrong line endings (copying files from Windows to Mac) causes - // lines to have a final \r - if (!line.empty() && line.back() == '\r') { - line = line.substr(0, line.length() -1); - } - - if (line.empty()) { - continue; - } - - std::stringstream str(line); - - glm::vec3 position = glm::vec3(0.f); - for (int j = 0; j < 3; ++j) { - str >> position[j]; - } - - std::string dummy; - str >> dummy; // text keyword - - std::string label; - str >> label; - dummy.clear(); - - while (str >> dummy) { - label += " " + dummy; - dummy.clear(); - } - - glm::vec3 transformedPos = glm::vec3( - _transformationMatrix * glm::dvec4(position, 1.0) - ); - _labelData.emplace_back(std::make_pair(transformedPos, label)); - - } while (!file.eof()); - - return true; -} - -bool RenderablePlanesCloud::loadCachedFile(const std::string& file) { - std::ifstream fileStream(file, std::ifstream::binary); - if (fileStream.good()) { - int8_t version = 0; - fileStream.read(reinterpret_cast(&version), sizeof(int8_t)); - if (version != CurrentCacheVersion) { - LINFO("The format of the cached file has changed: deleting old cache"); - fileStream.close(); - FileSys.deleteFile(file); - return false; - } - - int32_t nValues = 0; - fileStream.read(reinterpret_cast(&nValues), sizeof(int32_t)); - fileStream.read(reinterpret_cast( - &_nValuesPerAstronomicalObject), - sizeof(int32_t) - ); - - _fullData.resize(nValues); - fileStream.read(reinterpret_cast(&_fullData[0]), - nValues * sizeof(_fullData[0])); - - bool success = fileStream.good(); - return success; - } - else { - LERROR(fmt::format("Error opening file '{}' for loading cache file", file)); - return false; - } -} - -bool RenderablePlanesCloud::saveCachedFile(const std::string& file) const { - std::ofstream fileStream(file, std::ofstream::binary); - if (fileStream.good()) { - fileStream.write(reinterpret_cast(&CurrentCacheVersion), - sizeof(int8_t)); - - const int32_t nValues = static_cast(_fullData.size()); - if (nValues == 0) { - LERROR("Error writing cache: No values were loaded"); - return false; - } - fileStream.write(reinterpret_cast(&nValues), sizeof(int32_t)); - - const int32_t nValuesPerAstronomicalObject = static_cast( - _nValuesPerAstronomicalObject - ); - fileStream.write(reinterpret_cast( - &nValuesPerAstronomicalObject), - sizeof(int32_t) - ); - - const size_t nBytes = nValues * sizeof(_fullData[0]); - fileStream.write(reinterpret_cast(&_fullData[0]), nBytes); - - bool success = fileStream.good(); - return success; - } - else { - LERROR(fmt::format("Error opening file '{}' for save cache file", file)); - return false; + _textureMap.insert(std::pair(tex.index, std::move(t))); } } double RenderablePlanesCloud::unitToMeter(Unit unit) const { - switch (_unit) { + switch (unit) { case Meter: return 1.0; case Kilometer: return 1e3; case Parsec: return PARSEC; @@ -1087,29 +666,27 @@ double RenderablePlanesCloud::unitToMeter(Unit unit) const { void RenderablePlanesCloud::createPlanes() { if (_dataIsDirty && _hasSpeckFile) { + const int lumIdx = std::max(_dataset.index(_luminosityVar), 0); const double scale = unitToMeter(_unit); LDEBUG("Creating planes..."); float maxSize = 0.f; double maxRadius = 0.0; - for (size_t p = 0; p < _fullData.size(); p += _nValuesPerAstronomicalObject) { + for (const speck::Dataset::Entry& e : _dataset.entries) { const glm::vec4 transformedPos = glm::vec4( - _transformationMatrix * - glm::dvec4(_fullData[p + 0], _fullData[p + 1], _fullData[p + 2], 1.0) + _transformationMatrix * glm::dvec4(e.position, 1.0) ); const double r = glm::length(glm::dvec3(transformedPos) * scale); - if (r > maxRadius) { - maxRadius = r; - } + maxRadius = std::max(maxRadius, r); // Plane vectors u and v glm::vec4 u = glm::vec4( _transformationMatrix * glm::dvec4( - _fullData[p + _planeStartingIndexPos + 0], - _fullData[p + _planeStartingIndexPos + 1], - _fullData[p + _planeStartingIndexPos + 2], + e.data[_dataset.orientationDataIndex + 0], + e.data[_dataset.orientationDataIndex + 1], + e.data[_dataset.orientationDataIndex + 2], 1.f ) ); @@ -1119,9 +696,9 @@ void RenderablePlanesCloud::createPlanes() { glm::vec4 v = glm::vec4( _transformationMatrix * glm::dvec4( - _fullData[p + _planeStartingIndexPos + 3], - _fullData[p + _planeStartingIndexPos + 4], - _fullData[p + _planeStartingIndexPos + 5], + e.data[_dataset.orientationDataIndex + 3], + e.data[_dataset.orientationDataIndex + 4], + e.data[_dataset.orientationDataIndex + 5], 1.f ) ); @@ -1129,8 +706,7 @@ void RenderablePlanesCloud::createPlanes() { v.w = 0.f; if (!_luminosityVar.empty()) { - float lumS = _fullData[p + _variableDataPositionMap[_luminosityVar]] * - _sluminosity; + float lumS = e.data[lumIdx] * _sluminosity; u *= lumS; v *= lumS; } @@ -1150,12 +726,12 @@ void RenderablePlanesCloud::createPlanes() { maxSize = std::max(maxSize, vertex4[i]); } - vertex0 *= scale; - vertex1 *= scale; - vertex2 *= scale; - vertex4 *= scale; + vertex0 = glm::vec4(glm::dvec4(vertex0) * scale); + vertex1 = glm::vec4(glm::dvec4(vertex1) * scale); + vertex2 = glm::vec4(glm::dvec4(vertex2) * scale); + vertex4 = glm::vec4(glm::dvec4(vertex4) * scale); - GLfloat vertexData[] = { + const std::array VertexData = { // x y z w s t vertex0.x, vertex0.y, vertex0.z, 1.f, 0.f, 0.f, vertex1.x, vertex1.y, vertex1.z, 1.f, 1.f, 1.f, @@ -1165,12 +741,12 @@ void RenderablePlanesCloud::createPlanes() { vertex1.x, vertex1.y, vertex1.z, 1.f, 1.f, 1.f, }; - int textureIndex = static_cast(_fullData[p + _textureVariableIndex]); + int textureIndex = static_cast(e.data[_dataset.textureDataIndex]); std::unordered_map::iterator found = _planesMap.find(textureIndex); if (found != _planesMap.end()) { - for (int i = 0; i < PLANES_VERTEX_DATA_SIZE; ++i) { - found->second.planesCoordinates.push_back(vertexData[i]); + for (int i = 0; i < PlanesVertexDataSize; ++i) { + found->second.planesCoordinates.push_back(VertexData[i]); } found->second.numberOfPlanes++; } @@ -1180,10 +756,10 @@ void RenderablePlanesCloud::createPlanes() { glGenVertexArrays(1, &pA.vao); glGenBuffers(1, &pA.vbo); pA.numberOfPlanes = 1; - for (int i = 0; i < PLANES_VERTEX_DATA_SIZE; ++i) { - pA.planesCoordinates.push_back(vertexData[i]); + for (int i = 0; i < PlanesVertexDataSize; ++i) { + pA.planesCoordinates.push_back(VertexData[i]); } - _planesMap.insert(std::pair(textureIndex, pA)); + _planesMap.insert(std::pair(textureIndex, pA)); } } @@ -1193,21 +769,13 @@ void RenderablePlanesCloud::createPlanes() { glBindBuffer(GL_ARRAY_BUFFER, pAMapItem.second.vbo); glBufferData( GL_ARRAY_BUFFER, - sizeof(GLfloat) * PLANES_VERTEX_DATA_SIZE * - pAMapItem.second.numberOfPlanes, + sizeof(GLfloat) * PlanesVertexDataSize * pAMapItem.second.numberOfPlanes, pAMapItem.second.planesCoordinates.data(), GL_STATIC_DRAW ); // in_position glEnableVertexAttribArray(0); - glVertexAttribPointer( - 0, - 4, - GL_FLOAT, - GL_FALSE, - sizeof(GLfloat) * 6, - nullptr - ); + glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), nullptr); // texture coords glEnableVertexAttribArray(1); @@ -1216,8 +784,8 @@ void RenderablePlanesCloud::createPlanes() { 2, GL_FLOAT, GL_FALSE, - sizeof(GLfloat) * 6, - reinterpret_cast(sizeof(GLfloat) * 4) + 6 * sizeof(GLfloat), + reinterpret_cast(4 * sizeof(GLfloat)) ); glBindVertexArray(0); diff --git a/modules/digitaluniverse/rendering/renderableplanescloud.h b/modules/digitaluniverse/rendering/renderableplanescloud.h index 9123200e5c..32549a36f1 100644 --- a/modules/digitaluniverse/rendering/renderableplanescloud.h +++ b/modules/digitaluniverse/rendering/renderableplanescloud.h @@ -27,6 +27,7 @@ #include +#include #include #include #include @@ -50,7 +51,6 @@ namespace ghoul::opengl { namespace openspace { // (x, y, z, w, s, t) * 6 = 36 -const int PLANES_VERTEX_DATA_SIZE = 36; namespace documentation { struct Documentation; } @@ -98,12 +98,7 @@ private: const glm::dmat4& modelViewProjectionMatrix, const glm::dvec3& orthoRight, const glm::dvec3& orthoUp, float fadeInVariable); - bool loadData(); - bool loadTextures(); - bool readSpeckFile(); - bool readLabelFile(); - bool loadCachedFile(const std::string& file); - bool saveCachedFile(const std::string& file) const; + void loadTextures(); bool _hasSpeckFile = false; bool _dataIsDirty = true; @@ -113,8 +108,6 @@ private: int _textMinSize = 0; int _textMaxSize = 200; - int _planeStartingIndexPos = 0; - int _textureVariableIndex = 0; properties::FloatProperty _scaleFactor; properties::Vec3Property _textColor; @@ -125,13 +118,12 @@ private: properties::Vec2Property _fadeInDistance; properties::BoolProperty _disableFadeInDistance; properties::FloatProperty _planeMinSize; - - // DEBUG: properties::OptionProperty _renderOption; ghoul::opengl::ProgramObject* _program = nullptr; - UniformCache(modelViewProjectionTransform, alphaValue, fadeInValue, - galaxyTexture) _uniformCache; + UniformCache( + modelViewProjectionTransform, alphaValue, fadeInValue, galaxyTexture + ) _uniformCache; std::shared_ptr _font = nullptr; std::unordered_map> _textureMap; std::unordered_map _textureFileMap; @@ -144,18 +136,14 @@ private: Unit _unit = Parsec; - std::vector _fullData; - std::vector> _labelData; - std::unordered_map _variableDataPositionMap; - - int _nValuesPerAstronomicalObject = 0; + speck::Dataset _dataset; + speck::Labelset _labelset; float _sluminosity = 1.f; glm::dmat4 _transformationMatrix = glm::dmat4(1.0); }; - } // namespace openspace #endif // __OPENSPACE_MODULE_DIGITALUNIVERSE___RENDERABLEPLANESCLOUD___H__ diff --git a/modules/digitaluniverse/rendering/renderablepoints.cpp b/modules/digitaluniverse/rendering/renderablepoints.cpp index 96c5420b91..b2f4813a8e 100644 --- a/modules/digitaluniverse/rendering/renderablepoints.cpp +++ b/modules/digitaluniverse/rendering/renderablepoints.cpp @@ -40,6 +40,7 @@ #include #include #include +#include #include #include #include @@ -54,7 +55,6 @@ namespace { "spriteTexture", "hasColorMap" }; - constexpr int8_t CurrentCacheVersion = 1; constexpr double PARSEC = 0.308567756E17; constexpr openspace::properties::Property::PropertyInfo SpriteTextureInfo = { @@ -137,7 +137,7 @@ RenderablePoints::RenderablePoints(const ghoul::Dictionary& dictionary) addProperty(_opacity); registerUpdateRenderBinFromOpacity(); - _speckFile = absPath(p.file); + _speckFile = absPath(p.file).string(); if (p.unit.has_value()) { switch (*p.unit) { @@ -174,22 +174,20 @@ RenderablePoints::RenderablePoints(const ghoul::Dictionary& dictionary) addProperty(_pointColor); if (p.texture.has_value()) { - _spriteTexturePath = absPath(*p.texture); + _spriteTexturePath = absPath(*p.texture).string(); _spriteTextureFile = std::make_unique( - _spriteTexturePath + _spriteTexturePath.value() ); - _spriteTexturePath.onChange([&] { _spriteTextureIsDirty = true; }); - _spriteTextureFile->setCallback( - [&](const ghoul::filesystem::File&) { _spriteTextureIsDirty = true; } - ); + _spriteTexturePath.onChange([this]() { _spriteTextureIsDirty = true; }); + _spriteTextureFile->setCallback([this]() { _spriteTextureIsDirty = true; }); addProperty(_spriteTexturePath); _hasSpriteTexture = true; } if (p.colorMap.has_value()) { - _colorMapFile = absPath(*p.colorMap); + _colorMapFile = absPath(*p.colorMap).string(); _hasColorMapFile = true; } @@ -198,26 +196,22 @@ RenderablePoints::RenderablePoints(const ghoul::Dictionary& dictionary) } bool RenderablePoints::isReady() const { - return (_program != nullptr) && (!_fullData.empty()); + return _program && (!_dataset.entries.empty()); } void RenderablePoints::initialize() { ZoneScoped - bool success = loadData(); - if (!success) { - throw ghoul::RuntimeError("Error loading data"); + _dataset = speck::data::loadFileWithCache(_speckFile); + + if (_hasColorMapFile) { + readColorMapFile(); } } void RenderablePoints::initializeGL() { ZoneScoped - // OBS: The ProgramObject name is later used to release the program as well, so the - // name parameter to requestProgramObject and the first parameter to - // buildRenderProgram has to be the same or an assertion will be thrown at the - // end of the program. - if (_hasSpriteTexture) { _program = DigitalUniverseModule::ProgramObjectManager.request( "RenderablePoints Sprite", @@ -289,10 +283,7 @@ void RenderablePoints::render(const RenderData& data, RendererTasks&) { glEnable(GL_PROGRAM_POINT_SIZE); glBindVertexArray(_vao); - const GLsizei nAstronomicalObjects = static_cast( - _fullData.size() / _nValuesPerAstronomicalObject - ); - glDrawArrays(GL_POINTS, 0, nAstronomicalObjects); + glDrawArrays(GL_POINTS, 0, static_cast(_dataset.entries.size())); glDisable(GL_PROGRAM_POINT_SIZE); glBindVertexArray(0); @@ -305,7 +296,7 @@ void RenderablePoints::update(const UpdateData&) { if (_dataIsDirty) { LDEBUG("Regenerating data"); - createDataSlice(); + std::vector slice = createDataSlice(); if (_vao == 0) { glGenVertexArrays(1, &_vao); @@ -318,19 +309,13 @@ void RenderablePoints::update(const UpdateData&) { glBindBuffer(GL_ARRAY_BUFFER, _vbo); glBufferData( GL_ARRAY_BUFFER, - _slicedData.size() * sizeof(double), - &_slicedData[0], + slice.size() * sizeof(double), + slice.data(), GL_STATIC_DRAW ); GLint positionAttrib = _program->attributeLocation("in_position"); if (_hasColorMapFile) { - - // const size_t nAstronomicalObjects = _fullData.size() / - // _nValuesPerAstronomicalObject; - // const size_t nValues = _slicedData.size() / nAstronomicalObjects; - // GLsizei stride = static_cast(sizeof(double) * nValues); - glEnableVertexAttribArray(positionAttrib); glVertexAttribLPointer( positionAttrib, 4, GL_DOUBLE, sizeof(double) * 8, nullptr @@ -342,8 +327,8 @@ void RenderablePoints::update(const UpdateData&) { colorMapAttrib, 4, GL_DOUBLE, - sizeof(double) * 8, - reinterpret_cast(sizeof(double) * 4) + 8 * sizeof(double), + reinterpret_cast(4 * sizeof(double)) ); } else { @@ -361,7 +346,7 @@ void RenderablePoints::update(const UpdateData&) { _spriteTexture = nullptr; if (!_spriteTexturePath.value().empty()) { _spriteTexture = ghoul::io::TextureReader::ref().loadTexture( - absPath(_spriteTexturePath) + absPath(_spriteTexturePath).string() ); if (_spriteTexture) { LDEBUG(fmt::format( @@ -374,134 +359,20 @@ void RenderablePoints::update(const UpdateData&) { ); _spriteTextureFile = std::make_unique( - _spriteTexturePath - ); - _spriteTextureFile->setCallback( - [&](const ghoul::filesystem::File&) { _spriteTextureIsDirty = true; } + _spriteTexturePath.value() ); + _spriteTextureFile->setCallback([this]() { _spriteTextureIsDirty = true; }); } _spriteTextureIsDirty = false; } } -bool RenderablePoints::loadData() { - std::string cachedFile = FileSys.cacheManager()->cachedFilename( - _speckFile, - ghoul::filesystem::CacheManager::Persistent::Yes - ); - - bool hasCachedFile = FileSys.fileExists(cachedFile); - if (hasCachedFile) { - LINFO(fmt::format( - "Cached file '{}' used for Speck file '{}'", - cachedFile, _speckFile - )); - - bool success = loadCachedFile(cachedFile); - if (success) { - if (_hasColorMapFile) { - success &= readColorMapFile(); - } - return success; - } - else { - FileSys.cacheManager()->removeCacheFile(_speckFile); - // Intentional fall-through to the 'else' to generate the cache file for - // the next run - } - } - else { - LINFO(fmt::format("Cache for Speck file '{}' not found", _speckFile)); - } - LINFO(fmt::format("Loading Speck file '{}'", _speckFile)); - - bool success = readSpeckFile(); - if (!success) { - return false; - } - - LINFO("Saving cache"); - success = saveCachedFile(cachedFile); - - if (_hasColorMapFile) { - success &= readColorMapFile(); - } - - return success; -} - -bool RenderablePoints::readSpeckFile() { - std::ifstream file(_speckFile); - if (!file.good()) { - LERROR(fmt::format("Failed to open Speck file '{}'", _speckFile)); - return false; - } - - _nValuesPerAstronomicalObject = 0; - - // The beginning of the speck file has a header that either contains comments - // (signaled by a preceding '#') or information about the structure of the file - // (signaled by the keywords 'datavar', 'texturevar', and 'texture') - std::string line; - while (true) { - std::streampos position = file.tellg(); - std::getline(file, line); - - if (line[0] == '#' || line.empty()) { - continue; - } - - if (line.substr(0, 7) != "datavar" && - line.substr(0, 10) != "texturevar" && - line.substr(0, 7) != "texture") - { - // we read a line that doesn't belong to the header, so we have to jump - // back before the beginning of the current line - file.seekg(position); - break; - } - - if (line.substr(0, 7) == "datavar") { - // datavar lines are structured as follows: - // datavar # description - // where # is the index of the data variable; so if we repeatedly - // overwrite the 'nValues' variable with the latest index, we will end up - // with the total number of values (+3 since X Y Z are not counted in the - // Speck file index) - std::stringstream str(line); - - std::string dummy; - str >> dummy; - str >> _nValuesPerAstronomicalObject; - // We want the number, but the index is 0 based - _nValuesPerAstronomicalObject += 1; - } - } - - // X Y Z are not counted in the Speck file indices - _nValuesPerAstronomicalObject += 3; - - do { - std::vector values(_nValuesPerAstronomicalObject); - - std::getline(file, line); - std::stringstream str(line); - - for (int i = 0; i < _nValuesPerAstronomicalObject; ++i) { - str >> values[i]; - } - - _fullData.insert(_fullData.end(), values.begin(), values.end()); - } while (!file.eof()); - - return true; -} - -bool RenderablePoints::readColorMapFile() { +void RenderablePoints::readColorMapFile() { std::ifstream file(_colorMapFile); if (!file.good()) { - LERROR(fmt::format("Failed to open Color Map file '{}'", _colorMapFile)); - return false; + throw ghoul::RuntimeError(fmt::format( + "Failed to open Color Map file '{}'", _colorMapFile + )); } std::size_t numberOfColors = 0; @@ -526,7 +397,9 @@ bool RenderablePoints::readColorMapFile() { break; } else if (file.eof()) { - return false; + throw ghoul::RuntimeError(fmt::format( + "Failed to load colors from Color Map file '{}'", _colorMapFile + )); } } @@ -535,106 +408,26 @@ bool RenderablePoints::readColorMapFile() { std::stringstream str(line); glm::vec4 color; - for (int j = 0; j < 4; ++j) { - str >> color[j]; - } + str >> color.r >> color.g >> color.b >> color.a; _colorMapData.push_back(color); } - - return true; } -bool RenderablePoints::loadCachedFile(const std::string& file) { - std::ifstream fileStream(file, std::ifstream::binary); - if (fileStream.good()) { - int8_t version = 0; - fileStream.read(reinterpret_cast(&version), sizeof(int8_t)); - if (version != CurrentCacheVersion) { - LINFO("The format of the cached file has changed: deleting old cache"); - fileStream.close(); - FileSys.deleteFile(file); - return false; - } - - int32_t nValues = 0; - fileStream.read(reinterpret_cast(&nValues), sizeof(int32_t)); - fileStream.read( - reinterpret_cast(&_nValuesPerAstronomicalObject), - sizeof(int32_t) - ); - - _fullData.resize(nValues); - fileStream.read(reinterpret_cast( - &_fullData[0]), - nValues * sizeof(_fullData[0]) - ); - - const bool success = fileStream.good(); - return success; - } - else { - LERROR(fmt::format( - "Error opening file '{}' for loading cache file", - file - )); - return false; - } -} - -bool RenderablePoints::saveCachedFile(const std::string& file) const { - std::ofstream fileStream(file, std::ofstream::binary); - if (fileStream.good()) { - fileStream.write( - reinterpret_cast(&CurrentCacheVersion), - sizeof(int8_t) - ); - - const int32_t nValues = static_cast(_fullData.size()); - if (nValues == 0) { - LERROR("Error writing cache: No values were loaded"); - return false; - } - fileStream.write(reinterpret_cast(&nValues), sizeof(int32_t)); - - const int32_t nValuesPerAstronomicalObject = static_cast( - _nValuesPerAstronomicalObject - ); - fileStream.write( - reinterpret_cast(&nValuesPerAstronomicalObject), - sizeof(int32_t) - ); - - const size_t nBytes = nValues * sizeof(_fullData[0]); - fileStream.write(reinterpret_cast(&_fullData[0]), nBytes); - - const bool success = fileStream.good(); - return success; - } - else { - LERROR(fmt::format("Error opening file '{}' for save cache file", file)); - return false; - } -} - -void RenderablePoints::createDataSlice() { - _slicedData.clear(); +std::vector RenderablePoints::createDataSlice() { + std::vector slice; if (_hasColorMapFile) { - _slicedData.reserve(8 * (_fullData.size() / _nValuesPerAstronomicalObject)); + slice.reserve(8 * _dataset.entries.size()); } else { - _slicedData.reserve(4 * (_fullData.size()/_nValuesPerAstronomicalObject)); + slice.reserve(4 * _dataset.entries.size()); } int colorIndex = 0; - for (size_t i = 0; i < _fullData.size(); i += _nValuesPerAstronomicalObject) { - glm::dvec3 p = glm::dvec3( - _fullData[i + 0], - _fullData[i + 1], - _fullData[i + 2] - ); + for (const speck::Dataset::Entry& e : _dataset.entries) { + glm::dvec3 p = e.position; - // Converting untis + // Converting units if (_unit == Kilometer) { p *= 1E3; } @@ -658,15 +451,15 @@ void RenderablePoints::createDataSlice() { if (_hasColorMapFile) { for (int j = 0; j < 4; ++j) { - _slicedData.push_back(position[j]); + slice.push_back(position[j]); } for (int j = 0; j < 4; ++j) { - _slicedData.push_back(_colorMapData[colorIndex][j]); + slice.push_back(_colorMapData[colorIndex][j]); } } else { for (int j = 0; j < 4; ++j) { - _slicedData.push_back(position[j]); + slice.push_back(position[j]); } } @@ -674,6 +467,8 @@ void RenderablePoints::createDataSlice() { 0 : colorIndex + 1; } + + return slice; } } // namespace openspace diff --git a/modules/digitaluniverse/rendering/renderablepoints.h b/modules/digitaluniverse/rendering/renderablepoints.h index b842a90deb..0f7c4f1361 100644 --- a/modules/digitaluniverse/rendering/renderablepoints.h +++ b/modules/digitaluniverse/rendering/renderablepoints.h @@ -27,6 +27,7 @@ #include +#include #include #include #include @@ -63,7 +64,6 @@ public: static documentation::Documentation Documentation(); private: - enum Unit { Meter = 0, Kilometer = 1, @@ -74,13 +74,9 @@ private: GigalightYears = 6 }; - void createDataSlice(); + std::vector createDataSlice(); - bool loadData(); - bool readSpeckFile(); - bool readColorMapFile(); - bool loadCachedFile(const std::string& file); - bool saveCachedFile(const std::string& file) const; + void readColorMapFile(); bool _dataIsDirty = true; bool _hasSpriteTexture = false; @@ -94,19 +90,20 @@ private: std::unique_ptr _spriteTexture; std::unique_ptr _spriteTextureFile; ghoul::opengl::ProgramObject* _program = nullptr; - UniformCache(modelViewProjectionTransform, color, sides, alphaValue, scaleFactor, - spriteTexture, hasColorMap) _uniformCache; + UniformCache( + modelViewProjectionTransform, color, sides, alphaValue, scaleFactor, + spriteTexture, hasColorMap + ) _uniformCache; std::string _speckFile; std::string _colorMapFile; Unit _unit = Parsec; - std::vector _slicedData; - std::vector _fullData; + speck::Dataset _dataset; std::vector _colorMapData; - int _nValuesPerAstronomicalObject = 0; + //int _nValuesPerAstronomicalObject = 0; GLuint _vao = 0; GLuint _vbo = 0; diff --git a/modules/exoplanets/exoplanetsmodule.cpp b/modules/exoplanets/exoplanetsmodule.cpp index a918bb6ca1..4658cee9c2 100644 --- a/modules/exoplanets/exoplanetsmodule.cpp +++ b/modules/exoplanets/exoplanetsmodule.cpp @@ -199,14 +199,14 @@ ExoplanetsModule::ExoplanetsModule() std::string ExoplanetsModule::exoplanetsDataPath() const { return absPath( - fmt::format("{}/{}", _exoplanetsDataFolder, ExoplanetsDataFileName) - ); + fmt::format("{}/{}", _exoplanetsDataFolder.value(), ExoplanetsDataFileName) + ).string(); }; std::string ExoplanetsModule::lookUpTablePath() const { return absPath( fmt::format("{}/{}", _exoplanetsDataFolder, LookupTableFileName) - ); + ).string(); }; std::string ExoplanetsModule::bvColormapPath() const { diff --git a/modules/exoplanets/exoplanetsmodule_lua.inl b/modules/exoplanets/exoplanetsmodule_lua.inl index 5843cec267..5b0369dad7 100644 --- a/modules/exoplanets/exoplanetsmodule_lua.inl +++ b/modules/exoplanets/exoplanetsmodule_lua.inl @@ -512,7 +512,7 @@ void createExoplanetSystem(const std::string& starName) { // the luminosity of a star is proportional to: (radius^2)*(temperature^4) // Maybe a better option would be to compute the size based on the aboslute // magnitude or star luminosity, but for now this looks good enough. - float size = 59.f * radiusInMeter; + double size = 59.0 * radiusInMeter; if (hasTeff) { constexpr const float sunTeff = 5780.f; size *= std::pow(system.starData.teff / sunTeff, 2.0); diff --git a/modules/exoplanets/rendering/renderableorbitdisc.cpp b/modules/exoplanets/rendering/renderableorbitdisc.cpp index 7581788fac..bc1d0299b2 100644 --- a/modules/exoplanets/rendering/renderableorbitdisc.cpp +++ b/modules/exoplanets/rendering/renderableorbitdisc.cpp @@ -118,7 +118,7 @@ RenderableOrbitDisc::RenderableOrbitDisc(const ghoul::Dictionary& dictionary) setBoundingSphere(_size + _offset.value().y * _size); _texturePath = p.texture.string(); - _texturePath.onChange([&]() { _texture->loadFromFile(_texturePath); }); + _texturePath.onChange([&]() { _texture->loadFromFile(_texturePath.value()); }); addProperty(_texturePath); _eccentricity = p.eccentricity; @@ -148,7 +148,7 @@ void RenderableOrbitDisc::initializeGL() { ghoul::opengl::updateUniformLocations(*_shader, _uniformCache, UniformNames); - _texture->loadFromFile(_texturePath); + _texture->loadFromFile(_texturePath.value()); _texture->uploadToGpu(); _plane->initialize(); diff --git a/modules/exoplanets/tasks/exoplanetsdatapreparationtask.cpp b/modules/exoplanets/tasks/exoplanetsdatapreparationtask.cpp index 837e9cafa3..ed571c9460 100644 --- a/modules/exoplanets/tasks/exoplanetsdatapreparationtask.cpp +++ b/modules/exoplanets/tasks/exoplanetsdatapreparationtask.cpp @@ -34,39 +34,55 @@ #include #include #include +#include #include namespace { - constexpr const char* KeyInputDataFile = "InputDataFile"; - constexpr const char* KeyInputSpeck = "InputSPECK"; - constexpr const char* KeyOutputBin = "OutputBIN"; - constexpr const char* KeyOutputLut = "OutputLUT"; - constexpr const char* KeyTeffToBv = "TeffToBvFile"; - constexpr const char* _loggerCat = "ExoplanetsDataPreparationTask"; + + struct [[codegen::Dictionary(ExoplanetsDataPreparationTask)]] Parameters { + // The csv file to extract data from + std::filesystem::path inputDataFile; + + // The speck file with star locations + std::filesystem::path inputSPECK; + + // The bin file to export data into + std::string outputBIN [[codegen::annotation("A valid filepath")]]; + + // The txt file to write look-up table into + std::string outputLUT [[codegen::annotation("A valid filepath")]]; + + // The path to a teff to bv conversion file. Should be a txt file where each line + // has the format 'teff,bv' + std::filesystem::path teffToBvFile; + }; +#include "exoplanetsdatapreparationtask_codegen.cpp" } // namespace namespace openspace::exoplanets { +documentation::Documentation ExoplanetsDataPreparationTask::documentation() { + documentation::Documentation doc = codegen::doc(); + doc.id = "exoplanets_data_preparation_task"; + return doc; +} + ExoplanetsDataPreparationTask::ExoplanetsDataPreparationTask( const ghoul::Dictionary& dictionary) { - openspace::documentation::testSpecificationAndThrow( - documentation(), - dictionary, - "ExoplanetsDataPreparationTask" - ); + const Parameters p = codegen::bake(dictionary); - _inputDataPath = absPath(dictionary.value(KeyInputDataFile)); - _inputSpeckPath = absPath(dictionary.value(KeyInputSpeck)); - _outputBinPath = absPath(dictionary.value(KeyOutputBin)); - _outputLutPath = absPath(dictionary.value(KeyOutputLut)); - _teffToBvFilePath = absPath(dictionary.value(KeyTeffToBv)); + _inputDataPath = absPath(p.inputDataFile.string()); + _inputSpeckPath = absPath(p.inputSPECK.string()); + _outputBinPath = absPath(p.outputBIN); + _outputLutPath = absPath(p.outputLUT); + _teffToBvFilePath = absPath(p.teffToBvFile.string()); } std::string ExoplanetsDataPreparationTask::description() { return fmt::format( - "Extract data about exoplanets from file '{}' and write as bin to '{}'. The data " + "Extract data about exoplanets from file {} and write as bin to {}. The data " "file should be a csv version of the Planetary Systems Composite Data from the " "NASA exoplanets archive (https://exoplanetarchive.ipac.caltech.edu/).", _inputDataPath, _outputBinPath @@ -78,7 +94,7 @@ void ExoplanetsDataPreparationTask::perform( { std::ifstream inputDataFile(_inputDataPath); if (!inputDataFile.good()) { - LERROR(fmt::format("Failed to open input file '{}'", _inputDataPath)); + LERROR(fmt::format("Failed to open input file {}", _inputDataPath)); return; } @@ -441,45 +457,4 @@ float ExoplanetsDataPreparationTask::bvFromTeff(float teff) { return bv; } -documentation::Documentation ExoplanetsDataPreparationTask::documentation() { - using namespace documentation; - return { - "ExoplanetsDataPreparationTask", - "exoplanets_data_preparation_task", - { - { - KeyInputDataFile, - new FileVerifier, - Optional::No, - "The csv file to extract data from" - }, - { - KeyInputSpeck, - new FileVerifier, - Optional::No, - "The speck file with star locations" - }, - { - KeyOutputBin, - new StringAnnotationVerifier("A valid filepath"), - Optional::No, - "The bin file to export data into" - }, - { - KeyOutputLut, - new StringAnnotationVerifier("A valid filepath"), - Optional::No, - "The txt file to write look-up table into" - }, - { - KeyTeffToBv, - new FileVerifier, - Optional::No, - "The path to a teff to bv conversion file. Should be a txt file where " - "each line has the format 'teff,bv'" - } - } - }; -} - } // namespace openspace::exoplanets diff --git a/modules/exoplanets/tasks/exoplanetsdatapreparationtask.h b/modules/exoplanets/tasks/exoplanetsdatapreparationtask.h index ec3c7d823e..e5db06d53f 100644 --- a/modules/exoplanets/tasks/exoplanetsdatapreparationtask.h +++ b/modules/exoplanets/tasks/exoplanetsdatapreparationtask.h @@ -25,8 +25,10 @@ #ifndef __OPENSPACE_MODULE_EXOPLANETS___EXOPLANETSDATAPREPARATIONTASK___H__ #define __OPENSPACE_MODULE_EXOPLANETS___EXOPLANETSDATAPREPARATIONTASK___H__ -#include #include + +#include +#include #include namespace openspace::exoplanets { @@ -39,11 +41,11 @@ public: static documentation::Documentation documentation(); private: - std::string _inputDataPath; - std::string _inputSpeckPath; - std::string _outputBinPath; - std::string _outputLutPath; - std::string _teffToBvFilePath; + std::filesystem::path _inputDataPath; + std::filesystem::path _inputSpeckPath; + std::filesystem::path _outputBinPath; + std::filesystem::path _outputLutPath; + std::filesystem::path _teffToBvFilePath; glm::vec3 starPosition(const std::string& starName); diff --git a/modules/fieldlines/rendering/renderablefieldlines.cpp b/modules/fieldlines/rendering/renderablefieldlines.cpp index e71c1e4bb3..d07530d03c 100644 --- a/modules/fieldlines/rendering/renderablefieldlines.cpp +++ b/modules/fieldlines/rendering/renderablefieldlines.cpp @@ -192,7 +192,7 @@ void RenderableFieldlines::initializeDefaultPropertyValues() { std::string seedPointSourceFile = _seedPointsInfo.value( KeySeedPointsFile ); - _seedPointSourceFile = absPath(seedPointSourceFile); + _seedPointSourceFile = absPath(seedPointSourceFile).string(); } } else if (sourceType == SeedPointsSourceTable) { @@ -435,7 +435,7 @@ RenderableFieldlines::generateFieldlinesVolumeKameleon() return {}; } std::string fileName = _vectorFieldInfo.value(KeyVectorFieldFile); - fileName = absPath(fileName); + fileName = absPath(fileName).string(); //KameleonWrapper::Model modelType; if (model != VectorFieldKameleonModelBATSRUS) { diff --git a/modules/fieldlinessequence/fieldlinessequencemodule.cpp b/modules/fieldlinessequence/fieldlinessequencemodule.cpp index 35959eb514..9335c9cf8d 100644 --- a/modules/fieldlinessequence/fieldlinessequencemodule.cpp +++ b/modules/fieldlinessequence/fieldlinessequencemodule.cpp @@ -50,7 +50,9 @@ namespace openspace { std::string FieldlinesSequenceModule::DefaultTransferFunctionFile = ""; FieldlinesSequenceModule::FieldlinesSequenceModule() : OpenSpaceModule(Name) { - DefaultTransferFunctionFile = absPath("${TEMPORARY}/default_transfer_function.txt"); + DefaultTransferFunctionFile = absPath( + "${TEMPORARY}/default_transfer_function.txt" + ).string(); std::ofstream file(DefaultTransferFunctionFile); file << DefaultTransferfunctionSource; diff --git a/modules/fieldlinessequence/rendering/renderablefieldlinessequence.cpp b/modules/fieldlinessequence/rendering/renderablefieldlinessequence.cpp index 6857afa9ea..c786dad56b 100644 --- a/modules/fieldlinessequence/rendering/renderablefieldlinessequence.cpp +++ b/modules/fieldlinessequence/rendering/renderablefieldlinessequence.cpp @@ -39,8 +39,9 @@ #include #include #include -//#include -//#include +#include +#include +#include namespace { constexpr const char* _loggerCat = "RenderableFieldlinesSequence"; @@ -310,7 +311,9 @@ void RenderableFieldlinesSequence::initializeGL() { // Set a default color table, just in case the (optional) user defined paths are // corrupt or not provided! _colorTablePaths.push_back(FieldlinesSequenceModule::DefaultTransferFunctionFile); - _transferFunction = std::make_unique(absPath(_colorTablePaths[0])); + _transferFunction = std::make_unique( + absPath(_colorTablePaths[0]).string() + ); // EXTRACT OPTIONAL INFORMATION FROM DICTIONARY std::string outputFolderPath; @@ -434,13 +437,16 @@ bool RenderableFieldlinesSequence::extractMandatoryInfoFromDictionary( // Ensure that the source folder exists and then extract // the files with the same extension as - ghoul::filesystem::Directory sourceFolder(sourceFolderPath); - if (FileSys.directoryExists(sourceFolder)) { + if (std::filesystem::is_directory(sourceFolderPath)) { // Extract all file paths from the provided folder - _sourceFiles = sourceFolder.readFiles( - ghoul::filesystem::Directory::Recursive::No, - ghoul::filesystem::Directory::Sort::Yes - ); + _sourceFiles.clear(); + namespace fs = std::filesystem; + for (const fs::directory_entry& e : fs::directory_iterator(sourceFolderPath)) { + if (e.is_regular_file()) { + _sourceFiles.push_back(e.path().string()); + } + } + std::sort(_sourceFiles.begin(), _sourceFiles.end()); // Remove all files that don't have as extension _sourceFiles.erase( @@ -495,10 +501,9 @@ void RenderableFieldlinesSequence::extractOptionalInfoFromDictionary( } if (_dictionary->hasValue(KeyOutputFolder)) { - std::string temp = _dictionary->value(KeyOutputFolder); - ghoul::filesystem::Directory outputFolder(temp); - if (FileSys.directoryExists(outputFolder)) { - outputFolderPath = absPath(outputFolder); + outputFolderPath = _dictionary->value(KeyOutputFolder); + if (std::filesystem::is_directory(outputFolderPath)) { + outputFolderPath = absPath(outputFolderPath).string(); } else { LERROR(fmt::format( @@ -630,8 +635,9 @@ void RenderableFieldlinesSequence::loadOsflsStatesIntoRAM(const std::string& out if (newState.loadStateFromOsfls(filePath)) { addStateToSequence(newState); if (!outputFolder.empty()) { - ghoul::filesystem::File tmpFile(filePath); - newState.saveStateToJson(outputFolder + tmpFile.baseName()); + newState.saveStateToJson( + outputFolder + std::filesystem::path(filePath).stem().string() + ); } } else { @@ -979,15 +985,13 @@ bool RenderableFieldlinesSequence::extractSeedPointsFromFiles( std::string& path std::vector>& outMap) { std::vector files; - + std::filesystem::path seedPointDir; + if (_dictionary->hasValue(KeyCdfSeedPointDirectory)) { path = _dictionary->value(KeyCdfSeedPointDirectory); - ghoul::filesystem::Directory seedPointDir(path); - if (FileSys.directoryExists(seedPointDir)) { - path = absPath(path); - files = seedPointDir.readFiles( - ghoul::filesystem::Directory::Recursive::No, - ghoul::filesystem::Directory::Sort::Yes); + if (std::filesystem::is_directory(path)){ + seedPointDir = absPath(path); + path = seedPointDir.string(); } else { LERROR(fmt::format( @@ -1003,12 +1007,19 @@ bool RenderableFieldlinesSequence::extractSeedPointsFromFiles( std::string& path return false; } - for (const std::string& seedFilePath : files) { - - if (seedFilePath.find("mp_position") == std::string::npos) { + namespace fs = std::filesystem; + for (const fs::directory_entry& spFile : fs::directory_iterator(seedPointDir)){ + std::string seedFilePath = spFile.path().string(); + if (!spFile.is_regular_file() && seedFilePath.find("mp_position") + == std::string::npos) { continue; } - + + std::ifstream seedFile(spFile); + if (!seedFile.good()) { + LERROR(fmt::format("Could not open seed points file '{}'", seedFilePath)); + return false; + } size_t lastIndex = seedFilePath.find_last_of('.'); std::string name = seedFilePath.substr(0, lastIndex); // remove file extention @@ -1017,12 +1028,6 @@ bool RenderableFieldlinesSequence::extractSeedPointsFromFiles( std::string& path std::string date = name.substr(dateAndTimeSeperator - 8, 8); //8 for yyyymmdd std::string dateAndTime = date + time; - std::ifstream seedFile(FileSys.relativePath(seedFilePath)); - if (!seedFile.good()) { - LERROR(fmt::format("Could not open seed points file '{}'", seedFilePath)); - return false; - } - LDEBUG(fmt::format("Reading seed points from file '{}'", seedFilePath)); std::string line; std::vector outVec; diff --git a/modules/gaia/rendering/renderablegaiastars.cpp b/modules/gaia/rendering/renderablegaiastars.cpp index e89dc71d06..99a3a091ad 100644 --- a/modules/gaia/rendering/renderablegaiastars.cpp +++ b/modules/gaia/rendering/renderablegaiastars.cpp @@ -462,11 +462,11 @@ RenderableGaiaStars::RenderableGaiaStars(const ghoul::Dictionary& dictionary) const Parameters p = codegen::bake(dictionary); - _filePath = absPath(p.file); - _dataFile = std::make_unique(_filePath); - _dataFile->setCallback([&](const File&) { _dataIsDirty = true; }); + _filePath = absPath(p.file).string(); + _dataFile = std::make_unique(_filePath.value()); + _dataFile->setCallback([this]() { _dataIsDirty = true; }); - _filePath.onChange([&]() { _dataIsDirty = true; }); + _filePath.onChange([this]() { _dataIsDirty = true; }); addProperty(_filePath); _fileReaderOption.addOptions({ @@ -567,19 +567,21 @@ RenderableGaiaStars::RenderableGaiaStars(const ghoul::Dictionary& dictionary) }); addProperty(_shaderOption); - _pointSpreadFunctionTexturePath = absPath(p.texture); + _pointSpreadFunctionTexturePath = absPath(p.texture).string(); _pointSpreadFunctionTexturePath.onChange( - [&](){ _pointSpreadFunctionTextureIsDirty = true; } + [this](){ _pointSpreadFunctionTextureIsDirty = true; } + ); + _pointSpreadFunctionFile = std::make_unique( + _pointSpreadFunctionTexturePath.value() ); - _pointSpreadFunctionFile = std::make_unique(_pointSpreadFunctionTexturePath); _pointSpreadFunctionFile->setCallback( - [&](const File&) { _pointSpreadFunctionTextureIsDirty = true; } + [this]() { _pointSpreadFunctionTextureIsDirty = true; } ); - _colorTexturePath = absPath(p.colorMap); - _colorTextureFile = std::make_unique(_colorTexturePath); - _colorTexturePath.onChange([&]() { _colorTextureIsDirty = true; }); - _colorTextureFile->setCallback([&](const File&) { _colorTextureIsDirty = true; }); + _colorTexturePath = absPath(p.colorMap).string(); + _colorTextureFile = std::make_unique(_colorTexturePath.value()); + _colorTexturePath.onChange([this]() { _colorTextureIsDirty = true; }); + _colorTextureFile->setCallback([this]() { _colorTextureIsDirty = true; }); _luminosityMultiplier = p.luminosityMultiplier.value_or(_luminosityMultiplier); _magnitudeBoost = p.magnitudeBoost.value_or(_magnitudeBoost); @@ -1701,10 +1703,10 @@ void RenderableGaiaStars::update(const UpdateData&) { } case gaia::ShaderOption::Billboard_SSBO: case gaia::ShaderOption::Billboard_VBO: { - std::string vs = absPath( + std::filesystem::path vs = absPath( "${MODULE_GAIA}/shaders/gaia_tonemapping_vs.glsl" ); - std::string fs = absPath( + std::filesystem::path fs = absPath( "${MODULE_GAIA}/shaders/gaia_tonemapping_billboard_fs.glsl" ); std::unique_ptr programTM = @@ -2101,12 +2103,12 @@ void RenderableGaiaStars::update(const UpdateData&) { _pointSpreadFunctionTexture = nullptr; if (!_pointSpreadFunctionTexturePath.value().empty()) { _pointSpreadFunctionTexture = ghoul::io::TextureReader::ref().loadTexture( - absPath(_pointSpreadFunctionTexturePath) + absPath(_pointSpreadFunctionTexturePath).string() ); if (_pointSpreadFunctionTexture) { LDEBUG(fmt::format( - "Loaded texture from '{}'", absPath(_pointSpreadFunctionTexturePath) + "Loaded texture from {}", absPath(_pointSpreadFunctionTexturePath) )); _pointSpreadFunctionTexture->uploadTexture(); } @@ -2115,12 +2117,10 @@ void RenderableGaiaStars::update(const UpdateData&) { ); _pointSpreadFunctionFile = std::make_unique( - _pointSpreadFunctionTexturePath + _pointSpreadFunctionTexturePath.value() ); _pointSpreadFunctionFile->setCallback( - [&](const ghoul::filesystem::File&) { - _pointSpreadFunctionTextureIsDirty = true; - } + [this]() { _pointSpreadFunctionTextureIsDirty = true; } ); } _pointSpreadFunctionTextureIsDirty = false; @@ -2131,7 +2131,7 @@ void RenderableGaiaStars::update(const UpdateData&) { _colorTexture = nullptr; if (!_colorTexturePath.value().empty()) { _colorTexture = ghoul::io::TextureReader::ref().loadTexture( - absPath(_colorTexturePath) + absPath(_colorTexturePath).string() ); if (_colorTexture) { LDEBUG(fmt::format( @@ -2141,11 +2141,9 @@ void RenderableGaiaStars::update(const UpdateData&) { } _colorTextureFile = std::make_unique( - _colorTexturePath - ); - _colorTextureFile->setCallback( - [&](const ghoul::filesystem::File&) { _colorTextureIsDirty = true; } + _colorTexturePath.value() ); + _colorTextureFile->setCallback([this]() { _colorTextureIsDirty = true; }); } _colorTextureIsDirty = false; } diff --git a/modules/gaia/tasks/constructoctreetask.cpp b/modules/gaia/tasks/constructoctreetask.cpp index 7fefa17517..8b03780c94 100644 --- a/modules/gaia/tasks/constructoctreetask.cpp +++ b/modules/gaia/tasks/constructoctreetask.cpp @@ -28,9 +28,9 @@ #include #include #include -#include #include #include +#include #include #include @@ -295,8 +295,10 @@ ConstructOctreeTask::ConstructOctreeTask(const ghoul::Dictionary& dictionary) { } std::string ConstructOctreeTask::description() { - return "Read bin file (or files in folder): " + _inFileOrFolderPath + "\n " - "and write octree data file (or files) into: " + _outFileOrFolderPath + "\n"; + return fmt::format( + "Read bin file (or files in folder): {} and write octree data file (or files) " + "into: {}", _inFileOrFolderPath, _outFileOrFolderPath + ); } void ConstructOctreeTask::perform(const Task::ProgressCallback& onProgress) { @@ -323,7 +325,7 @@ void ConstructOctreeTask::constructOctreeFromSingleFile( _octreeManager->initOctree(0, _maxDist, _maxStarsPerNode); - LINFO("Reading data file: " + _inFileOrFolderPath); + LINFO(fmt::format("Reading data file: {}", _inFileOrFolderPath)); LINFO(fmt::format( "MAX DIST: {} - MAX STARS PER NODE: {}", @@ -404,8 +406,7 @@ void ConstructOctreeTask::constructOctreeFromSingleFile( } else { LERROR(fmt::format( - "Error opening file '{}' for loading preprocessed file!", - _inFileOrFolderPath + "Error opening file {} for loading preprocessed file", _inFileOrFolderPath )); } LINFO(fmt::format("{} of {} read stars were filtered", nFilteredStars, nTotalStars)); @@ -413,7 +414,7 @@ void ConstructOctreeTask::constructOctreeFromSingleFile( // Slice LOD data before writing to files. _octreeManager->sliceLodData(); - LINFO("Writing octree to: " + _outFileOrFolderPath); + LINFO(fmt::format("Writing octree to: {}", _outFileOrFolderPath)); std::ofstream outFileStream(_outFileOrFolderPath, std::ofstream::binary); if (outFileStream.good()) { if (nValues == 0) { @@ -425,7 +426,7 @@ void ConstructOctreeTask::constructOctreeFromSingleFile( } else { LERROR(fmt::format( - "Error opening file: {} as output data file.", _outFileOrFolderPath + "Error opening file: {} as output data file", _outFileOrFolderPath )); } } @@ -452,8 +453,16 @@ void ConstructOctreeTask::constructOctreeFromFolder( //int starsOutside2000 = 0; //int starsOutside5000 = 0; - ghoul::filesystem::Directory currentDir(_inFileOrFolderPath); - std::vector allInputFiles = currentDir.readFiles(); + std::vector allInputFiles; + if (std::filesystem::is_directory(_inFileOrFolderPath)) { + namespace fs = std::filesystem; + for (const fs::directory_entry& e : fs::directory_iterator(_inFileOrFolderPath)) { + if (!e.is_regular_file()) { + allInputFiles.push_back(e.path()); + } + } + } + std::vector filterValues; auto writeThreads = std::vector(8); @@ -467,10 +476,10 @@ void ConstructOctreeTask::constructOctreeFromFolder( )); for (size_t idx = 0; idx < allInputFiles.size(); ++idx) { - std::string inFilePath = allInputFiles[idx]; + std::filesystem::path inFilePath = allInputFiles[idx]; int nStarsInfile = 0; - LINFO("Reading data file: " + inFilePath); + LINFO(fmt::format("Reading data file: {}", inFilePath)); std::ifstream inFileStream(inFilePath, std::ifstream::binary); if (inFileStream.good()) { @@ -528,7 +537,7 @@ void ConstructOctreeTask::constructOctreeFromFolder( } else { LERROR(fmt::format( - "Error opening file '{}' for loading preprocessed file!", inFilePath + "Error opening file {} for loading preprocessed file!", inFilePath )); } @@ -552,7 +561,7 @@ void ConstructOctreeTask::constructOctreeFromFolder( std::thread t( &OctreeManager::writeToMultipleFiles, _indexOctreeManager, - _outFileOrFolderPath, + _outFileOrFolderPath.string(), idx ); writeThreads[idx] = std::move(t); @@ -582,17 +591,19 @@ void ConstructOctreeTask::constructOctreeFromFolder( // " - 5000kPc is " + std::to_string(starsOutside5000)); // Write index file of Octree structure. - std::string indexFileOutPath = _outFileOrFolderPath + "index.bin"; + std::filesystem::path indexFileOutPath = fmt::format( + "{}/index.bin", _outFileOrFolderPath.string() + ); std::ofstream outFileStream(indexFileOutPath, std::ofstream::binary); if (outFileStream.good()) { - LINFO("Writing index file!"); + LINFO("Writing index file"); _indexOctreeManager->writeToFile(outFileStream, false); outFileStream.close(); } else { LERROR(fmt::format( - "Error opening file: {} as index output file.", indexFileOutPath + "Error opening file: {} as index output file", indexFileOutPath )); } diff --git a/modules/gaia/tasks/constructoctreetask.h b/modules/gaia/tasks/constructoctreetask.h index e4be13457d..75701fd2a2 100644 --- a/modules/gaia/tasks/constructoctreetask.h +++ b/modules/gaia/tasks/constructoctreetask.h @@ -29,6 +29,7 @@ #include #include +#include namespace openspace { @@ -78,8 +79,8 @@ private: */ bool filterStar(const glm::vec2& range, float filterValue, float normValue = 0.f); - std::string _inFileOrFolderPath; - std::string _outFileOrFolderPath; + std::filesystem::path _inFileOrFolderPath; + std::filesystem::path _outFileOrFolderPath; int _maxDist = 0; int _maxStarsPerNode = 0; bool _singleFileInput = false; diff --git a/modules/gaia/tasks/readfitstask.cpp b/modules/gaia/tasks/readfitstask.cpp index 865433da6a..6404f576b2 100644 --- a/modules/gaia/tasks/readfitstask.cpp +++ b/modules/gaia/tasks/readfitstask.cpp @@ -30,10 +30,9 @@ #include #include -#include #include #include - +#include #include #include #include @@ -132,7 +131,7 @@ void ReadFitsTask::readSingleFitsFile(const Task::ProgressCallback& progressCall FitsFileReader fileReader(false); std::vector fullData = fileReader.readFitsFile( - _inFileOrFolderPath, + _inFileOrFolderPath.string(), nValuesPerStar, _firstRow, _lastRow, @@ -166,7 +165,7 @@ void ReadFitsTask::readSingleFitsFile(const Task::ProgressCallback& progressCall } else { LERROR(fmt::format( - "Error opening file: {} as output data file.", _outFileOrFolderPath + "Error opening file: {} as output data file", _outFileOrFolderPath )); } } @@ -185,8 +184,16 @@ void ReadFitsTask::readAllFitsFilesFromFolder(const Task::ProgressCallback&) { ConcurrentJobManager>> jobManager(threadPool); // Get all files in specified folder. - ghoul::filesystem::Directory currentDir(_inFileOrFolderPath); - std::vector allInputFiles = currentDir.readFiles(); + std::vector allInputFiles; + if (std::filesystem::is_directory(_inFileOrFolderPath)) { + namespace fs = std::filesystem; + for (const fs::directory_entry& e : fs::directory_iterator(_inFileOrFolderPath)) { + if (e.is_regular_file()) { + allInputFiles.push_back(e.path()); + } + } + } + size_t nInputFiles = allInputFiles.size(); LINFO("Files to read: " + std::to_string(nInputFiles)); @@ -238,12 +245,12 @@ void ReadFitsTask::readAllFitsFilesFromFolder(const Task::ProgressCallback&) { // Divide all files into ReadFilejobs and then delegate them onto several threads! while (!allInputFiles.empty()) { - std::string fileToRead = allInputFiles.back(); + std::filesystem::path fileToRead = allInputFiles.back(); allInputFiles.erase(allInputFiles.end() - 1); // Add reading of file to jobmanager, which will distribute it to our threadpool. auto readFileJob = std::make_shared( - fileToRead, + fileToRead.string(), _allColumnNames, _firstRow, _lastRow, @@ -294,7 +301,9 @@ void ReadFitsTask::readAllFitsFilesFromFolder(const Task::ProgressCallback&) { int ReadFitsTask::writeOctantToFile(const std::vector& octantData, int index, std::vector& isFirstWrite, int nValuesPerStar) { - std::string outPath = fmt::format("{}octant_{}.bin", _outFileOrFolderPath, index); + std::string outPath = fmt::format( + "{}octant_{}.bin", _outFileOrFolderPath.string(), index + ); std::ofstream fileStream(outPath, std::ofstream::binary | std::ofstream::app); if (fileStream.good()) { int32_t nValues = static_cast(octantData.size()); diff --git a/modules/gaia/tasks/readfitstask.h b/modules/gaia/tasks/readfitstask.h index ee965cc71c..82568057a1 100644 --- a/modules/gaia/tasks/readfitstask.h +++ b/modules/gaia/tasks/readfitstask.h @@ -26,9 +26,11 @@ #define __OPENSPACE_MODULE_GAIA___READFITSTASK___H__ #include + #include #include #include +#include namespace openspace { @@ -67,8 +69,8 @@ private: int writeOctantToFile(const std::vector& data, int index, std::vector& isFirstWrite, int nValuesPerStar); - std::string _inFileOrFolderPath; - std::string _outFileOrFolderPath; + std::filesystem::path _inFileOrFolderPath; + std::filesystem::path _outFileOrFolderPath; bool _singleFileProcess = false; size_t _threadsToUse = 1; int _firstRow = 0; diff --git a/modules/gaia/tasks/readspecktask.cpp b/modules/gaia/tasks/readspecktask.cpp index 04526a33d9..dc54605621 100644 --- a/modules/gaia/tasks/readspecktask.cpp +++ b/modules/gaia/tasks/readspecktask.cpp @@ -72,7 +72,10 @@ void ReadSpeckTask::perform(const Task::ProgressCallback& onProgress) { int32_t nRenderValues = 0; FitsFileReader fileReader(false); - std::vector fullData = fileReader.readSpeckFile(_inFilePath, nRenderValues); + std::vector fullData = fileReader.readSpeckFile( + _inFilePath.string(), + nRenderValues + ); onProgress(0.9f); diff --git a/modules/gaia/tasks/readspecktask.h b/modules/gaia/tasks/readspecktask.h index f42b07a81f..ce1a1bb446 100644 --- a/modules/gaia/tasks/readspecktask.h +++ b/modules/gaia/tasks/readspecktask.h @@ -27,6 +27,7 @@ #include +#include #include namespace openspace { @@ -43,8 +44,8 @@ public: static documentation::Documentation Documentation(); private: - std::string _inFilePath; - std::string _outFilePath; + std::filesystem::path _inFilePath; + std::filesystem::path _outFilePath; }; } // namespace openspace diff --git a/modules/galaxy/rendering/renderablegalaxy.cpp b/modules/galaxy/rendering/renderablegalaxy.cpp index 50cff4ca7a..0b3ee6bcd9 100644 --- a/modules/galaxy/rendering/renderablegalaxy.cpp +++ b/modules/galaxy/rendering/renderablegalaxy.cpp @@ -322,10 +322,9 @@ void RenderableGalaxy::initialize() { _volume = reader.read(); std::string cachedPointsFile = FileSys.cacheManager()->cachedFilename( - _pointsFilename, - ghoul::filesystem::CacheManager::Persistent::Yes + _pointsFilename ); - const bool hasCachedFile = FileSys.fileExists(cachedPointsFile); + const bool hasCachedFile = std::filesystem::is_regular_file(cachedPointsFile); if (hasCachedFile) { LINFO(fmt::format("Cached file '{}' used for galaxy point file '{}'", cachedPointsFile, _pointsFilename @@ -414,13 +413,12 @@ void RenderableGalaxy::initializeGL() { if (!_pointSpreadFunctionTexturePath.empty()) { _pointSpreadFunctionTexture = ghoul::io::TextureReader::ref().loadTexture( - absPath(_pointSpreadFunctionTexturePath) + absPath(_pointSpreadFunctionTexturePath).string() ); if (_pointSpreadFunctionTexture) { LDEBUG(fmt::format( - "Loaded texture from '{}'", - absPath(_pointSpreadFunctionTexturePath) + "Loaded texture from {}", absPath(_pointSpreadFunctionTexturePath) )); _pointSpreadFunctionTexture->uploadTexture(); } diff --git a/modules/globebrowsing/src/gdalwrapper.cpp b/modules/globebrowsing/src/gdalwrapper.cpp index 0e845581da..e41d0dbf0f 100644 --- a/modules/globebrowsing/src/gdalwrapper.cpp +++ b/modules/globebrowsing/src/gdalwrapper.cpp @@ -105,8 +105,11 @@ GdalWrapper::GdalWrapper(size_t maximumCacheSize, size_t maximumMaximumCacheSize addProperty(_gdalMaximumCacheSize); GDALAllRegister(); - CPLSetConfigOption("GDAL_DATA", absPath("${MODULE_GLOBEBROWSING}/gdal_data").c_str()); - CPLSetConfigOption("CPL_TMPDIR", absPath("${BASE}").c_str()); + CPLSetConfigOption( + "GDAL_DATA", + absPath("${MODULE_GLOBEBROWSING}/gdal_data").string().c_str() + ); + CPLSetConfigOption("CPL_TMPDIR", absPath("${BASE}").string().c_str()); CPLSetConfigOption("GDAL_HTTP_UNSAFESSL", "YES"); CPLSetConfigOption("GDAL_HTTP_TIMEOUT", "3"); // 3 seconds diff --git a/modules/globebrowsing/src/globelabelscomponent.cpp b/modules/globebrowsing/src/globelabelscomponent.cpp index 185dc0ce6b..7a09a11a7c 100644 --- a/modules/globebrowsing/src/globelabelscomponent.cpp +++ b/modules/globebrowsing/src/globelabelscomponent.cpp @@ -193,7 +193,7 @@ namespace { std::optional labelsColor [[codegen::color()]]; // [[codegen::verbatim(LabelsOpacityInfo.description)]] - std::optional labelsOpacity [[codegen::inrange(0.f, 1.0)]]; + std::optional labelsOpacity [[codegen::inrange(0.f, 1.f)]]; // [[codegen::verbatim(LabelsFadeInStartingDistanceInfo.description)]] std::optional fadeInStartingDistance; @@ -289,7 +289,7 @@ void GlobeLabelsComponent::initialize(const ghoul::Dictionary& dictionary, return; } - const bool loadSuccess = loadLabelsData(absPath(p.fileName->string())); + const bool loadSuccess = loadLabelsData(absPath(p.fileName->string()).string()); if (!loadSuccess) { return; } @@ -338,12 +338,11 @@ void GlobeLabelsComponent::initializeFonts() { bool GlobeLabelsComponent::loadLabelsData(const std::string& file) { std::string cachedFile = FileSys.cacheManager()->cachedFilename( - ghoul::filesystem::File(file), - "GlobeLabelsComponent|" + identifier(), - ghoul::filesystem::CacheManager::Persistent::Yes + file, + "GlobeLabelsComponent|" + identifier() ); - bool hasCachedFile = FileSys.fileExists(cachedFile); + bool hasCachedFile = std::filesystem::is_regular_file(cachedFile); if (hasCachedFile) { LINFO(fmt::format("Cached file '{}' used for labels file: {}", cachedFile, file)); @@ -476,7 +475,9 @@ bool GlobeLabelsComponent::loadCachedFile(const std::string& file) { if (version != CurrentCacheVersion) { LINFO("The format of the cached file has changed: deleting old cache"); fileStream.close(); - FileSys.deleteFile(file); + if (std::filesystem::is_regular_file(file)) { + std::filesystem::remove(file); + } return false; } @@ -498,8 +499,7 @@ bool GlobeLabelsComponent::saveCachedFile(const std::string& file) const { LERROR(fmt::format("Error opening file '{}' for save cache file", file)); return false; } - fileStream.write(reinterpret_cast(&CurrentCacheVersion), - sizeof(int8_t)); + fileStream.write(reinterpret_cast(&CurrentCacheVersion), sizeof(int8_t)); int32_t nValues = static_cast(_labels.labelsArray.size()); if (nValues == 0) { @@ -525,11 +525,10 @@ void GlobeLabelsComponent::draw(const RenderData& data) { viewTransform; glm::dmat4 mvp = vp * _globe->modelTransform(); - glm::dvec3 globePositionWorld = glm::dvec3(_globe->modelTransform() * - glm::vec4(0.f, 0.f, 0.f, 1.f)); - glm::dvec3 cameraToGlobeDistanceWorld = globePositionWorld - - data.camera.positionVec3(); - double distanceCameraGlobeWorld = glm::length(cameraToGlobeDistanceWorld); + glm::dvec3 globePosWorld = + glm::dvec3(_globe->modelTransform() * glm::vec4(0.f, 0.f, 0.f, 1.f)); + glm::dvec3 camToGlobeDistanceWorld = globePosWorld - data.camera.positionVec3(); + double distanceCameraGlobeWorld = glm::length(camToGlobeDistanceWorld); float varyingOpacity = 1.f; @@ -570,8 +569,7 @@ void GlobeLabelsComponent::draw(const RenderData& data) { void GlobeLabelsComponent::renderLabels(const RenderData& data, const glm::dmat4& modelViewProjectionMatrix, - float distToCamera, - float fadeInVariable + float distToCamera, float fadeInVariable ) { glm::vec4 textColor = glm::vec4( glm::vec3(_labelsColor), @@ -736,10 +734,6 @@ bool GlobeLabelsComponent::isLabelInFrustum(const glm::dmat4& MVMatrix, else if ((glm::dot(nearNormal, position) + nearDistance) < -Radius) { return false; } - // The far plane testing is disabled because the atm has no depth. - /*else if ((glm::dot(farNormal, position) + farDistance) < -Radius) { - return false; - }*/ return true; } diff --git a/modules/globebrowsing/src/globelabelscomponent.h b/modules/globebrowsing/src/globelabelscomponent.h index ce56c80b0d..bf45535604 100644 --- a/modules/globebrowsing/src/globelabelscomponent.h +++ b/modules/globebrowsing/src/globelabelscomponent.h @@ -68,7 +68,6 @@ private: float distToCamera, float fadeInVariable); bool isLabelInFrustum(const glm::dmat4& MVMatrix, const glm::dvec3& position) const; -private: // Labels Structures struct LabelEntry { char feature[256]; @@ -99,14 +98,13 @@ private: properties::FloatProperty _labelsDistanceEPS; properties::OptionProperty _labelAlignmentOption; -private: Labels _labels; // Font std::shared_ptr _font; // Globe - globebrowsing::RenderableGlobe* _globe; + globebrowsing::RenderableGlobe* _globe = nullptr; }; } // namespace openspace diff --git a/modules/globebrowsing/src/rawtiledatareader.cpp b/modules/globebrowsing/src/rawtiledatareader.cpp index 81254ff64d..6b7b56951a 100644 --- a/modules/globebrowsing/src/rawtiledatareader.cpp +++ b/modules/globebrowsing/src/rawtiledatareader.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #ifdef _MSC_VER #pragma warning (push) @@ -450,7 +451,7 @@ void RawTileDataReader::initialize() { if (module.isWMSCachingEnabled()) { ZoneScopedN("WMS Caching") std::string c; - if (FileSys.fileExists(_datasetFilePath)) { + if (std::filesystem::is_regular_file(_datasetFilePath)) { // Only replace the 'content' if the dataset is an XML file and we want to do // caching std::ifstream t(_datasetFilePath); @@ -486,7 +487,7 @@ void RawTileDataReader::initialize() { CPLCreateXMLElementAndValue( cache, "Path", - absPath(module.wmsCacheLocation()).c_str() + absPath(module.wmsCacheLocation()).string().c_str() ); CPLCreateXMLElementAndValue(cache, "Depth", "4"); CPLCreateXMLElementAndValue(cache, "Expires", "315576000"); // 10 years diff --git a/modules/globebrowsing/src/renderableglobe.cpp b/modules/globebrowsing/src/renderableglobe.cpp index 39c561518f..eaf7a178e4 100644 --- a/modules/globebrowsing/src/renderableglobe.cpp +++ b/modules/globebrowsing/src/renderableglobe.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -107,62 +108,57 @@ namespace { constexpr openspace::properties::Property::PropertyInfo ShowChunkEdgeInfo = { "ShowChunkEdges", "Show chunk edges", - "" // @TODO Missing documentation - }; - - constexpr openspace::properties::Property::PropertyInfo ShowChunkBoundsInfo = { - "ShowChunkBounds", - "Show chunk bounds", - "" // @TODO Missing documentation - }; - - constexpr openspace::properties::Property::PropertyInfo HeightResolutionInfo = { - "ShowHeightResolution", - "Show height resolution", - "" // @TODO Missing documentation - }; - - constexpr openspace::properties::Property::PropertyInfo HeightIntensityInfo = { - "ShowHeightIntensities", - "Show height intensities", - "" // @TODO Missing documentation + "If this value is set to 'true', the borders between chunks are shown using a " + "red highlight" }; constexpr openspace::properties::Property::PropertyInfo LevelProjectedAreaInfo = { "LevelByProjectedAreaElseDistance", "Level by projected area (else distance)", - "" // @TODO Missing documentation + "If this value is set to 'true', the tile level is determined by the area " + "projected on screen. If it is 'false', the distance to the center of the tile " + "is used instead." }; constexpr openspace::properties::Property::PropertyInfo ResetTileProviderInfo = { "ResetTileProviders", "Reset tile providers", - "" // @TODO Missing documentation + "If this property is triggered, all tile provides for the globe are reset and " + "data is reloaded from scratch." }; constexpr openspace::properties::Property::PropertyInfo ModelSpaceRenderingInfo = { "ModelSpaceRenderingCutoffLevel", "Model Space Rendering Cutoff Level", - "" // @TODO Missing documentation + "This value determines the tile level that is used as the cut off between " + "rendering tiles using the globe model rendering vs the flat in-game rendering " + "method. This value is a tradeoff between not having precision errors in the " + "rendering and represting a tile as flat or curved." }; constexpr openspace::properties::Property::PropertyInfo DynamicLodIterationCountInfo = { "DynamicLodIterationCount", "Data availability checks before LOD factor impact", - "" // @TODO Missing documentation + "The number of checks that have to fail/succeed in a row before the dynamic " + "level-of-detail adjusts the actual level-of-detail up or down during a session " + "recording" }; constexpr openspace::properties::Property::PropertyInfo PerformShadingInfo = { "PerformShading", "Perform shading", - "" // @TODO Missing documentation + "This value determines whether there should be lighting applied to the surface " + "of the globe. Note that if there is an atmosphere attached to the planet, there " + "is a separate setting to control the shadowing induced by the atmosphere part." }; constexpr openspace::properties::Property::PropertyInfo AccurateNormalsInfo = { "UseAccurateNormals", "Use Accurate Normals", - "" // @TODO Missing documentation + "This value determines whether higher-accuracy normals should be used in the " + "rendering. These normals are calculated based on the height field information " + "and are thus only available if the planet has a height map" }; constexpr openspace::properties::Property::PropertyInfo EclipseInfo = { @@ -200,25 +196,22 @@ namespace { constexpr openspace::properties::Property::PropertyInfo TargetLodScaleFactorInfo = { "TargetLodScaleFactor", "Target Level of Detail Scale Factor", - "" // @TODO Missing documentation + "Determines the targeted level-of-detail of the tiles for this globe. A higher " + "value means that the tiles rendered are a higher resolution for the same " + "distance of the camera to the planet." }; constexpr openspace::properties::Property::PropertyInfo CurrentLodScaleFactorInfo = { "CurrentLodScaleFactor", "Current Level of Detail Scale Factor (Read Only)", - "" // @TODO Missing documentation - }; - - constexpr openspace::properties::Property::PropertyInfo CameraMinHeightInfo = { - "CameraMinHeight", - "Camera Minimum Height", - "" // @TODO Missing documentation + "The currently used scale factor whose target value is deteremined by " + "'TargetLodScaleFactor'." }; constexpr openspace::properties::Property::PropertyInfo OrenNayarRoughnessInfo = { "OrenNayarRoughness", "orenNayarRoughness", - "" // @TODO Missing documentation + "The roughness factor that is used for the Oren-Nayar lighting mode" }; constexpr openspace::properties::Property::PropertyInfo NActiveLayersInfo = { @@ -506,9 +499,6 @@ RenderableGlobe::RenderableGlobe(const ghoul::Dictionary& dictionary) : Renderable(dictionary) , _debugProperties({ BoolProperty(ShowChunkEdgeInfo, false), - BoolProperty(ShowChunkBoundsInfo, false), - BoolProperty(HeightResolutionInfo, false), - BoolProperty(HeightIntensityInfo, false), BoolProperty(LevelProjectedAreaInfo, true), BoolProperty(ResetTileProviderInfo, false), IntProperty(ModelSpaceRenderingInfo, 14, 1, 22), @@ -524,7 +514,6 @@ RenderableGlobe::RenderableGlobe(const ghoul::Dictionary& dictionary) IntProperty(NumberShadowSamplesInfo, 5, 1, 7), FloatProperty(TargetLodScaleFactorInfo, 15.f, 1.f, 50.f), FloatProperty(CurrentLodScaleFactorInfo, 15.f, 1.f, 50.f), - FloatProperty(CameraMinHeightInfo, 100.f, 0.f, 1000.f), FloatProperty(OrenNayarRoughnessInfo, 0.f, 0.f, 1.f), IntProperty(NActiveLayersInfo, 0, 0, OpenGLCap.maxTextureUnits() / 3) }) @@ -605,16 +594,11 @@ RenderableGlobe::RenderableGlobe(const ghoul::Dictionary& dictionary) }); addProperty(_generalProperties.targetLodScaleFactor); addProperty(_generalProperties.currentLodScaleFactor); - addProperty(_generalProperties.cameraMinHeight); addProperty(_generalProperties.orenNayarRoughness); _generalProperties.nActiveLayers.setReadOnly(true); addProperty(_generalProperties.nActiveLayers); _debugPropertyOwner.addProperty(_debugProperties.showChunkEdges); - //_debugPropertyOwner.addProperty(_debugProperties.showChunkBounds); - //_debugPropertyOwner.addProperty(_debugProperties.showChunkAABB); - //_debugPropertyOwner.addProperty(_debugProperties.showHeightResolution); - //_debugPropertyOwner.addProperty(_debugProperties.showHeightIntensities); _debugPropertyOwner.addProperty(_debugProperties.levelByProjectedAreaElseDistance); _debugPropertyOwner.addProperty(_debugProperties.resetTileProviders); _debugPropertyOwner.addProperty(_debugProperties.modelSpaceRenderingCutoffLevel); @@ -628,8 +612,6 @@ RenderableGlobe::RenderableGlobe(const ghoul::Dictionary& dictionary) _generalProperties.eclipseHardShadows.onChange(notifyShaderRecompilation); _generalProperties.performShading.onChange(notifyShaderRecompilation); _debugProperties.showChunkEdges.onChange(notifyShaderRecompilation); - _debugProperties.showHeightResolution.onChange(notifyShaderRecompilation); - _debugProperties.showHeightIntensities.onChange(notifyShaderRecompilation); _layerManager.onChange([&](Layer* l) { _shadersNeedRecompilation = true; @@ -808,13 +790,6 @@ void RenderableGlobe::update(const UpdateData& data) { _localRenderer.program->setUniform("xSegments", _grid.xSegments); - if (_debugProperties.showHeightResolution) { - _localRenderer.program->setUniform( - "vertexResolution", - glm::vec2(_grid.xSegments, _grid.ySegments) - ); - } - ghoul::opengl::updateUniformLocations( *_localRenderer.program, _localRenderer.uniformCache, @@ -827,12 +802,6 @@ void RenderableGlobe::update(const UpdateData& data) { _globalRenderer.program->setUniform("xSegments", _grid.xSegments); - if (_debugProperties.showHeightResolution) { - _globalRenderer.program->setUniform( - "vertexResolution", - glm::vec2(_grid.xSegments, _grid.ySegments) - ); - } // Ellipsoid Radius (Model Space) _globalRenderer.program->setUniform( "radiiSquared", @@ -1220,48 +1189,32 @@ void RenderableGlobe::renderChunks(const RenderData& data, RendererTasks&, } _localRenderer.program->deactivate(); - if (_debugProperties.showChunkBounds) { - for (int i = 0; i < globalCount; ++i) { - debugRenderChunk( - *_globalChunkBuffer[i], - mvp, - _debugProperties.showChunkBounds - ); + if (global::sessionRecording->isSavingFramesDuringPlayback()) { + // If our tile cache is very full, we assume we need to adjust the level of detail + // dynamically to not keep rendering frames with unavailable data + // After certain number of iterations(_debugProperties.dynamicLodIterationCount) of + // unavailable/available data in a row, we assume that a change could be made. + const int iterCount = _debugProperties.dynamicLodIterationCount; + const bool exceededIterations = + static_cast(_iterationsOfUnavailableData) > iterCount; + const float clf = _generalProperties.currentLodScaleFactor; + const float clfMin = _generalProperties.currentLodScaleFactor.minValue(); + const float targetLod = _generalProperties.targetLodScaleFactor; + const bool validLodFactor = clf > clfMin; + if (exceededIterations && validLodFactor) { + _generalProperties.currentLodScaleFactor = + _generalProperties.currentLodScaleFactor - 0.1f; + _iterationsOfUnavailableData = 0; + _lodScaleFactorDirty = true; + } // Make 2 times the iterations with available data to move it up again + else if (static_cast(_iterationsOfAvailableData) > + (iterCount * 2) && clf < targetLod) + { + _generalProperties.currentLodScaleFactor = + _generalProperties.currentLodScaleFactor + 0.1f; + _iterationsOfAvailableData = 0; + _lodScaleFactorDirty = true; } - - for (int i = 0; i < localCount; ++i) { - debugRenderChunk( - *_localChunkBuffer[i], - mvp, - _debugProperties.showChunkBounds - ); - } - } - - // If our tile cache is very full, we assume we need to adjust the level of detail - // dynamically to not keep rendering frames with unavailable data - // After certain number of iterations(_debugProperties.dynamicLodIterationCount) of - // unavailable/available data in a row, we assume that a change could be made. - const int iterCount = _debugProperties.dynamicLodIterationCount; - const bool exceededIterations = - static_cast(_iterationsOfUnavailableData) > iterCount; - const float clf = _generalProperties.currentLodScaleFactor; - const float clfMin = _generalProperties.currentLodScaleFactor.minValue(); - const float targetLod = _generalProperties.targetLodScaleFactor; - const bool validLodFactor = clf > clfMin; - if (exceededIterations && validLodFactor) { - _generalProperties.currentLodScaleFactor = - _generalProperties.currentLodScaleFactor - 0.1f; - _iterationsOfUnavailableData = 0; - _lodScaleFactorDirty = true; - } // Make 2 times the iterations with available data to move it up again - else if (static_cast(_iterationsOfAvailableData) > - (iterCount * 2) && clf < targetLod) - { - _generalProperties.currentLodScaleFactor = - _generalProperties.currentLodScaleFactor + 0.1f; - _iterationsOfAvailableData = 0; - _lodScaleFactorDirty = true; } } @@ -1658,12 +1611,8 @@ void RenderableGlobe::recompileShaders() { std::to_string(_generalProperties.shadowMapping) ); pairs.emplace_back("showChunkEdges", std::to_string(_debugProperties.showChunkEdges)); - pairs.emplace_back("showHeightResolution", - std::to_string(_debugProperties.showHeightResolution) - ); - pairs.emplace_back("showHeightIntensities", - std::to_string(_debugProperties.showHeightIntensities) - ); + pairs.emplace_back("showHeightResolution", "0"); + pairs.emplace_back("showHeightIntensities", "0"); pairs.emplace_back("defaultHeight", std::to_string(DefaultHeight)); @@ -1771,13 +1720,6 @@ void RenderableGlobe::recompileShaders() { _localRenderer.program->setUniform("xSegments", _grid.xSegments); - if (_debugProperties.showHeightResolution) { - _localRenderer.program->setUniform( - "vertexResolution", - glm::vec2(_grid.xSegments, _grid.ySegments) - ); - } - ghoul::opengl::updateUniformLocations( *_localRenderer.program, _localRenderer.uniformCache, @@ -1799,12 +1741,6 @@ void RenderableGlobe::recompileShaders() { _globalRenderer.program->setUniform("xSegments", _grid.xSegments); - if (_debugProperties.showHeightResolution) { - _globalRenderer.program->setUniform( - "vertexResolution", - glm::vec2(_grid.xSegments, _grid.ySegments) - ); - } // Ellipsoid Radius (Model Space) _globalRenderer.program->setUniform( "radiiSquared", diff --git a/modules/globebrowsing/src/renderableglobe.h b/modules/globebrowsing/src/renderableglobe.h index c1ce8d7a98..35247cefe7 100644 --- a/modules/globebrowsing/src/renderableglobe.h +++ b/modules/globebrowsing/src/renderableglobe.h @@ -123,9 +123,6 @@ private: struct { properties::BoolProperty showChunkEdges; - properties::BoolProperty showChunkBounds; - properties::BoolProperty showHeightResolution; - properties::BoolProperty showHeightIntensities; properties::BoolProperty levelByProjectedAreaElseDistance; properties::BoolProperty resetTileProviders; properties::IntProperty modelSpaceRenderingCutoffLevel; @@ -142,7 +139,6 @@ private: properties::IntProperty nShadowSamples; properties::FloatProperty targetLodScaleFactor; properties::FloatProperty currentLodScaleFactor; - properties::FloatProperty cameraMinHeight; properties::FloatProperty orenNayarRoughness; properties::IntProperty nActiveLayers; } _generalProperties; diff --git a/modules/globebrowsing/src/ringscomponent.cpp b/modules/globebrowsing/src/ringscomponent.cpp index e59917235b..07883a709d 100644 --- a/modules/globebrowsing/src/ringscomponent.cpp +++ b/modules/globebrowsing/src/ringscomponent.cpp @@ -257,51 +257,53 @@ void RingsComponent::initialize() { addProperty(_size); if (p.texture.has_value()) { - _texturePath = absPath(p.texture->string()); - _textureFile = std::make_unique(_texturePath); - _texturePath.onChange([&]() { loadTexture(); }); + _texturePath = absPath(p.texture->string()).string(); + _textureFile = std::make_unique(_texturePath.value()); + _texturePath.onChange([this]() { loadTexture(); }); addProperty(_texturePath); - _textureFile->setCallback([&](const File&) { _textureIsDirty = true; }); + _textureFile->setCallback([this]() { _textureIsDirty = true; }); } if (p.textureFwrd.has_value()) { - _textureFwrdPath = absPath(p.textureFwrd->string()); - _textureFileForwards = std::make_unique(_textureFwrdPath); - _textureFwrdPath.onChange([&]() { loadTexture(); }); + _textureFwrdPath = absPath(p.textureFwrd->string()).string(); + _textureFileForwards = std::make_unique(_textureFwrdPath.value()); + _textureFwrdPath.onChange([this]() { loadTexture(); }); addProperty(_textureFwrdPath); - _textureFileForwards->setCallback([&](const File&) { _textureIsDirty = true; }); + _textureFileForwards->setCallback([this]() { _textureIsDirty = true; }); } if (p.textureBckwrd.has_value()) { - _textureBckwrdPath = absPath(p.textureBckwrd->string()); - _textureFileBackwards = std::make_unique(_textureBckwrdPath); - _textureBckwrdPath.onChange([&]() { loadTexture(); }); + _textureBckwrdPath = absPath(p.textureBckwrd->string()).string(); + _textureFileBackwards = std::make_unique(_textureBckwrdPath.value()); + _textureBckwrdPath.onChange([this]() { loadTexture(); }); addProperty(_textureBckwrdPath); - _textureFileBackwards->setCallback([&](const File&) { _textureIsDirty = true; }); + _textureFileBackwards->setCallback([this]() { _textureIsDirty = true; }); } if (p.textureUnlit.has_value()) { - _textureUnlitPath = absPath(p.textureUnlit->string()); - _textureFileUnlit = std::make_unique(_textureUnlitPath); - _textureUnlitPath.onChange([&]() { loadTexture(); }); + _textureUnlitPath = absPath(p.textureUnlit->string()).string(); + _textureFileUnlit = std::make_unique(_textureUnlitPath.value()); + _textureUnlitPath.onChange([this]() { loadTexture(); }); addProperty(_textureUnlitPath); - _textureFileUnlit->setCallback([&](const File&) { _textureIsDirty = true; }); + _textureFileUnlit->setCallback([this]() { _textureIsDirty = true; }); } if (p.textureColor.has_value()) { - _textureColorPath = absPath(p.textureColor->string()); - _textureFileColor = std::make_unique(_textureColorPath); - _textureColorPath.onChange([&]() { loadTexture(); }); + _textureColorPath = absPath(p.textureColor->string()).string(); + _textureFileColor = std::make_unique(_textureColorPath.value()); + _textureColorPath.onChange([this]() { loadTexture(); }); addProperty(_textureColorPath); - _textureFileColor->setCallback([&](const File&) { _textureIsDirty = true; }); + _textureFileColor->setCallback([this]() { _textureIsDirty = true; }); } if (p.textureTransparency.has_value()) { - _textureTransparencyPath = absPath(p.textureTransparency->string()); - _textureFileTransparency = std::make_unique(_textureTransparencyPath); - _textureTransparencyPath.onChange([&]() { loadTexture(); }); + _textureTransparencyPath = absPath(p.textureTransparency->string()).string(); + _textureFileTransparency = std::make_unique( + _textureTransparencyPath.value() + ); + _textureTransparencyPath.onChange([this]() { loadTexture(); }); addProperty(_textureTransparencyPath); - _textureFileTransparency->setCallback([&](const File&) { _textureIsDirty = true; }); + _textureFileTransparency->setCallback([this]() { _textureIsDirty = true; }); } _offset = p.offset.value_or(_offset); @@ -615,7 +617,7 @@ void RingsComponent::loadTexture() { if (!_texturePath.value().empty()) { std::unique_ptr texture = TextureReader::ref().loadTexture( - absPath(_texturePath) + absPath(_texturePath).string() ); if (texture) { @@ -628,16 +630,16 @@ void RingsComponent::loadTexture() { _texture->uploadTexture(); _texture->setFilter(ghoul::opengl::Texture::FilterMode::AnisotropicMipMap); - _textureFile = std::make_unique(_texturePath); - _textureFile->setCallback( - [&](const ghoul::filesystem::File&) { _textureIsDirty = true; } + _textureFile = std::make_unique( + _texturePath.value() ); + _textureFile->setCallback([this]() { _textureIsDirty = true; }); } } if (!_textureFwrdPath.value().empty()) { std::unique_ptr textureForwards = TextureReader::ref().loadTexture( - absPath(_textureFwrdPath) + absPath(_textureFwrdPath).string() ); if (textureForwards) { @@ -655,17 +657,15 @@ void RingsComponent::loadTexture() { ghoul::opengl::Texture::FilterMode::AnisotropicMipMap); _textureFileForwards = std::make_unique( - _textureFwrdPath - ); - _textureFileForwards->setCallback( - [&](const ghoul::filesystem::File&) { _textureIsDirty = true; } + _textureFwrdPath.value() ); + _textureFileForwards->setCallback([this]() { _textureIsDirty = true; }); } } if (!_textureBckwrdPath.value().empty()) { std::unique_ptr textureBackwards = TextureReader::ref().loadTexture( - absPath(_textureBckwrdPath) + absPath(_textureBckwrdPath).string() ); if (textureBackwards) { @@ -683,17 +683,15 @@ void RingsComponent::loadTexture() { ghoul::opengl::Texture::FilterMode::AnisotropicMipMap); _textureFileBackwards = std::make_unique( - _textureBckwrdPath - ); - _textureFileBackwards->setCallback( - [&](const ghoul::filesystem::File&) { _textureIsDirty = true; } + _textureBckwrdPath.value() ); + _textureFileBackwards->setCallback([this]() { _textureIsDirty = true; }); } } if (!_textureUnlitPath.value().empty()) { std::unique_ptr textureUnlit = TextureReader::ref().loadTexture( - absPath(_textureUnlitPath) + absPath(_textureUnlitPath).string() ); if (textureUnlit) { @@ -707,18 +705,18 @@ void RingsComponent::loadTexture() { _textureUnlit = std::move(textureUnlit); _textureUnlit->uploadTexture(); - _textureUnlit->setFilter(ghoul::opengl::Texture::FilterMode::AnisotropicMipMap); + _textureUnlit->setFilter(Texture::FilterMode::AnisotropicMipMap); - _textureFileUnlit = std::make_unique(_textureUnlitPath); - _textureFileUnlit->setCallback( - [&](const ghoul::filesystem::File&) { _textureIsDirty = true; } + _textureFileUnlit = std::make_unique( + _textureUnlitPath.value() ); + _textureFileUnlit->setCallback([this]() { _textureIsDirty = true; }); } } if (!_textureColorPath.value().empty()) { std::unique_ptr textureColor = TextureReader::ref().loadTexture( - absPath(_textureColorPath) + absPath(_textureColorPath).string() ); if (textureColor) { @@ -732,18 +730,18 @@ void RingsComponent::loadTexture() { _textureColor = std::move(textureColor); _textureColor->uploadTexture(); - _textureColor->setFilter(ghoul::opengl::Texture::FilterMode::AnisotropicMipMap); + _textureColor->setFilter(Texture::FilterMode::AnisotropicMipMap); - _textureFileColor = std::make_unique(_textureColorPath); - _textureFileColor->setCallback( - [&](const ghoul::filesystem::File&) { _textureIsDirty = true; } + _textureFileColor = std::make_unique( + _textureColorPath.value() ); + _textureFileColor->setCallback([this]() { _textureIsDirty = true; }); } } if (!_textureTransparencyPath.value().empty()) { std::unique_ptr textureTransparency = TextureReader::ref().loadTexture( - absPath(_textureTransparencyPath) + absPath(_textureTransparencyPath).string() ); if (textureTransparency) { @@ -762,11 +760,9 @@ void RingsComponent::loadTexture() { ); _textureFileTransparency = std::make_unique( - _textureTransparencyPath - ); - _textureFileTransparency->setCallback( - [&](const ghoul::filesystem::File&) { _textureIsDirty = true; } + _textureTransparencyPath.value() ); + _textureFileTransparency->setCallback([this]() { _textureIsDirty = true; }); } } diff --git a/modules/globebrowsing/src/tileprovider.cpp b/modules/globebrowsing/src/tileprovider.cpp index ddf1200ed9..53d75717f3 100644 --- a/modules/globebrowsing/src/tileprovider.cpp +++ b/modules/globebrowsing/src/tileprovider.cpp @@ -44,6 +44,7 @@ #include #include #include +#include #include #include "cpl_minixml.h" @@ -148,6 +149,21 @@ namespace temporal { "This is the path to the XML configuration file that describes the temporal tile " "information." }; + + constexpr openspace::properties::Property::PropertyInfo UseFixedTimeInfo = { + "UseFixedTime", + "Use Fixed Time", + "If this value is enabled, the time-varying timevarying dataset will always use " + "the time that is specified in the 'FixedTime' property, rather than using the " + "actual time from OpenSpace" + }; + + constexpr openspace::properties::Property::PropertyInfo FixedTimeInfo = { + "FixedTime", + "Fixed Time", + "If the 'UseFixedTime' is enabled, this time will be used instead of the actual " + "time taken from OpenSpace for the displayed tiles." + }; } // namespace temporal @@ -344,12 +360,11 @@ std::unique_ptr initTileProvider(TemporalTileProvider& t, const size_t numChars = strlen(temporal::UrlTimePlaceholder); // @FRAGILE: This will only find the first instance. Dangerous if that instance is // commented out ---abock - const std::string timeSpecifiedXml = xmlTemplate.replace(pos, numChars, timekey); - std::string gdalDatasetXml = timeSpecifiedXml; + std::string xml = xmlTemplate.replace(pos, numChars, timekey); - FileSys.expandPathTokens(gdalDatasetXml, IgnoredTokens); + xml = FileSys.expandPathTokens(std::move(xml), IgnoredTokens).string(); - t.initDict.setValue(KeyFilePath, gdalDatasetXml); + t.initDict.setValue(KeyFilePath, xml); return std::make_unique(t.initDict); } @@ -375,18 +390,29 @@ TileProvider* getTileProvider(TemporalTileProvider& t, std::string_view timekey) TileProvider* getTileProvider(TemporalTileProvider& t, const Time& time) { ZoneScoped - Time tCopy(time); - if (t.timeQuantizer.quantize(tCopy, true)) { - char Buffer[22]; - const int size = timeStringify(t.timeFormat, tCopy, Buffer); + if (t.useFixedTime && !t.fixedTime.value().empty()) { try { - return getTileProvider(t, std::string_view(Buffer, size)); + return getTileProvider(t, t.fixedTime.value()); } catch (const ghoul::RuntimeError& e) { LERRORC("TemporalTileProvider", e.message); return nullptr; } } + else { + Time tCopy(time); + if (t.timeQuantizer.quantize(tCopy, true)) { + char Buffer[22]; + const int size = timeStringify(t.timeFormat, tCopy, Buffer); + try { + return getTileProvider(t, std::string_view(Buffer, size)); + } + catch (const ghoul::RuntimeError& e) { + LERRORC("TemporalTileProvider", e.message); + return nullptr; + } + } + } return nullptr; } @@ -487,9 +513,9 @@ bool readFilePath(TemporalTileProvider& t) { } // File path was not a path to a file but a GDAL config or empty - ghoul::filesystem::File f(t.filePath); - if (FileSys.fileExists(f)) { - t.initDict.setValue(temporal::KeyBasePath, f.directoryName()); + std::filesystem::path f(t.filePath.value()); + if (std::filesystem::is_regular_file(f)) { + t.initDict.setValue(temporal::KeyBasePath, f.parent_path().string()); } t.gdalXmlTemplate = consumeTemporalMetaData(t, xml); @@ -831,6 +857,8 @@ TileProviderByLevel::TileProviderByLevel(const ghoul::Dictionary& dictionary) { TemporalTileProvider::TemporalTileProvider(const ghoul::Dictionary& dictionary) : initDict(dictionary) , filePath(temporal::FilePathInfo) + , useFixedTime(temporal::UseFixedTimeInfo, false) + , fixedTime(temporal::FixedTimeInfo) { ZoneScoped @@ -839,6 +867,16 @@ TemporalTileProvider::TemporalTileProvider(const ghoul::Dictionary& dictionary) filePath = dictionary.value(KeyFilePath); addProperty(filePath); + if (dictionary.hasValue(temporal::UseFixedTimeInfo.identifier)) { + useFixedTime = dictionary.value(temporal::UseFixedTimeInfo.identifier); + } + addProperty(useFixedTime); + + if (dictionary.hasValue(temporal::FixedTimeInfo.identifier)) { + fixedTime = dictionary.value(temporal::FixedTimeInfo.identifier); + } + addProperty(fixedTime); + successfulInitialization = readFilePath(*this); if (!successfulInitialization) { diff --git a/modules/globebrowsing/src/tileprovider.h b/modules/globebrowsing/src/tileprovider.h index 2d70e8dd0c..0641739388 100644 --- a/modules/globebrowsing/src/tileprovider.h +++ b/modules/globebrowsing/src/tileprovider.h @@ -34,6 +34,7 @@ #include #include #include +#include #include #include @@ -174,6 +175,8 @@ struct TemporalTileProvider : public TileProvider { ghoul::Dictionary initDict; properties::StringProperty filePath; + properties::BoolProperty useFixedTime; + properties::StringProperty fixedTime; std::string gdalXmlTemplate; std::unordered_map> tileProviderMap; diff --git a/modules/imgui/src/gui.cpp b/modules/imgui/src/gui.cpp index b86622ba16..1329d52063 100644 --- a/modules/imgui/src/gui.cpp +++ b/modules/imgui/src/gui.cpp @@ -38,7 +38,7 @@ #include #include #include - +#include namespace { constexpr const char* _loggerCat = "GUI"; @@ -53,7 +53,7 @@ namespace { constexpr const std::array UniformNames = { "tex", "ortho" }; void addScreenSpaceRenderableLocal(std::string identifier, std::string texturePath) { - if (!FileSys.fileExists(absPath(texturePath))) { + if (!std::filesystem::is_regular_file(absPath(texturePath))) { LWARNING(fmt::format("Could not find image '{}'", texturePath)); return; } @@ -206,8 +206,7 @@ void GUI::deinitialize() { void GUI::initializeGL() { std::string cachedFile = FileSys.cacheManager()->cachedFilename( configurationFile, - "", - ghoul::filesystem::CacheManager::Persistent::Yes + "" ); LDEBUG(fmt::format("Using {} as ImGUI cache location", cachedFile)); @@ -248,9 +247,9 @@ void GUI::initializeGL() { io.KeyMap[ImGuiKey_Y] = static_cast(Key::Y); io.KeyMap[ImGuiKey_Z] = static_cast(Key::Z); - io.Fonts->AddFontFromFileTTF(absPath(GuiFont).c_str(), FontSize); + io.Fonts->AddFontFromFileTTF(absPath(GuiFont).string().c_str(), FontSize); captionFont = io.Fonts->AddFontFromFileTTF( - absPath(GuiFont).c_str(), + absPath(GuiFont).string().c_str(), FontSize * 1.5f ); diff --git a/modules/imgui/src/guiassetcomponent.cpp b/modules/imgui/src/guiassetcomponent.cpp index 92b6eedaa5..d0eedb873f 100644 --- a/modules/imgui/src/guiassetcomponent.cpp +++ b/modules/imgui/src/guiassetcomponent.cpp @@ -30,9 +30,9 @@ #include #include #include - #include #include +#include namespace { std::string assetStateToString(openspace::Asset::State state) { @@ -90,10 +90,10 @@ void GuiAssetComponent::renderTree(const Asset& asset, const std::string& relati using namespace ghoul::filesystem; std::string assetPath = asset.assetFilePath(); - const std::string& assetDirectory = File(assetPath).directoryName(); + std::string assetDirectory = std::filesystem::path(assetPath).parent_path().string(); if (!relativeToPath.empty()) { - assetPath = FileSys.relativePath(assetPath, relativeToPath); + assetPath = std::filesystem::relative(assetPath, relativeToPath).string(); } std::string assetText = assetPath + " " + assetStateToString(asset.state()); diff --git a/modules/iswa/rendering/datacygnet.cpp b/modules/iswa/rendering/datacygnet.cpp index 440be61f3c..6d8e217807 100644 --- a/modules/iswa/rendering/datacygnet.cpp +++ b/modules/iswa/rendering/datacygnet.cpp @@ -276,7 +276,7 @@ void DataCygnet::readTransferFunctions(std::string tfPath) { if (tfFile.is_open()) { std::string line; while (getline(tfFile, line)) { - tfs.emplace_back(absPath(line)); + tfs.emplace_back(absPath(line).string()); } tfFile.close(); diff --git a/modules/iswa/rendering/kameleonplane.cpp b/modules/iswa/rendering/kameleonplane.cpp index 78d718fa5b..82fe6a0145 100644 --- a/modules/iswa/rendering/kameleonplane.cpp +++ b/modules/iswa/rendering/kameleonplane.cpp @@ -131,7 +131,7 @@ void KameleonPlane::initializeGL() { initializeTime(); createGeometry(); - readFieldlinePaths(absPath(_fieldlineIndexFile)); + readFieldlinePaths(absPath(_fieldlineIndexFile).string()); if (_group) { _dataProcessor = _group->dataProcessor(); diff --git a/modules/iswa/util/dataprocessorkameleon.cpp b/modules/iswa/util/dataprocessorkameleon.cpp index 7c0a1b4e1c..37ee4c1381 100644 --- a/modules/iswa/util/dataprocessorkameleon.cpp +++ b/modules/iswa/util/dataprocessorkameleon.cpp @@ -31,6 +31,7 @@ #include #include #include +#include namespace openspace { @@ -158,14 +159,14 @@ void DataProcessorKameleon::setDimensions(glm::size3_t dimensions) { } void DataProcessorKameleon::initializeKameleonWrapper(std::string path) { - const std::string& extension = ghoul::filesystem::File(absPath(path)).fileExtension(); - if (FileSys.fileExists(absPath(path)) && extension == "cdf") { + std::filesystem::path extension = std::filesystem::path(absPath(path)).extension(); + if (std::filesystem::is_regular_file(absPath(path)) && extension == ".cdf") { if (_kw) { _kw->close(); } _kwPath = std::move(path); - _kw = std::make_shared(absPath(_kwPath)); + _kw = std::make_shared(absPath(_kwPath).string()); } } diff --git a/modules/iswa/util/iswamanager.cpp b/modules/iswa/util/iswamanager.cpp index ff0655fdbc..8d1fccebf1 100644 --- a/modules/iswa/util/iswamanager.cpp +++ b/modules/iswa/util/iswamanager.cpp @@ -42,6 +42,7 @@ #include #include #include +#include #include #include "iswamanager_lua.inl" @@ -410,10 +411,9 @@ std::string IswaManager::parseKWToLuaTable(const CdfInfo& info, const std::strin return ""; } - const std::string& extension = - ghoul::filesystem::File(absPath(info.path)).fileExtension(); - if (extension == "cdf") { - KameleonWrapper kw = KameleonWrapper(absPath(info.path)); + std::filesystem::path ext = std::filesystem::path(absPath(info.path)).extension(); + if (ext == ".cdf") { + KameleonWrapper kw = KameleonWrapper(absPath(info.path).string()); std::string parent = kw.parent(); std::string frame = kw.frame(); @@ -587,10 +587,8 @@ void IswaManager::createSphere(MetadataFuture& data) { } void IswaManager::createKameleonPlane(CdfInfo info, std::string cut) { - const std::string& extension = ghoul::filesystem::File( - absPath(info.path) - ).fileExtension(); - if (FileSys.fileExists(absPath(info.path)) && extension == "cdf") { + std::filesystem::path ext = std::filesystem::path(absPath(info.path)).extension(); + if (std::filesystem::is_regular_file(absPath(info.path)) && ext == ".cdf") { if (!info.group.empty()) { std::string type = typeid(KameleonPlane).name(); registerGroup(info.group, type); @@ -621,16 +619,17 @@ void IswaManager::createKameleonPlane(CdfInfo info, std::string cut) { } } else { - LWARNING( absPath(info.path) + " is not a cdf file or can't be found."); + LWARNING( + fmt::format("{} is not a cdf file or can't be found", absPath(info.path)) + ); } } void IswaManager::createFieldline(std::string name, std::string cdfPath, std::string seedPath) { - const std::string& ext = ghoul::filesystem::File(absPath(cdfPath)).fileExtension(); - - if (FileSys.fileExists(absPath(cdfPath)) && ext == "cdf") { + std::filesystem::path ext = std::filesystem::path(absPath(cdfPath)).extension(); + if (std::filesystem::is_regular_file(absPath(cdfPath)) && ext == ".cdf") { std::string luaTable = "{" "Name = '" + name + "'," "Parent = 'Earth'," @@ -700,10 +699,10 @@ ghoul::Event<>& IswaManager::iswaEvent() { } void IswaManager::addCdfFiles(std::string cdfpath) { - cdfpath = absPath(cdfpath); - if (FileSys.fileExists(cdfpath)) { + std::filesystem::path cdf = absPath(cdfpath); + if (std::filesystem::is_regular_file(cdf)) { //std::string basePath = path.substr(0, path.find_last_of("/\\")); - std::ifstream jsonFile(cdfpath); + std::ifstream jsonFile(cdf); if (jsonFile.is_open()) { json cdfGroups = json::parse(jsonFile); @@ -742,7 +741,7 @@ void IswaManager::addCdfFiles(std::string cdfpath) { } } else { - LWARNING(cdfpath + " is not a cdf file or can't be found."); + LWARNING(fmt::format("{} is not a cdf file or can't be found", cdf)); } } diff --git a/modules/kameleon/src/kameleonwrapper.cpp b/modules/kameleon/src/kameleonwrapper.cpp index 5528881746..05927d31d8 100644 --- a/modules/kameleon/src/kameleonwrapper.cpp +++ b/modules/kameleon/src/kameleonwrapper.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #ifdef WIN32 #pragma warning (push) @@ -113,7 +114,7 @@ KameleonWrapper::~KameleonWrapper() { bool KameleonWrapper::open(const std::string& filename) { close(); - if (!FileSys.fileExists(filename)) { + if (!std::filesystem::is_regular_file(filename)) { return false; } diff --git a/modules/kameleonvolume/kameleonvolumereader.cpp b/modules/kameleonvolume/kameleonvolumereader.cpp index 63301ba596..9d4cef1d7c 100644 --- a/modules/kameleonvolume/kameleonvolumereader.cpp +++ b/modules/kameleonvolume/kameleonvolumereader.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #ifdef WIN32 #pragma warning (push) @@ -75,7 +76,7 @@ namespace { namespace openspace::kameleonvolume { KameleonVolumeReader::KameleonVolumeReader(std::string path) : _path(std::move(path)) { - if (!FileSys.fileExists(_path)) { + if (!std::filesystem::is_regular_file(_path)) { throw ghoul::FileNotFoundError(_path); } diff --git a/modules/kameleonvolume/rendering/renderablekameleonvolume.cpp b/modules/kameleonvolume/rendering/renderablekameleonvolume.cpp index b12c28ac9f..2e8ad45922 100644 --- a/modules/kameleonvolume/rendering/renderablekameleonvolume.cpp +++ b/modules/kameleonvolume/rendering/renderablekameleonvolume.cpp @@ -45,6 +45,7 @@ #include #include #include +#include namespace { constexpr const char* _loggerCat = "RenderableKameleonVolume"; @@ -172,7 +173,7 @@ RenderableKameleonVolume::RenderableKameleonVolume(const ghoul::Dictionary& dict } if (dictionary.hasValue(KeySource)) { - _sourcePath = absPath(dictionary.value(KeySource)); + _sourcePath = absPath(dictionary.value(KeySource)).string(); } if (dictionary.hasValue(KeyVariable)) { @@ -323,7 +324,7 @@ bool RenderableKameleonVolume::isCachingEnabled() const { } void RenderableKameleonVolume::load() { - if (!FileSys.fileExists(ghoul::filesystem::File(_sourcePath))) { + if (!std::filesystem::is_regular_file(_sourcePath.value())) { LERROR(fmt::format("File '{}' does not exist", _sourcePath.value())); return; } @@ -331,13 +332,11 @@ void RenderableKameleonVolume::load() { loadFromPath(_sourcePath); return; } - ghoul::filesystem::File sourceFile(_sourcePath); std::string cachePath = FileSys.cacheManager()->cachedFilename( - sourceFile.baseName(), - cacheSuffix(), - ghoul::filesystem::CacheManager::Persistent::Yes + std::filesystem::path(_sourcePath.value()).stem(), + cacheSuffix() ); - if (FileSys.fileExists(cachePath)) { + if (std::filesystem::is_regular_file(cachePath)) { loadRaw(cachePath); } else { @@ -352,15 +351,8 @@ std::string RenderableKameleonVolume::cacheSuffix() const { } void RenderableKameleonVolume::loadFromPath(const std::string& path) { - ghoul::filesystem::File file(path); - std::string extension = file.fileExtension(); - std::transform( - extension.begin(), - extension.end(), - extension.begin(), - [](char v) { return static_cast(tolower(v)); } - ); - if (extension == "cdf") { + std::filesystem::path extension = std::filesystem::path(path).extension(); + if (extension == ".cdf" || extension == ".CDF") { loadCdf(path); } else { diff --git a/modules/kameleonvolume/tasks/kameleondocumentationtask.cpp b/modules/kameleonvolume/tasks/kameleondocumentationtask.cpp index 9e4cac12a5..1b4666e512 100644 --- a/modules/kameleonvolume/tasks/kameleondocumentationtask.cpp +++ b/modules/kameleonvolume/tasks/kameleondocumentationtask.cpp @@ -29,30 +29,39 @@ #include #include #include +#include #include namespace { - constexpr const char* KeyInput = "Input"; - constexpr const char* KeyOutput = "Output"; constexpr const char* MainTemplateFilename = "${WEB}/kameleondocumentation/main.hbs"; constexpr const char* HandlebarsFilename = "${WEB}/common/handlebars-v4.0.5.js"; constexpr const char* JsFilename = "${WEB}/kameleondocumentation/script.js"; constexpr const char* BootstrapFilename = "${WEB}/common/bootstrap.min.css"; constexpr const char* CssFilename = "${WEB}/common/style.css"; + + struct [[codegen::Dictionary(KameleonDocumentationTask)]] Parameters { + // The CDF file to extract data from + std::filesystem::path input; + + // The HTML file to write documentation to + std::string output [[codegen::annotation("A valid filepath")]]; + }; +#include "kameleondocumentationtask_codegen.cpp" } // namespace namespace openspace::kameleonvolume { +documentation::Documentation KameleonDocumentationTask::documentation() { + documentation::Documentation doc = codegen::doc(); + doc.id = "kameleon_documentation_task"; + return doc; +} + KameleonDocumentationTask::KameleonDocumentationTask(const ghoul::Dictionary& dictionary) { - openspace::documentation::testSpecificationAndThrow( - documentation(), - dictionary, - "KameleonDocumentationTask" - ); - - _inputPath = absPath(dictionary.value(KeyInput)); - _outputPath = absPath(dictionary.value(KeyOutput)); + const Parameters p = codegen::bake(dictionary); + _inputPath = absPath(p.input.string()); + _outputPath = absPath(p.output); } std::string KameleonDocumentationTask::description() { @@ -63,14 +72,14 @@ std::string KameleonDocumentationTask::description() { } void KameleonDocumentationTask::perform(const Task::ProgressCallback & progressCallback) { - KameleonVolumeReader reader(_inputPath); + KameleonVolumeReader reader(_inputPath.string()); ghoul::Dictionary kameleonDictionary = reader.readMetaData(); progressCallback(0.33f); ghoul::Dictionary dictionary; dictionary.setValue("kameleon", std::move(kameleonDictionary)); dictionary.setValue("version", std::string(OPENSPACE_VERSION_NUMBER)); - dictionary.setValue("input", _inputPath); + dictionary.setValue("input", _inputPath.string()); std::string json = ghoul::formatJson(dictionary); progressCallback(0.66f); @@ -146,26 +155,4 @@ void KameleonDocumentationTask::perform(const Task::ProgressCallback & progressC progressCallback(1.0f); } -documentation::Documentation KameleonDocumentationTask::documentation() { - using namespace documentation; - return { - "KameleonDocumentationTask", - "kameleon_documentation_task", - { - { - KeyInput, - new StringAnnotationVerifier("A file path to a cdf file"), - Optional::No, - "The cdf file to extract data from" - }, - { - KeyOutput, - new StringAnnotationVerifier("A valid filepath"), - Optional::No, - "The html file to write documentation to" - } - } - }; -} - } // namespace openspace::kameleonvolume diff --git a/modules/kameleonvolume/tasks/kameleondocumentationtask.h b/modules/kameleonvolume/tasks/kameleondocumentationtask.h index 566f28b8a5..b321511fac 100644 --- a/modules/kameleonvolume/tasks/kameleondocumentationtask.h +++ b/modules/kameleonvolume/tasks/kameleondocumentationtask.h @@ -27,6 +27,7 @@ #include +#include #include namespace openspace::kameleonvolume { @@ -41,8 +42,8 @@ public: static documentation::Documentation documentation(); private: - std::string _inputPath; - std::string _outputPath; + std::filesystem::path _inputPath; + std::filesystem::path _outputPath; }; } // namespace openspace::kameleonvolume diff --git a/modules/kameleonvolume/tasks/kameleonmetadatatojsontask.cpp b/modules/kameleonvolume/tasks/kameleonmetadatatojsontask.cpp index ecd1bcccf5..f9ef7dd3d7 100644 --- a/modules/kameleonvolume/tasks/kameleonmetadatatojsontask.cpp +++ b/modules/kameleonvolume/tasks/kameleonmetadatatojsontask.cpp @@ -29,26 +29,34 @@ #include #include #include +#include #include namespace { - constexpr const char* KeyInput = "Input"; - constexpr const char* KeyOutput = "Output"; + struct [[codegen::Dictionary(KameleonMetadataToJsonTask)]] Parameters { + // The CDF file to extract data from + std::filesystem::path input; + + // The JSON file to export data into + std::string output [[codegen::annotation("A valid filepath")]]; + }; +#include "kameleonmetadatatojsontask_codegen.cpp" } // namespace namespace openspace::kameleonvolume { +documentation::Documentation KameleonMetadataToJsonTask::documentation() { + documentation::Documentation doc = codegen::doc(); + doc.id = "kameleon_metadata_to_json_task"; + return doc; +} + KameleonMetadataToJsonTask::KameleonMetadataToJsonTask( const ghoul::Dictionary& dictionary) { - openspace::documentation::testSpecificationAndThrow( - documentation(), - dictionary, - "KameleonMetadataToJsonTask" - ); - - _inputPath = absPath(dictionary.value(KeyInput)); - _outputPath = absPath(dictionary.value(KeyOutput)); + const Parameters p = codegen::bake(dictionary); + _inputPath = absPath(p.input.string()); + _outputPath = absPath(p.output); } std::string KameleonMetadataToJsonTask::description() { @@ -59,7 +67,7 @@ std::string KameleonMetadataToJsonTask::description() { } void KameleonMetadataToJsonTask::perform(const Task::ProgressCallback& progressCallback) { - KameleonVolumeReader reader(_inputPath); + KameleonVolumeReader reader(_inputPath.string()); ghoul::Dictionary dictionary = reader.readMetaData(); progressCallback(0.5f); @@ -69,26 +77,4 @@ void KameleonMetadataToJsonTask::perform(const Task::ProgressCallback& progressC progressCallback(1.0f); } -documentation::Documentation KameleonMetadataToJsonTask::documentation() { - using namespace documentation; - return { - "KameleonMetadataToJsonTask", - "kameleon_metadata_to_json_task", - { - { - KeyInput, - new StringAnnotationVerifier("A file path to a cdf file"), - Optional::No, - "The cdf file to extract data from" - }, - { - KeyOutput, - new StringAnnotationVerifier("A valid filepath"), - Optional::No, - "The JSON file to export data into" - } - } - }; -} - } // namespace openspace::kameleonvolume diff --git a/modules/kameleonvolume/tasks/kameleonmetadatatojsontask.h b/modules/kameleonvolume/tasks/kameleonmetadatatojsontask.h index 2e392116ce..b70385e286 100644 --- a/modules/kameleonvolume/tasks/kameleonmetadatatojsontask.h +++ b/modules/kameleonvolume/tasks/kameleonmetadatatojsontask.h @@ -27,6 +27,7 @@ #include +#include #include namespace openspace::kameleonvolume { @@ -41,8 +42,8 @@ public: static documentation::Documentation documentation(); private: - std::string _inputPath; - std::string _outputPath; + std::filesystem::path _inputPath; + std::filesystem::path _outputPath; }; } // namespace openspace::kameleonvolume diff --git a/modules/kameleonvolume/tasks/kameleonvolumetorawtask.cpp b/modules/kameleonvolume/tasks/kameleonvolumetorawtask.cpp index fd9c342afe..c4b4ef6747 100644 --- a/modules/kameleonvolume/tasks/kameleonvolumetorawtask.cpp +++ b/modules/kameleonvolume/tasks/kameleonvolumetorawtask.cpp @@ -31,13 +31,11 @@ #include #include #include +#include +#include namespace { - constexpr const char* KeyInput = "Input"; - constexpr const char* KeyRawVolumeOutput = "RawVolumeOutput"; - constexpr const char* KeyDictionaryOutput = "DictionaryOutput"; constexpr const char* KeyDimensions = "Dimensions"; - constexpr const char* KeyVariable = "Variable"; constexpr const char* KeyTime = "Time"; constexpr const char* KeyLowerDomainBound = "LowerDomainBound"; constexpr const char* KeyUpperDomainBound = "UpperDomainBound"; @@ -46,92 +44,64 @@ namespace { constexpr const char* KeyMaxValue = "MaxValue"; constexpr const char* KeyVisUnit = "VisUnit"; + + struct [[codegen::Dictionary(KameleonVolumeToRawTask)]] Parameters { + // The cdf file to extract data from + std::filesystem::path input; + + // The raw volume file to export data to + std::string rawVolumeOutput [[codegen::annotation("A valid filepath")]]; + + // The Lua dictionary file to export metadata to + std::string dictionaryOutput [[codegen::annotation("A valid filepath")]]; + + // The variable name to read from the kameleon dataset + std::string variable [[codegen::annotation("A valid kameleon variable")]]; + + // A vector representing the number of cells in each dimension + glm::ivec3 dimensions; + + // A vector representing the lower bound of the domain, in the native kameleon + // grid units + std::optional lowerDomainBound; + + // A vector representing the lower bound of the domain, in the native kameleon + // grid units + std::optional upperDomainBound; + + // The unit of the data + std::optional visUnit + [[codegen::annotation("A valid kameleon unit")]]; + }; +#include "kameleonvolumetorawtask_codegen.cpp" } // namespace namespace openspace::kameleonvolume { documentation::Documentation KameleonVolumeToRawTask::documentation() { - using namespace documentation; - return { - "KameleonVolumeToRawTask", - "kameleon_metadata_to_json_task", - { - { - KeyInput, - new StringAnnotationVerifier("A file path to a cdf file"), - Optional::No, - "The cdf file to extract data from", - }, - { - KeyRawVolumeOutput, - new StringAnnotationVerifier("A valid filepath"), - Optional::No, - "The raw volume file to export data to", - }, - { - KeyDictionaryOutput, - new StringAnnotationVerifier("A valid filepath"), - Optional::No, - "The lua dictionary file to export metadata to", - }, - { - KeyVariable, - new StringAnnotationVerifier("A valid kameleon variable"), - Optional::No, - "The variable name to read from the kameleon dataset", - }, - { - KeyDimensions, - new DoubleVector3Verifier, - Optional::No, - "A vector representing the number of cells in each dimension", - }, - { - KeyLowerDomainBound, - new DoubleVector3Verifier, - Optional::Yes, - "A vector representing the lower bound of the domain, " - "in the native kameleon grid units", - }, - { - KeyUpperDomainBound, - new DoubleVector3Verifier, - Optional::Yes, - "A vector representing the lower bound of the domain, " - "in the native kameleon grid units" - }, - { - KeyVisUnit, - new StringAnnotationVerifier("A valid kameleon unit"), - Optional::Yes, - "The unit of the data", - } - } - }; + documentation::Documentation doc = codegen::doc(); + doc.id = "kameleon_metadata_to_json_task"; + return doc; } - KameleonVolumeToRawTask::KameleonVolumeToRawTask(const ghoul::Dictionary& dictionary) { - openspace::documentation::testSpecificationAndThrow( - documentation(), - dictionary, - "KameleonVolumeToRawTask" - ); + const Parameters p = codegen::bake(dictionary); - _inputPath = absPath(dictionary.value(KeyInput)); - _rawVolumeOutputPath = absPath(dictionary.value(KeyRawVolumeOutput)); - _dictionaryOutputPath = absPath(dictionary.value(KeyDictionaryOutput)); - _variable = dictionary.value(KeyVariable); - _dimensions = glm::uvec3(dictionary.value(KeyDimensions)); + _inputPath = absPath(p.input.string()); + _rawVolumeOutputPath = absPath(p.rawVolumeOutput); + _dictionaryOutputPath = absPath(p.dictionaryOutput); + _variable = p.variable; + _dimensions = p.dimensions; - if (dictionary.hasKey(KeyLowerDomainBound)) { - _lowerDomainBound = dictionary.value(KeyLowerDomainBound); + if (p.lowerDomainBound.has_value()) { + _lowerDomainBound = *p.lowerDomainBound; } else { _autoDomainBounds = true; } - if (dictionary.hasKey(KeyUpperDomainBound)) { - _upperDomainBound = dictionary.value(KeyUpperDomainBound); + + if (p.upperDomainBound.has_value()) { + _upperDomainBound = *p.upperDomainBound; } else { _autoDomainBounds = true; @@ -147,7 +117,7 @@ std::string KameleonVolumeToRawTask::description() { } void KameleonVolumeToRawTask::perform(const Task::ProgressCallback& progressCallback) { - KameleonVolumeReader reader(_inputPath); + KameleonVolumeReader reader(_inputPath.string()); std::array variables = reader.gridVariableNames(); @@ -174,7 +144,7 @@ void KameleonVolumeToRawTask::perform(const Task::ProgressCallback& progressCall progressCallback(0.5f); - volume::RawVolumeWriter writer(_rawVolumeOutputPath); + volume::RawVolumeWriter writer(_rawVolumeOutputPath.string()); writer.write(*rawVolume); progressCallback(0.9f); diff --git a/modules/kameleonvolume/tasks/kameleonvolumetorawtask.h b/modules/kameleonvolume/tasks/kameleonvolumetorawtask.h index b187bfcc59..040f9c998f 100644 --- a/modules/kameleonvolume/tasks/kameleonvolumetorawtask.h +++ b/modules/kameleonvolume/tasks/kameleonvolumetorawtask.h @@ -28,6 +28,7 @@ #include #include +#include #include namespace openspace::kameleonvolume { @@ -42,9 +43,9 @@ public: static documentation::Documentation documentation(); private: - std::string _inputPath; - std::string _rawVolumeOutputPath; - std::string _dictionaryOutputPath; + std::filesystem::path _inputPath; + std::filesystem::path _rawVolumeOutputPath; + std::filesystem::path _dictionaryOutputPath; std::string _variable; std::string _units; diff --git a/modules/multiresvolume/rendering/renderablemultiresvolume.cpp b/modules/multiresvolume/rendering/renderablemultiresvolume.cpp index 6e75384f79..fef5801605 100644 --- a/modules/multiresvolume/rendering/renderablemultiresvolume.cpp +++ b/modules/multiresvolume/rendering/renderablemultiresvolume.cpp @@ -54,6 +54,7 @@ #include #include #include +#include #include #include @@ -170,7 +171,7 @@ RenderableMultiresVolume::RenderableMultiresVolume(const ghoul::Dictionary& dict , _scaling(ScalingInfo, glm::vec3(1.f), glm::vec3(0.f), glm::vec3(10.f)) { if (dictionary.hasValue(KeyDataSource)) { - _filename = absPath(dictionary.value(KeyDataSource)); + _filename = absPath(dictionary.value(KeyDataSource)).string(); } else { LERROR(fmt::format("Node did not contain a valid '{}'", KeyDataSource)); @@ -222,7 +223,7 @@ RenderableMultiresVolume::RenderableMultiresVolume(const ghoul::Dictionary& dict if (dictionary.hasValue(KeyTransferFunction)) { _transferFunctionPath = absPath( dictionary.value(KeyTransferFunction) - ); + ).string(); _transferFunction = std::make_shared(_transferFunctionPath); } else { @@ -454,14 +455,12 @@ bool RenderableMultiresVolume::initializeSelector() { switch (_selector) { case Selector::TF: if (_errorHistogramManager) { - std::stringstream cacheName; - ghoul::filesystem::File f = _filename; - cacheName << f.baseName() << "_" << nHistograms << "_errorHistograms"; - std::string cacheFilename; - cacheFilename = FileSys.cacheManager()->cachedFilename( - cacheName.str(), - "", - ghoul::filesystem::CacheManager::Persistent::Yes + std::string cacheFilename = FileSys.cacheManager()->cachedFilename( + fmt::format( + "{}_{}_errorHistograms", + std::filesystem::path(_filename).stem().string(), nHistograms + ), + "" ); std::ifstream cacheFile(cacheFilename, std::ios::in | std::ios::binary); if (cacheFile.is_open()) { @@ -472,12 +471,14 @@ bool RenderableMultiresVolume::initializeSelector() { ); success &= _errorHistogramManager->loadFromFile(cacheFilename); } - else if (_errorHistogramsPath != "") { + else if (!_errorHistogramsPath.empty()) { // Read histograms from scene data. LINFO(fmt::format( "Loading histograms from scene data: {}", _errorHistogramsPath )); - success &= _errorHistogramManager->loadFromFile(_errorHistogramsPath); + success &= _errorHistogramManager->loadFromFile( + _errorHistogramsPath.string() + ); } else { // Build histograms from tsp file. @@ -494,14 +495,11 @@ bool RenderableMultiresVolume::initializeSelector() { case Selector::SIMPLE: if (_histogramManager) { - std::stringstream cacheName; - ghoul::filesystem::File f = _filename; - cacheName << f.baseName() << "_" << nHistograms << "_histograms"; - std::string cacheFilename; - cacheFilename = FileSys.cacheManager()->cachedFilename( - cacheName.str(), - "", - ghoul::filesystem::CacheManager::Persistent::Yes + std::string cacheFilename = FileSys.cacheManager()->cachedFilename( + fmt::format("{}_{}_histogram", + std::filesystem::path(_filename).stem().string(), nHistograms + ), + "" ); std::ifstream cacheFile(cacheFilename, std::ios::in | std::ios::binary); if (cacheFile.is_open()) { @@ -528,12 +526,12 @@ bool RenderableMultiresVolume::initializeSelector() { case Selector::LOCAL: if (_localErrorHistogramManager) { - ghoul::filesystem::File f = _filename; - std::string cacheFilename; - cacheFilename = FileSys.cacheManager()->cachedFilename( - fmt::format("{}_{}_localErrorHistograms", f.baseName(), nHistograms), - "", - ghoul::filesystem::CacheManager::Persistent::Yes + std::string cacheFilename = FileSys.cacheManager()->cachedFilename( + fmt::format( + "{}_{}_localErrorHistograms", + std::filesystem::path(_filename).stem().string(), nHistograms + ), + "" ); std::ifstream cacheFile(cacheFilename, std::ios::in | std::ios::binary); if (cacheFile.is_open()) { @@ -628,8 +626,9 @@ void RenderableMultiresVolume::update(const UpdateData& data) { // Make sure that the directory exists ghoul::filesystem::File file(_statsFileName); - ghoul::filesystem::Directory directory(file.directoryName()); - FileSys.createDirectory(directory, ghoul::filesystem::FileSystem::Recursive::Yes); + std::filesystem::path directory = + std::filesystem::path(_statsFileName).parent_path(); + std::filesystem::create_directories(directory); std::ofstream ofs(_statsFileName, std::ofstream::out); diff --git a/modules/multiresvolume/rendering/renderablemultiresvolume.h b/modules/multiresvolume/rendering/renderablemultiresvolume.h index 28a9d56994..1b4c71914d 100644 --- a/modules/multiresvolume/rendering/renderablemultiresvolume.h +++ b/modules/multiresvolume/rendering/renderablemultiresvolume.h @@ -33,6 +33,7 @@ #include #include #include +#include namespace ghoul { class Dictionary; } namespace ghoul::filesystem { class File; } @@ -122,7 +123,7 @@ private: std::string _volumeName; std::string _transferFunctionPath; - std::string _errorHistogramsPath; + std::filesystem::path _errorHistogramsPath; std::shared_ptr _transferFunction; diff --git a/modules/multiresvolume/rendering/tsp.cpp b/modules/multiresvolume/rendering/tsp.cpp index e056dbaeb4..124d1cea24 100644 --- a/modules/multiresvolume/rendering/tsp.cpp +++ b/modules/multiresvolume/rendering/tsp.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -506,11 +507,9 @@ bool TSP::readCache() { if (!FileSys.cacheManager()) return false; - ghoul::filesystem::File f = _filename; std::string cacheFilename = FileSys.cacheManager()->cachedFilename( - f.baseName(), - "", - ghoul::filesystem::CacheManager::Persistent::Yes + std::filesystem::path(_filename).stem(), + "" ); std::ifstream file(cacheFilename, std::ios::in | std::ios::binary); @@ -546,11 +545,9 @@ bool TSP::writeCache() { return false; } - ghoul::filesystem::File f = _filename; std::string cacheFilename = FileSys.cacheManager()->cachedFilename( - f.baseName(), - "", - ghoul::filesystem::CacheManager::Persistent::Yes + std::filesystem::path(_filename).stem(), + "" ); std::ofstream file(cacheFilename, std::ios::out | std::ios::binary); diff --git a/modules/space/CMakeLists.txt b/modules/space/CMakeLists.txt index 583e3e89ab..09e56ed641 100644 --- a/modules/space/CMakeLists.txt +++ b/modules/space/CMakeLists.txt @@ -25,6 +25,7 @@ include(${OPENSPACE_CMAKE_EXT_DIR}/module_definition.cmake) set(HEADER_FILES + speckloader.h rendering/planetgeometry.h rendering/renderableconstellationbounds.h rendering/renderablehabitablezone.h @@ -43,6 +44,7 @@ set(HEADER_FILES source_group("Header Files" FILES ${HEADER_FILES}) set(SOURCE_FILES + speckloader.cpp rendering/planetgeometry.cpp rendering/renderableconstellationbounds.cpp rendering/renderablehabitablezone.cpp diff --git a/modules/space/rendering/renderableconstellationbounds.cpp b/modules/space/rendering/renderableconstellationbounds.cpp index e6f2821592..76bd35bca5 100644 --- a/modules/space/rendering/renderableconstellationbounds.cpp +++ b/modules/space/rendering/renderableconstellationbounds.cpp @@ -233,7 +233,7 @@ bool RenderableConstellationBounds::loadVertexFile() { return false; } - std::string fileName = absPath(_vertexFilename); + std::filesystem::path fileName = absPath(_vertexFilename); std::ifstream file; file.open(fileName); if (!file.good()) { @@ -273,7 +273,7 @@ bool RenderableConstellationBounds::loadVertexFile() { LERRORC( "RenderableConstellationBounds", fmt::format( - "Error reading file '{}' at line #{}", fileName, currentLineNumber + "Error reading file {} at line #{}", fileName, currentLineNumber ) ); break; diff --git a/modules/space/rendering/renderablerings.cpp b/modules/space/rendering/renderablerings.cpp index 6e45f719b6..383b6942ee 100644 --- a/modules/space/rendering/renderablerings.cpp +++ b/modules/space/rendering/renderablerings.cpp @@ -125,8 +125,8 @@ RenderableRings::RenderableRings(const ghoul::Dictionary& dictionary) _size.onChange([&]() { _planeIsDirty = true; }); addProperty(_size); - _texturePath = absPath(p.texture); - _textureFile = std::make_unique(_texturePath); + _texturePath = absPath(p.texture).string(); + _textureFile = std::make_unique(_texturePath.value()); _offset = p.offset.value_or(_offset); addProperty(_offset); @@ -134,7 +134,7 @@ RenderableRings::RenderableRings(const ghoul::Dictionary& dictionary) _texturePath.onChange([&]() { loadTexture(); }); addProperty(_texturePath); - _textureFile->setCallback([&](const File&) { _textureIsDirty = true; }); + _textureFile->setCallback([this]() { _textureIsDirty = true; }); _nightFactor = p.nightFactor.value_or(_nightFactor); addProperty(_nightFactor); @@ -236,23 +236,23 @@ void RenderableRings::loadTexture() { using namespace ghoul::io; using namespace ghoul::opengl; std::unique_ptr texture = TextureReader::ref().loadTexture( - absPath(_texturePath) + absPath(_texturePath).string() ); if (texture) { LDEBUGC( "RenderableRings", - fmt::format("Loaded texture from '{}'", absPath(_texturePath)) + fmt::format("Loaded texture from {}", absPath(_texturePath)) ); _texture = std::move(texture); _texture->uploadTexture(); _texture->setFilter(ghoul::opengl::Texture::FilterMode::AnisotropicMipMap); - _textureFile = std::make_unique(_texturePath); - _textureFile->setCallback( - [&](const ghoul::filesystem::File&) { _textureIsDirty = true; } + _textureFile = std::make_unique( + _texturePath.value() ); + _textureFile->setCallback([this]() { _textureIsDirty = true; }); } } } diff --git a/modules/space/rendering/renderablesatellites.cpp b/modules/space/rendering/renderablesatellites.cpp index f808be2147..15fb459236 100644 --- a/modules/space/rendering/renderablesatellites.cpp +++ b/modules/space/rendering/renderablesatellites.cpp @@ -41,6 +41,7 @@ #include #include #include +#include #include #include @@ -105,7 +106,7 @@ RenderableSatellites::RenderableSatellites(const ghoul::Dictionary& dictionary) } void RenderableSatellites::readDataFile(const std::string& filename) { - if (!FileSys.fileExists(filename)) { + if (!std::filesystem::is_regular_file(filename)) { throw ghoul::RuntimeError(fmt::format( "Satellite TLE file {} does not exist", filename )); diff --git a/modules/space/rendering/renderablesmallbody.cpp b/modules/space/rendering/renderablesmallbody.cpp index 72414c3453..cd37e99256 100644 --- a/modules/space/rendering/renderablesmallbody.cpp +++ b/modules/space/rendering/renderablesmallbody.cpp @@ -41,6 +41,7 @@ #include #include #include +#include #include #include #include @@ -172,7 +173,7 @@ RenderableSmallBody::RenderableSmallBody(const ghoul::Dictionary& dictionary) } void RenderableSmallBody::readDataFile(const std::string& filename) { - if (!FileSys.fileExists(filename)) { + if (!std::filesystem::is_regular_file(filename)) { throw ghoul::RuntimeError(fmt::format( "JPL SBDB file {} does not exist.", filename )); diff --git a/modules/space/rendering/renderablestars.cpp b/modules/space/rendering/renderablestars.cpp index eefa4ba887..d2af7317ab 100644 --- a/modules/space/rendering/renderablestars.cpp +++ b/modules/space/rendering/renderablestars.cpp @@ -52,21 +52,21 @@ namespace { constexpr const char* _loggerCat = "RenderableStars"; constexpr const std::array UniformNames = { - "modelMatrix", "cameraUp", "cameraViewProjectionMatrix", - "colorOption", "magnitudeExponent", "eyePosition", "psfParamConf", - "lumCent", "radiusCent", "brightnessCent", "colorTexture", - "alphaValue", "psfTexture", "otherDataTexture", "otherDataRange", - "filterOutOfRange", "fixedColor" + "modelMatrix", "cameraUp", "cameraViewProjectionMatrix", "colorOption", + "magnitudeExponent", "eyePosition", "psfParamConf", "lumCent", "radiusCent", + "brightnessCent", "colorTexture", "alphaValue", "psfTexture", "otherDataTexture", + "otherDataRange", "filterOutOfRange", "fixedColor" }; - constexpr int8_t CurrentCacheVersion = 3; - constexpr const int RenderOptionPointSpreadFunction = 0; constexpr const int RenderOptionTexture = 1; constexpr const int PsfMethodSpencer = 0; constexpr const int PsfMethodMoffat = 1; + constexpr const int PsfTextureSize = 64; + constexpr const int ConvolvedfTextureSize = 257; + constexpr double PARSEC = 0.308567756E17; struct ColorVBOLayout { @@ -159,12 +159,6 @@ namespace { "or filtered away" }; - constexpr openspace::properties::Property::PropertyInfo EnableTestGridInfo = { - "EnableTestGrid", - "Enable Test Grid", - "Set it to true for rendering the test grid." - }; - // Old Method constexpr openspace::properties::Property::PropertyInfo PsfTextureInfo = { "Texture", @@ -173,11 +167,11 @@ namespace { "stars." }; - /*constexpr openspace::properties::Property::PropertyInfo ShapeTextureInfo = { - "ShapeTexture", - "Shape Texture to be convolved", - "The path to the texture that should be used as the base shape for the stars." - };*/ + //constexpr openspace::properties::Property::PropertyInfo ShapeTextureInfo = { + // "ShapeTexture", + // "Shape Texture to be convolved", + // "The path to the texture that should be used as the base shape for the stars." + //}; // PSF constexpr openspace::properties::Property::PropertyInfo MagnitudeExponentInfo = { @@ -344,17 +338,28 @@ namespace { // [[codegen::verbatim(MagnitudeExponentInfo.description)]] std::optional magnitudeExponent; - // [[codegen::verbatim(EnableTestGridInfo.description)]] - std::optional enableTestGrid; + enum class RenderMethod { + PSF, + TextureBased [[codegen::key("Texture Based")]] + }; // [[codegen::verbatim(RenderMethodOptionInfo.description)]] - std::string renderMethod; + RenderMethod renderMethod; // [[codegen::verbatim(PsfTextureInfo.description)]] std::filesystem::path texture; + enum class SizeComposition { + AppBrightness [[codegen::key("App Brightness")]], + LumSize [[codegen::key("Lum and Size")]], + LumSizeAppBrightness [[codegen::key("Lum, Size and App Brightness")]], + AbsMagnitude [[codegen::key("Abs Magnitude")]], + AppMagnitude [[codegen::key("App Magnitude")]], + DistanceModulus [[codegen::key("Distance Modulus")]] + }; + // [[codegen::verbatim(SizeCompositionOptionInfo.description)]] - std::optional sizeComposition; + std::optional sizeComposition; // [[codegen::verbatim(FadeInDistancesInfo.description)]] std::optional fadeInDistances; @@ -440,15 +445,15 @@ RenderableStars::RenderableStars(const ghoul::Dictionary& dictionary) addProperty(_speckFile); _colorTexturePath = p.colorMap.string(); - _colorTextureFile = std::make_unique(_colorTexturePath); + _colorTextureFile = std::make_unique(_colorTexturePath.value()); - /*_shapeTexturePath = absPath(dictionary.value( - ShapeTextureInfo.identifier - )); - _shapeTextureFile = std::make_unique(_shapeTexturePath);*/ + //_shapeTexturePath = absPath(dictionary.value( + // ShapeTextureInfo.identifier + // )); + //_shapeTextureFile = std::make_unique(_shapeTexturePath); if (p.otherDataColorMap.has_value()) { - _otherDataColorMapPath = absPath(*p.otherDataColorMap); + _otherDataColorMapPath = absPath(*p.otherDataColorMap).string(); } _fixedColor.setViewOption(properties::Property::ViewOptions::Color, true); @@ -484,18 +489,9 @@ RenderableStars::RenderableStars(const ghoul::Dictionary& dictionary) addProperty(_colorOption); _colorTexturePath.onChange([&] { _colorTextureIsDirty = true; }); - _colorTextureFile->setCallback([&](const File&) { - _colorTextureIsDirty = true; - }); + _colorTextureFile->setCallback([this]() { _colorTextureIsDirty = true; }); addProperty(_colorTexturePath); - /*_shapeTexturePath.onChange([&] { _shapeTextureIsDirty = true; }); - _shapeTextureFile->setCallback([&](const File&) { - _shapeTextureIsDirty = true; - }); - addProperty(_shapeTexturePath);*/ - - _enableTestGrid = p.enableTestGrid.value_or(_enableTestGrid); _queuedOtherData = p.otherData.value_or(_queuedOtherData); _otherDataOption.onChange([&]() { _dataIsDirty = true; }); @@ -519,19 +515,21 @@ RenderableStars::RenderableStars(const ghoul::Dictionary& dictionary) _renderingMethodOption.addOption(RenderOptionTexture, "Textured Based"); addProperty(_renderingMethodOption); - if (p.renderMethod == "PSF") { + if (p.renderMethod == Parameters::RenderMethod::PSF) { _renderingMethodOption = RenderOptionPointSpreadFunction; } - else if (p.renderMethod == "Texture Based") { + else { _renderingMethodOption = RenderOptionTexture; } - _pointSpreadFunctionTexturePath = absPath(p.texture.string()); - _pointSpreadFunctionFile = std::make_unique(_pointSpreadFunctionTexturePath); - _pointSpreadFunctionTexturePath.onChange([&]() { + _pointSpreadFunctionTexturePath = absPath(p.texture.string()).string(); + _pointSpreadFunctionFile = std::make_unique( + _pointSpreadFunctionTexturePath.value() + ); + _pointSpreadFunctionTexturePath.onChange([this]() { _pointSpreadFunctionTextureIsDirty = true; }); - _pointSpreadFunctionFile->setCallback([&](const File&) { + _pointSpreadFunctionFile->setCallback([this]() { _pointSpreadFunctionTextureIsDirty = true; }); _userProvidedTextureOwner.addProperty(_pointSpreadFunctionTexturePath); @@ -551,23 +549,25 @@ RenderableStars::RenderableStars(const ghoul::Dictionary& dictionary) if (p.sizeComposition.has_value()) { - if (*p.sizeComposition == "App Brightness") { - _psfMultiplyOption = 0; - } - else if (*p.sizeComposition == "Lum and Size") { - _psfMultiplyOption = 1; - } - else if (*p.sizeComposition == "Lum, Size and App Brightness") { - _psfMultiplyOption = 2; - } - else if (*p.sizeComposition == "Abs Magnitude") { - _psfMultiplyOption = 3; - } - else if (*p.sizeComposition == "App Maginitude") { - _psfMultiplyOption = 4; - } - else if (*p.sizeComposition == "Distance Modulus") { - _psfMultiplyOption = 5; + switch (*p.sizeComposition) { + case Parameters::SizeComposition::AppBrightness: + _psfMultiplyOption = 0; + break; + case Parameters::SizeComposition::LumSize: + _psfMultiplyOption = 1; + break; + case Parameters::SizeComposition::LumSizeAppBrightness: + _psfMultiplyOption = 2; + break; + case Parameters::SizeComposition::AbsMagnitude: + _psfMultiplyOption = 3; + break; + case Parameters::SizeComposition::AppMagnitude: + _psfMultiplyOption = 4; + break; + case Parameters::SizeComposition::DistanceModulus: + _psfMultiplyOption = 5; + break; } } else { @@ -632,13 +632,16 @@ void RenderableStars::initializeGL() { loadData(); + // We need to wait until after loading the data until we can see if the requested + // data value actually exists or not. Once we determine the index, we no longer + // need the value and can clear it if (!_queuedOtherData.empty()) { - auto it = std::find(_dataNames.begin(), _dataNames.end(), _queuedOtherData); - if (it == _dataNames.end()) { + int idx = _dataset.index(_queuedOtherData); + if (idx == -1) { LERROR(fmt::format("Could not find other data column {}", _queuedOtherData)); } else { - _otherDataOption = static_cast(std::distance(_dataNames.begin(), it)); + _otherDataOption = idx; _queuedOtherData.clear(); } } @@ -651,7 +654,7 @@ void RenderableStars::initializeGL() { glBindVertexArray(_psfVao); glBindBuffer(GL_ARRAY_BUFFER, _psfVbo); - const GLfloat vertexData[] = { + constexpr const std::array VertexData = { //x y s t -1.f, -1.f, 0.f, 0.f, 1.f, 1.f, 1.f, 1.f, @@ -661,15 +664,8 @@ void RenderableStars::initializeGL() { 1.f, 1.f, 1.f, 1.f }; - glBufferData(GL_ARRAY_BUFFER, sizeof(vertexData), vertexData, GL_STATIC_DRAW); - glVertexAttribPointer( - 0, - 4, - GL_FLOAT, - GL_FALSE, - sizeof(GLfloat) * 4, - nullptr - ); + glBufferData(GL_ARRAY_BUFFER, sizeof(VertexData), VertexData.data(), GL_STATIC_DRAW); + glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), nullptr); glEnableVertexAttribArray(0); glBindVertexArray(0); @@ -685,8 +681,8 @@ void RenderableStars::initializeGL() { GL_TEXTURE_2D, 0, GL_RGBA8, - _psfTextureSize, - _psfTextureSize, + PsfTextureSize, + PsfTextureSize, 0, GL_RGBA, GL_BYTE, @@ -708,8 +704,8 @@ void RenderableStars::initializeGL() { GL_TEXTURE_2D, 0, GL_RGBA8, - _convolvedfTextureSize, - _convolvedfTextureSize, + ConvolvedfTextureSize, + ConvolvedfTextureSize, 0, GL_RGBA, GL_BYTE, @@ -742,13 +738,12 @@ void RenderableStars::loadPSFTexture() { std::filesystem::exists(_pointSpreadFunctionTexturePath.value())) { _pointSpreadFunctionTexture = ghoul::io::TextureReader::ref().loadTexture( - absPath(_pointSpreadFunctionTexturePath) + absPath(_pointSpreadFunctionTexturePath).string() ); if (_pointSpreadFunctionTexture) { LDEBUG(fmt::format( - "Loaded texture from '{}'", - absPath(_pointSpreadFunctionTexturePath) + "Loaded texture from '{}'", absPath(_pointSpreadFunctionTexturePath) )); _pointSpreadFunctionTexture->uploadTexture(); } @@ -757,26 +752,16 @@ void RenderableStars::loadPSFTexture() { ); _pointSpreadFunctionFile = std::make_unique( - _pointSpreadFunctionTexturePath - ); + _pointSpreadFunctionTexturePath.value() + ); _pointSpreadFunctionFile->setCallback( - [&](const ghoul::filesystem::File&) { - _pointSpreadFunctionTextureIsDirty = true; - } + [&]() { _pointSpreadFunctionTextureIsDirty = true; } ); } _pointSpreadFunctionTextureIsDirty = false; - } void RenderableStars::renderPSFToTexture() { - // Saves current FBO first - // GLint defaultFBO; - // defaultFBO = global::renderEngine->openglStateCache().defaultFramebuffer(); - -// GLint m_viewport[4]; -// global::renderEngine.openglStateCache().viewPort(m_viewport); - // Creates the FBO for the calculations GLuint psfFBO; glGenFramebuffers(1, &psfFBO); @@ -785,9 +770,7 @@ void RenderableStars::renderPSFToTexture() { glDrawBuffers(1, drawBuffers); glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, _psfTexture, 0); - - glViewport(0, 0, _psfTextureSize, _psfTextureSize); - + glViewport(0, 0, PsfTextureSize, PsfTextureSize); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); std::unique_ptr program = @@ -798,8 +781,8 @@ void RenderableStars::renderPSFToTexture() { ); program->activate(); - constexpr const float black[] = { 0.f, 0.f, 0.f, 0.f }; - glClearBufferfv(GL_COLOR, 0, black); + constexpr const std::array Black = { 0.f, 0.f, 0.f, 0.f }; + glClearBufferfv(GL_COLOR, 0, Black.data()); program->setUniform("psfMethod", _psfMethodOption.value()); program->setUniform("p0Param", _p0Param); @@ -873,7 +856,7 @@ void RenderableStars::renderPSFToTexture() { // m_viewport[2], // m_viewport[3] //); - //glDeleteFramebuffers(1, &psfFBO); + glDeleteFramebuffers(1, &psfFBO); //glDeleteFramebuffers(1, &convolveFBO); // Restores OpenGL blending state @@ -881,7 +864,7 @@ void RenderableStars::renderPSFToTexture() { } void RenderableStars::render(const RenderData& data, RendererTasks&) { - if (_fullData.empty()) { + if (_dataset.entries.empty()) { return; } @@ -981,7 +964,7 @@ void RenderableStars::render(const RenderData& data, RendererTasks&) { glBindVertexArray(_vao); - const GLsizei nStars = static_cast(_fullData.size() / _nValuesPerStar); + const GLsizei nStars = static_cast(_dataset.entries.size()); glDrawArrays(GL_POINTS, 0, nStars); glBindVertexArray(0); @@ -999,7 +982,7 @@ void RenderableStars::update(const UpdateData&) { _dataIsDirty = true; } - if (_fullData.empty()) { + if (_dataset.entries.empty()) { return; } @@ -1007,9 +990,7 @@ void RenderableStars::update(const UpdateData&) { const int value = _colorOption; LDEBUG("Regenerating data"); - createDataSlice(ColorOption(value)); - - int size = static_cast(_slicedData.size()); + std::vector slice = createDataSlice(ColorOption(value)); if (_vao == 0) { glGenVertexArrays(1, &_vao); @@ -1021,8 +1002,8 @@ void RenderableStars::update(const UpdateData&) { glBindBuffer(GL_ARRAY_BUFFER, _vbo); glBufferData( GL_ARRAY_BUFFER, - size * sizeof(GLfloat), - _slicedData.data(), + slice.size() * sizeof(GLfloat), + slice.data(), GL_STATIC_DRAW ); @@ -1032,8 +1013,8 @@ void RenderableStars::update(const UpdateData&) { "in_bvLumAbsMagAppMag" ); - const size_t nStars = _fullData.size() / _nValuesPerStar; - const size_t nValues = _slicedData.size() / nStars; + const size_t nStars = _dataset.entries.size(); + const size_t nValues = slice.size() / nStars; GLsizei stride = static_cast(sizeof(GLfloat) * nValues); @@ -1161,7 +1142,7 @@ void RenderableStars::update(const UpdateData&) { _colorTexture = nullptr; if (_colorTexturePath.value() != "") { _colorTexture = ghoul::io::TextureReader::ref().loadTexture( - absPath(_colorTexturePath) + absPath(_colorTexturePath).string() ); if (_colorTexture) { LDEBUG(fmt::format( @@ -1172,11 +1153,9 @@ void RenderableStars::update(const UpdateData&) { } _colorTextureFile = std::make_unique( - _colorTexturePath - ); - _colorTextureFile->setCallback( - [&](const ghoul::filesystem::File&) { _colorTextureIsDirty = true; } + _colorTexturePath.value() ); + _colorTextureFile->setCallback([this]() { _colorTextureIsDirty = true; }); } _colorTextureIsDirty = false; } @@ -1188,7 +1167,7 @@ void RenderableStars::update(const UpdateData&) { _otherDataColorMapTexture = nullptr; if (!_otherDataColorMapPath.value().empty()) { _otherDataColorMapTexture = ghoul::io::TextureReader::ref().loadTexture( - absPath(_otherDataColorMapPath) + absPath(_otherDataColorMapPath).string() ); if (_otherDataColorMapTexture) { LDEBUG(fmt::format( @@ -1208,278 +1187,39 @@ void RenderableStars::update(const UpdateData&) { } } -/* -void RenderableStars::loadShapeTexture() { - if (_shapeTextureIsDirty) { - LDEBUG("Reloading Shape Texture"); - _shapeTexture = nullptr; - if (_shapeTexturePath.value() != "") { - _shapeTexture = ghoul::io::TextureReader::ref().loadTexture( - absPath(_shapeTexturePath) - ); - if (_shapeTexture) { - LDEBUG(fmt::format( - "Loaded texture from '{}'", - absPath(_shapeTexturePath) - )); - _shapeTexture->uploadTexture(); - } - - _shapeTextureFile = std::make_unique( - _shapeTexturePath - ); - _shapeTextureFile->setCallback( - [&](const ghoul::filesystem::File&) { _shapeTextureIsDirty = true; } - ); - } - _shapeTextureIsDirty = false; - } -} -*/ - void RenderableStars::loadData() { - std::string file = absPath(_speckFile); - if (!FileSys.fileExists(file)) { + std::filesystem::path file = absPath(_speckFile); + if (!std::filesystem::is_regular_file(file)) { return; } - std::string cachedFile = FileSys.cacheManager()->cachedFilename( - file, - ghoul::filesystem::CacheManager::Persistent::Yes - ); - - _nValuesPerStar = 0; - _slicedData.clear(); - _fullData.clear(); - _dataNames.clear(); - - bool hasCachedFile = FileSys.fileExists(cachedFile); - if (hasCachedFile) { - LINFO(fmt::format("Cached file '{}' used for Speck file '{}'", - cachedFile, file - )); - - bool success = loadCachedFile(cachedFile); - if (success) { - return; - } - else { - FileSys.cacheManager()->removeCacheFile(file); - // Intentional fall-through to the 'else' computation to generate the cache - // file for the next run - } - } - else { - LINFO(fmt::format("Cache for Speck file '{}' not found", file)); - } - LINFO(fmt::format("Loading Speck file '{}'", file)); - - readSpeckFile(); - - LINFO("Saving cache"); - saveCachedFile(cachedFile); -} - -void RenderableStars::readSpeckFile() { - std::string _file = _speckFile; - std::ifstream file(_file); - if (!file.good()) { - LERROR(fmt::format("Failed to open Speck file '{}'", _file)); + _dataset = speck::data::loadFileWithCache(file); + if (_dataset.entries.empty()) { return; } - // The beginning of the speck file has a header that either contains comments - // (signaled by a preceding '#') or information about the structure of the file - // (signaled by the keywords 'datavar', 'texturevar', and 'texture') - std::string line; - while (true) { - std::streampos position = file.tellg(); - std::getline(file, line, '\n'); - - if (line[0] == '#' || line.empty()) { - continue; - } - - if (line.substr(0, 7) != "datavar" && - line.substr(0, 10) != "texturevar" && - line.substr(0, 7) != "texture") - { - // we read a line that doesn't belong to the header, so we have to jump back - // before the beginning of the current line - if (_enableTestGrid) { - file.seekg(position - std::streamoff(8)); - } - break; - } - - if (line.substr(0, 7) == "datavar") { - // datavar lines are structured as follows: - // datavar # description - // where # is the index of the data variable; so if we repeatedly overwrite - // the 'nValues' variable with the latest index, we will end up with the total - // number of values (+3 since X Y Z are not counted in the Speck file index) - std::stringstream str(line); - - std::string dummy; - str >> dummy; - str >> _nValuesPerStar; - - std::string name; - str >> name; - _dataNames.push_back(name); - - // +3 because the position x, y, z - if (name == "lum") { - _lumArrayPos = _nValuesPerStar + 3; - } - else if (name == "absmag") { - _absMagArrayPos = _nValuesPerStar + 3; - } - else if (name == "appmag") { - _appMagArrayPos = _nValuesPerStar + 3; - } - else if (name == "colorb_v") { - _bvColorArrayPos = _nValuesPerStar + 3; - } - else if (name == "vx") { - _velocityArrayPos = _nValuesPerStar + 3; - } - else if (name == "speed") { - _speedArrayPos = _nValuesPerStar + 3; - } - _nValuesPerStar += 1; // We want the number, but the index is 0 based - } + std::vector variableNames; + variableNames.reserve(_dataset.variables.size()); + for (const speck::Dataset::Variable& v : _dataset.variables) { + variableNames.push_back(v.name); } + _otherDataOption.addOptions(variableNames); - _nValuesPerStar += 3; // X Y Z are not counted in the Speck file indices - _otherDataOption.addOptions(_dataNames); - - float minLumValue = std::numeric_limits::max(); - float maxLumValue = std::numeric_limits::min(); - - do { - std::vector values(_nValuesPerStar); - std::stringstream str(line); - - for (int i = 0; i < _nValuesPerStar; ++i) { - str >> values[i]; - } - - bool nullArray = true; - for (float v : values) { - if (v != 0.0) { - nullArray = false; - break; - } - } - minLumValue = values[_lumArrayPos] < minLumValue ? - values[_lumArrayPos] : minLumValue; - maxLumValue = values[_lumArrayPos] > maxLumValue ? - values[_lumArrayPos] : maxLumValue; - if (!nullArray) { - _fullData.insert(_fullData.end(), values.begin(), values.end()); - } - - std::getline(file, line, '\n'); - - } while (!file.eof()); - - // Normalize Luminosity: - for (size_t i = 0; i < _fullData.size(); i += _nValuesPerStar) { - _fullData[i + _lumArrayPos] = - (_fullData[i + _lumArrayPos] - minLumValue) / (maxLumValue - minLumValue); + bool success = _dataset.normalizeVariable("lum"); + if (!success) { + throw ghoul::RuntimeError("Could not find required variable 'luminosity'"); } } -bool RenderableStars::loadCachedFile(const std::string& file) { - std::ifstream fileStream(file, std::ifstream::binary); - if (fileStream.good()) { - int8_t version = 0; - fileStream.read(reinterpret_cast(&version), sizeof(int8_t)); - if (version != CurrentCacheVersion) { - LINFO("The format of the cached file has changed: deleting old cache"); - fileStream.close(); - FileSys.deleteFile(file); - return false; - } - - int32_t nValues = 0; - fileStream.read(reinterpret_cast(&nValues), sizeof(int32_t)); - fileStream.read(reinterpret_cast(&_nValuesPerStar), sizeof(int32_t)); - - fileStream.read(reinterpret_cast(&_lumArrayPos), sizeof(int32_t)); - fileStream.read(reinterpret_cast(&_absMagArrayPos), sizeof(int32_t)); - fileStream.read(reinterpret_cast(&_appMagArrayPos), sizeof(int32_t)); - fileStream.read(reinterpret_cast(&_bvColorArrayPos), sizeof(int32_t)); - fileStream.read(reinterpret_cast(&_velocityArrayPos), sizeof(int32_t)); - fileStream.read(reinterpret_cast(&_speedArrayPos), sizeof(int32_t)); - - for (int i = 0; i < _nValuesPerStar - 3; ++i) { - uint16_t len; - fileStream.read(reinterpret_cast(&len), sizeof(uint16_t)); - std::vector buffer(len); - fileStream.read(buffer.data(), len); - std::string value(buffer.begin(), buffer.end()); - _dataNames.push_back(value); - } - _otherDataOption.addOptions(_dataNames); - - _fullData.resize(nValues); - fileStream.read(reinterpret_cast( - _fullData.data()), - nValues * sizeof(_fullData[0]) - ); - - bool success = fileStream.good(); - return success; - } - else { - LERROR(fmt::format("Error opening file '{}' for loading cache file", file)); - return false; - } -} - -void RenderableStars::saveCachedFile(const std::string& file) const { - std::ofstream fileStream(file, std::ofstream::binary); - - if (!fileStream.good()) { - LERROR(fmt::format("Error opening file '{}' for save cache file", file)); - return; - } - - fileStream.write( - reinterpret_cast(&CurrentCacheVersion), - sizeof(int8_t) - ); - - int32_t nValues = static_cast(_fullData.size()); - if (nValues == 0) { - throw ghoul::RuntimeError("Error writing cache: No values were loaded"); - } - fileStream.write(reinterpret_cast(&nValues), sizeof(int32_t)); - - int32_t nValuesPerStar = static_cast(_nValuesPerStar); - fileStream.write(reinterpret_cast(&nValuesPerStar), sizeof(int32_t)); - fileStream.write(reinterpret_cast(&_lumArrayPos), sizeof(int32_t)); - fileStream.write(reinterpret_cast(&_absMagArrayPos), sizeof(int32_t)); - fileStream.write(reinterpret_cast(&_appMagArrayPos), sizeof(int32_t)); - fileStream.write(reinterpret_cast(&_bvColorArrayPos), sizeof(int32_t)); - fileStream.write(reinterpret_cast(&_velocityArrayPos), sizeof(int32_t)); - fileStream.write(reinterpret_cast(&_speedArrayPos), sizeof(int32_t)); - - // -3 as we don't want to save the xyz values that are in the beginning of the file - for (int i = 0; i < _nValuesPerStar - 3; ++i) { - uint16_t len = static_cast(_dataNames[i].size()); - fileStream.write(reinterpret_cast(&len), sizeof(uint16_t)); - fileStream.write(_dataNames[i].c_str(), len); - } - - size_t nBytes = nValues * sizeof(_fullData[0]); - fileStream.write(reinterpret_cast(_fullData.data()), nBytes); -} - -void RenderableStars::createDataSlice(ColorOption option) { - _slicedData.clear(); +std::vector RenderableStars::createDataSlice(ColorOption option) { + const int bvIdx = std::max(_dataset.index("colorb_v"), 0); + const int lumIdx = std::max(_dataset.index("lum"), 0); + const int absMagIdx = std::max(_dataset.index("absmag"), 0); + const int appMagIdx = std::max(_dataset.index("appmag"), 0); + const int vxIdx = std::max(_dataset.index("vx"), 0); + const int vyIdx = std::max(_dataset.index("vy"), 0); + const int vzIdx = std::max(_dataset.index("vz"), 0); + const int speedIdx = std::max(_dataset.index("speed"), 0); _otherDataRange = glm::vec2( std::numeric_limits::max(), @@ -1488,18 +1228,12 @@ void RenderableStars::createDataSlice(ColorOption option) { double maxRadius = 0.0; - for (size_t i = 0; i < _fullData.size(); i += _nValuesPerStar) { - glm::dvec3 position = glm::dvec3( - _fullData[i + 0], - _fullData[i + 1], - _fullData[i + 2] - ); - position *= openspace::distanceconstants::Parsec; - - const double r = glm::length(position); - if (r > maxRadius) { - maxRadius = r; - } + std::vector result; + // 7 for the default Color option of 3 positions + bv + lum + abs + app magnitude + result.reserve(_dataset.entries.size() * 7); + for (const speck::Dataset::Entry& e : _dataset.entries) { + glm::dvec3 position = glm::dvec3(e.position) * distanceconstants::Parsec; + maxRadius = std::max(maxRadius, glm::length(position)); switch (option) { case ColorOption::Color: @@ -1516,23 +1250,12 @@ void RenderableStars::createDataSlice(ColorOption option) { static_cast(position[2]) }}; - if (_enableTestGrid) { - float sunColor = 0.650f; - layout.value.value = sunColor;// _fullData[i + 3]; - } - else { - layout.value.value = _fullData[i + _bvColorArrayPos]; - } - - layout.value.luminance = _fullData[i + _lumArrayPos]; - layout.value.absoluteMagnitude = _fullData[i + _absMagArrayPos]; - layout.value.apparentMagnitude = _fullData[i + _appMagArrayPos]; - - _slicedData.insert( - _slicedData.end(), - layout.data.begin(), - layout.data.end()); + layout.value.value = e.data[bvIdx]; + layout.value.luminance = e.data[lumIdx]; + layout.value.absoluteMagnitude = e.data[absMagIdx]; + layout.value.apparentMagnitude = e.data[appMagIdx]; + result.insert(result.end(), layout.data.begin(), layout.data.end()); break; } case ColorOption::Velocity: @@ -1548,20 +1271,16 @@ void RenderableStars::createDataSlice(ColorOption option) { static_cast(position[2]) }}; - layout.value.value = _fullData[i + _bvColorArrayPos]; - layout.value.luminance = _fullData[i + _lumArrayPos]; - layout.value.absoluteMagnitude = _fullData[i + _absMagArrayPos]; - layout.value.apparentMagnitude = _fullData[i + _appMagArrayPos]; + layout.value.value = e.data[bvIdx]; + layout.value.luminance = e.data[lumIdx]; + layout.value.absoluteMagnitude = e.data[absMagIdx]; + layout.value.apparentMagnitude = e.data[appMagIdx]; - layout.value.vx = _fullData[i + _velocityArrayPos]; - layout.value.vy = _fullData[i + _velocityArrayPos + 1]; - layout.value.vz = _fullData[i + _velocityArrayPos + 2]; + layout.value.vx = e.data[vxIdx]; + layout.value.vy = e.data[vyIdx]; + layout.value.vz = e.data[vzIdx]; - _slicedData.insert( - _slicedData.end(), - layout.data.begin(), - layout.data.end() - ); + result.insert(result.end(), layout.data.begin(), layout.data.end()); break; } case ColorOption::Speed: @@ -1577,18 +1296,13 @@ void RenderableStars::createDataSlice(ColorOption option) { static_cast(position[2]) }}; - layout.value.value = _fullData[i + _bvColorArrayPos]; - layout.value.luminance = _fullData[i + _lumArrayPos]; - layout.value.absoluteMagnitude = _fullData[i + _absMagArrayPos]; - layout.value.apparentMagnitude = _fullData[i + _appMagArrayPos]; + layout.value.value = e.data[bvIdx]; + layout.value.luminance = e.data[lumIdx]; + layout.value.absoluteMagnitude = e.data[absMagIdx]; + layout.value.apparentMagnitude = e.data[appMagIdx]; + layout.value.speed = e.data[speedIdx]; - layout.value.speed = _fullData[i + _speedArrayPos]; - - _slicedData.insert( - _slicedData.end(), - layout.data.begin(), - layout.data.end() - ); + result.insert(result.end(), layout.data.begin(), layout.data.end()); break; } case ColorOption::OtherData: @@ -1606,37 +1320,32 @@ void RenderableStars::createDataSlice(ColorOption option) { int index = _otherDataOption.value(); // plus 3 because of the position - layout.value.value = _fullData[i + index + 3]; + layout.value.value = e.data[index]; - if (_staticFilterValue.has_value() && - layout.value.value == _staticFilterValue) + if (_staticFilterValue.has_value() && e.data[index] == _staticFilterValue) { layout.value.value = _staticFilterReplacementValue; } - glm::vec2 range = _otherDataRange.value(); + glm::vec2 range = _otherDataRange; range.x = std::min(range.x, layout.value.value); range.y = std::max(range.y, layout.value.value); _otherDataRange = range; _otherDataRange.setMinValue(glm::vec2(range.x)); _otherDataRange.setMaxValue(glm::vec2(range.y)); - layout.value.luminance = _fullData[i + _lumArrayPos]; - layout.value.absoluteMagnitude = _fullData[i + _absMagArrayPos]; - layout.value.apparentMagnitude = _fullData[i + _appMagArrayPos]; - - _slicedData.insert( - _slicedData.end(), - layout.data.begin(), - layout.data.end() - ); + layout.value.luminance = e.data[lumIdx]; + layout.value.absoluteMagnitude = e.data[absMagIdx]; + layout.value.apparentMagnitude = e.data[appMagIdx]; + result.insert(result.end(), layout.data.begin(), layout.data.end()); break; } } } setBoundingSphere(maxRadius); + return result; } } // namespace openspace diff --git a/modules/space/rendering/renderablestars.h b/modules/space/rendering/renderablestars.h index 7f1e83eb02..663dd0ac06 100644 --- a/modules/space/rendering/renderablestars.h +++ b/modules/space/rendering/renderablestars.h @@ -27,6 +27,7 @@ #include +#include #include #include #include @@ -74,15 +75,8 @@ private: FixedColor = 4 }; - static const int _psfTextureSize = 64; - static const int _convolvedfTextureSize = 257; - - void createDataSlice(ColorOption option); - void loadData(); - void readSpeckFile(); - bool loadCachedFile(const std::string& file); - void saveCachedFile(const std::string& file) const; + std::vector createDataSlice(ColorOption option); properties::StringProperty _speckFile; @@ -90,10 +84,6 @@ private: std::unique_ptr _colorTexture; std::unique_ptr _colorTextureFile; - //properties::StringProperty _shapeTexturePath; - //std::unique_ptr _shapeTexture; - //std::unique_ptr _shapeTextureFile; - properties::OptionProperty _colorOption; properties::OptionProperty _otherDataOption; properties::StringProperty _otherDataColorMapPath; @@ -128,11 +118,10 @@ private: std::unique_ptr _program; UniformCache( - modelMatrix, cameraUp, cameraViewProjectionMatrix, - colorOption, magnitudeExponent, eyePosition, psfParamConf, - lumCent, radiusCent, brightnessCent, colorTexture, - alphaValue, psfTexture, otherDataTexture, otherDataRange, - filterOutOfRange, fixedColor + modelMatrix, cameraUp, cameraViewProjectionMatrix, colorOption, magnitudeExponent, + eyePosition, psfParamConf, lumCent, radiusCent, brightnessCent, colorTexture, + alphaValue, psfTexture, otherDataTexture, otherDataRange, filterOutOfRange, + fixedColor ) _uniformCache; bool _speckFileIsDirty = true; @@ -142,26 +131,13 @@ private: bool _dataIsDirty = true; bool _otherDataColorMapIsDirty = true; - // Test Grid Enabled - bool _enableTestGrid = false; + speck::Dataset _dataset; - std::vector _slicedData; - std::vector _fullData; - - int _nValuesPerStar = 0; std::string _queuedOtherData; - std::vector _dataNames; std::optional _staticFilterValue; float _staticFilterReplacementValue = 0.f; - std::size_t _lumArrayPos = 0; - std::size_t _absMagArrayPos = 0; - std::size_t _appMagArrayPos = 0; - std::size_t _bvColorArrayPos = 0; - std::size_t _velocityArrayPos = 0; - std::size_t _speedArrayPos = 0; - GLuint _vao = 0; GLuint _vbo = 0; GLuint _psfVao = 0; diff --git a/modules/space/speckloader.cpp b/modules/space/speckloader.cpp new file mode 100644 index 0000000000..1e35654b7f --- /dev/null +++ b/modules/space/speckloader.cpp @@ -0,0 +1,905 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2021 * + * * + * 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 { + constexpr const int8_t DataCacheFileVersion = 10; + constexpr const int8_t LabelCacheFileVersion = 10; + constexpr const int8_t ColorCacheFileVersion = 10; + + constexpr bool startsWith(std::string_view lhs, std::string_view rhs) noexcept { + return (rhs.size() <= lhs.size()) && (lhs.substr(0, rhs.size()) == rhs); + } + + void strip(std::string& line) noexcept { + // 1. Remove all spaces from the beginning + // 2. Remove # + // 3. Remove all spaces from the new beginning + // 4. Remove all spaces from the end + + while (!line.empty() && line[0] == ' ') { + line = line.substr(1); + } + + if (!line.empty() && line[0] == '#') { + line = line.substr(1); + } + + while (!line.empty() && line[0] == ' ') { + line = line.substr(1); + } + + while (!line.empty() && line.back() == ' ') { + line = line.substr(0, line.size() - 1); + } + } + + template + void checkSize(U value, std::string_view message) { + if (value > std::numeric_limits::max()) { + throw ghoul::RuntimeError(fmt::format("Error saving file: {}", message)); + } + } + + template + using LoadCacheFunc = std::function(std::filesystem::path)>; + + template + using SaveCacheFunc = std::function; + + template + using LoadSpeckFunc = std::function; + + + + template + T internalLoadFileWithCache(std::filesystem::path speckPath, + openspace::speck::SkipAllZeroLines skipAllZeroLines, + LoadSpeckFunc loadSpeckFunction, + LoadCacheFunc loadCacheFunction, + SaveCacheFunc saveCacheFunction) + { + static_assert( + std::is_same_v || + std::is_same_v || + std::is_same_v + ); + + std::string cachePath = FileSys.cacheManager()->cachedFilename(speckPath); + + if (std::filesystem::exists(cachePath)) { + LINFOC( + "SpeckLoader", + fmt::format( + "Cached file '{}' used for file {}", cachePath, speckPath + ) + ); + + std::optional dataset = loadCacheFunction(cachePath); + if (dataset.has_value()) { + // We could load the cache file and we are now done with this + return *dataset; + } + else { + FileSys.cacheManager()->removeCacheFile(cachePath); + } + } + LINFOC("SpeckLoader", fmt::format("Loading file {}", speckPath)); + T dataset = loadSpeckFunction(speckPath, skipAllZeroLines); + + if (!dataset.entries.empty()) { + LINFOC("SpeckLoader", "Saving cache"); + saveCacheFunction(dataset, cachePath); + } + return dataset; + } +} // namespace + +namespace openspace::speck { + +namespace data { + +Dataset loadFile(std::filesystem::path path, SkipAllZeroLines skipAllZeroLines) { + ghoul_assert(std::filesystem::exists(path), "File must exist"); + + std::ifstream file(path); + if (!file.good()) { + throw ghoul::RuntimeError(fmt::format("Failed to open speck file {}", path)); + } + + Dataset res; + + int nDataValues = 0; + + std::string line; + // First phase: Loading the header information + while (std::getline(file, line)) { + // Ignore empty line or commented-out lines + if (line.empty() || line[0] == '#') { + continue; + } + + // Guard against wrong line endings (copying files from Windows to Mac) causes + // lines to have a final \r + if (line.back() == '\r') { + line = line.substr(0, line.length() - 1); + } + + strip(line); + + // If the first character is a digit, we have left the preamble and are in the + // data section of the file + if (std::isdigit(line[0]) || line[0] == '-') { + break; + } + + + if (startsWith(line, "datavar")) { + // each datavar line is following the form: + // datavar + // with being the index of the data variable + + std::stringstream str(line); + std::string dummy; + Dataset::Variable v; + str >> dummy >> v.index >> v.name; + + nDataValues += 1; + res.variables.push_back(v); + continue; + } + + if (startsWith(line, "texturevar")) { + // each texturevar line is following the form: + // texturevar + // where is the data value index where the texture index is stored + if (res.textureDataIndex != -1) { + throw ghoul::RuntimeError(fmt::format( + "Error loading speck file {}: Texturevar defined twice", path + )); + } + + std::stringstream str(line); + std::string dummy; + str >> dummy >> res.textureDataIndex; + + continue; + } + + if (startsWith(line, "polyorivar")) { + // each polyorivar line is following the form: + // texturevar + // where is the data value index where the orientation index storage + // starts. There are 6 values stored in total, xyz + uvw + + if (res.orientationDataIndex != -1) { + throw ghoul::RuntimeError(fmt::format( + "Error loading speck file {}: Orientation index defined twice", path + )); + } + + std::stringstream str(line); + std::string dummy; + str >> dummy >> res.orientationDataIndex; + + // Ok.. this is kind of weird. Speck unfortunately doesn't tell us in the + // specification how many values a datavar has. Usually this is 1 value per + // datavar, unless it is a polygon orientation thing. Now, the datavar name + // for these can be anything (have seen 'orientation' and 'ori' before, so we + // can't really check by name for these or we will miss some if they are + // mispelled or whatever. So we have to go the roundabout way of adding the + // 5 remaining values (the 6th nDataValue was already added in the + // corresponding 'datavar' section) here + nDataValues += 5; + + continue; + } + + if (startsWith(line, "texture")) { + // each texture line is following one of two forms: + // 1: texture -M 1 halo.sgi + // 2: texture 1 M1.sgi + // The parameter in #1 is currently being ignored + + std::stringstream str(line); + + std::string dummy; + str >> dummy; + + if (line.find('-') != std::string::npos) { + str >> dummy; + } + + Dataset::Texture texture; + str >> texture.index >> texture.file; + + for (const Dataset::Texture& t : res.textures) { + if (t.index == texture.index) { + throw ghoul::RuntimeError(fmt::format( + "Error loading speck file {}: Texture index '{}' defined twice", + path, texture.index + )); + } + } + + res.textures.push_back(texture); + continue; + } + } + + std::sort( + res.variables.begin(), res.variables.end(), + [](const Dataset::Variable& lhs, const Dataset::Variable& rhs) { + return lhs.index < rhs.index; + } + ); + + std::sort( + res.textures.begin(), res.textures.end(), + [](const Dataset::Texture& lhs, const Dataset::Texture& rhs) { + return lhs.index < rhs.index; + } + ); + + // For the first line, we already loaded it and rejected it above, so if we do another + // std::getline, we'd miss the first data value line + bool isFirst = true; + while (isFirst || std::getline(file, line)) { + isFirst = false; + + // Ignore empty line or commented-out lines + if (line.empty() || line[0] == '#') { + continue; + } + + // Guard against wrong line endings (copying files from Windows to Mac) causes + // lines to have a final \r + if (line.back() == '\r') { + line = line.substr(0, line.length() - 1); + } + + strip(line); + + if (line.empty()) { + continue; + } + + // If the first character is a digit, we have left the preamble and are in the + // data section of the file + if (!std::isdigit(line[0]) && line[0] != '-') { + throw ghoul::RuntimeError(fmt::format( + "Error loading speck file {}: Header information and datasegment " + "intermixed", path + )); + } + + bool allZero = true; + + std::stringstream str(line); + Dataset::Entry entry; + str >> entry.position.x >> entry.position.y >> entry.position.z; + allZero &= (entry.position == glm::vec3(0.0)); + + entry.data.resize(nDataValues); + for (int i = 0; i < nDataValues; i += 1) { + str >> entry.data[i]; + allZero &= (entry.data[i] == 0.0); + } + + if (skipAllZeroLines && allZero) { + continue; + } + + std::string rest; + std::getline(str, rest); + if (!rest.empty()) { + + strip(rest); + entry.comment = rest; + } + + res.entries.push_back(std::move(entry)); + } + +#ifdef _DEBUG + if (!res.entries.empty()) { + size_t nValues = res.entries[0].data.size(); + ghoul_assert(nDataValues == nValues, "nDataValues calculation went wrong"); + for (const Dataset::Entry& e : res.entries) { + ghoul_assert( + e.data.size() == nDataValues, + "Row had different number of data values" + ); + } + } +#endif + + return res; +} + +std::optional loadCachedFile(std::filesystem::path path) { + std::ifstream file(path, std::ios::binary); + if (!file.good()) { + return std::nullopt; + } + + Dataset result; + + int8_t fileVersion; + file.read(reinterpret_cast(&fileVersion), sizeof(int8_t)); + if (fileVersion != DataCacheFileVersion) { + // Incompatible version and we won't be able to read the file + return std::nullopt; + } + + // + // Read variables + uint16_t nVariables; + file.read(reinterpret_cast(&nVariables), sizeof(uint16_t)); + result.variables.resize(nVariables); + for (int i = 0; i < nVariables; i += 1) { + Dataset::Variable var; + + int16_t idx; + file.read(reinterpret_cast(&idx), sizeof(int16_t)); + var.index = idx; + + uint16_t len; + file.read(reinterpret_cast(&len), sizeof(uint16_t)); + var.name.resize(len); + file.read(var.name.data(), len); + + result.variables[i] = std::move(var); + } + + // + // Read textures + uint16_t nTextures; + file.read(reinterpret_cast(&nTextures), sizeof(uint16_t)); + result.textures.resize(nTextures); + for (int i = 0; i < nTextures; i += 1) { + Dataset::Texture tex; + + int16_t idx; + file.read(reinterpret_cast(&idx), sizeof(int16_t)); + tex.index = idx; + + uint16_t len; + file.read(reinterpret_cast(&len), sizeof(uint16_t)); + tex.file.resize(len); + file.read(tex.file.data(), len); + + result.textures[i] = std::move(tex); + } + + // + // Read indices + int16_t texDataIdx; + file.read(reinterpret_cast(&texDataIdx), sizeof(int16_t)); + result.textureDataIndex = texDataIdx; + + int16_t oriDataIdx; + file.read(reinterpret_cast(&oriDataIdx), sizeof(int16_t)); + result.orientationDataIndex = oriDataIdx; + + // + // Read entries + uint64_t nEntries; + file.read(reinterpret_cast(&nEntries), sizeof(uint64_t)); + result.entries.reserve(nEntries); + for (int i = 0; i < nEntries; i += 1) { + Dataset::Entry e; + file.read(reinterpret_cast(&e.position.x), sizeof(float)); + file.read(reinterpret_cast(&e.position.y), sizeof(float)); + file.read(reinterpret_cast(&e.position.z), sizeof(float)); + + uint16_t nValues; + file.read(reinterpret_cast(&nValues), sizeof(uint16_t)); + e.data.resize(nValues); + file.read(reinterpret_cast(e.data.data()), nValues * sizeof(float)); + + uint16_t len; + file.read(reinterpret_cast(&len), sizeof(uint16_t)); + if (len > 0) { + std::string comment; + comment.resize(len); + file.read(comment.data(), len); + e.comment = std::move(comment); + } + + result.entries.push_back(std::move(e)); + } + + return result; +} + +void saveCachedFile(const Dataset& dataset, std::filesystem::path path) { + std::ofstream file(path, std::ofstream::binary); + + file.write(reinterpret_cast(&DataCacheFileVersion), sizeof(int8_t)); + + // + // Store variables + checkSize(dataset.variables.size(), "Too many variables"); + uint16_t nVariables = static_cast(dataset.variables.size()); + file.write(reinterpret_cast(&nVariables), sizeof(uint16_t)); + for (const Dataset::Variable& var : dataset.variables) { + checkSize(var.index, "Variable index too large"); + int16_t idx = static_cast(var.index); + file.write(reinterpret_cast(&idx), sizeof(int16_t)); + + checkSize(var.name.size(), "Variable name too long"); + uint16_t len = static_cast(var.name.size()); + file.write(reinterpret_cast(&len), sizeof(uint16_t)); + file.write(var.name.data(), len); + } + + // + // Store textures + checkSize(dataset.textures.size(), "Too many textures"); + uint16_t nTextures = static_cast(dataset.textures.size()); + file.write(reinterpret_cast(&nTextures), sizeof(uint16_t)); + for (const Dataset::Texture& tex : dataset.textures) { + checkSize(tex.index, "Texture index too large"); + int16_t idx = static_cast(tex.index); + file.write(reinterpret_cast(&idx), sizeof(int16_t)); + + + checkSize(tex.file.size(), "Texture file too long"); + uint16_t len = static_cast(tex.file.size()); + file.write(reinterpret_cast(&len), sizeof(uint16_t)); + file.write(tex.file.data(), len); + } + + // + // Store indices + checkSize(dataset.textureDataIndex, "Texture index too large"); + int16_t texIdx = static_cast(dataset.textureDataIndex); + file.write(reinterpret_cast(&texIdx), sizeof(int16_t)); + + checkSize(dataset.orientationDataIndex, "Orientation index too large"); + int16_t orientationIdx = static_cast(dataset.orientationDataIndex); + file.write(reinterpret_cast(&orientationIdx), sizeof(int16_t)); + + // + // Store entries + checkSize(dataset.entries.size(), "Too many entries"); + uint64_t nEntries = static_cast(dataset.entries.size()); + file.write(reinterpret_cast(&nEntries), sizeof(uint64_t)); + for (const Dataset::Entry& e : dataset.entries) { + file.write(reinterpret_cast(&e.position.x), sizeof(float)); + file.write(reinterpret_cast(&e.position.y), sizeof(float)); + file.write(reinterpret_cast(&e.position.z), sizeof(float)); + + checkSize(e.data.size(), "Too many data variables"); + uint16_t nValues = static_cast(e.data.size()); + file.write(reinterpret_cast(&nValues), sizeof(uint16_t)); + file.write( + reinterpret_cast(e.data.data()), + e.data.size() * sizeof(float) + ); + + if (e.comment.has_value()) { + checkSize(e.comment->size(), "Comment too long"); + } + uint16_t commentLen = e.comment.has_value() ? + static_cast(e.comment->size()) : + 0; + file.write(reinterpret_cast(&commentLen), sizeof(uint16_t)); + if (e.comment.has_value()) { + file.write(e.comment->data(), e.comment->size()); + } + } +} + +Dataset loadFileWithCache(std::filesystem::path speckPath, + SkipAllZeroLines skipAllZeroLines) +{ + return internalLoadFileWithCache( + speckPath, + skipAllZeroLines, + &loadFile, + &loadCachedFile, + &saveCachedFile + ); +} + +} // namespace data + +namespace label { + +Labelset loadFile(std::filesystem::path path, SkipAllZeroLines) { + ghoul_assert(std::filesystem::exists(path), "File must exist"); + + std::ifstream file(path); + if (!file.good()) { + throw ghoul::RuntimeError(fmt::format("Failed to open speck file '{}'", path)); + } + + Labelset res; + + std::string line; + // First phase: Loading the header information + while (std::getline(file, line)) { + // Ignore empty line or commented-out lines + if (line.empty() || line[0] == '#') { + continue; + } + + // Guard against wrong line endings (copying files from Windows to Mac) causes + // lines to have a final \r + if (line.back() == '\r') { + line = line.substr(0, line.length() - 1); + } + + strip(line); + + // If the first character is a digit, we have left the preamble and are in the + // data section of the file + if (std::isdigit(line[0]) || line[0] == '-') { + break; + } + + if (startsWith(line, "textcolor")) { + // each textcolor line is following the form: + // textcolor + // with being the index of the color into some configuration file (not + // really sure how these configuration files work, but they don't seem to be + // included in the speck file) + if (res.textColorIndex != -1) { + throw ghoul::RuntimeError(fmt::format( + "Error loading label file '{}': Textcolor defined twice", path + )); + } + + + std::stringstream str(line); + std::string dummy; + str >> dummy >> res.textColorIndex; + continue; + } + } + + // For the first line, we already loaded it and rejected it above, so if we do another + // std::getline, we'd miss the first data value line + bool isFirst = true; + while (isFirst || std::getline(file, line)) { + isFirst = false; + + // Ignore empty line or commented-out lines + if (line.empty() || line[0] == '#') { + continue; + } + + // Guard against wrong line endings (copying files from Windows to Mac) causes + // lines to have a final \r + if (line.back() == '\r') { + line = line.substr(0, line.length() - 1); + } + + strip(line); + + if (line.empty()) { + continue; + } + + // If the first character is a digit, we have left the preamble and are in the + // data section of the file + if (!std::isdigit(line[0]) && line[0] != '-') { + throw ghoul::RuntimeError(fmt::format( + "Error loading label file '{}': Header information and datasegment " + "intermixed", path + )); + } + + // Each line looks like this: + // text