Include orientation data in new point cloud and remove RenderablePlanesCloud (#3168)

* WIP start including rotation data

* Fix option to use orientation data or not

* Renames to reflect that points are no longer billboarded (at least not always)

* Increase max value for scale factor

* Fix correct scaling for tully and deep sky objects

* Remove the old RenderablePlanesCloud, we don't need it anymore

* Add unit handling for size scaling from data values

* Clarify some documentation about the points being rendered as planes

* Update datasets to the new ones on the server

* Use quaternions for orientation instead of vectors (simplifies interpolation and requires less data)

* Make interpolation of orientation work

* Fix size for deep sky objects being too small due to data being radius rather than diameter

* Add IsRadius flag for deepsky

* Update asset versions (major number update, due to breaking changes)

---------

Co-authored-by: Alexander Bock <alexander.bock@liu.se>
This commit is contained in:
Emma Broman
2024-04-18 14:06:55 +02:00
committed by GitHub
parent 2b5b882e14
commit 82ddbc57f8
19 changed files with 491 additions and 941 deletions

View File

@@ -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",

View File

@@ -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)",

View File

@@ -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",

View File

@@ -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)",

View File

@@ -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

View File

@@ -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<float>& result) const
std::vector<float>& 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<float>& 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);
}

View File

@@ -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<float>& result) const override;
void addOrientationDataForPoint(unsigned int index,
std::vector<float>& result) const override;
void initializeBufferData();
void updateBufferData() override;

View File

