💿🐜 Antkeeper source code https://antkeeper.com
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

546 lines
15 KiB

  1. /*
  2. * Copyright (C) 2017 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. #include "navmesh.hpp"
  20. #include <fstream>
  21. #include <map>
  22. #include <sstream>
  23. #include <limits>
  24. Navmesh::Navmesh()
  25. {}
  26. Navmesh::~Navmesh()
  27. {
  28. destroy();
  29. }
  30. bool Navmesh::create(const std::vector<Vector3>& vertices, const std::vector<std::size_t>& indices)
  31. {
  32. destroy();
  33. if (indices.size() % 3 != 0)
  34. {
  35. std::cerr << "Navmesh::create(): index count is non multiple of 3\n";
  36. return false;
  37. }
  38. // Copy vertices
  39. this->vertices.resize(vertices.size());
  40. for (std::size_t i = 0; i < vertices.size(); ++i)
  41. {
  42. Navmesh::Vertex* vertex = new Navmesh::Vertex();
  43. vertex->edge = nullptr;
  44. vertex->position = vertices[i];
  45. vertex->flags = 0;
  46. vertex->index = i;
  47. this->vertices[i] = vertex;
  48. }
  49. // Allocate triangles
  50. triangles.resize(indices.size() / 3, nullptr);
  51. std::size_t currentTriangle = 0;
  52. // Load triangles
  53. std::map<std::pair<std::size_t, std::size_t>, Navmesh::Edge*> edgeMap;
  54. for (std::size_t i = 0; i < indices.size(); i += 3)
  55. {
  56. std::size_t a = indices[i];
  57. std::size_t b = indices[i + 1];
  58. std::size_t c = indices[i + 2];
  59. if (a >= vertices.size() || b >= vertices.size() || c >= vertices.size())
  60. {
  61. std::cerr << "Navmesh::create(): Mesh contains invalid index.\n";
  62. destroy();
  63. return false;
  64. }
  65. // Allocate three edges and a triangle
  66. Navmesh::Edge* ab = new Navmesh::Edge();
  67. Navmesh::Edge* bc = new Navmesh::Edge();
  68. Navmesh::Edge* ca = new Navmesh::Edge();
  69. Navmesh::Triangle* triangle = new Navmesh::Triangle();
  70. // Zero triangle flags
  71. triangle->flags = 0;
  72. // Zero edge flags
  73. ab->flags = 0;
  74. bc->flags = 0;
  75. ca->flags = 0;
  76. // Set triangle start edge
  77. triangle->edge = ab;
  78. // For each edge in this triangle
  79. std::size_t triangleIndices[] = {a, b, c};
  80. Navmesh::Edge* triangleEdges[] = {ab, bc, ca};
  81. for (std::size_t j = 0; j < 3; ++j)
  82. {
  83. // Set edge properties
  84. Navmesh::Edge* edge = triangleEdges[j];
  85. edge->triangle = triangle;
  86. edge->vertex = this->vertices[triangleIndices[j]];
  87. edge->previous = triangleEdges[(j + 2) % 3];
  88. edge->next = triangleEdges[(j + 1) % 3];
  89. edge->symmetric = nullptr;
  90. // Point vertex to this edge
  91. edge->vertex->edge = edge;
  92. // Check for symmetry
  93. std::pair<std::size_t, std::size_t> symmetricPair(triangleIndices[(j + 1) % 3], triangleIndices[j]);
  94. std::map<std::pair<std::size_t, std::size_t>, Navmesh::Edge*>::iterator it = edgeMap.find(symmetricPair);
  95. if (it == edgeMap.end())
  96. {
  97. // No symmetric edge found, insert this edge into the map
  98. std::pair<std::size_t, std::size_t> pair(triangleIndices[j], triangleIndices[(j + 1) % 3]);
  99. edgeMap[pair] = edge;
  100. }
  101. else
  102. {
  103. // Symmetric edge found, connect
  104. edge->symmetric = it->second;
  105. it->second->symmetric = edge;
  106. }
  107. }
  108. // Set edge indices and add edges to the edge list
  109. ab->index = edges.size();
  110. edges.push_back(ab);
  111. bc->index = edges.size();
  112. edges.push_back(bc);
  113. ca->index = edges.size();
  114. edges.push_back(ca);
  115. // Set triangle index and add triangle to the triangle list
  116. triangle->index = currentTriangle;
  117. triangles[currentTriangle++] = triangle;
  118. }
  119. calculateNormals();
  120. return true;
  121. }
  122. void Navmesh::destroy()
  123. {
  124. for (std::size_t i = 0; i < vertices.size(); ++i)
  125. delete vertices[i];
  126. for (std::size_t i = 0; i < edges.size(); ++i)
  127. delete edges[i];
  128. for (std::size_t i = 0; i < triangles.size(); ++i)
  129. delete triangles[i];
  130. vertices.clear();
  131. edges.clear();
  132. triangles.clear();
  133. }
  134. bool Navmesh::loadOBJ(const std::string& filename)
  135. {
  136. // Open OBJ file
  137. std::ifstream file(filename.c_str());
  138. if (!file.is_open())
  139. {
  140. std::cerr << "Navmesh::loadOBJ(): Failed to open Wavefront OBJ file \"" << filename << "\"" << std::endl;
  141. return false;
  142. }
  143. // Read OBJ file from file stream
  144. if (!readOBJ(&file, filename))
  145. {
  146. std::cerr << "Navmesh::loadOBJ(): Failed to read Wavefront OBJ file \"" << filename << "\"" << std::endl;
  147. file.close();
  148. return false;
  149. }
  150. // Close OBJ file
  151. file.close();
  152. return true;
  153. }
  154. void Navmesh::traverse(Navmesh::Triangle* startTriangle, const Vector3& startPosition, const Vector3& startVelocity, std::vector<Navmesh::Step>* traversal)
  155. {
  156. // Form initial traversal step
  157. Navmesh::Step step;
  158. step.triangle = startTriangle;
  159. step.start = normalize_barycentric(startPosition);
  160. step.end = step.start;
  161. step.edge = nullptr;
  162. // Determine the maximum distance of the traversal
  163. float maxDistance = glm::length(startVelocity);
  164. // Set initial velocity
  165. Vector3 velocity = startVelocity;
  166. // Traverse navmesh
  167. float distance = 0.0f;
  168. while (distance < maxDistance)
  169. {
  170. // Grab triangle coordinates
  171. const Vector3& a = step.triangle->edge->vertex->position;
  172. const Vector3& b = step.triangle->edge->next->vertex->position;
  173. const Vector3& c = step.triangle->edge->previous->vertex->position;
  174. // Calculate target position
  175. Vector3 cartesianStart = cartesian(step.start, a, b, c);
  176. Vector3 target = cartesianStart + velocity;
  177. // Find closest point on triangle to target position
  178. closestPointOnTriangle(target, step.triangle, &step.end, &step.edge);
  179. step.end = normalize_barycentric(step.end);
  180. // Add step to the traversal
  181. traversal->push_back(step);
  182. // Determine distance traveled by the step
  183. Vector3 cartesianEnd = cartesian(step.end, a, b, c);
  184. distance += glm::length(cartesianEnd - cartesianStart);
  185. // Check for no movement
  186. if (cartesianEnd == cartesianStart)
  187. {
  188. /*
  189. std::cout << "the same!\n";
  190. if (step.edge == nullptr)
  191. std::cout << "\tand no edge\n";
  192. else if (step.edge->symmetric == nullptr)
  193. {
  194. std::cout << "\tand disconnected\n";
  195. //step.edge = step.edge->previous;
  196. }
  197. //break;
  198. */
  199. }
  200. // Check if traversal is complete or edge is disconnected
  201. if (step.edge == nullptr || step.edge->symmetric == nullptr)
  202. {
  203. break;
  204. }
  205. // Recalculate velocity
  206. Quaternion rotation = glm::rotation(step.triangle->normal, step.edge->symmetric->triangle->normal);
  207. velocity = glm::normalize(rotation * velocity) * (maxDistance - distance);
  208. // Move to the next triangle
  209. step.triangle = step.edge->symmetric->triangle;
  210. // Calculate barycentric starting coordinates of the next step
  211. step.start = normalize_barycentric(barycentric(cartesianEnd,
  212. step.triangle->edge->vertex->position,
  213. step.triangle->edge->next->vertex->position,
  214. step.triangle->edge->previous->vertex->position));
  215. step.end = step.start;
  216. step.edge = nullptr;
  217. }
  218. /*
  219. // Add triangle to visited list
  220. visited->push_back(triangle);
  221. // Grab triangle coordinates
  222. const glm::vec3& a = triangle->edge->vertex->position;
  223. const glm::vec3& b = triangle->edge->next->vertex->position;
  224. const glm::vec3& c = triangle->edge->previous->vertex->position;
  225. // Project target onto triangle
  226. glm::vec3 closestPoint;
  227. int edgeIndex = -1;
  228. WingedEdge::Edge* closestEdge = nullptr;
  229. project_on_triangle(target, a, b, c, &closestPoint, &edgeIndex);
  230. *end = closestPoint;
  231. // Determine if projected target is on an edge
  232. switch (edgeIndex)
  233. {
  234. case -1:
  235. // Projected target inside triangle
  236. return;
  237. case 0:
  238. closestEdge = triangle->edge;
  239. break;
  240. case 1:
  241. closestEdge = triangle->edge->next;
  242. break;
  243. case 2:
  244. closestEdge = triangle->edge->previous;
  245. break;
  246. }
  247. // If edge is not loose, repeat with connected triangle
  248. if (closestEdge->symmetric != nullptr)
  249. {
  250. for (std::size_t i = 0; i < visited->size() - 1; ++i)
  251. {
  252. if ((*visited)[i] == closestEdge->symmetric->triangle)
  253. return;
  254. }
  255. move(mesh, closestEdge->symmetric->triangle, closestPoint, target, visited, end);
  256. }
  257. */
  258. }
  259. /*
  260. if (steerCCW.isTriggered())
  261. {
  262. glm::quat rotation = glm::angleAxis(0.1f, navi.triangle->normal);
  263. navi_forward = glm::normalize(rotation * navi_forward);
  264. }
  265. if (steerCW.isTriggered())
  266. {
  267. glm::quat rotation = glm::angleAxis(-0.1f, navi.triangle->normal);
  268. navi_forward = glm::normalize(rotation * navi_forward);
  269. }
  270. if (navigate.isTriggered())
  271. {
  272. Mesh::Triangle* triangle = navi.triangle;
  273. glm::vec3 start = navi.position;
  274. glm::vec3 target = start + navi_forward * 0.02f;
  275. std::vector<Mesh::Triangle*> visited;
  276. glm::vec3 end;
  277. move(&sceneManager.getTerrain()->mesh, triangle, start, target, &visited, &end);
  278. Mesh::Triangle* end_triangle = visited[visited.size() - 1];
  279. const glm::vec3& a = end_triangle->edge->vertex->position;
  280. const glm::vec3& b = end_triangle->edge->next->vertex->position;
  281. const glm::vec3& c = end_triangle->edge->previous->vertex->position;
  282. glm::vec3 p = (a + b + c) / 3.0f;
  283. const glm::vec3& n = end_triangle->normal;
  284. glm::vec3 projected_start = project_on_plane(start, p, n);
  285. // Calculate difference between positions
  286. glm::vec3 difference = end - projected_start;
  287. if (glm::dot(difference, difference) != 0.0f)
  288. {
  289. if (end_triangle != triangle)
  290. {
  291. glm::quat alignment = glm::rotation(triangle->normal, end_triangle->normal);
  292. navi_forward = glm::normalize(alignment * navi_forward);
  293. }
  294. }
  295. navi.position = end;
  296. navi.triangle = visited[visited.size() - 1];
  297. }
  298. */
  299. void Navmesh::calculateNormals()
  300. {
  301. for (std::size_t i = 0; i < triangles.size(); ++i)
  302. {
  303. Navmesh::Triangle* triangle = triangles[i];
  304. // Calculate surface normal
  305. const Vector3& a = triangle->edge->vertex->position;
  306. const Vector3& b = triangle->edge->next->vertex->position;
  307. const Vector3& c = triangle->edge->previous->vertex->position;
  308. Vector3 ba = b - a;
  309. Vector3 ca = c - a;
  310. triangle->normal = glm::normalize(glm::cross(ba, ca));
  311. }
  312. }
  313. bool Navmesh::readOBJ(std::istream* stream, const std::string& filename)
  314. {
  315. std::string line;
  316. std::vector<Vector3> vertices;
  317. std::vector<std::size_t> indices;
  318. while (stream->good() && std::getline(*stream, line))
  319. {
  320. // Tokenize line
  321. std::vector<std::string> tokens;
  322. std::string token;
  323. std::istringstream linestream(line);
  324. while (linestream >> token)
  325. tokens.push_back(token);
  326. // Skip empty lines and comments
  327. if (tokens.empty() || tokens[0][0] == '#')
  328. continue;
  329. if (tokens[0] == "v")
  330. {
  331. if (tokens.size() != 4)
  332. {
  333. std::cerr << "Navmesh::readOBJ(): Invalid line \"" << line << "\" in file \"" << filename << "\"" << std::endl;
  334. return false;
  335. }
  336. Vector3 vertex;
  337. std::stringstream(tokens[1]) >> vertex.x;
  338. std::stringstream(tokens[2]) >> vertex.y;
  339. std::stringstream(tokens[3]) >> vertex.z;
  340. vertices.push_back(vertex);
  341. }
  342. else if (tokens[0] == "f")
  343. {
  344. if (tokens.size() != 4)
  345. {
  346. std::cerr << "Navmesh::readOBJ(): Invalid line \"" << line << "\" in file \"" << filename << "\"" << std::endl;
  347. return false;
  348. }
  349. std::size_t a, b, c;
  350. std::stringstream(tokens[1]) >> a;
  351. std::stringstream(tokens[2]) >> b;
  352. std::stringstream(tokens[3]) >> c;
  353. a -= 1;
  354. b -= 1;
  355. c -= 1;
  356. indices.push_back(a);
  357. indices.push_back(b);
  358. indices.push_back(c);
  359. }
  360. }
  361. return create(vertices, indices);
  362. }
  363. Vector3 Navmesh::barycentric(const Vector3& p, const Vector3& a, const Vector3& b, const Vector3& c)
  364. {
  365. Vector3 v0 = b - a;
  366. Vector3 v1 = c - a;
  367. Vector3 v2 = p - a;
  368. float d00 = glm::dot(v0, v0);
  369. float d01 = glm::dot(v0, v1);
  370. float d11 = glm::dot(v1, v1);
  371. float d20 = glm::dot(v2, v0);
  372. float d21 = glm::dot(v2, v1);
  373. float denom = d00 * d11 - d01 * d01;
  374. Vector3 result;
  375. result.y = (d11 * d20 - d01 * d21) / denom; // v
  376. result.z = (d00 * d21 - d01 * d20) / denom; // w
  377. result.x = 1.0f - result.y - result.z; // u
  378. return result;
  379. }
  380. Vector3 Navmesh::cartesian(const Vector3& p, const Vector3& a, const Vector3& b, const Vector3& c)
  381. {
  382. return a * p.x + b * p.y + c * p.z;
  383. }
  384. // code taken from Detour's dtClosestPtPointTriangle
  385. // @see https://github.com/recastnavigation/recastnavigation/blob/master/Detour/Source/DetourCommon.cpp
  386. // (zlib license)
  387. void Navmesh::closestPointOnTriangle(const Vector3& p, const Navmesh::Triangle* triangle, Vector3* closestPoint, Navmesh::Edge** closestEdge)
  388. {
  389. // Grab triangle coordinates
  390. const Vector3& a = triangle->edge->vertex->position;
  391. const Vector3& b = triangle->edge->next->vertex->position;
  392. const Vector3& c = triangle->edge->previous->vertex->position;
  393. // Check if P in vertex region outside A
  394. Vector3 ab = b - a;
  395. Vector3 ac = c - a;
  396. Vector3 ap = p - a;
  397. float d1 = glm::dot(ab, ap);
  398. float d2 = glm::dot(ac, ap);
  399. if (d1 <= 0.0f && d2 <= 0.0f)
  400. {
  401. // Barycentric coordinates (1, 0, 0)
  402. *closestPoint = Vector3(1.0f, 0.0f, 0.0f);
  403. *closestEdge = triangle->edge;
  404. return;
  405. }
  406. // Check if P in vertex region outside B
  407. Vector3 bp = p - b;
  408. float d3 = glm::dot(ab, bp);
  409. float d4 = glm::dot(ac, bp);
  410. if (d3 >= 0.0f && d4 <= d3)
  411. {
  412. // Barycentric coordinates (0, 1, 0)
  413. *closestPoint = Vector3(0.0f, 1.0f, 0.0f);
  414. *closestEdge = triangle->edge->next;
  415. return;
  416. }
  417. // Check if P in edge region of AB, if so return projection of P onto AB
  418. float vc = d1 * d4 - d3 * d2;
  419. if (vc <= 0.0f && d1 >= 0.0f && d3 <= 0.0f)
  420. {
  421. // barycentric coordinates (1-v,v,0)
  422. float v = d1 / (d1 - d3);
  423. *closestPoint = Vector3(1.0f - v, v, 0.0f);
  424. *closestEdge = triangle->edge;
  425. return;
  426. }
  427. // Check if P in vertex region outside C
  428. Vector3 cp = p - c;
  429. float d5 = glm::dot(ab, cp);
  430. float d6 = glm::dot(ac, cp);
  431. if (d6 >= 0.0f && d5 <= d6)
  432. {
  433. // Barycentric coordinates (0, 0, 1)
  434. *closestPoint = Vector3(0.0f, 0.0f, 1.0f);
  435. *closestEdge = triangle->edge->previous;
  436. return;
  437. }
  438. // Check if P in edge region of AC, if so return projection of P onto AC
  439. float vb = d5 * d2 - d1 * d6;
  440. if (vb <= 0.0f && d2 >= 0.0f && d6 <= 0.0f)
  441. {
  442. // Barycentric coordinates (1 - w, 0, w)
  443. float w = d2 / (d2 - d6);
  444. *closestPoint = Vector3(1.0f - w, 0.0f, w);
  445. *closestEdge = triangle->edge->previous;
  446. return;
  447. }
  448. // Check if P in edge region of BC, if so return projection of P onto BC
  449. float va = d3 * d6 - d5 * d4;
  450. if (va <= 0.0f && (d4 - d3) >= 0.0f && (d5 - d6) >= 0.0f)
  451. {
  452. // Barycentric coordinates (0, 1 - w, w)
  453. float w = (d4 - d3) / ((d4 - d3) + (d5 - d6));
  454. *closestPoint = Vector3(0.0f, 1.0f - w, w);
  455. *closestEdge = triangle->edge->next;
  456. return;
  457. }
  458. // P inside face region. Compute Q through its barycentric coordinates (u, v, w)
  459. float denom = 1.0f / (va + vb + vc);
  460. float v = vb * denom;
  461. float w = vc * denom;
  462. *closestPoint = Vector3(1.0f - v - w, v, w);
  463. *closestEdge = nullptr;
  464. }