blob: 9bb6c322d2719fe80718a3d52de5523a35a81a63 [file] [log] [blame]
use std::{fs, time::Instant};
use rive_rs::{Artboard, File, Instantiate, Viewport};
use vello::{
kurbo::{Affine, Rect, Vec2},
peniko::{Color, Fill},
util::{RenderContext, RenderSurface},
Renderer, RendererOptions, Scene, SceneBuilder,
};
use winit::{
dpi::LogicalSize,
event::{ElementState, Event, MouseButton, WindowEvent},
event_loop::{ControlFlow, EventLoop},
window::{Window, WindowBuilder},
};
struct RenderState {
surface: RenderSurface,
window: Window,
}
const INITIAL_WINDOW_SIZE: LogicalSize<u32> = LogicalSize::new(700, 700);
const FRAME_STATS_CAPACITY: usize = 30;
const SCROLL_FACTOR_THRESHOLD: f64 = 100.0;
fn main() {
let mut viewport = Viewport::default();
let mut scene: Option<Box<dyn rive_rs::Scene>> = None;
let event_loop = EventLoop::new();
let mut cached_window: Option<Window> = None;
let mut renderer: Option<Renderer> = None;
let mut render_cx = RenderContext::new().unwrap();
let mut render_state: Option<RenderState> = None;
let mut mouse_pos = Vec2::default();
let mut scroll_delta = 0.0;
let mut frame_start_time = Instant::now();
let mut stats = Vec::with_capacity(FRAME_STATS_CAPACITY);
event_loop.run(move |event, _event_loop, control_flow| match event {
Event::WindowEvent { ref event, .. } => {
let Some(render_state) = &mut render_state else {
return;
};
match event {
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
WindowEvent::Resized(size) => {
viewport.resize(size.width, size.height);
render_cx.resize_surface(&mut render_state.surface, size.width, size.height);
render_state.window.request_redraw();
}
WindowEvent::MouseInput {
state,
button: MouseButton::Left,
..
} => {
if let Some(scene) = &mut scene {
match state {
ElementState::Pressed => scene.pointer_down(
mouse_pos.x as f32,
mouse_pos.y as f32,
&viewport,
),
ElementState::Released => {
scene.pointer_up(mouse_pos.x as f32, mouse_pos.y as f32, &viewport)
}
}
}
}
WindowEvent::CursorMoved { position, .. } => {
mouse_pos = Vec2::new(position.x, position.y);
if let Some(scene) = &mut scene {
scene.pointer_move(mouse_pos.x as f32, mouse_pos.y as f32, &viewport);
}
}
WindowEvent::MouseWheel { delta, .. } => match delta {
winit::event::MouseScrollDelta::LineDelta(_, lines_y) => {
scroll_delta = (scroll_delta
- (*lines_y as f64).signum() * SCROLL_FACTOR_THRESHOLD)
.max(0.0);
}
winit::event::MouseScrollDelta::PixelDelta(pixels) => {
scroll_delta = (scroll_delta - pixels.y).max(0.0);
}
},
WindowEvent::DroppedFile(path) => {
scene = Some({
let file = File::new(&fs::read(path).unwrap()).unwrap();
let artboard = Artboard::instantiate(&file, None).unwrap();
Box::<dyn rive_rs::Scene>::instantiate(&artboard, None).unwrap()
});
}
_ => {}
}
}
Event::MainEventsCleared => {
if let Some(render_state) = &mut render_state {
render_state.window.request_redraw();
}
}
Event::RedrawRequested(_) => {
let mut rive_renderer = rive_rs::Renderer::default();
let factor = (scroll_delta / SCROLL_FACTOR_THRESHOLD).max(1.0) as u32;
let elapsed = &frame_start_time.elapsed();
stats.push(elapsed.as_secs_f64());
if stats.len() == FRAME_STATS_CAPACITY {
let average = stats.drain(..).sum::<f64>() / FRAME_STATS_CAPACITY as f64;
if let Some(state) = &mut render_state {
let copies = (factor > 1)
.then(|| format!(" ({} copies)", factor.pow(2)))
.unwrap_or_default();
state.window.set_title(&format!(
"Rive on Vello demo | {:.2}ms{}",
average * 1000.0,
copies
));
}
}
frame_start_time = Instant::now();
let Some(render_state) = &mut render_state else {
return;
};
let width = render_state.surface.config.width;
let height = render_state.surface.config.height;
let device_handle = &render_cx.devices[render_state.surface.dev_id];
let render_params = vello::RenderParams {
base_color: Color::DIM_GRAY,
width,
height,
};
let surface_texture = render_state
.surface
.surface
.get_current_texture()
.expect("failed to get surface texture");
let mut vello_scene = Scene::default();
let mut builder = SceneBuilder::for_scene(&mut vello_scene);
if let Some(scene) = &mut scene {
scene.advance_and_maybe_draw(&mut rive_renderer, *elapsed, &mut viewport);
for i in 0..factor.pow(2) {
builder.append(
rive_renderer.scene(),
Some(
Affine::default()
.then_scale(1.0 / factor as f64)
.then_translate(Vec2::new(
(i % factor) as f64 * width as f64 / factor as f64,
(i / factor) as f64 * height as f64 / factor as f64,
)),
),
);
}
} else {
// Vello doesn't draw base color when there is no geometry.
builder.fill(
Fill::NonZero,
Affine::IDENTITY,
Color::TRANSPARENT,
None,
&Rect::new(0.0, 0.0, 0.0, 0.0),
);
}
if !vello_scene.data().is_empty() {
vello::block_on_wgpu(
&device_handle.device,
renderer.as_mut().unwrap().render_to_surface_async(
&device_handle.device,
&device_handle.queue,
&vello_scene,
&surface_texture,
&render_params,
),
)
.expect("failed to render to surface");
}
surface_texture.present();
device_handle.device.poll(wgpu::Maintain::Poll);
}
Event::Suspended => {
if let Some(render_state) = render_state.take() {
cached_window = Some(render_state.window);
}
*control_flow = ControlFlow::Wait;
}
Event::Resumed => {
if render_state.is_some() {
return;
}
let window = cached_window.take().unwrap_or_else(|| {
WindowBuilder::new()
.with_inner_size(INITIAL_WINDOW_SIZE)
.with_resizable(true)
.with_title("Rive on Vello demo")
.build(_event_loop)
.unwrap()
});
let size = window.inner_size();
let surface_future = render_cx.create_surface(&window, size.width, size.height);
let surface = pollster::block_on(surface_future).expect("Error creating surface");
render_state = {
let render_state = RenderState { window, surface };
renderer = Some(
Renderer::new(
&render_cx.devices[render_state.surface.dev_id].device,
&RendererOptions {
surface_format: Some(render_state.surface.format),
timestamp_period: render_cx.devices[render_state.surface.dev_id]
.queue
.get_timestamp_period(),
use_cpu: false,
},
)
.expect("Could create renderer"),
);
Some(render_state)
};
*control_flow = ControlFlow::Poll;
}
_ => {}
});
}