Antkeeper  0.0.1
navmesh.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 
20 #include <engine/ai/navmesh.hpp>
24 #include <iterator>
25 #include <engine/debug/log.hpp>
26 
27 namespace ai {
28 
30 {
31  // Get vertex positions and face normals
32  const auto& vertex_positions = mesh.vertices().attributes().at<math::fvec3>("position");
33  const auto& face_normals = mesh.faces().attributes().at<math::fvec3>("normal");
34 
35  // Init traversal result
36  navmesh_traversal traversal;
37  traversal.edge = nullptr;
38 
39  geom::triangle_region region;
40 
41  auto target_point = end;
42  auto traversal_direction = math::normalize(end - start);
44 
45  geom::brep_edge* previous_closest_edge{};
46 
47  do
48  {
49  // Get vertex positions of face
50  auto loop_it = face->loops().begin();
51  const auto& a = vertex_positions[loop_it->vertex()->index()];
52  const auto& b = vertex_positions[(++loop_it)->vertex()->index()];
53  const auto& c = vertex_positions[(++loop_it)->vertex()->index()];
54 
55  // Find closest point on face to target point
56  std::tie(closest_point, region) = geom::closest_point(a, b, c, target_point);
57 
58  // If point is on the face
59  if (geom::is_face_region(region))
60  {
61  // Traversal complete
62  break;
63  }
64 
65  geom::brep_loop* closest_loop;
66 
67  // If point is on an edge
68  if (geom::is_edge_region(region))
69  {
70  // Get index of the edge
71  const auto edge_index = geom::edge_index(region);
72 
73  // Get pointer to the edge's loop
74  auto loop_it = face->loops().begin();
75  std::advance(loop_it, edge_index);
76  closest_loop = *loop_it;
77 
78  // If edge is a boundary edge
79  if (closest_loop->edge()->loops().size() == 1)
80  {
81  // Abort traversal
82  traversal.edge = closest_loop->edge();
83  break;
84  }
85  }
86  else
87  {
88  // Point is on a vertex, get index of vertex on which point lies
89  const auto vertex_index = geom::vertex_index(region);
90 
91  // Get pointer to loop originating at the vertex
92  auto loop_it = face->loops().begin();
93  std::advance(loop_it, vertex_index);
94  geom::brep_loop* loop = *loop_it;
95 
96  // If previous loop edge is a boundary edge
97  if (loop->previous()->edge()->loops().size() == 1)
98  {
99  // If current loop edge is also a boundary edge
100  if (loop->edge()->loops().size() == 1)
101  {
102  // Abort traversal
103  traversal.edge = loop->edge();
104  break;
105  }
106 
107  // Select current loop
108  closest_loop = loop;
109  }
110  // If current loop edge is a boundary edge
111  else if (loop->edge()->loops().size() == 1)
112  {
113  // Select previous loop
114  closest_loop = loop->previous();
115  }
116  else
117  // Neither loop edge is a boundary edge
118  {
119  // Calculate direction of current loop edge
120  const auto current_direction = math::normalize
121  (
122  vertex_positions[loop->next()->vertex()->index()] -
123  vertex_positions[loop->vertex()->index()]
124  );
125 
126  // Calculate direction of previous loop edge
127  const auto previous_direction = math::normalize
128  (
129  vertex_positions[loop->vertex()->index()] -
130  vertex_positions[loop->previous()->vertex()->index()]
131  );
132 
133  // Select loop with minimal angle between edge and traversal direction
134  if (std::abs(math::dot(traversal_direction, current_direction)) <
135  std::abs(math::dot(traversal_direction, previous_direction)))
136  {
137  closest_loop = loop;
138  }
139  else
140  {
141  closest_loop = loop->previous();
142  }
143  }
144  }
145 
146  // Get edge of closest loop
147  geom::brep_edge* closest_edge = closest_loop->edge();
148 
149  // If closest edge is previous closest edge
150  if (closest_edge == previous_closest_edge)
151  {
152  // Abort traversal
153  traversal.edge = closest_edge;
154  break;
155  }
156 
157  // Remember closest edge to prevent infinite loops
158  previous_closest_edge = closest_edge;
159 
160  // Find a loop and face that shares the closest edge
161  geom::brep_loop* symmetric_loop = closest_edge->loops().front();
162  if (symmetric_loop == closest_loop)
163  {
164  symmetric_loop = closest_edge->loops().back();
165  }
166  geom::brep_face* symmetric_face = symmetric_loop->face();
167 
168  // Find quaternion representing rotation from normal of first face to normal of second face
169  const auto& n0 = face_normals[face->index()];
170  const auto& n1 = face_normals[symmetric_face->index()];
171  const auto rotation = math::rotation(n0, n1);
172 
173  // Rotate target point
174  target_point = rotation * (target_point - closest_point) + closest_point;
175 
176  // Rotate traversal direction
177  traversal_direction = rotation * traversal_direction;
178 
179  // Move to next face
180  face = symmetric_face;
181  }
182  while (true);
183 
184  traversal.face = face;
185  traversal.target_point = target_point;
186  traversal.closest_point = closest_point;
187 
188  auto loop_it = face->loops().begin();
189  const auto& a = vertex_positions[loop_it->vertex()->index()];
190  const auto& b = vertex_positions[(++loop_it)->vertex()->index()];
191  const auto& c = vertex_positions[(++loop_it)->vertex()->index()];
192  traversal.barycentric = geom::cartesian_to_barycentric(traversal.closest_point, a, b, c);
193 
194  return traversal;
195 }
196 
197 } // namespace ai
brep_loop * front() const noexcept
Returns the first loop.
Definition: brep-edge.hpp:121
constexpr std::size_t size() const noexcept
Returns the number of loops in the list.
Definition: brep-edge.hpp:203
brep_loop * back() const noexcept
Returns the last loop.
Definition: brep-edge.hpp:127
Curve segment bounded by two vertices.
Definition: brep-edge.hpp:237
constexpr const brep_edge_loop_list & loops() const noexcept
Returns the list of loops that share this edge.
Definition: brep-edge.hpp:262
constexpr const_iterator begin() const noexcept
Returns an iterator to the first loop.
Definition: brep-face.hpp:137
Portion of a shell bounded by loops.
Definition: brep-face.hpp:244
constexpr const brep_face_loop_list & loops() const noexcept
Returns the list of loops associated with this face.
Definition: brep-face.hpp:261
constexpr std::size_t index() const noexcept
Returns the index of this face in the mesh face array.
Definition: brep-face.hpp:255
Connected boundary of a single face.
Definition: brep-loop.hpp:38
constexpr brep_loop * next() const noexcept
Returns a pointer to the next loop.
Definition: brep-loop.hpp:76
constexpr brep_edge * edge() const noexcept
Returns a pointer to the loop edge.
Definition: brep-loop.hpp:64
constexpr brep_loop * previous() const noexcept
Returns a pointer to the previous loop.
Definition: brep-loop.hpp:82
constexpr brep_face * face() const noexcept
Returns a pointer to the loop face.
Definition: brep-loop.hpp:70
constexpr brep_vertex * vertex() const noexcept
Returns a pointer to the loop vertex.
Definition: brep-loop.hpp:58
Boundary representation (B-rep) of a mesh.
Definition: brep-mesh.hpp:34
constexpr std::size_t index() const noexcept
Returns the index of this vertex in the mesh vertex array.
Artificial intelligence (AI)
Definition: ai.hpp:24
navmesh_traversal traverse_navmesh(const geom::brep_mesh &mesh, geom::brep_face *face, const math::fvec3 &start, const math::fvec3 &end)
Moves a point along the surface of a mesh.
Definition: navmesh.cpp:29
constexpr bool is_edge_region(triangle_region region) noexcept
Checks whether a triangle voronoi region is an edge region.
Definition: coordinates.hpp:76
constexpr std::uint8_t vertex_index(triangle_region region) noexcept
Returns the vertex index of a vertex region.
constexpr bool is_face_region(triangle_region region) noexcept
Checks whether a triangle voronoi region is a face region.
Definition: coordinates.hpp:64
triangle_region
Voronoi regions of a triangle.
Definition: coordinates.hpp:34
constexpr point< T, 3 > cartesian_to_barycentric(const point< T, 3 > &p, const point< T, 3 > &a, const point< T, 3 > &b, const point< T, 3 > &c) noexcept
Converts Cartesian coordinates to barycentric coordinates.
constexpr point< T, N > closest_point(const ray< T, N > &a, const point< T, N > &b) noexcept
Calculates the closest point on a ray to a point.
constexpr std::uint8_t edge_index(triangle_region region) noexcept
Returns the edge index of an edge region.
quaternion< T > normalize(const quaternion< T > &q)
Normalizes a quaternion.
Definition: quaternion.hpp:679
constexpr vector< T, N > abs(const vector< T, N > &x)
Returns the absolute values of each element.
Definition: vector.hpp:985
quaternion< T > rotation(const vec3< T > &from, const vec3< T > &to, T tolerance=T{1e-6})
Constructs a quaternion representing the minimum rotation from one direction to another direction.
Definition: quaternion.hpp:691
constexpr T dot(const quaternion< T > &a, const quaternion< T > &b) noexcept
Calculates the dot product of two quaternions.
Definition: quaternion.hpp:572
geom::brep_edge * edge
Definition: navmesh.hpp:36
n-dimensional vector.
Definition: vector.hpp:44