mirror of
https://github.com/OpenSpace/OpenSpace.git
synced 2026-01-06 11:39:49 -06:00
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:
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -49,6 +49,7 @@ struct SizeMappingComponent : public properties::PropertyOwner {
|
||||
properties::BoolProperty enabled;
|
||||
properties::OptionProperty parameterOption;
|
||||
properties::FloatProperty scaleFactor;
|
||||
properties::BoolProperty isRadius;
|
||||
};
|
||||
|
||||
} // namespace openspace
|
||||
|
||||
@@ -103,9 +103,6 @@ Fragment getFragment() {
|
||||
}
|
||||
|
||||
fullColor.a *= opacity * fadeInValue;
|
||||
if (fullColor.a < 0.01) {
|
||||
discard;
|
||||
}
|
||||
|
||||
Fragment frag;
|
||||
frag.color = fullColor;
|
||||
@@ -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
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
}
|
||||
Reference in New Issue
Block a user