use std::sync::Arc;
use wgpu::util::DeviceExt;
use crate::texture;
pub struct State {
pub window: Arc<winit::window::Window>,
pub instance: wgpu::Instance,
pub surface: wgpu::Surface<'static>,
pub adapter: wgpu::Adapter,
pub device: wgpu::Device,
pub queue: wgpu::Queue,
pub render_pipeline: wgpu::RenderPipeline,
pub surface_config: wgpu::SurfaceConfiguration,
pub camera: crate::instances::camera::Camera,
pub instances: Vec<crate::instances::Instance>,
pub camera_buffer: wgpu::Buffer,
pub camera_uniform: crate::instances::camera::CameraUniform,
pub camera_bind_group: wgpu::BindGroup,
pub depth_texture: texture::Texture,
pub f: fn(&mut State) -> (),
}
impl State {
pub fn new(window: winit::window::Window, f: fn(&mut State) -> ()) -> Self {
let window = Arc::new(window);
let window_size = window.inner_size();
let instance = wgpu::Instance::default();
let surface = instance.create_surface(window.clone()).unwrap();
let adapter = create_adapter(&instance, &surface);
let surface_config = surface
.get_default_config(&adapter, window_size.width, window_size.height)
.unwrap();
let (device, queue) = create_device(&adapter);
surface.configure(&device, &surface_config);
let texture_bind_group_layout =
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: Some("Texture Bind Group Layout"),
entries: &[
wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Texture {
sample_type: wgpu::TextureSampleType::Float { filterable: true },
view_dimension: wgpu::TextureViewDimension::D2,
multisampled: false,
},
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 1,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
count: None,
},
],
});
let shader = device.create_shader_module(wgpu::include_wgsl!("../shaders/shader.wgsl"));
let camera = crate::instances::camera::Camera::new(&window);
let camera_uniform = crate::instances::camera::CameraUniform::default();
let camera_buffer = create_buffer_init(
&device,
Some("Camera Buffer"),
wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
bytemuck::cast_slice(&[camera_uniform]),
);
let camera_bind_group_layout =
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: Some("Camera Bind Group Layout"),
entries: &[wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStages::VERTEX,
count: None,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Uniform,
has_dynamic_offset: false,
min_binding_size: None,
},
}],
});
let camera_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
label: Some("Camera Bind group"),
layout: &camera_bind_group_layout,
entries: &[wgpu::BindGroupEntry {
binding: 0,
resource: camera_buffer.as_entire_binding(),
}],
});
let render_pipeline_layout =
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("Render Pipeline Layout"),
bind_group_layouts: &[&camera_bind_group_layout, &texture_bind_group_layout],
push_constant_ranges: &[],
});
let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some("Render Pipeline"),
layout: Some(&render_pipeline_layout),
vertex: wgpu::VertexState {
module: &shader,
entry_point: "vs",
compilation_options: Default::default(),
buffers: &[crate::models::Vertex::desc()],
},
fragment: Some(wgpu::FragmentState {
module: &shader,
entry_point: "fs",
compilation_options: Default::default(),
targets: &[Some(wgpu::ColorTargetState {
blend: Some(wgpu::BlendState::REPLACE),
format: surface_config.format,
write_mask: wgpu::ColorWrites::ALL,
})],
}),
primitive: wgpu::PrimitiveState {
cull_mode: None,
..Default::default()
},
depth_stencil: Some(wgpu::DepthStencilState {
format: wgpu::TextureFormat::Depth32Float,
depth_write_enabled: true,
depth_compare: wgpu::CompareFunction::Less,
stencil: wgpu::StencilState::default(),
bias: wgpu::DepthBiasState::default(),
}),
multiview: None,
multisample: wgpu::MultisampleState::default(),
});
let depth_texture = texture::Texture::create_depth_texture(&surface_config, &device);
State {
window,
instance,
surface,
adapter,
device,
queue,
render_pipeline,
surface_config,
camera,
camera_buffer,
camera_uniform,
camera_bind_group,
instances: Vec::new(),
depth_texture,
f,
}
}
pub fn update(&mut self) {
(self.f)(self);
}
pub fn add_instance(&mut self, mesh_path: &str, position: glam::Vec3) {
let instance = crate::instances::Instance::new(mesh_path, self, position);
self.instances.push(instance);
}
}
fn create_adapter(instance: &wgpu::Instance, surface: &wgpu::Surface) -> wgpu::Adapter {
pollster::block_on(instance.request_adapter(&wgpu::RequestAdapterOptions {
power_preference: wgpu::PowerPreference::default(),
force_fallback_adapter: false,
compatible_surface: Some(surface),
}))
.unwrap()
}
fn create_buffer_init(
device: &wgpu::Device,
label: Option<&str>,
usage: wgpu::BufferUsages,
data: &[u8],
) -> wgpu::Buffer {
device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label,
usage,
contents: bytemuck::cast_slice(data),
})
}
fn create_device(adapter: &wgpu::Adapter) -> (wgpu::Device, wgpu::Queue) {
pollster::block_on(adapter.request_device(
&wgpu::DeviceDescriptor {
label: Some("Device"),
required_features: wgpu::Features::empty(),
required_limits:
wgpu::Limits::downlevel_webgl2_defaults().using_resolution(adapter.limits()),
},
None,
))
.unwrap()
}