Remove the submesh class as that caused more problems than it solved

This commit is contained in:
Magnus Norddahl 2024-01-29 21:56:42 +01:00
commit 7bc2ed5436
20 changed files with 836 additions and 994 deletions

View file

@ -893,7 +893,6 @@ set (PCH_SOURCES
rendering/hwrenderer/hw_vertexbuilder.cpp
rendering/hwrenderer/doom_aabbtree.cpp
rendering/hwrenderer/doom_levelmesh.cpp
rendering/hwrenderer/doom_levelsubmesh.cpp
rendering/hwrenderer/hw_models.cpp
rendering/hwrenderer/hw_precache.cpp
rendering/hwrenderer/scene/hw_lighting.cpp

View file

@ -7,6 +7,31 @@ LevelMesh::LevelMesh()
// Default portal
LevelMeshPortal portal;
Portals.Push(portal);
AddEmptyMesh();
UpdateCollision();
}
void LevelMesh::AddEmptyMesh()
{
// Default empty mesh (we can't make it completely empty since vulkan doesn't like that)
float minval = -100001.0f;
float maxval = -100000.0f;
Mesh.Vertices.Push({ minval, minval, minval });
Mesh.Vertices.Push({ maxval, minval, minval });
Mesh.Vertices.Push({ maxval, maxval, minval });
Mesh.Vertices.Push({ minval, minval, minval });
Mesh.Vertices.Push({ minval, maxval, minval });
Mesh.Vertices.Push({ maxval, maxval, minval });
Mesh.Vertices.Push({ minval, minval, maxval });
Mesh.Vertices.Push({ maxval, minval, maxval });
Mesh.Vertices.Push({ maxval, maxval, maxval });
Mesh.Vertices.Push({ minval, minval, maxval });
Mesh.Vertices.Push({ minval, maxval, maxval });
Mesh.Vertices.Push({ maxval, maxval, maxval });
for (int i = 0; i < 3 * 4; i++)
Mesh.Indexes.Push(i);
}
LevelMeshSurface* LevelMesh::Trace(const FVector3& start, FVector3 direction, float maxDist)
@ -21,18 +46,14 @@ LevelMeshSurface* LevelMesh::Trace(const FVector3& start, FVector3 direction, fl
{
FVector3 end = origin + direction * maxDist;
TraceHit hit0 = TriangleMeshShape::find_first_hit(StaticMesh->Collision.get(), origin, end);
TraceHit hit1 = TriangleMeshShape::find_first_hit(DynamicMesh->Collision.get(), origin, end);
LevelSubmesh* hitmesh = hit0.fraction < hit1.fraction ? StaticMesh.get() : DynamicMesh.get();
TraceHit hit = hit0.fraction < hit1.fraction ? hit0 : hit1;
TraceHit hit = TriangleMeshShape::find_first_hit(Collision.get(), origin, end);
if (hit.triangle < 0)
{
return nullptr;
}
hitSurface = hitmesh->GetSurface(hitmesh->Mesh.SurfaceIndexes[hit.triangle]);
hitSurface = GetSurface(Mesh.SurfaceIndexes[hit.triangle]);
int portal = hitSurface->PortalIndex;
if (!portal)
@ -59,44 +80,6 @@ LevelMeshSurface* LevelMesh::Trace(const FVector3& start, FVector3 direction, fl
LevelMeshTileStats LevelMesh::GatherTilePixelStats()
{
LevelMeshTileStats stats;
StaticMesh->GatherTilePixelStats(stats);
DynamicMesh->GatherTilePixelStats(stats);
return stats;
}
/////////////////////////////////////////////////////////////////////////////
LevelSubmesh::LevelSubmesh()
{
// Default empty mesh (we can't make it completely empty since vulkan doesn't like that)
float minval = -100001.0f;
float maxval = -100000.0f;
Mesh.Vertices.Push({ minval, minval, minval });
Mesh.Vertices.Push({ maxval, minval, minval });
Mesh.Vertices.Push({ maxval, maxval, minval });
Mesh.Vertices.Push({ minval, minval, minval });
Mesh.Vertices.Push({ minval, maxval, minval });
Mesh.Vertices.Push({ maxval, maxval, minval });
Mesh.Vertices.Push({ minval, minval, maxval });
Mesh.Vertices.Push({ maxval, minval, maxval });
Mesh.Vertices.Push({ maxval, maxval, maxval });
Mesh.Vertices.Push({ minval, minval, maxval });
Mesh.Vertices.Push({ minval, maxval, maxval });
Mesh.Vertices.Push({ maxval, maxval, maxval });
for (int i = 0; i < 3 * 4; i++)
Mesh.Indexes.Push(i);
UpdateCollision();
}
void LevelSubmesh::UpdateCollision()
{
Collision = std::make_unique<TriangleMeshShape>(Mesh.Vertices.Data(), Mesh.Vertices.Size(), Mesh.Indexes.Data(), Mesh.Indexes.Size());
}
void LevelSubmesh::GatherTilePixelStats(LevelMeshTileStats& stats)
{
int count = GetSurfaceCount();
for (const LightmapTile& tile : LightmapTiles)
{
@ -111,6 +94,12 @@ void LevelSubmesh::GatherTilePixelStats(LevelMeshTileStats& stats)
}
}
stats.tiles.total += LightmapTiles.Size();
return stats;
}
void LevelMesh::UpdateCollision()
{
Collision = std::make_unique<TriangleMeshShape>(Mesh.Vertices.Data(), Mesh.Vertices.Size(), Mesh.Indexes.Data(), Mesh.Indexes.Size());
}
struct LevelMeshPlaneGroup
@ -120,7 +109,7 @@ struct LevelMeshPlaneGroup
std::vector<LevelMeshSurface*> surfaces;
};
void LevelSubmesh::BuildTileSurfaceLists()
void LevelMesh::BuildTileSurfaceLists()
{
// Plane group surface is to be rendered with
TArray<LevelMeshPlaneGroup> PlaneGroups;
@ -184,12 +173,12 @@ void LevelSubmesh::BuildTileSurfaceLists()
if (surface != targetSurface && (maxUV.X < 0.0f || maxUV.Y < 0.0f || minUV.X > 1.0f || minUV.Y > 1.0f))
continue; // Bounding box not visible
tile->Surfaces.Push(surface);
tile->Surfaces.Push(GetSurfaceIndex(surface));
}
}
}
void LevelSubmesh::SetupTileTransforms()
void LevelMesh::SetupTileTransforms()
{
for (auto& tile : LightmapTiles)
{
@ -197,7 +186,7 @@ void LevelSubmesh::SetupTileTransforms()
}
}
void LevelSubmesh::PackLightmapAtlas(int lightmapStartIndex)
void LevelMesh::PackLightmapAtlas(int lightmapStartIndex)
{
std::vector<LightmapTile*> sortedTiles;
sortedTiles.reserve(LightmapTiles.Size());

View file

@ -25,24 +25,49 @@ struct LevelSubmeshDrawRange
int Count;
};
class LevelSubmesh
class LevelMesh
{
public:
LevelSubmesh();
virtual ~LevelSubmesh() = default;
LevelMesh();
virtual ~LevelMesh() = default;
virtual LevelMeshSurface* GetSurface(int index) { return nullptr; }
virtual unsigned int GetSurfaceIndex(const LevelMeshSurface* surface) const { return 0xffffffff; }
virtual int GetSurfaceCount() { return 0; }
virtual int AddSurfaceLights(const LevelMeshSurface* surface, LevelMeshLight* list, int listMaxSize) { return 0; }
LevelMeshSurface* Trace(const FVector3& start, FVector3 direction, float maxDist);
LevelMeshTileStats GatherTilePixelStats();
// Map defaults
FVector3 SunDirection = FVector3(0.0f, 0.0f, -1.0f);
FVector3 SunColor = FVector3(0.0f, 0.0f, 0.0f);
TArray<LevelMeshPortal> Portals;
struct
{
// Vertex data
TArray<FFlatVertex> Vertices;
TArray<uint32_t> Indexes;
TArray<int> SurfaceIndexes;
TArray<int> UniformIndexes;
// Surface info
TArray<SurfaceUniforms> Uniforms;
TArray<FMaterialState> Materials;
// Index data
TArray<uint32_t> Indexes;
TArray<int> SurfaceIndexes;
int DynamicIndexStart = 0;
// Above data must not be resized beyond these limits as that's the size of the GPU buffers)
int MaxVertices = 0;
int MaxIndexes = 0;
int MaxSurfaces = 0;
int MaxUniforms = 0;
int MaxSurfaceIndexes = 0;
int MaxNodes = 0;
} Mesh;
std::unique_ptr<TriangleMeshShape> Collision;
@ -62,32 +87,11 @@ public:
uint32_t AtlasPixelCount() const { return uint32_t(LMTextureCount * LMTextureSize * LMTextureSize); }
void UpdateCollision();
void GatherTilePixelStats(LevelMeshTileStats& stats);
void BuildTileSurfaceLists();
void SetupTileTransforms();
void PackLightmapAtlas(int lightmapStartIndex);
};
class LevelMesh
{
public:
LevelMesh();
virtual ~LevelMesh() = default;
std::unique_ptr<LevelSubmesh> StaticMesh = std::make_unique<LevelSubmesh>();
std::unique_ptr<LevelSubmesh> DynamicMesh = std::make_unique<LevelSubmesh>();
virtual int AddSurfaceLights(const LevelMeshSurface* surface, LevelMeshLight* list, int listMaxSize) { return 0; }
LevelMeshSurface* Trace(const FVector3& start, FVector3 direction, float maxDist);
LevelMeshTileStats GatherTilePixelStats();
// Map defaults
FVector3 SunDirection = FVector3(0.0f, 0.0f, -1.0f);
FVector3 SunColor = FVector3(0.0f, 0.0f, 0.0f);
TArray<LevelMeshPortal> Portals;
void AddEmptyMesh();
};
struct LevelMeshTileStats

View file

@ -7,14 +7,11 @@
#include "textureid.h"
#include "textures.h"
class LevelSubmesh;
class FGameTexture;
struct LevelMeshSurface;
struct LevelMeshSurface
{
LevelSubmesh* Submesh = nullptr;
struct
{
unsigned int StartVertIndex = 0;

View file

@ -45,7 +45,7 @@ struct LightmapTile
LightmapTileBinding Binding;
// Surfaces that are visible within the lightmap tile
TArray<LevelMeshSurface*> Surfaces;
TArray<int> Surfaces;
BBox Bounds;
uint16_t SampleDimension = 0;

View file

@ -92,7 +92,7 @@ void VkLevelMesh::CreateVulkanObjects()
.AddMemory(VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_KHR | VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_ACCELERATION_STRUCTURE_READ_BIT_KHR | VK_ACCESS_SHADER_READ_BIT)
.Execute(fb->GetCommands()->GetTransferCommands(), VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR | VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR);
CreateTopLevelAS();
CreateTopLevelAS(DynamicBLAS.AccelStruct ? 2 : 1);
// Finish building the accel struct before using it from the shaders
PipelineBarrier()
@ -129,7 +129,8 @@ void VkLevelMesh::BeginFrame()
deletelist->Add(std::move(TopLevelAS.TransferBuffer));
deletelist->Add(std::move(TopLevelAS.InstanceBuffer));
DynamicBLAS = CreateBLAS(Mesh->DynamicMesh.get(), true, Mesh->StaticMesh->Mesh.Vertices.Size(), Mesh->StaticMesh->Mesh.Indexes.Size());
if (Mesh->Mesh.DynamicIndexStart < (int)Mesh->Mesh.Indexes.Size())
DynamicBLAS = CreateBLAS(true, Mesh->Mesh.DynamicIndexStart, Mesh->Mesh.Indexes.Size() - Mesh->Mesh.DynamicIndexStart);
CreateTLASInstanceBuffer();
UploadTLASInstanceBuffer();
@ -139,7 +140,7 @@ void VkLevelMesh::BeginFrame()
.AddMemory(VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_KHR | VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_ACCELERATION_STRUCTURE_READ_BIT_KHR | VK_ACCESS_SHADER_READ_BIT)
.Execute(fb->GetCommands()->GetTransferCommands(), VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR | VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR);
UpdateTopLevelAS();
UpdateTopLevelAS(DynamicBLAS.AccelStruct ? 2 : 1);
// Finish building the accel struct before using it from the shaders
PipelineBarrier()
@ -161,36 +162,6 @@ void VkLevelMesh::UploadMeshes(bool dynamicOnly)
uploader.Upload(dynamicOnly);
}
int VkLevelMesh::GetMaxVertexBufferSize()
{
return Mesh->StaticMesh->Mesh.Vertices.Size() + MaxDynamicVertices;
}
int VkLevelMesh::GetMaxIndexBufferSize()
{
return Mesh->StaticMesh->Mesh.Indexes.Size() + MaxDynamicIndexes;
}
int VkLevelMesh::GetMaxNodeBufferSize()
{
return (int)Mesh->StaticMesh->Collision->get_nodes().size() + MaxDynamicNodes + 1; // + 1 for the merge root node
}
int VkLevelMesh::GetMaxSurfaceBufferSize()
{
return Mesh->StaticMesh->GetSurfaceCount() + MaxDynamicSurfaces;
}
int VkLevelMesh::GetMaxUniformsBufferSize()
{
return Mesh->StaticMesh->Mesh.Uniforms.Size() + MaxDynamicUniforms;
}
int VkLevelMesh::GetMaxSurfaceIndexBufferSize()
{
return Mesh->StaticMesh->Mesh.SurfaceIndexes.Size() + MaxDynamicSurfaceIndexes;
}
void VkLevelMesh::CreateBuffers()
{
VertexBuffer = BufferBuilder()
@ -201,7 +172,7 @@ void VkLevelMesh::CreateBuffers()
VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT |
VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR : 0) |
VK_BUFFER_USAGE_STORAGE_BUFFER_BIT)
.Size(GetMaxVertexBufferSize() * sizeof(FFlatVertex))
.Size(Mesh->Mesh.MaxVertices * sizeof(FFlatVertex))
.DebugName("VertexBuffer")
.Create(fb->GetDevice());
@ -209,7 +180,7 @@ void VkLevelMesh::CreateBuffers()
.Usage(
VK_BUFFER_USAGE_VERTEX_BUFFER_BIT |
VK_BUFFER_USAGE_TRANSFER_DST_BIT)
.Size(GetMaxVertexBufferSize() * sizeof(int))
.Size(Mesh->Mesh.MaxVertices * sizeof(int))
.DebugName("UniformIndexes")
.Create(fb->GetDevice());
@ -221,31 +192,31 @@ void VkLevelMesh::CreateBuffers()
VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT |
VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR : 0) |
VK_BUFFER_USAGE_STORAGE_BUFFER_BIT)
.Size((size_t)GetMaxIndexBufferSize() * sizeof(uint32_t))
.Size((size_t)Mesh->Mesh.MaxIndexes * sizeof(uint32_t))
.DebugName("IndexBuffer")
.Create(fb->GetDevice());
NodeBuffer = BufferBuilder()
.Usage(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT)
.Size(sizeof(CollisionNodeBufferHeader) + GetMaxNodeBufferSize() * sizeof(CollisionNode))
.Size(sizeof(CollisionNodeBufferHeader) + Mesh->Mesh.MaxNodes * sizeof(CollisionNode))
.DebugName("NodeBuffer")
.Create(fb->GetDevice());
SurfaceIndexBuffer = BufferBuilder()
.Usage(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT)
.Size(GetMaxSurfaceIndexBufferSize() * sizeof(int))
.Size(Mesh->Mesh.MaxSurfaceIndexes * sizeof(int))
.DebugName("SurfaceBuffer")
.Create(fb->GetDevice());
SurfaceBuffer = BufferBuilder()
.Usage(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT)
.Size(GetMaxSurfaceBufferSize() * sizeof(SurfaceInfo))
.Size(Mesh->Mesh.MaxSurfaces * sizeof(SurfaceInfo))
.DebugName("SurfaceBuffer")
.Create(fb->GetDevice());
UniformsBuffer = BufferBuilder()
.Usage(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT)
.Size(GetMaxUniformsBufferSize() * sizeof(SurfaceUniforms))
.Size(Mesh->Mesh.MaxUniforms * sizeof(SurfaceUniforms))
.DebugName("SurfaceUniformsBuffer")
.Create(fb->GetDevice());
@ -256,7 +227,7 @@ void VkLevelMesh::CreateBuffers()
.Create(fb->GetDevice());
}
VkLevelMesh::BLAS VkLevelMesh::CreateBLAS(LevelSubmesh* submesh, bool preferFastBuild, int vertexOffset, int indexOffset)
VkLevelMesh::BLAS VkLevelMesh::CreateBLAS(bool preferFastBuild, int indexOffset, int indexCount)
{
BLAS blas;
@ -272,7 +243,7 @@ VkLevelMesh::BLAS VkLevelMesh::CreateBLAS(LevelSubmesh* submesh, bool preferFast
accelStructBLDesc.geometry.triangles.vertexStride = sizeof(FFlatVertex);
accelStructBLDesc.geometry.triangles.indexType = VK_INDEX_TYPE_UINT32;
accelStructBLDesc.geometry.triangles.indexData.deviceAddress = IndexBuffer->GetDeviceAddress() + indexOffset * sizeof(uint32_t);
accelStructBLDesc.geometry.triangles.maxVertex = vertexOffset + submesh->Mesh.Vertices.Size() - 1;
accelStructBLDesc.geometry.triangles.maxVertex = Mesh->Mesh.Vertices.Size() - 1;
buildInfo.type = VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR;
buildInfo.flags = preferFastBuild ? VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_BUILD_BIT_KHR : VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_TRACE_BIT_KHR;
@ -280,7 +251,7 @@ VkLevelMesh::BLAS VkLevelMesh::CreateBLAS(LevelSubmesh* submesh, bool preferFast
buildInfo.geometryCount = 1;
buildInfo.ppGeometries = geometries;
uint32_t maxPrimitiveCount = submesh->Mesh.Indexes.Size() / 3;
uint32_t maxPrimitiveCount = indexCount / 3;
VkAccelerationStructureBuildSizesInfoKHR sizeInfo = { VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_SIZES_INFO_KHR };
vkGetAccelerationStructureBuildSizesKHR(fb->GetDevice()->device, VK_ACCELERATION_STRUCTURE_BUILD_TYPE_DEVICE_KHR, &buildInfo, &maxPrimitiveCount, &sizeInfo);
@ -318,12 +289,13 @@ VkLevelMesh::BLAS VkLevelMesh::CreateBLAS(LevelSubmesh* submesh, bool preferFast
void VkLevelMesh::CreateStaticBLAS()
{
StaticBLAS = CreateBLAS(Mesh->StaticMesh.get(), false, 0, 0);
StaticBLAS = CreateBLAS(false, 0, Mesh->Mesh.DynamicIndexStart);
}
void VkLevelMesh::CreateDynamicBLAS()
{
DynamicBLAS = CreateBLAS(Mesh->DynamicMesh.get(), true, Mesh->StaticMesh->Mesh.Vertices.Size(), Mesh->StaticMesh->Mesh.Indexes.Size());
if (Mesh->Mesh.DynamicIndexStart < (int)Mesh->Mesh.Indexes.Size())
DynamicBLAS = CreateBLAS(true, Mesh->Mesh.DynamicIndexStart, Mesh->Mesh.Indexes.Size() - Mesh->Mesh.DynamicIndexStart);
}
void VkLevelMesh::CreateTLASInstanceBuffer()
@ -341,7 +313,7 @@ void VkLevelMesh::CreateTLASInstanceBuffer()
.Create(fb->GetDevice());
}
void VkLevelMesh::CreateTopLevelAS()
void VkLevelMesh::CreateTopLevelAS(int instanceCount)
{
VkAccelerationStructureBuildGeometryInfoKHR buildInfo = { VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_GEOMETRY_INFO_KHR };
VkAccelerationStructureGeometryKHR accelStructTLDesc = { VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_KHR };
@ -386,12 +358,12 @@ void VkLevelMesh::CreateTopLevelAS()
VkAccelerationStructureBuildRangeInfoKHR rangeInfo = {};
VkAccelerationStructureBuildRangeInfoKHR* rangeInfos[] = { &rangeInfo };
rangeInfo.primitiveCount = 2;
rangeInfo.primitiveCount = instanceCount;
fb->GetCommands()->GetTransferCommands()->buildAccelerationStructures(1, &buildInfo, rangeInfos);
}
void VkLevelMesh::UpdateTopLevelAS()
void VkLevelMesh::UpdateTopLevelAS(int instanceCount)
{
VkAccelerationStructureBuildGeometryInfoKHR buildInfo = { VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_GEOMETRY_INFO_KHR };
VkAccelerationStructureGeometryKHR accelStructTLDesc = { VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_KHR };
@ -412,7 +384,7 @@ void VkLevelMesh::UpdateTopLevelAS()
VkAccelerationStructureBuildRangeInfoKHR rangeInfo = {};
VkAccelerationStructureBuildRangeInfoKHR* rangeInfos[] = { &rangeInfo };
rangeInfo.primitiveCount = 2;
rangeInfo.primitiveCount = instanceCount;
fb->GetCommands()->GetTransferCommands()->buildAccelerationStructures(1, &buildInfo, rangeInfos);
}
@ -427,12 +399,15 @@ void VkLevelMesh::UploadTLASInstanceBuffer()
instances[0].flags = 0;
instances[0].accelerationStructureReference = StaticBLAS.AccelStruct->GetDeviceAddress();
instances[1].transform.matrix[0][0] = 1.0f;
instances[1].transform.matrix[1][1] = 1.0f;
instances[1].transform.matrix[2][2] = 1.0f;
instances[1].mask = 0xff;
instances[1].flags = 0;
instances[1].accelerationStructureReference = DynamicBLAS.AccelStruct->GetDeviceAddress();
if (DynamicBLAS.AccelStruct)
{
instances[1].transform.matrix[0][0] = 1.0f;
instances[1].transform.matrix[1][1] = 1.0f;
instances[1].transform.matrix[2][2] = 1.0f;
instances[1].mask = 0xff;
instances[1].flags = 0;
instances[1].accelerationStructureReference = DynamicBLAS.AccelStruct->GetDeviceAddress();
}
auto data = (uint8_t*)TopLevelAS.TransferBuffer->Map(0, sizeof(VkAccelerationStructureInstanceKHR) * 2);
memcpy(data, instances, sizeof(VkAccelerationStructureInstanceKHR) * 2);
@ -450,7 +425,6 @@ VkLevelMeshUploader::VkLevelMeshUploader(VkLevelMesh* mesh) : Mesh(mesh)
void VkLevelMeshUploader::Upload(bool dynamicOnly)
{
UpdateSizes();
UpdateLocations();
start = dynamicOnly;
end = locations.Size();
@ -501,6 +475,7 @@ static FVector3 SwapYZ(const FVector3& v)
void VkLevelMeshUploader::UploadNodes()
{
#if 0
// Copy node buffer header and create a root node that merges the static and dynamic AABB trees
if (locations[1].Submesh->Collision->get_root() != -1)
{
@ -530,9 +505,10 @@ void VkLevelMeshUploader::UploadNodes()
cmdbuffer->copyBuffer(transferBuffer.get(), Mesh->NodeBuffer.get(), datapos + sizeof(CollisionNodeBufferHeader), sizeof(CollisionNodeBufferHeader) + nodesHeader.root * sizeof(CollisionNode), sizeof(CollisionNode));
}
else // second submesh is empty, just point the header at the first one
#endif
{
CollisionNodeBufferHeader nodesHeader;
nodesHeader.root = locations[0].Submesh->Collision->get_root();
nodesHeader.root = Mesh->Mesh->Collision->get_root();
*((CollisionNodeBufferHeader*)(data + datapos)) = nodesHeader;
cmdbuffer->copyBuffer(transferBuffer.get(), Mesh->NodeBuffer.get(), datapos, 0, sizeof(CollisionNodeBufferHeader));
@ -543,10 +519,9 @@ void VkLevelMeshUploader::UploadNodes()
for (unsigned int i = start; i < end; i++)
{
const SubmeshBufferLocation& cur = locations[i];
auto submesh = cur.Submesh;
CollisionNode* nodes = (CollisionNode*)(data + datapos);
for (auto& node : submesh->Collision->get_nodes())
for (auto& node : Mesh->Mesh->Collision->get_nodes())
{
CollisionNode info;
info.center = SwapYZ(node.aabb.Center);
@ -557,7 +532,7 @@ void VkLevelMeshUploader::UploadNodes()
*(nodes++) = info;
}
size_t copysize = submesh->Collision->get_nodes().size() * sizeof(CollisionNode);
size_t copysize = Mesh->Mesh->Collision->get_nodes().size() * sizeof(CollisionNode);
if (copysize > 0)
cmdbuffer->copyBuffer(transferBuffer.get(), Mesh->NodeBuffer.get(), datapos, sizeof(CollisionNodeBufferHeader) + cur.Node.Offset * sizeof(CollisionNode), copysize);
datapos += copysize;
@ -569,10 +544,9 @@ void VkLevelMeshUploader::UploadVertices()
for (unsigned int i = start; i < end; i++)
{
const SubmeshBufferLocation& cur = locations[i];
auto submesh = cur.Submesh;
size_t copysize = submesh->Mesh.Vertices.Size() * sizeof(FFlatVertex);
memcpy(data + datapos, submesh->Mesh.Vertices.Data(), copysize);
size_t copysize = Mesh->Mesh->Mesh.Vertices.Size() * sizeof(FFlatVertex);
memcpy(data + datapos, Mesh->Mesh->Mesh.Vertices.Data(), copysize);
if (copysize > 0)
cmdbuffer->copyBuffer(transferBuffer.get(), Mesh->VertexBuffer.get(), datapos, cur.Vertex.Offset * sizeof(FFlatVertex), copysize);
datapos += copysize;
@ -584,10 +558,9 @@ void VkLevelMeshUploader::UploadUniformIndexes()
for (unsigned int i = start; i < end; i++)
{
const SubmeshBufferLocation& cur = locations[i];
auto submesh = cur.Submesh;
size_t copysize = submesh->Mesh.UniformIndexes.Size() * sizeof(int);
memcpy(data + datapos, submesh->Mesh.UniformIndexes.Data(), copysize);
size_t copysize = Mesh->Mesh->Mesh.UniformIndexes.Size() * sizeof(int);
memcpy(data + datapos, Mesh->Mesh->Mesh.UniformIndexes.Data(), copysize);
if (copysize > 0)
cmdbuffer->copyBuffer(transferBuffer.get(), Mesh->UniformIndexBuffer.get(), datapos, cur.UniformIndexes.Offset * sizeof(int), copysize);
datapos += copysize;
@ -599,13 +572,12 @@ void VkLevelMeshUploader::UploadIndexes()
for (unsigned int i = start; i < end; i++)
{
const SubmeshBufferLocation& cur = locations[i];
auto submesh = cur.Submesh;
uint32_t* indexes = (uint32_t*)(data + datapos);
for (int j = 0, count = submesh->Mesh.Indexes.Size(); j < count; ++j)
*(indexes++) = cur.Vertex.Offset + submesh->Mesh.Indexes[j];
for (int j = 0, count = Mesh->Mesh->Mesh.Indexes.Size(); j < count; ++j)
*(indexes++) = cur.Vertex.Offset + Mesh->Mesh->Mesh.Indexes[j];
size_t copysize = submesh->Mesh.Indexes.Size() * sizeof(uint32_t);
size_t copysize = Mesh->Mesh->Mesh.Indexes.Size() * sizeof(uint32_t);
if (copysize > 0)
cmdbuffer->copyBuffer(transferBuffer.get(), Mesh->IndexBuffer.get(), datapos, cur.Index.Offset * sizeof(uint32_t), copysize);
datapos += copysize;
@ -617,13 +589,12 @@ void VkLevelMeshUploader::UploadSurfaceIndexes()
for (unsigned int i = start; i < end; i++)
{
const SubmeshBufferLocation& cur = locations[i];
auto submesh = cur.Submesh;
int* indexes = (int*)(data + datapos);
for (int j = 0, count = submesh->Mesh.SurfaceIndexes.Size(); j < count; ++j)
*(indexes++) = cur.SurfaceIndex.Offset + submesh->Mesh.SurfaceIndexes[j];
for (int j = 0, count = Mesh->Mesh->Mesh.SurfaceIndexes.Size(); j < count; ++j)
*(indexes++) = cur.SurfaceIndex.Offset + Mesh->Mesh->Mesh.SurfaceIndexes[j];
size_t copysize = submesh->Mesh.SurfaceIndexes.Size() * sizeof(int);
size_t copysize = Mesh->Mesh->Mesh.SurfaceIndexes.Size() * sizeof(int);
if (copysize > 0)
cmdbuffer->copyBuffer(transferBuffer.get(), Mesh->SurfaceIndexBuffer.get(), datapos, cur.SurfaceIndex.Offset * sizeof(int), copysize);
datapos += copysize;
@ -635,12 +606,11 @@ void VkLevelMeshUploader::UploadSurfaces()
for (unsigned int i = start; i < end; i++)
{
const SubmeshBufferLocation& cur = locations[i];
auto submesh = cur.Submesh;
SurfaceInfo* surfaces = (SurfaceInfo*)(data + datapos);
for (int j = 0, count = submesh->GetSurfaceCount(); j < count; ++j)
for (int j = 0, count = Mesh->Mesh->GetSurfaceCount(); j < count; ++j)
{
LevelMeshSurface* surface = submesh->GetSurface(j);
LevelMeshSurface* surface = Mesh->Mesh->GetSurface(j);
SurfaceInfo info;
info.Normal = FVector3(surface->Plane.X, surface->Plane.Z, surface->Plane.Y);
@ -660,7 +630,7 @@ void VkLevelMeshUploader::UploadSurfaces()
*(surfaces++) = info;
}
size_t copysize = submesh->GetSurfaceCount() * sizeof(SurfaceInfo);
size_t copysize = Mesh->Mesh->GetSurfaceCount() * sizeof(SurfaceInfo);
if (copysize > 0)
cmdbuffer->copyBuffer(transferBuffer.get(), Mesh->SurfaceBuffer.get(), datapos, cur.Surface.Offset * sizeof(SurfaceInfo), copysize);
datapos += copysize;
@ -672,12 +642,11 @@ void VkLevelMeshUploader::UploadUniforms()
for (unsigned int i = start; i < end; i++)
{
const SubmeshBufferLocation& cur = locations[i];
auto submesh = cur.Submesh;
for (int j = 0, count = submesh->Mesh.Uniforms.Size(); j < count; j++)
for (int j = 0, count = Mesh->Mesh->Mesh.Uniforms.Size(); j < count; j++)
{
auto& surfaceUniforms = submesh->Mesh.Uniforms[j];
auto& material = submesh->Mesh.Materials[j];
auto& surfaceUniforms = Mesh->Mesh->Mesh.Uniforms[j];
auto& material = Mesh->Mesh->Mesh.Materials[j];
if (material.mMaterial)
{
auto source = material.mMaterial->Source();
@ -691,8 +660,8 @@ void VkLevelMeshUploader::UploadUniforms()
}
SurfaceUniforms* uniforms = (SurfaceUniforms*)(data + datapos);
size_t copysize = submesh->Mesh.Uniforms.Size() * sizeof(SurfaceUniforms);
memcpy(uniforms, submesh->Mesh.Uniforms.Data(), copysize);
size_t copysize = Mesh->Mesh->Mesh.Uniforms.Size() * sizeof(SurfaceUniforms);
memcpy(uniforms, Mesh->Mesh->Mesh.Uniforms.Data(), copysize);
if (copysize > 0)
cmdbuffer->copyBuffer(transferBuffer.get(), Mesh->UniformsBuffer.get(), datapos, cur.Uniforms.Offset * sizeof(SurfaceUniforms), copysize);
datapos += copysize;
@ -720,47 +689,15 @@ void VkLevelMeshUploader::UploadPortals()
void VkLevelMeshUploader::UpdateSizes()
{
for (LevelSubmesh* submesh : { Mesh->GetMesh()->StaticMesh.get(), Mesh->GetMesh()->DynamicMesh.get() })
{
SubmeshBufferLocation location;
location.Submesh = submesh;
location.Vertex.Size = submesh->Mesh.Vertices.Size();
location.Index.Size = submesh->Mesh.Indexes.Size();
location.Node.Size = (int)submesh->Collision->get_nodes().size();
location.SurfaceIndex.Size = submesh->Mesh.SurfaceIndexes.Size();
location.Surface.Size = submesh->GetSurfaceCount();
location.UniformIndexes.Size = submesh->Mesh.UniformIndexes.Size();
location.Uniforms.Size = submesh->Mesh.Uniforms.Size();
locations.Push(location);
}
}
void VkLevelMeshUploader::UpdateLocations()
{
for (unsigned int i = 1, count = locations.Size(); i < count; i++)
{
const SubmeshBufferLocation& prev = locations[i - 1];
SubmeshBufferLocation& cur = locations[i];
cur.Vertex.Offset = prev.Vertex.Offset + prev.Vertex.Size;
cur.Index.Offset = prev.Index.Offset + prev.Index.Size;
cur.Node.Offset = prev.Node.Offset + prev.Node.Size;
cur.SurfaceIndex.Offset = prev.SurfaceIndex.Offset + prev.SurfaceIndex.Size;
cur.Surface.Offset = prev.Surface.Offset + prev.Surface.Size;
cur.UniformIndexes.Offset = prev.UniformIndexes.Offset + prev.UniformIndexes.Size;
cur.Uniforms.Offset = prev.Uniforms.Offset + prev.Uniforms.Size;
if (
cur.Vertex.Offset + cur.Vertex.Size > Mesh->GetMaxVertexBufferSize() ||
cur.Index.Offset + cur.Index.Size > Mesh->GetMaxIndexBufferSize() ||
cur.Node.Offset + cur.Node.Size > Mesh->GetMaxNodeBufferSize() ||
cur.SurfaceIndex.Offset + cur.SurfaceIndex.Size > Mesh->GetMaxSurfaceIndexBufferSize() ||
cur.Surface.Offset + cur.Surface.Size > Mesh->GetMaxSurfaceBufferSize() ||
cur.UniformIndexes.Offset + cur.UniformIndexes.Size > Mesh->GetMaxVertexBufferSize() ||
cur.Uniforms.Offset + cur.Uniforms.Size > Mesh->GetMaxUniformsBufferSize())
{
I_FatalError("Dynamic accel struct buffers are too small!");
}
}
SubmeshBufferLocation location;
location.Vertex.Size = Mesh->Mesh->Mesh.Vertices.Size();
location.Index.Size = Mesh->Mesh->Mesh.Indexes.Size();
location.Node.Size = (int)Mesh->Mesh->Collision->get_nodes().size();
location.SurfaceIndex.Size = Mesh->Mesh->Mesh.SurfaceIndexes.Size();
location.Surface.Size = Mesh->Mesh->GetSurfaceCount();
location.UniformIndexes.Size = Mesh->Mesh->Mesh.UniformIndexes.Size();
location.Uniforms.Size = Mesh->Mesh->Mesh.Uniforms.Size();
locations.Push(location);
}
size_t VkLevelMeshUploader::GetTransferSize()
@ -770,13 +707,13 @@ size_t VkLevelMeshUploader::GetTransferSize()
for (unsigned int i = start; i < end; i++)
{
const SubmeshBufferLocation& cur = locations[i];
transferBufferSize += cur.Submesh->Mesh.Vertices.Size() * sizeof(FFlatVertex);
transferBufferSize += cur.Submesh->Mesh.UniformIndexes.Size() * sizeof(int);
transferBufferSize += cur.Submesh->Mesh.Indexes.Size() * sizeof(uint32_t);
transferBufferSize += cur.Submesh->Collision->get_nodes().size() * sizeof(CollisionNode);
transferBufferSize += cur.Submesh->Mesh.SurfaceIndexes.Size() * sizeof(int);
transferBufferSize += cur.Submesh->GetSurfaceCount() * sizeof(SurfaceInfo);
transferBufferSize += cur.Submesh->Mesh.Uniforms.Size() * sizeof(SurfaceUniforms);
transferBufferSize += Mesh->Mesh->Mesh.Vertices.Size() * sizeof(FFlatVertex);
transferBufferSize += Mesh->Mesh->Mesh.UniformIndexes.Size() * sizeof(int);
transferBufferSize += Mesh->Mesh->Mesh.Indexes.Size() * sizeof(uint32_t);
transferBufferSize += Mesh->Mesh->Collision->get_nodes().size() * sizeof(CollisionNode);
transferBufferSize += Mesh->Mesh->Mesh.SurfaceIndexes.Size() * sizeof(int);
transferBufferSize += Mesh->Mesh->GetSurfaceCount() * sizeof(SurfaceInfo);
transferBufferSize += Mesh->Mesh->Mesh.Uniforms.Size() * sizeof(SurfaceUniforms);
}
if (start == 0)
transferBufferSize += Mesh->GetMesh()->Portals.Size() * sizeof(PortalInfo);

View file

@ -50,7 +50,6 @@ struct SubmeshBufferRange
struct SubmeshBufferLocation
{
LevelSubmesh* Submesh = nullptr;
SubmeshBufferRange Vertex;
SubmeshBufferRange Index;
SubmeshBufferRange Node;
@ -94,20 +93,13 @@ private:
void CreateStaticBLAS();
void CreateDynamicBLAS();
void CreateTLASInstanceBuffer();
void CreateTopLevelAS();
void CreateTopLevelAS(int instanceCount);
void UploadMeshes(bool dynamicOnly);
void UploadTLASInstanceBuffer();
void UpdateTopLevelAS();
void UpdateTopLevelAS(int instanceCount);
BLAS CreateBLAS(LevelSubmesh *submesh, bool preferFastBuild, int vertexOffset, int indexOffset);
int GetMaxVertexBufferSize();
int GetMaxIndexBufferSize();
int GetMaxNodeBufferSize();
int GetMaxSurfaceBufferSize();
int GetMaxUniformsBufferSize();
int GetMaxSurfaceIndexBufferSize();
BLAS CreateBLAS(bool preferFastBuild, int indexOffset, int indexCount);
VulkanRenderDevice* fb = nullptr;
@ -168,7 +160,6 @@ private:
void UploadUniforms();
void UploadPortals();
void UpdateSizes();
void UpdateLocations();
size_t GetTransferSize();
VkLevelMesh* Mesh;

View file

@ -180,10 +180,6 @@ void VkLightmapper::Render()
viewport.height = (float)bakeImageSize;
cmdbuffer->setViewport(0, 1, &viewport);
int dynamicSurfaceIndexOffset = mesh->StaticMesh->GetSurfaceCount();
int dynamicFirstIndexOffset = mesh->StaticMesh->Mesh.Indexes.Size();
LevelSubmesh* staticMesh = mesh->StaticMesh.get();
for (int i = 0, count = selectedTiles.Size(); i < count; i++)
{
auto& selectedTile = selectedTiles[i];
@ -202,17 +198,10 @@ void VkLightmapper::Render()
bool buffersFull = false;
// Paint all surfaces visible in the tile
for (LevelMeshSurface* surface : targetTile->Surfaces)
for (int surfaceIndex : targetTile->Surfaces)
{
int surfaceIndexOffset = 0;
int firstIndexOffset = 0;
if (surface->Submesh != staticMesh)
{
surfaceIndexOffset = dynamicSurfaceIndexOffset;
firstIndexOffset = dynamicFirstIndexOffset;
}
pc.SurfaceIndex = surfaceIndexOffset + surface->Submesh->GetSurfaceIndex(surface);
LevelMeshSurface* surface = mesh->GetSurface(surfaceIndex);
pc.SurfaceIndex = surfaceIndex;
if (surface->LightList.ResetCounter != lights.ResetCounter)
{
@ -254,7 +243,7 @@ void VkLightmapper::Render()
VkDrawIndexedIndirectCommand cmd;
cmd.indexCount = surface->MeshLocation.NumElements;
cmd.instanceCount = 1;
cmd.firstIndex = firstIndexOffset + surface->MeshLocation.StartElementIndex;
cmd.firstIndex = surface->MeshLocation.StartElementIndex;
cmd.vertexOffset = 0;
cmd.firstInstance = drawindexed.Pos;
drawindexed.Constants[drawindexed.Pos] = pc;

View file

@ -483,9 +483,9 @@ void VulkanRenderDevice::BeginFrame()
levelMeshChanged = false;
mLevelMesh->SetLevelMesh(levelMesh);
if (levelMesh && levelMesh->StaticMesh->LMTextureCount > 0)
if (levelMesh && levelMesh->LMTextureCount > 0)
{
GetTextureManager()->CreateLightmap(levelMesh->StaticMesh->LMTextureSize, levelMesh->StaticMesh->LMTextureCount, std::move(levelMesh->StaticMesh->LMTextureData));
GetTextureManager()->CreateLightmap(levelMesh->LMTextureSize, levelMesh->LMTextureCount, std::move(levelMesh->LMTextureData));
GetLightmapper()->SetLevelMesh(levelMesh);
}
}

View file

@ -856,12 +856,9 @@ void VkRenderState::DrawLevelMeshSurfaces(bool noFragmentShader)
ApplyLevelMesh();
auto mesh = fb->GetLevelMesh()->GetMesh();
for (LevelSubmesh* submesh : { mesh->StaticMesh.get(), mesh->DynamicMesh.get() })
for (LevelSubmeshDrawRange& range : mesh->DrawList)
{
for (LevelSubmeshDrawRange& range : submesh->DrawList)
{
DrawLevelMeshRange(mCommandBuffer, fb->GetLevelMeshPipelineKey(range.PipelineID), range.Start, range.Count, noFragmentShader);
}
DrawLevelMeshRange(mCommandBuffer, fb->GetLevelMeshPipelineKey(range.PipelineID), range.Start, range.Count, noFragmentShader);
}
}
@ -870,12 +867,9 @@ void VkRenderState::DrawLevelMeshPortals(bool noFragmentShader)
ApplyLevelMesh();
auto mesh = fb->GetLevelMesh()->GetMesh();
for (LevelSubmesh* submesh : { mesh->StaticMesh.get(), mesh->DynamicMesh.get() })
for (LevelSubmeshDrawRange& range : mesh->PortalList)
{
for (LevelSubmeshDrawRange& range : submesh->PortalList)
{
DrawLevelMeshRange(mCommandBuffer, fb->GetLevelMeshPipelineKey(range.PipelineID), range.Start, range.Count, noFragmentShader);
}
DrawLevelMeshRange(mCommandBuffer, fb->GetLevelMeshPipelineKey(range.PipelineID), range.Start, range.Count, noFragmentShader);
}
}

View file

@ -3017,8 +3017,8 @@ void MapLoader::InitLevelMesh(MapData* map)
{
if (Level->lightmaps)
{
Level->levelMesh->StaticMesh->SetupTileTransforms();
Level->levelMesh->StaticMesh->PackLightmapAtlas(0);
Level->levelMesh->SetupTileTransforms();
Level->levelMesh->PackLightmapAtlas(0);
}
}
}
@ -3100,12 +3100,11 @@ bool MapLoader::LoadLightmap(MapData* map)
uint8_t* data = (uint8_t*)&textureData[0];
fr.Read(data, numTexPixels * 3 * sizeof(uint16_t));
auto submesh = Level->levelMesh->StaticMesh.get();
const auto textureSize = submesh->LMTextureSize;
const auto textureSize = Level->levelMesh->LMTextureSize;
// Create lookup for finding tiles
std::map<LightmapTileBinding, LightmapTile*> levelTiles;
for (LightmapTile& tile : submesh->LightmapTiles)
for (LightmapTile& tile : Level->levelMesh->LightmapTiles)
{
levelTiles[tile.Binding] = &tile;
}
@ -3154,18 +3153,18 @@ bool MapLoader::LoadLightmap(MapData* map)
}
// Setup the tile transform for any tile missing in the lump (shouldn't be any, but if there are we let the lightmapper bake them)
for (auto& tile : submesh->LightmapTiles)
for (auto& tile : Level->levelMesh->LightmapTiles)
{
if (tile.NeedsUpdate)
tile.SetupTileTransform(submesh->LMTextureSize);
tile.SetupTileTransform(Level->levelMesh->LMTextureSize);
}
// Place all tiles in atlas textures
submesh->PackLightmapAtlas(0);
Level->levelMesh->PackLightmapAtlas(0);
// Start with empty lightmap textures
submesh->LMTextureData.Resize(submesh->LMTextureCount * textureSize * textureSize * 3);
memset(submesh->LMTextureData.Data(), 0, submesh->LMTextureData.Size() * sizeof(uint16_t));
Level->levelMesh->LMTextureData.Resize(Level->levelMesh->LMTextureCount * textureSize * textureSize * 3);
memset(Level->levelMesh->LMTextureData.Data(), 0, Level->levelMesh->LMTextureData.Size() * sizeof(uint16_t));
// Copy tile pixels to the texture
for (auto& binding : foundBindings)
@ -3174,7 +3173,7 @@ bool MapLoader::LoadLightmap(MapData* map)
LightmapTile* tile = binding.second;
const uint16_t* src = textureData.Data() + entry->pixelsOffset;
uint16_t* dst = &submesh->LMTextureData[tile->AtlasLocation.ArrayIndex * textureSize * textureSize * 3];
uint16_t* dst = &Level->levelMesh->LMTextureData[tile->AtlasLocation.ArrayIndex * textureSize * textureSize * 3];
int destx = tile->AtlasLocation.X;
int desty = tile->AtlasLocation.Y;
int width = tile->AtlasLocation.Width;

View file

@ -1,7 +1,6 @@
#include "templates.h"
#include "doom_levelmesh.h"
#include "doom_levelsubmesh.h"
#include "g_levellocals.h"
#include "texturemanager.h"
#include "playsim/p_lnspec.h"
@ -9,6 +8,13 @@
#include "g_levellocals.h"
#include "a_dynlight.h"
#include "hw_renderstate.h"
#include "hw_vertexbuilder.h"
#include "hwrenderer/scene/hw_drawstructs.h"
#include "hwrenderer/scene/hw_drawinfo.h"
#include "hwrenderer/scene/hw_walldispatcher.h"
#include "hwrenderer/scene/hw_flatdispatcher.h"
#include "common/rendering/hwrenderer/data/hw_meshbuilder.h"
#include <unordered_map>
static bool RequireLevelMesh()
{
@ -42,7 +48,7 @@ ADD_STAT(lightmap)
return out;
}
uint32_t atlasPixelCount = levelMesh->StaticMesh->AtlasPixelCount();
uint32_t atlasPixelCount = levelMesh->AtlasPixelCount();
auto stats = levelMesh->GatherTilePixelStats();
out.Format("Surfaces: %u (awaiting updates: %u)\nSurface pixel area to update: %u\nSurface pixel area: %u\nAtlas pixel area: %u\nAtlas efficiency: %.4f%%",
@ -67,13 +73,13 @@ CCMD(invalidatelightmap)
if (!RequireLightmap()) return;
int count = 0;
for (auto& tile : level.levelMesh->StaticMesh->LightmapTiles)
for (auto& tile : level.levelMesh->LightmapTiles)
{
if (!tile.NeedsUpdate)
++count;
tile.NeedsUpdate = true;
}
Printf("Marked %d out of %d tiles for update.\n", count, level.levelMesh->StaticMesh->LightmapTiles.Size());
Printf("Marked %d out of %d tiles for update.\n", count, level.levelMesh->LightmapTiles.Size());
}
void PrintSurfaceInfo(const DoomLevelMeshSurface* surface)
@ -82,10 +88,10 @@ 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("Surface %d (%p)\n Type: %d, TypeIndex: %d, ControlSector: %d\n", level.levelMesh->GetSurfaceIndex(surface), surface, surface->Type, surface->TypeIndex, surface->ControlSector ? surface->ControlSector->Index() : -1);
if (surface->LightmapTileIndex >= 0)
{
LightmapTile* tile = &surface->Submesh->LightmapTiles[surface->LightmapTileIndex];
LightmapTile* tile = &level.levelMesh->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);
@ -142,24 +148,46 @@ EXTERN_CVAR(Float, lm_scale);
DoomLevelMesh::DoomLevelMesh(FLevelLocals& doomMap)
{
// Remove the empty mesh added in the LevelMesh constructor
Mesh.Vertices.clear();
Mesh.Indexes.clear();
SunColor = doomMap.SunColor; // TODO keep only one copy?
SunDirection = doomMap.SunDirection;
BuildSectorGroups(doomMap);
CreatePortals(doomMap);
StaticMesh = std::make_unique<DoomLevelSubmesh>(this, doomMap, true);
DynamicMesh = std::make_unique<DoomLevelSubmesh>(this, doomMap, false);
LightmapSampleDistance = doomMap.LightmapSampleDistance;
CreateSurfaces(doomMap);
LinkSurfaces(doomMap);
SortIndexes();
BuildTileSurfaceLists();
Mesh.DynamicIndexStart = Mesh.Indexes.size();
UpdateCollision();
// Assume double the size of the static mesh will be enough for anything dynamic.
Mesh.MaxVertices = std::max(Mesh.Vertices.size() * 2, (size_t)10000);
Mesh.MaxIndexes = std::max(Mesh.Indexes.size() * 2, (size_t)10000);
Mesh.MaxSurfaces = std::max(Mesh.SurfaceIndexes.size() * 2, (size_t)10000);
Mesh.MaxUniforms = std::max(Mesh.Uniforms.size() * 2, (size_t)10000);
Mesh.MaxSurfaceIndexes = std::max(Mesh.SurfaceIndexes.size() * 2, (size_t)10000);
Mesh.MaxNodes = std::max(Collision->get_nodes().size() * 2, (size_t)10000);
}
void DoomLevelMesh::BeginFrame(FLevelLocals& doomMap)
{
#if 0
static_cast<DoomLevelSubmesh*>(DynamicMesh.get())->Update(doomMap);
if (doomMap.lightmaps)
{
DynamicMesh->SetupTileTransforms();
DynamicMesh->PackLightmapAtlas(StaticMesh->LMTextureCount);
}
#endif
}
bool DoomLevelMesh::TraceSky(const FVector3& start, FVector3 direction, float dist)
@ -252,24 +280,585 @@ FLightNode* DoomLevelMesh::GetSurfaceLightNode(const DoomLevelMeshSurface* dooms
return node;
}
void DoomLevelMesh::CreateSurfaces(FLevelLocals& doomMap)
{
// We can't use side->segs since it is null.
TArray<std::pair<subsector_t*, seg_t*>> sideSegs(doomMap.sides.Size(), true);
for (unsigned int i = 0; i < doomMap.subsectors.Size(); i++)
{
subsector_t* sub = &doomMap.subsectors[i];
sector_t* sector = sub->sector;
for (int i = 0, count = sub->numlines; i < count; i++)
{
seg_t* seg = sub->firstline + i;
if (seg->sidedef)
sideSegs[seg->sidedef->Index()] = { sub, seg };
}
}
MeshBuilder state;
std::map<LightmapTileBinding, int> bindings;
// Create surface objects for all sides
for (unsigned int i = 0; i < doomMap.sides.Size(); i++)
{
side_t* side = &doomMap.sides[i];
bool isPolyLine = !!(side->Flags & WALLF_POLYOBJ);
if (isPolyLine)
continue;
subsector_t* sub = sideSegs[i].first;
seg_t* seg = sideSegs[i].second;
if (!seg)
continue;
sector_t* front = side->sector;
sector_t* back = (side->linedef->frontsector == front) ? side->linedef->backsector : side->linedef->frontsector;
HWMeshHelper result;
HWWallDispatcher disp(&doomMap, &result, getRealLightmode(&doomMap, true));
HWWall wall;
wall.sub = sub;
wall.Process(&disp, state, seg, front, back);
// Part 1: solid geometry. This is set up so that there are no transparent parts
state.SetDepthFunc(DF_LEqual);
state.ClearDepthBias();
state.EnableTexture(true);
state.EnableBrightmap(true);
state.AlphaFunc(Alpha_GEqual, 0.f);
CreateWallSurface(side, disp, state, bindings, result.list, false, true);
for (HWWall& portal : result.portals)
{
WallPortals.Push(portal);
}
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, bindings, result.translucent, false, true);
state.AlphaFunc(Alpha_GEqual, 0.f);
state.SetRenderStyle(STYLE_Normal);
*/
}
// Create surfaces for all flats
for (unsigned int i = 0; i < doomMap.sectors.Size(); i++)
{
sector_t* sector = &doomMap.sectors[i];
if (sector->subsectors[0]->flags & SSECF_POLYORG)
continue;
for (FSection& section : doomMap.sections.SectionsForSector(i))
{
int sectionIndex = doomMap.sections.SectionIndex(&section);
HWFlatMeshHelper result;
HWFlatDispatcher disp(&doomMap, &result, getRealLightmode(&doomMap, true));
HWFlat flat;
flat.section = &section;
flat.ProcessSector(&disp, state, sector);
// Part 1: solid geometry. This is set up so that there are no transparent parts
state.SetDepthFunc(DF_LEqual);
state.ClearDepthBias();
state.EnableTexture(true);
state.EnableBrightmap(true);
CreateFlatSurface(disp, state, bindings, result.list);
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, bindings, result.translucentborder);
state.SetDepthMask(false);
CreateFlatSurface(disp, state, bindings, result.translucent);
state.AlphaFunc(Alpha_GEqual, 0.f);
state.SetDepthMask(true);
state.SetRenderStyle(STYLE_Normal);
}
}
}
void DoomLevelMesh::CreateWallSurface(side_t* side, HWWallDispatcher& disp, MeshBuilder& state, std::map<LightmapTileBinding, int>& bindings, TArray<HWWall>& list, bool isSky, bool translucent)
{
for (HWWall& wallpart : list)
{
if (isSky)
{
state.SetEffect(EFF_PORTAL);
state.EnableTexture(false);
state.SetRenderStyle(STYLE_Normal);
wallpart.MakeVertices(state, false);
wallpart.RenderWall(state, HWWall::RWF_BLANK);
wallpart.vertcount = 0;
wallpart.LevelMeshInfo.Type = ST_NONE;
wallpart.LevelMeshInfo.ControlSector = nullptr;
state.SetEffect(EFF_NONE);
state.EnableTexture(true);
}
else
{
if (wallpart.texture && wallpart.texture->isMasked())
{
state.AlphaFunc(Alpha_GEqual, gl_mask_threshold);
}
else
{
state.AlphaFunc(Alpha_GEqual, 0.f);
}
wallpart.DrawWall(&disp, state, translucent);
}
int pipelineID = 0;
int startVertIndex = Mesh.Vertices.Size();
int startElementIndex = Mesh.Indexes.Size();
for (auto& it : state.mSortedLists)
{
const MeshApplyState& applyState = it.first;
pipelineID = screen->GetLevelMeshPipelineID(applyState.applyData, applyState.surfaceUniforms, applyState.material);
int uniformsIndex = Mesh.Uniforms.Size();
Mesh.Uniforms.Push(applyState.surfaceUniforms);
Mesh.Materials.Push(applyState.material);
for (MeshDrawCommand& command : it.second.mDraws)
{
for (int i = command.Start, end = command.Start + command.Count; i < end; i++)
{
Mesh.Vertices.Push(state.mVertices[i]);
Mesh.UniformIndexes.Push(uniformsIndex);
}
if (command.DrawType == DT_TriangleFan)
{
for (int i = 2, count = command.Count; i < count; i++)
{
Mesh.Indexes.Push(startVertIndex);
Mesh.Indexes.Push(startVertIndex + i - 1);
Mesh.Indexes.Push(startVertIndex + i);
}
}
}
}
state.mSortedLists.clear();
state.mVertices.Clear();
state.mIndexes.Clear();
FVector2 v1 = FVector2(side->V1()->fPos());
FVector2 v2 = FVector2(side->V2()->fPos());
FVector2 N = FVector2(v2.Y - v1.Y, v1.X - v2.X).Unit();
DoomLevelMeshSurface surf;
surf.Type = wallpart.LevelMeshInfo.Type;
surf.ControlSector = wallpart.LevelMeshInfo.ControlSector;
surf.TypeIndex = side->Index();
surf.Side = side;
surf.AlwaysUpdate = !!(side->sector->Flags & SECF_LM_DYNAMIC);
surf.SectorGroup = sectorGroup[side->sector->Index()];
surf.Alpha = float(side->linedef->alpha);
surf.MeshLocation.StartVertIndex = startVertIndex;
surf.MeshLocation.StartElementIndex = startElementIndex;
surf.MeshLocation.NumVerts = Mesh.Vertices.Size() - startVertIndex;
surf.MeshLocation.NumElements = Mesh.Indexes.Size() - startElementIndex;
surf.Plane = FVector4(N.X, N.Y, 0.0f, v1 | N);
surf.Texture = wallpart.texture;
surf.PipelineID = pipelineID;
surf.PortalIndex = isSky ? linePortals[side->linedef->Index()] : 0;
surf.IsSky = isSky;
surf.Bounds = GetBoundsFromSurface(surf);
surf.LightmapTileIndex = disp.Level->lightmaps ? AddSurfaceToTile(surf, bindings) : -1;
Surfaces.Push(surf);
}
}
int DoomLevelMesh::AddSurfaceToTile(const DoomLevelMeshSurface& surf, std::map<LightmapTileBinding, int>& bindings)
{
if (surf.IsSky)
return -1;
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 DoomLevelMesh::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 DoomLevelMesh::CreateFlatSurface(HWFlatDispatcher& disp, MeshBuilder& state, std::map<LightmapTileBinding, int>& bindings, TArray<HWFlat>& list, bool isSky)
{
for (HWFlat& flatpart : list)
{
if (isSky)
{
state.SetEffect(EFF_PORTAL);
state.EnableTexture(false);
state.SetRenderStyle(STYLE_Normal);
flatpart.DrawSubsectors(&disp, state);
state.SetEffect(EFF_NONE);
state.EnableTexture(true);
}
else
{
if (flatpart.texture && flatpart.texture->isMasked())
{
state.AlphaFunc(Alpha_GEqual, gl_mask_threshold);
}
else
{
state.AlphaFunc(Alpha_GEqual, 0.f);
}
flatpart.DrawFlat(&disp, state, false);
}
VSMatrix textureMatrix;
textureMatrix.loadIdentity();
int pipelineID = 0;
int uniformsIndex = 0;
bool foundDraw = false;
for (auto& it : state.mSortedLists)
{
const MeshApplyState& applyState = it.first;
pipelineID = screen->GetLevelMeshPipelineID(applyState.applyData, applyState.surfaceUniforms, applyState.material);
uniformsIndex = Mesh.Uniforms.Size();
textureMatrix = applyState.textureMatrix;
Mesh.Uniforms.Push(applyState.surfaceUniforms);
Mesh.Materials.Push(applyState.material);
foundDraw = true;
break;
}
state.mSortedLists.clear();
state.mVertices.Clear();
state.mIndexes.Clear();
if (!foundDraw)
continue;
DoomLevelMeshSurface surf;
surf.Type = flatpart.ceiling ? ST_CEILING : ST_FLOOR;
surf.ControlSector = flatpart.controlsector ? flatpart.controlsector->model : nullptr;
surf.AlwaysUpdate = !!(flatpart.sector->Flags & SECF_LM_DYNAMIC);
surf.SectorGroup = sectorGroup[flatpart.sector->Index()];
surf.Alpha = flatpart.alpha;
surf.Texture = flatpart.texture;
surf.PipelineID = pipelineID;
surf.PortalIndex = sectorPortals[flatpart.ceiling][flatpart.sector->Index()];
surf.IsSky = isSky;
auto plane = surf.ControlSector ? surf.ControlSector->GetSecPlane(!flatpart.ceiling) : flatpart.sector->GetSecPlane(flatpart.ceiling);
surf.Plane = FVector4((float)plane.Normal().X, (float)plane.Normal().Y, (float)plane.Normal().Z, -(float)plane.D);
if (surf.ControlSector)
surf.Plane = -surf.Plane;
float skyZ = flatpart.ceiling ? 32768.0f : -32768.0f;
for (subsector_t* sub : flatpart.section->subsectors)
{
if (sub->numlines < 3)
continue;
int startVertIndex = Mesh.Vertices.Size();
int startElementIndex = Mesh.Indexes.Size();
for (int i = 0, end = sub->numlines; i < end; i++)
{
auto& vt = sub->firstline[end - 1 - i].v1;
FVector3 pt((float)vt->fX(), (float)vt->fY(), isSky ? skyZ : (float)plane.ZatPoint(vt));
FVector4 uv = textureMatrix * FVector4(pt.X * (1.0f / 64.0f), pt.Y * (-1.0f / 64.0f), 0.0f, 1.0f);
FFlatVertex ffv;
ffv.x = pt.X;
ffv.y = pt.Y;
ffv.z = pt.Z;
ffv.u = uv.X;
ffv.v = uv.Y;
ffv.lu = 0.0f;
ffv.lv = 0.0f;
ffv.lindex = -1.0f;
Mesh.Vertices.Push(ffv);
Mesh.UniformIndexes.Push(uniformsIndex);
}
if (flatpart.ceiling)
{
for (int i = 2, count = sub->numlines; i < count; i++)
{
Mesh.Indexes.Push(startVertIndex);
Mesh.Indexes.Push(startVertIndex + i - 1);
Mesh.Indexes.Push(startVertIndex + i);
}
}
else
{
for (int i = 2, count = sub->numlines; i < count; i++)
{
Mesh.Indexes.Push(startVertIndex + i);
Mesh.Indexes.Push(startVertIndex + i - 1);
Mesh.Indexes.Push(startVertIndex);
}
}
surf.TypeIndex = sub->Index();
surf.Subsector = sub;
surf.MeshLocation.StartVertIndex = startVertIndex;
surf.MeshLocation.StartElementIndex = startElementIndex;
surf.MeshLocation.NumVerts = sub->numlines;
surf.MeshLocation.NumElements = (sub->numlines - 2) * 3;
surf.Bounds = GetBoundsFromSurface(surf);
surf.LightmapTileIndex = disp.Level->lightmaps ? AddSurfaceToTile(surf, bindings) : -1;
Surfaces.Push(surf);
}
}
}
#if 0
void DoomLevelMesh::CreateDynamicSurfaces(FLevelLocals& doomMap)
{
// Look for polyobjects
for (unsigned int i = 0; i < doomMap.lines.Size(); i++)
{
side_t* side = doomMap.lines[i].sidedef[0];
bool isPolyLine = !!(side->Flags & WALLF_POLYOBJ);
if (!isPolyLine)
continue;
// Make sure we have a surface array on the polyobj sidedef
if (!side->surface)
{
auto array = std::make_unique<DoomLevelMeshSurface * []>(4);
memset(array.get(), 0, sizeof(DoomLevelMeshSurface*));
side->surface = array.get();
PolyLMSurfaces.Push(std::move(array));
}
CreateSideSurfaces(doomMap, side);
}
}
#endif
void DoomLevelMesh::SortIndexes()
{
// Order surfaces by pipeline
std::unordered_map<int64_t, TArray<int>> pipelineSurfaces;
for (size_t i = 0; i < Surfaces.Size(); i++)
{
DoomLevelMeshSurface* s = &Surfaces[i];
pipelineSurfaces[(int64_t(s->PipelineID) << 32) | int64_t(s->IsSky)].Push(i);
}
// Create reorder surface indexes by pipeline and create a draw range for each
TArray<uint32_t> sortedIndexes;
for (const auto& it : pipelineSurfaces)
{
LevelSubmeshDrawRange range;
range.PipelineID = it.first >> 32;
range.Start = sortedIndexes.Size();
// Move indexes to new array
for (unsigned int i : it.second)
{
DoomLevelMeshSurface& s = Surfaces[i];
unsigned int start = s.MeshLocation.StartElementIndex;
unsigned int count = s.MeshLocation.NumElements;
s.MeshLocation.StartElementIndex = sortedIndexes.Size();
for (unsigned int j = 0; j < count; j++)
{
sortedIndexes.Push(Mesh.Indexes[start + j]);
}
for (unsigned int j = 0; j < count; j += 3)
{
Mesh.SurfaceIndexes.Push((int)i);
}
}
range.Count = sortedIndexes.Size() - range.Start;
if ((it.first & 1) == 0)
DrawList.Push(range);
else
PortalList.Push(range);
}
Mesh.Indexes.Swap(sortedIndexes);
}
void DoomLevelMesh::LinkSurfaces(FLevelLocals& doomMap)
{
for (auto& surface : Surfaces)
{
if (surface.Type == ST_FLOOR || surface.Type == ST_CEILING)
{
SetSubsectorLightmap(&surface);
}
else
{
SetSideLightmap(&surface);
}
}
}
void DoomLevelMesh::SetSubsectorLightmap(DoomLevelMeshSurface* surface)
{
if (surface->Subsector->firstline && surface->Subsector->firstline->sidedef)
surface->Subsector->firstline->sidedef->sector->HasLightmaps = true;
if (!surface->ControlSector)
{
int index = surface->Type == ST_CEILING ? 1 : 0;
surface->Subsector->surface[index][0] = surface;
}
else
{
int index = surface->Type == ST_CEILING ? 0 : 1;
const auto& ffloors = surface->Subsector->sector->e->XFloor.ffloors;
for (unsigned int i = 0; i < ffloors.Size(); i++)
{
if (ffloors[i]->model == surface->ControlSector)
{
surface->Subsector->surface[index][i + 1] = surface;
}
}
}
}
void DoomLevelMesh::SetSideLightmap(DoomLevelMeshSurface* surface)
{
if (!surface->ControlSector)
{
if (surface->Type == ST_UPPERSIDE)
{
surface->Side->surface[0] = surface;
}
else if (surface->Type == ST_MIDDLESIDE)
{
surface->Side->surface[1] = surface;
surface->Side->surface[2] = surface;
}
else if (surface->Type == ST_LOWERSIDE)
{
surface->Side->surface[3] = surface;
}
}
else
{
const auto& ffloors = surface->Side->sector->e->XFloor.ffloors;
for (unsigned int i = 0; i < ffloors.Size(); i++)
{
if (ffloors[i]->model == surface->ControlSector)
{
surface->Side->surface[4 + i] = surface;
}
}
}
}
BBox DoomLevelMesh::GetBoundsFromSurface(const LevelMeshSurface& surface) const
{
BBox bounds;
bounds.Clear();
for (int i = int(surface.MeshLocation.StartVertIndex); i < int(surface.MeshLocation.StartVertIndex) + surface.MeshLocation.NumVerts; i++)
{
FVector3 v = Mesh.Vertices[(int)i].fPos();
bounds.min.X = std::min(bounds.min.X, v.X);
bounds.min.Y = std::min(bounds.min.Y, v.Y);
bounds.min.Z = std::min(bounds.min.Z, v.Z);
bounds.max.X = std::max(bounds.max.X, v.X);
bounds.max.Y = std::max(bounds.max.Y, v.Y);
bounds.max.Z = std::max(bounds.max.Z, v.Z);
}
return bounds;
}
void DoomLevelMesh::DumpMesh(const FString& objFilename, const FString& mtlFilename) const
{
DoomLevelSubmesh* submesh = static_cast<DoomLevelSubmesh*>(StaticMesh.get());
auto f = fopen(objFilename.GetChars(), "w");
fprintf(f, "# DoomLevelMesh debug export\n");
fprintf(f, "# Vertices: %u, Indexes: %u, Surfaces: %u\n", submesh->Mesh.Vertices.Size(), submesh->Mesh.Indexes.Size(), submesh->Surfaces.Size());
fprintf(f, "# Vertices: %u, Indexes: %u, Surfaces: %u\n", Mesh.Vertices.Size(), Mesh.Indexes.Size(), Surfaces.Size());
fprintf(f, "mtllib %s\n", mtlFilename.GetChars());
double scale = 1 / 10.0;
for (const auto& v : submesh->Mesh.Vertices)
for (const auto& v : Mesh.Vertices)
{
fprintf(f, "v %f %f %f\n", v.x * scale, v.y * scale, v.z * scale);
}
for (const auto& v : submesh->Mesh.Vertices)
for (const auto& v : Mesh.Vertices)
{
fprintf(f, "vt %f %f\n", v.lu, v.lv);
}
@ -302,15 +891,15 @@ void DoomLevelMesh::DumpMesh(const FString& objFilename, const FString& mtlFilen
bool useErrorMaterial = false;
int highestUsedAtlasPage = -1;
for (unsigned i = 0, count = submesh->Mesh.Indexes.Size(); i + 2 < count; i += 3)
for (unsigned i = 0, count = Mesh.Indexes.Size(); i + 2 < count; i += 3)
{
auto index = submesh->Mesh.SurfaceIndexes[i / 3];
auto index = Mesh.SurfaceIndexes[i / 3];
if (index != lastSurfaceIndex)
{
lastSurfaceIndex = index;
if (unsigned(index) >= submesh->Surfaces.Size())
if (unsigned(index) >= Surfaces.Size())
{
fprintf(f, "o Surface[%d] (bad index)\n", index);
fprintf(f, "usemtl error\n");
@ -319,12 +908,12 @@ void DoomLevelMesh::DumpMesh(const FString& objFilename, const FString& mtlFilen
}
else
{
const auto& surface = submesh->Surfaces[index];
const auto& surface = Surfaces[index];
fprintf(f, "o Surface[%d] %s %d%s\n", index, name(surface.Type), surface.TypeIndex, surface.IsSky ? " sky" : "");
if (surface.LightmapTileIndex >= 0)
{
auto& tile = submesh->LightmapTiles[surface.LightmapTileIndex];
auto& tile = LightmapTiles[surface.LightmapTileIndex];
fprintf(f, "usemtl lightmap%d\n", tile.AtlasLocation.ArrayIndex);
if (tile.AtlasLocation.ArrayIndex > highestUsedAtlasPage)
@ -337,9 +926,9 @@ void DoomLevelMesh::DumpMesh(const FString& objFilename, const FString& mtlFilen
// fprintf(f, "f %d %d %d\n", MeshElements[i] + 1, MeshElements[i + 1] + 1, MeshElements[i + 2] + 1);
fprintf(f, "f %d/%d %d/%d %d/%d\n",
submesh->Mesh.Indexes[i + 0] + 1, submesh->Mesh.Indexes[i + 0] + 1,
submesh->Mesh.Indexes[i + 1] + 1, submesh->Mesh.Indexes[i + 1] + 1,
submesh->Mesh.Indexes[i + 2] + 1, submesh->Mesh.Indexes[i + 2] + 1);
Mesh.Indexes[i + 0] + 1, Mesh.Indexes[i + 0] + 1,
Mesh.Indexes[i + 1] + 1, Mesh.Indexes[i + 1] + 1,
Mesh.Indexes[i + 2] + 1, Mesh.Indexes[i + 2] + 1);
}

View file

@ -2,13 +2,40 @@
#pragma once
#include "hw_levelmesh.h"
#include "doom_levelsubmesh.h"
#include "scene/hw_drawstructs.h"
#include "tarray.h"
#include "vectors.h"
#include "r_defs.h"
#include "bounds.h"
#include <set>
#include <map>
struct FLevelLocals;
struct FPolyObj;
struct HWWallDispatcher;
class DoomLevelMesh;
class MeshBuilder;
struct DoomLevelMeshSurface : public LevelMeshSurface
{
DoomLevelMeshSurfaceType Type = ST_NONE;
int TypeIndex = 0;
subsector_t* Subsector = nullptr;
side_t* Side = nullptr;
sector_t* ControlSector = nullptr;
int PipelineID = 0;
};
class DoomLevelMesh : public LevelMesh
{
public:
DoomLevelMesh(FLevelLocals &doomMap);
LevelMeshSurface* GetSurface(int index) override { return &Surfaces[index]; }
unsigned int GetSurfaceIndex(const LevelMeshSurface* surface) const override { return (unsigned int)(ptrdiff_t)(static_cast<const DoomLevelMeshSurface*>(surface) - Surfaces.Data()); }
int GetSurfaceCount() override { return Surfaces.Size(); }
int AddSurfaceLights(const LevelMeshSurface* surface, LevelMeshLight* list, int listMaxSize) override;
void BeginFrame(FLevelLocals& doomMap);
@ -17,11 +44,34 @@ public:
void BuildSectorGroups(const FLevelLocals& doomMap);
TArray<DoomLevelMeshSurface> Surfaces;
TArray<std::unique_ptr<DoomLevelMeshSurface* []>> PolyLMSurfaces;
TArray<HWWall> WallPortals;
TArray<int> sectorGroup; // index is sector, value is sectorGroup
TArray<int> sectorPortals[2]; // index is sector+plane, value is index into the portal list
TArray<int> linePortals; // index is linedef, value is index into the portal list
private:
void Reset();
void CreateSurfaces(FLevelLocals& doomMap);
void SetSubsectorLightmap(DoomLevelMeshSurface* surface);
void SetSideLightmap(DoomLevelMeshSurface* surface);
void SortIndexes();
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);
void LinkSurfaces(FLevelLocals& doomMap);
BBox GetBoundsFromSurface(const LevelMeshSurface& surface) const;
int AddSurfaceToTile(const DoomLevelMeshSurface& surf, std::map<LightmapTileBinding, int>& bindings);
int GetSampleDimension(const DoomLevelMeshSurface& surf);
void CreatePortals(FLevelLocals& doomMap);
FLightNode* GetSurfaceLightNode(const DoomLevelMeshSurface* doomsurf);
};

View file

@ -1,627 +0,0 @@
#include "templates.h"
#include "doom_levelsubmesh.h"
#include "g_levellocals.h"
#include "texturemanager.h"
#include "playsim/p_lnspec.h"
#include "c_dispatch.h"
#include "g_levellocals.h"
#include "a_dynlight.h"
#include "hw_renderstate.h"
#include "hw_vertexbuilder.h"
#include "hwrenderer/scene/hw_drawstructs.h"
#include "hwrenderer/scene/hw_drawinfo.h"
#include "hwrenderer/scene/hw_walldispatcher.h"
#include "hwrenderer/scene/hw_flatdispatcher.h"
#include "common/rendering/hwrenderer/data/hw_meshbuilder.h"
#include <unordered_map>
EXTERN_CVAR(Float, lm_scale);
DoomLevelSubmesh::DoomLevelSubmesh(DoomLevelMesh* mesh, FLevelLocals& doomMap, bool staticMesh) : LevelMesh(mesh), StaticMesh(staticMesh)
{
LightmapSampleDistance = doomMap.LightmapSampleDistance;
Reset();
if (StaticMesh)
{
CreateStaticSurfaces(doomMap);
LinkSurfaces(doomMap);
SortIndexes();
BuildTileSurfaceLists();
UpdateCollision();
}
}
void DoomLevelSubmesh::Update(FLevelLocals& doomMap)
{
if (!StaticMesh)
{
Reset();
CreateDynamicSurfaces(doomMap);
LinkSurfaces(doomMap);
SortIndexes();
BuildTileSurfaceLists();
UpdateCollision();
}
}
void DoomLevelSubmesh::Reset()
{
Surfaces.Clear();
Portals.Clear();
Mesh.Vertices.Clear();
Mesh.Indexes.Clear();
Mesh.SurfaceIndexes.Clear();
Mesh.UniformIndexes.Clear();
Mesh.Uniforms.Clear();
Mesh.Materials.Clear();
}
void DoomLevelSubmesh::CreateStaticSurfaces(FLevelLocals& doomMap)
{
// We can't use side->segs since it is null.
TArray<std::pair<subsector_t*, seg_t*>> sideSegs(doomMap.sides.Size(), true);
for (unsigned int i = 0; i < doomMap.subsectors.Size(); i++)
{
subsector_t* sub = &doomMap.subsectors[i];
sector_t* sector = sub->sector;
for (int i = 0, count = sub->numlines; i < count; i++)
{
seg_t* seg = sub->firstline + i;
if (seg->sidedef)
sideSegs[seg->sidedef->Index()] = { sub, seg };
}
}
MeshBuilder state;
std::map<LightmapTileBinding, int> bindings;
// Create surface objects for all sides
for (unsigned int i = 0; i < doomMap.sides.Size(); i++)
{
side_t* side = &doomMap.sides[i];
bool isPolyLine = !!(side->Flags & WALLF_POLYOBJ);
if (isPolyLine)
continue;
subsector_t* sub = sideSegs[i].first;
seg_t* seg = sideSegs[i].second;
if (!seg)
continue;
sector_t* front = side->sector;
sector_t* back = (side->linedef->frontsector == front) ? side->linedef->backsector : side->linedef->frontsector;
HWMeshHelper result;
HWWallDispatcher disp(&doomMap, &result, getRealLightmode(&doomMap, true));
HWWall wall;
wall.sub = sub;
wall.Process(&disp, state, seg, front, back);
// Part 1: solid geometry. This is set up so that there are no transparent parts
state.SetDepthFunc(DF_LEqual);
state.ClearDepthBias();
state.EnableTexture(true);
state.EnableBrightmap(true);
state.AlphaFunc(Alpha_GEqual, 0.f);
CreateWallSurface(side, disp, state, bindings, result.list, false, true);
for (HWWall& portal : result.portals)
{
Portals.Push(portal);
}
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, bindings, result.translucent, false, true);
state.AlphaFunc(Alpha_GEqual, 0.f);
state.SetRenderStyle(STYLE_Normal);
*/
}
// Create surfaces for all flats
for (unsigned int i = 0; i < doomMap.sectors.Size(); i++)
{
sector_t* sector = &doomMap.sectors[i];
if (sector->subsectors[0]->flags & SSECF_POLYORG)
continue;
for (FSection& section : doomMap.sections.SectionsForSector(i))
{
int sectionIndex = doomMap.sections.SectionIndex(&section);
HWFlatMeshHelper result;
HWFlatDispatcher disp(&doomMap, &result, getRealLightmode(&doomMap, true));
HWFlat flat;
flat.section = &section;
flat.ProcessSector(&disp, state, sector);
// Part 1: solid geometry. This is set up so that there are no transparent parts
state.SetDepthFunc(DF_LEqual);
state.ClearDepthBias();
state.EnableTexture(true);
state.EnableBrightmap(true);
CreateFlatSurface(disp, state, bindings, result.list);
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, bindings, result.translucentborder);
state.SetDepthMask(false);
CreateFlatSurface(disp, state, bindings, result.translucent);
state.AlphaFunc(Alpha_GEqual, 0.f);
state.SetDepthMask(true);
state.SetRenderStyle(STYLE_Normal);
}
}
}
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)
{
if (isSky)
{
state.SetEffect(EFF_PORTAL);
state.EnableTexture(false);
state.SetRenderStyle(STYLE_Normal);
wallpart.MakeVertices(state, false);
wallpart.RenderWall(state, HWWall::RWF_BLANK);
wallpart.vertcount = 0;
wallpart.LevelMeshInfo.Type = ST_NONE;
wallpart.LevelMeshInfo.ControlSector = nullptr;
state.SetEffect(EFF_NONE);
state.EnableTexture(true);
}
else
{
if (wallpart.texture && wallpart.texture->isMasked())
{
state.AlphaFunc(Alpha_GEqual, gl_mask_threshold);
}
else
{
state.AlphaFunc(Alpha_GEqual, 0.f);
}
wallpart.DrawWall(&disp, state, translucent);
}
int pipelineID = 0;
int startVertIndex = Mesh.Vertices.Size();
int startElementIndex = Mesh.Indexes.Size();
for (auto& it : state.mSortedLists)
{
const MeshApplyState& applyState = it.first;
pipelineID = screen->GetLevelMeshPipelineID(applyState.applyData, applyState.surfaceUniforms, applyState.material);
int uniformsIndex = Mesh.Uniforms.Size();
Mesh.Uniforms.Push(applyState.surfaceUniforms);
Mesh.Materials.Push(applyState.material);
for (MeshDrawCommand& command : it.second.mDraws)
{
for (int i = command.Start, end = command.Start + command.Count; i < end; i++)
{
Mesh.Vertices.Push(state.mVertices[i]);
Mesh.UniformIndexes.Push(uniformsIndex);
}
if (command.DrawType == DT_TriangleFan)
{
for (int i = 2, count = command.Count; i < count; i++)
{
Mesh.Indexes.Push(startVertIndex);
Mesh.Indexes.Push(startVertIndex + i - 1);
Mesh.Indexes.Push(startVertIndex + i);
}
}
}
}
state.mSortedLists.clear();
state.mVertices.Clear();
state.mIndexes.Clear();
FVector2 v1 = FVector2(side->V1()->fPos());
FVector2 v2 = FVector2(side->V2()->fPos());
FVector2 N = FVector2(v2.Y - v1.Y, v1.X - v2.X).Unit();
DoomLevelMeshSurface surf;
surf.Submesh = this;
surf.Type = wallpart.LevelMeshInfo.Type;
surf.ControlSector = wallpart.LevelMeshInfo.ControlSector;
surf.TypeIndex = side->Index();
surf.Side = side;
surf.AlwaysUpdate = !!(side->sector->Flags & SECF_LM_DYNAMIC);
surf.SectorGroup = LevelMesh->sectorGroup[side->sector->Index()];
surf.Alpha = float(side->linedef->alpha);
surf.MeshLocation.StartVertIndex = startVertIndex;
surf.MeshLocation.StartElementIndex = startElementIndex;
surf.MeshLocation.NumVerts = Mesh.Vertices.Size() - startVertIndex;
surf.MeshLocation.NumElements = Mesh.Indexes.Size() - startElementIndex;
surf.Plane = FVector4(N.X, N.Y, 0.0f, v1 | N);
surf.Texture = wallpart.texture;
surf.PipelineID = pipelineID;
surf.PortalIndex = isSky ? LevelMesh->linePortals[side->linedef->Index()] : 0;
surf.IsSky = isSky;
surf.Bounds = GetBoundsFromSurface(surf);
surf.LightmapTileIndex = disp.Level->lightmaps ? AddSurfaceToTile(surf, bindings) : -1;
Surfaces.Push(surf);
}
}
int DoomLevelSubmesh::AddSurfaceToTile(const DoomLevelMeshSurface& surf, std::map<LightmapTileBinding, int>& bindings)
{
if (surf.IsSky)
return -1;
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)
{
if (isSky)
{
state.SetEffect(EFF_PORTAL);
state.EnableTexture(false);
state.SetRenderStyle(STYLE_Normal);
flatpart.DrawSubsectors(&disp, state);
state.SetEffect(EFF_NONE);
state.EnableTexture(true);
}
else
{
if (flatpart.texture && flatpart.texture->isMasked())
{
state.AlphaFunc(Alpha_GEqual, gl_mask_threshold);
}
else
{
state.AlphaFunc(Alpha_GEqual, 0.f);
}
flatpart.DrawFlat(&disp, state, false);
}
VSMatrix textureMatrix;
textureMatrix.loadIdentity();
int pipelineID = 0;
int uniformsIndex = 0;
bool foundDraw = false;
for (auto& it : state.mSortedLists)
{
const MeshApplyState& applyState = it.first;
pipelineID = screen->GetLevelMeshPipelineID(applyState.applyData, applyState.surfaceUniforms, applyState.material);
uniformsIndex = Mesh.Uniforms.Size();
textureMatrix = applyState.textureMatrix;
Mesh.Uniforms.Push(applyState.surfaceUniforms);
Mesh.Materials.Push(applyState.material);
foundDraw = true;
break;
}
state.mSortedLists.clear();
state.mVertices.Clear();
state.mIndexes.Clear();
if (!foundDraw)
continue;
DoomLevelMeshSurface surf;
surf.Submesh = this;
surf.Type = flatpart.ceiling ? ST_CEILING : ST_FLOOR;
surf.ControlSector = flatpart.controlsector ? flatpart.controlsector->model : nullptr;
surf.AlwaysUpdate = !!(flatpart.sector->Flags & SECF_LM_DYNAMIC);
surf.SectorGroup = LevelMesh->sectorGroup[flatpart.sector->Index()];
surf.Alpha = flatpart.alpha;
surf.Texture = flatpart.texture;
surf.PipelineID = pipelineID;
surf.PortalIndex = LevelMesh->sectorPortals[flatpart.ceiling][flatpart.sector->Index()];
surf.IsSky = isSky;
auto plane = surf.ControlSector ? surf.ControlSector->GetSecPlane(!flatpart.ceiling) : flatpart.sector->GetSecPlane(flatpart.ceiling);
surf.Plane = FVector4((float)plane.Normal().X, (float)plane.Normal().Y, (float)plane.Normal().Z, -(float)plane.D);
if (surf.ControlSector)
surf.Plane = -surf.Plane;
float skyZ = flatpart.ceiling ? 32768.0f : -32768.0f;
for (subsector_t* sub : flatpart.section->subsectors)
{
if (sub->numlines < 3)
continue;
int startVertIndex = Mesh.Vertices.Size();
int startElementIndex = Mesh.Indexes.Size();
for (int i = 0, end = sub->numlines; i < end; i++)
{
auto& vt = sub->firstline[end - 1 - i].v1;
FVector3 pt((float)vt->fX(), (float)vt->fY(), isSky ? skyZ : (float)plane.ZatPoint(vt));
FVector4 uv = textureMatrix * FVector4(pt.X * (1.0f / 64.0f), pt.Y * (-1.0f / 64.0f), 0.0f, 1.0f);
FFlatVertex ffv;
ffv.x = pt.X;
ffv.y = pt.Y;
ffv.z = pt.Z;
ffv.u = uv.X;
ffv.v = uv.Y;
ffv.lu = 0.0f;
ffv.lv = 0.0f;
ffv.lindex = -1.0f;
Mesh.Vertices.Push(ffv);
Mesh.UniformIndexes.Push(uniformsIndex);
}
if (flatpart.ceiling)
{
for (int i = 2, count = sub->numlines; i < count; i++)
{
Mesh.Indexes.Push(startVertIndex);
Mesh.Indexes.Push(startVertIndex + i - 1);
Mesh.Indexes.Push(startVertIndex + i);
}
}
else
{
for (int i = 2, count = sub->numlines; i < count; i++)
{
Mesh.Indexes.Push(startVertIndex + i);
Mesh.Indexes.Push(startVertIndex + i - 1);
Mesh.Indexes.Push(startVertIndex);
}
}
surf.TypeIndex = sub->Index();
surf.Subsector = sub;
surf.MeshLocation.StartVertIndex = startVertIndex;
surf.MeshLocation.StartElementIndex = startElementIndex;
surf.MeshLocation.NumVerts = sub->numlines;
surf.MeshLocation.NumElements = (sub->numlines - 2) * 3;
surf.Bounds = GetBoundsFromSurface(surf);
surf.LightmapTileIndex = disp.Level->lightmaps ? AddSurfaceToTile(surf, bindings) : -1;
Surfaces.Push(surf);
}
}
}
void DoomLevelSubmesh::CreateDynamicSurfaces(FLevelLocals& doomMap)
{
#if 0
// Look for polyobjects
for (unsigned int i = 0; i < doomMap.lines.Size(); i++)
{
side_t* side = doomMap.lines[i].sidedef[0];
bool isPolyLine = !!(side->Flags & WALLF_POLYOBJ);
if (!isPolyLine)
continue;
// Make sure we have a surface array on the polyobj sidedef
if (!side->surface)
{
auto array = std::make_unique<DoomLevelMeshSurface * []>(4);
memset(array.get(), 0, sizeof(DoomLevelMeshSurface*));
side->surface = array.get();
PolyLMSurfaces.Push(std::move(array));
}
CreateSideSurfaces(doomMap, side);
}
#endif
}
void DoomLevelSubmesh::SortIndexes()
{
// Order surfaces by pipeline
std::unordered_map<int64_t, TArray<int>> pipelineSurfaces;
for (size_t i = 0; i < Surfaces.Size(); i++)
{
DoomLevelMeshSurface* s = &Surfaces[i];
pipelineSurfaces[(int64_t(s->PipelineID) << 32) | int64_t(s->IsSky)].Push(i);
}
// Create reorder surface indexes by pipeline and create a draw range for each
TArray<uint32_t> sortedIndexes;
for (const auto& it : pipelineSurfaces)
{
LevelSubmeshDrawRange range;
range.PipelineID = it.first >> 32;
range.Start = sortedIndexes.Size();
// Move indexes to new array
for (unsigned int i : it.second)
{
DoomLevelMeshSurface& s = Surfaces[i];
unsigned int start = s.MeshLocation.StartElementIndex;
unsigned int count = s.MeshLocation.NumElements;
s.MeshLocation.StartElementIndex = sortedIndexes.Size();
for (unsigned int j = 0; j < count; j++)
{
sortedIndexes.Push(Mesh.Indexes[start + j]);
}
for (unsigned int j = 0; j < count; j += 3)
{
Mesh.SurfaceIndexes.Push((int)i);
}
}
range.Count = sortedIndexes.Size() - range.Start;
if ((it.first & 1) == 0)
DrawList.Push(range);
else
PortalList.Push(range);
}
Mesh.Indexes.Swap(sortedIndexes);
}
void DoomLevelSubmesh::LinkSurfaces(FLevelLocals& doomMap)
{
for (auto& surface : Surfaces)
{
if (surface.Type == ST_FLOOR || surface.Type == ST_CEILING)
{
SetSubsectorLightmap(&surface);
}
else
{
SetSideLightmap(&surface);
}
}
}
void DoomLevelSubmesh::SetSubsectorLightmap(DoomLevelMeshSurface* surface)
{
if (surface->Subsector->firstline && surface->Subsector->firstline->sidedef)
surface->Subsector->firstline->sidedef->sector->HasLightmaps = true;
if (!surface->ControlSector)
{
int index = surface->Type == ST_CEILING ? 1 : 0;
surface->Subsector->surface[index][0] = surface;
}
else
{
int index = surface->Type == ST_CEILING ? 0 : 1;
const auto& ffloors = surface->Subsector->sector->e->XFloor.ffloors;
for (unsigned int i = 0; i < ffloors.Size(); i++)
{
if (ffloors[i]->model == surface->ControlSector)
{
surface->Subsector->surface[index][i + 1] = surface;
}
}
}
}
void DoomLevelSubmesh::SetSideLightmap(DoomLevelMeshSurface* surface)
{
if (!surface->ControlSector)
{
if (surface->Type == ST_UPPERSIDE)
{
surface->Side->surface[0] = surface;
}
else if (surface->Type == ST_MIDDLESIDE)
{
surface->Side->surface[1] = surface;
surface->Side->surface[2] = surface;
}
else if (surface->Type == ST_LOWERSIDE)
{
surface->Side->surface[3] = surface;
}
}
else
{
const auto& ffloors = surface->Side->sector->e->XFloor.ffloors;
for (unsigned int i = 0; i < ffloors.Size(); i++)
{
if (ffloors[i]->model == surface->ControlSector)
{
surface->Side->surface[4 + i] = surface;
}
}
}
}
BBox DoomLevelSubmesh::GetBoundsFromSurface(const LevelMeshSurface& surface) const
{
BBox bounds;
bounds.Clear();
for (int i = int(surface.MeshLocation.StartVertIndex); i < int(surface.MeshLocation.StartVertIndex) + surface.MeshLocation.NumVerts; i++)
{
FVector3 v = Mesh.Vertices[(int)i].fPos();
bounds.min.X = std::min(bounds.min.X, v.X);
bounds.min.Y = std::min(bounds.min.Y, v.Y);
bounds.min.Z = std::min(bounds.min.Z, v.Z);
bounds.max.X = std::max(bounds.max.X, v.X);
bounds.max.Y = std::max(bounds.max.Y, v.Y);
bounds.max.Z = std::max(bounds.max.Z, v.Z);
}
return bounds;
}

View file

@ -1,71 +0,0 @@
#pragma once
#include "hw_levelmesh.h"
#include "scene/hw_drawstructs.h"
#include "tarray.h"
#include "vectors.h"
#include "r_defs.h"
#include "bounds.h"
#include <set>
#include <map>
struct FLevelLocals;
struct FPolyObj;
struct HWWallDispatcher;
class DoomLevelMesh;
class MeshBuilder;
struct DoomLevelMeshSurface : public LevelMeshSurface
{
DoomLevelMeshSurfaceType Type = ST_NONE;
int TypeIndex = 0;
subsector_t* Subsector = nullptr;
side_t* Side = nullptr;
sector_t* ControlSector = nullptr;
int PipelineID = 0;
};
class DoomLevelSubmesh : public LevelSubmesh
{
public:
DoomLevelSubmesh(DoomLevelMesh* mesh, FLevelLocals& doomMap, bool staticMesh);
void Update(FLevelLocals& doomMap);
LevelMeshSurface* GetSurface(int index) override { return &Surfaces[index]; }
unsigned int GetSurfaceIndex(const LevelMeshSurface* surface) const override { return (unsigned int)(ptrdiff_t)(static_cast<const DoomLevelMeshSurface*>(surface) - Surfaces.Data()); }
int GetSurfaceCount() override { return Surfaces.Size(); }
TArray<DoomLevelMeshSurface> Surfaces;
TArray<std::unique_ptr<DoomLevelMeshSurface*[]>> PolyLMSurfaces;
TArray<HWWall> Portals;
private:
void Reset();
void CreateStaticSurfaces(FLevelLocals& doomMap);
void CreateDynamicSurfaces(FLevelLocals& doomMap);
void SetSubsectorLightmap(DoomLevelMeshSurface* surface);
void SetSideLightmap(DoomLevelMeshSurface* surface);
void SortIndexes();
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);
void LinkSurfaces(FLevelLocals& doomMap);
BBox GetBoundsFromSurface(const LevelMeshSurface& surface) const;
int AddSurfaceToTile(const DoomLevelMeshSurface& surf, std::map<LightmapTileBinding, int>& bindings);
int GetSampleDimension(const DoomLevelMeshSurface& surf);
DoomLevelMesh* LevelMesh = nullptr;
bool StaticMesh = true;
};
static_assert(alignof(FVector2) == alignof(float[2]) && sizeof(FVector2) == sizeof(float) * 2);

View file

@ -246,7 +246,7 @@ static int CreateIndexedSectorVerticesLM(sector_t* sec, const secplane_t& plane,
DoomLevelMeshSurface* lightmap = sub->surface[h].Size() > lightmapIndex ? sub->surface[h][lightmapIndex] : nullptr;
if (lightmap && lightmap->Type != ST_NONE) // surface may be missing if the subsector is degenerate triangle
{
FFlatVertex* luvs = &lightmap->Submesh->Mesh.Vertices[lightmap->MeshLocation.StartVertIndex];
FFlatVertex* luvs = &level.levelMesh->Mesh.Vertices[lightmap->MeshLocation.StartVertIndex];
for (unsigned int j = 0, end = sub->numlines; j < end; j++)
{
SetFlatVertex(vbo_shadowdata[vi + pos], sub->firstline[j].v1, plane, luvs[end - 1 - j].lu, luvs[end - 1 - j].lv, luvs[end - 1 - j].lindex);

View file

@ -424,12 +424,12 @@ void HWWall::ProcessDecal(HWDrawInfo *di, FRenderState& state, DBaseDecal *decal
if (surface && surface->LightmapTileIndex >= 0)
{
LightmapTile* tile = &surface->Submesh->LightmapTiles[surface->LightmapTileIndex];
LightmapTile* tile = &di->Level->levelMesh->LightmapTiles[surface->LightmapTileIndex];
float lightmapindex = (float)tile->AtlasLocation.ArrayIndex;
for (i = 0; i < 4; i++)
{
FVector2 lightmapuv = tile->ToUV(FVector3(dv[i].x, dv[i].y, dv[i].z), surface->Submesh->LMTextureSize);
FVector2 lightmapuv = tile->ToUV(FVector3(dv[i].x, dv[i].y, dv[i].z), di->Level->levelMesh->LMTextureSize);
verts.first[i].Set(dv[i].x, dv[i].z, dv[i].y, dv[i].u, dv[i].v, lightmapuv.X, lightmapuv.Y, lightmapindex);
}
}

View file

@ -411,7 +411,7 @@ void HWDrawInfo::CreateScene(bool drawpsprites, FRenderState& state)
validcount++; // used for processing sidedefs only once by the renderer.
auto& portals = static_cast<DoomLevelSubmesh*>(level.levelMesh->StaticMesh.get())->Portals;
auto& portals = level.levelMesh->WallPortals;
// draw level into depth buffer
state.SetColorMask(false);
@ -622,7 +622,7 @@ void HWDrawInfo::UpdateLightmaps()
{
if (!outer && VisibleTiles.Size() < unsigned(lm_background_updates))
{
for (auto& e : level.levelMesh->StaticMesh->LightmapTiles)
for (auto& e : level.levelMesh->LightmapTiles)
{
if (e.NeedsUpdate)
{

View file

@ -10,6 +10,7 @@
#include "hw_weapon.h"
#include "hw_drawlist.h"
#include "hw_renderstate.h"
#include "g_levellocals.h"
EXTERN_CVAR(Bool, lm_always_update);
EXTERN_CVAR(Int, lm_max_updates);
@ -235,7 +236,7 @@ public:
if (surface->LightmapTileIndex < 0)
return;
LightmapTile* tile = &surface->Submesh->LightmapTiles[surface->LightmapTileIndex];
LightmapTile* tile = &Level->levelMesh->LightmapTiles[surface->LightmapTileIndex];
if (lm_always_update || surface->AlwaysUpdate)
{
tile->NeedsUpdate = true;

View file

@ -26,7 +26,8 @@
#include "hw_renderstate.h"
#include "hwrenderer/scene/hw_drawinfo.h"
#include "hwrenderer/scene/hw_drawstructs.h"
#include "doom_levelsubmesh.h"
#include "doom_levelmesh.h"
#include "g_levellocals.h"
EXTERN_CVAR(Bool, gl_seamless)
@ -196,11 +197,11 @@ int HWWall::CreateVertices(FFlatVertex *&ptr, bool split)
{
if (surface && surface->LightmapTileIndex >= 0)
{
LightmapTile* tile = &surface->Submesh->LightmapTiles[surface->LightmapTileIndex];
FVector2 lolft = tile->ToUV(FVector3(glseg.x1, glseg.y1, zbottom[0]), surface->Submesh->LMTextureSize);
FVector2 uplft = tile->ToUV(FVector3(glseg.x1, glseg.y1, ztop[0]), surface->Submesh->LMTextureSize);
FVector2 uprgt = tile->ToUV(FVector3(glseg.x2, glseg.y2, ztop[1]), surface->Submesh->LMTextureSize);
FVector2 lorgt = tile->ToUV(FVector3(glseg.x2, glseg.y2, zbottom[1]), surface->Submesh->LMTextureSize);
LightmapTile* tile = &level.levelMesh->LightmapTiles[surface->LightmapTileIndex];
FVector2 lolft = tile->ToUV(FVector3(glseg.x1, glseg.y1, zbottom[0]), level.levelMesh->LMTextureSize);
FVector2 uplft = tile->ToUV(FVector3(glseg.x1, glseg.y1, ztop[0]), level.levelMesh->LMTextureSize);
FVector2 uprgt = tile->ToUV(FVector3(glseg.x2, glseg.y2, ztop[1]), level.levelMesh->LMTextureSize);
FVector2 lorgt = tile->ToUV(FVector3(glseg.x2, glseg.y2, zbottom[1]), level.levelMesh->LMTextureSize);
lightuv[LOLFT] = { lolft.X, lolft.Y };
lightuv[UPLFT] = { uplft.X, uplft.Y };
lightuv[UPRGT] = { uprgt.X, uprgt.Y };