Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion examples/demo-app/demo_app.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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();

Expand Down
50 changes: 50 additions & 0 deletions include/polyscope/pick.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<uint32_t>(pickStart & 0xFFFFFFFFull), static_cast<uint32_t>(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
4 changes: 4 additions & 0 deletions include/polyscope/render/engine.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,10 @@ class AttributeBuffer {
virtual void setData(const std::vector<std::array<glm::vec3, 3>>& data) = 0;
virtual void setData(const std::vector<std::array<glm::vec3, 4>>& data) = 0;

// Pre-allocate GPU memory for n elements without uploading any data. Subsequent setData()
// calls with size <= n will not need to reallocate the underlying buffer.
virtual void reserveCapacity(size_t n) = 0;

virtual uint32_t getNativeBufferID() = 0; // used to interop with external things, e.g. ImGui

// == Getters
Expand Down
55 changes: 55 additions & 0 deletions include/polyscope/render/managed_buffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,56 @@ class ManagedBuffer : public virtual WeakReferrable {
void setTextureSize(uint32_t sizeX, uint32_t sizeY, uint32_t sizeZ);
std::array<uint32_t, 3> getTextureSize() const;

// == Resize / capacity API

// The .size() of the buffer is the number of data elements it holds.
//
// The .capacity() of the buffer is the number of data elements it has capacity for without needing to reallocate
// (similar to std::vector).
//
// In a simple flow where we put data in a buffer once, the capacity is the same as the size and neither ever changes.
// But in settings where we e.g. incrementally add elements or change the number of data elements on each frame, we
// may want to allocate a larger capacity to avoid expensive re-allocation each time.

// Resize the buffer to newSize elements.
//
// If newSize <= capacity(), this is a cheap constant-time operation which just updates metadata.
//
// If newSize > capacity(), this triggers a full reallocation. The new capacity is set to at least
// 2*oldCapacity (amortized doubling). Any data that was GPU-resident is first copied back to the
// host, then the GPU buffer is reallocated in-place (same buffer object, new backing memory).
// ShaderPrograms holding a reference to the GPU buffer remain valid.
//
// Returns true if a reallocation occurred, false if the resize stayed within capacity.
//
// Valid for attributes and 1D textures only; call the 2D/3D variants below for multidimensional
// textures.
bool resize(size_t newSize);

// Resize a 2D texture. No-op (returns false) if dimensions are unchanged. Otherwise always
// triggers a reallocation (2D/3D textures have no capacity slack — capacity always equals size),
// reallocates the GPU buffer in-place, and returns true.
bool resizeTexture2D(uint32_t newSizeX, uint32_t newSizeY);

// Resize a 3D texture. No-op (returns false) if dimensions are unchanged. Otherwise always
// triggers a reallocation (2D/3D textures have no capacity slack — capacity always equals size),
// reallocates the GPU buffer in-place, and returns true.
bool resizeTexture3D(uint32_t newSizeX, uint32_t newSizeY, uint32_t newSizeZ);

// The maximum size the buffer can be resized to without triggering a reallocation. Always >= size().
size_t capacity();

// Set the managed capacity to newCapacity. Unlike resize(), which grows capacity via amortized
// doubling, this sets the logical capacity to a precise value. Error if newCapacity < size().
// Reallocates the GPU buffer in-place (same buffer object, new backing memory) if one exists.
//
// Note: data.capacity() is guaranteed to be >= managedCapacity, but may remain larger than
// newCapacity if the underlying vector already had more space allocated.
//
// Valid for attributes and 1D textures only; multidimensional textures always have capacity
// equal to their size, so use the 2D/3D resize() variants instead.
void setCapacity(size_t newCapacity);


// == Members for indexed data

Expand Down Expand Up @@ -200,6 +250,11 @@ class ManagedBuffer : public virtual WeakReferrable {

bool hostBufferIsPopulated; // true if the host buffer contains currently-valid data

// The buffer has capacity for at least this many elements. It is distinct from .size(), which is the actual number of
// elements currently stored in the buffer and may be smaller than the capacity.
// Any resize() operations that stay within the capacity are cheap, and do not trigger a full reallocation and copy.
size_t managedCapacity = 0;

std::shared_ptr<render::AttributeBuffer> renderAttributeBuffer;
std::shared_ptr<render::TextureBuffer> renderTextureBuffer;

Expand Down
1 change: 1 addition & 0 deletions include/polyscope/render/mock_opengl/mock_gl_engine.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ class GLAttributeBuffer : public AttributeBuffer {
std::vector<glm::uvec4> getDataRange_uvec4(size_t ind, size_t count) override;

uint32_t getNativeBufferID() override;
void reserveCapacity(size_t n) override;

protected:
private:
Expand Down
1 change: 1 addition & 0 deletions include/polyscope/render/opengl/gl_engine.h
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ class GLAttributeBuffer : public AttributeBuffer {
std::vector<glm::uvec4> getDataRange_uvec4(size_t ind, size_t count) override;

uint32_t getNativeBufferID() override;
void reserveCapacity(size_t n) override;

protected:
VertexBufferHandle VBOLoc;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
64 changes: 60 additions & 4 deletions include/polyscope/simple_triangle_mesh.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 <vector>

Expand All @@ -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 {
Expand Down Expand Up @@ -50,8 +63,27 @@ class SimpleTriangleMesh : public Structure {
render::ManagedBuffer<glm::vec3> vertices;
render::ManagedBuffer<glm::uvec3> faces;

size_t nVertices() { return vertices.size(); }
size_t nFaces() { return faces.size(); }

// === Quantities

// Scalars
template <class T>
SimpleTriangleMeshVertexScalarQuantity* addVertexScalarQuantity(std::string name, const T& values,
DataType type = DataType::STANDARD);

template <class T>
SimpleTriangleMeshFaceScalarQuantity* addFaceScalarQuantity(std::string name, const T& values,
DataType type = DataType::STANDARD);

// Colors
template <class T>
SimpleTriangleMeshVertexColorQuantity* addVertexColorQuantity(std::string name, const T& values);

template <class T>
SimpleTriangleMeshFaceColorQuantity* addFaceColorQuantity(std::string name, const T& values);

// === Mutate

template <class V>
Expand All @@ -60,6 +92,19 @@ class SimpleTriangleMesh : public Structure {
template <class V, class F>
void update(const V& newVertices, const F& newFaces);

// CPU-side update functions.

// update only positions (vertex count must stay the same).
template <class V>
void updateVertexPositions(const V& newPositions);

// update both vertices and faces; vertex/face counts may change
template <class V, class F>
void updateMesh(const V& newVertices, const F& newFaces);

// optionally pre-allocates capacity to avoid reallocations on future updateMesh() calls.
void reserve(size_t nVerts, size_t nFaces);

// Misc data
static const std::string structureTypeName;

Expand All @@ -84,6 +129,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);
Expand All @@ -102,6 +151,7 @@ class SimpleTriangleMesh : public Structure {
PersistentValue<std::string> material;
PersistentValue<BackFacePolicy> backFacePolicy;
PersistentValue<glm::vec3> backFaceColor;
PersistentValue<MeshSelectionMode> selectionMode;

// Drawing related things
// if nullptr, prepare() (resp. preparePick()) needs to be called
Expand All @@ -112,13 +162,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<float>& data,
DataType type);
SimpleTriangleMeshFaceScalarQuantity* addFaceScalarQuantityImpl(std::string name, const std::vector<float>& data,
DataType type);
SimpleTriangleMeshVertexColorQuantity* addVertexColorQuantityImpl(std::string name,
const std::vector<glm::vec3>& colors);
SimpleTriangleMeshFaceColorQuantity* addFaceColorQuantityImpl(std::string name, const std::vector<glm::vec3>& colors);

// == Picking related things
size_t pickStart;
glm::vec3 pickColor;
size_t pickStart = 0;
};


Expand Down
59 changes: 59 additions & 0 deletions include/polyscope/simple_triangle_mesh.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,65 @@ void SimpleTriangleMesh::update(const V& newPositions, const F& newFaces) {
faces.markHostBufferUpdated();
}

template <class V>
void SimpleTriangleMesh::updateVertexPositions(const V& newPositions) {
validateSize(newPositions, nVertices(), "newPositions");
verticesData = standardizeVectorArray<glm::vec3, 3>(newPositions);
vertices.markHostBufferUpdated();
updateObjectSpaceBounds();
}

template <class V, class F>
void SimpleTriangleMesh::updateMesh(const V& newVerts, const F& newFaces) {
std::vector<glm::vec3> vertsStd = standardizeVectorArray<glm::vec3, 3>(newVerts);
if (vertsStd.size() > verticesData.capacity()) {
// amortized doubling on the CPU-side backing vectors.
verticesData.reserve(std::max(vertsStd.size(), 2 * verticesData.capacity()));
}
verticesData = std::move(vertsStd);
vertices.markHostBufferUpdated();

std::vector<glm::uvec3> facesStd = standardizeVectorArray<glm::uvec3, 3>(newFaces);
if (facesStd.size() > facesData.capacity()) {
facesData.reserve(std::max(facesStd.size(), 2 * facesData.capacity()));
}
facesData = std::move(facesStd);
faces.markHostBufferUpdated();

updateObjectSpaceBounds();
}

// =====================================================
// ============== Quantities
// =====================================================

template <class T>
SimpleTriangleMeshVertexScalarQuantity* SimpleTriangleMesh::addVertexScalarQuantity(std::string name, const T& values,
DataType type) {
validateSize(values, nVertices(), "vertex scalar quantity " + name);
return addVertexScalarQuantityImpl(name, standardizeArray<float, T>(values), type);
}

template <class T>
SimpleTriangleMeshFaceScalarQuantity* SimpleTriangleMesh::addFaceScalarQuantity(std::string name, const T& values,
DataType type) {
validateSize(values, nFaces(), "face scalar quantity " + name);
return addFaceScalarQuantityImpl(name, standardizeArray<float, T>(values), type);
}

template <class T>
SimpleTriangleMeshVertexColorQuantity* SimpleTriangleMesh::addVertexColorQuantity(std::string name, const T& values) {
validateSize(values, nVertices(), "vertex color quantity " + name);
return addVertexColorQuantityImpl(name, standardizeVectorArray<glm::vec3, 3>(values));
}

template <class T>
SimpleTriangleMeshFaceColorQuantity* SimpleTriangleMesh::addFaceColorQuantity(std::string name, const T& values) {
validateSize(values, nFaces(), "face color quantity " + name);
return addFaceColorQuantityImpl(name, standardizeVectorArray<glm::vec3, 3>(values));
}


// Shorthand to get a mesh from polyscope
inline SimpleTriangleMesh* getSimpleTriangleMesh(std::string name) {
return dynamic_cast<SimpleTriangleMesh*>(getStructure(SimpleTriangleMesh::structureTypeName, name));
Expand Down
Loading
Loading