Add the ability to specify longitude and latitude subdivisions in RenderableSphericalGrid (closes #3494)

This commit is contained in:
Alexander Bock
2025-01-20 11:26:38 +01:00
parent c4cf883ec9
commit 5771427bd9
2 changed files with 79 additions and 59 deletions

View File

@@ -43,12 +43,37 @@ namespace {
openspace::properties::Property::Visibility::NoviceUser
};
constexpr openspace::properties::Property::PropertyInfo LongSegmentsInfo = {
"LongSegments",
"Number of Longitudinal Segments",
"The number of longitudinal segments the sphere is split into. Determines the "
"resolution of the rendered sphere in a left/right direction when looking "
"straight at the equator. Should be an even value (if an odd value is provided, "
"the value will be set to the new value minus one). If the `Segments` value is "
"provided as well, it will have precedence over this value",
openspace::properties::Property::Visibility::User
};
constexpr openspace::properties::Property::PropertyInfo LatSegmentsInfo = {
"LatSegments",
"Number of Latitudinal Segments",
"The number of latitudinal segments the sphere is split into. Determines the "
"resolution of the rendered sphere in a up/down direction when looking "
"straight at the equator. Should be an even value (if an odd value is provided, "
"the value will be set to the new value minus one). If the `Segments` value is "
"provided as well, it will have precedence over this value",
openspace::properties::Property::Visibility::User
};
constexpr openspace::properties::Property::PropertyInfo SegmentsInfo = {
"Segments",
"Number of Segments",
"The number of segments the sphere is split into. Determines the resolution "
"of the rendered sphere. Should be an even value (if an odd value is provided, "
"the value will be set to the new value minus one).",
"the value will be set to the new value minus one). Setting this value is equal "
"to setting `LongSegments` and `LatSegments` to the same value. If this value is "
"specified, it will overwrite the values provided in `LongSegments` and "
"`LatSegments`.",
openspace::properties::Property::Visibility::User
};
@@ -69,6 +94,12 @@ namespace {
// [[codegen::verbatim(ColorInfo.description)]]
std::optional<glm::vec3> color [[codegen::color()]];
// [[codegen::verbatim(LongSegmentsInfo.description)]]
std::optional<int> longSegments;
// [[codegen::verbatim(LatSegmentsInfo.description)]]
std::optional<int> latSegments;
// [[codegen::verbatim(SegmentsInfo.description)]]
std::optional<int> segments;
@@ -92,7 +123,8 @@ RenderableSphericalGrid::RenderableSphericalGrid(const ghoul::Dictionary& dictio
: Renderable(dictionary)
, _gridProgram(nullptr)
, _color(ColorInfo, glm::vec3(0.5f), glm::vec3(0.f), glm::vec3(1.f))
, _segments(SegmentsInfo, 36, 4, 200)
, _longSegments(LongSegmentsInfo, 36, 4, 200)
, _latSegments(LatSegmentsInfo, 36, 4, 200)
, _lineWidth(LineWidthInfo, 0.5f, 1.f, 20.f)
{
const Parameters p = codegen::bake<Parameters>(dictionary);
@@ -103,14 +135,19 @@ RenderableSphericalGrid::RenderableSphericalGrid(const ghoul::Dictionary& dictio
_color.setViewOption(properties::Property::ViewOptions::Color);
addProperty(_color);
_segments = p.segments.value_or(_segments);
_segments.onChange([this]() {
if (_segments.value() % 2 == 1) {
_segments = _segments - 1;
auto gridDirty = [this]() {
if (_longSegments.value() % 2 == 1) {
_longSegments = _longSegments - 1;
}
_gridIsDirty = true;
});
addProperty(_segments);
};
_longSegments = p.segments.value_or(p.longSegments.value_or(_longSegments));
_longSegments.onChange(gridDirty);
addProperty(_longSegments);
_latSegments = p.segments.value_or(p.latSegments.value_or(_latSegments));
_latSegments.onChange(gridDirty);
addProperty(_latSegments);
_lineWidth = p.lineWidth.value_or(_lineWidth);
addProperty(_lineWidth);
@@ -193,9 +230,10 @@ void RenderableSphericalGrid::render(const RenderData& data, RendererTasks&) {
// Change GL state:
#ifndef __APPLE__
glLineWidth(_lineWidth);
#else
#else // ^^^^ __APPLE__ // !__APPLE__ vvvv
glLineWidth(1.f);
#endif
#endif // __APPLE__
glEnablei(GL_BLEND, 0);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_LINE_SMOOTH);
@@ -203,7 +241,7 @@ void RenderableSphericalGrid::render(const RenderData& data, RendererTasks&) {
glBindVertexArray(_vaoID);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _iBufferID);
glDrawElements(_mode, _isize, GL_UNSIGNED_INT, nullptr);
glDrawElements(GL_LINES, 6 * _longSegments * _latSegments, GL_UNSIGNED_INT, nullptr);
glBindVertexArray(0);
_gridProgram->deactivate();
@@ -222,14 +260,14 @@ void RenderableSphericalGrid::render(const RenderData& data, RendererTasks&) {
const glm::dmat4 worldToModelTransform = glm::inverse(modelTransform);
glm::vec3 orthoRight = glm::normalize(
glm::vec3(worldToModelTransform * glm::vec4(right, 0.0))
glm::vec3(worldToModelTransform * glm::vec4(right, 0.f))
);
if (orthoRight == glm::vec3(0.0)) {
if (orthoRight == glm::vec3(0.f)) {
const glm::vec3 otherVector = glm::vec3(lookup.y, lookup.x, lookup.z);
right = glm::cross(viewDirection, otherVector);
orthoRight = glm::normalize(
glm::vec3(worldToModelTransform * glm::vec4(right, 0.0))
glm::vec3(worldToModelTransform * glm::vec4(right, 0.f))
);
}
const glm::vec3 orthoUp = glm::normalize(
@@ -244,28 +282,21 @@ void RenderableSphericalGrid::update(const UpdateData&) {
return;
}
_isize = 6 * _segments * _segments;
_vsize = (_segments + 1) * (_segments + 1);
_varray.resize(_vsize);
constexpr Vertex v = { 0.f, 0.f, 0.f };
std::fill(_varray.begin(), _varray.end(), v);
_iarray.resize(_isize);
std::fill(_iarray.begin(), _iarray.end(), 0);
unsigned int vertSize = (_longSegments + 1) * (_latSegments + 1);
std::vector<Vertex> vert = std::vector<Vertex>(vertSize, { 0.f, 0.f, 0.f });
unsigned int idxSize = 6 * _longSegments * _latSegments;
std::vector<int> idx = std::vector<int>(idxSize, 0);
int nr = 0;
const float fsegments = static_cast<float>(_segments);
for (int nSegment = 0; nSegment <= _segments; ++nSegment) {
for (int lat = 0; lat <= _latSegments; ++lat) {
// define an extra vertex around the y-axis due to texture mapping
for (int j = 0; j <= _segments; j++) {
const float fi = static_cast<float>(nSegment);
const float fj = static_cast<float>(j);
for (int lng = 0; lng <= _longSegments; lng++) {
// inclination angle (north to south)
const float theta = fi * glm::pi<float>() / fsegments * 2.f; // 0 -> PI
const float theta = lat * glm::pi<float>() / _latSegments * 2.f; // 0 -> PI
// azimuth angle (east to west)
const float phi = fj * glm::pi<float>() * 2.0f / fsegments; // 0 -> 2*PI
const float phi = lng * 2.f * glm::pi<float>() / _longSegments; // 0 -> 2*PI
const float x = std::sin(phi) * std::sin(theta); //
const float y = std::cos(theta); // up
@@ -276,7 +307,7 @@ void RenderableSphericalGrid::update(const UpdateData&) {
normal = glm::normalize(normal);
}
glm::vec4 tmp(x, y, z, 1.f);
glm::vec4 tmp = glm::vec4(x, y, z, 1.f);
const glm::mat4 rot = glm::rotate(
glm::mat4(1.f),
glm::half_pi<float>(),
@@ -285,41 +316,35 @@ void RenderableSphericalGrid::update(const UpdateData&) {
tmp = glm::vec4(glm::dmat4(rot) * glm::dvec4(tmp));
for (int i = 0; i < 3; i++) {
_varray[nr].location[i] = tmp[i];
vert[nr].location[i] = tmp[i];
}
++nr;
}
}
nr = 0;
// define indices for all triangles
for (int i = 1; i <= _segments; i++) {
for (int j = 0; j < _segments; j++) {
const int t = _segments + 1;
_iarray[nr] = t * (i - 1) + j + 0; ++nr;
_iarray[nr] = t * (i + 0) + j + 0; ++nr;
_iarray[nr] = t * (i + 0) + j + 1; ++nr;
_iarray[nr] = t * (i - 1) + j + 1; ++nr;
_iarray[nr] = t * (i - 1) + j + 0; ++nr;
for (int i = 1; i <= _latSegments; i++) {
for (int j = 0; j < _longSegments; j++) {
const int t = _longSegments + 1;
idx[nr] = t * (i - 1) + j + 0; ++nr;
idx[nr] = t * (i + 0) + j + 0; ++nr;
idx[nr] = t * (i + 0) + j + 1; ++nr;
idx[nr] = t * (i - 1) + j + 1; ++nr;
idx[nr] = t * (i - 1) + j + 0; ++nr;
}
}
glBindVertexArray(_vaoID);
glBindBuffer(GL_ARRAY_BUFFER, _vBufferID);
glBufferData(
GL_ARRAY_BUFFER,
_vsize * sizeof(Vertex),
_varray.data(),
GL_STATIC_DRAW
);
glBufferData(GL_ARRAY_BUFFER, vertSize * sizeof(Vertex), vert.data(), GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), nullptr);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _iBufferID);
glBufferData(
GL_ELEMENT_ARRAY_BUFFER,
_isize * sizeof(int),
_iarray.data(),
GL_STATIC_DRAW
idxSize * sizeof(int),
idx.data(), GL_STATIC_DRAW
);
_gridIsDirty = false;