You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
114 lines
4.0 KiB
114 lines
4.0 KiB
#include "common.h"
|
|
#include "bsp.h"
|
|
#include "tesselate.h"
|
|
#include "gpc.h"
|
|
|
|
std::vector<Tesselator::Polygon> Tesselator::tesselateFace(const face_t* face)
|
|
{
|
|
std::vector<Polygon> polygons;
|
|
|
|
const texinfo_t* texinfo = &world->texInfos[face->texinfo_id];
|
|
const miptex_t* miptex = &world->miptexes[texinfo->texture_id];
|
|
const plane_t* plane = &world->planes[face->plane_id];
|
|
|
|
double minS = DBL_MAX, minT = DBL_MAX;
|
|
double maxS = DBL_MIN, maxT = DBL_MIN;
|
|
|
|
gpc_polygon facePolygon = { 0 };
|
|
gpc_vertex_list contour;
|
|
contour.num_vertices = face->ledge_num;
|
|
contour.vertex = (gpc_vertex*)malloc(contour.num_vertices * sizeof(gpc_vertex));
|
|
if (contour.vertex == NULL)
|
|
return polygons;
|
|
|
|
double invSLenSqr = 1.0 / texinfo->vectorS.sqrMagnitude();
|
|
double invTLenSqr = 1.0 / texinfo->vectorT.sqrMagnitude();
|
|
|
|
// Build a polygon in normalized 2D texture space from the original face data
|
|
std::vector<Vec3> faceVertices;
|
|
for (int edgeListIdx = 0; edgeListIdx < face->ledge_num; ++edgeListIdx)
|
|
{
|
|
int edgeIdx = world->edgeList[face->ledge_id + edgeListIdx];
|
|
|
|
unsigned short vertIndex = edgeIdx > 0 ?
|
|
world->edges[edgeIdx].vertex0 :
|
|
world->edges[-edgeIdx].vertex1;
|
|
|
|
const vertex_t* vertex = &world->vertices[vertIndex];
|
|
Vec3 vertexPoint = vertex->toVec();
|
|
faceVertices.push_back(vertexPoint);
|
|
|
|
// vectorS and vectorT are normally perpendicular (dot product is 0), magnitude isn't always 1 but that's fine
|
|
// Means we can construct a coordinate space from them (cross product for the third vector) and transform the vertex point to ST-space
|
|
// And we can create an inverse transform... though just having s and t values probably isn't enough to completely transform back...
|
|
|
|
// Calculate texture UV bounds
|
|
double s = (vertexPoint.dotProduct(texinfo->vectorS) + texinfo->distS) / miptex->width;
|
|
double t = (vertexPoint.dotProduct(texinfo->vectorT) + texinfo->distT) / miptex->height;
|
|
if (s > maxS) maxS = s; if (s < minS) minS = s;
|
|
if (t > maxT) maxT = t; if (t < minT) minT = t;
|
|
|
|
contour.vertex[edgeListIdx] = gpc_vertex{ s, t };
|
|
}
|
|
|
|
gpc_add_contour(&facePolygon, &contour, 0);
|
|
|
|
auto faceVert = *faceVertices.begin();
|
|
|
|
// Create a virtual grid at the texture bounds and iterate over each cell to break up the face into repeating tiles
|
|
for (double y = floor(minT); y <= ceil(maxT); y += 1.0)
|
|
{
|
|
for (double x = floor(minS); x <= ceil(maxS); x += 1.0)
|
|
{
|
|
// Create a square polygon that covers the entire cell
|
|
gpc_polygon cell = { 0 };
|
|
gpc_vertex_list cell_bounds;
|
|
cell_bounds.num_vertices = 4;
|
|
cell_bounds.vertex = (gpc_vertex*)malloc(4 * sizeof(gpc_vertex));
|
|
cell_bounds.vertex[0] = gpc_vertex{ x, y };
|
|
cell_bounds.vertex[1] = gpc_vertex{ x, y + 1.0 };
|
|
cell_bounds.vertex[2] = gpc_vertex{ x + 1.0, y + 1.0 };
|
|
cell_bounds.vertex[3] = gpc_vertex{ x + 1.0, y };
|
|
gpc_add_contour(&cell, &cell_bounds, 0);
|
|
|
|
// Take the intersection to get the chunk of the face that's inside this cell
|
|
gpc_polygon result = { 0 };
|
|
gpc_polygon_clip(GPC_INT, &facePolygon, &cell, &result);
|
|
|
|
// We should get a polygon with exactly one contour as a result; if not, the face was not on this grid cell
|
|
if (result.num_contours <= 0)
|
|
continue;
|
|
|
|
Polygon newPoly;
|
|
|
|
// Reconstruct the polygon's vertices in 3D world space
|
|
for (int v = 0; v < result.contour[0].num_vertices; ++v)
|
|
{
|
|
const auto vert = &result.contour[0].vertex[v];
|
|
Vec3 newVert =
|
|
plane->normal * plane->dist +
|
|
texinfo->vectorS * (vert->x * miptex->width - texinfo->distS) * invSLenSqr +
|
|
texinfo->vectorT * (vert->y * miptex->height - texinfo->distT) * invTLenSqr;
|
|
|
|
size_t vertIndex = addVertex(newVert);
|
|
Vec3 normalizedUV(vert->x - x, vert->y - y, 0); // Normalize the UV to fall within [0..1] range
|
|
|
|
newPoly.polyVertices.push_back(PolyVertex{ vertIndex, normalizedUV });
|
|
}
|
|
|
|
polygons.push_back(newPoly);
|
|
|
|
gpc_free_polygon(&result);
|
|
gpc_free_polygon(&cell);
|
|
}
|
|
}
|
|
|
|
if (vertexIndices.find(faceVert) == vertexIndices.end())
|
|
{
|
|
gpc_free_polygon(&facePolygon);
|
|
return polygons;
|
|
}
|
|
|
|
gpc_free_polygon(&facePolygon);
|
|
return polygons;
|
|
}
|