Clipper.cpp 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. /*
  2. * Copyright (c) 2021, Jesse Buhagiar <jooster669@gmail.com>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <AK/Format.h>
  7. #include <AK/ScopeGuard.h>
  8. #include <LibGL/Clipper.h>
  9. bool GL::Clipper::point_within_clip_plane(const FloatVector4& vertex, ClipPlane plane)
  10. {
  11. switch (plane) {
  12. case ClipPlane::LEFT:
  13. return vertex.x() > -vertex.w();
  14. case ClipPlane::RIGHT:
  15. return vertex.x() < vertex.w();
  16. case ClipPlane::TOP:
  17. return vertex.y() < vertex.w();
  18. case ClipPlane::BOTTOM:
  19. return vertex.y() > -vertex.w();
  20. case ClipPlane::NEAR:
  21. return vertex.z() > -vertex.w();
  22. case ClipPlane::FAR:
  23. return vertex.z() < vertex.w();
  24. }
  25. return false;
  26. }
  27. // TODO: This needs to interpolate color/UV data as well!
  28. FloatVector4 GL::Clipper::clip_intersection_point(const FloatVector4& vec, const FloatVector4& prev_vec, ClipPlane plane_index)
  29. {
  30. //
  31. // This is an implementation of the Cyrus-Beck algorithm to clip lines against a plane
  32. // using the triangle's line segment in parametric form.
  33. // In this case, we find that n . [P(t) - plane] == 0 if the point lies on
  34. // the boundary, which in this case is the clip plane. We then substitute
  35. // P(t)= P1 + (P2-P1)*t (where P2 is a point inside the clipping boundary, and P1 is,
  36. // in this case, the point that lies outside the boundary due to our implementation of Sutherland-Hogdman)
  37. // into P(t) to arrive at the equation:
  38. //
  39. // n · [P1 + ((P2 - P1) * t) - plane] = 0.
  40. // Substitude seg_length = P2 - P1 (length of line segment) and dist = P1 - plane (distance from P1 to plane)
  41. //
  42. // By rearranging, we now end up with
  43. //
  44. // n·[P1 + (seg_length * t) - plane] = 0
  45. // n·(dist) + n·(seg_length * t) = 0
  46. // n·(seg_length*t) = -n·(dist)
  47. // Therefore
  48. // t = (-n·(dist))/(n·seg_length)
  49. //
  50. // NOTE: n is the normal vector to the plane we are clipping against.
  51. //
  52. // Proof of algorithm found here
  53. // http://studymaterial.unipune.ac.in:8080/jspui/bitstream/123456789/4843/1/Cyrus_Beck_Algo.pdf
  54. // And here (slightly more intuitive with a better diagram)
  55. // https://www.csee.umbc.edu/~rheingan/435/pages/res/gen-5.Clipping-single-page-0.html
  56. FloatVector4 seg_length = vec - prev_vec; // P2 - P1
  57. FloatVector4 dist = prev_vec - clip_planes[plane_index]; // Distance from "out of bounds" vector to plane
  58. float plane_normal_line_segment_dot_product = clip_plane_normals[plane_index].dot(seg_length);
  59. float neg_plane_normal_dist_dot_procut = -clip_plane_normals[plane_index].dot(dist);
  60. float t = (plane_normal_line_segment_dot_product / neg_plane_normal_dist_dot_procut);
  61. // P(t) = P1 + (t * (P2 - P1))
  62. FloatVector4 lerped_vec = prev_vec + (seg_length * t);
  63. return lerped_vec;
  64. }
  65. // FIXME: Getting too close to zNear causes VERY serious artifacting. Should we cull the whole triangle??
  66. void GL::Clipper::clip_triangle_against_frustum(Vector<FloatVector4>& input_verts)
  67. {
  68. Vector<FloatVector4, 6> clipped_polygon;
  69. Vector<FloatVector4, 6> input = input_verts;
  70. Vector<FloatVector4, 6>* current = &clipped_polygon;
  71. Vector<FloatVector4, 6>* output_list = &input;
  72. ScopeGuard guard { [&] { input_verts = *output_list; } };
  73. for (u8 plane = 0; plane < NUMBER_OF_CLIPPING_PLANES; plane++) {
  74. swap(current, output_list);
  75. clipped_polygon.clear();
  76. if (current->size() == 0) {
  77. return;
  78. }
  79. // Save me, C++23
  80. for (size_t i = 0; i < current->size(); i++) {
  81. const auto& curr_vec = current->at(i);
  82. const auto& prev_vec = current->at((i - 1) % current->size());
  83. if (point_within_clip_plane(curr_vec, static_cast<ClipPlane>(plane))) {
  84. if (!point_within_clip_plane(prev_vec, static_cast<ClipPlane>(plane))) {
  85. FloatVector4 intersect = clip_intersection_point(prev_vec, curr_vec, static_cast<ClipPlane>(plane));
  86. clipped_polygon.append(intersect);
  87. }
  88. clipped_polygon.append(curr_vec);
  89. } else if (point_within_clip_plane(prev_vec, static_cast<ClipPlane>(plane))) {
  90. FloatVector4 intersect = clip_intersection_point(prev_vec, curr_vec, static_cast<ClipPlane>(plane));
  91. clipped_polygon.append(intersect);
  92. }
  93. }
  94. }
  95. }