From e98e9fe4a3af50a6db241420012dd2caa556855a Mon Sep 17 00:00:00 2001 From: Malin E Date: Fri, 5 Aug 2022 11:05:57 +0200 Subject: [PATCH] WIP initiali version of new constellation classes --- .../digitaluniverse/constellationbounds.asset | 2 +- .../digitaluniverse/constellations.asset | 3 +- .../rendering/renderabledumeshes.cpp | 6 +- modules/space/CMakeLists.txt | 6 + .../rendering/renderableconstellation.cpp | 379 ++++++++++++ .../space/rendering/renderableconstellation.h | 121 ++++ .../renderableconstellationbounds.cpp | 140 +---- .../rendering/renderableconstellationbounds.h | 30 +- .../renderableconstellationlines.cpp | 581 ++++++++++++++++++ .../rendering/renderableconstellationlines.h | 137 +++++ .../space/shaders/constellationlines_fs.glsl | 49 ++ .../space/shaders/constellationlines_vs.glsl | 47 ++ modules/space/spacemodule.cpp | 5 + modules/space/speckloader.cpp | 18 +- modules/space/speckloader.h | 1 + 15 files changed, 1373 insertions(+), 152 deletions(-) create mode 100644 modules/space/rendering/renderableconstellation.cpp create mode 100644 modules/space/rendering/renderableconstellation.h create mode 100644 modules/space/rendering/renderableconstellationlines.cpp create mode 100644 modules/space/rendering/renderableconstellationlines.h create mode 100644 modules/space/shaders/constellationlines_fs.glsl create mode 100644 modules/space/shaders/constellationlines_vs.glsl diff --git a/data/assets/scene/digitaluniverse/constellationbounds.asset b/data/assets/scene/digitaluniverse/constellationbounds.asset index c53f0bead2..273fad59da 100644 --- a/data/assets/scene/digitaluniverse/constellationbounds.asset +++ b/data/assets/scene/digitaluniverse/constellationbounds.asset @@ -16,7 +16,7 @@ local object = { Type = "RenderableConstellationBounds", Enabled = false, File = data .. "bound_20.dat", - ConstellationFile = data .. "constellations.dat" + ConstellationNamesFile = data .. "constellations.dat" -- ConstellationSelection = zodiacs }, Transform = { diff --git a/data/assets/scene/digitaluniverse/constellations.asset b/data/assets/scene/digitaluniverse/constellations.asset index 9bd8d72f18..3c0bcafa09 100644 --- a/data/assets/scene/digitaluniverse/constellations.asset +++ b/data/assets/scene/digitaluniverse/constellations.asset @@ -29,11 +29,12 @@ local constellationsExtragalactic = { local constellations = { Identifier = "Constellations", Renderable = { - Type = "RenderableDUMeshes", + Type = "RenderableConstellationLines", Enabled = false, Opacity = 0.3, File = speck .. "constellations.speck", LabelFile = speck .. "constellations.label", + ConstellationNamesFile = "C:/Users/malej60/Documents/Sync/http/digitaluniverse_constellationbounds_data/1/constellations.dat", TextColor = { 0.8, 0.8, 0.8 }, TextOpacity = 0.3, TextSize = 14.5, diff --git a/modules/digitaluniverse/rendering/renderabledumeshes.cpp b/modules/digitaluniverse/rendering/renderabledumeshes.cpp index 6ef7bd79d8..01dc22f4ca 100644 --- a/modules/digitaluniverse/rendering/renderabledumeshes.cpp +++ b/modules/digitaluniverse/rendering/renderabledumeshes.cpp @@ -311,6 +311,10 @@ void RenderableDUMeshes::initialize() { fillSelectionProperty(); + if (_assetSelectedMeshes.empty()) { + return; + } + const std::vector options = _selectedMeshes.options(); std::set selectedNames; @@ -613,7 +617,7 @@ bool RenderableDUMeshes::readSpeckFile() { dimOrName >> dummyU; // numU or "name" std::getline(dimOrName, dummyV); // numV or the name of the mesh - if (dummyU == "name") { + if (dummyU == "id") { mesh.name = dummyV; // Trim leading whitespace if (!mesh.name.empty() && mesh.name[0] == ' ') { diff --git a/modules/space/CMakeLists.txt b/modules/space/CMakeLists.txt index dc8f0ddfd2..cc294fce94 100644 --- a/modules/space/CMakeLists.txt +++ b/modules/space/CMakeLists.txt @@ -27,7 +27,9 @@ include(${OPENSPACE_CMAKE_EXT_DIR}/module_definition.cmake) set(HEADER_FILES horizonsfile.h speckloader.h + rendering/renderableconstellation.h rendering/renderableconstellationbounds.h + rendering/renderableconstellationlines.h rendering/renderablefluxnodes.h rendering/renderablehabitablezone.h rendering/renderablerings.h @@ -48,7 +50,9 @@ set(SOURCE_FILES horizonsfile.cpp spacemodule_lua.inl speckloader.cpp + rendering/renderableconstellation.cpp rendering/renderableconstellationbounds.cpp + rendering/renderableconstellationlines.cpp rendering/renderablefluxnodes.cpp rendering/renderablehabitablezone.cpp rendering/renderablerings.cpp @@ -68,6 +72,8 @@ source_group("Source Files" FILES ${SOURCE_FILES}) set(SHADER_FILES shaders/constellationbounds_fs.glsl shaders/constellationbounds_vs.glsl + shaders/constellationlines_fs.glsl + shaders/constellationlines_vs.glsl shaders/debrisViz_fs.glsl shaders/debrisViz_vs.glsl shaders/fluxnodes_fs.glsl diff --git a/modules/space/rendering/renderableconstellation.cpp b/modules/space/rendering/renderableconstellation.cpp new file mode 100644 index 0000000000..08f8d2d184 --- /dev/null +++ b/modules/space/rendering/renderableconstellation.cpp @@ -0,0 +1,379 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2022 * + * * + * 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 +#include +#include +#include +#include +#include +#include "SpiceUsr.h" + +namespace { + constexpr int RenderOptionViewDirection = 0; + constexpr int RenderOptionPositionNormal = 1; + + constexpr openspace::properties::Property::PropertyInfo TextColorInfo = { + "TextColor", + "Text Color", + "The text color for the astronomical object" + }; + + constexpr openspace::properties::Property::PropertyInfo TextOpacityInfo = { + "TextOpacity", + "Text Opacity", + "Determines the transparency of the text label, where 1 is completely opaque " + "and 0 fully transparent" + }; + + constexpr openspace::properties::Property::PropertyInfo TextSizeInfo = { + "TextSize", + "Text Size", + "The text size for the astronomical object labels" + }; + + constexpr openspace::properties::Property::PropertyInfo LabelFileInfo = { + "LabelFile", + "Label File", + "The path to the label file that contains information about the astronomical " + "objects being rendered" + }; + + constexpr openspace::properties::Property::PropertyInfo LabelMinMaxSizeInfo = { + "TextMinMaxSize", + "Text Min/Max Size", + "The minimum and maximum size (in pixels) of the text for the labels for the " + "astronomical objects being rendered" + }; + + constexpr openspace::properties::Property::PropertyInfo ConstellationInfo = { + "ConstellationFile", + "Constellation File Path", + "Specifies the file that contains the mapping between constellation " + "abbreviations and full name of the constellation. If this value is empty, the " + "abbreviations are used as the full names" + }; + + constexpr openspace::properties::Property::PropertyInfo LineWidthInfo = { + "LineWidth", + "Line Width", + "The line width of the constellation " + }; + + constexpr openspace::properties::Property::PropertyInfo DrawLabelInfo = { + "DrawLabels", + "Draw Labels", + "Determines whether labels should be drawn or hidden" + }; + + constexpr openspace::properties::Property::PropertyInfo RenderOptionInfo = { + "RenderOption", + "Render Option", + "Debug option for rendering of billboards and texts" + }; + + constexpr openspace::properties::Property::PropertyInfo SelectionInfo = { + "ConstellationSelection", + "Constellation Selection", + "The constellations that are selected are displayed on the celestial sphere" + }; + + struct [[codegen::Dictionary(RenderableConstellation)]] Parameters { + // [[codegen::verbatim(DrawLabelInfo.description)]] + std::optional drawLabels; + + // [[codegen::verbatim(ConstellationInfo.description)]] + std::string constellationNamesFile; + + // [[codegen::verbatim(TextColorInfo.description)]] + std::optional textColor [[codegen::color()]]; + + // [[codegen::verbatim(TextOpacityInfo.description)]] + std::optional textOpacity; + + // [[codegen::verbatim(TextSizeInfo.description)]] + std::optional textSize; + + // [[codegen::verbatim(LabelFileInfo.description)]] + std::optional labelFile; + + // [[codegen::verbatim(LabelMinMaxSizeInfo.description)]] + std::optional textMinMaxSize; + + // [[codegen::verbatim(LineWidthInfo.description)]] + std::optional lineWidth; + + // [[codegen::verbatim(SelectionInfo.description)]] + std::optional> constellationSelection; + }; +#include "renderableconstellation_codegen.cpp" +} // namespace + +namespace openspace { + +documentation::Documentation RenderableConstellation::Documentation() { + return codegen::doc("space_renderable_constellation"); +} + +RenderableConstellation::RenderableConstellation(const ghoul::Dictionary& dictionary) + : Renderable(dictionary) + , _textColor(TextColorInfo, glm::vec3(1.f), glm::vec3(0.f), glm::vec3(1.f)) + , _textOpacity(TextOpacityInfo, 1.f, 0.f, 1.f) + , _textSize(TextSizeInfo, 8.f, 0.5f, 24.f) + , _drawLabels(DrawLabelInfo, false) + , _textMinMaxSize( + LabelMinMaxSizeInfo, + glm::ivec2(8, 500), + glm::ivec2(0), + glm::ivec2(1000) + ) + , _lineWidth(LineWidthInfo, 2.f, 1.f, 16.f) + , _renderOption(RenderOptionInfo, properties::OptionProperty::DisplayType::Dropdown) + , _constellationNamesFilename(ConstellationInfo) + , _constellationSelection(SelectionInfo) +{ + const Parameters p = codegen::bake(dictionary); + + addProperty(_opacity); + registerUpdateRenderBinFromOpacity(); + + _renderOption.addOption(RenderOptionViewDirection, "Camera View Direction"); + _renderOption.addOption(RenderOptionPositionNormal, "Camera Position Normal"); + // @TODO (abock. 2021-01-31) In the other DU classes, this is done with an enum, and + // doing it based on the fisheye rendering seems a bit brittle? + if (global::windowDelegate->isFisheyeRendering()) { + _renderOption = RenderOptionPositionNormal; + } + else { + _renderOption = RenderOptionViewDirection; + } + addProperty(_renderOption); + + // Avoid reading the translation file here, instead do it in the initialize() + _constellationNamesFilename = p.constellationNamesFile; + _constellationNamesFilename.onChange([&](){ loadConstellationFile(); }); + addProperty(_constellationNamesFilename); + + _lineWidth = p.lineWidth.value_or(_lineWidth); + addProperty(_lineWidth); + + if (p.labelFile.has_value()) { + _labelFile = absPath(*p.labelFile).string(); + _hasLabel = true; + + _drawLabels = p.drawLabels.value_or(_drawLabels); + addProperty(_drawLabels); + + _textColor = p.textColor.value_or(_textColor); + _hasLabel = p.textColor.has_value(); + _textColor.setViewOption(properties::Property::ViewOptions::Color); + addProperty(_textColor); + _textColor.onChange([&]() { _textColorIsDirty = true; }); + + _textOpacity = p.textOpacity.value_or(_textOpacity); + addProperty(_textOpacity); + + _textSize = p.textSize.value_or(_textSize); + addProperty(_textSize); + + _textMinMaxSize = p.textMinMaxSize.value_or(_textMinMaxSize); + _textMinMaxSize.setViewOption(properties::Property::ViewOptions::MinMaxRange); + addProperty(_textMinMaxSize); + } + + fillSelectionProperty(); + _constellationSelection.onChange([this]() { selectionPropertyHasChanged(); }); + addProperty(_constellationSelection); + + if (p.constellationSelection.has_value()) { + const std::vector options = _constellationSelection.options(); + + std::set selectedNames; + for (const std::string& s : *p.constellationSelection) { + const auto it = std::find(options.begin(), options.end(), s); + if (it == options.end()) { + // The user has specified a constellation name that doesn't exist + LWARNINGC( + "RenderableConstellation", + fmt::format("Option '{}' not found in list of constellations", s) + ); + } + else { + selectedNames.insert(s); + } + } + _constellationSelection = selectedNames; + } +} + +bool RenderableConstellation::loadConstellationFile() { + if (_constellationNamesFilename.value().empty()) { + return true; + } + + std::ifstream file; + file.exceptions(std::ifstream::goodbit); + file.open(absPath(_constellationNamesFilename)); + + std::string line; + int index = 0; + while (file.good()) { + std::getline(file, line); + if (line.empty()) { + continue; + } + + std::string abbreviation; + std::stringstream s(line); + s >> abbreviation; + + std::string fullName; + std::getline(s, fullName); + ghoul::trimWhitespace(fullName); + _constellationNamesTranslation.insert({ abbreviation, fullName }); + + ++index; + } + + return true; +} + +void RenderableConstellation::fillSelectionProperty() { + for (const std::pair& pair : _constellationNamesTranslation) { + _constellationSelection.addOption(pair.second); + } +} + +void RenderableConstellation::initialize() { + loadConstellationFile(); + + if (!_hasLabel) { + return; + } + + if (!_font) { + constexpr int FontSize = 50; + _font = global::fontManager->font( + "Mono", + static_cast(FontSize), + ghoul::fontrendering::FontManager::Outline::Yes, + ghoul::fontrendering::FontManager::LoadGlyphs::No + ); + } + + std::string labelFile = _labelFile; + if (!labelFile.empty()) { + _labelset = speck::label::loadFileWithCache(_labelFile); + } + + for (speck::Labelset::Entry& entry : _labelset.entries) { + if (!entry.identifier.empty()) { + entry.text = _constellationNamesTranslation[entry.identifier]; + } + } +} + +void RenderableConstellation::render(const RenderData& data, RendererTasks&) { + const glm::dmat4 modelMatrix = + glm::translate(glm::dmat4(1.0), data.modelTransform.translation) * // Translation + glm::dmat4(data.modelTransform.rotation) * // Spice rotation + glm::scale(glm::dmat4(1.0), glm::dvec3(data.modelTransform.scale)); + + const glm::dmat4 modelViewMatrix = data.camera.combinedViewMatrix() * modelMatrix; + const glm::dmat4 projectionMatrix = data.camera.projectionMatrix(); + const glm::dmat4 modelViewProjectionMatrix = projectionMatrix * modelViewMatrix; + + const glm::vec3 lookup = data.camera.lookUpVectorWorldSpace(); + const glm::vec3 viewDirection = data.camera.viewDirectionWorldSpace(); + glm::vec3 right = glm::cross(viewDirection, lookup); + const glm::vec3 up = glm::cross(right, viewDirection); + + const glm::dmat4 worldToModelTransform = glm::inverse(modelMatrix); + glm::vec3 orthoRight = glm::normalize( + glm::vec3(worldToModelTransform * glm::vec4(right, 0.0)) + ); + + if (orthoRight == glm::vec3(0.0)) { + glm::vec3 otherVector(lookup.y, lookup.x, lookup.z); + right = glm::cross(viewDirection, otherVector); + orthoRight = glm::normalize( + glm::vec3(worldToModelTransform * glm::vec4(right, 0.0)) + ); + } + + if (_drawLabels && _hasLabel) { + const glm::vec3 orthoUp = glm::normalize( + glm::vec3(worldToModelTransform * glm::dvec4(up, 0.0)) + ); + renderLabels(data, modelViewProjectionMatrix, orthoRight, orthoUp); + } +} + +void RenderableConstellation::renderLabels(const RenderData& data, + const glm::dmat4& modelViewProjectionMatrix, + const glm::vec3& orthoRight, + const glm::vec3& orthoUp) +{ + float scale = static_cast(toMeter(_labelUnit)); + + ghoul::fontrendering::FontRenderer::ProjectedLabelsInformation labelInfo; + labelInfo.orthoRight = orthoRight; + labelInfo.orthoUp = orthoUp; + labelInfo.minSize = _textMinMaxSize.value().x; + labelInfo.maxSize = _textMinMaxSize.value().y; + labelInfo.cameraPos = data.camera.positionVec3(); + labelInfo.cameraLookUp = data.camera.lookUpVectorWorldSpace(); + labelInfo.renderType = _renderOption; + labelInfo.mvpMatrix = modelViewProjectionMatrix; + labelInfo.scale = pow(10.f, _textSize); + labelInfo.enableDepth = true; + labelInfo.enableFalseDepth = false; + + glm::vec4 textColor = glm::vec4(glm::vec3(_textColor), _textOpacity); + + for (const speck::Labelset::Entry& e : _labelset.entries) { + glm::vec3 scaledPos(e.position); + scaledPos *= scale; + ghoul::fontrendering::FontRenderer::defaultProjectionRenderer().render( + *_font, + scaledPos, + e.text, + textColor, + labelInfo + ); + } +} + +} // namespace openspace diff --git a/modules/space/rendering/renderableconstellation.h b/modules/space/rendering/renderableconstellation.h new file mode 100644 index 0000000000..fd1acd37eb --- /dev/null +++ b/modules/space/rendering/renderableconstellation.h @@ -0,0 +1,121 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2022 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#ifndef __OPENSPACE_MODULE_SPACE___RENDERABLECONSTELLATION___H__ +#define __OPENSPACE_MODULE_SPACE___RENDERABLECONSTELLATION___H__ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace ghoul::fontrendering { class Font; } +namespace ghoul::opengl { class ProgramObject; } + +namespace openspace { + +namespace documentation { struct Documentation; } + +class RenderableConstellation : public Renderable { +public: + virtual ~RenderableConstellation() override = default; + + virtual void initialize() override; + virtual void initializeGL() override = 0; + virtual void deinitialize() override = 0; + virtual void deinitializeGL() override = 0; + + virtual bool isReady() const override = 0; + + virtual void render(const RenderData& data, RendererTasks& rendererTask) override; + void renderLabels(const RenderData& data, const glm::dmat4& modelViewProjectionMatrix, + const glm::vec3& orthoRight, const glm::vec3& orthoUp); + virtual void update(const UpdateData& data) override = 0; + + static documentation::Documentation Documentation(); + +protected: + explicit RenderableConstellation(const ghoul::Dictionary& dictionary); + + /** + * Callback method that gets triggered when _constellationSelection + * changes. + */ + virtual void selectionPropertyHasChanged() = 0; + + // Map over the constellations names and theis abbreviations + // key = abbreviations, value = full name + std::map _constellationNamesTranslation; + + // Linewidth for the constellation bounds + properties::FloatProperty _lineWidth; + + /// The property that stores all indices of constellations that should be drawn + properties::SelectionProperty _constellationSelection; + + bool _hasLabel = false; + properties::BoolProperty _drawLabels; + speck::Labelset _labelset; + +private: + /** + * Loads the file specified in _constellationNamesFilename that contains the mapping + * between abbreviations and full names of constellations. + * + * \return true if the loading succeeded, false otherwise + */ + bool loadConstellationFile(); + + /// Fills the _constellationSelection property with all constellations + void fillSelectionProperty(); + + /// The file containing constellation names and abbreviations + properties::StringProperty _constellationNamesFilename; + + //Label text settings + std::string _labelFile; + properties::Vec3Property _textColor; + bool _textColorIsDirty = true; + properties::FloatProperty _textOpacity; + properties::FloatProperty _textSize; + properties::IVec2Property _textMinMaxSize; + std::shared_ptr _font = nullptr; + DistanceUnit _labelUnit = DistanceUnit::Parsec; + + properties::OptionProperty _renderOption; +}; + +} // namespace openspace + +#endif // __OPENSPACE_MODULE_SPACE___RENDERABLECONSTELLATION___H__ diff --git a/modules/space/rendering/renderableconstellationbounds.cpp b/modules/space/rendering/renderableconstellationbounds.cpp index 6a1c798c5c..998e82e127 100644 --- a/modules/space/rendering/renderableconstellationbounds.cpp +++ b/modules/space/rendering/renderableconstellationbounds.cpp @@ -50,14 +50,6 @@ namespace { "constellations" }; - constexpr openspace::properties::Property::PropertyInfo ConstellationInfo = { - "ConstellationFile", - "Constellation File Path", - "Specifies the file that contains the mapping between constellation " - "abbreviations and full name of the constellation. If this value is empty, the " - "abbreviations are used as the full names" - }; - constexpr openspace::properties::Property::PropertyInfo ColorInfo = { "Color", "Color of constellation lines", @@ -65,33 +57,12 @@ namespace { "full opacity" }; - constexpr openspace::properties::Property::PropertyInfo LineWidthInfo = { - "LineWidth", - "Line Width", - "The line width of the constellation bounds" - }; - - constexpr openspace::properties::Property::PropertyInfo SelectionInfo = { - "ConstellationSelection", - "Constellation Selection", - "The constellations that are selected are displayed on the celestial sphere" - }; - struct [[codegen::Dictionary(RenderableConstellationBounds)]] Parameters { // [[codegen::verbatim(VertexInfo.description)]] std::string file; - // [[codegen::verbatim(ConstellationInfo.description)]] - std::optional constellationFile; - // [[codegen::verbatim(ColorInfo.description)]] std::optional color [[codegen::color()]]; - - // [[codegen::verbatim(LineWidthInfo.description)]] - std::optional lineWidth; - - // [[codegen::verbatim(SelectionInfo.description)]] - std::optional> constellationSelection; }; #include "renderableconstellationbounds_codegen.cpp" } // namespace @@ -103,54 +74,27 @@ documentation::Documentation RenderableConstellationBounds::Documentation() { } RenderableConstellationBounds::RenderableConstellationBounds( - const ghoul::Dictionary& dictionary) - : Renderable(dictionary) + const ghoul::Dictionary& dictionary) + : RenderableConstellation(dictionary) , _vertexFilename(VertexInfo) - , _constellationFilename(ConstellationInfo) , _color(ColorInfo, glm::vec3(1.f, 0.f, 0.f), glm::vec3(0.f), glm::vec3(1.f)) - , _lineWidth(LineWidthInfo, 2.f, 1.f, 32.f) - , _constellationSelection(SelectionInfo) { const Parameters p = codegen::bake(dictionary); + // Avoid loading the vertex file here, do it in multithreded initialize() instead + _vertexFilename = p.file; _vertexFilename.onChange([&](){ loadVertexFile(); }); addProperty(_vertexFilename); - _vertexFilename = p.file; - - _constellationFilename.onChange([&](){ loadConstellationFile(); }); - _constellationFilename = p.constellationFile.value_or(_constellationFilename); - addProperty(_constellationFilename); _color.setViewOption(properties::Property::ViewOptions::Color); _color = p.color.value_or(_color); addProperty(_color); +} - _lineWidth = p.lineWidth.value_or(_lineWidth); - addProperty(_lineWidth); +void RenderableConstellationBounds::initialize() { + RenderableConstellation::initialize(); - fillSelectionProperty(); - _constellationSelection.onChange([this]() { selectionPropertyHasChanged(); }); - addProperty(_constellationSelection); - - if (p.constellationSelection.has_value()) { - const std::vector options = _constellationSelection.options(); - - std::set selectedNames; - for (const std::string& s : *p.constellationSelection) { - const auto it = std::find(options.begin(), options.end(), s); - if (it == options.end()) { - // The user has specified a constellation name that doesn't exist - LWARNINGC( - "RenderableConstellationBounds", - fmt::format("Option '{}' not found in list of constellations", s) - ); - } - else { - selectedNames.insert(s); - } - } - _constellationSelection = selectedNames; - } + loadVertexFile(); } void RenderableConstellationBounds::initializeGL() { @@ -179,6 +123,9 @@ void RenderableConstellationBounds::initializeGL() { glBindVertexArray(0); } +void RenderableConstellationBounds::deinitialize() { +} + void RenderableConstellationBounds::deinitializeGL() { glDeleteBuffers(1, &_vbo); _vbo = 0; @@ -192,10 +139,10 @@ void RenderableConstellationBounds::deinitializeGL() { } bool RenderableConstellationBounds::isReady() const { - return (_vao != 0) && (_vbo != 0) && _program; + return _program && _vao != 0 && _vbo != 0 && !_labelset.entries.empty(); } -void RenderableConstellationBounds::render(const RenderData& data, RendererTasks&) { +void RenderableConstellationBounds::render(const RenderData& data, RendererTasks& tasks) { _program->activate(); _program->setUniform("campos", glm::vec4(data.camera.positionVec3(), 1.f)); @@ -223,6 +170,12 @@ void RenderableConstellationBounds::render(const RenderData& data, RendererTasks } glBindVertexArray(0); _program->deactivate(); + + RenderableConstellation::render(data, tasks); +} + +void RenderableConstellationBounds::update(const UpdateData& data) { + } bool RenderableConstellationBounds::loadVertexFile() { @@ -326,61 +279,6 @@ bool RenderableConstellationBounds::loadVertexFile() { return true; } -bool RenderableConstellationBounds::loadConstellationFile() { - if (_constellationFilename.value().empty()) { - return true; - } - - std::ifstream file; - file.exceptions(std::ifstream::goodbit); - file.open(absPath(_constellationFilename)); - - std::string line; - int index = 0; - while (file.good()) { - std::getline(file, line); - if (line.empty()) { - continue; - } - - std::string abbreviation; - std::stringstream s(line); - s >> abbreviation; - - const auto it = std::find_if( - _constellationBounds.begin(), - _constellationBounds.end(), - [abbreviation](const ConstellationBound& bound) { - return bound.constellationAbbreviation == abbreviation; - } - ); - if (it == _constellationBounds.end()) { - LERRORC( - "RenderableConstellationBounds", - fmt::format("Could not find constellation '{}' in list", abbreviation) - ); - return false; - } - - // Update the constellations full name - std::string fullName; - std::getline(s, fullName); - ghoul::trimWhitespace(fullName); - it->constellationFullName = std::move(fullName); - - ++index; - } - - return true; -} - -void RenderableConstellationBounds::fillSelectionProperty() { - for (int i = 0 ; i < static_cast(_constellationBounds.size()); ++i) { - const ConstellationBound& bound = _constellationBounds[i]; - _constellationSelection.addOption(bound.constellationFullName); - } -} - void RenderableConstellationBounds::selectionPropertyHasChanged() { // If no values are selected (the default), we want to show all constellations if (!_constellationSelection.hasSelected()) { diff --git a/modules/space/rendering/renderableconstellationbounds.h b/modules/space/rendering/renderableconstellationbounds.h index bf52622de4..877187a4c4 100644 --- a/modules/space/rendering/renderableconstellationbounds.h +++ b/modules/space/rendering/renderableconstellationbounds.h @@ -25,9 +25,8 @@ #ifndef __OPENSPACE_MODULE_SPACE___RENDERABLECONSTELLATIONBOUNDS___H__ #define __OPENSPACE_MODULE_SPACE___RENDERABLECONSTELLATIONBOUNDS___H__ -#include +#include -#include #include #include #include @@ -48,16 +47,19 @@ namespace documentation { struct Documentation; } * _distance property. Currently, all constellation bounds are lines, which * leads to artifacts if the radius is very small. */ -class RenderableConstellationBounds : public Renderable { +class RenderableConstellationBounds : public RenderableConstellation { public: RenderableConstellationBounds(const ghoul::Dictionary& dictionary); + void initialize() override; void initializeGL() override; + void deinitialize() override; void deinitializeGL() override; bool isReady() const override; void render(const RenderData& data, RendererTasks& rendererTask) override; + void update(const UpdateData& data) override; static documentation::Documentation Documentation(); @@ -80,35 +82,18 @@ private: */ bool loadVertexFile(); - /** - * Loads the file specified in _constellationFilename that contains the mapping - * between abbreviations and full names of constellations. - * - * \return true if the loading succeeded, false otherwise - */ - bool loadConstellationFile(); - - /// Fills the _constellationSelection property with all constellations - void fillSelectionProperty(); - /** * Callback method that gets triggered when _constellationSelection * changes. */ - void selectionPropertyHasChanged(); + void selectionPropertyHasChanged() override; /// The filename containing the constellation bounds properties::StringProperty _vertexFilename; - /// The file containing constellation names - properties::StringProperty _constellationFilename; - /// Determines the color of the constellation lines properties::Vec3Property _color; - // Linewidth for the constellation bounds - properties::FloatProperty _lineWidth; - std::unique_ptr _program; /// The list of all loaded constellation bounds @@ -121,9 +106,6 @@ private: }; std::vector _vertexValues; ///< A list of all vertices of all bounds - /// The property that stores all indices of constellations that should be drawn - properties::SelectionProperty _constellationSelection; - GLuint _vao = 0; GLuint _vbo = 0; }; diff --git a/modules/space/rendering/renderableconstellationlines.cpp b/modules/space/rendering/renderableconstellationlines.cpp new file mode 100644 index 0000000000..06452e2621 --- /dev/null +++ b/modules/space/rendering/renderableconstellationlines.cpp @@ -0,0 +1,581 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2022 * + * * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace { + constexpr std::string_view _loggerCat = "RenderableConstellationLines"; + + constexpr std::array UniformNames = { + "modelViewTransform", "projectionTransform", "alphaValue", "color" + }; + + constexpr int RenderOptionViewDirection = 0; + constexpr int RenderOptionPositionNormal = 1; + + constexpr openspace::properties::Property::PropertyInfo DrawElementsInfo = { + "DrawElements", + "Draw Elements", + "Enables/Disables the drawing of the astronomical objects" + }; + + constexpr openspace::properties::Property::PropertyInfo MeshColorInfo = { + "MeshColor", + "Meshes colors", + "The defined colors for the meshes to be rendered" + }; + + struct [[codegen::Dictionary(RenderableConstellationLines)]] Parameters { + // The path to the SPECK file that contains information about the astronomical + // object being rendered + std::string file; + + enum class [[codegen::map(openspace::DistanceUnit)]] Unit { + Meter [[codegen::key("m")]], + Kilometer [[codegen::key("Km")]], + Parsec [[codegen::key("pc")]], + Kiloparsec [[codegen::key("Kpc")]], + Megaparsec [[codegen::key("Mpc")]], + Gigaparsec [[codegen::key("Gpc")]], + Gigalightyear [[codegen::key("Gly")]] + }; + std::optional unit; + + // [[codegen::verbatim(MeshColorInfo.description)]] + std::optional> meshColor; + }; +#include "renderableconstellationlines_codegen.cpp" +} // namespace + +namespace openspace { + +documentation::Documentation RenderableConstellationLines::Documentation() { + return codegen::doc("space_renderable_constellationlines"); +} + +RenderableConstellationLines::RenderableConstellationLines( + const ghoul::Dictionary& dictionary) + : RenderableConstellation(dictionary) + , _drawElements(DrawElementsInfo, true) +{ + const Parameters p = codegen::bake(dictionary); + + _speckFile = absPath(p.file).string(); + _hasSpeckFile = true; + _drawElements.onChange([&]() { _hasSpeckFile = !_hasSpeckFile; }); + addProperty(_drawElements); + + if (p.unit.has_value()) { + _unit = codegen::map(*p.unit); + } + else { + _unit = DistanceUnit::Meter; + } + + if (p.meshColor.has_value()) { + std::vector ops = *p.meshColor; + for (size_t i = 0; i < ops.size(); ++i) { + _meshColorMap.insert({ static_cast(i) + 1, ops[i] }); + } + } +} + +void RenderableConstellationLines::selectionPropertyHasChanged() { + // If no values are selected (the default), we want to show all constellations + if (!_constellationSelection.hasSelected()) { + for (std::pair& pair : _renderingMeshesMap) { + pair.second.isEnabled = true; + } + } + else { + // Enable all constellations that are selected + for (std::pair& pair : _renderingMeshesMap) { + pair.second.isEnabled = + _constellationSelection.isSelected(pair.second.identifier); + } + } +} + +bool RenderableConstellationLines::isReady() const { + return (_program != nullptr) && !_renderingMeshesMap.empty() && + !_labelset.entries.empty(); +} + +void RenderableConstellationLines::initialize() { + RenderableConstellation::initialize(); + + bool success = loadData(); + if (!success) { + throw ghoul::RuntimeError("Error loading data"); + } +} + +void RenderableConstellationLines::initializeGL() { + _program = DigitalUniverseModule::ProgramObjectManager.request( + "RenderableConstellationLines", + []() { + return global::renderEngine->buildRenderProgram( + "RenderableConstellationLines", + absPath("${MODULE_SPACE}/shaders/constellationlines_vs.glsl"), + absPath("${MODULE_SPACE}/shaders/constellationlines_fs.glsl") + ); + } + ); + + ghoul::opengl::updateUniformLocations(*_program, _uniformCache, UniformNames); + + createMeshes(); +} + +void RenderableConstellationLines::deinitialize() { +} + +void RenderableConstellationLines::deinitializeGL() { + for (const std::pair& pair : _renderingMeshesMap) { + for (int i = 0; i < pair.second.numU; ++i) { + glDeleteVertexArrays(1, &pair.second.vaoArray[i]); + glDeleteBuffers(1, &pair.second.vboArray[i]); + } + } + + DigitalUniverseModule::ProgramObjectManager.release( + "RenderableConstellationLines", + [](ghoul::opengl::ProgramObject* p) { + global::renderEngine->removeRenderProgram(p); + } + ); +} + +void RenderableConstellationLines::renderMeshes(const RenderData&, + const glm::dmat4& modelViewMatrix, + const glm::dmat4& projectionMatrix) +{ + glEnablei(GL_BLEND, 0); + glBlendFunc(GL_SRC_ALPHA, GL_ONE); + //glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDepthMask(false); + glEnable(GL_DEPTH_TEST); + + _program->activate(); + + _program->setUniform(_uniformCache.modelViewTransform, modelViewMatrix); + _program->setUniform(_uniformCache.projectionTransform, projectionMatrix); + _program->setUniform(_uniformCache.alphaValue, opacity()); + + for (const std::pair& pair : _renderingMeshesMap) { + if (!pair.second.isEnabled) { + continue; + } + + _program->setUniform(_uniformCache.color, _meshColorMap[pair.second.colorIndex]); + for (size_t i = 0; i < pair.second.vaoArray.size(); ++i) { + glBindVertexArray(pair.second.vaoArray[i]); + switch (pair.second.style) { + case Solid: + break; + case Wire: + glLineWidth(_lineWidth); + glDrawArrays(GL_LINE_STRIP, 0, pair.second.numV); + global::renderEngine->openglStateCache().resetLineState(); + break; + case Point: + glDrawArrays(GL_POINTS, 0, pair.second.numV); + break; + default: + break; + } + } + } + + glBindVertexArray(0); + _program->deactivate(); + + // Restores GL State + global::renderEngine->openglStateCache().resetDepthState(); + global::renderEngine->openglStateCache().resetBlendState(); +} + +void RenderableConstellationLines::render(const RenderData& data, RendererTasks& tasks) { + const glm::dmat4 modelMatrix = + glm::translate(glm::dmat4(1.0), data.modelTransform.translation) * // Translation + glm::dmat4(data.modelTransform.rotation) * // Spice rotation + glm::scale(glm::dmat4(1.0), glm::dvec3(data.modelTransform.scale)); + + const glm::dmat4 modelViewMatrix = data.camera.combinedViewMatrix() * modelMatrix; + const glm::dmat4 projectionMatrix = data.camera.projectionMatrix(); + + if (_hasSpeckFile) { + renderMeshes(data, modelViewMatrix, projectionMatrix); + } + + RenderableConstellation::render(data, tasks); +} + +void RenderableConstellationLines::update(const UpdateData&) { + if (_program->isDirty()) { + _program->rebuildFromFile(); + ghoul::opengl::updateUniformLocations(*_program, _uniformCache, UniformNames); + } +} + +bool RenderableConstellationLines::loadData() { + bool success = false; + if (_hasSpeckFile) { + LINFO(fmt::format("Loading Speck file {}", std::filesystem::path(_speckFile))); + success = readSpeckFile(); + if (!success) { + return false; + } + } + + return success; +} + +bool RenderableConstellationLines::readSpeckFile() { + std::ifstream file(_speckFile); + if (!file.good()) { + LERROR(fmt::format( + "Failed to open Speck file {}", std::filesystem::path(_speckFile) + )); + return false; + } + + const float scale = static_cast(toMeter(_unit)); + double maxRadius = 0.0; + + int meshIndex = 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); + + if (file.eof()) { + break; + } + + // 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::size_t found = line.find("mesh"); + if (found == std::string::npos) { + continue; + } + else { + // mesh lines are structured as follows: + // mesh -t texnum -c colorindex -s style { + // where textnum is the index of the texture; + // colorindex is the index of the color for the mesh + // and style is solid, wire or point (for now we support only wire) + std::stringstream str(line); + + RenderingMesh mesh; + mesh.meshIndex = meshIndex; + + std::string dummy; + str >> dummy; // mesh command + dummy.clear(); + str >> dummy; // texture index command? + do { + if (dummy == "-t") { + dummy.clear(); + str >> mesh.textureIndex; // texture index + } + else if (dummy == "-c") { + dummy.clear(); + str >> mesh.colorIndex; // color index command + } + else if (dummy == "-s") { + dummy.clear(); + str >> dummy; // style value command + if (dummy == "solid") { + mesh.style = Solid; + } + else if (dummy == "wire") { + mesh.style = Wire; + } + else if (dummy == "point") { + mesh.style = Point; + } + else { + mesh.style = INVALID; + break; + } + } + dummy.clear(); + str >> dummy; + } while (dummy != "{"); + + std::getline(file, line); + std::stringstream dimOrName(line); + std::string dummyU, dummyV; + + // Try to read name of mesh if it exist + dimOrName >> dummyU; // numU or "id" + std::getline(dimOrName, dummyV); // numV or the identifier of the mesh + + if (dummyU == "id") { + ghoul::trimWhitespace(dummyV); + mesh.identifier = _constellationNamesTranslation[dummyV]; + + // Dimensions are specified in the next line as usual + std::getline(file, line); + std::stringstream dim(line); + dim >> mesh.numU; // numU + dim >> mesh.numV; // numV + } + else { + mesh.numU = stoi(dummyU); + mesh.numV = stoi(dummyV); + } + + // We can now read the vertices data: + for (int l = 0; l < mesh.numU * mesh.numV; ++l) { + std::getline(file, line); + if (line.substr(0, 1) == "}") { + break; + } + + std::stringstream lineData(line); + + // Try to read three values for the position + glm::vec3 pos; + bool success = true; + for (int i = 0; i < 3; ++i) { + GLfloat value; + lineData >> value; + bool errorReading = lineData.rdstate() & std::ifstream::failbit; + if (errorReading) { + success = false; + break; + } + + GLfloat scaledValue = value * scale; + pos[i] = scaledValue; + mesh.vertices.push_back(scaledValue); + } + + if (!success) { + LERROR(fmt::format( + "Failed reading position on line {} of mesh {} in file: '{}'. " + "Stopped reading mesh data", l, meshIndex, _speckFile + )); + break; + } + + // Check if new max radius + const double r = glm::length(glm::dvec3(pos)); + maxRadius = std::max(maxRadius, r); + + // OLD CODE: + // (2022-03-23, emmbr) None of our files included texture coordinates, + // and if they would they would still not be used by the shader + //for (int i = 0; i < 7; ++i) { + // GLfloat value; + // lineData >> value; + // bool errorReading = lineData.rdstate() & std::ifstream::failbit; + // if (!errorReading) { + // mesh.vertices.push_back(value); + // } + // else { + // break; + // } + //} + } + + std::getline(file, line); + if (line.substr(0, 1) == "}") { + _renderingMeshesMap.insert({ meshIndex++, mesh }); + } + else { + return false; + } + } + } + setBoundingSphere(maxRadius); + + return true; +} + +void RenderableConstellationLines::createMeshes() { + if (!(_dataIsDirty && _hasSpeckFile)) { + return; + } + LDEBUG("Creating planes"); + + for (std::pair& p : _renderingMeshesMap) { + for (int i = 0; i < p.second.numU; ++i) { + GLuint vao; + glGenVertexArrays(1, &vao); + p.second.vaoArray.push_back(vao); + + GLuint vbo; + glGenBuffers(1, &vbo); + p.second.vboArray.push_back(vbo); + + glBindVertexArray(vao); + glBindBuffer(GL_ARRAY_BUFFER, vbo); + //glBufferData(GL_ARRAY_BUFFER, it->second.numV * sizeof(GLfloat), + glBufferData( + GL_ARRAY_BUFFER, + p.second.vertices.size() * sizeof(GLfloat), + &p.second.vertices[0], + GL_STATIC_DRAW + ); + // in_position + glEnableVertexAttribArray(0); + // (2022-03-23, emmbr) This code was actually never used. We only read three + // values per line and did not handle any texture cooridnates, even if there + // would have been some in the file + //// U and V may not be given by the user + //if (p.second.vertices.size() / (p.second.numU * p.second.numV) > 3) { + // glVertexAttribPointer( + // 0, + // 3, + // GL_FLOAT, + // GL_FALSE, + // sizeof(GLfloat) * 5, + // reinterpret_cast(sizeof(GLfloat) * i * p.second.numV) + // ); + + // // texture coords + // glEnableVertexAttribArray(1); + // glVertexAttribPointer( + // 1, + // 2, + // GL_FLOAT, + // GL_FALSE, + // sizeof(GLfloat) * 7, + // reinterpret_cast(sizeof(GLfloat) * 3 * i * p.second.numV) + // ); + //} + //else { // no U and V: + glVertexAttribPointer( + 0, + 3, + GL_FLOAT, + GL_FALSE, + 0, + reinterpret_cast(sizeof(GLfloat) * 3 * i * p.second.numV) + ); + //} + } + + // Grid: we need columns + if (p.second.numU > 1) { + for (int i = 0; i < p.second.numV; ++i) { + GLuint cvao; + glGenVertexArrays(1, &cvao); + p.second.vaoArray.push_back(cvao); + + GLuint cvbo; + glGenBuffers(1, &cvbo); + p.second.vboArray.push_back(cvbo); + + glBindVertexArray(cvao); + glBindBuffer(GL_ARRAY_BUFFER, cvbo); + glBufferData( + GL_ARRAY_BUFFER, + p.second.vertices.size() * sizeof(GLfloat), + &p.second.vertices[0], + GL_STATIC_DRAW + ); + // in_position + glEnableVertexAttribArray(0); + // U and V may not be given by the user + if (p.second.vertices.size() / (p.second.numU * p.second.numV) > 3) { + glVertexAttribPointer( + 0, + 3, + GL_FLOAT, + GL_FALSE, + p.second.numV * sizeof(GLfloat) * 5, + reinterpret_cast(sizeof(GLfloat) * i) + ); + + // texture coords + glEnableVertexAttribArray(1); + glVertexAttribPointer( + 1, + 2, + GL_FLOAT, + GL_FALSE, + p.second.numV * sizeof(GLfloat) * 7, + reinterpret_cast(sizeof(GLfloat) * 3 * i) + ); + } + else { // no U and V: + glVertexAttribPointer( + 0, + 3, + GL_FLOAT, + GL_FALSE, + p.second.numV * sizeof(GLfloat) * 3, + reinterpret_cast(sizeof(GLfloat) * 3 * i) + ); + } + } + } + } + + glBindVertexArray(0); + + _dataIsDirty = false; +} + +} // namespace openspace diff --git a/modules/space/rendering/renderableconstellationlines.h b/modules/space/rendering/renderableconstellationlines.h new file mode 100644 index 0000000000..985ebaaac3 --- /dev/null +++ b/modules/space/rendering/renderableconstellationlines.h @@ -0,0 +1,137 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2022 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#ifndef __OPENSPACE_MODULE_DIGITALUNIVERSE___RENDERABLECONSTELLATIONLINES___H__ +#define __OPENSPACE_MODULE_DIGITALUNIVERSE___RENDERABLECONSTELLATIONLINES___H__ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace ghoul::filesystem { class File; } +namespace ghoul::fontrendering { class Font; } +namespace ghoul::opengl { + class ProgramObject; + class Texture; +} // namespace ghoul::opengl + +namespace openspace { + +namespace documentation { struct Documentation; } + +class RenderableConstellationLines : public RenderableConstellation { +public: + explicit RenderableConstellationLines(const ghoul::Dictionary& dictionary); + ~RenderableConstellationLines() override = default; + + void initialize() override; + void initializeGL() override; + void deinitialize() override; + void deinitializeGL() override; + + bool isReady() const override; + + void render(const RenderData& data, RendererTasks& rendererTask) override; + void update(const UpdateData& data) override; + + static documentation::Documentation Documentation(); + +private: + enum MeshType { + Solid = 0, + Wire = 1, + Point = 2, + INVALID = 9 + }; + + struct RenderingMesh { + int meshIndex; + int colorIndex; + int textureIndex; + // From: Partiview User's Guide + // Brian Abbott + // Hayden Planetarium American Museum of Natural History New York, USA + // "Specifies the dimensions of the mesh" + // "If you wish to draw a line between points, then numU will be 1 while + // numV will equal the number of points to connect. + // If you want a square, 4000×4000 grid with lines every 200 units, + // then numU numV will both equal 21 + int numU; + int numV; + MeshType style; + std::vector vaoArray; + std::vector vboArray; + std::vector vertices; + bool isEnabled = true; + std::string identifier; + }; + + void createMeshes(); + void renderMeshes(const RenderData& data, const glm::dmat4& modelViewMatrix, + const glm::dmat4& projectionMatrix); + + bool loadData(); + bool readSpeckFile(); + + /** + * Callback method that gets triggered when _selectedMeshes + * changes. + */ + void selectionPropertyHasChanged(); + + bool _hasSpeckFile = false; + bool _dataIsDirty = true; + bool _textColorIsDirty = true; + std::vector _assetSelectedMeshes; + + properties::BoolProperty _drawElements; + + ghoul::opengl::ProgramObject* _program = nullptr; + UniformCache(modelViewTransform, projectionTransform, alphaValue, + color) _uniformCache; + std::shared_ptr _font = nullptr; + + std::string _speckFile; + + DistanceUnit _unit = DistanceUnit::Parsec; + + std::vector _fullData; + + std::unordered_map _meshColorMap; + std::unordered_map _renderingMeshesMap; +}; +} // namespace openspace + +#endif // __OPENSPACE_MODULE_DIGITALUNIVERSE___RENDERABLECONSTELLATIONLINES___H__ diff --git a/modules/space/shaders/constellationlines_fs.glsl b/modules/space/shaders/constellationlines_fs.glsl new file mode 100644 index 0000000000..243d5c66a5 --- /dev/null +++ b/modules/space/shaders/constellationlines_fs.glsl @@ -0,0 +1,49 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2022 * + * * + * 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 "fragment.glsl" + +in float vs_screenSpaceDepth; +in vec4 vs_positionViewSpace; + +uniform vec3 color; +uniform float alphaValue; + + +Fragment getFragment() { + Fragment frag; + + if (alphaValue == 0.0) { + discard; + } + + frag.color = vec4(color, alphaValue); + frag.depth = vs_screenSpaceDepth; + + // JCC: Need to change the position to camera space + frag.gPosition = vs_positionViewSpace; + frag.gNormal = vec4(0.0, 0.0, 0.0, 1.0); + + return frag; +} diff --git a/modules/space/shaders/constellationlines_vs.glsl b/modules/space/shaders/constellationlines_vs.glsl new file mode 100644 index 0000000000..e8611e67d3 --- /dev/null +++ b/modules/space/shaders/constellationlines_vs.glsl @@ -0,0 +1,47 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2022 * + * * + * 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. * + ****************************************************************************************/ + +#version __CONTEXT__ + +#include "PowerScaling/powerScaling_vs.hglsl" + +in vec3 in_position; + +out float vs_screenSpaceDepth; +out vec4 vs_positionViewSpace; + +uniform dmat4 modelViewTransform; +uniform dmat4 projectionTransform; + + +void main() { + dvec4 positionViewSpace = modelViewTransform * dvec4(in_position, 1.0); + vec4 positionClipSpace = vec4(projectionTransform * positionViewSpace); + vec4 positionScreenSpace = vec4(z_normalization(positionClipSpace)); + + vs_screenSpaceDepth = positionScreenSpace.w; + vs_positionViewSpace = vec4(positionViewSpace); + + gl_Position = positionScreenSpace; +} diff --git a/modules/space/spacemodule.cpp b/modules/space/spacemodule.cpp index 6bf6ec7c20..588d373799 100644 --- a/modules/space/spacemodule.cpp +++ b/modules/space/spacemodule.cpp @@ -25,6 +25,7 @@ #include #include +#include #include #include #include @@ -80,6 +81,9 @@ void SpaceModule::internalInitialize(const ghoul::Dictionary& dictionary) { fRenderable->registerClass( "RenderableConstellationBounds" ); + fRenderable->registerClass( + "RenderableConstellationLines" + ); fRenderable->registerClass("RenderableFluxNodes"); fRenderable->registerClass("RenderableHabitableZone"); fRenderable->registerClass("RenderableRings"); @@ -117,6 +121,7 @@ std::vector SpaceModule::documentations() const { HorizonsTranslation::Documentation(), KeplerTranslation::Documentation(), RenderableConstellationBounds::Documentation(), + RenderableConstellationLines::Documentation(), RenderableFluxNodes::Documentation(), RenderableHabitableZone::Documentation(), RenderableRings::Documentation(), diff --git a/modules/space/speckloader.cpp b/modules/space/speckloader.cpp index 05fa8fb817..a0811b3fce 100644 --- a/modules/space/speckloader.cpp +++ b/modules/space/speckloader.cpp @@ -321,7 +321,7 @@ Dataset loadFile(std::filesystem::path path, SkipAllZeroLines skipAllZeroLines) if (!str.good()) { // Need to subtract one of the line number here as we increase the current - // line count in the beginning of the while loop we are currently in + // line count in the beginning of the while loop we are currently in throw ghoul::RuntimeError(fmt::format( "Error loading position information out of data line {} in file {}. " "Value was not a number", @@ -346,7 +346,7 @@ Dataset loadFile(std::filesystem::path path, SkipAllZeroLines skipAllZeroLines) if (valueStream.fail()) { // Need to subtract one of the line number here as we increase the // current line count in the beginning of the while loop we are - // currently in + // currently in throw ghoul::RuntimeError(fmt::format( "Error loading data value {} out of data line {} in file {}. " "Value was not a number", @@ -674,10 +674,20 @@ Labelset loadFile(std::filesystem::path path, SkipAllZeroLines) { std::getline(str, rest); strip(rest); + if (startsWith(rest, "id")) { + // optional arument with identifier + // Remove the 'id' text + rest = rest.substr(std::string_view("id ").size()); + size_t index = rest.find("text"); + entry.identifier = rest.substr(0, index); + + // update the rest, remove the identifier + rest = rest.substr(index); + } if (!startsWith(rest, "text")) { throw ghoul::RuntimeError(fmt::format( - "Error loading label file {}: File contains some value between " - "positions and text label, which is unsupported", path + "Error loading label file {}: File contains an unsupported value " + "between positions and text label", path )); } diff --git a/modules/space/speckloader.h b/modules/space/speckloader.h index 0cc13c686e..8e48dbd764 100644 --- a/modules/space/speckloader.h +++ b/modules/space/speckloader.h @@ -68,6 +68,7 @@ struct Labelset { struct Entry { glm::vec3 position = glm::vec3(0.f); + std::string identifier; std::string text; }; std::vector entries;