Skip to content

Commit

Permalink
simplify: Add meshopt_SimplifyLockBorder option
Browse files Browse the repository at this point in the history
This change adds option bitmask to meshopt_simplify and adds an option
that allows to lock the topological border, which can be valuable to
ensure that multiple parts of the same, larger, mesh can be simplified
in isolation and then stitched together.

This is the last change to meshopt_simplify signature; new options will
either be added as an option bit, which preserves binary interface, or
will be part of new functions such as meshopt_simplifyAttributes or
meshopt_simplifyExtended if we need to add more than just attributes to
the API.
  • Loading branch information
zeux committed Jun 20, 2022
1 parent ea4558d commit 66040c7
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 10 deletions.
2 changes: 1 addition & 1 deletion demo/main.cpp
Expand Up @@ -450,7 +450,7 @@ void simplify(const Mesh& mesh, float threshold = 0.2f)
float result_error = 0;

lod.indices.resize(mesh.indices.size()); // note: simplify needs space for index_count elements in the destination array, not target_index_count
lod.indices.resize(meshopt_simplify(&lod.indices[0], &mesh.indices[0], mesh.indices.size(), &mesh.vertices[0].px, mesh.vertices.size(), sizeof(Vertex), target_index_count, target_error, &result_error));
lod.indices.resize(meshopt_simplify(&lod.indices[0], &mesh.indices[0], mesh.indices.size(), &mesh.vertices[0].px, mesh.vertices.size(), sizeof(Vertex), target_index_count, target_error, 0, &result_error));

lod.vertices.resize(lod.indices.size() < mesh.vertices.size() ? lod.indices.size() : mesh.vertices.size()); // note: this is just to reduce the cost of resize()
lod.vertices.resize(meshopt_optimizeVertexFetch(&lod.vertices[0], &lod.indices[0], lod.indices.size(), &mesh.vertices[0], mesh.vertices.size(), sizeof(Vertex)));
Expand Down
47 changes: 45 additions & 2 deletions demo/tests.cpp
Expand Up @@ -977,14 +977,56 @@ static void simplifyDegenerate()
};

unsigned int expected[] = {
0, 1, 4,
4, 1, 2, // clang-format :-/
0, 1, 4,
4, 1, 2, // clang-format :-/
};

assert(meshopt_simplify(ib, ib, 18, vb, 6, 12, 3, 1e-3f) == 6);
assert(memcmp(ib, expected, sizeof(expected)) == 0);
}

static void simplifyLockBorder()
{
float vb[] = {
0.000000f, 0.000000f, 0.000000f,
0.000000f, 1.000000f, 0.000000f,
0.000000f, 2.000000f, 0.000000f,
1.000000f, 0.000000f, 0.000000f,
1.000000f, 1.000000f, 0.000000f,
1.000000f, 2.000000f, 0.000000f,
2.000000f, 0.000000f, 0.000000f,
2.000000f, 1.000000f, 0.000000f,
2.000000f, 2.000000f, 0.000000f, // clang-format :-/
};

// 0 1 2
// 3 4 5
// 6 7 8

unsigned int ib[] = {
0, 1, 3,
3, 1, 4,
1, 2, 4,
4, 2, 5,
3, 4, 6,
6, 4, 7,
4, 5, 7,
7, 5, 8, // clang-format :-/
};

unsigned int expected[] = {
0, 1, 3,
1, 2, 3,
3, 2, 5,
6, 3, 7,
3, 5, 7,
7, 5, 8, // clang-format :-/
};

assert(meshopt_simplify(ib, ib, 24, vb, 9, 12, 3, 1e-3f, meshopt_SimplifyLockBorder) == 18);
assert(memcmp(ib, expected, sizeof(expected)) == 0);
}

