Atlas UV mapping

This commit is contained in:
RaveYard 2023-08-31 11:25:24 +02:00 committed by Christoph Oelckers
parent e09ac00121
commit 73b968d79a
3 changed files with 215 additions and 73 deletions

View file

@ -3336,39 +3336,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<uint16_t>& buffer, int width, int height) {
auto constructDebugTexture = [&](TArray<uint16_t>& 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
@ -3412,8 +3467,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)
{

View file

@ -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<Surface*> 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<LightmapTexture>(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;

View file

@ -6,6 +6,9 @@
#include "vectors.h"
#include "r_defs.h"
#include "bounds.h"
#include "dp_rect_pack.h"
typedef dp::rect_pack::RectPacker<int> 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);
};