Use operator overloading for 3D matrix multiplication

This commit is contained in:
Patrick Walton 2019-07-11 13:59:39 -07:00
parent abf97c9adb
commit eb0a61679d
5 changed files with 66 additions and 64 deletions

View File

@ -153,15 +153,13 @@ impl CameraTransform3D {
pub fn to_transform(&self) -> Transform3DF { pub fn to_transform(&self) -> Transform3DF {
let mut transform = Transform3DF::from_rotation(self.yaw, self.pitch, 0.0); let mut transform = Transform3DF::from_rotation(self.yaw, self.pitch, 0.0);
transform = transform.post_mul(&Transform3DF::from_uniform_scale(2.0 * self.scale)); transform *= Transform3DF::from_uniform_scale(2.0 * self.scale);
transform = transform.post_mul(&Transform3DF::from_translation( transform *= Transform3DF::from_translation(-self.position.x(),
-self.position.x(), -self.position.y(),
-self.position.y(), -self.position.z());
-self.position.z(),
));
// Flip Y. // Flip Y.
transform = transform.post_mul(&Transform3DF::from_scale(1.0, -1.0, 1.0)); transform *= Transform3DF::from_scale(1.0, -1.0, 1.0);
transform transform
} }

View File

@ -256,10 +256,9 @@ impl<W> DemoApp<W> where W: Window {
if modelview_transform.offset(*velocity) { if modelview_transform.offset(*velocity) {
self.dirty = true; self.dirty = true;
} }
let perspective = scene_transform let perspective = scene_transform.perspective *
.perspective scene_transform.modelview_to_eye *
.post_mul(&scene_transform.modelview_to_eye) modelview_transform.to_transform();
.post_mul(&modelview_transform.to_transform());
Some(RenderTransform::Perspective(perspective)) Some(RenderTransform::Perspective(perspective))
} }
Camera::TwoD(transform) => Some(RenderTransform::Transform2D(transform)), Camera::TwoD(transform) => Some(RenderTransform::Transform2D(transform)),
@ -356,13 +355,20 @@ impl<W> DemoApp<W> where W: Window {
*scene_transform = eye_transforms[0]; *scene_transform = eye_transforms[0];
for (index, eye_transform) in eye_transforms.iter().enumerate().skip(1) { for (index, eye_transform) in eye_transforms.iter().enumerate().skip(1) {
let weight = 1.0 / (index + 1) as f32; let weight = 1.0 / (index + 1) as f32;
scene_transform.perspective.transform = scene_transform.perspective.transform.lerp(weight, &eye_transform.perspective.transform); scene_transform.perspective.transform =
scene_transform.modelview_to_eye = scene_transform.modelview_to_eye.lerp(weight, &eye_transform.modelview_to_eye); scene_transform.perspective
.transform
.lerp(weight, &eye_transform.perspective.transform);
scene_transform.modelview_to_eye =
scene_transform.modelview_to_eye
.lerp(weight, &eye_transform.modelview_to_eye);
} }
// TODO: calculate the eye offset from the eye transforms? // TODO: calculate the eye offset from the eye transforms?
let z_offset = -DEFAULT_EYE_OFFSET * scene_transform.perspective.transform.c0.x(); let z_offset = -DEFAULT_EYE_OFFSET *
scene_transform.modelview_to_eye = scene_transform.modelview_to_eye scene_transform.perspective.transform.c0.x();
.pre_mul(&Transform3DF::from_translation(0.0, 0.0, z_offset)); scene_transform.modelview_to_eye =
Transform3DF::from_translation(0.0, 0.0, z_offset) *
scene_transform.modelview_to_eye;
} }
} }
Event::KeyDown(Keycode::Alphanumeric(b'w')) => { Event::KeyDown(Keycode::Alphanumeric(b'w')) => {

View File

@ -169,18 +169,16 @@ impl<W> DemoApp<W> where W: Window {
1.0, 1.0,
); );
let scene_transform_matrix = scene_transform let scene_transform_matrix = scene_transform.perspective *
.perspective scene_transform.modelview_to_eye *
.post_mul(&scene_transform.modelview_to_eye) modelview_transform.to_transform() *
.post_mul(&modelview_transform.to_transform()) quad_scale_transform;
.post_mul(&quad_scale_transform);
let eye_transform = &eye_transforms[render_scene_index as usize]; let eye_transform = &eye_transforms[render_scene_index as usize];
let eye_transform_matrix = eye_transform let eye_transform_matrix = eye_transform.perspective *
.perspective eye_transform.modelview_to_eye *
.post_mul(&eye_transform.modelview_to_eye) modelview_transform.to_transform() *
.post_mul(&modelview_transform.to_transform()) quad_scale_transform;
.post_mul(&quad_scale_transform);
debug!( debug!(
"eye transform({}).modelview_to_eye={:?}", "eye transform({}).modelview_to_eye={:?}",
@ -214,17 +212,13 @@ impl<W> DemoApp<W> where W: Window {
let ground_scale = self.scene_metadata.view_box.max_x() * 2.0; let ground_scale = self.scene_metadata.view_box.max_x() * 2.0;
let mut base_transform = perspective.transform; let base_transform = perspective.transform *
base_transform = base_transform.post_mul(&Transform3DF::from_translation( Transform3DF::from_translation(-0.5 * self.scene_metadata.view_box.max_x(),
-0.5 * self.scene_metadata.view_box.max_x(), self.scene_metadata.view_box.max_y(),
self.scene_metadata.view_box.max_y(), -0.5 * ground_scale);
-0.5 * ground_scale,
));
// Fill ground. // Fill ground.
let mut transform = base_transform; let transform = base_transform * Transform3DF::from_scale(ground_scale, 1.0, ground_scale);
transform =
transform.post_mul(&Transform3DF::from_scale(ground_scale, 1.0, ground_scale));
// Don't clear the first scene after drawing it. // Don't clear the first scene after drawing it.
let clear_color = if render_scene_index == 0 { let clear_color = if render_scene_index == 0 {

View File

@ -14,7 +14,7 @@ use crate::vector::{Vector2F, Vector2I, Vector4F};
use crate::rect::RectF; use crate::rect::RectF;
use crate::transform2d::Matrix2x2F; use crate::transform2d::Matrix2x2F;
use pathfinder_simd::default::F32x4; use pathfinder_simd::default::F32x4;
use std::ops::{Add, Neg}; use std::ops::{Add, Mul, MulAssign, Neg};
/// An transform, optimized with SIMD. /// An transform, optimized with SIMD.
/// ///
@ -218,31 +218,6 @@ impl Transform3DF {
} }
} }
// FIXME(pcwalton): Is this right, due to transposition? I think we may have to reverse the
// two.
//
// https://stackoverflow.com/a/18508113
#[inline]
pub fn pre_mul(&self, other: &Transform3DF) -> Transform3DF {
return Transform3DF {
c0: mul_col(self.c0, other),
c1: mul_col(self.c1, other),
c2: mul_col(self.c2, other),
c3: mul_col(self.c3, other),
};
fn mul_col(a_col: F32x4, b: &Transform3DF) -> F32x4 {
let (a0, a1) = (F32x4::splat(a_col[0]), F32x4::splat(a_col[1]));
let (a2, a3) = (F32x4::splat(a_col[2]), F32x4::splat(a_col[3]));
a0 * b.c0 + a1 * b.c1 + a2 * b.c2 + a3 * b.c3
}
}
#[inline]
pub fn post_mul(&self, other: &Transform3DF) -> Transform3DF {
other.pre_mul(self)
}
#[inline] #[inline]
pub fn transform_point(&self, point: Vector4F) -> Vector4F { pub fn transform_point(&self, point: Vector4F) -> Vector4F {
let term0 = self.c0 * F32x4::splat(point.x()); let term0 = self.c0 * F32x4::splat(point.x());
@ -313,6 +288,32 @@ impl Transform3DF {
} }
} }
impl Mul<Transform3DF> for Transform3DF {
type Output = Transform3DF;
// https://stackoverflow.com/a/18508113
#[inline]
fn mul(self, other: Transform3DF) -> Transform3DF {
return Transform3DF {
c0: mul_col(&self, other.c0),
c1: mul_col(&self, other.c1),
c2: mul_col(&self, other.c2),
c3: mul_col(&self, other.c3),
};
#[inline]
fn mul_col(a: &Transform3DF, b_col: F32x4) -> F32x4 {
a.c0 * b_col.xxxx() + a.c1 * b_col.yyyy() + a.c2 * b_col.zzzz() + a.c3 * b_col.wwww()
}
}
}
impl MulAssign<Transform3DF> for Transform3DF {
fn mul_assign(&mut self, other: Transform3DF) {
*self = *self * other
}
}
impl Add<Matrix2x2F> for Matrix2x2F { impl Add<Matrix2x2F> for Matrix2x2F {
type Output = Matrix2x2F; type Output = Matrix2x2F;
#[inline] #[inline]
@ -366,11 +367,14 @@ impl Perspective {
let max_point = upper_left.max(upper_right).max(lower_left).max(lower_right); let max_point = upper_left.max(upper_right).max(lower_left).max(lower_right);
RectF::from_points(min_point, max_point) RectF::from_points(min_point, max_point)
} }
}
impl Mul<Transform3DF> for Perspective {
type Output = Perspective;
#[inline] #[inline]
pub fn post_mul(&self, other: &Transform3DF) -> Perspective { fn mul(self, other: Transform3DF) -> Perspective {
Perspective { Perspective {
transform: self.transform.post_mul(other), transform: self.transform * other,
window_size: self.window_size, window_size: self.window_size,
} }
} }

View File

@ -472,7 +472,7 @@ where
let draw_viewport = self.draw_viewport().size().to_f32(); let draw_viewport = self.draw_viewport().size().to_f32();
let scale = F32x2::new(2.0 / draw_viewport.x(), -2.0 / draw_viewport.y()); let scale = F32x2::new(2.0 / draw_viewport.x(), -2.0 / draw_viewport.y());
let transform = Transform3DF::from_scale(scale.x(), scale.y(), 1.0); let transform = Transform3DF::from_scale(scale.x(), scale.y(), 1.0);
Transform3DF::from_translation(-1.0, 1.0, 0.0).post_mul(&transform) Transform3DF::from_translation(-1.0, 1.0, 0.0) * transform
} }
fn draw_alpha_tiles(&mut self, count: u32) { fn draw_alpha_tiles(&mut self, count: u32) {