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

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