/***************************************************************************************** * * * OpenSpace * * * * Copyright (c) 2014-2021 * * * * Permission is hereby granted, free of charge, to any person obtaining a copy of this * * software and associated documentation files (the "Software"), to deal in the Software * * without restriction, including without limitation the rights to use, copy, modify, * * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * * permit persons to whom the Software is furnished to do so, subject to the following * * conditions: * * * * The above copyright notice and this permission notice shall be included in all copies * * or substantial portions of the Software. * * * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ****************************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace { constexpr const char* MeterUnit = "m"; constexpr const char* KilometerUnit = "Km"; constexpr const char* MegameterUnit = "Mm"; constexpr const char* GigameterUnit = "Gm"; constexpr const char* AstronomicalUnit = "au"; constexpr const char* TerameterUnit = "Tm"; constexpr const char* PetameterUnit = "Pm"; constexpr const char* ParsecUnit = "pc"; constexpr const char* KiloparsecUnit = "Kpc"; constexpr const char* MegaparsecUnit = "Mpc"; constexpr const char* GigaparsecUnit = "Gpc"; constexpr const char* GigalightyearUnit = "Gly"; enum BlendMode { BlendModeNormal = 0, BlendModeAdditive }; constexpr const int ViewDirection = 0; constexpr const int NormalDirection = 1; constexpr double PARSEC = 0.308567756E17; constexpr openspace::properties::Property::PropertyInfo BlendModeInfo = { "BlendMode", "Blending Mode", "This determines the blending mode that is applied to this plane." }; constexpr openspace::properties::Property::PropertyInfo LabelColorInfo = { "LabelColor", "Label Color", "The label color for the astronomical object." }; constexpr openspace::properties::Property::PropertyInfo FontSizeInfo = { "FontSize", "Font Size", "The font size for the astronomical object labels." }; constexpr openspace::properties::Property::PropertyInfo LabelSizeInfo = { "LabelSize", "Label Size", "The label size for the astronomical object labels." }; constexpr openspace::properties::Property::PropertyInfo LabelTextInfo = { "LabelText", "Label Text", "The text that will be displayed on screen." }; constexpr openspace::properties::Property::PropertyInfo LabelMinSizeInfo = { "LabelMinSize", "Label Min Size", "The minimal size (in pixels) of the labels for the astronomical " "objects being rendered." }; constexpr openspace::properties::Property::PropertyInfo LabelMaxSizeInfo = { "LabelMaxSize", "Label Max Size", "The maximum size (in pixels) of the labels for the astronomical " "objects being rendered." }; constexpr openspace::properties::Property::PropertyInfo TransformationMatrixInfo = { "TransformationMatrix", "Transformation Matrix", "Transformation matrix to be applied to each astronomical object." }; constexpr openspace::properties::Property::PropertyInfo LabelOrientationOptionInfo = { "LabelOrientationOption", "Label Orientation Option", "Label orientation rendering mode." }; constexpr openspace::properties::Property::PropertyInfo EnableFadingEffectInfo = { "EnableFading", "Enable/Disable Fade-in effect", "Enable/Disable the Fade-in effect." }; constexpr openspace::properties::Property::PropertyInfo PixelSizeControlInfo = { "EnablePixelSizeControl", "Enable pixel size control.", "Enable pixel size control for rectangular projections." }; constexpr openspace::properties::Property::PropertyInfo FadeStartUnitOptionInfo = { "FadeStartUnit", "Fade-In/-Out Start Unit.", "Unit for fade-in/-out starting position calculation." }; constexpr openspace::properties::Property::PropertyInfo FadeEndUnitOptionInfo = { "FadeEndUnit", "Fade-In/-Out End Unit.", "Unit for fade-in/-out ending position calculation." }; constexpr openspace::properties::Property::PropertyInfo FadeStartDistInfo = { "FadeStartDistance", "Fade-In/-Out starting distance.", "Fade-In/-Out starting distance." }; constexpr openspace::properties::Property::PropertyInfo FadeEndDistInfo = { "FadeEndDistance", "Fade-In/-Out ending distance.", "Fade-In/-Out ending distance." }; constexpr openspace::properties::Property::PropertyInfo FadeStartSpeedInfo = { "FadeStartSpeed", "Fade-In/-Out starting speed.", "Fade-In/-Out starting speed." }; constexpr openspace::properties::Property::PropertyInfo FadeEndSpeedInfo = { "FadeEndSpeed", "Fade-In/-Out ending speed.", "Fade-In/-Out ending speed." }; struct [[codegen::Dictionary(RenderableLabels)]] Parameters { enum class BlendMode { Normal, Additive }; // [[codegen::verbatim(BlendModeInfo.description)]] std::optional blendMode; enum class Orientation { ViewDirection [[codegen::key("Camera View Direction")]], PositionNormal [[codegen::key("Camera Position Normal")]] }; // [[codegen::verbatim(LabelOrientationOptionInfo.description)]] std::optional labelOrientationOption; // [[codegen::verbatim(LabelColorInfo.description)]] std::optional labelColor [[codegen::color()]]; // [[codegen::verbatim(LabelTextInfo.description)]] std::optional labelText; // [[codegen::verbatim(FontSizeInfo.description)]] std::optional fontSize; // [[codegen::verbatim(LabelSizeInfo.description)]] std::optional labelSize; // [[codegen::verbatim(LabelMinSizeInfo.description)]] std::optional labelMinSize; // [[codegen::verbatim(LabelMaxSizeInfo.description)]] std::optional labelMaxSize; // [[codegen::verbatim(EnableFadingEffectInfo.description)]] std::optional enableFading; // [[codegen::verbatim(PixelSizeControlInfo.description)]] std::optional enablePixelControl; // [[codegen::verbatim(TransformationMatrixInfo.description)]] std::optional transformationMatrix; enum class Unit { Meter [[codegen::key("m")]], Kilometer [[codegen::key("Km")]], Megameter [[codegen::key("Mm")]], Gigameter [[codegen::key("Gm")]], Terameter [[codegen::key("Tm")]], Petameter [[codegen::key("Pm")]], AstronomicalUnit [[codegen::key("au")]], Parsec [[codegen::key("pc")]], KiloParsec [[codegen::key("Kpc")]], MegaParsec [[codgen::key("Mpc")]], GigaParsec [[codegen::key("Gpc")]], GigaLightyear [[codegen::key("Gly")]] }; // [[codegen::verbatim(FadeStartUnitOptionInfo.description)]] std::optional fadeStartUnit; // [[codegen::verbatim(FadeEndUnitOptionInfo.description)]] std::optional fadeEndUnit; // [[codegen::verbatim(FadeStartDistInfo.description)]] std::optional fadeStartDistance; // [[codegen::verbatim(FadeEndDistInfo.description)]] std::optional fadeEndDistance; // [[codegen::verbatim(FadeStartSpeedInfo.description)]] std::optional fadeStartSpeed; // [[codegen::verbatim(FadeEndSpeedInfo.description)]] std::optional fadeEndSpeed; }; #include "renderablelabels_codegen.cpp" } // namespace namespace openspace { documentation::Documentation RenderableLabels::Documentation() { return codegen::doc(); } RenderableLabels::RenderableLabels(const ghoul::Dictionary& dictionary) : Renderable(dictionary) , _blendMode(BlendModeInfo, properties::OptionProperty::DisplayType::Dropdown) , _labelColor( LabelColorInfo, glm::vec3(1.f, 1.f, 1.f), glm::vec3(0.f), glm::vec3(1.f) ) , _labelSize(LabelSizeInfo, 8.f, 0.5f, 30.f) , _fontSize(FontSizeInfo, 50.f, 1.f, 100.f) , _labelMinSize(LabelMinSizeInfo, 8.f, 0.5f, 24.f) , _labelMaxSize(LabelMaxSizeInfo, 20.f, 0.5f, 100.f) , _pixelSizeControl(PixelSizeControlInfo, false) , _enableFadingEffect(EnableFadingEffectInfo, false) , _labelText(LabelTextInfo, "") , _fadeStartDistance(FadeStartDistInfo, 1.f, 0.f, 100.f) , _fadeEndDistance(FadeEndDistInfo, 1.f, 0.f, 100.f) , _fadeStartSpeed(FadeStartSpeedInfo, 1.f, 1.f, 100.f) , _fadeEndSpeed(FadeEndSpeedInfo, 1.f, 1.f, 100.f) , _labelOrientationOption( LabelOrientationOptionInfo, properties::OptionProperty::DisplayType::Dropdown ) , _fadeStartUnitOption( FadeStartUnitOptionInfo, properties::OptionProperty::DisplayType::Dropdown ) , _fadeEndUnitOption( FadeEndUnitOptionInfo, properties::OptionProperty::DisplayType::Dropdown ) { const Parameters p = codegen::bake(dictionary); addProperty(_opacity); registerUpdateRenderBinFromOpacity(); _blendMode.addOptions({ { BlendModeNormal, "Normal" }, { BlendModeAdditive, "Additive"} }); _blendMode.onChange([&]() { switch (_blendMode) { case BlendModeNormal: setRenderBinFromOpacity(); break; case BlendModeAdditive: setRenderBin(Renderable::RenderBin::PreDeferredTransparent); break; default: throw ghoul::MissingCaseException(); } }); if (p.blendMode.has_value()) { switch (*p.blendMode) { case Parameters::BlendMode::Normal: _blendMode = BlendModeNormal; break; case Parameters::BlendMode::Additive: _blendMode = BlendModeAdditive; break; } } addProperty(_blendMode); _labelOrientationOption.addOption(ViewDirection, "Camera View Direction"); _labelOrientationOption.addOption(NormalDirection, "Camera Position Normal"); _labelOrientationOption = NormalDirection; if (p.labelOrientationOption.has_value()) { switch (*p.labelOrientationOption) { case Parameters::Orientation::ViewDirection: _labelOrientationOption = ViewDirection; break; case Parameters::Orientation::PositionNormal: _labelOrientationOption = NormalDirection; break; } } addProperty(_labelOrientationOption); _labelText = p.labelText.value_or(_labelText); addProperty(_labelText); _labelColor = p.labelColor.value_or(_labelColor); _labelColor.setViewOption(properties::Property::ViewOptions::Color); addProperty(_labelColor); _fontSize = p.fontSize.value_or(_fontSize); _fontSize.onChange([&]() { _font = global::fontManager->font( "Mono", _fontSize, ghoul::fontrendering::FontManager::Outline::Yes, ghoul::fontrendering::FontManager::LoadGlyphs::No ); }); addProperty(_fontSize); _labelSize = p.labelSize.value_or(_labelSize); addProperty(_labelSize); _labelMinSize = p.labelMinSize.value_or(_labelMinSize); addProperty(_labelMinSize); _labelMaxSize = p.labelMaxSize.value_or(_labelMaxSize); addProperty(_labelMaxSize); _transformationMatrix = p.transformationMatrix.value_or(_transformationMatrix); _pixelSizeControl = p.enablePixelControl.value_or(_pixelSizeControl); if (_pixelSizeControl) { // @TODO (abock, 2021-01-28) I don't know why we only add the property if the // pixel control is enabled, but I think this is an error addProperty(_pixelSizeControl); } _enableFadingEffect = p.enableFading.value_or(_enableFadingEffect); addProperty(_enableFadingEffect); _fadeStartDistance = p.fadeStartDistance.value_or(_fadeStartDistance); addProperty(_fadeStartDistance); _fadeStartUnitOption.addOption(Meter, MeterUnit); _fadeStartUnitOption.addOption(Kilometer, KilometerUnit); _fadeStartUnitOption.addOption(Megameter, MegameterUnit); _fadeStartUnitOption.addOption(Gigameter, GigameterUnit); _fadeStartUnitOption.addOption(AU, AstronomicalUnit); _fadeStartUnitOption.addOption(Terameter, TerameterUnit); _fadeStartUnitOption.addOption(Petameter, PetameterUnit); _fadeStartUnitOption.addOption(Parsec, ParsecUnit); _fadeStartUnitOption.addOption(Kiloparsec, KiloparsecUnit); _fadeStartUnitOption.addOption(Megaparsec, MegaparsecUnit); _fadeStartUnitOption.addOption(Gigaparsec, GigaparsecUnit); _fadeStartUnitOption.addOption(GigalightYears, GigalightyearUnit); if (p.fadeStartUnit.has_value()) { switch (*p.fadeStartUnit) { case Parameters::Unit::Meter: _fadeStartUnitOption = Meter; break; case Parameters::Unit::Kilometer: _fadeStartUnitOption = Kilometer; break; case Parameters::Unit::Megameter: _fadeStartUnitOption = Megameter; break; case Parameters::Unit::Gigameter: _fadeStartUnitOption = Gigameter; break; case Parameters::Unit::Terameter: _fadeStartUnitOption = Terameter; break; case Parameters::Unit::Petameter: _fadeStartUnitOption = Petameter; break; case Parameters::Unit::AstronomicalUnit: _fadeStartUnitOption = AU; break; case Parameters::Unit::Parsec: _fadeStartUnitOption = Parsec; break; case Parameters::Unit::KiloParsec: _fadeStartUnitOption = Kiloparsec; break; case Parameters::Unit::MegaParsec: _fadeStartUnitOption = Megaparsec; break; case Parameters::Unit::GigaParsec: _fadeStartUnitOption = Gigaparsec; break; case Parameters::Unit::GigaLightyear: _fadeStartUnitOption = GigalightYears; break; } } else { _fadeStartUnitOption = AU; } addProperty(_fadeStartUnitOption); _fadeStartSpeed = p.fadeStartSpeed.value_or(_fadeStartSpeed); addProperty(_fadeStartSpeed); _fadeEndDistance = p.fadeEndDistance.value_or(_fadeEndDistance); addProperty(_fadeEndDistance); _fadeEndUnitOption.addOption(Meter, MeterUnit); _fadeEndUnitOption.addOption(Kilometer, KilometerUnit); _fadeEndUnitOption.addOption(Megameter, MegameterUnit); _fadeEndUnitOption.addOption(Gigameter, GigameterUnit); _fadeEndUnitOption.addOption(AU, AstronomicalUnit); _fadeEndUnitOption.addOption(Terameter, TerameterUnit); _fadeEndUnitOption.addOption(Petameter, PetameterUnit); _fadeEndUnitOption.addOption(Parsec, ParsecUnit); _fadeEndUnitOption.addOption(Kiloparsec, KiloparsecUnit); _fadeEndUnitOption.addOption(Megaparsec, MegaparsecUnit); _fadeEndUnitOption.addOption(Gigaparsec, GigaparsecUnit); _fadeEndUnitOption.addOption(GigalightYears, GigalightyearUnit); if (p.fadeEndUnit.has_value()) { switch (*p.fadeEndUnit) { case Parameters::Unit::Meter: _fadeStartUnitOption = Meter; break; case Parameters::Unit::Kilometer: _fadeStartUnitOption = Kilometer; break; case Parameters::Unit::Megameter: _fadeStartUnitOption = Megameter; break; case Parameters::Unit::Gigameter: _fadeStartUnitOption = Gigameter; break; case Parameters::Unit::Terameter: _fadeStartUnitOption = Terameter; break; case Parameters::Unit::Petameter: _fadeStartUnitOption = Petameter; break; case Parameters::Unit::AstronomicalUnit: _fadeStartUnitOption = AU; break; case Parameters::Unit::Parsec: _fadeStartUnitOption = Parsec; break; case Parameters::Unit::KiloParsec: _fadeEndUnitOption = Kiloparsec; break; case Parameters::Unit::MegaParsec: _fadeEndUnitOption = Megaparsec; break; case Parameters::Unit::GigaParsec: _fadeEndUnitOption = Gigaparsec; break; case Parameters::Unit::GigaLightyear: _fadeEndUnitOption = GigalightYears; break; } } else { _fadeEndUnitOption = AU; } addProperty(_fadeEndUnitOption); _fadeEndSpeed = p.fadeEndSpeed.value_or(_fadeEndSpeed); addProperty(_fadeEndSpeed); } bool RenderableLabels::isReady() const { return true; } void RenderableLabels::initialize() { ZoneScoped setRenderBin(Renderable::RenderBin::PreDeferredTransparent); } void RenderableLabels::initializeGL() { if (_font == nullptr) { //size_t _fontSize = 50; _font = global::fontManager->font( "Mono", _fontSize, ghoul::fontrendering::FontManager::Outline::Yes, ghoul::fontrendering::FontManager::LoadGlyphs::No ); } } void RenderableLabels::deinitializeGL() {} void RenderableLabels::render(const RenderData& data, RendererTasks&) { //bool additiveBlending = (_blendMode == BlendModeAdditive); //if (additiveBlending) { glDepthMask(false); glBlendFunc(GL_SRC_ALPHA, GL_ONE); //} float fadeInVariable = 1.f; if (_enableFadingEffect) { float distanceNodeToCamera = static_cast( glm::distance(data.camera.positionVec3(), data.modelTransform.translation) ); float sUnit = unit(_fadeStartUnitOption); float eUnit = unit(_fadeEndUnitOption); float startX = _fadeStartDistance * sUnit; float endX = _fadeEndDistance * eUnit; fadeInVariable = linearSmoothStepFunc( distanceNodeToCamera, startX, endX, sUnit, eUnit ); } glm::dmat4 modelMatrix(1.0); glm::dmat4 modelViewMatrix = data.camera.combinedViewMatrix() * modelMatrix; glm::dmat4 projectionMatrix = glm::dmat4(data.camera.projectionMatrix()); glm::dmat4 modelViewProjectionMatrix = projectionMatrix * modelViewMatrix; glm::dvec3 cameraViewDirectionWorld = -data.camera.viewDirectionWorldSpace(); glm::dvec3 cameraUpDirectionWorld = data.camera.lookUpVectorWorldSpace(); glm::dvec3 orthoRight = glm::normalize( glm::cross(cameraUpDirectionWorld, cameraViewDirectionWorld) ); if (orthoRight == glm::dvec3(0.0)) { glm::dvec3 otherVector( cameraUpDirectionWorld.y, cameraUpDirectionWorld.x, cameraUpDirectionWorld.z ); orthoRight = glm::normalize(glm::cross(otherVector, cameraViewDirectionWorld)); } glm::dvec3 orthoUp = glm::normalize(glm::cross(cameraViewDirectionWorld, orthoRight)); renderLabels(data, modelViewProjectionMatrix, orthoRight, orthoUp, fadeInVariable); //if (additiveBlending) { glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glDepthMask(true); //} } void RenderableLabels::setLabelText(const std::string & newText) { _labelText = newText; } void RenderableLabels::renderLabels(const RenderData& data, const glm::dmat4& modelViewProjectionMatrix, const glm::dvec3& orthoRight, const glm::dvec3& orthoUp, float fadeInVariable) { glm::vec4 textColor = glm::vec4(glm::vec3(_labelColor), 1.f); textColor.a *= fadeInVariable; textColor.a *= _opacity; ghoul::fontrendering::FontRenderer::ProjectedLabelsInformation labelInfo; labelInfo.orthoRight = orthoRight; labelInfo.orthoUp = orthoUp; labelInfo.minSize = static_cast(_labelMinSize); labelInfo.maxSize = static_cast(_labelMaxSize); labelInfo.cameraPos = data.camera.positionVec3(); labelInfo.cameraLookUp = data.camera.lookUpVectorWorldSpace(); labelInfo.renderType = _labelOrientationOption; labelInfo.mvpMatrix = modelViewProjectionMatrix; labelInfo.scale = powf(10.f, _labelSize); labelInfo.enableDepth = true; labelInfo.enableFalseDepth = false; // We don't use spice rotation and scale glm::vec3 transformedPos( _transformationMatrix * glm::dvec4(data.modelTransform.translation, 1.0) ); ghoul::fontrendering::FontRenderer::defaultProjectionRenderer().render( *_font, transformedPos, _labelText.value(), textColor, labelInfo ); } float RenderableLabels::changedPerlinSmoothStepFunc(float x, float startX, float endX) const { float f1 = 6.f * powf((x - startX), 5.f) - 15.f * powf((x - startX), 4.f) + 10.f * powf((x - startX), 3.f); float f2 = -6.f * powf((x - endX), 5.f) + 15.f * powf((x - endX), 4.f) - 10.f * powf((x - endX), 3.f) + 1.f; float f3 = 1.f; if (x <= startX) { return std::clamp(f1, 0.f, 1.f); } else if (x > startX && x < endX) { return f3; } else if (x >= endX) { return std::clamp(f2, 0.f, 1.f); } return x; } float RenderableLabels::linearSmoothStepFunc(float x, float startX, float endX, float sUnit, float eUnit) const { float sdiv = 1.f / (sUnit * _fadeStartSpeed); float ediv = -1.f / (eUnit * _fadeEndSpeed); float f1 = sdiv * (x - startX) + 1.f; float f2 = ediv * (x - endX) + 1.f; float f3 = 1.f; if (x <= startX) { return std::clamp(f1, 0.f, 1.f); } else if (x > startX && x < endX) { return f3; } else if (x >= endX) { return std::clamp(f2, 0.f, 1.f); } return x; } float RenderableLabels::unit(int unit) const { switch (static_cast(unit)) { case Meter: return 1.f; case Kilometer: return 1e3f; case Megameter: return 1e6f; case Gigameter: return 1e9f; case AU: return 149597870700.f; case Terameter: return 1e12f; case Petameter: return 1e15f; case Parsec: return static_cast(PARSEC); case Kiloparsec: return static_cast(1e3 * PARSEC); case Megaparsec: return static_cast(1e6 * PARSEC); case Gigaparsec: return static_cast(1e9 * PARSEC); case GigalightYears: return static_cast(306391534.73091 * PARSEC); default: throw std::logic_error("Missing case label"); } } std::string RenderableLabels::toString(int unit) const { switch (static_cast(unit)) { case Meter: return MeterUnit; case Kilometer: return KilometerUnit; case Megameter: return MegameterUnit; case Gigameter: return GigameterUnit; case AU: return AstronomicalUnit; case Terameter: return TerameterUnit; case Petameter: return PetameterUnit; case Parsec: return ParsecUnit; case Kiloparsec: return KiloparsecUnit; case Megaparsec: return MegaparsecUnit; case Gigaparsec: return GigaparsecUnit; case GigalightYears: return GigalightyearUnit; default: throw std::logic_error("Missing case label"); } } } // namespace openspace