Browse Source

Sample lightmaps to accumulate and average lighting values per vertex, then store those in the exported BSP file.

master
Nico de Poel 3 years ago
parent
commit
9f9bf49a86
  1. 42
      bsp.h
  2. 102
      main.cpp

42
bsp.h

@ -37,11 +37,14 @@ typedef struct // The BSP file header
typedef float scalar_t; // Scalar value, typedef float scalar_t; // Scalar value,
typedef struct // Vector or Position
typedef struct Vec3 // Vector or Position
{ {
scalar_t x; // horizontal scalar_t x; // horizontal
scalar_t y; // horizontal scalar_t y; // horizontal
scalar_t z; // vertical scalar_t z; // vertical
Vec3() : x(0), y(0), z(0) { }
Vec3(float x, float y, float z) : x(x), y(y), z(z) { }
} vec3_t; } vec3_t;
typedef struct typedef struct
@ -53,10 +56,35 @@ typedef struct
long type; // Type of plane, depending on normal vector. long type; // Type of plane, depending on normal vector.
} plane_t; } plane_t;
typedef struct // Bounding Box, Float values
typedef struct BoundBox // Bounding Box, Float values
{ {
vec3_t min; // minimum values of X,Y,Z vec3_t min; // minimum values of X,Y,Z
vec3_t max; // maximum values of X,Y,Z vec3_t max; // maximum values of X,Y,Z
BoundBox() { }
void init(vec3_t point)
{
min = point;
max = point;
}
void includePoint(vec3_t point)
{
if (point.x < min.x)
min.x = point.x;
if (point.y < min.y)
min.y = point.y;
if (point.z < min.z)
min.z = point.z;
if (point.x > max.x)
max.x = point.x;
if (point.y > max.y)
max.y = point.y;
if (point.z > max.z)
max.z = point.z;
}
} boundbox_t; } boundbox_t;
typedef struct // Bounding Box, Short values typedef struct // Bounding Box, Short values
@ -82,11 +110,16 @@ typedef struct // Mip Texture
unsigned long offset8; // offset to u_char Pix[width/8 * height/8] unsigned long offset8; // offset to u_char Pix[width/8 * height/8]
} miptex_t; } miptex_t;
typedef struct
typedef struct Vertex
{ {
float X; // X,Y,Z coordinates of the vertex float X; // X,Y,Z coordinates of the 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()
{
return vec3_t(X, Y, Z);
}
} vertex_t; } vertex_t;
typedef struct typedef struct
@ -181,4 +214,7 @@ typedef struct
int entitiesLength; int entitiesLength;
char* entities; char* entities;
int lightmapLength;
unsigned char* lightmap;
} world_t; } world_t;

102
main.cpp

