mirror of
https://github.com/ZDoom/ZDRay.git
synced 2024-11-10 06:41:37 +00:00
Add the delauney triangulator (not used, but I don't want to lose this code in case I decide to use it after all)
This commit is contained in:
parent
2acec31f7d
commit
8f097378d5
3 changed files with 298 additions and 1 deletions
|
@ -65,7 +65,8 @@ if( MSVC )
|
|||
# String pooling
|
||||
# Function-level linking
|
||||
# Disable run-time type information
|
||||
set( ALL_C_FLAGS "/GF /Gy /GR-" )
|
||||
# Enable C++ conformance mode
|
||||
set( ALL_C_FLAGS "/GF /Gy /GR- /permissive-" )
|
||||
|
||||
# Avoid CRT DLL dependancies in release builds
|
||||
set( REL_C_FLAGS "/MT" )
|
||||
|
@ -154,6 +155,8 @@ set( SOURCES
|
|||
src/lightmap/surfaces.cpp
|
||||
src/lightmap/worker.cpp
|
||||
src/lightmap/collision.cpp
|
||||
src/lightmap/delauneytriangulator.cpp
|
||||
src/lightmap/delauneytriangulator.h
|
||||
src/math/angle.cpp
|
||||
src/math/bounds.cpp
|
||||
src/math/mathlib.cpp
|
||||
|
|
249
src/lightmap/delauneytriangulator.cpp
Normal file
249
src/lightmap/delauneytriangulator.cpp
Normal file
|
@ -0,0 +1,249 @@
|
|||
|
||||
#include "delauneytriangulator.h"
|
||||
|
||||
void DelauneyTriangulator::triangulate()
|
||||
{
|
||||
std::vector<Vertex*> ordered_vertices = remove_duplicates(create_ordered_vertex_list());
|
||||
|
||||
Vertex super_A, super_B, super_C;
|
||||
Triangle super_triangle;
|
||||
super_triangle.A = &super_A;
|
||||
super_triangle.B = &super_B;
|
||||
super_triangle.C = &super_C;
|
||||
calculate_supertriangle(ordered_vertices, super_triangle);
|
||||
|
||||
triangles = perform_delauney_triangulation(ordered_vertices, super_triangle);
|
||||
}
|
||||
|
||||
std::vector<DelauneyTriangulator::Vertex*> DelauneyTriangulator::create_ordered_vertex_list()
|
||||
{
|
||||
if (vertices.empty())
|
||||
return {};
|
||||
|
||||
std::vector<Vertex*> ordered_vertices;
|
||||
|
||||
size_t num_vertices = vertices.size();
|
||||
for (size_t index_vertices = 0; index_vertices < num_vertices; index_vertices++)
|
||||
{
|
||||
ordered_vertices.push_back(&vertices[index_vertices]);
|
||||
}
|
||||
|
||||
std::sort(ordered_vertices.begin(), ordered_vertices.end(), [](Vertex* a, Vertex* b) { return a->x == b->x ? a->y < b->y : a->x < b->x; });
|
||||
|
||||
return ordered_vertices;
|
||||
}
|
||||
|
||||
std::vector<DelauneyTriangulator::Vertex*> DelauneyTriangulator::remove_duplicates(const std::vector<Vertex*>& ordered_vertices)
|
||||
{
|
||||
// Link duplicates and remove them from the ordered list:
|
||||
|
||||
std::vector<Vertex*> filtered_vertices;
|
||||
Vertex* prev = nullptr;
|
||||
for (Vertex* v : ordered_vertices)
|
||||
{
|
||||
v->next = nullptr; // clear for all vertices just in case triangulate is ever called twice
|
||||
|
||||
if (prev && prev->x == v->x && prev->y == v->y)
|
||||
{
|
||||
prev->next = v;
|
||||
}
|
||||
else
|
||||
{
|
||||
filtered_vertices.push_back(v);
|
||||
}
|
||||
|
||||
prev = v;
|
||||
}
|
||||
}
|
||||
|
||||
void DelauneyTriangulator::calculate_supertriangle(std::vector<Vertex*>& vertices, Triangle& super_triangle)
|
||||
{
|
||||
// Find min and max values:
|
||||
|
||||
size_t num_vertices = vertices.size();
|
||||
|
||||
float min_x = 0.0f;
|
||||
float max_x = 0.0f;
|
||||
float min_y = 0.0f;
|
||||
float max_y = 0.0f;
|
||||
if (num_vertices > 0)
|
||||
{
|
||||
min_x = vertices[0]->x;
|
||||
max_x = vertices[0]->x;
|
||||
min_y = vertices[0]->y;
|
||||
max_y = vertices[0]->y;
|
||||
}
|
||||
|
||||
for (size_t index_vertices = 1; index_vertices < num_vertices; index_vertices++)
|
||||
{
|
||||
Vertex* cur_vertex = vertices[index_vertices];
|
||||
|
||||
min_x = std::min(min_x, cur_vertex->x);
|
||||
max_x = std::max(max_x, cur_vertex->x);
|
||||
min_y = std::min(min_y, cur_vertex->y);
|
||||
max_y = std::max(max_y, cur_vertex->y);
|
||||
}
|
||||
|
||||
// Setup super triangle based on min/max values:
|
||||
|
||||
float dx = max_x - min_x;
|
||||
float dy = max_y - min_y;
|
||||
float dmax = (dx > dy) ? dx : dy;
|
||||
float xmid = (max_x + min_x) * 0.5f;
|
||||
float ymid = (max_y + min_y) * 0.5f;
|
||||
|
||||
super_triangle.A->x = xmid - 20.0f * dmax;
|
||||
super_triangle.A->y = ymid - dmax;
|
||||
super_triangle.A->data = nullptr;
|
||||
|
||||
super_triangle.B->x = xmid;
|
||||
super_triangle.B->y = ymid + 20.0f * dmax;
|
||||
super_triangle.B->data = nullptr;
|
||||
|
||||
super_triangle.C->x = xmid + 20.0f * dmax;
|
||||
super_triangle.C->y = ymid - dmax;
|
||||
super_triangle.C->data = nullptr;
|
||||
|
||||
calc_cirumcenter(super_triangle);
|
||||
}
|
||||
|
||||
std::vector<DelauneyTriangulator::Triangle> DelauneyTriangulator::perform_delauney_triangulation(const std::vector<Vertex*>& vertices, const Triangle& super_triangle)
|
||||
{
|
||||
std::vector<Triangle> triangles;
|
||||
|
||||
// add supertriangle vertices to the end of the vertex list
|
||||
triangles.push_back(super_triangle);
|
||||
|
||||
std::vector<Triangle_Edge> edges;
|
||||
|
||||
// for each sample point in the vertex list:
|
||||
size_t num_vertices = vertices.size();
|
||||
for (size_t index_vertices = 0; index_vertices < num_vertices; index_vertices++)
|
||||
{
|
||||
Vertex* insertion_point = vertices[index_vertices];
|
||||
|
||||
edges.clear();
|
||||
|
||||
// For each triangle currently in the triangle list
|
||||
std::vector<Triangle>::size_type index_triangles, num_triangles;
|
||||
num_triangles = triangles.size();
|
||||
for (index_triangles = 0; index_triangles < num_triangles; index_triangles++)
|
||||
{
|
||||
Triangle& cur_triangle = triangles[index_triangles];
|
||||
|
||||
// Check if the point lies in the triangle circumcircle:
|
||||
float dist_x = insertion_point->x - cur_triangle.circumcenter_x;
|
||||
float dist_y = insertion_point->y - cur_triangle.circumcenter_y;
|
||||
float dist2 = dist_x * dist_x + dist_y * dist_y;
|
||||
if (dist2 < cur_triangle.radius2)
|
||||
{
|
||||
// Add triangle edges to edge buffer:
|
||||
edges.push_back(Triangle_Edge(cur_triangle.A, cur_triangle.B));
|
||||
edges.push_back(Triangle_Edge(cur_triangle.B, cur_triangle.C));
|
||||
edges.push_back(Triangle_Edge(cur_triangle.C, cur_triangle.A));
|
||||
|
||||
// Remove triange from triangle list:
|
||||
triangles.erase(triangles.begin() + index_triangles);
|
||||
index_triangles--;
|
||||
num_triangles--;
|
||||
}
|
||||
}
|
||||
|
||||
// Delete all doubly specified edges from the edge buffer. This leaves the edges of the enclosing polygon only
|
||||
int64_t index_edges1, index_edges2, num_edges; // intentionally integer to allow index to be negative when deleting index.
|
||||
num_edges = (int64_t)edges.size();
|
||||
for (index_edges1 = 0; index_edges1 < num_edges; index_edges1++)
|
||||
{
|
||||
Triangle_Edge& edge1 = edges[index_edges1];
|
||||
for (index_edges2 = 0/*index_edges1+1*/; index_edges2 < num_edges; index_edges2++)
|
||||
{
|
||||
if (index_edges1 == index_edges2) continue;
|
||||
Triangle_Edge& edge2 = edges[index_edges2];
|
||||
if ((edge1.first == edge2.first && edge1.second == edge2.second) ||
|
||||
(edge1.second == edge2.first && edge1.first == edge2.second))
|
||||
{
|
||||
// Same edges, delete both:
|
||||
if (index_edges1 < index_edges2)
|
||||
{
|
||||
edges.erase(edges.begin() + index_edges2);
|
||||
edges.erase(edges.begin() + index_edges1);
|
||||
}
|
||||
else
|
||||
{
|
||||
edges.erase(edges.begin() + index_edges1);
|
||||
edges.erase(edges.begin() + index_edges2);
|
||||
}
|
||||
num_edges -= 2;
|
||||
index_edges1--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// add to the triangle list all triangles formed between the point and the edges of the enclosing polygon
|
||||
for (index_edges1 = 0; index_edges1 < num_edges; index_edges1++)
|
||||
{
|
||||
Triangle triangle;
|
||||
triangle.A = edges[index_edges1].first;
|
||||
triangle.B = edges[index_edges1].second;
|
||||
triangle.C = insertion_point;
|
||||
calc_cirumcenter(triangle);
|
||||
triangles.push_back(triangle);
|
||||
}
|
||||
}
|
||||
|
||||
// remove any triangles from the triangle list that use the supertriangle vertices
|
||||
size_t num_triangles = triangles.size();
|
||||
for (size_t index_triangles = 0; index_triangles < num_triangles; index_triangles++)
|
||||
{
|
||||
Triangle& cur_triangle = triangles[index_triangles];
|
||||
|
||||
if (
|
||||
cur_triangle.A == super_triangle.A ||
|
||||
cur_triangle.A == super_triangle.B ||
|
||||
cur_triangle.A == super_triangle.C ||
|
||||
cur_triangle.B == super_triangle.A ||
|
||||
cur_triangle.B == super_triangle.B ||
|
||||
cur_triangle.B == super_triangle.C ||
|
||||
cur_triangle.C == super_triangle.A ||
|
||||
cur_triangle.C == super_triangle.B ||
|
||||
cur_triangle.C == super_triangle.C)
|
||||
{
|
||||
// triangle shares one or more points with supertriangle, remove it:
|
||||
triangles.erase(triangles.begin() + index_triangles);
|
||||
index_triangles--;
|
||||
num_triangles--;
|
||||
}
|
||||
}
|
||||
|
||||
return triangles;
|
||||
}
|
||||
|
||||
void DelauneyTriangulator::calc_cirumcenter(Triangle& triangle)
|
||||
{
|
||||
float a_0 = triangle.A->x;
|
||||
float a_1 = triangle.A->y;
|
||||
float b_0 = triangle.B->x;
|
||||
float b_1 = triangle.B->y;
|
||||
float c_0 = triangle.C->x;
|
||||
float c_1 = triangle.C->y;
|
||||
|
||||
float A = b_0 - a_0;
|
||||
float B = b_1 - a_1;
|
||||
float C = c_0 - a_0;
|
||||
float D = c_1 - a_1;
|
||||
|
||||
float E = A * (a_0 + b_0) + B * (a_1 + b_1);
|
||||
float F = C * (a_0 + c_0) + D * (a_1 + c_1);
|
||||
|
||||
float G = 2.0f * (A * (c_1 - b_1) - B * (c_0 - b_0));
|
||||
|
||||
float p_0 = (D * E - B * F) / G;
|
||||
float p_1 = (A * F - C * E) / G;
|
||||
|
||||
triangle.circumcenter_x = p_0;
|
||||
triangle.circumcenter_y = p_1;
|
||||
float radius_x = triangle.A->x - triangle.circumcenter_x;
|
||||
float radius_y = triangle.A->y - triangle.circumcenter_y;
|
||||
triangle.radius2 = radius_x * radius_x + radius_y * radius_y;
|
||||
}
|
45
src/lightmap/delauneytriangulator.h
Normal file
45
src/lightmap/delauneytriangulator.h
Normal file
|
@ -0,0 +1,45 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
// Bowyer-Watson delauney triangulator (http://paulbourke.net/papers/triangulate/)
|
||||
class DelauneyTriangulator
|
||||
{
|
||||
public:
|
||||
struct Vertex
|
||||
{
|
||||
Vertex() = default;
|
||||
Vertex(float x, float y, void* data = nullptr) : x(x), y(y), data(data) { }
|
||||
|
||||
float x = 0.0f;
|
||||
float y = 0.0f;
|
||||
void* data = nullptr;
|
||||
Vertex* next = nullptr;
|
||||
};
|
||||
|
||||
struct Triangle
|
||||
{
|
||||
Vertex* A;
|
||||
Vertex* B;
|
||||
Vertex* C;
|
||||
float circumcenter_x;
|
||||
float circumcenter_y;
|
||||
float radius2;
|
||||
};
|
||||
|
||||
typedef std::pair<Vertex*, Vertex*> Triangle_Edge;
|
||||
|
||||
std::vector<Vertex> vertices;
|
||||
std::vector<Triangle> triangles;
|
||||
|
||||
void triangulate();
|
||||
|
||||
private:
|
||||
std::vector<Vertex*> create_ordered_vertex_list();
|
||||
static std::vector<Vertex*> remove_duplicates(const std::vector<Vertex*>& ordered_vertices);
|
||||
static void calculate_supertriangle(std::vector<Vertex*>& vertices, Triangle& super_triangle);
|
||||
static void calc_cirumcenter(Triangle& triangle);
|
||||
static std::vector<Triangle> perform_delauney_triangulation(const std::vector<Vertex*>& vertices, const Triangle& super_triangle);
|
||||
};
|
Loading…
Reference in a new issue