Display timing info in the demo
This commit is contained in:
parent
6b8848bb35
commit
e105bdb7c9
|
@ -11,6 +11,9 @@ gl = "0.6"
|
|||
jemallocator = "0.1"
|
||||
rayon = "1.0"
|
||||
sdl2 = "0.32"
|
||||
serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
serde_json = "1.0"
|
||||
usvg = "0.4"
|
||||
|
||||
[dependencies.image]
|
||||
|
|
|
@ -0,0 +1,105 @@
|
|||
{
|
||||
"name": "D-DIN",
|
||||
"size": 28,
|
||||
"bold": false,
|
||||
"italic": false,
|
||||
"width": 512,
|
||||
"height": 128,
|
||||
"characters": {
|
||||
"0":{"x":338,"y":0,"width":18,"height":30,"originX":0,"originY":28,"advance":18},
|
||||
"1":{"x":30,"y":64,"width":11,"height":29,"originX":0,"originY":28,"advance":12},
|
||||
"2":{"x":490,"y":35,"width":17,"height":29,"originX":0,"originY":28,"advance":17},
|
||||
"3":{"x":356,"y":0,"width":18,"height":30,"originX":0,"originY":28,"advance":18},
|
||||
"4":{"x":398,"y":35,"width":19,"height":29,"originX":0,"originY":28,"advance":19},
|
||||
"5":{"x":374,"y":0,"width":18,"height":30,"originX":0,"originY":28,"advance":18},
|
||||
"6":{"x":392,"y":0,"width":18,"height":30,"originX":0,"originY":28,"advance":18},
|
||||
"7":{"x":436,"y":35,"width":18,"height":29,"originX":1,"originY":28,"advance":16},
|
||||
"8":{"x":319,"y":0,"width":19,"height":30,"originX":0,"originY":28,"advance":19},
|
||||
"9":{"x":454,"y":35,"width":18,"height":29,"originX":0,"originY":28,"advance":18},
|
||||
" ":{"x":147,"y":93,"width":3,"height":3,"originX":1,"originY":1,"advance":8},
|
||||
"!":{"x":62,"y":64,"width":7,"height":29,"originX":-1,"originY":28,"advance":9},
|
||||
"\"":{"x":52,"y":93,"width":12,"height":12,"originX":0,"originY":28,"advance":11},
|
||||
"#":{"x":233,"y":35,"width":21,"height":29,"originX":1,"originY":28,"advance":19},
|
||||
"$":{"x":138,"y":0,"width":18,"height":34,"originX":1,"originY":30,"advance":17},
|
||||
"%":{"x":156,"y":0,"width":31,"height":30,"originX":0,"originY":28,"advance":31},
|
||||
"&":{"x":187,"y":0,"width":22,"height":30,"originX":0,"originY":28,"advance":22},
|
||||
"'":{"x":64,"y":93,"width":6,"height":12,"originX":0,"originY":28,"advance":6},
|
||||
"(":{"x":59,"y":0,"width":10,"height":35,"originX":0,"originY":30,"advance":11},
|
||||
")":{"x":48,"y":0,"width":11,"height":35,"originX":0,"originY":30,"advance":11},
|
||||
"*":{"x":19,"y":93,"width":14,"height":14,"originX":0,"originY":28,"advance":14},
|
||||
"+":{"x":432,"y":64,"width":19,"height":19,"originX":0,"originY":20,"advance":19},
|
||||
",":{"x":70,"y":93,"width":6,"height":11,"originX":0,"originY":5,"advance":6},
|
||||
"-":{"x":105,"y":93,"width":14,"height":6,"originX":0,"originY":16,"advance":15},
|
||||
".":{"x":119,"y":93,"width":7,"height":6,"originX":0,"originY":5,"advance":7},
|
||||
"/":{"x":446,"y":0,"width":14,"height":30,"originX":0,"originY":28,"advance":15},
|
||||
":":{"x":470,"y":64,"width":7,"height":19,"originX":0,"originY":18,"advance":7},
|
||||
";":{"x":171,"y":64,"width":7,"height":24,"originX":0,"originY":18,"advance":7},
|
||||
"<":{"x":477,"y":64,"width":19,"height":17,"originX":0,"originY":19,"advance":19},
|
||||
"=":{"x":33,"y":93,"width":19,"height":13,"originX":0,"originY":17,"advance":19},
|
||||
">":{"x":0,"y":93,"width":19,"height":17,"originX":0,"originY":19,"advance":19},
|
||||
"?":{"x":0,"y":64,"width":17,"height":29,"originX":1,"originY":28,"advance":16},
|
||||
"@":{"x":106,"y":0,"width":32,"height":34,"originX":0,"originY":28,"advance":32},
|
||||
"A":{"x":26,"y":35,"width":25,"height":29,"originX":2,"originY":28,"advance":22},
|
||||
"B":{"x":254,"y":35,"width":21,"height":29,"originX":-1,"originY":28,"advance":23},
|
||||
"C":{"x":209,"y":0,"width":22,"height":30,"originX":0,"originY":28,"advance":23},
|
||||
"D":{"x":145,"y":35,"width":22,"height":29,"originX":-1,"originY":28,"advance":23},
|
||||
"E":{"x":338,"y":35,"width":20,"height":29,"originX":-1,"originY":28,"advance":20},
|
||||
"F":{"x":358,"y":35,"width":20,"height":29,"originX":-1,"originY":28,"advance":20},
|
||||
"G":{"x":231,"y":0,"width":22,"height":30,"originX":0,"originY":28,"advance":23},
|
||||
"H":{"x":275,"y":35,"width":21,"height":29,"originX":-1,"originY":28,"advance":23},
|
||||
"I":{"x":69,"y":64,"width":7,"height":29,"originX":-1,"originY":28,"advance":9},
|
||||
"J":{"x":41,"y":64,"width":11,"height":29,"originX":1,"originY":28,"advance":12},
|
||||
"K":{"x":76,"y":35,"width":23,"height":29,"originX":-1,"originY":28,"advance":22},
|
||||
"L":{"x":378,"y":35,"width":20,"height":29,"originX":-1,"originY":28,"advance":20},
|
||||
"M":{"x":0,"y":35,"width":26,"height":29,"originX":-1,"originY":28,"advance":28},
|
||||
"N":{"x":99,"y":35,"width":23,"height":29,"originX":-1,"originY":28,"advance":25},
|
||||
"O":{"x":253,"y":0,"width":22,"height":30,"originX":0,"originY":28,"advance":23},
|
||||
"P":{"x":296,"y":35,"width":21,"height":29,"originX":-1,"originY":28,"advance":22},
|
||||
"Q":{"x":0,"y":0,"width":22,"height":35,"originX":0,"originY":28,"advance":23},
|
||||
"R":{"x":167,"y":35,"width":22,"height":29,"originX":-1,"originY":28,"advance":22},
|
||||
"S":{"x":275,"y":0,"width":22,"height":30,"originX":1,"originY":28,"advance":20},
|
||||
"T":{"x":189,"y":35,"width":22,"height":29,"originX":2,"originY":28,"advance":18},
|
||||
"U":{"x":297,"y":0,"width":22,"height":30,"originX":-1,"originY":28,"advance":24},
|
||||
"V":{"x":122,"y":35,"width":23,"height":29,"originX":2,"originY":28,"advance":19},
|
||||
"W":{"x":474,"y":0,"width":33,"height":29,"originX":2,"originY":28,"advance":30},
|
||||
"X":{"x":51,"y":35,"width":25,"height":29,"originX":2,"originY":28,"advance":21},
|
||||
"Y":{"x":211,"y":35,"width":22,"height":29,"originX":2,"originY":28,"advance":19},
|
||||
"Z":{"x":317,"y":35,"width":21,"height":29,"originX":1,"originY":28,"advance":19},
|
||||
"[":{"x":69,"y":0,"width":10,"height":35,"originX":-1,"originY":30,"advance":11},
|
||||
"\\":{"x":460,"y":0,"width":14,"height":30,"originX":0,"originY":28,"advance":15},
|
||||
"]":{"x":79,"y":0,"width":10,"height":35,"originX":0,"originY":30,"advance":11},
|
||||
"^":{"x":451,"y":64,"width":19,"height":19,"originX":0,"originY":28,"advance":19},
|
||||
"_":{"x":126,"y":93,"width":21,"height":5,"originX":2,"originY":0,"advance":17},
|
||||
"`":{"x":95,"y":93,"width":10,"height":8,"originX":-4,"originY":28,"advance":19},
|
||||
"a":{"x":235,"y":64,"width":18,"height":23,"originX":0,"originY":21,"advance":19},
|
||||
"b":{"x":410,"y":0,"width":18,"height":30,"originX":-1,"originY":28,"advance":19},
|
||||
"c":{"x":178,"y":64,"width":19,"height":23,"originX":0,"originY":21,"advance":19},
|
||||
"d":{"x":428,"y":0,"width":18,"height":30,"originX":0,"originY":28,"advance":19},
|
||||
"e":{"x":197,"y":64,"width":19,"height":23,"originX":0,"originY":21,"advance":19},
|
||||
"f":{"x":17,"y":64,"width":13,"height":29,"originX":0,"originY":28,"advance":12},
|
||||
"g":{"x":96,"y":64,"width":18,"height":28,"originX":0,"originY":21,"advance":19},
|
||||
"h":{"x":472,"y":35,"width":18,"height":29,"originX":-1,"originY":28,"advance":19},
|
||||
"i":{"x":150,"y":64,"width":7,"height":28,"originX":-1,"originY":27,"advance":9},
|
||||
"j":{"x":89,"y":0,"width":10,"height":35,"originX":3,"originY":28,"advance":8},
|
||||
"k":{"x":417,"y":35,"width":19,"height":29,"originX":-1,"originY":28,"advance":18},
|
||||
"l":{"x":52,"y":64,"width":10,"height":29,"originX":-1,"originY":28,"advance":10},
|
||||
"m":{"x":289,"y":64,"width":29,"height":22,"originX":-1,"originY":21,"advance":31},
|
||||
"n":{"x":366,"y":64,"width":18,"height":22,"originX":-1,"originY":21,"advance":19},
|
||||
"o":{"x":216,"y":64,"width":19,"height":23,"originX":0,"originY":21,"advance":19},
|
||||
"p":{"x":114,"y":64,"width":18,"height":28,"originX":-1,"originY":21,"advance":19},
|
||||
"q":{"x":132,"y":64,"width":18,"height":28,"originX":0,"originY":21,"advance":19},
|
||||
"r":{"x":419,"y":64,"width":13,"height":22,"originX":-1,"originY":21,"advance":13},
|
||||
"s":{"x":253,"y":64,"width":18,"height":23,"originX":0,"originY":21,"advance":17},
|
||||
"t":{"x":157,"y":64,"width":14,"height":27,"originX":1,"originY":26,"advance":13},
|
||||
"u":{"x":271,"y":64,"width":18,"height":23,"originX":-1,"originY":21,"advance":19},
|
||||
"v":{"x":347,"y":64,"width":19,"height":22,"originX":1,"originY":21,"advance":16},
|
||||
"w":{"x":318,"y":64,"width":29,"height":22,"originX":1,"originY":21,"advance":27},
|
||||
"x":{"x":384,"y":64,"width":18,"height":22,"originX":1,"originY":21,"advance":16},
|
||||
"y":{"x":76,"y":64,"width":20,"height":28,"originX":2,"originY":21,"advance":16},
|
||||
"z":{"x":402,"y":64,"width":17,"height":22,"originX":0,"originY":21,"advance":17},
|
||||
"{":{"x":22,"y":0,"width":13,"height":35,"originX":0,"originY":30,"advance":13},
|
||||
"|":{"x":99,"y":0,"width":7,"height":35,"originX":-2,"originY":30,"advance":11},
|
||||
"}":{"x":35,"y":0,"width":13,"height":35,"originX":0,"originY":30,"advance":13},
|
||||
"~":{"x":76,"y":93,"width":19,"height":9,"originX":0,"originY":15,"advance":19}
|
||||
}
|
||||
}
|
Before Width: | Height: | Size: 6.9 KiB After Width: | Height: | Size: 6.9 KiB |
Binary file not shown.
After Width: | Height: | Size: 21 KiB |
|
@ -0,0 +1,24 @@
|
|||
#version 330
|
||||
|
||||
// pathfinder/demo3/shaders/debug_font.fs.glsl
|
||||
//
|
||||
// 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.
|
||||
|
||||
precision highp float;
|
||||
|
||||
uniform sampler2D uFontTexture;
|
||||
|
||||
in vec2 vTexCoord;
|
||||
|
||||
out vec4 oFragColor;
|
||||
|
||||
void main() {
|
||||
float coverage = texture(uFontTexture, vTexCoord).r;
|
||||
oFragColor = vec4(coverage);
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
#version 330
|
||||
|
||||
// pathfinder/demo3/shaders/debug_font.vs.glsl
|
||||
//
|
||||
// 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.
|
||||
|
||||
precision highp float;
|
||||
|
||||
uniform vec2 uFramebufferSize;
|
||||
uniform vec2 uFontTextureSize;
|
||||
|
||||
in vec2 aPosition;
|
||||
in vec2 aTexCoord;
|
||||
|
||||
out vec2 vTexCoord;
|
||||
|
||||
void main() {
|
||||
vTexCoord = aTexCoord / uFontTextureSize;
|
||||
vec2 position = aPosition / uFramebufferSize * 2.0 - 1.0;
|
||||
gl_Position = vec4(position.x, -position.y, 0.0, 1.0);
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
#version 330
|
||||
|
||||
// pathfinder/demo3/shaders/debug_solid.fs.glsl
|
||||
//
|
||||
// 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.
|
||||
|
||||
precision highp float;
|
||||
|
||||
uniform vec4 uColor;
|
||||
|
||||
out vec4 oFragColor;
|
||||
|
||||
void main() {
|
||||
oFragColor = uColor;
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
#version 330
|
||||
|
||||
// pathfinder/demo3/shaders/debug_solid.vs.glsl
|
||||
//
|
||||
// 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.
|
||||
|
||||
precision highp float;
|
||||
|
||||
uniform vec2 uFramebufferSize;
|
||||
|
||||
in vec2 aPosition;
|
||||
|
||||
void main() {
|
||||
vec2 position = aPosition / uFramebufferSize * 2.0 - 1.0;
|
||||
gl_Position = vec4(position.x, -position.y, 0.0, 1.0);
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
#version 330
|
||||
|
||||
// pathfinder/demo3/resources/shaders/mask_tile.vs.glsl
|
||||
// pathfinder/demo3/shaders/mask_tile.vs.glsl
|
||||
//
|
||||
// Copyright © 2018 The Pathfinder Project Developers.
|
||||
//
|
||||
|
|
|
@ -0,0 +1,374 @@
|
|||
// pathfinder/demo3/src/debug_text.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.
|
||||
|
||||
//! Minimal text rendering.
|
||||
//!
|
||||
//! We don't render the demo UI text using Pathfinder itself so that we can use the debug UI to
|
||||
//! debug Pathfinder if it's totally busted.
|
||||
//!
|
||||
//! The debug font atlas was generated using: https://evanw.github.io/font-texture-generator/
|
||||
|
||||
use crate::device::{Buffer, BufferTarget, BufferUploadMode, Program, Texture, Uniform, VertexAttr};
|
||||
use euclid::{Point2D, Rect, Size2D};
|
||||
use gl::types::{GLfloat, GLint, GLsizei, GLuint};
|
||||
use gl;
|
||||
use pathfinder_renderer::paint::ColorU;
|
||||
use serde_json;
|
||||
use std::collections::HashMap;
|
||||
use std::fs::File;
|
||||
use std::io::BufReader;
|
||||
use std::ptr;
|
||||
use std::time::Duration;
|
||||
|
||||
const DEBUG_FONT_VERTEX_SIZE: GLint = 8;
|
||||
const DEBUG_SOLID_VERTEX_SIZE: GLint = 4;
|
||||
|
||||
const WINDOW_WIDTH: i16 = 400;
|
||||
const WINDOW_HEIGHT: i16 = LINE_HEIGHT * 2 + PADDING + 3;
|
||||
const PADDING: i16 = 12;
|
||||
const FONT_ASCENT: i16 = 28;
|
||||
const LINE_HEIGHT: i16 = 42;
|
||||
|
||||
static WINDOW_COLOR: ColorU = ColorU { r: 30, g: 30, b: 30, a: 255 - 30 };
|
||||
|
||||
static JSON_PATH: &'static str = "resources/debug-font.json";
|
||||
static PNG_NAME: &'static str = "debug-font";
|
||||
|
||||
static QUAD_INDICES: [u32; 6] = [0, 1, 3, 1, 2, 3];
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[allow(dead_code)]
|
||||
pub struct DebugFont {
|
||||
name: String,
|
||||
size: i32,
|
||||
bold: bool,
|
||||
italic: bool,
|
||||
width: u32,
|
||||
height: u32,
|
||||
characters: HashMap<char, DebugCharacter>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct DebugCharacter {
|
||||
x: u16,
|
||||
y: u16,
|
||||
width: u16,
|
||||
height: u16,
|
||||
#[serde(rename = "originX")]
|
||||
origin_x: i16,
|
||||
#[serde(rename = "originY")]
|
||||
origin_y: i16,
|
||||
advance: i16,
|
||||
}
|
||||
|
||||
impl DebugFont {
|
||||
fn load() -> DebugFont {
|
||||
serde_json::from_reader(BufReader::new(File::open(JSON_PATH).unwrap())).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DebugRenderer {
|
||||
framebuffer_size: Size2D<u32>,
|
||||
font_program: DebugFontProgram,
|
||||
font_vertex_array: DebugFontVertexArray,
|
||||
font_texture: Texture,
|
||||
font: DebugFont,
|
||||
solid_program: DebugSolidProgram,
|
||||
solid_vertex_array: DebugSolidVertexArray,
|
||||
}
|
||||
|
||||
impl DebugRenderer {
|
||||
pub fn new(framebuffer_size: &Size2D<u32>) -> DebugRenderer {
|
||||
let font_program = DebugFontProgram::new();
|
||||
let font_vertex_array = DebugFontVertexArray::new(&font_program);
|
||||
let font_texture = Texture::from_png(PNG_NAME);
|
||||
let font = DebugFont::load();
|
||||
|
||||
let solid_program = DebugSolidProgram::new();
|
||||
let solid_vertex_array = DebugSolidVertexArray::new(&solid_program);
|
||||
solid_vertex_array.index_buffer.upload(&QUAD_INDICES,
|
||||
BufferTarget::Index,
|
||||
BufferUploadMode::Static);
|
||||
|
||||
DebugRenderer {
|
||||
framebuffer_size: *framebuffer_size,
|
||||
font_program,
|
||||
font_vertex_array,
|
||||
font_texture,
|
||||
font,
|
||||
solid_program,
|
||||
solid_vertex_array,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_framebuffer_size(&mut self, window_size: &Size2D<u32>) {
|
||||
self.framebuffer_size = *window_size;
|
||||
}
|
||||
|
||||
pub fn draw(&self, prepare_time: Duration, tile_time: Duration) {
|
||||
let window_rect =
|
||||
Rect::new(Point2D::new(self.framebuffer_size.width as i16 - PADDING - WINDOW_WIDTH,
|
||||
self.framebuffer_size.height as i16 - PADDING - WINDOW_HEIGHT),
|
||||
Size2D::new(WINDOW_WIDTH, WINDOW_HEIGHT));
|
||||
self.draw_solid_rect(&window_rect, WINDOW_COLOR);
|
||||
self.draw_text(&format!("Preparation: {:.3} ms", duration_ms(prepare_time)),
|
||||
&Point2D::new(window_rect.origin.x + PADDING,
|
||||
window_rect.origin.y + PADDING + FONT_ASCENT));
|
||||
self.draw_text(&format!("Tiling: {:.3} ms", duration_ms(tile_time)),
|
||||
&Point2D::new(window_rect.origin.x + PADDING,
|
||||
window_rect.origin.y + PADDING + FONT_ASCENT + LINE_HEIGHT));
|
||||
}
|
||||
|
||||
fn draw_solid_rect(&self, rect: &Rect<i16>, color: ColorU) {
|
||||
let vertex_data = [
|
||||
DebugSolidVertex::new(rect.origin),
|
||||
DebugSolidVertex::new(rect.top_right()),
|
||||
DebugSolidVertex::new(rect.bottom_right()),
|
||||
DebugSolidVertex::new(rect.bottom_left()),
|
||||
];
|
||||
self.solid_vertex_array
|
||||
.vertex_buffer
|
||||
.upload(&vertex_data, BufferTarget::Vertex, BufferUploadMode::Dynamic);
|
||||
|
||||
unsafe {
|
||||
gl::BindVertexArray(self.solid_vertex_array.gl_vertex_array);
|
||||
gl::UseProgram(self.solid_program.program.gl_program);
|
||||
gl::Uniform2f(self.solid_program.framebuffer_size_uniform.location,
|
||||
self.framebuffer_size.width as GLfloat,
|
||||
self.framebuffer_size.height as GLfloat);
|
||||
gl::Uniform4f(self.solid_program.color_uniform.location,
|
||||
color.r as f32 * (1.0 / 255.0),
|
||||
color.g as f32 * (1.0 / 255.0),
|
||||
color.b as f32 * (1.0 / 255.0),
|
||||
color.a as f32 * (1.0 / 255.0));
|
||||
gl::BlendEquation(gl::FUNC_ADD);
|
||||
gl::BlendFunc(gl::ONE, gl::ONE_MINUS_SRC_ALPHA);
|
||||
gl::Enable(gl::BLEND);
|
||||
gl::DrawElements(gl::TRIANGLES, 6, gl::UNSIGNED_INT, ptr::null());
|
||||
gl::Disable(gl::BLEND);
|
||||
}
|
||||
}
|
||||
|
||||
fn draw_text(&self, string: &str, origin: &Point2D<i16>) {
|
||||
let mut next = *origin;
|
||||
let char_count = string.chars().count();
|
||||
let mut vertex_data = Vec::with_capacity(char_count * 4);
|
||||
let mut index_data = Vec::with_capacity(char_count * 6);
|
||||
for mut character in string.chars() {
|
||||
if !self.font.characters.contains_key(&character) {
|
||||
character = '?';
|
||||
}
|
||||
let info = &self.font.characters[&character];
|
||||
let position_rect =
|
||||
Rect::new(Point2D::new(next.x - info.origin_x, next.y - info.origin_y),
|
||||
Size2D::new(info.width as i16, info.height as i16));
|
||||
let tex_coord_rect = Rect::new(Point2D::new(info.x, info.y),
|
||||
Size2D::new(info.width, info.height));
|
||||
let first_vertex_index = vertex_data.len();
|
||||
vertex_data.extend_from_slice(&[
|
||||
DebugFontVertex::new(position_rect.origin, tex_coord_rect.origin),
|
||||
DebugFontVertex::new(position_rect.top_right(), tex_coord_rect.top_right()),
|
||||
DebugFontVertex::new(position_rect.bottom_right(), tex_coord_rect.bottom_right()),
|
||||
DebugFontVertex::new(position_rect.bottom_left(), tex_coord_rect.bottom_left()),
|
||||
]);
|
||||
index_data.extend(QUAD_INDICES.iter().map(|&i| i + first_vertex_index as u32));
|
||||
next.x += info.advance;
|
||||
}
|
||||
|
||||
self.font_vertex_array
|
||||
.vertex_buffer
|
||||
.upload(&vertex_data, BufferTarget::Vertex, BufferUploadMode::Dynamic);
|
||||
self.font_vertex_array
|
||||
.index_buffer
|
||||
.upload(&index_data, BufferTarget::Index, BufferUploadMode::Dynamic);
|
||||
|
||||
unsafe {
|
||||
gl::BindVertexArray(self.font_vertex_array.gl_vertex_array);
|
||||
gl::UseProgram(self.font_program.program.gl_program);
|
||||
gl::Uniform2f(self.font_program.framebuffer_size_uniform.location,
|
||||
self.framebuffer_size.width as GLfloat,
|
||||
self.framebuffer_size.height as GLfloat);
|
||||
gl::Uniform2f(self.font_program.font_texture_size_uniform.location,
|
||||
self.font_texture.size.width as GLfloat,
|
||||
self.font_texture.size.height as GLfloat);
|
||||
self.font_texture.bind(0);
|
||||
gl::Uniform1i(self.font_program.font_texture_uniform.location, 0);
|
||||
gl::BlendEquation(gl::FUNC_ADD);
|
||||
gl::BlendFunc(gl::ONE, gl::ONE_MINUS_SRC_ALPHA);
|
||||
gl::Enable(gl::BLEND);
|
||||
gl::DrawElements(gl::TRIANGLES,
|
||||
index_data.len() as GLsizei,
|
||||
gl::UNSIGNED_INT,
|
||||
ptr::null());
|
||||
gl::Disable(gl::BLEND);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct DebugFontVertexArray {
|
||||
gl_vertex_array: GLuint,
|
||||
vertex_buffer: Buffer,
|
||||
index_buffer: Buffer,
|
||||
}
|
||||
|
||||
impl DebugFontVertexArray {
|
||||
fn new(debug_font_program: &DebugFontProgram) -> DebugFontVertexArray {
|
||||
let vertex_buffer = Buffer::new();
|
||||
let index_buffer = Buffer::new();
|
||||
let mut gl_vertex_array = 0;
|
||||
unsafe {
|
||||
let position_attr = VertexAttr::new(&debug_font_program.program, "Position");
|
||||
let tex_coord_attr = VertexAttr::new(&debug_font_program.program, "TexCoord");
|
||||
|
||||
gl::GenVertexArrays(1, &mut gl_vertex_array);
|
||||
gl::BindVertexArray(gl_vertex_array);
|
||||
gl::UseProgram(debug_font_program.program.gl_program);
|
||||
gl::BindBuffer(gl::ARRAY_BUFFER, vertex_buffer.gl_buffer);
|
||||
position_attr.configure_float(2,
|
||||
gl::UNSIGNED_SHORT,
|
||||
false,
|
||||
DEBUG_FONT_VERTEX_SIZE,
|
||||
0,
|
||||
0);
|
||||
tex_coord_attr.configure_float(2,
|
||||
gl::UNSIGNED_SHORT,
|
||||
false,
|
||||
DEBUG_FONT_VERTEX_SIZE,
|
||||
4,
|
||||
0);
|
||||
gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, index_buffer.gl_buffer);
|
||||
}
|
||||
|
||||
DebugFontVertexArray { gl_vertex_array, vertex_buffer, index_buffer }
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for DebugFontVertexArray {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
gl::DeleteVertexArrays(1, &mut self.gl_vertex_array);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct DebugSolidVertexArray {
|
||||
gl_vertex_array: GLuint,
|
||||
vertex_buffer: Buffer,
|
||||
index_buffer: Buffer,
|
||||
}
|
||||
|
||||
impl DebugSolidVertexArray {
|
||||
fn new(debug_solid_program: &DebugSolidProgram) -> DebugSolidVertexArray {
|
||||
let vertex_buffer = Buffer::new();
|
||||
let index_buffer = Buffer::new();
|
||||
let mut gl_vertex_array = 0;
|
||||
unsafe {
|
||||
let position_attr = VertexAttr::new(&debug_solid_program.program, "Position");
|
||||
|
||||
gl::GenVertexArrays(1, &mut gl_vertex_array);
|
||||
gl::BindVertexArray(gl_vertex_array);
|
||||
gl::UseProgram(debug_solid_program.program.gl_program);
|
||||
gl::BindBuffer(gl::ARRAY_BUFFER, vertex_buffer.gl_buffer);
|
||||
position_attr.configure_float(2,
|
||||
gl::UNSIGNED_SHORT,
|
||||
false,
|
||||
DEBUG_SOLID_VERTEX_SIZE,
|
||||
0,
|
||||
0);
|
||||
gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, index_buffer.gl_buffer);
|
||||
}
|
||||
|
||||
DebugSolidVertexArray { gl_vertex_array, vertex_buffer, index_buffer }
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for DebugSolidVertexArray {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
gl::DeleteVertexArrays(1, &mut self.gl_vertex_array);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct DebugFontProgram {
|
||||
program: Program,
|
||||
framebuffer_size_uniform: Uniform,
|
||||
font_texture_size_uniform: Uniform,
|
||||
font_texture_uniform: Uniform,
|
||||
}
|
||||
|
||||
impl DebugFontProgram {
|
||||
fn new() -> DebugFontProgram {
|
||||
let program = Program::new("debug_font");
|
||||
let framebuffer_size_uniform = Uniform::new(&program, "FramebufferSize");
|
||||
let font_texture_size_uniform = Uniform::new(&program, "FontTextureSize");
|
||||
let font_texture_uniform = Uniform::new(&program, "FontTexture");
|
||||
DebugFontProgram {
|
||||
program,
|
||||
framebuffer_size_uniform,
|
||||
font_texture_size_uniform,
|
||||
font_texture_uniform,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct DebugSolidProgram {
|
||||
program: Program,
|
||||
framebuffer_size_uniform: Uniform,
|
||||
color_uniform: Uniform,
|
||||
}
|
||||
|
||||
impl DebugSolidProgram {
|
||||
fn new() -> DebugSolidProgram {
|
||||
let program = Program::new("debug_solid");
|
||||
let framebuffer_size_uniform = Uniform::new(&program, "FramebufferSize");
|
||||
let color_uniform = Uniform::new(&program, "Color");
|
||||
DebugSolidProgram { program, framebuffer_size_uniform, color_uniform }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
#[allow(dead_code)]
|
||||
struct DebugFontVertex {
|
||||
position_x: i16,
|
||||
position_y: i16,
|
||||
tex_coord_x: u16,
|
||||
tex_coord_y: u16,
|
||||
}
|
||||
|
||||
impl DebugFontVertex {
|
||||
fn new(position: Point2D<i16>, tex_coord: Point2D<u16>) -> DebugFontVertex {
|
||||
DebugFontVertex {
|
||||
position_x: position.x,
|
||||
position_y: position.y,
|
||||
tex_coord_x: tex_coord.x,
|
||||
tex_coord_y: tex_coord.y,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
#[allow(dead_code)]
|
||||
struct DebugSolidVertex {
|
||||
position_x: i16,
|
||||
position_y: i16,
|
||||
}
|
||||
|
||||
impl DebugSolidVertex {
|
||||
fn new(position: Point2D<i16>) -> DebugSolidVertex {
|
||||
DebugSolidVertex { position_x: position.x, position_y: position.y }
|
||||
}
|
||||
}
|
||||
|
||||
fn duration_ms(time: Duration) -> f64 {
|
||||
time.as_secs() as f64 * 1000.0 + time.subsec_nanos() as f64 / 1000000.0
|
||||
}
|
|
@ -0,0 +1,375 @@
|
|||
// pathfinder/demo3/src/device.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.
|
||||
|
||||
//! Minimal abstractions over GPU device capabilities.
|
||||
|
||||
use euclid::Size2D;
|
||||
use gl::types::{GLchar, GLint, GLsizei, GLsizeiptr, GLuint, GLvoid};
|
||||
use std::ffi::CString;
|
||||
use std::fs::File;
|
||||
use std::io::Read;
|
||||
use std::mem;
|
||||
use std::ptr;
|
||||
|
||||
pub struct VertexAttr {
|
||||
attr: GLuint,
|
||||
}
|
||||
|
||||
impl VertexAttr {
|
||||
pub fn new(program: &Program, name: &str) -> VertexAttr {
|
||||
let name = CString::new(format!("a{}", name)).unwrap();
|
||||
let attr = unsafe {
|
||||
gl::GetAttribLocation(program.gl_program, name.as_ptr() as *const GLchar) as GLuint
|
||||
};
|
||||
VertexAttr { attr }
|
||||
}
|
||||
|
||||
pub fn configure_float(&self,
|
||||
size: GLint,
|
||||
gl_type: GLuint,
|
||||
normalized: bool,
|
||||
stride: GLsizei,
|
||||
offset: usize,
|
||||
divisor: GLuint) {
|
||||
unsafe {
|
||||
gl::VertexAttribPointer(self.attr,
|
||||
size,
|
||||
gl_type,
|
||||
if normalized { gl::TRUE } else { gl::FALSE },
|
||||
stride,
|
||||
offset as *const GLvoid);
|
||||
gl::VertexAttribDivisor(self.attr, divisor);
|
||||
gl::EnableVertexAttribArray(self.attr);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn configure_int(&self,
|
||||
size: GLint,
|
||||
gl_type: GLuint,
|
||||
stride: GLsizei,
|
||||
offset: usize,
|
||||
divisor: GLuint) {
|
||||
unsafe {
|
||||
gl::VertexAttribIPointer(self.attr, size, gl_type, stride, offset as *const GLvoid);
|
||||
gl::VertexAttribDivisor(self.attr, divisor);
|
||||
gl::EnableVertexAttribArray(self.attr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Framebuffer {
|
||||
pub gl_framebuffer: GLuint,
|
||||
pub texture: Texture,
|
||||
}
|
||||
|
||||
impl Framebuffer {
|
||||
pub fn new(size: &Size2D<u32>) -> Framebuffer {
|
||||
let texture = Texture::new_r16f(size);
|
||||
let mut gl_framebuffer = 0;
|
||||
unsafe {
|
||||
gl::GenFramebuffers(1, &mut gl_framebuffer);
|
||||
assert_eq!(gl::GetError(), gl::NO_ERROR);
|
||||
gl::BindFramebuffer(gl::FRAMEBUFFER, gl_framebuffer);
|
||||
texture.bind(0);
|
||||
gl::FramebufferTexture2D(gl::FRAMEBUFFER,
|
||||
gl::COLOR_ATTACHMENT0,
|
||||
gl::TEXTURE_2D,
|
||||
texture.gl_texture,
|
||||
0);
|
||||
assert_eq!(gl::CheckFramebufferStatus(gl::FRAMEBUFFER), gl::FRAMEBUFFER_COMPLETE);
|
||||
}
|
||||
Framebuffer { gl_framebuffer, texture }
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Framebuffer {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
gl::DeleteFramebuffers(1, &mut self.gl_framebuffer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Buffer {
|
||||
pub gl_buffer: GLuint,
|
||||
}
|
||||
|
||||
impl Buffer {
|
||||
pub fn new() -> Buffer {
|
||||
unsafe {
|
||||
let mut gl_buffer = 0;
|
||||
gl::GenBuffers(1, &mut gl_buffer);
|
||||
Buffer { gl_buffer }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn upload<T>(&self, data: &[T], target: BufferTarget, mode: BufferUploadMode) {
|
||||
let target = match target {
|
||||
BufferTarget::Vertex => gl::ARRAY_BUFFER,
|
||||
BufferTarget::Index => gl::ELEMENT_ARRAY_BUFFER,
|
||||
};
|
||||
let mode = match mode {
|
||||
BufferUploadMode::Static => gl::STATIC_DRAW,
|
||||
BufferUploadMode::Dynamic => gl::DYNAMIC_DRAW,
|
||||
};
|
||||
unsafe {
|
||||
gl::BindBuffer(target, self.gl_buffer);
|
||||
gl::BufferData(target,
|
||||
(data.len() * mem::size_of::<T>()) as GLsizeiptr,
|
||||
data.as_ptr() as *const GLvoid,
|
||||
mode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Buffer {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
gl::DeleteBuffers(1, &mut self.gl_buffer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum BufferTarget {
|
||||
Vertex,
|
||||
Index,
|
||||
}
|
||||
|
||||
pub enum BufferUploadMode {
|
||||
Static,
|
||||
Dynamic,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Uniform {
|
||||
pub location: GLint,
|
||||
}
|
||||
|
||||
impl Uniform {
|
||||
pub fn new(program: &Program, name: &str) -> Uniform {
|
||||
let name = CString::new(format!("u{}", name)).unwrap();
|
||||
let location = unsafe {
|
||||
gl::GetUniformLocation(program.gl_program, name.as_ptr() as *const GLchar)
|
||||
};
|
||||
Uniform { location }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Program {
|
||||
pub gl_program: GLuint,
|
||||
#[allow(dead_code)]
|
||||
vertex_shader: Shader,
|
||||
#[allow(dead_code)]
|
||||
fragment_shader: Shader,
|
||||
}
|
||||
|
||||
impl Program {
|
||||
pub fn new(name: &'static str) -> Program {
|
||||
let vertex_shader = Shader::new(name, ShaderKind::Vertex);
|
||||
let fragment_shader = Shader::new(name, ShaderKind::Fragment);
|
||||
|
||||
let gl_program;
|
||||
unsafe {
|
||||
gl_program = gl::CreateProgram();
|
||||
gl::AttachShader(gl_program, vertex_shader.gl_shader);
|
||||
gl::AttachShader(gl_program, fragment_shader.gl_shader);
|
||||
gl::LinkProgram(gl_program);
|
||||
|
||||
let mut link_status = 0;
|
||||
gl::GetProgramiv(gl_program, gl::LINK_STATUS, &mut link_status);
|
||||
if link_status != gl::TRUE as GLint {
|
||||
let mut info_log_length = 0;
|
||||
gl::GetProgramiv(gl_program, gl::INFO_LOG_LENGTH, &mut info_log_length);
|
||||
let mut info_log = vec![0; info_log_length as usize];
|
||||
gl::GetProgramInfoLog(gl_program,
|
||||
info_log.len() as GLint,
|
||||
ptr::null_mut(),
|
||||
info_log.as_mut_ptr() as *mut GLchar);
|
||||
eprintln!("Program info log:\n{}", String::from_utf8_lossy(&info_log));
|
||||
panic!("Program '{}' linking failed", name);
|
||||
}
|
||||
}
|
||||
|
||||
Program { gl_program, vertex_shader, fragment_shader }
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Program {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
gl::DeleteProgram(self.gl_program)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Shader {
|
||||
gl_shader: GLuint,
|
||||
}
|
||||
|
||||
impl Shader {
|
||||
fn new(name: &str, kind: ShaderKind) -> Shader {
|
||||
let suffix = match kind { ShaderKind::Vertex => 'v', ShaderKind::Fragment => 'f' };
|
||||
// FIXME(pcwalton): Put the shaders somewhere else. Maybe compile them in?
|
||||
let path = format!("shaders/{}.{}s.glsl", name, suffix);
|
||||
let mut source = vec![];
|
||||
File::open(&path).unwrap().read_to_end(&mut source).unwrap();
|
||||
unsafe {
|
||||
let gl_shader_kind = match kind {
|
||||
ShaderKind::Vertex => gl::VERTEX_SHADER,
|
||||
ShaderKind::Fragment => gl::FRAGMENT_SHADER,
|
||||
};
|
||||
let gl_shader = gl::CreateShader(gl_shader_kind);
|
||||
gl::ShaderSource(gl_shader,
|
||||
1,
|
||||
[source.as_ptr() as *const GLchar].as_ptr(),
|
||||
[source.len() as GLint].as_ptr());
|
||||
gl::CompileShader(gl_shader);
|
||||
|
||||
let mut compile_status = 0;
|
||||
gl::GetShaderiv(gl_shader, gl::COMPILE_STATUS, &mut compile_status);
|
||||
if compile_status != gl::TRUE as GLint {
|
||||
let mut info_log_length = 0;
|
||||
gl::GetShaderiv(gl_shader, gl::INFO_LOG_LENGTH, &mut info_log_length);
|
||||
let mut info_log = vec![0; info_log_length as usize];
|
||||
gl::GetShaderInfoLog(gl_shader,
|
||||
info_log.len() as GLint,
|
||||
ptr::null_mut(),
|
||||
info_log.as_mut_ptr() as *mut GLchar);
|
||||
eprintln!("Shader info log:\n{}", String::from_utf8_lossy(&info_log));
|
||||
panic!("{:?} shader '{}' compilation failed", kind, name);
|
||||
}
|
||||
|
||||
Shader { gl_shader }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Shader {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
gl::DeleteShader(self.gl_shader)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
enum ShaderKind {
|
||||
Vertex,
|
||||
Fragment,
|
||||
}
|
||||
|
||||
pub struct Texture {
|
||||
gl_texture: GLuint,
|
||||
pub size: Size2D<u32>,
|
||||
}
|
||||
|
||||
impl Texture {
|
||||
fn new_r16f(size: &Size2D<u32>) -> Texture {
|
||||
let mut texture = Texture { gl_texture: 0, size: *size };
|
||||
unsafe {
|
||||
gl::GenTextures(1, &mut texture.gl_texture);
|
||||
texture.bind(0);
|
||||
gl::TexImage2D(gl::TEXTURE_2D,
|
||||
0,
|
||||
gl::R16F as GLint,
|
||||
size.width as GLsizei,
|
||||
size.height as GLsizei,
|
||||
0,
|
||||
gl::RED,
|
||||
gl::HALF_FLOAT,
|
||||
ptr::null());
|
||||
}
|
||||
|
||||
texture.set_parameters();
|
||||
texture
|
||||
}
|
||||
|
||||
pub fn new_rgba(size: &Size2D<u32>) -> Texture {
|
||||
let mut texture = Texture { gl_texture: 0, size: *size };
|
||||
unsafe {
|
||||
gl::GenTextures(1, &mut texture.gl_texture);
|
||||
texture.bind(0);
|
||||
gl::TexImage2D(gl::TEXTURE_2D,
|
||||
0,
|
||||
gl::RGBA as GLint,
|
||||
size.width as GLsizei,
|
||||
size.height as GLsizei,
|
||||
0,
|
||||
gl::RGBA,
|
||||
gl::UNSIGNED_BYTE,
|
||||
ptr::null());
|
||||
}
|
||||
|
||||
texture.set_parameters();
|
||||
texture
|
||||
}
|
||||
|
||||
pub fn from_png(name: &str) -> Texture {
|
||||
let path = format!("resources/textures/{}.png", name);
|
||||
let image = image::open(&path).unwrap().to_luma();
|
||||
|
||||
let mut texture = Texture {
|
||||
gl_texture: 0,
|
||||
size: Size2D::new(image.width(), image.height()),
|
||||
};
|
||||
|
||||
unsafe {
|
||||
gl::GenTextures(1, &mut texture.gl_texture);
|
||||
texture.bind(0);
|
||||
gl::TexImage2D(gl::TEXTURE_2D,
|
||||
0,
|
||||
gl::RED as GLint,
|
||||
image.width() as GLsizei,
|
||||
image.height() as GLsizei,
|
||||
0,
|
||||
gl::RED,
|
||||
gl::UNSIGNED_BYTE,
|
||||
image.as_ptr() as *const GLvoid);
|
||||
}
|
||||
|
||||
texture.set_parameters();
|
||||
texture
|
||||
}
|
||||
|
||||
pub fn bind(&self, unit: u32) {
|
||||
unsafe {
|
||||
gl::ActiveTexture(gl::TEXTURE0 + unit);
|
||||
gl::BindTexture(gl::TEXTURE_2D, self.gl_texture);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn upload_rgba(&self, size: &Size2D<u32>, data: &[u8]) {
|
||||
assert!(data.len() >= size.width as usize * size.height as usize * 4);
|
||||
unsafe {
|
||||
self.bind(0);
|
||||
gl::TexImage2D(gl::TEXTURE_2D,
|
||||
0,
|
||||
gl::RGBA as GLint,
|
||||
size.width as GLsizei,
|
||||
size.height as GLsizei,
|
||||
0,
|
||||
gl::RGBA,
|
||||
gl::UNSIGNED_BYTE,
|
||||
data.as_ptr() as *const GLvoid);
|
||||
}
|
||||
|
||||
self.set_parameters();
|
||||
}
|
||||
|
||||
fn set_parameters(&self) {
|
||||
self.bind(0);
|
||||
unsafe {
|
||||
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::LINEAR as GLint);
|
||||
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::LINEAR as GLint);
|
||||
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_S, gl::CLAMP_TO_EDGE as GLint);
|
||||
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_T, gl::CLAMP_TO_EDGE as GLint);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -8,12 +8,17 @@
|
|||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
|
||||
use crate::debug_text::DebugRenderer;
|
||||
use crate::device::{Buffer, BufferTarget, BufferUploadMode, Framebuffer, Program, Texture};
|
||||
use crate::device::{Uniform, VertexAttr};
|
||||
use clap::{App, Arg};
|
||||
use euclid::{Point2D, Rect, Size2D};
|
||||
use gl::types::{GLchar, GLfloat, GLint, GLsizei, GLsizeiptr, GLuint, GLvoid};
|
||||
use gl::types::{GLfloat, GLint, GLuint};
|
||||
use jemallocator;
|
||||
use pathfinder_geometry::point::{Point2DF32, Point4DF32};
|
||||
use pathfinder_geometry::transform::Transform2DF32;
|
||||
use pathfinder_geometry::point::Point4DF32;
|
||||
use pathfinder_geometry::transform3d::{Perspective, Transform3DF32};
|
||||
use pathfinder_renderer::builder::SceneBuilder;
|
||||
use pathfinder_renderer::gpu_data::{Batch, BuiltScene, SolidTileScenePrimitive};
|
||||
|
@ -27,14 +32,13 @@ 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;
|
||||
use std::mem;
|
||||
use std::time::Instant;
|
||||
use std::path::PathBuf;
|
||||
use std::ptr;
|
||||
use usvg::{Options as UsvgOptions, Tree};
|
||||
|
||||
mod debug_text;
|
||||
mod device;
|
||||
|
||||
#[global_allocator]
|
||||
static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
|
||||
|
||||
|
@ -88,8 +92,9 @@ fn main() {
|
|||
let (mut camera_yaw, mut camera_pitch) = (0.0, 0.0);
|
||||
|
||||
let window_size = Size2D::new(drawable_width, drawable_height);
|
||||
renderer.debug_renderer.set_framebuffer_size(&window_size);
|
||||
|
||||
let mut base_scene = load_scene(&options, &window_size);
|
||||
let base_scene = load_scene(&options, &window_size);
|
||||
let mut dump_transformed_scene = false;
|
||||
|
||||
let mut events = vec![];
|
||||
|
@ -97,6 +102,8 @@ fn main() {
|
|||
while !exit {
|
||||
let mut scene = base_scene.clone();
|
||||
|
||||
let mut start_time = Instant::now();
|
||||
|
||||
if options.run_in_3d {
|
||||
let rotation = Transform3DF32::from_rotation(-camera_yaw, -camera_pitch, 0.0);
|
||||
camera_position = camera_position + rotation.transform_point(camera_velocity);
|
||||
|
@ -114,22 +121,38 @@ fn main() {
|
|||
transform.post_mul(&Transform3DF32::from_scale(1.0 / 800.0, 1.0 / 800.0, 1.0));
|
||||
|
||||
let perspective = Perspective::new(&transform, &window_size);
|
||||
scene.apply_perspective(&perspective);
|
||||
|
||||
match options.jobs {
|
||||
Some(1) => scene.apply_perspective_sequentially(&perspective),
|
||||
_ => scene.apply_perspective(&perspective),
|
||||
}
|
||||
} else {
|
||||
scene.prepare();
|
||||
}
|
||||
|
||||
let elapsed_prepare_time = Instant::now() - start_time;
|
||||
|
||||
if dump_transformed_scene {
|
||||
println!("{:?}", scene);
|
||||
dump_transformed_scene = false;
|
||||
}
|
||||
|
||||
// Tile the scene.
|
||||
|
||||
start_time = Instant::now();
|
||||
|
||||
let built_scene = build_scene(&scene, &options);
|
||||
|
||||
let elapsed_tile_time = Instant::now() - start_time;
|
||||
|
||||
// Draw the scene.
|
||||
|
||||
unsafe {
|
||||
gl::ClearColor(0.7, 0.7, 0.7, 1.0);
|
||||
gl::Clear(gl::COLOR_BUFFER_BIT);
|
||||
renderer.render_scene(&built_scene);
|
||||
|
||||
renderer.debug_renderer.draw(elapsed_prepare_time, elapsed_tile_time);
|
||||
}
|
||||
|
||||
window.gl_swap_window();
|
||||
|
@ -288,6 +311,8 @@ struct Renderer {
|
|||
mask_framebuffer: Framebuffer,
|
||||
fill_colors_texture: Texture,
|
||||
|
||||
debug_renderer: DebugRenderer,
|
||||
|
||||
main_framebuffer_size: Size2D<u32>,
|
||||
}
|
||||
|
||||
|
@ -300,7 +325,9 @@ impl Renderer {
|
|||
let area_lut_texture = Texture::from_png("area-lut");
|
||||
|
||||
let quad_vertex_positions_buffer = Buffer::new();
|
||||
quad_vertex_positions_buffer.upload(&QUAD_VERTEX_POSITIONS, BufferUploadMode::Static);
|
||||
quad_vertex_positions_buffer.upload(&QUAD_VERTEX_POSITIONS,
|
||||
BufferTarget::Vertex,
|
||||
BufferUploadMode::Static);
|
||||
|
||||
let fill_vertex_array = FillVertexArray::new(&fill_program, &quad_vertex_positions_buffer);
|
||||
let mask_tile_vertex_array = MaskTileVertexArray::new(&mask_tile_program,
|
||||
|
@ -314,6 +341,8 @@ impl Renderer {
|
|||
let fill_colors_texture = Texture::new_rgba(&Size2D::new(FILL_COLORS_TEXTURE_WIDTH,
|
||||
FILL_COLORS_TEXTURE_HEIGHT));
|
||||
|
||||
let debug_renderer = DebugRenderer::new(main_framebuffer_size);
|
||||
|
||||
Renderer {
|
||||
fill_program,
|
||||
solid_tile_program,
|
||||
|
@ -326,6 +355,8 @@ impl Renderer {
|
|||
mask_framebuffer,
|
||||
fill_colors_texture,
|
||||
|
||||
debug_renderer,
|
||||
|
||||
main_framebuffer_size: *main_framebuffer_size,
|
||||
}
|
||||
}
|
||||
|
@ -356,13 +387,18 @@ impl Renderer {
|
|||
}
|
||||
|
||||
fn upload_solid_tiles(&mut self, solid_tiles: &[SolidTileScenePrimitive]) {
|
||||
self.solid_tile_vertex_array.vertex_buffer.upload(solid_tiles, BufferUploadMode::Dynamic);
|
||||
self.solid_tile_vertex_array
|
||||
.vertex_buffer
|
||||
.upload(solid_tiles, BufferTarget::Vertex, BufferUploadMode::Dynamic);
|
||||
}
|
||||
|
||||
fn upload_batch(&mut self, batch: &Batch) {
|
||||
self.fill_vertex_array.vertex_buffer.upload(&batch.fills, BufferUploadMode::Dynamic);
|
||||
self.mask_tile_vertex_array.vertex_buffer.upload(&batch.mask_tiles,
|
||||
BufferUploadMode::Dynamic);
|
||||
self.fill_vertex_array
|
||||
.vertex_buffer
|
||||
.upload(&batch.fills, BufferTarget::Vertex, BufferUploadMode::Dynamic);
|
||||
self.mask_tile_vertex_array
|
||||
.vertex_buffer
|
||||
.upload(&batch.mask_tiles, BufferTarget::Vertex, BufferUploadMode::Dynamic);
|
||||
}
|
||||
|
||||
fn draw_batch_fills(&mut self, batch: &Batch) {
|
||||
|
@ -577,126 +613,6 @@ impl Drop for SolidTileVertexArray {
|
|||
}
|
||||
}
|
||||
|
||||
struct VertexAttr {
|
||||
attr: GLuint,
|
||||
}
|
||||
|
||||
impl VertexAttr {
|
||||
fn new(program: &Program, name: &str) -> VertexAttr {
|
||||
let name = CString::new(format!("a{}", name)).unwrap();
|
||||
let attr = unsafe {
|
||||
gl::GetAttribLocation(program.gl_program, name.as_ptr() as *const GLchar) as GLuint
|
||||
};
|
||||
VertexAttr { attr }
|
||||
}
|
||||
|
||||
fn configure_float(&self,
|
||||
size: GLint,
|
||||
gl_type: GLuint,
|
||||
normalized: bool,
|
||||
stride: GLsizei,
|
||||
offset: usize,
|
||||
divisor: GLuint) {
|
||||
unsafe {
|
||||
gl::VertexAttribPointer(self.attr,
|
||||
size,
|
||||
gl_type,
|
||||
if normalized { gl::TRUE } else { gl::FALSE },
|
||||
stride,
|
||||
offset as *const GLvoid);
|
||||
gl::VertexAttribDivisor(self.attr, divisor);
|
||||
gl::EnableVertexAttribArray(self.attr);
|
||||
}
|
||||
}
|
||||
|
||||
fn configure_int(&self,
|
||||
size: GLint,
|
||||
gl_type: GLuint,
|
||||
stride: GLsizei,
|
||||
offset: usize,
|
||||
divisor: GLuint) {
|
||||
unsafe {
|
||||
gl::VertexAttribIPointer(self.attr, size, gl_type, stride, offset as *const GLvoid);
|
||||
gl::VertexAttribDivisor(self.attr, divisor);
|
||||
gl::EnableVertexAttribArray(self.attr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Framebuffer {
|
||||
gl_framebuffer: GLuint,
|
||||
texture: Texture,
|
||||
}
|
||||
|
||||
impl Framebuffer {
|
||||
fn new(size: &Size2D<u32>) -> Framebuffer {
|
||||
let texture = Texture::new_r16f(size);
|
||||
let mut gl_framebuffer = 0;
|
||||
unsafe {
|
||||
gl::GenFramebuffers(1, &mut gl_framebuffer);
|
||||
assert_eq!(gl::GetError(), gl::NO_ERROR);
|
||||
gl::BindFramebuffer(gl::FRAMEBUFFER, gl_framebuffer);
|
||||
texture.bind(0);
|
||||
gl::FramebufferTexture2D(gl::FRAMEBUFFER,
|
||||
gl::COLOR_ATTACHMENT0,
|
||||
gl::TEXTURE_2D,
|
||||
texture.gl_texture,
|
||||
0);
|
||||
assert_eq!(gl::CheckFramebufferStatus(gl::FRAMEBUFFER), gl::FRAMEBUFFER_COMPLETE);
|
||||
}
|
||||
Framebuffer { gl_framebuffer, texture }
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Framebuffer {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
gl::DeleteFramebuffers(1, &mut self.gl_framebuffer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Buffer {
|
||||
gl_buffer: GLuint,
|
||||
}
|
||||
|
||||
impl Buffer {
|
||||
fn new() -> Buffer {
|
||||
unsafe {
|
||||
let mut gl_buffer = 0;
|
||||
gl::GenBuffers(1, &mut gl_buffer);
|
||||
Buffer { gl_buffer }
|
||||
}
|
||||
}
|
||||
|
||||
fn upload<T>(&self, data: &[T], mode: BufferUploadMode) {
|
||||
let mode = match mode {
|
||||
BufferUploadMode::Static => gl::STATIC_DRAW,
|
||||
BufferUploadMode::Dynamic => gl::DYNAMIC_DRAW,
|
||||
};
|
||||
unsafe {
|
||||
gl::BindBuffer(gl::ARRAY_BUFFER, self.gl_buffer);
|
||||
gl::BufferData(gl::ARRAY_BUFFER,
|
||||
(data.len() * mem::size_of::<T>()) as GLsizeiptr,
|
||||
data.as_ptr() as *const GLvoid,
|
||||
mode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Buffer {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
gl::DeleteBuffers(1, &mut self.gl_buffer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum BufferUploadMode {
|
||||
Static,
|
||||
Dynamic,
|
||||
}
|
||||
|
||||
struct FillProgram {
|
||||
program: Program,
|
||||
framebuffer_size_uniform: Uniform,
|
||||
|
@ -775,225 +691,3 @@ impl MaskTileProgram {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Uniform {
|
||||
location: GLint,
|
||||
}
|
||||
|
||||
impl Uniform {
|
||||
fn new(program: &Program, name: &str) -> Uniform {
|
||||
let name = CString::new(format!("u{}", name)).unwrap();
|
||||
let location = unsafe {
|
||||
gl::GetUniformLocation(program.gl_program, name.as_ptr() as *const GLchar)
|
||||
};
|
||||
Uniform { location }
|
||||
}
|
||||
}
|
||||
|
||||
struct Program {
|
||||
gl_program: GLuint,
|
||||
#[allow(dead_code)]
|
||||
vertex_shader: Shader,
|
||||
#[allow(dead_code)]
|
||||
fragment_shader: Shader,
|
||||
}
|
||||
|
||||
impl Program {
|
||||
fn new(name: &'static str) -> Program {
|
||||
let vertex_shader = Shader::new(name, ShaderKind::Vertex);
|
||||
let fragment_shader = Shader::new(name, ShaderKind::Fragment);
|
||||
|
||||
let gl_program;
|
||||
unsafe {
|
||||
gl_program = gl::CreateProgram();
|
||||
gl::AttachShader(gl_program, vertex_shader.gl_shader);
|
||||
gl::AttachShader(gl_program, fragment_shader.gl_shader);
|
||||
gl::LinkProgram(gl_program);
|
||||
|
||||
let mut link_status = 0;
|
||||
gl::GetProgramiv(gl_program, gl::LINK_STATUS, &mut link_status);
|
||||
if link_status != gl::TRUE as GLint {
|
||||
let mut info_log_length = 0;
|
||||
gl::GetProgramiv(gl_program, gl::INFO_LOG_LENGTH, &mut info_log_length);
|
||||
let mut info_log = vec![0; info_log_length as usize];
|
||||
gl::GetProgramInfoLog(gl_program,
|
||||
info_log.len() as GLint,
|
||||
ptr::null_mut(),
|
||||
info_log.as_mut_ptr() as *mut GLchar);
|
||||
eprintln!("Program info log:\n{}", String::from_utf8_lossy(&info_log));
|
||||
panic!("Program '{}' linking failed", name);
|
||||
}
|
||||
}
|
||||
|
||||
Program { gl_program, vertex_shader, fragment_shader }
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Program {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
gl::DeleteProgram(self.gl_program)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Shader {
|
||||
gl_shader: GLuint,
|
||||
}
|
||||
|
||||
impl Shader {
|
||||
fn new(name: &str, kind: ShaderKind) -> Shader {
|
||||
let suffix = match kind { ShaderKind::Vertex => 'v', ShaderKind::Fragment => 'f' };
|
||||
// FIXME(pcwalton): Put the shaders somewhere else. Maybe compile them in?
|
||||
let path = format!("shaders/{}.{}s.glsl", name, suffix);
|
||||
let mut source = vec![];
|
||||
File::open(&path).unwrap().read_to_end(&mut source).unwrap();
|
||||
unsafe {
|
||||
let gl_shader_kind = match kind {
|
||||
ShaderKind::Vertex => gl::VERTEX_SHADER,
|
||||
ShaderKind::Fragment => gl::FRAGMENT_SHADER,
|
||||
};
|
||||
let gl_shader = gl::CreateShader(gl_shader_kind);
|
||||
gl::ShaderSource(gl_shader,
|
||||
1,
|
||||
[source.as_ptr() as *const GLchar].as_ptr(),
|
||||
[source.len() as GLint].as_ptr());
|
||||
gl::CompileShader(gl_shader);
|
||||
|
||||
let mut compile_status = 0;
|
||||
gl::GetShaderiv(gl_shader, gl::COMPILE_STATUS, &mut compile_status);
|
||||
if compile_status != gl::TRUE as GLint {
|
||||
let mut info_log_length = 0;
|
||||
gl::GetShaderiv(gl_shader, gl::INFO_LOG_LENGTH, &mut info_log_length);
|
||||
let mut info_log = vec![0; info_log_length as usize];
|
||||
gl::GetShaderInfoLog(gl_shader,
|
||||
info_log.len() as GLint,
|
||||
ptr::null_mut(),
|
||||
info_log.as_mut_ptr() as *mut GLchar);
|
||||
eprintln!("Shader info log:\n{}", String::from_utf8_lossy(&info_log));
|
||||
panic!("{:?} shader '{}' compilation failed", kind, name);
|
||||
}
|
||||
|
||||
Shader { gl_shader }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Shader {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
gl::DeleteShader(self.gl_shader)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
enum ShaderKind {
|
||||
Vertex,
|
||||
Fragment,
|
||||
}
|
||||
|
||||
struct Texture {
|
||||
gl_texture: GLuint,
|
||||
}
|
||||
|
||||
impl Texture {
|
||||
fn new_r16f(size: &Size2D<u32>) -> Texture {
|
||||
let mut texture = Texture { gl_texture: 0 };
|
||||
unsafe {
|
||||
gl::GenTextures(1, &mut texture.gl_texture);
|
||||
texture.bind(0);
|
||||
gl::TexImage2D(gl::TEXTURE_2D,
|
||||
0,
|
||||
gl::R16F as GLint,
|
||||
size.width as GLsizei,
|
||||
size.height as GLsizei,
|
||||
0,
|
||||
gl::RED,
|
||||
gl::HALF_FLOAT,
|
||||
ptr::null());
|
||||
}
|
||||
|
||||
texture.set_parameters();
|
||||
texture
|
||||
}
|
||||
|
||||
fn new_rgba(size: &Size2D<u32>) -> Texture {
|
||||
let mut texture = Texture { gl_texture: 0 };
|
||||
unsafe {
|
||||
gl::GenTextures(1, &mut texture.gl_texture);
|
||||
texture.bind(0);
|
||||
gl::TexImage2D(gl::TEXTURE_2D,
|
||||
0,
|
||||
gl::RGBA as GLint,
|
||||
size.width as GLsizei,
|
||||
size.height as GLsizei,
|
||||
0,
|
||||
gl::RGBA,
|
||||
gl::UNSIGNED_BYTE,
|
||||
ptr::null());
|
||||
}
|
||||
|
||||
texture.set_parameters();
|
||||
texture
|
||||
}
|
||||
|
||||
fn from_png(name: &str) -> Texture {
|
||||
let path = format!("textures/{}.png", name);
|
||||
let image = image::open(&path).unwrap().to_luma();
|
||||
|
||||
let mut texture = Texture { gl_texture: 0 };
|
||||
unsafe {
|
||||
gl::GenTextures(1, &mut texture.gl_texture);
|
||||
texture.bind(0);
|
||||
gl::TexImage2D(gl::TEXTURE_2D,
|
||||
0,
|
||||
gl::RED as GLint,
|
||||
image.width() as GLsizei,
|
||||
image.height() as GLsizei,
|
||||
0,
|
||||
gl::RED,
|
||||
gl::UNSIGNED_BYTE,
|
||||
image.as_ptr() as *const GLvoid);
|
||||
}
|
||||
|
||||
texture.set_parameters();
|
||||
texture
|
||||
}
|
||||
|
||||
fn bind(&self, unit: u32) {
|
||||
unsafe {
|
||||
gl::ActiveTexture(gl::TEXTURE0 + unit);
|
||||
gl::BindTexture(gl::TEXTURE_2D, self.gl_texture);
|
||||
}
|
||||
}
|
||||
|
||||
fn upload_rgba(&self, size: &Size2D<u32>, data: &[u8]) {
|
||||
assert!(data.len() >= size.width as usize * size.height as usize * 4);
|
||||
unsafe {
|
||||
self.bind(0);
|
||||
gl::TexImage2D(gl::TEXTURE_2D,
|
||||
0,
|
||||
gl::RGBA as GLint,
|
||||
size.width as GLsizei,
|
||||
size.height as GLsizei,
|
||||
0,
|
||||
gl::RGBA,
|
||||
gl::UNSIGNED_BYTE,
|
||||
data.as_ptr() as *const GLvoid);
|
||||
}
|
||||
|
||||
self.set_parameters();
|
||||
}
|
||||
|
||||
fn set_parameters(&self) {
|
||||
self.bind(0);
|
||||
unsafe {
|
||||
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::LINEAR as GLint);
|
||||
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::LINEAR as GLint);
|
||||
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_S, gl::CLAMP_TO_EDGE as GLint);
|
||||
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_T, gl::CLAMP_TO_EDGE as GLint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,10 +18,11 @@ use euclid::Rect;
|
|||
use hashbrown::HashMap;
|
||||
use pathfinder_geometry::clip::PolygonClipper3D;
|
||||
use pathfinder_geometry::outline::Outline;
|
||||
use pathfinder_geometry::point::Point3DF32;
|
||||
use pathfinder_geometry::point::{Point2DF32, Point3DF32};
|
||||
use pathfinder_geometry::transform3d::Perspective;
|
||||
use pathfinder_geometry::transform::Transform2DF32;
|
||||
use rayon::iter::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator};
|
||||
use rayon::iter::{IndexedParallelIterator, IntoParallelRefIterator};
|
||||
use rayon::iter::{IntoParallelRefMutIterator, ParallelIterator};
|
||||
use std::fmt::{self, Debug, Formatter};
|
||||
|
||||
#[derive(Clone)]
|
||||
|
@ -102,6 +103,19 @@ impl Scene {
|
|||
.collect()
|
||||
}
|
||||
|
||||
fn update_bounds(&mut self) {
|
||||
let mut bounds = Rect::zero();
|
||||
for (object_index, object) in self.objects.iter_mut().enumerate() {
|
||||
if object_index == 0 {
|
||||
bounds = *object.outline.bounds();
|
||||
} else {
|
||||
bounds = bounds.union(object.outline.bounds());
|
||||
}
|
||||
}
|
||||
|
||||
self.bounds = bounds;
|
||||
}
|
||||
|
||||
pub fn prepare(&mut self) {
|
||||
for object in &mut self.objects {
|
||||
object.outline.make_monotonic();
|
||||
|
@ -109,46 +123,45 @@ impl Scene {
|
|||
}
|
||||
|
||||
pub fn transform(&mut self, transform: &Transform2DF32) {
|
||||
let mut bounds = Rect::zero();
|
||||
for (object_index, object) in self.objects.iter_mut().enumerate() {
|
||||
for object in &mut self.objects {
|
||||
object.outline.transform(transform);
|
||||
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;
|
||||
self.update_bounds();
|
||||
}
|
||||
|
||||
pub fn apply_perspective(&mut self, perspective: &Perspective) {
|
||||
fn apply_perspective_to_quad(&self, perspective: &Perspective) -> Vec<Point2DF32> {
|
||||
let quad = self.clip_bounding_quad_with_perspective(perspective);
|
||||
//println!("bounds={:?} PRE-transform quad={:?}", self.bounds, quad);
|
||||
let inverse_transform = perspective.transform.inverse();
|
||||
let quad: Vec<_> = quad.into_iter().map(|point| {
|
||||
inverse_transform.transform_point_3d(point).to_2d()
|
||||
}).collect();
|
||||
//println!("bounds={:?} POST-transform quad={:?}", self.bounds, quad);
|
||||
quad.into_iter()
|
||||
.map(|point| inverse_transform.transform_point_3d(point).to_2d())
|
||||
.collect()
|
||||
}
|
||||
|
||||
let mut bounds = Rect::zero();
|
||||
for (object_index, object) in self.objects.iter_mut().enumerate() {
|
||||
pub fn apply_perspective_sequentially(&mut self, perspective: &Perspective) {
|
||||
let quad = self.apply_perspective_to_quad(perspective);
|
||||
|
||||
for object in &mut self.objects {
|
||||
object.outline.clip_against_polygon(&quad);
|
||||
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;
|
||||
self.update_bounds();
|
||||
}
|
||||
|
||||
pub fn apply_perspective(&mut self, perspective: &Perspective) {
|
||||
let quad = self.apply_perspective_to_quad(perspective);
|
||||
let view_box = &self.view_box;
|
||||
|
||||
self.objects.par_iter_mut().for_each(|object| {
|
||||
object.outline.clip_against_polygon(&quad);
|
||||
object.outline.apply_perspective(perspective);
|
||||
object.outline.clip_against_rect(&view_box);
|
||||
});
|
||||
|
||||
self.update_bounds();
|
||||
}
|
||||
|
||||
fn clip_bounding_quad_with_perspective(&self, perspective: &Perspective) -> Vec<Point3DF32> {
|
||||
|
|
Loading…
Reference in New Issue