#[path = "../application_framework.rs"] mod application_framework; use contrast_renderer::renderer::RenderOperation; use geometric_algebra::{ ppga3d::{Motor, Rotor, Translator}, GeometricProduct, One, }; const OPEN_SANS_TTF: &[u8] = include_bytes!("../fonts/OpenSans-Regular.ttf"); const MSAA_SAMPLE_COUNT: u32 = 4; struct Application { depth_stencil_texture_view: Option, msaa_color_texture_view: Option, renderer: contrast_renderer::renderer::Renderer, dynamic_stroke_options: [contrast_renderer::path::DynamicStrokeOptions; 1], instance_buffers: [contrast_renderer::renderer::Buffer; 2], shape: contrast_renderer::renderer::Shape, viewport_size: wgpu::Extent3d, view_rotation: Rotor, view_distance: f32, } impl application_framework::Application for Application { fn new(device: &wgpu::Device, _queue: &mut wgpu::Queue, surface_configuration: &wgpu::SurfaceConfiguration) -> Self { let renderer = contrast_renderer::renderer::Renderer::new( device, contrast_renderer::renderer::Configuration { blending: wgpu::ColorTargetState { format: surface_configuration.format, blend: Some(wgpu::BlendState { color: wgpu::BlendComponent { src_factor: wgpu::BlendFactor::One, dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha, operation: wgpu::BlendOperation::Add, }, alpha: wgpu::BlendComponent { src_factor: wgpu::BlendFactor::One, dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha, operation: wgpu::BlendOperation::Add, }, }), write_mask: wgpu::ColorWrites::ALL, }, cull_mode: Some(wgpu::Face::Back), depth_stencil_format: wgpu::TextureFormat::Depth24PlusStencil8, depth_compare: wgpu::CompareFunction::LessEqual, depth_write_enabled: true, color_attachment_in_stencil_pass: true, msaa_sample_count: MSAA_SAMPLE_COUNT, clip_nesting_counter_bits: 4, winding_counter_bits: 4, alpha_layer_count: 0, }, ) .unwrap(); let dynamic_stroke_options = [contrast_renderer::path::DynamicStrokeOptions::Dashed { join: contrast_renderer::path::Join::Miter, pattern: vec![contrast_renderer::path::DashInterval { gap_start: 3.0.into(), gap_end: 4.0.into(), dash_start: contrast_renderer::path::Cap::Butt, dash_end: contrast_renderer::path::Cap::Butt, }], phase: 0.0.into(), }]; let font_face = ttf_parser::Face::from_slice(OPEN_SANS_TTF, 0).unwrap(); let mut paths = contrast_renderer::text::paths_of_text( &font_face, &contrast_renderer::text::Layout { size: 2.7.into(), orientation: contrast_renderer::text::Orientation::LeftToRight, major_alignment: contrast_renderer::text::Alignment::Center, minor_alignment: contrast_renderer::text::Alignment::Center, }, "Hello World", None, ); for path in &mut paths { path.reverse(); } paths.insert(0, contrast_renderer::path::Path::from_rounded_rect([0.0, 0.0], [5.8, 1.3], 0.5)); paths[0].stroke_options = Some(contrast_renderer::path::StrokeOptions { width: 0.1.into(), offset: 0.0.into(), miter_clip: 1.0.into(), closed: true, dynamic_stroke_options_group: 0, curve_approximation: contrast_renderer::path::CurveApproximation::UniformTangentAngle(0.1.into()), }); let shape = contrast_renderer::renderer::Shape::from_paths(device, &renderer, &dynamic_stroke_options, paths.as_slice(), None).unwrap(); Self { depth_stencil_texture_view: None, msaa_color_texture_view: None, renderer, dynamic_stroke_options, instance_buffers: [ contrast_renderer::renderer::Buffer::new(device, wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST, &[]), contrast_renderer::renderer::Buffer::new(device, wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST, &[]), ], shape, viewport_size: wgpu::Extent3d::default(), view_rotation: Rotor::one(), view_distance: 5.0, } } fn resize(&mut self, device: &wgpu::Device, _queue: &mut wgpu::Queue, surface_configuration: &wgpu::SurfaceConfiguration) { self.viewport_size = wgpu::Extent3d { width: surface_configuration.width, height: surface_configuration.height, depth_or_array_layers: 1, }; let depth_stencil_texture_descriptor = wgpu::TextureDescriptor { size: self.viewport_size, mip_level_count: 1, sample_count: MSAA_SAMPLE_COUNT, dimension: wgpu::TextureDimension::D2, format: self.renderer.get_config().depth_stencil_format, view_formats: &[self.renderer.get_config().depth_stencil_format], usage: wgpu::TextureUsages::RENDER_ATTACHMENT, label: None, }; let depth_stencil_texture = device.create_texture(&depth_stencil_texture_descriptor); self.depth_stencil_texture_view = Some(depth_stencil_texture.create_view(&wgpu::TextureViewDescriptor { dimension: Some(wgpu::TextureViewDimension::D2), ..wgpu::TextureViewDescriptor::default() })); if MSAA_SAMPLE_COUNT > 1 { let msaa_color_texture_descriptor = wgpu::TextureDescriptor { size: self.viewport_size, mip_level_count: 1, sample_count: MSAA_SAMPLE_COUNT, dimension: wgpu::TextureDimension::D2, format: surface_configuration.format, view_formats: &[surface_configuration.format], usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING, label: None, }; let msaa_color_texture = device.create_texture(&msaa_color_texture_descriptor); self.msaa_color_texture_view = Some(msaa_color_texture.create_view(&wgpu::TextureViewDescriptor { dimension: Some(wgpu::TextureViewDimension::D2), ..wgpu::TextureViewDescriptor::default() })); } self.renderer .resize_internal_buffers(device, self.viewport_size, self.msaa_color_texture_view.as_ref().unwrap()); } fn render(&mut self, device: &wgpu::Device, queue: &mut wgpu::Queue, frame: &wgpu::SurfaceTexture, animation_time: f64) { match &mut self.dynamic_stroke_options[0] { contrast_renderer::path::DynamicStrokeOptions::Dashed { phase, .. } => { *phase = (animation_time as f32 * 2.0).into(); } _ => unreachable!(), } self.shape.set_dynamic_stroke_options(queue, 0, &self.dynamic_stroke_options[0]).unwrap(); let projection_matrix = contrast_renderer::utils::matrix_multiplication( &contrast_renderer::utils::perspective_projection( std::f32::consts::PI * 0.5, self.viewport_size.width as f32 / self.viewport_size.height as f32, 1.0, 1000.0, ), &contrast_renderer::utils::motor3d_to_mat4( &Translator::new(1.0, 0.0, 0.0, -0.5 * self.view_distance).geometric_product(self.view_rotation), ), ); const ROWS: usize = 9; const COLUMNS: usize = 5; let mut instances_transform: Vec<[geometric_algebra::ppga3d::Point; 4]> = Vec::with_capacity(1 + ROWS * COLUMNS); let mut instances_color: Vec = Vec::with_capacity(1 + ROWS * COLUMNS); instances_transform.push(projection_matrix); instances_color.push([1.0, 1.0, 1.0, 1.0].into()); for y in 0..ROWS { for x in 0..COLUMNS { instances_transform.push(contrast_renderer::utils::matrix_multiplication( &projection_matrix, &contrast_renderer::utils::motor3d_to_mat4( &(Motor::new( 1.0, 0.0, 0.0, 0.0, 0.0, (x as f32 + 0.5 - COLUMNS as f32 * 0.5) * 7.0, (y as f32 + 0.5 - ROWS as f32 * 0.5) * 3.0, -5.0, )), ), )); let red = x as f32 / COLUMNS as f32; let green = y as f32 / ROWS as f32; instances_color.push([red, green, 1.0 - red - green, 1.0].into()); } } self.instance_buffers[0].update(device, queue, &contrast_renderer::concat_buffers!([&instances_transform]).1); self.instance_buffers[1].update(device, queue, &contrast_renderer::concat_buffers!([&instances_color]).1); let frame_view = frame.texture.create_view(&wgpu::TextureViewDescriptor::default()); let msaa_frame_view = if MSAA_SAMPLE_COUNT == 1 { &frame_view } else { self.msaa_color_texture_view.as_ref().unwrap() }; let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); { let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { label: None, color_attachments: &[Some(wgpu::RenderPassColorAttachment { view: msaa_frame_view, resolve_target: if MSAA_SAMPLE_COUNT == 1 { None } else { Some(&frame_view) }, ops: wgpu::Operations { load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT), store: wgpu::StoreOp::Store, }, })], depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment { view: self.depth_stencil_texture_view.as_ref().unwrap(), depth_ops: Some(wgpu::Operations { load: wgpu::LoadOp::Clear(1.0), store: wgpu::StoreOp::Store, }), stencil_ops: Some(wgpu::Operations { load: wgpu::LoadOp::Clear(0), store: wgpu::StoreOp::Store, }), }), timestamp_writes: None, occlusion_query_set: None, }); render_pass.set_vertex_buffer(0, self.instance_buffers[0].buffer.slice(..)); for instance_index in 0..(1 + ROWS * COLUMNS) as u32 { self.shape.render( &self.renderer, &mut render_pass, instance_index..instance_index + 1, RenderOperation::Stencil, ); render_pass.set_vertex_buffer(1, self.instance_buffers[1].buffer.slice(..)); self.shape.render( &self.renderer, &mut render_pass, instance_index..instance_index + 1, RenderOperation::Color, ); } } queue.submit(Some(encoder.finish())); } fn window_event(&mut self, event: winit::event::WindowEvent) { match event { winit::event::WindowEvent::CursorMoved { position, .. } => { let position = [ std::f32::consts::PI * 1.2 * (position.x as f32 / self.viewport_size.width as f32 - 0.5), std::f32::consts::PI * 1.2 * (position.y as f32 / self.viewport_size.height as f32 - 0.5), ]; self.view_rotation = contrast_renderer::utils::rotate_around_axis(position[0], &[0.0, 1.0, 0.0]) .geometric_product(contrast_renderer::utils::rotate_around_axis(position[1], &[1.0, 0.0, 0.0])); } winit::event::WindowEvent::MouseWheel { delta, .. } => { let difference = match delta { winit::event::MouseScrollDelta::LineDelta(x, y) => [x, y], winit::event::MouseScrollDelta::PixelDelta(delta) => [delta.x as f32 * 0.1, delta.y as f32 * 0.1], }; self.view_distance = (self.view_distance + difference[1]).clamp(2.0, 100.0); } _ => {} } } } fn main() { application_framework::ApplicationManager::run::("Contrast Renderer - Showcase"); }