use std::collections::HashSet; use std::f32::consts::PI; use std::sync::Arc; use bytemuck::bytes_of; use egui_wgpu::ScreenDescriptor; use gpu_shared::ChunkPushConstants; use wgpu::ExperimentalFeatures; use wgpu::Extent3d; use wgpu::Features; use wgpu::FeaturesWGPU; use wgpu::FeaturesWebGPU; use wgpu::RenderPipeline; use wgpu::ShaderStages; use wgpu::TextureDescriptor; use wgpu::TextureFormat; use wgpu::TextureUsages; use wgpu::TextureView; use wgpu::include_spirv; use wgpu::include_spirv_raw; use winit::event::MouseScrollDelta; use winit::event::WindowEvent; use winit::keyboard::KeyCode; use winit::window::Window; use crate::egui_renderer::EguiState; pub struct State { window: Arc, device: wgpu::Device, queue: wgpu::Queue, size: winit::dpi::PhysicalSize, surface: wgpu::Surface<'static>, surface_format: wgpu::TextureFormat, egui_state: EguiState, // Camera camera: Camera, // pressed events pressed_set: HashSet, pipeline: RenderPipeline, depth_buffer: TextureView, } impl State { pub async fn new(window: Arc) -> State { let instance = wgpu::Instance::new(&wgpu::InstanceDescriptor::default()); let adapter = instance .request_adapter(&wgpu::RequestAdapterOptions::default()) .await .unwrap(); let (device, queue) = adapter .request_device(&wgpu::DeviceDescriptor { required_features: Features { features_wgpu: FeaturesWGPU::PUSH_CONSTANTS | FeaturesWGPU::EXPERIMENTAL_PASSTHROUGH_SHADERS, features_webgpu: FeaturesWebGPU::empty(), }, experimental_features: unsafe { ExperimentalFeatures::enabled() }, required_limits: wgpu::Limits { max_push_constant_size: size_of::() as u32, ..Default::default() }, ..Default::default() }) .await .unwrap(); let size = window.inner_size(); let surface = instance.create_surface(window.clone()).unwrap(); let cap = surface.get_capabilities(&adapter); let surface_format = cap.formats[0]; // let shaders = include_spirv!(env!("shaders.spv")); // let shader_module = device.create_shader_module(shaders); let shaders = include_spirv_raw!(env!("shaders.spv")); let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { label: Some("chunk_pipeline"), layout: Some( &device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { label: None, bind_group_layouts: &[], push_constant_ranges: &[wgpu::PushConstantRange { stages: wgpu::ShaderStages::VERTEX, range: 0..(size_of::() as u32), }], }), ), vertex: wgpu::VertexState { module: &shader_module, entry_point: Some("main_vs"), buffers: &[], compilation_options: wgpu::PipelineCompilationOptions::default(), }, fragment: Some(wgpu::FragmentState { module: &shader_module, entry_point: Some("main_fs"), targets: &[Some(wgpu::ColorTargetState { format: surface_format, blend: None, write_mask: wgpu::ColorWrites::ALL, })], compilation_options: wgpu::PipelineCompilationOptions::default(), }), primitive: wgpu::PrimitiveState { topology: wgpu::PrimitiveTopology::TriangleList, ..Default::default() }, depth_stencil: Some(wgpu::DepthStencilState { format: TextureFormat::Depth24PlusStencil8, depth_write_enabled: true, depth_compare: wgpu::CompareFunction::Less, stencil: wgpu::StencilState::default(), bias: wgpu::DepthBiasState::default(), }), multisample: wgpu::MultisampleState { count: 1, mask: !0, alpha_to_coverage_enabled: false, }, multiview: None, cache: None, }); let state = State { egui_state: EguiState::new(&device, surface_format, &window), camera: Camera { eye: glam::Vec3::new(0., 0., 0.), up: glam::Vec3::ZERO.with_y(1.), aspect: size.width as f32 / size.height as f32, fovy: 90., znear: 0.001, zfar: 1000., radius: 2., yaw: 1., pitch: 0., rotation_speed: 0.005, speed: 0.005, }, window, queue, size, surface, surface_format, pipeline, pressed_set: HashSet::new(), depth_buffer: device .create_texture(&TextureDescriptor { label: Some("Depth buffer"), size: Extent3d { width: size.width, height: size.height, depth_or_array_layers: 1, }, mip_level_count: 1, sample_count: 1, dimension: wgpu::TextureDimension::D2, format: TextureFormat::Depth24PlusStencil8, usage: TextureUsages::RENDER_ATTACHMENT, view_formats: &[], }) .create_view(&Default::default()), device, }; // Configure surface for the first time state.configure_surface(); state } pub fn get_window(&self) -> &Window { &self.window } pub fn configure_surface(&self) { let surface_config = wgpu::SurfaceConfiguration { usage: wgpu::TextureUsages::RENDER_ATTACHMENT, format: self.surface_format, // Request compatibility with the sRGB-format texture view we‘re going to create later. view_formats: vec![self.surface_format.add_srgb_suffix()], alpha_mode: wgpu::CompositeAlphaMode::Auto, width: self.size.width, height: self.size.height, desired_maximum_frame_latency: 2, present_mode: wgpu::PresentMode::AutoVsync, }; self.surface.configure(&self.device, &surface_config); } pub fn resize(&mut self, new_size: winit::dpi::PhysicalSize) { self.size = new_size; // reconfigure the surface self.configure_surface(); self.depth_buffer = self .device .create_texture(&TextureDescriptor { label: Some("Depth buffer"), size: Extent3d { width: new_size.width, height: new_size.height, depth_or_array_layers: 1, }, mip_level_count: 1, sample_count: 1, dimension: wgpu::TextureDimension::D2, format: TextureFormat::Depth24PlusStencil8, usage: TextureUsages::RENDER_ATTACHMENT, view_formats: &[], }) .create_view(&Default::default()); } pub fn render(&mut self) { self.update_camera(); // Create texture view let surface_texture = self .surface .get_current_texture() .expect("failed to acquire next swapchain texture"); let texture_view = surface_texture .texture .create_view(&wgpu::TextureViewDescriptor { // Without add_srgb_suffix() the image we will be working with // might not be "gamma correct". format: Some(self.surface_format.add_srgb_suffix()), ..Default::default() }); // Renders a GREEN screen let mut encoder = self.device.create_command_encoder(&Default::default()); { let mut renderpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { label: None, color_attachments: &[Some(wgpu::RenderPassColorAttachment { view: &texture_view, depth_slice: None, resolve_target: None, ops: wgpu::Operations { load: wgpu::LoadOp::Clear(wgpu::Color { r: 0.01, g: 0.01, b: 0.01, a: 1., }), store: wgpu::StoreOp::Store, }, })], depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment { view: &self.depth_buffer, depth_ops: Some(wgpu::Operations { load: wgpu::LoadOp::Clear(1.), store: wgpu::StoreOp::Discard, }), stencil_ops: None, }), timestamp_writes: None, occlusion_query_set: None, }); renderpass.set_pipeline(&self.pipeline); renderpass.set_push_constants( ShaderStages::VERTEX, 0, bytes_of(&ChunkPushConstants { view_projection: self.camera.view_proj(), transform: glam::Mat4::IDENTITY, eye_position: self.camera.eye, _zero_pad: 0., }), ); renderpass.draw(0..36, 0..1); // End the renderpass. } // Egui { let screen_descriptor = ScreenDescriptor { size_in_pixels: [self.size.width, self.size.height], pixels_per_point: 1., }; self.egui_state.begin_frame(&self.window); egui::Window::new("Hello Window").resizable(true).show( self.egui_state.context(), |ui| { ui.label("Hello, world."); }, ); self.egui_state.end_frame_and_draw( &self.device, &self.queue, &mut encoder, &self.window, &texture_view, screen_descriptor, ); } // Submit the command in the queue to execute self.queue.submit([encoder.finish()]); self.window.pre_present_notify(); surface_texture.present(); } // ------------------------------ // MOVEMENT // ------------------------------ pub fn handle_event(&mut self, event: &WindowEvent) { self.egui_state.handle_event(&self.window, event); if let WindowEvent::KeyboardInput { event, .. } = event { match (event.state, event.physical_key) { (winit::event::ElementState::Pressed, winit::keyboard::PhysicalKey::Code(c)) => { self.pressed_set.insert(c); } (winit::event::ElementState::Released, winit::keyboard::PhysicalKey::Code(c)) => { self.pressed_set.remove(&c); } _ => {} } } } fn update_camera(&mut self) { let mut movement = glam::Vec3::new(0., 0., 0.); if self.pressed_set.contains(&KeyCode::KeyW) { movement.z += self.camera.speed; } if self.pressed_set.contains(&KeyCode::KeyS) { movement.z -= self.camera.speed; } // Left rigth if self.pressed_set.contains(&KeyCode::KeyA) { movement.x += self.camera.speed; } if self.pressed_set.contains(&KeyCode::KeyD) { movement.x -= self.camera.speed; } let rot_movement = glam::Mat3::from_rotation_y(-self.camera.yaw) * glam::Mat3::from_rotation_x(-self.camera.pitch) * movement; self.camera.eye -= rot_movement; } pub fn cursor_moved(&mut self, x: f32, y: f32) { const SENSIBILITY: f32 = 0.0004; let position = glam::Vec2::new(x, y); let offset = position * SENSIBILITY; self.camera.yaw += offset.x; self.camera.pitch += offset.y; } pub fn mouse_wheel(&mut self, delta: MouseScrollDelta) { if let MouseScrollDelta::LineDelta(_, y) = delta { self.camera.speed += y * (self.camera.speed * 0.05); } } } #[derive(Clone, Copy)] pub struct Camera { pub eye: glam::Vec3, pub up: glam::Vec3, pub aspect: f32, pub fovy: f32, pub znear: f32, pub zfar: f32, pub radius: f32, pub yaw: f32, pub pitch: f32, pub rotation_speed: f32, pub speed: f32, } #[rustfmt::skip] pub const OPENGL_TO_WGPU_MATRIX: glam::Mat4 = glam::Mat4::from_cols( glam::Vec4::new(1.0, 0.0, 0.0, 0.0), glam::Vec4::new(0.0, 1.0, 0.0, 0.0), glam::Vec4::new(0.0, 0.0, 0.5, 0.0), glam::Vec4::new(0.0, 0.0, 0.5, 1.0), ); impl Camera { pub fn view_proj(&self) -> glam::Mat4 { let view = glam::Mat4::from_translation(self.eye) * glam::Mat4::from_rotation_y(-self.yaw) * glam::Mat4::from_rotation_x(-self.pitch); let proj = glam::Mat4::perspective_rh(PI * self.fovy / 180., self.aspect, self.znear, self.zfar); proj * view.inverse() } }