diff --git a/src/common/rendering/hwrenderer/data/hw_levelmesh.h b/src/common/rendering/hwrenderer/data/hw_levelmesh.h index 2204dea95d..0a01f4e29f 100644 --- a/src/common/rendering/hwrenderer/data/hw_levelmesh.h +++ b/src/common/rendering/hwrenderer/data/hw_levelmesh.h @@ -78,6 +78,11 @@ struct LevelMeshSurface // Smoothing group surface is to be rendered with int smoothingGroupIndex = -1; + // + // Utility/Info + // + inline uint32_t Area() const { return texWidth * texHeight; } + // // VkLightmap extra stuff that I dislike: // @@ -171,6 +176,15 @@ struct IdenticalPortalComparator } }; +struct LevelMeshSurfaceStats +{ + struct Stats + { + uint32_t total = 0, dirty = 0, sky = 0; + }; + + Stats surfaces, pixels; +}; class LevelMesh { @@ -200,10 +214,41 @@ public: TArray SmoothingGroups; TArray Portals; + // Lightmap atlas int LMTextureCount = 0; int LMTextureSize = 0; TArray LMTextureData; // TODO better place for this? + inline uint32_t AtlasPixelCount() const { return uint32_t(LMTextureCount * LMTextureSize * LMTextureSize); } + inline LevelMeshSurfaceStats GatherSurfacePixelStats() //const + { + LevelMeshSurfaceStats stats; + + int count = GetSurfaceCount(); + for (int i = 0; i < count; ++i) + { + const auto* surface = GetSurface(i); + auto area = surface->Area(); + + stats.pixels.total += area; + + if (surface->needsUpdate) + { + stats.surfaces.dirty++; + stats.pixels.dirty += area; + } + if (surface->bSky) + { + stats.surfaces.sky++; + stats.pixels.sky += area; + } + } + + stats.surfaces.total = count; + return stats; + } + + // Map defaults FVector3 SunDirection = FVector3(0.0f, 0.0f, -1.0f); FVector3 SunColor = FVector3(0.0f, 0.0f, 0.0f); uint16_t LightmapSampleDistance = 16; diff --git a/src/common/rendering/vulkan/accelstructs/vk_lightmap.cpp b/src/common/rendering/vulkan/accelstructs/vk_lightmap.cpp index cc625c4f99..7d3893e3b5 100644 --- a/src/common/rendering/vulkan/accelstructs/vk_lightmap.cpp +++ b/src/common/rendering/vulkan/accelstructs/vk_lightmap.cpp @@ -78,7 +78,7 @@ void VkLightmap::Raytrace(const TArray& surfaces) for (auto& surface : surfaces) { surface->needsUpdate = false; // it may have been set to false already, but lightmapper ultimately decides so - pixels += surface->texHeight * surface->texWidth; + pixels += surface->Area(); } UploadUniforms(); diff --git a/src/rendering/hwrenderer/doom_levelmesh.cpp b/src/rendering/hwrenderer/doom_levelmesh.cpp index 99629c5541..aca3a1a539 100644 --- a/src/rendering/hwrenderer/doom_levelmesh.cpp +++ b/src/rendering/hwrenderer/doom_levelmesh.cpp @@ -10,122 +10,86 @@ #include "common/rendering/vulkan/accelstructs/vk_lightmap.h" #include -void PrintMissingLevelMesh() -{ - Printf("No level mesh. Perhaps your level has no lightmap loaded?\n"); -} +void PrintMissingLevelMesh() { Printf("No level mesh. Perhaps your level has no lightmap loaded?\n"); } +void PrintNoLightmap() { Printf("Lightmap is not enabled in this level.\n"); } -void PrintNoLightmaps() -{ - Printf("Lightmap is not enabled in this level.\n"); -} +#define GET_LEVELMESH() auto* levelMesh = level.levelMesh +#define REQUIRE_LEVELMESH(returnValue) GET_LEVELMESH(); do { if(!levelMesh) { PrintMissingLevelMesh(); return returnValue ; } } while(false) +#define REQUIRE_LIGHTMAP(returnValue) REQUIRE_LEVELMESH(returnValue); do { if(!level.lightmaps) { PrintNoLightmap(); return returnValue ; } } while(false) ADD_STAT(lightmap) { FString out; - if (level.levelMesh && level.lightmaps) + GET_LEVELMESH(); + + if (!levelMesh || !level.lightmaps) { - auto* mesh = level.levelMesh; - - uint32_t usedPixels = 0; - uint32_t updatesNeeded = 0; - uint32_t skies = 0; - uint32_t pixelsTbd = 0; - for (auto& surface : mesh->Surfaces) - { - uint32_t area = surface.texWidth * surface.texHeight; - - if (surface.needsUpdate) - { - pixelsTbd += area; - updatesNeeded++; - } - - if (surface.bSky) - { - skies++; - } - - usedPixels += area; - } - - uint32_t atlasPixelCount = level.levelMesh->LMTextureSize * level.levelMesh->LMTextureSize * level.levelMesh->LMTextureCount; - - out.Format("Surfaces: %u (sky: %u, awaiting updates: %u)\nSurface pixel area to update: %u\nSurface pixel area: %u\nAtlas pixel area: %u\nAtlas efficiency: %.4f%%", - mesh->Surfaces.Size(), skies, updatesNeeded, - pixelsTbd, - usedPixels, - atlasPixelCount, - float(usedPixels) / float(atlasPixelCount) * 100.0f ); - } - else - { - out.Format("No level mesh."); + out.Format("No lightmap"); + return out; } + + uint32_t atlasPixelCount = levelMesh->AtlasPixelCount(); + auto stats = levelMesh->GatherSurfacePixelStats(); + + out.Format("Surfaces: %u (sky: %u, awaiting updates: %u)\nSurface pixel area to update: %u\nSurface pixel area: %u\nAtlas pixel area: %u\nAtlas efficiency: %.4f%%", + stats.surfaces.total, stats.surfaces.sky, stats.surfaces.dirty, + stats.pixels.dirty, + stats.pixels.total, + atlasPixelCount, + float(stats.pixels.total) / float(atlasPixelCount) * 100.0f ); + return out; } CCMD(dumplevelmesh) { - if (level.levelMesh) - { - level.levelMesh->DumpMesh(FString("levelmesh.obj"), FString("levelmesh.mtl")); - Printf("Level mesh exported.\n"); - } - else - { - PrintMissingLevelMesh(); - } + REQUIRE_LEVELMESH(); + levelMesh->DumpMesh(FString("levelmesh.obj"), FString("levelmesh.mtl")); + Printf("Level mesh exported.\n"); } CCMD(invalidatelightmap) { - if (!level.levelMesh) - { - PrintMissingLevelMesh(); - return; - } - if (!level.lightmaps) - { - PrintNoLightmaps(); - return; - } + REQUIRE_LIGHTMAP(); int count = 0; - for (auto& surface : level.levelMesh->Surfaces) + for (auto& surface : levelMesh->Surfaces) { if (!surface.needsUpdate) ++count; surface.needsUpdate = true; } - Printf("Marked %d out of %d surfaces for update.\n", count, level.levelMesh->Surfaces.Size()); + Printf("Marked %d out of %d surfaces for update.\n", count, levelMesh->Surfaces.Size()); } -CCMD(lightmapinfo) +void PrintSurfaceInfo(const DoomLevelMeshSurface* surface) { - if (!level.levelMesh) - { - PrintMissingLevelMesh(); - return; - } - if (!level.lightmaps) - { - PrintNoLightmaps(); - return; - } + REQUIRE_LEVELMESH(); + + Printf("Surface %d (%p)\n Type: %d, TypeIndex: %d, ControlSector: %d\n", levelMesh->GetSurfaceIndex(surface), surface, surface->Type, surface->typeIndex, surface->ControlSector ? surface->ControlSector->Index() : -1); + Printf(" Atlas page: %d, x:%d, y:%d\n", surface->atlasPageIndex, surface->atlasX, surface->atlasY); + Printf(" Pixels: %dx%d (area: %d)\n", surface->texWidth, surface->texHeight, surface->Area()); + Printf(" Sample dimension: %d\n", surface->sampleDimension); + Printf(" Needs update?: %d\n", surface->needsUpdate); +} + +FVector3 RayDir(FAngle angle, FAngle pitch) +{ + auto pc = float(pitch.Cos()); + return FVector3{ pc * float(angle.Cos()), pc * float(angle.Sin()), -float(pitch.Sin()) }; +} + +DVector3 RayDir(DAngle angle, DAngle pitch) +{ + auto pc = pitch.Cos(); + return DVector3{ pc * (angle.Cos()), pc * (angle.Sin()), -(pitch.Sin()) }; } CCMD(surfaceinfo) { - auto* mesh = level.levelMesh; - if (!mesh) - { - PrintMissingLevelMesh(); - return; - } - - auto pov = players[consoleplayer].mo; + REQUIRE_LEVELMESH(); + auto pov = players[consoleplayer].mo; if (!pov) { Printf("players[consoleplayer].mo is null.\n"); @@ -134,27 +98,17 @@ CCMD(surfaceinfo) auto posXYZ = FVector3(pov->Pos()); posXYZ.Z = players[consoleplayer].viewz; - auto angle = pov->Angles.Yaw; auto pitch = pov->Angles.Pitch; - auto pc = float(pitch.Cos()); - auto dir = FVector3{ pc * float(angle.Cos()), pc * float(angle.Sin()), -float(pitch.Sin()) }; // No seriously, is there a dedication function for this? - - const auto surface = (DoomLevelMeshSurface*)mesh->Trace(posXYZ, dir, 32000.0f); - + const auto surface = (DoomLevelMeshSurface*)levelMesh->Trace(posXYZ, FVector3(RayDir(angle, pitch)), 32000.0f); if (surface) { - Printf("Surface %d (%p)\n Type: %d, TypeIndex: %d, ControlSector: %d\n", mesh->GetSurfaceIndex(surface), surface, surface->Type, surface->typeIndex, surface->ControlSector ? surface->ControlSector->Index() : -1); - Printf(" Atlas page: %d, x:%d, y:%d\n", surface->atlasPageIndex, surface->atlasX, surface->atlasY); - Printf(" Pixels: %dx%d (area: %d)\n", surface->texWidth, surface->texHeight, surface->texWidth * surface->texHeight); - Printf(" Sample dimension: %d\n", surface->sampleDimension); - Printf(" Needs update?: %d\n", surface->needsUpdate); + PrintSurfaceInfo(surface); } else { Printf("No surface was hit.\n"); - return; } }