From 3d90ea194a8ebf375da1f385672af5c5c50fb140 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Tue, 20 Nov 2018 11:53:20 -0500 Subject: [PATCH] Feature/other data star renderer (#770) * Adding ability to render other data values from Star speck files * Add new method to OptionProperty to take vector of options * Make it possible to change the speck file at runtime * Include viridis color lut * Add Apogee and Galah datasets * Make it possible to filter stars by other data values * Add ability to statically filter data values --- data/assets/scene/digitaluniverse/stars.asset | 8 +- data/assets/scene/milkyway/gaia/apogee.asset | 46 ++ data/assets/scene/milkyway/gaia/galah.asset | 46 ++ data/assets/scene/milkyway/stars/denver.asset | 2 +- include/openspace/properties/optionproperty.h | 18 +- modules/space/rendering/renderablestars.cpp | 629 ++++++++++++------ modules/space/rendering/renderablestars.h | 44 +- modules/space/shaders/star_fs.glsl | 31 +- src/properties/optionproperty.cpp | 6 + 9 files changed, 596 insertions(+), 234 deletions(-) create mode 100644 data/assets/scene/milkyway/gaia/apogee.asset create mode 100644 data/assets/scene/milkyway/gaia/galah.asset diff --git a/data/assets/scene/digitaluniverse/stars.asset b/data/assets/scene/digitaluniverse/stars.asset index 7f87ad6ea8..67a6145985 100644 --- a/data/assets/scene/digitaluniverse/stars.asset +++ b/data/assets/scene/digitaluniverse/stars.asset @@ -20,10 +20,10 @@ local colorLUT = asset.syncedResource({ Name = "Stars Color Table", Type = "HttpSynchronization", Identifier = "stars_colormap", - Version = 1 + Version = 2 }) -local object = { +local stars = { Identifier = "Stars", Renderable = { Type = "RenderableStars", @@ -36,6 +36,4 @@ local object = { } } - - -assetHelper.registerSceneGraphNodesAndExport(asset, { object }) \ No newline at end of file +assetHelper.registerSceneGraphNodesAndExport(asset, { stars }) diff --git a/data/assets/scene/milkyway/gaia/apogee.asset b/data/assets/scene/milkyway/gaia/apogee.asset new file mode 100644 index 0000000000..44332682a8 --- /dev/null +++ b/data/assets/scene/milkyway/gaia/apogee.asset @@ -0,0 +1,46 @@ +local assetHelper = asset.require('util/asset_helper') + + + +local textures = asset.syncedResource({ + Name = "Stars Textures", + Type = "HttpSynchronization", + Identifier = "stars_textures", + Version = 1 +}) + +local speck = asset.syncedResource({ + Name = "Apogee Speck Files", + Type = "HttpSynchronization", + Identifier = "gaia_apogee", + Version = 1 +}) + +local colorLUT = asset.syncedResource({ + Name = "Stars Color Table", + Type = "HttpSynchronization", + Identifier = "stars_colormap", + Version = 2 +}) + +local gaia_abundance_apogee = { + Identifier = "Gaia Abundance Apogee", + Renderable = { + Type = "RenderableStars", + Enabled = false, + File = speck .. "/GaiaAbundApogee.speck", + ColorOption = "Other Data", + OtherData = "FeH", + ScaleFactor = 100, + Texture = textures .. "/halo.png", + ColorMap = colorLUT .. "/colorbv.cmap", + OtherDataColorMap = colorLUT .. "/viridis.cmap", + StaticFilter = -9999, + StaticFilterReplacement = 0.0 + }, + GUI = { + Path = "/Milky Way/Gaia" + } +} + +assetHelper.registerSceneGraphNodesAndExport(asset, { gaia_abundance_apogee }) diff --git a/data/assets/scene/milkyway/gaia/galah.asset b/data/assets/scene/milkyway/gaia/galah.asset new file mode 100644 index 0000000000..359e409111 --- /dev/null +++ b/data/assets/scene/milkyway/gaia/galah.asset @@ -0,0 +1,46 @@ +local assetHelper = asset.require('util/asset_helper') + + + +local textures = asset.syncedResource({ + Name = "Stars Textures", + Type = "HttpSynchronization", + Identifier = "stars_textures", + Version = 1 +}) + +local speck = asset.syncedResource({ + Name = "Galah Speck Files", + Type = "HttpSynchronization", + Identifier = "gaia_galah", + Version = 1 +}) + +local colorLUT = asset.syncedResource({ + Name = "Stars Color Table", + Type = "HttpSynchronization", + Identifier = "stars_colormap", + Version = 2 +}) + +local gaia_abundance_galah = { + Identifier = "Gaia Abundance Galah", + Renderable = { + Type = "RenderableStars", + Enabled = false, + File = speck .. "/GaiaAbundGalah.speck", + Texture = textures .. "/halo.png", + ColorOption = "Other Data", + OtherData = "FeH", + ScaleFactor = 100, + ColorMap = colorLUT .. "/colorbv.cmap", + OtherDataColorMap = colorLUT .. "/viridis.cmap", + StaticFilter = -9999, + StaticFilterReplacement = 0.0 + }, + GUI = { + Path = "/Milky Way/Gaia" + } +} + +assetHelper.registerSceneGraphNodesAndExport(asset, { gaia_abundance_galah }) diff --git a/data/assets/scene/milkyway/stars/denver.asset b/data/assets/scene/milkyway/stars/denver.asset index 86fc3ff621..bee27521e8 100644 --- a/data/assets/scene/milkyway/stars/denver.asset +++ b/data/assets/scene/milkyway/stars/denver.asset @@ -20,7 +20,7 @@ local colorLUT = asset.syncedResource({ Name = "Stars Color Table", Type = "HttpSynchronization", Identifier = "stars-denver_colormap", - Version = 1 + Version = 2 }) local object = { diff --git a/include/openspace/properties/optionproperty.h b/include/openspace/properties/optionproperty.h index 864b4b6277..a9d90332c2 100644 --- a/include/openspace/properties/optionproperty.h +++ b/include/openspace/properties/optionproperty.h @@ -102,13 +102,21 @@ public: void addOption(int value, std::string desc); /** - * Adds multiple options to the OptionProperty. Each value in the vector consists of - * an integer value and a string description. - * - * \param options Pairs of that are added to the OptionProperty - */ + * Adds multiple options to the OptionProperty. Each value in the vector consists of + * an integer value and a string description. + * + * \param options Pairs of that are added to the OptionProperty + */ void addOptions(std::vector> options); + /** + * Adds multiple options to the OptionProperty. Each value in the vector is assigned + * to its location. + * + * \param options A list of options that should be added to the OptionProperty + */ + void addOptions(std::vector options); + /** * Returns the list of available options. * diff --git a/modules/space/rendering/renderablestars.cpp b/modules/space/rendering/renderablestars.cpp index cb2453cef2..6572aed9ab 100644 --- a/modules/space/rendering/renderablestars.cpp +++ b/modules/space/rendering/renderablestars.cpp @@ -29,7 +29,6 @@ #include #include #include - #include #include #include @@ -38,7 +37,6 @@ #include #include #include - #include #include #include @@ -47,44 +45,45 @@ namespace { constexpr const char* _loggerCat = "RenderableStars"; constexpr const char* KeyFile = "File"; + constexpr const char* KeyStaticFilterValue = "StaticFilter"; + constexpr const char* KeyStaticFilterReplacement = "StaticFilterReplacement"; - constexpr const std::array UniformNames = { + constexpr const std::array UniformNames = { "view", "projection", "colorOption", "alphaValue", "scaleFactor", - "minBillboardSize", "screenSize", "scaling", "psfTexture", "colorTexture" + "minBillboardSize", "screenSize", "scaling", "psfTexture", "colorTexture", + "otherDataTexture", "otherDataRange", "filterOutOfRange" }; - constexpr int8_t CurrentCacheVersion = 1; + constexpr int8_t CurrentCacheVersion = 2; - struct ColorVBOLayout { + struct CommonDataLayout { std::array position; // (x,y,z,e) - - float bvColor; // B-V color value - float luminance; - float absoluteMagnitude; - }; - - struct VelocityVBOLayout { - std::array position; // (x,y,z,e) - - float bvColor; // B-V color value + float value; float luminance; float absoluteMagnitude; + }; + + struct ColorVBOLayout : public CommonDataLayout {}; + + struct VelocityVBOLayout : public CommonDataLayout { float vx; // v_x float vy; // v_y float vz; // v_z }; - struct SpeedVBOLayout { - std::array position; // (x,y,z,e) - - float bvColor; // B-V color value - float luminance; - float absoluteMagnitude; - + struct SpeedVBOLayout : public CommonDataLayout { float speed; }; + struct OtherDataLayout : public CommonDataLayout {}; + + constexpr openspace::properties::Property::PropertyInfo SpeckFileInfo = { + "SpeckFile", + "Speck File", + "The speck file that is loaded to get the data for rendering these stars." + }; + constexpr openspace::properties::Property::PropertyInfo PsfTextureInfo = { "Texture", "Point Spread Function Texture", @@ -126,6 +125,32 @@ namespace { "This value is used as a lower limit on the size of stars that are rendered. Any " "stars that have a smaller apparent size will be discarded entirely." }; + + constexpr openspace::properties::Property::PropertyInfo OtherDataOptionInfo = { + "OtherData", + "Other Data Column", + "The index of the speck file data column that is used as the color input" + }; + + constexpr openspace::properties::Property::PropertyInfo OtherDataValueRangeInfo = { + "OtherDataValueRange", + "Range of the other data values", + "This value is the min/max value range that is used to normalize the other data " + "values so they can be used by the specified color map." + }; + + constexpr openspace::properties::Property::PropertyInfo OtherDataColorMapInfo = { + "OtherDataColorMap", + "Other Data Color Map", + "The color map that is used if the 'Other Data' rendering method is selected" + }; + + constexpr openspace::properties::Property::PropertyInfo FilterOutOfRangeInfo = { + "FilterOutOfRange", + "Filter Out of Range", + "Determines whether other data values outside the value range should be visible " + "or filtered away" + }; } // namespace namespace openspace { @@ -162,11 +187,43 @@ documentation::Documentation RenderableStars::Documentation() { }, { ColorOptionInfo.identifier, - new StringInListVerifier({ - "Color", "Velocity", "Speed" - }), - Optional::Yes, - ColorOptionInfo.description + new StringInListVerifier({ "Color", "Velocity", "Speed", "Other Data" }), + Optional::Yes, + ColorOptionInfo.description + }, + { + OtherDataOptionInfo.identifier, + new StringVerifier, + Optional::Yes, + OtherDataOptionInfo.description + }, + { + OtherDataColorMapInfo.identifier, + new StringVerifier, + Optional::Yes, + OtherDataColorMapInfo.description + }, + { + FilterOutOfRangeInfo.identifier, + new BoolVerifier, + Optional::Yes, + FilterOutOfRangeInfo.description + }, + { + KeyStaticFilterValue, + new DoubleVerifier, + Optional::Yes, + "This value specifies a value that is always filtered out of the value " + "ranges on loading. This can be used to trim the dataset's automatic " + "value range." + }, + { + KeyStaticFilterReplacement, + new DoubleVerifier, + Optional::Yes, + "This is the value that is used to replace statically filtered values. " + "Setting this value only makes sense if 'StaticFilter' is 'true', as " + "well." }, { TransparencyInfo.identifier, @@ -192,22 +249,25 @@ documentation::Documentation RenderableStars::Documentation() { RenderableStars::RenderableStars(const ghoul::Dictionary& dictionary) : Renderable(dictionary) + , _speckFile(SpeckFileInfo) , _pointSpreadFunctionTexturePath(PsfTextureInfo) - , _pointSpreadFunctionTexture(nullptr) - , _pointSpreadFunctionTextureIsDirty(true) , _colorTexturePath(ColorTextureInfo) - , _colorTexture(nullptr) - , _colorTextureIsDirty(true) , _colorOption(ColorOptionInfo, properties::OptionProperty::DisplayType::Dropdown) - , _dataIsDirty(true) + , _otherDataOption( + OtherDataOptionInfo, + properties::OptionProperty::DisplayType::Dropdown + ) + , _otherDataColorMapPath(OtherDataColorMapInfo) + , _otherDataRange( + OtherDataValueRangeInfo, + glm::vec2(0.f, 1.f), + glm::vec2(-10.f, -10.f), + glm::vec2(10.f, 10.f) + ) + , _filterOutOfRange(FilterOutOfRangeInfo, false) , _alphaValue(TransparencyInfo, 1.f, 0.f, 1.f) , _scaleFactor(ScaleFactorInfo, 1.f, 0.f, 10.f) , _minBillboardSize(MinBillboardSizeInfo, 1.f, 1.f, 100.f) - , _program(nullptr) - , _speckFile("") - , _nValuesPerStar(0) - , _vao(0) - , _vbo(0) { using File = ghoul::filesystem::File; @@ -217,80 +277,117 @@ RenderableStars::RenderableStars(const ghoul::Dictionary& dictionary) "RenderableStars" ); + _speckFile = absPath(dictionary.value(KeyFile)); + _speckFile.onChange([&]() { _speckFileIsDirty = true; }); + addProperty(_speckFile); + _pointSpreadFunctionTexturePath = absPath(dictionary.value( PsfTextureInfo.identifier - )); + )); _pointSpreadFunctionFile = std::make_unique(_pointSpreadFunctionTexturePath); _colorTexturePath = absPath(dictionary.value( ColorTextureInfo.identifier + )); + + if (dictionary.hasKey(OtherDataColorMapInfo.identifier)) { + _otherDataColorMapPath = absPath(dictionary.value( + OtherDataColorMapInfo.identifier )); + } _colorTextureFile = std::make_unique(_colorTexturePath); - _speckFile = absPath(dictionary.value(KeyFile)); _colorOption.addOptions({ { ColorOption::Color, "Color" }, { ColorOption::Velocity, "Velocity" }, - { ColorOption::Speed, "Speed" } - }); + { ColorOption::Speed, "Speed" }, + { ColorOption::OtherData, "Other Data" } + }); if (dictionary.hasKey(ColorOptionInfo.identifier)) { const std::string colorOption = dictionary.value( ColorOptionInfo.identifier - ); + ); if (colorOption == "Color") { _colorOption = ColorOption::Color; } else if (colorOption == "Velocity") { _colorOption = ColorOption::Velocity; } - else { + else if (colorOption == "Speed") { _colorOption = ColorOption::Speed; } + else { + _colorOption = ColorOption::OtherData; + } } _colorOption.onChange([&] { _dataIsDirty = true; }); addProperty(_colorOption); - _pointSpreadFunctionTexturePath.onChange( - [&] { _pointSpreadFunctionTextureIsDirty = true; } - ); - _pointSpreadFunctionFile->setCallback( - [&](const File&) { _pointSpreadFunctionTextureIsDirty = true; } - ); + _pointSpreadFunctionTexturePath.onChange([&] { + _pointSpreadFunctionTextureIsDirty = true; + }); + _pointSpreadFunctionFile->setCallback([&](const File&) { + _pointSpreadFunctionTextureIsDirty = true; + }); addProperty(_pointSpreadFunctionTexturePath); _colorTexturePath.onChange([&] { _colorTextureIsDirty = true; }); - _colorTextureFile->setCallback( - [&](const File&) { _colorTextureIsDirty = true; } + _colorTextureFile->setCallback([&](const File&) { _colorTextureIsDirty = true; } ); addProperty(_colorTexturePath); if (dictionary.hasKey(TransparencyInfo.identifier)) { _alphaValue = static_cast( dictionary.value(TransparencyInfo.identifier) - ); + ); } addProperty(_alphaValue); if (dictionary.hasKey(ScaleFactorInfo.identifier)) { _scaleFactor = static_cast( dictionary.value(ScaleFactorInfo.identifier) - ); + ); } addProperty(_scaleFactor); if (dictionary.hasKey(MinBillboardSizeInfo.identifier)) { _minBillboardSize = static_cast( dictionary.value(MinBillboardSizeInfo.identifier) - ); + ); } addProperty(_minBillboardSize); + + if (dictionary.hasKey(OtherDataOptionInfo.identifier)) { + _queuedOtherData = dictionary.value(OtherDataOptionInfo.identifier); + } + + _otherDataOption.onChange([&]() { _dataIsDirty = true; }); + addProperty(_otherDataOption); + + addProperty(_otherDataRange); + + addProperty(_otherDataColorMapPath); + _otherDataColorMapPath.onChange([&]() { _otherDataColorMapIsDirty = true; }); + + if (dictionary.hasKey(KeyStaticFilterValue)) { + _staticFilterValue = static_cast( + dictionary.value(KeyStaticFilterValue) + ); + } + if (dictionary.hasKey(KeyStaticFilterReplacement)) { + _staticFilterReplacementValue = static_cast( + dictionary.value(KeyStaticFilterReplacement) + ); + } + + addProperty(_filterOutOfRange); } RenderableStars::~RenderableStars() {} // NOLINT bool RenderableStars::isReady() const { - return (_program != nullptr) && (!_fullData.empty()); + return _program != nullptr; } void RenderableStars::initializeGL() { @@ -302,10 +399,19 @@ void RenderableStars::initializeGL() { ghoul::opengl::updateUniformLocations(*_program, _uniformCache, UniformNames); - bool success = loadData(); - if (!success) { - throw ghoul::RuntimeError("Error loading data"); + loadData(); + + if (!_queuedOtherData.empty()) { + auto it = std::find(_dataNames.begin(), _dataNames.end(), _queuedOtherData); + if (it == _dataNames.end()) { + LERROR(fmt::format("Could not find other data column {}", _queuedOtherData)); + } + else { + _otherDataOption = std::distance(_dataNames.begin(), it); + _queuedOtherData.clear(); + } } + _speckFileIsDirty = false; } void RenderableStars::deinitializeGL() { @@ -324,6 +430,10 @@ void RenderableStars::deinitializeGL() { } void RenderableStars::render(const RenderData& data, RendererTasks&) { + if (_fullData.empty()) { + return; + } + glDepthMask(false); _program->activate(); @@ -352,9 +462,26 @@ void RenderableStars::render(const RenderData& data, RendererTasks&) { _program->setUniform(_uniformCache.psfTexture, psfUnit); ghoul::opengl::TextureUnit colorUnit; - colorUnit.activate(); - _colorTexture->bind(); - _program->setUniform(_uniformCache.colorTexture, colorUnit); + if (_colorTexture) { + colorUnit.activate(); + _colorTexture->bind(); + _program->setUniform(_uniformCache.colorTexture, colorUnit); + } + + ghoul::opengl::TextureUnit otherDataUnit; + if (_colorOption == ColorOption::OtherData && _otherDataColorMapTexture) { + otherDataUnit.activate(); + _otherDataColorMapTexture->bind(); + _program->setUniform(_uniformCache.otherDataTexture, otherDataUnit); + } + else { + // We need to set the uniform to something, or the shader doesn't work + _program->setUniform(_uniformCache.otherDataTexture, colorUnit); + } + // Same here, if we don't set this value, the rendering disappears even if we don't + // use this color mode --- abock 2018-11-19 + _program->setUniform(_uniformCache.otherDataRange, _otherDataRange); + _program->setUniform(_uniformCache.filterOutOfRange, _filterOutOfRange); glBindVertexArray(_vao); const GLsizei nStars = static_cast(_fullData.size() / _nValuesPerStar); @@ -367,6 +494,16 @@ void RenderableStars::render(const RenderData& data, RendererTasks&) { } void RenderableStars::update(const UpdateData&) { + if (_speckFileIsDirty) { + loadData(); + _speckFileIsDirty = false; + _dataIsDirty = true; + } + + if (_fullData.empty()) { + return; + } + if (_dataIsDirty) { const int value = _colorOption; LDEBUG("Regenerating data"); @@ -386,7 +523,7 @@ void RenderableStars::update(const UpdateData&) { glBufferData( GL_ARRAY_BUFFER, size * sizeof(GLfloat), - &_slicedData[0], + _slicedData.data(), GL_STATIC_DRAW ); @@ -402,87 +539,106 @@ void RenderableStars::update(const UpdateData&) { glEnableVertexAttribArray(brightnessDataAttrib); const int colorOption = _colorOption; switch (colorOption) { - case ColorOption::Color: - glVertexAttribPointer( - positionAttrib, - 4, - GL_FLOAT, - GL_FALSE, - stride, - nullptr // = offsetof(ColorVBOLayout, position) - ); - glVertexAttribPointer( - brightnessDataAttrib, - 3, - GL_FLOAT, - GL_FALSE, - stride, - reinterpret_cast(offsetof(ColorVBOLayout, bvColor)) - ); + case ColorOption::Color: + glVertexAttribPointer( + positionAttrib, + 4, + GL_FLOAT, + GL_FALSE, + stride, + nullptr // = offsetof(ColorVBOLayout, position) + ); + glVertexAttribPointer( + brightnessDataAttrib, + 3, + GL_FLOAT, + GL_FALSE, + stride, + reinterpret_cast(offsetof(ColorVBOLayout, value)) + ); - break; - case ColorOption::Velocity: - { - glVertexAttribPointer( - positionAttrib, - 4, - GL_FLOAT, - GL_FALSE, - stride, - nullptr // = offsetof(VelocityVBOLayout, position) - ); - glVertexAttribPointer( - brightnessDataAttrib, - 3, - GL_FLOAT, - GL_FALSE, - stride, - reinterpret_cast(offsetof(VelocityVBOLayout, bvColor)) //NOLINT - ); + break; + case ColorOption::Velocity: + { + glVertexAttribPointer( + positionAttrib, + 4, + GL_FLOAT, + GL_FALSE, + stride, + nullptr // = offsetof(VelocityVBOLayout, position) + ); + glVertexAttribPointer( + brightnessDataAttrib, + 3, + GL_FLOAT, + GL_FALSE, + stride, + reinterpret_cast(offsetof(VelocityVBOLayout, value)) //NOLINT + ); - GLint velocityAttrib = _program->attributeLocation("in_velocity"); - glEnableVertexAttribArray(velocityAttrib); - glVertexAttribPointer( - velocityAttrib, - 3, - GL_FLOAT, - GL_TRUE, - stride, - reinterpret_cast(offsetof(VelocityVBOLayout, vx)) // NOLINT - ); + GLint velocityAttrib = _program->attributeLocation("in_velocity"); + glEnableVertexAttribArray(velocityAttrib); + glVertexAttribPointer( + velocityAttrib, + 3, + GL_FLOAT, + GL_TRUE, + stride, + reinterpret_cast(offsetof(VelocityVBOLayout, vx)) // NOLINT + ); - break; - } - case ColorOption::Speed: - { - glVertexAttribPointer( - positionAttrib, - 4, - GL_FLOAT, - GL_FALSE, - stride, - nullptr // = offsetof(SpeedVBOLayout, position) - ); - glVertexAttribPointer( - brightnessDataAttrib, - 3, - GL_FLOAT, - GL_FALSE, - stride, - reinterpret_cast(offsetof(SpeedVBOLayout, bvColor)) // NOLINT - ); + break; + } + case ColorOption::Speed: + { + glVertexAttribPointer( + positionAttrib, + 4, + GL_FLOAT, + GL_FALSE, + stride, + nullptr // = offsetof(SpeedVBOLayout, position) + ); + glVertexAttribPointer( + brightnessDataAttrib, + 3, + GL_FLOAT, + GL_FALSE, + stride, + reinterpret_cast(offsetof(SpeedVBOLayout, value)) // NOLINT + ); - GLint speedAttrib = _program->attributeLocation("in_speed"); - glEnableVertexAttribArray(speedAttrib); - glVertexAttribPointer( - speedAttrib, - 1, - GL_FLOAT, - GL_TRUE, - stride, - reinterpret_cast(offsetof(SpeedVBOLayout, speed)) // NOLINT - ); - } + GLint speedAttrib = _program->attributeLocation("in_speed"); + glEnableVertexAttribArray(speedAttrib); + glVertexAttribPointer( + speedAttrib, + 1, + GL_FLOAT, + GL_TRUE, + stride, + reinterpret_cast(offsetof(SpeedVBOLayout, speed)) // NOLINT + ); + break; + } + case ColorOption::OtherData: + glVertexAttribPointer( + positionAttrib, + 4, + GL_FLOAT, + GL_FALSE, + stride, + nullptr // = offsetof(OtherDataLayout, position) + ); + glVertexAttribPointer( + brightnessDataAttrib, + 3, + GL_FLOAT, + GL_FALSE, + stride, + reinterpret_cast(offsetof(OtherDataLayout, value)) // NOLINT + ); + break; } glBindBuffer(GL_ARRAY_BUFFER, 0); @@ -512,11 +668,11 @@ void RenderableStars::update(const UpdateData&) { _pointSpreadFunctionFile = std::make_unique( _pointSpreadFunctionTexturePath - ); + ); _pointSpreadFunctionFile->setCallback( [&](const ghoul::filesystem::File&) { - _pointSpreadFunctionTextureIsDirty = true; - } + _pointSpreadFunctionTextureIsDirty = true; + } ); } _pointSpreadFunctionTextureIsDirty = false; @@ -539,7 +695,7 @@ void RenderableStars::update(const UpdateData&) { _colorTextureFile = std::make_unique( _colorTexturePath - ); + ); _colorTextureFile->setCallback( [&](const ghoul::filesystem::File&) { _colorTextureIsDirty = true; } ); @@ -547,30 +703,57 @@ void RenderableStars::update(const UpdateData&) { _colorTextureIsDirty = false; } + if (_otherDataColorMapIsDirty) { + LDEBUG("Reloading Color Texture"); + _otherDataColorMapTexture = nullptr; + if (_otherDataColorMapPath.value() != "") { + _otherDataColorMapTexture = ghoul::io::TextureReader::ref().loadTexture( + absPath(_otherDataColorMapPath) + ); + if (_otherDataColorMapTexture) { + LDEBUG(fmt::format( + "Loaded texture from '{}'", + absPath(_otherDataColorMapPath) + )); + _otherDataColorMapTexture->uploadTexture(); + } + } + _otherDataColorMapIsDirty = false; + + } + if (_program->isDirty()) { _program->rebuildFromFile(); ghoul::opengl::updateUniformLocations(*_program, _uniformCache, UniformNames); } } -bool RenderableStars::loadData() { +void RenderableStars::loadData() { std::string _file = _speckFile; + if (!FileSys.fileExists(absPath(_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 + cachedFile, _file )); bool success = loadCachedFile(cachedFile); if (success) { - return true; + return; } else { FileSys.cacheManager()->removeCacheFile(_file); @@ -583,27 +766,20 @@ bool RenderableStars::loadData() { } LINFO(fmt::format("Loading Speck file '{}'", _file)); - bool success = readSpeckFile(); - if (!success) { - return false; - } + readSpeckFile(); LINFO("Saving cache"); - success = saveCachedFile(cachedFile); - - return success; + saveCachedFile(cachedFile); } -bool RenderableStars::readSpeckFile() { +void RenderableStars::readSpeckFile() { std::string _file = _speckFile; std::ifstream file(_file); if (!file.good()) { LERROR(fmt::format("Failed to open Speck file '{}'", _file)); - return false; + return; } - _nValuesPerStar = 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') @@ -637,10 +813,18 @@ bool RenderableStars::readSpeckFile() { std::string dummy; str >> dummy; str >> _nValuesPerStar; + + std::string name; + str >> name; + + _dataNames.push_back(name); _nValuesPerStar += 1; // We want the number, but the index is 0 based } } + _otherDataOption.clearOptions(); + _otherDataOption.addOptions(_dataNames); + _nValuesPerStar += 3; // X Y Z are not counted in the Speck file indices do { @@ -663,8 +847,6 @@ bool RenderableStars::readSpeckFile() { _fullData.insert(_fullData.end(), values.begin(), values.end()); } } while (!file.eof()); - - return true; } bool RenderableStars::loadCachedFile(const std::string& file) { @@ -683,6 +865,16 @@ bool RenderableStars::loadCachedFile(const std::string& file) { fileStream.read(reinterpret_cast(&nValues), sizeof(int32_t)); fileStream.read(reinterpret_cast(&_nValuesPerStar), 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[0]), nValues * sizeof(_fullData[0])); @@ -696,32 +888,33 @@ bool RenderableStars::loadCachedFile(const std::string& file) { } } -bool RenderableStars::saveCachedFile(const std::string& file) const { +void RenderableStars::saveCachedFile(const std::string& file) const { std::ofstream fileStream(file, std::ofstream::binary); - if (fileStream.good()) { - 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 nValuesPerStar = static_cast(_nValuesPerStar); - fileStream.write(reinterpret_cast(&nValuesPerStar), sizeof(int32_t)); - - size_t nBytes = nValues * sizeof(_fullData[0]); - fileStream.write(reinterpret_cast(&_fullData[0]), nBytes); - - bool success = fileStream.good(); - return success; - } - else { + if (!fileStream.good()) { LERROR(fmt::format("Error opening file '{}' for save cache file", file)); - return false; + 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)); + + // -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 = _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) { @@ -739,29 +932,17 @@ void RenderableStars::createDataSlice(ColorOption option) { //} } + _otherDataRange = glm::vec2( + std::numeric_limits::max(), + -std::numeric_limits::max() + ); + for (size_t i = 0; i < _fullData.size(); i += _nValuesPerStar) { glm::vec3 p = glm::vec3(_fullData[i + 0], _fullData[i + 1], _fullData[i + 2]); - // This is only temporary until the scalegraph is in place. It places all stars - // on a sphere with a small variation in the distance to account for blending - // issues ---abock - //if (p != glm::vec3(0.f)) - // p = glm::normalize(p); - - //float distLy = _fullData[i + 6]; - //float normalizedDist = (distLy - minDistance) / (maxDistance - minDistance); - //float distance = 18.f - normalizedDist / 1.f ; - - - //psc position = psc(glm::vec4(p, distance)); - // Convert parsecs -> meter psc position = psc(glm::vec4(p * 0.308567756f, 17)); - //position[1] *= parsecsToMetersFactor[0]; - //position[2] *= parsecsToMetersFactor[0]; - //position[3] += parsecsToMetersFactor[1]; - switch (option) { case ColorOption::Color: { @@ -775,11 +956,11 @@ void RenderableStars::createDataSlice(ColorOption option) { } }; #ifdef USING_STELLAR_TEST_GRID - layout.value.bvColor = _fullData[i + 3]; + layout.value.value = _fullData[i + 3]; layout.value.luminance = _fullData[i + 3]; layout.value.absoluteMagnitude = _fullData[i + 3]; #else - layout.value.bvColor = _fullData[i + 3]; + layout.value.value = _fullData[i + 3]; layout.value.luminance = _fullData[i + 4]; layout.value.absoluteMagnitude = _fullData[i + 5]; #endif @@ -801,7 +982,7 @@ void RenderableStars::createDataSlice(ColorOption option) { position[0], position[1], position[2], position[3] } }; - layout.value.bvColor = _fullData[i + 3]; + layout.value.value = _fullData[i + 3]; layout.value.luminance = _fullData[i + 4]; layout.value.absoluteMagnitude = _fullData[i + 5]; @@ -825,7 +1006,7 @@ void RenderableStars::createDataSlice(ColorOption option) { position[0], position[1], position[2], position[3] } }; - layout.value.bvColor = _fullData[i + 3]; + layout.value.value = _fullData[i + 3]; layout.value.luminance = _fullData[i + 4]; layout.value.absoluteMagnitude = _fullData[i + 5]; @@ -836,6 +1017,44 @@ void RenderableStars::createDataSlice(ColorOption option) { layout.data.end()); break; } + case ColorOption::OtherData: + { + union { + OtherDataLayout value; + std::array data; + } layout; + + layout.value.position = { + { position[0], position[1], position[2], position[3] } + }; + + int index = _otherDataOption.value(); + layout.value.value = _fullData[i + index + 3]; + + if (_staticFilterValue.has_value() && + layout.value.value == _staticFilterValue) + { + layout.value.value = _staticFilterReplacementValue; + } + + glm::vec2 range = _otherDataRange.value(); + 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 + 4]; + layout.value.absoluteMagnitude = _fullData[i + 5]; + + _slicedData.insert( + _slicedData.end(), + layout.data.begin(), + layout.data.end() + ); + + break; + } } } } diff --git a/modules/space/rendering/renderablestars.h b/modules/space/rendering/renderablestars.h index fd2cd92e5a..1f6c6491b0 100644 --- a/modules/space/rendering/renderablestars.h +++ b/modules/space/rendering/renderablestars.h @@ -30,9 +30,10 @@ #include #include #include - +#include #include #include +#include namespace ghoul::filesystem { class File; } namespace ghoul::opengl { @@ -63,28 +64,32 @@ private: enum ColorOption { Color = 0, Velocity = 1, - Speed = 2 + Speed = 2, + OtherData = 3 }; void createDataSlice(ColorOption option); - bool loadData(); - bool readSpeckFile(); + void loadData(); + void readSpeckFile(); bool loadCachedFile(const std::string& file); - bool saveCachedFile(const std::string& file) const; + void saveCachedFile(const std::string& file) const; + + properties::StringProperty _speckFile; properties::StringProperty _pointSpreadFunctionTexturePath; std::unique_ptr _pointSpreadFunctionTexture; std::unique_ptr _pointSpreadFunctionFile; - bool _pointSpreadFunctionTextureIsDirty; properties::StringProperty _colorTexturePath; std::unique_ptr _colorTexture; std::unique_ptr _colorTextureFile; - bool _colorTextureIsDirty; - properties::OptionProperty _colorOption; - bool _dataIsDirty; + properties::OptionProperty _otherDataOption; + properties::StringProperty _otherDataColorMapPath; + properties::Vec2Property _otherDataRange; + std::unique_ptr _otherDataColorMapTexture; + properties::BoolProperty _filterOutOfRange; properties::FloatProperty _alphaValue; properties::FloatProperty _scaleFactor; @@ -92,16 +97,27 @@ private: std::unique_ptr _program; UniformCache(view, projection, colorOption, alphaValue, scaleFactor, - minBillboardSize, screenSize, scaling, psfTexture, colorTexture) _uniformCache; + minBillboardSize, screenSize, scaling, psfTexture, colorTexture, + otherDataTexture, otherDataRange, filterOutOfRange) _uniformCache; - std::string _speckFile; + bool _speckFileIsDirty = true; + bool _pointSpreadFunctionTextureIsDirty = true; + bool _colorTextureIsDirty = true; + bool _dataIsDirty = true; + bool _otherDataColorMapIsDirty = true; std::vector _slicedData; std::vector _fullData; - int _nValuesPerStar; + int _nValuesPerStar = 0; + std::string _queuedOtherData; + std::vector _dataNames; - GLuint _vao; - GLuint _vbo; + std::optional _staticFilterValue; + float _staticFilterReplacementValue = 0.f; + + + GLuint _vao = 0; + GLuint _vbo = 0; }; } // namespace openspace diff --git a/modules/space/shaders/star_fs.glsl b/modules/space/shaders/star_fs.glsl index 57cc7fd866..d6ef38ec0c 100644 --- a/modules/space/shaders/star_fs.glsl +++ b/modules/space/shaders/star_fs.glsl @@ -27,9 +27,10 @@ // keep in sync with renderablestars.h:ColorOption enum const int COLOROPTION_COLOR = 0; -const int COLOROPTION_VELOCITY = 1; +const int COLOROPTION_VELOCITY = 1; const int COLOROPTION_SPEED = 2; - +const int COLOROPTION_OTHERDATA = 3; + uniform sampler2D psfTexture; uniform sampler1D colorTexture; uniform float minBillboardSize; @@ -37,6 +38,10 @@ uniform float minBillboardSize; uniform float alphaValue; uniform int colorOption; +uniform sampler1D otherDataTexture; +uniform vec2 otherDataRange; +uniform bool filterOutOfRange; + in vec4 vs_position; in vec4 ge_gPosition; in vec3 ge_brightness; @@ -57,22 +62,40 @@ vec4 bv2rgb(float bv) { return texture(colorTexture, t); } +bool isOtherDataValueInRange() { + float t = (ge_brightness.x - otherDataRange.x) / (otherDataRange.y - otherDataRange.x); + return t >= 0.0 && t <= 1.0; +} +vec4 otherDataValue() { + float t = (ge_brightness.x - otherDataRange.x) / (otherDataRange.y - otherDataRange.x); + t = clamp(t, 0.0, 1.0); + return texture(otherDataTexture, t); +} + Fragment getFragment() { // Something in the color calculations need to be changed because before it was dependent // on the gl blend functions since the abuffer was not involved vec4 color = vec4(0.0); switch (colorOption) { - case COLOROPTION_COLOR: + case COLOROPTION_COLOR: color = bv2rgb(ge_brightness.x); break; case COLOROPTION_VELOCITY: - color = vec4(abs(ge_velocity), 0.5); + color = vec4(abs(ge_velocity), 0.5); break; case COLOROPTION_SPEED: // @TODO Include a transfer function here ---abock color = vec4(vec3(ge_speed), 0.5); break; + case COLOROPTION_OTHERDATA: + if (filterOutOfRange && !isOtherDataValueInRange()) { + discard; + } + else { + color = otherDataValue(); + } + break; } vec4 textureColor = texture(psfTexture, texCoord); diff --git a/src/properties/optionproperty.cpp b/src/properties/optionproperty.cpp index 540493b869..c7a0200b11 100644 --- a/src/properties/optionproperty.cpp +++ b/src/properties/optionproperty.cpp @@ -80,6 +80,12 @@ void OptionProperty::addOptions(std::vector> options } } +void OptionProperty::addOptions(std::vector options) { + for (int i = 0; i < static_cast(options.size()); ++i) { + addOption(i, std::move(options[i])); + } +} + void OptionProperty::clearOptions() { _options.clear(); _value = 0;