Browse Source

Further improvement to lightmapping system:

- Added a function to find all faces that contain a given point. This is useful for both sampling lightmaps, and sampling textures later.
- New face vertex lighting function simply collects all faces for a given vertex, and samples all relevant lightmaps for it
- A whole bunch more experiments that were more complex and that went nowhere
- Ironically all this doesn't really change the current quality of the lighting but hey, that's a result too. And this added code will prove useful soon enough.
master
Nico de Poel 3 years ago
parent
commit
e0373524ca
  1. 1
      common.h
  2. 229
      lighting.cpp
  3. 11
      lighting.h
  4. 3
      main.cpp

1
common.h

@ -1,4 +1,5 @@
#pragma once #pragma once
#define _USE_MATH_DEFINES
#include <memory.h> #include <memory.h>
#include <cstdlib> #include <cstdlib>
#include <cstdio> #include <cstdio>

229
lighting.cpp

@ -250,7 +250,236 @@ unsigned char compute_faceVertex_light2(const world_t* world, const face_t* face
return (unsigned char)(light / numSamples); return (unsigned char)(light / numSamples);
} }
// Start with a hash set of all faces
// From the first face onward, group together faces into an interconnected surface based on angle between faces, using a flood-fill type of approach:
// - Add the first face to a surface face list
// - Take the next face from the surface face list (use index to go to next, don't remove it from the surface list)
// - For each face vertex, look up all other faces connected to that vertex
// - If the other face is not present in the original hash set, skip it (it was already added to a surface)
// - If the other face has an angle > 60 degrees with the current face, skip it (sharp edge)
// - Add the other face to the surface face list, remove it from the hash set
// - Repeat from step 'take the next face'
SurfaceList group_surfaces(const world_t* world, const VertexFaces& vertexFaces)
{
SurfaceList surfaces;
std::unordered_set<const face_t*> faceSet;
for (int i = 0; i < world->numFaces; ++i)
faceSet.insert(&world->faces[i]);
while (!faceSet.empty())
{
const face_t* firstFace = *faceSet.begin();
faceSet.erase(firstFace);
std::vector<const face_t*> surfaceFaces;
surfaceFaces.push_back(firstFace);
for (size_t faceIdx = 0; faceIdx < surfaceFaces.size(); ++faceIdx)
{
const face_t* thisFace = surfaceFaces[faceIdx];
const plane_t* thisPlane = &world->planes[thisFace->plane_id];
vec3_t thisNormal = thisFace->side ? -thisPlane->normal : thisPlane->normal;
for (int edgeListIdx = 0; edgeListIdx < thisFace->ledge_num; ++edgeListIdx)
{
int edgeIdx = world->edgeList[thisFace->ledge_id + edgeListIdx];
const edge_t* edge = &world->edges[abs(edgeIdx)];
for (int v = 0; v < 1; ++v)
{
unsigned short vertIndex = *(&edge->vertex0 + v);
const vertex_t* vertex = &world->vertices[vertIndex];
auto vertexFaceIter = vertexFaces.find(vertex);
if (vertexFaceIter == vertexFaces.end())
{
printf("Couldn't find list of faces for vertex %d, weird...\n", vertIndex);
continue;
}
for (auto faceIter = vertexFaceIter->second.begin(); faceIter != vertexFaceIter->second.end(); ++faceIter)
{
const face_t* otherFace = *faceIter;
if (faceSet.find(otherFace) == faceSet.end())
continue; // Face has already been added to a surface, skip it
const plane_t* otherPlane = &world->planes[otherFace->plane_id];
vec3_t otherNormal = otherFace->side ? -otherPlane->normal : otherPlane->normal;
float dot = thisNormal.dotProduct(otherNormal);
if (dot < 0.5f)
continue; // Sharp edge, face belongs to a different surface
// Add face to this surface and make sure it won't be reconsidered for any other surfaces
surfaceFaces.push_back(otherFace);
faceSet.erase(otherFace);
}
}
}
}
Surface surface;
surface.faces.insert(surfaceFaces.begin(), surfaceFaces.end());
surfaces.push_back(surface);
}
return surfaces;
}
// To sample lightmap at any arbitrary world position:
// - Find the surface that the face belongs to
// - Select all faces from the surface where the position lies in its plane and is inside polygon boundaries
// - Sample those faces at the specified position (sample_lightmap) and average
std::vector<const face_t*> find_facesWithPoint(const world_t* world, const face_t* refFace, const SurfaceList& surfaces, Vec3 point)
{
std::vector<const face_t*> faces;
// Find the surface that the face belongs to
for (auto surfIter = surfaces.begin(); surfIter != surfaces.end(); ++surfIter)
{
if (surfIter->faces.find(refFace) == surfIter->faces.end())
continue;
// Select all faces from the surface where the point lies in its plane and is inside polygon boundaries
for (auto faceIter = surfIter->faces.begin(); faceIter != surfIter->faces.end(); ++faceIter)
{
const face_t* face = *faceIter;
const plane_t* plane = &world->planes[face->plane_id];
// Check if the point lies on the face's plane
float pointPlaneDist = (point.x * plane->normal.x + point.y * plane->normal.y + point.z * plane->normal.z) - plane->dist;
if (fabs(pointPlaneDist) > FLT_EPSILON)
continue;
// Simple case first: check if this point lies on one of the face's edges
bool onEdge = false;
for (int edgeListIdx = 0; edgeListIdx < face->ledge_num; ++edgeListIdx)
{
int edgeIdx = world->edgeList[face->ledge_id + edgeListIdx];
const edge_t* edge = &world->edges[abs(edgeIdx)];
Vec3 vert0 = world->vertices[edge->vertex0].toVec();
Vec3 vert1 = world->vertices[edge->vertex1].toVec();
Vec3 dir0 = vert1 - vert0;
Vec3 dir1 = point - vert0;
float mag0 = dir0.magnitude();
float mag1 = dir1.magnitude();
if (mag1 / mag0 <= 1.0f + FLT_EPSILON && dir0.dotProduct(dir1) >= (mag0 - FLT_EPSILON) * (mag1 - FLT_EPSILON))
onEdge = true;
}
if (onEdge)
faces.push_back(face);
}
break;
}
return faces;
}
// Further improvements: // Further improvements:
// - Reconstruct connected surfaces through vertices and edges, so we can fully sample all adjacent lightmaps. // - Reconstruct connected surfaces through vertices and edges, so we can fully sample all adjacent lightmaps.
// Right now we don't properly detect when one face has vertices halfway along the edge of an adjacent face. // Right now we don't properly detect when one face has vertices halfway along the edge of an adjacent face.
// - Sample more lightmap points around each vertex to obtain a more representative average value // - Sample more lightmap points around each vertex to obtain a more representative average value
unsigned char compute_faceVertex_light3(const world_t* world, const face_t* refFace, const SurfaceList& surfaces, const FaceBounds& faceBounds, Vec3 point)
{
auto faces = find_facesWithPoint(world, refFace, surfaces, point);
if (faces.empty())
return 0;
unsigned int light = 0;
for (auto faceIter = faces.begin(); faceIter != faces.end(); ++faceIter)
{
const face_t* face = *faceIter;
light += sample_lightmap(world, face, faceBounds.find(face)->second, point) + (0xFF - face->baselight);
}
return (unsigned char)(light / faces.size());
}
// Find the list of all faces that contain the given point, i.e. the point lies on the face's plane and is contained within its polygon bounds
std::vector<const face_t*> world_facesWithPoint(const world_t* world, Vec3 point)
{
std::vector<const face_t*> faces;
for (int faceIdx = 0; faceIdx < world->numFaces; ++faceIdx)
{
const face_t* face = &world->faces[faceIdx];
const plane_t* plane = &world->planes[face->plane_id];
// Check if the point lies on the face's plane (it's not strictly necessary to do this, but this check makes the whole function a lot faster)
double pointPlaneDist = ((double)point.x * plane->normal.x + (double)point.y * plane->normal.y + (double)point.z * plane->normal.z) - plane->dist;
if (fabs(pointPlaneDist) > 0.0001)
continue;
// Check if the point is contained within the face's polygon
double angleSum = 0;
for (int edgeListIdx = 0; edgeListIdx < face->ledge_num; ++edgeListIdx)
{
int edgeIdx = world->edgeList[face->ledge_id + edgeListIdx];
Vec3 v0, v1;
if (edgeIdx > 0)
{
const edge_t* edge = &world->edges[edgeIdx];
v0 = world->vertices[edge->vertex0].toVec();
v1 = world->vertices[edge->vertex1].toVec();
}
else
{
const edge_t* edge = &world->edges[-edgeIdx];
v0 = world->vertices[edge->vertex1].toVec();
v1 = world->vertices[edge->vertex0].toVec();
}
Vec3 p0 = v0 - point;
Vec3 p1 = v1 - point;
double m0 = p0.magnitude();
double m1 = p1.magnitude();
if ((m0 * m1) <= 0.0001)
{
faces.push_back(face);
break;
}
double dot = p0.dotProduct(p1) / (m0 * m1);
angleSum += acos(dot);
}
if (fabs(2 * M_PI - angleSum) <= 0.0001)
faces.push_back(face);
}
return faces;
}
unsigned char compute_faceVertex_light4(const world_t* world, const face_t* refFace, const FaceBounds& faceBounds, Vec3 point)
{
auto faces = world_facesWithPoint(world, point);
if (faces.empty())
return 0;
const plane_t* refPlane = &world->planes[refFace->plane_id];
vec3_t refNormal = refFace->side ? -refPlane->normal : refPlane->normal;
unsigned int light = 0, numSamples = 0;
for (auto faceIter = faces.begin(); faceIter != faces.end(); ++faceIter)
{
const face_t* face = *faceIter;
const plane_t* plane = &world->planes[face->plane_id];
vec3_t normal = face->side ? -plane->normal : plane->normal;
// Check if the face is at a shallow angle with the reference face
float dot = normal.dotProduct(refNormal);
if (dot < 0.5f)
continue;
light += sample_lightmap(world, face, faceBounds.find(face)->second, point) + (0xFF - face->baselight);
numSamples++;
}
// We should always end up with at least one sample (that from refFace itself), so if we divide by zero here something is very much wrong
return (unsigned char)(light / numSamples);
}

11
lighting.h

@ -10,8 +10,14 @@ struct EdgeData
bool isSharpEdge = false; bool isSharpEdge = false;
}; };
struct Surface
{
std::unordered_set<const face_t*> faces;
};
typedef std::unordered_map<const face_t*, BoundBox> FaceBounds; typedef std::unordered_map<const face_t*, BoundBox> FaceBounds;
typedef std::unordered_map<const vertex_t*, std::unordered_set<const face_t*>> VertexFaces; typedef std::unordered_map<const vertex_t*, std::unordered_set<const face_t*>> VertexFaces;
typedef std::vector<Surface> SurfaceList;
unsigned char sample_lightmap(const world_t* world, const face_t* face, const BoundBox& bounds, const Vec3& point); unsigned char sample_lightmap(const world_t* world, const face_t* face, const BoundBox& bounds, const Vec3& point);
void export_lightmap(const world_t* world, const face_t* face, const BoundBox& bounds, int faceIdx); void export_lightmap(const world_t* world, const face_t* face, const BoundBox& bounds, int faceIdx);
@ -19,3 +25,8 @@ void export_lightmap(const world_t* world, const face_t* face, const BoundBox& b
std::unordered_map<const edge_t*, EdgeData> analyze_edges(const world_t* world); std::unordered_map<const edge_t*, EdgeData> analyze_edges(const world_t* world);
unsigned char compute_faceVertex_light(const world_t* world, const face_t* face, unsigned short vertexIndex, const FaceBounds& faceBounds, const std::unordered_map<const edge_t*, EdgeData>& edgeData); unsigned char compute_faceVertex_light(const world_t* world, const face_t* face, unsigned short vertexIndex, const FaceBounds& faceBounds, const std::unordered_map<const edge_t*, EdgeData>& edgeData);
unsigned char compute_faceVertex_light2(const world_t* world, const face_t* face, unsigned short vertexIndex, const FaceBounds& faceBounds, const VertexFaces& vertexFaces); unsigned char compute_faceVertex_light2(const world_t* world, const face_t* face, unsigned short vertexIndex, const FaceBounds& faceBounds, const VertexFaces& vertexFaces);
SurfaceList group_surfaces(const world_t* world, const VertexFaces& vertexFaces);
unsigned char compute_faceVertex_light3(const world_t* world, const face_t* refFace, const SurfaceList& surfaces, const FaceBounds& faceBounds, Vec3 point);
unsigned char compute_faceVertex_light4(const world_t* world, const face_t* refFace, const FaceBounds& faceBounds, Vec3 point);

3
main.cpp

@ -334,7 +334,8 @@ int process_faces(const world_t* world)
for (size_t faceVertIdx = 0; faceVertIdx < outFace.numFaceVertices; ++faceVertIdx) for (size_t faceVertIdx = 0; faceVertIdx < outFace.numFaceVertices; ++faceVertIdx)
{ {
ps1bsp_facevertex_t& faceVertex = outFaceVertices[outFace.firstFaceVertex + faceVertIdx]; ps1bsp_facevertex_t& faceVertex = outFaceVertices[outFace.firstFaceVertex + faceVertIdx];
faceVertex.light = compute_faceVertex_light2(world, face, faceVertex.index, faceBounds, vertexFaces);
const vertex_t* vertex = &world->vertices[faceVertex.index];
faceVertex.light = compute_faceVertex_light4(world, face, faceBounds, vertex->toVec());
} }
} }

Loading…
Cancel
Save