Antkeeper  0.0.1
brep-operations.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 
21 #include <engine/math/vector.hpp>
23 #include <engine/debug/log.hpp>
24 #include <algorithm>
25 #include <cmath>
26 
27 namespace geom {
28 
30 {
31  const auto& vertex_positions = mesh.vertices().attributes().at<math::fvec3>("position");
32  auto& face_normals = static_cast<brep_attribute<math::fvec3>&>(*mesh.faces().attributes().try_emplace<math::fvec3>("normal").first);
33 
34  for (brep_face* face: mesh.faces())
35  {
36  auto loop = face->loops().begin();
37  const auto& a = vertex_positions[loop->vertex()->index()];
38  const auto& b = vertex_positions[(++loop)->vertex()->index()];
39  const auto& c = vertex_positions[(++loop)->vertex()->index()];
40 
41  face_normals[face->index()] = math::normalize(math::cross(b - a, c - a));
42  }
43 }
44 
46 {
47  // Generate face normals if they don't exist
48  if (!mesh.faces().attributes().contains("normal"))
49  {
51  }
52 
53  const auto& vertex_positions = mesh.vertices().attributes().at<math::fvec3>("position");
54  const auto& face_normals = mesh.faces().attributes().at<math::fvec3>("normal");
55  auto& vertex_normals = static_cast<brep_attribute<math::fvec3>&>(*mesh.vertices().attributes().try_emplace<math::fvec3>("normal").first);
56 
57  for (brep_vertex* vertex: mesh.vertices())
58  {
59  // Zero vertex normal
60  auto& vertex_normal = vertex_normals[vertex->index()];
61  vertex_normal = {};
62 
63  // Skip vertices with no edges
64  if (vertex->edges().empty())
65  {
66  continue;
67  }
68 
69  // Get vertex position
70  const auto& vertex_position = vertex_positions[vertex->index()];
71 
72  // For each edge bounded by this vertex
73  for (brep_edge* edge: vertex->edges())
74  {
75  // Skip edges with no associated face
76  if (edge->loops().empty())
77  {
78  continue;
79  }
80 
81  // Calculate direction vector of current edge
82  const auto direction0 = math::normalize
83  (
84  vertex_positions[edge->vertices()[edge->vertices().front() == vertex]->index()] -
85  vertex_position
86  );
87 
88  // For each edge loop
89  for (brep_loop* loop: edge->loops())
90  {
91  // Skip loops not originating at vertex
92  if (loop->vertex() != vertex)
93  {
94  continue;
95  }
96 
97  // Calculate direction vector of previous edge
98  const auto direction1 = math::normalize
99  (
100  vertex_positions[loop->previous()->vertex()->index()] -
101  vertex_position
102  );
103 
104  // Find angle between two edges
105  const auto cos_edge_angle = math::dot(direction0, direction1);
106  const auto edge_angle = std::acos(cos_edge_angle);
107 
108  // Weigh face normal by edge angle and add to vertex normal
109  vertex_normal += face_normals[loop->face()->index()] * edge_angle;
110  }
111  }
112 
113  // Normalize vertex normal
114  const auto sqr_length = math::sqr_length(vertex_normal);
115  if (sqr_length)
116  {
117  vertex_normal /= std::sqrt(sqr_length);
118  }
119  }
120 }
121 
123 {
124  const auto& vertex_positions = mesh.vertices().attributes().at<math::fvec3>("position");
125  auto& loop_barycentric = static_cast<brep_attribute<math::fvec3>&>(*mesh.loops().attributes().try_emplace<math::fvec3>("barycentric").first);
126 
127  for (brep_face* face: mesh.faces())
128  {
129  auto loop = face->loops().begin();
130  loop_barycentric[loop->index()] = {1.0f, 0.0f, 0.0f};
131  loop_barycentric[(++loop)->index()] = {0.0f, 1.0f, 0.0f};
132  loop_barycentric[(++loop)->index()] = {0.0f, 0.0f, 1.0f};
133  }
134 }
135 
136 std::unique_ptr<render::model> generate_model(const brep_mesh& mesh, std::shared_ptr<render::material> material)
137 {
138  // Get vertex positions
139  const geom::brep_attribute<math::fvec3>* vertex_positions = nullptr;
140  if (auto attribute_it = mesh.vertices().attributes().find("position"); attribute_it != mesh.vertices().attributes().end())
141  {
142  vertex_positions = &static_cast<const geom::brep_attribute<math::fvec3>&>(*attribute_it);
143  }
144 
145  // Get vertex normals
146  const geom::brep_attribute<math::fvec3>* vertex_normals = nullptr;
147  if (auto attribute_it = mesh.vertices().attributes().find("normal"); attribute_it != mesh.vertices().attributes().end())
148  {
149  vertex_normals = &static_cast<const geom::brep_attribute<math::fvec3>&>(*attribute_it);
150  }
151 
152  // Allocate model
153  auto model = std::make_unique<render::model>();
154 
155  // Init model bounds
156  auto& bounds = model->get_bounds();
158 
159  // Construct model VAO
160  std::size_t vertex_stride = 0;
161  std::vector<gl::vertex_input_attribute> vertex_attributes;
162  gl::vertex_input_attribute position_attribute{};
163  if (vertex_positions)
164  {
166  position_attribute.binding = 0;
167  position_attribute.format = gl::format::r32g32b32_sfloat;
168  position_attribute.offset = 0;
169  vertex_attributes.emplace_back(position_attribute);
170 
171  vertex_stride += 3 * sizeof(float);
172  }
173  gl::vertex_input_attribute normal_attribute{};
174  if (vertex_normals)
175  {
177  normal_attribute.binding = 0;
178  normal_attribute.format = gl::format::r32g32b32_sfloat;
179  normal_attribute.offset = static_cast<std::uint32_t>(vertex_stride);
180  vertex_attributes.emplace_back(normal_attribute);
181 
182  vertex_stride += 3 * sizeof(float);
183  }
184  auto& vao = model->get_vertex_array();
185  vao = std::make_unique<gl::vertex_array>(vertex_attributes);
186 
187  // Interleave vertex data
188  std::vector<std::byte> vertex_data(mesh.faces().size() * 3 * vertex_stride);
189  if (vertex_positions)
190  {
191  std::byte* v = vertex_data.data() + position_attribute.offset;
192  for (auto face: mesh.faces())
193  {
194  for (auto loop: face->loops())
195  {
196  const auto& position = (*vertex_positions)[loop->vertex()->index()];
197  std::memcpy(v, position.data(), sizeof(float) * 3);
198  v += vertex_stride;
199 
200  // Extend model bounds
201  bounds.extend(position);
202  }
203  }
204  }
205  if (vertex_normals)
206  {
207  std::byte* v = vertex_data.data() + normal_attribute.offset;
208  for (auto face: mesh.faces())
209  {
210  for (auto loop: face->loops())
211  {
212  const auto& normal = (*vertex_normals)[loop->vertex()->index()];
213  std::memcpy(v, normal.data(), sizeof(float) * 3);
214  v += vertex_stride;
215  }
216  }
217  }
218 
219  // Construct model VBO
220  auto& vbo = model->get_vertex_buffer();
221  vbo = std::make_unique<gl::vertex_buffer>(gl::buffer_usage::static_draw, vertex_data);
222  model->set_vertex_offset(0);
223  model->set_vertex_stride(vertex_stride);
224 
225  // Create material group
226  model->get_groups().resize(1);
227  render::model_group& model_group = model->get_groups().front();
228 
229  model_group.id = {};
230  model_group.material = material;
232  model_group.first_vertex = 0;
233  model_group.vertex_count = static_cast<std::uint32_t>(mesh.faces().size() * 3);
234 
235  return model;
236 }
237 
238 } // namespace geom
Per-element B-rep data.
Curve segment bounded by two vertices.
Definition: brep-edge.hpp:237
Portion of a shell bounded by loops.
Definition: brep-face.hpp:244
Connected boundary of a single face.
Definition: brep-loop.hpp:38
Boundary representation (B-rep) of a mesh.
Definition: brep-mesh.hpp:34
A point in space.
Geometric algorithms.
@ a
Vertex A region.
@ c
Vertex C region.
@ b
Vertex B region.
std::unique_ptr< render::model > generate_model(const brep_mesh &mesh, std::shared_ptr< render::material > material)
Generates a model from a B-rep mesh.
void generate_loop_barycentric(brep_mesh &mesh)
Generates the math::fvec3 loop attribute "barycentric" for a B-rep mesh.
void generate_face_normals(brep_mesh &mesh)
Generates the math::fvec3 face attribute "normal" for a B-rep mesh.
void generate_vertex_normals(brep_mesh &mesh)
Generates the math::fvec3 vertex attribute "normal" for a B-rep mesh.
@ static_draw
Data will be modified once, by the application, and used many times, for drawing commands.
@ triangle_list
Separate triangle primitives.
quaternion< T > normalize(const quaternion< T > &q)
Normalizes a quaternion.
Definition: quaternion.hpp:679
constexpr T sqr_length(const quaternion< T > &q) noexcept
Calculates the square length of a quaternion.
Definition: quaternion.hpp:744
constexpr vector< T, 3 > cross(const vector< T, 3 > &x, const vector< T, 3 > &y) noexcept
Calculates the cross product of two vectors.
Definition: vector.hpp:1095
vector< T, N > sqrt(const vector< T, N > &x)
Takes the square root of each element.
constexpr T dot(const quaternion< T > &a, const quaternion< T > &b) noexcept
Calculates the dot product of two quaternions.
Definition: quaternion.hpp:572
std::uint32_t location
Shader input location number for this attribute.
n-dimensional vector.
Definition: vector.hpp:44
static constexpr vector infinity() noexcept
Returns a vector of infinities, where every element is equal to infinity.
Definition: vector.hpp:342
Part of a model which is associated with exactly one material.
Definition: model.hpp:41
std::shared_ptr< render::material > material
Definition: model.hpp:46
gl::primitive_topology primitive_topology
Definition: model.hpp:43
std::uint32_t vertex_count
Definition: model.hpp:45
std::uint32_t first_vertex
Definition: model.hpp:44
hash::fnv1a32_t id
Definition: model.hpp:42