💿🐜 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.

691 lines
19 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. calculateBounds();
  121. return true;
  122. }
  123. void Navmesh::destroy()
  124. {
  125. for (std::size_t i = 0; i < vertices.size(); ++i)
  126. delete vertices[i];
  127. for (std::size_t i = 0; i < edges.size(); ++i)
  128. delete edges[i];
  129. for (std::size_t i = 0; i < triangles.size(); ++i)
  130. delete triangles[i];
  131. vertices.clear();
  132. edges.clear();
  133. triangles.clear();
  134. }
  135. bool Navmesh::loadOBJ(const std::string& filename)
  136. {
  137. // Open OBJ file
  138. std::ifstream file(filename.c_str());
  139. if (!file.is_open())
  140. {
  141. std::cerr << "Navmesh::loadOBJ(): Failed to open Wavefront OBJ file \"" << filename << "\"" << std::endl;
  142. return false;
  143. }
  144. // Read OBJ file from file stream
  145. if (!readOBJ(&file, filename))
  146. {
  147. std::cerr << "Navmesh::loadOBJ(): Failed to read Wavefront OBJ file \"" << filename << "\"" << std::endl;
  148. file.close();
  149. return false;
  150. }
  151. // Close OBJ file
  152. file.close();
  153. return true;
  154. }
  155. void Navmesh::traverse(Navmesh::Triangle* startTriangle, const Vector3& startPosition, const Vector3& startVelocity, std::vector<Navmesh::Step>* traversal)
  156. {
  157. // Form initial traversal step
  158. Navmesh::Step step;
  159. step.triangle = startTriangle;
  160. step.start = normalize_barycentric(startPosition);
  161. step.end = step.start;
  162. step.edge = nullptr;
  163. // Determine the maximum distance of the traversal
  164. float maxDistance = glm::length(startVelocity);
  165. // Set initial velocity
  166. Vector3 velocity = startVelocity;
  167. // Traverse navmesh
  168. float distance = 0.0f;
  169. while (distance < maxDistance)
  170. {
  171. // Grab triangle coordinates
  172. const Vector3& a = step.triangle->edge->vertex->position;
  173. const Vector3& b = step.triangle->edge->next->vertex->position;
  174. const Vector3& c = step.triangle->edge->previous->vertex->position;
  175. // Calculate target position
  176. Vector3 cartesianStart = cartesian(step.start, a, b, c);
  177. Vector3 target = cartesianStart + velocity;
  178. // Find closest point on triangle to target position
  179. closestPointOnTriangle(target, step.triangle, &step.end, &step.edge);
  180. step.end = normalize_barycentric(step.end);
  181. // Add step to the traversal
  182. traversal->push_back(step);
  183. // Determine distance traveled by the step
  184. Vector3 cartesianEnd = cartesian(step.end, a, b, c);
  185. distance += glm::length(cartesianEnd - cartesianStart);
  186. // Check for no movement
  187. if (cartesianEnd == cartesianStart)
  188. {
  189. /*
  190. std::cout << "the same!\n";
  191. if (step.edge == nullptr)
  192. std::cout << "\tand no edge\n";
  193. else if (step.edge->symmetric == nullptr)
  194. {
  195. std::cout << "\tand disconnected\n";
  196. //step.edge = step.edge->previous;
  197. }
  198. //break;
  199. */
  200. }
  201. // Check if traversal is complete or edge is disconnected
  202. if (step.edge == nullptr || step.edge->symmetric == nullptr)
  203. {
  204. break;
  205. }
  206. // Recalculate velocity
  207. Quaternion rotation = glm::rotation(step.triangle->normal, step.edge->symmetric->triangle->normal);
  208. velocity = glm::normalize(rotation * velocity) * (maxDistance - distance);
  209. // Move to the next triangle
  210. step.triangle = step.edge->symmetric->triangle;
  211. // Ensure triangle wasn't already visited
  212. for (const Step& visited: (*traversal))
  213. {
  214. if (step.triangle == visited.triangle)
  215. {
  216. return;
  217. }
  218. }
  219. // Calculate barycentric starting coordinates of the next step
  220. step.start = normalize_barycentric(barycentric(cartesianEnd,
  221. step.triangle->edge->vertex->position,
  222. step.triangle->edge->next->vertex->position,
  223. step.triangle->edge->previous->vertex->position));
  224. step.end = step.start;
  225. step.edge = nullptr;
  226. }
  227. /*
  228. // Add triangle to visited list
  229. visited->push_back(triangle);
  230. // Grab triangle coordinates
  231. const glm::vec3& a = triangle->edge->vertex->position;
  232. const glm::vec3& b = triangle->edge->next->vertex->position;
  233. const glm::vec3& c = triangle->edge->previous->vertex->position;
  234. // Project target onto triangle
  235. glm::vec3 closestPoint;
  236. int edgeIndex = -1;
  237. WingedEdge::Edge* closestEdge = nullptr;
  238. project_on_triangle(target, a, b, c, &closestPoint, &edgeIndex);
  239. *end = closestPoint;
  240. // Determine if projected target is on an edge
  241. switch (edgeIndex)
  242. {
  243. case -1:
  244. // Projected target inside triangle
  245. return;
  246. case 0:
  247. closestEdge = triangle->edge;
  248. break;
  249. case 1:
  250. closestEdge = triangle->edge->next;
  251. break;
  252. case 2:
  253. closestEdge = triangle->edge->previous;
  254. break;
  255. }
  256. // If edge is not loose, repeat with connected triangle
  257. if (closestEdge->symmetric != nullptr)
  258. {
  259. for (std::size_t i = 0; i < visited->size() - 1; ++i)
  260. {
  261. if ((*visited)[i] == closestEdge->symmetric->triangle)
  262. return;
  263. }
  264. move(mesh, closestEdge->symmetric->triangle, closestPoint, target, visited, end);
  265. }
  266. */
  267. }
  268. /*
  269. if (steerCCW.isTriggered())
  270. {
  271. glm::quat rotation = glm::angleAxis(0.1f, navi.triangle->normal);
  272. navi_forward = glm::normalize(rotation * navi_forward);
  273. }
  274. if (steerCW.isTriggered())
  275. {
  276. glm::quat rotation = glm::angleAxis(-0.1f, navi.triangle->normal);
  277. navi_forward = glm::normalize(rotation * navi_forward);
  278. }
  279. if (navigate.isTriggered())
  280. {
  281. Mesh::Triangle* triangle = navi.triangle;
  282. glm::vec3 start = navi.position;
  283. glm::vec3 target = start + navi_forward * 0.02f;
  284. std::vector<Mesh::Triangle*> visited;
  285. glm::vec3 end;
  286. move(&sceneManager.getTerrain()->mesh, triangle, start, target, &visited, &end);
  287. Mesh::Triangle* end_triangle = visited[visited.size() - 1];
  288. const glm::vec3& a = end_triangle->edge->vertex->position;
  289. const glm::vec3& b = end_triangle->edge->next->vertex->position;
  290. const glm::vec3& c = end_triangle->edge->previous->vertex->position;
  291. glm::vec3 p = (a + b + c) / 3.0f;
  292. const glm::vec3& n = end_triangle->normal;
  293. glm::vec3 projected_start = project_on_plane(start, p, n);
  294. // Calculate difference between positions
  295. glm::vec3 difference = end - projected_start;
  296. if (glm::dot(difference, difference) != 0.0f)
  297. {
  298. if (end_triangle != triangle)
  299. {
  300. glm::quat alignment = glm::rotation(triangle->normal, end_triangle->normal);
  301. navi_forward = glm::normalize(alignment * navi_forward);
  302. }
  303. }
  304. navi.position = end;
  305. navi.triangle = visited[visited.size() - 1];
  306. }
  307. */
  308. void Navmesh::calculateNormals()
  309. {
  310. for (std::size_t i = 0; i < triangles.size(); ++i)
  311. {
  312. Navmesh::Triangle* triangle = triangles[i];
  313. // Calculate surface normal
  314. const Vector3& a = triangle->edge->vertex->position;
  315. const Vector3& b = triangle->edge->next->vertex->position;
  316. const Vector3& c = triangle->edge->previous->vertex->position;
  317. Vector3 ba = b - a;
  318. Vector3 ca = c - a;
  319. triangle->normal = glm::normalize(glm::cross(ba, ca));
  320. }
  321. }
  322. void Navmesh::calculateBounds()
  323. {
  324. Vector3 min(std::numeric_limits<float>::infinity());
  325. Vector3 max(-std::numeric_limits<float>::infinity());
  326. for (const Navmesh::Vertex* vertex: vertices)
  327. {
  328. min.x = std::min(min.x, vertex->position.x);
  329. min.y = std::min(min.y, vertex->position.y);
  330. min.z = std::min(min.z, vertex->position.z);
  331. max.x = std::max(max.x, vertex->position.x);
  332. max.y = std::max(max.y, vertex->position.y);
  333. max.z = std::max(max.z, vertex->position.z);
  334. }
  335. bounds.setMin(min);
  336. bounds.setMax(max);
  337. }
  338. bool Navmesh::readOBJ(std::istream* stream, const std::string& filename)
  339. {
  340. std::string line;
  341. std::vector<Vector3> vertices;
  342. std::vector<std::size_t> indices;
  343. while (stream->good() && std::getline(*stream, line))
  344. {
  345. // Tokenize line
  346. std::vector<std::string> tokens;
  347. std::string token;
  348. std::istringstream linestream(line);
  349. while (linestream >> token)
  350. tokens.push_back(token);
  351. // Skip empty lines and comments
  352. if (tokens.empty() || tokens[0][0] == '#')
  353. continue;
  354. if (tokens[0] == "v")
  355. {
  356. if (tokens.size() != 4)
  357. {
  358. std::cerr << "Navmesh::readOBJ(): Invalid line \"" << line << "\" in file \"" << filename << "\"" << std::endl;
  359. return false;
  360. }
  361. Vector3 vertex;
  362. std::stringstream(tokens[1]) >> vertex.x;
  363. std::stringstream(tokens[2]) >> vertex.y;
  364. std::stringstream(tokens[3]) >> vertex.z;
  365. vertices.push_back(vertex);
  366. }
  367. else if (tokens[0] == "f")
  368. {
  369. if (tokens.size() != 4)
  370. {
  371. std::cerr << "Navmesh::readOBJ(): Invalid line \"" << line << "\" in file \"" << filename << "\"" << std::endl;
  372. return false;
  373. }
  374. std::size_t a, b, c;
  375. std::stringstream(tokens[1]) >> a;
  376. std::stringstream(tokens[2]) >> b;
  377. std::stringstream(tokens[3]) >> c;
  378. a -= 1;
  379. b -= 1;
  380. c -= 1;
  381. indices.push_back(a);
  382. indices.push_back(b);
  383. indices.push_back(c);
  384. }
  385. }
  386. return create(vertices, indices);
  387. }
  388. Vector3 Navmesh::barycentric(const Vector3& p, const Vector3& a, const Vector3& b, const Vector3& c)
  389. {
  390. Vector3 v0 = b - a;
  391. Vector3 v1 = c - a;
  392. Vector3 v2 = p - a;
  393. float d00 = glm::dot(v0, v0);
  394. float d01 = glm::dot(v0, v1);
  395. float d11 = glm::dot(v1, v1);
  396. float d20 = glm::dot(v2, v0);
  397. float d21 = glm::dot(v2, v1);
  398. float denom = d00 * d11 - d01 * d01;
  399. Vector3 result;
  400. result.y = (d11 * d20 - d01 * d21) / denom; // v
  401. result.z = (d00 * d21 - d01 * d20) / denom; // w
  402. result.x = 1.0f - result.y - result.z; // u
  403. return result;
  404. }
  405. Vector3 Navmesh::cartesian(const Vector3& p, const Vector3& a, const Vector3& b, const Vector3& c)
  406. {
  407. return a * p.x + b * p.y + c * p.z;
  408. }
  409. // code taken from Detour's dtClosestPtPointTriangle
  410. // @see https://github.com/recastnavigation/recastnavigation/blob/master/Detour/Source/DetourCommon.cpp
  411. // (zlib license)
  412. void Navmesh::closestPointOnTriangle(const Vector3& p, const Navmesh::Triangle* triangle, Vector3* closestPoint, Navmesh::Edge** closestEdge)
  413. {
  414. // Grab triangle coordinates
  415. const Vector3& a = triangle->edge->vertex->position;
  416. const Vector3& b = triangle->edge->next->vertex->position;
  417. const Vector3& c = triangle->edge->previous->vertex->position;
  418. // Check if P in vertex region outside A
  419. Vector3 ab = b - a;
  420. Vector3 ac = c - a;
  421. Vector3 ap = p - a;
  422. float d1 = glm::dot(ab, ap);
  423. float d2 = glm::dot(ac, ap);
  424. if (d1 <= 0.0f && d2 <= 0.0f)
  425. {
  426. // Barycentric coordinates (1, 0, 0)
  427. *closestPoint = Vector3(1.0f, 0.0f, 0.0f);
  428. *closestEdge = triangle->edge;
  429. return;
  430. }
  431. // Check if P in vertex region outside B
  432. Vector3 bp = p - b;
  433. float d3 = glm::dot(ab, bp);
  434. float d4 = glm::dot(ac, bp);
  435. if (d3 >= 0.0f && d4 <= d3)
  436. {
  437. // Barycentric coordinates (0, 1, 0)
  438. *closestPoint = Vector3(0.0f, 1.0f, 0.0f);
  439. *closestEdge = triangle->edge->next;
  440. return;
  441. }
  442. // Check if P in edge region of AB, if so return projection of P onto AB
  443. float vc = d1 * d4 - d3 * d2;
  444. if (vc <= 0.0f && d1 >= 0.0f && d3 <= 0.0f)
  445. {
  446. // barycentric coordinates (1-v,v,0)
  447. float v = d1 / (d1 - d3);
  448. *closestPoint = Vector3(1.0f - v, v, 0.0f);
  449. *closestEdge = triangle->edge;
  450. return;
  451. }
  452. // Check if P in vertex region outside C
  453. Vector3 cp = p - c;
  454. float d5 = glm::dot(ab, cp);
  455. float d6 = glm::dot(ac, cp);
  456. if (d6 >= 0.0f && d5 <= d6)
  457. {
  458. // Barycentric coordinates (0, 0, 1)
  459. *closestPoint = Vector3(0.0f, 0.0f, 1.0f);
  460. *closestEdge = triangle->edge->previous;
  461. return;
  462. }
  463. // Check if P in edge region of AC, if so return projection of P onto AC
  464. float vb = d5 * d2 - d1 * d6;
  465. if (vb <= 0.0f && d2 >= 0.0f && d6 <= 0.0f)
  466. {
  467. // Barycentric coordinates (1 - w, 0, w)
  468. float w = d2 / (d2 - d6);
  469. *closestPoint = Vector3(1.0f - w, 0.0f, w);
  470. *closestEdge = triangle->edge->previous;
  471. return;
  472. }
  473. // Check if P in edge region of BC, if so return projection of P onto BC
  474. float va = d3 * d6 - d5 * d4;
  475. if (va <= 0.0f && (d4 - d3) >= 0.0f && (d5 - d6) >= 0.0f)
  476. {
  477. // Barycentric coordinates (0, 1 - w, w)
  478. float w = (d4 - d3) / ((d4 - d3) + (d5 - d6));
  479. *closestPoint = Vector3(0.0f, 1.0f - w, w);
  480. *closestEdge = triangle->edge->next;
  481. return;
  482. }
  483. // P inside face region. Compute Q through its barycentric coordinates (u, v, w)
  484. float denom = 1.0f / (va + vb + vc);
  485. float v = vb * denom;
  486. float w = vc * denom;
  487. *closestPoint = Vector3(1.0f - v - w, v, w);
  488. *closestEdge = nullptr;
  489. }
  490. std::tuple<bool, float, float, float> intersects(const Ray& ray, const Navmesh::Triangle* triangle)
  491. {
  492. return ray.intersects(triangle->edge->vertex->position, triangle->edge->next->vertex->position, triangle->edge->previous->vertex->position);
  493. }
  494. std::tuple<bool, float, float, std::size_t, std::size_t> intersects(const Ray& ray, const std::list<Navmesh::Triangle*>& triangles)
  495. {
  496. bool intersection = false;
  497. float t0 = std::numeric_limits<float>::infinity();
  498. float t1 = -std::numeric_limits<float>::infinity();
  499. std::size_t index0 = triangles.size();
  500. std::size_t index1 = triangles.size();
  501. for (const Navmesh::Triangle* triangle: triangles)
  502. {
  503. auto result = intersects(ray, triangle);
  504. if (std::get<0>(result))
  505. {
  506. intersection = true;
  507. float t = std::get<1>(result);
  508. float cosTheta = glm::dot(ray.direction, triangle->normal);
  509. if (cosTheta <= 0.0f)
  510. {
  511. // Front-facing
  512. if (t < t0)
  513. {
  514. t0 = t;
  515. index0 = triangle->index;
  516. }
  517. }
  518. else
  519. {
  520. // Back-facing
  521. if (t > t1)
  522. {
  523. t1 = t;
  524. index1 = triangle->index;
  525. }
  526. }
  527. }
  528. }
  529. return std::make_tuple(intersection, t0, t1, index0, index1);
  530. }
  531. std::tuple<bool, float, float, std::size_t, std::size_t> intersects(const Ray& ray, const Navmesh& mesh)
  532. {
  533. const std::vector<Navmesh::Triangle*>& triangles = *mesh.getTriangles();
  534. bool intersection = false;
  535. float t0 = std::numeric_limits<float>::infinity();
  536. float t1 = -std::numeric_limits<float>::infinity();
  537. std::size_t index0 = triangles.size();
  538. std::size_t index1 = triangles.size();
  539. for (std::size_t i = 0; i < triangles.size(); ++i)
  540. {
  541. const Navmesh::Triangle* triangle = triangles[i];
  542. const Vector3& a = triangle->edge->vertex->position;
  543. const Vector3& b = triangle->edge->next->vertex->position;
  544. const Vector3& c = triangle->edge->previous->vertex->position;
  545. auto result = ray.intersects(a, b, c);
  546. if (std::get<0>(result))
  547. {
  548. intersection = true;
  549. float t = std::get<1>(result);
  550. float cosTheta = glm::dot(ray.direction, triangle->normal);
  551. if (cosTheta <= 0.0f)
  552. {
  553. // Front-facing
  554. t0 = std::min(t0, t);
  555. index0 = i;
  556. }
  557. else
  558. {
  559. // Back-facing
  560. t1 = std::max(t1, t);
  561. index1 = i;
  562. }
  563. }
  564. }
  565. return std::make_tuple(intersection, t0, t1, index0, index1);
  566. }
  567. Octree<Navmesh::Triangle*>* Navmesh::createOctree(std::size_t maxDepth)
  568. {
  569. Octree<Navmesh::Triangle*>* result = new Octree<Navmesh::Triangle*>(bounds, maxDepth);
  570. for (Navmesh::Triangle* triangle: triangles)
  571. {
  572. Vector3 min;
  573. min.x = std::min(triangle->edge->vertex->position.x, std::min(triangle->edge->next->vertex->position.x, triangle->edge->previous->vertex->position.x));
  574. min.y = std::min(triangle->edge->vertex->position.y, std::min(triangle->edge->next->vertex->position.y, triangle->edge->previous->vertex->position.y));
  575. min.z = std::min(triangle->edge->vertex->position.z, std::min(triangle->edge->next->vertex->position.z, triangle->edge->previous->vertex->position.z));
  576. Vector3 max;
  577. max.x = std::max(triangle->edge->vertex->position.x, std::max(triangle->edge->next->vertex->position.x, triangle->edge->previous->vertex->position.x));
  578. max.y = std::max(triangle->edge->vertex->position.y, std::max(triangle->edge->next->vertex->position.y, triangle->edge->previous->vertex->position.y));
  579. max.z = std::max(triangle->edge->vertex->position.z, std::max(triangle->edge->next->vertex->position.z, triangle->edge->previous->vertex->position.z));
  580. result->insert(AABB(min, max), triangle);
  581. }
  582. return result;
  583. }