Split level mesh surface from lightmap tiles as we have multiple surfaces per tile
This commit is contained in:
parent
421b8532ff
commit
48b6a7ec46
19 changed files with 446 additions and 597 deletions
|
|
@ -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<TriangleMeshShape>(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<LevelMeshSurface*> surfaces;
|
||||
};
|
||||
|
||||
void LevelSubmesh::BuildTileSurfaceLists()
|
||||
{
|
||||
// Smoothing group surface is to be rendered with
|
||||
TArray<LevelMeshSmoothingGroup> SmoothingGroups;
|
||||
TArray<int> SmoothingGroupIndexes(GetSurfaceCount());
|
||||
// Plane group surface is to be rendered with
|
||||
TArray<LevelMeshPlaneGroup> PlaneGroups;
|
||||
TArray<int> 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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 <memory>
|
||||
|
||||
struct LevelMeshSurfaceStats;
|
||||
struct LevelMeshTileStats;
|
||||
|
||||
struct LevelSubmeshDrawRange
|
||||
{
|
||||
|
|
@ -48,19 +49,21 @@ public:
|
|||
|
||||
// Lightmap atlas
|
||||
int LMTextureCount = 0;
|
||||
int LMTextureSize = 0;
|
||||
int LMTextureSize = 1024;
|
||||
TArray<uint16_t> LMTextureData;
|
||||
|
||||
uint16_t LightmapSampleDistance = 16;
|
||||
|
||||
TArray<LightmapTile> 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<LevelMeshPortal> 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;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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<LevelMeshSurface*> 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<LevelMeshSurface*> surfaces;
|
||||
};
|
||||
|
|
|
|||
64
src/common/rendering/hwrenderer/data/hw_lightmaptile.h
Normal file
64
src/common/rendering/hwrenderer/data/hw_lightmaptile.h
Normal file
|
|
@ -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<LevelMeshSurface*> 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);
|
||||
}
|
||||
};
|
||||
|
|
@ -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<LevelMeshSurface*>& surfaces) {}
|
||||
virtual void UpdateLightmaps(const TArray<LightmapTile*>& tiles) {}
|
||||
|
||||
virtual DCanvas* GetCanvas() { return nullptr; }
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -31,10 +31,10 @@ struct SurfaceInfo
|
|||
{
|
||||
FVector3 Normal;
|
||||
float Sky;
|
||||
float SamplingDistance;
|
||||
uint32_t PortalIndex;
|
||||
int32_t TextureIndex;
|
||||
float Alpha;
|
||||
float Padding;
|
||||
};
|
||||
|
||||
struct PortalInfo
|
||||
|
|
|
|||
|
|
@ -92,16 +92,16 @@ void VkLightmapper::BeginFrame()
|
|||
drawindexed.Pos = 0;
|
||||
}
|
||||
|
||||
void VkLightmapper::Raytrace(const TArray<LevelMeshSurface*>& surfaces)
|
||||
void VkLightmapper::Raytrace(const TArray<LightmapTile*>& 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<LevelMeshSurface*>& surfaces)
|
|||
}
|
||||
}
|
||||
|
||||
void VkLightmapper::SelectSurfaces(const TArray<LevelMeshSurface*>& surfaces)
|
||||
void VkLightmapper::SelectTiles(const TArray<LightmapTile*>& 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<uint16_t>(bakeImage.maxX, uint16_t(selected.X + surface->AtlasTile.Width + spacing));
|
||||
bakeImage.maxY = std::max<uint16_t>(bakeImage.maxY, uint16_t(selected.Y + surface->AtlasTile.Height + spacing));
|
||||
bakeImage.maxX = std::max<uint16_t>(bakeImage.maxX, uint16_t(selected.X + tile->AtlasLocation.Width + spacing));
|
||||
bakeImage.maxY = std::max<uint16_t>(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.
|
||||
|
|
|
|||
|
|
@ -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<LevelMeshSurface*>& surfaces);
|
||||
void Raytrace(const TArray<LightmapTile*>& surfaces);
|
||||
void SetLevelMesh(LevelMesh* level);
|
||||
|
||||
private:
|
||||
void ReleaseResources();
|
||||
|
||||
void SelectSurfaces(const TArray<LevelMeshSurface*>& surfaces);
|
||||
void SelectTiles(const TArray<LightmapTile*>& surfaces);
|
||||
void UploadUniforms();
|
||||
void Render();
|
||||
void Resolve();
|
||||
|
|
@ -165,8 +165,8 @@ private:
|
|||
|
||||
bool useRayQuery = true;
|
||||
|
||||
TArray<SelectedSurface> selectedSurfaces;
|
||||
TArray<TArray<SelectedSurface*>> copylists;
|
||||
TArray<SelectedTile> selectedTiles;
|
||||
TArray<TArray<SelectedTile*>> copylists;
|
||||
TArray<LevelMeshLight> templightlist;
|
||||
|
||||
struct
|
||||
|
|
|
|||
|
|
@ -553,9 +553,9 @@ void VulkanRenderDevice::SetLevelMesh(LevelMesh* mesh)
|
|||
levelMeshChanged = true;
|
||||
}
|
||||
|
||||
void VulkanRenderDevice::UpdateLightmaps(const TArray<LevelMeshSurface*>& surfaces)
|
||||
void VulkanRenderDevice::UpdateLightmaps(const TArray<LightmapTile*>& tiles)
|
||||
{
|
||||
GetLightmapper()->Raytrace(surfaces);
|
||||
GetLightmapper()->Raytrace(tiles);
|
||||
}
|
||||
|
||||
void VulkanRenderDevice::SetShadowMaps(const TArray<float>& lights, hwrenderer::LevelAABBTree* tree, bool newTree)
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ public:
|
|||
void AmbientOccludeScene(float m5) override;
|
||||
void SetSceneRenderTarget(bool useSSAO) override;
|
||||
void SetLevelMesh(LevelMesh* mesh) override;
|
||||
void UpdateLightmaps(const TArray<LevelMeshSurface*>& surfaces) override;
|
||||
void UpdateLightmaps(const TArray<LightmapTile*>& tiles) override;
|
||||
void SetShadowMaps(const TArray<float>& lights, hwrenderer::LevelAABBTree* tree, bool newTree) override;
|
||||
void SetSaveBuffers(bool yes) override;
|
||||
void ImageTransitionScene(bool unknown) override;
|
||||
|
|
|
|||
|
|
@ -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<int, TArray<DoomLevelMeshSurface*>> 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<DoomLevelMeshSurface*, int> detectedSurfaces;
|
||||
TArray<SurfaceEntry> zdraySurfaces;
|
||||
zdraySurfaces.Reserve(numSurfaces);
|
||||
|
||||
auto submesh = static_cast<DoomLevelSubmesh*>(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<TileEntry> 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<FVector2> zdrayUvs;
|
||||
zdrayUvs.Resize(numTexCoords);
|
||||
fr.Read(&zdrayUvs[0], numTexCoords * 2 * sizeof(float));
|
||||
// Load texture coordinates
|
||||
// TArray<FVector2> 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<LightmapTileBinding, LightmapTile*> 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;
|
||||
|
|
|
|||
|
|
@ -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<DoomLevelSubmesh*>(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() : "<nullptr>");
|
||||
|
|
@ -160,11 +164,6 @@ bool DoomLevelMesh::TraceSky(const FVector3& start, FVector3 direction, float di
|
|||
return surface && surface->IsSky;
|
||||
}
|
||||
|
||||
void DoomLevelMesh::PackLightmapAtlas()
|
||||
{
|
||||
static_cast<DoomLevelSubmesh*>(StaticMesh.get())->PackLightmapAtlas(0);
|
||||
}
|
||||
|
||||
int DoomLevelMesh::AddSurfaceLights(const LevelMeshSurface* surface, LevelMeshLight* list, int listMaxSize)
|
||||
{
|
||||
FLightNode* node = GetSurfaceLightNode(static_cast<const DoomLevelMeshSurface*>(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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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<LightmapTileBinding, int> 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<HWWall>& list, bool isSky, bool translucent)
|
||||
void DoomLevelSubmesh::CreateWallSurface(side_t* side, HWWallDispatcher& disp, MeshBuilder& state, std::map<LightmapTileBinding, int>& bindings, TArray<HWWall>& 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<HWFlat>& list, bool isSky)
|
||||
int DoomLevelSubmesh::AddSurfaceToTile(const DoomLevelMeshSurface& surf, std::map<LightmapTileBinding, int>& 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<LightmapTileBinding, int>& bindings, TArray<HWFlat>& 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<LevelMeshSurface*> sortedSurfaces;
|
||||
sortedSurfaces.reserve(Surfaces.Size());
|
||||
std::vector<LightmapTile*> 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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
#include "bounds.h"
|
||||
#include <dp_rect_pack.h>
|
||||
#include <set>
|
||||
#include <map>
|
||||
|
||||
typedef dp::rect_pack::RectPacker<int> 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<HWWall>& list, bool isSky, bool translucent);
|
||||
void CreateFlatSurface(HWFlatDispatcher& disp, MeshBuilder& state, TArray<HWFlat>& list, bool isSky = false);
|
||||
void CreateWallSurface(side_t* side, HWWallDispatcher& disp, MeshBuilder& state, std::map<LightmapTileBinding, int>& bindings, TArray<HWWall>& list, bool isSky, bool translucent);
|
||||
void CreateFlatSurface(HWFlatDispatcher& disp, MeshBuilder& state, std::map<LightmapTileBinding, int>& bindings, TArray<HWFlat>& 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<LightmapTileBinding, int>& bindings);
|
||||
int GetSampleDimension(const DoomLevelMeshSurface& surf);
|
||||
|
||||
static bool IsDegenerate(const FVector3& v0, const FVector3& v1, const FVector3& v2);
|
||||
|
||||
|
|
|
|||
|
|
@ -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<DoomLevelSubmesh*>(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);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -153,7 +153,7 @@ struct HWDrawInfo
|
|||
TArray<HUDSprite> hudsprites; // These may just be stored by value.
|
||||
TArray<AActor*> Coronas;
|
||||
TArray<Fogball> Fogballs;
|
||||
TArray<LevelMeshSurface*> VisibleSurfaces;
|
||||
TArray<LightmapTile*> VisibleTiles;
|
||||
uint64_t LastFrameTime = 0;
|
||||
|
||||
TArray<MissingTextureInfo> 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,10 +11,10 @@ struct SurfaceInfo
|
|||
{
|
||||
vec3 Normal;
|
||||
float Sky;
|
||||
float SamplingDistance;
|
||||
uint PortalIndex;
|
||||
int TextureIndex;
|
||||
float Alpha;
|
||||
float Padding;
|
||||
};
|
||||
|
||||
struct PortalInfo
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue