Add Moiré demo
This commit is contained in:
parent
a5d373cb91
commit
639a8f39e8
|
@ -170,6 +170,20 @@ dependencies = [
|
||||||
"sdl2-sys 0.32.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
"sdl2-sys 0.32.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "canvas_moire"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"gl 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"pathfinder_canvas 0.1.0",
|
||||||
|
"pathfinder_geometry 0.3.0",
|
||||||
|
"pathfinder_gl 0.1.0",
|
||||||
|
"pathfinder_gpu 0.1.0",
|
||||||
|
"pathfinder_renderer 0.1.0",
|
||||||
|
"sdl2 0.32.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"sdl2-sys 0.32.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "canvas_text"
|
name = "canvas_text"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|
|
@ -6,6 +6,7 @@ members = [
|
||||||
"demo/magicleap",
|
"demo/magicleap",
|
||||||
"demo/native",
|
"demo/native",
|
||||||
"examples/canvas_minimal",
|
"examples/canvas_minimal",
|
||||||
|
"examples/canvas_moire",
|
||||||
"examples/canvas_text",
|
"examples/canvas_text",
|
||||||
"geometry",
|
"geometry",
|
||||||
"gl",
|
"gl",
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// pathfinder/canvas_minimal/src/main.rs
|
// pathfinder/examples/canvas_minimal/src/main.rs
|
||||||
//
|
//
|
||||||
// Copyright © 2019 The Pathfinder Project Developers.
|
// Copyright © 2019 The Pathfinder Project Developers.
|
||||||
//
|
//
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
[package]
|
||||||
|
name = "canvas_moire"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Patrick Walton <pcwalton@mimiga.net>"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
gl = "0.6"
|
||||||
|
sdl2 = "0.32"
|
||||||
|
sdl2-sys = "0.32"
|
||||||
|
|
||||||
|
[dependencies.pathfinder_canvas]
|
||||||
|
path = "../../canvas"
|
||||||
|
|
||||||
|
[dependencies.pathfinder_geometry]
|
||||||
|
path = "../../geometry"
|
||||||
|
|
||||||
|
[dependencies.pathfinder_gl]
|
||||||
|
path = "../../gl"
|
||||||
|
|
||||||
|
[dependencies.pathfinder_gpu]
|
||||||
|
path = "../../gpu"
|
||||||
|
|
||||||
|
[dependencies.pathfinder_renderer]
|
||||||
|
path = "../../renderer"
|
|
@ -0,0 +1,196 @@
|
||||||
|
// pathfinder/examples/canvas_moire/src/main.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.
|
||||||
|
|
||||||
|
use pathfinder_canvas::{CanvasRenderingContext2D, FillStyle, Path2D};
|
||||||
|
use pathfinder_geometry::basic::point::{Point2DF32, Point2DI32};
|
||||||
|
use pathfinder_geometry::color::{ColorF, ColorU};
|
||||||
|
use pathfinder_gl::{GLDevice, GLVersion};
|
||||||
|
use pathfinder_gpu::resources::FilesystemResourceLoader;
|
||||||
|
use pathfinder_gpu::{ClearParams, Device};
|
||||||
|
use pathfinder_renderer::concurrent::rayon::RayonExecutor;
|
||||||
|
use pathfinder_renderer::concurrent::scene_proxy::SceneProxy;
|
||||||
|
use pathfinder_renderer::gpu::renderer::{DestFramebuffer, Renderer};
|
||||||
|
use pathfinder_renderer::options::RenderOptions;
|
||||||
|
use sdl2::event::Event;
|
||||||
|
use sdl2::keyboard::Keycode;
|
||||||
|
use sdl2::video::GLProfile;
|
||||||
|
use std::f32;
|
||||||
|
|
||||||
|
const VELOCITY: f32 = 0.02;
|
||||||
|
const OUTER_RADIUS: f32 = 64.0;
|
||||||
|
const INNER_RADIUS: f32 = 48.0;
|
||||||
|
|
||||||
|
// FIXME(pcwalton): Adding more circles causes clipping problems. Fix them!
|
||||||
|
const CIRCLE_COUNT: u32 = 12;
|
||||||
|
|
||||||
|
const CIRCLE_SPACING: f32 = 48.0;
|
||||||
|
const CIRCLE_THICKNESS: f32 = 16.0;
|
||||||
|
|
||||||
|
const COLOR_CYCLE_SPEED: f32 = 0.0025;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// Set up SDL2.
|
||||||
|
let sdl_context = sdl2::init().unwrap();
|
||||||
|
let video = sdl_context.video().unwrap();
|
||||||
|
|
||||||
|
// Make sure we have at least a GL 3.0 context. Pathfinder requires this.
|
||||||
|
let gl_attributes = video.gl_attr();
|
||||||
|
gl_attributes.set_context_profile(GLProfile::Core);
|
||||||
|
gl_attributes.set_context_version(3, 3);
|
||||||
|
|
||||||
|
// Open a window.
|
||||||
|
let window_size = Point2DI32::new(1067, 800);
|
||||||
|
let window = video.window("Moire example", window_size.x() as u32, window_size.y() as u32)
|
||||||
|
.opengl()
|
||||||
|
.allow_highdpi()
|
||||||
|
.build()
|
||||||
|
.unwrap();
|
||||||
|
let mut event_pump = sdl_context.event_pump().unwrap();
|
||||||
|
|
||||||
|
// Get the real window size (for HiDPI).
|
||||||
|
let (drawable_width, drawable_height) = window.drawable_size();
|
||||||
|
let drawable_size = Point2DI32::new(drawable_width as i32, drawable_height as i32);
|
||||||
|
|
||||||
|
// Create the GL context, and make it current.
|
||||||
|
let gl_context = window.gl_create_context().unwrap();
|
||||||
|
gl::load_with(|name| video.gl_get_proc_address(name) as *const _);
|
||||||
|
window.gl_make_current(&gl_context).unwrap();
|
||||||
|
|
||||||
|
// Create our renderers.
|
||||||
|
let renderer = Renderer::new(GLDevice::new(GLVersion::GL3, 0),
|
||||||
|
&FilesystemResourceLoader::locate(),
|
||||||
|
DestFramebuffer::full_window(drawable_size));
|
||||||
|
let mut moire_renderer = MoireRenderer::new(renderer, window_size, drawable_size);
|
||||||
|
|
||||||
|
// Enter main render loop.
|
||||||
|
loop {
|
||||||
|
moire_renderer.render();
|
||||||
|
window.gl_swap_window();
|
||||||
|
|
||||||
|
match event_pump.poll_event() {
|
||||||
|
Some(Event::Quit {..}) |
|
||||||
|
Some(Event::KeyDown { keycode: Some(Keycode::Escape), .. }) => return,
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct MoireRenderer {
|
||||||
|
renderer: Renderer<GLDevice>,
|
||||||
|
scene: Option<SceneProxy>,
|
||||||
|
frame: i32,
|
||||||
|
window_size: Point2DI32,
|
||||||
|
drawable_size: Point2DI32,
|
||||||
|
device_pixel_ratio: f32,
|
||||||
|
colors: ColorGradient,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MoireRenderer {
|
||||||
|
fn new(renderer: Renderer<GLDevice>, window_size: Point2DI32, drawable_size: Point2DI32)
|
||||||
|
-> MoireRenderer {
|
||||||
|
MoireRenderer {
|
||||||
|
renderer,
|
||||||
|
scene: None,
|
||||||
|
frame: 0,
|
||||||
|
window_size,
|
||||||
|
drawable_size,
|
||||||
|
device_pixel_ratio: drawable_size.x() as f32 / window_size.x() as f32,
|
||||||
|
colors: ColorGradient::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render(&mut self) {
|
||||||
|
// Calculate animation values.
|
||||||
|
let time = self.frame as f32;
|
||||||
|
let (sin_time, cos_time) = (f32::sin(time * VELOCITY), f32::cos(time * VELOCITY));
|
||||||
|
let color_time = time * COLOR_CYCLE_SPEED;
|
||||||
|
let background_color = self.colors.sample(color_time);
|
||||||
|
let foreground_color = self.colors.sample(color_time + 0.5);
|
||||||
|
|
||||||
|
// Calculate outer and inner circle centers (circle and Leminscate of Gerono respectively).
|
||||||
|
let window_center = self.window_size.to_f32().scale(0.5);
|
||||||
|
let outer_center = window_center + Point2DF32::new(sin_time, cos_time).scale(OUTER_RADIUS);
|
||||||
|
let inner_center = window_center +
|
||||||
|
Point2DF32::new(1.0, sin_time).scale(cos_time * INNER_RADIUS);
|
||||||
|
|
||||||
|
// Clear to background color.
|
||||||
|
self.renderer.device.clear(&ClearParams {
|
||||||
|
color: Some(background_color),
|
||||||
|
..ClearParams::default()
|
||||||
|
});
|
||||||
|
|
||||||
|
// Make a canvas.
|
||||||
|
let mut canvas = CanvasRenderingContext2D::new(self.drawable_size.to_f32());
|
||||||
|
canvas.set_line_width(CIRCLE_THICKNESS * self.device_pixel_ratio);
|
||||||
|
canvas.set_stroke_style(FillStyle::Color(foreground_color.to_u8()));
|
||||||
|
|
||||||
|
// Draw circles.
|
||||||
|
self.draw_circles(&mut canvas, outer_center);
|
||||||
|
self.draw_circles(&mut canvas, inner_center);
|
||||||
|
|
||||||
|
// Build scene if necessary.
|
||||||
|
// TODO(pcwalton): Allow the user to build an empty scene proxy so they don't have to do this.
|
||||||
|
match self.scene {
|
||||||
|
None => self.scene = Some(SceneProxy::new(canvas.into_scene(), RayonExecutor)),
|
||||||
|
Some(ref mut scene) => scene.replace_scene(canvas.into_scene()),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render the scene.
|
||||||
|
self.scene.as_mut().unwrap().build_and_render(&mut self.renderer,
|
||||||
|
RenderOptions::default());
|
||||||
|
|
||||||
|
self.frame += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw_circles(&self, canvas: &mut CanvasRenderingContext2D, center: Point2DF32) {
|
||||||
|
for index in 0..CIRCLE_COUNT {
|
||||||
|
let mut path = Path2D::new();
|
||||||
|
self.add_circle_subpath(&mut path,
|
||||||
|
center.scale(self.device_pixel_ratio),
|
||||||
|
index as f32 * CIRCLE_SPACING * self.device_pixel_ratio);
|
||||||
|
canvas.stroke_path(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_circle_subpath(&self, path: &mut Path2D, center: Point2DF32, radius: f32) {
|
||||||
|
path.move_to(center + Point2DF32::new(0.0, -radius));
|
||||||
|
path.quadratic_curve_to(center + Point2DF32::new(radius, -radius),
|
||||||
|
center + Point2DF32::new(radius, 0.0));
|
||||||
|
path.quadratic_curve_to(center + Point2DF32::new(radius, radius),
|
||||||
|
center + Point2DF32::new(0.0, radius));
|
||||||
|
path.quadratic_curve_to(center + Point2DF32::new(-radius, radius),
|
||||||
|
center + Point2DF32::new(-radius, 0.0));
|
||||||
|
path.quadratic_curve_to(center + Point2DF32::new(-radius, -radius),
|
||||||
|
center + Point2DF32::new(0.0, -radius));
|
||||||
|
path.close_path();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ColorGradient([ColorF; 5]);
|
||||||
|
|
||||||
|
impl ColorGradient {
|
||||||
|
fn new() -> ColorGradient {
|
||||||
|
// Extracted from https://stock.adobe.com/69426938/
|
||||||
|
ColorGradient([
|
||||||
|
ColorU::from_u32(0x024873ff).to_f32(),
|
||||||
|
ColorU::from_u32(0x03658cff).to_f32(),
|
||||||
|
ColorU::from_u32(0x0388a6ff).to_f32(),
|
||||||
|
ColorU::from_u32(0xf28e6bff).to_f32(),
|
||||||
|
ColorU::from_u32(0xd95a4eff).to_f32(),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sample(&self, mut t: f32) -> ColorF {
|
||||||
|
let count = self.0.len();
|
||||||
|
t *= count as f32;
|
||||||
|
let (lo, hi) = (t.floor() as usize % count, t.ceil() as usize % count);
|
||||||
|
self.0[lo].lerp(self.0[hi], f32::fract(t))
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
// pathfinder/canvas_text/src/main.rs
|
// pathfinder/examples/canvas_text/src/main.rs
|
||||||
//
|
//
|
||||||
// Copyright © 2019 The Pathfinder Project Developers.
|
// Copyright © 2019 The Pathfinder Project Developers.
|
||||||
//
|
//
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
use pathfinder_simd::default::F32x4;
|
use pathfinder_simd::default::F32x4;
|
||||||
use std::fmt::{self, Debug, Formatter};
|
use std::fmt::{self, Debug, Formatter};
|
||||||
|
|
||||||
|
// TODO(pcwalton): Maybe this should be a u32?
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Default)]
|
#[derive(Clone, Copy, PartialEq, Eq, Hash, Default)]
|
||||||
pub struct ColorU {
|
pub struct ColorU {
|
||||||
pub r: u8,
|
pub r: u8,
|
||||||
|
@ -20,6 +21,16 @@ pub struct ColorU {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ColorU {
|
impl ColorU {
|
||||||
|
#[inline]
|
||||||
|
pub fn from_u32(rgba: u32) -> ColorU {
|
||||||
|
ColorU {
|
||||||
|
r: (rgba >> 24) as u8,
|
||||||
|
g: ((rgba >> 16) & 0xff) as u8,
|
||||||
|
b: ((rgba >> 8) & 0xff) as u8,
|
||||||
|
a: (rgba & 0xff) as u8,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn black() -> ColorU {
|
pub fn black() -> ColorU {
|
||||||
ColorU {
|
ColorU {
|
||||||
|
@ -68,6 +79,17 @@ impl ColorF {
|
||||||
ColorF(F32x4::splat(1.0))
|
ColorF(F32x4::splat(1.0))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn to_u8(&self) -> ColorU {
|
||||||
|
let color = (self.0 * F32x4::splat(255.0)).round().to_i32x4();
|
||||||
|
ColorU { r: color[0] as u8, g: color[1] as u8, b: color[2] as u8, a: color[3] as u8 }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn lerp(&self, other: ColorF, t: f32) -> ColorF {
|
||||||
|
ColorF(self.0 + (other.0 - self.0) * F32x4::splat(t))
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn r(&self) -> f32 {
|
pub fn r(&self) -> f32 {
|
||||||
self.0[0]
|
self.0[0]
|
||||||
|
|
|
@ -78,9 +78,11 @@ impl SceneProxy {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn build_and_render<D>(&self, renderer: &mut Renderer<D>, options: RenderOptions)
|
pub fn build_and_render<D>(&self, renderer: &mut Renderer<D>, options: RenderOptions)
|
||||||
where D: Device {
|
where D: Device {
|
||||||
|
renderer.begin_scene();
|
||||||
for command in self.build_with_stream(options) {
|
for command in self.build_with_stream(options) {
|
||||||
renderer.render_command(&command)
|
renderer.render_command(&command)
|
||||||
}
|
}
|
||||||
|
renderer.end_scene();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_svg(&self) -> Vec<u8> {
|
pub fn as_svg(&self) -> Vec<u8> {
|
||||||
|
|
|
@ -71,6 +71,11 @@ impl F32x4 {
|
||||||
unsafe { F32x4(ceil_v4f32(self.0)) }
|
unsafe { F32x4(ceil_v4f32(self.0)) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn round(self) -> F32x4 {
|
||||||
|
unsafe { F32x4(round_v4f32(self.0)) }
|
||||||
|
}
|
||||||
|
|
||||||
// Packed comparisons
|
// Packed comparisons
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -407,6 +412,8 @@ extern "C" {
|
||||||
fn floor_v4f32(a: float32x4_t) -> float32x4_t;
|
fn floor_v4f32(a: float32x4_t) -> float32x4_t;
|
||||||
#[link_name = "llvm.ceil.v4f32"]
|
#[link_name = "llvm.ceil.v4f32"]
|
||||||
fn ceil_v4f32(a: float32x4_t) -> float32x4_t;
|
fn ceil_v4f32(a: float32x4_t) -> float32x4_t;
|
||||||
|
#[link_name = "llvm.round.v4f32"]
|
||||||
|
fn round_v4f32(a: float32x4_t) -> float32x4_t;
|
||||||
|
|
||||||
#[link_name = "llvm.aarch64.neon.frecpe.v4f32"]
|
#[link_name = "llvm.aarch64.neon.frecpe.v4f32"]
|
||||||
fn vrecpe_v4f32(a: float32x4_t) -> float32x4_t;
|
fn vrecpe_v4f32(a: float32x4_t) -> float32x4_t;
|
||||||
|
|
|
@ -89,6 +89,16 @@ impl F32x4 {
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn round(self) -> F32x4 {
|
||||||
|
F32x4([
|
||||||
|
self[0].round(),
|
||||||
|
self[1].round(),
|
||||||
|
self[2].round(),
|
||||||
|
self[3].round(),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
// Packed comparisons
|
// Packed comparisons
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
// option. This file may not be copied, modified, or distributed
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
use std::arch::x86_64::{self, __m128, __m128i};
|
use std::arch::x86_64::{self, __m128, __m128i, _MM_FROUND_TO_NEAREST_INT};
|
||||||
use std::cmp::PartialEq;
|
use std::cmp::PartialEq;
|
||||||
use std::fmt::{self, Debug, Formatter};
|
use std::fmt::{self, Debug, Formatter};
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
@ -78,6 +78,11 @@ impl F32x4 {
|
||||||
unsafe { F32x4(x86_64::_mm_ceil_ps(self.0)) }
|
unsafe { F32x4(x86_64::_mm_ceil_ps(self.0)) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn round(self) -> F32x4 {
|
||||||
|
unsafe { F32x4(x86_64::_mm_round_ps(self.0, _MM_FROUND_TO_NEAREST_INT)) }
|
||||||
|
}
|
||||||
|
|
||||||
// Packed comparisons
|
// Packed comparisons
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
Loading…
Reference in New Issue