diff --git a/examples/demo-app/demo_app.cpp b/examples/demo-app/demo_app.cpp index 723fab9b..f1eaee4b 100644 --- a/examples/demo-app/demo_app.cpp +++ b/examples/demo-app/demo_app.cpp @@ -821,6 +821,7 @@ void callback() { std::cout << " io.MousePos.x: " << io.MousePos.x << " io.MousePos.y: " << io.MousePos.y << std::endl; std::cout << " screenCoords.x: " << screenCoords.x << " screenCoords.y: " << screenCoords.y << std::endl; std::cout << " bufferInd.x: " << xInd << " bufferInd.y: " << yInd << std::endl; + std::cout << " depth: " << pickResult.depth << std::endl; std::cout << " worldRay: "; polyscope::operator<<(std::cout, worldRay) << std::endl; std::cout << " worldPos: "; diff --git a/include/polyscope/camera_view.h b/include/polyscope/camera_view.h index 5f7ec990..61733109 100644 --- a/include/polyscope/camera_view.h +++ b/include/polyscope/camera_view.h @@ -47,6 +47,7 @@ class CameraView : public QuantityStructure { virtual void draw() override; virtual void drawDelayed() override; virtual void drawPick() override; + virtual void drawPickDelayed() override; virtual void updateObjectSpaceBounds() override; virtual std::string typeName() override; virtual void refresh() override; diff --git a/include/polyscope/context.h b/include/polyscope/context.h index b4935c07..085be831 100644 --- a/include/polyscope/context.h +++ b/include/polyscope/context.h @@ -107,6 +107,7 @@ struct Context { bool haveSelectionVal = false; uint64_t nextPickBufferInd = 1; std::unordered_map> structureRanges; + std::unordered_map> quantityRanges; // ====================================================== // === Internal globals from internal.h diff --git a/include/polyscope/curve_network.h b/include/polyscope/curve_network.h index e90366e0..3b6f2569 100644 --- a/include/polyscope/curve_network.h +++ b/include/polyscope/curve_network.h @@ -59,6 +59,7 @@ class CurveNetwork : public QuantityStructure { virtual void draw() override; virtual void drawDelayed() override; virtual void drawPick() override; + virtual void drawPickDelayed() override; virtual void updateObjectSpaceBounds() override; virtual std::string typeName() override; diff --git a/include/polyscope/floating_quantity_structure.h b/include/polyscope/floating_quantity_structure.h index c678dc69..21aa03a0 100644 --- a/include/polyscope/floating_quantity_structure.h +++ b/include/polyscope/floating_quantity_structure.h @@ -51,6 +51,7 @@ class FloatingQuantityStructure : public QuantityStructure #include +#include #include #include "polyscope/utilities.h" @@ -13,6 +14,7 @@ namespace polyscope { // Forward decls class Structure; +class Quantity; // == Main query @@ -26,9 +28,11 @@ class Structure; struct PickResult { bool isHit = false; Structure* structure = nullptr; + Quantity* quantity = nullptr; WeakHandle structureHandle; // same as .structure, but with lifetime tracking std::string structureType = ""; std::string structureName = ""; + std::string quantityName = ""; glm::vec2 screenCoords; glm::ivec2 bufferInds; glm::vec3 position; @@ -60,18 +64,21 @@ namespace pick { std::pair pickAtScreenCoords(glm::vec2 screenCoords); // takes screen coordinates std::pair pickAtBufferCoords(int xPos, int yPos); // takes indices into the buffer std::pair evaluatePickQuery(int xPos, int yPos); // old, badly named. takes buffer coordinates. +std::tuple evaluatePickQueryFull(int xPos, + int yPos); // badly named. takes buffer coordinates. // == Helpers // Set up picking (internal) -// Called by a structure to figure out what data it should render to the pick buffer. +// Called by a structure/quantity to figure out what data it should render to the pick buffer. // Request 'count' contiguous indices for drawing a pick buffer. The return value is the start of the range. uint64_t requestPickBufferRange(Structure* requestingStructure, uint64_t count); +uint64_t requestPickBufferRange(Quantity* requestingQuantity, uint64_t count); // Convert between global pick indexing for the whole program, and local per-structure pick indexing -std::pair globalIndexToLocal(uint64_t globalInd); -uint64_t localIndexToGlobal(std::pair localPick); +std::tuple globalIndexToLocal(uint64_t globalInd); +uint64_t localIndexToGlobal(std::tuple localPick); // Convert indices to float3 color and back // Structures will want to use these to fill their pick buffers diff --git a/include/polyscope/point_cloud.h b/include/polyscope/point_cloud.h index c0bc61e2..4963cf37 100644 --- a/include/polyscope/point_cloud.h +++ b/include/polyscope/point_cloud.h @@ -60,6 +60,7 @@ class PointCloud : public QuantityStructure { virtual void draw() override; virtual void drawDelayed() override; virtual void drawPick() override; + virtual void drawPickDelayed() override; virtual void updateObjectSpaceBounds() override; virtual std::string typeName() override; virtual void refresh() override; diff --git a/include/polyscope/polyscope.h b/include/polyscope/polyscope.h index 070a434f..3d3ae7fe 100644 --- a/include/polyscope/polyscope.h +++ b/include/polyscope/polyscope.h @@ -130,10 +130,6 @@ Structure* getStructure(std::string type, std::string name = ""); // True if such a structure exists bool hasStructure(std::string type, std::string name); -// Look up the string type and name for a structure from its pointer -// (performs a naive search over all structures for now, use sparingly) -std::tuple lookUpStructure(Structure* structure); - // De-register a structure, of any type. Also removes any quantities associated with the structure void removeStructure(Structure* structure, bool errorIfAbsent = false); void removeStructure(std::string type, std::string name, bool errorIfAbsent = false); diff --git a/include/polyscope/quantity.h b/include/polyscope/quantity.h index e19b94a4..87afeb47 100644 --- a/include/polyscope/quantity.h +++ b/include/polyscope/quantity.h @@ -30,6 +30,10 @@ class Quantity : public render::ManagedBufferRegistry { virtual void draw(); virtual void drawDelayed(); // drawing that should happen after the main phase + // Draw pick buffers for the quantity + virtual void drawPick(); + virtual void drawPickDelayed(); // drawing that should happen after the main phase + // Draw the ImGUI ui elements virtual void buildUI(); // draws the tree node and enabled checkbox common to almost all quantities, and calls // drawCustomUI() below. Can still be overidden in case something else is wanted. diff --git a/include/polyscope/render_image_quantity_base.h b/include/polyscope/render_image_quantity_base.h index e402c455..649d99b9 100644 --- a/include/polyscope/render_image_quantity_base.h +++ b/include/polyscope/render_image_quantity_base.h @@ -26,8 +26,7 @@ class RenderImageQuantityBase : public FloatingQuantity, public FullscreenArtist const std::vector& depthData, const std::vector& normalData, ImageOrigin imageOrigin); - // virtual void draw() override; - // virtual void drawDelayed() override; + virtual void drawPickDelayed() override; virtual void refresh() override; @@ -73,9 +72,14 @@ class RenderImageQuantityBase : public FloatingQuantity, public FullscreenArtist PersistentValue transparency; PersistentValue allowFullscreenCompositing; + // Picking is the same for all + std::shared_ptr pickProgram; + glm::vec3 pickColor; + // Helpers void prepareGeometryBuffers(); void addOptionsPopupEntries(); + void preparePick(); }; diff --git a/include/polyscope/simple_triangle_mesh.h b/include/polyscope/simple_triangle_mesh.h index 1cf0cf5b..10311b70 100644 --- a/include/polyscope/simple_triangle_mesh.h +++ b/include/polyscope/simple_triangle_mesh.h @@ -47,6 +47,7 @@ class SimpleTriangleMesh : public QuantityStructure { virtual void draw() override; virtual void drawDelayed() override; virtual void drawPick() override; + virtual void drawPickDelayed() override; virtual void updateObjectSpaceBounds() override; virtual std::string typeName() override; virtual void refresh() override; diff --git a/include/polyscope/structure.h b/include/polyscope/structure.h index 0ce4bdea..a153e96f 100644 --- a/include/polyscope/structure.h +++ b/include/polyscope/structure.h @@ -42,8 +42,9 @@ class Structure : public render::ManagedBufferRegistry, public virtual WeakRefer // == Render the the structure on screen virtual void draw() = 0; - virtual void drawDelayed() = 0; virtual void drawPick() = 0; + virtual void drawDelayed() = 0; // a second render pass + virtual void drawPickDelayed() = 0; // == Add rendering rules std::vector addStructureRules(std::vector initRules); @@ -58,7 +59,8 @@ class Structure : public render::ManagedBufferRegistry, public virtual WeakRefer virtual void buildPickUI(const PickResult& result) = 0; // Draw pick UI elements based on a selection result // = Identifying data - const std::string name; // should be unique amongst registered structures with this type + const std::string name; // should be unique amongst registered structures with this type + const std::string subtypeName; // specific type name, like "Point Cloud" std::string uniquePrefix(); std::string getName() { return name; }; // used by pybind to access the name property diff --git a/include/polyscope/surface_mesh.h b/include/polyscope/surface_mesh.h index e0834a0e..e3bb9b82 100644 --- a/include/polyscope/surface_mesh.h +++ b/include/polyscope/surface_mesh.h @@ -86,6 +86,7 @@ class SurfaceMesh : public QuantityStructure { virtual void draw() override; virtual void drawDelayed() override; virtual void drawPick() override; + virtual void drawPickDelayed() override; virtual void updateObjectSpaceBounds() override; virtual std::string typeName() override; virtual void refresh() override; diff --git a/include/polyscope/volume_grid.h b/include/polyscope/volume_grid.h index 8e12dfe8..3467d9c8 100644 --- a/include/polyscope/volume_grid.h +++ b/include/polyscope/volume_grid.h @@ -42,6 +42,7 @@ class VolumeGrid : public QuantityStructure { virtual void draw() override; virtual void drawDelayed() override; virtual void drawPick() override; + virtual void drawPickDelayed() override; virtual void updateObjectSpaceBounds() override; virtual std::string typeName() override; virtual void refresh() override; diff --git a/include/polyscope/volume_mesh.h b/include/polyscope/volume_mesh.h index 83e4d30a..51389703 100644 --- a/include/polyscope/volume_mesh.h +++ b/include/polyscope/volume_mesh.h @@ -62,6 +62,7 @@ class VolumeMesh : public QuantityStructure { virtual void draw() override; virtual void drawDelayed() override; virtual void drawPick() override; + virtual void drawPickDelayed() override; virtual void updateObjectSpaceBounds() override; virtual std::string typeName() override; virtual void refresh() override; diff --git a/src/camera_view.cpp b/src/camera_view.cpp index 28066c73..e41cd416 100644 --- a/src/camera_view.cpp +++ b/src/camera_view.cpp @@ -128,6 +128,27 @@ void CameraView::drawPick() { pickFrameProgram->setUniform("u_vertPickRadius", 0.); pickFrameProgram->draw(); + + for (auto& x : quantities) { + x.second->drawPick(); + } + for (auto& x : floatingQuantities) { + x.second->drawPick(); + } +} + + +void CameraView::drawPickDelayed() { + if (!isEnabled()) { + return; + } + + for (auto& x : quantities) { + x.second->drawPickDelayed(); + } + for (auto& x : floatingQuantities) { + x.second->drawPickDelayed(); + } } void CameraView::prepare() { diff --git a/src/curve_network.cpp b/src/curve_network.cpp index da8f0ffb..9a7d35d1 100644 --- a/src/curve_network.cpp +++ b/src/curve_network.cpp @@ -197,6 +197,26 @@ void CurveNetwork::drawPick() { edgePickProgram->draw(); nodePickProgram->draw(); + + for (auto& x : quantities) { + x.second->drawPick(); + } + for (auto& x : floatingQuantities) { + x.second->drawPick(); + } +} + +void CurveNetwork::drawPickDelayed() { + if (!isEnabled()) { + return; + } + + for (auto& x : quantities) { + x.second->drawPickDelayed(); + } + for (auto& x : floatingQuantities) { + x.second->drawPickDelayed(); + } } std::vector CurveNetwork::addCurveNetworkNodeRules(std::vector initRules) { diff --git a/src/floating_quantity_structure.cpp b/src/floating_quantity_structure.cpp index 89d0b9d6..b8e47d25 100644 --- a/src/floating_quantity_structure.cpp +++ b/src/floating_quantity_structure.cpp @@ -49,7 +49,27 @@ void FloatingQuantityStructure::drawDelayed() { } } -void FloatingQuantityStructure::drawPick() {} +void FloatingQuantityStructure::drawPick() { + if (!isEnabled()) return; + + for (auto& qp : quantities) { + qp.second->drawPick(); + } + for (auto& qp : floatingQuantities) { + qp.second->drawPick(); + } +} + +void FloatingQuantityStructure::drawPickDelayed() { + if (!isEnabled()) return; + + for (auto& qp : quantities) { + qp.second->drawPickDelayed(); + } + for (auto& qp : floatingQuantities) { + qp.second->drawPickDelayed(); + } +} // override the structure UI, since this one is a bit different void FloatingQuantityStructure::buildUI() { diff --git a/src/pick.cpp b/src/pick.cpp index e0eacd84..db1cfe6e 100644 --- a/src/pick.cpp +++ b/src/pick.cpp @@ -21,33 +21,43 @@ PickResult pickAtBufferInds(glm::ivec2 bufferInds) { // Query the pick buffer // (this necessarily renders to pickFrameBuffer) - std::pair rawPickResult = pick::pickAtBufferCoords(bufferInds.x, bufferInds.y); + std::tuple rawPickResult = pick::evaluatePickQueryFull(bufferInds.x, bufferInds.y); // Query the depth buffer populated above render::FrameBuffer* pickFramebuffer = render::engine->pickFramebuffer.get(); float clipDepth = pickFramebuffer->readDepth(bufferInds.x, view::bufferHeight - bufferInds.y); // Transcribe result into return tuple - result.structure = rawPickResult.first; + result.structure = std::get<0>(rawPickResult); + result.quantity = std::get<1>(rawPickResult); result.bufferInds = bufferInds; result.screenCoords = view::bufferIndsToScreenCoords(bufferInds); result.position = view::screenCoordsAndDepthToWorldPosition(result.screenCoords, clipDepth); result.depth = glm::length(result.position - view::getCameraWorldPosition()); - if (rawPickResult.first == nullptr) { - result.isHit = false; - result.structureType = ""; - result.structureName = ""; - result.localIndex = INVALID_IND_64; - } else { + + // null-initialize, might be overwritten below + result.isHit = false; + result.structureType = ""; + result.structureName = ""; + result.quantityName = ""; + result.localIndex = INVALID_IND_64; + + if (result.structure != nullptr) { result.structureHandle = result.structure->getWeakHandle(); result.isHit = true; - std::tuple lookupResult = lookUpStructure(rawPickResult.first); - result.structureType = std::get<0>(lookupResult); - result.structureName = std::get<1>(lookupResult); - result.localIndex = rawPickResult.second; + result.structureType = result.structure->subtypeName; + result.structureName = result.structure->name; + result.localIndex = std::get<2>(rawPickResult); } + if (result.quantity != nullptr) { + result.isHit = true; + result.quantityName = result.quantity->name; + // ideally we would have typenames and WeakHandles for quantities too, but the classes do not offer either of those + // for now. ONEDAY: add them + result.localIndex = std::get<2>(rawPickResult); + } return result; } @@ -81,7 +91,15 @@ void setSelection(PickResult newPick) { namespace pick { // == Set up picking -uint64_t requestPickBufferRange(Structure* requestingStructure, uint64_t count) { +namespace { + +// helper sharing logic for both cases below +uint64_t requestPickBufferRange(Structure* requestingStructure, Quantity* requestingQuantity, uint64_t count) { + + if (requestingStructure != nullptr && requestingQuantity != nullptr) { + // this is an either-or function sharing logic, it shouldn't be called with both non-null + throw std::logic_error("only one of the requestPickBufferRange() pointers should be null, not both"); + } // Check if we can satisfy the request uint64_t maxPickInd = std::numeric_limits::max(); @@ -102,18 +120,33 @@ uint64_t requestPickBufferRange(Structure* requestingStructure, uint64_t count) uint64_t ret = state::globalContext.nextPickBufferInd; state::globalContext.nextPickBufferInd += count; - state::globalContext.structureRanges[requestingStructure] = - std::make_tuple(ret, state::globalContext.nextPickBufferInd); + if (requestingStructure != nullptr) { + state::globalContext.structureRanges[requestingStructure] = + std::make_tuple(ret, state::globalContext.nextPickBufferInd); + } + if (requestingQuantity != nullptr) { + state::globalContext.quantityRanges[requestingQuantity] = + std::make_tuple(ret, state::globalContext.nextPickBufferInd); + } return ret; } +} // namespace + +uint64_t requestPickBufferRange(Structure* requestingStructure, uint64_t count) { + return requestPickBufferRange(requestingStructure, nullptr, count); +} + +uint64_t requestPickBufferRange(Quantity* requestingQuantity, uint64_t count) { + return requestPickBufferRange(nullptr, requestingQuantity, count); +} // == Helpers -std::pair globalIndexToLocal(uint64_t globalInd) { +std::tuple globalIndexToLocal(uint64_t globalInd) { // ONEDAY: this could be asymptotically better if we cared - // Loop through the ranges that we have allocated to find the one correpsonding to this structure. + // Loop through the ranges that we have allocated to find the one corresponding to this structure. for (const auto& x : state::globalContext.structureRanges) { Structure* structure = x.first; @@ -121,24 +154,54 @@ std::pair globalIndexToLocal(uint64_t globalInd) { uint64_t rangeEnd = std::get<1>(x.second); if (globalInd >= rangeStart && globalInd < rangeEnd) { - return {structure, globalInd - rangeStart}; + return {structure, nullptr, globalInd - rangeStart}; + } + } + + + // Look through quantity ranges + for (const auto& x : state::globalContext.quantityRanges) { + + Quantity* quantity = x.first; + uint64_t rangeStart = std::get<0>(x.second); + uint64_t rangeEnd = std::get<1>(x.second); + + if (globalInd >= rangeStart && globalInd < rangeEnd) { + + // look up the structure that goes with this quantity + Structure* structure = &quantity->parent; + + return {structure, quantity, globalInd - rangeStart}; } } - return {nullptr, 0}; + return {nullptr, nullptr, 0}; } -uint64_t localIndexToGlobal(std::pair localPick) { - if (localPick.first == nullptr) return 0; +uint64_t localIndexToGlobal(std::tuple localPick) { + Structure* structurePtr = std::get<0>(localPick); + Quantity* quantityPtr = std::get<1>(localPick); + uint64_t localInd = std::get<2>(localPick); - if (state::globalContext.structureRanges.find(localPick.first) == state::globalContext.structureRanges.end()) { - exception("structure does not match any allocated pick range"); + if (structurePtr == nullptr && quantityPtr == nullptr) return 0; + + if (state::globalContext.structureRanges.find(structurePtr) != state::globalContext.structureRanges.end()) { + std::tuple range = state::globalContext.structureRanges[structurePtr]; + uint64_t rangeStart = std::get<0>(range); + uint64_t rangeEnd = std::get<1>(range); + return rangeStart + localInd; } - std::tuple range = state::globalContext.structureRanges[localPick.first]; - uint64_t rangeStart = std::get<0>(range); - uint64_t rangeEnd = std::get<1>(range); - return rangeStart + localPick.second; + if (state::globalContext.quantityRanges.find(quantityPtr) != state::globalContext.quantityRanges.end()) { + std::tuple range = state::globalContext.quantityRanges[quantityPtr]; + uint64_t rangeStart = std::get<0>(range); + uint64_t rangeEnd = std::get<1>(range); + return rangeStart + localInd; + } + + exception("structure/quantity does not match any allocated pick range"); + + return 0; } std::pair pickAtScreenCoords(glm::vec2 screenCoords) { @@ -149,13 +212,13 @@ std::pair pickAtScreenCoords(glm::vec2 screenCoords) { std::pair pickAtBufferCoords(int xPos, int yPos) { return evaluatePickQuery(xPos, yPos); } -std::pair evaluatePickQuery(int xPos, int yPos) { +std::tuple evaluatePickQueryFull(int xPos, int yPos) { // NOTE: hack used for debugging: if xPos == yPos == -1 we do a pick render but do not query the value. // Be sure not to pick outside of buffer if (xPos < -1 || xPos >= view::bufferWidth || yPos < -1 || yPos >= view::bufferHeight) { - return {nullptr, 0}; + return {nullptr, nullptr, 0}; } render::FrameBuffer* pickFramebuffer = render::engine->pickFramebuffer.get(); @@ -166,7 +229,7 @@ std::pair evaluatePickQuery(int xPos, int yPos) { pickFramebuffer->resize(view::bufferWidth, view::bufferHeight); pickFramebuffer->setViewport(0, 0, view::bufferWidth, view::bufferHeight); pickFramebuffer->clearColor = glm::vec3{0., 0., 0.}; - if (!pickFramebuffer->bindForRendering()) return {nullptr, 0}; + if (!pickFramebuffer->bindForRendering()) return {nullptr, nullptr, 0}; pickFramebuffer->clear(); // Render pick buffer @@ -175,9 +238,15 @@ std::pair evaluatePickQuery(int xPos, int yPos) { x.second->drawPick(); } } + for (auto& catMap : state::structures) { + for (auto& s : catMap.second) { + s.second->drawPickDelayed(); + } + } + if (xPos == -1 || yPos == -1) { - return {nullptr, 0}; + return {nullptr, nullptr, 0}; } // Read from the pick buffer @@ -187,5 +256,10 @@ std::pair evaluatePickQuery(int xPos, int yPos) { return pick::globalIndexToLocal(globalInd); } +std::pair evaluatePickQuery(int xPos, int yPos) { + std::tuple t = evaluatePickQueryFull(xPos, yPos); + return std::pair(std::get<0>(t), std::get<2>(t)); +} + } // namespace pick } // namespace polyscope diff --git a/src/point_cloud.cpp b/src/point_cloud.cpp index 509c9876..25e24a05 100644 --- a/src/point_cloud.cpp +++ b/src/point_cloud.cpp @@ -130,6 +130,26 @@ void PointCloud::drawPick() { setPointCloudUniforms(*pickProgram); pickProgram->draw(); + + for (auto& x : quantities) { + x.second->drawPick(); + } + for (auto& x : floatingQuantities) { + x.second->drawPick(); + } +} + +void PointCloud::drawPickDelayed() { + if (!isEnabled()) { + return; + } + + for (auto& x : quantities) { + x.second->drawPickDelayed(); + } + for (auto& x : floatingQuantities) { + x.second->drawPickDelayed(); + } } void PointCloud::ensureRenderProgramPrepared() { diff --git a/src/polyscope.cpp b/src/polyscope.cpp index f987135a..aa733d37 100644 --- a/src/polyscope.cpp +++ b/src/polyscope.cpp @@ -943,6 +943,9 @@ void buildPickGui() { ImGui::NewLine(); ImGui::TextUnformatted((selection.structureType + ": " + selection.structureName).c_str()); + if (selection.quantityName != "") { + ImGui::TextUnformatted(("Quantity: " + selection.quantityName).c_str()); + } ImGui::Separator(); if (selection.structureHandle.isValid()) { @@ -1252,20 +1255,6 @@ bool hasStructure(std::string type, std::string name) { return sMap.find(name) != sMap.end(); } -std::tuple lookUpStructure(Structure* structure) { - - for (auto& typeMap : state::structures) { - for (auto& entry : typeMap.second) { - if (entry.second.get() == structure) { - return std::tuple(typeMap.first, entry.first); - } - } - } - - // not found - return std::tuple("", ""); -} - void removeStructure(std::string type, std::string name, bool errorIfAbsent) { // If there are no structures of that type it is an automatic fail diff --git a/src/quantity.cpp b/src/quantity.cpp index 6f578e1a..cf55e257 100644 --- a/src/quantity.cpp +++ b/src/quantity.cpp @@ -23,6 +23,10 @@ void Quantity::draw() {} void Quantity::drawDelayed() {} +void Quantity::drawPick() {} + +void Quantity::drawPickDelayed() {} + void Quantity::buildUI() {} void Quantity::buildCustomUI() {} diff --git a/src/render_image_quantity_base.cpp b/src/render_image_quantity_base.cpp index 5f7a4096..b9a4f638 100644 --- a/src/render_image_quantity_base.cpp +++ b/src/render_image_quantity_base.cpp @@ -64,7 +64,53 @@ void RenderImageQuantityBase::updateBaseBuffers(const std::vector& newDep requestRedraw(); } -void RenderImageQuantityBase::refresh() { Quantity::refresh(); } + +void RenderImageQuantityBase::drawPickDelayed() { + if (!isEnabled()) return; + + if (!pickProgram) preparePick(); + + // set uniforms + glm::mat4 P = view::getCameraPerspectiveMatrix(); + glm::mat4 Pinv = glm::inverse(P); + + pickProgram->setUniform("u_projMatrix", glm::value_ptr(P)); + pickProgram->setUniform("u_invProjMatrix", glm::value_ptr(Pinv)); + pickProgram->setUniform("u_viewport", render::engine->getCurrentViewport()); + pickProgram->setUniform("u_transparency", 1.0); + pickProgram->setUniform("u_color", pickColor); + + // draw + pickProgram->draw(); +} + +void RenderImageQuantityBase::refresh() { + pickProgram = nullptr; + Quantity::refresh(); +} + + +void RenderImageQuantityBase::preparePick() { + + // Request pick indices + size_t pickCount = 1; + size_t pickStart = pick::requestPickBufferRange(this, pickCount); + pickColor = pick::indToVec(pickStart); + + // Create the sourceProgram + // clang-format off + pickProgram = render::engine->requestShader("TEXTURE_DRAW_RENDERIMAGE_PLAIN", + { + getImageOriginRule(imageOrigin), + "SHADECOLOR_FROM_UNIFORM", + }, + render::ShaderReplacementDefaults::Pick + ); + // clang-format on + + pickProgram->setAttribute("a_position", render::engine->screenTrianglesCoords()); + pickProgram->setTextureFromBuffer("t_depth", depths.getRenderTextureBuffer().get()); +} void RenderImageQuantityBase::disableFullscreenDrawing() { if (isEnabled()) { diff --git a/src/simple_triangle_mesh.cpp b/src/simple_triangle_mesh.cpp index 9171b224..4db7bb77 100644 --- a/src/simple_triangle_mesh.cpp +++ b/src/simple_triangle_mesh.cpp @@ -140,6 +140,26 @@ void SimpleTriangleMesh::drawPick() { setPickUniforms(*pickProgram); pickProgram->draw(); + + for (auto& x : quantities) { + x.second->drawPick(); + } + for (auto& x : floatingQuantities) { + x.second->drawPick(); + } +} + +void SimpleTriangleMesh::drawPickDelayed() { + if (!isEnabled()) { + return; + } + + for (auto& x : quantities) { + x.second->drawPickDelayed(); + } + for (auto& x : floatingQuantities) { + x.second->drawPickDelayed(); + } } void SimpleTriangleMesh::setSimpleTriangleMeshUniforms(render::ShaderProgram& p, bool withSurfaceShade) { diff --git a/src/structure.cpp b/src/structure.cpp index cc92997b..264e0ff3 100644 --- a/src/structure.cpp +++ b/src/structure.cpp @@ -8,8 +8,8 @@ namespace polyscope { -Structure::Structure(std::string name_, std::string subtypeName) - : name(name_), enabled(subtypeName + "#" + name + "#enabled", true), +Structure::Structure(std::string name_, std::string subtypeName_) + : name(name_), subtypeName(subtypeName_), enabled(subtypeName + "#" + name + "#enabled", true), objectTransform(subtypeName + "#" + name + "#object_transform", glm::mat4(1.0)), transparency(subtypeName + "#" + name + "#transparency", 1.0), transformGizmo(subtypeName + "#" + name + "#transform_gizmo", objectTransform.get(), &objectTransform), diff --git a/src/surface_mesh.cpp b/src/surface_mesh.cpp index 9c306cad..fe198f81 100644 --- a/src/surface_mesh.cpp +++ b/src/surface_mesh.cpp @@ -810,7 +810,28 @@ void SurfaceMesh::drawPick() { pickProgram->draw(); + for (auto& x : quantities) { + x.second->drawPick(); + } + render::engine->setBackfaceCull(); // return to default setting + + for (auto& x : floatingQuantities) { + x.second->drawPick(); + } +} + +void SurfaceMesh::drawPickDelayed() { + if (!isEnabled()) { + return; + } + + for (auto& x : quantities) { + x.second->drawPickDelayed(); + } + for (auto& x : floatingQuantities) { + x.second->drawPickDelayed(); + } } void SurfaceMesh::prepare() { diff --git a/src/volume_grid.cpp b/src/volume_grid.cpp index 5fa57c6a..f108a767 100644 --- a/src/volume_grid.cpp +++ b/src/volume_grid.cpp @@ -170,6 +170,26 @@ void VolumeGrid::drawPick() { // Draw the actual grid render::engine->setBackfaceCull(true); pickProgram->draw(); + + for (auto& x : quantities) { + x.second->drawPick(); + } + for (auto& x : floatingQuantities) { + x.second->drawPick(); + } +} + +void VolumeGrid::drawPickDelayed() { + if (!isEnabled()) { + return; + } + + for (auto& x : quantities) { + x.second->drawPickDelayed(); + } + for (auto& x : floatingQuantities) { + x.second->drawPickDelayed(); + } } std::vector VolumeGrid::addGridCubeRules(std::vector initRules, bool withShade) { diff --git a/src/volume_mesh.cpp b/src/volume_mesh.cpp index 3e4bf589..78094bbc 100644 --- a/src/volume_mesh.cpp +++ b/src/volume_mesh.cpp @@ -487,6 +487,26 @@ void VolumeMesh::drawPick() { pickProgram->setUniform("u_vertPickRadius", 0.2); pickProgram->draw(); + + for (auto& x : quantities) { + x.second->drawPick(); + } + for (auto& x : floatingQuantities) { + x.second->drawPick(); + } +} + +void VolumeMesh::drawPickDelayed() { + if (!isEnabled()) { + return; + } + + for (auto& x : quantities) { + x.second->drawPickDelayed(); + } + for (auto& x : floatingQuantities) { + x.second->drawPickDelayed(); + } } void VolumeMesh::prepare() { diff --git a/test/src/floating_test.cpp b/test/src/floating_test.cpp index 09a139dd..c1578be6 100644 --- a/test/src/floating_test.cpp +++ b/test/src/floating_test.cpp @@ -103,6 +103,7 @@ TEST_F(PolyscopeTest, FloatingRenderImageTest) { im->updateBuffers(depthVals, normalVals); im->setEnabled(true); polyscope::show(3); + polyscope::pickAtScreenCoords(glm::vec2(0.3, 0.8)); } { // with no normals polyscope::DepthRenderImageQuantity* im = polyscope::addDepthRenderImageQuantity( @@ -110,6 +111,7 @@ TEST_F(PolyscopeTest, FloatingRenderImageTest) { im->updateBuffers(depthVals, normalValsEmpty); im->setEnabled(true); polyscope::show(3); + polyscope::pickAtScreenCoords(glm::vec2(0.3, 0.8)); } { // ColorImageQuantity @@ -118,6 +120,7 @@ TEST_F(PolyscopeTest, FloatingRenderImageTest) { im->updateBuffers(depthVals, normalVals, colorVals); im->setEnabled(true); polyscope::show(3); + polyscope::pickAtScreenCoords(glm::vec2(0.3, 0.8)); } { // with no normals polyscope::ColorRenderImageQuantity* im = polyscope::addColorRenderImageQuantity( @@ -125,6 +128,7 @@ TEST_F(PolyscopeTest, FloatingRenderImageTest) { im->updateBuffers(depthVals, normalValsEmpty, colorVals); im->setEnabled(true); polyscope::show(3); + polyscope::pickAtScreenCoords(glm::vec2(0.3, 0.8)); } { // ScalarRenderImageQuantity @@ -133,6 +137,7 @@ TEST_F(PolyscopeTest, FloatingRenderImageTest) { im->updateBuffers(depthVals, normalVals, scalarVals); im->setEnabled(true); polyscope::show(3); + polyscope::pickAtScreenCoords(glm::vec2(0.3, 0.8)); } { // with no normals polyscope::ScalarRenderImageQuantity* im = polyscope::addScalarRenderImageQuantity( @@ -140,6 +145,7 @@ TEST_F(PolyscopeTest, FloatingRenderImageTest) { im->updateBuffers(depthVals, normalValsEmpty, scalarVals); im->setEnabled(true); polyscope::show(3); + polyscope::pickAtScreenCoords(glm::vec2(0.3, 0.8)); } { // categorical polyscope::ScalarRenderImageQuantity* im = @@ -148,6 +154,7 @@ TEST_F(PolyscopeTest, FloatingRenderImageTest) { im->updateBuffers(depthVals, normalVals, scalarVals); im->setEnabled(true); polyscope::show(3); + polyscope::pickAtScreenCoords(glm::vec2(0.3, 0.8)); } { // RawColorImageQuantity @@ -156,6 +163,7 @@ TEST_F(PolyscopeTest, FloatingRenderImageTest) { im->updateBuffers(depthVals, colorVals); im->setEnabled(true); polyscope::show(3); + polyscope::pickAtScreenCoords(glm::vec2(0.3, 0.8)); } { // RawColorAlphaImageQuantity @@ -166,6 +174,7 @@ TEST_F(PolyscopeTest, FloatingRenderImageTest) { polyscope::show(3); im->setIsPremultiplied(true); polyscope::show(3); + polyscope::pickAtScreenCoords(glm::vec2(0.3, 0.8)); } // make sure it doesn't blow up with transparancy