diff --git a/data/assets/scene/digitaluniverse/deepsky.asset b/data/assets/scene/digitaluniverse/deepsky.asset index c17f0d6c1f..cb4bcf2012 100644 --- a/data/assets/scene/digitaluniverse/deepsky.asset +++ b/data/assets/scene/digitaluniverse/deepsky.asset @@ -9,7 +9,7 @@ local speck = asset.resource({ Name = "Deep Sky Objects Speck Files", Type = "HttpSynchronization", Identifier = "digitaluniverse_deepsky_speck", - Version = 1 + Version = 2 }) @@ -57,19 +57,27 @@ local DeepSkyObjects = { local DeepSkyObjectsImages = { Identifier = "DeepSkyObjectsImages", Renderable = { - Type = "RenderablePlanesCloud", + Type = "RenderablePointCloud", Enabled = false, - Color = { 1.0, 1.0, 1.0 }, Opacity = 0.99, - ScaleFactor = 1.0, File = speck .. "dso.speck", - TexturePath = textures, - Luminosity = "radius", - ScaleLuminosity = 0.001, + Texture = { + Folder = textures + }, + -- Use fixed orientation, and rotate planes based on orientation information in + -- the dataset + OrientationRenderOption = "Fixed Rotation", + UseOrientationData = true, Unit = "pc", - -- Fade in value in the same unit as "Unit" - --FadeInDistances = {0.001, 0.05010}, - PlaneMinSize = 5.0 + SizeSettings = { + SizeMapping = { + ParameterOptions = { "radius" }, + ScaleFactor = "Parsec", + IsRadius = true + }, + -- No exponential scaling, just use size mapping to set the correct size + ScaleExponent = 0.0 + } }, Transform = { Rotation = { @@ -111,7 +119,7 @@ asset.export(DeepSkyObjectsImages) asset.meta = { Name = "Deep Sky Objects Images", - Version = "2.0", + Version = "3.0", Description = "Digital Universe asset for Deep Sky Objects and their Images", Author = "Nate Greenstein, Matt Everhart, Brian Abbott (AMNH)", URL = "https://www.amnh.org/research/hayden-planetarium/digital-universe", diff --git a/data/assets/scene/digitaluniverse/milkyway.asset b/data/assets/scene/digitaluniverse/milkyway.asset index 6e1445e36f..ee0c393536 100644 --- a/data/assets/scene/digitaluniverse/milkyway.asset +++ b/data/assets/scene/digitaluniverse/milkyway.asset @@ -1,34 +1,41 @@ -local planeTextures = asset.resource({ +local textures = asset.resource({ Name = "Milky Way Plane Textures", Type = "HttpSynchronization", Identifier = "digitaluniverse_milkyway_textures", Version = 2 }) -local planeSpeck = asset.resource({ +local speck = asset.resource({ Name = "Milky Way Plane Speck", Type = "HttpSynchronization", Identifier = "digitaluniverse_milkyway_speck", - Version = 1 + Version = 2 }) -local Plane = { +local Object = { Identifier = "MilkyWayGalaxyImage", - Parent = "Root", Renderable = { - Type = "RenderablePlanesCloud", - Enabled = true, - Color = { 1.0, 1.0, 1.0 }, + Type = "RenderablePointCloud", Opacity = 0.99, - ScaleFactor = 2.8, - File = planeSpeck .. "galaxy.speck", - TexturePath = planeTextures, - Luminosity = "size", - ScaleLuminosity = 1.0, - FadeInDistances = { 3000.0, 50000.0 }, - PlaneMinSize = 5.0, - Unit = "pc" + File = speck .. "galaxy.speck", + Texture = { + Folder = textures + }, + -- Use fixed orientation, and rotate planes based on orientation information in + -- the dataset + OrientationRenderOption = "Fixed Rotation", + UseOrientationData = true, + Unit = "pc", + Fading = { + FadeInDistances = { 16000.0, 100000.0 } -- Fade in value in the same unit as "Unit" + }, + SizeSettings = { + SizeMapping = { + ParameterOptions = { "size" } + }, + ScaleExponent = 16.936 + }, }, GUI = { Name = "Milky Way Galaxy Image", @@ -46,20 +53,20 @@ local Plane = { asset.onInitialize(function() - openspace.addSceneGraphNode(Plane) + openspace.addSceneGraphNode(Object) end) asset.onDeinitialize(function() - openspace.removeSceneGraphNode(Plane) + openspace.removeSceneGraphNode(Object) end) -asset.export(Plane) +asset.export(Object) asset.meta = { Name = "MilkyWay Galaxy", - Version = "2.1", + Version = "3.0", Description = [[Digital Universe asset containt 2D image of the MilkyWay. For extragalactic viewing]], Author = "Brian Abbott, Carter Emmart (AMNH)", diff --git a/data/assets/scene/digitaluniverse/milkyway_arm_labels.asset b/data/assets/scene/digitaluniverse/milkyway_arm_labels.asset index 4961fc648a..84b08bf565 100644 --- a/data/assets/scene/digitaluniverse/milkyway_arm_labels.asset +++ b/data/assets/scene/digitaluniverse/milkyway_arm_labels.asset @@ -1,34 +1,42 @@ -local planeTextures = asset.resource({ +local textures = asset.resource({ Name = "Milky Way Plane Textures", Type = "HttpSynchronization", Identifier = "digitaluniverse_milkyway_textures", Version = 2 }) -local planeSpeck = asset.resource({ +local speck = asset.resource({ Name = "Milky Way Plane Speck", Type = "HttpSynchronization", Identifier = "digitaluniverse_milkyway_speck", - Version = 1 + Version = 2 }) local Object = { Identifier = "MilkyWayGalaxyArmLabelsImage", - Parent = "Root", Renderable = { - Type = "RenderablePlanesCloud", + Type = "RenderablePointCloud", Enabled = false, - Color = { 1.0, 1.0, 1.0 }, Opacity = 0.99, - ScaleFactor = 2.8, - File = planeSpeck .. "galaxyArmLabels.speck", - TexturePath = planeTextures, - Luminosity = "size", - ScaleLuminosity = 1.0, - FadeInDistances = { 3000.0, 50000.0 }, - PlaneMinSize = 5.0, - Unit = "pc" + File = speck .. "galaxyArmLabels.speck", + Texture = { + Folder = textures + }, + -- Use fixed orientation, and rotate planes based on orientation information in + -- the dataset + OrientationRenderOption = "Fixed Rotation", + UseOrientationData = true, + Unit = "pc", + Fading = { + FadeInDistances = { 8000.0, 140000.0 } -- Fade in value in the same unit as "Unit" + }, + SizeSettings = { + SizeMapping = { + ParameterOptions = { "size" } + }, + ScaleExponent = 16.936 + }, }, GUI = { Name = "Milky Way Arms Labels", @@ -56,7 +64,7 @@ asset.export(Object) asset.meta = { Name = "Milky Way Arms Labels", - Version = "1.1", + Version = "2.0", Description = "Image with arm labels for the Milky Way galaxy", Author = "Brian Abbott (AMNH)", URL = "https://www.amnh.org/research/hayden-planetarium/digital-universe", diff --git a/data/assets/scene/digitaluniverse/tully.asset b/data/assets/scene/digitaluniverse/tully.asset index dbbd1db0b5..855f3bb862 100644 --- a/data/assets/scene/digitaluniverse/tully.asset +++ b/data/assets/scene/digitaluniverse/tully.asset @@ -9,7 +9,7 @@ local speck = asset.resource({ Name = "Tully Speck Files", Type = "HttpSynchronization", Identifier = "digitaluniverse_tully_speck", - Version = 3 + Version = 4 }) @@ -84,20 +84,30 @@ local TullyGalaxies = { local TullyGalaxiesImages = { Identifier = "TullyGalaxiesImages", Renderable = { - Type = "RenderablePlanesCloud", - Enabled = true, - Color = { 1.0, 1.0, 1.0 }, + Type = "RenderablePointCloud", Opacity = 0.99, - ScaleFactor = 1.0, File = speck .. "tully.speck", - TexturePath = textures, - Luminosity = "diamkpc", - ScaleLuminosity = 0.001, + SkipFirstDataPoint = true, + Texture = { + Folder = textures + }, TransformationMatrix = TransformMatrix, + -- Use fixed orientation, and rotate planes based on orientation information in + -- the dataset + OrientationRenderOption = "Fixed Rotation", + UseOrientationData = true, Unit = "Mpc", - -- Fade in value in the same unit as "Unit" - FadeInDistances = {0.0005, 0.003}, - PlaneMinSize = 1.0 + Fading = { + FadeInDistances = { 0.0005, 0.003 } -- Fade in value in the same unit as "Unit" + }, + SizeSettings = { + SizeMapping = { + ParameterOptions = { "diamkpc" }, + ScaleFactor = "Kiloparsec" + }, + -- No exponential scaling, just use size mapping to set the correct size + ScaleExponent = 0.0 + } }, GUI = { Name = "Tully Galaxies Images", @@ -133,7 +143,7 @@ asset.export(TullyGalaxiesImages) asset.meta = { Name = "Tully Galaxies", - Version = "4.0", + Version = "5.0", Description = [[Digital Universe asset for Tully Galaxies, including point cloud and images]], Author = "Stuart Levy (NCSA/UIUC), Brian Abbott (AMNH)", diff --git a/modules/base/CMakeLists.txt b/modules/base/CMakeLists.txt index 53ad712da3..54bbd06ec7 100644 --- a/modules/base/CMakeLists.txt +++ b/modules/base/CMakeLists.txt @@ -168,10 +168,10 @@ set(SHADER_FILES shaders/model_vs.glsl shaders/plane_fs.glsl shaders/plane_vs.glsl - shaders/pointcloud/billboardpoint_fs.glsl - shaders/pointcloud/billboardpoint_gs.glsl - shaders/pointcloud/billboardpoint_vs.glsl - shaders/pointcloud/billboardpoint_interpolated_vs.glsl + shaders/pointcloud/pointcloud_fs.glsl + shaders/pointcloud/pointcloud_gs.glsl + shaders/pointcloud/pointcloud_vs.glsl + shaders/pointcloud/pointcloud_interpolated_vs.glsl shaders/polygon_fs.glsl shaders/polygon_gs.glsl shaders/polygon_vs.glsl diff --git a/modules/base/rendering/pointcloud/renderableinterpolatedpoints.cpp b/modules/base/rendering/pointcloud/renderableinterpolatedpoints.cpp index be13a0ab67..5d04e0242a 100644 --- a/modules/base/rendering/pointcloud/renderableinterpolatedpoints.cpp +++ b/modules/base/rendering/pointcloud/renderableinterpolatedpoints.cpp @@ -321,12 +321,12 @@ void RenderableInterpolatedPoints::initializeShadersAndGlExtras() { _program = BaseModule::ProgramObjectManager.request( "RenderablePointCloud_Interpolated", []() { - std::filesystem::path path = absPath("${MODULE_BASE}/shaders/pointcloud/"); + std::filesystem::path path = absPath("${MODULE_BASE}/shaders/pointcloud"); return global::renderEngine->buildRenderProgram( "RenderablePointCloud_Interpolated", - path / "billboardpoint_interpolated_vs.glsl", - path / "billboardpoint_fs.glsl", - path / "billboardpoint_gs.glsl" + path / "pointcloud_interpolated_vs.glsl", + path / "pointcloud_fs.glsl", + path / "pointcloud_gs.glsl" ); } ); @@ -368,9 +368,13 @@ int RenderableInterpolatedPoints::nAttributesPerPoint() const { // Use two more positions (xyz) n += 2 * 3; } + if (useOrientationData()) { + // Use one more orientation quaternion (wxyz) + n += 4; + } // And potentially some more color and size data - n += _hasColorMapFile ? 1 : 0; - n += _hasDatavarSize ? 1 : 0; + n += hasColorData() ? 1 : 0; + n += hasSizeData() ? 1 : 0; return n; } @@ -430,29 +434,54 @@ void RenderableInterpolatedPoints::addPositionDataForPoint(unsigned int index, } void RenderableInterpolatedPoints::addColorAndSizeDataForPoint(unsigned int index, - std::vector& result) const + std::vector& result) const { using namespace dataloader; auto [firstIndex, secondIndex] = interpolationIndices(index); const Dataset::Entry& e0 = _dataset.entries[firstIndex]; const Dataset::Entry& e1 = _dataset.entries[secondIndex]; - int colorParamIndex = currentColorParameterIndex(); - if (_hasColorMapFile && colorParamIndex >= 0) { + if (hasColorData()) { + const int colorParamIndex = currentColorParameterIndex(); result.push_back(e0.data[colorParamIndex]); result.push_back(e1.data[colorParamIndex]); } - int sizeParamIndex = currentSizeParameterIndex(); - if (_hasDatavarSize && sizeParamIndex >= 0) { + if (hasSizeData()) { + const int sizeParamIndex = currentSizeParameterIndex(); // @TODO: Consider more detailed control over the scaling. Currently the value // is multiplied with the value as is. Should have similar mapping properties // as the color mapping - result.push_back(e0.data[sizeParamIndex]); - result.push_back(e1.data[sizeParamIndex]); + + // Convert to diameter if data is given as radius + float multiplier = _sizeSettings.sizeMapping->isRadius ? 2.f : 1.f; + result.push_back(multiplier * e0.data[sizeParamIndex]); + result.push_back(multiplier * e1.data[sizeParamIndex]); } } +void RenderableInterpolatedPoints::addOrientationDataForPoint(unsigned int index, + std::vector& result) const +{ + using namespace dataloader; + auto [firstIndex, secondIndex] = interpolationIndices(index); + const Dataset::Entry& e0 = _dataset.entries[firstIndex]; + const Dataset::Entry& e1 = _dataset.entries[secondIndex]; + + glm::quat q0 = orientationQuaternion(e0); + glm::quat q1 = orientationQuaternion(e1); + + result.push_back(q0.x); + result.push_back(q0.y); + result.push_back(q0.z); + result.push_back(q0.w); + + result.push_back(q1.x); + result.push_back(q1.y); + result.push_back(q1.z); + result.push_back(q1.w); +} + void RenderableInterpolatedPoints::initializeBufferData() { if (_vao == 0) { glGenVertexArrays(1, &_vao); @@ -481,16 +510,21 @@ void RenderableInterpolatedPoints::initializeBufferData() { offset = bufferVertexAttribute("in_position_after", 3, attibutesPerPoint, offset); } - if (_hasColorMapFile) { + if (hasColorData()) { offset = bufferVertexAttribute("in_colorParameter0", 1, attibutesPerPoint, offset); offset = bufferVertexAttribute("in_colorParameter1", 1, attibutesPerPoint, offset); } - if (_hasDatavarSize) { + if (hasSizeData()) { offset = bufferVertexAttribute("in_scalingParameter0", 1, attibutesPerPoint, offset); offset = bufferVertexAttribute("in_scalingParameter1", 1, attibutesPerPoint, offset); } + if (useOrientationData()) { + offset = bufferVertexAttribute("in_orientation0", 4, attibutesPerPoint, offset); + offset = bufferVertexAttribute("in_orientation1", 4, attibutesPerPoint, offset); + } + if (_hasSpriteTexture) { offset = bufferVertexAttribute("in_textureLayer", 1, attibutesPerPoint, offset); } diff --git a/modules/base/rendering/pointcloud/renderableinterpolatedpoints.h b/modules/base/rendering/pointcloud/renderableinterpolatedpoints.h index c457729253..085fbb442e 100644 --- a/modules/base/rendering/pointcloud/renderableinterpolatedpoints.h +++ b/modules/base/rendering/pointcloud/renderableinterpolatedpoints.h @@ -63,7 +63,7 @@ protected: /** * Create the rendering data for the positions for the point with the given index * and append that to the result. Compared to the base class, this class may require - * 2-4 positions, depending on if * spline interpolation is used or not. + * 2-4 positions, depending on if spline interpolation is used or not. * * The values are computed based on the current interpolation value. * @@ -82,6 +82,9 @@ protected: void addColorAndSizeDataForPoint(unsigned int index, std::vector& result) const override; + void addOrientationDataForPoint(unsigned int index, + std::vector& result) const override; + void initializeBufferData(); void updateBufferData() override; diff --git a/modules/base/rendering/pointcloud/renderablepointcloud.cpp b/modules/base/rendering/pointcloud/renderablepointcloud.cpp index 8d8ed3aef9..861b2edb4e 100644 --- a/modules/base/rendering/pointcloud/renderablepointcloud.cpp +++ b/modules/base/rendering/pointcloud/renderablepointcloud.cpp @@ -44,6 +44,8 @@ #include #include #include +#include +#include #include #include #include @@ -56,7 +58,7 @@ namespace { constexpr std::string_view _loggerCat = "RenderablePointCloud"; - constexpr std::array UniformNames = { + constexpr std::array UniformNames = { "cameraViewMatrix", "projectionMatrix", "modelMatrix", "cameraPosition", "cameraLookUp", "renderOption", "maxAngularSize", "color", "opacity", "scaleExponent", "scaleFactor", "up", "right", "fadeInValue", "hasSpriteTexture", @@ -64,12 +66,13 @@ namespace { "nanColor", "useNanColor", "hideOutsideRange", "enableMaxSizeControl", "aboveRangeColor", "useAboveRangeColor", "belowRangeColor", "useBelowRangeColor", "hasDvarScaling", "dvarScaleFactor", "enableOutline", "outlineColor", - "outlineWeight", "aspectRatioScale" + "outlineWeight", "aspectRatioScale", "useOrientationData" }; enum RenderOption { ViewDirection = 0, - PositionNormal + PositionNormal, + FixedRotation }; constexpr openspace::properties::Property::PropertyInfo TextureEnabledInfo = { @@ -146,15 +149,7 @@ namespace { "The labels for the points. If no label file is provided, the labels will be " "created to match the points in the data file. For a CSV file, you should then " "specify which column is the 'Name' column in the data mapping. For SPECK files " - "the labels are created from the comment at the end of each line" - }; - - constexpr openspace::properties::Property::PropertyInfo RenderOptionInfo = { - "RenderOption", - "Render Option", - "Option wether the point billboards should face the camera or not. Used for " - "non-linear display environments such as fisheye.", - openspace::properties::Property::Visibility::AdvancedUser + "the labels are created from the comment at the end of each line." }; constexpr openspace::properties::Property::PropertyInfo FadeInDistancesInfo = { @@ -199,6 +194,29 @@ namespace { openspace::properties::Property::Visibility::AdvancedUser }; + constexpr openspace::properties::Property::PropertyInfo UseOrientationDataInfo = { + "UseOrientationData", + "Use Orientation Data", + "Include the orietation data in the dataset when rendering the points, if there " + "is any. To see the rotation, you also need to set the \"Orientation Render " + "Option\" to \"Fixed Rotation\".", + openspace::properties::Property::Visibility::AdvancedUser + }; + + constexpr openspace::properties::Property::PropertyInfo OrientationRenderOptionInfo = { + "OrientationRenderOption", + "Orientation Render Option", + "Controls how the planes for the points will be oriented. \"Camera View " + "Direction\" rotates the points so that the plane is orthogonal to the viewing " + "direction of the camera (useful for planar displays), and \"Camera Position " + "Normal\" rotates the points towards the position of the camera (useful for " + "spherical displays, like dome theaters). In both these cases the points will " + "be billboarded towards the camera. In contrast, \"Fixed Rotation\" does not " + "rotate the points at all based on the camera and should be used when the " + "dataset contains orientation information for the points.", + openspace::properties::Property::Visibility::AdvancedUser + }; + constexpr openspace::properties::Property::PropertyInfo NumShownDataPointsInfo = { "NumberOfDataPoints", "Number of Shown Data Points", @@ -207,6 +225,13 @@ namespace { openspace::properties::Property::Visibility::User }; + constexpr openspace::properties::Property::PropertyInfo HasOrientationDataInfo = { + "HasOrientationData", + "Has Orientation Data", + "Set to true if orientation data was read from the dataset", + openspace::properties::Property::Visibility::AdvancedUser + }; + constexpr openspace::properties::Property::PropertyInfo ScaleExponentInfo = { "ScaleExponent", "Scale Exponent", @@ -215,7 +240,8 @@ namespace { "value should be. If not included, it is computed based on the maximum " "positional component of the data points. This is useful for showing the " "dataset at all, but you will likely want to change it to something that looks " - "good.", + "good. Note that a scale exponent of 0 leads to the points having a diameter of " + "1 meter, i.e. no exponential scaling.", openspace::properties::Property::Visibility::User }; @@ -298,7 +324,7 @@ namespace { // fading, sprite texture, color mapping and whether the colors of overlapping points // should be blended additively or not. // - // The point size depends on a few different things: + // The points are rendered as planes whose size depends on a few different things: // // - At the core, scaling is done based on an exponential value, the 'ScaleExponent'. // A relatively small change to this value will lead to a large change in size. @@ -312,6 +338,9 @@ namespace { // - There is also an option to limit the size of the points based on a given max // size value. // + // - And an option to scale the points based on a data value (see 'SizeMapping' in + // 'SizeSettings') + // // - To easily change the visual size of the points, the multiplicative 'ScaleFactor' // may be used. A value of 2 makes the points twice as large, visually, compared // to 1. @@ -364,10 +393,14 @@ namespace { enum class [[codegen::map(RenderOption)]] RenderOption { ViewDirection [[codegen::key("Camera View Direction")]], - PositionNormal [[codegen::key("Camera Position Normal")]] + PositionNormal [[codegen::key("Camera Position Normal")]], + FixedRotation [[codegen::key("Fixed Rotation")]] }; - // [[codegen::verbatim(RenderOptionInfo.description)]] - std::optional renderOption; + // [[codegen::verbatim(OrientationRenderOptionInfo.description)]] + std::optional orientationRenderOption; + + // [[codegen::verbatim(UseOrientationDataInfo.description)]] + std::optional useOrientationData; // [[codegen::verbatim(UseAdditiveBlendingInfo.description)]] std::optional useAdditiveBlending; @@ -465,7 +498,7 @@ documentation::Documentation RenderablePointCloud::Documentation() { RenderablePointCloud::SizeSettings::SizeSettings(const ghoul::Dictionary& dictionary) : properties::PropertyOwner({ "Sizing", "Sizing", ""}) , scaleExponent(ScaleExponentInfo, 1.f, 0.f, 25.f) - , scaleFactor(ScaleFactorInfo, 1.f, 0.f, 50.f) + , scaleFactor(ScaleFactorInfo, 1.f, 0.f, 100.f) , useMaxSizeControl(UseMaxSizeControlInfo, false) , maxAngularSize(MaxSizeInfo, 1.f, 0.f, 45.f) { @@ -586,8 +619,13 @@ RenderablePointCloud::RenderablePointCloud(const ghoul::Dictionary& dictionary) , _fading(dictionary) , _useAdditiveBlending(UseAdditiveBlendingInfo, true) , _drawElements(DrawElementsInfo, true) - , _renderOption(RenderOptionInfo, properties::OptionProperty::DisplayType::Dropdown) + , _useRotation(UseOrientationDataInfo, false) + , _renderOption( + OrientationRenderOptionInfo, + properties::OptionProperty::DisplayType::Dropdown + ) , _nDataPoints(NumShownDataPointsInfo, 0) + , _hasOrientationData(HasOrientationDataInfo, false) { ZoneScoped; @@ -609,15 +647,20 @@ RenderablePointCloud::RenderablePointCloud(const ghoul::Dictionary& dictionary) _renderOption.addOption(RenderOption::ViewDirection, "Camera View Direction"); _renderOption.addOption(RenderOption::PositionNormal, "Camera Position Normal"); + _renderOption.addOption(RenderOption::FixedRotation, "Fixed Rotation"); - if (p.renderOption.has_value()) { - _renderOption = codegen::map(*p.renderOption); + if (p.orientationRenderOption.has_value()) { + _renderOption = codegen::map(*p.orientationRenderOption); } else { _renderOption = RenderOption::ViewDirection; } addProperty(_renderOption); + _useRotation = p.useOrientationData.value_or(_useRotation); + _useRotation.onChange([this]() { _dataIsDirty = true; }); + addProperty(_useRotation); + _useAdditiveBlending = p.useAdditiveBlending.value_or(_useAdditiveBlending); addProperty(_useAdditiveBlending); @@ -669,10 +712,11 @@ RenderablePointCloud::RenderablePointCloud(const ghoul::Dictionary& dictionary) _transformationMatrix = p.transformationMatrix.value_or(_transformationMatrix); - if (p.sizeSettings.has_value() && p.sizeSettings->sizeMapping.has_value()) { + if (_sizeSettings.sizeMapping != nullptr) { _sizeSettings.sizeMapping->parameterOption.onChange( [this]() { _dataIsDirty = true; } ); + _sizeSettings.sizeMapping->isRadius.onChange([this]() { _dataIsDirty = true; }); _hasDatavarSize = true; } @@ -728,6 +772,9 @@ RenderablePointCloud::RenderablePointCloud(const ghoul::Dictionary& dictionary) _nDataPoints.setReadOnly(true); addProperty(_nDataPoints); + + _hasOrientationData.setReadOnly(true); + addProperty(_hasOrientationData); } bool RenderablePointCloud::isReady() const { @@ -768,6 +815,7 @@ void RenderablePointCloud::initialize() { } _nDataPoints = static_cast(_dataset.entries.size()); + _hasOrientationData = _dataset.orientationDataIndex >= 0; // If no scale exponent was specified, compute one that will at least show the // points based on the scale of the positions in the dataset @@ -835,9 +883,9 @@ void RenderablePointCloud::initializeShadersAndGlExtras() { []() { return global::renderEngine->buildRenderProgram( "RenderablePointCloud", - absPath("${MODULE_BASE}/shaders/pointcloud/billboardpoint_vs.glsl"), - absPath("${MODULE_BASE}/shaders/pointcloud/billboardpoint_fs.glsl"), - absPath("${MODULE_BASE}/shaders/pointcloud/billboardpoint_gs.glsl") + absPath("${MODULE_BASE}/shaders/pointcloud/pointcloud_vs.glsl"), + absPath("${MODULE_BASE}/shaders/pointcloud/pointcloud_fs.glsl"), + absPath("${MODULE_BASE}/shaders/pointcloud/pointcloud_gs.glsl") ); } ); @@ -1090,11 +1138,11 @@ float RenderablePointCloud::computeDistanceFadeValue(const RenderData& data) con void RenderablePointCloud::setExtraUniforms() {} -void RenderablePointCloud::renderBillboards(const RenderData& data, - const glm::dmat4& modelMatrix, - const glm::dvec3& orthoRight, - const glm::dvec3& orthoUp, - float fadeInVariable) +void RenderablePointCloud::renderPoints(const RenderData& data, + const glm::dmat4& modelMatrix, + const glm::dvec3& orthoRight, + const glm::dvec3& orthoUp, + float fadeInVariable) { if (!_hasDataFile || _dataset.entries.empty()) { return; @@ -1164,7 +1212,7 @@ void RenderablePointCloud::renderBillboards(const RenderData& data, _program->setUniform(_uniformCache.outlineColor, _colorSettings.outlineColor); _program->setUniform(_uniformCache.outlineWeight, _colorSettings.outlineWeight); - bool useColorMap = _hasColorMapFile && _colorSettings.colorMapping->enabled && + bool useColorMap = hasColorData() && _colorSettings.colorMapping->enabled && _colorSettings.colorMapping->texture(); _program->setUniform(_uniformCache.useColormap, useColorMap); @@ -1212,6 +1260,8 @@ void RenderablePointCloud::renderBillboards(const RenderData& data, ); } + _program->setUniform(_uniformCache.useOrientationData, useOrientationData()); + bool useTexture = _hasSpriteTexture && _texture.enabled; _program->setUniform(_uniformCache.hasSpriteTexture, useTexture); @@ -1275,7 +1325,7 @@ void RenderablePointCloud::render(const RenderData& data, RendererTasks&) { glm::dvec3 orthoUp = glm::normalize(glm::cross(cameraViewDirectionWorld, orthoRight)); if (_hasDataFile && _drawElements) { - renderBillboards(data, modelMatrix, orthoRight, orthoUp, fadeInVar); + renderPoints(data, modelMatrix, orthoRight, orthoUp, fadeInVar); } if (_hasLabels) { @@ -1314,10 +1364,52 @@ glm::dvec3 RenderablePointCloud::transformedPosition( return glm::dvec3(_transformationMatrix * position); } +glm::quat RenderablePointCloud::orientationQuaternion( + const dataloader::Dataset::Entry& e) const +{ + const int orientationDataIndex = _dataset.orientationDataIndex; + + const glm::vec3 u = glm::normalize(glm::vec3( + _transformationMatrix * + glm::dvec4( + e.data[orientationDataIndex + 0], + e.data[orientationDataIndex + 1], + e.data[orientationDataIndex + 2], + 1.f + ) + )); + + const glm::vec3 v = glm::normalize(glm::vec3( + _transformationMatrix * + glm::dvec4( + e.data[orientationDataIndex + 3], + e.data[orientationDataIndex + 4], + e.data[orientationDataIndex + 5], + 1.f + ) + )); + + // Get the quaternion that represents the rotation from XY plane to the plane that is + // spanned by the UV vectors. + + // First rotate to align the z-axis with plane normal + const glm::vec3 planeNormal = glm::normalize(glm::cross(u, v)); + glm::quat q = glm::normalize(glm::rotation(glm::vec3(0.f, 0.f, 1.f), planeNormal)); + + // Add rotation around plane normal (rotate new x-axis to u) + const glm::vec3 rotatedRight = glm::normalize( + glm::vec3(glm::mat4_cast(q) * glm::vec4(1.f, 0.f, 0.f, 1.f)) + ); + q = glm::normalize(glm::rotation(rotatedRight, u)) * q; + + return q; +} + int RenderablePointCloud::nAttributesPerPoint() const { int n = 3; // position - n += _hasColorMapFile ? 1 : 0; - n += _hasDatavarSize ? 1 : 0; + n += hasColorData() ? 1 : 0; + n += hasSizeData() ? 1 : 0; + n += useOrientationData() ? 4 : 0; n += _hasSpriteTexture ? 1 : 0; // texture id return n; } @@ -1370,14 +1462,18 @@ void RenderablePointCloud::updateBufferData() { offset = bufferVertexAttribute("in_position", 3, attibutesPerPoint, offset); - if (_hasColorMapFile) { + if (hasColorData()) { offset = bufferVertexAttribute("in_colorParameter", 1, attibutesPerPoint, offset); } - if (_hasDatavarSize) { + if (hasSizeData()) { offset = bufferVertexAttribute("in_scalingParameter", 1, attibutesPerPoint, offset); } + if (useOrientationData()) { + offset = bufferVertexAttribute("in_orientation", 4, attibutesPerPoint, offset); + } + if (_hasSpriteTexture) { offset = bufferVertexAttribute("in_textureLayer", 1, attibutesPerPoint, offset); } @@ -1432,7 +1528,7 @@ int RenderablePointCloud::currentColorParameterIndex() const { _colorSettings.colorMapping->dataColumn; if (!_hasColorMapFile || property.options().empty()) { - return 0; + return -1; } return _dataset.index(property.option().description); @@ -1443,12 +1539,32 @@ int RenderablePointCloud::currentSizeParameterIndex() const { _sizeSettings.sizeMapping->parameterOption; if (!_hasDatavarSize || property.options().empty()) { - return 0; + return -1; } return _dataset.index(property.option().description); } +bool RenderablePointCloud::hasColorData() const { + const int colorParamIndex = currentColorParameterIndex(); + return _hasColorMapFile && colorParamIndex >= 0; +} + +bool RenderablePointCloud::hasSizeData() const { + const int sizeParamIndex = currentSizeParameterIndex(); + return _hasDatavarSize && sizeParamIndex >= 0; +} + +bool RenderablePointCloud::hasMultiTextureData() const { + // What datavar is the texture, if any + const int textureIdIndex = _dataset.textureDataIndex; + return _hasSpriteTexture && textureIdIndex >= 0; +} + +bool RenderablePointCloud::useOrientationData() const { + return _hasOrientationData && _useRotation; +} + void RenderablePointCloud::addPositionDataForPoint(unsigned int index, std::vector& result, double& maxRadius) const @@ -1470,20 +1586,35 @@ void RenderablePointCloud::addColorAndSizeDataForPoint(unsigned int index, { const dataloader::Dataset::Entry& e = _dataset.entries[index]; - int colorParamIndex = currentColorParameterIndex(); - if (_hasColorMapFile && colorParamIndex >= 0) { + if (hasColorData()) { + const int colorParamIndex = currentColorParameterIndex(); result.push_back(e.data[colorParamIndex]); } - int sizeParamIndex = currentSizeParameterIndex(); - if (_hasDatavarSize && sizeParamIndex >= 0) { + if (hasSizeData()) { + const int sizeParamIndex = currentSizeParameterIndex(); // @TODO: Consider more detailed control over the scaling. Currently the value // is multiplied with the value as is. Should have similar mapping properties // as the color mapping - result.push_back(e.data[sizeParamIndex]); + + // Convert to diameter if data is given as radius + float multiplier = _sizeSettings.sizeMapping->isRadius ? 2.f : 1.f; + result.push_back(multiplier * e.data[sizeParamIndex]); } } +void RenderablePointCloud::addOrientationDataForPoint(unsigned int index, + std::vector& result) const +{ + const dataloader::Dataset::Entry& e = _dataset.entries[index]; + glm::quat q = orientationQuaternion(e); + + result.push_back(q.x); + result.push_back(q.y); + result.push_back(q.z); + result.push_back(q.w); +} + std::vector RenderablePointCloud::createDataSlice() { ZoneScoped; @@ -1491,9 +1622,6 @@ std::vector RenderablePointCloud::createDataSlice() { return std::vector(); } - // What datavar is the texture, if any - int textureIdIndex = _dataset.textureDataIndex; - double maxRadius = 0.0; // One sub-array per texture array, since each of these will correspond to a separate @@ -1511,13 +1639,14 @@ std::vector RenderablePointCloud::createDataSlice() { const dataloader::Dataset::Entry& e = _dataset.entries[i]; unsigned int subresultIndex = 0; + // Default texture layer for single texture is zero float textureLayer = 0.f; bool useMultiTexture = (_textureMode == TextureInputMode::Multi) && - (textureIdIndex >= 0); + hasMultiTextureData(); - if (_hasSpriteTexture && useMultiTexture) { - int texId = static_cast(e.data[textureIdIndex]); + if (useMultiTexture) { + int texId = static_cast(e.data[_dataset.textureDataIndex]); size_t texIndex = _indexInDataToTextureIndex[texId]; textureLayer = static_cast( _textureIndexToArrayMap[texIndex].layer @@ -1531,6 +1660,10 @@ std::vector RenderablePointCloud::createDataSlice() { addPositionDataForPoint(i, subArrayToUse, maxRadius); addColorAndSizeDataForPoint(i, subArrayToUse); + if (useOrientationData()) { + addOrientationDataForPoint(i, subArrayToUse); + } + // Texture layer if (_hasSpriteTexture) { subArrayToUse.push_back(static_cast(textureLayer)); diff --git a/modules/base/rendering/pointcloud/renderablepointcloud.h b/modules/base/rendering/pointcloud/renderablepointcloud.h index 28f5d28e62..6051d66e67 100644 --- a/modules/base/rendering/pointcloud/renderablepointcloud.h +++ b/modules/base/rendering/pointcloud/renderablepointcloud.h @@ -98,6 +98,7 @@ protected: virtual void preUpdate(); glm::dvec3 transformedPosition(const dataloader::Dataset::Entry& e) const; + glm::quat orientationQuaternion(const dataloader::Dataset::Entry& e) const; virtual int nAttributesPerPoint() const; @@ -118,10 +119,17 @@ protected: /// Find the index of the currently chosen size parameter in the dataset int currentSizeParameterIndex() const; + bool hasColorData() const; + bool hasSizeData() const; + bool hasMultiTextureData() const; + bool useOrientationData() const; + virtual void addPositionDataForPoint(unsigned int index, std::vector& result, double& maxRadius) const; virtual void addColorAndSizeDataForPoint(unsigned int index, std::vector& result) const; + virtual void addOrientationDataForPoint(unsigned int index, + std::vector& result) const; std::vector createDataSlice(); @@ -146,7 +154,7 @@ protected: float computeDistanceFadeValue(const RenderData& data) const; - void renderBillboards(const RenderData& data, const glm::dmat4& modelMatrix, + void renderPoints(const RenderData& data, const glm::dmat4& modelMatrix, const glm::dvec3& orthoRight, const glm::dvec3& orthoUp, float fadeInVariable); gl::GLenum internalGlFormat(bool useAlpha) const; @@ -194,11 +202,13 @@ protected: Fading _fading; properties::BoolProperty _useAdditiveBlending; + properties::BoolProperty _useRotation; properties::BoolProperty _drawElements; properties::OptionProperty _renderOption; properties::UIntProperty _nDataPoints; + properties::BoolProperty _hasOrientationData; struct Texture : properties::PropertyOwner { Texture(); @@ -221,7 +231,7 @@ protected: cmapRangeMin, cmapRangeMax, nanColor, useNanColor, hideOutsideRange, enableMaxSizeControl, aboveRangeColor, useAboveRangeColor, belowRangeColor, useBelowRangeColor, hasDvarScaling, dvarScaleFactor, enableOutline, outlineColor, - outlineWeight, aspectRatioScale + outlineWeight, aspectRatioScale, useOrientationData ) _uniformCache; std::filesystem::path _dataFile; diff --git a/modules/base/rendering/pointcloud/sizemappingcomponent.cpp b/modules/base/rendering/pointcloud/sizemappingcomponent.cpp index c9e3bdad8d..ae01cefdc9 100644 --- a/modules/base/rendering/pointcloud/sizemappingcomponent.cpp +++ b/modules/base/rendering/pointcloud/sizemappingcomponent.cpp @@ -25,6 +25,7 @@ #include #include +#include #include namespace { @@ -57,6 +58,14 @@ namespace { openspace::properties::Property::Visibility::AdvancedUser }; + constexpr openspace::properties::Property::PropertyInfo IsRadiusInfo = { + "IsRadius", + "Size is Radius", + "If true, the size value in the data is interpreted as the radius of the points. " + "Otherwise, it is interpreted as the diameter.", + openspace::properties::Property::Visibility::AdvancedUser + }; + struct [[codegen::Dictionary(SizeMappingComponent)]] Parameters { // [[codegen::verbatim(EnabledInfo.description)]] std::optional enabled; @@ -69,7 +78,36 @@ namespace { std::optional parameter; // [[codegen::verbatim(ScaleFactorInfo.description)]] - std::optional scaleFactor; + enum class [[codegen::map(openspace::DistanceUnit)]] ScaleUnit { + Nanometer, + Micrometer, + Millimeter, + Centimeter, + Decimeter, + Meter, + Kilometer, + AU, + Lighthour, + Lightday, + Lightmonth, + Lightyear, + Parsec, + Kiloparsec, + Megaparsec, + Gigaparsec, + Gigalightyear + }; + + // The scale to use for the size values in the dataset, given as either a string + // representing a specific unit or a value to multiply all the datapoints with + // to convert the value to meter. The resulting value will be applied as a + // multiplicative factor. For example, if the size data is given in is in + // kilometers then specify either ScaleFactor = 'Kilometer' or + // ScaleFactor = 1000.0. + std::optional> scaleFactor; + + // [[codegen::verbatim(IsRadiusInfo.description)]] + std::optional isRadius; }; #include "sizemappingcomponent_codegen.cpp" } // namespace @@ -88,10 +126,12 @@ SizeMappingComponent::SizeMappingComponent() properties::OptionProperty::DisplayType::Dropdown ) , scaleFactor(ScaleFactorInfo, 1.f, 0.f, 1000.f) + , isRadius(IsRadiusInfo, false) { addProperty(enabled); addProperty(parameterOption); addProperty(scaleFactor); + addProperty(isRadius); } SizeMappingComponent::SizeMappingComponent(const ghoul::Dictionary& dictionary) @@ -125,7 +165,19 @@ SizeMappingComponent::SizeMappingComponent(const ghoul::Dictionary& dictionary) )); } - scaleFactor = p.scaleFactor.value_or(scaleFactor); + if (p.scaleFactor.has_value()) { + if (std::holds_alternative(*p.scaleFactor)) { + const Parameters::ScaleUnit scaleUnit = + std::get(*p.scaleFactor); + const DistanceUnit distanceUnit = codegen::map(scaleUnit); + scaleFactor = toMeter(distanceUnit); + } + else if (std::holds_alternative(*p.scaleFactor)) { + scaleFactor = std::get(*p.scaleFactor); + } + } + + isRadius = p.isRadius.value_or(isRadius); } } // namespace openspace diff --git a/modules/base/rendering/pointcloud/sizemappingcomponent.h b/modules/base/rendering/pointcloud/sizemappingcomponent.h index 800436a09f..3155309e1a 100644 --- a/modules/base/rendering/pointcloud/sizemappingcomponent.h +++ b/modules/base/rendering/pointcloud/sizemappingcomponent.h @@ -49,6 +49,7 @@ struct SizeMappingComponent : public properties::PropertyOwner { properties::BoolProperty enabled; properties::OptionProperty parameterOption; properties::FloatProperty scaleFactor; + properties::BoolProperty isRadius; }; } // namespace openspace diff --git a/modules/base/shaders/pointcloud/billboardpoint_fs.glsl b/modules/base/shaders/pointcloud/pointcloud_fs.glsl similarity index 98% rename from modules/base/shaders/pointcloud/billboardpoint_fs.glsl rename to modules/base/shaders/pointcloud/pointcloud_fs.glsl index 9c0b0ef7b7..ee160705f1 100644 --- a/modules/base/shaders/pointcloud/billboardpoint_fs.glsl +++ b/modules/base/shaders/pointcloud/pointcloud_fs.glsl @@ -103,9 +103,6 @@ Fragment getFragment() { } fullColor.a *= opacity * fadeInValue; - if (fullColor.a < 0.01) { - discard; - } Fragment frag; frag.color = fullColor; diff --git a/modules/base/shaders/pointcloud/billboardpoint_gs.glsl b/modules/base/shaders/pointcloud/pointcloud_gs.glsl similarity index 83% rename from modules/base/shaders/pointcloud/billboardpoint_gs.glsl rename to modules/base/shaders/pointcloud/pointcloud_gs.glsl index a3114b8dd9..7e8af327c4 100644 --- a/modules/base/shaders/pointcloud/billboardpoint_gs.glsl +++ b/modules/base/shaders/pointcloud/pointcloud_gs.glsl @@ -30,6 +30,7 @@ layout(points) in; flat in float textureLayer[]; flat in float colorParameter[]; flat in float scalingParameter[]; +flat in vec4 orientation[]; // quaternion layout(triangle_strip, max_vertices = 4) out; flat out float gs_colorParameter; @@ -48,6 +49,7 @@ uniform dmat4 modelMatrix; uniform bool enableMaxSizeControl; uniform bool hasDvarScaling; uniform float dvarScaleFactor; +uniform bool useOrientationData; // RenderOption: CameraViewDirection uniform vec3 up; @@ -72,6 +74,24 @@ const vec2 corners[4] = vec2[4]( const int RenderOptionCameraViewDirection = 0; const int RenderOptionCameraPositionNormal = 1; +const int RenderOptionFixedRotation = 2; + +// Quaternion math code from: +// https://gist.github.com/mattatz/40a91588d5fb38240403f198a938a593 + +vec4 quatMult(vec4 q1, vec4 q2) { + return vec4( + q2.xyz * q1.w + q1.xyz * q2.w + cross(q1.xyz, q2.xyz), + q1.w * q2.w - dot(q1.xyz, q2.xyz) + ); +} + +// Vector rotation with a quaternion +// http://mathworld.wolfram.com/Quaternion.html +vec3 rotate_vector(vec3 v, vec4 q) { + vec4 q_conjugate = q * vec4(-1.0, -1.0, -1.0, 1.0); + return quatMult(q, quatMult(vec4(v, 0.0), q_conjugate)).xyz; +} void main() { vec4 pos = gl_in[0].gl_Position; @@ -85,21 +105,32 @@ void main() { scaleMultiply *= scalingParameter[0] * dvarScaleFactor; } - vec3 scaledRight = vec3(0.0); - vec3 scaledUp = vec3(0.0); + vec3 scaledRight = vec3(1.0, 0.0, 0.0); + vec3 scaledUp = vec3(0.0, 1.0, 0.0); if (renderOption == RenderOptionCameraViewDirection) { - scaledRight = scaleMultiply * right * 0.5; - scaledUp = scaleMultiply * up * 0.5; + scaledRight = right; + scaledUp = up; } else if (renderOption == RenderOptionCameraPositionNormal) { vec3 normal = vec3(normalize(cameraPosition - dpos.xyz)); vec3 newRight = normalize(cross(cameraLookUp, normal)); vec3 newUp = cross(normal, newRight); - scaledRight = scaleMultiply * newRight * 0.5; - scaledUp = scaleMultiply * newUp * 0.5; + scaledRight = newRight; + scaledUp = newUp; } + else if (renderOption == RenderOptionFixedRotation) { + if (useOrientationData) { + vec4 quat = orientation[0]; + scaledRight = normalize(rotate_vector(scaledRight, quat)); + scaledUp = normalize(rotate_vector(scaledUp, quat)); + } + // Else use default + } + + scaledRight *= scaleMultiply * 0.5; + scaledUp *= scaleMultiply * 0.5; if (enableMaxSizeControl) { // Limit the max size of the points, as the angle in "FOV" that the point is allowed diff --git a/modules/base/shaders/pointcloud/billboardpoint_interpolated_vs.glsl b/modules/base/shaders/pointcloud/pointcloud_interpolated_vs.glsl similarity index 71% rename from modules/base/shaders/pointcloud/billboardpoint_interpolated_vs.glsl rename to modules/base/shaders/pointcloud/pointcloud_interpolated_vs.glsl index a6a3407f2e..5b28d95e7b 100644 --- a/modules/base/shaders/pointcloud/billboardpoint_interpolated_vs.glsl +++ b/modules/base/shaders/pointcloud/pointcloud_interpolated_vs.glsl @@ -40,6 +40,9 @@ in float in_scalingParameter1; in float in_textureLayer; +in vec4 in_orientation0; // quaternion +in vec4 in_orientation1; // quaternion + uniform bool useSpline; uniform float interpolationValue; @@ -47,6 +50,8 @@ flat out float textureLayer; flat out float colorParameter; flat out float scalingParameter; +flat out vec4 orientation; // quaternion + float interpolateDataValue(float v0, float v1, float t) { const float Epsilon = 1E-7; const float NaN = log(-1.0); // undefined @@ -73,6 +78,53 @@ vec3 interpolateCatmullRom(float t, vec3 p0, vec3 p1, vec3 p2, vec3 p3) { ); } +// Quaternion math from: https://gist.github.com/mattatz/40a91588d5fb38240403f198a938a593 +vec4 quaternionSlerp(vec4 a, vec4 b, float t) { + // if either input is zero, return the other. + if (length(a) == 0.0) { + if (length(b) == 0.0) { + return vec4(0.0, 0.0, 0.0, 1.0); + } + return b; + } + else if (length(b) == 0.0) { + return a; + } + + float cosHalfAngle = a.w * b.w + dot(a.xyz, b.xyz); + + if (cosHalfAngle >= 1.0 || cosHalfAngle <= -1.0) { + return a; + } + else if (cosHalfAngle < 0.0) { + b.xyz = -b.xyz; + b.w = -b.w; + cosHalfAngle = -cosHalfAngle; + } + + float blendA; + float blendB; + if (cosHalfAngle < 0.99) { + // Do proper slerp for big angles + float halfAngle = acos(cosHalfAngle); + float sinHalfAngle = sin(halfAngle); + float oneOverSinHalfAngle = 1.0 / sinHalfAngle; + blendA = sin(halfAngle * (1.0 - t)) * oneOverSinHalfAngle; + blendB = sin(halfAngle * t) * oneOverSinHalfAngle; + } + else { + // Do lerp if angle is really small + blendA = 1.0 - t; + blendB = t; + } + + vec4 result = vec4(blendA * a.xyz + blendB * b.xyz, blendA * a.w + blendB * b.w); + if (length(result) > 0.0) { + return normalize(result); + } + return vec4(0.0, 0.0, 0.0, 1.0); +} + void main() { float t = interpolationValue; @@ -90,6 +142,8 @@ void main() { ); } + orientation = quaternionSlerp(in_orientation0, in_orientation1, t); + textureLayer = in_textureLayer; gl_Position = vec4(position, 1.0); diff --git a/modules/base/shaders/pointcloud/billboardpoint_vs.glsl b/modules/base/shaders/pointcloud/pointcloud_vs.glsl similarity index 95% rename from modules/base/shaders/pointcloud/billboardpoint_vs.glsl rename to modules/base/shaders/pointcloud/pointcloud_vs.glsl index 2a1e661ba5..aabc12c904 100644 --- a/modules/base/shaders/pointcloud/billboardpoint_vs.glsl +++ b/modules/base/shaders/pointcloud/pointcloud_vs.glsl @@ -30,14 +30,17 @@ in vec3 in_position; in float in_textureLayer; in float in_colorParameter; in float in_scalingParameter; +in vec4 in_orientation; // quaternion flat out float textureLayer; flat out float colorParameter; flat out float scalingParameter; +flat out vec4 orientation; // quaternion void main() { textureLayer = in_textureLayer; colorParameter = in_colorParameter; scalingParameter = in_scalingParameter; + orientation = in_orientation; gl_Position = vec4(in_position, 1.0); } diff --git a/modules/digitaluniverse/CMakeLists.txt b/modules/digitaluniverse/CMakeLists.txt index df3b0b2eeb..0815b5df8e 100644 --- a/modules/digitaluniverse/CMakeLists.txt +++ b/modules/digitaluniverse/CMakeLists.txt @@ -26,13 +26,11 @@ include(${PROJECT_SOURCE_DIR}/support/cmake/module_definition.cmake) set(HEADER_FILES rendering/renderabledumeshes.h - rendering/renderableplanescloud.h ) source_group("Header Files" FILES ${HEADER_FILES}) set(SOURCE_FILES rendering/renderabledumeshes.cpp - rendering/renderableplanescloud.cpp ) source_group("Source Files" FILES ${SOURCE_FILES}) diff --git a/modules/digitaluniverse/digitaluniversemodule.cpp b/modules/digitaluniverse/digitaluniversemodule.cpp index 38f510df56..03d69f996e 100644 --- a/modules/digitaluniverse/digitaluniversemodule.cpp +++ b/modules/digitaluniverse/digitaluniversemodule.cpp @@ -25,7 +25,6 @@ #include #include -#include #include #include #include @@ -46,7 +45,6 @@ void DigitalUniverseModule::internalInitialize(const ghoul::Dictionary&) { FactoryManager::ref().factory(); ghoul_assert(fRenderable, "Renderable factory was not created"); - fRenderable->registerClass("RenderablePlanesCloud"); fRenderable->registerClass("RenderableDUMeshes"); } @@ -57,7 +55,6 @@ void DigitalUniverseModule::internalDeinitializeGL() { std::vector DigitalUniverseModule::documentations() const { return { - RenderablePlanesCloud::Documentation(), RenderableDUMeshes::Documentation() }; } diff --git a/modules/digitaluniverse/rendering/renderableplanescloud.cpp b/modules/digitaluniverse/rendering/renderableplanescloud.cpp deleted file mode 100644 index b26d2029a8..0000000000 --- a/modules/digitaluniverse/rendering/renderableplanescloud.cpp +++ /dev/null @@ -1,669 +0,0 @@ -/***************************************************************************************** - * * - * OpenSpace * - * * - * Copyright (c) 2014-2024 * - * * - * 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 - -namespace { - constexpr std::string_view _loggerCat = "RenderablePlanesCloud"; - - constexpr int PlanesVertexDataSize = 36; - - constexpr std::array UniformNames = { - "modelViewProjectionTransform", "alphaValue", "fadeInValue", "galaxyTexture" - }; - - enum BlendMode { - BlendModeNormal = 0, - BlendModeAdditive - }; - - constexpr openspace::properties::Property::PropertyInfo ScaleFactorInfo = { - "ScaleFactor", - "Scale Factor", - "This value is used as a multiplicative factor that is applied to the apparent " - "size of each point", - // @VISIBILITY(2.5) - openspace::properties::Property::Visibility::User - }; - - static const openspace::properties::PropertyOwner::PropertyOwnerInfo LabelsInfo = { - "Labels", - "Labels", - "The labels for the astronomical objects" - }; - - constexpr openspace::properties::Property::PropertyInfo DrawElementsInfo = { - "DrawElements", - "Draw Elements", - "Enables/Disables the drawing of the astronomical objects", - openspace::properties::Property::Visibility::NoviceUser - }; - - constexpr openspace::properties::Property::PropertyInfo TransformationMatrixInfo = { - "TransformationMatrix", - "Transformation Matrix", - "Transformation matrix to be applied to each astronomical object", - openspace::properties::Property::Visibility::Developer - }; - - constexpr openspace::properties::Property::PropertyInfo BlendModeInfo = { - "BlendMode", - "Blending Mode", - "This determines the blending mode that is applied to this plane", - openspace::properties::Property::Visibility::AdvancedUser - }; - - constexpr openspace::properties::Property::PropertyInfo TexturePathInfo = { - "TexturePath", - "Texture Path", - "This value specifies the path for the textures in disk", - openspace::properties::Property::Visibility::AdvancedUser - }; - - constexpr openspace::properties::Property::PropertyInfo LuminosityInfo = { - "Luminosity", - "Luminosity variable", - "Datavar variable to control the luminosity/size of the astronomical objects", - // @VISIBILITY(2.67) - openspace::properties::Property::Visibility::User - }; - - constexpr openspace::properties::Property::PropertyInfo ScaleLuminosityInfo = { - "ScaleLuminosity", - "ScaleLuminosity variable", - "Scaling control for the luminosity/size of the astronomical objects", - // @VISIBILITY(2.67) - openspace::properties::Property::Visibility::User - }; - - constexpr openspace::properties::Property::PropertyInfo RenderOptionInfo = { - "RenderOption", - "Render Option", - "Debug option for rendering of billboards and texts", - openspace::properties::Property::Visibility::AdvancedUser - }; - - constexpr openspace::properties::Property::PropertyInfo FadeInDistancesInfo = { - "FadeInDistances", - "Fade-In Start and End Distances", - "These values determine the initial and final distances from the center of " - "our galaxy from which the astronomical object will start and end fading-in", - openspace::properties::Property::Visibility::AdvancedUser - }; - - constexpr openspace::properties::Property::PropertyInfo DisableFadeInInfo = { - "DisableFadeIn", - "Disable Fade-in effect", - "Enables/Disables the Fade-in effect", - openspace::properties::Property::Visibility::User - }; - - constexpr openspace::properties::Property::PropertyInfo PlaneMinSizeInfo = { - "PlaneMinSize", - "Plane Min Size in Pixels", - "The min size (in pixels) for the plane representing the astronomical object", - openspace::properties::Property::Visibility::AdvancedUser - }; - - struct [[codegen::Dictionary(RenderablePlanesCloud)]] Parameters { - // The path to the SPECK file that contains information about the astronomical - // object being rendered - std::optional file; - - // [[codegen::verbatim(ScaleFactorInfo.description)]] - std::optional scaleFactor; - - // [[codegen::verbatim(LabelsInfo.description)]] - std::optional labels - [[codegen::reference("labelscomponent")]]; - - // [[codegen::verbatim(TransformationMatrixInfo.description)]] - std::optional transformationMatrix; - - enum class BlendMode { - Normal, - Additive - }; - - // [[codegen::verbatim(BlendModeInfo.description)]] - std::optional blendMode; - - 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(TexturePathInfo.description)]] - std::string texturePath; - - // [[codegen::verbatim(LuminosityInfo.description)]] - std::optional luminosity; - - // [[codegen::verbatim(ScaleLuminosityInfo.description)]] - std::optional scaleLuminosity; - - // [[codegen::verbatim(FadeInDistancesInfo.description)]] - std::optional fadeInDistances; - - // [[codegen::verbatim(DisableFadeInInfo.description)]] - std::optional disableFadeIn; - - // [[codegen::verbatim(PlaneMinSizeInfo.description)]] - std::optional planeMinSize; - }; -#include "renderableplanescloud_codegen.cpp" -} // namespace - -namespace openspace { - -documentation::Documentation RenderablePlanesCloud::Documentation() { - return codegen::doc("digitaluniverse_RenderablePlanesCloud"); -} - -RenderablePlanesCloud::RenderablePlanesCloud(const ghoul::Dictionary& dictionary) - : Renderable(dictionary) - , _scaleFactor(ScaleFactorInfo, 1.f, 0.f, 1000.f) - , _drawElements(DrawElementsInfo, true) - , _blendMode(BlendModeInfo, properties::OptionProperty::DisplayType::Dropdown) - , _fadeInDistances( - FadeInDistancesInfo, - glm::vec2(0.f), - glm::vec2(0.f), - glm::vec2(200000.f) - ) - , _disableFadeInDistance(DisableFadeInInfo, true) - , _planeMinSize(PlaneMinSizeInfo, 0.5, 0.0, 500.0) - , _renderOption(RenderOptionInfo, properties::OptionProperty::DisplayType::Dropdown) -{ - const Parameters p = codegen::bake(dictionary); - - addProperty(Fadeable::_opacity); - - if (p.file.has_value()) { - _speckFile = absPath(*p.file); - _hasSpeckFile = true; - _drawElements.onChange([this]() { _hasSpeckFile = !_hasSpeckFile; }); - addProperty(_drawElements); - } - - // DEBUG: - _renderOption.addOption(0, "Camera View Direction"); - _renderOption.addOption(1, "Camera Position Normal"); - _renderOption.addOption(2, "Screen center Position Normal"); - addProperty(_renderOption); - //_renderOption = 1; - - if (p.unit.has_value()) { - _unit = codegen::map(*p.unit); - } - else { - _unit = DistanceUnit::Meter; - } - - _scaleFactor = p.scaleFactor.value_or(_scaleFactor); - addProperty(_scaleFactor); - _scaleFactor.onChange([this]() { _dataIsDirty = true; }); - - if (p.labels.has_value()) { - _labels = std::make_unique(*p.labels); - _hasLabels = true; - addPropertySubOwner(_labels.get()); - // Fading of the labels should also depend on the fading of the renderable - _labels->setParentFadeable(this); - } - - _transformationMatrix = p.transformationMatrix.value_or(_transformationMatrix); - - _blendMode.addOptions({ - { BlendModeNormal, "Normal" }, - { BlendModeAdditive, "Additive" } - }); - _blendMode.onChange([this]() { - BlendMode m = static_cast(_blendMode.value()); - switch (m) { - case BlendModeNormal: - setRenderBin(Renderable::RenderBin::Opaque); - break; - case BlendModeAdditive: - setRenderBin(Renderable::RenderBin::PreDeferredTransparent); - break; - } - }); - - if (p.blendMode.has_value()) { - switch (*p.blendMode) { - case Parameters::BlendMode::Normal: - _blendMode = BlendModeNormal; - break; - case Parameters::BlendMode::Additive: - _blendMode = BlendModeAdditive; - break; - } - } - - _texturesPath = absPath(p.texturePath); - - _luminosityVar = p.luminosity.value_or(_luminosityVar); - _sluminosity = p.scaleLuminosity.value_or(_sluminosity); - - if (p.fadeInDistances.has_value()) { - _fadeInDistances = *p.fadeInDistances; - _disableFadeInDistance = false; - _fadeInDistances.setViewOption(properties::Property::ViewOptions::MinMaxRange); - addProperty(_fadeInDistances); - addProperty(_disableFadeInDistance); - } - - _planeMinSize = p.planeMinSize.value_or(_planeMinSize); - - if (p.planeMinSize.has_value()) { - addProperty(_planeMinSize); - } -} - -bool RenderablePlanesCloud::isReady() const { - bool isReady = _program && !_dataset.entries.empty(); - - // If we have labels, they also need to be loaded - if (_hasLabels) { - isReady = isReady || _labels->isReady(); - } - return isReady; -} - -void RenderablePlanesCloud::initialize() { - ZoneScoped; - - if (_hasSpeckFile && std::filesystem::is_regular_file(_speckFile)) { - _dataset = dataloader::data::loadFileWithCache(_speckFile); - if (_dataset.entries.empty()) { - throw ghoul::RuntimeError("Error loading data"); - } - } - - if (_hasLabels) { - _labels->initialize(); - } -} - -void RenderablePlanesCloud::initializeGL() { - ZoneScoped; - - _program = DigitalUniverseModule::ProgramObjectManager.request( - "RenderablePlanesCloud", - []() -> std::unique_ptr { - return global::renderEngine->buildRenderProgram( - "RenderablePlanesCloud", - absPath("${MODULE_DIGITALUNIVERSE}/shaders/plane_vs.glsl"), - absPath("${MODULE_DIGITALUNIVERSE}/shaders/plane_fs.glsl") - ); - } - ); - - ghoul::opengl::updateUniformLocations(*_program, _uniformCache, UniformNames); - - createPlanes(); - loadTextures(); -} - -void RenderablePlanesCloud::deleteDataGPUAndCPU() { - for (std::unordered_map::reference pAMapItem : _planesMap) { - glDeleteBuffers(1, &pAMapItem.second.vbo); - glDeleteVertexArrays(1, &pAMapItem.second.vao); - pAMapItem.second.planesCoordinates.clear(); - } - _planesMap.clear(); -} - -void RenderablePlanesCloud::deinitializeGL() { - deleteDataGPUAndCPU(); - - DigitalUniverseModule::ProgramObjectManager.release( - "RenderablePlanesCloud", - [](ghoul::opengl::ProgramObject* p) { - global::renderEngine->removeRenderProgram(p); - } - ); -} - -void RenderablePlanesCloud::renderPlanes(const RenderData&, - const glm::dmat4& modelViewTransform, - const glm::dmat4& projectionTransform, - const float fadeInVariable) -{ - glEnablei(GL_BLEND, 0); - glBlendFunc(GL_SRC_ALPHA, GL_ONE); - //glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glDepthMask(false); - - _program->activate(); - - glm::dmat4 modelViewProjectionTransform = - glm::dmat4(projectionTransform) * modelViewTransform; - _program->setUniform( - _uniformCache.modelViewProjectionTransform, - modelViewProjectionTransform - ); - _program->setUniform(_uniformCache.alphaValue, opacity()); - _program->setUniform(_uniformCache.fadeInValue, fadeInVariable); - - glDisable(GL_CULL_FACE); - - GLint viewport[4]; - global::renderEngine->openglStateCache().viewport(viewport); - - ghoul::opengl::TextureUnit unit; - unit.activate(); - _program->setUniform(_uniformCache.galaxyTexture, unit); - int currentTextureIndex = -1; - - for (std::unordered_map::reference pAMapItem : _planesMap) { - // For planes with undefined textures references - if (pAMapItem.first == 30) { - continue; - } - - // We only bind a new texture when it is needed - if (currentTextureIndex != pAMapItem.first) { - _textureMap[pAMapItem.first]->bind(); - currentTextureIndex = pAMapItem.first; - } - glBindVertexArray(pAMapItem.second.vao); - glDrawArrays(GL_TRIANGLES, 0, 6 * pAMapItem.second.numberOfPlanes); - } - - glBindVertexArray(0); - _program->deactivate(); - - // Restores OpenGL Rendering State - global::renderEngine->openglStateCache().resetBlendState(); - global::renderEngine->openglStateCache().resetDepthState(); - global::renderEngine->openglStateCache().resetPolygonAndClippingState(); -} - -void RenderablePlanesCloud::render(const RenderData& data, RendererTasks&) { - const double scale = toMeter(_unit); - - float fadeInVariable = 1.f; - if (!_disableFadeInDistance) { - float distCamera = static_cast(glm::length(data.camera.positionVec3())); - distCamera = static_cast(distCamera / scale); - const glm::vec2 fadeRange = _fadeInDistances; - //const float a = 1.f / ((fadeRange.y - fadeRange.x) * scale); - const float a = 1.f / ((fadeRange.y - fadeRange.x)); - const float b = -(fadeRange.x / (fadeRange.y - fadeRange.x)); - const float funcValue = a * distCamera + b; - fadeInVariable *= funcValue > 1.f ? 1.f : funcValue; - - if (funcValue < 0.01f) { - return; - } - } - - const glm::dmat4 modelTransform = calcModelTransform(data); - const glm::dmat4 modelViewTransform = calcModelViewTransform(data, modelTransform); - const glm::mat4 projectionTransform = data.camera.projectionMatrix(); - - if (_hasSpeckFile) { - renderPlanes(data, modelViewTransform, projectionTransform, fadeInVariable); - } - - if (_hasLabels) { - const glm::dmat4 modelViewProjectionTransform = - glm::dmat4(projectionTransform) * modelViewTransform; - - const glm::dmat4 invMVPParts = glm::inverse(modelTransform) * - glm::inverse(data.camera.combinedViewMatrix()) * - glm::inverse(glm::dmat4(projectionTransform)); - const glm::dvec3 orthoRight = glm::normalize( - glm::dvec3(invMVPParts * glm::dvec4(1.0, 0.0, 0.0, 0.0)) - ); - const glm::dvec3 orthoUp = glm::normalize( - glm::dvec3(invMVPParts * glm::dvec4(0.0, 1.0, 0.0, 0.0)) - ); - - _labels->render( - data, - modelViewProjectionTransform, - orthoRight, - orthoUp, - fadeInVariable - ); - } -} - -void RenderablePlanesCloud::update(const UpdateData&) { - if (_dataIsDirty && _hasSpeckFile) { - deleteDataGPUAndCPU(); - createPlanes(); - _dataIsDirty = false; - } - - if (_program->isDirty()) { - _program->rebuildFromFile(); - ghoul::opengl::updateUniformLocations(*_program, _uniformCache, UniformNames); - } -} - -void RenderablePlanesCloud::loadTextures() { - for (const dataloader::Dataset::Texture& tex : _dataset.textures) { - std::filesystem::path fullPath = absPath(_texturesPath / tex.file); - std::filesystem::path pngPath = fullPath; - pngPath.replace_extension(".png"); - - std::filesystem::path path; - if (std::filesystem::is_regular_file(fullPath)) { - path = fullPath; - } - else if (std::filesystem::is_regular_file(pngPath)) { - path = pngPath; - } - else { - // We can't really recover from this as it would crash during rendering anyway - throw ghoul::RuntimeError(std::format( - "Could not find image file '{}'", tex.file - )); - } - - std::unique_ptr t = - ghoul::io::TextureReader::ref().loadTexture(path, 2); - - if (t) { - LINFOC("RenderablePlanesCloud", std::format("Loaded texture '{}'", path)); - t->uploadTexture(); - t->setFilter(ghoul::opengl::Texture::FilterMode::LinearMipMap); - t->purgeFromRAM(); - } - else { - // Same here, we won't be able to recover from this nullptr - throw ghoul::RuntimeError(std::format( - "Could not find image file '{}'", tex.file - )); - } - - _textureMap.insert(std::pair(tex.index, std::move(t))); - } -} - -void RenderablePlanesCloud::createPlanes() { - if (_dataIsDirty && _hasSpeckFile) { - const int lumIdx = std::max(_dataset.index(_luminosityVar), 0); - const double scale = toMeter(_unit); - - LDEBUG("Creating planes..."); - float maxSize = 0.f; - double maxRadius = 0.0; - for (const dataloader::Dataset::Entry& e : _dataset.entries) { - const glm::vec4 transformedPos = glm::vec4( - _transformationMatrix * glm::dvec4(e.position, 1.0) - ); - - const double r = glm::length(glm::dvec3(transformedPos) * scale); - maxRadius = std::max(maxRadius, r); - - // Plane vectors u and v - glm::vec4 u = glm::vec4( - _transformationMatrix * - glm::dvec4( - e.data[_dataset.orientationDataIndex + 0], - e.data[_dataset.orientationDataIndex + 1], - e.data[_dataset.orientationDataIndex + 2], - 1.f - ) - ); - u /= 2.f; - u.w = 0.f; - - glm::vec4 v = glm::vec4( - _transformationMatrix * - glm::dvec4( - e.data[_dataset.orientationDataIndex + 3], - e.data[_dataset.orientationDataIndex + 4], - e.data[_dataset.orientationDataIndex + 5], - 1.f - ) - ); - v /= 2.f; - v.w = 0.f; - - if (!_luminosityVar.empty()) { - float lumS = e.data[lumIdx] * _sluminosity; - u *= lumS; - v *= lumS; - } - - u *= _scaleFactor; - v *= _scaleFactor; - - glm::vec4 vertex0 = transformedPos - u - v; // same as 3 - glm::vec4 vertex1 = transformedPos + u + v; // same as 5 - glm::vec4 vertex2 = transformedPos - u + v; - glm::vec4 vertex4 = transformedPos + u - v; - - for (int i = 0; i < 3; i++) { - maxSize = std::max(maxSize, vertex0[i]); - maxSize = std::max(maxSize, vertex1[i]); - maxSize = std::max(maxSize, vertex2[i]); - maxSize = std::max(maxSize, vertex4[i]); - } - - vertex0 = glm::vec4(glm::dvec4(vertex0) * scale); - vertex1 = glm::vec4(glm::dvec4(vertex1) * scale); - vertex2 = glm::vec4(glm::dvec4(vertex2) * scale); - vertex4 = glm::vec4(glm::dvec4(vertex4) * scale); - - const std::array VertexData = { - // x y z w s t - vertex0.x, vertex0.y, vertex0.z, 1.f, 0.f, 0.f, - vertex1.x, vertex1.y, vertex1.z, 1.f, 1.f, 1.f, - vertex2.x, vertex2.y, vertex2.z, 1.f, 0.f, 1.f, - vertex0.x, vertex0.y, vertex0.z, 1.f, 0.f, 0.f, - vertex4.x, vertex4.y, vertex4.z, 1.f, 1.f, 0.f, - vertex1.x, vertex1.y, vertex1.z, 1.f, 1.f, 1.f, - }; - - int textureIndex = static_cast(e.data[_dataset.textureDataIndex]); - std::unordered_map::iterator found = - _planesMap.find(textureIndex); - if (found != _planesMap.end()) { - for (int i = 0; i < PlanesVertexDataSize; i++) { - found->second.planesCoordinates.push_back(VertexData[i]); - } - found->second.numberOfPlanes++; - } - else { - PlaneAggregate pA; - pA.textureIndex = textureIndex; - glGenVertexArrays(1, &pA.vao); - glGenBuffers(1, &pA.vbo); - pA.numberOfPlanes = 1; - for (int i = 0; i < PlanesVertexDataSize; i++) { - pA.planesCoordinates.push_back(VertexData[i]); - } - _planesMap.insert(std::pair(textureIndex, pA)); - } - } - - // Send data to GPU - for (const std::pair& pAMapItem : _planesMap) { - glBindVertexArray(pAMapItem.second.vao); - glBindBuffer(GL_ARRAY_BUFFER, pAMapItem.second.vbo); - glBufferData( - GL_ARRAY_BUFFER, - sizeof(GLfloat) * PlanesVertexDataSize * pAMapItem.second.numberOfPlanes, - pAMapItem.second.planesCoordinates.data(), - GL_STATIC_DRAW - ); - // in_position - glEnableVertexAttribArray(0); - glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), nullptr); - - // texture coords - glEnableVertexAttribArray(1); - glVertexAttribPointer( - 1, - 2, - GL_FLOAT, - GL_FALSE, - 6 * sizeof(GLfloat), - reinterpret_cast(4 * sizeof(GLfloat)) - ); - - glBindVertexArray(0); - } - - _dataIsDirty = false; - - setBoundingSphere(maxRadius * _scaleFactor); - _fadeInDistances.setMaxValue(glm::vec2(10.f * maxSize)); - } -} - -} // namespace openspace diff --git a/modules/digitaluniverse/rendering/renderableplanescloud.h b/modules/digitaluniverse/rendering/renderableplanescloud.h deleted file mode 100644 index ed419bec63..0000000000 --- a/modules/digitaluniverse/rendering/renderableplanescloud.h +++ /dev/null @@ -1,127 +0,0 @@ -/***************************************************************************************** - * * - * OpenSpace * - * * - * Copyright (c) 2014-2024 * - * * - * 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___RENDERABLEPLANESCLOUD___H__ -#define __OPENSPACE_MODULE_DIGITALUNIVERSE___RENDERABLEPLANESCLOUD___H__ - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace ghoul::filesystem { class File; } -namespace ghoul::opengl { - class ProgramObject; - class Texture; -} // namespace ghoul::opengl - -namespace openspace { - -// (x, y, z, w, s, t) * 6 = 36 - -namespace documentation { struct Documentation; } - -class RenderablePlanesCloud : public Renderable { -public: - explicit RenderablePlanesCloud(const ghoul::Dictionary& dictionary); - ~RenderablePlanesCloud() override = default; - - void initialize() override; - void initializeGL() 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: - struct PlaneAggregate { - int textureIndex; - int numberOfPlanes; - GLuint vao; - GLuint vbo; - std::vector planesCoordinates; - }; - - void deleteDataGPUAndCPU(); - void createPlanes(); - void renderPlanes(const RenderData& data, const glm::dmat4& modelViewTransform, - const glm::dmat4& projectionTransform, float fadeInVariable); - - void loadTextures(); - - bool _hasSpeckFile = false; - bool _dataIsDirty = true; - bool _hasLabels = false; - - properties::FloatProperty _scaleFactor; - properties::BoolProperty _drawElements; - properties::OptionProperty _blendMode; - properties::Vec2Property _fadeInDistances; - properties::BoolProperty _disableFadeInDistance; - properties::FloatProperty _planeMinSize; - properties::OptionProperty _renderOption; - - ghoul::opengl::ProgramObject* _program = nullptr; - UniformCache( - modelViewProjectionTransform, alphaValue, fadeInValue, galaxyTexture - ) _uniformCache; - std::unordered_map> _textureMap; - std::unordered_map _textureFileMap; - std::unordered_map _planesMap; - - std::filesystem::path _speckFile; - std::filesystem::path _texturesPath; - std::string _luminosityVar; - - DistanceUnit _unit = DistanceUnit::Parsec; - - dataloader::Dataset _dataset; - - // Everything related to the labels is handled by LabelsComponent - std::unique_ptr _labels; - - float _sluminosity = 1.f; - - glm::dmat4 _transformationMatrix = glm::dmat4(1.0); -}; - -} // namespace openspace - -#endif // __OPENSPACE_MODULE_DIGITALUNIVERSE___RENDERABLEPLANESCLOUD___H__