Using Meshoptimizer Library to merge meshlets and simplify their geometry

After reading a paper and watchind some videos about nanite I decided to try (Doing my best…) to implement an hierarchical LODs rendering using mesh shaders. I’m using Zeux’s library Meshoptmizer(GitHub - zeux/meshoptimizer: Mesh optimization library that makes meshes smaller and faster to render) to help me achieve that. The library defines a meshlet as a couple of indexes and counts.

struct meshopt_Meshlet
{
	/* offsets within meshlet_vertices and meshlet_triangles arrays with meshlet data */
	unsigned int vertex_offset;
	unsigned int triangle_offset;

	/* number of vertices and triangles used in the meshlet; data is stored in consecutive range defined by offset and count */
	unsigned int vertex_count;
	unsigned int triangle_count;
};

The function meshopt_buildMeshlets outputs an array of meshopt_meshlet 's and the buffers that each meshopt_meshlet indexes into (meshlet_triangles and meshlet_vertices). I would like to know, given this structure, how would I merge two meshletsand then use the method meshopt_simplify to simplify their geometry. All this indexing process is quite complicated for me, I’m trying to think in a way that makes sense and works for me.

                Cluster father;
                Cluster children1               = active_clusters[neighbor_of_i];
                Cluster children2               = active_clusters[i];
                father.meshlet.triangle_offset  = min(children1.meshlet.triangle_offset,children2.meshlet.triangle_offset);
                father.meshlet.vertex_offset    = min(children1.meshlet.vertex_offset,children2.meshlet.vertex_offset);
                father.meshlet.vertex_count     = children1.meshlet.vertex_count + children2.meshlet.vertex_count;
                father.meshlet.triangle_count   = children1.meshlet.triangle_count + children2.meshlet.triangle_count;
                father.aabb                     = compute_cluster_aabb(vertices,father.meshlet);//Compute AABB

Consider that Cluster has a meshopt_meshlet field.

Could anyone demonstrate to me how do Merge two meshopt_meshlets and simplify their geomtry using meshopt_simplify, I’m not sure about how to properly index the the final vertex/index buffer. This question may be a bit hard for me to properly explain myself, so please let me know what details you need.

1 Like

to use the meshopt_simplify on the merged meshlet, you need to set the offset correctly.
below is my code,it can merge meshlets and simplify it.Hope it will help!

static void merge(std::vector<meshopt_Meshlet>& meshlets, std::vector<uint32_t>& index, std::vector<meshopt_Bounds>& meshletBounds, std::vector<uint8_t>vertices, int stride)
		{
			std::vector<uint32_t>new_index;
			std::vector<meshopt_Meshlet>new_meshlets;
			std::vector<meshopt_Bounds>new_bound;
			std::vector<bool>is_merged(meshlets.size(), false);
			auto offset = 0;
			for (int cnt = 0; cnt + 1 < meshlets.size(); cnt = cnt + 2)
			{
				meshopt_Meshlet mt1, mt2;
				{
					.....
                                       //find mt1,mt2
				}
				for (int i = 0; i < mt1.triangle_count * 3; i++)
				{
					new_index.emplace_back(index[mt1.triangle_offset + i]);
				}
				for (int i = 0; i < mt2.triangle_count * 3; i++)
				{
					new_index.emplace_back(index[mt2.triangle_offset + i]);
				}
				meshopt_Meshlet tmp;
				tmp.triangle_count = mt1.triangle_count + mt2.triangle_count;
				tmp.triangle_offset = offset;
				new_meshlets.emplace_back(tmp);
				offset = new_index.size();
				//
				auto p = meshopt_computeClusterBounds(&new_index[tmp.triangle_offset], tmp.triangle_count * 3, (float*)vertices.data(), vertices.size() / stride, stride);
				new_bound.emplace_back(p);
				//
			}
			//odd size
			if (meshlets.size() % 2 != 0)
			{
				meshopt_Meshlet last;
				for (int id = 0; id < is_merged.size(); id++)
				{
					if (is_merged[id] == false)
					{
						last = meshlets[id];
					}
				}
				for (int i = 0; i < last.triangle_count * 3; i++)
				{
					new_index.emplace_back(index[last.triangle_offset + i]);
				}
				meshopt_Meshlet tmp;
				tmp.triangle_count = last.triangle_count;
				tmp.triangle_offset = offset;
				new_meshlets.emplace_back(tmp);
				offset = new_index.size();
			}
			meshlets.assign(new_meshlets.begin(), new_meshlets.end());
			index.assign(new_index.begin(), new_index.end());
			//simplify meshlets
			for (int i = 0; i < meshlets.size(); i++)
			{
				auto& mt = meshlets[i];
				float target_error = 0.9;
				float lod_error = 0.f;
				auto n = meshopt_simplify(&index[mt.triangle_offset], &index[mt.triangle_offset], mt.triangle_count * 3, (float*)vertices.data(), vertices.size() / stride, stride,
					6, target_error, 1, &lod_error);
				mt.triangle_count = n / 3;
			}
			//recalculate the bounds
			for (int i = 0; i < meshlets.size(); i++)
			{
				auto tmp = meshlets[i];
				auto p = meshopt_computeClusterBounds(&new_index[tmp.triangle_offset], tmp.triangle_count * 3, (float*)vertices.data(), vertices.size() / stride, stride);
				new_bound.emplace_back(p);
			}
			meshletBounds.assign(new_bound.begin(), new_bound.end());


1 Like

This topic was automatically closed 183 days after the last reply. New replies are no longer allowed.