Vulkan Schnee 0.0.1
High-performance rendering engine
Loading...
Searching...
No Matches
MeshPrimitive.cpp
Go to the documentation of this file.
9#include "glm/gtx/norm.hpp"
10
11#include <meshoptimizer.h>
12
13namespace EngineCore
14{
15
17 GltfLoader::GltfMeshPrimitiveData meshPrimitiveData,
20 )
21 : context( context )
23 , vertices( meshPrimitiveData.getVertices() )
24 , indices( meshPrimitiveData.getIndices() )
25 {
26 PLOGI << "=== MeshPrimitive CONSTRUCTOR STARTED ===";
27 PLOGI << "MeshPrimitive constructor - vertices: " << vertices.size() << ", indices: " << indices.size();
28
29 // Extract material name from primitive data
30 if ( meshPrimitiveData.getMaterialData().hasVulkanSchneeExtension() )
31 {
32 materialName = meshPrimitiveData.getMaterialData().getVulkanSchneeExtension().materialName;
33 materialData = meshPrimitiveData.getMaterialData().getMaterialDataRaw();
34 PLOGI << "MeshPrimitive loaded with materialID: " << static_cast<int>( materialName );
35 }
36 else
37 {
38 materialName = PipelineNames::NORMALS_SHADER; // Default material
39 PLOGI << "MeshPrimitive using default material (normalsShader)";
40 }
41
42 if ( meshPrimitiveData.getMaterialData().hasTexture() )
43 {
44 textureIndex = meshPrimitiveData.getColorTextureIndex();
45 colorTexture = meshPrimitiveData.getColorTexturePtr();
46 }
47 PLOGI << "=== About to generate meshlets... ===";
49 PLOGI << "=== Meshlet generation completed - meshlets: " << meshlets.size() << " ===";
50 PLOGI << "=== MeshPrimitive CONSTRUCTOR FINISHED ===";
51 }
52
54
55 std::vector<Vertex> MeshPrimitive::getVertices() const
56 {
57 return vertices;
58 }
59
60 const std::vector<Vertex> & MeshPrimitive::getVerticesByRef() const
61 {
62 return vertices;
63 }
64
65 std::vector<uint32_t> MeshPrimitive::getIndices() const
66 {
67 return indices;
68 }
69
70 const std::vector<uint32_t> & MeshPrimitive::getIndicesByRef() const
71 {
72 return indices;
73 }
74
76 {
77 return textureIndex;
78 }
79
80 std::vector<meshopt_Meshlet> MeshPrimitive::getMeshlets() const
81 {
82 return meshlets;
83 }
84
85 const std::vector<meshopt_Meshlet> & MeshPrimitive::getMeshletByRef() const
86 {
87 return meshlets;
88 }
89
90 std::vector<uint8_t> MeshPrimitive::getMeshletTriangles() const
91 {
92 return meshletTriangles;
93 }
94
95 const std::vector<uint8_t> & MeshPrimitive::getMeshletTrianglesByRef() const
96 {
97 return meshletTriangles;
98 }
99
100 std::vector<uint32_t> MeshPrimitive::getMeshletVertices() const
101 {
102 return meshletVertices;
103 }
104
105 const std::vector<uint32_t> & MeshPrimitive::getMeshletVerticesByRef() const
106 {
107 return meshletVertices;
108 }
109
111 {
112 return meshletTriangles.size();
113 }
114
116 {
117 return meshletVertices.size();
118 }
119
121 {
122 return meshlets.size();
123 }
124
126 {
127 return objectCullingData.worldPositionAndRadius.w;
128 }
129
131 {
132 return glm::vec3(objectCullingData.worldPositionAndRadius);
133 }
134
139
141 {
142 return colorTexture->getDescriptorIndex() != 0;
143 }
144
149
154
156 {
157 materialShader = shader;
158 }
159
161 {
162 this->materialId = materialId;
163 }
164
169
170 const std::vector<MeshletBounds> & MeshPrimitive::getMeshletBoundsByRef() const
171 {
172 return meshletBounds;
173 }
174
175 // In MeshPrimitive.cpp
177 {
178 TRACY_ZONE_SCOPED_NAMED( "Generate Meshlets" );
179 PLOGI << "=== GENERATE MESHLETS STARTED ===";
180 PLOGI << "Starting meshlet generation - vertices: " << vertices.size() << ", indices: " << indices.size();
181
182 // --- 0. Vertex Optimization ---
183 // Your existing vertex welding and optimization is good. Keep it.
184 std::vector<uint32_t> remap( vertices.size() );
185 size_t uniqueVertexCount;
186 {
187 TRACY_ZONE_SCOPED_NAMED( "Generate Vertex Remap" );
188 uniqueVertexCount = meshopt_generateVertexRemap(
189 remap.data(),
190 indices.data(),
191 indices.size(),
192 vertices.data(),
193 vertices.size(),
194 sizeof( Vertex )
195 );
196 }
197
198 {
199 std::vector<Vertex> uniqueVertices( uniqueVertexCount );
200 std::vector<uint32_t> uniqueIndices( indices.size() );
201
202 {
203 TRACY_ZONE_SCOPED_NAMED( "Remap vertex buffer" );
204 meshopt_remapVertexBuffer(
205 uniqueVertices.data(), vertices.data(), vertices.size(), sizeof( Vertex ), remap.data()
206 );
207 }
208 {
209 TRACY_ZONE_SCOPED_NAMED( "Remap index buffer" );
210 meshopt_remapIndexBuffer(
211 uniqueIndices.data(), indices.data(), indices.size(), remap.data()
212 );
213 }
214
215 // From now on, use the optimized, unique data.
216 this->vertices = uniqueVertices;
217 this->indices = uniqueIndices;
218 }
219
220 {
221 TRACY_ZONE_SCOPED_NAMED( "Optimize vertex cache" );
222 meshopt_optimizeVertexCache(
223 this->indices.data(), this->indices.data(), this->indices.size(), this->vertices.size()
224 );
225 }
226 {
227 TRACY_ZONE_SCOPED_NAMED( "Optimize vertex fetch" );
228 meshopt_optimizeVertexFetch(
229 this->vertices.data(),
230 this->indices.data(),
231 this->indices.size(),
232 this->vertices.data(),
233 this->vertices.size(),
234 sizeof( Vertex )
235 );
236 }
237
238 // --- 1. Meshlet Generation ---
239 constexpr size_t maxVerticesPerMeshlet = 252;
240 constexpr size_t maxTrianglesPerMeshlet = 252;
241 constexpr float coneWeight = 0.5f;
242 size_t maxMeshlets;
243
244 {
245 TRACY_ZONE_SCOPED_NAMED( "Generate meshlet bounds" );
246 maxMeshlets =
247 meshopt_buildMeshletsBound( indices.size(), maxVerticesPerMeshlet, maxTrianglesPerMeshlet );
248 }
249
250 meshlets.resize( maxMeshlets );
251 meshletVertices.resize( maxMeshlets * maxVerticesPerMeshlet );
252 // Directly generate into the uint8_t member variable.
253 meshletTriangles.resize( maxMeshlets * maxTrianglesPerMeshlet * 3 );
254 size_t meshletCount;
255 {
256 TRACY_ZONE_SCOPED_NAMED( "Build meshlets" );
257 meshletCount = meshopt_buildMeshlets(
258 meshlets.data(),
259 meshletVertices.data(),
260 meshletTriangles.data(), // Generate into temporary uint8_t buffer
261 indices.data(), // Use optimized indices
262 indices.size(),
263 &vertices[0].position.x, // Use optimized vertices
264 vertices.size(),
265 sizeof( Vertex ),
266 maxVerticesPerMeshlet,
267 maxTrianglesPerMeshlet,
268 coneWeight // Use cone weight for better culling
269 );
270 }
271
272 // --- 2. Finalize and Optimize within Meshlets ---
273 {
274 TRACY_ZONE_SCOPED_NAMED( "Optimize meshlets" );
275 if ( meshletCount > 0 )
276 {
277 const auto & [vertex_offset, triangle_offset, vertex_count, triangle_count] =
278 meshlets[meshletCount - 1];
279
280 meshlets.resize( meshletCount );
281 meshletVertices.resize( vertex_offset + vertex_count );
282 meshletTriangles.resize( triangle_offset + ( triangle_count * 3 ) );
283
284 // This is a key step for performance inside the shader.
285 for ( size_t i = 0; i < meshletCount; ++i )
286 {
287 const meshopt_Meshlet & m = meshlets[i];
288 meshopt_optimizeMeshlet(
289 &meshletVertices[m.vertex_offset],
290 &meshletTriangles[m.triangle_offset],
291 m.triangle_count,
292 m.vertex_count
293 );
294 }
295 }
296 else
297 {
298 meshlets.clear();
299 meshletVertices.clear();
300 meshletTriangles.clear();
301 }
302 }
303
304 {
305 TRACY_ZONE_SCOPED_NAMED( "Generate meshlet bounds" );
307 }
308
309 PLOGI << "Generated " << meshlets.size() << " meshlets for primitive.";
310 }
311
313 {
314 TRACY_ZONE_SCOPED_NAMED( "Generate bounding spheres" );
315 if ( vertices.empty() )
316 return;
317
318 // Find most separated points along each axis
319 auto primitiveBoundsHelper = MeshletBoundsHelper( vertices[0].position );
320
321 uint32_t meshletCount = meshlets.size();
322 meshletBounds.resize( meshletCount );
323
324 for ( size_t i = 0; i < meshletCount; ++i )
325 {
326 TRACY_ZONE_SCOPED_NAMED( "Generate bounding spheres" );
327 const meshopt_Meshlet & m = meshlets[i];
328 auto meshletBoundsHelper =
329 MeshletBoundsHelper( vertices[meshletVertices[m.vertex_offset]].position );
330 {
331 TRACY_ZONE_SCOPED_NAMED( "Enlarge bounds" );
332 for ( uint32_t vertexId = 0; vertexId < m.vertex_count; ++vertexId )
333 {
334 const glm::vec3 & pos = vertices[meshletVertices[m.vertex_offset + vertexId]].position;
335 primitiveBoundsHelper.enlarge( pos );
336 meshletBoundsHelper.enlarge( pos );
337 }
338 }
339
340 {
341 TRACY_ZONE_SCOPED_NAMED( "Calculate bounds sphere" );
342 meshletBounds[i] = meshletBoundsHelper.getBounds( { m }, vertices, meshletVertices );
343 meshletBounds[i].meshletIndex = i;
344 }
345 }
346
347 {
348 TRACY_ZONE_SCOPED_NAMED( "Get primitive bounds" );
349 MeshletBounds primitiveBounds =
350 primitiveBoundsHelper.getBounds( meshlets, vertices, meshletVertices );
351 objectCullingData.worldPositionAndRadius = primitiveBounds.worldPositionAndRadius;
352 }
353 }
354
356 {
357 if ( point.x < minX.x )
358 minX = point;
359 if ( point.x > maxX.x )
360 maxX = point;
361 if ( point.y < minY.y )
362 minY = point;
363 if ( point.y > maxY.y )
364 maxY = point;
365 if ( point.z < minZ.z )
366 minZ = point;
367 if ( point.z > maxZ.z )
368 maxZ = point;
369 }
370
372 const std::vector<meshopt_Meshlet> & meshlets,
373 const std::vector<Vertex> & vertices,
374 const std::vector<uint32_t> & meshletVertices
375 ) const
376 {
377 // Find the most separated pair
378 TRACY_ZONE_SCOPED_NAMED( "Calculate Bounds" );
379
380 float distX = glm::distance2( minX, maxX );
381 float distY = glm::distance2( minY, maxY );
382 float distZ = glm::distance2( minZ, maxZ );
383
384 glm::vec3 p1, p2;
385 if ( distX > distY && distX > distZ )
386 {
387 p1 = minX;
388 p2 = maxX;
389 }
390 else if ( distY > distZ )
391 {
392 p1 = minY;
393 p2 = maxY;
394 }
395 else
396 {
397 p1 = minZ;
398 p2 = maxZ;
399 }
400
401 // Initial sphere
402 glm::vec3 center = ( p1 + p2 ) * 0.5f;
403 float radius = glm::distance( center, p2 );
404
405 // Expand to include all points using Ritter's algorithm
406 {
407 TRACY_ZONE_SCOPED_NAMED( "Expand bounds to include all points" );
408
409 // First pass: find the point farthest from current center
410 float maxDistSq = 0.0f;
411 glm::vec3 farthestPoint = center;
412
413 for ( const auto & meshlet : meshlets )
414 {
415 for ( uint32_t vertexId = 0; vertexId < meshlet.vertex_count; ++vertexId )
416 {
417 const glm::vec3 & pos =
418 vertices[meshletVertices[meshlet.vertex_offset + vertexId]].position;
419 glm::vec3 diff = pos - center;
420 float distSq = glm::dot( diff, diff ); // Use squared distance to avoid sqrt
421
422 if ( distSq > maxDistSq )
423 {
424 maxDistSq = distSq;
425 farthestPoint = pos;
426 }
427 }
428 }
429
430 // If we found a point outside the sphere, expand the sphere
431 if ( maxDistSq > radius * radius )
432 {
433 float dist = glm::sqrt( maxDistSq );
434 float newRadius = ( radius + dist ) * 0.5f;
435 center += ( farthestPoint - center ) * ( ( dist - radius ) / dist );
436 radius = newRadius;
437
438 // Second pass: expand again to ensure all points are included
439 // This is typically sufficient for most cases
440 maxDistSq = 0.0f;
441 for ( const auto & meshlet : meshlets )
442 {
443 for ( uint32_t vertexId = 0; vertexId < meshlet.vertex_count; ++vertexId )
444 {
445 const glm::vec3 & pos =
446 vertices[meshletVertices[meshlet.vertex_offset + vertexId]].position;
447 glm::vec3 diff = pos - center;
448 float distSq = glm::dot( diff, diff );
449
450 if ( distSq > maxDistSq )
451 {
452 maxDistSq = distSq;
453 farthestPoint = pos;
454 }
455 }
456 }
457
458 if ( maxDistSq > radius * radius )
459 {
460 float dist = glm::sqrt( maxDistSq );
461 float newRadius = ( radius + dist ) * 0.5f;
462 center += ( farthestPoint - center ) * ( ( dist - radius ) / dist );
463 radius = newRadius;
464 }
465 }
466 }
467
468 return {
469 {center.x, center.y, center.z, radius},
470 };
471 }
472} // namespace EngineCore
#define TRACY_ZONE_SCOPED_NAMED(name)
The application context is the core class which stores the basic openxr and vulkan objects.
std::vector< uint8_t > getMeshletTriangles() const
float getBoundingSphereRadius() const
ApplicationContext * context
std::vector< meshopt_Meshlet > getMeshlets() const
std::vector< Vertex > getVertices() const
PipelineNames getMaterialName() const
uint32_t getTextureIndex() const
ObjectCullingData objectCullingData
const std::vector< uint32_t > & getIndicesByRef() const
const std::vector< MeshletBounds > & getMeshletBoundsByRef() const
uint32_t getMeshletTriangleCount() const
std::vector< uint32_t > meshletVertices
const std::vector< Vertex > & getVerticesByRef() const
std::vector< Vertex > vertices
const std::vector< uint32_t > & getMeshletVerticesByRef() const
void setMaterialShader(MaterialShader *shader)
std::vector< uint32_t > getIndices() const
std::vector< meshopt_Meshlet > meshlets
std::vector< MeshletBounds > meshletBounds
std::vector< uint8_t > meshletTriangles
MaterialShader * materialShader
const ObjectCullingData & getPrimitiveBoundsByRef() const
uint32_t getMeshletVertexCount() const
uint32_t getMeshletsCount() const
const std::vector< uint8_t > & getMeshletTrianglesByRef() const
MaterialShader * getMaterialShader() const
void setMaterialId(uint32_t materialId)
std::vector< uint32_t > getMeshletVertices() const
std::vector< uint32_t > indices
const std::vector< meshopt_Meshlet > & getMeshletByRef() const
glm::vec3 getBoundingSphereCenter() const
ObjectCullingData getCullingData() const
Log category system implementation.
Stores intermediate data from loading the asset from disk. These are the vertices,...
Definition GltfLoader.h:350
MeshletBounds getBounds(const std::vector< meshopt_Meshlet > &meshlets, const std::vector< Vertex > &vertices, const std::vector< uint32_t > &meshletVertices) const
Used in meshlet culling.
Definition RenderData.h:92
glm::vec4 worldPositionAndRadius
Definition RenderData.h:93
Data for object culling.
Definition RenderData.h:144
The fundamental building block of all meshes in this engine.
Definition Vertex.h:15