10#include "meshoptimizer.h"
18 const std::vector<Vertex>& vertices,
19 const std::vector<uint32_t>& indices)
21 if (indices.empty()) {
22 return glm::vec4(0.0f);
26 glm::vec3 center(0.0f);
27 for (uint32_t idx : indices) {
28 center += glm::vec3(vertices[idx].position);
30 center /=
static_cast<float>(indices.size());
33 float radiusSq = 0.0f;
34 for (uint32_t idx : indices) {
35 glm::vec3 pos = glm::vec3(vertices[idx].position);
36 float distSq = glm::dot(pos - center, pos - center);
37 radiusSq = std::max(radiusSq, distSq);
40 return glm::vec4(center, std::sqrt(radiusSq));
44 const std::vector<Vertex>& vertices,
45 const meshopt_Meshlet& meshlet,
46 const std::vector<uint32_t>& meshletVertices)
48 if (meshlet.vertex_count == 0) {
49 return glm::vec4(0.0f);
53 glm::vec3 center(0.0f);
54 for (uint32_t i = 0; i < meshlet.vertex_count; ++i) {
55 uint32_t vertexIndex = meshletVertices[meshlet.vertex_offset + i];
56 center += glm::vec3(vertices[vertexIndex].position);
58 center /=
static_cast<float>(meshlet.vertex_count);
61 float radiusSq = 0.0f;
62 for (uint32_t i = 0; i < meshlet.vertex_count; ++i) {
63 uint32_t vertexIndex = meshletVertices[meshlet.vertex_offset + i];
64 glm::vec3 pos = glm::vec3(vertices[vertexIndex].position);
65 float distSq = glm::dot(pos - center, pos - center);
66 radiusSq = std::max(radiusSq, distSq);
69 return glm::vec4(center, std::sqrt(radiusSq));
73 const std::vector<Vertex>& srcVertices,
74 const std::vector<uint32_t>& srcIndices,
75 size_t targetIndexCount,
77 size_t maxVerticesPerMeshlet,
78 size_t maxTrianglesPerMeshlet)
83 result.
indices.resize(srcIndices.size());
84 float resultError = 0.0f;
86 size_t newIndexCount = meshopt_simplify(
90 &srcVertices[0].position.x,
99 result.
indices.resize(newIndexCount);
100 result.
error = resultError;
102 if (newIndexCount == 0) {
107 std::vector<uint32_t> remap(srcVertices.size());
108 size_t uniqueVertexCount = meshopt_generateVertexRemap(
117 result.
vertices.resize(uniqueVertexCount);
118 meshopt_remapVertexBuffer(
126 std::vector<uint32_t> remappedIndices(result.
indices.size());
127 meshopt_remapIndexBuffer(
128 remappedIndices.data(),
133 result.
indices = std::move(remappedIndices);
136 meshopt_optimizeVertexCache(
144 size_t maxMeshlets = meshopt_buildMeshletsBound(
146 maxVerticesPerMeshlet,
147 maxTrianglesPerMeshlet
150 result.
meshlets.resize(maxMeshlets);
154 size_t meshletCount = meshopt_buildMeshlets(
163 maxVerticesPerMeshlet,
164 maxTrianglesPerMeshlet,
169 if (meshletCount > 0) {
170 const auto& lastMeshlet = result.
meshlets[meshletCount - 1];
171 result.
meshlets.resize(meshletCount);
172 result.
meshletVertices.resize(lastMeshlet.vertex_offset + lastMeshlet.vertex_count);
173 result.
meshletTriangles.resize(lastMeshlet.triangle_offset + lastMeshlet.triangle_count * 3);
184 const std::vector<Vertex>& vertices,
185 const std::vector<uint32_t>& indices,
186 const std::vector<meshopt_Meshlet>& baseMeshlets,
187 const std::vector<uint32_t>& baseMeshletVertices,
188 const std::vector<uint8_t>& baseMeshletTriangles,
195 VS_LOG(
LogLOD,
Debug,
"Mesh too small for LOD generation (indices: {}, min triangles: {})",
200 VS_LOG(
LogLOD,
Info,
"Generating LOD hierarchy: {} triangles, {} base meshlets",
201 indices.size() / 3, baseMeshlets.size());
204 std::vector<LodLevelData> lodLevels;
213 baseLod.
error = 0.0f;
214 lodLevels.push_back(std::move(baseLod));
219 size_t currentIndexCount = indices.size();
220 float accumulatedError = 0.0f;
222 for (
size_t level = 1; level < config.
maxLodLevels; ++level) {
223 size_t targetIndexCount =
static_cast<size_t>(currentIndexCount * config.
simplificationRatio);
224 targetIndexCount = (targetIndexCount / 3) * 3;
226 if (targetIndexCount < 12) {
230 const auto& prevLevel = lodLevels.back();
241 VS_LOG(
LogLOD,
Debug,
"LOD level {} generation failed or produced empty result", level);
245 accumulatedError += newLevel.
error;
246 newLevel.
error = accumulatedError;
251 currentIndexCount = newLevel.
indices.size();
252 lodLevels.push_back(std::move(newLevel));
255 if (newLevel.
meshlets.size() <= 2) {
267 size_t totalMeshlets = 0;
268 for (
const auto& level : lodLevels) {
269 totalMeshlets += level.meshlets.size();
272 result.
clusters.reserve(totalMeshlets);
273 result.
groups.reserve(lodLevels.size() - 1);
276 uint32_t globalMeshletIndex = 0;
277 std::vector<uint32_t> levelMeshletStartIndices;
279 for (
size_t levelIdx = 0; levelIdx < lodLevels.size(); ++levelIdx) {
280 const auto& level = lodLevels[levelIdx];
281 levelMeshletStartIndices.push_back(globalMeshletIndex);
283 for (
size_t meshletIdx = 0; meshletIdx < level.meshlets.size(); ++meshletIdx) {
284 const auto& meshlet = level.meshlets[meshletIdx];
288 level.vertices, meshlet, level.meshletVertices);
289 cluster.
error = level.error;
299 if (levelIdx < lodLevels.size() - 1) {
308 globalMeshletIndex++;
313 for (
size_t levelIdx = 0; levelIdx < lodLevels.size() - 1; ++levelIdx) {
314 const auto& coarserLevel = lodLevels[levelIdx + 1];
319 coarserLevel.vertices, coarserLevel.indices);
321 group.
depth =
static_cast<int32_t
>(lodLevels.size() - 1 - levelIdx);
323 result.
groups.push_back(group);
328 size_t totalVertices = 0;
329 size_t totalTriangles = 0;
330 for (
const auto& level : lodLevels) {
331 for (
const auto& meshlet : level.meshlets) {
332 totalVertices += meshlet.vertex_count;
333 totalTriangles += meshlet.triangle_count;
341 uint32_t vertexOffset = 0;
342 uint32_t triangleOffset = 0;
344 for (
const auto& level : lodLevels) {
345 for (
const auto& meshlet : level.meshlets) {
347 meshopt_Meshlet unifiedMeshlet;
348 unifiedMeshlet.vertex_offset = vertexOffset;
349 unifiedMeshlet.vertex_count = meshlet.vertex_count;
350 unifiedMeshlet.triangle_offset = triangleOffset;
351 unifiedMeshlet.triangle_count = meshlet.triangle_count;
355 for (uint32_t i = 0; i < meshlet.vertex_count; ++i) {
356 uint32_t srcVertexIndex = level.meshletVertices[meshlet.vertex_offset + i];
361 for (uint32_t i = 0; i < meshlet.triangle_count; ++i) {
362 const uint8_t* triPtr = &level.meshletTriangles[meshlet.triangle_offset + i * 3];
368 vertexOffset += meshlet.vertex_count;
369 triangleOffset += meshlet.triangle_count;
373 VS_LOG(
LogLOD,
Info,
"LOD hierarchy generated: {} levels, {} clusters, {} groups",
::EngineCore::LogCategory LogLOD("LogLOD", ::EngineCore::LogVerbosity::Info, ::EngineCore::LogVerbosity::Verbose)
#define VS_LOG(Category, Verbosity, Format,...)
Log a message with category and verbosity.
static glm::vec4 computeMeshletBoundingSphere(const std::vector< Vertex > &vertices, const meshopt_Meshlet &meshlet, const std::vector< uint32_t > &meshletVertices)
Compute bounding sphere for a meshlet.
static LodLevelData generateLodLevel(const std::vector< Vertex > &srcVertices, const std::vector< uint32_t > &srcIndices, size_t targetIndexCount, float targetError, size_t maxVerticesPerMeshlet, size_t maxTrianglesPerMeshlet)
static Ecs::LodHierarchyResult generate(const std::vector< Vertex > &vertices, const std::vector< uint32_t > &indices, const std::vector< meshopt_Meshlet > &baseMeshlets, const std::vector< uint32_t > &baseMeshletVertices, const std::vector< uint8_t > &baseMeshletTriangles, const LodGenerationConfig &config={})
Generate LOD hierarchy from mesh geometry.
static glm::vec4 computeBoundingSphere(const std::vector< Vertex > &vertices, const std::vector< uint32_t > &indices)
Compute bounding sphere for a set of vertices.
Log category system implementation.
@ Info
Important state changes.
@ Debug
Development debugging.
Result of LOD hierarchy generation for a mesh primitive. Contains per-cluster and per-group data for ...
std::vector< EngineCore::ClusterLodData > clusters
Per-cluster data (all LOD levels combined)
UnpackedMeshletData unpackedData
Unpacked GPU data for LOD meshlets.
std::vector< EngineCore::ClusterGroupData > groups
MeshletData meshletData
Meshlet data for all clusters (extends base meshlet data)
size_t baseLevelClusterCount
Clusters at LOD 0 (finest level)
size_t lodLevelCount
Number of LOD levels generated.
std::vector< meshopt_Meshlet > meshlets
static PackedTriangle pack(uint8_t i0, uint8_t i1, uint8_t i2)
Pack 3 local indices into a single uint32_t.
std::vector< PackedTriangle > triangles
Packed triangle indices (4 bytes each)
std::vector< Vertex > vertices
Unpacked vertex data per meshlet.
Per-group LOD data representing a simplified version of child clusters. Groups are created by merging...
int32_t depth
DAG depth (0 = coarsest level)
float simplifiedError
Error threshold for this group.
glm::vec4 simplifiedBoundsPositionAndRadius
xyz = local center, w = radius
Per-cluster LOD data for GPU-driven LOD selection. Each cluster maps to exactly one meshlet and conta...
glm::vec4 boundsPositionAndRadius
xyz = local center, w = radius
float error
Simplification error (mesh-local units)
uint32_t meshletIndex
Index into UnifiedMeshlet buffer.
int32_t refinedGroupId
Parent group index (-1 for base/finest geometry)
Generate a single LOD level by simplifying geometry.
std::vector< uint8_t > meshletTriangles
std::vector< Vertex > vertices
std::vector< meshopt_Meshlet > meshlets
std::vector< uint32_t > indices
std::vector< uint32_t > meshletVertices
float error
Simplification error for this level.
Configuration for LOD hierarchy generation.
size_t maxTrianglesPerCluster
Max triangles per cluster/meshlet.
float simplificationRatio
Reduction ratio per LOD level (0.5 = halve each level)
size_t minTrianglesForLod
Minimum triangles to consider LOD generation.
size_t maxLodLevels
Maximum LOD levels to generate.
float targetError
Target simplification error.
The fundamental building block of all meshes in this engine.