From 447cb32ec289642fe246a6a9c02719dd9e64e18c Mon Sep 17 00:00:00 2001 From: snoyer Date: Sun, 14 Jul 2024 12:17:06 +0400 Subject: [PATCH 01/12] generalize up direction --- testing/baselines/TestGridX.png | 4 +- .../Testing/TestF3DOpenGLGridMapper.cxx | 9 - .../private/module/vtkF3DOpenGLGridMapper.cxx | 44 +-- .../private/module/vtkF3DOpenGLGridMapper.h | 12 +- vtkext/private/module/vtkF3DRenderer.cxx | 255 +++++++++++++++--- vtkext/private/module/vtkF3DRenderer.h | 15 +- 6 files changed, 259 insertions(+), 80 deletions(-) diff --git a/testing/baselines/TestGridX.png b/testing/baselines/TestGridX.png index 3d133b013d..c0133bf1d6 100644 --- a/testing/baselines/TestGridX.png +++ b/testing/baselines/TestGridX.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f77f362288ecec58cce5a81b8ede81f2b9ee4a39a13890944bf5d0143641e17a -size 23757 +oid sha256:ec32b9deb15aab8273b719daa51545c8a1af611e805261ce2b268e0d2d009d5c +size 24152 diff --git a/vtkext/private/module/Testing/TestF3DOpenGLGridMapper.cxx b/vtkext/private/module/Testing/TestF3DOpenGLGridMapper.cxx index e32cd54ffb..d3095bc510 100644 --- a/vtkext/private/module/Testing/TestF3DOpenGLGridMapper.cxx +++ b/vtkext/private/module/Testing/TestF3DOpenGLGridMapper.cxx @@ -62,17 +62,8 @@ int TestF3DOpenGLGridMapper(int argc, char* argv[]) /* `OriginOffset` offset is only for drawing the axes within the actor, * it should not affect the actual bounding box */ - mapper->SetUpIndex(0); - if (!CheckBounds("YZ with offset", mapper, -safeMargin, +safeMargin, -r, +r, -r, +r)) - return EXIT_FAILURE; - - mapper->SetUpIndex(1); if (!CheckBounds("XZ with offset", mapper, -r, +r, -safeMargin, +safeMargin, -r, +r)) return EXIT_FAILURE; - - mapper->SetUpIndex(2); - if (!CheckBounds("XY with offset", mapper, -r, +r, -r, +r, -safeMargin, +safeMargin)) - return EXIT_FAILURE; } return EXIT_SUCCESS; diff --git a/vtkext/private/module/vtkF3DOpenGLGridMapper.cxx b/vtkext/private/module/vtkF3DOpenGLGridMapper.cxx index 799ac4c230..913a1c10a3 100644 --- a/vtkext/private/module/vtkF3DOpenGLGridMapper.cxx +++ b/vtkext/private/module/vtkF3DOpenGLGridMapper.cxx @@ -28,7 +28,6 @@ void vtkF3DOpenGLGridMapper::PrintSelf(ostream& os, vtkIndent indent) os << indent << "FadeDistance: " << this->FadeDistance << "\n"; os << indent << "UnitSquare: " << this->UnitSquare << "\n"; os << indent << "Subdivisions: " << this->Subdivisions << "\n"; - os << indent << "UpIndex: " << this->UpIndex << "\n"; } //---------------------------------------------------------------------------- @@ -40,9 +39,6 @@ void vtkF3DOpenGLGridMapper::ReplaceShaderValues( std::string VSSource = shaders[vtkShader::Vertex]->GetSource(); std::string FSSource = shaders[vtkShader::Fragment]->GetSource(); - const std::string axes3d = this->UpIndex == 0 ? "zyx" : this->UpIndex == 1 ? "xzy" : "xyz"; - const std::string axes2d = this->UpIndex == 0 ? "zy" : this->UpIndex == 1 ? "xz" : "xy"; - // clang-format off vtkShaderProgram::Substitute(VSSource, "//VTK::PositionVC::Dec", "uniform vec3 originOffset;\n" @@ -52,8 +48,8 @@ void vtkF3DOpenGLGridMapper::ReplaceShaderValues( ); vtkShaderProgram::Substitute(VSSource, "//VTK::PositionVC::Impl", "gridCoord = vertexMC.xy * fadeDist;\n" - "gridOffset = originOffset." + axes2d + ";\n" - "gl_Position = MCDCMatrix * vec4(vertexMC." + axes3d + " * fadeDist, 1.0);\n" + "gridOffset = originOffset.xz;\n" + "gl_Position = MCDCMatrix * vec4(vertexMC.xzy * fadeDist, 1.0);\n" ); vtkShaderProgram::Substitute(FSSource, "//VTK::CustomUniforms::Dec", @@ -157,26 +153,8 @@ void vtkF3DOpenGLGridMapper::SetMapperShaderParameters( cellBO.Program->SetUniformf("gridLineWidth", 0.6); cellBO.Program->SetUniformf("minorOpacity", 0.5); cellBO.Program->SetUniformf("lineAntialias", 1); - - const float xColor[4] = { 1, 0, 0, 1 }; - const float yColor[4] = { 0, 1, 0, 1 }; - const float zColor[4] = { 0, 0, 1, 1 }; - switch (this->UpIndex) - { - case 0: - cellBO.Program->SetUniform4f("axis1Color", zColor); - cellBO.Program->SetUniform4f("axis2Color", yColor); - break; - case 1: - cellBO.Program->SetUniform4f("axis1Color", xColor); - cellBO.Program->SetUniform4f("axis2Color", zColor); - break; - case 2: - default: - cellBO.Program->SetUniform4f("axis1Color", xColor); - cellBO.Program->SetUniform4f("axis2Color", yColor); - break; - } + cellBO.Program->SetUniform4f("axis1Color", this->Axis1Color); + cellBO.Program->SetUniform4f("axis2Color", this->Axis2Color); } //---------------------------------------------------------------------------- @@ -210,14 +188,12 @@ void vtkF3DOpenGLGridMapper::BuildBufferObjects(vtkRenderer* ren, vtkActor* vtkN //----------------------------------------------------------------------------- double* vtkF3DOpenGLGridMapper::GetBounds() { - double r[3] = { this->FadeDistance, this->FadeDistance, this->FadeDistance }; - r[this->UpIndex] = 1e-4; - this->Bounds[0] = -r[0]; - this->Bounds[1] = +r[0]; - this->Bounds[2] = -r[1]; - this->Bounds[3] = +r[1]; - this->Bounds[4] = -r[2]; - this->Bounds[5] = +r[2]; + this->Bounds[0] = -this->FadeDistance; + this->Bounds[1] = +this->FadeDistance; + this->Bounds[2] = -1e-4; + this->Bounds[3] = +1e-4; + this->Bounds[4] = -this->FadeDistance; + this->Bounds[5] = +this->FadeDistance; return this->Bounds; } diff --git a/vtkext/private/module/vtkF3DOpenGLGridMapper.h b/vtkext/private/module/vtkF3DOpenGLGridMapper.h index 5eeaadf2a6..a8aee59fc8 100644 --- a/vtkext/private/module/vtkF3DOpenGLGridMapper.h +++ b/vtkext/private/module/vtkF3DOpenGLGridMapper.h @@ -38,9 +38,14 @@ class vtkF3DOpenGLGridMapper : public vtkOpenGLPolyDataMapper vtkSetMacro(Subdivisions, int); /** - * Set the up vector index (X, Y, Z axis respectively). + * Set the color (RGBA) of the first axes */ - vtkSetClampMacro(UpIndex, int, 0, 2); + vtkSetVector4Macro(Axis1Color, float); + + /** + * Set the color (RGBA) of the second axes + */ + vtkSetVector4Macro(Axis2Color, float); using vtkOpenGLPolyDataMapper::GetBounds; double* GetBounds() override; @@ -68,7 +73,8 @@ class vtkF3DOpenGLGridMapper : public vtkOpenGLPolyDataMapper double FadeDistance = 10.0; double UnitSquare = 1.0; int Subdivisions = 10; - int UpIndex = 1; + float Axis1Color[4] = { 0.0, 0.0, 0.0, 1.0 }; + float Axis2Color[4] = { 0.0, 0.0, 0.0, 1.0 }; }; #endif diff --git a/vtkext/private/module/vtkF3DRenderer.cxx b/vtkext/private/module/vtkF3DRenderer.cxx index 3ce1bdff52..56c0a1eec7 100644 --- a/vtkext/private/module/vtkF3DRenderer.cxx +++ b/vtkext/private/module/vtkF3DRenderer.cxx @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -23,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -31,6 +33,8 @@ #include #include #include +#include +#include #include #include #include @@ -39,6 +43,7 @@ #include #include #include +#include #include #include #include @@ -229,6 +234,9 @@ void vtkF3DRenderer::Initialize(const std::string& up) // Importer rely on the Environment being set, so this is needed in the initialization const std::regex re("([-+]?)([XYZ])", std::regex_constants::icase); + const std::regex re2("([+-]?([0-9]+([.][0-9]*)?|[.][0-9]+))," + "([+-]?([0-9]+([.][0-9]*)?|[.][0-9]+))," + "([+-]?([0-9]+([.][0-9]*)?|[.][0-9]+))"); std::smatch match; if (std::regex_match(up, match, re)) { @@ -236,13 +244,76 @@ void vtkF3DRenderer::Initialize(const std::string& up) const int index = std::toupper(match[2].str()[0]) - 'X'; assert(index >= 0 && index < 3); - this->UpIndex = index; + std::array up = { 0, 0, 0 }; + up[index] = sign; - std::fill(this->UpVector, this->UpVector + 3, 0); - this->UpVector[this->UpIndex] = sign; + std::array right = { 0, 0, 0 }; + right[index == 0 ? 1 : 0] = 1.0; - std::fill(this->RightVector, this->RightVector + 3, 0); - this->RightVector[this->UpIndex == 0 ? 1 : 0] = 1.0; + this->InitializeEnvironment(up, right); + } + else if (std::regex_match(up, match, re2)) + { + const std::array up = { + ::atof(match[1].str().c_str()), // + ::atof(match[4].str().c_str()), // + ::atof(match[7].str().c_str()), // + }; + const std::array right = { 1, 0, 0 }; + + this->InitializeEnvironment(up, right); + } + else + { + F3DLog::Print(F3DLog::Severity::Warning, up + " is not a valid up direction"); + } +} + +//---------------------------------------------------------------------------- +void vtkF3DRenderer::InitializeEnvironment( + const std::array& upDir, const std::array& rightDir) +{ + const auto isEqual = [](const std::array& u, const std::array& v) + { + constexpr double e = 1e-8; + return ::abs(u[0] - v[0]) < e && ::abs(u[1] - v[1]) < e && ::abs(u[2] - v[2]) < e; + }; + + /* if `up` is `(0,0,0)` make it `(0,1,0)` */ + std::array up = upDir; + if (isEqual(up, { 0, 0, 0 })) + { + up[1] = 1.0; + } + vtkMath::Normalize(up.data()); + + /* make sure `right` is not `(0,0,0)` or the same as `up` */ + std::array right = rightDir; + for (size_t i = 0; (isEqual(right, { 0, 0, 0 }) || isEqual(right, up)) && i < 3; ++i) + { + right[0] = 0; + right[1] = 0; + right[2] = 0; + right[i] = 1; + } + vtkMath::Normalize(right.data()); + + /* make `front` orthogonal */ + std::array front; + vtkMath::Cross(right.data(), up.data(), front.data()); + vtkMath::Normalize(front.data()); + + /* ensure `right` is orthogonal */ + vtkMath::Cross(up.data(), front.data(), right.data()); + vtkMath::Normalize(right.data()); + + { + this->UpVector[0] = up[0]; + this->UpVector[1] = up[1]; + this->UpVector[2] = up[2]; + this->RightVector[0] = right[0]; + this->RightVector[1] = right[1]; + this->RightVector[2] = right[2]; double pos[3]; vtkMath::Cross(this->UpVector, this->RightVector, pos); @@ -254,8 +325,6 @@ void vtkF3DRenderer::Initialize(const std::string& up) cam->SetViewUp(this->UpVector); // skybox orientation - double front[3]; - vtkMath::Cross(this->RightVector, this->UpVector, front); this->SkyboxActor->SetFloorPlane(this->UpVector[0], this->UpVector[1], this->UpVector[2], 0.0); this->SkyboxActor->SetFloorRight(front[0], front[1], front[2]); @@ -263,10 +332,6 @@ void vtkF3DRenderer::Initialize(const std::string& up) this->SetEnvironmentUp(this->UpVector); this->SetEnvironmentRight(this->RightVector); } - else - { - F3DLog::Print(F3DLog::Severity::Warning, up + " is not a valid up direction"); - } } //---------------------------------------------------------------------------- @@ -507,15 +572,35 @@ void vtkF3DRenderer::ShowGrid(bool show) //---------------------------------------------------------------------------- void vtkF3DRenderer::ConfigureGridUsingCurrentActors() { + double* up = this->GetEnvironmentUp(); + double* right = this->GetEnvironmentRight(); + double front[3]; + vtkMath::Cross(right, up, front); + + vtkNew upMatrix; + { + const double m[16] = { + right[0], right[1], right[2], 0, // + up[0], up[1], up[2], 0, // + front[0], front[1], front[2], 0, // + 0, 0, 0, 1, // + }; + upMatrix->DeepCopy(m); + } + vtkNew upMatrixInv; + upMatrixInv->DeepCopy(upMatrix); + upMatrixInv->Invert(); + vtkNew upTransformInv; + upTransformInv->SetMatrix(upMatrixInv); + vtkNew upTransform; + upTransform->SetMatrix(upMatrix); + // Configure grid using visible prop bounds and actors // Also initialize GridInfo bool show = this->GridVisible; if (show) { - double bounds[6]; - this->ComputeVisiblePropBounds(bounds); - - vtkBoundingBox bbox(bounds); + const vtkBoundingBox bbox = this->ComputeVisiblePropOrientedBounds(upMatrix); if (!bbox.IsValid()) { @@ -530,26 +615,31 @@ void vtkF3DRenderer::ConfigureGridUsingCurrentActors() tmpUnitSquare = pow(10.0, round(log10(diag * 0.1))); } - double gridPos[3] = { 0, 0, 0 }; + double center[3]; + bbox.GetCenter(center); + + double gridPos[3]; + upTransformInv->TransformPoint(center, gridPos); + + double downShift = 0; if (this->GridAbsolute) { - for (int i = 0; i < 3; i++) - { - gridPos[i] = this->UpVector[i] ? 0 : 0.5 * (bounds[2 * i] + bounds[2 * i + 1]); - } + double origin[3] = { 0, 0, 0 }; + downShift += vtkPlane::DistanceToPlane(center, up, origin); } else { - for (int i = 0; i < 3; i++) - { - // a small margin is added to the size to avoid z-fighting if large translucent - // triangles are exactly aligned with the grid bounds - constexpr double margin = 1.0001; - double size = margin * (bounds[2 * i + 1] - bounds[2 * i]); - gridPos[i] = 0.5 * (bounds[2 * i] + bounds[2 * i + 1] - this->UpVector[i] * size); - } + // a small margin is added to the size to avoid z-fighting if large translucent + // triangles are exactly aligned with the grid bounds + constexpr double margin = 0.0001; + downShift += bbox.GetLength(1) / 2 + margin; } + double delta[3]; + this->GetEnvironmentUp(delta); + vtkMath::MultiplyScalar(delta, downShift); + vtkMath::Subtract(gridPos, delta, gridPos); + std::stringstream stream; stream << "Using grid unit square size = " << tmpUnitSquare << "\n" << "Grid origin set to [" << gridPos[0] << ", " << gridPos[1] << ", " << gridPos[2] @@ -560,11 +650,12 @@ void vtkF3DRenderer::ConfigureGridUsingCurrentActors() gridMapper->SetFadeDistance(diag); gridMapper->SetUnitSquare(tmpUnitSquare); gridMapper->SetSubdivisions(this->GridSubdivisions); - gridMapper->SetUpIndex(this->UpIndex); if (this->GridAbsolute) - gridMapper->SetOriginOffset(-gridPos[0], -gridPos[1], -gridPos[2]); - + gridMapper->SetOriginOffset(-center[0], -center[1], -center[2]); + this->GridActor->SetOrientation(upTransformInv->GetOrientation()); this->GridActor->GetProperty()->SetColor(this->GridColor); + gridMapper->SetAxis1Color(::abs(right[0]), ::abs(right[1]), ::abs(right[2]), 1); + gridMapper->SetAxis2Color(::abs(front[0]), ::abs(front[1]), ::abs(front[2]), 1); this->GridActor->ForceTranslucentOn(); this->GridActor->SetPosition(gridPos); this->GridActor->SetMapper(gridMapper); @@ -1560,6 +1651,108 @@ void vtkF3DRenderer::ResetCameraClippingRange() this->GridActor->SetUseBounds(gridUseBounds); } +vtkBoundingBox vtkF3DRenderer::ComputeVisiblePropOrientedBounds(const vtkMatrix4x4* matrix) +{ + const auto isMatrixAxisAligned = [](const vtkMatrix4x4* m, const double tol = 1e-8) + { + for (size_t i = 0; i < 3; ++i) + { + size_t nonzerosI = 0; + size_t nonzerosJ = 0; + for (size_t j = 0; j < 3; ++j) + { + if (::abs(m->Element[i][j]) > tol) + nonzerosI++; + if (::abs(m->Element[j][i]) > tol) + nonzerosJ++; + } + if (nonzerosI > 1 || nonzerosJ > 1) + return false; + } + return true; + }; + + /* Use `PokeMatrix` around the call to `GetBounds()` to extend box. + * Only gives the thightest bounds if the transformation is axis-aligned. */ + const auto extendBoxAxisAligned = [&](vtkProp3D* prop3d, vtkBoundingBox& box) + { + vtkNew tmpMatrix; + vtkMatrix4x4::Multiply4x4(matrix, prop3d->GetMatrix(), tmpMatrix); + prop3d->PokeMatrix(tmpMatrix); + + box.AddBounds(prop3d->GetBounds()); + + prop3d->PokeMatrix(NULL); + }; + + /* Use custom logic to extend box. + * Should give the tightest bounds even when non-axis-aligned */ + const auto extendBoxArbitrary = [&](vtkProp3D* prop3d, vtkBoundingBox& box) + { + vtkActor* actor = dynamic_cast(prop3d); + if (actor) + { + vtkPolyDataMapper* polyMapper = dynamic_cast(actor->GetMapper()); + if (polyMapper) + { + vtkNew t; + t->Concatenate(*matrix->Element); + t->Concatenate(actor->GetMatrix()); + vtkPolyData* polydata = polyMapper->GetInput(); + if (polydata) + { + for (vtkIdType i = 0; i < polydata->GetNumberOfPoints(); ++i) + { + box.AddPoint(t->TransformPoint(polydata->GetPoint(i))); + } + return true; + } + } + } + return false; + }; + + const bool isAxisAligned = isMatrixAxisAligned(matrix); + vtkBoundingBox box; + + /* use `ComputeVisiblePropBounds()`'s logic to iterate `vtkProp3D`s contributing to the bounds */ + { + vtkProp* prop; + vtkCollectionSimpleIterator pit; + for (this->Props->InitTraversal(pit); (prop = this->Props->GetNextProp(pit));) + { + if (prop->GetVisibility() && prop->GetUseBounds()) + { + const double* bounds = prop->GetBounds(); + if (bounds != nullptr && vtkMath::AreBoundsInitialized(bounds)) + { + vtkProp3D* prop3d = dynamic_cast(prop); + if (prop3d) + { + if (isAxisAligned) + { + extendBoxAxisAligned(prop3d, box); + } + else + { + if (!extendBoxArbitrary(prop3d, box)) + { + const std::string repr = std::string(prop3d->GetClassName()); + F3DLog::Print(F3DLog::Severity::Warning, + "Could not properly account for " + repr + + " in non-axis-aligned bounds computation"); + extendBoxAxisAligned(prop3d, box); + } + } + } + } + } + } + } + + return box; +} + //---------------------------------------------------------------------------- int vtkF3DRenderer::UpdateLights() { diff --git a/vtkext/private/module/vtkF3DRenderer.h b/vtkext/private/module/vtkF3DRenderer.h index 58cf3217db..53a52bc651 100644 --- a/vtkext/private/module/vtkF3DRenderer.h +++ b/vtkext/private/module/vtkF3DRenderer.h @@ -11,6 +11,7 @@ #ifndef vtkF3DRenderer_h #define vtkF3DRenderer_h +#include #include #include @@ -133,6 +134,19 @@ class vtkF3DRenderer : public vtkOpenGLRenderer */ virtual void Initialize(const std::string& up); + /** + * Initialize the environment orientation (camera, skybox, axes). + * The `up` direction will be normalized. + * The `right` direction will be normalized and may be adjusted to ensure it is orthogonal. + */ + virtual void InitializeEnvironment( + const std::array& upDir, const std::array& rightDir); + + /** + * Compute bounds of visible props as transformed by given matrix. + */ + vtkBoundingBox ComputeVisiblePropOrientedBounds(const vtkMatrix4x4*); + /** * Get the OpenGL skybox */ @@ -295,7 +309,6 @@ class vtkF3DRenderer : public vtkOpenGLRenderer bool InvertZoom = false; int RaytracingSamples = 0; - int UpIndex = 1; double UpVector[3] = { 0.0, 1.0, 0.0 }; double RightVector[3] = { 1.0, 0.0, 0.0 }; double CircleOfConfusionRadius = 20.0; From a02b01a07c87cd4b3265a4c3856a97cecd5f9579 Mon Sep 17 00:00:00 2001 From: snoyer Date: Sun, 14 Jul 2024 13:59:23 +0400 Subject: [PATCH 02/12] add test --- application/testing/CMakeLists.txt | 1 + testing/baselines/TestGridUp123.png | 3 +++ 2 files changed, 4 insertions(+) create mode 100644 testing/baselines/TestGridUp123.png diff --git a/application/testing/CMakeLists.txt b/application/testing/CMakeLists.txt index 5b768646c2..aad5743ef3 100644 --- a/application/testing/CMakeLists.txt +++ b/application/testing/CMakeLists.txt @@ -131,6 +131,7 @@ f3d_test(NAME TestNRRD DATA beach.nrrd ARGS -s DEFAULT_LIGHTS) f3d_test(NAME TestGridX DATA suzanne.ply ARGS -g --up=+X DEFAULT_LIGHTS) f3d_test(NAME TestGridY DATA suzanne.ply ARGS -g --up=+Y DEFAULT_LIGHTS) f3d_test(NAME TestGridZ DATA suzanne.ply ARGS -g --up=+Z DEFAULT_LIGHTS) +f3d_test(NAME TestGridUp123 DATA suzanne.ply ARGS -g --up=1,2,3 DEFAULT_LIGHTS) f3d_test(NAME TestGridOptions DATA suzanne.ply ARGS -g --camera-elevation-angle=45 --grid-unit=2 --grid-subdivisions=3 DEFAULT_LIGHTS) f3d_test(NAME TestGridAbsolute DATA f3d.vtp ARGS -g --up=-Y --camera-direction=-.5,+1,+1 --grid-absolute DEFAULT_LIGHTS) f3d_test(NAME TestGridClipping DATA offset-flat-box.glb ARGS -g --grid-absolute --camera-position=70,120,350 DEFAULT_LIGHTS) diff --git a/testing/baselines/TestGridUp123.png b/testing/baselines/TestGridUp123.png new file mode 100644 index 0000000000..1e30e3b0ba --- /dev/null +++ b/testing/baselines/TestGridUp123.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bac662d04fc7d743989c9209bc9787d90ab8cc6062b0734ed481905155a85450 +size 22389 From f05933703ca8fbc1c6e9fc3e546911bdb7976c87 Mon Sep 17 00:00:00 2001 From: snoyer Date: Sun, 14 Jul 2024 14:15:06 +0400 Subject: [PATCH 03/12] fix shadowed parameter --- vtkext/private/module/vtkF3DRenderer.cxx | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/vtkext/private/module/vtkF3DRenderer.cxx b/vtkext/private/module/vtkF3DRenderer.cxx index 56c0a1eec7..a3fc8366ad 100644 --- a/vtkext/private/module/vtkF3DRenderer.cxx +++ b/vtkext/private/module/vtkF3DRenderer.cxx @@ -244,24 +244,24 @@ void vtkF3DRenderer::Initialize(const std::string& up) const int index = std::toupper(match[2].str()[0]) - 'X'; assert(index >= 0 && index < 3); - std::array up = { 0, 0, 0 }; - up[index] = sign; + std::array upDir = { 0, 0, 0 }; + upDir[index] = sign; - std::array right = { 0, 0, 0 }; - right[index == 0 ? 1 : 0] = 1.0; + std::array rightDir = { 0, 0, 0 }; + rightDir[index == 0 ? 1 : 0] = 1.0; - this->InitializeEnvironment(up, right); + this->InitializeEnvironment(upDir, rightDir); } else if (std::regex_match(up, match, re2)) { - const std::array up = { + const std::array upDir = { ::atof(match[1].str().c_str()), // ::atof(match[4].str().c_str()), // ::atof(match[7].str().c_str()), // }; - const std::array right = { 1, 0, 0 }; + const std::array rightDir = { 1, 0, 0 }; - this->InitializeEnvironment(up, right); + this->InitializeEnvironment(upDir, rightDir); } else { From 20720fd90d2f1b234abda6a45255aa35954d49a1 Mon Sep 17 00:00:00 2001 From: snoyer Date: Sun, 14 Jul 2024 14:44:59 +0400 Subject: [PATCH 04/12] add tests for coverage --- application/testing/CMakeLists.txt | 2 ++ testing/baselines/TestGridUp000.png | 3 +++ testing/baselines/TestGridUp100.png | 3 +++ 3 files changed, 8 insertions(+) create mode 100644 testing/baselines/TestGridUp000.png create mode 100644 testing/baselines/TestGridUp100.png diff --git a/application/testing/CMakeLists.txt b/application/testing/CMakeLists.txt index aad5743ef3..a7fb7c14e2 100644 --- a/application/testing/CMakeLists.txt +++ b/application/testing/CMakeLists.txt @@ -132,6 +132,8 @@ f3d_test(NAME TestGridX DATA suzanne.ply ARGS -g --up=+X DEFAULT_LIGHTS) f3d_test(NAME TestGridY DATA suzanne.ply ARGS -g --up=+Y DEFAULT_LIGHTS) f3d_test(NAME TestGridZ DATA suzanne.ply ARGS -g --up=+Z DEFAULT_LIGHTS) f3d_test(NAME TestGridUp123 DATA suzanne.ply ARGS -g --up=1,2,3 DEFAULT_LIGHTS) +f3d_test(NAME TestGridUp100 DATA suzanne.ply ARGS -g --up=1,0,0 DEFAULT_LIGHTS) +f3d_test(NAME TestGridUp000 DATA suzanne.ply ARGS -g --up=0,0,0 DEFAULT_LIGHTS) f3d_test(NAME TestGridOptions DATA suzanne.ply ARGS -g --camera-elevation-angle=45 --grid-unit=2 --grid-subdivisions=3 DEFAULT_LIGHTS) f3d_test(NAME TestGridAbsolute DATA f3d.vtp ARGS -g --up=-Y --camera-direction=-.5,+1,+1 --grid-absolute DEFAULT_LIGHTS) f3d_test(NAME TestGridClipping DATA offset-flat-box.glb ARGS -g --grid-absolute --camera-position=70,120,350 DEFAULT_LIGHTS) diff --git a/testing/baselines/TestGridUp000.png b/testing/baselines/TestGridUp000.png new file mode 100644 index 0000000000..2478e92502 --- /dev/null +++ b/testing/baselines/TestGridUp000.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:317358f35dd1d55e215c16c8459ae486bc4aa2cd95a830ea7414039f84ff256b +size 31140 diff --git a/testing/baselines/TestGridUp100.png b/testing/baselines/TestGridUp100.png new file mode 100644 index 0000000000..c0133bf1d6 --- /dev/null +++ b/testing/baselines/TestGridUp100.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ec32b9deb15aab8273b719daa51545c8a1af611e805261ce2b268e0d2d009d5c +size 24152 From 599195aeed42634bc64a4dea34884e1ee7d3b1fc Mon Sep 17 00:00:00 2001 From: snoyer Date: Sun, 14 Jul 2024 15:03:16 +0400 Subject: [PATCH 05/12] use `nullptr` --- vtkext/private/module/vtkF3DRenderer.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vtkext/private/module/vtkF3DRenderer.cxx b/vtkext/private/module/vtkF3DRenderer.cxx index a3fc8366ad..50cf1386d7 100644 --- a/vtkext/private/module/vtkF3DRenderer.cxx +++ b/vtkext/private/module/vtkF3DRenderer.cxx @@ -1682,7 +1682,7 @@ vtkBoundingBox vtkF3DRenderer::ComputeVisiblePropOrientedBounds(const vtkMatrix4 box.AddBounds(prop3d->GetBounds()); - prop3d->PokeMatrix(NULL); + prop3d->PokeMatrix(nullptr); }; /* Use custom logic to extend box. From 682961e07cc6bc3a9c8433379fb8b850161da976 Mon Sep 17 00:00:00 2001 From: snoyer Date: Mon, 15 Jul 2024 19:09:08 +0400 Subject: [PATCH 06/12] fix vector comparison --- vtkext/private/module/vtkF3DRenderer.cxx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/vtkext/private/module/vtkF3DRenderer.cxx b/vtkext/private/module/vtkF3DRenderer.cxx index 50cf1386d7..ff38233b49 100644 --- a/vtkext/private/module/vtkF3DRenderer.cxx +++ b/vtkext/private/module/vtkF3DRenderer.cxx @@ -287,16 +287,17 @@ void vtkF3DRenderer::InitializeEnvironment( } vtkMath::Normalize(up.data()); - /* make sure `right` is not `(0,0,0)` or the same as `up` */ + /* make sure `right` is not `(0,0,0)` or colinear with `up` */ std::array right = rightDir; - for (size_t i = 0; (isEqual(right, { 0, 0, 0 }) || isEqual(right, up)) && i < 3; ++i) + vtkMath::Normalize(right.data()); + for (size_t i = 0; + (isEqual(right, { 0, 0, 0 }) || ::abs(vtkMath::Dot(right, up)) > 0.999) && i < 3; ++i) { right[0] = 0; right[1] = 0; right[2] = 0; right[i] = 1; } - vtkMath::Normalize(right.data()); /* make `front` orthogonal */ std::array front; From e0c51256fad6dc831e2862ba4cbf95a0120fed81 Mon Sep 17 00:00:00 2001 From: snoyer Date: Mon, 15 Jul 2024 19:09:33 +0400 Subject: [PATCH 07/12] optimizations --- vtkext/private/module/vtkF3DRenderer.cxx | 104 ++++++++++++----------- 1 file changed, 53 insertions(+), 51 deletions(-) diff --git a/vtkext/private/module/vtkF3DRenderer.cxx b/vtkext/private/module/vtkF3DRenderer.cxx index ff38233b49..644bb46a36 100644 --- a/vtkext/private/module/vtkF3DRenderer.cxx +++ b/vtkext/private/module/vtkF3DRenderer.cxx @@ -573,34 +573,30 @@ void vtkF3DRenderer::ShowGrid(bool show) //---------------------------------------------------------------------------- void vtkF3DRenderer::ConfigureGridUsingCurrentActors() { - double* up = this->GetEnvironmentUp(); - double* right = this->GetEnvironmentRight(); - double front[3]; - vtkMath::Cross(right, up, front); - - vtkNew upMatrix; - { - const double m[16] = { - right[0], right[1], right[2], 0, // - up[0], up[1], up[2], 0, // - front[0], front[1], front[2], 0, // - 0, 0, 0, 1, // - }; - upMatrix->DeepCopy(m); - } - vtkNew upMatrixInv; - upMatrixInv->DeepCopy(upMatrix); - upMatrixInv->Invert(); - vtkNew upTransformInv; - upTransformInv->SetMatrix(upMatrixInv); - vtkNew upTransform; - upTransform->SetMatrix(upMatrix); - // Configure grid using visible prop bounds and actors // Also initialize GridInfo bool show = this->GridVisible; if (show) { + double* up = this->GetEnvironmentUp(); + double* right = this->GetEnvironmentRight(); + double front[3]; + vtkMath::Cross(right, up, front); + + vtkNew upMatrix; + { + const double m[16] = { + right[0], right[1], right[2], 0, // + up[0], up[1], up[2], 0, // + front[0], front[1], front[2], 0, // + 0, 0, 0, 1, // + }; + upMatrix->DeepCopy(m); + } + vtkNew upMatrixInv; + upMatrixInv->DeepCopy(upMatrix); + upMatrixInv->Transpose(); // matrix is orthonormal, no need to use `Invert()` + const vtkBoundingBox bbox = this->ComputeVisiblePropOrientedBounds(upMatrix); if (!bbox.IsValid()) @@ -619,8 +615,7 @@ void vtkF3DRenderer::ConfigureGridUsingCurrentActors() double center[3]; bbox.GetCenter(center); - double gridPos[3]; - upTransformInv->TransformPoint(center, gridPos); + double* gridPos = upMatrixInv->MultiplyDoublePoint(center); double downShift = 0; if (this->GridAbsolute) @@ -651,14 +646,20 @@ void vtkF3DRenderer::ConfigureGridUsingCurrentActors() gridMapper->SetFadeDistance(diag); gridMapper->SetUnitSquare(tmpUnitSquare); gridMapper->SetSubdivisions(this->GridSubdivisions); + if (this->GridAbsolute) gridMapper->SetOriginOffset(-center[0], -center[1], -center[2]); - this->GridActor->SetOrientation(upTransformInv->GetOrientation()); + + double orientation[3]; + vtkTransform::GetOrientation(orientation, upMatrixInv); + this->GridActor->SetOrientation(orientation); + this->GridActor->SetPosition(gridPos); + this->GridActor->GetProperty()->SetColor(this->GridColor); gridMapper->SetAxis1Color(::abs(right[0]), ::abs(right[1]), ::abs(right[2]), 1); gridMapper->SetAxis2Color(::abs(front[0]), ::abs(front[1]), ::abs(front[2]), 1); + this->GridActor->ForceTranslucentOn(); - this->GridActor->SetPosition(gridPos); this->GridActor->SetMapper(gridMapper); this->GridActor->UseBoundsOff(); this->GridActor->PickableOff(); @@ -1696,15 +1697,18 @@ vtkBoundingBox vtkF3DRenderer::ComputeVisiblePropOrientedBounds(const vtkMatrix4 vtkPolyDataMapper* polyMapper = dynamic_cast(actor->GetMapper()); if (polyMapper) { - vtkNew t; - t->Concatenate(*matrix->Element); - t->Concatenate(actor->GetMatrix()); + vtkNew tmpMatrix; + vtkMatrix4x4::Multiply4x4(matrix, actor->GetMatrix(), tmpMatrix); vtkPolyData* polydata = polyMapper->GetInput(); if (polydata) { + double p[4] = { 0, 0, 0, 1 }; + double q[4]; for (vtkIdType i = 0; i < polydata->GetNumberOfPoints(); ++i) { - box.AddPoint(t->TransformPoint(polydata->GetPoint(i))); + polydata->GetPoint(i, p); + tmpMatrix->MultiplyPoint(p, q); + box.AddPoint(q); } return true; } @@ -1717,34 +1721,32 @@ vtkBoundingBox vtkF3DRenderer::ComputeVisiblePropOrientedBounds(const vtkMatrix4 vtkBoundingBox box; /* use `ComputeVisiblePropBounds()`'s logic to iterate `vtkProp3D`s contributing to the bounds */ + vtkProp* prop; + vtkCollectionSimpleIterator pit; + for (this->Props->InitTraversal(pit); (prop = this->Props->GetNextProp(pit));) { - vtkProp* prop; - vtkCollectionSimpleIterator pit; - for (this->Props->InitTraversal(pit); (prop = this->Props->GetNextProp(pit));) + if (prop->GetVisibility() && prop->GetUseBounds()) { - if (prop->GetVisibility() && prop->GetUseBounds()) + const double* bounds = prop->GetBounds(); + if (bounds != nullptr && vtkMath::AreBoundsInitialized(bounds)) { - const double* bounds = prop->GetBounds(); - if (bounds != nullptr && vtkMath::AreBoundsInitialized(bounds)) + vtkProp3D* prop3d = dynamic_cast(prop); + if (prop3d) { - vtkProp3D* prop3d = dynamic_cast(prop); - if (prop3d) + if (isAxisAligned) { - if (isAxisAligned) + extendBoxAxisAligned(prop3d, box); + } + else + { + if (!extendBoxArbitrary(prop3d, box)) { + const std::string repr = std::string(prop3d->GetClassName()); + F3DLog::Print(F3DLog::Severity::Warning, + "Could not properly account for " + repr + + " in non-axis-aligned bounds computation"); extendBoxAxisAligned(prop3d, box); } - else - { - if (!extendBoxArbitrary(prop3d, box)) - { - const std::string repr = std::string(prop3d->GetClassName()); - F3DLog::Print(F3DLog::Severity::Warning, - "Could not properly account for " + repr + - " in non-axis-aligned bounds computation"); - extendBoxAxisAligned(prop3d, box); - } - } } } } From 218ca3703a203358bf56c30b1bfb20f234f90736 Mon Sep 17 00:00:00 2001 From: snoyer Date: Tue, 16 Jul 2024 14:47:59 +0400 Subject: [PATCH 08/12] remove unused param --- vtkext/private/module/vtkF3DRenderer.cxx | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/vtkext/private/module/vtkF3DRenderer.cxx b/vtkext/private/module/vtkF3DRenderer.cxx index 644bb46a36..8360847007 100644 --- a/vtkext/private/module/vtkF3DRenderer.cxx +++ b/vtkext/private/module/vtkF3DRenderer.cxx @@ -273,15 +273,15 @@ void vtkF3DRenderer::Initialize(const std::string& up) void vtkF3DRenderer::InitializeEnvironment( const std::array& upDir, const std::array& rightDir) { - const auto isEqual = [](const std::array& u, const std::array& v) + const auto isNullVector = [](const std::array& v) { constexpr double e = 1e-8; - return ::abs(u[0] - v[0]) < e && ::abs(u[1] - v[1]) < e && ::abs(u[2] - v[2]) < e; + return ::abs(v[0]) < e && ::abs(v[1]) < e && ::abs(v[2]) < e; }; /* if `up` is `(0,0,0)` make it `(0,1,0)` */ std::array up = upDir; - if (isEqual(up, { 0, 0, 0 })) + if (isNullVector(up)) { up[1] = 1.0; } @@ -290,8 +290,7 @@ void vtkF3DRenderer::InitializeEnvironment( /* make sure `right` is not `(0,0,0)` or colinear with `up` */ std::array right = rightDir; vtkMath::Normalize(right.data()); - for (size_t i = 0; - (isEqual(right, { 0, 0, 0 }) || ::abs(vtkMath::Dot(right, up)) > 0.999) && i < 3; ++i) + for (size_t i = 0; (isNullVector(right) || ::abs(vtkMath::Dot(right, up)) > 0.999) && i < 3; ++i) { right[0] = 0; right[1] = 0; From 252c0cf4a729eb369347d02d3ff2fab3772e6a4c Mon Sep 17 00:00:00 2001 From: snoyer Date: Tue, 16 Jul 2024 14:48:59 +0400 Subject: [PATCH 09/12] make vector long enough --- vtkext/private/module/vtkF3DRenderer.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vtkext/private/module/vtkF3DRenderer.cxx b/vtkext/private/module/vtkF3DRenderer.cxx index 8360847007..76a276f32e 100644 --- a/vtkext/private/module/vtkF3DRenderer.cxx +++ b/vtkext/private/module/vtkF3DRenderer.cxx @@ -611,7 +611,7 @@ void vtkF3DRenderer::ConfigureGridUsingCurrentActors() tmpUnitSquare = pow(10.0, round(log10(diag * 0.1))); } - double center[3]; + double center[4] = { 0, 0, 0, 1 }; bbox.GetCenter(center); double* gridPos = upMatrixInv->MultiplyDoublePoint(center); From c2e5b470f58c2e78ea447d627f4cc056b9f288c0 Mon Sep 17 00:00:00 2001 From: snoyer Date: Wed, 17 Jul 2024 17:24:44 +0400 Subject: [PATCH 10/12] use VTK casts --- vtkext/private/module/vtkF3DRenderer.cxx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/vtkext/private/module/vtkF3DRenderer.cxx b/vtkext/private/module/vtkF3DRenderer.cxx index 60e6f95622..0cdba427cd 100644 --- a/vtkext/private/module/vtkF3DRenderer.cxx +++ b/vtkext/private/module/vtkF3DRenderer.cxx @@ -1678,10 +1678,10 @@ vtkBoundingBox vtkF3DRenderer::ComputeVisiblePropOrientedBounds(const vtkMatrix4 * Should give the tightest bounds even when non-axis-aligned */ const auto extendBoxArbitrary = [&](vtkProp3D* prop3d, vtkBoundingBox& box) { - vtkActor* actor = dynamic_cast(prop3d); + vtkActor* actor = vtkActor::SafeDownCast(prop3d); if (actor) { - vtkPolyDataMapper* polyMapper = dynamic_cast(actor->GetMapper()); + vtkPolyDataMapper* polyMapper = vtkPolyDataMapper::SafeDownCast(actor->GetMapper()); if (polyMapper) { vtkNew tmpMatrix; @@ -1717,7 +1717,7 @@ vtkBoundingBox vtkF3DRenderer::ComputeVisiblePropOrientedBounds(const vtkMatrix4 const double* bounds = prop->GetBounds(); if (bounds != nullptr && vtkMath::AreBoundsInitialized(bounds)) { - vtkProp3D* prop3d = dynamic_cast(prop); + vtkProp3D* prop3d = vtkProp3D::SafeDownCast(prop); if (prop3d) { if (isAxisAligned) From 28643e6b30b4a1c66ec43240559b45206a52b735 Mon Sep 17 00:00:00 2001 From: snoyer Date: Sun, 18 Aug 2024 14:26:11 +0100 Subject: [PATCH 11/12] use `std::stod` --- vtkext/private/module/vtkF3DRenderer.cxx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/vtkext/private/module/vtkF3DRenderer.cxx b/vtkext/private/module/vtkF3DRenderer.cxx index 0cdba427cd..8f77d3f6f0 100644 --- a/vtkext/private/module/vtkF3DRenderer.cxx +++ b/vtkext/private/module/vtkF3DRenderer.cxx @@ -252,9 +252,9 @@ void vtkF3DRenderer::Initialize(const std::string& up) else if (std::regex_match(up, match, re2)) { const std::array upDir = { - ::atof(match[1].str().c_str()), // - ::atof(match[4].str().c_str()), // - ::atof(match[7].str().c_str()), // + std::stod(match[1].str()), // + std::stod(match[4].str()), // + std::stod(match[7].str()), // }; const std::array rightDir = { 1, 0, 0 }; From 3804fc51aa6709ce449bf9387852e10fc7d825d7 Mon Sep 17 00:00:00 2001 From: snoyer Date: Mon, 19 Aug 2024 13:56:54 +0100 Subject: [PATCH 12/12] remove unnecessary scopes --- vtkext/private/module/vtkF3DRenderer.cxx | 56 +++++++++++------------- 1 file changed, 26 insertions(+), 30 deletions(-) diff --git a/vtkext/private/module/vtkF3DRenderer.cxx b/vtkext/private/module/vtkF3DRenderer.cxx index 8f77d3f6f0..07239d9067 100644 --- a/vtkext/private/module/vtkF3DRenderer.cxx +++ b/vtkext/private/module/vtkF3DRenderer.cxx @@ -304,31 +304,29 @@ void vtkF3DRenderer::InitializeEnvironment( vtkMath::Cross(up.data(), front.data(), right.data()); vtkMath::Normalize(right.data()); - { - this->UpVector[0] = up[0]; - this->UpVector[1] = up[1]; - this->UpVector[2] = up[2]; - this->RightVector[0] = right[0]; - this->RightVector[1] = right[1]; - this->RightVector[2] = right[2]; + this->UpVector[0] = up[0]; + this->UpVector[1] = up[1]; + this->UpVector[2] = up[2]; + this->RightVector[0] = right[0]; + this->RightVector[1] = right[1]; + this->RightVector[2] = right[2]; - double pos[3]; - vtkMath::Cross(this->UpVector, this->RightVector, pos); - vtkMath::MultiplyScalar(pos, -1.0); + double pos[3]; + vtkMath::Cross(this->UpVector, this->RightVector, pos); + vtkMath::MultiplyScalar(pos, -1.0); - vtkCamera* cam = this->GetActiveCamera(); - cam->SetFocalPoint(0.0, 0.0, 0.0); - cam->SetPosition(pos); - cam->SetViewUp(this->UpVector); + vtkCamera* cam = this->GetActiveCamera(); + cam->SetFocalPoint(0.0, 0.0, 0.0); + cam->SetPosition(pos); + cam->SetViewUp(this->UpVector); - // skybox orientation - this->SkyboxActor->SetFloorPlane(this->UpVector[0], this->UpVector[1], this->UpVector[2], 0.0); - this->SkyboxActor->SetFloorRight(front[0], front[1], front[2]); + // skybox orientation + this->SkyboxActor->SetFloorPlane(this->UpVector[0], this->UpVector[1], this->UpVector[2], 0.0); + this->SkyboxActor->SetFloorRight(front[0], front[1], front[2]); - // environment orientation - this->SetEnvironmentUp(this->UpVector); - this->SetEnvironmentRight(this->RightVector); - } + // environment orientation + this->SetEnvironmentUp(this->UpVector); + this->SetEnvironmentRight(this->RightVector); } //---------------------------------------------------------------------------- @@ -571,15 +569,13 @@ void vtkF3DRenderer::ConfigureGridUsingCurrentActors() vtkMath::Cross(right, up, front); vtkNew upMatrix; - { - const double m[16] = { - right[0], right[1], right[2], 0, // - up[0], up[1], up[2], 0, // - front[0], front[1], front[2], 0, // - 0, 0, 0, 1, // - }; - upMatrix->DeepCopy(m); - } + const double m[16] = { + right[0], right[1], right[2], 0, // + up[0], up[1], up[2], 0, // + front[0], front[1], front[2], 0, // + 0, 0, 0, 1, // + }; + upMatrix->DeepCopy(m); vtkNew upMatrixInv; upMatrixInv->DeepCopy(upMatrix); upMatrixInv->Transpose(); // matrix is orthonormal, no need to use `Invert()`