Implmenting indices, and textures

This commit is contained in:
SeanOMik 2023-04-20 02:07:11 -04:00
parent e5b6a24ae6
commit 5434c11f4a
Signed by: SeanOMik
GPG Key ID: 568F326C7EB33ACB
11 changed files with 270 additions and 39 deletions

BIN
res/happy-tree.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

View File

@ -2,27 +2,32 @@
struct VertexInput { struct VertexInput {
@location(0) position: vec3<f32>, @location(0) position: vec3<f32>,
@location(1) color: vec3<f32>, @location(1) tex_coords: vec2<f32>,
}; }
struct VertexOutput { struct VertexOutput {
@builtin(position) clip_position: vec4<f32>, @builtin(position) clip_position: vec4<f32>,
@location(0) color: vec3<f32>, @location(0) tex_coords: vec2<f32>,
}; }
@vertex @vertex
fn vs_main( fn vs_main(
model: VertexInput, model: VertexInput,
) -> VertexOutput { ) -> VertexOutput {
var out: VertexOutput; var out: VertexOutput;
out.color = model.color; out.tex_coords = model.tex_coords;
out.clip_position = vec4<f32>(model.position, 1.0); out.clip_position = vec4<f32>(model.position, 1.0);
return out; return out;
} }
// Fragment shader // Fragment shader
@group(0) @binding(0)
var t_diffuse: texture_2d<f32>;
@group(0)@binding(1)
var s_diffuse: sampler;
@fragment @fragment
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> { fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
return vec4<f32>(in.color, 1.0); return textureSample(t_diffuse, s_diffuse, in.tex_coords);
} }

View File