@ -164,6 +164,52 @@ static void leaf_zone(const dleaf_t* leaf, short zone[3])
zone[2] = midZ & mask; zone[2] = midZ & mask;
} }
static unsigned char sample_lightmap(const world_t* world, const face_t *face, const BoundBox& bounds, const Vec3& point)
{
if (face->lightmap < 0)
return 0;
const unsigned char* lightmap = &world->lightmap[face->lightmap];
const plane_t* plane = &world->planes[face->plane_id];
float w, h, u, v;
switch (plane->type)
{
case 0:
case 3:
// 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;
break;
case 1:
case 4:
// 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;
break;
case 2:
case 5:
// 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;
break;
default:
printf("Error: unknown plane type %d\n", plane->type);
return 0;
}
int width = (int)(w / 16);
int height = (int)(h / 16);
return lightmap[(int)((height * v + u) * width)];
}
int process_faces(const world_t* world) int process_faces(const world_t* world)
{ {
// Write some data to a file // Write some data to a file
@ -185,10 +231,18 @@ 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;
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).
if (inVertex->X > -8192 && inVertex->X < 8192 && inVertex->Y > -8192 && inVertex->Y < 8192 && inVertex->Z > -8192 && inVertex->Z < 8192) if (inVertex->X > -8192 && inVertex->X < 8192 && inVertex->Y > -8192 && inVertex->Y < 8192 && inVertex->Z > -8192 && inVertex->Z < 8192)
@ -197,10 +251,11 @@ int process_faces(const world_t* world)
outVertex.y = (short)(inVertex->Y * 4); outVertex.y = (short)(inVertex->Y * 4);
outVertex.z = (short)(inVertex->Z * 4); outVertex.z = (short)(inVertex->Z * 4);
} }
outVertex.baseLight = 128;
outVertex.finalLight = 128;
outVertex.baseLight = 0;
outVertex.finalLight = 0;
outVertex.r = 0;
fwrite(&outVertex, sizeof(ps1bsp_vertex_t), 1, fbsp);
outVertices.push_back(outVertex);
} }
std::vector<ps1bsp_triangle_t> outTriangles; std::vector<ps1bsp_triangle_t> outTriangles;
@ -224,32 +279,38 @@ int process_faces(const world_t* world)
world->edges[-edgeIdx].vertex1; world->edges[-edgeIdx].vertex1;
faceVertIndices.push_back(vertIndex); faceVertIndices.push_back(vertIndex);
}
//printf("Face %d: %d vertices\n", faceIdx, faceVerts.size());
// Triangulate face into polygons (triangle fan, the naive method)
// TODO better method: generate a quad strip topology
ps1bsp_triangle_t outTriangle;
outTriangle.vertex0 = faceVertIndices[0];
for (int faceVertIdx = 1; faceVertIdx < faceVertIndices.size() - 1; ++faceVertIdx)
unsigned char light = sample_lightmap(world, face, bounds, world->vertices[vertIndex].toVec());
if (light > 0)
{ {
outTriangle.vertex1 = faceVertIndices[faceVertIdx];
outTriangle.vertex2 = faceVertIndices[faceVertIdx + 1];
outTriangles.push_back(outTriangle);
*(unsigned short*)(&outVertices[vertIndex].baseLight) += light;
outVertices[vertIndex].r++;
}
} }
outFace.numVertices = faceVertIndices.size() - outFace.firstVertexIndex; outFace.numVertices = faceVertIndices.size() - outFace.firstVertexIndex;
outFaces.push_back(outFace); outFaces.push_back(outFace);
} }
// Average the lightmap values for this vertex
for (auto iter = outVertices.begin(); iter != outVertices.end(); ++iter)
{
unsigned char count = (*iter).r;
if (count == 0)
continue;
unsigned short accumulate = *(unsigned short*)(&(*iter).baseLight);
(*iter).baseLight = accumulate / count;
(*iter).r = 0;
}
// Write triangle and face data to file // Write triangle and face data to file
fwrite(outVertices.data(), sizeof(ps1bsp_vertex_t), outVertices.size(), fbsp);
fwrite(faceVertIndices.data(), sizeof(unsigned short), faceVertIndices.size(), fbsp); fwrite(faceVertIndices.data(), sizeof(unsigned short), faceVertIndices.size(), fbsp);
fwrite(outFaces.data(), sizeof(ps1bsp_face_t), outFaces.size(), fbsp); fwrite(outFaces.data(), sizeof(ps1bsp_face_t), outFaces.size(), fbsp);
// Update header information // Update header information
outHeader.numVertices = world->numVertices;
outHeader.numVertices = outVertices.size();
outHeader.numFaceVertIndices = faceVertIndices.size(); outHeader.numFaceVertIndices = faceVertIndices.size();
outHeader.numFaces = outFaces.size(); outHeader.numFaces = outFaces.size();
@ -440,6 +501,15 @@ int load_bsp(const char* bspname, world_t* world)
fseek(f, header->leaves.offset, SEEK_SET); fseek(f, header->leaves.offset, SEEK_SET);
fread(world->leaves, sizeof(dleaf_t), world->numLeaves, f); fread(world->leaves, sizeof(dleaf_t), world->numLeaves, f);
// Load lightmaps
world->lightmapLength = header->lightmaps.size / sizeof(unsigned char);
world->lightmap = (unsigned char*)malloc(header->lightmaps.size);
if (world->lightmap == NULL)
return 0;
fseek(f, header->lightmaps.offset, SEEK_SET);
fread(world->lightmap, sizeof(unsigned char), world->lightmapLength, f);
fclose(f); fclose(f);
return 1; return 1;
} }

Loading…
Cancel
Save