use std::borrow::Cow;
use crate::engine::Engine;
use crate::prelude::*;
use glam::Mat4;
use wgpu::util::DeviceExt;
#[repr(C)]
#[derive(Clone, Copy, bytemuck::Pod, bytemuck::Zeroable, Debug)]
pub struct Mesh3DVertex {
pub position: [f32; 3],
pub uv: [f32; 2],
}
#[derive(Clone)]
pub struct Mesh3D {
pub vertex: wgpu::Buffer,
pub index: wgpu::Buffer,
pub index_count: u32,
}
impl Mesh3D {
pub fn new(g: &Engine, vertices: &[Mesh3DVertex], indices: &[u16]) -> Self {
let state = g.backend_state();
let vertex = state
.device
.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("mesh3d.vertices"),
contents: bytemuck::cast_slice(vertices),
usage: wgpu::BufferUsages::VERTEX,
});
let index = state
.device
.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("mesh3d.indices"),
contents: bytemuck::cast_slice(indices),
usage: wgpu::BufferUsages::INDEX,
});
Self {
vertex,
index,
index_count: indices.len() as u32,
}
}
}
pub struct Mesh3DShader {
pipeline: wgpu::RenderPipeline,
uniform: wgpu::Buffer,
bind_group: wgpu::BindGroup,
}
impl Mesh3DShader {
pub fn new(g: &Engine) -> Self {
let state = g.backend_state();
let device = &state.device;
let format = state.surface_view_format;
let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
label: Some("mesh3d.shader"),
source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!(
"../assets/shaders/mesh3d.wgsl"
))),
});
let uniform = device.create_buffer(&wgpu::BufferDescriptor {
label: Some("mesh3d.uniform"),
size: std::mem::size_of::<Mat4>() as u64,
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
mapped_at_creation: false,
});
let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: Some("mesh3d.bgl"),
entries: &[wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStages::VERTEX,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Uniform,
has_dynamic_offset: false,
min_binding_size: None,
},
count: None,
}],
});
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
label: Some("mesh3d.bg"),
layout: &bind_group_layout,
entries: &[wgpu::BindGroupEntry {
binding: 0,
resource: uniform.as_entire_binding(),
}],
});
let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("mesh3d.pl"),
bind_group_layouts: &[&bind_group_layout],
push_constant_ranges: &[],
});
let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some("mesh3d.pipeline"),
layout: Some(&pipeline_layout),
cache: None,
vertex: wgpu::VertexState {
module: &shader,
entry_point: Some("vs_main"),
buffers: &[wgpu::VertexBufferLayout {
array_stride: std::mem::size_of::<Mesh3DVertex>() as wgpu::BufferAddress,
step_mode: wgpu::VertexStepMode::Vertex,
attributes: &wgpu::vertex_attr_array![0 => Float32x3, 1 => Float32x2],
}],
compilation_options: Default::default(),
},
fragment: Some(wgpu::FragmentState {
module: &shader,
entry_point: Some("fs_main"),
targets: &[Some(wgpu::ColorTargetState {
format,
blend: Some(wgpu::BlendState::REPLACE),
write_mask: wgpu::ColorWrites::ALL,
})],
compilation_options: Default::default(),
}),
primitive: wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::TriangleList,
cull_mode: Some(wgpu::Face::Back),
front_face: wgpu::FrontFace::Ccw,
..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(),
}),
multisample: wgpu::MultisampleState::default(),
multiview: None,
});
Self {
pipeline,
uniform,
bind_group,
}
}
pub fn record_to_encoder(
&mut self,
g: &Engine,
color_target: &wgpu::Texture,
depth_view: &wgpu::TextureView,
view_proj: Mat4,
draws: &[(&Mesh3D, Mat4)],
encoder: &mut wgpu::CommandEncoder,
) {
let color_view = color_target.create_view(&wgpu::TextureViewDescriptor::default());
{
let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("mesh3d.pass"),
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: &color_view,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Load,
store: wgpu::StoreOp::Store,
},
depth_slice: None,
})],
depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
view: depth_view,
depth_ops: Some(wgpu::Operations {
load: wgpu::LoadOp::Load,
store: wgpu::StoreOp::Store,
}),
stencil_ops: None,
}),
occlusion_query_set: None,
timestamp_writes: None,
});
pass.set_pipeline(&self.pipeline);
pass.set_bind_group(0, &self.bind_group, &[]);
for (mesh, model) in draws.iter() {
let mvp = view_proj * *model;
g.backend_state().queue.write_buffer(
&self.uniform,
0,
bytemuck::cast_slice(&[mvp]),
);
pass.set_vertex_buffer(0, mesh.vertex.slice(..));
pass.set_index_buffer(mesh.index.slice(..), wgpu::IndexFormat::Uint16);
pass.draw_indexed(0..mesh.index_count, 0, 0..1);
}
}
}
}