From 92478c32c0aec628372ee0f9163bee4f6d7a744d Mon Sep 17 00:00:00 2001 From: Nicholas Sharp Date: Mon, 27 Nov 2023 02:12:24 -0800 Subject: [PATCH 1/6] minor typo fix --- include/polyscope/camera_parameters.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/polyscope/camera_parameters.h b/include/polyscope/camera_parameters.h index 7fef0b25..90ac1d7b 100644 --- a/include/polyscope/camera_parameters.h +++ b/include/polyscope/camera_parameters.h @@ -32,7 +32,7 @@ class CameraIntrinsics { static CameraIntrinsics fromFoVDegVerticalAndAspect(const float& fovVertDeg, const float& aspectRatioWidthOverHeight); static CameraIntrinsics fromFoVDegHorizontalAndAspect(const float& fovHorzDeg, const float& aspectRatioWidthOverHeight); - static CameraIntrinsics fromFoVDegHorizontalAndVertical(const float& fovHorzDeg, const float& forV); + static CameraIntrinsics fromFoVDegHorizontalAndVertical(const float& fovHorzDeg, const float& fovVertDeg); // create/test 'invalid' params static CameraIntrinsics createInvalid(); From 352213ffc60e33d397c24a251ae65bd13455ffbf Mon Sep 17 00:00:00 2001 From: Nicholas Sharp Date: Tue, 28 Nov 2023 19:42:38 -0800 Subject: [PATCH 2/6] implement categorical island viz --- include/polyscope/parameterization_quantity.h | 8 ++- .../polyscope/parameterization_quantity.ipp | 53 +++++++++++++++++-- .../polyscope/render/opengl/shaders/rules.h | 1 + .../opengl/shaders/surface_mesh_shaders.h | 1 + .../surface_parameterization_quantity.h | 14 +++++ include/polyscope/types.h | 10 +++- src/color_management.cpp | 2 + src/render/managed_buffer.cpp | 2 +- src/render/mock_opengl/mock_gl_engine.cpp | 2 + src/render/opengl/gl_engine.cpp | 2 + src/render/opengl/shaders/common.cpp | 30 +++++++++++ src/render/opengl/shaders/rules.cpp | 40 ++++++++++++++ .../opengl/shaders/surface_mesh_shaders.cpp | 24 +++++++++ src/surface_parameterization_quantity.cpp | 29 ++++++++-- test/src/surface_mesh_test.cpp | 6 +++ 15 files changed, 212 insertions(+), 12 deletions(-) diff --git a/include/polyscope/parameterization_quantity.h b/include/polyscope/parameterization_quantity.h index 762e09ca..d6c18e63 100644 --- a/include/polyscope/parameterization_quantity.h +++ b/include/polyscope/parameterization_quantity.h @@ -29,9 +29,10 @@ class ParameterizationQuantity { QuantityT& quantity; - // Wrapper around the actual buffer of scalar data stored in the class. + // Wrapper around the actual buffers of data stored in the class. // Interaction with the data (updating it on CPU or GPU side, accessing it, etc) happens through this wrapper. render::ManagedBuffer coords; + render::ManagedBuffer islandLabels; // only optionally populated const ParamCoordsType coordsType; @@ -71,6 +72,8 @@ class ParameterizationQuantity { protected: // Raw storage for the data. You should only interact with this via the managed buffer above std::vector coordsData; + std::vector islandLabelsData; + bool islandLabelsPopulated = false; // === Visualization parameters @@ -82,6 +85,9 @@ class ParameterizationQuantity { PersistentValue altDarkness; PersistentValue cMap; float localRot = 0.; // for LOCAL (angular shift, in radians) + + // helpers + bool haveIslandLabels(); }; } // namespace polyscope diff --git a/include/polyscope/parameterization_quantity.ipp b/include/polyscope/parameterization_quantity.ipp index 2262904e..9cc43594 100644 --- a/include/polyscope/parameterization_quantity.ipp +++ b/include/polyscope/parameterization_quantity.ipp @@ -14,6 +14,9 @@ std::string styleName(ParamVizStyle v) { case ParamVizStyle::CHECKER: return "checker"; break; + case ParamVizStyle::CHECKER_ISLANDS: + return "checker islands"; + break; case ParamVizStyle::GRID: return "grid"; break; @@ -34,8 +37,15 @@ template ParameterizationQuantity::ParameterizationQuantity(QuantityT& quantity_, const std::vector& coords_, ParamCoordsType type_, ParamVizStyle style_) - : quantity(quantity_), coords(&quantity, quantity.uniquePrefix() + "#coords", coordsData), coordsType(type_), - coordsData(coords_), checkerSize(quantity.uniquePrefix() + "#checkerSize", 0.02), + : quantity(quantity_), + + // buffers + coords(&quantity, quantity.uniquePrefix() + "#coords", coordsData), + islandLabels(&quantity, quantity.uniquePrefix() + "#islandLabels", islandLabelsData), coordsType(type_), + coordsData(coords_), + + // options + checkerSize(quantity.uniquePrefix() + "#checkerSize", 0.02), vizStyle(quantity.uniquePrefix() + "#vizStyle", style_), checkColor1(quantity.uniquePrefix() + "#checkColor1", render::RGB_PINK), checkColor2(quantity.uniquePrefix() + "#checkColor2", glm::vec3(.976, .856, .885)), @@ -70,6 +80,17 @@ void ParameterizationQuantity::buildParameterizationUI() { if (ImGui::ColorEdit3("##colors", &checkColor2.get()[0], ImGuiColorEditFlags_NoInputs)) setCheckerColors(getCheckerColors()); break; + case ParamVizStyle::CHECKER_ISLANDS: + if (render::buildColormapSelector(cMap.get())) { + setColorMap(getColorMap()); + } + ImGui::PushItemWidth(100); + if (ImGui::DragFloat("alt darkness", &altDarkness.get(), 0.01, 0., 1.)) { + altDarkness.manuallyChanged(); + requestRedraw(); + } + ImGui::PopItemWidth(); + break; case ParamVizStyle::GRID: ImGui::SameLine(); if (ImGui::ColorEdit3("##base", &gridBackgroundColor.get()[0], ImGuiColorEditFlags_NoInputs)) @@ -105,8 +126,13 @@ void ParameterizationQuantity::buildParameterizationOptionsUI() { // Choose viz style if (ImGui::BeginMenu("Style")) { - for (ParamVizStyle s : - {ParamVizStyle::CHECKER, ParamVizStyle::GRID, ParamVizStyle::LOCAL_CHECK, ParamVizStyle::LOCAL_RAD}) { + for (ParamVizStyle s : {ParamVizStyle::CHECKER_ISLANDS, ParamVizStyle::CHECKER, ParamVizStyle::GRID, + ParamVizStyle::LOCAL_CHECK, ParamVizStyle::LOCAL_RAD}) { + + if (s == ParamVizStyle::CHECKER_ISLANDS && !haveIslandLabels()) { + // only allow CHECKER_ISLANDS if we actually have island labels + continue; + } bool selected = s == getStyle(); std::string fancyName = styleName(s); @@ -125,6 +151,9 @@ std::vector ParameterizationQuantity::addParameterizatio case ParamVizStyle::CHECKER: rules.insert(rules.end(), {"SHADE_CHECKER_VALUE2"}); break; + case ParamVizStyle::CHECKER_ISLANDS: + rules.insert(rules.end(), {"SHADE_CHECKER_CATEGORY"}); + break; case ParamVizStyle::GRID: rules.insert(rules.end(), {"SHADE_GRID_VALUE2"}); break; @@ -144,6 +173,9 @@ void ParameterizationQuantity::fillParameterizationBuffers(render::Sh switch (getStyle()) { case ParamVizStyle::CHECKER: break; + case ParamVizStyle::CHECKER_ISLANDS: + p.setTextureFromColormap("t_colormap", cMap.get()); + break; case ParamVizStyle::GRID: break; case ParamVizStyle::LOCAL_CHECK: @@ -175,6 +207,9 @@ void ParameterizationQuantity::setParameterizationUniforms(render::Sh p.setUniform("u_color1", getCheckerColors().first); p.setUniform("u_color2", getCheckerColors().second); break; + case ParamVizStyle::CHECKER_ISLANDS: + p.setUniform("u_modDarkness", getAltDarkness()); + break; case ParamVizStyle::GRID: p.setUniform("u_gridLineColor", getGridColors().first); p.setUniform("u_gridBackgroundColor", getGridColors().second); @@ -195,9 +230,19 @@ void ParameterizationQuantity::updateCoords(const V& newCoords) { coords.markHostBufferUpdated(); } +template +bool ParameterizationQuantity::haveIslandLabels() { + return islandLabelsPopulated; +} template QuantityT* ParameterizationQuantity::setStyle(ParamVizStyle newStyle) { + + if (newStyle == ParamVizStyle::CHECKER_ISLANDS && !haveIslandLabels()) { + exception("Cannot set parameterization visualization style to 'CHECKER_ISLANDS', no islands have been set"); + newStyle = ParamVizStyle::CHECKER; + } + vizStyle = newStyle; quantity.refresh(); requestRedraw(); diff --git a/include/polyscope/render/opengl/shaders/rules.h b/include/polyscope/render/opengl/shaders/rules.h index 477cdf32..1fe39694 100644 --- a/include/polyscope/render/opengl/shaders/rules.h +++ b/include/polyscope/render/opengl/shaders/rules.h @@ -25,6 +25,7 @@ extern const ShaderReplacementRule SHADE_COLORMAP_VALUE; // colormapped f extern const ShaderReplacementRule SHADE_COLORMAP_ANGULAR2; // colormapped from angle of shadeValue2 extern const ShaderReplacementRule SHADE_GRID_VALUE2; // generate a two-color grid with lines from shadeValue2 extern const ShaderReplacementRule SHADE_CHECKER_VALUE2; // generate a two-color checker from shadeValue2 +extern const ShaderReplacementRule SHADE_CHECKER_CATEGORY; // generate a checker with colors from a categorical int extern const ShaderReplacementRule SHADEVALUE_MAG_VALUE2; // generate a shadeValue from the magnitude of shadeValue2 extern const ShaderReplacementRule ISOLINE_STRIPE_VALUECOLOR; // modulate albedoColor based on shadeValue extern const ShaderReplacementRule CHECKER_VALUE2COLOR; // modulate albedoColor based on shadeValue2 diff --git a/include/polyscope/render/opengl/shaders/surface_mesh_shaders.h b/include/polyscope/render/opengl/shaders/surface_mesh_shaders.h index 2be1931e..bea3c591 100644 --- a/include/polyscope/render/opengl/shaders/surface_mesh_shaders.h +++ b/include/polyscope/render/opengl/shaders/surface_mesh_shaders.h @@ -24,6 +24,7 @@ extern const ShaderReplacementRule MESH_BACKFACE_NORMAL_FLIP; extern const ShaderReplacementRule MESH_BACKFACE_DIFFERENT; extern const ShaderReplacementRule MESH_BACKFACE_DARKEN; extern const ShaderReplacementRule MESH_PROPAGATE_VALUE; +extern const ShaderReplacementRule MESH_PROPAGATE_INT; extern const ShaderReplacementRule MESH_PROPAGATE_VALUE2; extern const ShaderReplacementRule MESH_PROPAGATE_TCOORD; extern const ShaderReplacementRule MESH_PROPAGATE_COLOR; diff --git a/include/polyscope/surface_parameterization_quantity.h b/include/polyscope/surface_parameterization_quantity.h index 195accc4..d6406a4b 100644 --- a/include/polyscope/surface_parameterization_quantity.h +++ b/include/polyscope/surface_parameterization_quantity.h @@ -29,12 +29,15 @@ class SurfaceParameterizationQuantity : public SurfaceMeshQuantity, virtual void refresh() override; virtual void buildCustomUI() override; + template + void setIslandLabels(const V& newIslandLabels); protected: std::shared_ptr program; // Helpers void createProgram(); + size_t nFaces(); // works around an incomplete def of the parent mesh virtual void fillCoordBuffers(render::ShaderProgram& p) = 0; }; @@ -74,4 +77,15 @@ class SurfaceVertexParameterizationQuantity : public SurfaceParameterizationQuan virtual void fillCoordBuffers(render::ShaderProgram& p) override; }; + +// == template implementations + +template +void SurfaceParameterizationQuantity::setIslandLabels(const V& newIslandLabels) { + validateSize(newIslandLabels, this->nFaces(), "scalar quantity " + quantity.name); + islandLabels.data = standardizeArray(newIslandLabels); + islandLabels.markHostBufferUpdated(); + islandLabelsPopulated = true; +} + } // namespace polyscope diff --git a/include/polyscope/types.h b/include/polyscope/types.h index 8a284b8c..c4be1d4d 100644 --- a/include/polyscope/types.h +++ b/include/polyscope/types.h @@ -24,8 +24,14 @@ enum class VolumeCellType { TET = 0, HEX }; enum class ImplicitRenderMode { SphereMarch, FixedStep }; enum class ImageOrigin { LowerLeft, UpperLeft }; -enum class ParamCoordsType { UNIT = 0, WORLD }; // UNIT -> [0,1], WORLD -> length-valued -enum class ParamVizStyle { CHECKER = 0, GRID, LOCAL_CHECK, LOCAL_RAD }; // TODO add "UV" with test UV map +enum class ParamCoordsType { UNIT = 0, WORLD }; // UNIT -> [0,1], WORLD -> length-valued +enum class ParamVizStyle { + CHECKER = 0, + GRID, + LOCAL_CHECK, + LOCAL_RAD, + CHECKER_ISLANDS +}; // TODO add "UV" with test UV map enum class ManagedBufferType { Float, diff --git a/src/color_management.cpp b/src/color_management.cpp index 220cd781..1fcb37d6 100644 --- a/src/color_management.cpp +++ b/src/color_management.cpp @@ -25,6 +25,8 @@ glm::vec3 unitClamp(glm::vec3 x) { return {unitClamp(x[0]), unitClamp(x[1]), uni // Used to sample colors. Samples a series of most-distant values from a range [0,1] // offset from a starting value 'start' and wrapped around. index=0 returns start // +// (We also use this logic via a duplicate implementation in some shaders) +// // Example: if start = 0, emits f(0, i) = {0, 1/2, 1/4, 3/4, 1/8, 5/8, 3/8, 7/8, ...} // if start = 0.3 emits (0.3 + f(0, i)) % 1 float getIndexedDistinctValue(float start, int index) { diff --git a/src/render/managed_buffer.cpp b/src/render/managed_buffer.cpp index ecba28e6..2a91a0d7 100644 --- a/src/render/managed_buffer.cpp +++ b/src/render/managed_buffer.cpp @@ -591,7 +591,7 @@ template<> ManagedBufferMap& ManagedBufferMap> 1; + p /= 2.0; + } + + // Apply modular offset + val = mod(val + start, 1.0); + + return clamp(val, 0.f, 1.f); +} + + // Two useful references: // - https://stackoverflow.com/questions/38938498/how-do-i-convert-gl-fragcoord-to-a-world-space-point-in-a-fragment-shader // - https://stackoverflow.com/questions/11277501/how-to-recover-view-space-position-given-view-space-depth-value-and-ndc-xy diff --git a/src/render/opengl/shaders/rules.cpp b/src/render/opengl/shaders/rules.cpp index 59481cd1..6c032340 100644 --- a/src/render/opengl/shaders/rules.cpp +++ b/src/render/opengl/shaders/rules.cpp @@ -203,6 +203,7 @@ const ShaderReplacementRule SHADE_CHECKER_VALUE2 ( uniform vec3 u_color2; )"}, {"GENERATE_SHADE_COLOR", R"( + // NOTE checker math shared with other shaders float mX = mod(shadeValue2.x, 2.0 * u_modLen) / u_modLen - 1.f; // in [-1, 1] float mY = mod(shadeValue2.y, 2.0 * u_modLen) / u_modLen - 1.f; float minD = min( min(abs(mX), 1.0 - abs(mX)), min(abs(mY), 1.0 - abs(mY))) * 2.; // rect distace from flipping sign in [0,1] @@ -224,6 +225,45 @@ const ShaderReplacementRule SHADE_CHECKER_VALUE2 ( /* textures */ {} ); +const ShaderReplacementRule SHADE_CHECKER_CATEGORY( + /* rule name */ "SHADE_CHECKER_CATEGORY", + { /* replacement sources */ + {"FRAG_DECLARATIONS", R"( + uniform float u_modLen; + uniform float u_modDarkness; + uniform sampler1D t_colormap; + + float intToDistinctReal(float start, int index); + )"}, + {"GENERATE_SHADE_COLOR", R"( + // sample the categorical color + float catVal = intToDistinctReal(0., shadeInt); + vec3 catColor = texture(t_colormap, catVal).rgb; + vec3 catColorDark = catColor * u_modDarkness; + + // NOTE checker math shared with other shaders + float mX = mod(shadeValue2.x, 2.0 * u_modLen) / u_modLen - 1.f; // in [-1, 1] + float mY = mod(shadeValue2.y, 2.0 * u_modLen) / u_modLen - 1.f; + float minD = min( min(abs(mX), 1.0 - abs(mX)), min(abs(mY), 1.0 - abs(mY))) * 2.; // rect distace from flipping sign in [0,1] + float p = 6; + float minDSmooth = pow(minD, 1. / p); + // TODO do some clever screen space derivative thing to prevent aliasing + float v = (mX * mY); // in [-1, 1], color switches at 0 + float adjV = sign(v) * minDSmooth; + float s = smoothstep(-1.f, 1.f, adjV); + vec3 albedoColor = mix(catColor, catColorDark, s); + )"} + }, + /* uniforms */ { + {"u_modLen", RenderDataType::Float}, + {"u_modDarkness", RenderDataType::Float}, + }, + /* attributes */ {}, + /* textures */ { + {"t_colormap", 1} + } +); + // input vec2 shadeValue2 // output: float shadeValue const ShaderReplacementRule SHADEVALUE_MAG_VALUE2( diff --git a/src/render/opengl/shaders/surface_mesh_shaders.cpp b/src/render/opengl/shaders/surface_mesh_shaders.cpp index 3597cfec..1a8b6216 100644 --- a/src/render/opengl/shaders/surface_mesh_shaders.cpp +++ b/src/render/opengl/shaders/surface_mesh_shaders.cpp @@ -256,6 +256,30 @@ const ShaderReplacementRule MESH_PROPAGATE_VALUE ( /* textures */ {} ); +const ShaderReplacementRule MESH_PROPAGATE_INT ( + /* rule name */ "MESH_PROPAGATE_INT", + { /* replacement sources */ + {"VERT_DECLARATIONS", R"( + in int a_int; + flat out int a_intToFrag; + )"}, + {"VERT_ASSIGNMENTS", R"( + a_intToFrag = a_int; + )"}, + {"FRAG_DECLARATIONS", R"( + flat in int a_intToFrag; + )"}, + {"GENERATE_SHADE_VALUE", R"( + int shadeInt = a_intToFrag; + )"}, + }, + /* uniforms */ {}, + /* attributes */ { + {"a_int", RenderDataType::Int}, + }, + /* textures */ {} +); + const ShaderReplacementRule MESH_PROPAGATE_HALFEDGE_VALUE ( /* rule name */ "MESH_PROPAGATE_HALFEDGE_VALUE", { /* replacement sources */ diff --git a/src/surface_parameterization_quantity.cpp b/src/surface_parameterization_quantity.cpp index bcc5d4c7..59045ad5 100644 --- a/src/surface_parameterization_quantity.cpp +++ b/src/surface_parameterization_quantity.cpp @@ -17,7 +17,14 @@ namespace polyscope { SurfaceParameterizationQuantity::SurfaceParameterizationQuantity(std::string name, SurfaceMesh& mesh_, const std::vector& coords_, ParamCoordsType type_, ParamVizStyle style_) - : SurfaceMeshQuantity(name, mesh_, true), ParameterizationQuantity(*this, coords_, type_, style_) {} + : SurfaceMeshQuantity(name, mesh_, true), ParameterizationQuantity(*this, coords_, type_, style_) { + + // sanity check, this should basically never happen, but this guards against weird edge cases such + // as persistent values restoring the style, device updates, etc + if (getStyle() == ParamVizStyle::CHECKER_ISLANDS && !haveIslandLabels()) { + setStyle(ParamVizStyle::CHECKER); + } +} void SurfaceParameterizationQuantity::draw() { if (!isEnabled()) return; @@ -37,14 +44,21 @@ void SurfaceParameterizationQuantity::draw() { void SurfaceParameterizationQuantity::createProgram() { + // sanity check, this should basically never happen, but this guards against weird edge cases such + // as persistent values restoring the style, device updates, etc + if (getStyle() == ParamVizStyle::CHECKER_ISLANDS && !haveIslandLabels()) { + setStyle(ParamVizStyle::CHECKER); + } + // Create the program to draw this quantity // clang-format off program = render::engine->requestShader("MESH", render::engine->addMaterialRules(parent.getMaterial(), parent.addSurfaceMeshRules( - addParameterizationRules( - {"MESH_PROPAGATE_VALUE2"} - ) + addParameterizationRules({ + "MESH_PROPAGATE_VALUE2", + getStyle() == ParamVizStyle::CHECKER_ISLANDS ? "MESH_PROPAGATE_INT" : "", + }) ) ) ); @@ -55,6 +69,10 @@ void SurfaceParameterizationQuantity::createProgram() { fillParameterizationBuffers(*program); parent.setMeshGeometryAttributes(*program); + if(getStyle() == ParamVizStyle::CHECKER_ISLANDS) { + program->setAttribute("a_int", islandLabels.getIndexedRenderAttributeBuffer(parent.triangleFaceInds)); + } + render::engine->setMaterial(*program, parent.getMaterial()); } @@ -75,6 +93,9 @@ void SurfaceParameterizationQuantity::buildCustomUI() { buildParameterizationUI(); } +size_t SurfaceParameterizationQuantity::nFaces() { + return parent.nFaces(); +} void SurfaceParameterizationQuantity::refresh() { program.reset(); diff --git a/test/src/surface_mesh_test.cpp b/test/src/surface_mesh_test.cpp index 085ab8d3..8c36c911 100644 --- a/test/src/surface_mesh_test.cpp +++ b/test/src/surface_mesh_test.cpp @@ -316,6 +316,12 @@ TEST_F(PolyscopeTest, SurfaceMeshCornerParam) { q1->setStyle(polyscope::ParamVizStyle::LOCAL_RAD); polyscope::show(3); + // set islands + std::vector islandLabels(psMesh->nFaces(), 0); + q1->setIslandLabels(islandLabels); + q1->setStyle(polyscope::ParamVizStyle::CHECKER_ISLANDS); + polyscope::show(3); + polyscope::removeAllStructures(); } From e1e70dbe096499182e9689621175604f0408f7a2 Mon Sep 17 00:00:00 2001 From: Nicholas Sharp Date: Wed, 29 Nov 2023 00:17:19 -0800 Subject: [PATCH 3/6] implement param seam viz --- .../surface_parameterization_quantity.h | 7 ++ src/surface_parameterization_quantity.cpp | 103 ++++++++++++++++++ test/src/surface_mesh_test.cpp | 5 + 3 files changed, 115 insertions(+) diff --git a/include/polyscope/surface_parameterization_quantity.h b/include/polyscope/surface_parameterization_quantity.h index d6406a4b..c924d759 100644 --- a/include/polyscope/surface_parameterization_quantity.h +++ b/include/polyscope/surface_parameterization_quantity.h @@ -12,6 +12,8 @@ namespace polyscope { +// forward declarations +class CurveNetwork; // ============================================================== // ================ Base Parameterization ===================== @@ -29,9 +31,14 @@ class SurfaceParameterizationQuantity : public SurfaceMeshQuantity, virtual void refresh() override; virtual void buildCustomUI() override; + // Set islands labels. Technically, this data is just any categorical integer labels per-face of the mesh. + // The intended use is to label islands (connected components in parameterization space) of the UV map. + // When style == CHECKER_ISLANDS, these will be use to visualize the islands with different colors. template void setIslandLabels(const V& newIslandLabels); + CurveNetwork* createCurveNetworkFromSeams(std::string structureName = ""); + protected: std::shared_ptr program; diff --git a/src/surface_parameterization_quantity.cpp b/src/surface_parameterization_quantity.cpp index 59045ad5..88cb506b 100644 --- a/src/surface_parameterization_quantity.cpp +++ b/src/surface_parameterization_quantity.cpp @@ -2,6 +2,10 @@ #include "polyscope/surface_parameterization_quantity.h" +#include +#include + +#include "polyscope/curve_network.h" #include "polyscope/file_helpers.h" #include "polyscope/polyscope.h" #include "polyscope/render/engine.h" @@ -93,6 +97,105 @@ void SurfaceParameterizationQuantity::buildCustomUI() { buildParameterizationUI(); } +CurveNetwork* SurfaceParameterizationQuantity::createCurveNetworkFromSeams(std::string structureName) { + + // set the name to default + if (structureName == "") { + structureName = parent.name + " - " + name + " - seams"; + } + + // Populate data on the host + coords.ensureHostBufferPopulated(); + parent.triangleCornerInds.ensureHostBufferPopulated(); + parent.triangleVertexInds.ensureHostBufferPopulated(); + parent.edgeIsReal.ensureHostBufferPopulated(); + parent.vertexPositions.ensureHostBufferPopulated(); + + // helper to canonicalize edge direction + auto canonicalizeEdge = [](std::pair& inds, std::pair& coords) + { + if(inds.first > inds.second) { + std::swap(inds.first, inds.second); + std::swap(coords.first, coords.second); + } + }; + + // map to find matching & not-matching edges + // TODO set up combining hash to use unordered_map/set instead + std::map, std::pair> edgeCoords; + std::map, int32_t> edgeCount; + std::set> seamEdges; + + // loop over all edges + for(size_t iT = 0; iT < parent.nFacesTriangulation(); iT++) { + for(size_t k = 0; k < 3; k++) { + if(parent.edgeIsReal.data[9*iT][k] == 0.) continue; // skip internal tesselation edges + + // gather data for the edge + int32_t iV_tail = parent.triangleVertexInds.data[3*iT + (k+0)%3]; + int32_t iV_tip = parent.triangleVertexInds.data[3*iT + (k+1)%3]; + int32_t iC_tail = parent.triangleCornerInds.data[3*iT + (k+0)%3]; + int32_t iC_tip = parent.triangleCornerInds.data[3*iT + (k+1)%3]; + std::pair eInd (iV_tail, iV_tip); + std::pair eC (coords.data[iC_tail], coords.data[iC_tip]); + canonicalizeEdge(eInd, eC); // make sure ordering is consistent + + // increment the count + if(edgeCount.find(eInd) == edgeCount.end()) { + edgeCount[eInd] = 1; + } else { + edgeCount[eInd] ++; + } + + // check for a collision against a previously seen copy of this edge + if(edgeCoords.find(eInd) == edgeCoords.end()) { + edgeCoords[eInd] = eC; + } else { + if( edgeCoords[eInd] != eC) { + // it's different! mark the seam + seamEdges.emplace(eInd); + } + } + } + } + + // add all edges that appeared any number of times other than 2 + // (boundaries and nonmanifold edges are always seams) + for(auto& entry : edgeCount) { + if(entry.second != 2) { + seamEdges.emplace(entry.first); + } + } + + // densely enumerate the nodes of the seam curves + std::vector> seamEdgeInds; + std::map vertexIndToDense; + std::vector seamEdgeNodes; + for(const std::pair& edge: seamEdges) { + int32_t vA = edge.first; + int32_t vB = edge.second; + + // get unique vertices for the edges + if(vertexIndToDense.find(vA) == vertexIndToDense.end()) { + vertexIndToDense[vA] = seamEdgeNodes.size(); + seamEdgeNodes.push_back(parent.vertexPositions.data[vA]); + } + int32_t nA = vertexIndToDense[vA]; + if(vertexIndToDense.find(vB) == vertexIndToDense.end()) { + vertexIndToDense[vB] = seamEdgeNodes.size(); + seamEdgeNodes.push_back(parent.vertexPositions.data[vB]); + } + int32_t nB = vertexIndToDense[vB]; + + // add the edge + seamEdgeInds.push_back({nA, nB}); + } + + // add the curve network + + return registerCurveNetwork(structureName, seamEdgeNodes, seamEdgeInds); +} + size_t SurfaceParameterizationQuantity::nFaces() { return parent.nFaces(); } diff --git a/test/src/surface_mesh_test.cpp b/test/src/surface_mesh_test.cpp index 8c36c911..437ca3b4 100644 --- a/test/src/surface_mesh_test.cpp +++ b/test/src/surface_mesh_test.cpp @@ -321,6 +321,11 @@ TEST_F(PolyscopeTest, SurfaceMeshCornerParam) { q1->setIslandLabels(islandLabels); q1->setStyle(polyscope::ParamVizStyle::CHECKER_ISLANDS); polyscope::show(3); + + + // create the curve network + q1->createCurveNetworkFromSeams(); + polyscope::show(3); polyscope::removeAllStructures(); } From 7b10309586852b79ebdd6ae998da3c82d6e7eadc Mon Sep 17 00:00:00 2001 From: Nicholas Sharp Date: Wed, 29 Nov 2023 22:20:00 -0800 Subject: [PATCH 4/6] fixes to param island shader --- include/polyscope/parameterization_quantity.h | 4 ++-- .../render/opengl/shaders/surface_mesh_shaders.h | 1 + .../surface_parameterization_quantity.h | 2 +- src/render/mock_opengl/mock_gl_engine.cpp | 2 +- src/render/opengl/gl_engine.cpp | 2 +- src/render/opengl/shaders/common.cpp | 2 +- src/render/opengl/shaders/rules.cpp | 5 +++-- .../opengl/shaders/surface_mesh_shaders.cpp | 16 ++++++++-------- src/surface_parameterization_quantity.cpp | 6 +++--- 9 files changed, 21 insertions(+), 19 deletions(-) diff --git a/include/polyscope/parameterization_quantity.h b/include/polyscope/parameterization_quantity.h index d6c18e63..6504848a 100644 --- a/include/polyscope/parameterization_quantity.h +++ b/include/polyscope/parameterization_quantity.h @@ -32,7 +32,7 @@ class ParameterizationQuantity { // Wrapper around the actual buffers of data stored in the class. // Interaction with the data (updating it on CPU or GPU side, accessing it, etc) happens through this wrapper. render::ManagedBuffer coords; - render::ManagedBuffer islandLabels; // only optionally populated + render::ManagedBuffer islandLabels; // only optionally populated. should be integers. const ParamCoordsType coordsType; @@ -72,7 +72,7 @@ class ParameterizationQuantity { protected: // Raw storage for the data. You should only interact with this via the managed buffer above std::vector coordsData; - std::vector islandLabelsData; + std::vector islandLabelsData; bool islandLabelsPopulated = false; // === Visualization parameters diff --git a/include/polyscope/render/opengl/shaders/surface_mesh_shaders.h b/include/polyscope/render/opengl/shaders/surface_mesh_shaders.h index bea3c591..a3e64bfb 100644 --- a/include/polyscope/render/opengl/shaders/surface_mesh_shaders.h +++ b/include/polyscope/render/opengl/shaders/surface_mesh_shaders.h @@ -24,6 +24,7 @@ extern const ShaderReplacementRule MESH_BACKFACE_NORMAL_FLIP; extern const ShaderReplacementRule MESH_BACKFACE_DIFFERENT; extern const ShaderReplacementRule MESH_BACKFACE_DARKEN; extern const ShaderReplacementRule MESH_PROPAGATE_VALUE; +extern const ShaderReplacementRule MESH_PROPAGATE_FLAT_VALUE; extern const ShaderReplacementRule MESH_PROPAGATE_INT; extern const ShaderReplacementRule MESH_PROPAGATE_VALUE2; extern const ShaderReplacementRule MESH_PROPAGATE_TCOORD; diff --git a/include/polyscope/surface_parameterization_quantity.h b/include/polyscope/surface_parameterization_quantity.h index c924d759..6d3f69a5 100644 --- a/include/polyscope/surface_parameterization_quantity.h +++ b/include/polyscope/surface_parameterization_quantity.h @@ -90,7 +90,7 @@ class SurfaceVertexParameterizationQuantity : public SurfaceParameterizationQuan template void SurfaceParameterizationQuantity::setIslandLabels(const V& newIslandLabels) { validateSize(newIslandLabels, this->nFaces(), "scalar quantity " + quantity.name); - islandLabels.data = standardizeArray(newIslandLabels); + islandLabels.data = standardizeArray(newIslandLabels); islandLabels.markHostBufferUpdated(); islandLabelsPopulated = true; } diff --git a/src/render/mock_opengl/mock_gl_engine.cpp b/src/render/mock_opengl/mock_gl_engine.cpp index 9af353ea..ab008783 100644 --- a/src/render/mock_opengl/mock_gl_engine.cpp +++ b/src/render/mock_opengl/mock_gl_engine.cpp @@ -1981,7 +1981,7 @@ void MockGLEngine::populateDefaultShadersAndRules() { registerShaderRule("MESH_BACKFACE_DIFFERENT", MESH_BACKFACE_DIFFERENT); registerShaderRule("MESH_BACKFACE_DARKEN", MESH_BACKFACE_DARKEN); registerShaderRule("MESH_PROPAGATE_VALUE", MESH_PROPAGATE_VALUE); - registerShaderRule("MESH_PROPAGATE_INT", MESH_PROPAGATE_INT); + registerShaderRule("MESH_PROPAGATE_FLAT_VALUE", MESH_PROPAGATE_FLAT_VALUE); registerShaderRule("MESH_PROPAGATE_VALUE2", MESH_PROPAGATE_VALUE2); registerShaderRule("MESH_PROPAGATE_TCOORD", MESH_PROPAGATE_TCOORD); registerShaderRule("MESH_PROPAGATE_COLOR", MESH_PROPAGATE_COLOR); diff --git a/src/render/opengl/gl_engine.cpp b/src/render/opengl/gl_engine.cpp index c0fae18c..4ca3062b 100644 --- a/src/render/opengl/gl_engine.cpp +++ b/src/render/opengl/gl_engine.cpp @@ -2749,7 +2749,7 @@ void GLEngine::populateDefaultShadersAndRules() { registerShaderRule("MESH_BACKFACE_DIFFERENT", MESH_BACKFACE_DIFFERENT); registerShaderRule("MESH_BACKFACE_DARKEN", MESH_BACKFACE_DARKEN); registerShaderRule("MESH_PROPAGATE_VALUE", MESH_PROPAGATE_VALUE); - registerShaderRule("MESH_PROPAGATE_INT", MESH_PROPAGATE_INT); + registerShaderRule("MESH_PROPAGATE_FLAT_VALUE", MESH_PROPAGATE_FLAT_VALUE); registerShaderRule("MESH_PROPAGATE_VALUE2", MESH_PROPAGATE_VALUE2); registerShaderRule("MESH_PROPAGATE_TCOORD", MESH_PROPAGATE_TCOORD); registerShaderRule("MESH_PROPAGATE_COLOR", MESH_PROPAGATE_COLOR); diff --git a/src/render/opengl/shaders/common.cpp b/src/render/opengl/shaders/common.cpp index eae4a5e0..8ccd6cf0 100644 --- a/src/render/opengl/shaders/common.cpp +++ b/src/render/opengl/shaders/common.cpp @@ -131,7 +131,7 @@ float intToDistinctReal(float start, int index) { float val = 0.f; float p = 0.5f; for(int iShift = 0; iShift < NBitsUntilRepeat; iShift++) { // unroll please - val += float(index % 2 == 1) * p; + val += float((index % 2) == 1) * p; index = index >> 1; p /= 2.0; } diff --git a/src/render/opengl/shaders/rules.cpp b/src/render/opengl/shaders/rules.cpp index 6c032340..42ac462a 100644 --- a/src/render/opengl/shaders/rules.cpp +++ b/src/render/opengl/shaders/rules.cpp @@ -237,8 +237,9 @@ const ShaderReplacementRule SHADE_CHECKER_CATEGORY( )"}, {"GENERATE_SHADE_COLOR", R"( // sample the categorical color - float catVal = intToDistinctReal(0., shadeInt); - vec3 catColor = texture(t_colormap, catVal).rgb; + float catVal = intToDistinctReal(0., int(shadeValue)); + float scaleFac = 1.2f; // pump up the brightness a bit, so the modDarkness doesn't make it too dark + vec3 catColor = scaleFac * texture(t_colormap, catVal).rgb; vec3 catColorDark = catColor * u_modDarkness; // NOTE checker math shared with other shaders diff --git a/src/render/opengl/shaders/surface_mesh_shaders.cpp b/src/render/opengl/shaders/surface_mesh_shaders.cpp index 1a8b6216..2469f088 100644 --- a/src/render/opengl/shaders/surface_mesh_shaders.cpp +++ b/src/render/opengl/shaders/surface_mesh_shaders.cpp @@ -256,26 +256,26 @@ const ShaderReplacementRule MESH_PROPAGATE_VALUE ( /* textures */ {} ); -const ShaderReplacementRule MESH_PROPAGATE_INT ( - /* rule name */ "MESH_PROPAGATE_INT", +const ShaderReplacementRule MESH_PROPAGATE_FLAT_VALUE ( + /* rule name */ "MESH_PROPAGATE_FLAT_VALUE", { /* replacement sources */ {"VERT_DECLARATIONS", R"( - in int a_int; - flat out int a_intToFrag; + in float a_value; + flat out float a_valueToFrag; )"}, {"VERT_ASSIGNMENTS", R"( - a_intToFrag = a_int; + a_valueToFrag = a_value; )"}, {"FRAG_DECLARATIONS", R"( - flat in int a_intToFrag; + flat in float a_valueToFrag; )"}, {"GENERATE_SHADE_VALUE", R"( - int shadeInt = a_intToFrag; + float shadeValue = a_valueToFrag; )"}, }, /* uniforms */ {}, /* attributes */ { - {"a_int", RenderDataType::Int}, + {"a_value", RenderDataType::Float}, }, /* textures */ {} ); diff --git a/src/surface_parameterization_quantity.cpp b/src/surface_parameterization_quantity.cpp index 88cb506b..d51ed0a4 100644 --- a/src/surface_parameterization_quantity.cpp +++ b/src/surface_parameterization_quantity.cpp @@ -61,7 +61,7 @@ void SurfaceParameterizationQuantity::createProgram() { parent.addSurfaceMeshRules( addParameterizationRules({ "MESH_PROPAGATE_VALUE2", - getStyle() == ParamVizStyle::CHECKER_ISLANDS ? "MESH_PROPAGATE_INT" : "", + getStyle() == ParamVizStyle::CHECKER_ISLANDS ? "MESH_PROPAGATE_FLAT_VALUE" : "", }) ) ) @@ -74,7 +74,7 @@ void SurfaceParameterizationQuantity::createProgram() { parent.setMeshGeometryAttributes(*program); if(getStyle() == ParamVizStyle::CHECKER_ISLANDS) { - program->setAttribute("a_int", islandLabels.getIndexedRenderAttributeBuffer(parent.triangleFaceInds)); + program->setAttribute("a_value", islandLabels.getIndexedRenderAttributeBuffer(parent.triangleFaceInds)); } render::engine->setMaterial(*program, parent.getMaterial()); @@ -129,7 +129,7 @@ CurveNetwork* SurfaceParameterizationQuantity::createCurveNetworkFromSeams(std:: // loop over all edges for(size_t iT = 0; iT < parent.nFacesTriangulation(); iT++) { for(size_t k = 0; k < 3; k++) { - if(parent.edgeIsReal.data[9*iT][k] == 0.) continue; // skip internal tesselation edges + if(parent.edgeIsReal.data[3*iT][k] == 0.) continue; // skip internal tesselation edges // gather data for the edge int32_t iV_tail = parent.triangleVertexInds.data[3*iT + (k+0)%3]; From da6b9918470f0da23d7526f0d2689e5a3be60ea7 Mon Sep 17 00:00:00 2001 From: Nicholas Sharp Date: Wed, 29 Nov 2023 22:22:19 -0800 Subject: [PATCH 5/6] add option to create param seams curve from UI --- src/surface_parameterization_quantity.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/surface_parameterization_quantity.cpp b/src/surface_parameterization_quantity.cpp index d51ed0a4..ce9de526 100644 --- a/src/surface_parameterization_quantity.cpp +++ b/src/surface_parameterization_quantity.cpp @@ -90,6 +90,8 @@ void SurfaceParameterizationQuantity::buildCustomUI() { if (ImGui::BeginPopup("OptionsPopup")) { buildParameterizationOptionsUI(); + + if (ImGui::MenuItem("Create curve network from seams")) createCurveNetworkFromSeams(); ImGui::EndPopup(); } From fb03958bedca203ff1c4e064fcafa460eec2c02d Mon Sep 17 00:00:00 2001 From: Nicholas Sharp Date: Wed, 29 Nov 2023 22:29:58 -0800 Subject: [PATCH 6/6] clean up UI options --- include/polyscope/parameterization_quantity.ipp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/polyscope/parameterization_quantity.ipp b/include/polyscope/parameterization_quantity.ipp index 9cc43594..a556d8b3 100644 --- a/include/polyscope/parameterization_quantity.ipp +++ b/include/polyscope/parameterization_quantity.ipp @@ -81,15 +81,15 @@ void ParameterizationQuantity::buildParameterizationUI() { setCheckerColors(getCheckerColors()); break; case ParamVizStyle::CHECKER_ISLANDS: - if (render::buildColormapSelector(cMap.get())) { - setColorMap(getColorMap()); - } ImGui::PushItemWidth(100); if (ImGui::DragFloat("alt darkness", &altDarkness.get(), 0.01, 0., 1.)) { altDarkness.manuallyChanged(); requestRedraw(); } ImGui::PopItemWidth(); + if (render::buildColormapSelector(cMap.get())) { + setColorMap(getColorMap()); + } break; case ParamVizStyle::GRID: ImGui::SameLine();