Basic perspective support

This commit is contained in:
Patrick Walton 2019-01-16 16:53:10 -08:00
parent a95db7dff9
commit 93ae7d3548
4 changed files with 90 additions and 31 deletions

View File

@ -14,7 +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_geometry::transform3d::{Perspective, Transform3DF32};
use pathfinder_renderer::builder::SceneBuilder;
use pathfinder_renderer::gpu_data::{Batch, BuiltScene, SolidTileScenePrimitive};
use pathfinder_renderer::paint::ObjectShader;
@ -26,6 +26,7 @@ use rayon::ThreadPoolBuilder;
use sdl2::event::Event;
use sdl2::keyboard::Keycode;
use sdl2::video::GLProfile;
use std::f32::consts::FRAC_PI_4;
use std::ffi::CString;
use std::fs::File;
use std::io::Read;
@ -84,12 +85,14 @@ fn main() {
//let mut scale = 1.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));
let mut transform = Transform3DF32::from_perspective(FRAC_PI_4, 4.0 / 3.0, 0.01, 100.0);
transform = transform.post_mul(&Transform3DF32::from_translation(0.0, 0.0, -6.0));
let window_size = Size2D::new(MAIN_FRAMEBUFFER_WIDTH, MAIN_FRAMEBUFFER_HEIGHT);
let perspective = Perspective::new(&transform, &window_size);
while !exit {
let mut scene = base_scene.clone();
scene.transform_3d(&transform);
scene.apply_perspective(&perspective);
//scene.transform(&Transform2DF32::from_rotation(theta));
//scene.transform(&Transform2DF32::from_scale(&Point2DF32::splat(scale)));
//theta += 0.001;

View File

@ -15,9 +15,9 @@ use crate::line_segment::LineSegmentF32;
use crate::monotonic::MonotonicConversionIter;
use crate::point::Point2DF32;
use crate::segment::{Segment, SegmentFlags, SegmentKind};
use crate::transform3d::Perspective;
use crate::transform::Transform2DF32;
use euclid::{Point2D, Rect, Size2D};
use lyon_path::PathEvent;
use std::fmt::{self, Debug, Formatter};
use std::mem;
@ -107,6 +107,12 @@ impl Outline {
self.bounds = transform.transform_rect(&self.bounds);
}
#[inline]
pub fn apply_perspective(&mut self, perspective: &Perspective) {
self.contours.iter_mut().for_each(|contour| contour.apply_perspective(perspective));
self.bounds = perspective.transform_rect(&self.bounds);
}
#[inline]
fn push_contour(&mut self, contour: Contour) {
if self.contours.is_empty() {
@ -288,6 +294,16 @@ impl Contour {
self.make_monotonic();
}
#[inline]
pub fn apply_perspective(&mut self, perspective: &Perspective) {
for (point_index, point) in self.points.iter_mut().enumerate() {
*point = perspective.transform_point(point);
union_rect(&mut self.bounds, *point, point_index == 0);
}
self.make_monotonic();
}
#[inline]
pub fn make_monotonic(&mut self) {
let contour = mem::replace(self, Contour::new());

View File

@ -13,7 +13,7 @@
use crate::point::{Point2DF32, Point4DF32};
use crate::segment::Segment;
use crate::simd::F32x4;
use euclid::Size2D;
use euclid::{Point2D, Rect, Size2D};
/// An transform, optimized with SIMD.
///
@ -163,17 +163,52 @@ impl Transform3DF32 {
}
}
/// Transforms a path with a SIMD 3D transform.
pub struct Transform3DF32PathIter<I>
#[derive(Clone, Copy, Debug)]
pub struct Perspective {
pub transform: Transform3DF32,
pub window_size: Size2D<u32>,
}
impl Perspective {
#[inline]
pub fn new(transform: &Transform3DF32, window_size: &Size2D<u32>) -> Perspective {
Perspective { transform: *transform, window_size: *window_size }
}
#[inline]
pub 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
}
// TODO(pcwalton): SIMD?
#[inline]
pub fn transform_rect(&self, rect: &Rect<f32>) -> Rect<f32> {
let upper_left = self.transform_point(&Point2DF32::from_euclid(rect.origin));
let upper_right = self.transform_point(&Point2DF32::from_euclid(rect.top_right()));
let lower_left = self.transform_point(&Point2DF32::from_euclid(rect.bottom_left()));
let lower_right = self.transform_point(&Point2DF32::from_euclid(rect.bottom_right()));
let min_x = upper_left.x().min(upper_right.x()).min(lower_left.x()).min(lower_right.x());
let min_y = upper_left.y().min(upper_right.y()).min(lower_left.y()).min(lower_right.y());
let max_x = upper_left.x().max(upper_right.x()).max(lower_left.x()).max(lower_right.x());
let max_y = upper_left.y().max(upper_right.y()).max(lower_left.y()).max(lower_right.y());
let (width, height) = (max_x - min_x, max_y - min_y);
Rect::new(Point2D::new(min_x, min_y), Size2D::new(width, height))
}
}
/// Transforms a path with a perspective projection.
pub struct PerspectivePathIter<I>
where
I: Iterator<Item = Segment>,
{
iter: I,
transform: Transform3DF32,
window_size: Size2D<u32>,
perspective: Perspective,
}
impl<I> Iterator for Transform3DF32PathIter<I>
impl<I> Iterator for PerspectivePathIter<I>
where
I: Iterator<Item = Segment>,
{
@ -183,12 +218,12 @@ where
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()));
segment.baseline.set_from(&self.perspective.transform_point(&segment.baseline.from()));
segment.baseline.set_to(&self.perspective.transform_point(&segment.baseline.to()));
if !segment.is_line() {
segment.ctrl.set_from(&self.transform_point(&segment.ctrl.from()));
segment.ctrl.set_from(&self.perspective.transform_point(&segment.ctrl.from()));
if !segment.is_quadratic() {
segment.ctrl.set_to(&self.transform_point(&segment.ctrl.to()));
segment.ctrl.set_to(&self.perspective.transform_point(&segment.ctrl.to()));
}
}
}
@ -196,26 +231,13 @@ where
}
}
impl<I> Transform3DF32PathIter<I>
impl<I> PerspectivePathIter<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
pub fn new(iter: I, perspective: &Perspective) -> PerspectivePathIter<I> {
PerspectivePathIter { iter, perspective: *perspective }
}
}

View File

@ -17,6 +17,7 @@ use crate::z_buffer::ZBuffer;
use euclid::Rect;
use hashbrown::HashMap;
use pathfinder_geometry::outline::Outline;
use pathfinder_geometry::transform3d::Perspective;
use pathfinder_geometry::transform::Transform2DF32;
use rayon::iter::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator};
@ -114,6 +115,23 @@ impl Scene {
//println!("new bounds={:?}", bounds);
self.bounds = bounds;
}
pub fn apply_perspective(&mut self, perspective: &Perspective) {
let mut bounds = Rect::zero();
for (object_index, object) in self.objects.iter_mut().enumerate() {
object.outline.apply_perspective(perspective);
object.outline.clip_against_rect(&self.view_box);
if object_index == 0 {
bounds = *object.outline.bounds();
} else {
bounds = bounds.union(object.outline.bounds());
}
}
//println!("new bounds={:?}", bounds);
self.bounds = bounds;
}
}
#[derive(Clone, Debug)]