pathfinder/renderer/src/scene.rs

221 lines
7.1 KiB
Rust
Raw Normal View History

// pathfinder/renderer/src/scene.rs
//
// Copyright © 2019 The Pathfinder Project Developers.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! A set of paths to be rendered.
use crate::gpu_data::BuiltObject;
use crate::paint::{ObjectShader, Paint, PaintId, ShaderId};
use crate::tiles::Tiler;
use crate::z_buffer::ZBuffer;
use euclid::Rect;
use hashbrown::HashMap;
2019-01-25 17:28:53 -05:00
use pathfinder_geometry::clip::PolygonClipper3D;
use pathfinder_geometry::outline::Outline;
2019-02-01 20:07:03 -05:00
use pathfinder_geometry::point::{Point2DF32, Point3DF32};
2019-01-16 19:53:10 -05:00
use pathfinder_geometry::transform3d::Perspective;
2019-01-15 14:42:25 -05:00
use pathfinder_geometry::transform::Transform2DF32;
use rayon::iter::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator};
use std::fmt::{self, Debug, Formatter};
#[derive(Clone)]
pub struct Scene {
pub objects: Vec<PathObject>,
pub paints: Vec<Paint>,
pub paint_cache: HashMap<Paint, PaintId>,
pub bounds: Rect<f32>,
pub view_box: Rect<f32>,
}
impl Scene {
#[inline]
pub fn new() -> Scene {
Scene {
objects: vec![],
paints: vec![],
paint_cache: HashMap::new(),
bounds: Rect::zero(),
view_box: Rect::zero(),
}
}
#[allow(clippy::trivially_copy_pass_by_ref)]
pub fn push_paint(&mut self, paint: &Paint) -> PaintId {
if let Some(paint_id) = self.paint_cache.get(paint) {
return *paint_id;
}
let paint_id = PaintId(self.paints.len() as u16);
self.paint_cache.insert(*paint, paint_id);
self.paints.push(*paint);
paint_id
}
pub fn build_shaders(&self) -> Vec<ObjectShader> {
self.paints
.iter()
.map(|paint| ObjectShader {
fill_color: paint.color,
})
.collect()
}
pub fn build_objects_sequentially(&self, build_transform: &BuildTransform, z_buffer: &ZBuffer)
-> Vec<BuiltObject> {
self.objects
.iter()
.enumerate()
.map(|(object_index, object)| {
let outline = self.apply_build_transform(&object.outline, build_transform);
let mut tiler = Tiler::new(
&outline,
&self.view_box,
object_index as u16,
ShaderId(object.paint.0),
z_buffer,
);
tiler.generate_tiles();
tiler.built_object
})
.collect()
}
pub fn build_objects(&self, build_transform: &BuildTransform, z_buffer: &ZBuffer)
-> Vec<BuiltObject> {
self.objects
.par_iter()
.enumerate()
.map(|(object_index, object)| {
let outline = self.apply_build_transform(&object.outline, build_transform);
let mut tiler = Tiler::new(
&outline,
&self.view_box,
object_index as u16,
ShaderId(object.paint.0),
z_buffer,
);
tiler.generate_tiles();
tiler.built_object
})
.collect()
}
2019-01-15 14:42:25 -05:00
fn apply_build_transform(&self, outline: &Outline, build_transform: &BuildTransform)
-> Outline {
// FIXME(pcwalton): Don't clone?
let mut outline = (*outline).clone();
match *build_transform {
BuildTransform::Perspective(ref perspective) => {
// FIXME(pcwalton): Do this only once!
let quad = self.apply_perspective_to_quad(perspective);
outline.clip_against_polygon(&quad);
outline.apply_perspective(perspective);
2019-01-30 17:42:06 -05:00
}
BuildTransform::Transform2D(ref transform) => {
outline.transform(transform);
}
BuildTransform::None => {}
2019-01-30 17:42:06 -05:00
}
outline.clip_against_rect(&self.view_box);
outline.make_monotonic();
outline
2019-01-16 19:53:10 -05:00
}
2019-01-30 17:42:06 -05:00
fn apply_perspective_to_quad(&self, perspective: &Perspective) -> Vec<Point2DF32> {
2019-01-25 17:28:53 -05:00
let quad = self.clip_bounding_quad_with_perspective(perspective);
2019-01-25 20:07:37 -05:00
let inverse_transform = perspective.transform.inverse();
2019-01-30 17:42:06 -05:00
quad.into_iter()
2019-02-01 20:05:35 -05:00
.map(|point| inverse_transform.transform_point(point).perspective_divide().to_2d())
2019-01-30 17:42:06 -05:00
.collect()
}
2019-01-25 17:28:53 -05:00
2019-02-01 20:07:03 -05:00
fn clip_bounding_quad_with_perspective(&self, perspective: &Perspective) -> Vec<Point3DF32> {
2019-01-25 17:28:53 -05:00
let mut points = vec![
2019-02-01 20:07:03 -05:00
Point3DF32::from_euclid_2d(&self.bounds.origin),
Point3DF32::from_euclid_2d(&self.bounds.top_right()),
Point3DF32::from_euclid_2d(&self.bounds.bottom_right()),
Point3DF32::from_euclid_2d(&self.bounds.bottom_left()),
2019-01-25 17:28:53 -05:00
];
2019-01-28 17:58:57 -05:00
//println!("-----");
//println!("bounds={:?} ORIGINAL quad={:?}", self.bounds, points);
2019-01-25 17:28:53 -05:00
for point in &mut points {
2019-01-31 18:29:13 -05:00
*point = perspective.transform.transform_point(*point);
2019-01-25 17:28:53 -05:00
}
2019-01-31 18:29:13 -05:00
//println!("... PERSPECTIVE quad={:?}", points);
points = PolygonClipper3D::new(points).clip();
//println!("... CLIPPED quad={:?}", points);
points.into_iter().map(|point| point.perspective_divide()).collect()
2019-01-25 17:28:53 -05:00
}
}
impl Debug for Scene {
fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
writeln!(formatter,
"<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"{} {} {} {}\">",
self.view_box.origin.x,
self.view_box.origin.y,
self.view_box.size.width,
self.view_box.size.height)?;
for object in &self.objects {
let paint = &self.paints[object.paint.0 as usize];
write!(formatter, " <path")?;
if !object.name.is_empty() {
write!(formatter, " id=\"{}\"", object.name)?;
}
writeln!(formatter, " fill=\"{:?}\" d=\"{:?}\" />", paint.color, object.outline)?;
}
writeln!(formatter, "</svg>")?;
Ok(())
}
}
2019-01-15 14:42:25 -05:00
#[derive(Clone, Debug)]
pub struct PathObject {
outline: Outline,
paint: PaintId,
name: String,
kind: PathObjectKind,
}
#[derive(Clone, Copy, Debug)]
pub enum PathObjectKind {
Fill,
Stroke,
}
impl PathObject {
#[inline]
pub fn new(outline: Outline, paint: PaintId, name: String, kind: PathObjectKind)
-> PathObject {
PathObject {
outline,
paint,
name,
kind,
}
}
#[inline]
pub fn outline(&self) -> &Outline {
&self.outline
}
}
#[inline]
pub fn scene_tile_index(tile_x: i16, tile_y: i16, tile_rect: Rect<i16>) -> u32 {
(tile_y - tile_rect.origin.y) as u32 * tile_rect.size.width as u32
+ (tile_x - tile_rect.origin.x) as u32
2019-01-15 16:49:26 -05:00
}
pub enum BuildTransform {
None,
Transform2D(Transform2DF32),
Perspective(Perspective),
}