forked from nonsensical-dev/zenyx-engine
feat(rendering): rendering textures with camera
Co-authored-by: BitSyndicate <contact@bitsyndicate.de>
This commit is contained in:
parent
f6d1bfda7d
commit
d67acaadfa
9 changed files with 1037 additions and 329 deletions
307
src/main.rs
307
src/main.rs
|
@ -1,20 +1,22 @@
|
|||
use std::collections::BTreeMap;
|
||||
use std::sync::LazyLock;
|
||||
use std::{collections::HashMap, sync::Arc};
|
||||
use std::sync::Arc;
|
||||
|
||||
use terminator::Terminator;
|
||||
use vulkano::buffer::BufferUsage;
|
||||
use wgpu::util::{DeviceExt, RenderEncoder};
|
||||
use bytemuck::bytes_of;
|
||||
use tracing::info;
|
||||
use wgpu::util::DeviceExt;
|
||||
use wgpu::{
|
||||
Backends, BufferSlice, BufferUsages, FragmentState, IndexFormat, Instance, InstanceDescriptor,
|
||||
PipelineCompilationOptions, Surface,
|
||||
Backends, BufferUsages, FragmentState, IndexFormat, Instance, InstanceDescriptor,
|
||||
PipelineCompilationOptions,
|
||||
};
|
||||
use winit::application::ApplicationHandler;
|
||||
use winit::event::{ElementState, MouseButton};
|
||||
use winit::event_loop::{ActiveEventLoop, EventLoop};
|
||||
use winit::window::{Window, WindowAttributes, WindowId};
|
||||
use zlog::LogLevel;
|
||||
use zlog::{config::LoggerConfig, query::LogQuery};
|
||||
use zlog::config::LoggerConfig;
|
||||
pub mod camera;
|
||||
pub mod model;
|
||||
pub mod texture;
|
||||
struct WindowContext<'window> {
|
||||
window: Arc<Window>,
|
||||
renderer: WgpuRenderer<'window>,
|
||||
|
@ -33,10 +35,52 @@ struct WgpuRenderer<'surface> {
|
|||
surface: wgpu::Surface<'surface>,
|
||||
surface_config: wgpu::SurfaceConfiguration,
|
||||
render_pipeline: wgpu::RenderPipeline,
|
||||
vertex_buffer: wgpu::Buffer,
|
||||
index_buffer: wgpu::Buffer,
|
||||
diffuse_bind_group: wgpu::BindGroup,
|
||||
camera: camera::Camera,
|
||||
camera_uniform: camera::CameraUniform,
|
||||
camera_buffer: wgpu::Buffer,
|
||||
camera_bind_group: wgpu::BindGroup,
|
||||
}
|
||||
|
||||
impl WgpuRenderer<'_> {
|
||||
pub fn draw(&mut self) {
|
||||
let surface_texture = self.surface.get_current_texture().unwrap();
|
||||
let view = surface_texture.texture.create_view(&Default::default());
|
||||
let mut encoder =
|
||||
self.device
|
||||
.create_command_encoder(&wgpu::wgt::CommandEncoderDescriptor {
|
||||
label: Some("Render encoder"),
|
||||
});
|
||||
{
|
||||
let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||
label: Some("Render"),
|
||||
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
|
||||
view: &view,
|
||||
resolve_target: None,
|
||||
ops: wgpu::Operations {
|
||||
load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
|
||||
store: wgpu::StoreOp::Store,
|
||||
},
|
||||
})],
|
||||
depth_stencil_attachment: None,
|
||||
timestamp_writes: None,
|
||||
occlusion_query_set: None,
|
||||
});
|
||||
rpass.set_pipeline(&self.render_pipeline);
|
||||
rpass.set_bind_group(0, &self.diffuse_bind_group, &[]);
|
||||
|
||||
rpass.set_bind_group(1, &self.camera_bind_group, &[]);
|
||||
rpass.set_vertex_buffer(0, self.vertex_buffer.slice(..));
|
||||
rpass.set_index_buffer(self.index_buffer.slice(..), IndexFormat::Uint32);
|
||||
rpass.draw_indexed(0..INDICIES.len() as u32, 0, 0..1);
|
||||
}
|
||||
self.queue.submit(Some(encoder.finish()));
|
||||
surface_texture.present()
|
||||
}
|
||||
}
|
||||
|
||||
// impl WgpuRenderer {
|
||||
// pub fn new(window: Arc<Window>) {}
|
||||
struct App<'window> {
|
||||
state: WgpuState,
|
||||
windows: BTreeMap<WindowId, WindowContext<'window>>,
|
||||
|
@ -49,6 +93,7 @@ impl App<'_> {
|
|||
windows: BTreeMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn spawn_window(&mut self, event_loop: &ActiveEventLoop) {
|
||||
let attr = WindowAttributes::default()
|
||||
.with_title("Zenyx - SubWindow")
|
||||
|
@ -69,14 +114,20 @@ impl App<'_> {
|
|||
struct WgpuState {
|
||||
instance: wgpu::Instance,
|
||||
}
|
||||
unsafe impl bytemuck::Pod for Vertex {}
|
||||
unsafe impl bytemuck::Zeroable for Vertex {}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[repr(C)]
|
||||
struct Vertex {
|
||||
position: glm::Vec3,
|
||||
color: glm::Vec3,
|
||||
position: cgmath::Vector3<f32>,
|
||||
color: cgmath::Vector3<f32>,
|
||||
normal: cgmath::Vector3<f32>,
|
||||
tex_coords: cgmath::Vector2<f32>,
|
||||
}
|
||||
|
||||
impl Vertex {
|
||||
const ATTRIBS: [wgpu::VertexAttribute; 2] = [
|
||||
const ATTRIBS: [wgpu::VertexAttribute; 4] = [
|
||||
wgpu::VertexAttribute {
|
||||
offset: 0,
|
||||
shader_location: 0,
|
||||
|
@ -87,6 +138,16 @@ impl Vertex {
|
|||
shader_location: 1,
|
||||
format: wgpu::VertexFormat::Float32x3,
|
||||
},
|
||||
wgpu::VertexAttribute {
|
||||
offset: std::mem::offset_of!(Vertex, normal) as u64,
|
||||
shader_location: 2,
|
||||
format: wgpu::VertexFormat::Float32x3,
|
||||
},
|
||||
wgpu::VertexAttribute {
|
||||
offset: std::mem::offset_of!(Vertex, tex_coords) as u64,
|
||||
shader_location: 3,
|
||||
format: wgpu::VertexFormat::Float32x2,
|
||||
},
|
||||
];
|
||||
|
||||
const fn desc<'a>() -> wgpu::VertexBufferLayout<'a> {
|
||||
|
@ -97,7 +158,7 @@ impl Vertex {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
static ICON: &[u8] = include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/assets/Badge.png"));
|
||||
impl WgpuState {
|
||||
fn new() -> Self {
|
||||
let backends = Backends::PRIMARY;
|
||||
|
@ -126,31 +187,69 @@ impl WgpuState {
|
|||
let size = window.inner_size();
|
||||
let width = size.width.max(1);
|
||||
let height = size.height.max(1);
|
||||
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 camera_bind_group_layout =
|
||||
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
||||
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,
|
||||
}],
|
||||
label: Some("Camera Bind group layout"),
|
||||
});
|
||||
#[repr(align(4))]
|
||||
struct ShaderCode<const N: usize>([u8; N]);
|
||||
let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
||||
label: Some("Pipeline Layout"),
|
||||
bind_group_layouts: &[],
|
||||
bind_group_layouts: &[&texture_bind_group_layout, &camera_bind_group_layout],
|
||||
push_constant_ranges: &[],
|
||||
});
|
||||
|
||||
let vert_shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
|
||||
label: Some("Vertex Shader"),
|
||||
source: unsafe {
|
||||
static shader_code: &[u8] =
|
||||
static SHADER_CODE: &[u8] =
|
||||
&ShaderCode(*include_bytes!(concat!(env!("OUT_DIR"), "/vert.spv"))).0;
|
||||
// assert!(bytes.len() % 4 == 0);
|
||||
let shader = shader_code.align_to::<u32>().1;
|
||||
let shader = SHADER_CODE.align_to::<u32>().1;
|
||||
wgpu::ShaderSource::SpirV(std::borrow::Cow::Borrowed(shader))
|
||||
},
|
||||
});
|
||||
let frag_shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
|
||||
label: Some("Fragment Shader"),
|
||||
source: unsafe {
|
||||
static shader_code: &[u8] =
|
||||
static SHADER_CODE: &[u8] =
|
||||
&ShaderCode(*include_bytes!(concat!(env!("OUT_DIR"), "/frag.spv"))).0;
|
||||
// assert!(bytes.len() % 4 == 0);
|
||||
let shader = shader_code.align_to::<u32>().1;
|
||||
debug_assert!(SHADER_CODE.len() % 4 == 0);
|
||||
let shader = SHADER_CODE.align_to::<u32>().1;
|
||||
wgpu::ShaderSource::SpirV(std::borrow::Cow::Borrowed(shader))
|
||||
},
|
||||
});
|
||||
|
@ -202,7 +301,6 @@ impl WgpuState {
|
|||
} else {
|
||||
wgpu::PresentMode::Fifo
|
||||
};
|
||||
|
||||
let surface_config = wgpu::SurfaceConfiguration {
|
||||
width,
|
||||
height,
|
||||
|
@ -215,36 +313,114 @@ impl WgpuState {
|
|||
};
|
||||
|
||||
surface.configure(&device, &surface_config);
|
||||
let diffuse_texture =
|
||||
texture::Texture::from_bytes(&device, &queue, ICON, "zenyx-icon").unwrap();
|
||||
let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||
label: Some("Vertex buffer"),
|
||||
contents: bytemuck::cast_slice(VERTICES.as_ref()),
|
||||
usage: BufferUsages::VERTEX,
|
||||
});
|
||||
let index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||
label: Some("Index buffer"),
|
||||
contents: bytemuck::cast_slice(&INDICIES),
|
||||
usage: BufferUsages::INDEX,
|
||||
});
|
||||
|
||||
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 camera = camera::Camera {
|
||||
// position the camera 1 unit up and 2 units back
|
||||
// +z is out of the screen
|
||||
eye: (0.0, 0.0, 2.0).into(),
|
||||
// have it look at the origin
|
||||
target: (0.0, 0.0, 0.0).into(),
|
||||
// which way is "up"
|
||||
up: cgmath::Vector3::unit_y(),
|
||||
aspect: surface_config.width as f32 / surface_config.height as f32,
|
||||
fovy: 45.0,
|
||||
znear: 0.1,
|
||||
zfar: 100.0,
|
||||
};
|
||||
|
||||
let mut camera_uniform = camera::CameraUniform::default();
|
||||
camera_uniform.update_view_proj(&camera);
|
||||
|
||||
let camera_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||
label: Some("Camera Buffer"),
|
||||
contents: bytemuck::cast_slice(&[camera_uniform]),
|
||||
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
|
||||
});
|
||||
|
||||
let camera_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||
layout: &camera_bind_group_layout,
|
||||
entries: &[wgpu::BindGroupEntry {
|
||||
binding: 0,
|
||||
resource: camera_buffer.as_entire_binding(),
|
||||
}],
|
||||
label: Some("Camera Bind Group"),
|
||||
});
|
||||
|
||||
let render_pipeline = device.create_render_pipeline(&pipeline_descriptor);
|
||||
WgpuRenderer {
|
||||
surface,
|
||||
surface_config,
|
||||
diffuse_bind_group,
|
||||
render_pipeline,
|
||||
device,
|
||||
vertex_buffer,
|
||||
index_buffer,
|
||||
camera,
|
||||
queue,
|
||||
camera_uniform,
|
||||
camera_buffer,
|
||||
camera_bind_group,
|
||||
}
|
||||
}
|
||||
}
|
||||
static VERTICES: LazyLock<[Vertex; 3]> = std::sync::LazyLock::new(|| {
|
||||
static VERTICES: [Vertex; 4] = {
|
||||
[
|
||||
Vertex {
|
||||
position: glm::vec3(-0.6, -0.5, 0.0),
|
||||
color: glm::vec3(1.0, 0.0, 0.0),
|
||||
position: cgmath::vec3(-0.5, -0.5, 0.0),
|
||||
color: cgmath::vec3(1.0, 0.0, 0.0),
|
||||
normal: cgmath::vec3(0.0, 0.0, 0.0),
|
||||
tex_coords: cgmath::vec2(1.0, 0.0),
|
||||
},
|
||||
Vertex {
|
||||
position: glm::vec3(0.6, -0.5, 0.0),
|
||||
color: glm::vec3(0.0, 1.0, 0.0),
|
||||
position: cgmath::vec3(0.5, -0.5, 0.0),
|
||||
color: cgmath::vec3(0.0, 1.0, 0.0),
|
||||
normal: cgmath::vec3(0.0, 0.0, 0.0),
|
||||
tex_coords: cgmath::vec2(0.0, 0.0),
|
||||
},
|
||||
Vertex {
|
||||
position: glm::vec3(0.0, 0.5, 0.0),
|
||||
color: glm::vec3(0.0, 0.0, 1.0),
|
||||
position: cgmath::vec3(0.5, 0.5, 0.0),
|
||||
color: cgmath::vec3(0.0, 0.0, 1.0),
|
||||
normal: cgmath::vec3(0.0, 0.0, 0.0),
|
||||
tex_coords: cgmath::vec2(0.0, 1.0),
|
||||
},
|
||||
Vertex {
|
||||
position: cgmath::vec3(-0.5, 0.5, 0.0),
|
||||
color: cgmath::vec3(0.0, 0.0, 1.0),
|
||||
normal: cgmath::vec3(0.0, 0.0, 0.0),
|
||||
tex_coords: cgmath::vec2(1.0, 1.0),
|
||||
},
|
||||
]
|
||||
});
|
||||
};
|
||||
|
||||
static INDICIES: [u32; 3] = [0, 1, 2];
|
||||
static INDICIES: [u32; 6] = [0, 1, 2, 2, 3, 0];
|
||||
|
||||
impl<'window> ApplicationHandler for App<'window> {
|
||||
impl ApplicationHandler for App<'_> {
|
||||
fn window_event(
|
||||
&mut self,
|
||||
event_loop: &winit::event_loop::ActiveEventLoop,
|
||||
|
@ -253,50 +429,8 @@ impl<'window> ApplicationHandler for App<'window> {
|
|||
) {
|
||||
match event {
|
||||
winit::event::WindowEvent::RedrawRequested => {
|
||||
let window_ctx = self.windows.get(&window_id).unwrap();
|
||||
let surface_texture = window_ctx.renderer.surface.get_current_texture().unwrap();
|
||||
let view = surface_texture.texture.create_view(&Default::default());
|
||||
let mut encoder = window_ctx.renderer.device.create_command_encoder(
|
||||
&wgpu::wgt::CommandEncoderDescriptor {
|
||||
label: Some("Render encoder"),
|
||||
},
|
||||
);
|
||||
{
|
||||
let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||
label: Some("Render"),
|
||||
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
|
||||
view: &view,
|
||||
resolve_target: None,
|
||||
ops: wgpu::Operations {
|
||||
load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
|
||||
store: wgpu::StoreOp::Store,
|
||||
},
|
||||
})],
|
||||
depth_stencil_attachment: None,
|
||||
timestamp_writes: None,
|
||||
occlusion_query_set: None,
|
||||
});
|
||||
rpass.set_pipeline(&window_ctx.renderer.render_pipeline);
|
||||
let vertex_buffer = window_ctx.renderer.device.create_buffer_init(
|
||||
&wgpu::util::BufferInitDescriptor {
|
||||
label: Some("Vertex buffer"),
|
||||
contents: unsafe { VERTICES.align_to::<u8>().1 },
|
||||
usage: BufferUsages::VERTEX,
|
||||
},
|
||||
);
|
||||
let index_buffer = window_ctx.renderer.device.create_buffer_init(
|
||||
&wgpu::util::BufferInitDescriptor {
|
||||
label: Some("Index buffer"),
|
||||
contents: unsafe { INDICIES.align_to::<u8>().1 },
|
||||
usage: BufferUsages::INDEX,
|
||||
},
|
||||
);
|
||||
rpass.set_vertex_buffer(0, vertex_buffer.slice(..));
|
||||
rpass.set_index_buffer(index_buffer.slice(..), IndexFormat::Uint32);
|
||||
rpass.draw_indexed(0..INDICIES.len() as u32, 0, 0..1);
|
||||
}
|
||||
window_ctx.renderer.queue.submit(Some(encoder.finish()));
|
||||
surface_texture.present()
|
||||
let window_ctx = self.windows.get_mut(&window_id).unwrap();
|
||||
window_ctx.renderer.draw()
|
||||
}
|
||||
winit::event::WindowEvent::CloseRequested => {
|
||||
let _ = self.windows.remove(&window_id);
|
||||
|
@ -304,23 +438,33 @@ impl<'window> ApplicationHandler for App<'window> {
|
|||
event_loop.exit();
|
||||
}
|
||||
}
|
||||
winit::event::WindowEvent::MouseInput {
|
||||
device_id,
|
||||
state,
|
||||
button,
|
||||
} => {
|
||||
winit::event::WindowEvent::MouseInput { state, button, .. } => {
|
||||
if button == MouseButton::Left && state == ElementState::Pressed {
|
||||
self.spawn_window(event_loop);
|
||||
}
|
||||
}
|
||||
winit::event::WindowEvent::Resized(size) => {
|
||||
if let Some(window_ctx) = self.windows.get(&window_id) {
|
||||
if let Some(window_ctx) = self.windows.get_mut(&window_id) {
|
||||
if size.width == 0 || size.height == 0 {
|
||||
return;
|
||||
}
|
||||
window_ctx
|
||||
.renderer
|
||||
.camera
|
||||
.update_aspect(size.width as f32 / size.height as f32);
|
||||
window_ctx
|
||||
.renderer
|
||||
.camera_uniform
|
||||
.update_view_proj(&window_ctx.renderer.camera);
|
||||
window_ctx.renderer.queue.write_buffer(
|
||||
&window_ctx.renderer.camera_buffer,
|
||||
0,
|
||||
bytes_of(&window_ctx.renderer.camera_uniform),
|
||||
);
|
||||
let mut new_config = window_ctx.renderer.surface_config.clone();
|
||||
new_config.width = size.width;
|
||||
new_config.height = size.height;
|
||||
|
||||
window_ctx
|
||||
.renderer
|
||||
.surface
|
||||
|
@ -364,5 +508,6 @@ fn main() -> Result<(), terminator::Terminator> {
|
|||
|
||||
let mut app = App::new();
|
||||
event_loop.run_app(&mut app)?;
|
||||
info!("Exiting...");
|
||||
Ok(())
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue