use crate::{
camera::{Camera, CameraStruct},
model::Material,
prefabs::Prefab,
prelude::{Vertex, Instance},
loader::{self, load_texture},
shader,
structs::{CameraController, MeshType, SingleMesh},
texture, window, resources::{UpdateInstance, MousePos, DeltaTime, WindowEvents}, primitives::rect,
};
use bevy_ecs::prelude::*;
use glam::Vec2;
use instant::Duration;
use slab::Slab;
use wgpu::util::DeviceExt;
use winit::{
dpi::PhysicalPosition,
event::{ElementState, KeyboardInput, MouseButton, WindowEvent},
event_loop::EventLoop,
window::Window,
};
pub struct State {
pub device: wgpu::Device,
pub config: wgpu::SurfaceConfiguration,
pub render_pipeline: wgpu::RenderPipeline,
pub camera: CameraStruct,
pub depth_texture: texture::Texture,
pub window: window::Window,
pub texture_bind_group_layout: wgpu::BindGroupLayout,
pub mouse_pressed: bool,
pub mouse_locked: bool,
build_path: String,
pub world: World,
pub schedule: Schedule,
}
impl State {
pub async fn new(
mouse_lock: bool,
build_path: &str,
cam: Camera,
speed: f32,
sensitivity: f32,
) -> (Self, EventLoop<()>) {
let (window, event_loop) = window::Window::new(mouse_lock).await;
let (device, queue) = window
.adapter
.request_device(
&wgpu::DeviceDescriptor {
label: None,
features: wgpu::Features::empty(),
limits: if cfg!(target_arch = "wasm32") {
wgpu::Limits::downlevel_webgl2_defaults()
} else {
wgpu::Limits::default()
},
},
None,
)
.await
.unwrap();
log::warn!("Surface");
let surface_caps = window.surface.get_capabilities(&window.adapter);
let surface_format = surface_caps
.formats
.iter()
.copied()
.find(|f| f.is_srgb())
.unwrap_or(surface_caps.formats[0]);
let config = wgpu::SurfaceConfiguration {
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
format: surface_format,
width: window.size.width,
height: window.size.height,
present_mode: surface_caps.present_modes[0],
alpha_mode: surface_caps.alpha_modes[0],
view_formats: vec![],
};
window.surface.configure(&device, &config);
let texture_bind_group_layout =
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
entries: &[
wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Texture {
multisampled: false,
view_dimension: wgpu::TextureViewDimension::D2,
sample_type: wgpu::TextureSampleType::Float { filterable: true },
},
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 1,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
count: None,
},
],
label: Some("texture_bind_group_layout"),
});
let camera = CameraStruct::new(
&device,
&config,
cam,
CameraController::new(speed, sensitivity),
);
let depth_texture =
texture::Texture::create_depth_texture(&device, &config, "depth_texture");
let render_pipeline_layout =
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("Render Pipeline Layout"),
bind_group_layouts: &[&texture_bind_group_layout, &camera.bind_group_layout],
push_constant_ranges: &[],
});
let render_pipeline = shader::make_shader(
include_str!("shader.wgsl"),
&device,
render_pipeline_layout,
&config,
);
window.window.set_visible(true);
let mut world = World::new();
world.insert_resource(MousePos {
pos: PhysicalPosition { x: 0.0, y: 0.0 },
});
world.insert_resource(UpdateInstance {
queue,
prefab_slab: Slab::new(),
});
world.insert_resource(DeltaTime { dt: Duration::ZERO });
world.insert_resource(WindowEvents { keys_pressed: vec![] });
let schedule = Schedule::default();
(
Self {
device,
config,
render_pipeline,
camera,
depth_texture,
window,
texture_bind_group_layout,
mouse_pressed: false,
mouse_locked: mouse_lock,
build_path: build_path.to_string(),
world,
schedule,
},
event_loop,
)
}
pub fn window(&self) -> &Window {
&self.window.window
}
pub fn resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>) {
if new_size.width > 0 && new_size.height > 0 {
self.camera
.projection
.resize(new_size.width, new_size.height);
self.window.size = new_size;
self.config.width = new_size.width;
self.config.height = new_size.height;
self.window.surface.configure(&self.device, &self.config);
self.depth_texture =
texture::Texture::create_depth_texture(&self.device, &self.config, "depth_texture");
}
}
pub fn input(&mut self, event: &WindowEvent) -> bool {
match event {
WindowEvent::KeyboardInput {
input:
KeyboardInput {
virtual_keycode: Some(key),
state,
..
},
..
} => {
let key_pressed = (*key, *state);
self.world
.get_resource_mut::<WindowEvents>()
.unwrap()
.keys_pressed
.push(key_pressed);
self.camera.camera_controller.process_keyboard(*key, *state)
}
WindowEvent::MouseWheel { delta, .. } => {
self.camera.camera_controller.process_scroll(delta);
true
}
WindowEvent::MouseInput {
button: MouseButton::Left,
state,
..
} => {
self.mouse_pressed = *state == ElementState::Pressed;
true
}
_ => false,
}
}
pub fn update(&mut self) {
self.camera
.camera_uniform
.update_view_proj(&self.camera.camera_transform, &self.camera.projection);
let queue = self.world.get_resource_mut::<UpdateInstance>().unwrap();
queue.queue.write_buffer(
&self.camera.buffer,
0,
bytemuck::cast_slice(&[self.camera.camera_uniform]),
);
}
pub async fn create_model_instances(
&mut self,
model: &str,
instances: Vec<&mut Instance>,
is_updating: bool,
) {
let mut instance_updater = self.world.get_resource_mut::<UpdateInstance>().unwrap();
let loaded_model = loader::load_model(
model,
&self.build_path,
&self.device,
&instance_updater.queue,
&self.texture_bind_group_layout,
)
.await
.unwrap();
let mut instance_data = vec![];
let mut length = 0;
for instance in &instances {
let instance_raw = instance.to_raw();
if instance_raw.is_some() {
instance_data.push(instance_raw.unwrap());
length += 1;
}
}
let instance_buffer = self
.device
.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("Instance Buffer"),
contents: bytemuck::cast_slice(&instance_data),
usage: if is_updating {
wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST
} else {
wgpu::BufferUsages::VERTEX
},
});
let container = Prefab::new(
instance_buffer,
MeshType::Model(loaded_model),
length,
);
let entry = instance_updater.prefab_slab.vacant_entry();
let key = entry.key();
for instance in instances {
instance.prefab_index = key;
}
entry.insert(container);
}
pub async fn compile_material(&self, texture_name: &str) -> Material {
let queue = self.world.get_resource::<UpdateInstance>().unwrap();
let diffuse_texture =
load_texture(texture_name, &self.build_path, &self.device, &queue.queue)
.await
.unwrap();
let texture_bind_group = self.device.create_bind_group(&wgpu::BindGroupDescriptor {
layout: &self.texture_bind_group_layout,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::TextureView(&diffuse_texture.view),
},
wgpu::BindGroupEntry {
binding: 1,
resource: wgpu::BindingResource::Sampler(&diffuse_texture.sampler),
},
],
label: None,
});
Material {
bind_group: texture_bind_group,
}
}
pub fn build_mesh(
&mut self,
vertices: Vec<Vertex>,
indices: Vec<u32>,
instances: Vec<&mut Instance>,
material: Material,
is_updating: bool,
) {
let vertex_buffer = self
.device
.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("Vertex Buffer"),
contents: bytemuck::cast_slice(&vertices),
usage: wgpu::BufferUsages::VERTEX,
});
let index_buffer = self
.device
.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("Index Buffer"),
contents: bytemuck::cast_slice(&indices),
usage: wgpu::BufferUsages::INDEX,
});
let mesh = SingleMesh {
vertex_buffer,
index_buffer,
num_elements: indices.len() as u32,
material,
};
let mut instance_data = vec![];
let mut length = 0;
for instance in &instances {
let instance_raw = instance.to_raw();
if instance_raw.is_some() {
instance_data.push(instance_raw.unwrap());
length += 1;
}
}
let instance_buffer = self
.device
.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("Instance Buffer"),
contents: bytemuck::cast_slice(&instance_data),
usage: if is_updating {
wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST
} else {
wgpu::BufferUsages::VERTEX
},
});
let container = Prefab::new(
instance_buffer,
MeshType::SingleMesh(mesh),
length,
);
let mut update_instance = self.world.get_resource_mut::<UpdateInstance>().unwrap();
let entry = update_instance.prefab_slab.vacant_entry();
let key = entry.key();
for instance in instances {
instance.prefab_index = key;
}
entry.insert(container);
}
pub fn make_sprites(
&mut self,
instances: Vec<&mut Instance>,
material: Material,
is_updating: bool
) {
let (vertices, indices) = rect(Vec2::new(0.5,0.5), Vec2::new(-0.5,-0.5));
let vertex_buffer = self.device
.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("Vertex Buffer"),
contents: bytemuck::cast_slice(&vertices),
usage: wgpu::BufferUsages::VERTEX,
});
let index_buffer = self.device
.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("Index Buffer"),
contents: bytemuck::cast_slice(&indices),
usage: wgpu::BufferUsages::INDEX,
});
let mesh = SingleMesh {
vertex_buffer,index_buffer, num_elements: indices.len() as u32,
material,
};
let mut instance_data = vec![];
let mut length = 0;
for instance in &instances {
let instance_raw = instance.to_raw();
if instance_raw.is_some() {
instance_data.push(instance_raw.unwrap());
length += 1;
}
}
let instance_buffer = self
.device
.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("Instance Buffer"),
contents: bytemuck::cast_slice(&instance_data),
usage: if is_updating {
wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST
} else {
wgpu::BufferUsages::VERTEX
},
});
let container = Prefab::new(
instance_buffer,
MeshType::SingleMesh(mesh),
length,
);
let mut update_instance = self.world.get_resource_mut::<UpdateInstance>().unwrap();
let entry = update_instance.prefab_slab.vacant_entry();
let key = entry.key();
for instance in instances {
instance.prefab_index = key;
}
entry.insert(container);
}
}