Fix 3D transform math and add some unit tests
This commit is contained in:
parent
05d2b26fa7
commit
a95db7dff9
|
@ -14,6 +14,7 @@ use gl::types::{GLchar, GLfloat, GLint, GLsizei, GLsizeiptr, GLuint, GLvoid};
|
|||
use jemallocator;
|
||||
use pathfinder_geometry::point::Point2DF32;
|
||||
use pathfinder_geometry::transform::Transform2DF32;
|
||||
use pathfinder_geometry::transform3d::Transform3DF32;
|
||||
use pathfinder_renderer::builder::SceneBuilder;
|
||||
use pathfinder_renderer::gpu_data::{Batch, BuiltScene, SolidTileScenePrimitive};
|
||||
use pathfinder_renderer::paint::ObjectShader;
|
||||
|
@ -81,13 +82,17 @@ fn main() {
|
|||
let mut renderer = Renderer::new(&Size2D::new(drawable_width, drawable_height));
|
||||
|
||||
//let mut scale = 1.0;
|
||||
let mut theta = 0.0;
|
||||
//let mut theta = 0.0;
|
||||
|
||||
let mut transform = Transform3DF32::from_perspective(f32::PI / 4.0, 4.0 / 3.0, 0.01, 1000.0);
|
||||
transform = transform.pre_mul(Transform3DF32::from_translation(0.0, 0.0, -100.0));
|
||||
|
||||
while !exit {
|
||||
let mut scene = base_scene.clone();
|
||||
scene.transform(&Transform2DF32::from_rotation(theta));
|
||||
scene.transform_3d(&transform);
|
||||
//scene.transform(&Transform2DF32::from_rotation(theta));
|
||||
//scene.transform(&Transform2DF32::from_scale(&Point2DF32::splat(scale)));
|
||||
theta += 0.001;
|
||||
//theta += 0.001;
|
||||
//scale -= 0.0003;
|
||||
//scale += 0.0001;
|
||||
|
||||
|
|
|
@ -101,7 +101,7 @@ impl Mul<Point2DF32> for Point2DF32 {
|
|||
|
||||
// 3D homogeneous points.
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Default)]
|
||||
pub struct Point4DF32(pub F32x4);
|
||||
|
||||
impl Point4DF32 {
|
||||
|
|
|
@ -10,13 +10,15 @@
|
|||
|
||||
//! 3D transforms that can be applied to paths.
|
||||
|
||||
use crate::point::Point4DF32;
|
||||
use crate::point::{Point2DF32, Point4DF32};
|
||||
use crate::segment::Segment;
|
||||
use crate::simd::F32x4;
|
||||
use euclid::Size2D;
|
||||
|
||||
/// An transform, optimized with SIMD.
|
||||
///
|
||||
/// In column-major order.
|
||||
#[derive(Clone, Copy)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub struct Transform3DF32 {
|
||||
c0: F32x4,
|
||||
c1: F32x4,
|
||||
|
@ -52,7 +54,7 @@ impl Transform3DF32 {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub fn scale(x: f32, y: f32, z: f32) -> Transform3DF32 {
|
||||
pub fn from_scale(x: f32, y: f32, z: f32) -> Transform3DF32 {
|
||||
Transform3DF32::row_major( x, 0.0, 0.0, 0.0,
|
||||
0.0, y, 0.0, 0.0,
|
||||
0.0, 0.0, z, 0.0,
|
||||
|
@ -60,7 +62,7 @@ impl Transform3DF32 {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub fn translate(x: f32, y: f32, z: f32) -> Transform3DF32 {
|
||||
pub fn from_translation(x: f32, y: f32, z: f32) -> Transform3DF32 {
|
||||
Transform3DF32::row_major(1.0, 0.0, 0.0, x,
|
||||
0.0, 1.0, 0.0, y,
|
||||
0.0, 0.0, 1.0, z,
|
||||
|
@ -68,7 +70,7 @@ impl Transform3DF32 {
|
|||
}
|
||||
|
||||
// TODO(pcwalton): Optimize.
|
||||
pub fn rotate(roll: f32, pitch: f32, yaw: f32) -> Transform3DF32 {
|
||||
pub fn from_rotation(roll: f32, pitch: f32, yaw: f32) -> Transform3DF32 {
|
||||
let (cos_roll, sin_roll) = (roll.cos(), roll.sin());
|
||||
let (cos_pitch, sin_pitch) = (pitch.cos(), pitch.sin());
|
||||
let (cos_yaw, sin_yaw) = (yaw.cos(), yaw.sin());
|
||||
|
@ -87,9 +89,25 @@ impl Transform3DF32 {
|
|||
0.0, 0.0, 0.0, 1.0)
|
||||
}
|
||||
|
||||
/// Just like `glOrtho()`.
|
||||
#[inline]
|
||||
pub fn from_ortho(left: f32, right: f32, bottom: f32, top: f32, near_val: f32, far_val: f32)
|
||||
-> Transform3DF32 {
|
||||
let x_inv = 1.0 / (right - left);
|
||||
let y_inv = 1.0 / (top - bottom);
|
||||
let z_inv = 1.0 / (far_val - near_val);
|
||||
let tx = -(right + left) * x_inv;
|
||||
let ty = -(top + bottom) * y_inv;
|
||||
let tz = -(far_val + near_val) * z_inv;
|
||||
Transform3DF32::row_major(2.0 * x_inv, 0.0, 0.0, tx,
|
||||
0.0, 2.0 * y_inv, 0.0, ty,
|
||||
0.0, 0.0, -2.0 * z_inv, tz,
|
||||
0.0, 0.0, 0.0, 1.0)
|
||||
}
|
||||
|
||||
/// Just like `gluPerspective()`.
|
||||
#[inline]
|
||||
pub fn perspective(fov_y: f32, aspect: f32, z_near: f32, z_far: f32) -> Transform3DF32 {
|
||||
pub fn from_perspective(fov_y: f32, aspect: f32, z_near: f32, z_far: f32) -> Transform3DF32 {
|
||||
let f = 1.0 / (fov_y * 0.5).tan();
|
||||
let z_denom = 1.0 / (z_near - z_far);
|
||||
let m00 = f / aspect;
|
||||
|
@ -115,24 +133,24 @@ impl Transform3DF32 {
|
|||
//
|
||||
// https://stackoverflow.com/a/18508113
|
||||
#[inline]
|
||||
pub fn post_mul(&self, other: &Transform3DF32) -> Transform3DF32 {
|
||||
pub fn pre_mul(&self, other: &Transform3DF32) -> Transform3DF32 {
|
||||
return Transform3DF32 {
|
||||
c0: mul_row(self.c0, other),
|
||||
c1: mul_row(self.c1, other),
|
||||
c2: mul_row(self.c2, other),
|
||||
c3: mul_row(self.c3, other),
|
||||
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_row(a_row: F32x4, b: &Transform3DF32) -> F32x4 {
|
||||
let (a0, a1) = (F32x4::splat(a_row[0]), F32x4::splat(a_row[1]));
|
||||
let (a2, a3) = (F32x4::splat(a_row[2]), F32x4::splat(a_row[3]));
|
||||
fn mul_col(a_col: F32x4, b: &Transform3DF32) -> 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 pre_mul(&self, other: &Transform3DF32) -> Transform3DF32 {
|
||||
other.post_mul(self)
|
||||
pub fn post_mul(&self, other: &Transform3DF32) -> Transform3DF32 {
|
||||
other.pre_mul(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -144,3 +162,123 @@ impl Transform3DF32 {
|
|||
Point4DF32(term0 + term1 + term2 + term3)
|
||||
}
|
||||
}
|
||||
|
||||
/// Transforms a path with a SIMD 3D transform.
|
||||
pub struct Transform3DF32PathIter<I>
|
||||
where
|
||||
I: Iterator<Item = Segment>,
|
||||
{
|
||||
iter: I,
|
||||
transform: Transform3DF32,
|
||||
window_size: Size2D<u32>,
|
||||
}
|
||||
|
||||
impl<I> Iterator for Transform3DF32PathIter<I>
|
||||
where
|
||||
I: Iterator<Item = Segment>,
|
||||
{
|
||||
type Item = Segment;
|
||||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<Segment> {
|
||||
let mut segment = self.iter.next()?;
|
||||
if !segment.is_none() {
|
||||
segment.baseline.set_from(&self.transform_point(&segment.baseline.from()));
|
||||
segment.baseline.set_to(&self.transform_point(&segment.baseline.to()));
|
||||
if !segment.is_line() {
|
||||
segment.ctrl.set_from(&self.transform_point(&segment.ctrl.from()));
|
||||
if !segment.is_quadratic() {
|
||||
segment.ctrl.set_to(&self.transform_point(&segment.ctrl.to()));
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(segment)
|
||||
}
|
||||
}
|
||||
|
||||
impl<I> Transform3DF32PathIter<I>
|
||||
where
|
||||
I: Iterator<Item = Segment>,
|
||||
{
|
||||
#[inline]
|
||||
pub fn new(iter: I, transform: &Transform3DF32, window_size: &Size2D<u32>)
|
||||
-> Transform3DF32PathIter<I> {
|
||||
Transform3DF32PathIter {
|
||||
iter,
|
||||
transform: *transform,
|
||||
window_size: *window_size,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn transform_point(&self, point: &Point2DF32) -> Point2DF32 {
|
||||
let point = self.transform.transform_point(point.to_4d()).perspective_divide().to_2d();
|
||||
let window_size = self.window_size.to_f32();
|
||||
let size_scale = Point2DF32::new(window_size.width * 0.5, window_size.height * 0.5);
|
||||
(point + Point2DF32::splat(1.0)) * size_scale
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::point::Point4DF32;
|
||||
use crate::transform3d::Transform3DF32;
|
||||
|
||||
#[test]
|
||||
fn test_post_mul() {
|
||||
let a = Transform3DF32::row_major(3.0, 1.0, 4.0, 5.0,
|
||||
9.0, 2.0, 6.0, 5.0,
|
||||
3.0, 5.0, 8.0, 9.0,
|
||||
7.0, 9.0, 3.0, 2.0);
|
||||
let b = Transform3DF32::row_major(3.0, 8.0, 4.0, 6.0,
|
||||
2.0, 6.0, 4.0, 3.0,
|
||||
3.0, 8.0, 3.0, 2.0,
|
||||
7.0, 9.0, 5.0, 0.0);
|
||||
let c = Transform3DF32::row_major(58.0, 107.0, 53.0, 29.0,
|
||||
84.0, 177.0, 87.0, 72.0,
|
||||
106.0, 199.0, 101.0, 49.0,
|
||||
62.0, 152.0, 83.0, 75.0);
|
||||
assert_eq!(a.post_mul(&b), c);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pre_mul() {
|
||||
let a = Transform3DF32::row_major(3.0, 1.0, 4.0, 5.0,
|
||||
9.0, 2.0, 6.0, 5.0,
|
||||
3.0, 5.0, 8.0, 9.0,
|
||||
7.0, 9.0, 3.0, 2.0);
|
||||
let b = Transform3DF32::row_major(3.0, 8.0, 4.0, 6.0,
|
||||
2.0, 6.0, 4.0, 3.0,
|
||||
3.0, 8.0, 3.0, 2.0,
|
||||
7.0, 9.0, 5.0, 0.0);
|
||||
let c = Transform3DF32::row_major(135.0, 93.0, 110.0, 103.0,
|
||||
93.0, 61.0, 85.0, 82.0,
|
||||
104.0, 52.0, 90.0, 86.0,
|
||||
117.0, 50.0, 122.0, 125.0);
|
||||
assert_eq!(a.pre_mul(&b), c);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_transform_point() {
|
||||
let a = Transform3DF32::row_major(3.0, 1.0, 4.0, 5.0,
|
||||
9.0, 2.0, 6.0, 5.0,
|
||||
3.0, 5.0, 8.0, 9.0,
|
||||
7.0, 9.0, 3.0, 2.0);
|
||||
let p = Point4DF32::new(3.0, 8.0, 4.0, 6.0);
|
||||
let q = Point4DF32::new(63.0, 97.0, 135.0, 117.0);
|
||||
assert_eq!(a.transform_point(p), q);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_transpose() {
|
||||
let a = Transform3DF32::row_major(3.0, 1.0, 4.0, 5.0,
|
||||
9.0, 2.0, 6.0, 5.0,
|
||||
3.0, 5.0, 8.0, 9.0,
|
||||
7.0, 9.0, 3.0, 2.0);
|
||||
let b = Transform3DF32::row_major(3.0, 9.0, 3.0, 7.0,
|
||||
1.0, 2.0, 5.0, 9.0,
|
||||
4.0, 6.0, 8.0, 3.0,
|
||||
5.0, 5.0, 9.0, 2.0);
|
||||
assert_eq!(a.transpose(), b);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue