Basic perspective support
This commit is contained in:
parent
a95db7dff9
commit
93ae7d3548
|
@ -14,7 +14,7 @@ use gl::types::{GLchar, GLfloat, GLint, GLsizei, GLsizeiptr, GLuint, GLvoid};
|
||||||
use jemallocator;
|
use jemallocator;
|
||||||
use pathfinder_geometry::point::Point2DF32;
|
use pathfinder_geometry::point::Point2DF32;
|
||||||
use pathfinder_geometry::transform::Transform2DF32;
|
use pathfinder_geometry::transform::Transform2DF32;
|
||||||
use pathfinder_geometry::transform3d::Transform3DF32;
|
use pathfinder_geometry::transform3d::{Perspective, Transform3DF32};
|
||||||
use pathfinder_renderer::builder::SceneBuilder;
|
use pathfinder_renderer::builder::SceneBuilder;
|
||||||
use pathfinder_renderer::gpu_data::{Batch, BuiltScene, SolidTileScenePrimitive};
|
use pathfinder_renderer::gpu_data::{Batch, BuiltScene, SolidTileScenePrimitive};
|
||||||
use pathfinder_renderer::paint::ObjectShader;
|
use pathfinder_renderer::paint::ObjectShader;
|
||||||
|
@ -26,6 +26,7 @@ use rayon::ThreadPoolBuilder;
|
||||||
use sdl2::event::Event;
|
use sdl2::event::Event;
|
||||||
use sdl2::keyboard::Keycode;
|
use sdl2::keyboard::Keycode;
|
||||||
use sdl2::video::GLProfile;
|
use sdl2::video::GLProfile;
|
||||||
|
use std::f32::consts::FRAC_PI_4;
|
||||||
use std::ffi::CString;
|
use std::ffi::CString;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
|
@ -84,12 +85,14 @@ fn main() {
|
||||||
//let mut scale = 1.0;
|
//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);
|
let mut transform = Transform3DF32::from_perspective(FRAC_PI_4, 4.0 / 3.0, 0.01, 100.0);
|
||||||
transform = transform.pre_mul(Transform3DF32::from_translation(0.0, 0.0, -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 {
|
while !exit {
|
||||||
let mut scene = base_scene.clone();
|
let mut scene = base_scene.clone();
|
||||||
scene.transform_3d(&transform);
|
scene.apply_perspective(&perspective);
|
||||||
//scene.transform(&Transform2DF32::from_rotation(theta));
|
//scene.transform(&Transform2DF32::from_rotation(theta));
|
||||||
//scene.transform(&Transform2DF32::from_scale(&Point2DF32::splat(scale)));
|
//scene.transform(&Transform2DF32::from_scale(&Point2DF32::splat(scale)));
|
||||||
//theta += 0.001;
|
//theta += 0.001;
|
||||||
|
|
|
@ -15,9 +15,9 @@ use crate::line_segment::LineSegmentF32;
|
||||||
use crate::monotonic::MonotonicConversionIter;
|
use crate::monotonic::MonotonicConversionIter;
|
||||||
use crate::point::Point2DF32;
|
use crate::point::Point2DF32;
|
||||||
use crate::segment::{Segment, SegmentFlags, SegmentKind};
|
use crate::segment::{Segment, SegmentFlags, SegmentKind};
|
||||||
|
use crate::transform3d::Perspective;
|
||||||
use crate::transform::Transform2DF32;
|
use crate::transform::Transform2DF32;
|
||||||
use euclid::{Point2D, Rect, Size2D};
|
use euclid::{Point2D, Rect, Size2D};
|
||||||
use lyon_path::PathEvent;
|
|
||||||
use std::fmt::{self, Debug, Formatter};
|
use std::fmt::{self, Debug, Formatter};
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
||||||
|
@ -107,6 +107,12 @@ impl Outline {
|
||||||
self.bounds = transform.transform_rect(&self.bounds);
|
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]
|
#[inline]
|
||||||
fn push_contour(&mut self, contour: Contour) {
|
fn push_contour(&mut self, contour: Contour) {
|
||||||
if self.contours.is_empty() {
|
if self.contours.is_empty() {
|
||||||
|
@ -288,6 +294,16 @@ impl Contour {
|
||||||
self.make_monotonic();
|
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]
|
#[inline]
|
||||||
pub fn make_monotonic(&mut self) {
|
pub fn make_monotonic(&mut self) {
|
||||||
let contour = mem::replace(self, Contour::new());
|
let contour = mem::replace(self, Contour::new());
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
use crate::point::{Point2DF32, Point4DF32};
|
use crate::point::{Point2DF32, Point4DF32};
|
||||||
use crate::segment::Segment;
|
use crate::segment::Segment;
|
||||||
use crate::simd::F32x4;
|
use crate::simd::F32x4;
|
||||||
use euclid::Size2D;
|
use euclid::{Point2D, Rect, Size2D};
|
||||||
|
|
||||||
/// An transform, optimized with SIMD.
|
/// An transform, optimized with SIMD.
|
||||||
///
|
///
|
||||||
|
@ -163,17 +163,52 @@ impl Transform3DF32 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Transforms a path with a SIMD 3D transform.
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub struct Transform3DF32PathIter<I>
|
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
|
where
|
||||||
I: Iterator<Item = Segment>,
|
I: Iterator<Item = Segment>,
|
||||||
{
|
{
|
||||||
iter: I,
|
iter: I,
|
||||||
transform: Transform3DF32,
|
perspective: Perspective,
|
||||||
window_size: Size2D<u32>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<I> Iterator for Transform3DF32PathIter<I>
|
impl<I> Iterator for PerspectivePathIter<I>
|
||||||
where
|
where
|
||||||
I: Iterator<Item = Segment>,
|
I: Iterator<Item = Segment>,
|
||||||
{
|
{
|
||||||
|
@ -183,12 +218,12 @@ where
|
||||||
fn next(&mut self) -> Option<Segment> {
|
fn next(&mut self) -> Option<Segment> {
|
||||||
let mut segment = self.iter.next()?;
|
let mut segment = self.iter.next()?;
|
||||||
if !segment.is_none() {
|
if !segment.is_none() {
|
||||||
segment.baseline.set_from(&self.transform_point(&segment.baseline.from()));
|
segment.baseline.set_from(&self.perspective.transform_point(&segment.baseline.from()));
|
||||||
segment.baseline.set_to(&self.transform_point(&segment.baseline.to()));
|
segment.baseline.set_to(&self.perspective.transform_point(&segment.baseline.to()));
|
||||||
if !segment.is_line() {
|
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() {
|
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
|
where
|
||||||
I: Iterator<Item = Segment>,
|
I: Iterator<Item = Segment>,
|
||||||
{
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new(iter: I, transform: &Transform3DF32, window_size: &Size2D<u32>)
|
pub fn new(iter: I, perspective: &Perspective) -> PerspectivePathIter<I> {
|
||||||
-> Transform3DF32PathIter<I> {
|
PerspectivePathIter { iter, perspective: *perspective }
|
||||||
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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@ use crate::z_buffer::ZBuffer;
|
||||||
use euclid::Rect;
|
use euclid::Rect;
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use pathfinder_geometry::outline::Outline;
|
use pathfinder_geometry::outline::Outline;
|
||||||
|
use pathfinder_geometry::transform3d::Perspective;
|
||||||
use pathfinder_geometry::transform::Transform2DF32;
|
use pathfinder_geometry::transform::Transform2DF32;
|
||||||
use rayon::iter::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator};
|
use rayon::iter::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator};
|
||||||
|
|
||||||
|
@ -114,6 +115,23 @@ impl Scene {
|
||||||
//println!("new bounds={:?}", bounds);
|
//println!("new bounds={:?}", bounds);
|
||||||
self.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)]
|
#[derive(Clone, Debug)]
|
||||||
|
|
Loading…
Reference in New Issue