From 5263a68dc93db259965b3bfda0f541ceaa08165d Mon Sep 17 00:00:00 2001 From: RaveYard <29225776+MrRaveYard@users.noreply.github.com> Date: Thu, 31 Aug 2023 11:25:24 +0200 Subject: [PATCH] Atlas UV mapping --- src/maploader/maploader.cpp | 97 +++++++++++++--- src/rendering/hwrenderer/doom_levelmesh.cpp | 121 ++++++++++++++++++++ src/rendering/hwrenderer/doom_levelmesh.h | 70 +++-------- 3 files changed, 215 insertions(+), 73 deletions(-) diff --git a/src/maploader/maploader.cpp b/src/maploader/maploader.cpp index 5df89b3aa..374464788 100644 --- a/src/maploader/maploader.cpp +++ b/src/maploader/maploader.cpp @@ -3341,39 +3341,94 @@ void MapLoader::InitLightmap(MapData* map) Level->LMSurfaces.Reset(); Level->LMTextureData.Reset(); + Level->LMTextureSize = 1024; // TODO cvar + + Level->LMTextureCount = Level->levelMesh->SetupLightmapUvs(Level->LMTextureSize); + // Debug placeholder stuff { - Level->LMTextureCount = 1; - Level->LMTextureSize = 1024; - - auto constructDebugTexture = [&](TArray& buffer, int width, int height) { + auto constructDebugTexture = [&](TArray& buffer, int layers, int width, int height) { uint16_t* ptr = buffer.Data(); - for (int y = 0; y < height; ++y) + for (int i = 0; i < layers; ++i) { - for (int x = 0; x < width; ++x) + for (int y = 0; y < height; ++y) { - *(ptr++) = floatToHalf(float(x) / width); - *(ptr++) = floatToHalf(float(y) / height); - *(ptr++) = floatToHalf((x + y) % 2 == 0 ? 1.0f : 0.0f); + for (int x = 0; x < width; ++x) + { + /**(ptr++) = floatToHalf(float(x) / width); + *(ptr++) = floatToHalf(float(y) / height); + *(ptr++) = floatToHalf((x + y) % 2 == 0 ? 1.0f : 0.0f);*/ + switch (i % 3) + { + case 0: + *(ptr++) = floatToHalf(1.0f); + *(ptr++) = floatToHalf(0.0f); + *(ptr++) = floatToHalf(0.0f); + break; + case 1: + *(ptr++) = floatToHalf(0.0f); + *(ptr++) = floatToHalf(1.0f); + *(ptr++) = floatToHalf(0.0f); + break; + case 2: + *(ptr++) = floatToHalf(0.0f); + *(ptr++) = floatToHalf(0.0f); + *(ptr++) = floatToHalf(1.0f); + break; + } + } + } + } + + auto get_xy = [&](int page, int x, int y) -> uint16_t* + { + return buffer.Data() + ((y * width) + x + (height * width * page)) * 3; + }; + + + srand(1337); + for (auto& surface : Level->levelMesh->Surfaces) + { + float r; + float g; + float b; + + r = rand() % 32 / 32.0; + g = rand() % 32 / 32.0; + b = rand() % 32 / 32.0; + + for (int y = 0; y <= surface.texHeight; ++y) + { + for (int x = 0; x <= surface.texWidth; ++x) + { + auto ptr = get_xy(surface.atlasPageIndex, surface.atlasX + x, surface.atlasY + y); + + ptr[0] = floatToHalf(r); + ptr[1] = floatToHalf(g); + ptr[2] = floatToHalf(b); + + if (x % 4 == 0 || y % 4 == 0) + { + ptr[0] = floatToHalf(0.0f); + ptr[1] = floatToHalf(0.0f); + ptr[2] = floatToHalf(0.0f); + } + } } } }; int size = Level->LMTextureSize; - int layers = 1; + int layers = Level->LMTextureCount; Level->LMTextureData.Resize(size * size * 3 * layers); - constructDebugTexture(Level->LMTextureData, size, size); + constructDebugTexture(Level->LMTextureData, Level->LMTextureCount, size, size); } - for (auto& surface : Level->levelMesh->Surfaces) - { - Level->levelMesh->BuildSurfaceParams(Level->LMTextureSize, Level->LMTextureSize, surface); - } - - Level->LMTexCoords = Level->levelMesh->LightmapUvs; - +#if 0 + Level->LMTexCoords = std::move(Level->levelMesh->LightmapUvs); +#endif // Allocate room for all surfaces @@ -3417,8 +3472,12 @@ void MapLoader::InitLightmap(MapData* map) l.Type = surface.type; l.LightmapNum = 0; - +#if 0 l.TexCoords = &Level->LMTexCoords[surface.startUvIndex]; +#else + l.TexCoords = &Level->levelMesh->LightmapUvs[surface.startUvIndex]; +#endif + l.LightmapNum = surface.atlasPageIndex; if (surface.type == ST_FLOOR || surface.type == ST_CEILING) { diff --git a/src/rendering/hwrenderer/doom_levelmesh.cpp b/src/rendering/hwrenderer/doom_levelmesh.cpp index 076bcaf9a..4f8fc100b 100644 --- a/src/rendering/hwrenderer/doom_levelmesh.cpp +++ b/src/rendering/hwrenderer/doom_levelmesh.cpp @@ -5,9 +5,11 @@ #include "texturemanager.h" #include "playsim/p_lnspec.h" + #include "c_dispatch.h" #include "g_levellocals.h" + CCMD(dumplevelmesh) { if (level.levelMesh) @@ -464,6 +466,125 @@ void DoomLevelMesh::DumpMesh(const FString& filename) const fclose(f); } +int DoomLevelMesh::SetupLightmapUvs(int lightmapSize) +{ + std::vector sortedSurfaces; + sortedSurfaces.reserve(Surfaces.Size()); + + for (auto& surface : Surfaces) + { + BuildSurfaceParams(lightmapSize, lightmapSize, surface); + sortedSurfaces.push_back(&surface); + } + + std::sort(sortedSurfaces.begin(), sortedSurfaces.end(), [](Surface* a, Surface* b) { return a->texHeight != b->texHeight ? a->texHeight > b->texHeight : a->texWidth > b->texWidth; }); + + RectPacker packer(lightmapSize, lightmapSize, RectPacker::Spacing(0)); + + for (Surface* surf : sortedSurfaces) + { + FinishSurface(lightmapSize, lightmapSize, packer, *surf); + } + + return packer.getNumPages(); +} + +void DoomLevelMesh::FinishSurface(int lightmapTextureWidth, int lightmapTextureHeight, RectPacker& packer, Surface& surface) +{ + int sampleWidth = surface.texWidth; + int sampleHeight = surface.texHeight; + + auto result = packer.insert(sampleWidth, sampleHeight); + int x = result.pos.x, y = result.pos.y; + surface.atlasPageIndex = result.pageIndex; + + // calculate final texture coordinates + auto uvIndex = surface.startUvIndex; + for (int i = 0; i < (int)surface.numVerts; i++) + { + auto& u = LightmapUvs[++uvIndex]; + auto& v = LightmapUvs[++uvIndex]; + u = (u + x) / (float)lightmapTextureWidth; + v = (v + y) / (float)lightmapTextureHeight; + } + + surface.atlasX = x; + surface.atlasY = y; + +#if 0 + while (result.pageIndex >= textures.size()) + { + textures.push_back(std::make_unique(textureWidth, textureHeight)); + } + + uint16_t* currentTexture = textures[surface->atlasPageIndex]->Pixels(); + + FVector3* colorSamples = surface->texPixels.data(); + // store results to lightmap texture + for (int i = 0; i < sampleHeight; i++) + { + for (int j = 0; j < sampleWidth; j++) + { + // get texture offset + int offs = ((textureWidth * (i + surface->atlasY)) + surface->atlasX) * 3; + + // convert RGB to bytes + currentTexture[offs + j * 3 + 0] = floatToHalf(clamp(colorSamples[i * sampleWidth + j].x, 0.0f, 65000.0f)); + currentTexture[offs + j * 3 + 1] = floatToHalf(clamp(colorSamples[i * sampleWidth + j].y, 0.0f, 65000.0f)); + currentTexture[offs + j * 3 + 2] = floatToHalf(clamp(colorSamples[i * sampleWidth + j].z, 0.0f, 65000.0f)); + } + } +#endif +} + +BBox DoomLevelMesh::GetBoundsFromSurface(const Surface& surface) const +{ + constexpr float M_INFINITY = 1e30; // TODO cleanup + + FVector3 low(M_INFINITY, M_INFINITY, M_INFINITY); + FVector3 hi(-M_INFINITY, -M_INFINITY, -M_INFINITY); + + for (int i = int(surface.startVertIndex); i < int(surface.startVertIndex) + surface.numVerts; i++) + { + for (int j = 0; j < 3; j++) + { + if (MeshVertices[i][j] < low[j]) + { + low[j] = MeshVertices[i][j]; + } + if (MeshVertices[i][j] > hi[j]) + { + hi[j] = MeshVertices[i][j]; + } + } + } + + BBox bounds; + bounds.Clear(); + bounds.min = low; + bounds.max = hi; + return bounds; +} + +DoomLevelMesh::PlaneAxis DoomLevelMesh::BestAxis(const secplane_t& p) +{ + float na = fabs(float(p.Normal().X)); + float nb = fabs(float(p.Normal().Y)); + float nc = fabs(float(p.Normal().Z)); + + // figure out what axis the plane lies on + if (na >= nb && na >= nc) + { + return AXIS_YZ; + } + else if (nb >= na && nb >= nc) + { + return AXIS_XZ; + } + + return AXIS_XY; +} + void DoomLevelMesh::BuildSurfaceParams(int lightMapTextureWidth, int lightMapTextureHeight, Surface& surface) { secplane_t* plane; diff --git a/src/rendering/hwrenderer/doom_levelmesh.h b/src/rendering/hwrenderer/doom_levelmesh.h index 5c301dcf0..e623e2e80 100644 --- a/src/rendering/hwrenderer/doom_levelmesh.h +++ b/src/rendering/hwrenderer/doom_levelmesh.h @@ -6,6 +6,9 @@ #include "vectors.h" #include "r_defs.h" #include "bounds.h" +#include "dp_rect_pack.h" + +typedef dp::rect_pack::RectPacker RectPacker; struct FLevelLocals; @@ -39,7 +42,12 @@ struct Surface int texHeight = 0; // UV coordinates for the vertices - int startUvIndex = -666; + int startUvIndex = 0; + + // Pixel coordinates in atlas + int atlasPageIndex = 0; + int atlasX = 0; + int atlasY = 0; }; class DoomLevelMesh : public hwrenderer::LevelMesh @@ -64,6 +72,8 @@ public: void DumpMesh(const FString& filename) const; + int SetupLightmapUvs(int lightmapSize); + private: void CreateSubsectorSurfaces(FLevelLocals &doomMap); void CreateCeilingSurface(FLevelLocals &doomMap, subsector_t *sub, sector_t *sector, int typeIndex, bool is3DFloor); @@ -93,35 +103,6 @@ private: // WIP internal lightmapper - BBox GetBoundsFromSurface(const Surface& surface) const - { - constexpr float M_INFINITY = 1e30; // TODO cleanup - - FVector3 low(M_INFINITY, M_INFINITY, M_INFINITY); - FVector3 hi(-M_INFINITY, -M_INFINITY, -M_INFINITY); - - for (int i = int(surface.startVertIndex); i < int(surface.startVertIndex) + surface.numVerts; i++) - { - for (int j = 0; j < 3; j++) - { - if (MeshVertices[i][j] < low[j]) - { - low[j] = MeshVertices[i][j]; - } - if (MeshVertices[i][j] > hi[j]) - { - hi[j] = MeshVertices[i][j]; - } - } - } - - BBox bounds; - bounds.Clear(); - bounds.min = low; - bounds.max = hi; - return bounds; - } - enum PlaneAxis { AXIS_YZ = 0, @@ -129,30 +110,11 @@ private: AXIS_XY }; - inline static PlaneAxis BestAxis(const secplane_t& p) - { - float na = fabs(float(p.Normal().X)); - float nb = fabs(float(p.Normal().Y)); - float nc = fabs(float(p.Normal().Z)); + static PlaneAxis BestAxis(const secplane_t& p); + BBox GetBoundsFromSurface(const Surface& surface) const; - // figure out what axis the plane lies on - if (na >= nb && na >= nc) - { - return AXIS_YZ; - } - else if (nb >= na && nb >= nc) - { - return AXIS_XZ; - } + inline int AllocUvs(int amount) { return LightmapUvs.Reserve(amount * 2); } - return AXIS_XY; - } - - int AllocUvs(int amount) - { - return LightmapUvs.Reserve(amount * 2); - } - - public: - void BuildSurfaceParams(int lightMapTextureWidth, int lightMapTextureHeight, Surface& surface); + void BuildSurfaceParams(int lightMapTextureWidth, int lightMapTextureHeight, Surface& surface); + void FinishSurface(int lightmapTextureWidth, int lightmapTextureHeight, RectPacker& packer, Surface& surface); };