diff --git a/examples/demo-app/demo_app.cpp b/examples/demo-app/demo_app.cpp index afe26351..8a2cae4c 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); @@ -186,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/pick.ipp b/include/polyscope/pick.ipp index 9d4a3476..da4b3299 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 d89d3433..9185b6c1 100644 --- a/include/polyscope/render/opengl/shaders/surface_mesh_shaders.h +++ b/include/polyscope/render/opengl/shaders/surface_mesh_shaders.h @@ -36,6 +36,9 @@ 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; +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 4b0c6041..4f022d35 100644 --- a/include/polyscope/simple_triangle_mesh.h +++ b/include/polyscope/simple_triangle_mesh.h @@ -8,7 +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/types.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 @@ -18,9 +24,16 @@ namespace polyscope { 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 + 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 { @@ -50,8 +63,27 @@ 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 + 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 + SimpleTriangleMeshVertexColorQuantity* addVertexColorQuantity(std::string name, const T& values); + + template + SimpleTriangleMeshFaceColorQuantity* addFaceColorQuantity(std::string name, const T& values); + // === Mutate template @@ -84,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); @@ -102,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 @@ -112,13 +149,19 @@ 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, + 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; - glm::vec3 pickColor; + size_t pickStart = 0; }; diff --git a/include/polyscope/simple_triangle_mesh.ipp b/include/polyscope/simple_triangle_mesh.ipp index dc284bef..fb3f11b2 100644 --- a/include/polyscope/simple_triangle_mesh.ipp +++ b/include/polyscope/simple_triangle_mesh.ipp @@ -40,6 +40,37 @@ void SimpleTriangleMesh::update(const V& newPositions, const F& newFaces) { faces.markHostBufferUpdated(); } +// ===================================================== +// ============== Quantities +// ===================================================== + +template +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 +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) { 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 00000000..d3b8d352 --- /dev/null +++ b/include/polyscope/simple_triangle_mesh_color_quantity.h @@ -0,0 +1,61 @@ +// 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 buildColorOptionsUI() override; + virtual void refresh() override; + virtual std::string niceName() override; + + const std::string definedOn; + +protected: + 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; + virtual void buildVertexInfoGUI(size_t vInd) override; +}; + + +// ======================================================== +// ========== Face Color ========== +// ======================================================== + +class SimpleTriangleMeshFaceColorQuantity : public SimpleTriangleMeshColorQuantity { +public: + SimpleTriangleMeshFaceColorQuantity(std::string name, const std::vector& colors, + SimpleTriangleMesh& mesh); + + virtual void createProgram() override; + virtual void buildFaceInfoGUI(size_t fInd) override; +}; + + +} // 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 00000000..ab8a0116 --- /dev/null +++ b/include/polyscope/simple_triangle_mesh_quantity.h @@ -0,0 +1,25 @@ +// 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 + + // 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 new file mode 100644 index 00000000..9a7623c0 --- /dev/null +++ b/include/polyscope/simple_triangle_mesh_scalar_quantity.h @@ -0,0 +1,62 @@ +// 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; + + const std::string definedOn; + +protected: + std::shared_ptr program; + + 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; + virtual void buildVertexInfoGUI(size_t vInd) 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; + virtual void buildFaceInfoGUI(size_t fInd) override; +}; + + +} // namespace polyscope diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8b4eca3b..da32e3a5 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/render/mock_opengl/mock_gl_engine.cpp b/src/render/mock_opengl/mock_gl_engine.cpp index ef4877e2..adec30c3 100644 --- a/src/render/mock_opengl/mock_gl_engine.cpp +++ b/src/render/mock_opengl/mock_gl_engine.cpp @@ -2233,7 +2233,10 @@ 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); + 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 608f0133..3ee383b8 100644 --- a/src/render/opengl/gl_engine.cpp +++ b/src/render/opengl/gl_engine.cpp @@ -2716,6 +2716,9 @@ 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); + 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 58f98551..ac7a1aed 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 860cb2fc..6b842c6a 100644 --- a/src/render/opengl/shaders/surface_mesh_shaders.cpp +++ b/src/render/opengl/shaders/surface_mesh_shaders.cpp @@ -731,6 +731,62 @@ 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}} +); + +// 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 549a2ce4..cfd8d103 100644 --- a/src/simple_triangle_mesh.cpp +++ b/src/simple_triangle_mesh.cpp @@ -5,6 +5,9 @@ #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 "polyscope/view.h" #include "imgui.h" @@ -28,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); @@ -74,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(); + } } @@ -187,7 +202,7 @@ void SimpleTriangleMesh::ensureRenderProgramPrepared() { render::engine->addMaterialRules(getMaterial(), addSimpleTriangleMeshRules( { - "SHADE_BASECOLOR", "COMPUTE_SHADE_NORMAL_FROM_POSITION", "PROJ_AND_INV_PROJ_MAT" + "SHADE_BASECOLOR" } ) ) @@ -205,29 +220,33 @@ void SimpleTriangleMesh::ensurePickProgramPrepared() { if (pickProgram) return; // clang-format off - pickProgram = render::engine->requestShader("SIMPLE_MESH", - addSimpleTriangleMeshRules( - { - "SHADECOLOR_FROM_UNIFORM", "COMPUTE_SHADE_NORMAL_FROM_POSITION", "PROJ_AND_INV_PROJ_MAT" - } - , 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) { 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 @@ -292,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; } @@ -307,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); + } } @@ -347,5 +466,47 @@ 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 + +SimpleTriangleMeshVertexScalarQuantity* +SimpleTriangleMesh::addVertexScalarQuantityImpl(std::string name, const std::vector& data, DataType type) { + checkForQuantityWithNameAndDeleteOrError(name); + SimpleTriangleMeshVertexScalarQuantity* q = new SimpleTriangleMeshVertexScalarQuantity(name, data, *this, type); + addQuantity(q); + return q; +} + +SimpleTriangleMeshFaceScalarQuantity* +SimpleTriangleMesh::addFaceScalarQuantityImpl(std::string name, const std::vector& data, DataType type) { + checkForQuantityWithNameAndDeleteOrError(name); + 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; +} + } // 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 00000000..3300d9b5 --- /dev/null +++ b/src/simple_triangle_mesh_color_quantity.cpp @@ -0,0 +1,136 @@ +// 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 { + +// ======================================================== +// ========== Color Quantity Base ========== +// ======================================================== + +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::buildColorOptionsUI() { + ColorQuantity::buildColorOptionsUI(); + ImGui::TextUnformatted("(no options available)"); +} + +void SimpleTriangleMeshColorQuantity::buildCustomUI() { + ImGui::SameLine(); + if (ImGui::Button("Options")) { + ImGui::OpenPopup("OptionsPopup"); + } + if (ImGui::BeginPopup("OptionsPopup")) { + buildColorOptionsUI(); + ImGui::EndPopup(); + } + buildColorUI(); +} + +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::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", + render::engine->addMaterialRules(parent.getMaterial(), + addColorRules( + parent.addSimpleTriangleMeshRules( + {"MESH_PROPAGATE_COLOR", "SHADE_COLOR"} + ) + ) + ) + ); + // clang-format on + + parent.setSimpleTriangleMeshProgramGeometryAttributes(*program); + program->setAttribute("a_color", colors.getRenderAttributeBuffer()); + render::engine->setMaterial(*program, parent.getMaterial()); +} + + +// ======================================================== +// ========== Face Color ========== +// ======================================================== + +SimpleTriangleMeshFaceColorQuantity::SimpleTriangleMeshFaceColorQuantity(std::string name, + const std::vector& colors_, + SimpleTriangleMesh& mesh_) + : SimpleTriangleMeshColorQuantity(name, colors_, "face", mesh_) { + 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", + 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_quantity.cpp b/src/simple_triangle_mesh_quantity.cpp new file mode 100644 index 00000000..aa917224 --- /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 00000000..92de0748 --- /dev/null +++ b/src/simple_triangle_mesh_scalar_quantity.cpp @@ -0,0 +1,129 @@ +// 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 { + +// ======================================================== +// ========== 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_) {} + +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::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::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", + render::engine->addMaterialRules(parent.getMaterial(), + parent.addSimpleTriangleMeshRules( + addScalarRules( + {"MESH_PROPAGATE_VALUE"} + ) + ) + ) + ); + // clang-format on + + parent.setSimpleTriangleMeshProgramGeometryAttributes(*program); + program->setAttribute("a_value", values.getRenderAttributeBuffer()); + program->setTextureFromColormap("t_colormap", cMap.get()); + render::engine->setMaterial(*program, parent.getMaterial()); +} + + +// ======================================================== +// ========== 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()); +} + +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", + 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/misc_test.cpp b/test/src/misc_test.cpp index c7b00c1c..60d7dea4 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 diff --git a/test/src/surface_mesh_test.cpp b/test/src/surface_mesh_test.cpp index 16b2ca21..ed2d3402 100644 --- a/test/src/surface_mesh_test.cpp +++ b/test/src/surface_mesh_test.cpp @@ -665,3 +665,65 @@ 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(); +} + +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(); +}