@@ -44,6 +44,8 @@
#include <ghoul/opengl/textureconversion.h>
#include <ghoul/opengl/textureunit.h>
#include <glm/gtx/string_cast.hpp>
#include <glm/gtx/quaternion.hpp>
#include <glm/gtx/vector_angle.hpp>
#include <array>
#include <cmath>
#include <cstdint>
@@ -56,7 +58,7 @@
namespace {
constexpr std::string_view _loggerCat = "RenderablePointCloud";
constexpr std::array<const char*, 34> UniformNames = {
constexpr std::array<const char*, 35> 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> renderOption;
// [[codegen::verbatim(OrientationRenderOptionInfo.description)]]
std::optional<RenderOption> orientationRenderOption;
// [[codegen::verbatim(UseOrientationDataInfo.description)]]
std::optional<bool> useOrientationData;
// [[codegen::verbatim(UseAdditiveBlendingInfo.description)]]
std::optional<bool> 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<RenderOption>(*p.renderOption);
if (p.orientationRenderOption.has_value()) {
_renderOption = codegen::map<RenderOption>(*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<unsigned int>(_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<float>& 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<float>& 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<float> RenderablePointCloud::createDataSlice() {
ZoneScoped;
@@ -1491,9 +1622,6 @@ std::vector<float> RenderablePointCloud::createDataSlice() {
return std::vector<float>();
}
// 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<float> 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<int>(e.data[textureIdIndex]);
if (useMultiTexture) {
int texId = static_cast<int>(e.data[_dataset.textureDataIndex]);
size_t texIndex = _indexInDataToTextureIndex[texId];
textureLayer = static_cast<float>(
_textureIndexToArrayMap[texIndex].layer
@@ -1531,6 +1660,10 @@ std::vector<float> RenderablePointCloud::createDataSlice() {
addPositionDataForPoint(i, subArrayToUse, maxRadius);
addColorAndSizeDataForPoint(i, subArrayToUse);
if (useOrientationData()) {
addOrientationDataForPoint(i, subArrayToUse);
}
// Texture layer
if (_hasSpriteTexture) {
subArrayToUse.push_back(static_cast<float>(textureLayer));

View File

@@ -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<float>& result,
double& maxRadius) const;
virtual void addColorAndSizeDataForPoint(unsigned int index,
std::vector<float>& result) const;
virtual void addOrientationDataForPoint(unsigned int index,
std::vector<float>& result) const;
std::vector<float> 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;

View File

@@ -25,6 +25,7 @@
#include <modules/base/rendering/pointcloud/sizemappingcomponent.h>
#include <openspace/documentation/documentation.h>
#include <openspace/util/distanceconversion.h>
#include <ghoul/logging/logmanager.h>
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<bool> enabled;
@@ -69,7 +78,36 @@ namespace {
std::optional<std::string> parameter;
// [[codegen::verbatim(ScaleFactorInfo.description)]]
std::optional<float> 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 <code>ScaleFactor = 'Kilometer'</code> or
// <code>ScaleFactor = 1000.0</code>.
std::optional<std::variant<ScaleUnit, double>> scaleFactor;
// [[codegen::verbatim(IsRadiusInfo.description)]]
std::optional<bool> 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<Parameters::ScaleUnit>(*p.scaleFactor)) {
const Parameters::ScaleUnit scaleUnit =
std::get<Parameters::ScaleUnit>(*p.scaleFactor);
const DistanceUnit distanceUnit = codegen::map<DistanceUnit>(scaleUnit);
scaleFactor = toMeter(distanceUnit);
}
else if (std::holds_alternative<double>(*p.scaleFactor)) {
scaleFactor = std::get<double>(*p.scaleFactor);
}
}
isRadius = p.isRadius.value_or(isRadius);
}
} // namespace openspace

View File

@@ -49,6 +49,7 @@ struct SizeMappingComponent : public properties::PropertyOwner {
properties::BoolProperty enabled;
properties::OptionProperty parameterOption;
properties::FloatProperty scaleFactor;
properties::BoolProperty isRadius;
};
} // namespace openspace

View File

@@ -103,9 +103,6 @@ Fragment getFragment() {
}
fullColor.a *= opacity * fadeInValue;
if (fullColor.a < 0.01) {
discard;
}
Fragment frag;
frag.color = fullColor;

View File

@@ -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

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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})

View File

@@ -25,7 +25,6 @@
#include <modules/digitaluniverse/digitaluniversemodule.h>
#include <modules/digitaluniverse/rendering/renderabledumeshes.h>
#include <modules/digitaluniverse/rendering/renderableplanescloud.h>
#include <openspace/documentation/documentation.h>
#include <openspace/rendering/renderable.h>
#include <openspace/util/factorymanager.h>
@@ -46,7 +45,6 @@ void DigitalUniverseModule::internalInitialize(const ghoul::Dictionary&) {
FactoryManager::ref().factory<Renderable>();
ghoul_assert(fRenderable, "Renderable factory was not created");
fRenderable->registerClass<RenderablePlanesCloud>("RenderablePlanesCloud");
fRenderable->registerClass<RenderableDUMeshes>("RenderableDUMeshes");
}
@@ -57,7 +55,6 @@ void DigitalUniverseModule::internalDeinitializeGL() {
std::vector<documentation::Documentation> DigitalUniverseModule::documentations() const {
return {
RenderablePlanesCloud::Documentation(),
RenderableDUMeshes::Documentation()
};
}

View File

@@ -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 <modules/digitaluniverse/rendering/renderableplanescloud.h>
#include <modules/digitaluniverse/digitaluniversemodule.h>
#include <openspace/documentation/documentation.h>
#include <openspace/documentation/verifier.h>
#include <openspace/engine/globals.h>
#include <openspace/rendering/renderengine.h>
#include <openspace/util/updatestructures.h>
#include <ghoul/filesystem/filesystem.h>
#include <ghoul/io/texture/texturereader.h>
#include <ghoul/logging/logmanager.h>
#include <ghoul/misc/profiling.h>
#include <ghoul/opengl/openglstatecache.h>
#include <ghoul/opengl/programobject.h>
#include <ghoul/opengl/texture.h>
#include <ghoul/opengl/textureunit.h>
#include <array>
#include <filesystem>
#include <fstream>
#include <optional>
#include <string>
namespace {
constexpr std::string_view _loggerCat = "RenderablePlanesCloud";
constexpr int PlanesVertexDataSize = 36;
constexpr std::array<const char*, 4> 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<std::string> file;
// [[codegen::verbatim(ScaleFactorInfo.description)]]
std::optional<float> scaleFactor;
// [[codegen::verbatim(LabelsInfo.description)]]
std::optional<ghoul::Dictionary> labels
[[codegen::reference("labelscomponent")]];
// [[codegen::verbatim(TransformationMatrixInfo.description)]]
std::optional<glm::dmat4x4> transformationMatrix;
enum class BlendMode {
Normal,
Additive
};
// [[codegen::verbatim(BlendModeInfo.description)]]
std::optional<BlendMode> 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> unit;
// [[codegen::verbatim(TexturePathInfo.description)]]
std::string texturePath;
// [[codegen::verbatim(LuminosityInfo.description)]]
std::optional<std::string> luminosity;
// [[codegen::verbatim(ScaleLuminosityInfo.description)]]
std::optional<float> scaleLuminosity;
// [[codegen::verbatim(FadeInDistancesInfo.description)]]
std::optional<glm::vec2> fadeInDistances;
// [[codegen::verbatim(DisableFadeInInfo.description)]]
std::optional<bool> disableFadeIn;
// [[codegen::verbatim(PlaneMinSizeInfo.description)]]
std::optional<float> planeMinSize;
};
#include "renderableplanescloud_codegen.cpp"
} // namespace
namespace openspace {
documentation::Documentation RenderablePlanesCloud::Documentation() {
return codegen::doc<Parameters>("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<Parameters>(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<DistanceUnit>(*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<LabelsComponent>(*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>(_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<ghoul::opengl::ProgramObject> {
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<int, PlaneAggregate>::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<int, PlaneAggregate>::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<float>(glm::length(data.camera.positionVec3()));
distCamera = static_cast<float>(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<ghoul::opengl::Texture> 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<GLfloat, 36> 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<int>(e.data[_dataset.textureDataIndex]);
std::unordered_map<int, PlaneAggregate>::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<const int, PlaneAggregate>& 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<GLvoid*>(4 * sizeof(GLfloat))
);
glBindVertexArray(0);
}
_dataIsDirty = false;
setBoundingSphere(maxRadius * _scaleFactor);
_fadeInDistances.setMaxValue(glm::vec2(10.f * maxSize));
}
}
} // namespace openspace

View File

@@ -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 <openspace/rendering/renderable.h>
#include <openspace/data/dataloader.h>
#include <openspace/properties/optionproperty.h>
#include <openspace/properties/stringproperty.h>
#include <openspace/properties/scalar/boolproperty.h>
#include <openspace/properties/scalar/floatproperty.h>
#include <openspace/properties/vector/vec2property.h>
#include <openspace/properties/vector/vec3property.h>
#include <openspace/rendering/labelscomponent.h>
#include <openspace/util/distanceconversion.h>
#include <ghoul/opengl/ghoul_gl.h>
#include <ghoul/opengl/uniformcache.h>
#include <filesystem>
#include <functional>
#include <unordered_map>
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<GLfloat> 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<int, std::unique_ptr<ghoul::opengl::Texture>> _textureMap;
std::unordered_map<int, std::string> _textureFileMap;
std::unordered_map<int, PlaneAggregate> _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<LabelsComponent> _labels;
float _sluminosity = 1.f;
glm::dmat4 _transformationMatrix = glm::dmat4(1.0);
};
} // namespace openspace
#endif // __OPENSPACE_MODULE_DIGITALUNIVERSE___RENDERABLEPLANESCLOUD___H__