Browse Source

Fixed lightmap sampling:

- Implement bounds calculation per face instead of for all faces, which was silly
- Face lightmap width and height calculations now match what Quake does
- Added an export function for lightmap data, to make debugging easier
- Include face baselight into vertex lighting, if/when that ever makes a difference
master
Nico de Poel 3 years ago
parent
commit
c429529e88
  1. 2
      bsp.h
  2. 113
      main.cpp

2
bsp.h

@ -116,7 +116,7 @@ typedef struct Vertex
float Y; // usually some integer value float Y; // usually some integer value
float Z; // but coded in floating point float Z; // but coded in floating point
vec3_t toVec()
vec3_t toVec() const
{ {
return vec3_t(X, Y, Z); return vec3_t(X, Y, Z);
} }

113
main.cpp

@ -172,42 +172,94 @@ static unsigned char sample_lightmap(const world_t* world, const face_t *face, c
const unsigned char* lightmap = &world->lightmap[face->lightmap]; const unsigned char* lightmap = &world->lightmap[face->lightmap];
const plane_t* plane = &world->planes[face->plane_id]; const plane_t* plane = &world->planes[face->plane_id];
float w, h, u, v;
int width, height;
float u, v;
switch (plane->type) switch (plane->type)
{ {
case 0: case 0:
case 3: case 3:
// Towards X // Towards X
w = bounds.max.y - bounds.min.y;
h = bounds.max.z - bounds.min.z;
u = (point.y - bounds.min.y) / w;
v = (point.z - bounds.min.z) / h;
width = (int)(ceil(bounds.max.y / 16) - floor(bounds.min.y / 16)) * 16;
height = (int)(ceil(bounds.max.z / 16) - floor(bounds.min.z / 16)) * 16;
u = (point.y - bounds.min.y) / width;
v = (point.z - bounds.min.z) / height;
break; break;
case 1: case 1:
case 4: case 4:
// Towards Y // Towards Y
w = bounds.max.x - bounds.min.x;
h = bounds.max.z - bounds.min.z;
u = (point.x - bounds.min.x) / w;
v = (point.z - bounds.min.z) / h;
width = (int)(ceil(bounds.max.x / 16) - floor(bounds.min.x / 16)) * 16;
height = (int)(ceil(bounds.max.z / 16) - floor(bounds.min.z / 16)) * 16;
u = (point.x - bounds.min.x) / width;
v = (point.z - bounds.min.z) / height;
break; break;
case 2: case 2:
case 5: case 5:
// Towards Z // Towards Z
w = bounds.max.x - bounds.min.x;
h = bounds.max.y - bounds.min.y;
u = (point.x - bounds.min.x) / w;
v = (point.y - bounds.min.y) / h;
width = (int)(ceil(bounds.max.x / 16) - floor(bounds.min.x / 16)) * 16;
height = (int)(ceil(bounds.max.y / 16) - floor(bounds.min.y / 16)) * 16;
u = (point.x - bounds.min.x) / width;
v = (point.y - bounds.min.y) / height;
break; break;
default: default:
printf("Error: unknown plane type %d\n", plane->type); printf("Error: unknown plane type %d\n", plane->type);
return 0; return 0;
} }
int width = (int)(w / 16);
int height = (int)(h / 16);
height >>= 4;
width >>= 4;
return lightmap[(int)((height * v + u) * width)];
return lightmap[(int)(v * (width + 1) + u)];
}
static void export_lightmap(const world_t* world, const face_t* face, const BoundBox& bounds, int faceIdx)
{
if (face->lightmap < 0)
return;
const unsigned char* lightmap = &world->lightmap[face->lightmap];
const plane_t* plane = &world->planes[face->plane_id];
int width, height;
switch (plane->type)
{
case 0:
case 3:
// Towards X
width = ceil(bounds.max.y / 16) - floor(bounds.min.y / 16);
height = ceil(bounds.max.z / 16) - floor(bounds.min.z / 16);
break;
case 1:
case 4:
// Towards Y
width = ceil(bounds.max.x / 16) - floor(bounds.min.x / 16);
height = ceil(bounds.max.z / 16) - floor(bounds.min.z / 16);
break;
case 2:
case 5:
// Towards Z
width = ceil(bounds.max.x / 16) - floor(bounds.min.x / 16);
height = ceil(bounds.max.y / 16) - floor(bounds.min.y / 16);
break;
default:
printf("Error: unknown plane type %d\n", plane->type);
return;
}
width += 1;
char path[_MAX_PATH];
sprintf_s(path, _MAX_PATH, "lightmap_face%d_e%d_PT%d_%dx%d.raw", faceIdx, face->ledge_num, plane->type, width, height);
FILE* flm;
fopen_s(&flm, path, "wb");
if (!flm)
return;
for (int y = 0; y < height; ++y)
{
fwrite(&lightmap[y * width], sizeof(unsigned char), width, flm);
}
fclose(flm);
} }
int process_faces(const world_t* world) int process_faces(const world_t* world)
@ -231,17 +283,11 @@ int process_faces(const world_t* world)
} }
// Write vertex data to a file (no vertex splitting yet) // Write vertex data to a file (no vertex splitting yet)
BoundBox bounds;
std::vector<ps1bsp_vertex_t> outVertices; std::vector<ps1bsp_vertex_t> outVertices;
for (unsigned short i = 0; i < world->numVertices; ++i) for (unsigned short i = 0; i < world->numVertices; ++i)
{ {
// TODO: we should respect the ordering from vertexRef->index here but meh, problem for later // TODO: we should respect the ordering from vertexRef->index here but meh, problem for later
vertex_t* inVertex = &world->vertices[i]; vertex_t* inVertex = &world->vertices[i];
if (i == 0)
bounds.init(inVertex->toVec());
else
bounds.includePoint(inVertex->toVec());
ps1bsp_vertex_t outVertex = { 0 }; ps1bsp_vertex_t outVertex = { 0 };
// Ensure we don't overflow 16-bit short values. Most Quake maps will stay within these bounds so it *should* be fine (for now). // Ensure we don't overflow 16-bit short values. Most Quake maps will stay within these bounds so it *should* be fine (for now).
@ -270,6 +316,7 @@ int process_faces(const world_t* world)
outFace.firstVertexIndex = faceVertIndices.size(); outFace.firstVertexIndex = faceVertIndices.size();
// Traverse the list of face edges to collect all of the face's vertices // Traverse the list of face edges to collect all of the face's vertices
BoundBox bounds;
for (int edgeListIdx = 0; edgeListIdx < face->ledge_num; ++edgeListIdx) for (int edgeListIdx = 0; edgeListIdx < face->ledge_num; ++edgeListIdx)
{ {
int edgeIdx = world->edgeList[face->ledge_id + edgeListIdx]; int edgeIdx = world->edgeList[face->ledge_id + edgeListIdx];
@ -280,14 +327,30 @@ int process_faces(const world_t* world)
faceVertIndices.push_back(vertIndex); faceVertIndices.push_back(vertIndex);
unsigned char light = sample_lightmap(world, face, bounds, world->vertices[vertIndex].toVec());
if (light > 0)
// Calculate bounding box of this face
const vertex_t* vertex = &world->vertices[vertIndex];
if (edgeListIdx == 0)
bounds.init(vertex->toVec());
else
bounds.includePoint(vertex->toVec());
}
// Accumulate lightmap contribution of this face on each vertex
if (face->lightmap >= 0)
{
for (int indexIdx = outFace.firstVertexIndex; indexIdx < faceVertIndices.size(); ++indexIdx)
{ {
*(unsigned short*)(&outVertices[vertIndex].baseLight) += light;
int vertIndex = faceVertIndices[indexIdx];
unsigned char light = sample_lightmap(world, face, bounds, world->vertices[vertIndex].toVec());
*(unsigned short*)(&outVertices[vertIndex].baseLight) += light + (0xFF - face->baselight);
outVertices[vertIndex].r++; outVertices[vertIndex].r++;
} }
} }
// For visualizing and debugging lightmaps
//if (face->ledge_num >= 10)
// export_lightmap(world, face, bounds, faceIdx);
outFace.numVertices = faceVertIndices.size() - outFace.firstVertexIndex; outFace.numVertices = faceVertIndices.size() - outFace.firstVertexIndex;
outFaces.push_back(outFace); outFaces.push_back(outFace);
} }

Loading…
Cancel
Save