From 48b6a7ec46ed76d62d716c35da1fbe562a478d80 Mon Sep 17 00:00:00 2001 From: Magnus Norddahl Date: Sun, 17 Dec 2023 14:17:22 +0100 Subject: [PATCH] Split level mesh surface from lightmap tiles as we have multiple surfaces per tile --- .../hwrenderer/data/hw_levelmesh.cpp | 92 ++-- .../rendering/hwrenderer/data/hw_levelmesh.h | 19 +- .../hwrenderer/data/hw_levemeshsurface.h | 37 +- .../hwrenderer/data/hw_lightmaptile.h | 64 +++ src/common/rendering/v_video.h | 2 +- src/common/rendering/vulkan/vk_levelmesh.cpp | 1 - src/common/rendering/vulkan/vk_levelmesh.h | 2 +- .../rendering/vulkan/vk_lightmapper.cpp | 101 ++--- src/common/rendering/vulkan/vk_lightmapper.h | 12 +- .../rendering/vulkan/vk_renderdevice.cpp | 4 +- src/common/rendering/vulkan/vk_renderdevice.h | 2 +- src/maploader/maploader.cpp | 401 ++++-------------- src/rendering/hwrenderer/doom_levelmesh.cpp | 42 +- src/rendering/hwrenderer/doom_levelmesh.h | 1 - .../hwrenderer/doom_levelsubmesh.cpp | 252 ++++++----- src/rendering/hwrenderer/doom_levelsubmesh.h | 11 +- .../hwrenderer/scene/hw_drawinfo.cpp | 14 +- src/rendering/hwrenderer/scene/hw_drawinfo.h | 14 +- .../shaders/lightmap/binding_lightmapper.glsl | 2 +- 19 files changed, 461 insertions(+), 612 deletions(-) create mode 100644 src/common/rendering/hwrenderer/data/hw_lightmaptile.h diff --git a/src/common/rendering/hwrenderer/data/hw_levelmesh.cpp b/src/common/rendering/hwrenderer/data/hw_levelmesh.cpp index 25ae6928d..ae3f3f56e 100644 --- a/src/common/rendering/hwrenderer/data/hw_levelmesh.cpp +++ b/src/common/rendering/hwrenderer/data/hw_levelmesh.cpp @@ -55,11 +55,11 @@ LevelMeshSurface* LevelMesh::Trace(const FVector3& start, FVector3 direction, fl return hitSurface; // I hit something } -LevelMeshSurfaceStats LevelMesh::GatherSurfacePixelStats() +LevelMeshTileStats LevelMesh::GatherTilePixelStats() { - LevelMeshSurfaceStats stats; - StaticMesh->GatherSurfacePixelStats(stats); - DynamicMesh->GatherSurfacePixelStats(stats); + LevelMeshTileStats stats; + StaticMesh->GatherTilePixelStats(stats); + DynamicMesh->GatherTilePixelStats(stats); return stats; } @@ -94,98 +94,96 @@ void LevelSubmesh::UpdateCollision() Collision = std::make_unique(Mesh.Vertices.Data(), Mesh.Vertices.Size(), Mesh.Indexes.Data(), Mesh.Indexes.Size()); } -void LevelSubmesh::GatherSurfacePixelStats(LevelMeshSurfaceStats& stats) +void LevelSubmesh::GatherTilePixelStats(LevelMeshTileStats& stats) { int count = GetSurfaceCount(); - for (int i = 0; i < count; ++i) + for (const LightmapTile& tile : LightmapTiles) { - const auto* surface = GetSurface(i); - auto area = surface->AtlasTile.Area(); + auto area = tile.AtlasLocation.Area(); stats.pixels.total += area; - if (surface->NeedsUpdate) + if (tile.NeedsUpdate) { - stats.surfaces.dirty++; + stats.tiles.dirty++; stats.pixels.dirty += area; } - if (surface->IsSky) - { - stats.surfaces.sky++; - stats.pixels.sky += area; - } } - stats.surfaces.total += count; + stats.tiles.total += LightmapTiles.Size(); } +struct LevelMeshPlaneGroup +{ + FVector4 plane = FVector4(0, 0, 1, 0); + int sectorGroup = 0; + std::vector surfaces; +}; + void LevelSubmesh::BuildTileSurfaceLists() { - // Smoothing group surface is to be rendered with - TArray SmoothingGroups; - TArray SmoothingGroupIndexes(GetSurfaceCount()); + // Plane group surface is to be rendered with + TArray PlaneGroups; + TArray PlaneGroupIndexes(GetSurfaceCount()); for (int i = 0, count = GetSurfaceCount(); i < count; i++) { auto surface = GetSurface(i); - // Is this surface in the same plane as an existing smoothing group? - int smoothingGroupIndex = -1; + // Is this surface in the same plane as an existing plane group? + int planeGroupIndex = -1; - for (size_t j = 0; j < SmoothingGroups.Size(); j++) + for (size_t j = 0; j < PlaneGroups.Size(); j++) { - if (surface->SectorGroup == SmoothingGroups[j].sectorGroup) + if (surface->SectorGroup == PlaneGroups[j].sectorGroup) { - float direction = SmoothingGroups[j].plane.XYZ() | surface->Plane.XYZ(); + float direction = PlaneGroups[j].plane.XYZ() | surface->Plane.XYZ(); if (direction >= 0.9999f && direction <= 1.001f) { auto point = (surface->Plane.XYZ() * surface->Plane.W); - auto planeDistance = (SmoothingGroups[j].plane.XYZ() | point) - SmoothingGroups[j].plane.W; + auto planeDistance = (PlaneGroups[j].plane.XYZ() | point) - PlaneGroups[j].plane.W; float dist = std::abs(planeDistance); if (dist <= 0.01f) { - smoothingGroupIndex = (int)j; + planeGroupIndex = (int)j; break; } } } } - // Surface is in a new plane. Create a smoothing group for it - if (smoothingGroupIndex == -1) + // Surface is in a new plane. Create a plane group for it + if (planeGroupIndex == -1) { - smoothingGroupIndex = SmoothingGroups.Size(); + planeGroupIndex = PlaneGroups.Size(); - LevelMeshSmoothingGroup group; + LevelMeshPlaneGroup group; group.plane = surface->Plane; group.sectorGroup = surface->SectorGroup; - SmoothingGroups.Push(group); + PlaneGroups.Push(group); } - SmoothingGroups[smoothingGroupIndex].surfaces.push_back(surface); - SmoothingGroupIndexes.Push(smoothingGroupIndex); + PlaneGroups[planeGroupIndex].surfaces.push_back(surface); + PlaneGroupIndexes.Push(planeGroupIndex); } + for (auto& tile : LightmapTiles) + tile.Surfaces.Clear(); + for (int i = 0, count = GetSurfaceCount(); i < count; i++) { - auto targetSurface = GetSurface(i); - targetSurface->TileSurfaces.Clear(); - for (LevelMeshSurface* surface : SmoothingGroups[SmoothingGroupIndexes[i]].surfaces) + LevelMeshSurface* targetSurface = GetSurface(i); + if (targetSurface->LightmapTileIndex < 0) + continue; + LightmapTile* tile = &LightmapTiles[targetSurface->LightmapTileIndex]; + for (LevelMeshSurface* surface : PlaneGroups[PlaneGroupIndexes[i]].surfaces) { - FVector2 minUV = ToUV(surface->Bounds.min, targetSurface); - FVector2 maxUV = ToUV(surface->Bounds.max, targetSurface); + FVector2 minUV = tile->ToUV(surface->Bounds.min); + FVector2 maxUV = tile->ToUV(surface->Bounds.max); if (surface != targetSurface && (maxUV.X < 0.0f || maxUV.Y < 0.0f || minUV.X > 1.0f || minUV.Y > 1.0f)) continue; // Bounding box not visible - targetSurface->TileSurfaces.Push(surface); + tile->Surfaces.Push(surface); } } } - -FVector2 LevelSubmesh::ToUV(const FVector3& vert, const LevelMeshSurface* targetSurface) -{ - FVector3 localPos = vert - targetSurface->TileTransform.TranslateWorldToLocal; - float u = (1.0f + (localPos | targetSurface->TileTransform.ProjLocalToU)) / (targetSurface->AtlasTile.Width + 2); - float v = (1.0f + (localPos | targetSurface->TileTransform.ProjLocalToV)) / (targetSurface->AtlasTile.Height + 2); - return FVector2(u, v); -} diff --git a/src/common/rendering/hwrenderer/data/hw_levelmesh.h b/src/common/rendering/hwrenderer/data/hw_levelmesh.h index 621a8ceb8..980fac2aa 100644 --- a/src/common/rendering/hwrenderer/data/hw_levelmesh.h +++ b/src/common/rendering/hwrenderer/data/hw_levelmesh.h @@ -7,12 +7,13 @@ #include "flatvertices.h" #include "hw_levelmeshlight.h" #include "hw_levelmeshportal.h" +#include "hw_lightmaptile.h" #include "hw_levemeshsurface.h" #include "hw_materialstate.h" #include "hw_surfaceuniforms.h" #include -struct LevelMeshSurfaceStats; +struct LevelMeshTileStats; struct LevelSubmeshDrawRange { @@ -48,19 +49,21 @@ public: // Lightmap atlas int LMTextureCount = 0; - int LMTextureSize = 0; + int LMTextureSize = 1024; TArray LMTextureData; uint16_t LightmapSampleDistance = 16; + TArray LightmapTiles; + uint32_t AtlasPixelCount() const { return uint32_t(LMTextureCount * LMTextureSize * LMTextureSize); } void UpdateCollision(); - void GatherSurfacePixelStats(LevelMeshSurfaceStats& stats); + void GatherTilePixelStats(LevelMeshTileStats& stats); void BuildTileSurfaceLists(); private: - FVector2 ToUV(const FVector3& vert, const LevelMeshSurface* targetSurface); + FVector2 ToUV(const FVector3& vert, const LightmapTile* tile); }; class LevelMesh @@ -76,7 +79,7 @@ public: LevelMeshSurface* Trace(const FVector3& start, FVector3 direction, float maxDist); - LevelMeshSurfaceStats GatherSurfacePixelStats(); + LevelMeshTileStats GatherTilePixelStats(); // Map defaults FVector3 SunDirection = FVector3(0.0f, 0.0f, -1.0f); @@ -85,12 +88,12 @@ public: TArray Portals; }; -struct LevelMeshSurfaceStats +struct LevelMeshTileStats { struct Stats { - uint32_t total = 0, dirty = 0, sky = 0; + uint32_t total = 0, dirty = 0; }; - Stats surfaces, pixels; + Stats tiles, pixels; }; diff --git a/src/common/rendering/hwrenderer/data/hw_levemeshsurface.h b/src/common/rendering/hwrenderer/data/hw_levemeshsurface.h index e9ab89a1d..854ab7937 100644 --- a/src/common/rendering/hwrenderer/data/hw_levemeshsurface.h +++ b/src/common/rendering/hwrenderer/data/hw_levemeshsurface.h @@ -9,6 +9,7 @@ class LevelSubmesh; class FGameTexture; +struct LevelMeshSurface; struct LevelMeshSurface { @@ -22,21 +23,10 @@ struct LevelMeshSurface unsigned int NumElements = 0; } MeshLocation; + BBox Bounds; FVector4 Plane = FVector4(0.0f, 0.0f, 1.0f, 0.0f); + int LightmapTileIndex = -1; - // Surface location in lightmap texture - struct - { - int X = 0; - int Y = 0; - int Width = 0; - int Height = 0; - int ArrayIndex = 0; - uint32_t Area() const { return Width * Height; } - } AtlasTile; - - // True if the surface needs to be rendered into the lightmap texture before it can be used - bool NeedsUpdate = true; bool AlwaysUpdate = false; FGameTexture* Texture = nullptr; @@ -46,20 +36,6 @@ struct LevelMeshSurface int PortalIndex = 0; int SectorGroup = 0; - BBox Bounds; - uint16_t SampleDimension = 0; - - // Calculate world coordinates to UV coordinates - struct - { - FVector3 TranslateWorldToLocal = { 0.0f, 0.0f, 0.0f }; - FVector3 ProjLocalToU = { 0.0f, 0.0f, 0.0f }; - FVector3 ProjLocalToV = { 0.0f, 0.0f, 0.0f }; - } TileTransform; - - // Surfaces that are visible within the lightmap tile - TArray TileSurfaces; - // Light list location in the lightmapper GPU buffers struct { @@ -68,10 +44,3 @@ struct LevelMeshSurface int ResetCounter = -1; } LightList; }; - -struct LevelMeshSmoothingGroup -{ - FVector4 plane = FVector4(0, 0, 1, 0); - int sectorGroup = 0; - std::vector surfaces; -}; diff --git a/src/common/rendering/hwrenderer/data/hw_lightmaptile.h b/src/common/rendering/hwrenderer/data/hw_lightmaptile.h new file mode 100644 index 000000000..406b360c8 --- /dev/null +++ b/src/common/rendering/hwrenderer/data/hw_lightmaptile.h @@ -0,0 +1,64 @@ + +#pragma once + +#include "tarray.h" +#include "vectors.h" +#include "bounds.h" + +struct LevelMeshSurface; + +struct LightmapTileBinding +{ + uint32_t Type = 0; + uint32_t TypeIndex = 0; + uint32_t ControlSector = 0xffffffff; + + bool operator<(const LightmapTileBinding& other) const + { + if (TypeIndex != other.TypeIndex) return TypeIndex < other.TypeIndex; + if (ControlSector != other.ControlSector) return ControlSector < other.ControlSector; + return Type < other.Type; + } +}; + +struct LightmapTile +{ + // Surface location in lightmap texture + struct + { + int X = 0; + int Y = 0; + int Width = 0; + int Height = 0; + int ArrayIndex = 0; + uint32_t Area() const { return Width * Height; } + } AtlasLocation; + + // Calculate world coordinates to UV coordinates + struct + { + FVector3 TranslateWorldToLocal = { 0.0f, 0.0f, 0.0f }; + FVector3 ProjLocalToU = { 0.0f, 0.0f, 0.0f }; + FVector3 ProjLocalToV = { 0.0f, 0.0f, 0.0f }; + } Transform; + + LightmapTileBinding Binding; + + // Surfaces that are visible within the lightmap tile + TArray Surfaces; + + BBox Bounds; + uint16_t SampleDimension = 0; + FVector4 Plane = FVector4(0.0f, 0.0f, 1.0f, 0.0f); + + // True if the tile needs to be rendered into the lightmap texture before it can be used + bool NeedsUpdate = true; + + FVector2 ToUV(const FVector3& vert) const + { + FVector3 localPos = vert - Transform.TranslateWorldToLocal; + float u = (1.0f + (localPos | Transform.ProjLocalToU)) / (AtlasLocation.Width + 2); + float v = (1.0f + (localPos | Transform.ProjLocalToV)) / (AtlasLocation.Height + 2); + return FVector2(u, v); + } +}; diff --git a/src/common/rendering/v_video.h b/src/common/rendering/v_video.h index 2e0739993..721347e87 100644 --- a/src/common/rendering/v_video.h +++ b/src/common/rendering/v_video.h @@ -139,7 +139,7 @@ public: virtual bool IsPoly() { return false; } virtual bool CompileNextShader() { return true; } virtual void SetLevelMesh(LevelMesh *mesh) { } - virtual void UpdateLightmaps(const TArray& surfaces) {} + virtual void UpdateLightmaps(const TArray& tiles) {} virtual DCanvas* GetCanvas() { return nullptr; } diff --git a/src/common/rendering/vulkan/vk_levelmesh.cpp b/src/common/rendering/vulkan/vk_levelmesh.cpp index 9fe848273..4deacf01f 100644 --- a/src/common/rendering/vulkan/vk_levelmesh.cpp +++ b/src/common/rendering/vulkan/vk_levelmesh.cpp @@ -640,7 +640,6 @@ void VkLevelMeshUploader::UploadSurfaces() SurfaceInfo info; info.Normal = FVector3(surface->Plane.X, surface->Plane.Z, surface->Plane.Y); info.PortalIndex = surface->PortalIndex; - info.SamplingDistance = (float)surface->SampleDimension; info.Sky = surface->IsSky; info.Alpha = surface->Alpha; if (surface->Texture) diff --git a/src/common/rendering/vulkan/vk_levelmesh.h b/src/common/rendering/vulkan/vk_levelmesh.h index 35f428c51..e23f620c0 100644 --- a/src/common/rendering/vulkan/vk_levelmesh.h +++ b/src/common/rendering/vulkan/vk_levelmesh.h @@ -31,10 +31,10 @@ struct SurfaceInfo { FVector3 Normal; float Sky; - float SamplingDistance; uint32_t PortalIndex; int32_t TextureIndex; float Alpha; + float Padding; }; struct PortalInfo diff --git a/src/common/rendering/vulkan/vk_lightmapper.cpp b/src/common/rendering/vulkan/vk_lightmapper.cpp index 4a7a5126a..380121163 100644 --- a/src/common/rendering/vulkan/vk_lightmapper.cpp +++ b/src/common/rendering/vulkan/vk_lightmapper.cpp @@ -92,16 +92,16 @@ void VkLightmapper::BeginFrame() drawindexed.Pos = 0; } -void VkLightmapper::Raytrace(const TArray& surfaces) +void VkLightmapper::Raytrace(const TArray& tiles) { - if (mesh && surfaces.Size() > 0) + if (mesh && tiles.Size() > 0) { lightmapRaytraceLast.active = true; lightmapRaytraceLast.ResetAndClock(); - SelectSurfaces(surfaces); - if (selectedSurfaces.Size() > 0) + SelectTiles(tiles); + if (selectedTiles.Size() > 0) { fb->GetCommands()->PushGroup(fb->GetCommands()->GetTransferCommands(), "lightmap.total"); @@ -119,36 +119,36 @@ void VkLightmapper::Raytrace(const TArray& surfaces) } } -void VkLightmapper::SelectSurfaces(const TArray& surfaces) +void VkLightmapper::SelectTiles(const TArray& tiles) { bakeImage.maxX = 0; bakeImage.maxY = 0; - selectedSurfaces.Clear(); + selectedTiles.Clear(); const int spacing = 5; // Note: the spacing is here to avoid that the resolve sampler finds data from other surface tiles RectPacker packer(bakeImageSize - spacing, bakeImageSize - spacing, RectPacker::Spacing(spacing)); - for (int i = 0, count = surfaces.Size(); i < count; i++) + for (int i = 0, count = tiles.Size(); i < count; i++) { - LevelMeshSurface* surface = surfaces[i]; + LightmapTile* tile = tiles[i]; - if (!surface->NeedsUpdate) + if (!tile->NeedsUpdate) continue; // Only grab surfaces until our bake texture is full - auto result = packer.insert(surface->AtlasTile.Width + 2, surface->AtlasTile.Height + 2); + auto result = packer.insert(tile->AtlasLocation.Width + 2, tile->AtlasLocation.Height + 2); if (result.pageIndex == 0) { - SelectedSurface selected; - selected.Surface = surface; + SelectedTile selected; + selected.Tile = tile; selected.X = result.pos.x + 1; selected.Y = result.pos.y + 1; - selectedSurfaces.Push(selected); + selectedTiles.Push(selected); - bakeImage.maxX = std::max(bakeImage.maxX, uint16_t(selected.X + surface->AtlasTile.Width + spacing)); - bakeImage.maxY = std::max(bakeImage.maxY, uint16_t(selected.Y + surface->AtlasTile.Height + spacing)); + bakeImage.maxX = std::max(bakeImage.maxX, uint16_t(selected.X + tile->AtlasLocation.Width + spacing)); + bakeImage.maxY = std::max(bakeImage.maxY, uint16_t(selected.Y + tile->AtlasLocation.Height + spacing)); - surface->NeedsUpdate = false; + tile->NeedsUpdate = false; } } } @@ -180,35 +180,40 @@ void VkLightmapper::Render() viewport.height = (float)bakeImageSize; cmdbuffer->setViewport(0, 1, &viewport); - for (int i = 0, count = selectedSurfaces.Size(); i < count; i++) - { - auto& selectedSurface = selectedSurfaces[i]; - LevelMeshSurface* targetSurface = selectedSurface.Surface; + int dynamicSurfaceIndexOffset = mesh->StaticMesh->GetSurfaceCount(); + int dynamicFirstIndexOffset = mesh->StaticMesh->Mesh.Indexes.Size(); + LevelSubmesh* staticMesh = mesh->StaticMesh.get(); - int surfaceIndexOffset = 0; - int firstIndexOffset = 0; - if (targetSurface->Submesh != mesh->StaticMesh.get()) - { - surfaceIndexOffset = mesh->StaticMesh->GetSurfaceCount(); - firstIndexOffset = mesh->StaticMesh->Mesh.Indexes.Size(); - } + for (int i = 0, count = selectedTiles.Size(); i < count; i++) + { + auto& selectedTile = selectedTiles[i]; + LightmapTile* targetTile = selectedTile.Tile; LightmapRaytracePC pc; - pc.TileX = (float)selectedSurface.X; - pc.TileY = (float)selectedSurface.Y; - pc.SurfaceIndex = surfaceIndexOffset + targetSurface->Submesh->GetSurfaceIndex(targetSurface); + pc.TileX = (float)selectedTile.X; + pc.TileY = (float)selectedTile.Y; pc.TextureSize = (float)bakeImageSize; - pc.TileWidth = (float)targetSurface->AtlasTile.Width; - pc.TileHeight = (float)targetSurface->AtlasTile.Height; - pc.WorldToLocal = SwapYZ(targetSurface->TileTransform.TranslateWorldToLocal); - pc.ProjLocalToU = SwapYZ(targetSurface->TileTransform.ProjLocalToU); - pc.ProjLocalToV = SwapYZ(targetSurface->TileTransform.ProjLocalToV); + pc.TileWidth = (float)targetTile->AtlasLocation.Width; + pc.TileHeight = (float)targetTile->AtlasLocation.Height; + pc.WorldToLocal = SwapYZ(targetTile->Transform.TranslateWorldToLocal); + pc.ProjLocalToU = SwapYZ(targetTile->Transform.ProjLocalToU); + pc.ProjLocalToV = SwapYZ(targetTile->Transform.ProjLocalToV); bool buffersFull = false; // Paint all surfaces visible in the tile - for (LevelMeshSurface* surface : targetSurface->TileSurfaces) + for (LevelMeshSurface* surface : targetTile->Surfaces) { + int surfaceIndexOffset = 0; + int firstIndexOffset = 0; + if (surface->Submesh != staticMesh) + { + surfaceIndexOffset = dynamicSurfaceIndexOffset; + firstIndexOffset = dynamicFirstIndexOffset; + } + + pc.SurfaceIndex = surfaceIndexOffset + surface->Submesh->GetSurfaceIndex(surface); + if (surface->LightList.ResetCounter != lights.ResetCounter) { int lightCount = mesh->AddSurfaceLights(surface, templightlist.Data(), (int)templightlist.Size()); @@ -272,13 +277,13 @@ void VkLightmapper::Render() { while (i < count) { - selectedSurfaces[i].Surface->NeedsUpdate = true; + selectedTiles[i].Tile->NeedsUpdate = true; i++; } break; } - selectedSurface.Rendered = true; + selectedTile.Rendered = true; } #ifdef USE_DRAWINDIRECT @@ -407,19 +412,19 @@ void VkLightmapper::CopyResult() uint32_t pixels = 0; lastSurfaceCount = 0; for (auto& list : copylists) list.Clear(); - for (int i = 0, count = selectedSurfaces.Size(); i < count; i++) + for (int i = 0, count = selectedTiles.Size(); i < count; i++) { - auto& selected = selectedSurfaces[i]; + auto& selected = selectedTiles[i]; if (selected.Rendered) { - unsigned int pageIndex = (unsigned int)selected.Surface->AtlasTile.ArrayIndex; + unsigned int pageIndex = (unsigned int)selected.Tile->AtlasLocation.ArrayIndex; if (pageIndex >= copylists.Size()) { copylists.Resize(pageIndex + 1); } copylists[pageIndex].Push(&selected); - pixels += selected.Surface->AtlasTile.Area(); + pixels += selected.Tile->AtlasLocation.Area(); lastSurfaceCount++; } } @@ -484,17 +489,17 @@ void VkLightmapper::CopyResult() // Copy the tile positions into a storage buffer for the vertex shader to read start = pos; - for (SelectedSurface* selected : list) + for (SelectedTile* selected : list) { - LevelMeshSurface* surface = selected->Surface; + LightmapTile* tile = selected->Tile; CopyTileInfo* copyinfo = ©tiles.Tiles[pos++]; copyinfo->SrcPosX = selected->X; copyinfo->SrcPosY = selected->Y; - copyinfo->DestPosX = surface->AtlasTile.X; - copyinfo->DestPosY = surface->AtlasTile.Y; - copyinfo->TileWidth = surface->AtlasTile.Width; - copyinfo->TileHeight = surface->AtlasTile.Height; + copyinfo->DestPosX = tile->AtlasLocation.X; + copyinfo->DestPosY = tile->AtlasLocation.Y; + copyinfo->TileWidth = tile->AtlasLocation.Width; + copyinfo->TileHeight = tile->AtlasLocation.Height; } // Draw the tiles. One instance per tile. diff --git a/src/common/rendering/vulkan/vk_lightmapper.h b/src/common/rendering/vulkan/vk_lightmapper.h index 34b8572cf..f8e14fab5 100644 --- a/src/common/rendering/vulkan/vk_lightmapper.h +++ b/src/common/rendering/vulkan/vk_lightmapper.h @@ -95,9 +95,9 @@ struct LightInfo float Padding3; }; -struct SelectedSurface +struct SelectedTile { - LevelMeshSurface* Surface = nullptr; + LightmapTile* Tile = nullptr; int X = -1; int Y = -1; bool Rendered = false; @@ -126,13 +126,13 @@ public: ~VkLightmapper(); void BeginFrame(); - void Raytrace(const TArray& surfaces); + void Raytrace(const TArray& surfaces); void SetLevelMesh(LevelMesh* level); private: void ReleaseResources(); - void SelectSurfaces(const TArray& surfaces); + void SelectTiles(const TArray& surfaces); void UploadUniforms(); void Render(); void Resolve(); @@ -165,8 +165,8 @@ private: bool useRayQuery = true; - TArray selectedSurfaces; - TArray> copylists; + TArray selectedTiles; + TArray> copylists; TArray templightlist; struct diff --git a/src/common/rendering/vulkan/vk_renderdevice.cpp b/src/common/rendering/vulkan/vk_renderdevice.cpp index 5cac3e741..ac112a97d 100644 --- a/src/common/rendering/vulkan/vk_renderdevice.cpp +++ b/src/common/rendering/vulkan/vk_renderdevice.cpp @@ -553,9 +553,9 @@ void VulkanRenderDevice::SetLevelMesh(LevelMesh* mesh) levelMeshChanged = true; } -void VulkanRenderDevice::UpdateLightmaps(const TArray& surfaces) +void VulkanRenderDevice::UpdateLightmaps(const TArray& tiles) { - GetLightmapper()->Raytrace(surfaces); + GetLightmapper()->Raytrace(tiles); } void VulkanRenderDevice::SetShadowMaps(const TArray& lights, hwrenderer::LevelAABBTree* tree, bool newTree) diff --git a/src/common/rendering/vulkan/vk_renderdevice.h b/src/common/rendering/vulkan/vk_renderdevice.h index d790ffc39..563a29caf 100644 --- a/src/common/rendering/vulkan/vk_renderdevice.h +++ b/src/common/rendering/vulkan/vk_renderdevice.h @@ -65,7 +65,7 @@ public: void AmbientOccludeScene(float m5) override; void SetSceneRenderTarget(bool useSSAO) override; void SetLevelMesh(LevelMesh* mesh) override; - void UpdateLightmaps(const TArray& surfaces) override; + void UpdateLightmaps(const TArray& tiles) override; void SetShadowMaps(const TArray& lights, hwrenderer::LevelAABBTree* tree, bool newTree) override; void SetSaveBuffers(bool yes) override; void ImageTransitionScene(bool unknown) override; diff --git a/src/maploader/maploader.cpp b/src/maploader/maploader.cpp index ec1b37756..1cca5076e 100644 --- a/src/maploader/maploader.cpp +++ b/src/maploader/maploader.cpp @@ -3012,18 +3012,12 @@ void MapLoader::InitLevelMesh(MapData* map) Level->levelMesh = new DoomLevelMesh(*Level); // Lightmap binding/loading - if (Level->lightmaps) - { - if (!LoadLightmap(map)) - { - Level->levelMesh->PackLightmapAtlas(); - } - } + LoadLightmap(map); } bool MapLoader::LoadLightmap(MapData* map) { - if (!map->Size(ML_LIGHTMAP)) + if (!Level->lightmaps || !map->Size(ML_LIGHTMAP)) return false; FileReader fr; @@ -3031,65 +3025,34 @@ bool MapLoader::LoadLightmap(MapData* map) return false; int version = fr.ReadInt32(); - if (version == 0) - { - Printf(PRINT_HIGH, "LoadLightmap: This is an old unsupported alpha version of the lightmap lump. Please rebuild the map with a newer version of zdray.\n"); - return false; - } - if (version == 1) + if (version < 2) { Printf(PRINT_HIGH, "LoadLightmap: This is an old unsupported version of the lightmap lump. Please rebuild the map with a newer version of zdray.\n"); return false; } - if (version != 2) + else if (version != 2) { Printf(PRINT_HIGH, "LoadLightmap: unsupported lightmap lump version\n"); return false; } - uint32_t numSurfaces = fr.ReadUInt32(); + uint32_t numTiles = fr.ReadUInt32(); uint32_t numTexPixels = fr.ReadUInt32(); - uint32_t numTexCoords = fr.ReadUInt32(); + uint32_t numTexCoords = fr.ReadUInt32(); // To do: remove from a future version of the format. We don't need this. if (developer >= 5) { - Printf("LoadLightmap: Surfaces: %u, Pixels: %u, UVs: %u\n", numSurfaces, numTexPixels, numTexCoords); + Printf("LoadLightmap: Tiles: %u, Pixels: %u, UVs: %u\n", numTiles, numTexPixels, numTexCoords); } - if (numSurfaces == 0 || numTexCoords == 0 || numTexPixels == 0) + if (numTiles == 0 || numTexCoords == 0 || numTexPixels == 0) return false; - bool errors = false; + int errors = 0; - // Load the surfaces we have lightmap data for + // Load the tiles we have lightmap data for - const int surfaceTypes = 5; // ST_CEILING, ST_FLOOR, etc. - TMap> surfaceGroups[surfaceTypes]; // Let's assume there is only a handful of 3d floor surfaces matching the same typeIndex - - auto findSurfaceIndex = [&](int type, int index, const sector_t* sec) { - const auto* surfaces = surfaceGroups[type - 1].CheckKey(index); - - if (surfaces) - { - for (unsigned i = 0, count = surfaces->Size(); i < count; ++i) - { - const auto surface = (*surfaces)[i]; - - if (surface->ControlSector == sec) - { - return Level->levelMesh->StaticMesh->GetSurfaceIndex(surface); - } - } - } - return 0xffffffff; - }; - - auto getControlSector = [&](uint32_t index) - { - return index < Level->sectors.Size() ? &Level->sectors[index] : nullptr; - }; - - struct SurfaceEntry // V2 entries + struct TileEntry // V2 entries { uint32_t type, typeIndex; uint32_t controlSector; // 0xFFFFFFFF is none @@ -3100,114 +3063,21 @@ bool MapLoader::LoadLightmap(MapData* map) DoomLevelMeshSurface* targetSurface; }; - TMap detectedSurfaces; - TArray zdraySurfaces; - zdraySurfaces.Reserve(numSurfaces); - - auto submesh = static_cast(Level->levelMesh->StaticMesh.get()); - - for (auto& surface : submesh->Surfaces) - { - surface.NeedsUpdate = false; // let's consider everything valid until we make a mistake trying to change this surface - - if (surface.Type > ST_NONE && surface.Type <= ST_FLOOR) - { - if (auto list = surfaceGroups[surface.Type - 1].CheckKey(surface.TypeIndex)) - { - list->Push(&surface); - } - else - { - surfaceGroups[surface.Type - 1].InsertNew(surface.TypeIndex).Push(&surface); - } - } - } + TArray tileEntries; + tileEntries.Reserve(numTiles); uint32_t usedSurfaceIndex = 0; - for (uint32_t i = 0; i < numSurfaces; i++) + for (uint32_t i = 0; i < numTiles; i++) { - SurfaceEntry surface; - surface.type = fr.ReadUInt32(); - surface.typeIndex = fr.ReadUInt32(); - surface.controlSector = fr.ReadUInt32(); - surface.width = fr.ReadUInt16(); - surface.height = fr.ReadUInt16(); - surface.pixelsOffset = fr.ReadUInt32(); - surface.uvCount = fr.ReadUInt32(); - surface.uvOffset = fr.ReadUInt32(); - - auto controlSector = getControlSector(surface.controlSector); - - // Check against the internal levelmesh - - if (surface.type <= ST_NONE || surface.type > ST_FLOOR) - { - errors = true; - if (developer >= 1) - { - Printf(PRINT_HIGH, "Lightmap lump surface index %d uses invalid type %d\n", i, surface.type); - } - continue; - } - - if (i >= submesh->Surfaces.Size()) - { - errors = true; - if (developer >= 1) - { - Printf(PRINT_HIGH, "Lightmap lump surface index %d is out of bounds\n", i); - } - continue; - } - - auto levelSurface = &submesh->Surfaces[i]; - - if (levelSurface->Type != surface.type || levelSurface->TypeIndex != surface.typeIndex || levelSurface->ControlSector != controlSector) - { - auto internalIndex = findSurfaceIndex(surface.type, surface.typeIndex, controlSector); - - if (internalIndex < submesh->Surfaces.Size()) - { - levelSurface = &submesh->Surfaces[internalIndex]; - } - else - { - errors = true; - if (developer >= 1) - { - Printf(PRINT_HIGH, "Lightmap lump surface %d mismatch. Couldn't find surface type:%d, typeindex:%d, controlsector:%d\n", i, surface.type, surface.typeIndex, surface.controlSector); - } - // TODO detailed printout - continue; - } - } - - if (auto ptr = detectedSurfaces.CheckKey(levelSurface)) - { - (*ptr)++; - if (developer >= 1) - { - Printf(PRINT_HIGH, "Lightmap lump surface index %d is referencing surface %d (ref count: %d). Surface type:%d, typeindex:%d, controlsector:%d\n", i, submesh->GetSurfaceIndex(levelSurface), *ptr, surface.type, surface.typeIndex, surface.controlSector); - } - } - else - { - levelSurface->AtlasTile.Width = surface.width; - levelSurface->AtlasTile.Height = surface.height; - - surface.targetSurface = levelSurface; - detectedSurfaces.Insert(levelSurface, 1); - zdraySurfaces[usedSurfaceIndex++] = surface; - } - } - - Level->levelMesh->PackLightmapAtlas(); - - zdraySurfaces.Resize(usedSurfaceIndex); - - if (developer >= 1) - { - Printf("Lightmap contains %d surfaces out of which %d were successfully matched.\n", numSurfaces, zdraySurfaces.Size()); + TileEntry& entry = tileEntries[i]; + entry.type = fr.ReadUInt32(); + entry.typeIndex = fr.ReadUInt32(); + entry.controlSector = fr.ReadUInt32(); + entry.width = fr.ReadUInt16(); + entry.height = fr.ReadUInt16(); + entry.pixelsOffset = fr.ReadUInt32(); + entry.uvCount = fr.ReadUInt32(); + entry.uvOffset = fr.ReadUInt32(); } // Load pixels @@ -3216,184 +3086,81 @@ bool MapLoader::LoadLightmap(MapData* map) uint8_t* data = (uint8_t*)&textureData[0]; fr.Read(data, numTexPixels * 3 * sizeof(uint16_t)); - // Load texture coordinates - TArray zdrayUvs; - zdrayUvs.Resize(numTexCoords); - fr.Read(&zdrayUvs[0], numTexCoords * 2 * sizeof(float)); + // Load texture coordinates + // TArray zdrayUvs; + // zdrayUvs.Resize(numTexCoords); + // fr.Read(&zdrayUvs[0], numTexCoords * 2 * sizeof(float)); - // Load lightmap textures + auto submesh = Level->levelMesh->StaticMesh.get(); const auto textureSize = submesh->LMTextureSize; + + // Start with empty lightmap textures submesh->LMTextureData.Resize(submesh->LMTextureCount * textureSize * textureSize * 3); + memset(submesh->LMTextureData.Data(), 0, submesh->LMTextureData.Size() * sizeof(uint16_t)); - auto pixels = &submesh->LMTextureData[0]; - for (int i = 0, count = submesh->LMTextureData.Size(); i < count; i += 3) + // Create lookup for finding tiles + std::map levelTiles; + for (LightmapTile& tile : submesh->LightmapTiles) { - pixels[i] = floatToHalf(0.0); - pixels[i + 1] = floatToHalf(0.0); - pixels[i + 2] = floatToHalf(0.0); + levelTiles[tile.Binding] = &tile; } -#if 0 // debug surface mapping - for (auto& surface : submesh->Surfaces) + // Bind tiles and copy their pixels to the texture + for (const TileEntry& entry : tileEntries) { - int dstX = surface.AtlasTile.X; - int dstY = surface.AtlasTile.Y; - int dstPage = surface.AtlasTile.ArrayIndex; + LightmapTileBinding binding; + binding.Type = entry.type; + binding.TypeIndex = entry.typeIndex; + binding.ControlSector = entry.controlSector; - // copy pixels - uint16_t* dst = &submesh->LMTextureData[dstPage * textureSize * textureSize * 3]; - - uint32_t srcIndex = 0; - - if (auto ptr = detectedSurfaces.CheckKey(&surface)) + auto it = levelTiles.find(binding); + if (it == levelTiles.end()) { - for (int y = 0; y < surface.AtlasTile.Height; ++y) - { - for (int x = 0; x < surface.AtlasTile.Width; ++x) - { - uint32_t dstIndex = uint32_t(dstX + x + (dstY + y) * textureSize) * 3; + if (errors < 10 && developer >= 1) + Printf("Could not find lightmap tile in level mesh (type = %d, index = %d, control sector = %d)\n", entry.type, entry.typeIndex, entry.controlSector); + errors++; + continue; + } - dst[dstIndex] = floatToHalf(1.0); - dst[dstIndex + 1] = floatToHalf(0.0); - dst[dstIndex + 2] = floatToHalf(0.0); - } + LightmapTile* tile = it->second; + + // To do: add transform info to the lump so that we can stretch the pixels as the lump lightmapper might be using fixed point coordinates that could cause alignment issues + + if (tile->AtlasLocation.Width != entry.width || tile->AtlasLocation.Height != entry.height) + { + if (errors < 10 && developer >= 1) + Printf("Lightmap tile size mismatch (type = %d, index = %d, control sector = %d)\n", entry.type, entry.typeIndex, entry.controlSector); + errors++; + continue; + } + + const uint16_t* src = textureData.Data() + entry.pixelsOffset; + uint16_t* dst = &submesh->LMTextureData[tile->AtlasLocation.ArrayIndex * textureSize * textureSize * 3]; + + int x = tile->AtlasLocation.X; + int y = tile->AtlasLocation.Y; + int w = tile->AtlasLocation.Width; + int h = tile->AtlasLocation.Height; + + for (int yy = 0; yy < w; yy++) + { + const uint16_t* srcline = src + yy * w * 3; + uint16_t* dstline = dst + (x + yy * textureSize) * 3; + for (int xx = 0, end = w * 3; xx < end; xx++) + { + dstline[xx] = srcline[xx]; } } + + tile->NeedsUpdate = false; + } + + if (errors > 0) + { + if (developer <= 0) + Printf(PRINT_HIGH, "Pre-calculated LIGHTMAP surfaces do not match current level surfaces. Restart this level with 'developer 1' for further details.\nPerhaps you forget to rebuild lightmaps after modifying the map?\n"); else - { - for (int y = 0; y < surface.AtlasTile.Height; ++y) - { - for (int x = 0; x < surface.AtlasTile.Width; ++x) - { - uint32_t dstIndex = uint32_t(dstX + x + (dstY + y) * textureSize) * 3; - - dst[dstIndex] = floatToHalf(0.0); - dst[dstIndex + 1] = floatToHalf(0.0); - dst[dstIndex + 2] = floatToHalf(0.0); - } - } - } - } -#endif - - // Map surface pixels into atlas - int index = -1; - for (auto& surface : zdraySurfaces) - { - ++index; // for debug output - - auto& realSurface = *surface.targetSurface; - - // calculate pixel positions - const uint32_t srcPixelOffset = surface.pixelsOffset; - - const int dstX = realSurface.AtlasTile.X; - const int dstY = realSurface.AtlasTile.Y; - const int dstPage = realSurface.AtlasTile.ArrayIndex; - - // Sanity checks - if (dstX < 0 || dstY < 0 || dstX + surface.width > textureSize || dstY + surface.height > textureSize || dstPage >= submesh->LMTextureCount) - { - errors = true; - if (developer >= 1) - { - Printf("Can't map lightmap surface %d pixels[%u] -> ((x:%d, y:%d), (x2:%d, y2:%d), page:%d)\n", index, srcPixelOffset, dstX, dstY, dstX + surface.width, dstY + surface.height, dstPage); - } - realSurface.NeedsUpdate = true; - continue; - } - - if (realSurface.AtlasTile.Width != surface.width || realSurface.AtlasTile.Height != surface.height) - { - errors = true; - if (developer >= 1) - { - Printf("Surface size mismatch: Attempting to remap %dx%d to %dx%d pixel area.\n", surface.width, surface.height, realSurface.AtlasTile.Width, realSurface.AtlasTile.Height); - } - realSurface.NeedsUpdate = true; - continue; - } - - if (developer >= 5) - { - Printf("Mapping lightmap surface pixels[%u] (count: %u) -> ((x:%d, y:%d), (x2:%d, y2:%d), page:%d) area: %u\n", - srcPixelOffset, surface.width * surface.height * 3, - dstX, dstY, - dstX + realSurface.AtlasTile.Width, dstY + realSurface.AtlasTile.Height, - dstPage, - realSurface.AtlasTile.Area() * 3); - } - - // copy pixels - uint32_t srcIndex = 0; - uint16_t* src = &textureData[srcPixelOffset]; - - uint16_t* dst = &submesh->LMTextureData[realSurface.AtlasTile.ArrayIndex * textureSize * textureSize * 3]; - - int endY = realSurface.AtlasTile.Y + realSurface.AtlasTile.Height; - int endX = realSurface.AtlasTile.X + realSurface.AtlasTile.Width; - - for (int y = realSurface.AtlasTile.Y; y < endY; ++y) - { - for (int x = realSurface.AtlasTile.X; x < endX; ++x) - { - uint32_t dstIndex = uint32_t(x + (y * textureSize)) * 3; - - dst[dstIndex] = src[srcIndex++]; - dst[dstIndex + 1] = src[srcIndex++]; - dst[dstIndex + 2] = src[srcIndex++]; - } - } - } - - // Use UVs from the lightmap - for (auto& surface : zdraySurfaces) - { - auto& realSurface = *surface.targetSurface; - - auto* vertices = &submesh->Mesh.Vertices[realSurface.MeshLocation.StartVertIndex]; - auto* newUVs = &zdrayUvs[surface.uvOffset]; - - for (uint32_t i = 0; i < surface.uvCount; ++i) - { - FVector2 oldUv = FVector2(vertices[i].lu, vertices[i].lv); - - if (developer >= 5) - { - Printf("Old UV: %.6f %.6f (w:%d, h:%d) (x:%d, y:%d), Lump UVs %.3f %.3f\n", vertices[i].lu, vertices[i].lv, realSurface.AtlasTile.Width, realSurface.AtlasTile.Height, realSurface.AtlasTile.X, realSurface.AtlasTile.Y, newUVs[i].X, newUVs[i].Y); - } - - // Finish surface - vertices[i].lu = (newUVs[i].X + realSurface.AtlasTile.X) / textureSize; - vertices[i].lv = (newUVs[i].Y + realSurface.AtlasTile.Y) / textureSize; - - if (developer >= 5) - { - if (abs(oldUv.X - vertices[i].lu) >= 0.0000001f || abs(oldUv.Y - vertices[i].lv) >= 0.0000001f) - { - Printf("New UV: %.6f %.6f\n", vertices[i].lu, vertices[i].lv); - } - } - } - } - - if (developer >= 3) - { - int loadedSurfaces = 0; - for (auto& surface : submesh->Surfaces) - { - if (!surface.NeedsUpdate) - { - loadedSurfaces++; - } - } - - Printf(PRINT_HIGH, "%d/%d surfaces were successfully loaded from lightmap.\n", loadedSurfaces, submesh->Surfaces.Size()); - } - - if (errors && developer <= 0) - { - Printf(PRINT_HIGH, "Pre-calculated LIGHTMAP surfaces do not match current level surfaces. Restart this level with 'developer 1' for further details.\nPerhaps you forget to rebuild lightmaps after modifying the map?\n"); + Printf(PRINT_HIGH, "Pre-calculated LIGHTMAP surfaces do not match current level surfaces.\nPerhaps you forget to rebuild lightmaps after modifying the map?\n"); } return true; diff --git a/src/rendering/hwrenderer/doom_levelmesh.cpp b/src/rendering/hwrenderer/doom_levelmesh.cpp index 4241c93a2..7ff6d8f9e 100644 --- a/src/rendering/hwrenderer/doom_levelmesh.cpp +++ b/src/rendering/hwrenderer/doom_levelmesh.cpp @@ -43,10 +43,10 @@ ADD_STAT(lightmap) } uint32_t atlasPixelCount = levelMesh->StaticMesh->AtlasPixelCount(); - auto stats = levelMesh->GatherSurfacePixelStats(); + auto stats = levelMesh->GatherTilePixelStats(); - 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, std::max(stats.surfaces.dirty - stats.surfaces.sky, (uint32_t)0), + out.Format("Surfaces: %u (awaiting updates: %u)\nSurface pixel area to update: %u\nSurface pixel area: %u\nAtlas pixel area: %u\nAtlas efficiency: %.4f%%", + stats.tiles.total, stats.tiles.dirty, stats.pixels.dirty, stats.pixels.total, atlasPixelCount, @@ -67,13 +67,13 @@ CCMD(invalidatelightmap) if (!RequireLightmap()) return; int count = 0; - for (auto& surface : static_cast(level.levelMesh->StaticMesh.get())->Surfaces) + for (auto& tile : level.levelMesh->StaticMesh->LightmapTiles) { - if (!surface.NeedsUpdate) + if (!tile.NeedsUpdate) ++count; - surface.NeedsUpdate = true; + tile.NeedsUpdate = true; } - Printf("Marked %d out of %d surfaces for update.\n", count, level.levelMesh->StaticMesh->GetSurfaceCount()); + Printf("Marked %d out of %d tiles for update.\n", count, level.levelMesh->StaticMesh->LightmapTiles.Size()); } void PrintSurfaceInfo(const DoomLevelMeshSurface* surface) @@ -83,10 +83,14 @@ void PrintSurfaceInfo(const DoomLevelMeshSurface* surface) auto gameTexture = surface->Texture; Printf("Surface %d (%p)\n Type: %d, TypeIndex: %d, ControlSector: %d\n", surface->Submesh->GetSurfaceIndex(surface), surface, surface->Type, surface->TypeIndex, surface->ControlSector ? surface->ControlSector->Index() : -1); - Printf(" Atlas page: %d, x:%d, y:%d\n", surface->AtlasTile.ArrayIndex, surface->AtlasTile.X, surface->AtlasTile.Y); - Printf(" Pixels: %dx%d (area: %d)\n", surface->AtlasTile.Width, surface->AtlasTile.Height, surface->AtlasTile.Area()); - Printf(" Sample dimension: %d\n", surface->SampleDimension); - Printf(" Needs update?: %d\n", surface->NeedsUpdate); + if (surface->LightmapTileIndex >= 0) + { + LightmapTile* tile = &surface->Submesh->LightmapTiles[surface->LightmapTileIndex]; + Printf(" Atlas page: %d, x:%d, y:%d\n", tile->AtlasLocation.ArrayIndex, tile->AtlasLocation.X, tile->AtlasLocation.Y); + Printf(" Pixels: %dx%d (area: %d)\n", tile->AtlasLocation.Width, tile->AtlasLocation.Height, tile->AtlasLocation.Area()); + Printf(" Sample dimension: %d\n", tile->SampleDimension); + Printf(" Needs update?: %d\n", tile->NeedsUpdate); + } Printf(" Always update?: %d\n", surface->AlwaysUpdate); Printf(" Sector group: %d\n", surface->SectorGroup); Printf(" Texture: '%s'\n", gameTexture ? gameTexture->GetName().GetChars() : ""); @@ -160,11 +164,6 @@ bool DoomLevelMesh::TraceSky(const FVector3& start, FVector3 direction, float di return surface && surface->IsSky; } -void DoomLevelMesh::PackLightmapAtlas() -{ - static_cast(StaticMesh.get())->PackLightmapAtlas(0); -} - int DoomLevelMesh::AddSurfaceLights(const LevelMeshSurface* surface, LevelMeshLight* list, int listMaxSize) { FLightNode* node = GetSurfaceLightNode(static_cast(surface)); @@ -317,11 +316,16 @@ void DoomLevelMesh::DumpMesh(const FString& objFilename, const FString& mtlFilen { const auto& surface = submesh->Surfaces[index]; fprintf(f, "o Surface[%d] %s %d%s\n", index, name(surface.Type), surface.TypeIndex, surface.IsSky ? " sky" : ""); - fprintf(f, "usemtl lightmap%d\n", surface.AtlasTile.ArrayIndex); - if (surface.AtlasTile.ArrayIndex > highestUsedAtlasPage) + if (surface.LightmapTileIndex >= 0) { - highestUsedAtlasPage = surface.AtlasTile.ArrayIndex; + auto& tile = submesh->LightmapTiles[surface.LightmapTileIndex]; + fprintf(f, "usemtl lightmap%d\n", tile.AtlasLocation.ArrayIndex); + + if (tile.AtlasLocation.ArrayIndex > highestUsedAtlasPage) + { + highestUsedAtlasPage = tile.AtlasLocation.ArrayIndex; + } } } } diff --git a/src/rendering/hwrenderer/doom_levelmesh.h b/src/rendering/hwrenderer/doom_levelmesh.h index 4a1c29141..0aa48ac98 100644 --- a/src/rendering/hwrenderer/doom_levelmesh.h +++ b/src/rendering/hwrenderer/doom_levelmesh.h @@ -13,7 +13,6 @@ public: void BeginFrame(FLevelLocals& doomMap); bool TraceSky(const FVector3& start, FVector3 direction, float dist); - void PackLightmapAtlas(); void DumpMesh(const FString& objFilename, const FString& mtlFilename) const; void BuildSectorGroups(const FLevelLocals& doomMap); diff --git a/src/rendering/hwrenderer/doom_levelsubmesh.cpp b/src/rendering/hwrenderer/doom_levelsubmesh.cpp index 9685224d4..3a83b1f2a 100644 --- a/src/rendering/hwrenderer/doom_levelsubmesh.cpp +++ b/src/rendering/hwrenderer/doom_levelsubmesh.cpp @@ -32,9 +32,9 @@ DoomLevelSubmesh::DoomLevelSubmesh(DoomLevelMesh* mesh, FLevelLocals& doomMap, b LinkSurfaces(doomMap); CreateIndexes(); - SetupLightmapUvs(doomMap); BuildTileSurfaceLists(); UpdateCollision(); + PackLightmapAtlas(0); } } @@ -48,7 +48,6 @@ void DoomLevelSubmesh::Update(FLevelLocals& doomMap, int lightmapStartIndex) LinkSurfaces(doomMap); CreateIndexes(); - SetupLightmapUvs(doomMap); BuildTileSurfaceLists(); UpdateCollision(); @@ -86,6 +85,7 @@ void DoomLevelSubmesh::CreateStaticSurfaces(FLevelLocals& doomMap) } MeshBuilder state; + std::map bindings; // Create surface objects for all sides for (unsigned int i = 0; i < doomMap.sides.Size(); i++) @@ -115,19 +115,19 @@ void DoomLevelSubmesh::CreateStaticSurfaces(FLevelLocals& doomMap) state.EnableTexture(true); state.EnableBrightmap(true); state.AlphaFunc(Alpha_GEqual, 0.f); - CreateWallSurface(side, disp, state, result.list, false, true); + CreateWallSurface(side, disp, state, bindings, result.list, false, true); for (HWWall& portal : result.portals) { Portals.Push(portal); } - CreateWallSurface(side, disp, state, result.portals, true, false); + CreateWallSurface(side, disp, state, bindings, result.portals, true, false); // final pass: translucent stuff state.AlphaFunc(Alpha_GEqual, gl_mask_sprite_threshold); state.SetRenderStyle(STYLE_Translucent); - CreateWallSurface(side, disp, state, result.translucent, false, true); + CreateWallSurface(side, disp, state, bindings, result.translucent, false, true); state.AlphaFunc(Alpha_GEqual, 0.f); state.SetRenderStyle(STYLE_Normal); } @@ -154,24 +154,29 @@ void DoomLevelSubmesh::CreateStaticSurfaces(FLevelLocals& doomMap) state.ClearDepthBias(); state.EnableTexture(true); state.EnableBrightmap(true); - CreateFlatSurface(disp, state, result.list); + CreateFlatSurface(disp, state, bindings, result.list); - CreateFlatSurface(disp, state, result.portals, true); + CreateFlatSurface(disp, state, bindings, result.portals, true); // final pass: translucent stuff state.AlphaFunc(Alpha_GEqual, gl_mask_sprite_threshold); state.SetRenderStyle(STYLE_Translucent); - CreateFlatSurface(disp, state, result.translucentborder); + CreateFlatSurface(disp, state, bindings, result.translucentborder); state.SetDepthMask(false); - CreateFlatSurface(disp, state, result.translucent); + CreateFlatSurface(disp, state, bindings, result.translucent); state.AlphaFunc(Alpha_GEqual, 0.f); state.SetDepthMask(true); state.SetRenderStyle(STYLE_Normal); } } + + for (auto& tile : LightmapTiles) + { + SetupTileTransform(LMTextureSize, LMTextureSize, tile); + } } -void DoomLevelSubmesh::CreateWallSurface(side_t* side, HWWallDispatcher& disp, MeshBuilder& state, TArray& list, bool isSky, bool translucent) +void DoomLevelSubmesh::CreateWallSurface(side_t* side, HWWallDispatcher& disp, MeshBuilder& state, std::map& bindings, TArray& list, bool isSky, bool translucent) { for (HWWall& wallpart : list) { @@ -254,11 +259,74 @@ void DoomLevelSubmesh::CreateWallSurface(side_t* side, HWWallDispatcher& disp, M surf.PipelineID = pipelineID; surf.PortalIndex = isSky ? LevelMesh->linePortals[side->linedef->Index()] : 0; surf.IsSky = isSky; + surf.Bounds = GetBoundsFromSurface(surf); + surf.LightmapTileIndex = AddSurfaceToTile(surf, bindings); Surfaces.Push(surf); } } -void DoomLevelSubmesh::CreateFlatSurface(HWFlatDispatcher& disp, MeshBuilder& state, TArray& list, bool isSky) +int DoomLevelSubmesh::AddSurfaceToTile(const DoomLevelMeshSurface& surf, std::map& bindings) +{ + LightmapTileBinding binding; + binding.Type = surf.Type; + binding.TypeIndex = surf.TypeIndex; + binding.ControlSector = surf.ControlSector ? surf.ControlSector->Index() : (int)0xffffffffUL; + + auto it = bindings.find(binding); + if (it != bindings.end()) + { + int index = it->second; + + LightmapTile& tile = LightmapTiles[index]; + tile.Bounds.min.X = std::min(tile.Bounds.min.X, surf.Bounds.min.X); + tile.Bounds.min.Y = std::min(tile.Bounds.min.Y, surf.Bounds.min.Y); + tile.Bounds.min.Z = std::min(tile.Bounds.min.Z, surf.Bounds.min.Z); + tile.Bounds.max.X = std::max(tile.Bounds.max.X, surf.Bounds.max.X); + tile.Bounds.max.Y = std::max(tile.Bounds.max.Y, surf.Bounds.max.Y); + tile.Bounds.max.Z = std::max(tile.Bounds.max.Z, surf.Bounds.max.Z); + + return index; + } + else + { + int index = LightmapTiles.Size(); + + LightmapTile tile; + tile.Binding = binding; + tile.Bounds = surf.Bounds; + tile.Plane = surf.Plane; + tile.SampleDimension = GetSampleDimension(surf); + + LightmapTiles.Push(tile); + bindings[binding] = index; + return index; + } +} + +int DoomLevelSubmesh::GetSampleDimension(const DoomLevelMeshSurface& surf) +{ + uint16_t sampleDimension = 0; // To do: something seems to have gone missing with the sample dimension! + + if (sampleDimension <= 0) + { + sampleDimension = LightmapSampleDistance; + } + + sampleDimension = uint16_t(max(int(roundf(float(sampleDimension) / max(1.0f / 4, float(lm_scale)))), 1)); + + // Round to nearest power of two + uint32_t n = uint16_t(sampleDimension); + n |= n >> 1; + n |= n >> 2; + n |= n >> 4; + n |= n >> 8; + n = (n + 1) >> 1; + sampleDimension = uint16_t(n) ? uint16_t(n) : uint16_t(0xFFFF); + + return sampleDimension; +} + +void DoomLevelSubmesh::CreateFlatSurface(HWFlatDispatcher& disp, MeshBuilder& state, std::map& bindings, TArray& list, bool isSky) { for (HWFlat& flatpart : list) { @@ -355,6 +423,8 @@ void DoomLevelSubmesh::CreateFlatSurface(HWFlatDispatcher& disp, MeshBuilder& st surf.Subsector = sub; surf.MeshLocation.StartVertIndex = startVertIndex; surf.MeshLocation.NumVerts = sub->numlines; + surf.Bounds = GetBoundsFromSurface(surf); + surf.LightmapTileIndex = AddSurfaceToTile(surf, bindings); Surfaces.Push(surf); } } @@ -553,54 +623,52 @@ bool DoomLevelSubmesh::IsDegenerate(const FVector3 &v0, const FVector3 &v1, cons return crosslengthsqr <= 1.e-6f; } -void DoomLevelSubmesh::SetupLightmapUvs(FLevelLocals& doomMap) -{ - LMTextureSize = 1024; - - for (auto& surface : Surfaces) - { - SetupTileTransform(LMTextureSize, LMTextureSize, surface); - } -} - void DoomLevelSubmesh::PackLightmapAtlas(int lightmapStartIndex) { - std::vector sortedSurfaces; - sortedSurfaces.reserve(Surfaces.Size()); + std::vector sortedTiles; + sortedTiles.reserve(LightmapTiles.Size()); - for (auto& surface : Surfaces) + for (auto& tile : LightmapTiles) { - sortedSurfaces.push_back(&surface); + sortedTiles.push_back(&tile); } - std::sort(sortedSurfaces.begin(), sortedSurfaces.end(), [](LevelMeshSurface* a, LevelMeshSurface* b) { return a->AtlasTile.Height != b->AtlasTile.Height ? a->AtlasTile.Height > b->AtlasTile.Height : a->AtlasTile.Width > b->AtlasTile.Width; }); + std::sort(sortedTiles.begin(), sortedTiles.end(), [](LightmapTile* a, LightmapTile* b) { return a->AtlasLocation.Height != b->AtlasLocation.Height ? a->AtlasLocation.Height > b->AtlasLocation.Height : a->AtlasLocation.Width > b->AtlasLocation.Width; }); RectPacker packer(LMTextureSize, LMTextureSize, RectPacker::Spacing(0)); - for (LevelMeshSurface* surf : sortedSurfaces) + for (LightmapTile* tile : sortedTiles) { - int sampleWidth = surf->AtlasTile.Width; - int sampleHeight = surf->AtlasTile.Height; + int sampleWidth = tile->AtlasLocation.Width; + int sampleHeight = tile->AtlasLocation.Height; auto result = packer.insert(sampleWidth, sampleHeight); int x = result.pos.x, y = result.pos.y; - surf->AtlasTile.X = x; - surf->AtlasTile.Y = y; - surf->AtlasTile.ArrayIndex = lightmapStartIndex + (int)result.pageIndex; - - // calculate final texture coordinates - for (int i = 0; i < (int)surf->MeshLocation.NumVerts; i++) - { - auto& vertex = Mesh.Vertices[surf->MeshLocation.StartVertIndex + i]; - vertex.lu = (vertex.lu + x) / (float)LMTextureSize; - vertex.lv = (vertex.lv + y) / (float)LMTextureSize; - vertex.lindex = (float)surf->AtlasTile.ArrayIndex; - } + tile->AtlasLocation.X = x; + tile->AtlasLocation.Y = y; + tile->AtlasLocation.ArrayIndex = lightmapStartIndex + (int)result.pageIndex; } LMTextureCount = (int)packer.getNumPages(); + // Calculate final texture coordinates + for (auto& surface : Surfaces) + { + if (surface.LightmapTileIndex >= 0) + { + const LightmapTile& tile = LightmapTiles[surface.LightmapTileIndex]; + for (int i = 0; i < surface.MeshLocation.NumVerts; i++) + { + FVector3 tDelta = Mesh.Vertices[surface.MeshLocation.StartVertIndex + i].fPos() - tile.Transform.TranslateWorldToLocal; + auto& vertex = Mesh.Vertices[surface.MeshLocation.StartVertIndex + i]; + vertex.lu = (tile.AtlasLocation.X + (tDelta | tile.Transform.ProjLocalToU)) / (float)LMTextureSize; + vertex.lv = (tile.AtlasLocation.Y + (tDelta | tile.Transform.ProjLocalToV)) / (float)LMTextureSize; + vertex.lindex = (float)tile.AtlasLocation.ArrayIndex; + } + } + } + #if 0 // Debug atlas tile locations: uint16_t colors[30] = { @@ -617,21 +685,20 @@ void DoomLevelSubmesh::PackLightmapAtlas(int lightmapStartIndex) }; LMTextureData.Resize(LMTextureSize * LMTextureSize * LMTextureCount * 3); uint16_t* pixels = LMTextureData.Data(); - for (DoomLevelMeshSurface& surf : Surfaces) + for (LightmapTile& tile : LightmapTiles) { - surf.AlwaysUpdate = false; - surf.NeedsUpdate = false; + tile.NeedsUpdate = false; - int index = surf.Side ? surf.Side->Index() : (surf.Subsector && surf.Subsector->sector ? surf.Subsector->sector->Index() : 0); + int index = tile.Binding.TypeIndex; uint16_t* color = colors + (index % 10) * 3; - int x = surf.AtlasTile.X; - int y = surf.AtlasTile.Y; - int w = surf.AtlasTile.Width; - int h = surf.AtlasTile.Height; + int x = tile.AtlasLocation.X; + int y = tile.AtlasLocation.Y; + int w = tile.AtlasLocation.Width; + int h = tile.AtlasLocation.Height; for (int yy = y; yy < y + h; yy++) { - uint16_t* line = pixels + surf.AtlasTile.ArrayIndex * LMTextureSize * LMTextureSize + yy * LMTextureSize * 3; + uint16_t* line = pixels + tile.AtlasLocation.ArrayIndex * LMTextureSize * LMTextureSize + yy * LMTextureSize * 3; for (int xx = x; xx < x + w; xx++) { line[xx * 3] = color[0]; @@ -640,35 +707,31 @@ void DoomLevelSubmesh::PackLightmapAtlas(int lightmapStartIndex) } } } + for (DoomLevelMeshSurface& surf : Surfaces) + { + surf.AlwaysUpdate = false; + } #endif } BBox DoomLevelSubmesh::GetBoundsFromSurface(const LevelMeshSurface& surface) const { - constexpr float M_INFINITY = 1e30f; // TODO cleanup - - FVector3 low(M_INFINITY, M_INFINITY, M_INFINITY); - FVector3 hi(-M_INFINITY, -M_INFINITY, -M_INFINITY); - + BBox bounds; + bounds.Clear(); for (int i = int(surface.MeshLocation.StartVertIndex); i < int(surface.MeshLocation.StartVertIndex) + surface.MeshLocation.NumVerts; i++) { for (int j = 0; j < 3; j++) { - if (Mesh.Vertices[i].fPos()[j] < low[j]) + if (Mesh.Vertices[i].fPos()[j] < bounds.min[j]) { - low[j] = Mesh.Vertices[i].fPos()[j]; + bounds.min[j] = Mesh.Vertices[i].fPos()[j]; } - if (Mesh.Vertices[i].fPos()[j] > hi[j]) + if (Mesh.Vertices[i].fPos()[j] > bounds.max[j]) { - hi[j] = Mesh.Vertices[i].fPos()[j]; + bounds.max[j] = Mesh.Vertices[i].fPos()[j]; } } } - - BBox bounds; - bounds.Clear(); - bounds.min = low; - bounds.max = hi; return bounds; } @@ -691,41 +754,22 @@ DoomLevelSubmesh::PlaneAxis DoomLevelSubmesh::BestAxis(const FVector4& p) return AXIS_XY; } -void DoomLevelSubmesh::SetupTileTransform(int lightMapTextureWidth, int lightMapTextureHeight, LevelMeshSurface& surface) +void DoomLevelSubmesh::SetupTileTransform(int lightMapTextureWidth, int lightMapTextureHeight, LightmapTile& tile) { - BBox bounds = GetBoundsFromSurface(surface); - surface.Bounds = bounds; - - if (surface.SampleDimension <= 0) - { - surface.SampleDimension = LightmapSampleDistance; - } - - surface.SampleDimension = uint16_t(max(int(roundf(float(surface.SampleDimension) / max(1.0f / 4, float(lm_scale)))), 1)); - - { - // Round to nearest power of two - uint32_t n = uint16_t(surface.SampleDimension); - n |= n >> 1; - n |= n >> 2; - n |= n >> 4; - n |= n >> 8; - n = (n + 1) >> 1; - surface.SampleDimension = uint16_t(n) ? uint16_t(n) : uint16_t(0xFFFF); - } + BBox bounds = tile.Bounds; // round off dimensions FVector3 roundedSize; for (int i = 0; i < 3; i++) { - bounds.min[i] = surface.SampleDimension * (floor(bounds.min[i] / surface.SampleDimension) - 1); - bounds.max[i] = surface.SampleDimension * (ceil(bounds.max[i] / surface.SampleDimension) + 1); - roundedSize[i] = (bounds.max[i] - bounds.min[i]) / surface.SampleDimension; + bounds.min[i] = tile.SampleDimension * (floor(bounds.min[i] / tile.SampleDimension) - 1); + bounds.max[i] = tile.SampleDimension * (ceil(bounds.max[i] / tile.SampleDimension) + 1); + roundedSize[i] = (bounds.max[i] - bounds.min[i]) / tile.SampleDimension; } FVector3 tCoords[2] = { FVector3(0.0f, 0.0f, 0.0f), FVector3(0.0f, 0.0f, 0.0f) }; - PlaneAxis axis = BestAxis(surface.Plane); + PlaneAxis axis = BestAxis(tile.Plane); int width; int height; @@ -735,22 +779,22 @@ void DoomLevelSubmesh::SetupTileTransform(int lightMapTextureWidth, int lightMap case AXIS_YZ: width = (int)roundedSize.Y; height = (int)roundedSize.Z; - tCoords[0].Y = 1.0f / surface.SampleDimension; - tCoords[1].Z = 1.0f / surface.SampleDimension; + tCoords[0].Y = 1.0f / tile.SampleDimension; + tCoords[1].Z = 1.0f / tile.SampleDimension; break; case AXIS_XZ: width = (int)roundedSize.X; height = (int)roundedSize.Z; - tCoords[0].X = 1.0f / surface.SampleDimension; - tCoords[1].Z = 1.0f / surface.SampleDimension; + tCoords[0].X = 1.0f / tile.SampleDimension; + tCoords[1].Z = 1.0f / tile.SampleDimension; break; case AXIS_XY: width = (int)roundedSize.X; height = (int)roundedSize.Y; - tCoords[0].X = 1.0f / surface.SampleDimension; - tCoords[1].Y = 1.0f / surface.SampleDimension; + tCoords[0].X = 1.0f / tile.SampleDimension; + tCoords[1].Y = 1.0f / tile.SampleDimension; break; } @@ -768,18 +812,10 @@ void DoomLevelSubmesh::SetupTileTransform(int lightMapTextureWidth, int lightMap height = (lightMapTextureHeight - 2); } - surface.TileTransform.TranslateWorldToLocal = bounds.min; - surface.TileTransform.ProjLocalToU = tCoords[0]; - surface.TileTransform.ProjLocalToV = tCoords[1]; + tile.Transform.TranslateWorldToLocal = bounds.min; + tile.Transform.ProjLocalToU = tCoords[0]; + tile.Transform.ProjLocalToV = tCoords[1]; - for (int i = 0; i < surface.MeshLocation.NumVerts; i++) - { - FVector3 tDelta = Mesh.Vertices[surface.MeshLocation.StartVertIndex + i].fPos() - surface.TileTransform.TranslateWorldToLocal; - - Mesh.Vertices[surface.MeshLocation.StartVertIndex + i].lu = (tDelta | surface.TileTransform.ProjLocalToU); - Mesh.Vertices[surface.MeshLocation.StartVertIndex + i].lv = (tDelta | surface.TileTransform.ProjLocalToV); - } - - surface.AtlasTile.Width = width; - surface.AtlasTile.Height = height; + tile.AtlasLocation.Width = width; + tile.AtlasLocation.Height = height; } diff --git a/src/rendering/hwrenderer/doom_levelsubmesh.h b/src/rendering/hwrenderer/doom_levelsubmesh.h index 7491f666d..564d90c50 100644 --- a/src/rendering/hwrenderer/doom_levelsubmesh.h +++ b/src/rendering/hwrenderer/doom_levelsubmesh.h @@ -9,6 +9,7 @@ #include "bounds.h" #include #include +#include typedef dp::rect_pack::RectPacker RectPacker; @@ -60,12 +61,10 @@ private: void SetSubsectorLightmap(DoomLevelMeshSurface* surface); void SetSideLightmap(DoomLevelMeshSurface* surface); - void SetupLightmapUvs(FLevelLocals& doomMap); - void CreateIndexes(); - void CreateWallSurface(side_t* side, HWWallDispatcher& disp, MeshBuilder& state, TArray& list, bool isSky, bool translucent); - void CreateFlatSurface(HWFlatDispatcher& disp, MeshBuilder& state, TArray& list, bool isSky = false); + void CreateWallSurface(side_t* side, HWWallDispatcher& disp, MeshBuilder& state, std::map& bindings, TArray& list, bool isSky, bool translucent); + void CreateFlatSurface(HWFlatDispatcher& disp, MeshBuilder& state, std::map& bindings, TArray& list, bool isSky = false); static FVector4 ToPlane(const FVector3& pt1, const FVector3& pt2, const FVector3& pt3) { @@ -100,7 +99,9 @@ private: static PlaneAxis BestAxis(const FVector4& p); BBox GetBoundsFromSurface(const LevelMeshSurface& surface) const; - void SetupTileTransform(int lightMapTextureWidth, int lightMapTextureHeight, LevelMeshSurface& surface); + void SetupTileTransform(int lightMapTextureWidth, int lightMapTextureHeight, LightmapTile& tile); + int AddSurfaceToTile(const DoomLevelMeshSurface& surf, std::map& bindings); + int GetSampleDimension(const DoomLevelMeshSurface& surf); static bool IsDegenerate(const FVector3& v0, const FVector3& v1, const FVector3& v2); diff --git a/src/rendering/hwrenderer/scene/hw_drawinfo.cpp b/src/rendering/hwrenderer/scene/hw_drawinfo.cpp index 22f91a810..d058fd064 100644 --- a/src/rendering/hwrenderer/scene/hw_drawinfo.cpp +++ b/src/rendering/hwrenderer/scene/hw_drawinfo.cpp @@ -129,7 +129,7 @@ void HWDrawInfo::StartScene(FRenderViewpoint &parentvp, HWViewpointUniforms *uni hudsprites.Clear(); Coronas.Clear(); Fogballs.Clear(); - VisibleSurfaces.Clear(); + VisibleTiles.Clear(); vpIndex = 0; // Fullbright information needs to be propagated from the main view. @@ -620,20 +620,20 @@ void HWDrawInfo::PutWallPortal(HWWall wall, FRenderState& state) void HWDrawInfo::UpdateLightmaps() { - if (!outer && VisibleSurfaces.Size() < unsigned(lm_background_updates)) + if (!outer && VisibleTiles.Size() < unsigned(lm_background_updates)) { - for (auto& e : static_cast(level.levelMesh->StaticMesh.get())->Surfaces) + for (auto& e : level.levelMesh->StaticMesh->LightmapTiles) { - if (e.NeedsUpdate && !e.IsSky && !e.PortalIndex) + if (e.NeedsUpdate) { - VisibleSurfaces.Push(&e); + VisibleTiles.Push(&e); - if (VisibleSurfaces.Size() >= unsigned(lm_background_updates)) + if (VisibleTiles.Size() >= unsigned(lm_background_updates)) break; } } } - screen->UpdateLightmaps(VisibleSurfaces); + screen->UpdateLightmaps(VisibleTiles); } //----------------------------------------------------------------------------- diff --git a/src/rendering/hwrenderer/scene/hw_drawinfo.h b/src/rendering/hwrenderer/scene/hw_drawinfo.h index ccd20b523..b455d847c 100644 --- a/src/rendering/hwrenderer/scene/hw_drawinfo.h +++ b/src/rendering/hwrenderer/scene/hw_drawinfo.h @@ -153,7 +153,7 @@ struct HWDrawInfo TArray hudsprites; // These may just be stored by value. TArray Coronas; TArray Fogballs; - TArray VisibleSurfaces; + TArray VisibleTiles; uint64_t LastFrameTime = 0; TArray MissingUpperTextures; @@ -232,18 +232,22 @@ public: return; } + if (surface->LightmapTileIndex < 0) + return; + + LightmapTile* tile = &surface->Submesh->LightmapTiles[surface->LightmapTileIndex]; if (lm_always_update || surface->AlwaysUpdate) { - surface->NeedsUpdate = true; + tile->NeedsUpdate = true; } - else if (VisibleSurfaces.Size() >= unsigned(lm_max_updates)) + else if (VisibleTiles.Size() >= unsigned(lm_max_updates)) { return; } - if (surface->NeedsUpdate && !surface->PortalIndex && !surface->IsSky) + if (tile->NeedsUpdate) { - VisibleSurfaces.Push(surface); + VisibleTiles.Push(tile); } } diff --git a/wadsrc/static/shaders/lightmap/binding_lightmapper.glsl b/wadsrc/static/shaders/lightmap/binding_lightmapper.glsl index b7b689b83..591973a3c 100644 --- a/wadsrc/static/shaders/lightmap/binding_lightmapper.glsl +++ b/wadsrc/static/shaders/lightmap/binding_lightmapper.glsl @@ -11,10 +11,10 @@ struct SurfaceInfo { vec3 Normal; float Sky; - float SamplingDistance; uint PortalIndex; int TextureIndex; float Alpha; + float Padding; }; struct PortalInfo