Antkeeper  0.0.1
mesh-functions.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2023 Christopher J. Howard
3  *
4  * This file is part of Antkeeper source code.
5  *
6  * Antkeeper source code is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * Antkeeper source code is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
18  */
19 
22 #include <unordered_map>
23 #include <vector>
24 
25 namespace geom {
26 
27 struct edge_hasher
28 {
29  std::size_t operator()(const std::array<std::size_t, 2>& v) const noexcept
30  {
31  std::size_t hash = std::hash<std::size_t>()(v[0]);
32  return hash ^ (std::hash<std::size_t>()(v[1]) + 0x9e3779b9 + (hash << 6) + (hash >> 2));
33  }
34 };
35 
36 void create_triangle_mesh(mesh& mesh, const std::vector<float3>& vertices, const std::vector<std::array<std::uint_fast32_t, 3>>& triangles)
37 {
38  for (const auto& vertex: vertices)
39  mesh.add_vertex(vertex);
40 
41  std::unordered_map<std::array<std::size_t, 2>, geom::mesh::edge*, edge_hasher> edge_map;
42  const std::vector<mesh::vertex*>& mesh_vertices = mesh.get_vertices();
43  std::vector<geom::mesh::edge*> loop(3);
44 
45  for (const auto& triangle: triangles)
46  {
47  geom::mesh::vertex* triangle_vertices[3] =
48  {
49  mesh_vertices[triangle[0]],
50  mesh_vertices[triangle[1]],
51  mesh_vertices[triangle[2]]
52  };
53 
54  for (int j = 0; j < 3; ++j)
55  {
56  geom::mesh::vertex* start = triangle_vertices[j];
57  geom::mesh::vertex* end = triangle_vertices[(j + 1) % 3];
58 
59  if (auto it = edge_map.find({start->index, end->index}); it != edge_map.end())
60  {
61  loop[j] = it->second;
62  }
63  else
64  {
65  loop[j] = mesh.add_edge(start, end);
66  edge_map[{start->index, end->index}] = loop[j];
67  edge_map[{end->index, start->index}] = loop[j]->symmetric;
68  }
69 
70  }
71 
72  mesh.add_face(loop);
73  }
74 }
75 
76 void calculate_face_normals(float3* normals, const mesh& mesh)
77 {
78  const std::vector<mesh::face*>& faces = mesh.get_faces();
79 
80  for (std::size_t i = 0; i < faces.size(); ++i)
81  {
82  const mesh::face& face = *(faces[i]);
83  const float3& a = face.edge->vertex->position;
84  const float3& b = face.edge->next->vertex->position;
85  const float3& c = face.edge->previous->vertex->position;
86 
87  normals[i] = math::normalize(math::cross(b - a, c - a));
88  }
89 }
90 
92 {
93  const float3& a = face.edge->vertex->position;
94  const float3& b = face.edge->next->vertex->position;
95  const float3& c = face.edge->previous->vertex->position;
96  return math::normalize(math::cross(b - a, c - a));
97 }
98 
99 void calculate_vertex_tangents(float4* tangents, const float2* texcoords, const float3* normals, const mesh& mesh)
100 {
101  const std::vector<mesh::face*>& faces = mesh.get_faces();
102  const std::vector<mesh::vertex*>& vertices = mesh.get_vertices();
103 
104  // Allocate tangent and bitangent buffers
105  std::vector<float3> tangent_buffer(vertices.size(), float3{0.0f, 0.0f, 0.0f});
106  std::vector<float3> bitangent_buffer(vertices.size(), float3{0.0f, 0.0f, 0.0f});
107 
108  // Accumulate tangents and bitangents
109  for (std::size_t i = 0; i < faces.size(); ++i)
110  {
111  const mesh::face& face = *(faces[i]);
112  std::size_t ia = face.edge->vertex->index;
113  std::size_t ib = face.edge->next->vertex->index;
114  std::size_t ic = face.edge->previous->vertex->index;
115  const float3& a = vertices[ia]->position;
116  const float3& b = vertices[ib]->position;
117  const float3& c = vertices[ic]->position;
118  const float2& uva = texcoords[ia];
119  const float2& uvb = texcoords[ib];
120  const float2& uvc = texcoords[ic];
121 
122  float3 ba = b - a;
123  float3 ca = c - a;
124  float2 uvba = uvb - uva;
125  float2 uvca = uvc - uva;
126 
127  float f = 1.0f / (uvba.x() * uvca.y() - uvca.x() * uvba.y());
128  float3 tangent = (ba * uvca.y() - ca * uvba.y()) * f;
129  float3 bitangent = (ba * -uvca.x() + ca * uvba.x()) * f;
130 
131  tangent_buffer[ia] += tangent;
132  tangent_buffer[ib] += tangent;
133  tangent_buffer[ic] += tangent;
134  bitangent_buffer[ia] += bitangent;
135  bitangent_buffer[ib] += bitangent;
136  bitangent_buffer[ic] += bitangent;
137  }
138 
139  // Orthogonalize tangents
140  for (std::size_t i = 0; i < vertices.size(); ++i)
141  {
142  const float3& n = normals[i];
143  const float3& t = tangent_buffer[i];
144  const float3& b = bitangent_buffer[i];
145 
146  // Gram-Schmidt orthogonalize tangent
147  float3 tangent = math::normalize(t - n * math::dot(n, t));
148 
149  // Calculate bitangent sign
150  float bitangent_sign = (math::dot(math::cross(n, t), b) < 0.0f) ? -1.0f : 1.0f;
151 
152  tangents[i] = {tangent.x(), tangent.y(), tangent.z(), bitangent_sign};
153  }
154 }
155 
157 {
158  box<float> bounds;
159  for (int i = 0; i < 3; ++i)
160  {
161  bounds.min[i] = std::numeric_limits<float>::infinity();
162  bounds.max[i] = -std::numeric_limits<float>::infinity();
163  }
164 
165  for (const mesh::vertex* vertex: mesh.get_vertices())
166  {
167  const auto& position = vertex->position;
168  bounds.extend(position);
169  }
170 
171  return bounds;
172 }
173 
175 {
176  mesh::face* face = mesh.get_faces()[index];
177 
178  // Collect face edges and sum edge vertex positions
179  mesh::loop loop = {face->edge};
180  float3 sum_positions = face->edge->vertex->position;
181  for (mesh::edge* edge = face->edge->next; edge != face->edge; edge = edge->next)
182  {
183  loop.push_back(edge);
184  sum_positions += edge->vertex->position;
185  }
186 
187  if (loop.size() <= 2)
188  return nullptr;
189 
190  // Remove face
191  mesh.remove_face(face);
192 
193  // Add vertex in center
194  mesh::vertex* center = mesh.add_vertex(sum_positions / static_cast<float>(loop.size()));
195 
196  // Create first triangle
197  geom::mesh::edge* ab = loop[0];
198  geom::mesh::edge* bc = mesh.add_edge(ab->next->vertex, center);
199  geom::mesh::edge* ca = mesh.add_edge(center, ab->vertex);
200  mesh.add_face({ab, bc, ca});
201 
202  // Save first triangle CA edge
203  geom::mesh::edge* first_triangle_ca = ca;
204 
205  // Create remaining triangles
206  for (std::size_t i = 1; i < loop.size(); ++i)
207  {
208  ab = loop[i];
209  ca = bc->symmetric;
210 
211  if (i == loop.size() - 1)
212  bc = first_triangle_ca->symmetric;
213  else
214  bc = mesh.add_edge(ab->next->vertex, center);
215 
216  mesh.add_face({ab, bc, ca});
217  }
218 
219  return center;
220 }
221 
222 } // namespace geom
Half-edge mesh.
Definition: mesh.hpp:34
void remove_face(mesh::face *face)
Removes a face from the mesh.
Definition: mesh.cpp:264
mesh::face * add_face(const loop &loop)
Adds a face to the mesh.
Definition: mesh.cpp:212
const std::vector< mesh::face * > & get_faces() const
Returns the mesh faces.
Definition: mesh.hpp:209
mesh::vertex * add_vertex(const float3 &position)
Adds a vertex to the mesh.
Definition: mesh.cpp:145
std::vector< edge * > loop
List of edges which form a face.
Definition: mesh.hpp:94
mesh::edge * add_edge(mesh::vertex *a, mesh::vertex *b)
Adds two half edges to the mesh.
Definition: mesh.cpp:159
const std::vector< mesh::vertex * > & get_vertices() const
Returns the mesh vertices.
Definition: mesh.hpp:199
Geometric algorithms.
Definition: cartesian.hpp:26
void calculate_face_normals(float3 *normals, const mesh &mesh)
Calculates normals for each face.
void create_triangle_mesh(mesh &mesh, const std::vector< float3 > &vertices, const std::vector< std::array< std::uint_fast32_t, 3 >> &triangles)
Creates a triangle mesh from a list of vertices and indices.
box< float > calculate_bounds(const mesh &mesh)
Calculates the AABB bounds of a mesh.
mesh::vertex * poke_face(mesh &mesh, std::size_t index)
Triangulates a face by adding a new vertex in the center, then creating triangles between the edges o...
void calculate_vertex_tangents(float4 *tangents, const float2 *texcoords, const float3 *normals, const mesh &mesh)
Calculates smooth tangents per-vertex.
float3 calculate_face_normal(const mesh::face &face)
Hash functions.
Definition: combine.hpp:25
quaternion< T > normalize(const quaternion< T > &q)
Normalizes a quaternion.
Definition: quaternion.hpp:631
constexpr vector< T, 3 > cross(const vector< T, 3 > &x, const vector< T, 3 > &y) noexcept
Calculate the cross product of two vectors.
Definition: vector.hpp:945
constexpr T dot(const quaternion< T > &a, const quaternion< T > &b) noexcept
Calculates the dot product of two quaternions.
Definition: quaternion.hpp:525
@ index
General purpose index (uint)
@ position
Vertex position (vec3)
@ tangent
Vertex tangent (vec4)
Half-edge mesh edge, containing its index and pointers to its starting vertex, parent face,...
Definition: mesh.hpp:59
mesh::edge * next
Pointer to the next edge in the parent face.
Definition: mesh.hpp:73
mesh::vertex * vertex
Pointer to the vertex at which the edge starts.
Definition: mesh.hpp:64
mesh::edge * symmetric
Pointer to the symmetric edge.
Definition: mesh.hpp:76
mesh::edge * previous
Pointer to the previous edge in the parent face.
Definition: mesh.hpp:70
Half-edge mesh face, containing its index and a pointer to its first edge.
Definition: mesh.hpp:83
mesh::edge * edge
Pointer to the first edge in this face.
Definition: mesh.hpp:88
Half-edge mesh vertex, containing its index, a pointer to its parent edge, and its position vector.
Definition: mesh.hpp:44
float3 position
Vertex position.
Definition: mesh.hpp:52
std::size_t index
Index of this vertex.
Definition: mesh.hpp:46
n-dimensional axis-aligned rectangle.
vector_type min
Minimum extent of the hyperrectangle.
vector_type max
Maximum extent of the hyperrectangle.
void extend(const vector_type &point) noexcept
Extends the hyperrectangle to include a point.
constexpr element_type & x() noexcept
Returns a reference to the first element.
Definition: vector.hpp:161
constexpr element_type & y() noexcept
Returns a reference to the second element.
Definition: vector.hpp:177