From 60b0f9dc08f79763a3d43da6de7d04df585d6e57 Mon Sep 17 00:00:00 2001 From: Nicholas Sharp Date: Wed, 25 Mar 2026 16:54:06 -0500 Subject: [PATCH 1/4] initial work on simple triangle vertex and face quantities --- examples/demo-app/demo_app.cpp | 6 +- include/polyscope/simple_triangle_mesh.h | 23 +++++++ include/polyscope/simple_triangle_mesh.ipp | 18 +++++ .../simple_triangle_mesh_color_quantity.h | 30 ++++++++ .../polyscope/simple_triangle_mesh_quantity.h | 20 ++++++ .../simple_triangle_mesh_scalar_quantity.h | 33 +++++++++ src/CMakeLists.txt | 6 ++ src/simple_triangle_mesh.cpp | 22 ++++++ src/simple_triangle_mesh_color_quantity.cpp | 68 ++++++++++++++++++ src/simple_triangle_mesh_quantity.cpp | 13 ++++ src/simple_triangle_mesh_scalar_quantity.cpp | 69 +++++++++++++++++++ test/src/surface_mesh_test.cpp | 36 ++++++++++ 12 files changed, 343 insertions(+), 1 deletion(-) create mode 100644 include/polyscope/simple_triangle_mesh_color_quantity.h create mode 100644 include/polyscope/simple_triangle_mesh_quantity.h create mode 100644 include/polyscope/simple_triangle_mesh_scalar_quantity.h create mode 100644 src/simple_triangle_mesh_color_quantity.cpp create mode 100644 src/simple_triangle_mesh_quantity.cpp create mode 100644 src/simple_triangle_mesh_scalar_quantity.cpp diff --git a/examples/demo-app/demo_app.cpp b/examples/demo-app/demo_app.cpp index afe263518..566abcc2a 100644 --- a/examples/demo-app/demo_app.cpp +++ b/examples/demo-app/demo_app.cpp @@ -121,7 +121,7 @@ void processFileOBJ(std::string filename) { } auto psMesh = polyscope::registerSurfaceMesh(niceName, vertexPositionsGLM, faceIndices); - auto psSimpleMesh = polyscope::registerSimpleTriangleMesh(niceName, vertexPositionsGLM, faceIndices); + auto psSimpleMesh = polyscope::registerSimpleTriangleMesh(niceName + " (simple)", vertexPositionsGLM, faceIndices); psSimpleMesh->setEnabled(false); // Useful data @@ -154,6 +154,10 @@ void processFileOBJ(std::string filename) { polyscope::getSurfaceMesh(niceName)->addVertexScalarQuantity("categorical vert", valCat, polyscope::DataType::CATEGORICAL); + // Simple triangle mesh quantities + psSimpleMesh->addVertexScalarQuantity("cY", valY); + psSimpleMesh->addVertexColorQuantity("vColor", randColor); + polyscope::getSurfaceMesh(niceName)->addVertexDistanceQuantity("cY_dist", valY); polyscope::getSurfaceMesh(niceName)->addVertexSignedDistanceQuantity("cY_signeddist", valY); diff --git a/include/polyscope/simple_triangle_mesh.h b/include/polyscope/simple_triangle_mesh.h index 4b0c60419..753e4560d 100644 --- a/include/polyscope/simple_triangle_mesh.h +++ b/include/polyscope/simple_triangle_mesh.h @@ -8,8 +8,13 @@ #include "polyscope/render/engine.h" #include "polyscope/render/managed_buffer.h" #include "polyscope/scaled_value.h" +#include "polyscope/standardize_data_array.h" #include "polyscope/structure.h" +#include "polyscope/simple_triangle_mesh_color_quantity.h" +#include "polyscope/simple_triangle_mesh_quantity.h" +#include "polyscope/simple_triangle_mesh_scalar_quantity.h" + #include namespace polyscope { @@ -18,6 +23,8 @@ namespace polyscope { class SimpleTriangleMesh; // Forward declare quantity types +class SimpleTriangleMeshScalarQuantity; +class SimpleTriangleMeshColorQuantity; struct SimpleTriangleMeshPickResult { // this does nothing for now, just matching pattern from other structures @@ -50,8 +57,20 @@ class SimpleTriangleMesh : public Structure { render::ManagedBuffer vertices; render::ManagedBuffer faces; + size_t nVertices() { return vertices.size(); } + size_t nFaces() { return faces.size(); } + // === Quantities + // Scalars + template + SimpleTriangleMeshScalarQuantity* addVertexScalarQuantity(std::string name, const T& values, + DataType type = DataType::STANDARD); + + // Colors + template + SimpleTriangleMeshColorQuantity* addVertexColorQuantity(std::string name, const T& values); + // === Mutate template @@ -115,6 +134,10 @@ class SimpleTriangleMesh : public Structure { void setPickUniforms(render::ShaderProgram& p); // === Quantity adder implementations + SimpleTriangleMeshScalarQuantity* addVertexScalarQuantityImpl(std::string name, const std::vector& data, + DataType type); + SimpleTriangleMeshColorQuantity* addVertexColorQuantityImpl(std::string name, + const std::vector& colors); // == Picking related things size_t pickStart; diff --git a/include/polyscope/simple_triangle_mesh.ipp b/include/polyscope/simple_triangle_mesh.ipp index dc284befc..2983ba8d1 100644 --- a/include/polyscope/simple_triangle_mesh.ipp +++ b/include/polyscope/simple_triangle_mesh.ipp @@ -40,6 +40,24 @@ void SimpleTriangleMesh::update(const V& newPositions, const F& newFaces) { faces.markHostBufferUpdated(); } +// ===================================================== +// ============== Quantities +// ===================================================== + +template +SimpleTriangleMeshScalarQuantity* SimpleTriangleMesh::addVertexScalarQuantity(std::string name, const T& values, + DataType type) { + validateSize(values, nVertices(), "vertex scalar quantity " + name); + return addVertexScalarQuantityImpl(name, standardizeArray(values), type); +} + +template +SimpleTriangleMeshColorQuantity* SimpleTriangleMesh::addVertexColorQuantity(std::string name, const T& values) { + validateSize(values, nVertices(), "vertex color quantity " + name); + return addVertexColorQuantityImpl(name, standardizeVectorArray(values)); +} + + // Shorthand to get a mesh from polyscope inline SimpleTriangleMesh* getSimpleTriangleMesh(std::string name) { return dynamic_cast(getStructure(SimpleTriangleMesh::structureTypeName, name)); diff --git a/include/polyscope/simple_triangle_mesh_color_quantity.h b/include/polyscope/simple_triangle_mesh_color_quantity.h new file mode 100644 index 000000000..2383511e9 --- /dev/null +++ b/include/polyscope/simple_triangle_mesh_color_quantity.h @@ -0,0 +1,30 @@ +// Copyright 2017-2023, Nicholas Sharp and the Polyscope contributors. https://polyscope.run + +#pragma once + +#include "polyscope/color_quantity.h" +#include "polyscope/simple_triangle_mesh_quantity.h" + +#include + +namespace polyscope { + +class SimpleTriangleMeshColorQuantity : public SimpleTriangleMeshQuantity, + public ColorQuantity { +public: + SimpleTriangleMeshColorQuantity(std::string name, const std::vector& colors, std::string definedOn, + SimpleTriangleMesh& mesh); + + virtual void draw() override; + virtual void buildCustomUI() override; + virtual void refresh() override; + virtual std::string niceName() override; + + std::string definedOn; + +protected: + void createProgram(); + std::shared_ptr program; +}; + +} // namespace polyscope diff --git a/include/polyscope/simple_triangle_mesh_quantity.h b/include/polyscope/simple_triangle_mesh_quantity.h new file mode 100644 index 000000000..6df4e0e45 --- /dev/null +++ b/include/polyscope/simple_triangle_mesh_quantity.h @@ -0,0 +1,20 @@ +// Copyright 2017-2023, Nicholas Sharp and the Polyscope contributors. https://polyscope.run + +#pragma once + +#include "polyscope/quantity.h" +#include "polyscope/structure.h" + +namespace polyscope { + +class SimpleTriangleMesh; + +class SimpleTriangleMeshQuantity : public Quantity { +public: + SimpleTriangleMeshQuantity(std::string name, SimpleTriangleMesh& parentStructure, bool dominates = false); + virtual ~SimpleTriangleMeshQuantity(){}; + + SimpleTriangleMesh& parent; // shadows and hides the generic member in Quantity +}; + +} // namespace polyscope diff --git a/include/polyscope/simple_triangle_mesh_scalar_quantity.h b/include/polyscope/simple_triangle_mesh_scalar_quantity.h new file mode 100644 index 000000000..729b24a71 --- /dev/null +++ b/include/polyscope/simple_triangle_mesh_scalar_quantity.h @@ -0,0 +1,33 @@ +// Copyright 2017-2023, Nicholas Sharp and the Polyscope contributors. https://polyscope.run + +#pragma once + +#include "polyscope/affine_remapper.h" +#include "polyscope/render/color_maps.h" +#include "polyscope/scalar_quantity.h" +#include "polyscope/simple_triangle_mesh_quantity.h" + +#include + +namespace polyscope { + +class SimpleTriangleMeshScalarQuantity : public SimpleTriangleMeshQuantity, + public ScalarQuantity { +public: + SimpleTriangleMeshScalarQuantity(std::string name, const std::vector& values, std::string definedOn, + SimpleTriangleMesh& mesh, DataType dataType); + + virtual void draw() override; + virtual void buildCustomUI() override; + virtual void refresh() override; + virtual std::string niceName() override; + + +protected: + std::string definedOn; + std::shared_ptr program; + + void createProgram(); +}; + +} // namespace polyscope diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8b4eca3bc..da32e3a50 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -235,6 +235,9 @@ SET(SRCS # Simple triangle mesh simple_triangle_mesh.cpp + simple_triangle_mesh_quantity.cpp + simple_triangle_mesh_scalar_quantity.cpp + simple_triangle_mesh_color_quantity.cpp # Floating quantities floating_quantity_structure.cpp @@ -337,6 +340,9 @@ SET(HEADERS ${INCLUDE_ROOT}/screenshot.h ${INCLUDE_ROOT}/simple_triangle_mesh.h ${INCLUDE_ROOT}/simple_triangle_mesh.ipp + ${INCLUDE_ROOT}/simple_triangle_mesh_quantity.h + ${INCLUDE_ROOT}/simple_triangle_mesh_scalar_quantity.h + ${INCLUDE_ROOT}/simple_triangle_mesh_color_quantity.h ${INCLUDE_ROOT}/slice_plane.h ${INCLUDE_ROOT}/standardize_data_array.h ${INCLUDE_ROOT}/structure.h diff --git a/src/simple_triangle_mesh.cpp b/src/simple_triangle_mesh.cpp index 549a2ce40..da77aa60c 100644 --- a/src/simple_triangle_mesh.cpp +++ b/src/simple_triangle_mesh.cpp @@ -5,6 +5,8 @@ #include "polyscope/pick.h" #include "polyscope/polyscope.h" #include "polyscope/render/engine.h" +#include "polyscope/simple_triangle_mesh_color_quantity.h" +#include "polyscope/simple_triangle_mesh_scalar_quantity.h" #include "imgui.h" @@ -348,4 +350,24 @@ SimpleTriangleMesh* SimpleTriangleMesh::setBackFaceColor(glm::vec3 val) { glm::vec3 SimpleTriangleMesh::getBackFaceColor() { return backFaceColor.get(); } +// === Quantity adder implementations + +SimpleTriangleMeshScalarQuantity* SimpleTriangleMesh::addVertexScalarQuantityImpl(std::string name, + const std::vector& data, + DataType type) { + checkForQuantityWithNameAndDeleteOrError(name); + SimpleTriangleMeshScalarQuantity* q = new SimpleTriangleMeshScalarQuantity(name, data, "vertex", *this, type); + addQuantity(q); + return q; +} + +SimpleTriangleMeshColorQuantity* SimpleTriangleMesh::addVertexColorQuantityImpl(std::string name, + const std::vector& colors) { + checkForQuantityWithNameAndDeleteOrError(name); + SimpleTriangleMeshColorQuantity* q = new SimpleTriangleMeshColorQuantity(name, colors, "vertex", *this); + addQuantity(q); + return q; +} + + } // namespace polyscope diff --git a/src/simple_triangle_mesh_color_quantity.cpp b/src/simple_triangle_mesh_color_quantity.cpp new file mode 100644 index 000000000..3e6430ef8 --- /dev/null +++ b/src/simple_triangle_mesh_color_quantity.cpp @@ -0,0 +1,68 @@ +// Copyright 2017-2023, Nicholas Sharp and the Polyscope contributors. https://polyscope.run + +#include "polyscope/simple_triangle_mesh_color_quantity.h" + +#include "polyscope/polyscope.h" +#include "polyscope/simple_triangle_mesh.h" + +#include "imgui.h" + +namespace polyscope { + +SimpleTriangleMeshColorQuantity::SimpleTriangleMeshColorQuantity(std::string name, + const std::vector& colors_, + std::string definedOn_, + SimpleTriangleMesh& mesh_) + : SimpleTriangleMeshQuantity(name, mesh_, true), ColorQuantity(*this, colors_), definedOn(definedOn_) {} + +void SimpleTriangleMeshColorQuantity::draw() { + if (!isEnabled()) return; + + if (program == nullptr) createProgram(); + + parent.setStructureUniforms(*program); + parent.setSimpleTriangleMeshUniforms(*program); + setColorUniforms(*program); + render::engine->setMaterialUniforms(*program, parent.getMaterial()); + + program->draw(); +} + +void SimpleTriangleMeshColorQuantity::buildCustomUI() { + ImGui::SameLine(); + if (ImGui::Button("Options")) { + ImGui::OpenPopup("OptionsPopup"); + } + if (ImGui::BeginPopup("OptionsPopup")) { + buildColorOptionsUI(); + ImGui::EndPopup(); + } + buildColorUI(); +} + +void SimpleTriangleMeshColorQuantity::createProgram() { + // clang-format off + program = render::engine->requestShader("SIMPLE_MESH", + render::engine->addMaterialRules(parent.getMaterial(), + addColorRules( + parent.addSimpleTriangleMeshRules( + {"MESH_PROPAGATE_COLOR", "SHADE_COLOR", "COMPUTE_SHADE_NORMAL_FROM_POSITION", "PROJ_AND_INV_PROJ_MAT"} + ) + ) + ) + ); + // clang-format on + + parent.setSimpleTriangleMeshProgramGeometryAttributes(*program); + program->setAttribute("a_color", colors.getRenderAttributeBuffer()); + render::engine->setMaterial(*program, parent.getMaterial()); +} + +void SimpleTriangleMeshColorQuantity::refresh() { + program.reset(); + Quantity::refresh(); +} + +std::string SimpleTriangleMeshColorQuantity::niceName() { return name + " (" + definedOn + " color)"; } + +} // namespace polyscope diff --git a/src/simple_triangle_mesh_quantity.cpp b/src/simple_triangle_mesh_quantity.cpp new file mode 100644 index 000000000..aa917224c --- /dev/null +++ b/src/simple_triangle_mesh_quantity.cpp @@ -0,0 +1,13 @@ +// Copyright 2017-2023, Nicholas Sharp and the Polyscope contributors. https://polyscope.run + +#include "polyscope/simple_triangle_mesh_quantity.h" + +#include "polyscope/simple_triangle_mesh.h" + +namespace polyscope { + +SimpleTriangleMeshQuantity::SimpleTriangleMeshQuantity(std::string name, SimpleTriangleMesh& parentStructure, + bool dominates) + : Quantity(name, parentStructure, dominates), parent(parentStructure) {} + +} // namespace polyscope diff --git a/src/simple_triangle_mesh_scalar_quantity.cpp b/src/simple_triangle_mesh_scalar_quantity.cpp new file mode 100644 index 000000000..1e48ce04c --- /dev/null +++ b/src/simple_triangle_mesh_scalar_quantity.cpp @@ -0,0 +1,69 @@ +// Copyright 2017-2023, Nicholas Sharp and the Polyscope contributors. https://polyscope.run + +#include "polyscope/simple_triangle_mesh_scalar_quantity.h" + +#include "polyscope/polyscope.h" +#include "polyscope/simple_triangle_mesh.h" + +#include "imgui.h" + +namespace polyscope { + +SimpleTriangleMeshScalarQuantity::SimpleTriangleMeshScalarQuantity(std::string name, const std::vector& values_, + std::string definedOn_, + SimpleTriangleMesh& mesh_, DataType dataType_) + : SimpleTriangleMeshQuantity(name, mesh_, true), ScalarQuantity(*this, values_, dataType_), + definedOn(definedOn_) {} + +void SimpleTriangleMeshScalarQuantity::draw() { + if (!isEnabled()) return; + + if (program == nullptr) createProgram(); + + parent.setStructureUniforms(*program); + parent.setSimpleTriangleMeshUniforms(*program); + setScalarUniforms(*program); + render::engine->setMaterialUniforms(*program, parent.getMaterial()); + + program->draw(); +} + +void SimpleTriangleMeshScalarQuantity::buildCustomUI() { + ImGui::SameLine(); + if (ImGui::Button("Options")) { + ImGui::OpenPopup("OptionsPopup"); + } + if (ImGui::BeginPopup("OptionsPopup")) { + buildScalarOptionsUI(); + ImGui::EndPopup(); + } + buildScalarUI(); +} + +void SimpleTriangleMeshScalarQuantity::createProgram() { + // clang-format off + program = render::engine->requestShader("SIMPLE_MESH", + render::engine->addMaterialRules(parent.getMaterial(), + parent.addSimpleTriangleMeshRules( + addScalarRules( + {"MESH_PROPAGATE_VALUE", "COMPUTE_SHADE_NORMAL_FROM_POSITION", "PROJ_AND_INV_PROJ_MAT"} + ) + ) + ) + ); + // clang-format on + + parent.setSimpleTriangleMeshProgramGeometryAttributes(*program); + program->setAttribute("a_value", values.getRenderAttributeBuffer()); + program->setTextureFromColormap("t_colormap", cMap.get()); + render::engine->setMaterial(*program, parent.getMaterial()); +} + +void SimpleTriangleMeshScalarQuantity::refresh() { + program.reset(); + Quantity::refresh(); +} + +std::string SimpleTriangleMeshScalarQuantity::niceName() { return name + " (" + definedOn + " scalar)"; } + +} // namespace polyscope diff --git a/test/src/surface_mesh_test.cpp b/test/src/surface_mesh_test.cpp index 16b2ca21f..bbef85700 100644 --- a/test/src/surface_mesh_test.cpp +++ b/test/src/surface_mesh_test.cpp @@ -665,3 +665,39 @@ TEST_F(PolyscopeTest, SimpleTriangleMesUpdate) { polyscope::removeAllStructures(); } + +TEST_F(PolyscopeTest, SimpleTriangleMeshVertexScalar) { + auto psMesh = registerSimpleTriangleMesh(); + std::vector vScalar(psMesh->nVertices(), 7.); + auto q1 = psMesh->addVertexScalarQuantity("vScalar", vScalar); + q1->setEnabled(true); + polyscope::show(3); + + q1->updateData(vScalar); + polyscope::show(3); + + polyscope::removeAllStructures(); +} + +TEST_F(PolyscopeTest, SimpleTriangleMeshVertexScalarCategorical) { + auto psMesh = registerSimpleTriangleMesh(); + std::vector vScalar(psMesh->nVertices(), 2.); + auto q1 = psMesh->addVertexScalarQuantity("vScalar", vScalar, polyscope::DataType::CATEGORICAL); + q1->setEnabled(true); + polyscope::show(3); + + polyscope::removeAllStructures(); +} + +TEST_F(PolyscopeTest, SimpleTriangleMeshVertexColor) { + auto psMesh = registerSimpleTriangleMesh(); + std::vector vColors(psMesh->nVertices(), glm::vec3{.2, .3, .4}); + auto q1 = psMesh->addVertexColorQuantity("vColor", vColors); + q1->setEnabled(true); + polyscope::show(3); + + q1->updateData(vColors); + polyscope::show(3); + + polyscope::removeAllStructures(); +} From b68e6b9fc43be5424626d2f77bad4171b67e7e1d Mon Sep 17 00:00:00 2001 From: Nicholas Sharp Date: Wed, 25 Mar 2026 16:59:41 -0500 Subject: [PATCH 2/4] clean up unfiorms --- src/simple_triangle_mesh.cpp | 7 +++++-- src/simple_triangle_mesh_color_quantity.cpp | 2 +- src/simple_triangle_mesh_scalar_quantity.cpp | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/simple_triangle_mesh.cpp b/src/simple_triangle_mesh.cpp index da77aa60c..c7f3f626a 100644 --- a/src/simple_triangle_mesh.cpp +++ b/src/simple_triangle_mesh.cpp @@ -189,7 +189,7 @@ void SimpleTriangleMesh::ensureRenderProgramPrepared() { render::engine->addMaterialRules(getMaterial(), addSimpleTriangleMeshRules( { - "SHADE_BASECOLOR", "COMPUTE_SHADE_NORMAL_FROM_POSITION", "PROJ_AND_INV_PROJ_MAT" + "SHADE_BASECOLOR" } ) ) @@ -210,7 +210,7 @@ void SimpleTriangleMesh::ensurePickProgramPrepared() { pickProgram = render::engine->requestShader("SIMPLE_MESH", addSimpleTriangleMeshRules( { - "SHADECOLOR_FROM_UNIFORM", "COMPUTE_SHADE_NORMAL_FROM_POSITION", "PROJ_AND_INV_PROJ_MAT" + "SHADECOLOR_FROM_UNIFORM" } , false), render::ShaderReplacementDefaults::Pick ); @@ -230,6 +230,9 @@ std::vector SimpleTriangleMesh::addSimpleTriangleMeshRules(std::vec initRules = addStructureRules(initRules); + initRules.push_back("COMPUTE_SHADE_NORMAL_FROM_POSITION"); + initRules.push_back("PROJ_AND_INV_PROJ_MAT"); + if (withSurfaceShade) { // rules that only get used when we're shading the surface of the mesh diff --git a/src/simple_triangle_mesh_color_quantity.cpp b/src/simple_triangle_mesh_color_quantity.cpp index 3e6430ef8..2342c204f 100644 --- a/src/simple_triangle_mesh_color_quantity.cpp +++ b/src/simple_triangle_mesh_color_quantity.cpp @@ -46,7 +46,7 @@ void SimpleTriangleMeshColorQuantity::createProgram() { render::engine->addMaterialRules(parent.getMaterial(), addColorRules( parent.addSimpleTriangleMeshRules( - {"MESH_PROPAGATE_COLOR", "SHADE_COLOR", "COMPUTE_SHADE_NORMAL_FROM_POSITION", "PROJ_AND_INV_PROJ_MAT"} + {"MESH_PROPAGATE_COLOR", "SHADE_COLOR"} ) ) ) diff --git a/src/simple_triangle_mesh_scalar_quantity.cpp b/src/simple_triangle_mesh_scalar_quantity.cpp index 1e48ce04c..bc3026d8b 100644 --- a/src/simple_triangle_mesh_scalar_quantity.cpp +++ b/src/simple_triangle_mesh_scalar_quantity.cpp @@ -46,7 +46,7 @@ void SimpleTriangleMeshScalarQuantity::createProgram() { render::engine->addMaterialRules(parent.getMaterial(), parent.addSimpleTriangleMeshRules( addScalarRules( - {"MESH_PROPAGATE_VALUE", "COMPUTE_SHADE_NORMAL_FROM_POSITION", "PROJ_AND_INV_PROJ_MAT"} + {"MESH_PROPAGATE_VALUE"} ) ) ) From cdec7f1ebffad50d32f4ec3900ab1d519835654b Mon Sep 17 00:00:00 2001 From: Nicholas Sharp Date: Sun, 5 Apr 2026 09:43:48 -0700 Subject: [PATCH 3/4] quantities on simple triangle meshes --- examples/demo-app/demo_app.cpp | 3 + .../opengl/shaders/surface_mesh_shaders.h | 2 + include/polyscope/simple_triangle_mesh.h | 28 ++++++-- include/polyscope/simple_triangle_mesh.ipp | 19 +++++- .../simple_triangle_mesh_color_quantity.h | 32 +++++++++- .../simple_triangle_mesh_scalar_quantity.h | 31 ++++++++- src/render/mock_opengl/mock_gl_engine.cpp | 2 + src/render/opengl/gl_engine.cpp | 2 + .../opengl/shaders/surface_mesh_shaders.cpp | 34 ++++++++++ src/simple_triangle_mesh.cpp | 29 +++++++-- src/simple_triangle_mesh_color_quantity.cpp | 54 ++++++++++++++-- src/simple_triangle_mesh_scalar_quantity.cpp | 64 ++++++++++++++++--- test/src/surface_mesh_test.cpp | 26 ++++++++ 13 files changed, 291 insertions(+), 35 deletions(-) diff --git a/examples/demo-app/demo_app.cpp b/examples/demo-app/demo_app.cpp index 566abcc2a..8a2cae4cc 100644 --- a/examples/demo-app/demo_app.cpp +++ b/examples/demo-app/demo_app.cpp @@ -190,6 +190,9 @@ void processFileOBJ(std::string filename) { polyscope::getSurfaceMesh(niceName)->addFaceScalarQuantity("categorical face", fCat, polyscope::DataType::CATEGORICAL); + psSimpleMesh->addFaceScalarQuantity("face area", fArea, polyscope::DataType::MAGNITUDE); + psSimpleMesh->addFaceColorQuantity("fColor", fColor); + // size_t nEdges = psMesh->nEdges(); diff --git a/include/polyscope/render/opengl/shaders/surface_mesh_shaders.h b/include/polyscope/render/opengl/shaders/surface_mesh_shaders.h index d89d34338..b5e9484b2 100644 --- a/include/polyscope/render/opengl/shaders/surface_mesh_shaders.h +++ b/include/polyscope/render/opengl/shaders/surface_mesh_shaders.h @@ -36,6 +36,8 @@ extern const ShaderReplacementRule MESH_PROPAGATE_CULLPOS; extern const ShaderReplacementRule MESH_PROPAGATE_PICK; extern const ShaderReplacementRule MESH_PROPAGATE_PICK_SIMPLE; extern const ShaderReplacementRule MESH_PROPAGATE_TYPE_AND_BASECOLOR2_SHADE; +extern const ShaderReplacementRule SIMPLE_MESH_PROPAGATE_FACE_VALUE; +extern const ShaderReplacementRule SIMPLE_MESH_PROPAGATE_FACE_COLOR; } // namespace backend_openGL3 diff --git a/include/polyscope/simple_triangle_mesh.h b/include/polyscope/simple_triangle_mesh.h index 753e4560d..51a066971 100644 --- a/include/polyscope/simple_triangle_mesh.h +++ b/include/polyscope/simple_triangle_mesh.h @@ -24,7 +24,11 @@ class SimpleTriangleMesh; // Forward declare quantity types class SimpleTriangleMeshScalarQuantity; +class SimpleTriangleMeshVertexScalarQuantity; +class SimpleTriangleMeshFaceScalarQuantity; class SimpleTriangleMeshColorQuantity; +class SimpleTriangleMeshVertexColorQuantity; +class SimpleTriangleMeshFaceColorQuantity; struct SimpleTriangleMeshPickResult { // this does nothing for now, just matching pattern from other structures @@ -64,12 +68,19 @@ class SimpleTriangleMesh : public Structure { // Scalars template - SimpleTriangleMeshScalarQuantity* addVertexScalarQuantity(std::string name, const T& values, - DataType type = DataType::STANDARD); + SimpleTriangleMeshVertexScalarQuantity* addVertexScalarQuantity(std::string name, const T& values, + DataType type = DataType::STANDARD); + + template + SimpleTriangleMeshFaceScalarQuantity* addFaceScalarQuantity(std::string name, const T& values, + DataType type = DataType::STANDARD); // Colors template - SimpleTriangleMeshColorQuantity* addVertexColorQuantity(std::string name, const T& values); + SimpleTriangleMeshVertexColorQuantity* addVertexColorQuantity(std::string name, const T& values); + + template + SimpleTriangleMeshFaceColorQuantity* addFaceColorQuantity(std::string name, const T& values); // === Mutate @@ -134,10 +145,13 @@ class SimpleTriangleMesh : public Structure { void setPickUniforms(render::ShaderProgram& p); // === Quantity adder implementations - SimpleTriangleMeshScalarQuantity* addVertexScalarQuantityImpl(std::string name, const std::vector& data, - DataType type); - SimpleTriangleMeshColorQuantity* addVertexColorQuantityImpl(std::string name, - const std::vector& colors); + SimpleTriangleMeshVertexScalarQuantity* addVertexScalarQuantityImpl(std::string name, const std::vector& data, + DataType type); + SimpleTriangleMeshFaceScalarQuantity* addFaceScalarQuantityImpl(std::string name, const std::vector& data, + DataType type); + SimpleTriangleMeshVertexColorQuantity* addVertexColorQuantityImpl(std::string name, + const std::vector& colors); + SimpleTriangleMeshFaceColorQuantity* addFaceColorQuantityImpl(std::string name, const std::vector& colors); // == Picking related things size_t pickStart; diff --git a/include/polyscope/simple_triangle_mesh.ipp b/include/polyscope/simple_triangle_mesh.ipp index 2983ba8d1..fb3f11b2a 100644 --- a/include/polyscope/simple_triangle_mesh.ipp +++ b/include/polyscope/simple_triangle_mesh.ipp @@ -45,18 +45,31 @@ void SimpleTriangleMesh::update(const V& newPositions, const F& newFaces) { // ===================================================== template -SimpleTriangleMeshScalarQuantity* SimpleTriangleMesh::addVertexScalarQuantity(std::string name, const T& values, - DataType type) { +SimpleTriangleMeshVertexScalarQuantity* SimpleTriangleMesh::addVertexScalarQuantity(std::string name, const T& values, + DataType type) { validateSize(values, nVertices(), "vertex scalar quantity " + name); return addVertexScalarQuantityImpl(name, standardizeArray(values), type); } template -SimpleTriangleMeshColorQuantity* SimpleTriangleMesh::addVertexColorQuantity(std::string name, const T& values) { +SimpleTriangleMeshFaceScalarQuantity* SimpleTriangleMesh::addFaceScalarQuantity(std::string name, const T& values, + DataType type) { + validateSize(values, nFaces(), "face scalar quantity " + name); + return addFaceScalarQuantityImpl(name, standardizeArray(values), type); +} + +template +SimpleTriangleMeshVertexColorQuantity* SimpleTriangleMesh::addVertexColorQuantity(std::string name, const T& values) { validateSize(values, nVertices(), "vertex color quantity " + name); return addVertexColorQuantityImpl(name, standardizeVectorArray(values)); } +template +SimpleTriangleMeshFaceColorQuantity* SimpleTriangleMesh::addFaceColorQuantity(std::string name, const T& values) { + validateSize(values, nFaces(), "face color quantity " + name); + return addFaceColorQuantityImpl(name, standardizeVectorArray(values)); +} + // Shorthand to get a mesh from polyscope inline SimpleTriangleMesh* getSimpleTriangleMesh(std::string name) { diff --git a/include/polyscope/simple_triangle_mesh_color_quantity.h b/include/polyscope/simple_triangle_mesh_color_quantity.h index 2383511e9..7e8bce09a 100644 --- a/include/polyscope/simple_triangle_mesh_color_quantity.h +++ b/include/polyscope/simple_triangle_mesh_color_quantity.h @@ -20,11 +20,39 @@ class SimpleTriangleMeshColorQuantity : public SimpleTriangleMeshQuantity, virtual void refresh() override; virtual std::string niceName() override; - std::string definedOn; + const std::string definedOn; protected: - void createProgram(); std::shared_ptr program; + + virtual void createProgram() = 0; +}; + + +// ======================================================== +// ========== Vertex Color ========== +// ======================================================== + +class SimpleTriangleMeshVertexColorQuantity : public SimpleTriangleMeshColorQuantity { +public: + SimpleTriangleMeshVertexColorQuantity(std::string name, const std::vector& colors, + SimpleTriangleMesh& mesh); + + virtual void createProgram() override; }; + +// ======================================================== +// ========== Face Color ========== +// ======================================================== + +class SimpleTriangleMeshFaceColorQuantity : public SimpleTriangleMeshColorQuantity { +public: + SimpleTriangleMeshFaceColorQuantity(std::string name, const std::vector& colors, + SimpleTriangleMesh& mesh); + + virtual void createProgram() override; +}; + + } // namespace polyscope diff --git a/include/polyscope/simple_triangle_mesh_scalar_quantity.h b/include/polyscope/simple_triangle_mesh_scalar_quantity.h index 729b24a71..cae5277d4 100644 --- a/include/polyscope/simple_triangle_mesh_scalar_quantity.h +++ b/include/polyscope/simple_triangle_mesh_scalar_quantity.h @@ -22,12 +22,39 @@ class SimpleTriangleMeshScalarQuantity : public SimpleTriangleMeshQuantity, virtual void refresh() override; virtual std::string niceName() override; + const std::string definedOn; protected: - std::string definedOn; std::shared_ptr program; - void createProgram(); + virtual void createProgram() = 0; }; + +// ======================================================== +// ========== Vertex Scalar ========== +// ======================================================== + +class SimpleTriangleMeshVertexScalarQuantity : public SimpleTriangleMeshScalarQuantity { +public: + SimpleTriangleMeshVertexScalarQuantity(std::string name, const std::vector& values, SimpleTriangleMesh& mesh, + DataType dataType = DataType::STANDARD); + + virtual void createProgram() override; +}; + + +// ======================================================== +// ========== Face Scalar ========== +// ======================================================== + +class SimpleTriangleMeshFaceScalarQuantity : public SimpleTriangleMeshScalarQuantity { +public: + SimpleTriangleMeshFaceScalarQuantity(std::string name, const std::vector& values, SimpleTriangleMesh& mesh, + DataType dataType = DataType::STANDARD); + + virtual void createProgram() override; +}; + + } // namespace polyscope diff --git a/src/render/mock_opengl/mock_gl_engine.cpp b/src/render/mock_opengl/mock_gl_engine.cpp index ef4877e22..a1a77de28 100644 --- a/src/render/mock_opengl/mock_gl_engine.cpp +++ b/src/render/mock_opengl/mock_gl_engine.cpp @@ -2233,6 +2233,8 @@ void MockGLEngine::populateDefaultShadersAndRules() { registerShaderRule("MESH_PROPAGATE_TYPE_AND_BASECOLOR2_SHADE", MESH_PROPAGATE_TYPE_AND_BASECOLOR2_SHADE); registerShaderRule("MESH_PROPAGATE_PICK", MESH_PROPAGATE_PICK); registerShaderRule("MESH_PROPAGATE_PICK_SIMPLE", MESH_PROPAGATE_PICK_SIMPLE); + registerShaderRule("SIMPLE_MESH_PROPAGATE_FACE_VALUE", SIMPLE_MESH_PROPAGATE_FACE_VALUE); + registerShaderRule("SIMPLE_MESH_PROPAGATE_FACE_COLOR", SIMPLE_MESH_PROPAGATE_FACE_COLOR); // volume gridcube things registerShaderRule("GRIDCUBE_PROPAGATE_NODE_VALUE", GRIDCUBE_PROPAGATE_NODE_VALUE); diff --git a/src/render/opengl/gl_engine.cpp b/src/render/opengl/gl_engine.cpp index 608f01330..c6ea0377f 100644 --- a/src/render/opengl/gl_engine.cpp +++ b/src/render/opengl/gl_engine.cpp @@ -2716,6 +2716,8 @@ void GLEngine::populateDefaultShadersAndRules() { registerShaderRule("MESH_PROPAGATE_TYPE_AND_BASECOLOR2_SHADE", MESH_PROPAGATE_TYPE_AND_BASECOLOR2_SHADE); registerShaderRule("MESH_PROPAGATE_PICK", MESH_PROPAGATE_PICK); registerShaderRule("MESH_PROPAGATE_PICK_SIMPLE", MESH_PROPAGATE_PICK_SIMPLE); + registerShaderRule("SIMPLE_MESH_PROPAGATE_FACE_VALUE", SIMPLE_MESH_PROPAGATE_FACE_VALUE); + registerShaderRule("SIMPLE_MESH_PROPAGATE_FACE_COLOR", SIMPLE_MESH_PROPAGATE_FACE_COLOR); // volume gridcube things registerShaderRule("GRIDCUBE_PROPAGATE_NODE_VALUE", GRIDCUBE_PROPAGATE_NODE_VALUE); diff --git a/src/render/opengl/shaders/surface_mesh_shaders.cpp b/src/render/opengl/shaders/surface_mesh_shaders.cpp index 860cb2fc1..fae610877 100644 --- a/src/render/opengl/shaders/surface_mesh_shaders.cpp +++ b/src/render/opengl/shaders/surface_mesh_shaders.cpp @@ -731,6 +731,40 @@ const ShaderReplacementRule MESH_PROPAGATE_PICK_SIMPLE ( // this one does faces ); +// Uses gl_PrimitiveID to look up per-face scalar data from a 1D texture. +// Requires indexed triangle drawing (DrawMode::IndexedTriangles) where primitive index == face index. +const ShaderReplacementRule SIMPLE_MESH_PROPAGATE_FACE_VALUE( + /* rule name */ "SIMPLE_MESH_PROPAGATE_FACE_VALUE", + { /* replacement sources */ + {"FRAG_DECLARATIONS", R"( + uniform sampler1D t_faceValues; + )"}, + {"GENERATE_SHADE_VALUE", R"( + float shadeValue = texelFetch(t_faceValues, gl_PrimitiveID, 0).r; + )"}, + }, + /* uniforms */ {}, + /* attributes */ {}, + /* textures */ {{"t_faceValues", 1}} +); + +// Uses gl_PrimitiveID to look up per-face color data from a 1D texture. +// Requires indexed triangle drawing (DrawMode::IndexedTriangles) where primitive index == face index. +const ShaderReplacementRule SIMPLE_MESH_PROPAGATE_FACE_COLOR( + /* rule name */ "SIMPLE_MESH_PROPAGATE_FACE_COLOR", + { /* replacement sources */ + {"FRAG_DECLARATIONS", R"( + uniform sampler1D t_faceColors; + )"}, + {"GENERATE_SHADE_VALUE", R"( + vec3 shadeColor = texelFetch(t_faceColors, gl_PrimitiveID, 0).rgb; + )"}, + }, + /* uniforms */ {}, + /* attributes */ {}, + /* textures */ {{"t_faceColors", 1}} +); + // clang-format on } // namespace backend_openGL3 diff --git a/src/simple_triangle_mesh.cpp b/src/simple_triangle_mesh.cpp index c7f3f626a..0fb563eec 100644 --- a/src/simple_triangle_mesh.cpp +++ b/src/simple_triangle_mesh.cpp @@ -355,19 +355,34 @@ glm::vec3 SimpleTriangleMesh::getBackFaceColor() { return backFaceColor.get(); } // === Quantity adder implementations -SimpleTriangleMeshScalarQuantity* SimpleTriangleMesh::addVertexScalarQuantityImpl(std::string name, - const std::vector& data, - DataType type) { +SimpleTriangleMeshVertexScalarQuantity* +SimpleTriangleMesh::addVertexScalarQuantityImpl(std::string name, const std::vector& data, DataType type) { checkForQuantityWithNameAndDeleteOrError(name); - SimpleTriangleMeshScalarQuantity* q = new SimpleTriangleMeshScalarQuantity(name, data, "vertex", *this, type); + SimpleTriangleMeshVertexScalarQuantity* q = new SimpleTriangleMeshVertexScalarQuantity(name, data, *this, type); addQuantity(q); return q; } -SimpleTriangleMeshColorQuantity* SimpleTriangleMesh::addVertexColorQuantityImpl(std::string name, - const std::vector& colors) { +SimpleTriangleMeshFaceScalarQuantity* +SimpleTriangleMesh::addFaceScalarQuantityImpl(std::string name, const std::vector& data, DataType type) { checkForQuantityWithNameAndDeleteOrError(name); - SimpleTriangleMeshColorQuantity* q = new SimpleTriangleMeshColorQuantity(name, colors, "vertex", *this); + SimpleTriangleMeshFaceScalarQuantity* q = new SimpleTriangleMeshFaceScalarQuantity(name, data, *this, type); + addQuantity(q); + return q; +} + +SimpleTriangleMeshVertexColorQuantity* +SimpleTriangleMesh::addVertexColorQuantityImpl(std::string name, const std::vector& colors) { + checkForQuantityWithNameAndDeleteOrError(name); + SimpleTriangleMeshVertexColorQuantity* q = new SimpleTriangleMeshVertexColorQuantity(name, colors, *this); + addQuantity(q); + return q; +} + +SimpleTriangleMeshFaceColorQuantity* +SimpleTriangleMesh::addFaceColorQuantityImpl(std::string name, const std::vector& colors) { + checkForQuantityWithNameAndDeleteOrError(name); + SimpleTriangleMeshFaceColorQuantity* q = new SimpleTriangleMeshFaceColorQuantity(name, colors, *this); addQuantity(q); return q; } diff --git a/src/simple_triangle_mesh_color_quantity.cpp b/src/simple_triangle_mesh_color_quantity.cpp index 2342c204f..8d40bba46 100644 --- a/src/simple_triangle_mesh_color_quantity.cpp +++ b/src/simple_triangle_mesh_color_quantity.cpp @@ -9,6 +9,10 @@ namespace polyscope { +// ======================================================== +// ========== Color Quantity Base ========== +// ======================================================== + SimpleTriangleMeshColorQuantity::SimpleTriangleMeshColorQuantity(std::string name, const std::vector& colors_, std::string definedOn_, @@ -40,7 +44,24 @@ void SimpleTriangleMeshColorQuantity::buildCustomUI() { buildColorUI(); } -void SimpleTriangleMeshColorQuantity::createProgram() { +void SimpleTriangleMeshColorQuantity::refresh() { + program.reset(); + Quantity::refresh(); +} + +std::string SimpleTriangleMeshColorQuantity::niceName() { return name + " (" + definedOn + " color)"; } + + +// ======================================================== +// ========== Vertex Color ========== +// ======================================================== + +SimpleTriangleMeshVertexColorQuantity::SimpleTriangleMeshVertexColorQuantity(std::string name, + const std::vector& colors_, + SimpleTriangleMesh& mesh_) + : SimpleTriangleMeshColorQuantity(name, colors_, "vertex", mesh_) {} + +void SimpleTriangleMeshVertexColorQuantity::createProgram() { // clang-format off program = render::engine->requestShader("SIMPLE_MESH", render::engine->addMaterialRules(parent.getMaterial(), @@ -58,11 +79,34 @@ void SimpleTriangleMeshColorQuantity::createProgram() { render::engine->setMaterial(*program, parent.getMaterial()); } -void SimpleTriangleMeshColorQuantity::refresh() { - program.reset(); - Quantity::refresh(); + +// ======================================================== +// ========== Face Color ========== +// ======================================================== + +SimpleTriangleMeshFaceColorQuantity::SimpleTriangleMeshFaceColorQuantity(std::string name, + const std::vector& colors_, + SimpleTriangleMesh& mesh_) + : SimpleTriangleMeshColorQuantity(name, colors_, "face", mesh_) { + colors.setTextureSize(parent.nFaces()); } -std::string SimpleTriangleMeshColorQuantity::niceName() { return name + " (" + definedOn + " color)"; } +void SimpleTriangleMeshFaceColorQuantity::createProgram() { + // clang-format off + program = render::engine->requestShader("SIMPLE_MESH", + render::engine->addMaterialRules(parent.getMaterial(), + addColorRules( + parent.addSimpleTriangleMeshRules( + {"SIMPLE_MESH_PROPAGATE_FACE_COLOR", "SHADE_COLOR"} + ) + ) + ) + ); + // clang-format on + + parent.setSimpleTriangleMeshProgramGeometryAttributes(*program); + program->setTextureFromBuffer("t_faceColors", colors.getRenderTextureBuffer().get()); + render::engine->setMaterial(*program, parent.getMaterial()); +} } // namespace polyscope diff --git a/src/simple_triangle_mesh_scalar_quantity.cpp b/src/simple_triangle_mesh_scalar_quantity.cpp index bc3026d8b..e13a2c947 100644 --- a/src/simple_triangle_mesh_scalar_quantity.cpp +++ b/src/simple_triangle_mesh_scalar_quantity.cpp @@ -9,11 +9,14 @@ namespace polyscope { +// ======================================================== +// ========== Scalar Quantity Base ========== +// ======================================================== + SimpleTriangleMeshScalarQuantity::SimpleTriangleMeshScalarQuantity(std::string name, const std::vector& values_, - std::string definedOn_, - SimpleTriangleMesh& mesh_, DataType dataType_) - : SimpleTriangleMeshQuantity(name, mesh_, true), ScalarQuantity(*this, values_, dataType_), - definedOn(definedOn_) {} + std::string definedOn_, SimpleTriangleMesh& mesh_, + DataType dataType_) + : SimpleTriangleMeshQuantity(name, mesh_, true), ScalarQuantity(*this, values_, dataType_), definedOn(definedOn_) {} void SimpleTriangleMeshScalarQuantity::draw() { if (!isEnabled()) return; @@ -40,7 +43,25 @@ void SimpleTriangleMeshScalarQuantity::buildCustomUI() { buildScalarUI(); } -void SimpleTriangleMeshScalarQuantity::createProgram() { +void SimpleTriangleMeshScalarQuantity::refresh() { + program.reset(); + Quantity::refresh(); +} + +std::string SimpleTriangleMeshScalarQuantity::niceName() { return name + " (" + definedOn + " scalar)"; } + + +// ======================================================== +// ========== Vertex Scalar ========== +// ======================================================== + +SimpleTriangleMeshVertexScalarQuantity::SimpleTriangleMeshVertexScalarQuantity(std::string name, + const std::vector& values_, + SimpleTriangleMesh& mesh_, + DataType dataType_) + : SimpleTriangleMeshScalarQuantity(name, values_, "vertex", mesh_, dataType_) {} + +void SimpleTriangleMeshVertexScalarQuantity::createProgram() { // clang-format off program = render::engine->requestShader("SIMPLE_MESH", render::engine->addMaterialRules(parent.getMaterial(), @@ -59,11 +80,36 @@ void SimpleTriangleMeshScalarQuantity::createProgram() { render::engine->setMaterial(*program, parent.getMaterial()); } -void SimpleTriangleMeshScalarQuantity::refresh() { - program.reset(); - Quantity::refresh(); + +// ======================================================== +// ========== Face Scalar ========== +// ======================================================== + +SimpleTriangleMeshFaceScalarQuantity::SimpleTriangleMeshFaceScalarQuantity(std::string name, + const std::vector& values_, + SimpleTriangleMesh& mesh_, + DataType dataType_) + : SimpleTriangleMeshScalarQuantity(name, values_, "face", mesh_, dataType_) { + values.setTextureSize(parent.nFaces()); } -std::string SimpleTriangleMeshScalarQuantity::niceName() { return name + " (" + definedOn + " scalar)"; } +void SimpleTriangleMeshFaceScalarQuantity::createProgram() { + // clang-format off + program = render::engine->requestShader("SIMPLE_MESH", + render::engine->addMaterialRules(parent.getMaterial(), + parent.addSimpleTriangleMeshRules( + addScalarRules( + {"SIMPLE_MESH_PROPAGATE_FACE_VALUE"} + ) + ) + ) + ); + // clang-format on + + parent.setSimpleTriangleMeshProgramGeometryAttributes(*program); + program->setTextureFromBuffer("t_faceValues", values.getRenderTextureBuffer().get()); + program->setTextureFromColormap("t_colormap", cMap.get()); + render::engine->setMaterial(*program, parent.getMaterial()); +} } // namespace polyscope diff --git a/test/src/surface_mesh_test.cpp b/test/src/surface_mesh_test.cpp index bbef85700..ed2d34024 100644 --- a/test/src/surface_mesh_test.cpp +++ b/test/src/surface_mesh_test.cpp @@ -701,3 +701,29 @@ TEST_F(PolyscopeTest, SimpleTriangleMeshVertexColor) { polyscope::removeAllStructures(); } + +TEST_F(PolyscopeTest, SimpleTriangleMeshFaceScalar) { + auto psMesh = registerSimpleTriangleMesh(); + std::vector fScalar(psMesh->nFaces(), 7.); + auto q1 = psMesh->addFaceScalarQuantity("fScalar", fScalar); + q1->setEnabled(true); + polyscope::show(3); + + q1->updateData(fScalar); + polyscope::show(3); + + polyscope::removeAllStructures(); +} + +TEST_F(PolyscopeTest, SimpleTriangleMeshFaceColor) { + auto psMesh = registerSimpleTriangleMesh(); + std::vector fColors(psMesh->nFaces(), glm::vec3{.2, .3, .4}); + auto q1 = psMesh->addFaceColorQuantity("fColor", fColors); + q1->setEnabled(true); + polyscope::show(3); + + q1->updateData(fColors); + polyscope::show(3); + + polyscope::removeAllStructures(); +} From a4be2fe0dc9febc05b9170832c5ed2ff5de2adb2 Mon Sep 17 00:00:00 2001 From: Nicholas Sharp Date: Sun, 5 Apr 2026 21:28:10 -0700 Subject: [PATCH 4/4] clean up quantities, implement picking for SimpleTriangleMesh --- include/polyscope/pick.ipp | 50 ++++++ .../opengl/shaders/surface_mesh_shaders.h | 1 + include/polyscope/simple_triangle_mesh.h | 14 +- .../simple_triangle_mesh_color_quantity.h | 3 + .../polyscope/simple_triangle_mesh_quantity.h | 7 +- .../simple_triangle_mesh_scalar_quantity.h | 2 + src/render/mock_opengl/mock_gl_engine.cpp | 3 +- src/render/opengl/gl_engine.cpp | 1 + src/render/opengl/shaders/common.cpp | 13 +- .../opengl/shaders/surface_mesh_shaders.cpp | 22 +++ src/simple_triangle_mesh.cpp | 153 ++++++++++++++++-- src/simple_triangle_mesh_color_quantity.cpp | 28 +++- src/simple_triangle_mesh_scalar_quantity.cpp | 14 ++ test/src/misc_test.cpp | 37 +++++ 14 files changed, 323 insertions(+), 25 deletions(-) diff --git a/include/polyscope/pick.ipp b/include/polyscope/pick.ipp index 9d4a34767..da4b32997 100644 --- a/include/polyscope/pick.ipp +++ b/include/polyscope/pick.ipp @@ -13,6 +13,8 @@ const uint64_t bitsForPickPacking = 22; inline glm::vec3 indToVec(size_t globalInd) { + // NOTE: there is a duplicate version of this logic in a macro below, which must be kept in sync. + // Can comfortably fit a 22 bit integer exactly in a single precision float uint64_t factor = 1 << bitsForPickPacking; uint64_t mask = factor - 1; @@ -48,5 +50,53 @@ inline uint64_t vecToInd(glm::vec3 vec) { return ind; } + +// == Weird alternate implementation of indToVec() +// This is here because sometimes we want to evaluate the indToVec() bit-bashing logic in a shader, and get exactly +// the same result as the C++ version above. A GLSL implementation is not too bad, but it's hard to test to ensure +// it really matches. +// +// The solution here is a funky macro'd implementation, that compiles as C++ or GLSL. We compile it as C++ in the tests +// and verify it matches the usual indToVec() implementation, then compile it as GLSL in shaders. +// +// All of the macro stuff you see below is just boilerplate to make that possible. + +#define POLYSCOPE_PICK_STR_H(...) #__VA_ARGS__ +#define POLYSCOPE_PICK_STR(...) POLYSCOPE_PICK_STR_H(__VA_ARGS__) + +// See note above. This is logic that is meant to be identical to indToVec(). +// clang-format off +#define POLYSCOPE_PICK_INDEX_COLOR_BODY \ + POLYSCOPE_PICK_UINT idxLow = pickStartLow + primID; \ + POLYSCOPE_PICK_UINT carry = (idxLow < pickStartLow) ? 1u : 0u; \ + POLYSCOPE_PICK_UINT idxHigh = pickStartHigh + carry; \ + POLYSCOPE_PICK_UINT low22 = idxLow & 0x3FFFFFu; \ + POLYSCOPE_PICK_UINT med22 = ((idxLow >> 22u) | (idxHigh << 10u)) & 0x3FFFFFu; \ + POLYSCOPE_PICK_UINT high22 = (idxHigh >> 12u) & 0x3FFFFFu; \ + return POLYSCOPE_PICK_VEC3(float(low22), float(med22), float(high22)) / 4194304.0f; +// clang-format on + +// C++ version: compile the body with C++ types. +#define POLYSCOPE_PICK_UINT uint32_t +#define POLYSCOPE_PICK_VEC3 glm::vec3 +inline glm::vec3 pickIndexToColorImpl(uint32_t pickStartLow, uint32_t pickStartHigh, uint32_t primID) { + POLYSCOPE_PICK_INDEX_COLOR_BODY +} +#undef POLYSCOPE_PICK_UINT +#undef POLYSCOPE_PICK_VEC3 + +// Convenience wrapper for C++ callers that have a combined uint64_t pickStart. +inline glm::vec3 pickIndexToColor(uint64_t pickStart, uint32_t primID) { + return pickIndexToColorImpl(static_cast(pickStart & 0xFFFFFFFFull), static_cast(pickStart >> 32), + primID); +} + +// GLSL string macro. Stringifies POLYSCOPE_PICK_INDEX_COLOR_BODY using GLSL type names. +// REQUIRES: POLYSCOPE_PICK_UINT must equal "uint" and POLYSCOPE_PICK_VEC3 must equal "vec3" +// at the point of expansion (see common.cpp for the usage pattern). +#define POLYSCOPE_PICK_INDEX_TO_COLOR_GLSL \ + "vec3 pickIndexToColor(uint pickStartLow, uint pickStartHigh, uint primID) { " POLYSCOPE_PICK_STR( \ + POLYSCOPE_PICK_INDEX_COLOR_BODY) " }" + } // namespace pick } // namespace polyscope diff --git a/include/polyscope/render/opengl/shaders/surface_mesh_shaders.h b/include/polyscope/render/opengl/shaders/surface_mesh_shaders.h index b5e9484b2..9185b6c16 100644 --- a/include/polyscope/render/opengl/shaders/surface_mesh_shaders.h +++ b/include/polyscope/render/opengl/shaders/surface_mesh_shaders.h @@ -38,6 +38,7 @@ extern const ShaderReplacementRule MESH_PROPAGATE_PICK_SIMPLE; extern const ShaderReplacementRule MESH_PROPAGATE_TYPE_AND_BASECOLOR2_SHADE; extern const ShaderReplacementRule SIMPLE_MESH_PROPAGATE_FACE_VALUE; extern const ShaderReplacementRule SIMPLE_MESH_PROPAGATE_FACE_COLOR; +extern const ShaderReplacementRule SIMPLE_MESH_PROPAGATE_FACE_PICK; } // namespace backend_openGL3 diff --git a/include/polyscope/simple_triangle_mesh.h b/include/polyscope/simple_triangle_mesh.h index 51a066971..4f022d358 100644 --- a/include/polyscope/simple_triangle_mesh.h +++ b/include/polyscope/simple_triangle_mesh.h @@ -10,6 +10,7 @@ #include "polyscope/scaled_value.h" #include "polyscope/standardize_data_array.h" #include "polyscope/structure.h" +#include "polyscope/types.h" #include "polyscope/simple_triangle_mesh_color_quantity.h" #include "polyscope/simple_triangle_mesh_quantity.h" @@ -31,7 +32,8 @@ class SimpleTriangleMeshVertexColorQuantity; class SimpleTriangleMeshFaceColorQuantity; struct SimpleTriangleMeshPickResult { - // this does nothing for now, just matching pattern from other structures + MeshElement elementType = MeshElement::FACE; // which kind of element was clicked + int64_t index = -1; // index of the clicked element (vertex or face) }; class SimpleTriangleMesh : public Structure { @@ -114,6 +116,10 @@ class SimpleTriangleMesh : public Structure { SimpleTriangleMesh* setBackFacePolicy(BackFacePolicy newPolicy); BackFacePolicy getBackFacePolicy(); + // Selection mode (controls vertex vs face pick threshold) + SimpleTriangleMesh* setSelectionMode(MeshSelectionMode newMode); + MeshSelectionMode getSelectionMode(); + // Rendering helpers used by quantities void setSimpleTriangleMeshUniforms(render::ShaderProgram& p, bool withSurfaceShade = true); void setSimpleTriangleMeshProgramGeometryAttributes(render::ShaderProgram& p); @@ -132,6 +138,7 @@ class SimpleTriangleMesh : public Structure { PersistentValue material; PersistentValue backFacePolicy; PersistentValue backFaceColor; + PersistentValue selectionMode; // Drawing related things // if nullptr, prepare() (resp. preparePick()) needs to be called @@ -142,7 +149,7 @@ class SimpleTriangleMesh : public Structure { // Do setup work related to drawing, including allocating openGL data void ensureRenderProgramPrepared(); void ensurePickProgramPrepared(); - void setPickUniforms(render::ShaderProgram& p); + void setPickUniforms(render::ShaderProgram& p); // sets u_pickStartLow/High for SIMPLE_MESH_PROPAGATE_FACE_PICK // === Quantity adder implementations SimpleTriangleMeshVertexScalarQuantity* addVertexScalarQuantityImpl(std::string name, const std::vector& data, @@ -154,8 +161,7 @@ class SimpleTriangleMesh : public Structure { SimpleTriangleMeshFaceColorQuantity* addFaceColorQuantityImpl(std::string name, const std::vector& colors); // == Picking related things - size_t pickStart; - glm::vec3 pickColor; + size_t pickStart = 0; }; diff --git a/include/polyscope/simple_triangle_mesh_color_quantity.h b/include/polyscope/simple_triangle_mesh_color_quantity.h index 7e8bce09a..d3b8d352b 100644 --- a/include/polyscope/simple_triangle_mesh_color_quantity.h +++ b/include/polyscope/simple_triangle_mesh_color_quantity.h @@ -17,6 +17,7 @@ class SimpleTriangleMeshColorQuantity : public SimpleTriangleMeshQuantity, virtual void draw() override; virtual void buildCustomUI() override; + virtual void buildColorOptionsUI() override; virtual void refresh() override; virtual std::string niceName() override; @@ -39,6 +40,7 @@ class SimpleTriangleMeshVertexColorQuantity : public SimpleTriangleMeshColorQuan SimpleTriangleMesh& mesh); virtual void createProgram() override; + virtual void buildVertexInfoGUI(size_t vInd) override; }; @@ -52,6 +54,7 @@ class SimpleTriangleMeshFaceColorQuantity : public SimpleTriangleMeshColorQuanti SimpleTriangleMesh& mesh); virtual void createProgram() override; + virtual void buildFaceInfoGUI(size_t fInd) override; }; diff --git a/include/polyscope/simple_triangle_mesh_quantity.h b/include/polyscope/simple_triangle_mesh_quantity.h index 6df4e0e45..ab8a0116c 100644 --- a/include/polyscope/simple_triangle_mesh_quantity.h +++ b/include/polyscope/simple_triangle_mesh_quantity.h @@ -12,9 +12,14 @@ class SimpleTriangleMesh; class SimpleTriangleMeshQuantity : public Quantity { public: SimpleTriangleMeshQuantity(std::string name, SimpleTriangleMesh& parentStructure, bool dominates = false); - virtual ~SimpleTriangleMeshQuantity(){}; + virtual ~SimpleTriangleMeshQuantity() {}; SimpleTriangleMesh& parent; // shadows and hides the generic member in Quantity + + // Called by buildPickUI() to display this quantity's value for the selected element. + // Override in vertex quantities (for vertex picks) and face quantities (for face picks). + virtual void buildVertexInfoGUI(size_t vInd) {} + virtual void buildFaceInfoGUI(size_t fInd) {} }; } // namespace polyscope diff --git a/include/polyscope/simple_triangle_mesh_scalar_quantity.h b/include/polyscope/simple_triangle_mesh_scalar_quantity.h index cae5277d4..9a7623c02 100644 --- a/include/polyscope/simple_triangle_mesh_scalar_quantity.h +++ b/include/polyscope/simple_triangle_mesh_scalar_quantity.h @@ -41,6 +41,7 @@ class SimpleTriangleMeshVertexScalarQuantity : public SimpleTriangleMeshScalarQu DataType dataType = DataType::STANDARD); virtual void createProgram() override; + virtual void buildVertexInfoGUI(size_t vInd) override; }; @@ -54,6 +55,7 @@ class SimpleTriangleMeshFaceScalarQuantity : public SimpleTriangleMeshScalarQuan DataType dataType = DataType::STANDARD); virtual void createProgram() override; + virtual void buildFaceInfoGUI(size_t fInd) override; }; diff --git a/src/render/mock_opengl/mock_gl_engine.cpp b/src/render/mock_opengl/mock_gl_engine.cpp index a1a77de28..adec30c3a 100644 --- a/src/render/mock_opengl/mock_gl_engine.cpp +++ b/src/render/mock_opengl/mock_gl_engine.cpp @@ -2235,7 +2235,8 @@ void MockGLEngine::populateDefaultShadersAndRules() { registerShaderRule("MESH_PROPAGATE_PICK_SIMPLE", MESH_PROPAGATE_PICK_SIMPLE); registerShaderRule("SIMPLE_MESH_PROPAGATE_FACE_VALUE", SIMPLE_MESH_PROPAGATE_FACE_VALUE); registerShaderRule("SIMPLE_MESH_PROPAGATE_FACE_COLOR", SIMPLE_MESH_PROPAGATE_FACE_COLOR); - + registerShaderRule("SIMPLE_MESH_PROPAGATE_FACE_PICK", SIMPLE_MESH_PROPAGATE_FACE_PICK); + // volume gridcube things registerShaderRule("GRIDCUBE_PROPAGATE_NODE_VALUE", GRIDCUBE_PROPAGATE_NODE_VALUE); registerShaderRule("GRIDCUBE_PROPAGATE_CELL_VALUE", GRIDCUBE_PROPAGATE_CELL_VALUE); diff --git a/src/render/opengl/gl_engine.cpp b/src/render/opengl/gl_engine.cpp index c6ea0377f..3ee383b87 100644 --- a/src/render/opengl/gl_engine.cpp +++ b/src/render/opengl/gl_engine.cpp @@ -2718,6 +2718,7 @@ void GLEngine::populateDefaultShadersAndRules() { registerShaderRule("MESH_PROPAGATE_PICK_SIMPLE", MESH_PROPAGATE_PICK_SIMPLE); registerShaderRule("SIMPLE_MESH_PROPAGATE_FACE_VALUE", SIMPLE_MESH_PROPAGATE_FACE_VALUE); registerShaderRule("SIMPLE_MESH_PROPAGATE_FACE_COLOR", SIMPLE_MESH_PROPAGATE_FACE_COLOR); + registerShaderRule("SIMPLE_MESH_PROPAGATE_FACE_PICK", SIMPLE_MESH_PROPAGATE_FACE_PICK); // volume gridcube things registerShaderRule("GRIDCUBE_PROPAGATE_NODE_VALUE", GRIDCUBE_PROPAGATE_NODE_VALUE); diff --git a/src/render/opengl/shaders/common.cpp b/src/render/opengl/shaders/common.cpp index 58f985511..ac7a1aed2 100644 --- a/src/render/opengl/shaders/common.cpp +++ b/src/render/opengl/shaders/common.cpp @@ -3,6 +3,8 @@ #include "polyscope/render/opengl/shaders/common.h" +#include "polyscope/pick.h" // for POLYSCOPE_PICK_INDEX_TO_COLOR_GLSL + namespace polyscope { namespace render { namespace backend_openGL3 { @@ -437,6 +439,15 @@ bool rayTaperedCylinderIntersection(vec3 rayStart, vec3 rayDir, vec3 cylTail, ve } +)" +// Define GLSL type names so POLYSCOPE_PICK_INDEX_COLOR_BODY stringifies as GLSL (see pick.ipp). +#define POLYSCOPE_PICK_UINT uint +#define POLYSCOPE_PICK_VEC3 vec3 + POLYSCOPE_PICK_INDEX_TO_COLOR_GLSL +#undef POLYSCOPE_PICK_UINT +#undef POLYSCOPE_PICK_VEC3 + R"( + bool rayConeIntersection(vec3 rayStart, vec3 rayDir, vec3 coneBase, vec3 coneTip, float coneRad, out float tHit, out vec3 pHit, out vec3 nHit) { rayDir = normalize(rayDir); @@ -510,6 +521,6 @@ bool rayConeIntersection(vec3 rayStart, vec3 rayDir, vec3 coneBase, vec3 coneTip )"; -} +} // namespace backend_openGL3 } // namespace render } // namespace polyscope diff --git a/src/render/opengl/shaders/surface_mesh_shaders.cpp b/src/render/opengl/shaders/surface_mesh_shaders.cpp index fae610877..6b842c6a0 100644 --- a/src/render/opengl/shaders/surface_mesh_shaders.cpp +++ b/src/render/opengl/shaders/surface_mesh_shaders.cpp @@ -765,6 +765,28 @@ const ShaderReplacementRule SIMPLE_MESH_PROPAGATE_FACE_COLOR( /* textures */ {{"t_faceColors", 1}} ); +// Encodes the pick color for each face directly from gl_PrimitiveID + pickStart, with no texture lookup. +// Delegates encoding to pickIndexToColor() in common.cpp (see there for the bit layout). +const ShaderReplacementRule SIMPLE_MESH_PROPAGATE_FACE_PICK( + /* rule name */ "SIMPLE_MESH_PROPAGATE_FACE_PICK", + { /* replacement sources */ + {"FRAG_DECLARATIONS", R"( + uniform uint u_pickStartLow; // lower 32 bits of the global pick index for face 0 + uniform uint u_pickStartHigh; // upper 32 bits of the global pick index for face 0 + vec3 pickIndexToColor(uint pickStartLow, uint pickStartHigh, uint primID); // defined in common.cpp + )"}, + {"GENERATE_SHADE_VALUE", R"( + vec3 shadeColor = pickIndexToColor(u_pickStartLow, u_pickStartHigh, uint(gl_PrimitiveID)); + )"}, + }, + /* uniforms */ { + {"u_pickStartLow", RenderDataType::UInt}, + {"u_pickStartHigh", RenderDataType::UInt}, + }, + /* attributes */ {}, + /* textures */ {} +); + // clang-format on } // namespace backend_openGL3 diff --git a/src/simple_triangle_mesh.cpp b/src/simple_triangle_mesh.cpp index 0fb563eec..cfd8d103c 100644 --- a/src/simple_triangle_mesh.cpp +++ b/src/simple_triangle_mesh.cpp @@ -7,6 +7,7 @@ #include "polyscope/render/engine.h" #include "polyscope/simple_triangle_mesh_color_quantity.h" #include "polyscope/simple_triangle_mesh_scalar_quantity.h" +#include "polyscope/view.h" #include "imgui.h" @@ -30,7 +31,8 @@ SimpleTriangleMesh::SimpleTriangleMesh(std::string name, std::vector surfaceColor(uniquePrefix() + "surfaceColor", getNextUniqueColor()), material(uniquePrefix() + "material", "clay"), backFacePolicy(uniquePrefix() + "backFacePolicy", BackFacePolicy::Different), - backFaceColor(uniquePrefix() + "backFaceColor", glm::vec3(1.f - surfaceColor.get().r, 1.f - surfaceColor.get().g, 1.f - surfaceColor.get().b)) + backFaceColor(uniquePrefix() + "backFaceColor", glm::vec3(1.f - surfaceColor.get().r, 1.f - surfaceColor.get().g, 1.f - surfaceColor.get().b)), + selectionMode(uniquePrefix() + "selectionMode", MeshSelectionMode::Auto) // clang-format on { cullWholeElements.setPassive(false); @@ -76,6 +78,17 @@ void SimpleTriangleMesh::buildCustomOptionsUI() { setBackFacePolicy(BackFacePolicy::Cull); ImGui::EndMenu(); } + + // Selection mode + if (ImGui::BeginMenu("Selection Mode")) { + if (ImGui::MenuItem("auto", NULL, selectionMode.get() == MeshSelectionMode::Auto)) + setSelectionMode(MeshSelectionMode::Auto); + if (ImGui::MenuItem("vertices only", NULL, selectionMode.get() == MeshSelectionMode::VerticesOnly)) + setSelectionMode(MeshSelectionMode::VerticesOnly); + if (ImGui::MenuItem("faces only", NULL, selectionMode.get() == MeshSelectionMode::FacesOnly)) + setSelectionMode(MeshSelectionMode::FacesOnly); + ImGui::EndMenu(); + } } @@ -207,23 +220,24 @@ void SimpleTriangleMesh::ensurePickProgramPrepared() { if (pickProgram) return; // clang-format off - pickProgram = render::engine->requestShader("SIMPLE_MESH", - addSimpleTriangleMeshRules( - { - "SHADECOLOR_FROM_UNIFORM" - } - , false), render::ShaderReplacementDefaults::Pick - ); + pickProgram = render::engine->requestShader("SIMPLE_MESH", + addSimpleTriangleMeshRules({"SIMPLE_MESH_PROPAGATE_FACE_PICK"}, false), + render::ShaderReplacementDefaults::Pick); // clang-format on setSimpleTriangleMeshProgramGeometryAttributes(*pickProgram); - // Request pick indices - pickStart = pick::requestPickBufferRange(this, 1); - pickColor = pick::indToVec(pickStart); + // Allocate one pick index per face + pickStart = pick::requestPickBufferRange(this, nFaces()); } -void SimpleTriangleMesh::setPickUniforms(render::ShaderProgram& p) { p.setUniform("u_color", pickColor); } +void SimpleTriangleMesh::setPickUniforms(render::ShaderProgram& p) { + // Encode pickStart as two uint uniforms for the shader's 64-bit index arithmetic. + // NOTE: must stay in sync with SIMPLE_MESH_PROPAGATE_FACE_PICK in surface_mesh_shaders.cpp + // and with pick::indToVec() / pick::bitsForPickPacking in pick.ipp. + p.setUniform("u_pickStartLow", static_cast(pickStart & 0xFFFFFFFFull)); + p.setUniform("u_pickStartHigh", static_cast(pickStart >> 32)); +} std::vector SimpleTriangleMesh::addSimpleTriangleMeshRules(std::vector initRules, bool withSurfaceShade) { @@ -297,14 +311,86 @@ void SimpleTriangleMesh::updateObjectSpaceBounds() { SimpleTriangleMeshPickResult SimpleTriangleMesh::interpretPickResult(const PickResult& rawResult) { if (rawResult.structure != this) { - // caller must ensure that the PickResult belongs to this structure - // by checking the structure pointer or name exception("called interpretPickResult(), but the pick result is not from this structure"); } SimpleTriangleMeshPickResult result; - // currently nothing + size_t localInd = rawResult.localIndex; + if (localInd >= nFaces()) { + // Shouldn't happen, but guard against stale pick data + return result; + } + + // Recover the 3 vertex positions of the clicked face + glm::uvec3 triInds = faces.getValue(localInd); + glm::vec3 pA = vertices.getValue(triInds[0]); + glm::vec3 pB = vertices.getValue(triInds[1]); + glm::vec3 pC = vertices.getValue(triInds[2]); + + // Compute barycentric coordinates of the pick via sub-triangle areas. + // Each lambda_i is the area of the sub-triangle formed by the other two vertices and the click point P, + // divided by the total. Clamp each area to be nonneg and finite, then normalise. If the total is zero, fall back to + // (1/3, 1/3, 1/3). + glm::vec3 P = rawResult.position; + + auto subArea = [](glm::vec3 u, glm::vec3 v, glm::vec3 w) -> float { + float a = glm::length(glm::cross(v - u, w - u)); + return (std::isfinite(a) && a >= 0.f) ? a : 0.f; + }; + + float aA = subArea(P, pB, pC); + float aB = subArea(pA, P, pC); + float aC = subArea(pA, pB, P); + float total = aA + aB + aC; + + float lambdaA, lambdaB, lambdaC; + if (total == 0.f) { + lambdaA = lambdaB = lambdaC = 1.f / 3.f; + } else { + lambdaA = aA / total; + lambdaB = aB / total; + lambdaC = aC / total; + } + + // Find the dominant vertex + size_t nearestLocalIdx = 0; + float maxLambda = lambdaA; + if (lambdaB > maxLambda) { + maxLambda = lambdaB; + nearestLocalIdx = 1; + } + if (lambdaC > maxLambda) { + maxLambda = lambdaC; + nearestLocalIdx = 2; + } + size_t nearestVertexIdx = triInds[nearestLocalIdx]; + + // Threshold: lambda must exceed this to count as a vertex click. + // Matches SurfaceMesh: Auto uses 1 - 0.2 = 0.8, VerticesOnly always picks vertex, FacesOnly never does. + float vertexPickThreshold; + switch (selectionMode.get()) { + case MeshSelectionMode::Auto: + vertexPickThreshold = 0.8f; + break; + case MeshSelectionMode::VerticesOnly: + vertexPickThreshold = 0.0f; + break; + case MeshSelectionMode::FacesOnly: + vertexPickThreshold = 2.0f; + break; + default: + vertexPickThreshold = 0.8f; + break; + } + + if (maxLambda > vertexPickThreshold) { + result.elementType = MeshElement::VERTEX; + result.index = static_cast(nearestVertexIdx); + } else { + result.elementType = MeshElement::FACE; + result.index = static_cast(localInd); + } return result; } @@ -312,7 +398,35 @@ SimpleTriangleMeshPickResult SimpleTriangleMesh::interpretPickResult(const PickR void SimpleTriangleMesh::buildPickUI(const PickResult& rawResult) { SimpleTriangleMeshPickResult result = interpretPickResult(rawResult); - // Do nothing for now, we just pick a single constant for the whole structure + if (result.index < 0) return; + + if (result.elementType == MeshElement::VERTEX) { + ImGui::TextUnformatted(("Vertex #" + std::to_string(result.index)).c_str()); + + ImGui::Spacing(); + ImGui::Indent(20.f); + ImGui::Columns(2); + ImGui::SetColumnWidth(0, ImGui::GetWindowWidth() / 3); + for (auto& x : quantities) { + SimpleTriangleMeshQuantity* q = static_cast(x.second.get()); + q->buildVertexInfoGUI(static_cast(result.index)); + } + ImGui::Columns(1); + ImGui::Indent(-20.f); + } else { + ImGui::TextUnformatted(("Face #" + std::to_string(result.index)).c_str()); + + ImGui::Spacing(); + ImGui::Indent(20.f); + ImGui::Columns(2); + ImGui::SetColumnWidth(0, ImGui::GetWindowWidth() / 3); + for (auto& x : quantities) { + SimpleTriangleMeshQuantity* q = static_cast(x.second.get()); + q->buildFaceInfoGUI(static_cast(result.index)); + } + ImGui::Columns(1); + ImGui::Indent(-20.f); + } } @@ -352,6 +466,13 @@ SimpleTriangleMesh* SimpleTriangleMesh::setBackFaceColor(glm::vec3 val) { glm::vec3 SimpleTriangleMesh::getBackFaceColor() { return backFaceColor.get(); } +SimpleTriangleMesh* SimpleTriangleMesh::setSelectionMode(MeshSelectionMode newMode) { + selectionMode = newMode; + requestRedraw(); + return this; +} +MeshSelectionMode SimpleTriangleMesh::getSelectionMode() { return selectionMode.get(); } + // === Quantity adder implementations diff --git a/src/simple_triangle_mesh_color_quantity.cpp b/src/simple_triangle_mesh_color_quantity.cpp index 8d40bba46..3300d9b5a 100644 --- a/src/simple_triangle_mesh_color_quantity.cpp +++ b/src/simple_triangle_mesh_color_quantity.cpp @@ -15,8 +15,7 @@ namespace polyscope { SimpleTriangleMeshColorQuantity::SimpleTriangleMeshColorQuantity(std::string name, const std::vector& colors_, - std::string definedOn_, - SimpleTriangleMesh& mesh_) + std::string definedOn_, SimpleTriangleMesh& mesh_) : SimpleTriangleMeshQuantity(name, mesh_, true), ColorQuantity(*this, colors_), definedOn(definedOn_) {} void SimpleTriangleMeshColorQuantity::draw() { @@ -32,6 +31,11 @@ void SimpleTriangleMeshColorQuantity::draw() { program->draw(); } +void SimpleTriangleMeshColorQuantity::buildColorOptionsUI() { + ColorQuantity::buildColorOptionsUI(); + ImGui::TextUnformatted("(no options available)"); +} + void SimpleTriangleMeshColorQuantity::buildCustomUI() { ImGui::SameLine(); if (ImGui::Button("Options")) { @@ -61,6 +65,16 @@ SimpleTriangleMeshVertexColorQuantity::SimpleTriangleMeshVertexColorQuantity(std SimpleTriangleMesh& mesh_) : SimpleTriangleMeshColorQuantity(name, colors_, "vertex", mesh_) {} +void SimpleTriangleMeshVertexColorQuantity::buildVertexInfoGUI(size_t vInd) { + ImGui::TextUnformatted(name.c_str()); + ImGui::NextColumn(); + glm::vec3 val = colors.getValue(vInd); + ImGui::ColorEdit3("", &val[0], ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoPicker); + ImGui::SameLine(); + ImGui::Text("%.3f, %.3f, %.3f", val.r, val.g, val.b); + ImGui::NextColumn(); +} + void SimpleTriangleMeshVertexColorQuantity::createProgram() { // clang-format off program = render::engine->requestShader("SIMPLE_MESH", @@ -91,6 +105,16 @@ SimpleTriangleMeshFaceColorQuantity::SimpleTriangleMeshFaceColorQuantity(std::st colors.setTextureSize(parent.nFaces()); } +void SimpleTriangleMeshFaceColorQuantity::buildFaceInfoGUI(size_t fInd) { + ImGui::TextUnformatted(name.c_str()); + ImGui::NextColumn(); + glm::vec3 val = colors.getValue(fInd); + ImGui::ColorEdit3("", &val[0], ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoPicker); + ImGui::SameLine(); + ImGui::Text("%.3f, %.3f, %.3f", val.r, val.g, val.b); + ImGui::NextColumn(); +} + void SimpleTriangleMeshFaceColorQuantity::createProgram() { // clang-format off program = render::engine->requestShader("SIMPLE_MESH", diff --git a/src/simple_triangle_mesh_scalar_quantity.cpp b/src/simple_triangle_mesh_scalar_quantity.cpp index e13a2c947..92de07484 100644 --- a/src/simple_triangle_mesh_scalar_quantity.cpp +++ b/src/simple_triangle_mesh_scalar_quantity.cpp @@ -61,6 +61,13 @@ SimpleTriangleMeshVertexScalarQuantity::SimpleTriangleMeshVertexScalarQuantity(s DataType dataType_) : SimpleTriangleMeshScalarQuantity(name, values_, "vertex", mesh_, dataType_) {} +void SimpleTriangleMeshVertexScalarQuantity::buildVertexInfoGUI(size_t vInd) { + ImGui::TextUnformatted(name.c_str()); + ImGui::NextColumn(); + ImGui::Text("%g", values.getValue(vInd)); + ImGui::NextColumn(); +} + void SimpleTriangleMeshVertexScalarQuantity::createProgram() { // clang-format off program = render::engine->requestShader("SIMPLE_MESH", @@ -93,6 +100,13 @@ SimpleTriangleMeshFaceScalarQuantity::SimpleTriangleMeshFaceScalarQuantity(std:: values.setTextureSize(parent.nFaces()); } +void SimpleTriangleMeshFaceScalarQuantity::buildFaceInfoGUI(size_t fInd) { + ImGui::TextUnformatted(name.c_str()); + ImGui::NextColumn(); + ImGui::Text("%g", values.getValue(fInd)); + ImGui::NextColumn(); +} + void SimpleTriangleMeshFaceScalarQuantity::createProgram() { // clang-format off program = render::engine->requestShader("SIMPLE_MESH", diff --git a/test/src/misc_test.cpp b/test/src/misc_test.cpp index c7b00c1c9..60d7dea4e 100644 --- a/test/src/misc_test.cpp +++ b/test/src/misc_test.cpp @@ -206,4 +206,41 @@ TEST_F(PolyscopeTest, TestSlicePlane) { polyscope::removeAllSlicePlanes(); polyscope::removeAllStructures(); +} + + +// ============================================================ +// =============== Pick index encoding +// ============================================================ + +TEST_F(PolyscopeTest, PickIndexEncoding) { + // See the note in pick.ipp. We have multiple implementations of the pick index decoding logic, + // and this verifies they produce identical output. + + // indToVec takes size_t; cast explicitly to avoid linker issues on platforms where + // size_t and uint64_t have different mangled names (e.g. arm64: ulong vs ulonglong). + auto check = [](uint64_t pickStart, uint32_t primID) { + glm::vec3 expected = polyscope::pick::indToVec(static_cast(pickStart + primID)); + glm::vec3 actual = polyscope::pick::pickIndexToColor(pickStart, primID); + EXPECT_FLOAT_EQ(actual.x, expected.x); + EXPECT_FLOAT_EQ(actual.y, expected.y); + EXPECT_FLOAT_EQ(actual.z, expected.z); + }; + + check(0, 0); + check(0, 1); + check(0, 42); + check(0, (1u << 22) - 1u); // max value in low22 word + check(0, (1u << 22)); // overflows into med22 + check(5'000'000, 0); // pickStart > 2^22 + check(0x0000'00FF'FF00'0000ull, 0); // high bits set + + // --- Carry cases: pickStartLow + primID overflows uint32 --- + // Carry from low32 into high32 (the `carry` variable in the body): + check(0xFFFF0000u, 0x10000u); // low: 0xFFFF0000 + 0x10000 wraps -> carry=1 + check(0xFFFFFFFFu, 1u); // extreme: low wraps to 0 exactly -> carry=1 + check(0xFFFFFFFFu, 0xFFFFFFFFu); // both max -> low=0xFFFFFFFE, carry=1 + // Carry with non-zero pickStartHigh (tests idxHigh = pickStartHigh + carry): + check(0x1'0000'0000ull, 0xFFFFFFFFu); // high=1, low wraps -> idxHigh = 1 + 1 = 2 + check(0x1'FFFF'0000ull, 0x10000u); // high=1 + carry from low wrap } \ No newline at end of file