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

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