@ -160,7 +160,7 @@ impl<'a, 'b> Game<'static, 'static> {
let filter = FilterFn::new(|metadata| { let filter = FilterFn::new(|metadata| {
metadata.module_path() metadata.module_path()
.unwrap_or_else(|| metadata.target()) .unwrap_or_else(|| metadata.target())
.starts_with("lyra_engine") && (LevelFilter::INFO >= metadata.level().to_owned()) .starts_with("lyra_engine") && (LevelFilter::DEBUG >= metadata.level().to_owned())
}); });
let layer = tracing_subscriber::fmt::layer(); let layer = tracing_subscriber::fmt::layer();

29
src/render/mesh.rs Normal file
View File

@ -0,0 +1,29 @@
use wgpu::{BindGroup, BindGroupLayout};
use super::{vertex::Vertex, render_buffer::{BufferStorage}};
pub struct Mesh {
//material
//texture: Option<Texture>,
pub vertices: Vec<Vertex>,
pub indices: Option<Vec<u16>>,
pub buffer_vertex: BufferStorage,
pub buffer_indices: Option<BufferStorage>, // TOOD: make optional
pub texture_bindgroup: Option<BindGroup>,
pub texture_layout: Option<BindGroupLayout>
}
impl Mesh {
pub fn new(vertices: Vec<Vertex>, indices: Option<Vec<u16>>, buffer_vertex: BufferStorage, buffer_indices: Option<BufferStorage>, texture_bindgroup: Option<BindGroup>, texture_layout: Option<BindGroupLayout>) -> Self {
Self {
vertices,
indices,
buffer_vertex,
buffer_indices,
texture_bindgroup,
texture_layout,
}
}
}

View File

@ -3,3 +3,6 @@ pub mod render_pipeline;
pub mod vertex; pub mod vertex;
pub mod desc_buf_lay; pub mod desc_buf_lay;
pub mod render_buffer; pub mod render_buffer;
pub mod render_job;
pub mod mesh;
pub mod texture;

View File

@ -1,15 +1,18 @@
use super::{desc_buf_lay::DescVertexBufferLayout, vertex::Vertex}; use super::{desc_buf_lay::DescVertexBufferLayout, vertex::Vertex};
#[allow(dead_code)]
#[derive(Debug)] #[derive(Debug)]
pub enum RenderBuffer { pub enum RenderBuffer {
VertexBuffer(BufferStorage), VertexBuffer(BufferStorage),
VertexIndexBufferPair((BufferStorage, BufferStorage)),
} }
#[allow(dead_code)]
impl RenderBuffer { impl RenderBuffer {
pub fn desc<'a>(&self) -> wgpu::VertexBufferLayout<'a> { pub fn desc<'a>(&self) -> Option<wgpu::VertexBufferLayout<'a>> {
match self { match self {
RenderBuffer::VertexBuffer(b) => Vertex::desc(), RenderBuffer::VertexBuffer(_) => Some(Vertex::desc()),
_ => panic!("Cannot create a VertexBufferLayout for {:?}!", *self) RenderBuffer::VertexIndexBufferPair(_) => Some(Vertex::desc()),
} }
} }
} }
@ -18,13 +21,16 @@ impl RenderBuffer {
pub struct BufferStorage { pub struct BufferStorage {
buffer: wgpu::Buffer, buffer: wgpu::Buffer,
slot: u32, slot: u32,
count: Option<u32>
} }
#[allow(dead_code)]
impl BufferStorage { impl BufferStorage {
pub fn new(buffer: wgpu::Buffer, slot: u32) -> Self { pub fn new(buffer: wgpu::Buffer, slot: u32, count: Option<u32>) -> Self {
Self { Self {
buffer, buffer,
slot, slot,
count,
} }
} }
@ -39,4 +45,8 @@ impl BufferStorage {
pub fn slot(&self) -> u32 { pub fn slot(&self) -> u32 {
self.slot self.slot
} }
pub fn count(&self) -> Option<u32> {
self.count
}
} }

17
src/render/render_job.rs Normal file
View File

@ -0,0 +1,17 @@
use super::{mesh::Mesh};
pub struct RenderJob {
mesh: Mesh
}
impl RenderJob {
pub fn new(mesh: Mesh) -> Self {
Self {
mesh,
}
}
pub fn mesh(&self)-> &Mesh {
&self.mesh
}
}

View File

@ -1,25 +1,33 @@
use std::ops::Range; use std::ops::Range;
use wgpu::{TextureFormat, PipelineLayout, RenderPipeline, VertexBufferLayout, RenderPass}; use wgpu::{PipelineLayout, RenderPipeline, RenderPass};
use super::render_buffer::RenderBuffer; use super::{render_job::RenderJob, vertex::Vertex, desc_buf_lay::DescVertexBufferLayout};
pub struct FullRenderPipeline { pub struct FullRenderPipeline {
layout: PipelineLayout, layout: PipelineLayout,
wgpu_pipeline: RenderPipeline, wgpu_pipeline: RenderPipeline,
buffers: Vec<RenderBuffer>, jobs: Vec<RenderJob>,
} }
impl FullRenderPipeline { impl FullRenderPipeline {
pub fn new(device: &wgpu::Device, config: &wgpu::SurfaceConfiguration, shader: &wgpu::ShaderModule, buffers: Vec<RenderBuffer>) -> Self { pub fn new(device: &wgpu::Device, config: &wgpu::SurfaceConfiguration, shader: &wgpu::ShaderModule, jobs: Vec<RenderJob>) -> Self {
let buffer_layouts: Vec<VertexBufferLayout> = buffers.iter().map(|b| match b { // Extract the layouts from all the jobs
RenderBuffer::VertexBuffer(_) => b.desc() let mut buffer_layouts = vec![];
}).collect(); let mut bind_group_layouts = vec![];
for job in jobs.iter() {
// Push layout for the vertex buffer, index buffer doesn't need one
buffer_layouts.push(Vertex::desc());
if let Some(layout) = job.mesh().texture_layout.as_ref() {
bind_group_layouts.push(layout);
}
}
let render_pipeline_layout = let render_pipeline_layout =
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("Render Pipeline Layout"), label: Some("Render Pipeline Layout"),
bind_group_layouts: &[], bind_group_layouts: &bind_group_layouts,
push_constant_ranges: &[], push_constant_ranges: &[],
}); });
@ -64,14 +72,16 @@ impl FullRenderPipeline {
Self { Self {
layout: render_pipeline_layout, layout: render_pipeline_layout,
wgpu_pipeline: render_pipeline, wgpu_pipeline: render_pipeline,
buffers, jobs,
} }
} }
#[allow(dead_code)]
pub fn get_layout(&self) -> &PipelineLayout { pub fn get_layout(&self) -> &PipelineLayout {
&self.layout &self.layout
} }
#[allow(dead_code)]
pub fn get_wgpu_pipeline(&self) -> &RenderPipeline { pub fn get_wgpu_pipeline(&self) -> &RenderPipeline {
&self.wgpu_pipeline &self.wgpu_pipeline
} }
@ -79,15 +89,23 @@ impl FullRenderPipeline {
pub fn render<'a>(&'a self, pass: &mut RenderPass<'a>, vertices: Range<u32>, instances: Range<u32>) { pub fn render<'a>(&'a self, pass: &mut RenderPass<'a>, vertices: Range<u32>, instances: Range<u32>) {
pass.set_pipeline(&self.wgpu_pipeline); pass.set_pipeline(&self.wgpu_pipeline);
for buffer in self.buffers.iter() { for job in self.jobs.iter() {
match buffer { let mesh = job.mesh();
RenderBuffer::VertexBuffer(b) => {
pass.set_vertex_buffer(b.slot(), b.buffer().slice(..)); if let Some(tex) = mesh.texture_bindgroup.as_ref() {
}, pass.set_bind_group(0, &tex, &[]);
_ => {}
}
} }
pass.draw(vertices, instances); if let Some(indices) = mesh.buffer_indices.as_ref() {
let indices_len = indices.count().unwrap(); // index buffers will have count, if not thats a bug
pass.set_vertex_buffer(mesh.buffer_vertex.slot(), mesh.buffer_vertex.buffer().slice(..));
pass.set_index_buffer(indices.buffer().slice(..), wgpu::IndexFormat::Uint16);
pass.draw_indexed(0..indices_len, 0, instances.clone());
} else {
pass.set_vertex_buffer(mesh.buffer_vertex.slot(), mesh.buffer_vertex.buffer().slice(..));
pass.draw(vertices.clone(), instances.clone());
}
}
} }
} }

