- add support for tracing light cells

This commit is contained in:
Magnus Norddahl 2018-11-07 01:31:50 +01:00
parent 639bc29085
commit 2d970672e8
3 changed files with 169 additions and 25 deletions

View file

@ -51,8 +51,6 @@
extern int Multisample;
extern thread_local kexVec3 *colorSamples;
const kexVec3 kexLightmapBuilder::gridSize(64, 64, 128);
kexLightmapBuilder::kexLightmapBuilder()
{
}
@ -165,7 +163,7 @@ kexBBox kexLightmapBuilder::GetBoundsFromSurface(const surface_t *surface)
// Traces to the ceiling surface. Will emit light if the surface that was traced is a sky
bool kexLightmapBuilder::EmitFromCeiling(const surface_t *surface, const kexVec3 &origin, const kexVec3 &normal, kexVec3 &color)
{
float attenuation = normal.Dot(map->GetSunDirection());
float attenuation = surface ? normal.Dot(map->GetSunDirection()) : 1.0f;
if (attenuation <= 0)
{
@ -211,7 +209,10 @@ static float radians(float degrees)
// Traces a line from the texel's origin to the sunlight direction and against all nearby thing lights
kexVec3 kexLightmapBuilder::LightTexelSample(const kexVec3 &origin, surface_t *surface)
{
kexPlane plane = surface->plane;
kexPlane plane;
if (surface)
plane = surface->plane;
kexVec3 color(0.0f, 0.0f, 0.0f);
// check all thing lights
@ -227,7 +228,7 @@ kexVec3 kexLightmapBuilder::LightTexelSample(const kexVec3 &origin, surface_t *s
kexVec3 lightOrigin(tl->origin.x, tl->origin.y, originZ);
if (plane.Distance(lightOrigin) - plane.d < 0)
if (surface && plane.Distance(lightOrigin) - plane.d < 0)
{
// completely behind the plane
continue;
@ -274,7 +275,8 @@ kexVec3 kexLightmapBuilder::LightTexelSample(const kexVec3 &origin, surface_t *s
float attenuation = 1.0f - (dist / radius);
attenuation *= spotAttenuation;
attenuation *= plane.Normal().Dot(dir);
if (surface)
attenuation *= plane.Normal().Dot(dir);
attenuation *= intensity;
// accumulate results
@ -283,10 +285,10 @@ kexVec3 kexLightmapBuilder::LightTexelSample(const kexVec3 &origin, surface_t *s
tracedTexels++;
}
if (surface->type != ST_CEILING)
if (!surface || surface->type != ST_CEILING)
{
// see if it's exposed to sunlight
if (EmitFromCeiling(surface, origin, plane.Normal(), color))
if (EmitFromCeiling(surface, origin, surface ? plane.Normal() : kexVec3::vecUp, color))
tracedTexels++;
}
@ -534,26 +536,18 @@ void kexLightmapBuilder::TraceSurface(surface_t *surface)
void kexLightmapBuilder::LightSurface(const int surfid)
{
float remaining;
int numsurfs = surfaces.size();
// TODO: this should NOT happen, but apparently, it can randomly occur
if (surfaces.size() == 0)
{
return;
}
BuildSurfaceParams(surfaces[surfid].get());
TraceSurface(surfaces[surfid].get());
std::unique_lock<std::mutex> lock(mutex);
int numsurfs = surfaces.size();
int lastproc = processed * 100 / numsurfs;
processed++;
int curproc = processed * 100 / numsurfs;
if (lastproc != curproc || processed == 1)
{
remaining = (float)processed / (float)numsurfs;
float remaining = (float)processed / (float)numsurfs;
printf("%i%c surfaces done\r", (int)(remaining * 100.0f), '%');
}
}
@ -562,8 +556,21 @@ void kexLightmapBuilder::CreateLightmaps(FLevel &doomMap)
{
map = &doomMap;
printf("-------------- Tracing cells ---------------\n");
SetupLightCellGrid();
processed = 0;
tracedTexels = 0;
kexWorker::RunJob(grid.blocks.size(), [=](int id) {
LightBlock(id);
});
printf("Cells traced: %i \n\n", tracedTexels);
printf("------------- Tracing surfaces -------------\n");
tracedTexels = 0;
processed = 0;
kexWorker::RunJob(surfaces.size(), [=](int id) {
LightSurface(id);
@ -572,6 +579,120 @@ void kexLightmapBuilder::CreateLightmaps(FLevel &doomMap)
printf("Texels traced: %i \n\n", tracedTexels);
}
void kexLightmapBuilder::SetupLightCellGrid()
{
kexBBox worldBBox = map->CollisionMesh->get_bbox();
float blockWorldSize = LIGHTCELL_BLOCK_SIZE * LIGHTCELL_SIZE;
grid.x = static_cast<int>(std::floor(worldBBox.min.x / blockWorldSize));
grid.y = static_cast<int>(std::floor(worldBBox.min.y / blockWorldSize));
grid.width = static_cast<int>(std::ceil(worldBBox.max.x / blockWorldSize + 1.0f) - 1.0f) - grid.x;
grid.height = static_cast<int>(std::ceil(worldBBox.max.y / blockWorldSize + 1.0f) - 1.0f) - grid.y;
grid.blocks.resize(grid.width * grid.height);
}
void kexLightmapBuilder::LightBlock(int id)
{
float blockWorldSize = LIGHTCELL_BLOCK_SIZE * LIGHTCELL_SIZE;
// Locate block in world
LightCellBlock &block = grid.blocks[id];
int x = grid.x + id % grid.width;
int y = grid.y + id / grid.height;
float worldX = blockWorldSize * x + 0.5f * LIGHTCELL_SIZE;
float worldY = blockWorldSize * y + 0.5f * LIGHTCELL_SIZE;
// Analyze for cells
IntSector *sectors[LIGHTCELL_BLOCK_SIZE * LIGHTCELL_BLOCK_SIZE];
float ceilings[LIGHTCELL_BLOCK_SIZE * LIGHTCELL_BLOCK_SIZE];
float floors[LIGHTCELL_BLOCK_SIZE * LIGHTCELL_BLOCK_SIZE];
float maxCeiling = -M_INFINITY;
float minFloor = M_INFINITY;
for (int yy = 0; yy < LIGHTCELL_BLOCK_SIZE; yy++)
{
for (int xx = 0; xx < LIGHTCELL_BLOCK_SIZE; xx++)
{
int idx = xx + yy * LIGHTCELL_BLOCK_SIZE;
float cellWorldX = worldX + xx * LIGHTCELL_SIZE;
float cellWorldY = worldY + yy * LIGHTCELL_SIZE;
MapSubsectorEx *subsector = map->PointInSubSector(cellWorldX, cellWorldY);
if (subsector)
{
IntSector *sector = map->GetSectorFromSubSector(subsector);
float ceiling = sector->ceilingplane.zAt(cellWorldX, cellWorldY);
float floor = sector->floorplane.zAt(cellWorldX, cellWorldY);
sectors[idx] = sector;
ceilings[idx] = ceiling;
floors[idx] = floor;
maxCeiling = std::max(maxCeiling, ceiling);
minFloor = std::min(minFloor, floor);
}
else
{
sectors[idx] = nullptr;
ceilings[idx] = -M_INFINITY;
floors[idx] = M_INFINITY;
}
}
}
if (minFloor != M_INFINITY)
{
// Allocate space for the cells
block.z = static_cast<int>(std::floor(minFloor / LIGHTCELL_SIZE));
block.layers = static_cast<int>(std::ceil(maxCeiling / LIGHTCELL_SIZE + 1.0f) - 1.0f) - block.z;
block.cells.Resize(LIGHTCELL_BLOCK_SIZE * LIGHTCELL_BLOCK_SIZE * block.layers);
// Ray trace the cells
for (int yy = 0; yy < LIGHTCELL_BLOCK_SIZE; yy++)
{
for (int xx = 0; xx < LIGHTCELL_BLOCK_SIZE; xx++)
{
int idx = xx + yy * LIGHTCELL_BLOCK_SIZE;
float cellWorldX = worldX + xx * LIGHTCELL_SIZE;
float cellWorldY = worldY + yy * LIGHTCELL_SIZE;
for (int zz = 0; zz < block.layers; zz++)
{
float cellWorldZ = (block.z + zz + 0.5f) * LIGHTCELL_SIZE;
kexVec3 color;
if (cellWorldZ > floors[idx] && cellWorldZ < ceilings[idx])
{
color = LightTexelSample({ cellWorldX, cellWorldY, cellWorldZ }, nullptr);
}
else
{
color = { 0.0f, 0.0f, 0.0f };
}
block.cells[idx + zz * LIGHTCELL_BLOCK_SIZE * LIGHTCELL_BLOCK_SIZE] = color;
}
}
}
}
else
{
// Entire block is outside the map
block.z = 0;
block.layers = 0;
}
std::unique_lock<std::mutex> lock(mutex);
int numblocks = grid.blocks.size();
int lastproc = processed * 100 / numblocks;
processed++;
int curproc = processed * 100 / numblocks;
if (lastproc != curproc || processed == 1)
{
float remaining = (float)processed / (float)numblocks;
printf("%i%c cells done\r", (int)(remaining * 100.0f), '%');
}
}
void kexLightmapBuilder::AddLightmapLump(FWadWriter &wadFile)
{
// Calculate size of lump

View file

@ -28,22 +28,39 @@
#pragma once
#include "surfaces.h"
#include "framework/tarray.h"
#include <mutex>
#define LIGHTMAP_MAX_SIZE 1024
#define LIGHTCELL_SIZE 64
#define LIGHTCELL_BLOCK_SIZE 16
class FWadWriter;
class LightCellBlock
{
public:
int z;
int layers;
TArray<kexVec3> cells;
};
class LightCellGrid
{
public:
int x, y;
int width, height;
std::vector<LightCellBlock> blocks;
};
class kexLightmapBuilder
{
public:
kexLightmapBuilder();
~kexLightmapBuilder();
void BuildSurfaceParams(surface_t *surface);
void TraceSurface(surface_t *surface);
void CreateLightmaps(FLevel &doomMap);
void LightSurface(const int surfid);
//void WriteTexturesToTGA();
void WriteMeshToOBJ();
void AddLightmapLump(FWadWriter &wadFile);
@ -53,8 +70,6 @@ public:
int textureWidth = 128;
int textureHeight = 128;
static const kexVec3 gridSize;
private:
void NewTexture();
bool MakeRoomForBlock(const int width, const int height, int *x, int *y, int *num);
@ -62,6 +77,12 @@ private:
kexVec3 LightTexelSample(const kexVec3 &origin, surface_t *surface);
bool EmitFromCeiling(const surface_t *surface, const kexVec3 &origin, const kexVec3 &normal, kexVec3 &color);
void BuildSurfaceParams(surface_t *surface);
void TraceSurface(surface_t *surface);
void SetupLightCellGrid();
void LightBlock(int blockid);
void LightSurface(const int surfid);
FLevel *map;
std::vector<std::vector<uint16_t>> textures;
std::vector<std::vector<int>> allocBlocks;
@ -69,6 +90,8 @@ private:
int extraSamples = 2;
int tracedTexels = 0;
LightCellGrid grid;
std::mutex mutex;
int processed = 0;
};

View file

@ -247,7 +247,7 @@ float kexLightSurface::TraceSurface(FLevel *map, const surface_t *fragmentSurfac
return 1.0f; // light surface will always be fullbright
kexVec3 lightSurfaceNormal = surface->plane.Normal();
kexVec3 fragmentNormal = fragmentSurface->plane.Normal();
kexVec3 fragmentNormal = fragmentSurface ? fragmentSurface->plane.Normal() : kexVec3::vecUp;
float gzdoomRadiusScale = 2.0f; // 2.0 because gzdoom's dynlights do this and we want them to match
@ -266,7 +266,7 @@ float kexLightSurface::TraceSurface(FLevel *map, const surface_t *fragmentSurfac
count++;
float attenuation = kexVec3::Dot(lightDir, fragmentNormal);
float attenuation = fragmentSurface ? kexVec3::Dot(lightDir, fragmentNormal) : 1.0f;
if (attenuation <= 0.0f)
continue; // not even facing the light surface