2019-01-14 17:20:36 -05:00
|
|
|
// 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;
|
2019-01-14 17:20:36 -05:00
|
|
|
use pathfinder_geometry::outline::Outline;
|
2019-01-25 17:28:53 -05:00
|
|
|
use pathfinder_geometry::point::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;
|
2019-01-14 17:20:36 -05:00
|
|
|
use rayon::iter::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator};
|
2019-01-29 17:50:15 -05:00
|
|
|
use std::fmt::{self, Debug, Formatter};
|
2019-01-14 17:20:36 -05:00
|
|
|
|
2019-01-29 17:50:15 -05:00
|
|
|
#[derive(Clone)]
|
2019-01-14 17:20:36 -05:00
|
|
|
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, z_buffer: &ZBuffer) -> Vec<BuiltObject> {
|
|
|
|
self.objects
|
|
|
|
.iter()
|
|
|
|
.enumerate()
|
|
|
|
.map(|(object_index, object)| {
|
|
|
|
let mut tiler = Tiler::new(
|
|
|
|
&object.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, z_buffer: &ZBuffer) -> Vec<BuiltObject> {
|
|
|
|
self.objects
|
|
|
|
.par_iter()
|
|
|
|
.enumerate()
|
|
|
|
.map(|(object_index, object)| {
|
|
|
|
let mut tiler = Tiler::new(
|
|
|
|
&object.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
|
|
|
|
2019-01-29 18:35:07 -05:00
|
|
|
pub fn prepare(&mut self) {
|
|
|
|
for object in &mut self.objects {
|
|
|
|
object.outline.make_monotonic();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-15 14:42:25 -05:00
|
|
|
pub fn transform(&mut self, transform: &Transform2DF32) {
|
2019-01-15 16:49:26 -05:00
|
|
|
let mut bounds = Rect::zero();
|
|
|
|
for (object_index, object) in self.objects.iter_mut().enumerate() {
|
|
|
|
object.outline.transform(transform);
|
|
|
|
object.outline.clip_against_rect(&self.view_box);
|
2019-01-16 19:53:10 -05:00
|
|
|
|
|
|
|
if object_index == 0 {
|
|
|
|
bounds = *object.outline.bounds();
|
|
|
|
} else {
|
|
|
|
bounds = bounds.union(object.outline.bounds());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//println!("new bounds={:?}", bounds);
|
|
|
|
self.bounds = bounds;
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn apply_perspective(&mut self, perspective: &Perspective) {
|
2019-01-25 17:28:53 -05:00
|
|
|
let quad = self.clip_bounding_quad_with_perspective(perspective);
|
2019-01-28 17:58:57 -05:00
|
|
|
//println!("bounds={:?} PRE-transform quad={:?}", self.bounds, quad);
|
2019-01-25 20:07:37 -05:00
|
|
|
let inverse_transform = perspective.transform.inverse();
|
|
|
|
let quad: Vec<_> = quad.into_iter().map(|point| {
|
|
|
|
inverse_transform.transform_point_3d(point).to_2d()
|
|
|
|
}).collect();
|
2019-01-28 17:58:57 -05:00
|
|
|
//println!("bounds={:?} POST-transform quad={:?}", self.bounds, quad);
|
2019-01-25 17:28:53 -05:00
|
|
|
|
2019-01-16 19:53:10 -05:00
|
|
|
let mut bounds = Rect::zero();
|
|
|
|
for (object_index, object) in self.objects.iter_mut().enumerate() {
|
2019-01-28 17:58:57 -05:00
|
|
|
object.outline.clip_against_polygon(&quad);
|
2019-01-16 19:53:10 -05:00
|
|
|
object.outline.apply_perspective(perspective);
|
|
|
|
object.outline.clip_against_rect(&self.view_box);
|
2019-01-15 16:49:26 -05:00
|
|
|
|
|
|
|
if object_index == 0 {
|
|
|
|
bounds = *object.outline.bounds();
|
|
|
|
} else {
|
|
|
|
bounds = bounds.union(object.outline.bounds());
|
|
|
|
}
|
2019-01-15 14:42:25 -05:00
|
|
|
}
|
2019-01-15 16:49:26 -05:00
|
|
|
|
|
|
|
//println!("new bounds={:?}", bounds);
|
|
|
|
self.bounds = bounds;
|
2019-01-15 14:42:25 -05:00
|
|
|
}
|
2019-01-25 17:28:53 -05:00
|
|
|
|
|
|
|
fn clip_bounding_quad_with_perspective(&self, perspective: &Perspective) -> Vec<Point3DF32> {
|
|
|
|
let mut points = vec![
|
|
|
|
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-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-25 20:07:37 -05:00
|
|
|
*point = perspective.transform.transform_point_3d(*point);
|
2019-01-25 17:28:53 -05:00
|
|
|
}
|
2019-01-28 17:58:57 -05:00
|
|
|
//println!("PERSPECTIVE quad={:?}", points);
|
2019-01-25 20:07:37 -05:00
|
|
|
//points
|
2019-01-25 17:28:53 -05:00
|
|
|
PolygonClipper3D::new(points).clip()
|
|
|
|
}
|
2019-01-14 17:20:36 -05:00
|
|
|
}
|
|
|
|
|
2019-01-29 17:50:15 -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)]
|
2019-01-14 17:20:36 -05:00
|
|
|
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,
|
|
|
|
}
|
|
|
|
}
|
2019-01-29 17:50:15 -05:00
|
|
|
|
|
|
|
#[inline]
|
|
|
|
pub fn outline(&self) -> &Outline {
|
|
|
|
&self.outline
|
|
|
|
}
|
2019-01-14 17:20:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
#[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
|
|
|
}
|