mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 07:22:45 +00:00 
			
		
		
		
	 e8cd89a538
			
		
	
	
		e8cd89a538
		
	
	
	
	
		
			
			This code has also been optimised to be much more memory friendly by removing a _lot_ of extraneous copies. The result is that, when profiled, it's around 8x faster than the previous implementation. Co-Authored-By: Ali Mohammad Pur <ali.mpfard@gmail.com>
		
			
				
	
	
		
			110 lines
		
	
	
	
		
			4.3 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			110 lines
		
	
	
	
		
			4.3 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
|  * Copyright (c) 2021, Jesse Buhagiar <jooster669@gmail.com>
 | |
|  *
 | |
|  * SPDX-License-Identifier: BSD-2-Clause
 | |
|  */
 | |
| 
 | |
| #include <AK/Format.h>
 | |
| #include <AK/ScopeGuard.h>
 | |
| #include <LibGL/Clipper.h>
 | |
| 
 | |
| bool GL::Clipper::point_within_clip_plane(const FloatVector4& vertex, ClipPlane plane)
 | |
| {
 | |
|     switch (plane) {
 | |
|     case ClipPlane::LEFT:
 | |
|         return vertex.x() > -vertex.w();
 | |
|     case ClipPlane::RIGHT:
 | |
|         return vertex.x() < vertex.w();
 | |
|     case ClipPlane::TOP:
 | |
|         return vertex.y() < vertex.w();
 | |
|     case ClipPlane::BOTTOM:
 | |
|         return vertex.y() > -vertex.w();
 | |
|     case ClipPlane::NEAR:
 | |
|         return vertex.z() > -vertex.w();
 | |
|     case ClipPlane::FAR:
 | |
|         return vertex.z() < vertex.w();
 | |
|     }
 | |
| 
 | |
|     return false;
 | |
| }
 | |
| 
 | |
| // TODO: This needs to interpolate color/UV data as well!
 | |
| FloatVector4 GL::Clipper::clip_intersection_point(const FloatVector4& vec, const FloatVector4& prev_vec, ClipPlane plane_index)
 | |
| {
 | |
| 
 | |
|     //
 | |
|     // This is an implementation of the Cyrus-Beck algorithm to clip lines against a plane
 | |
|     // using the triangle's line segment in parametric form.
 | |
|     // In this case, we find that n . [P(t) - plane] == 0 if the point lies on
 | |
|     // the boundary, which in this case is the clip plane. We then substitute
 | |
|     // P(t)= P1 + (P2-P1)*t (where P2 is a point inside the clipping boundary, and P1 is,
 | |
|     // in this case, the point that lies outside the boundary due to our implementation of Sutherland-Hogdman)
 | |
|     // into P(t) to arrive at the equation:
 | |
|     //
 | |
|     //  n · [P1 + ((P2 - P1) * t) - plane] = 0.
 | |
|     //  Substitude seg_length = P2 - P1 (length of line segment) and dist = P1 - plane (distance from P1 to plane)
 | |
|     //
 | |
|     //  By rearranging, we now end up with
 | |
|     //
 | |
|     //  n·[P1 + (seg_length * t) - plane] = 0
 | |
|     //  n·(dist) + n·(seg_length * t) = 0
 | |
|     //  n·(seg_length*t) = -n·(dist)
 | |
|     //  Therefore
 | |
|     //  t = (-n·(dist))/(n·seg_length)
 | |
|     //
 | |
|     // NOTE: n is the normal vector to the plane we are clipping against.
 | |
|     //
 | |
|     // Proof of algorithm found here
 | |
|     // http://studymaterial.unipune.ac.in:8080/jspui/bitstream/123456789/4843/1/Cyrus_Beck_Algo.pdf
 | |
|     // And here (slightly more intuitive with a better diagram)
 | |
|     // https://www.csee.umbc.edu/~rheingan/435/pages/res/gen-5.Clipping-single-page-0.html
 | |
| 
 | |
|     FloatVector4 seg_length = vec - prev_vec;                // P2 - P1
 | |
|     FloatVector4 dist = prev_vec - clip_planes[plane_index]; // Distance from "out of bounds" vector to plane
 | |
| 
 | |
|     float plane_normal_line_segment_dot_product = clip_plane_normals[plane_index].dot(seg_length);
 | |
|     float neg_plane_normal_dist_dot_procut = -clip_plane_normals[plane_index].dot(dist);
 | |
|     float t = (plane_normal_line_segment_dot_product / neg_plane_normal_dist_dot_procut);
 | |
| 
 | |
|     // P(t) = P1 + (t * (P2 - P1))
 | |
|     FloatVector4 lerped_vec = prev_vec + (seg_length * t);
 | |
| 
 | |
|     return lerped_vec;
 | |
| }
 | |
| 
 | |
| // FIXME: Getting too close to zNear causes VERY serious artifacting. Should we cull the whole triangle??
 | |
| void GL::Clipper::clip_triangle_against_frustum(Vector<FloatVector4>& input_verts)
 | |
| {
 | |
|     Vector<FloatVector4, 6> clipped_polygon;
 | |
|     Vector<FloatVector4, 6> input = input_verts;
 | |
|     Vector<FloatVector4, 6>* current = &clipped_polygon;
 | |
|     Vector<FloatVector4, 6>* output_list = &input;
 | |
|     ScopeGuard guard { [&] { input_verts = *output_list; } };
 | |
| 
 | |
|     for (u8 plane = 0; plane < NUMBER_OF_CLIPPING_PLANES; plane++) {
 | |
|         swap(current, output_list);
 | |
|         clipped_polygon.clear();
 | |
| 
 | |
|         if (current->size() == 0) {
 | |
|             return;
 | |
|         }
 | |
| 
 | |
|         // Save me, C++23
 | |
|         for (size_t i = 0; i < current->size(); i++) {
 | |
|             const auto& curr_vec = current->at(i);
 | |
|             const auto& prev_vec = current->at((i - 1) % current->size());
 | |
| 
 | |
|             if (point_within_clip_plane(curr_vec, static_cast<ClipPlane>(plane))) {
 | |
|                 if (!point_within_clip_plane(prev_vec, static_cast<ClipPlane>(plane))) {
 | |
|                     FloatVector4 intersect = clip_intersection_point(prev_vec, curr_vec, static_cast<ClipPlane>(plane));
 | |
|                     clipped_polygon.append(intersect);
 | |
|                 }
 | |
| 
 | |
|                 clipped_polygon.append(curr_vec);
 | |
|             } else if (point_within_clip_plane(prev_vec, static_cast<ClipPlane>(plane))) {
 | |
|                 FloatVector4 intersect = clip_intersection_point(prev_vec, curr_vec, static_cast<ClipPlane>(plane));
 | |
|                 clipped_polygon.append(intersect);
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| }
 |