Compare commits
3 commits
53ad206e5c
...
2e6d6a7ba0
Author | SHA1 | Date | |
---|---|---|---|
2e6d6a7ba0 | |||
506c9a1146 | |||
5a27470896 |
10 changed files with 1090 additions and 329 deletions
863
Cargo.lock
generated
863
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -12,7 +12,7 @@ repository = "https://codeberg.org/Caznix/Zenyx"
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
members = [ "subcrates/renderer","subcrates/zlog"]
|
members = ["subcrates/renderer", "subcrates/zlog"]
|
||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
zlog = { path = "subcrates/zlog" }
|
zlog = { path = "subcrates/zlog" }
|
||||||
|
@ -37,14 +37,17 @@ incremental = false
|
||||||
panic = "abort"
|
panic = "abort"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
glm = "0.2.3"
|
bytemuck = "1.22.0"
|
||||||
|
cgmath = "0.18.0"
|
||||||
|
image = "0.25.6"
|
||||||
smol = "2.0.2"
|
smol = "2.0.2"
|
||||||
|
|
||||||
terminator = "0.3.2"
|
terminator = "0.3.2"
|
||||||
thiserror = "2.0.12"
|
thiserror = "2.0.12"
|
||||||
|
tobj = "4.0.3"
|
||||||
tracing = "0.1.41"
|
tracing = "0.1.41"
|
||||||
tracing-subscriber = { version = "0.3.19", features = ["env-filter"] }
|
tracing-subscriber = { version = "0.3.19", features = ["env-filter"] }
|
||||||
vulkano = "0.35.1"
|
vulkano = "0.35.1"
|
||||||
wgpu = { version = "25.0.0", features = ["spirv"] }
|
wgpu = { version = "25.0.0", features = ["spirv"] }
|
||||||
winit = "0.30.9"
|
winit = { version = "0.30.9", features = ["android-native-activity"] }
|
||||||
zlog.workspace = true
|
zlog.workspace = true
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
#version 450
|
#version 450
|
||||||
|
|
||||||
layout(location = 0) in vec3 color;
|
layout(location = 0) in vec2 tex_coords;
|
||||||
|
layout(set = 0, binding = 0) uniform texture2D t_diffuse;
|
||||||
|
layout(set = 0, binding = 1) uniform sampler s_diffuse;
|
||||||
layout(location = 0) out vec4 out_color;
|
layout(location = 0) out vec4 out_color;
|
||||||
|
// layout(group = 0, binding = 0) out texture2D;
|
||||||
void main() {
|
void main() {
|
||||||
out_color = vec4(color, 1.0);
|
out_color = texture(sampler2D(t_diffuse, s_diffuse), tex_coords);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,9 +2,14 @@
|
||||||
|
|
||||||
layout(location = 0) in vec3 position;
|
layout(location = 0) in vec3 position;
|
||||||
layout(location = 1) in vec3 color;
|
layout(location = 1) in vec3 color;
|
||||||
layout(location = 0) out vec3 out_color;
|
layout(location = 3) in vec2 tex_coords;
|
||||||
|
layout(location = 0) out vec2 tex_coord;
|
||||||
|
layout(set = 1, binding = 0) uniform UniformBufferObject {
|
||||||
|
mat4x4 projection;
|
||||||
|
} view;
|
||||||
void main() {
|
void main() {
|
||||||
gl_Position = vec4(position, 1.0);
|
gl_Position = view.projection * vec4(position, 1.0);
|
||||||
out_color = color;
|
tex_coord = tex_coords;
|
||||||
|
// gl_Position
|
||||||
|
// out_color = color;
|
||||||
}
|
}
|
||||||
|
|
53
src/camera.rs
Normal file
53
src/camera.rs
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
pub struct Camera {
|
||||||
|
pub eye: cgmath::Point3<f32>,
|
||||||
|
pub target: cgmath::Point3<f32>,
|
||||||
|
pub up: cgmath::Vector3<f32>,
|
||||||
|
pub aspect: f32,
|
||||||
|
pub fovy: f32,
|
||||||
|
pub znear: f32,
|
||||||
|
pub zfar: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Camera {
|
||||||
|
fn build_view_projection_matrix(&self) -> cgmath::Matrix4<f32> {
|
||||||
|
let view = cgmath::Matrix4::look_at_rh(self.eye, self.target, self.up);
|
||||||
|
|
||||||
|
let proj = cgmath::perspective(cgmath::Deg(self.fovy), self.aspect, self.znear, self.zfar);
|
||||||
|
|
||||||
|
OPENGL_TO_WGPU_MATRIX * proj * view
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_aspect(&mut self, aspect: f32) {
|
||||||
|
self.aspect = aspect;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
|
pub const OPENGL_TO_WGPU_MATRIX: cgmath::Matrix4<f32> = cgmath::Matrix4::new(
|
||||||
|
1.0, 0.0, 0.0, 0.0,
|
||||||
|
0.0, 1.0, 0.0, 0.0,
|
||||||
|
0.0, 0.0, 0.5, 0.5,
|
||||||
|
0.0, 0.0, 0.0, 1.0,
|
||||||
|
);
|
||||||
|
|
||||||
|
unsafe impl bytemuck::Pod for CameraUniform {}
|
||||||
|
unsafe impl bytemuck::Zeroable for CameraUniform {}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct CameraUniform {
|
||||||
|
view_proj: cgmath::Matrix4<f32>,
|
||||||
|
}
|
||||||
|
impl Default for CameraUniform {
|
||||||
|
fn default() -> CameraUniform {
|
||||||
|
use cgmath::SquareMatrix;
|
||||||
|
Self {
|
||||||
|
view_proj: cgmath::Matrix4::identity(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl CameraUniform {
|
||||||
|
pub fn update_view_proj(&mut self, camera: &Camera) {
|
||||||
|
self.view_proj = camera.build_view_projection_matrix();
|
||||||
|
}
|
||||||
|
}
|
329
src/main.rs
329
src/main.rs
|
@ -1,18 +1,22 @@
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use std::sync::LazyLock;
|
use std::sync::Arc;
|
||||||
use std::{collections::HashMap, sync::Arc};
|
|
||||||
|
|
||||||
use terminator::Terminator;
|
use bytemuck::bytes_of;
|
||||||
use vulkano::buffer::BufferUsage;
|
use tracing::info;
|
||||||
use wgpu::util::{DeviceExt, RenderEncoder};
|
use wgpu::util::DeviceExt;
|
||||||
use wgpu::{
|
use wgpu::{
|
||||||
Backends, BufferSlice, BufferUsages, FragmentState, IndexFormat, Instance, InstanceDescriptor,
|
Backends, BufferUsages, FragmentState, IndexFormat, Instance, InstanceDescriptor,
|
||||||
PipelineCompilationOptions, Surface,
|
PipelineCompilationOptions,
|
||||||
};
|
};
|
||||||
use winit::application::ApplicationHandler;
|
use winit::application::ApplicationHandler;
|
||||||
use winit::event_loop::EventLoop;
|
use winit::event::{ElementState, MouseButton};
|
||||||
|
use winit::event_loop::{ActiveEventLoop, EventLoop};
|
||||||
use winit::window::{Window, WindowAttributes, WindowId};
|
use winit::window::{Window, WindowAttributes, WindowId};
|
||||||
use zlog::{config::LoggerConfig, query::LogQuery};
|
use zlog::LogLevel;
|
||||||
|
use zlog::config::LoggerConfig;
|
||||||
|
pub mod camera;
|
||||||
|
pub mod model;
|
||||||
|
pub mod texture;
|
||||||
struct WindowContext<'window> {
|
struct WindowContext<'window> {
|
||||||
window: Arc<Window>,
|
window: Arc<Window>,
|
||||||
renderer: WgpuRenderer<'window>,
|
renderer: WgpuRenderer<'window>,
|
||||||
|
@ -31,10 +35,52 @@ struct WgpuRenderer<'surface> {
|
||||||
surface: wgpu::Surface<'surface>,
|
surface: wgpu::Surface<'surface>,
|
||||||
surface_config: wgpu::SurfaceConfiguration,
|
surface_config: wgpu::SurfaceConfiguration,
|
||||||
render_pipeline: wgpu::RenderPipeline,
|
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> {
|
struct App<'window> {
|
||||||
state: WgpuState,
|
state: WgpuState,
|
||||||
windows: BTreeMap<WindowId, WindowContext<'window>>,
|
windows: BTreeMap<WindowId, WindowContext<'window>>,
|
||||||
|
@ -47,19 +93,41 @@ impl App<'_> {
|
||||||
windows: BTreeMap::new(),
|
windows: BTreeMap::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn spawn_window(&mut self, event_loop: &ActiveEventLoop) {
|
||||||
|
let attr = WindowAttributes::default()
|
||||||
|
.with_title("Zenyx - SubWindow")
|
||||||
|
.with_min_inner_size(winit::dpi::LogicalSize::new(1, 1));
|
||||||
|
|
||||||
|
let window = event_loop.create_window(attr).unwrap();
|
||||||
|
let window = Arc::new(window);
|
||||||
|
let renderer = self.state.create_renderer(window.clone());
|
||||||
|
let window_ctx = WindowContext {
|
||||||
|
renderer: smol::block_on(renderer),
|
||||||
|
window: window.clone(),
|
||||||
|
};
|
||||||
|
let window_id = window.id();
|
||||||
|
self.windows.insert(window_id, window_ctx);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct WgpuState {
|
struct WgpuState {
|
||||||
instance: wgpu::Instance,
|
instance: wgpu::Instance,
|
||||||
}
|
}
|
||||||
|
unsafe impl bytemuck::Pod for Vertex {}
|
||||||
|
unsafe impl bytemuck::Zeroable for Vertex {}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
struct Vertex {
|
struct Vertex {
|
||||||
position: glm::Vec3,
|
position: cgmath::Vector3<f32>,
|
||||||
color: glm::Vec3,
|
color: cgmath::Vector3<f32>,
|
||||||
|
normal: cgmath::Vector3<f32>,
|
||||||
|
tex_coords: cgmath::Vector2<f32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Vertex {
|
impl Vertex {
|
||||||
const ATTRIBS: [wgpu::VertexAttribute; 2] = [
|
const ATTRIBS: [wgpu::VertexAttribute; 4] = [
|
||||||
wgpu::VertexAttribute {
|
wgpu::VertexAttribute {
|
||||||
offset: 0,
|
offset: 0,
|
||||||
shader_location: 0,
|
shader_location: 0,
|
||||||
|
@ -70,6 +138,16 @@ impl Vertex {
|
||||||
shader_location: 1,
|
shader_location: 1,
|
||||||
format: wgpu::VertexFormat::Float32x3,
|
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> {
|
const fn desc<'a>() -> wgpu::VertexBufferLayout<'a> {
|
||||||
|
@ -80,7 +158,7 @@ impl Vertex {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
static ICON: &[u8] = include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/assets/Badge.png"));
|
||||||
impl WgpuState {
|
impl WgpuState {
|
||||||
fn new() -> Self {
|
fn new() -> Self {
|
||||||
let backends = Backends::PRIMARY;
|
let backends = Backends::PRIMARY;
|
||||||
|
@ -109,31 +187,69 @@ impl WgpuState {
|
||||||
let size = window.inner_size();
|
let size = window.inner_size();
|
||||||
let width = size.width.max(1);
|
let width = size.width.max(1);
|
||||||
let height = size.height.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))]
|
#[repr(align(4))]
|
||||||
struct ShaderCode<const N: usize>([u8; N]);
|
struct ShaderCode<const N: usize>([u8; N]);
|
||||||
let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
||||||
label: Some("Pipeline Layout"),
|
label: Some("Pipeline Layout"),
|
||||||
bind_group_layouts: &[],
|
bind_group_layouts: &[&texture_bind_group_layout, &camera_bind_group_layout],
|
||||||
push_constant_ranges: &[],
|
push_constant_ranges: &[],
|
||||||
});
|
});
|
||||||
|
|
||||||
let vert_shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
|
let vert_shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
|
||||||
label: Some("Vertex Shader"),
|
label: Some("Vertex Shader"),
|
||||||
source: unsafe {
|
source: unsafe {
|
||||||
static shader_code: &[u8] =
|
static SHADER_CODE: &[u8] =
|
||||||
&ShaderCode(*include_bytes!(concat!(env!("OUT_DIR"), "/vert.spv"))).0;
|
&ShaderCode(*include_bytes!(concat!(env!("OUT_DIR"), "/vert.spv"))).0;
|
||||||
// assert!(bytes.len() % 4 == 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))
|
wgpu::ShaderSource::SpirV(std::borrow::Cow::Borrowed(shader))
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
let frag_shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
|
let frag_shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
|
||||||
label: Some("Fragment Shader"),
|
label: Some("Fragment Shader"),
|
||||||
source: unsafe {
|
source: unsafe {
|
||||||
static shader_code: &[u8] =
|
static SHADER_CODE: &[u8] =
|
||||||
&ShaderCode(*include_bytes!(concat!(env!("OUT_DIR"), "/frag.spv"))).0;
|
&ShaderCode(*include_bytes!(concat!(env!("OUT_DIR"), "/frag.spv"))).0;
|
||||||
// assert!(bytes.len() % 4 == 0);
|
debug_assert!(SHADER_CODE.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))
|
wgpu::ShaderSource::SpirV(std::borrow::Cow::Borrowed(shader))
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -185,7 +301,6 @@ impl WgpuState {
|
||||||
} else {
|
} else {
|
||||||
wgpu::PresentMode::Fifo
|
wgpu::PresentMode::Fifo
|
||||||
};
|
};
|
||||||
|
|
||||||
let surface_config = wgpu::SurfaceConfiguration {
|
let surface_config = wgpu::SurfaceConfiguration {
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
|
@ -198,36 +313,114 @@ impl WgpuState {
|
||||||
};
|
};
|
||||||
|
|
||||||
surface.configure(&device, &surface_config);
|
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);
|
let render_pipeline = device.create_render_pipeline(&pipeline_descriptor);
|
||||||
WgpuRenderer {
|
WgpuRenderer {
|
||||||
surface,
|
surface,
|
||||||
surface_config,
|
surface_config,
|
||||||
|
diffuse_bind_group,
|
||||||
render_pipeline,
|
render_pipeline,
|
||||||
device,
|
device,
|
||||||
|
vertex_buffer,
|
||||||
|
index_buffer,
|
||||||
|
camera,
|
||||||
queue,
|
queue,
|
||||||
|
camera_uniform,
|
||||||
|
camera_buffer,
|
||||||
|
camera_bind_group,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
static VERTICES: LazyLock<[Vertex; 3]> = std::sync::LazyLock::new(|| {
|
static VERTICES: [Vertex; 4] = {
|
||||||
[
|
[
|
||||||
Vertex {
|
Vertex {
|
||||||
position: glm::vec3(-0.6, -0.5, 0.0),
|
position: cgmath::vec3(-0.5, -0.5, 0.0),
|
||||||
color: glm::vec3(1.0, 0.0, 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 {
|
Vertex {
|
||||||
position: glm::vec3(0.6, -0.5, 0.0),
|
position: cgmath::vec3(0.5, -0.5, 0.0),
|
||||||
color: glm::vec3(0.0, 1.0, 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 {
|
Vertex {
|
||||||
position: glm::vec3(0.0, 0.5, 0.0),
|
position: cgmath::vec3(0.5, 0.5, 0.0),
|
||||||
color: glm::vec3(0.0, 0.0, 1.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(
|
fn window_event(
|
||||||
&mut self,
|
&mut self,
|
||||||
event_loop: &winit::event_loop::ActiveEventLoop,
|
event_loop: &winit::event_loop::ActiveEventLoop,
|
||||||
|
@ -236,62 +429,42 @@ impl<'window> ApplicationHandler for App<'window> {
|
||||||
) {
|
) {
|
||||||
match event {
|
match event {
|
||||||
winit::event::WindowEvent::RedrawRequested => {
|
winit::event::WindowEvent::RedrawRequested => {
|
||||||
let window_ctx = self.windows.get(&window_id).unwrap();
|
let window_ctx = self.windows.get_mut(&window_id).unwrap();
|
||||||
let surface_texture = window_ctx.renderer.surface.get_current_texture().unwrap();
|
window_ctx.renderer.draw()
|
||||||
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()
|
|
||||||
}
|
}
|
||||||
winit::event::WindowEvent::CloseRequested => {
|
winit::event::WindowEvent::CloseRequested => {
|
||||||
|
let _ = self.windows.remove(&window_id);
|
||||||
|
if self.windows.is_empty() {
|
||||||
event_loop.exit();
|
event_loop.exit();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
winit::event::WindowEvent::MouseInput { state, button, .. } => {
|
||||||
|
if button == MouseButton::Left && state == ElementState::Pressed {
|
||||||
|
self.spawn_window(event_loop);
|
||||||
|
}
|
||||||
|
}
|
||||||
winit::event::WindowEvent::Resized(size) => {
|
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 {
|
if size.width == 0 || size.height == 0 {
|
||||||
return;
|
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();
|
let mut new_config = window_ctx.renderer.surface_config.clone();
|
||||||
new_config.width = size.width;
|
new_config.width = size.width;
|
||||||
new_config.height = size.height;
|
new_config.height = size.height;
|
||||||
|
|
||||||
window_ctx
|
window_ctx
|
||||||
.renderer
|
.renderer
|
||||||
.surface
|
.surface
|
||||||
|
@ -327,6 +500,7 @@ fn main() -> Result<(), terminator::Terminator> {
|
||||||
.log_to_stdout(true)
|
.log_to_stdout(true)
|
||||||
.file_include_time(true)
|
.file_include_time(true)
|
||||||
.log_to_file(true)
|
.log_to_file(true)
|
||||||
|
.level(LogLevel::Info)
|
||||||
.log_path("zenyx.log");
|
.log_path("zenyx.log");
|
||||||
let _logger = zlog::Logger::new(config);
|
let _logger = zlog::Logger::new(config);
|
||||||
let event_loop = EventLoop::new()?;
|
let event_loop = EventLoop::new()?;
|
||||||
|
@ -334,5 +508,6 @@ fn main() -> Result<(), terminator::Terminator> {
|
||||||
|
|
||||||
let mut app = App::new();
|
let mut app = App::new();
|
||||||
event_loop.run_app(&mut app)?;
|
event_loop.run_app(&mut app)?;
|
||||||
|
info!("Exiting...");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
14
src/model.rs
Normal file
14
src/model.rs
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
use crate::texture::Texture;
|
||||||
|
pub struct Material {
|
||||||
|
pub name: String,
|
||||||
|
pub diffuse_texture: Texture,
|
||||||
|
pub bind_group: wgpu::BindGroup,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Mesh {
|
||||||
|
pub name: String,
|
||||||
|
pub vertex_buffer: wgpu::Buffer,
|
||||||
|
pub index_buffer: wgpu::Buffer,
|
||||||
|
pub num_elements: u32,
|
||||||
|
pub material: usize,
|
||||||
|
}
|
87
src/texture.rs
Normal file
87
src/texture.rs
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
use image::GenericImageView;
|
||||||
|
use thiserror::Error;
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum TextureError {
|
||||||
|
#[error("Failed to load image")]
|
||||||
|
ImageError(
|
||||||
|
#[from]
|
||||||
|
#[source]
|
||||||
|
image::ImageError,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
pub struct Texture {
|
||||||
|
#[allow(unused)]
|
||||||
|
pub texture: wgpu::Texture,
|
||||||
|
pub view: wgpu::TextureView,
|
||||||
|
pub sampler: wgpu::Sampler,
|
||||||
|
}
|
||||||
|
impl Texture {
|
||||||
|
pub fn from_bytes(
|
||||||
|
device: &wgpu::Device,
|
||||||
|
queue: &wgpu::Queue,
|
||||||
|
bytes: &[u8],
|
||||||
|
label: &str,
|
||||||
|
) -> Result<Self, TextureError> {
|
||||||
|
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>,
|
||||||
|
) -> Result<Self, TextureError> {
|
||||||
|
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::TexelCopyTextureInfo {
|
||||||
|
aspect: wgpu::TextureAspect::All,
|
||||||
|
texture: &texture,
|
||||||
|
mip_level: 0,
|
||||||
|
origin: wgpu::Origin3d::ZERO,
|
||||||
|
},
|
||||||
|
&rgba,
|
||||||
|
wgpu::TexelCopyBufferLayout {
|
||||||
|
offset: 0,
|
||||||
|
bytes_per_row: Some(4 * dimensions.0),
|
||||||
|
rows_per_image: Some(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,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,18 +4,19 @@ use crate::LogLevel;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct LoggerConfig {
|
pub struct LoggerConfig {
|
||||||
pub(crate) log_level: LogLevel,
|
pub(crate) log_level: Option<LogLevel>,
|
||||||
pub(crate) log_to_file: bool,
|
pub(crate) log_to_file: bool,
|
||||||
pub(crate) log_file_path: PathBuf,
|
pub(crate) log_file_path: PathBuf,
|
||||||
pub(crate) log_to_stdout: bool,
|
pub(crate) log_to_stdout: bool,
|
||||||
pub(crate) stdout_color: bool,
|
pub(crate) stdout_color: bool,
|
||||||
pub(crate) stdout_include_time: bool,
|
pub(crate) stdout_include_time: bool,
|
||||||
pub(crate) file_include_time: bool,
|
pub(crate) file_include_time: bool,
|
||||||
|
pub(crate) crate_max_level: Option<LogLevel>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LoggerConfig {
|
impl LoggerConfig {
|
||||||
pub fn level(mut self, level: LogLevel) -> Self {
|
pub fn level(mut self, level: LogLevel) -> Self {
|
||||||
self.log_level = level;
|
self.log_level = Some(level);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,7 +54,8 @@ impl LoggerConfig {
|
||||||
impl Default for LoggerConfig {
|
impl Default for LoggerConfig {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
log_level: LogLevel::Debug,
|
log_level: None,
|
||||||
|
crate_max_level: None,
|
||||||
log_to_file: true,
|
log_to_file: true,
|
||||||
log_file_path: "app.log".into(),
|
log_file_path: "app.log".into(),
|
||||||
log_to_stdout: true,
|
log_to_stdout: true,
|
||||||
|
|
|
@ -18,7 +18,7 @@ use std::{
|
||||||
|
|
||||||
use config::LoggerConfig;
|
use config::LoggerConfig;
|
||||||
use query::LogQuery;
|
use query::LogQuery;
|
||||||
use tracing::{Event, Level, Subscriber};
|
use tracing::{Event, Level, Subscriber, level_filters::LevelFilter, subscriber::DefaultGuard};
|
||||||
use tracing_subscriber::{
|
use tracing_subscriber::{
|
||||||
layer::{Context, Layer, SubscriberExt},
|
layer::{Context, Layer, SubscriberExt},
|
||||||
registry::LookupSpan,
|
registry::LookupSpan,
|
||||||
|
@ -75,7 +75,9 @@ impl BufferLayer {
|
||||||
impl Drop for BufferLayer {
|
impl Drop for BufferLayer {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
for tx in &self.senders {
|
for tx in &self.senders {
|
||||||
tx.send(LogEvent::Shutdown).unwrap();
|
if let Err(e) = tx.send(LogEvent::Shutdown) {
|
||||||
|
panic!("{e}")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
self.senders.clear();
|
self.senders.clear();
|
||||||
}
|
}
|
||||||
|
@ -142,14 +144,14 @@ pub enum LogLevel {
|
||||||
Trace,
|
Trace,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<LogLevel> for tracing::Level {
|
impl From<LogLevel> for LevelFilter {
|
||||||
fn from(level: LogLevel) -> Self {
|
fn from(level: LogLevel) -> Self {
|
||||||
match level {
|
match level {
|
||||||
LogLevel::Error => Level::ERROR,
|
LogLevel::Error => LevelFilter::ERROR,
|
||||||
LogLevel::Info => Level::INFO,
|
LogLevel::Info => LevelFilter::INFO,
|
||||||
LogLevel::Debug => Level::DEBUG,
|
LogLevel::Debug => LevelFilter::DEBUG,
|
||||||
LogLevel::Trace => Level::TRACE,
|
LogLevel::Trace => LevelFilter::TRACE,
|
||||||
LogLevel::Warn => Level::WARN,
|
LogLevel::Warn => LevelFilter::WARN,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -238,13 +240,22 @@ impl Logger {
|
||||||
}
|
}
|
||||||
|
|
||||||
let buffer_layer = BufferLayer::new(log_entries.clone(), senders);
|
let buffer_layer = BufferLayer::new(log_entries.clone(), senders);
|
||||||
let subscriber = tracing_subscriber::registry().with(buffer_layer);
|
|
||||||
let guard = subscriber.set_default();
|
|
||||||
|
|
||||||
Logger {
|
let mk_logger = |guard: DefaultGuard| Logger {
|
||||||
subscriber_guard: Some(guard),
|
subscriber_guard: Some(guard),
|
||||||
log_entries,
|
log_entries,
|
||||||
_handles: handles,
|
_handles: handles,
|
||||||
|
};
|
||||||
|
if let Some(level) = config.log_level {
|
||||||
|
let subscriber = tracing_subscriber::registry()
|
||||||
|
.with(buffer_layer)
|
||||||
|
.with(LevelFilter::from(level));
|
||||||
|
let guard = subscriber.set_default();
|
||||||
|
mk_logger(guard)
|
||||||
|
} else {
|
||||||
|
let subscriber = tracing_subscriber::registry().with(buffer_layer);
|
||||||
|
let guard = subscriber.set_default();
|
||||||
|
mk_logger(guard)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue