Browse Source
Encapsulated GPC-based tesselation into a Tesselator class, which now also produces normalized UVs per vertex-texture pair and a list of Polygon structs with texture and vertex index data.
master
Encapsulated GPC-based tesselation into a Tesselator class, which now also produces normalized UVs per vertex-texture pair and a list of Polygon structs with texture and vertex index data.
master
6 changed files with 176 additions and 99 deletions
-
2PS1BSP.vcxproj
-
6PS1BSP.vcxproj.filters
-
12common.h
-
103main.cpp
-
119tesselate.cpp
-
33tesselate.h
@ -0,0 +1,119 @@ |
|||||
|
#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; |
||||
|
|
||||
|
// Build a polygon in normalized 2D texture space from the original face data
|
||||
|
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(); |
||||
|
|
||||
|
// 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); |
||||
|
|
||||
|
// 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 + 1.0, y }; |
||||
|
cell_bounds.vertex[2] = gpc_vertex{ x + 1.0, y + 1.0 }; |
||||
|
cell_bounds.vertex[3] = gpc_vertex{ x, y + 1.0 }; |
||||
|
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; |
||||
|
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; |
||||
|
newPoly.texinfo = texinfo; |
||||
|
|
||||
|
// 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 * (float)(vert->x * miptex->width - texinfo->distS) + |
||||
|
texinfo->vectorT * (float)(vert->y * miptex->height - texinfo->distT); |
||||
|
|
||||
|
// Make sure we don't store duplicate vertices
|
||||
|
size_t vertexIndex; |
||||
|
auto vertIter = vertexIndices.find(newVert); |
||||
|
if (vertIter != vertexIndices.end()) |
||||
|
{ |
||||
|
vertexIndex = vertIter->second; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
vertexIndex = vertices.size(); |
||||
|
vertexIndices[newVert] = vertexIndex; |
||||
|
vertices.push_back(newVert); |
||||
|
} |
||||
|
|
||||
|
newPoly.indices.push_back(vertexIndex); |
||||
|
|
||||
|
// Store the relevant (S, T) coordinates for each vertex-texinfo pair
|
||||
|
VertexTexturePair uvPair{ newVert, texinfo }; |
||||
|
auto vertUVIter = vertexUVs.find(uvPair); |
||||
|
if (vertUVIter == vertexUVs.end()) |
||||
|
{ |
||||
|
// Normalize the UV to fall within [0..1] range
|
||||
|
Vec3 uv(vert->x - x, vert->y - y, 0); |
||||
|
vertexUVs[uvPair] = uv; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
polygons.push_back(newPoly); |
||||
|
|
||||
|
gpc_free_polygon(&result); |
||||
|
gpc_free_polygon(&cell); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
gpc_free_polygon(&facePolygon); |
||||
|
return polygons; |
||||
|
} |
||||
@ -0,0 +1,33 @@ |
|||||
|
#pragma once |
||||
|
|
||||
|
class Tesselator |
||||
|
{ |
||||
|
public: |
||||
|
typedef std::vector<Vec3> VertexList; |
||||
|
typedef std::unordered_map<Vec3, size_t> VertexIndexMap; |
||||
|
typedef std::pair<Vec3, const texinfo_t*> VertexTexturePair; |
||||
|
typedef std::map<VertexTexturePair, Vec3> VertexUVMap; |
||||
|
|
||||
|
struct Polygon |
||||
|
{ |
||||
|
const texinfo_t* texinfo; |
||||
|
std::vector<size_t> indices; |
||||
|
}; |
||||
|
|
||||
|
Tesselator(const world_t* world) : world(world) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
const VertexList& getVertices() const { return vertices; } |
||||
|
const VertexIndexMap& getVertexIndices() const { return vertexIndices; } |
||||
|
const VertexUVMap& getVertexUVs() const { return vertexUVs; } |
||||
|
|
||||
|
std::vector<Polygon> tesselateFace(const face_t* face); |
||||
|
|
||||
|
private: |
||||
|
const world_t* world; |
||||
|
|
||||
|
VertexList vertices; |
||||
|
VertexIndexMap vertexIndices; |
||||
|
VertexUVMap vertexUVs; |
||||
|
}; |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue