Uses Graham scan on projected coordinates.
473{
474 if (vertices.size() < 3) {
475 return vertices;
476 }
477
478 glm::vec3 n = glm::normalize(projection_normal);
479 glm::vec3 u;
480
481 if (std::abs(n.z) < 0.9F) {
482 u = glm::normalize(glm::cross(n, glm::vec3(0, 0, 1)));
483 } else {
484 u = glm::normalize(glm::cross(n, glm::vec3(1, 0, 0)));
485 }
486
487 glm::vec3 v = glm::cross(n, u);
488
489 struct Point2D {
490 glm::vec2 pos;
491 size_t index;
492 };
493
494 std::vector<Point2D>
points;
495 points.reserve(vertices.size());
496
497 for (size_t i = 0; i < vertices.size(); ++i) {
498 glm::vec3 offset = vertices[i];
499 float x = glm::dot(offset, u);
500 float y = glm::dot(offset, v);
501 points.push_back({ .pos = glm::vec2(x, y), .index = i });
502 }
503
504 auto pivot_it = std::ranges::min_element(
points,
505 [](
const Point2D&
a,
const Point2D&
b) {
506 if (std::abs(
a.pos.y -
b.pos.y) < 1e-6F) {
507 return a.pos.x < b.pos.x;
508 }
509 return a.pos.y <
b.pos.y;
510 });
511
512 std::swap(
points[0], *pivot_it);
513 Point2D pivot =
points[0];
514
516 [&pivot](
const Point2D&
a,
const Point2D&
b) {
517 glm::vec2 va = a.pos - pivot.pos;
518 glm::vec2 vb = b.pos - pivot.pos;
519
520 float cross = va.x * vb.y - va.y * vb.x;
521 if (std::abs(cross) < 1e-6F) {
522 return glm::length(va) < glm::length(vb);
523 }
524 return cross > 0.0F;
525 });
526
527
528 std::vector<size_t> hull;
529 hull.push_back(
points[0].index);
530 hull.push_back(
points[1].index);
531
532 auto ccw = [](
const glm::vec2&
a,
const glm::vec2&
b,
const glm::vec2& c) {
533 return (
b.x -
a.x) * (c.y -
a.y) - (
b.y -
a.y) * (c.x -
a.x) > 0.0F;
534 };
535
536 for (
size_t i = 2; i <
points.size(); ++i) {
537 while (hull.size() >= 2) {
538 size_t top = hull.back();
539 size_t second = hull[hull.size() - 2];
540
541 glm::vec2 p1 =
points[second].pos;
542 glm::vec2 p2 =
points[top].pos;
543 glm::vec2 p3 =
points[i].pos;
544
545 if (ccw(p1, p2, p3)) {
546 break;
547 }
548 hull.pop_back();
549 }
550 hull.push_back(
points[i].index);
551 }
552
553 std::vector<glm::vec3> result;
554 result.reserve(hull.size() + 1);
555
556 for (size_t idx : hull) {
557 result.push_back(vertices[idx]);
558 }
559
560 result.push_back(vertices[hull[0]]);
561
562 return result;
563}
std::vector< glm::vec2 > * points