mirror of
https://github.com/ZDoom/gzdoom.git
synced 2025-03-13 22:42:07 +00:00
Atlas UV mapping
This commit is contained in:
parent
e09ac00121
commit
73b968d79a
3 changed files with 215 additions and 73 deletions
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue