433 lines
14 KiB
Rust
433 lines
14 KiB
Rust
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<Window>,
|
||
device: wgpu::Device,
|
||
queue: wgpu::Queue,
|
||
size: winit::dpi::PhysicalSize<u32>,
|
||
surface: wgpu::Surface<'static>,
|
||
surface_format: wgpu::TextureFormat,
|
||
egui_state: EguiState,
|
||
|
||
// Camera
|
||
camera: Camera,
|
||
|
||
// pressed events
|
||
pressed_set: HashSet<KeyCode>,
|
||
pipeline: RenderPipeline,
|
||
depth_buffer: TextureView,
|
||
}
|
||
|
||
impl State
|
||
{
|
||
pub async fn new(window: Arc<Window>) -> 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::<ChunkPushConstants>() 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::<ChunkPushConstants>() 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<u32>)
|
||
{
|
||
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()
|
||
}
|
||
}
|