Compare commits
No commits in common. "sample" and "master" have entirely different histories.
8
Cargo.lock
generated
8
Cargo.lock
generated
|
|
@ -6,7 +6,6 @@ version = 4
|
||||||
name = "PROJECT-NAME"
|
name = "PROJECT-NAME"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytemuck",
|
|
||||||
"glam",
|
"glam",
|
||||||
"pollster",
|
"pollster",
|
||||||
"wgpu",
|
"wgpu",
|
||||||
|
|
@ -178,9 +177,9 @@ checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bytemuck"
|
name = "bytemuck"
|
||||||
version = "1.25.2"
|
version = "1.24.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "95832e849adfb21180ccb6826a99da14e5d266ae5c2e668e1602cf234f153797"
|
checksum = "1fbdf580320f38b612e485521afda1ee26d10cc9884efaaa750d383e13e3c5f4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytemuck_derive",
|
"bytemuck_derive",
|
||||||
]
|
]
|
||||||
|
|
@ -527,9 +526,6 @@ name = "glam"
|
||||||
version = "0.30.10"
|
version = "0.30.10"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "19fc433e8437a212d1b6f1e68c7824af3aed907da60afa994e7f542d18d12aa9"
|
checksum = "19fc433e8437a212d1b6f1e68c7824af3aed907da60afa994e7f542d18d12aa9"
|
||||||
dependencies = [
|
|
||||||
"bytemuck",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "glow"
|
name = "glow"
|
||||||
|
|
|
||||||
|
|
@ -18,8 +18,7 @@ debug = false
|
||||||
opt-level = 3
|
opt-level = 3
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bytemuck = "1.25.2"
|
glam = { version = "0.30.9" }
|
||||||
glam = { version = "0.30.9", features = ["bytemuck"] }
|
|
||||||
pollster = "0.4.0"
|
pollster = "0.4.0"
|
||||||
wgpu = "27.0.1"
|
wgpu = "27.0.1"
|
||||||
winit = "0.30.12"
|
winit = "0.30.12"
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,6 @@ This example combines Qt6 GUI (C++) with WGPU rendering (Rust). It doesn’t do
|
||||||
|
|
||||||
Currently only X11 (XCB) is supported but it should be easy to extend to other platforms.
|
Currently only X11 (XCB) is supported but it should be easy to extend to other platforms.
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
This program is intended as a starting point rather than a dependency, so just copy it.
|
This program is intended as a starting point rather than a dependency, so just copy it.
|
||||||
|
|
|
||||||
1
init.sh
1
init.sh
|
|
@ -56,7 +56,6 @@ git rm -r skel-test
|
||||||
|
|
||||||
# replace the README with a stub
|
# replace the README with a stub
|
||||||
echo "# $name" >| README.md
|
echo "# $name" >| README.md
|
||||||
git rm screenshot.png
|
|
||||||
|
|
||||||
# escape HTML special characters (&, <, and >), but also sed special characters (& again, \, and /)
|
# escape HTML special characters (&, <, and >), but also sed special characters (& again, \, and /)
|
||||||
htmlname=$(echo -n "$name" | sed 's/&/\&/g; s/</\</g; s/>/\>/g; s:[\\/&]:\\&:g')
|
htmlname=$(echo -n "$name" | sed 's/&/\&/g; s/</\</g; s/>/\>/g; s:[\\/&]:\\&:g')
|
||||||
|
|
|
||||||
BIN
screenshot.png
BIN
screenshot.png
Binary file not shown.
|
Before Width: | Height: | Size: 45 KiB |
|
|
@ -1,78 +0,0 @@
|
||||||
use glam::{Affine3A, Mat3, Mat4, vec3};
|
|
||||||
|
|
||||||
pub(crate) fn rotation(yaw: f32, pitch: f32) -> Mat3 {
|
|
||||||
let (ys, yc) = yaw.sin_cos();
|
|
||||||
let (ps, pc) = pitch.sin_cos();
|
|
||||||
Mat3::from_cols_array_2d(&[[0., 0., -1.], [-1., 0., 0.], [0., 1., 0.]])
|
|
||||||
* Mat3::from_cols_array_2d(&[[pc, 0., -ps], [0., 1., 0.], [ps, 0., pc]])
|
|
||||||
* Mat3::from_cols_array_2d(&[[yc, -ys, 0.], [ys, yc, 0.], [0., 0., 1.]])
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn view(yaw: f32, pitch: f32, distance: f32) -> Affine3A {
|
|
||||||
Affine3A::from_mat3_translation(rotation(yaw, pitch), vec3(0., 0., distance))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn projection(fovy: f32, aspect: f32) -> Mat4 {
|
|
||||||
Mat4::perspective_lh(fovy, aspect, 0.1, 10.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
use glam::Vec3;
|
|
||||||
use std::f32::consts::PI;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_rotation() {
|
|
||||||
let mut ok = true;
|
|
||||||
for (yaw, pitch, x, y, z) in [
|
|
||||||
(0.0, 0.0, [0., 0., -1.], [-1., 0., 0.], [0., 1., 0.]),
|
|
||||||
(0.5, 0.0, [1., 0., 0.], [0., 0., -1.], [0., 1., 0.]),
|
|
||||||
(0.0, 0.5, [0., -1., 0.], [-1., 0., 0.], [0., 0., -1.]),
|
|
||||||
(0.5, 0.5, [1., 0., 0.], [0., -1., 0.], [0., 0., -1.]),
|
|
||||||
] {
|
|
||||||
let m = rotation(yaw * PI, pitch * PI);
|
|
||||||
if !m.abs_diff_eq(Mat3::from_cols_array_2d(&[x, y, z]), 1e-3) {
|
|
||||||
ok = false;
|
|
||||||
println!("Wrong rotation for {}, {}", 180. * yaw, 180. * pitch);
|
|
||||||
println!("X: expected: {}, actual: {}", Vec3::from_array(x), m.x_axis);
|
|
||||||
println!("Y: expected: {}, actual: {}", Vec3::from_array(y), m.y_axis);
|
|
||||||
println!("Z: expected: {}, actual: {}", Vec3::from_array(z), m.z_axis);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !ok {
|
|
||||||
panic!();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_view() {
|
|
||||||
let mut ok = true;
|
|
||||||
for (yaw, pitch, x1, y1, z1) in [
|
|
||||||
(0.0, 0.0, [0., 0., 2.], [-1., 0., 3.], [0., 1., 3.]),
|
|
||||||
(0.5, 0.0, [1., 0., 3.], [0., 0., 2.], [0., 1., 3.]),
|
|
||||||
(0.0, 0.5, [0., -1., 3.], [-1., 0., 3.], [0., 0., 2.]),
|
|
||||||
(0.5, 0.5, [1., 0., 3.], [0., -1., 3.], [0., 0., 2.]),
|
|
||||||
] {
|
|
||||||
let m = view(yaw * PI, pitch * PI, 3.);
|
|
||||||
for (label, world, expected) in
|
|
||||||
[('X', Vec3::X, x1), ('Y', Vec3::Y, y1), ('Z', Vec3::Z, z1)]
|
|
||||||
{
|
|
||||||
let expected = Vec3::from_array(expected);
|
|
||||||
let actual = m.transform_point3(world);
|
|
||||||
if !actual.abs_diff_eq(expected, 1e-3) {
|
|
||||||
ok = false;
|
|
||||||
println!(
|
|
||||||
"Wrong transform for {}, {}: for {label}, expected: {expected}, got: {actual}",
|
|
||||||
180. * yaw,
|
|
||||||
180. * pitch
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !ok {
|
|
||||||
panic!();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
105
src/lib.rs
105
src/lib.rs
|
|
@ -1,28 +1,20 @@
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
|
||||||
use glam::{Mat4, UVec2, Vec3, Vec4, vec3, vec4};
|
use glam::{UVec2, Vec4, vec4};
|
||||||
|
|
||||||
mod camera;
|
const DEPTH_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Depth16Unorm;
|
||||||
mod render;
|
const OUTPUT_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Bgra8UnormSrgb;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct RedrawArgs {
|
pub struct RedrawArgs {
|
||||||
pub background: Vec4,
|
pub background: Vec4,
|
||||||
pub cam_yaw: f32,
|
|
||||||
pub cam_pitch: f32,
|
|
||||||
pub cam_distance: f32,
|
|
||||||
pub cam_fovy: f32,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for RedrawArgs {
|
impl Default for RedrawArgs {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
background: vec4(0.25, 0.49, 0.93, 1.00),
|
background: vec4(0.05, 0.20, 0.85, 1.00),
|
||||||
cam_yaw: std::f32::consts::FRAC_PI_4,
|
|
||||||
cam_pitch: std::f32::consts::FRAC_PI_6,
|
|
||||||
cam_distance: 3.,
|
|
||||||
cam_fovy: std::f32::consts::FRAC_PI_2,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -38,64 +30,9 @@ pub struct Core {
|
||||||
queue: wgpu::Queue,
|
queue: wgpu::Queue,
|
||||||
surface: wgpu::Surface<'static>,
|
surface: wgpu::Surface<'static>,
|
||||||
|
|
||||||
mesh_pipeline: render::mesh::Pipeline,
|
|
||||||
mesh_color: render::mesh::GpuMeshIndexed,
|
|
||||||
mesh_gray: render::mesh::GpuMeshIndexed,
|
|
||||||
depth: wgpu::Texture,
|
depth: wgpu::Texture,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cube(a: f32, b: f32) -> render::mesh::CpuMeshIndexed {
|
|
||||||
let gray_level = (b + a) / 2.;
|
|
||||||
let gray = Vec3::splat(gray_level);
|
|
||||||
let color_level = (b - a) / 2.;
|
|
||||||
let mut mesh = render::mesh::CpuMeshIndexed::default();
|
|
||||||
for (u, v, w) in [
|
|
||||||
(Vec3::X, Vec3::Y, Vec3::Z),
|
|
||||||
(Vec3::Y, Vec3::Z, Vec3::X),
|
|
||||||
(Vec3::Z, Vec3::X, Vec3::Y),
|
|
||||||
] {
|
|
||||||
for m in [1., -1.] {
|
|
||||||
let base = mesh.vertices.len() as u16;
|
|
||||||
for offset in [m * v + u, -m * v + u, -m * v - u, m * v - u] {
|
|
||||||
let vertex = render::mesh::Vertex {
|
|
||||||
position: (m * w + offset),
|
|
||||||
color: gray + color_level * m * w,
|
|
||||||
normal: m * w,
|
|
||||||
};
|
|
||||||
mesh.vertices.push(vertex);
|
|
||||||
}
|
|
||||||
mesh.faces.push([base, base + 1, base + 2]);
|
|
||||||
mesh.faces.push([base, base + 2, base + 3]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mesh
|
|
||||||
}
|
|
||||||
|
|
||||||
fn srgb_to_linear(color: Vec3) -> Vec3 {
|
|
||||||
color.map(|x| {
|
|
||||||
if x > 0.04045 {
|
|
||||||
((x + 0.055) / 1.055).powf(2.4)
|
|
||||||
} else {
|
|
||||||
x / 12.92
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn srgba_to_linear(color: Vec4) -> Vec4 {
|
|
||||||
let alpha = color.w;
|
|
||||||
let color = srgb_to_linear(vec3(color.x, color.y, color.z));
|
|
||||||
vec4(color.x, color.y, color.z, alpha)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn color_to_wgpu(color: Vec4) -> wgpu::Color {
|
|
||||||
wgpu::Color {
|
|
||||||
r: color.x.into(),
|
|
||||||
g: color.y.into(),
|
|
||||||
b: color.z.into(),
|
|
||||||
a: color.w.into(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Core {
|
impl Core {
|
||||||
pub fn new(gpu: Gpu, pixel_size: UVec2) -> Self {
|
pub fn new(gpu: Gpu, pixel_size: UVec2) -> Self {
|
||||||
let Gpu {
|
let Gpu {
|
||||||
|
|
@ -105,9 +42,7 @@ impl Core {
|
||||||
} = gpu;
|
} = gpu;
|
||||||
|
|
||||||
let depth = Self::create_depth_buffer(&device, pixel_size);
|
let depth = Self::create_depth_buffer(&device, pixel_size);
|
||||||
let mesh_pipeline = render::mesh::Pipeline::new(&device);
|
queue.submit([]); // flush buffer updates
|
||||||
let mesh_color = render::mesh::GpuMeshIndexed::new(&device, &cube(0.2, 0.8));
|
|
||||||
let mesh_gray = render::mesh::GpuMeshIndexed::new(&device, &cube(0.7, 0.7));
|
|
||||||
|
|
||||||
Self::configure_surface(&surface, &device, pixel_size);
|
Self::configure_surface(&surface, &device, pixel_size);
|
||||||
|
|
||||||
|
|
@ -115,9 +50,6 @@ impl Core {
|
||||||
device,
|
device,
|
||||||
queue,
|
queue,
|
||||||
surface,
|
surface,
|
||||||
mesh_pipeline,
|
|
||||||
mesh_color,
|
|
||||||
mesh_gray,
|
|
||||||
depth,
|
depth,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -129,20 +61,6 @@ impl Core {
|
||||||
let h = size.height as f32;
|
let h = size.height as f32;
|
||||||
w / h
|
w / h
|
||||||
};
|
};
|
||||||
let view = Mat4::from(camera::view(
|
|
||||||
args.cam_yaw,
|
|
||||||
args.cam_pitch,
|
|
||||||
args.cam_distance,
|
|
||||||
));
|
|
||||||
let proj = camera::projection(args.cam_fovy, aspect);
|
|
||||||
self.mesh_pipeline.set_params(
|
|
||||||
&self.queue,
|
|
||||||
render::mesh::Params {
|
|
||||||
mvp: proj * view,
|
|
||||||
light: vec3(0.7, 0.5, 1.0).normalize(),
|
|
||||||
_zero: 0.,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
self.queue.submit([]); // flush buffer updates
|
self.queue.submit([]); // flush buffer updates
|
||||||
|
|
||||||
let view = output.create_view(&wgpu::TextureViewDescriptor::default());
|
let view = output.create_view(&wgpu::TextureViewDescriptor::default());
|
||||||
|
|
@ -158,7 +76,12 @@ impl Core {
|
||||||
depth_slice: None,
|
depth_slice: None,
|
||||||
resolve_target: None,
|
resolve_target: None,
|
||||||
ops: wgpu::Operations {
|
ops: wgpu::Operations {
|
||||||
load: wgpu::LoadOp::Clear(color_to_wgpu(srgba_to_linear(args.background))),
|
load: wgpu::LoadOp::Clear(wgpu::Color {
|
||||||
|
r: args.background.x.into(),
|
||||||
|
g: args.background.y.into(),
|
||||||
|
b: args.background.z.into(),
|
||||||
|
a: args.background.w.into(),
|
||||||
|
}),
|
||||||
store: wgpu::StoreOp::Store,
|
store: wgpu::StoreOp::Store,
|
||||||
},
|
},
|
||||||
})],
|
})],
|
||||||
|
|
@ -172,7 +95,7 @@ impl Core {
|
||||||
}),
|
}),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
});
|
||||||
self.mesh_gray.render(&self.mesh_pipeline, &mut pass);
|
|
||||||
drop(pass);
|
drop(pass);
|
||||||
self.queue.submit(std::iter::once(encoder.finish()));
|
self.queue.submit(std::iter::once(encoder.finish()));
|
||||||
}
|
}
|
||||||
|
|
@ -188,7 +111,7 @@ impl Core {
|
||||||
mip_level_count: 1,
|
mip_level_count: 1,
|
||||||
sample_count: 1,
|
sample_count: 1,
|
||||||
dimension: wgpu::TextureDimension::D2,
|
dimension: wgpu::TextureDimension::D2,
|
||||||
format: render::DEPTH_FORMAT,
|
format: DEPTH_FORMAT,
|
||||||
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
|
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
|
||||||
view_formats: &[],
|
view_formats: &[],
|
||||||
})
|
})
|
||||||
|
|
@ -199,7 +122,7 @@ impl Core {
|
||||||
device,
|
device,
|
||||||
&wgpu::SurfaceConfiguration {
|
&wgpu::SurfaceConfiguration {
|
||||||
usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_DST,
|
usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_DST,
|
||||||
format: render::OUTPUT_FORMAT,
|
format: OUTPUT_FORMAT,
|
||||||
width: pixel_size.x,
|
width: pixel_size.x,
|
||||||
height: pixel_size.y,
|
height: pixel_size.y,
|
||||||
present_mode: wgpu::PresentMode::Fifo,
|
present_mode: wgpu::PresentMode::Fifo,
|
||||||
|
|
|
||||||
|
|
@ -1,209 +0,0 @@
|
||||||
use std::mem::offset_of;
|
|
||||||
|
|
||||||
use bytemuck::{Pod, Zeroable, bytes_of, cast_slice};
|
|
||||||
use glam::{Mat4, Vec3};
|
|
||||||
use wgpu::util::DeviceExt;
|
|
||||||
|
|
||||||
static SHADER: &str = include_str!("mesh.wgsl");
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Zeroable, Pod)]
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct Vertex {
|
|
||||||
pub position: Vec3,
|
|
||||||
pub color: Vec3,
|
|
||||||
pub normal: Vec3,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Zeroable, Pod)]
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct Params {
|
|
||||||
pub mvp: Mat4,
|
|
||||||
pub light: Vec3,
|
|
||||||
pub _zero: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default)]
|
|
||||||
pub struct CpuMesh {
|
|
||||||
pub faces: Vec<[Vertex; 3]>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default)]
|
|
||||||
pub struct CpuMeshIndexed {
|
|
||||||
pub vertices: Vec<Vertex>,
|
|
||||||
pub faces: Vec<[u16; 3]>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct GpuMesh {
|
|
||||||
vertices: wgpu::Buffer,
|
|
||||||
vertex_count: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct GpuMeshIndexed {
|
|
||||||
vertices: wgpu::Buffer,
|
|
||||||
indices: wgpu::Buffer,
|
|
||||||
index_count: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GpuMesh {
|
|
||||||
pub fn new(device: &wgpu::Device, data: &CpuMesh) -> Self {
|
|
||||||
let vertices = cast_slice::<_, Vertex>(&data.faces);
|
|
||||||
let vertex_count = vertices.len().try_into().expect("too many faces");
|
|
||||||
let vertices = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
|
||||||
label: None,
|
|
||||||
usage: wgpu::BufferUsages::VERTEX,
|
|
||||||
contents: cast_slice(&vertices),
|
|
||||||
});
|
|
||||||
Self {
|
|
||||||
vertices,
|
|
||||||
vertex_count,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GpuMeshIndexed {
|
|
||||||
pub fn new(device: &wgpu::Device, data: &CpuMeshIndexed) -> Self {
|
|
||||||
let vertex_count = data.vertices.len();
|
|
||||||
if vertex_count >= 1 << 16 {
|
|
||||||
panic!("too many vertices");
|
|
||||||
}
|
|
||||||
let indices = cast_slice::<_, u16>(&data.faces);
|
|
||||||
let index_count = indices.len().try_into().expect("too many faces");
|
|
||||||
for index in indices {
|
|
||||||
if *index as usize >= vertex_count {
|
|
||||||
panic!("vertex index out of bounds: {index} out of {vertex_count}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let vertices = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
|
||||||
label: None,
|
|
||||||
usage: wgpu::BufferUsages::VERTEX,
|
|
||||||
contents: cast_slice(&data.vertices),
|
|
||||||
});
|
|
||||||
let indices = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
|
||||||
label: None,
|
|
||||||
usage: wgpu::BufferUsages::INDEX,
|
|
||||||
contents: cast_slice(&indices),
|
|
||||||
});
|
|
||||||
Self {
|
|
||||||
vertices,
|
|
||||||
indices,
|
|
||||||
index_count,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Pipeline {
|
|
||||||
params: wgpu::Buffer,
|
|
||||||
bindings: wgpu::BindGroup,
|
|
||||||
pipeline: wgpu::RenderPipeline,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Pipeline {
|
|
||||||
pub fn new(device: &wgpu::Device) -> Self {
|
|
||||||
let params = device.create_buffer(&wgpu::BufferDescriptor {
|
|
||||||
label: None,
|
|
||||||
size: size_of::<Params>() as u64,
|
|
||||||
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
|
|
||||||
mapped_at_creation: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
|
|
||||||
label: None,
|
|
||||||
source: wgpu::ShaderSource::Wgsl(SHADER.into()),
|
|
||||||
});
|
|
||||||
let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
|
|
||||||
label: None,
|
|
||||||
layout: None,
|
|
||||||
vertex: wgpu::VertexState {
|
|
||||||
module: &shader,
|
|
||||||
entry_point: None,
|
|
||||||
compilation_options: wgpu::PipelineCompilationOptions::default(),
|
|
||||||
buffers: &[wgpu::VertexBufferLayout {
|
|
||||||
array_stride: size_of::<Vertex>() as u64,
|
|
||||||
step_mode: wgpu::VertexStepMode::Vertex,
|
|
||||||
attributes: &[
|
|
||||||
wgpu::VertexAttribute {
|
|
||||||
shader_location: 0,
|
|
||||||
offset: offset_of!(Vertex, position) as u64,
|
|
||||||
format: wgpu::VertexFormat::Float32x3,
|
|
||||||
},
|
|
||||||
wgpu::VertexAttribute {
|
|
||||||
shader_location: 1,
|
|
||||||
offset: offset_of!(Vertex, color) as u64,
|
|
||||||
format: wgpu::VertexFormat::Float32x3,
|
|
||||||
},
|
|
||||||
wgpu::VertexAttribute {
|
|
||||||
shader_location: 2,
|
|
||||||
offset: offset_of!(Vertex, normal) as u64,
|
|
||||||
format: wgpu::VertexFormat::Float32x3,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}],
|
|
||||||
},
|
|
||||||
primitive: wgpu::PrimitiveState {
|
|
||||||
topology: wgpu::PrimitiveTopology::TriangleList,
|
|
||||||
cull_mode: Some(wgpu::Face::Back),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
depth_stencil: Some(wgpu::DepthStencilState {
|
|
||||||
format: crate::render::DEPTH_FORMAT,
|
|
||||||
depth_write_enabled: true,
|
|
||||||
depth_compare: wgpu::CompareFunction::LessEqual,
|
|
||||||
stencil: wgpu::StencilState::default(),
|
|
||||||
bias: wgpu::DepthBiasState::default(),
|
|
||||||
}),
|
|
||||||
multisample: wgpu::MultisampleState {
|
|
||||||
count: 1,
|
|
||||||
mask: !0,
|
|
||||||
alpha_to_coverage_enabled: false,
|
|
||||||
},
|
|
||||||
fragment: Some(wgpu::FragmentState {
|
|
||||||
module: &shader,
|
|
||||||
entry_point: None,
|
|
||||||
compilation_options: wgpu::PipelineCompilationOptions::default(),
|
|
||||||
targets: &[Some(wgpu::ColorTargetState {
|
|
||||||
format: crate::render::OUTPUT_FORMAT,
|
|
||||||
blend: Some(wgpu::BlendState::REPLACE),
|
|
||||||
write_mask: wgpu::ColorWrites::ALL,
|
|
||||||
})],
|
|
||||||
}),
|
|
||||||
multiview: None,
|
|
||||||
cache: None,
|
|
||||||
});
|
|
||||||
let bindings = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
|
||||||
label: None,
|
|
||||||
layout: &pipeline.get_bind_group_layout(0),
|
|
||||||
entries: &[wgpu::BindGroupEntry {
|
|
||||||
binding: 0,
|
|
||||||
resource: params.as_entire_binding(),
|
|
||||||
}],
|
|
||||||
});
|
|
||||||
Self {
|
|
||||||
params,
|
|
||||||
bindings,
|
|
||||||
pipeline,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_params(&self, queue: &wgpu::Queue, params: Params) {
|
|
||||||
queue.write_buffer(&self.params, 0, bytes_of(¶ms));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GpuMesh {
|
|
||||||
pub fn render(&self, pipeline: &Pipeline, pass: &mut wgpu::RenderPass) {
|
|
||||||
pass.set_pipeline(&pipeline.pipeline);
|
|
||||||
pass.set_bind_group(0, &pipeline.bindings, &[]);
|
|
||||||
pass.set_vertex_buffer(0, self.vertices.slice(..));
|
|
||||||
pass.draw(0..self.vertex_count, 0..1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GpuMeshIndexed {
|
|
||||||
pub fn render(&self, pipeline: &Pipeline, pass: &mut wgpu::RenderPass) {
|
|
||||||
pass.set_pipeline(&pipeline.pipeline);
|
|
||||||
pass.set_bind_group(0, &pipeline.bindings, &[]);
|
|
||||||
pass.set_vertex_buffer(0, self.vertices.slice(..));
|
|
||||||
pass.set_index_buffer(self.indices.slice(..), wgpu::IndexFormat::Uint16);
|
|
||||||
pass.draw_indexed(0..self.index_count, 0, 0..1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,36 +0,0 @@
|
||||||
struct Params {
|
|
||||||
mvp: mat4x4f,
|
|
||||||
light: vec3f,
|
|
||||||
_zero: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Vertex {
|
|
||||||
@location(0) position: vec3f,
|
|
||||||
@location(1) color: vec3f,
|
|
||||||
@location(2) normal: vec3f,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Varying {
|
|
||||||
@builtin(position) screen: vec4f,
|
|
||||||
@location(0) color: vec4f,
|
|
||||||
@location(1) normal: vec3f,
|
|
||||||
}
|
|
||||||
|
|
||||||
@group(0) @binding(0) var<uniform> params: Params;
|
|
||||||
|
|
||||||
@vertex
|
|
||||||
fn on_vertex(in: Vertex) -> Varying {
|
|
||||||
let position = params.mvp * vec4f(in.position, 1.0);
|
|
||||||
let color = vec4f(in.color, 1.0);
|
|
||||||
let normal = in.normal;
|
|
||||||
return Varying(position, color, normal);
|
|
||||||
}
|
|
||||||
|
|
||||||
@fragment
|
|
||||||
fn on_fragment(in: Varying) -> @location(0) vec4f {
|
|
||||||
let normal = normalize(in.normal);
|
|
||||||
let light = dot(params.light, normal);
|
|
||||||
let u = 0.5 + 0.5 * light;
|
|
||||||
let color = vec4f(u * in.color.xyz, in.color.w);
|
|
||||||
return color;
|
|
||||||
}
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
pub mod mesh;
|
|
||||||
|
|
||||||
pub const DEPTH_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Depth24Plus;
|
|
||||||
pub const OUTPUT_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Bgra8UnormSrgb;
|
|
||||||
|
|
@ -13,10 +13,6 @@ struct Core;
|
||||||
|
|
||||||
struct RedrawArgs {
|
struct RedrawArgs {
|
||||||
Vec4 background;
|
Vec4 background;
|
||||||
float cam_yaw;
|
|
||||||
float cam_pitch;
|
|
||||||
float cam_distance;
|
|
||||||
float cam_fovy;
|
|
||||||
};
|
};
|
||||||
} // namespace ffi
|
} // namespace ffi
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,10 +15,6 @@ void PROJECTNAME::updateView() {
|
||||||
const auto color = m_ui->inBackground->color();
|
const auto color = m_ui->inBackground->color();
|
||||||
RedrawArgs args{
|
RedrawArgs args{
|
||||||
.background = { color.redF(), color.greenF(), color.blueF(), 1.00 },
|
.background = { color.redF(), color.greenF(), color.blueF(), 1.00 },
|
||||||
.cam_yaw = qDegreesToRadians((float)m_ui->camYaw->value()),
|
|
||||||
.cam_pitch = qDegreesToRadians((float)m_ui->camPitch->value()),
|
|
||||||
.cam_distance = (float)m_ui->camDistance->value(),
|
|
||||||
.cam_fovy = qDegreesToRadians((float)m_ui->camFoV->value()),
|
|
||||||
};
|
};
|
||||||
m_ui->viewport->setView(args);
|
m_ui->viewport->setView(args);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -40,122 +40,6 @@
|
||||||
</attribute>
|
</attribute>
|
||||||
<widget class="QWidget" name="dockWidgetContents">
|
<widget class="QWidget" name="dockWidgetContents">
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||||
<item>
|
|
||||||
<widget class="QGroupBox" name="groupBox">
|
|
||||||
<property name="title">
|
|
||||||
<string>Camera</string>
|
|
||||||
</property>
|
|
||||||
<layout class="QVBoxLayout" name="verticalLayout">
|
|
||||||
<item>
|
|
||||||
<widget class="QLabel" name="label_2">
|
|
||||||
<property name="text">
|
|
||||||
<string>Yaw</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QDoubleSpinBox" name="camYaw">
|
|
||||||
<property name="suffix">
|
|
||||||
<string> °</string>
|
|
||||||
</property>
|
|
||||||
<property name="decimals">
|
|
||||||
<number>1</number>
|
|
||||||
</property>
|
|
||||||
<property name="minimum">
|
|
||||||
<double>-180.000000000000000</double>
|
|
||||||
</property>
|
|
||||||
<property name="maximum">
|
|
||||||
<double>180.000000000000000</double>
|
|
||||||
</property>
|
|
||||||
<property name="singleStep">
|
|
||||||
<double>5.000000000000000</double>
|
|
||||||
</property>
|
|
||||||
<property name="value">
|
|
||||||
<double>45.000000000000000</double>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QLabel" name="label_3">
|
|
||||||
<property name="text">
|
|
||||||
<string>Pitch</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QDoubleSpinBox" name="camPitch">
|
|
||||||
<property name="suffix">
|
|
||||||
<string> °</string>
|
|
||||||
</property>
|
|
||||||
<property name="decimals">
|
|
||||||
<number>1</number>
|
|
||||||
</property>
|
|
||||||
<property name="minimum">
|
|
||||||
<double>-90.000000000000000</double>
|
|
||||||
</property>
|
|
||||||
<property name="maximum">
|
|
||||||
<double>90.000000000000000</double>
|
|
||||||
</property>
|
|
||||||
<property name="singleStep">
|
|
||||||
<double>5.000000000000000</double>
|
|
||||||
</property>
|
|
||||||
<property name="value">
|
|
||||||
<double>30.000000000000000</double>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QLabel" name="label_5">
|
|
||||||
<property name="text">
|
|
||||||
<string>Distance</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QDoubleSpinBox" name="camDistance">
|
|
||||||
<property name="maximum">
|
|
||||||
<double>10.000000000000000</double>
|
|
||||||
</property>
|
|
||||||
<property name="stepType">
|
|
||||||
<enum>QAbstractSpinBox::StepType::AdaptiveDecimalStepType</enum>
|
|
||||||
</property>
|
|
||||||
<property name="value">
|
|
||||||
<double>4.000000000000000</double>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QLabel" name="label_4">
|
|
||||||
<property name="text">
|
|
||||||
<string>Field of view</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QDoubleSpinBox" name="camFoV">
|
|
||||||
<property name="suffix">
|
|
||||||
<string> °</string>
|
|
||||||
</property>
|
|
||||||
<property name="decimals">
|
|
||||||
<number>1</number>
|
|
||||||
</property>
|
|
||||||
<property name="minimum">
|
|
||||||
<double>15.000000000000000</double>
|
|
||||||
</property>
|
|
||||||
<property name="maximum">
|
|
||||||
<double>150.000000000000000</double>
|
|
||||||
</property>
|
|
||||||
<property name="singleStep">
|
|
||||||
<double>5.000000000000000</double>
|
|
||||||
</property>
|
|
||||||
<property name="value">
|
|
||||||
<double>90.000000000000000</double>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="label">
|
<widget class="QLabel" name="label">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
|
|
@ -167,9 +51,9 @@
|
||||||
<widget class="KColorCombo" name="inBackground">
|
<widget class="KColorCombo" name="inBackground">
|
||||||
<property name="color">
|
<property name="color">
|
||||||
<color>
|
<color>
|
||||||
<red>0</red>
|
<red>25</red>
|
||||||
<green>99</green>
|
<green>220</green>
|
||||||
<blue>133</blue>
|
<blue>0</blue>
|
||||||
</color>
|
</color>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
|
|
@ -222,70 +106,6 @@
|
||||||
</hint>
|
</hint>
|
||||||
</hints>
|
</hints>
|
||||||
</connection>
|
</connection>
|
||||||
<connection>
|
|
||||||
<sender>camYaw</sender>
|
|
||||||
<signal>valueChanged(double)</signal>
|
|
||||||
<receiver>MainWindow</receiver>
|
|
||||||
<slot>updateView()</slot>
|
|
||||||
<hints>
|
|
||||||
<hint type="sourcelabel">
|
|
||||||
<x>1507</x>
|
|
||||||
<y>136</y>
|
|
||||||
</hint>
|
|
||||||
<hint type="destinationlabel">
|
|
||||||
<x>1475</x>
|
|
||||||
<y>183</y>
|
|
||||||
</hint>
|
|
||||||
</hints>
|
|
||||||
</connection>
|
|
||||||
<connection>
|
|
||||||
<sender>camPitch</sender>
|
|
||||||
<signal>valueChanged(double)</signal>
|
|
||||||
<receiver>MainWindow</receiver>
|
|
||||||
<slot>updateView()</slot>
|
|
||||||
<hints>
|
|
||||||
<hint type="sourcelabel">
|
|
||||||
<x>1507</x>
|
|
||||||
<y>214</y>
|
|
||||||
</hint>
|
|
||||||
<hint type="destinationlabel">
|
|
||||||
<x>1479</x>
|
|
||||||
<y>280</y>
|
|
||||||
</hint>
|
|
||||||
</hints>
|
|
||||||
</connection>
|
|
||||||
<connection>
|
|
||||||
<sender>camDistance</sender>
|
|
||||||
<signal>valueChanged(double)</signal>
|
|
||||||
<receiver>MainWindow</receiver>
|
|
||||||
<slot>updateView()</slot>
|
|
||||||
<hints>
|
|
||||||
<hint type="sourcelabel">
|
|
||||||
<x>1536</x>
|
|
||||||
<y>260</y>
|
|
||||||
</hint>
|
|
||||||
<hint type="destinationlabel">
|
|
||||||
<x>1478</x>
|
|
||||||
<y>345</y>
|
|
||||||
</hint>
|
|
||||||
</hints>
|
|
||||||
</connection>
|
|
||||||
<connection>
|
|
||||||
<sender>camFoV</sender>
|
|
||||||
<signal>valueChanged(double)</signal>
|
|
||||||
<receiver>MainWindow</receiver>
|
|
||||||
<slot>updateView()</slot>
|
|
||||||
<hints>
|
|
||||||
<hint type="sourcelabel">
|
|
||||||
<x>1541</x>
|
|
||||||
<y>325</y>
|
|
||||||
</hint>
|
|
||||||
<hint type="destinationlabel">
|
|
||||||
<x>1476</x>
|
|
||||||
<y>432</y>
|
|
||||||
</hint>
|
|
||||||
</hints>
|
|
||||||
</connection>
|
|
||||||
</connections>
|
</connections>
|
||||||
<slots>
|
<slots>
|
||||||
<slot>updateView()</slot>
|
<slot>updateView()</slot>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user