static void adjacency()
{
// 0 1/4
Expand Down Expand Up @@ -1098,6 +1140,7 @@ void runTests()
simplifyFlip();
simplifyScale();
simplifyDegenerate();
simplifyLockBorder();

adjacency();
tessellation();
Expand Down
18 changes: 14 additions & 4 deletions src/meshoptimizer.h
Expand Up @@ -312,6 +312,15 @@ MESHOPTIMIZER_EXPERIMENTAL void meshopt_encodeFilterOct(void* destination, size_
MESHOPTIMIZER_EXPERIMENTAL void meshopt_encodeFilterQuat(void* destination, size_t count, size_t stride, int bits, const float* data);
MESHOPTIMIZER_EXPERIMENTAL void meshopt_encodeFilterExp(void* destination, size_t count, size_t stride, int bits, const float* data);

/**
* Simplification options
*/
enum
{
/* Do not move vertices that are located on the topological border (vertices on triangle edges that don't have a paired triangle). Useful for simplifying portions of the larger mesh. */
meshopt_SimplifyLockBorder = 1 << 0,
};

/**
* Experimental: Mesh simplifier
* Reduces the number of triangles in the mesh, attempting to preserve mesh appearance as much as possible
Expand All @@ -324,9 +333,10 @@ MESHOPTIMIZER_EXPERIMENTAL void meshopt_encodeFilterExp(void* destination, size_
* destination must contain enough space for the target index buffer, worst case is index_count elements (*not* target_index_count)!
* vertex_positions should have float3 position in the first 12 bytes of each vertex - similar to glVertexPointer
* target_error represents the error relative to mesh extents that can be tolerated, e.g. 0.01 = 1% deformation
* options must be a bitmask composed of meshopt_SimplifyX options; 0 is a safe default
* result_error can be NULL; when it's not NULL, it will contain the resulting (relative) error after simplification
*/
MESHOPTIMIZER_EXPERIMENTAL size_t meshopt_simplify(unsigned int* destination, const unsigned int* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, size_t target_index_count, float target_error, float* result_error);
MESHOPTIMIZER_EXPERIMENTAL size_t meshopt_simplify(unsigned int* destination, const unsigned int* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, size_t target_index_count, float target_error, unsigned int options, float* result_error);

/**
* Experimental: Mesh simplifier (sloppy)
Expand Down Expand Up @@ -605,7 +615,7 @@ inline size_t meshopt_encodeIndexSequence(unsigned char* buffer, size_t buffer_s
template <typename T>
inline int meshopt_decodeIndexSequence(T* destination, size_t index_count, const unsigned char* buffer, size_t buffer_size);
template <typename T>
inline size_t meshopt_simplify(T* destination, const T* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, size_t target_index_count, float target_error, float* result_error = 0);
inline size_t meshopt_simplify(T* destination, const T* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, size_t target_index_count, float target_error, unsigned int options = 0, float* result_error = 0);
template <typename T>
inline size_t meshopt_simplifySloppy(T* destination, const T* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, size_t target_index_count, float target_error, float* result_error = 0);
template <typename T>
Expand Down Expand Up @@ -940,12 +950,12 @@ inline int meshopt_decodeIndexSequence(T* destination, size_t index_count, const
}

template <typename T>
inline size_t meshopt_simplify(T* destination, const T* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, size_t target_index_count, float target_error, float* result_error)
inline size_t meshopt_simplify(T* destination, const T* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, size_t target_index_count, float target_error, unsigned int options, float* result_error)
{
meshopt_IndexAdapter<T> in(0, indices, index_count);
meshopt_IndexAdapter<T> out(destination, 0, index_count);

return meshopt_simplify(out.data, in.data, index_count, vertex_positions, vertex_count, vertex_positions_stride, target_index_count, target_error, result_error);
return meshopt_simplify(out.data, in.data, index_count, vertex_positions, vertex_count, vertex_positions_stride, target_index_count, target_error, options, result_error);
}

template <typename T>
Expand Down
12 changes: 9 additions & 3 deletions src/simplifier.cpp
Expand Up @@ -252,7 +252,7 @@ static bool hasEdge(const EdgeAdjacency& adjacency, unsigned int a, unsigned int
return false;
}

static void classifyVertices(unsigned char* result, unsigned int* loop, unsigned int* loopback, size_t vertex_count, const EdgeAdjacency& adjacency, const unsigned int* remap, const unsigned int* wedge)
static void classifyVertices(unsigned char* result, unsigned int* loop, unsigned int* loopback, size_t vertex_count, const EdgeAdjacency& adjacency, const unsigned int* remap, const unsigned int* wedge, unsigned int options)
{
memset(loop, -1, vertex_count * sizeof(unsigned int));
memset(loopback, -1, vertex_count * sizeof(unsigned int));
Expand Down Expand Up @@ -362,6 +362,11 @@ static void classifyVertices(unsigned char* result, unsigned int* loop, unsigned
}
}

if (options & meshopt_SimplifyLockBorder)
for (size_t i = 0; i < vertex_count; ++i)
if (result[i] == Kind_Border)
result[i] = Kind_Locked;

#if TRACE
printf("locked: many open edges %d, disconnected seam %d, many seam edges %d, many wedges %d\n",
int(stats[0]), int(stats[1]), int(stats[2]), int(stats[3]));
Expand Down Expand Up @@ -1272,14 +1277,15 @@ MESHOPTIMIZER_API unsigned int* meshopt_simplifyDebugLoop = 0;
MESHOPTIMIZER_API unsigned int* meshopt_simplifyDebugLoopBack = 0;
#endif

size_t meshopt_simplify(unsigned int* destination, const unsigned int* indices, size_t index_count, const float* vertex_positions_data, size_t vertex_count, size_t vertex_positions_stride, size_t target_index_count, float target_error, float* out_result_error)
size_t meshopt_simplify(unsigned int* destination, const unsigned int* indices, size_t index_count, const float* vertex_positions_data, size_t vertex_count, size_t vertex_positions_stride, size_t target_index_count, float target_error, unsigned int options, float* out_result_error)
{
using namespace meshopt;

assert(index_count % 3 == 0);
assert(vertex_positions_stride > 0 && vertex_positions_stride <= 256);
assert(vertex_positions_stride % sizeof(float) == 0);
assert(target_index_count <= index_count);
assert((options & ~(meshopt_SimplifyLockBorder)) == 0);

meshopt_Allocator allocator;

Expand All @@ -1299,7 +1305,7 @@ size_t meshopt_simplify(unsigned int* destination, const unsigned int* indices,
unsigned char* vertex_kind = allocator.allocate<unsigned char>(vertex_count);
unsigned int* loop = allocator.allocate<unsigned int>(vertex_count);
unsigned int* loopback = allocator.allocate<unsigned int>(vertex_count);
classifyVertices(vertex_kind, loop, loopback, vertex_count, adjacency, remap, wedge);
classifyVertices(vertex_kind, loop, loopback, vertex_count, adjacency, remap, wedge, options);

#if TRACE
size_t unique_positions = 0;
Expand Down

0 comments on commit 66040c7

Please sign in to comment.