From 89244ba6cfeddc5d1fa8db2f4f996a29c6d717b4 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Thu, 31 Jan 2019 15:29:13 -0800 Subject: [PATCH] Do clipping in 3D homogeneous space --- demo3/src/main.rs | 12 ++++--- geometry/src/clip.rs | 66 ++++++++++++++----------------------- geometry/src/point.rs | 10 ++++++ geometry/src/transform3d.rs | 3 +- renderer/src/scene.rs | 19 ++++++----- 5 files changed, 54 insertions(+), 56 deletions(-) diff --git a/demo3/src/main.rs b/demo3/src/main.rs index 57d92707..4307fc93 100644 --- a/demo3/src/main.rs +++ b/demo3/src/main.rs @@ -37,7 +37,7 @@ const MAIN_FRAMEBUFFER_WIDTH: u32 = 1067; const MAIN_FRAMEBUFFER_HEIGHT: u32 = 800; const MOUSELOOK_ROTATION_SPEED: f32 = 0.01; -const CAMERA_VELOCITY: f32 = 0.03; +const CAMERA_VELOCITY: f32 = 60.0; fn main() { let options = Options::get(); @@ -65,7 +65,7 @@ fn main() { let (drawable_width, drawable_height) = window.drawable_size(); let mut renderer = Renderer::new(&Size2D::new(drawable_width, drawable_height)); - let mut camera_position = Point4DF32::new(1.1, 1.0, 3.0, 1.0); + let mut camera_position = Point4DF32::new(500.0, 500.0, 3000.0, 1.0); let mut camera_velocity = Point4DF32::new(0.0, 0.0, 0.0, 1.0); let (mut camera_yaw, mut camera_pitch) = (0.0, 0.0); @@ -87,7 +87,11 @@ fn main() { camera_position = camera_position + rotation.transform_point(camera_velocity); let mut transform = - Transform3DF32::from_perspective(FRAC_PI_4, 4.0 / 3.0, 0.0001, 100.0); + Transform3DF32::from_perspective(FRAC_PI_4, 4.0 / 3.0, 0.025, 100.0); + + transform = transform.post_mul(&Transform3DF32::from_scale(1.0 / 800.0, + 1.0 / 800.0, + 1.0 / 800.0)); transform = transform.post_mul(&Transform3DF32::from_rotation(camera_yaw, camera_pitch, 0.0)); @@ -95,8 +99,6 @@ fn main() { transform.post_mul(&Transform3DF32::from_translation(-camera_position.x(), -camera_position.y(), -camera_position.z())); - transform = - transform.post_mul(&Transform3DF32::from_scale(1.0 / 800.0, 1.0 / 800.0, 1.0)); let perspective = Perspective::new(&transform, &window_size); diff --git a/geometry/src/clip.rs b/geometry/src/clip.rs index 29a6e053..24ff0717 100644 --- a/geometry/src/clip.rs +++ b/geometry/src/clip.rs @@ -10,7 +10,7 @@ use crate::line_segment::LineSegmentF32; use crate::outline::{Contour, PointFlags}; -use crate::point::{Point2DF32, Point3DF32}; +use crate::point::{Point2DF32, Point4DF32}; use crate::segment::{CubicSegment, Segment}; use crate::util::lerp; use arrayvec::ArrayVec; @@ -268,18 +268,6 @@ impl ContourClipper { prev = next; } - /* - let top = Point2DF32::new(lerp(self.clip_rect.origin.x, self.clip_rect.max_x(), 0.5), - self.clip_rect.origin.y); - self.clip_against(Edge(LineSegmentF32::new(&Point2DF32::from_euclid(self.clip_rect - .bottom_left()), - &top))); - self.clip_against(Edge(LineSegmentF32::new(&top, - &Point2DF32::from_euclid(self.clip_rect - .bottom_right())))); - self.clip_against(Edge::bottom(&self.clip_rect)); - */ - self.contour } @@ -335,27 +323,12 @@ impl ContourClipper { if let Some(last_position) = contour.last_position() { if last_position != segment.baseline.from() { // Add a line to join up segments. - //check_point(&segment.baseline.from(), edge); contour.push_point(segment.baseline.from(), PointFlags::empty()); } } - //check_point(&segment.baseline.to(), edge); contour.push_segment(*segment); } - - /* - fn check_point(point: &Point2DF32, edge: Edge) { - match edge { - Edge::Left(x) if point.x() + 0.1 >= x => return, - Edge::Top(y) if point.y() + 0.1 >= y => return, - Edge::Right(x) if point.x() - 0.1 <= x => return, - Edge::Bottom(y) if point.y() - 0.1 <= y => return, - _ => {} - } - panic!("point {:?} outside edge {:?}", point, edge); - } - */ } } @@ -368,24 +341,31 @@ enum EdgeRelativeLocation { // 3D quad clipping pub struct PolygonClipper3D { - subject: Vec, + subject: Vec, } impl PolygonClipper3D { #[inline] - pub fn new(subject: Vec) -> PolygonClipper3D { + pub fn new(subject: Vec) -> PolygonClipper3D { PolygonClipper3D { subject } } - pub fn clip(mut self) -> Vec { + pub fn clip(mut self) -> Vec { // TODO(pcwalton): Fast path for completely contained polygon? - self.clip_against(Edge3D::Left); - self.clip_against(Edge3D::Right); + //println!("before clipping against bottom: {:?}", self.subject); self.clip_against(Edge3D::Bottom); + //println!("before clipping against top: {:?}", self.subject); self.clip_against(Edge3D::Top); - self.clip_against(Edge3D::Near); + //println!("before clipping against left: {:?}", self.subject); + self.clip_against(Edge3D::Left); + //println!("before clipping against right: {:?}", self.subject); + self.clip_against(Edge3D::Right); + //println!("before clipping against far: {:?}", self.subject); self.clip_against(Edge3D::Far); + //println!("before clipping against near: {:?}", self.subject); + self.clip_against(Edge3D::Near); + //println!("after clipping: {:?}", self.subject); self.subject } @@ -422,24 +402,28 @@ enum Edge3D { impl Edge3D { #[inline] - fn point_is_inside(self, point: Point3DF32) -> bool { + fn point_is_inside(self, point: Point4DF32) -> bool { + let w = point.w(); match self { - Edge3D::Left => point.x() >= -1.0, Edge3D::Right => point.x() <= 1.0, - Edge3D::Bottom => point.y() >= -1.0, Edge3D::Top => point.y() <= 1.0, - Edge3D::Near => point.z() >= -1.0, Edge3D::Far => point.z() <= 1.0, + Edge3D::Left => point.x() >= -w, Edge3D::Right => point.x() <= w, + Edge3D::Bottom => point.y() >= -w, Edge3D::Top => point.y() <= w, + Edge3D::Near => point.z() >= -w, Edge3D::Far => point.z() <= w, } } - fn line_intersection(self, prev: Point3DF32, next: Point3DF32) -> Point3DF32 { + // Blinn & Newell, "Clipping using homogeneous coordinates", SIGGRAPH 1978. + fn line_intersection(self, prev: Point4DF32, next: Point4DF32) -> Point4DF32 { let (x0, x1) = match self { Edge3D::Left | Edge3D::Right => (prev.x(), next.x()), Edge3D::Bottom | Edge3D::Top => (prev.y(), next.y()), Edge3D::Near | Edge3D::Far => (prev.z(), next.z()), }; - let x = match self { + let (w0, w1) = (prev.w(), next.w()); + let sign = match self { Edge3D::Left | Edge3D::Bottom | Edge3D::Near => -1.0, Edge3D::Right | Edge3D::Top | Edge3D::Far => 1.0, }; - prev.lerp(next, (x - x0) / (x1 - x0)) + let alpha = ((x0 - sign * w0) as f64) / ((sign * (w1 - w0) - (x1 - x0)) as f64); + prev.lerp(next, alpha as f32) } } diff --git a/geometry/src/point.rs b/geometry/src/point.rs index 8c07003f..6770d903 100644 --- a/geometry/src/point.rs +++ b/geometry/src/point.rs @@ -179,6 +179,11 @@ impl Point4DF32 { Point4DF32(F32x4::new(x, y, z, w)) } + #[inline] + pub fn from_euclid_2d(point: &Point2D) -> Point4DF32 { + Point4DF32::new(point.x, point.y, 0.0, 1.0) + } + #[inline] pub fn splat(value: f32) -> Point4DF32 { Point4DF32(F32x4::splat(value)) @@ -247,6 +252,11 @@ impl Point4DF32 { pub fn is_zero(self) -> bool { self.x() == 0.0 && self.y() == 0.0 && self.z() == 0.0 } + + #[inline] + pub fn lerp(self, other: Point4DF32, t: f32) -> Point4DF32 { + Point4DF32(self.0 + (other.0 - self.0) * F32x4::splat(t)) + } } impl Add for Point4DF32 { diff --git a/geometry/src/transform3d.rs b/geometry/src/transform3d.rs index 3861de2a..f73d2433 100644 --- a/geometry/src/transform3d.rs +++ b/geometry/src/transform3d.rs @@ -181,7 +181,8 @@ impl Transform3DF32 { #[inline] pub fn transform_point_3d(&self, point: Point3DF32) -> Point3DF32 { - self.transform_point(point.to_4d()).perspective_divide() + let point4d = self.transform_point(point.to_4d()); + point4d.perspective_divide() } #[inline] diff --git a/renderer/src/scene.rs b/renderer/src/scene.rs index 1dc19503..4970ada3 100644 --- a/renderer/src/scene.rs +++ b/renderer/src/scene.rs @@ -18,7 +18,7 @@ use euclid::Rect; use hashbrown::HashMap; use pathfinder_geometry::clip::PolygonClipper3D; use pathfinder_geometry::outline::Outline; -use pathfinder_geometry::point::{Point2DF32, Point3DF32}; +use pathfinder_geometry::point::{Point2DF32, Point3DF32, Point4DF32}; use pathfinder_geometry::transform3d::Perspective; use pathfinder_geometry::transform::Transform2DF32; use rayon::iter::{IndexedParallelIterator, IntoParallelRefIterator}; @@ -170,19 +170,20 @@ impl Scene { fn clip_bounding_quad_with_perspective(&self, perspective: &Perspective) -> Vec { let mut points = vec![ - Point3DF32::from_euclid_2d(&self.bounds.origin), - Point3DF32::from_euclid_2d(&self.bounds.top_right()), - Point3DF32::from_euclid_2d(&self.bounds.bottom_right()), - Point3DF32::from_euclid_2d(&self.bounds.bottom_left()), + Point4DF32::from_euclid_2d(&self.bounds.origin), + Point4DF32::from_euclid_2d(&self.bounds.top_right()), + Point4DF32::from_euclid_2d(&self.bounds.bottom_right()), + Point4DF32::from_euclid_2d(&self.bounds.bottom_left()), ]; //println!("-----"); //println!("bounds={:?} ORIGINAL quad={:?}", self.bounds, points); for point in &mut points { - *point = perspective.transform.transform_point_3d(*point); + *point = perspective.transform.transform_point(*point); } - //println!("PERSPECTIVE quad={:?}", points); - //points - PolygonClipper3D::new(points).clip() + //println!("... PERSPECTIVE quad={:?}", points); + points = PolygonClipper3D::new(points).clip(); + //println!("... CLIPPED quad={:?}", points); + points.into_iter().map(|point| point.perspective_divide()).collect() } }