View File

@ -4,13 +4,12 @@ use std::borrow::Cow;
use async_trait::async_trait; use async_trait::async_trait;
use wgpu::util::DeviceExt; use wgpu::util::DeviceExt;
//use winit::{window::Window, event::WindowEvent}; use winit::window::Window;
//use winit::window::Window;
use winit::{window::Window, event::WindowEvent};
use crate::resources; use crate::resources;
use super::{render_pipeline::FullRenderPipeline, vertex::{Vertex, VERTICES}, desc_buf_lay::DescVertexBufferLayout, render_buffer::{BufferStorage, RenderBuffer}}; use super::texture::Texture;
use super::{render_pipeline::FullRenderPipeline, vertex::{VERTICES}, render_buffer::BufferStorage, render_job::RenderJob, mesh::Mesh};
#[async_trait] #[async_trait]
pub trait Renderer { pub trait Renderer {
@ -90,6 +89,51 @@ impl BasicRenderer {
surface.configure(&device, &config); surface.configure(&device, &config);
let diffuse_bytes = include_bytes!("../../res/happy-tree.png");
let diffuse_texture = Texture::from_bytes(&device, &queue, diffuse_bytes, "happy-tree.png").unwrap();
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,
// This should match the filterable field of the
// corresponding Texture entry above.
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
count: None,
},
],
label: Some("texture_bind_group_layout"),
});
let diffuse_bind_group = device.create_bind_group(
&wgpu::BindGroupDescriptor {
layout: &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: Some("diffuse_bind_group"),
}
);
let shader_src = resources::load_string("shader.wgsl").await.expect("Failed to load shader!"); let shader_src = resources::load_string("shader.wgsl").await.expect("Failed to load shader!");
let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
@ -105,9 +149,24 @@ impl BasicRenderer {
} }
); );
let index_buffer = device.create_buffer_init(
&wgpu::util::BufferInitDescriptor {
label: Some("Index Buffer"),
contents: bytemuck::cast_slice(super::vertex::INDICES),
usage: wgpu::BufferUsages::INDEX,
}
);
let job = RenderJob::new(Mesh::new(
super::vertex::VERTICES.to_vec(), Some(super::vertex::INDICES.to_vec()),
BufferStorage::new(vertex_buffer, 0, None),
Some(BufferStorage::new(index_buffer, 0, Some(super::vertex::INDICES.len() as u32))),
Some(diffuse_bind_group), Some(texture_bind_group_layout)
));
let pipelines = vec![ let pipelines = vec![
FullRenderPipeline::new(&device, &config, &shader, FullRenderPipeline::new(&device, &config, &shader,
vec![RenderBuffer::VertexBuffer(BufferStorage::new(vertex_buffer, 0))]) vec![job,])
]; ];
Self { Self {

81
src/render/texture.rs Normal file
View File

@ -0,0 +1,81 @@
use image::GenericImageView;
#[allow(dead_code)]
pub struct Texture {
texture: wgpu::Texture,
view: wgpu::TextureView,
sampler: wgpu::Sampler,
}
impl Texture {
pub fn from_bytes(device: &wgpu::Device, queue: &wgpu::Queue, bytes: &[u8], label: &str) -> anyhow::Result<Self> {
let img = image::load_from_memory(bytes)?;
Self::from_image(device, queue, &img, Some(label))
}
pub fn from_image(device: &wgpu::Device, queue: &wgpu::Queue, img: &image::DynamicImage, label: Option<&str> ) -> anyhow::Result<Self> {
let rgba = img.to_rgba8();
let dimensions = img.dimensions();
let size = wgpu::Extent3d {
width: dimensions.0,
height: dimensions.1,
depth_or_array_layers: 1,
};
let texture = device.create_texture(
&wgpu::TextureDescriptor {
label,
size,
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: wgpu::TextureFormat::Rgba8UnormSrgb,
usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
view_formats: &[],
}
);
queue.write_texture(
wgpu::ImageCopyTexture {
aspect: wgpu::TextureAspect::All,
texture: &texture,
mip_level: 0,
origin: wgpu::Origin3d::ZERO,
},
&rgba,
wgpu::ImageDataLayout {
offset: 0,
bytes_per_row: std::num::NonZeroU32::new(4 * dimensions.0),
rows_per_image: std::num::NonZeroU32::new(dimensions.1),
},
size,
);
let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
let sampler = device.create_sampler(
&wgpu::SamplerDescriptor {
address_mode_u: wgpu::AddressMode::ClampToEdge,
address_mode_v: wgpu::AddressMode::ClampToEdge,
address_mode_w: wgpu::AddressMode::ClampToEdge,
mag_filter: wgpu::FilterMode::Linear,
min_filter: wgpu::FilterMode::Nearest,
mipmap_filter: wgpu::FilterMode::Nearest,
..Default::default()
}
);
Ok(Self { texture, view, sampler })
}
pub fn texture(&self) -> &wgpu::Texture {
&self.texture
}
pub fn view(&self) -> &wgpu::TextureView {
&self.view
}
pub fn sampler(&self) -> &wgpu::Sampler {
&self.sampler
}
}

View File

@ -4,13 +4,22 @@ use super::desc_buf_lay::DescVertexBufferLayout;
#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)] #[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)]
pub struct Vertex { pub struct Vertex {
pub position: [f32; 3], pub position: [f32; 3],
pub color: [f32; 3], //pub color: [f32; 3], // TODO: add color again
tex_coords: [f32; 2]
} }
pub const VERTICES: &[Vertex] = &[ pub const VERTICES: &[Vertex] = &[
Vertex { position: [0.0, 0.5, 0.0], color: [1.0, 0.0, 0.0] }, Vertex { position: [-0.0868241, 0.49240386, 0.0], tex_coords: [0.4131759, 0.00759614], }, // A
Vertex { position: [-0.5, -0.5, 0.0], color: [0.0, 1.0, 0.0] }, Vertex { position: [-0.49513406, 0.06958647, 0.0], tex_coords: [0.0048659444, 0.43041354], }, // B
Vertex { position: [0.5, -0.5, 0.0], color: [0.0, 0.0, 1.0] }, Vertex { position: [-0.21918549, -0.44939706, 0.0], tex_coords: [0.28081453, 0.949397], }, // C
Vertex { position: [0.35966998, -0.3473291, 0.0], tex_coords: [0.85967, 0.84732914], }, // D
Vertex { position: [0.44147372, 0.2347359, 0.0], tex_coords: [0.9414737, 0.2652641], }, // E
];
pub const INDICES: &[u16] = &[
0, 1, 4,
1, 2, 4,
2, 3, 4,
]; ];
impl DescVertexBufferLayout for Vertex { impl DescVertexBufferLayout for Vertex {
@ -27,7 +36,7 @@ impl DescVertexBufferLayout for Vertex {
wgpu::VertexAttribute { wgpu::VertexAttribute {
offset: std::mem::size_of::<[f32; 3]>() as wgpu::BufferAddress, offset: std::mem::size_of::<[f32; 3]>() as wgpu::BufferAddress,
shader_location: 1, shader_location: 1,
format: wgpu::VertexFormat::Float32x3, format: wgpu::VertexFormat::Float32x2,
} }
] ]
} }