From 521ab3b5ba9dc60c991250ae456a7a688a8cec30 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Sat, 7 Dec 2019 11:51:47 -0800 Subject: [PATCH] Add a 3D vector type --- demo/common/src/renderer.rs | 4 +- geometry/src/transform3d.rs | 3 +- geometry/src/vector.rs | 108 +++++++++++++++++++++++++++++++++--- renderer/src/options.rs | 25 ++++----- 4 files changed, 116 insertions(+), 24 deletions(-) diff --git a/demo/common/src/renderer.rs b/demo/common/src/renderer.rs index f66be371..88e0a73c 100644 --- a/demo/common/src/renderer.rs +++ b/demo/common/src/renderer.rs @@ -163,7 +163,7 @@ impl DemoApp where W: Window { let scene_framebuffer = self.scene_framebuffer.as_ref().unwrap(); let scene_texture = self.renderer.device.framebuffer_texture(scene_framebuffer); - let mut quad_scale = self.scene_metadata.view_box.size().to_3d(); + let mut quad_scale = self.scene_metadata.view_box.size().to_4d(); quad_scale.set_z(1.0); let quad_scale_transform = Transform4F::from_scale(quad_scale); @@ -210,7 +210,7 @@ impl DemoApp where W: Window { let ground_scale = self.scene_metadata.view_box.max_x() * 2.0; - let mut offset = self.scene_metadata.view_box.lower_right().to_3d(); + let mut offset = self.scene_metadata.view_box.lower_right().to_4d(); offset.set_z(ground_scale); offset = offset * Vector4F::new(-0.5, 1.0, -0.5, 1.0); let base_transform = perspective.transform * Transform4F::from_translation(offset); diff --git a/geometry/src/transform3d.rs b/geometry/src/transform3d.rs index 9f29b659..e8c41ad5 100644 --- a/geometry/src/transform3d.rs +++ b/geometry/src/transform3d.rs @@ -387,8 +387,7 @@ impl Mul for Perspective { type Output = Vector2F; #[inline] fn mul(self, vector: Vector2F) -> Vector2F { - let point = (self.transform * vector.to_3d()).perspective_divide().to_2d() * - Vector2F::new(1.0, -1.0); + let point = (self.transform * vector.to_4d()).to_2d() * Vector2F::new(1.0, -1.0); (point + Vector2F::splat(1.0)) * self.window_size.to_f32().scale(0.5) } } diff --git a/geometry/src/vector.rs b/geometry/src/vector.rs index 768b6e58..03e190fe 100644 --- a/geometry/src/vector.rs +++ b/geometry/src/vector.rs @@ -29,7 +29,12 @@ impl Vector2F { } #[inline] - pub fn to_3d(self) -> Vector4F { + pub fn to_3d(self) -> Vector3F { + Vector3F(self.0.to_f32x4().concat_xy_zw(F32x4::new(0.0, 0.0, 0.0, 0.0))) + } + + #[inline] + pub fn to_4d(self) -> Vector4F { Vector4F(self.0.to_f32x4().concat_xy_zw(F32x4::new(0.0, 0.0, 0.0, 1.0))) } @@ -260,6 +265,84 @@ impl PartialEq for Vector2I { } } +/// 3D points. +/// +/// The w value in the SIMD vector is always 0.0. +#[derive(Clone, Copy, Debug, Default, PartialEq)] +pub struct Vector3F(pub F32x4); + +impl Vector3F { + #[inline] + pub fn new(x: f32, y: f32, z: f32) -> Vector3F { + Vector3F(F32x4::new(x, y, z, 0.0)) + } + + #[inline] + pub fn splat(x: f32) -> Vector3F { + let mut vector = F32x4::splat(x); + vector.set_w(0.0); + Vector3F(vector) + } + + /// Truncates this vector to 2D. + #[inline] + pub fn to_2d(self) -> Vector2F { + Vector2F(self.0.xy()) + } + + /// Converts this vector to an equivalent 3D homogeneous one with a w component of 1.0. + #[inline] + pub fn to_4d(self) -> Vector4F { + let mut vector = self.0; + vector.set_w(1.0); + Vector4F(vector) + } + + #[inline] + pub fn cross(self, other: Vector3F) -> Vector3F { + Vector3F(self.0.yzxw() * other.0.zxyw() - self.0.zxyw() * other.0.yzxw()) + } + + #[inline] + pub fn square_length(self) -> f32 { + let squared = self.0 * self.0; + squared[0] + squared[1] + squared[2] + } + + #[inline] + pub fn length(self) -> f32 { + f32::sqrt(self.square_length()) + } + + #[inline] + pub fn normalize(self) -> Vector3F { + Vector3F(self.0 * F32x4::splat(1.0 / self.length())) + } +} + +impl Add for Vector3F { + type Output = Vector3F; + #[inline] + fn add(self, other: Vector3F) -> Vector3F { + Vector3F(self.0 + other.0) + } +} + +impl AddAssign for Vector3F { + #[inline] + fn add_assign(&mut self, other: Vector3F) { + self.0 += other.0 + } +} + +impl Sub for Vector3F { + type Output = Vector3F; + #[inline] + fn sub(self, other: Vector3F) -> Vector3F { + Vector3F(self.0 - other.0) + } +} + /// 3D homogeneous points. #[derive(Clone, Copy, Debug, PartialEq)] pub struct Vector4F(pub F32x4); @@ -277,7 +360,15 @@ impl Vector4F { #[inline] pub fn to_2d(self) -> Vector2F { - Vector2F(self.0.xy()) + self.to_3d().to_2d() + } + + /// Performs perspective division to convert this vector to 3D. + #[inline] + pub fn to_3d(self) -> Vector3F { + let mut vector = self.0 * F32x4::splat(1.0 / self.w()); + vector.set_w(0.0); + Vector3F(vector) } #[inline] @@ -327,11 +418,6 @@ impl Vector4F { self.0[3] = w } - #[inline] - pub fn perspective_divide(self) -> Vector4F { - Vector4F(self.0 * F32x4::splat(1.0 / self.w())) - } - #[inline] pub fn approx_eq(self, other: Vector4F, epsilon: f32) -> bool { self.0.approx_eq(other.0, epsilon) @@ -385,6 +471,14 @@ impl Neg for Vector4F { } } +impl Sub for Vector4F { + type Output = Vector4F; + #[inline] + fn sub(self, other: Vector4F) -> Vector4F { + Vector4F(self.0 - other.0) + } +} + impl Default for Vector4F { #[inline] fn default() -> Vector4F { diff --git a/renderer/src/options.rs b/renderer/src/options.rs index 170d2cb7..86ba541e 100644 --- a/renderer/src/options.rs +++ b/renderer/src/options.rs @@ -75,10 +75,10 @@ impl RenderTransform { }; let mut points = vec![ - bounds.origin().to_3d(), - bounds.upper_right().to_3d(), - bounds.lower_right().to_3d(), - bounds.lower_left().to_3d(), + bounds.origin().to_4d(), + bounds.upper_right().to_4d(), + bounds.lower_right().to_4d(), + bounds.lower_left().to_4d(), ]; debug!("-----"); debug!("bounds={:?} ORIGINAL quad={:?}", bounds, points); @@ -89,24 +89,23 @@ impl RenderTransform { // Compute depth. let quad = [ - points[0].perspective_divide(), - points[1].perspective_divide(), - points[2].perspective_divide(), - points[3].perspective_divide(), + points[0].to_3d().to_4d(), + points[1].to_3d().to_4d(), + points[2].to_3d().to_4d(), + points[3].to_3d().to_4d(), ]; debug!("... PERSPECTIVE-DIVIDED points = {:?}", quad); points = PolygonClipper3D::new(points).clip(); debug!("... CLIPPED quad={:?}", points); for point in &mut points { - *point = point.perspective_divide() + *point = point.to_3d().to_4d() } let inverse_transform = perspective.transform.inverse(); - let clip_polygon = points - .into_iter() - .map(|point| (inverse_transform * point).perspective_divide().to_2d()) - .collect(); + let clip_polygon = points.into_iter() + .map(|point| (inverse_transform * point).to_2d()) + .collect(); return PreparedRenderTransform::Perspective { perspective, clip_polygon,