34 const VkBufferUsageFlags storageUsage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
59 constexpr bool persistentMapping =
true;
71 const VkBufferUsageFlags vertexBufferUsage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
128 const VkBufferUsageFlags storageUsage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
153 constexpr bool persistentMapping =
true;
165 const VkBufferUsageFlags vertexBufferUsage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
242 if (buffer.has_value()) {
275 PLOGI <<
"updateIfDirty: isDirty=true, proceeding with update";
280 bool buffersRecreated;
291 return buffersRecreated;
306 PLOGD <<
"SceneManager not available, skipping primitive data update";
318 std::vector<ObjectCullingData> primitiveCullingBuffer;
319 std::vector<LocalBoundsData> localBoundsDataBuffer;
320 std::vector<PrimitiveMeshletData> primitiveMeshletBuffer;
321 std::vector<glm::mat4> perObjectDataLocal;
322 std::vector<MeshPrimitiveRenderData> primitiveRenderDataBuffer;
325 std::vector<UnifiedMeshlet> meshletDataBuffer;
326 std::vector<MeshletBounds> meshletBoundsBuffer;
329 std::vector<Vertex> optimizedVertexBuffer;
330 std::vector<PackedTriangle> optimizedTriangleBuffer;
342 PLOGD <<
"updatePrimitiveData: Found " << meshView.size() <<
" mesh components via ECS";
346 size_t totalPrimitives = 0;
347 size_t totalMeshlets = 0;
348 size_t totalVertices = 0;
349 size_t totalTriangles = 0;
351 for (
auto entity : meshView) {
353 if (!meshComp || !meshComp->
isVisible())
continue;
357 if (!meshData)
continue;
363 totalMeshlets += primitive.
meshletData->meshlets.size();
364 totalVertices += primitive.
unpackedData->vertices.size();
365 totalTriangles += primitive.
unpackedData->triangles.size();
370 primitiveCullingBuffer.reserve(totalPrimitives);
371 localBoundsDataBuffer.reserve(totalPrimitives);
372 primitiveMeshletBuffer.reserve(totalPrimitives);
373 perObjectDataLocal.reserve(totalPrimitives);
374 primitiveRenderDataBuffer.reserve(totalPrimitives);
375 meshletDataBuffer.reserve(totalMeshlets);
376 meshletBoundsBuffer.reserve(totalMeshlets);
377 optimizedVertexBuffer.reserve(totalVertices);
378 optimizedTriangleBuffer.reserve(totalTriangles);
380 PLOGI <<
"[TIMING] Pre-allocated buffers: " << totalPrimitives <<
" primitives, "
381 << totalMeshlets <<
" meshlets, " << totalVertices <<
" vertices, "
382 << totalTriangles <<
" triangles"
383 <<
" (vertex buffer: " << (totalVertices *
sizeof(
Vertex) / 1024 / 1024) <<
" MB, "
384 <<
"triangle buffer: " << (totalTriangles *
sizeof(PackedTriangle) / 1024) <<
" KB)";
386 PLOGI <<
"[TIMING] Starting data population loop...";
388 for (
auto entity : meshView) {
390 if (!meshComp || !meshComp->
isVisible()) {
391 PLOGD <<
" Skipping: MeshComponent not visible";
398 PLOGD <<
" Skipping: MeshAsset not in render snapshot";
405 PLOGD <<
" Skipping: MeshAsset has no primitive data (unexpected)";
409 PLOGD <<
" Processing mesh with " << meshData->
primitives.size() <<
" primitives";
415 PLOGD <<
" Primitive has no meshlet/unpacked data, skipping";
425 localBoundsDataBuffer.push_back(localBounds);
432 float scaleX = glm::length(glm::vec3(worldMatrix[0]));
433 float scaleY = glm::length(glm::vec3(worldMatrix[1]));
434 float scaleZ = glm::length(glm::vec3(worldMatrix[2]));
435 float maxScale = std::max({scaleX, scaleY, scaleZ});
440 culling.objectIndex = primitiveId;
441 primitiveCullingBuffer.push_back(culling);
444 uint32_t meshletStart = meshletId;
445 uint32_t meshletCountForPrimitive =
static_cast<uint32_t
>(primitive.
meshletData->meshlets.size());
448 uint32_t pipelineIndex = 0;
452 std::optional<MaterialAsset*> materialAsset =
455 if (materialAsset.has_value()) {
456 pipelineType = materialAsset.value()->getType();
465 PLOGW <<
"updatePrimitiveDataInstanced: Pipeline lookup failed for pipelineType="
466 <<
static_cast<int>(pipelineType) <<
" primitiveId=" << primitiveId
473 PLOGI <<
"updatePrimitiveDataInstanced: STATIC_LIGHTMAP primitive found!"
474 <<
" primitiveId=" << primitiveId
475 <<
" pipelineIndex=" << pipelineIndex
476 <<
" meshletCount=" << meshletCountForPrimitive
485 primitiveMeshletBuffer.push_back(meshletMeta);
488 perObjectDataLocal.push_back(worldMatrix);
492 uint32_t materialId = 0;
496 switch (pipelineType) {
529 primitiveRenderDataBuffer.push_back(renderData);
533 PLOGW <<
"Primitive has no pre-unpacked data, skipping";
537 const auto& unpackedVertices = primitive.
unpackedData->vertices;
538 const auto& unpackedTriangles = primitive.
unpackedData->triangles;
541 size_t vertexBaseOffset = optimizedVertexBuffer.size();
542 size_t triangleBaseOffset = optimizedTriangleBuffer.size();
545 optimizedVertexBuffer.insert(optimizedVertexBuffer.end(),
546 unpackedVertices.begin(), unpackedVertices.end());
549 size_t triangleInsertStart = optimizedTriangleBuffer.size();
550 optimizedTriangleBuffer.resize(optimizedTriangleBuffer.size() + unpackedTriangles.size());
551 static_assert(
sizeof(PackedTriangle) ==
sizeof(
Ecs::PackedTriangle),
"PackedTriangle layout mismatch");
552 std::memcpy(&optimizedTriangleBuffer[triangleInsertStart],
553 unpackedTriangles.data(),
554 unpackedTriangles.size() *
sizeof(PackedTriangle));
557 size_t unpackedVertexOffset = 0;
558 size_t unpackedTriangleOffset = 0;
561 for (
size_t m = 0; m < meshletCountForPrimitive; m++) {
562 const auto& meshlet = primitive.
meshletData->meshlets[m];
565 uint32_t vertexDataOffset =
static_cast<uint32_t
>(vertexBaseOffset + unpackedVertexOffset);
566 uint32_t triangleDataOffset =
static_cast<uint32_t
>(triangleBaseOffset + unpackedTriangleOffset);
575 meshletDataBuffer.push_back(unified);
577 unpackedVertexOffset += meshlet.vertex_count;
578 unpackedTriangleOffset += meshlet.triangle_count;
586 meshletBoundsBuffer.push_back(bounds);
605 std::array<uint32_t, 16> meshletCountPerPipeline{};
606 for (
const auto& meshlet : meshletDataBuffer) {
607 if (meshlet.pipelineIndex < meshletCountPerPipeline.size()) {
608 meshletCountPerPipeline[meshlet.pipelineIndex]++;
611 PLOGI <<
"[DEBUG] Meshlets per pipeline:";
612 for (uint32_t i = 0; i < 9; ++i) {
613 if (meshletCountPerPipeline[i] > 0) {
614 PLOGI <<
" Pipeline " << i <<
": " << meshletCountPerPipeline[i] <<
" meshlets";
618 PLOGI <<
"[TIMING] Data population loop complete";
622 PLOGD << (
"RenderingDataManager: No primitives to upload");
627 PLOGI <<
" Total vertices: " << optimizedVertexBuffer.size() <<
", total triangles: " << optimizedTriangleBuffer.size();
632 bool anyBufferRecreated =
false;
638 const VkBufferUsageFlags storageUsage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
649 size_t localBoundsSize = localBoundsDataBuffer.size() *
sizeof(
LocalBoundsData);
650 anyBufferRecreated |=
localBoundsBuffer->ensureSize(localBoundsSize, storageUsage);
659 size_t transformSize = perObjectDataLocal.size() *
sizeof(glm::mat4);
664 size_t meshletSize = meshletDataBuffer.size() *
sizeof(
UnifiedMeshlet);
665 anyBufferRecreated |=
meshletBuffer->ensureSize(meshletSize, storageUsage);
669 size_t boundsSize = meshletBoundsBuffer.size() *
sizeof(
MeshletBounds);
679 const VkBufferUsageFlags vertexBufferUsage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
680 PLOGI <<
"[TIMING] Starting vertex buffer upload (" << (optimizedVertexBuffer.size() *
sizeof(
Vertex) / 1024 / 1024) <<
" MB)...";
681 size_t vertexSize = optimizedVertexBuffer.size() *
sizeof(
Vertex);
682 anyBufferRecreated |=
vertexBuffer->ensureSize(vertexSize, vertexBufferUsage);
683 PLOGI <<
"[TIMING] Vertex buffer ensureSize complete";
685 PLOGI <<
"[TIMING] Vertex buffer upload complete";
688 PLOGI <<
"[TIMING] Starting triangle buffer upload (" << (optimizedTriangleBuffer.size() *
sizeof(PackedTriangle) / 1024) <<
" KB)...";
689 size_t triangleSize = optimizedTriangleBuffer.size() *
sizeof(PackedTriangle);
690 anyBufferRecreated |=
triangleBuffer->ensureSize(triangleSize, storageUsage);
691 PLOGI <<
"[TIMING] Triangle buffer ensureSize complete";
693 PLOGI <<
"[TIMING] Triangle buffer upload complete";
695 PLOGI <<
" > Staged " << optimizedVertexBuffer.size() <<
" vertices, "
696 << optimizedTriangleBuffer.size() <<
" triangles for upload";
698 if (anyBufferRecreated) {
699 PLOGI <<
" > Buffer(s) were recreated - descriptor sets need updating";
704 PLOGI <<
" > Data version incremented to " <<
dataVersion_;
706 return anyBufferRecreated;
711 PLOGI <<
"[VS_DIAG] updatePrimitiveDataInstanced() called";
720 PLOGD <<
"SceneManager not available, skipping primitive data update";
743 PLOGI <<
"[INCREMENTAL] Full geometry rebuild triggered";
770 PLOGI <<
"[INCREMENTAL] Incremental geometry update - " <<
geometryCache_.size() <<
" geometries already cached";
785 if (prevInstanceCount > 0) {
798 uint32_t instanceId = 0;
805 size_t newGeometryCount = 0;
808 size_t totalInstances = 0;
809 size_t singleMeshletCount = 0;
810 size_t multiMeshletCount = 0;
816 PLOGD <<
"updatePrimitiveDataInstanced: Found " << meshView.size() <<
" mesh components via ECS";
827 for (
auto entity : meshView) {
829 if (!meshComp || !meshComp->
isVisible())
continue;
832 if (!asset)
continue;
836 if (!meshData)
continue;
841 float scaleX = glm::length(glm::vec3(worldMatrix[0]));
842 float scaleY = glm::length(glm::vec3(worldMatrix[1]));
843 float scaleZ = glm::length(glm::vec3(worldMatrix[2]));
844 float maxScale = std::max({scaleX, scaleY, scaleZ});
846 uint32_t primIdx = 0;
865 uint32_t pipelineIndex = 0;
869 std::optional<MaterialAsset*> materialAsset =
871 if (materialAsset.has_value()) {
872 pipelineType = materialAsset.value()->getType();
883 uint32_t meshletStart = meshletId;
888 bool hasLodHierarchy = primitive.
lodHierarchy.has_value() &&
891 const std::vector<Vertex>* unpackedVerticesPtr;
892 const std::vector<Ecs::PackedTriangle>* unpackedTrianglesPtr;
893 const std::vector<meshopt_Meshlet>* meshletsPtr;
895 if (hasLodHierarchy) {
897 unpackedVerticesPtr = &primitive.
lodHierarchy->unpackedData.vertices;
898 unpackedTrianglesPtr = &primitive.
lodHierarchy->unpackedData.triangles;
899 meshletsPtr = &primitive.
lodHierarchy->meshletData.meshlets;
900 VS_LOG(
LogLOD,
Debug,
"Using LOD hierarchy data: {} meshlets, {} vertices, {} triangles",
901 meshletsPtr->size(), unpackedVerticesPtr->size(), unpackedTrianglesPtr->size());
904 unpackedVerticesPtr = &primitive.
unpackedData->vertices;
905 unpackedTrianglesPtr = &primitive.
unpackedData->triangles;
909 const auto& unpackedVertices = *unpackedVerticesPtr;
910 const auto& unpackedTriangles = *unpackedTrianglesPtr;
911 const auto& meshlets = *meshletsPtr;
912 uint32_t
meshletCount =
static_cast<uint32_t
>(meshlets.size());
930 unpackedVertices.begin(), unpackedVertices.end());
934 static_assert(
sizeof(PackedTriangle) ==
sizeof(
Ecs::PackedTriangle),
"PackedTriangle layout mismatch");
936 unpackedTriangles.data(),
937 unpackedTriangles.size() *
sizeof(PackedTriangle));
940 size_t unpackedVertexOffset = 0;
941 size_t unpackedTriangleOffset = 0;
944 const auto& meshlet = meshlets[m];
951 unified.
vertexDataOffset =
static_cast<uint32_t
>(vertexBaseOffset + unpackedVertexOffset);
952 unified.
triangleDataOffset =
static_cast<uint32_t
>(triangleBaseOffset + unpackedTriangleOffset);
964 unpackedVertexOffset += meshlet.vertex_count;
965 unpackedTriangleOffset += meshlet.triangle_count;
984 singleMeshletCount++;
988 for (
const auto& tri : unpackedTriangles) {
993 mapping.
vsIndexCount =
static_cast<uint32_t
>(unpackedTriangles.size() * 3);
1001 singleGeo.
vertexOffset =
static_cast<int32_t
>(vertexBaseOffset);
1005 multiMeshletCount++;
1017 uint32_t clusterCount =
static_cast<uint32_t
>(lodData.clusters.size());
1019 uint32_t groupCount =
static_cast<uint32_t
>(lodData.groups.size());
1028 for (
const auto& cluster : lodData.clusters) {
1032 clusterCopy.
meshletIndex = meshletStart + cluster.meshletIndex;
1037 for (
const auto& group : lodData.groups) {
1045 VS_LOG(
LogLOD,
Warning,
"[DIAG] RDM Geometry {}: clusterStart={} clusterCount={} groupStart={} groupCount={} meshletStart={} totalMeshlets={}",
1046 meshGeometryId, clusterStart, clusterCount, groupStart, groupCount, meshletStart,
meshletCount);
1049 if (!lodData.clusters.empty()) {
1050 const auto& firstCluster = lodData.clusters[0];
1051 VS_LOG(
LogLOD,
Warning,
"[DIAG] RDM First cluster: error={:.4f} refinedGroupId={} meshletIndex={} (global={})",
1052 firstCluster.error, firstCluster.refinedGroupId, firstCluster.meshletIndex,
1053 meshletStart + firstCluster.meshletIndex);
1057 meshGeometryId, clusterCount, groupCount);
1072 glm::vec3 worldCenter = worldMatrix * glm::vec4(geoMapping.
localBoundsCenter, 1.0f);
1077 uint32_t materialId = 0;
1083 std::optional<MaterialAsset*> materialAsset =
1085 if (materialAsset.has_value()) {
1086 lookupPipelineType = materialAsset.value()->getType();
1087 PLOGD <<
"[DEBUG] Material asset found: path='" << matPathKey
1088 <<
"' type=" <<
static_cast<int>(lookupPipelineType);
1090 PLOGW <<
"[DEBUG] Material asset NOT FOUND for path: '" << matPathKey <<
"'";
1093 PLOGW <<
"[DEBUG] No material path or no assetManager, using default. matPathKey='" << matPathKey <<
"'";
1097 switch (lookupPipelineType) {
1101 materialId = it->second;
1102 PLOGW <<
"[DEBUG] FLAT_COLOR lookup SUCCESS: matPathKey='" << matPathKey
1103 <<
"' -> materialId=" << materialId;
1105 PLOGW <<
"[DEBUG] FLAT_COLOR lookup FAILED: matPathKey='" << matPathKey
1123 materialId = it->second;
1124 PLOGW <<
"[DEBUG] PBR lookup SUCCESS: matPathKey='" << matPathKey
1125 <<
"' -> materialId=" << materialId
1126 <<
" pipelineType=" <<
static_cast<int>(lookupPipelineType);
1128 PLOGW <<
"[DEBUG] PBR lookup FAILED: matPathKey='" << matPathKey
1137 materialId = it->second;
1139 PLOGW <<
"[DEBUG] NORMALS/DEFAULT lookup: matPathKey='" << matPathKey
1140 <<
"' pipelineType=" <<
static_cast<int>(lookupPipelineType)
1141 <<
" materialId=" << materialId;
1217 PLOGI <<
"[INCREMENTAL] Incremental update: " << newGeometryCount <<
" NEW geometries added, "
1226 PLOGD <<
"RenderingDataManager: No instances to upload";
1237 bool anyBufferRecreated =
false;
1238 const VkBufferUsageFlags storageUsage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
1253 if (fullRebuild || anyBufferRecreated) {
1259 PLOGI <<
"[INCREMENTAL] Partial geometry data upload: " << newCount <<
" new geometries";
1279 anyBufferRecreated |=
localBoundsBuffer->ensureSize(localBoundsSize, storageUsage);
1296 std::array<uint32_t, 16> meshletCountPerPipeline{};
1298 if (meshlet.pipelineIndex < meshletCountPerPipeline.size()) {
1299 meshletCountPerPipeline[meshlet.pipelineIndex]++;
1303 for (uint32_t i = 0; i < 9; ++i) {
1304 if (meshletCountPerPipeline[i] > 0) {
1305 PLOGI <<
" Pipeline " << i <<
": " << meshletCountPerPipeline[i] <<
" meshlets";
1315 anyBufferRecreated |=
meshletBuffer->ensureSize(totalSize, storageUsage);
1317 if (fullRebuild || anyBufferRecreated) {
1323 PLOGI <<
"[INCREMENTAL] Partial meshlet upload: " << newCount <<
" new meshlets";
1333 if (fullRebuild || anyBufferRecreated) {
1346 const VkBufferUsageFlags vertexBufferUsage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
1347 anyBufferRecreated |=
vertexBuffer->ensureSize(totalSize, vertexBufferUsage);
1349 if (fullRebuild || anyBufferRecreated) {
1350 PLOGI <<
"[INSTANCING] Full vertex buffer upload (" << (totalSize / 1024) <<
" KB)";
1354 size_t newSize = newCount *
sizeof(
Vertex);
1356 PLOGI <<
"[INCREMENTAL] Partial vertex upload: " << newCount <<
" new vertices (" << (newSize / 1024) <<
" KB)";
1364 anyBufferRecreated |=
triangleBuffer->ensureSize(totalSize, storageUsage);
1366 if (fullRebuild || anyBufferRecreated) {
1367 PLOGI <<
"[INSTANCING] Full triangle buffer upload (" << (totalSize / 1024) <<
" KB)";
1371 size_t newSize = newCount *
sizeof(PackedTriangle);
1373 PLOGI <<
"[INCREMENTAL] Partial triangle upload: " << newCount <<
" new triangles (" << (newSize / 1024) <<
" KB)";
1382 const VkBufferUsageFlags vsIndexUsage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT;
1383 anyBufferRecreated |=
vsIndexBuffer_->ensureSize(totalSize, vsIndexUsage);
1385 if (fullRebuild || anyBufferRecreated) {
1390 size_t newSize = newCount *
sizeof(uint32_t);
1392 PLOGI <<
"[INCREMENTAL] Partial VS index upload: " << newCount <<
" new indices";
1402 if (fullRebuild || anyBufferRecreated) {
1409 PLOGI <<
"[INCREMENTAL] Partial single-meshlet geometry upload: " << newCount <<
" new geometries";
1416 VS_LOG(
LogLOD,
Warning,
"[DIAG] RDM Upload: clusterCount_={} clusterGroupCount_={} workingClusters={} workingGroups={}",
1423 if (fullRebuild || anyBufferRecreated) {
1429 VS_LOG(
LogLOD,
Warning,
"[DIAG] No LOD cluster data to upload (workingClusterLodData_ is empty)");
1436 if (fullRebuild || anyBufferRecreated) {
1443 PLOGI <<
"[INSTANCING] Memory savings: geometry shared across " <<
instanceCount_
1444 <<
" instances instead of duplicated";
1454 if (anyBufferRecreated) {
1455 PLOGI <<
"[INSTANCING] Buffer(s) were recreated - descriptor sets need updating";
1459 PLOGI <<
"[INSTANCING] Data version incremented to " <<
dataVersion_;
1461 return anyBufferRecreated;
1470 PLOGI <<
"Geometry cache invalidated - full rebuild required";
1502 for (
auto entity : meshView) {
1504 if (!meshComp || !meshComp->
isVisible())
continue;
1507 if (!asset)
continue;
1511 if (!meshData)
continue;
1525 PLOGW <<
"Transform update collected " << primitiveId
1527 <<
". Structure may have changed - triggering full rebuild.";
1537 std::vector<entt::entity> entitiesToClear;
1539 for (
auto entity : dirtyView) {
1540 entitiesToClear.push_back(entity);
1542 for (
auto entity : entitiesToClear) {
1547 PLOGD <<
"RenderingDataManager: Full transform rebuild for " <<
primitiveCount <<
" primitives";
1553 std::vector<entt::entity> dirtyEntities;
1555 for (
auto entity : dirtyView) {
1556 dirtyEntities.push_back(entity);
1560 if (dirtyEntities.empty()) {
1561 PLOGD <<
"RenderingDataManager: No dirty transforms, skipping update";
1565 uint32_t updatedCount = 0;
1567 for (entt::entity entity : dirtyEntities) {
1582 if (!meshComp || !meshComp->
isVisible()) {
1595 for (
auto entity : dirtyEntities) {
1599 PLOGD <<
"RenderingDataManager: Updated " << updatedCount <<
" dirty transforms out of " <<
primitiveCount;
1625 PLOGI <<
"RenderingDataManager::onMeshLoaded called - queuing for next frame";
1640 PLOGI <<
"onTextureLoaded called with path: " << texturePath;
1642 if (!textureAsset) {
1643 PLOGW <<
"onTextureLoaded called with null textureAsset";
1649 entt::entity entity = textureAsset->
getEntity();
1650 if (entity == entt::null) {
1651 PLOGW <<
"onTextureLoaded: TextureAsset has null entity for path: " << texturePath;
1658 PLOGW <<
"onTextureLoaded: No ImageData component for path: " << texturePath;
1665 if (!assetManager) {
1666 PLOGW <<
"onTextureLoaded: AssetManager not available";
1673 PLOGD <<
"onTextureLoaded: Texture already registered: " << texturePath;
1681 PLOGD <<
"onTextureLoaded - entity=" <<
static_cast<uint32_t
>(entity)
1682 <<
" path=" << texturePath
1683 <<
" dims=" << imageData.
image.width <<
"x" << imageData.image.height;
1693 PLOGD <<
"onTextureLoaded: No pre-registered type for " << texturePath <<
", defaulting to BaseColor";
1702 if (registeredTexture) {
1703 PLOGI <<
"onTextureLoaded: Created Vulkan texture for " << texturePath
1705 <<
" type=" <<
static_cast<int>(textureType);
1710 PLOGW <<
"onTextureLoaded: Failed to register texture: " << texturePath;
1740 if (!sceneManager) {
1741 PLOGD <<
"SceneManager not available, skipping material collection";
1752 std::vector<Actor*> allActors = sceneManager->
getAllActors();
1754 for (
Actor* actor : allActors) {
1757 for (
auto* meshComp : actor->getComponents<
MeshComponent>()) {
1758 if (!meshComp->isVisible())
continue;
1760 MeshAsset* asset = meshComp->getMeshAsset();
1761 if (!asset)
continue;
1764 if (!meshData)
continue;
1766 for (
const auto& primitive : meshData->primitives) {
1767 if (!primitive.meshletData.has_value())
continue;
1772 if (!primitive.materialPath.empty() && assetManager) {
1773 std::optional<MaterialAsset*> materialAsset =
1776 if (materialAsset.has_value()) {
1777 pipelineType = materialAsset.value()->getType();
1792 PLOGI <<
"RenderingDataManager: Collected materials for " << primitiveId <<
" primitives";
1796 PLOGD <<
" Pipeline " <<
static_cast<int>(pipeline) <<
": " << count <<
" primitives";
1811 PLOGD <<
"markDirty() called - setting isDirty=true";
1833 std::vector<Ecs::CompletedMeshletGeneration>&& completions)
1835 if (completions.empty()) {
1839 PLOGI <<
"Processing " << completions.size() <<
" completed meshlet generations";
1844 for (
auto& completion : completions) {
1847 if (!primitiveData) {
1848 PLOGW <<
"MeshPrimitiveData not found for entity during meshlet completion processing";
1853 if (completion.primitiveResults.size() != primitiveData->
primitives.size()) {
1854 PLOGW <<
"Primitive count mismatch: " << completion.primitiveResults.size()
1855 <<
" vs " << primitiveData->
primitives.size();
1860 PLOGI <<
"processCompletedMeshletGenerations: Writing meshlet data for " << completion.meshPath.getAssetHandle()
1861 <<
" entity=" <<
static_cast<uint32_t
>(completion.meshEntity)
1862 <<
" (" << completion.primitiveResults.size() <<
" primitives)";
1863 for (
size_t i = 0; i < completion.primitiveResults.size(); ++i) {
1864 auto& result = completion.primitiveResults[i];
1868 PLOGI <<
" primitive[" << i <<
"] materialPath BEFORE write: "
1869 << (prim.materialPath.empty() ?
"(empty)" : prim.materialPath.getAssetHandle());
1872 prim.data = std::move(result.optimizedData);
1875 prim.meshletData = std::move(result.meshletData);
1878 prim.unpackedData = std::move(result.unpackedData);
1881 prim.lodHierarchy = std::move(result.lodHierarchy);
1884 prim.boundingSphere = result.boundingSphere;
1887 PLOGI <<
" primitive[" << i <<
"] materialPath AFTER write: "
1888 << (prim.materialPath.empty() ?
"(empty)" : prim.materialPath.getAssetHandle());
1894 PLOGD <<
"Wrote meshlet data to registry for: " << completion.meshPath.getFilePath();
1899 if (meshAssetOpt.has_value()) {
1916 for (
auto entity : meshView) {
1918 if (!meshComp || !meshComp->
isVisible())
continue;
1921 if (!asset)
continue;
1925 if (!meshData)
continue;
1964 std::vector<VkDescriptorImageInfo> descriptorInfos;
1968 if (texture && texture->getVkImageView() != VK_NULL_HANDLE) {
1969 VkDescriptorImageInfo imageInfo{
1970 .sampler = texture->getVkImageSampler(),
1971 .imageView = texture->getVkImageView(),
1972 .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL
1974 descriptorInfos.push_back(imageInfo);
1978 return descriptorInfos;
1990 return it->second.value();
1994 throw std::runtime_error(
"Material buffer for pipeline " + std::to_string(
static_cast<int>(pipelineName)) +
" not found");
2000 const std::vector<PipelineNames> pipelineTypes = {
2012 const VkBufferUsageFlags storageUsage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
2013 const VkMemoryPropertyFlags memProps = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
2014 constexpr size_t MATERIAL_BUFFER_SIZE = 256;
2021 std::string bufferName =
"Material Buffer Pipeline " + std::to_string(
static_cast<int>(pipelineName));
2034 if (!assetManager) {
2035 PLOGD <<
"AssetManager not available, skipping material buffer upload";
2056 fallbackFlatColor.
color = glm::vec3(1.0f, 0.0f, 1.0f);
2084 fallbackNormals.
debug = 0;
2087 PLOGD <<
"Added fallback materials at index 0 for all material buffers";
2095 size_t totalMaterials = 0;
2096 size_t loadedMaterials = 0;
2110 PLOGD <<
"uploadMaterialBuffers: processing material " << matPathKey
2111 <<
" pipelineType=" <<
static_cast<int>(pipelineType);
2114 switch (pipelineType) {
2125 gpuMat.
color = matData.getColor();
2128 PLOGW <<
"[DEBUG] Added FLAT_COLOR material: path='" << matPathKey
2129 <<
"' -> materialId=" << materialId
2130 <<
" color=(" << gpuMat.
color.r <<
", " << gpuMat.
color.g <<
", " << gpuMat.
color.b <<
")";
2183 if (baseColorHandle.
isValid()) {
2186 if (!baseColorPath.empty() && registeredPath != baseColorPath) {
2187 PLOGE <<
" [" << matPathKey <<
"] HANDLE MISMATCH! baseColor handle=" << baseColorHandle.
getId()
2188 <<
" points to '" << registeredPath <<
"' but material expects '" << baseColorPath <<
"'";
2192 if (texIndex != 0xFFFFFFFF) {
2194 PLOGI <<
" [" << matPathKey <<
"] baseColor: handle=" << baseColorHandle.
getId()
2195 <<
" -> descriptorIdx=" << texIndex <<
" path=" << baseColorPath;
2197 PLOGW <<
" [" << matPathKey <<
"] baseColor: handle=" << baseColorHandle.
getId()
2198 <<
" valid but no descriptor yet, path=" << baseColorPath;
2203 if (!baseColorPath.empty()) {
2205 if (texIndex != 0xFFFFFFFF) {
2207 PLOGI <<
" Resolved base color texture via path: " << baseColorPath <<
" -> " << texIndex;
2216 if (texIndex != 0xFFFFFFFF) {
2218 PLOGD <<
" Resolved normal texture via handle -> " << texIndex;
2222 if (!normalPath.empty()) {
2224 if (texIndex != 0xFFFFFFFF) {
2226 PLOGD <<
" Resolved normal texture via path: " << normalPath <<
" -> " << texIndex;
2233 if (metallicRoughnessHandle.
isValid()) {
2235 if (texIndex != 0xFFFFFFFF) {
2237 PLOGD <<
" Resolved metallic-roughness texture via handle -> " << texIndex;
2241 if (!metallicRoughnessPath.empty()) {
2243 if (texIndex != 0xFFFFFFFF) {
2245 PLOGD <<
" Resolved metallic-roughness texture via path: " << metallicRoughnessPath <<
" -> " << texIndex;
2252 if (emissiveHandle.
isValid()) {
2254 if (texIndex != 0xFFFFFFFF) {
2256 PLOGD <<
" Resolved emissive texture via handle -> " << texIndex;
2260 if (!emissivePath.empty()) {
2262 if (texIndex != 0xFFFFFFFF) {
2264 PLOGD <<
" Resolved emissive texture via path: " << emissivePath <<
" -> " << texIndex;
2271 if (lightmapHandle.
isValid()) {
2273 if (texIndex != 0xFFFFFFFF) {
2276 PLOGI <<
" Resolved lightmap texture via handle -> " << texIndex;
2278 PLOGW <<
" Lightmap texture not loaded yet (handle valid but no descriptor)";
2282 if (!lightmapPath.empty()) {
2284 if (texIndex != 0xFFFFFFFF) {
2287 PLOGI <<
" Resolved lightmap texture via path: " << lightmapPath <<
" -> " << texIndex;
2299 gpuMat.
sh_scale = matData.sh_scale1();
2308 gpuMat.
hasLightmap = (matData.lightmap_texture_index() != 0xFFFFFFFF) ? 1 : 0;
2312 gpuMat.
sh_scale = matData.sh_scale1();
2319 gpuMat.
sh_scale = matData.sh_scale1();
2326 gpuMat.
sh_scale = matData.sh_scale1();
2333 gpuMat.
sh_scale = matData.sh_scale1();
2346 gpuMat.
sh_scale = matData.sh_scale1();
2356 PLOGI <<
"STATIC_LIGHTMAP material: " << matPathKey
2357 <<
" bufferIndex=" << materialId
2378 gpuMat.
debug = matData.getDebug() ? 1 : 0;
2390 defaultMat.
debug = 0;
2394 PLOGW <<
"[DEBUG] uploadMaterialBuffers summary: iterated " << totalMaterials <<
" materials"
2395 <<
" (" << loadedMaterials <<
" loaded)"
2402 const VkBufferUsageFlags storageUsage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
2403 const VkMemoryPropertyFlags memProps = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
2406 auto uploadMaterialBuffer = [&](
PipelineNames pipeline,
const void* data,
size_t dataSize,
const char* name) {
2416 buffer.
create(requiredSize, storageUsage, memProps);
2418 PLOGI <<
"Resized material buffer " << name <<
" to " << requiredSize <<
" bytes";
2423 void* mappedData = buffer.
map();
2425 std::memcpy(mappedData, data, dataSize);
2436 "Material_DiffuseFlatColor");
2444 "Material_DiffuseShader");
2453 PLOGI <<
"[DEBUG] PBR material data size: " << pbrDataSize <<
" bytes";
2461 PLOGW <<
"[DEBUG] workingPbrMaterials_ is EMPTY - no PBR materials to upload!";
2469 "Material_Normals");
2473 PLOGI <<
"Uploaded material buffers: "
2505 const VkBufferUsageFlags storageUsage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
2506 const VkMemoryPropertyFlags memProps = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
2513 defaultProbe.
coefficients[0] = glm::vec4(7.197292f, 6.285527f, 4.887862f, 0.0f);
2515 defaultProbe.
coefficients[1] = glm::vec4(-1.463182f, -0.987888f, -0.403854f, 0.0f);
2517 defaultProbe.
coefficients[2] = glm::vec4(2.734015f, 2.181526f, 1.884693f, 0.0f);
2519 defaultProbe.
coefficients[3] = glm::vec4(1.463182f, 0.987888f, 0.403854f, 0.0f);
2521 defaultProbe.
coefficients[4] = glm::vec4(0.051453f, -0.015701f, -0.085487f, 0.0f);
2523 defaultProbe.
coefficients[5] = glm::vec4(-0.278889f, -0.220688f, -0.151132f, 0.0f);
2525 defaultProbe.
coefficients[6] = glm::vec4(-1.445439f, -0.174282f, 1.112568f, 0.0f);
2527 defaultProbe.
coefficients[7] = glm::vec4(0.278889f, 0.220688f, 0.151132f, 0.0f);
2529 defaultProbe.
coefficients[8] = glm::vec4(0.051453f, -0.015701f, -0.085487f, 0.0f);
2532 defaultProbe.
scale = 0.15f;
2548 std::memcpy(mappedData,
shProbeData_.data(), bufferSize);
2552 PLOGI <<
"Initialized SH probe buffer with " <<
shProbeData_.size() <<
" probe(s)";
2558 throw std::runtime_error(
"SH probe buffer not initialized");
2566 PLOGD <<
"Default textures already queued for loading";
2571 if (!assetManager &&
engine) {
2572 assetManager =
engine->getAssetManager().get();
2575 if (!assetManager) {
2576 PLOGE <<
"Cannot initialize default textures: AssetManager not available";
2580 PLOGI <<
"Queueing default PBR textures for loading via texture pipeline...";
2585 if (handleRegistry) {
2601 PLOGI <<
"Default textures queued: "
2610 if (textureIndex != 0xFFFFFFFF) {
2611 return textureIndex;
2616 if (!assetManager &&
engine) {
2617 assetManager =
engine->getAssetManager().get();
2620 if (!assetManager) {
2625 switch (defaultType) {
::EngineCore::LogCategory LogLOD("LogLOD", ::EngineCore::LogVerbosity::Info, ::EngineCore::LogVerbosity::Verbose)
#define VS_LOG(Category, Verbosity, Format,...)
Log a message with category and verbosity.
#define primitive_rendering_id
#define meshlet_rendering_id
constexpr EngineCore::PipelineNames DEFAULT_MATERIAL_PIPELINE
#define TRACY_ZONE_SCOPED_NAMED(name)
entt::entity getEntity() const
Getter for data.
CpuLoadingState getLoadingState()
Gets the loading state of this asset.
std::optional< AssetClass * > getAsset(const Key &path)
Try's to get an asset. If the asset does not exist (can be checked with exists) it will return a std:...
void forEachAsset(Func &&func) const
Iterates over all assets and calls the provided callback for each.
static entt::registry & get()
Gets the registry for all components.
An Actor is similar to an EngineCore::Entity. An actor is an Entity with a transform.
The application context is the core class which stores the basic openxr and vulkan objects.
MeshAssetManager * getMeshAssetManager() const
Gets the mesh asset manager for mesh asset lookups.
uint32_t getTextureDescriptorIndex(const std::filesystem::path &path)
Gets the descriptor index of a loaded texture by path.
MaterialAssetManager * getMaterialAssetManager() const
Gets the material asset manager for material lookups.
void loadEcsTexture(const std::filesystem::path &path)
Loads a texture (.png / .jpg / .exr)
TextureHandleRegistry * getTextureHandleRegistry()
Gets the texture handle registry for O(1) descriptor index lookups.
Texture * registerTexture(const std::filesystem::path &path, Texture texture)
Registers a texture with the texture manager which prevents the same texture from being loaded twice.
Material data for an Object which displays a flat color.
Dynamic textures material with PBR and lightmap support.
L1 Spherical Harmoics shader.
the material asset is another wrapper for asset data which is stored in entt. It has the EngineCore::...
const std::filesystem::path & getBaseColorTexturePath() const
Gets the base color texture path for this material.
MetallicRoughnessTextureHandle getMetallicRoughnessTextureHandle() const
Get the type-safe metallic-roughness texture handle.
EmissiveTextureHandle getEmissiveTextureHandle() const
Get the type-safe emissive texture handle.
const std::filesystem::path & getMetallicRoughnessTexturePath() const
LightmapTextureHandle getLightmapTextureHandle() const
Get the type-safe lightmap texture handle.
NormalTextureHandle getNormalTextureHandle() const
Get the type-safe normal texture handle.
const std::filesystem::path & getLightmapTexturePath() const
const std::filesystem::path & getEmissiveTexturePath() const
T & getData()
Method to get the material data from the ecs with the corresponding type.
const std::filesystem::path & getNormalTexturePath() const
AlbedoTextureHandle getBaseColorTextureHandle() const
Get the type-safe base color texture handle.
PipelineNames getType() const
Gets the material type for pipeline lookup.
The mesh asset stores geometry data and.
Ecs::MeshPrimitiveData * getMeshPrimitiveData()
Gets the mesh data for this asset.
A component which can be attached as many times to an actor as one wants. It makes it possible to ren...
MeshAsset * getMeshAsset() const
Gets the asset used by this mesh rendering component.
bool isVisible() const
If this mesh component should be considered for rendering.
glm::mat4 getWorldTransform() const
Getter for the world transform of the mesh component.
Moveable diffuse shader with PBR and lightmap support.
Material data for an object which displays its normals.
bool ensureOutputBufferSizes(uint32_t primitiveCount)
Ensures output buffers are sized to handle the given primitive count. Called by RenderingDataManager ...
bool getPipelineIndex(GraphicsPipeline *pipeline, uint32_t &pipelineIndex) const
Gets the index of a pipeline.
AssetManager * injectedAssetManager
bool defaultTexturesLoading_
True if loadEcsTexture has been called.
std::unordered_map< uint32_t, PipelineNames > primitiveToPipeline
Maps primitive IDs to their pipeline.
std::vector< VkDescriptorImageInfo > generateTextureDescriptorInfos() const
Generate descriptor infos for all GPU-uploaded textures.
size_t committedSingleMeshletGeoCount_
Single-meshlet geometries already on GPU.
std::vector< Texture * > getTexturesToUpload() const
Get the list of textures waiting for GPU upload.
ApplicationContext * context
std::optional< VulkanStagedBuffer > vertexBuffer
Optimized vertex data (Vertex structs)
uint32_t getValidTextureIndex(uint32_t textureIndex, DefaultTextureType defaultType)
void processCompletedMeshletGenerations(std::vector< Ecs::CompletedMeshletGeneration > &&completions)
Process completed meshlet generations and write them to the registry. This should be called at a safe...
void processPendingMeshLoads()
Process pending mesh loads at a safe point in the frame. This should be called BEFORE updateIfDirty()...
std::vector< MeshAsset * > pendingMeshLoads_
uint32_t instanceCount_
Number of instances in buffers.
std::unordered_map< PipelineNames, std::optional< VulkanBuffer > > materialBuffersByPipeline
std::optional< VulkanStagedBuffer > localBoundsBuffer
LocalBoundsData per primitive (static, uploaded once)
bool updatePrimitiveDataInstanced()
Instanced version of updatePrimitiveData using geometry deduplication. Separates geometry upload (onc...
size_t committedVertexCount_
Vertices already on GPU.
const VulkanBuffer & getSHProbeBuffer() const
Get the SH probe buffer for spherical harmonic lighting.
bool hasTexturesToUpload() const
Check if there are textures waiting to be uploaded to GPU.
uint32_t nextMeshletId_
Next meshlet ID to assign.
bool updatePrimitiveData()
Regenerates all buffers and the rendering ids of all objects, primitives and meshlets.
std::optional< VulkanStagedBuffer > instanceDataBuffer
InstanceData per visible instance.
uint32_t getTextureCount() const
Get the count of textures currently on the GPU.
std::vector< Vertex > workingVertexBuffer_
std::vector< InstanceData > workingInstanceData_
void invalidateGeometryCache()
Clears the geometry cache and marks for full rebuild. Called when meshes are unloaded.
std::unordered_map< PipelineNames, uint32_t > pipelinePrimitiveCounts
Count of primitives per pipeline.
bool instancingEnabled_
Feature flag for instanced rendering.
void clearTexturesToUpload()
Clear the texture upload queue after upload is complete.
std::unordered_map< std::string, uint32_t > materialPathToDiffuseIndex_
std::vector< VulkanStagedBufferSyncObjects > pendingTransformSyncObjects_
std::unordered_map< GeometryCacheKey, MeshGeometryMapping, GeometryCacheKeyHash > geometryCache_
std::unordered_set< MeshAsset * > renderCycleMeshSnapshot_
bool isDirty
a flag which defers the update of the buffer updates to the end of the frame.
void processPendingDeletions()
Process deferred buffer deletions Call this after all frames have completed and descriptor sets are u...
std::vector< Texture * > gpuTextures
Textures currently on GPU.
static constexpr const char * DEFAULT_NORMAL_TEXTURE_PATH
void initializeDefaultTextures()
Queue default textures for loading via the texture pipeline.
void snapshotRenderableMeshes()
Take a snapshot of currently render-ready meshes. Called at the start of update cycle to ensure consi...
void setRenderer(Renderer *renderer)
Set the renderer for pipeline index lookups. Required when Engine is null.
SceneManager * injectedSceneManager
void markDirty()
Flags the Rendering Data Manager as dirty so that all buffers managed by this manager are in need of ...
bool updateTransforms()
Updates only transform-related buffers (world matrices, bounding spheres)
std::optional< VulkanStagedBuffer > clusterGroupDataBuffer_
ClusterGroupData per LOD group.
std::optional< VulkanStagedBuffer > triangleBuffer
Triangle indices for meshlets.
void setSceneManager(SceneManager *sceneManager)
Set the scene manager for retrieving actors. Required when Engine is null.
std::vector< SingleMeshletGeometryData > workingSingleMeshletGeoData_
std::unordered_map< uint32_t, MeshComponent * > primitiveIdToComponent_
uint32_t clusterGroupCount_
Total cluster groups.
std::vector< Ecs::PackedTriangle > workingTriangleBuffer_
std::vector< Texture * > texturesToUpload
Textures waiting for GPU upload.
uint32_t clusterCount_
Total clusters across all LOD levels.
std::vector< ClusterLodData > workingClusterLodData_
uint32_t multiMeshletGeometryCount_
Geometries with meshletCount > 1 (mesh shader path)
void onMeshLoaded(MeshAsset *asset)
Event hook which executes when an Mesh is loaded.
std::optional< VulkanStagedBuffer > primitiveRenderData
MeshPrimitiveRenderData per primitive (texture/material IDs)
size_t committedGeometryCount_
Geometries already on GPU.
std::vector< PrimitiveMeshletData > workingPrimitiveMeshletBuffer_
bool updateIfDirty()
Triggers an update of all GPU buffers if dirty.
std::vector< GpuDiffuseFlatColorMaterial > workingFlatColorMaterials_
DefaultTextureType
Get a valid texture index, substituting defaults for missing textures.
PipelineNames getPipelineForPrimitive(uint32_t primitiveId) const
Gets the pipeline ID (PipelineNames enum) for a given primitive.
Renderer * injectedRenderer
void uploadMaterialBuffers()
Upload material data from MaterialAssets to GPU buffers.
std::optional< VulkanStagedBuffer > perObjectDataBuffer
mat4 world transform per primitive (matches shader PerObjectData)
std::vector< InstanceCullingData > workingInstanceCullingData_
void onMaterialUnloaded(MaterialAsset *materialAsset)
Event hook for when a material has been unloaded.
uint32_t uniqueGeometryCount_
Number of unique geometries in buffers.
std::unordered_map< std::string, uint32_t > materialPathToFlatColorIndex_
size_t committedVsIndexCount_
VS indices already on GPU.
std::vector< uint32_t > workingVsIndexBuffer_
RenderingDataManager(const Engine *engine, ApplicationContext *context)
std::vector< MeshGeometryData > workingGeometryDataBuffer_
std::vector< glm::mat4 > cachedTransforms_
bool isMeshInSnapshot(MeshAsset *asset) const
Check if a mesh asset is in the current render cycle snapshot.
std::vector< VulkanStagedBufferSyncObjects > pendingSyncObjects_
std::unordered_map< std::string, uint32_t > materialPathToNormalsIndex_
std::vector< glm::mat4 > workingPerObjectData_
std::optional< VulkanStagedBuffer > primitiveCullingData
ObjectCullingData per primitive (unused after GPU transform optimization)
~RenderingDataManager()
Destructor - cleans up all GPU buffers.
void collectMaterialsFromScene()
Collects all materials from loaded meshes and creates per-pipeline data This builds material-to-pipel...
bool transformCacheValid_
void queueTextureForUpload(Texture *texture)
Queue a texture for GPU upload Called by AssetManager when a new texture is created and needs uploadi...
std::optional< VulkanStagedBuffer > meshletBuffer
UnifiedMeshlet data.
void onRenderableSpawned(MeshComponent *component)
Event hook which is executed when a renderable component gets added to an actor and can thus be rende...
std::vector< ObjectCullingData > workingPrimitiveCullingBuffer_
std::vector< MeshletBounds > workingMeshletBoundsBuffer_
void onRenderableDestroyed(MeshComponent *component)
Event hook which is executed when a renderable component gets removed from an actor and thus has to b...
void initializeSHProbeBuffer()
Initialize default SH probe data Sets up a single default probe with hardcoded coefficients.
std::optional< VulkanStagedBuffer > primitiveMeshletData
PrimitiveMeshletData per primitive.
std::optional< VulkanStagedBuffer > meshGeometryDataBuffer
MeshGeometryData per unique geometry.
std::optional< VulkanStagedBuffer > instanceCullingDataBuffer
InstanceCullingData per instance.
void onMaterialLoaded(MaterialAsset *materialAsset)
Event hook for when a material has been loaded.
std::unordered_map< entt::entity, std::vector< uint32_t > > entityToPrimitiveIds_
std::vector< GpuDiffuseShaderMaterial > workingDiffuseMaterials_
std::unordered_map< std::string, uint32_t > materialPathToPbrIndex_
bool geometryDirty_
Unique meshes changed (requires geometry buffer update)
void onTextureUnloaded(TextureAsset *textureAsset)
Event hook for when a texture has been unloaded.
std::vector< LocalBoundsData > workingLocalBoundsDataBuffer_
std::vector< GpuPbrMaterial > workingPbrMaterials_
std::optional< VulkanStagedBuffer > singleMeshletGeometryBuffer_
SingleMeshletGeometryData per single-meshlet geometry.
std::optional< VulkanStagedBuffer > clusterLodDataBuffer_
ClusterLodData per cluster.
const VulkanBuffer & getMaterialBufferForPipeline(PipelineNames pipelineName) const
Get the material buffer for a specific pipeline type.
static constexpr const char * DEFAULT_BLACK_TEXTURE_PATH
void onMeshUnloaded(MeshAsset *asset)
Event hook which executes when a Mesh is unloaded.
std::optional< VulkanStagedBuffer > vsIndexBuffer_
Contiguous uint32_t indices for vertex shader.
std::vector< ClusterGroupData > workingClusterGroupData_
std::vector< MeshPrimitiveRenderData > workingPrimitiveRenderData_
size_t committedTriangleCount_
Triangles already on GPU.
bool geometryNeedsFullRebuild_
True on startup or after mesh unload.
std::optional< VulkanStagedBuffer > meshletBoundsData
MeshletBounds for meshlet culling.
std::vector< GpuNormalsMaterial > workingNormalsMaterials_
void initializeMaterialBuffers()
Initialize material buffers for all pipeline types.
void setAssetManager(AssetManager *assetManager)
Set the asset manager for material lookups. Required when Engine is null.
std::vector< UnifiedMeshlet > workingMeshletDataBuffer_
std::vector< SHProbeData > shProbeData_
CPU-side SH probe data.
void onTextureLoaded(TextureAsset *textureAsset, const std::filesystem::path &texturePath)
Event hook for when a texture has been loaded by the asset pipeline. Creates a Vulkan Texture from th...
uint32_t nextGeometryId_
Next geometry ID to assign.
static constexpr const char * DEFAULT_WHITE_TEXTURE_PATH
std::optional< VulkanBuffer > shProbeBuffer_
SHProbeData buffer at binding 18.
uint32_t singleMeshletGeometryCount_
Geometries with meshletCount == 1 (vertex shader path)
size_t committedMeshletCount_
Meshlets already on GPU.
Manages game objects within a scene, handling registration, ID allocation, and GPU buffer synchroniza...
std::vector< Actor * > getAllActors()
Gets a list of pointers to all actors in the scene.
Static lightmap material with PBR support.
Wrapper for texture data.
Central registry for texture handles, providing O(1) descriptor index lookup.
TypedTextureHandle< Type > getOrCreateHandle(const std::filesystem::path &path)
Get or create a typed handle for a texture path.
std::filesystem::path getPathForHandle(TextureHandle handle) const
Get the path associated with a handle (for debugging/validation)
uint32_t resolveDescriptorIndex(TextureHandle handle) const
Resolve a handle to its descriptor index (O(1) operation)
void onTextureLoaded(const std::filesystem::path &path, Texture *texture, uint32_t descriptorIndex)
Called when a texture finishes loading.
TextureType getTextureTypeForPath(const std::filesystem::path &path) const
Get the texture type for a path (if registered)
uint32_t getDescriptorIndex() const
uint32_t getId() const
Get the registry ID for this handle.
bool isValid() const
Check if this handle points to a valid registry entry.
RAII wrapper for Vulkan buffer and device memory.
void setDebugName(const std::string &name)
Sets the name shown in vulkan validation layer.
VkDeviceSize getBufferSize() const
void * map()
Maps the buffer to be writable by the cpu. The mapped memory range is also stored in the object.
void unmap()
Unmaps the memory if it is mapped.
void destroy()
Destroys all vulkan resources of the buffer.
void create(size_t size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties)
Creates a buffer with a certain size, usage flags and memory properties.
@ LOADED
All components of the asset have been loaded, and it is ready to be used.
Log category system implementation.
TypedTextureHandle< TextureType::MetallicRoughness > MetallicRoughnessTextureHandle
TypedTextureHandle< TextureType::Lightmap > LightmapTextureHandle
constexpr size_t MIN_BUFFER_SIZE
TypedTextureHandle< TextureType::Emissive > EmissiveTextureHandle
TypedTextureHandle< TextureType::Normal > NormalTextureHandle
@ Warning
Potential issue.
@ Info
Important state changes.
@ Debug
Development debugging.
TypedTextureHandle< TextureType::BaseColor > AlbedoTextureHandle
TextureType
Represents the semantic type of a texture, determining its Vulkan format.
@ BaseColor
Color data with gamma (SRGB format for 8-bit)
@ Unknown
Fallback type, uses SRGB as default.
@ Normal
Linear data for normal maps (UNORM format for 8-bit)
bool empty() const
Checks whether this path doesn't point to anything. So it is empty.
std::string getAssetHandle() const
Reference to a MeshComponent for ECS-based iteration. Allows efficient querying of all mesh component...
std::vector< Ecs::MeshPrimitive > primitives
Ecs::BoundingSphere boundingSphere
std::optional< UnpackedMeshletData > unpackedData
Pre-unpacked GPU-ready data.
std::optional< MeshletData > meshletData
Asset::Path materialPath
Reference to MaterialAsset for pipeline lookup.
std::optional< LodHierarchyResult > lodHierarchy
LOD hierarchy (if generated)
Ecs::BoundingSphere boundingSphere
GPU-ready packed triangle data (matches shader layout). Stores 3 x 8-bit local indices packed into a ...
Per-group LOD data representing a simplified version of child clusters. Groups are created by merging...
Per-cluster LOD data for GPU-driven LOD selection. Each cluster maps to exactly one meshlet and conta...
uint32_t meshletIndex
Index into UnifiedMeshlet buffer.
GPU-side layout for flat color material Matches GLSL struct in triangle_flat.frag.
GPU-side layout for diffuse shader material Used for basic textured materials.
uint32_t baseColorTextureIndex
glm::vec4 baseColorFactor
GPU-side layout for normals debug material.
GPU-side layout for PBR material with spherical harmonics Used by MovableDiffuseShader,...
uint32_t baseColorTextureIndex
uint32_t lightmapTextureIndex
glm::vec4 baseColorFactor
uint32_t normalTextureIndex
uint32_t emissiveTextureIndex
uint32_t roughnessMetallicTextureIndex
Instance culling data - replaces ObjectCullingData for instancing. Contains world-space bounding sphe...
uint32_t meshGeometryId
Index into MeshGeometryData buffer.
uint32_t instanceId
Index into InstanceData buffer.
glm::vec4 worldPositionAndRadius
xyz = world position, w = radius
Per-instance data - one per visible MeshComponent. References shared geometry via meshGeometryId.
uint32_t materialId
Material instance ID (for material variations)
uint32_t colorTextureId
Texture array index.
glm::mat4 worldMatrix
64 bytes - world transform
uint32_t meshGeometryId
Index into MeshGeometryData buffer.
Local-space bounding sphere data - uploaded once, never changes.
glm::vec4 localCenterAndRadius
xyz = local center, w = local radius
Shared geometry metadata - one per unique MeshAsset primitive. Allows multiple instances to reference...
uint32_t clusterCount
Total clusters (all LOD levels)
uint32_t meshletStartIndex
First meshlet in shared meshlet buffer.
uint32_t meshletCount
Number of meshlets for this geometry.
uint32_t pipelineIndex
Material pipeline index.
uint32_t groupCount
Total groups in LOD hierarchy.
uint32_t clusterStartIndex
First ClusterLodData (0xFFFFFFFF if no LOD)
uint32_t groupStartIndex
First ClusterGroupData.
uint32_t singleMeshletGeoIndex
Index into SingleMeshletGeometryData (0xFFFFFFFF if multi-meshlet)
Mapping from MeshAsset primitive to shared geometry buffer location. Used by RenderingDataManager to ...
bool isSingleMeshlet
True if meshletCount == 1 (use vertex shader path)
uint32_t vsIndexOffset
Offset into VS index buffer (contiguous uint32_t)
float localBoundsRadius
Cached local bounding sphere radius.
uint32_t meshGeometryId
Index into MeshGeometryData buffer.
glm::vec3 localBoundsCenter
Cached local bounding sphere center.
uint32_t pipelineIndex
Cached pipeline index from material.
uint32_t meshletStartIndex
First meshlet in shared buffer.
uint32_t triangleBaseOffset
Offset into triangle buffer.
uint32_t meshletCount
Number of meshlets.
uint32_t vertexBaseOffset
Offset into vertex buffer.
uint32_t vsIndexCount
Number of indices (triangleCount * 3)
glm::vec4 worldPositionAndRadius
glm::vec4 worldPositionAndRadius
The center point of the bounding sphere (w component stores radius)
uint32_t objectIndex
Object index linking back to the Object Data.
Data for the primitive culling shader to assemble and pass on to the unpacking shader.
uint32_t meshletStartIndex
Key for geometry cache - uniquely identifies a primitive within a mesh. Uses MeshAsset pointer and pr...
SH Probe data structure for dynamic lighting Contains L0-L2 RGB spherical harmonic coefficients.
glm::vec4 coefficients[9]
GPU data for single-meshlet geometry using vertex shader path. One entry per unique single-meshlet ge...
uint32_t firstIndex
Offset into index buffer.
uint32_t pipelineIndex
Material pipeline index.
int32_t vertexOffset
Added to each index value.
uint32_t indexCount
Number of indices to draw.
Information about the wereabouts of a meshlet in memory on the gpu.
uint32_t vertexDataOffset
uint32_t triangleDataOffset
The fundamental building block of all meshes in this engine.