Skip to content
Closed
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
83 changes: 71 additions & 12 deletions demo/clusterlod.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,10 @@ struct clodMesh

// attribute mask to flag attribute discontinuities for permissive simplification; mask (1<<K) corresponds to attribute K
unsigned int attribute_protect_mask;

// per-triangle material IDs for material-aware simplification; can be NULL
// when it's not NULL, must contain index_count/3 material IDs (one per triangle)
unsigned int* triangle_materials;
};

// To compute approximate (perspective) projection error of a cluster in screen space (0..1; multiply by screen height to get pixels):
Expand Down Expand Up @@ -118,6 +122,9 @@ struct clodCluster

// cluster vertex count; indices[] has vertex_count unique entries
size_t vertex_count;

// per-triangle material IDs (size = index_count / 3); NULL if materials are not used
const unsigned int* materials;
};

struct clodGroup
Expand Down Expand Up @@ -189,6 +196,7 @@ struct Cluster
{
size_t vertices;
std::vector<unsigned int> indices;
std::vector<unsigned int> materials; // per-triangle material IDs (size = indices.size() / 3)

int group;
int refined;
Expand Down Expand Up @@ -231,7 +239,7 @@ static clodBounds boundsMerge(const std::vector<Cluster>& clusters, const std::v
return result;
}

static std::vector<Cluster> clusterize(const clodConfig& config, const clodMesh& mesh, const unsigned int* indices, size_t index_count)
static std::vector<Cluster> clusterize(const clodConfig& config, const clodMesh& mesh, const unsigned int* indices, size_t index_count, const unsigned int* triangle_materials = NULL)
{
size_t max_meshlets = meshopt_buildMeshletsBound(index_count, config.max_vertices, config.min_triangles);

Expand All @@ -244,14 +252,28 @@ static std::vector<Cluster> clusterize(const clodConfig& config, const clodMesh&
std::vector<unsigned char> meshlet_triangles(index_count);
#endif

if (config.cluster_spatial)
// use Flex with triangle mapping when materials are provided (spatial doesn't support mapping)
std::vector<unsigned int> meshlet_triangle_map;

if (triangle_materials)
{
meshlet_triangle_map.resize(index_count / 3);
meshlets.resize(meshopt_buildMeshletsFlexWithMapping(meshlets.data(), meshlet_vertices.data(), meshlet_triangles.data(), meshlet_triangle_map.data(), indices, index_count,
mesh.vertex_positions, mesh.vertex_count, mesh.vertex_positions_stride,
config.max_vertices, config.min_triangles, config.max_triangles, 0.f, config.cluster_split_factor));
}
else if (config.cluster_spatial)
{
meshlets.resize(meshopt_buildMeshletsSpatial(meshlets.data(), meshlet_vertices.data(), meshlet_triangles.data(), indices, index_count,
mesh.vertex_positions, mesh.vertex_count, mesh.vertex_positions_stride,
config.max_vertices, config.min_triangles, config.max_triangles, config.cluster_fill_weight));
}
else
{
meshlets.resize(meshopt_buildMeshletsFlex(meshlets.data(), meshlet_vertices.data(), meshlet_triangles.data(), indices, index_count,
mesh.vertex_positions, mesh.vertex_count, mesh.vertex_positions_stride,
config.max_vertices, config.min_triangles, config.max_triangles, 0.f, config.cluster_split_factor));
}

std::vector<Cluster> clusters(meshlets.size());

Expand All @@ -269,6 +291,13 @@ static std::vector<Cluster> clusterize(const clodConfig& config, const clodMesh&
for (size_t j = 0; j < meshlet.triangle_count * 3; ++j)
clusters[i].indices[j] = meshlet_vertices[meshlet.vertex_offset + meshlet_triangles[meshlet.triangle_offset + j]];

if (triangle_materials)
{
clusters[i].materials.resize(meshlet.triangle_count);
for (size_t j = 0; j < meshlet.triangle_count; ++j)
clusters[i].materials[j] = triangle_materials[meshlet_triangle_map[meshlet.triangle_offset / 3 + j]];
}

clusters[i].group = -1;
clusters[i].refined = -1;
}
Expand Down Expand Up @@ -421,7 +450,7 @@ static void simplifyFallback(std::vector<unsigned int>& lod, const clodMesh& mes
lod[i] = subset[lod[i]].id;
}

static std::vector<unsigned int> simplify(const clodConfig& config, const clodMesh& mesh, const std::vector<unsigned int>& indices, const std::vector<unsigned char>& locks, size_t target_count, float* error)
static std::vector<unsigned int> simplify(const clodConfig& config, const clodMesh& mesh, const std::vector<unsigned int>& indices, const std::vector<unsigned char>& locks, size_t target_count, float* error, std::vector<unsigned int>* inout_materials = NULL)
{
if (target_count > indices.size())
return indices;
Expand All @@ -430,19 +459,33 @@ static std::vector<unsigned int> simplify(const clodConfig& config, const clodMe

unsigned int options = meshopt_SimplifySparse | meshopt_SimplifyErrorAbsolute | (config.simplify_permissive ? meshopt_SimplifyPermissive : 0) | (config.simplify_regularize ? meshopt_SimplifyRegularize : 0);

lod.resize(meshopt_simplifyWithAttributes(&lod[0], &indices[0], indices.size(),
// copy materials for permissive retry
std::vector<unsigned int> lod_materials;
unsigned int* lod_materials_ptr = NULL;
if (inout_materials && !inout_materials->empty())
{
lod_materials = *inout_materials;
lod_materials_ptr = &lod_materials[0];
}

lod.resize(meshopt_simplifyWithAttributesAndMaterials(&lod[0], &indices[0], indices.size(),
mesh.vertex_positions, mesh.vertex_count, mesh.vertex_positions_stride,
mesh.vertex_attributes, mesh.vertex_attributes_stride, mesh.attribute_weights, mesh.attribute_count,
&locks[0], target_count, FLT_MAX, options, error));
&locks[0], lod_materials_ptr, target_count, FLT_MAX, options, error));

if (lod.size() > target_count && config.simplify_fallback_permissive && !config.simplify_permissive)
lod.resize(meshopt_simplifyWithAttributes(&lod[0], &indices[0], indices.size(),
{
if (lod_materials_ptr)
lod_materials = *inout_materials; // reset materials for retry

lod.resize(meshopt_simplifyWithAttributesAndMaterials(&lod[0], &indices[0], indices.size(),
mesh.vertex_positions, mesh.vertex_count, mesh.vertex_positions_stride,
mesh.vertex_attributes, mesh.vertex_attributes_stride, mesh.attribute_weights, mesh.attribute_count,
&locks[0], target_count, FLT_MAX, options | meshopt_SimplifyPermissive, error));
&locks[0], lod_materials_ptr, target_count, FLT_MAX, options | meshopt_SimplifyPermissive, error));
}

// while it's possible to call simplifySloppy directly, it doesn't support sparsity or absolute error, so we need to do some extra work
if (lod.size() > target_count && config.simplify_fallback_sloppy)
// while it's possible to call simplifySloppy directly, it doesn't support sparsity, absolute error, or material tracking
if (lod.size() > target_count && config.simplify_fallback_sloppy && !inout_materials)
{
simplifyFallback(lod, mesh, indices, locks, target_count, error);
*error *= config.simplify_error_factor_sloppy; // scale error up to account for appearance degradation
Expand Down Expand Up @@ -478,6 +521,12 @@ static std::vector<unsigned int> simplify(const clodConfig& config, const clodMe
*error = std::min(*error, sqrtf(max_edge_sq) * config.simplify_error_edge_limit);
}

if (lod_materials_ptr)
{
lod_materials.resize(lod.size() / 3);
*inout_materials = lod_materials;
}

return lod;
}

Expand All @@ -495,6 +544,7 @@ static int outputGroup(const clodConfig& config, const clodMesh& mesh, const std
result.indices = cluster.indices.data();
result.index_count = cluster.indices.size();
result.vertex_count = cluster.vertices;
result.materials = cluster.materials.empty() ? NULL : cluster.materials.data();
}

return output_callback ? output_callback(output_context, {depth, simplified}, group_clusters.data(), group_clusters.size()) : -1;
Expand Down Expand Up @@ -580,7 +630,7 @@ size_t clodBuild(clodConfig config, clodMesh mesh, void* output_context, clodOut
}

// initial clusterization splits the original mesh
std::vector<Cluster> clusters = clusterize(config, mesh, mesh.indices, mesh.index_count);
std::vector<Cluster> clusters = clusterize(config, mesh, mesh.indices, mesh.index_count, mesh.triangle_materials);

// compute initial precise bounds; subsequent bounds will be using group-merged bounds
for (Cluster& cluster : clusters)
Expand All @@ -607,8 +657,17 @@ size_t clodBuild(clodConfig config, clodMesh mesh, void* output_context, clodOut
{
std::vector<unsigned int> merged;
merged.reserve(groups[i].size() * config.max_triangles * 3);

std::vector<unsigned int> merged_materials;
if (mesh.triangle_materials)
merged_materials.reserve(groups[i].size() * config.max_triangles);

for (size_t j = 0; j < groups[i].size(); ++j)
{
merged.insert(merged.end(), clusters[groups[i][j]].indices.begin(), clusters[groups[i][j]].indices.end());
if (mesh.triangle_materials)
merged_materials.insert(merged_materials.end(), clusters[groups[i][j]].materials.begin(), clusters[groups[i][j]].materials.end());
}

size_t target_size = size_t((merged.size() / 3) * config.simplify_ratio) * 3;

Expand All @@ -617,7 +676,7 @@ size_t clodBuild(clodConfig config, clodMesh mesh, void* output_context, clodOut
clodBounds bounds = boundsMerge(clusters, groups[i]);

float error = 0.f;
std::vector<unsigned int> simplified = simplify(config, mesh, merged, locks, target_size, &error);
std::vector<unsigned int> simplified = simplify(config, mesh, merged, locks, target_size, &error, merged_materials.empty() ? NULL : &merged_materials);
if (simplified.size() > merged.size() * config.simplify_threshold)
{
bounds.error = FLT_MAX; // terminal group, won't simplify further
Expand All @@ -635,7 +694,7 @@ size_t clodBuild(clodConfig config, clodMesh mesh, void* output_context, clodOut
for (size_t j = 0; j < groups[i].size(); ++j)
clusters[groups[i][j]].indices = std::vector<unsigned int>();

std::vector<Cluster> split = clusterize(config, mesh, simplified.data(), simplified.size());
std::vector<Cluster> split = clusterize(config, mesh, simplified.data(), simplified.size(), merged_materials.empty() ? NULL : merged_materials.data());

for (Cluster& cluster : split)
{
Expand Down
19 changes: 14 additions & 5 deletions src/clusterizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ static float computeTriangleCones(Cone* triangles, const unsigned int* indices,
return mesh_area;
}

static bool appendMeshlet(meshopt_Meshlet& meshlet, unsigned int a, unsigned int b, unsigned int c, short* used, meshopt_Meshlet* meshlets, unsigned int* meshlet_vertices, unsigned char* meshlet_triangles, size_t meshlet_offset, size_t max_vertices, size_t max_triangles, bool split = false)
static bool appendMeshlet(meshopt_Meshlet& meshlet, unsigned int a, unsigned int b, unsigned int c, unsigned int triangle_index, short* used, meshopt_Meshlet* meshlets, unsigned int* meshlet_vertices, unsigned char* meshlet_triangles, unsigned int* meshlet_triangle_map, size_t meshlet_offset, size_t max_vertices, size_t max_triangles, bool split = false)
{
short& av = used[a];
short& bv = used[b];
Expand Down Expand Up @@ -286,6 +286,10 @@ static bool appendMeshlet(meshopt_Meshlet& meshlet, unsigned int a, unsigned int
meshlet_triangles[meshlet.triangle_offset + meshlet.triangle_count * 3 + 0] = (unsigned char)av;
meshlet_triangles[meshlet.triangle_offset + meshlet.triangle_count * 3 + 1] = (unsigned char)bv;
meshlet_triangles[meshlet.triangle_offset + meshlet.triangle_count * 3 + 2] = (unsigned char)cv;

if (meshlet_triangle_map)
meshlet_triangle_map[meshlet.triangle_offset / 3 + meshlet.triangle_count] = triangle_index;

meshlet.triangle_count++;

return result;
Expand Down Expand Up @@ -1024,7 +1028,7 @@ size_t meshopt_buildMeshletsBound(size_t index_count, size_t max_vertices, size_
return meshlet_limit_vertices > meshlet_limit_triangles ? meshlet_limit_vertices : meshlet_limit_triangles;
}

size_t meshopt_buildMeshletsFlex(meshopt_Meshlet* meshlets, unsigned int* meshlet_vertices, unsigned char* meshlet_triangles, const unsigned int* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, size_t max_vertices, size_t min_triangles, size_t max_triangles, float cone_weight, float split_factor)
size_t meshopt_buildMeshletsFlexWithMapping(meshopt_Meshlet* meshlets, unsigned int* meshlet_vertices, unsigned char* meshlet_triangles, unsigned int* meshlet_triangle_map, const unsigned int* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, size_t max_vertices, size_t min_triangles, size_t max_triangles, float cone_weight, float split_factor)
{
using namespace meshopt;

Expand Down Expand Up @@ -1166,7 +1170,7 @@ size_t meshopt_buildMeshletsFlex(meshopt_Meshlet* meshlets, unsigned int* meshle
assert(a < vertex_count && b < vertex_count && c < vertex_count);

// add meshlet to the output; when the current meshlet is full we reset the accumulated bounds
if (appendMeshlet(meshlet, a, b, c, used, meshlets, meshlet_vertices, meshlet_triangles, meshlet_offset, max_vertices, max_triangles, split))
if (appendMeshlet(meshlet, a, b, c, best_triangle, used, meshlets, meshlet_vertices, meshlet_triangles, meshlet_triangle_map, meshlet_offset, max_vertices, max_triangles, split))
{
meshlet_offset++;
memset(&meshlet_cone_acc, 0, sizeof(meshlet_cone_acc));
Expand Down Expand Up @@ -1215,6 +1219,11 @@ size_t meshopt_buildMeshletsFlex(meshopt_Meshlet* meshlets, unsigned int* meshle
return meshlet_offset;
}

size_t meshopt_buildMeshletsFlex(meshopt_Meshlet* meshlets, unsigned int* meshlet_vertices, unsigned char* meshlet_triangles, const unsigned int* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, size_t max_vertices, size_t min_triangles, size_t max_triangles, float cone_weight, float split_factor)
{
return meshopt_buildMeshletsFlexWithMapping(meshlets, meshlet_vertices, meshlet_triangles, NULL, indices, index_count, vertex_positions, vertex_count, vertex_positions_stride, max_vertices, min_triangles, max_triangles, cone_weight, split_factor);
}

size_t meshopt_buildMeshlets(meshopt_Meshlet* meshlets, unsigned int* meshlet_vertices, unsigned char* meshlet_triangles, const unsigned int* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, size_t max_vertices, size_t max_triangles, float cone_weight)
{
return meshopt_buildMeshletsFlex(meshlets, meshlet_vertices, meshlet_triangles, indices, index_count, vertex_positions, vertex_count, vertex_positions_stride, max_vertices, max_triangles, max_triangles, cone_weight, 0.0f);
Expand Down Expand Up @@ -1244,7 +1253,7 @@ size_t meshopt_buildMeshletsScan(meshopt_Meshlet* meshlets, unsigned int* meshle
assert(a < vertex_count && b < vertex_count && c < vertex_count);

// appends triangle to the meshlet and writes previous meshlet to the output if full
meshlet_offset += appendMeshlet(meshlet, a, b, c, used, meshlets, meshlet_vertices, meshlet_triangles, meshlet_offset, max_vertices, max_triangles);
meshlet_offset += appendMeshlet(meshlet, a, b, c, unsigned(i / 3), used, meshlets, meshlet_vertices, meshlet_triangles, NULL, meshlet_offset, max_vertices, max_triangles);
}

if (meshlet.triangle_count)
Expand Down Expand Up @@ -1342,7 +1351,7 @@ size_t meshopt_buildMeshletsSpatial(struct meshopt_Meshlet* meshlets, unsigned i
unsigned int a = indices[index * 3 + 0], b = indices[index * 3 + 1], c = indices[index * 3 + 2];

// appends triangle to the meshlet and writes previous meshlet to the output if full
meshlet_offset += appendMeshlet(meshlet, a, b, c, used, meshlets, meshlet_vertices, meshlet_triangles, meshlet_offset, max_vertices, max_triangles, split);
meshlet_offset += appendMeshlet(meshlet, a, b, c, index, used, meshlets, meshlet_vertices, meshlet_triangles, NULL, meshlet_offset, max_vertices, max_triangles, split);
meshlet_pending -= boundary[i];
}

Expand Down
19 changes: 19 additions & 0 deletions src/meshoptimizer.h
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,15 @@ MESHOPTIMIZER_API size_t meshopt_simplify(unsigned int* destination, const unsig
*/
MESHOPTIMIZER_API size_t meshopt_simplifyWithAttributes(unsigned int* destination, const unsigned int* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, const float* vertex_attributes, size_t vertex_attributes_stride, const float* attribute_weights, size_t attribute_count, const unsigned char* vertex_lock, size_t target_index_count, float target_error, unsigned int options, float* result_error);

/**
* Mesh simplifier with attribute metric and per-triangle material tracking
* Similar to meshopt_simplifyWithAttributes, but also tracks per-triangle material IDs through simplification.
*
* triangle_materials can be NULL; when it's not NULL, must contain index_count/3 material IDs (one per triangle), modified in-place during simplification
* All other parameters are the same as meshopt_simplifyWithAttributes
*/
MESHOPTIMIZER_API size_t meshopt_simplifyWithAttributesAndMaterials(unsigned int* destination, const unsigned int* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, const float* vertex_attributes, size_t vertex_attributes_stride, const float* attribute_weights, size_t attribute_count, const unsigned char* vertex_lock, unsigned int* triangle_materials, size_t target_index_count, float target_error, unsigned int options, float* result_error);

/**
* Mesh simplifier with position/attribute update
* Reduces the number of triangles in the mesh, attempting to preserve mesh appearance as much as possible.
Expand Down Expand Up @@ -716,6 +725,16 @@ MESHOPTIMIZER_API size_t meshopt_buildMeshletsBound(size_t index_count, size_t m
*/
MESHOPTIMIZER_API size_t meshopt_buildMeshletsFlex(struct meshopt_Meshlet* meshlets, unsigned int* meshlet_vertices, unsigned char* meshlet_triangles, const unsigned int* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, size_t max_vertices, size_t min_triangles, size_t max_triangles, float cone_weight, float split_factor);

/**
* Meshlet builder with triangle mapping support
* Similar to meshopt_buildMeshletsFlex, but also outputs a mapping from meshlet triangles to original triangles.
*
* meshlet_triangle_map can be NULL; when it's not NULL, must contain enough space for index_count/3 elements
* meshlet_triangle_map[i] will contain the original triangle index for the i-th triangle in the meshlet output
* All other parameters are the same as meshopt_buildMeshletsFlex
*/
MESHOPTIMIZER_API size_t meshopt_buildMeshletsFlexWithMapping(struct meshopt_Meshlet* meshlets, unsigned int* meshlet_vertices, unsigned char* meshlet_triangles, unsigned int* meshlet_triangle_map, const unsigned int* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, size_t max_vertices, size_t min_triangles, size_t max_triangles, float cone_weight, float split_factor);

/**
* Meshlet builder that produces clusters optimized for raytracing
* Splits the mesh into a set of meshlets, similarly to meshopt_buildMeshlets, but optimizes cluster subdivision for raytracing and allows to specify minimum and maximum number of triangles per meshlet.
Expand Down
Loading
Loading